Merge branches 'pm-core', 'pm-qos', 'pm-domains' and 'pm-opp'
* pm-core:
PM / runtime: Fix some typos
* pm-qos:
PM / QoS: Remove global notifiers
* pm-domains:
PM / Domains: Power off masters immediately in the power off sequence
PM / Domains: Rename is_async to one_dev_on for genpd_power_off()
PM / Domains: Move genpd_power_off() above genpd_power_on()
* pm-opp:
PM / OPP: Documentation: Fix opp-microvolt in examples
PM / OPP: fix off-by-one bug in dev_pm_opp_get_max_volt_latency loop
diff --git a/CREDITS b/CREDITS
index c585607..c5626bf 100644
--- a/CREDITS
+++ b/CREDITS
@@ -2478,12 +2478,11 @@
S: Germany
N: Arnaldo Carvalho de Melo
-E: acme@ghostprotocols.net
+E: acme@kernel.org
E: arnaldo.melo@gmail.com
E: acme@redhat.com
-W: http://oops.ghostprotocols.net:81/blog/
P: 1024D/9224DF01 D5DF E3BB E3C8 BCBB F8AD 841A B6AB 4681 9224 DF01
-D: IPX, LLC, DCCP, cyc2x, wl3501_cs, net/ hacks
+D: tools/, IPX, LLC, DCCP, cyc2x, wl3501_cs, net/ hacks
S: Brazil
N: Karsten Merker
diff --git a/Documentation/ABI/testing/sysfs-class-devfreq-event b/Documentation/ABI/testing/sysfs-class-devfreq-event
new file mode 100644
index 0000000..ceaf0f6
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-devfreq-event
@@ -0,0 +1,25 @@
+What: /sys/class/devfreq-event/event(x)/
+Date: January 2017
+Contact: Chanwoo Choi <cw00.choi@samsung.com>
+Description:
+ Provide a place in sysfs for the devfreq-event objects.
+ This allows accessing various devfreq-event specific variables.
+ The name of devfreq-event object denoted as 'event(x)' which
+ includes the unique number of 'x' for each devfreq-event object.
+
+What: /sys/class/devfreq-event/event(x)/name
+Date: January 2017
+Contact: Chanwoo Choi <cw00.choi@samsung.com>
+Description:
+ The /sys/class/devfreq-event/event(x)/name attribute contains
+ the name of the devfreq-event object. This attribute is
+ read-only.
+
+What: /sys/class/devfreq-event/event(x)/enable_count
+Date: January 2017
+Contact: Chanwoo Choi <cw00.choi@samsung.com>
+Description:
+ The /sys/class/devfreq-event/event(x)/enable_count attribute
+ contains the reference count to enable the devfreq-event
+ object. If the device is enabled, the value of attribute is
+ greater than zero.
diff --git a/Documentation/ABI/testing/sysfs-class-led b/Documentation/ABI/testing/sysfs-class-led
index 491cdee..5f67f7a 100644
--- a/Documentation/ABI/testing/sysfs-class-led
+++ b/Documentation/ABI/testing/sysfs-class-led
@@ -23,6 +23,23 @@
If the LED does not support different brightness levels, this
should be 1.
+What: /sys/class/leds/<led>/brightness_hw_changed
+Date: January 2017
+KernelVersion: 4.11
+Description:
+ Last hardware set brightness level for this LED. Some LEDs
+ may be changed autonomously by hardware/firmware. Only LEDs
+ where this happens and the driver can detect this, will have
+ this file.
+
+ This file supports poll() to detect when the hardware changes
+ the brightness.
+
+ Reading this file will return the last brightness level set
+ by the hardware, this may be different from the current
+ brightness. Reading this file when no hw brightness change
+ event has happened will return an ENODATA error.
+
What: /sys/class/leds/<led>/trigger
Date: March 2006
KernelVersion: 2.6.17
diff --git a/Documentation/ABI/testing/sysfs-devices-edac b/Documentation/ABI/testing/sysfs-devices-edac
index 6568e00..46ff929 100644
--- a/Documentation/ABI/testing/sysfs-devices-edac
+++ b/Documentation/ABI/testing/sysfs-devices-edac
@@ -138,3 +138,20 @@
Description: This attribute file will display what type of memory is
currently on this csrow. Normally, either buffered or
unbuffered memory (for example, Unbuffered-DDR3).
+
+What: /sys/devices/system/edac/mc/mc*/(dimm|rank)*/dimm_ce_count
+Date: October 2016
+Contact: linux-edac@vger.kernel.org
+Description: This attribute file displays the total count of correctable
+ errors that have occurred on this DIMM. This count is very important
+ to examine. CEs provide early indications that a DIMM is beginning
+ to fail. This count field should be monitored for non-zero values
+ and report such information to the system administrator.
+
+What: /sys/devices/system/edac/mc/mc*/(dimm|rank)*/dimm_ue_count
+Date: October 2016
+Contact: linux-edac@vger.kernel.org
+Description: This attribute file displays the total count of uncorrectable
+ errors that have occurred on this DIMM. If panic_on_ue is set, this
+ counter will not have a chance to increment, since EDAC will panic the
+ system
diff --git a/Documentation/ABI/testing/sysfs-kernel-iommu_groups b/Documentation/ABI/testing/sysfs-kernel-iommu_groups
index 9b31556..35c64e0 100644
--- a/Documentation/ABI/testing/sysfs-kernel-iommu_groups
+++ b/Documentation/ABI/testing/sysfs-kernel-iommu_groups
@@ -12,3 +12,15 @@
file if the IOMMU driver has chosen to register a more
common name for the group.
Users:
+
+What: /sys/kernel/iommu_groups/reserved_regions
+Date: January 2017
+KernelVersion: v4.11
+Contact: Eric Auger <eric.auger@redhat.com>
+Description: /sys/kernel/iommu_groups/reserved_regions list IOVA
+ regions that are reserved. Not necessarily all
+ reserved regions are listed. This is typically used to
+ output direct-mapped, MSI, non mappable regions. Each
+ region is described on a single line: the 1st field is
+ the base IOVA, the second is the end IOVA and the third
+ field describes the type of the region.
diff --git a/Documentation/DMA-attributes.txt b/Documentation/DMA-attributes.txt
index 98bf7ac..44c6bc4 100644
--- a/Documentation/DMA-attributes.txt
+++ b/Documentation/DMA-attributes.txt
@@ -143,3 +143,13 @@
where allocation failures are not a problem, and shouldn't bother the logs.
NOTE: At the moment DMA_ATTR_NO_WARN is only implemented on PowerPC.
+
+DMA_ATTR_PRIVILEGED
+------------------------------
+
+Some advanced peripherals such as remote processors and GPUs perform
+accesses to DMA buffers in both privileged "supervisor" and unprivileged
+"user" modes. This attribute is used to indicate to the DMA-mapping
+subsystem that the buffer is fully accessible at the elevated privilege
+level (and ideally inaccessible or at least read-only at the
+lesser-privileged levels).
diff --git a/Documentation/RCU/Design/Data-Structures/Data-Structures.html b/Documentation/RCU/Design/Data-Structures/Data-Structures.html
index 7eb47ac..d583c65 100644
--- a/Documentation/RCU/Design/Data-Structures/Data-Structures.html
+++ b/Documentation/RCU/Design/Data-Structures/Data-Structures.html
@@ -4,7 +4,7 @@
<head><title>A Tour Through TREE_RCU's Data Structures [LWN.net]</title>
<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
- <p>January 27, 2016</p>
+ <p>December 18, 2016</p>
<p>This article was contributed by Paul E. McKenney</p>
<h3>Introduction</h3>
@@ -31,9 +31,6 @@
Accessor Functions</a>
</ol>
-At the end we have the
-<a href="#Answers to Quick Quizzes">answers to the quick quizzes</a>.
-
<h3><a name="Data-Structure Relationships">Data-Structure Relationships</a></h3>
<p>RCU is for all intents and purposes a large state machine, and its
diff --git a/Documentation/RCU/Design/Expedited-Grace-Periods/ExpRCUFlow.svg b/Documentation/RCU/Design/Expedited-Grace-Periods/ExpRCUFlow.svg
new file mode 100644
index 0000000..7c6c90b
--- /dev/null
+++ b/Documentation/RCU/Design/Expedited-Grace-Periods/ExpRCUFlow.svg
@@ -0,0 +1,830 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Creator: fig2dev Version 3.2 Patchlevel 5e -->
+
+<!-- CreationDate: Wed Dec 9 17:39:46 2015 -->
+
+<!-- Magnification: 3.000 -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="952.6817"
+ height="1219.6219"
+ viewBox="-66 -66 12729.905 16296.808"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="ExpRCUFlow.svg">
+ <metadata
+ id="metadata94">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs92">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path4146"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend"
+ style="overflow:visible">
+ <path
+ id="path3852"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-9"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path3852-7"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-7"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-6"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-1"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-4"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-16"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-8"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-160"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-5"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-78"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-66"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-8"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-56"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-19"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-89"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-85"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-3"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-73"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-55"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-5"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-88"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-198"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-2"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-4"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-22"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker5072"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path5074"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-87"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-63"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-6"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-26"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-0"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-51"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1090"
+ inkscape:window-height="1148"
+ id="namedview90"
+ showgrid="true"
+ inkscape:zoom="0.80021373"
+ inkscape:cx="462.49289"
+ inkscape:cy="623.19585"
+ inkscape:window-x="557"
+ inkscape:window-y="24"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4"
+ inkscape:snap-grids="false"
+ fit-margin-top="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5"
+ fit-margin-left="5" />
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4"
+ transform="translate(23.312813,523.41305)">
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line -->
+ <!-- Arrowhead on XXXpoint 11475 2250 - 11475 3465-->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line -->
+ <!-- Arrowhead on XXXpoint 11475 5625 - 11475 6840-->
+ <!-- Line -->
+ <!-- Arrowhead on XXXpoint 7875 225 - 10665 225-->
+ <!-- Line -->
+ <!-- Arrowhead on XXXpoint 9675 675 - 7785 675-->
+ <!-- Line -->
+ <!-- Arrowhead on XXXpoint 9675 4725 - 10665 4725-->
+ <!-- Line -->
+ <!-- Arrowhead on XXXpoint 9225 5175 - 10665 5175-->
+ <!-- Line -->
+ <!-- Arrowhead on XXXpoint 8775 11475 - 10665 11475-->
+ <!-- Line: box -->
+ <!-- Line -->
+ <!-- Arrowhead on XXXpoint 11475 9000 - 11475 10215-->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <g
+ id="g4104"
+ transform="translate(-1068.9745,0)">
+ <rect
+ transform="matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)"
+ y="-7383.8755"
+ x="-6124.8989"
+ height="1966.2251"
+ width="1953.6969"
+ id="rect3032-1-0"
+ style="fill:#96ff96;fill-opacity:1;stroke:#000000;stroke-width:45.00382233;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4098"
+ y="818.40338"
+ x="8168.2671"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="818.40338"
+ x="8168.2671"
+ id="tspan4100"
+ sodipodi:role="line">Idle or</tspan><tspan
+ id="tspan4102"
+ y="1152.4579"
+ x="8168.2671"
+ sodipodi:role="line">offline?</tspan></text>
+ </g>
+ <g
+ id="g4114"
+ transform="translate(0,147.96969)">
+ <rect
+ id="rect6"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1475.6636"
+ width="4401.7612"
+ y="0"
+ x="0" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110"
+ y="835.11212"
+ x="2206.4917"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="835.11212"
+ x="2206.4917"
+ id="tspan4112"
+ sodipodi:role="line">CPU N Start</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
+ d="M 4432.5052,897.4924 5684.8749,880.79414"
+ id="path4119"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
+ d="M 8503.0006,874.12161 9755.3703,857.42334"
+ id="path4119-8"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="8617.0977"
+ y="705.50983"
+ id="text4593"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4595"
+ x="8617.0977"
+ y="705.50983">Y</tspan></text>
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-9"
+ transform="translate(9722.4732,131.27105)">
+ <rect
+ id="rect6-0"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1425.5687"
+ width="2748.6331"
+ y="0"
+ x="80.17308" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-5"
+ y="835.11212"
+ x="1460.1007"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="835.11212"
+ x="1460.1007"
+ id="tspan4112-9"
+ sodipodi:role="line">Done</tspan></text>
+ </g>
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-5"
+ transform="translate(0,3705.3456)">
+ <rect
+ id="rect6-1"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1475.6636"
+ width="4401.7612"
+ y="0"
+ x="0" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-9"
+ y="835.11212"
+ x="2206.4917"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="835.11212"
+ x="2206.4917"
+ sodipodi:role="line"
+ id="tspan4776">Send IPI to CPU N</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
+ d="M 7102.5627,2263.5171 4430.8404,3682.8694"
+ id="path4119-3"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4104-1"
+ transform="translate(-1065.3349,6403.5782)">
+ <rect
+ transform="matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)"
+ y="-7383.8755"
+ x="-6124.8989"
+ height="1966.2251"
+ width="1953.6969"
+ id="rect3032-1-0-6"
+ style="fill:#96ff96;fill-opacity:1;stroke:#000000;stroke-width:45.00382233;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4098-3"
+ y="482.00006"
+ x="8168.2671"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ id="tspan4102-8"
+ y="482.00006"
+ x="8168.2671"
+ sodipodi:role="line">In RCU</tspan><tspan
+ y="816.05457"
+ x="8168.2671"
+ sodipodi:role="line"
+ id="tspan4833">read-side</tspan><tspan
+ y="1150.109"
+ x="8168.2671"
+ sodipodi:role="line"
+ id="tspan4835">critical</tspan><tspan
+ y="1484.1636"
+ x="8168.2671"
+ sodipodi:role="line"
+ id="tspan4837">section?</tspan></text>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:267.24362183px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="6463.0864"
+ y="2285.6765"
+ id="text4593-0"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4595-6"
+ x="6463.0864"
+ y="2285.6765">N</tspan></text>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:40.08654108, 80.17308215;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
+ d="m 2189.1897,5219.361 16.6983,1252.3697"
+ id="path4119-0"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-5-2"
+ transform="translate(0,6551.5479)">
+ <rect
+ id="rect6-1-7"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1475.6636"
+ width="4401.7612"
+ y="0"
+ x="0" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-9-5"
+ y="835.11212"
+ x="2206.4917"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="835.11212"
+ x="2206.4917"
+ sodipodi:role="line"
+ id="tspan4776-5">IPI Handler</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
+ d="m 4432.5052,7297.9678 1252.3697,-16.6982"
+ id="path4119-2"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
+ d="m 8503.0013,7278.6595 1252.369,-16.6982"
+ id="path4119-8-7"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-size:267.24362183px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="8617.0977"
+ y="7110.0186"
+ id="text4593-4"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4595-0"
+ x="8617.0977"
+ y="7110.0186">N</tspan></text>
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-9-3"
+ transform="translate(9722.4732,6535.809)">
+ <rect
+ id="rect6-0-7"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1425.5687"
+ width="2748.6331"
+ y="29.467337"
+ x="80.17308" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-5-7"
+ y="503.71591"
+ x="1460.1007"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="503.71591"
+ x="1460.1007"
+ id="tspan4112-9-0"
+ sodipodi:role="line">Report CPU</tspan><tspan
+ y="837.77039"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan4923">Quiescent</tspan><tspan
+ y="1171.825"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan4925">State</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:40.08654335, 80.17308669;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
+ d="m 7102.5627,8725.7454 16.6983,1252.3697"
+ id="path4119-0-0"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-size:267.24362183px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="6797.0522"
+ y="9018.6807"
+ id="text4593-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4595-2"
+ x="6797.0522"
+ y="9018.6807">Y</tspan></text>
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-9-3-8"
+ transform="translate(-80.17308,11381.108)">
+ <rect
+ id="rect6-0-7-5"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1425.5687"
+ width="2748.6331"
+ y="29.467337"
+ x="80.17308" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-5-7-6"
+ y="841.88086"
+ x="1460.1007"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="841.88086"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan4925-1">rcu_read_unlock()</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:40.08654562, 80.17309124;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
+ d="m 1362.6256,10071.26 16.6983,1252.369"
+ id="path4119-0-0-7"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
+ d="m 1362.6256,12883.919 16.6983,1252.369"
+ id="path4119-0-0-7-7"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-9-3-8-1"
+ transform="translate(9722.4732,11389.458)">
+ <rect
+ id="rect6-0-7-5-1"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1425.5687"
+ width="2748.6331"
+ y="29.467337"
+ x="80.17308" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-5-7-6-2"
+ y="841.88086"
+ x="1460.1007"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="841.88086"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan4925-1-2">Context Switch</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:40.08654789, 80.17309578;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
+ d="m 11165.272,10071.26 16.698,1252.369"
+ id="path4119-0-0-7-8"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-9-3-9"
+ transform="translate(-80.17308,14163.046)">
+ <rect
+ id="rect6-0-7-1"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1425.5687"
+ width="2748.6331"
+ y="29.467337"
+ x="80.17308" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-5-7-3"
+ y="503.71591"
+ x="1460.1007"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="503.71591"
+ x="1460.1007"
+ id="tspan4112-9-0-4"
+ sodipodi:role="line">Report CPU</tspan><tspan
+ y="837.77039"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan4923-3">and Task</tspan><tspan
+ y="1171.825"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan4925-9">Quiescent States</tspan></text>
+ </g>
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-9-3-8-1-8"
+ transform="translate(5663.2978,11389.458)">
+ <rect
+ id="rect6-0-7-5-1-1"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1425.5687"
+ width="2748.6331"
+ y="29.467337"
+ x="80.17308" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-5-7-6-2-4"
+ y="841.88086"
+ x="1460.1007"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="841.88086"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan4925-1-2-4">Enqueue Task</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
+ d="M 9827.612,12141.988 8575.243,12125.29"
+ id="path4119-8-7-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
+ d="m 7106.0965,12818.962 16.6983,1252.369"
+ id="path4119-0-0-7-7-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-9-3-9-2"
+ transform="translate(5663.2978,14098.088)">
+ <rect
+ id="rect6-0-7-1-8"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1425.5687"
+ width="2748.6331"
+ y="29.467337"
+ x="80.17308" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-5-7-3-4"
+ y="503.71591"
+ x="1460.1007"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="503.71591"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan4923-3-2">Report CPU</tspan><tspan
+ y="837.77039"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan4925-9-9">Quiescent</tspan><tspan
+ y="1171.825"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan5239">State</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:40.08654562, 80.17309124;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
+ d="M 5733.305,14095.542 2761.014,12809.774"
+ id="path4119-0-0-2"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:40.08654107, 80.17308214;stroke-dashoffset:0"
+ d="m 1353.3524,10079.499 9701.6916,0 100.189,-16.698"
+ id="path5265"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccc" />
+ </g>
+</svg>
diff --git a/Documentation/RCU/Design/Expedited-Grace-Periods/ExpSchedFlow.svg b/Documentation/RCU/Design/Expedited-Grace-Periods/ExpSchedFlow.svg
new file mode 100644
index 0000000..e4233ac
--- /dev/null
+++ b/Documentation/RCU/Design/Expedited-Grace-Periods/ExpSchedFlow.svg
@@ -0,0 +1,826 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Creator: fig2dev Version 3.2 Patchlevel 5e -->
+
+<!-- CreationDate: Wed Dec 9 17:39:46 2015 -->
+
+<!-- Magnification: 3.000 -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="952.6817"
+ height="1425.6191"
+ viewBox="-66 -66 12729.905 19049.38"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="ExpSchedFlow.svg">
+ <metadata
+ id="metadata94">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs92">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path4146"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend"
+ style="overflow:visible">
+ <path
+ id="path3852"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-9"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path3852-7"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-7"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-6"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-1"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-4"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-16"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-8"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-160"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-5"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-78"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-66"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-8"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-56"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-19"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-89"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-85"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-3"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-73"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-55"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-5"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-88"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-198"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-2"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-4"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-22"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker5072"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path5074"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-87"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-63"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-6"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-26"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-0"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4146-51"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-58"
+ style="overflow:visible">
+ <path
+ id="path4146-61"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1090"
+ inkscape:window-height="1148"
+ id="namedview90"
+ showgrid="true"
+ inkscape:zoom="0.80021373"
+ inkscape:cx="462.49289"
+ inkscape:cy="473.6718"
+ inkscape:window-x="770"
+ inkscape:window-y="24"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4114-9-3-9"
+ inkscape:snap-grids="false"
+ fit-margin-top="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5"
+ fit-margin-left="5" />
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4"
+ transform="translate(23.312814,523.41265)">
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line -->
+ <!-- Arrowhead on XXXpoint 11475 2250 - 11475 3465-->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line: box -->
+ <!-- Line -->
+ <!-- Arrowhead on XXXpoint 11475 5625 - 11475 6840-->
+ <!-- Line -->
+ <!-- Arrowhead on XXXpoint 7875 225 - 10665 225-->
+ <!-- Line -->
+ <!-- Arrowhead on XXXpoint 9675 675 - 7785 675-->
+ <!-- Line -->
+ <!-- Arrowhead on XXXpoint 9675 4725 - 10665 4725-->
+ <!-- Line -->
+ <!-- Arrowhead on XXXpoint 9225 5175 - 10665 5175-->
+ <!-- Line -->
+ <!-- Arrowhead on XXXpoint 8775 11475 - 10665 11475-->
+ <!-- Line: box -->
+ <!-- Line -->
+ <!-- Arrowhead on XXXpoint 11475 9000 - 11475 10215-->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <!-- Text -->
+ <g
+ id="g4104"
+ transform="translate(-1068.9745,0)">
+ <rect
+ transform="matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)"
+ y="-7383.8755"
+ x="-6124.8989"
+ height="1966.2251"
+ width="1953.6969"
+ id="rect3032-1-0"
+ style="fill:#96ff96;fill-opacity:1;stroke:#000000;stroke-width:45.00382233;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4098"
+ y="818.40338"
+ x="8168.2671"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="818.40338"
+ x="8168.2671"
+ id="tspan4100"
+ sodipodi:role="line">Idle or</tspan><tspan
+ id="tspan4102"
+ y="1152.4579"
+ x="8168.2671"
+ sodipodi:role="line">offline?</tspan></text>
+ </g>
+ <g
+ id="g4114"
+ transform="translate(0,147.96969)">
+ <rect
+ id="rect6"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1475.6636"
+ width="4401.7612"
+ y="0"
+ x="0" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110"
+ y="835.11212"
+ x="2206.4917"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="835.11212"
+ x="2206.4917"
+ id="tspan4112"
+ sodipodi:role="line">CPU N Start</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
+ d="M 4432.5052,897.4924 5684.8749,880.79414"
+ id="path4119"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
+ d="M 8503.0006,874.12161 9755.3703,857.42334"
+ id="path4119-8"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="8617.0977"
+ y="705.50983"
+ id="text4593"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4595"
+ x="8617.0977"
+ y="705.50983">Y</tspan></text>
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-9"
+ transform="translate(9722.4732,131.27105)">
+ <rect
+ id="rect6-0"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1425.5687"
+ width="2748.6331"
+ y="0"
+ x="80.17308" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-5"
+ y="835.11212"
+ x="1460.1007"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="835.11212"
+ x="1460.1007"
+ id="tspan4112-9"
+ sodipodi:role="line">Done</tspan></text>
+ </g>
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-5"
+ transform="translate(0,3705.3456)">
+ <rect
+ id="rect6-1"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1475.6636"
+ width="4401.7612"
+ y="0"
+ x="0" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-9"
+ y="835.11212"
+ x="2206.4917"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="835.11212"
+ x="2206.4917"
+ sodipodi:role="line"
+ id="tspan4776">Send IPI to CPU N</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
+ d="M 7102.5627,2263.5171 4430.8404,3682.8694"
+ id="path4119-3"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4104-1"
+ transform="translate(-1065.3349,6403.5782)">
+ <rect
+ transform="matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)"
+ y="-7383.8755"
+ x="-6124.8989"
+ height="1966.2251"
+ width="1953.6969"
+ id="rect3032-1-0-6"
+ style="fill:#96ff96;fill-opacity:1;stroke:#000000;stroke-width:45.00382233;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4098-3"
+ y="985.4306"
+ x="8168.2671"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="985.4306"
+ x="8168.2671"
+ sodipodi:role="line"
+ id="tspan3109">CPU idle?</tspan></text>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:267.24362183px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="6463.0864"
+ y="2285.6765"
+ id="text4593-0"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4595-6"
+ x="6463.0864"
+ y="2285.6765">N</tspan></text>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:40.08654108, 80.17308215;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
+ d="m 2189.1897,5219.361 16.6983,1252.3697"
+ id="path4119-0"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-5-2"
+ transform="translate(0,6551.5479)">
+ <rect
+ id="rect6-1-7"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1475.6636"
+ width="4401.7612"
+ y="0"
+ x="0" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-9-5"
+ y="835.11212"
+ x="2206.4917"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="835.11212"
+ x="2206.4917"
+ sodipodi:role="line"
+ id="tspan4776-5">IPI Handler</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
+ d="m 4432.5052,7297.9678 1252.3697,-16.6982"
+ id="path4119-2"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
+ d="m 8503.0013,7278.6595 1252.369,-16.6982"
+ id="path4119-8-7"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-size:267.24362183px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="8617.0977"
+ y="7110.0186"
+ id="text4593-4"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4595-0"
+ x="8617.0977"
+ y="7110.0186">Y</tspan></text>
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-9-3"
+ transform="translate(9722.4732,6535.809)">
+ <rect
+ id="rect6-0-7"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1425.5687"
+ width="2748.6331"
+ y="29.467337"
+ x="80.17308" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-5-7"
+ y="503.71591"
+ x="1460.1007"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="503.71591"
+ x="1460.1007"
+ id="tspan4112-9-0"
+ sodipodi:role="line">Report CPU</tspan><tspan
+ y="837.77039"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan4923">Quiescent</tspan><tspan
+ y="1171.825"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan4925">State</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:40.08654335, 80.17308669;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
+ d="m 7102.5627,11478.337 16.6983,1252.35"
+ id="path4119-0-0"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-size:267.24362183px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="6797.0522"
+ y="9018.6807"
+ id="text4593-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4595-2"
+ x="6797.0522"
+ y="9018.6807">N</tspan></text>
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-9-3-8"
+ transform="translate(-80.17308,14133.68)">
+ <rect
+ id="rect6-0-7-5"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1425.5687"
+ width="2748.6331"
+ y="29.467337"
+ x="80.17308" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-5-7-6"
+ y="841.88086"
+ x="1460.1007"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="841.88086"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan4925-1">Context Switch</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:40.08654562, 80.17309124;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
+ d="m 1362.6256,12823.832 16.6983,1252.369"
+ id="path4119-0-0-7"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
+ d="m 1362.6256,15636.491 16.6983,1252.369"
+ id="path4119-0-0-7-7"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-9-3-8-1"
+ transform="translate(9722.4732,14142.03)">
+ <rect
+ id="rect6-0-7-5-1"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1425.5687"
+ width="2748.6331"
+ y="29.467337"
+ x="80.17308" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-5-7-6-2"
+ y="841.88086"
+ x="1460.1007"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="841.88086"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan4925-1-2">CPU Offline</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:40.08654789, 80.17309578;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
+ d="m 11165.272,12823.832 16.698,1252.369"
+ id="path4119-0-0-7-8"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-9-3-9"
+ transform="translate(-80.17308,16915.618)">
+ <rect
+ id="rect6-0-7-1"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1425.5687"
+ width="2748.6331"
+ y="29.467337"
+ x="80.17308" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-5-7-3"
+ y="505.47754"
+ x="1460.1007"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="505.47754"
+ x="1460.1007"
+ id="tspan4112-9-0-4"
+ sodipodi:role="line">Report CPU</tspan><tspan
+ y="839.53204"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan4925-9">Quiescent</tspan><tspan
+ y="1173.5865"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan3168">State</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
+ d="m 11165.272,15571.534 16.698,1252.369"
+ id="path4119-0-0-7-7-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-9-3-9-2"
+ transform="translate(9722.4732,16850.66)">
+ <rect
+ id="rect6-0-7-1-8"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1425.5687"
+ width="2748.6331"
+ y="29.467337"
+ x="80.17308" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-5-7-3-4"
+ y="503.71591"
+ x="1460.1007"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="503.71591"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan4923-3-2">Report CPU</tspan><tspan
+ y="837.77039"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan4925-9-9">Quiescent</tspan><tspan
+ y="1171.825"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan5239">State</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:40.08654107, 80.17308214;stroke-dashoffset:0"
+ d="m 1353.3524,12832.071 9701.6916,0 100.189,-16.698"
+ id="path5265"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:40.08654022;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
+ d="m 7112.6465,8669.1867 16.6983,1252.369"
+ id="path4119-0-0-7-7-5-7"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4114-9-3-8-1-8-3"
+ transform="translate(5663.1399,9972.3627)">
+ <rect
+ id="rect6-0-7-5-1-1-9"
+ style="fill:#87cfff;stroke:#000000;stroke-width:45.00382233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+ rx="0"
+ height="1425.5687"
+ width="2748.6331"
+ y="29.467337"
+ x="80.17308" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4110-5-7-6-2-4-0"
+ y="841.88086"
+ x="1460.1007"
+ style="font-size:267.24359131px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="841.88086"
+ x="1460.1007"
+ sodipodi:role="line"
+ id="tspan4925-1-2-4-5">reched_cpu()</tspan></text>
+ </g>
+ </g>
+</svg>
diff --git a/Documentation/RCU/Design/Expedited-Grace-Periods/Expedited-Grace-Periods.html b/Documentation/RCU/Design/Expedited-Grace-Periods/Expedited-Grace-Periods.html
new file mode 100644
index 0000000..7a3194c
--- /dev/null
+++ b/Documentation/RCU/Design/Expedited-Grace-Periods/Expedited-Grace-Periods.html
@@ -0,0 +1,626 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+ <html>
+ <head><title>A Tour Through TREE_RCU's Expedited Grace Periods</title>
+ <meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+
+<h2>Introduction</h2>
+
+This document describes RCU's expedited grace periods.
+Unlike RCU's normal grace periods, which accept long latencies to attain
+high efficiency and minimal disturbance, expedited grace periods accept
+lower efficiency and significant disturbance to attain shorter latencies.
+
+<p>
+There are three flavors of RCU (RCU-bh, RCU-preempt, and RCU-sched),
+but only two flavors of expedited grace periods because the RCU-bh
+expedited grace period maps onto the RCU-sched expedited grace period.
+Each of the remaining two implementations is covered in its own section.
+
+<ol>
+<li> <a href="#Expedited Grace Period Design">
+ Expedited Grace Period Design</a>
+<li> <a href="#RCU-preempt Expedited Grace Periods">
+ RCU-preempt Expedited Grace Periods</a>
+<li> <a href="#RCU-sched Expedited Grace Periods">
+ RCU-sched Expedited Grace Periods</a>
+<li> <a href="#Expedited Grace Period and CPU Hotplug">
+ Expedited Grace Period and CPU Hotplug</a>
+<li> <a href="#Expedited Grace Period Refinements">
+ Expedited Grace Period Refinements</a>
+</ol>
+
+<h2><a name="Expedited Grace Period Design">
+Expedited Grace Period Design</a></h2>
+
+<p>
+The expedited RCU grace periods cannot be accused of being subtle,
+given that they for all intents and purposes hammer every CPU that
+has not yet provided a quiescent state for the current expedited
+grace period.
+The one saving grace is that the hammer has grown a bit smaller
+over time: The old call to <tt>try_stop_cpus()</tt> has been
+replaced with a set of calls to <tt>smp_call_function_single()</tt>,
+each of which results in an IPI to the target CPU.
+The corresponding handler function checks the CPU's state, motivating
+a faster quiescent state where possible, and triggering a report
+of that quiescent state.
+As always for RCU, once everything has spent some time in a quiescent
+state, the expedited grace period has completed.
+
+<p>
+The details of the <tt>smp_call_function_single()</tt> handler's
+operation depend on the RCU flavor, as described in the following
+sections.
+
+<h2><a name="RCU-preempt Expedited Grace Periods">
+RCU-preempt Expedited Grace Periods</a></h2>
+
+<p>
+The overall flow of the handling of a given CPU by an RCU-preempt
+expedited grace period is shown in the following diagram:
+
+<p><img src="ExpRCUFlow.svg" alt="ExpRCUFlow.svg" width="55%">
+
+<p>
+The solid arrows denote direct action, for example, a function call.
+The dotted arrows denote indirect action, for example, an IPI
+or a state that is reached after some time.
+
+<p>
+If a given CPU is offline or idle, <tt>synchronize_rcu_expedited()</tt>
+will ignore it because idle and offline CPUs are already residing
+in quiescent states.
+Otherwise, the expedited grace period will use
+<tt>smp_call_function_single()</tt> to send the CPU an IPI, which
+is handled by <tt>sync_rcu_exp_handler()</tt>.
+
+<p>
+However, because this is preemptible RCU, <tt>sync_rcu_exp_handler()</tt>
+can check to see if the CPU is currently running in an RCU read-side
+critical section.
+If not, the handler can immediately report a quiescent state.
+Otherwise, it sets flags so that the outermost <tt>rcu_read_unlock()</tt>
+invocation will provide the needed quiescent-state report.
+This flag-setting avoids the previous forced preemption of all
+CPUs that might have RCU read-side critical sections.
+In addition, this flag-setting is done so as to avoid increasing
+the overhead of the common-case fastpath through the scheduler.
+
+<p>
+Again because this is preemptible RCU, an RCU read-side critical section
+can be preempted.
+When that happens, RCU will enqueue the task, which will the continue to
+block the current expedited grace period until it resumes and finds its
+outermost <tt>rcu_read_unlock()</tt>.
+The CPU will report a quiescent state just after enqueuing the task because
+the CPU is no longer blocking the grace period.
+It is instead the preempted task doing the blocking.
+The list of blocked tasks is managed by <tt>rcu_preempt_ctxt_queue()</tt>,
+which is called from <tt>rcu_preempt_note_context_switch()</tt>, which
+in turn is called from <tt>rcu_note_context_switch()</tt>, which in
+turn is called from the scheduler.
+
+<table>
+<tr><th> </th></tr>
+<tr><th align="left">Quick Quiz:</th></tr>
+<tr><td>
+ Why not just have the expedited grace period check the
+ state of all the CPUs?
+ After all, that would avoid all those real-time-unfriendly IPIs.
+</td></tr>
+<tr><th align="left">Answer:</th></tr>
+<tr><td bgcolor="#ffffff"><font color="ffffff">
+ Because we want the RCU read-side critical sections to run fast,
+ which means no memory barriers.
+ Therefore, it is not possible to safely check the state from some
+ other CPU.
+ And even if it was possible to safely check the state, it would
+ still be necessary to IPI the CPU to safely interact with the
+ upcoming <tt>rcu_read_unlock()</tt> invocation, which means that
+ the remote state testing would not help the worst-case
+ latency that real-time applications care about.
+
+ <p><font color="ffffff">One way to prevent your real-time
+ application from getting hit with these IPIs is to
+ build your kernel with <tt>CONFIG_NO_HZ_FULL=y</tt>.
+ RCU would then perceive the CPU running your application
+ as being idle, and it would be able to safely detect that
+ state without needing to IPI the CPU.
+</font></td></tr>
+<tr><td> </td></tr>
+</table>
+
+<p>
+Please note that this is just the overall flow:
+Additional complications can arise due to races with CPUs going idle
+or offline, among other things.
+
+<h2><a name="RCU-sched Expedited Grace Periods">
+RCU-sched Expedited Grace Periods</a></h2>
+
+<p>
+The overall flow of the handling of a given CPU by an RCU-sched
+expedited grace period is shown in the following diagram:
+
+<p><img src="ExpSchedFlow.svg" alt="ExpSchedFlow.svg" width="55%">
+
+<p>
+As with RCU-preempt's <tt>synchronize_rcu_expedited()</tt>,
+<tt>synchronize_sched_expedited()</tt> ignores offline and
+idle CPUs, again because they are in remotely detectable
+quiescent states.
+However, the <tt>synchronize_rcu_expedited()</tt> handler
+is <tt>sync_sched_exp_handler()</tt>, and because the
+<tt>rcu_read_lock_sched()</tt> and <tt>rcu_read_unlock_sched()</tt>
+leave no trace of their invocation, in general it is not possible to tell
+whether or not the current CPU is in an RCU read-side critical section.
+The best that <tt>sync_sched_exp_handler()</tt> can do is to check
+for idle, on the off-chance that the CPU went idle while the IPI
+was in flight.
+If the CPU is idle, then tt>sync_sched_exp_handler()</tt> reports
+the quiescent state.
+
+<p>
+Otherwise, the handler invokes <tt>resched_cpu()</tt>, which forces
+a future context switch.
+At the time of the context switch, the CPU reports the quiescent state.
+Should the CPU go offline first, it will report the quiescent state
+at that time.
+
+<h2><a name="Expedited Grace Period and CPU Hotplug">
+Expedited Grace Period and CPU Hotplug</a></h2>
+
+<p>
+The expedited nature of expedited grace periods require a much tighter
+interaction with CPU hotplug operations than is required for normal
+grace periods.
+In addition, attempting to IPI offline CPUs will result in splats, but
+failing to IPI online CPUs can result in too-short grace periods.
+Neither option is acceptable in production kernels.
+
+<p>
+The interaction between expedited grace periods and CPU hotplug operations
+is carried out at several levels:
+
+<ol>
+<li> The number of CPUs that have ever been online is tracked
+ by the <tt>rcu_state</tt> structure's <tt>->ncpus</tt>
+ field.
+ The <tt>rcu_state</tt> structure's <tt>->ncpus_snap</tt>
+ field tracks the number of CPUs that have ever been online
+ at the beginning of an RCU expedited grace period.
+ Note that this number never decreases, at least in the absence
+ of a time machine.
+<li> The identities of the CPUs that have ever been online is
+ tracked by the <tt>rcu_node</tt> structure's
+ <tt>->expmaskinitnext</tt> field.
+ The <tt>rcu_node</tt> structure's <tt>->expmaskinit</tt>
+ field tracks the identities of the CPUs that were online
+ at least once at the beginning of the most recent RCU
+ expedited grace period.
+ The <tt>rcu_state</tt> structure's <tt>->ncpus</tt> and
+ <tt>->ncpus_snap</tt> fields are used to detect when
+ new CPUs have come online for the first time, that is,
+ when the <tt>rcu_node</tt> structure's <tt>->expmaskinitnext</tt>
+ field has changed since the beginning of the last RCU
+ expedited grace period, which triggers an update of each
+ <tt>rcu_node</tt> structure's <tt>->expmaskinit</tt>
+ field from its <tt>->expmaskinitnext</tt> field.
+<li> Each <tt>rcu_node</tt> structure's <tt>->expmaskinit</tt>
+ field is used to initialize that structure's
+ <tt>->expmask</tt> at the beginning of each RCU
+ expedited grace period.
+ This means that only those CPUs that have been online at least
+ once will be considered for a given grace period.
+<li> Any CPU that goes offline will clear its bit in its leaf
+ <tt>rcu_node</tt> structure's <tt>->qsmaskinitnext</tt>
+ field, so any CPU with that bit clear can safely be ignored.
+ However, it is possible for a CPU coming online or going offline
+ to have this bit set for some time while <tt>cpu_online</tt>
+ returns <tt>false</tt>.
+<li> For each non-idle CPU that RCU believes is currently online, the grace
+ period invokes <tt>smp_call_function_single()</tt>.
+ If this succeeds, the CPU was fully online.
+ Failure indicates that the CPU is in the process of coming online
+ or going offline, in which case it is necessary to wait for a
+ short time period and try again.
+ The purpose of this wait (or series of waits, as the case may be)
+ is to permit a concurrent CPU-hotplug operation to complete.
+<li> In the case of RCU-sched, one of the last acts of an outgoing CPU
+ is to invoke <tt>rcu_report_dead()</tt>, which
+ reports a quiescent state for that CPU.
+ However, this is likely paranoia-induced redundancy. <!-- @@@ -->
+</ol>
+
+<table>
+<tr><th> </th></tr>
+<tr><th align="left">Quick Quiz:</th></tr>
+<tr><td>
+ Why all the dancing around with multiple counters and masks
+ tracking CPUs that were once online?
+ Why not just have a single set of masks tracking the currently
+ online CPUs and be done with it?
+</td></tr>
+<tr><th align="left">Answer:</th></tr>
+<tr><td bgcolor="#ffffff"><font color="ffffff">
+ Maintaining single set of masks tracking the online CPUs <i>sounds</i>
+ easier, at least until you try working out all the race conditions
+ between grace-period initialization and CPU-hotplug operations.
+ For example, suppose initialization is progressing down the
+ tree while a CPU-offline operation is progressing up the tree.
+ This situation can result in bits set at the top of the tree
+ that have no counterparts at the bottom of the tree.
+ Those bits will never be cleared, which will result in
+ grace-period hangs.
+ In short, that way lies madness, to say nothing of a great many
+ bugs, hangs, and deadlocks.
+
+ <p><font color="ffffff">
+ In contrast, the current multi-mask multi-counter scheme ensures
+ that grace-period initialization will always see consistent masks
+ up and down the tree, which brings significant simplifications
+ over the single-mask method.
+
+ <p><font color="ffffff">
+ This is an instance of
+ <a href="http://www.cs.columbia.edu/~library/TR-repository/reports/reports-1992/cucs-039-92.ps.gz"><font color="ffffff">
+ deferring work in order to avoid synchronization</a>.
+ Lazily recording CPU-hotplug events at the beginning of the next
+ grace period greatly simplifies maintenance of the CPU-tracking
+ bitmasks in the <tt>rcu_node</tt> tree.
+</font></td></tr>
+<tr><td> </td></tr>
+</table>
+
+<h2><a name="Expedited Grace Period Refinements">
+Expedited Grace Period Refinements</a></h2>
+
+<ol>
+<li> <a href="#Idle-CPU Checks">Idle-CPU checks</a>.
+<li> <a href="#Batching via Sequence Counter">
+ Batching via sequence counter</a>.
+<li> <a href="#Funnel Locking and Wait/Wakeup">
+ Funnel locking and wait/wakeup</a>.
+<li> <a href="#Use of Workqueues">Use of Workqueues</a>.
+<li> <a href="#Stall Warnings">Stall warnings</a>.
+</ol>
+
+<h3><a name="Idle-CPU Checks">Idle-CPU Checks</a></h3>
+
+<p>
+Each expedited grace period checks for idle CPUs when initially forming
+the mask of CPUs to be IPIed and again just before IPIing a CPU
+(both checks are carried out by <tt>sync_rcu_exp_select_cpus()</tt>).
+If the CPU is idle at any time between those two times, the CPU will
+not be IPIed.
+Instead, the task pushing the grace period forward will include the
+idle CPUs in the mask passed to <tt>rcu_report_exp_cpu_mult()</tt>.
+
+<p>
+For RCU-sched, there is an additional check for idle in the IPI
+handler, <tt>sync_sched_exp_handler()</tt>.
+If the IPI has interrupted the idle loop, then
+<tt>sync_sched_exp_handler()</tt> invokes <tt>rcu_report_exp_rdp()</tt>
+to report the corresponding quiescent state.
+
+<p>
+For RCU-preempt, there is no specific check for idle in the
+IPI handler (<tt>sync_rcu_exp_handler()</tt>), but because
+RCU read-side critical sections are not permitted within the
+idle loop, if <tt>sync_rcu_exp_handler()</tt> sees that the CPU is within
+RCU read-side critical section, the CPU cannot possibly be idle.
+Otherwise, <tt>sync_rcu_exp_handler()</tt> invokes
+<tt>rcu_report_exp_rdp()</tt> to report the corresponding quiescent
+state, regardless of whether or not that quiescent state was due to
+the CPU being idle.
+
+<p>
+In summary, RCU expedited grace periods check for idle when building
+the bitmask of CPUs that must be IPIed, just before sending each IPI,
+and (either explicitly or implicitly) within the IPI handler.
+
+<h3><a name="Batching via Sequence Counter">
+Batching via Sequence Counter</a></h3>
+
+<p>
+If each grace-period request was carried out separately, expedited
+grace periods would have abysmal scalability and
+problematic high-load characteristics.
+Because each grace-period operation can serve an unlimited number of
+updates, it is important to <i>batch</i> requests, so that a single
+expedited grace-period operation will cover all requests in the
+corresponding batch.
+
+<p>
+This batching is controlled by a sequence counter named
+<tt>->expedited_sequence</tt> in the <tt>rcu_state</tt> structure.
+This counter has an odd value when there is an expedited grace period
+in progress and an even value otherwise, so that dividing the counter
+value by two gives the number of completed grace periods.
+During any given update request, the counter must transition from
+even to odd and then back to even, thus indicating that a grace
+period has elapsed.
+Therefore, if the initial value of the counter is <tt>s</tt>,
+the updater must wait until the counter reaches at least the
+value <tt>(s+3)&~0x1</tt>.
+This counter is managed by the following access functions:
+
+<ol>
+<li> <tt>rcu_exp_gp_seq_start()</tt>, which marks the start of
+ an expedited grace period.
+<li> <tt>rcu_exp_gp_seq_end()</tt>, which marks the end of an
+ expedited grace period.
+<li> <tt>rcu_exp_gp_seq_snap()</tt>, which obtains a snapshot of
+ the counter.
+<li> <tt>rcu_exp_gp_seq_done()</tt>, which returns <tt>true</tt>
+ if a full expedited grace period has elapsed since the
+ corresponding call to <tt>rcu_exp_gp_seq_snap()</tt>.
+</ol>
+
+<p>
+Again, only one request in a given batch need actually carry out
+a grace-period operation, which means there must be an efficient
+way to identify which of many concurrent reqeusts will initiate
+the grace period, and that there be an efficient way for the
+remaining requests to wait for that grace period to complete.
+However, that is the topic of the next section.
+
+<h3><a name="Funnel Locking and Wait/Wakeup">
+Funnel Locking and Wait/Wakeup</a></h3>
+
+<p>
+The natural way to sort out which of a batch of updaters will initiate
+the expedited grace period is to use the <tt>rcu_node</tt> combining
+tree, as implemented by the <tt>exp_funnel_lock()</tt> function.
+The first updater corresponding to a given grace period arriving
+at a given <tt>rcu_node</tt> structure records its desired grace-period
+sequence number in the <tt>->exp_seq_rq</tt> field and moves up
+to the next level in the tree.
+Otherwise, if the <tt>->exp_seq_rq</tt> field already contains
+the sequence number for the desired grace period or some later one,
+the updater blocks on one of four wait queues in the
+<tt>->exp_wq[]</tt> array, using the second-from-bottom
+and third-from bottom bits as an index.
+An <tt>->exp_lock</tt> field in the <tt>rcu_node</tt> structure
+synchronizes access to these fields.
+
+<p>
+An empty <tt>rcu_node</tt> tree is shown in the following diagram,
+with the white cells representing the <tt>->exp_seq_rq</tt> field
+and the red cells representing the elements of the
+<tt>->exp_wq[]</tt> array.
+
+<p><img src="Funnel0.svg" alt="Funnel0.svg" width="75%">
+
+<p>
+The next diagram shows the situation after the arrival of Task A
+and Task B at the leftmost and rightmost leaf <tt>rcu_node</tt>
+structures, respectively.
+The current value of the <tt>rcu_state</tt> structure's
+<tt>->expedited_sequence</tt> field is zero, so adding three and
+clearing the bottom bit results in the value two, which both tasks
+record in the <tt>->exp_seq_rq</tt> field of their respective
+<tt>rcu_node</tt> structures:
+
+<p><img src="Funnel1.svg" alt="Funnel1.svg" width="75%">
+
+<p>
+Each of Tasks A and B will move up to the root
+<tt>rcu_node</tt> structure.
+Suppose that Task A wins, recording its desired grace-period sequence
+number and resulting in the state shown below:
+
+<p><img src="Funnel2.svg" alt="Funnel2.svg" width="75%">
+
+<p>
+Task A now advances to initiate a new grace period, while Task B
+moves up to the root <tt>rcu_node</tt> structure, and, seeing that
+its desired sequence number is already recorded, blocks on
+<tt>->exp_wq[1]</tt>.
+
+<table>
+<tr><th> </th></tr>
+<tr><th align="left">Quick Quiz:</th></tr>
+<tr><td>
+ Why <tt>->exp_wq[1]</tt>?
+ Given that the value of these tasks' desired sequence number is
+ two, so shouldn't they instead block on <tt>->exp_wq[2]</tt>?
+</td></tr>
+<tr><th align="left">Answer:</th></tr>
+<tr><td bgcolor="#ffffff"><font color="ffffff">
+ No.
+
+ <p><font color="ffffff">
+ Recall that the bottom bit of the desired sequence number indicates
+ whether or not a grace period is currently in progress.
+ It is therefore necessary to shift the sequence number right one
+ bit position to obtain the number of the grace period.
+ This results in <tt>->exp_wq[1]</tt>.
+</font></td></tr>
+<tr><td> </td></tr>
+</table>
+
+<p>
+If Tasks C and D also arrive at this point, they will compute the
+same desired grace-period sequence number, and see that both leaf
+<tt>rcu_node</tt> structures already have that value recorded.
+They will therefore block on their respective <tt>rcu_node</tt>
+structures' <tt>->exp_wq[1]</tt> fields, as shown below:
+
+<p><img src="Funnel3.svg" alt="Funnel3.svg" width="75%">
+
+<p>
+Task A now acquires the <tt>rcu_state</tt> structure's
+<tt>->exp_mutex</tt> and initiates the grace period, which
+increments <tt>->expedited_sequence</tt>.
+Therefore, if Tasks E and F arrive, they will compute
+a desired sequence number of 4 and will record this value as
+shown below:
+
+<p><img src="Funnel4.svg" alt="Funnel4.svg" width="75%">
+
+<p>
+Tasks E and F will propagate up the <tt>rcu_node</tt>
+combining tree, with Task F blocking on the root <tt>rcu_node</tt>
+structure and Task E wait for Task A to finish so that
+it can start the next grace period.
+The resulting state is as shown below:
+
+<p><img src="Funnel5.svg" alt="Funnel5.svg" width="75%">
+
+<p>
+Once the grace period completes, Task A
+starts waking up the tasks waiting for this grace period to complete,
+increments the <tt>->expedited_sequence</tt>,
+acquires the <tt>->exp_wake_mutex</tt> and then releases the
+<tt>->exp_mutex</tt>.
+This results in the following state:
+
+<p><img src="Funnel6.svg" alt="Funnel6.svg" width="75%">
+
+<p>
+Task E can then acquire <tt>->exp_mutex</tt> and increment
+<tt>->expedited_sequence</tt> to the value three.
+If new tasks G and H arrive and moves up the combining tree at the
+same time, the state will be as follows:
+
+<p><img src="Funnel7.svg" alt="Funnel7.svg" width="75%">
+
+<p>
+Note that three of the root <tt>rcu_node</tt> structure's
+waitqueues are now occupied.
+However, at some point, Task A will wake up the
+tasks blocked on the <tt>->exp_wq</tt> waitqueues, resulting
+in the following state:
+
+<p><img src="Funnel8.svg" alt="Funnel8.svg" width="75%">
+
+<p>
+Execution will continue with Tasks E and H completing
+their grace periods and carrying out their wakeups.
+
+<table>
+<tr><th> </th></tr>
+<tr><th align="left">Quick Quiz:</th></tr>
+<tr><td>
+ What happens if Task A takes so long to do its wakeups
+ that Task E's grace period completes?
+</td></tr>
+<tr><th align="left">Answer:</th></tr>
+<tr><td bgcolor="#ffffff"><font color="ffffff">
+ Then Task E will block on the <tt>->exp_wake_mutex</tt>,
+ which will also prevent it from releasing <tt>->exp_mutex</tt>,
+ which in turn will prevent the next grace period from starting.
+ This last is important in preventing overflow of the
+ <tt>->exp_wq[]</tt> array.
+</font></td></tr>
+<tr><td> </td></tr>
+</table>
+
+<h3><a name="Use of Workqueues">Use of Workqueues</a></h3>
+
+<p>
+In earlier implementations, the task requesting the expedited
+grace period also drove it to completion.
+This straightforward approach had the disadvantage of needing to
+account for signals sent to user tasks,
+so more recent implemementations use the Linux kernel's
+<a href="https://www.kernel.org/doc/Documentation/workqueue.txt">workqueues</a>.
+
+<p>
+The requesting task still does counter snapshotting and funnel-lock
+processing, but the task reaching the top of the funnel lock
+does a <tt>schedule_work()</tt> (from <tt>_synchronize_rcu_expedited()</tt>
+so that a workqueue kthread does the actual grace-period processing.
+Because workqueue kthreads do not accept signals, grace-period-wait
+processing need not allow for signals.
+
+In addition, this approach allows wakeups for the previous expedited
+grace period to be overlapped with processing for the next expedited
+grace period.
+Because there are only four sets of waitqueues, it is necessary to
+ensure that the previous grace period's wakeups complete before the
+next grace period's wakeups start.
+This is handled by having the <tt>->exp_mutex</tt>
+guard expedited grace-period processing and the
+<tt>->exp_wake_mutex</tt> guard wakeups.
+The key point is that the <tt>->exp_mutex</tt> is not released
+until the first wakeup is complete, which means that the
+<tt>->exp_wake_mutex</tt> has already been acquired at that point.
+This approach ensures that the previous grace period's wakeups can
+be carried out while the current grace period is in process, but
+that these wakeups will complete before the next grace period starts.
+This means that only three waitqueues are required, guaranteeing that
+the four that are provided are sufficient.
+
+<h3><a name="Stall Warnings">Stall Warnings</a></h3>
+
+<p>
+Expediting grace periods does nothing to speed things up when RCU
+readers take too long, and therefore expedited grace periods check
+for stalls just as normal grace periods do.
+
+<table>
+<tr><th> </th></tr>
+<tr><th align="left">Quick Quiz:</th></tr>
+<tr><td>
+ But why not just let the normal grace-period machinery
+ detect the stalls, given that a given reader must block
+ both normal and expedited grace periods?
+</td></tr>
+<tr><th align="left">Answer:</th></tr>
+<tr><td bgcolor="#ffffff"><font color="ffffff">
+ Because it is quite possible that at a given time there
+ is no normal grace period in progress, in which case the
+ normal grace period cannot emit a stall warning.
+</font></td></tr>
+<tr><td> </td></tr>
+</table>
+
+The <tt>synchronize_sched_expedited_wait()</tt> function loops waiting
+for the expedited grace period to end, but with a timeout set to the
+current RCU CPU stall-warning time.
+If this time is exceeded, any CPUs or <tt>rcu_node</tt> structures
+blocking the current grace period are printed.
+Each stall warning results in another pass through the loop, but the
+second and subsequent passes use longer stall times.
+
+<h3><a name="Summary">
+Summary</a></h3>
+
+<p>
+Expedited grace periods use a sequence-number approach to promote
+batching, so that a single grace-period operation can serve numerous
+requests.
+A funnel lock is used to efficiently identify the one task out of
+a concurrent group that will request the grace period.
+All members of the group will block on waitqueues provided in
+the <tt>rcu_node</tt> structure.
+The actual grace-period processing is carried out by a workqueue.
+
+<p>
+CPU-hotplug operations are noted lazily in order to prevent the need
+for tight synchronization between expedited grace periods and
+CPU-hotplug operations.
+The dyntick-idle counters are used to avoid sending IPIs to idle CPUs,
+at least in the common case.
+RCU-preempt and RCU-sched use different IPI handlers and different
+code to respond to the state changes carried out by those handlers,
+but otherwise use common code.
+
+<p>
+Quiescent states are tracked using the <tt>rcu_node</tt> tree,
+and once all necessary quiescent states have been reported,
+all tasks waiting on this expedited grace period are awakened.
+A pair of mutexes are used to allow one grace period's wakeups
+to proceed concurrently with the next grace period's processing.
+
+<p>
+This combination of mechanisms allows expedited grace periods to
+run reasonably efficiently.
+However, for non-time-critical tasks, normal grace periods should be
+used instead because their longer duration permits much higher
+degrees of batching, and thus much lower per-request overheads.
+
+</body></html>
diff --git a/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel0.svg b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel0.svg
new file mode 100644
index 0000000..98af665
--- /dev/null
+++ b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel0.svg
@@ -0,0 +1,275 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="490.05093"
+ height="125.78741"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="Funnel0.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path3792"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path3789"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart-4"
+ style="overflow:visible">
+ <path
+ id="path3789-9"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-4"
+ style="overflow:visible">
+ <path
+ id="path3792-4"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.3670394"
+ inkscape:cx="201.06495"
+ inkscape:cy="-86.548414"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1351"
+ inkscape:window-height="836"
+ inkscape:window-x="171"
+ inkscape:window-y="279"
+ inkscape:window-maximized="0"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-117.08462,-249.92053)">
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot2985"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"><flowRegion
+ id="flowRegion2987"><rect
+ id="rect2989"
+ width="82.85714"
+ height="11.428572"
+ x="240"
+ y="492.36218" /></flowRegion><flowPara
+ id="flowPara2991" /></flowRoot> <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="362.371"
+ y="262.51819"
+ id="text4441"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4443"
+ x="362.371"
+ y="262.51819">->expedited_sequence: 0</tspan></text>
+ <rect
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect3101"
+ width="43.158947"
+ height="26.33428"
+ x="253.55223"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3"
+ width="43.158947"
+ height="26.33428"
+ x="297.04141"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6"
+ width="43.158947"
+ height="26.33428"
+ x="427.509"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7"
+ width="43.158947"
+ height="26.33428"
+ x="384.01981"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7-5"
+ width="43.158947"
+ height="26.33428"
+ x="340.53061"
+ y="275.07489" />
+ <g
+ id="g3997"
+ transform="translate(-0.87295532,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="145.45404"
+ y="360.25174"
+ id="text3013"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015"
+ x="145.45404"
+ y="360.25174"
+ style="font-size:10px">:0</tspan></text>
+ </g>
+ <g
+ id="g3997-7"
+ transform="translate(260.06223,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35-0"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62-9"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9-3"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1-6"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2-0"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="145.45404"
+ y="360.25174"
+ id="text3013-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6"
+ x="145.45404"
+ y="360.25174"
+ style="font-size:10px">:0</tspan></text>
+ </g>
+ </g>
+</svg>
diff --git a/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel1.svg b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel1.svg
new file mode 100644
index 0000000..e0184a3
--- /dev/null
+++ b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel1.svg
@@ -0,0 +1,275 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="490.05093"
+ height="125.78741"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="Funnel1.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path3792"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path3789"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart-4"
+ style="overflow:visible">
+ <path
+ id="path3789-9"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-4"
+ style="overflow:visible">
+ <path
+ id="path3792-4"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.3670394"
+ inkscape:cx="201.06495"
+ inkscape:cy="-86.548414"
+ inkscape:document-units="px"
+ inkscape:current-layer="g3997-7"
+ showgrid="false"
+ inkscape:window-width="1351"
+ inkscape:window-height="836"
+ inkscape:window-x="363"
+ inkscape:window-y="336"
+ inkscape:window-maximized="0"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-117.08462,-249.92053)">
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot2985"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"><flowRegion
+ id="flowRegion2987"><rect
+ id="rect2989"
+ width="82.85714"
+ height="11.428572"
+ x="240"
+ y="492.36218" /></flowRegion><flowPara
+ id="flowPara2991" /></flowRoot> <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="362.371"
+ y="262.51819"
+ id="text4441"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4443"
+ x="362.371"
+ y="262.51819">->expedited_sequence: 0</tspan></text>
+ <rect
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect3101"
+ width="43.158947"
+ height="26.33428"
+ x="253.55223"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3"
+ width="43.158947"
+ height="26.33428"
+ x="297.04141"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6"
+ width="43.158947"
+ height="26.33428"
+ x="427.509"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7"
+ width="43.158947"
+ height="26.33428"
+ x="384.01981"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7-5"
+ width="43.158947"
+ height="26.33428"
+ x="340.53061"
+ y="275.07489" />
+ <g
+ id="g3997"
+ transform="translate(-0.87295532,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="146.00092"
+ y="360.25174"
+ id="text3013"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015"
+ x="146.00092"
+ y="360.25174"
+ style="font-size:10px">A:2</tspan></text>
+ </g>
+ <g
+ id="g3997-7"
+ transform="translate(260.06223,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35-0"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62-9"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9-3"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1-6"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2-0"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="145.54926"
+ y="360.25174"
+ id="text3013-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6"
+ x="145.54926"
+ y="360.25174"
+ style="font-size:10px">B:2</tspan></text>
+ </g>
+ </g>
+</svg>
diff --git a/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel2.svg b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel2.svg
new file mode 100644
index 0000000..1bc3fed
--- /dev/null
+++ b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel2.svg
@@ -0,0 +1,287 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="490.05093"
+ height="125.78741"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="Funnel2.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path3792"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path3789"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart-4"
+ style="overflow:visible">
+ <path
+ id="path3789-9"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-4"
+ style="overflow:visible">
+ <path
+ id="path3792-4"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.3670394"
+ inkscape:cx="114.01552"
+ inkscape:cy="-86.548414"
+ inkscape:document-units="px"
+ inkscape:current-layer="g3997-7"
+ showgrid="false"
+ inkscape:window-width="1351"
+ inkscape:window-height="836"
+ inkscape:window-x="363"
+ inkscape:window-y="336"
+ inkscape:window-maximized="0"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-117.08462,-249.92053)">
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot2985"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"><flowRegion
+ id="flowRegion2987"><rect
+ id="rect2989"
+ width="82.85714"
+ height="11.428572"
+ x="240"
+ y="492.36218" /></flowRegion><flowPara
+ id="flowPara2991" /></flowRoot> <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="362.371"
+ y="262.51819"
+ id="text4441"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4443"
+ x="362.371"
+ y="262.51819">->expedited_sequence: 0</tspan></text>
+ <rect
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect3101"
+ width="43.158947"
+ height="26.33428"
+ x="253.55223"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3"
+ width="43.158947"
+ height="26.33428"
+ x="297.04141"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6"
+ width="43.158947"
+ height="26.33428"
+ x="427.509"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7"
+ width="43.158947"
+ height="26.33428"
+ x="384.01981"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7-5"
+ width="43.158947"
+ height="26.33428"
+ x="340.53061"
+ y="275.07489" />
+ <g
+ id="g3997"
+ transform="translate(-0.87295532,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="146.00092"
+ y="360.25174"
+ id="text3013"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015"
+ x="146.00092"
+ y="360.25174"
+ style="font-size:10px">:2</tspan></text>
+ </g>
+ <g
+ id="g3997-7"
+ transform="translate(260.06223,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35-0"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62-9"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9-3"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1-6"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2-0"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="145.54926"
+ y="360.25174"
+ id="text3013-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6"
+ x="145.54926"
+ y="360.25174"
+ style="font-size:10px">B:2</tspan></text>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="275.59558"
+ y="291.95297"
+ id="text3013-36"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-7"
+ x="275.59558"
+ y="291.95297"
+ style="font-size:10px">A:2</tspan></text>
+ </g>
+</svg>
diff --git a/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel3.svg b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel3.svg
new file mode 100644
index 0000000..6d8a1bf
--- /dev/null
+++ b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel3.svg
@@ -0,0 +1,323 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="490.05093"
+ height="125.78741"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="Funnel3.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path3792"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path3789"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart-4"
+ style="overflow:visible">
+ <path
+ id="path3789-9"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-4"
+ style="overflow:visible">
+ <path
+ id="path3792-4"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.3670394"
+ inkscape:cx="114.01552"
+ inkscape:cy="-86.548414"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1351"
+ inkscape:window-height="836"
+ inkscape:window-x="68"
+ inkscape:window-y="180"
+ inkscape:window-maximized="0"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-117.08462,-249.92053)">
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot2985"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"><flowRegion
+ id="flowRegion2987"><rect
+ id="rect2989"
+ width="82.85714"
+ height="11.428572"
+ x="240"
+ y="492.36218" /></flowRegion><flowPara
+ id="flowPara2991" /></flowRoot> <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="362.371"
+ y="262.51819"
+ id="text4441"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4443"
+ x="362.371"
+ y="262.51819">->expedited_sequence: 0 GP: A</tspan></text>
+ <rect
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect3101"
+ width="43.158947"
+ height="26.33428"
+ x="253.55223"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3"
+ width="43.158947"
+ height="26.33428"
+ x="297.04141"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6"
+ width="43.158947"
+ height="26.33428"
+ x="427.509"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7"
+ width="43.158947"
+ height="26.33428"
+ x="384.01981"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7-5"
+ width="43.158947"
+ height="26.33428"
+ x="340.53061"
+ y="275.07489" />
+ <g
+ id="g3997"
+ transform="translate(-0.87295532,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="146.00092"
+ y="360.25174"
+ id="text3013"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015"
+ x="146.00092"
+ y="360.25174"
+ style="font-size:10px">:2</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="232.51051"
+ y="360.18094"
+ id="text3013-3-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-6"
+ x="232.51051"
+ y="360.18094"
+ style="font-size:10px">C</tspan></text>
+ </g>
+ <g
+ id="g3019"
+ transform="translate(260.06223,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35-0"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62-9"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9-3"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1-6"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2-0"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="145.54926"
+ y="360.25174"
+ id="text3013-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6"
+ x="145.54926"
+ y="360.25174"
+ style="font-size:10px">:2</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="232.31764"
+ y="360.18582"
+ id="text3013-3-3-7"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-6-5"
+ x="232.31764"
+ y="360.18582"
+ style="font-size:10px">D</tspan></text>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="275.59558"
+ y="291.95297"
+ id="text3013-36"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-7"
+ x="275.59558"
+ y="291.95297"
+ style="font-size:10px">:2</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="361.97092"
+ y="291.88705"
+ id="text3013-3-36"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-7"
+ x="361.97092"
+ y="291.88705"
+ style="font-size:10px">B</tspan></text>
+ </g>
+</svg>
diff --git a/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel4.svg b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel4.svg
new file mode 100644
index 0000000..44018fd
--- /dev/null
+++ b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel4.svg
@@ -0,0 +1,323 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="490.05093"
+ height="125.78741"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="Funnel4.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path3792"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path3789"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart-4"
+ style="overflow:visible">
+ <path
+ id="path3789-9"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-4"
+ style="overflow:visible">
+ <path
+ id="path3792-4"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.3670394"
+ inkscape:cx="114.01552"
+ inkscape:cy="-86.548414"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1351"
+ inkscape:window-height="836"
+ inkscape:window-x="68"
+ inkscape:window-y="180"
+ inkscape:window-maximized="0"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-117.08462,-249.92053)">
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot2985"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"><flowRegion
+ id="flowRegion2987"><rect
+ id="rect2989"
+ width="82.85714"
+ height="11.428572"
+ x="240"
+ y="492.36218" /></flowRegion><flowPara
+ id="flowPara2991" /></flowRoot> <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="362.371"
+ y="262.51819"
+ id="text4441"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4443"
+ x="362.371"
+ y="262.51819">->expedited_sequence: 1 GP: A</tspan></text>
+ <rect
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect3101"
+ width="43.158947"
+ height="26.33428"
+ x="253.55223"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3"
+ width="43.158947"
+ height="26.33428"
+ x="297.04141"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6"
+ width="43.158947"
+ height="26.33428"
+ x="427.509"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7"
+ width="43.158947"
+ height="26.33428"
+ x="384.01981"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7-5"
+ width="43.158947"
+ height="26.33428"
+ x="340.53061"
+ y="275.07489" />
+ <g
+ id="g3997"
+ transform="translate(-0.87295532,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="146.00092"
+ y="360.25174"
+ id="text3013"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015"
+ x="146.00092"
+ y="360.25174"
+ style="font-size:10px">E:4</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="232.51051"
+ y="360.18094"
+ id="text3013-3-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-6"
+ x="232.51051"
+ y="360.18094"
+ style="font-size:10px">C</tspan></text>
+ </g>
+ <g
+ id="g3019"
+ transform="translate(260.06223,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35-0"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62-9"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9-3"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1-6"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2-0"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="145.54926"
+ y="360.25174"
+ id="text3013-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6"
+ x="145.54926"
+ y="360.25174"
+ style="font-size:10px">F:4</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="232.31764"
+ y="360.18582"
+ id="text3013-3-3-7"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-6-5"
+ x="232.31764"
+ y="360.18582"
+ style="font-size:10px">D</tspan></text>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="275.59558"
+ y="291.95297"
+ id="text3013-36"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-7"
+ x="275.59558"
+ y="291.95297"
+ style="font-size:10px">:2</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="361.97092"
+ y="291.88705"
+ id="text3013-3-36"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-7"
+ x="361.97092"
+ y="291.88705"
+ style="font-size:10px">B</tspan></text>
+ </g>
+</svg>
diff --git a/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel5.svg b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel5.svg
new file mode 100644
index 0000000..e5eef50
--- /dev/null
+++ b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel5.svg
@@ -0,0 +1,335 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="490.05093"
+ height="125.78741"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="Funnel5.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path3792"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path3789"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart-4"
+ style="overflow:visible">
+ <path
+ id="path3789-9"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-4"
+ style="overflow:visible">
+ <path
+ id="path3792-4"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.3670394"
+ inkscape:cx="114.01552"
+ inkscape:cy="-86.548414"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1351"
+ inkscape:window-height="836"
+ inkscape:window-x="68"
+ inkscape:window-y="180"
+ inkscape:window-maximized="0"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-117.08462,-249.92053)">
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot2985"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"><flowRegion
+ id="flowRegion2987"><rect
+ id="rect2989"
+ width="82.85714"
+ height="11.428572"
+ x="240"
+ y="492.36218" /></flowRegion><flowPara
+ id="flowPara2991" /></flowRoot> <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="362.371"
+ y="262.51819"
+ id="text4441"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4443"
+ x="362.371"
+ y="262.51819">->expedited_sequence: 1 GP: A,E</tspan></text>
+ <rect
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect3101"
+ width="43.158947"
+ height="26.33428"
+ x="253.55223"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3"
+ width="43.158947"
+ height="26.33428"
+ x="297.04141"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6"
+ width="43.158947"
+ height="26.33428"
+ x="427.509"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7"
+ width="43.158947"
+ height="26.33428"
+ x="384.01981"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7-5"
+ width="43.158947"
+ height="26.33428"
+ x="340.53061"
+ y="275.07489" />
+ <g
+ id="g3997"
+ transform="translate(-0.87295532,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="146.00092"
+ y="360.25174"
+ id="text3013"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015"
+ x="146.00092"
+ y="360.25174"
+ style="font-size:10px">:4</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="232.51051"
+ y="360.18094"
+ id="text3013-3-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-6"
+ x="232.51051"
+ y="360.18094"
+ style="font-size:10px">C</tspan></text>
+ </g>
+ <g
+ id="g3019"
+ transform="translate(260.06223,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35-0"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62-9"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9-3"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1-6"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2-0"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="145.54926"
+ y="360.25174"
+ id="text3013-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6"
+ x="145.54926"
+ y="360.25174"
+ style="font-size:10px">:4</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="232.31764"
+ y="360.18582"
+ id="text3013-3-3-7"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-6-5"
+ x="232.31764"
+ y="360.18582"
+ style="font-size:10px">D</tspan></text>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="275.59558"
+ y="291.95297"
+ id="text3013-36"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-7"
+ x="275.59558"
+ y="291.95297"
+ style="font-size:10px">:4</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="361.97092"
+ y="291.88705"
+ id="text3013-3-36"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-7"
+ x="361.97092"
+ y="291.88705"
+ style="font-size:10px">B</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="405.40396"
+ y="291.88705"
+ id="text3013-3-36-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-7-6"
+ x="405.40396"
+ y="291.88705"
+ style="font-size:10px">F</tspan></text>
+ </g>
+</svg>
diff --git a/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel6.svg b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel6.svg
new file mode 100644
index 0000000..fbd2c18
--- /dev/null
+++ b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel6.svg
@@ -0,0 +1,335 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="490.05093"
+ height="125.78741"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="Funnel6.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path3792"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path3789"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart-4"
+ style="overflow:visible">
+ <path
+ id="path3789-9"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-4"
+ style="overflow:visible">
+ <path
+ id="path3792-4"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.3670394"
+ inkscape:cx="114.01552"
+ inkscape:cy="-86.548414"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1351"
+ inkscape:window-height="836"
+ inkscape:window-x="68"
+ inkscape:window-y="180"
+ inkscape:window-maximized="0"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-117.08462,-249.92053)">
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot2985"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"><flowRegion
+ id="flowRegion2987"><rect
+ id="rect2989"
+ width="82.85714"
+ height="11.428572"
+ x="240"
+ y="492.36218" /></flowRegion><flowPara
+ id="flowPara2991" /></flowRoot> <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="362.371"
+ y="262.51819"
+ id="text4441"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4443"
+ x="362.371"
+ y="262.51819">->expedited_sequence: 2 GP: E Wakeup: A</tspan></text>
+ <rect
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect3101"
+ width="43.158947"
+ height="26.33428"
+ x="253.55223"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3"
+ width="43.158947"
+ height="26.33428"
+ x="297.04141"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6"
+ width="43.158947"
+ height="26.33428"
+ x="427.509"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7"
+ width="43.158947"
+ height="26.33428"
+ x="384.01981"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7-5"
+ width="43.158947"
+ height="26.33428"
+ x="340.53061"
+ y="275.07489" />
+ <g
+ id="g3997"
+ transform="translate(-0.87295532,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="146.00092"
+ y="360.25174"
+ id="text3013"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015"
+ x="146.00092"
+ y="360.25174"
+ style="font-size:10px">:4</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="232.51051"
+ y="360.18094"
+ id="text3013-3-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-6"
+ x="232.51051"
+ y="360.18094"
+ style="font-size:10px">C</tspan></text>
+ </g>
+ <g
+ id="g3019"
+ transform="translate(260.06223,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35-0"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62-9"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9-3"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1-6"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2-0"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="145.54926"
+ y="360.25174"
+ id="text3013-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6"
+ x="145.54926"
+ y="360.25174"
+ style="font-size:10px">:4</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="232.31764"
+ y="360.18582"
+ id="text3013-3-3-7"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-6-5"
+ x="232.31764"
+ y="360.18582"
+ style="font-size:10px">D</tspan></text>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="275.59558"
+ y="291.95297"
+ id="text3013-36"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-7"
+ x="275.59558"
+ y="291.95297"
+ style="font-size:10px">:4</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="361.97092"
+ y="291.88705"
+ id="text3013-3-36"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-7"
+ x="361.97092"
+ y="291.88705"
+ style="font-size:10px">B</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="405.40396"
+ y="291.88705"
+ id="text3013-3-36-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-7-6"
+ x="405.40396"
+ y="291.88705"
+ style="font-size:10px">F</tspan></text>
+ </g>
+</svg>
diff --git a/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel7.svg b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel7.svg
new file mode 100644
index 0000000..502e159
--- /dev/null
+++ b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel7.svg
@@ -0,0 +1,347 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="490.05093"
+ height="125.78741"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="Funnel7.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path3792"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path3789"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart-4"
+ style="overflow:visible">
+ <path
+ id="path3789-9"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-4"
+ style="overflow:visible">
+ <path
+ id="path3792-4"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.3670394"
+ inkscape:cx="114.01552"
+ inkscape:cy="-86.548414"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1351"
+ inkscape:window-height="836"
+ inkscape:window-x="68"
+ inkscape:window-y="180"
+ inkscape:window-maximized="0"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-117.08462,-249.92053)">
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot2985"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"><flowRegion
+ id="flowRegion2987"><rect
+ id="rect2989"
+ width="82.85714"
+ height="11.428572"
+ x="240"
+ y="492.36218" /></flowRegion><flowPara
+ id="flowPara2991" /></flowRoot> <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="362.371"
+ y="262.51819"
+ id="text4441"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4443"
+ x="362.371"
+ y="262.51819">->expedited_sequence: 3 GP: E,H Wakeup: A</tspan></text>
+ <rect
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect3101"
+ width="43.158947"
+ height="26.33428"
+ x="253.55223"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3"
+ width="43.158947"
+ height="26.33428"
+ x="297.04141"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect3101-3-6"
+ width="43.158947"
+ height="26.33428"
+ x="427.509"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7"
+ width="43.158947"
+ height="26.33428"
+ x="384.01981"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7-5"
+ width="43.158947"
+ height="26.33428"
+ x="340.53061"
+ y="275.07489" />
+ <g
+ id="g3997"
+ transform="translate(-0.87295532,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="146.00092"
+ y="360.25174"
+ id="text3013"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015"
+ x="146.00092"
+ y="360.25174"
+ style="font-size:10px">:4</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="232.51051"
+ y="360.18094"
+ id="text3013-3-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-6"
+ x="232.51051"
+ y="360.18094"
+ style="font-size:10px">C</tspan></text>
+ </g>
+ <g
+ id="g3019"
+ transform="translate(260.06223,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35-0"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62-9"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9-3"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1-6"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2-0"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="145.54926"
+ y="360.25174"
+ id="text3013-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6"
+ x="145.54926"
+ y="360.25174"
+ style="font-size:10px">:6</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="232.31764"
+ y="360.18582"
+ id="text3013-3-3-7"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-6-5"
+ x="232.31764"
+ y="360.18582"
+ style="font-size:10px">D</tspan></text>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="275.59558"
+ y="291.95297"
+ id="text3013-36"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-7"
+ x="275.59558"
+ y="291.95297"
+ style="font-size:10px">:6</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="361.97092"
+ y="291.88705"
+ id="text3013-3-36"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-7"
+ x="361.97092"
+ y="291.88705"
+ style="font-size:10px">B</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="405.40396"
+ y="291.88705"
+ id="text3013-3-36-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-7-6"
+ x="405.40396"
+ y="291.88705"
+ style="font-size:10px">F</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="449.22031"
+ y="291.88217"
+ id="text3013-3-36-3-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-7-6-6"
+ x="449.22031"
+ y="291.88217"
+ style="font-size:10px">G</tspan></text>
+ </g>
+</svg>
diff --git a/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel8.svg b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel8.svg
new file mode 100644
index 0000000..6774015
--- /dev/null
+++ b/Documentation/RCU/Design/Expedited-Grace-Periods/Funnel8.svg
@@ -0,0 +1,311 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="490.05093"
+ height="125.78741"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="Funnel8.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path3792"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path3789"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart-4"
+ style="overflow:visible">
+ <path
+ id="path3789-9"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-4"
+ style="overflow:visible">
+ <path
+ id="path3792-4"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.3670394"
+ inkscape:cx="114.01552"
+ inkscape:cy="-86.548414"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1351"
+ inkscape:window-height="836"
+ inkscape:window-x="68"
+ inkscape:window-y="180"
+ inkscape:window-maximized="0"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-117.08462,-249.92053)">
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot2985"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"><flowRegion
+ id="flowRegion2987"><rect
+ id="rect2989"
+ width="82.85714"
+ height="11.428572"
+ x="240"
+ y="492.36218" /></flowRegion><flowPara
+ id="flowPara2991" /></flowRoot> <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="362.371"
+ y="262.51819"
+ id="text4441"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4443"
+ x="362.371"
+ y="262.51819">->expedited_sequence: 3 GP: E,H</tspan></text>
+ <rect
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect3101"
+ width="43.158947"
+ height="26.33428"
+ x="253.55223"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3"
+ width="43.158947"
+ height="26.33428"
+ x="297.04141"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect3101-3-6"
+ width="43.158947"
+ height="26.33428"
+ x="427.509"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7"
+ width="43.158947"
+ height="26.33428"
+ x="384.01981"
+ y="275.07489" />
+ <rect
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect3101-3-6-7-5"
+ width="43.158947"
+ height="26.33428"
+ x="340.53061"
+ y="275.07489" />
+ <g
+ id="g3997"
+ transform="translate(-0.87295532,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2"
+ style="fill:#ff8282;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="146.00092"
+ y="360.25174"
+ id="text3013"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015"
+ x="146.00092"
+ y="360.25174"
+ style="font-size:10px">:4</tspan></text>
+ </g>
+ <g
+ id="g3019"
+ transform="translate(260.06223,0)">
+ <rect
+ y="343.37366"
+ x="123.95757"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-35-0"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="167.44673"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-62-9"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="297.91437"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-9-3"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="254.42516"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-1-6"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <rect
+ y="343.37366"
+ x="210.93593"
+ height="26.33428"
+ width="43.158947"
+ id="rect3101-3-6-7-5-2-0"
+ style="fill:#ff8282;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="145.54926"
+ y="360.25174"
+ id="text3013-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6"
+ x="145.54926"
+ y="360.25174"
+ style="font-size:10px">:6</tspan></text>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="275.59558"
+ y="291.95297"
+ id="text3013-36"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-7"
+ x="275.59558"
+ y="291.95297"
+ style="font-size:10px">:6</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="405.40396"
+ y="291.88705"
+ id="text3013-3-36-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-7-6"
+ x="405.40396"
+ y="291.88705"
+ style="font-size:10px">F</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="449.22031"
+ y="291.88217"
+ id="text3013-3-36-3-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015-6-7-6-6"
+ x="449.22031"
+ y="291.88217"
+ style="font-size:10px">G</tspan></text>
+ </g>
+</svg>
diff --git a/Documentation/RCU/Design/Requirements/Requirements.html b/Documentation/RCU/Design/Requirements/Requirements.html
index 39bcb74..2159349 100644
--- a/Documentation/RCU/Design/Requirements/Requirements.html
+++ b/Documentation/RCU/Design/Requirements/Requirements.html
@@ -1480,7 +1480,7 @@
<p>
Furthermore, uncertainty about external state is inherent in many cases.
-For example, a pair of veternarians might use heartbeat to determine
+For example, a pair of veterinarians might use heartbeat to determine
whether or not a given cat was alive.
But how long should they wait after the last heartbeat to decide that
the cat is in fact dead?
@@ -1489,9 +1489,9 @@
and life more than 100 times per minute.
Moreover, just as with human beings, a cat's heart might stop for
some period of time, so the exact wait period is a judgment call.
-One of our pair of veternarians might wait 30 seconds before pronouncing
+One of our pair of veterinarians might wait 30 seconds before pronouncing
the cat dead, while the other might insist on waiting a full minute.
-The two veternarians would then disagree on the state of the cat during
+The two veterinarians would then disagree on the state of the cat during
the final 30 seconds of the minute following the last heartbeat.
<p>
@@ -1945,7 +1945,7 @@
<ol>
<li> It is all too easy to forget to use <tt>rcu_read_lock()</tt>
everywhere that it is needed, so kernels built with
- <tt>CONFIG_PROVE_RCU=y</tt> will spat if
+ <tt>CONFIG_PROVE_RCU=y</tt> will splat if
<tt>rcu_dereference()</tt> is used outside of an
RCU read-side critical section.
Update-side code can use <tt>rcu_dereference_protected()</tt>,
@@ -2421,7 +2421,7 @@
<li> Blocking is prohibited.
In practice, this is not a serious restriction given that idle
tasks are prohibited from blocking to begin with.
-<li> Although nesting <tt>RCU_NONIDLE()</tt> is permited, they cannot
+<li> Although nesting <tt>RCU_NONIDLE()</tt> is permitted, they cannot
nest indefinitely deeply.
However, given that they can be nested on the order of a million
deep, even on 32-bit systems, this should not be a serious
@@ -2885,7 +2885,7 @@
<h3><a name="Tasks RCU">Tasks RCU</a></h3>
<p>
-Some forms of tracing use “tramopolines” to handle the
+Some forms of tracing use “trampolines” to handle the
binary rewriting required to install different types of probes.
It would be good to be able to free old trampolines, which sounds
like a job for some form of RCU.
diff --git a/Documentation/RCU/trace.txt b/Documentation/RCU/trace.txt
index 00a3a38..6549012 100644
--- a/Documentation/RCU/trace.txt
+++ b/Documentation/RCU/trace.txt
@@ -237,7 +237,7 @@
The output of "cat rcu/rcu_preempt/rcuexp" looks as follows:
-s=21872 wd1=0 wd2=0 wd3=5 n=0 enq=0 sc=21872
+s=21872 wd1=0 wd2=0 wd3=5 enq=0 sc=21872
These fields are as follows:
@@ -249,9 +249,6 @@
completed an expedited grace period that satisfies the attempted
request. "Our work is done."
-o "n" is number of times that a concurrent CPU-hotplug operation
- forced a fallback to a normal grace period.
-
o "enq" is the number of quiescent states still outstanding.
o "sc" is the number of times that the attempt to start a
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index be7c0d9..635d111 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -549,15 +549,6 @@
loops can be debugged more effectively on production
systems.
- clocksource.arm_arch_timer.fsl-a008585=
- [ARM64]
- Format: <bool>
- Enable/disable the workaround of Freescale/NXP
- erratum A-008585. This can be useful for KVM
- guests, if the guest device tree doesn't show the
- erratum. If unspecified, the workaround is
- enabled based on the device tree.
-
clearcpuid=BITNUM [X86]
Disable CPUID feature X for the kernel. See
arch/x86/include/asm/cpufeatures.h for the valid bit
@@ -3278,6 +3269,13 @@
Lazy RCU callbacks are those which RCU can
prove do nothing more than free memory.
+ rcutree.rcu_kick_kthreads= [KNL]
+ Cause the grace-period kthread to get an extra
+ wake_up() if it sleeps three times longer than
+ it should at force-quiescent-state time.
+ This wake_up() will be accompanied by a
+ WARN_ONCE() splat and an ftrace_dump().
+
rcuperf.gp_exp= [KNL]
Measure performance of expedited synchronous
grace-period primitives.
@@ -3563,6 +3561,10 @@
rhash_entries= [KNL,NET]
Set number of hash buckets for route cache
+ ring3mwait=disable
+ [KNL] Disable ring 3 MONITOR/MWAIT feature on supported
+ CPUs.
+
ro [KNL] Mount root device read-only on boot
rodata= [KNL]
diff --git a/Documentation/admin-guide/ras.rst b/Documentation/admin-guide/ras.rst
index d71340e8..9939348 100644
--- a/Documentation/admin-guide/ras.rst
+++ b/Documentation/admin-guide/ras.rst
@@ -438,11 +438,13 @@
│ │ ├── ce_count
│ │ ├── ce_noinfo_count
│ │ ├── dimm0
+ │ │ │ ├── dimm_ce_count
│ │ │ ├── dimm_dev_type
│ │ │ ├── dimm_edac_mode
│ │ │ ├── dimm_label
│ │ │ ├── dimm_location
│ │ │ ├── dimm_mem_type
+ │ │ │ ├── dimm_ue_count
│ │ │ ├── size
│ │ │ └── uevent
│ │ ├── max_location
@@ -457,11 +459,13 @@
│ │ ├── ce_count
│ │ ├── ce_noinfo_count
│ │ ├── dimm0
+ │ │ │ ├── dimm_ce_count
│ │ │ ├── dimm_dev_type
│ │ │ ├── dimm_edac_mode
│ │ │ ├── dimm_label
│ │ │ ├── dimm_location
│ │ │ ├── dimm_mem_type
+ │ │ │ ├── dimm_ue_count
│ │ │ ├── size
│ │ │ └── uevent
│ │ ├── max_location
@@ -483,6 +487,22 @@
This attribute file displays, in count of megabytes, the memory
that this csrow contains.
+- ``dimm_ue_count`` - Uncorrectable Errors count attribute file
+
+ This attribute file displays the total count of uncorrectable
+ errors that have occurred on this DIMM. If panic_on_ue is set
+ this counter will not have a chance to increment, since EDAC
+ will panic the system.
+
+- ``dimm_ce_count`` - Correctable Errors count attribute file
+
+ This attribute file displays the total count of correctable
+ errors that have occurred on this DIMM. This count is very
+ important to examine. CEs provide early indications that a
+ DIMM is beginning to fail. This count field should be
+ monitored for non-zero values and report such information
+ to the system administrator.
+
- ``dimm_dev_type`` - Device type attribute file
This attribute file will display what type of DRAM device is
diff --git a/Documentation/cpu-freq/core.txt b/Documentation/cpu-freq/core.txt
index 4bc7287..978463a 100644
--- a/Documentation/cpu-freq/core.txt
+++ b/Documentation/cpu-freq/core.txt
@@ -8,6 +8,8 @@
Dominik Brodowski <linux@brodo.de>
David Kimdon <dwhedon@debian.org>
+ Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ Viresh Kumar <viresh.kumar@linaro.org>
@@ -36,10 +38,11 @@
kernel "constant" loops_per_jiffy is updated on frequency changes
here.
-Reference counting is done by cpufreq_get_cpu and cpufreq_put_cpu,
-which make sure that the cpufreq processor driver is correctly
-registered with the core, and will not be unloaded until
-cpufreq_put_cpu is called.
+Reference counting of the cpufreq policies is done by cpufreq_cpu_get
+and cpufreq_cpu_put, which make sure that the cpufreq driver is
+correctly registered with the core, and will not be unloaded until
+cpufreq_put_cpu is called. That also ensures that the respective cpufreq
+policy doesn't get freed while being used.
2. CPUFreq notifiers
====================
@@ -69,18 +72,16 @@
The phase is specified in the second argument to the notifier.
The third argument, a void *pointer, points to a struct cpufreq_policy
-consisting of five values: cpu, min, max, policy and max_cpu_freq. min
-and max are the lower and upper frequencies (in kHz) of the new
-policy, policy the new policy, cpu the number of the affected CPU; and
-max_cpu_freq the maximum supported CPU frequency. This value is given
-for informational purposes only.
+consisting of several values, including min, max (the lower and upper
+frequencies (in kHz) of the new policy).
2.2 CPUFreq transition notifiers
--------------------------------
-These are notified twice when the CPUfreq driver switches the CPU core
-frequency and this change has any external implications.
+These are notified twice for each online CPU in the policy, when the
+CPUfreq driver switches the CPU core frequency and this change has no
+any external implications.
The second argument specifies the phase - CPUFREQ_PRECHANGE or
CPUFREQ_POSTCHANGE.
@@ -90,6 +91,7 @@
cpu - number of the affected CPU
old - old frequency
new - new frequency
+flags - flags of the cpufreq driver
3. CPUFreq Table Generation with Operating Performance Point (OPP)
==================================================================
diff --git a/Documentation/cpu-freq/cpu-drivers.txt b/Documentation/cpu-freq/cpu-drivers.txt
index 772b94f..f71e6be 100644
--- a/Documentation/cpu-freq/cpu-drivers.txt
+++ b/Documentation/cpu-freq/cpu-drivers.txt
@@ -9,6 +9,8 @@
Dominik Brodowski <linux@brodo.de>
+ Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ Viresh Kumar <viresh.kumar@linaro.org>
@@ -49,49 +51,65 @@
What shall this struct cpufreq_driver contain?
-cpufreq_driver.name - The name of this driver.
+ .name - The name of this driver.
-cpufreq_driver.init - A pointer to the per-CPU initialization
- function.
+ .init - A pointer to the per-policy initialization function.
-cpufreq_driver.verify - A pointer to a "verification" function.
+ .verify - A pointer to a "verification" function.
-cpufreq_driver.setpolicy _or_
-cpufreq_driver.target/
-target_index - See below on the differences.
+ .setpolicy _or_ .fast_switch _or_ .target _or_ .target_index - See
+ below on the differences.
And optionally
-cpufreq_driver.exit - A pointer to a per-CPU cleanup
- function called during CPU_POST_DEAD
- phase of cpu hotplug process.
+ .flags - Hints for the cpufreq core.
-cpufreq_driver.stop_cpu - A pointer to a per-CPU stop function
- called during CPU_DOWN_PREPARE phase of
- cpu hotplug process.
+ .driver_data - cpufreq driver specific data.
-cpufreq_driver.resume - A pointer to a per-CPU resume function
- which is called with interrupts disabled
- and _before_ the pre-suspend frequency
- and/or policy is restored by a call to
- ->target/target_index or ->setpolicy.
+ .resolve_freq - Returns the most appropriate frequency for a target
+ frequency. Doesn't change the frequency though.
-cpufreq_driver.attr - A pointer to a NULL-terminated list of
- "struct freq_attr" which allow to
- export values to sysfs.
+ .get_intermediate and target_intermediate - Used to switch to stable
+ frequency while changing CPU frequency.
-cpufreq_driver.get_intermediate
-and target_intermediate Used to switch to stable frequency while
- changing CPU frequency.
+ .get - Returns current frequency of the CPU.
+
+ .bios_limit - Returns HW/BIOS max frequency limitations for the CPU.
+
+ .exit - A pointer to a per-policy cleanup function called during
+ CPU_POST_DEAD phase of cpu hotplug process.
+
+ .stop_cpu - A pointer to a per-policy stop function called during
+ CPU_DOWN_PREPARE phase of cpu hotplug process.
+
+ .suspend - A pointer to a per-policy suspend function which is called
+ with interrupts disabled and _after_ the governor is stopped for the
+ policy.
+
+ .resume - A pointer to a per-policy resume function which is called
+ with interrupts disabled and _before_ the governor is started again.
+
+ .ready - A pointer to a per-policy ready function which is called after
+ the policy is fully initialized.
+
+ .attr - A pointer to a NULL-terminated list of "struct freq_attr" which
+ allow to export values to sysfs.
+
+ .boost_enabled - If set, boost frequencies are enabled.
+
+ .set_boost - A pointer to a per-policy function to enable/disable boost
+ frequencies.
1.2 Per-CPU Initialization
--------------------------
Whenever a new CPU is registered with the device model, or after the
-cpufreq driver registers itself, the per-CPU initialization function
-cpufreq_driver.init is called. It takes a struct cpufreq_policy
-*policy as argument. What to do now?
+cpufreq driver registers itself, the per-policy initialization function
+cpufreq_driver.init is called if no cpufreq policy existed for the CPU.
+Note that the .init() and .exit() routines are called only once for the
+policy and not for each CPU managed by the policy. It takes a struct
+cpufreq_policy *policy as argument. What to do now?
If necessary, activate the CPUfreq support on your CPU.
@@ -117,47 +135,45 @@
cpufreq_driver.setpolicy or
cpufreq_driver.target/target_index is called
with these values.
+policy->cpus Update this with the masks of the
+ (online + offline) CPUs that do DVFS
+ along with this CPU (i.e. that share
+ clock/voltage rails with it).
For setting some of these values (cpuinfo.min[max]_freq, policy->min[max]), the
frequency table helpers might be helpful. See the section 2 for more information
on them.
-SMP systems normally have same clock source for a group of cpus. For these the
-.init() would be called only once for the first online cpu. Here the .init()
-routine must initialize policy->cpus with mask of all possible cpus (Online +
-Offline) that share the clock. Then the core would copy this mask onto
-policy->related_cpus and will reset policy->cpus to carry only online cpus.
-
1.3 verify
-------------
+----------
When the user decides a new policy (consisting of
"policy,governor,min,max") shall be set, this policy must be validated
so that incompatible values can be corrected. For verifying these
-values, a frequency table helper and/or the
-cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned
-int min_freq, unsigned int max_freq) function might be helpful. See
-section 2 for details on frequency table helpers.
+values cpufreq_verify_within_limits(struct cpufreq_policy *policy,
+unsigned int min_freq, unsigned int max_freq) function might be helpful.
+See section 2 for details on frequency table helpers.
You need to make sure that at least one valid frequency (or operating
range) is within policy->min and policy->max. If necessary, increase
policy->max first, and only if this is no solution, decrease policy->min.
-1.4 target/target_index or setpolicy?
-----------------------------
+1.4 target or target_index or setpolicy or fast_switch?
+-------------------------------------------------------
Most cpufreq drivers or even most cpu frequency scaling algorithms
-only allow the CPU to be set to one frequency. For these, you use the
-->target/target_index call.
+only allow the CPU frequency to be set to predefined fixed values. For
+these, you use the ->target(), ->target_index() or ->fast_switch()
+callbacks.
-Some cpufreq-capable processors switch the frequency between certain
-limits on their own. These shall use the ->setpolicy call
+Some cpufreq capable processors switch the frequency between certain
+limits on their own. These shall use the ->setpolicy() callback.
1.5. target/target_index
--------------
+------------------------
The target_index call has two arguments: struct cpufreq_policy *policy,
and unsigned int index (into the exposed frequency table).
@@ -186,9 +202,20 @@
Here again the frequency table helper might assist you - see section 2
for details.
+1.6. fast_switch
+----------------
-1.6 setpolicy
----------------
+This function is used for frequency switching from scheduler's context.
+Not all drivers are expected to implement it, as sleeping from within
+this callback isn't allowed. This callback must be highly optimized to
+do switching as fast as possible.
+
+This function has two arguments: struct cpufreq_policy *policy and
+unsigned int target_frequency.
+
+
+1.7 setpolicy
+-------------
The setpolicy call only takes a struct cpufreq_policy *policy as
argument. You need to set the lower limit of the in-processor or
@@ -198,7 +225,7 @@
powersaving-oriented setting when CPUFREQ_POLICY_POWERSAVE. Also check
the reference implementation in drivers/cpufreq/longrun.c
-1.7 get_intermediate and target_intermediate
+1.8 get_intermediate and target_intermediate
--------------------------------------------
Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION unset.
@@ -222,42 +249,36 @@
As most cpufreq processors only allow for being set to a few specific
frequencies, a "frequency table" with some functions might assist in
-some work of the processor driver. Such a "frequency table" consists
-of an array of struct cpufreq_frequency_table entries, with any value in
-"driver_data" you want to use, and the corresponding frequency in
-"frequency". At the end of the table, you need to add a
-cpufreq_frequency_table entry with frequency set to CPUFREQ_TABLE_END. And
-if you want to skip one entry in the table, set the frequency to
-CPUFREQ_ENTRY_INVALID. The entries don't need to be in ascending
-order.
+some work of the processor driver. Such a "frequency table" consists of
+an array of struct cpufreq_frequency_table entries, with driver specific
+values in "driver_data", the corresponding frequency in "frequency" and
+flags set. At the end of the table, you need to add a
+cpufreq_frequency_table entry with frequency set to CPUFREQ_TABLE_END.
+And if you want to skip one entry in the table, set the frequency to
+CPUFREQ_ENTRY_INVALID. The entries don't need to be in sorted in any
+particular order, but if they are cpufreq core will do DVFS a bit
+quickly for them as search for best match is faster.
-By calling cpufreq_table_validate_and_show(struct cpufreq_policy *policy,
- struct cpufreq_frequency_table *table);
-the cpuinfo.min_freq and cpuinfo.max_freq values are detected, and
-policy->min and policy->max are set to the same values. This is
-helpful for the per-CPU initialization stage.
+By calling cpufreq_table_validate_and_show(), the cpuinfo.min_freq and
+cpuinfo.max_freq values are detected, and policy->min and policy->max
+are set to the same values. This is helpful for the per-CPU
+initialization stage.
-int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
- struct cpufreq_frequency_table *table);
-assures that at least one valid frequency is within policy->min and
-policy->max, and all other criteria are met. This is helpful for the
-->verify call.
+cpufreq_frequency_table_verify() assures that at least one valid
+frequency is within policy->min and policy->max, and all other criteria
+are met. This is helpful for the ->verify call.
-int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
- unsigned int target_freq,
- unsigned int relation);
-
-is the corresponding frequency table helper for the ->target
-stage. Just pass the values to this function, and this function
-returns the number of the frequency table entry which contains
-the frequency the CPU shall be set to.
+cpufreq_frequency_table_target() is the corresponding frequency table
+helper for the ->target stage. Just pass the values to this function,
+and this function returns the of the frequency table entry which
+contains the frequency the CPU shall be set to.
The following macros can be used as iterators over cpufreq_frequency_table:
cpufreq_for_each_entry(pos, table) - iterates over all entries of frequency
table.
-cpufreq-for_each_valid_entry(pos, table) - iterates over all entries,
+cpufreq_for_each_valid_entry(pos, table) - iterates over all entries,
excluding CPUFREQ_ENTRY_INVALID frequencies.
Use arguments "pos" - a cpufreq_frequency_table * as a loop cursor and
"table" - the cpufreq_frequency_table * you want to iterate over.
diff --git a/Documentation/cpu-freq/cpufreq-stats.txt b/Documentation/cpu-freq/cpufreq-stats.txt
index 3c355f6..2bbe207 100644
--- a/Documentation/cpu-freq/cpufreq-stats.txt
+++ b/Documentation/cpu-freq/cpufreq-stats.txt
@@ -34,10 +34,10 @@
- total_trans
- trans_table
-All the statistics will be from the time the stats driver has been inserted
-to the time when a read of a particular statistic is done. Obviously, stats
-driver will not have any information about the frequency transitions before
-the stats driver insertion.
+All the statistics will be from the time the stats driver has been inserted
+(or the time the stats were reset) to the time when a read of a particular
+statistic is done. Obviously, stats driver will not have any information
+about the frequency transitions before the stats driver insertion.
--------------------------------------------------------------------------------
<mysystem>:/sys/devices/system/cpu/cpu0/cpufreq/stats # ls -l
@@ -110,25 +110,13 @@
CPU Frequency scaling --->
[*] CPU Frequency scaling
[*] CPU frequency translation statistics
- [*] CPU frequency translation statistics details
"CPU Frequency scaling" (CONFIG_CPU_FREQ) should be enabled to configure
cpufreq-stats.
"CPU frequency translation statistics" (CONFIG_CPU_FREQ_STAT) provides the
-basic statistics which includes time_in_state and total_trans.
+statistics which includes time_in_state, total_trans and trans_table.
-"CPU frequency translation statistics details" (CONFIG_CPU_FREQ_STAT_DETAILS)
-provides fine grained cpufreq stats by trans_table. The reason for having a
-separate config option for trans_table is:
-- trans_table goes against the traditional /sysfs rule of one value per
- interface. It provides a whole bunch of value in a 2 dimensional matrix
- form.
-
-Once these two options are enabled and your CPU supports cpufrequency, you
+Once this option is enabled and your CPU supports cpufrequency, you
will be able to see the CPU frequency statistics in /sysfs.
-
-
-
-
diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt
index c15aa75..61b3184 100644
--- a/Documentation/cpu-freq/governors.txt
+++ b/Documentation/cpu-freq/governors.txt
@@ -10,6 +10,8 @@
Dominik Brodowski <linux@brodo.de>
some additions and corrections by Nico Golde <nico@ngolde.de>
+ Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ Viresh Kumar <viresh.kumar@linaro.org>
@@ -28,32 +30,27 @@
2.3 Userspace
2.4 Ondemand
2.5 Conservative
+2.6 Schedutil
3. The Governor Interface in the CPUfreq Core
+4. References
1. What Is A CPUFreq Governor?
==============================
Most cpufreq drivers (except the intel_pstate and longrun) or even most
-cpu frequency scaling algorithms only offer the CPU to be set to one
-frequency. In order to offer dynamic frequency scaling, the cpufreq
-core must be able to tell these drivers of a "target frequency". So
-these specific drivers will be transformed to offer a "->target/target_index"
-call instead of the existing "->setpolicy" call. For "longrun", all
-stays the same, though.
+cpu frequency scaling algorithms only allow the CPU frequency to be set
+to predefined fixed values. In order to offer dynamic frequency
+scaling, the cpufreq core must be able to tell these drivers of a
+"target frequency". So these specific drivers will be transformed to
+offer a "->target/target_index/fast_switch()" call instead of the
+"->setpolicy()" call. For set_policy drivers, all stays the same,
+though.
How to decide what frequency within the CPUfreq policy should be used?
-That's done using "cpufreq governors". Two are already in this patch
--- they're the already existing "powersave" and "performance" which
-set the frequency statically to the lowest or highest frequency,
-respectively. At least two more such governors will be ready for
-addition in the near future, but likely many more as there are various
-different theories and models about dynamic frequency scaling
-around. Using such a generic interface as cpufreq offers to scaling
-governors, these can be tested extensively, and the best one can be
-selected for each specific use.
+That's done using "cpufreq governors".
Basically, it's the following flow graph:
@@ -71,7 +68,7 @@
/ the limits of policy->{min,max}
/ \
/ \
- Using the ->setpolicy call, Using the ->target/target_index call,
+ Using the ->setpolicy call, Using the ->target/target_index/fast_switch call,
the limits and the the frequency closest
"policy" is set. to target_freq is set.
It is assured that it
@@ -109,114 +106,159 @@
2.4 Ondemand
------------
-The CPUfreq governor "ondemand" sets the CPU depending on the
-current usage. To do this the CPU must have the capability to
-switch the frequency very quickly. There are a number of sysfs file
-accessible parameters:
+The CPUfreq governor "ondemand" sets the CPU frequency depending on the
+current system load. Load estimation is triggered by the scheduler
+through the update_util_data->func hook; when triggered, cpufreq checks
+the CPU-usage statistics over the last period and the governor sets the
+CPU accordingly. The CPU must have the capability to switch the
+frequency very quickly.
-sampling_rate: measured in uS (10^-6 seconds), this is how often you
-want the kernel to look at the CPU usage and to make decisions on
-what to do about the frequency. Typically this is set to values of
-around '10000' or more. It's default value is (cmp. with users-guide.txt):
-transition_latency * 1000
-Be aware that transition latency is in ns and sampling_rate is in us, so you
-get the same sysfs value by default.
-Sampling rate should always get adjusted considering the transition latency
-To set the sampling rate 750 times as high as the transition latency
-in the bash (as said, 1000 is default), do:
-echo `$(($(cat cpuinfo_transition_latency) * 750 / 1000)) \
- >ondemand/sampling_rate
+Sysfs files:
-sampling_rate_min:
-The sampling rate is limited by the HW transition latency:
-transition_latency * 100
-Or by kernel restrictions:
-If CONFIG_NO_HZ_COMMON is set, the limit is 10ms fixed.
-If CONFIG_NO_HZ_COMMON is not set or nohz=off boot parameter is used, the
-limits depend on the CONFIG_HZ option:
-HZ=1000: min=20000us (20ms)
-HZ=250: min=80000us (80ms)
-HZ=100: min=200000us (200ms)
-The highest value of kernel and HW latency restrictions is shown and
-used as the minimum sampling rate.
+* sampling_rate:
-up_threshold: defines what the average CPU usage between the samplings
-of 'sampling_rate' needs to be for the kernel to make a decision on
-whether it should increase the frequency. For example when it is set
-to its default value of '95' it means that between the checking
-intervals the CPU needs to be on average more than 95% in use to then
-decide that the CPU frequency needs to be increased.
+ Measured in uS (10^-6 seconds), this is how often you want the kernel
+ to look at the CPU usage and to make decisions on what to do about the
+ frequency. Typically this is set to values of around '10000' or more.
+ It's default value is (cmp. with users-guide.txt): transition_latency
+ * 1000. Be aware that transition latency is in ns and sampling_rate
+ is in us, so you get the same sysfs value by default. Sampling rate
+ should always get adjusted considering the transition latency to set
+ the sampling rate 750 times as high as the transition latency in the
+ bash (as said, 1000 is default), do:
-ignore_nice_load: this parameter takes a value of '0' or '1'. When
-set to '0' (its default), all processes are counted towards the
-'cpu utilisation' value. When set to '1', the processes that are
-run with a 'nice' value will not count (and thus be ignored) in the
-overall usage calculation. This is useful if you are running a CPU
-intensive calculation on your laptop that you do not care how long it
-takes to complete as you can 'nice' it and prevent it from taking part
-in the deciding process of whether to increase your CPU frequency.
+ $ echo `$(($(cat cpuinfo_transition_latency) * 750 / 1000)) > ondemand/sampling_rate
-sampling_down_factor: this parameter controls the rate at which the
-kernel makes a decision on when to decrease the frequency while running
-at top speed. When set to 1 (the default) decisions to reevaluate load
-are made at the same interval regardless of current clock speed. But
-when set to greater than 1 (e.g. 100) it acts as a multiplier for the
-scheduling interval for reevaluating load when the CPU is at its top
-speed due to high load. This improves performance by reducing the overhead
-of load evaluation and helping the CPU stay at its top speed when truly
-busy, rather than shifting back and forth in speed. This tunable has no
-effect on behavior at lower speeds/lower CPU loads.
+* sampling_rate_min:
-powersave_bias: this parameter takes a value between 0 to 1000. It
-defines the percentage (times 10) value of the target frequency that
-will be shaved off of the target. For example, when set to 100 -- 10%,
-when ondemand governor would have targeted 1000 MHz, it will target
-1000 MHz - (10% of 1000 MHz) = 900 MHz instead. This is set to 0
-(disabled) by default.
-When AMD frequency sensitivity powersave bias driver --
-drivers/cpufreq/amd_freq_sensitivity.c is loaded, this parameter
-defines the workload frequency sensitivity threshold in which a lower
-frequency is chosen instead of ondemand governor's original target.
-The frequency sensitivity is a hardware reported (on AMD Family 16h
-Processors and above) value between 0 to 100% that tells software how
-the performance of the workload running on a CPU will change when
-frequency changes. A workload with sensitivity of 0% (memory/IO-bound)
-will not perform any better on higher core frequency, whereas a
-workload with sensitivity of 100% (CPU-bound) will perform better
-higher the frequency. When the driver is loaded, this is set to 400
-by default -- for CPUs running workloads with sensitivity value below
-40%, a lower frequency is chosen. Unloading the driver or writing 0
-will disable this feature.
+ The sampling rate is limited by the HW transition latency:
+ transition_latency * 100
+
+ Or by kernel restrictions:
+ - If CONFIG_NO_HZ_COMMON is set, the limit is 10ms fixed.
+ - If CONFIG_NO_HZ_COMMON is not set or nohz=off boot parameter is
+ used, the limits depend on the CONFIG_HZ option:
+ HZ=1000: min=20000us (20ms)
+ HZ=250: min=80000us (80ms)
+ HZ=100: min=200000us (200ms)
+
+ The highest value of kernel and HW latency restrictions is shown and
+ used as the minimum sampling rate.
+
+* up_threshold:
+
+ This defines what the average CPU usage between the samplings of
+ 'sampling_rate' needs to be for the kernel to make a decision on
+ whether it should increase the frequency. For example when it is set
+ to its default value of '95' it means that between the checking
+ intervals the CPU needs to be on average more than 95% in use to then
+ decide that the CPU frequency needs to be increased.
+
+* ignore_nice_load:
+
+ This parameter takes a value of '0' or '1'. When set to '0' (its
+ default), all processes are counted towards the 'cpu utilisation'
+ value. When set to '1', the processes that are run with a 'nice'
+ value will not count (and thus be ignored) in the overall usage
+ calculation. This is useful if you are running a CPU intensive
+ calculation on your laptop that you do not care how long it takes to
+ complete as you can 'nice' it and prevent it from taking part in the
+ deciding process of whether to increase your CPU frequency.
+
+* sampling_down_factor:
+
+ This parameter controls the rate at which the kernel makes a decision
+ on when to decrease the frequency while running at top speed. When set
+ to 1 (the default) decisions to reevaluate load are made at the same
+ interval regardless of current clock speed. But when set to greater
+ than 1 (e.g. 100) it acts as a multiplier for the scheduling interval
+ for reevaluating load when the CPU is at its top speed due to high
+ load. This improves performance by reducing the overhead of load
+ evaluation and helping the CPU stay at its top speed when truly busy,
+ rather than shifting back and forth in speed. This tunable has no
+ effect on behavior at lower speeds/lower CPU loads.
+
+* powersave_bias:
+
+ This parameter takes a value between 0 to 1000. It defines the
+ percentage (times 10) value of the target frequency that will be
+ shaved off of the target. For example, when set to 100 -- 10%, when
+ ondemand governor would have targeted 1000 MHz, it will target
+ 1000 MHz - (10% of 1000 MHz) = 900 MHz instead. This is set to 0
+ (disabled) by default.
+
+ When AMD frequency sensitivity powersave bias driver --
+ drivers/cpufreq/amd_freq_sensitivity.c is loaded, this parameter
+ defines the workload frequency sensitivity threshold in which a lower
+ frequency is chosen instead of ondemand governor's original target.
+ The frequency sensitivity is a hardware reported (on AMD Family 16h
+ Processors and above) value between 0 to 100% that tells software how
+ the performance of the workload running on a CPU will change when
+ frequency changes. A workload with sensitivity of 0% (memory/IO-bound)
+ will not perform any better on higher core frequency, whereas a
+ workload with sensitivity of 100% (CPU-bound) will perform better
+ higher the frequency. When the driver is loaded, this is set to 400 by
+ default -- for CPUs running workloads with sensitivity value below
+ 40%, a lower frequency is chosen. Unloading the driver or writing 0
+ will disable this feature.
2.5 Conservative
----------------
The CPUfreq governor "conservative", much like the "ondemand"
-governor, sets the CPU depending on the current usage. It differs in
-behaviour in that it gracefully increases and decreases the CPU speed
-rather than jumping to max speed the moment there is any load on the
-CPU. This behaviour more suitable in a battery powered environment.
-The governor is tweaked in the same manner as the "ondemand" governor
-through sysfs with the addition of:
+governor, sets the CPU frequency depending on the current usage. It
+differs in behaviour in that it gracefully increases and decreases the
+CPU speed rather than jumping to max speed the moment there is any load
+on the CPU. This behaviour is more suitable in a battery powered
+environment. The governor is tweaked in the same manner as the
+"ondemand" governor through sysfs with the addition of:
-freq_step: this describes what percentage steps the cpu freq should be
-increased and decreased smoothly by. By default the cpu frequency will
-increase in 5% chunks of your maximum cpu frequency. You can change this
-value to anywhere between 0 and 100 where '0' will effectively lock your
-CPU at a speed regardless of its load whilst '100' will, in theory, make
-it behave identically to the "ondemand" governor.
+* freq_step:
-down_threshold: same as the 'up_threshold' found for the "ondemand"
-governor but for the opposite direction. For example when set to its
-default value of '20' it means that if the CPU usage needs to be below
-20% between samples to have the frequency decreased.
+ This describes what percentage steps the cpu freq should be increased
+ and decreased smoothly by. By default the cpu frequency will increase
+ in 5% chunks of your maximum cpu frequency. You can change this value
+ to anywhere between 0 and 100 where '0' will effectively lock your CPU
+ at a speed regardless of its load whilst '100' will, in theory, make
+ it behave identically to the "ondemand" governor.
-sampling_down_factor: similar functionality as in "ondemand" governor.
-But in "conservative", it controls the rate at which the kernel makes
-a decision on when to decrease the frequency while running in any
-speed. Load for frequency increase is still evaluated every
-sampling rate.
+* down_threshold:
+
+ Same as the 'up_threshold' found for the "ondemand" governor but for
+ the opposite direction. For example when set to its default value of
+ '20' it means that if the CPU usage needs to be below 20% between
+ samples to have the frequency decreased.
+
+* sampling_down_factor:
+
+ Similar functionality as in "ondemand" governor. But in
+ "conservative", it controls the rate at which the kernel makes a
+ decision on when to decrease the frequency while running in any speed.
+ Load for frequency increase is still evaluated every sampling rate.
+
+
+2.6 Schedutil
+-------------
+
+The "schedutil" governor aims at better integration with the Linux
+kernel scheduler. Load estimation is achieved through the scheduler's
+Per-Entity Load Tracking (PELT) mechanism, which also provides
+information about the recent load [1]. This governor currently does
+load based DVFS only for tasks managed by CFS. RT and DL scheduler tasks
+are always run at the highest frequency. Unlike all the other
+governors, the code is located under the kernel/sched/ directory.
+
+Sysfs files:
+
+* rate_limit_us:
+
+ This contains a value in microseconds. The governor waits for
+ rate_limit_us time before reevaluating the load again, after it has
+ evaluated the load once.
+
+For an in-depth comparison with the other governors refer to [2].
+
3. The Governor Interface in the CPUfreq Core
=============================================
@@ -225,26 +267,10 @@
"cpufreq_register_governor". The struct cpufreq_governor, which has to
be passed to that function, must contain the following values:
-governor->name - A unique name for this governor
-governor->governor - The governor callback function
-governor->owner - .THIS_MODULE for the governor module (if
- appropriate)
+governor->name - A unique name for this governor.
+governor->owner - .THIS_MODULE for the governor module (if appropriate).
-The governor->governor callback is called with the current (or to-be-set)
-cpufreq_policy struct for that CPU, and an unsigned int event. The
-following events are currently defined:
-
-CPUFREQ_GOV_START: This governor shall start its duty for the CPU
- policy->cpu
-CPUFREQ_GOV_STOP: This governor shall end its duty for the CPU
- policy->cpu
-CPUFREQ_GOV_LIMITS: The limits for CPU policy->cpu have changed to
- policy->min and policy->max.
-
-If you need other "events" externally of your driver, _only_ use the
-cpufreq_governor_l(unsigned int cpu, unsigned int event) call to the
-CPUfreq core to ensure proper locking.
-
+plus a set of hooks to the functions implementing the governor's logic.
The CPUfreq governor may call the CPU processor driver using one of
these two functions:
@@ -258,12 +284,18 @@
unsigned int relation);
target_freq must be within policy->min and policy->max, of course.
-What's the difference between these two functions? When your governor
-still is in a direct code path of a call to governor->governor, the
-per-CPU cpufreq lock is still held in the cpufreq core, and there's
-no need to lock it again (in fact, this would cause a deadlock). So
-use __cpufreq_driver_target only in these cases. In all other cases
-(for example, when there's a "daemonized" function that wakes up
-every second), use cpufreq_driver_target to lock the cpufreq per-CPU
-lock before the command is passed to the cpufreq processor driver.
+What's the difference between these two functions? When your governor is
+in a direct code path of a call to governor callbacks, like
+governor->start(), the policy->rwsem is still held in the cpufreq core,
+and there's no need to lock it again (in fact, this would cause a
+deadlock). So use __cpufreq_driver_target only in these cases. In all
+other cases (for example, when there's a "daemonized" function that
+wakes up every second), use cpufreq_driver_target to take policy->rwsem
+before the command is passed to the cpufreq driver.
+
+4. References
+=============
+
+[1] Per-entity load tracking: https://lwn.net/Articles/531853/
+[2] Improvements in CPU frequency management: https://lwn.net/Articles/682391/
diff --git a/Documentation/cpu-freq/index.txt b/Documentation/cpu-freq/index.txt
index dc024ab..ef1d392 100644
--- a/Documentation/cpu-freq/index.txt
+++ b/Documentation/cpu-freq/index.txt
@@ -18,16 +18,29 @@
Documents in this directory:
----------------------------
-core.txt - General description of the CPUFreq core and
- of CPUFreq notifiers
-cpu-drivers.txt - How to implement a new cpufreq processor driver
+amd-powernow.txt - AMD powernow driver specific file.
+
+boost.txt - Frequency boosting support.
+
+core.txt - General description of the CPUFreq core and
+ of CPUFreq notifiers.
+
+cpu-drivers.txt - How to implement a new cpufreq processor driver.
+
+cpufreq-nforce2.txt - nVidia nForce2 platform specific file.
+
+cpufreq-stats.txt - General description of sysfs cpufreq stats.
governors.txt - What are cpufreq governors and how to
implement them?
index.txt - File index, Mailing list and Links (this document)
+intel-pstate.txt - Intel pstate cpufreq driver specific file.
+
+pcc-cpufreq.txt - PCC cpufreq driver specific file.
+
user-guide.txt - User Guide to CPUFreq
@@ -35,9 +48,7 @@
------------
There is a CPU frequency changing CVS commit and general list where
you can report bugs, problems or submit patches. To post a message,
-send an email to linux-pm@vger.kernel.org, to subscribe go to
-http://vger.kernel.org/vger-lists.html#linux-pm and follow the
-instructions there.
+send an email to linux-pm@vger.kernel.org.
Links
-----
@@ -48,7 +59,7 @@
* http://cvs.arm.linux.org.uk/
the CPUFreq Mailing list:
-* http://vger.kernel.org/vger-lists.html#cpufreq
+* http://vger.kernel.org/vger-lists.html#linux-pm
Clock and voltage scaling for the SA-1100:
* http://www.lartmaker.nl/projects/scaling
diff --git a/Documentation/cpu-freq/intel-pstate.txt b/Documentation/cpu-freq/intel-pstate.txt
index 1953994..3fdcdfd 100644
--- a/Documentation/cpu-freq/intel-pstate.txt
+++ b/Documentation/cpu-freq/intel-pstate.txt
@@ -85,6 +85,21 @@
Refer to "Intel® 64 and IA-32 Architectures Software Developer’s Manual
Volume 3: System Programming Guide" to understand ratios.
+There is one more sysfs attribute in /sys/devices/system/cpu/intel_pstate/
+that can be used for controlling the operation mode of the driver:
+
+ status: Three settings are possible:
+ "off" - The driver is not in use at this time.
+ "active" - The driver works as a P-state governor (default).
+ "passive" - The driver works as a regular cpufreq one and collaborates
+ with the generic cpufreq governors (it sets P-states as
+ requested by those governors).
+ The current setting is returned by reads from this attribute. Writing one
+ of the above strings to it changes the operation mode as indicated by that
+ string, if possible. If HW-managed P-states (HWP) are enabled, it is not
+ possible to change the driver's operation mode and attempts to write to
+ this attribute will fail.
+
cpufreq sysfs for Intel P-State
Since this driver registers with cpufreq, cpufreq sysfs is also presented.
diff --git a/Documentation/cpu-freq/user-guide.txt b/Documentation/cpu-freq/user-guide.txt
index 109e97b..107f6fd 100644
--- a/Documentation/cpu-freq/user-guide.txt
+++ b/Documentation/cpu-freq/user-guide.txt
@@ -18,7 +18,7 @@
Contents:
---------
1. Supported Architectures and Processors
-1.1 ARM
+1.1 ARM and ARM64
1.2 x86
1.3 sparc64
1.4 ppc
@@ -37,16 +37,10 @@
1. Supported Architectures and Processors
=========================================
-1.1 ARM
--------
+1.1 ARM and ARM64
+-----------------
-The following ARM processors are supported by cpufreq:
-
-ARM Integrator
-ARM-SA1100
-ARM-SA1110
-Intel PXA
-
+Almost all ARM and ARM64 platforms support CPU frequency scaling.
1.2 x86
-------
@@ -69,6 +63,7 @@
Transmeta Efficeon
VIA Cyrix 3 / C3
various processors on some ACPI 2.0-compatible systems [*]
+And many more
[*] Only if "ACPI Processor Performance States" are available
to the ACPI<->BIOS interface.
@@ -147,10 +142,19 @@
"cpufreq" within the cpu-device directory
(e.g. /sys/devices/system/cpu/cpu0/cpufreq/ for the first CPU).
+affected_cpus : List of Online CPUs that require software
+ coordination of frequency.
+
+cpuinfo_cur_freq : Current frequency of the CPU as obtained from
+ the hardware, in KHz. This is the frequency
+ the CPU actually runs at.
+
cpuinfo_min_freq : this file shows the minimum operating
frequency the processor can run at(in kHz)
+
cpuinfo_max_freq : this file shows the maximum operating
frequency the processor can run at(in kHz)
+
cpuinfo_transition_latency The time it takes on this CPU to
switch between two frequencies in nano
seconds. If unknown or known to be
@@ -163,25 +167,30 @@
userspace daemon. Make sure to not
switch the frequency too often
resulting in performance loss.
-scaling_driver : this file shows what cpufreq driver is
- used to set the frequency on this CPU
+
+related_cpus : List of Online + Offline CPUs that need software
+ coordination of frequency.
+
+scaling_available_frequencies : List of available frequencies, in KHz.
scaling_available_governors : this file shows the CPUfreq governors
available in this kernel. You can see the
currently activated governor in
+scaling_cur_freq : Current frequency of the CPU as determined by
+ the governor and cpufreq core, in KHz. This is
+ the frequency the kernel thinks the CPU runs
+ at.
+
+scaling_driver : this file shows what cpufreq driver is
+ used to set the frequency on this CPU
+
scaling_governor, and by "echoing" the name of another
governor you can change it. Please note
that some governors won't load - they only
work on some specific architectures or
processors.
-cpuinfo_cur_freq : Current frequency of the CPU as obtained from
- the hardware, in KHz. This is the frequency
- the CPU actually runs at.
-
-scaling_available_frequencies : List of available frequencies, in KHz.
-
scaling_min_freq and
scaling_max_freq show the current "policy limits" (in
kHz). By echoing new values into these
@@ -190,16 +199,11 @@
first set scaling_max_freq, then
scaling_min_freq.
-affected_cpus : List of Online CPUs that require software
- coordination of frequency.
-
-related_cpus : List of Online + Offline CPUs that need software
- coordination of frequency.
-
-scaling_cur_freq : Current frequency of the CPU as determined by
- the governor and cpufreq core, in KHz. This is
- the frequency the kernel thinks the CPU runs
- at.
+scaling_setspeed This can be read to get the currently programmed
+ value by the governor. This can be written to
+ change the current frequency for a group of
+ CPUs, represented by a policy. This is supported
+ currently only by the userspace governor.
bios_limit : If the BIOS tells the OS to limit a CPU to
lower frequencies, the user can read out the
diff --git a/Documentation/devicetree/bindings/arm/arch_timer.txt b/Documentation/devicetree/bindings/arm/arch_timer.txt
index ad440a2..e926aea 100644
--- a/Documentation/devicetree/bindings/arm/arch_timer.txt
+++ b/Documentation/devicetree/bindings/arm/arch_timer.txt
@@ -31,6 +31,12 @@
This also affects writes to the tval register, due to the implicit
counter read.
+- hisilicon,erratum-161010101 : A boolean property. Indicates the
+ presence of Hisilicon erratum 161010101, which says that reading the
+ counters is unreliable in some cases, and reads may return a value 32
+ beyond the correct value. This also affects writes to the tval
+ registers, due to the implicit counter read.
+
** Optional properties:
- arm,cpu-registers-not-fw-configured : Firmware does not initialize
diff --git a/Documentation/devicetree/bindings/cpufreq/ti-cpufreq.txt b/Documentation/devicetree/bindings/cpufreq/ti-cpufreq.txt
new file mode 100644
index 0000000..ba0e15a
--- /dev/null
+++ b/Documentation/devicetree/bindings/cpufreq/ti-cpufreq.txt
@@ -0,0 +1,128 @@
+TI CPUFreq and OPP bindings
+================================
+
+Certain TI SoCs, like those in the am335x, am437x, am57xx, and dra7xx
+families support different OPPs depending on the silicon variant in use.
+The ti-cpufreq driver can use revision and an efuse value from the SoC to
+provide the OPP framework with supported hardware information. This is
+used to determine which OPPs from the operating-points-v2 table get enabled
+when it is parsed by the OPP framework.
+
+Required properties:
+--------------------
+In 'cpus' nodes:
+- operating-points-v2: Phandle to the operating-points-v2 table to use.
+
+In 'operating-points-v2' table:
+- compatible: Should be
+ - 'operating-points-v2-ti-cpu' for am335x, am43xx, and dra7xx/am57xx SoCs
+- syscon: A phandle pointing to a syscon node representing the control module
+ register space of the SoC.
+
+Optional properties:
+--------------------
+For each opp entry in 'operating-points-v2' table:
+- opp-supported-hw: Two bitfields indicating:
+ 1. Which revision of the SoC the OPP is supported by
+ 2. Which eFuse bits indicate this OPP is available
+
+ A bitwise AND is performed against these values and if any bit
+ matches, the OPP gets enabled.
+
+Example:
+--------
+
+/* From arch/arm/boot/dts/am33xx.dtsi */
+cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cpu@0 {
+ compatible = "arm,cortex-a8";
+ device_type = "cpu";
+ reg = <0>;
+
+ operating-points-v2 = <&cpu0_opp_table>;
+
+ clocks = <&dpll_mpu_ck>;
+ clock-names = "cpu";
+
+ clock-latency = <300000>; /* From omap-cpufreq driver */
+ };
+};
+
+/*
+ * cpu0 has different OPPs depending on SoC revision and some on revisions
+ * 0x2 and 0x4 have eFuse bits that indicate if they are available or not
+ */
+cpu0_opp_table: opp-table {
+ compatible = "operating-points-v2-ti-cpu";
+ syscon = <&scm_conf>;
+
+ /*
+ * The three following nodes are marked with opp-suspend
+ * because they can not be enabled simultaneously on a
+ * single SoC.
+ */
+ opp50@300000000 {
+ opp-hz = /bits/ 64 <300000000>;
+ opp-microvolt = <950000 931000 969000>;
+ opp-supported-hw = <0x06 0x0010>;
+ opp-suspend;
+ };
+
+ opp100@275000000 {
+ opp-hz = /bits/ 64 <275000000>;
+ opp-microvolt = <1100000 1078000 1122000>;
+ opp-supported-hw = <0x01 0x00FF>;
+ opp-suspend;
+ };
+
+ opp100@300000000 {
+ opp-hz = /bits/ 64 <300000000>;
+ opp-microvolt = <1100000 1078000 1122000>;
+ opp-supported-hw = <0x06 0x0020>;
+ opp-suspend;
+ };
+
+ opp100@500000000 {
+ opp-hz = /bits/ 64 <500000000>;
+ opp-microvolt = <1100000 1078000 1122000>;
+ opp-supported-hw = <0x01 0xFFFF>;
+ };
+
+ opp100@600000000 {
+ opp-hz = /bits/ 64 <600000000>;
+ opp-microvolt = <1100000 1078000 1122000>;
+ opp-supported-hw = <0x06 0x0040>;
+ };
+
+ opp120@600000000 {
+ opp-hz = /bits/ 64 <600000000>;
+ opp-microvolt = <1200000 1176000 1224000>;
+ opp-supported-hw = <0x01 0xFFFF>;
+ };
+
+ opp120@720000000 {
+ opp-hz = /bits/ 64 <720000000>;
+ opp-microvolt = <1200000 1176000 1224000>;
+ opp-supported-hw = <0x06 0x0080>;
+ };
+
+ oppturbo@720000000 {
+ opp-hz = /bits/ 64 <720000000>;
+ opp-microvolt = <1260000 1234800 1285200>;
+ opp-supported-hw = <0x01 0xFFFF>;
+ };
+
+ oppturbo@800000000 {
+ opp-hz = /bits/ 64 <800000000>;
+ opp-microvolt = <1260000 1234800 1285200>;
+ opp-supported-hw = <0x06 0x0100>;
+ };
+
+ oppnitro@1000000000 {
+ opp-hz = /bits/ 64 <1000000000>;
+ opp-microvolt = <1325000 1298500 1351500>;
+ opp-supported-hw = <0x04 0x0200>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/devfreq/exynos-bus.txt b/Documentation/devicetree/bindings/devfreq/exynos-bus.txt
index d3ec8e6..d085ef9 100644
--- a/Documentation/devicetree/bindings/devfreq/exynos-bus.txt
+++ b/Documentation/devicetree/bindings/devfreq/exynos-bus.txt
@@ -123,6 +123,20 @@
|--- FSYS
|--- FSYS2
+- In case of Exynos5433, there is VDD_INT power line as following:
+ VDD_INT |--- G2D (parent device)
+ |--- MSCL
+ |--- GSCL
+ |--- JPEG
+ |--- MFC
+ |--- HEVC
+ |--- BUS0
+ |--- BUS1
+ |--- BUS2
+ |--- PERIS (Fixed clock rate)
+ |--- PERIC (Fixed clock rate)
+ |--- FSYS (Fixed clock rate)
+
Example1:
Show the AXI buses of Exynos3250 SoC. Exynos3250 divides the buses to
power line (regulator). The MIF (Memory Interface) AXI bus is used to
diff --git a/Documentation/devicetree/bindings/hwmon/adc128d818.txt b/Documentation/devicetree/bindings/hwmon/adc128d818.txt
new file mode 100644
index 0000000..08bab0e
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/adc128d818.txt
@@ -0,0 +1,38 @@
+TI ADC128D818 ADC System Monitor With Temperature Sensor
+--------------------------------------------------------
+
+Operation modes:
+
+ - Mode 0: 7 single-ended voltage readings (IN0-IN6),
+ 1 temperature reading (internal)
+ - Mode 1: 8 single-ended voltage readings (IN0-IN7),
+ no temperature
+ - Mode 2: 4 pseudo-differential voltage readings
+ (IN0-IN1, IN3-IN2, IN4-IN5, IN7-IN6),
+ 1 temperature reading (internal)
+ - Mode 3: 4 single-ended voltage readings (IN0-IN3),
+ 2 pseudo-differential voltage readings
+ (IN4-IN5, IN7-IN6),
+ 1 temperature reading (internal)
+
+If no operation mode is configured via device tree, the driver keeps the
+currently active chip operation mode (default is mode 0).
+
+
+Required node properties:
+
+ - compatible: must be set to "ti,adc128d818"
+ - reg: I2C address of the device
+
+Optional node properties:
+
+ - ti,mode: Operation mode (see above).
+
+
+Example (operation mode 2):
+
+ adc128d818@1d {
+ compatible = "ti,adc128d818";
+ reg = <0x1d>;
+ ti,mode = <2>;
+ };
diff --git a/Documentation/devicetree/bindings/hwmon/lm70.txt b/Documentation/devicetree/bindings/hwmon/lm70.txt
index e7fd921..ea417a0 100644
--- a/Documentation/devicetree/bindings/hwmon/lm70.txt
+++ b/Documentation/devicetree/bindings/hwmon/lm70.txt
@@ -4,6 +4,7 @@
- compatible: one of
"ti,lm70"
"ti,tmp121"
+ "ti,tmp122"
"ti,lm71"
"ti,lm74"
diff --git a/Documentation/devicetree/bindings/hwmon/lm90.txt b/Documentation/devicetree/bindings/hwmon/lm90.txt
index e863248..9758126 100644
--- a/Documentation/devicetree/bindings/hwmon/lm90.txt
+++ b/Documentation/devicetree/bindings/hwmon/lm90.txt
@@ -33,6 +33,11 @@
LM90 "-ALERT" pin output.
See interrupt-controller/interrupts.txt for the format.
+- #thermal-sensor-cells: should be set to 1. See thermal/thermal.txt for
+ details. See <include/dt-bindings/thermal/lm90.h> for the
+ definition of the local, remote and 2nd remote sensor index
+ constants.
+
Example LM90 node:
temp-sensor {
@@ -41,4 +46,5 @@
vcc-supply = <&palmas_ldo6_reg>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(O, 4) IRQ_TYPE_LEVEL_LOW>;
+ #thermal-sensor-cells = <1>;
}
diff --git a/Documentation/devicetree/bindings/hwmon/sht15.txt b/Documentation/devicetree/bindings/hwmon/sht15.txt
new file mode 100644
index 0000000..6a80277
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/sht15.txt
@@ -0,0 +1,19 @@
+Sensirion SHT15 Humidity and Temperature Sensor
+
+Required properties:
+
+ - "compatible": must be "sensirion,sht15".
+ - "data-gpios": GPIO connected to the data line.
+ - "clk-gpios": GPIO connected to the clock line.
+ - "vcc-supply": regulator that drives the VCC pin.
+
+Example:
+
+ sensor {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sensor>;
+ compatible = "sensirion,sht15";
+ clk-gpios = <&gpio4 12 0>;
+ data-gpios = <&gpio4 13 0>;
+ vcc-supply = <®_sht15>;
+ };
diff --git a/Documentation/devicetree/bindings/hwmon/stts751.txt b/Documentation/devicetree/bindings/hwmon/stts751.txt
new file mode 100644
index 0000000..3ee1dc3
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/stts751.txt
@@ -0,0 +1,15 @@
+* STTS751 thermometer.
+
+Required node properties:
+- compatible: "stts751"
+- reg: I2C bus address of the device
+
+Optional properties:
+- smbus-timeout-disable: when set, the smbus timeout function will be disabled
+
+Example stts751 node:
+
+temp-sensor {
+ compatible = "stts751";
+ reg = <0x48>;
+}
diff --git a/Documentation/devicetree/bindings/interrupt-controller/cortina,gemini-interrupt-controller.txt b/Documentation/devicetree/bindings/interrupt-controller/cortina,gemini-interrupt-controller.txt
new file mode 100644
index 0000000..97c1167
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/cortina,gemini-interrupt-controller.txt
@@ -0,0 +1,22 @@
+* Cortina Systems Gemini interrupt controller
+
+This interrupt controller is found on the Gemini SoCs.
+
+Required properties:
+- compatible: must be "cortina,gemini-interrupt-controller"
+- reg: The register bank for the interrupt controller.
+- interrupt-controller: Identifies the node as an interrupt controller
+- #interrupt-cells: The number of cells to define the interrupts.
+ Must be 2 as the controller can specify level or rising edge
+ IRQs. The bindings follows the standard binding for controllers
+ with two cells specified in
+ interrupt-controller/interrupts.txt
+
+Example:
+
+interrupt-controller@48000000 {
+ compatible = "cortina,gemini-interrupt-controller";
+ reg = <0x48000000 0x1000>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+};
diff --git a/Documentation/devicetree/bindings/leds/common.txt b/Documentation/devicetree/bindings/leds/common.txt
index 696be57..24b6560 100644
--- a/Documentation/devicetree/bindings/leds/common.txt
+++ b/Documentation/devicetree/bindings/leds/common.txt
@@ -61,16 +61,24 @@
Examples:
-system-status {
- label = "Status";
- linux,default-trigger = "heartbeat";
- ...
+gpio-leds {
+ compatible = "gpio-leds";
+
+ system-status {
+ label = "Status";
+ linux,default-trigger = "heartbeat";
+ gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
+ };
};
-camera-flash {
- label = "Flash";
- led-sources = <0>, <1>;
- led-max-microamp = <50000>;
- flash-max-microamp = <320000>;
- flash-max-timeout-us = <500000>;
+max77693-led {
+ compatible = "maxim,max77693-led";
+
+ camera-flash {
+ label = "Flash";
+ led-sources = <0>, <1>;
+ led-max-microamp = <50000>;
+ flash-max-microamp = <320000>;
+ flash-max-timeout-us = <500000>;
+ };
};
diff --git a/Documentation/devicetree/bindings/mtd/aspeed-smc.txt b/Documentation/devicetree/bindings/mtd/aspeed-smc.txt
new file mode 100644
index 0000000..49f6528
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/aspeed-smc.txt
@@ -0,0 +1,51 @@
+* Aspeed Firmware Memory controller
+* Aspeed SPI Flash Memory Controller
+
+The Firmware Memory Controller in the Aspeed AST2500 SoC supports
+three chip selects, two of which are always of SPI type and the third
+can be SPI or NOR type flash. These bindings only describe SPI.
+
+The two SPI flash memory controllers in the AST2500 each support two
+chip selects.
+
+Required properties:
+ - compatible : Should be one of
+ "aspeed,ast2400-fmc" for the AST2400 Firmware Memory Controller
+ "aspeed,ast2400-spi" for the AST2400 SPI Flash memory Controller
+ "aspeed,ast2500-fmc" for the AST2500 Firmware Memory Controller
+ "aspeed,ast2500-spi" for the AST2500 SPI flash memory controllers
+
+ - reg : the first contains the control register location and length,
+ the second contains the memory window mapping address and length
+ - #address-cells : must be 1 corresponding to chip select child binding
+ - #size-cells : must be 0 corresponding to chip select child binding
+
+Optional properties:
+ - interrupts : Should contain the interrupt for the dma device if an
+ FMC
+
+The child nodes are the SPI flash modules which must have a compatible
+property as specified in bindings/mtd/jedec,spi-nor.txt
+
+Optionally, the child node can contain properties for SPI mode (may be
+ignored):
+ - spi-max-frequency - max frequency of spi bus
+
+
+Example:
+fmc: fmc@1e620000 {
+ compatible = "aspeed,ast2500-fmc";
+ reg = < 0x1e620000 0x94
+ 0x20000000 0x02000000 >;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <19>;
+ flash@0 {
+ reg = < 0 >;
+ compatible = "jedec,spi-nor";
+ /* spi-max-frequency = <>; */
+ /* m25p,fast-read; */
+ #address-cells = <1>;
+ #size-cells = <1>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/mtd/common.txt b/Documentation/devicetree/bindings/mtd/common.txt
new file mode 100644
index 0000000..fc068b9
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/common.txt
@@ -0,0 +1,15 @@
+* Common properties of all MTD devices
+
+Optional properties:
+- label: user-defined MTD device name. Can be used to assign user
+ friendly names to MTD devices (instead of the flash model or flash
+ controller based name) in order to ease flash device identification
+ and/or describe what they are used for.
+
+Example:
+
+ flash@0 {
+ label = "System-firmware";
+
+ /* flash type specific properties */
+ };
diff --git a/Documentation/devicetree/bindings/mtd/cortina,gemini-flash.txt b/Documentation/devicetree/bindings/mtd/cortina,gemini-flash.txt
new file mode 100644
index 0000000..3fa1b34
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/cortina,gemini-flash.txt
@@ -0,0 +1,24 @@
+Flash device on Cortina Systems Gemini SoC
+
+This flash is regular CFI compatible (Intel or AMD extended) flash chips with
+some special bits that can be controlled by the machine's system controller.
+
+Required properties:
+- compatible : must be "cortina,gemini-flash", "cfi-flash";
+- reg : memory address for the flash chip
+- syscon : must be a phandle to the system controller
+- bank-width : width in bytes of flash interface, should be <2>
+
+For the rest of the properties, see mtd-physmap.txt.
+
+The device tree may optionally contain sub-nodes describing partitions of the
+address space. See partition.txt for more detail.
+
+Example:
+
+flash@30000000 {
+ compatible = "cortina,gemini-flash", "cfi-flash";
+ reg = <0x30000000 0x01000000>;
+ syscon = <&syscon>;
+ bank-width = <2>;
+};
diff --git a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt
index 2c91c03..3e920ec 100644
--- a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt
+++ b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt
@@ -14,6 +14,8 @@
at25df641
at26df081a
mr25h256
+ mr25h10
+ mr25h40
mx25l4005a
mx25l1606e
mx25l6405d
diff --git a/Documentation/devicetree/bindings/mtd/mtk-quadspi.txt b/Documentation/devicetree/bindings/mtd/mtk-quadspi.txt
index fb314f0..5ded66a 100644
--- a/Documentation/devicetree/bindings/mtd/mtk-quadspi.txt
+++ b/Documentation/devicetree/bindings/mtd/mtk-quadspi.txt
@@ -1,7 +1,13 @@
* Serial NOR flash controller for MTK MT81xx (and similar)
Required properties:
-- compatible: should be "mediatek,mt8173-nor";
+- compatible: The possible values are:
+ "mediatek,mt2701-nor"
+ "mediatek,mt7623-nor"
+ "mediatek,mt8173-nor"
+ For mt8173, compatible should be "mediatek,mt8173-nor".
+ For every other SoC, should contain both the SoC-specific compatible string
+ and "mediatek,mt8173-nor".
- reg: physical base address and length of the controller's register
- clocks: the phandle of the clocks needed by the nor controller
- clock-names: the names of the clocks
diff --git a/Documentation/devicetree/bindings/opp/opp.txt b/Documentation/devicetree/bindings/opp/opp.txt
index 9f5ca44..7951961 100644
--- a/Documentation/devicetree/bindings/opp/opp.txt
+++ b/Documentation/devicetree/bindings/opp/opp.txt
@@ -188,14 +188,14 @@
opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
- opp-microvolt = <970000 975000 985000>;
+ opp-microvolt = <975000 970000 985000>;
opp-microamp = <70000>;
clock-latency-ns = <300000>;
opp-suspend;
};
opp@1100000000 {
opp-hz = /bits/ 64 <1100000000>;
- opp-microvolt = <980000 1000000 1010000>;
+ opp-microvolt = <1000000 980000 1010000>;
opp-microamp = <80000>;
clock-latency-ns = <310000>;
};
@@ -267,14 +267,14 @@
opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
- opp-microvolt = <970000 975000 985000>;
+ opp-microvolt = <975000 970000 985000>;
opp-microamp = <70000>;
clock-latency-ns = <300000>;
opp-suspend;
};
opp@1100000000 {
opp-hz = /bits/ 64 <1100000000>;
- opp-microvolt = <980000 1000000 1010000>;
+ opp-microvolt = <1000000 980000 1010000>;
opp-microamp = <80000>;
clock-latency-ns = <310000>;
};
@@ -343,14 +343,14 @@
opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
- opp-microvolt = <970000 975000 985000>;
+ opp-microvolt = <975000 970000 985000>;
opp-microamp = <70000>;
clock-latency-ns = <300000>;
opp-suspend;
};
opp@1100000000 {
opp-hz = /bits/ 64 <1100000000>;
- opp-microvolt = <980000 1000000 1010000>;
+ opp-microvolt = <1000000 980000 1010000>;
opp-microamp = <80000>;
clock-latency-ns = <310000>;
};
@@ -369,7 +369,7 @@
opp@1300000000 {
opp-hz = /bits/ 64 <1300000000>;
- opp-microvolt = <1045000 1050000 1055000>;
+ opp-microvolt = <1050000 1045000 1055000>;
opp-microamp = <95000>;
clock-latency-ns = <400000>;
opp-suspend;
@@ -382,7 +382,7 @@
};
opp@1500000000 {
opp-hz = /bits/ 64 <1500000000>;
- opp-microvolt = <1010000 1100000 1110000>;
+ opp-microvolt = <1100000 1010000 1110000>;
opp-microamp = <95000>;
clock-latency-ns = <400000>;
turbo-mode;
@@ -424,9 +424,9 @@
opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
- opp-microvolt = <970000 975000 985000>, /* Supply 0 */
- <960000 965000 975000>, /* Supply 1 */
- <960000 965000 975000>; /* Supply 2 */
+ opp-microvolt = <975000 970000 985000>, /* Supply 0 */
+ <965000 960000 975000>, /* Supply 1 */
+ <965000 960000 975000>; /* Supply 2 */
opp-microamp = <70000>, /* Supply 0 */
<70000>, /* Supply 1 */
<70000>; /* Supply 2 */
@@ -437,9 +437,9 @@
opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
- opp-microvolt = <970000 975000 985000>, /* Supply 0 */
- <960000 965000 975000>, /* Supply 1 */
- <960000 965000 975000>; /* Supply 2 */
+ opp-microvolt = <975000 970000 985000>, /* Supply 0 */
+ <965000 960000 975000>, /* Supply 1 */
+ <965000 960000 975000>; /* Supply 2 */
opp-microamp = <70000>, /* Supply 0 */
<0>, /* Supply 1 doesn't need this */
<70000>; /* Supply 2 */
@@ -474,7 +474,7 @@
*/
opp-supported-hw = <0xF 0xFFFFFFFF 0xFFFFFFFF>
opp-hz = /bits/ 64 <600000000>;
- opp-microvolt = <900000 915000 925000>;
+ opp-microvolt = <915000 900000 925000>;
...
};
@@ -487,7 +487,7 @@
*/
opp-supported-hw = <0x20 0xff0000ff 0x0000f4f0>
opp-hz = /bits/ 64 <800000000>;
- opp-microvolt = <900000 915000 925000>;
+ opp-microvolt = <915000 900000 925000>;
...
};
};
@@ -512,18 +512,18 @@
opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
- opp-microvolt-slow = <900000 915000 925000>;
- opp-microvolt-fast = <970000 975000 985000>;
+ opp-microvolt-slow = <915000 900000 925000>;
+ opp-microvolt-fast = <975000 970000 985000>;
opp-microamp-slow = <70000>;
opp-microamp-fast = <71000>;
};
opp@1200000000 {
opp-hz = /bits/ 64 <1200000000>;
- opp-microvolt-slow = <900000 915000 925000>, /* Supply vcc0 */
- <910000 925000 935000>; /* Supply vcc1 */
- opp-microvolt-fast = <970000 975000 985000>, /* Supply vcc0 */
- <960000 965000 975000>; /* Supply vcc1 */
+ opp-microvolt-slow = <915000 900000 925000>, /* Supply vcc0 */
+ <925000 910000 935000>; /* Supply vcc1 */
+ opp-microvolt-fast = <975000 970000 985000>, /* Supply vcc0 */
+ <965000 960000 975000>; /* Supply vcc1 */
opp-microamp = <70000>; /* Will be used for both slow/fast */
};
};
diff --git a/Documentation/devicetree/bindings/power/supply/axp20x_ac_power.txt b/Documentation/devicetree/bindings/power/supply/axp20x_ac_power.txt
new file mode 100644
index 0000000..826e8a8
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/axp20x_ac_power.txt
@@ -0,0 +1,22 @@
+AXP20X and AXP22X PMICs' AC power supply
+
+Required Properties:
+ - compatible: One of:
+ "x-powers,axp202-ac-power-supply"
+ "x-powers,axp221-ac-power-supply"
+
+This node is a subnode of the axp20x PMIC.
+
+The AXP20X can read the current current and voltage supplied by AC by
+reading ADC channels from the AXP20X ADC.
+
+The AXP22X is only able to tell if an AC power supply is present and
+usable.
+
+Example:
+
+&axp209 {
+ ac_power_supply: ac-power-supply {
+ compatible = "x-powers,axp202-ac-power-supply";
+ };
+};
diff --git a/Documentation/devicetree/bindings/power/supply/axp20x_usb_power.txt b/Documentation/devicetree/bindings/power/supply/axp20x_usb_power.txt
index f1d7bee..ba8d35f 100644
--- a/Documentation/devicetree/bindings/power/supply/axp20x_usb_power.txt
+++ b/Documentation/devicetree/bindings/power/supply/axp20x_usb_power.txt
@@ -3,6 +3,11 @@
Required Properties:
-compatible: One of: "x-powers,axp202-usb-power-supply"
"x-powers,axp221-usb-power-supply"
+ "x-powers,axp223-usb-power-supply"
+
+The AXP223 PMIC shares most of its behaviour with the AXP221 but has slight
+variations such as the former being able to set the VBUS power supply max
+current to 100mA, unlike the latter.
This node is a subnode of the axp20x PMIC.
diff --git a/Documentation/devicetree/bindings/power/supply/bq27xxx.txt b/Documentation/devicetree/bindings/power/supply/bq27xxx.txt
new file mode 100644
index 0000000..b0c95ef
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/bq27xxx.txt
@@ -0,0 +1,36 @@
+Binding for TI BQ27XXX fuel gauge family
+
+Required properties:
+- compatible: Should contain one of the following:
+ * "ti,bq27200" - BQ27200
+ * "ti,bq27210" - BQ27210
+ * "ti,bq27500" - deprecated, use revision specific property below
+ * "ti,bq27510" - deprecated, use revision specific property below
+ * "ti,bq27520" - deprecated, use revision specific property below
+ * "ti,bq27500-1" - BQ27500/1
+ * "ti,bq27510g1" - BQ27510-g1
+ * "ti,bq27510g2" - BQ27510-g2
+ * "ti,bq27510g3" - BQ27510-g3
+ * "ti,bq27520g1" - BQ27520-g1
+ * "ti,bq27520g2" - BQ27520-g2
+ * "ti,bq27520g3" - BQ27520-g3
+ * "ti,bq27520g4" - BQ27520-g4
+ * "ti,bq27530" - BQ27530
+ * "ti,bq27531" - BQ27531
+ * "ti,bq27541" - BQ27541
+ * "ti,bq27542" - BQ27542
+ * "ti,bq27546" - BQ27546
+ * "ti,bq27742" - BQ27742
+ * "ti,bq27545" - BQ27545
+ * "ti,bq27421" - BQ27421
+ * "ti,bq27425" - BQ27425
+ * "ti,bq27441" - BQ27441
+ * "ti,bq27621" - BQ27621
+- reg: integer, i2c address of the device.
+
+Example:
+
+bq27510g3 {
+ compatible = "ti,bq27510g3";
+ reg = <0x55>;
+};
diff --git a/Documentation/devicetree/bindings/power/supply/qcom_smbb.txt b/Documentation/devicetree/bindings/power/supply/qcom_smbb.txt
index 65b88fa..06f8a5d 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom_smbb.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom_smbb.txt
@@ -105,6 +105,22 @@
regulation must be done externally to fully comply with
the JEITA safety guidelines if this flag is set.
+- usb_otg_in-supply:
+ Usage: optional
+ Value type: <phandle>
+ Description: Reference to the regulator supplying power to the USB_OTG_IN
+ pin.
+
+child nodes:
+- otg-vbus:
+ Usage: optional
+ Description: This node defines a regulator used to control the direction
+ of VBUS voltage - specifically: whether to supply voltage
+ to VBUS for host mode operation of the OTG port, or allow
+ input voltage from external VBUS for charging. In the
+ hardware, the supply for this regulator comes from
+ usb_otg_in-supply.
+
EXAMPLE
charger@1000 {
compatible = "qcom,pm8941-charger";
@@ -128,4 +144,7 @@
qcom,fast-charge-current-limit = <1000000>;
qcom,dc-charge-current-limit = <1000000>;
+ usb_otg_in-supply = <&pm8941_5vs1>;
+
+ otg-vbus {};
};
diff --git a/Documentation/devicetree/bindings/power/supply/sbs_sbs-charger.txt b/Documentation/devicetree/bindings/power/supply/sbs_sbs-charger.txt
new file mode 100644
index 0000000..a371962
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/sbs_sbs-charger.txt
@@ -0,0 +1,23 @@
+SBS sbs-charger
+~~~~~~~~~~
+
+Required properties:
+ - compatible: "<vendor>,<part-number>", "sbs,sbs-charger" as fallback. The part
+ number compatible string might be used in order to take care of vendor
+ specific registers.
+
+Optional properties:
+- interrupt-parent: Should be the phandle for the interrupt controller. Use in
+ conjunction with "interrupts".
+- interrupts: Interrupt mapping for GPIO IRQ. Use in conjunction with
+ "interrupt-parent". If an interrupt is not provided the driver will switch
+ automatically to polling.
+
+Example:
+
+ ltc4100@9 {
+ compatible = "lltc,ltc4100", "sbs,sbs-charger";
+ reg = <0x9>;
+ interrupt-parent = <&gpio6>;
+ interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
+ };
diff --git a/Documentation/devicetree/bindings/power/supply/ti,bq24735.txt b/Documentation/devicetree/bindings/power/supply/ti,bq24735.txt
index 3bf5575..de45e1a 100644
--- a/Documentation/devicetree/bindings/power/supply/ti,bq24735.txt
+++ b/Documentation/devicetree/bindings/power/supply/ti,bq24735.txt
@@ -8,8 +8,10 @@
- interrupts : Specify the interrupt to be used to trigger when the AC
adapter is either plugged in or removed.
- ti,ac-detect-gpios : This GPIO is optionally used to read the AC adapter
- presence. This is a Host GPIO that is configured as an input and
- connected to the bq24735.
+ status. This is a Host GPIO that is configured as an input and connected
+ to the ACOK pin on the bq24735. Note: for backwards compatibility reasons,
+ the GPIO must be active on AC adapter absence despite ACOK being active
+ (high) on AC adapter presence.
- ti,charge-current : Used to control and set the charging current. This value
must be between 128mA and 8.128A with a 64mA step resolution. The POR value
is 0x0000h. This number is in mA (e.g. 8192), see spec for more information
@@ -25,6 +27,8 @@
- ti,external-control : Indicates that the charger is configured externally
and that the host should not attempt to enable/disable charging or set the
charge voltage/current.
+ - poll-interval : In case 'interrupts' is not specified, poll AC adapter
+ presence with this interval (milliseconds).
Example:
diff --git a/Documentation/devicetree/bindings/power_supply/maxim,max14656.txt b/Documentation/devicetree/bindings/power_supply/maxim,max14656.txt
new file mode 100644
index 0000000..e03e85a
--- /dev/null
+++ b/Documentation/devicetree/bindings/power_supply/maxim,max14656.txt
@@ -0,0 +1,25 @@
+Maxim MAX14656 / AL32 USB Charger Detector
+
+Required properties :
+- compatible : "maxim,max14656";
+- reg: i2c slave address
+- interrupt-parent: the phandle for the interrupt controller
+- interrupts: interrupt line
+
+Example:
+
+&i2c2 {
+ clock-frequency = <50000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
+ status = "okay";
+
+ max14656@35 {
+ compatible = "maxim,max14656";
+ reg = <0x35>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_charger_detect>;
+ interrupt-parent = <&gpio6>;
+ interrupts = <26 IRQ_TYPE_LEVEL_HIGH>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/regulator/anatop-regulator.txt b/Documentation/devicetree/bindings/regulator/anatop-regulator.txt
index 37c4ea0..1d58c8c 100644
--- a/Documentation/devicetree/bindings/regulator/anatop-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/anatop-regulator.txt
@@ -14,6 +14,7 @@
- anatop-delay-bit-shift: Bit shift for the step time register
- anatop-delay-bit-width: Number of bits used in the step time register
- vin-supply: The supply for this regulator
+- anatop-enable-bit: Regulator enable bit offset
Any property defined as part of the core regulator
binding, defined in regulator.txt, can also be used.
diff --git a/Documentation/devicetree/bindings/regulator/cpcap-regulator.txt b/Documentation/devicetree/bindings/regulator/cpcap-regulator.txt
new file mode 100644
index 0000000..675f443
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/cpcap-regulator.txt
@@ -0,0 +1,34 @@
+Motorola CPCAP PMIC voltage regulators
+------------------------------------
+
+Requires node properties:
+- "compatible" value one of:
+ "motorola,cpcap-regulator"
+ "motorola,mapphone-cpcap-regulator"
+
+Required regulator properties:
+- "regulator-name"
+- "regulator-enable-ramp-delay"
+- "regulator-min-microvolt"
+- "regulator-max-microvolt"
+
+Optional regulator properties:
+- "regulator-boot-on"
+
+See Documentation/devicetree/bindings/regulator/regulator.txt
+for more details about the regulator properties.
+
+Example:
+
+cpcap_regulator: regulator {
+ compatible = "motorola,cpcap-regulator";
+
+ cpcap_regulators: regulators {
+ sw5: SW5 {
+ regulator-min-microvolt = <5050000>;
+ regulator-max-microvolt = <5050000>;
+ regulator-enable-ramp-delay = <50000>;
+ regulator-boot-on;
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/regulator/gpio-regulator.txt b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt
index e5cac1e..dd1ed78 100644
--- a/Documentation/devicetree/bindings/regulator/gpio-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt
@@ -13,7 +13,7 @@
- startup-delay-us : Startup time in microseconds.
- enable-active-high : Polarity of GPIO is active high (default is low).
- regulator-type : Specifies what is being regulated, must be either
- "voltage" or "current", defaults to current.
+ "voltage" or "current", defaults to voltage.
Any property defined as part of the core regulator binding defined in
regulator.txt can also be used.
diff --git a/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.txt b/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.txt
index 1f8d6f8..4e3dfb5 100644
--- a/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.txt
@@ -22,6 +22,7 @@
"qcom,rpm-pm8841-regulators"
"qcom,rpm-pm8916-regulators"
"qcom,rpm-pm8941-regulators"
+ "qcom,rpm-pm8994-regulators"
"qcom,rpm-pma8084-regulators"
- vdd_s1-supply:
@@ -80,6 +81,56 @@
- vdd_s10-supply:
- vdd_s11-supply:
- vdd_s12-supply:
+- vdd_l1-supply:
+- vdd_l2_l26_l28-supply:
+- vdd_l3_l11-supply:
+- vdd_l4_l27_l31-supply:
+- vdd_l5_l7-supply:
+- vdd_l6_l12_l32-supply:
+- vdd_l5_l7-supply:
+- vdd_l8_l16_l30-supply:
+- vdd_l9_l10_l18_l22-supply:
+- vdd_l9_l10_l18_l22-supply:
+- vdd_l3_l11-supply:
+- vdd_l6_l12_l32-supply:
+- vdd_l13_l19_l23_l24-supply:
+- vdd_l14_l15-supply:
+- vdd_l14_l15-supply:
+- vdd_l8_l16_l30-supply:
+- vdd_l17_l29-supply:
+- vdd_l9_l10_l18_l22-supply:
+- vdd_l13_l19_l23_l24-supply:
+- vdd_l20_l21-supply:
+- vdd_l20_l21-supply:
+- vdd_l9_l10_l18_l22-supply:
+- vdd_l13_l19_l23_l24-supply:
+- vdd_l13_l19_l23_l24-supply:
+- vdd_l25-supply:
+- vdd_l2_l26_l28-supply:
+- vdd_l4_l27_l31-supply:
+- vdd_l2_l26_l28-supply:
+- vdd_l17_l29-supply:
+- vdd_l8_l16_l30-supply:
+- vdd_l4_l27_l31-supply:
+- vdd_l6_l12_l32-supply:
+- vdd_lvs1_2-supply:
+ Usage: optional (pm8994 only)
+ Value type: <phandle>
+ Definition: reference to regulator supplying the input pin, as
+ described in the data sheet
+
+- vdd_s1-supply:
+- vdd_s2-supply:
+- vdd_s3-supply:
+- vdd_s4-supply:
+- vdd_s5-supply:
+- vdd_s6-supply:
+- vdd_s7-supply:
+- vdd_s8-supply:
+- vdd_s9-supply:
+- vdd_s10-supply:
+- vdd_s11-supply:
+- vdd_s12-supply:
- vdd_l1_l11-supply:
- vdd_l2_l3_l4_l27-supply:
- vdd_l5_l7-supply:
@@ -113,6 +164,11 @@
l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, lvs1, lvs2,
lvs3, 5vs1, 5vs2
+pm8994:
+ s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, l1, l2, l3, l4, l5,
+ l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20,
+ l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, lvs1, lvs2
+
pma8084:
s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, l1, l2, l3, l4, l5,
l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20,
diff --git a/Documentation/devicetree/bindings/spi/spi-lantiq-ssc.txt b/Documentation/devicetree/bindings/spi/spi-lantiq-ssc.txt
new file mode 100644
index 0000000..6069b95
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/spi-lantiq-ssc.txt
@@ -0,0 +1,29 @@
+Lantiq Synchronous Serial Controller (SSC) SPI master driver
+
+Required properties:
+- compatible: "lantiq,ase-spi", "lantiq,falcon-spi", "lantiq,xrx100-spi"
+- #address-cells: see spi-bus.txt
+- #size-cells: see spi-bus.txt
+- reg: address and length of the spi master registers
+- interrupts: should contain the "spi_rx", "spi_tx" and "spi_err" interrupt.
+
+
+Optional properties:
+- clocks: spi clock phandle
+- num-cs: see spi-bus.txt, set to 8 if unset
+- base-cs: the number of the first chip select, set to 1 if unset.
+
+Example:
+
+
+spi: spi@E100800 {
+ compatible = "lantiq,xrx200-spi", "lantiq,xrx100-spi";
+ reg = <0xE100800 0x100>;
+ interrupt-parent = <&icu0>;
+ interrupts = <22 23 24>;
+ interrupt-names = "spi_rx", "spi_tx", "spi_err";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ num-cs = <6>;
+ base-cs = <1>;
+};
diff --git a/Documentation/devicetree/bindings/spi/spi-rockchip.txt b/Documentation/devicetree/bindings/spi/spi-rockchip.txt
index d2ca153..83da493 100644
--- a/Documentation/devicetree/bindings/spi/spi-rockchip.txt
+++ b/Documentation/devicetree/bindings/spi/spi-rockchip.txt
@@ -31,6 +31,10 @@
- rx-sample-delay-ns: nanoseconds to delay after the SCLK edge before sampling
Rx data (may need to be fine tuned for high capacitance lines).
No delay (0) by default.
+- pinctrl-names: Names for the pin configuration(s); may be "default" or
+ "sleep", where the "sleep" configuration may describe the state
+ the pins should be in during system suspend. See also
+ pinctrl/pinctrl-bindings.txt.
Example:
@@ -46,4 +50,7 @@
interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru SCLK_SPI0>, <&cru PCLK_SPI0>;
clock-names = "spiclk", "apb_pclk";
+ pinctrl-0 = <&spi1_pins>;
+ pinctrl-1 = <&spi1_sleep>;
+ pinctrl-names = "default", "sleep";
};
diff --git a/Documentation/devicetree/bindings/timer/cortina,gemini-timer.txt b/Documentation/devicetree/bindings/timer/cortina,gemini-timer.txt
new file mode 100644
index 0000000..16ea1d3
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/cortina,gemini-timer.txt
@@ -0,0 +1,22 @@
+Cortina Systems Gemini timer
+
+This timer is embedded in the Cortina Systems Gemini SoCs.
+
+Required properties:
+
+- compatible : Must be "cortina,gemini-timer"
+- reg : Should contain registers location and length
+- interrupts : Should contain the three timer interrupts with
+ flags for rising edge
+- syscon : a phandle to the global Gemini system controller
+
+Example:
+
+timer@43000000 {
+ compatible = "cortina,gemini-timer";
+ reg = <0x43000000 0x1000>;
+ interrupts = <14 IRQ_TYPE_EDGE_RISING>, /* Timer 1 */
+ <15 IRQ_TYPE_EDGE_RISING>, /* Timer 2 */
+ <16 IRQ_TYPE_EDGE_RISING>; /* Timer 3 */
+ syscon = <&syscon>;
+};
diff --git a/Documentation/devicetree/bindings/timer/renesas,ostm.txt b/Documentation/devicetree/bindings/timer/renesas,ostm.txt
new file mode 100644
index 0000000..be3ae0f
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/renesas,ostm.txt
@@ -0,0 +1,30 @@
+* Renesas OS Timer (OSTM)
+
+The OSTM is a multi-channel 32-bit timer/counter with fixed clock
+source that can operate in either interval count down timer or free-running
+compare match mode.
+
+Channels are independent from each other.
+
+Required Properties:
+
+ - compatible: must be one or more of the following:
+ - "renesas,r7s72100-ostm" for the r7s72100 OSTM
+ - "renesas,ostm" for any OSTM
+ This is a fallback for the above renesas,*-ostm entries
+
+ - reg: base address and length of the register block for a timer channel.
+
+ - interrupts: interrupt specifier for the timer channel.
+
+ - clocks: clock specifier for the timer channel.
+
+Example: R7S72100 (RZ/A1H) OSTM node
+
+ ostm0: timer@fcfec000 {
+ compatible = "renesas,r7s72100-ostm", "renesas,ostm";
+ reg = <0xfcfec000 0x30>;
+ interrupts = <GIC_SPI 102 IRQ_TYPE_EDGE_RISING>;
+ clocks = <&mstp5_clks R7S72100_CLK_OSTM0>;
+ power-domains = <&cpg_clocks>;
+ };
diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
index ca9d1eb..bf34d5b 100644
--- a/Documentation/driver-model/devres.txt
+++ b/Documentation/driver-model/devres.txt
@@ -306,6 +306,11 @@
devm_request_any_context_irq()
devm_request_irq()
devm_request_threaded_irq()
+ devm_irq_alloc_descs()
+ devm_irq_alloc_desc()
+ devm_irq_alloc_desc_at()
+ devm_irq_alloc_desc_from()
+ devm_irq_alloc_descs_from()
LED
devm_led_classdev_register()
diff --git a/Documentation/hwmon/hwmon-kernel-api.txt b/Documentation/hwmon/hwmon-kernel-api.txt
index 2505ae6..53a8066 100644
--- a/Documentation/hwmon/hwmon-kernel-api.txt
+++ b/Documentation/hwmon/hwmon-kernel-api.txt
@@ -89,6 +89,10 @@
hwmon_device_register_with_info and if the automatic (device managed)
removal would be too late.
+All supported hwmon device registration functions only accept valid device
+names. Device names including invalid characters (whitespace, '*', or '-')
+will be rejected. The 'name' parameter is mandatory.
+
Using devm_hwmon_device_register_with_info()
--------------------------------------------
diff --git a/Documentation/hwmon/lm70 b/Documentation/hwmon/lm70
index 1bb2db4..c3a1f2e 100644
--- a/Documentation/hwmon/lm70
+++ b/Documentation/hwmon/lm70
@@ -6,6 +6,8 @@
Datasheet: http://www.national.com/pf/LM/LM70.html
* Texas Instruments TMP121/TMP123
Information: http://focus.ti.com/docs/prod/folders/print/tmp121.html
+ * Texas Instruments TMP122/TMP124
+ Information: http://www.ti.com/product/tmp122
* National Semiconductor LM71
Datasheet: http://www.ti.com/product/LM71
* National Semiconductor LM74
@@ -35,8 +37,10 @@
with a "SPI master controller driver", see drivers/spi/spi_lm70llp.c
and its associated documentation.
-The LM74 and TMP121/TMP123 are very similar; main difference is 13-bit
-temperature data (0.0625 degrees celsius resolution).
+The LM74 and TMP121/TMP122/TMP123/TMP124 are very similar; main difference is
+13-bit temperature data (0.0625 degrees celsius resolution).
+
+The TMP122/TMP124 also feature configurable temperature thresholds.
The LM71 is also very similar; main difference is 14-bit temperature
data (0.03125 degrees celsius resolution).
diff --git a/Documentation/hwmon/sht21 b/Documentation/hwmon/sht21
index db17fda..47f4765 100644
--- a/Documentation/hwmon/sht21
+++ b/Documentation/hwmon/sht21
@@ -35,6 +35,7 @@
temp1_input - temperature input
humidity1_input - humidity input
+eic - Electronic Identification Code
Notes
-----
@@ -45,5 +46,5 @@
Celsius, the device should not be active for more than 10% of the time,
e.g. maximum two measurements per second at the given resolution.
-Different resolutions, the on-chip heater, using the CRC checksum and reading
-the serial number are not supported yet.
+Different resolutions, the on-chip heater, and using the CRC checksum
+are not supported yet.
diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
index 2cc95ad..fc337c3 100644
--- a/Documentation/hwmon/sysfs-interface
+++ b/Documentation/hwmon/sysfs-interface
@@ -86,8 +86,9 @@
name The chip name.
This should be a short, lowercase string, not containing
- spaces nor dashes, representing the chip name. This is
- the only mandatory attribute.
+ whitespace, dashes, or the wildcard character '*'.
+ This attribute represents the chip name. It is the only
+ mandatory attribute.
I2C devices get this attribute created automatically.
RO
diff --git a/Documentation/leds/leds-class.txt b/Documentation/leds/leds-class.txt
index f1f7ec9..836cb16 100644
--- a/Documentation/leds/leds-class.txt
+++ b/Documentation/leds/leds-class.txt
@@ -65,6 +65,21 @@
blinking, returns -EBUSY if software blink fallback is enabled.
+LED registration API
+====================
+
+A driver wanting to register a LED classdev for use by other drivers /
+userspace needs to allocate and fill a led_classdev struct and then call
+[devm_]led_classdev_register. If the non devm version is used the driver
+must call led_classdev_unregister from its remove function before
+free-ing the led_classdev struct.
+
+If the driver can detect hardware initiated brightness changes and thus
+wants to have a brightness_hw_changed attribute then the LED_BRIGHT_HW_CHANGED
+flag must be set in flags before registering. Calling
+led_classdev_notify_brightness_hw_changed on a classdev not registered with
+the LED_BRIGHT_HW_CHANGED flag is a bug and will trigger a WARN_ON.
+
Hardware accelerated blink of LEDs
==================================
diff --git a/Documentation/livepatch/livepatch.txt b/Documentation/livepatch/livepatch.txt
index f596731..7f04e13 100644
--- a/Documentation/livepatch/livepatch.txt
+++ b/Documentation/livepatch/livepatch.txt
@@ -329,25 +329,6 @@
by "notrace".
- + Anything inlined into __schedule() can not be patched.
-
- The switch_to macro is inlined into __schedule(). It switches the
- context between two processes in the middle of the macro. It does
- not save RIP in x86_64 version (contrary to 32-bit version). Instead,
- the currently used __schedule()/switch_to() handles both processes.
-
- Now, let's have two different tasks. One calls the original
- __schedule(), its registers are stored in a defined order and it
- goes to sleep in the switch_to macro and some other task is restored
- using the original __schedule(). Then there is the second task which
- calls patched__schedule(), it goes to sleep there and the first task
- is picked by the patched__schedule(). Its RSP is restored and now
- the registers should be restored as well. But the order is different
- in the new patched__schedule(), so...
-
- There is work in progress to remove this limitation.
-
-
+ Livepatch modules can not be removed.
The current implementation just redirects the functions at the very
diff --git a/Documentation/locking/ww-mutex-design.txt b/Documentation/locking/ww-mutex-design.txt
index 8a112dc..34c3a1b 100644
--- a/Documentation/locking/ww-mutex-design.txt
+++ b/Documentation/locking/ww-mutex-design.txt
@@ -309,11 +309,15 @@
normal mutex locks, which are far more common. As such there is only a small
increase in code size if wait/wound mutexes are not used.
+ We maintain the following invariants for the wait list:
+ (1) Waiters with an acquire context are sorted by stamp order; waiters
+ without an acquire context are interspersed in FIFO order.
+ (2) Among waiters with contexts, only the first one can have other locks
+ acquired already (ctx->acquired > 0). Note that this waiter may come
+ after other waiters without contexts in the list.
+
In general, not much contention is expected. The locks are typically used to
- serialize access to resources for devices. The only way to make wakeups
- smarter would be at the cost of adding a field to struct mutex_waiter. This
- would add overhead to all cases where normal mutexes are used, and
- ww_mutexes are generally less performance sensitive.
+ serialize access to resources for devices.
Lockdep:
Special care has been taken to warn for as many cases of api abuse
diff --git a/Documentation/media/uapi/v4l/pixfmt-007.rst b/Documentation/media/uapi/v4l/pixfmt-007.rst
index 44bb5a7..95a23a2 100644
--- a/Documentation/media/uapi/v4l/pixfmt-007.rst
+++ b/Documentation/media/uapi/v4l/pixfmt-007.rst
@@ -211,7 +211,13 @@
The :ref:`srgb` standard defines the colorspace used by most webcams
and computer graphics. The default transfer function is
``V4L2_XFER_FUNC_SRGB``. The default Y'CbCr encoding is
-``V4L2_YCBCR_ENC_601``. The default Y'CbCr quantization is full range.
+``V4L2_YCBCR_ENC_601``. The default Y'CbCr quantization is limited range.
+
+Note that the :ref:`sycc` standard specifies full range quantization,
+however all current capture hardware supported by the kernel convert
+R'G'B' to limited range Y'CbCr. So choosing full range as the default
+would break how applications interpret the quantization range.
+
The chromaticities of the primary colors and the white reference are:
@@ -276,7 +282,7 @@
Y' is clamped to the range [0…1] and Cb and Cr are clamped to the range
[-0.5…0.5]. This transform is identical to one defined in SMPTE
-170M/BT.601. The Y'CbCr quantization is full range.
+170M/BT.601. The Y'CbCr quantization is limited range.
.. _col-adobergb:
@@ -288,10 +294,15 @@
graphics that use the AdobeRGB colorspace. This is also known as the
:ref:`oprgb` standard. The default transfer function is
``V4L2_XFER_FUNC_ADOBERGB``. The default Y'CbCr encoding is
-``V4L2_YCBCR_ENC_601``. The default Y'CbCr quantization is full
-range. The chromaticities of the primary colors and the white reference
-are:
+``V4L2_YCBCR_ENC_601``. The default Y'CbCr quantization is limited
+range.
+Note that the :ref:`oprgb` standard specifies full range quantization,
+however all current capture hardware supported by the kernel convert
+R'G'B' to limited range Y'CbCr. So choosing full range as the default
+would break how applications interpret the quantization range.
+
+The chromaticities of the primary colors and the white reference are:
.. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}|
@@ -344,7 +355,7 @@
Y' is clamped to the range [0…1] and Cb and Cr are clamped to the range
[-0.5…0.5]. This transform is identical to one defined in SMPTE
-170M/BT.601. The Y'CbCr quantization is full range.
+170M/BT.601. The Y'CbCr quantization is limited range.
.. _col-bt2020:
diff --git a/Documentation/memory-barriers.txt b/Documentation/memory-barriers.txt
index ba818ec..d2b0a8d 100644
--- a/Documentation/memory-barriers.txt
+++ b/Documentation/memory-barriers.txt
@@ -640,6 +640,10 @@
CONTROL DEPENDENCIES
--------------------
+Control dependencies can be a bit tricky because current compilers do
+not understand them. The purpose of this section is to help you prevent
+the compiler's ignorance from breaking your code.
+
A load-load control dependency requires a full read memory barrier, not
simply a data dependency barrier to make it work correctly. Consider the
following bit of code:
@@ -667,14 +671,15 @@
q = READ_ONCE(a);
if (q) {
- WRITE_ONCE(b, p);
+ WRITE_ONCE(b, 1);
}
-Control dependencies pair normally with other types of barriers. That
-said, please note that READ_ONCE() is not optional! Without the
-READ_ONCE(), the compiler might combine the load from 'a' with other
-loads from 'a', and the store to 'b' with other stores to 'b', with
-possible highly counterintuitive effects on ordering.
+Control dependencies pair normally with other types of barriers.
+That said, please note that neither READ_ONCE() nor WRITE_ONCE()
+are optional! Without the READ_ONCE(), the compiler might combine the
+load from 'a' with other loads from 'a'. Without the WRITE_ONCE(),
+the compiler might combine the store to 'b' with other stores to 'b'.
+Either can result in highly counterintuitive effects on ordering.
Worse yet, if the compiler is able to prove (say) that the value of
variable 'a' is always non-zero, it would be well within its rights
@@ -682,7 +687,7 @@
as follows:
q = a;
- b = p; /* BUG: Compiler and CPU can both reorder!!! */
+ b = 1; /* BUG: Compiler and CPU can both reorder!!! */
So don't leave out the READ_ONCE().
@@ -692,11 +697,11 @@
q = READ_ONCE(a);
if (q) {
barrier();
- WRITE_ONCE(b, p);
+ WRITE_ONCE(b, 1);
do_something();
} else {
barrier();
- WRITE_ONCE(b, p);
+ WRITE_ONCE(b, 1);
do_something_else();
}
@@ -705,12 +710,12 @@
q = READ_ONCE(a);
barrier();
- WRITE_ONCE(b, p); /* BUG: No ordering vs. load from a!!! */
+ WRITE_ONCE(b, 1); /* BUG: No ordering vs. load from a!!! */
if (q) {
- /* WRITE_ONCE(b, p); -- moved up, BUG!!! */
+ /* WRITE_ONCE(b, 1); -- moved up, BUG!!! */
do_something();
} else {
- /* WRITE_ONCE(b, p); -- moved up, BUG!!! */
+ /* WRITE_ONCE(b, 1); -- moved up, BUG!!! */
do_something_else();
}
@@ -723,10 +728,10 @@
q = READ_ONCE(a);
if (q) {
- smp_store_release(&b, p);
+ smp_store_release(&b, 1);
do_something();
} else {
- smp_store_release(&b, p);
+ smp_store_release(&b, 1);
do_something_else();
}
@@ -735,10 +740,10 @@
q = READ_ONCE(a);
if (q) {
- WRITE_ONCE(b, p);
+ WRITE_ONCE(b, 1);
do_something();
} else {
- WRITE_ONCE(b, r);
+ WRITE_ONCE(b, 2);
do_something_else();
}
@@ -751,10 +756,10 @@
q = READ_ONCE(a);
if (q % MAX) {
- WRITE_ONCE(b, p);
+ WRITE_ONCE(b, 1);
do_something();
} else {
- WRITE_ONCE(b, r);
+ WRITE_ONCE(b, 2);
do_something_else();
}
@@ -763,7 +768,7 @@
transform the above code into the following:
q = READ_ONCE(a);
- WRITE_ONCE(b, p);
+ WRITE_ONCE(b, 1);
do_something_else();
Given this transformation, the CPU is not required to respect the ordering
@@ -776,10 +781,10 @@
q = READ_ONCE(a);
BUILD_BUG_ON(MAX <= 1); /* Order load from a with store to b. */
if (q % MAX) {
- WRITE_ONCE(b, p);
+ WRITE_ONCE(b, 1);
do_something();
} else {
- WRITE_ONCE(b, r);
+ WRITE_ONCE(b, 2);
do_something_else();
}
@@ -812,30 +817,28 @@
q = READ_ONCE(a);
if (q) {
- WRITE_ONCE(b, p);
+ WRITE_ONCE(b, 1);
} else {
- WRITE_ONCE(b, r);
+ WRITE_ONCE(b, 2);
}
- WRITE_ONCE(c, 1); /* BUG: No ordering against the read from "a". */
+ WRITE_ONCE(c, 1); /* BUG: No ordering against the read from 'a'. */
It is tempting to argue that there in fact is ordering because the
compiler cannot reorder volatile accesses and also cannot reorder
-the writes to "b" with the condition. Unfortunately for this line
-of reasoning, the compiler might compile the two writes to "b" as
+the writes to 'b' with the condition. Unfortunately for this line
+of reasoning, the compiler might compile the two writes to 'b' as
conditional-move instructions, as in this fanciful pseudo-assembly
language:
ld r1,a
- ld r2,p
- ld r3,r
cmp r1,$0
- cmov,ne r4,r2
- cmov,eq r4,r3
+ cmov,ne r4,$1
+ cmov,eq r4,$2
st r4,b
st $1,c
A weakly ordered CPU would have no dependency of any sort between the load
-from "a" and the store to "c". The control dependencies would extend
+from 'a' and the store to 'c'. The control dependencies would extend
only to the pair of cmov instructions and the store depending on them.
In short, control dependencies apply only to the stores in the then-clause
and else-clause of the if-statement in question (including functions
@@ -843,7 +846,7 @@
Finally, control dependencies do -not- provide transitivity. This is
demonstrated by two related examples, with the initial values of
-x and y both being zero:
+'x' and 'y' both being zero:
CPU 0 CPU 1
======================= =======================
@@ -915,6 +918,9 @@
(*) Control dependencies do -not- provide transitivity. If you
need transitivity, use smp_mb().
+ (*) Compilers do not understand control dependencies. It is therefore
+ your job to ensure that they do not break your code.
+
SMP BARRIER PAIRING
-------------------
diff --git a/Documentation/mtd/intel-spi.txt b/Documentation/mtd/intel-spi.txt
new file mode 100644
index 0000000..bc35772
--- /dev/null
+++ b/Documentation/mtd/intel-spi.txt
@@ -0,0 +1,88 @@
+Upgrading BIOS using intel-spi
+------------------------------
+
+Many Intel CPUs like Baytrail and Braswell include SPI serial flash host
+controller which is used to hold BIOS and other platform specific data.
+Since contents of the SPI serial flash is crucial for machine to function,
+it is typically protected by different hardware protection mechanisms to
+avoid accidental (or on purpose) overwrite of the content.
+
+Not all manufacturers protect the SPI serial flash, mainly because it
+allows upgrading the BIOS image directly from an OS.
+
+The intel-spi driver makes it possible to read and write the SPI serial
+flash, if certain protection bits are not set and locked. If it finds
+any of them set, the whole MTD device is made read-only to prevent
+partial overwrites. By default the driver exposes SPI serial flash
+contents as read-only but it can be changed from kernel command line,
+passing "intel-spi.writeable=1".
+
+Please keep in mind that overwriting the BIOS image on SPI serial flash
+might render the machine unbootable and requires special equipment like
+Dediprog to revive. You have been warned!
+
+Below are the steps how to upgrade MinnowBoard MAX BIOS directly from
+Linux.
+
+ 1) Download and extract the latest Minnowboard MAX BIOS SPI image
+ [1]. At the time writing this the latest image is v92.
+
+ 2) Install mtd-utils package [2]. We need this in order to erase the SPI
+ serial flash. Distros like Debian and Fedora have this prepackaged with
+ name "mtd-utils".
+
+ 3) Add "intel-spi.writeable=1" to the kernel command line and reboot
+ the board (you can also reload the driver passing "writeable=1" as
+ module parameter to modprobe).
+
+ 4) Once the board is up and running again, find the right MTD partition
+ (it is named as "BIOS"):
+
+ # cat /proc/mtd
+ dev: size erasesize name
+ mtd0: 00800000 00001000 "BIOS"
+
+ So here it will be /dev/mtd0 but it may vary.
+
+ 5) Make backup of the existing image first:
+
+ # dd if=/dev/mtd0ro of=bios.bak
+ 16384+0 records in
+ 16384+0 records out
+ 8388608 bytes (8.4 MB) copied, 10.0269 s, 837 kB/s
+
+ 6) Verify the backup
+
+ # sha1sum /dev/mtd0ro bios.bak
+ fdbb011920572ca6c991377c4b418a0502668b73 /dev/mtd0ro
+ fdbb011920572ca6c991377c4b418a0502668b73 bios.bak
+
+ The SHA1 sums must match. Otherwise do not continue any further!
+
+ 7) Erase the SPI serial flash. After this step, do not reboot the
+ board! Otherwise it will not start anymore.
+
+ # flash_erase /dev/mtd0 0 0
+ Erasing 4 Kibyte @ 7ff000 -- 100 % complete
+
+ 8) Once completed without errors you can write the new BIOS image:
+
+ # dd if=MNW2MAX1.X64.0092.R01.1605221712.bin of=/dev/mtd0
+
+ 9) Verify that the new content of the SPI serial flash matches the new
+ BIOS image:
+
+ # sha1sum /dev/mtd0ro MNW2MAX1.X64.0092.R01.1605221712.bin
+ 9b4df9e4be2057fceec3a5529ec3d950836c87a2 /dev/mtd0ro
+ 9b4df9e4be2057fceec3a5529ec3d950836c87a2 MNW2MAX1.X64.0092.R01.1605221712.bin
+
+ The SHA1 sums should match.
+
+ 10) Now you can reboot your board and observe the new BIOS starting up
+ properly.
+
+References
+----------
+
+[1] https://firmware.intel.com/sites/default/files/MinnowBoard.MAX_.X64.92.R01.zip
+[2] http://www.linux-mtd.infradead.org/
diff --git a/Documentation/power/opp.txt b/Documentation/power/opp.txt
index c6279c2..0c007e2 100644
--- a/Documentation/power/opp.txt
+++ b/Documentation/power/opp.txt
@@ -79,22 +79,6 @@
specific framework which uses the OPP library. Similar care needs to be taken
care to refresh the cpufreq table in cases of these operations.
-WARNING on OPP List locking mechanism:
--------------------------------------------------
-OPP library uses RCU for exclusivity. RCU allows the query functions to operate
-in multiple contexts and this synchronization mechanism is optimal for a read
-intensive operations on data structure as the OPP library caters to.
-
-To ensure that the data retrieved are sane, the users such as SoC framework
-should ensure that the section of code operating on OPP queries are locked
-using RCU read locks. The opp_find_freq_{exact,ceil,floor},
-opp_get_{voltage, freq, opp_count} fall into this category.
-
-opp_{add,enable,disable} are updaters which use mutex and implement it's own
-RCU locking mechanisms. These functions should *NOT* be called under RCU locks
-and other contexts that prevent blocking functions in RCU or mutex operations
-from working.
-
2. Initial OPP List Registration
================================
The SoC implementation calls dev_pm_opp_add function iteratively to add OPPs per
@@ -137,15 +121,18 @@
found, else returns error. These errors are expected to be handled by standard
error checks such as IS_ERR() and appropriate actions taken by the caller.
+Callers of these functions shall call dev_pm_opp_put() after they have used the
+OPP. Otherwise the memory for the OPP will never get freed and result in
+memleak.
+
dev_pm_opp_find_freq_exact - Search for an OPP based on an *exact* frequency and
availability. This function is especially useful to enable an OPP which
is not available by default.
Example: In a case when SoC framework detects a situation where a
higher frequency could be made available, it can use this function to
find the OPP prior to call the dev_pm_opp_enable to actually make it available.
- rcu_read_lock();
opp = dev_pm_opp_find_freq_exact(dev, 1000000000, false);
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
/* dont operate on the pointer.. just do a sanity check.. */
if (IS_ERR(opp)) {
pr_err("frequency not disabled!\n");
@@ -163,9 +150,8 @@
frequency.
Example: To find the highest opp for a device:
freq = ULONG_MAX;
- rcu_read_lock();
- dev_pm_opp_find_freq_floor(dev, &freq);
- rcu_read_unlock();
+ opp = dev_pm_opp_find_freq_floor(dev, &freq);
+ dev_pm_opp_put(opp);
dev_pm_opp_find_freq_ceil - Search for an available OPP which is *at least* the
provided frequency. This function is useful while searching for a
@@ -173,17 +159,15 @@
frequency.
Example 1: To find the lowest opp for a device:
freq = 0;
- rcu_read_lock();
- dev_pm_opp_find_freq_ceil(dev, &freq);
- rcu_read_unlock();
+ opp = dev_pm_opp_find_freq_ceil(dev, &freq);
+ dev_pm_opp_put(opp);
Example 2: A simplified implementation of a SoC cpufreq_driver->target:
soc_cpufreq_target(..)
{
/* Do stuff like policy checks etc. */
/* Find the best frequency match for the req */
- rcu_read_lock();
opp = dev_pm_opp_find_freq_ceil(dev, &freq);
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
if (!IS_ERR(opp))
soc_switch_to_freq_voltage(freq);
else
@@ -208,9 +192,8 @@
implementation might choose to do something as follows:
if (cur_temp < temp_low_thresh) {
/* Enable 1GHz if it was disabled */
- rcu_read_lock();
opp = dev_pm_opp_find_freq_exact(dev, 1000000000, false);
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
/* just error check */
if (!IS_ERR(opp))
ret = dev_pm_opp_enable(dev, 1000000000);
@@ -224,9 +207,8 @@
choose to do something as follows:
if (cur_temp > temp_high_thresh) {
/* Disable 1GHz if it was enabled */
- rcu_read_lock();
opp = dev_pm_opp_find_freq_exact(dev, 1000000000, true);
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
/* just error check */
if (!IS_ERR(opp))
ret = dev_pm_opp_disable(dev, 1000000000);
@@ -249,10 +231,9 @@
soc_switch_to_freq_voltage(freq)
{
/* do things */
- rcu_read_lock();
opp = dev_pm_opp_find_freq_ceil(dev, &freq);
v = dev_pm_opp_get_voltage(opp);
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
if (v)
regulator_set_voltage(.., v);
/* do other things */
@@ -266,12 +247,12 @@
{
/* do things.. */
max_freq = ULONG_MAX;
- rcu_read_lock();
max_opp = dev_pm_opp_find_freq_floor(dev,&max_freq);
requested_opp = dev_pm_opp_find_freq_ceil(dev,&freq);
if (!IS_ERR(max_opp) && !IS_ERR(requested_opp))
r = soc_test_validity(max_opp, requested_opp);
- rcu_read_unlock();
+ dev_pm_opp_put(max_opp);
+ dev_pm_opp_put(requested_opp);
/* do other things */
}
soc_test_validity(..)
@@ -289,7 +270,6 @@
soc_notify_coproc_available_frequencies()
{
/* Do things */
- rcu_read_lock();
num_available = dev_pm_opp_get_opp_count(dev);
speeds = kzalloc(sizeof(u32) * num_available, GFP_KERNEL);
/* populate the table in increasing order */
@@ -298,8 +278,8 @@
speeds[i] = freq;
freq++;
i++;
+ dev_pm_opp_put(opp);
}
- rcu_read_unlock();
soc_notify_coproc(AVAILABLE_FREQs, speeds, num_available);
/* Do other things */
diff --git a/Documentation/power/pm_qos_interface.txt b/Documentation/power/pm_qos_interface.txt
index 129f7c0..21d2d48 100644
--- a/Documentation/power/pm_qos_interface.txt
+++ b/Documentation/power/pm_qos_interface.txt
@@ -163,8 +163,7 @@
under the device's power directory.
Notification mechanisms:
-The per-device PM QoS framework has 2 different and distinct notification trees:
-a per-device notification tree and a global notification tree.
+The per-device PM QoS framework has a per-device notification tree.
int dev_pm_qos_add_notifier(device, notifier):
Adds a notification callback function for the device.
@@ -174,16 +173,6 @@
int dev_pm_qos_remove_notifier(device, notifier):
Removes the notification callback function for the device.
-int dev_pm_qos_add_global_notifier(notifier):
-Adds a notification callback function in the global notification tree of the
-framework.
-The callback is called when the aggregated value for any device is changed
-(for resume latency device PM QoS only).
-
-int dev_pm_qos_remove_global_notifier(notifier):
-Removes the notification callback function from the global notification tree
-of the framework.
-
Active state latency tolerance
diff --git a/Documentation/power/states.txt b/Documentation/power/states.txt
index 008ecb5..bc45482 100644
--- a/Documentation/power/states.txt
+++ b/Documentation/power/states.txt
@@ -25,7 +25,7 @@
Consequently, there are two ways to cause the system to go into the
Suspend-To-Idle sleep state. The first one is to write "freeze" directly to
/sys/power/state. The second one is to write "s2idle" to /sys/power/mem_sleep
-and then to wrtie "mem" to /sys/power/state. Similarly, there are two ways
+and then to write "mem" to /sys/power/state. Similarly, there are two ways
to cause the system to go into the Power-On Suspend sleep state (the strings to
write to the control files in that case are "standby" or "shallow" and "mem",
respectively) if that state is supported by the platform. In turn, there is
diff --git a/Documentation/scheduler/sched-deadline.txt b/Documentation/scheduler/sched-deadline.txt
index 8e37b0b..cbc1b46 100644
--- a/Documentation/scheduler/sched-deadline.txt
+++ b/Documentation/scheduler/sched-deadline.txt
@@ -408,6 +408,11 @@
* the new scheduling related syscalls that manipulate it, i.e.,
sched_setattr() and sched_getattr() are implemented.
+ For debugging purposes, the leftover runtime and absolute deadline of a
+ SCHED_DEADLINE task can be retrieved through /proc/<pid>/sched (entries
+ dl.runtime and dl.deadline, both values in ns). A programmatic way to
+ retrieve these values from production code is under discussion.
+
4.3 Default behavior
---------------------
@@ -476,6 +481,7 @@
Still missing:
+ - programmatic way to retrieve current runtime and absolute deadline
- refinements to deadline inheritance, especially regarding the possibility
of retaining bandwidth isolation among non-interacting tasks. This is
being studied from both theoretical and practical points of view, and
diff --git a/Documentation/scheduler/sched-rt-group.txt b/Documentation/scheduler/sched-rt-group.txt
index a03f0d9..d8fce3e 100644
--- a/Documentation/scheduler/sched-rt-group.txt
+++ b/Documentation/scheduler/sched-rt-group.txt
@@ -158,11 +158,11 @@
Consider two sibling groups A and B; both have 50% bandwidth, but A's
period is twice the length of B's.
-* group A: period=100000us, runtime=10000us
- - this runs for 0.01s once every 0.1s
+* group A: period=100000us, runtime=50000us
+ - this runs for 0.05s once every 0.1s
-* group B: period= 50000us, runtime=10000us
- - this runs for 0.01s twice every 0.1s (or once every 0.05 sec).
+* group B: period= 50000us, runtime=25000us
+ - this runs for 0.025s twice every 0.1s (or once every 0.05 sec).
This means that currently a while (1) loop in A will run for the full period of
B and can starve B's tasks (assuming they are of lower priority) for a whole
diff --git a/Documentation/spi/ep93xx_spi b/Documentation/spi/ep93xx_spi
deleted file mode 100644
index 832ddce..0000000
--- a/Documentation/spi/ep93xx_spi
+++ /dev/null
@@ -1,105 +0,0 @@
-Cirrus EP93xx SPI controller driver HOWTO
-=========================================
-
-ep93xx_spi driver brings SPI master support for EP93xx SPI controller. Chip
-selects are implemented with GPIO lines.
-
-NOTE: If possible, don't use SFRMOUT (SFRM1) signal as a chip select. It will
-not work correctly (it cannot be controlled by software). Use GPIO lines
-instead.
-
-Sample configuration
-====================
-
-Typically driver configuration is done in platform board files (the files under
-arch/arm/mach-ep93xx/*.c). In this example we configure MMC over SPI through
-this driver on TS-7260 board. You can adapt the code to suit your needs.
-
-This example uses EGPIO9 as SD/MMC card chip select (this is wired in DIO1
-header on the board).
-
-You need to select CONFIG_MMC_SPI to use mmc_spi driver.
-
-arch/arm/mach-ep93xx/ts72xx.c:
-
-...
-#include <linux/gpio.h>
-#include <linux/spi/spi.h>
-
-#include <linux/platform_data/spi-ep93xx.h>
-
-/* this is our GPIO line used for chip select */
-#define MMC_CHIP_SELECT_GPIO EP93XX_GPIO_LINE_EGPIO9
-
-static int ts72xx_mmc_spi_setup(struct spi_device *spi)
-{
- int err;
-
- err = gpio_request(MMC_CHIP_SELECT_GPIO, spi->modalias);
- if (err)
- return err;
-
- gpio_direction_output(MMC_CHIP_SELECT_GPIO, 1);
-
- return 0;
-}
-
-static void ts72xx_mmc_spi_cleanup(struct spi_device *spi)
-{
- gpio_set_value(MMC_CHIP_SELECT_GPIO, 1);
- gpio_direction_input(MMC_CHIP_SELECT_GPIO);
- gpio_free(MMC_CHIP_SELECT_GPIO);
-}
-
-static void ts72xx_mmc_spi_cs_control(struct spi_device *spi, int value)
-{
- gpio_set_value(MMC_CHIP_SELECT_GPIO, value);
-}
-
-static struct ep93xx_spi_chip_ops ts72xx_mmc_spi_ops = {
- .setup = ts72xx_mmc_spi_setup,
- .cleanup = ts72xx_mmc_spi_cleanup,
- .cs_control = ts72xx_mmc_spi_cs_control,
-};
-
-static struct spi_board_info ts72xx_spi_devices[] __initdata = {
- {
- .modalias = "mmc_spi",
- .controller_data = &ts72xx_mmc_spi_ops,
- /*
- * We use 10 MHz even though the maximum is 7.4 MHz. The driver
- * will limit it automatically to max. frequency.
- */
- .max_speed_hz = 10 * 1000 * 1000,
- .bus_num = 0,
- .chip_select = 0,
- .mode = SPI_MODE_0,
- },
-};
-
-static struct ep93xx_spi_info ts72xx_spi_info = {
- .num_chipselect = ARRAY_SIZE(ts72xx_spi_devices),
-};
-
-static void __init ts72xx_init_machine(void)
-{
- ...
- ep93xx_register_spi(&ts72xx_spi_info, ts72xx_spi_devices,
- ARRAY_SIZE(ts72xx_spi_devices));
-}
-
-The driver can use DMA for the transfers also. In this case ts72xx_spi_info
-becomes:
-
-static struct ep93xx_spi_info ts72xx_spi_info = {
- .num_chipselect = ARRAY_SIZE(ts72xx_spi_devices),
- .use_dma = true;
-};
-
-Note that CONFIG_EP93XX_DMA should be enabled as well.
-
-Thanks to
-=========
-Martin Guy, H. Hartley Sweeten and others who helped me during development of
-the driver. Simplemachines.it donated me a Sim.One board which I used testing
-the driver on EP9307.
diff --git a/Documentation/timers/timer_stats.txt b/Documentation/timers/timer_stats.txt
deleted file mode 100644
index de835ee..0000000
--- a/Documentation/timers/timer_stats.txt
+++ /dev/null
@@ -1,73 +0,0 @@
-timer_stats - timer usage statistics
-------------------------------------
-
-timer_stats is a debugging facility to make the timer (ab)usage in a Linux
-system visible to kernel and userspace developers. If enabled in the config
-but not used it has almost zero runtime overhead, and a relatively small
-data structure overhead. Even if collection is enabled runtime all the
-locking is per-CPU and lookup is hashed.
-
-timer_stats should be used by kernel and userspace developers to verify that
-their code does not make unduly use of timers. This helps to avoid unnecessary
-wakeups, which should be avoided to optimize power consumption.
-
-It can be enabled by CONFIG_TIMER_STATS in the "Kernel hacking" configuration
-section.
-
-timer_stats collects information about the timer events which are fired in a
-Linux system over a sample period:
-
-- the pid of the task(process) which initialized the timer
-- the name of the process which initialized the timer
-- the function where the timer was initialized
-- the callback function which is associated to the timer
-- the number of events (callbacks)
-
-timer_stats adds an entry to /proc: /proc/timer_stats
-
-This entry is used to control the statistics functionality and to read out the
-sampled information.
-
-The timer_stats functionality is inactive on bootup.
-
-To activate a sample period issue:
-# echo 1 >/proc/timer_stats
-
-To stop a sample period issue:
-# echo 0 >/proc/timer_stats
-
-The statistics can be retrieved by:
-# cat /proc/timer_stats
-
-While sampling is enabled, each readout from /proc/timer_stats will see
-newly updated statistics. Once sampling is disabled, the sampled information
-is kept until a new sample period is started. This allows multiple readouts.
-
-Sample output of /proc/timer_stats:
-
-Timerstats sample period: 3.888770 s
- 12, 0 swapper hrtimer_stop_sched_tick (hrtimer_sched_tick)
- 15, 1 swapper hcd_submit_urb (rh_timer_func)
- 4, 959 kedac schedule_timeout (process_timeout)
- 1, 0 swapper page_writeback_init (wb_timer_fn)
- 28, 0 swapper hrtimer_stop_sched_tick (hrtimer_sched_tick)
- 22, 2948 IRQ 4 tty_flip_buffer_push (delayed_work_timer_fn)
- 3, 3100 bash schedule_timeout (process_timeout)
- 1, 1 swapper queue_delayed_work_on (delayed_work_timer_fn)
- 1, 1 swapper queue_delayed_work_on (delayed_work_timer_fn)
- 1, 1 swapper neigh_table_init_no_netlink (neigh_periodic_timer)
- 1, 2292 ip __netdev_watchdog_up (dev_watchdog)
- 1, 23 events/1 do_cache_clean (delayed_work_timer_fn)
-90 total events, 30.0 events/sec
-
-The first column is the number of events, the second column the pid, the third
-column is the name of the process. The forth column shows the function which
-initialized the timer and in parenthesis the callback function which was
-executed on expiry.
-
- Thomas, Ingo
-
-Added flag to indicate 'deferrable timer' in /proc/timer_stats. A deferrable
-timer will appear as follows
- 10D, 1 swapper queue_delayed_work_on (delayed_work_timer_fn)
-
diff --git a/Documentation/x86/zero-page.txt b/Documentation/x86/zero-page.txt
index 95a4d34..b8527c6 100644
--- a/Documentation/x86/zero-page.txt
+++ b/Documentation/x86/zero-page.txt
@@ -31,6 +31,8 @@
1E9/001 ALL eddbuf_entries Number of entries in eddbuf (below)
1EA/001 ALL edd_mbr_sig_buf_entries Number of entries in edd_mbr_sig_buffer
(below)
+1EB/001 ALL kbd_status Numlock is enabled
+1EC/001 ALL secure_boot Secure boot is enabled in the firmware
1EF/001 ALL sentinel Used to detect broken bootloaders
290/040 ALL edd_mbr_sig_buffer EDD MBR signatures
2D0/A00 ALL e820_map E820 memory map table
diff --git a/MAINTAINERS b/MAINTAINERS
index 107c10e..baac725 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -643,7 +643,7 @@
F: drivers/gpio/gpio-altera.c
ALTERA SYSTEM RESOURCE DRIVER FOR ARRIA10 DEVKIT
-M: Thor Thayer <tthayer@opensource.altera.com>
+M: Thor Thayer <thor.thayer@linux.intel.com>
S: Maintained
F: drivers/gpio/gpio-altera-a10sr.c
F: drivers/mfd/altera-a10sr.c
@@ -877,8 +877,8 @@
F: drivers/hwmon/applesmc.c
APPLETALK NETWORK LAYER
-M: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
-S: Maintained
+L: netdev@vger.kernel.org
+S: Odd fixes
F: drivers/net/appletalk/
F: net/appletalk/
@@ -1788,7 +1788,7 @@
F: drivers/clk/socfpga/
ARM/SOCFPGA EDAC SUPPORT
-M: Thor Thayer <tthayer@opensource.altera.com>
+M: Thor Thayer <thor.thayer@linux.intel.com>
S: Maintained
F: drivers/edac/altera_edac.
@@ -2692,6 +2692,13 @@
F: include/linux/bcm963xx_nvram.h
F: include/linux/bcm963xx_tag.h
+BROADCOM BMIPS CPUFREQ DRIVER
+M: Markus Mayer <mmayer@broadcom.com>
+M: bcm-kernel-feedback-list@broadcom.com
+L: linux-pm@vger.kernel.org
+S: Maintained
+F: drivers/cpufreq/bmips-cpufreq.c
+
BROADCOM TG3 GIGABIT ETHERNET DRIVER
M: Siva Reddy Kallam <siva.kallam@broadcom.com>
M: Prashant Sreedharan <prashant@broadcom.com>
@@ -6727,9 +6734,8 @@
F: drivers/tty/ipwireless/
IPX NETWORK LAYER
-M: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
L: netdev@vger.kernel.org
-S: Maintained
+S: Odd fixes
F: include/net/ipx.h
F: include/uapi/linux/ipx.h
F: net/ipx/
@@ -7501,8 +7507,8 @@
F: drivers/misc/lkdtm*
LLC (802.2)
-M: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
-S: Maintained
+L: netdev@vger.kernel.org
+S: Odd fixes
F: include/linux/llc.h
F: include/uapi/linux/llc.h
F: include/net/llc*
@@ -7535,6 +7541,7 @@
F: Documentation/hwmon/lm90
F: Documentation/devicetree/bindings/hwmon/lm90.txt
F: drivers/hwmon/lm90.c
+F: include/dt-bindings/thermal/lm90.h
LM95234 HARDWARE MONITOR DRIVER
M: Guenter Roeck <linux@roeck-us.net>
@@ -13373,10 +13380,8 @@
F: drivers/input/misc/wistron_btns.c
WL3501 WIRELESS PCMCIA CARD DRIVER
-M: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
L: linux-wireless@vger.kernel.org
-W: http://oops.ghostprotocols.net:81/blog
-S: Maintained
+S: Odd fixes
F: drivers/net/wireless/wl3501*
WOLFSON MICROELECTRONICS DRIVERS
diff --git a/Makefile b/Makefile
index 503dae1..4e2abc3 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
VERSION = 4
PATCHLEVEL = 10
SUBLEVEL = 0
-EXTRAVERSION = -rc8
+EXTRAVERSION =
NAME = Fearless Coyote
# *DOCUMENTATION*
@@ -87,10 +87,12 @@
ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
quiet=silent_
+ tools_silent=s
endif
else # make-3.8x
ifneq ($(filter s% -s%,$(MAKEFLAGS)),)
quiet=silent_
+ tools_silent=-s
endif
endif
@@ -1607,11 +1609,11 @@
# Clear a bunch of variables before executing the submake
tools/: FORCE
$(Q)mkdir -p $(objtree)/tools
- $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(shell cd $(objtree) && /bin/pwd) subdir=tools -C $(src)/tools/
+ $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(tools_silent) $(filter --j% -j,$(MAKEFLAGS))" O=$(shell cd $(objtree) && /bin/pwd) subdir=tools -C $(src)/tools/
tools/%: FORCE
$(Q)mkdir -p $(objtree)/tools
- $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(shell cd $(objtree) && /bin/pwd) subdir=tools -C $(src)/tools/ $*
+ $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(tools_silent) $(filter --j% -j,$(MAKEFLAGS))" O=$(shell cd $(objtree) && /bin/pwd) subdir=tools -C $(src)/tools/ $*
# Single targets
# ---------------------------------------------------------------------------
diff --git a/arch/alpha/include/asm/Kbuild b/arch/alpha/include/asm/Kbuild
index bf8475c..baa152b 100644
--- a/arch/alpha/include/asm/Kbuild
+++ b/arch/alpha/include/asm/Kbuild
@@ -1,7 +1,6 @@
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += exec.h
generic-y += export.h
generic-y += irq_work.h
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index 54d8616..9d27a7d 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -1145,7 +1145,7 @@
SYSCALL_DEFINE2(osf_getrusage, int, who, struct rusage32 __user *, ru)
{
struct rusage32 r;
- cputime_t utime, stime;
+ u64 utime, stime;
unsigned long utime_jiffies, stime_jiffies;
if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN)
@@ -1155,16 +1155,16 @@
switch (who) {
case RUSAGE_SELF:
task_cputime(current, &utime, &stime);
- utime_jiffies = cputime_to_jiffies(utime);
- stime_jiffies = cputime_to_jiffies(stime);
+ utime_jiffies = nsecs_to_jiffies(utime);
+ stime_jiffies = nsecs_to_jiffies(stime);
jiffies_to_timeval32(utime_jiffies, &r.ru_utime);
jiffies_to_timeval32(stime_jiffies, &r.ru_stime);
r.ru_minflt = current->min_flt;
r.ru_majflt = current->maj_flt;
break;
case RUSAGE_CHILDREN:
- utime_jiffies = cputime_to_jiffies(current->signal->cutime);
- stime_jiffies = cputime_to_jiffies(current->signal->cstime);
+ utime_jiffies = nsecs_to_jiffies(current->signal->cutime);
+ stime_jiffies = nsecs_to_jiffies(current->signal->cstime);
jiffies_to_timeval32(utime_jiffies, &r.ru_utime);
jiffies_to_timeval32(stime_jiffies, &r.ru_stime);
r.ru_minflt = current->signal->cmin_flt;
diff --git a/arch/arc/include/asm/Kbuild b/arch/arc/include/asm/Kbuild
index c332604..63a0401 100644
--- a/arch/arc/include/asm/Kbuild
+++ b/arch/arc/include/asm/Kbuild
@@ -2,7 +2,6 @@
generic-y += bitsperlong.h
generic-y += bugs.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += device.h
generic-y += div64.h
generic-y += emergency-restart.h
diff --git a/arch/arm/configs/exynos_defconfig b/arch/arm/configs/exynos_defconfig
index 79c415c..809f0bf 100644
--- a/arch/arm/configs/exynos_defconfig
+++ b/arch/arm/configs/exynos_defconfig
@@ -24,7 +24,7 @@
CONFIG_ARM_ATAG_DTB_COMPAT=y
CONFIG_CMDLINE="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc mem=256M"
CONFIG_CPU_FREQ=y
-CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
CONFIG_CPU_FREQ_GOV_USERSPACE=m
diff --git a/arch/arm/configs/multi_v5_defconfig b/arch/arm/configs/multi_v5_defconfig
index 361686a..69a4bd1 100644
--- a/arch/arm/configs/multi_v5_defconfig
+++ b/arch/arm/configs/multi_v5_defconfig
@@ -58,7 +58,7 @@
CONFIG_ARM_APPENDED_DTB=y
CONFIG_ARM_ATAG_DTB_COMPAT=y
CONFIG_CPU_FREQ=y
-CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
CONFIG_CPU_IDLE=y
CONFIG_ARM_KIRKWOOD_CPUIDLE=y
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 028d2b7..2e7a63f 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -132,7 +132,7 @@
CONFIG_KEXEC=y
CONFIG_EFI=y
CONFIG_CPU_FREQ=y
-CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
CONFIG_CPU_FREQ_GOV_USERSPACE=m
@@ -824,6 +824,7 @@
CONFIG_QCOM_WCNSS_CTRL=m
CONFIG_ROCKCHIP_PM_DOMAINS=y
CONFIG_COMMON_CLK_QCOM=y
+CONFIG_QCOM_CLK_RPM=y
CONFIG_CHROME_PLATFORMS=y
CONFIG_STAGING_BOARD=y
CONFIG_CROS_EC_CHARDEV=m
diff --git a/arch/arm/configs/mvebu_v5_defconfig b/arch/arm/configs/mvebu_v5_defconfig
index f7f6039..4b598da 100644
--- a/arch/arm/configs/mvebu_v5_defconfig
+++ b/arch/arm/configs/mvebu_v5_defconfig
@@ -44,7 +44,7 @@
CONFIG_ARM_APPENDED_DTB=y
CONFIG_ARM_ATAG_DTB_COMPAT=y
CONFIG_CPU_FREQ=y
-CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
CONFIG_CPU_IDLE=y
CONFIG_ARM_KIRKWOOD_CPUIDLE=y
diff --git a/arch/arm/configs/pxa_defconfig b/arch/arm/configs/pxa_defconfig
index e4314b1..271dc7e 100644
--- a/arch/arm/configs/pxa_defconfig
+++ b/arch/arm/configs/pxa_defconfig
@@ -97,7 +97,7 @@
CONFIG_CMDLINE="root=/dev/ram0 ro"
CONFIG_KEXEC=y
CONFIG_CPU_FREQ=y
-CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
CONFIG_CPU_FREQ_GOV_USERSPACE=m
diff --git a/arch/arm/configs/shmobile_defconfig b/arch/arm/configs/shmobile_defconfig
index 1b0f8ae..adeaecd 100644
--- a/arch/arm/configs/shmobile_defconfig
+++ b/arch/arm/configs/shmobile_defconfig
@@ -38,7 +38,7 @@
CONFIG_ARM_APPENDED_DTB=y
CONFIG_KEXEC=y
CONFIG_CPU_FREQ=y
-CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
diff --git a/arch/arm/include/asm/Kbuild b/arch/arm/include/asm/Kbuild
index efb2175..b14e8c7 100644
--- a/arch/arm/include/asm/Kbuild
+++ b/arch/arm/include/asm/Kbuild
@@ -2,7 +2,6 @@
generic-y += bitsperlong.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += current.h
generic-y += early_ioremap.h
generic-y += emergency-restart.h
diff --git a/arch/arm/include/asm/efi.h b/arch/arm/include/asm/efi.h
index 0b06f53..e4e6a9d6 100644
--- a/arch/arm/include/asm/efi.h
+++ b/arch/arm/include/asm/efi.h
@@ -55,6 +55,7 @@
#define efi_call_early(f, ...) sys_table_arg->boottime->f(__VA_ARGS__)
#define __efi_call_early(f, ...) f(__VA_ARGS__)
+#define efi_call_runtime(f, ...) sys_table_arg->runtime->f(__VA_ARGS__)
#define efi_is_64bit() (false)
#define efi_call_proto(protocol, f, instance, ...) \
diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h
index 1f59ea05..b7e0125 100644
--- a/arch/arm/include/asm/uaccess.h
+++ b/arch/arm/include/asm/uaccess.h
@@ -478,11 +478,10 @@
arm_copy_from_user(void *to, const void __user *from, unsigned long n);
static inline unsigned long __must_check
-__copy_from_user(void *to, const void __user *from, unsigned long n)
+__arch_copy_from_user(void *to, const void __user *from, unsigned long n)
{
unsigned int __ua_flags;
- check_object_size(to, n, false);
__ua_flags = uaccess_save_and_enable();
n = arm_copy_from_user(to, from, n);
uaccess_restore(__ua_flags);
@@ -495,18 +494,15 @@
__copy_to_user_std(void __user *to, const void *from, unsigned long n);
static inline unsigned long __must_check
-__copy_to_user(void __user *to, const void *from, unsigned long n)
+__arch_copy_to_user(void __user *to, const void *from, unsigned long n)
{
#ifndef CONFIG_UACCESS_WITH_MEMCPY
unsigned int __ua_flags;
-
- check_object_size(from, n, true);
__ua_flags = uaccess_save_and_enable();
n = arm_copy_to_user(to, from, n);
uaccess_restore(__ua_flags);
return n;
#else
- check_object_size(from, n, true);
return arm_copy_to_user(to, from, n);
#endif
}
@@ -526,25 +522,49 @@
}
#else
-#define __copy_from_user(to, from, n) (memcpy(to, (void __force *)from, n), 0)
-#define __copy_to_user(to, from, n) (memcpy((void __force *)to, from, n), 0)
+#define __arch_copy_from_user(to, from, n) \
+ (memcpy(to, (void __force *)from, n), 0)
+#define __arch_copy_to_user(to, from, n) \
+ (memcpy((void __force *)to, from, n), 0)
#define __clear_user(addr, n) (memset((void __force *)addr, 0, n), 0)
#endif
-static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
+static inline unsigned long __must_check
+__copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+ check_object_size(to, n, false);
+ return __arch_copy_from_user(to, from, n);
+}
+
+static inline unsigned long __must_check
+copy_from_user(void *to, const void __user *from, unsigned long n)
{
unsigned long res = n;
+
+ check_object_size(to, n, false);
+
if (likely(access_ok(VERIFY_READ, from, n)))
- res = __copy_from_user(to, from, n);
+ res = __arch_copy_from_user(to, from, n);
if (unlikely(res))
memset(to + (n - res), 0, res);
return res;
}
-static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)
+static inline unsigned long __must_check
+__copy_to_user(void __user *to, const void *from, unsigned long n)
{
+ check_object_size(from, n, true);
+
+ return __arch_copy_to_user(to, from, n);
+}
+
+static inline unsigned long __must_check
+copy_to_user(void __user *to, const void *from, unsigned long n)
+{
+ check_object_size(from, n, true);
+
if (access_ok(VERIFY_WRITE, to, n))
- n = __copy_to_user(to, from, n);
+ n = __arch_copy_to_user(to, from, n);
return n;
}
diff --git a/arch/arm/lib/getuser.S b/arch/arm/lib/getuser.S
index 8ecfd15..df73914 100644
--- a/arch/arm/lib/getuser.S
+++ b/arch/arm/lib/getuser.S
@@ -67,7 +67,7 @@
ENDPROC(__get_user_4)
ENTRY(__get_user_8)
- check_uaccess r0, 8, r1, r2, __get_user_bad
+ check_uaccess r0, 8, r1, r2, __get_user_bad8
#ifdef CONFIG_THUMB2_KERNEL
5: TUSER(ldr) r2, [r0]
6: TUSER(ldr) r3, [r0, #4]
diff --git a/arch/arm/mach-ep93xx/edb93xx.c b/arch/arm/mach-ep93xx/edb93xx.c
index ad92d9f..0ac1763 100644
--- a/arch/arm/mach-ep93xx/edb93xx.c
+++ b/arch/arm/mach-ep93xx/edb93xx.c
@@ -27,7 +27,6 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
-#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/i2c-gpio.h>
#include <linux/spi/spi.h>
@@ -106,33 +105,10 @@
.gpio_nreset = -EINVAL, /* filled in later */
};
-static int edb93xx_cs4271_hw_setup(struct spi_device *spi)
-{
- return gpio_request_one(EP93XX_GPIO_LINE_EGPIO6,
- GPIOF_OUT_INIT_HIGH, spi->modalias);
-}
-
-static void edb93xx_cs4271_hw_cleanup(struct spi_device *spi)
-{
- gpio_free(EP93XX_GPIO_LINE_EGPIO6);
-}
-
-static void edb93xx_cs4271_hw_cs_control(struct spi_device *spi, int value)
-{
- gpio_set_value(EP93XX_GPIO_LINE_EGPIO6, value);
-}
-
-static struct ep93xx_spi_chip_ops edb93xx_cs4271_hw = {
- .setup = edb93xx_cs4271_hw_setup,
- .cleanup = edb93xx_cs4271_hw_cleanup,
- .cs_control = edb93xx_cs4271_hw_cs_control,
-};
-
static struct spi_board_info edb93xx_spi_board_info[] __initdata = {
{
.modalias = "cs4271",
.platform_data = &edb93xx_cs4271_data,
- .controller_data = &edb93xx_cs4271_hw,
.max_speed_hz = 6000000,
.bus_num = 0,
.chip_select = 0,
@@ -140,8 +116,13 @@
},
};
+static int edb93xx_spi_chipselects[] __initdata = {
+ EP93XX_GPIO_LINE_EGPIO6,
+};
+
static struct ep93xx_spi_info edb93xx_spi_info __initdata = {
- .num_chipselect = ARRAY_SIZE(edb93xx_spi_board_info),
+ .chipselect = edb93xx_spi_chipselects,
+ .num_chipselect = ARRAY_SIZE(edb93xx_spi_chipselects),
};
static void __init edb93xx_register_spi(void)
diff --git a/arch/arm/mach-ep93xx/simone.c b/arch/arm/mach-ep93xx/simone.c
index 7bb540c4..c7a40f2 100644
--- a/arch/arm/mach-ep93xx/simone.c
+++ b/arch/arm/mach-ep93xx/simone.c
@@ -49,56 +49,6 @@
#define MMC_CARD_DETECT_GPIO EP93XX_GPIO_LINE_EGPIO0
/*
- * Up to v1.3, the Sim.One used SFRMOUT as SD card chip select, but this goes
- * low between multi-message command blocks. From v1.4, it uses a GPIO instead.
- * v1.3 parts will still work, since the signal on SFRMOUT is automatic.
- */
-#define MMC_CHIP_SELECT_GPIO EP93XX_GPIO_LINE_EGPIO1
-
-/*
- * MMC SPI chip select GPIO handling. If you are using SFRMOUT (SFRM1) signal,
- * you can leave these empty and pass NULL as .controller_data.
- */
-
-static int simone_mmc_spi_setup(struct spi_device *spi)
-{
- unsigned int gpio = MMC_CHIP_SELECT_GPIO;
- int err;
-
- err = gpio_request(gpio, spi->modalias);
- if (err)
- return err;
-
- err = gpio_direction_output(gpio, 1);
- if (err) {
- gpio_free(gpio);
- return err;
- }
-
- return 0;
-}
-
-static void simone_mmc_spi_cleanup(struct spi_device *spi)
-{
- unsigned int gpio = MMC_CHIP_SELECT_GPIO;
-
- gpio_set_value(gpio, 1);
- gpio_direction_input(gpio);
- gpio_free(gpio);
-}
-
-static void simone_mmc_spi_cs_control(struct spi_device *spi, int value)
-{
- gpio_set_value(MMC_CHIP_SELECT_GPIO, value);
-}
-
-static struct ep93xx_spi_chip_ops simone_mmc_spi_ops = {
- .setup = simone_mmc_spi_setup,
- .cleanup = simone_mmc_spi_cleanup,
- .cs_control = simone_mmc_spi_cs_control,
-};
-
-/*
* MMC card detection GPIO setup.
*/
@@ -152,7 +102,6 @@
static struct spi_board_info simone_spi_devices[] __initdata = {
{
.modalias = "mmc_spi",
- .controller_data = &simone_mmc_spi_ops,
.platform_data = &simone_mmc_spi_data,
/*
* We use 10 MHz even though the maximum is 3.7 MHz. The driver
@@ -165,8 +114,18 @@
},
};
+/*
+ * Up to v1.3, the Sim.One used SFRMOUT as SD card chip select, but this goes
+ * low between multi-message command blocks. From v1.4, it uses a GPIO instead.
+ * v1.3 parts will still work, since the signal on SFRMOUT is automatic.
+ */
+static int simone_spi_chipselects[] __initdata = {
+ EP93XX_GPIO_LINE_EGPIO1,
+};
+
static struct ep93xx_spi_info simone_spi_info __initdata = {
- .num_chipselect = ARRAY_SIZE(simone_spi_devices),
+ .chipselect = simone_spi_chipselects,
+ .num_chipselect = ARRAY_SIZE(simone_spi_chipselects),
.use_dma = 1,
};
diff --git a/arch/arm/mach-ep93xx/vision_ep9307.c b/arch/arm/mach-ep93xx/vision_ep9307.c
index 5cced59..1daf944 100644
--- a/arch/arm/mach-ep93xx/vision_ep9307.c
+++ b/arch/arm/mach-ep93xx/vision_ep9307.c
@@ -175,33 +175,9 @@
.gpio_nreset = EP93XX_GPIO_LINE_H(2),
};
-static int vision_cs4271_hw_setup(struct spi_device *spi)
-{
- return gpio_request_one(EP93XX_GPIO_LINE_EGPIO6,
- GPIOF_OUT_INIT_HIGH, spi->modalias);
-}
-
-static void vision_cs4271_hw_cleanup(struct spi_device *spi)
-{
- gpio_free(EP93XX_GPIO_LINE_EGPIO6);
-}
-
-static void vision_cs4271_hw_cs_control(struct spi_device *spi, int value)
-{
- gpio_set_value(EP93XX_GPIO_LINE_EGPIO6, value);
-}
-
-static struct ep93xx_spi_chip_ops vision_cs4271_hw = {
- .setup = vision_cs4271_hw_setup,
- .cleanup = vision_cs4271_hw_cleanup,
- .cs_control = vision_cs4271_hw_cs_control,
-};
-
/*************************************************************************
* SPI Flash
*************************************************************************/
-#define VISION_SPI_FLASH_CS EP93XX_GPIO_LINE_EGPIO7
-
static struct mtd_partition vision_spi_flash_partitions[] = {
{
.name = "SPI bootstrap",
@@ -224,68 +200,20 @@
.nr_parts = ARRAY_SIZE(vision_spi_flash_partitions),
};
-static int vision_spi_flash_hw_setup(struct spi_device *spi)
-{
- return gpio_request_one(VISION_SPI_FLASH_CS, GPIOF_INIT_HIGH,
- spi->modalias);
-}
-
-static void vision_spi_flash_hw_cleanup(struct spi_device *spi)
-{
- gpio_free(VISION_SPI_FLASH_CS);
-}
-
-static void vision_spi_flash_hw_cs_control(struct spi_device *spi, int value)
-{
- gpio_set_value(VISION_SPI_FLASH_CS, value);
-}
-
-static struct ep93xx_spi_chip_ops vision_spi_flash_hw = {
- .setup = vision_spi_flash_hw_setup,
- .cleanup = vision_spi_flash_hw_cleanup,
- .cs_control = vision_spi_flash_hw_cs_control,
-};
-
/*************************************************************************
* SPI SD/MMC host
*************************************************************************/
-#define VISION_SPI_MMC_CS EP93XX_GPIO_LINE_G(2)
-#define VISION_SPI_MMC_WP EP93XX_GPIO_LINE_F(0)
-#define VISION_SPI_MMC_CD EP93XX_GPIO_LINE_EGPIO15
-
static struct mmc_spi_platform_data vision_spi_mmc_data = {
.detect_delay = 100,
.powerup_msecs = 100,
.ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
.flags = MMC_SPI_USE_CD_GPIO | MMC_SPI_USE_RO_GPIO,
- .cd_gpio = VISION_SPI_MMC_CD,
+ .cd_gpio = EP93XX_GPIO_LINE_EGPIO15,
.cd_debounce = 1,
- .ro_gpio = VISION_SPI_MMC_WP,
+ .ro_gpio = EP93XX_GPIO_LINE_F(0),
.caps2 = MMC_CAP2_RO_ACTIVE_HIGH,
};
-static int vision_spi_mmc_hw_setup(struct spi_device *spi)
-{
- return gpio_request_one(VISION_SPI_MMC_CS, GPIOF_INIT_HIGH,
- spi->modalias);
-}
-
-static void vision_spi_mmc_hw_cleanup(struct spi_device *spi)
-{
- gpio_free(VISION_SPI_MMC_CS);
-}
-
-static void vision_spi_mmc_hw_cs_control(struct spi_device *spi, int value)
-{
- gpio_set_value(VISION_SPI_MMC_CS, value);
-}
-
-static struct ep93xx_spi_chip_ops vision_spi_mmc_hw = {
- .setup = vision_spi_mmc_hw_setup,
- .cleanup = vision_spi_mmc_hw_cleanup,
- .cs_control = vision_spi_mmc_hw_cs_control,
-};
-
/*************************************************************************
* SPI Bus
*************************************************************************/
@@ -293,7 +221,6 @@
{
.modalias = "cs4271",
.platform_data = &vision_cs4271_data,
- .controller_data = &vision_cs4271_hw,
.max_speed_hz = 6000000,
.bus_num = 0,
.chip_select = 0,
@@ -301,7 +228,6 @@
}, {
.modalias = "sst25l",
.platform_data = &vision_spi_flash_data,
- .controller_data = &vision_spi_flash_hw,
.max_speed_hz = 20000000,
.bus_num = 0,
.chip_select = 1,
@@ -309,7 +235,6 @@
}, {
.modalias = "mmc_spi",
.platform_data = &vision_spi_mmc_data,
- .controller_data = &vision_spi_mmc_hw,
.max_speed_hz = 20000000,
.bus_num = 0,
.chip_select = 2,
@@ -317,8 +242,15 @@
},
};
+static int vision_spi_chipselects[] __initdata = {
+ EP93XX_GPIO_LINE_EGPIO6,
+ EP93XX_GPIO_LINE_EGPIO7,
+ EP93XX_GPIO_LINE_G(2),
+};
+
static struct ep93xx_spi_info vision_spi_master __initdata = {
- .num_chipselect = ARRAY_SIZE(vision_spi_board_info),
+ .chipselect = vision_spi_chipselects,
+ .num_chipselect = ARRAY_SIZE(vision_spi_chipselects),
.use_dma = 1,
};
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c
index 76b0454..0598630 100644
--- a/arch/arm/mach-omap2/pm.c
+++ b/arch/arm/mach-omap2/pm.c
@@ -130,17 +130,16 @@
freq = clk_get_rate(clk);
clk_put(clk);
- rcu_read_lock();
opp = dev_pm_opp_find_freq_ceil(dev, &freq);
if (IS_ERR(opp)) {
- rcu_read_unlock();
pr_err("%s: unable to find boot up OPP for vdd_%s\n",
__func__, vdd_name);
goto exit;
}
bootup_volt = dev_pm_opp_get_voltage(opp);
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
+
if (!bootup_volt) {
pr_err("%s: unable to find voltage corresponding to the bootup OPP for vdd_%s\n",
__func__, vdd_name);
diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig
index 2bb4b09..ad7d604 100644
--- a/arch/arm/mach-shmobile/Kconfig
+++ b/arch/arm/mach-shmobile/Kconfig
@@ -57,6 +57,7 @@
select PM
select PM_GENERIC_DOMAINS
select SYS_SUPPORTS_SH_MTU2
+ select RENESAS_OSTM
config ARCH_R8A73A4
bool "R-Mobile APE6 (R8A73A40)"
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index ab77100..82d3e79 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1171,6 +1171,25 @@
#ifdef CONFIG_ARM_DMA_USE_IOMMU
+static int __dma_info_to_prot(enum dma_data_direction dir, unsigned long attrs)
+{
+ int prot = 0;
+
+ if (attrs & DMA_ATTR_PRIVILEGED)
+ prot |= IOMMU_PRIV;
+
+ switch (dir) {
+ case DMA_BIDIRECTIONAL:
+ return prot | IOMMU_READ | IOMMU_WRITE;
+ case DMA_TO_DEVICE:
+ return prot | IOMMU_READ;
+ case DMA_FROM_DEVICE:
+ return prot | IOMMU_WRITE;
+ default:
+ return prot;
+ }
+}
+
/* IOMMU */
static int extend_iommu_mapping(struct dma_iommu_mapping *mapping);
@@ -1394,7 +1413,8 @@
* Create a mapping in device IO address space for specified pages
*/
static dma_addr_t
-__iommu_create_mapping(struct device *dev, struct page **pages, size_t size)
+__iommu_create_mapping(struct device *dev, struct page **pages, size_t size,
+ unsigned long attrs)
{
struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
@@ -1419,7 +1439,7 @@
len = (j - i) << PAGE_SHIFT;
ret = iommu_map(mapping->domain, iova, phys, len,
- IOMMU_READ|IOMMU_WRITE);
+ __dma_info_to_prot(DMA_BIDIRECTIONAL, attrs));
if (ret < 0)
goto fail;
iova += len;
@@ -1476,7 +1496,8 @@
}
static void *__iommu_alloc_simple(struct device *dev, size_t size, gfp_t gfp,
- dma_addr_t *handle, int coherent_flag)
+ dma_addr_t *handle, int coherent_flag,
+ unsigned long attrs)
{
struct page *page;
void *addr;
@@ -1488,7 +1509,7 @@
if (!addr)
return NULL;
- *handle = __iommu_create_mapping(dev, &page, size);
+ *handle = __iommu_create_mapping(dev, &page, size, attrs);
if (*handle == DMA_ERROR_CODE)
goto err_mapping;
@@ -1522,7 +1543,7 @@
if (coherent_flag == COHERENT || !gfpflags_allow_blocking(gfp))
return __iommu_alloc_simple(dev, size, gfp, handle,
- coherent_flag);
+ coherent_flag, attrs);
/*
* Following is a work-around (a.k.a. hack) to prevent pages
@@ -1537,7 +1558,7 @@
if (!pages)
return NULL;
- *handle = __iommu_create_mapping(dev, pages, size);
+ *handle = __iommu_create_mapping(dev, pages, size, attrs);
if (*handle == DMA_ERROR_CODE)
goto err_buffer;
@@ -1672,27 +1693,6 @@
GFP_KERNEL);
}
-static int __dma_direction_to_prot(enum dma_data_direction dir)
-{
- int prot;
-
- switch (dir) {
- case DMA_BIDIRECTIONAL:
- prot = IOMMU_READ | IOMMU_WRITE;
- break;
- case DMA_TO_DEVICE:
- prot = IOMMU_READ;
- break;
- case DMA_FROM_DEVICE:
- prot = IOMMU_WRITE;
- break;
- default:
- prot = 0;
- }
-
- return prot;
-}
-
/*
* Map a part of the scatter-gather list into contiguous io address space
*/
@@ -1722,7 +1722,7 @@
if (!is_coherent && (attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
__dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir);
- prot = __dma_direction_to_prot(dir);
+ prot = __dma_info_to_prot(dir, attrs);
ret = iommu_map(mapping->domain, iova, phys, len, prot);
if (ret < 0)
@@ -1930,7 +1930,7 @@
if (dma_addr == DMA_ERROR_CODE)
return dma_addr;
- prot = __dma_direction_to_prot(dir);
+ prot = __dma_info_to_prot(dir, attrs);
ret = iommu_map(mapping->domain, dma_addr, page_to_phys(page), len, prot);
if (ret < 0)
@@ -2036,7 +2036,7 @@
if (dma_addr == DMA_ERROR_CODE)
return dma_addr;
- prot = __dma_direction_to_prot(dir) | IOMMU_MMIO;
+ prot = __dma_info_to_prot(dir, attrs) | IOMMU_MMIO;
ret = iommu_map(mapping->domain, dma_addr, addr, len, prot);
if (ret < 0)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 1117421..f7dfd6d5 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -96,7 +96,7 @@
select HAVE_RCU_TABLE_FREE
select HAVE_SYSCALL_TRACEPOINTS
select HAVE_KPROBES
- select HAVE_KRETPROBES if HAVE_KPROBES
+ select HAVE_KRETPROBES
select IOMMU_DMA if IOMMU_SUPPORT
select IRQ_DOMAIN
select IRQ_FORCED_THREADING
diff --git a/arch/arm64/include/asm/Kbuild b/arch/arm64/include/asm/Kbuild
index 8365a84..a12f1af 100644
--- a/arch/arm64/include/asm/Kbuild
+++ b/arch/arm64/include/asm/Kbuild
@@ -1,6 +1,5 @@
generic-y += bugs.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += delay.h
generic-y += div64.h
generic-y += dma.h
diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h
index eaa5bbe..b4b3400 100644
--- a/arch/arm64/include/asm/arch_timer.h
+++ b/arch/arm64/include/asm/arch_timer.h
@@ -29,41 +29,29 @@
#include <clocksource/arm_arch_timer.h>
-#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585)
+#if IS_ENABLED(CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND)
extern struct static_key_false arch_timer_read_ool_enabled;
-#define needs_fsl_a008585_workaround() \
+#define needs_unstable_timer_counter_workaround() \
static_branch_unlikely(&arch_timer_read_ool_enabled)
#else
-#define needs_fsl_a008585_workaround() false
+#define needs_unstable_timer_counter_workaround() false
#endif
-u32 __fsl_a008585_read_cntp_tval_el0(void);
-u32 __fsl_a008585_read_cntv_tval_el0(void);
-u64 __fsl_a008585_read_cntvct_el0(void);
-/*
- * The number of retries is an arbitrary value well beyond the highest number
- * of iterations the loop has been observed to take.
- */
-#define __fsl_a008585_read_reg(reg) ({ \
- u64 _old, _new; \
- int _retries = 200; \
- \
- do { \
- _old = read_sysreg(reg); \
- _new = read_sysreg(reg); \
- _retries--; \
- } while (unlikely(_old != _new) && _retries); \
- \
- WARN_ON_ONCE(!_retries); \
- _new; \
-})
+struct arch_timer_erratum_workaround {
+ const char *id; /* Indicate the Erratum ID */
+ u32 (*read_cntp_tval_el0)(void);
+ u32 (*read_cntv_tval_el0)(void);
+ u64 (*read_cntvct_el0)(void);
+};
+
+extern const struct arch_timer_erratum_workaround *timer_unstable_counter_workaround;
#define arch_timer_reg_read_stable(reg) \
({ \
u64 _val; \
- if (needs_fsl_a008585_workaround()) \
- _val = __fsl_a008585_read_##reg(); \
+ if (needs_unstable_timer_counter_workaround()) \
+ _val = timer_unstable_counter_workaround->read_##reg();\
else \
_val = read_sysreg(reg); \
_val; \
diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h
index 0b6b163..e744528 100644
--- a/arch/arm64/include/asm/efi.h
+++ b/arch/arm64/include/asm/efi.h
@@ -50,6 +50,7 @@
#define efi_call_early(f, ...) sys_table_arg->boottime->f(__VA_ARGS__)
#define __efi_call_early(f, ...) f(__VA_ARGS__)
+#define efi_call_runtime(f, ...) sys_table_arg->runtime->f(__VA_ARGS__)
#define efi_is_64bit() (true)
#define efi_call_proto(protocol, f, instance, ...) \
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index e040827..4a14b25 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -558,7 +558,7 @@
unsigned long attrs)
{
bool coherent = is_device_dma_coherent(dev);
- int ioprot = dma_direction_to_prot(DMA_BIDIRECTIONAL, coherent);
+ int ioprot = dma_info_to_prot(DMA_BIDIRECTIONAL, coherent, attrs);
size_t iosize = size;
void *addr;
@@ -712,7 +712,7 @@
unsigned long attrs)
{
bool coherent = is_device_dma_coherent(dev);
- int prot = dma_direction_to_prot(dir, coherent);
+ int prot = dma_info_to_prot(dir, coherent, attrs);
dma_addr_t dev_addr = iommu_dma_map_page(dev, page, offset, size, prot);
if (!iommu_dma_mapping_error(dev, dev_addr) &&
@@ -770,7 +770,7 @@
__iommu_sync_sg_for_device(dev, sgl, nelems, dir);
return iommu_dma_map_sg(dev, sgl, nelems,
- dma_direction_to_prot(dir, coherent));
+ dma_info_to_prot(dir, coherent, attrs));
}
static void __iommu_unmap_sg_attrs(struct device *dev,
@@ -799,7 +799,6 @@
.sync_sg_for_device = __iommu_sync_sg_for_device,
.map_resource = iommu_dma_map_resource,
.unmap_resource = iommu_dma_unmap_resource,
- .dma_supported = iommu_dma_supported,
.mapping_error = iommu_dma_mapping_error,
};
diff --git a/arch/avr32/include/asm/Kbuild b/arch/avr32/include/asm/Kbuild
index 241b9b9..3d7ef2c 100644
--- a/arch/avr32/include/asm/Kbuild
+++ b/arch/avr32/include/asm/Kbuild
@@ -1,6 +1,5 @@
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += delay.h
generic-y += device.h
generic-y += div64.h
diff --git a/arch/blackfin/include/asm/Kbuild b/arch/blackfin/include/asm/Kbuild
index 2fb67b5..d6fa60b 100644
--- a/arch/blackfin/include/asm/Kbuild
+++ b/arch/blackfin/include/asm/Kbuild
@@ -2,7 +2,6 @@
generic-y += auxvec.h
generic-y += bitsperlong.h
generic-y += bugs.h
-generic-y += cputime.h
generic-y += current.h
generic-y += device.h
generic-y += div64.h
diff --git a/arch/c6x/include/asm/Kbuild b/arch/c6x/include/asm/Kbuild
index 64465e7..4e9f574 100644
--- a/arch/c6x/include/asm/Kbuild
+++ b/arch/c6x/include/asm/Kbuild
@@ -5,7 +5,6 @@
generic-y += bitsperlong.h
generic-y += bugs.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += current.h
generic-y += device.h
generic-y += div64.h
diff --git a/arch/cris/include/asm/Kbuild b/arch/cris/include/asm/Kbuild
index 1778805..9f19e19 100644
--- a/arch/cris/include/asm/Kbuild
+++ b/arch/cris/include/asm/Kbuild
@@ -4,7 +4,6 @@
generic-y += bitsperlong.h
generic-y += clkdev.h
generic-y += cmpxchg.h
-generic-y += cputime.h
generic-y += device.h
generic-y += div64.h
generic-y += errno.h
diff --git a/arch/frv/include/asm/Kbuild b/arch/frv/include/asm/Kbuild
index 1fa084c..0f5b0d5 100644
--- a/arch/frv/include/asm/Kbuild
+++ b/arch/frv/include/asm/Kbuild
@@ -1,6 +1,5 @@
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += exec.h
generic-y += irq_work.h
generic-y += mcs_spinlock.h
diff --git a/arch/h8300/include/asm/Kbuild b/arch/h8300/include/asm/Kbuild
index 373cb23..5efd0c8 100644
--- a/arch/h8300/include/asm/Kbuild
+++ b/arch/h8300/include/asm/Kbuild
@@ -5,7 +5,6 @@
generic-y += cacheflush.h
generic-y += checksum.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += current.h
generic-y += delay.h
generic-y += device.h
diff --git a/arch/hexagon/include/asm/Kbuild b/arch/hexagon/include/asm/Kbuild
index db8ddab..a43a7c9 100644
--- a/arch/hexagon/include/asm/Kbuild
+++ b/arch/hexagon/include/asm/Kbuild
@@ -6,7 +6,6 @@
generic-y += bug.h
generic-y += bugs.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += current.h
generic-y += device.h
generic-y += div64.h
diff --git a/arch/ia64/include/asm/cputime.h b/arch/ia64/include/asm/cputime.h
index e2d3f5b..3d665c0 100644
--- a/arch/ia64/include/asm/cputime.h
+++ b/arch/ia64/include/asm/cputime.h
@@ -18,11 +18,7 @@
#ifndef __IA64_CPUTIME_H
#define __IA64_CPUTIME_H
-#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
-# include <asm-generic/cputime.h>
-#else
-# include <asm/processor.h>
-# include <asm-generic/cputime_nsecs.h>
+#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
extern void arch_vtime_task_switch(struct task_struct *tsk);
#endif /* CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
diff --git a/arch/ia64/include/asm/thread_info.h b/arch/ia64/include/asm/thread_info.h
index c702642..8742d74 100644
--- a/arch/ia64/include/asm/thread_info.h
+++ b/arch/ia64/include/asm/thread_info.h
@@ -27,6 +27,12 @@
mm_segment_t addr_limit; /* user-level address space limit */
int preempt_count; /* 0=premptable, <0=BUG; will also serve as bh-counter */
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
+ __u64 utime;
+ __u64 stime;
+ __u64 gtime;
+ __u64 hardirq_time;
+ __u64 softirq_time;
+ __u64 idle_time;
__u64 ac_stamp;
__u64 ac_leave;
__u64 ac_stime;
diff --git a/arch/ia64/kernel/head.S b/arch/ia64/kernel/head.S
index c9b5e94..3204fdd 100644
--- a/arch/ia64/kernel/head.S
+++ b/arch/ia64/kernel/head.S
@@ -1031,7 +1031,7 @@
END(ia64_native_sched_clock)
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
-GLOBAL_ENTRY(cycle_to_cputime)
+GLOBAL_ENTRY(cycle_to_nsec)
alloc r16=ar.pfs,1,0,0,0
addl r8=THIS_CPU(ia64_cpu_info) + IA64_CPUINFO_NSEC_PER_CYC_OFFSET,r0
;;
@@ -1047,7 +1047,7 @@
;;
shrp r8=r9,r8,IA64_NSEC_PER_CYC_SHIFT
br.ret.sptk.many rp
-END(cycle_to_cputime)
+END(cycle_to_nsec)
#endif /* CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
#ifdef CONFIG_IA64_BRL_EMU
diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c
index 7ec7acc..c483ece 100644
--- a/arch/ia64/kernel/setup.c
+++ b/arch/ia64/kernel/setup.c
@@ -619,6 +619,8 @@
check_sal_cache_flush();
#endif
paging_init();
+
+ clear_sched_clock_stable();
}
/*
diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
index 71775b95..faa1168 100644
--- a/arch/ia64/kernel/time.c
+++ b/arch/ia64/kernel/time.c
@@ -21,6 +21,7 @@
#include <linux/timex.h>
#include <linux/timekeeper_internal.h>
#include <linux/platform_device.h>
+#include <linux/cputime.h>
#include <asm/machvec.h>
#include <asm/delay.h>
@@ -59,18 +60,43 @@
#include <linux/kernel_stat.h>
-extern cputime_t cycle_to_cputime(u64 cyc);
+extern u64 cycle_to_nsec(u64 cyc);
-void vtime_account_user(struct task_struct *tsk)
+void vtime_flush(struct task_struct *tsk)
{
- cputime_t delta_utime;
struct thread_info *ti = task_thread_info(tsk);
+ u64 delta;
- if (ti->ac_utime) {
- delta_utime = cycle_to_cputime(ti->ac_utime);
- account_user_time(tsk, delta_utime);
- ti->ac_utime = 0;
+ if (ti->utime)
+ account_user_time(tsk, cycle_to_nsec(ti->utime));
+
+ if (ti->gtime)
+ account_guest_time(tsk, cycle_to_nsec(ti->gtime));
+
+ if (ti->idle_time)
+ account_idle_time(cycle_to_nsec(ti->idle_time));
+
+ if (ti->stime) {
+ delta = cycle_to_nsec(ti->stime);
+ account_system_index_time(tsk, delta, CPUTIME_SYSTEM);
}
+
+ if (ti->hardirq_time) {
+ delta = cycle_to_nsec(ti->hardirq_time);
+ account_system_index_time(tsk, delta, CPUTIME_IRQ);
+ }
+
+ if (ti->softirq_time) {
+ delta = cycle_to_nsec(ti->softirq_time));
+ account_system_index_time(tsk, delta, CPUTIME_SOFTIRQ);
+ }
+
+ ti->utime = 0;
+ ti->gtime = 0;
+ ti->idle_time = 0;
+ ti->stime = 0;
+ ti->hardirq_time = 0;
+ ti->softirq_time = 0;
}
/*
@@ -83,7 +109,7 @@
struct thread_info *pi = task_thread_info(prev);
struct thread_info *ni = task_thread_info(current);
- pi->ac_stamp = ni->ac_stamp;
+ ni->ac_stamp = pi->ac_stamp;
ni->ac_stime = ni->ac_utime = 0;
}
@@ -91,18 +117,15 @@
* Account time for a transition between system, hard irq or soft irq state.
* Note that this function is called with interrupts enabled.
*/
-static cputime_t vtime_delta(struct task_struct *tsk)
+static __u64 vtime_delta(struct task_struct *tsk)
{
struct thread_info *ti = task_thread_info(tsk);
- cputime_t delta_stime;
- __u64 now;
+ __u64 now, delta_stime;
WARN_ON_ONCE(!irqs_disabled());
now = ia64_get_itc();
-
- delta_stime = cycle_to_cputime(ti->ac_stime + (now - ti->ac_stamp));
- ti->ac_stime = 0;
+ delta_stime = now - ti->ac_stamp;
ti->ac_stamp = now;
return delta_stime;
@@ -110,15 +133,25 @@
void vtime_account_system(struct task_struct *tsk)
{
- cputime_t delta = vtime_delta(tsk);
+ struct thread_info *ti = task_thread_info(tsk);
+ __u64 stime = vtime_delta(tsk);
- account_system_time(tsk, 0, delta);
+ if ((tsk->flags & PF_VCPU) && !irq_count())
+ ti->gtime += stime;
+ else if (hardirq_count())
+ ti->hardirq_time += stime;
+ else if (in_serving_softirq())
+ ti->softirq_time += stime;
+ else
+ ti->stime += stime;
}
EXPORT_SYMBOL_GPL(vtime_account_system);
void vtime_account_idle(struct task_struct *tsk)
{
- account_idle_time(vtime_delta(tsk));
+ struct thread_info *ti = task_thread_info(tsk);
+
+ ti->idle_time += vtime_delta(tsk);
}
#endif /* CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
diff --git a/arch/m32r/include/asm/Kbuild b/arch/m32r/include/asm/Kbuild
index 860e440..652100b 100644
--- a/arch/m32r/include/asm/Kbuild
+++ b/arch/m32r/include/asm/Kbuild
@@ -1,6 +1,5 @@
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += exec.h
generic-y += irq_work.h
generic-y += kvm_para.h
diff --git a/arch/m68k/68000/m68328.c b/arch/m68k/68000/m68328.c
index e53caf4..419751b 100644
--- a/arch/m68k/68000/m68328.c
+++ b/arch/m68k/68000/m68328.c
@@ -45,9 +45,9 @@
void __init config_BSP(char *command, int len)
{
- printk(KERN_INFO "\n68328 support D. Jeff Dionne <jeff@uclinux.org>\n");
- printk(KERN_INFO "68328 support Kenneth Albanowski <kjahds@kjshds.com>\n");
- printk(KERN_INFO "68328/Pilot support Bernhard Kuhn <kuhn@lpr.e-technik.tu-muenchen.de>\n");
+ pr_info("68328 support D. Jeff Dionne <jeff@uclinux.org>\n");
+ pr_info("68328 support Kenneth Albanowski <kjahds@kjshds.com>\n");
+ pr_info("68328/Pilot support Bernhard Kuhn <kuhn@lpr.e-technik.tu-muenchen.de>\n");
mach_hwclk = m68328_hwclk;
mach_reset = m68328_reset;
diff --git a/arch/m68k/68000/m68EZ328.c b/arch/m68k/68000/m68EZ328.c
index e6ab321f..6a309a3 100644
--- a/arch/m68k/68000/m68EZ328.c
+++ b/arch/m68k/68000/m68EZ328.c
@@ -57,12 +57,12 @@
{
unsigned char *p;
- printk(KERN_INFO "\n68EZ328 DragonBallEZ support (C) 1999 Rt-Control, Inc\n");
+ pr_info("68EZ328 DragonBallEZ support (C) 1999 Rt-Control, Inc\n");
#ifdef CONFIG_UCSIMM
- printk(KERN_INFO "uCsimm serial string [%s]\n",getserialnum());
+ pr_info("uCsimm serial string [%s]\n", getserialnum());
p = cs8900a_hwaddr = gethwaddr(0);
- printk(KERN_INFO "uCsimm hwaddr %pM\n", p);
+ pr_info("uCsimm hwaddr %pM\n", p);
p = getbenv("APPEND");
if (p) strcpy(p,command);
diff --git a/arch/m68k/68000/m68VZ328.c b/arch/m68k/68000/m68VZ328.c
index 1154bdb..81b5491 100644
--- a/arch/m68k/68000/m68VZ328.c
+++ b/arch/m68k/68000/m68VZ328.c
@@ -150,9 +150,9 @@
{
char *p;
- printk(KERN_INFO "uCdimm serial string [%s]\n", getserialnum());
+ pr_info("uCdimm serial string [%s]\n", getserialnum());
p = cs8900a_hwaddr = gethwaddr(0);
- printk(KERN_INFO "uCdimm hwaddr %pM\n", p);
+ pr_info("uCdimm hwaddr %pM\n", p);
p = getbenv("APPEND");
if (p)
strcpy(p, command);
@@ -177,7 +177,7 @@
void __init config_BSP(char *command, int size)
{
- printk(KERN_INFO "68VZ328 DragonBallVZ support (c) 2001 Lineo, Inc.\n");
+ pr_info("68VZ328 DragonBallVZ support (c) 2001 Lineo, Inc.\n");
init_hardware(command, size);
diff --git a/arch/m68k/atari/atakeyb.c b/arch/m68k/atari/atakeyb.c
index 264db11..3709189 100644
--- a/arch/m68k/atari/atakeyb.c
+++ b/arch/m68k/atari/atakeyb.c
@@ -149,7 +149,7 @@
if (acia_stat & ACIA_OVRN) {
/* a very fast typist or a slow system, give a warning */
/* ...happens often if interrupts were disabled for too long */
- printk(KERN_DEBUG "Keyboard overrun\n");
+ pr_debug("Keyboard overrun\n");
scancode = acia.key_data;
if (ikbd_self_test)
/* During self test, don't do resyncing, just process the code */
@@ -228,14 +228,14 @@
keytyp = KTYP(keyval) - 0xf0;
keyval = KVAL(keyval);
- printk(KERN_WARNING "Key with scancode %d ", scancode);
+ pr_warn("Key with scancode %d ", scancode);
if (keytyp == KT_LATIN || keytyp == KT_LETTER) {
if (keyval < ' ')
- printk("('^%c') ", keyval + '@');
+ pr_cont("('^%c') ", keyval + '@');
else
- printk("('%c') ", keyval);
+ pr_cont("('%c') ", keyval);
}
- printk("is broken -- will be ignored.\n");
+ pr_cont("is broken -- will be ignored.\n");
break;
} else if (test_bit(scancode, broken_keys))
break;
@@ -299,7 +299,7 @@
#endif
if (acia_stat & (ACIA_FE | ACIA_PE)) {
- printk("Error in keyboard communication\n");
+ pr_err("Error in keyboard communication\n");
}
/* handle_scancode() can take a lot of time, so check again if
@@ -553,7 +553,7 @@
barrier();
/* if not incremented: no 0xf1 received */
if (ikbd_self_test == 1)
- printk(KERN_ERR "WARNING: keyboard self test failed!\n");
+ pr_err("Keyboard self test failed!\n");
ikbd_self_test = 0;
ikbd_mouse_disable();
diff --git a/arch/m68k/atari/config.c b/arch/m68k/atari/config.c
index e328eaf..565c6f0 100644
--- a/arch/m68k/atari/config.c
+++ b/arch/m68k/atari/config.c
@@ -234,44 +234,44 @@
* Determine hardware present
*/
- printk("Atari hardware found: ");
+ pr_info("Atari hardware found:");
if (MACH_IS_MEDUSA) {
/* There's no Atari video hardware on the Medusa, but all the
* addresses below generate a DTACK so no bus error occurs! */
} else if (hwreg_present(f030_xreg)) {
ATARIHW_SET(VIDEL_SHIFTER);
- printk("VIDEL ");
+ pr_cont(" VIDEL");
/* This is a temporary hack: If there is Falcon video
* hardware, we assume that the ST-DMA serves SCSI instead of
* ACSI. In the future, there should be a better method for
* this...
*/
ATARIHW_SET(ST_SCSI);
- printk("STDMA-SCSI ");
+ pr_cont(" STDMA-SCSI");
} else if (hwreg_present(tt_palette)) {
ATARIHW_SET(TT_SHIFTER);
- printk("TT_SHIFTER ");
+ pr_cont(" TT_SHIFTER");
} else if (hwreg_present(&shifter.bas_hi)) {
if (hwreg_present(&shifter.bas_lo) &&
(shifter.bas_lo = 0x0aau, shifter.bas_lo == 0x0aau)) {
ATARIHW_SET(EXTD_SHIFTER);
- printk("EXTD_SHIFTER ");
+ pr_cont(" EXTD_SHIFTER");
} else {
ATARIHW_SET(STND_SHIFTER);
- printk("STND_SHIFTER ");
+ pr_cont(" STND_SHIFTER");
}
}
if (hwreg_present(&st_mfp.par_dt_reg)) {
ATARIHW_SET(ST_MFP);
- printk("ST_MFP ");
+ pr_cont(" ST_MFP");
}
if (hwreg_present(&tt_mfp.par_dt_reg)) {
ATARIHW_SET(TT_MFP);
- printk("TT_MFP ");
+ pr_cont(" TT_MFP");
}
if (hwreg_present(&tt_scsi_dma.dma_addr_hi)) {
ATARIHW_SET(SCSI_DMA);
- printk("TT_SCSI_DMA ");
+ pr_cont(" TT_SCSI_DMA");
}
/*
* The ST-DMA address registers aren't readable
@@ -284,27 +284,27 @@
(st_dma.dma_vhi = 0xaa) && (st_dma.dma_hi = 0x55) &&
st_dma.dma_vhi == 0xaa && st_dma.dma_hi == 0x55)) {
ATARIHW_SET(EXTD_DMA);
- printk("EXTD_DMA ");
+ pr_cont(" EXTD_DMA");
}
if (hwreg_present(&tt_scsi.scsi_data)) {
ATARIHW_SET(TT_SCSI);
- printk("TT_SCSI ");
+ pr_cont(" TT_SCSI");
}
if (hwreg_present(&sound_ym.rd_data_reg_sel)) {
ATARIHW_SET(YM_2149);
- printk("YM2149 ");
+ pr_cont(" YM2149");
}
if (!MACH_IS_MEDUSA && hwreg_present(&tt_dmasnd.ctrl)) {
ATARIHW_SET(PCM_8BIT);
- printk("PCM ");
+ pr_cont(" PCM");
}
if (hwreg_present(&falcon_codec.unused5)) {
ATARIHW_SET(CODEC);
- printk("CODEC ");
+ pr_cont(" CODEC");
}
if (hwreg_present(&dsp56k_host_interface.icr)) {
ATARIHW_SET(DSP56K);
- printk("DSP56K ");
+ pr_cont(" DSP56K");
}
if (hwreg_present(&tt_scc_dma.dma_ctrl) &&
#if 0
@@ -316,33 +316,33 @@
#endif
) {
ATARIHW_SET(SCC_DMA);
- printk("SCC_DMA ");
+ pr_cont(" SCC_DMA");
}
if (scc_test(&atari_scc.cha_a_ctrl)) {
ATARIHW_SET(SCC);
- printk("SCC ");
+ pr_cont(" SCC");
}
if (scc_test(&st_escc.cha_b_ctrl)) {
ATARIHW_SET(ST_ESCC);
- printk("ST_ESCC ");
+ pr_cont(" ST_ESCC");
}
if (hwreg_present(&tt_scu.sys_mask)) {
ATARIHW_SET(SCU);
/* Assume a VME bus if there's a SCU */
ATARIHW_SET(VME);
- printk("VME SCU ");
+ pr_cont(" VME SCU");
}
if (hwreg_present((void *)(0xffff9210))) {
ATARIHW_SET(ANALOG_JOY);
- printk("ANALOG_JOY ");
+ pr_cont(" ANALOG_JOY");
}
if (hwreg_present(blitter.halftone)) {
ATARIHW_SET(BLITTER);
- printk("BLITTER ");
+ pr_cont(" BLITTER");
}
if (hwreg_present((void *)0xfff00039)) {
ATARIHW_SET(IDE);
- printk("IDE ");
+ pr_cont(" IDE");
}
#if 1 /* This maybe wrong */
if (!MACH_IS_MEDUSA && hwreg_present(&tt_microwire.data) &&
@@ -355,31 +355,31 @@
ATARIHW_SET(MICROWIRE);
while (tt_microwire.mask != 0x7ff)
;
- printk("MICROWIRE ");
+ pr_cont(" MICROWIRE");
}
#endif
if (hwreg_present(&tt_rtc.regsel)) {
ATARIHW_SET(TT_CLK);
- printk("TT_CLK ");
+ pr_cont(" TT_CLK");
mach_hwclk = atari_tt_hwclk;
mach_set_clock_mmss = atari_tt_set_clock_mmss;
}
if (hwreg_present(&mste_rtc.sec_ones)) {
ATARIHW_SET(MSTE_CLK);
- printk("MSTE_CLK ");
+ pr_cont(" MSTE_CLK");
mach_hwclk = atari_mste_hwclk;
mach_set_clock_mmss = atari_mste_set_clock_mmss;
}
if (!MACH_IS_MEDUSA && hwreg_present(&dma_wd.fdc_speed) &&
hwreg_write(&dma_wd.fdc_speed, 0)) {
ATARIHW_SET(FDCSPEED);
- printk("FDC_SPEED ");
+ pr_cont(" FDC_SPEED");
}
if (!ATARIHW_PRESENT(ST_SCSI)) {
ATARIHW_SET(ACSI);
- printk("ACSI ");
+ pr_cont(" ACSI");
}
- printk("\n");
+ pr_cont("\n");
if (CPU_IS_040_OR_060)
/* Now it seems to be safe to turn of the tt0 transparent
diff --git a/arch/m68k/bvme6000/config.c b/arch/m68k/bvme6000/config.c
index 611d4d9..2cfff47 100644
--- a/arch/m68k/bvme6000/config.c
+++ b/arch/m68k/bvme6000/config.c
@@ -63,8 +63,8 @@
{
volatile PitRegsPtr pit = (PitRegsPtr)BVME_PIT_BASE;
- printk ("\r\n\nCalled bvme6000_reset\r\n"
- "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r");
+ pr_info("\r\n\nCalled bvme6000_reset\r\n"
+ "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r");
/* The string of returns is to delay the reset until the whole
* message is output. */
/* Enable the watchdog, via PIT port C bit 4 */
@@ -117,8 +117,8 @@
mach_reset = bvme6000_reset;
mach_get_model = bvme6000_get_model;
- printk ("Board is %sconfigured as a System Controller\n",
- *config_reg_ptr & BVME_CONFIG_SW1 ? "" : "not ");
+ pr_info("Board is %sconfigured as a System Controller\n",
+ *config_reg_ptr & BVME_CONFIG_SW1 ? "" : "not ");
/* Now do the PIT configuration */
diff --git a/arch/m68k/bvme6000/rtc.c b/arch/m68k/bvme6000/rtc.c
index d53c9b3..e4f1faf 100644
--- a/arch/m68k/bvme6000/rtc.c
+++ b/arch/m68k/bvme6000/rtc.c
@@ -168,7 +168,7 @@
if (!MACH_IS_BVME6000)
return -ENODEV;
- printk(KERN_INFO "DP8570A Real Time Clock Driver v%s\n", RTC_VERSION);
+ pr_info("DP8570A Real Time Clock Driver v%s\n", RTC_VERSION);
return misc_register(&rtc_dev);
}
module_init(rtc_DP8570A_init);
diff --git a/arch/m68k/configs/amiga_defconfig b/arch/m68k/configs/amiga_defconfig
index b98acd15..048bf07 100644
--- a/arch/m68k/configs/amiga_defconfig
+++ b/arch/m68k/configs/amiga_defconfig
@@ -66,6 +66,7 @@
CONFIG_INET_XFRM_MODE_BEET=m
CONFIG_INET_DIAG=m
CONFIG_INET_UDP_DIAG=m
+CONFIG_INET_RAW_DIAG=m
CONFIG_IPV6=m
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_INET6_AH=m
@@ -76,10 +77,10 @@
CONFIG_IPV6_GRE=m
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
CONFIG_NF_CONNTRACK_ZONES=y
# CONFIG_NF_CONNTRACK_PROCFS is not set
# CONFIG_NF_CT_PROTO_DCCP is not set
-CONFIG_NF_CT_PROTO_UDPLITE=m
CONFIG_NF_CONNTRACK_AMANDA=m
CONFIG_NF_CONNTRACK_FTP=m
CONFIG_NF_CONNTRACK_H323=m
@@ -95,6 +96,7 @@
CONFIG_NF_TABLES_NETDEV=m
CONFIG_NFT_EXTHDR=m
CONFIG_NFT_META=m
+CONFIG_NFT_RT=m
CONFIG_NFT_NUMGEN=m
CONFIG_NFT_CT=m
CONFIG_NFT_SET_RBTREE=m
@@ -105,11 +107,13 @@
CONFIG_NFT_MASQ=m
CONFIG_NFT_REDIR=m
CONFIG_NFT_NAT=m
+CONFIG_NFT_OBJREF=m
CONFIG_NFT_QUEUE=m
CONFIG_NFT_QUOTA=m
CONFIG_NFT_REJECT=m
CONFIG_NFT_COMPAT=m
CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB_INET=m
CONFIG_NFT_DUP_NETDEV=m
CONFIG_NFT_FWD_NETDEV=m
CONFIG_NETFILTER_XT_SET=m
@@ -176,6 +180,7 @@
CONFIG_IP_SET_HASH_IPPORT=m
CONFIG_IP_SET_HASH_IPPORTIP=m
CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_IPMAC=m
CONFIG_IP_SET_HASH_MAC=m
CONFIG_IP_SET_HASH_NETPORTNET=m
CONFIG_IP_SET_HASH_NET=m
@@ -184,8 +189,10 @@
CONFIG_IP_SET_HASH_NETIFACE=m
CONFIG_IP_SET_LIST_SET=m
CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_NF_SOCKET_IPV4=m
CONFIG_NFT_CHAIN_ROUTE_IPV4=m
CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
CONFIG_NF_TABLES_ARP=m
CONFIG_NF_LOG_ARP=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
@@ -212,8 +219,10 @@
CONFIG_IP_NF_ARPFILTER=m
CONFIG_IP_NF_ARP_MANGLE=m
CONFIG_NF_CONNTRACK_IPV6=m
+CONFIG_NF_SOCKET_IPV6=m
CONFIG_NFT_CHAIN_ROUTE_IPV6=m
CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV6=m
CONFIG_NFT_MASQ_IPV6=m
CONFIG_NFT_REDIR_IPV6=m
@@ -294,6 +303,7 @@
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_TEST_ASYNC_DRIVER_PROBE=m
CONFIG_CONNECTOR=m
CONFIG_PARPORT=m
CONFIG_PARPORT_AMIGA=m
@@ -369,6 +379,7 @@
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_VETH=m
# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_NET_VENDOR_ALACRITECH is not set
# CONFIG_NET_VENDOR_AMAZON is not set
CONFIG_A2065=y
CONFIG_ARIADNE=y
@@ -390,6 +401,7 @@
# CONFIG_NET_VENDOR_ROCKER is not set
# CONFIG_NET_VENDOR_SAMSUNG is not set
# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
# CONFIG_NET_VENDOR_SMSC is not set
# CONFIG_NET_VENDOR_STMICRO is not set
# CONFIG_NET_VENDOR_SYNOPSYS is not set
@@ -421,7 +433,6 @@
CONFIG_INPUT_M68K_BEEP=m
# CONFIG_SERIO is not set
# CONFIG_LEGACY_PTYS is not set
-# CONFIG_DEVKMEM is not set
CONFIG_PRINTER=m
# CONFIG_HW_RANDOM is not set
CONFIG_NTP_PPS=y
@@ -569,6 +580,7 @@
CONFIG_TEST_UDELAY=m
CONFIG_TEST_STATIC_KEYS=m
CONFIG_EARLY_PRINTK=y
+CONFIG_ENCRYPTED_KEYS=m
CONFIG_CRYPTO_RSA=m
CONFIG_CRYPTO_DH=m
CONFIG_CRYPTO_ECDH=m
diff --git a/arch/m68k/configs/apollo_defconfig b/arch/m68k/configs/apollo_defconfig
index f80dc57..d4de249 100644
--- a/arch/m68k/configs/apollo_defconfig
+++ b/arch/m68k/configs/apollo_defconfig
@@ -64,6 +64,7 @@
CONFIG_INET_XFRM_MODE_BEET=m
CONFIG_INET_DIAG=m
CONFIG_INET_UDP_DIAG=m
+CONFIG_INET_RAW_DIAG=m
CONFIG_IPV6=m
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_INET6_AH=m
@@ -74,10 +75,10 @@
CONFIG_IPV6_GRE=m
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
CONFIG_NF_CONNTRACK_ZONES=y
# CONFIG_NF_CONNTRACK_PROCFS is not set
# CONFIG_NF_CT_PROTO_DCCP is not set
-CONFIG_NF_CT_PROTO_UDPLITE=m
CONFIG_NF_CONNTRACK_AMANDA=m
CONFIG_NF_CONNTRACK_FTP=m
CONFIG_NF_CONNTRACK_H323=m
@@ -93,6 +94,7 @@
CONFIG_NF_TABLES_NETDEV=m
CONFIG_NFT_EXTHDR=m
CONFIG_NFT_META=m
+CONFIG_NFT_RT=m
CONFIG_NFT_NUMGEN=m
CONFIG_NFT_CT=m
CONFIG_NFT_SET_RBTREE=m
@@ -103,11 +105,13 @@
CONFIG_NFT_MASQ=m
CONFIG_NFT_REDIR=m
CONFIG_NFT_NAT=m
+CONFIG_NFT_OBJREF=m
CONFIG_NFT_QUEUE=m
CONFIG_NFT_QUOTA=m
CONFIG_NFT_REJECT=m
CONFIG_NFT_COMPAT=m
CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB_INET=m
CONFIG_NFT_DUP_NETDEV=m
CONFIG_NFT_FWD_NETDEV=m
CONFIG_NETFILTER_XT_SET=m
@@ -174,6 +178,7 @@
CONFIG_IP_SET_HASH_IPPORT=m
CONFIG_IP_SET_HASH_IPPORTIP=m
CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_IPMAC=m
CONFIG_IP_SET_HASH_MAC=m
CONFIG_IP_SET_HASH_NETPORTNET=m
CONFIG_IP_SET_HASH_NET=m
@@ -182,8 +187,10 @@
CONFIG_IP_SET_HASH_NETIFACE=m
CONFIG_IP_SET_LIST_SET=m
CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_NF_SOCKET_IPV4=m
CONFIG_NFT_CHAIN_ROUTE_IPV4=m
CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
CONFIG_NF_TABLES_ARP=m
CONFIG_NF_LOG_ARP=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
@@ -210,8 +217,10 @@
CONFIG_IP_NF_ARPFILTER=m
CONFIG_IP_NF_ARP_MANGLE=m
CONFIG_NF_CONNTRACK_IPV6=m
+CONFIG_NF_SOCKET_IPV6=m
CONFIG_NFT_CHAIN_ROUTE_IPV6=m
CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV6=m
CONFIG_NFT_MASQ_IPV6=m
CONFIG_NFT_REDIR_IPV6=m
@@ -292,6 +301,7 @@
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_TEST_ASYNC_DRIVER_PROBE=m
CONFIG_CONNECTOR=m
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_CRYPTOLOOP=m
@@ -350,6 +360,7 @@
CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_VETH=m
+# CONFIG_NET_VENDOR_ALACRITECH is not set
# CONFIG_NET_VENDOR_AMAZON is not set
# CONFIG_NET_VENDOR_ARC is not set
# CONFIG_NET_CADENCE is not set
@@ -365,6 +376,7 @@
# CONFIG_NET_VENDOR_ROCKER is not set
# CONFIG_NET_VENDOR_SAMSUNG is not set
# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
# CONFIG_NET_VENDOR_STMICRO is not set
# CONFIG_NET_VENDOR_SYNOPSYS is not set
# CONFIG_NET_VENDOR_VIA is not set
@@ -391,7 +403,6 @@
CONFIG_SERIO=m
CONFIG_USERIO=m
# CONFIG_LEGACY_PTYS is not set
-# CONFIG_DEVKMEM is not set
# CONFIG_HW_RANDOM is not set
CONFIG_NTP_PPS=y
CONFIG_PPS_CLIENT_LDISC=m
@@ -528,6 +539,7 @@
CONFIG_TEST_UDELAY=m
CONFIG_TEST_STATIC_KEYS=m
CONFIG_EARLY_PRINTK=y
+CONFIG_ENCRYPTED_KEYS=m
CONFIG_CRYPTO_RSA=m
CONFIG_CRYPTO_DH=m
CONFIG_CRYPTO_ECDH=m
diff --git a/arch/m68k/configs/atari_defconfig b/arch/m68k/configs/atari_defconfig
index 4e16b18..fc0fd3f 100644
--- a/arch/m68k/configs/atari_defconfig
+++ b/arch/m68k/configs/atari_defconfig
@@ -64,6 +64,7 @@
CONFIG_INET_XFRM_MODE_BEET=m
CONFIG_INET_DIAG=m
CONFIG_INET_UDP_DIAG=m
+CONFIG_INET_RAW_DIAG=m
CONFIG_IPV6=m
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_INET6_AH=m
@@ -74,10 +75,10 @@
CONFIG_IPV6_GRE=m
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
CONFIG_NF_CONNTRACK_ZONES=y
# CONFIG_NF_CONNTRACK_PROCFS is not set
# CONFIG_NF_CT_PROTO_DCCP is not set
-CONFIG_NF_CT_PROTO_UDPLITE=m
CONFIG_NF_CONNTRACK_AMANDA=m
CONFIG_NF_CONNTRACK_FTP=m
CONFIG_NF_CONNTRACK_H323=m
@@ -93,6 +94,7 @@
CONFIG_NF_TABLES_NETDEV=m
CONFIG_NFT_EXTHDR=m
CONFIG_NFT_META=m
+CONFIG_NFT_RT=m
CONFIG_NFT_NUMGEN=m
CONFIG_NFT_CT=m
CONFIG_NFT_SET_RBTREE=m
@@ -103,11 +105,13 @@
CONFIG_NFT_MASQ=m
CONFIG_NFT_REDIR=m
CONFIG_NFT_NAT=m
+CONFIG_NFT_OBJREF=m
CONFIG_NFT_QUEUE=m
CONFIG_NFT_QUOTA=m
CONFIG_NFT_REJECT=m
CONFIG_NFT_COMPAT=m
CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB_INET=m
CONFIG_NFT_DUP_NETDEV=m
CONFIG_NFT_FWD_NETDEV=m
CONFIG_NETFILTER_XT_SET=m
@@ -174,6 +178,7 @@
CONFIG_IP_SET_HASH_IPPORT=m
CONFIG_IP_SET_HASH_IPPORTIP=m
CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_IPMAC=m
CONFIG_IP_SET_HASH_MAC=m
CONFIG_IP_SET_HASH_NETPORTNET=m
CONFIG_IP_SET_HASH_NET=m
@@ -182,8 +187,10 @@
CONFIG_IP_SET_HASH_NETIFACE=m
CONFIG_IP_SET_LIST_SET=m
CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_NF_SOCKET_IPV4=m
CONFIG_NFT_CHAIN_ROUTE_IPV4=m
CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
CONFIG_NF_TABLES_ARP=m
CONFIG_NF_LOG_ARP=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
@@ -210,8 +217,10 @@
CONFIG_IP_NF_ARPFILTER=m
CONFIG_IP_NF_ARP_MANGLE=m
CONFIG_NF_CONNTRACK_IPV6=m
+CONFIG_NF_SOCKET_IPV6=m
CONFIG_NFT_CHAIN_ROUTE_IPV6=m
CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV6=m
CONFIG_NFT_MASQ_IPV6=m
CONFIG_NFT_REDIR_IPV6=m
@@ -292,6 +301,7 @@
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_TEST_ASYNC_DRIVER_PROBE=m
CONFIG_CONNECTOR=m
CONFIG_PARPORT=m
CONFIG_PARPORT_ATARI=m
@@ -359,6 +369,7 @@
CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_VETH=m
+# CONFIG_NET_VENDOR_ALACRITECH is not set
# CONFIG_NET_VENDOR_AMAZON is not set
CONFIG_ATARILANCE=y
# CONFIG_NET_VENDOR_ARC is not set
@@ -375,6 +386,7 @@
# CONFIG_NET_VENDOR_ROCKER is not set
# CONFIG_NET_VENDOR_SAMSUNG is not set
# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
CONFIG_SMC91X=y
# CONFIG_NET_VENDOR_STMICRO is not set
# CONFIG_NET_VENDOR_SYNOPSYS is not set
@@ -404,7 +416,6 @@
CONFIG_INPUT_M68K_BEEP=m
# CONFIG_SERIO is not set
# CONFIG_LEGACY_PTYS is not set
-# CONFIG_DEVKMEM is not set
CONFIG_PRINTER=m
# CONFIG_HW_RANDOM is not set
CONFIG_NTP_PPS=y
@@ -549,6 +560,7 @@
CONFIG_TEST_UDELAY=m
CONFIG_TEST_STATIC_KEYS=m
CONFIG_EARLY_PRINTK=y
+CONFIG_ENCRYPTED_KEYS=m
CONFIG_CRYPTO_RSA=m
CONFIG_CRYPTO_DH=m
CONFIG_CRYPTO_ECDH=m
diff --git a/arch/m68k/configs/bvme6000_defconfig b/arch/m68k/configs/bvme6000_defconfig
index 2767bbf..52e984a 100644
--- a/arch/m68k/configs/bvme6000_defconfig
+++ b/arch/m68k/configs/bvme6000_defconfig
@@ -62,6 +62,7 @@
CONFIG_INET_XFRM_MODE_BEET=m
CONFIG_INET_DIAG=m
CONFIG_INET_UDP_DIAG=m
+CONFIG_INET_RAW_DIAG=m
CONFIG_IPV6=m
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_INET6_AH=m
@@ -72,10 +73,10 @@
CONFIG_IPV6_GRE=m
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
CONFIG_NF_CONNTRACK_ZONES=y
# CONFIG_NF_CONNTRACK_PROCFS is not set
# CONFIG_NF_CT_PROTO_DCCP is not set
-CONFIG_NF_CT_PROTO_UDPLITE=m
CONFIG_NF_CONNTRACK_AMANDA=m
CONFIG_NF_CONNTRACK_FTP=m
CONFIG_NF_CONNTRACK_H323=m
@@ -91,6 +92,7 @@
CONFIG_NF_TABLES_NETDEV=m
CONFIG_NFT_EXTHDR=m
CONFIG_NFT_META=m
+CONFIG_NFT_RT=m
CONFIG_NFT_NUMGEN=m
CONFIG_NFT_CT=m
CONFIG_NFT_SET_RBTREE=m
@@ -101,11 +103,13 @@
CONFIG_NFT_MASQ=m
CONFIG_NFT_REDIR=m
CONFIG_NFT_NAT=m
+CONFIG_NFT_OBJREF=m
CONFIG_NFT_QUEUE=m
CONFIG_NFT_QUOTA=m
CONFIG_NFT_REJECT=m
CONFIG_NFT_COMPAT=m
CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB_INET=m
CONFIG_NFT_DUP_NETDEV=m
CONFIG_NFT_FWD_NETDEV=m
CONFIG_NETFILTER_XT_SET=m
@@ -172,6 +176,7 @@
CONFIG_IP_SET_HASH_IPPORT=m
CONFIG_IP_SET_HASH_IPPORTIP=m
CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_IPMAC=m
CONFIG_IP_SET_HASH_MAC=m
CONFIG_IP_SET_HASH_NETPORTNET=m
CONFIG_IP_SET_HASH_NET=m
@@ -180,8 +185,10 @@
CONFIG_IP_SET_HASH_NETIFACE=m
CONFIG_IP_SET_LIST_SET=m
CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_NF_SOCKET_IPV4=m
CONFIG_NFT_CHAIN_ROUTE_IPV4=m
CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
CONFIG_NF_TABLES_ARP=m
CONFIG_NF_LOG_ARP=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
@@ -208,8 +215,10 @@
CONFIG_IP_NF_ARPFILTER=m
CONFIG_IP_NF_ARP_MANGLE=m
CONFIG_NF_CONNTRACK_IPV6=m
+CONFIG_NF_SOCKET_IPV6=m
CONFIG_NFT_CHAIN_ROUTE_IPV6=m
CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV6=m
CONFIG_NFT_MASQ_IPV6=m
CONFIG_NFT_REDIR_IPV6=m
@@ -290,6 +299,7 @@
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_TEST_ASYNC_DRIVER_PROBE=m
CONFIG_CONNECTOR=m
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_CRYPTOLOOP=m
@@ -349,6 +359,7 @@
CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_VETH=m
+# CONFIG_NET_VENDOR_ALACRITECH is not set
# CONFIG_NET_VENDOR_AMAZON is not set
# CONFIG_NET_VENDOR_ARC is not set
# CONFIG_NET_CADENCE is not set
@@ -364,6 +375,7 @@
# CONFIG_NET_VENDOR_ROCKER is not set
# CONFIG_NET_VENDOR_SAMSUNG is not set
# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
# CONFIG_NET_VENDOR_STMICRO is not set
# CONFIG_NET_VENDOR_SYNOPSYS is not set
# CONFIG_NET_VENDOR_VIA is not set
@@ -389,7 +401,6 @@
# CONFIG_SERIO is not set
CONFIG_VT_HW_CONSOLE_BINDING=y
# CONFIG_LEGACY_PTYS is not set
-# CONFIG_DEVKMEM is not set
# CONFIG_HW_RANDOM is not set
CONFIG_NTP_PPS=y
CONFIG_PPS_CLIENT_LDISC=m
@@ -520,6 +531,7 @@
CONFIG_TEST_UDELAY=m
CONFIG_TEST_STATIC_KEYS=m
CONFIG_EARLY_PRINTK=y
+CONFIG_ENCRYPTED_KEYS=m
CONFIG_CRYPTO_RSA=m
CONFIG_CRYPTO_DH=m
CONFIG_CRYPTO_ECDH=m
diff --git a/arch/m68k/configs/hp300_defconfig b/arch/m68k/configs/hp300_defconfig
index d13ba30..aaeed44 100644
--- a/arch/m68k/configs/hp300_defconfig
+++ b/arch/m68k/configs/hp300_defconfig
@@ -64,6 +64,7 @@
CONFIG_INET_XFRM_MODE_BEET=m
CONFIG_INET_DIAG=m
CONFIG_INET_UDP_DIAG=m
+CONFIG_INET_RAW_DIAG=m
CONFIG_IPV6=m
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_INET6_AH=m
@@ -74,10 +75,10 @@
CONFIG_IPV6_GRE=m
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
CONFIG_NF_CONNTRACK_ZONES=y
# CONFIG_NF_CONNTRACK_PROCFS is not set
# CONFIG_NF_CT_PROTO_DCCP is not set
-CONFIG_NF_CT_PROTO_UDPLITE=m
CONFIG_NF_CONNTRACK_AMANDA=m
CONFIG_NF_CONNTRACK_FTP=m
CONFIG_NF_CONNTRACK_H323=m
@@ -93,6 +94,7 @@
CONFIG_NF_TABLES_NETDEV=m
CONFIG_NFT_EXTHDR=m
CONFIG_NFT_META=m
+CONFIG_NFT_RT=m
CONFIG_NFT_NUMGEN=m
CONFIG_NFT_CT=m
CONFIG_NFT_SET_RBTREE=m
@@ -103,11 +105,13 @@
CONFIG_NFT_MASQ=m
CONFIG_NFT_REDIR=m
CONFIG_NFT_NAT=m
+CONFIG_NFT_OBJREF=m
CONFIG_NFT_QUEUE=m
CONFIG_NFT_QUOTA=m
CONFIG_NFT_REJECT=m
CONFIG_NFT_COMPAT=m
CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB_INET=m
CONFIG_NFT_DUP_NETDEV=m
CONFIG_NFT_FWD_NETDEV=m
CONFIG_NETFILTER_XT_SET=m
@@ -174,6 +178,7 @@
CONFIG_IP_SET_HASH_IPPORT=m
CONFIG_IP_SET_HASH_IPPORTIP=m
CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_IPMAC=m
CONFIG_IP_SET_HASH_MAC=m
CONFIG_IP_SET_HASH_NETPORTNET=m
CONFIG_IP_SET_HASH_NET=m
@@ -182,8 +187,10 @@
CONFIG_IP_SET_HASH_NETIFACE=m
CONFIG_IP_SET_LIST_SET=m
CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_NF_SOCKET_IPV4=m
CONFIG_NFT_CHAIN_ROUTE_IPV4=m
CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
CONFIG_NF_TABLES_ARP=m
CONFIG_NF_LOG_ARP=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
@@ -210,8 +217,10 @@
CONFIG_IP_NF_ARPFILTER=m
CONFIG_IP_NF_ARP_MANGLE=m
CONFIG_NF_CONNTRACK_IPV6=m
+CONFIG_NF_SOCKET_IPV6=m
CONFIG_NFT_CHAIN_ROUTE_IPV6=m
CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV6=m
CONFIG_NFT_MASQ_IPV6=m
CONFIG_NFT_REDIR_IPV6=m
@@ -292,6 +301,7 @@
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_TEST_ASYNC_DRIVER_PROBE=m
CONFIG_CONNECTOR=m
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_CRYPTOLOOP=m
@@ -350,6 +360,7 @@
CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_VETH=m
+# CONFIG_NET_VENDOR_ALACRITECH is not set
# CONFIG_NET_VENDOR_AMAZON is not set
CONFIG_HPLANCE=y
# CONFIG_NET_VENDOR_ARC is not set
@@ -366,6 +377,7 @@
# CONFIG_NET_VENDOR_ROCKER is not set
# CONFIG_NET_VENDOR_SAMSUNG is not set
# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
# CONFIG_NET_VENDOR_STMICRO is not set
# CONFIG_NET_VENDOR_SYNOPSYS is not set
# CONFIG_NET_VENDOR_VIA is not set
@@ -394,7 +406,6 @@
CONFIG_SERIO_SERPORT=m
CONFIG_USERIO=m
# CONFIG_LEGACY_PTYS is not set
-# CONFIG_DEVKMEM is not set
# CONFIG_HW_RANDOM is not set
CONFIG_NTP_PPS=y
CONFIG_PPS_CLIENT_LDISC=m
@@ -530,6 +541,7 @@
CONFIG_TEST_UDELAY=m
CONFIG_TEST_STATIC_KEYS=m
CONFIG_EARLY_PRINTK=y
+CONFIG_ENCRYPTED_KEYS=m
CONFIG_CRYPTO_RSA=m
CONFIG_CRYPTO_DH=m
CONFIG_CRYPTO_ECDH=m
diff --git a/arch/m68k/configs/mac_defconfig b/arch/m68k/configs/mac_defconfig
index 78b5101..3bbc9b2 100644
--- a/arch/m68k/configs/mac_defconfig
+++ b/arch/m68k/configs/mac_defconfig
@@ -63,6 +63,7 @@
CONFIG_INET_XFRM_MODE_BEET=m
CONFIG_INET_DIAG=m
CONFIG_INET_UDP_DIAG=m
+CONFIG_INET_RAW_DIAG=m
CONFIG_IPV6=m
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_INET6_AH=m
@@ -73,10 +74,10 @@
CONFIG_IPV6_GRE=m
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
CONFIG_NF_CONNTRACK_ZONES=y
# CONFIG_NF_CONNTRACK_PROCFS is not set
# CONFIG_NF_CT_PROTO_DCCP is not set
-CONFIG_NF_CT_PROTO_UDPLITE=m
CONFIG_NF_CONNTRACK_AMANDA=m
CONFIG_NF_CONNTRACK_FTP=m
CONFIG_NF_CONNTRACK_H323=m
@@ -92,6 +93,7 @@
CONFIG_NF_TABLES_NETDEV=m
CONFIG_NFT_EXTHDR=m
CONFIG_NFT_META=m
+CONFIG_NFT_RT=m
CONFIG_NFT_NUMGEN=m
CONFIG_NFT_CT=m
CONFIG_NFT_SET_RBTREE=m
@@ -102,11 +104,13 @@
CONFIG_NFT_MASQ=m
CONFIG_NFT_REDIR=m
CONFIG_NFT_NAT=m
+CONFIG_NFT_OBJREF=m
CONFIG_NFT_QUEUE=m
CONFIG_NFT_QUOTA=m
CONFIG_NFT_REJECT=m
CONFIG_NFT_COMPAT=m
CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB_INET=m
CONFIG_NFT_DUP_NETDEV=m
CONFIG_NFT_FWD_NETDEV=m
CONFIG_NETFILTER_XT_SET=m
@@ -173,6 +177,7 @@
CONFIG_IP_SET_HASH_IPPORT=m
CONFIG_IP_SET_HASH_IPPORTIP=m
CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_IPMAC=m
CONFIG_IP_SET_HASH_MAC=m
CONFIG_IP_SET_HASH_NETPORTNET=m
CONFIG_IP_SET_HASH_NET=m
@@ -181,8 +186,10 @@
CONFIG_IP_SET_HASH_NETIFACE=m
CONFIG_IP_SET_LIST_SET=m
CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_NF_SOCKET_IPV4=m
CONFIG_NFT_CHAIN_ROUTE_IPV4=m
CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
CONFIG_NF_TABLES_ARP=m
CONFIG_NF_LOG_ARP=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
@@ -209,8 +216,10 @@
CONFIG_IP_NF_ARPFILTER=m
CONFIG_IP_NF_ARP_MANGLE=m
CONFIG_NF_CONNTRACK_IPV6=m
+CONFIG_NF_SOCKET_IPV6=m
CONFIG_NFT_CHAIN_ROUTE_IPV6=m
CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV6=m
CONFIG_NFT_MASQ_IPV6=m
CONFIG_NFT_REDIR_IPV6=m
@@ -294,6 +303,7 @@
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_TEST_ASYNC_DRIVER_PROBE=m
CONFIG_CONNECTOR=m
CONFIG_BLK_DEV_SWIM=m
CONFIG_BLK_DEV_LOOP=y
@@ -366,6 +376,7 @@
CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_VETH=m
+# CONFIG_NET_VENDOR_ALACRITECH is not set
# CONFIG_NET_VENDOR_AMAZON is not set
CONFIG_MACMACE=y
# CONFIG_NET_VENDOR_ARC is not set
@@ -384,6 +395,7 @@
# CONFIG_NET_VENDOR_ROCKER is not set
# CONFIG_NET_VENDOR_SAMSUNG is not set
# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
# CONFIG_NET_VENDOR_SMSC is not set
# CONFIG_NET_VENDOR_STMICRO is not set
# CONFIG_NET_VENDOR_SYNOPSYS is not set
@@ -413,7 +425,6 @@
CONFIG_SERIO=m
CONFIG_USERIO=m
# CONFIG_LEGACY_PTYS is not set
-# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_PMACZILOG=y
CONFIG_SERIAL_PMACZILOG_TTYS=y
CONFIG_SERIAL_PMACZILOG_CONSOLE=y
@@ -552,6 +563,7 @@
CONFIG_TEST_UDELAY=m
CONFIG_TEST_STATIC_KEYS=m
CONFIG_EARLY_PRINTK=y
+CONFIG_ENCRYPTED_KEYS=m
CONFIG_CRYPTO_RSA=m
CONFIG_CRYPTO_DH=m
CONFIG_CRYPTO_ECDH=m
diff --git a/arch/m68k/configs/multi_defconfig b/arch/m68k/configs/multi_defconfig
index 38e5bcb..8f2c0de 100644
--- a/arch/m68k/configs/multi_defconfig
+++ b/arch/m68k/configs/multi_defconfig
@@ -73,6 +73,7 @@
CONFIG_INET_XFRM_MODE_BEET=m
CONFIG_INET_DIAG=m
CONFIG_INET_UDP_DIAG=m
+CONFIG_INET_RAW_DIAG=m
CONFIG_IPV6=m
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_INET6_AH=m
@@ -83,10 +84,10 @@
CONFIG_IPV6_GRE=m
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
CONFIG_NF_CONNTRACK_ZONES=y
# CONFIG_NF_CONNTRACK_PROCFS is not set
# CONFIG_NF_CT_PROTO_DCCP is not set
-CONFIG_NF_CT_PROTO_UDPLITE=m
CONFIG_NF_CONNTRACK_AMANDA=m
CONFIG_NF_CONNTRACK_FTP=m
CONFIG_NF_CONNTRACK_H323=m
@@ -102,6 +103,7 @@
CONFIG_NF_TABLES_NETDEV=m
CONFIG_NFT_EXTHDR=m
CONFIG_NFT_META=m
+CONFIG_NFT_RT=m
CONFIG_NFT_NUMGEN=m
CONFIG_NFT_CT=m
CONFIG_NFT_SET_RBTREE=m
@@ -112,11 +114,13 @@
CONFIG_NFT_MASQ=m
CONFIG_NFT_REDIR=m
CONFIG_NFT_NAT=m
+CONFIG_NFT_OBJREF=m
CONFIG_NFT_QUEUE=m
CONFIG_NFT_QUOTA=m
CONFIG_NFT_REJECT=m
CONFIG_NFT_COMPAT=m
CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB_INET=m
CONFIG_NFT_DUP_NETDEV=m
CONFIG_NFT_FWD_NETDEV=m
CONFIG_NETFILTER_XT_SET=m
@@ -183,6 +187,7 @@
CONFIG_IP_SET_HASH_IPPORT=m
CONFIG_IP_SET_HASH_IPPORTIP=m
CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_IPMAC=m
CONFIG_IP_SET_HASH_MAC=m
CONFIG_IP_SET_HASH_NETPORTNET=m
CONFIG_IP_SET_HASH_NET=m
@@ -191,8 +196,10 @@
CONFIG_IP_SET_HASH_NETIFACE=m
CONFIG_IP_SET_LIST_SET=m
CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_NF_SOCKET_IPV4=m
CONFIG_NFT_CHAIN_ROUTE_IPV4=m
CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
CONFIG_NF_TABLES_ARP=m
CONFIG_NF_LOG_ARP=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
@@ -219,8 +226,10 @@
CONFIG_IP_NF_ARPFILTER=m
CONFIG_IP_NF_ARP_MANGLE=m
CONFIG_NF_CONNTRACK_IPV6=m
+CONFIG_NF_SOCKET_IPV6=m
CONFIG_NFT_CHAIN_ROUTE_IPV6=m
CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV6=m
CONFIG_NFT_MASQ_IPV6=m
CONFIG_NFT_REDIR_IPV6=m
@@ -304,6 +313,7 @@
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_TEST_ASYNC_DRIVER_PROBE=m
CONFIG_CONNECTOR=m
CONFIG_PARPORT=m
CONFIG_PARPORT_PC=m
@@ -400,6 +410,7 @@
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_VETH=m
# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_NET_VENDOR_ALACRITECH is not set
# CONFIG_NET_VENDOR_AMAZON is not set
CONFIG_A2065=y
CONFIG_ARIADNE=y
@@ -430,6 +441,7 @@
# CONFIG_NET_VENDOR_ROCKER is not set
# CONFIG_NET_VENDOR_SAMSUNG is not set
# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
CONFIG_SMC91X=y
# CONFIG_NET_VENDOR_STMICRO is not set
# CONFIG_NET_VENDOR_SYNOPSYS is not set
@@ -468,7 +480,6 @@
CONFIG_SERIO_Q40KBD=y
CONFIG_USERIO=m
# CONFIG_LEGACY_PTYS is not set
-# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_PMACZILOG=y
CONFIG_SERIAL_PMACZILOG_TTYS=y
CONFIG_SERIAL_PMACZILOG_CONSOLE=y
@@ -632,6 +643,7 @@
CONFIG_TEST_UDELAY=m
CONFIG_TEST_STATIC_KEYS=m
CONFIG_EARLY_PRINTK=y
+CONFIG_ENCRYPTED_KEYS=m
CONFIG_CRYPTO_RSA=m
CONFIG_CRYPTO_DH=m
CONFIG_CRYPTO_ECDH=m
diff --git a/arch/m68k/configs/mvme147_defconfig b/arch/m68k/configs/mvme147_defconfig
index 2868719..c743dd2 100644
--- a/arch/m68k/configs/mvme147_defconfig
+++ b/arch/m68k/configs/mvme147_defconfig
@@ -61,6 +61,7 @@
CONFIG_INET_XFRM_MODE_BEET=m
CONFIG_INET_DIAG=m
CONFIG_INET_UDP_DIAG=m
+CONFIG_INET_RAW_DIAG=m
CONFIG_IPV6=m
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_INET6_AH=m
@@ -71,10 +72,10 @@
CONFIG_IPV6_GRE=m
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
CONFIG_NF_CONNTRACK_ZONES=y
# CONFIG_NF_CONNTRACK_PROCFS is not set
# CONFIG_NF_CT_PROTO_DCCP is not set
-CONFIG_NF_CT_PROTO_UDPLITE=m
CONFIG_NF_CONNTRACK_AMANDA=m
CONFIG_NF_CONNTRACK_FTP=m
CONFIG_NF_CONNTRACK_H323=m
@@ -90,6 +91,7 @@
CONFIG_NF_TABLES_NETDEV=m
CONFIG_NFT_EXTHDR=m
CONFIG_NFT_META=m
+CONFIG_NFT_RT=m
CONFIG_NFT_NUMGEN=m
CONFIG_NFT_CT=m
CONFIG_NFT_SET_RBTREE=m
@@ -100,11 +102,13 @@
CONFIG_NFT_MASQ=m
CONFIG_NFT_REDIR=m
CONFIG_NFT_NAT=m
+CONFIG_NFT_OBJREF=m
CONFIG_NFT_QUEUE=m
CONFIG_NFT_QUOTA=m
CONFIG_NFT_REJECT=m
CONFIG_NFT_COMPAT=m
CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB_INET=m
CONFIG_NFT_DUP_NETDEV=m
CONFIG_NFT_FWD_NETDEV=m
CONFIG_NETFILTER_XT_SET=m
@@ -171,6 +175,7 @@
CONFIG_IP_SET_HASH_IPPORT=m
CONFIG_IP_SET_HASH_IPPORTIP=m
CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_IPMAC=m
CONFIG_IP_SET_HASH_MAC=m
CONFIG_IP_SET_HASH_NETPORTNET=m
CONFIG_IP_SET_HASH_NET=m
@@ -179,8 +184,10 @@
CONFIG_IP_SET_HASH_NETIFACE=m
CONFIG_IP_SET_LIST_SET=m
CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_NF_SOCKET_IPV4=m
CONFIG_NFT_CHAIN_ROUTE_IPV4=m
CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
CONFIG_NF_TABLES_ARP=m
CONFIG_NF_LOG_ARP=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
@@ -207,8 +214,10 @@
CONFIG_IP_NF_ARPFILTER=m
CONFIG_IP_NF_ARP_MANGLE=m
CONFIG_NF_CONNTRACK_IPV6=m
+CONFIG_NF_SOCKET_IPV6=m
CONFIG_NFT_CHAIN_ROUTE_IPV6=m
CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV6=m
CONFIG_NFT_MASQ_IPV6=m
CONFIG_NFT_REDIR_IPV6=m
@@ -289,6 +298,7 @@
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_TEST_ASYNC_DRIVER_PROBE=m
CONFIG_CONNECTOR=m
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_CRYPTOLOOP=m
@@ -348,6 +358,7 @@
CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_VETH=m
+# CONFIG_NET_VENDOR_ALACRITECH is not set
# CONFIG_NET_VENDOR_AMAZON is not set
CONFIG_MVME147_NET=y
# CONFIG_NET_VENDOR_ARC is not set
@@ -364,6 +375,7 @@
# CONFIG_NET_VENDOR_ROCKER is not set
# CONFIG_NET_VENDOR_SAMSUNG is not set
# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
# CONFIG_NET_VENDOR_STMICRO is not set
# CONFIG_NET_VENDOR_SYNOPSYS is not set
# CONFIG_NET_VENDOR_VIA is not set
@@ -389,7 +401,6 @@
# CONFIG_SERIO is not set
CONFIG_VT_HW_CONSOLE_BINDING=y
# CONFIG_LEGACY_PTYS is not set
-# CONFIG_DEVKMEM is not set
# CONFIG_HW_RANDOM is not set
CONFIG_NTP_PPS=y
CONFIG_PPS_CLIENT_LDISC=m
@@ -520,6 +531,7 @@
CONFIG_TEST_UDELAY=m
CONFIG_TEST_STATIC_KEYS=m
CONFIG_EARLY_PRINTK=y
+CONFIG_ENCRYPTED_KEYS=m
CONFIG_CRYPTO_RSA=m
CONFIG_CRYPTO_DH=m
CONFIG_CRYPTO_ECDH=m
diff --git a/arch/m68k/configs/mvme16x_defconfig b/arch/m68k/configs/mvme16x_defconfig
index 5a5f109..2ccaca8 100644
--- a/arch/m68k/configs/mvme16x_defconfig
+++ b/arch/m68k/configs/mvme16x_defconfig
@@ -62,6 +62,7 @@
CONFIG_INET_XFRM_MODE_BEET=m
CONFIG_INET_DIAG=m
CONFIG_INET_UDP_DIAG=m
+CONFIG_INET_RAW_DIAG=m
CONFIG_IPV6=m
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_INET6_AH=m
@@ -72,10 +73,10 @@
CONFIG_IPV6_GRE=m
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
CONFIG_NF_CONNTRACK_ZONES=y
# CONFIG_NF_CONNTRACK_PROCFS is not set
# CONFIG_NF_CT_PROTO_DCCP is not set
-CONFIG_NF_CT_PROTO_UDPLITE=m
CONFIG_NF_CONNTRACK_AMANDA=m
CONFIG_NF_CONNTRACK_FTP=m
CONFIG_NF_CONNTRACK_H323=m
@@ -91,6 +92,7 @@
CONFIG_NF_TABLES_NETDEV=m
CONFIG_NFT_EXTHDR=m
CONFIG_NFT_META=m
+CONFIG_NFT_RT=m
CONFIG_NFT_NUMGEN=m
CONFIG_NFT_CT=m
CONFIG_NFT_SET_RBTREE=m
@@ -101,11 +103,13 @@
CONFIG_NFT_MASQ=m
CONFIG_NFT_REDIR=m
CONFIG_NFT_NAT=m
+CONFIG_NFT_OBJREF=m
CONFIG_NFT_QUEUE=m
CONFIG_NFT_QUOTA=m
CONFIG_NFT_REJECT=m
CONFIG_NFT_COMPAT=m
CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB_INET=m
CONFIG_NFT_DUP_NETDEV=m
CONFIG_NFT_FWD_NETDEV=m
CONFIG_NETFILTER_XT_SET=m
@@ -172,6 +176,7 @@
CONFIG_IP_SET_HASH_IPPORT=m
CONFIG_IP_SET_HASH_IPPORTIP=m
CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_IPMAC=m
CONFIG_IP_SET_HASH_MAC=m
CONFIG_IP_SET_HASH_NETPORTNET=m
CONFIG_IP_SET_HASH_NET=m
@@ -180,8 +185,10 @@
CONFIG_IP_SET_HASH_NETIFACE=m
CONFIG_IP_SET_LIST_SET=m
CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_NF_SOCKET_IPV4=m
CONFIG_NFT_CHAIN_ROUTE_IPV4=m
CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
CONFIG_NF_TABLES_ARP=m
CONFIG_NF_LOG_ARP=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
@@ -208,8 +215,10 @@
CONFIG_IP_NF_ARPFILTER=m
CONFIG_IP_NF_ARP_MANGLE=m
CONFIG_NF_CONNTRACK_IPV6=m
+CONFIG_NF_SOCKET_IPV6=m
CONFIG_NFT_CHAIN_ROUTE_IPV6=m
CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV6=m
CONFIG_NFT_MASQ_IPV6=m
CONFIG_NFT_REDIR_IPV6=m
@@ -290,6 +299,7 @@
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_TEST_ASYNC_DRIVER_PROBE=m
CONFIG_CONNECTOR=m
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_CRYPTOLOOP=m
@@ -349,6 +359,7 @@
CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_VETH=m
+# CONFIG_NET_VENDOR_ALACRITECH is not set
# CONFIG_NET_VENDOR_AMAZON is not set
# CONFIG_NET_VENDOR_ARC is not set
# CONFIG_NET_CADENCE is not set
@@ -364,6 +375,7 @@
# CONFIG_NET_VENDOR_ROCKER is not set
# CONFIG_NET_VENDOR_SAMSUNG is not set
# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
# CONFIG_NET_VENDOR_STMICRO is not set
# CONFIG_NET_VENDOR_SYNOPSYS is not set
# CONFIG_NET_VENDOR_VIA is not set
@@ -389,7 +401,6 @@
# CONFIG_SERIO is not set
CONFIG_VT_HW_CONSOLE_BINDING=y
# CONFIG_LEGACY_PTYS is not set
-# CONFIG_DEVKMEM is not set
# CONFIG_HW_RANDOM is not set
CONFIG_NTP_PPS=y
CONFIG_PPS_CLIENT_LDISC=m
@@ -520,6 +531,7 @@
CONFIG_TEST_UDELAY=m
CONFIG_TEST_STATIC_KEYS=m
CONFIG_EARLY_PRINTK=y
+CONFIG_ENCRYPTED_KEYS=m
CONFIG_CRYPTO_RSA=m
CONFIG_CRYPTO_DH=m
CONFIG_CRYPTO_ECDH=m
diff --git a/arch/m68k/configs/q40_defconfig b/arch/m68k/configs/q40_defconfig
index e557c9d..5599f3f 100644
--- a/arch/m68k/configs/q40_defconfig
+++ b/arch/m68k/configs/q40_defconfig
@@ -62,6 +62,7 @@
CONFIG_INET_XFRM_MODE_BEET=m
CONFIG_INET_DIAG=m
CONFIG_INET_UDP_DIAG=m
+CONFIG_INET_RAW_DIAG=m
CONFIG_IPV6=m
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_INET6_AH=m
@@ -72,10 +73,10 @@
CONFIG_IPV6_GRE=m
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
CONFIG_NF_CONNTRACK_ZONES=y
# CONFIG_NF_CONNTRACK_PROCFS is not set
# CONFIG_NF_CT_PROTO_DCCP is not set
-CONFIG_NF_CT_PROTO_UDPLITE=m
CONFIG_NF_CONNTRACK_AMANDA=m
CONFIG_NF_CONNTRACK_FTP=m
CONFIG_NF_CONNTRACK_H323=m
@@ -91,6 +92,7 @@
CONFIG_NF_TABLES_NETDEV=m
CONFIG_NFT_EXTHDR=m
CONFIG_NFT_META=m
+CONFIG_NFT_RT=m
CONFIG_NFT_NUMGEN=m
CONFIG_NFT_CT=m
CONFIG_NFT_SET_RBTREE=m
@@ -101,11 +103,13 @@
CONFIG_NFT_MASQ=m
CONFIG_NFT_REDIR=m
CONFIG_NFT_NAT=m
+CONFIG_NFT_OBJREF=m
CONFIG_NFT_QUEUE=m
CONFIG_NFT_QUOTA=m
CONFIG_NFT_REJECT=m
CONFIG_NFT_COMPAT=m
CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB_INET=m
CONFIG_NFT_DUP_NETDEV=m
CONFIG_NFT_FWD_NETDEV=m
CONFIG_NETFILTER_XT_SET=m
@@ -172,6 +176,7 @@
CONFIG_IP_SET_HASH_IPPORT=m
CONFIG_IP_SET_HASH_IPPORTIP=m
CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_IPMAC=m
CONFIG_IP_SET_HASH_MAC=m
CONFIG_IP_SET_HASH_NETPORTNET=m
CONFIG_IP_SET_HASH_NET=m
@@ -180,8 +185,10 @@
CONFIG_IP_SET_HASH_NETIFACE=m
CONFIG_IP_SET_LIST_SET=m
CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_NF_SOCKET_IPV4=m
CONFIG_NFT_CHAIN_ROUTE_IPV4=m
CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
CONFIG_NF_TABLES_ARP=m
CONFIG_NF_LOG_ARP=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
@@ -208,8 +215,10 @@
CONFIG_IP_NF_ARPFILTER=m
CONFIG_IP_NF_ARP_MANGLE=m
CONFIG_NF_CONNTRACK_IPV6=m
+CONFIG_NF_SOCKET_IPV6=m
CONFIG_NFT_CHAIN_ROUTE_IPV6=m
CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV6=m
CONFIG_NFT_MASQ_IPV6=m
CONFIG_NFT_REDIR_IPV6=m
@@ -290,6 +299,7 @@
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_TEST_ASYNC_DRIVER_PROBE=m
CONFIG_CONNECTOR=m
CONFIG_PARPORT=m
CONFIG_PARPORT_PC=m
@@ -356,6 +366,7 @@
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_VETH=m
# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_NET_VENDOR_ALACRITECH is not set
# CONFIG_NET_VENDOR_AMAZON is not set
# CONFIG_NET_VENDOR_AMD is not set
# CONFIG_NET_VENDOR_ARC is not set
@@ -374,6 +385,7 @@
# CONFIG_NET_VENDOR_ROCKER is not set
# CONFIG_NET_VENDOR_SAMSUNG is not set
# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
# CONFIG_NET_VENDOR_SMSC is not set
# CONFIG_NET_VENDOR_STMICRO is not set
# CONFIG_NET_VENDOR_SYNOPSYS is not set
@@ -404,7 +416,6 @@
CONFIG_SERIO_Q40KBD=y
CONFIG_USERIO=m
# CONFIG_LEGACY_PTYS is not set
-# CONFIG_DEVKMEM is not set
CONFIG_PRINTER=m
# CONFIG_HW_RANDOM is not set
CONFIG_NTP_PPS=y
@@ -543,6 +554,7 @@
CONFIG_TEST_UDELAY=m
CONFIG_TEST_STATIC_KEYS=m
CONFIG_EARLY_PRINTK=y
+CONFIG_ENCRYPTED_KEYS=m
CONFIG_CRYPTO_RSA=m
CONFIG_CRYPTO_DH=m
CONFIG_CRYPTO_ECDH=m
diff --git a/arch/m68k/configs/sun3_defconfig b/arch/m68k/configs/sun3_defconfig
index c6a748a..313bf0a 100644
--- a/arch/m68k/configs/sun3_defconfig
+++ b/arch/m68k/configs/sun3_defconfig
@@ -59,6 +59,7 @@
CONFIG_INET_XFRM_MODE_BEET=m
CONFIG_INET_DIAG=m
CONFIG_INET_UDP_DIAG=m
+CONFIG_INET_RAW_DIAG=m
CONFIG_IPV6=m
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_INET6_AH=m
@@ -69,10 +70,10 @@
CONFIG_IPV6_GRE=m
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
CONFIG_NF_CONNTRACK_ZONES=y
# CONFIG_NF_CONNTRACK_PROCFS is not set
# CONFIG_NF_CT_PROTO_DCCP is not set
-CONFIG_NF_CT_PROTO_UDPLITE=m
CONFIG_NF_CONNTRACK_AMANDA=m
CONFIG_NF_CONNTRACK_FTP=m
CONFIG_NF_CONNTRACK_H323=m
@@ -88,6 +89,7 @@
CONFIG_NF_TABLES_NETDEV=m
CONFIG_NFT_EXTHDR=m
CONFIG_NFT_META=m
+CONFIG_NFT_RT=m
CONFIG_NFT_NUMGEN=m
CONFIG_NFT_CT=m
CONFIG_NFT_SET_RBTREE=m
@@ -98,11 +100,13 @@
CONFIG_NFT_MASQ=m
CONFIG_NFT_REDIR=m
CONFIG_NFT_NAT=m
+CONFIG_NFT_OBJREF=m
CONFIG_NFT_QUEUE=m
CONFIG_NFT_QUOTA=m
CONFIG_NFT_REJECT=m
CONFIG_NFT_COMPAT=m
CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB_INET=m
CONFIG_NFT_DUP_NETDEV=m
CONFIG_NFT_FWD_NETDEV=m
CONFIG_NETFILTER_XT_SET=m
@@ -169,6 +173,7 @@
CONFIG_IP_SET_HASH_IPPORT=m
CONFIG_IP_SET_HASH_IPPORTIP=m
CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_IPMAC=m
CONFIG_IP_SET_HASH_MAC=m
CONFIG_IP_SET_HASH_NETPORTNET=m
CONFIG_IP_SET_HASH_NET=m
@@ -177,8 +182,10 @@
CONFIG_IP_SET_HASH_NETIFACE=m
CONFIG_IP_SET_LIST_SET=m
CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_NF_SOCKET_IPV4=m
CONFIG_NFT_CHAIN_ROUTE_IPV4=m
CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
CONFIG_NF_TABLES_ARP=m
CONFIG_NF_LOG_ARP=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
@@ -205,8 +212,10 @@
CONFIG_IP_NF_ARPFILTER=m
CONFIG_IP_NF_ARP_MANGLE=m
CONFIG_NF_CONNTRACK_IPV6=m
+CONFIG_NF_SOCKET_IPV6=m
CONFIG_NFT_CHAIN_ROUTE_IPV6=m
CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV6=m
CONFIG_NFT_MASQ_IPV6=m
CONFIG_NFT_REDIR_IPV6=m
@@ -287,6 +296,7 @@
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_TEST_ASYNC_DRIVER_PROBE=m
CONFIG_CONNECTOR=m
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_CRYPTOLOOP=m
@@ -346,6 +356,7 @@
CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_VETH=m
+# CONFIG_NET_VENDOR_ALACRITECH is not set
# CONFIG_NET_VENDOR_AMAZON is not set
CONFIG_SUN3LANCE=y
# CONFIG_NET_VENDOR_ARC is not set
@@ -361,6 +372,7 @@
# CONFIG_NET_VENDOR_ROCKER is not set
# CONFIG_NET_VENDOR_SAMSUNG is not set
# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
# CONFIG_NET_VENDOR_STMICRO is not set
# CONFIG_NET_VENDOR_SUN is not set
# CONFIG_NET_VENDOR_SYNOPSYS is not set
@@ -388,7 +400,6 @@
CONFIG_MOUSE_SERIAL=m
CONFIG_USERIO=m
# CONFIG_LEGACY_PTYS is not set
-# CONFIG_DEVKMEM is not set
# CONFIG_HW_RANDOM is not set
CONFIG_NTP_PPS=y
CONFIG_PPS_CLIENT_LDISC=m
@@ -521,6 +532,7 @@
CONFIG_TEST_FIRMWARE=m
CONFIG_TEST_UDELAY=m
CONFIG_TEST_STATIC_KEYS=m
+CONFIG_ENCRYPTED_KEYS=m
CONFIG_CRYPTO_RSA=m
CONFIG_CRYPTO_DH=m
CONFIG_CRYPTO_ECDH=m
diff --git a/arch/m68k/configs/sun3x_defconfig b/arch/m68k/configs/sun3x_defconfig
index 10d6085..38b6136 100644
--- a/arch/m68k/configs/sun3x_defconfig
+++ b/arch/m68k/configs/sun3x_defconfig
@@ -59,6 +59,7 @@
CONFIG_INET_XFRM_MODE_BEET=m
CONFIG_INET_DIAG=m
CONFIG_INET_UDP_DIAG=m
+CONFIG_INET_RAW_DIAG=m
CONFIG_IPV6=m
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_INET6_AH=m
@@ -69,10 +70,10 @@
CONFIG_IPV6_GRE=m
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
CONFIG_NF_CONNTRACK_ZONES=y
# CONFIG_NF_CONNTRACK_PROCFS is not set
# CONFIG_NF_CT_PROTO_DCCP is not set
-CONFIG_NF_CT_PROTO_UDPLITE=m
CONFIG_NF_CONNTRACK_AMANDA=m
CONFIG_NF_CONNTRACK_FTP=m
CONFIG_NF_CONNTRACK_H323=m
@@ -88,6 +89,7 @@
CONFIG_NF_TABLES_NETDEV=m
CONFIG_NFT_EXTHDR=m
CONFIG_NFT_META=m
+CONFIG_NFT_RT=m
CONFIG_NFT_NUMGEN=m
CONFIG_NFT_CT=m
CONFIG_NFT_SET_RBTREE=m
@@ -98,11 +100,13 @@
CONFIG_NFT_MASQ=m
CONFIG_NFT_REDIR=m
CONFIG_NFT_NAT=m
+CONFIG_NFT_OBJREF=m
CONFIG_NFT_QUEUE=m
CONFIG_NFT_QUOTA=m
CONFIG_NFT_REJECT=m
CONFIG_NFT_COMPAT=m
CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB_INET=m
CONFIG_NFT_DUP_NETDEV=m
CONFIG_NFT_FWD_NETDEV=m
CONFIG_NETFILTER_XT_SET=m
@@ -169,6 +173,7 @@
CONFIG_IP_SET_HASH_IPPORT=m
CONFIG_IP_SET_HASH_IPPORTIP=m
CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_IPMAC=m
CONFIG_IP_SET_HASH_MAC=m
CONFIG_IP_SET_HASH_NETPORTNET=m
CONFIG_IP_SET_HASH_NET=m
@@ -177,8 +182,10 @@
CONFIG_IP_SET_HASH_NETIFACE=m
CONFIG_IP_SET_LIST_SET=m
CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_NF_SOCKET_IPV4=m
CONFIG_NFT_CHAIN_ROUTE_IPV4=m
CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
CONFIG_NF_TABLES_ARP=m
CONFIG_NF_LOG_ARP=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
@@ -205,8 +212,10 @@
CONFIG_IP_NF_ARPFILTER=m
CONFIG_IP_NF_ARP_MANGLE=m
CONFIG_NF_CONNTRACK_IPV6=m
+CONFIG_NF_SOCKET_IPV6=m
CONFIG_NFT_CHAIN_ROUTE_IPV6=m
CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV6=m
CONFIG_NFT_MASQ_IPV6=m
CONFIG_NFT_REDIR_IPV6=m
@@ -287,6 +296,7 @@
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_TEST_ASYNC_DRIVER_PROBE=m
CONFIG_CONNECTOR=m
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_CRYPTOLOOP=m
@@ -346,6 +356,7 @@
CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_VETH=m
+# CONFIG_NET_VENDOR_ALACRITECH is not set
# CONFIG_NET_VENDOR_AMAZON is not set
CONFIG_SUN3LANCE=y
# CONFIG_NET_VENDOR_ARC is not set
@@ -362,6 +373,7 @@
# CONFIG_NET_VENDOR_ROCKER is not set
# CONFIG_NET_VENDOR_SAMSUNG is not set
# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
# CONFIG_NET_VENDOR_STMICRO is not set
# CONFIG_NET_VENDOR_SYNOPSYS is not set
# CONFIG_NET_VENDOR_VIA is not set
@@ -388,7 +400,6 @@
CONFIG_MOUSE_SERIAL=m
CONFIG_USERIO=m
# CONFIG_LEGACY_PTYS is not set
-# CONFIG_DEVKMEM is not set
# CONFIG_HW_RANDOM is not set
CONFIG_NTP_PPS=y
CONFIG_PPS_CLIENT_LDISC=m
@@ -522,6 +533,7 @@
CONFIG_TEST_UDELAY=m
CONFIG_TEST_STATIC_KEYS=m
CONFIG_EARLY_PRINTK=y
+CONFIG_ENCRYPTED_KEYS=m
CONFIG_CRYPTO_RSA=m
CONFIG_CRYPTO_DH=m
CONFIG_CRYPTO_ECDH=m
diff --git a/arch/m68k/include/asm/Kbuild b/arch/m68k/include/asm/Kbuild
index 1f2e5d3..6c76d6c 100644
--- a/arch/m68k/include/asm/Kbuild
+++ b/arch/m68k/include/asm/Kbuild
@@ -1,7 +1,6 @@
generic-y += barrier.h
generic-y += bitsperlong.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += device.h
generic-y += emergency-restart.h
generic-y += errno.h
diff --git a/arch/m68k/include/asm/bug.h b/arch/m68k/include/asm/bug.h
index ef9a2e4..5bc8d91d 100644
--- a/arch/m68k/include/asm/bug.h
+++ b/arch/m68k/include/asm/bug.h
@@ -6,12 +6,12 @@
#ifdef CONFIG_DEBUG_BUGVERBOSE
#ifndef CONFIG_SUN3
#define BUG() do { \
- printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \
+ pr_crit("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \
__builtin_trap(); \
} while (0)
#else
#define BUG() do { \
- printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \
+ pr_crit("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \
panic("BUG!"); \
} while (0)
#endif
diff --git a/arch/m68k/include/asm/floppy.h b/arch/m68k/include/asm/floppy.h
index 47365b1..c3b9ad6 100644
--- a/arch/m68k/include/asm/floppy.h
+++ b/arch/m68k/include/asm/floppy.h
@@ -234,9 +234,9 @@
virtual_dma_residue += virtual_dma_count;
virtual_dma_count=0;
#ifdef TRACE_FLPY_INT
- printk("count=%x, residue=%x calls=%d bytes=%d dma_wait=%d\n",
- virtual_dma_count, virtual_dma_residue, calls, bytes,
- dma_wait);
+ pr_info("count=%x, residue=%x calls=%d bytes=%d dma_wait=%d\n",
+ virtual_dma_count, virtual_dma_residue, calls, bytes,
+ dma_wait);
calls = 0;
dma_wait=0;
#endif
diff --git a/arch/m68k/include/asm/macints.h b/arch/m68k/include/asm/macints.h
index 92aa8a4..cddb2d3 100644
--- a/arch/m68k/include/asm/macints.h
+++ b/arch/m68k/include/asm/macints.h
@@ -14,22 +14,6 @@
#include <asm/irq.h>
-/* Setting this prints debugging info for unclaimed interrupts */
-
-#define DEBUG_SPURIOUS
-
-/* Setting this prints debugging info on each autovector interrupt */
-
-/* #define DEBUG_IRQS */
-
-/* Setting this prints debugging info on each Nubus interrupt */
-
-/* #define DEBUG_NUBUS_INT */
-
-/* Setting this prints debugging info on irqs as they enabled and disabled. */
-
-/* #define DEBUG_IRQUSE */
-
/*
* Base IRQ number for all Mac68K interrupt sources. Each source
* has eight indexes (base -> base+7).
diff --git a/arch/m68k/include/asm/math-emu.h b/arch/m68k/include/asm/math-emu.h
index 5e9249b..b062696 100644
--- a/arch/m68k/include/asm/math-emu.h
+++ b/arch/m68k/include/asm/math-emu.h
@@ -105,21 +105,21 @@
#ifdef FPU_EMU_DEBUG
extern unsigned int fp_debugprint;
-#define dprint(bit, fmt, args...) ({ \
+#define dprint(bit, fmt, ...) ({ \
if (fp_debugprint & (1 << (bit))) \
- printk(fmt, ## args); \
+ pr_info(fmt, ##__VA_ARGS__); \
})
#else
-#define dprint(bit, fmt, args...)
+#define dprint(bit, fmt, ...) no_printk(fmt, ##__VA_ARGS__)
#endif
#define uprint(str) ({ \
static int __count = 3; \
\
if (__count > 0) { \
- printk("You just hit an unimplemented " \
+ pr_err("You just hit an unimplemented " \
"fpu instruction (%s)\n", str); \
- printk("Please report this to ....\n"); \
+ pr_err("Please report this to ....\n"); \
__count--; \
} \
})
diff --git a/arch/m68k/include/asm/sun3_pgtable.h b/arch/m68k/include/asm/sun3_pgtable.h
index 48657f9..d5104a7 100644
--- a/arch/m68k/include/asm/sun3_pgtable.h
+++ b/arch/m68k/include/asm/sun3_pgtable.h
@@ -151,11 +151,11 @@
#define pte_ERROR(e) \
- printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e))
+ pr_err("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e))
#define pmd_ERROR(e) \
- printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e))
+ pr_err("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e))
#define pgd_ERROR(e) \
- printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e))
+ pr_err("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e))
/*
diff --git a/arch/m68k/include/asm/sun3xflop.h b/arch/m68k/include/asm/sun3xflop.h
index a02ea3a..159269b 100644
--- a/arch/m68k/include/asm/sun3xflop.h
+++ b/arch/m68k/include/asm/sun3xflop.h
@@ -48,7 +48,7 @@
// udelay(5);
switch(port & 7) {
default:
- printk("floppy: Asked to read unknown port %d\n", port);
+ pr_crit("floppy: Asked to read unknown port %d\n", port);
panic("floppy: Port bolixed.");
case 4: /* FD_STATUS */
return (*sun3x_fdc.status_r) & ~STATUS_DMA;
@@ -70,7 +70,7 @@
// udelay(5);
switch(port & 7) {
default:
- printk("floppy: Asked to write to unknown port %d\n", port);
+ pr_crit("floppy: Asked to write to unknown port %d\n", port);
panic("floppy: Port bolixed.");
case 2: /* FD_DOR */
/* Oh geese, 82072 on the Sun has no DOR register,
@@ -127,7 +127,7 @@
return IRQ_HANDLED;
}
-// printk("doing pdma\n");// st %x\n", sun_fdc->status_82072);
+// pr_info("doing pdma\n");// st %x\n", sun_fdc->status_82072);
#ifdef TRACE_FLPY_INT
if(!calls)
@@ -171,7 +171,7 @@
#ifdef TRACE_FLPY_INT
calls++;
#endif
-// printk("st=%02x\n", st);
+// pr_info("st=%02x\n", st);
if(st == 0x20)
return IRQ_HANDLED;
if(!(st & 0x20)) {
@@ -180,9 +180,9 @@
doing_pdma = 0;
#ifdef TRACE_FLPY_INT
- printk("count=%x, residue=%x calls=%d bytes=%x dma_wait=%d\n",
- virtual_dma_count, virtual_dma_residue, calls, bytes,
- dma_wait);
+ pr_info("count=%x, residue=%x calls=%d bytes=%x dma_wait=%d\n",
+ virtual_dma_count, virtual_dma_residue, calls, bytes,
+ dma_wait);
calls = 0;
dma_wait=0;
#endif
diff --git a/arch/m68k/kernel/dma.c b/arch/m68k/kernel/dma.c
index 0707006..1e4f386 100644
--- a/arch/m68k/kernel/dma.c
+++ b/arch/m68k/kernel/dma.c
@@ -110,8 +110,8 @@
cache_clear(handle, size);
break;
default:
- if (printk_ratelimit())
- printk("dma_sync_single_for_device: unsupported dir %u\n", dir);
+ pr_err_ratelimited("dma_sync_single_for_device: unsupported dir %u\n",
+ dir);
break;
}
}
diff --git a/arch/m68k/kernel/module.c b/arch/m68k/kernel/module.c
index eb46fd6..aaac2da 100644
--- a/arch/m68k/kernel/module.c
+++ b/arch/m68k/kernel/module.c
@@ -12,9 +12,9 @@
#include <linux/kernel.h>
#if 0
-#define DEBUGP printk
+#define DEBUGP(fmt, ...) printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#else
-#define DEBUGP(fmt...)
+#define DEBUGP(fmt, ...) no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif
#ifdef CONFIG_MODULES
@@ -51,8 +51,8 @@
*location += sym->st_value - (uint32_t)location;
break;
default:
- printk(KERN_ERR "module %s: Unknown relocation: %u\n",
- me->name, ELF32_R_TYPE(rel[i].r_info));
+ pr_err("module %s: Unknown relocation: %u\n", me->name,
+ ELF32_R_TYPE(rel[i].r_info));
return -ENOEXEC;
}
}
@@ -91,8 +91,8 @@
*location = rel[i].r_addend + sym->st_value - (uint32_t)location;
break;
default:
- printk(KERN_ERR "module %s: Unknown relocation: %u\n",
- me->name, ELF32_R_TYPE(rel[i].r_info));
+ pr_err("module %s: Unknown relocation: %u\n", me->name,
+ ELF32_R_TYPE(rel[i].r_info));
return -ENOEXEC;
}
}
diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c
index aaf28f8..f0a8e9b 100644
--- a/arch/m68k/kernel/process.c
+++ b/arch/m68k/kernel/process.c
@@ -87,17 +87,17 @@
void show_regs(struct pt_regs * regs)
{
- printk("\n");
- printk("Format %02x Vector: %04x PC: %08lx Status: %04x %s\n",
- regs->format, regs->vector, regs->pc, regs->sr, print_tainted());
- printk("ORIG_D0: %08lx D0: %08lx A2: %08lx A1: %08lx\n",
- regs->orig_d0, regs->d0, regs->a2, regs->a1);
- printk("A0: %08lx D5: %08lx D4: %08lx\n",
- regs->a0, regs->d5, regs->d4);
- printk("D3: %08lx D2: %08lx D1: %08lx\n",
- regs->d3, regs->d2, regs->d1);
+ pr_info("Format %02x Vector: %04x PC: %08lx Status: %04x %s\n",
+ regs->format, regs->vector, regs->pc, regs->sr,
+ print_tainted());
+ pr_info("ORIG_D0: %08lx D0: %08lx A2: %08lx A1: %08lx\n",
+ regs->orig_d0, regs->d0, regs->a2, regs->a1);
+ pr_info("A0: %08lx D5: %08lx D4: %08lx\n", regs->a0, regs->d5,
+ regs->d4);
+ pr_info("D3: %08lx D2: %08lx D1: %08lx\n", regs->d3, regs->d2,
+ regs->d1);
if (!(regs->sr & PS_S))
- printk("USP: %08lx\n", rdusp());
+ pr_info("USP: %08lx\n", rdusp());
}
void flush_thread(void)
diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c
index 8ead291..093b7c4 100644
--- a/arch/m68k/kernel/signal.c
+++ b/arch/m68k/kernel/signal.c
@@ -598,9 +598,7 @@
/*
* user process trying to return with weird frame format
*/
-#ifdef DEBUG
- printk("user process returning with weird frame format\n");
-#endif
+ pr_debug("user process returning with weird frame format\n");
return 1;
}
if (!fsize) {
@@ -846,10 +844,8 @@
int err = 0, sig = ksig->sig;
if (fsize < 0) {
-#ifdef DEBUG
- printk ("setup_frame: Unknown frame format %#x\n",
- regs->format);
-#endif
+ pr_debug("setup_frame: Unknown frame format %#x\n",
+ regs->format);
return -EFAULT;
}
@@ -905,9 +901,7 @@
if (regs->stkadj) {
struct pt_regs *tregs =
(struct pt_regs *)((ulong)regs + regs->stkadj);
-#ifdef DEBUG
- printk("Performing stackadjust=%04x\n", regs->stkadj);
-#endif
+ pr_debug("Performing stackadjust=%04lx\n", regs->stkadj);
/* This must be copied with decreasing addresses to
handle overlaps. */
tregs->vector = 0;
@@ -926,10 +920,8 @@
int err = 0, sig = ksig->sig;
if (fsize < 0) {
-#ifdef DEBUG
- printk ("setup_frame: Unknown frame format %#x\n",
- regs->format);
-#endif
+ pr_debug("setup_frame: Unknown frame format %#x\n",
+ regs->format);
return -EFAULT;
}
@@ -993,9 +985,7 @@
if (regs->stkadj) {
struct pt_regs *tregs =
(struct pt_regs *)((ulong)regs + regs->stkadj);
-#ifdef DEBUG
- printk("Performing stackadjust=%04x\n", regs->stkadj);
-#endif
+ pr_debug("Performing stackadjust=%04lx\n", regs->stkadj);
/* This must be copied with decreasing addresses to
handle overlaps. */
tregs->vector = 0;
diff --git a/arch/m68k/kernel/sys_m68k.c b/arch/m68k/kernel/sys_m68k.c
index 98a2daa..933e481 100644
--- a/arch/m68k/kernel/sys_m68k.c
+++ b/arch/m68k/kernel/sys_m68k.c
@@ -398,7 +398,6 @@
* Verify that the specified address region actually belongs
* to this process.
*/
- ret = -EINVAL;
down_read(¤t->mm->mmap_sem);
vma = find_vma(current->mm, addr);
if (!vma || addr < vma->vm_start || addr + len > vma->vm_end)
diff --git a/arch/m68k/kernel/uboot.c b/arch/m68k/kernel/uboot.c
index b3536a8..b29c3b2 100644
--- a/arch/m68k/kernel/uboot.c
+++ b/arch/m68k/kernel/uboot.c
@@ -83,8 +83,7 @@
initrd_start = uboot_initrd_start;
initrd_end = uboot_initrd_end;
ROOT_DEV = Root_RAM0;
- printk(KERN_INFO "initrd at 0x%lx:0x%lx\n",
- initrd_start, initrd_end);
+ pr_info("initrd at 0x%lx:0x%lx\n", initrd_start, initrd_end);
}
#endif /* if defined(CONFIG_BLK_DEV_INITRD) */
}
diff --git a/arch/m68k/mac/baboon.c b/arch/m68k/mac/baboon.c
index f6f7d42..514acde 100644
--- a/arch/m68k/mac/baboon.c
+++ b/arch/m68k/mac/baboon.c
@@ -14,8 +14,6 @@
#include <asm/macints.h>
#include <asm/mac_baboon.h>
-/* #define DEBUG_IRQS */
-
int baboon_present;
static volatile struct baboon *baboon;
@@ -50,12 +48,6 @@
int irq_bit, irq_num;
unsigned char events;
-#ifdef DEBUG_IRQS
- printk("baboon_irq: mb_control %02X mb_ifr %02X mb_status %02X\n",
- (uint) baboon->mb_control, (uint) baboon->mb_ifr,
- (uint) baboon->mb_status);
-#endif
-
events = baboon->mb_ifr & 0x07;
if (!events)
return;
@@ -97,18 +89,10 @@
void baboon_irq_enable(int irq)
{
-#ifdef DEBUG_IRQUSE
- printk("baboon_irq_enable(%d)\n", irq);
-#endif
-
mac_irq_enable(irq_get_irq_data(IRQ_NUBUS_C));
}
void baboon_irq_disable(int irq)
{
-#ifdef DEBUG_IRQUSE
- printk("baboon_irq_disable(%d)\n", irq);
-#endif
-
mac_irq_disable(irq_get_irq_data(IRQ_NUBUS_C));
}
diff --git a/arch/m68k/mac/macints.c b/arch/m68k/mac/macints.c
index 9f98c08..b5cd06d 100644
--- a/arch/m68k/mac/macints.c
+++ b/arch/m68k/mac/macints.c
@@ -125,16 +125,9 @@
#include <asm/hwtest.h>
#include <asm/irq_regs.h>
-#define SHUTUP_SONIC
-
-/*
- * console_loglevel determines NMI handler function
- */
+extern void show_registers(struct pt_regs *);
irqreturn_t mac_nmi_handler(int, void *);
-irqreturn_t mac_debug_handler(int, void *);
-
-/* #define DEBUG_MACINTS */
static unsigned int mac_irq_startup(struct irq_data *);
static void mac_irq_shutdown(struct irq_data *);
@@ -149,21 +142,8 @@
void __init mac_init_IRQ(void)
{
-#ifdef DEBUG_MACINTS
- printk("mac_init_IRQ(): Setting things up...\n");
-#endif
m68k_setup_irq_controller(&mac_irq_chip, handle_simple_irq, IRQ_USER,
NUM_MAC_SOURCES - IRQ_USER);
- /* Make sure the SONIC interrupt is cleared or things get ugly */
-#ifdef SHUTUP_SONIC
- printk("Killing onboard sonic... ");
- /* This address should hopefully be mapped already */
- if (hwreg_present((void*)(0x50f0a000))) {
- *(long *)(0x50f0a014) = 0x7fffL;
- *(long *)(0x50f0a010) = 0L;
- }
- printk("Done.\n");
-#endif /* SHUTUP_SONIC */
/*
* Now register the handlers for the master IRQ handlers
@@ -182,9 +162,6 @@
if (request_irq(IRQ_AUTO_7, mac_nmi_handler, 0, "NMI",
mac_nmi_handler))
pr_err("Couldn't register NMI\n");
-#ifdef DEBUG_MACINTS
- printk("mac_init_IRQ(): Done!\n");
-#endif
}
/*
@@ -276,65 +253,17 @@
mac_irq_disable(data);
}
-static int num_debug[8];
-
-irqreturn_t mac_debug_handler(int irq, void *dev_id)
-{
- if (num_debug[irq] < 10) {
- printk("DEBUG: Unexpected IRQ %d\n", irq);
- num_debug[irq]++;
- }
- return IRQ_HANDLED;
-}
-
-static int in_nmi;
-static volatile int nmi_hold;
+static volatile int in_nmi;
irqreturn_t mac_nmi_handler(int irq, void *dev_id)
{
- int i;
- /*
- * generate debug output on NMI switch if 'debug' kernel option given
- * (only works with Penguin!)
- */
+ if (in_nmi)
+ return IRQ_HANDLED;
+ in_nmi = 1;
- in_nmi++;
- for (i=0; i<100; i++)
- udelay(1000);
+ pr_info("Non-Maskable Interrupt\n");
+ show_registers(get_irq_regs());
- if (in_nmi == 1) {
- nmi_hold = 1;
- printk("... pausing, press NMI to resume ...");
- } else {
- printk(" ok!\n");
- nmi_hold = 0;
- }
-
- barrier();
-
- while (nmi_hold == 1)
- udelay(1000);
-
- if (console_loglevel >= 8) {
-#if 0
- struct pt_regs *fp = get_irq_regs();
- show_state();
- printk("PC: %08lx\nSR: %04x SP: %p\n", fp->pc, fp->sr, fp);
- printk("d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n",
- fp->d0, fp->d1, fp->d2, fp->d3);
- printk("d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n",
- fp->d4, fp->d5, fp->a0, fp->a1);
-
- if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page)
- printk("Corrupted stack page\n");
- printk("Process %s (pid: %d, stackpage=%08lx)\n",
- current->comm, current->pid, current->kernel_stack_page);
- if (intr_count == 1)
- dump_stack((struct frame *)fp);
-#else
- /* printk("NMI "); */
-#endif
- }
- in_nmi--;
+ in_nmi = 0;
return IRQ_HANDLED;
}
diff --git a/arch/m68k/mac/misc.c b/arch/m68k/mac/misc.c
index c6d351f..3b1f7a6 100644
--- a/arch/m68k/mac/misc.c
+++ b/arch/m68k/mac/misc.c
@@ -4,7 +4,6 @@
#include <linux/types.h>
#include <linux/errno.h>
-#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/sched.h>
diff --git a/arch/m68k/mac/oss.c b/arch/m68k/mac/oss.c
index 55d65927..ca84dcf 100644
--- a/arch/m68k/mac/oss.c
+++ b/arch/m68k/mac/oss.c
@@ -68,15 +68,6 @@
int events = oss->irq_pending &
(OSS_IP_IOPSCC | OSS_IP_SCSI | OSS_IP_IOPISM);
-#ifdef DEBUG_IRQS
- if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) {
- unsigned int irq = irq_desc_get_irq(desc);
-
- printk("oss_irq: irq %u events = 0x%04X\n", irq,
- (int) oss->irq_pending);
- }
-#endif
-
if (events & OSS_IP_IOPSCC) {
oss->irq_pending &= ~OSS_IP_IOPSCC;
generic_handle_irq(IRQ_MAC_SCC);
@@ -107,11 +98,6 @@
if (!events)
return;
-#ifdef DEBUG_NUBUS_INT
- if (console_loglevel > 7) {
- printk("oss_nubus_irq: events = 0x%04X\n", events);
- }
-#endif
/* There are only six slots on the OSS, not seven */
i = 6;
@@ -163,9 +149,6 @@
*/
void oss_irq_enable(int irq) {
-#ifdef DEBUG_IRQUSE
- printk("oss_irq_enable(%d)\n", irq);
-#endif
switch(irq) {
case IRQ_MAC_SCC:
oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC;
@@ -199,9 +182,6 @@
*/
void oss_irq_disable(int irq) {
-#ifdef DEBUG_IRQUSE
- printk("oss_irq_disable(%d)\n", irq);
-#endif
switch(irq) {
case IRQ_MAC_SCC:
oss->irq_level[OSS_IOPSCC] = 0;
diff --git a/arch/m68k/mac/psc.c b/arch/m68k/mac/psc.c
index cb2b1a3..439a2a2 100644
--- a/arch/m68k/mac/psc.c
+++ b/arch/m68k/mac/psc.c
@@ -122,11 +122,6 @@
int irq_num;
unsigned char irq_bit, events;
-#ifdef DEBUG_IRQS
- printk("psc_irq: irq %u pIFR = 0x%02X pIER = 0x%02X\n",
- irq, (int) psc_read_byte(pIFR), (int) psc_read_byte(pIER));
-#endif
-
events = psc_read_byte(pIFR) & psc_read_byte(pIER) & 0xF;
if (!events)
return;
@@ -160,9 +155,6 @@
int irq_idx = IRQ_IDX(irq);
int pIER = pIERbase + (irq_src << 4);
-#ifdef DEBUG_IRQUSE
- printk("psc_irq_enable(%d)\n", irq);
-#endif
psc_write_byte(pIER, (1 << irq_idx) | 0x80);
}
@@ -171,8 +163,5 @@
int irq_idx = IRQ_IDX(irq);
int pIER = pIERbase + (irq_src << 4);
-#ifdef DEBUG_IRQUSE
- printk("psc_irq_disable(%d)\n", irq);
-#endif
psc_write_byte(pIER, 1 << irq_idx);
}
diff --git a/arch/m68k/mac/via.c b/arch/m68k/mac/via.c
index 920ff63..16629e9 100644
--- a/arch/m68k/mac/via.c
+++ b/arch/m68k/mac/via.c
@@ -550,10 +550,6 @@
int irq_src = IRQ_SRC(irq);
int irq_idx = IRQ_IDX(irq);
-#ifdef DEBUG_IRQUSE
- printk(KERN_DEBUG "via_irq_enable(%d)\n", irq);
-#endif
-
if (irq_src == 1) {
via1[vIER] = IER_SET_BIT(irq_idx);
} else if (irq_src == 2) {
@@ -582,10 +578,6 @@
int irq_src = IRQ_SRC(irq);
int irq_idx = IRQ_IDX(irq);
-#ifdef DEBUG_IRQUSE
- printk(KERN_DEBUG "via_irq_disable(%d)\n", irq);
-#endif
-
if (irq_src == 1) {
via1[vIER] = IER_CLR_BIT(irq_idx);
} else if (irq_src == 2) {
diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c
index 9c1e656..a6ffead 100644
--- a/arch/m68k/mm/init.c
+++ b/arch/m68k/mm/init.c
@@ -66,7 +66,7 @@
end = (unsigned long)phys_to_virt(info->addr + info->size - 1) >> __virt_to_node_shift();
for (; i <= end; i++) {
if (pg_data_table[i])
- printk("overlap at %u for chunk %u\n", i, node);
+ pr_warn("overlap at %u for chunk %u\n", i, node);
pg_data_table[i] = pg_data_map + node;
}
#endif
diff --git a/arch/m68k/mm/memory.c b/arch/m68k/mm/memory.c
index 51bc9d25..4902b68 100644
--- a/arch/m68k/mm/memory.c
+++ b/arch/m68k/mm/memory.c
@@ -47,9 +47,7 @@
}
PD_MARKBITS(dp) &= ~mask;
-#ifdef DEBUG
- printk("init_pointer_table: %lx, %x\n", ptable, PD_MARKBITS(dp));
-#endif
+ pr_debug("init_pointer_table: %lx, %x\n", ptable, PD_MARKBITS(dp));
/* unreserve the page so it's possible to free that page */
PD_PAGE(dp)->flags &= ~(1 << PG_reserved);
diff --git a/arch/m68k/mm/sun3kmap.c b/arch/m68k/mm/sun3kmap.c
index 3dc41158..ae03555 100644
--- a/arch/m68k/mm/sun3kmap.c
+++ b/arch/m68k/mm/sun3kmap.c
@@ -40,6 +40,7 @@
sun3_put_pte(virt, pte);
#ifdef SUN3_KMAP_DEBUG
+ pr_info("mapin:");
print_pte_vaddr(virt);
#endif
@@ -80,8 +81,8 @@
return NULL;
#ifdef SUN3_KMAP_DEBUG
- printk("ioremap: got virt %p size %lx(%lx)\n",
- area->addr, size, area->size);
+ pr_info("ioremap: got virt %p size %lx(%lx)\n", area->addr, size,
+ area->size);
#endif
pages = size / PAGE_SIZE;
diff --git a/arch/m68k/mm/sun3mmu.c b/arch/m68k/mm/sun3mmu.c
index b5b7d53..177d776 100644
--- a/arch/m68k/mm/sun3mmu.c
+++ b/arch/m68k/mm/sun3mmu.c
@@ -44,9 +44,6 @@
unsigned long zones_size[MAX_NR_ZONES] = { 0, };
unsigned long size;
-#ifdef TEST_VERIFY_AREA
- wp_works_ok = 0;
-#endif
empty_zero_page = alloc_bootmem_pages(PAGE_SIZE);
address = PAGE_OFFSET;
diff --git a/arch/m68k/mvme147/config.c b/arch/m68k/mvme147/config.c
index c11d38d..8778612 100644
--- a/arch/m68k/mvme147/config.c
+++ b/arch/m68k/mvme147/config.c
@@ -63,7 +63,7 @@
void mvme147_reset(void)
{
- printk ("\r\n\nCalled mvme147_reset\r\n");
+ pr_info("\r\n\nCalled mvme147_reset\r\n");
m147_pcc->watchdog = 0x0a; /* Clear timer */
m147_pcc->watchdog = 0xa5; /* Enable watchdog - 100ms to reset */
while (1)
diff --git a/arch/m68k/mvme16x/config.c b/arch/m68k/mvme16x/config.c
index 58e2409..6fa06d4 100644
--- a/arch/m68k/mvme16x/config.c
+++ b/arch/m68k/mvme16x/config.c
@@ -72,8 +72,8 @@
void mvme16x_reset(void)
{
- printk ("\r\n\nCalled mvme16x_reset\r\n"
- "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r");
+ pr_info("\r\n\nCalled mvme16x_reset\r\n"
+ "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r");
/* The string of returns is to delay the reset until the whole
* message is output. Assert reset bit in GCSR */
*(volatile char *)0xfff40107 = 0x80;
@@ -289,7 +289,7 @@
if (strncmp("BDID", p->bdid, 4))
{
- printk ("\n\nBug call .BRD_ID returned garbage - giving up\n\n");
+ pr_crit("Bug call .BRD_ID returned garbage - giving up\n");
while (1)
;
}
@@ -298,25 +298,25 @@
vme_brdtype = brdno;
mvme16x_get_model(id);
- printk ("\nBRD_ID: %s BUG %x.%x %02x/%02x/%02x\n", id, p->rev>>4,
- p->rev&0xf, p->yr, p->mth, p->day);
+ pr_info("BRD_ID: %s BUG %x.%x %02x/%02x/%02x\n", id, p->rev >> 4,
+ p->rev & 0xf, p->yr, p->mth, p->day);
if (brdno == 0x0162 || brdno == 0x172)
{
unsigned char rev = *(unsigned char *)MVME162_VERSION_REG;
mvme16x_config = rev | MVME16x_CONFIG_GOT_SCCA;
- printk ("MVME%x Hardware status:\n", brdno);
- printk (" CPU Type 68%s040\n",
- rev & MVME16x_CONFIG_GOT_FPU ? "" : "LC");
- printk (" CPU clock %dMHz\n",
- rev & MVME16x_CONFIG_SPEED_32 ? 32 : 25);
- printk (" VMEchip2 %spresent\n",
- rev & MVME16x_CONFIG_NO_VMECHIP2 ? "NOT " : "");
- printk (" SCSI interface %spresent\n",
- rev & MVME16x_CONFIG_NO_SCSICHIP ? "NOT " : "");
- printk (" Ethernet interface %spresent\n",
- rev & MVME16x_CONFIG_NO_ETHERNET ? "NOT " : "");
+ pr_info("MVME%x Hardware status:\n", brdno);
+ pr_info(" CPU Type 68%s040\n",
+ rev & MVME16x_CONFIG_GOT_FPU ? "" : "LC");
+ pr_info(" CPU clock %dMHz\n",
+ rev & MVME16x_CONFIG_SPEED_32 ? 32 : 25);
+ pr_info(" VMEchip2 %spresent\n",
+ rev & MVME16x_CONFIG_NO_VMECHIP2 ? "NOT " : "");
+ pr_info(" SCSI interface %spresent\n",
+ rev & MVME16x_CONFIG_NO_SCSICHIP ? "NOT " : "");
+ pr_info(" Ethernet interface %spresent\n",
+ rev & MVME16x_CONFIG_NO_ETHERNET ? "NOT " : "");
}
else
{
diff --git a/arch/m68k/mvme16x/rtc.c b/arch/m68k/mvme16x/rtc.c
index 8f00847..7b24577 100644
--- a/arch/m68k/mvme16x/rtc.c
+++ b/arch/m68k/mvme16x/rtc.c
@@ -158,7 +158,7 @@
if (!MACH_IS_MVME16x)
return -ENODEV;
- printk(KERN_INFO "MK48T08 Real Time Clock Driver v%s\n", RTC_VERSION);
+ pr_info("MK48T08 Real Time Clock Driver v%s\n", RTC_VERSION);
return misc_register(&rtc_dev);
}
device_initcall(rtc_MK48T08_init);
diff --git a/arch/m68k/q40/config.c b/arch/m68k/q40/config.c
index ea89a24..71c0867 100644
--- a/arch/m68k/q40/config.c
+++ b/arch/m68k/q40/config.c
@@ -84,7 +84,7 @@
{
/* useful for early debugging stages - writes kernel messages into SRAM */
if (MACH_IS_Q40 && !strncmp(arg, "mem", 3)) {
- /*printk("using NVRAM debug, q40_mem_cptr=%p\n",q40_mem_cptr);*/
+ /*pr_info("using NVRAM debug, q40_mem_cptr=%p\n",q40_mem_cptr);*/
_cpleft = 2000 - ((long)q40_mem_cptr-0xff020000) / 4;
register_console(&q40_console_driver);
}
@@ -124,8 +124,8 @@
static void q40_reset(void)
{
- halted = 1;
- printk("\n\n*******************************************\n"
+ halted = 1;
+ pr_info("*******************************************\n"
"Called q40_reset : press the RESET button!!\n"
"*******************************************\n");
Q40_LED_ON();
@@ -135,10 +135,10 @@
static void q40_halt(void)
{
- halted = 1;
- printk("\n\n*******************\n"
- " Called q40_halt\n"
- "*******************\n");
+ halted = 1;
+ pr_info("*******************\n"
+ " Called q40_halt\n"
+ "*******************\n");
Q40_LED_ON();
while (1)
;
diff --git a/arch/m68k/q40/q40ints.c b/arch/m68k/q40/q40ints.c
index 513f9bb..3e76032 100644
--- a/arch/m68k/q40/q40ints.c
+++ b/arch/m68k/q40/q40ints.c
@@ -48,7 +48,8 @@
switch (irq) {
case 1: case 2: case 8: case 9:
case 11: case 12: case 13:
- printk("%s: ISA IRQ %d not implemented by HW\n", __func__, irq);
+ pr_warn("%s: ISA IRQ %d not implemented by HW\n", __func__,
+ irq);
/* FIXME return -ENXIO; */
}
return 0;
@@ -250,7 +251,7 @@
disable_irq(irq);
disabled = 1;
#else
- /*printk("IRQ_INPROGRESS detected for irq %d, disabling - %s disabled\n",
+ /*pr_warn("IRQ_INPROGRESS detected for irq %d, disabling - %s disabled\n",
irq, disabled ? "already" : "not yet"); */
fp->sr = (((fp->sr) & (~0x700))+0x200);
disabled = 1;
@@ -273,7 +274,7 @@
}
#else
disabled = 0;
- /*printk("reenabling irq %d\n", irq); */
+ /*pr_info("reenabling irq %d\n", irq); */
#endif
}
// used to do 'goto repeat;' here, this delayed bh processing too long
@@ -281,7 +282,8 @@
}
}
if (mer && ccleirq > 0 && !aliased_irq) {
- printk("ISA interrupt from unknown source? EIRQ_REG = %x\n",mer);
+ pr_warn("ISA interrupt from unknown source? EIRQ_REG = %x\n",
+ mer);
ccleirq--;
}
}
@@ -301,7 +303,7 @@
if (irq >= 5 && irq <= 15) {
mext_disabled--;
if (mext_disabled > 0)
- printk("q40_irq_enable : nested disable/enable\n");
+ pr_warn("q40_irq_enable : nested disable/enable\n");
if (mext_disabled == 0)
master_outb(1, EXT_ENABLE_REG);
}
@@ -321,6 +323,7 @@
master_outb(0, EXT_ENABLE_REG);
mext_disabled++;
if (mext_disabled > 1)
- printk("disable_irq nesting count %d\n",mext_disabled);
+ pr_info("disable_irq nesting count %d\n",
+ mext_disabled);
}
}
diff --git a/arch/m68k/sun3/config.c b/arch/m68k/sun3/config.c
index 3af34fa..1d28d38 100644
--- a/arch/m68k/sun3/config.c
+++ b/arch/m68k/sun3/config.c
@@ -134,7 +134,7 @@
{
unsigned long memory_start, memory_end;
- printk("ARCH: SUN3\n");
+ pr_info("ARCH: SUN3\n");
idprom_init();
/* Subtract kernel memory from available memory */
diff --git a/arch/m68k/sun3/dvma.c b/arch/m68k/sun3/dvma.c
index d95506e..ca02ee2 100644
--- a/arch/m68k/sun3/dvma.c
+++ b/arch/m68k/sun3/dvma.c
@@ -31,8 +31,7 @@
ptep = pfn_pte(virt_to_pfn(kaddr), PAGE_KERNEL);
pte = pte_val(ptep);
-// printk("dvma_remap: addr %lx -> %lx pte %08lx len %x\n",
-// kaddr, vaddr, pte, len);
+// pr_info("dvma_remap: addr %lx -> %lx pte %08lx\n", kaddr, vaddr, pte);
if(ptelist[(vaddr & 0xff000) >> PAGE_SHIFT] != pte) {
sun3_put_pte(vaddr, pte);
ptelist[(vaddr & 0xff000) >> PAGE_SHIFT] = pte;
diff --git a/arch/m68k/sun3/idprom.c b/arch/m68k/sun3/idprom.c
index cfe9aa4..9c23f50 100644
--- a/arch/m68k/sun3/idprom.c
+++ b/arch/m68k/sun3/idprom.c
@@ -64,12 +64,14 @@
for (i = 0; i < NUM_SUN_MACHINES; i++) {
if(Sun_Machines[i].id_machtype == machtype) {
if (machtype != (SM_SUN4M_OBP | 0x00))
- printk("TYPE: %s\n", Sun_Machines[i].name);
+ pr_info("TYPE: %s\n", Sun_Machines[i].name);
else {
#if 0
+ char sysname[128];
+
prom_getproperty(prom_root_node, "banner-name",
sysname, sizeof(sysname));
- printk("TYPE: %s\n", sysname);
+ pr_info("TYPE: %s\n", sysname);
#endif
}
return;
@@ -125,5 +127,5 @@
display_system_type(idprom->id_machtype);
- printk("Ethernet address: %pM\n", idprom->id_ethaddr);
+ pr_info("Ethernet address: %pM\n", idprom->id_ethaddr);
}
diff --git a/arch/m68k/sun3/mmu_emu.c b/arch/m68k/sun3/mmu_emu.c
index 0f95134..e9d7fbe 100644
--- a/arch/m68k/sun3/mmu_emu.c
+++ b/arch/m68k/sun3/mmu_emu.c
@@ -72,21 +72,21 @@
#if 0
/* Verbose version. */
unsigned long val = pte_val (pte);
- printk (" pte=%lx [addr=%lx",
+ pr_cont(" pte=%lx [addr=%lx",
val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT);
- if (val & SUN3_PAGE_VALID) printk (" valid");
- if (val & SUN3_PAGE_WRITEABLE) printk (" write");
- if (val & SUN3_PAGE_SYSTEM) printk (" sys");
- if (val & SUN3_PAGE_NOCACHE) printk (" nocache");
- if (val & SUN3_PAGE_ACCESSED) printk (" accessed");
- if (val & SUN3_PAGE_MODIFIED) printk (" modified");
+ if (val & SUN3_PAGE_VALID) pr_cont(" valid");
+ if (val & SUN3_PAGE_WRITEABLE) pr_cont(" write");
+ if (val & SUN3_PAGE_SYSTEM) pr_cont(" sys");
+ if (val & SUN3_PAGE_NOCACHE) pr_cont(" nocache");
+ if (val & SUN3_PAGE_ACCESSED) pr_cont(" accessed");
+ if (val & SUN3_PAGE_MODIFIED) pr_cont(" modified");
switch (val & SUN3_PAGE_TYPE_MASK) {
- case SUN3_PAGE_TYPE_MEMORY: printk (" memory"); break;
- case SUN3_PAGE_TYPE_IO: printk (" io"); break;
- case SUN3_PAGE_TYPE_VME16: printk (" vme16"); break;
- case SUN3_PAGE_TYPE_VME32: printk (" vme32"); break;
+ case SUN3_PAGE_TYPE_MEMORY: pr_cont(" memory"); break;
+ case SUN3_PAGE_TYPE_IO: pr_cont(" io"); break;
+ case SUN3_PAGE_TYPE_VME16: pr_cont(" vme16"); break;
+ case SUN3_PAGE_TYPE_VME32: pr_cont(" vme32"); break;
}
- printk ("]\n");
+ pr_cont("]\n");
#else
/* Terse version. More likely to fit on a line. */
unsigned long val = pte_val (pte);
@@ -108,7 +108,7 @@
default: type = "unknown?"; break;
}
- printk (" pte=%08lx [%07lx %s %s]\n",
+ pr_cont(" pte=%08lx [%07lx %s %s]\n",
val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT, flags, type);
#endif
}
@@ -116,7 +116,7 @@
/* Print the PTE value for a given virtual address. For debugging. */
void print_pte_vaddr (unsigned long vaddr)
{
- printk (" vaddr=%lx [%02lx]", vaddr, sun3_get_segmap (vaddr));
+ pr_cont(" vaddr=%lx [%02lx]", vaddr, sun3_get_segmap (vaddr));
print_pte (__pte (sun3_get_pte (vaddr)));
}
@@ -153,7 +153,7 @@
if(!pmeg_alloc[i]) {
#ifdef DEBUG_MMU_EMU
- printk("freed: ");
+ pr_info("freed:");
print_pte_vaddr (seg);
#endif
sun3_put_segmap(seg, SUN3_INVALID_PMEG);
@@ -165,7 +165,7 @@
if (sun3_get_segmap (seg) != SUN3_INVALID_PMEG) {
#ifdef DEBUG_PROM_MAPS
for(i = 0; i < 16; i++) {
- printk ("mapped:");
+ pr_info("mapped:");
print_pte_vaddr (seg + (i*PAGE_SIZE));
break;
}
@@ -293,8 +293,8 @@
#ifdef DEBUG_MMU_EMU
-printk("mmu_emu_map_pmeg: pmeg %x to context %d vaddr %x\n",
- curr_pmeg, context, vaddr);
+ pr_info("mmu_emu_map_pmeg: pmeg %x to context %d vaddr %x\n",
+ curr_pmeg, context, vaddr);
#endif
/* Invalidate old mapping for the pmeg, if any */
@@ -370,7 +370,7 @@
}
#ifdef DEBUG_MMU_EMU
- printk ("mmu_emu_handle_fault: vaddr=%lx type=%s crp=%p\n",
+ pr_info("mmu_emu_handle_fault: vaddr=%lx type=%s crp=%p\n",
vaddr, read_flag ? "read" : "write", crp);
#endif
@@ -378,14 +378,15 @@
offset = (vaddr >> SUN3_PTE_SIZE_BITS) & 0xF;
#ifdef DEBUG_MMU_EMU
- printk ("mmu_emu_handle_fault: segment=%lx offset=%lx\n", segment, offset);
+ pr_info("mmu_emu_handle_fault: segment=%lx offset=%lx\n", segment,
+ offset);
#endif
pte = (pte_t *) pgd_val (*(crp + segment));
//todo: next line should check for valid pmd properly.
if (!pte) {
-// printk ("mmu_emu_handle_fault: invalid pmd\n");
+// pr_info("mmu_emu_handle_fault: invalid pmd\n");
return 0;
}
@@ -417,9 +418,9 @@
pte_val (*pte) |= SUN3_PAGE_ACCESSED;
#ifdef DEBUG_MMU_EMU
- printk ("seg:%d crp:%p ->", get_fs().seg, crp);
+ pr_info("seg:%ld crp:%p ->", get_fs().seg, crp);
print_pte_vaddr (vaddr);
- printk ("\n");
+ pr_cont("\n");
#endif
return 1;
diff --git a/arch/m68k/sun3/prom/printf.c b/arch/m68k/sun3/prom/printf.c
index df85018..5b82bea 100644
--- a/arch/m68k/sun3/prom/printf.c
+++ b/arch/m68k/sun3/prom/printf.c
@@ -39,7 +39,7 @@
#ifdef CONFIG_KGDB
if (kgdb_initialized) {
- printk("kgdb_initialized = %d\n", kgdb_initialized);
+ pr_info("kgdb_initialized = %d\n", kgdb_initialized);
putpacket(bptr, 1);
} else
#else
diff --git a/arch/m68k/sun3/sun3dvma.c b/arch/m68k/sun3/sun3dvma.c
index b37521a..d36bd15 100644
--- a/arch/m68k/sun3/sun3dvma.c
+++ b/arch/m68k/sun3/sun3dvma.c
@@ -62,7 +62,7 @@
int i;
int j = 0;
- printk("dvma entry usage:\n");
+ pr_info("dvma entry usage:\n");
for(i = 0; i < IOMMU_TOTAL_ENTRIES; i++) {
if(!iommu_use[i])
@@ -70,16 +70,15 @@
j++;
- printk("dvma entry: %08lx len %08lx\n",
- ( i << DVMA_PAGE_SHIFT) + DVMA_START,
- iommu_use[i]);
+ pr_info("dvma entry: %08x len %08lx\n",
+ (i << DVMA_PAGE_SHIFT) + DVMA_START, iommu_use[i]);
}
- printk("%d entries in use total\n", j);
+ pr_info("%d entries in use total\n", j);
- printk("allocation/free calls: %lu/%lu\n", dvma_allocs, dvma_frees);
- printk("allocation/free bytes: %Lx/%Lx\n", dvma_alloc_bytes,
- dvma_free_bytes);
+ pr_info("allocation/free calls: %lu/%lu\n", dvma_allocs, dvma_frees);
+ pr_info("allocation/free bytes: %Lx/%Lx\n", dvma_alloc_bytes,
+ dvma_free_bytes);
}
static void print_holes(struct list_head *holes)
@@ -88,18 +87,18 @@
struct list_head *cur;
struct hole *hole;
- printk("listing dvma holes\n");
+ pr_info("listing dvma holes\n");
list_for_each(cur, holes) {
hole = list_entry(cur, struct hole, list);
if((hole->start == 0) && (hole->end == 0) && (hole->size == 0))
continue;
- printk("hole: start %08lx end %08lx size %08lx\n", hole->start, hole->end, hole->size);
+ pr_info("hole: start %08lx end %08lx size %08lx\n",
+ hole->start, hole->end, hole->size);
}
- printk("end of hole listing...\n");
-
+ pr_info("end of hole listing...\n");
}
#endif /* DVMA_DEBUG */
@@ -137,7 +136,7 @@
if(list_empty(&hole_cache)) {
if(!refill()) {
- printk("out of dvma hole cache!\n");
+ pr_crit("out of dvma hole cache!\n");
BUG();
}
}
@@ -157,7 +156,7 @@
if(list_empty(&hole_list)) {
#ifdef DVMA_DEBUG
- printk("out of dvma holes! (printing hole cache)\n");
+ pr_crit("out of dvma holes! (printing hole cache)\n");
print_holes(&hole_cache);
print_use();
#endif
@@ -195,7 +194,7 @@
}
- printk("unable to find dvma hole!\n");
+ pr_crit("unable to find dvma hole!\n");
BUG();
return 0;
}
@@ -287,15 +286,12 @@
len = 0x800;
if(!kaddr || !len) {
-// printk("error: kaddr %lx len %x\n", kaddr, len);
+// pr_err("error: kaddr %lx len %x\n", kaddr, len);
// *(int *)4 = 0;
return 0;
}
-#ifdef DEBUG
- printk("dvma_map request %08lx bytes from %08lx\n",
- len, kaddr);
-#endif
+ pr_debug("dvma_map request %08x bytes from %08lx\n", len, kaddr);
off = kaddr & ~DVMA_PAGE_MASK;
kaddr &= PAGE_MASK;
len += off;
@@ -307,12 +303,13 @@
align = ((align + (DVMA_PAGE_SIZE-1)) & DVMA_PAGE_MASK);
baddr = get_baddr(len, align);
-// printk("using baddr %lx\n", baddr);
+// pr_info("using baddr %lx\n", baddr);
if(!dvma_map_iommu(kaddr, baddr, len))
return (baddr + off);
- printk("dvma_map failed kaddr %lx baddr %lx len %x\n", kaddr, baddr, len);
+ pr_crit("dvma_map failed kaddr %lx baddr %lx len %x\n", kaddr, baddr,
+ len);
BUG();
return 0;
}
@@ -343,9 +340,7 @@
if(!len)
return NULL;
-#ifdef DEBUG
- printk("dvma_malloc request %lx bytes\n", len);
-#endif
+ pr_debug("dvma_malloc request %lx bytes\n", len);
len = ((len + (DVMA_PAGE_SIZE-1)) & DVMA_PAGE_MASK);
if((kaddr = __get_free_pages(GFP_ATOMIC, get_order(len))) == 0)
@@ -364,10 +359,8 @@
return NULL;
}
-#ifdef DEBUG
- printk("mapped %08lx bytes %08lx kern -> %08lx bus\n",
- len, kaddr, baddr);
-#endif
+ pr_debug("mapped %08lx bytes %08lx kern -> %08lx bus\n", len, kaddr,
+ baddr);
return (void *)vaddr;
diff --git a/arch/m68k/sun3x/dvma.c b/arch/m68k/sun3x/dvma.c
index d5ddcda..9413c87 100644
--- a/arch/m68k/sun3x/dvma.c
+++ b/arch/m68k/sun3x/dvma.c
@@ -58,21 +58,17 @@
((addr & 0x03c00000) >> \
(DVMA_PAGE_SHIFT+4)))
-#undef DEBUG
-
#ifdef DEBUG
/* code to print out a dvma mapping for debugging purposes */
void dvma_print (unsigned long dvma_addr)
{
- unsigned long index;
+ unsigned long index;
- index = dvma_addr >> DVMA_PAGE_SHIFT;
+ index = dvma_addr >> DVMA_PAGE_SHIFT;
- printk("idx %lx dvma_addr %08lx paddr %08lx\n", index, dvma_addr,
- dvma_entry_paddr(index));
-
-
+ pr_info("idx %lx dvma_addr %08lx paddr %08lx\n", index, dvma_addr,
+ dvma_entry_paddr(index));
}
#endif
@@ -91,10 +87,7 @@
end = PAGE_ALIGN(vaddr + len);
-#ifdef DEBUG
- printk("dvma: mapping kern %08lx to virt %08lx\n",
- kaddr, vaddr);
-#endif
+ pr_debug("dvma: mapping kern %08lx to virt %08lx\n", kaddr, vaddr);
pgd = pgd_offset_k(vaddr);
do {
@@ -126,10 +119,8 @@
end3 = end2;
do {
-#ifdef DEBUG
- printk("mapping %08lx phys to %08lx\n",
- __pa(kaddr), vaddr);
-#endif
+ pr_debug("mapping %08lx phys to %08lx\n",
+ __pa(kaddr), vaddr);
set_pte(pte, pfn_pte(virt_to_pfn(kaddr),
PAGE_KERNEL));
pte++;
@@ -162,7 +153,8 @@
for(; index < end ; index++) {
// if(dvma_entry_use(index))
// BUG();
-// printk("mapping pa %lx to ba %lx\n", __pa(kaddr), index << DVMA_PAGE_SHIFT);
+// pr_info("mapping pa %lx to ba %lx\n", __pa(kaddr),
+// index << DVMA_PAGE_SHIFT);
dvma_entry_set(index, __pa(kaddr));
@@ -190,13 +182,12 @@
end = (DVMA_PAGE_ALIGN(baddr+len) >> DVMA_PAGE_SHIFT);
for(; index < end ; index++) {
-#ifdef DEBUG
- printk("freeing bus mapping %08x\n", index << DVMA_PAGE_SHIFT);
-#endif
+ pr_debug("freeing bus mapping %08x\n",
+ index << DVMA_PAGE_SHIFT);
#if 0
if(!dvma_entry_use(index))
- printk("dvma_unmap freeing unused entry %04x\n",
- index);
+ pr_info("dvma_unmap freeing unused entry %04x\n",
+ index);
else
dvma_entry_dec(index);
#endif
diff --git a/arch/m68k/sun3x/prom.c b/arch/m68k/sun3x/prom.c
index 0898c3f..5d60e65 100644
--- a/arch/m68k/sun3x/prom.c
+++ b/arch/m68k/sun3x/prom.c
@@ -106,9 +106,9 @@
idprom_init();
if (!((idprom->id_machtype & SM_ARCH_MASK) == SM_SUN3X)) {
- printk("Warning: machine reports strange type %02x\n",
+ pr_warn("Machine reports strange type %02x\n",
idprom->id_machtype);
- printk("Pretending it's a 3/80, but very afraid...\n");
+ pr_warn("Pretending it's a 3/80, but very afraid...\n");
idprom->id_machtype = SM_SUN3X | SM_3_80;
}
diff --git a/arch/metag/include/asm/Kbuild b/arch/metag/include/asm/Kbuild
index 167150c..d3731f0 100644
--- a/arch/metag/include/asm/Kbuild
+++ b/arch/metag/include/asm/Kbuild
@@ -2,7 +2,6 @@
generic-y += bitsperlong.h
generic-y += bugs.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += current.h
generic-y += device.h
generic-y += dma.h
diff --git a/arch/microblaze/include/asm/Kbuild b/arch/microblaze/include/asm/Kbuild
index b0ae88c..6275eb0 100644
--- a/arch/microblaze/include/asm/Kbuild
+++ b/arch/microblaze/include/asm/Kbuild
@@ -1,7 +1,6 @@
generic-y += barrier.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += device.h
generic-y += exec.h
generic-y += irq_work.h
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index b3c5bde..e137eed 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -1703,6 +1703,8 @@
select WEAK_ORDERING
select CPU_SUPPORTS_HIGHMEM
select CPU_HAS_PREFETCH
+ select CPU_SUPPORTS_CPUFREQ
+ select MIPS_EXTERNAL_TIMER
help
Support for BMIPS32/3300/4350/4380 and BMIPS5000 processors.
diff --git a/arch/mips/configs/bmips_stb_defconfig b/arch/mips/configs/bmips_stb_defconfig
index 4eb5d6e..3cefa6b 100644
--- a/arch/mips/configs/bmips_stb_defconfig
+++ b/arch/mips/configs/bmips_stb_defconfig
@@ -9,13 +9,20 @@
# CONFIG_SWAP is not set
CONFIG_NO_HZ=y
CONFIG_BLK_DEV_INITRD=y
-CONFIG_RD_GZIP=y
CONFIG_EXPERT=y
# CONFIG_VM_EVENT_COUNTERS is not set
# CONFIG_SLUB_DEBUG is not set
# CONFIG_BLK_DEV_BSG is not set
# CONFIG_IOSCHED_DEADLINE is not set
# CONFIG_IOSCHED_CFQ is not set
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+CONFIG_BMIPS_CPUFREQ=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_PACKET_DIAG=y
@@ -24,7 +31,6 @@
# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
# CONFIG_INET_XFRM_MODE_TUNNEL is not set
# CONFIG_INET_XFRM_MODE_BEET is not set
-# CONFIG_INET_LRO is not set
# CONFIG_INET_DIAG is not set
CONFIG_CFG80211=y
CONFIG_NL80211_TESTMODE=y
@@ -34,8 +40,6 @@
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_STANDALONE is not set
# CONFIG_PREVENT_FIRMWARE_BUILD is not set
-CONFIG_PRINTK_TIME=y
-CONFIG_BRCMSTB_GISB_ARB=y
CONFIG_MTD=y
CONFIG_MTD_CFI=y
CONFIG_MTD_CFI_INTELEXT=y
@@ -51,16 +55,15 @@
# CONFIG_INPUT is not set
# CONFIG_SERIO is not set
# CONFIG_VT is not set
-# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_OF_PLATFORM=y
# CONFIG_HW_RANDOM is not set
-CONFIG_POWER_SUPPLY=y
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_BRCMSTB=y
CONFIG_POWER_RESET_SYSCON=y
+CONFIG_POWER_SUPPLY=y
# CONFIG_HWMON is not set
CONFIG_USB=y
CONFIG_USB_EHCI_HCD=y
@@ -82,6 +85,7 @@
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ASCII=y
CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
CONFIG_DEBUG_FS=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_CMDLINE_BOOL=y
diff --git a/arch/mips/configs/lemote2f_defconfig b/arch/mips/configs/lemote2f_defconfig
index 5da76e0..bed7455 100644
--- a/arch/mips/configs/lemote2f_defconfig
+++ b/arch/mips/configs/lemote2f_defconfig
@@ -40,7 +40,6 @@
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_DEBUG=y
CONFIG_CPU_FREQ_STAT=m
-CONFIG_CPU_FREQ_STAT_DETAILS=y
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
CONFIG_CPU_FREQ_GOV_USERSPACE=m
diff --git a/arch/mips/include/asm/Kbuild b/arch/mips/include/asm/Kbuild
index 3269b74..994b1c4 100644
--- a/arch/mips/include/asm/Kbuild
+++ b/arch/mips/include/asm/Kbuild
@@ -1,7 +1,6 @@
# MIPS headers
generic-(CONFIG_GENERIC_CSUM) += checksum.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += current.h
generic-y += dma-contiguous.h
generic-y += emergency-restart.h
diff --git a/arch/mips/kernel/binfmt_elfn32.c b/arch/mips/kernel/binfmt_elfn32.c
index 9c7f3e1..4a2ff39 100644
--- a/arch/mips/kernel/binfmt_elfn32.c
+++ b/arch/mips/kernel/binfmt_elfn32.c
@@ -99,15 +99,7 @@
#undef TASK_SIZE
#define TASK_SIZE TASK_SIZE32
-#undef cputime_to_timeval
-#define cputime_to_timeval cputime_to_compat_timeval
-static __inline__ void
-cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value)
-{
- unsigned long jiffies = cputime_to_jiffies(cputime);
-
- value->tv_usec = (jiffies % HZ) * (1000000L / HZ);
- value->tv_sec = jiffies / HZ;
-}
+#undef ns_to_timeval
+#define ns_to_timeval ns_to_compat_timeval
#include "../../../fs/binfmt_elf.c"
diff --git a/arch/mips/kernel/binfmt_elfo32.c b/arch/mips/kernel/binfmt_elfo32.c
index 1ab3432..3916404 100644
--- a/arch/mips/kernel/binfmt_elfo32.c
+++ b/arch/mips/kernel/binfmt_elfo32.c
@@ -102,15 +102,7 @@
#undef TASK_SIZE
#define TASK_SIZE TASK_SIZE32
-#undef cputime_to_timeval
-#define cputime_to_timeval cputime_to_compat_timeval
-static __inline__ void
-cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value)
-{
- unsigned long jiffies = cputime_to_jiffies(cputime);
-
- value->tv_usec = (jiffies % HZ) * (1000000L / HZ);
- value->tv_sec = jiffies / HZ;
-}
+#undef ns_to_timeval
+#define ns_to_timeval ns_to_compat_timeval
#include "../../../fs/binfmt_elf.c"
diff --git a/arch/mn10300/include/asm/Kbuild b/arch/mn10300/include/asm/Kbuild
index 1c8dd0f..97f64c7 100644
--- a/arch/mn10300/include/asm/Kbuild
+++ b/arch/mn10300/include/asm/Kbuild
@@ -1,7 +1,6 @@
generic-y += barrier.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += exec.h
generic-y += irq_work.h
generic-y += mcs_spinlock.h
diff --git a/arch/nios2/include/asm/Kbuild b/arch/nios2/include/asm/Kbuild
index d63330e..35b0e88 100644
--- a/arch/nios2/include/asm/Kbuild
+++ b/arch/nios2/include/asm/Kbuild
@@ -6,7 +6,6 @@
generic-y += bug.h
generic-y += bugs.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += current.h
generic-y += device.h
generic-y += div64.h
diff --git a/arch/openrisc/include/asm/Kbuild b/arch/openrisc/include/asm/Kbuild
index 2832f03..ef8d1cc 100644
--- a/arch/openrisc/include/asm/Kbuild
+++ b/arch/openrisc/include/asm/Kbuild
@@ -12,7 +12,6 @@
generic-y += clkdev.h
generic-y += cmpxchg-local.h
generic-y += cmpxchg.h
-generic-y += cputime.h
generic-y += current.h
generic-y += device.h
generic-y += div64.h
diff --git a/arch/parisc/include/asm/Kbuild b/arch/parisc/include/asm/Kbuild
index 91f53c0..4e179d7 100644
--- a/arch/parisc/include/asm/Kbuild
+++ b/arch/parisc/include/asm/Kbuild
@@ -2,7 +2,6 @@
generic-y += auxvec.h
generic-y += barrier.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += device.h
generic-y += div64.h
generic-y += emergency-restart.h
diff --git a/arch/parisc/kernel/binfmt_elf32.c b/arch/parisc/kernel/binfmt_elf32.c
index 00dc66f..f2adcf3 100644
--- a/arch/parisc/kernel/binfmt_elf32.c
+++ b/arch/parisc/kernel/binfmt_elf32.c
@@ -91,14 +91,7 @@
current->thread.map_base = DEFAULT_MAP_BASE32; \
current->thread.task_size = DEFAULT_TASK_SIZE32 \
-#undef cputime_to_timeval
-#define cputime_to_timeval cputime_to_compat_timeval
-static __inline__ void
-cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value)
-{
- unsigned long jiffies = cputime_to_jiffies(cputime);
- value->tv_usec = (jiffies % HZ) * (1000000L / HZ);
- value->tv_sec = jiffies / HZ;
-}
+#undef ns_to_timeval
+#define ns_to_timeval ns_to_compat_timeval
#include "../../../fs/binfmt_elf.c"
diff --git a/arch/parisc/kernel/setup.c b/arch/parisc/kernel/setup.c
index 2e66a88..068ed36 100644
--- a/arch/parisc/kernel/setup.c
+++ b/arch/parisc/kernel/setup.c
@@ -36,6 +36,7 @@
#undef PCI_DEBUG
#include <linux/proc_fs.h>
#include <linux/export.h>
+#include <linux/sched.h>
#include <asm/processor.h>
#include <asm/sections.h>
@@ -176,6 +177,7 @@
conswitchp = &dummy_con; /* we use do_take_over_console() later ! */
#endif
+ clear_sched_clock_stable();
}
/*
diff --git a/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi b/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi
index c744569..a97296c 100644
--- a/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi
@@ -678,5 +678,6 @@
compatible = "fsl,t2080-l2-cache-controller";
reg = <0xc20000 0x40000>;
next-level-cache = <&cpc>;
+ interrupts = <16 2 1 9>;
};
};
diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig
index 3ce91a3..1d2d69d 100644
--- a/arch/powerpc/configs/ppc6xx_defconfig
+++ b/arch/powerpc/configs/ppc6xx_defconfig
@@ -62,7 +62,6 @@
CONFIG_GEF_SBC610=y
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_STAT=m
-CONFIG_CPU_FREQ_STAT_DETAILS=y
CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
diff --git a/arch/powerpc/include/asm/accounting.h b/arch/powerpc/include/asm/accounting.h
index c133246..3abcf98 100644
--- a/arch/powerpc/include/asm/accounting.h
+++ b/arch/powerpc/include/asm/accounting.h
@@ -12,9 +12,17 @@
/* Stuff for accurate time accounting */
struct cpu_accounting_data {
- unsigned long user_time; /* accumulated usermode TB ticks */
- unsigned long system_time; /* accumulated system TB ticks */
- unsigned long user_time_scaled; /* accumulated usermode SPURR ticks */
+ /* Accumulated cputime values to flush on ticks*/
+ unsigned long utime;
+ unsigned long stime;
+ unsigned long utime_scaled;
+ unsigned long stime_scaled;
+ unsigned long gtime;
+ unsigned long hardirq_time;
+ unsigned long softirq_time;
+ unsigned long steal_time;
+ unsigned long idle_time;
+ /* Internal counters */
unsigned long starttime; /* TB value snapshot */
unsigned long starttime_user; /* TB value on exit to usermode */
unsigned long startspurr; /* SPURR value snapshot */
diff --git a/arch/powerpc/include/asm/cputime.h b/arch/powerpc/include/asm/cputime.h
index aa2e6a3..99b5418 100644
--- a/arch/powerpc/include/asm/cputime.h
+++ b/arch/powerpc/include/asm/cputime.h
@@ -16,12 +16,7 @@
#ifndef __POWERPC_CPUTIME_H
#define __POWERPC_CPUTIME_H
-#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
-#include <asm-generic/cputime.h>
-#ifdef __KERNEL__
-static inline void setup_cputime_one_jiffy(void) { }
-#endif
-#else
+#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
#include <linux/types.h>
#include <linux/time.h>
@@ -36,65 +31,6 @@
#define cmpxchg_cputime(ptr, old, new) cmpxchg(ptr, old, new)
#ifdef __KERNEL__
-
-/*
- * One jiffy in timebase units computed during initialization
- */
-extern cputime_t cputime_one_jiffy;
-
-/*
- * Convert cputime <-> jiffies
- */
-extern u64 __cputime_jiffies_factor;
-
-static inline unsigned long cputime_to_jiffies(const cputime_t ct)
-{
- return mulhdu((__force u64) ct, __cputime_jiffies_factor);
-}
-
-static inline cputime_t jiffies_to_cputime(const unsigned long jif)
-{
- u64 ct;
- unsigned long sec;
-
- /* have to be a little careful about overflow */
- ct = jif % HZ;
- sec = jif / HZ;
- if (ct) {
- ct *= tb_ticks_per_sec;
- do_div(ct, HZ);
- }
- if (sec)
- ct += (cputime_t) sec * tb_ticks_per_sec;
- return (__force cputime_t) ct;
-}
-
-static inline void setup_cputime_one_jiffy(void)
-{
- cputime_one_jiffy = jiffies_to_cputime(1);
-}
-
-static inline cputime64_t jiffies64_to_cputime64(const u64 jif)
-{
- u64 ct;
- u64 sec = jif;
-
- /* have to be a little careful about overflow */
- ct = do_div(sec, HZ);
- if (ct) {
- ct *= tb_ticks_per_sec;
- do_div(ct, HZ);
- }
- if (sec)
- ct += (u64) sec * tb_ticks_per_sec;
- return (__force cputime64_t) ct;
-}
-
-static inline u64 cputime64_to_jiffies64(const cputime_t ct)
-{
- return mulhdu((__force u64) ct, __cputime_jiffies_factor);
-}
-
/*
* Convert cputime <-> microseconds
*/
@@ -105,117 +41,6 @@
return mulhdu((__force u64) ct, __cputime_usec_factor);
}
-static inline cputime_t usecs_to_cputime(const unsigned long us)
-{
- u64 ct;
- unsigned long sec;
-
- /* have to be a little careful about overflow */
- ct = us % 1000000;
- sec = us / 1000000;
- if (ct) {
- ct *= tb_ticks_per_sec;
- do_div(ct, 1000000);
- }
- if (sec)
- ct += (cputime_t) sec * tb_ticks_per_sec;
- return (__force cputime_t) ct;
-}
-
-#define usecs_to_cputime64(us) usecs_to_cputime(us)
-
-/*
- * Convert cputime <-> seconds
- */
-extern u64 __cputime_sec_factor;
-
-static inline unsigned long cputime_to_secs(const cputime_t ct)
-{
- return mulhdu((__force u64) ct, __cputime_sec_factor);
-}
-
-static inline cputime_t secs_to_cputime(const unsigned long sec)
-{
- return (__force cputime_t)((u64) sec * tb_ticks_per_sec);
-}
-
-/*
- * Convert cputime <-> timespec
- */
-static inline void cputime_to_timespec(const cputime_t ct, struct timespec *p)
-{
- u64 x = (__force u64) ct;
- unsigned int frac;
-
- frac = do_div(x, tb_ticks_per_sec);
- p->tv_sec = x;
- x = (u64) frac * 1000000000;
- do_div(x, tb_ticks_per_sec);
- p->tv_nsec = x;
-}
-
-static inline cputime_t timespec_to_cputime(const struct timespec *p)
-{
- u64 ct;
-
- ct = (u64) p->tv_nsec * tb_ticks_per_sec;
- do_div(ct, 1000000000);
- return (__force cputime_t)(ct + (u64) p->tv_sec * tb_ticks_per_sec);
-}
-
-/*
- * Convert cputime <-> timeval
- */
-static inline void cputime_to_timeval(const cputime_t ct, struct timeval *p)
-{
- u64 x = (__force u64) ct;
- unsigned int frac;
-
- frac = do_div(x, tb_ticks_per_sec);
- p->tv_sec = x;
- x = (u64) frac * 1000000;
- do_div(x, tb_ticks_per_sec);
- p->tv_usec = x;
-}
-
-static inline cputime_t timeval_to_cputime(const struct timeval *p)
-{
- u64 ct;
-
- ct = (u64) p->tv_usec * tb_ticks_per_sec;
- do_div(ct, 1000000);
- return (__force cputime_t)(ct + (u64) p->tv_sec * tb_ticks_per_sec);
-}
-
-/*
- * Convert cputime <-> clock_t (units of 1/USER_HZ seconds)
- */
-extern u64 __cputime_clockt_factor;
-
-static inline unsigned long cputime_to_clock_t(const cputime_t ct)
-{
- return mulhdu((__force u64) ct, __cputime_clockt_factor);
-}
-
-static inline cputime_t clock_t_to_cputime(const unsigned long clk)
-{
- u64 ct;
- unsigned long sec;
-
- /* have to be a little careful about overflow */
- ct = clk % USER_HZ;
- sec = clk / USER_HZ;
- if (ct) {
- ct *= tb_ticks_per_sec;
- do_div(ct, USER_HZ);
- }
- if (sec)
- ct += (u64) sec * tb_ticks_per_sec;
- return (__force cputime_t) ct;
-}
-
-#define cputime64_to_clock_t(ct) cputime_to_clock_t((cputime_t)(ct))
-
/*
* PPC64 uses PACA which is task independent for storing accounting data while
* PPC32 uses struct thread_info, therefore at task switch the accounting data
diff --git a/arch/powerpc/include/asm/livepatch.h b/arch/powerpc/include/asm/livepatch.h
index a402f7f..47a03b9 100644
--- a/arch/powerpc/include/asm/livepatch.h
+++ b/arch/powerpc/include/asm/livepatch.h
@@ -28,13 +28,6 @@
return 0;
}
-static inline int klp_write_module_reloc(struct module *mod, unsigned long
- type, unsigned long loc, unsigned long value)
-{
- /* This requires infrastructure changes; we need the loadinfos. */
- return -ENOSYS;
-}
-
static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip)
{
regs->nip = ip;
diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
index 6a6792b..708c3e5 100644
--- a/arch/powerpc/include/asm/paca.h
+++ b/arch/powerpc/include/asm/paca.h
@@ -187,7 +187,6 @@
/* Stuff for accurate time accounting */
struct cpu_accounting_data accounting;
- u64 stolen_time; /* TB ticks taken by hypervisor */
u64 dtl_ridx; /* read index in dispatch log */
struct dtl_entry *dtl_curr; /* pointer corresponding to dtl_ridx */
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 195a9fc..9e8e771 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -249,9 +249,9 @@
DEFINE(ACCOUNT_STARTTIME_USER,
offsetof(struct paca_struct, accounting.starttime_user));
DEFINE(ACCOUNT_USER_TIME,
- offsetof(struct paca_struct, accounting.user_time));
+ offsetof(struct paca_struct, accounting.utime));
DEFINE(ACCOUNT_SYSTEM_TIME,
- offsetof(struct paca_struct, accounting.system_time));
+ offsetof(struct paca_struct, accounting.stime));
DEFINE(PACA_TRAP_SAVE, offsetof(struct paca_struct, trap_save));
DEFINE(PACA_NAPSTATELOST, offsetof(struct paca_struct, nap_state_lost));
DEFINE(PACA_SPRG_VDSO, offsetof(struct paca_struct, sprg_vdso));
@@ -262,9 +262,9 @@
DEFINE(ACCOUNT_STARTTIME_USER,
offsetof(struct thread_info, accounting.starttime_user));
DEFINE(ACCOUNT_USER_TIME,
- offsetof(struct thread_info, accounting.user_time));
+ offsetof(struct thread_info, accounting.utime));
DEFINE(ACCOUNT_SYSTEM_TIME,
- offsetof(struct thread_info, accounting.system_time));
+ offsetof(struct thread_info, accounting.stime));
#endif
#endif /* CONFIG_PPC64 */
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index bc2e08d..14e4855 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -57,6 +57,7 @@
#include <linux/clk-provider.h>
#include <linux/suspend.h>
#include <linux/rtc.h>
+#include <linux/cputime.h>
#include <asm/trace.h>
#include <asm/io.h>
@@ -72,7 +73,6 @@
#include <asm/smp.h>
#include <asm/vdso_datapage.h>
#include <asm/firmware.h>
-#include <asm/cputime.h>
#include <asm/asm-prototypes.h>
/* powerpc clocksource/clockevent code */
@@ -152,20 +152,11 @@
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
/*
- * Factors for converting from cputime_t (timebase ticks) to
- * jiffies, microseconds, seconds, and clock_t (1/USER_HZ seconds).
- * These are all stored as 0.64 fixed-point binary fractions.
+ * Factor for converting from cputime_t (timebase ticks) to
+ * microseconds. This is stored as 0.64 fixed-point binary fraction.
*/
-u64 __cputime_jiffies_factor;
-EXPORT_SYMBOL(__cputime_jiffies_factor);
u64 __cputime_usec_factor;
EXPORT_SYMBOL(__cputime_usec_factor);
-u64 __cputime_sec_factor;
-EXPORT_SYMBOL(__cputime_sec_factor);
-u64 __cputime_clockt_factor;
-EXPORT_SYMBOL(__cputime_clockt_factor);
-
-cputime_t cputime_one_jiffy;
#ifdef CONFIG_PPC_SPLPAR
void (*dtl_consumer)(struct dtl_entry *, u64);
@@ -181,14 +172,8 @@
{
struct div_result res;
- div128_by_32(HZ, 0, tb_ticks_per_sec, &res);
- __cputime_jiffies_factor = res.result_low;
div128_by_32(1000000, 0, tb_ticks_per_sec, &res);
__cputime_usec_factor = res.result_low;
- div128_by_32(1, 0, tb_ticks_per_sec, &res);
- __cputime_sec_factor = res.result_low;
- div128_by_32(USER_HZ, 0, tb_ticks_per_sec, &res);
- __cputime_clockt_factor = res.result_low;
}
/*
@@ -271,25 +256,19 @@
sst = scan_dispatch_log(acct->starttime_user);
ust = scan_dispatch_log(acct->starttime);
- acct->system_time -= sst;
- acct->user_time -= ust;
- local_paca->stolen_time += ust + sst;
+ acct->stime -= sst;
+ acct->utime -= ust;
+ acct->steal_time += ust + sst;
local_paca->soft_enabled = save_soft_enabled;
}
static inline u64 calculate_stolen_time(u64 stop_tb)
{
- u64 stolen = 0;
+ if (get_paca()->dtl_ridx != be64_to_cpu(get_lppaca()->dtl_idx))
+ return scan_dispatch_log(stop_tb);
- if (get_paca()->dtl_ridx != be64_to_cpu(get_lppaca()->dtl_idx)) {
- stolen = scan_dispatch_log(stop_tb);
- get_paca()->accounting.system_time -= stolen;
- }
-
- stolen += get_paca()->stolen_time;
- get_paca()->stolen_time = 0;
- return stolen;
+ return 0;
}
#else /* CONFIG_PPC_SPLPAR */
@@ -305,28 +284,27 @@
* or soft irq state.
*/
static unsigned long vtime_delta(struct task_struct *tsk,
- unsigned long *sys_scaled,
- unsigned long *stolen)
+ unsigned long *stime_scaled,
+ unsigned long *steal_time)
{
unsigned long now, nowscaled, deltascaled;
- unsigned long udelta, delta, user_scaled;
+ unsigned long stime;
+ unsigned long utime, utime_scaled;
struct cpu_accounting_data *acct = get_accounting(tsk);
WARN_ON_ONCE(!irqs_disabled());
now = mftb();
nowscaled = read_spurr(now);
- acct->system_time += now - acct->starttime;
+ stime = now - acct->starttime;
acct->starttime = now;
deltascaled = nowscaled - acct->startspurr;
acct->startspurr = nowscaled;
- *stolen = calculate_stolen_time(now);
+ *steal_time = calculate_stolen_time(now);
- delta = acct->system_time;
- acct->system_time = 0;
- udelta = acct->user_time - acct->utime_sspurr;
- acct->utime_sspurr = acct->user_time;
+ utime = acct->utime - acct->utime_sspurr;
+ acct->utime_sspurr = acct->utime;
/*
* Because we don't read the SPURR on every kernel entry/exit,
@@ -338,62 +316,105 @@
* the user ticks get saved up in paca->user_time_scaled to be
* used by account_process_tick.
*/
- *sys_scaled = delta;
- user_scaled = udelta;
- if (deltascaled != delta + udelta) {
- if (udelta) {
- *sys_scaled = deltascaled * delta / (delta + udelta);
- user_scaled = deltascaled - *sys_scaled;
+ *stime_scaled = stime;
+ utime_scaled = utime;
+ if (deltascaled != stime + utime) {
+ if (utime) {
+ *stime_scaled = deltascaled * stime / (stime + utime);
+ utime_scaled = deltascaled - *stime_scaled;
} else {
- *sys_scaled = deltascaled;
+ *stime_scaled = deltascaled;
}
}
- acct->user_time_scaled += user_scaled;
+ acct->utime_scaled += utime_scaled;
- return delta;
+ return stime;
}
void vtime_account_system(struct task_struct *tsk)
{
- unsigned long delta, sys_scaled, stolen;
+ unsigned long stime, stime_scaled, steal_time;
+ struct cpu_accounting_data *acct = get_accounting(tsk);
- delta = vtime_delta(tsk, &sys_scaled, &stolen);
- account_system_time(tsk, 0, delta);
- tsk->stimescaled += sys_scaled;
- if (stolen)
- account_steal_time(stolen);
+ stime = vtime_delta(tsk, &stime_scaled, &steal_time);
+
+ stime -= min(stime, steal_time);
+ acct->steal_time += steal_time;
+
+ if ((tsk->flags & PF_VCPU) && !irq_count()) {
+ acct->gtime += stime;
+ acct->utime_scaled += stime_scaled;
+ } else {
+ if (hardirq_count())
+ acct->hardirq_time += stime;
+ else if (in_serving_softirq())
+ acct->softirq_time += stime;
+ else
+ acct->stime += stime;
+
+ acct->stime_scaled += stime_scaled;
+ }
}
EXPORT_SYMBOL_GPL(vtime_account_system);
void vtime_account_idle(struct task_struct *tsk)
{
- unsigned long delta, sys_scaled, stolen;
+ unsigned long stime, stime_scaled, steal_time;
+ struct cpu_accounting_data *acct = get_accounting(tsk);
- delta = vtime_delta(tsk, &sys_scaled, &stolen);
- account_idle_time(delta + stolen);
+ stime = vtime_delta(tsk, &stime_scaled, &steal_time);
+ acct->idle_time += stime + steal_time;
}
/*
- * Transfer the user time accumulated in the paca
- * by the exception entry and exit code to the generic
- * process user time records.
+ * Account the whole cputime accumulated in the paca
* Must be called with interrupts disabled.
* Assumes that vtime_account_system/idle() has been called
* recently (i.e. since the last entry from usermode) so that
* get_paca()->user_time_scaled is up to date.
*/
-void vtime_account_user(struct task_struct *tsk)
+void vtime_flush(struct task_struct *tsk)
{
- cputime_t utime, utimescaled;
struct cpu_accounting_data *acct = get_accounting(tsk);
- utime = acct->user_time;
- utimescaled = acct->user_time_scaled;
- acct->user_time = 0;
- acct->user_time_scaled = 0;
+ if (acct->utime)
+ account_user_time(tsk, cputime_to_nsecs(acct->utime));
+
+ if (acct->utime_scaled)
+ tsk->utimescaled += cputime_to_nsecs(acct->utime_scaled);
+
+ if (acct->gtime)
+ account_guest_time(tsk, cputime_to_nsecs(acct->gtime));
+
+ if (acct->steal_time)
+ account_steal_time(cputime_to_nsecs(acct->steal_time));
+
+ if (acct->idle_time)
+ account_idle_time(cputime_to_nsecs(acct->idle_time));
+
+ if (acct->stime)
+ account_system_index_time(tsk, cputime_to_nsecs(acct->stime),
+ CPUTIME_SYSTEM);
+ if (acct->stime_scaled)
+ tsk->stimescaled += cputime_to_nsecs(acct->stime_scaled);
+
+ if (acct->hardirq_time)
+ account_system_index_time(tsk, cputime_to_nsecs(acct->hardirq_time),
+ CPUTIME_IRQ);
+ if (acct->softirq_time)
+ account_system_index_time(tsk, cputime_to_nsecs(acct->softirq_time),
+ CPUTIME_SOFTIRQ);
+
+ acct->utime = 0;
+ acct->utime_scaled = 0;
acct->utime_sspurr = 0;
- account_user_time(tsk, utime);
- tsk->utimescaled += utimescaled;
+ acct->gtime = 0;
+ acct->steal_time = 0;
+ acct->idle_time = 0;
+ acct->stime = 0;
+ acct->stime_scaled = 0;
+ acct->hardirq_time = 0;
+ acct->softirq_time = 0;
}
#ifdef CONFIG_PPC32
@@ -407,8 +428,7 @@
struct cpu_accounting_data *acct = get_accounting(current);
acct->starttime = get_accounting(prev)->starttime;
- acct->system_time = 0;
- acct->user_time = 0;
+ acct->startspurr = get_accounting(prev)->startspurr;
}
#endif /* CONFIG_PPC32 */
@@ -1018,7 +1038,6 @@
tb_ticks_per_sec = ppc_tb_freq;
tb_ticks_per_usec = ppc_tb_freq / 1000000;
calc_cputime_factors();
- setup_cputime_one_jiffy();
/*
* Compute scale factor for sched_clock.
diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
index 93abf8a..8e15880 100644
--- a/arch/powerpc/mm/init_64.c
+++ b/arch/powerpc/mm/init_64.c
@@ -347,7 +347,8 @@
void __init mmu_early_init_devtree(void)
{
/* Disable radix mode based on kernel command line. */
- if (disable_radix)
+ /* We don't yet have the machinery to do radix as a guest. */
+ if (disable_radix || !(mfmsr() & MSR_HV))
cur_cpu_spec->mmu_features &= ~MMU_FTR_TYPE_RADIX;
if (early_radix_enabled())
diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c
index 9c0e17c..3f864c3 100644
--- a/arch/powerpc/xmon/xmon.c
+++ b/arch/powerpc/xmon/xmon.c
@@ -2287,14 +2287,14 @@
DUMP(p, subcore_sibling_mask, "x");
#endif
- DUMP(p, accounting.user_time, "llx");
- DUMP(p, accounting.system_time, "llx");
- DUMP(p, accounting.user_time_scaled, "llx");
+ DUMP(p, accounting.utime, "llx");
+ DUMP(p, accounting.stime, "llx");
+ DUMP(p, accounting.utime_scaled, "llx");
DUMP(p, accounting.starttime, "llx");
DUMP(p, accounting.starttime_user, "llx");
DUMP(p, accounting.startspurr, "llx");
DUMP(p, accounting.utime_sspurr, "llx");
- DUMP(p, stolen_time, "llx");
+ DUMP(p, accounting.steal_time, "llx");
#undef DUMP
catch_memory_errors = 0;
diff --git a/arch/s390/appldata/appldata_os.c b/arch/s390/appldata/appldata_os.c
index 69b23b2..08b9e94 100644
--- a/arch/s390/appldata/appldata_os.c
+++ b/arch/s390/appldata/appldata_os.c
@@ -113,21 +113,21 @@
j = 0;
for_each_online_cpu(i) {
os_data->os_cpu[j].per_cpu_user =
- cputime_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_USER]);
+ nsecs_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_USER]);
os_data->os_cpu[j].per_cpu_nice =
- cputime_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_NICE]);
+ nsecs_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_NICE]);
os_data->os_cpu[j].per_cpu_system =
- cputime_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]);
+ nsecs_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]);
os_data->os_cpu[j].per_cpu_idle =
- cputime_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_IDLE]);
+ nsecs_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_IDLE]);
os_data->os_cpu[j].per_cpu_irq =
- cputime_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_IRQ]);
+ nsecs_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_IRQ]);
os_data->os_cpu[j].per_cpu_softirq =
- cputime_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]);
+ nsecs_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]);
os_data->os_cpu[j].per_cpu_iowait =
- cputime_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT]);
+ nsecs_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT]);
os_data->os_cpu[j].per_cpu_steal =
- cputime_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_STEAL]);
+ nsecs_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_STEAL]);
os_data->os_cpu[j].cpu_id = i;
j++;
}
diff --git a/arch/s390/include/asm/cputime.h b/arch/s390/include/asm/cputime.h
index 221b454..d1c407d 100644
--- a/arch/s390/include/asm/cputime.h
+++ b/arch/s390/include/asm/cputime.h
@@ -25,33 +25,6 @@
return n / base;
}
-#define cputime_one_jiffy jiffies_to_cputime(1)
-
-/*
- * Convert cputime to jiffies and back.
- */
-static inline unsigned long cputime_to_jiffies(const cputime_t cputime)
-{
- return __div((__force unsigned long long) cputime, CPUTIME_PER_SEC / HZ);
-}
-
-static inline cputime_t jiffies_to_cputime(const unsigned int jif)
-{
- return (__force cputime_t)(jif * (CPUTIME_PER_SEC / HZ));
-}
-
-static inline u64 cputime64_to_jiffies64(cputime64_t cputime)
-{
- unsigned long long jif = (__force unsigned long long) cputime;
- do_div(jif, CPUTIME_PER_SEC / HZ);
- return jif;
-}
-
-static inline cputime64_t jiffies64_to_cputime64(const u64 jif)
-{
- return (__force cputime64_t)(jif * (CPUTIME_PER_SEC / HZ));
-}
-
/*
* Convert cputime to microseconds and back.
*/
@@ -60,88 +33,8 @@
return (__force unsigned long long) cputime >> 12;
}
-static inline cputime_t usecs_to_cputime(const unsigned int m)
-{
- return (__force cputime_t)(m * CPUTIME_PER_USEC);
-}
-#define usecs_to_cputime64(m) usecs_to_cputime(m)
-
-/*
- * Convert cputime to milliseconds and back.
- */
-static inline unsigned int cputime_to_secs(const cputime_t cputime)
-{
- return __div((__force unsigned long long) cputime, CPUTIME_PER_SEC / 2) >> 1;
-}
-
-static inline cputime_t secs_to_cputime(const unsigned int s)
-{
- return (__force cputime_t)(s * CPUTIME_PER_SEC);
-}
-
-/*
- * Convert cputime to timespec and back.
- */
-static inline cputime_t timespec_to_cputime(const struct timespec *value)
-{
- unsigned long long ret = value->tv_sec * CPUTIME_PER_SEC;
- return (__force cputime_t)(ret + __div(value->tv_nsec * CPUTIME_PER_USEC, NSEC_PER_USEC));
-}
-
-static inline void cputime_to_timespec(const cputime_t cputime,
- struct timespec *value)
-{
- unsigned long long __cputime = (__force unsigned long long) cputime;
- value->tv_nsec = (__cputime % CPUTIME_PER_SEC) * NSEC_PER_USEC / CPUTIME_PER_USEC;
- value->tv_sec = __cputime / CPUTIME_PER_SEC;
-}
-
-/*
- * Convert cputime to timeval and back.
- * Since cputime and timeval have the same resolution (microseconds)
- * this is easy.
- */
-static inline cputime_t timeval_to_cputime(const struct timeval *value)
-{
- unsigned long long ret = value->tv_sec * CPUTIME_PER_SEC;
- return (__force cputime_t)(ret + value->tv_usec * CPUTIME_PER_USEC);
-}
-
-static inline void cputime_to_timeval(const cputime_t cputime,
- struct timeval *value)
-{
- unsigned long long __cputime = (__force unsigned long long) cputime;
- value->tv_usec = (__cputime % CPUTIME_PER_SEC) / CPUTIME_PER_USEC;
- value->tv_sec = __cputime / CPUTIME_PER_SEC;
-}
-
-/*
- * Convert cputime to clock and back.
- */
-static inline clock_t cputime_to_clock_t(cputime_t cputime)
-{
- unsigned long long clock = (__force unsigned long long) cputime;
- do_div(clock, CPUTIME_PER_SEC / USER_HZ);
- return clock;
-}
-
-static inline cputime_t clock_t_to_cputime(unsigned long x)
-{
- return (__force cputime_t)(x * (CPUTIME_PER_SEC / USER_HZ));
-}
-
-/*
- * Convert cputime64 to clock.
- */
-static inline clock_t cputime64_to_clock_t(cputime64_t cputime)
-{
- unsigned long long clock = (__force unsigned long long) cputime;
- do_div(clock, CPUTIME_PER_SEC / USER_HZ);
- return clock;
-}
-
-cputime64_t arch_cpu_idle_time(int cpu);
+u64 arch_cpu_idle_time(int cpu);
#define arch_idle_time(cpu) arch_cpu_idle_time(cpu)
diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h
index 9bfad2a..61261e0 100644
--- a/arch/s390/include/asm/lowcore.h
+++ b/arch/s390/include/asm/lowcore.h
@@ -85,53 +85,56 @@
__u64 mcck_enter_timer; /* 0x02c0 */
__u64 exit_timer; /* 0x02c8 */
__u64 user_timer; /* 0x02d0 */
- __u64 system_timer; /* 0x02d8 */
- __u64 steal_timer; /* 0x02e0 */
- __u64 last_update_timer; /* 0x02e8 */
- __u64 last_update_clock; /* 0x02f0 */
- __u64 int_clock; /* 0x02f8 */
- __u64 mcck_clock; /* 0x0300 */
- __u64 clock_comparator; /* 0x0308 */
+ __u64 guest_timer; /* 0x02d8 */
+ __u64 system_timer; /* 0x02e0 */
+ __u64 hardirq_timer; /* 0x02e8 */
+ __u64 softirq_timer; /* 0x02f0 */
+ __u64 steal_timer; /* 0x02f8 */
+ __u64 last_update_timer; /* 0x0300 */
+ __u64 last_update_clock; /* 0x0308 */
+ __u64 int_clock; /* 0x0310 */
+ __u64 mcck_clock; /* 0x0318 */
+ __u64 clock_comparator; /* 0x0320 */
/* Current process. */
- __u64 current_task; /* 0x0310 */
- __u8 pad_0x318[0x320-0x318]; /* 0x0318 */
- __u64 kernel_stack; /* 0x0320 */
+ __u64 current_task; /* 0x0328 */
+ __u8 pad_0x318[0x320-0x318]; /* 0x0330 */
+ __u64 kernel_stack; /* 0x0338 */
/* Interrupt, panic and restart stack. */
- __u64 async_stack; /* 0x0328 */
- __u64 panic_stack; /* 0x0330 */
- __u64 restart_stack; /* 0x0338 */
+ __u64 async_stack; /* 0x0340 */
+ __u64 panic_stack; /* 0x0348 */
+ __u64 restart_stack; /* 0x0350 */
/* Restart function and parameter. */
- __u64 restart_fn; /* 0x0340 */
- __u64 restart_data; /* 0x0348 */
- __u64 restart_source; /* 0x0350 */
+ __u64 restart_fn; /* 0x0358 */
+ __u64 restart_data; /* 0x0360 */
+ __u64 restart_source; /* 0x0368 */
/* Address space pointer. */
- __u64 kernel_asce; /* 0x0358 */
- __u64 user_asce; /* 0x0360 */
+ __u64 kernel_asce; /* 0x0370 */
+ __u64 user_asce; /* 0x0378 */
/*
* The lpp and current_pid fields form a
* 64-bit value that is set as program
* parameter with the LPP instruction.
*/
- __u32 lpp; /* 0x0368 */
- __u32 current_pid; /* 0x036c */
+ __u32 lpp; /* 0x0380 */
+ __u32 current_pid; /* 0x0384 */
/* SMP info area */
- __u32 cpu_nr; /* 0x0370 */
- __u32 softirq_pending; /* 0x0374 */
- __u64 percpu_offset; /* 0x0378 */
- __u64 vdso_per_cpu_data; /* 0x0380 */
- __u64 machine_flags; /* 0x0388 */
- __u32 preempt_count; /* 0x0390 */
- __u8 pad_0x0394[0x0398-0x0394]; /* 0x0394 */
- __u64 gmap; /* 0x0398 */
- __u32 spinlock_lockval; /* 0x03a0 */
- __u32 fpu_flags; /* 0x03a4 */
- __u8 pad_0x03a8[0x0400-0x03a8]; /* 0x03a8 */
+ __u32 cpu_nr; /* 0x0388 */
+ __u32 softirq_pending; /* 0x038c */
+ __u64 percpu_offset; /* 0x0390 */
+ __u64 vdso_per_cpu_data; /* 0x0398 */
+ __u64 machine_flags; /* 0x03a0 */
+ __u32 preempt_count; /* 0x03a8 */
+ __u8 pad_0x03ac[0x03b0-0x03ac]; /* 0x03ac */
+ __u64 gmap; /* 0x03b0 */
+ __u32 spinlock_lockval; /* 0x03b8 */
+ __u32 fpu_flags; /* 0x03bc */
+ __u8 pad_0x03c0[0x0400-0x03c0]; /* 0x03c0 */
/* Per cpu primary space access list */
__u32 paste[16]; /* 0x0400 */
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h
index 6bca916..977a5b6 100644
--- a/arch/s390/include/asm/processor.h
+++ b/arch/s390/include/asm/processor.h
@@ -111,7 +111,10 @@
unsigned int acrs[NUM_ACRS];
unsigned long ksp; /* kernel stack pointer */
unsigned long user_timer; /* task cputime in user space */
+ unsigned long guest_timer; /* task cputime in kvm guest */
unsigned long system_timer; /* task cputime in kernel space */
+ unsigned long hardirq_timer; /* task cputime in hardirq context */
+ unsigned long softirq_timer; /* task cputime in softirq context */
unsigned long sys_call_table; /* system call table address */
mm_segment_t mm_segment;
unsigned long gmap_addr; /* address of last gmap fault. */
diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c
index 7a55c29..d3bf69e 100644
--- a/arch/s390/kernel/idle.c
+++ b/arch/s390/kernel/idle.c
@@ -12,7 +12,7 @@
#include <linux/notifier.h>
#include <linux/init.h>
#include <linux/cpu.h>
-#include <asm/cputime.h>
+#include <linux/cputime.h>
#include <asm/nmi.h>
#include <asm/smp.h>
#include "entry.h"
@@ -43,7 +43,7 @@
idle->clock_idle_enter = idle->clock_idle_exit = 0ULL;
idle->idle_time += idle_time;
idle->idle_count++;
- account_idle_time(idle_time);
+ account_idle_time(cputime_to_nsecs(idle_time));
write_seqcount_end(&idle->seqcount);
}
NOKPROBE_SYMBOL(enabled_wait);
@@ -84,7 +84,7 @@
}
DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL);
-cputime64_t arch_cpu_idle_time(int cpu)
+u64 arch_cpu_idle_time(int cpu)
{
struct s390_idle_data *idle = &per_cpu(s390_idle, cpu);
unsigned long long now, idle_enter, idle_exit;
@@ -96,7 +96,8 @@
idle_enter = ACCESS_ONCE(idle->clock_idle_enter);
idle_exit = ACCESS_ONCE(idle->clock_idle_exit);
} while (read_seqcount_retry(&idle->seqcount, seq));
- return idle_enter ? ((idle_exit ?: now) - idle_enter) : 0;
+
+ return cputime_to_nsecs(idle_enter ? ((idle_exit ?: now) - idle_enter) : 0);
}
void arch_cpu_idle_enter(void)
diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c
index 1b5c5ee..b4a3e9e 100644
--- a/arch/s390/kernel/vtime.c
+++ b/arch/s390/kernel/vtime.c
@@ -6,13 +6,13 @@
*/
#include <linux/kernel_stat.h>
+#include <linux/cputime.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/timex.h>
#include <linux/types.h>
#include <linux/time.h>
-#include <asm/cputime.h>
#include <asm/vtimer.h>
#include <asm/vtime.h>
#include <asm/cpu_mf.h>
@@ -90,14 +90,41 @@
__this_cpu_write(mt_scaling_jiffies, jiffies_64);
}
+static inline u64 update_tsk_timer(unsigned long *tsk_vtime, u64 new)
+{
+ u64 delta;
+
+ delta = new - *tsk_vtime;
+ *tsk_vtime = new;
+ return delta;
+}
+
+
+static inline u64 scale_vtime(u64 vtime)
+{
+ u64 mult = __this_cpu_read(mt_scaling_mult);
+ u64 div = __this_cpu_read(mt_scaling_div);
+
+ if (smp_cpu_mtid)
+ return vtime * mult / div;
+ return vtime;
+}
+
+static void account_system_index_scaled(struct task_struct *p,
+ cputime_t cputime, cputime_t scaled,
+ enum cpu_usage_stat index)
+{
+ p->stimescaled += cputime_to_nsecs(scaled);
+ account_system_index_time(p, cputime_to_nsecs(cputime), index);
+}
+
/*
* Update process times based on virtual cpu times stored by entry.S
* to the lowcore fields user_timer, system_timer & steal_clock.
*/
static int do_account_vtime(struct task_struct *tsk)
{
- u64 timer, clock, user, system, steal;
- u64 user_scaled, system_scaled;
+ u64 timer, clock, user, guest, system, hardirq, softirq, steal;
timer = S390_lowcore.last_update_timer;
clock = S390_lowcore.last_update_clock;
@@ -110,53 +137,76 @@
#endif
: "=m" (S390_lowcore.last_update_timer),
"=m" (S390_lowcore.last_update_clock));
- S390_lowcore.system_timer += timer - S390_lowcore.last_update_timer;
- S390_lowcore.steal_timer += S390_lowcore.last_update_clock - clock;
+ clock = S390_lowcore.last_update_clock - clock;
+ timer -= S390_lowcore.last_update_timer;
+
+ if (hardirq_count())
+ S390_lowcore.hardirq_timer += timer;
+ else
+ S390_lowcore.system_timer += timer;
/* Update MT utilization calculation */
if (smp_cpu_mtid &&
time_after64(jiffies_64, this_cpu_read(mt_scaling_jiffies)))
update_mt_scaling();
- user = S390_lowcore.user_timer - tsk->thread.user_timer;
- S390_lowcore.steal_timer -= user;
- tsk->thread.user_timer = S390_lowcore.user_timer;
+ /* Calculate cputime delta */
+ user = update_tsk_timer(&tsk->thread.user_timer,
+ READ_ONCE(S390_lowcore.user_timer));
+ guest = update_tsk_timer(&tsk->thread.guest_timer,
+ READ_ONCE(S390_lowcore.guest_timer));
+ system = update_tsk_timer(&tsk->thread.system_timer,
+ READ_ONCE(S390_lowcore.system_timer));
+ hardirq = update_tsk_timer(&tsk->thread.hardirq_timer,
+ READ_ONCE(S390_lowcore.hardirq_timer));
+ softirq = update_tsk_timer(&tsk->thread.softirq_timer,
+ READ_ONCE(S390_lowcore.softirq_timer));
+ S390_lowcore.steal_timer +=
+ clock - user - guest - system - hardirq - softirq;
- system = S390_lowcore.system_timer - tsk->thread.system_timer;
- S390_lowcore.steal_timer -= system;
- tsk->thread.system_timer = S390_lowcore.system_timer;
-
- user_scaled = user;
- system_scaled = system;
- /* Do MT utilization scaling */
- if (smp_cpu_mtid) {
- u64 mult = __this_cpu_read(mt_scaling_mult);
- u64 div = __this_cpu_read(mt_scaling_div);
-
- user_scaled = (user_scaled * mult) / div;
- system_scaled = (system_scaled * mult) / div;
+ /* Push account value */
+ if (user) {
+ account_user_time(tsk, cputime_to_nsecs(user));
+ tsk->utimescaled += cputime_to_nsecs(scale_vtime(user));
}
- account_user_time(tsk, user);
- tsk->utimescaled += user_scaled;
- account_system_time(tsk, 0, system);
- tsk->stimescaled += system_scaled;
+
+ if (guest) {
+ account_guest_time(tsk, cputime_to_nsecs(guest));
+ tsk->utimescaled += cputime_to_nsecs(scale_vtime(guest));
+ }
+
+ if (system)
+ account_system_index_scaled(tsk, system, scale_vtime(system),
+ CPUTIME_SYSTEM);
+ if (hardirq)
+ account_system_index_scaled(tsk, hardirq, scale_vtime(hardirq),
+ CPUTIME_IRQ);
+ if (softirq)
+ account_system_index_scaled(tsk, softirq, scale_vtime(softirq),
+ CPUTIME_SOFTIRQ);
steal = S390_lowcore.steal_timer;
if ((s64) steal > 0) {
S390_lowcore.steal_timer = 0;
- account_steal_time(steal);
+ account_steal_time(cputime_to_nsecs(steal));
}
- return virt_timer_forward(user + system);
+ return virt_timer_forward(user + guest + system + hardirq + softirq);
}
void vtime_task_switch(struct task_struct *prev)
{
do_account_vtime(prev);
prev->thread.user_timer = S390_lowcore.user_timer;
+ prev->thread.guest_timer = S390_lowcore.guest_timer;
prev->thread.system_timer = S390_lowcore.system_timer;
+ prev->thread.hardirq_timer = S390_lowcore.hardirq_timer;
+ prev->thread.softirq_timer = S390_lowcore.softirq_timer;
S390_lowcore.user_timer = current->thread.user_timer;
+ S390_lowcore.guest_timer = current->thread.guest_timer;
S390_lowcore.system_timer = current->thread.system_timer;
+ S390_lowcore.hardirq_timer = current->thread.hardirq_timer;
+ S390_lowcore.softirq_timer = current->thread.softirq_timer;
}
/*
@@ -164,7 +214,7 @@
* accounting system time in order to correctly compute
* the stolen time accounting.
*/
-void vtime_account_user(struct task_struct *tsk)
+void vtime_flush(struct task_struct *tsk)
{
if (do_account_vtime(tsk))
virt_timer_expire();
@@ -176,32 +226,22 @@
*/
void vtime_account_irq_enter(struct task_struct *tsk)
{
- u64 timer, system, system_scaled;
+ u64 timer;
timer = S390_lowcore.last_update_timer;
S390_lowcore.last_update_timer = get_vtimer();
- S390_lowcore.system_timer += timer - S390_lowcore.last_update_timer;
+ timer -= S390_lowcore.last_update_timer;
- /* Update MT utilization calculation */
- if (smp_cpu_mtid &&
- time_after64(jiffies_64, this_cpu_read(mt_scaling_jiffies)))
- update_mt_scaling();
+ if ((tsk->flags & PF_VCPU) && (irq_count() == 0))
+ S390_lowcore.guest_timer += timer;
+ else if (hardirq_count())
+ S390_lowcore.hardirq_timer += timer;
+ else if (in_serving_softirq())
+ S390_lowcore.softirq_timer += timer;
+ else
+ S390_lowcore.system_timer += timer;
- system = S390_lowcore.system_timer - tsk->thread.system_timer;
- S390_lowcore.steal_timer -= system;
- tsk->thread.system_timer = S390_lowcore.system_timer;
- system_scaled = system;
- /* Do MT utilization scaling */
- if (smp_cpu_mtid) {
- u64 mult = __this_cpu_read(mt_scaling_mult);
- u64 div = __this_cpu_read(mt_scaling_div);
-
- system_scaled = (system_scaled * mult) / div;
- }
- account_system_time(tsk, 0, system);
- tsk->stimescaled += system_scaled;
-
- virt_timer_forward(system);
+ virt_timer_forward(timer);
}
EXPORT_SYMBOL_GPL(vtime_account_irq_enter);
diff --git a/arch/score/include/asm/Kbuild b/arch/score/include/asm/Kbuild
index a05218f..51970bb 100644
--- a/arch/score/include/asm/Kbuild
+++ b/arch/score/include/asm/Kbuild
@@ -4,7 +4,6 @@
generic-y += barrier.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += irq_work.h
generic-y += mcs_spinlock.h
generic-y += mm-arch-hooks.h
diff --git a/arch/sh/configs/sh7785lcr_32bit_defconfig b/arch/sh/configs/sh7785lcr_32bit_defconfig
index 9bdcf72..2fce54d 100644
--- a/arch/sh/configs/sh7785lcr_32bit_defconfig
+++ b/arch/sh/configs/sh7785lcr_32bit_defconfig
@@ -25,7 +25,7 @@
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_CPU_FREQ=y
-CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
CONFIG_SH_CPU_FREQ=y
CONFIG_HEARTBEAT=y
diff --git a/arch/sh/include/asm/Kbuild b/arch/sh/include/asm/Kbuild
index 751c337..cf2a750 100644
--- a/arch/sh/include/asm/Kbuild
+++ b/arch/sh/include/asm/Kbuild
@@ -1,7 +1,6 @@
generic-y += bitsperlong.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += current.h
generic-y += delay.h
generic-y += div64.h
diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild
index 0569bfa..e9e837b 100644
--- a/arch/sparc/include/asm/Kbuild
+++ b/arch/sparc/include/asm/Kbuild
@@ -2,7 +2,6 @@
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += div64.h
generic-y += emergency-restart.h
generic-y += exec.h
diff --git a/arch/tile/include/asm/Kbuild b/arch/tile/include/asm/Kbuild
index 2d1f563..aa48b6e 100644
--- a/arch/tile/include/asm/Kbuild
+++ b/arch/tile/include/asm/Kbuild
@@ -4,8 +4,6 @@
generic-y += bug.h
generic-y += bugs.h
generic-y += clkdev.h
-generic-y += cputime.h
-generic-y += div64.h
generic-y += emergency-restart.h
generic-y += errno.h
generic-y += exec.h
diff --git a/arch/tile/include/asm/div64.h b/arch/tile/include/asm/div64.h
new file mode 100644
index 0000000..9f765cd
--- /dev/null
+++ b/arch/tile/include/asm/div64.h
@@ -0,0 +1,16 @@
+#ifndef _ASM_TILE_DIV64_H
+#define _ASM_TILE_DIV64_H
+
+#include <linux/types.h>
+
+#ifdef __tilegx__
+static inline u64 mul_u32_u32(u32 a, u32 b)
+{
+ return __insn_mul_lu_lu(a, b);
+}
+#define mul_u32_u32 mul_u32_u32
+#endif
+
+#include <asm-generic/div64.h>
+
+#endif /* _ASM_TILE_DIV64_H */
diff --git a/arch/um/drivers/random.c b/arch/um/drivers/random.c
index 05523f1..57f0305 100644
--- a/arch/um/drivers/random.c
+++ b/arch/um/drivers/random.c
@@ -76,7 +76,7 @@
add_sigio_fd(random_fd);
add_wait_queue(&host_read_wait, &wait);
- set_task_state(current, TASK_INTERRUPTIBLE);
+ set_current_state(TASK_INTERRUPTIBLE);
schedule();
remove_wait_queue(&host_read_wait, &wait);
diff --git a/arch/um/include/asm/Kbuild b/arch/um/include/asm/Kbuild
index 052f7f6..90c281c 100644
--- a/arch/um/include/asm/Kbuild
+++ b/arch/um/include/asm/Kbuild
@@ -1,7 +1,6 @@
generic-y += barrier.h
generic-y += bug.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += current.h
generic-y += delay.h
generic-y += device.h
diff --git a/arch/unicore32/include/asm/Kbuild b/arch/unicore32/include/asm/Kbuild
index 256c45b..5d51ade 100644
--- a/arch/unicore32/include/asm/Kbuild
+++ b/arch/unicore32/include/asm/Kbuild
@@ -4,7 +4,6 @@
generic-y += bitsperlong.h
generic-y += bugs.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += current.h
generic-y += device.h
generic-y += div64.h
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index e487493..f8fbfc5 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1070,7 +1070,7 @@
def_bool y
config X86_MCE_INJECT
- depends on X86_MCE
+ depends on X86_MCE && X86_LOCAL_APIC
tristate "Machine check injector support"
---help---
Provide support for injecting machine checks for testing purposes.
@@ -1994,10 +1994,6 @@
theoretically possible, but the implementations are further
limited due to memory layouts.
- If CONFIG_HIBERNATE is also enabled, KASLR is disabled at boot
- time. To enable it, boot with "kaslr" on the kernel command
- line (which will also disable hibernation).
-
If unsure, say N.
# Relocation on x86 needs some additional build support
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index 67eec55..783099f 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -120,14 +120,6 @@
against certain classes of kernel exploits.
If in doubt, say "N".
-config DEBUG_NX_TEST
- tristate "Testcase for the NX non-executable stack feature"
- depends on DEBUG_KERNEL && m
- ---help---
- This option enables a testcase for the CPU NX capability
- and the software setup of this feature.
- If in doubt, say "N"
-
config DOUBLEFAULT
default y
bool "Enable doublefault exception handler" if EXPERT
diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h
index e5612f3..9b42b6d 100644
--- a/arch/x86/boot/boot.h
+++ b/arch/x86/boot/boot.h
@@ -333,6 +333,7 @@
unsigned int atou(const char *s);
unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base);
size_t strlen(const char *s);
+char *strchr(const char *s, int c);
/* tty.c */
void puts(const char *);
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index ff01c8f..801c7a1 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -32,160 +32,13 @@
\
table = (typeof(table))sys_table; \
\
+ c->runtime_services = table->runtime; \
c->boot_services = table->boottime; \
c->text_output = table->con_out; \
}
BOOT_SERVICES(32);
BOOT_SERVICES(64);
-void efi_char16_printk(efi_system_table_t *, efi_char16_t *);
-
-static efi_status_t
-__file_size32(void *__fh, efi_char16_t *filename_16,
- void **handle, u64 *file_sz)
-{
- efi_file_handle_32_t *h, *fh = __fh;
- efi_file_info_t *info;
- efi_status_t status;
- efi_guid_t info_guid = EFI_FILE_INFO_ID;
- u32 info_sz;
-
- status = efi_early->call((unsigned long)fh->open, fh, &h, filename_16,
- EFI_FILE_MODE_READ, (u64)0);
- if (status != EFI_SUCCESS) {
- efi_printk(sys_table, "Failed to open file: ");
- efi_char16_printk(sys_table, filename_16);
- efi_printk(sys_table, "\n");
- return status;
- }
-
- *handle = h;
-
- info_sz = 0;
- status = efi_early->call((unsigned long)h->get_info, h, &info_guid,
- &info_sz, NULL);
- if (status != EFI_BUFFER_TOO_SMALL) {
- efi_printk(sys_table, "Failed to get file info size\n");
- return status;
- }
-
-grow:
- status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
- info_sz, (void **)&info);
- if (status != EFI_SUCCESS) {
- efi_printk(sys_table, "Failed to alloc mem for file info\n");
- return status;
- }
-
- status = efi_early->call((unsigned long)h->get_info, h, &info_guid,
- &info_sz, info);
- if (status == EFI_BUFFER_TOO_SMALL) {
- efi_call_early(free_pool, info);
- goto grow;
- }
-
- *file_sz = info->file_size;
- efi_call_early(free_pool, info);
-
- if (status != EFI_SUCCESS)
- efi_printk(sys_table, "Failed to get initrd info\n");
-
- return status;
-}
-
-static efi_status_t
-__file_size64(void *__fh, efi_char16_t *filename_16,
- void **handle, u64 *file_sz)
-{
- efi_file_handle_64_t *h, *fh = __fh;
- efi_file_info_t *info;
- efi_status_t status;
- efi_guid_t info_guid = EFI_FILE_INFO_ID;
- u64 info_sz;
-
- status = efi_early->call((unsigned long)fh->open, fh, &h, filename_16,
- EFI_FILE_MODE_READ, (u64)0);
- if (status != EFI_SUCCESS) {
- efi_printk(sys_table, "Failed to open file: ");
- efi_char16_printk(sys_table, filename_16);
- efi_printk(sys_table, "\n");
- return status;
- }
-
- *handle = h;
-
- info_sz = 0;
- status = efi_early->call((unsigned long)h->get_info, h, &info_guid,
- &info_sz, NULL);
- if (status != EFI_BUFFER_TOO_SMALL) {
- efi_printk(sys_table, "Failed to get file info size\n");
- return status;
- }
-
-grow:
- status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
- info_sz, (void **)&info);
- if (status != EFI_SUCCESS) {
- efi_printk(sys_table, "Failed to alloc mem for file info\n");
- return status;
- }
-
- status = efi_early->call((unsigned long)h->get_info, h, &info_guid,
- &info_sz, info);
- if (status == EFI_BUFFER_TOO_SMALL) {
- efi_call_early(free_pool, info);
- goto grow;
- }
-
- *file_sz = info->file_size;
- efi_call_early(free_pool, info);
-
- if (status != EFI_SUCCESS)
- efi_printk(sys_table, "Failed to get initrd info\n");
-
- return status;
-}
-efi_status_t
-efi_file_size(efi_system_table_t *sys_table, void *__fh,
- efi_char16_t *filename_16, void **handle, u64 *file_sz)
-{
- if (efi_early->is64)
- return __file_size64(__fh, filename_16, handle, file_sz);
-
- return __file_size32(__fh, filename_16, handle, file_sz);
-}
-
-efi_status_t
-efi_file_read(void *handle, unsigned long *size, void *addr)
-{
- unsigned long func;
-
- if (efi_early->is64) {
- efi_file_handle_64_t *fh = handle;
-
- func = (unsigned long)fh->read;
- return efi_early->call(func, handle, size, addr);
- } else {
- efi_file_handle_32_t *fh = handle;
-
- func = (unsigned long)fh->read;
- return efi_early->call(func, handle, size, addr);
- }
-}
-
-efi_status_t efi_file_close(void *handle)
-{
- if (efi_early->is64) {
- efi_file_handle_64_t *fh = handle;
-
- return efi_early->call((unsigned long)fh->close, handle);
- } else {
- efi_file_handle_32_t *fh = handle;
-
- return efi_early->call((unsigned long)fh->close, handle);
- }
-}
-
static inline efi_status_t __open_volume32(void *__image, void **__fh)
{
efi_file_io_interface_t *io;
@@ -249,30 +102,8 @@
void efi_char16_printk(efi_system_table_t *table, efi_char16_t *str)
{
- unsigned long output_string;
- size_t offset;
-
- if (efi_early->is64) {
- struct efi_simple_text_output_protocol_64 *out;
- u64 *func;
-
- offset = offsetof(typeof(*out), output_string);
- output_string = efi_early->text_output + offset;
- out = (typeof(out))(unsigned long)efi_early->text_output;
- func = (u64 *)output_string;
-
- efi_early->call(*func, out, str);
- } else {
- struct efi_simple_text_output_protocol_32 *out;
- u32 *func;
-
- offset = offsetof(typeof(*out), output_string);
- output_string = efi_early->text_output + offset;
- out = (typeof(out))(unsigned long)efi_early->text_output;
- func = (u32 *)output_string;
-
- efi_early->call(*func, out, str);
- }
+ efi_call_proto(efi_simple_text_output_protocol, output_string,
+ efi_early->text_output, str);
}
static efi_status_t
@@ -1157,6 +988,13 @@
else
setup_boot_services32(efi_early);
+ /*
+ * If the boot loader gave us a value for secure_boot then we use that,
+ * otherwise we ask the BIOS.
+ */
+ if (boot_params->secure_boot == efi_secureboot_mode_unset)
+ boot_params->secure_boot = efi_get_secureboot(sys_table);
+
setup_graphics(boot_params);
setup_efi_pci(boot_params);
diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index fd0b6a2..d85b962 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -82,7 +82,7 @@
/* Relocate efi_config->call() */
leal efi32_config(%esi), %eax
- add %esi, 32(%eax)
+ add %esi, 40(%eax)
pushl %eax
call make_boot_params
@@ -108,7 +108,7 @@
/* Relocate efi_config->call() */
leal efi32_config(%esi), %eax
- add %esi, 32(%eax)
+ add %esi, 40(%eax)
pushl %eax
2:
call efi_main
@@ -264,7 +264,7 @@
#ifdef CONFIG_EFI_STUB
.data
efi32_config:
- .fill 4,8,0
+ .fill 5,8,0
.long efi_call_phys
.long 0
.byte 0
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index 4d85e60..d2ae1f8 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -264,7 +264,7 @@
/*
* Relocate efi_config->call().
*/
- addq %rbp, efi64_config+32(%rip)
+ addq %rbp, efi64_config+40(%rip)
movq %rax, %rdi
call make_boot_params
@@ -284,7 +284,7 @@
* Relocate efi_config->call().
*/
movq efi_config(%rip), %rax
- addq %rbp, 32(%rax)
+ addq %rbp, 40(%rax)
2:
movq efi_config(%rip), %rdi
call efi_main
@@ -456,14 +456,14 @@
#ifdef CONFIG_EFI_MIXED
.global efi32_config
efi32_config:
- .fill 4,8,0
+ .fill 5,8,0
.quad efi64_thunk
.byte 0
#endif
.global efi64_config
efi64_config:
- .fill 4,8,0
+ .fill 5,8,0
.quad efi_call
.byte 1
#endif /* CONFIG_EFI_STUB */
diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
index a66854d..8b7c9e7 100644
--- a/arch/x86/boot/compressed/kaslr.c
+++ b/arch/x86/boot/compressed/kaslr.c
@@ -11,6 +11,7 @@
*/
#include "misc.h"
#include "error.h"
+#include "../boot.h"
#include <generated/compile.h>
#include <linux/module.h>
@@ -52,15 +53,22 @@
#include "../../lib/kaslr.c"
struct mem_vector {
- unsigned long start;
- unsigned long size;
+ unsigned long long start;
+ unsigned long long size;
};
+/* Only supporting at most 4 unusable memmap regions with kaslr */
+#define MAX_MEMMAP_REGIONS 4
+
+static bool memmap_too_large;
+
enum mem_avoid_index {
MEM_AVOID_ZO_RANGE = 0,
MEM_AVOID_INITRD,
MEM_AVOID_CMDLINE,
MEM_AVOID_BOOTPARAMS,
+ MEM_AVOID_MEMMAP_BEGIN,
+ MEM_AVOID_MEMMAP_END = MEM_AVOID_MEMMAP_BEGIN + MAX_MEMMAP_REGIONS - 1,
MEM_AVOID_MAX,
};
@@ -77,6 +85,123 @@
return true;
}
+/**
+ * _memparse - Parse a string with mem suffixes into a number
+ * @ptr: Where parse begins
+ * @retptr: (output) Optional pointer to next char after parse completes
+ *
+ * Parses a string into a number. The number stored at @ptr is
+ * potentially suffixed with K, M, G, T, P, E.
+ */
+static unsigned long long _memparse(const char *ptr, char **retptr)
+{
+ char *endptr; /* Local pointer to end of parsed string */
+
+ unsigned long long ret = simple_strtoull(ptr, &endptr, 0);
+
+ switch (*endptr) {
+ case 'E':
+ case 'e':
+ ret <<= 10;
+ case 'P':
+ case 'p':
+ ret <<= 10;
+ case 'T':
+ case 't':
+ ret <<= 10;
+ case 'G':
+ case 'g':
+ ret <<= 10;
+ case 'M':
+ case 'm':
+ ret <<= 10;
+ case 'K':
+ case 'k':
+ ret <<= 10;
+ endptr++;
+ default:
+ break;
+ }
+
+ if (retptr)
+ *retptr = endptr;
+
+ return ret;
+}
+
+static int
+parse_memmap(char *p, unsigned long long *start, unsigned long long *size)
+{
+ char *oldp;
+
+ if (!p)
+ return -EINVAL;
+
+ /* We don't care about this option here */
+ if (!strncmp(p, "exactmap", 8))
+ return -EINVAL;
+
+ oldp = p;
+ *size = _memparse(p, &p);
+ if (p == oldp)
+ return -EINVAL;
+
+ switch (*p) {
+ case '@':
+ /* Skip this region, usable */
+ *start = 0;
+ *size = 0;
+ return 0;
+ case '#':
+ case '$':
+ case '!':
+ *start = _memparse(p + 1, &p);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static void mem_avoid_memmap(void)
+{
+ char arg[128];
+ int rc;
+ int i;
+ char *str;
+
+ /* See if we have any memmap areas */
+ rc = cmdline_find_option("memmap", arg, sizeof(arg));
+ if (rc <= 0)
+ return;
+
+ i = 0;
+ str = arg;
+ while (str && (i < MAX_MEMMAP_REGIONS)) {
+ int rc;
+ unsigned long long start, size;
+ char *k = strchr(str, ',');
+
+ if (k)
+ *k++ = 0;
+
+ rc = parse_memmap(str, &start, &size);
+ if (rc < 0)
+ break;
+ str = k;
+ /* A usable region that should not be skipped */
+ if (size == 0)
+ continue;
+
+ mem_avoid[MEM_AVOID_MEMMAP_BEGIN + i].start = start;
+ mem_avoid[MEM_AVOID_MEMMAP_BEGIN + i].size = size;
+ i++;
+ }
+
+ /* More than 4 memmaps, fail kaslr */
+ if ((i >= MAX_MEMMAP_REGIONS) && str)
+ memmap_too_large = true;
+}
+
/*
* In theory, KASLR can put the kernel anywhere in the range of [16M, 64T).
* The mem_avoid array is used to store the ranges that need to be avoided
@@ -197,6 +322,9 @@
/* We don't need to set a mapping for setup_data. */
+ /* Mark the memmap regions we need to avoid */
+ mem_avoid_memmap();
+
#ifdef CONFIG_X86_VERBOSE_BOOTUP
/* Make sure video RAM can be used. */
add_identity_map(0, PMD_SIZE);
@@ -379,6 +507,12 @@
int i;
unsigned long addr;
+ /* Check if we had too many memmaps. */
+ if (memmap_too_large) {
+ debug_putstr("Aborted e820 scan (more than 4 memmap= args)!\n");
+ return 0;
+ }
+
/* Make sure minimum is aligned. */
minimum = ALIGN(minimum, CONFIG_PHYSICAL_ALIGN);
@@ -456,7 +590,7 @@
/* Walk e820 and find a random address. */
random_addr = find_random_phys_addr(min_addr, output_size);
if (!random_addr) {
- warn("KASLR disabled: could not find suitable E820 region!");
+ warn("Physical KASLR disabled: no suitable memory region!");
} else {
/* Update the new physical address location. */
if (*output != random_addr) {
diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c
index 9e240fc..5457b02 100644
--- a/arch/x86/boot/string.c
+++ b/arch/x86/boot/string.c
@@ -156,3 +156,16 @@
}
return NULL;
}
+
+/**
+ * strchr - Find the first occurrence of the character c in the string s.
+ * @s: the string to be searched
+ * @c: the character to search for
+ */
+char *strchr(const char *s, int c)
+{
+ while (*s != (char)c)
+ if (*s++ == '\0')
+ return NULL;
+ return (char *)s;
+}
diff --git a/arch/x86/events/Makefile b/arch/x86/events/Makefile
index 1d392c3..b8ccdb5 100644
--- a/arch/x86/events/Makefile
+++ b/arch/x86/events/Makefile
@@ -1,11 +1,4 @@
-obj-y += core.o
-
-obj-$(CONFIG_CPU_SUP_AMD) += amd/core.o amd/uncore.o
-obj-$(CONFIG_PERF_EVENTS_AMD_POWER) += amd/power.o
-obj-$(CONFIG_X86_LOCAL_APIC) += amd/ibs.o msr.o
-ifdef CONFIG_AMD_IOMMU
-obj-$(CONFIG_CPU_SUP_AMD) += amd/iommu.o
-endif
-
-obj-$(CONFIG_CPU_SUP_INTEL) += msr.o
+obj-y += core.o
+obj-y += amd/
+obj-$(CONFIG_X86_LOCAL_APIC) += msr.o
obj-$(CONFIG_CPU_SUP_INTEL) += intel/
diff --git a/arch/x86/events/amd/Makefile b/arch/x86/events/amd/Makefile
new file mode 100644
index 0000000..b1da46f
--- /dev/null
+++ b/arch/x86/events/amd/Makefile
@@ -0,0 +1,7 @@
+obj-$(CONFIG_CPU_SUP_AMD) += core.o uncore.o
+obj-$(CONFIG_PERF_EVENTS_AMD_POWER) += power.o
+obj-$(CONFIG_X86_LOCAL_APIC) += ibs.o
+ifdef CONFIG_AMD_IOMMU
+obj-$(CONFIG_CPU_SUP_AMD) += iommu.o
+endif
+
diff --git a/arch/x86/events/amd/uncore.c b/arch/x86/events/amd/uncore.c
index a0b1bdb..4d1f7f2d 100644
--- a/arch/x86/events/amd/uncore.c
+++ b/arch/x86/events/amd/uncore.c
@@ -22,13 +22,17 @@
#define NUM_COUNTERS_NB 4
#define NUM_COUNTERS_L2 4
-#define MAX_COUNTERS NUM_COUNTERS_NB
+#define NUM_COUNTERS_L3 6
+#define MAX_COUNTERS 6
#define RDPMC_BASE_NB 6
-#define RDPMC_BASE_L2 10
+#define RDPMC_BASE_LLC 10
#define COUNTER_SHIFT 16
+static int num_counters_llc;
+static int num_counters_nb;
+
static HLIST_HEAD(uncore_unused_list);
struct amd_uncore {
@@ -45,30 +49,30 @@
};
static struct amd_uncore * __percpu *amd_uncore_nb;
-static struct amd_uncore * __percpu *amd_uncore_l2;
+static struct amd_uncore * __percpu *amd_uncore_llc;
static struct pmu amd_nb_pmu;
-static struct pmu amd_l2_pmu;
+static struct pmu amd_llc_pmu;
static cpumask_t amd_nb_active_mask;
-static cpumask_t amd_l2_active_mask;
+static cpumask_t amd_llc_active_mask;
static bool is_nb_event(struct perf_event *event)
{
return event->pmu->type == amd_nb_pmu.type;
}
-static bool is_l2_event(struct perf_event *event)
+static bool is_llc_event(struct perf_event *event)
{
- return event->pmu->type == amd_l2_pmu.type;
+ return event->pmu->type == amd_llc_pmu.type;
}
static struct amd_uncore *event_to_amd_uncore(struct perf_event *event)
{
if (is_nb_event(event) && amd_uncore_nb)
return *per_cpu_ptr(amd_uncore_nb, event->cpu);
- else if (is_l2_event(event) && amd_uncore_l2)
- return *per_cpu_ptr(amd_uncore_l2, event->cpu);
+ else if (is_llc_event(event) && amd_uncore_llc)
+ return *per_cpu_ptr(amd_uncore_llc, event->cpu);
return NULL;
}
@@ -183,16 +187,16 @@
return -ENOENT;
/*
- * NB and L2 counters (MSRs) are shared across all cores that share the
- * same NB / L2 cache. Interrupts can be directed to a single target
- * core, however, event counts generated by processes running on other
- * cores cannot be masked out. So we do not support sampling and
- * per-thread events.
+ * NB and Last level cache counters (MSRs) are shared across all cores
+ * that share the same NB / Last level cache. Interrupts can be directed
+ * to a single target core, however, event counts generated by processes
+ * running on other cores cannot be masked out. So we do not support
+ * sampling and per-thread events.
*/
if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
return -EINVAL;
- /* NB and L2 counters do not have usr/os/guest/host bits */
+ /* NB and Last level cache counters do not have usr/os/guest/host bits */
if (event->attr.exclude_user || event->attr.exclude_kernel ||
event->attr.exclude_host || event->attr.exclude_guest)
return -EINVAL;
@@ -226,8 +230,8 @@
if (pmu->type == amd_nb_pmu.type)
active_mask = &amd_nb_active_mask;
- else if (pmu->type == amd_l2_pmu.type)
- active_mask = &amd_l2_active_mask;
+ else if (pmu->type == amd_llc_pmu.type)
+ active_mask = &amd_llc_active_mask;
else
return 0;
@@ -244,30 +248,47 @@
.attrs = amd_uncore_attrs,
};
-PMU_FORMAT_ATTR(event, "config:0-7,32-35");
-PMU_FORMAT_ATTR(umask, "config:8-15");
+/*
+ * Similar to PMU_FORMAT_ATTR but allowing for format_attr to be assigned based
+ * on family
+ */
+#define AMD_FORMAT_ATTR(_dev, _name, _format) \
+static ssize_t \
+_dev##_show##_name(struct device *dev, \
+ struct device_attribute *attr, \
+ char *page) \
+{ \
+ BUILD_BUG_ON(sizeof(_format) >= PAGE_SIZE); \
+ return sprintf(page, _format "\n"); \
+} \
+static struct device_attribute format_attr_##_dev##_name = __ATTR_RO(_dev);
-static struct attribute *amd_uncore_format_attr[] = {
- &format_attr_event.attr,
- &format_attr_umask.attr,
- NULL,
+/* Used for each uncore counter type */
+#define AMD_ATTRIBUTE(_name) \
+static struct attribute *amd_uncore_format_attr_##_name[] = { \
+ &format_attr_event_##_name.attr, \
+ &format_attr_umask.attr, \
+ NULL, \
+}; \
+static struct attribute_group amd_uncore_format_group_##_name = { \
+ .name = "format", \
+ .attrs = amd_uncore_format_attr_##_name, \
+}; \
+static const struct attribute_group *amd_uncore_attr_groups_##_name[] = { \
+ &amd_uncore_attr_group, \
+ &amd_uncore_format_group_##_name, \
+ NULL, \
};
-static struct attribute_group amd_uncore_format_group = {
- .name = "format",
- .attrs = amd_uncore_format_attr,
-};
-
-static const struct attribute_group *amd_uncore_attr_groups[] = {
- &amd_uncore_attr_group,
- &amd_uncore_format_group,
- NULL,
-};
+AMD_FORMAT_ATTR(event, , "config:0-7,32-35");
+AMD_FORMAT_ATTR(umask, , "config:8-15");
+AMD_FORMAT_ATTR(event, _df, "config:0-7,32-35,59-60");
+AMD_FORMAT_ATTR(event, _l3, "config:0-7");
+AMD_ATTRIBUTE(df);
+AMD_ATTRIBUTE(l3);
static struct pmu amd_nb_pmu = {
.task_ctx_nr = perf_invalid_context,
- .attr_groups = amd_uncore_attr_groups,
- .name = "amd_nb",
.event_init = amd_uncore_event_init,
.add = amd_uncore_add,
.del = amd_uncore_del,
@@ -276,10 +297,8 @@
.read = amd_uncore_read,
};
-static struct pmu amd_l2_pmu = {
+static struct pmu amd_llc_pmu = {
.task_ctx_nr = perf_invalid_context,
- .attr_groups = amd_uncore_attr_groups,
- .name = "amd_l2",
.event_init = amd_uncore_event_init,
.add = amd_uncore_add,
.del = amd_uncore_del,
@@ -296,14 +315,14 @@
static int amd_uncore_cpu_up_prepare(unsigned int cpu)
{
- struct amd_uncore *uncore_nb = NULL, *uncore_l2;
+ struct amd_uncore *uncore_nb = NULL, *uncore_llc;
if (amd_uncore_nb) {
uncore_nb = amd_uncore_alloc(cpu);
if (!uncore_nb)
goto fail;
uncore_nb->cpu = cpu;
- uncore_nb->num_counters = NUM_COUNTERS_NB;
+ uncore_nb->num_counters = num_counters_nb;
uncore_nb->rdpmc_base = RDPMC_BASE_NB;
uncore_nb->msr_base = MSR_F15H_NB_PERF_CTL;
uncore_nb->active_mask = &amd_nb_active_mask;
@@ -312,18 +331,18 @@
*per_cpu_ptr(amd_uncore_nb, cpu) = uncore_nb;
}
- if (amd_uncore_l2) {
- uncore_l2 = amd_uncore_alloc(cpu);
- if (!uncore_l2)
+ if (amd_uncore_llc) {
+ uncore_llc = amd_uncore_alloc(cpu);
+ if (!uncore_llc)
goto fail;
- uncore_l2->cpu = cpu;
- uncore_l2->num_counters = NUM_COUNTERS_L2;
- uncore_l2->rdpmc_base = RDPMC_BASE_L2;
- uncore_l2->msr_base = MSR_F16H_L2I_PERF_CTL;
- uncore_l2->active_mask = &amd_l2_active_mask;
- uncore_l2->pmu = &amd_l2_pmu;
- uncore_l2->id = -1;
- *per_cpu_ptr(amd_uncore_l2, cpu) = uncore_l2;
+ uncore_llc->cpu = cpu;
+ uncore_llc->num_counters = num_counters_llc;
+ uncore_llc->rdpmc_base = RDPMC_BASE_LLC;
+ uncore_llc->msr_base = MSR_F16H_L2I_PERF_CTL;
+ uncore_llc->active_mask = &amd_llc_active_mask;
+ uncore_llc->pmu = &amd_llc_pmu;
+ uncore_llc->id = -1;
+ *per_cpu_ptr(amd_uncore_llc, cpu) = uncore_llc;
}
return 0;
@@ -376,17 +395,17 @@
*per_cpu_ptr(amd_uncore_nb, cpu) = uncore;
}
- if (amd_uncore_l2) {
+ if (amd_uncore_llc) {
unsigned int apicid = cpu_data(cpu).apicid;
unsigned int nshared;
- uncore = *per_cpu_ptr(amd_uncore_l2, cpu);
+ uncore = *per_cpu_ptr(amd_uncore_llc, cpu);
cpuid_count(0x8000001d, 2, &eax, &ebx, &ecx, &edx);
nshared = ((eax >> 14) & 0xfff) + 1;
uncore->id = apicid - (apicid % nshared);
- uncore = amd_uncore_find_online_sibling(uncore, amd_uncore_l2);
- *per_cpu_ptr(amd_uncore_l2, cpu) = uncore;
+ uncore = amd_uncore_find_online_sibling(uncore, amd_uncore_llc);
+ *per_cpu_ptr(amd_uncore_llc, cpu) = uncore;
}
return 0;
@@ -419,8 +438,8 @@
if (amd_uncore_nb)
uncore_online(cpu, amd_uncore_nb);
- if (amd_uncore_l2)
- uncore_online(cpu, amd_uncore_l2);
+ if (amd_uncore_llc)
+ uncore_online(cpu, amd_uncore_llc);
return 0;
}
@@ -456,8 +475,8 @@
if (amd_uncore_nb)
uncore_down_prepare(cpu, amd_uncore_nb);
- if (amd_uncore_l2)
- uncore_down_prepare(cpu, amd_uncore_l2);
+ if (amd_uncore_llc)
+ uncore_down_prepare(cpu, amd_uncore_llc);
return 0;
}
@@ -479,8 +498,8 @@
if (amd_uncore_nb)
uncore_dead(cpu, amd_uncore_nb);
- if (amd_uncore_l2)
- uncore_dead(cpu, amd_uncore_l2);
+ if (amd_uncore_llc)
+ uncore_dead(cpu, amd_uncore_llc);
return 0;
}
@@ -492,6 +511,47 @@
if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
goto fail_nodev;
+ switch(boot_cpu_data.x86) {
+ case 23:
+ /* Family 17h: */
+ num_counters_nb = NUM_COUNTERS_NB;
+ num_counters_llc = NUM_COUNTERS_L3;
+ /*
+ * For Family17h, the NorthBridge counters are
+ * re-purposed as Data Fabric counters. Also, support is
+ * added for L3 counters. The pmus are exported based on
+ * family as either L2 or L3 and NB or DF.
+ */
+ amd_nb_pmu.name = "amd_df";
+ amd_llc_pmu.name = "amd_l3";
+ format_attr_event_df.show = &event_show_df;
+ format_attr_event_l3.show = &event_show_l3;
+ break;
+ case 22:
+ /* Family 16h - may change: */
+ num_counters_nb = NUM_COUNTERS_NB;
+ num_counters_llc = NUM_COUNTERS_L2;
+ amd_nb_pmu.name = "amd_nb";
+ amd_llc_pmu.name = "amd_l2";
+ format_attr_event_df = format_attr_event;
+ format_attr_event_l3 = format_attr_event;
+ break;
+ default:
+ /*
+ * All prior families have the same number of
+ * NorthBridge and Last Level Cache counters
+ */
+ num_counters_nb = NUM_COUNTERS_NB;
+ num_counters_llc = NUM_COUNTERS_L2;
+ amd_nb_pmu.name = "amd_nb";
+ amd_llc_pmu.name = "amd_l2";
+ format_attr_event_df = format_attr_event;
+ format_attr_event_l3 = format_attr_event;
+ break;
+ }
+ amd_nb_pmu.attr_groups = amd_uncore_attr_groups_df;
+ amd_llc_pmu.attr_groups = amd_uncore_attr_groups_l3;
+
if (!boot_cpu_has(X86_FEATURE_TOPOEXT))
goto fail_nodev;
@@ -510,16 +570,16 @@
}
if (boot_cpu_has(X86_FEATURE_PERFCTR_L2)) {
- amd_uncore_l2 = alloc_percpu(struct amd_uncore *);
- if (!amd_uncore_l2) {
+ amd_uncore_llc = alloc_percpu(struct amd_uncore *);
+ if (!amd_uncore_llc) {
ret = -ENOMEM;
- goto fail_l2;
+ goto fail_llc;
}
- ret = perf_pmu_register(&amd_l2_pmu, amd_l2_pmu.name, -1);
+ ret = perf_pmu_register(&amd_llc_pmu, amd_llc_pmu.name, -1);
if (ret)
- goto fail_l2;
+ goto fail_llc;
- pr_info("perf: AMD L2I counters detected\n");
+ pr_info("perf: AMD LLC counters detected\n");
ret = 0;
}
@@ -529,7 +589,7 @@
if (cpuhp_setup_state(CPUHP_PERF_X86_AMD_UNCORE_PREP,
"perf/x86/amd/uncore:prepare",
amd_uncore_cpu_up_prepare, amd_uncore_cpu_dead))
- goto fail_l2;
+ goto fail_llc;
if (cpuhp_setup_state(CPUHP_AP_PERF_X86_AMD_UNCORE_STARTING,
"perf/x86/amd/uncore:starting",
@@ -546,11 +606,11 @@
cpuhp_remove_state(CPUHP_AP_PERF_X86_AMD_UNCORE_STARTING);
fail_prep:
cpuhp_remove_state(CPUHP_PERF_X86_AMD_UNCORE_PREP);
-fail_l2:
+fail_llc:
if (boot_cpu_has(X86_FEATURE_PERFCTR_NB))
perf_pmu_unregister(&amd_nb_pmu);
- if (amd_uncore_l2)
- free_percpu(amd_uncore_l2);
+ if (amd_uncore_llc)
+ free_percpu(amd_uncore_llc);
fail_nb:
if (amd_uncore_nb)
free_percpu(amd_uncore_nb);
diff --git a/arch/x86/events/intel/cstate.c b/arch/x86/events/intel/cstate.c
index 1076c9a..aff4b5b 100644
--- a/arch/x86/events/intel/cstate.c
+++ b/arch/x86/events/intel/cstate.c
@@ -541,6 +541,9 @@
X86_CSTATES_MODEL(INTEL_FAM6_SKYLAKE_MOBILE, snb_cstates),
X86_CSTATES_MODEL(INTEL_FAM6_SKYLAKE_DESKTOP, snb_cstates),
+ X86_CSTATES_MODEL(INTEL_FAM6_KABYLAKE_MOBILE, snb_cstates),
+ X86_CSTATES_MODEL(INTEL_FAM6_KABYLAKE_DESKTOP, snb_cstates),
+
X86_CSTATES_MODEL(INTEL_FAM6_XEON_PHI_KNL, knl_cstates),
X86_CSTATES_MODEL(INTEL_FAM6_XEON_PHI_KNM, knl_cstates),
{ },
diff --git a/arch/x86/events/intel/pt.c b/arch/x86/events/intel/pt.c
index 1c1b9fe..5900471 100644
--- a/arch/x86/events/intel/pt.c
+++ b/arch/x86/events/intel/pt.c
@@ -99,18 +99,24 @@
};
PMU_FORMAT_ATTR(cyc, "config:1" );
+PMU_FORMAT_ATTR(pwr_evt, "config:4" );
+PMU_FORMAT_ATTR(fup_on_ptw, "config:5" );
PMU_FORMAT_ATTR(mtc, "config:9" );
PMU_FORMAT_ATTR(tsc, "config:10" );
PMU_FORMAT_ATTR(noretcomp, "config:11" );
+PMU_FORMAT_ATTR(ptw, "config:12" );
PMU_FORMAT_ATTR(mtc_period, "config:14-17" );
PMU_FORMAT_ATTR(cyc_thresh, "config:19-22" );
PMU_FORMAT_ATTR(psb_period, "config:24-27" );
static struct attribute *pt_formats_attr[] = {
&format_attr_cyc.attr,
+ &format_attr_pwr_evt.attr,
+ &format_attr_fup_on_ptw.attr,
&format_attr_mtc.attr,
&format_attr_tsc.attr,
&format_attr_noretcomp.attr,
+ &format_attr_ptw.attr,
&format_attr_mtc_period.attr,
&format_attr_cyc_thresh.attr,
&format_attr_psb_period.attr,
diff --git a/arch/x86/events/intel/rapl.c b/arch/x86/events/intel/rapl.c
index 22ef4f7..22054ca 100644
--- a/arch/x86/events/intel/rapl.c
+++ b/arch/x86/events/intel/rapl.c
@@ -771,6 +771,9 @@
X86_RAPL_MODEL_MATCH(INTEL_FAM6_SKYLAKE_DESKTOP, skl_rapl_init),
X86_RAPL_MODEL_MATCH(INTEL_FAM6_SKYLAKE_X, hsx_rapl_init),
+ X86_RAPL_MODEL_MATCH(INTEL_FAM6_KABYLAKE_MOBILE, skl_rapl_init),
+ X86_RAPL_MODEL_MATCH(INTEL_FAM6_KABYLAKE_DESKTOP, skl_rapl_init),
+
X86_RAPL_MODEL_MATCH(INTEL_FAM6_ATOM_GOLDMONT, hsw_rapl_init),
{},
};
diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c
index 1ab4597..758c1aa 100644
--- a/arch/x86/events/intel/uncore.c
+++ b/arch/x86/events/intel/uncore.c
@@ -1328,6 +1328,8 @@
X86_UNCORE_MODEL_MATCH(INTEL_FAM6_SKYLAKE_DESKTOP,skl_uncore_init),
X86_UNCORE_MODEL_MATCH(INTEL_FAM6_SKYLAKE_MOBILE, skl_uncore_init),
X86_UNCORE_MODEL_MATCH(INTEL_FAM6_SKYLAKE_X, skx_uncore_init),
+ X86_UNCORE_MODEL_MATCH(INTEL_FAM6_KABYLAKE_MOBILE, skl_uncore_init),
+ X86_UNCORE_MODEL_MATCH(INTEL_FAM6_KABYLAKE_DESKTOP, skl_uncore_init),
{},
};
diff --git a/arch/x86/include/asm/Kbuild b/arch/x86/include/asm/Kbuild
index 2b892e2..5d6a53f 100644
--- a/arch/x86/include/asm/Kbuild
+++ b/arch/x86/include/asm/Kbuild
@@ -7,7 +7,6 @@
generated-y += xen-hypercalls.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += dma-contiguous.h
generic-y += early_ioremap.h
generic-y += mcs_spinlock.h
diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h
index 0c5fbc6..eff8e36 100644
--- a/arch/x86/include/asm/apic.h
+++ b/arch/x86/include/asm/apic.h
@@ -195,7 +195,7 @@
static inline void native_apic_msr_eoi_write(u32 reg, u32 v)
{
- wrmsr_notrace(APIC_BASE_MSR + (APIC_EOI >> 4), APIC_EOI_ACK, 0);
+ __wrmsr(APIC_BASE_MSR + (APIC_EOI >> 4), APIC_EOI_ACK, 0);
}
static inline u32 native_apic_msr_read(u32 reg)
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index eafee31..4e77723 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -100,7 +100,7 @@
#define X86_FEATURE_XTOPOLOGY ( 3*32+22) /* cpu topology enum extensions */
#define X86_FEATURE_TSC_RELIABLE ( 3*32+23) /* TSC is known to be reliable */
#define X86_FEATURE_NONSTOP_TSC ( 3*32+24) /* TSC does not stop in C states */
-/* free, was #define X86_FEATURE_CLFLUSH_MONITOR ( 3*32+25) * "" clflush reqd with monitor */
+#define X86_FEATURE_CPUID ( 3*32+25) /* CPU has CPUID instruction itself */
#define X86_FEATURE_EXTD_APICID ( 3*32+26) /* has extended APICID (8 bits) */
#define X86_FEATURE_AMD_DCM ( 3*32+27) /* multi-node processor */
#define X86_FEATURE_APERFMPERF ( 3*32+28) /* APERFMPERF */
@@ -186,7 +186,7 @@
*
* Reuse free bits when adding new feature flags!
*/
-
+#define X86_FEATURE_RING3MWAIT ( 7*32+ 0) /* Ring 3 MONITOR/MWAIT */
#define X86_FEATURE_CPB ( 7*32+ 2) /* AMD Core Performance Boost */
#define X86_FEATURE_EPB ( 7*32+ 3) /* IA32_ENERGY_PERF_BIAS support */
#define X86_FEATURE_CAT_L3 ( 7*32+ 4) /* Cache Allocation Technology L3 */
@@ -288,6 +288,7 @@
#define X86_FEATURE_AVX512VBMI (16*32+ 1) /* AVX512 Vector Bit Manipulation instructions*/
#define X86_FEATURE_PKU (16*32+ 3) /* Protection Keys for Userspace */
#define X86_FEATURE_OSPKE (16*32+ 4) /* OS Protection Keys Enable */
+#define X86_FEATURE_AVX512_VPOPCNTDQ (16*32+14) /* POPCNT for vectors of DW/QW */
#define X86_FEATURE_RDPID (16*32+ 22) /* RDPID instruction */
/* AMD-defined CPU features, CPUID level 0x80000007 (ebx), word 17 */
@@ -320,5 +321,4 @@
#define X86_BUG_SWAPGS_FENCE X86_BUG(11) /* SWAPGS without input dep on GS */
#define X86_BUG_MONITOR X86_BUG(12) /* IPI required to wake up remote CPU */
#define X86_BUG_AMD_E400 X86_BUG(13) /* CPU is among the affected by Erratum 400 */
-
#endif /* _ASM_X86_CPUFEATURES_H */
diff --git a/arch/x86/include/asm/div64.h b/arch/x86/include/asm/div64.h
index ced283a..af95c47 100644
--- a/arch/x86/include/asm/div64.h
+++ b/arch/x86/include/asm/div64.h
@@ -59,6 +59,17 @@
}
#define div_u64_rem div_u64_rem
+static inline u64 mul_u32_u32(u32 a, u32 b)
+{
+ u32 high, low;
+
+ asm ("mull %[b]" : "=a" (low), "=d" (high)
+ : [a] "a" (a), [b] "rm" (b) );
+
+ return low | ((u64)high) << 32;
+}
+#define mul_u32_u32 mul_u32_u32
+
#else
# include <asm-generic/div64.h>
#endif /* CONFIG_X86_32 */
diff --git a/arch/x86/include/asm/e820.h b/arch/x86/include/asm/e820.h
index ec23d8e..67313f3 100644
--- a/arch/x86/include/asm/e820.h
+++ b/arch/x86/include/asm/e820.h
@@ -30,8 +30,6 @@
int checktype);
extern void update_e820(void);
extern void e820_setup_gap(void);
-extern int e820_search_gap(unsigned long *gapstart, unsigned long *gapsize,
- unsigned long start_addr, unsigned long long end_addr);
struct setup_data;
extern void parse_e820_ext(u64 phys_addr, u32 data_len);
diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
index e99675b..2f77bce 100644
--- a/arch/x86/include/asm/efi.h
+++ b/arch/x86/include/asm/efi.h
@@ -191,6 +191,7 @@
struct efi_config {
u64 image_handle;
u64 table;
+ u64 runtime_services;
u64 boot_services;
u64 text_output;
efi_status_t (*call)(unsigned long, ...);
@@ -226,6 +227,10 @@
#define __efi_call_early(f, ...) \
__efi_early()->call((unsigned long)f, __VA_ARGS__);
+#define efi_call_runtime(f, ...) \
+ __efi_early()->call(efi_table_attr(efi_runtime_services, f, \
+ __efi_early()->runtime_services), __VA_ARGS__)
+
extern bool efi_reboot_required(void);
#else
diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
index e7f155c..9d49c18 100644
--- a/arch/x86/include/asm/elf.h
+++ b/arch/x86/include/asm/elf.h
@@ -258,6 +258,15 @@
#define ELF_HWCAP (boot_cpu_data.x86_capability[CPUID_1_EDX])
+extern u32 elf_hwcap2;
+
+/*
+ * HWCAP2 supplies mask with kernel enabled CPU features, so that
+ * the application can discover that it can safely use them.
+ * The bits are defined in uapi/asm/hwcap2.h.
+ */
+#define ELF_HWCAP2 (elf_hwcap2)
+
/* This yields a string that ld.so will use to load implementation
specific libraries for optimization. This is more specific in
intent than poking at uname or /proc/cpuinfo.
diff --git a/arch/x86/include/asm/fpu/internal.h b/arch/x86/include/asm/fpu/internal.h
index d4a6849..255645f 100644
--- a/arch/x86/include/asm/fpu/internal.h
+++ b/arch/x86/include/asm/fpu/internal.h
@@ -87,6 +87,16 @@
#else
static inline void fpstate_init_soft(struct swregs_state *soft) {}
#endif
+
+static inline void fpstate_init_xstate(struct xregs_state *xsave)
+{
+ /*
+ * XRSTORS requires these bits set in xcomp_bv, or it will
+ * trigger #GP:
+ */
+ xsave->header.xcomp_bv = XCOMP_BV_COMPACTED_FORMAT | xfeatures_mask;
+}
+
static inline void fpstate_init_fxstate(struct fxregs_state *fx)
{
fx->cwd = 0x37f;
diff --git a/arch/x86/include/asm/intel-mid.h b/arch/x86/include/asm/intel-mid.h
index 49da9f4..fe04491 100644
--- a/arch/x86/include/asm/intel-mid.h
+++ b/arch/x86/include/asm/intel-mid.h
@@ -27,7 +27,6 @@
extern int intel_mid_pwr_get_lss_id(struct pci_dev *pdev);
extern int get_gpio_by_name(const char *name);
-extern void intel_scu_device_register(struct platform_device *pdev);
extern int __init sfi_parse_mrtc(struct sfi_table_header *table);
extern int __init sfi_parse_mtmr(struct sfi_table_header *table);
extern int sfi_mrtc_num;
@@ -42,10 +41,8 @@
char name[SFI_NAME_LEN + 1];
u8 type;
u8 delay;
+ u8 msic;
void *(*get_platform_data)(void *info);
- /* Custom handler for devices */
- void (*device_handler)(struct sfi_device_table_entry *pentry,
- struct devs_id *dev);
};
#define sfi_device(i) \
diff --git a/arch/x86/include/asm/io.h b/arch/x86/include/asm/io.h
index d34bd37..7afb0e2 100644
--- a/arch/x86/include/asm/io.h
+++ b/arch/x86/include/asm/io.h
@@ -164,6 +164,17 @@
#define virt_to_bus virt_to_phys
#define bus_to_virt phys_to_virt
+/*
+ * The default ioremap() behavior is non-cached; if you need something
+ * else, you probably want one of the following.
+ */
+extern void __iomem *ioremap_nocache(resource_size_t offset, unsigned long size);
+extern void __iomem *ioremap_uc(resource_size_t offset, unsigned long size);
+#define ioremap_uc ioremap_uc
+
+extern void __iomem *ioremap_cache(resource_size_t offset, unsigned long size);
+extern void __iomem *ioremap_prot(resource_size_t offset, unsigned long size, unsigned long prot_val);
+
/**
* ioremap - map bus memory into CPU space
* @offset: bus address of the memory
@@ -178,17 +189,6 @@
* If the area you are trying to map is a PCI BAR you should have a
* look at pci_iomap().
*/
-extern void __iomem *ioremap_nocache(resource_size_t offset, unsigned long size);
-extern void __iomem *ioremap_uc(resource_size_t offset, unsigned long size);
-#define ioremap_uc ioremap_uc
-
-extern void __iomem *ioremap_cache(resource_size_t offset, unsigned long size);
-extern void __iomem *ioremap_prot(resource_size_t offset, unsigned long size,
- unsigned long prot_val);
-
-/*
- * The default ioremap() behavior is non-cached:
- */
static inline void __iomem *ioremap(resource_size_t offset, unsigned long size)
{
return ioremap_nocache(offset, size);
@@ -207,18 +207,42 @@
*/
#define xlate_dev_kmem_ptr(p) p
+/**
+ * memset_io Set a range of I/O memory to a constant value
+ * @addr: The beginning of the I/O-memory range to set
+ * @val: The value to set the memory to
+ * @count: The number of bytes to set
+ *
+ * Set a range of I/O memory to a given value.
+ */
static inline void
memset_io(volatile void __iomem *addr, unsigned char val, size_t count)
{
memset((void __force *)addr, val, count);
}
+/**
+ * memcpy_fromio Copy a block of data from I/O memory
+ * @dst: The (RAM) destination for the copy
+ * @src: The (I/O memory) source for the data
+ * @count: The number of bytes to copy
+ *
+ * Copy a block of data from I/O memory.
+ */
static inline void
memcpy_fromio(void *dst, const volatile void __iomem *src, size_t count)
{
memcpy(dst, (const void __force *)src, count);
}
+/**
+ * memcpy_toio Copy a block of data into I/O memory
+ * @dst: The (I/O memory) destination for the copy
+ * @src: The (RAM) source for the data
+ * @count: The number of bytes to copy
+ *
+ * Copy a block of data to I/O memory.
+ */
static inline void
memcpy_toio(volatile void __iomem *dst, const void *src, size_t count)
{
diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h
index 5132f2a..e638736 100644
--- a/arch/x86/include/asm/mce.h
+++ b/arch/x86/include/asm/mce.h
@@ -97,10 +97,6 @@
#define MCE_OVERFLOW 0 /* bit 0 in flags means overflow */
-/* Software defined banks */
-#define MCE_EXTENDED_BANK 128
-#define MCE_THERMAL_BANK (MCE_EXTENDED_BANK + 0)
-
#define MCE_LOG_LEN 32
#define MCE_LOG_SIGNATURE "MACHINECHECK"
@@ -193,6 +189,15 @@
extern struct mca_config mca_cfg;
extern struct mca_msr_regs msr_ops;
+
+enum mce_notifier_prios {
+ MCE_PRIO_SRAO = INT_MAX,
+ MCE_PRIO_EXTLOG = INT_MAX - 1,
+ MCE_PRIO_NFIT = INT_MAX - 2,
+ MCE_PRIO_EDAC = INT_MAX - 3,
+ MCE_PRIO_LOWEST = 0,
+};
+
extern void mce_register_decode_chain(struct notifier_block *nb);
extern void mce_unregister_decode_chain(struct notifier_block *nb);
@@ -306,8 +311,6 @@
void intel_init_thermal(struct cpuinfo_x86 *c);
-void mce_log_therm_throt_event(__u64 status);
-
/* Interrupt Handler for core thermal thresholds */
extern int (*platform_thermal_notify)(__u64 msr_val);
@@ -362,12 +365,13 @@
unsigned int bank_type; /* Use with smca_bank_types for easy indexing. */
u32 hwid_mcatype; /* (hwid,mcatype) tuple */
u32 xec_bitmap; /* Bitmap of valid ExtErrorCodes; current max is 21. */
+ u8 count; /* Number of instances. */
};
struct smca_bank {
struct smca_hwid *hwid;
- /* Instance ID */
- u32 id;
+ u32 id; /* Value of MCA_IPID[InstanceId]. */
+ u8 sysfs_id; /* Value used for sysfs name. */
};
extern struct smca_bank smca_banks[MAX_NR_BANKS];
diff --git a/arch/x86/include/asm/microcode.h b/arch/x86/include/asm/microcode.h
index 2266f86..daadeee 100644
--- a/arch/x86/include/asm/microcode.h
+++ b/arch/x86/include/asm/microcode.h
@@ -7,18 +7,17 @@
#define native_rdmsr(msr, val1, val2) \
do { \
- u64 __val = native_read_msr((msr)); \
+ u64 __val = __rdmsr((msr)); \
(void)((val1) = (u32)__val); \
(void)((val2) = (u32)(__val >> 32)); \
} while (0)
#define native_wrmsr(msr, low, high) \
- native_write_msr(msr, low, high)
+ __wrmsr(msr, low, high)
#define native_wrmsrl(msr, val) \
- native_write_msr((msr), \
- (u32)((u64)(val)), \
- (u32)((u64)(val) >> 32))
+ __wrmsr((msr), (u32)((u64)(val)), \
+ (u32)((u64)(val) >> 32))
struct ucode_patch {
struct list_head plist;
diff --git a/arch/x86/include/asm/microcode_amd.h b/arch/x86/include/asm/microcode_amd.h
index 3e3e20b..3d57009 100644
--- a/arch/x86/include/asm/microcode_amd.h
+++ b/arch/x86/include/asm/microcode_amd.h
@@ -54,6 +54,4 @@
save_microcode_in_initrd_amd(unsigned int family) { return -EINVAL; }
void reload_ucode_amd(void) {}
#endif
-
-extern bool check_current_patch_level(u32 *rev, bool early);
#endif /* _ASM_X86_MICROCODE_AMD_H */
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 710273c..00293a9 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -543,6 +543,11 @@
#define MSR_IA32_MISC_ENABLE_IP_PREF_DISABLE_BIT 39
#define MSR_IA32_MISC_ENABLE_IP_PREF_DISABLE (1ULL << MSR_IA32_MISC_ENABLE_IP_PREF_DISABLE_BIT)
+/* MISC_FEATURE_ENABLES non-architectural features */
+#define MSR_MISC_FEATURE_ENABLES 0x00000140
+
+#define MSR_MISC_FEATURE_ENABLES_RING3MWAIT_BIT 1
+
#define MSR_IA32_TSC_DEADLINE 0x000006E0
/* P4/Xeon+ specific */
diff --git a/arch/x86/include/asm/msr.h b/arch/x86/include/asm/msr.h
index db0b90c..898dba2 100644
--- a/arch/x86/include/asm/msr.h
+++ b/arch/x86/include/asm/msr.h
@@ -80,7 +80,14 @@
static inline void do_trace_rdpmc(unsigned int msr, u64 val, int failed) {}
#endif
-static inline unsigned long long native_read_msr(unsigned int msr)
+/*
+ * __rdmsr() and __wrmsr() are the two primitives which are the bare minimum MSR
+ * accessors and should not have any tracing or other functionality piggybacking
+ * on them - those are *purely* for accessing MSRs and nothing more. So don't even
+ * think of extending them - you will be slapped with a stinking trout or a frozen
+ * shark will reach you, wherever you are! You've been warned.
+ */
+static inline unsigned long long notrace __rdmsr(unsigned int msr)
{
DECLARE_ARGS(val, low, high);
@@ -88,11 +95,30 @@
"2:\n"
_ASM_EXTABLE_HANDLE(1b, 2b, ex_handler_rdmsr_unsafe)
: EAX_EDX_RET(val, low, high) : "c" (msr));
- if (msr_tracepoint_active(__tracepoint_read_msr))
- do_trace_read_msr(msr, EAX_EDX_VAL(val, low, high), 0);
+
return EAX_EDX_VAL(val, low, high);
}
+static inline void notrace __wrmsr(unsigned int msr, u32 low, u32 high)
+{
+ asm volatile("1: wrmsr\n"
+ "2:\n"
+ _ASM_EXTABLE_HANDLE(1b, 2b, ex_handler_wrmsr_unsafe)
+ : : "c" (msr), "a"(low), "d" (high) : "memory");
+}
+
+static inline unsigned long long native_read_msr(unsigned int msr)
+{
+ unsigned long long val;
+
+ val = __rdmsr(msr);
+
+ if (msr_tracepoint_active(__tracepoint_read_msr))
+ do_trace_read_msr(msr, val, 0);
+
+ return val;
+}
+
static inline unsigned long long native_read_msr_safe(unsigned int msr,
int *err)
{
@@ -116,29 +142,14 @@
/* Can be uninlined because referenced by paravirt */
static inline void notrace
-__native_write_msr_notrace(unsigned int msr, u32 low, u32 high)
-{
- asm volatile("1: wrmsr\n"
- "2:\n"
- _ASM_EXTABLE_HANDLE(1b, 2b, ex_handler_wrmsr_unsafe)
- : : "c" (msr), "a"(low), "d" (high) : "memory");
-}
-
-/* Can be uninlined because referenced by paravirt */
-static inline void notrace
native_write_msr(unsigned int msr, u32 low, u32 high)
{
- __native_write_msr_notrace(msr, low, high);
+ __wrmsr(msr, low, high);
+
if (msr_tracepoint_active(__tracepoint_write_msr))
do_trace_write_msr(msr, ((u64)high << 32 | low), 0);
}
-static inline void
-wrmsr_notrace(unsigned int msr, u32 low, u32 high)
-{
- __native_write_msr_notrace(msr, low, high);
-}
-
/* Can be uninlined because referenced by paravirt */
static inline int notrace
native_write_msr_safe(unsigned int msr, u32 low, u32 high)
diff --git a/arch/x86/include/asm/pgtable_32.h b/arch/x86/include/asm/pgtable_32.h
index b6c0b40..fbc7336 100644
--- a/arch/x86/include/asm/pgtable_32.h
+++ b/arch/x86/include/asm/pgtable_32.h
@@ -27,6 +27,7 @@
extern pgd_t swapper_pg_dir[1024];
extern pgd_t initial_page_table[1024];
+extern pmd_t initial_pg_pmd[];
static inline void pgtable_cache_init(void) { }
static inline void check_pgt_cache(void) { }
@@ -75,4 +76,35 @@
#define kern_addr_valid(kaddr) (0)
#endif
+/*
+ * This is how much memory in addition to the memory covered up to
+ * and including _end we need mapped initially.
+ * We need:
+ * (KERNEL_IMAGE_SIZE/4096) / 1024 pages (worst case, non PAE)
+ * (KERNEL_IMAGE_SIZE/4096) / 512 + 4 pages (worst case for PAE)
+ *
+ * Modulo rounding, each megabyte assigned here requires a kilobyte of
+ * memory, which is currently unreclaimed.
+ *
+ * This should be a multiple of a page.
+ *
+ * KERNEL_IMAGE_SIZE should be greater than pa(_end)
+ * and small than max_low_pfn, otherwise will waste some page table entries
+ */
+#if PTRS_PER_PMD > 1
+#define PAGE_TABLE_SIZE(pages) (((pages) / PTRS_PER_PMD) + PTRS_PER_PGD)
+#else
+#define PAGE_TABLE_SIZE(pages) ((pages) / PTRS_PER_PGD)
+#endif
+
+/*
+ * Number of possible pages in the lowmem region.
+ *
+ * We shift 2 by 31 instead of 1 by 32 to the left in order to avoid a
+ * gas warning about overflowing shift count when gas has been compiled
+ * with only a host target support using a 32-bit type for internal
+ * representation.
+ */
+#define LOWMEM_PAGES ((((2<<31) - __PAGE_OFFSET) >> PAGE_SHIFT))
+
#endif /* _ASM_X86_PGTABLE_32_H */
diff --git a/arch/x86/include/asm/spinlock.h b/arch/x86/include/asm/spinlock.h
index 921bea7..6d39190 100644
--- a/arch/x86/include/asm/spinlock.h
+++ b/arch/x86/include/asm/spinlock.h
@@ -23,9 +23,6 @@
/* How long a lock should spin before we consider blocking */
#define SPIN_THRESHOLD (1 << 15)
-extern struct static_key paravirt_ticketlocks_enabled;
-static __always_inline bool static_key_false(struct static_key *key);
-
#include <asm/qspinlock.h>
/*
diff --git a/arch/x86/include/asm/uv/uv.h b/arch/x86/include/asm/uv/uv.h
index 062921e..6686820 100644
--- a/arch/x86/include/asm/uv/uv.h
+++ b/arch/x86/include/asm/uv/uv.h
@@ -10,6 +10,7 @@
extern enum uv_system_type get_uv_system_type(void);
extern int is_uv_system(void);
+extern int is_uv_hubless(void);
extern void uv_cpu_init(void);
extern void uv_nmi_init(void);
extern void uv_system_init(void);
@@ -23,6 +24,7 @@
static inline enum uv_system_type get_uv_system_type(void) { return UV_NONE; }
static inline int is_uv_system(void) { return 0; }
+static inline int is_uv_hubless(void) { return 0; }
static inline void uv_cpu_init(void) { }
static inline void uv_system_init(void) { }
static inline const struct cpumask *
diff --git a/arch/x86/include/asm/uv/uv_hub.h b/arch/x86/include/asm/uv/uv_hub.h
index 097b80c..72e8300 100644
--- a/arch/x86/include/asm/uv/uv_hub.h
+++ b/arch/x86/include/asm/uv/uv_hub.h
@@ -772,6 +772,7 @@
/* Per Hub NMI support */
extern void uv_nmi_setup(void);
+extern void uv_nmi_setup_hubless(void);
/* BMC sets a bit this MMR non-zero before sending an NMI */
#define UVH_NMI_MMR UVH_SCRATCH5
@@ -799,6 +800,8 @@
atomic_t read_mmr_count; /* count of MMR reads */
atomic_t nmi_count; /* count of true UV NMIs */
unsigned long nmi_value; /* last value read from NMI MMR */
+ bool hub_present; /* false means UV hubless system */
+ bool pch_owner; /* indicates this hub owns PCH */
};
struct uv_cpu_nmi_s {
diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h
index b10bf31..5138dac 100644
--- a/arch/x86/include/uapi/asm/bootparam.h
+++ b/arch/x86/include/uapi/asm/bootparam.h
@@ -135,7 +135,8 @@
__u8 eddbuf_entries; /* 0x1e9 */
__u8 edd_mbr_sig_buf_entries; /* 0x1ea */
__u8 kbd_status; /* 0x1eb */
- __u8 _pad5[3]; /* 0x1ec */
+ __u8 secure_boot; /* 0x1ec */
+ __u8 _pad5[2]; /* 0x1ed */
/*
* The sentinel is set to a nonzero value (0xff) in header.S.
*
diff --git a/arch/x86/include/uapi/asm/hwcap2.h b/arch/x86/include/uapi/asm/hwcap2.h
new file mode 100644
index 0000000..0bd2be5
--- /dev/null
+++ b/arch/x86/include/uapi/asm/hwcap2.h
@@ -0,0 +1,7 @@
+#ifndef _ASM_X86_HWCAP2_H
+#define _ASM_X86_HWCAP2_H
+
+/* MONITOR/MWAIT enabled in Ring 3 */
+#define HWCAP2_RING3MWAIT (1 << 0)
+
+#endif
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 581386c..bdcdb3b 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -101,7 +101,6 @@
obj-$(CONFIG_AMD_NB) += amd_nb.o
obj-$(CONFIG_DEBUG_RODATA_TEST) += test_rodata.o
-obj-$(CONFIG_DEBUG_NX_TEST) += test_nx.o
obj-$(CONFIG_DEBUG_NMI_SELFTEST) += nmi_selftest.o
obj-$(CONFIG_KVM_GUEST) += kvm.o kvmclock.o
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index 64422f8..7ff007e 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -35,6 +35,7 @@
#include <linux/bootmem.h>
#include <linux/ioport.h>
#include <linux/pci.h>
+#include <linux/efi-bgrt.h>
#include <asm/irqdomain.h>
#include <asm/pci_x86.h>
@@ -1557,6 +1558,12 @@
return 0;
}
+static int __init acpi_parse_bgrt(struct acpi_table_header *table)
+{
+ efi_bgrt_init(table);
+ return 0;
+}
+
int __init acpi_boot_init(void)
{
/* those are executed after early-quirks are executed */
@@ -1581,6 +1588,8 @@
acpi_process_madt();
acpi_table_parse(ACPI_SIG_HPET, acpi_parse_hpet);
+ if (IS_ENABLED(CONFIG_ACPI_BGRT))
+ acpi_table_parse(ACPI_SIG_BGRT, acpi_parse_bgrt);
if (!acpi_noirq)
x86_init.pci.init = pci_acpi_init;
diff --git a/arch/x86/kernel/acpi/cstate.c b/arch/x86/kernel/acpi/cstate.c
index af15f44..8233a63 100644
--- a/arch/x86/kernel/acpi/cstate.c
+++ b/arch/x86/kernel/acpi/cstate.c
@@ -12,7 +12,6 @@
#include <linux/sched.h>
#include <acpi/processor.h>
-#include <asm/acpi.h>
#include <asm/mwait.h>
#include <asm/special_insns.h>
@@ -89,7 +88,8 @@
retval = 0;
/* If the HW does not support any sub-states in this C-state */
if (num_cstate_subtype == 0) {
- pr_warn(FW_BUG "ACPI MWAIT C-state 0x%x not supported by HW (0x%x)\n", cx->address, edx_part);
+ pr_warn(FW_BUG "ACPI MWAIT C-state 0x%x not supported by HW (0x%x)\n",
+ cx->address, edx_part);
retval = -1;
goto out;
}
@@ -104,8 +104,8 @@
if (!mwait_supported[cstate_type]) {
mwait_supported[cstate_type] = 1;
printk(KERN_DEBUG
- "Monitor-Mwait will be used to enter C-%d "
- "state\n", cx->type);
+ "Monitor-Mwait will be used to enter C-%d state\n",
+ cx->type);
}
snprintf(cx->desc,
ACPI_CX_DESC_LEN, "ACPI FFH INTEL MWAIT 0x%x",
@@ -166,6 +166,7 @@
static int __init ffh_cstate_init(void)
{
struct cpuinfo_x86 *c = &boot_cpu_data;
+
if (c->x86_vendor != X86_VENDOR_INTEL)
return -1;
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 5b7e43e..8567c85 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -529,18 +529,19 @@
* The local apic timer can be used for any function which is CPU local.
*/
static struct clock_event_device lapic_clockevent = {
- .name = "lapic",
- .features = CLOCK_EVT_FEAT_PERIODIC |
- CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP
- | CLOCK_EVT_FEAT_DUMMY,
- .shift = 32,
- .set_state_shutdown = lapic_timer_shutdown,
- .set_state_periodic = lapic_timer_set_periodic,
- .set_state_oneshot = lapic_timer_set_oneshot,
- .set_next_event = lapic_next_event,
- .broadcast = lapic_timer_broadcast,
- .rating = 100,
- .irq = -1,
+ .name = "lapic",
+ .features = CLOCK_EVT_FEAT_PERIODIC |
+ CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP
+ | CLOCK_EVT_FEAT_DUMMY,
+ .shift = 32,
+ .set_state_shutdown = lapic_timer_shutdown,
+ .set_state_periodic = lapic_timer_set_periodic,
+ .set_state_oneshot = lapic_timer_set_oneshot,
+ .set_state_oneshot_stopped = lapic_timer_shutdown,
+ .set_next_event = lapic_next_event,
+ .broadcast = lapic_timer_broadcast,
+ .rating = 100,
+ .irq = -1,
};
static DEFINE_PER_CPU(struct clock_event_device, lapic_events);
@@ -1245,7 +1246,7 @@
/**
* setup_local_APIC - setup the local APIC
*
- * Used to setup local APIC while initializing BSP or bringin up APs.
+ * Used to setup local APIC while initializing BSP or bringing up APs.
* Always called with preemption disabled.
*/
void setup_local_APIC(void)
@@ -2028,8 +2029,8 @@
/*
* The number of allocated logical CPU IDs. Since logical CPU IDs are allocated
* contiguously, it equals to current allocated max logical CPU ID plus 1.
- * All allocated CPU ID should be in [0, nr_logical_cpuidi), so the maximum of
- * nr_logical_cpuids is nr_cpu_ids.
+ * All allocated CPU IDs should be in the [0, nr_logical_cpuids) range,
+ * so the maximum of nr_logical_cpuids is nr_cpu_ids.
*
* NOTE: Reserve 0 for BSP.
*/
@@ -2094,7 +2095,7 @@
* Since fixing handling of boot_cpu_physical_apicid requires
* another discussion and tests on each platform, we leave it
* for now and here we use read_apic_id() directly in this
- * function, generic_processor_info().
+ * function, __generic_processor_info().
*/
if (disabled_cpu_apicid != BAD_APICID &&
disabled_cpu_apicid != read_apic_id() &&
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index bd6b8c2..347bb9f 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -1107,12 +1107,12 @@
ioapic = mp_find_ioapic(gsi);
if (ioapic < 0)
- return -1;
+ return -ENODEV;
pin = mp_find_ioapic_pin(ioapic, gsi);
idx = find_irq_entry(ioapic, pin, mp_INT);
if ((flags & IOAPIC_MAP_CHECK) && idx < 0)
- return -1;
+ return -ENODEV;
return mp_map_pin_to_irq(gsi, idx, ioapic, pin, flags, info);
}
@@ -1875,6 +1875,7 @@
.irq_ack = irq_chip_ack_parent,
.irq_eoi = ioapic_ack_level,
.irq_set_affinity = ioapic_set_affinity,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
.flags = IRQCHIP_SKIP_SET_WAKE,
};
@@ -1886,6 +1887,7 @@
.irq_ack = irq_chip_ack_parent,
.irq_eoi = ioapic_ir_ack_level,
.irq_set_affinity = ioapic_set_affinity,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
.flags = IRQCHIP_SKIP_SET_WAKE,
};
diff --git a/arch/x86/kernel/apic/x2apic_uv_x.c b/arch/x86/kernel/apic/x2apic_uv_x.c
index 35690a1..e9f8f8c 100644
--- a/arch/x86/kernel/apic/x2apic_uv_x.c
+++ b/arch/x86/kernel/apic/x2apic_uv_x.c
@@ -41,40 +41,44 @@
DEFINE_PER_CPU(int, x2apic_extra_bits);
-#define PR_DEVEL(fmt, args...) pr_devel("%s: " fmt, __func__, args)
+static enum uv_system_type uv_system_type;
+static bool uv_hubless_system;
+static u64 gru_start_paddr, gru_end_paddr;
+static u64 gru_dist_base, gru_first_node_paddr = -1LL, gru_last_node_paddr;
+static u64 gru_dist_lmask, gru_dist_umask;
+static union uvh_apicid uvh_apicid;
-static enum uv_system_type uv_system_type;
-static u64 gru_start_paddr, gru_end_paddr;
-static u64 gru_dist_base, gru_first_node_paddr = -1LL, gru_last_node_paddr;
-static u64 gru_dist_lmask, gru_dist_umask;
-static union uvh_apicid uvh_apicid;
-
-/* info derived from CPUID */
+/* Information derived from CPUID: */
static struct {
unsigned int apicid_shift;
unsigned int apicid_mask;
unsigned int socketid_shift; /* aka pnode_shift for UV1/2/3 */
unsigned int pnode_mask;
unsigned int gpa_shift;
+ unsigned int gnode_shift;
} uv_cpuid;
int uv_min_hub_revision_id;
EXPORT_SYMBOL_GPL(uv_min_hub_revision_id);
+
unsigned int uv_apicid_hibits;
EXPORT_SYMBOL_GPL(uv_apicid_hibits);
static struct apic apic_x2apic_uv_x;
static struct uv_hub_info_s uv_hub_info_node0;
-/* Set this to use hardware error handler instead of kernel panic */
+/* Set this to use hardware error handler instead of kernel panic: */
static int disable_uv_undefined_panic = 1;
+
unsigned long uv_undefined(char *str)
{
if (likely(!disable_uv_undefined_panic))
panic("UV: error: undefined MMR: %s\n", str);
else
pr_crit("UV: error: undefined MMR: %s\n", str);
- return ~0ul; /* cause a machine fault */
+
+ /* Cause a machine fault: */
+ return ~0ul;
}
EXPORT_SYMBOL(uv_undefined);
@@ -85,18 +89,19 @@
mmr = early_ioremap(UV_LOCAL_MMR_BASE | addr, sizeof(*mmr));
val = *mmr;
early_iounmap(mmr, sizeof(*mmr));
+
return val;
}
static inline bool is_GRU_range(u64 start, u64 end)
{
if (gru_dist_base) {
- u64 su = start & gru_dist_umask; /* upper (incl pnode) bits */
- u64 sl = start & gru_dist_lmask; /* base offset bits */
+ u64 su = start & gru_dist_umask; /* Upper (incl pnode) bits */
+ u64 sl = start & gru_dist_lmask; /* Base offset bits */
u64 eu = end & gru_dist_umask;
u64 el = end & gru_dist_lmask;
- /* Must reside completely within a single GRU range */
+ /* Must reside completely within a single GRU range: */
return (sl == gru_dist_base && el == gru_dist_base &&
su >= gru_first_node_paddr &&
su <= gru_last_node_paddr &&
@@ -133,13 +138,14 @@
break;
case UV4_HUB_PART_NUMBER:
uv_min_hub_revision_id += UV4_HUB_REVISION_BASE - 1;
+ uv_cpuid.gnode_shift = 2; /* min partition is 4 sockets */
break;
}
uv_hub_info->hub_revision = uv_min_hub_revision_id;
uv_cpuid.pnode_mask = (1 << m_n_config.s.n_skt) - 1;
pnode = (node_id.s.node_id >> 1) & uv_cpuid.pnode_mask;
- uv_cpuid.gpa_shift = 46; /* default unless changed */
+ uv_cpuid.gpa_shift = 46; /* Default unless changed */
pr_info("UV: rev:%d part#:%x nodeid:%04x n_skt:%d pnmsk:%x pn:%x\n",
node_id.s.revision, node_id.s.part_number, node_id.s.node_id,
@@ -147,11 +153,12 @@
return pnode;
}
-/* [copied from arch/x86/kernel/cpu/topology.c:detect_extended_topology()] */
-#define SMT_LEVEL 0 /* leaf 0xb SMT level */
-#define INVALID_TYPE 0 /* leaf 0xb sub-leaf types */
-#define SMT_TYPE 1
-#define CORE_TYPE 2
+/* [Copied from arch/x86/kernel/cpu/topology.c:detect_extended_topology()] */
+
+#define SMT_LEVEL 0 /* Leaf 0xb SMT level */
+#define INVALID_TYPE 0 /* Leaf 0xb sub-leaf types */
+#define SMT_TYPE 1
+#define CORE_TYPE 2
#define LEAFB_SUBTYPE(ecx) (((ecx) >> 8) & 0xff)
#define BITS_SHIFT_NEXT_LEVEL(eax) ((eax) & 0x1f)
@@ -165,11 +172,13 @@
pr_info("UV: CPU does not have CPUID.11\n");
return;
}
+
cpuid_count(0xb, SMT_LEVEL, &eax, &ebx, &ecx, &edx);
if (ebx == 0 || (LEAFB_SUBTYPE(ecx) != SMT_TYPE)) {
pr_info("UV: CPUID.11 not implemented\n");
return;
}
+
sid_shift = BITS_SHIFT_NEXT_LEVEL(eax);
sub_index = 1;
do {
@@ -180,8 +189,9 @@
}
sub_index++;
} while (LEAFB_SUBTYPE(ecx) != INVALID_TYPE);
- uv_cpuid.apicid_shift = 0;
- uv_cpuid.apicid_mask = (~(-1 << sid_shift));
+
+ uv_cpuid.apicid_shift = 0;
+ uv_cpuid.apicid_mask = (~(-1 << sid_shift));
uv_cpuid.socketid_shift = sid_shift;
}
@@ -192,10 +202,8 @@
set_x2apic_bits();
- pr_info("UV: apicid_shift:%d apicid_mask:0x%x\n",
- uv_cpuid.apicid_shift, uv_cpuid.apicid_mask);
- pr_info("UV: socketid_shift:%d pnode_mask:0x%x\n",
- uv_cpuid.socketid_shift, uv_cpuid.pnode_mask);
+ pr_info("UV: apicid_shift:%d apicid_mask:0x%x\n", uv_cpuid.apicid_shift, uv_cpuid.apicid_mask);
+ pr_info("UV: socketid_shift:%d pnode_mask:0x%x\n", uv_cpuid.socketid_shift, uv_cpuid.pnode_mask);
}
/*
@@ -208,10 +216,8 @@
union uv1h_lb_target_physical_apic_id_mask_u apicid_mask;
if (is_uv1_hub()) {
- apicid_mask.v =
- uv_early_read_mmr(UV1H_LB_TARGET_PHYSICAL_APIC_ID_MASK);
- uv_apicid_hibits =
- apicid_mask.s1.bit_enables & UV_APICID_HIBIT_MASK;
+ apicid_mask.v = uv_early_read_mmr(UV1H_LB_TARGET_PHYSICAL_APIC_ID_MASK);
+ uv_apicid_hibits = apicid_mask.s1.bit_enables & UV_APICID_HIBIT_MASK;
}
}
@@ -220,20 +226,26 @@
int pnodeid;
int uv_apic;
- if (strncmp(oem_id, "SGI", 3) != 0)
+ if (strncmp(oem_id, "SGI", 3) != 0) {
+ if (strncmp(oem_id, "NSGI", 4) == 0) {
+ uv_hubless_system = true;
+ pr_info("UV: OEM IDs %s/%s, HUBLESS\n",
+ oem_id, oem_table_id);
+ }
return 0;
+ }
if (numa_off) {
pr_err("UV: NUMA is off, disabling UV support\n");
return 0;
}
- /* Setup early hub type field in uv_hub_info for Node 0 */
+ /* Set up early hub type field in uv_hub_info for Node 0 */
uv_cpu_info->p_uv_hub_info = &uv_hub_info_node0;
/*
* Determine UV arch type.
- * SGI: UV100/1000
+ * SGI: UV100/1000
* SGI2: UV2000/3000
* SGI3: UV300 (truncated to 4 chars because of different varieties)
* SGI4: UV400 (truncated to 4 chars because of different varieties)
@@ -249,31 +261,32 @@
pnodeid = early_get_pnodeid();
early_get_apic_socketid_shift();
- x86_platform.is_untracked_pat_range = uv_is_untracked_pat_range;
+
+ x86_platform.is_untracked_pat_range = uv_is_untracked_pat_range;
x86_platform.nmi_init = uv_nmi_init;
- if (!strcmp(oem_table_id, "UVX")) { /* most common */
+ if (!strcmp(oem_table_id, "UVX")) {
+ /* This is the most common hardware variant: */
uv_system_type = UV_X2APIC;
uv_apic = 0;
- } else if (!strcmp(oem_table_id, "UVH")) { /* only UV1 systems */
+ } else if (!strcmp(oem_table_id, "UVH")) {
+ /* Only UV1 systems: */
uv_system_type = UV_NON_UNIQUE_APIC;
- __this_cpu_write(x2apic_extra_bits,
- pnodeid << uvh_apicid.s.pnode_shift);
+ __this_cpu_write(x2apic_extra_bits, pnodeid << uvh_apicid.s.pnode_shift);
uv_set_apicid_hibit();
uv_apic = 1;
- } else if (!strcmp(oem_table_id, "UVL")) { /* only used for */
- uv_system_type = UV_LEGACY_APIC; /* very small systems */
+ } else if (!strcmp(oem_table_id, "UVL")) {
+ /* Only used for very small systems: */
+ uv_system_type = UV_LEGACY_APIC;
uv_apic = 0;
} else {
goto badbios;
}
- pr_info("UV: OEM IDs %s/%s, System/HUB Types %d/%d, uv_apic %d\n",
- oem_id, oem_table_id, uv_system_type,
- uv_min_hub_revision_id, uv_apic);
+ pr_info("UV: OEM IDs %s/%s, System/HUB Types %d/%d, uv_apic %d\n", oem_id, oem_table_id, uv_system_type, uv_min_hub_revision_id, uv_apic);
return uv_apic;
@@ -294,6 +307,12 @@
}
EXPORT_SYMBOL_GPL(is_uv_system);
+int is_uv_hubless(void)
+{
+ return uv_hubless_system;
+}
+EXPORT_SYMBOL_GPL(is_uv_hubless);
+
void **__uv_hub_info_list;
EXPORT_SYMBOL_GPL(__uv_hub_info_list);
@@ -306,16 +325,18 @@
unsigned long sn_rtc_cycles_per_second;
EXPORT_SYMBOL(sn_rtc_cycles_per_second);
-/* the following values are used for the per node hub info struct */
-static __initdata unsigned short *_node_to_pnode;
-static __initdata unsigned short _min_socket, _max_socket;
-static __initdata unsigned short _min_pnode, _max_pnode, _gr_table_len;
-static __initdata struct uv_gam_range_entry *uv_gre_table;
-static __initdata struct uv_gam_parameters *uv_gp_table;
-static __initdata unsigned short *_socket_to_node;
-static __initdata unsigned short *_socket_to_pnode;
-static __initdata unsigned short *_pnode_to_socket;
-static __initdata struct uv_gam_range_s *_gr_table;
+/* The following values are used for the per node hub info struct */
+static __initdata unsigned short *_node_to_pnode;
+static __initdata unsigned short _min_socket, _max_socket;
+static __initdata unsigned short _min_pnode, _max_pnode, _gr_table_len;
+static __initdata struct uv_gam_range_entry *uv_gre_table;
+static __initdata struct uv_gam_parameters *uv_gp_table;
+static __initdata unsigned short *_socket_to_node;
+static __initdata unsigned short *_socket_to_pnode;
+static __initdata unsigned short *_pnode_to_socket;
+
+static __initdata struct uv_gam_range_s *_gr_table;
+
#define SOCK_EMPTY ((unsigned short)~0)
extern int uv_hub_info_version(void)
@@ -324,7 +345,7 @@
}
EXPORT_SYMBOL(uv_hub_info_version);
-/* Build GAM range lookup table */
+/* Build GAM range lookup table: */
static __init void build_uv_gr_table(void)
{
struct uv_gam_range_entry *gre = uv_gre_table;
@@ -342,25 +363,24 @@
for (; gre->type != UV_GAM_RANGE_TYPE_UNUSED; gre++) {
if (gre->type == UV_GAM_RANGE_TYPE_HOLE) {
- if (!ram_limit) { /* mark hole between ram/non-ram */
+ if (!ram_limit) {
+ /* Mark hole between RAM/non-RAM: */
ram_limit = last_limit;
last_limit = gre->limit;
lsid++;
continue;
}
last_limit = gre->limit;
- pr_info("UV: extra hole in GAM RE table @%d\n",
- (int)(gre - uv_gre_table));
+ pr_info("UV: extra hole in GAM RE table @%d\n", (int)(gre - uv_gre_table));
continue;
}
if (_max_socket < gre->sockid) {
- pr_err("UV: GAM table sockid(%d) too large(>%d) @%d\n",
- gre->sockid, _max_socket,
- (int)(gre - uv_gre_table));
+ pr_err("UV: GAM table sockid(%d) too large(>%d) @%d\n", gre->sockid, _max_socket, (int)(gre - uv_gre_table));
continue;
}
sid = gre->sockid - _min_socket;
- if (lsid < sid) { /* new range */
+ if (lsid < sid) {
+ /* New range: */
grt = &_gr_table[indx];
grt->base = lindx;
grt->nasid = gre->nasid;
@@ -369,27 +389,32 @@
lindx = indx++;
continue;
}
- if (lsid == sid && !ram_limit) { /* update range */
- if (grt->limit == last_limit) { /* .. if contiguous */
+ /* Update range: */
+ if (lsid == sid && !ram_limit) {
+ /* .. if contiguous: */
+ if (grt->limit == last_limit) {
grt->limit = last_limit = gre->limit;
continue;
}
}
- if (!ram_limit) { /* non-contiguous ram range */
+ /* Non-contiguous RAM range: */
+ if (!ram_limit) {
grt++;
grt->base = lindx;
grt->nasid = gre->nasid;
grt->limit = last_limit = gre->limit;
continue;
}
- grt++; /* non-contiguous/non-ram */
- grt->base = grt - _gr_table; /* base is this entry */
+ /* Non-contiguous/non-RAM: */
+ grt++;
+ /* base is this entry */
+ grt->base = grt - _gr_table;
grt->nasid = gre->nasid;
grt->limit = last_limit = gre->limit;
lsid++;
}
- /* shorten table if possible */
+ /* Shorten table if possible */
grt++;
i = grt - _gr_table;
if (i < _gr_table_len) {
@@ -403,16 +428,15 @@
}
}
- /* display resultant gam range table */
+ /* Display resultant GAM range table: */
for (i = 0, grt = _gr_table; i < _gr_table_len; i++, grt++) {
+ unsigned long start, end;
int gb = grt->base;
- unsigned long start = gb < 0 ? 0 :
- (unsigned long)_gr_table[gb].limit << UV_GAM_RANGE_SHFT;
- unsigned long end =
- (unsigned long)grt->limit << UV_GAM_RANGE_SHFT;
- pr_info("UV: GAM Range %2d %04x 0x%013lx-0x%013lx (%d)\n",
- i, grt->nasid, start, end, gb);
+ start = gb < 0 ? 0 : (unsigned long)_gr_table[gb].limit << UV_GAM_RANGE_SHFT;
+ end = (unsigned long)grt->limit << UV_GAM_RANGE_SHFT;
+
+ pr_info("UV: GAM Range %2d %04x 0x%013lx-0x%013lx (%d)\n", i, grt->nasid, start, end, gb);
}
}
@@ -423,16 +447,19 @@
pnode = uv_apicid_to_pnode(phys_apicid);
phys_apicid |= uv_apicid_hibits;
+
val = (1UL << UVH_IPI_INT_SEND_SHFT) |
(phys_apicid << UVH_IPI_INT_APIC_ID_SHFT) |
((start_rip << UVH_IPI_INT_VECTOR_SHFT) >> 12) |
APIC_DM_INIT;
+
uv_write_global_mmr64(pnode, UVH_IPI_INT, val);
val = (1UL << UVH_IPI_INT_SEND_SHFT) |
(phys_apicid << UVH_IPI_INT_APIC_ID_SHFT) |
((start_rip << UVH_IPI_INT_VECTOR_SHFT) >> 12) |
APIC_DM_STARTUP;
+
uv_write_global_mmr64(pnode, UVH_IPI_INT, val);
return 0;
@@ -566,7 +593,7 @@
.apic_id_registered = uv_apic_id_registered,
.irq_delivery_mode = dest_Fixed,
- .irq_dest_mode = 0, /* physical */
+ .irq_dest_mode = 0, /* Physical */
.target_cpus = online_target_cpus,
.disable_esr = 0,
@@ -627,23 +654,22 @@
switch (i) {
case 0:
m_redirect = UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_0_MMR;
- m_overlay = UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_0_MMR;
+ m_overlay = UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_0_MMR;
break;
case 1:
m_redirect = UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_1_MMR;
- m_overlay = UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_1_MMR;
+ m_overlay = UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_1_MMR;
break;
case 2:
m_redirect = UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_2_MMR;
- m_overlay = UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_2_MMR;
+ m_overlay = UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_2_MMR;
break;
}
alias.v = uv_read_local_mmr(m_overlay);
if (alias.s.enable && alias.s.base == 0) {
*size = (1UL << alias.s.m_alias);
redirect.v = uv_read_local_mmr(m_redirect);
- *base = (unsigned long)redirect.s.dest_base
- << DEST_SHIFT;
+ *base = (unsigned long)redirect.s.dest_base << DEST_SHIFT;
return;
}
}
@@ -652,8 +678,7 @@
enum map_type {map_wb, map_uc};
-static __init void map_high(char *id, unsigned long base, int pshift,
- int bshift, int max_pnode, enum map_type map_type)
+static __init void map_high(char *id, unsigned long base, int pshift, int bshift, int max_pnode, enum map_type map_type)
{
unsigned long bytes, paddr;
@@ -678,16 +703,19 @@
int nid;
gru.v = c;
- /* only base bits 42:28 relevant in dist mode */
+
+ /* Only base bits 42:28 relevant in dist mode */
gru_dist_base = gru.v & 0x000007fff0000000UL;
if (!gru_dist_base) {
pr_info("UV: Map GRU_DIST base address NULL\n");
return;
}
+
bytes = 1UL << UVH_RH_GAM_GRU_OVERLAY_CONFIG_MMR_BASE_SHFT;
gru_dist_lmask = ((1UL << uv_hub_info->m_val) - 1) & ~(bytes - 1);
gru_dist_umask = ~((1UL << uv_hub_info->m_val) - 1);
gru_dist_base &= gru_dist_lmask; /* Clear bits above M */
+
for_each_online_node(nid) {
paddr = ((u64)uv_node_to_pnode(nid) << uv_hub_info->m_val) |
gru_dist_base;
@@ -695,11 +723,12 @@
gru_first_node_paddr = min(paddr, gru_first_node_paddr);
gru_last_node_paddr = max(paddr, gru_last_node_paddr);
}
+
/* Save upper (63:M) bits of address only for is_GRU_range */
gru_first_node_paddr &= gru_dist_umask;
gru_last_node_paddr &= gru_dist_umask;
- pr_debug("UV: Map GRU_DIST base 0x%016llx 0x%016llx - 0x%016llx\n",
- gru_dist_base, gru_first_node_paddr, gru_last_node_paddr);
+
+ pr_debug("UV: Map GRU_DIST base 0x%016llx 0x%016llx - 0x%016llx\n", gru_dist_base, gru_first_node_paddr, gru_last_node_paddr);
}
static __init void map_gru_high(int max_pnode)
@@ -719,6 +748,7 @@
map_gru_distributed(gru.v);
return;
}
+
base = (gru.v & mask) >> shift;
map_high("GRU", base, shift, shift, max_pnode, map_wb);
gru_start_paddr = ((u64)base << shift);
@@ -772,8 +802,8 @@
id = mmiohs[index].id;
overlay.v = uv_read_local_mmr(mmiohs[index].overlay);
- pr_info("UV: %s overlay 0x%lx base:0x%x m_io:%d\n",
- id, overlay.v, overlay.s3.base, overlay.s3.m_io);
+
+ pr_info("UV: %s overlay 0x%lx base:0x%x m_io:%d\n", id, overlay.v, overlay.s3.base, overlay.s3.m_io);
if (!overlay.s3.enable) {
pr_info("UV: %s disabled\n", id);
return;
@@ -784,7 +814,8 @@
m_io = overlay.s3.m_io;
mmr = mmiohs[index].redirect;
n = UV3H_RH_GAM_MMIOH_REDIRECT_CONFIG0_MMR_DEPTH;
- min_pnode *= 2; /* convert to NASID */
+ /* Convert to NASID: */
+ min_pnode *= 2;
max_pnode *= 2;
max_io = lnasid = fi = li = -1;
@@ -793,16 +824,18 @@
redirect.v = uv_read_local_mmr(mmr + i * 8);
nasid = redirect.s3.nasid;
+ /* Invalid NASID: */
if (nasid < min_pnode || max_pnode < nasid)
- nasid = -1; /* invalid NASID */
+ nasid = -1;
if (nasid == lnasid) {
li = i;
- if (i != n-1) /* last entry check */
+ /* Last entry check: */
+ if (i != n-1)
continue;
}
- /* check if we have a cached (or last) redirect to print */
+ /* Check if we have a cached (or last) redirect to print: */
if (lnasid != -1 || (i == n-1 && nasid != -1)) {
unsigned long addr1, addr2;
int f, l;
@@ -814,12 +847,9 @@
f = fi;
l = li;
}
- addr1 = (base << shift) +
- f * (1ULL << m_io);
- addr2 = (base << shift) +
- (l + 1) * (1ULL << m_io);
- pr_info("UV: %s[%03d..%03d] NASID 0x%04x ADDR 0x%016lx - 0x%016lx\n",
- id, fi, li, lnasid, addr1, addr2);
+ addr1 = (base << shift) + f * (1ULL << m_io);
+ addr2 = (base << shift) + (l + 1) * (1ULL << m_io);
+ pr_info("UV: %s[%03d..%03d] NASID 0x%04x ADDR 0x%016lx - 0x%016lx\n", id, fi, li, lnasid, addr1, addr2);
if (max_io < l)
max_io = l;
}
@@ -827,8 +857,7 @@
lnasid = nasid;
}
- pr_info("UV: %s base:0x%lx shift:%d M_IO:%d MAX_IO:%d\n",
- id, base, shift, m_io, max_io);
+ pr_info("UV: %s base:0x%lx shift:%d M_IO:%d MAX_IO:%d\n", id, base, shift, m_io, max_io);
if (max_io >= 0)
map_high(id, base, shift, m_io, max_io, map_uc);
@@ -841,36 +870,35 @@
int shift, enable, m_io, n_io;
if (is_uv3_hub() || is_uv4_hub()) {
- /* Map both MMIOH Regions */
+ /* Map both MMIOH regions: */
map_mmioh_high_uv3(0, min_pnode, max_pnode);
map_mmioh_high_uv3(1, min_pnode, max_pnode);
return;
}
if (is_uv1_hub()) {
- mmr = UV1H_RH_GAM_MMIOH_OVERLAY_CONFIG_MMR;
- shift = UV1H_RH_GAM_MMIOH_OVERLAY_CONFIG_MMR_BASE_SHFT;
- mmioh.v = uv_read_local_mmr(mmr);
- enable = !!mmioh.s1.enable;
- base = mmioh.s1.base;
- m_io = mmioh.s1.m_io;
- n_io = mmioh.s1.n_io;
+ mmr = UV1H_RH_GAM_MMIOH_OVERLAY_CONFIG_MMR;
+ shift = UV1H_RH_GAM_MMIOH_OVERLAY_CONFIG_MMR_BASE_SHFT;
+ mmioh.v = uv_read_local_mmr(mmr);
+ enable = !!mmioh.s1.enable;
+ base = mmioh.s1.base;
+ m_io = mmioh.s1.m_io;
+ n_io = mmioh.s1.n_io;
} else if (is_uv2_hub()) {
- mmr = UV2H_RH_GAM_MMIOH_OVERLAY_CONFIG_MMR;
- shift = UV2H_RH_GAM_MMIOH_OVERLAY_CONFIG_MMR_BASE_SHFT;
- mmioh.v = uv_read_local_mmr(mmr);
- enable = !!mmioh.s2.enable;
- base = mmioh.s2.base;
- m_io = mmioh.s2.m_io;
- n_io = mmioh.s2.n_io;
- } else
+ mmr = UV2H_RH_GAM_MMIOH_OVERLAY_CONFIG_MMR;
+ shift = UV2H_RH_GAM_MMIOH_OVERLAY_CONFIG_MMR_BASE_SHFT;
+ mmioh.v = uv_read_local_mmr(mmr);
+ enable = !!mmioh.s2.enable;
+ base = mmioh.s2.base;
+ m_io = mmioh.s2.m_io;
+ n_io = mmioh.s2.n_io;
+ } else {
return;
+ }
if (enable) {
max_pnode &= (1 << n_io) - 1;
- pr_info(
- "UV: base:0x%lx shift:%d N_IO:%d M_IO:%d max_pnode:0x%x\n",
- base, shift, m_io, n_io, max_pnode);
+ pr_info("UV: base:0x%lx shift:%d N_IO:%d M_IO:%d max_pnode:0x%x\n", base, shift, m_io, n_io, max_pnode);
map_high("MMIOH", base, shift, m_io, max_pnode, map_uc);
} else {
pr_info("UV: MMIOH disabled\n");
@@ -888,16 +916,16 @@
long status;
u64 ticks_per_sec;
- status = uv_bios_freq_base(BIOS_FREQ_BASE_REALTIME_CLOCK,
- &ticks_per_sec);
+ status = uv_bios_freq_base(BIOS_FREQ_BASE_REALTIME_CLOCK, &ticks_per_sec);
+
if (status != BIOS_STATUS_SUCCESS || ticks_per_sec < 100000) {
- printk(KERN_WARNING
- "unable to determine platform RTC clock frequency, "
- "guessing.\n");
- /* BIOS gives wrong value for clock freq. so guess */
+ pr_warn("UV: unable to determine platform RTC clock frequency, guessing.\n");
+
+ /* BIOS gives wrong value for clock frequency, so guess: */
sn_rtc_cycles_per_second = 1000000000000UL / 30000UL;
- } else
+ } else {
sn_rtc_cycles_per_second = ticks_per_sec;
+ }
}
/*
@@ -908,19 +936,19 @@
struct timer_list *timer = &uv_scir_info->timer;
unsigned char bits = uv_scir_info->state;
- /* flip heartbeat bit */
+ /* Flip heartbeat bit: */
bits ^= SCIR_CPU_HEARTBEAT;
- /* is this cpu idle? */
+ /* Is this CPU idle? */
if (idle_cpu(raw_smp_processor_id()))
bits &= ~SCIR_CPU_ACTIVITY;
else
bits |= SCIR_CPU_ACTIVITY;
- /* update system controller interface reg */
+ /* Update system controller interface reg: */
uv_set_scir_bits(bits);
- /* enable next timer period */
+ /* Enable next timer period: */
mod_timer(timer, jiffies + SCIR_CPU_HB_INTERVAL);
}
@@ -935,7 +963,7 @@
add_timer_on(timer, cpu);
uv_cpu_scir_info(cpu)->enabled = 1;
- /* also ensure that boot cpu is enabled */
+ /* Also ensure that boot CPU is enabled: */
cpu = 0;
}
return 0;
@@ -968,9 +996,11 @@
{
int cpu;
- if (is_uv_system())
+ if (is_uv_system()) {
for_each_online_cpu(cpu)
uv_heartbeat_enable(cpu);
+ }
+
return 0;
}
@@ -979,14 +1009,10 @@
#endif /* !CONFIG_HOTPLUG_CPU */
/* Direct Legacy VGA I/O traffic to designated IOH */
-int uv_set_vga_state(struct pci_dev *pdev, bool decode,
- unsigned int command_bits, u32 flags)
+int uv_set_vga_state(struct pci_dev *pdev, bool decode, unsigned int command_bits, u32 flags)
{
int domain, bus, rc;
- PR_DEVEL("devfn %x decode %d cmd %x flags %d\n",
- pdev->devfn, decode, command_bits, flags);
-
if (!(flags & PCI_VGA_STATE_CHANGE_BRIDGE))
return 0;
@@ -997,13 +1023,12 @@
bus = pdev->bus->number;
rc = uv_bios_set_legacy_vga_target(decode, domain, bus);
- PR_DEVEL("vga decode %d %x:%x, rc: %d\n", decode, domain, bus, rc);
return rc;
}
/*
- * Called on each cpu to initialize the per_cpu UV data area.
+ * Called on each CPU to initialize the per_cpu UV data area.
* FIXME: hotplug not supported yet
*/
void uv_cpu_init(void)
@@ -1030,90 +1055,79 @@
union uvh_rh_gam_config_mmr_u m_n_config;
union uv3h_gr0_gam_gr_config_u m_gr_config;
- m_n_config.v = uv_read_local_mmr(UVH_RH_GAM_CONFIG_MMR);
- mnp->n_val = m_n_config.s.n_skt;
+ /* Make sure the whole structure is well initialized: */
+ memset(mnp, 0, sizeof(*mnp));
+
+ m_n_config.v = uv_read_local_mmr(UVH_RH_GAM_CONFIG_MMR);
+ mnp->n_val = m_n_config.s.n_skt;
+
if (is_uv4_hub()) {
- mnp->m_val = 0;
- mnp->n_lshift = 0;
+ mnp->m_val = 0;
+ mnp->n_lshift = 0;
} else if (is_uv3_hub()) {
- mnp->m_val = m_n_config.s3.m_skt;
- m_gr_config.v = uv_read_local_mmr(UV3H_GR0_GAM_GR_CONFIG);
- mnp->n_lshift = m_gr_config.s3.m_skt;
+ mnp->m_val = m_n_config.s3.m_skt;
+ m_gr_config.v = uv_read_local_mmr(UV3H_GR0_GAM_GR_CONFIG);
+ mnp->n_lshift = m_gr_config.s3.m_skt;
} else if (is_uv2_hub()) {
- mnp->m_val = m_n_config.s2.m_skt;
- mnp->n_lshift = mnp->m_val == 40 ? 40 : 39;
+ mnp->m_val = m_n_config.s2.m_skt;
+ mnp->n_lshift = mnp->m_val == 40 ? 40 : 39;
} else if (is_uv1_hub()) {
- mnp->m_val = m_n_config.s1.m_skt;
- mnp->n_lshift = mnp->m_val;
+ mnp->m_val = m_n_config.s1.m_skt;
+ mnp->n_lshift = mnp->m_val;
}
mnp->m_shift = mnp->m_val ? 64 - mnp->m_val : 0;
}
-void __init uv_init_hub_info(struct uv_hub_info_s *hub_info)
+void __init uv_init_hub_info(struct uv_hub_info_s *hi)
{
- struct mn mn = {0}; /* avoid unitialized warnings */
union uvh_node_id_u node_id;
+ struct mn mn;
get_mn(&mn);
- hub_info->m_val = mn.m_val;
- hub_info->n_val = mn.n_val;
- hub_info->m_shift = mn.m_shift;
- hub_info->n_lshift = mn.n_lshift ? mn.n_lshift : 0;
-
- hub_info->hub_revision = uv_hub_info->hub_revision;
- hub_info->pnode_mask = uv_cpuid.pnode_mask;
- hub_info->min_pnode = _min_pnode;
- hub_info->min_socket = _min_socket;
- hub_info->pnode_to_socket = _pnode_to_socket;
- hub_info->socket_to_node = _socket_to_node;
- hub_info->socket_to_pnode = _socket_to_pnode;
- hub_info->gr_table_len = _gr_table_len;
- hub_info->gr_table = _gr_table;
- hub_info->gpa_mask = mn.m_val ?
+ hi->gpa_mask = mn.m_val ?
(1UL << (mn.m_val + mn.n_val)) - 1 :
(1UL << uv_cpuid.gpa_shift) - 1;
- node_id.v = uv_read_local_mmr(UVH_NODE_ID);
- hub_info->gnode_extra =
- (node_id.s.node_id & ~((1 << mn.n_val) - 1)) >> 1;
+ hi->m_val = mn.m_val;
+ hi->n_val = mn.n_val;
+ hi->m_shift = mn.m_shift;
+ hi->n_lshift = mn.n_lshift ? mn.n_lshift : 0;
+ hi->hub_revision = uv_hub_info->hub_revision;
+ hi->pnode_mask = uv_cpuid.pnode_mask;
+ hi->min_pnode = _min_pnode;
+ hi->min_socket = _min_socket;
+ hi->pnode_to_socket = _pnode_to_socket;
+ hi->socket_to_node = _socket_to_node;
+ hi->socket_to_pnode = _socket_to_pnode;
+ hi->gr_table_len = _gr_table_len;
+ hi->gr_table = _gr_table;
- hub_info->gnode_upper =
- ((unsigned long)hub_info->gnode_extra << mn.m_val);
+ node_id.v = uv_read_local_mmr(UVH_NODE_ID);
+ uv_cpuid.gnode_shift = max_t(unsigned int, uv_cpuid.gnode_shift, mn.n_val);
+ hi->gnode_extra = (node_id.s.node_id & ~((1 << uv_cpuid.gnode_shift) - 1)) >> 1;
+ hi->gnode_upper = (unsigned long)hi->gnode_extra << mn.m_val;
if (uv_gp_table) {
- hub_info->global_mmr_base = uv_gp_table->mmr_base;
- hub_info->global_mmr_shift = uv_gp_table->mmr_shift;
- hub_info->global_gru_base = uv_gp_table->gru_base;
- hub_info->global_gru_shift = uv_gp_table->gru_shift;
- hub_info->gpa_shift = uv_gp_table->gpa_shift;
- hub_info->gpa_mask = (1UL << hub_info->gpa_shift) - 1;
+ hi->global_mmr_base = uv_gp_table->mmr_base;
+ hi->global_mmr_shift = uv_gp_table->mmr_shift;
+ hi->global_gru_base = uv_gp_table->gru_base;
+ hi->global_gru_shift = uv_gp_table->gru_shift;
+ hi->gpa_shift = uv_gp_table->gpa_shift;
+ hi->gpa_mask = (1UL << hi->gpa_shift) - 1;
} else {
- hub_info->global_mmr_base =
- uv_read_local_mmr(UVH_RH_GAM_MMR_OVERLAY_CONFIG_MMR) &
- ~UV_MMR_ENABLE;
- hub_info->global_mmr_shift = _UV_GLOBAL_MMR64_PNODE_SHIFT;
+ hi->global_mmr_base = uv_read_local_mmr(UVH_RH_GAM_MMR_OVERLAY_CONFIG_MMR) & ~UV_MMR_ENABLE;
+ hi->global_mmr_shift = _UV_GLOBAL_MMR64_PNODE_SHIFT;
}
- get_lowmem_redirect(
- &hub_info->lowmem_remap_base, &hub_info->lowmem_remap_top);
+ get_lowmem_redirect(&hi->lowmem_remap_base, &hi->lowmem_remap_top);
- hub_info->apic_pnode_shift = uv_cpuid.socketid_shift;
+ hi->apic_pnode_shift = uv_cpuid.socketid_shift;
- /* show system specific info */
- pr_info("UV: N:%d M:%d m_shift:%d n_lshift:%d\n",
- hub_info->n_val, hub_info->m_val,
- hub_info->m_shift, hub_info->n_lshift);
-
- pr_info("UV: gpa_mask/shift:0x%lx/%d pnode_mask:0x%x apic_pns:%d\n",
- hub_info->gpa_mask, hub_info->gpa_shift,
- hub_info->pnode_mask, hub_info->apic_pnode_shift);
-
- pr_info("UV: mmr_base/shift:0x%lx/%ld gru_base/shift:0x%lx/%ld\n",
- hub_info->global_mmr_base, hub_info->global_mmr_shift,
- hub_info->global_gru_base, hub_info->global_gru_shift);
-
- pr_info("UV: gnode_upper:0x%lx gnode_extra:0x%x\n",
- hub_info->gnode_upper, hub_info->gnode_extra);
+ /* Show system specific info: */
+ pr_info("UV: N:%d M:%d m_shift:%d n_lshift:%d\n", hi->n_val, hi->m_val, hi->m_shift, hi->n_lshift);
+ pr_info("UV: gpa_mask/shift:0x%lx/%d pnode_mask:0x%x apic_pns:%d\n", hi->gpa_mask, hi->gpa_shift, hi->pnode_mask, hi->apic_pnode_shift);
+ pr_info("UV: mmr_base/shift:0x%lx/%ld gru_base/shift:0x%lx/%ld\n", hi->global_mmr_base, hi->global_mmr_shift, hi->global_gru_base, hi->global_gru_shift);
+ pr_info("UV: gnode_upper:0x%lx gnode_extra:0x%x\n", hi->gnode_upper, hi->gnode_extra);
}
static void __init decode_gam_params(unsigned long ptr)
@@ -1139,12 +1153,9 @@
for (; gre->type != UV_GAM_RANGE_TYPE_UNUSED; gre++) {
if (!index) {
pr_info("UV: GAM Range Table...\n");
- pr_info("UV: # %20s %14s %5s %4s %5s %3s %2s\n",
- "Range", "", "Size", "Type", "NASID",
- "SID", "PN");
+ pr_info("UV: # %20s %14s %5s %4s %5s %3s %2s\n", "Range", "", "Size", "Type", "NASID", "SID", "PN");
}
- pr_info(
- "UV: %2d: 0x%014lx-0x%014lx %5luG %3d %04x %02x %02x\n",
+ pr_info("UV: %2d: 0x%014lx-0x%014lx %5luG %3d %04x %02x %02x\n",
index++,
(unsigned long)lgre << UV_GAM_RANGE_SHFT,
(unsigned long)gre->limit << UV_GAM_RANGE_SHFT,
@@ -1162,29 +1173,32 @@
if (pnode_max < gre->pnode)
pnode_max = gre->pnode;
}
- _min_socket = sock_min;
- _max_socket = sock_max;
- _min_pnode = pnode_min;
- _max_pnode = pnode_max;
- _gr_table_len = index;
- pr_info(
- "UV: GRT: %d entries, sockets(min:%x,max:%x) pnodes(min:%x,max:%x)\n",
- index, _min_socket, _max_socket, _min_pnode, _max_pnode);
+ _min_socket = sock_min;
+ _max_socket = sock_max;
+ _min_pnode = pnode_min;
+ _max_pnode = pnode_max;
+ _gr_table_len = index;
+
+ pr_info("UV: GRT: %d entries, sockets(min:%x,max:%x) pnodes(min:%x,max:%x)\n", index, _min_socket, _max_socket, _min_pnode, _max_pnode);
}
-static void __init decode_uv_systab(void)
+static int __init decode_uv_systab(void)
{
struct uv_systab *st;
int i;
+ if (uv_hub_info->hub_revision < UV4_HUB_REVISION_BASE)
+ return 0; /* No extended UVsystab required */
+
st = uv_systab;
- if ((!st || st->revision < UV_SYSTAB_VERSION_UV4) && !is_uv4_hub())
- return;
- if (st->revision != UV_SYSTAB_VERSION_UV4_LATEST) {
- pr_crit(
- "UV: BIOS UVsystab version(%x) mismatch, expecting(%x)\n",
- st->revision, UV_SYSTAB_VERSION_UV4_LATEST);
- BUG();
+ if ((!st) || (st->revision < UV_SYSTAB_VERSION_UV4_LATEST)) {
+ int rev = st ? st->revision : 0;
+
+ pr_err("UV: BIOS UVsystab version(%x) mismatch, expecting(%x)\n", rev, UV_SYSTAB_VERSION_UV4_LATEST);
+ pr_err("UV: Cannot support UV operations, switching to generic PC\n");
+ uv_system_type = UV_NONE;
+
+ return -EINVAL;
}
for (i = 0; st->entry[i].type != UV_SYSTAB_TYPE_UNUSED; i++) {
@@ -1205,10 +1219,11 @@
break;
}
}
+ return 0;
}
/*
- * Setup physical blade translations from UVH_NODE_PRESENT_TABLE
+ * Set up physical blade translations from UVH_NODE_PRESENT_TABLE
* .. NB: UVH_NODE_PRESENT_TABLE is going away,
* .. being replaced by GAM Range Table
*/
@@ -1244,14 +1259,13 @@
if (!gre) {
if (is_uv1_hub() || is_uv2_hub() || is_uv3_hub()) {
pr_info("UV: No UVsystab socket table, ignoring\n");
- return; /* not required */
+ return;
}
- pr_crit(
- "UV: Error: UVsystab address translations not available!\n");
+ pr_crit("UV: Error: UVsystab address translations not available!\n");
BUG();
}
- /* build socket id -> node id, pnode */
+ /* Build socket id -> node id, pnode */
num = maxsock - minsock + 1;
bytes = num * sizeof(_socket_to_node[0]);
_socket_to_node = kmalloc(bytes, GFP_KERNEL);
@@ -1268,27 +1282,27 @@
for (i = 0; i < nump; i++)
_pnode_to_socket[i] = SOCK_EMPTY;
- /* fill in pnode/node/addr conversion list values */
+ /* Fill in pnode/node/addr conversion list values: */
pr_info("UV: GAM Building socket/pnode conversion tables\n");
for (; gre->type != UV_GAM_RANGE_TYPE_UNUSED; gre++) {
if (gre->type == UV_GAM_RANGE_TYPE_HOLE)
continue;
i = gre->sockid - minsock;
+ /* Duplicate: */
if (_socket_to_pnode[i] != SOCK_EMPTY)
- continue; /* duplicate */
+ continue;
_socket_to_pnode[i] = gre->pnode;
i = gre->pnode - minpnode;
_pnode_to_socket[i] = gre->sockid;
- pr_info(
- "UV: sid:%02x type:%d nasid:%04x pn:%02x pn2s:%2x\n",
+ pr_info("UV: sid:%02x type:%d nasid:%04x pn:%02x pn2s:%2x\n",
gre->sockid, gre->type, gre->nasid,
_socket_to_pnode[gre->sockid - minsock],
_pnode_to_socket[gre->pnode - minpnode]);
}
- /* Set socket -> node values */
+ /* Set socket -> node values: */
lnid = -1;
for_each_present_cpu(cpu) {
int nid = cpu_to_node(cpu);
@@ -1304,7 +1318,7 @@
sockid, apicid, nid);
}
- /* Setup physical blade to pnode translation from GAM Range Table */
+ /* Set up physical blade to pnode translation from GAM Range Table: */
bytes = num_possible_nodes() * sizeof(_node_to_pnode[0]);
_node_to_pnode = kmalloc(bytes, GFP_KERNEL);
BUG_ON(!_node_to_pnode);
@@ -1314,8 +1328,7 @@
for (sockid = minsock; sockid <= maxsock; sockid++) {
if (lnid == _socket_to_node[sockid - minsock]) {
- _node_to_pnode[lnid] =
- _socket_to_pnode[sockid - minsock];
+ _node_to_pnode[lnid] = _socket_to_pnode[sockid - minsock];
break;
}
}
@@ -1332,8 +1345,7 @@
pr_info("UV: Checking socket->node/pnode for identity maps\n");
if (minsock == 0) {
for (i = 0; i < num; i++)
- if (_socket_to_node[i] == SOCK_EMPTY ||
- i != _socket_to_node[i])
+ if (_socket_to_node[i] == SOCK_EMPTY || i != _socket_to_node[i])
break;
if (i >= num) {
kfree(_socket_to_node);
@@ -1354,7 +1366,7 @@
}
}
-void __init uv_system_init(void)
+static void __init uv_system_init_hub(void)
{
struct uv_hub_info_s hub_info = {0};
int bytes, cpu, nodeid;
@@ -1372,8 +1384,13 @@
map_low_mmrs();
- uv_bios_init(); /* get uv_systab for decoding */
- decode_uv_systab();
+ /* Get uv_systab for decoding: */
+ uv_bios_init();
+
+ /* If there's an UVsystab problem then abort UV init: */
+ if (decode_uv_systab() < 0)
+ return;
+
build_socket_tables();
build_uv_gr_table();
uv_init_hub_info(&hub_info);
@@ -1381,14 +1398,10 @@
if (!_node_to_pnode)
boot_init_possible_blades(&hub_info);
- /* uv_num_possible_blades() is really the hub count */
- pr_info("UV: Found %d hubs, %d nodes, %d cpus\n",
- uv_num_possible_blades(),
- num_possible_nodes(),
- num_possible_cpus());
+ /* uv_num_possible_blades() is really the hub count: */
+ pr_info("UV: Found %d hubs, %d nodes, %d CPUs\n", uv_num_possible_blades(), num_possible_nodes(), num_possible_cpus());
- uv_bios_get_sn_info(0, &uv_type, &sn_partition_id, &sn_coherency_id,
- &sn_region_size, &system_serial_number);
+ uv_bios_get_sn_info(0, &uv_type, &sn_partition_id, &sn_coherency_id, &sn_region_size, &system_serial_number);
hub_info.coherency_domain_number = sn_coherency_id;
uv_rtc_init();
@@ -1401,33 +1414,31 @@
struct uv_hub_info_s *new_hub;
if (__uv_hub_info_list[nodeid]) {
- pr_err("UV: Node %d UV HUB already initialized!?\n",
- nodeid);
+ pr_err("UV: Node %d UV HUB already initialized!?\n", nodeid);
BUG();
}
/* Allocate new per hub info list */
- new_hub = (nodeid == 0) ?
- &uv_hub_info_node0 :
- kzalloc_node(bytes, GFP_KERNEL, nodeid);
+ new_hub = (nodeid == 0) ? &uv_hub_info_node0 : kzalloc_node(bytes, GFP_KERNEL, nodeid);
BUG_ON(!new_hub);
__uv_hub_info_list[nodeid] = new_hub;
new_hub = uv_hub_info_list(nodeid);
BUG_ON(!new_hub);
*new_hub = hub_info;
- /* Use information from GAM table if available */
+ /* Use information from GAM table if available: */
if (_node_to_pnode)
new_hub->pnode = _node_to_pnode[nodeid];
- else /* Fill in during cpu loop */
+ else /* Or fill in during CPU loop: */
new_hub->pnode = 0xffff;
+
new_hub->numa_blade_id = uv_node_to_blade_id(nodeid);
new_hub->memory_nid = -1;
new_hub->nr_possible_cpus = 0;
new_hub->nr_online_cpus = 0;
}
- /* Initialize per cpu info */
+ /* Initialize per CPU info: */
for_each_possible_cpu(cpu) {
int apicid = per_cpu(x86_cpu_to_apicid, cpu);
int numa_node_id;
@@ -1438,22 +1449,24 @@
pnode = uv_apicid_to_pnode(apicid);
uv_cpu_info_per(cpu)->p_uv_hub_info = uv_hub_info_list(nodeid);
- uv_cpu_info_per(cpu)->blade_cpu_id =
- uv_cpu_hub_info(cpu)->nr_possible_cpus++;
+ uv_cpu_info_per(cpu)->blade_cpu_id = uv_cpu_hub_info(cpu)->nr_possible_cpus++;
if (uv_cpu_hub_info(cpu)->memory_nid == -1)
uv_cpu_hub_info(cpu)->memory_nid = cpu_to_node(cpu);
- if (nodeid != numa_node_id && /* init memoryless node */
+
+ /* Init memoryless node: */
+ if (nodeid != numa_node_id &&
uv_hub_info_list(numa_node_id)->pnode == 0xffff)
uv_hub_info_list(numa_node_id)->pnode = pnode;
else if (uv_cpu_hub_info(cpu)->pnode == 0xffff)
uv_cpu_hub_info(cpu)->pnode = pnode;
+
uv_cpu_scir_info(cpu)->offset = uv_scir_offset(apicid);
}
for_each_node(nodeid) {
unsigned short pnode = uv_hub_info_list(nodeid)->pnode;
- /* Add pnode info for pre-GAM list nodes without cpus */
+ /* Add pnode info for pre-GAM list nodes without CPUs: */
if (pnode == 0xffff) {
unsigned long paddr;
@@ -1479,15 +1492,30 @@
uv_scir_register_cpu_notifier();
proc_mkdir("sgi_uv", NULL);
- /* register Legacy VGA I/O redirection handler */
+ /* Register Legacy VGA I/O redirection handler: */
pci_register_set_vga_state(uv_set_vga_state);
/*
* For a kdump kernel the reset must be BOOT_ACPI, not BOOT_EFI, as
- * EFI is not enabled in the kdump kernel.
+ * EFI is not enabled in the kdump kernel:
*/
if (is_kdump_kernel())
reboot_type = BOOT_ACPI;
}
+/*
+ * There is a small amount of UV specific code needed to initialize a
+ * UV system that does not have a "UV HUB" (referred to as "hubless").
+ */
+void __init uv_system_init(void)
+{
+ if (likely(!is_uv_system() && !is_uv_hubless()))
+ return;
+
+ if (is_uv_system())
+ uv_system_init_hub();
+ else
+ uv_nmi_setup_hubless();
+}
+
apic_driver(apic_x2apic_uv_x);
diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c
index 45d44c173c..4a7080c 100644
--- a/arch/x86/kernel/apm_32.c
+++ b/arch/x86/kernel/apm_32.c
@@ -905,8 +905,8 @@
{
static int use_apm_idle; /* = 0 */
static unsigned int last_jiffies; /* = 0 */
- static unsigned int last_stime; /* = 0 */
- cputime_t stime, utime;
+ static u64 last_stime; /* = 0 */
+ u64 stime, utime;
int apm_idle_done = 0;
unsigned int jiffies_since_last_check = jiffies - last_jiffies;
@@ -919,7 +919,7 @@
} else if (jiffies_since_last_check > idle_period) {
unsigned int idle_percentage;
- idle_percentage = cputime_to_jiffies(stime - last_stime);
+ idle_percentage = nsecs_to_jiffies(stime - last_stime);
idle_percentage *= 100;
idle_percentage /= jiffies_since_last_check;
use_apm_idle = (idle_percentage > idle_threshold);
diff --git a/arch/x86/kernel/asm-offsets.c b/arch/x86/kernel/asm-offsets.c
index c62e015..de827d6 100644
--- a/arch/x86/kernel/asm-offsets.c
+++ b/arch/x86/kernel/asm-offsets.c
@@ -81,6 +81,7 @@
BLANK();
OFFSET(BP_scratch, boot_params, scratch);
+ OFFSET(BP_secure_boot, boot_params, secure_boot);
OFFSET(BP_loadflags, boot_params, hdr.loadflags);
OFFSET(BP_hardware_subarch, boot_params, hdr.hardware_subarch);
OFFSET(BP_version, boot_params, hdr.version);
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index 2b4cf04..4e95b2e 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -555,8 +555,10 @@
if (c->x86_power & (1 << 8)) {
set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
set_cpu_cap(c, X86_FEATURE_NONSTOP_TSC);
- if (!check_tsc_unstable())
- set_sched_clock_stable();
+ if (check_tsc_unstable())
+ clear_sched_clock_stable();
+ } else {
+ clear_sched_clock_stable();
}
/* Bit 12 of 8000_0007 edx is accumulated power mechanism. */
diff --git a/arch/x86/kernel/cpu/centaur.c b/arch/x86/kernel/cpu/centaur.c
index 1661d8e..2c234a6 100644
--- a/arch/x86/kernel/cpu/centaur.c
+++ b/arch/x86/kernel/cpu/centaur.c
@@ -1,5 +1,5 @@
-#include <linux/bitops.h>
-#include <linux/kernel.h>
+
+#include <linux/sched.h>
#include <asm/cpufeature.h>
#include <asm/e820.h>
@@ -104,6 +104,8 @@
#ifdef CONFIG_X86_64
set_cpu_cap(c, X86_FEATURE_SYSENTER32);
#endif
+
+ clear_sched_clock_stable();
}
static void init_centaur(struct cpuinfo_x86 *c)
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index ede03e8..f07005e6 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -35,6 +35,7 @@
#include <asm/desc.h>
#include <asm/fpu/internal.h>
#include <asm/mtrr.h>
+#include <asm/hwcap2.h>
#include <linux/numa.h>
#include <asm/asm.h>
#include <asm/bugs.h>
@@ -51,6 +52,8 @@
#include "cpu.h"
+u32 elf_hwcap2 __read_mostly;
+
/* all of these masks are initialized in setup_cpu_local_masks() */
cpumask_var_t cpu_initialized_mask;
cpumask_var_t cpu_callout_mask;
@@ -83,6 +86,7 @@
strcpy(c->x86_model_id, "386");
}
#endif
+ clear_sched_clock_stable();
}
static const struct cpu_dev default_cpu = {
@@ -655,6 +659,16 @@
}
}
+static void apply_forced_caps(struct cpuinfo_x86 *c)
+{
+ int i;
+
+ for (i = 0; i < NCAPINTS; i++) {
+ c->x86_capability[i] &= ~cpu_caps_cleared[i];
+ c->x86_capability[i] |= cpu_caps_set[i];
+ }
+}
+
void get_cpu_cap(struct cpuinfo_x86 *c)
{
u32 eax, ebx, ecx, edx;
@@ -748,6 +762,13 @@
c->x86_capability[CPUID_8000_000A_EDX] = cpuid_edx(0x8000000a);
init_scattered_cpuid_features(c);
+
+ /*
+ * Clear/Set all flags overridden by options, after probe.
+ * This needs to happen each time we re-probe, which may happen
+ * several times during CPU initialization.
+ */
+ apply_forced_caps(c);
}
static void identify_cpu_without_cpuid(struct cpuinfo_x86 *c)
@@ -801,14 +822,12 @@
memset(&c->x86_capability, 0, sizeof c->x86_capability);
c->extended_cpuid_level = 0;
- if (!have_cpuid_p())
- identify_cpu_without_cpuid(c);
-
/* cyrix could have cpuid enabled via c_identify()*/
if (have_cpuid_p()) {
cpu_detect(c);
get_cpu_vendor(c);
get_cpu_cap(c);
+ setup_force_cpu_cap(X86_FEATURE_CPUID);
if (this_cpu->c_early_init)
this_cpu->c_early_init(c);
@@ -818,6 +837,9 @@
if (this_cpu->c_bsp_init)
this_cpu->c_bsp_init(c);
+ } else {
+ identify_cpu_without_cpuid(c);
+ setup_clear_cpu_cap(X86_FEATURE_CPUID);
}
setup_force_cpu_cap(X86_FEATURE_ALWAYS);
@@ -1035,10 +1057,7 @@
this_cpu->c_identify(c);
/* Clear/Set all flags overridden by options, after probe */
- for (i = 0; i < NCAPINTS; i++) {
- c->x86_capability[i] &= ~cpu_caps_cleared[i];
- c->x86_capability[i] |= cpu_caps_set[i];
- }
+ apply_forced_caps(c);
#ifdef CONFIG_X86_64
c->apicid = apic->phys_pkg_id(c->initial_apicid, 0);
@@ -1056,6 +1075,8 @@
*/
if (this_cpu->c_init)
this_cpu->c_init(c);
+ else
+ clear_sched_clock_stable();
/* Disable the PN if appropriate */
squash_the_stupid_serial_number(c);
@@ -1097,10 +1118,7 @@
* Clear/Set all flags overridden by options, need do it
* before following smp all cpus cap AND.
*/
- for (i = 0; i < NCAPINTS; i++) {
- c->x86_capability[i] &= ~cpu_caps_cleared[i];
- c->x86_capability[i] |= cpu_caps_set[i];
- }
+ apply_forced_caps(c);
/*
* On SMP, boot_cpu_data holds the common feature set between
diff --git a/arch/x86/kernel/cpu/cyrix.c b/arch/x86/kernel/cpu/cyrix.c
index bd9dcd6..47416f9 100644
--- a/arch/x86/kernel/cpu/cyrix.c
+++ b/arch/x86/kernel/cpu/cyrix.c
@@ -9,6 +9,7 @@
#include <asm/pci-direct.h>
#include <asm/tsc.h>
#include <asm/cpufeature.h>
+#include <linux/sched.h>
#include "cpu.h"
@@ -183,6 +184,7 @@
set_cpu_cap(c, X86_FEATURE_CYRIX_ARR);
break;
}
+ clear_sched_clock_stable();
}
static void init_cyrix(struct cpuinfo_x86 *c)
diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index 203f860..017ecd3 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -15,6 +15,8 @@
#include <asm/cpu.h>
#include <asm/intel-family.h>
#include <asm/microcode_intel.h>
+#include <asm/hwcap2.h>
+#include <asm/elf.h>
#ifdef CONFIG_X86_64
#include <linux/topology.h>
@@ -62,6 +64,46 @@
}
}
+static bool ring3mwait_disabled __read_mostly;
+
+static int __init ring3mwait_disable(char *__unused)
+{
+ ring3mwait_disabled = true;
+ return 0;
+}
+__setup("ring3mwait=disable", ring3mwait_disable);
+
+static void probe_xeon_phi_r3mwait(struct cpuinfo_x86 *c)
+{
+ /*
+ * Ring 3 MONITOR/MWAIT feature cannot be detected without
+ * cpu model and family comparison.
+ */
+ if (c->x86 != 6)
+ return;
+ switch (c->x86_model) {
+ case INTEL_FAM6_XEON_PHI_KNL:
+ case INTEL_FAM6_XEON_PHI_KNM:
+ break;
+ default:
+ return;
+ }
+
+ if (ring3mwait_disabled) {
+ msr_clear_bit(MSR_MISC_FEATURE_ENABLES,
+ MSR_MISC_FEATURE_ENABLES_RING3MWAIT_BIT);
+ return;
+ }
+
+ msr_set_bit(MSR_MISC_FEATURE_ENABLES,
+ MSR_MISC_FEATURE_ENABLES_RING3MWAIT_BIT);
+
+ set_cpu_cap(c, X86_FEATURE_RING3MWAIT);
+
+ if (c == &boot_cpu_data)
+ ELF_HWCAP2 |= HWCAP2_RING3MWAIT;
+}
+
static void early_init_intel(struct cpuinfo_x86 *c)
{
u64 misc_enable;
@@ -119,8 +161,10 @@
if (c->x86_power & (1 << 8)) {
set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
set_cpu_cap(c, X86_FEATURE_NONSTOP_TSC);
- if (!check_tsc_unstable())
- set_sched_clock_stable();
+ if (check_tsc_unstable())
+ clear_sched_clock_stable();
+ } else {
+ clear_sched_clock_stable();
}
/* Penwell and Cloverview have the TSC which doesn't sleep on S3 */
@@ -560,6 +604,8 @@
detect_vmx_virtcap(c);
init_intel_energy_perf(c);
+
+ probe_xeon_phi_r3mwait(c);
}
#ifdef CONFIG_X86_32
diff --git a/arch/x86/kernel/cpu/mcheck/mce-apei.c b/arch/x86/kernel/cpu/mcheck/mce-apei.c
index 83f1a98..2eee853 100644
--- a/arch/x86/kernel/cpu/mcheck/mce-apei.c
+++ b/arch/x86/kernel/cpu/mcheck/mce-apei.c
@@ -52,8 +52,11 @@
if (severity >= GHES_SEV_RECOVERABLE)
m.status |= MCI_STATUS_UC;
- if (severity >= GHES_SEV_PANIC)
+
+ if (severity >= GHES_SEV_PANIC) {
m.status |= MCI_STATUS_PCC;
+ m.tsc = rdtsc();
+ }
m.addr = mem_err->physical_addr;
mce_log(&m);
diff --git a/arch/x86/kernel/cpu/mcheck/mce-genpool.c b/arch/x86/kernel/cpu/mcheck/mce-genpool.c
index 93d824e..1e5a50c 100644
--- a/arch/x86/kernel/cpu/mcheck/mce-genpool.c
+++ b/arch/x86/kernel/cpu/mcheck/mce-genpool.c
@@ -72,7 +72,7 @@
return new_head.first;
}
-void mce_gen_pool_process(void)
+void mce_gen_pool_process(struct work_struct *__unused)
{
struct llist_node *head;
struct mce_evt_llist *node, *tmp;
diff --git a/arch/x86/kernel/cpu/mcheck/mce-inject.c b/arch/x86/kernel/cpu/mcheck/mce-inject.c
index 517619e..99165b2 100644
--- a/arch/x86/kernel/cpu/mcheck/mce-inject.c
+++ b/arch/x86/kernel/cpu/mcheck/mce-inject.c
@@ -152,7 +152,6 @@
if (context == MCJ_CTX_RANDOM)
return;
-#ifdef CONFIG_X86_LOCAL_APIC
if (m->inject_flags & (MCJ_IRQ_BROADCAST | MCJ_NMI_BROADCAST)) {
unsigned long start;
int cpu;
@@ -192,9 +191,7 @@
raise_local();
put_cpu();
put_online_cpus();
- } else
-#endif
- {
+ } else {
preempt_disable();
raise_local();
preempt_enable();
diff --git a/arch/x86/kernel/cpu/mcheck/mce-internal.h b/arch/x86/kernel/cpu/mcheck/mce-internal.h
index cd74a3f..903043e 100644
--- a/arch/x86/kernel/cpu/mcheck/mce-internal.h
+++ b/arch/x86/kernel/cpu/mcheck/mce-internal.h
@@ -31,7 +31,7 @@
struct mce mce;
};
-void mce_gen_pool_process(void);
+void mce_gen_pool_process(struct work_struct *__unused);
bool mce_gen_pool_empty(void);
int mce_gen_pool_add(struct mce *mce);
int mce_gen_pool_init(void);
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c
index 537c664..8e9725c 100644
--- a/arch/x86/kernel/cpu/mcheck/mce.c
+++ b/arch/x86/kernel/cpu/mcheck/mce.c
@@ -128,7 +128,6 @@
{
memset(m, 0, sizeof(struct mce));
m->cpu = m->extcpu = smp_processor_id();
- m->tsc = rdtsc();
/* We hope get_seconds stays lockless */
m->time = get_seconds();
m->cpuvendor = boot_cpu_data.x86_vendor;
@@ -217,9 +216,7 @@
{
atomic_inc(&num_notifiers);
- /* Ensure SRAO notifier has the highest priority in the decode chain. */
- if (nb != &mce_srao_nb && nb->priority == INT_MAX)
- nb->priority -= 1;
+ WARN_ON(nb->priority > MCE_PRIO_LOWEST && nb->priority < MCE_PRIO_EDAC);
atomic_notifier_chain_register(&x86_mce_decoder_chain, nb);
}
@@ -583,7 +580,7 @@
}
static struct notifier_block mce_srao_nb = {
.notifier_call = srao_decode_notifier,
- .priority = INT_MAX,
+ .priority = MCE_PRIO_SRAO,
};
static int mce_default_notifier(struct notifier_block *nb, unsigned long val,
@@ -609,7 +606,7 @@
static struct notifier_block mce_default_nb = {
.notifier_call = mce_default_notifier,
/* lowest prio, we want it to run last. */
- .priority = 0,
+ .priority = MCE_PRIO_LOWEST,
};
/*
@@ -710,14 +707,8 @@
mce_gather_info(&m, NULL);
- /*
- * m.tsc was set in mce_setup(). Clear it if not requested.
- *
- * FIXME: Propagate @flags to mce_gather_info/mce_setup() to avoid
- * that dance.
- */
- if (!(flags & MCP_TIMESTAMP))
- m.tsc = 0;
+ if (flags & MCP_TIMESTAMP)
+ m.tsc = rdtsc();
for (i = 0; i < mca_cfg.banks; i++) {
if (!mce_banks[i].ctl || !test_bit(i, *b))
@@ -1156,6 +1147,7 @@
goto out;
mce_gather_info(&m, regs);
+ m.tsc = rdtsc();
final = this_cpu_ptr(&mces_seen);
*final = m;
@@ -1322,41 +1314,6 @@
#endif
/*
- * Action optional processing happens here (picking up
- * from the list of faulting pages that do_machine_check()
- * placed into the genpool).
- */
-static void mce_process_work(struct work_struct *dummy)
-{
- mce_gen_pool_process();
-}
-
-#ifdef CONFIG_X86_MCE_INTEL
-/***
- * mce_log_therm_throt_event - Logs the thermal throttling event to mcelog
- * @cpu: The CPU on which the event occurred.
- * @status: Event status information
- *
- * This function should be called by the thermal interrupt after the
- * event has been processed and the decision was made to log the event
- * further.
- *
- * The status parameter will be saved to the 'status' field of 'struct mce'
- * and historically has been the register value of the
- * MSR_IA32_THERMAL_STATUS (Intel) msr.
- */
-void mce_log_therm_throt_event(__u64 status)
-{
- struct mce m;
-
- mce_setup(&m);
- m.bank = MCE_THERMAL_BANK;
- m.status = status;
- mce_log(&m);
-}
-#endif /* CONFIG_X86_MCE_INTEL */
-
-/*
* Periodic polling timer for "silent" machine check errors. If the
* poller finds an MCE, poll 2x faster. When the poller finds no more
* errors, poll 2x slower (up to check_interval seconds).
@@ -2189,7 +2146,7 @@
mce_register_decode_chain(&mce_default_nb);
mcheck_vendor_init_severity();
- INIT_WORK(&mce_work, mce_process_work);
+ INIT_WORK(&mce_work, mce_gen_pool_process);
init_irq_work(&mce_irq_work, mce_irq_work_cb);
return 0;
diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c
index a5fd137..9e5427d 100644
--- a/arch/x86/kernel/cpu/mcheck/mce_amd.c
+++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c
@@ -192,6 +192,7 @@
smca_banks[bank].hwid = s_hwid;
smca_banks[bank].id = instance_id;
+ smca_banks[bank].sysfs_id = s_hwid->count++;
break;
}
}
@@ -777,7 +778,8 @@
mce_setup(&m);
m.status = status;
- m.bank = bank;
+ m.bank = bank;
+ m.tsc = rdtsc();
if (threshold_err)
m.misc = misc;
@@ -1064,9 +1066,12 @@
return NULL;
}
+ if (smca_banks[bank].hwid->count == 1)
+ return smca_get_name(bank_type);
+
snprintf(buf_mcatype, MAX_MCATYPE_NAME_LEN,
"%s_%x", smca_get_name(bank_type),
- smca_banks[bank].id);
+ smca_banks[bank].sysfs_id);
return buf_mcatype;
}
diff --git a/arch/x86/kernel/cpu/mcheck/therm_throt.c b/arch/x86/kernel/cpu/mcheck/therm_throt.c
index 465aca8..85469f8 100644
--- a/arch/x86/kernel/cpu/mcheck/therm_throt.c
+++ b/arch/x86/kernel/cpu/mcheck/therm_throt.c
@@ -6,7 +6,7 @@
*
* Maintains a counter in /sys that keeps track of the number of thermal
* events, such that the user knows how bad the thermal problem might be
- * (since the logging to syslog and mcelog is rate limited).
+ * (since the logging to syslog is rate limited).
*
* Author: Dmitriy Zavin (dmitriyz@google.com)
*
@@ -141,13 +141,8 @@
* IRQ has been acknowledged.
*
* It will take care of rate limiting and printing messages to the syslog.
- *
- * Returns: 0 : Event should NOT be further logged, i.e. still in
- * "timeout" from previous log message.
- * 1 : Event should be logged further, and a message has been
- * printed to the syslog.
*/
-static int therm_throt_process(bool new_event, int event, int level)
+static void therm_throt_process(bool new_event, int event, int level)
{
struct _thermal_state *state;
unsigned int this_cpu = smp_processor_id();
@@ -162,16 +157,16 @@
else if (event == POWER_LIMIT_EVENT)
state = &pstate->core_power_limit;
else
- return 0;
+ return;
} else if (level == PACKAGE_LEVEL) {
if (event == THERMAL_THROTTLING_EVENT)
state = &pstate->package_throttle;
else if (event == POWER_LIMIT_EVENT)
state = &pstate->package_power_limit;
else
- return 0;
+ return;
} else
- return 0;
+ return;
old_event = state->new_event;
state->new_event = new_event;
@@ -181,7 +176,7 @@
if (time_before64(now, state->next_check) &&
state->count != state->last_count)
- return 0;
+ return;
state->next_check = now + CHECK_INTERVAL;
state->last_count = state->count;
@@ -193,16 +188,14 @@
this_cpu,
level == CORE_LEVEL ? "Core" : "Package",
state->count);
- return 1;
+ return;
}
if (old_event) {
if (event == THERMAL_THROTTLING_EVENT)
pr_info("CPU%d: %s temperature/speed normal\n", this_cpu,
level == CORE_LEVEL ? "Core" : "Package");
- return 1;
+ return;
}
-
- return 0;
}
static int thresh_event_valid(int level, int event)
@@ -365,10 +358,9 @@
/* Check for violation of core thermal thresholds*/
notify_thresholds(msr_val);
- if (therm_throt_process(msr_val & THERM_STATUS_PROCHOT,
- THERMAL_THROTTLING_EVENT,
- CORE_LEVEL) != 0)
- mce_log_therm_throt_event(msr_val);
+ therm_throt_process(msr_val & THERM_STATUS_PROCHOT,
+ THERMAL_THROTTLING_EVENT,
+ CORE_LEVEL);
if (this_cpu_has(X86_FEATURE_PLN) && int_pln_enable)
therm_throt_process(msr_val & THERM_STATUS_POWER_LIMIT,
diff --git a/arch/x86/kernel/cpu/microcode/amd.c b/arch/x86/kernel/cpu/microcode/amd.c
index 079e817..7889ae4 100644
--- a/arch/x86/kernel/cpu/microcode/amd.c
+++ b/arch/x86/kernel/cpu/microcode/amd.c
@@ -42,16 +42,19 @@
/*
* This points to the current valid container of microcode patches which we will
- * save from the initrd/builtin before jettisoning its contents.
+ * save from the initrd/builtin before jettisoning its contents. @mc is the
+ * microcode patch we found to match.
*/
-struct container {
- u8 *data;
- size_t size;
-} cont;
+struct cont_desc {
+ struct microcode_amd *mc;
+ u32 cpuid_1_eax;
+ u32 psize;
+ u8 *data;
+ size_t size;
+};
static u32 ucode_new_rev;
static u8 amd_ucode_patch[PATCH_MAX_SIZE];
-static u16 this_equiv_id;
/*
* Microcode patch container file is prepended to the initrd in cpio
@@ -60,57 +63,13 @@
static const char
ucode_path[] __maybe_unused = "kernel/x86/microcode/AuthenticAMD.bin";
-static size_t compute_container_size(u8 *data, u32 total_size)
+static u16 find_equiv_id(struct equiv_cpu_entry *equiv_table, u32 sig)
{
- size_t size = 0;
- u32 *header = (u32 *)data;
-
- if (header[0] != UCODE_MAGIC ||
- header[1] != UCODE_EQUIV_CPU_TABLE_TYPE || /* type */
- header[2] == 0) /* size */
- return size;
-
- size = header[2] + CONTAINER_HDR_SZ;
- total_size -= size;
- data += size;
-
- while (total_size) {
- u16 patch_size;
-
- header = (u32 *)data;
-
- if (header[0] != UCODE_UCODE_TYPE)
- break;
-
- /*
- * Sanity-check patch size.
- */
- patch_size = header[1];
- if (patch_size > PATCH_MAX_SIZE)
- break;
-
- size += patch_size + SECTION_HDR_SIZE;
- data += patch_size + SECTION_HDR_SIZE;
- total_size -= patch_size + SECTION_HDR_SIZE;
+ for (; equiv_table && equiv_table->installed_cpu; equiv_table++) {
+ if (sig == equiv_table->installed_cpu)
+ return equiv_table->equiv_cpu;
}
- return size;
-}
-
-static inline u16 find_equiv_id(struct equiv_cpu_entry *equiv_cpu_table,
- unsigned int sig)
-{
- int i = 0;
-
- if (!equiv_cpu_table)
- return 0;
-
- while (equiv_cpu_table[i].installed_cpu != 0) {
- if (sig == equiv_cpu_table[i].installed_cpu)
- return equiv_cpu_table[i].equiv_cpu;
-
- i++;
- }
return 0;
}
@@ -118,91 +77,109 @@
* This scans the ucode blob for the proper container as we can have multiple
* containers glued together. Returns the equivalence ID from the equivalence
* table or 0 if none found.
+ * Returns the amount of bytes consumed while scanning. @desc contains all the
+ * data we're going to use in later stages of the application.
*/
-static u16
-find_proper_container(u8 *ucode, size_t size, struct container *ret_cont)
+static ssize_t parse_container(u8 *ucode, ssize_t size, struct cont_desc *desc)
{
- struct container ret = { NULL, 0 };
- u32 eax, ebx, ecx, edx;
struct equiv_cpu_entry *eq;
- int offset, left;
- u16 eq_id = 0;
- u32 *header;
- u8 *data;
+ ssize_t orig_size = size;
+ u32 *hdr = (u32 *)ucode;
+ u16 eq_id;
+ u8 *buf;
- data = ucode;
- left = size;
- header = (u32 *)data;
+ /* Am I looking at an equivalence table header? */
+ if (hdr[0] != UCODE_MAGIC ||
+ hdr[1] != UCODE_EQUIV_CPU_TABLE_TYPE ||
+ hdr[2] == 0)
+ return CONTAINER_HDR_SZ;
+ buf = ucode;
- /* find equiv cpu table */
- if (header[0] != UCODE_MAGIC ||
- header[1] != UCODE_EQUIV_CPU_TABLE_TYPE || /* type */
- header[2] == 0) /* size */
- return eq_id;
+ eq = (struct equiv_cpu_entry *)(buf + CONTAINER_HDR_SZ);
- eax = 0x00000001;
- ecx = 0;
- native_cpuid(&eax, &ebx, &ecx, &edx);
+ /* Find the equivalence ID of our CPU in this table: */
+ eq_id = find_equiv_id(eq, desc->cpuid_1_eax);
- while (left > 0) {
- eq = (struct equiv_cpu_entry *)(data + CONTAINER_HDR_SZ);
+ buf += hdr[2] + CONTAINER_HDR_SZ;
+ size -= hdr[2] + CONTAINER_HDR_SZ;
- ret.data = data;
+ /*
+ * Scan through the rest of the container to find where it ends. We do
+ * some basic sanity-checking too.
+ */
+ while (size > 0) {
+ struct microcode_amd *mc;
+ u32 patch_size;
- /* Advance past the container header */
- offset = header[2] + CONTAINER_HDR_SZ;
- data += offset;
- left -= offset;
+ hdr = (u32 *)buf;
- eq_id = find_equiv_id(eq, eax);
- if (eq_id) {
- ret.size = compute_container_size(ret.data, left + offset);
+ if (hdr[0] != UCODE_UCODE_TYPE)
+ break;
- /*
- * truncate how much we need to iterate over in the
- * ucode update loop below
- */
- left = ret.size - offset;
+ /* Sanity-check patch size. */
+ patch_size = hdr[1];
+ if (patch_size > PATCH_MAX_SIZE)
+ break;
- *ret_cont = ret;
- return eq_id;
+ /* Skip patch section header: */
+ buf += SECTION_HDR_SIZE;
+ size -= SECTION_HDR_SIZE;
+
+ mc = (struct microcode_amd *)buf;
+ if (eq_id == mc->hdr.processor_rev_id) {
+ desc->psize = patch_size;
+ desc->mc = mc;
}
- /*
- * support multiple container files appended together. if this
- * one does not have a matching equivalent cpu entry, we fast
- * forward to the next container file.
- */
- while (left > 0) {
- header = (u32 *)data;
-
- if (header[0] == UCODE_MAGIC &&
- header[1] == UCODE_EQUIV_CPU_TABLE_TYPE)
- break;
-
- offset = header[1] + SECTION_HDR_SIZE;
- data += offset;
- left -= offset;
- }
-
- /* mark where the next microcode container file starts */
- offset = data - (u8 *)ucode;
- ucode = data;
+ buf += patch_size;
+ size -= patch_size;
}
- return eq_id;
+ /*
+ * If we have found a patch (desc->mc), it means we're looking at the
+ * container which has a patch for this CPU so return 0 to mean, @ucode
+ * already points to the proper container. Otherwise, we return the size
+ * we scanned so that we can advance to the next container in the
+ * buffer.
+ */
+ if (desc->mc) {
+ desc->data = ucode;
+ desc->size = orig_size - size;
+
+ return 0;
+ }
+
+ return orig_size - size;
}
-static int __apply_microcode_amd(struct microcode_amd *mc_amd)
+/*
+ * Scan the ucode blob for the proper container as we can have multiple
+ * containers glued together.
+ */
+static void scan_containers(u8 *ucode, size_t size, struct cont_desc *desc)
+{
+ ssize_t rem = size;
+
+ while (rem >= 0) {
+ ssize_t s = parse_container(ucode, rem, desc);
+ if (!s)
+ return;
+
+ ucode += s;
+ rem -= s;
+ }
+}
+
+static int __apply_microcode_amd(struct microcode_amd *mc)
{
u32 rev, dummy;
- native_wrmsrl(MSR_AMD64_PATCH_LOADER, (u64)(long)&mc_amd->hdr.data_code);
+ native_wrmsrl(MSR_AMD64_PATCH_LOADER, (u64)(long)&mc->hdr.data_code);
/* verify patch application was successful */
native_rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
- if (rev != mc_amd->hdr.patch_id)
+ if (rev != mc->hdr.patch_id)
return -1;
return 0;
@@ -217,17 +194,16 @@
* load_microcode_amd() to save equivalent cpu table and microcode patches in
* kernel heap memory.
*
- * Returns true if container found (sets @ret_cont), false otherwise.
+ * Returns true if container found (sets @desc), false otherwise.
*/
-static bool apply_microcode_early_amd(void *ucode, size_t size, bool save_patch,
- struct container *ret_cont)
+static bool
+apply_microcode_early_amd(u32 cpuid_1_eax, void *ucode, size_t size, bool save_patch)
{
+ struct cont_desc desc = { 0 };
u8 (*patch)[PATCH_MAX_SIZE];
- u32 rev, *header, *new_rev;
- struct container ret;
- int offset, left;
- u16 eq_id = 0;
- u8 *data;
+ struct microcode_amd *mc;
+ u32 rev, dummy, *new_rev;
+ bool ret = false;
#ifdef CONFIG_X86_32
new_rev = (u32 *)__pa_nodebug(&ucode_new_rev);
@@ -237,50 +213,27 @@
patch = &amd_ucode_patch;
#endif
- if (check_current_patch_level(&rev, true))
- return false;
+ desc.cpuid_1_eax = cpuid_1_eax;
- eq_id = find_proper_container(ucode, size, &ret);
- if (!eq_id)
- return false;
+ scan_containers(ucode, size, &desc);
- this_equiv_id = eq_id;
- header = (u32 *)ret.data;
+ mc = desc.mc;
+ if (!mc)
+ return ret;
- /* We're pointing to an equiv table, skip over it. */
- data = ret.data + header[2] + CONTAINER_HDR_SZ;
- left = ret.size - (header[2] + CONTAINER_HDR_SZ);
+ native_rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
+ if (rev >= mc->hdr.patch_id)
+ return ret;
- while (left > 0) {
- struct microcode_amd *mc;
+ if (!__apply_microcode_amd(mc)) {
+ *new_rev = mc->hdr.patch_id;
+ ret = true;
- header = (u32 *)data;
- if (header[0] != UCODE_UCODE_TYPE || /* type */
- header[1] == 0) /* size */
- break;
-
- mc = (struct microcode_amd *)(data + SECTION_HDR_SIZE);
-
- if (eq_id == mc->hdr.processor_rev_id && rev < mc->hdr.patch_id) {
-
- if (!__apply_microcode_amd(mc)) {
- rev = mc->hdr.patch_id;
- *new_rev = rev;
-
- if (save_patch)
- memcpy(patch, mc, min_t(u32, header[1], PATCH_MAX_SIZE));
- }
- }
-
- offset = header[1] + SECTION_HDR_SIZE;
- data += offset;
- left -= offset;
+ if (save_patch)
+ memcpy(patch, mc, min_t(u32, desc.psize, PATCH_MAX_SIZE));
}
- if (ret_cont)
- *ret_cont = ret;
-
- return true;
+ return ret;
}
static bool get_builtin_microcode(struct cpio_data *cp, unsigned int family)
@@ -298,10 +251,9 @@
#endif
}
-void __init load_ucode_amd_bsp(unsigned int family)
+void __load_ucode_amd(unsigned int cpuid_1_eax, struct cpio_data *ret)
{
struct ucode_cpu_info *uci;
- u32 eax, ebx, ecx, edx;
struct cpio_data cp;
const char *path;
bool use_pa;
@@ -316,184 +268,95 @@
use_pa = false;
}
- if (!get_builtin_microcode(&cp, family))
+ if (!get_builtin_microcode(&cp, x86_family(cpuid_1_eax)))
cp = find_microcode_in_initrd(path, use_pa);
+ /* Needed in load_microcode_amd() */
+ uci->cpu_sig.sig = cpuid_1_eax;
+
+ *ret = cp;
+}
+
+void __init load_ucode_amd_bsp(unsigned int cpuid_1_eax)
+{
+ struct cpio_data cp = { };
+
+ __load_ucode_amd(cpuid_1_eax, &cp);
if (!(cp.data && cp.size))
return;
- /* Get BSP's CPUID.EAX(1), needed in load_microcode_amd() */
- eax = 1;
- ecx = 0;
- native_cpuid(&eax, &ebx, &ecx, &edx);
- uci->cpu_sig.sig = eax;
-
- apply_microcode_early_amd(cp.data, cp.size, true, NULL);
+ apply_microcode_early_amd(cpuid_1_eax, cp.data, cp.size, true);
}
-#ifdef CONFIG_X86_32
-/*
- * On 32-bit, since AP's early load occurs before paging is turned on, we
- * cannot traverse cpu_equiv_table and microcode_cache in kernel heap memory.
- * So during cold boot, AP will apply_ucode_in_initrd() just like the BSP.
- * In save_microcode_in_initrd_amd() BSP's patch is copied to amd_ucode_patch,
- * which is used upon resume from suspend.
- */
-void load_ucode_amd_ap(unsigned int family)
+void load_ucode_amd_ap(unsigned int cpuid_1_eax)
{
struct microcode_amd *mc;
struct cpio_data cp;
+ u32 *new_rev, rev, dummy;
- mc = (struct microcode_amd *)__pa_nodebug(amd_ucode_patch);
- if (mc->hdr.patch_id && mc->hdr.processor_rev_id) {
- __apply_microcode_amd(mc);
- return;
+ if (IS_ENABLED(CONFIG_X86_32)) {
+ mc = (struct microcode_amd *)__pa_nodebug(amd_ucode_patch);
+ new_rev = (u32 *)__pa_nodebug(&ucode_new_rev);
+ } else {
+ mc = (struct microcode_amd *)amd_ucode_patch;
+ new_rev = &ucode_new_rev;
}
- if (!get_builtin_microcode(&cp, family))
- cp = find_microcode_in_initrd((const char *)__pa_nodebug(ucode_path), true);
+ native_rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
+ /* Check whether we have saved a new patch already: */
+ if (*new_rev && rev < mc->hdr.patch_id) {
+ if (!__apply_microcode_amd(mc)) {
+ *new_rev = mc->hdr.patch_id;
+ return;
+ }
+ }
+
+ __load_ucode_amd(cpuid_1_eax, &cp);
if (!(cp.data && cp.size))
return;
- /*
- * This would set amd_ucode_patch above so that the following APs can
- * use it directly instead of going down this path again.
- */
- apply_microcode_early_amd(cp.data, cp.size, true, NULL);
+ apply_microcode_early_amd(cpuid_1_eax, cp.data, cp.size, false);
}
-#else
-void load_ucode_amd_ap(unsigned int family)
-{
- struct equiv_cpu_entry *eq;
- struct microcode_amd *mc;
- u32 rev, eax;
- u16 eq_id;
-
- /* 64-bit runs with paging enabled, thus early==false. */
- if (check_current_patch_level(&rev, false))
- return;
-
- /* First AP hasn't cached it yet, go through the blob. */
- if (!cont.data) {
- struct cpio_data cp = { NULL, 0, "" };
-
- if (cont.size == -1)
- return;
-
-reget:
- if (!get_builtin_microcode(&cp, family)) {
-#ifdef CONFIG_BLK_DEV_INITRD
- if (!initrd_gone)
- cp = find_cpio_data(ucode_path, (void *)initrd_start,
- initrd_end - initrd_start, NULL);
-#endif
- if (!(cp.data && cp.size)) {
- /*
- * Mark it so that other APs do not scan again
- * for no real reason and slow down boot
- * needlessly.
- */
- cont.size = -1;
- return;
- }
- }
-
- if (!apply_microcode_early_amd(cp.data, cp.size, false, &cont)) {
- cont.size = -1;
- return;
- }
- }
-
- eax = cpuid_eax(0x00000001);
- eq = (struct equiv_cpu_entry *)(cont.data + CONTAINER_HDR_SZ);
-
- eq_id = find_equiv_id(eq, eax);
- if (!eq_id)
- return;
-
- if (eq_id == this_equiv_id) {
- mc = (struct microcode_amd *)amd_ucode_patch;
-
- if (mc && rev < mc->hdr.patch_id) {
- if (!__apply_microcode_amd(mc))
- ucode_new_rev = mc->hdr.patch_id;
- }
-
- } else {
-
- /*
- * AP has a different equivalence ID than BSP, looks like
- * mixed-steppings silicon so go through the ucode blob anew.
- */
- goto reget;
- }
-}
-#endif /* CONFIG_X86_32 */
static enum ucode_state
load_microcode_amd(int cpu, u8 family, const u8 *data, size_t size);
-int __init save_microcode_in_initrd_amd(unsigned int fam)
+int __init save_microcode_in_initrd_amd(unsigned int cpuid_1_eax)
{
+ struct cont_desc desc = { 0 };
enum ucode_state ret;
- int retval = 0;
- u16 eq_id;
+ struct cpio_data cp;
- if (!cont.data) {
- if (IS_ENABLED(CONFIG_X86_32) && (cont.size != -1)) {
- struct cpio_data cp = { NULL, 0, "" };
+ cp = find_microcode_in_initrd(ucode_path, false);
+ if (!(cp.data && cp.size))
+ return -EINVAL;
-#ifdef CONFIG_BLK_DEV_INITRD
- cp = find_cpio_data(ucode_path, (void *)initrd_start,
- initrd_end - initrd_start, NULL);
-#endif
+ desc.cpuid_1_eax = cpuid_1_eax;
- if (!(cp.data && cp.size)) {
- cont.size = -1;
- return -EINVAL;
- }
+ scan_containers(cp.data, cp.size, &desc);
+ if (!desc.mc)
+ return -EINVAL;
- eq_id = find_proper_container(cp.data, cp.size, &cont);
- if (!eq_id) {
- cont.size = -1;
- return -EINVAL;
- }
-
- } else
- return -EINVAL;
- }
-
- ret = load_microcode_amd(smp_processor_id(), fam, cont.data, cont.size);
+ ret = load_microcode_amd(smp_processor_id(), x86_family(cpuid_1_eax),
+ desc.data, desc.size);
if (ret != UCODE_OK)
- retval = -EINVAL;
+ return -EINVAL;
- /*
- * This will be freed any msec now, stash patches for the current
- * family and switch to patch cache for cpu hotplug, etc later.
- */
- cont.data = NULL;
- cont.size = 0;
-
- return retval;
+ return 0;
}
void reload_ucode_amd(void)
{
struct microcode_amd *mc;
- u32 rev;
-
- /*
- * early==false because this is a syscore ->resume path and by
- * that time paging is long enabled.
- */
- if (check_current_patch_level(&rev, false))
- return;
+ u32 rev, dummy;
mc = (struct microcode_amd *)amd_ucode_patch;
if (!mc)
return;
+ rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
+
if (rev < mc->hdr.patch_id) {
if (!__apply_microcode_amd(mc)) {
ucode_new_rev = mc->hdr.patch_id;
@@ -631,60 +494,13 @@
return patch_size;
}
-/*
- * Those patch levels cannot be updated to newer ones and thus should be final.
- */
-static u32 final_levels[] = {
- 0x01000098,
- 0x0100009f,
- 0x010000af,
- 0, /* T-101 terminator */
-};
-
-/*
- * Check the current patch level on this CPU.
- *
- * @rev: Use it to return the patch level. It is set to 0 in the case of
- * error.
- *
- * Returns:
- * - true: if update should stop
- * - false: otherwise
- */
-bool check_current_patch_level(u32 *rev, bool early)
-{
- u32 lvl, dummy, i;
- bool ret = false;
- u32 *levels;
-
- native_rdmsr(MSR_AMD64_PATCH_LEVEL, lvl, dummy);
-
- if (IS_ENABLED(CONFIG_X86_32) && early)
- levels = (u32 *)__pa_nodebug(&final_levels);
- else
- levels = final_levels;
-
- for (i = 0; levels[i]; i++) {
- if (lvl == levels[i]) {
- lvl = 0;
- ret = true;
- break;
- }
- }
-
- if (rev)
- *rev = lvl;
-
- return ret;
-}
-
static int apply_microcode_amd(int cpu)
{
struct cpuinfo_x86 *c = &cpu_data(cpu);
struct microcode_amd *mc_amd;
struct ucode_cpu_info *uci;
struct ucode_patch *p;
- u32 rev;
+ u32 rev, dummy;
BUG_ON(raw_smp_processor_id() != cpu);
@@ -697,8 +513,7 @@
mc_amd = p->data;
uci->mc = p->data;
- if (check_current_patch_level(&rev, false))
- return -1;
+ rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
/* need to apply patch? */
if (rev >= mc_amd->hdr.patch_id) {
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
index 73102d9..b4a4cd3 100644
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -66,19 +66,50 @@
struct ucode_cpu_info ucode_cpu_info[NR_CPUS];
-/*
- * Operations that are run on a target cpu:
- */
-
struct cpu_info_ctx {
struct cpu_signature *cpu_sig;
int err;
};
+/*
+ * Those patch levels cannot be updated to newer ones and thus should be final.
+ */
+static u32 final_levels[] = {
+ 0x01000098,
+ 0x0100009f,
+ 0x010000af,
+ 0, /* T-101 terminator */
+};
+
+/*
+ * Check the current patch level on this CPU.
+ *
+ * Returns:
+ * - true: if update should stop
+ * - false: otherwise
+ */
+static bool amd_check_current_patch_level(void)
+{
+ u32 lvl, dummy, i;
+ u32 *levels;
+
+ native_rdmsr(MSR_AMD64_PATCH_LEVEL, lvl, dummy);
+
+ if (IS_ENABLED(CONFIG_X86_32))
+ levels = (u32 *)__pa_nodebug(&final_levels);
+ else
+ levels = final_levels;
+
+ for (i = 0; levels[i]; i++) {
+ if (lvl == levels[i])
+ return true;
+ }
+ return false;
+}
+
static bool __init check_loader_disabled_bsp(void)
{
static const char *__dis_opt_str = "dis_ucode_ldr";
- u32 a, b, c, d;
#ifdef CONFIG_X86_32
const char *cmdline = (const char *)__pa_nodebug(boot_command_line);
@@ -94,18 +125,19 @@
if (!have_cpuid_p())
return *res;
- a = 1;
- c = 0;
- native_cpuid(&a, &b, &c, &d);
-
/*
* CPUID(1).ECX[31]: reserved for hypervisor use. This is still not
* completely accurate as xen pv guests don't see that CPUID bit set but
* that's good enough as they don't land on the BSP path anyway.
*/
- if (c & BIT(31))
+ if (native_cpuid_ecx(1) & BIT(31))
return *res;
+ if (x86_cpuid_vendor() == X86_VENDOR_AMD) {
+ if (amd_check_current_patch_level())
+ return *res;
+ }
+
if (cmdline_find_option_bool(cmdline, option) <= 0)
*res = false;
@@ -133,23 +165,21 @@
void __init load_ucode_bsp(void)
{
- int vendor;
- unsigned int family;
+ unsigned int cpuid_1_eax;
if (check_loader_disabled_bsp())
return;
- vendor = x86_cpuid_vendor();
- family = x86_cpuid_family();
+ cpuid_1_eax = native_cpuid_eax(1);
- switch (vendor) {
+ switch (x86_cpuid_vendor()) {
case X86_VENDOR_INTEL:
- if (family >= 6)
+ if (x86_family(cpuid_1_eax) >= 6)
load_ucode_intel_bsp();
break;
case X86_VENDOR_AMD:
- if (family >= 0x10)
- load_ucode_amd_bsp(family);
+ if (x86_family(cpuid_1_eax) >= 0x10)
+ load_ucode_amd_bsp(cpuid_1_eax);
break;
default:
break;
@@ -167,22 +197,21 @@
void load_ucode_ap(void)
{
- int vendor, family;
+ unsigned int cpuid_1_eax;
if (check_loader_disabled_ap())
return;
- vendor = x86_cpuid_vendor();
- family = x86_cpuid_family();
+ cpuid_1_eax = native_cpuid_eax(1);
- switch (vendor) {
+ switch (x86_cpuid_vendor()) {
case X86_VENDOR_INTEL:
- if (family >= 6)
+ if (x86_family(cpuid_1_eax) >= 6)
load_ucode_intel_ap();
break;
case X86_VENDOR_AMD:
- if (family >= 0x10)
- load_ucode_amd_ap(family);
+ if (x86_family(cpuid_1_eax) >= 0x10)
+ load_ucode_amd_ap(cpuid_1_eax);
break;
default:
break;
@@ -201,7 +230,7 @@
break;
case X86_VENDOR_AMD:
if (c->x86 >= 0x10)
- ret = save_microcode_in_initrd_amd(c->x86);
+ return save_microcode_in_initrd_amd(cpuid_eax(1));
break;
default:
break;
diff --git a/arch/x86/kernel/cpu/transmeta.c b/arch/x86/kernel/cpu/transmeta.c
index 3417856..c1ea5b9 100644
--- a/arch/x86/kernel/cpu/transmeta.c
+++ b/arch/x86/kernel/cpu/transmeta.c
@@ -1,4 +1,5 @@
#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/mm.h>
#include <asm/cpufeature.h>
#include <asm/msr.h>
@@ -14,6 +15,8 @@
if (xlvl >= 0x80860001)
c->x86_capability[CPUID_8086_0001_EDX] = cpuid_edx(0x80860001);
}
+
+ clear_sched_clock_stable();
}
static void init_transmeta(struct cpuinfo_x86 *c)
diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c
index 90e8dde..b2bbad6 100644
--- a/arch/x86/kernel/e820.c
+++ b/arch/x86/kernel/e820.c
@@ -580,24 +580,19 @@
}
#define MAX_GAP_END 0x100000000ull
/*
- * Search for a gap in the e820 memory space from start_addr to end_addr.
+ * Search for a gap in the e820 memory space from 0 to MAX_GAP_END.
*/
-__init int e820_search_gap(unsigned long *gapstart, unsigned long *gapsize,
- unsigned long start_addr, unsigned long long end_addr)
+static int __init e820_search_gap(unsigned long *gapstart,
+ unsigned long *gapsize)
{
- unsigned long long last;
+ unsigned long long last = MAX_GAP_END;
int i = e820->nr_map;
int found = 0;
- last = (end_addr && end_addr < MAX_GAP_END) ? end_addr : MAX_GAP_END;
-
while (--i >= 0) {
unsigned long long start = e820->map[i].addr;
unsigned long long end = start + e820->map[i].size;
- if (end < start_addr)
- continue;
-
/*
* Since "last" is at most 4GB, we know we'll
* fit in 32 bits if this condition is true
@@ -628,18 +623,19 @@
unsigned long gapstart, gapsize;
int found;
- gapstart = 0x10000000;
gapsize = 0x400000;
- found = e820_search_gap(&gapstart, &gapsize, 0, MAX_GAP_END);
+ found = e820_search_gap(&gapstart, &gapsize);
-#ifdef CONFIG_X86_64
if (!found) {
+#ifdef CONFIG_X86_64
gapstart = (max_pfn << PAGE_SHIFT) + 1024*1024;
printk(KERN_ERR
"e820: cannot find a gap in the 32bit address range\n"
"e820: PCI devices with unassigned 32bit BARs may break!\n");
- }
+#else
+ gapstart = 0x10000000;
#endif
+ }
/*
* e820_reserve_resources_late protect stolen RAM already
diff --git a/arch/x86/kernel/fpu/core.c b/arch/x86/kernel/fpu/core.c
index de72344..e1114f0 100644
--- a/arch/x86/kernel/fpu/core.c
+++ b/arch/x86/kernel/fpu/core.c
@@ -9,7 +9,6 @@
#include <asm/fpu/regset.h>
#include <asm/fpu/signal.h>
#include <asm/fpu/types.h>
-#include <asm/fpu/xstate.h>
#include <asm/traps.h>
#include <linux/hardirq.h>
@@ -179,14 +178,8 @@
memset(state, 0, fpu_kernel_xstate_size);
- /*
- * XRSTORS requires that this bit is set in xcomp_bv, or
- * it will #GP. Make sure it is replaced after the memset().
- */
if (static_cpu_has(X86_FEATURE_XSAVES))
- state->xsave.header.xcomp_bv = XCOMP_BV_COMPACTED_FORMAT |
- xfeatures_mask;
-
+ fpstate_init_xstate(&state->xsave);
if (static_cpu_has(X86_FEATURE_FXSR))
fpstate_init_fxstate(&state->fxsave);
else
diff --git a/arch/x86/kernel/fpu/init.c b/arch/x86/kernel/fpu/init.c
index 60dece3..19bdd1b 100644
--- a/arch/x86/kernel/fpu/init.c
+++ b/arch/x86/kernel/fpu/init.c
@@ -48,13 +48,7 @@
fpu__init_cpu_xstate();
}
-/*
- * The earliest FPU detection code.
- *
- * Set the X86_FEATURE_FPU CPU-capability bit based on
- * trying to execute an actual sequence of FPU instructions:
- */
-static void fpu__init_system_early_generic(struct cpuinfo_x86 *c)
+static bool fpu__probe_without_cpuid(void)
{
unsigned long cr0;
u16 fsw, fcw;
@@ -65,18 +59,25 @@
cr0 &= ~(X86_CR0_TS | X86_CR0_EM);
write_cr0(cr0);
- if (!test_bit(X86_FEATURE_FPU, (unsigned long *)cpu_caps_cleared)) {
- asm volatile("fninit ; fnstsw %0 ; fnstcw %1"
- : "+m" (fsw), "+m" (fcw));
+ asm volatile("fninit ; fnstsw %0 ; fnstcw %1" : "+m" (fsw), "+m" (fcw));
- if (fsw == 0 && (fcw & 0x103f) == 0x003f)
- set_cpu_cap(c, X86_FEATURE_FPU);
+ pr_info("x86/fpu: Probing for FPU: FSW=0x%04hx FCW=0x%04hx\n", fsw, fcw);
+
+ return fsw == 0 && (fcw & 0x103f) == 0x003f;
+}
+
+static void fpu__init_system_early_generic(struct cpuinfo_x86 *c)
+{
+ if (!boot_cpu_has(X86_FEATURE_CPUID) &&
+ !test_bit(X86_FEATURE_FPU, (unsigned long *)cpu_caps_cleared)) {
+ if (fpu__probe_without_cpuid())
+ setup_force_cpu_cap(X86_FEATURE_FPU);
else
- clear_cpu_cap(c, X86_FEATURE_FPU);
+ setup_clear_cpu_cap(X86_FEATURE_FPU);
}
#ifndef CONFIG_MATH_EMULATION
- if (!boot_cpu_has(X86_FEATURE_FPU)) {
+ if (!test_cpu_cap(&boot_cpu_data, X86_FEATURE_FPU)) {
pr_emerg("x86/fpu: Giving up, no FPU found and no math emulation present\n");
for (;;)
asm volatile("hlt");
diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c
index 1d77704..c24ac1e 100644
--- a/arch/x86/kernel/fpu/xstate.c
+++ b/arch/x86/kernel/fpu/xstate.c
@@ -78,6 +78,7 @@
setup_clear_cpu_cap(X86_FEATURE_PKU);
setup_clear_cpu_cap(X86_FEATURE_AVX512_4VNNIW);
setup_clear_cpu_cap(X86_FEATURE_AVX512_4FMAPS);
+ setup_clear_cpu_cap(X86_FEATURE_AVX512_VPOPCNTDQ);
}
/*
@@ -705,8 +706,14 @@
WARN_ON_FPU(!on_boot_cpu);
on_boot_cpu = 0;
+ if (!boot_cpu_has(X86_FEATURE_FPU)) {
+ pr_info("x86/fpu: No FPU detected\n");
+ return;
+ }
+
if (!boot_cpu_has(X86_FEATURE_XSAVE)) {
- pr_info("x86/fpu: Legacy x87 FPU detected.\n");
+ pr_info("x86/fpu: x87 FPU will use %s\n",
+ boot_cpu_has(X86_FEATURE_FXSR) ? "FXSAVE" : "FSAVE");
return;
}
diff --git a/arch/x86/kernel/head32.c b/arch/x86/kernel/head32.c
index f16c55b..e5fb436 100644
--- a/arch/x86/kernel/head32.c
+++ b/arch/x86/kernel/head32.c
@@ -49,3 +49,65 @@
start_kernel();
}
+
+/*
+ * Initialize page tables. This creates a PDE and a set of page
+ * tables, which are located immediately beyond __brk_base. The variable
+ * _brk_end is set up to point to the first "safe" location.
+ * Mappings are created both at virtual address 0 (identity mapping)
+ * and PAGE_OFFSET for up to _end.
+ *
+ * In PAE mode initial_page_table is statically defined to contain
+ * enough entries to cover the VMSPLIT option (that is the top 1, 2 or 3
+ * entries). The identity mapping is handled by pointing two PGD entries
+ * to the first kernel PMD. Note the upper half of each PMD or PTE are
+ * always zero at this stage.
+ */
+void __init mk_early_pgtbl_32(void)
+{
+#ifdef __pa
+#undef __pa
+#endif
+#define __pa(x) ((unsigned long)(x) - PAGE_OFFSET)
+ pte_t pte, *ptep;
+ int i;
+ unsigned long *ptr;
+ /* Enough space to fit pagetables for the low memory linear map */
+ const unsigned long limit = __pa(_end) +
+ (PAGE_TABLE_SIZE(LOWMEM_PAGES) << PAGE_SHIFT);
+#ifdef CONFIG_X86_PAE
+ pmd_t pl2, *pl2p = (pmd_t *)__pa(initial_pg_pmd);
+#define SET_PL2(pl2, val) { (pl2).pmd = (val); }
+#else
+ pgd_t pl2, *pl2p = (pgd_t *)__pa(initial_page_table);
+#define SET_PL2(pl2, val) { (pl2).pgd = (val); }
+#endif
+
+ ptep = (pte_t *)__pa(__brk_base);
+ pte.pte = PTE_IDENT_ATTR;
+
+ while ((pte.pte & PTE_PFN_MASK) < limit) {
+
+ SET_PL2(pl2, (unsigned long)ptep | PDE_IDENT_ATTR);
+ *pl2p = pl2;
+#ifndef CONFIG_X86_PAE
+ /* Kernel PDE entry */
+ *(pl2p + ((PAGE_OFFSET >> PGDIR_SHIFT))) = pl2;
+#endif
+ for (i = 0; i < PTRS_PER_PTE; i++) {
+ *ptep = pte;
+ pte.pte += PAGE_SIZE;
+ ptep++;
+ }
+
+ pl2p++;
+ }
+
+ ptr = (unsigned long *)__pa(&max_pfn_mapped);
+ /* Can't use pte_pfn() since it's a call with CONFIG_PARAVIRT */
+ *ptr = (pte.pte & PTE_PFN_MASK) >> PAGE_SHIFT;
+
+ ptr = (unsigned long *)__pa(&_brk_end);
+ *ptr = (unsigned long)ptep + PAGE_OFFSET;
+}
+
diff --git a/arch/x86/kernel/head_32.S b/arch/x86/kernel/head_32.S
index 4e8577d..1f85ee8 100644
--- a/arch/x86/kernel/head_32.S
+++ b/arch/x86/kernel/head_32.S
@@ -24,6 +24,7 @@
#include <asm/nops.h>
#include <asm/bootparam.h>
#include <asm/export.h>
+#include <asm/pgtable_32.h>
/* Physical address */
#define pa(X) ((X) - __PAGE_OFFSET)
@@ -41,44 +42,10 @@
#define X86_CAPABILITY new_cpu_data+CPUINFO_x86_capability
#define X86_VENDOR_ID new_cpu_data+CPUINFO_x86_vendor_id
-/*
- * This is how much memory in addition to the memory covered up to
- * and including _end we need mapped initially.
- * We need:
- * (KERNEL_IMAGE_SIZE/4096) / 1024 pages (worst case, non PAE)
- * (KERNEL_IMAGE_SIZE/4096) / 512 + 4 pages (worst case for PAE)
- *
- * Modulo rounding, each megabyte assigned here requires a kilobyte of
- * memory, which is currently unreclaimed.
- *
- * This should be a multiple of a page.
- *
- * KERNEL_IMAGE_SIZE should be greater than pa(_end)
- * and small than max_low_pfn, otherwise will waste some page table entries
- */
-
-#if PTRS_PER_PMD > 1
-#define PAGE_TABLE_SIZE(pages) (((pages) / PTRS_PER_PMD) + PTRS_PER_PGD)
-#else
-#define PAGE_TABLE_SIZE(pages) ((pages) / PTRS_PER_PGD)
-#endif
#define SIZEOF_PTREGS 17*4
/*
- * Number of possible pages in the lowmem region.
- *
- * We shift 2 by 31 instead of 1 by 32 to the left in order to avoid a
- * gas warning about overflowing shift count when gas has been compiled
- * with only a host target support using a 32-bit type for internal
- * representation.
- */
-LOWMEM_PAGES = (((2<<31) - __PAGE_OFFSET) >> PAGE_SHIFT)
-
-/* Enough space to fit pagetables for the low memory linear map */
-MAPPING_BEYOND_END = PAGE_TABLE_SIZE(LOWMEM_PAGES) << PAGE_SHIFT
-
-/*
* Worst-case size of the kernel mapping we need to make:
* a relocatable kernel can live anywhere in lowmem, so we need to be able
* to map all of lowmem.
@@ -160,90 +127,15 @@
call load_ucode_bsp
#endif
-/*
- * Initialize page tables. This creates a PDE and a set of page
- * tables, which are located immediately beyond __brk_base. The variable
- * _brk_end is set up to point to the first "safe" location.
- * Mappings are created both at virtual address 0 (identity mapping)
- * and PAGE_OFFSET for up to _end.
- */
-#ifdef CONFIG_X86_PAE
+ /* Create early pagetables. */
+ call mk_early_pgtbl_32
- /*
- * In PAE mode initial_page_table is statically defined to contain
- * enough entries to cover the VMSPLIT option (that is the top 1, 2 or 3
- * entries). The identity mapping is handled by pointing two PGD entries
- * to the first kernel PMD.
- *
- * Note the upper half of each PMD or PTE are always zero at this stage.
- */
-
+ /* Do early initialization of the fixmap area */
+ movl $pa(initial_pg_fixmap)+PDE_IDENT_ATTR,%eax
+#ifdef CONFIG_X86_PAE
#define KPMDS (((-__PAGE_OFFSET) >> 30) & 3) /* Number of kernel PMDs */
-
- xorl %ebx,%ebx /* %ebx is kept at zero */
-
- movl $pa(__brk_base), %edi
- movl $pa(initial_pg_pmd), %edx
- movl $PTE_IDENT_ATTR, %eax
-10:
- leal PDE_IDENT_ATTR(%edi),%ecx /* Create PMD entry */
- movl %ecx,(%edx) /* Store PMD entry */
- /* Upper half already zero */
- addl $8,%edx
- movl $512,%ecx
-11:
- stosl
- xchgl %eax,%ebx
- stosl
- xchgl %eax,%ebx
- addl $0x1000,%eax
- loop 11b
-
- /*
- * End condition: we must map up to the end + MAPPING_BEYOND_END.
- */
- movl $pa(_end) + MAPPING_BEYOND_END + PTE_IDENT_ATTR, %ebp
- cmpl %ebp,%eax
- jb 10b
-1:
- addl $__PAGE_OFFSET, %edi
- movl %edi, pa(_brk_end)
- shrl $12, %eax
- movl %eax, pa(max_pfn_mapped)
-
- /* Do early initialization of the fixmap area */
- movl $pa(initial_pg_fixmap)+PDE_IDENT_ATTR,%eax
movl %eax,pa(initial_pg_pmd+0x1000*KPMDS-8)
-#else /* Not PAE */
-
-page_pde_offset = (__PAGE_OFFSET >> 20);
-
- movl $pa(__brk_base), %edi
- movl $pa(initial_page_table), %edx
- movl $PTE_IDENT_ATTR, %eax
-10:
- leal PDE_IDENT_ATTR(%edi),%ecx /* Create PDE entry */
- movl %ecx,(%edx) /* Store identity PDE entry */
- movl %ecx,page_pde_offset(%edx) /* Store kernel PDE entry */
- addl $4,%edx
- movl $1024, %ecx
-11:
- stosl
- addl $0x1000,%eax
- loop 11b
- /*
- * End condition: we must map up to the end + MAPPING_BEYOND_END.
- */
- movl $pa(_end) + MAPPING_BEYOND_END + PTE_IDENT_ATTR, %ebp
- cmpl %ebp,%eax
- jb 10b
- addl $__PAGE_OFFSET, %edi
- movl %edi, pa(_brk_end)
- shrl $12, %eax
- movl %eax, pa(max_pfn_mapped)
-
- /* Do early initialization of the fixmap area */
- movl $pa(initial_pg_fixmap)+PDE_IDENT_ATTR,%eax
+#else
movl %eax,pa(initial_page_table+0xffc)
#endif
@@ -666,6 +558,7 @@
__PAGE_ALIGNED_BSS
.align PAGE_SIZE
#ifdef CONFIG_X86_PAE
+.globl initial_pg_pmd
initial_pg_pmd:
.fill 1024*KPMDS,4,0
#else
diff --git a/arch/x86/kernel/itmt.c b/arch/x86/kernel/itmt.c
index cb9c1ed..f73f475 100644
--- a/arch/x86/kernel/itmt.c
+++ b/arch/x86/kernel/itmt.c
@@ -132,10 +132,8 @@
sysctl_sched_itmt_enabled = 1;
- if (sysctl_sched_itmt_enabled) {
- x86_topology_update = true;
- rebuild_sched_domains();
- }
+ x86_topology_update = true;
+ rebuild_sched_domains();
mutex_unlock(&itmt_update_mutex);
diff --git a/arch/x86/kernel/jump_label.c b/arch/x86/kernel/jump_label.c
index fc25f69..c37bd0f 100644
--- a/arch/x86/kernel/jump_label.c
+++ b/arch/x86/kernel/jump_label.c
@@ -32,8 +32,7 @@
* Something went wrong. Crash the box, as something could be
* corrupting the kernel.
*/
- pr_warning("Unexpected op at %pS [%p] (%02x %02x %02x %02x %02x) %s:%d\n",
- ip, ip, ip[0], ip[1], ip[2], ip[3], ip[4], __FILE__, line);
+ pr_crit("jump_label: Fatal kernel bug, unexpected op at %pS [%p] (%5ph) %d\n", ip, ip, ip, line);
BUG();
}
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
index eb35093..520b8df 100644
--- a/arch/x86/kernel/kprobes/core.c
+++ b/arch/x86/kernel/kprobes/core.c
@@ -745,7 +745,7 @@
* will be the real return address, and all the rest will
* point to kretprobe_trampoline.
*/
- hlist_for_each_entry_safe(ri, tmp, head, hlist) {
+ hlist_for_each_entry(ri, head, hlist) {
if (ri->task != current)
/* another task is sharing our hash bucket */
continue;
diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c
index 36bc664..099fcba 100644
--- a/arch/x86/kernel/kvm.c
+++ b/arch/x86/kernel/kvm.c
@@ -620,18 +620,4 @@
}
}
-static __init int kvm_spinlock_init_jump(void)
-{
- if (!kvm_para_available())
- return 0;
- if (!kvm_para_has_feature(KVM_FEATURE_PV_UNHALT))
- return 0;
-
- static_key_slow_inc(¶virt_ticketlocks_enabled);
- printk(KERN_INFO "KVM setup paravirtual spinlock\n");
-
- return 0;
-}
-early_initcall(kvm_spinlock_init_jump);
-
#endif /* CONFIG_PARAVIRT_SPINLOCKS */
diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c
index 2a5cafd..542710b 100644
--- a/arch/x86/kernel/kvmclock.c
+++ b/arch/x86/kernel/kvmclock.c
@@ -107,12 +107,12 @@
{
if (!stable) {
pv_time_ops.sched_clock = kvm_clock_read;
+ clear_sched_clock_stable();
return;
}
kvm_sched_clock_offset = kvm_clock_read();
pv_time_ops.sched_clock = kvm_sched_clock_read;
- set_sched_clock_stable();
printk(KERN_INFO "kvm-clock: using sched offset of %llu cycles\n",
kvm_sched_clock_offset);
diff --git a/arch/x86/kernel/paravirt-spinlocks.c b/arch/x86/kernel/paravirt-spinlocks.c
index 6d4bf81..6259327 100644
--- a/arch/x86/kernel/paravirt-spinlocks.c
+++ b/arch/x86/kernel/paravirt-spinlocks.c
@@ -42,6 +42,3 @@
#endif /* SMP */
};
EXPORT_SYMBOL(pv_lock_ops);
-
-struct static_key paravirt_ticketlocks_enabled = STATIC_KEY_INIT_FALSE;
-EXPORT_SYMBOL(paravirt_ticketlocks_enabled);
diff --git a/arch/x86/kernel/pci-calgary_64.c b/arch/x86/kernel/pci-calgary_64.c
index 5d400ba..d475179 100644
--- a/arch/x86/kernel/pci-calgary_64.c
+++ b/arch/x86/kernel/pci-calgary_64.c
@@ -296,7 +296,7 @@
/* were we called with bad_dma_address? */
badend = DMA_ERROR_CODE + (EMERGENCY_PAGES * PAGE_SIZE);
- if (unlikely((dma_addr >= DMA_ERROR_CODE) && (dma_addr < badend))) {
+ if (unlikely(dma_addr < badend)) {
WARN(1, KERN_ERR "Calgary: driver tried unmapping bad DMA "
"address 0x%Lx\n", dma_addr);
return;
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 4cfba94..69780ed 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -1176,6 +1176,20 @@
/* Allocate bigger log buffer */
setup_log_buf(1);
+ if (efi_enabled(EFI_BOOT)) {
+ switch (boot_params.secure_boot) {
+ case efi_secureboot_mode_disabled:
+ pr_info("Secure boot disabled\n");
+ break;
+ case efi_secureboot_mode_enabled:
+ pr_info("Secure boot enabled\n");
+ break;
+ default:
+ pr_info("Secure boot could not be determined\n");
+ break;
+ }
+ }
+
reserve_initrd();
acpi_table_upgrade();
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 99b920d..a0d3868 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -1347,8 +1347,7 @@
pr_info("CPU0: ");
print_cpu_info(&cpu_data(0));
- if (is_uv_system())
- uv_system_init();
+ uv_system_init();
set_mtrr_aps_delayed_init();
diff --git a/arch/x86/kernel/test_nx.c b/arch/x86/kernel/test_nx.c
deleted file mode 100644
index a3b875c..0000000
--- a/arch/x86/kernel/test_nx.c
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * test_nx.c: functional test for NX functionality
- *
- * (C) Copyright 2008 Intel Corporation
- * Author: Arjan van de Ven <arjan@linux.intel.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; version 2
- * of the License.
- */
-#include <linux/module.h>
-#include <linux/sort.h>
-#include <linux/slab.h>
-
-#include <linux/uaccess.h>
-#include <asm/asm.h>
-
-extern int rodata_test_data;
-
-/*
- * This file checks 4 things:
- * 1) Check if the stack is not executable
- * 2) Check if kmalloc memory is not executable
- * 3) Check if the .rodata section is not executable
- * 4) Check if the .data section of a module is not executable
- *
- * To do this, the test code tries to execute memory in stack/kmalloc/etc,
- * and then checks if the expected trap happens.
- *
- * Sadly, this implies having a dynamic exception handling table entry.
- * ... which can be done (and will make Rusty cry)... but it can only
- * be done in a stand-alone module with only 1 entry total.
- * (otherwise we'd have to sort and that's just too messy)
- */
-
-
-
-/*
- * We want to set up an exception handling point on our stack,
- * which means a variable value. This function is rather dirty
- * and walks the exception table of the module, looking for a magic
- * marker and replaces it with a specific function.
- */
-static void fudze_exception_table(void *marker, void *new)
-{
- struct module *mod = THIS_MODULE;
- struct exception_table_entry *extable;
-
- /*
- * Note: This module has only 1 exception table entry,
- * so searching and sorting is not needed. If that changes,
- * this would be the place to search and re-sort the exception
- * table.
- */
- if (mod->num_exentries > 1) {
- printk(KERN_ERR "test_nx: too many exception table entries!\n");
- printk(KERN_ERR "test_nx: test results are not reliable.\n");
- return;
- }
- extable = (struct exception_table_entry *)mod->extable;
- extable[0].insn = (unsigned long)new;
-}
-
-
-/*
- * exception tables get their symbols translated so we need
- * to use a fake function to put in there, which we can then
- * replace at runtime.
- */
-void foo_label(void);
-
-/*
- * returns 0 for not-executable, negative for executable
- *
- * Note: we cannot allow this function to be inlined, because
- * that would give us more than 1 exception table entry.
- * This in turn would break the assumptions above.
- */
-static noinline int test_address(void *address)
-{
- unsigned long result;
-
- /* Set up an exception table entry for our address */
- fudze_exception_table(&foo_label, address);
- result = 1;
- asm volatile(
- "foo_label:\n"
- "0: call *%[fake_code]\n"
- "1:\n"
- ".section .fixup,\"ax\"\n"
- "2: mov %[zero], %[rslt]\n"
- " ret\n"
- ".previous\n"
- _ASM_EXTABLE(0b,2b)
- : [rslt] "=r" (result)
- : [fake_code] "r" (address), [zero] "r" (0UL), "0" (result)
- );
- /* change the exception table back for the next round */
- fudze_exception_table(address, &foo_label);
-
- if (result)
- return -ENODEV;
- return 0;
-}
-
-static unsigned char test_data = 0xC3; /* 0xC3 is the opcode for "ret" */
-
-static int test_NX(void)
-{
- int ret = 0;
- /* 0xC3 is the opcode for "ret" */
- char stackcode[] = {0xC3, 0x90, 0 };
- char *heap;
-
- test_data = 0xC3;
-
- printk(KERN_INFO "Testing NX protection\n");
-
- /* Test 1: check if the stack is not executable */
- if (test_address(&stackcode)) {
- printk(KERN_ERR "test_nx: stack was executable\n");
- ret = -ENODEV;
- }
-
-
- /* Test 2: Check if the heap is executable */
- heap = kmalloc(64, GFP_KERNEL);
- if (!heap)
- return -ENOMEM;
- heap[0] = 0xC3; /* opcode for "ret" */
-
- if (test_address(heap)) {
- printk(KERN_ERR "test_nx: heap was executable\n");
- ret = -ENODEV;
- }
- kfree(heap);
-
- /*
- * The following 2 tests currently fail, this needs to get fixed
- * Until then, don't run them to avoid too many people getting scared
- * by the error message
- */
-
- /* Test 3: Check if the .rodata section is executable */
- if (rodata_test_data != 0xC3) {
- printk(KERN_ERR "test_nx: .rodata marker has invalid value\n");
- ret = -ENODEV;
- } else if (test_address(&rodata_test_data)) {
- printk(KERN_ERR "test_nx: .rodata section is executable\n");
- ret = -ENODEV;
- }
-
-#if 0
- /* Test 4: Check if the .data section of a module is executable */
- if (test_address(&test_data)) {
- printk(KERN_ERR "test_nx: .data section is executable\n");
- ret = -ENODEV;
- }
-
-#endif
- return ret;
-}
-
-static void test_exit(void)
-{
-}
-
-module_init(test_NX);
-module_exit(test_exit);
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Testcase for the NX infrastructure");
-MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>");
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index bf0c6d0..1dc86ee 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -563,11 +563,9 @@
* as we may switch to the interrupt stack.
*/
debug_stack_usage_inc();
- preempt_disable();
cond_local_irq_enable(regs);
do_trap(X86_TRAP_BP, SIGTRAP, "int3", regs, error_code, NULL);
cond_local_irq_disable(regs);
- preempt_enable_no_resched();
debug_stack_usage_dec();
exit:
ist_exit(regs);
@@ -742,14 +740,12 @@
debug_stack_usage_inc();
/* It's safe to allow irq's after DR6 has been saved */
- preempt_disable();
cond_local_irq_enable(regs);
if (v8086_mode(regs)) {
handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code,
X86_TRAP_DB);
cond_local_irq_disable(regs);
- preempt_enable_no_resched();
debug_stack_usage_dec();
goto exit;
}
@@ -769,7 +765,6 @@
if (tsk->thread.debugreg6 & (DR_STEP | DR_TRAP_BITS) || user_icebp)
send_sigtrap(tsk, regs, error_code, si_code);
cond_local_irq_disable(regs);
- preempt_enable_no_resched();
debug_stack_usage_dec();
exit:
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index 37e7cf5..2724dc8 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -1107,6 +1107,16 @@
return (u64)rdtsc_ordered();
}
+static void tsc_cs_mark_unstable(struct clocksource *cs)
+{
+ if (tsc_unstable)
+ return;
+ tsc_unstable = 1;
+ clear_sched_clock_stable();
+ disable_sched_clock_irqtime();
+ pr_info("Marking TSC unstable due to clocksource watchdog\n");
+}
+
/*
* .mask MUST be CLOCKSOURCE_MASK(64). See comment above read_tsc()
*/
@@ -1119,6 +1129,7 @@
CLOCK_SOURCE_MUST_VERIFY,
.archdata = { .vclock_mode = VCLOCK_TSC },
.resume = tsc_resume,
+ .mark_unstable = tsc_cs_mark_unstable,
};
void mark_tsc_unstable(char *reason)
diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c
index ec5d754..0442d98 100644
--- a/arch/x86/kernel/vm86_32.c
+++ b/arch/x86/kernel/vm86_32.c
@@ -160,11 +160,12 @@
static void mark_screen_rdonly(struct mm_struct *mm)
{
+ struct vm_area_struct *vma;
+ spinlock_t *ptl;
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
- spinlock_t *ptl;
int i;
down_write(&mm->mmap_sem);
@@ -177,7 +178,7 @@
pmd = pmd_offset(pud, 0xA0000);
if (pmd_trans_huge(*pmd)) {
- struct vm_area_struct *vma = find_vma(mm, 0xA0000);
+ vma = find_vma(mm, 0xA0000);
split_huge_pmd(vma, pmd, 0xA0000);
}
if (pmd_none_or_clear_bad(pmd))
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index 1572c35..2ecd7da 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -964,10 +964,11 @@
/* Calculate cpu time spent by current task in 100ns units */
static u64 current_task_runtime_100ns(void)
{
- cputime_t utime, stime;
+ u64 utime, stime;
task_cputime_adjusted(current, &utime, &stime);
- return div_u64(cputime_to_nsecs(utime + stime), 100);
+
+ return div_u64(utime + stime, 100);
}
static int kvm_hv_set_msr(struct kvm_vcpu *vcpu, u32 msr, u64 data, bool host)
diff --git a/arch/x86/lib/delay.c b/arch/x86/lib/delay.c
index 073d1f1..a8e91ae 100644
--- a/arch/x86/lib/delay.c
+++ b/arch/x86/lib/delay.c
@@ -156,13 +156,13 @@
inline void __const_udelay(unsigned long xloops)
{
+ unsigned long lpj = this_cpu_read(cpu_info.loops_per_jiffy) ? : loops_per_jiffy;
int d0;
xloops *= 4;
asm("mull %%edx"
:"=d" (xloops), "=&a" (d0)
- :"1" (xloops), "0"
- (this_cpu_read(cpu_info.loops_per_jiffy) * (HZ/4)));
+ :"1" (xloops), "0" (lpj * (HZ / 4)));
__delay(++xloops);
}
diff --git a/arch/x86/mm/dump_pagetables.c b/arch/x86/mm/dump_pagetables.c
index 8aa6bea..58b5bee 100644
--- a/arch/x86/mm/dump_pagetables.c
+++ b/arch/x86/mm/dump_pagetables.c
@@ -18,6 +18,7 @@
#include <linux/sched.h>
#include <linux/seq_file.h>
+#include <asm/kasan.h>
#include <asm/pgtable.h>
/*
@@ -51,6 +52,10 @@
LOW_KERNEL_NR,
VMALLOC_START_NR,
VMEMMAP_START_NR,
+#ifdef CONFIG_KASAN
+ KASAN_SHADOW_START_NR,
+ KASAN_SHADOW_END_NR,
+#endif
# ifdef CONFIG_X86_ESPFIX64
ESPFIX_START_NR,
# endif
@@ -76,6 +81,10 @@
{ 0/* PAGE_OFFSET */, "Low Kernel Mapping" },
{ 0/* VMALLOC_START */, "vmalloc() Area" },
{ 0/* VMEMMAP_START */, "Vmemmap" },
+#ifdef CONFIG_KASAN
+ { KASAN_SHADOW_START, "KASAN shadow" },
+ { KASAN_SHADOW_END, "KASAN shadow end" },
+#endif
# ifdef CONFIG_X86_ESPFIX64
{ ESPFIX_BASE_ADDR, "ESPfix Area", 16 },
# endif
@@ -327,18 +336,31 @@
#if PTRS_PER_PUD > 1
+/*
+ * This is an optimization for CONFIG_DEBUG_WX=y + CONFIG_KASAN=y
+ * KASAN fills page tables with the same values. Since there is no
+ * point in checking page table more than once we just skip repeated
+ * entries. This saves us dozens of seconds during boot.
+ */
+static bool pud_already_checked(pud_t *prev_pud, pud_t *pud, bool checkwx)
+{
+ return checkwx && prev_pud && (pud_val(*prev_pud) == pud_val(*pud));
+}
+
static void walk_pud_level(struct seq_file *m, struct pg_state *st, pgd_t addr,
unsigned long P)
{
int i;
pud_t *start;
pgprotval_t prot;
+ pud_t *prev_pud = NULL;
start = (pud_t *) pgd_page_vaddr(addr);
for (i = 0; i < PTRS_PER_PUD; i++) {
st->current_address = normalize_addr(P + i * PUD_LEVEL_MULT);
- if (!pud_none(*start)) {
+ if (!pud_none(*start) &&
+ !pud_already_checked(prev_pud, start, st->check_wx)) {
if (pud_large(*start) || !pud_present(*start)) {
prot = pud_flags(*start);
note_page(m, st, __pgprot(prot), 2);
@@ -349,6 +371,7 @@
} else
note_page(m, st, __pgprot(0), 2);
+ prev_pud = start;
start++;
}
}
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c
index 5a287e5..28d4213 100644
--- a/arch/x86/mm/pageattr.c
+++ b/arch/x86/mm/pageattr.c
@@ -214,7 +214,20 @@
int in_flags, struct page **pages)
{
unsigned int i, level;
+#ifdef CONFIG_PREEMPT
+ /*
+ * Avoid wbinvd() because it causes latencies on all CPUs,
+ * regardless of any CPU isolation that may be in effect.
+ *
+ * This should be extended for CAT enabled systems independent of
+ * PREEMPT because wbinvd() does not respect the CAT partitions and
+ * this is exposed to unpriviledged users through the graphics
+ * subsystem.
+ */
+ unsigned long do_wbinvd = 0;
+#else
unsigned long do_wbinvd = cache && numpages >= 1024; /* 4M threshold */
+#endif
BUG_ON(irqs_disabled());
diff --git a/arch/x86/mm/pat_rbtree.c b/arch/x86/mm/pat_rbtree.c
index 159b52c..d76485b 100644
--- a/arch/x86/mm/pat_rbtree.c
+++ b/arch/x86/mm/pat_rbtree.c
@@ -47,7 +47,7 @@
{
u64 ret = 0;
if (node) {
- struct memtype *data = container_of(node, struct memtype, rb);
+ struct memtype *data = rb_entry(node, struct memtype, rb);
ret = data->subtree_max_end;
}
return ret;
@@ -79,7 +79,7 @@
struct memtype *last_lower = NULL;
while (node) {
- struct memtype *data = container_of(node, struct memtype, rb);
+ struct memtype *data = rb_entry(node, struct memtype, rb);
if (get_subtree_max_end(node->rb_left) > start) {
/* Lowest overlap if any must be on left side */
@@ -121,7 +121,7 @@
node = rb_next(&match->rb);
if (node)
- match = container_of(node, struct memtype, rb);
+ match = rb_entry(node, struct memtype, rb);
else
match = NULL;
}
@@ -150,7 +150,7 @@
node = rb_next(&match->rb);
while (node) {
- match = container_of(node, struct memtype, rb);
+ match = rb_entry(node, struct memtype, rb);
if (match->start >= end) /* Checked all possible matches */
goto success;
@@ -181,7 +181,7 @@
struct rb_node *parent = NULL;
while (*node) {
- struct memtype *data = container_of(*node, struct memtype, rb);
+ struct memtype *data = rb_entry(*node, struct memtype, rb);
parent = *node;
if (data->subtree_max_end < newdata->end)
@@ -270,7 +270,7 @@
}
if (node) { /* pos == i */
- struct memtype *this = container_of(node, struct memtype, rb);
+ struct memtype *this = rb_entry(node, struct memtype, rb);
*out = *this;
return 0;
} else {
diff --git a/arch/x86/platform/efi/efi-bgrt.c b/arch/x86/platform/efi/efi-bgrt.c
index 6aad870..04ca876 100644
--- a/arch/x86/platform/efi/efi-bgrt.c
+++ b/arch/x86/platform/efi/efi-bgrt.c
@@ -19,8 +19,7 @@
#include <linux/efi.h>
#include <linux/efi-bgrt.h>
-struct acpi_table_bgrt *bgrt_tab;
-void *__initdata bgrt_image;
+struct acpi_table_bgrt bgrt_tab;
size_t __initdata bgrt_image_size;
struct bmp_header {
@@ -28,66 +27,58 @@
u32 size;
} __packed;
-void __init efi_bgrt_init(void)
+void __init efi_bgrt_init(struct acpi_table_header *table)
{
- acpi_status status;
void *image;
struct bmp_header bmp_header;
+ struct acpi_table_bgrt *bgrt = &bgrt_tab;
if (acpi_disabled)
return;
- status = acpi_get_table("BGRT", 0,
- (struct acpi_table_header **)&bgrt_tab);
- if (ACPI_FAILURE(status))
- return;
-
- if (bgrt_tab->header.length < sizeof(*bgrt_tab)) {
+ if (table->length < sizeof(bgrt_tab)) {
pr_notice("Ignoring BGRT: invalid length %u (expected %zu)\n",
- bgrt_tab->header.length, sizeof(*bgrt_tab));
+ table->length, sizeof(bgrt_tab));
return;
}
- if (bgrt_tab->version != 1) {
+ *bgrt = *(struct acpi_table_bgrt *)table;
+ if (bgrt->version != 1) {
pr_notice("Ignoring BGRT: invalid version %u (expected 1)\n",
- bgrt_tab->version);
- return;
+ bgrt->version);
+ goto out;
}
- if (bgrt_tab->status & 0xfe) {
+ if (bgrt->status & 0xfe) {
pr_notice("Ignoring BGRT: reserved status bits are non-zero %u\n",
- bgrt_tab->status);
- return;
+ bgrt->status);
+ goto out;
}
- if (bgrt_tab->image_type != 0) {
+ if (bgrt->image_type != 0) {
pr_notice("Ignoring BGRT: invalid image type %u (expected 0)\n",
- bgrt_tab->image_type);
- return;
+ bgrt->image_type);
+ goto out;
}
- if (!bgrt_tab->image_address) {
+ if (!bgrt->image_address) {
pr_notice("Ignoring BGRT: null image address\n");
- return;
+ goto out;
}
- image = memremap(bgrt_tab->image_address, sizeof(bmp_header), MEMREMAP_WB);
+ image = early_memremap(bgrt->image_address, sizeof(bmp_header));
if (!image) {
pr_notice("Ignoring BGRT: failed to map image header memory\n");
- return;
+ goto out;
}
memcpy(&bmp_header, image, sizeof(bmp_header));
- memunmap(image);
+ early_memunmap(image, sizeof(bmp_header));
if (bmp_header.id != 0x4d42) {
pr_notice("Ignoring BGRT: Incorrect BMP magic number 0x%x (expected 0x4d42)\n",
bmp_header.id);
- return;
+ goto out;
}
bgrt_image_size = bmp_header.size;
+ efi_mem_reserve(bgrt->image_address, bgrt_image_size);
- bgrt_image = memremap(bgrt_tab->image_address, bmp_header.size, MEMREMAP_WB);
- if (!bgrt_image) {
- pr_notice("Ignoring BGRT: failed to map image memory\n");
- bgrt_image = NULL;
- return;
- }
-
- efi_mem_reserve(bgrt_tab->image_address, bgrt_image_size);
+ return;
+out:
+ memset(bgrt, 0, sizeof(bgrt_tab));
}
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 274dfc4..565dff3 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -542,11 +542,6 @@
efi_print_memmap();
}
-void __init efi_late_init(void)
-{
- efi_bgrt_init();
-}
-
void __init efi_set_executable(efi_memory_desc_t *md, bool executable)
{
u64 addr, npages;
@@ -960,6 +955,11 @@
return;
}
+ if (efi_enabled(EFI_DBG)) {
+ pr_info("EFI runtime memory map:\n");
+ efi_print_memmap();
+ }
+
BUG_ON(!efi.systab);
if (efi_setup_page_tables(pa, 1 << pg_shift)) {
diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c
index 2f25a36..a4695da 100644
--- a/arch/x86/platform/efi/efi_64.c
+++ b/arch/x86/platform/efi/efi_64.c
@@ -414,10 +414,44 @@
efi_setup = phys_addr + sizeof(struct setup_data);
}
-void __init efi_runtime_update_mappings(void)
+static int __init efi_update_mappings(efi_memory_desc_t *md, unsigned long pf)
{
unsigned long pfn;
pgd_t *pgd = efi_pgd;
+ int err1, err2;
+
+ /* Update the 1:1 mapping */
+ pfn = md->phys_addr >> PAGE_SHIFT;
+ err1 = kernel_map_pages_in_pgd(pgd, pfn, md->phys_addr, md->num_pages, pf);
+ if (err1) {
+ pr_err("Error while updating 1:1 mapping PA 0x%llx -> VA 0x%llx!\n",
+ md->phys_addr, md->virt_addr);
+ }
+
+ err2 = kernel_map_pages_in_pgd(pgd, pfn, md->virt_addr, md->num_pages, pf);
+ if (err2) {
+ pr_err("Error while updating VA mapping PA 0x%llx -> VA 0x%llx!\n",
+ md->phys_addr, md->virt_addr);
+ }
+
+ return err1 || err2;
+}
+
+static int __init efi_update_mem_attr(struct mm_struct *mm, efi_memory_desc_t *md)
+{
+ unsigned long pf = 0;
+
+ if (md->attribute & EFI_MEMORY_XP)
+ pf |= _PAGE_NX;
+
+ if (!(md->attribute & EFI_MEMORY_RO))
+ pf |= _PAGE_RW;
+
+ return efi_update_mappings(md, pf);
+}
+
+void __init efi_runtime_update_mappings(void)
+{
efi_memory_desc_t *md;
if (efi_enabled(EFI_OLD_MEMMAP)) {
@@ -426,6 +460,24 @@
return;
}
+ /*
+ * Use the EFI Memory Attribute Table for mapping permissions if it
+ * exists, since it is intended to supersede EFI_PROPERTIES_TABLE.
+ */
+ if (efi_enabled(EFI_MEM_ATTR)) {
+ efi_memattr_apply_permissions(NULL, efi_update_mem_attr);
+ return;
+ }
+
+ /*
+ * EFI_MEMORY_ATTRIBUTES_TABLE is intended to replace
+ * EFI_PROPERTIES_TABLE. So, use EFI_PROPERTIES_TABLE to update
+ * permissions only if EFI_MEMORY_ATTRIBUTES_TABLE is not
+ * published by the firmware. Even if we find a buggy implementation of
+ * EFI_MEMORY_ATTRIBUTES_TABLE, don't fall back to
+ * EFI_PROPERTIES_TABLE, because of the same reason.
+ */
+
if (!efi_enabled(EFI_NX_PE_DATA))
return;
@@ -446,15 +498,7 @@
(md->type != EFI_RUNTIME_SERVICES_CODE))
pf |= _PAGE_RW;
- /* Update the 1:1 mapping */
- pfn = md->phys_addr >> PAGE_SHIFT;
- if (kernel_map_pages_in_pgd(pgd, pfn, md->phys_addr, md->num_pages, pf))
- pr_warn("Error mapping PA 0x%llx -> VA 0x%llx!\n",
- md->phys_addr, md->virt_addr);
-
- if (kernel_map_pages_in_pgd(pgd, pfn, md->virt_addr, md->num_pages, pf))
- pr_warn("Error mapping PA 0x%llx -> VA 0x%llx!\n",
- md->phys_addr, md->virt_addr);
+ efi_update_mappings(md, pf);
}
}
diff --git a/arch/x86/platform/intel-mid/device_libs/Makefile b/arch/x86/platform/intel-mid/device_libs/Makefile
index 90e4f2a..a7dbec4 100644
--- a/arch/x86/platform/intel-mid/device_libs/Makefile
+++ b/arch/x86/platform/intel-mid/device_libs/Makefile
@@ -5,14 +5,12 @@
# WiFi
obj-$(subst m,y,$(CONFIG_BRCMFMAC_SDIO)) += platform_bcm43xx.o
# IPC Devices
-obj-y += platform_ipc.o
obj-$(subst m,y,$(CONFIG_MFD_INTEL_MSIC)) += platform_msic.o
obj-$(subst m,y,$(CONFIG_SND_MFLD_MACHINE)) += platform_msic_audio.o
obj-$(subst m,y,$(CONFIG_GPIO_MSIC)) += platform_msic_gpio.o
obj-$(subst m,y,$(CONFIG_MFD_INTEL_MSIC)) += platform_msic_ocd.o
obj-$(subst m,y,$(CONFIG_MFD_INTEL_MSIC)) += platform_msic_battery.o
obj-$(subst m,y,$(CONFIG_INTEL_MID_POWER_BUTTON)) += platform_msic_power_btn.o
-obj-$(subst m,y,$(CONFIG_GPIO_INTEL_PMIC)) += platform_pmic_gpio.o
obj-$(subst m,y,$(CONFIG_INTEL_MFLD_THERMAL)) += platform_msic_thermal.o
# SPI Devices
obj-$(subst m,y,$(CONFIG_SPI_SPIDEV)) += platform_mrfld_spidev.o
@@ -28,4 +26,5 @@
obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_tca6416.o
# MISC Devices
obj-$(subst m,y,$(CONFIG_KEYBOARD_GPIO)) += platform_gpio_keys.o
+obj-$(subst m,y,$(CONFIG_RTC_DRV_CMOS)) += platform_mrfld_rtc.o
obj-$(subst m,y,$(CONFIG_INTEL_MID_WATCHDOG)) += platform_mrfld_wdt.o
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_gpio_keys.c b/arch/x86/platform/intel-mid/device_libs/platform_gpio_keys.c
index 52534ec..7428387 100644
--- a/arch/x86/platform/intel-mid/device_libs/platform_gpio_keys.c
+++ b/arch/x86/platform/intel-mid/device_libs/platform_gpio_keys.c
@@ -32,6 +32,9 @@
{SW_LID, -1, 1, "lid_switch", EV_SW, 0, 20},
{KEY_VOLUMEUP, -1, 1, "vol_up", EV_KEY, 0, 20},
{KEY_VOLUMEDOWN, -1, 1, "vol_down", EV_KEY, 0, 20},
+ {KEY_MUTE, -1, 1, "mute_enable", EV_KEY, 0, 20},
+ {KEY_VOLUMEUP, -1, 1, "volume_up", EV_KEY, 0, 20},
+ {KEY_VOLUMEDOWN, -1, 1, "volume_down", EV_KEY, 0, 20},
{KEY_CAMERA, -1, 1, "camera_full", EV_KEY, 0, 20},
{KEY_CAMERA_FOCUS, -1, 1, "camera_half", EV_KEY, 0, 20},
{SW_KEYPAD_SLIDE, -1, 1, "MagSw1", EV_SW, 0, 20},
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_ipc.c b/arch/x86/platform/intel-mid/device_libs/platform_ipc.c
deleted file mode 100644
index a84b73d..0000000
--- a/arch/x86/platform/intel-mid/device_libs/platform_ipc.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * platform_ipc.c: IPC platform library file
- *
- * (C) Copyright 2013 Intel Corporation
- * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.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; version 2
- * of the License.
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/sfi.h>
-#include <linux/gpio.h>
-#include <asm/intel-mid.h>
-#include "platform_ipc.h"
-
-void __init ipc_device_handler(struct sfi_device_table_entry *pentry,
- struct devs_id *dev)
-{
- struct platform_device *pdev;
- void *pdata = NULL;
- static struct resource res __initdata = {
- .name = "IRQ",
- .flags = IORESOURCE_IRQ,
- };
-
- pr_debug("IPC bus, name = %16.16s, irq = 0x%2x\n",
- pentry->name, pentry->irq);
-
- /*
- * We need to call platform init of IPC devices to fill misc_pdata
- * structure. It will be used in msic_init for initialization.
- */
- if (dev != NULL)
- pdata = dev->get_platform_data(pentry);
-
- /*
- * On Medfield the platform device creation is handled by the MSIC
- * MFD driver so we don't need to do it here.
- */
- if (intel_mid_has_msic())
- return;
-
- pdev = platform_device_alloc(pentry->name, 0);
- if (pdev == NULL) {
- pr_err("out of memory for SFI platform device '%s'.\n",
- pentry->name);
- return;
- }
- res.start = pentry->irq;
- platform_device_add_resources(pdev, &res, 1);
-
- pdev->dev.platform_data = pdata;
- intel_scu_device_register(pdev);
-}
-
-static const struct devs_id pmic_audio_dev_id __initconst = {
- .name = "pmic_audio",
- .type = SFI_DEV_TYPE_IPC,
- .delay = 1,
- .device_handler = &ipc_device_handler,
-};
-
-sfi_device(pmic_audio_dev_id);
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_ipc.h b/arch/x86/platform/intel-mid/device_libs/platform_ipc.h
deleted file mode 100644
index 79bb09d..0000000
--- a/arch/x86/platform/intel-mid/device_libs/platform_ipc.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * platform_ipc.h: IPC platform library header file
- *
- * (C) Copyright 2013 Intel Corporation
- * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.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; version 2
- * of the License.
- */
-#ifndef _PLATFORM_IPC_H_
-#define _PLATFORM_IPC_H_
-
-void __init
-ipc_device_handler(struct sfi_device_table_entry *pentry, struct devs_id *dev);
-
-#endif
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_mrfld_rtc.c b/arch/x86/platform/intel-mid/device_libs/platform_mrfld_rtc.c
new file mode 100644
index 0000000..3135416
--- /dev/null
+++ b/arch/x86/platform/intel-mid/device_libs/platform_mrfld_rtc.c
@@ -0,0 +1,48 @@
+/*
+ * Intel Merrifield legacy RTC initialization file
+ *
+ * (C) Copyright 2017 Intel Corporation
+ *
+ * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.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; version 2
+ * of the License.
+ */
+
+#include <linux/init.h>
+
+#include <asm/hw_irq.h>
+#include <asm/intel-mid.h>
+#include <asm/io_apic.h>
+#include <asm/time.h>
+#include <asm/x86_init.h>
+
+static int __init mrfld_legacy_rtc_alloc_irq(void)
+{
+ struct irq_alloc_info info;
+ int ret;
+
+ if (!x86_platform.legacy.rtc)
+ return -ENODEV;
+
+ ioapic_set_alloc_attr(&info, NUMA_NO_NODE, 1, 0);
+ ret = mp_map_gsi_to_irq(RTC_IRQ, IOAPIC_MAP_ALLOC, &info);
+ if (ret < 0) {
+ pr_info("Failed to allocate RTC interrupt. Disabling RTC\n");
+ x86_platform.legacy.rtc = 0;
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __init mrfld_legacy_rtc_init(void)
+{
+ if (intel_mid_identify_cpu() != INTEL_MID_CPU_CHIP_TANGIER)
+ return -ENODEV;
+
+ return mrfld_legacy_rtc_alloc_irq();
+}
+arch_initcall(mrfld_legacy_rtc_init);
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_mrfld_wdt.c b/arch/x86/platform/intel-mid/device_libs/platform_mrfld_wdt.c
index 3f1f1c7..86edd1e9 100644
--- a/arch/x86/platform/intel-mid/device_libs/platform_mrfld_wdt.c
+++ b/arch/x86/platform/intel-mid/device_libs/platform_mrfld_wdt.c
@@ -28,9 +28,9 @@
static int tangier_probe(struct platform_device *pdev)
{
- int gsi;
struct irq_alloc_info info;
struct intel_mid_wdt_pdata *pdata = pdev->dev.platform_data;
+ int gsi, irq;
if (!pdata)
return -EINVAL;
@@ -38,10 +38,10 @@
/* IOAPIC builds identity mapping between GSI and IRQ on MID */
gsi = pdata->irq;
ioapic_set_alloc_attr(&info, cpu_to_node(0), 1, 0);
- if (mp_map_gsi_to_irq(gsi, IOAPIC_MAP_ALLOC, &info) <= 0) {
- dev_warn(&pdev->dev, "cannot find interrupt %d in ioapic\n",
- gsi);
- return -EINVAL;
+ irq = mp_map_gsi_to_irq(gsi, IOAPIC_MAP_ALLOC, &info);
+ if (irq < 0) {
+ dev_warn(&pdev->dev, "cannot find interrupt %d in ioapic\n", gsi);
+ return irq;
}
return 0;
@@ -82,4 +82,4 @@
return 0;
}
-rootfs_initcall(register_mid_wdt);
+arch_initcall(register_mid_wdt);
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_audio.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_audio.c
index cb3490ec..d4dc744 100644
--- a/arch/x86/platform/intel-mid/device_libs/platform_msic_audio.c
+++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_audio.c
@@ -20,7 +20,6 @@
#include <asm/intel-mid.h>
#include "platform_msic.h"
-#include "platform_ipc.h"
static void *msic_audio_platform_data(void *info)
{
@@ -40,8 +39,8 @@
.name = "msic_audio",
.type = SFI_DEV_TYPE_IPC,
.delay = 1,
+ .msic = 1,
.get_platform_data = &msic_audio_platform_data,
- .device_handler = &ipc_device_handler,
};
sfi_device(msic_audio_dev_id);
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_battery.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_battery.c
index 4f72193..5c3e991 100644
--- a/arch/x86/platform/intel-mid/device_libs/platform_msic_battery.c
+++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_battery.c
@@ -19,7 +19,6 @@
#include <asm/intel-mid.h>
#include "platform_msic.h"
-#include "platform_ipc.h"
static void __init *msic_battery_platform_data(void *info)
{
@@ -30,8 +29,8 @@
.name = "msic_battery",
.type = SFI_DEV_TYPE_IPC,
.delay = 1,
+ .msic = 1,
.get_platform_data = &msic_battery_platform_data,
- .device_handler = &ipc_device_handler,
};
sfi_device(msic_battery_dev_id);
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_gpio.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_gpio.c
index 70de5b5..9fdb88d 100644
--- a/arch/x86/platform/intel-mid/device_libs/platform_msic_gpio.c
+++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_gpio.c
@@ -20,7 +20,6 @@
#include <asm/intel-mid.h>
#include "platform_msic.h"
-#include "platform_ipc.h"
static void __init *msic_gpio_platform_data(void *info)
{
@@ -41,8 +40,8 @@
.name = "msic_gpio",
.type = SFI_DEV_TYPE_IPC,
.delay = 1,
+ .msic = 1,
.get_platform_data = &msic_gpio_platform_data,
- .device_handler = &ipc_device_handler,
};
sfi_device(msic_gpio_dev_id);
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_ocd.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_ocd.c
index 3d7c201..7ae37cd 100644
--- a/arch/x86/platform/intel-mid/device_libs/platform_msic_ocd.c
+++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_ocd.c
@@ -20,7 +20,6 @@
#include <asm/intel-mid.h>
#include "platform_msic.h"
-#include "platform_ipc.h"
static void __init *msic_ocd_platform_data(void *info)
{
@@ -42,8 +41,8 @@
.name = "msic_ocd",
.type = SFI_DEV_TYPE_IPC,
.delay = 1,
+ .msic = 1,
.get_platform_data = &msic_ocd_platform_data,
- .device_handler = &ipc_device_handler,
};
sfi_device(msic_ocd_dev_id);
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_power_btn.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_power_btn.c
index 038f618..96809b9 100644
--- a/arch/x86/platform/intel-mid/device_libs/platform_msic_power_btn.c
+++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_power_btn.c
@@ -18,7 +18,6 @@
#include <asm/intel-mid.h>
#include "platform_msic.h"
-#include "platform_ipc.h"
static void __init *msic_power_btn_platform_data(void *info)
{
@@ -29,8 +28,8 @@
.name = "msic_power_btn",
.type = SFI_DEV_TYPE_IPC,
.delay = 1,
+ .msic = 1,
.get_platform_data = &msic_power_btn_platform_data,
- .device_handler = &ipc_device_handler,
};
sfi_device(msic_power_btn_dev_id);
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_thermal.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_thermal.c
index 114a575..3e4167d 100644
--- a/arch/x86/platform/intel-mid/device_libs/platform_msic_thermal.c
+++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_thermal.c
@@ -19,7 +19,6 @@
#include <asm/intel-mid.h>
#include "platform_msic.h"
-#include "platform_ipc.h"
static void __init *msic_thermal_platform_data(void *info)
{
@@ -30,8 +29,8 @@
.name = "msic_thermal",
.type = SFI_DEV_TYPE_IPC,
.delay = 1,
+ .msic = 1,
.get_platform_data = &msic_thermal_platform_data,
- .device_handler = &ipc_device_handler,
};
sfi_device(msic_thermal_dev_id);
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_pmic_gpio.c b/arch/x86/platform/intel-mid/device_libs/platform_pmic_gpio.c
deleted file mode 100644
index e30cb62..0000000
--- a/arch/x86/platform/intel-mid/device_libs/platform_pmic_gpio.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * platform_pmic_gpio.c: PMIC GPIO platform data initialization file
- *
- * (C) Copyright 2013 Intel Corporation
- * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.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; version 2
- * of the License.
- */
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/scatterlist.h>
-#include <linux/gpio.h>
-#include <linux/init.h>
-#include <linux/sfi.h>
-#include <linux/intel_pmic_gpio.h>
-#include <asm/intel-mid.h>
-
-#include "platform_ipc.h"
-
-static void __init *pmic_gpio_platform_data(void *info)
-{
- static struct intel_pmic_gpio_platform_data pmic_gpio_pdata;
- int gpio_base = get_gpio_by_name("pmic_gpio_base");
-
- if (gpio_base < 0)
- gpio_base = 64;
- pmic_gpio_pdata.gpio_base = gpio_base;
- pmic_gpio_pdata.irq_base = gpio_base + INTEL_MID_IRQ_OFFSET;
- pmic_gpio_pdata.gpiointr = 0xffffeff8;
-
- return &pmic_gpio_pdata;
-}
-
-static const struct devs_id pmic_gpio_spi_dev_id __initconst = {
- .name = "pmic_gpio",
- .type = SFI_DEV_TYPE_SPI,
- .delay = 1,
- .get_platform_data = &pmic_gpio_platform_data,
-};
-
-static const struct devs_id pmic_gpio_ipc_dev_id __initconst = {
- .name = "pmic_gpio",
- .type = SFI_DEV_TYPE_IPC,
- .delay = 1,
- .get_platform_data = &pmic_gpio_platform_data,
- .device_handler = &ipc_device_handler
-};
-
-sfi_device(pmic_gpio_spi_dev_id);
-sfi_device(pmic_gpio_ipc_dev_id);
diff --git a/arch/x86/platform/intel-mid/mrfld.c b/arch/x86/platform/intel-mid/mrfld.c
index e0607c7..ae7bdeb 100644
--- a/arch/x86/platform/intel-mid/mrfld.c
+++ b/arch/x86/platform/intel-mid/mrfld.c
@@ -91,6 +91,7 @@
static void __init tangier_arch_setup(void)
{
x86_platform.calibrate_tsc = tangier_calibrate_tsc;
+ x86_platform.legacy.rtc = 1;
}
/* tangier arch ops */
diff --git a/arch/x86/platform/intel-mid/sfi.c b/arch/x86/platform/intel-mid/sfi.c
index 051d264..19b43e3 100644
--- a/arch/x86/platform/intel-mid/sfi.c
+++ b/arch/x86/platform/intel-mid/sfi.c
@@ -15,7 +15,6 @@
#include <linux/interrupt.h>
#include <linux/scatterlist.h>
#include <linux/sfi.h>
-#include <linux/intel_pmic_gpio.h>
#include <linux/spi/spi.h>
#include <linux/i2c.h>
#include <linux/skbuff.h>
@@ -226,7 +225,7 @@
return -EINVAL;
}
-void __init intel_scu_device_register(struct platform_device *pdev)
+static void __init intel_scu_ipc_device_register(struct platform_device *pdev)
{
if (ipc_next_dev == MAX_IPCDEVS)
pr_err("too many SCU IPC devices");
@@ -335,10 +334,22 @@
pr_debug("IPC bus, name = %16.16s, irq = 0x%2x\n",
pentry->name, pentry->irq);
+
+ /*
+ * We need to call platform init of IPC devices to fill misc_pdata
+ * structure. It will be used in msic_init for initialization.
+ */
pdata = intel_mid_sfi_get_pdata(dev, pentry);
if (IS_ERR(pdata))
return;
+ /*
+ * On Medfield the platform device creation is handled by the MSIC
+ * MFD driver so we don't need to do it here.
+ */
+ if (dev->msic && intel_mid_has_msic())
+ return;
+
pdev = platform_device_alloc(pentry->name, 0);
if (pdev == NULL) {
pr_err("out of memory for SFI platform device '%s'.\n",
@@ -348,7 +359,10 @@
install_irq_resource(pdev, pentry->irq);
pdev->dev.platform_data = pdata;
- platform_device_add(pdev);
+ if (dev->delay)
+ intel_scu_ipc_device_register(pdev);
+ else
+ platform_device_add(pdev);
}
static void __init sfi_handle_spi_dev(struct sfi_device_table_entry *pentry,
@@ -503,27 +517,23 @@
if (!dev)
continue;
- if (dev->device_handler) {
- dev->device_handler(pentry, dev);
- } else {
- switch (pentry->type) {
- case SFI_DEV_TYPE_IPC:
- sfi_handle_ipc_dev(pentry, dev);
- break;
- case SFI_DEV_TYPE_SPI:
- sfi_handle_spi_dev(pentry, dev);
- break;
- case SFI_DEV_TYPE_I2C:
- sfi_handle_i2c_dev(pentry, dev);
- break;
- case SFI_DEV_TYPE_SD:
- sfi_handle_sd_dev(pentry, dev);
- break;
- case SFI_DEV_TYPE_UART:
- case SFI_DEV_TYPE_HSI:
- default:
- break;
- }
+ switch (pentry->type) {
+ case SFI_DEV_TYPE_IPC:
+ sfi_handle_ipc_dev(pentry, dev);
+ break;
+ case SFI_DEV_TYPE_SPI:
+ sfi_handle_spi_dev(pentry, dev);
+ break;
+ case SFI_DEV_TYPE_I2C:
+ sfi_handle_i2c_dev(pentry, dev);
+ break;
+ case SFI_DEV_TYPE_SD:
+ sfi_handle_sd_dev(pentry, dev);
+ break;
+ case SFI_DEV_TYPE_UART:
+ case SFI_DEV_TYPE_HSI:
+ default:
+ break;
}
}
return 0;
diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c
index 8410e7d..9743d0c 100644
--- a/arch/x86/platform/uv/uv_nmi.c
+++ b/arch/x86/platform/uv/uv_nmi.c
@@ -45,8 +45,8 @@
*
* Handle system-wide NMI events generated by the global 'power nmi' command.
*
- * Basic operation is to field the NMI interrupt on each cpu and wait
- * until all cpus have arrived into the nmi handler. If some cpus do not
+ * Basic operation is to field the NMI interrupt on each CPU and wait
+ * until all CPU's have arrived into the nmi handler. If some CPU's do not
* make it into the handler, try and force them in with the IPI(NMI) signal.
*
* We also have to lessen UV Hub MMR accesses as much as possible as this
@@ -56,7 +56,7 @@
* To do this we register our primary NMI notifier on the NMI_UNKNOWN
* chain. This reduces the number of false NMI calls when the perf
* tools are running which generate an enormous number of NMIs per
- * second (~4M/s for 1024 cpu threads). Our secondary NMI handler is
+ * second (~4M/s for 1024 CPU threads). Our secondary NMI handler is
* very short as it only checks that if it has been "pinged" with the
* IPI(NMI) signal as mentioned above, and does not read the UV Hub's MMR.
*
@@ -65,8 +65,20 @@
static struct uv_hub_nmi_s **uv_hub_nmi_list;
DEFINE_PER_CPU(struct uv_cpu_nmi_s, uv_cpu_nmi);
-EXPORT_PER_CPU_SYMBOL_GPL(uv_cpu_nmi);
+/* UV hubless values */
+#define NMI_CONTROL_PORT 0x70
+#define NMI_DUMMY_PORT 0x71
+#define PAD_OWN_GPP_D_0 0x2c
+#define GPI_NMI_STS_GPP_D_0 0x164
+#define GPI_NMI_ENA_GPP_D_0 0x174
+#define STS_GPP_D_0_MASK 0x1
+#define PAD_CFG_DW0_GPP_D_0 0x4c0
+#define GPIROUTNMI (1ul << 17)
+#define PCH_PCR_GPIO_1_BASE 0xfdae0000ul
+#define PCH_PCR_GPIO_ADDRESS(offset) (int *)((u64)(pch_base) | (u64)(offset))
+
+static u64 *pch_base;
static unsigned long nmi_mmr;
static unsigned long nmi_mmr_clear;
static unsigned long nmi_mmr_pending;
@@ -100,7 +112,7 @@
static int param_set_local64(const char *val, const struct kernel_param *kp)
{
- /* clear on any write */
+ /* Clear on any write */
local64_set((local64_t *)kp->arg, 0);
return 0;
}
@@ -144,16 +156,80 @@
static int uv_nmi_retry_count = 500;
module_param_named(retry_count, uv_nmi_retry_count, int, 0644);
-/*
- * Valid NMI Actions:
- * "dump" - dump process stack for each cpu
- * "ips" - dump IP info for each cpu
- * "kdump" - do crash dump
- * "kdb" - enter KDB (default)
- * "kgdb" - enter KGDB
- */
-static char uv_nmi_action[8] = "kdb";
-module_param_string(action, uv_nmi_action, sizeof(uv_nmi_action), 0644);
+static bool uv_pch_intr_enable = true;
+static bool uv_pch_intr_now_enabled;
+module_param_named(pch_intr_enable, uv_pch_intr_enable, bool, 0644);
+
+static bool uv_pch_init_enable = true;
+module_param_named(pch_init_enable, uv_pch_init_enable, bool, 0644);
+
+static int uv_nmi_debug;
+module_param_named(debug, uv_nmi_debug, int, 0644);
+
+#define nmi_debug(fmt, ...) \
+ do { \
+ if (uv_nmi_debug) \
+ pr_info(fmt, ##__VA_ARGS__); \
+ } while (0)
+
+/* Valid NMI Actions */
+#define ACTION_LEN 16
+static struct nmi_action {
+ char *action;
+ char *desc;
+} valid_acts[] = {
+ { "kdump", "do kernel crash dump" },
+ { "dump", "dump process stack for each cpu" },
+ { "ips", "dump Inst Ptr info for each cpu" },
+ { "kdb", "enter KDB (needs kgdboc= assignment)" },
+ { "kgdb", "enter KGDB (needs gdb target remote)" },
+ { "health", "check if CPUs respond to NMI" },
+};
+typedef char action_t[ACTION_LEN];
+static action_t uv_nmi_action = { "dump" };
+
+static int param_get_action(char *buffer, const struct kernel_param *kp)
+{
+ return sprintf(buffer, "%s\n", uv_nmi_action);
+}
+
+static int param_set_action(const char *val, const struct kernel_param *kp)
+{
+ int i;
+ int n = ARRAY_SIZE(valid_acts);
+ char arg[ACTION_LEN], *p;
+
+ /* (remove possible '\n') */
+ strncpy(arg, val, ACTION_LEN - 1);
+ arg[ACTION_LEN - 1] = '\0';
+ p = strchr(arg, '\n');
+ if (p)
+ *p = '\0';
+
+ for (i = 0; i < n; i++)
+ if (!strcmp(arg, valid_acts[i].action))
+ break;
+
+ if (i < n) {
+ strcpy(uv_nmi_action, arg);
+ pr_info("UV: New NMI action:%s\n", uv_nmi_action);
+ return 0;
+ }
+
+ pr_err("UV: Invalid NMI action:%s, valid actions are:\n", arg);
+ for (i = 0; i < n; i++)
+ pr_err("UV: %-8s - %s\n",
+ valid_acts[i].action, valid_acts[i].desc);
+ return -EINVAL;
+}
+
+static const struct kernel_param_ops param_ops_action = {
+ .get = param_get_action,
+ .set = param_set_action,
+};
+#define param_check_action(name, p) __param_check(name, p, action_t)
+
+module_param_named(action, uv_nmi_action, action, 0644);
static inline bool uv_nmi_action_is(const char *action)
{
@@ -192,8 +268,200 @@
}
/*
- * If first cpu in on this hub, set hub_nmi "in_nmi" and "owner" values and
- * return true. If first cpu in on the system, set global "in_nmi" flag.
+ * UV hubless NMI handler functions
+ */
+static inline void uv_reassert_nmi(void)
+{
+ /* (from arch/x86/include/asm/mach_traps.h) */
+ outb(0x8f, NMI_CONTROL_PORT);
+ inb(NMI_DUMMY_PORT); /* dummy read */
+ outb(0x0f, NMI_CONTROL_PORT);
+ inb(NMI_DUMMY_PORT); /* dummy read */
+}
+
+static void uv_init_hubless_pch_io(int offset, int mask, int data)
+{
+ int *addr = PCH_PCR_GPIO_ADDRESS(offset);
+ int readd = readl(addr);
+
+ if (mask) { /* OR in new data */
+ int writed = (readd & ~mask) | data;
+
+ nmi_debug("UV:PCH: %p = %x & %x | %x (%x)\n",
+ addr, readd, ~mask, data, writed);
+ writel(writed, addr);
+ } else if (readd & data) { /* clear status bit */
+ nmi_debug("UV:PCH: %p = %x\n", addr, data);
+ writel(data, addr);
+ }
+
+ (void)readl(addr); /* flush write data */
+}
+
+static void uv_nmi_setup_hubless_intr(void)
+{
+ uv_pch_intr_now_enabled = uv_pch_intr_enable;
+
+ uv_init_hubless_pch_io(
+ PAD_CFG_DW0_GPP_D_0, GPIROUTNMI,
+ uv_pch_intr_now_enabled ? GPIROUTNMI : 0);
+
+ nmi_debug("UV:NMI: GPP_D_0 interrupt %s\n",
+ uv_pch_intr_now_enabled ? "enabled" : "disabled");
+}
+
+static struct init_nmi {
+ unsigned int offset;
+ unsigned int mask;
+ unsigned int data;
+} init_nmi[] = {
+ { /* HOSTSW_OWN_GPP_D_0 */
+ .offset = 0x84,
+ .mask = 0x1,
+ .data = 0x0, /* ACPI Mode */
+ },
+
+/* Clear status: */
+ { /* GPI_INT_STS_GPP_D_0 */
+ .offset = 0x104,
+ .mask = 0x0,
+ .data = 0x1, /* Clear Status */
+ },
+ { /* GPI_GPE_STS_GPP_D_0 */
+ .offset = 0x124,
+ .mask = 0x0,
+ .data = 0x1, /* Clear Status */
+ },
+ { /* GPI_SMI_STS_GPP_D_0 */
+ .offset = 0x144,
+ .mask = 0x0,
+ .data = 0x1, /* Clear Status */
+ },
+ { /* GPI_NMI_STS_GPP_D_0 */
+ .offset = 0x164,
+ .mask = 0x0,
+ .data = 0x1, /* Clear Status */
+ },
+
+/* Disable interrupts: */
+ { /* GPI_INT_EN_GPP_D_0 */
+ .offset = 0x114,
+ .mask = 0x1,
+ .data = 0x0, /* Disable interrupt generation */
+ },
+ { /* GPI_GPE_EN_GPP_D_0 */
+ .offset = 0x134,
+ .mask = 0x1,
+ .data = 0x0, /* Disable interrupt generation */
+ },
+ { /* GPI_SMI_EN_GPP_D_0 */
+ .offset = 0x154,
+ .mask = 0x1,
+ .data = 0x0, /* Disable interrupt generation */
+ },
+ { /* GPI_NMI_EN_GPP_D_0 */
+ .offset = 0x174,
+ .mask = 0x1,
+ .data = 0x0, /* Disable interrupt generation */
+ },
+
+/* Setup GPP_D_0 Pad Config: */
+ { /* PAD_CFG_DW0_GPP_D_0 */
+ .offset = 0x4c0,
+ .mask = 0xffffffff,
+ .data = 0x82020100,
+/*
+ * 31:30 Pad Reset Config (PADRSTCFG): = 2h # PLTRST# (default)
+ *
+ * 29 RX Pad State Select (RXPADSTSEL): = 0 # Raw RX pad state directly
+ * from RX buffer (default)
+ *
+ * 28 RX Raw Override to '1' (RXRAW1): = 0 # No Override
+ *
+ * 26:25 RX Level/Edge Configuration (RXEVCFG):
+ * = 0h # Level
+ * = 1h # Edge
+ *
+ * 23 RX Invert (RXINV): = 0 # No Inversion (signal active high)
+ *
+ * 20 GPIO Input Route IOxAPIC (GPIROUTIOXAPIC):
+ * = 0 # Routing does not cause peripheral IRQ...
+ * # (we want an NMI not an IRQ)
+ *
+ * 19 GPIO Input Route SCI (GPIROUTSCI): = 0 # Routing does not cause SCI.
+ * 18 GPIO Input Route SMI (GPIROUTSMI): = 0 # Routing does not cause SMI.
+ * 17 GPIO Input Route NMI (GPIROUTNMI): = 1 # Routing can cause NMI.
+ *
+ * 11:10 Pad Mode (PMODE1/0): = 0h = GPIO control the Pad.
+ * 9 GPIO RX Disable (GPIORXDIS):
+ * = 0 # Enable the input buffer (active low enable)
+ *
+ * 8 GPIO TX Disable (GPIOTXDIS):
+ * = 1 # Disable the output buffer; i.e. Hi-Z
+ *
+ * 1 GPIO RX State (GPIORXSTATE): This is the current internal RX pad state..
+ * 0 GPIO TX State (GPIOTXSTATE):
+ * = 0 # (Leave at default)
+ */
+ },
+
+/* Pad Config DW1 */
+ { /* PAD_CFG_DW1_GPP_D_0 */
+ .offset = 0x4c4,
+ .mask = 0x3c00,
+ .data = 0, /* Termination = none (default) */
+ },
+};
+
+static void uv_init_hubless_pch_d0(void)
+{
+ int i, read;
+
+ read = *PCH_PCR_GPIO_ADDRESS(PAD_OWN_GPP_D_0);
+ if (read != 0) {
+ pr_info("UV: Hubless NMI already configured\n");
+ return;
+ }
+
+ nmi_debug("UV: Initializing UV Hubless NMI on PCH\n");
+ for (i = 0; i < ARRAY_SIZE(init_nmi); i++) {
+ uv_init_hubless_pch_io(init_nmi[i].offset,
+ init_nmi[i].mask,
+ init_nmi[i].data);
+ }
+}
+
+static int uv_nmi_test_hubless(struct uv_hub_nmi_s *hub_nmi)
+{
+ int *pstat = PCH_PCR_GPIO_ADDRESS(GPI_NMI_STS_GPP_D_0);
+ int status = *pstat;
+
+ hub_nmi->nmi_value = status;
+ atomic_inc(&hub_nmi->read_mmr_count);
+
+ if (!(status & STS_GPP_D_0_MASK)) /* Not a UV external NMI */
+ return 0;
+
+ *pstat = STS_GPP_D_0_MASK; /* Is a UV NMI: clear GPP_D_0 status */
+ (void)*pstat; /* Flush write */
+
+ return 1;
+}
+
+static int uv_test_nmi(struct uv_hub_nmi_s *hub_nmi)
+{
+ if (hub_nmi->hub_present)
+ return uv_nmi_test_mmr(hub_nmi);
+
+ if (hub_nmi->pch_owner) /* Only PCH owner can check status */
+ return uv_nmi_test_hubless(hub_nmi);
+
+ return -1;
+}
+
+/*
+ * If first CPU in on this hub, set hub_nmi "in_nmi" and "owner" values and
+ * return true. If first CPU in on the system, set global "in_nmi" flag.
*/
static int uv_set_in_nmi(int cpu, struct uv_hub_nmi_s *hub_nmi)
{
@@ -214,6 +482,7 @@
{
int cpu = smp_processor_id();
int nmi = 0;
+ int nmi_detected = 0;
local64_inc(&uv_nmi_count);
this_cpu_inc(uv_cpu_nmi.queries);
@@ -224,35 +493,48 @@
break;
if (raw_spin_trylock(&hub_nmi->nmi_lock)) {
+ nmi_detected = uv_test_nmi(hub_nmi);
- /* check hub MMR NMI flag */
- if (uv_nmi_test_mmr(hub_nmi)) {
+ /* Check flag for UV external NMI */
+ if (nmi_detected > 0) {
uv_set_in_nmi(cpu, hub_nmi);
nmi = 1;
break;
}
- /* MMR NMI flag is clear */
+ /* A non-PCH node in a hubless system waits for NMI */
+ else if (nmi_detected < 0)
+ goto slave_wait;
+
+ /* MMR/PCH NMI flag is clear */
raw_spin_unlock(&hub_nmi->nmi_lock);
} else {
- /* wait a moment for the hub nmi locker to set flag */
- cpu_relax();
+
+ /* Wait a moment for the HUB NMI locker to set flag */
+slave_wait: cpu_relax();
udelay(uv_nmi_slave_delay);
- /* re-check hub in_nmi flag */
+ /* Re-check hub in_nmi flag */
nmi = atomic_read(&hub_nmi->in_nmi);
if (nmi)
break;
}
- /* check if this BMC missed setting the MMR NMI flag */
+ /*
+ * Check if this BMC missed setting the MMR NMI flag (or)
+ * UV hubless system where only PCH owner can check flag
+ */
if (!nmi) {
nmi = atomic_read(&uv_in_nmi);
if (nmi)
uv_set_in_nmi(cpu, hub_nmi);
}
+ /* If we're holding the hub lock, release it now */
+ if (nmi_detected < 0)
+ raw_spin_unlock(&hub_nmi->nmi_lock);
+
} while (0);
if (!nmi)
@@ -269,12 +551,15 @@
if (cpu == atomic_read(&hub_nmi->cpu_owner)) {
atomic_set(&hub_nmi->cpu_owner, -1);
atomic_set(&hub_nmi->in_nmi, 0);
- uv_local_mmr_clear_nmi();
+ if (hub_nmi->hub_present)
+ uv_local_mmr_clear_nmi();
+ else
+ uv_reassert_nmi();
raw_spin_unlock(&hub_nmi->nmi_lock);
}
}
-/* Ping non-responding cpus attemping to force them into the NMI handler */
+/* Ping non-responding CPU's attemping to force them into the NMI handler */
static void uv_nmi_nr_cpus_ping(void)
{
int cpu;
@@ -285,7 +570,7 @@
apic->send_IPI_mask(uv_nmi_cpu_mask, APIC_DM_NMI);
}
-/* Clean up flags for cpus that ignored both NMI and ping */
+/* Clean up flags for CPU's that ignored both NMI and ping */
static void uv_nmi_cleanup_mask(void)
{
int cpu;
@@ -297,11 +582,12 @@
}
}
-/* Loop waiting as cpus enter nmi handler */
+/* Loop waiting as CPU's enter NMI handler */
static int uv_nmi_wait_cpus(int first)
{
int i, j, k, n = num_online_cpus();
int last_k = 0, waiting = 0;
+ int cpu = smp_processor_id();
if (first) {
cpumask_copy(uv_nmi_cpu_mask, cpu_online_mask);
@@ -310,6 +596,12 @@
k = n - cpumask_weight(uv_nmi_cpu_mask);
}
+ /* PCH NMI causes only one CPU to respond */
+ if (first && uv_pch_intr_now_enabled) {
+ cpumask_clear_cpu(cpu, uv_nmi_cpu_mask);
+ return n - k - 1;
+ }
+
udelay(uv_nmi_initial_delay);
for (i = 0; i < uv_nmi_retry_count; i++) {
int loop_delay = uv_nmi_loop_delay;
@@ -325,13 +617,13 @@
k = n;
break;
}
- if (last_k != k) { /* abort if no new cpus coming in */
+ if (last_k != k) { /* abort if no new CPU's coming in */
last_k = k;
waiting = 0;
} else if (++waiting > uv_nmi_wait_count)
break;
- /* extend delay if waiting only for cpu 0 */
+ /* Extend delay if waiting only for CPU 0: */
if (waiting && (n - k) == 1 &&
cpumask_test_cpu(0, uv_nmi_cpu_mask))
loop_delay *= 100;
@@ -342,29 +634,29 @@
return n - k;
}
-/* Wait until all slave cpus have entered UV NMI handler */
+/* Wait until all slave CPU's have entered UV NMI handler */
static void uv_nmi_wait(int master)
{
- /* indicate this cpu is in */
+ /* Indicate this CPU is in: */
this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_IN);
- /* if not the first cpu in (the master), then we are a slave cpu */
+ /* If not the first CPU in (the master), then we are a slave CPU */
if (!master)
return;
do {
- /* wait for all other cpus to gather here */
+ /* Wait for all other CPU's to gather here */
if (!uv_nmi_wait_cpus(1))
break;
- /* if not all made it in, send IPI NMI to them */
- pr_alert("UV: Sending NMI IPI to %d non-responding CPUs: %*pbl\n",
+ /* If not all made it in, send IPI NMI to them */
+ pr_alert("UV: Sending NMI IPI to %d CPUs: %*pbl\n",
cpumask_weight(uv_nmi_cpu_mask),
cpumask_pr_args(uv_nmi_cpu_mask));
uv_nmi_nr_cpus_ping();
- /* if all cpus are in, then done */
+ /* If all CPU's are in, then done */
if (!uv_nmi_wait_cpus(0))
break;
@@ -416,7 +708,7 @@
this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_DUMP_DONE);
}
-/* Trigger a slave cpu to dump it's state */
+/* Trigger a slave CPU to dump it's state */
static void uv_nmi_trigger_dump(int cpu)
{
int retry = uv_nmi_trigger_delay;
@@ -437,7 +729,7 @@
uv_cpu_nmi_per(cpu).state = UV_NMI_STATE_DUMP_DONE;
}
-/* Wait until all cpus ready to exit */
+/* Wait until all CPU's ready to exit */
static void uv_nmi_sync_exit(int master)
{
atomic_dec(&uv_nmi_cpus_in_nmi);
@@ -451,7 +743,23 @@
}
}
-/* Walk through cpu list and dump state of each */
+/* Current "health" check is to check which CPU's are responsive */
+static void uv_nmi_action_health(int cpu, struct pt_regs *regs, int master)
+{
+ if (master) {
+ int in = atomic_read(&uv_nmi_cpus_in_nmi);
+ int out = num_online_cpus() - in;
+
+ pr_alert("UV: NMI CPU health check (non-responding:%d)\n", out);
+ atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT);
+ } else {
+ while (!atomic_read(&uv_nmi_slave_continue))
+ cpu_relax();
+ }
+ uv_nmi_sync_exit(master);
+}
+
+/* Walk through CPU list and dump state of each */
static void uv_nmi_dump_state(int cpu, struct pt_regs *regs, int master)
{
if (master) {
@@ -538,7 +846,7 @@
#else /* !CONFIG_KGDB_KDB */
static inline int uv_nmi_kdb_reason(void)
{
- /* Insure user is expecting to attach gdb remote */
+ /* Ensure user is expecting to attach gdb remote */
if (uv_nmi_action_is("kgdb"))
return 0;
@@ -563,7 +871,7 @@
if (reason < 0)
return;
- /* call KGDB NMI handler as MASTER */
+ /* Call KGDB NMI handler as MASTER */
ret = kgdb_nmicallin(cpu, X86_TRAP_NMI, regs, reason,
&uv_nmi_slave_continue);
if (ret) {
@@ -571,7 +879,7 @@
atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT);
}
} else {
- /* wait for KGDB signal that it's ready for slaves to enter */
+ /* Wait for KGDB signal that it's ready for slaves to enter */
int sig;
do {
@@ -579,7 +887,7 @@
sig = atomic_read(&uv_nmi_slave_continue);
} while (!sig);
- /* call KGDB as slave */
+ /* Call KGDB as slave */
if (sig == SLAVE_CONTINUE)
kgdb_nmicallback(cpu, regs);
}
@@ -623,18 +931,23 @@
strncpy(uv_nmi_action, "dump", strlen(uv_nmi_action));
}
- /* Pause as all cpus enter the NMI handler */
+ /* Pause as all CPU's enter the NMI handler */
uv_nmi_wait(master);
- /* Dump state of each cpu */
- if (uv_nmi_action_is("ips") || uv_nmi_action_is("dump"))
+ /* Process actions other than "kdump": */
+ if (uv_nmi_action_is("health")) {
+ uv_nmi_action_health(cpu, regs, master);
+ } else if (uv_nmi_action_is("ips") || uv_nmi_action_is("dump")) {
uv_nmi_dump_state(cpu, regs, master);
-
- /* Call KGDB/KDB if enabled */
- else if (uv_nmi_action_is("kdb") || uv_nmi_action_is("kgdb"))
+ } else if (uv_nmi_action_is("kdb") || uv_nmi_action_is("kgdb")) {
uv_call_kgdb_kdb(cpu, regs, master);
+ } else {
+ if (master)
+ pr_alert("UV: unknown NMI action: %s\n", uv_nmi_action);
+ uv_nmi_sync_exit(master);
+ }
- /* Clear per_cpu "in nmi" flag */
+ /* Clear per_cpu "in_nmi" flag */
this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_OUT);
/* Clear MMR NMI flag on each hub */
@@ -648,6 +961,7 @@
atomic_set(&uv_nmi_cpu, -1);
atomic_set(&uv_in_nmi, 0);
atomic_set(&uv_nmi_kexec_failed, 0);
+ atomic_set(&uv_nmi_slave_continue, SLAVE_CLEAR);
}
uv_nmi_touch_watchdogs();
@@ -657,7 +971,7 @@
}
/*
- * NMI handler for pulling in CPUs when perf events are grabbing our NMI
+ * NMI handler for pulling in CPU's when perf events are grabbing our NMI
*/
static int uv_handle_nmi_ping(unsigned int reason, struct pt_regs *regs)
{
@@ -690,35 +1004,62 @@
unsigned int value;
/*
- * Unmask NMI on all cpus
+ * Unmask NMI on all CPU's
*/
value = apic_read(APIC_LVT1) | APIC_DM_NMI;
value &= ~APIC_LVT_MASKED;
apic_write(APIC_LVT1, value);
}
-void uv_nmi_setup(void)
+/* Setup HUB NMI info */
+void __init uv_nmi_setup_common(bool hubbed)
{
int size = sizeof(void *) * (1 << NODES_SHIFT);
- int cpu, nid;
+ int cpu;
- /* Setup hub nmi info */
- uv_nmi_setup_mmrs();
uv_hub_nmi_list = kzalloc(size, GFP_KERNEL);
- pr_info("UV: NMI hub list @ 0x%p (%d)\n", uv_hub_nmi_list, size);
+ nmi_debug("UV: NMI hub list @ 0x%p (%d)\n", uv_hub_nmi_list, size);
BUG_ON(!uv_hub_nmi_list);
size = sizeof(struct uv_hub_nmi_s);
for_each_present_cpu(cpu) {
- nid = cpu_to_node(cpu);
+ int nid = cpu_to_node(cpu);
if (uv_hub_nmi_list[nid] == NULL) {
uv_hub_nmi_list[nid] = kzalloc_node(size,
GFP_KERNEL, nid);
BUG_ON(!uv_hub_nmi_list[nid]);
raw_spin_lock_init(&(uv_hub_nmi_list[nid]->nmi_lock));
atomic_set(&uv_hub_nmi_list[nid]->cpu_owner, -1);
+ uv_hub_nmi_list[nid]->hub_present = hubbed;
+ uv_hub_nmi_list[nid]->pch_owner = (nid == 0);
}
uv_hub_nmi_per(cpu) = uv_hub_nmi_list[nid];
}
BUG_ON(!alloc_cpumask_var(&uv_nmi_cpu_mask, GFP_KERNEL));
+}
+
+/* Setup for UV Hub systems */
+void __init uv_nmi_setup(void)
+{
+ uv_nmi_setup_mmrs();
+ uv_nmi_setup_common(true);
uv_register_nmi_notifier();
+ pr_info("UV: Hub NMI enabled\n");
+}
+
+/* Setup for UV Hubless systems */
+void __init uv_nmi_setup_hubless(void)
+{
+ uv_nmi_setup_common(false);
+ pch_base = xlate_dev_mem_ptr(PCH_PCR_GPIO_1_BASE);
+ nmi_debug("UV: PCH base:%p from 0x%lx, GPP_D_0\n",
+ pch_base, PCH_PCR_GPIO_1_BASE);
+ if (uv_pch_init_enable)
+ uv_init_hubless_pch_d0();
+ uv_init_hubless_pch_io(GPI_NMI_ENA_GPP_D_0,
+ STS_GPP_D_0_MASK, STS_GPP_D_0_MASK);
+ uv_nmi_setup_hubless_intr();
+ /* Ensure NMI enabled in Processor Interface Reg: */
+ uv_reassert_nmi();
+ uv_register_nmi_notifier();
+ pr_info("UV: Hubless NMI enabled\n");
}
diff --git a/arch/x86/ras/Kconfig b/arch/x86/ras/Kconfig
index d957d5f..0bc60a3 100644
--- a/arch/x86/ras/Kconfig
+++ b/arch/x86/ras/Kconfig
@@ -1,6 +1,6 @@
config MCE_AMD_INJ
tristate "Simple MCE injection interface for AMD processors"
- depends on RAS && EDAC_DECODE_MCE && DEBUG_FS && AMD_NB
+ depends on RAS && X86_MCE && DEBUG_FS && AMD_NB
default n
help
This is a simple debugfs interface to inject MCEs and test different
diff --git a/arch/x86/xen/spinlock.c b/arch/x86/xen/spinlock.c
index e8a9ea7..25a7c43 100644
--- a/arch/x86/xen/spinlock.c
+++ b/arch/x86/xen/spinlock.c
@@ -141,25 +141,6 @@
pv_lock_ops.vcpu_is_preempted = PV_CALLEE_SAVE(xen_vcpu_stolen);
}
-/*
- * While the jump_label init code needs to happend _after_ the jump labels are
- * enabled and before SMP is started. Hence we use pre-SMP initcall level
- * init. We cannot do it in xen_init_spinlocks as that is done before
- * jump labels are activated.
- */
-static __init int xen_init_spinlocks_jump(void)
-{
- if (!xen_pvspin)
- return 0;
-
- if (!xen_domain())
- return 0;
-
- static_key_slow_inc(¶virt_ticketlocks_enabled);
- return 0;
-}
-early_initcall(xen_init_spinlocks_jump);
-
static __init int xen_parse_nopvspin(char *arg)
{
xen_pvspin = false;
diff --git a/arch/xtensa/include/asm/Kbuild b/arch/xtensa/include/asm/Kbuild
index b7fbaa5..9e9760b 100644
--- a/arch/xtensa/include/asm/Kbuild
+++ b/arch/xtensa/include/asm/Kbuild
@@ -1,7 +1,6 @@
generic-y += bitsperlong.h
generic-y += bug.h
generic-y += clkdev.h
-generic-y += cputime.h
generic-y += div64.h
generic-y += dma-contiguous.h
generic-y += emergency-restart.h
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index c73a6fc..838f07e 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -3758,7 +3758,7 @@
}
#ifdef CONFIG_CFQ_GROUP_IOSCHED
-static void check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio)
+static bool check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio)
{
struct cfq_data *cfqd = cic_to_cfqd(cic);
struct cfq_queue *cfqq;
@@ -3775,15 +3775,7 @@
* spuriously on a newly created cic but there's no harm.
*/
if (unlikely(!cfqd) || likely(cic->blkcg_serial_nr == serial_nr))
- return;
-
- /*
- * If we have a non-root cgroup, we can depend on that to
- * do proper throttling of writes. Turn off wbt for that
- * case, if it was enabled by default.
- */
- if (nonroot_cg)
- wbt_disable_default(cfqd->queue);
+ return nonroot_cg;
/*
* Drop reference to queues. New queues will be assigned in new
@@ -3804,9 +3796,13 @@
}
cic->blkcg_serial_nr = serial_nr;
+ return nonroot_cg;
}
#else
-static inline void check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio) { }
+static inline bool check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio)
+{
+ return false;
+}
#endif /* CONFIG_CFQ_GROUP_IOSCHED */
static struct cfq_queue **
@@ -4448,11 +4444,12 @@
const int rw = rq_data_dir(rq);
const bool is_sync = rq_is_sync(rq);
struct cfq_queue *cfqq;
+ bool disable_wbt;
spin_lock_irq(q->queue_lock);
check_ioprio_changed(cic, bio);
- check_blkcg_changed(cic, bio);
+ disable_wbt = check_blkcg_changed(cic, bio);
new_queue:
cfqq = cic_to_cfqq(cic, is_sync);
if (!cfqq || cfqq == &cfqd->oom_cfqq) {
@@ -4488,6 +4485,10 @@
rq->elv.priv[0] = cfqq;
rq->elv.priv[1] = cfqq->cfqg;
spin_unlock_irq(q->queue_lock);
+
+ if (disable_wbt)
+ wbt_disable_default(q);
+
return 0;
}
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 9ed0878..a391bbc 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -55,7 +55,7 @@
acpi-$(CONFIG_ACPI_NUMA) += numa.o
acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
acpi-y += acpi_lpat.o
-acpi-$(CONFIG_ACPI_GENERIC_GSI) += gsi.o
+acpi-$(CONFIG_ACPI_GENERIC_GSI) += irq.o
acpi-$(CONFIG_ACPI_WATCHDOG) += acpi_watchdog.o
# These are (potentially) separate modules
diff --git a/drivers/acpi/acpi_extlog.c b/drivers/acpi/acpi_extlog.c
index b3842ff..a15270a 100644
--- a/drivers/acpi/acpi_extlog.c
+++ b/drivers/acpi/acpi_extlog.c
@@ -212,6 +212,7 @@
}
static struct notifier_block extlog_mce_dec = {
.notifier_call = extlog_print,
+ .priority = MCE_PRIO_EXTLOG,
};
static int __init extlog_init(void)
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index e0d2e6e..3752521 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -536,7 +536,7 @@
if (!iort_fwnode)
return NULL;
- ops = iommu_get_instance(iort_fwnode);
+ ops = iommu_ops_from_fwnode(iort_fwnode);
if (!ops)
return NULL;
diff --git a/drivers/acpi/bgrt.c b/drivers/acpi/bgrt.c
index 75f128e..ca28aa57 100644
--- a/drivers/acpi/bgrt.c
+++ b/drivers/acpi/bgrt.c
@@ -15,40 +15,41 @@
#include <linux/sysfs.h>
#include <linux/efi-bgrt.h>
+static void *bgrt_image;
static struct kobject *bgrt_kobj;
static ssize_t show_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->version);
+ return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab.version);
}
static DEVICE_ATTR(version, S_IRUGO, show_version, NULL);
static ssize_t show_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->status);
+ return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab.status);
}
static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
static ssize_t show_type(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->image_type);
+ return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab.image_type);
}
static DEVICE_ATTR(type, S_IRUGO, show_type, NULL);
static ssize_t show_xoffset(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->image_offset_x);
+ return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab.image_offset_x);
}
static DEVICE_ATTR(xoffset, S_IRUGO, show_xoffset, NULL);
static ssize_t show_yoffset(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->image_offset_y);
+ return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab.image_offset_y);
}
static DEVICE_ATTR(yoffset, S_IRUGO, show_yoffset, NULL);
@@ -84,15 +85,24 @@
{
int ret;
- if (!bgrt_image)
+ if (!bgrt_tab.image_address)
return -ENODEV;
+ bgrt_image = memremap(bgrt_tab.image_address, bgrt_image_size,
+ MEMREMAP_WB);
+ if (!bgrt_image) {
+ pr_notice("Ignoring BGRT: failed to map image memory\n");
+ return -ENOMEM;
+ }
+
bin_attr_image.private = bgrt_image;
bin_attr_image.size = bgrt_image_size;
bgrt_kobj = kobject_create_and_add("bgrt", acpi_kobj);
- if (!bgrt_kobj)
- return -EINVAL;
+ if (!bgrt_kobj) {
+ ret = -EINVAL;
+ goto out_memmap;
+ }
ret = sysfs_create_group(bgrt_kobj, &bgrt_attribute_group);
if (ret)
@@ -102,6 +112,8 @@
out_kobject:
kobject_put(bgrt_kobj);
+out_memmap:
+ memunmap(bgrt_image);
return ret;
}
device_initcall(bgrt_init);
diff --git a/drivers/acpi/gsi.c b/drivers/acpi/gsi.c
deleted file mode 100644
index ee9e0f2..0000000
--- a/drivers/acpi/gsi.c
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * ACPI GSI IRQ layer
- *
- * Copyright (C) 2015 ARM Ltd.
- * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.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/acpi.h>
-#include <linux/irq.h>
-#include <linux/irqdomain.h>
-#include <linux/of.h>
-
-enum acpi_irq_model_id acpi_irq_model;
-
-static struct fwnode_handle *acpi_gsi_domain_id;
-
-/**
- * acpi_gsi_to_irq() - Retrieve the linux irq number for a given GSI
- * @gsi: GSI IRQ number to map
- * @irq: pointer where linux IRQ number is stored
- *
- * irq location updated with irq value [>0 on success, 0 on failure]
- *
- * Returns: linux IRQ number on success (>0)
- * -EINVAL on failure
- */
-int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
-{
- struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
- DOMAIN_BUS_ANY);
-
- *irq = irq_find_mapping(d, gsi);
- /*
- * *irq == 0 means no mapping, that should
- * be reported as a failure
- */
- return (*irq > 0) ? *irq : -EINVAL;
-}
-EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
-
-/**
- * acpi_register_gsi() - Map a GSI to a linux IRQ number
- * @dev: device for which IRQ has to be mapped
- * @gsi: GSI IRQ number
- * @trigger: trigger type of the GSI number to be mapped
- * @polarity: polarity of the GSI to be mapped
- *
- * Returns: a valid linux IRQ number on success
- * -EINVAL on failure
- */
-int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
- int polarity)
-{
- struct irq_fwspec fwspec;
-
- if (WARN_ON(!acpi_gsi_domain_id)) {
- pr_warn("GSI: No registered irqchip, giving up\n");
- return -EINVAL;
- }
-
- fwspec.fwnode = acpi_gsi_domain_id;
- fwspec.param[0] = gsi;
- fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
- fwspec.param_count = 2;
-
- return irq_create_fwspec_mapping(&fwspec);
-}
-EXPORT_SYMBOL_GPL(acpi_register_gsi);
-
-/**
- * acpi_unregister_gsi() - Free a GSI<->linux IRQ number mapping
- * @gsi: GSI IRQ number
- */
-void acpi_unregister_gsi(u32 gsi)
-{
- struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
- DOMAIN_BUS_ANY);
- int irq = irq_find_mapping(d, gsi);
-
- irq_dispose_mapping(irq);
-}
-EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
-
-/**
- * acpi_set_irq_model - Setup the GSI irqdomain information
- * @model: the value assigned to acpi_irq_model
- * @fwnode: the irq_domain identifier for mapping and looking up
- * GSI interrupts
- */
-void __init acpi_set_irq_model(enum acpi_irq_model_id model,
- struct fwnode_handle *fwnode)
-{
- acpi_irq_model = model;
- acpi_gsi_domain_id = fwnode;
-}
diff --git a/drivers/acpi/irq.c b/drivers/acpi/irq.c
new file mode 100644
index 0000000..830299a
--- /dev/null
+++ b/drivers/acpi/irq.c
@@ -0,0 +1,297 @@
+/*
+ * ACPI GSI IRQ layer
+ *
+ * Copyright (C) 2015 ARM Ltd.
+ * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.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/acpi.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+
+enum acpi_irq_model_id acpi_irq_model;
+
+static struct fwnode_handle *acpi_gsi_domain_id;
+
+/**
+ * acpi_gsi_to_irq() - Retrieve the linux irq number for a given GSI
+ * @gsi: GSI IRQ number to map
+ * @irq: pointer where linux IRQ number is stored
+ *
+ * irq location updated with irq value [>0 on success, 0 on failure]
+ *
+ * Returns: linux IRQ number on success (>0)
+ * -EINVAL on failure
+ */
+int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
+{
+ struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
+ DOMAIN_BUS_ANY);
+
+ *irq = irq_find_mapping(d, gsi);
+ /*
+ * *irq == 0 means no mapping, that should
+ * be reported as a failure
+ */
+ return (*irq > 0) ? *irq : -EINVAL;
+}
+EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
+
+/**
+ * acpi_register_gsi() - Map a GSI to a linux IRQ number
+ * @dev: device for which IRQ has to be mapped
+ * @gsi: GSI IRQ number
+ * @trigger: trigger type of the GSI number to be mapped
+ * @polarity: polarity of the GSI to be mapped
+ *
+ * Returns: a valid linux IRQ number on success
+ * -EINVAL on failure
+ */
+int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
+ int polarity)
+{
+ struct irq_fwspec fwspec;
+
+ if (WARN_ON(!acpi_gsi_domain_id)) {
+ pr_warn("GSI: No registered irqchip, giving up\n");
+ return -EINVAL;
+ }
+
+ fwspec.fwnode = acpi_gsi_domain_id;
+ fwspec.param[0] = gsi;
+ fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
+ fwspec.param_count = 2;
+
+ return irq_create_fwspec_mapping(&fwspec);
+}
+EXPORT_SYMBOL_GPL(acpi_register_gsi);
+
+/**
+ * acpi_unregister_gsi() - Free a GSI<->linux IRQ number mapping
+ * @gsi: GSI IRQ number
+ */
+void acpi_unregister_gsi(u32 gsi)
+{
+ struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
+ DOMAIN_BUS_ANY);
+ int irq = irq_find_mapping(d, gsi);
+
+ irq_dispose_mapping(irq);
+}
+EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
+
+/**
+ * acpi_get_irq_source_fwhandle() - Retrieve fwhandle from IRQ resource source.
+ * @source: acpi_resource_source to use for the lookup.
+ *
+ * Description:
+ * Retrieve the fwhandle of the device referenced by the given IRQ resource
+ * source.
+ *
+ * Return:
+ * The referenced device fwhandle or NULL on failure
+ */
+static struct fwnode_handle *
+acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source)
+{
+ struct fwnode_handle *result;
+ struct acpi_device *device;
+ acpi_handle handle;
+ acpi_status status;
+
+ if (!source->string_length)
+ return acpi_gsi_domain_id;
+
+ status = acpi_get_handle(NULL, source->string_ptr, &handle);
+ if (WARN_ON(ACPI_FAILURE(status)))
+ return NULL;
+
+ device = acpi_bus_get_acpi_device(handle);
+ if (WARN_ON(!device))
+ return NULL;
+
+ result = &device->fwnode;
+ acpi_bus_put_acpi_device(device);
+ return result;
+}
+
+/*
+ * Context for the resource walk used to lookup IRQ resources.
+ * Contains a return code, the lookup index, and references to the flags
+ * and fwspec where the result is returned.
+ */
+struct acpi_irq_parse_one_ctx {
+ int rc;
+ unsigned int index;
+ unsigned long *res_flags;
+ struct irq_fwspec *fwspec;
+};
+
+/**
+ * acpi_irq_parse_one_match - Handle a matching IRQ resource.
+ * @fwnode: matching fwnode
+ * @hwirq: hardware IRQ number
+ * @triggering: triggering attributes of hwirq
+ * @polarity: polarity attributes of hwirq
+ * @polarity: polarity attributes of hwirq
+ * @shareable: shareable attributes of hwirq
+ * @ctx: acpi_irq_parse_one_ctx updated by this function
+ *
+ * Description:
+ * Handle a matching IRQ resource by populating the given ctx with
+ * the information passed.
+ */
+static inline void acpi_irq_parse_one_match(struct fwnode_handle *fwnode,
+ u32 hwirq, u8 triggering,
+ u8 polarity, u8 shareable,
+ struct acpi_irq_parse_one_ctx *ctx)
+{
+ if (!fwnode)
+ return;
+ ctx->rc = 0;
+ *ctx->res_flags = acpi_dev_irq_flags(triggering, polarity, shareable);
+ ctx->fwspec->fwnode = fwnode;
+ ctx->fwspec->param[0] = hwirq;
+ ctx->fwspec->param[1] = acpi_dev_get_irq_type(triggering, polarity);
+ ctx->fwspec->param_count = 2;
+}
+
+/**
+ * acpi_irq_parse_one_cb - Handle the given resource.
+ * @ares: resource to handle
+ * @context: context for the walk
+ *
+ * Description:
+ * This is called by acpi_walk_resources passing each resource returned by
+ * the _CRS method. We only inspect IRQ resources. Since IRQ resources
+ * might contain multiple interrupts we check if the index is within this
+ * one's interrupt array, otherwise we subtract the current resource IRQ
+ * count from the lookup index to prepare for the next resource.
+ * Once a match is found we call acpi_irq_parse_one_match to populate
+ * the result and end the walk by returning AE_CTRL_TERMINATE.
+ *
+ * Return:
+ * AE_OK if the walk should continue, AE_CTRL_TERMINATE if a matching
+ * IRQ resource was found.
+ */
+static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
+ void *context)
+{
+ struct acpi_irq_parse_one_ctx *ctx = context;
+ struct acpi_resource_irq *irq;
+ struct acpi_resource_extended_irq *eirq;
+ struct fwnode_handle *fwnode;
+
+ switch (ares->type) {
+ case ACPI_RESOURCE_TYPE_IRQ:
+ irq = &ares->data.irq;
+ if (ctx->index >= irq->interrupt_count) {
+ ctx->index -= irq->interrupt_count;
+ return AE_OK;
+ }
+ fwnode = acpi_gsi_domain_id;
+ acpi_irq_parse_one_match(fwnode, irq->interrupts[ctx->index],
+ irq->triggering, irq->polarity,
+ irq->sharable, ctx);
+ return AE_CTRL_TERMINATE;
+ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+ eirq = &ares->data.extended_irq;
+ if (eirq->producer_consumer == ACPI_PRODUCER)
+ return AE_OK;
+ if (ctx->index >= eirq->interrupt_count) {
+ ctx->index -= eirq->interrupt_count;
+ return AE_OK;
+ }
+ fwnode = acpi_get_irq_source_fwhandle(&eirq->resource_source);
+ acpi_irq_parse_one_match(fwnode, eirq->interrupts[ctx->index],
+ eirq->triggering, eirq->polarity,
+ eirq->sharable, ctx);
+ return AE_CTRL_TERMINATE;
+ }
+
+ return AE_OK;
+}
+
+/**
+ * acpi_irq_parse_one - Resolve an interrupt for a device
+ * @handle: the device whose interrupt is to be resolved
+ * @index: index of the interrupt to resolve
+ * @fwspec: structure irq_fwspec filled by this function
+ * @flags: resource flags filled by this function
+ *
+ * Description:
+ * Resolves an interrupt for a device by walking its CRS resources to find
+ * the appropriate ACPI IRQ resource and populating the given struct irq_fwspec
+ * and flags.
+ *
+ * Return:
+ * The result stored in ctx.rc by the callback, or the default -EINVAL value
+ * if an error occurs.
+ */
+static int acpi_irq_parse_one(acpi_handle handle, unsigned int index,
+ struct irq_fwspec *fwspec, unsigned long *flags)
+{
+ struct acpi_irq_parse_one_ctx ctx = { -EINVAL, index, flags, fwspec };
+
+ acpi_walk_resources(handle, METHOD_NAME__CRS, acpi_irq_parse_one_cb, &ctx);
+ return ctx.rc;
+}
+
+/**
+ * acpi_irq_get - Lookup an ACPI IRQ resource and use it to initialize resource.
+ * @handle: ACPI device handle
+ * @index: ACPI IRQ resource index to lookup
+ * @res: Linux IRQ resource to initialize
+ *
+ * Description:
+ * Look for the ACPI IRQ resource with the given index and use it to initialize
+ * the given Linux IRQ resource.
+ *
+ * Return:
+ * 0 on success
+ * -EINVAL if an error occurs
+ * -EPROBE_DEFER if the IRQ lookup/conversion failed
+ */
+int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res)
+{
+ struct irq_fwspec fwspec;
+ struct irq_domain *domain;
+ unsigned long flags;
+ int rc;
+
+ rc = acpi_irq_parse_one(handle, index, &fwspec, &flags);
+ if (rc)
+ return rc;
+
+ domain = irq_find_matching_fwnode(fwspec.fwnode, DOMAIN_BUS_ANY);
+ if (!domain)
+ return -EPROBE_DEFER;
+
+ rc = irq_create_fwspec_mapping(&fwspec);
+ if (rc <= 0)
+ return -EINVAL;
+
+ res->start = rc;
+ res->end = rc;
+ res->flags = flags;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_irq_get);
+
+/**
+ * acpi_set_irq_model - Setup the GSI irqdomain information
+ * @model: the value assigned to acpi_irq_model
+ * @fwnode: the irq_domain identifier for mapping and looking up
+ * GSI interrupts
+ */
+void __init acpi_set_irq_model(enum acpi_irq_model_id model,
+ struct fwnode_handle *fwnode)
+{
+ acpi_irq_model = model;
+ acpi_gsi_domain_id = fwnode;
+}
diff --git a/drivers/acpi/nfit/mce.c b/drivers/acpi/nfit/mce.c
index e5ce81c..3ba1c34 100644
--- a/drivers/acpi/nfit/mce.c
+++ b/drivers/acpi/nfit/mce.c
@@ -90,6 +90,7 @@
static struct notifier_block nfit_mce_dec = {
.notifier_call = nfit_handle_mce,
+ .priority = MCE_PRIO_NFIT,
};
void nfit_mce_register(void)
diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c
index f0b4a98..18b72ee 100644
--- a/drivers/acpi/processor_perflib.c
+++ b/drivers/acpi/processor_perflib.c
@@ -75,10 +75,8 @@
struct acpi_processor *pr;
unsigned int ppc = 0;
- if (event == CPUFREQ_START && ignore_ppc <= 0) {
+ if (ignore_ppc < 0)
ignore_ppc = 0;
- return 0;
- }
if (ignore_ppc)
return 0;
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index cb57962..8b11d6d 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -43,6 +43,19 @@
acpi_iospace_resource_valid(struct resource *res) { return true; }
#endif
+#if IS_ENABLED(CONFIG_ACPI_GENERIC_GSI)
+static inline bool is_gsi(struct acpi_resource_extended_irq *ext_irq)
+{
+ return ext_irq->resource_source.string_length == 0 &&
+ ext_irq->producer_consumer == ACPI_CONSUMER;
+}
+#else
+static inline bool is_gsi(struct acpi_resource_extended_irq *ext_irq)
+{
+ return true;
+}
+#endif
+
static bool acpi_dev_resource_len_valid(u64 start, u64 end, u64 len, bool io)
{
u64 reslen = end - start + 1;
@@ -470,9 +483,12 @@
acpi_dev_irqresource_disabled(res, 0);
return false;
}
- acpi_dev_get_irqresource(res, ext_irq->interrupts[index],
+ if (is_gsi(ext_irq))
+ acpi_dev_get_irqresource(res, ext_irq->interrupts[index],
ext_irq->triggering, ext_irq->polarity,
ext_irq->sharable, false);
+ else
+ acpi_dev_irqresource_disabled(res, 0);
break;
default:
res->flags = 0;
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 4c28e1a..2c3b359 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -17,6 +17,7 @@
#include <linux/of.h>
#include <linux/cpufeature.h>
#include <linux/tick.h>
+#include <linux/pm_qos.h>
#include "base.h"
@@ -376,6 +377,7 @@
per_cpu(cpu_sys_devices, num) = &cpu->dev;
register_cpu_under_node(num, cpu_to_node(num));
+ dev_pm_qos_expose_latency_limit(&cpu->dev, 0);
return 0;
}
diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c
index be6a599..0fc7c4d 100644
--- a/drivers/base/platform-msi.c
+++ b/drivers/base/platform-msi.c
@@ -206,7 +206,7 @@
{
struct platform_msi_priv_data *datap;
/*
- * Limit the number of interrupts to 256 per device. Should we
+ * Limit the number of interrupts to 2048 per device. Should we
* need to bump this up, DEV_ID_SHIFT should be adjusted
* accordingly (which would impact the max number of MSI
* capable devices).
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index c4af003..647e476 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -102,6 +102,16 @@
}
r = platform_get_resource(dev, IORESOURCE_IRQ, num);
+ if (has_acpi_companion(&dev->dev)) {
+ if (r && r->flags & IORESOURCE_DISABLED) {
+ int ret;
+
+ ret = acpi_irq_get(ACPI_HANDLE(&dev->dev), num, r);
+ if (ret)
+ return ret;
+ }
+ }
+
/*
* The resources may pass trigger flags to the irqs that need
* to be set up. It so happens that the trigger flags for
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 2997026..e697dec 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -130,7 +130,7 @@
ret = pm_runtime_is_irq_safe(dev) && !genpd_is_irq_safe(genpd);
- /* Warn once for each IRQ safe dev in no sleep domain */
+ /* Warn once if IRQ safe dev in no sleep domain */
if (ret)
dev_warn_once(dev, "PM domain %s will not be powered off\n",
genpd->name);
@@ -201,7 +201,7 @@
smp_mb__after_atomic();
}
-static int genpd_power_on(struct generic_pm_domain *genpd, bool timed)
+static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
{
unsigned int state_idx = genpd->state_idx;
ktime_t time_start;
@@ -231,7 +231,7 @@
return ret;
}
-static int genpd_power_off(struct generic_pm_domain *genpd, bool timed)
+static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed)
{
unsigned int state_idx = genpd->state_idx;
ktime_t time_start;
@@ -262,10 +262,10 @@
}
/**
- * genpd_queue_power_off_work - Queue up the execution of genpd_poweroff().
+ * genpd_queue_power_off_work - Queue up the execution of genpd_power_off().
* @genpd: PM domain to power off.
*
- * Queue up the execution of genpd_poweroff() unless it's already been done
+ * Queue up the execution of genpd_power_off() unless it's already been done
* before.
*/
static void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
@@ -274,14 +274,101 @@
}
/**
- * genpd_poweron - Restore power to a given PM domain and its masters.
+ * genpd_power_off - Remove power from a given PM domain.
+ * @genpd: PM domain to power down.
+ * @one_dev_on: If invoked from genpd's ->runtime_suspend|resume() callback, the
+ * RPM status of the releated device is in an intermediate state, not yet turned
+ * into RPM_SUSPENDED. This means genpd_power_off() must allow one device to not
+ * be RPM_SUSPENDED, while it tries to power off the PM domain.
+ *
+ * If all of the @genpd's devices have been suspended and all of its subdomains
+ * have been powered down, remove power from @genpd.
+ */
+static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
+ unsigned int depth)
+{
+ struct pm_domain_data *pdd;
+ struct gpd_link *link;
+ unsigned int not_suspended = 0;
+
+ /*
+ * Do not try to power off the domain in the following situations:
+ * (1) The domain is already in the "power off" state.
+ * (2) System suspend is in progress.
+ */
+ if (genpd->status == GPD_STATE_POWER_OFF
+ || genpd->prepared_count > 0)
+ return 0;
+
+ if (atomic_read(&genpd->sd_count) > 0)
+ return -EBUSY;
+
+ list_for_each_entry(pdd, &genpd->dev_list, list_node) {
+ enum pm_qos_flags_status stat;
+
+ stat = dev_pm_qos_flags(pdd->dev,
+ PM_QOS_FLAG_NO_POWER_OFF
+ | PM_QOS_FLAG_REMOTE_WAKEUP);
+ if (stat > PM_QOS_FLAGS_NONE)
+ return -EBUSY;
+
+ /*
+ * Do not allow PM domain to be powered off, when an IRQ safe
+ * device is part of a non-IRQ safe domain.
+ */
+ if (!pm_runtime_suspended(pdd->dev) ||
+ irq_safe_dev_in_no_sleep_domain(pdd->dev, genpd))
+ not_suspended++;
+ }
+
+ if (not_suspended > 1 || (not_suspended == 1 && !one_dev_on))
+ return -EBUSY;
+
+ if (genpd->gov && genpd->gov->power_down_ok) {
+ if (!genpd->gov->power_down_ok(&genpd->domain))
+ return -EAGAIN;
+ }
+
+ if (genpd->power_off) {
+ int ret;
+
+ if (atomic_read(&genpd->sd_count) > 0)
+ return -EBUSY;
+
+ /*
+ * If sd_count > 0 at this point, one of the subdomains hasn't
+ * managed to call genpd_power_on() for the master yet after
+ * incrementing it. In that case genpd_power_on() will wait
+ * for us to drop the lock, so we can call .power_off() and let
+ * the genpd_power_on() restore power for us (this shouldn't
+ * happen very often).
+ */
+ ret = _genpd_power_off(genpd, true);
+ if (ret)
+ return ret;
+ }
+
+ genpd->status = GPD_STATE_POWER_OFF;
+
+ list_for_each_entry(link, &genpd->slave_links, slave_node) {
+ genpd_sd_counter_dec(link->master);
+ genpd_lock_nested(link->master, depth + 1);
+ genpd_power_off(link->master, false, depth + 1);
+ genpd_unlock(link->master);
+ }
+
+ return 0;
+}
+
+/**
+ * genpd_power_on - Restore power to a given PM domain and its masters.
* @genpd: PM domain to power up.
* @depth: nesting count for lockdep.
*
* Restore power to @genpd and all of its masters so that it is possible to
* resume a device belonging to it.
*/
-static int genpd_poweron(struct generic_pm_domain *genpd, unsigned int depth)
+static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
{
struct gpd_link *link;
int ret = 0;
@@ -300,7 +387,7 @@
genpd_sd_counter_inc(master);
genpd_lock_nested(master, depth + 1);
- ret = genpd_poweron(master, depth + 1);
+ ret = genpd_power_on(master, depth + 1);
genpd_unlock(master);
if (ret) {
@@ -309,7 +396,7 @@
}
}
- ret = genpd_power_on(genpd, true);
+ ret = _genpd_power_on(genpd, true);
if (ret)
goto err;
@@ -321,7 +408,9 @@
&genpd->slave_links,
slave_node) {
genpd_sd_counter_dec(link->master);
- genpd_queue_power_off_work(link->master);
+ genpd_lock_nested(link->master, depth + 1);
+ genpd_power_off(link->master, false, depth + 1);
+ genpd_unlock(link->master);
}
return ret;
@@ -368,87 +457,6 @@
}
/**
- * genpd_poweroff - Remove power from a given PM domain.
- * @genpd: PM domain to power down.
- * @is_async: PM domain is powered down from a scheduled work
- *
- * If all of the @genpd's devices have been suspended and all of its subdomains
- * have been powered down, remove power from @genpd.
- */
-static int genpd_poweroff(struct generic_pm_domain *genpd, bool is_async)
-{
- struct pm_domain_data *pdd;
- struct gpd_link *link;
- unsigned int not_suspended = 0;
-
- /*
- * Do not try to power off the domain in the following situations:
- * (1) The domain is already in the "power off" state.
- * (2) System suspend is in progress.
- */
- if (genpd->status == GPD_STATE_POWER_OFF
- || genpd->prepared_count > 0)
- return 0;
-
- if (atomic_read(&genpd->sd_count) > 0)
- return -EBUSY;
-
- list_for_each_entry(pdd, &genpd->dev_list, list_node) {
- enum pm_qos_flags_status stat;
-
- stat = dev_pm_qos_flags(pdd->dev,
- PM_QOS_FLAG_NO_POWER_OFF
- | PM_QOS_FLAG_REMOTE_WAKEUP);
- if (stat > PM_QOS_FLAGS_NONE)
- return -EBUSY;
-
- /*
- * Do not allow PM domain to be powered off, when an IRQ safe
- * device is part of a non-IRQ safe domain.
- */
- if (!pm_runtime_suspended(pdd->dev) ||
- irq_safe_dev_in_no_sleep_domain(pdd->dev, genpd))
- not_suspended++;
- }
-
- if (not_suspended > 1 || (not_suspended == 1 && is_async))
- return -EBUSY;
-
- if (genpd->gov && genpd->gov->power_down_ok) {
- if (!genpd->gov->power_down_ok(&genpd->domain))
- return -EAGAIN;
- }
-
- if (genpd->power_off) {
- int ret;
-
- if (atomic_read(&genpd->sd_count) > 0)
- return -EBUSY;
-
- /*
- * If sd_count > 0 at this point, one of the subdomains hasn't
- * managed to call genpd_poweron() for the master yet after
- * incrementing it. In that case genpd_poweron() will wait
- * for us to drop the lock, so we can call .power_off() and let
- * the genpd_poweron() restore power for us (this shouldn't
- * happen very often).
- */
- ret = genpd_power_off(genpd, true);
- if (ret)
- return ret;
- }
-
- genpd->status = GPD_STATE_POWER_OFF;
-
- list_for_each_entry(link, &genpd->slave_links, slave_node) {
- genpd_sd_counter_dec(link->master);
- genpd_queue_power_off_work(link->master);
- }
-
- return 0;
-}
-
-/**
* genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
* @work: Work structure used for scheduling the execution of this function.
*/
@@ -459,7 +467,7 @@
genpd = container_of(work, struct generic_pm_domain, power_off_work);
genpd_lock(genpd);
- genpd_poweroff(genpd, true);
+ genpd_power_off(genpd, false, 0);
genpd_unlock(genpd);
}
@@ -578,7 +586,7 @@
return 0;
genpd_lock(genpd);
- genpd_poweroff(genpd, false);
+ genpd_power_off(genpd, true, 0);
genpd_unlock(genpd);
return 0;
@@ -618,7 +626,7 @@
}
genpd_lock(genpd);
- ret = genpd_poweron(genpd, 0);
+ ret = genpd_power_on(genpd, 0);
genpd_unlock(genpd);
if (ret)
@@ -658,7 +666,7 @@
if (!pm_runtime_is_irq_safe(dev) ||
(pm_runtime_is_irq_safe(dev) && genpd_is_irq_safe(genpd))) {
genpd_lock(genpd);
- genpd_poweroff(genpd, 0);
+ genpd_power_off(genpd, true, 0);
genpd_unlock(genpd);
}
@@ -674,9 +682,9 @@
__setup("pd_ignore_unused", pd_ignore_unused_setup);
/**
- * genpd_poweroff_unused - Power off all PM domains with no devices in use.
+ * genpd_power_off_unused - Power off all PM domains with no devices in use.
*/
-static int __init genpd_poweroff_unused(void)
+static int __init genpd_power_off_unused(void)
{
struct generic_pm_domain *genpd;
@@ -694,7 +702,7 @@
return 0;
}
-late_initcall(genpd_poweroff_unused);
+late_initcall(genpd_power_off_unused);
#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_GENERIC_DOMAINS_OF)
@@ -727,18 +735,20 @@
}
/**
- * genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
+ * genpd_sync_power_off - Synchronously power off a PM domain and its masters.
* @genpd: PM domain to power off, if possible.
+ * @use_lock: use the lock.
+ * @depth: nesting count for lockdep.
*
* Check if the given PM domain can be powered off (during system suspend or
* hibernation) and do that if so. Also, in that case propagate to its masters.
*
* This function is only called in "noirq" and "syscore" stages of system power
- * transitions, so it need not acquire locks (all of the "noirq" callbacks are
- * executed sequentially, so it is guaranteed that it will never run twice in
- * parallel).
+ * transitions. The "noirq" callbacks may be executed asynchronously, thus in
+ * these cases the lock must be held.
*/
-static void genpd_sync_poweroff(struct generic_pm_domain *genpd)
+static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock,
+ unsigned int depth)
{
struct gpd_link *link;
@@ -751,26 +761,35 @@
/* Choose the deepest state when suspending */
genpd->state_idx = genpd->state_count - 1;
- genpd_power_off(genpd, false);
+ _genpd_power_off(genpd, false);
genpd->status = GPD_STATE_POWER_OFF;
list_for_each_entry(link, &genpd->slave_links, slave_node) {
genpd_sd_counter_dec(link->master);
- genpd_sync_poweroff(link->master);
+
+ if (use_lock)
+ genpd_lock_nested(link->master, depth + 1);
+
+ genpd_sync_power_off(link->master, use_lock, depth + 1);
+
+ if (use_lock)
+ genpd_unlock(link->master);
}
}
/**
- * genpd_sync_poweron - Synchronously power on a PM domain and its masters.
+ * genpd_sync_power_on - Synchronously power on a PM domain and its masters.
* @genpd: PM domain to power on.
+ * @use_lock: use the lock.
+ * @depth: nesting count for lockdep.
*
* This function is only called in "noirq" and "syscore" stages of system power
- * transitions, so it need not acquire locks (all of the "noirq" callbacks are
- * executed sequentially, so it is guaranteed that it will never run twice in
- * parallel).
+ * transitions. The "noirq" callbacks may be executed asynchronously, thus in
+ * these cases the lock must be held.
*/
-static void genpd_sync_poweron(struct generic_pm_domain *genpd)
+static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock,
+ unsigned int depth)
{
struct gpd_link *link;
@@ -778,11 +797,18 @@
return;
list_for_each_entry(link, &genpd->slave_links, slave_node) {
- genpd_sync_poweron(link->master);
genpd_sd_counter_inc(link->master);
+
+ if (use_lock)
+ genpd_lock_nested(link->master, depth + 1);
+
+ genpd_sync_power_on(link->master, use_lock, depth + 1);
+
+ if (use_lock)
+ genpd_unlock(link->master);
}
- genpd_power_on(genpd, false);
+ _genpd_power_on(genpd, false);
genpd->status = GPD_STATE_ACTIVE;
}
@@ -888,13 +914,10 @@
return ret;
}
- /*
- * Since all of the "noirq" callbacks are executed sequentially, it is
- * guaranteed that this function will never run twice in parallel for
- * the same PM domain, so it is not necessary to use locking here.
- */
+ genpd_lock(genpd);
genpd->suspended_count++;
- genpd_sync_poweroff(genpd);
+ genpd_sync_power_off(genpd, true, 0);
+ genpd_unlock(genpd);
return 0;
}
@@ -919,13 +942,10 @@
if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))
return 0;
- /*
- * Since all of the "noirq" callbacks are executed sequentially, it is
- * guaranteed that this function will never run twice in parallel for
- * the same PM domain, so it is not necessary to use locking here.
- */
- genpd_sync_poweron(genpd);
+ genpd_lock(genpd);
+ genpd_sync_power_on(genpd, true, 0);
genpd->suspended_count--;
+ genpd_unlock(genpd);
if (genpd->dev_ops.stop && genpd->dev_ops.start)
ret = pm_runtime_force_resume(dev);
@@ -1002,22 +1022,20 @@
return -EINVAL;
/*
- * Since all of the "noirq" callbacks are executed sequentially, it is
- * guaranteed that this function will never run twice in parallel for
- * the same PM domain, so it is not necessary to use locking here.
- *
* At this point suspended_count == 0 means we are being run for the
* first time for the given domain in the present cycle.
*/
+ genpd_lock(genpd);
if (genpd->suspended_count++ == 0)
/*
* The boot kernel might put the domain into arbitrary state,
- * so make it appear as powered off to genpd_sync_poweron(),
+ * so make it appear as powered off to genpd_sync_power_on(),
* so that it tries to power it on in case it was really off.
*/
genpd->status = GPD_STATE_POWER_OFF;
- genpd_sync_poweron(genpd);
+ genpd_sync_power_on(genpd, true, 0);
+ genpd_unlock(genpd);
if (genpd->dev_ops.stop && genpd->dev_ops.start)
ret = pm_runtime_force_resume(dev);
@@ -1072,9 +1090,9 @@
if (suspend) {
genpd->suspended_count++;
- genpd_sync_poweroff(genpd);
+ genpd_sync_power_off(genpd, false, 0);
} else {
- genpd_sync_poweron(genpd);
+ genpd_sync_power_on(genpd, false, 0);
genpd->suspended_count--;
}
}
@@ -2043,7 +2061,7 @@
dev->pm_domain->sync = genpd_dev_pm_sync;
genpd_lock(pd);
- ret = genpd_poweron(pd, 0);
+ ret = genpd_power_on(pd, 0);
genpd_unlock(pd);
out:
return ret ? -EPROBE_DEFER : 0;
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c
index 35ff062..dae6172 100644
--- a/drivers/base/power/opp/core.c
+++ b/drivers/base/power/opp/core.c
@@ -32,13 +32,7 @@
/* Lock to allow exclusive modification to the device and opp lists */
DEFINE_MUTEX(opp_table_lock);
-#define opp_rcu_lockdep_assert() \
-do { \
- RCU_LOCKDEP_WARN(!rcu_read_lock_held() && \
- !lockdep_is_held(&opp_table_lock), \
- "Missing rcu_read_lock() or " \
- "opp_table_lock protection"); \
-} while (0)
+static void dev_pm_opp_get(struct dev_pm_opp *opp);
static struct opp_device *_find_opp_dev(const struct device *dev,
struct opp_table *opp_table)
@@ -52,38 +46,46 @@
return NULL;
}
+static struct opp_table *_find_opp_table_unlocked(struct device *dev)
+{
+ struct opp_table *opp_table;
+
+ list_for_each_entry(opp_table, &opp_tables, node) {
+ if (_find_opp_dev(dev, opp_table)) {
+ _get_opp_table_kref(opp_table);
+
+ return opp_table;
+ }
+ }
+
+ return ERR_PTR(-ENODEV);
+}
+
/**
* _find_opp_table() - find opp_table struct using device pointer
* @dev: device pointer used to lookup OPP table
*
- * Search OPP table for one containing matching device. Does a RCU reader
- * operation to grab the pointer needed.
+ * Search OPP table for one containing matching device.
*
* Return: pointer to 'struct opp_table' if found, otherwise -ENODEV or
* -EINVAL based on type of error.
*
- * Locking: For readers, this function must be called under rcu_read_lock().
- * opp_table is a RCU protected pointer, which means that opp_table is valid
- * as long as we are under RCU lock.
- *
- * For Writers, this function must be called with opp_table_lock held.
+ * The callers must call dev_pm_opp_put_opp_table() after the table is used.
*/
struct opp_table *_find_opp_table(struct device *dev)
{
struct opp_table *opp_table;
- opp_rcu_lockdep_assert();
-
if (IS_ERR_OR_NULL(dev)) {
pr_err("%s: Invalid parameters\n", __func__);
return ERR_PTR(-EINVAL);
}
- list_for_each_entry_rcu(opp_table, &opp_tables, node)
- if (_find_opp_dev(dev, opp_table))
- return opp_table;
+ mutex_lock(&opp_table_lock);
+ opp_table = _find_opp_table_unlocked(dev);
+ mutex_unlock(&opp_table_lock);
- return ERR_PTR(-ENODEV);
+ return opp_table;
}
/**
@@ -94,29 +96,15 @@
* return 0
*
* This is useful only for devices with single power supply.
- *
- * Locking: This function must be called under rcu_read_lock(). opp is a rcu
- * protected pointer. This means that opp which could have been fetched by
- * opp_find_freq_{exact,ceil,floor} functions is valid as long as we are
- * under RCU lock. The pointer returned by the opp_find_freq family must be
- * used in the same section as the usage of this function with the pointer
- * prior to unlocking with rcu_read_unlock() to maintain the integrity of the
- * pointer.
*/
unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
{
- struct dev_pm_opp *tmp_opp;
- unsigned long v = 0;
-
- opp_rcu_lockdep_assert();
-
- tmp_opp = rcu_dereference(opp);
- if (IS_ERR_OR_NULL(tmp_opp))
+ if (IS_ERR_OR_NULL(opp)) {
pr_err("%s: Invalid parameters\n", __func__);
- else
- v = tmp_opp->supplies[0].u_volt;
+ return 0;
+ }
- return v;
+ return opp->supplies[0].u_volt;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage);
@@ -126,29 +114,15 @@
*
* Return: frequency in hertz corresponding to the opp, else
* return 0
- *
- * Locking: This function must be called under rcu_read_lock(). opp is a rcu
- * protected pointer. This means that opp which could have been fetched by
- * opp_find_freq_{exact,ceil,floor} functions is valid as long as we are
- * under RCU lock. The pointer returned by the opp_find_freq family must be
- * used in the same section as the usage of this function with the pointer
- * prior to unlocking with rcu_read_unlock() to maintain the integrity of the
- * pointer.
*/
unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp)
{
- struct dev_pm_opp *tmp_opp;
- unsigned long f = 0;
-
- opp_rcu_lockdep_assert();
-
- tmp_opp = rcu_dereference(opp);
- if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available)
+ if (IS_ERR_OR_NULL(opp) || !opp->available) {
pr_err("%s: Invalid parameters\n", __func__);
- else
- f = tmp_opp->rate;
+ return 0;
+ }
- return f;
+ return opp->rate;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq);
@@ -161,28 +135,15 @@
* quickly. Running on them for longer times may overheat the chip.
*
* Return: true if opp is turbo opp, else false.
- *
- * Locking: This function must be called under rcu_read_lock(). opp is a rcu
- * protected pointer. This means that opp which could have been fetched by
- * opp_find_freq_{exact,ceil,floor} functions is valid as long as we are
- * under RCU lock. The pointer returned by the opp_find_freq family must be
- * used in the same section as the usage of this function with the pointer
- * prior to unlocking with rcu_read_unlock() to maintain the integrity of the
- * pointer.
*/
bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp)
{
- struct dev_pm_opp *tmp_opp;
-
- opp_rcu_lockdep_assert();
-
- tmp_opp = rcu_dereference(opp);
- if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) {
+ if (IS_ERR_OR_NULL(opp) || !opp->available) {
pr_err("%s: Invalid parameters\n", __func__);
return false;
}
- return tmp_opp->turbo;
+ return opp->turbo;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo);
@@ -191,52 +152,29 @@
* @dev: device for which we do this operation
*
* Return: This function returns the max clock latency in nanoseconds.
- *
- * Locking: This function takes rcu_read_lock().
*/
unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev)
{
struct opp_table *opp_table;
unsigned long clock_latency_ns;
- rcu_read_lock();
-
opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table))
- clock_latency_ns = 0;
- else
- clock_latency_ns = opp_table->clock_latency_ns_max;
+ return 0;
- rcu_read_unlock();
+ clock_latency_ns = opp_table->clock_latency_ns_max;
+
+ dev_pm_opp_put_opp_table(opp_table);
+
return clock_latency_ns;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency);
-static int _get_regulator_count(struct device *dev)
-{
- struct opp_table *opp_table;
- int count;
-
- rcu_read_lock();
-
- opp_table = _find_opp_table(dev);
- if (!IS_ERR(opp_table))
- count = opp_table->regulator_count;
- else
- count = 0;
-
- rcu_read_unlock();
-
- return count;
-}
-
/**
* dev_pm_opp_get_max_volt_latency() - Get max voltage latency in nanoseconds
* @dev: device for which we do this operation
*
* Return: This function returns the max voltage latency in nanoseconds.
- *
- * Locking: This function takes rcu_read_lock().
*/
unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev)
{
@@ -250,35 +188,33 @@
unsigned long max;
} *uV;
- count = _get_regulator_count(dev);
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table))
+ return 0;
+
+ count = opp_table->regulator_count;
/* Regulator may not be required for the device */
if (!count)
- return 0;
+ goto put_opp_table;
regulators = kmalloc_array(count, sizeof(*regulators), GFP_KERNEL);
if (!regulators)
- return 0;
+ goto put_opp_table;
uV = kmalloc_array(count, sizeof(*uV), GFP_KERNEL);
if (!uV)
goto free_regulators;
- rcu_read_lock();
-
- opp_table = _find_opp_table(dev);
- if (IS_ERR(opp_table)) {
- rcu_read_unlock();
- goto free_uV;
- }
-
memcpy(regulators, opp_table->regulators, count * sizeof(*regulators));
+ mutex_lock(&opp_table->lock);
+
for (i = 0; i < count; i++) {
uV[i].min = ~0;
uV[i].max = 0;
- list_for_each_entry_rcu(opp, &opp_table->opp_list, node) {
+ list_for_each_entry(opp, &opp_table->opp_list, node) {
if (!opp->available)
continue;
@@ -289,22 +225,24 @@
}
}
- rcu_read_unlock();
+ mutex_unlock(&opp_table->lock);
/*
* The caller needs to ensure that opp_table (and hence the regulator)
* isn't freed, while we are executing this routine.
*/
- for (i = 0; reg = regulators[i], i < count; i++) {
+ for (i = 0; i < count; i++) {
+ reg = regulators[i];
ret = regulator_set_voltage_time(reg, uV[i].min, uV[i].max);
if (ret > 0)
latency_ns += ret * 1000;
}
-free_uV:
kfree(uV);
free_regulators:
kfree(regulators);
+put_opp_table:
+ dev_pm_opp_put_opp_table(opp_table);
return latency_ns;
}
@@ -317,8 +255,6 @@
*
* Return: This function returns the max transition latency, in nanoseconds, to
* switch from one OPP to other.
- *
- * Locking: This function takes rcu_read_lock().
*/
unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev)
{
@@ -328,32 +264,29 @@
EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_transition_latency);
/**
- * dev_pm_opp_get_suspend_opp() - Get suspend opp
+ * dev_pm_opp_get_suspend_opp_freq() - Get frequency of suspend opp in Hz
* @dev: device for which we do this operation
*
- * Return: This function returns pointer to the suspend opp if it is
- * defined and available, otherwise it returns NULL.
- *
- * Locking: This function must be called under rcu_read_lock(). opp is a rcu
- * protected pointer. The reason for the same is that the opp pointer which is
- * returned will remain valid for use with opp_get_{voltage, freq} only while
- * under the locked area. The pointer returned must be used prior to unlocking
- * with rcu_read_unlock() to maintain the integrity of the pointer.
+ * Return: This function returns the frequency of the OPP marked as suspend_opp
+ * if one is available, else returns 0;
*/
-struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev)
+unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev)
{
struct opp_table *opp_table;
-
- opp_rcu_lockdep_assert();
+ unsigned long freq = 0;
opp_table = _find_opp_table(dev);
- if (IS_ERR(opp_table) || !opp_table->suspend_opp ||
- !opp_table->suspend_opp->available)
- return NULL;
+ if (IS_ERR(opp_table))
+ return 0;
- return opp_table->suspend_opp;
+ if (opp_table->suspend_opp && opp_table->suspend_opp->available)
+ freq = dev_pm_opp_get_freq(opp_table->suspend_opp);
+
+ dev_pm_opp_put_opp_table(opp_table);
+
+ return freq;
}
-EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp);
+EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp_freq);
/**
* dev_pm_opp_get_opp_count() - Get number of opps available in the opp table
@@ -361,8 +294,6 @@
*
* Return: This function returns the number of available opps if there are any,
* else returns 0 if none or the corresponding error value.
- *
- * Locking: This function takes rcu_read_lock().
*/
int dev_pm_opp_get_opp_count(struct device *dev)
{
@@ -370,23 +301,24 @@
struct dev_pm_opp *temp_opp;
int count = 0;
- rcu_read_lock();
-
opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table)) {
count = PTR_ERR(opp_table);
dev_err(dev, "%s: OPP table not found (%d)\n",
__func__, count);
- goto out_unlock;
+ return count;
}
- list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) {
+ mutex_lock(&opp_table->lock);
+
+ list_for_each_entry(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available)
count++;
}
-out_unlock:
- rcu_read_unlock();
+ mutex_unlock(&opp_table->lock);
+ dev_pm_opp_put_opp_table(opp_table);
+
return count;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count);
@@ -411,11 +343,8 @@
* This provides a mechanism to enable an opp which is not available currently
* or the opposite as well.
*
- * Locking: This function must be called under rcu_read_lock(). opp is a rcu
- * protected pointer. The reason for the same is that the opp pointer which is
- * returned will remain valid for use with opp_get_{voltage, freq} only while
- * under the locked area. The pointer returned must be used prior to unlocking
- * with rcu_read_unlock() to maintain the integrity of the pointer.
+ * The callers are required to call dev_pm_opp_put() for the returned OPP after
+ * use.
*/
struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
unsigned long freq,
@@ -424,8 +353,6 @@
struct opp_table *opp_table;
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
- opp_rcu_lockdep_assert();
-
opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table)) {
int r = PTR_ERR(opp_table);
@@ -434,14 +361,22 @@
return ERR_PTR(r);
}
- list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) {
+ mutex_lock(&opp_table->lock);
+
+ list_for_each_entry(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available == available &&
temp_opp->rate == freq) {
opp = temp_opp;
+
+ /* Increment the reference count of OPP */
+ dev_pm_opp_get(opp);
break;
}
}
+ mutex_unlock(&opp_table->lock);
+ dev_pm_opp_put_opp_table(opp_table);
+
return opp;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact);
@@ -451,14 +386,21 @@
{
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
- list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) {
+ mutex_lock(&opp_table->lock);
+
+ list_for_each_entry(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available && temp_opp->rate >= *freq) {
opp = temp_opp;
*freq = opp->rate;
+
+ /* Increment the reference count of OPP */
+ dev_pm_opp_get(opp);
break;
}
}
+ mutex_unlock(&opp_table->lock);
+
return opp;
}
@@ -477,18 +419,14 @@
* ERANGE: no match found for search
* ENODEV: if device not found in list of registered devices
*
- * Locking: This function must be called under rcu_read_lock(). opp is a rcu
- * protected pointer. The reason for the same is that the opp pointer which is
- * returned will remain valid for use with opp_get_{voltage, freq} only while
- * under the locked area. The pointer returned must be used prior to unlocking
- * with rcu_read_unlock() to maintain the integrity of the pointer.
+ * The callers are required to call dev_pm_opp_put() for the returned OPP after
+ * use.
*/
struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev,
unsigned long *freq)
{
struct opp_table *opp_table;
-
- opp_rcu_lockdep_assert();
+ struct dev_pm_opp *opp;
if (!dev || !freq) {
dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq);
@@ -499,7 +437,11 @@
if (IS_ERR(opp_table))
return ERR_CAST(opp_table);
- return _find_freq_ceil(opp_table, freq);
+ opp = _find_freq_ceil(opp_table, freq);
+
+ dev_pm_opp_put_opp_table(opp_table);
+
+ return opp;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil);
@@ -518,11 +460,8 @@
* ERANGE: no match found for search
* ENODEV: if device not found in list of registered devices
*
- * Locking: This function must be called under rcu_read_lock(). opp is a rcu
- * protected pointer. The reason for the same is that the opp pointer which is
- * returned will remain valid for use with opp_get_{voltage, freq} only while
- * under the locked area. The pointer returned must be used prior to unlocking
- * with rcu_read_unlock() to maintain the integrity of the pointer.
+ * The callers are required to call dev_pm_opp_put() for the returned OPP after
+ * use.
*/
struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
unsigned long *freq)
@@ -530,8 +469,6 @@
struct opp_table *opp_table;
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
- opp_rcu_lockdep_assert();
-
if (!dev || !freq) {
dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq);
return ERR_PTR(-EINVAL);
@@ -541,7 +478,9 @@
if (IS_ERR(opp_table))
return ERR_CAST(opp_table);
- list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) {
+ mutex_lock(&opp_table->lock);
+
+ list_for_each_entry(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available) {
/* go to the next node, before choosing prev */
if (temp_opp->rate > *freq)
@@ -550,6 +489,13 @@
opp = temp_opp;
}
}
+
+ /* Increment the reference count of OPP */
+ if (!IS_ERR(opp))
+ dev_pm_opp_get(opp);
+ mutex_unlock(&opp_table->lock);
+ dev_pm_opp_put_opp_table(opp_table);
+
if (!IS_ERR(opp))
*freq = opp->rate;
@@ -557,34 +503,6 @@
}
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor);
-/*
- * The caller needs to ensure that opp_table (and hence the clk) isn't freed,
- * while clk returned here is used.
- */
-static struct clk *_get_opp_clk(struct device *dev)
-{
- struct opp_table *opp_table;
- struct clk *clk;
-
- rcu_read_lock();
-
- opp_table = _find_opp_table(dev);
- if (IS_ERR(opp_table)) {
- dev_err(dev, "%s: device opp doesn't exist\n", __func__);
- clk = ERR_CAST(opp_table);
- goto unlock;
- }
-
- clk = opp_table->clk;
- if (IS_ERR(clk))
- dev_err(dev, "%s: No clock available for the device\n",
- __func__);
-
-unlock:
- rcu_read_unlock();
- return clk;
-}
-
static int _set_opp_voltage(struct device *dev, struct regulator *reg,
struct dev_pm_opp_supply *supply)
{
@@ -680,8 +598,6 @@
*
* This configures the power-supplies and clock source to the levels specified
* by the OPP corresponding to the target_freq.
- *
- * Locking: This function takes rcu_read_lock().
*/
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
{
@@ -700,9 +616,19 @@
return -EINVAL;
}
- clk = _get_opp_clk(dev);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table)) {
+ dev_err(dev, "%s: device opp doesn't exist\n", __func__);
+ return PTR_ERR(opp_table);
+ }
+
+ clk = opp_table->clk;
+ if (IS_ERR(clk)) {
+ dev_err(dev, "%s: No clock available for the device\n",
+ __func__);
+ ret = PTR_ERR(clk);
+ goto put_opp_table;
+ }
freq = clk_round_rate(clk, target_freq);
if ((long)freq <= 0)
@@ -714,16 +640,8 @@
if (old_freq == freq) {
dev_dbg(dev, "%s: old/new frequencies (%lu Hz) are same, nothing to do\n",
__func__, freq);
- return 0;
- }
-
- rcu_read_lock();
-
- opp_table = _find_opp_table(dev);
- if (IS_ERR(opp_table)) {
- dev_err(dev, "%s: device opp doesn't exist\n", __func__);
- rcu_read_unlock();
- return PTR_ERR(opp_table);
+ ret = 0;
+ goto put_opp_table;
}
old_opp = _find_freq_ceil(opp_table, &old_freq);
@@ -737,8 +655,7 @@
ret = PTR_ERR(opp);
dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n",
__func__, freq, ret);
- rcu_read_unlock();
- return ret;
+ goto put_old_opp;
}
dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n", __func__,
@@ -748,8 +665,8 @@
/* Only frequency scaling */
if (!regulators) {
- rcu_read_unlock();
- return _generic_set_opp_clk_only(dev, clk, old_freq, freq);
+ ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq);
+ goto put_opps;
}
if (opp_table->set_opp)
@@ -773,28 +690,26 @@
data->new_opp.rate = freq;
memcpy(data->new_opp.supplies, opp->supplies, size);
- rcu_read_unlock();
+ ret = set_opp(data);
- return set_opp(data);
+put_opps:
+ dev_pm_opp_put(opp);
+put_old_opp:
+ if (!IS_ERR(old_opp))
+ dev_pm_opp_put(old_opp);
+put_opp_table:
+ dev_pm_opp_put_opp_table(opp_table);
+ return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate);
/* OPP-dev Helpers */
-static void _kfree_opp_dev_rcu(struct rcu_head *head)
-{
- struct opp_device *opp_dev;
-
- opp_dev = container_of(head, struct opp_device, rcu_head);
- kfree_rcu(opp_dev, rcu_head);
-}
-
static void _remove_opp_dev(struct opp_device *opp_dev,
struct opp_table *opp_table)
{
opp_debug_unregister(opp_dev, opp_table);
list_del(&opp_dev->node);
- call_srcu(&opp_table->srcu_head.srcu, &opp_dev->rcu_head,
- _kfree_opp_dev_rcu);
+ kfree(opp_dev);
}
struct opp_device *_add_opp_dev(const struct device *dev,
@@ -809,7 +724,7 @@
/* Initialize opp-dev */
opp_dev->dev = dev;
- list_add_rcu(&opp_dev->node, &opp_table->dev_list);
+ list_add(&opp_dev->node, &opp_table->dev_list);
/* Create debugfs entries for the opp_table */
ret = opp_debug_register(opp_dev, opp_table);
@@ -820,26 +735,12 @@
return opp_dev;
}
-/**
- * _add_opp_table() - Find OPP table or allocate a new one
- * @dev: device for which we do this operation
- *
- * It tries to find an existing table first, if it couldn't find one, it
- * allocates a new OPP table and returns that.
- *
- * Return: valid opp_table pointer if success, else NULL.
- */
-static struct opp_table *_add_opp_table(struct device *dev)
+static struct opp_table *_allocate_opp_table(struct device *dev)
{
struct opp_table *opp_table;
struct opp_device *opp_dev;
int ret;
- /* Check for existing table for 'dev' first */
- opp_table = _find_opp_table(dev);
- if (!IS_ERR(opp_table))
- return opp_table;
-
/*
* Allocate a new OPP table. In the infrequent case where a new
* device is needed to be added, we pay this penalty.
@@ -867,51 +768,46 @@
ret);
}
- srcu_init_notifier_head(&opp_table->srcu_head);
+ BLOCKING_INIT_NOTIFIER_HEAD(&opp_table->head);
INIT_LIST_HEAD(&opp_table->opp_list);
+ mutex_init(&opp_table->lock);
+ kref_init(&opp_table->kref);
/* Secure the device table modification */
- list_add_rcu(&opp_table->node, &opp_tables);
+ list_add(&opp_table->node, &opp_tables);
return opp_table;
}
-/**
- * _kfree_device_rcu() - Free opp_table RCU handler
- * @head: RCU head
- */
-static void _kfree_device_rcu(struct rcu_head *head)
+void _get_opp_table_kref(struct opp_table *opp_table)
{
- struct opp_table *opp_table = container_of(head, struct opp_table,
- rcu_head);
-
- kfree_rcu(opp_table, rcu_head);
+ kref_get(&opp_table->kref);
}
-/**
- * _remove_opp_table() - Removes a OPP table
- * @opp_table: OPP table to be removed.
- *
- * Removes/frees OPP table if it doesn't contain any OPPs.
- */
-static void _remove_opp_table(struct opp_table *opp_table)
+struct opp_table *dev_pm_opp_get_opp_table(struct device *dev)
{
+ struct opp_table *opp_table;
+
+ /* Hold our table modification lock here */
+ mutex_lock(&opp_table_lock);
+
+ opp_table = _find_opp_table_unlocked(dev);
+ if (!IS_ERR(opp_table))
+ goto unlock;
+
+ opp_table = _allocate_opp_table(dev);
+
+unlock:
+ mutex_unlock(&opp_table_lock);
+
+ return opp_table;
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_table);
+
+static void _opp_table_kref_release(struct kref *kref)
+{
+ struct opp_table *opp_table = container_of(kref, struct opp_table, kref);
struct opp_device *opp_dev;
- if (!list_empty(&opp_table->opp_list))
- return;
-
- if (opp_table->supported_hw)
- return;
-
- if (opp_table->prop_name)
- return;
-
- if (opp_table->regulators)
- return;
-
- if (opp_table->set_opp)
- return;
-
/* Release clk */
if (!IS_ERR(opp_table->clk))
clk_put(opp_table->clk);
@@ -924,63 +820,60 @@
/* dev_list must be empty now */
WARN_ON(!list_empty(&opp_table->dev_list));
- list_del_rcu(&opp_table->node);
- call_srcu(&opp_table->srcu_head.srcu, &opp_table->rcu_head,
- _kfree_device_rcu);
+ mutex_destroy(&opp_table->lock);
+ list_del(&opp_table->node);
+ kfree(opp_table);
+
+ mutex_unlock(&opp_table_lock);
}
-/**
- * _kfree_opp_rcu() - Free OPP RCU handler
- * @head: RCU head
- */
-static void _kfree_opp_rcu(struct rcu_head *head)
+void dev_pm_opp_put_opp_table(struct opp_table *opp_table)
{
- struct dev_pm_opp *opp = container_of(head, struct dev_pm_opp, rcu_head);
+ kref_put_mutex(&opp_table->kref, _opp_table_kref_release,
+ &opp_table_lock);
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_put_opp_table);
- kfree_rcu(opp, rcu_head);
+void _opp_free(struct dev_pm_opp *opp)
+{
+ kfree(opp);
}
-/**
- * _opp_remove() - Remove an OPP from a table definition
- * @opp_table: points back to the opp_table struct this opp belongs to
- * @opp: pointer to the OPP to remove
- * @notify: OPP_EVENT_REMOVE notification should be sent or not
- *
- * This function removes an opp definition from the opp table.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * It is assumed that the caller holds required mutex for an RCU updater
- * strategy.
- */
-void _opp_remove(struct opp_table *opp_table, struct dev_pm_opp *opp,
- bool notify)
+static void _opp_kref_release(struct kref *kref)
{
+ struct dev_pm_opp *opp = container_of(kref, struct dev_pm_opp, kref);
+ struct opp_table *opp_table = opp->opp_table;
+
/*
* Notify the changes in the availability of the operable
* frequency/voltage list.
*/
- if (notify)
- srcu_notifier_call_chain(&opp_table->srcu_head,
- OPP_EVENT_REMOVE, opp);
+ blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_REMOVE, opp);
opp_debug_remove_one(opp);
- list_del_rcu(&opp->node);
- call_srcu(&opp_table->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu);
+ list_del(&opp->node);
+ kfree(opp);
- _remove_opp_table(opp_table);
+ mutex_unlock(&opp_table->lock);
+ dev_pm_opp_put_opp_table(opp_table);
}
+static void dev_pm_opp_get(struct dev_pm_opp *opp)
+{
+ kref_get(&opp->kref);
+}
+
+void dev_pm_opp_put(struct dev_pm_opp *opp)
+{
+ kref_put_mutex(&opp->kref, _opp_kref_release, &opp->opp_table->lock);
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_put);
+
/**
* dev_pm_opp_remove() - Remove an OPP from OPP table
* @dev: device for which we do this operation
* @freq: OPP to remove with matching 'freq'
*
* This function removes an opp from the opp table.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
*/
void dev_pm_opp_remove(struct device *dev, unsigned long freq)
{
@@ -988,12 +881,11 @@
struct opp_table *opp_table;
bool found = false;
- /* Hold our table modification lock here */
- mutex_lock(&opp_table_lock);
-
opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table))
- goto unlock;
+ return;
+
+ mutex_lock(&opp_table->lock);
list_for_each_entry(opp, &opp_table->opp_list, node) {
if (opp->rate == freq) {
@@ -1002,28 +894,23 @@
}
}
- if (!found) {
+ mutex_unlock(&opp_table->lock);
+
+ if (found) {
+ dev_pm_opp_put(opp);
+ } else {
dev_warn(dev, "%s: Couldn't find OPP with freq: %lu\n",
__func__, freq);
- goto unlock;
}
- _opp_remove(opp_table, opp, true);
-unlock:
- mutex_unlock(&opp_table_lock);
+ dev_pm_opp_put_opp_table(opp_table);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_remove);
-struct dev_pm_opp *_allocate_opp(struct device *dev,
- struct opp_table **opp_table)
+struct dev_pm_opp *_opp_allocate(struct opp_table *table)
{
struct dev_pm_opp *opp;
int count, supply_size;
- struct opp_table *table;
-
- table = _add_opp_table(dev);
- if (!table)
- return NULL;
/* Allocate space for at least one supply */
count = table->regulator_count ? table->regulator_count : 1;
@@ -1031,17 +918,13 @@
/* allocate new OPP node and supplies structures */
opp = kzalloc(sizeof(*opp) + supply_size, GFP_KERNEL);
- if (!opp) {
- kfree(table);
+ if (!opp)
return NULL;
- }
/* Put the supplies at the end of the OPP structure as an empty array */
opp->supplies = (struct dev_pm_opp_supply *)(opp + 1);
INIT_LIST_HEAD(&opp->node);
- *opp_table = table;
-
return opp;
}
@@ -1067,11 +950,21 @@
return true;
}
+/*
+ * Returns:
+ * 0: On success. And appropriate error message for duplicate OPPs.
+ * -EBUSY: For OPP with same freq/volt and is available. The callers of
+ * _opp_add() must return 0 if they receive -EBUSY from it. This is to make
+ * sure we don't print error messages unnecessarily if different parts of
+ * kernel try to initialize the OPP table.
+ * -EEXIST: For OPP with same freq but different volt or is unavailable. This
+ * should be considered an error by the callers of _opp_add().
+ */
int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
struct opp_table *opp_table)
{
struct dev_pm_opp *opp;
- struct list_head *head = &opp_table->opp_list;
+ struct list_head *head;
int ret;
/*
@@ -1082,7 +975,10 @@
* loop, don't replace it with head otherwise it will become an infinite
* loop.
*/
- list_for_each_entry_rcu(opp, &opp_table->opp_list, node) {
+ mutex_lock(&opp_table->lock);
+ head = &opp_table->opp_list;
+
+ list_for_each_entry(opp, &opp_table->opp_list, node) {
if (new_opp->rate > opp->rate) {
head = &opp->node;
continue;
@@ -1098,12 +994,21 @@
new_opp->supplies[0].u_volt, new_opp->available);
/* Should we compare voltages for all regulators here ? */
- return opp->available &&
- new_opp->supplies[0].u_volt == opp->supplies[0].u_volt ? 0 : -EEXIST;
+ ret = opp->available &&
+ new_opp->supplies[0].u_volt == opp->supplies[0].u_volt ? -EBUSY : -EEXIST;
+
+ mutex_unlock(&opp_table->lock);
+ return ret;
}
+ list_add(&new_opp->node, head);
+ mutex_unlock(&opp_table->lock);
+
new_opp->opp_table = opp_table;
- list_add_rcu(&new_opp->node, head);
+ kref_init(&new_opp->kref);
+
+ /* Get a reference to the OPP table */
+ _get_opp_table_kref(opp_table);
ret = opp_debug_create_one(new_opp, opp_table);
if (ret)
@@ -1121,6 +1026,7 @@
/**
* _opp_add_v1() - Allocate a OPP based on v1 bindings.
+ * @opp_table: OPP table
* @dev: device for which we do this operation
* @freq: Frequency in Hz for this OPP
* @u_volt: Voltage in uVolts for this OPP
@@ -1133,12 +1039,6 @@
* NOTE: "dynamic" parameter impacts OPPs added by the dev_pm_opp_of_add_table
* and freed by dev_pm_opp_of_remove_table.
*
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
- *
* Return:
* 0 On success OR
* Duplicate OPPs (both freq and volt are same) and opp->available
@@ -1146,22 +1046,16 @@
* Duplicate OPPs (both freq and volt are same) and !opp->available
* -ENOMEM Memory allocation failure
*/
-int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt,
- bool dynamic)
+int _opp_add_v1(struct opp_table *opp_table, struct device *dev,
+ unsigned long freq, long u_volt, bool dynamic)
{
- struct opp_table *opp_table;
struct dev_pm_opp *new_opp;
unsigned long tol;
int ret;
- /* Hold our table modification lock here */
- mutex_lock(&opp_table_lock);
-
- new_opp = _allocate_opp(dev, &opp_table);
- if (!new_opp) {
- ret = -ENOMEM;
- goto unlock;
- }
+ new_opp = _opp_allocate(opp_table);
+ if (!new_opp)
+ return -ENOMEM;
/* populate the opp table */
new_opp->rate = freq;
@@ -1173,22 +1067,23 @@
new_opp->dynamic = dynamic;
ret = _opp_add(dev, new_opp, opp_table);
- if (ret)
+ if (ret) {
+ /* Don't return error for duplicate OPPs */
+ if (ret == -EBUSY)
+ ret = 0;
goto free_opp;
-
- mutex_unlock(&opp_table_lock);
+ }
/*
* Notify the changes in the availability of the operable
* frequency/voltage list.
*/
- srcu_notifier_call_chain(&opp_table->srcu_head, OPP_EVENT_ADD, new_opp);
+ blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADD, new_opp);
return 0;
free_opp:
- _opp_remove(opp_table, new_opp, false);
-unlock:
- mutex_unlock(&opp_table_lock);
+ _opp_free(new_opp);
+
return ret;
}
@@ -1202,27 +1097,16 @@
* specify the hierarchy of versions it supports. OPP layer will then enable
* OPPs, which are available for those versions, based on its 'opp-supported-hw'
* property.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
*/
-int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions,
- unsigned int count)
+struct opp_table *dev_pm_opp_set_supported_hw(struct device *dev,
+ const u32 *versions, unsigned int count)
{
struct opp_table *opp_table;
- int ret = 0;
+ int ret;
- /* Hold our table modification lock here */
- mutex_lock(&opp_table_lock);
-
- opp_table = _add_opp_table(dev);
- if (!opp_table) {
- ret = -ENOMEM;
- goto unlock;
- }
+ opp_table = dev_pm_opp_get_opp_table(dev);
+ if (!opp_table)
+ return ERR_PTR(-ENOMEM);
/* Make sure there are no concurrent readers while updating opp_table */
WARN_ON(!list_empty(&opp_table->opp_list));
@@ -1243,65 +1127,40 @@
}
opp_table->supported_hw_count = count;
- mutex_unlock(&opp_table_lock);
- return 0;
+
+ return opp_table;
err:
- _remove_opp_table(opp_table);
-unlock:
- mutex_unlock(&opp_table_lock);
+ dev_pm_opp_put_opp_table(opp_table);
- return ret;
+ return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_set_supported_hw);
/**
* dev_pm_opp_put_supported_hw() - Releases resources blocked for supported hw
- * @dev: Device for which supported-hw has to be put.
+ * @opp_table: OPP table returned by dev_pm_opp_set_supported_hw().
*
* This is required only for the V2 bindings, and is called for a matching
* dev_pm_opp_set_supported_hw(). Until this is called, the opp_table structure
* will not be freed.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
*/
-void dev_pm_opp_put_supported_hw(struct device *dev)
+void dev_pm_opp_put_supported_hw(struct opp_table *opp_table)
{
- struct opp_table *opp_table;
-
- /* Hold our table modification lock here */
- mutex_lock(&opp_table_lock);
-
- /* Check for existing table for 'dev' first */
- opp_table = _find_opp_table(dev);
- if (IS_ERR(opp_table)) {
- dev_err(dev, "Failed to find opp_table: %ld\n",
- PTR_ERR(opp_table));
- goto unlock;
- }
-
/* Make sure there are no concurrent readers while updating opp_table */
WARN_ON(!list_empty(&opp_table->opp_list));
if (!opp_table->supported_hw) {
- dev_err(dev, "%s: Doesn't have supported hardware list\n",
- __func__);
- goto unlock;
+ pr_err("%s: Doesn't have supported hardware list\n",
+ __func__);
+ return;
}
kfree(opp_table->supported_hw);
opp_table->supported_hw = NULL;
opp_table->supported_hw_count = 0;
- /* Try freeing opp_table if this was the last blocking resource */
- _remove_opp_table(opp_table);
-
-unlock:
- mutex_unlock(&opp_table_lock);
+ dev_pm_opp_put_opp_table(opp_table);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw);
@@ -1314,26 +1173,15 @@
* specify the extn to be used for certain property names. The properties to
* which the extension will apply are opp-microvolt and opp-microamp. OPP core
* should postfix the property name with -<name> while looking for them.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
*/
-int dev_pm_opp_set_prop_name(struct device *dev, const char *name)
+struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name)
{
struct opp_table *opp_table;
- int ret = 0;
+ int ret;
- /* Hold our table modification lock here */
- mutex_lock(&opp_table_lock);
-
- opp_table = _add_opp_table(dev);
- if (!opp_table) {
- ret = -ENOMEM;
- goto unlock;
- }
+ opp_table = dev_pm_opp_get_opp_table(dev);
+ if (!opp_table)
+ return ERR_PTR(-ENOMEM);
/* Make sure there are no concurrent readers while updating opp_table */
WARN_ON(!list_empty(&opp_table->opp_list));
@@ -1352,63 +1200,37 @@
goto err;
}
- mutex_unlock(&opp_table_lock);
- return 0;
+ return opp_table;
err:
- _remove_opp_table(opp_table);
-unlock:
- mutex_unlock(&opp_table_lock);
+ dev_pm_opp_put_opp_table(opp_table);
- return ret;
+ return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_set_prop_name);
/**
* dev_pm_opp_put_prop_name() - Releases resources blocked for prop-name
- * @dev: Device for which the prop-name has to be put.
+ * @opp_table: OPP table returned by dev_pm_opp_set_prop_name().
*
* This is required only for the V2 bindings, and is called for a matching
* dev_pm_opp_set_prop_name(). Until this is called, the opp_table structure
* will not be freed.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
*/
-void dev_pm_opp_put_prop_name(struct device *dev)
+void dev_pm_opp_put_prop_name(struct opp_table *opp_table)
{
- struct opp_table *opp_table;
-
- /* Hold our table modification lock here */
- mutex_lock(&opp_table_lock);
-
- /* Check for existing table for 'dev' first */
- opp_table = _find_opp_table(dev);
- if (IS_ERR(opp_table)) {
- dev_err(dev, "Failed to find opp_table: %ld\n",
- PTR_ERR(opp_table));
- goto unlock;
- }
-
/* Make sure there are no concurrent readers while updating opp_table */
WARN_ON(!list_empty(&opp_table->opp_list));
if (!opp_table->prop_name) {
- dev_err(dev, "%s: Doesn't have a prop-name\n", __func__);
- goto unlock;
+ pr_err("%s: Doesn't have a prop-name\n", __func__);
+ return;
}
kfree(opp_table->prop_name);
opp_table->prop_name = NULL;
- /* Try freeing opp_table if this was the last blocking resource */
- _remove_opp_table(opp_table);
-
-unlock:
- mutex_unlock(&opp_table_lock);
+ dev_pm_opp_put_opp_table(opp_table);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name);
@@ -1455,12 +1277,6 @@
* well.
*
* This must be called before any OPPs are initialized for the device.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
*/
struct opp_table *dev_pm_opp_set_regulators(struct device *dev,
const char * const names[],
@@ -1470,13 +1286,9 @@
struct regulator *reg;
int ret, i;
- mutex_lock(&opp_table_lock);
-
- opp_table = _add_opp_table(dev);
- if (!opp_table) {
- ret = -ENOMEM;
- goto unlock;
- }
+ opp_table = dev_pm_opp_get_opp_table(dev);
+ if (!opp_table)
+ return ERR_PTR(-ENOMEM);
/* This should be called before OPPs are initialized */
if (WARN_ON(!list_empty(&opp_table->opp_list))) {
@@ -1518,7 +1330,6 @@
if (ret)
goto free_regulators;
- mutex_unlock(&opp_table_lock);
return opp_table;
free_regulators:
@@ -1529,9 +1340,7 @@
opp_table->regulators = NULL;
opp_table->regulator_count = 0;
err:
- _remove_opp_table(opp_table);
-unlock:
- mutex_unlock(&opp_table_lock);
+ dev_pm_opp_put_opp_table(opp_table);
return ERR_PTR(ret);
}
@@ -1540,22 +1349,14 @@
/**
* dev_pm_opp_put_regulators() - Releases resources blocked for regulator
* @opp_table: OPP table returned from dev_pm_opp_set_regulators().
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
*/
void dev_pm_opp_put_regulators(struct opp_table *opp_table)
{
int i;
- mutex_lock(&opp_table_lock);
-
if (!opp_table->regulators) {
pr_err("%s: Doesn't have regulators set\n", __func__);
- goto unlock;
+ return;
}
/* Make sure there are no concurrent readers while updating opp_table */
@@ -1570,11 +1371,7 @@
opp_table->regulators = NULL;
opp_table->regulator_count = 0;
- /* Try freeing opp_table if this was the last blocking resource */
- _remove_opp_table(opp_table);
-
-unlock:
- mutex_unlock(&opp_table_lock);
+ dev_pm_opp_put_opp_table(opp_table);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulators);
@@ -1587,29 +1384,19 @@
* regulators per device), instead of the generic OPP set rate helper.
*
* This must be called before any OPPs are initialized for the device.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
*/
-int dev_pm_opp_register_set_opp_helper(struct device *dev,
+struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev,
int (*set_opp)(struct dev_pm_set_opp_data *data))
{
struct opp_table *opp_table;
int ret;
if (!set_opp)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
- mutex_lock(&opp_table_lock);
-
- opp_table = _add_opp_table(dev);
- if (!opp_table) {
- ret = -ENOMEM;
- goto unlock;
- }
+ opp_table = dev_pm_opp_get_opp_table(dev);
+ if (!opp_table)
+ return ERR_PTR(-ENOMEM);
/* This should be called before OPPs are initialized */
if (WARN_ON(!list_empty(&opp_table->opp_list))) {
@@ -1625,47 +1412,28 @@
opp_table->set_opp = set_opp;
- mutex_unlock(&opp_table_lock);
- return 0;
+ return opp_table;
err:
- _remove_opp_table(opp_table);
-unlock:
- mutex_unlock(&opp_table_lock);
+ dev_pm_opp_put_opp_table(opp_table);
- return ret;
+ return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_register_set_opp_helper);
/**
* dev_pm_opp_register_put_opp_helper() - Releases resources blocked for
* set_opp helper
- * @dev: Device for which custom set_opp helper has to be cleared.
+ * @opp_table: OPP table returned from dev_pm_opp_register_set_opp_helper().
*
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
+ * Release resources blocked for platform specific set_opp helper.
*/
-void dev_pm_opp_register_put_opp_helper(struct device *dev)
+void dev_pm_opp_register_put_opp_helper(struct opp_table *opp_table)
{
- struct opp_table *opp_table;
-
- mutex_lock(&opp_table_lock);
-
- /* Check for existing table for 'dev' first */
- opp_table = _find_opp_table(dev);
- if (IS_ERR(opp_table)) {
- dev_err(dev, "Failed to find opp_table: %ld\n",
- PTR_ERR(opp_table));
- goto unlock;
- }
-
if (!opp_table->set_opp) {
- dev_err(dev, "%s: Doesn't have custom set_opp helper set\n",
- __func__);
- goto unlock;
+ pr_err("%s: Doesn't have custom set_opp helper set\n",
+ __func__);
+ return;
}
/* Make sure there are no concurrent readers while updating opp_table */
@@ -1673,11 +1441,7 @@
opp_table->set_opp = NULL;
- /* Try freeing opp_table if this was the last blocking resource */
- _remove_opp_table(opp_table);
-
-unlock:
- mutex_unlock(&opp_table_lock);
+ dev_pm_opp_put_opp_table(opp_table);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_register_put_opp_helper);
@@ -1691,12 +1455,6 @@
* The opp is made available by default and it can be controlled using
* dev_pm_opp_enable/disable functions.
*
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
- *
* Return:
* 0 On success OR
* Duplicate OPPs (both freq and volt are same) and opp->available
@@ -1706,7 +1464,17 @@
*/
int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
{
- return _opp_add_v1(dev, freq, u_volt, true);
+ struct opp_table *opp_table;
+ int ret;
+
+ opp_table = dev_pm_opp_get_opp_table(dev);
+ if (!opp_table)
+ return -ENOMEM;
+
+ ret = _opp_add_v1(opp_table, dev, freq, u_volt, true);
+
+ dev_pm_opp_put_opp_table(opp_table);
+ return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_add);
@@ -1716,41 +1484,30 @@
* @freq: OPP frequency to modify availability
* @availability_req: availability status requested for this opp
*
- * Set the availability of an OPP with an RCU operation, opp_{enable,disable}
- * share a common logic which is isolated here.
+ * Set the availability of an OPP, opp_{enable,disable} share a common logic
+ * which is isolated here.
*
* Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the
* copy operation, returns 0 if no modification was done OR modification was
* successful.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks to
- * keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex locking or synchronize_rcu() blocking calls cannot be used.
*/
static int _opp_set_availability(struct device *dev, unsigned long freq,
bool availability_req)
{
struct opp_table *opp_table;
- struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV);
+ struct dev_pm_opp *tmp_opp, *opp = ERR_PTR(-ENODEV);
int r = 0;
- /* keep the node allocated */
- new_opp = kmalloc(sizeof(*new_opp), GFP_KERNEL);
- if (!new_opp)
- return -ENOMEM;
-
- mutex_lock(&opp_table_lock);
-
/* Find the opp_table */
opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table)) {
r = PTR_ERR(opp_table);
dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r);
- goto unlock;
+ return r;
}
+ mutex_lock(&opp_table->lock);
+
/* Do we have the frequency? */
list_for_each_entry(tmp_opp, &opp_table->opp_list, node) {
if (tmp_opp->rate == freq) {
@@ -1758,6 +1515,7 @@
break;
}
}
+
if (IS_ERR(opp)) {
r = PTR_ERR(opp);
goto unlock;
@@ -1766,29 +1524,20 @@
/* Is update really needed? */
if (opp->available == availability_req)
goto unlock;
- /* copy the old data over */
- *new_opp = *opp;
- /* plug in new node */
- new_opp->available = availability_req;
-
- list_replace_rcu(&opp->node, &new_opp->node);
- mutex_unlock(&opp_table_lock);
- call_srcu(&opp_table->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu);
+ opp->available = availability_req;
/* Notify the change of the OPP availability */
if (availability_req)
- srcu_notifier_call_chain(&opp_table->srcu_head,
- OPP_EVENT_ENABLE, new_opp);
+ blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ENABLE,
+ opp);
else
- srcu_notifier_call_chain(&opp_table->srcu_head,
- OPP_EVENT_DISABLE, new_opp);
-
- return 0;
+ blocking_notifier_call_chain(&opp_table->head,
+ OPP_EVENT_DISABLE, opp);
unlock:
- mutex_unlock(&opp_table_lock);
- kfree(new_opp);
+ mutex_unlock(&opp_table->lock);
+ dev_pm_opp_put_opp_table(opp_table);
return r;
}
@@ -1801,12 +1550,6 @@
* corresponding error value. It is meant to be used for users an OPP available
* after being temporarily made unavailable with dev_pm_opp_disable.
*
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function indirectly uses RCU and mutex locks to keep the
- * integrity of the internal data structures. Callers should ensure that
- * this function is *NOT* called under RCU protection or in contexts where
- * mutex locking or synchronize_rcu() blocking calls cannot be used.
- *
* Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the
* copy operation, returns 0 if no modification was done OR modification was
* successful.
@@ -1827,12 +1570,6 @@
* control by users to make this OPP not available until the circumstances are
* right to make it available again (with a call to dev_pm_opp_enable).
*
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function indirectly uses RCU and mutex locks to keep the
- * integrity of the internal data structures. Callers should ensure that
- * this function is *NOT* called under RCU protection or in contexts where
- * mutex locking or synchronize_rcu() blocking calls cannot be used.
- *
* Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the
* copy operation, returns 0 if no modification was done OR modification was
* successful.
@@ -1844,41 +1581,78 @@
EXPORT_SYMBOL_GPL(dev_pm_opp_disable);
/**
- * dev_pm_opp_get_notifier() - find notifier_head of the device with opp
- * @dev: device pointer used to lookup OPP table.
+ * dev_pm_opp_register_notifier() - Register OPP notifier for the device
+ * @dev: Device for which notifier needs to be registered
+ * @nb: Notifier block to be registered
*
- * Return: pointer to notifier head if found, otherwise -ENODEV or
- * -EINVAL based on type of error casted as pointer. value must be checked
- * with IS_ERR to determine valid pointer or error result.
- *
- * Locking: This function must be called under rcu_read_lock(). opp_table is a
- * RCU protected pointer. The reason for the same is that the opp pointer which
- * is returned will remain valid for use with opp_get_{voltage, freq} only while
- * under the locked area. The pointer returned must be used prior to unlocking
- * with rcu_read_unlock() to maintain the integrity of the pointer.
+ * Return: 0 on success or a negative error value.
*/
-struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev)
+int dev_pm_opp_register_notifier(struct device *dev, struct notifier_block *nb)
{
- struct opp_table *opp_table = _find_opp_table(dev);
+ struct opp_table *opp_table;
+ int ret;
+ opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table))
- return ERR_CAST(opp_table); /* matching type */
+ return PTR_ERR(opp_table);
- return &opp_table->srcu_head;
+ ret = blocking_notifier_chain_register(&opp_table->head, nb);
+
+ dev_pm_opp_put_opp_table(opp_table);
+
+ return ret;
}
-EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier);
+EXPORT_SYMBOL(dev_pm_opp_register_notifier);
+
+/**
+ * dev_pm_opp_unregister_notifier() - Unregister OPP notifier for the device
+ * @dev: Device for which notifier needs to be unregistered
+ * @nb: Notifier block to be unregistered
+ *
+ * Return: 0 on success or a negative error value.
+ */
+int dev_pm_opp_unregister_notifier(struct device *dev,
+ struct notifier_block *nb)
+{
+ struct opp_table *opp_table;
+ int ret;
+
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table))
+ return PTR_ERR(opp_table);
+
+ ret = blocking_notifier_chain_unregister(&opp_table->head, nb);
+
+ dev_pm_opp_put_opp_table(opp_table);
+
+ return ret;
+}
+EXPORT_SYMBOL(dev_pm_opp_unregister_notifier);
/*
* Free OPPs either created using static entries present in DT or even the
* dynamically added entries based on remove_all param.
*/
-void _dev_pm_opp_remove_table(struct device *dev, bool remove_all)
+void _dev_pm_opp_remove_table(struct opp_table *opp_table, struct device *dev,
+ bool remove_all)
{
- struct opp_table *opp_table;
struct dev_pm_opp *opp, *tmp;
- /* Hold our table modification lock here */
- mutex_lock(&opp_table_lock);
+ /* Find if opp_table manages a single device */
+ if (list_is_singular(&opp_table->dev_list)) {
+ /* Free static OPPs */
+ list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) {
+ if (remove_all || !opp->dynamic)
+ dev_pm_opp_put(opp);
+ }
+ } else {
+ _remove_opp_dev(_find_opp_dev(dev, opp_table), opp_table);
+ }
+}
+
+void _dev_pm_opp_find_and_remove_table(struct device *dev, bool remove_all)
+{
+ struct opp_table *opp_table;
/* Check for existing table for 'dev' */
opp_table = _find_opp_table(dev);
@@ -1890,22 +1664,12 @@
IS_ERR_OR_NULL(dev) ?
"Invalid device" : dev_name(dev),
error);
- goto unlock;
+ return;
}
- /* Find if opp_table manages a single device */
- if (list_is_singular(&opp_table->dev_list)) {
- /* Free static OPPs */
- list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) {
- if (remove_all || !opp->dynamic)
- _opp_remove(opp_table, opp, true);
- }
- } else {
- _remove_opp_dev(_find_opp_dev(dev, opp_table), opp_table);
- }
+ _dev_pm_opp_remove_table(opp_table, dev, remove_all);
-unlock:
- mutex_unlock(&opp_table_lock);
+ dev_pm_opp_put_opp_table(opp_table);
}
/**
@@ -1914,15 +1678,9 @@
*
* Free both OPPs created using static entries present in DT and the
* dynamically added entries.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function indirectly uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
*/
void dev_pm_opp_remove_table(struct device *dev)
{
- _dev_pm_opp_remove_table(dev, true);
+ _dev_pm_opp_find_and_remove_table(dev, true);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_remove_table);
diff --git a/drivers/base/power/opp/cpu.c b/drivers/base/power/opp/cpu.c
index 8c3434b..2d87bc1 100644
--- a/drivers/base/power/opp/cpu.c
+++ b/drivers/base/power/opp/cpu.c
@@ -42,11 +42,6 @@
*
* WARNING: It is important for the callers to ensure refreshing their copy of
* the table if any of the mentioned functions have been invoked in the interim.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Since we just use the regular accessor functions to access the internal data
- * structures, we use RCU read lock inside this function. As a result, users of
- * this function DONOT need to use explicit locks for invoking.
*/
int dev_pm_opp_init_cpufreq_table(struct device *dev,
struct cpufreq_frequency_table **table)
@@ -56,19 +51,13 @@
int i, max_opps, ret = 0;
unsigned long rate;
- rcu_read_lock();
-
max_opps = dev_pm_opp_get_opp_count(dev);
- if (max_opps <= 0) {
- ret = max_opps ? max_opps : -ENODATA;
- goto out;
- }
+ if (max_opps <= 0)
+ return max_opps ? max_opps : -ENODATA;
freq_table = kcalloc((max_opps + 1), sizeof(*freq_table), GFP_ATOMIC);
- if (!freq_table) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!freq_table)
+ return -ENOMEM;
for (i = 0, rate = 0; i < max_opps; i++, rate++) {
/* find next rate */
@@ -83,6 +72,8 @@
/* Is Boost/turbo opp ? */
if (dev_pm_opp_is_turbo(opp))
freq_table[i].flags = CPUFREQ_BOOST_FREQ;
+
+ dev_pm_opp_put(opp);
}
freq_table[i].driver_data = i;
@@ -91,7 +82,6 @@
*table = &freq_table[0];
out:
- rcu_read_unlock();
if (ret)
kfree(freq_table);
@@ -147,12 +137,6 @@
* This removes the OPP tables for CPUs present in the @cpumask.
* This should be used to remove all the OPPs entries associated with
* the cpus in @cpumask.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
*/
void dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask)
{
@@ -169,12 +153,6 @@
* @cpumask.
*
* Returns -ENODEV if OPP table isn't already present.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
*/
int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev,
const struct cpumask *cpumask)
@@ -184,13 +162,9 @@
struct device *dev;
int cpu, ret = 0;
- mutex_lock(&opp_table_lock);
-
opp_table = _find_opp_table(cpu_dev);
- if (IS_ERR(opp_table)) {
- ret = PTR_ERR(opp_table);
- goto unlock;
- }
+ if (IS_ERR(opp_table))
+ return PTR_ERR(opp_table);
for_each_cpu(cpu, cpumask) {
if (cpu == cpu_dev->id)
@@ -213,8 +187,8 @@
/* Mark opp-table as multiple CPUs are sharing it now */
opp_table->shared_opp = OPP_TABLE_ACCESS_SHARED;
}
-unlock:
- mutex_unlock(&opp_table_lock);
+
+ dev_pm_opp_put_opp_table(opp_table);
return ret;
}
@@ -229,12 +203,6 @@
*
* Returns -ENODEV if OPP table isn't already present and -EINVAL if the OPP
* table's status is access-unknown.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
*/
int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
{
@@ -242,17 +210,13 @@
struct opp_table *opp_table;
int ret = 0;
- mutex_lock(&opp_table_lock);
-
opp_table = _find_opp_table(cpu_dev);
- if (IS_ERR(opp_table)) {
- ret = PTR_ERR(opp_table);
- goto unlock;
- }
+ if (IS_ERR(opp_table))
+ return PTR_ERR(opp_table);
if (opp_table->shared_opp == OPP_TABLE_ACCESS_UNKNOWN) {
ret = -EINVAL;
- goto unlock;
+ goto put_opp_table;
}
cpumask_clear(cpumask);
@@ -264,8 +228,8 @@
cpumask_set_cpu(cpu_dev->id, cpumask);
}
-unlock:
- mutex_unlock(&opp_table_lock);
+put_opp_table:
+ dev_pm_opp_put_opp_table(opp_table);
return ret;
}
diff --git a/drivers/base/power/opp/of.c b/drivers/base/power/opp/of.c
index 3f7d259..7794286 100644
--- a/drivers/base/power/opp/of.c
+++ b/drivers/base/power/opp/of.c
@@ -24,9 +24,11 @@
static struct opp_table *_managed_opp(const struct device_node *np)
{
- struct opp_table *opp_table;
+ struct opp_table *opp_table, *managed_table = NULL;
- list_for_each_entry_rcu(opp_table, &opp_tables, node) {
+ mutex_lock(&opp_table_lock);
+
+ list_for_each_entry(opp_table, &opp_tables, node) {
if (opp_table->np == np) {
/*
* Multiple devices can point to the same OPP table and
@@ -35,14 +37,18 @@
* But the OPPs will be considered as shared only if the
* OPP table contains a "opp-shared" property.
*/
- if (opp_table->shared_opp == OPP_TABLE_ACCESS_SHARED)
- return opp_table;
+ if (opp_table->shared_opp == OPP_TABLE_ACCESS_SHARED) {
+ _get_opp_table_kref(opp_table);
+ managed_table = opp_table;
+ }
- return NULL;
+ break;
}
}
- return NULL;
+ mutex_unlock(&opp_table_lock);
+
+ return managed_table;
}
void _of_init_opp_table(struct opp_table *opp_table, struct device *dev)
@@ -229,34 +235,28 @@
* @dev: device pointer used to lookup OPP table.
*
* Free OPPs created using static entries present in DT.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function indirectly uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
*/
void dev_pm_opp_of_remove_table(struct device *dev)
{
- _dev_pm_opp_remove_table(dev, false);
+ _dev_pm_opp_find_and_remove_table(dev, false);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table);
/* Returns opp descriptor node for a device, caller must do of_node_put() */
-static struct device_node *_of_get_opp_desc_node(struct device *dev)
+struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev)
{
/*
- * TODO: Support for multiple OPP tables.
- *
* There should be only ONE phandle present in "operating-points-v2"
* property.
*/
return of_parse_phandle(dev->of_node, "operating-points-v2", 0);
}
+EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_opp_desc_node);
/**
* _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings)
+ * @opp_table: OPP table
* @dev: device for which we do this operation
* @np: device node
*
@@ -264,12 +264,6 @@
* opp can be controlled using dev_pm_opp_enable/disable functions and may be
* removed by dev_pm_opp_remove.
*
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
- *
* Return:
* 0 On success OR
* Duplicate OPPs (both freq and volt are same) and opp->available
@@ -278,22 +272,17 @@
* -ENOMEM Memory allocation failure
* -EINVAL Failed parsing the OPP node
*/
-static int _opp_add_static_v2(struct device *dev, struct device_node *np)
+static int _opp_add_static_v2(struct opp_table *opp_table, struct device *dev,
+ struct device_node *np)
{
- struct opp_table *opp_table;
struct dev_pm_opp *new_opp;
u64 rate;
u32 val;
int ret;
- /* Hold our table modification lock here */
- mutex_lock(&opp_table_lock);
-
- new_opp = _allocate_opp(dev, &opp_table);
- if (!new_opp) {
- ret = -ENOMEM;
- goto unlock;
- }
+ new_opp = _opp_allocate(opp_table);
+ if (!new_opp)
+ return -ENOMEM;
ret = of_property_read_u64(np, "opp-hz", &rate);
if (ret < 0) {
@@ -327,8 +316,12 @@
goto free_opp;
ret = _opp_add(dev, new_opp, opp_table);
- if (ret)
+ if (ret) {
+ /* Don't return error for duplicate OPPs */
+ if (ret == -EBUSY)
+ ret = 0;
goto free_opp;
+ }
/* OPP to select on device suspend */
if (of_property_read_bool(np, "opp-suspend")) {
@@ -345,8 +338,6 @@
if (new_opp->clock_latency_ns > opp_table->clock_latency_ns_max)
opp_table->clock_latency_ns_max = new_opp->clock_latency_ns;
- mutex_unlock(&opp_table_lock);
-
pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu\n",
__func__, new_opp->turbo, new_opp->rate,
new_opp->supplies[0].u_volt, new_opp->supplies[0].u_volt_min,
@@ -356,13 +347,12 @@
* Notify the changes in the availability of the operable
* frequency/voltage list.
*/
- srcu_notifier_call_chain(&opp_table->srcu_head, OPP_EVENT_ADD, new_opp);
+ blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADD, new_opp);
return 0;
free_opp:
- _opp_remove(opp_table, new_opp, false);
-unlock:
- mutex_unlock(&opp_table_lock);
+ _opp_free(new_opp);
+
return ret;
}
@@ -373,41 +363,35 @@
struct opp_table *opp_table;
int ret = 0, count = 0;
- mutex_lock(&opp_table_lock);
-
opp_table = _managed_opp(opp_np);
if (opp_table) {
/* OPPs are already managed */
if (!_add_opp_dev(dev, opp_table))
ret = -ENOMEM;
- mutex_unlock(&opp_table_lock);
- return ret;
+ goto put_opp_table;
}
- mutex_unlock(&opp_table_lock);
+
+ opp_table = dev_pm_opp_get_opp_table(dev);
+ if (!opp_table)
+ return -ENOMEM;
/* We have opp-table node now, iterate over it and add OPPs */
for_each_available_child_of_node(opp_np, np) {
count++;
- ret = _opp_add_static_v2(dev, np);
+ ret = _opp_add_static_v2(opp_table, dev, np);
if (ret) {
dev_err(dev, "%s: Failed to add OPP, %d\n", __func__,
ret);
- goto free_table;
+ _dev_pm_opp_remove_table(opp_table, dev, false);
+ goto put_opp_table;
}
}
/* There should be one of more OPP defined */
- if (WARN_ON(!count))
- return -ENOENT;
-
- mutex_lock(&opp_table_lock);
-
- opp_table = _find_opp_table(dev);
- if (WARN_ON(IS_ERR(opp_table))) {
- ret = PTR_ERR(opp_table);
- mutex_unlock(&opp_table_lock);
- goto free_table;
+ if (WARN_ON(!count)) {
+ ret = -ENOENT;
+ goto put_opp_table;
}
opp_table->np = opp_np;
@@ -416,12 +400,8 @@
else
opp_table->shared_opp = OPP_TABLE_ACCESS_EXCLUSIVE;
- mutex_unlock(&opp_table_lock);
-
- return 0;
-
-free_table:
- dev_pm_opp_of_remove_table(dev);
+put_opp_table:
+ dev_pm_opp_put_opp_table(opp_table);
return ret;
}
@@ -429,9 +409,10 @@
/* Initializes OPP tables based on old-deprecated bindings */
static int _of_add_opp_table_v1(struct device *dev)
{
+ struct opp_table *opp_table;
const struct property *prop;
const __be32 *val;
- int nr;
+ int nr, ret = 0;
prop = of_find_property(dev->of_node, "operating-points", NULL);
if (!prop)
@@ -449,18 +430,27 @@
return -EINVAL;
}
+ opp_table = dev_pm_opp_get_opp_table(dev);
+ if (!opp_table)
+ return -ENOMEM;
+
val = prop->value;
while (nr) {
unsigned long freq = be32_to_cpup(val++) * 1000;
unsigned long volt = be32_to_cpup(val++);
- if (_opp_add_v1(dev, freq, volt, false))
- dev_warn(dev, "%s: Failed to add OPP %ld\n",
- __func__, freq);
+ ret = _opp_add_v1(opp_table, dev, freq, volt, false);
+ if (ret) {
+ dev_err(dev, "%s: Failed to add OPP %ld (%d)\n",
+ __func__, freq, ret);
+ _dev_pm_opp_remove_table(opp_table, dev, false);
+ break;
+ }
nr -= 2;
}
- return 0;
+ dev_pm_opp_put_opp_table(opp_table);
+ return ret;
}
/**
@@ -469,12 +459,6 @@
*
* Register the initial OPP table with the OPP library for given device.
*
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function indirectly uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
- *
* Return:
* 0 On success OR
* Duplicate OPPs (both freq and volt are same) and opp->available
@@ -495,7 +479,7 @@
* OPPs have two version of bindings now. The older one is deprecated,
* try for the new binding first.
*/
- opp_np = _of_get_opp_desc_node(dev);
+ opp_np = dev_pm_opp_of_get_opp_desc_node(dev);
if (!opp_np) {
/*
* Try old-deprecated bindings for backward compatibility with
@@ -519,12 +503,6 @@
*
* This removes the OPP tables for CPUs present in the @cpumask.
* This should be used only to remove static entries created from DT.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
*/
void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpumask)
{
@@ -537,12 +515,6 @@
* @cpumask: cpumask for which OPP table needs to be added.
*
* This adds the OPP tables for CPUs present in the @cpumask.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
*/
int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask)
{
@@ -590,12 +562,6 @@
* This updates the @cpumask with CPUs that are sharing OPPs with @cpu_dev.
*
* Returns -ENOENT if operating-points-v2 isn't present for @cpu_dev.
- *
- * Locking: The internal opp_table and opp structures are RCU protected.
- * Hence this function internally uses RCU updater strategy with mutex locks
- * to keep the integrity of the internal data structures. Callers should ensure
- * that this function is *NOT* called under RCU protection or in contexts where
- * mutex cannot be locked.
*/
int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev,
struct cpumask *cpumask)
@@ -605,7 +571,7 @@
int cpu, ret = 0;
/* Get OPP descriptor node */
- np = _of_get_opp_desc_node(cpu_dev);
+ np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
if (!np) {
dev_dbg(cpu_dev, "%s: Couldn't find opp node.\n", __func__);
return -ENOENT;
@@ -630,7 +596,7 @@
}
/* Get OPP descriptor node */
- tmp_np = _of_get_opp_desc_node(tcpu_dev);
+ tmp_np = dev_pm_opp_of_get_opp_desc_node(tcpu_dev);
if (!tmp_np) {
dev_err(tcpu_dev, "%s: Couldn't find opp node.\n",
__func__);
diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h
index af9f2b8..166eef9 100644
--- a/drivers/base/power/opp/opp.h
+++ b/drivers/base/power/opp/opp.h
@@ -16,11 +16,11 @@
#include <linux/device.h>
#include <linux/kernel.h>
+#include <linux/kref.h>
#include <linux/list.h>
#include <linux/limits.h>
#include <linux/pm_opp.h>
-#include <linux/rculist.h>
-#include <linux/rcupdate.h>
+#include <linux/notifier.h>
struct clk;
struct regulator;
@@ -51,11 +51,9 @@
* @node: opp table node. The nodes are maintained throughout the lifetime
* of boot. It is expected only an optimal set of OPPs are
* added to the library by the SoC framework.
- * RCU usage: opp table is traversed with RCU locks. node
- * modification is possible realtime, hence the modifications
- * are protected by the opp_table_lock for integrity.
* IMPORTANT: the opp nodes should be maintained in increasing
* order.
+ * @kref: for reference count of the OPP.
* @available: true/false - marks if this OPP as available or not
* @dynamic: not-created from static DT entries.
* @turbo: true if turbo (boost) OPP
@@ -65,7 +63,6 @@
* @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's
* frequency from any other OPP's frequency.
* @opp_table: points back to the opp_table struct this opp belongs to
- * @rcu_head: RCU callback head used for deferred freeing
* @np: OPP's device node.
* @dentry: debugfs dentry pointer (per opp)
*
@@ -73,6 +70,7 @@
*/
struct dev_pm_opp {
struct list_head node;
+ struct kref kref;
bool available;
bool dynamic;
@@ -85,7 +83,6 @@
unsigned long clock_latency_ns;
struct opp_table *opp_table;
- struct rcu_head rcu_head;
struct device_node *np;
@@ -98,7 +95,6 @@
* struct opp_device - devices managed by 'struct opp_table'
* @node: list node
* @dev: device to which the struct object belongs
- * @rcu_head: RCU callback head used for deferred freeing
* @dentry: debugfs dentry pointer (per device)
*
* This is an internal data structure maintaining the devices that are managed
@@ -107,7 +103,6 @@
struct opp_device {
struct list_head node;
const struct device *dev;
- struct rcu_head rcu_head;
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
@@ -125,12 +120,11 @@
* @node: table node - contains the devices with OPPs that
* have been registered. Nodes once added are not modified in this
* table.
- * RCU usage: nodes are not modified in the table of opp_table,
- * however addition is possible and is secured by opp_table_lock
- * @srcu_head: notifier head to notify the OPP availability changes.
- * @rcu_head: RCU callback head used for deferred freeing
+ * @head: notifier head to notify the OPP availability changes.
* @dev_list: list of devices that share these OPPs
* @opp_list: table of opps
+ * @kref: for reference count of the table.
+ * @lock: mutex protecting the opp_list.
* @np: struct device_node pointer for opp's DT node.
* @clock_latency_ns_max: Max clock latency in nanoseconds.
* @shared_opp: OPP is shared between multiple devices.
@@ -151,18 +145,15 @@
* This is an internal data structure maintaining the link to opps attached to
* a device. This structure is not meant to be shared to users as it is
* meant for book keeping and private to OPP library.
- *
- * Because the opp structures can be used from both rcu and srcu readers, we
- * need to wait for the grace period of both of them before freeing any
- * resources. And so we have used kfree_rcu() from within call_srcu() handlers.
*/
struct opp_table {
struct list_head node;
- struct srcu_notifier_head srcu_head;
- struct rcu_head rcu_head;
+ struct blocking_notifier_head head;
struct list_head dev_list;
struct list_head opp_list;
+ struct kref kref;
+ struct mutex lock;
struct device_node *np;
unsigned long clock_latency_ns_max;
@@ -190,14 +181,17 @@
};
/* Routines internal to opp core */
+void _get_opp_table_kref(struct opp_table *opp_table);
struct opp_table *_find_opp_table(struct device *dev);
struct opp_device *_add_opp_dev(const struct device *dev, struct opp_table *opp_table);
-void _dev_pm_opp_remove_table(struct device *dev, bool remove_all);
-struct dev_pm_opp *_allocate_opp(struct device *dev, struct opp_table **opp_table);
+void _dev_pm_opp_remove_table(struct opp_table *opp_table, struct device *dev, bool remove_all);
+void _dev_pm_opp_find_and_remove_table(struct device *dev, bool remove_all);
+struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table);
+void _opp_free(struct dev_pm_opp *opp);
int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table);
-void _opp_remove(struct opp_table *opp_table, struct dev_pm_opp *opp, bool notify);
-int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt, bool dynamic);
+int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic);
void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, bool of);
+struct opp_table *_add_opp_table(struct device *dev);
#ifdef CONFIG_OF
void _of_init_opp_table(struct opp_table *opp_table, struct device *dev);
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
index 58fcc75..271bec7 100644
--- a/drivers/base/power/qos.c
+++ b/drivers/base/power/qos.c
@@ -17,12 +17,9 @@
*
* This QoS design is best effort based. Dependents register their QoS needs.
* Watchers register to keep track of the current QoS needs of the system.
- * Watchers can register different types of notification callbacks:
- * . a per-device notification callback using the dev_pm_qos_*_notifier API.
- * The notification chain data is stored in the per-device constraint
- * data struct.
- * . a system-wide notification callback using the dev_pm_qos_*_global_notifier
- * API. The notification chain data is stored in a static variable.
+ * Watchers can register a per-device notification callback using the
+ * dev_pm_qos_*_notifier API. The notification chain data is stored in the
+ * per-device constraint data struct.
*
* Note about the per-device constraint data struct allocation:
* . The per-device constraints data struct ptr is tored into the device
@@ -49,8 +46,6 @@
static DEFINE_MUTEX(dev_pm_qos_mtx);
static DEFINE_MUTEX(dev_pm_qos_sysfs_mtx);
-static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
-
/**
* __dev_pm_qos_flags - Check PM QoS flags for a given device.
* @dev: Device to check the PM QoS flags for.
@@ -135,8 +130,7 @@
* @value: Value to assign to the QoS request.
*
* Internal function to update the constraints list using the PM QoS core
- * code and if needed call the per-device and the global notification
- * callbacks
+ * code and if needed call the per-device callbacks.
*/
static int apply_constraint(struct dev_pm_qos_request *req,
enum pm_qos_req_action action, s32 value)
@@ -148,12 +142,6 @@
case DEV_PM_QOS_RESUME_LATENCY:
ret = pm_qos_update_target(&qos->resume_latency,
&req->data.pnode, action, value);
- if (ret) {
- value = pm_qos_read_value(&qos->resume_latency);
- blocking_notifier_call_chain(&dev_pm_notifiers,
- (unsigned long)value,
- req);
- }
break;
case DEV_PM_QOS_LATENCY_TOLERANCE:
ret = pm_qos_update_target(&qos->latency_tolerance,
@@ -281,7 +269,7 @@
dev->power.qos = ERR_PTR(-ENODEV);
spin_unlock_irq(&dev->power.lock);
- kfree(c->notifiers);
+ kfree(qos->resume_latency.notifiers);
kfree(qos);
out:
@@ -536,36 +524,6 @@
EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
/**
- * dev_pm_qos_add_global_notifier - sets notification entry for changes to
- * target value of the PM QoS constraints for any device
- *
- * @notifier: notifier block managed by caller.
- *
- * Will register the notifier into a notification chain that gets called
- * upon changes to the target value for any device.
- */
-int dev_pm_qos_add_global_notifier(struct notifier_block *notifier)
-{
- return blocking_notifier_chain_register(&dev_pm_notifiers, notifier);
-}
-EXPORT_SYMBOL_GPL(dev_pm_qos_add_global_notifier);
-
-/**
- * dev_pm_qos_remove_global_notifier - deletes notification for changes to
- * target value of PM QoS constraints for any device
- *
- * @notifier: notifier block to be removed.
- *
- * Will remove the notifier from the notification chain that gets called
- * upon changes to the target value for any device.
- */
-int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier)
-{
- return blocking_notifier_chain_unregister(&dev_pm_notifiers, notifier);
-}
-EXPORT_SYMBOL_GPL(dev_pm_qos_remove_global_notifier);
-
-/**
* dev_pm_qos_add_ancestor_request - Add PM QoS request for device's ancestor.
* @dev: Device whose ancestor to add the request for.
* @req: Pointer to the preallocated handle.
diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c
index b11af3f..b1e9aae 100644
--- a/drivers/base/regmap/regcache-rbtree.c
+++ b/drivers/base/regmap/regcache-rbtree.c
@@ -81,7 +81,7 @@
node = rbtree_ctx->root.rb_node;
while (node) {
- rbnode = container_of(node, struct regcache_rbtree_node, node);
+ rbnode = rb_entry(node, struct regcache_rbtree_node, node);
regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
&top_reg);
if (reg >= base_reg && reg <= top_reg) {
@@ -108,8 +108,7 @@
parent = NULL;
new = &root->rb_node;
while (*new) {
- rbnode_tmp = container_of(*new, struct regcache_rbtree_node,
- node);
+ rbnode_tmp = rb_entry(*new, struct regcache_rbtree_node, node);
/* base and top registers of the current rbnode */
regcache_rbtree_get_base_top_reg(map, rbnode_tmp, &base_reg_tmp,
&top_reg_tmp);
@@ -152,7 +151,7 @@
for (node = rb_first(&rbtree_ctx->root); node != NULL;
node = rb_next(node)) {
- n = container_of(node, struct regcache_rbtree_node, node);
+ n = rb_entry(node, struct regcache_rbtree_node, node);
mem_size += sizeof(*n);
mem_size += (n->blklen * map->cache_word_size);
mem_size += BITS_TO_LONGS(n->blklen) * sizeof(long);
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 4e58256..b0a0dcf 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -224,7 +224,7 @@
}
/**
- * regcache_read: Fetch the value of a given register from the cache.
+ * regcache_read - Fetch the value of a given register from the cache.
*
* @map: map to configure.
* @reg: The register index.
@@ -255,7 +255,7 @@
}
/**
- * regcache_write: Set the value of a given register in the cache.
+ * regcache_write - Set the value of a given register in the cache.
*
* @map: map to configure.
* @reg: The register index.
@@ -328,7 +328,7 @@
}
/**
- * regcache_sync: Sync the register cache with the hardware.
+ * regcache_sync - Sync the register cache with the hardware.
*
* @map: map to configure.
*
@@ -396,7 +396,7 @@
EXPORT_SYMBOL_GPL(regcache_sync);
/**
- * regcache_sync_region: Sync part of the register cache with the hardware.
+ * regcache_sync_region - Sync part of the register cache with the hardware.
*
* @map: map to sync.
* @min: first register to sync
@@ -452,7 +452,7 @@
EXPORT_SYMBOL_GPL(regcache_sync_region);
/**
- * regcache_drop_region: Discard part of the register cache
+ * regcache_drop_region - Discard part of the register cache
*
* @map: map to operate on
* @min: first register to discard
@@ -483,10 +483,10 @@
EXPORT_SYMBOL_GPL(regcache_drop_region);
/**
- * regcache_cache_only: Put a register map into cache only mode
+ * regcache_cache_only - Put a register map into cache only mode
*
* @map: map to configure
- * @cache_only: flag if changes should be written to the hardware
+ * @enable: flag if changes should be written to the hardware
*
* When a register map is marked as cache only writes to the register
* map API will only update the register cache, they will not cause
@@ -505,7 +505,7 @@
EXPORT_SYMBOL_GPL(regcache_cache_only);
/**
- * regcache_mark_dirty: Indicate that HW registers were reset to default values
+ * regcache_mark_dirty - Indicate that HW registers were reset to default values
*
* @map: map to mark
*
@@ -527,10 +527,10 @@
EXPORT_SYMBOL_GPL(regcache_mark_dirty);
/**
- * regcache_cache_bypass: Put a register map into cache bypass mode
+ * regcache_cache_bypass - Put a register map into cache bypass mode
*
* @map: map to configure
- * @cache_bypass: flag if changes should not be written to the cache
+ * @enable: flag if changes should not be written to the cache
*
* When a register map is marked with the cache bypass option, writes
* to the register map API will only update the hardware and not the
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index ec26247..cd54189 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -398,13 +398,14 @@
};
/**
- * regmap_add_irq_chip(): Use standard regmap IRQ controller handling
+ * regmap_add_irq_chip() - Use standard regmap IRQ controller handling
*
- * map: The regmap for the device.
- * irq: The IRQ the device uses to signal interrupts
- * irq_flags: The IRQF_ flags to use for the primary interrupt.
- * chip: Configuration for the interrupt controller.
- * data: Runtime data structure for the controller, allocated on success
+ * @map: The regmap for the device.
+ * @irq: The IRQ the device uses to signal interrupts.
+ * @irq_flags: The IRQF_ flags to use for the primary interrupt.
+ * @irq_base: Allocate at specific IRQ number if irq_base > 0.
+ * @chip: Configuration for the interrupt controller.
+ * @data: Runtime data structure for the controller, allocated on success.
*
* Returns 0 on success or an errno on failure.
*
@@ -659,12 +660,12 @@
EXPORT_SYMBOL_GPL(regmap_add_irq_chip);
/**
- * regmap_del_irq_chip(): Stop interrupt handling for a regmap IRQ chip
+ * regmap_del_irq_chip() - Stop interrupt handling for a regmap IRQ chip
*
* @irq: Primary IRQ for the device
- * @d: regmap_irq_chip_data allocated by regmap_add_irq_chip()
+ * @d: ®map_irq_chip_data allocated by regmap_add_irq_chip()
*
- * This function also dispose all mapped irq on chip.
+ * This function also disposes of all mapped IRQs on the chip.
*/
void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
{
@@ -723,18 +724,19 @@
}
/**
- * devm_regmap_add_irq_chip(): Resource manager regmap_add_irq_chip()
+ * devm_regmap_add_irq_chip() - Resource manager regmap_add_irq_chip()
*
- * @dev: The device pointer on which irq_chip belongs to.
- * @map: The regmap for the device.
- * @irq: The IRQ the device uses to signal interrupts
+ * @dev: The device pointer on which irq_chip belongs to.
+ * @map: The regmap for the device.
+ * @irq: The IRQ the device uses to signal interrupts
* @irq_flags: The IRQF_ flags to use for the primary interrupt.
- * @chip: Configuration for the interrupt controller.
- * @data: Runtime data structure for the controller, allocated on success
+ * @irq_base: Allocate at specific IRQ number if irq_base > 0.
+ * @chip: Configuration for the interrupt controller.
+ * @data: Runtime data structure for the controller, allocated on success
*
* Returns 0 on success or an errno on failure.
*
- * The regmap_irq_chip data automatically be released when the device is
+ * The ®map_irq_chip_data will be automatically released when the device is
* unbound.
*/
int devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq,
@@ -765,11 +767,13 @@
EXPORT_SYMBOL_GPL(devm_regmap_add_irq_chip);
/**
- * devm_regmap_del_irq_chip(): Resource managed regmap_del_irq_chip()
+ * devm_regmap_del_irq_chip() - Resource managed regmap_del_irq_chip()
*
* @dev: Device for which which resource was allocated.
- * @irq: Primary IRQ for the device
- * @d: regmap_irq_chip_data allocated by regmap_add_irq_chip()
+ * @irq: Primary IRQ for the device.
+ * @data: ®map_irq_chip_data allocated by regmap_add_irq_chip().
+ *
+ * A resource managed version of regmap_del_irq_chip().
*/
void devm_regmap_del_irq_chip(struct device *dev, int irq,
struct regmap_irq_chip_data *data)
@@ -786,11 +790,11 @@
EXPORT_SYMBOL_GPL(devm_regmap_del_irq_chip);
/**
- * regmap_irq_chip_get_base(): Retrieve interrupt base for a regmap IRQ chip
+ * regmap_irq_chip_get_base() - Retrieve interrupt base for a regmap IRQ chip
+ *
+ * @data: regmap irq controller to operate on.
*
* Useful for drivers to request their own IRQs.
- *
- * @data: regmap_irq controller to operate on.
*/
int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data)
{
@@ -800,12 +804,12 @@
EXPORT_SYMBOL_GPL(regmap_irq_chip_get_base);
/**
- * regmap_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ
+ * regmap_irq_get_virq() - Map an interrupt on a chip to a virtual IRQ
+ *
+ * @data: regmap irq controller to operate on.
+ * @irq: index of the interrupt requested in the chip IRQs.
*
* Useful for drivers to request their own IRQs.
- *
- * @data: regmap_irq controller to operate on.
- * @irq: index of the interrupt requested in the chip IRQs
*/
int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq)
{
@@ -818,14 +822,14 @@
EXPORT_SYMBOL_GPL(regmap_irq_get_virq);
/**
- * regmap_irq_get_domain(): Retrieve the irq_domain for the chip
+ * regmap_irq_get_domain() - Retrieve the irq_domain for the chip
+ *
+ * @data: regmap_irq controller to operate on.
*
* Useful for drivers to request their own IRQs and for integration
* with subsystems. For ease of integration NULL is accepted as a
* domain, allowing devices to just call this even if no domain is
* allocated.
- *
- * @data: regmap_irq controller to operate on.
*/
struct irq_domain *regmap_irq_get_domain(struct regmap_irq_chip_data *data)
{
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index ae63bb0..b9a779a 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -459,7 +459,7 @@
while (*new) {
struct regmap_range_node *this =
- container_of(*new, struct regmap_range_node, node);
+ rb_entry(*new, struct regmap_range_node, node);
parent = *new;
if (data->range_max < this->range_min)
@@ -483,7 +483,7 @@
while (node) {
struct regmap_range_node *this =
- container_of(node, struct regmap_range_node, node);
+ rb_entry(node, struct regmap_range_node, node);
if (reg < this->range_min)
node = node->rb_left;
@@ -1091,8 +1091,7 @@
}
/**
- * devm_regmap_field_alloc(): Allocate and initialise a register field
- * in a register map.
+ * devm_regmap_field_alloc() - Allocate and initialise a register field.
*
* @dev: Device that will be interacted with
* @regmap: regmap bank in which this register field is located.
@@ -1118,13 +1117,15 @@
EXPORT_SYMBOL_GPL(devm_regmap_field_alloc);
/**
- * devm_regmap_field_free(): Free register field allocated using
- * devm_regmap_field_alloc. Usally drivers need not call this function,
- * as the memory allocated via devm will be freed as per device-driver
- * life-cyle.
+ * devm_regmap_field_free() - Free a register field allocated using
+ * devm_regmap_field_alloc.
*
* @dev: Device that will be interacted with
* @field: regmap field which should be freed.
+ *
+ * Free register field allocated using devm_regmap_field_alloc(). Usually
+ * drivers need not call this function, as the memory allocated via devm
+ * will be freed as per device-driver life-cyle.
*/
void devm_regmap_field_free(struct device *dev,
struct regmap_field *field)
@@ -1134,8 +1135,7 @@
EXPORT_SYMBOL_GPL(devm_regmap_field_free);
/**
- * regmap_field_alloc(): Allocate and initialise a register field
- * in a register map.
+ * regmap_field_alloc() - Allocate and initialise a register field.
*
* @regmap: regmap bank in which this register field is located.
* @reg_field: Register field with in the bank.
@@ -1159,7 +1159,8 @@
EXPORT_SYMBOL_GPL(regmap_field_alloc);
/**
- * regmap_field_free(): Free register field allocated using regmap_field_alloc
+ * regmap_field_free() - Free register field allocated using
+ * regmap_field_alloc.
*
* @field: regmap field which should be freed.
*/
@@ -1170,7 +1171,7 @@
EXPORT_SYMBOL_GPL(regmap_field_free);
/**
- * regmap_reinit_cache(): Reinitialise the current register cache
+ * regmap_reinit_cache() - Reinitialise the current register cache
*
* @map: Register map to operate on.
* @config: New configuration. Only the cache data will be used.
@@ -1205,7 +1206,9 @@
EXPORT_SYMBOL_GPL(regmap_reinit_cache);
/**
- * regmap_exit(): Free a previously allocated register map
+ * regmap_exit() - Free a previously allocated register map
+ *
+ * @map: Register map to operate on.
*/
void regmap_exit(struct regmap *map)
{
@@ -1245,7 +1248,7 @@
}
/**
- * dev_get_regmap(): Obtain the regmap (if any) for a device
+ * dev_get_regmap() - Obtain the regmap (if any) for a device
*
* @dev: Device to retrieve the map for
* @name: Optional name for the register map, usually NULL.
@@ -1268,7 +1271,7 @@
EXPORT_SYMBOL_GPL(dev_get_regmap);
/**
- * regmap_get_device(): Obtain the device from a regmap
+ * regmap_get_device() - Obtain the device from a regmap
*
* @map: Register map to operate on.
*
@@ -1654,7 +1657,7 @@
}
/**
- * regmap_write(): Write a value to a single register
+ * regmap_write() - Write a value to a single register
*
* @map: Register map to write to
* @reg: Register to write to
@@ -1681,7 +1684,7 @@
EXPORT_SYMBOL_GPL(regmap_write);
/**
- * regmap_write_async(): Write a value to a single register asynchronously
+ * regmap_write_async() - Write a value to a single register asynchronously
*
* @map: Register map to write to
* @reg: Register to write to
@@ -1712,7 +1715,7 @@
EXPORT_SYMBOL_GPL(regmap_write_async);
/**
- * regmap_raw_write(): Write raw values to one or more registers
+ * regmap_raw_write() - Write raw values to one or more registers
*
* @map: Register map to write to
* @reg: Initial register to write to
@@ -1750,9 +1753,8 @@
EXPORT_SYMBOL_GPL(regmap_raw_write);
/**
- * regmap_field_update_bits_base():
- * Perform a read/modify/write cycle on the register field
- * with change, async, force option
+ * regmap_field_update_bits_base() - Perform a read/modify/write cycle a
+ * register field.
*
* @field: Register field to write to
* @mask: Bitmask to change
@@ -1761,6 +1763,9 @@
* @async: Boolean indicating asynchronously
* @force: Boolean indicating use force update
*
+ * Perform a read/modify/write cycle on the register field with change,
+ * async, force option.
+ *
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
@@ -1777,9 +1782,8 @@
EXPORT_SYMBOL_GPL(regmap_field_update_bits_base);
/**
- * regmap_fields_update_bits_base():
- * Perform a read/modify/write cycle on the register field
- * with change, async, force option
+ * regmap_fields_update_bits_base() - Perform a read/modify/write cycle a
+ * register field with port ID
*
* @field: Register field to write to
* @id: port ID
@@ -1808,8 +1812,8 @@
}
EXPORT_SYMBOL_GPL(regmap_fields_update_bits_base);
-/*
- * regmap_bulk_write(): Write multiple registers to the device
+/**
+ * regmap_bulk_write() - Write multiple registers to the device
*
* @map: Register map to write to
* @reg: First register to be write from
@@ -2174,18 +2178,18 @@
return _regmap_raw_multi_reg_write(map, regs, num_regs);
}
-/*
- * regmap_multi_reg_write(): Write multiple registers to the device
- *
- * where the set of register,value pairs are supplied in any order,
- * possibly not all in a single range.
+/**
+ * regmap_multi_reg_write() - Write multiple registers to the device
*
* @map: Register map to write to
* @regs: Array of structures containing register,value to be written
* @num_regs: Number of registers to write
*
+ * Write multiple registers to the device where the set of register, value
+ * pairs are supplied in any order, possibly not all in a single range.
+ *
* The 'normal' block write mode will send ultimately send data on the
- * target bus as R,V1,V2,V3,..,Vn where successively higer registers are
+ * target bus as R,V1,V2,V3,..,Vn where successively higher registers are
* addressed. However, this alternative block multi write mode will send
* the data as R1,V1,R2,V2,..,Rn,Vn on the target bus. The target device
* must of course support the mode.
@@ -2208,16 +2212,17 @@
}
EXPORT_SYMBOL_GPL(regmap_multi_reg_write);
-/*
- * regmap_multi_reg_write_bypassed(): Write multiple registers to the
- * device but not the cache
- *
- * where the set of register are supplied in any order
+/**
+ * regmap_multi_reg_write_bypassed() - Write multiple registers to the
+ * device but not the cache
*
* @map: Register map to write to
* @regs: Array of structures containing register,value to be written
* @num_regs: Number of registers to write
*
+ * Write multiple registers to the device but not the cache where the set
+ * of register are supplied in any order.
+ *
* This function is intended to be used for writing a large block of data
* atomically to the device in single transfer for those I2C client devices
* that implement this alternative block write mode.
@@ -2248,8 +2253,8 @@
EXPORT_SYMBOL_GPL(regmap_multi_reg_write_bypassed);
/**
- * regmap_raw_write_async(): Write raw values to one or more registers
- * asynchronously
+ * regmap_raw_write_async() - Write raw values to one or more registers
+ * asynchronously
*
* @map: Register map to write to
* @reg: Initial register to write to
@@ -2385,7 +2390,7 @@
}
/**
- * regmap_read(): Read a value from a single register
+ * regmap_read() - Read a value from a single register
*
* @map: Register map to read from
* @reg: Register to be read from
@@ -2412,7 +2417,7 @@
EXPORT_SYMBOL_GPL(regmap_read);
/**
- * regmap_raw_read(): Read raw data from the device
+ * regmap_raw_read() - Read raw data from the device
*
* @map: Register map to read from
* @reg: First register to be read from
@@ -2477,7 +2482,7 @@
EXPORT_SYMBOL_GPL(regmap_raw_read);
/**
- * regmap_field_read(): Read a value to a single register field
+ * regmap_field_read() - Read a value to a single register field
*
* @field: Register field to read from
* @val: Pointer to store read value
@@ -2502,7 +2507,7 @@
EXPORT_SYMBOL_GPL(regmap_field_read);
/**
- * regmap_fields_read(): Read a value to a single register field with port ID
+ * regmap_fields_read() - Read a value to a single register field with port ID
*
* @field: Register field to read from
* @id: port ID
@@ -2535,7 +2540,7 @@
EXPORT_SYMBOL_GPL(regmap_fields_read);
/**
- * regmap_bulk_read(): Read multiple registers from the device
+ * regmap_bulk_read() - Read multiple registers from the device
*
* @map: Register map to read from
* @reg: First register to be read from
@@ -2692,9 +2697,7 @@
}
/**
- * regmap_update_bits_base:
- * Perform a read/modify/write cycle on the
- * register map with change, async, force option
+ * regmap_update_bits_base() - Perform a read/modify/write cycle on a register
*
* @map: Register map to update
* @reg: Register to update
@@ -2704,10 +2707,14 @@
* @async: Boolean indicating asynchronously
* @force: Boolean indicating use force update
*
- * if async was true,
- * With most buses the read must be done synchronously so this is most
- * useful for devices with a cache which do not need to interact with
- * the hardware to determine the current register value.
+ * Perform a read/modify/write cycle on a register map with change, async, force
+ * options.
+ *
+ * If async is true:
+ *
+ * With most buses the read must be done synchronously so this is most useful
+ * for devices with a cache which do not need to interact with the hardware to
+ * determine the current register value.
*
* Returns zero for success, a negative number on error.
*/
@@ -2765,7 +2772,7 @@
}
/**
- * regmap_async_complete: Ensure all asynchronous I/O has completed.
+ * regmap_async_complete - Ensure all asynchronous I/O has completed.
*
* @map: Map to operate on.
*
@@ -2797,8 +2804,8 @@
EXPORT_SYMBOL_GPL(regmap_async_complete);
/**
- * regmap_register_patch: Register and apply register updates to be applied
- * on device initialistion
+ * regmap_register_patch - Register and apply register updates to be applied
+ * on device initialistion
*
* @map: Register map to apply updates to.
* @regs: Values to update.
@@ -2855,8 +2862,10 @@
}
EXPORT_SYMBOL_GPL(regmap_register_patch);
-/*
- * regmap_get_val_bytes(): Report the size of a register value
+/**
+ * regmap_get_val_bytes() - Report the size of a register value
+ *
+ * @map: Register map to operate on.
*
* Report the size of a register value, mainly intended to for use by
* generic infrastructure built on top of regmap.
@@ -2871,7 +2880,9 @@
EXPORT_SYMBOL_GPL(regmap_get_val_bytes);
/**
- * regmap_get_max_register(): Report the max register value
+ * regmap_get_max_register() - Report the max register value
+ *
+ * @map: Register map to operate on.
*
* Report the max register value, mainly intended to for use by
* generic infrastructure built on top of regmap.
@@ -2883,7 +2894,9 @@
EXPORT_SYMBOL_GPL(regmap_get_max_register);
/**
- * regmap_get_reg_stride(): Report the register address stride
+ * regmap_get_reg_stride() - Report the register address stride
+ *
+ * @map: Register map to operate on.
*
* Report the register address stride, mainly intended to for use by
* generic infrastructure built on top of regmap.
diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c
index ab62b81..dece26f 100644
--- a/drivers/block/drbd/drbd_bitmap.c
+++ b/drivers/block/drbd/drbd_bitmap.c
@@ -1070,7 +1070,7 @@
.done = 0,
.flags = flags,
.error = 0,
- .kref = { ATOMIC_INIT(2) },
+ .kref = KREF_INIT(2),
};
if (!get_ldev_if_state(device, D_ATTACHING)) { /* put is in drbd_bm_aio_ctx_destroy() */
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index 8348272..c3ff60c 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -2948,7 +2948,6 @@
struct drbd_resource *resource = device->resource;
struct drbd_connection *connection;
struct drbd_peer_device *peer_device;
- int refs = 3;
/* move to free_peer_device() */
for_each_peer_device(peer_device, device)
@@ -2956,13 +2955,15 @@
drbd_debugfs_device_cleanup(device);
for_each_connection(connection, resource) {
idr_remove(&connection->peer_devices, device->vnr);
- refs++;
+ kref_put(&device->kref, drbd_destroy_device);
}
idr_remove(&resource->devices, device->vnr);
+ kref_put(&device->kref, drbd_destroy_device);
idr_remove(&drbd_devices, device_to_minor(device));
+ kref_put(&device->kref, drbd_destroy_device);
del_gendisk(device->vdisk);
synchronize_rcu();
- kref_sub(&device->kref, refs, drbd_destroy_device);
+ kref_put(&device->kref, drbd_destroy_device);
}
static int __init drbd_init(void)
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c
index de279fe..b489ac2 100644
--- a/drivers/block/drbd/drbd_req.c
+++ b/drivers/block/drbd/drbd_req.c
@@ -421,7 +421,6 @@
struct drbd_peer_device *peer_device = first_peer_device(device);
unsigned s = req->rq_state;
int c_put = 0;
- int k_put = 0;
if (drbd_suspended(device) && !((s | clear) & RQ_COMPLETION_SUSP))
set |= RQ_COMPLETION_SUSP;
@@ -437,6 +436,8 @@
/* intent: get references */
+ kref_get(&req->kref);
+
if (!(s & RQ_LOCAL_PENDING) && (set & RQ_LOCAL_PENDING))
atomic_inc(&req->completion_ref);
@@ -473,15 +474,12 @@
if (!(s & RQ_LOCAL_ABORTED) && (set & RQ_LOCAL_ABORTED)) {
D_ASSERT(device, req->rq_state & RQ_LOCAL_PENDING);
- /* local completion may still come in later,
- * we need to keep the req object around. */
- kref_get(&req->kref);
++c_put;
}
if ((s & RQ_LOCAL_PENDING) && (clear & RQ_LOCAL_PENDING)) {
if (req->rq_state & RQ_LOCAL_ABORTED)
- ++k_put;
+ kref_put(&req->kref, drbd_req_destroy);
else
++c_put;
list_del_init(&req->req_pending_local);
@@ -503,7 +501,7 @@
if (s & RQ_NET_SENT)
atomic_sub(req->i.size >> 9, &device->ap_in_flight);
if (s & RQ_EXP_BARR_ACK)
- ++k_put;
+ kref_put(&req->kref, drbd_req_destroy);
req->net_done_jif = jiffies;
/* in ahead/behind mode, or just in case,
@@ -516,25 +514,16 @@
/* potentially complete and destroy */
- if (k_put || c_put) {
- /* Completion does it's own kref_put. If we are going to
- * kref_sub below, we need req to be still around then. */
- int at_least = k_put + !!c_put;
- int refcount = atomic_read(&req->kref.refcount);
- if (refcount < at_least)
- drbd_err(device,
- "mod_rq_state: Logic BUG: %x -> %x: refcount = %d, should be >= %d\n",
- s, req->rq_state, refcount, at_least);
- }
-
/* If we made progress, retry conflicting peer requests, if any. */
if (req->i.waiting)
wake_up(&device->misc_wait);
- if (c_put)
- k_put += drbd_req_put_completion_ref(req, m, c_put);
- if (k_put)
- kref_sub(&req->kref, k_put, drbd_req_destroy);
+ if (c_put) {
+ if (drbd_req_put_completion_ref(req, m, c_put))
+ kref_put(&req->kref, drbd_req_destroy);
+ } else {
+ kref_put(&req->kref, drbd_req_destroy);
+ }
}
static void drbd_report_io_error(struct drbd_device *device, struct drbd_request *req)
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 36d2b9f..436baa6 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -1535,7 +1535,7 @@
static void rbd_obj_request_get(struct rbd_obj_request *obj_request)
{
dout("%s: obj %p (was %d)\n", __func__, obj_request,
- atomic_read(&obj_request->kref.refcount));
+ kref_read(&obj_request->kref));
kref_get(&obj_request->kref);
}
@@ -1544,14 +1544,14 @@
{
rbd_assert(obj_request != NULL);
dout("%s: obj %p (was %d)\n", __func__, obj_request,
- atomic_read(&obj_request->kref.refcount));
+ kref_read(&obj_request->kref));
kref_put(&obj_request->kref, rbd_obj_request_destroy);
}
static void rbd_img_request_get(struct rbd_img_request *img_request)
{
dout("%s: img %p (was %d)\n", __func__, img_request,
- atomic_read(&img_request->kref.refcount));
+ kref_read(&img_request->kref));
kref_get(&img_request->kref);
}
@@ -1562,7 +1562,7 @@
{
rbd_assert(img_request != NULL);
dout("%s: img %p (was %d)\n", __func__, img_request,
- atomic_read(&img_request->kref.refcount));
+ kref_read(&img_request->kref));
if (img_request_child_test(img_request))
kref_put(&img_request->kref, rbd_parent_request_destroy);
else
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 10332c2..264c5ea 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -770,7 +770,7 @@
/* Stop all the virtqueues. */
vdev->config->reset(vdev);
- refc = atomic_read(&disk_to_dev(vblk->disk)->kobj.kref.refcount);
+ refc = kref_read(&disk_to_dev(vblk->disk)->kobj.kref);
put_disk(vblk->disk);
vdev->config->del_vqs(vdev);
kfree(vblk->vqs);
diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
index f010562..2c44aeb 100644
--- a/drivers/clk/tegra/clk-dfll.c
+++ b/drivers/clk/tegra/clk-dfll.c
@@ -633,16 +633,12 @@
struct dev_pm_opp *opp;
int i, uv;
- rcu_read_lock();
-
opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate);
- if (IS_ERR(opp)) {
- rcu_read_unlock();
+ if (IS_ERR(opp))
return PTR_ERR(opp);
- }
- uv = dev_pm_opp_get_voltage(opp);
- rcu_read_unlock();
+ uv = dev_pm_opp_get_voltage(opp);
+ dev_pm_opp_put(opp);
for (i = 0; i < td->i2c_lut_size; i++) {
if (regulator_list_voltage(td->vdd_reg, td->i2c_lut[i]) == uv)
@@ -1440,8 +1436,6 @@
struct dev_pm_opp *opp;
int lut;
- rcu_read_lock();
-
rate = ULONG_MAX;
opp = dev_pm_opp_find_freq_floor(td->soc->dev, &rate);
if (IS_ERR(opp)) {
@@ -1449,6 +1443,7 @@
goto out;
}
v_max = dev_pm_opp_get_voltage(opp);
+ dev_pm_opp_put(opp);
v = td->soc->cvb->min_millivolts * 1000;
lut = find_vdd_map_entry_exact(td, v);
@@ -1465,6 +1460,8 @@
if (v_opp <= td->soc->cvb->min_millivolts * 1000)
td->dvco_rate_min = dev_pm_opp_get_freq(opp);
+ dev_pm_opp_put(opp);
+
for (;;) {
v += max(1, (v_max - v) / (MAX_DFLL_VOLTAGES - j));
if (v >= v_opp)
@@ -1496,8 +1493,6 @@
ret = 0;
out:
- rcu_read_unlock();
-
return ret;
}
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 4866f7a..3356ab8 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -5,6 +5,10 @@
bool
select CLKSRC_PROBE
+config CLKEVT_OF
+ bool
+ select CLKEVT_PROBE
+
config CLKSRC_ACPI
bool
select CLKSRC_PROBE
@@ -12,6 +16,9 @@
config CLKSRC_PROBE
bool
+config CLKEVT_PROBE
+ bool
+
config CLKSRC_I8253
bool
@@ -60,6 +67,16 @@
select DW_APB_TIMER
select CLKSRC_OF
+config GEMINI_TIMER
+ bool "Cortina Gemini timer driver" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS
+ depends on HAS_IOMEM
+ select CLKSRC_MMIO
+ select CLKSRC_OF
+ select MFD_SYSCON
+ help
+ Enables support for the Gemini timer
+
config ROCKCHIP_TIMER
bool "Rockchip timer driver" if COMPILE_TEST
depends on ARM || ARM64
@@ -325,16 +342,30 @@
This must be disabled for hardware validation purposes to detect any
hardware anomalies of missing events.
+config ARM_ARCH_TIMER_OOL_WORKAROUND
+ bool
+
config FSL_ERRATUM_A008585
bool "Workaround for Freescale/NXP Erratum A-008585"
default y
depends on ARM_ARCH_TIMER && ARM64
+ select ARM_ARCH_TIMER_OOL_WORKAROUND
help
This option enables a workaround for Freescale/NXP Erratum
A-008585 ("ARM generic timer may contain an erroneous
value"). The workaround will only be active if the
fsl,erratum-a008585 property is found in the timer node.
+config HISILICON_ERRATUM_161010101
+ bool "Workaround for Hisilicon Erratum 161010101"
+ default y
+ select ARM_ARCH_TIMER_OOL_WORKAROUND
+ depends on ARM_ARCH_TIMER && ARM64
+ help
+ This option enables a workaround for Hisilicon Erratum
+ 161010101. The workaround will be active if the hisilicon,erratum-161010101
+ property is found in the timer node.
+
config ARM_GLOBAL_TIMER
bool "Support for the ARM global timer" if COMPILE_TEST
select CLKSRC_OF if OF
@@ -467,6 +498,13 @@
Timer Pulse Unit 2 (MTU2) hardware available on SoCs from Renesas.
This hardware comes with 16 bit-timer registers.
+config RENESAS_OSTM
+ bool "Renesas OSTM timer driver" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS
+ select CLKSRC_MMIO
+ help
+ Enables the support for the Renesas OSTM.
+
config SH_TIMER_TMU
bool "Renesas TMU timer driver" if COMPILE_TEST
depends on GENERIC_CLOCKEVENTS
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index a14111e..d227d13 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_CLKSRC_PROBE) += clksrc-probe.o
+obj-$(CONFIG_CLKEVT_PROBE) += clkevt-probe.o
obj-$(CONFIG_ATMEL_PIT) += timer-atmel-pit.o
obj-$(CONFIG_ATMEL_ST) += timer-atmel-st.o
obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o
@@ -8,6 +9,7 @@
obj-$(CONFIG_CLKSRC_JCORE_PIT) += jcore-pit.o
obj-$(CONFIG_SH_TIMER_CMT) += sh_cmt.o
obj-$(CONFIG_SH_TIMER_MTU2) += sh_mtu2.o
+obj-$(CONFIG_RENESAS_OSTM) += renesas-ostm.o
obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o
obj-$(CONFIG_EM_TIMER_STI) += em_sti.o
obj-$(CONFIG_CLKBLD_I8253) += i8253.o
@@ -15,6 +17,7 @@
obj-$(CONFIG_DIGICOLOR_TIMER) += timer-digicolor.o
obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o
+obj-$(CONFIG_GEMINI_TIMER) += timer-gemini.o
obj-$(CONFIG_ROCKCHIP_TIMER) += rockchip_timer.o
obj-$(CONFIG_CLKSRC_NOMADIK_MTU) += nomadik-mtu.o
obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 4c8c3fb..93aa136 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -96,41 +96,107 @@
*/
#ifdef CONFIG_FSL_ERRATUM_A008585
-DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
-EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
+/*
+ * The number of retries is an arbitrary value well beyond the highest number
+ * of iterations the loop has been observed to take.
+ */
+#define __fsl_a008585_read_reg(reg) ({ \
+ u64 _old, _new; \
+ int _retries = 200; \
+ \
+ do { \
+ _old = read_sysreg(reg); \
+ _new = read_sysreg(reg); \
+ _retries--; \
+ } while (unlikely(_old != _new) && _retries); \
+ \
+ WARN_ON_ONCE(!_retries); \
+ _new; \
+})
-static int fsl_a008585_enable = -1;
-
-static int __init early_fsl_a008585_cfg(char *buf)
-{
- int ret;
- bool val;
-
- ret = strtobool(buf, &val);
- if (ret)
- return ret;
-
- fsl_a008585_enable = val;
- return 0;
-}
-early_param("clocksource.arm_arch_timer.fsl-a008585", early_fsl_a008585_cfg);
-
-u32 __fsl_a008585_read_cntp_tval_el0(void)
+static u32 notrace fsl_a008585_read_cntp_tval_el0(void)
{
return __fsl_a008585_read_reg(cntp_tval_el0);
}
-u32 __fsl_a008585_read_cntv_tval_el0(void)
+static u32 notrace fsl_a008585_read_cntv_tval_el0(void)
{
return __fsl_a008585_read_reg(cntv_tval_el0);
}
-u64 __fsl_a008585_read_cntvct_el0(void)
+static u64 notrace fsl_a008585_read_cntvct_el0(void)
{
return __fsl_a008585_read_reg(cntvct_el0);
}
-EXPORT_SYMBOL(__fsl_a008585_read_cntvct_el0);
-#endif /* CONFIG_FSL_ERRATUM_A008585 */
+#endif
+
+#ifdef CONFIG_HISILICON_ERRATUM_161010101
+/*
+ * Verify whether the value of the second read is larger than the first by
+ * less than 32 is the only way to confirm the value is correct, so clear the
+ * lower 5 bits to check whether the difference is greater than 32 or not.
+ * Theoretically the erratum should not occur more than twice in succession
+ * when reading the system counter, but it is possible that some interrupts
+ * may lead to more than twice read errors, triggering the warning, so setting
+ * the number of retries far beyond the number of iterations the loop has been
+ * observed to take.
+ */
+#define __hisi_161010101_read_reg(reg) ({ \
+ u64 _old, _new; \
+ int _retries = 50; \
+ \
+ do { \
+ _old = read_sysreg(reg); \
+ _new = read_sysreg(reg); \
+ _retries--; \
+ } while (unlikely((_new - _old) >> 5) && _retries); \
+ \
+ WARN_ON_ONCE(!_retries); \
+ _new; \
+})
+
+static u32 notrace hisi_161010101_read_cntp_tval_el0(void)
+{
+ return __hisi_161010101_read_reg(cntp_tval_el0);
+}
+
+static u32 notrace hisi_161010101_read_cntv_tval_el0(void)
+{
+ return __hisi_161010101_read_reg(cntv_tval_el0);
+}
+
+static u64 notrace hisi_161010101_read_cntvct_el0(void)
+{
+ return __hisi_161010101_read_reg(cntvct_el0);
+}
+#endif
+
+#ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND
+const struct arch_timer_erratum_workaround *timer_unstable_counter_workaround = NULL;
+EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround);
+
+DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
+EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
+
+static const struct arch_timer_erratum_workaround ool_workarounds[] = {
+#ifdef CONFIG_FSL_ERRATUM_A008585
+ {
+ .id = "fsl,erratum-a008585",
+ .read_cntp_tval_el0 = fsl_a008585_read_cntp_tval_el0,
+ .read_cntv_tval_el0 = fsl_a008585_read_cntv_tval_el0,
+ .read_cntvct_el0 = fsl_a008585_read_cntvct_el0,
+ },
+#endif
+#ifdef CONFIG_HISILICON_ERRATUM_161010101
+ {
+ .id = "hisilicon,erratum-161010101",
+ .read_cntp_tval_el0 = hisi_161010101_read_cntp_tval_el0,
+ .read_cntv_tval_el0 = hisi_161010101_read_cntv_tval_el0,
+ .read_cntvct_el0 = hisi_161010101_read_cntvct_el0,
+ },
+#endif
+};
+#endif /* CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND */
static __always_inline
void arch_timer_reg_write(int access, enum arch_timer_reg reg, u32 val,
@@ -281,8 +347,8 @@
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
}
-#ifdef CONFIG_FSL_ERRATUM_A008585
-static __always_inline void fsl_a008585_set_next_event(const int access,
+#ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND
+static __always_inline void erratum_set_next_event_generic(const int access,
unsigned long evt, struct clock_event_device *clk)
{
unsigned long ctrl;
@@ -300,20 +366,20 @@
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
}
-static int fsl_a008585_set_next_event_virt(unsigned long evt,
+static int erratum_set_next_event_virt(unsigned long evt,
struct clock_event_device *clk)
{
- fsl_a008585_set_next_event(ARCH_TIMER_VIRT_ACCESS, evt, clk);
+ erratum_set_next_event_generic(ARCH_TIMER_VIRT_ACCESS, evt, clk);
return 0;
}
-static int fsl_a008585_set_next_event_phys(unsigned long evt,
+static int erratum_set_next_event_phys(unsigned long evt,
struct clock_event_device *clk)
{
- fsl_a008585_set_next_event(ARCH_TIMER_PHYS_ACCESS, evt, clk);
+ erratum_set_next_event_generic(ARCH_TIMER_PHYS_ACCESS, evt, clk);
return 0;
}
-#endif /* CONFIG_FSL_ERRATUM_A008585 */
+#endif /* CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND */
static int arch_timer_set_next_event_virt(unsigned long evt,
struct clock_event_device *clk)
@@ -343,16 +409,16 @@
return 0;
}
-static void fsl_a008585_set_sne(struct clock_event_device *clk)
+static void erratum_workaround_set_sne(struct clock_event_device *clk)
{
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND
if (!static_branch_unlikely(&arch_timer_read_ool_enabled))
return;
if (arch_timer_uses_ppi == VIRT_PPI)
- clk->set_next_event = fsl_a008585_set_next_event_virt;
+ clk->set_next_event = erratum_set_next_event_virt;
else
- clk->set_next_event = fsl_a008585_set_next_event_phys;
+ clk->set_next_event = erratum_set_next_event_phys;
#endif
}
@@ -385,7 +451,7 @@
BUG();
}
- fsl_a008585_set_sne(clk);
+ erratum_workaround_set_sne(clk);
} else {
clk->features |= CLOCK_EVT_FEAT_DYNIRQ;
clk->name = "arch_mem_timer";
@@ -580,7 +646,7 @@
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
-static struct cyclecounter cyclecounter = {
+static struct cyclecounter cyclecounter __ro_after_init = {
.read = arch_counter_read_cc,
.mask = CLOCKSOURCE_MASK(56),
};
@@ -605,7 +671,7 @@
clocksource_counter.archdata.vdso_direct = true;
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND
/*
* Don't use the vdso fastpath if errata require using
* the out-of-line counter accessor.
@@ -893,12 +959,15 @@
arch_timer_c3stop = !of_property_read_bool(np, "always-on");
-#ifdef CONFIG_FSL_ERRATUM_A008585
- if (fsl_a008585_enable < 0)
- fsl_a008585_enable = of_property_read_bool(np, "fsl,erratum-a008585");
- if (fsl_a008585_enable) {
- static_branch_enable(&arch_timer_read_ool_enabled);
- pr_info("Enabling workaround for FSL erratum A-008585\n");
+#ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND
+ for (i = 0; i < ARRAY_SIZE(ool_workarounds); i++) {
+ if (of_property_read_bool(np, ool_workarounds[i].id)) {
+ timer_unstable_counter_workaround = &ool_workarounds[i];
+ static_branch_enable(&arch_timer_read_ool_enabled);
+ pr_info("arch_timer: Enabling workaround for %s\n",
+ timer_unstable_counter_workaround->id);
+ break;
+ }
}
#endif
diff --git a/drivers/clocksource/clkevt-probe.c b/drivers/clocksource/clkevt-probe.c
new file mode 100644
index 0000000..8c30fec
--- /dev/null
+++ b/drivers/clocksource/clkevt-probe.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2016, Linaro Ltd. All rights reserved.
+ * Daniel Lezcano <daniel.lezcano@linaro.org>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/clockchip.h>
+
+extern struct of_device_id __clkevt_of_table[];
+
+static const struct of_device_id __clkevt_of_table_sentinel
+ __used __section(__clkevt_of_table_end);
+
+int __init clockevent_probe(void)
+{
+ struct device_node *np;
+ const struct of_device_id *match;
+ of_init_fn_1_ret init_func;
+ int ret, clockevents = 0;
+
+ for_each_matching_node_and_match(np, __clkevt_of_table, &match) {
+ if (!of_device_is_available(np))
+ continue;
+
+ init_func = match->data;
+
+ ret = init_func(np);
+ if (ret) {
+ pr_warn("Failed to initialize '%s' (%d)\n",
+ np->name, ret);
+ continue;
+ }
+
+ clockevents++;
+ }
+
+ if (!clockevents) {
+ pr_crit("%s: no matching clockevent found\n", __func__);
+ return -ENODEV;
+ }
+
+ return 0;
+}
diff --git a/drivers/clocksource/renesas-ostm.c b/drivers/clocksource/renesas-ostm.c
new file mode 100644
index 0000000..c76f576
--- /dev/null
+++ b/drivers/clocksource/renesas-ostm.c
@@ -0,0 +1,265 @@
+/*
+ * Renesas Timer Support - OSTM
+ *
+ * Copyright (C) 2017 Renesas Electronics America, Inc.
+ * Copyright (C) 2017 Chris Brandt
+ *
+ * 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
+ *
+ * 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/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/sched_clock.h>
+#include <linux/slab.h>
+
+/*
+ * The OSTM contains independent channels.
+ * The first OSTM channel probed will be set up as a free running
+ * clocksource. Additionally we will use this clocksource for the system
+ * schedule timer sched_clock().
+ *
+ * The second (or more) channel probed will be set up as an interrupt
+ * driven clock event.
+ */
+
+struct ostm_device {
+ void __iomem *base;
+ unsigned long ticks_per_jiffy;
+ struct clock_event_device ced;
+};
+
+static void __iomem *system_clock; /* For sched_clock() */
+
+/* OSTM REGISTERS */
+#define OSTM_CMP 0x000 /* RW,32 */
+#define OSTM_CNT 0x004 /* R,32 */
+#define OSTM_TE 0x010 /* R,8 */
+#define OSTM_TS 0x014 /* W,8 */
+#define OSTM_TT 0x018 /* W,8 */
+#define OSTM_CTL 0x020 /* RW,8 */
+
+#define TE 0x01
+#define TS 0x01
+#define TT 0x01
+#define CTL_PERIODIC 0x00
+#define CTL_ONESHOT 0x02
+#define CTL_FREERUN 0x02
+
+static struct ostm_device *ced_to_ostm(struct clock_event_device *ced)
+{
+ return container_of(ced, struct ostm_device, ced);
+}
+
+static void ostm_timer_stop(struct ostm_device *ostm)
+{
+ if (readb(ostm->base + OSTM_TE) & TE) {
+ writeb(TT, ostm->base + OSTM_TT);
+
+ /*
+ * Read back the register simply to confirm the write operation
+ * has completed since I/O writes can sometimes get queued by
+ * the bus architecture.
+ */
+ while (readb(ostm->base + OSTM_TE) & TE)
+ ;
+ }
+}
+
+static int __init ostm_init_clksrc(struct ostm_device *ostm, unsigned long rate)
+{
+ /*
+ * irq not used (clock sources don't use interrupts)
+ */
+
+ ostm_timer_stop(ostm);
+
+ writel(0, ostm->base + OSTM_CMP);
+ writeb(CTL_FREERUN, ostm->base + OSTM_CTL);
+ writeb(TS, ostm->base + OSTM_TS);
+
+ return clocksource_mmio_init(ostm->base + OSTM_CNT,
+ "ostm", rate,
+ 300, 32, clocksource_mmio_readl_up);
+}
+
+static u64 notrace ostm_read_sched_clock(void)
+{
+ return readl(system_clock);
+}
+
+static void __init ostm_init_sched_clock(struct ostm_device *ostm,
+ unsigned long rate)
+{
+ system_clock = ostm->base + OSTM_CNT;
+ sched_clock_register(ostm_read_sched_clock, 32, rate);
+}
+
+static int ostm_clock_event_next(unsigned long delta,
+ struct clock_event_device *ced)
+{
+ struct ostm_device *ostm = ced_to_ostm(ced);
+
+ ostm_timer_stop(ostm);
+
+ writel(delta, ostm->base + OSTM_CMP);
+ writeb(CTL_ONESHOT, ostm->base + OSTM_CTL);
+ writeb(TS, ostm->base + OSTM_TS);
+
+ return 0;
+}
+
+static int ostm_shutdown(struct clock_event_device *ced)
+{
+ struct ostm_device *ostm = ced_to_ostm(ced);
+
+ ostm_timer_stop(ostm);
+
+ return 0;
+}
+static int ostm_set_periodic(struct clock_event_device *ced)
+{
+ struct ostm_device *ostm = ced_to_ostm(ced);
+
+ if (clockevent_state_oneshot(ced) || clockevent_state_periodic(ced))
+ ostm_timer_stop(ostm);
+
+ writel(ostm->ticks_per_jiffy - 1, ostm->base + OSTM_CMP);
+ writeb(CTL_PERIODIC, ostm->base + OSTM_CTL);
+ writeb(TS, ostm->base + OSTM_TS);
+
+ return 0;
+}
+
+static int ostm_set_oneshot(struct clock_event_device *ced)
+{
+ struct ostm_device *ostm = ced_to_ostm(ced);
+
+ ostm_timer_stop(ostm);
+
+ return 0;
+}
+
+static irqreturn_t ostm_timer_interrupt(int irq, void *dev_id)
+{
+ struct ostm_device *ostm = dev_id;
+
+ if (clockevent_state_oneshot(&ostm->ced))
+ ostm_timer_stop(ostm);
+
+ /* notify clockevent layer */
+ if (ostm->ced.event_handler)
+ ostm->ced.event_handler(&ostm->ced);
+
+ return IRQ_HANDLED;
+}
+
+static int __init ostm_init_clkevt(struct ostm_device *ostm, int irq,
+ unsigned long rate)
+{
+ struct clock_event_device *ced = &ostm->ced;
+ int ret = -ENXIO;
+
+ ret = request_irq(irq, ostm_timer_interrupt,
+ IRQF_TIMER | IRQF_IRQPOLL,
+ "ostm", ostm);
+ if (ret) {
+ pr_err("ostm: failed to request irq\n");
+ return ret;
+ }
+
+ ced->name = "ostm";
+ ced->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC;
+ ced->set_state_shutdown = ostm_shutdown;
+ ced->set_state_periodic = ostm_set_periodic;
+ ced->set_state_oneshot = ostm_set_oneshot;
+ ced->set_next_event = ostm_clock_event_next;
+ ced->shift = 32;
+ ced->rating = 300;
+ ced->cpumask = cpumask_of(0);
+ clockevents_config_and_register(ced, rate, 0xf, 0xffffffff);
+
+ return 0;
+}
+
+static int __init ostm_init(struct device_node *np)
+{
+ struct ostm_device *ostm;
+ int ret = -EFAULT;
+ struct clk *ostm_clk = NULL;
+ int irq;
+ unsigned long rate;
+
+ ostm = kzalloc(sizeof(*ostm), GFP_KERNEL);
+ if (!ostm)
+ return -ENOMEM;
+
+ ostm->base = of_iomap(np, 0);
+ if (!ostm->base) {
+ pr_err("ostm: failed to remap I/O memory\n");
+ goto err;
+ }
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (irq < 0) {
+ pr_err("ostm: Failed to get irq\n");
+ goto err;
+ }
+
+ ostm_clk = of_clk_get(np, 0);
+ if (IS_ERR(ostm_clk)) {
+ pr_err("ostm: Failed to get clock\n");
+ ostm_clk = NULL;
+ goto err;
+ }
+
+ ret = clk_prepare_enable(ostm_clk);
+ if (ret) {
+ pr_err("ostm: Failed to enable clock\n");
+ goto err;
+ }
+
+ rate = clk_get_rate(ostm_clk);
+ ostm->ticks_per_jiffy = (rate + HZ / 2) / HZ;
+
+ /*
+ * First probed device will be used as system clocksource. Any
+ * additional devices will be used as clock events.
+ */
+ if (!system_clock) {
+ ret = ostm_init_clksrc(ostm, rate);
+
+ if (!ret) {
+ ostm_init_sched_clock(ostm, rate);
+ pr_info("ostm: used for clocksource\n");
+ }
+
+ } else {
+ ret = ostm_init_clkevt(ostm, irq, rate);
+
+ if (!ret)
+ pr_info("ostm: used for clock events\n");
+ }
+
+err:
+ if (ret) {
+ clk_disable_unprepare(ostm_clk);
+ iounmap(ostm->base);
+ kfree(ostm);
+ return ret;
+ }
+
+ return 0;
+}
+
+CLOCKSOURCE_OF_DECLARE(ostm, "renesas,ostm", ostm_init);
diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c
index d4ca996..745844e 100644
--- a/drivers/clocksource/tcb_clksrc.c
+++ b/drivers/clocksource/tcb_clksrc.c
@@ -10,6 +10,7 @@
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/atmel_tc.h>
+#include <linux/sched_clock.h>
/*
@@ -56,11 +57,16 @@
return (upper << 16) | lower;
}
-static u64 tc_get_cycles32(struct clocksource *cs)
+static u32 tc_get_cv32(void)
{
return __raw_readl(tcaddr + ATMEL_TC_REG(0, CV));
}
+static u64 tc_get_cycles32(struct clocksource *cs)
+{
+ return tc_get_cv32();
+}
+
static struct clocksource clksrc = {
.name = "tcb_clksrc",
.rating = 200,
@@ -69,6 +75,11 @@
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
+static u64 notrace tc_read_sched_clock(void)
+{
+ return tc_get_cv32();
+}
+
#ifdef CONFIG_GENERIC_CLOCKEVENTS
struct tc_clkevt_device {
@@ -339,6 +350,9 @@
clksrc.read = tc_get_cycles32;
/* setup ony channel 0 */
tcb_setup_single_chan(tc, best_divisor_idx);
+
+ /* register sched_clock on chips with single 32 bit counter */
+ sched_clock_register(tc_read_sched_clock, 32, divided_rate);
} else {
/* tclib will give us three clocks no matter what the
* underlying platform supports.
diff --git a/drivers/clocksource/timer-gemini.c b/drivers/clocksource/timer-gemini.c
new file mode 100644
index 0000000..dda27b7
--- /dev/null
+++ b/drivers/clocksource/timer-gemini.c
@@ -0,0 +1,277 @@
+/*
+ * Gemini timer driver
+ * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
+ *
+ * Based on a rewrite of arch/arm/mach-gemini/timer.c:
+ * Copyright (C) 2001-2006 Storlink, Corp.
+ * Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ */
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/sched_clock.h>
+
+/*
+ * Relevant registers in the global syscon
+ */
+#define GLOBAL_STATUS 0x04
+#define CPU_AHB_RATIO_MASK (0x3 << 18)
+#define CPU_AHB_1_1 (0x0 << 18)
+#define CPU_AHB_3_2 (0x1 << 18)
+#define CPU_AHB_24_13 (0x2 << 18)
+#define CPU_AHB_2_1 (0x3 << 18)
+#define REG_TO_AHB_SPEED(reg) ((((reg) >> 15) & 0x7) * 10 + 130)
+
+/*
+ * Register definitions for the timers
+ */
+#define TIMER1_COUNT (0x00)
+#define TIMER1_LOAD (0x04)
+#define TIMER1_MATCH1 (0x08)
+#define TIMER1_MATCH2 (0x0c)
+#define TIMER2_COUNT (0x10)
+#define TIMER2_LOAD (0x14)
+#define TIMER2_MATCH1 (0x18)
+#define TIMER2_MATCH2 (0x1c)
+#define TIMER3_COUNT (0x20)
+#define TIMER3_LOAD (0x24)
+#define TIMER3_MATCH1 (0x28)
+#define TIMER3_MATCH2 (0x2c)
+#define TIMER_CR (0x30)
+#define TIMER_INTR_STATE (0x34)
+#define TIMER_INTR_MASK (0x38)
+
+#define TIMER_1_CR_ENABLE (1 << 0)
+#define TIMER_1_CR_CLOCK (1 << 1)
+#define TIMER_1_CR_INT (1 << 2)
+#define TIMER_2_CR_ENABLE (1 << 3)
+#define TIMER_2_CR_CLOCK (1 << 4)
+#define TIMER_2_CR_INT (1 << 5)
+#define TIMER_3_CR_ENABLE (1 << 6)
+#define TIMER_3_CR_CLOCK (1 << 7)
+#define TIMER_3_CR_INT (1 << 8)
+#define TIMER_1_CR_UPDOWN (1 << 9)
+#define TIMER_2_CR_UPDOWN (1 << 10)
+#define TIMER_3_CR_UPDOWN (1 << 11)
+#define TIMER_DEFAULT_FLAGS (TIMER_1_CR_UPDOWN | \
+ TIMER_3_CR_ENABLE | \
+ TIMER_3_CR_UPDOWN)
+
+#define TIMER_1_INT_MATCH1 (1 << 0)
+#define TIMER_1_INT_MATCH2 (1 << 1)
+#define TIMER_1_INT_OVERFLOW (1 << 2)
+#define TIMER_2_INT_MATCH1 (1 << 3)
+#define TIMER_2_INT_MATCH2 (1 << 4)
+#define TIMER_2_INT_OVERFLOW (1 << 5)
+#define TIMER_3_INT_MATCH1 (1 << 6)
+#define TIMER_3_INT_MATCH2 (1 << 7)
+#define TIMER_3_INT_OVERFLOW (1 << 8)
+#define TIMER_INT_ALL_MASK 0x1ff
+
+static unsigned int tick_rate;
+static void __iomem *base;
+
+static u64 notrace gemini_read_sched_clock(void)
+{
+ return readl(base + TIMER3_COUNT);
+}
+
+static int gemini_timer_set_next_event(unsigned long cycles,
+ struct clock_event_device *evt)
+{
+ u32 cr;
+
+ /* Setup the match register */
+ cr = readl(base + TIMER1_COUNT);
+ writel(cr + cycles, base + TIMER1_MATCH1);
+ if (readl(base + TIMER1_COUNT) - cr > cycles)
+ return -ETIME;
+
+ return 0;
+}
+
+static int gemini_timer_shutdown(struct clock_event_device *evt)
+{
+ u32 cr;
+
+ /*
+ * Disable also for oneshot: the set_next() call will arm the timer
+ * instead.
+ */
+ /* Stop timer and interrupt. */
+ cr = readl(base + TIMER_CR);
+ cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT);
+ writel(cr, base + TIMER_CR);
+
+ /* Setup counter start from 0 */
+ writel(0, base + TIMER1_COUNT);
+ writel(0, base + TIMER1_LOAD);
+
+ /* enable interrupt */
+ cr = readl(base + TIMER_INTR_MASK);
+ cr &= ~(TIMER_1_INT_OVERFLOW | TIMER_1_INT_MATCH2);
+ cr |= TIMER_1_INT_MATCH1;
+ writel(cr, base + TIMER_INTR_MASK);
+
+ /* start the timer */
+ cr = readl(base + TIMER_CR);
+ cr |= TIMER_1_CR_ENABLE;
+ writel(cr, base + TIMER_CR);
+
+ return 0;
+}
+
+static int gemini_timer_set_periodic(struct clock_event_device *evt)
+{
+ u32 period = DIV_ROUND_CLOSEST(tick_rate, HZ);
+ u32 cr;
+
+ /* Stop timer and interrupt */
+ cr = readl(base + TIMER_CR);
+ cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT);
+ writel(cr, base + TIMER_CR);
+
+ /* Setup timer to fire at 1/HT intervals. */
+ cr = 0xffffffff - (period - 1);
+ writel(cr, base + TIMER1_COUNT);
+ writel(cr, base + TIMER1_LOAD);
+
+ /* enable interrupt on overflow */
+ cr = readl(base + TIMER_INTR_MASK);
+ cr &= ~(TIMER_1_INT_MATCH1 | TIMER_1_INT_MATCH2);
+ cr |= TIMER_1_INT_OVERFLOW;
+ writel(cr, base + TIMER_INTR_MASK);
+
+ /* Start the timer */
+ cr = readl(base + TIMER_CR);
+ cr |= TIMER_1_CR_ENABLE;
+ cr |= TIMER_1_CR_INT;
+ writel(cr, base + TIMER_CR);
+
+ return 0;
+}
+
+/* Use TIMER1 as clock event */
+static struct clock_event_device gemini_clockevent = {
+ .name = "TIMER1",
+ /* Reasonably fast and accurate clock event */
+ .rating = 300,
+ .shift = 32,
+ .features = CLOCK_EVT_FEAT_PERIODIC |
+ CLOCK_EVT_FEAT_ONESHOT,
+ .set_next_event = gemini_timer_set_next_event,
+ .set_state_shutdown = gemini_timer_shutdown,
+ .set_state_periodic = gemini_timer_set_periodic,
+ .set_state_oneshot = gemini_timer_shutdown,
+ .tick_resume = gemini_timer_shutdown,
+};
+
+/*
+ * IRQ handler for the timer
+ */
+static irqreturn_t gemini_timer_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = &gemini_clockevent;
+
+ evt->event_handler(evt);
+ return IRQ_HANDLED;
+}
+
+static struct irqaction gemini_timer_irq = {
+ .name = "Gemini Timer Tick",
+ .flags = IRQF_TIMER,
+ .handler = gemini_timer_interrupt,
+};
+
+static int __init gemini_timer_of_init(struct device_node *np)
+{
+ static struct regmap *map;
+ int irq;
+ int ret;
+ u32 val;
+
+ map = syscon_regmap_lookup_by_phandle(np, "syscon");
+ if (IS_ERR(map)) {
+ pr_err("Can't get regmap for syscon handle");
+ return -ENODEV;
+ }
+ ret = regmap_read(map, GLOBAL_STATUS, &val);
+ if (ret) {
+ pr_err("Can't read syscon status register");
+ return -ENXIO;
+ }
+
+ base = of_iomap(np, 0);
+ if (!base) {
+ pr_err("Can't remap registers");
+ return -ENXIO;
+ }
+ /* IRQ for timer 1 */
+ irq = irq_of_parse_and_map(np, 0);
+ if (irq <= 0) {
+ pr_err("Can't parse IRQ");
+ return -EINVAL;
+ }
+
+ tick_rate = REG_TO_AHB_SPEED(val) * 1000000;
+ printk(KERN_INFO "Bus: %dMHz", tick_rate / 1000000);
+
+ tick_rate /= 6; /* APB bus run AHB*(1/6) */
+
+ switch (val & CPU_AHB_RATIO_MASK) {
+ case CPU_AHB_1_1:
+ printk(KERN_CONT "(1/1)\n");
+ break;
+ case CPU_AHB_3_2:
+ printk(KERN_CONT "(3/2)\n");
+ break;
+ case CPU_AHB_24_13:
+ printk(KERN_CONT "(24/13)\n");
+ break;
+ case CPU_AHB_2_1:
+ printk(KERN_CONT "(2/1)\n");
+ break;
+ }
+
+ /*
+ * Reset the interrupt mask and status
+ */
+ writel(TIMER_INT_ALL_MASK, base + TIMER_INTR_MASK);
+ writel(0, base + TIMER_INTR_STATE);
+ writel(TIMER_DEFAULT_FLAGS, base + TIMER_CR);
+
+ /*
+ * Setup free-running clocksource timer (interrupts
+ * disabled.)
+ */
+ writel(0, base + TIMER3_COUNT);
+ writel(0, base + TIMER3_LOAD);
+ writel(0, base + TIMER3_MATCH1);
+ writel(0, base + TIMER3_MATCH2);
+ clocksource_mmio_init(base + TIMER3_COUNT,
+ "gemini_clocksource", tick_rate,
+ 300, 32, clocksource_mmio_readl_up);
+ sched_clock_register(gemini_read_sched_clock, 32, tick_rate);
+
+ /*
+ * Setup clockevent timer (interrupt-driven.)
+ */
+ writel(0, base + TIMER1_COUNT);
+ writel(0, base + TIMER1_LOAD);
+ writel(0, base + TIMER1_MATCH1);
+ writel(0, base + TIMER1_MATCH2);
+ setup_irq(irq, &gemini_timer_irq);
+ gemini_clockevent.cpumask = cpumask_of(0);
+ clockevents_config_and_register(&gemini_clockevent, tick_rate,
+ 1, 0xffffffff);
+
+ return 0;
+}
+CLOCKSOURCE_OF_DECLARE(nomadik_mtu, "cortina,gemini-timer",
+ gemini_timer_of_init);
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index d8b164a..4ebae43 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -37,14 +37,6 @@
If in doubt, say N.
-config CPU_FREQ_STAT_DETAILS
- bool "CPU frequency transition statistics details"
- depends on CPU_FREQ_STAT
- help
- Show detailed CPU frequency transition table in sysfs.
-
- If in doubt, say N.
-
choice
prompt "Default CPUFreq governor"
default CPU_FREQ_DEFAULT_GOV_USERSPACE if ARM_SA1100_CPUFREQ || ARM_SA1110_CPUFREQ
@@ -271,6 +263,16 @@
endif
if MIPS
+config BMIPS_CPUFREQ
+ tristate "BMIPS CPUfreq Driver"
+ help
+ This option adds a CPUfreq driver for BMIPS processors with
+ support for configurable CPU frequency.
+
+ For now, BMIPS5 chips are supported (such as the Broadcom 7425).
+
+ If in doubt, say N.
+
config LOONGSON2_CPUFREQ
tristate "Loongson2 CPUFreq Driver"
help
@@ -332,7 +334,7 @@
config QORIQ_CPUFREQ
tristate "CPU frequency scaling driver for Freescale QorIQ SoCs"
- depends on OF && COMMON_CLK && (PPC_E500MC || ARM)
+ depends on OF && COMMON_CLK && (PPC_E500MC || ARM || ARM64)
depends on !CPU_THERMAL || THERMAL
select CLK_QORIQ
help
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 920c469..74fa5c5 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -247,6 +247,17 @@
help
This adds the CPUFreq driver support for Tegra124 SOCs.
+config ARM_TI_CPUFREQ
+ bool "Texas Instruments CPUFreq support"
+ depends on ARCH_OMAP2PLUS
+ help
+ This driver enables valid OPPs on the running platform based on
+ values contained within the SoC in use. Enable this in order to
+ use the cpufreq-dt driver on all Texas Instruments platforms that
+ provide dt based operating-points-v2 tables with opp-supported-hw
+ data provided. Required for cpufreq support on AM335x, AM437x,
+ DRA7x, and AM57x platforms.
+
config ARM_PXA2xx_CPUFREQ
tristate "Intel PXA2xx CPUfreq driver"
depends on PXA27x || PXA25x
@@ -257,7 +268,7 @@
config ACPI_CPPC_CPUFREQ
tristate "CPUFreq driver based on the ACPI CPPC spec"
- depends on ACPI
+ depends on ACPI_PROCESSOR
select ACPI_CPPC_LIB
default n
help
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 1e46c39..9f5a804 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -77,6 +77,7 @@
obj-$(CONFIG_ARM_STI_CPUFREQ) += sti-cpufreq.o
obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o
obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o
+obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o
obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o
obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o
@@ -98,6 +99,7 @@
# Other platform drivers
obj-$(CONFIG_AVR32_AT32AP_CPUFREQ) += at32ap-cpufreq.o
obj-$(CONFIG_BFIN_CPU_FREQ) += blackfin-cpufreq.o
+obj-$(CONFIG_BMIPS_CPUFREQ) += bmips-cpufreq.o
obj-$(CONFIG_CRIS_MACH_ARTPEC3) += cris-artpec3-cpufreq.o
obj-$(CONFIG_ETRAXFS) += cris-etraxfs-cpufreq.o
obj-$(CONFIG_IA64_ACPI_CPUFREQ) += ia64-acpi-cpufreq.o
diff --git a/drivers/cpufreq/bmips-cpufreq.c b/drivers/cpufreq/bmips-cpufreq.c
new file mode 100644
index 0000000..1653151
--- /dev/null
+++ b/drivers/cpufreq/bmips-cpufreq.c
@@ -0,0 +1,188 @@
+/*
+ * CPU frequency scaling for Broadcom BMIPS SoCs
+ *
+ * Copyright (c) 2017 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpufreq.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+/* for mips_hpt_frequency */
+#include <asm/time.h>
+
+#define BMIPS_CPUFREQ_PREFIX "bmips"
+#define BMIPS_CPUFREQ_NAME BMIPS_CPUFREQ_PREFIX "-cpufreq"
+
+#define TRANSITION_LATENCY (25 * 1000) /* 25 us */
+
+#define BMIPS5_CLK_DIV_SET_SHIFT 0x7
+#define BMIPS5_CLK_DIV_SHIFT 0x4
+#define BMIPS5_CLK_DIV_MASK 0xf
+
+enum bmips_type {
+ BMIPS5000,
+ BMIPS5200,
+};
+
+struct cpufreq_compat {
+ const char *compatible;
+ unsigned int bmips_type;
+ unsigned int clk_mult;
+ unsigned int max_freqs;
+};
+
+#define BMIPS(c, t, m, f) { \
+ .compatible = c, \
+ .bmips_type = (t), \
+ .clk_mult = (m), \
+ .max_freqs = (f), \
+}
+
+static struct cpufreq_compat bmips_cpufreq_compat[] = {
+ BMIPS("brcm,bmips5000", BMIPS5000, 8, 4),
+ BMIPS("brcm,bmips5200", BMIPS5200, 8, 4),
+ { }
+};
+
+static struct cpufreq_compat *priv;
+
+static int htp_freq_to_cpu_freq(unsigned int clk_mult)
+{
+ return mips_hpt_frequency * clk_mult / 1000;
+}
+
+static struct cpufreq_frequency_table *
+bmips_cpufreq_get_freq_table(const struct cpufreq_policy *policy)
+{
+ struct cpufreq_frequency_table *table;
+ unsigned long cpu_freq;
+ int i;
+
+ cpu_freq = htp_freq_to_cpu_freq(priv->clk_mult);
+
+ table = kmalloc((priv->max_freqs + 1) * sizeof(*table), GFP_KERNEL);
+ if (!table)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < priv->max_freqs; i++) {
+ table[i].frequency = cpu_freq / (1 << i);
+ table[i].driver_data = i;
+ }
+ table[i].frequency = CPUFREQ_TABLE_END;
+
+ return table;
+}
+
+static unsigned int bmips_cpufreq_get(unsigned int cpu)
+{
+ unsigned int div;
+ uint32_t mode;
+
+ switch (priv->bmips_type) {
+ case BMIPS5200:
+ case BMIPS5000:
+ mode = read_c0_brcm_mode();
+ div = ((mode >> BMIPS5_CLK_DIV_SHIFT) & BMIPS5_CLK_DIV_MASK);
+ break;
+ default:
+ div = 0;
+ }
+
+ return htp_freq_to_cpu_freq(priv->clk_mult) / (1 << div);
+}
+
+static int bmips_cpufreq_target_index(struct cpufreq_policy *policy,
+ unsigned int index)
+{
+ unsigned int div = policy->freq_table[index].driver_data;
+
+ switch (priv->bmips_type) {
+ case BMIPS5200:
+ case BMIPS5000:
+ change_c0_brcm_mode(BMIPS5_CLK_DIV_MASK << BMIPS5_CLK_DIV_SHIFT,
+ (1 << BMIPS5_CLK_DIV_SET_SHIFT) |
+ (div << BMIPS5_CLK_DIV_SHIFT));
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+static int bmips_cpufreq_exit(struct cpufreq_policy *policy)
+{
+ kfree(policy->freq_table);
+
+ return 0;
+}
+
+static int bmips_cpufreq_init(struct cpufreq_policy *policy)
+{
+ struct cpufreq_frequency_table *freq_table;
+ int ret;
+
+ freq_table = bmips_cpufreq_get_freq_table(policy);
+ if (IS_ERR(freq_table)) {
+ ret = PTR_ERR(freq_table);
+ pr_err("%s: couldn't determine frequency table (%d).\n",
+ BMIPS_CPUFREQ_NAME, ret);
+ return ret;
+ }
+
+ ret = cpufreq_generic_init(policy, freq_table, TRANSITION_LATENCY);
+ if (ret)
+ bmips_cpufreq_exit(policy);
+ else
+ pr_info("%s: registered\n", BMIPS_CPUFREQ_NAME);
+
+ return ret;
+}
+
+static struct cpufreq_driver bmips_cpufreq_driver = {
+ .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
+ .verify = cpufreq_generic_frequency_table_verify,
+ .target_index = bmips_cpufreq_target_index,
+ .get = bmips_cpufreq_get,
+ .init = bmips_cpufreq_init,
+ .exit = bmips_cpufreq_exit,
+ .attr = cpufreq_generic_attr,
+ .name = BMIPS_CPUFREQ_PREFIX,
+};
+
+static int __init bmips_cpufreq_probe(void)
+{
+ struct cpufreq_compat *cc;
+ struct device_node *np;
+
+ for (cc = bmips_cpufreq_compat; cc->compatible; cc++) {
+ np = of_find_compatible_node(NULL, "cpu", cc->compatible);
+ if (np) {
+ of_node_put(np);
+ priv = cc;
+ break;
+ }
+ }
+
+ /* We hit the guard element of the array. No compatible CPU found. */
+ if (!cc->compatible)
+ return -ENODEV;
+
+ return cpufreq_register_driver(&bmips_cpufreq_driver);
+}
+device_initcall(bmips_cpufreq_probe);
+
+MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
+MODULE_DESCRIPTION("CPUfreq driver for Broadcom BMIPS SoCs");
+MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/brcmstb-avs-cpufreq.c b/drivers/cpufreq/brcmstb-avs-cpufreq.c
index c943606..7281a2c 100644
--- a/drivers/cpufreq/brcmstb-avs-cpufreq.c
+++ b/drivers/cpufreq/brcmstb-avs-cpufreq.c
@@ -878,7 +878,6 @@
iounmap(priv->avs_intr_base);
unmap_base:
iounmap(priv->base);
- platform_set_drvdata(pdev, NULL);
return ret;
}
@@ -1042,7 +1041,6 @@
priv = platform_get_drvdata(pdev);
iounmap(priv->base);
iounmap(priv->avs_intr_base);
- platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
index 7fcaf26..921b4a6 100644
--- a/drivers/cpufreq/cpufreq-dt-platdev.c
+++ b/drivers/cpufreq/cpufreq-dt-platdev.c
@@ -87,8 +87,6 @@
{ .compatible = "socionext,uniphier-ld11", },
{ .compatible = "socionext,uniphier-ld20", },
- { .compatible = "ti,am33xx", },
- { .compatible = "ti,dra7", },
{ .compatible = "ti,omap2", },
{ .compatible = "ti,omap3", },
{ .compatible = "ti,omap4", },
diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c
index 2690133..c943787 100644
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -148,7 +148,6 @@
struct private_data *priv;
struct device *cpu_dev;
struct clk *cpu_clk;
- struct dev_pm_opp *suspend_opp;
unsigned int transition_latency;
bool fallback = false;
const char *name;
@@ -252,11 +251,7 @@
policy->driver_data = priv;
policy->clk = cpu_clk;
- rcu_read_lock();
- suspend_opp = dev_pm_opp_get_suspend_opp(cpu_dev);
- if (suspend_opp)
- policy->suspend_freq = dev_pm_opp_get_freq(suspend_opp) / 1000;
- rcu_read_unlock();
+ policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000;
ret = cpufreq_table_validate_and_show(policy, freq_table);
if (ret) {
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index cc475ef..a475432 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -132,7 +132,7 @@
u64 cur_wall_time;
u64 busy_time;
- cur_wall_time = jiffies64_to_cputime64(get_jiffies_64());
+ cur_wall_time = jiffies64_to_nsecs(get_jiffies_64());
busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER];
busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM];
@@ -143,9 +143,9 @@
idle_time = cur_wall_time - busy_time;
if (wall)
- *wall = cputime_to_usecs(cur_wall_time);
+ *wall = div_u64(cur_wall_time, NSEC_PER_USEC);
- return cputime_to_usecs(idle_time);
+ return div_u64(idle_time, NSEC_PER_USEC);
}
u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy)
@@ -1078,15 +1078,11 @@
return NULL;
}
-static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy, bool notify)
+static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy)
{
struct kobject *kobj;
struct completion *cmp;
- if (notify)
- blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
- CPUFREQ_REMOVE_POLICY, policy);
-
down_write(&policy->rwsem);
cpufreq_stats_free_table(policy);
kobj = &policy->kobj;
@@ -1104,7 +1100,7 @@
pr_debug("wait complete\n");
}
-static void cpufreq_policy_free(struct cpufreq_policy *policy, bool notify)
+static void cpufreq_policy_free(struct cpufreq_policy *policy)
{
unsigned long flags;
int cpu;
@@ -1117,7 +1113,7 @@
per_cpu(cpufreq_cpu_data, cpu) = NULL;
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
- cpufreq_policy_put_kobj(policy, notify);
+ cpufreq_policy_put_kobj(policy);
free_cpumask_var(policy->real_cpus);
free_cpumask_var(policy->related_cpus);
free_cpumask_var(policy->cpus);
@@ -1170,8 +1166,6 @@
if (new_policy) {
/* related_cpus should at least include policy->cpus. */
cpumask_copy(policy->related_cpus, policy->cpus);
- /* Clear mask of registered CPUs */
- cpumask_clear(policy->real_cpus);
}
/*
@@ -1244,17 +1238,12 @@
goto out_exit_policy;
cpufreq_stats_create_table(policy);
- blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
- CPUFREQ_CREATE_POLICY, policy);
write_lock_irqsave(&cpufreq_driver_lock, flags);
list_add(&policy->policy_list, &cpufreq_policy_list);
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
}
- blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
- CPUFREQ_START, policy);
-
ret = cpufreq_init_policy(policy);
if (ret) {
pr_err("%s: Failed to initialize policy for cpu: %d (%d)\n",
@@ -1282,7 +1271,7 @@
if (cpufreq_driver->exit)
cpufreq_driver->exit(policy);
out_free_policy:
- cpufreq_policy_free(policy, !new_policy);
+ cpufreq_policy_free(policy);
return ret;
}
@@ -1403,7 +1392,7 @@
remove_cpu_dev_symlink(policy, dev);
if (cpumask_empty(policy->real_cpus))
- cpufreq_policy_free(policy, true);
+ cpufreq_policy_free(policy);
}
/**
diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c
index 0196467..631bd2c 100644
--- a/drivers/cpufreq/cpufreq_governor.c
+++ b/drivers/cpufreq/cpufreq_governor.c
@@ -152,7 +152,7 @@
if (ignore_nice) {
u64 cur_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE];
- idle_time += cputime_to_usecs(cur_nice - j_cdbs->prev_cpu_nice);
+ idle_time += div_u64(cur_nice - j_cdbs->prev_cpu_nice, NSEC_PER_USEC);
j_cdbs->prev_cpu_nice = cur_nice;
}
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c
index ac284e6..f570ead 100644
--- a/drivers/cpufreq/cpufreq_stats.c
+++ b/drivers/cpufreq/cpufreq_stats.c
@@ -13,7 +13,6 @@
#include <linux/cpufreq.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/cputime.h>
static DEFINE_SPINLOCK(cpufreq_stats_lock);
@@ -25,9 +24,7 @@
unsigned int last_index;
u64 *time_in_state;
unsigned int *freq_table;
-#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
unsigned int *trans_table;
-#endif
};
static int cpufreq_stats_update(struct cpufreq_stats *stats)
@@ -46,9 +43,7 @@
unsigned int count = stats->max_state;
memset(stats->time_in_state, 0, count * sizeof(u64));
-#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
memset(stats->trans_table, 0, count * count * sizeof(int));
-#endif
stats->last_time = get_jiffies_64();
stats->total_trans = 0;
}
@@ -84,7 +79,6 @@
return count;
}
-#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
{
struct cpufreq_stats *stats = policy->stats;
@@ -129,7 +123,6 @@
return len;
}
cpufreq_freq_attr_ro(trans_table);
-#endif
cpufreq_freq_attr_ro(total_trans);
cpufreq_freq_attr_ro(time_in_state);
@@ -139,9 +132,7 @@
&total_trans.attr,
&time_in_state.attr,
&reset.attr,
-#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
&trans_table.attr,
-#endif
NULL
};
static struct attribute_group stats_attr_group = {
@@ -200,9 +191,7 @@
alloc_size = count * sizeof(int) + count * sizeof(u64);
-#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
alloc_size += count * count * sizeof(int);
-#endif
/* Allocate memory for time_in_state/freq_table/trans_table in one go */
stats->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
@@ -211,9 +200,7 @@
stats->freq_table = (unsigned int *)(stats->time_in_state + count);
-#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
stats->trans_table = stats->freq_table + count;
-#endif
stats->max_state = count;
@@ -259,8 +246,6 @@
cpufreq_stats_update(stats);
stats->last_index = new_index;
-#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
stats->trans_table[old_index * stats->max_state + new_index]++;
-#endif
stats->total_trans++;
}
diff --git a/drivers/cpufreq/exynos5440-cpufreq.c b/drivers/cpufreq/exynos5440-cpufreq.c
index c0f3373..9180d34 100644
--- a/drivers/cpufreq/exynos5440-cpufreq.c
+++ b/drivers/cpufreq/exynos5440-cpufreq.c
@@ -118,12 +118,10 @@
unsigned int tmp, clk_div, ema_div, freq, volt_id;
struct dev_pm_opp *opp;
- rcu_read_lock();
cpufreq_for_each_entry(pos, freq_tbl) {
opp = dev_pm_opp_find_freq_exact(dvfs_info->dev,
pos->frequency * 1000, true);
if (IS_ERR(opp)) {
- rcu_read_unlock();
dev_err(dvfs_info->dev,
"failed to find valid OPP for %u KHZ\n",
pos->frequency);
@@ -140,6 +138,7 @@
/* Calculate EMA */
volt_id = dev_pm_opp_get_voltage(opp);
+
volt_id = (MAX_VOLTAGE - volt_id) / VOLTAGE_STEP;
if (volt_id < PMIC_HIGH_VOLT) {
ema_div = (CPUEMA_HIGH << P0_7_CPUEMA_SHIFT) |
@@ -157,9 +156,9 @@
__raw_writel(tmp, dvfs_info->base + XMU_PMU_P0_7 + 4 *
(pos - freq_tbl));
+ dev_pm_opp_put(opp);
}
- rcu_read_unlock();
return 0;
}
diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c
index ef1fa81..7719b02 100644
--- a/drivers/cpufreq/imx6q-cpufreq.c
+++ b/drivers/cpufreq/imx6q-cpufreq.c
@@ -53,16 +53,15 @@
freq_hz = new_freq * 1000;
old_freq = clk_get_rate(arm_clk) / 1000;
- rcu_read_lock();
opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz);
if (IS_ERR(opp)) {
- rcu_read_unlock();
dev_err(cpu_dev, "failed to find OPP for %ld\n", freq_hz);
return PTR_ERR(opp);
}
volt = dev_pm_opp_get_voltage(opp);
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
+
volt_old = regulator_get_voltage(arm_reg);
dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n",
@@ -321,14 +320,15 @@
* freq_table initialised from OPP is therefore sorted in the
* same order.
*/
- rcu_read_lock();
opp = dev_pm_opp_find_freq_exact(cpu_dev,
freq_table[0].frequency * 1000, true);
min_volt = dev_pm_opp_get_voltage(opp);
+ dev_pm_opp_put(opp);
opp = dev_pm_opp_find_freq_exact(cpu_dev,
freq_table[--num].frequency * 1000, true);
max_volt = dev_pm_opp_get_voltage(opp);
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
+
ret = regulator_set_voltage_time(arm_reg, min_volt, max_volt);
if (ret > 0)
transition_latency += ret * 1000;
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 50bd6d9..eb0f7fb 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -358,6 +358,8 @@
static int hwp_active __read_mostly;
static bool per_cpu_limits __read_mostly;
+static bool driver_registered __read_mostly;
+
#ifdef CONFIG_ACPI
static bool acpi_ppc;
#endif
@@ -394,6 +396,7 @@
static struct perf_limits *limits = &powersave_limits;
#endif
+static DEFINE_MUTEX(intel_pstate_driver_lock);
static DEFINE_MUTEX(intel_pstate_limits_lock);
#ifdef CONFIG_ACPI
@@ -538,7 +541,6 @@
acpi_processor_unregister_performance(policy->cpu);
}
-
#else
static inline void intel_pstate_init_acpi_perf_limits(struct cpufreq_policy *policy)
{
@@ -873,7 +875,10 @@
rdmsrl_on_cpu(cpu, MSR_HWP_CAPABILITIES, &cap);
hw_min = HWP_LOWEST_PERF(cap);
- hw_max = HWP_HIGHEST_PERF(cap);
+ if (limits->no_turbo)
+ hw_max = HWP_GUARANTEED_PERF(cap);
+ else
+ hw_max = HWP_HIGHEST_PERF(cap);
range = hw_max - hw_min;
max_perf_pct = perf_limits->max_perf_pct;
@@ -887,11 +892,6 @@
adj_range = max_perf_pct * range / 100;
max = hw_min + adj_range;
- if (limits->no_turbo) {
- hw_max = HWP_GUARANTEED_PERF(cap);
- if (hw_max < max)
- max = hw_max;
- }
value &= ~HWP_MAX_PERF(~0L);
value |= HWP_MAX_PERF(max);
@@ -1007,37 +1007,59 @@
}
DEFINE_SIMPLE_ATTRIBUTE(fops_pid_param, pid_param_get, pid_param_set, "%llu\n");
+static struct dentry *debugfs_parent;
+
struct pid_param {
char *name;
void *value;
+ struct dentry *dentry;
};
static struct pid_param pid_files[] = {
- {"sample_rate_ms", &pid_params.sample_rate_ms},
- {"d_gain_pct", &pid_params.d_gain_pct},
- {"i_gain_pct", &pid_params.i_gain_pct},
- {"deadband", &pid_params.deadband},
- {"setpoint", &pid_params.setpoint},
- {"p_gain_pct", &pid_params.p_gain_pct},
- {NULL, NULL}
+ {"sample_rate_ms", &pid_params.sample_rate_ms, },
+ {"d_gain_pct", &pid_params.d_gain_pct, },
+ {"i_gain_pct", &pid_params.i_gain_pct, },
+ {"deadband", &pid_params.deadband, },
+ {"setpoint", &pid_params.setpoint, },
+ {"p_gain_pct", &pid_params.p_gain_pct, },
+ {NULL, NULL, }
};
-static void __init intel_pstate_debug_expose_params(void)
+static void intel_pstate_debug_expose_params(void)
{
- struct dentry *debugfs_parent;
- int i = 0;
+ int i;
debugfs_parent = debugfs_create_dir("pstate_snb", NULL);
if (IS_ERR_OR_NULL(debugfs_parent))
return;
- while (pid_files[i].name) {
- debugfs_create_file(pid_files[i].name, 0660,
- debugfs_parent, pid_files[i].value,
- &fops_pid_param);
- i++;
+
+ for (i = 0; pid_files[i].name; i++) {
+ struct dentry *dentry;
+
+ dentry = debugfs_create_file(pid_files[i].name, 0660,
+ debugfs_parent, pid_files[i].value,
+ &fops_pid_param);
+ if (!IS_ERR(dentry))
+ pid_files[i].dentry = dentry;
}
}
+static void intel_pstate_debug_hide_params(void)
+{
+ int i;
+
+ if (IS_ERR_OR_NULL(debugfs_parent))
+ return;
+
+ for (i = 0; pid_files[i].name; i++) {
+ debugfs_remove(pid_files[i].dentry);
+ pid_files[i].dentry = NULL;
+ }
+
+ debugfs_remove(debugfs_parent);
+ debugfs_parent = NULL;
+}
+
/************************** debugfs end ************************/
/************************** sysfs begin ************************/
@@ -1048,6 +1070,34 @@
return sprintf(buf, "%u\n", limits->object); \
}
+static ssize_t intel_pstate_show_status(char *buf);
+static int intel_pstate_update_status(const char *buf, size_t size);
+
+static ssize_t show_status(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ ssize_t ret;
+
+ mutex_lock(&intel_pstate_driver_lock);
+ ret = intel_pstate_show_status(buf);
+ mutex_unlock(&intel_pstate_driver_lock);
+
+ return ret;
+}
+
+static ssize_t store_status(struct kobject *a, struct attribute *b,
+ const char *buf, size_t count)
+{
+ char *p = memchr(buf, '\n', count);
+ int ret;
+
+ mutex_lock(&intel_pstate_driver_lock);
+ ret = intel_pstate_update_status(buf, p ? p - buf : count);
+ mutex_unlock(&intel_pstate_driver_lock);
+
+ return ret < 0 ? ret : count;
+}
+
static ssize_t show_turbo_pct(struct kobject *kobj,
struct attribute *attr, char *buf)
{
@@ -1055,12 +1105,22 @@
int total, no_turbo, turbo_pct;
uint32_t turbo_fp;
+ mutex_lock(&intel_pstate_driver_lock);
+
+ if (!driver_registered) {
+ mutex_unlock(&intel_pstate_driver_lock);
+ return -EAGAIN;
+ }
+
cpu = all_cpu_data[0];
total = cpu->pstate.turbo_pstate - cpu->pstate.min_pstate + 1;
no_turbo = cpu->pstate.max_pstate - cpu->pstate.min_pstate + 1;
turbo_fp = div_fp(no_turbo, total);
turbo_pct = 100 - fp_toint(mul_fp(turbo_fp, int_tofp(100)));
+
+ mutex_unlock(&intel_pstate_driver_lock);
+
return sprintf(buf, "%u\n", turbo_pct);
}
@@ -1070,8 +1130,18 @@
struct cpudata *cpu;
int total;
+ mutex_lock(&intel_pstate_driver_lock);
+
+ if (!driver_registered) {
+ mutex_unlock(&intel_pstate_driver_lock);
+ return -EAGAIN;
+ }
+
cpu = all_cpu_data[0];
total = cpu->pstate.turbo_pstate - cpu->pstate.min_pstate + 1;
+
+ mutex_unlock(&intel_pstate_driver_lock);
+
return sprintf(buf, "%u\n", total);
}
@@ -1080,12 +1150,21 @@
{
ssize_t ret;
+ mutex_lock(&intel_pstate_driver_lock);
+
+ if (!driver_registered) {
+ mutex_unlock(&intel_pstate_driver_lock);
+ return -EAGAIN;
+ }
+
update_turbo_state();
if (limits->turbo_disabled)
ret = sprintf(buf, "%u\n", limits->turbo_disabled);
else
ret = sprintf(buf, "%u\n", limits->no_turbo);
+ mutex_unlock(&intel_pstate_driver_lock);
+
return ret;
}
@@ -1099,12 +1178,20 @@
if (ret != 1)
return -EINVAL;
+ mutex_lock(&intel_pstate_driver_lock);
+
+ if (!driver_registered) {
+ mutex_unlock(&intel_pstate_driver_lock);
+ return -EAGAIN;
+ }
+
mutex_lock(&intel_pstate_limits_lock);
update_turbo_state();
if (limits->turbo_disabled) {
pr_warn("Turbo disabled by BIOS or unavailable on processor\n");
mutex_unlock(&intel_pstate_limits_lock);
+ mutex_unlock(&intel_pstate_driver_lock);
return -EPERM;
}
@@ -1114,6 +1201,8 @@
intel_pstate_update_policies();
+ mutex_unlock(&intel_pstate_driver_lock);
+
return count;
}
@@ -1127,6 +1216,13 @@
if (ret != 1)
return -EINVAL;
+ mutex_lock(&intel_pstate_driver_lock);
+
+ if (!driver_registered) {
+ mutex_unlock(&intel_pstate_driver_lock);
+ return -EAGAIN;
+ }
+
mutex_lock(&intel_pstate_limits_lock);
limits->max_sysfs_pct = clamp_t(int, input, 0 , 100);
@@ -1142,6 +1238,8 @@
intel_pstate_update_policies();
+ mutex_unlock(&intel_pstate_driver_lock);
+
return count;
}
@@ -1155,6 +1253,13 @@
if (ret != 1)
return -EINVAL;
+ mutex_lock(&intel_pstate_driver_lock);
+
+ if (!driver_registered) {
+ mutex_unlock(&intel_pstate_driver_lock);
+ return -EAGAIN;
+ }
+
mutex_lock(&intel_pstate_limits_lock);
limits->min_sysfs_pct = clamp_t(int, input, 0 , 100);
@@ -1170,12 +1275,15 @@
intel_pstate_update_policies();
+ mutex_unlock(&intel_pstate_driver_lock);
+
return count;
}
show_one(max_perf_pct, max_perf_pct);
show_one(min_perf_pct, min_perf_pct);
+define_one_global_rw(status);
define_one_global_rw(no_turbo);
define_one_global_rw(max_perf_pct);
define_one_global_rw(min_perf_pct);
@@ -1183,6 +1291,7 @@
define_one_global_ro(num_pstates);
static struct attribute *intel_pstate_attributes[] = {
+ &status.attr,
&no_turbo.attr,
&turbo_pct.attr,
&num_pstates.attr,
@@ -1364,48 +1473,71 @@
return (value >> 8) & 0xFF;
}
+static int core_get_tdp_ratio(u64 plat_info)
+{
+ /* Check how many TDP levels present */
+ if (plat_info & 0x600000000) {
+ u64 tdp_ctrl;
+ u64 tdp_ratio;
+ int tdp_msr;
+ int err;
+
+ /* Get the TDP level (0, 1, 2) to get ratios */
+ err = rdmsrl_safe(MSR_CONFIG_TDP_CONTROL, &tdp_ctrl);
+ if (err)
+ return err;
+
+ /* TDP MSR are continuous starting at 0x648 */
+ tdp_msr = MSR_CONFIG_TDP_NOMINAL + (tdp_ctrl & 0x03);
+ err = rdmsrl_safe(tdp_msr, &tdp_ratio);
+ if (err)
+ return err;
+
+ /* For level 1 and 2, bits[23:16] contain the ratio */
+ if (tdp_ctrl & 0x03)
+ tdp_ratio >>= 16;
+
+ tdp_ratio &= 0xff; /* ratios are only 8 bits long */
+ pr_debug("tdp_ratio %x\n", (int)tdp_ratio);
+
+ return (int)tdp_ratio;
+ }
+
+ return -ENXIO;
+}
+
static int core_get_max_pstate(void)
{
u64 tar;
u64 plat_info;
int max_pstate;
+ int tdp_ratio;
int err;
rdmsrl(MSR_PLATFORM_INFO, plat_info);
max_pstate = (plat_info >> 8) & 0xFF;
+ tdp_ratio = core_get_tdp_ratio(plat_info);
+ if (tdp_ratio <= 0)
+ return max_pstate;
+
+ if (hwp_active) {
+ /* Turbo activation ratio is not used on HWP platforms */
+ return tdp_ratio;
+ }
+
err = rdmsrl_safe(MSR_TURBO_ACTIVATION_RATIO, &tar);
if (!err) {
+ int tar_levels;
+
/* Do some sanity checking for safety */
- if (plat_info & 0x600000000) {
- u64 tdp_ctrl;
- u64 tdp_ratio;
- int tdp_msr;
-
- err = rdmsrl_safe(MSR_CONFIG_TDP_CONTROL, &tdp_ctrl);
- if (err)
- goto skip_tar;
-
- tdp_msr = MSR_CONFIG_TDP_NOMINAL + (tdp_ctrl & 0x3);
- err = rdmsrl_safe(tdp_msr, &tdp_ratio);
- if (err)
- goto skip_tar;
-
- /* For level 1 and 2, bits[23:16] contain the ratio */
- if (tdp_ctrl)
- tdp_ratio >>= 16;
-
- tdp_ratio &= 0xff; /* ratios are only 8 bits long */
- if (tdp_ratio - 1 == tar) {
- max_pstate = tar;
- pr_debug("max_pstate=TAC %x\n", max_pstate);
- } else {
- goto skip_tar;
- }
+ tar_levels = tar & 0xff;
+ if (tdp_ratio - 1 == tar_levels) {
+ max_pstate = tar_levels;
+ pr_debug("max_pstate=TAC %x\n", max_pstate);
}
}
-skip_tar:
return max_pstate;
}
@@ -2072,6 +2204,20 @@
static int intel_pstate_verify_policy(struct cpufreq_policy *policy)
{
+ struct cpudata *cpu = all_cpu_data[policy->cpu];
+ struct perf_limits *perf_limits;
+
+ if (policy->policy == CPUFREQ_POLICY_PERFORMANCE)
+ perf_limits = &performance_limits;
+ else
+ perf_limits = &powersave_limits;
+
+ update_turbo_state();
+ policy->cpuinfo.max_freq = perf_limits->turbo_disabled ||
+ perf_limits->no_turbo ?
+ cpu->pstate.max_freq :
+ cpu->pstate.turbo_freq;
+
cpufreq_verify_within_cpu_limits(policy);
if (policy->policy != CPUFREQ_POLICY_POWERSAVE &&
@@ -2299,6 +2445,111 @@
static struct cpufreq_driver *intel_pstate_driver = &intel_pstate;
+static void intel_pstate_driver_cleanup(void)
+{
+ unsigned int cpu;
+
+ get_online_cpus();
+ for_each_online_cpu(cpu) {
+ if (all_cpu_data[cpu]) {
+ if (intel_pstate_driver == &intel_pstate)
+ intel_pstate_clear_update_util_hook(cpu);
+
+ kfree(all_cpu_data[cpu]);
+ all_cpu_data[cpu] = NULL;
+ }
+ }
+ put_online_cpus();
+}
+
+static int intel_pstate_register_driver(void)
+{
+ int ret;
+
+ ret = cpufreq_register_driver(intel_pstate_driver);
+ if (ret) {
+ intel_pstate_driver_cleanup();
+ return ret;
+ }
+
+ mutex_lock(&intel_pstate_limits_lock);
+ driver_registered = true;
+ mutex_unlock(&intel_pstate_limits_lock);
+
+ if (intel_pstate_driver == &intel_pstate && !hwp_active &&
+ pstate_funcs.get_target_pstate != get_target_pstate_use_cpu_load)
+ intel_pstate_debug_expose_params();
+
+ return 0;
+}
+
+static int intel_pstate_unregister_driver(void)
+{
+ if (hwp_active)
+ return -EBUSY;
+
+ if (intel_pstate_driver == &intel_pstate && !hwp_active &&
+ pstate_funcs.get_target_pstate != get_target_pstate_use_cpu_load)
+ intel_pstate_debug_hide_params();
+
+ mutex_lock(&intel_pstate_limits_lock);
+ driver_registered = false;
+ mutex_unlock(&intel_pstate_limits_lock);
+
+ cpufreq_unregister_driver(intel_pstate_driver);
+ intel_pstate_driver_cleanup();
+
+ return 0;
+}
+
+static ssize_t intel_pstate_show_status(char *buf)
+{
+ if (!driver_registered)
+ return sprintf(buf, "off\n");
+
+ return sprintf(buf, "%s\n", intel_pstate_driver == &intel_pstate ?
+ "active" : "passive");
+}
+
+static int intel_pstate_update_status(const char *buf, size_t size)
+{
+ int ret;
+
+ if (size == 3 && !strncmp(buf, "off", size))
+ return driver_registered ?
+ intel_pstate_unregister_driver() : -EINVAL;
+
+ if (size == 6 && !strncmp(buf, "active", size)) {
+ if (driver_registered) {
+ if (intel_pstate_driver == &intel_pstate)
+ return 0;
+
+ ret = intel_pstate_unregister_driver();
+ if (ret)
+ return ret;
+ }
+
+ intel_pstate_driver = &intel_pstate;
+ return intel_pstate_register_driver();
+ }
+
+ if (size == 7 && !strncmp(buf, "passive", size)) {
+ if (driver_registered) {
+ if (intel_pstate_driver != &intel_pstate)
+ return 0;
+
+ ret = intel_pstate_unregister_driver();
+ if (ret)
+ return ret;
+ }
+
+ intel_pstate_driver = &intel_cpufreq;
+ return intel_pstate_register_driver();
+ }
+
+ return -EINVAL;
+}
+
static int no_load __initdata;
static int no_hwp __initdata;
static int hwp_only __initdata;
@@ -2486,9 +2737,9 @@
static int __init intel_pstate_init(void)
{
- int cpu, rc = 0;
const struct x86_cpu_id *id;
struct cpu_defaults *cpu_def;
+ int rc = 0;
if (no_load)
return -ENODEV;
@@ -2520,45 +2771,29 @@
if (intel_pstate_platform_pwr_mgmt_exists())
return -ENODEV;
+ if (!hwp_active && hwp_only)
+ return -ENOTSUPP;
+
pr_info("Intel P-state driver initializing\n");
all_cpu_data = vzalloc(sizeof(void *) * num_possible_cpus());
if (!all_cpu_data)
return -ENOMEM;
- if (!hwp_active && hwp_only)
- goto out;
-
intel_pstate_request_control_from_smm();
- rc = cpufreq_register_driver(intel_pstate_driver);
- if (rc)
- goto out;
-
- if (intel_pstate_driver == &intel_pstate && !hwp_active &&
- pstate_funcs.get_target_pstate != get_target_pstate_use_cpu_load)
- intel_pstate_debug_expose_params();
-
intel_pstate_sysfs_expose_params();
+ mutex_lock(&intel_pstate_driver_lock);
+ rc = intel_pstate_register_driver();
+ mutex_unlock(&intel_pstate_driver_lock);
+ if (rc)
+ return rc;
+
if (hwp_active)
pr_info("HWP enabled\n");
- return rc;
-out:
- get_online_cpus();
- for_each_online_cpu(cpu) {
- if (all_cpu_data[cpu]) {
- if (intel_pstate_driver == &intel_pstate)
- intel_pstate_clear_update_util_hook(cpu);
-
- kfree(all_cpu_data[cpu]);
- }
- }
-
- put_online_cpus();
- vfree(all_cpu_data);
- return -ENODEV;
+ return 0;
}
device_initcall(intel_pstate_init);
diff --git a/drivers/cpufreq/mt8173-cpufreq.c b/drivers/cpufreq/mt8173-cpufreq.c
index 643f431..ab25b123 100644
--- a/drivers/cpufreq/mt8173-cpufreq.c
+++ b/drivers/cpufreq/mt8173-cpufreq.c
@@ -232,16 +232,14 @@
freq_hz = freq_table[index].frequency * 1000;
- rcu_read_lock();
opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz);
if (IS_ERR(opp)) {
- rcu_read_unlock();
pr_err("cpu%d: failed to find OPP for %ld\n",
policy->cpu, freq_hz);
return PTR_ERR(opp);
}
vproc = dev_pm_opp_get_voltage(opp);
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
/*
* If the new voltage or the intermediate voltage is higher than the
@@ -411,16 +409,14 @@
/* Search a safe voltage for intermediate frequency. */
rate = clk_get_rate(inter_clk);
- rcu_read_lock();
opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
if (IS_ERR(opp)) {
- rcu_read_unlock();
pr_err("failed to get intermediate opp for cpu%d\n", cpu);
ret = PTR_ERR(opp);
goto out_free_opp_table;
}
info->intermediate_voltage = dev_pm_opp_get_voltage(opp);
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
info->cpu_dev = cpu_dev;
info->proc_reg = proc_reg;
diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c
index 376e63c..71e81bb 100644
--- a/drivers/cpufreq/omap-cpufreq.c
+++ b/drivers/cpufreq/omap-cpufreq.c
@@ -63,16 +63,14 @@
freq = ret;
if (mpu_reg) {
- rcu_read_lock();
opp = dev_pm_opp_find_freq_ceil(mpu_dev, &freq);
if (IS_ERR(opp)) {
- rcu_read_unlock();
dev_err(mpu_dev, "%s: unable to find MPU OPP for %d\n",
__func__, new_freq);
return -EINVAL;
}
volt = dev_pm_opp_get_voltage(opp);
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
tol = volt * OPP_TOLERANCE / 100;
volt_old = regulator_get_voltage(mpu_reg);
}
diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c
index 37671b5..3ff5160 100644
--- a/drivers/cpufreq/powernv-cpufreq.c
+++ b/drivers/cpufreq/powernv-cpufreq.c
@@ -144,6 +144,7 @@
unsigned int max;
unsigned int nominal;
unsigned int nr_pstates;
+ bool wof_enabled;
} powernv_pstate_info;
/* Use following macros for conversions between pstate_id and index */
@@ -203,6 +204,7 @@
const __be32 *pstate_ids, *pstate_freqs;
u32 len_ids, len_freqs;
u32 pstate_min, pstate_max, pstate_nominal;
+ u32 pstate_turbo, pstate_ultra_turbo;
power_mgt = of_find_node_by_path("/ibm,opal/power-mgt");
if (!power_mgt) {
@@ -225,8 +227,29 @@
pr_warn("ibm,pstate-nominal not found\n");
return -ENODEV;
}
+
+ if (of_property_read_u32(power_mgt, "ibm,pstate-ultra-turbo",
+ &pstate_ultra_turbo)) {
+ powernv_pstate_info.wof_enabled = false;
+ goto next;
+ }
+
+ if (of_property_read_u32(power_mgt, "ibm,pstate-turbo",
+ &pstate_turbo)) {
+ powernv_pstate_info.wof_enabled = false;
+ goto next;
+ }
+
+ if (pstate_turbo == pstate_ultra_turbo)
+ powernv_pstate_info.wof_enabled = false;
+ else
+ powernv_pstate_info.wof_enabled = true;
+
+next:
pr_info("cpufreq pstate min %d nominal %d max %d\n", pstate_min,
pstate_nominal, pstate_max);
+ pr_info("Workload Optimized Frequency is %s in the platform\n",
+ (powernv_pstate_info.wof_enabled) ? "enabled" : "disabled");
pstate_ids = of_get_property(power_mgt, "ibm,pstate-ids", &len_ids);
if (!pstate_ids) {
@@ -268,6 +291,13 @@
powernv_pstate_info.nominal = i;
else if (id == pstate_min)
powernv_pstate_info.min = i;
+
+ if (powernv_pstate_info.wof_enabled && id == pstate_turbo) {
+ int j;
+
+ for (j = i - 1; j >= (int)powernv_pstate_info.max; j--)
+ powernv_freqs[j].flags = CPUFREQ_BOOST_FREQ;
+ }
}
/* End of list marker entry */
@@ -305,9 +335,12 @@
struct freq_attr cpufreq_freq_attr_cpuinfo_nominal_freq =
__ATTR_RO(cpuinfo_nominal_freq);
+#define SCALING_BOOST_FREQS_ATTR_INDEX 2
+
static struct freq_attr *powernv_cpu_freq_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
&cpufreq_freq_attr_cpuinfo_nominal_freq,
+ &cpufreq_freq_attr_scaling_boost_freqs,
NULL,
};
@@ -1013,11 +1046,22 @@
register_reboot_notifier(&powernv_cpufreq_reboot_nb);
opal_message_notifier_register(OPAL_MSG_OCC, &powernv_cpufreq_opal_nb);
- rc = cpufreq_register_driver(&powernv_cpufreq_driver);
- if (!rc)
- return 0;
+ if (powernv_pstate_info.wof_enabled)
+ powernv_cpufreq_driver.boost_enabled = true;
+ else
+ powernv_cpu_freq_attr[SCALING_BOOST_FREQS_ATTR_INDEX] = NULL;
- pr_info("Failed to register the cpufreq driver (%d)\n", rc);
+ rc = cpufreq_register_driver(&powernv_cpufreq_driver);
+ if (rc) {
+ pr_info("Failed to register the cpufreq driver (%d)\n", rc);
+ goto cleanup_notifiers;
+ }
+
+ if (powernv_pstate_info.wof_enabled)
+ cpufreq_enable_boost_support();
+
+ return 0;
+cleanup_notifiers:
unregister_all_notifiers();
clean_chip_info();
out:
diff --git a/drivers/cpufreq/ppc_cbe_cpufreq_pmi.c b/drivers/cpufreq/ppc_cbe_cpufreq_pmi.c
index dc11248..eeaa9225 100644
--- a/drivers/cpufreq/ppc_cbe_cpufreq_pmi.c
+++ b/drivers/cpufreq/ppc_cbe_cpufreq_pmi.c
@@ -100,9 +100,6 @@
/* Should this really be called for CPUFREQ_ADJUST and CPUFREQ_NOTIFY
* policy events?)
*/
- if (event == CPUFREQ_START)
- return 0;
-
node = cbe_cpu_to_node(policy->cpu);
pr_debug("got notified, event=%lu, node=%u\n", event, node);
diff --git a/drivers/cpufreq/qoriq-cpufreq.c b/drivers/cpufreq/qoriq-cpufreq.c
index 53d8c3f..a6fefac 100644
--- a/drivers/cpufreq/qoriq-cpufreq.c
+++ b/drivers/cpufreq/qoriq-cpufreq.c
@@ -11,6 +11,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/cpufreq.h>
#include <linux/cpu_cooling.h>
#include <linux/errno.h>
@@ -37,53 +38,20 @@
struct thermal_cooling_device *cdev;
};
+/*
+ * Don't use cpufreq on this SoC -- used when the SoC would have otherwise
+ * matched a more generic compatible.
+ */
+#define SOC_BLACKLIST 1
+
/**
* struct soc_data - SoC specific data
- * @freq_mask: mask the disallowed frequencies
- * @flag: unique flags
+ * @flags: SOC_xxx
*/
struct soc_data {
- u32 freq_mask[4];
- u32 flag;
+ u32 flags;
};
-#define FREQ_MASK 1
-/* see hardware specification for the allowed frqeuencies */
-static const struct soc_data sdata[] = {
- { /* used by p2041 and p3041 */
- .freq_mask = {0x8, 0x8, 0x2, 0x2},
- .flag = FREQ_MASK,
- },
- { /* used by p5020 */
- .freq_mask = {0x8, 0x2},
- .flag = FREQ_MASK,
- },
- { /* used by p4080, p5040 */
- .freq_mask = {0},
- .flag = 0,
- },
-};
-
-/*
- * the minimum allowed core frequency, in Hz
- * for chassis v1.0, >= platform frequency
- * for chassis v2.0, >= platform frequency / 2
- */
-static u32 min_cpufreq;
-static const u32 *fmask;
-
-#if defined(CONFIG_ARM)
-static int get_cpu_physical_id(int cpu)
-{
- return topology_core_id(cpu);
-}
-#else
-static int get_cpu_physical_id(int cpu)
-{
- return get_hard_smp_processor_id(cpu);
-}
-#endif
-
static u32 get_bus_freq(void)
{
struct device_node *soc;
@@ -101,9 +69,10 @@
return sysfreq;
}
-static struct device_node *cpu_to_clk_node(int cpu)
+static struct clk *cpu_to_clk(int cpu)
{
- struct device_node *np, *clk_np;
+ struct device_node *np;
+ struct clk *clk;
if (!cpu_present(cpu))
return NULL;
@@ -112,37 +81,28 @@
if (!np)
return NULL;
- clk_np = of_parse_phandle(np, "clocks", 0);
- if (!clk_np)
- return NULL;
-
+ clk = of_clk_get(np, 0);
of_node_put(np);
-
- return clk_np;
+ return clk;
}
/* traverse cpu nodes to get cpu mask of sharing clock wire */
static void set_affected_cpus(struct cpufreq_policy *policy)
{
- struct device_node *np, *clk_np;
struct cpumask *dstp = policy->cpus;
+ struct clk *clk;
int i;
- np = cpu_to_clk_node(policy->cpu);
- if (!np)
- return;
-
for_each_present_cpu(i) {
- clk_np = cpu_to_clk_node(i);
- if (!clk_np)
+ clk = cpu_to_clk(i);
+ if (IS_ERR(clk)) {
+ pr_err("%s: no clock for cpu %d\n", __func__, i);
continue;
+ }
- if (clk_np == np)
+ if (clk_is_match(policy->clk, clk))
cpumask_set_cpu(i, dstp);
-
- of_node_put(clk_np);
}
- of_node_put(np);
}
/* reduce the duplicated frequencies in frequency table */
@@ -198,10 +158,11 @@
static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
- struct device_node *np, *pnode;
+ struct device_node *np;
int i, count, ret;
- u32 freq, mask;
+ u32 freq;
struct clk *clk;
+ const struct clk_hw *hwclk;
struct cpufreq_frequency_table *table;
struct cpu_data *data;
unsigned int cpu = policy->cpu;
@@ -221,17 +182,13 @@
goto err_nomem2;
}
- pnode = of_parse_phandle(np, "clocks", 0);
- if (!pnode) {
- pr_err("%s: could not get clock information\n", __func__);
- goto err_nomem2;
- }
+ hwclk = __clk_get_hw(policy->clk);
+ count = clk_hw_get_num_parents(hwclk);
- count = of_property_count_strings(pnode, "clock-names");
data->pclk = kcalloc(count, sizeof(struct clk *), GFP_KERNEL);
if (!data->pclk) {
pr_err("%s: no memory\n", __func__);
- goto err_node;
+ goto err_nomem2;
}
table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL);
@@ -240,23 +197,11 @@
goto err_pclk;
}
- if (fmask)
- mask = fmask[get_cpu_physical_id(cpu)];
- else
- mask = 0x0;
-
for (i = 0; i < count; i++) {
- clk = of_clk_get(pnode, i);
+ clk = clk_hw_get_parent_by_index(hwclk, i)->clk;
data->pclk[i] = clk;
freq = clk_get_rate(clk);
- /*
- * the clock is valid if its frequency is not masked
- * and large than minimum allowed frequency.
- */
- if (freq < min_cpufreq || (mask & (1 << i)))
- table[i].frequency = CPUFREQ_ENTRY_INVALID;
- else
- table[i].frequency = freq / 1000;
+ table[i].frequency = freq / 1000;
table[i].driver_data = i;
}
freq_table_redup(table, count);
@@ -282,7 +227,6 @@
policy->cpuinfo.transition_latency = u64temp + 1;
of_node_put(np);
- of_node_put(pnode);
return 0;
@@ -290,10 +234,7 @@
kfree(table);
err_pclk:
kfree(data->pclk);
-err_node:
- of_node_put(pnode);
err_nomem2:
- policy->driver_data = NULL;
kfree(data);
err_np:
of_node_put(np);
@@ -357,12 +298,25 @@
.attr = cpufreq_generic_attr,
};
+static const struct soc_data blacklist = {
+ .flags = SOC_BLACKLIST,
+};
+
static const struct of_device_id node_matches[] __initconst = {
- { .compatible = "fsl,p2041-clockgen", .data = &sdata[0], },
- { .compatible = "fsl,p3041-clockgen", .data = &sdata[0], },
- { .compatible = "fsl,p5020-clockgen", .data = &sdata[1], },
- { .compatible = "fsl,p4080-clockgen", .data = &sdata[2], },
- { .compatible = "fsl,p5040-clockgen", .data = &sdata[2], },
+ /* e6500 cannot use cpufreq due to erratum A-008083 */
+ { .compatible = "fsl,b4420-clockgen", &blacklist },
+ { .compatible = "fsl,b4860-clockgen", &blacklist },
+ { .compatible = "fsl,t2080-clockgen", &blacklist },
+ { .compatible = "fsl,t4240-clockgen", &blacklist },
+
+ { .compatible = "fsl,ls1012a-clockgen", },
+ { .compatible = "fsl,ls1021a-clockgen", },
+ { .compatible = "fsl,ls1043a-clockgen", },
+ { .compatible = "fsl,ls1046a-clockgen", },
+ { .compatible = "fsl,ls1088a-clockgen", },
+ { .compatible = "fsl,ls2080a-clockgen", },
+ { .compatible = "fsl,p4080-clockgen", },
+ { .compatible = "fsl,qoriq-clockgen-1.0", },
{ .compatible = "fsl,qoriq-clockgen-2.0", },
{}
};
@@ -380,16 +334,12 @@
match = of_match_node(node_matches, np);
data = match->data;
- if (data) {
- if (data->flag)
- fmask = data->freq_mask;
- min_cpufreq = get_bus_freq();
- } else {
- min_cpufreq = get_bus_freq() / 2;
- }
of_node_put(np);
+ if (data && data->flags & SOC_BLACKLIST)
+ return -ENODEV;
+
ret = cpufreq_register_driver(&qoriq_cpufreq_driver);
if (!ret)
pr_info("Freescale QorIQ CPU frequency scaling driver\n");
diff --git a/drivers/cpufreq/s3c2416-cpufreq.c b/drivers/cpufreq/s3c2416-cpufreq.c
index d6d4257..5b2db3c 100644
--- a/drivers/cpufreq/s3c2416-cpufreq.c
+++ b/drivers/cpufreq/s3c2416-cpufreq.c
@@ -400,7 +400,6 @@
rate = clk_get_rate(s3c_freq->hclk);
if (rate < 133 * 1000 * 1000) {
pr_err("cpufreq: HCLK not at 133MHz\n");
- clk_put(s3c_freq->hclk);
ret = -EINVAL;
goto err_armclk;
}
diff --git a/drivers/cpufreq/sti-cpufreq.c b/drivers/cpufreq/sti-cpufreq.c
index b366e6d..a7db901 100644
--- a/drivers/cpufreq/sti-cpufreq.c
+++ b/drivers/cpufreq/sti-cpufreq.c
@@ -160,6 +160,7 @@
int pcode, substrate, major, minor;
int ret;
char name[MAX_PCODE_NAME_LEN];
+ struct opp_table *opp_table;
reg_fields = sti_cpufreq_match();
if (!reg_fields) {
@@ -211,20 +212,20 @@
snprintf(name, MAX_PCODE_NAME_LEN, "pcode%d", pcode);
- ret = dev_pm_opp_set_prop_name(dev, name);
- if (ret) {
+ opp_table = dev_pm_opp_set_prop_name(dev, name);
+ if (IS_ERR(opp_table)) {
dev_err(dev, "Failed to set prop name\n");
- return ret;
+ return PTR_ERR(opp_table);
}
version[0] = BIT(major);
version[1] = BIT(minor);
version[2] = BIT(substrate);
- ret = dev_pm_opp_set_supported_hw(dev, version, VERSION_ELEMENTS);
- if (ret) {
+ opp_table = dev_pm_opp_set_supported_hw(dev, version, VERSION_ELEMENTS);
+ if (IS_ERR(opp_table)) {
dev_err(dev, "Failed to set supported hardware\n");
- return ret;
+ return PTR_ERR(opp_table);
}
dev_dbg(dev, "pcode: %d major: %d minor: %d substrate: %d\n",
diff --git a/drivers/cpufreq/ti-cpufreq.c b/drivers/cpufreq/ti-cpufreq.c
new file mode 100644
index 0000000..a7b5658
--- /dev/null
+++ b/drivers/cpufreq/ti-cpufreq.c
@@ -0,0 +1,268 @@
+/*
+ * TI CPUFreq/OPP hw-supported driver
+ *
+ * Copyright (C) 2016-2017 Texas Instruments, Inc.
+ * Dave Gerlach <d-gerlach@ti.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.
+ */
+
+#include <linux/cpu.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pm_opp.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define REVISION_MASK 0xF
+#define REVISION_SHIFT 28
+
+#define AM33XX_800M_ARM_MPU_MAX_FREQ 0x1E2F
+#define AM43XX_600M_ARM_MPU_MAX_FREQ 0xFFA
+
+#define DRA7_EFUSE_HAS_OD_MPU_OPP 11
+#define DRA7_EFUSE_HAS_HIGH_MPU_OPP 15
+#define DRA7_EFUSE_HAS_ALL_MPU_OPP 23
+
+#define DRA7_EFUSE_NOM_MPU_OPP BIT(0)
+#define DRA7_EFUSE_OD_MPU_OPP BIT(1)
+#define DRA7_EFUSE_HIGH_MPU_OPP BIT(2)
+
+#define VERSION_COUNT 2
+
+struct ti_cpufreq_data;
+
+struct ti_cpufreq_soc_data {
+ unsigned long (*efuse_xlate)(struct ti_cpufreq_data *opp_data,
+ unsigned long efuse);
+ unsigned long efuse_fallback;
+ unsigned long efuse_offset;
+ unsigned long efuse_mask;
+ unsigned long efuse_shift;
+ unsigned long rev_offset;
+};
+
+struct ti_cpufreq_data {
+ struct device *cpu_dev;
+ struct device_node *opp_node;
+ struct regmap *syscon;
+ const struct ti_cpufreq_soc_data *soc_data;
+};
+
+static unsigned long amx3_efuse_xlate(struct ti_cpufreq_data *opp_data,
+ unsigned long efuse)
+{
+ if (!efuse)
+ efuse = opp_data->soc_data->efuse_fallback;
+ /* AM335x and AM437x use "OPP disable" bits, so invert */
+ return ~efuse;
+}
+
+static unsigned long dra7_efuse_xlate(struct ti_cpufreq_data *opp_data,
+ unsigned long efuse)
+{
+ unsigned long calculated_efuse = DRA7_EFUSE_NOM_MPU_OPP;
+
+ /*
+ * The efuse on dra7 and am57 parts contains a specific
+ * value indicating the highest available OPP.
+ */
+
+ switch (efuse) {
+ case DRA7_EFUSE_HAS_ALL_MPU_OPP:
+ case DRA7_EFUSE_HAS_HIGH_MPU_OPP:
+ calculated_efuse |= DRA7_EFUSE_HIGH_MPU_OPP;
+ case DRA7_EFUSE_HAS_OD_MPU_OPP:
+ calculated_efuse |= DRA7_EFUSE_OD_MPU_OPP;
+ }
+
+ return calculated_efuse;
+}
+
+static struct ti_cpufreq_soc_data am3x_soc_data = {
+ .efuse_xlate = amx3_efuse_xlate,
+ .efuse_fallback = AM33XX_800M_ARM_MPU_MAX_FREQ,
+ .efuse_offset = 0x07fc,
+ .efuse_mask = 0x1fff,
+ .rev_offset = 0x600,
+};
+
+static struct ti_cpufreq_soc_data am4x_soc_data = {
+ .efuse_xlate = amx3_efuse_xlate,
+ .efuse_fallback = AM43XX_600M_ARM_MPU_MAX_FREQ,
+ .efuse_offset = 0x0610,
+ .efuse_mask = 0x3f,
+ .rev_offset = 0x600,
+};
+
+static struct ti_cpufreq_soc_data dra7_soc_data = {
+ .efuse_xlate = dra7_efuse_xlate,
+ .efuse_offset = 0x020c,
+ .efuse_mask = 0xf80000,
+ .efuse_shift = 19,
+ .rev_offset = 0x204,
+};
+
+/**
+ * ti_cpufreq_get_efuse() - Parse and return efuse value present on SoC
+ * @opp_data: pointer to ti_cpufreq_data context
+ * @efuse_value: Set to the value parsed from efuse
+ *
+ * Returns error code if efuse not read properly.
+ */
+static int ti_cpufreq_get_efuse(struct ti_cpufreq_data *opp_data,
+ u32 *efuse_value)
+{
+ struct device *dev = opp_data->cpu_dev;
+ u32 efuse;
+ int ret;
+
+ ret = regmap_read(opp_data->syscon, opp_data->soc_data->efuse_offset,
+ &efuse);
+ if (ret) {
+ dev_err(dev,
+ "Failed to read the efuse value from syscon: %d\n",
+ ret);
+ return ret;
+ }
+
+ efuse = (efuse & opp_data->soc_data->efuse_mask);
+ efuse >>= opp_data->soc_data->efuse_shift;
+
+ *efuse_value = opp_data->soc_data->efuse_xlate(opp_data, efuse);
+
+ return 0;
+}
+
+/**
+ * ti_cpufreq_get_rev() - Parse and return rev value present on SoC
+ * @opp_data: pointer to ti_cpufreq_data context
+ * @revision_value: Set to the value parsed from revision register
+ *
+ * Returns error code if revision not read properly.
+ */
+static int ti_cpufreq_get_rev(struct ti_cpufreq_data *opp_data,
+ u32 *revision_value)
+{
+ struct device *dev = opp_data->cpu_dev;
+ u32 revision;
+ int ret;
+
+ ret = regmap_read(opp_data->syscon, opp_data->soc_data->rev_offset,
+ &revision);
+ if (ret) {
+ dev_err(dev,
+ "Failed to read the revision number from syscon: %d\n",
+ ret);
+ return ret;
+ }
+
+ *revision_value = BIT((revision >> REVISION_SHIFT) & REVISION_MASK);
+
+ return 0;
+}
+
+static int ti_cpufreq_setup_syscon_register(struct ti_cpufreq_data *opp_data)
+{
+ struct device *dev = opp_data->cpu_dev;
+ struct device_node *np = opp_data->opp_node;
+
+ opp_data->syscon = syscon_regmap_lookup_by_phandle(np,
+ "syscon");
+ if (IS_ERR(opp_data->syscon)) {
+ dev_err(dev,
+ "\"syscon\" is missing, cannot use OPPv2 table.\n");
+ return PTR_ERR(opp_data->syscon);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id ti_cpufreq_of_match[] = {
+ { .compatible = "ti,am33xx", .data = &am3x_soc_data, },
+ { .compatible = "ti,am4372", .data = &am4x_soc_data, },
+ { .compatible = "ti,dra7", .data = &dra7_soc_data },
+ {},
+};
+
+static int ti_cpufreq_init(void)
+{
+ u32 version[VERSION_COUNT];
+ struct device_node *np;
+ const struct of_device_id *match;
+ struct ti_cpufreq_data *opp_data;
+ int ret;
+
+ np = of_find_node_by_path("/");
+ match = of_match_node(ti_cpufreq_of_match, np);
+ if (!match)
+ return -ENODEV;
+
+ opp_data = kzalloc(sizeof(*opp_data), GFP_KERNEL);
+ if (!opp_data)
+ return -ENOMEM;
+
+ opp_data->soc_data = match->data;
+
+ opp_data->cpu_dev = get_cpu_device(0);
+ if (!opp_data->cpu_dev) {
+ pr_err("%s: Failed to get device for CPU0\n", __func__);
+ return -ENODEV;
+ }
+
+ opp_data->opp_node = dev_pm_opp_of_get_opp_desc_node(opp_data->cpu_dev);
+ if (!opp_data->opp_node) {
+ dev_info(opp_data->cpu_dev,
+ "OPP-v2 not supported, cpufreq-dt will attempt to use legacy tables.\n");
+ goto register_cpufreq_dt;
+ }
+
+ ret = ti_cpufreq_setup_syscon_register(opp_data);
+ if (ret)
+ goto fail_put_node;
+
+ /*
+ * OPPs determine whether or not they are supported based on
+ * two metrics:
+ * 0 - SoC Revision
+ * 1 - eFuse value
+ */
+ ret = ti_cpufreq_get_rev(opp_data, &version[0]);
+ if (ret)
+ goto fail_put_node;
+
+ ret = ti_cpufreq_get_efuse(opp_data, &version[1]);
+ if (ret)
+ goto fail_put_node;
+
+ of_node_put(opp_data->opp_node);
+
+ ret = PTR_ERR_OR_ZERO(dev_pm_opp_set_supported_hw(opp_data->cpu_dev,
+ version, VERSION_COUNT));
+ if (ret) {
+ dev_err(opp_data->cpu_dev,
+ "Failed to set supported hardware\n");
+ goto fail_put_node;
+ }
+
+register_cpufreq_dt:
+ platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
+
+ return 0;
+
+fail_put_node:
+ of_node_put(opp_data->opp_node);
+
+ return ret;
+}
+device_initcall(ti_cpufreq_init);
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index d9b5b93..8d6d25c 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -19,6 +19,7 @@
#include <linux/tick.h>
#include <linux/sched.h>
#include <linux/math64.h>
+#include <linux/cpu.h>
/*
* Please note when changing the tuning values:
@@ -280,17 +281,23 @@
static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
{
struct menu_device *data = this_cpu_ptr(&menu_devices);
+ struct device *device = get_cpu_device(dev->cpu);
int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
int i;
unsigned int interactivity_req;
unsigned int expected_interval;
unsigned long nr_iowaiters, cpu_load;
+ int resume_latency = dev_pm_qos_read_value(device);
if (data->needs_update) {
menu_update(drv, dev);
data->needs_update = 0;
}
+ /* resume_latency is 0 means no restriction */
+ if (resume_latency && resume_latency < latency_req)
+ latency_req = resume_latency;
+
/* Special case when user has set very strict latency requirement */
if (unlikely(latency_req == 0))
return 0;
@@ -357,9 +364,9 @@
if (s->disabled || su->disable)
continue;
if (s->target_residency > data->predicted_us)
- continue;
+ break;
if (s->exit_latency > latency_req)
- continue;
+ break;
data->last_state_idx = i;
}
diff --git a/drivers/devfreq/devfreq-event.c b/drivers/devfreq/devfreq-event.c
index 9aea2c7..8648b32 100644
--- a/drivers/devfreq/devfreq-event.c
+++ b/drivers/devfreq/devfreq-event.c
@@ -306,7 +306,7 @@
struct devfreq_event_desc *desc)
{
struct devfreq_event_dev *edev;
- static atomic_t event_no = ATOMIC_INIT(0);
+ static atomic_t event_no = ATOMIC_INIT(-1);
int ret;
if (!dev || !desc)
@@ -329,7 +329,7 @@
edev->dev.class = devfreq_event_class;
edev->dev.release = devfreq_event_release_edev;
- dev_set_name(&edev->dev, "event.%d", atomic_inc_return(&event_no) - 1);
+ dev_set_name(&edev->dev, "event%d", atomic_inc_return(&event_no));
ret = device_register(&edev->dev);
if (ret < 0) {
put_device(&edev->dev);
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 47206a2..551a271 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -111,18 +111,16 @@
return;
}
- rcu_read_lock();
for (i = 0, freq = 0; i < profile->max_state; i++, freq++) {
opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &freq);
if (IS_ERR(opp)) {
devm_kfree(devfreq->dev.parent, profile->freq_table);
profile->max_state = 0;
- rcu_read_unlock();
return;
}
+ dev_pm_opp_put(opp);
profile->freq_table[i] = freq;
}
- rcu_read_unlock();
}
/**
@@ -130,7 +128,7 @@
* @devfreq: the devfreq instance
* @freq: the update target frequency
*/
-static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
+int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
{
int lev, prev_lev, ret = 0;
unsigned long cur_time;
@@ -166,6 +164,7 @@
devfreq->last_stat_updated = cur_time;
return ret;
}
+EXPORT_SYMBOL(devfreq_update_status);
/**
* find_devfreq_governor() - find devfreq governor from name
@@ -474,11 +473,15 @@
}
/**
- * _remove_devfreq() - Remove devfreq from the list and release its resources.
- * @devfreq: the devfreq struct
+ * devfreq_dev_release() - Callback for struct device to release the device.
+ * @dev: the devfreq device
+ *
+ * Remove devfreq from the list and release its resources.
*/
-static void _remove_devfreq(struct devfreq *devfreq)
+static void devfreq_dev_release(struct device *dev)
{
+ struct devfreq *devfreq = to_devfreq(dev);
+
mutex_lock(&devfreq_list_lock);
if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) {
mutex_unlock(&devfreq_list_lock);
@@ -500,19 +503,6 @@
}
/**
- * devfreq_dev_release() - Callback for struct device to release the device.
- * @dev: the devfreq device
- *
- * This calls _remove_devfreq() if _remove_devfreq() is not called.
- */
-static void devfreq_dev_release(struct device *dev)
-{
- struct devfreq *devfreq = to_devfreq(dev);
-
- _remove_devfreq(devfreq);
-}
-
-/**
* devfreq_add_device() - Add devfreq feature to the device
* @dev: the device to add devfreq feature.
* @profile: device-specific profile to run devfreq.
@@ -527,6 +517,7 @@
{
struct devfreq *devfreq;
struct devfreq_governor *governor;
+ static atomic_t devfreq_no = ATOMIC_INIT(-1);
int err = 0;
if (!dev || !profile || !governor_name) {
@@ -538,15 +529,14 @@
devfreq = find_device_devfreq(dev);
mutex_unlock(&devfreq_list_lock);
if (!IS_ERR(devfreq)) {
- dev_err(dev, "%s: Unable to create devfreq for the device. It already has one.\n", __func__);
+ dev_err(dev, "%s: Unable to create devfreq for the device.\n",
+ __func__);
err = -EINVAL;
goto err_out;
}
devfreq = kzalloc(sizeof(struct devfreq), GFP_KERNEL);
if (!devfreq) {
- dev_err(dev, "%s: Unable to create devfreq for the device\n",
- __func__);
err = -ENOMEM;
goto err_out;
}
@@ -569,18 +559,21 @@
mutex_lock(&devfreq->lock);
}
- dev_set_name(&devfreq->dev, "%s", dev_name(dev));
+ dev_set_name(&devfreq->dev, "devfreq%d",
+ atomic_inc_return(&devfreq_no));
err = device_register(&devfreq->dev);
if (err) {
mutex_unlock(&devfreq->lock);
goto err_out;
}
- devfreq->trans_table = devm_kzalloc(&devfreq->dev, sizeof(unsigned int) *
+ devfreq->trans_table = devm_kzalloc(&devfreq->dev,
+ sizeof(unsigned int) *
devfreq->profile->max_state *
devfreq->profile->max_state,
GFP_KERNEL);
- devfreq->time_in_state = devm_kzalloc(&devfreq->dev, sizeof(unsigned long) *
+ devfreq->time_in_state = devm_kzalloc(&devfreq->dev,
+ sizeof(unsigned long) *
devfreq->profile->max_state,
GFP_KERNEL);
devfreq->last_stat_updated = jiffies;
@@ -939,6 +932,9 @@
if (df->governor == governor) {
ret = 0;
goto out;
+ } else if (df->governor->immutable || governor->immutable) {
+ ret = -EINVAL;
+ goto out;
}
if (df->governor) {
@@ -968,13 +964,33 @@
struct device_attribute *attr,
char *buf)
{
- struct devfreq_governor *tmp_governor;
+ struct devfreq *df = to_devfreq(d);
ssize_t count = 0;
mutex_lock(&devfreq_list_lock);
- list_for_each_entry(tmp_governor, &devfreq_governor_list, node)
- count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
- "%s ", tmp_governor->name);
+
+ /*
+ * The devfreq with immutable governor (e.g., passive) shows
+ * only own governor.
+ */
+ if (df->governor->immutable) {
+ count = scnprintf(&buf[count], DEVFREQ_NAME_LEN,
+ "%s ", df->governor_name);
+ /*
+ * The devfreq device shows the registered governor except for
+ * immutable governors such as passive governor .
+ */
+ } else {
+ struct devfreq_governor *governor;
+
+ list_for_each_entry(governor, &devfreq_governor_list, node) {
+ if (governor->immutable)
+ continue;
+ count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
+ "%s ", governor->name);
+ }
+ }
+
mutex_unlock(&devfreq_list_lock);
/* Truncate the trailing space */
@@ -995,7 +1011,7 @@
if (devfreq->profile->get_cur_freq &&
!devfreq->profile->get_cur_freq(devfreq->dev.parent, &freq))
- return sprintf(buf, "%lu\n", freq);
+ return sprintf(buf, "%lu\n", freq);
return sprintf(buf, "%lu\n", devfreq->previous_freq);
}
@@ -1112,17 +1128,16 @@
ssize_t count = 0;
unsigned long freq = 0;
- rcu_read_lock();
do {
opp = dev_pm_opp_find_freq_ceil(dev, &freq);
if (IS_ERR(opp))
break;
+ dev_pm_opp_put(opp);
count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
"%lu ", freq);
freq++;
} while (1);
- rcu_read_unlock();
/* Truncate the trailing space */
if (count)
@@ -1224,11 +1239,8 @@
* @freq: The frequency given to target function
* @flags: Flags handed from devfreq framework.
*
- * Locking: This function must be called under rcu_read_lock(). opp is a rcu
- * protected pointer. The reason for the same is that the opp pointer which is
- * returned will remain valid for use with opp_get_{voltage, freq} only while
- * under the locked area. The pointer returned must be used prior to unlocking
- * with rcu_read_unlock() to maintain the integrity of the pointer.
+ * The callers are required to call dev_pm_opp_put() for the returned OPP after
+ * use.
*/
struct dev_pm_opp *devfreq_recommended_opp(struct device *dev,
unsigned long *freq,
@@ -1265,18 +1277,7 @@
*/
int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq)
{
- struct srcu_notifier_head *nh;
- int ret = 0;
-
- rcu_read_lock();
- nh = dev_pm_opp_get_notifier(dev);
- if (IS_ERR(nh))
- ret = PTR_ERR(nh);
- rcu_read_unlock();
- if (!ret)
- ret = srcu_notifier_chain_register(nh, &devfreq->nb);
-
- return ret;
+ return dev_pm_opp_register_notifier(dev, &devfreq->nb);
}
EXPORT_SYMBOL(devfreq_register_opp_notifier);
@@ -1292,18 +1293,7 @@
*/
int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq)
{
- struct srcu_notifier_head *nh;
- int ret = 0;
-
- rcu_read_lock();
- nh = dev_pm_opp_get_notifier(dev);
- if (IS_ERR(nh))
- ret = PTR_ERR(nh);
- rcu_read_unlock();
- if (!ret)
- ret = srcu_notifier_chain_unregister(nh, &devfreq->nb);
-
- return ret;
+ return dev_pm_opp_unregister_notifier(dev, &devfreq->nb);
}
EXPORT_SYMBOL(devfreq_unregister_opp_notifier);
diff --git a/drivers/devfreq/event/exynos-ppmu.c b/drivers/devfreq/event/exynos-ppmu.c
index 107eb91..9b73509 100644
--- a/drivers/devfreq/event/exynos-ppmu.c
+++ b/drivers/devfreq/event/exynos-ppmu.c
@@ -17,13 +17,13 @@
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/suspend.h>
#include <linux/devfreq-event.h>
#include "exynos-ppmu.h"
struct exynos_ppmu_data {
- void __iomem *base;
struct clk *clk;
};
@@ -33,6 +33,7 @@
unsigned int num_events;
struct device *dev;
+ struct regmap *regmap;
struct exynos_ppmu_data ppmu;
};
@@ -107,20 +108,28 @@
static int exynos_ppmu_disable(struct devfreq_event_dev *edev)
{
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
+ int ret;
u32 pmnc;
/* Disable all counters */
- __raw_writel(PPMU_CCNT_MASK |
- PPMU_PMCNT0_MASK |
- PPMU_PMCNT1_MASK |
- PPMU_PMCNT2_MASK |
- PPMU_PMCNT3_MASK,
- info->ppmu.base + PPMU_CNTENC);
+ ret = regmap_write(info->regmap, PPMU_CNTENC,
+ PPMU_CCNT_MASK |
+ PPMU_PMCNT0_MASK |
+ PPMU_PMCNT1_MASK |
+ PPMU_PMCNT2_MASK |
+ PPMU_PMCNT3_MASK);
+ if (ret < 0)
+ return ret;
/* Disable PPMU */
- pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC);
+ ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
+ if (ret < 0)
+ return ret;
+
pmnc &= ~PPMU_PMNC_ENABLE_MASK;
- __raw_writel(pmnc, info->ppmu.base + PPMU_PMNC);
+ ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
+ if (ret < 0)
+ return ret;
return 0;
}
@@ -129,29 +138,42 @@
{
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
int id = exynos_ppmu_find_ppmu_id(edev);
+ int ret;
u32 pmnc, cntens;
if (id < 0)
return id;
/* Enable specific counter */
- cntens = __raw_readl(info->ppmu.base + PPMU_CNTENS);
+ ret = regmap_read(info->regmap, PPMU_CNTENS, &cntens);
+ if (ret < 0)
+ return ret;
+
cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
- __raw_writel(cntens, info->ppmu.base + PPMU_CNTENS);
+ ret = regmap_write(info->regmap, PPMU_CNTENS, cntens);
+ if (ret < 0)
+ return ret;
/* Set the event of Read/Write data count */
- __raw_writel(PPMU_RO_DATA_CNT | PPMU_WO_DATA_CNT,
- info->ppmu.base + PPMU_BEVTxSEL(id));
+ ret = regmap_write(info->regmap, PPMU_BEVTxSEL(id),
+ PPMU_RO_DATA_CNT | PPMU_WO_DATA_CNT);
+ if (ret < 0)
+ return ret;
/* Reset cycle counter/performance counter and enable PPMU */
- pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC);
+ ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
+ if (ret < 0)
+ return ret;
+
pmnc &= ~(PPMU_PMNC_ENABLE_MASK
| PPMU_PMNC_COUNTER_RESET_MASK
| PPMU_PMNC_CC_RESET_MASK);
pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);
pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);
pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);
- __raw_writel(pmnc, info->ppmu.base + PPMU_PMNC);
+ ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
+ if (ret < 0)
+ return ret;
return 0;
}
@@ -161,40 +183,64 @@
{
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
int id = exynos_ppmu_find_ppmu_id(edev);
- u32 pmnc, cntenc;
+ unsigned int total_count, load_count;
+ unsigned int pmcnt3_high, pmcnt3_low;
+ unsigned int pmnc, cntenc;
+ int ret;
if (id < 0)
return -EINVAL;
/* Disable PPMU */
- pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC);
+ ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
+ if (ret < 0)
+ return ret;
+
pmnc &= ~PPMU_PMNC_ENABLE_MASK;
- __raw_writel(pmnc, info->ppmu.base + PPMU_PMNC);
+ ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
+ if (ret < 0)
+ return ret;
/* Read cycle count */
- edata->total_count = __raw_readl(info->ppmu.base + PPMU_CCNT);
+ ret = regmap_read(info->regmap, PPMU_CCNT, &total_count);
+ if (ret < 0)
+ return ret;
+ edata->total_count = total_count;
/* Read performance count */
switch (id) {
case PPMU_PMNCNT0:
case PPMU_PMNCNT1:
case PPMU_PMNCNT2:
- edata->load_count
- = __raw_readl(info->ppmu.base + PPMU_PMNCT(id));
+ ret = regmap_read(info->regmap, PPMU_PMNCT(id), &load_count);
+ if (ret < 0)
+ return ret;
+ edata->load_count = load_count;
break;
case PPMU_PMNCNT3:
- edata->load_count =
- ((__raw_readl(info->ppmu.base + PPMU_PMCNT3_HIGH) << 8)
- | __raw_readl(info->ppmu.base + PPMU_PMCNT3_LOW));
+ ret = regmap_read(info->regmap, PPMU_PMCNT3_HIGH, &pmcnt3_high);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(info->regmap, PPMU_PMCNT3_LOW, &pmcnt3_low);
+ if (ret < 0)
+ return ret;
+
+ edata->load_count = ((pmcnt3_high << 8) | pmcnt3_low);
break;
default:
return -EINVAL;
}
/* Disable specific counter */
- cntenc = __raw_readl(info->ppmu.base + PPMU_CNTENC);
+ ret = regmap_read(info->regmap, PPMU_CNTENC, &cntenc);
+ if (ret < 0)
+ return ret;
+
cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
- __raw_writel(cntenc, info->ppmu.base + PPMU_CNTENC);
+ ret = regmap_write(info->regmap, PPMU_CNTENC, cntenc);
+ if (ret < 0)
+ return ret;
dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name,
edata->load_count, edata->total_count);
@@ -214,36 +260,93 @@
static int exynos_ppmu_v2_disable(struct devfreq_event_dev *edev)
{
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
+ int ret;
u32 pmnc, clear;
/* Disable all counters */
clear = (PPMU_CCNT_MASK | PPMU_PMCNT0_MASK | PPMU_PMCNT1_MASK
| PPMU_PMCNT2_MASK | PPMU_PMCNT3_MASK);
+ ret = regmap_write(info->regmap, PPMU_V2_FLAG, clear);
+ if (ret < 0)
+ return ret;
- __raw_writel(clear, info->ppmu.base + PPMU_V2_FLAG);
- __raw_writel(clear, info->ppmu.base + PPMU_V2_INTENC);
- __raw_writel(clear, info->ppmu.base + PPMU_V2_CNTENC);
- __raw_writel(clear, info->ppmu.base + PPMU_V2_CNT_RESET);
+ ret = regmap_write(info->regmap, PPMU_V2_INTENC, clear);
+ if (ret < 0)
+ return ret;
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CIG_CFG0);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CIG_CFG1);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CIG_CFG2);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CIG_RESULT);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CNT_AUTO);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CH_EV0_TYPE);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CH_EV1_TYPE);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CH_EV2_TYPE);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_CH_EV3_TYPE);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_SM_ID_V);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_SM_ID_A);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_SM_OTHERS_V);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_SM_OTHERS_A);
- __raw_writel(0x0, info->ppmu.base + PPMU_V2_INTERRUPT_RESET);
+ ret = regmap_write(info->regmap, PPMU_V2_CNTENC, clear);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_CNT_RESET, clear);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG0, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG1, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG2, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_CIG_RESULT, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_CNT_AUTO, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_CH_EV0_TYPE, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_CH_EV1_TYPE, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_CH_EV2_TYPE, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_CH_EV3_TYPE, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_SM_ID_V, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_SM_ID_A, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_V, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_A, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(info->regmap, PPMU_V2_INTERRUPT_RESET, 0x0);
+ if (ret < 0)
+ return ret;
/* Disable PPMU */
- pmnc = __raw_readl(info->ppmu.base + PPMU_V2_PMNC);
+ ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
+ if (ret < 0)
+ return ret;
+
pmnc &= ~PPMU_PMNC_ENABLE_MASK;
- __raw_writel(pmnc, info->ppmu.base + PPMU_V2_PMNC);
+ ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
+ if (ret < 0)
+ return ret;
return 0;
}
@@ -251,30 +354,43 @@
static int exynos_ppmu_v2_set_event(struct devfreq_event_dev *edev)
{
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
+ unsigned int pmnc, cntens;
int id = exynos_ppmu_find_ppmu_id(edev);
- u32 pmnc, cntens;
+ int ret;
/* Enable all counters */
- cntens = __raw_readl(info->ppmu.base + PPMU_V2_CNTENS);
+ ret = regmap_read(info->regmap, PPMU_V2_CNTENS, &cntens);
+ if (ret < 0)
+ return ret;
+
cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
- __raw_writel(cntens, info->ppmu.base + PPMU_V2_CNTENS);
+ ret = regmap_write(info->regmap, PPMU_V2_CNTENS, cntens);
+ if (ret < 0)
+ return ret;
/* Set the event of Read/Write data count */
switch (id) {
case PPMU_PMNCNT0:
case PPMU_PMNCNT1:
case PPMU_PMNCNT2:
- __raw_writel(PPMU_V2_RO_DATA_CNT | PPMU_V2_WO_DATA_CNT,
- info->ppmu.base + PPMU_V2_CH_EVx_TYPE(id));
+ ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),
+ PPMU_V2_RO_DATA_CNT | PPMU_V2_WO_DATA_CNT);
+ if (ret < 0)
+ return ret;
break;
case PPMU_PMNCNT3:
- __raw_writel(PPMU_V2_EVT3_RW_DATA_CNT,
- info->ppmu.base + PPMU_V2_CH_EVx_TYPE(id));
+ ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),
+ PPMU_V2_EVT3_RW_DATA_CNT);
+ if (ret < 0)
+ return ret;
break;
}
/* Reset cycle counter/performance counter and enable PPMU */
- pmnc = __raw_readl(info->ppmu.base + PPMU_V2_PMNC);
+ ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
+ if (ret < 0)
+ return ret;
+
pmnc &= ~(PPMU_PMNC_ENABLE_MASK
| PPMU_PMNC_COUNTER_RESET_MASK
| PPMU_PMNC_CC_RESET_MASK
@@ -284,7 +400,10 @@
pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);
pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);
pmnc |= (PPMU_V2_MODE_MANUAL << PPMU_V2_PMNC_START_MODE_SHIFT);
- __raw_writel(pmnc, info->ppmu.base + PPMU_V2_PMNC);
+
+ ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
+ if (ret < 0)
+ return ret;
return 0;
}
@@ -294,37 +413,61 @@
{
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
int id = exynos_ppmu_find_ppmu_id(edev);
- u32 pmnc, cntenc;
- u32 pmcnt_high, pmcnt_low;
- u64 load_count = 0;
+ int ret;
+ unsigned int pmnc, cntenc;
+ unsigned int pmcnt_high, pmcnt_low;
+ unsigned int total_count, count;
+ unsigned long load_count = 0;
/* Disable PPMU */
- pmnc = __raw_readl(info->ppmu.base + PPMU_V2_PMNC);
+ ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
+ if (ret < 0)
+ return ret;
+
pmnc &= ~PPMU_PMNC_ENABLE_MASK;
- __raw_writel(pmnc, info->ppmu.base + PPMU_V2_PMNC);
+ ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
+ if (ret < 0)
+ return ret;
/* Read cycle count and performance count */
- edata->total_count = __raw_readl(info->ppmu.base + PPMU_V2_CCNT);
+ ret = regmap_read(info->regmap, PPMU_V2_CCNT, &total_count);
+ if (ret < 0)
+ return ret;
+ edata->total_count = total_count;
switch (id) {
case PPMU_PMNCNT0:
case PPMU_PMNCNT1:
case PPMU_PMNCNT2:
- load_count = __raw_readl(info->ppmu.base + PPMU_V2_PMNCT(id));
+ ret = regmap_read(info->regmap, PPMU_V2_PMNCT(id), &count);
+ if (ret < 0)
+ return ret;
+ load_count = count;
break;
case PPMU_PMNCNT3:
- pmcnt_high = __raw_readl(info->ppmu.base + PPMU_V2_PMCNT3_HIGH);
- pmcnt_low = __raw_readl(info->ppmu.base + PPMU_V2_PMCNT3_LOW);
- load_count = ((u64)((pmcnt_high & 0xff)) << 32)
- + (u64)pmcnt_low;
+ ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_HIGH,
+ &pmcnt_high);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_LOW, &pmcnt_low);
+ if (ret < 0)
+ return ret;
+
+ load_count = ((u64)((pmcnt_high & 0xff)) << 32)+ (u64)pmcnt_low;
break;
}
edata->load_count = load_count;
/* Disable all counters */
- cntenc = __raw_readl(info->ppmu.base + PPMU_V2_CNTENC);
+ ret = regmap_read(info->regmap, PPMU_V2_CNTENC, &cntenc);
+ if (ret < 0)
+ return 0;
+
cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
- __raw_writel(cntenc, info->ppmu.base + PPMU_V2_CNTENC);
+ ret = regmap_write(info->regmap, PPMU_V2_CNTENC, cntenc);
+ if (ret < 0)
+ return ret;
dev_dbg(&edev->dev, "%25s (load: %ld / %ld)\n", edev->desc->name,
edata->load_count, edata->total_count);
@@ -411,10 +554,19 @@
return 0;
}
-static int exynos_ppmu_parse_dt(struct exynos_ppmu *info)
+static struct regmap_config exynos_ppmu_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static int exynos_ppmu_parse_dt(struct platform_device *pdev,
+ struct exynos_ppmu *info)
{
struct device *dev = info->dev;
struct device_node *np = dev->of_node;
+ struct resource *res;
+ void __iomem *base;
int ret = 0;
if (!np) {
@@ -423,10 +575,17 @@
}
/* Maps the memory mapped IO to control PPMU register */
- info->ppmu.base = of_iomap(np, 0);
- if (IS_ERR_OR_NULL(info->ppmu.base)) {
- dev_err(dev, "failed to map memory region\n");
- return -ENOMEM;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ exynos_ppmu_regmap_config.max_register = resource_size(res) - 4;
+ info->regmap = devm_regmap_init_mmio(dev, base,
+ &exynos_ppmu_regmap_config);
+ if (IS_ERR(info->regmap)) {
+ dev_err(dev, "failed to initialize regmap\n");
+ return PTR_ERR(info->regmap);
}
info->ppmu.clk = devm_clk_get(dev, "ppmu");
@@ -438,15 +597,10 @@
ret = of_get_devfreq_events(np, info);
if (ret < 0) {
dev_err(dev, "failed to parse exynos ppmu dt node\n");
- goto err;
+ return ret;
}
return 0;
-
-err:
- iounmap(info->ppmu.base);
-
- return ret;
}
static int exynos_ppmu_probe(struct platform_device *pdev)
@@ -463,7 +617,7 @@
info->dev = &pdev->dev;
/* Parse dt data to get resource */
- ret = exynos_ppmu_parse_dt(info);
+ ret = exynos_ppmu_parse_dt(pdev, info);
if (ret < 0) {
dev_err(&pdev->dev,
"failed to parse devicetree for resource\n");
@@ -476,8 +630,7 @@
if (!info->edev) {
dev_err(&pdev->dev,
"failed to allocate memory devfreq-event devices\n");
- ret = -ENOMEM;
- goto err;
+ return -ENOMEM;
}
edev = info->edev;
platform_set_drvdata(pdev, info);
@@ -488,17 +641,16 @@
ret = PTR_ERR(edev[i]);
dev_err(&pdev->dev,
"failed to add devfreq-event device\n");
- goto err;
+ return PTR_ERR(edev[i]);
}
+
+ pr_info("exynos-ppmu: new PPMU device registered %s (%s)\n",
+ dev_name(&pdev->dev), desc[i].name);
}
clk_prepare_enable(info->ppmu.clk);
return 0;
-err:
- iounmap(info->ppmu.base);
-
- return ret;
}
static int exynos_ppmu_remove(struct platform_device *pdev)
@@ -506,7 +658,6 @@
struct exynos_ppmu *info = platform_get_drvdata(pdev);
clk_disable_unprepare(info->ppmu.clk);
- iounmap(info->ppmu.base);
return 0;
}
diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c
index 9af86f4..49f6892 100644
--- a/drivers/devfreq/exynos-bus.c
+++ b/drivers/devfreq/exynos-bus.c
@@ -103,18 +103,17 @@
int ret = 0;
/* Get new opp-bus instance according to new bus clock */
- rcu_read_lock();
new_opp = devfreq_recommended_opp(dev, freq, flags);
if (IS_ERR(new_opp)) {
dev_err(dev, "failed to get recommended opp instance\n");
- rcu_read_unlock();
return PTR_ERR(new_opp);
}
new_freq = dev_pm_opp_get_freq(new_opp);
new_volt = dev_pm_opp_get_voltage(new_opp);
+ dev_pm_opp_put(new_opp);
+
old_freq = bus->curr_freq;
- rcu_read_unlock();
if (old_freq == new_freq)
return 0;
@@ -147,8 +146,8 @@
}
bus->curr_freq = new_freq;
- dev_dbg(dev, "Set the frequency of bus (%lukHz -> %lukHz)\n",
- old_freq/1000, new_freq/1000);
+ dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
+ old_freq, new_freq, clk_get_rate(bus->clk));
out:
mutex_unlock(&bus->lock);
@@ -214,17 +213,16 @@
int ret = 0;
/* Get new opp-bus instance according to new bus clock */
- rcu_read_lock();
new_opp = devfreq_recommended_opp(dev, freq, flags);
if (IS_ERR(new_opp)) {
dev_err(dev, "failed to get recommended opp instance\n");
- rcu_read_unlock();
return PTR_ERR(new_opp);
}
new_freq = dev_pm_opp_get_freq(new_opp);
+ dev_pm_opp_put(new_opp);
+
old_freq = bus->curr_freq;
- rcu_read_unlock();
if (old_freq == new_freq)
return 0;
@@ -241,8 +239,8 @@
*freq = new_freq;
bus->curr_freq = new_freq;
- dev_dbg(dev, "Set the frequency of bus (%lukHz -> %lukHz)\n",
- old_freq/1000, new_freq/1000);
+ dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
+ old_freq, new_freq, clk_get_rate(bus->clk));
out:
mutex_unlock(&bus->lock);
@@ -358,16 +356,14 @@
rate = clk_get_rate(bus->clk);
- rcu_read_lock();
opp = devfreq_recommended_opp(dev, &rate, 0);
if (IS_ERR(opp)) {
dev_err(dev, "failed to find dev_pm_opp\n");
- rcu_read_unlock();
ret = PTR_ERR(opp);
goto err_opp;
}
bus->curr_freq = dev_pm_opp_get_freq(opp);
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
return 0;
diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h
index fad7d63..71576b8 100644
--- a/drivers/devfreq/governor.h
+++ b/drivers/devfreq/governor.h
@@ -38,4 +38,6 @@
extern int devfreq_add_governor(struct devfreq_governor *governor);
extern int devfreq_remove_governor(struct devfreq_governor *governor);
+extern int devfreq_update_status(struct devfreq *devfreq, unsigned long freq);
+
#endif /* _GOVERNOR_H */
diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c
index 9ef46e2..673ad8cc 100644
--- a/drivers/devfreq/governor_passive.c
+++ b/drivers/devfreq/governor_passive.c
@@ -59,14 +59,14 @@
* list of parent device. Because in this case, *freq is temporary
* value which is decided by ondemand governor.
*/
- rcu_read_lock();
opp = devfreq_recommended_opp(parent_devfreq->dev.parent, freq, 0);
- rcu_read_unlock();
if (IS_ERR(opp)) {
ret = PTR_ERR(opp);
goto out;
}
+ dev_pm_opp_put(opp);
+
/*
* Get the OPP table's index of decided freqeuncy by governor
* of parent device.
@@ -112,6 +112,11 @@
if (ret < 0)
goto out;
+ if (devfreq->profile->freq_table
+ && (devfreq_update_status(devfreq, freq)))
+ dev_err(&devfreq->dev,
+ "Couldn't update frequency transition information.\n");
+
devfreq->previous_freq = freq;
out:
@@ -179,6 +184,7 @@
static struct devfreq_governor devfreq_passive = {
.name = "passive",
+ .immutable = 1,
.get_target_freq = devfreq_passive_get_target_freq,
.event_handler = devfreq_passive_event_handler,
};
diff --git a/drivers/devfreq/governor_userspace.c b/drivers/devfreq/governor_userspace.c
index 35de6e8..1769760 100644
--- a/drivers/devfreq/governor_userspace.c
+++ b/drivers/devfreq/governor_userspace.c
@@ -1,5 +1,5 @@
/*
- * linux/drivers/devfreq/governor_simpleondemand.c
+ * linux/drivers/devfreq/governor_userspace.c
*
* Copyright (C) 2011 Samsung Electronics
* MyungJoo Ham <myungjoo.ham@samsung.com>
@@ -50,7 +50,6 @@
unsigned long wanted;
int err = 0;
-
mutex_lock(&devfreq->lock);
data = devfreq->data;
@@ -112,7 +111,13 @@
static void userspace_exit(struct devfreq *devfreq)
{
- sysfs_remove_group(&devfreq->dev.kobj, &dev_attr_group);
+ /*
+ * Remove the sysfs entry, unless this is being called after
+ * device_del(), which should have done this already via kobject_del().
+ */
+ if (devfreq->dev.kobj.sd)
+ sysfs_remove_group(&devfreq->dev.kobj, &dev_attr_group);
+
kfree(devfreq->data);
devfreq->data = NULL;
}
diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c
index 27d2f34..40a2499 100644
--- a/drivers/devfreq/rk3399_dmc.c
+++ b/drivers/devfreq/rk3399_dmc.c
@@ -91,17 +91,13 @@
unsigned long target_volt, target_rate;
int err;
- rcu_read_lock();
opp = devfreq_recommended_opp(dev, freq, flags);
- if (IS_ERR(opp)) {
- rcu_read_unlock();
+ if (IS_ERR(opp))
return PTR_ERR(opp);
- }
target_rate = dev_pm_opp_get_freq(opp);
target_volt = dev_pm_opp_get_voltage(opp);
-
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
if (dmcfreq->rate == target_rate)
return 0;
@@ -422,15 +418,13 @@
data->rate = clk_get_rate(data->dmc_clk);
- rcu_read_lock();
opp = devfreq_recommended_opp(dev, &data->rate, 0);
- if (IS_ERR(opp)) {
- rcu_read_unlock();
+ if (IS_ERR(opp))
return PTR_ERR(opp);
- }
+
data->rate = dev_pm_opp_get_freq(opp);
data->volt = dev_pm_opp_get_voltage(opp);
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
rk3399_devfreq_dmc_profile.initial_freq = data->rate;
diff --git a/drivers/devfreq/tegra-devfreq.c b/drivers/devfreq/tegra-devfreq.c
index fe9dce0..214fff9 100644
--- a/drivers/devfreq/tegra-devfreq.c
+++ b/drivers/devfreq/tegra-devfreq.c
@@ -487,15 +487,13 @@
struct dev_pm_opp *opp;
unsigned long rate = *freq * KHZ;
- rcu_read_lock();
opp = devfreq_recommended_opp(dev, &rate, flags);
if (IS_ERR(opp)) {
- rcu_read_unlock();
dev_err(dev, "Failed to find opp for %lu KHz\n", *freq);
return PTR_ERR(opp);
}
rate = dev_pm_opp_get_freq(opp);
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
clk_set_min_rate(tegra->emc_clock, rate);
clk_set_rate(tegra->emc_clock, 0);
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 7539f73..f37f497 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -1859,9 +1859,10 @@
* Alloc MicroCode buffer for 'chans' Channel threads.
* A channel's buffer offset is (Channel_Id * MCODE_BUFF_PERCHAN)
*/
- pl330->mcode_cpu = dma_alloc_coherent(pl330->ddma.dev,
+ pl330->mcode_cpu = dma_alloc_attrs(pl330->ddma.dev,
chans * pl330->mcbufsz,
- &pl330->mcode_bus, GFP_KERNEL);
+ &pl330->mcode_bus, GFP_KERNEL,
+ DMA_ATTR_PRIVILEGED);
if (!pl330->mcode_cpu) {
dev_err(pl330->ddma.dev, "%s:%d Can't allocate memory!\n",
__func__, __LINE__);
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index 2602511..82dab16 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -3065,6 +3065,8 @@
/* Check whether at least one UMC is enabled: */
if (umc_en_mask)
ecc_en = umc_en_mask == ecc_en_mask;
+ else
+ edac_dbg(0, "Node %d: No enabled UMCs.\n", nid);
/* Assume UMC MCA banks are enabled. */
nb_mce_en = true;
@@ -3075,14 +3077,15 @@
nb_mce_en = nb_mce_bank_enabled_on_node(nid);
if (!nb_mce_en)
- amd64_notice("NB MCE bank disabled, set MSR 0x%08x[4] on node %d to enable.\n",
+ edac_dbg(0, "NB MCE bank disabled, set MSR 0x%08x[4] on node %d to enable.\n",
MSR_IA32_MCG_CTL, nid);
}
- amd64_info("DRAM ECC %s.\n", (ecc_en ? "enabled" : "disabled"));
+ amd64_info("Node %d: DRAM ECC %s.\n",
+ nid, (ecc_en ? "enabled" : "disabled"));
if (!ecc_en || !nb_mce_en) {
- amd64_notice("%s", ecc_msg);
+ amd64_info("%s", ecc_msg);
return false;
}
return true;
@@ -3300,15 +3303,6 @@
goto err_add_mc;
}
- /* register stuff with EDAC MCE */
- if (report_gart_errors)
- amd_report_gart_errors(true);
-
- if (pvt->umc)
- amd_register_ecc_decoder(decode_umc_error);
- else
- amd_register_ecc_decoder(decode_bus_error);
-
return 0;
err_add_mc:
@@ -3342,7 +3336,7 @@
ecc_stngs[nid] = s;
if (!ecc_enabled(F3, nid)) {
- ret = -ENODEV;
+ ret = 0;
if (!ecc_enable_override)
goto err_enable;
@@ -3363,6 +3357,8 @@
if (boot_cpu_data.x86 < 0x17)
restore_ecc_error_reporting(s, nid, F3);
+
+ goto err_enable;
}
return ret;
@@ -3396,14 +3392,6 @@
free_mc_sibling_devs(pvt);
- /* unregister from EDAC MCE */
- amd_report_gart_errors(false);
-
- if (pvt->umc)
- amd_unregister_ecc_decoder(decode_umc_error);
- else
- amd_unregister_ecc_decoder(decode_bus_error);
-
kfree(ecc_stngs[nid]);
ecc_stngs[nid] = NULL;
@@ -3452,8 +3440,11 @@
int err = -ENODEV;
int i;
+ if (!x86_match_cpu(amd64_cpuids))
+ return -ENODEV;
+
if (amd_cache_northbridges() < 0)
- goto err_ret;
+ return -ENODEV;
opstate_init();
@@ -3466,14 +3457,30 @@
if (!msrs)
goto err_free;
- for (i = 0; i < amd_nb_num(); i++)
- if (probe_one_instance(i)) {
+ for (i = 0; i < amd_nb_num(); i++) {
+ err = probe_one_instance(i);
+ if (err) {
/* unwind properly */
while (--i >= 0)
remove_one_instance(i);
goto err_pci;
}
+ }
+
+ if (!edac_has_mcs()) {
+ err = -ENODEV;
+ goto err_pci;
+ }
+
+ /* register stuff with EDAC MCE */
+ if (report_gart_errors)
+ amd_report_gart_errors(true);
+
+ if (boot_cpu_data.x86 >= 0x17)
+ amd_register_ecc_decoder(decode_umc_error);
+ else
+ amd_register_ecc_decoder(decode_bus_error);
setup_pci_device();
@@ -3493,7 +3500,6 @@
kfree(ecc_stngs);
ecc_stngs = NULL;
-err_ret:
return err;
}
@@ -3504,6 +3510,14 @@
if (pci_ctl)
edac_pci_release_generic_ctl(pci_ctl);
+ /* unregister from EDAC MCE */
+ amd_report_gart_errors(false);
+
+ if (boot_cpu_data.x86 >= 0x17)
+ amd_unregister_ecc_decoder(decode_umc_error);
+ else
+ amd_unregister_ecc_decoder(decode_bus_error);
+
for (i = 0; i < amd_nb_num(); i++)
remove_one_instance(i);
diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h
index 496603d..1d4b74e 100644
--- a/drivers/edac/amd64_edac.h
+++ b/drivers/edac/amd64_edac.h
@@ -16,19 +16,14 @@
#include <linux/slab.h>
#include <linux/mmzone.h>
#include <linux/edac.h>
+#include <asm/cpu_device_id.h>
#include <asm/msr.h>
#include "edac_module.h"
#include "mce_amd.h"
-#define amd64_debug(fmt, arg...) \
- edac_printk(KERN_DEBUG, "amd64", fmt, ##arg)
-
#define amd64_info(fmt, arg...) \
edac_printk(KERN_INFO, "amd64", fmt, ##arg)
-#define amd64_notice(fmt, arg...) \
- edac_printk(KERN_NOTICE, "amd64", fmt, ##arg)
-
#define amd64_warn(fmt, arg...) \
edac_printk(KERN_WARNING, "amd64", "Warning: " fmt, ##arg)
@@ -90,7 +85,7 @@
* sections 3.5.4 and 3.5.5 for more information.
*/
-#define EDAC_AMD64_VERSION "3.4.0"
+#define EDAC_AMD64_VERSION "3.5.0"
#define EDAC_MOD_STR "amd64_edac"
/* Extended Model from CPUID, for CPU Revision numbers */
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index 750891e..e5573c5 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -453,6 +453,20 @@
}
EXPORT_SYMBOL_GPL(edac_mc_free);
+bool edac_has_mcs(void)
+{
+ bool ret;
+
+ mutex_lock(&mem_ctls_mutex);
+
+ ret = list_empty(&mc_devices);
+
+ mutex_unlock(&mem_ctls_mutex);
+
+ return !ret;
+}
+EXPORT_SYMBOL_GPL(edac_has_mcs);
+
/* Caller must hold mem_ctls_mutex */
static struct mem_ctl_info *__find_mci_by_dev(struct device *dev)
{
diff --git a/drivers/edac/edac_mc.h b/drivers/edac/edac_mc.h
index 50fc1dc..5357800 100644
--- a/drivers/edac/edac_mc.h
+++ b/drivers/edac/edac_mc.h
@@ -149,6 +149,15 @@
extern void edac_mc_free(struct mem_ctl_info *mci);
/**
+ * edac_has_mcs() - Check if any MCs have been allocated.
+ *
+ * Returns:
+ * True if MC instances have been registered successfully.
+ * False otherwise.
+ */
+extern bool edac_has_mcs(void);
+
+/**
* edac_mc_find() - Search for a mem_ctl_info structure whose index is @idx.
*
* @idx: index to be seek
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index 39dbab7..445862d 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -569,6 +569,40 @@
return sprintf(data, "%s\n", edac_caps[dimm->edac_mode]);
}
+static ssize_t dimmdev_ce_count_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
+{
+ struct dimm_info *dimm = to_dimm(dev);
+ u32 count;
+ int off;
+
+ off = EDAC_DIMM_OFF(dimm->mci->layers,
+ dimm->mci->n_layers,
+ dimm->location[0],
+ dimm->location[1],
+ dimm->location[2]);
+ count = dimm->mci->ce_per_layer[dimm->mci->n_layers-1][off];
+ return sprintf(data, "%u\n", count);
+}
+
+static ssize_t dimmdev_ue_count_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
+{
+ struct dimm_info *dimm = to_dimm(dev);
+ u32 count;
+ int off;
+
+ off = EDAC_DIMM_OFF(dimm->mci->layers,
+ dimm->mci->n_layers,
+ dimm->location[0],
+ dimm->location[1],
+ dimm->location[2]);
+ count = dimm->mci->ue_per_layer[dimm->mci->n_layers-1][off];
+ return sprintf(data, "%u\n", count);
+}
+
/* dimm/rank attribute files */
static DEVICE_ATTR(dimm_label, S_IRUGO | S_IWUSR,
dimmdev_label_show, dimmdev_label_store);
@@ -577,6 +611,8 @@
static DEVICE_ATTR(dimm_mem_type, S_IRUGO, dimmdev_mem_type_show, NULL);
static DEVICE_ATTR(dimm_dev_type, S_IRUGO, dimmdev_dev_type_show, NULL);
static DEVICE_ATTR(dimm_edac_mode, S_IRUGO, dimmdev_edac_mode_show, NULL);
+static DEVICE_ATTR(dimm_ce_count, S_IRUGO, dimmdev_ce_count_show, NULL);
+static DEVICE_ATTR(dimm_ue_count, S_IRUGO, dimmdev_ue_count_show, NULL);
/* attributes of the dimm<id>/rank<id> object */
static struct attribute *dimm_attrs[] = {
@@ -586,6 +622,8 @@
&dev_attr_dimm_mem_type.attr,
&dev_attr_dimm_dev_type.attr,
&dev_attr_dimm_edac_mode.attr,
+ &dev_attr_dimm_ce_count.attr,
+ &dev_attr_dimm_ue_count.attr,
NULL,
};
@@ -831,7 +869,7 @@
static DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL);
/* memory scrubber attribute file */
-DEVICE_ATTR(sdram_scrub_rate, 0, mci_sdram_scrub_rate_show,
+static DEVICE_ATTR(sdram_scrub_rate, 0, mci_sdram_scrub_rate_show,
mci_sdram_scrub_rate_store); /* umode set later in is_visible */
static struct attribute *mci_attrs[] = {
diff --git a/drivers/edac/fsl_ddr_edac.c b/drivers/edac/fsl_ddr_edac.c
index 4e9608a..efc8276 100644
--- a/drivers/edac/fsl_ddr_edac.c
+++ b/drivers/edac/fsl_ddr_edac.c
@@ -145,12 +145,12 @@
return 0;
}
-DEVICE_ATTR(inject_data_hi, S_IRUGO | S_IWUSR,
- fsl_mc_inject_data_hi_show, fsl_mc_inject_data_hi_store);
-DEVICE_ATTR(inject_data_lo, S_IRUGO | S_IWUSR,
- fsl_mc_inject_data_lo_show, fsl_mc_inject_data_lo_store);
-DEVICE_ATTR(inject_ctrl, S_IRUGO | S_IWUSR,
- fsl_mc_inject_ctrl_show, fsl_mc_inject_ctrl_store);
+static DEVICE_ATTR(inject_data_hi, S_IRUGO | S_IWUSR,
+ fsl_mc_inject_data_hi_show, fsl_mc_inject_data_hi_store);
+static DEVICE_ATTR(inject_data_lo, S_IRUGO | S_IWUSR,
+ fsl_mc_inject_data_lo_show, fsl_mc_inject_data_lo_store);
+static DEVICE_ATTR(inject_ctrl, S_IRUGO | S_IWUSR,
+ fsl_mc_inject_ctrl_show, fsl_mc_inject_ctrl_store);
static struct attribute *fsl_ddr_dev_attrs[] = {
&dev_attr_inject_data_hi.attr,
diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c
index 0a912bf..e391f5a 100644
--- a/drivers/edac/i7300_edac.c
+++ b/drivers/edac/i7300_edac.c
@@ -304,7 +304,6 @@
#define REDMEMA 0xdc
#define REDMEMB 0x7c
- #define IS_SECOND_CH(v) ((v) * (1 << 17))
#define RECMEMA 0xe0
#define RECMEMA_BANK(v) (((v) >> 12) & 7)
@@ -483,8 +482,9 @@
pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
REDMEMB, &value);
channel = (branch << 1);
- if (IS_SECOND_CH(value))
- channel++;
+
+ /* Second channel ? */
+ channel += !!(value & BIT(17));
/* Clear the error bit */
pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index 69b5ade..75ad847 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -1835,6 +1835,7 @@
static struct notifier_block i7_mce_dec = {
.notifier_call = i7core_mce_check_error,
+ .priority = MCE_PRIO_EDAC,
};
struct memdev_dmi_entry {
diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c
index 7baa8ac..9dcdab2 100644
--- a/drivers/edac/i82975x_edac.c
+++ b/drivers/edac/i82975x_edac.c
@@ -494,6 +494,10 @@
}
mchbar &= 0xffffc000; /* bits 31:14 used for 16K window */
mch_window = ioremap_nocache(mchbar, 0x1000);
+ if (!mch_window) {
+ edac_dbg(3, "error ioremapping MCHBAR!\n");
+ goto fail0;
+ }
#ifdef i82975x_DEBUG_IOMEM
i82975x_printk(KERN_INFO, "MCHBAR real = %0x, remapped = %p\n",
diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c
index 34208f3..ba35b7e 100644
--- a/drivers/edac/mce_amd.c
+++ b/drivers/edac/mce_amd.c
@@ -937,12 +937,13 @@
}
if (m->status & MCI_STATUS_DEFERRED)
- return "Deferred error.";
+ return "Deferred error, no action required.";
return "Corrected error, no action required.";
}
-int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data)
+static int
+amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data)
{
struct mce *m = (struct mce *)data;
struct cpuinfo_x86 *c = &cpu_data(m->extcpu);
@@ -991,20 +992,22 @@
pr_cont("]: 0x%016llx\n", m->status);
if (m->status & MCI_STATUS_ADDRV)
- pr_emerg(HW_ERR "Error Addr: 0x%016llx", m->addr);
+ pr_emerg(HW_ERR "Error Addr: 0x%016llx\n", m->addr);
if (boot_cpu_has(X86_FEATURE_SMCA)) {
+ pr_emerg(HW_ERR "IPID: 0x%016llx", m->ipid);
+
if (m->status & MCI_STATUS_SYNDV)
pr_cont(", Syndrome: 0x%016llx", m->synd);
- pr_cont(", IPID: 0x%016llx", m->ipid);
-
pr_cont("\n");
decode_smca_errors(m);
goto err_code;
- } else
- pr_cont("\n");
+ }
+
+ if (m->tsc)
+ pr_emerg(HW_ERR "TSC: %llu\n", m->tsc);
if (!fam_ops)
goto err_code;
@@ -1047,10 +1050,10 @@
return NOTIFY_STOP;
}
-EXPORT_SYMBOL_GPL(amd_decode_mce);
static struct notifier_block amd_mce_dec_nb = {
.notifier_call = amd_decode_mce,
+ .priority = MCE_PRIO_EDAC,
};
static int __init mce_amd_init(void)
diff --git a/drivers/edac/mce_amd.h b/drivers/edac/mce_amd.h
index c2359a1..0b6a686 100644
--- a/drivers/edac/mce_amd.h
+++ b/drivers/edac/mce_amd.h
@@ -79,6 +79,5 @@
void amd_report_gart_errors(bool);
void amd_register_ecc_decoder(void (*f)(int, struct mce *));
void amd_unregister_ecc_decoder(void (*f)(int, struct mce *));
-int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data);
#endif /* _EDAC_MCE_AMD_H */
diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c
index 8f66cbe..67f7bc3 100644
--- a/drivers/edac/mpc85xx_edac.c
+++ b/drivers/edac/mpc85xx_edac.c
@@ -629,6 +629,7 @@
{ .compatible = "fsl,p1020-l2-cache-controller", },
{ .compatible = "fsl,p1021-l2-cache-controller", },
{ .compatible = "fsl,p2020-l2-cache-controller", },
+ { .compatible = "fsl,t2080-l2-cache-controller", },
{},
};
MODULE_DEVICE_TABLE(of, mpc85xx_l2_err_of_match);
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index 54ae6dc..a65ea44 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -304,7 +304,6 @@
u64 (*rir_limit)(u32 reg);
u64 (*sad_limit)(u32 reg);
u32 (*interleave_mode)(u32 reg);
- char* (*show_interleave_mode)(u32 reg);
u32 (*dram_attr)(u32 reg);
const u32 *dram_rule;
const u32 *interleave_list;
@@ -811,11 +810,6 @@
return GET_BITFIELD(reg, 1, 1);
}
-char *show_interleave_mode(u32 reg)
-{
- return interleave_mode(reg) ? "8:6" : "[8:6]XOR[18:16]";
-}
-
static u32 dram_attr(u32 reg)
{
return GET_BITFIELD(reg, 2, 3);
@@ -831,29 +825,16 @@
return GET_BITFIELD(reg, 1, 2);
}
-static char *knl_show_interleave_mode(u32 reg)
+static const char * const knl_intlv_mode[] = {
+ "[8:6]", "[10:8]", "[14:12]", "[32:30]"
+};
+
+static const char *get_intlv_mode_str(u32 reg, enum type t)
{
- char *s;
-
- switch (knl_interleave_mode(reg)) {
- case 0:
- s = "use address bits [8:6]";
- break;
- case 1:
- s = "use address bits [10:8]";
- break;
- case 2:
- s = "use address bits [14:12]";
- break;
- case 3:
- s = "use address bits [32:30]";
- break;
- default:
- WARN_ON(1);
- break;
- }
-
- return s;
+ if (t == KNIGHTS_LANDING)
+ return knl_intlv_mode[knl_interleave_mode(reg)];
+ else
+ return interleave_mode(reg) ? "[8:6]" : "[8:6]XOR[18:16]";
}
static u32 dram_attr_knl(u32 reg)
@@ -1810,7 +1791,7 @@
show_dram_attr(pvt->info.dram_attr(reg)),
gb, (mb*1000)/1024,
((u64)tmp_mb) << 20L,
- pvt->info.show_interleave_mode(reg),
+ get_intlv_mode_str(reg, pvt->info.type),
reg);
prv = limit;
@@ -3136,7 +3117,8 @@
}
static struct notifier_block sbridge_mce_dec = {
- .notifier_call = sbridge_mce_check_error,
+ .notifier_call = sbridge_mce_check_error,
+ .priority = MCE_PRIO_EDAC,
};
/****************************************************************************
@@ -3227,7 +3209,6 @@
pvt->info.rir_limit = rir_limit;
pvt->info.sad_limit = sad_limit;
pvt->info.interleave_mode = interleave_mode;
- pvt->info.show_interleave_mode = show_interleave_mode;
pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list;
@@ -3251,7 +3232,6 @@
pvt->info.rir_limit = rir_limit;
pvt->info.sad_limit = sad_limit;
pvt->info.interleave_mode = interleave_mode;
- pvt->info.show_interleave_mode = show_interleave_mode;
pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(sbridge_dram_rule);
pvt->info.interleave_list = sbridge_interleave_list;
@@ -3275,7 +3255,6 @@
pvt->info.rir_limit = haswell_rir_limit;
pvt->info.sad_limit = sad_limit;
pvt->info.interleave_mode = interleave_mode;
- pvt->info.show_interleave_mode = show_interleave_mode;
pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list;
@@ -3299,7 +3278,6 @@
pvt->info.rir_limit = haswell_rir_limit;
pvt->info.sad_limit = sad_limit;
pvt->info.interleave_mode = interleave_mode;
- pvt->info.show_interleave_mode = show_interleave_mode;
pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list;
@@ -3323,7 +3301,6 @@
pvt->info.rir_limit = NULL;
pvt->info.sad_limit = knl_sad_limit;
pvt->info.interleave_mode = knl_interleave_mode;
- pvt->info.show_interleave_mode = knl_show_interleave_mode;
pvt->info.dram_attr = dram_attr_knl;
pvt->info.max_sad = ARRAY_SIZE(knl_dram_rule);
pvt->info.interleave_list = knl_interleave_list;
diff --git a/drivers/edac/skx_edac.c b/drivers/edac/skx_edac.c
index 79ef675..1159dba 100644
--- a/drivers/edac/skx_edac.c
+++ b/drivers/edac/skx_edac.c
@@ -1007,7 +1007,8 @@
}
static struct notifier_block skx_mce_dec = {
- .notifier_call = skx_mce_check_error,
+ .notifier_call = skx_mce_check_error,
+ .priority = MCE_PRIO_EDAC,
};
static void skx_remove(void)
diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c
index f853ad2..1027d7b4 100644
--- a/drivers/firmware/efi/arm-init.c
+++ b/drivers/firmware/efi/arm-init.c
@@ -250,7 +250,6 @@
}
reserve_regions();
- efi_memattr_init();
efi_esrt_init();
efi_memmap_unmap();
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 9291480..e7d4040 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -529,6 +529,8 @@
}
}
+ efi_memattr_init();
+
/* Parse the EFI Properties table if it exists */
if (efi.properties_table != EFI_INVALID_TABLE_ADDR) {
efi_properties_table_t *tbl;
diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c
index 1491407..08b0268 100644
--- a/drivers/firmware/efi/esrt.c
+++ b/drivers/firmware/efi/esrt.c
@@ -269,7 +269,7 @@
max -= efi.esrt;
if (max < size) {
- pr_err("ESRT header doen't fit on single memory map entry. (size: %zu max: %zu)\n",
+ pr_err("ESRT header doesn't fit on single memory map entry. (size: %zu max: %zu)\n",
size, max);
return;
}
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index d564d25..f742596 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -11,7 +11,7 @@
-mno-mmx -mno-sse
cflags-$(CONFIG_ARM64) := $(subst -pg,,$(KBUILD_CFLAGS))
-cflags-$(CONFIG_ARM) := $(subst -pg,,$(KBUILD_CFLAGS)) -g0 \
+cflags-$(CONFIG_ARM) := $(subst -pg,,$(KBUILD_CFLAGS)) \
-fno-builtin -fpic -mno-single-pic-base
cflags-$(CONFIG_EFI_ARMSTUB) += -I$(srctree)/scripts/dtc/libfdt
@@ -28,7 +28,7 @@
# Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
KCOV_INSTRUMENT := n
-lib-y := efi-stub-helper.o gop.o
+lib-y := efi-stub-helper.o gop.o secureboot.o
# include the stub's generic dependencies from lib/ when building for ARM/arm64
arm-deps := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c sort.c
@@ -60,7 +60,7 @@
extra-$(CONFIG_EFI_ARMSTUB) := $(lib-y)
lib-$(CONFIG_EFI_ARMSTUB) := $(patsubst %.o,%.stub.o,$(lib-y))
-STUBCOPY_FLAGS-y := -R .debug* -R *ksymtab* -R *kcrctab*
+STUBCOPY_RM-y := -R *ksymtab* -R *kcrctab*
STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \
--prefix-symbols=__efistub_
STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS
@@ -68,17 +68,25 @@
$(obj)/%.stub.o: $(obj)/%.o FORCE
$(call if_changed,stubcopy)
+#
+# Strip debug sections and some other sections that may legally contain
+# absolute relocations, so that we can inspect the remaining sections for
+# such relocations. If none are found, regenerate the output object, but
+# this time, use objcopy and leave all sections in place.
+#
quiet_cmd_stubcopy = STUBCPY $@
- cmd_stubcopy = if $(OBJCOPY) $(STUBCOPY_FLAGS-y) $< $@; then \
- $(OBJDUMP) -r $@ | grep $(STUBCOPY_RELOC-y) \
- && (echo >&2 "$@: absolute symbol references not allowed in the EFI stub"; \
- rm -f $@; /bin/false); else /bin/false; fi
+ cmd_stubcopy = if $(STRIP) --strip-debug $(STUBCOPY_RM-y) -o $@ $<; \
+ then if $(OBJDUMP) -r $@ | grep $(STUBCOPY_RELOC-y); \
+ then (echo >&2 "$@: absolute symbol references not allowed in the EFI stub"; \
+ rm -f $@; /bin/false); \
+ else $(OBJCOPY) $(STUBCOPY_FLAGS-y) $< $@; fi \
+ else /bin/false; fi
#
# ARM discards the .data section because it disallows r/w data in the
# decompressor. So move our .data to .data.efistub, which is preserved
# explicitly by the decompressor linker script.
#
-STUBCOPY_FLAGS-$(CONFIG_ARM) += --rename-section .data=.data.efistub \
- -R ___ksymtab+sort -R ___kcrctab+sort
+STUBCOPY_FLAGS-$(CONFIG_ARM) += --rename-section .data=.data.efistub
+STUBCOPY_RM-$(CONFIG_ARM) += -R ___ksymtab+sort -R ___kcrctab+sort
STUBCOPY_RELOC-$(CONFIG_ARM) := R_ARM_ABS
diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c
index b4f7d78..d4056c6 100644
--- a/drivers/firmware/efi/libstub/arm-stub.c
+++ b/drivers/firmware/efi/libstub/arm-stub.c
@@ -20,52 +20,6 @@
bool __nokaslr;
-static int efi_get_secureboot(efi_system_table_t *sys_table_arg)
-{
- static efi_char16_t const sb_var_name[] = {
- 'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', 0 };
- static efi_char16_t const sm_var_name[] = {
- 'S', 'e', 't', 'u', 'p', 'M', 'o', 'd', 'e', 0 };
-
- efi_guid_t var_guid = EFI_GLOBAL_VARIABLE_GUID;
- efi_get_variable_t *f_getvar = sys_table_arg->runtime->get_variable;
- u8 val;
- unsigned long size = sizeof(val);
- efi_status_t status;
-
- status = f_getvar((efi_char16_t *)sb_var_name, (efi_guid_t *)&var_guid,
- NULL, &size, &val);
-
- if (status != EFI_SUCCESS)
- goto out_efi_err;
-
- if (val == 0)
- return 0;
-
- status = f_getvar((efi_char16_t *)sm_var_name, (efi_guid_t *)&var_guid,
- NULL, &size, &val);
-
- if (status != EFI_SUCCESS)
- goto out_efi_err;
-
- if (val == 1)
- return 0;
-
- return 1;
-
-out_efi_err:
- switch (status) {
- case EFI_NOT_FOUND:
- return 0;
- case EFI_DEVICE_ERROR:
- return -EIO;
- case EFI_SECURITY_VIOLATION:
- return -EACCES;
- default:
- return -EINVAL;
- }
-}
-
efi_status_t efi_open_volume(efi_system_table_t *sys_table_arg,
void *__image, void **__fh)
{
@@ -91,75 +45,6 @@
return status;
}
-efi_status_t efi_file_close(void *handle)
-{
- efi_file_handle_t *fh = handle;
-
- return fh->close(handle);
-}
-
-efi_status_t
-efi_file_read(void *handle, unsigned long *size, void *addr)
-{
- efi_file_handle_t *fh = handle;
-
- return fh->read(handle, size, addr);
-}
-
-
-efi_status_t
-efi_file_size(efi_system_table_t *sys_table_arg, void *__fh,
- efi_char16_t *filename_16, void **handle, u64 *file_sz)
-{
- efi_file_handle_t *h, *fh = __fh;
- efi_file_info_t *info;
- efi_status_t status;
- efi_guid_t info_guid = EFI_FILE_INFO_ID;
- unsigned long info_sz;
-
- status = fh->open(fh, &h, filename_16, EFI_FILE_MODE_READ, (u64)0);
- if (status != EFI_SUCCESS) {
- efi_printk(sys_table_arg, "Failed to open file: ");
- efi_char16_printk(sys_table_arg, filename_16);
- efi_printk(sys_table_arg, "\n");
- return status;
- }
-
- *handle = h;
-
- info_sz = 0;
- status = h->get_info(h, &info_guid, &info_sz, NULL);
- if (status != EFI_BUFFER_TOO_SMALL) {
- efi_printk(sys_table_arg, "Failed to get file info size\n");
- return status;
- }
-
-grow:
- status = sys_table_arg->boottime->allocate_pool(EFI_LOADER_DATA,
- info_sz, (void **)&info);
- if (status != EFI_SUCCESS) {
- efi_printk(sys_table_arg, "Failed to alloc mem for file info\n");
- return status;
- }
-
- status = h->get_info(h, &info_guid, &info_sz,
- info);
- if (status == EFI_BUFFER_TOO_SMALL) {
- sys_table_arg->boottime->free_pool(info);
- goto grow;
- }
-
- *file_sz = info->file_size;
- sys_table_arg->boottime->free_pool(info);
-
- if (status != EFI_SUCCESS)
- efi_printk(sys_table_arg, "Failed to get initrd info\n");
-
- return status;
-}
-
-
-
void efi_char16_printk(efi_system_table_t *sys_table_arg,
efi_char16_t *str)
{
@@ -226,7 +111,7 @@
efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID;
unsigned long reserve_addr = 0;
unsigned long reserve_size = 0;
- int secure_boot = 0;
+ enum efi_secureboot_mode secure_boot;
struct screen_info *si;
/* Check if we were booted by the EFI firmware */
@@ -296,19 +181,14 @@
pr_efi_err(sys_table, "Failed to parse EFI cmdline options\n");
secure_boot = efi_get_secureboot(sys_table);
- if (secure_boot > 0)
- pr_efi(sys_table, "UEFI Secure Boot is enabled.\n");
-
- if (secure_boot < 0) {
- pr_efi_err(sys_table,
- "could not determine UEFI Secure Boot status.\n");
- }
/*
- * Unauthenticated device tree data is a security hazard, so
- * ignore 'dtb=' unless UEFI Secure Boot is disabled.
+ * Unauthenticated device tree data is a security hazard, so ignore
+ * 'dtb=' unless UEFI Secure Boot is disabled. We assume that secure
+ * boot is enabled if we can't determine its state.
*/
- if (secure_boot != 0 && strstr(cmdline_ptr, "dtb=")) {
+ if (secure_boot != efi_secureboot_mode_disabled &&
+ strstr(cmdline_ptr, "dtb=")) {
pr_efi(sys_table, "Ignoring DTB from command line.\n");
} else {
status = handle_cmdline_files(sys_table, image, cmdline_ptr,
diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c
index 757badc..919822b 100644
--- a/drivers/firmware/efi/libstub/efi-stub-helper.c
+++ b/drivers/firmware/efi/libstub/efi-stub-helper.c
@@ -338,6 +338,69 @@
efi_call_early(free_pages, addr, nr_pages);
}
+static efi_status_t efi_file_size(efi_system_table_t *sys_table_arg, void *__fh,
+ efi_char16_t *filename_16, void **handle,
+ u64 *file_sz)
+{
+ efi_file_handle_t *h, *fh = __fh;
+ efi_file_info_t *info;
+ efi_status_t status;
+ efi_guid_t info_guid = EFI_FILE_INFO_ID;
+ unsigned long info_sz;
+
+ status = efi_call_proto(efi_file_handle, open, fh, &h, filename_16,
+ EFI_FILE_MODE_READ, (u64)0);
+ if (status != EFI_SUCCESS) {
+ efi_printk(sys_table_arg, "Failed to open file: ");
+ efi_char16_printk(sys_table_arg, filename_16);
+ efi_printk(sys_table_arg, "\n");
+ return status;
+ }
+
+ *handle = h;
+
+ info_sz = 0;
+ status = efi_call_proto(efi_file_handle, get_info, h, &info_guid,
+ &info_sz, NULL);
+ if (status != EFI_BUFFER_TOO_SMALL) {
+ efi_printk(sys_table_arg, "Failed to get file info size\n");
+ return status;
+ }
+
+grow:
+ status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
+ info_sz, (void **)&info);
+ if (status != EFI_SUCCESS) {
+ efi_printk(sys_table_arg, "Failed to alloc mem for file info\n");
+ return status;
+ }
+
+ status = efi_call_proto(efi_file_handle, get_info, h, &info_guid,
+ &info_sz, info);
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ efi_call_early(free_pool, info);
+ goto grow;
+ }
+
+ *file_sz = info->file_size;
+ efi_call_early(free_pool, info);
+
+ if (status != EFI_SUCCESS)
+ efi_printk(sys_table_arg, "Failed to get initrd info\n");
+
+ return status;
+}
+
+static efi_status_t efi_file_read(void *handle, unsigned long *size, void *addr)
+{
+ return efi_call_proto(efi_file_handle, read, handle, size, addr);
+}
+
+static efi_status_t efi_file_close(void *handle)
+{
+ return efi_call_proto(efi_file_handle, close, handle);
+}
+
/*
* Parse the ASCII string 'cmdline' for EFI options, denoted by the efi=
* option, e.g. efi=nochunk.
@@ -351,6 +414,14 @@
char *str;
/*
+ * Currently, the only efi= option we look for is 'nochunk', which
+ * is intended to work around known issues on certain x86 UEFI
+ * versions. So ignore for now on other architectures.
+ */
+ if (!IS_ENABLED(CONFIG_X86))
+ return EFI_SUCCESS;
+
+ /*
* If no EFI parameters were specified on the cmdline we've got
* nothing to do.
*/
@@ -523,7 +594,8 @@
size = files[j].size;
while (size) {
unsigned long chunksize;
- if (size > __chunk_size)
+
+ if (IS_ENABLED(CONFIG_X86) && size > __chunk_size)
chunksize = __chunk_size;
else
chunksize = size;
diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
index 0e2a96b..71c4d0e 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -29,14 +29,6 @@
efi_status_t efi_open_volume(efi_system_table_t *sys_table_arg, void *__image,
void **__fh);
-efi_status_t efi_file_size(efi_system_table_t *sys_table_arg, void *__fh,
- efi_char16_t *filename_16, void **handle,
- u64 *file_sz);
-
-efi_status_t efi_file_read(void *handle, unsigned long *size, void *addr);
-
-efi_status_t efi_file_close(void *handle);
-
unsigned long get_dram_base(efi_system_table_t *sys_table_arg);
efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
diff --git a/drivers/firmware/efi/libstub/secureboot.c b/drivers/firmware/efi/libstub/secureboot.c
new file mode 100644
index 0000000..6def402
--- /dev/null
+++ b/drivers/firmware/efi/libstub/secureboot.c
@@ -0,0 +1,84 @@
+/*
+ * Secure boot handling.
+ *
+ * Copyright (C) 2013,2014 Linaro Limited
+ * Roy Franz <roy.franz@linaro.org
+ * Copyright (C) 2013 Red Hat, Inc.
+ * Mark Salter <msalter@redhat.com>
+ *
+ * This file is part of the Linux kernel, and is made available under the
+ * terms of the GNU General Public License version 2.
+ */
+#include <linux/efi.h>
+#include <asm/efi.h>
+
+/* BIOS variables */
+static const efi_guid_t efi_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
+static const efi_char16_t const efi_SecureBoot_name[] = {
+ 'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', 0
+};
+static const efi_char16_t const efi_SetupMode_name[] = {
+ 'S', 'e', 't', 'u', 'p', 'M', 'o', 'd', 'e', 0
+};
+
+/* SHIM variables */
+static const efi_guid_t shim_guid = EFI_SHIM_LOCK_GUID;
+static efi_char16_t const shim_MokSBState_name[] = {
+ 'M', 'o', 'k', 'S', 'B', 'S', 't', 'a', 't', 'e', 0
+};
+
+#define get_efi_var(name, vendor, ...) \
+ efi_call_runtime(get_variable, \
+ (efi_char16_t *)(name), (efi_guid_t *)(vendor), \
+ __VA_ARGS__);
+
+/*
+ * Determine whether we're in secure boot mode.
+ */
+enum efi_secureboot_mode efi_get_secureboot(efi_system_table_t *sys_table_arg)
+{
+ u32 attr;
+ u8 secboot, setupmode, moksbstate;
+ unsigned long size;
+ efi_status_t status;
+
+ size = sizeof(secboot);
+ status = get_efi_var(efi_SecureBoot_name, &efi_variable_guid,
+ NULL, &size, &secboot);
+ if (status != EFI_SUCCESS)
+ goto out_efi_err;
+
+ size = sizeof(setupmode);
+ status = get_efi_var(efi_SetupMode_name, &efi_variable_guid,
+ NULL, &size, &setupmode);
+ if (status != EFI_SUCCESS)
+ goto out_efi_err;
+
+ if (secboot == 0 || setupmode == 1)
+ return efi_secureboot_mode_disabled;
+
+ /*
+ * See if a user has put the shim into insecure mode. If so, and if the
+ * variable doesn't have the runtime attribute set, we might as well
+ * honor that.
+ */
+ size = sizeof(moksbstate);
+ status = get_efi_var(shim_MokSBState_name, &shim_guid,
+ &attr, &size, &moksbstate);
+
+ /* If it fails, we don't care why. Default to secure */
+ if (status != EFI_SUCCESS)
+ goto secure_boot_enabled;
+ if (!(attr & EFI_VARIABLE_RUNTIME_ACCESS) && moksbstate == 1)
+ return efi_secureboot_mode_disabled;
+
+secure_boot_enabled:
+ pr_efi(sys_table_arg, "UEFI Secure Boot is enabled.\n");
+ return efi_secureboot_mode_enabled;
+
+out_efi_err:
+ pr_efi_err(sys_table_arg, "Could not determine UEFI Secure Boot status.\n");
+ if (status == EFI_NOT_FOUND)
+ return efi_secureboot_mode_disabled;
+ return efi_secureboot_mode_unknown;
+}
diff --git a/drivers/firmware/efi/memattr.c b/drivers/firmware/efi/memattr.c
index 236004b..8986757 100644
--- a/drivers/firmware/efi/memattr.c
+++ b/drivers/firmware/efi/memattr.c
@@ -43,6 +43,7 @@
tbl_size = sizeof(*tbl) + tbl->num_entries * tbl->desc_size;
memblock_reserve(efi.mem_attr_table, tbl_size);
+ set_bit(EFI_MEM_ATTR, &efi.flags);
unmap:
early_memunmap(tbl, sizeof(*tbl));
@@ -174,8 +175,11 @@
md.phys_addr + size - 1,
efi_md_typeattr_format(buf, sizeof(buf), &md));
- if (valid)
+ if (valid) {
ret = fn(mm, &md);
+ if (ret)
+ pr_err("Error updating mappings, skipping subsequent md's\n");
+ }
}
memunmap(tbl);
return ret;
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index aa64448..f59771d 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -1817,7 +1817,7 @@
mgr->payloads[i].vcpi = req_payload.vcpi;
} else if (mgr->payloads[i].num_slots) {
mgr->payloads[i].num_slots = 0;
- drm_dp_destroy_payload_step1(mgr, port, port->vcpi.vcpi, &mgr->payloads[i]);
+ drm_dp_destroy_payload_step1(mgr, port, mgr->payloads[i].vcpi, &mgr->payloads[i]);
req_payload.payload_state = mgr->payloads[i].payload_state;
mgr->payloads[i].start_slot = 0;
}
diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c
index 1d6c335..33cd516 100644
--- a/drivers/gpu/drm/drm_gem_cma_helper.c
+++ b/drivers/gpu/drm/drm_gem_cma_helper.c
@@ -376,7 +376,7 @@
off = drm_vma_node_start(&obj->vma_node);
seq_printf(m, "%2d (%2d) %08llx %pad %p %zu",
- obj->name, obj->refcount.refcount.counter,
+ obj->name, kref_read(&obj->refcount),
off, &cma_obj->paddr, cma_obj->vaddr, obj->size);
seq_printf(m, "\n");
diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c
index ffb2ab3..6b68e90 100644
--- a/drivers/gpu/drm/drm_info.c
+++ b/drivers/gpu/drm/drm_info.c
@@ -118,7 +118,7 @@
seq_printf(m, "%6d %8zd %7d %8d\n",
obj->name, obj->size,
obj->handle_count,
- atomic_read(&obj->refcount.refcount));
+ kref_read(&obj->refcount));
return 0;
}
diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c
index 9f17085..c6885a4 100644
--- a/drivers/gpu/drm/drm_mode_object.c
+++ b/drivers/gpu/drm/drm_mode_object.c
@@ -159,7 +159,7 @@
void drm_mode_object_unreference(struct drm_mode_object *obj)
{
if (obj->free_cb) {
- DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount));
+ DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, kref_read(&obj->refcount));
kref_put(&obj->refcount, obj->free_cb);
}
}
@@ -176,7 +176,7 @@
void drm_mode_object_reference(struct drm_mode_object *obj)
{
if (obj->free_cb) {
- DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount));
+ DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, kref_read(&obj->refcount));
kref_get(&obj->refcount);
}
}
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
index 114dddb..aa6e35d 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
@@ -486,7 +486,7 @@
seq_printf(m, "%08x: %c %2d (%2d) %08lx %p %zd\n",
etnaviv_obj->flags, is_active(etnaviv_obj) ? 'A' : 'I',
- obj->name, obj->refcount.refcount.counter,
+ obj->name, kref_read(&obj->refcount),
off, etnaviv_obj->vaddr, obj->size);
rcu_read_lock();
diff --git a/drivers/gpu/drm/i915/i915_gem_object.h b/drivers/gpu/drm/i915/i915_gem_object.h
index 6a368de..ecfefb9 100644
--- a/drivers/gpu/drm/i915/i915_gem_object.h
+++ b/drivers/gpu/drm/i915/i915_gem_object.h
@@ -256,7 +256,7 @@
static inline bool
i915_gem_object_is_dead(const struct drm_i915_gem_object *obj)
{
- return atomic_read(&obj->base.refcount.refcount) == 0;
+ return kref_read(&obj->base.refcount) == 0;
}
static inline bool
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index 8098677..1974ccb 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -642,7 +642,7 @@
seq_printf(m, "%08x: %c %2d (%2d) %08llx %p\t",
msm_obj->flags, is_active(msm_obj) ? 'A' : 'I',
- obj->name, obj->refcount.refcount.counter,
+ obj->name, kref_read(&obj->refcount),
off, msm_obj->vaddr);
for (id = 0; id < priv->num_aspaces; id++)
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
index a6126c9..88ee60d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
@@ -527,7 +527,7 @@
* caller should have a reference on the fence,
* else fence could get freed here
*/
- WARN_ON(atomic_read(&fence->base.refcount.refcount) <= 1);
+ WARN_ON(kref_read(&fence->base.refcount) <= 1);
/*
* This needs uevents to work correctly, but dma_fence_add_callback relies on
diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c
index 4a90c69..74a9968 100644
--- a/drivers/gpu/drm/omapdrm/omap_gem.c
+++ b/drivers/gpu/drm/omapdrm/omap_gem.c
@@ -1033,7 +1033,7 @@
off = drm_vma_node_start(&obj->vma_node);
seq_printf(m, "%08x: %2d (%2d) %08llx %pad (%2d) %p %4d",
- omap_obj->flags, obj->name, obj->refcount.refcount.counter,
+ omap_obj->flags, obj->name, kref_read(&obj->refcount),
off, &omap_obj->paddr, omap_obj->paddr_cnt,
omap_obj->vaddr, omap_obj->roll);
diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c
index fb16070..4a4f953 100644
--- a/drivers/gpu/drm/radeon/radeon_cursor.c
+++ b/drivers/gpu/drm/radeon/radeon_cursor.c
@@ -205,8 +205,8 @@
}
if (x <= (crtc->x - w) || y <= (crtc->y - radeon_crtc->cursor_height) ||
- x >= (crtc->x + crtc->mode.crtc_hdisplay) ||
- y >= (crtc->y + crtc->mode.crtc_vdisplay))
+ x >= (crtc->x + crtc->mode.hdisplay) ||
+ y >= (crtc->y + crtc->mode.vdisplay))
goto out_of_bounds;
x += xorigin;
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index d506361..ffc6cb5 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -140,8 +140,8 @@
struct ttm_bo_device *bdev = bo->bdev;
size_t acc_size = bo->acc_size;
- BUG_ON(atomic_read(&bo->list_kref.refcount));
- BUG_ON(atomic_read(&bo->kref.refcount));
+ BUG_ON(kref_read(&bo->list_kref));
+ BUG_ON(kref_read(&bo->kref));
BUG_ON(atomic_read(&bo->cpu_writers));
BUG_ON(bo->mem.mm_node != NULL);
BUG_ON(!list_empty(&bo->lru));
@@ -181,61 +181,46 @@
}
EXPORT_SYMBOL(ttm_bo_add_to_lru);
-int ttm_bo_del_from_lru(struct ttm_buffer_object *bo)
+static void ttm_bo_ref_bug(struct kref *list_kref)
+{
+ BUG();
+}
+
+void ttm_bo_del_from_lru(struct ttm_buffer_object *bo)
{
struct ttm_bo_device *bdev = bo->bdev;
- int put_count = 0;
if (bdev->driver->lru_removal)
bdev->driver->lru_removal(bo);
if (!list_empty(&bo->swap)) {
list_del_init(&bo->swap);
- ++put_count;
+ kref_put(&bo->list_kref, ttm_bo_ref_bug);
}
if (!list_empty(&bo->lru)) {
list_del_init(&bo->lru);
- ++put_count;
+ kref_put(&bo->list_kref, ttm_bo_ref_bug);
}
-
- return put_count;
-}
-
-static void ttm_bo_ref_bug(struct kref *list_kref)
-{
- BUG();
-}
-
-void ttm_bo_list_ref_sub(struct ttm_buffer_object *bo, int count,
- bool never_free)
-{
- kref_sub(&bo->list_kref, count,
- (never_free) ? ttm_bo_ref_bug : ttm_bo_release_list);
}
void ttm_bo_del_sub_from_lru(struct ttm_buffer_object *bo)
{
- int put_count;
-
spin_lock(&bo->glob->lru_lock);
- put_count = ttm_bo_del_from_lru(bo);
+ ttm_bo_del_from_lru(bo);
spin_unlock(&bo->glob->lru_lock);
- ttm_bo_list_ref_sub(bo, put_count, true);
}
EXPORT_SYMBOL(ttm_bo_del_sub_from_lru);
void ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo)
{
struct ttm_bo_device *bdev = bo->bdev;
- int put_count = 0;
lockdep_assert_held(&bo->resv->lock.base);
if (bdev->driver->lru_removal)
bdev->driver->lru_removal(bo);
- put_count = ttm_bo_del_from_lru(bo);
- ttm_bo_list_ref_sub(bo, put_count, true);
+ ttm_bo_del_from_lru(bo);
ttm_bo_add_to_lru(bo);
}
EXPORT_SYMBOL(ttm_bo_move_to_lru_tail);
@@ -447,7 +432,6 @@
{
struct ttm_bo_device *bdev = bo->bdev;
struct ttm_bo_global *glob = bo->glob;
- int put_count;
int ret;
spin_lock(&glob->lru_lock);
@@ -455,13 +439,10 @@
if (!ret) {
if (!ttm_bo_wait(bo, false, true)) {
- put_count = ttm_bo_del_from_lru(bo);
-
+ ttm_bo_del_from_lru(bo);
spin_unlock(&glob->lru_lock);
ttm_bo_cleanup_memtype_use(bo);
- ttm_bo_list_ref_sub(bo, put_count, true);
-
return;
} else
ttm_bo_flush_all_fences(bo);
@@ -504,7 +485,6 @@
bool no_wait_gpu)
{
struct ttm_bo_global *glob = bo->glob;
- int put_count;
int ret;
ret = ttm_bo_wait(bo, false, true);
@@ -554,15 +534,13 @@
return ret;
}
- put_count = ttm_bo_del_from_lru(bo);
+ ttm_bo_del_from_lru(bo);
list_del_init(&bo->ddestroy);
- ++put_count;
+ kref_put(&bo->list_kref, ttm_bo_ref_bug);
spin_unlock(&glob->lru_lock);
ttm_bo_cleanup_memtype_use(bo);
- ttm_bo_list_ref_sub(bo, put_count, true);
-
return 0;
}
@@ -740,7 +718,7 @@
struct ttm_bo_global *glob = bdev->glob;
struct ttm_mem_type_manager *man = &bdev->man[mem_type];
struct ttm_buffer_object *bo;
- int ret = -EBUSY, put_count;
+ int ret = -EBUSY;
spin_lock(&glob->lru_lock);
list_for_each_entry(bo, &man->lru, lru) {
@@ -771,13 +749,11 @@
return ret;
}
- put_count = ttm_bo_del_from_lru(bo);
+ ttm_bo_del_from_lru(bo);
spin_unlock(&glob->lru_lock);
BUG_ON(ret != 0);
- ttm_bo_list_ref_sub(bo, put_count, true);
-
ret = ttm_bo_evict(bo, interruptible, no_wait_gpu);
ttm_bo_unreserve(bo);
@@ -1669,7 +1645,6 @@
container_of(shrink, struct ttm_bo_global, shrink);
struct ttm_buffer_object *bo;
int ret = -EBUSY;
- int put_count;
uint32_t swap_placement = (TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM);
spin_lock(&glob->lru_lock);
@@ -1692,11 +1667,9 @@
return ret;
}
- put_count = ttm_bo_del_from_lru(bo);
+ ttm_bo_del_from_lru(bo);
spin_unlock(&glob->lru_lock);
- ttm_bo_list_ref_sub(bo, put_count, true);
-
/**
* Move to system cached
*/
diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c
index d35bc49..5e1bcab 100644
--- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c
+++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c
@@ -48,9 +48,7 @@
list_for_each_entry(entry, list, head) {
struct ttm_buffer_object *bo = entry->bo;
- unsigned put_count = ttm_bo_del_from_lru(bo);
-
- ttm_bo_list_ref_sub(bo, put_count, true);
+ ttm_bo_del_from_lru(bo);
}
}
diff --git a/drivers/gpu/drm/ttm/ttm_object.c b/drivers/gpu/drm/ttm/ttm_object.c
index 4f5fa8d..fdb451e 100644
--- a/drivers/gpu/drm/ttm/ttm_object.c
+++ b/drivers/gpu/drm/ttm/ttm_object.c
@@ -304,7 +304,7 @@
* Verify that the ref->obj pointer was actually valid!
*/
rmb();
- if (unlikely(atomic_read(&ref->kref.refcount) == 0))
+ if (unlikely(kref_read(&ref->kref) == 0))
goto out_false;
rcu_read_unlock();
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 4070b73..1aeb80e 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -785,6 +785,11 @@
config HID_RMI
tristate "Synaptics RMI4 device support"
depends on HID
+ select RMI4_CORE
+ select RMI4_F03
+ select RMI4_F11
+ select RMI4_F12
+ select RMI4_F30
---help---
Support for Synaptics RMI4 touchpads.
Say Y here if you have a Synaptics RMI4 touchpads over i2c-hid or usbhid
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index ea36b55..538ff69 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -43,7 +43,6 @@
*/
#define DRIVER_DESC "HID core driver"
-#define DRIVER_LICENSE "GPL"
int hid_debug = 0;
module_param_named(debug, hid_debug, int, 0600);
@@ -724,13 +723,7 @@
hid->group = HID_GROUP_SENSOR_HUB;
if (hid->vendor == USB_VENDOR_ID_MICROSOFT &&
- (hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 ||
- hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 ||
- hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP ||
- hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_4 ||
- hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2 ||
- hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP ||
- hid->product == USB_DEVICE_ID_MS_POWER_COVER) &&
+ hid->product == USB_DEVICE_ID_MS_POWER_COVER &&
hid->group == HID_GROUP_MULTITOUCH)
hid->group = HID_GROUP_GENERIC;
@@ -826,7 +819,8 @@
hid->group = HID_GROUP_WACOM;
break;
case USB_VENDOR_ID_SYNAPTICS:
- if (hid->group == HID_GROUP_GENERIC)
+ if (hid->group == HID_GROUP_GENERIC ||
+ hid->group == HID_GROUP_MULTITOUCH_WIN_8)
if ((parser->scan_flags & HID_SCAN_FLAG_VENDOR_SPECIFIC)
&& (parser->scan_flags & HID_SCAN_FLAG_GD_POINTER))
/*
@@ -1887,6 +1881,9 @@
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) },
#if IS_ENABLED(CONFIG_HID_MAYFLASH)
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE1) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE2) },
#endif
{ HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_WN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_FA) },
@@ -1933,6 +1930,7 @@
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPPRODOCK) },
#endif
+ { HID_USB_DEVICE(USB_VENDOR_ID_LG, USB_DEVICE_ID_LG_MELFAS_MT) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
@@ -1985,12 +1983,6 @@
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3) },
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) },
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) },
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4) },
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2) },
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3KV1) },
@@ -2126,6 +2118,7 @@
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLADE_14) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM6533) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_COVER) },
{ }
};
@@ -2314,7 +2307,7 @@
static int hid_uevent(struct device *dev, struct kobj_uevent_env *env)
{
- struct hid_device *hdev = to_hid_device(dev);
+ struct hid_device *hdev = to_hid_device(dev);
if (add_uevent_var(env, "HID_ID=%04X:%08X:%08X",
hdev->bus, hdev->vendor, hdev->product))
@@ -2867,5 +2860,5 @@
MODULE_AUTHOR("Andreas Gal");
MODULE_AUTHOR("Vojtech Pavlik");
MODULE_AUTHOR("Jiri Kosina");
-MODULE_LICENSE(DRIVER_LICENSE);
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 350accf..86c95d3 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -323,7 +323,8 @@
#define USB_DEVICE_ID_DRAGONRISE_WIIU 0x1800
#define USB_DEVICE_ID_DRAGONRISE_PS3 0x1801
#define USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR 0x1803
-#define USB_DEVICE_ID_DRAGONRISE_GAMECUBE 0x1843
+#define USB_DEVICE_ID_DRAGONRISE_GAMECUBE1 0x1843
+#define USB_DEVICE_ID_DRAGONRISE_GAMECUBE2 0x1844
#define USB_VENDOR_ID_DWAV 0x0eef
#define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001
@@ -630,9 +631,11 @@
#define USB_DEVICE_ID_LENOVO_CUSBKBD 0x6047
#define USB_DEVICE_ID_LENOVO_CBTKBD 0x6048
#define USB_DEVICE_ID_LENOVO_TPPRODOCK 0x6067
+#define USB_DEVICE_ID_LENOVO_X1_COVER 0x6085
#define USB_VENDOR_ID_LG 0x1fd2
#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
+#define USB_DEVICE_ID_LG_MELFAS_MT 0x6007
#define USB_VENDOR_ID_LOGITECH 0x046d
#define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e
@@ -725,12 +728,6 @@
#define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799
#define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7
#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9
-#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 0x07dc
-#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 0x07e2
-#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP 0x07dd
-#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4 0x07e4
-#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2 0x07e8
-#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP 0x07e9
#define USB_DEVICE_ID_MS_POWER_COVER 0x07da
#define USB_VENDOR_ID_MOJO 0x8282
diff --git a/drivers/hid/hid-mf.c b/drivers/hid/hid-mf.c
index d909076..03f1051 100644
--- a/drivers/hid/hid-mf.c
+++ b/drivers/hid/hid-mf.c
@@ -6,12 +6,14 @@
*
* Tested with:
* 0079:1801 "DragonRise Inc. Mayflash PS3 Game Controller Adapter"
+ * 0079:1803 "DragonRise Inc. Mayflash Wireless Sensor DolphinBar"
+ * 0079:1843 "DragonRise Inc. Mayflash GameCube Game Controller Adapter"
+ * 0079:1844 "DragonRise Inc. Mayflash GameCube Game Controller Adapter (v04)"
*
* The following adapters probably work too, but need to be tested:
* 0079:1800 "DragonRise Inc. Mayflash WIIU Game Controller Adapter"
- * 0079:1843 "DragonRise Inc. Mayflash GameCube Game Controller Adapter"
*
- * Copyright (c) 2016 Marcel Hasler <mahasler@gmail.com>
+ * Copyright (c) 2016-2017 Marcel Hasler <mahasler@gmail.com>
*/
/*
@@ -125,8 +127,8 @@
dev_dbg(&hid->dev, "Mayflash HID hardware probe...\n");
- /* Split device into four inputs */
- hid->quirks |= HID_QUIRK_MULTI_INPUT;
+ /* Apply quirks as needed */
+ hid->quirks |= id->driver_data;
error = hid_parse(hid);
if (error) {
@@ -151,7 +153,14 @@
}
static const struct hid_device_id mf_devices[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3), },
+ { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3),
+ .driver_data = HID_QUIRK_MULTI_INPUT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR),
+ .driver_data = HID_QUIRK_MULTI_INPUT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE1),
+ .driver_data = HID_QUIRK_MULTI_INPUT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE2),
+ .driver_data = 0 }, /* No quirk required */
{ }
};
MODULE_DEVICE_TABLE(hid, mf_devices);
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index 74b7b84..96e7d32 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -274,18 +274,6 @@
.driver_data = MS_NOGET },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500),
.driver_data = MS_DUPLICATE_USAGES },
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3),
- .driver_data = MS_HIDINPUT },
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2),
- .driver_data = MS_HIDINPUT },
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP),
- .driver_data = MS_HIDINPUT },
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4),
- .driver_data = MS_HIDINPUT },
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2),
- .driver_data = MS_HIDINPUT },
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP),
- .driver_data = MS_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER),
.driver_data = MS_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD),
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 6dca668..6926474 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -68,6 +68,7 @@
#define MT_QUIRK_HOVERING (1 << 11)
#define MT_QUIRK_CONTACT_CNT_ACCURATE (1 << 12)
#define MT_QUIRK_FORCE_GET_FEATURE (1 << 13)
+#define MT_QUIRK_FIX_CONST_CONTACT_ID (1 << 14)
#define MT_INPUTMODE_TOUCHSCREEN 0x02
#define MT_INPUTMODE_TOUCHPAD 0x03
@@ -157,6 +158,7 @@
#define MT_CLS_FLATFROG 0x0107
#define MT_CLS_GENERALTOUCH_TWOFINGERS 0x0108
#define MT_CLS_GENERALTOUCH_PWT_TENFINGERS 0x0109
+#define MT_CLS_LG 0x010a
#define MT_CLS_VTL 0x0110
#define MT_DEFAULT_MAXCONTACT 10
@@ -263,6 +265,12 @@
.sn_move = 2048,
.maxcontacts = 40,
},
+ { .name = MT_CLS_LG,
+ .quirks = MT_QUIRK_ALWAYS_VALID |
+ MT_QUIRK_FIX_CONST_CONTACT_ID |
+ MT_QUIRK_IGNORE_DUPLICATES |
+ MT_QUIRK_HOVERING |
+ MT_QUIRK_CONTACT_CNT_ACCURATE },
{ .name = MT_CLS_VTL,
.quirks = MT_QUIRK_ALWAYS_VALID |
MT_QUIRK_CONTACT_CNT_ACCURATE |
@@ -1078,6 +1086,34 @@
return 0;
}
+static void mt_fix_const_field(struct hid_field *field, unsigned int usage)
+{
+ if (field->usage[0].hid != usage ||
+ !(field->flags & HID_MAIN_ITEM_CONSTANT))
+ return;
+
+ field->flags &= ~HID_MAIN_ITEM_CONSTANT;
+ field->flags |= HID_MAIN_ITEM_VARIABLE;
+}
+
+static void mt_fix_const_fields(struct hid_device *hdev, unsigned int usage)
+{
+ struct hid_report *report;
+ int i;
+
+ list_for_each_entry(report,
+ &hdev->report_enum[HID_INPUT_REPORT].report_list,
+ list) {
+
+ if (!report->maxfield)
+ continue;
+
+ for (i = 0; i < report->maxfield; i++)
+ if (report->field[i]->maxusage >= 1)
+ mt_fix_const_field(report->field[i], usage);
+ }
+}
+
static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret, i;
@@ -1151,6 +1187,9 @@
if (ret != 0)
return ret;
+ if (mtclass->quirks & MT_QUIRK_FIX_CONST_CONTACT_ID)
+ mt_fix_const_fields(hdev, HID_DG_CONTACTID);
+
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret)
return ret;
@@ -1398,6 +1437,11 @@
MT_USB_DEVICE(USB_VENDOR_ID_ILITEK,
USB_DEVICE_ID_ILITEK_MULTITOUCH) },
+ /* LG Melfas panel */
+ { .driver_data = MT_CLS_LG,
+ HID_USB_DEVICE(USB_VENDOR_ID_LG,
+ USB_DEVICE_ID_LG_MELFAS_MT) },
+
/* MosArt panels */
{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,
MT_USB_DEVICE(USB_VENDOR_ID_ASUS,
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
index be89bcb..5b40c26 100644
--- a/drivers/hid/hid-rmi.c
+++ b/drivers/hid/hid-rmi.c
@@ -14,11 +14,14 @@
#include <linux/hid.h>
#include <linux/input.h>
#include <linux/input/mt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/sched.h>
+#include <linux/rmi.h>
#include "hid-ids.h"
#define RMI_MOUSE_REPORT_ID 0x01 /* Mouse emulation Report */
@@ -33,9 +36,6 @@
#define RMI_READ_DATA_PENDING 1
#define RMI_STARTED 2
-#define RMI_SLEEP_NORMAL 0x0
-#define RMI_SLEEP_DEEP_SLEEP 0x1
-
/* device flags */
#define RMI_DEVICE BIT(0)
#define RMI_DEVICE_HAS_PHYS_BUTTONS BIT(1)
@@ -54,25 +54,12 @@
RMI_MODE_NO_PACKED_ATTN_REPORTS = 2,
};
-struct rmi_function {
- unsigned page; /* page of the function */
- u16 query_base_addr; /* base address for queries */
- u16 command_base_addr; /* base address for commands */
- u16 control_base_addr; /* base address for controls */
- u16 data_base_addr; /* base address for datas */
- unsigned int interrupt_base; /* cross-function interrupt number
- * (uniq in the device)*/
- unsigned int interrupt_count; /* number of interrupts */
- unsigned int report_size; /* size of a report */
- unsigned long irq_mask; /* mask of the interrupts
- * (to be applied against ATTN IRQ) */
-};
-
/**
* struct rmi_data - stores information for hid communication
*
* @page_mutex: Locks current page to avoid changing pages in unexpected ways.
* @page: Keeps track of the current virtual page
+ * @xport: transport device to be registered with the RMI4 core.
*
* @wait: Used for waiting for read data
*
@@ -84,26 +71,18 @@
*
* @flags: flags for the current device (started, reading, etc...)
*
- * @f11: placeholder of internal RMI function F11 description
- * @f30: placeholder of internal RMI function F30 description
- *
- * @max_fingers: maximum finger count reported by the device
- * @max_x: maximum x value reported by the device
- * @max_y: maximum y value reported by the device
- *
- * @gpio_led_count: count of GPIOs + LEDs reported by F30
- * @button_count: actual physical buttons count
- * @button_mask: button mask used to decode GPIO ATTN reports
- * @button_state_mask: pull state of the buttons
- *
- * @input: pointer to the kernel input device
- *
* @reset_work: worker which will be called in case of a mouse report
* @hdev: pointer to the struct hid_device
+ *
+ * @device_flags: flags which describe the device
+ *
+ * @domain: the IRQ domain allocated for this RMI4 device
+ * @rmi_irq: the irq that will be used to generate events to rmi-core
*/
struct rmi_data {
struct mutex page_mutex;
int page;
+ struct rmi_transport_dev xport;
wait_queue_head_t wait;
@@ -115,34 +94,13 @@
unsigned long flags;
- struct rmi_function f01;
- struct rmi_function f11;
- struct rmi_function f30;
-
- unsigned int max_fingers;
- unsigned int max_x;
- unsigned int max_y;
- unsigned int x_size_mm;
- unsigned int y_size_mm;
- bool read_f11_ctrl_regs;
- u8 f11_ctrl_regs[RMI_F11_CTRL_REG_COUNT];
-
- unsigned int gpio_led_count;
- unsigned int button_count;
- unsigned long button_mask;
- unsigned long button_state_mask;
-
- struct input_dev *input;
-
struct work_struct reset_work;
struct hid_device *hdev;
unsigned long device_flags;
- unsigned long firmware_id;
- u8 f01_ctrl0;
- u8 interrupt_enable_mask;
- bool restore_interrupt_mask;
+ struct irq_domain *domain;
+ int rmi_irq;
};
#define RMI_PAGE(addr) (((addr) >> 8) & 0xff)
@@ -220,10 +178,11 @@
return ret;
}
-static int rmi_read_block(struct hid_device *hdev, u16 addr, void *buf,
- const int len)
+static int rmi_hid_read_block(struct rmi_transport_dev *xport, u16 addr,
+ void *buf, size_t len)
{
- struct rmi_data *data = hid_get_drvdata(hdev);
+ struct rmi_data *data = container_of(xport, struct rmi_data, xport);
+ struct hid_device *hdev = data->hdev;
int ret;
int bytes_read;
int bytes_needed;
@@ -292,15 +251,11 @@
return ret;
}
-static inline int rmi_read(struct hid_device *hdev, u16 addr, void *buf)
+static int rmi_hid_write_block(struct rmi_transport_dev *xport, u16 addr,
+ const void *buf, size_t len)
{
- return rmi_read_block(hdev, addr, buf, 1);
-}
-
-static int rmi_write_block(struct hid_device *hdev, u16 addr, void *buf,
- const int len)
-{
- struct rmi_data *data = hid_get_drvdata(hdev);
+ struct rmi_data *data = container_of(xport, struct rmi_data, xport);
+ struct hid_device *hdev = data->hdev;
int ret;
mutex_lock(&data->page_mutex);
@@ -332,62 +287,20 @@
return ret;
}
-static inline int rmi_write(struct hid_device *hdev, u16 addr, void *buf)
-{
- return rmi_write_block(hdev, addr, buf, 1);
-}
-
-static void rmi_f11_process_touch(struct rmi_data *hdata, int slot,
- u8 finger_state, u8 *touch_data)
-{
- int x, y, wx, wy;
- int wide, major, minor;
- int z;
-
- input_mt_slot(hdata->input, slot);
- input_mt_report_slot_state(hdata->input, MT_TOOL_FINGER,
- finger_state == 0x01);
- if (finger_state == 0x01) {
- x = (touch_data[0] << 4) | (touch_data[2] & 0x0F);
- y = (touch_data[1] << 4) | (touch_data[2] >> 4);
- wx = touch_data[3] & 0x0F;
- wy = touch_data[3] >> 4;
- wide = (wx > wy);
- major = max(wx, wy);
- minor = min(wx, wy);
- z = touch_data[4];
-
- /* y is inverted */
- y = hdata->max_y - y;
-
- input_event(hdata->input, EV_ABS, ABS_MT_POSITION_X, x);
- input_event(hdata->input, EV_ABS, ABS_MT_POSITION_Y, y);
- input_event(hdata->input, EV_ABS, ABS_MT_ORIENTATION, wide);
- input_event(hdata->input, EV_ABS, ABS_MT_PRESSURE, z);
- input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
- input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
- }
-}
-
static int rmi_reset_attn_mode(struct hid_device *hdev)
{
struct rmi_data *data = hid_get_drvdata(hdev);
+ struct rmi_device *rmi_dev = data->xport.rmi_dev;
int ret;
ret = rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
if (ret)
return ret;
- if (data->restore_interrupt_mask) {
- ret = rmi_write(hdev, data->f01.control_base_addr + 1,
- &data->interrupt_enable_mask);
- if (ret) {
- hid_err(hdev, "can not write F01 control register\n");
- return ret;
- }
- }
+ if (test_bit(RMI_STARTED, &data->flags))
+ ret = rmi_dev->driver->reset_handler(rmi_dev);
- return 0;
+ return ret;
}
static void rmi_reset_work(struct work_struct *work)
@@ -399,102 +312,22 @@
rmi_reset_attn_mode(hdata->hdev);
}
-static inline int rmi_schedule_reset(struct hid_device *hdev)
-{
- struct rmi_data *hdata = hid_get_drvdata(hdev);
- return schedule_work(&hdata->reset_work);
-}
-
-static int rmi_f11_input_event(struct hid_device *hdev, u8 irq, u8 *data,
- int size)
-{
- struct rmi_data *hdata = hid_get_drvdata(hdev);
- int offset;
- int i;
-
- if (!(irq & hdata->f11.irq_mask) || size <= 0)
- return 0;
-
- offset = (hdata->max_fingers >> 2) + 1;
- for (i = 0; i < hdata->max_fingers; i++) {
- int fs_byte_position = i >> 2;
- int fs_bit_position = (i & 0x3) << 1;
- int finger_state = (data[fs_byte_position] >> fs_bit_position) &
- 0x03;
- int position = offset + 5 * i;
-
- if (position + 5 > size) {
- /* partial report, go on with what we received */
- printk_once(KERN_WARNING
- "%s %s: Detected incomplete finger report. Finger reports may occasionally get dropped on this platform.\n",
- dev_driver_string(&hdev->dev),
- dev_name(&hdev->dev));
- hid_dbg(hdev, "Incomplete finger report\n");
- break;
- }
-
- rmi_f11_process_touch(hdata, i, finger_state, &data[position]);
- }
- input_mt_sync_frame(hdata->input);
- input_sync(hdata->input);
- return hdata->f11.report_size;
-}
-
-static int rmi_f30_input_event(struct hid_device *hdev, u8 irq, u8 *data,
- int size)
-{
- struct rmi_data *hdata = hid_get_drvdata(hdev);
- int i;
- int button = 0;
- bool value;
-
- if (!(irq & hdata->f30.irq_mask))
- return 0;
-
- if (size < (int)hdata->f30.report_size) {
- hid_warn(hdev, "Click Button pressed, but the click data is missing\n");
- return 0;
- }
-
- for (i = 0; i < hdata->gpio_led_count; i++) {
- if (test_bit(i, &hdata->button_mask)) {
- value = (data[i / 8] >> (i & 0x07)) & BIT(0);
- if (test_bit(i, &hdata->button_state_mask))
- value = !value;
- input_event(hdata->input, EV_KEY, BTN_LEFT + button++,
- value);
- }
- }
- return hdata->f30.report_size;
-}
-
static int rmi_input_event(struct hid_device *hdev, u8 *data, int size)
{
struct rmi_data *hdata = hid_get_drvdata(hdev);
- unsigned long irq_mask = 0;
- unsigned index = 2;
+ struct rmi_device *rmi_dev = hdata->xport.rmi_dev;
+ unsigned long flags;
if (!(test_bit(RMI_STARTED, &hdata->flags)))
return 0;
- irq_mask |= hdata->f11.irq_mask;
- irq_mask |= hdata->f30.irq_mask;
+ local_irq_save(flags);
- if (data[1] & ~irq_mask)
- hid_dbg(hdev, "unknown intr source:%02lx %s:%d\n",
- data[1] & ~irq_mask, __FILE__, __LINE__);
+ rmi_set_attn_data(rmi_dev, data[1], &data[2], size - 2);
- if (hdata->f11.interrupt_base < hdata->f30.interrupt_base) {
- index += rmi_f11_input_event(hdev, data[1], &data[index],
- size - index);
- index += rmi_f30_input_event(hdev, data[1], &data[index],
- size - index);
- } else {
- index += rmi_f30_input_event(hdev, data[1], &data[index],
- size - index);
- index += rmi_f11_input_event(hdev, data[1], &data[index],
- size - index);
- }
+ generic_handle_irq(hdata->rmi_irq);
+
+ local_irq_restore(flags);
return 1;
}
@@ -568,7 +401,7 @@
return 1;
}
- rmi_schedule_reset(hdev);
+ schedule_work(&data->reset_work);
return 1;
}
@@ -576,637 +409,71 @@
}
#ifdef CONFIG_PM
-static int rmi_set_sleep_mode(struct hid_device *hdev, int sleep_mode)
+static int rmi_suspend(struct hid_device *hdev, pm_message_t message)
{
struct rmi_data *data = hid_get_drvdata(hdev);
+ struct rmi_device *rmi_dev = data->xport.rmi_dev;
int ret;
- u8 f01_ctrl0;
- f01_ctrl0 = (data->f01_ctrl0 & ~0x3) | sleep_mode;
+ if (!(data->device_flags & RMI_DEVICE))
+ return 0;
- ret = rmi_write(hdev, data->f01.control_base_addr,
- &f01_ctrl0);
+ ret = rmi_driver_suspend(rmi_dev, false);
if (ret) {
- hid_err(hdev, "can not write sleep mode\n");
+ hid_warn(hdev, "Failed to suspend device: %d\n", ret);
return ret;
}
return 0;
}
-static int rmi_suspend(struct hid_device *hdev, pm_message_t message)
+static int rmi_post_resume(struct hid_device *hdev)
{
struct rmi_data *data = hid_get_drvdata(hdev);
- int ret;
- u8 buf[RMI_F11_CTRL_REG_COUNT];
-
- if (!(data->device_flags & RMI_DEVICE))
- return 0;
-
- ret = rmi_read_block(hdev, data->f11.control_base_addr, buf,
- RMI_F11_CTRL_REG_COUNT);
- if (ret)
- hid_warn(hdev, "can not read F11 control registers\n");
- else
- memcpy(data->f11_ctrl_regs, buf, RMI_F11_CTRL_REG_COUNT);
-
-
- if (!device_may_wakeup(hdev->dev.parent))
- return rmi_set_sleep_mode(hdev, RMI_SLEEP_DEEP_SLEEP);
-
- return 0;
-}
-
-static int rmi_post_reset(struct hid_device *hdev)
-{
- struct rmi_data *data = hid_get_drvdata(hdev);
+ struct rmi_device *rmi_dev = data->xport.rmi_dev;
int ret;
if (!(data->device_flags & RMI_DEVICE))
return 0;
ret = rmi_reset_attn_mode(hdev);
+ if (ret)
+ return ret;
+
+ ret = rmi_driver_resume(rmi_dev, false);
if (ret) {
- hid_err(hdev, "can not set rmi mode\n");
+ hid_warn(hdev, "Failed to resume device: %d\n", ret);
return ret;
}
- if (data->read_f11_ctrl_regs) {
- ret = rmi_write_block(hdev, data->f11.control_base_addr,
- data->f11_ctrl_regs, RMI_F11_CTRL_REG_COUNT);
- if (ret)
- hid_warn(hdev,
- "can not write F11 control registers after reset\n");
- }
-
- if (!device_may_wakeup(hdev->dev.parent)) {
- ret = rmi_set_sleep_mode(hdev, RMI_SLEEP_NORMAL);
- if (ret) {
- hid_err(hdev, "can not write sleep mode\n");
- return ret;
- }
- }
-
- return ret;
-}
-
-static int rmi_post_resume(struct hid_device *hdev)
-{
- struct rmi_data *data = hid_get_drvdata(hdev);
-
- if (!(data->device_flags & RMI_DEVICE))
- return 0;
-
- return rmi_reset_attn_mode(hdev);
+ return 0;
}
#endif /* CONFIG_PM */
-#define RMI4_MAX_PAGE 0xff
-#define RMI4_PAGE_SIZE 0x0100
-
-#define PDT_START_SCAN_LOCATION 0x00e9
-#define PDT_END_SCAN_LOCATION 0x0005
-#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
-
-struct pdt_entry {
- u8 query_base_addr:8;
- u8 command_base_addr:8;
- u8 control_base_addr:8;
- u8 data_base_addr:8;
- u8 interrupt_source_count:3;
- u8 bits3and4:2;
- u8 function_version:2;
- u8 bit7:1;
- u8 function_number:8;
-} __attribute__((__packed__));
-
-static inline unsigned long rmi_gen_mask(unsigned irq_base, unsigned irq_count)
+static int rmi_hid_reset(struct rmi_transport_dev *xport, u16 reset_addr)
{
- return GENMASK(irq_count + irq_base - 1, irq_base);
-}
+ struct rmi_data *data = container_of(xport, struct rmi_data, xport);
+ struct hid_device *hdev = data->hdev;
-static void rmi_register_function(struct rmi_data *data,
- struct pdt_entry *pdt_entry, int page, unsigned interrupt_count)
-{
- struct rmi_function *f = NULL;
- u16 page_base = page << 8;
-
- switch (pdt_entry->function_number) {
- case 0x01:
- f = &data->f01;
- break;
- case 0x11:
- f = &data->f11;
- break;
- case 0x30:
- f = &data->f30;
- break;
- }
-
- if (f) {
- f->page = page;
- f->query_base_addr = page_base | pdt_entry->query_base_addr;
- f->command_base_addr = page_base | pdt_entry->command_base_addr;
- f->control_base_addr = page_base | pdt_entry->control_base_addr;
- f->data_base_addr = page_base | pdt_entry->data_base_addr;
- f->interrupt_base = interrupt_count;
- f->interrupt_count = pdt_entry->interrupt_source_count;
- f->irq_mask = rmi_gen_mask(f->interrupt_base,
- f->interrupt_count);
- data->interrupt_enable_mask |= f->irq_mask;
- }
-}
-
-static int rmi_scan_pdt(struct hid_device *hdev)
-{
- struct rmi_data *data = hid_get_drvdata(hdev);
- struct pdt_entry entry;
- int page;
- bool page_has_function;
- int i;
- int retval;
- int interrupt = 0;
- u16 page_start, pdt_start , pdt_end;
-
- hid_info(hdev, "Scanning PDT...\n");
-
- for (page = 0; (page <= RMI4_MAX_PAGE); page++) {
- page_start = RMI4_PAGE_SIZE * page;
- pdt_start = page_start + PDT_START_SCAN_LOCATION;
- pdt_end = page_start + PDT_END_SCAN_LOCATION;
-
- page_has_function = false;
- for (i = pdt_start; i >= pdt_end; i -= sizeof(entry)) {
- retval = rmi_read_block(hdev, i, &entry, sizeof(entry));
- if (retval) {
- hid_err(hdev,
- "Read of PDT entry at %#06x failed.\n",
- i);
- goto error_exit;
- }
-
- if (RMI4_END_OF_PDT(entry.function_number))
- break;
-
- page_has_function = true;
-
- hid_info(hdev, "Found F%02X on page %#04x\n",
- entry.function_number, page);
-
- rmi_register_function(data, &entry, page, interrupt);
- interrupt += entry.interrupt_source_count;
- }
-
- if (!page_has_function)
- break;
- }
-
- hid_info(hdev, "%s: Done with PDT scan.\n", __func__);
- retval = 0;
-
-error_exit:
- return retval;
-}
-
-#define RMI_DEVICE_F01_BASIC_QUERY_LEN 11
-
-static int rmi_populate_f01(struct hid_device *hdev)
-{
- struct rmi_data *data = hid_get_drvdata(hdev);
- u8 basic_queries[RMI_DEVICE_F01_BASIC_QUERY_LEN];
- u8 info[3];
- int ret;
- bool has_query42;
- bool has_lts;
- bool has_sensor_id;
- bool has_ds4_queries = false;
- bool has_build_id_query = false;
- bool has_package_id_query = false;
- u16 query_offset = data->f01.query_base_addr;
- u16 prod_info_addr;
- u8 ds4_query_len;
-
- ret = rmi_read_block(hdev, query_offset, basic_queries,
- RMI_DEVICE_F01_BASIC_QUERY_LEN);
- if (ret) {
- hid_err(hdev, "Can not read basic queries from Function 0x1.\n");
- return ret;
- }
-
- has_lts = !!(basic_queries[0] & BIT(2));
- has_sensor_id = !!(basic_queries[1] & BIT(3));
- has_query42 = !!(basic_queries[1] & BIT(7));
-
- query_offset += 11;
- prod_info_addr = query_offset + 6;
- query_offset += 10;
-
- if (has_lts)
- query_offset += 20;
-
- if (has_sensor_id)
- query_offset++;
-
- if (has_query42) {
- ret = rmi_read(hdev, query_offset, info);
- if (ret) {
- hid_err(hdev, "Can not read query42.\n");
- return ret;
- }
- has_ds4_queries = !!(info[0] & BIT(0));
- query_offset++;
- }
-
- if (has_ds4_queries) {
- ret = rmi_read(hdev, query_offset, &ds4_query_len);
- if (ret) {
- hid_err(hdev, "Can not read DS4 Query length.\n");
- return ret;
- }
- query_offset++;
-
- if (ds4_query_len > 0) {
- ret = rmi_read(hdev, query_offset, info);
- if (ret) {
- hid_err(hdev, "Can not read DS4 query.\n");
- return ret;
- }
-
- has_package_id_query = !!(info[0] & BIT(0));
- has_build_id_query = !!(info[0] & BIT(1));
- }
- }
-
- if (has_package_id_query)
- prod_info_addr++;
-
- if (has_build_id_query) {
- ret = rmi_read_block(hdev, prod_info_addr, info, 3);
- if (ret) {
- hid_err(hdev, "Can not read product info.\n");
- return ret;
- }
-
- data->firmware_id = info[1] << 8 | info[0];
- data->firmware_id += info[2] * 65536;
- }
-
- ret = rmi_read_block(hdev, data->f01.control_base_addr, info,
- 2);
-
- if (ret) {
- hid_err(hdev, "can not read f01 ctrl registers\n");
- return ret;
- }
-
- data->f01_ctrl0 = info[0];
-
- if (!info[1]) {
- /*
- * Do to a firmware bug in some touchpads the F01 interrupt
- * enable control register will be cleared on reset.
- * This will stop the touchpad from reporting data, so
- * if F01 CTRL1 is 0 then we need to explicitly enable
- * interrupts for the functions we want data for.
- */
- data->restore_interrupt_mask = true;
-
- ret = rmi_write(hdev, data->f01.control_base_addr + 1,
- &data->interrupt_enable_mask);
- if (ret) {
- hid_err(hdev, "can not write to control reg 1: %d.\n",
- ret);
- return ret;
- }
- }
-
- return 0;
-}
-
-static int rmi_populate_f11(struct hid_device *hdev)
-{
- struct rmi_data *data = hid_get_drvdata(hdev);
- u8 buf[20];
- int ret;
- bool has_query9;
- bool has_query10 = false;
- bool has_query11;
- bool has_query12;
- bool has_query27;
- bool has_query28;
- bool has_query36 = false;
- bool has_physical_props;
- bool has_gestures;
- bool has_rel;
- bool has_data40 = false;
- bool has_dribble = false;
- bool has_palm_detect = false;
- unsigned x_size, y_size;
- u16 query_offset;
-
- if (!data->f11.query_base_addr) {
- hid_err(hdev, "No 2D sensor found, giving up.\n");
- return -ENODEV;
- }
-
- /* query 0 contains some useful information */
- ret = rmi_read(hdev, data->f11.query_base_addr, buf);
- if (ret) {
- hid_err(hdev, "can not get query 0: %d.\n", ret);
- return ret;
- }
- has_query9 = !!(buf[0] & BIT(3));
- has_query11 = !!(buf[0] & BIT(4));
- has_query12 = !!(buf[0] & BIT(5));
- has_query27 = !!(buf[0] & BIT(6));
- has_query28 = !!(buf[0] & BIT(7));
-
- /* query 1 to get the max number of fingers */
- ret = rmi_read(hdev, data->f11.query_base_addr + 1, buf);
- if (ret) {
- hid_err(hdev, "can not get NumberOfFingers: %d.\n", ret);
- return ret;
- }
- data->max_fingers = (buf[0] & 0x07) + 1;
- if (data->max_fingers > 5)
- data->max_fingers = 10;
-
- data->f11.report_size = data->max_fingers * 5 +
- DIV_ROUND_UP(data->max_fingers, 4);
-
- if (!(buf[0] & BIT(4))) {
- hid_err(hdev, "No absolute events, giving up.\n");
- return -ENODEV;
- }
-
- has_rel = !!(buf[0] & BIT(3));
- has_gestures = !!(buf[0] & BIT(5));
-
- ret = rmi_read(hdev, data->f11.query_base_addr + 5, buf);
- if (ret) {
- hid_err(hdev, "can not get absolute data sources: %d.\n", ret);
- return ret;
- }
-
- has_dribble = !!(buf[0] & BIT(4));
-
- /*
- * At least 4 queries are guaranteed to be present in F11
- * +1 for query 5 which is present since absolute events are
- * reported and +1 for query 12.
- */
- query_offset = 6;
-
- if (has_rel)
- ++query_offset; /* query 6 is present */
-
- if (has_gestures) {
- /* query 8 to find out if query 10 exists */
- ret = rmi_read(hdev,
- data->f11.query_base_addr + query_offset + 1, buf);
- if (ret) {
- hid_err(hdev, "can not read gesture information: %d.\n",
- ret);
- return ret;
- }
- has_palm_detect = !!(buf[0] & BIT(0));
- has_query10 = !!(buf[0] & BIT(2));
-
- query_offset += 2; /* query 7 and 8 are present */
- }
-
- if (has_query9)
- ++query_offset;
-
- if (has_query10)
- ++query_offset;
-
- if (has_query11)
- ++query_offset;
-
- /* query 12 to know if the physical properties are reported */
- if (has_query12) {
- ret = rmi_read(hdev, data->f11.query_base_addr
- + query_offset, buf);
- if (ret) {
- hid_err(hdev, "can not get query 12: %d.\n", ret);
- return ret;
- }
- has_physical_props = !!(buf[0] & BIT(5));
-
- if (has_physical_props) {
- query_offset += 1;
- ret = rmi_read_block(hdev,
- data->f11.query_base_addr
- + query_offset, buf, 4);
- if (ret) {
- hid_err(hdev, "can not read query 15-18: %d.\n",
- ret);
- return ret;
- }
-
- x_size = buf[0] | (buf[1] << 8);
- y_size = buf[2] | (buf[3] << 8);
-
- data->x_size_mm = DIV_ROUND_CLOSEST(x_size, 10);
- data->y_size_mm = DIV_ROUND_CLOSEST(y_size, 10);
-
- hid_info(hdev, "%s: size in mm: %d x %d\n",
- __func__, data->x_size_mm, data->y_size_mm);
-
- /*
- * query 15 - 18 contain the size of the sensor
- * and query 19 - 26 contain bezel dimensions
- */
- query_offset += 12;
- }
- }
-
- if (has_query27)
- ++query_offset;
-
- if (has_query28) {
- ret = rmi_read(hdev, data->f11.query_base_addr
- + query_offset, buf);
- if (ret) {
- hid_err(hdev, "can not get query 28: %d.\n", ret);
- return ret;
- }
-
- has_query36 = !!(buf[0] & BIT(6));
- }
-
- if (has_query36) {
- query_offset += 2;
- ret = rmi_read(hdev, data->f11.query_base_addr
- + query_offset, buf);
- if (ret) {
- hid_err(hdev, "can not get query 36: %d.\n", ret);
- return ret;
- }
-
- has_data40 = !!(buf[0] & BIT(5));
- }
-
-
- if (has_data40)
- data->f11.report_size += data->max_fingers * 2;
-
- ret = rmi_read_block(hdev, data->f11.control_base_addr,
- data->f11_ctrl_regs, RMI_F11_CTRL_REG_COUNT);
- if (ret) {
- hid_err(hdev, "can not read ctrl block of size 11: %d.\n", ret);
- return ret;
- }
-
- /* data->f11_ctrl_regs now contains valid register data */
- data->read_f11_ctrl_regs = true;
-
- data->max_x = data->f11_ctrl_regs[6] | (data->f11_ctrl_regs[7] << 8);
- data->max_y = data->f11_ctrl_regs[8] | (data->f11_ctrl_regs[9] << 8);
-
- if (has_dribble) {
- data->f11_ctrl_regs[0] = data->f11_ctrl_regs[0] & ~BIT(6);
- ret = rmi_write(hdev, data->f11.control_base_addr,
- data->f11_ctrl_regs);
- if (ret) {
- hid_err(hdev, "can not write to control reg 0: %d.\n",
- ret);
- return ret;
- }
- }
-
- if (has_palm_detect) {
- data->f11_ctrl_regs[11] = data->f11_ctrl_regs[11] & ~BIT(0);
- ret = rmi_write(hdev, data->f11.control_base_addr + 11,
- &data->f11_ctrl_regs[11]);
- if (ret) {
- hid_err(hdev, "can not write to control reg 11: %d.\n",
- ret);
- return ret;
- }
- }
-
- return 0;
-}
-
-static int rmi_populate_f30(struct hid_device *hdev)
-{
- struct rmi_data *data = hid_get_drvdata(hdev);
- u8 buf[20];
- int ret;
- bool has_gpio, has_led;
- unsigned bytes_per_ctrl;
- u8 ctrl2_addr;
- int ctrl2_3_length;
- int i;
-
- /* function F30 is for physical buttons */
- if (!data->f30.query_base_addr) {
- hid_err(hdev, "No GPIO/LEDs found, giving up.\n");
- return -ENODEV;
- }
-
- ret = rmi_read_block(hdev, data->f30.query_base_addr, buf, 2);
- if (ret) {
- hid_err(hdev, "can not get F30 query registers: %d.\n", ret);
- return ret;
- }
-
- has_gpio = !!(buf[0] & BIT(3));
- has_led = !!(buf[0] & BIT(2));
- data->gpio_led_count = buf[1] & 0x1f;
-
- /* retrieve ctrl 2 & 3 registers */
- bytes_per_ctrl = (data->gpio_led_count + 7) / 8;
- /* Ctrl0 is present only if both has_gpio and has_led are set*/
- ctrl2_addr = (has_gpio && has_led) ? bytes_per_ctrl : 0;
- /* Ctrl1 is always be present */
- ctrl2_addr += bytes_per_ctrl;
- ctrl2_3_length = 2 * bytes_per_ctrl;
-
- data->f30.report_size = bytes_per_ctrl;
-
- ret = rmi_read_block(hdev, data->f30.control_base_addr + ctrl2_addr,
- buf, ctrl2_3_length);
- if (ret) {
- hid_err(hdev, "can not read ctrl 2&3 block of size %d: %d.\n",
- ctrl2_3_length, ret);
- return ret;
- }
-
- for (i = 0; i < data->gpio_led_count; i++) {
- int byte_position = i >> 3;
- int bit_position = i & 0x07;
- u8 dir_byte = buf[byte_position];
- u8 data_byte = buf[byte_position + bytes_per_ctrl];
- bool dir = (dir_byte >> bit_position) & BIT(0);
- bool dat = (data_byte >> bit_position) & BIT(0);
-
- if (dir == 0) {
- /* input mode */
- if (dat) {
- /* actual buttons have pull up resistor */
- data->button_count++;
- set_bit(i, &data->button_mask);
- set_bit(i, &data->button_state_mask);
- }
- }
-
- }
-
- return 0;
-}
-
-static int rmi_populate(struct hid_device *hdev)
-{
- struct rmi_data *data = hid_get_drvdata(hdev);
- int ret;
-
- ret = rmi_scan_pdt(hdev);
- if (ret) {
- hid_err(hdev, "PDT scan failed with code %d.\n", ret);
- return ret;
- }
-
- ret = rmi_populate_f01(hdev);
- if (ret) {
- hid_err(hdev, "Error while initializing F01 (%d).\n", ret);
- return ret;
- }
-
- ret = rmi_populate_f11(hdev);
- if (ret) {
- hid_err(hdev, "Error while initializing F11 (%d).\n", ret);
- return ret;
- }
-
- if (!(data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS)) {
- ret = rmi_populate_f30(hdev);
- if (ret)
- hid_warn(hdev, "Error while initializing F30 (%d).\n", ret);
- }
-
- return 0;
+ return rmi_reset_attn_mode(hdev);
}
static int rmi_input_configured(struct hid_device *hdev, struct hid_input *hi)
{
struct rmi_data *data = hid_get_drvdata(hdev);
struct input_dev *input = hi->input;
- int ret;
- int res_x, res_y, i;
+ int ret = 0;
- data->input = input;
+ if (!(data->device_flags & RMI_DEVICE))
+ return 0;
+
+ data->xport.input = input;
hid_dbg(hdev, "Opening low level driver\n");
ret = hid_hw_open(hdev);
if (ret)
return ret;
- if (!(data->device_flags & RMI_DEVICE))
- return 0;
-
/* Allow incoming hid reports */
hid_device_io_start(hdev);
@@ -1222,40 +489,10 @@
goto exit;
}
- ret = rmi_populate(hdev);
- if (ret)
+ ret = rmi_register_transport_device(&data->xport);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "failed to register transport driver\n");
goto exit;
-
- hid_info(hdev, "firmware id: %ld\n", data->firmware_id);
-
- __set_bit(EV_ABS, input->evbit);
- input_set_abs_params(input, ABS_MT_POSITION_X, 1, data->max_x, 0, 0);
- input_set_abs_params(input, ABS_MT_POSITION_Y, 1, data->max_y, 0, 0);
-
- if (data->x_size_mm && data->y_size_mm) {
- res_x = (data->max_x - 1) / data->x_size_mm;
- res_y = (data->max_y - 1) / data->y_size_mm;
-
- input_abs_set_res(input, ABS_MT_POSITION_X, res_x);
- input_abs_set_res(input, ABS_MT_POSITION_Y, res_y);
- }
-
- input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0);
- input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xff, 0, 0);
- input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0);
- input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0);
-
- ret = input_mt_init_slots(input, data->max_fingers, INPUT_MT_POINTER);
- if (ret < 0)
- goto exit;
-
- if (data->button_count) {
- __set_bit(EV_KEY, input->evbit);
- for (i = 0; i < data->button_count; i++)
- __set_bit(BTN_LEFT + i, input->keybit);
-
- if (data->button_count == 1)
- __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
}
set_bit(RMI_STARTED, &data->flags);
@@ -1304,6 +541,71 @@
return 0;
}
+static struct rmi_device_platform_data rmi_hid_pdata = {
+ .sensor_pdata = {
+ .sensor_type = rmi_sensor_touchpad,
+ .axis_align.flip_y = true,
+ .dribble = RMI_REG_STATE_ON,
+ .palm_detect = RMI_REG_STATE_OFF,
+ },
+};
+
+static const struct rmi_transport_ops hid_rmi_ops = {
+ .write_block = rmi_hid_write_block,
+ .read_block = rmi_hid_read_block,
+ .reset = rmi_hid_reset,
+};
+
+static void rmi_irq_teardown(void *data)
+{
+ struct rmi_data *hdata = data;
+ struct irq_domain *domain = hdata->domain;
+
+ if (!domain)
+ return;
+
+ irq_dispose_mapping(irq_find_mapping(domain, 0));
+
+ irq_domain_remove(domain);
+ hdata->domain = NULL;
+ hdata->rmi_irq = 0;
+}
+
+static int rmi_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw_irq_num)
+{
+ irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops rmi_irq_ops = {
+ .map = rmi_irq_map,
+};
+
+static int rmi_setup_irq_domain(struct hid_device *hdev)
+{
+ struct rmi_data *hdata = hid_get_drvdata(hdev);
+ int ret;
+
+ hdata->domain = irq_domain_create_linear(hdev->dev.fwnode, 1,
+ &rmi_irq_ops, hdata);
+ if (!hdata->domain)
+ return -ENOMEM;
+
+ ret = devm_add_action_or_reset(&hdev->dev, &rmi_irq_teardown, hdata);
+ if (ret)
+ return ret;
+
+ hdata->rmi_irq = irq_create_mapping(hdata->domain, 0);
+ if (hdata->rmi_irq <= 0) {
+ hid_err(hdev, "Can't allocate an IRQ\n");
+ return hdata->rmi_irq < 0 ? hdata->rmi_irq : -ENXIO;
+ }
+
+ return 0;
+}
+
static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct rmi_data *data = NULL;
@@ -1365,8 +667,8 @@
data->writeReport = devm_kzalloc(&hdev->dev, alloc_size, GFP_KERNEL);
if (!data->writeReport) {
- ret = -ENOMEM;
- return ret;
+ hid_err(hdev, "failed to allocate buffer for HID reports\n");
+ return -ENOMEM;
}
data->readReport = data->writeReport + data->output_report_size;
@@ -1375,6 +677,21 @@
mutex_init(&data->page_mutex);
+ ret = rmi_setup_irq_domain(hdev);
+ if (ret) {
+ hid_err(hdev, "failed to allocate IRQ domain\n");
+ return ret;
+ }
+
+ if (data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS)
+ rmi_hid_pdata.f30_data.disable = true;
+
+ data->xport.dev = hdev->dev.parent;
+ data->xport.pdata = rmi_hid_pdata;
+ data->xport.pdata.irq = data->rmi_irq;
+ data->xport.proto_name = "hid";
+ data->xport.ops = &hid_rmi_ops;
+
start:
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
@@ -1382,17 +699,6 @@
return ret;
}
- if ((data->device_flags & RMI_DEVICE) &&
- !test_bit(RMI_STARTED, &data->flags))
- /*
- * The device maybe in the bootloader if rmi_input_configured
- * failed to find F11 in the PDT. Print an error, but don't
- * return an error from rmi_probe so that hidraw will be
- * accessible from userspace. That way a userspace tool
- * can be used to reload working firmware on the touchpad.
- */
- hid_err(hdev, "Device failed to be properly configured\n");
-
return 0;
}
@@ -1401,6 +707,8 @@
struct rmi_data *hdata = hid_get_drvdata(hdev);
clear_bit(RMI_STARTED, &hdata->flags);
+ cancel_work_sync(&hdata->reset_work);
+ rmi_unregister_transport_device(&hdata->xport);
hid_hw_stop(hdev);
}
@@ -1408,6 +716,7 @@
static const struct hid_device_id rmi_id[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLADE_14),
.driver_data = RMI_DEVICE_HAS_PHYS_BUTTONS },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_COVER) },
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_RMI, HID_ANY_ID, HID_ANY_ID) },
{ }
};
@@ -1425,7 +734,7 @@
#ifdef CONFIG_PM
.suspend = rmi_suspend,
.resume = rmi_post_resume,
- .reset_resume = rmi_post_reset,
+ .reset_resume = rmi_post_resume,
#endif
};
diff --git a/drivers/hid/intel-ish-hid/ipc/hw-ish-regs.h b/drivers/hid/intel-ish-hid/ipc/hw-ish-regs.h
index ab68afc..a5897b9 100644
--- a/drivers/hid/intel-ish-hid/ipc/hw-ish-regs.h
+++ b/drivers/hid/intel-ish-hid/ipc/hw-ish-regs.h
@@ -111,6 +111,14 @@
#define IPC_ILUP_BIT (1<<IPC_ILUP_OFFS)
/*
+ * ISH FW status bits in ISH FW Status Register
+ */
+#define IPC_ISH_FWSTS_SHIFT 12
+#define IPC_ISH_FWSTS_MASK GENMASK(15, 12)
+#define IPC_GET_ISH_FWSTS(status) \
+ (((status) & IPC_ISH_FWSTS_MASK) >> IPC_ISH_FWSTS_SHIFT)
+
+/*
* FW status bits (relevant)
*/
#define IPC_FWSTS_ILUP 0x1
diff --git a/drivers/hid/intel-ish-hid/ipc/hw-ish.h b/drivers/hid/intel-ish-hid/ipc/hw-ish.h
index 46615a0..fd34307 100644
--- a/drivers/hid/intel-ish-hid/ipc/hw-ish.h
+++ b/drivers/hid/intel-ish-hid/ipc/hw-ish.h
@@ -61,6 +61,18 @@
void __iomem *mem_addr;
};
+/*
+ * ISH FW status type
+ */
+enum {
+ FWSTS_AFTER_RESET = 0,
+ FWSTS_WAIT_FOR_HOST = 4,
+ FWSTS_START_KERNEL_DMA = 5,
+ FWSTS_FW_IS_RUNNING = 7,
+ FWSTS_SENSOR_APP_LOADED = 8,
+ FWSTS_SENSOR_APP_RUNNING = 15
+};
+
#define to_ish_hw(dev) (struct ish_hw *)((dev)->hw)
irqreturn_t ish_irq_handler(int irq, void *dev_id);
diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c
index 20d647d..8df81dc 100644
--- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c
+++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c
@@ -24,7 +24,6 @@
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
-#include <linux/miscdevice.h>
#define CREATE_TRACE_POINTS
#include <trace/events/intel_ish.h>
#include "ishtp-dev.h"
@@ -47,7 +46,8 @@
*
* Callback to direct log messages to Linux trace buffers
*/
-static void ish_event_tracer(struct ishtp_device *dev, char *format, ...)
+static __printf(2, 3)
+void ish_event_tracer(struct ishtp_device *dev, const char *format, ...)
{
if (trace_ishtp_dump_enabled()) {
va_list args;
@@ -205,12 +205,15 @@
#ifdef CONFIG_PM
static struct device *ish_resume_device;
+/* 50ms to get resume response */
+#define WAIT_FOR_RESUME_ACK_MS 50
+
/**
* ish_resume_handler() - Work function to complete resume
* @work: work struct
*
* The resume work function to complete resume function asynchronously.
- * There are two types of platforms, one where ISH is not powered off,
+ * There are two resume paths, one where ISH is not powered off,
* in that case a simple resume message is enough, others we need
* a reset sequence.
*/
@@ -218,20 +221,31 @@
{
struct pci_dev *pdev = to_pci_dev(ish_resume_device);
struct ishtp_device *dev = pci_get_drvdata(pdev);
+ uint32_t fwsts;
int ret;
- ishtp_send_resume(dev);
-
- /* 50 ms to get resume response */
- if (dev->resume_flag)
- ret = wait_event_interruptible_timeout(dev->resume_wait,
- !dev->resume_flag,
- msecs_to_jiffies(50));
+ /* Get ISH FW status */
+ fwsts = IPC_GET_ISH_FWSTS(dev->ops->get_fw_status(dev));
/*
- * If no resume response. This platform is not S0ix compatible
- * So on resume full reboot of ISH processor will happen, so
- * need to go through init sequence again
+ * If currently, in ISH FW, sensor app is loaded or beyond that,
+ * it means ISH isn't powered off, in this case, send a resume message.
+ */
+ if (fwsts >= FWSTS_SENSOR_APP_LOADED) {
+ ishtp_send_resume(dev);
+
+ /* Waiting to get resume response */
+ if (dev->resume_flag)
+ ret = wait_event_interruptible_timeout(dev->resume_wait,
+ !dev->resume_flag,
+ msecs_to_jiffies(WAIT_FOR_RESUME_ACK_MS));
+ }
+
+ /*
+ * If in ISH FW, sensor app isn't loaded yet, or no resume response.
+ * That means this platform is not S0ix compatible, or something is
+ * wrong with ISH FW. So on resume, full reboot of ISH processor will
+ * happen, so need to go through init sequence again.
*/
if (dev->resume_flag)
ish_init(dev);
diff --git a/drivers/hid/intel-ish-hid/ishtp-hid.c b/drivers/hid/intel-ish-hid/ishtp-hid.c
index 277983a..cd23903 100644
--- a/drivers/hid/intel-ish-hid/ishtp-hid.c
+++ b/drivers/hid/intel-ish-hid/ishtp-hid.c
@@ -208,7 +208,7 @@
hid->version = le16_to_cpu(ISH_HID_VERSION);
hid->vendor = le16_to_cpu(ISH_HID_VENDOR);
hid->product = le16_to_cpu(ISH_HID_PRODUCT);
- snprintf(hid->name, sizeof(hid->name), "%s %04hX:%04hX", "hid-ishtp",
+ snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "hid-ishtp",
hid->vendor, hid->product);
rv = hid_add_device(hid);
diff --git a/drivers/hid/intel-ish-hid/ishtp/bus.c b/drivers/hid/intel-ish-hid/ishtp/bus.c
index f4cbc74..5f382fe 100644
--- a/drivers/hid/intel-ish-hid/ishtp/bus.c
+++ b/drivers/hid/intel-ish-hid/ishtp/bus.c
@@ -358,7 +358,7 @@
kfree(to_ishtp_cl_device(dev));
}
-static struct device_type ishtp_cl_device_type = {
+static const struct device_type ishtp_cl_device_type = {
.release = ishtp_cl_dev_release,
};
diff --git a/drivers/hid/intel-ish-hid/ishtp/hbm.c b/drivers/hid/intel-ish-hid/ishtp/hbm.c
index 59460b6..b721360 100644
--- a/drivers/hid/intel-ish-hid/ishtp/hbm.c
+++ b/drivers/hid/intel-ish-hid/ishtp/hbm.c
@@ -19,7 +19,6 @@
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/spinlock.h>
-#include <linux/miscdevice.h>
#include "ishtp-dev.h"
#include "hbm.h"
#include "client.h"
diff --git a/drivers/hid/intel-ish-hid/ishtp/init.c b/drivers/hid/intel-ish-hid/ishtp/init.c
index ac36441..d27e035 100644
--- a/drivers/hid/intel-ish-hid/ishtp/init.c
+++ b/drivers/hid/intel-ish-hid/ishtp/init.c
@@ -16,7 +16,6 @@
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/sched.h>
-#include <linux/miscdevice.h>
#include "ishtp-dev.h"
#include "hbm.h"
#include "client.h"
diff --git a/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h
index a94f9a8..6a6d927 100644
--- a/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h
+++ b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h
@@ -238,7 +238,8 @@
uint64_t ishtp_host_dma_rx_buf_phys;
/* Dump to trace buffers if enabled*/
- void (*print_log)(struct ishtp_device *dev, char *format, ...);
+ __printf(2, 3) void (*print_log)(struct ishtp_device *dev,
+ const char *format, ...);
/* Debug stats */
unsigned int ipc_rx_cnt;
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 333108e..961bc6f 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -43,7 +43,6 @@
*/
#define DRIVER_DESC "USB HID core driver"
-#define DRIVER_LICENSE "GPL"
/*
* Module parameters.
@@ -1660,4 +1659,4 @@
MODULE_AUTHOR("Vojtech Pavlik");
MODULE_AUTHOR("Jiri Kosina");
MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE(DRIVER_LICENSE);
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 30a2977..d6847a6 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -85,7 +85,7 @@
{ USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR, HID_QUIRK_MULTI_INPUT },
- { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE, HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE1, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_ELAN, HID_ANY_ID, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
@@ -103,12 +103,6 @@
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_PRO_2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2, HID_QUIRK_NO_INIT_REPORTS },
- { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3, HID_QUIRK_NO_INIT_REPORTS },
- { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2, HID_QUIRK_NO_INIT_REPORTS },
- { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP, HID_QUIRK_NO_INIT_REPORTS },
- { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4, HID_QUIRK_NO_INIT_REPORTS },
- { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2, HID_QUIRK_NO_INIT_REPORTS },
- { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
@@ -297,7 +291,7 @@
}
-/**
+/**
* usbhid_quirks_init: apply USB HID quirks specified at module load time
*/
int usbhid_quirks_init(char **quirks_param)
@@ -361,7 +355,7 @@
if (bl_entry != NULL)
dbg_hid("Found squirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n",
- bl_entry->quirks, bl_entry->idVendor,
+ bl_entry->quirks, bl_entry->idVendor,
bl_entry->idProduct);
return bl_entry;
}
diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c
index 9a332e6..7fb2d1e 100644
--- a/drivers/hid/usbhid/usbkbd.c
+++ b/drivers/hid/usbhid/usbkbd.c
@@ -39,11 +39,10 @@
#define DRIVER_VERSION ""
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
#define DRIVER_DESC "USB HID Boot Protocol keyboard driver"
-#define DRIVER_LICENSE "GPL"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE(DRIVER_LICENSE);
+MODULE_LICENSE("GPL");
static const unsigned char usb_kbd_keycode[256] = {
0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
diff --git a/drivers/hid/usbhid/usbmouse.c b/drivers/hid/usbhid/usbmouse.c
index bf16d72..dd911c5 100644
--- a/drivers/hid/usbhid/usbmouse.c
+++ b/drivers/hid/usbhid/usbmouse.c
@@ -42,11 +42,10 @@
#define DRIVER_VERSION "v1.6"
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
#define DRIVER_DESC "USB HID Boot Protocol mouse driver"
-#define DRIVER_LICENSE "GPL"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE(DRIVER_LICENSE);
+MODULE_LICENSE("GPL");
struct usb_mouse {
char name[128];
diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h
index d303e41..38ee212 100644
--- a/drivers/hid/wacom.h
+++ b/drivers/hid/wacom.h
@@ -102,7 +102,6 @@
#define DRIVER_VERSION "v2.00"
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
#define DRIVER_DESC "USB Wacom tablet driver"
-#define DRIVER_LICENSE "GPL"
#define USB_VENDOR_ID_WACOM 0x056a
#define USB_VENDOR_ID_LENOVO 0x17ef
@@ -166,7 +165,9 @@
struct work_struct wireless_work;
struct work_struct battery_work;
struct work_struct remote_work;
+ struct delayed_work init_work;
struct wacom_remote *remote;
+ bool generic_has_leds;
struct wacom_leds {
struct wacom_group_leds *groups;
unsigned int count;
@@ -218,4 +219,6 @@
struct wacom_led *wacom_led_find(struct wacom *wacom, unsigned int group,
unsigned int id);
struct wacom_led *wacom_led_next(struct wacom *wacom, struct wacom_led *cur);
+int wacom_equivalent_usage(int usage);
+int wacom_initialize_leds(struct wacom *wacom);
#endif
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 8aeca03..be8f7e2 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -16,15 +16,7 @@
#include <linux/input/mt.h>
#define WAC_MSG_RETRIES 5
-
-#define WAC_CMD_WL_LED_CONTROL 0x03
-#define WAC_CMD_LED_CONTROL 0x20
-#define WAC_CMD_ICON_START 0x21
-#define WAC_CMD_ICON_XFER 0x23
-#define WAC_CMD_ICON_BT_XFER 0x26
#define WAC_CMD_RETRIES 10
-#define WAC_CMD_DELETE_PAIRING 0x20
-#define WAC_CMD_UNPAIR_ALL 0xFF
#define DEV_ATTR_RW_PERM (S_IRUGO | S_IWUSR | S_IWGRP)
#define DEV_ATTR_WO_PERM (S_IWUSR | S_IWGRP)
@@ -120,11 +112,12 @@
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_features *features = &wacom->wacom_wac.features;
struct hid_data *hid_data = &wacom->wacom_wac.hid_data;
+ unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid);
u8 *data;
int ret;
int n;
- switch (usage->hid) {
+ switch (equivalent_usage) {
case HID_DG_CONTACTMAX:
/* leave touch_max as is if predefined */
if (!features->touch_max) {
@@ -333,8 +326,14 @@
if (features->type == HID_GENERIC) {
/* Any last-minute generic device setup */
if (features->touch_max > 1) {
- input_mt_init_slots(wacom_wac->touch_input, wacom_wac->features.touch_max,
- INPUT_MT_DIRECT);
+ if (features->device_type & WACOM_DEVICETYPE_DIRECT)
+ input_mt_init_slots(wacom_wac->touch_input,
+ wacom_wac->features.touch_max,
+ INPUT_MT_DIRECT);
+ else
+ input_mt_init_slots(wacom_wac->touch_input,
+ wacom_wac->features.touch_max,
+ INPUT_MT_POINTER);
}
}
}
@@ -497,11 +496,11 @@
* from the tablet, it is necessary to switch the tablet out of this
* mode and into one which sends the full range of tablet data.
*/
-static int wacom_query_tablet_data(struct hid_device *hdev,
- struct wacom_features *features)
+static int _wacom_query_tablet_data(struct wacom *wacom)
{
- struct wacom *wacom = hid_get_drvdata(hdev);
+ struct hid_device *hdev = wacom->hdev;
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+ struct wacom_features *features = &wacom_wac->features;
if (hdev->bus == BUS_BLUETOOTH)
return wacom_bt_query_tablet_data(hdev, 1, features);
@@ -757,9 +756,6 @@
unsigned char report_id = WAC_CMD_LED_CONTROL;
int buf_size = 9;
- if (!hid_get_drvdata(wacom->hdev))
- return -ENODEV;
-
if (!wacom->led.groups)
return -ENOTSUPP;
@@ -767,12 +763,21 @@
report_id = WAC_CMD_WL_LED_CONTROL;
buf_size = 13;
}
+ else if (wacom->wacom_wac.features.type == INTUOSP2_BT) {
+ report_id = WAC_CMD_WL_INTUOSP2;
+ buf_size = 51;
+ }
buf = kzalloc(buf_size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
- if (wacom->wacom_wac.features.type >= INTUOS5S &&
- wacom->wacom_wac.features.type <= INTUOSPL) {
+ if (wacom->wacom_wac.features.type == HID_GENERIC) {
+ buf[0] = WAC_CMD_LED_CONTROL_GENERIC;
+ buf[1] = wacom->led.llv;
+ buf[2] = wacom->led.groups[0].select & 0x03;
+
+ } else if ((wacom->wacom_wac.features.type >= INTUOS5S &&
+ wacom->wacom_wac.features.type <= INTUOSPL)) {
/*
* Touch Ring and crop mark LED luminance may take on
* one of four values:
@@ -792,6 +797,16 @@
} else
buf[1] = led_bits;
}
+ else if (wacom->wacom_wac.features.type == INTUOSP2_BT) {
+ buf[0] = report_id;
+ buf[4] = 100; // Power Connection LED (ORANGE)
+ buf[5] = 100; // BT Connection LED (BLUE)
+ buf[6] = 100; // Paper Mode (RED?)
+ buf[7] = 100; // Paper Mode (GREEN?)
+ buf[8] = 100; // Paper Mode (BLUE?)
+ buf[9] = wacom->led.llv;
+ buf[10] = wacom->led.groups[0].select & 0x03;
+ }
else {
int led = wacom->led.groups[0].select | 0x4;
@@ -1032,6 +1047,17 @@
.attrs = intuos5_led_attrs,
};
+static struct attribute *generic_led_attrs[] = {
+ &dev_attr_status0_luminance.attr,
+ &dev_attr_status_led0_select.attr,
+ NULL
+};
+
+static struct attribute_group generic_led_attr_group = {
+ .name = "wacom_led",
+ .attrs = generic_led_attrs,
+};
+
struct wacom_sysfs_group_devres {
struct attribute_group *group;
struct kobject *root;
@@ -1353,7 +1379,7 @@
return 0;
}
-static int wacom_initialize_leds(struct wacom *wacom)
+int wacom_initialize_leds(struct wacom *wacom)
{
int error;
@@ -1362,6 +1388,23 @@
/* Initialize default values */
switch (wacom->wacom_wac.features.type) {
+ case HID_GENERIC:
+ if (!wacom->generic_has_leds)
+ return 0;
+ wacom->led.llv = 100;
+ wacom->led.max_llv = 100;
+
+ error = wacom_leds_alloc_and_register(wacom, 1, 4, false);
+ if (error) {
+ hid_err(wacom->hdev,
+ "cannot create leds err: %d\n", error);
+ return error;
+ }
+
+ error = wacom_devm_sysfs_create_group(wacom,
+ &generic_led_attr_group);
+ break;
+
case INTUOS4S:
case INTUOS4:
case INTUOS4WL:
@@ -1420,6 +1463,17 @@
&intuos5_led_attr_group);
break;
+ case INTUOSP2_BT:
+ wacom->led.llv = 50;
+ wacom->led.max_llv = 100;
+ error = wacom_leds_alloc_and_register(wacom, 1, 4, false);
+ if (error) {
+ hid_err(wacom->hdev,
+ "cannot create leds err: %d\n", error);
+ return error;
+ }
+ return 0;
+
case REMOTE:
wacom->led.llv = 255;
wacom->led.max_llv = 255;
@@ -1440,11 +1494,23 @@
"cannot create sysfs group err: %d\n", error);
return error;
}
- wacom_led_control(wacom);
return 0;
}
+static void wacom_init_work(struct work_struct *work)
+{
+ struct wacom *wacom = container_of(work, struct wacom, init_work.work);
+
+ _wacom_query_tablet_data(wacom);
+ wacom_led_control(wacom);
+}
+
+static void wacom_query_tablet_data(struct wacom *wacom)
+{
+ schedule_delayed_work(&wacom->init_work, msecs_to_jiffies(1000));
+}
+
static enum power_supply_property wacom_battery_props[] = {
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_PRESENT,
@@ -2020,6 +2086,24 @@
wacom->wacom_wac.pad_input = NULL;
}
+static void wacom_set_shared_values(struct wacom_wac *wacom_wac)
+{
+ if (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH) {
+ wacom_wac->shared->type = wacom_wac->features.type;
+ wacom_wac->shared->touch_input = wacom_wac->touch_input;
+ }
+
+ if (wacom_wac->has_mute_touch_switch)
+ wacom_wac->shared->has_mute_touch_switch = true;
+
+ if (wacom_wac->shared->has_mute_touch_switch &&
+ wacom_wac->shared->touch_input) {
+ set_bit(EV_SW, wacom_wac->shared->touch_input->evbit);
+ input_set_capability(wacom_wac->shared->touch_input, EV_SW,
+ SW_MUTE_DEVICE);
+ }
+}
+
static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
{
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
@@ -2118,7 +2202,7 @@
if (!wireless) {
/* Note that if query fails it is not a hard failure */
- wacom_query_tablet_data(hdev, features);
+ wacom_query_tablet_data(wacom);
}
/* touch only Bamboo doesn't support pen */
@@ -2139,13 +2223,7 @@
if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR)
error = hid_hw_open(hdev);
- if ((wacom_wac->features.type == INTUOSHT ||
- wacom_wac->features.type == INTUOSHT2) &&
- (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH)) {
- wacom_wac->shared->type = wacom_wac->features.type;
- wacom_wac->shared->touch_input = wacom_wac->touch_input;
- }
-
+ wacom_set_shared_values(wacom_wac);
devres_close_group(&hdev->dev, wacom);
return 0;
@@ -2450,6 +2528,7 @@
wacom->usbdev = dev;
wacom->intf = intf;
mutex_init(&wacom->lock);
+ INIT_DELAYED_WORK(&wacom->init_work, wacom_init_work);
INIT_WORK(&wacom->wireless_work, wacom_wireless_work);
INIT_WORK(&wacom->battery_work, wacom_battery_work);
INIT_WORK(&wacom->remote_work, wacom_remote_work);
@@ -2491,12 +2570,17 @@
hid_hw_stop(hdev);
+ cancel_delayed_work_sync(&wacom->init_work);
cancel_work_sync(&wacom->wireless_work);
cancel_work_sync(&wacom->battery_work);
cancel_work_sync(&wacom->remote_work);
if (hdev->bus == BUS_BLUETOOTH)
device_remove_file(&hdev->dev, &dev_attr_speed);
+ /* make sure we don't trigger the LEDs */
+ wacom_led_groups_release(wacom);
+ wacom_release_resources(wacom);
+
hid_set_drvdata(hdev, NULL);
}
@@ -2504,12 +2588,11 @@
static int wacom_resume(struct hid_device *hdev)
{
struct wacom *wacom = hid_get_drvdata(hdev);
- struct wacom_features *features = &wacom->wacom_wac.features;
mutex_lock(&wacom->lock);
/* switch to wacom mode first */
- wacom_query_tablet_data(hdev, features);
+ _wacom_query_tablet_data(wacom);
wacom_led_control(wacom);
mutex_unlock(&wacom->lock);
@@ -2540,4 +2623,4 @@
MODULE_VERSION(DRIVER_VERSION);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE(DRIVER_LICENSE);
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 672145b..4aa3de9 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -43,6 +43,8 @@
static int wacom_numbered_button_to_key(int n);
+static void wacom_update_led(struct wacom *wacom, int button_count, int mask,
+ int group);
/*
* Percent of battery capacity for Graphire.
* 8th value means AC online and show 100% capacity.
@@ -1192,6 +1194,166 @@
return count;
}
+static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
+{
+ const int pen_frame_len = 14;
+ const int pen_frames = 7;
+
+ struct input_dev *pen_input = wacom->pen_input;
+ unsigned char *data = wacom->data;
+ int i;
+
+ wacom->serial[0] = get_unaligned_le64(&data[99]);
+ wacom->id[0] = get_unaligned_le16(&data[107]);
+ if (wacom->serial[0] >> 52 == 1) {
+ /* Add back in missing bits of ID for non-USI pens */
+ wacom->id[0] |= (wacom->serial[0] >> 32) & 0xFFFFF;
+ }
+ wacom->tool[0] = wacom_intuos_get_tool_type(wacom_intuos_id_mangle(wacom->id[0]));
+
+ for (i = 0; i < pen_frames; i++) {
+ unsigned char *frame = &data[i*pen_frame_len + 1];
+ bool valid = frame[0] & 0x80;
+ bool prox = frame[0] & 0x40;
+ bool range = frame[0] & 0x20;
+
+ if (!valid)
+ continue;
+
+ if (range) {
+ input_report_abs(pen_input, ABS_X, get_unaligned_le16(&frame[1]));
+ input_report_abs(pen_input, ABS_Y, get_unaligned_le16(&frame[3]));
+ input_report_abs(pen_input, ABS_TILT_X, frame[7]);
+ input_report_abs(pen_input, ABS_TILT_Y, frame[8]);
+ input_report_abs(pen_input, ABS_Z, get_unaligned_le16(&frame[9]));
+ input_report_abs(pen_input, ABS_WHEEL, get_unaligned_le16(&frame[11]));
+ }
+ input_report_abs(pen_input, ABS_PRESSURE, get_unaligned_le16(&frame[5]));
+ input_report_abs(pen_input, ABS_DISTANCE, range ? frame[13] : wacom->features.distance_max);
+
+ input_report_key(pen_input, BTN_TOUCH, frame[0] & 0x01);
+ input_report_key(pen_input, BTN_STYLUS, frame[0] & 0x02);
+ input_report_key(pen_input, BTN_STYLUS2, frame[0] & 0x04);
+
+ input_report_key(pen_input, wacom->tool[0], prox);
+ input_event(pen_input, EV_MSC, MSC_SERIAL, wacom->serial[0]);
+ input_report_abs(pen_input, ABS_MISC,
+ wacom_intuos_id_mangle(wacom->id[0])); /* report tool id */
+
+ wacom->shared->stylus_in_proximity = prox;
+
+ input_sync(pen_input);
+ }
+}
+
+static void wacom_intuos_pro2_bt_touch(struct wacom_wac *wacom)
+{
+ const int finger_touch_len = 8;
+ const int finger_frames = 4;
+ const int finger_frame_len = 43;
+
+ struct input_dev *touch_input = wacom->touch_input;
+ unsigned char *data = wacom->data;
+ int num_contacts_left = 5;
+ int i, j;
+
+ for (i = 0; i < finger_frames; i++) {
+ unsigned char *frame = &data[i*finger_frame_len + 109];
+ int current_num_contacts = frame[0] & 0x7F;
+ int contacts_to_send;
+
+ if (!(frame[0] & 0x80))
+ continue;
+
+ /*
+ * First packet resets the counter since only the first
+ * packet in series will have non-zero current_num_contacts.
+ */
+ if (current_num_contacts)
+ wacom->num_contacts_left = current_num_contacts;
+
+ contacts_to_send = min(num_contacts_left, wacom->num_contacts_left);
+
+ for (j = 0; j < contacts_to_send; j++) {
+ unsigned char *touch = &frame[j*finger_touch_len + 1];
+ int slot = input_mt_get_slot_by_key(touch_input, touch[0]);
+ int x = get_unaligned_le16(&touch[2]);
+ int y = get_unaligned_le16(&touch[4]);
+ int w = touch[6] * input_abs_get_res(touch_input, ABS_MT_POSITION_X);
+ int h = touch[7] * input_abs_get_res(touch_input, ABS_MT_POSITION_Y);
+
+ if (slot < 0)
+ continue;
+
+ input_mt_slot(touch_input, slot);
+ input_mt_report_slot_state(touch_input, MT_TOOL_FINGER, touch[1] & 0x01);
+ input_report_abs(touch_input, ABS_MT_POSITION_X, x);
+ input_report_abs(touch_input, ABS_MT_POSITION_Y, y);
+ input_report_abs(touch_input, ABS_MT_TOUCH_MAJOR, max(w, h));
+ input_report_abs(touch_input, ABS_MT_TOUCH_MINOR, min(w, h));
+ input_report_abs(touch_input, ABS_MT_ORIENTATION, w > h);
+ }
+
+ input_mt_sync_frame(touch_input);
+
+ wacom->num_contacts_left -= contacts_to_send;
+ if (wacom->num_contacts_left <= 0) {
+ wacom->num_contacts_left = 0;
+ wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom);
+ }
+ }
+
+ input_report_switch(touch_input, SW_MUTE_DEVICE, !(data[281] >> 7));
+ input_sync(touch_input);
+}
+
+static void wacom_intuos_pro2_bt_pad(struct wacom_wac *wacom)
+{
+ struct input_dev *pad_input = wacom->pad_input;
+ unsigned char *data = wacom->data;
+
+ int buttons = (data[282] << 1) | ((data[281] >> 6) & 0x01);
+ int ring = data[285];
+ int prox = buttons | (ring & 0x80);
+
+ wacom_report_numbered_buttons(pad_input, 9, buttons);
+
+ input_report_abs(pad_input, ABS_WHEEL, (ring & 0x80) ? (ring & 0x7f) : 0);
+
+ input_report_key(pad_input, wacom->tool[1], prox ? 1 : 0);
+ input_report_abs(pad_input, ABS_MISC, prox ? PAD_DEVICE_ID : 0);
+ input_event(pad_input, EV_MSC, MSC_SERIAL, 0xffffffff);
+
+ input_sync(pad_input);
+}
+
+static void wacom_intuos_pro2_bt_battery(struct wacom_wac *wacom)
+{
+ unsigned char *data = wacom->data;
+
+ bool chg = data[284] & 0x80;
+ int battery_status = data[284] & 0x7F;
+
+ wacom_notify_battery(wacom, battery_status, chg, 1, chg);
+}
+
+static int wacom_intuos_pro2_bt_irq(struct wacom_wac *wacom, size_t len)
+{
+ unsigned char *data = wacom->data;
+
+ if (data[0] != 0x80) {
+ dev_dbg(wacom->pen_input->dev.parent,
+ "%s: received unknown report #%d\n", __func__, data[0]);
+ return 0;
+ }
+
+ wacom_intuos_pro2_bt_pen(wacom);
+ wacom_intuos_pro2_bt_touch(wacom);
+ wacom_intuos_pro2_bt_pad(wacom);
+ wacom_intuos_pro2_bt_battery(wacom);
+ return 0;
+}
+
static int wacom_24hdt_irq(struct wacom_wac *wacom)
{
struct input_dev *input = wacom->touch_input;
@@ -1446,7 +1608,7 @@
return 0;
}
-static int wacom_equivalent_usage(int usage)
+int wacom_equivalent_usage(int usage)
{
if ((usage & HID_USAGE_PAGE) == WACOM_HID_UP_WACOMDIGITIZER) {
int subpage = (usage & 0xFF00) << 8;
@@ -1473,6 +1635,16 @@
return subpage | subusage;
}
+ if ((usage & HID_USAGE_PAGE) == WACOM_HID_UP_WACOMTOUCH) {
+ int subpage = (usage & 0xFF00) << 8;
+ int subusage = (usage & 0xFF);
+
+ if (subpage == HID_UP_UNDEFINED)
+ subpage = WACOM_HID_SP_DIGITIZER;
+
+ return subpage | subusage;
+ }
+
return usage;
}
@@ -1552,12 +1724,14 @@
wacom_map_usage(input, usage, field, EV_ABS, ABS_Z, 0);
features->device_type |= WACOM_DEVICETYPE_PAD;
break;
+ case WACOM_HID_WD_BUTTONCENTER:
+ wacom->generic_has_leds = true;
+ /* fall through */
case WACOM_HID_WD_BUTTONHOME:
case WACOM_HID_WD_BUTTONUP:
case WACOM_HID_WD_BUTTONDOWN:
case WACOM_HID_WD_BUTTONLEFT:
case WACOM_HID_WD_BUTTONRIGHT:
- case WACOM_HID_WD_BUTTONCENTER:
wacom_map_usage(input, usage, field, EV_KEY,
wacom_numbered_button_to_key(features->numbered_buttons),
0);
@@ -1565,7 +1739,17 @@
features->device_type |= WACOM_DEVICETYPE_PAD;
break;
case WACOM_HID_WD_TOUCHONOFF:
- wacom_map_usage(input, usage, field, EV_SW, SW_MUTE_DEVICE, 0);
+ /*
+ * This usage, which is used to mute touch events, comes
+ * from the pad packet, but is reported on the touch
+ * interface. Because the touch interface may not have
+ * been created yet, we cannot call wacom_map_usage(). In
+ * order to process this usage when we receive it, we set
+ * the usage type and code directly.
+ */
+ wacom_wac->has_mute_touch_switch = true;
+ usage->type = EV_SW;
+ usage->code = SW_MUTE_DEVICE;
features->device_type |= WACOM_DEVICETYPE_PAD;
break;
case WACOM_HID_WD_TOUCHSTRIP:
@@ -1580,6 +1764,10 @@
wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0);
features->device_type |= WACOM_DEVICETYPE_PAD;
break;
+ case WACOM_HID_WD_TOUCHRINGSTATUS:
+ wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0);
+ features->device_type |= WACOM_DEVICETYPE_PAD;
+ break;
}
switch (equivalent_usage & 0xfffffff0) {
@@ -1622,17 +1810,40 @@
struct input_dev *input = wacom_wac->pad_input;
struct wacom_features *features = &wacom_wac->features;
unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
+ int i;
+
+ /*
+ * Avoid reporting this event and setting inrange_state if this usage
+ * hasn't been mapped.
+ */
+ if (!usage->type)
+ return;
if (wacom_equivalent_usage(field->physical) == HID_DG_TABLETFUNCTIONKEY) {
- wacom_wac->hid_data.inrange_state |= value;
+ if (usage->hid != WACOM_HID_WD_TOUCHRING)
+ wacom_wac->hid_data.inrange_state |= value;
}
switch (equivalent_usage) {
case WACOM_HID_WD_TOUCHRINGSTATUS:
+ if (!value)
+ input_event(input, usage->type, usage->code, 0);
break;
+ case WACOM_HID_WD_TOUCHONOFF:
+ if (wacom_wac->shared->touch_input) {
+ input_report_switch(wacom_wac->shared->touch_input,
+ SW_MUTE_DEVICE, !value);
+ input_sync(wacom_wac->shared->touch_input);
+ }
+ break;
+
+ case WACOM_HID_WD_BUTTONCENTER:
+ for (i = 0; i < wacom->led.count; i++)
+ wacom_update_led(wacom, features->numbered_buttons,
+ value, i);
+ /* fall through*/
default:
- features->input_event_flag = true;
input_event(input, usage->type, usage->code, value);
break;
}
@@ -1670,20 +1881,15 @@
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
- struct wacom_features *features = &wacom_wac->features;
struct input_dev *input = wacom_wac->pad_input;
bool active = wacom_wac->hid_data.inrange_state != 0;
/* report prox for expresskey events */
if (wacom_equivalent_usage(report->field[0]->physical) == HID_DG_TABLETFUNCTIONKEY) {
- features->input_event_flag = true;
input_event(input, EV_ABS, ABS_MISC, active ? PAD_DEVICE_ID : 0);
- }
-
- if (features->input_event_flag) {
- features->input_event_flag = false;
input_sync(input);
}
+
}
static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
@@ -2058,8 +2264,10 @@
for (j = 0; j < field->maxusage; j++) {
struct hid_usage *usage = &field->usage[j];
+ unsigned int equivalent_usage =
+ wacom_equivalent_usage(usage->hid);
- switch (usage->hid) {
+ switch (equivalent_usage) {
case HID_GD_X:
case HID_GD_Y:
case HID_DG_WIDTH:
@@ -2068,7 +2276,7 @@
case HID_DG_INRANGE:
case HID_DG_INVERT:
case HID_DG_TIPSWITCH:
- hid_data->last_slot_field = usage->hid;
+ hid_data->last_slot_field = equivalent_usage;
break;
case HID_DG_CONTACTCOUNT:
hid_data->cc_report = report->id;
@@ -2123,8 +2331,8 @@
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct wacom_features *features = &wacom_wac->features;
- /* currently, only direct devices have proper hid report descriptors */
- features->device_type |= WACOM_DEVICETYPE_DIRECT;
+ if (WACOM_DIRECT_DEVICE(field))
+ features->device_type |= WACOM_DEVICETYPE_DIRECT;
if (WACOM_PAD_FIELD(field))
wacom_wac_pad_usage_mapping(hdev, field, usage);
@@ -2142,6 +2350,9 @@
if (wacom->wacom_wac.features.type != HID_GENERIC)
return;
+ if (value > field->logical_maximum || value < field->logical_minimum)
+ return;
+
if (WACOM_PAD_FIELD(field)) {
wacom_wac_pad_battery_event(hdev, field, usage, value);
if (wacom->wacom_wac.pad_input)
@@ -2669,6 +2880,10 @@
sync = wacom_intuos_irq(wacom_wac);
break;
+ case INTUOSP2_BT:
+ sync = wacom_intuos_pro2_bt_irq(wacom_wac, len);
+ break;
+
case TABLETPC:
case TABLETPCE:
case TABLETPC2FG:
@@ -2779,8 +2994,6 @@
struct wacom_features *features = &wacom->wacom_wac.features;
/* The pen and pad share the same interface on most devices */
- if (features->numbered_buttons > 0)
- features->device_type |= WACOM_DEVICETYPE_PAD;
if (features->type == GRAPHIRE_BT || features->type == WACOM_G4 ||
features->type == DTUS ||
(features->type >= INTUOS3S && features->type <= WACOM_MO)) {
@@ -2840,6 +3053,13 @@
if (features->type == REMOTE)
features->device_type = WACOM_DEVICETYPE_PAD;
+ if (features->type == INTUOSP2_BT) {
+ features->device_type |= WACOM_DEVICETYPE_PEN |
+ WACOM_DEVICETYPE_PAD |
+ WACOM_DEVICETYPE_TOUCH;
+ features->quirks |= WACOM_QUIRK_BATTERY;
+ }
+
switch (features->type) {
case PL:
case DTU:
@@ -2986,6 +3206,7 @@
case INTUOSPL:
case INTUOS5S:
case INTUOSPS:
+ case INTUOSP2_BT:
input_set_abs_params(input_dev, ABS_DISTANCE, 0,
features->distance_max,
features->distance_fuzz, 0);
@@ -3094,6 +3315,27 @@
}
switch (features->type) {
+ case INTUOSP2_BT:
+ input_dev->evbit[0] |= BIT_MASK(EV_SW);
+ __set_bit(SW_MUTE_DEVICE, input_dev->swbit);
+
+ if (wacom_wac->shared->touch->product == 0x361) {
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, 12440, 4, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, 8640, 4, 0);
+ }
+ else if (wacom_wac->shared->touch->product == 0x360) {
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, 8960, 4, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, 5920, 4, 0);
+ }
+ input_abs_set_res(input_dev, ABS_MT_POSITION_X, 40);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_X, 40);
+
+ /* fall through */
+
case INTUOS5:
case INTUOS5L:
case INTUOSPM:
@@ -3290,6 +3532,9 @@
{
struct wacom_features *features = &wacom_wac->features;
+ if ((features->type == HID_GENERIC) && features->numbered_buttons > 0)
+ features->device_type |= WACOM_DEVICETYPE_PAD;
+
if (!(features->device_type & WACOM_DEVICETYPE_PAD))
return -ENODEV;
@@ -3391,6 +3636,7 @@
case INTUOSPL:
case INTUOS5S:
case INTUOSPS:
+ case INTUOSP2_BT:
input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
break;
@@ -3949,6 +4195,12 @@
DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 4,
WACOM_DTU_OFFSET, WACOM_DTU_OFFSET,
WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
+static const struct wacom_features wacom_features_0x360 =
+ { "Wacom Intuos Pro M", 44800, 29600, 8191, 63,
+ INTUOSP2_BT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 9, .touch_max = 10 };
+static const struct wacom_features wacom_features_0x361 =
+ { "Wacom Intuos Pro L", 62200, 43200, 8191, 63,
+ INTUOSP2_BT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 9, .touch_max = 10 };
static const struct wacom_features wacom_features_HID_ANY_ID =
{ "Wacom HID", .type = HID_GENERIC, .oVid = HID_ANY_ID, .oPid = HID_ANY_ID };
@@ -4115,6 +4367,8 @@
{ USB_DEVICE_WACOM(0x33D) },
{ USB_DEVICE_WACOM(0x33E) },
{ USB_DEVICE_WACOM(0x343) },
+ { BT_DEVICE_WACOM(0x360) },
+ { BT_DEVICE_WACOM(0x361) },
{ USB_DEVICE_WACOM(0x4001) },
{ USB_DEVICE_WACOM(0x4004) },
{ USB_DEVICE_WACOM(0x5000) },
@@ -4123,6 +4377,7 @@
{ USB_DEVICE_WACOM(HID_ANY_ID) },
{ I2C_DEVICE_WACOM(HID_ANY_ID) },
+ { BT_DEVICE_WACOM(HID_ANY_ID) },
{ }
};
MODULE_DEVICE_TABLE(hid, wacom_ids);
diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h
index fb0e50a..857ccee 100644
--- a/drivers/hid/wacom_wac.h
+++ b/drivers/hid/wacom_wac.h
@@ -12,8 +12,8 @@
#include <linux/types.h>
#include <linux/hid.h>
-/* maximum packet length for USB devices */
-#define WACOM_PKGLEN_MAX 192
+/* maximum packet length for USB/BT devices */
+#define WACOM_PKGLEN_MAX 361
#define WACOM_NAME_MAX 64
#define WACOM_MAX_REMOTES 5
@@ -72,6 +72,17 @@
#define WACOM_REPORT_REMOTE 17
#define WACOM_REPORT_INTUOSHT2_ID 8
+/* wacom command report ids */
+#define WAC_CMD_WL_LED_CONTROL 0x03
+#define WAC_CMD_LED_CONTROL 0x20
+#define WAC_CMD_ICON_START 0x21
+#define WAC_CMD_ICON_XFER 0x23
+#define WAC_CMD_ICON_BT_XFER 0x26
+#define WAC_CMD_DELETE_PAIRING 0x20
+#define WAC_CMD_LED_CONTROL_GENERIC 0x32
+#define WAC_CMD_UNPAIR_ALL 0xFF
+#define WAC_CMD_WL_INTUOSP2 0x82
+
/* device quirks */
#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0001
#define WACOM_QUIRK_SENSE 0x0002
@@ -91,6 +102,7 @@
#define WACOM_HID_SP_DIGITIZER 0x000d0000
#define WACOM_HID_SP_DIGITIZERINFO 0x00100000
#define WACOM_HID_WD_DIGITIZER (WACOM_HID_UP_WACOMDIGITIZER | 0x01)
+#define WACOM_HID_WD_PEN (WACOM_HID_UP_WACOMDIGITIZER | 0x02)
#define WACOM_HID_WD_SENSE (WACOM_HID_UP_WACOMDIGITIZER | 0x36)
#define WACOM_HID_WD_DIGITIZERFNKEYS (WACOM_HID_UP_WACOMDIGITIZER | 0x39)
#define WACOM_HID_WD_SERIALHI (WACOM_HID_UP_WACOMDIGITIZER | 0x5c)
@@ -104,6 +116,7 @@
#define WACOM_HID_WD_ACCELEROMETER_Y (WACOM_HID_UP_WACOMDIGITIZER | 0x0402)
#define WACOM_HID_WD_ACCELEROMETER_Z (WACOM_HID_UP_WACOMDIGITIZER | 0x0403)
#define WACOM_HID_WD_BATTERY_CHARGING (WACOM_HID_UP_WACOMDIGITIZER | 0x0404)
+#define WACOM_HID_WD_TOUCHONOFF (WACOM_HID_UP_WACOMDIGITIZER | 0x0454)
#define WACOM_HID_WD_BATTERY_LEVEL (WACOM_HID_UP_WACOMDIGITIZER | 0x043b)
#define WACOM_HID_WD_EXPRESSKEY00 (WACOM_HID_UP_WACOMDIGITIZER | 0x0910)
#define WACOM_HID_WD_EXPRESSKEYCAP00 (WACOM_HID_UP_WACOMDIGITIZER | 0x0950)
@@ -113,7 +126,6 @@
#define WACOM_HID_WD_BUTTONLEFT (WACOM_HID_UP_WACOMDIGITIZER | 0x0993)
#define WACOM_HID_WD_BUTTONRIGHT (WACOM_HID_UP_WACOMDIGITIZER | 0x0994)
#define WACOM_HID_WD_BUTTONCENTER (WACOM_HID_UP_WACOMDIGITIZER | 0x0995)
-#define WACOM_HID_WD_TOUCHONOFF (WACOM_HID_UP_WACOMDIGITIZER | 0x0996)
#define WACOM_HID_WD_FINGERWHEEL (WACOM_HID_UP_WACOMDIGITIZER | 0x0d03)
#define WACOM_HID_WD_OFFSETLEFT (WACOM_HID_UP_WACOMDIGITIZER | 0x0d30)
#define WACOM_HID_WD_OFFSETTOP (WACOM_HID_UP_WACOMDIGITIZER | 0x0d31)
@@ -127,6 +139,12 @@
#define WACOM_HID_UP_G11 0xff110000
#define WACOM_HID_G11_PEN (WACOM_HID_UP_G11 | 0x02)
#define WACOM_HID_G11_TOUCHSCREEN (WACOM_HID_UP_G11 | 0x11)
+#define WACOM_HID_UP_WACOMTOUCH 0xff000000
+#define WACOM_HID_WT_TOUCHSCREEN (WACOM_HID_UP_WACOMTOUCH | 0x04)
+#define WACOM_HID_WT_TOUCHPAD (WACOM_HID_UP_WACOMTOUCH | 0x05)
+#define WACOM_HID_WT_CONTACTMAX (WACOM_HID_UP_WACOMTOUCH | 0x55)
+#define WACOM_HID_WT_X (WACOM_HID_UP_WACOMTOUCH | 0x130)
+#define WACOM_HID_WT_Y (WACOM_HID_UP_WACOMTOUCH | 0x131)
#define WACOM_PAD_FIELD(f) (((f)->physical == HID_DG_TABLETFUNCTIONKEY) || \
((f)->physical == WACOM_HID_WD_DIGITIZERFNKEYS) || \
@@ -144,7 +162,14 @@
((f)->physical == HID_DG_FINGER) || \
((f)->application == HID_DG_TOUCHSCREEN) || \
((f)->application == WACOM_HID_G9_TOUCHSCREEN) || \
- ((f)->application == WACOM_HID_G11_TOUCHSCREEN))
+ ((f)->application == WACOM_HID_G11_TOUCHSCREEN) || \
+ ((f)->application == WACOM_HID_WT_TOUCHPAD) || \
+ ((f)->application == HID_DG_TOUCHPAD))
+
+#define WACOM_DIRECT_DEVICE(f) (((f)->application == HID_DG_TOUCHSCREEN) || \
+ ((f)->application == WACOM_HID_WT_TOUCHSCREEN) || \
+ ((f)->application == HID_DG_PEN) || \
+ ((f)->application == WACOM_HID_WD_PEN))
enum {
PENPARTNER = 0,
@@ -170,6 +195,7 @@
INTUOSPS,
INTUOSPM,
INTUOSPL,
+ INTUOSP2_BT,
WACOM_21UX2,
WACOM_22HD,
DTK,
@@ -232,7 +258,6 @@
int pktlen;
bool check_for_hid_type;
int hid_type;
- bool input_event_flag;
};
struct wacom_shared {
@@ -244,6 +269,7 @@
struct input_dev *touch_input;
struct hid_device *pen;
struct hid_device *touch;
+ bool has_mute_touch_switch;
};
struct hid_data {
@@ -300,6 +326,7 @@
int mode_report;
int mode_value;
struct hid_data hid_data;
+ bool has_mute_touch_switch;
};
#endif
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 190d270..0649d53f3 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1459,6 +1459,16 @@
This driver can also be built as a module. If so, the module
will be called sch5636.
+config SENSORS_STTS751
+ tristate "ST Microelectronics STTS751"
+ depends on I2C
+ help
+ If you say yes here you get support for STTS751
+ temperature sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called stts751.
+
config SENSORS_SMM665
tristate "Summit Microelectronics SMM665"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index d2cb7e8..5509edf 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -148,6 +148,7 @@
obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
+obj-$(CONFIG_SENSORS_STTS751) += stts751.o
obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o
obj-$(CONFIG_SENSORS_TC74) += tc74.o
obj-$(CONFIG_SENSORS_THMC50) += thmc50.o
diff --git a/drivers/hwmon/adc128d818.c b/drivers/hwmon/adc128d818.c
index ad2b47e..bbe3a5c 100644
--- a/drivers/hwmon/adc128d818.c
+++ b/drivers/hwmon/adc128d818.c
@@ -28,6 +28,7 @@
#include <linux/regulator/consumer.h>
#include <linux/mutex.h>
#include <linux/bitops.h>
+#include <linux/of.h>
/* Addresses to scan
* The chip also supports addresses 0x35..0x37. Don't scan those addresses
@@ -58,15 +59,22 @@
#define ADC128_REG_MAN_ID 0x3e
#define ADC128_REG_DEV_ID 0x3f
+/* No. of voltage entries in adc128_attrs */
+#define ADC128_ATTR_NUM_VOLT (8 * 4)
+
+/* Voltage inputs visible per operation mode */
+static const u8 num_inputs[] = { 7, 8, 4, 6 };
+
struct adc128_data {
struct i2c_client *client;
struct regulator *regulator;
int vref; /* Reference voltage in mV */
struct mutex update_lock;
+ u8 mode; /* Operation mode */
bool valid; /* true if following fields are valid */
unsigned long last_updated; /* In jiffies */
- u16 in[3][7]; /* Register value, normalized to 12 bit
+ u16 in[3][8]; /* Register value, normalized to 12 bit
* 0: input voltage
* 1: min limit
* 2: max limit
@@ -87,7 +95,7 @@
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
- for (i = 0; i < 7; i++) {
+ for (i = 0; i < num_inputs[data->mode]; i++) {
rv = i2c_smbus_read_word_swapped(client,
ADC128_REG_IN(i));
if (rv < 0)
@@ -107,20 +115,25 @@
data->in[2][i] = rv << 4;
}
- rv = i2c_smbus_read_word_swapped(client, ADC128_REG_TEMP);
- if (rv < 0)
- goto abort;
- data->temp[0] = rv >> 7;
+ if (data->mode != 1) {
+ rv = i2c_smbus_read_word_swapped(client,
+ ADC128_REG_TEMP);
+ if (rv < 0)
+ goto abort;
+ data->temp[0] = rv >> 7;
- rv = i2c_smbus_read_byte_data(client, ADC128_REG_TEMP_MAX);
- if (rv < 0)
- goto abort;
- data->temp[1] = rv << 1;
+ rv = i2c_smbus_read_byte_data(client,
+ ADC128_REG_TEMP_MAX);
+ if (rv < 0)
+ goto abort;
+ data->temp[1] = rv << 1;
- rv = i2c_smbus_read_byte_data(client, ADC128_REG_TEMP_HYST);
- if (rv < 0)
- goto abort;
- data->temp[2] = rv << 1;
+ rv = i2c_smbus_read_byte_data(client,
+ ADC128_REG_TEMP_HYST);
+ if (rv < 0)
+ goto abort;
+ data->temp[2] = rv << 1;
+ }
rv = i2c_smbus_read_byte_data(client, ADC128_REG_ALARM);
if (rv < 0)
@@ -240,6 +253,25 @@
return sprintf(buf, "%u\n", !!(alarms & mask));
}
+static umode_t adc128_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct adc128_data *data = dev_get_drvdata(dev);
+
+ if (index < ADC128_ATTR_NUM_VOLT) {
+ /* Voltage, visible according to num_inputs[] */
+ if (index >= num_inputs[data->mode] * 4)
+ return 0;
+ } else {
+ /* Temperature, visible if not in mode 1 */
+ if (data->mode == 1)
+ return 0;
+ }
+
+ return attr->mode;
+}
+
static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO,
adc128_show_in, NULL, 0, 0);
static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO,
@@ -289,6 +321,13 @@
static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO,
adc128_show_in, adc128_set_in, 6, 2);
+static SENSOR_DEVICE_ATTR_2(in7_input, S_IRUGO,
+ adc128_show_in, NULL, 7, 0);
+static SENSOR_DEVICE_ATTR_2(in7_min, S_IWUSR | S_IRUGO,
+ adc128_show_in, adc128_set_in, 7, 1);
+static SENSOR_DEVICE_ATTR_2(in7_max, S_IWUSR | S_IRUGO,
+ adc128_show_in, adc128_set_in, 7, 2);
+
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adc128_show_temp, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
adc128_show_temp, adc128_set_temp, 1);
@@ -302,44 +341,54 @@
static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, adc128_show_alarm, NULL, 4);
static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, adc128_show_alarm, NULL, 5);
static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, adc128_show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, adc128_show_alarm, NULL, 7);
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adc128_show_alarm, NULL, 7);
static struct attribute *adc128_attrs[] = {
- &sensor_dev_attr_in0_min.dev_attr.attr,
- &sensor_dev_attr_in1_min.dev_attr.attr,
- &sensor_dev_attr_in2_min.dev_attr.attr,
- &sensor_dev_attr_in3_min.dev_attr.attr,
- &sensor_dev_attr_in4_min.dev_attr.attr,
- &sensor_dev_attr_in5_min.dev_attr.attr,
- &sensor_dev_attr_in6_min.dev_attr.attr,
- &sensor_dev_attr_in0_max.dev_attr.attr,
- &sensor_dev_attr_in1_max.dev_attr.attr,
- &sensor_dev_attr_in2_max.dev_attr.attr,
- &sensor_dev_attr_in3_max.dev_attr.attr,
- &sensor_dev_attr_in4_max.dev_attr.attr,
- &sensor_dev_attr_in5_max.dev_attr.attr,
- &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
&sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
&sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
&sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
&sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
&sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
&sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
- &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
- &sensor_dev_attr_in0_alarm.dev_attr.attr,
- &sensor_dev_attr_in1_alarm.dev_attr.attr,
- &sensor_dev_attr_in2_alarm.dev_attr.attr,
- &sensor_dev_attr_in3_alarm.dev_attr.attr,
- &sensor_dev_attr_in4_alarm.dev_attr.attr,
- &sensor_dev_attr_in5_alarm.dev_attr.attr,
- &sensor_dev_attr_in6_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
NULL
};
-ATTRIBUTE_GROUPS(adc128);
+
+static struct attribute_group adc128_group = {
+ .attrs = adc128_attrs,
+ .is_visible = adc128_is_visible,
+};
+__ATTRIBUTE_GROUPS(adc128);
static int adc128_detect(struct i2c_client *client, struct i2c_board_info *info)
{
@@ -387,6 +436,15 @@
if (err)
return err;
+ /* Set operation mode, if non-default */
+ if (data->mode != 0) {
+ err = i2c_smbus_write_byte_data(client,
+ ADC128_REG_CONFIG_ADV,
+ data->mode << 1);
+ if (err)
+ return err;
+ }
+
/* Start monitoring */
err = i2c_smbus_write_byte_data(client, ADC128_REG_CONFIG, 0x01);
if (err)
@@ -433,6 +491,21 @@
data->vref = 2560; /* 2.56V, in mV */
}
+ /* Operation mode is optional. If unspecified, keep current mode */
+ if (of_property_read_u8(dev->of_node, "ti,mode", &data->mode) == 0) {
+ if (data->mode > 3) {
+ dev_err(dev, "invalid operation mode %d\n",
+ data->mode);
+ err = -EINVAL;
+ goto error;
+ }
+ } else {
+ err = i2c_smbus_read_byte_data(client, ADC128_REG_CONFIG_ADV);
+ if (err < 0)
+ goto error;
+ data->mode = (err >> 1) & ADC128_REG_MASK;
+ }
+
data->client = client;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c
index 1fdcc3e..eacf10f 100644
--- a/drivers/hwmon/adm1021.c
+++ b/drivers/hwmon/adm1021.c
@@ -191,7 +191,7 @@
return sprintf(buf, "%u\n", (data->alarms >> index) & 1);
}
-static ssize_t show_alarms(struct device *dev,
+static ssize_t alarms_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
@@ -251,16 +251,16 @@
return count;
}
-static ssize_t show_low_power(struct device *dev,
+static ssize_t low_power_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct adm1021_data *data = adm1021_update_device(dev);
return sprintf(buf, "%d\n", data->low_power);
}
-static ssize_t set_low_power(struct device *dev,
- struct device_attribute *devattr,
- const char *buf, size_t count)
+static ssize_t low_power_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
{
struct adm1021_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -303,8 +303,8 @@
static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3);
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 2);
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
-static DEVICE_ATTR(low_power, S_IWUSR | S_IRUGO, show_low_power, set_low_power);
+static DEVICE_ATTR_RO(alarms);
+static DEVICE_ATTR_RW(low_power);
static struct attribute *adm1021_attributes[] = {
&sensor_dev_attr_temp1_max.dev_attr.attr,
diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c
index 1abb460..1e4dad3 100644
--- a/drivers/hwmon/adm1025.c
+++ b/drivers/hwmon/adm1025.c
@@ -333,12 +333,12 @@
set_temp(2);
static ssize_t
-show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
+alarms_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct adm1025_data *data = adm1025_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static DEVICE_ATTR_RO(alarms);
static ssize_t
show_alarm(struct device *dev, struct device_attribute *attr, char *buf)
@@ -358,21 +358,21 @@
static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_alarm, NULL, 14);
static ssize_t
-show_vid(struct device *dev, struct device_attribute *attr, char *buf)
+cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct adm1025_data *data = adm1025_update_device(dev);
return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
static ssize_t
-show_vrm(struct device *dev, struct device_attribute *attr, char *buf)
+vrm_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct adm1025_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", data->vrm);
}
-static ssize_t set_vrm(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct adm1025_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -388,7 +388,7 @@
data->vrm = val;
return count;
}
-static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
+static DEVICE_ATTR_RW(vrm);
/*
* Real code
diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c
index b2a5d9e..e43f09a 100644
--- a/drivers/hwmon/adm1026.c
+++ b/drivers/hwmon/adm1026.c
@@ -1034,15 +1034,15 @@
temp_crit_reg(2);
temp_crit_reg(3);
-static ssize_t show_analog_out_reg(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t analog_out_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct adm1026_data *data = adm1026_update_device(dev);
return sprintf(buf, "%d\n", DAC_FROM_REG(data->analog_out));
}
-static ssize_t set_analog_out_reg(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t analog_out_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct adm1026_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -1060,11 +1060,10 @@
return count;
}
-static DEVICE_ATTR(analog_out, S_IRUGO | S_IWUSR, show_analog_out_reg,
- set_analog_out_reg);
+static DEVICE_ATTR_RW(analog_out);
-static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct adm1026_data *data = adm1026_update_device(dev);
int vid = (data->gpio >> 11) & 0x1f;
@@ -1073,17 +1072,17 @@
return sprintf(buf, "%d\n", vid_from_reg(vid, data->vrm));
}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
-static ssize_t show_vrm_reg(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct adm1026_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", data->vrm);
}
-static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct adm1026_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -1100,16 +1099,16 @@
return count;
}
-static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
+static DEVICE_ATTR_RW(vrm);
-static ssize_t show_alarms_reg(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct adm1026_data *data = adm1026_update_device(dev);
return sprintf(buf, "%ld\n", data->alarms);
}
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
+static DEVICE_ATTR_RO(alarms);
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -1148,14 +1147,15 @@
static SENSOR_DEVICE_ATTR(in10_alarm, S_IRUGO, show_alarm, NULL, 25);
static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 26);
-static ssize_t show_alarm_mask(struct device *dev,
+static ssize_t alarm_mask_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct adm1026_data *data = adm1026_update_device(dev);
return sprintf(buf, "%ld\n", data->alarm_mask);
}
-static ssize_t set_alarm_mask(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t alarm_mask_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct adm1026_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -1186,18 +1186,17 @@
return count;
}
-static DEVICE_ATTR(alarm_mask, S_IRUGO | S_IWUSR, show_alarm_mask,
- set_alarm_mask);
+static DEVICE_ATTR_RW(alarm_mask);
-static ssize_t show_gpio(struct device *dev, struct device_attribute *attr,
+static ssize_t gpio_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct adm1026_data *data = adm1026_update_device(dev);
return sprintf(buf, "%ld\n", data->gpio);
}
-static ssize_t set_gpio(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t gpio_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct adm1026_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -1221,16 +1220,18 @@
return count;
}
-static DEVICE_ATTR(gpio, S_IRUGO | S_IWUSR, show_gpio, set_gpio);
+static DEVICE_ATTR_RW(gpio);
-static ssize_t show_gpio_mask(struct device *dev, struct device_attribute *attr,
+static ssize_t gpio_mask_show(struct device *dev,
+ struct device_attribute *attr,
char *buf)
{
struct adm1026_data *data = adm1026_update_device(dev);
return sprintf(buf, "%ld\n", data->gpio_mask);
}
-static ssize_t set_gpio_mask(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t gpio_mask_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
{
struct adm1026_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -1254,17 +1255,17 @@
return count;
}
-static DEVICE_ATTR(gpio_mask, S_IRUGO | S_IWUSR, show_gpio_mask, set_gpio_mask);
+static DEVICE_ATTR_RW(gpio_mask);
-static ssize_t show_pwm_reg(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t pwm1_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct adm1026_data *data = adm1026_update_device(dev);
return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm1.pwm));
}
-static ssize_t set_pwm_reg(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t pwm1_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct adm1026_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -1285,16 +1286,17 @@
return count;
}
-static ssize_t show_auto_pwm_min(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t temp1_auto_point1_pwm_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
struct adm1026_data *data = adm1026_update_device(dev);
return sprintf(buf, "%d\n", data->pwm1.auto_pwm_min);
}
-static ssize_t set_auto_pwm_min(struct device *dev,
- struct device_attribute *attr, const char *buf,
- size_t count)
+static ssize_t temp1_auto_point1_pwm_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct adm1026_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -1316,21 +1318,23 @@
return count;
}
-static ssize_t show_auto_pwm_max(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t temp1_auto_point2_pwm_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
return sprintf(buf, "%d\n", ADM1026_PWM_MAX);
}
-static ssize_t show_pwm_enable(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t pwm1_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct adm1026_data *data = adm1026_update_device(dev);
return sprintf(buf, "%d\n", data->pwm1.enable);
}
-static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t pwm1_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct adm1026_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -1366,25 +1370,25 @@
}
/* enable PWM fan control */
-static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm_reg, set_pwm_reg);
-static DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, show_pwm_reg, set_pwm_reg);
-static DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, show_pwm_reg, set_pwm_reg);
-static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable,
- set_pwm_enable);
-static DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR, show_pwm_enable,
- set_pwm_enable);
-static DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR, show_pwm_enable,
- set_pwm_enable);
-static DEVICE_ATTR(temp1_auto_point1_pwm, S_IRUGO | S_IWUSR,
- show_auto_pwm_min, set_auto_pwm_min);
+static DEVICE_ATTR_RW(pwm1);
+static DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, pwm1_show, pwm1_store);
+static DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, pwm1_show, pwm1_store);
+static DEVICE_ATTR_RW(pwm1_enable);
+static DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR, pwm1_enable_show,
+ pwm1_enable_store);
+static DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR, pwm1_enable_show,
+ pwm1_enable_store);
+static DEVICE_ATTR_RW(temp1_auto_point1_pwm);
static DEVICE_ATTR(temp2_auto_point1_pwm, S_IRUGO | S_IWUSR,
- show_auto_pwm_min, set_auto_pwm_min);
+ temp1_auto_point1_pwm_show, temp1_auto_point1_pwm_store);
static DEVICE_ATTR(temp3_auto_point1_pwm, S_IRUGO | S_IWUSR,
- show_auto_pwm_min, set_auto_pwm_min);
+ temp1_auto_point1_pwm_show, temp1_auto_point1_pwm_store);
-static DEVICE_ATTR(temp1_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL);
-static DEVICE_ATTR(temp2_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL);
-static DEVICE_ATTR(temp3_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL);
+static DEVICE_ATTR_RO(temp1_auto_point2_pwm);
+static DEVICE_ATTR(temp2_auto_point2_pwm, S_IRUGO, temp1_auto_point2_pwm_show,
+ NULL);
+static DEVICE_ATTR(temp3_auto_point2_pwm, S_IRUGO, temp1_auto_point2_pwm_show,
+ NULL);
static struct attribute *adm1026_attributes[] = {
&sensor_dev_attr_in0_input.dev_attr.attr,
diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c
index a581898..bcf5082 100644
--- a/drivers/hwmon/adm1031.c
+++ b/drivers/hwmon/adm1031.c
@@ -829,14 +829,14 @@
temp_reg(3);
/* Alarms */
-static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n", data->alarm);
}
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static DEVICE_ATTR_RO(alarms);
static ssize_t show_alarm(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -867,7 +867,7 @@
16000, 8000, 4000, 2000, 1000, 500, 250, 125,
};
-static ssize_t show_update_interval(struct device *dev,
+static ssize_t update_interval_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct adm1031_data *data = dev_get_drvdata(dev);
@@ -875,9 +875,9 @@
return sprintf(buf, "%u\n", data->update_interval);
}
-static ssize_t set_update_interval(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t update_interval_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct adm1031_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -912,8 +912,7 @@
return count;
}
-static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval,
- set_update_interval);
+static DEVICE_ATTR_RW(update_interval);
static struct attribute *adm1031_attributes[] = {
&sensor_dev_attr_fan1_input.dev_attr.attr,
diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c
index 72bf248..255413f 100644
--- a/drivers/hwmon/adm9240.c
+++ b/drivers/hwmon/adm9240.c
@@ -262,8 +262,8 @@
/*** sysfs accessors ***/
/* temperature */
-static ssize_t show_temp(struct device *dev, struct device_attribute *dummy,
- char *buf)
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *dummy, char *buf)
{
struct adm9240_data *data = adm9240_update_device(dev);
return sprintf(buf, "%d\n", data->temp / 128 * 500); /* 9-bit value */
@@ -298,7 +298,7 @@
return count;
}
-static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
+static DEVICE_ATTR_RO(temp1_input);
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
show_max, set_max, 0);
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
@@ -501,13 +501,13 @@
fan(2);
/* alarms */
-static ssize_t show_alarms(struct device *dev,
+static ssize_t alarms_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct adm9240_data *data = adm9240_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static DEVICE_ATTR_RO(alarms);
static ssize_t show_alarm(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -527,25 +527,25 @@
static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7);
/* vid */
-static ssize_t show_vid(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct adm9240_data *data = adm9240_update_device(dev);
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
/* analog output */
-static ssize_t show_aout(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t aout_output_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct adm9240_data *data = adm9240_update_device(dev);
return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout));
}
-static ssize_t set_aout(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t aout_output_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct adm9240_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -562,7 +562,7 @@
mutex_unlock(&data->update_lock);
return count;
}
-static DEVICE_ATTR(aout_output, S_IRUGO | S_IWUSR, show_aout, set_aout);
+static DEVICE_ATTR_RW(aout_output);
static ssize_t chassis_clear(struct device *dev,
struct device_attribute *attr,
diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c
index bdeaece..b939f8a 100644
--- a/drivers/hwmon/adt7411.c
+++ b/drivers/hwmon/adt7411.c
@@ -21,6 +21,21 @@
#include <linux/hwmon-sysfs.h>
#include <linux/slab.h>
+#define ADT7411_REG_STAT_1 0x00
+#define ADT7411_STAT_1_INT_TEMP_HIGH BIT(0)
+#define ADT7411_STAT_1_INT_TEMP_LOW BIT(1)
+#define ADT7411_STAT_1_EXT_TEMP_HIGH_AIN1 BIT(2)
+#define ADT7411_STAT_1_EXT_TEMP_LOW BIT(3)
+#define ADT7411_STAT_1_EXT_TEMP_FAULT BIT(4)
+#define ADT7411_STAT_1_AIN2 BIT(5)
+#define ADT7411_STAT_1_AIN3 BIT(6)
+#define ADT7411_STAT_1_AIN4 BIT(7)
+#define ADT7411_REG_STAT_2 0x01
+#define ADT7411_STAT_2_AIN5 BIT(0)
+#define ADT7411_STAT_2_AIN6 BIT(1)
+#define ADT7411_STAT_2_AIN7 BIT(2)
+#define ADT7411_STAT_2_AIN8 BIT(3)
+#define ADT7411_STAT_2_VDD BIT(4)
#define ADT7411_REG_INT_TEMP_VDD_LSB 0x03
#define ADT7411_REG_EXT_TEMP_AIN14_LSB 0x04
#define ADT7411_REG_VDD_MSB 0x06
@@ -28,20 +43,31 @@
#define ADT7411_REG_EXT_TEMP_AIN1_MSB 0x08
#define ADT7411_REG_CFG1 0x18
-#define ADT7411_CFG1_START_MONITOR (1 << 0)
-#define ADT7411_CFG1_RESERVED_BIT1 (1 << 1)
-#define ADT7411_CFG1_EXT_TDM (1 << 2)
-#define ADT7411_CFG1_RESERVED_BIT3 (1 << 3)
+#define ADT7411_CFG1_START_MONITOR BIT(0)
+#define ADT7411_CFG1_RESERVED_BIT1 BIT(1)
+#define ADT7411_CFG1_EXT_TDM BIT(2)
+#define ADT7411_CFG1_RESERVED_BIT3 BIT(3)
#define ADT7411_REG_CFG2 0x19
-#define ADT7411_CFG2_DISABLE_AVG (1 << 5)
+#define ADT7411_CFG2_DISABLE_AVG BIT(5)
#define ADT7411_REG_CFG3 0x1a
-#define ADT7411_CFG3_ADC_CLK_225 (1 << 0)
-#define ADT7411_CFG3_RESERVED_BIT1 (1 << 1)
-#define ADT7411_CFG3_RESERVED_BIT2 (1 << 2)
-#define ADT7411_CFG3_RESERVED_BIT3 (1 << 3)
-#define ADT7411_CFG3_REF_VDD (1 << 4)
+#define ADT7411_CFG3_ADC_CLK_225 BIT(0)
+#define ADT7411_CFG3_RESERVED_BIT1 BIT(1)
+#define ADT7411_CFG3_RESERVED_BIT2 BIT(2)
+#define ADT7411_CFG3_RESERVED_BIT3 BIT(3)
+#define ADT7411_CFG3_REF_VDD BIT(4)
+
+#define ADT7411_REG_VDD_HIGH 0x23
+#define ADT7411_REG_VDD_LOW 0x24
+#define ADT7411_REG_TEMP_HIGH(nr) (0x25 + 2 * (nr))
+#define ADT7411_REG_TEMP_LOW(nr) (0x26 + 2 * (nr))
+#define ADT7411_REG_IN_HIGH(nr) ((nr) > 1 \
+ ? 0x2b + 2 * ((nr)-2) \
+ : 0x27)
+#define ADT7411_REG_IN_LOW(nr) ((nr) > 1 \
+ ? 0x2c + 2 * ((nr)-2) \
+ : 0x28)
#define ADT7411_REG_DEVICE_ID 0x4d
#define ADT7411_REG_MANUFACTURER_ID 0x4e
@@ -51,6 +77,30 @@
static const unsigned short normal_i2c[] = { 0x48, 0x4a, 0x4b, I2C_CLIENT_END };
+static const u8 adt7411_in_alarm_reg[] = {
+ ADT7411_REG_STAT_2,
+ ADT7411_REG_STAT_1,
+ ADT7411_REG_STAT_1,
+ ADT7411_REG_STAT_1,
+ ADT7411_REG_STAT_1,
+ ADT7411_REG_STAT_2,
+ ADT7411_REG_STAT_2,
+ ADT7411_REG_STAT_2,
+ ADT7411_REG_STAT_2,
+};
+
+static const u8 adt7411_in_alarm_bits[] = {
+ ADT7411_STAT_2_VDD,
+ ADT7411_STAT_1_EXT_TEMP_HIGH_AIN1,
+ ADT7411_STAT_1_AIN2,
+ ADT7411_STAT_1_AIN3,
+ ADT7411_STAT_1_AIN4,
+ ADT7411_STAT_2_AIN5,
+ ADT7411_STAT_2_AIN6,
+ ADT7411_STAT_2_AIN7,
+ ADT7411_STAT_2_AIN8,
+};
+
struct adt7411_data {
struct mutex device_lock; /* for "atomic" device accesses */
struct mutex update_lock;
@@ -165,6 +215,19 @@
};
ATTRIBUTE_GROUPS(adt7411);
+static int adt7411_read_in_alarm(struct device *dev, int channel, long *val)
+{
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, adt7411_in_alarm_reg[channel]);
+ if (ret < 0)
+ return ret;
+ *val = !!(ret & adt7411_in_alarm_bits[channel]);
+ return 0;
+}
+
static int adt7411_read_in_vdd(struct device *dev, u32 attr, long *val)
{
struct adt7411_data *data = dev_get_drvdata(dev);
@@ -179,11 +242,51 @@
return ret;
*val = ret * 7000 / 1024;
return 0;
+ case hwmon_in_min:
+ ret = i2c_smbus_read_byte_data(client, ADT7411_REG_VDD_LOW);
+ if (ret < 0)
+ return ret;
+ *val = ret * 7000 / 256;
+ return 0;
+ case hwmon_in_max:
+ ret = i2c_smbus_read_byte_data(client, ADT7411_REG_VDD_HIGH);
+ if (ret < 0)
+ return ret;
+ *val = ret * 7000 / 256;
+ return 0;
+ case hwmon_in_alarm:
+ return adt7411_read_in_alarm(dev, 0, val);
default:
return -EOPNOTSUPP;
}
}
+static int adt7411_update_vref(struct device *dev)
+{
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int val;
+
+ if (time_after_eq(jiffies, data->next_update)) {
+ val = i2c_smbus_read_byte_data(client, ADT7411_REG_CFG3);
+ if (val < 0)
+ return val;
+
+ if (val & ADT7411_CFG3_REF_VDD) {
+ val = adt7411_read_in_vdd(dev, hwmon_in_input,
+ &data->vref_cached);
+ if (val < 0)
+ return val;
+ } else {
+ data->vref_cached = 2250;
+ }
+
+ data->next_update = jiffies + HZ;
+ }
+
+ return 0;
+}
+
static int adt7411_read_in_chan(struct device *dev, u32 attr, int channel,
long *val)
{
@@ -191,26 +294,13 @@
struct i2c_client *client = data->client;
int ret;
- int lsb_reg, lsb_shift;
+ int reg, lsb_reg, lsb_shift;
int nr = channel - 1;
mutex_lock(&data->update_lock);
- if (time_after_eq(jiffies, data->next_update)) {
- ret = i2c_smbus_read_byte_data(client, ADT7411_REG_CFG3);
- if (ret < 0)
- goto exit_unlock;
-
- if (ret & ADT7411_CFG3_REF_VDD) {
- ret = adt7411_read_in_vdd(dev, hwmon_in_input,
- &data->vref_cached);
- if (ret < 0)
- goto exit_unlock;
- } else {
- data->vref_cached = 2250;
- }
-
- data->next_update = jiffies + HZ;
- }
+ ret = adt7411_update_vref(dev);
+ if (ret < 0)
+ goto exit_unlock;
switch (attr) {
case hwmon_in_input:
@@ -224,6 +314,20 @@
*val = ret * data->vref_cached / 1024;
ret = 0;
break;
+ case hwmon_in_min:
+ case hwmon_in_max:
+ reg = (attr == hwmon_in_min)
+ ? ADT7411_REG_IN_LOW(channel)
+ : ADT7411_REG_IN_HIGH(channel);
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0)
+ goto exit_unlock;
+ *val = ret * data->vref_cached / 256;
+ ret = 0;
+ break;
+ case hwmon_in_alarm:
+ ret = adt7411_read_in_alarm(dev, channel, val);
+ break;
default:
ret = -EOPNOTSUPP;
break;
@@ -242,12 +346,44 @@
return adt7411_read_in_chan(dev, attr, channel, val);
}
+
+static int adt7411_read_temp_alarm(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret, bit;
+
+ ret = i2c_smbus_read_byte_data(client, ADT7411_REG_STAT_1);
+ if (ret < 0)
+ return ret;
+
+ switch (attr) {
+ case hwmon_temp_min_alarm:
+ bit = channel ? ADT7411_STAT_1_EXT_TEMP_LOW
+ : ADT7411_STAT_1_INT_TEMP_LOW;
+ break;
+ case hwmon_temp_max_alarm:
+ bit = channel ? ADT7411_STAT_1_EXT_TEMP_HIGH_AIN1
+ : ADT7411_STAT_1_INT_TEMP_HIGH;
+ break;
+ case hwmon_temp_fault:
+ bit = ADT7411_STAT_1_EXT_TEMP_FAULT;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ *val = !!(ret & bit);
+ return 0;
+}
+
static int adt7411_read_temp(struct device *dev, u32 attr, int channel,
long *val)
{
struct adt7411_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
- int ret, regl, regh;
+ int ret, reg, regl, regh;
switch (attr) {
case hwmon_temp_input:
@@ -261,6 +397,21 @@
ret = ret & 0x200 ? ret - 0x400 : ret; /* 10 bit signed */
*val = ret * 250;
return 0;
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ reg = (attr == hwmon_temp_min)
+ ? ADT7411_REG_TEMP_LOW(channel)
+ : ADT7411_REG_TEMP_HIGH(channel);
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0)
+ return ret;
+ ret = ret & 0x80 ? ret - 0x100 : ret; /* 8 bit signed */
+ *val = ret * 1000;
+ return 0;
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_fault:
+ return adt7411_read_temp_alarm(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
@@ -279,26 +430,143 @@
}
}
+static int adt7411_write_in_vdd(struct device *dev, u32 attr, long val)
+{
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int reg;
+
+ val = clamp_val(val, 0, 255 * 7000 / 256);
+ val = DIV_ROUND_CLOSEST(val * 256, 7000);
+
+ switch (attr) {
+ case hwmon_in_min:
+ reg = ADT7411_REG_VDD_LOW;
+ break;
+ case hwmon_in_max:
+ reg = ADT7411_REG_VDD_HIGH;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+static int adt7411_write_in_chan(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret, reg;
+
+ mutex_lock(&data->update_lock);
+ ret = adt7411_update_vref(dev);
+ if (ret < 0)
+ goto exit_unlock;
+ val = clamp_val(val, 0, 255 * data->vref_cached / 256);
+ val = DIV_ROUND_CLOSEST(val * 256, data->vref_cached);
+
+ switch (attr) {
+ case hwmon_in_min:
+ reg = ADT7411_REG_IN_LOW(channel);
+ break;
+ case hwmon_in_max:
+ reg = ADT7411_REG_IN_HIGH(channel);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ goto exit_unlock;
+ }
+
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ exit_unlock:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static int adt7411_write_in(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ if (channel == 0)
+ return adt7411_write_in_vdd(dev, attr, val);
+ else
+ return adt7411_write_in_chan(dev, attr, channel, val);
+}
+
+static int adt7411_write_temp(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int reg;
+
+ val = clamp_val(val, -128000, 127000);
+ val = DIV_ROUND_CLOSEST(val, 1000);
+
+ switch (attr) {
+ case hwmon_temp_min:
+ reg = ADT7411_REG_TEMP_LOW(channel);
+ break;
+ case hwmon_temp_max:
+ reg = ADT7411_REG_TEMP_HIGH(channel);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+static int adt7411_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_in:
+ return adt7411_write_in(dev, attr, channel, val);
+ case hwmon_temp:
+ return adt7411_write_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static umode_t adt7411_is_visible(const void *_data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
const struct adt7411_data *data = _data;
+ bool visible;
switch (type) {
case hwmon_in:
- if (channel > 0 && channel < 3)
- return data->use_ext_temp ? 0 : S_IRUGO;
- else
- return S_IRUGO;
+ visible = channel == 0 || channel >= 3 || !data->use_ext_temp;
+ switch (attr) {
+ case hwmon_in_input:
+ case hwmon_in_alarm:
+ return visible ? S_IRUGO : 0;
+ case hwmon_in_min:
+ case hwmon_in_max:
+ return visible ? S_IRUGO | S_IWUSR : 0;
+ }
+ break;
case hwmon_temp:
- if (channel == 1)
- return data->use_ext_temp ? S_IRUGO : 0;
- else
- return S_IRUGO;
+ visible = channel == 0 || data->use_ext_temp;
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_fault:
+ return visible ? S_IRUGO : 0;
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ return visible ? S_IRUGO | S_IWUSR : 0;
+ }
+ break;
default:
- return 0;
+ break;
}
+ return 0;
}
static int adt7411_detect(struct i2c_client *client,
@@ -372,15 +640,15 @@
}
static const u32 adt7411_in_config[] = {
- HWMON_I_INPUT,
- HWMON_I_INPUT,
- HWMON_I_INPUT,
- HWMON_I_INPUT,
- HWMON_I_INPUT,
- HWMON_I_INPUT,
- HWMON_I_INPUT,
- HWMON_I_INPUT,
- HWMON_I_INPUT,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
0
};
@@ -390,8 +658,10 @@
};
static const u32 adt7411_temp_config[] = {
- HWMON_T_INPUT,
- HWMON_T_INPUT,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX | HWMON_T_MAX_ALARM,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX | HWMON_T_MAX_ALARM | HWMON_T_FAULT,
0
};
@@ -409,6 +679,7 @@
static const struct hwmon_ops adt7411_hwmon_ops = {
.is_visible = adt7411_is_visible,
.read = adt7411_read,
+ .write = adt7411_write,
};
static const struct hwmon_chip_info adt7411_chip_info = {
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index c9a1d9c..2cd9207 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -403,7 +403,7 @@
return data;
}
-static ssize_t show_auto_update_interval(struct device *dev,
+static ssize_t auto_update_interval_show(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
@@ -411,10 +411,9 @@
return sprintf(buf, "%d\n", data->auto_update_interval);
}
-static ssize_t set_auto_update_interval(struct device *dev,
- struct device_attribute *devattr,
- const char *buf,
- size_t count)
+static ssize_t auto_update_interval_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
{
struct adt7470_data *data = dev_get_drvdata(dev);
long temp;
@@ -431,7 +430,7 @@
return count;
}
-static ssize_t show_num_temp_sensors(struct device *dev,
+static ssize_t num_temp_sensors_show(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
@@ -439,10 +438,9 @@
return sprintf(buf, "%d\n", data->num_temp_sensors);
}
-static ssize_t set_num_temp_sensors(struct device *dev,
- struct device_attribute *devattr,
- const char *buf,
- size_t count)
+static ssize_t num_temp_sensors_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
{
struct adt7470_data *data = dev_get_drvdata(dev);
long temp;
@@ -537,7 +535,7 @@
return sprintf(buf, "%d\n", 1000 * data->temp[attr->index]);
}
-static ssize_t show_alarm_mask(struct device *dev,
+static ssize_t alarm_mask_show(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
@@ -546,10 +544,9 @@
return sprintf(buf, "%x\n", data->alarms_mask);
}
-static ssize_t set_alarm_mask(struct device *dev,
- struct device_attribute *devattr,
- const char *buf,
- size_t count)
+static ssize_t alarm_mask_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
{
struct adt7470_data *data = dev_get_drvdata(dev);
long mask;
@@ -723,8 +720,8 @@
11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500
};
-static ssize_t show_pwm_freq(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static ssize_t pwm1_freq_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
{
struct adt7470_data *data = adt7470_update_device(dev);
unsigned char cfg_reg_1;
@@ -745,9 +742,9 @@
return scnprintf(buf, PAGE_SIZE, "%d\n", adt7470_freq_map[index]);
}
-static ssize_t set_pwm_freq(struct device *dev,
- struct device_attribute *devattr,
- const char *buf, size_t count)
+static ssize_t pwm1_freq_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
{
struct adt7470_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -1012,12 +1009,9 @@
return sprintf(buf, "0\n");
}
-static DEVICE_ATTR(alarm_mask, S_IWUSR | S_IRUGO, show_alarm_mask,
- set_alarm_mask);
-static DEVICE_ATTR(num_temp_sensors, S_IWUSR | S_IRUGO, show_num_temp_sensors,
- set_num_temp_sensors);
-static DEVICE_ATTR(auto_update_interval, S_IWUSR | S_IRUGO,
- show_auto_update_interval, set_auto_update_interval);
+static DEVICE_ATTR_RW(alarm_mask);
+static DEVICE_ATTR_RW(num_temp_sensors);
+static DEVICE_ATTR_RW(auto_update_interval);
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
set_temp_max, 0);
@@ -1133,7 +1127,7 @@
static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3);
-static DEVICE_ATTR(pwm1_freq, S_IWUSR | S_IRUGO, show_pwm_freq, set_pwm_freq);
+static DEVICE_ATTR_RW(pwm1_freq);
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
show_pwm_min, set_pwm_min, 0);
diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c
index 3cefd1a..c646670 100644
--- a/drivers/hwmon/adt7475.c
+++ b/drivers/hwmon/adt7475.c
@@ -856,16 +856,17 @@
return count;
}
-static ssize_t show_pwm_at_crit(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static ssize_t pwm_use_point2_pwm_at_crit_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
{
struct adt7475_data *data = adt7475_update_device(dev);
return sprintf(buf, "%d\n", !!(data->config4 & CONFIG4_MAXDUTY));
}
-static ssize_t set_pwm_at_crit(struct device *dev,
- struct device_attribute *devattr,
- const char *buf, size_t count)
+static ssize_t pwm_use_point2_pwm_at_crit_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct adt7475_data *data = i2c_get_clientdata(client);
@@ -888,15 +889,15 @@
return count;
}
-static ssize_t show_vrm(struct device *dev, struct device_attribute *devattr,
+static ssize_t vrm_show(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct adt7475_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", (int)data->vrm);
}
-static ssize_t set_vrm(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
+static ssize_t vrm_store(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
{
struct adt7475_data *data = dev_get_drvdata(dev);
long val;
@@ -910,8 +911,8 @@
return count;
}
-static ssize_t show_vid(struct device *dev, struct device_attribute *devattr,
- char *buf)
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
{
struct adt7475_data *data = adt7475_update_device(dev);
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
@@ -1057,11 +1058,10 @@
set_pwm, MAX, 2);
/* Non-standard name, might need revisiting */
-static DEVICE_ATTR(pwm_use_point2_pwm_at_crit, S_IWUSR | S_IRUGO,
- show_pwm_at_crit, set_pwm_at_crit);
+static DEVICE_ATTR_RW(pwm_use_point2_pwm_at_crit);
-static DEVICE_ATTR(vrm, S_IWUSR | S_IRUGO, show_vrm, set_vrm);
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+static DEVICE_ATTR_RW(vrm);
+static DEVICE_ATTR_RO(cpu0_vid);
static struct attribute *adt7475_attrs[] = {
&sensor_dev_attr_in1_input.dev_attr.attr,
diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c
index 98141f4..0f538f8 100644
--- a/drivers/hwmon/adt7x10.c
+++ b/drivers/hwmon/adt7x10.c
@@ -331,9 +331,8 @@
return sprintf(buf, "%d\n", !!(ret & attr->index));
}
-static ssize_t adt7x10_show_name(struct device *dev,
- struct device_attribute *da,
- char *buf)
+static ssize_t name_show(struct device *dev, struct device_attribute *da,
+ char *buf)
{
struct adt7x10_data *data = dev_get_drvdata(dev);
@@ -359,7 +358,7 @@
NULL, ADT7X10_STAT_T_HIGH);
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7x10_show_alarm,
NULL, ADT7X10_STAT_T_CRIT);
-static DEVICE_ATTR(name, S_IRUGO, adt7x10_show_name, NULL);
+static DEVICE_ATTR_RO(name);
static struct attribute *adt7x10_attributes[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c
index 272fcc8..62e1913 100644
--- a/drivers/hwmon/asb100.c
+++ b/drivers/hwmon/asb100.c
@@ -483,25 +483,25 @@
sysfs_temp(4);
/* VID */
-static ssize_t show_vid(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct asb100_data *data = asb100_update_device(dev);
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
/* VRM */
-static ssize_t show_vrm(struct device *dev, struct device_attribute *attr,
+static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct asb100_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", data->vrm);
}
-static ssize_t set_vrm(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct asb100_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -519,16 +519,16 @@
}
/* Alarms */
-static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
+static DEVICE_ATTR_RW(vrm);
-static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct asb100_data *data = asb100_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static DEVICE_ATTR_RO(alarms);
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -550,15 +550,15 @@
static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13);
/* 1 PWM */
-static ssize_t show_pwm1(struct device *dev, struct device_attribute *attr,
+static ssize_t pwm1_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct asb100_data *data = asb100_update_device(dev);
return sprintf(buf, "%d\n", ASB100_PWM_FROM_REG(data->pwm & 0x0f));
}
-static ssize_t set_pwm1(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t pwm1_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct asb100_data *data = i2c_get_clientdata(client);
@@ -577,15 +577,16 @@
return count;
}
-static ssize_t show_pwm_enable1(struct device *dev,
+static ssize_t pwm1_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct asb100_data *data = asb100_update_device(dev);
return sprintf(buf, "%d\n", (data->pwm & 0x80) ? 1 : 0);
}
-static ssize_t set_pwm_enable1(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t pwm1_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct asb100_data *data = i2c_get_clientdata(client);
@@ -604,9 +605,8 @@
return count;
}
-static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm1, set_pwm1);
-static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
- show_pwm_enable1, set_pwm_enable1);
+static DEVICE_ATTR_RW(pwm1);
+static DEVICE_ATTR_RW(pwm1_enable);
static struct attribute *asb100_attributes[] = {
&sensor_dev_attr_in0_input.dev_attr.attr,
diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c
index f2f2f2f..b7eadb5 100644
--- a/drivers/hwmon/atxp1.c
+++ b/drivers/hwmon/atxp1.c
@@ -81,8 +81,8 @@
}
/* sys file functions for cpu0_vid */
-static ssize_t atxp1_showvcore(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
int size;
struct atxp1_data *data;
@@ -95,9 +95,9 @@
return size;
}
-static ssize_t atxp1_storevcore(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t cpu0_vid_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
{
struct atxp1_data *data = atxp1_update_device(dev);
struct i2c_client *client = data->client;
@@ -154,12 +154,11 @@
* CPU core reference voltage
* unit: millivolt
*/
-static DEVICE_ATTR(cpu0_vid, S_IRUGO | S_IWUSR, atxp1_showvcore,
- atxp1_storevcore);
+static DEVICE_ATTR_RW(cpu0_vid);
/* sys file functions for GPIO1 */
-static ssize_t atxp1_showgpio1(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t gpio1_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
int size;
struct atxp1_data *data;
@@ -171,9 +170,8 @@
return size;
}
-static ssize_t atxp1_storegpio1(struct device *dev,
- struct device_attribute *attr, const char *buf,
- size_t count)
+static ssize_t gpio1_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct atxp1_data *data = atxp1_update_device(dev);
struct i2c_client *client = data->client;
@@ -201,11 +199,11 @@
* GPIO1 data register
* unit: Four bit as hex (e.g. 0x0f)
*/
-static DEVICE_ATTR(gpio1, S_IRUGO | S_IWUSR, atxp1_showgpio1, atxp1_storegpio1);
+static DEVICE_ATTR_RW(gpio1);
/* sys file functions for GPIO2 */
-static ssize_t atxp1_showgpio2(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t gpio2_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
int size;
struct atxp1_data *data;
@@ -217,9 +215,8 @@
return size;
}
-static ssize_t atxp1_storegpio2(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t gpio2_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct atxp1_data *data = atxp1_update_device(dev);
struct i2c_client *client = data->client;
@@ -246,7 +243,7 @@
* GPIO2 data register
* unit: Eight bit as hex (e.g. 0xff)
*/
-static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2);
+static DEVICE_ATTR_RW(gpio2);
static struct attribute *atxp1_attrs[] = {
&dev_attr_gpio1.attr,
diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c
index 8763c4a..aa40a00 100644
--- a/drivers/hwmon/dme1737.c
+++ b/drivers/hwmon/dme1737.c
@@ -279,7 +279,8 @@
static inline int IN_TO_REG(long val, int nominal)
{
- return clamp_val((val * 192 + nominal / 2) / nominal, 0, 255);
+ val = clamp_val(val, 0, 255 * nominal / 192);
+ return DIV_ROUND_CLOSEST(val * 192, nominal);
}
/*
@@ -295,7 +296,8 @@
static inline int TEMP_TO_REG(long val)
{
- return clamp_val((val < 0 ? val - 500 : val + 500) / 1000, -128, 127);
+ val = clamp_val(val, -128000, 127000);
+ return DIV_ROUND_CLOSEST(val, 1000);
}
/* Temperature range */
@@ -331,9 +333,10 @@
return (((ix == 1) ? reg : reg >> 4) & 0x0f) * 1000;
}
-static inline int TEMP_HYST_TO_REG(long val, int ix, int reg)
+static inline int TEMP_HYST_TO_REG(int temp, long hyst, int ix, int reg)
{
- int hyst = clamp_val((val + 500) / 1000, 0, 15);
+ hyst = clamp_val(hyst, temp - 15000, temp);
+ hyst = DIV_ROUND_CLOSEST(temp - hyst, 1000);
return (ix == 1) ? (reg & 0xf0) | hyst : (reg & 0x0f) | (hyst << 4);
}
@@ -1022,7 +1025,9 @@
int ix = sensor_attr_2->index;
int fn = sensor_attr_2->nr;
long val;
+ int temp;
int err;
+ u8 reg;
err = kstrtol(buf, 10, &val);
if (err)
@@ -1035,10 +1040,9 @@
data->zone_low[ix] = dme1737_read(data,
DME1737_REG_ZONE_LOW(ix));
/* Modify the temp hyst value */
- data->zone_hyst[ix == 2] = TEMP_HYST_TO_REG(
- TEMP_FROM_REG(data->zone_low[ix], 8) -
- val, ix, dme1737_read(data,
- DME1737_REG_ZONE_HYST(ix == 2)));
+ temp = TEMP_FROM_REG(data->zone_low[ix], 8);
+ reg = dme1737_read(data, DME1737_REG_ZONE_HYST(ix == 2));
+ data->zone_hyst[ix == 2] = TEMP_HYST_TO_REG(temp, val, ix, reg);
dme1737_write(data, DME1737_REG_ZONE_HYST(ix == 2),
data->zone_hyst[ix == 2]);
break;
@@ -1055,10 +1059,10 @@
* Modify the temp range value (which is stored in the upper
* nibble of the pwm_freq register)
*/
- data->pwm_freq[ix] = TEMP_RANGE_TO_REG(val -
- TEMP_FROM_REG(data->zone_low[ix], 8),
- dme1737_read(data,
- DME1737_REG_PWM_FREQ(ix)));
+ temp = TEMP_FROM_REG(data->zone_low[ix], 8);
+ val = clamp_val(val, temp, temp + 80000);
+ reg = dme1737_read(data, DME1737_REG_PWM_FREQ(ix));
+ data->pwm_freq[ix] = TEMP_RANGE_TO_REG(val - temp, reg);
dme1737_write(data, DME1737_REG_PWM_FREQ(ix),
data->pwm_freq[ix]);
break;
@@ -1468,7 +1472,7 @@
* Miscellaneous sysfs attributes
* --------------------------------------------------------------------- */
-static ssize_t show_vrm(struct device *dev, struct device_attribute *attr,
+static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -1477,8 +1481,8 @@
return sprintf(buf, "%d\n", data->vrm);
}
-static ssize_t set_vrm(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct dme1737_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -1495,15 +1499,15 @@
return count;
}
-static ssize_t show_vid(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct dme1737_data *data = dme1737_update_device(dev);
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
}
-static ssize_t show_name(struct device *dev, struct device_attribute *attr,
+static ssize_t name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct dme1737_data *data = dev_get_drvdata(dev);
@@ -1645,9 +1649,9 @@
/* Misc */
-static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); /* for ISA devices */
+static DEVICE_ATTR_RW(vrm);
+static DEVICE_ATTR_RO(cpu0_vid);
+static DEVICE_ATTR_RO(name); /* for ISA devices */
/*
* This struct holds all the attributes that are always present and need to be
diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c
index 8890870..5c317fc 100644
--- a/drivers/hwmon/ds1621.c
+++ b/drivers/hwmon/ds1621.c
@@ -263,7 +263,7 @@
return count;
}
-static ssize_t show_alarms(struct device *dev, struct device_attribute *da,
+static ssize_t alarms_show(struct device *dev, struct device_attribute *da,
char *buf)
{
struct ds1621_data *data = ds1621_update_client(dev);
@@ -278,15 +278,16 @@
return sprintf(buf, "%d\n", !!(data->conf & attr->index));
}
-static ssize_t show_convrate(struct device *dev, struct device_attribute *da,
- char *buf)
+static ssize_t update_interval_show(struct device *dev,
+ struct device_attribute *da, char *buf)
{
struct ds1621_data *data = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%hu\n", data->update_interval);
}
-static ssize_t set_convrate(struct device *dev, struct device_attribute *da,
- const char *buf, size_t count)
+static ssize_t update_interval_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
{
struct ds1621_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -315,9 +316,8 @@
return count;
}
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
-static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_convrate,
- set_convrate);
+static DEVICE_ATTR_RO(alarms);
+static DEVICE_ATTR_RW(update_interval);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp, set_temp, 1);
diff --git a/drivers/hwmon/emc2103.c b/drivers/hwmon/emc2103.c
index 4b870ee..1ed9a7a 100644
--- a/drivers/hwmon/emc2103.c
+++ b/drivers/hwmon/emc2103.c
@@ -284,7 +284,7 @@
}
static ssize_t
-show_fan(struct device *dev, struct device_attribute *da, char *buf)
+fan1_input_show(struct device *dev, struct device_attribute *da, char *buf)
{
struct emc2103_data *data = emc2103_update_device(dev);
int rpm = 0;
@@ -294,7 +294,7 @@
}
static ssize_t
-show_fan_div(struct device *dev, struct device_attribute *da, char *buf)
+fan1_div_show(struct device *dev, struct device_attribute *da, char *buf)
{
struct emc2103_data *data = emc2103_update_device(dev);
int fan_div = 8 / data->fan_multiplier;
@@ -307,8 +307,8 @@
* of least surprise; the user doesn't expect the fan target to change just
* because the divider changed.
*/
-static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
- const char *buf, size_t count)
+static ssize_t fan1_div_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
{
struct emc2103_data *data = emc2103_update_device(dev);
struct i2c_client *client = data->client;
@@ -369,7 +369,7 @@
}
static ssize_t
-show_fan_target(struct device *dev, struct device_attribute *da, char *buf)
+fan1_target_show(struct device *dev, struct device_attribute *da, char *buf)
{
struct emc2103_data *data = emc2103_update_device(dev);
int rpm = 0;
@@ -382,8 +382,9 @@
return sprintf(buf, "%d\n", rpm);
}
-static ssize_t set_fan_target(struct device *dev, struct device_attribute *da,
- const char *buf, size_t count)
+static ssize_t fan1_target_store(struct device *dev,
+ struct device_attribute *da, const char *buf,
+ size_t count)
{
struct emc2103_data *data = emc2103_update_device(dev);
struct i2c_client *client = data->client;
@@ -412,7 +413,7 @@
}
static ssize_t
-show_fan_fault(struct device *dev, struct device_attribute *da, char *buf)
+fan1_fault_show(struct device *dev, struct device_attribute *da, char *buf)
{
struct emc2103_data *data = emc2103_update_device(dev);
bool fault = ((data->fan_tach & 0x1fe0) == 0x1fe0);
@@ -420,14 +421,15 @@
}
static ssize_t
-show_pwm_enable(struct device *dev, struct device_attribute *da, char *buf)
+pwm1_enable_show(struct device *dev, struct device_attribute *da, char *buf)
{
struct emc2103_data *data = emc2103_update_device(dev);
return sprintf(buf, "%d\n", data->fan_rpm_control ? 3 : 0);
}
-static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *da,
- const char *buf, size_t count)
+static ssize_t pwm1_enable_store(struct device *dev,
+ struct device_attribute *da, const char *buf,
+ size_t count)
{
struct emc2103_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -512,14 +514,12 @@
static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_temp_max_alarm,
NULL, 3);
-static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL);
-static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, show_fan_div, set_fan_div);
-static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_fan_target,
- set_fan_target);
-static DEVICE_ATTR(fan1_fault, S_IRUGO, show_fan_fault, NULL);
+static DEVICE_ATTR_RO(fan1_input);
+static DEVICE_ATTR_RW(fan1_div);
+static DEVICE_ATTR_RW(fan1_target);
+static DEVICE_ATTR_RO(fan1_fault);
-static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable,
- set_pwm_enable);
+static DEVICE_ATTR_RW(pwm1_enable);
/* sensors present on all models */
static struct attribute *emc2103_attributes[] = {
diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c
index facd05c..73c6811 100644
--- a/drivers/hwmon/f71805f.c
+++ b/drivers/hwmon/f71805f.c
@@ -946,7 +946,7 @@
return count;
}
-static ssize_t show_alarms_in(struct device *dev, struct device_attribute
+static ssize_t alarms_in_show(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct f71805f_data *data = f71805f_update_device(dev);
@@ -954,7 +954,7 @@
return sprintf(buf, "%lu\n", data->alarms & 0x7ff);
}
-static ssize_t show_alarms_fan(struct device *dev, struct device_attribute
+static ssize_t alarms_fan_show(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct f71805f_data *data = f71805f_update_device(dev);
@@ -962,7 +962,7 @@
return sprintf(buf, "%lu\n", (data->alarms >> 16) & 0x07);
}
-static ssize_t show_alarms_temp(struct device *dev, struct device_attribute
+static ssize_t alarms_temp_show(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct f71805f_data *data = f71805f_update_device(dev);
@@ -980,7 +980,7 @@
return sprintf(buf, "%lu\n", (data->alarms >> bitnr) & 1);
}
-static ssize_t show_name(struct device *dev, struct device_attribute
+static ssize_t name_show(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct f71805f_data *data = dev_get_drvdata(dev);
@@ -1176,11 +1176,11 @@
static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 16);
static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 17);
static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 18);
-static DEVICE_ATTR(alarms_in, S_IRUGO, show_alarms_in, NULL);
-static DEVICE_ATTR(alarms_fan, S_IRUGO, show_alarms_fan, NULL);
-static DEVICE_ATTR(alarms_temp, S_IRUGO, show_alarms_temp, NULL);
+static DEVICE_ATTR_RO(alarms_in);
+static DEVICE_ATTR_RO(alarms_fan);
+static DEVICE_ATTR_RO(alarms_temp);
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
static struct attribute *f71805f_attributes[] = {
&sensor_dev_attr_in0_input.dev_attr.attr,
diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c
index cb28e4b..ca54ce5 100644
--- a/drivers/hwmon/f71882fg.c
+++ b/drivers/hwmon/f71882fg.c
@@ -390,7 +390,7 @@
static ssize_t store_pwm_auto_point_temp(struct device *dev,
struct device_attribute *devattr, const char *buf, size_t count);
/* Sysfs misc */
-static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
char *buf);
static int f71882fg_probe(struct platform_device *pdev);
@@ -404,7 +404,7 @@
.remove = f71882fg_remove,
};
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
/*
* Temp attr for the f71858fg, the f71858fg is special as it has its
@@ -2212,7 +2212,7 @@
return count;
}
-static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct f71882fg_data *data = dev_get_drvdata(dev);
diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
index 15aa49d..9545a34 100644
--- a/drivers/hwmon/fam15h_power.c
+++ b/drivers/hwmon/fam15h_power.c
@@ -83,8 +83,8 @@
return boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model >= 0x60;
}
-static ssize_t show_power(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t power1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
u32 val, tdp_limit, running_avg_range;
s32 running_avg_capture;
@@ -136,16 +136,16 @@
curr_pwr_watts = (curr_pwr_watts * 15625) >> (10 + running_avg_range);
return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts);
}
-static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
+static DEVICE_ATTR_RO(power1_input);
-static ssize_t show_power_crit(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t power1_crit_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct fam15h_power_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", data->processor_pwr_watts);
}
-static DEVICE_ATTR(power1_crit, S_IRUGO, show_power_crit, NULL);
+static DEVICE_ATTR_RO(power1_crit);
static void do_read_registers_on_cu(void *_data)
{
@@ -212,9 +212,8 @@
return 0;
}
-static ssize_t acc_show_power(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static ssize_t power1_average_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct fam15h_power_data *data = dev_get_drvdata(dev);
u64 prev_cu_acc_power[MAX_CUS], prev_ptsc[MAX_CUS],
@@ -267,20 +266,20 @@
return sprintf(buf, "%llu\n", (unsigned long long)avg_acc);
}
-static DEVICE_ATTR(power1_average, S_IRUGO, acc_show_power, NULL);
+static DEVICE_ATTR_RO(power1_average);
-static ssize_t acc_show_power_period(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static ssize_t power1_average_interval_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
struct fam15h_power_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%lu\n", data->power_period);
}
-static ssize_t acc_set_power_period(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t power1_average_interval_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct fam15h_power_data *data = dev_get_drvdata(dev);
unsigned long temp;
@@ -301,8 +300,7 @@
return count;
}
-static DEVICE_ATTR(power1_average_interval, S_IRUGO | S_IWUSR,
- acc_show_power_period, acc_set_power_period);
+static DEVICE_ATTR_RW(power1_average_interval);
static int fam15h_power_init_attrs(struct pci_dev *pdev,
struct fam15h_power_data *data)
diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c
index d58abdc..5e78229 100644
--- a/drivers/hwmon/fschmd.c
+++ b/drivers/hwmon/fschmd.c
@@ -561,7 +561,7 @@
* The FSC hwmon family has the ability to force an attached alert led to flash
* from software, we export this as an alert_led sysfs attr
*/
-static ssize_t show_alert_led(struct device *dev,
+static ssize_t alert_led_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct fschmd_data *data = fschmd_update_device(dev);
@@ -572,7 +572,7 @@
return sprintf(buf, "0\n");
}
-static ssize_t store_alert_led(struct device *dev,
+static ssize_t alert_led_store(struct device *dev,
struct device_attribute *devattr, const char *buf, size_t count)
{
u8 reg;
@@ -602,7 +602,7 @@
return count;
}
-static DEVICE_ATTR(alert_led, 0644, show_alert_led, store_alert_led);
+static DEVICE_ATTR_RW(alert_led);
static struct sensor_device_attribute fschmd_attr[] = {
SENSOR_ATTR(in0_input, 0444, show_in_value, NULL, 0),
diff --git a/drivers/hwmon/g760a.c b/drivers/hwmon/g760a.c
index ec6a77d..7be1371 100644
--- a/drivers/hwmon/g760a.c
+++ b/drivers/hwmon/g760a.c
@@ -107,8 +107,8 @@
return data;
}
-static ssize_t show_fan(struct device *dev, struct device_attribute *da,
- char *buf)
+static ssize_t fan1_input_show(struct device *dev,
+ struct device_attribute *da, char *buf)
{
struct g760a_data *data = g760a_update_client(dev);
unsigned int rpm = 0;
@@ -121,8 +121,8 @@
return sprintf(buf, "%d\n", rpm);
}
-static ssize_t show_fan_alarm(struct device *dev, struct device_attribute *da,
- char *buf)
+static ssize_t fan1_alarm_show(struct device *dev,
+ struct device_attribute *da, char *buf)
{
struct g760a_data *data = g760a_update_client(dev);
@@ -131,16 +131,16 @@
return sprintf(buf, "%d\n", fan_alarm);
}
-static ssize_t get_pwm(struct device *dev, struct device_attribute *da,
- char *buf)
+static ssize_t pwm1_show(struct device *dev, struct device_attribute *da,
+ char *buf)
{
struct g760a_data *data = g760a_update_client(dev);
return sprintf(buf, "%d\n", PWM_FROM_CNT(data->set_cnt));
}
-static ssize_t set_pwm(struct device *dev, struct device_attribute *da,
- const char *buf, size_t count)
+static ssize_t pwm1_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
{
struct g760a_data *data = g760a_update_client(dev);
struct i2c_client *client = data->client;
@@ -157,9 +157,9 @@
return count;
}
-static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm);
-static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL);
-static DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL);
+static DEVICE_ATTR_RW(pwm1);
+static DEVICE_ATTR_RO(fan1_input);
+static DEVICE_ATTR_RO(fan1_alarm);
static struct attribute *g760a_attrs[] = {
&dev_attr_pwm1.attr,
diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c
index 628be9c..6dca2fd 100644
--- a/drivers/hwmon/g762.c
+++ b/drivers/hwmon/g762.c
@@ -738,8 +738,8 @@
* Read function for fan1_input sysfs file. Return current fan RPM value, or
* 0 if fan is out of control.
*/
-static ssize_t get_fan_rpm(struct device *dev, struct device_attribute *da,
- char *buf)
+static ssize_t fan1_input_show(struct device *dev,
+ struct device_attribute *da, char *buf)
{
struct g762_data *data = g762_update_client(dev);
unsigned int rpm = 0;
@@ -764,8 +764,8 @@
* Read and write functions for pwm1_mode sysfs file. Get and set fan speed
* control mode i.e. PWM (1) or DC (0).
*/
-static ssize_t get_pwm_mode(struct device *dev, struct device_attribute *da,
- char *buf)
+static ssize_t pwm1_mode_show(struct device *dev, struct device_attribute *da,
+ char *buf)
{
struct g762_data *data = g762_update_client(dev);
@@ -776,8 +776,9 @@
!!(data->fan_cmd1 & G762_REG_FAN_CMD1_OUT_MODE));
}
-static ssize_t set_pwm_mode(struct device *dev, struct device_attribute *da,
- const char *buf, size_t count)
+static ssize_t pwm1_mode_store(struct device *dev,
+ struct device_attribute *da, const char *buf,
+ size_t count)
{
unsigned long val;
int ret;
@@ -796,8 +797,8 @@
* Read and write functions for fan1_div sysfs file. Get and set fan
* controller prescaler value
*/
-static ssize_t get_fan_div(struct device *dev,
- struct device_attribute *da, char *buf)
+static ssize_t fan1_div_show(struct device *dev, struct device_attribute *da,
+ char *buf)
{
struct g762_data *data = g762_update_client(dev);
@@ -807,9 +808,8 @@
return sprintf(buf, "%d\n", G762_CLKDIV_FROM_REG(data->fan_cmd1));
}
-static ssize_t set_fan_div(struct device *dev,
- struct device_attribute *da,
- const char *buf, size_t count)
+static ssize_t fan1_div_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
{
unsigned long val;
int ret;
@@ -828,8 +828,8 @@
* Read and write functions for fan1_pulses sysfs file. Get and set number
* of tachometer pulses per fan revolution.
*/
-static ssize_t get_fan_pulses(struct device *dev,
- struct device_attribute *da, char *buf)
+static ssize_t fan1_pulses_show(struct device *dev,
+ struct device_attribute *da, char *buf)
{
struct g762_data *data = g762_update_client(dev);
@@ -839,9 +839,9 @@
return sprintf(buf, "%d\n", G762_PULSE_FROM_REG(data->fan_cmd1));
}
-static ssize_t set_fan_pulses(struct device *dev,
- struct device_attribute *da,
- const char *buf, size_t count)
+static ssize_t fan1_pulses_store(struct device *dev,
+ struct device_attribute *da, const char *buf,
+ size_t count)
{
unsigned long val;
int ret;
@@ -870,8 +870,8 @@
* but we do not accept 0 as this mode is not natively supported by the chip
* and it is not emulated by g762 driver. -EINVAL is returned in this case.
*/
-static ssize_t get_pwm_enable(struct device *dev,
- struct device_attribute *da, char *buf)
+static ssize_t pwm1_enable_show(struct device *dev,
+ struct device_attribute *da, char *buf)
{
struct g762_data *data = g762_update_client(dev);
@@ -882,9 +882,9 @@
(!!(data->fan_cmd1 & G762_REG_FAN_CMD1_FAN_MODE)) + 1);
}
-static ssize_t set_pwm_enable(struct device *dev,
- struct device_attribute *da,
- const char *buf, size_t count)
+static ssize_t pwm1_enable_store(struct device *dev,
+ struct device_attribute *da, const char *buf,
+ size_t count)
{
unsigned long val;
int ret;
@@ -904,8 +904,8 @@
* (which affects fan speed) in open-loop mode. 0 stops the fan and 255
* makes it run at full speed.
*/
-static ssize_t get_pwm(struct device *dev, struct device_attribute *da,
- char *buf)
+static ssize_t pwm1_show(struct device *dev, struct device_attribute *da,
+ char *buf)
{
struct g762_data *data = g762_update_client(dev);
@@ -915,8 +915,8 @@
return sprintf(buf, "%d\n", data->set_out);
}
-static ssize_t set_pwm(struct device *dev, struct device_attribute *da,
- const char *buf, size_t count)
+static ssize_t pwm1_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
{
unsigned long val;
int ret;
@@ -942,8 +942,8 @@
* Also note that due to rounding errors it is possible that you don't read
* back exactly the value you have set.
*/
-static ssize_t get_fan_target(struct device *dev, struct device_attribute *da,
- char *buf)
+static ssize_t fan1_target_show(struct device *dev,
+ struct device_attribute *da, char *buf)
{
struct g762_data *data = g762_update_client(dev);
unsigned int rpm;
@@ -961,8 +961,9 @@
return sprintf(buf, "%u\n", rpm);
}
-static ssize_t set_fan_target(struct device *dev, struct device_attribute *da,
- const char *buf, size_t count)
+static ssize_t fan1_target_store(struct device *dev,
+ struct device_attribute *da, const char *buf,
+ size_t count)
{
unsigned long val;
int ret;
@@ -978,7 +979,7 @@
}
/* read function for fan1_fault sysfs file. */
-static ssize_t get_fan_failure(struct device *dev, struct device_attribute *da,
+static ssize_t fan1_fault_show(struct device *dev, struct device_attribute *da,
char *buf)
{
struct g762_data *data = g762_update_client(dev);
@@ -993,8 +994,8 @@
* read function for fan1_alarm sysfs file. Note that OOC condition is
* enabled low
*/
-static ssize_t get_fan_ooc(struct device *dev, struct device_attribute *da,
- char *buf)
+static ssize_t fan1_alarm_show(struct device *dev,
+ struct device_attribute *da, char *buf)
{
struct g762_data *data = g762_update_client(dev);
@@ -1004,18 +1005,15 @@
return sprintf(buf, "%u\n", !(data->fan_sta & G762_REG_FAN_STA_OOC));
}
-static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm);
-static DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, get_pwm_mode, set_pwm_mode);
-static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
- get_pwm_enable, set_pwm_enable);
-static DEVICE_ATTR(fan1_input, S_IRUGO, get_fan_rpm, NULL);
-static DEVICE_ATTR(fan1_alarm, S_IRUGO, get_fan_ooc, NULL);
-static DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan_failure, NULL);
-static DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO,
- get_fan_target, set_fan_target);
-static DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, get_fan_div, set_fan_div);
-static DEVICE_ATTR(fan1_pulses, S_IWUSR | S_IRUGO,
- get_fan_pulses, set_fan_pulses);
+static DEVICE_ATTR_RW(pwm1);
+static DEVICE_ATTR_RW(pwm1_mode);
+static DEVICE_ATTR_RW(pwm1_enable);
+static DEVICE_ATTR_RO(fan1_input);
+static DEVICE_ATTR_RO(fan1_alarm);
+static DEVICE_ATTR_RO(fan1_fault);
+static DEVICE_ATTR_RW(fan1_target);
+static DEVICE_ATTR_RW(fan1_div);
+static DEVICE_ATTR_RW(fan1_pulses);
/* Driver data */
static struct attribute *g762_attrs[] = {
diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c
index 0212c83..b267510 100644
--- a/drivers/hwmon/gl518sm.c
+++ b/drivers/hwmon/gl518sm.c
@@ -86,9 +86,8 @@
#define BOOL_FROM_REG(val) ((val) ? 0 : 1)
#define BOOL_TO_REG(val) ((val) ? 0 : 1)
-#define TEMP_TO_REG(val) clamp_val(((((val) < 0 ? \
- (val) - 500 : \
- (val) + 500) / 1000) + 119), 0, 255)
+#define TEMP_CLAMP(val) clamp_val(val, -119000, 136000)
+#define TEMP_TO_REG(val) (DIV_ROUND_CLOSEST(TEMP_CLAMP(val), 1000) + 119)
#define TEMP_FROM_REG(val) (((val) - 119) * 1000)
static inline u8 FAN_TO_REG(long rpm, int div)
@@ -101,11 +100,13 @@
}
#define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : (480000 / ((val) * (div))))
-#define IN_TO_REG(val) clamp_val((((val) + 9) / 19), 0, 255)
+#define IN_CLAMP(val) clamp_val(val, 0, 255 * 19)
+#define IN_TO_REG(val) DIV_ROUND_CLOSEST(IN_CLAMP(val), 19)
#define IN_FROM_REG(val) ((val) * 19)
-#define VDD_TO_REG(val) clamp_val((((val) * 4 + 47) / 95), 0, 255)
-#define VDD_FROM_REG(val) (((val) * 95 + 2) / 4)
+#define VDD_CLAMP(val) clamp_val(val, 0, 255 * 95 / 4)
+#define VDD_TO_REG(val) DIV_ROUND_CLOSEST(VDD_CLAMP(val) * 4, 95)
+#define VDD_FROM_REG(val) DIV_ROUND_CLOSEST((val) * 95, 4)
#define DIV_FROM_REG(val) (1 << (val))
diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c
index dee93ec..4ff32ee 100644
--- a/drivers/hwmon/gl520sm.c
+++ b/drivers/hwmon/gl520sm.c
@@ -200,19 +200,21 @@
* Sysfs stuff
*/
-static ssize_t get_cpu_vid(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct gl520_data *data = gl520_update_device(dev);
return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, get_cpu_vid, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
-#define VDD_FROM_REG(val) (((val) * 95 + 2) / 4)
-#define VDD_TO_REG(val) clamp_val((((val) * 4 + 47) / 95), 0, 255)
+#define VDD_FROM_REG(val) DIV_ROUND_CLOSEST((val) * 95, 4)
+#define VDD_CLAMP(val) clamp_val(val, 0, 255 * 95 / 4)
+#define VDD_TO_REG(val) DIV_ROUND_CLOSEST(VDD_CLAMP(val) * 4, 95)
-#define IN_FROM_REG(val) ((val) * 19)
-#define IN_TO_REG(val) clamp_val((((val) + 9) / 19), 0, 255)
+#define IN_FROM_REG(val) ((val) * 19)
+#define IN_CLAMP(val) clamp_val(val, 0, 255 * 19)
+#define IN_TO_REG(val) DIV_ROUND_CLOSEST(IN_CLAMP(val), 19)
static ssize_t get_in_input(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -349,8 +351,13 @@
#define DIV_FROM_REG(val) (1 << (val))
#define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : (480000 / ((val) << (div))))
-#define FAN_TO_REG(val, div) ((val) <= 0 ? 0 : \
- clamp_val((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255))
+
+#define FAN_BASE(div) (480000 >> (div))
+#define FAN_CLAMP(val, div) clamp_val(val, FAN_BASE(div) / 255, \
+ FAN_BASE(div))
+#define FAN_TO_REG(val, div) ((val) == 0 ? 0 : \
+ DIV_ROUND_CLOSEST(480000, \
+ FAN_CLAMP(val, div) << (div)))
static ssize_t get_fan_input(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -381,8 +388,8 @@
return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[n]));
}
-static ssize_t get_fan_off(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t fan1_off_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct gl520_data *data = gl520_update_device(dev);
return sprintf(buf, "%d\n", data->fan_off);
@@ -476,8 +483,9 @@
return count;
}
-static ssize_t set_fan_off(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t fan1_off_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
{
struct gl520_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -510,12 +518,11 @@
get_fan_div, set_fan_div, 0);
static SENSOR_DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR,
get_fan_div, set_fan_div, 1);
-static DEVICE_ATTR(fan1_off, S_IRUGO | S_IWUSR,
- get_fan_off, set_fan_off);
+static DEVICE_ATTR_RW(fan1_off);
-#define TEMP_FROM_REG(val) (((val) - 130) * 1000)
-#define TEMP_TO_REG(val) clamp_val(((((val) < 0 ? \
- (val) - 500 : (val) + 500) / 1000) + 130), 0, 255)
+#define TEMP_FROM_REG(val) (((val) - 130) * 1000)
+#define TEMP_CLAMP(val) clamp_val(val, -130000, 125000)
+#define TEMP_TO_REG(val) (DIV_ROUND_CLOSEST(TEMP_CLAMP(val), 1000) + 130)
static ssize_t get_temp_input(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -596,29 +603,30 @@
static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR,
get_temp_max_hyst, set_temp_max_hyst, 1);
-static ssize_t get_alarms(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct gl520_data *data = gl520_update_device(dev);
return sprintf(buf, "%d\n", data->alarms);
}
-static ssize_t get_beep_enable(struct device *dev, struct device_attribute
- *attr, char *buf)
+static ssize_t beep_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct gl520_data *data = gl520_update_device(dev);
return sprintf(buf, "%d\n", data->beep_enable);
}
-static ssize_t get_beep_mask(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t beep_mask_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct gl520_data *data = gl520_update_device(dev);
return sprintf(buf, "%d\n", data->beep_mask);
}
-static ssize_t set_beep_enable(struct device *dev, struct device_attribute
- *attr, const char *buf, size_t count)
+static ssize_t beep_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct gl520_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -641,8 +649,9 @@
return count;
}
-static ssize_t set_beep_mask(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t beep_mask_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
{
struct gl520_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -661,11 +670,9 @@
return count;
}
-static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
-static DEVICE_ATTR(beep_enable, S_IRUGO | S_IWUSR,
- get_beep_enable, set_beep_enable);
-static DEVICE_ATTR(beep_mask, S_IRUGO | S_IWUSR,
- get_beep_mask, set_beep_mask);
+static DEVICE_ATTR_RO(alarms);
+static DEVICE_ATTR_RW(beep_enable);
+static DEVICE_ATTR_RW(beep_mask);
static ssize_t get_alarm(struct device *dev, struct device_attribute *attr,
char *buf)
diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c
index 685568b..9c355b9 100644
--- a/drivers/hwmon/gpio-fan.c
+++ b/drivers/hwmon/gpio-fan.c
@@ -77,8 +77,8 @@
return IRQ_NONE;
}
-static ssize_t show_fan_alarm(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t fan1_alarm_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
struct gpio_fan_alarm *alarm = fan_data->alarm;
@@ -90,7 +90,7 @@
return sprintf(buf, "%d\n", value);
}
-static DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL);
+static DEVICE_ATTR_RO(fan1_alarm);
static int fan_alarm_init(struct gpio_fan_data *fan_data,
struct gpio_fan_alarm *alarm)
@@ -188,8 +188,8 @@
return fan_data->num_speed - 1;
}
-static ssize_t show_pwm(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t pwm1_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
u8 pwm = fan_data->speed_index * 255 / (fan_data->num_speed - 1);
@@ -197,8 +197,8 @@
return sprintf(buf, "%d\n", pwm);
}
-static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t pwm1_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
unsigned long pwm;
@@ -224,16 +224,17 @@
return ret;
}
-static ssize_t show_pwm_enable(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t pwm1_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", fan_data->pwm_enable);
}
-static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t pwm1_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
unsigned long val;
@@ -257,22 +258,22 @@
return count;
}
-static ssize_t show_pwm_mode(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t pwm1_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
return sprintf(buf, "0\n");
}
-static ssize_t show_rpm_min(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t fan1_min_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", fan_data->speed[0].rpm);
}
-static ssize_t show_rpm_max(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t fan1_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
@@ -280,8 +281,8 @@
fan_data->speed[fan_data->num_speed - 1].rpm);
}
-static ssize_t show_rpm(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t fan1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
@@ -313,14 +314,13 @@
return ret;
}
-static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm);
-static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
- show_pwm_enable, set_pwm_enable);
-static DEVICE_ATTR(pwm1_mode, S_IRUGO, show_pwm_mode, NULL);
-static DEVICE_ATTR(fan1_min, S_IRUGO, show_rpm_min, NULL);
-static DEVICE_ATTR(fan1_max, S_IRUGO, show_rpm_max, NULL);
-static DEVICE_ATTR(fan1_input, S_IRUGO, show_rpm, NULL);
-static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_rpm, set_rpm);
+static DEVICE_ATTR_RW(pwm1);
+static DEVICE_ATTR_RW(pwm1_enable);
+static DEVICE_ATTR_RO(pwm1_mode);
+static DEVICE_ATTR_RO(fan1_min);
+static DEVICE_ATTR_RO(fan1_max);
+static DEVICE_ATTR_RO(fan1_input);
+static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, fan1_input_show, set_rpm);
static umode_t gpio_fan_is_visible(struct kobject *kobj,
struct attribute *attr, int index)
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index 3932f92..28375d5 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -63,11 +63,11 @@
};
static ssize_t
-show_name(struct device *dev, struct device_attribute *attr, char *buf)
+name_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", to_hwmon_device(dev)->name);
}
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
static struct attribute *hwmon_dev_attrs[] = {
&dev_attr_name.attr,
@@ -544,9 +544,11 @@
struct device *hdev;
int i, j, err, id;
- /* Do not accept invalid characters in hwmon name attribute */
+ /* Complain about invalid characters in hwmon name attribute */
if (name && (!strlen(name) || strpbrk(name, "-* \t\n")))
- return ERR_PTR(-EINVAL);
+ dev_warn(dev,
+ "hwmon: '%s' is not a valid name attribute, please fix\n",
+ name);
id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL);
if (id < 0)
@@ -606,7 +608,7 @@
if (err)
goto free_hwmon;
- if (chip && chip->ops->read &&
+ if (dev && chip && chip->ops->read &&
chip->info[0]->type == hwmon_chip &&
(chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) {
const struct hwmon_channel_info **info = chip->info;
@@ -651,6 +653,9 @@
void *drvdata,
const struct attribute_group **groups)
{
+ if (!name)
+ return ERR_PTR(-EINVAL);
+
return __hwmon_device_register(dev, name, drvdata, NULL, groups);
}
EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups);
@@ -674,6 +679,9 @@
const struct hwmon_chip_info *chip,
const struct attribute_group **extra_groups)
{
+ if (!name)
+ return ERR_PTR(-EINVAL);
+
if (chip && (!chip->ops || !chip->ops->is_visible || !chip->info))
return ERR_PTR(-EINVAL);
@@ -695,7 +703,7 @@
dev_warn(dev,
"hwmon_device_register() is deprecated. Please convert the driver to use hwmon_device_register_with_info().\n");
- return hwmon_device_register_with_groups(dev, NULL, NULL, NULL);
+ return __hwmon_device_register(dev, NULL, NULL, NULL, NULL);
}
EXPORT_SYMBOL_GPL(hwmon_device_register);
diff --git a/drivers/hwmon/i5500_temp.c b/drivers/hwmon/i5500_temp.c
index 3e3ccbf..400e067 100644
--- a/drivers/hwmon/i5500_temp.c
+++ b/drivers/hwmon/i5500_temp.c
@@ -43,8 +43,8 @@
*/
/* Sensor resolution : 0.5 degree C */
-static ssize_t show_temp(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev->parent);
long temp;
@@ -83,7 +83,7 @@
return sprintf(buf, "%u\n", (unsigned int)ctsts & (1 << nr));
}
-static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
+static DEVICE_ATTR_RO(temp1_input);
static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_thresh, NULL, 0xE2);
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_thresh, NULL, 0xEC);
static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_thresh, NULL, 0xEE);
diff --git a/drivers/hwmon/i5k_amb.c b/drivers/hwmon/i5k_amb.c
index 6b3d197..a5a9f45 100644
--- a/drivers/hwmon/i5k_amb.c
+++ b/drivers/hwmon/i5k_amb.c
@@ -114,14 +114,14 @@
unsigned int num_attrs;
};
-static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
char *buf)
{
return sprintf(buf, "%s\n", DRVNAME);
}
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
static struct platform_device *amb_pdev;
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index ad82cb2..efb01c2 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -12,6 +12,7 @@
*
* Supports: IT8603E Super I/O chip w/LPC interface
* IT8620E Super I/O chip w/LPC interface
+ * IT8622E Super I/O chip w/LPC interface
* IT8623E Super I/O chip w/LPC interface
* IT8628E Super I/O chip w/LPC interface
* IT8705F Super I/O chip w/LPC interface
@@ -31,6 +32,7 @@
* IT8783E/F Super I/O chip w/LPC interface
* IT8786E Super I/O chip w/LPC interface
* IT8790E Super I/O chip w/LPC interface
+ * IT8792E Super I/O chip w/LPC interface
* Sis950 A clone of the IT8705F
*
* Copyright (C) 2001 Chris Gauthron
@@ -69,8 +71,8 @@
#define DRVNAME "it87"
enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8732,
- it8771, it8772, it8781, it8782, it8783, it8786, it8790, it8603,
- it8620, it8628 };
+ it8771, it8772, it8781, it8782, it8783, it8786, it8790,
+ it8792, it8603, it8620, it8622, it8628 };
static unsigned short force_id;
module_param(force_id, ushort, 0);
@@ -151,6 +153,7 @@
#define IT8726F_DEVID 0x8726
#define IT8728F_DEVID 0x8728
#define IT8732F_DEVID 0x8732
+#define IT8792E_DEVID 0x8733
#define IT8771E_DEVID 0x8771
#define IT8772E_DEVID 0x8772
#define IT8781F_DEVID 0x8781
@@ -160,6 +163,7 @@
#define IT8790E_DEVID 0x8790
#define IT8603E_DEVID 0x8603
#define IT8620E_DEVID 0x8620
+#define IT8622E_DEVID 0x8622
#define IT8623E_DEVID 0x8623
#define IT8628E_DEVID 0x8628
#define IT87_ACT_REG 0x30
@@ -293,9 +297,11 @@
#define FEAT_SIX_FANS BIT(11) /* Supports six fans */
#define FEAT_10_9MV_ADC BIT(12)
#define FEAT_AVCC3 BIT(13) /* Chip supports in9/AVCC3 */
-#define FEAT_SIX_PWM BIT(14) /* Chip supports 6 pwm chn */
-#define FEAT_PWM_FREQ2 BIT(15) /* Separate pwm freq 2 */
-#define FEAT_SIX_TEMP BIT(16) /* Up to 6 temp sensors */
+#define FEAT_FIVE_PWM BIT(14) /* Chip supports 5 pwm chn */
+#define FEAT_SIX_PWM BIT(15) /* Chip supports 6 pwm chn */
+#define FEAT_PWM_FREQ2 BIT(16) /* Separate pwm freq 2 */
+#define FEAT_SIX_TEMP BIT(17) /* Up to 6 temp sensors */
+#define FEAT_VIN3_5V BIT(18) /* VIN3 connected to +5V */
static const struct it87_devices it87_devices[] = {
[it87] = {
@@ -419,6 +425,15 @@
| FEAT_PWM_FREQ2,
.peci_mask = 0x07,
},
+ [it8792] = {
+ .name = "it8792",
+ .suffix = "E",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
+ | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL,
+ .peci_mask = 0x07,
+ .old_peci_mask = 0x02, /* Actually reports PCH */
+ },
[it8603] = {
.name = "it8603",
.suffix = "E",
@@ -433,7 +448,16 @@
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS
| FEAT_IN7_INTERNAL | FEAT_SIX_PWM | FEAT_PWM_FREQ2
- | FEAT_SIX_TEMP,
+ | FEAT_SIX_TEMP | FEAT_VIN3_5V,
+ .peci_mask = 0x07,
+ },
+ [it8622] = {
+ .name = "it8622",
+ .suffix = "E",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_FIVE_FANS
+ | FEAT_FIVE_PWM | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2
+ | FEAT_AVCC3 | FEAT_VIN3_5V,
.peci_mask = 0x07,
},
[it8628] = {
@@ -442,7 +466,7 @@
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS
| FEAT_IN7_INTERNAL | FEAT_SIX_PWM | FEAT_PWM_FREQ2
- | FEAT_SIX_TEMP,
+ | FEAT_SIX_TEMP | FEAT_VIN3_5V,
.peci_mask = 0x07,
},
};
@@ -465,9 +489,12 @@
#define has_in7_internal(data) ((data)->features & FEAT_IN7_INTERNAL)
#define has_six_fans(data) ((data)->features & FEAT_SIX_FANS)
#define has_avcc3(data) ((data)->features & FEAT_AVCC3)
+#define has_five_pwm(data) ((data)->features & (FEAT_FIVE_PWM \
+ | FEAT_SIX_PWM))
#define has_six_pwm(data) ((data)->features & FEAT_SIX_PWM)
#define has_pwm_freq2(data) ((data)->features & FEAT_PWM_FREQ2)
#define has_six_temp(data) ((data)->features & FEAT_SIX_TEMP)
+#define has_vin3_5v(data) ((data)->features & FEAT_VIN3_5V)
struct it87_sio_data {
enum chips type;
@@ -1300,25 +1327,35 @@
it87_write_value(data, IT87_REG_FAN_MAIN_CTRL,
data->fan_main_ctrl);
} else {
+ u8 ctrl;
+
/* No on/off mode, set maximum pwm value */
data->pwm_duty[nr] = pwm_to_reg(data, 0xff);
it87_write_value(data, IT87_REG_PWM_DUTY[nr],
data->pwm_duty[nr]);
/* and set manual mode */
- data->pwm_ctrl[nr] = has_newer_autopwm(data) ?
- data->pwm_temp_map[nr] :
- data->pwm_duty[nr];
- it87_write_value(data, IT87_REG_PWM[nr],
- data->pwm_ctrl[nr]);
+ if (has_newer_autopwm(data)) {
+ ctrl = (data->pwm_ctrl[nr] & 0x7c) |
+ data->pwm_temp_map[nr];
+ } else {
+ ctrl = data->pwm_duty[nr];
+ }
+ data->pwm_ctrl[nr] = ctrl;
+ it87_write_value(data, IT87_REG_PWM[nr], ctrl);
}
} else {
- if (val == 1) /* Manual mode */
- data->pwm_ctrl[nr] = has_newer_autopwm(data) ?
- data->pwm_temp_map[nr] :
- data->pwm_duty[nr];
- else /* Automatic mode */
- data->pwm_ctrl[nr] = 0x80 | data->pwm_temp_map[nr];
- it87_write_value(data, IT87_REG_PWM[nr], data->pwm_ctrl[nr]);
+ u8 ctrl;
+
+ if (has_newer_autopwm(data)) {
+ ctrl = (data->pwm_ctrl[nr] & 0x7c) |
+ data->pwm_temp_map[nr];
+ if (val != 1)
+ ctrl |= 0x80;
+ } else {
+ ctrl = (val == 1 ? data->pwm_duty[nr] : 0x80);
+ }
+ data->pwm_ctrl[nr] = ctrl;
+ it87_write_value(data, IT87_REG_PWM[nr], ctrl);
if (data->type != it8603 && nr < 3) {
/* set SmartGuardian mode */
@@ -1344,6 +1381,7 @@
return -EINVAL;
mutex_lock(&data->update_lock);
+ it87_update_pwm_ctrl(data, nr);
if (has_newer_autopwm(data)) {
/*
* If we are in automatic mode, the PWM duty cycle register
@@ -1456,13 +1494,15 @@
}
mutex_lock(&data->update_lock);
+ it87_update_pwm_ctrl(data, nr);
data->pwm_temp_map[nr] = reg;
/*
* If we are in automatic mode, write the temp mapping immediately;
* otherwise, just store it for later use.
*/
if (data->pwm_ctrl[nr] & 0x80) {
- data->pwm_ctrl[nr] = 0x80 | data->pwm_temp_map[nr];
+ data->pwm_ctrl[nr] = (data->pwm_ctrl[nr] & 0xfc) |
+ data->pwm_temp_map[nr];
it87_write_value(data, IT87_REG_PWM[nr], data->pwm_ctrl[nr]);
}
mutex_unlock(&data->update_lock);
@@ -1762,14 +1802,14 @@
show_auto_pwm_slope, set_auto_pwm_slope, 5);
/* Alarms */
-static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct it87_data *data = it87_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static DEVICE_ATTR_RO(alarms);
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -1877,16 +1917,16 @@
static SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO, show_beep, NULL, 2);
static SENSOR_DEVICE_ATTR(temp3_beep, S_IRUGO, show_beep, NULL, 2);
-static ssize_t show_vrm_reg(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct it87_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", data->vrm);
}
-static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct it87_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -1898,16 +1938,16 @@
return count;
}
-static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
+static DEVICE_ATTR_RW(vrm);
-static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct it87_data *data = it87_update_device(dev);
return sprintf(buf, "%ld\n", (long)vid_from_reg(data->vid, data->vrm));
}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
static ssize_t show_label(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -1916,17 +1956,21 @@
"+5V",
"5VSB",
"Vbat",
+ "AVCC",
};
static const char * const labels_it8721[] = {
"+3.3V",
"3VSB",
"Vbat",
+ "+3.3V",
};
struct it87_data *data = dev_get_drvdata(dev);
int nr = to_sensor_dev_attr(attr)->index;
const char *label;
- if (has_12mv_adc(data) || has_10_9mv_adc(data))
+ if (has_vin3_5v(data) && nr == 0)
+ label = labels[0];
+ else if (has_12mv_adc(data) || has_10_9mv_adc(data))
label = labels_it8721[nr];
else
label = labels[nr];
@@ -1937,7 +1981,7 @@
static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 1);
static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_label, NULL, 2);
/* AVCC3 */
-static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL, 3);
static umode_t it87_in_is_visible(struct kobject *kobj,
struct attribute *attr, int index)
@@ -2386,6 +2430,9 @@
case IT8732F_DEVID:
sio_data->type = it8732;
break;
+ case IT8792E_DEVID:
+ sio_data->type = it8792;
+ break;
case IT8771E_DEVID:
sio_data->type = it8771;
break;
@@ -2414,6 +2461,9 @@
case IT8620E_DEVID:
sio_data->type = it8620;
break;
+ case IT8622E_DEVID:
+ sio_data->type = it8622;
+ break;
case IT8628E_DEVID:
sio_data->type = it8628;
break;
@@ -2457,8 +2507,10 @@
else
sio_data->skip_in |= BIT(9);
- if (!has_six_pwm(config))
+ if (!has_five_pwm(config))
sio_data->skip_pwm |= BIT(3) | BIT(4) | BIT(5);
+ else if (!has_six_pwm(config))
+ sio_data->skip_pwm |= BIT(5);
if (!has_vid(config))
sio_data->skip_vid = 1;
@@ -2587,7 +2639,7 @@
/* Check for pwm4 */
reg = superio_inb(sioaddr, IT87_SIO_GPIO4_REG);
- if (!(reg & BIT(2)))
+ if (reg & BIT(2))
sio_data->skip_pwm |= BIT(3);
/* Check for pwm2, fan2 */
@@ -2602,6 +2654,50 @@
sio_data->skip_fan |= BIT(5);
}
+ /* Check if AVCC is on VIN3 */
+ reg = superio_inb(sioaddr, IT87_SIO_PINX2_REG);
+ if (reg & BIT(0))
+ sio_data->internal |= BIT(0);
+ else
+ sio_data->skip_in |= BIT(9);
+
+ sio_data->beep_pin = superio_inb(sioaddr,
+ IT87_SIO_BEEP_PIN_REG) & 0x3f;
+ } else if (sio_data->type == it8622) {
+ int reg;
+
+ superio_select(sioaddr, GPIO);
+
+ /* Check for pwm4, fan4 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO1_REG);
+ if (reg & BIT(6))
+ sio_data->skip_fan |= BIT(3);
+ if (reg & BIT(5))
+ sio_data->skip_pwm |= BIT(3);
+
+ /* Check for pwm3, fan3, pwm5, fan5 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO3_REG);
+ if (reg & BIT(6))
+ sio_data->skip_pwm |= BIT(2);
+ if (reg & BIT(7))
+ sio_data->skip_fan |= BIT(2);
+ if (reg & BIT(3))
+ sio_data->skip_pwm |= BIT(4);
+ if (reg & BIT(1))
+ sio_data->skip_fan |= BIT(4);
+
+ /* Check for pwm2, fan2 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO5_REG);
+ if (reg & BIT(1))
+ sio_data->skip_pwm |= BIT(1);
+ if (reg & BIT(2))
+ sio_data->skip_fan |= BIT(1);
+
+ /* Check for AVCC */
+ reg = superio_inb(sioaddr, IT87_SIO_PINX2_REG);
+ if (!(reg & BIT(0)))
+ sio_data->skip_in |= BIT(9);
+
sio_data->beep_pin = superio_inb(sioaddr,
IT87_SIO_BEEP_PIN_REG) & 0x3f;
} else {
diff --git a/drivers/hwmon/jz4740-hwmon.c b/drivers/hwmon/jz4740-hwmon.c
index 0621ee1..2d40a2e 100644
--- a/drivers/hwmon/jz4740-hwmon.c
+++ b/drivers/hwmon/jz4740-hwmon.c
@@ -44,8 +44,8 @@
return IRQ_HANDLED;
}
-static ssize_t jz4740_hwmon_read_adcin(struct device *dev,
- struct device_attribute *dev_attr, char *buf)
+static ssize_t in0_input_show(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
{
struct jz4740_hwmon *hwmon = dev_get_drvdata(dev);
struct platform_device *pdev = hwmon->pdev;
@@ -79,7 +79,7 @@
return ret;
}
-static DEVICE_ATTR(in0_input, S_IRUGO, jz4740_hwmon_read_adcin, NULL);
+static DEVICE_ATTR_RO(in0_input);
static struct attribute *jz4740_attrs[] = {
&dev_attr_in0_input.attr,
diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c
index 9cdfde6..ce3b91f 100644
--- a/drivers/hwmon/k10temp.c
+++ b/drivers/hwmon/k10temp.c
@@ -72,8 +72,8 @@
mutex_unlock(&nb_smu_ind_mutex);
}
-static ssize_t show_temp(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
u32 regval;
struct pci_dev *pdev = dev_get_drvdata(dev);
@@ -88,8 +88,8 @@
return sprintf(buf, "%u\n", (regval >> 21) * 125);
}
-static ssize_t show_temp_max(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t temp1_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", 70 * 1000);
}
@@ -110,8 +110,8 @@
return sprintf(buf, "%d\n", value);
}
-static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
-static DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, NULL);
+static DEVICE_ATTR_RO(temp1_input);
+static DEVICE_ATTR_RO(temp1_max);
static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_crit, NULL, 1);
diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c
index 734d55d..5a632bc 100644
--- a/drivers/hwmon/k8temp.c
+++ b/drivers/hwmon/k8temp.c
@@ -100,7 +100,7 @@
* Sysfs stuff
*/
-static ssize_t show_name(struct device *dev, struct device_attribute
+static ssize_t name_show(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct k8temp_data *data = dev_get_drvdata(dev);
@@ -133,7 +133,7 @@
static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1);
static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 1, 0);
static SENSOR_DEVICE_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 1, 1);
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
static const struct pci_device_id k8temp_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) },
diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c
index 33bfdb4..2e19486 100644
--- a/drivers/hwmon/lm63.c
+++ b/drivers/hwmon/lm63.c
@@ -417,16 +417,16 @@
return count;
}
-static ssize_t show_pwm1_enable(struct device *dev,
+static ssize_t pwm1_enable_show(struct device *dev,
struct device_attribute *dummy, char *buf)
{
struct lm63_data *data = lm63_update_device(dev);
return sprintf(buf, "%d\n", data->config_fan & 0x20 ? 1 : 2);
}
-static ssize_t set_pwm1_enable(struct device *dev,
- struct device_attribute *dummy,
- const char *buf, size_t count)
+static ssize_t pwm1_enable_store(struct device *dev,
+ struct device_attribute *dummy,
+ const char *buf, size_t count)
{
struct lm63_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -600,7 +600,7 @@
* Hysteresis register holds a relative value, while we want to present
* an absolute to user-space
*/
-static ssize_t show_temp2_crit_hyst(struct device *dev,
+static ssize_t temp2_crit_hyst_show(struct device *dev,
struct device_attribute *dummy, char *buf)
{
struct lm63_data *data = lm63_update_device(dev);
@@ -624,9 +624,9 @@
* And now the other way around, user-space provides an absolute
* hysteresis value and we have to store a relative one
*/
-static ssize_t set_temp2_crit_hyst(struct device *dev,
- struct device_attribute *dummy,
- const char *buf, size_t count)
+static ssize_t temp2_crit_hyst_store(struct device *dev,
+ struct device_attribute *dummy,
+ const char *buf, size_t count)
{
struct lm63_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -670,7 +670,7 @@
data->update_interval = UPDATE_INTERVAL(data->max_convrate_hz, i);
}
-static ssize_t show_update_interval(struct device *dev,
+static ssize_t update_interval_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lm63_data *data = dev_get_drvdata(dev);
@@ -678,9 +678,9 @@
return sprintf(buf, "%u\n", data->update_interval);
}
-static ssize_t set_update_interval(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t update_interval_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct lm63_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -697,16 +697,17 @@
return count;
}
-static ssize_t show_type(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t temp2_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct lm63_data *data = dev_get_drvdata(dev);
return sprintf(buf, data->trutherm ? "1\n" : "2\n");
}
-static ssize_t set_type(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t temp2_type_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct lm63_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -731,7 +732,7 @@
return count;
}
-static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
+static ssize_t alarms_show(struct device *dev, struct device_attribute *dummy,
char *buf)
{
struct lm63_data *data = lm63_update_device(dev);
@@ -753,8 +754,7 @@
set_fan, 1);
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0);
-static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
- show_pwm1_enable, set_pwm1_enable);
+static DEVICE_ATTR_RW(pwm1_enable);
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
show_pwm1, set_pwm1, 1);
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO,
@@ -841,10 +841,9 @@
set_temp11, 3);
static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_remote_temp8,
set_temp8, 2);
-static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp2_crit_hyst,
- set_temp2_crit_hyst);
+static DEVICE_ATTR_RW(temp2_crit_hyst);
-static DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, set_type);
+static DEVICE_ATTR_RW(temp2_type);
/* Individual alarm files */
static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, show_alarm, NULL, 0);
@@ -854,10 +853,9 @@
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4);
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
/* Raw alarm file for compatibility */
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static DEVICE_ATTR_RO(alarms);
-static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval,
- set_update_interval);
+static DEVICE_ATTR_RW(update_interval);
static struct attribute *lm63_attributes[] = {
&sensor_dev_attr_pwm1.dev_attr.attr,
diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c
index 583f883..543556d 100644
--- a/drivers/hwmon/lm70.c
+++ b/drivers/hwmon/lm70.c
@@ -46,6 +46,7 @@
#define LM70_CHIP_TMP121 1 /* TI TMP121/TMP123 */
#define LM70_CHIP_LM71 2 /* NS LM71 */
#define LM70_CHIP_LM74 3 /* NS LM74 */
+#define LM70_CHIP_TMP122 4 /* TI TMP122/TMP124 */
struct lm70 {
struct spi_device *spi;
@@ -54,8 +55,8 @@
};
/* sysfs hook function */
-static ssize_t lm70_sense_temp(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct lm70 *p_lm70 = dev_get_drvdata(dev);
struct spi_device *spi = p_lm70->spi;
@@ -72,7 +73,8 @@
*/
status = spi_write_then_read(spi, NULL, 0, &rxbuf[0], 2);
if (status < 0) {
- pr_warn("spi_write_then_read failed with status %d\n", status);
+ dev_warn(dev, "spi_write_then_read failed with status %d\n",
+ status);
goto out;
}
raw = (rxbuf[0] << 8) + rxbuf[1];
@@ -91,7 +93,7 @@
* Celsius.
* So it's equivalent to multiplying by 0.25 * 1000 = 250.
*
- * LM74 and TMP121/TMP123:
+ * LM74 and TMP121/TMP122/TMP123/TMP124:
* 13 bits of 2's complement data, discard LSB 3 bits,
* resolution 0.0625 degrees celsius.
*
@@ -105,6 +107,7 @@
break;
case LM70_CHIP_TMP121:
+ case LM70_CHIP_TMP122:
case LM70_CHIP_LM74:
val = ((int)raw / 8) * 625 / 10;
break;
@@ -120,7 +123,7 @@
return status;
}
-static DEVICE_ATTR(temp1_input, S_IRUGO, lm70_sense_temp, NULL);
+static DEVICE_ATTR_RO(temp1_input);
static struct attribute *lm70_attrs[] = {
&dev_attr_temp1_input.attr,
@@ -142,6 +145,10 @@
.data = (void *) LM70_CHIP_TMP121,
},
{
+ .compatible = "ti,tmp122",
+ .data = (void *) LM70_CHIP_TMP122,
+ },
+ {
.compatible = "ti,lm71",
.data = (void *) LM70_CHIP_LM71,
},
@@ -190,6 +197,7 @@
static const struct spi_device_id lm70_ids[] = {
{ "lm70", LM70_CHIP_LM70 },
{ "tmp121", LM70_CHIP_TMP121 },
+ { "tmp122", LM70_CHIP_TMP122 },
{ "lm71", LM70_CHIP_LM71 },
{ "lm74", LM70_CHIP_LM74 },
{ },
diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c
index 539efe4..0cb7ff6 100644
--- a/drivers/hwmon/lm78.c
+++ b/drivers/hwmon/lm78.c
@@ -236,22 +236,23 @@
show_in_offset(6);
/* Temperature */
-static ssize_t show_temp(struct device *dev, struct device_attribute *da,
- char *buf)
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *da, char *buf)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
}
-static ssize_t show_temp_over(struct device *dev, struct device_attribute *da,
+static ssize_t temp1_max_show(struct device *dev, struct device_attribute *da,
char *buf)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
}
-static ssize_t set_temp_over(struct device *dev, struct device_attribute *da,
- const char *buf, size_t count)
+static ssize_t temp1_max_store(struct device *dev,
+ struct device_attribute *da, const char *buf,
+ size_t count)
{
struct lm78_data *data = dev_get_drvdata(dev);
long val;
@@ -268,15 +269,16 @@
return count;
}
-static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *da,
- char *buf)
+static ssize_t temp1_max_hyst_show(struct device *dev,
+ struct device_attribute *da, char *buf)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst));
}
-static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *da,
- const char *buf, size_t count)
+static ssize_t temp1_max_hyst_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
{
struct lm78_data *data = dev_get_drvdata(dev);
long val;
@@ -293,11 +295,9 @@
return count;
}
-static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
-static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
- show_temp_over, set_temp_over);
-static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
- show_temp_hyst, set_temp_hyst);
+static DEVICE_ATTR_RO(temp1_input);
+static DEVICE_ATTR_RW(temp1_max);
+static DEVICE_ATTR_RW(temp1_max_hyst);
/* 3 Fans */
static ssize_t show_fan(struct device *dev, struct device_attribute *da,
@@ -431,22 +431,22 @@
static SENSOR_DEVICE_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2);
/* VID */
-static ssize_t show_vid(struct device *dev, struct device_attribute *da,
- char *buf)
+static ssize_t cpu0_vid_show(struct device *dev, struct device_attribute *da,
+ char *buf)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", vid_from_reg(data->vid, 82));
}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
/* Alarms */
-static ssize_t show_alarms(struct device *dev, struct device_attribute *da,
+static ssize_t alarms_show(struct device *dev, struct device_attribute *da,
char *buf)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static DEVICE_ATTR_RO(alarms);
static ssize_t show_alarm(struct device *dev, struct device_attribute *da,
char *buf)
diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c
index 4bcd9b8..08e3945 100644
--- a/drivers/hwmon/lm80.c
+++ b/drivers/hwmon/lm80.c
@@ -432,7 +432,7 @@
return count;
}
-static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct lm80_data *data = lm80_update_device(dev);
@@ -505,7 +505,7 @@
set_temp, t_os_max);
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp,
set_temp, t_os_hyst);
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static DEVICE_ATTR_RO(alarms);
static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c
index 9e4d0e1..cbfd0bb 100644
--- a/drivers/hwmon/lm83.c
+++ b/drivers/hwmon/lm83.c
@@ -188,7 +188,7 @@
return count;
}
-static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
+static ssize_t alarms_show(struct device *dev, struct device_attribute *dummy,
char *buf)
{
struct lm83_data *data = lm83_update_device(dev);
@@ -236,7 +236,7 @@
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 13);
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 15);
/* Raw alarm file for compatibility */
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static DEVICE_ATTR_RO(alarms);
static struct attribute *lm83_attributes[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c
index 29c8136..691469f 100644
--- a/drivers/hwmon/lm85.c
+++ b/drivers/hwmon/lm85.c
@@ -604,8 +604,8 @@
/* vid, vrm, alarms */
-static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct lm85_data *data = lm85_update_device(dev);
int vid;
@@ -621,17 +621,17 @@
return sprintf(buf, "%d\n", vid);
}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
-static ssize_t show_vrm_reg(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct lm85_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%ld\n", (long) data->vrm);
}
-static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct lm85_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -648,16 +648,16 @@
return count;
}
-static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
+static DEVICE_ATTR_RW(vrm);
-static ssize_t show_alarms_reg(struct device *dev, struct device_attribute
- *attr, char *buf)
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct lm85_data *data = lm85_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
+static DEVICE_ATTR_RO(alarms);
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
char *buf)
diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
index 13cca36..e06faf9 100644
--- a/drivers/hwmon/lm87.c
+++ b/drivers/hwmon/lm87.c
@@ -445,23 +445,23 @@
set_temp(2);
set_temp(3);
-static ssize_t show_temp_crit_int(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t temp1_crit_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_int));
}
-static ssize_t show_temp_crit_ext(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t temp2_crit_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_ext));
}
-static DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit_int, NULL);
-static DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp_crit_ext, NULL);
-static DEVICE_ATTR(temp3_crit, S_IRUGO, show_temp_crit_ext, NULL);
+static DEVICE_ATTR_RO(temp1_crit);
+static DEVICE_ATTR_RO(temp2_crit);
+static DEVICE_ATTR(temp3_crit, S_IRUGO, temp2_crit_show, NULL);
static ssize_t show_fan_input(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -586,30 +586,30 @@
set_fan(1);
set_fan(2);
-static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
return sprintf(buf, "%d\n", data->alarms);
}
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static DEVICE_ATTR_RO(alarms);
-static ssize_t show_vid(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
-static ssize_t show_vrm(struct device *dev, struct device_attribute *attr,
+static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct lm87_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", data->vrm);
}
-static ssize_t set_vrm(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct lm87_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -625,16 +625,17 @@
data->vrm = val;
return count;
}
-static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
+static DEVICE_ATTR_RW(vrm);
-static ssize_t show_aout(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t aout_output_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout));
}
-static ssize_t set_aout(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t aout_output_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct i2c_client *client = dev_get_drvdata(dev);
struct lm87_data *data = i2c_get_clientdata(client);
@@ -651,7 +652,7 @@
mutex_unlock(&data->update_lock);
return count;
}
-static DEVICE_ATTR(aout_output, S_IRUGO | S_IWUSR, show_aout, set_aout);
+static DEVICE_ATTR_RW(aout_output);
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
char *buf)
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 841f242..aff5297 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -830,7 +830,7 @@
}
/* pec used for ADM1032 only */
-static ssize_t show_pec(struct device *dev, struct device_attribute *dummy,
+static ssize_t pec_show(struct device *dev, struct device_attribute *dummy,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -838,8 +838,8 @@
return sprintf(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC));
}
-static ssize_t set_pec(struct device *dev, struct device_attribute *dummy,
- const char *buf, size_t count)
+static ssize_t pec_store(struct device *dev, struct device_attribute *dummy,
+ const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
long val;
@@ -863,7 +863,7 @@
return count;
}
-static DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec);
+static DEVICE_ATTR_RW(pec);
static int lm90_get_temp11(struct lm90_data *data, int index)
{
diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c
index cfaf70b..2a91974 100644
--- a/drivers/hwmon/lm92.c
+++ b/drivers/hwmon/lm92.c
@@ -181,8 +181,8 @@
- TEMP_FROM_REG(data->temp[t_hyst]));
}
-static ssize_t show_temp_min_hyst(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t temp1_min_hyst_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct lm92_data *data = lm92_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[t_min])
@@ -213,7 +213,7 @@
return count;
}
-static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct lm92_data *data = lm92_update_device(dev);
@@ -235,11 +235,11 @@
set_temp_hyst, t_crit);
static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp, set_temp,
t_min);
-static DEVICE_ATTR(temp1_min_hyst, S_IRUGO, show_temp_min_hyst, NULL);
+static DEVICE_ATTR_RO(temp1_min_hyst);
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp, set_temp,
t_max);
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp_hyst, NULL, t_max);
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static DEVICE_ATTR_RO(alarms);
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 2);
static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 1);
diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c
index 90bb048..77a0a83 100644
--- a/drivers/hwmon/lm93.c
+++ b/drivers/hwmon/lm93.c
@@ -2156,7 +2156,7 @@
show_pwm_auto_spinup_time,
store_pwm_auto_spinup_time, 1);
-static ssize_t show_pwm_auto_prochot_ramp(struct device *dev,
+static ssize_t pwm_auto_prochot_ramp_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lm93_data *data = lm93_update_device(dev);
@@ -2164,7 +2164,7 @@
LM93_RAMP_FROM_REG(data->pwm_ramp_ctl >> 4 & 0x0f));
}
-static ssize_t store_pwm_auto_prochot_ramp(struct device *dev,
+static ssize_t pwm_auto_prochot_ramp_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -2186,11 +2186,9 @@
return count;
}
-static DEVICE_ATTR(pwm_auto_prochot_ramp, S_IRUGO | S_IWUSR,
- show_pwm_auto_prochot_ramp,
- store_pwm_auto_prochot_ramp);
+static DEVICE_ATTR_RW(pwm_auto_prochot_ramp);
-static ssize_t show_pwm_auto_vrdhot_ramp(struct device *dev,
+static ssize_t pwm_auto_vrdhot_ramp_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lm93_data *data = lm93_update_device(dev);
@@ -2198,7 +2196,7 @@
LM93_RAMP_FROM_REG(data->pwm_ramp_ctl & 0x0f));
}
-static ssize_t store_pwm_auto_vrdhot_ramp(struct device *dev,
+static ssize_t pwm_auto_vrdhot_ramp_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -2220,9 +2218,7 @@
return 0;
}
-static DEVICE_ATTR(pwm_auto_vrdhot_ramp, S_IRUGO | S_IWUSR,
- show_pwm_auto_vrdhot_ramp,
- store_pwm_auto_vrdhot_ramp);
+static DEVICE_ATTR_RW(pwm_auto_vrdhot_ramp);
static ssize_t show_vid(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -2378,7 +2374,7 @@
static SENSOR_DEVICE_ATTR(prochot2_interval, S_IWUSR | S_IRUGO,
show_prochot_interval, store_prochot_interval, 1);
-static ssize_t show_prochot_override_duty_cycle(struct device *dev,
+static ssize_t prochot_override_duty_cycle_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
@@ -2386,7 +2382,7 @@
return sprintf(buf, "%d\n", data->prochot_override & 0x0f);
}
-static ssize_t store_prochot_override_duty_cycle(struct device *dev,
+static ssize_t prochot_override_duty_cycle_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -2408,18 +2404,16 @@
return count;
}
-static DEVICE_ATTR(prochot_override_duty_cycle, S_IRUGO | S_IWUSR,
- show_prochot_override_duty_cycle,
- store_prochot_override_duty_cycle);
+static DEVICE_ATTR_RW(prochot_override_duty_cycle);
-static ssize_t show_prochot_short(struct device *dev,
+static ssize_t prochot_short_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lm93_data *data = lm93_update_device(dev);
return sprintf(buf, "%d\n", (data->config & 0x10) ? 1 : 0);
}
-static ssize_t store_prochot_short(struct device *dev,
+static ssize_t prochot_short_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -2442,8 +2436,7 @@
return count;
}
-static DEVICE_ATTR(prochot_short, S_IRUGO | S_IWUSR,
- show_prochot_short, store_prochot_short);
+static DEVICE_ATTR_RW(prochot_short);
static ssize_t show_vrdhot(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -2457,23 +2450,23 @@
static SENSOR_DEVICE_ATTR(vrdhot1, S_IRUGO, show_vrdhot, NULL, 0);
static SENSOR_DEVICE_ATTR(vrdhot2, S_IRUGO, show_vrdhot, NULL, 1);
-static ssize_t show_gpio(struct device *dev, struct device_attribute *attr,
+static ssize_t gpio_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct lm93_data *data = lm93_update_device(dev);
return sprintf(buf, "%d\n", LM93_GPI_FROM_REG(data->gpi));
}
-static DEVICE_ATTR(gpio, S_IRUGO, show_gpio, NULL);
+static DEVICE_ATTR_RO(gpio);
-static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct lm93_data *data = lm93_update_device(dev);
return sprintf(buf, "%d\n", LM93_ALARMS_FROM_REG(data->block1));
}
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static DEVICE_ATTR_RO(alarms);
static struct attribute *lm93_attrs[] = {
&sensor_dev_attr_in1_input.dev_attr.attr,
diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c
index 8796de3..c7fcc9e 100644
--- a/drivers/hwmon/lm95234.c
+++ b/drivers/hwmon/lm95234.c
@@ -450,8 +450,8 @@
return count;
}
-static ssize_t show_interval(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t update_interval_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct lm95234_data *data = dev_get_drvdata(dev);
int ret = lm95234_update_device(data);
@@ -463,8 +463,9 @@
DIV_ROUND_CLOSEST(data->interval * 1000, HZ));
}
-static ssize_t set_interval(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t update_interval_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct lm95234_data *data = dev_get_drvdata(dev);
int ret = lm95234_update_device(data);
@@ -566,8 +567,7 @@
static SENSOR_DEVICE_ATTR(temp5_offset, S_IWUSR | S_IRUGO, show_offset,
set_offset, 3);
-static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval,
- set_interval);
+static DEVICE_ATTR_RW(update_interval);
static struct attribute *lm95234_common_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
diff --git a/drivers/hwmon/ltc4151.c b/drivers/hwmon/ltc4151.c
index 8445c9f..b904cb5 100644
--- a/drivers/hwmon/ltc4151.c
+++ b/drivers/hwmon/ltc4151.c
@@ -215,6 +215,7 @@
{ .compatible = "lltc,ltc4151" },
{},
};
+MODULE_DEVICE_TABLE(of, ltc4151_match);
/* This is the driver that will be inserted */
static struct i2c_driver ltc4151_driver = {
diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c
index 303d0c9..8ddd4d6 100644
--- a/drivers/hwmon/max1111.c
+++ b/drivers/hwmon/max1111.c
@@ -98,7 +98,7 @@
* likely to be used by hwmon applications to distinguish between
* different devices, explicitly add a name attribute here.
*/
-static ssize_t show_name(struct device *dev,
+static ssize_t name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
@@ -125,7 +125,7 @@
#define MAX1111_ADC_ATTR(_id) \
SENSOR_DEVICE_ATTR(in##_id##_input, S_IRUGO, show_adc, NULL, _id)
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
static MAX1111_ADC_ATTR(0);
static MAX1111_ADC_ATTR(1);
static MAX1111_ADC_ATTR(2);
diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c
index eda9cf5..a182789 100644
--- a/drivers/hwmon/max1619.c
+++ b/drivers/hwmon/max1619.c
@@ -173,7 +173,7 @@
return count;
}
-static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct max1619_data *data = max1619_update_device(dev);
@@ -199,7 +199,7 @@
static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp,
set_temp, t_hyst2);
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static DEVICE_ATTR_RO(alarms);
static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 2);
static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3);
diff --git a/drivers/hwmon/max197.c b/drivers/hwmon/max197.c
index 0762856..638567f 100644
--- a/drivers/hwmon/max197.c
+++ b/drivers/hwmon/max197.c
@@ -207,8 +207,8 @@
return ret;
}
-static ssize_t max197_show_name(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t name_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
return sprintf(buf, "%s\n", pdev->name);
@@ -231,7 +231,7 @@
&sensor_dev_attr_in##chan##_max.dev_attr.attr, \
&sensor_dev_attr_in##chan##_min.dev_attr.attr
-static DEVICE_ATTR(name, S_IRUGO, max197_show_name, NULL);
+static DEVICE_ATTR_RO(name);
MAX197_SENSOR_DEVICE_ATTR_CH(0);
MAX197_SENSOR_DEVICE_ATTR_CH(1);
diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c
index a993b44..65be4b1 100644
--- a/drivers/hwmon/max6650.c
+++ b/drivers/hwmon/max6650.c
@@ -270,8 +270,8 @@
* controlled.
*/
-static ssize_t get_target(struct device *dev, struct device_attribute *devattr,
- char *buf)
+static ssize_t fan1_target_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
{
struct max6650_data *data = max6650_update_device(dev);
int kscale, ktach, rpm;
@@ -318,8 +318,9 @@
data->speed);
}
-static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
+static ssize_t fan1_target_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
{
struct max6650_data *data = dev_get_drvdata(dev);
unsigned long rpm;
@@ -350,8 +351,8 @@
* back exactly the value you have set.
*/
-static ssize_t get_pwm(struct device *dev, struct device_attribute *devattr,
- char *buf)
+static ssize_t pwm1_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
{
int pwm;
struct max6650_data *data = max6650_update_device(dev);
@@ -371,8 +372,9 @@
return sprintf(buf, "%d\n", pwm);
}
-static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
+static ssize_t pwm1_store(struct device *dev,
+ struct device_attribute *devattr, const char *buf,
+ size_t count)
{
struct max6650_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -406,8 +408,8 @@
* 2 = Closed loop, RPM for all fans regulated by fan1 tachometer
* 3 = Fan off
*/
-static ssize_t get_enable(struct device *dev, struct device_attribute *devattr,
- char *buf)
+static ssize_t pwm1_enable_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
{
struct max6650_data *data = max6650_update_device(dev);
int mode = (data->config & MAX6650_CFG_MODE_MASK) >> 4;
@@ -416,8 +418,9 @@
return sprintf(buf, "%d\n", sysfs_modes[mode]);
}
-static ssize_t set_enable(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
+static ssize_t pwm1_enable_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
{
struct max6650_data *data = dev_get_drvdata(dev);
unsigned long mode;
@@ -458,16 +461,17 @@
* defined for that. See the data sheet for details.
*/
-static ssize_t get_div(struct device *dev, struct device_attribute *devattr,
- char *buf)
+static ssize_t fan1_div_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
{
struct max6650_data *data = max6650_update_device(dev);
return sprintf(buf, "%d\n", DIV_FROM_REG(data->count));
}
-static ssize_t set_div(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
+static ssize_t fan1_div_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
{
struct max6650_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -534,10 +538,10 @@
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1);
static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2);
static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, get_fan, NULL, 3);
-static DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, get_target, set_target);
-static DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, get_div, set_div);
-static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, get_enable, set_enable);
-static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm);
+static DEVICE_ATTR_RW(fan1_target);
+static DEVICE_ATTR_RW(fan1_div);
+static DEVICE_ATTR_RW(pwm1_enable);
+static DEVICE_ATTR_RW(pwm1);
static SENSOR_DEVICE_ATTR(fan1_max_alarm, S_IRUGO, get_alarm, NULL,
MAX6650_ALRM_MAX);
static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, get_alarm, NULL,
diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c
index 0c02f40..960a1db 100644
--- a/drivers/hwmon/mc13783-adc.c
+++ b/drivers/hwmon/mc13783-adc.c
@@ -40,8 +40,8 @@
char name[PLATFORM_NAME_SIZE];
};
-static ssize_t mc13783_adc_show_name(struct device *dev, struct device_attribute
- *devattr, char *buf)
+static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
{
struct mc13783_adc_priv *priv = dev_get_drvdata(dev);
@@ -111,7 +111,7 @@
return sprintf(buf, "%u\n", val);
}
-static DEVICE_ATTR(name, S_IRUGO, mc13783_adc_show_name, NULL);
+static DEVICE_ATTR_RO(name);
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, mc13783_adc_read_bp, NULL, 2);
static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, mc13783_adc_read_gp, NULL, 5);
static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, mc13783_adc_read_gp, NULL, 6);
diff --git a/drivers/hwmon/mcp3021.c b/drivers/hwmon/mcp3021.c
index 1929734..de886f8 100644
--- a/drivers/hwmon/mcp3021.c
+++ b/drivers/hwmon/mcp3021.c
@@ -86,8 +86,8 @@
return DIV_ROUND_CLOSEST(data->vdd * val, 1 << data->output_res);
}
-static ssize_t show_in_input(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t in0_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct mcp3021_data *data = i2c_get_clientdata(client);
@@ -102,7 +102,7 @@
return sprintf(buf, "%d\n", in_input);
}
-static DEVICE_ATTR(in0_input, 0444, show_in_input, NULL);
+static DEVICE_ATTR_RO(in0_input);
static int mcp3021_probe(struct i2c_client *client,
const struct i2c_device_id *id)
diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c
index 559c596..8b0bc4f 100644
--- a/drivers/hwmon/nct6683.c
+++ b/drivers/hwmon/nct6683.c
@@ -979,7 +979,7 @@
};
static ssize_t
-show_global_beep(struct device *dev, struct device_attribute *attr, char *buf)
+beep_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct nct6683_data *data = dev_get_drvdata(dev);
int ret;
@@ -1004,7 +1004,7 @@
}
static ssize_t
-store_global_beep(struct device *dev, struct device_attribute *attr,
+beep_enable_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct nct6683_data *data = dev_get_drvdata(dev);
@@ -1039,7 +1039,8 @@
/* Case open detection */
static ssize_t
-show_caseopen(struct device *dev, struct device_attribute *attr, char *buf)
+intrusion0_alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct nct6683_data *data = dev_get_drvdata(dev);
int ret;
@@ -1064,8 +1065,8 @@
}
static ssize_t
-clear_caseopen(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+intrusion0_alarm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct nct6683_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -1102,10 +1103,8 @@
return count;
}
-static DEVICE_ATTR(intrusion0_alarm, S_IWUSR | S_IRUGO, show_caseopen,
- clear_caseopen);
-static DEVICE_ATTR(beep_enable, S_IWUSR | S_IRUGO, show_global_beep,
- store_global_beep);
+static DEVICE_ATTR_RW(intrusion0_alarm);
+static DEVICE_ATTR_RW(beep_enable);
static struct attribute *nct6683_attributes_other[] = {
&dev_attr_intrusion0_alarm.attr,
diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c
index ce75dd4..2458b40 100644
--- a/drivers/hwmon/nct6775.c
+++ b/drivers/hwmon/nct6775.c
@@ -3127,14 +3127,14 @@
};
static ssize_t
-show_vid(struct device *dev, struct device_attribute *attr, char *buf)
+cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct nct6775_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
/* Case open detection */
diff --git a/drivers/hwmon/nsa320-hwmon.c b/drivers/hwmon/nsa320-hwmon.c
index 0517a2657..5a16109 100644
--- a/drivers/hwmon/nsa320-hwmon.c
+++ b/drivers/hwmon/nsa320-hwmon.c
@@ -122,8 +122,8 @@
return sprintf(buf, "%s\n", nsa320_input_names[channel]);
}
-static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
s32 mcu_data = nsa320_hwmon_update(dev);
@@ -133,8 +133,8 @@
return sprintf(buf, "%d\n", (mcu_data & 0xffff) * 100);
}
-static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t fan1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
s32 mcu_data = nsa320_hwmon_update(dev);
@@ -145,9 +145,9 @@
}
static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, NSA320_TEMP);
-static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
+static DEVICE_ATTR_RO(temp1_input);
static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, show_label, NULL, NSA320_FAN);
-static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL);
+static DEVICE_ATTR_RO(fan1_input);
static struct attribute *nsa320_attrs[] = {
&sensor_dev_attr_temp1_label.dev_attr.attr,
diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c
index d50fbf9..7e36977 100644
--- a/drivers/hwmon/pc87360.c
+++ b/drivers/hwmon/pc87360.c
@@ -589,22 +589,22 @@
&in_min_alarm[X].dev_attr.attr, \
&in_max_alarm[X].dev_attr.attr
-static ssize_t show_vid(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct pc87360_data *data = pc87360_update_device(dev);
return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
-static ssize_t show_vrm(struct device *dev, struct device_attribute *attr,
+static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct pc87360_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", data->vrm);
}
-static ssize_t set_vrm(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct pc87360_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -620,15 +620,15 @@
data->vrm = val;
return count;
}
-static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
+static DEVICE_ATTR_RW(vrm);
-static ssize_t show_in_alarms(struct device *dev,
+static ssize_t alarms_in_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct pc87360_data *data = pc87360_update_device(dev);
return sprintf(buf, "%u\n", data->in_alarms);
}
-static DEVICE_ATTR(alarms_in, S_IRUGO, show_in_alarms, NULL);
+static DEVICE_ATTR_RO(alarms_in);
static struct attribute *pc8736x_vin_attr_array[] = {
VIN_UNIT_ATTRS(0),
@@ -1006,14 +1006,14 @@
show_temp_crit, set_temp_crit, 2),
};
-static ssize_t show_temp_alarms(struct device *dev,
+static ssize_t alarms_temp_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct pc87360_data *data = pc87360_update_device(dev);
return sprintf(buf, "%u\n", data->temp_alarms);
}
-static DEVICE_ATTR(alarms_temp, S_IRUGO, show_temp_alarms, NULL);
+static DEVICE_ATTR_RO(alarms_temp);
/*
* show_temp_min/max_alarm() reads data from the per-channel status
@@ -1106,14 +1106,14 @@
{ .attrs = pc8736x_temp_attr[2] }
};
-static ssize_t show_name(struct device *dev,
+static ssize_t name_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct pc87360_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", data->name);
}
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
/*
* Device detection, registration and update
diff --git a/drivers/hwmon/pc87427.c b/drivers/hwmon/pc87427.c
index cb9fdd3..dc5a9d5 100644
--- a/drivers/hwmon/pc87427.c
+++ b/drivers/hwmon/pc87427.c
@@ -943,14 +943,14 @@
{ .attrs = pc87427_attributes_temp[5] },
};
-static ssize_t show_name(struct device *dev, struct device_attribute
+static ssize_t name_show(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", data->name);
}
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
/*
diff --git a/drivers/hwmon/pcf8591.c b/drivers/hwmon/pcf8591.c
index 5740888..60e25c8 100644
--- a/drivers/hwmon/pcf8591.c
+++ b/drivers/hwmon/pcf8591.c
@@ -103,16 +103,16 @@
show_in_channel(2);
show_in_channel(3);
-static ssize_t show_out0_ouput(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t out0_output_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev));
return sprintf(buf, "%d\n", data->aout * 10);
}
-static ssize_t set_out0_output(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t out0_output_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
unsigned long val;
struct i2c_client *client = to_i2c_client(dev);
@@ -132,19 +132,18 @@
return count;
}
-static DEVICE_ATTR(out0_output, S_IWUSR | S_IRUGO,
- show_out0_ouput, set_out0_output);
+static DEVICE_ATTR_RW(out0_output);
-static ssize_t show_out0_enable(struct device *dev,
+static ssize_t out0_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev));
return sprintf(buf, "%u\n", !(!(data->control & PCF8591_CONTROL_AOEF)));
}
-static ssize_t set_out0_enable(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t out0_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct pcf8591_data *data = i2c_get_clientdata(client);
@@ -165,8 +164,7 @@
return count;
}
-static DEVICE_ATTR(out0_enable, S_IWUSR | S_IRUGO,
- show_out0_enable, set_out0_enable);
+static DEVICE_ATTR_RW(out0_enable);
static struct attribute *pcf8591_attributes[] = {
&dev_attr_out0_enable.attr,
diff --git a/drivers/hwmon/sch5627.c b/drivers/hwmon/sch5627.c
index 19f85c0..91544f2 100644
--- a/drivers/hwmon/sch5627.c
+++ b/drivers/hwmon/sch5627.c
@@ -205,7 +205,7 @@
return 5400540 / reg;
}
-static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME);
@@ -326,7 +326,7 @@
SCH5627_IN_LABELS[attr->index]);
}
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c
index 68c350c..bda3d52 100644
--- a/drivers/hwmon/sch56xx-common.c
+++ b/drivers/hwmon/sch56xx-common.c
@@ -28,7 +28,6 @@
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/watchdog.h>
-#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include "sch56xx-common.h"
diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c
index a2fdbb7..e4d642b 100644
--- a/drivers/hwmon/sht15.c
+++ b/drivers/hwmon/sht15.c
@@ -34,6 +34,7 @@
#include <linux/slab.h>
#include <linux/atomic.h>
#include <linux/bitrev.h>
+#include <linux/of_gpio.h>
/* Commands */
#define SHT15_MEASURE_TEMP 0x03
@@ -769,7 +770,7 @@
return ret ? ret : sprintf(buf, "%d\n", sht15_calc_humid(data));
}
-static ssize_t show_name(struct device *dev,
+static ssize_t name_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
@@ -787,7 +788,7 @@
SHT15_STATUS_LOW_BATTERY);
static SENSOR_DEVICE_ATTR(heater_enable, S_IRUGO | S_IWUSR, sht15_show_status,
sht15_store_heater, SHT15_STATUS_HEATER);
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
static struct attribute *sht15_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_humidity1_input.dev_attr.attr,
@@ -911,6 +912,54 @@
return NOTIFY_OK;
}
+#ifdef CONFIG_OF
+static const struct of_device_id sht15_dt_match[] = {
+ { .compatible = "sensirion,sht15" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sht15_dt_match);
+
+/*
+ * This function returns NULL if pdev isn't a device instatiated by dt,
+ * a pointer to pdata if it could successfully get all information
+ * from dt or a negative ERR_PTR() on error.
+ */
+static struct sht15_platform_data *sht15_probe_dt(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct sht15_platform_data *pdata;
+
+ /* no device tree device */
+ if (!np)
+ return NULL;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->gpio_data = of_get_named_gpio(np, "data-gpios", 0);
+ if (pdata->gpio_data < 0) {
+ if (pdata->gpio_data != -EPROBE_DEFER)
+ dev_err(dev, "data-gpios not found\n");
+ return ERR_PTR(pdata->gpio_data);
+ }
+
+ pdata->gpio_sck = of_get_named_gpio(np, "clk-gpios", 0);
+ if (pdata->gpio_sck < 0) {
+ if (pdata->gpio_sck != -EPROBE_DEFER)
+ dev_err(dev, "clk-gpios not found\n");
+ return ERR_PTR(pdata->gpio_sck);
+ }
+
+ return pdata;
+}
+#else
+static inline struct sht15_platform_data *sht15_probe_dt(struct device *dev)
+{
+ return NULL;
+}
+#endif
+
static int sht15_probe(struct platform_device *pdev)
{
int ret;
@@ -928,11 +977,17 @@
data->dev = &pdev->dev;
init_waitqueue_head(&data->wait_queue);
- if (dev_get_platdata(&pdev->dev) == NULL) {
- dev_err(&pdev->dev, "no platform data supplied\n");
- return -EINVAL;
+ data->pdata = sht15_probe_dt(&pdev->dev);
+ if (IS_ERR(data->pdata))
+ return PTR_ERR(data->pdata);
+ if (data->pdata == NULL) {
+ data->pdata = dev_get_platdata(&pdev->dev);
+ if (data->pdata == NULL) {
+ dev_err(&pdev->dev, "no platform data supplied\n");
+ return -EINVAL;
+ }
}
- data->pdata = dev_get_platdata(&pdev->dev);
+
data->supply_uv = data->pdata->supply_mv * 1000;
if (data->pdata->checksum)
data->checksumming = true;
@@ -1075,6 +1130,7 @@
static struct platform_driver sht15_driver = {
.driver = {
.name = "sht15",
+ .of_match_table = of_match_ptr(sht15_dt_match),
},
.probe = sht15_probe,
.remove = sht15_remove,
diff --git a/drivers/hwmon/sht21.c b/drivers/hwmon/sht21.c
index 84cdb1c..06706d2 100644
--- a/drivers/hwmon/sht21.c
+++ b/drivers/hwmon/sht21.c
@@ -34,23 +34,29 @@
/* I2C command bytes */
#define SHT21_TRIG_T_MEASUREMENT_HM 0xe3
#define SHT21_TRIG_RH_MEASUREMENT_HM 0xe5
+#define SHT21_READ_SNB_CMD1 0xFA
+#define SHT21_READ_SNB_CMD2 0x0F
+#define SHT21_READ_SNAC_CMD1 0xFC
+#define SHT21_READ_SNAC_CMD2 0xC9
/**
* struct sht21 - SHT21 device specific data
* @hwmon_dev: device registered with hwmon
* @lock: mutex to protect measurement values
- * @valid: only 0 before first measurement is taken
* @last_update: time of last update (jiffies)
* @temperature: cached temperature measurement value
* @humidity: cached humidity measurement value
+ * @valid: only 0 before first measurement is taken
+ * @eic: cached electronic identification code text
*/
struct sht21 {
struct i2c_client *client;
struct mutex lock;
- char valid;
unsigned long last_update;
int temperature;
int humidity;
+ char valid;
+ char eic[18];
};
/**
@@ -165,15 +171,97 @@
return sprintf(buf, "%d\n", sht21->humidity);
}
+static ssize_t eic_read(struct sht21 *sht21)
+{
+ struct i2c_client *client = sht21->client;
+ u8 tx[2];
+ u8 rx[8];
+ u8 eic[8];
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = tx,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 8,
+ .buf = rx,
+ },
+ };
+ int ret;
+
+ tx[0] = SHT21_READ_SNB_CMD1;
+ tx[1] = SHT21_READ_SNB_CMD2;
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if (ret < 0)
+ goto out;
+ eic[2] = rx[0];
+ eic[3] = rx[2];
+ eic[4] = rx[4];
+ eic[5] = rx[6];
+
+ tx[0] = SHT21_READ_SNAC_CMD1;
+ tx[1] = SHT21_READ_SNAC_CMD2;
+ msgs[1].len = 6;
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if (ret < 0)
+ goto out;
+ eic[0] = rx[3];
+ eic[1] = rx[4];
+ eic[6] = rx[0];
+ eic[7] = rx[1];
+
+ ret = snprintf(sht21->eic, sizeof(sht21->eic),
+ "%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ eic[0], eic[1], eic[2], eic[3],
+ eic[4], eic[5], eic[6], eic[7]);
+out:
+ if (ret < 0)
+ sht21->eic[0] = 0;
+
+ return ret;
+}
+
+/**
+ * eic_show() - show Electronic Identification Code in sysfs
+ * @dev: device
+ * @attr: device attribute
+ * @buf: sysfs buffer (PAGE_SIZE) where EIC is written
+ *
+ * Will be called on read access to eic sysfs attribute.
+ * Returns number of bytes written into buffer, negative errno on error.
+ */
+static ssize_t eic_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sht21 *sht21 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = sizeof(sht21->eic) - 1;
+ mutex_lock(&sht21->lock);
+ if (!sht21->eic[0])
+ ret = eic_read(sht21);
+ if (ret > 0)
+ memcpy(buf, sht21->eic, ret);
+ mutex_unlock(&sht21->lock);
+ return ret;
+}
+
/* sysfs attributes */
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, sht21_show_temperature,
NULL, 0);
static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO, sht21_show_humidity,
NULL, 0);
+static DEVICE_ATTR_RO(eic);
static struct attribute *sht21_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_humidity1_input.dev_attr.attr,
+ &dev_attr_eic.attr,
NULL
};
diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c
index 45a028f..6d789aa 100644
--- a/drivers/hwmon/sis5595.c
+++ b/drivers/hwmon/sis5595.c
@@ -304,22 +304,23 @@
show_in_offset(4);
/* Temperature */
-static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
}
-static ssize_t show_temp_over(struct device *dev, struct device_attribute *attr,
+static ssize_t temp1_max_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
}
-static ssize_t set_temp_over(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t temp1_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
{
struct sis5595_data *data = dev_get_drvdata(dev);
long val;
@@ -336,15 +337,16 @@
return count;
}
-static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t temp1_max_hyst_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst));
}
-static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t temp1_max_hyst_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct sis5595_data *data = dev_get_drvdata(dev);
long val;
@@ -361,11 +363,9 @@
return count;
}
-static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
-static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
- show_temp_over, set_temp_over);
-static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
- show_temp_hyst, set_temp_hyst);
+static DEVICE_ATTR_RO(temp1_input);
+static DEVICE_ATTR_RW(temp1_max);
+static DEVICE_ATTR_RW(temp1_max_hyst);
/* 2 Fans */
static ssize_t show_fan(struct device *dev, struct device_attribute *da,
@@ -492,13 +492,13 @@
show_fan_offset(2);
/* Alarms */
-static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", data->alarms);
}
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static DEVICE_ATTR_RO(alarms);
static ssize_t show_alarm(struct device *dev, struct device_attribute *da,
char *buf)
@@ -516,13 +516,13 @@
static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7);
static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 15);
-static ssize_t show_name(struct device *dev, struct device_attribute *attr,
+static ssize_t name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct sis5595_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", data->name);
}
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
static struct attribute *sis5595_attributes[] = {
&sensor_dev_attr_in0_input.dev_attr.attr,
diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c
index 5d32318..c7b6a42 100644
--- a/drivers/hwmon/smsc47m1.c
+++ b/drivers/hwmon/smsc47m1.c
@@ -264,8 +264,8 @@
return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[attr->index]));
}
-static ssize_t get_alarms(struct device *dev, struct device_attribute
- *devattr, char *buf)
+static ssize_t alarms_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
{
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
return sprintf(buf, "%d\n", data->alarms);
@@ -440,16 +440,16 @@
fan_present(2);
fan_present(3);
-static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
+static DEVICE_ATTR_RO(alarms);
-static ssize_t show_name(struct device *dev, struct device_attribute
+static ssize_t name_show(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct smsc47m1_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", data->name);
}
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
static struct attribute *smsc47m1_attributes_fan1[] = {
&sensor_dev_attr_fan1_input.dev_attr.attr,
diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c
index 15650f2..6989408 100644
--- a/drivers/hwmon/smsc47m192.c
+++ b/drivers/hwmon/smsc47m192.c
@@ -400,23 +400,23 @@
show_temp_index(3)
/* VID */
-static ssize_t show_vid(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct smsc47m192_data *data = smsc47m192_update_device(dev);
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
-static ssize_t show_vrm(struct device *dev, struct device_attribute *attr,
+static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct smsc47m192_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", data->vrm);
}
-static ssize_t set_vrm(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct smsc47m192_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -431,7 +431,7 @@
data->vrm = val;
return count;
}
-static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
+static DEVICE_ATTR_RW(vrm);
/* Alarms */
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
diff --git a/drivers/hwmon/stts751.c b/drivers/hwmon/stts751.c
new file mode 100644
index 0000000..5545068
--- /dev/null
+++ b/drivers/hwmon/stts751.c
@@ -0,0 +1,834 @@
+/*
+ * STTS751 sensor driver
+ *
+ * Copyright (C) 2016-2017 Istituto Italiano di Tecnologia - RBCS - EDL
+ * Robotics, Brain and Cognitive Sciences department
+ * Electronic Design Laboratory
+ *
+ * Written by Andrea Merello <andrea.merello@gmail.com>
+ *
+ * Based on LM95241 driver and LM90 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.
+ *
+ * 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/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/util_macros.h>
+
+#define DEVNAME "stts751"
+
+static const unsigned short normal_i2c[] = {
+ 0x48, 0x49, 0x38, 0x39, /* STTS751-0 */
+ 0x4A, 0x4B, 0x3A, 0x3B, /* STTS751-1 */
+ I2C_CLIENT_END };
+
+#define STTS751_REG_TEMP_H 0x00
+#define STTS751_REG_STATUS 0x01
+#define STTS751_STATUS_TRIPT BIT(0)
+#define STTS751_STATUS_TRIPL BIT(5)
+#define STTS751_STATUS_TRIPH BIT(6)
+#define STTS751_REG_TEMP_L 0x02
+#define STTS751_REG_CONF 0x03
+#define STTS751_CONF_RES_MASK 0x0C
+#define STTS751_CONF_RES_SHIFT 2
+#define STTS751_CONF_EVENT_DIS BIT(7)
+#define STTS751_CONF_STOP BIT(6)
+#define STTS751_REG_RATE 0x04
+#define STTS751_REG_HLIM_H 0x05
+#define STTS751_REG_HLIM_L 0x06
+#define STTS751_REG_LLIM_H 0x07
+#define STTS751_REG_LLIM_L 0x08
+#define STTS751_REG_TLIM 0x20
+#define STTS751_REG_HYST 0x21
+#define STTS751_REG_SMBUS_TO 0x22
+
+#define STTS751_REG_PROD_ID 0xFD
+#define STTS751_REG_MAN_ID 0xFE
+#define STTS751_REG_REV_ID 0xFF
+
+#define STTS751_0_PROD_ID 0x00
+#define STTS751_1_PROD_ID 0x01
+#define ST_MAN_ID 0x53
+
+/*
+ * Possible update intervals are (in mS):
+ * 16000, 8000, 4000, 2000, 1000, 500, 250, 125, 62.5, 31.25
+ * However we are not going to complicate things too much and we stick to the
+ * approx value in mS.
+ */
+static const int stts751_intervals[] = {
+ 16000, 8000, 4000, 2000, 1000, 500, 250, 125, 63, 31
+};
+
+static const struct i2c_device_id stts751_id[] = {
+ { "stts751", 0 },
+ { }
+};
+
+struct stts751_priv {
+ struct device *dev;
+ struct i2c_client *client;
+ struct mutex access_lock;
+ u8 interval;
+ int res;
+ int event_max, event_min;
+ int therm;
+ int hyst;
+ bool smbus_timeout;
+ int temp;
+ unsigned long last_update, last_alert_update;
+ u8 config;
+ bool min_alert, max_alert, therm_trip;
+ bool data_valid, alert_valid;
+ bool notify_max, notify_min;
+};
+
+/*
+ * These functions converts temperature from HW format to integer format and
+ * vice-vers. They are (mostly) taken from lm90 driver. Unit is in mC.
+ */
+static int stts751_to_deg(s16 hw_val)
+{
+ return hw_val * 125 / 32;
+}
+
+static s32 stts751_to_hw(int val)
+{
+ return DIV_ROUND_CLOSEST(val, 125) * 32;
+}
+
+static int stts751_adjust_resolution(struct stts751_priv *priv)
+{
+ u8 res;
+
+ switch (priv->interval) {
+ case 9:
+ /* 10 bits */
+ res = 0;
+ break;
+ case 8:
+ /* 11 bits */
+ res = 1;
+ break;
+ default:
+ /* 12 bits */
+ res = 3;
+ break;
+ }
+
+ if (priv->res == res)
+ return 0;
+
+ priv->config &= ~STTS751_CONF_RES_MASK;
+ priv->config |= res << STTS751_CONF_RES_SHIFT;
+ dev_dbg(&priv->client->dev, "setting res %d. config %x",
+ res, priv->config);
+ priv->res = res;
+
+ return i2c_smbus_write_byte_data(priv->client,
+ STTS751_REG_CONF, priv->config);
+}
+
+static int stts751_update_temp(struct stts751_priv *priv)
+{
+ s32 integer1, integer2, frac;
+
+ /*
+ * There is a trick here, like in the lm90 driver. We have to read two
+ * registers to get the sensor temperature, but we have to beware a
+ * conversion could occur between the readings. We could use the
+ * one-shot conversion register, but we don't want to do this (disables
+ * hardware monitoring). So the solution used here is to read the high
+ * byte once, then the low byte, then the high byte again. If the new
+ * high byte matches the old one, then we have a valid reading. Else we
+ * have to read the low byte again, and now we believe we have a correct
+ * reading.
+ */
+ integer1 = i2c_smbus_read_byte_data(priv->client, STTS751_REG_TEMP_H);
+ if (integer1 < 0) {
+ dev_dbg(&priv->client->dev,
+ "I2C read failed (temp H). ret: %x\n", integer1);
+ return integer1;
+ }
+
+ frac = i2c_smbus_read_byte_data(priv->client, STTS751_REG_TEMP_L);
+ if (frac < 0) {
+ dev_dbg(&priv->client->dev,
+ "I2C read failed (temp L). ret: %x\n", frac);
+ return frac;
+ }
+
+ integer2 = i2c_smbus_read_byte_data(priv->client, STTS751_REG_TEMP_H);
+ if (integer2 < 0) {
+ dev_dbg(&priv->client->dev,
+ "I2C 2nd read failed (temp H). ret: %x\n", integer2);
+ return integer2;
+ }
+
+ if (integer1 != integer2) {
+ frac = i2c_smbus_read_byte_data(priv->client,
+ STTS751_REG_TEMP_L);
+ if (frac < 0) {
+ dev_dbg(&priv->client->dev,
+ "I2C 2nd read failed (temp L). ret: %x\n",
+ frac);
+ return frac;
+ }
+ }
+
+ priv->temp = stts751_to_deg((integer1 << 8) | frac);
+ return 0;
+}
+
+static int stts751_set_temp_reg16(struct stts751_priv *priv, int temp,
+ u8 hreg, u8 lreg)
+{
+ s32 hwval;
+ int ret;
+
+ hwval = stts751_to_hw(temp);
+
+ ret = i2c_smbus_write_byte_data(priv->client, hreg, hwval >> 8);
+ if (ret)
+ return ret;
+
+ return i2c_smbus_write_byte_data(priv->client, lreg, hwval & 0xff);
+}
+
+static int stts751_set_temp_reg8(struct stts751_priv *priv, int temp, u8 reg)
+{
+ s32 hwval;
+
+ hwval = stts751_to_hw(temp);
+ return i2c_smbus_write_byte_data(priv->client, reg, hwval >> 8);
+}
+
+static int stts751_read_reg16(struct stts751_priv *priv, int *temp,
+ u8 hreg, u8 lreg)
+{
+ int integer, frac;
+
+ integer = i2c_smbus_read_byte_data(priv->client, hreg);
+ if (integer < 0)
+ return integer;
+
+ frac = i2c_smbus_read_byte_data(priv->client, lreg);
+ if (frac < 0)
+ return frac;
+
+ *temp = stts751_to_deg((integer << 8) | frac);
+
+ return 0;
+}
+
+static int stts751_read_reg8(struct stts751_priv *priv, int *temp, u8 reg)
+{
+ int integer;
+
+ integer = i2c_smbus_read_byte_data(priv->client, reg);
+ if (integer < 0)
+ return integer;
+
+ *temp = stts751_to_deg(integer << 8);
+
+ return 0;
+}
+
+/*
+ * Update alert flags without waiting for cache to expire. We detects alerts
+ * immediately for the sake of the alert handler; we still need to deal with
+ * caching to workaround the fact that alarm flags int the status register,
+ * despite what the datasheet claims, gets always cleared on read.
+ */
+static int stts751_update_alert(struct stts751_priv *priv)
+{
+ int ret;
+ bool conv_done;
+ int cache_time = msecs_to_jiffies(stts751_intervals[priv->interval]);
+
+ /*
+ * Add another 10% because if we run faster than the HW conversion
+ * rate we will end up in reporting incorrectly alarms.
+ */
+ cache_time += cache_time / 10;
+
+ ret = i2c_smbus_read_byte_data(priv->client, STTS751_REG_STATUS);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&priv->client->dev, "status reg %x\n", ret);
+ conv_done = ret & (STTS751_STATUS_TRIPH | STTS751_STATUS_TRIPL);
+ /*
+ * Reset the cache if the cache time expired, or if we are sure
+ * we have valid data from a device conversion, or if we know
+ * our cache has been never written.
+ *
+ * Note that when the cache has been never written the point is
+ * to correctly initialize the timestamp, rather than clearing
+ * the cache values.
+ *
+ * Note that updating the cache timestamp when we get an alarm flag
+ * is required, otherwise we could incorrectly report alarms to be zero.
+ */
+ if (time_after(jiffies, priv->last_alert_update + cache_time) ||
+ conv_done || !priv->alert_valid) {
+ priv->max_alert = false;
+ priv->min_alert = false;
+ priv->alert_valid = true;
+ priv->last_alert_update = jiffies;
+ dev_dbg(&priv->client->dev, "invalidating alert cache\n");
+ }
+
+ priv->max_alert |= !!(ret & STTS751_STATUS_TRIPH);
+ priv->min_alert |= !!(ret & STTS751_STATUS_TRIPL);
+ priv->therm_trip = !!(ret & STTS751_STATUS_TRIPT);
+
+ dev_dbg(&priv->client->dev, "max_alert: %d, min_alert: %d, therm_trip: %d\n",
+ priv->max_alert, priv->min_alert, priv->therm_trip);
+
+ return 0;
+}
+
+static void stts751_alert(struct i2c_client *client,
+ enum i2c_alert_protocol type, unsigned int data)
+{
+ int ret;
+ struct stts751_priv *priv = i2c_get_clientdata(client);
+
+ if (type != I2C_PROTOCOL_SMBUS_ALERT)
+ return;
+
+ dev_dbg(&client->dev, "alert!");
+
+ mutex_lock(&priv->access_lock);
+ ret = stts751_update_alert(priv);
+ if (ret < 0) {
+ /* default to worst case */
+ priv->max_alert = true;
+ priv->min_alert = true;
+
+ dev_warn(priv->dev,
+ "Alert received, but can't communicate to the device. Triggering all alarms!");
+ }
+
+ if (priv->max_alert) {
+ if (priv->notify_max)
+ dev_notice(priv->dev, "got alert for HIGH temperature");
+ priv->notify_max = false;
+
+ /* unblock alert poll */
+ sysfs_notify(&priv->dev->kobj, NULL, "temp1_max_alarm");
+ }
+
+ if (priv->min_alert) {
+ if (priv->notify_min)
+ dev_notice(priv->dev, "got alert for LOW temperature");
+ priv->notify_min = false;
+
+ /* unblock alert poll */
+ sysfs_notify(&priv->dev->kobj, NULL, "temp1_min_alarm");
+ }
+
+ if (priv->min_alert || priv->max_alert)
+ kobject_uevent(&priv->dev->kobj, KOBJ_CHANGE);
+
+ mutex_unlock(&priv->access_lock);
+}
+
+static int stts751_update(struct stts751_priv *priv)
+{
+ int ret;
+ int cache_time = msecs_to_jiffies(stts751_intervals[priv->interval]);
+
+ if (time_after(jiffies, priv->last_update + cache_time) ||
+ !priv->data_valid) {
+ ret = stts751_update_temp(priv);
+ if (ret)
+ return ret;
+
+ ret = stts751_update_alert(priv);
+ if (ret)
+ return ret;
+ priv->data_valid = true;
+ priv->last_update = jiffies;
+ }
+
+ return 0;
+}
+
+static ssize_t show_max_alarm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ mutex_lock(&priv->access_lock);
+ ret = stts751_update(priv);
+ if (!ret)
+ priv->notify_max = true;
+ mutex_unlock(&priv->access_lock);
+ if (ret < 0)
+ return ret;
+
+ return snprintf(buf, PAGE_SIZE - 1, "%d\n", priv->max_alert);
+}
+
+static ssize_t show_min_alarm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ mutex_lock(&priv->access_lock);
+ ret = stts751_update(priv);
+ if (!ret)
+ priv->notify_min = true;
+ mutex_unlock(&priv->access_lock);
+ if (ret < 0)
+ return ret;
+
+ return snprintf(buf, PAGE_SIZE - 1, "%d\n", priv->min_alert);
+}
+
+static ssize_t show_input(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ mutex_lock(&priv->access_lock);
+ ret = stts751_update(priv);
+ mutex_unlock(&priv->access_lock);
+ if (ret < 0)
+ return ret;
+
+ return snprintf(buf, PAGE_SIZE - 1, "%d\n", priv->temp);
+}
+
+static ssize_t show_therm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE - 1, "%d\n", priv->therm);
+}
+
+static ssize_t set_therm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ long temp;
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ if (kstrtol(buf, 10, &temp) < 0)
+ return -EINVAL;
+
+ /* HW works in range -64C to +127.937C */
+ temp = clamp_val(temp, -64000, 127937);
+ mutex_lock(&priv->access_lock);
+ ret = stts751_set_temp_reg8(priv, temp, STTS751_REG_TLIM);
+ if (ret)
+ goto exit;
+
+ dev_dbg(&priv->client->dev, "setting therm %ld", temp);
+
+ /*
+ * hysteresis reg is relative to therm, so the HW does not need to be
+ * adjusted, we need to update our local copy only.
+ */
+ priv->hyst = temp - (priv->therm - priv->hyst);
+ priv->therm = temp;
+
+exit:
+ mutex_unlock(&priv->access_lock);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t show_hyst(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE - 1, "%d\n", priv->hyst);
+}
+
+static ssize_t set_hyst(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ long temp;
+
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ if (kstrtol(buf, 10, &temp) < 0)
+ return -EINVAL;
+
+ mutex_lock(&priv->access_lock);
+ /* HW works in range -64C to +127.937C */
+ temp = clamp_val(temp, -64000, priv->therm);
+ priv->hyst = temp;
+ dev_dbg(&priv->client->dev, "setting hyst %ld", temp);
+ temp = priv->therm - temp;
+ ret = stts751_set_temp_reg8(priv, temp, STTS751_REG_HYST);
+ mutex_unlock(&priv->access_lock);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t show_therm_trip(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ mutex_lock(&priv->access_lock);
+ ret = stts751_update(priv);
+ mutex_unlock(&priv->access_lock);
+ if (ret < 0)
+ return ret;
+
+ return snprintf(buf, PAGE_SIZE - 1, "%d\n", priv->therm_trip);
+}
+
+static ssize_t show_max(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE - 1, "%d\n", priv->event_max);
+}
+
+static ssize_t set_max(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ long temp;
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ if (kstrtol(buf, 10, &temp) < 0)
+ return -EINVAL;
+
+ mutex_lock(&priv->access_lock);
+ /* HW works in range -64C to +127.937C */
+ temp = clamp_val(temp, priv->event_min, 127937);
+ ret = stts751_set_temp_reg16(priv, temp,
+ STTS751_REG_HLIM_H, STTS751_REG_HLIM_L);
+ if (ret)
+ goto exit;
+
+ dev_dbg(&priv->client->dev, "setting event max %ld", temp);
+ priv->event_max = temp;
+ ret = count;
+exit:
+ mutex_unlock(&priv->access_lock);
+ return ret;
+}
+
+static ssize_t show_min(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE - 1, "%d\n", priv->event_min);
+}
+
+static ssize_t set_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ long temp;
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ if (kstrtol(buf, 10, &temp) < 0)
+ return -EINVAL;
+
+ mutex_lock(&priv->access_lock);
+ /* HW works in range -64C to +127.937C */
+ temp = clamp_val(temp, -64000, priv->event_max);
+ ret = stts751_set_temp_reg16(priv, temp,
+ STTS751_REG_LLIM_H, STTS751_REG_LLIM_L);
+ if (ret)
+ goto exit;
+
+ dev_dbg(&priv->client->dev, "setting event min %ld", temp);
+ priv->event_min = temp;
+ ret = count;
+exit:
+ mutex_unlock(&priv->access_lock);
+ return ret;
+}
+
+static ssize_t show_interval(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE - 1, "%d\n",
+ stts751_intervals[priv->interval]);
+}
+
+static ssize_t set_interval(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ int idx;
+ int ret = count;
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ idx = find_closest_descending(val, stts751_intervals,
+ ARRAY_SIZE(stts751_intervals));
+
+ dev_dbg(&priv->client->dev, "setting interval. req:%lu, idx: %d, val: %d",
+ val, idx, stts751_intervals[idx]);
+
+ mutex_lock(&priv->access_lock);
+ if (priv->interval == idx)
+ goto exit;
+
+ /*
+ * In early development stages I've become suspicious about the chip
+ * starting to misbehave if I ever set, even briefly, an invalid
+ * configuration. While I'm not sure this is really needed, be
+ * conservative and set rate/resolution in such an order that avoids
+ * passing through an invalid configuration.
+ */
+
+ /* speed up: lower the resolution, then modify convrate */
+ if (priv->interval < idx) {
+ dev_dbg(&priv->client->dev, "lower resolution, then modify convrate");
+ priv->interval = idx;
+ ret = stts751_adjust_resolution(priv);
+ if (ret)
+ goto exit;
+ }
+
+ ret = i2c_smbus_write_byte_data(priv->client, STTS751_REG_RATE, idx);
+ if (ret)
+ goto exit;
+ /* slow down: modify convrate, then raise resolution */
+ if (priv->interval != idx) {
+ dev_dbg(&priv->client->dev, "modify convrate, then raise resolution");
+ priv->interval = idx;
+ ret = stts751_adjust_resolution(priv);
+ if (ret)
+ goto exit;
+ }
+ ret = count;
+exit:
+ mutex_unlock(&priv->access_lock);
+
+ return ret;
+}
+
+static int stts751_detect(struct i2c_client *new_client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = new_client->adapter;
+ const char *name;
+ int tmp;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ tmp = i2c_smbus_read_byte_data(new_client, STTS751_REG_MAN_ID);
+ if (tmp != ST_MAN_ID)
+ return -ENODEV;
+
+ /* lower temperaure registers always have bits 0-3 set to zero */
+ tmp = i2c_smbus_read_byte_data(new_client, STTS751_REG_TEMP_L);
+ if (tmp & 0xf)
+ return -ENODEV;
+
+ tmp = i2c_smbus_read_byte_data(new_client, STTS751_REG_HLIM_L);
+ if (tmp & 0xf)
+ return -ENODEV;
+
+ tmp = i2c_smbus_read_byte_data(new_client, STTS751_REG_LLIM_L);
+ if (tmp & 0xf)
+ return -ENODEV;
+
+ /* smbus timeout register always have bits 0-7 set to zero */
+ tmp = i2c_smbus_read_byte_data(new_client, STTS751_REG_SMBUS_TO);
+ if (tmp & 0x7f)
+ return -ENODEV;
+
+ tmp = i2c_smbus_read_byte_data(new_client, STTS751_REG_PROD_ID);
+
+ switch (tmp) {
+ case STTS751_0_PROD_ID:
+ name = "STTS751-0";
+ break;
+ case STTS751_1_PROD_ID:
+ name = "STTS751-1";
+ break;
+ default:
+ return -ENODEV;
+ }
+ dev_dbg(&new_client->dev, "Chip %s detected", name);
+
+ strlcpy(info->type, stts751_id[0].name, I2C_NAME_SIZE);
+ return 0;
+}
+
+static int stts751_read_chip_config(struct stts751_priv *priv)
+{
+ int ret;
+ int tmp;
+
+ ret = i2c_smbus_read_byte_data(priv->client, STTS751_REG_CONF);
+ if (ret < 0)
+ return ret;
+ priv->config = ret;
+ priv->res = (ret & STTS751_CONF_RES_MASK) >> STTS751_CONF_RES_SHIFT;
+
+ ret = i2c_smbus_read_byte_data(priv->client, STTS751_REG_RATE);
+ if (ret < 0)
+ return ret;
+ priv->interval = ret;
+
+ ret = stts751_read_reg16(priv, &priv->event_max,
+ STTS751_REG_HLIM_H, STTS751_REG_HLIM_L);
+ if (ret)
+ return ret;
+
+ ret = stts751_read_reg16(priv, &priv->event_min,
+ STTS751_REG_LLIM_H, STTS751_REG_LLIM_L);
+ if (ret)
+ return ret;
+
+ ret = stts751_read_reg8(priv, &priv->therm, STTS751_REG_TLIM);
+ if (ret)
+ return ret;
+
+ ret = stts751_read_reg8(priv, &tmp, STTS751_REG_HYST);
+ if (ret)
+ return ret;
+ priv->hyst = priv->therm - tmp;
+
+ return 0;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, 0444, show_input, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_min, 0644, show_min, set_min, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, 0644, show_max, set_max, 0);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, 0444, show_min_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, 0444, show_max_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_crit, 0644, show_therm, set_therm, 0);
+static SENSOR_DEVICE_ATTR(temp1_crit_hyst, 0644, show_hyst, set_hyst, 0);
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, 0444, show_therm_trip, NULL, 0);
+static SENSOR_DEVICE_ATTR(update_interval, 0644,
+ show_interval, set_interval, 0);
+
+static struct attribute *stts751_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_update_interval.dev_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(stts751);
+
+static int stts751_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct stts751_priv *priv;
+ int ret;
+ bool smbus_nto;
+ int rev_id;
+
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->client = client;
+ priv->notify_max = true;
+ priv->notify_min = true;
+ i2c_set_clientdata(client, priv);
+ mutex_init(&priv->access_lock);
+
+ if (device_property_present(&client->dev,
+ "smbus-timeout-disable")) {
+ smbus_nto = device_property_read_bool(&client->dev,
+ "smbus-timeout-disable");
+
+ ret = i2c_smbus_write_byte_data(client, STTS751_REG_SMBUS_TO,
+ smbus_nto ? 0 : 0x80);
+ if (ret)
+ return ret;
+ }
+
+ rev_id = i2c_smbus_read_byte_data(client, STTS751_REG_REV_ID);
+ if (rev_id < 0)
+ return -ENODEV;
+ if (rev_id != 0x1) {
+ dev_dbg(&client->dev, "Chip revision 0x%x is untested\n",
+ rev_id);
+ }
+
+ ret = stts751_read_chip_config(priv);
+ if (ret)
+ return ret;
+
+ priv->config &= ~(STTS751_CONF_STOP | STTS751_CONF_EVENT_DIS);
+ ret = i2c_smbus_write_byte_data(client, STTS751_REG_CONF, priv->config);
+ if (ret)
+ return ret;
+
+ priv->dev = devm_hwmon_device_register_with_groups(&client->dev,
+ client->name, priv,
+ stts751_groups);
+ return PTR_ERR_OR_ZERO(priv->dev);
+}
+
+MODULE_DEVICE_TABLE(i2c, stts751_id);
+
+static struct i2c_driver stts751_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = DEVNAME,
+ },
+ .probe = stts751_probe,
+ .id_table = stts751_id,
+ .detect = stts751_detect,
+ .alert = stts751_alert,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(stts751_driver);
+
+MODULE_AUTHOR("Andrea Merello <andrea.merello@gmail.com>");
+MODULE_DESCRIPTION("STTS751 sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c
index eeeed2c..1f2d13d 100644
--- a/drivers/hwmon/tmp401.c
+++ b/drivers/hwmon/tmp401.c
@@ -82,16 +82,6 @@
{ 0, 0x11 }, /* offset */
};
-static const u8 TMP401_TEMP_LSB[7][2] = {
- { 0x15, 0x10 }, /* temp */
- { 0x17, 0x14 }, /* low limit */
- { 0x16, 0x13 }, /* high limit */
- { 0, 0 }, /* therm (crit) limit (unused) */
- { 0x31, 0x35 }, /* lowest */
- { 0x33, 0x37 }, /* highest */
- { 0, 0x12 }, /* offset */
-};
-
static const u8 TMP432_TEMP_MSB_READ[4][3] = {
{ 0x00, 0x01, 0x23 }, /* temp */
{ 0x06, 0x08, 0x16 }, /* low limit */
@@ -106,12 +96,6 @@
{ 0x20, 0x19, 0x1A }, /* therm (crit) limit */
};
-static const u8 TMP432_TEMP_LSB[3][3] = {
- { 0x29, 0x10, 0x24 }, /* temp */
- { 0x3E, 0x14, 0x18 }, /* low limit */
- { 0x3D, 0x13, 0x17 }, /* high limit */
-};
-
/* [0] = fault, [1] = low, [2] = high, [3] = therm/crit */
static const u8 TMP432_STATUS_REG[] = {
0x1b, 0x36, 0x35, 0x37 };
@@ -213,25 +197,20 @@
for (i = 0; i < num_sensors; i++) { /* local / r1 / r2 */
for (j = 0; j < num_regs; j++) { /* temp / low / ... */
u8 regaddr;
- /*
- * High byte must be read first immediately followed
- * by the low byte
- */
+
regaddr = data->kind == tmp432 ?
TMP432_TEMP_MSB_READ[j][i] :
TMP401_TEMP_MSB_READ[j][i];
- val = i2c_smbus_read_byte_data(client, regaddr);
+ if (j == 3) { /* crit is msb only */
+ val = i2c_smbus_read_byte_data(client, regaddr);
+ } else {
+ val = i2c_smbus_read_word_swapped(client,
+ regaddr);
+ }
if (val < 0)
return val;
- data->temp[j][i] = val << 8;
- if (j == 3) /* crit is msb only */
- continue;
- regaddr = data->kind == tmp432 ? TMP432_TEMP_LSB[j][i]
- : TMP401_TEMP_LSB[j][i];
- val = i2c_smbus_read_byte_data(client, regaddr);
- if (val < 0)
- return val;
- data->temp[j][i] |= val;
+
+ data->temp[j][i] = j == 3 ? val << 8 : val;
}
}
return 0;
@@ -373,11 +352,11 @@
regaddr = data->kind == tmp432 ? TMP432_TEMP_MSB_WRITE[nr][index]
: TMP401_TEMP_MSB_WRITE[nr][index];
- i2c_smbus_write_byte_data(client, regaddr, reg >> 8);
- if (nr != 3) {
- regaddr = data->kind == tmp432 ? TMP432_TEMP_LSB[nr][index]
- : TMP401_TEMP_LSB[nr][index];
- i2c_smbus_write_byte_data(client, regaddr, reg & 0xFF);
+ if (nr == 3) { /* crit is msb only */
+ i2c_smbus_write_byte_data(client, regaddr, reg >> 8);
+ } else {
+ /* Hardware expects big endian data --> use _swapped */
+ i2c_smbus_write_word_swapped(client, regaddr, reg);
}
data->temp[nr][index] = reg;
@@ -449,7 +428,7 @@
return count;
}
-static ssize_t show_update_interval(struct device *dev,
+static ssize_t update_interval_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tmp401_data *data = dev_get_drvdata(dev);
@@ -457,9 +436,9 @@
return sprintf(buf, "%u\n", data->update_interval);
}
-static ssize_t set_update_interval(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t update_interval_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct tmp401_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@@ -521,8 +500,7 @@
static SENSOR_DEVICE_ATTR_2(temp2_crit_alarm, S_IRUGO, show_status, NULL,
3, TMP432_STATUS_REMOTE1);
-static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval,
- set_update_interval);
+static DEVICE_ATTR_RW(update_interval);
static struct attribute *tmp401_attributes[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c
index d1f209a..07a0cb0 100644
--- a/drivers/hwmon/via-cputemp.c
+++ b/drivers/hwmon/via-cputemp.c
@@ -88,8 +88,8 @@
return sprintf(buf, "%lu\n", ((unsigned long)eax & 0xffffff) * 1000);
}
-static ssize_t show_cpu_vid(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
{
struct via_cputemp_data *data = dev_get_drvdata(dev);
u32 eax, edx;
@@ -119,7 +119,7 @@
};
/* Optional attributes */
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_cpu_vid, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
static int via_cputemp_probe(struct platform_device *pdev)
{
diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c
index 40dd93c..81f35e3 100644
--- a/drivers/hwmon/via686a.c
+++ b/drivers/hwmon/via686a.c
@@ -580,14 +580,14 @@
show_fan_offset(2);
/* Alarms */
-static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static DEVICE_ATTR_RO(alarms);
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -607,13 +607,13 @@
static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6);
static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7);
-static ssize_t show_name(struct device *dev, struct device_attribute
+static ssize_t name_show(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct via686a_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", data->name);
}
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
static struct attribute *via686a_attributes[] = {
&sensor_dev_attr_in0_input.dev_attr.attr,
diff --git a/drivers/hwmon/vt8231.c b/drivers/hwmon/vt8231.c
index cb69a8c..367b5eb5 100644
--- a/drivers/hwmon/vt8231.c
+++ b/drivers/hwmon/vt8231.c
@@ -263,8 +263,8 @@
}
/* Special case for input 5 as this has 3.3V scaling built into the chip */
-static ssize_t show_in5(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t in5_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct vt8231_data *data = vt8231_update_device(dev);
@@ -272,7 +272,7 @@
(((data->in[5] - 3) * 10000 * 54) / (958 * 34)));
}
-static ssize_t show_in5_min(struct device *dev, struct device_attribute *attr,
+static ssize_t in5_min_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct vt8231_data *data = vt8231_update_device(dev);
@@ -281,7 +281,7 @@
(((data->in_min[5] - 3) * 10000 * 54) / (958 * 34)));
}
-static ssize_t show_in5_max(struct device *dev, struct device_attribute *attr,
+static ssize_t in5_max_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct vt8231_data *data = vt8231_update_device(dev);
@@ -290,8 +290,9 @@
(((data->in_max[5] - 3) * 10000 * 54) / (958 * 34)));
}
-static ssize_t set_in5_min(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t in5_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
{
struct vt8231_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -309,8 +310,9 @@
return count;
}
-static ssize_t set_in5_max(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t in5_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
{
struct vt8231_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -342,34 +344,35 @@
define_voltage_sysfs(3);
define_voltage_sysfs(4);
-static DEVICE_ATTR(in5_input, S_IRUGO, show_in5, NULL);
-static DEVICE_ATTR(in5_min, S_IRUGO | S_IWUSR, show_in5_min, set_in5_min);
-static DEVICE_ATTR(in5_max, S_IRUGO | S_IWUSR, show_in5_max, set_in5_max);
+static DEVICE_ATTR_RO(in5_input);
+static DEVICE_ATTR_RW(in5_min);
+static DEVICE_ATTR_RW(in5_max);
/* Temperatures */
-static ssize_t show_temp0(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct vt8231_data *data = vt8231_update_device(dev);
return sprintf(buf, "%d\n", data->temp[0] * 250);
}
-static ssize_t show_temp0_max(struct device *dev, struct device_attribute *attr,
+static ssize_t temp1_max_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct vt8231_data *data = vt8231_update_device(dev);
return sprintf(buf, "%d\n", data->temp_max[0] * 1000);
}
-static ssize_t show_temp0_min(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t temp1_max_hyst_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct vt8231_data *data = vt8231_update_device(dev);
return sprintf(buf, "%d\n", data->temp_min[0] * 1000);
}
-static ssize_t set_temp0_max(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t temp1_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
{
struct vt8231_data *data = dev_get_drvdata(dev);
long val;
@@ -385,8 +388,9 @@
mutex_unlock(&data->update_lock);
return count;
}
-static ssize_t set_temp0_min(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t temp1_max_hyst_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct vt8231_data *data = dev_get_drvdata(dev);
long val;
@@ -481,10 +485,9 @@
static SENSOR_DEVICE_ATTR(temp##offset##_max_hyst, S_IRUGO | S_IWUSR, \
show_temp_min, set_temp_min, offset - 1)
-static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp0, NULL);
-static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp0_max, set_temp0_max);
-static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp0_min,
- set_temp0_min);
+static DEVICE_ATTR_RO(temp1_input);
+static DEVICE_ATTR_RW(temp1_max);
+static DEVICE_ATTR_RW(temp1_max_hyst);
define_temperature_sysfs(2);
define_temperature_sysfs(3);
@@ -603,13 +606,13 @@
define_fan_sysfs(2);
/* Alarms */
-static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct vt8231_data *data = vt8231_update_device(dev);
return sprintf(buf, "%d\n", data->alarms);
}
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static DEVICE_ATTR_RO(alarms);
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -633,13 +636,13 @@
static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6);
static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7);
-static ssize_t show_name(struct device *dev, struct device_attribute
+static ssize_t name_show(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct vt8231_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", data->name);
}
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
static struct attribute *vt8231_attributes_temps[6][5] = {
{
diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c
index 697007a..ab346ed 100644
--- a/drivers/hwmon/w83627ehf.c
+++ b/drivers/hwmon/w83627ehf.c
@@ -1687,14 +1687,14 @@
fan_time_functions(fan_stop_time, FAN_STOP_TIME)
-static ssize_t show_name(struct device *dev, struct device_attribute *attr,
+static ssize_t name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct w83627ehf_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", data->name);
}
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
static struct sensor_device_attribute sda_sf3_arrays_fan4[] = {
SENSOR_ATTR(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
@@ -1754,12 +1754,12 @@
};
static ssize_t
-show_vid(struct device *dev, struct device_attribute *attr, char *buf)
+cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83627ehf_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
/* Case open detection */
diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c
index 721295b..8ac89d0 100644
--- a/drivers/hwmon/w83627hf.c
+++ b/drivers/hwmon/w83627hf.c
@@ -575,26 +575,30 @@
return sprintf(buf,"%ld\n", in0);
}
-static ssize_t show_regs_in_0(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t in0_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct w83627hf_data *data = w83627hf_update_device(dev);
return show_in_0(data, buf, data->in[0]);
}
-static ssize_t show_regs_in_min0(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t in0_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct w83627hf_data *data = w83627hf_update_device(dev);
return show_in_0(data, buf, data->in_min[0]);
}
-static ssize_t show_regs_in_max0(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t in0_max_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct w83627hf_data *data = w83627hf_update_device(dev);
return show_in_0(data, buf, data->in_max[0]);
}
-static ssize_t store_regs_in_min0(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t in0_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
{
struct w83627hf_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -622,8 +626,9 @@
return count;
}
-static ssize_t store_regs_in_max0(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t in0_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
{
struct w83627hf_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -651,11 +656,9 @@
return count;
}
-static DEVICE_ATTR(in0_input, S_IRUGO, show_regs_in_0, NULL);
-static DEVICE_ATTR(in0_min, S_IRUGO | S_IWUSR,
- show_regs_in_min0, store_regs_in_min0);
-static DEVICE_ATTR(in0_max, S_IRUGO | S_IWUSR,
- show_regs_in_max0, store_regs_in_max0);
+static DEVICE_ATTR_RO(in0_input);
+static DEVICE_ATTR_RW(in0_min);
+static DEVICE_ATTR_RW(in0_max);
static ssize_t
show_fan_input(struct device *dev, struct device_attribute *devattr, char *buf)
@@ -796,21 +799,22 @@
sysfs_temp_decl(3);
static ssize_t
-show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf)
+cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83627hf_data *data = w83627hf_update_device(dev);
return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm));
}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
static ssize_t
-show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf)
+vrm_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83627hf_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%ld\n", (long) data->vrm);
}
static ssize_t
-store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+vrm_store(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
{
struct w83627hf_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -826,15 +830,15 @@
return count;
}
-static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
+static DEVICE_ATTR_RW(vrm);
static ssize_t
-show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf)
+alarms_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83627hf_data *data = w83627hf_update_device(dev);
return sprintf(buf, "%ld\n", (long) data->alarms);
}
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
+static DEVICE_ATTR_RO(alarms);
static ssize_t
show_alarm(struct device *dev, struct device_attribute *attr, char *buf)
@@ -860,7 +864,7 @@
static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13);
static ssize_t
-show_beep_mask(struct device *dev, struct device_attribute *attr, char *buf)
+beep_mask_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83627hf_data *data = w83627hf_update_device(dev);
return sprintf(buf, "%ld\n",
@@ -868,7 +872,7 @@
}
static ssize_t
-store_beep_mask(struct device *dev, struct device_attribute *attr,
+beep_mask_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct w83627hf_data *data = dev_get_drvdata(dev);
@@ -895,8 +899,7 @@
return count;
}
-static DEVICE_ATTR(beep_mask, S_IRUGO | S_IWUSR,
- show_beep_mask, store_beep_mask);
+static DEVICE_ATTR_RW(beep_mask);
static ssize_t
show_beep(struct device *dev, struct device_attribute *attr, char *buf)
@@ -1264,13 +1267,13 @@
sysfs_temp_type(3);
static ssize_t
-show_name(struct device *dev, struct device_attribute *devattr, char *buf)
+name_show(struct device *dev, struct device_attribute *devattr, char *buf)
{
struct w83627hf_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", data->name);
}
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
static int __init w83627hf_find(int sioaddr, unsigned short *addr,
struct w83627hf_sio_data *sio_data)
diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c
index 54848fd..246fb23 100644
--- a/drivers/hwmon/w83781d.c
+++ b/drivers/hwmon/w83781d.c
@@ -416,24 +416,24 @@
sysfs_temp_offsets(3);
static ssize_t
-show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf)
+cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83781d_data *data = w83781d_update_device(dev);
return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm));
}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
static ssize_t
-show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf)
+vrm_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83781d_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%ld\n", (long) data->vrm);
}
static ssize_t
-store_vrm_reg(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+vrm_store(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
{
struct w83781d_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -447,16 +447,16 @@
return count;
}
-static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
+static DEVICE_ATTR_RW(vrm);
static ssize_t
-show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf)
+alarms_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83781d_data *data = w83781d_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
+static DEVICE_ATTR_RO(alarms);
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -491,7 +491,7 @@
static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5);
static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_temp3_alarm, NULL, 0);
-static ssize_t show_beep_mask(struct device *dev,
+static ssize_t beep_mask_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct w83781d_data *data = w83781d_update_device(dev);
@@ -500,7 +500,7 @@
}
static ssize_t
-store_beep_mask(struct device *dev, struct device_attribute *attr,
+beep_mask_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct w83781d_data *data = dev_get_drvdata(dev);
@@ -527,8 +527,7 @@
return count;
}
-static DEVICE_ATTR(beep_mask, S_IRUGO | S_IWUSR,
- show_beep_mask, store_beep_mask);
+static DEVICE_ATTR_RW(beep_mask);
static ssize_t show_beep(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -708,7 +707,7 @@
}
static ssize_t
-show_pwm2_enable(struct device *dev, struct device_attribute *da, char *buf)
+pwm2_enable_show(struct device *dev, struct device_attribute *da, char *buf)
{
struct w83781d_data *data = w83781d_update_device(dev);
return sprintf(buf, "%d\n", (int)data->pwm2_enable);
@@ -736,7 +735,7 @@
}
static ssize_t
-store_pwm2_enable(struct device *dev, struct device_attribute *da,
+pwm2_enable_store(struct device *dev, struct device_attribute *da,
const char *buf, size_t count)
{
struct w83781d_data *data = dev_get_drvdata(dev);
@@ -778,8 +777,7 @@
static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, show_pwm, store_pwm, 2);
static SENSOR_DEVICE_ATTR(pwm4, S_IRUGO | S_IWUSR, show_pwm, store_pwm, 3);
/* only PWM2 can be enabled/disabled */
-static DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR,
- show_pwm2_enable, store_pwm2_enable);
+static DEVICE_ATTR_RW(pwm2_enable);
static ssize_t
show_sensor(struct device *dev, struct device_attribute *da, char *buf)
@@ -1616,12 +1614,12 @@
* we must create it by ourselves.
*/
static ssize_t
-show_name(struct device *dev, struct device_attribute *devattr, char *buf)
+name_show(struct device *dev, struct device_attribute *devattr, char *buf)
{
struct w83781d_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", data->name);
}
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+static DEVICE_ATTR_RO(name);
static struct w83781d_data *w83781d_data_if_isa(void)
{
diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c
index 001df85..8af6081 100644
--- a/drivers/hwmon/w83791d.c
+++ b/drivers/hwmon/w83791d.c
@@ -1041,14 +1041,14 @@
};
/* get realtime status of all sensors items: voltage, temp, fan */
-static ssize_t show_alarms_reg(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct w83791d_data *data = w83791d_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
+static DEVICE_ATTR_RO(alarms);
/* Beep control */
@@ -1147,25 +1147,24 @@
};
/* cpu voltage regulation information */
-static ssize_t show_vid_reg(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct w83791d_data *data = w83791d_update_device(dev);
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
+static DEVICE_ATTR_RO(cpu0_vid);
-static ssize_t show_vrm_reg(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct w83791d_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", data->vrm);
}
-static ssize_t store_vrm_reg(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct w83791d_data *data = dev_get_drvdata(dev);
unsigned long val;
@@ -1188,7 +1187,7 @@
return count;
}
-static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
+static DEVICE_ATTR_RW(vrm);
#define IN_UNIT_ATTRS(X) \
&sda_in_input[X].dev_attr.attr, \
diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c
index 0a8bce7..d764602 100644
--- a/drivers/hwmon/w83792d.c
+++ b/drivers/hwmon/w83792d.c
@@ -578,7 +578,7 @@
/* get realtime status of all sensors items: voltage, temp, fan */
static ssize_t
-show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf)
+alarms_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83792d_data *data = w83792d_update_device(dev);
return sprintf(buf, "%d\n", data->alarms);
@@ -735,16 +735,16 @@
}
static ssize_t
-show_chassis_clear(struct device *dev, struct device_attribute *attr,
- char *buf)
+intrusion0_alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct w83792d_data *data = w83792d_update_device(dev);
return sprintf(buf, "%d\n", data->chassis);
}
static ssize_t
-store_chassis_clear(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+intrusion0_alarm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83792d_data *data = i2c_get_clientdata(client);
@@ -1047,7 +1047,7 @@
show_temp23, store_temp23, 0, 4);
static SENSOR_DEVICE_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR,
show_temp23, store_temp23, 1, 4);
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
+static DEVICE_ATTR_RO(alarms);
static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 2);
@@ -1067,8 +1067,7 @@
static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 21);
static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 22);
static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_alarm, NULL, 23);
-static DEVICE_ATTR(intrusion0_alarm, S_IRUGO | S_IWUSR,
- show_chassis_clear, store_chassis_clear);
+static DEVICE_ATTR_RW(intrusion0_alarm);
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0);
static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1);
static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2);
diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c
index 816aa6c..dab5c51 100644
--- a/drivers/hwmon/w83793.c
+++ b/drivers/hwmon/w83793.c
@@ -324,7 +324,7 @@
};
static ssize_t
-show_vrm(struct device *dev, struct device_attribute *attr, char *buf)
+vrm_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83793_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", data->vrm);
@@ -342,7 +342,7 @@
}
static ssize_t
-store_vrm(struct device *dev, struct device_attribute *attr,
+vrm_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct w83793_data *data = dev_get_drvdata(dev);
@@ -1169,7 +1169,7 @@
SENSOR_ATTR_2(cpu0_vid, S_IRUGO, show_vid, NULL, NOT_USED, 0),
SENSOR_ATTR_2(cpu1_vid, S_IRUGO, show_vid, NULL, NOT_USED, 1),
};
-static DEVICE_ATTR(vrm, S_IWUSR | S_IRUGO, show_vrm, store_vrm);
+static DEVICE_ATTR_RW(vrm);
static struct sensor_device_attribute_2 sda_single_files[] = {
SENSOR_ATTR_2(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm_beep,
diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c
index 6d81c56..e9db857 100644
--- a/drivers/i2c/busses/i2c-designware-core.c
+++ b/drivers/i2c/busses/i2c-designware-core.c
@@ -475,30 +475,28 @@
static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
{
struct i2c_msg *msgs = dev->msgs;
- u32 ic_tar = 0;
+ u32 ic_con, ic_tar = 0;
/* Disable the adapter */
__i2c_dw_enable_and_wait(dev, false);
/* if the slave address is ten bit address, enable 10BITADDR */
- if (dev->dynamic_tar_update_enabled) {
+ ic_con = dw_readl(dev, DW_IC_CON);
+ if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) {
+ ic_con |= DW_IC_CON_10BITADDR_MASTER;
/*
* If I2C_DYNAMIC_TAR_UPDATE is set, the 10-bit addressing
- * mode has to be enabled via bit 12 of IC_TAR register,
- * otherwise bit 4 of IC_CON is used.
+ * mode has to be enabled via bit 12 of IC_TAR register.
+ * We set it always as I2C_DYNAMIC_TAR_UPDATE can't be
+ * detected from registers.
*/
- if (msgs[dev->msg_write_idx].flags & I2C_M_TEN)
- ic_tar = DW_IC_TAR_10BITADDR_MASTER;
+ ic_tar = DW_IC_TAR_10BITADDR_MASTER;
} else {
- u32 ic_con = dw_readl(dev, DW_IC_CON);
-
- if (msgs[dev->msg_write_idx].flags & I2C_M_TEN)
- ic_con |= DW_IC_CON_10BITADDR_MASTER;
- else
- ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
- dw_writel(dev, ic_con, DW_IC_CON);
+ ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
}
+ dw_writel(dev, ic_con, DW_IC_CON);
+
/*
* Set the slave (target) address and enable 10-bit addressing mode
* if applicable.
@@ -963,7 +961,6 @@
{
struct i2c_adapter *adap = &dev->adapter;
int r;
- u32 reg;
init_completion(&dev->cmd_complete);
@@ -971,26 +968,6 @@
if (r)
return r;
- r = i2c_dw_acquire_lock(dev);
- if (r)
- return r;
-
- /*
- * Test if dynamic TAR update is enabled in this controller by writing
- * to IC_10BITADDR_MASTER field in IC_CON: when it is enabled this
- * field is read-only so it should not succeed
- */
- reg = dw_readl(dev, DW_IC_CON);
- dw_writel(dev, reg ^ DW_IC_CON_10BITADDR_MASTER, DW_IC_CON);
-
- if ((dw_readl(dev, DW_IC_CON) & DW_IC_CON_10BITADDR_MASTER) ==
- (reg & DW_IC_CON_10BITADDR_MASTER)) {
- dev->dynamic_tar_update_enabled = true;
- dev_dbg(dev->dev, "Dynamic TAR update enabled");
- }
-
- i2c_dw_release_lock(dev);
-
snprintf(adap->name, sizeof(adap->name),
"Synopsys DesignWare I2C adapter");
adap->retries = 3;
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index 26250b4..c1db3a5 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -125,7 +125,6 @@
int (*acquire_lock)(struct dw_i2c_dev *dev);
void (*release_lock)(struct dw_i2c_dev *dev);
bool pm_runtime_disabled;
- bool dynamic_tar_update_enabled;
};
#define ACCESS_SWAP 0x00000001
diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.h b/drivers/infiniband/hw/cxgb3/iwch_cm.h
index b9efadf..e66e759 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_cm.h
+++ b/drivers/infiniband/hw/cxgb3/iwch_cm.h
@@ -55,14 +55,14 @@
#define put_ep(ep) { \
PDBG("put_ep (via %s:%u) ep %p refcnt %d\n", __func__, __LINE__, \
- ep, atomic_read(&((ep)->kref.refcount))); \
- WARN_ON(atomic_read(&((ep)->kref.refcount)) < 1); \
+ ep, kref_read(&((ep)->kref))); \
+ WARN_ON(kref_read(&((ep)->kref)) < 1); \
kref_put(&((ep)->kref), __free_ep); \
}
#define get_ep(ep) { \
PDBG("get_ep (via %s:%u) ep %p, refcnt %d\n", __func__, __LINE__, \
- ep, atomic_read(&((ep)->kref.refcount))); \
+ ep, kref_read(&((ep)->kref))); \
kref_get(&((ep)->kref)); \
}
diff --git a/drivers/infiniband/hw/cxgb3/iwch_qp.c b/drivers/infiniband/hw/cxgb3/iwch_qp.c
index d939980..a9194db 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_qp.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_qp.c
@@ -961,7 +961,7 @@
case IWCH_QP_STATE_RTS:
switch (attrs->next_state) {
case IWCH_QP_STATE_CLOSING:
- BUG_ON(atomic_read(&qhp->ep->com.kref.refcount) < 2);
+ BUG_ON(kref_read(&qhp->ep->com.kref) < 2);
qhp->attr.state = IWCH_QP_STATE_CLOSING;
if (!internal) {
abort=0;
diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
index 8cd4d05..d19662f 100644
--- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
+++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
@@ -672,14 +672,14 @@
#define c4iw_put_ep(ep) { \
PDBG("put_ep (via %s:%u) ep %p refcnt %d\n", __func__, __LINE__, \
- ep, atomic_read(&((ep)->kref.refcount))); \
- WARN_ON(atomic_read(&((ep)->kref.refcount)) < 1); \
+ ep, kref_read(&((ep)->kref))); \
+ WARN_ON(kref_read(&((ep)->kref)) < 1); \
kref_put(&((ep)->kref), _c4iw_free_ep); \
}
#define c4iw_get_ep(ep) { \
PDBG("get_ep (via %s:%u) ep %p, refcnt %d\n", __func__, __LINE__, \
- ep, atomic_read(&((ep)->kref.refcount))); \
+ ep, kref_read(&((ep)->kref))); \
kref_get(&((ep)->kref)); \
}
void _c4iw_free_ep(struct kref *kref);
diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c
index 04c1c38..d4fd2f5 100644
--- a/drivers/infiniband/hw/cxgb4/qp.c
+++ b/drivers/infiniband/hw/cxgb4/qp.c
@@ -1580,7 +1580,7 @@
case C4IW_QP_STATE_RTS:
switch (attrs->next_state) {
case C4IW_QP_STATE_CLOSING:
- BUG_ON(atomic_read(&qhp->ep->com.kref.refcount) < 2);
+ BUG_ON(kref_read(&qhp->ep->com.kref) < 2);
t4_set_wq_in_error(&qhp->wq);
set_state(qhp, C4IW_QP_STATE_CLOSING);
ep = qhp->ep;
diff --git a/drivers/infiniband/hw/usnic/usnic_ib_sysfs.c b/drivers/infiniband/hw/usnic/usnic_ib_sysfs.c
index 80ef3f8..0444324 100644
--- a/drivers/infiniband/hw/usnic/usnic_ib_sysfs.c
+++ b/drivers/infiniband/hw/usnic/usnic_ib_sysfs.c
@@ -80,7 +80,7 @@
left = PAGE_SIZE;
mutex_lock(&us_ibdev->usdev_lock);
- if (atomic_read(&us_ibdev->vf_cnt.refcount) > 0) {
+ if (kref_read(&us_ibdev->vf_cnt) > 0) {
char *busname;
/*
@@ -99,7 +99,7 @@
PCI_FUNC(us_ibdev->pdev->devfn),
netdev_name(us_ibdev->netdev),
us_ibdev->ufdev->mac,
- atomic_read(&us_ibdev->vf_cnt.refcount));
+ kref_read(&us_ibdev->vf_cnt));
UPDATE_PTR_LEFT(n, ptr, left);
for (res_type = USNIC_VNIC_RES_TYPE_EOL;
@@ -147,7 +147,7 @@
us_ibdev = container_of(device, struct usnic_ib_dev, ib_dev.dev);
return scnprintf(buf, PAGE_SIZE, "%u\n",
- atomic_read(&us_ibdev->vf_cnt.refcount));
+ kref_read(&us_ibdev->vf_cnt));
}
static ssize_t
diff --git a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c
index 74819a7..69df8e3 100644
--- a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c
+++ b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c
@@ -291,11 +291,11 @@
qp_per_vf = max(us_ibdev->vf_res_cnt[USNIC_VNIC_RES_TYPE_WQ],
us_ibdev->vf_res_cnt[USNIC_VNIC_RES_TYPE_RQ]);
props->max_qp = qp_per_vf *
- atomic_read(&us_ibdev->vf_cnt.refcount);
+ kref_read(&us_ibdev->vf_cnt);
props->device_cap_flags = IB_DEVICE_PORT_ACTIVE_EVENT |
IB_DEVICE_SYS_IMAGE_GUID | IB_DEVICE_BLOCK_MULTICAST_LOOPBACK;
props->max_cq = us_ibdev->vf_res_cnt[USNIC_VNIC_RES_TYPE_CQ] *
- atomic_read(&us_ibdev->vf_cnt.refcount);
+ kref_read(&us_ibdev->vf_cnt);
props->max_pd = USNIC_UIOM_MAX_PD_CNT;
props->max_mr = USNIC_UIOM_MAX_MR_CNT;
props->local_ca_ack_delay = 0;
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index fa598f7..1e1d0ad 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -1231,6 +1231,7 @@
{ "ELAN0000", 0 },
{ "ELAN0100", 0 },
{ "ELAN0600", 0 },
+ { "ELAN0605", 0 },
{ "ELAN1000", 0 },
{ }
};
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 8ee54d7..37e204f 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -352,9 +352,6 @@
select IOMMU_API
select MEMORY
select MTK_SMI
- select COMMON_CLK_MT2701_MMSYS
- select COMMON_CLK_MT2701_IMGSYS
- select COMMON_CLK_MT2701_VDECSYS
help
Support for the M4U on certain Mediatek SoCs. M4U generation 1 HW is
Multimedia Memory Managememt Unit. This option enables remapping of
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 3ef0f42..1b5b8c5 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -112,7 +112,7 @@
* Domain for untranslated devices - only allocated
* if iommu=pt passed on kernel cmd line.
*/
-static const struct iommu_ops amd_iommu_ops;
+const struct iommu_ops amd_iommu_ops;
static ATOMIC_NOTIFIER_HEAD(ppr_notifier);
int amd_iommu_max_glx_val = -1;
@@ -445,6 +445,7 @@
static int iommu_init_device(struct device *dev)
{
struct iommu_dev_data *dev_data;
+ struct amd_iommu *iommu;
int devid;
if (dev->archdata.iommu)
@@ -454,6 +455,8 @@
if (devid < 0)
return devid;
+ iommu = amd_iommu_rlookup_table[devid];
+
dev_data = find_dev_data(devid);
if (!dev_data)
return -ENOMEM;
@@ -469,8 +472,7 @@
dev->archdata.iommu = dev_data;
- iommu_device_link(amd_iommu_rlookup_table[dev_data->devid]->iommu_dev,
- dev);
+ iommu_device_link(&iommu->iommu, dev);
return 0;
}
@@ -495,13 +497,16 @@
static void iommu_uninit_device(struct device *dev)
{
- int devid;
struct iommu_dev_data *dev_data;
+ struct amd_iommu *iommu;
+ int devid;
devid = get_device_id(dev);
if (devid < 0)
return;
+ iommu = amd_iommu_rlookup_table[devid];
+
dev_data = search_dev_data(devid);
if (!dev_data)
return;
@@ -509,8 +514,7 @@
if (dev_data->domain)
detach_device(dev);
- iommu_device_unlink(amd_iommu_rlookup_table[dev_data->devid]->iommu_dev,
- dev);
+ iommu_device_unlink(&iommu->iommu, dev);
iommu_group_remove_device(dev);
@@ -3161,9 +3165,10 @@
return false;
}
-static void amd_iommu_get_dm_regions(struct device *dev,
- struct list_head *head)
+static void amd_iommu_get_resv_regions(struct device *dev,
+ struct list_head *head)
{
+ struct iommu_resv_region *region;
struct unity_map_entry *entry;
int devid;
@@ -3172,41 +3177,56 @@
return;
list_for_each_entry(entry, &amd_iommu_unity_map, list) {
- struct iommu_dm_region *region;
+ size_t length;
+ int prot = 0;
if (devid < entry->devid_start || devid > entry->devid_end)
continue;
- region = kzalloc(sizeof(*region), GFP_KERNEL);
+ length = entry->address_end - entry->address_start;
+ if (entry->prot & IOMMU_PROT_IR)
+ prot |= IOMMU_READ;
+ if (entry->prot & IOMMU_PROT_IW)
+ prot |= IOMMU_WRITE;
+
+ region = iommu_alloc_resv_region(entry->address_start,
+ length, prot,
+ IOMMU_RESV_DIRECT);
if (!region) {
pr_err("Out of memory allocating dm-regions for %s\n",
dev_name(dev));
return;
}
-
- region->start = entry->address_start;
- region->length = entry->address_end - entry->address_start;
- if (entry->prot & IOMMU_PROT_IR)
- region->prot |= IOMMU_READ;
- if (entry->prot & IOMMU_PROT_IW)
- region->prot |= IOMMU_WRITE;
-
list_add_tail(®ion->list, head);
}
+
+ region = iommu_alloc_resv_region(MSI_RANGE_START,
+ MSI_RANGE_END - MSI_RANGE_START + 1,
+ 0, IOMMU_RESV_RESERVED);
+ if (!region)
+ return;
+ list_add_tail(®ion->list, head);
+
+ region = iommu_alloc_resv_region(HT_RANGE_START,
+ HT_RANGE_END - HT_RANGE_START + 1,
+ 0, IOMMU_RESV_RESERVED);
+ if (!region)
+ return;
+ list_add_tail(®ion->list, head);
}
-static void amd_iommu_put_dm_regions(struct device *dev,
+static void amd_iommu_put_resv_regions(struct device *dev,
struct list_head *head)
{
- struct iommu_dm_region *entry, *next;
+ struct iommu_resv_region *entry, *next;
list_for_each_entry_safe(entry, next, head, list)
kfree(entry);
}
-static void amd_iommu_apply_dm_region(struct device *dev,
+static void amd_iommu_apply_resv_region(struct device *dev,
struct iommu_domain *domain,
- struct iommu_dm_region *region)
+ struct iommu_resv_region *region)
{
struct dma_ops_domain *dma_dom = to_dma_ops_domain(to_pdomain(domain));
unsigned long start, end;
@@ -3217,7 +3237,7 @@
WARN_ON_ONCE(reserve_iova(&dma_dom->iovad, start, end) == NULL);
}
-static const struct iommu_ops amd_iommu_ops = {
+const struct iommu_ops amd_iommu_ops = {
.capable = amd_iommu_capable,
.domain_alloc = amd_iommu_domain_alloc,
.domain_free = amd_iommu_domain_free,
@@ -3230,9 +3250,9 @@
.add_device = amd_iommu_add_device,
.remove_device = amd_iommu_remove_device,
.device_group = amd_iommu_device_group,
- .get_dm_regions = amd_iommu_get_dm_regions,
- .put_dm_regions = amd_iommu_put_dm_regions,
- .apply_dm_region = amd_iommu_apply_dm_region,
+ .get_resv_regions = amd_iommu_get_resv_regions,
+ .put_resv_regions = amd_iommu_put_resv_regions,
+ .apply_resv_region = amd_iommu_apply_resv_region,
.pgsize_bitmap = AMD_IOMMU_PGSIZES,
};
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index 6799cf9..04cdac7 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -94,6 +94,8 @@
* out of it.
*/
+extern const struct iommu_ops amd_iommu_ops;
+
/*
* structure describing one IOMMU in the ACPI table. Typically followed by one
* or more ivhd_entrys.
@@ -1635,9 +1637,10 @@
amd_iommu_erratum_746_workaround(iommu);
amd_iommu_ats_write_check_workaround(iommu);
- iommu->iommu_dev = iommu_device_create(&iommu->dev->dev, iommu,
- amd_iommu_groups, "ivhd%d",
- iommu->index);
+ iommu_device_sysfs_add(&iommu->iommu, &iommu->dev->dev,
+ amd_iommu_groups, "ivhd%d", iommu->index);
+ iommu_device_set_ops(&iommu->iommu, &amd_iommu_ops);
+ iommu_device_register(&iommu->iommu);
return pci_enable_device(iommu->dev);
}
@@ -2230,7 +2233,7 @@
*/
ret = check_ivrs_checksum(ivrs_base);
if (ret)
- return ret;
+ goto out;
amd_iommu_target_ivhd_type = get_highest_supported_ivhd_type(ivrs_base);
DUMP_printk("Using IVHD type %#x\n", amd_iommu_target_ivhd_type);
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index 0d91785..af00f38 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -535,8 +535,8 @@
/* if one, we need to send a completion wait command */
bool need_sync;
- /* IOMMU sysfs device */
- struct device *iommu_dev;
+ /* Handle for IOMMU core code */
+ struct iommu_device iommu;
/*
* We can't rely on the BIOS to restore all values on reinit, so we
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 4d6ec44..5806a6a 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -269,9 +269,6 @@
#define STRTAB_STE_1_SHCFG_INCOMING 1UL
#define STRTAB_STE_1_SHCFG_SHIFT 44
-#define STRTAB_STE_1_PRIVCFG_UNPRIV 2UL
-#define STRTAB_STE_1_PRIVCFG_SHIFT 48
-
#define STRTAB_STE_2_S2VMID_SHIFT 0
#define STRTAB_STE_2_S2VMID_MASK 0xffffUL
#define STRTAB_STE_2_VTCR_SHIFT 32
@@ -412,6 +409,9 @@
/* High-level queue structures */
#define ARM_SMMU_POLL_TIMEOUT_US 100
+#define MSI_IOVA_BASE 0x8000000
+#define MSI_IOVA_LENGTH 0x100000
+
static bool disable_bypass;
module_param_named(disable_bypass, disable_bypass, bool, S_IRUGO);
MODULE_PARM_DESC(disable_bypass,
@@ -616,6 +616,9 @@
unsigned int sid_bits;
struct arm_smmu_strtab_cfg strtab_cfg;
+
+ /* IOMMU core code handle */
+ struct iommu_device iommu;
};
/* SMMU private data for each master */
@@ -1042,13 +1045,8 @@
}
}
- /* Nuke the existing Config, as we're going to rewrite it */
- val &= ~(STRTAB_STE_0_CFG_MASK << STRTAB_STE_0_CFG_SHIFT);
-
- if (ste->valid)
- val |= STRTAB_STE_0_V;
- else
- val &= ~STRTAB_STE_0_V;
+ /* Nuke the existing STE_0 value, as we're going to rewrite it */
+ val = ste->valid ? STRTAB_STE_0_V : 0;
if (ste->bypass) {
val |= disable_bypass ? STRTAB_STE_0_CFG_ABORT
@@ -1073,9 +1071,7 @@
#ifdef CONFIG_PCI_ATS
STRTAB_STE_1_EATS_TRANS << STRTAB_STE_1_EATS_SHIFT |
#endif
- STRTAB_STE_1_STRW_NSEL1 << STRTAB_STE_1_STRW_SHIFT |
- STRTAB_STE_1_PRIVCFG_UNPRIV <<
- STRTAB_STE_1_PRIVCFG_SHIFT);
+ STRTAB_STE_1_STRW_NSEL1 << STRTAB_STE_1_STRW_SHIFT);
if (smmu->features & ARM_SMMU_FEAT_STALLS)
dst[1] |= cpu_to_le64(STRTAB_STE_1_S1STALLD);
@@ -1083,7 +1079,6 @@
val |= (ste->s1_cfg->cdptr_dma & STRTAB_STE_0_S1CTXPTR_MASK
<< STRTAB_STE_0_S1CTXPTR_SHIFT) |
STRTAB_STE_0_CFG_S1_TRANS;
-
}
if (ste->s2_cfg) {
@@ -1372,8 +1367,6 @@
switch (cap) {
case IOMMU_CAP_CACHE_COHERENCY:
return true;
- case IOMMU_CAP_INTR_REMAP:
- return true; /* MSIs are just memory writes */
case IOMMU_CAP_NOEXEC:
return true;
default:
@@ -1795,8 +1788,10 @@
}
group = iommu_group_get_for_dev(dev);
- if (!IS_ERR(group))
+ if (!IS_ERR(group)) {
iommu_group_put(group);
+ iommu_device_link(&smmu->iommu, dev);
+ }
return PTR_ERR_OR_ZERO(group);
}
@@ -1805,14 +1800,17 @@
{
struct iommu_fwspec *fwspec = dev->iommu_fwspec;
struct arm_smmu_master_data *master;
+ struct arm_smmu_device *smmu;
if (!fwspec || fwspec->ops != &arm_smmu_ops)
return;
master = fwspec->iommu_priv;
+ smmu = master->smmu;
if (master && master->ste.valid)
arm_smmu_detach_dev(dev);
iommu_group_remove_device(dev);
+ iommu_device_unlink(&smmu->iommu, dev);
kfree(master);
iommu_fwspec_free(dev);
}
@@ -1883,6 +1881,29 @@
return iommu_fwspec_add_ids(dev, args->args, 1);
}
+static void arm_smmu_get_resv_regions(struct device *dev,
+ struct list_head *head)
+{
+ struct iommu_resv_region *region;
+ int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
+
+ region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH,
+ prot, IOMMU_RESV_MSI);
+ if (!region)
+ return;
+
+ list_add_tail(®ion->list, head);
+}
+
+static void arm_smmu_put_resv_regions(struct device *dev,
+ struct list_head *head)
+{
+ struct iommu_resv_region *entry, *next;
+
+ list_for_each_entry_safe(entry, next, head, list)
+ kfree(entry);
+}
+
static struct iommu_ops arm_smmu_ops = {
.capable = arm_smmu_capable,
.domain_alloc = arm_smmu_domain_alloc,
@@ -1898,6 +1919,8 @@
.domain_get_attr = arm_smmu_domain_get_attr,
.domain_set_attr = arm_smmu_domain_set_attr,
.of_xlate = arm_smmu_of_xlate,
+ .get_resv_regions = arm_smmu_get_resv_regions,
+ .put_resv_regions = arm_smmu_put_resv_regions,
.pgsize_bitmap = -1UL, /* Restricted during device attach */
};
@@ -1983,17 +2006,9 @@
u32 size, l1size;
struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
- /*
- * If we can resolve everything with a single L2 table, then we
- * just need a single L1 descriptor. Otherwise, calculate the L1
- * size, capped to the SIDSIZE.
- */
- if (smmu->sid_bits < STRTAB_SPLIT) {
- size = 0;
- } else {
- size = STRTAB_L1_SZ_SHIFT - (ilog2(STRTAB_L1_DESC_DWORDS) + 3);
- size = min(size, smmu->sid_bits - STRTAB_SPLIT);
- }
+ /* Calculate the L1 size, capped to the SIDSIZE. */
+ size = STRTAB_L1_SZ_SHIFT - (ilog2(STRTAB_L1_DESC_DWORDS) + 3);
+ size = min(size, smmu->sid_bits - STRTAB_SPLIT);
cfg->num_l1_ents = 1 << size;
size += STRTAB_SPLIT;
@@ -2504,6 +2519,13 @@
smmu->ssid_bits = reg >> IDR1_SSID_SHIFT & IDR1_SSID_MASK;
smmu->sid_bits = reg >> IDR1_SID_SHIFT & IDR1_SID_MASK;
+ /*
+ * If the SMMU supports fewer bits than would fill a single L2 stream
+ * table, use a linear table instead.
+ */
+ if (smmu->sid_bits <= STRTAB_SPLIT)
+ smmu->features &= ~ARM_SMMU_FEAT_2_LVL_STRTAB;
+
/* IDR5 */
reg = readl_relaxed(smmu->base + ARM_SMMU_IDR5);
@@ -2613,6 +2635,7 @@
{
int irq, ret;
struct resource *res;
+ resource_size_t ioaddr;
struct arm_smmu_device *smmu;
struct device *dev = &pdev->dev;
bool bypass;
@@ -2630,6 +2653,7 @@
dev_err(dev, "MMIO region too small (%pr)\n", res);
return -EINVAL;
}
+ ioaddr = res->start;
smmu->base = devm_ioremap_resource(dev, res);
if (IS_ERR(smmu->base))
@@ -2682,7 +2706,15 @@
return ret;
/* And we're up. Go go go! */
- iommu_register_instance(dev->fwnode, &arm_smmu_ops);
+ ret = iommu_device_sysfs_add(&smmu->iommu, dev, NULL,
+ "smmu3.%pa", &ioaddr);
+ if (ret)
+ return ret;
+
+ iommu_device_set_ops(&smmu->iommu, &arm_smmu_ops);
+ iommu_device_set_fwnode(&smmu->iommu, dev->fwnode);
+
+ ret = iommu_device_register(&smmu->iommu);
#ifdef CONFIG_PCI
if (pci_bus_type.iommu_ops != &arm_smmu_ops) {
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index a60cded..abf6496 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -24,6 +24,7 @@
* - v7/v8 long-descriptor format
* - Non-secure access to the SMMU
* - Context fault reporting
+ * - Extended Stream ID (16 bit)
*/
#define pr_fmt(fmt) "arm-smmu: " fmt
@@ -87,6 +88,7 @@
#define sCR0_CLIENTPD (1 << 0)
#define sCR0_GFRE (1 << 1)
#define sCR0_GFIE (1 << 2)
+#define sCR0_EXIDENABLE (1 << 3)
#define sCR0_GCFGFRE (1 << 4)
#define sCR0_GCFGFIE (1 << 5)
#define sCR0_USFCFG (1 << 10)
@@ -126,6 +128,7 @@
#define ID0_NUMIRPT_MASK 0xff
#define ID0_NUMSIDB_SHIFT 9
#define ID0_NUMSIDB_MASK 0xf
+#define ID0_EXIDS (1 << 8)
#define ID0_NUMSMRG_SHIFT 0
#define ID0_NUMSMRG_MASK 0xff
@@ -169,6 +172,7 @@
#define ARM_SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2))
#define S2CR_CBNDX_SHIFT 0
#define S2CR_CBNDX_MASK 0xff
+#define S2CR_EXIDVALID (1 << 10)
#define S2CR_TYPE_SHIFT 16
#define S2CR_TYPE_MASK 0x3
enum arm_smmu_s2cr_type {
@@ -260,6 +264,7 @@
#define TTBCR2_SEP_SHIFT 15
#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT)
+#define TTBCR2_AS (1 << 4)
#define TTBRn_ASID_SHIFT 48
@@ -281,6 +286,9 @@
#define FSYNR0_WNR (1 << 4)
+#define MSI_IOVA_BASE 0x8000000
+#define MSI_IOVA_LENGTH 0x100000
+
static int force_stage;
module_param(force_stage, int, S_IRUGO);
MODULE_PARM_DESC(force_stage,
@@ -351,6 +359,7 @@
#define ARM_SMMU_FEAT_FMT_AARCH64_64K (1 << 9)
#define ARM_SMMU_FEAT_FMT_AARCH32_L (1 << 10)
#define ARM_SMMU_FEAT_FMT_AARCH32_S (1 << 11)
+#define ARM_SMMU_FEAT_EXIDS (1 << 12)
u32 features;
#define ARM_SMMU_OPT_SECURE_CFG_ACCESS (1 << 0)
@@ -380,6 +389,9 @@
unsigned int *irqs;
u32 cavium_id_base; /* Specific to Cavium */
+
+ /* IOMMU core code handle */
+ struct iommu_device iommu;
};
enum arm_smmu_context_fmt {
@@ -778,6 +790,8 @@
reg = pgtbl_cfg->arm_lpae_s1_cfg.tcr;
reg2 = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32;
reg2 |= TTBCR2_SEP_UPSTREAM;
+ if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64)
+ reg2 |= TTBCR2_AS;
}
if (smmu->version > ARM_SMMU_V1)
writel_relaxed(reg2, cb_base + ARM_SMMU_CB_TTBCR2);
@@ -1048,7 +1062,7 @@
struct arm_smmu_smr *smr = smmu->smrs + idx;
u32 reg = smr->id << SMR_ID_SHIFT | smr->mask << SMR_MASK_SHIFT;
- if (smr->valid)
+ if (!(smmu->features & ARM_SMMU_FEAT_EXIDS) && smr->valid)
reg |= SMR_VALID;
writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_SMR(idx));
}
@@ -1060,6 +1074,9 @@
(s2cr->cbndx & S2CR_CBNDX_MASK) << S2CR_CBNDX_SHIFT |
(s2cr->privcfg & S2CR_PRIVCFG_MASK) << S2CR_PRIVCFG_SHIFT;
+ if (smmu->features & ARM_SMMU_FEAT_EXIDS && smmu->smrs &&
+ smmu->smrs[idx].valid)
+ reg |= S2CR_EXIDVALID;
writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_S2CR(idx));
}
@@ -1070,6 +1087,34 @@
arm_smmu_write_smr(smmu, idx);
}
+/*
+ * The width of SMR's mask field depends on sCR0_EXIDENABLE, so this function
+ * should be called after sCR0 is written.
+ */
+static void arm_smmu_test_smr_masks(struct arm_smmu_device *smmu)
+{
+ void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+ u32 smr;
+
+ if (!smmu->smrs)
+ return;
+
+ /*
+ * SMR.ID bits may not be preserved if the corresponding MASK
+ * bits are set, so check each one separately. We can reject
+ * masters later if they try to claim IDs outside these masks.
+ */
+ smr = smmu->streamid_mask << SMR_ID_SHIFT;
+ writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(0));
+ smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0));
+ smmu->streamid_mask = smr >> SMR_ID_SHIFT;
+
+ smr = smmu->streamid_mask << SMR_MASK_SHIFT;
+ writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(0));
+ smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0));
+ smmu->smr_mask_mask = smr >> SMR_MASK_SHIFT;
+}
+
static int arm_smmu_find_sme(struct arm_smmu_device *smmu, u16 id, u16 mask)
{
struct arm_smmu_smr *smrs = smmu->smrs;
@@ -1214,7 +1259,7 @@
continue;
s2cr[idx].type = type;
- s2cr[idx].privcfg = S2CR_PRIVCFG_UNPRIV;
+ s2cr[idx].privcfg = S2CR_PRIVCFG_DEFAULT;
s2cr[idx].cbndx = cbndx;
arm_smmu_write_s2cr(smmu, idx);
}
@@ -1371,8 +1416,6 @@
* requests.
*/
return true;
- case IOMMU_CAP_INTR_REMAP:
- return true; /* MSIs are just memory writes */
case IOMMU_CAP_NOEXEC:
return true;
default:
@@ -1444,6 +1487,8 @@
if (ret)
goto out_free;
+ iommu_device_link(&smmu->iommu, dev);
+
return 0;
out_free:
@@ -1456,10 +1501,17 @@
static void arm_smmu_remove_device(struct device *dev)
{
struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+ struct arm_smmu_master_cfg *cfg;
+ struct arm_smmu_device *smmu;
+
if (!fwspec || fwspec->ops != &arm_smmu_ops)
return;
+ cfg = fwspec->iommu_priv;
+ smmu = cfg->smmu;
+
+ iommu_device_unlink(&smmu->iommu, dev);
arm_smmu_master_free_smes(fwspec);
iommu_group_remove_device(dev);
kfree(fwspec->iommu_priv);
@@ -1549,6 +1601,29 @@
return iommu_fwspec_add_ids(dev, &fwid, 1);
}
+static void arm_smmu_get_resv_regions(struct device *dev,
+ struct list_head *head)
+{
+ struct iommu_resv_region *region;
+ int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
+
+ region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH,
+ prot, IOMMU_RESV_MSI);
+ if (!region)
+ return;
+
+ list_add_tail(®ion->list, head);
+}
+
+static void arm_smmu_put_resv_regions(struct device *dev,
+ struct list_head *head)
+{
+ struct iommu_resv_region *entry, *next;
+
+ list_for_each_entry_safe(entry, next, head, list)
+ kfree(entry);
+}
+
static struct iommu_ops arm_smmu_ops = {
.capable = arm_smmu_capable,
.domain_alloc = arm_smmu_domain_alloc,
@@ -1564,6 +1639,8 @@
.domain_get_attr = arm_smmu_domain_get_attr,
.domain_set_attr = arm_smmu_domain_set_attr,
.of_xlate = arm_smmu_of_xlate,
+ .get_resv_regions = arm_smmu_get_resv_regions,
+ .put_resv_regions = arm_smmu_put_resv_regions,
.pgsize_bitmap = -1UL, /* Restricted during device attach */
};
@@ -1648,6 +1725,9 @@
if (smmu->features & ARM_SMMU_FEAT_VMID16)
reg |= sCR0_VMID16EN;
+ if (smmu->features & ARM_SMMU_FEAT_EXIDS)
+ reg |= sCR0_EXIDENABLE;
+
/* Push the button */
__arm_smmu_tlb_sync(smmu);
writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
@@ -1735,11 +1815,14 @@
"\t(IDR0.CTTW overridden by FW configuration)\n");
/* Max. number of entries we have for stream matching/indexing */
- size = 1 << ((id >> ID0_NUMSIDB_SHIFT) & ID0_NUMSIDB_MASK);
+ if (smmu->version == ARM_SMMU_V2 && id & ID0_EXIDS) {
+ smmu->features |= ARM_SMMU_FEAT_EXIDS;
+ size = 1 << 16;
+ } else {
+ size = 1 << ((id >> ID0_NUMSIDB_SHIFT) & ID0_NUMSIDB_MASK);
+ }
smmu->streamid_mask = size - 1;
if (id & ID0_SMS) {
- u32 smr;
-
smmu->features |= ARM_SMMU_FEAT_STREAM_MATCH;
size = (id >> ID0_NUMSMRG_SHIFT) & ID0_NUMSMRG_MASK;
if (size == 0) {
@@ -1748,21 +1831,6 @@
return -ENODEV;
}
- /*
- * SMR.ID bits may not be preserved if the corresponding MASK
- * bits are set, so check each one separately. We can reject
- * masters later if they try to claim IDs outside these masks.
- */
- smr = smmu->streamid_mask << SMR_ID_SHIFT;
- writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(0));
- smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0));
- smmu->streamid_mask = smr >> SMR_ID_SHIFT;
-
- smr = smmu->streamid_mask << SMR_MASK_SHIFT;
- writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(0));
- smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0));
- smmu->smr_mask_mask = smr >> SMR_MASK_SHIFT;
-
/* Zero-initialised to mark as invalid */
smmu->smrs = devm_kcalloc(smmu->dev, size, sizeof(*smmu->smrs),
GFP_KERNEL);
@@ -1770,8 +1838,7 @@
return -ENOMEM;
dev_notice(smmu->dev,
- "\tstream matching with %lu register groups, mask 0x%x",
- size, smmu->smr_mask_mask);
+ "\tstream matching with %lu register groups", size);
}
/* s2cr->type == 0 means translation, so initialise explicitly */
smmu->s2crs = devm_kmalloc_array(smmu->dev, size, sizeof(*smmu->s2crs),
@@ -2011,6 +2078,7 @@
static int arm_smmu_device_probe(struct platform_device *pdev)
{
struct resource *res;
+ resource_size_t ioaddr;
struct arm_smmu_device *smmu;
struct device *dev = &pdev->dev;
int num_irqs, i, err;
@@ -2031,6 +2099,7 @@
return err;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ioaddr = res->start;
smmu->base = devm_ioremap_resource(dev, res);
if (IS_ERR(smmu->base))
return PTR_ERR(smmu->base);
@@ -2091,9 +2160,25 @@
}
}
- iommu_register_instance(dev->fwnode, &arm_smmu_ops);
+ err = iommu_device_sysfs_add(&smmu->iommu, smmu->dev, NULL,
+ "smmu.%pa", &ioaddr);
+ if (err) {
+ dev_err(dev, "Failed to register iommu in sysfs\n");
+ return err;
+ }
+
+ iommu_device_set_ops(&smmu->iommu, &arm_smmu_ops);
+ iommu_device_set_fwnode(&smmu->iommu, dev->fwnode);
+
+ err = iommu_device_register(&smmu->iommu);
+ if (err) {
+ dev_err(dev, "Failed to register iommu\n");
+ return err;
+ }
+
platform_set_drvdata(pdev, smmu);
arm_smmu_device_reset(smmu);
+ arm_smmu_test_smr_masks(smmu);
/* Oh, for a proper bus abstraction */
if (!iommu_present(&platform_bus_type))
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 2db0d64..48d36ce 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -37,15 +37,50 @@
phys_addr_t phys;
};
-struct iommu_dma_cookie {
- struct iova_domain iovad;
- struct list_head msi_page_list;
- spinlock_t msi_lock;
+enum iommu_dma_cookie_type {
+ IOMMU_DMA_IOVA_COOKIE,
+ IOMMU_DMA_MSI_COOKIE,
};
+struct iommu_dma_cookie {
+ enum iommu_dma_cookie_type type;
+ union {
+ /* Full allocator for IOMMU_DMA_IOVA_COOKIE */
+ struct iova_domain iovad;
+ /* Trivial linear page allocator for IOMMU_DMA_MSI_COOKIE */
+ dma_addr_t msi_iova;
+ };
+ struct list_head msi_page_list;
+ spinlock_t msi_lock;
+};
+
+static inline size_t cookie_msi_granule(struct iommu_dma_cookie *cookie)
+{
+ if (cookie->type == IOMMU_DMA_IOVA_COOKIE)
+ return cookie->iovad.granule;
+ return PAGE_SIZE;
+}
+
static inline struct iova_domain *cookie_iovad(struct iommu_domain *domain)
{
- return &((struct iommu_dma_cookie *)domain->iova_cookie)->iovad;
+ struct iommu_dma_cookie *cookie = domain->iova_cookie;
+
+ if (cookie->type == IOMMU_DMA_IOVA_COOKIE)
+ return &cookie->iovad;
+ return NULL;
+}
+
+static struct iommu_dma_cookie *cookie_alloc(enum iommu_dma_cookie_type type)
+{
+ struct iommu_dma_cookie *cookie;
+
+ cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
+ if (cookie) {
+ spin_lock_init(&cookie->msi_lock);
+ INIT_LIST_HEAD(&cookie->msi_page_list);
+ cookie->type = type;
+ }
+ return cookie;
}
int iommu_dma_init(void)
@@ -62,25 +97,53 @@
*/
int iommu_get_dma_cookie(struct iommu_domain *domain)
{
- struct iommu_dma_cookie *cookie;
-
if (domain->iova_cookie)
return -EEXIST;
- cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
- if (!cookie)
+ domain->iova_cookie = cookie_alloc(IOMMU_DMA_IOVA_COOKIE);
+ if (!domain->iova_cookie)
return -ENOMEM;
- spin_lock_init(&cookie->msi_lock);
- INIT_LIST_HEAD(&cookie->msi_page_list);
- domain->iova_cookie = cookie;
return 0;
}
EXPORT_SYMBOL(iommu_get_dma_cookie);
/**
+ * iommu_get_msi_cookie - Acquire just MSI remapping resources
+ * @domain: IOMMU domain to prepare
+ * @base: Start address of IOVA region for MSI mappings
+ *
+ * Users who manage their own IOVA allocation and do not want DMA API support,
+ * but would still like to take advantage of automatic MSI remapping, can use
+ * this to initialise their own domain appropriately. Users should reserve a
+ * contiguous IOVA region, starting at @base, large enough to accommodate the
+ * number of PAGE_SIZE mappings necessary to cover every MSI doorbell address
+ * used by the devices attached to @domain.
+ */
+int iommu_get_msi_cookie(struct iommu_domain *domain, dma_addr_t base)
+{
+ struct iommu_dma_cookie *cookie;
+
+ if (domain->type != IOMMU_DOMAIN_UNMANAGED)
+ return -EINVAL;
+
+ if (domain->iova_cookie)
+ return -EEXIST;
+
+ cookie = cookie_alloc(IOMMU_DMA_MSI_COOKIE);
+ if (!cookie)
+ return -ENOMEM;
+
+ cookie->msi_iova = base;
+ domain->iova_cookie = cookie;
+ return 0;
+}
+EXPORT_SYMBOL(iommu_get_msi_cookie);
+
+/**
* iommu_put_dma_cookie - Release a domain's DMA mapping resources
- * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie()
+ * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie() or
+ * iommu_get_msi_cookie()
*
* IOMMU drivers should normally call this from their domain_free callback.
*/
@@ -92,7 +155,7 @@
if (!cookie)
return;
- if (cookie->iovad.granule)
+ if (cookie->type == IOMMU_DMA_IOVA_COOKIE && cookie->iovad.granule)
put_iova_domain(&cookie->iovad);
list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) {
@@ -137,11 +200,13 @@
int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
u64 size, struct device *dev)
{
- struct iova_domain *iovad = cookie_iovad(domain);
+ struct iommu_dma_cookie *cookie = domain->iova_cookie;
+ struct iova_domain *iovad = &cookie->iovad;
unsigned long order, base_pfn, end_pfn;
+ bool pci = dev && dev_is_pci(dev);
- if (!iovad)
- return -ENODEV;
+ if (!cookie || cookie->type != IOMMU_DMA_IOVA_COOKIE)
+ return -EINVAL;
/* Use the smallest supported page size for IOVA granularity */
order = __ffs(domain->pgsize_bitmap);
@@ -161,19 +226,31 @@
end_pfn = min_t(unsigned long, end_pfn,
domain->geometry.aperture_end >> order);
}
+ /*
+ * PCI devices may have larger DMA masks, but still prefer allocating
+ * within a 32-bit mask to avoid DAC addressing. Such limitations don't
+ * apply to the typical platform device, so for those we may as well
+ * leave the cache limit at the top of their range to save an rb_last()
+ * traversal on every allocation.
+ */
+ if (pci)
+ end_pfn &= DMA_BIT_MASK(32) >> order;
- /* All we can safely do with an existing domain is enlarge it */
+ /* start_pfn is always nonzero for an already-initialised domain */
if (iovad->start_pfn) {
if (1UL << order != iovad->granule ||
- base_pfn != iovad->start_pfn ||
- end_pfn < iovad->dma_32bit_pfn) {
+ base_pfn != iovad->start_pfn) {
pr_warn("Incompatible range for DMA domain\n");
return -EFAULT;
}
- iovad->dma_32bit_pfn = end_pfn;
+ /*
+ * If we have devices with different DMA masks, move the free
+ * area cache limit down for the benefit of the smaller one.
+ */
+ iovad->dma_32bit_pfn = min(end_pfn, iovad->dma_32bit_pfn);
} else {
init_iova_domain(iovad, 1UL << order, base_pfn, end_pfn);
- if (dev && dev_is_pci(dev))
+ if (pci)
iova_reserve_pci_windows(to_pci_dev(dev), iovad);
}
return 0;
@@ -181,16 +258,22 @@
EXPORT_SYMBOL(iommu_dma_init_domain);
/**
- * dma_direction_to_prot - Translate DMA API directions to IOMMU API page flags
+ * dma_info_to_prot - Translate DMA API directions and attributes to IOMMU API
+ * page flags.
* @dir: Direction of DMA transfer
* @coherent: Is the DMA master cache-coherent?
+ * @attrs: DMA attributes for the mapping
*
* Return: corresponding IOMMU API page protection flags
*/
-int dma_direction_to_prot(enum dma_data_direction dir, bool coherent)
+int dma_info_to_prot(enum dma_data_direction dir, bool coherent,
+ unsigned long attrs)
{
int prot = coherent ? IOMMU_CACHE : 0;
+ if (attrs & DMA_ATTR_PRIVILEGED)
+ prot |= IOMMU_PRIV;
+
switch (dir) {
case DMA_BIDIRECTIONAL:
return prot | IOMMU_READ | IOMMU_WRITE;
@@ -204,19 +287,28 @@
}
static struct iova *__alloc_iova(struct iommu_domain *domain, size_t size,
- dma_addr_t dma_limit)
+ dma_addr_t dma_limit, struct device *dev)
{
struct iova_domain *iovad = cookie_iovad(domain);
unsigned long shift = iova_shift(iovad);
unsigned long length = iova_align(iovad, size) >> shift;
+ struct iova *iova = NULL;
if (domain->geometry.force_aperture)
dma_limit = min(dma_limit, domain->geometry.aperture_end);
+
+ /* Try to get PCI devices a SAC address */
+ if (dma_limit > DMA_BIT_MASK(32) && dev_is_pci(dev))
+ iova = alloc_iova(iovad, length, DMA_BIT_MASK(32) >> shift,
+ true);
/*
* Enforce size-alignment to be safe - there could perhaps be an
* attribute to control this per-device, or at least per-domain...
*/
- return alloc_iova(iovad, length, dma_limit >> shift, true);
+ if (!iova)
+ iova = alloc_iova(iovad, length, dma_limit >> shift, true);
+
+ return iova;
}
/* The IOVA allocator knows what we mapped, so just unmap whatever that was */
@@ -369,7 +461,7 @@
if (!pages)
return NULL;
- iova = __alloc_iova(domain, size, dev->coherent_dma_mask);
+ iova = __alloc_iova(domain, size, dev->coherent_dma_mask, dev);
if (!iova)
goto out_free_pages;
@@ -440,7 +532,7 @@
struct iova_domain *iovad = cookie_iovad(domain);
size_t iova_off = iova_offset(iovad, phys);
size_t len = iova_align(iovad, size + iova_off);
- struct iova *iova = __alloc_iova(domain, len, dma_get_mask(dev));
+ struct iova *iova = __alloc_iova(domain, len, dma_get_mask(dev), dev);
if (!iova)
return DMA_ERROR_CODE;
@@ -598,7 +690,7 @@
prev = s;
}
- iova = __alloc_iova(domain, iova_len, dma_get_mask(dev));
+ iova = __alloc_iova(domain, iova_len, dma_get_mask(dev), dev);
if (!iova)
goto out_restore_sg;
@@ -633,7 +725,7 @@
size_t size, enum dma_data_direction dir, unsigned long attrs)
{
return __iommu_dma_map(dev, phys, size,
- dma_direction_to_prot(dir, false) | IOMMU_MMIO);
+ dma_info_to_prot(dir, false, attrs) | IOMMU_MMIO);
}
void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle,
@@ -642,16 +734,6 @@
__iommu_dma_unmap(iommu_get_domain_for_dev(dev), handle);
}
-int iommu_dma_supported(struct device *dev, u64 mask)
-{
- /*
- * 'Special' IOMMUs which don't have the same addressing capability
- * as the CPU will have to wait until we have some way to query that
- * before they'll be able to use this framework.
- */
- return 1;
-}
-
int iommu_dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
{
return dma_addr == DMA_ERROR_CODE;
@@ -662,11 +744,12 @@
{
struct iommu_dma_cookie *cookie = domain->iova_cookie;
struct iommu_dma_msi_page *msi_page;
- struct iova_domain *iovad = &cookie->iovad;
+ struct iova_domain *iovad = cookie_iovad(domain);
struct iova *iova;
int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
+ size_t size = cookie_msi_granule(cookie);
- msi_addr &= ~(phys_addr_t)iova_mask(iovad);
+ msi_addr &= ~(phys_addr_t)(size - 1);
list_for_each_entry(msi_page, &cookie->msi_page_list, list)
if (msi_page->phys == msi_addr)
return msi_page;
@@ -675,13 +758,18 @@
if (!msi_page)
return NULL;
- iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));
- if (!iova)
- goto out_free_page;
-
msi_page->phys = msi_addr;
- msi_page->iova = iova_dma_addr(iovad, iova);
- if (iommu_map(domain, msi_page->iova, msi_addr, iovad->granule, prot))
+ if (iovad) {
+ iova = __alloc_iova(domain, size, dma_get_mask(dev), dev);
+ if (!iova)
+ goto out_free_page;
+ msi_page->iova = iova_dma_addr(iovad, iova);
+ } else {
+ msi_page->iova = cookie->msi_iova;
+ cookie->msi_iova += size;
+ }
+
+ if (iommu_map(domain, msi_page->iova, msi_addr, size, prot))
goto out_free_iova;
INIT_LIST_HEAD(&msi_page->list);
@@ -689,7 +777,10 @@
return msi_page;
out_free_iova:
- __free_iova(iovad, iova);
+ if (iovad)
+ __free_iova(iovad, iova);
+ else
+ cookie->msi_iova -= size;
out_free_page:
kfree(msi_page);
return NULL;
@@ -730,7 +821,7 @@
msg->data = ~0U;
} else {
msg->address_hi = upper_32_bits(msi_page->iova);
- msg->address_lo &= iova_mask(&cookie->iovad);
+ msg->address_lo &= cookie_msi_granule(cookie) - 1;
msg->address_lo += lower_32_bits(msi_page->iova);
}
}
diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index 8ccbd70..d9c0dec 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -74,6 +74,8 @@
static int alloc_iommu(struct dmar_drhd_unit *drhd);
static void free_iommu(struct intel_iommu *iommu);
+extern const struct iommu_ops intel_iommu_ops;
+
static void dmar_register_drhd_unit(struct dmar_drhd_unit *drhd)
{
/*
@@ -1078,14 +1080,17 @@
raw_spin_lock_init(&iommu->register_lock);
if (intel_iommu_enabled) {
- iommu->iommu_dev = iommu_device_create(NULL, iommu,
- intel_iommu_groups,
- "%s", iommu->name);
-
- if (IS_ERR(iommu->iommu_dev)) {
- err = PTR_ERR(iommu->iommu_dev);
+ err = iommu_device_sysfs_add(&iommu->iommu, NULL,
+ intel_iommu_groups,
+ "%s", iommu->name);
+ if (err)
goto err_unmap;
- }
+
+ iommu_device_set_ops(&iommu->iommu, &intel_iommu_ops);
+
+ err = iommu_device_register(&iommu->iommu);
+ if (err)
+ goto err_unmap;
}
drhd->iommu = iommu;
@@ -1103,7 +1108,8 @@
static void free_iommu(struct intel_iommu *iommu)
{
- iommu_device_destroy(iommu->iommu_dev);
+ iommu_device_sysfs_remove(&iommu->iommu);
+ iommu_device_unregister(&iommu->iommu);
if (iommu->irq) {
if (iommu->pr_irq) {
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 57ba0d3..a7e0821 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -276,6 +276,8 @@
struct list_head owner_node; /* node for owner controllers list */
phys_addr_t pgtable; /* assigned page table structure */
unsigned int version; /* our version */
+
+ struct iommu_device iommu; /* IOMMU core handle */
};
static struct exynos_iommu_domain *to_exynos_domain(struct iommu_domain *dom)
@@ -381,13 +383,14 @@
{
sysmmu_pte_t *ent;
- dev_err(data->sysmmu, "%s FAULT occurred at %#x (page table base: %pa)\n",
- finfo->name, fault_addr, &data->pgtable);
+ dev_err(data->sysmmu, "%s: %s FAULT occurred at %#x\n",
+ dev_name(data->master), finfo->name, fault_addr);
+ dev_dbg(data->sysmmu, "Page table base: %pa\n", &data->pgtable);
ent = section_entry(phys_to_virt(data->pgtable), fault_addr);
- dev_err(data->sysmmu, "\tLv1 entry: %#x\n", *ent);
+ dev_dbg(data->sysmmu, "\tLv1 entry: %#x\n", *ent);
if (lv1ent_page(ent)) {
ent = page_entry(ent, fault_addr);
- dev_err(data->sysmmu, "\t Lv2 entry: %#x\n", *ent);
+ dev_dbg(data->sysmmu, "\t Lv2 entry: %#x\n", *ent);
}
}
@@ -611,6 +614,18 @@
data->sysmmu = dev;
spin_lock_init(&data->lock);
+ ret = iommu_device_sysfs_add(&data->iommu, &pdev->dev, NULL,
+ dev_name(data->sysmmu));
+ if (ret)
+ return ret;
+
+ iommu_device_set_ops(&data->iommu, &exynos_iommu_ops);
+ iommu_device_set_fwnode(&data->iommu, &dev->of_node->fwnode);
+
+ ret = iommu_device_register(&data->iommu);
+ if (ret)
+ return ret;
+
platform_set_drvdata(pdev, data);
__sysmmu_get_version(data);
@@ -628,8 +643,6 @@
pm_runtime_enable(dev);
- of_iommu_set_ops(dev->of_node, &exynos_iommu_ops);
-
return 0;
}
@@ -743,6 +756,8 @@
DMA_TO_DEVICE);
/* For mapping page table entries we rely on dma == phys */
BUG_ON(handle != virt_to_phys(domain->pgtable));
+ if (dma_mapping_error(dma_dev, handle))
+ goto err_lv2ent;
spin_lock_init(&domain->lock);
spin_lock_init(&domain->pgtablelock);
@@ -754,6 +769,8 @@
return &domain->domain;
+err_lv2ent:
+ free_pages((unsigned long)domain->lv2entcnt, 1);
err_counter:
free_pages((unsigned long)domain->pgtable, 2);
err_dma_cookie:
@@ -897,6 +914,7 @@
}
if (lv1ent_fault(sent)) {
+ dma_addr_t handle;
sysmmu_pte_t *pent;
bool need_flush_flpd_cache = lv1ent_zero(sent);
@@ -908,7 +926,12 @@
update_pte(sent, mk_lv1ent_page(virt_to_phys(pent)));
kmemleak_ignore(pent);
*pgcounter = NUM_LV2ENTRIES;
- dma_map_single(dma_dev, pent, LV2TABLE_SIZE, DMA_TO_DEVICE);
+ handle = dma_map_single(dma_dev, pent, LV2TABLE_SIZE,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dma_dev, handle)) {
+ kmem_cache_free(lv2table_kmem_cache, pent);
+ return ERR_PTR(-EADDRINUSE);
+ }
/*
* If pre-fetched SLPD is a faulty SLPD in zero_l2_table,
@@ -1231,9 +1254,21 @@
static void exynos_iommu_remove_device(struct device *dev)
{
+ struct exynos_iommu_owner *owner = dev->archdata.iommu;
+
if (!has_sysmmu(dev))
return;
+ if (owner->domain) {
+ struct iommu_group *group = iommu_group_get(dev);
+
+ if (group) {
+ WARN_ON(owner->domain !=
+ iommu_group_default_domain(group));
+ exynos_iommu_detach_device(owner->domain, dev);
+ iommu_group_put(group);
+ }
+ }
iommu_group_remove_device(dev);
}
@@ -1242,7 +1277,7 @@
{
struct exynos_iommu_owner *owner = dev->archdata.iommu;
struct platform_device *sysmmu = of_find_device_by_node(spec->np);
- struct sysmmu_drvdata *data;
+ struct sysmmu_drvdata *data, *entry;
if (!sysmmu)
return -ENODEV;
@@ -1261,6 +1296,10 @@
dev->archdata.iommu = owner;
}
+ list_for_each_entry(entry, &owner->controllers, owner_node)
+ if (entry == data)
+ return 0;
+
list_add_tail(&data->owner_node, &owner->controllers);
data->master = dev;
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 8a18525..f5e02f8 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -440,6 +440,7 @@
u64 end_address; /* reserved end address */
struct dmar_dev_scope *devices; /* target devices */
int devices_cnt; /* target device count */
+ struct iommu_resv_region *resv; /* reserved region handle */
};
struct dmar_atsr_unit {
@@ -547,7 +548,7 @@
static DEFINE_SPINLOCK(device_domain_lock);
static LIST_HEAD(device_domain_list);
-static const struct iommu_ops intel_iommu_ops;
+const struct iommu_ops intel_iommu_ops;
static bool translation_pre_enabled(struct intel_iommu *iommu)
{
@@ -1144,7 +1145,7 @@
if (!dma_pte_present(pte) || dma_pte_superpage(pte))
goto next;
- level_pfn = pfn & level_mask(level - 1);
+ level_pfn = pfn & level_mask(level);
level_pte = phys_to_virt(dma_pte_addr(pte));
if (level > 2)
@@ -3325,13 +3326,14 @@
iommu_identity_mapping |= IDENTMAP_GFX;
#endif
+ check_tylersburg_isoch();
+
if (iommu_identity_mapping) {
ret = si_domain_init(hw_pass_through);
if (ret)
goto free_iommu;
}
- check_tylersburg_isoch();
/*
* If we copied translations from a previous kernel in the kdump
@@ -4246,27 +4248,40 @@
int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header, void *arg)
{
struct acpi_dmar_reserved_memory *rmrr;
+ int prot = DMA_PTE_READ|DMA_PTE_WRITE;
struct dmar_rmrr_unit *rmrru;
+ size_t length;
rmrru = kzalloc(sizeof(*rmrru), GFP_KERNEL);
if (!rmrru)
- return -ENOMEM;
+ goto out;
rmrru->hdr = header;
rmrr = (struct acpi_dmar_reserved_memory *)header;
rmrru->base_address = rmrr->base_address;
rmrru->end_address = rmrr->end_address;
+
+ length = rmrr->end_address - rmrr->base_address + 1;
+ rmrru->resv = iommu_alloc_resv_region(rmrr->base_address, length, prot,
+ IOMMU_RESV_DIRECT);
+ if (!rmrru->resv)
+ goto free_rmrru;
+
rmrru->devices = dmar_alloc_dev_scope((void *)(rmrr + 1),
((void *)rmrr) + rmrr->header.length,
&rmrru->devices_cnt);
- if (rmrru->devices_cnt && rmrru->devices == NULL) {
- kfree(rmrru);
- return -ENOMEM;
- }
+ if (rmrru->devices_cnt && rmrru->devices == NULL)
+ goto free_all;
list_add(&rmrru->list, &dmar_rmrr_units);
return 0;
+free_all:
+ kfree(rmrru->resv);
+free_rmrru:
+ kfree(rmrru);
+out:
+ return -ENOMEM;
}
static struct dmar_atsr_unit *dmar_find_atsr(struct acpi_dmar_atsr *atsr)
@@ -4480,6 +4495,7 @@
list_for_each_entry_safe(rmrru, rmrr_n, &dmar_rmrr_units, list) {
list_del(&rmrru->list);
dmar_free_dev_scope(&rmrru->devices, &rmrru->devices_cnt);
+ kfree(rmrru->resv);
kfree(rmrru);
}
@@ -4853,10 +4869,13 @@
init_iommu_pm_ops();
- for_each_active_iommu(iommu, drhd)
- iommu->iommu_dev = iommu_device_create(NULL, iommu,
- intel_iommu_groups,
- "%s", iommu->name);
+ for_each_active_iommu(iommu, drhd) {
+ iommu_device_sysfs_add(&iommu->iommu, NULL,
+ intel_iommu_groups,
+ "%s", iommu->name);
+ iommu_device_set_ops(&iommu->iommu, &intel_iommu_ops);
+ iommu_device_register(&iommu->iommu);
+ }
bus_set_iommu(&pci_bus_type, &intel_iommu_ops);
bus_register_notifier(&pci_bus_type, &device_nb);
@@ -5178,7 +5197,7 @@
if (!iommu)
return -ENODEV;
- iommu_device_link(iommu->iommu_dev, dev);
+ iommu_device_link(&iommu->iommu, dev);
group = iommu_group_get_for_dev(dev);
@@ -5200,7 +5219,46 @@
iommu_group_remove_device(dev);
- iommu_device_unlink(iommu->iommu_dev, dev);
+ iommu_device_unlink(&iommu->iommu, dev);
+}
+
+static void intel_iommu_get_resv_regions(struct device *device,
+ struct list_head *head)
+{
+ struct iommu_resv_region *reg;
+ struct dmar_rmrr_unit *rmrr;
+ struct device *i_dev;
+ int i;
+
+ rcu_read_lock();
+ for_each_rmrr_units(rmrr) {
+ for_each_active_dev_scope(rmrr->devices, rmrr->devices_cnt,
+ i, i_dev) {
+ if (i_dev != device)
+ continue;
+
+ list_add_tail(&rmrr->resv->list, head);
+ }
+ }
+ rcu_read_unlock();
+
+ reg = iommu_alloc_resv_region(IOAPIC_RANGE_START,
+ IOAPIC_RANGE_END - IOAPIC_RANGE_START + 1,
+ 0, IOMMU_RESV_RESERVED);
+ if (!reg)
+ return;
+ list_add_tail(®->list, head);
+}
+
+static void intel_iommu_put_resv_regions(struct device *dev,
+ struct list_head *head)
+{
+ struct iommu_resv_region *entry, *next;
+
+ list_for_each_entry_safe(entry, next, head, list) {
+ if (entry->type == IOMMU_RESV_RESERVED)
+ kfree(entry);
+ }
}
#ifdef CONFIG_INTEL_IOMMU_SVM
@@ -5332,20 +5390,22 @@
}
#endif /* CONFIG_INTEL_IOMMU_SVM */
-static const struct iommu_ops intel_iommu_ops = {
- .capable = intel_iommu_capable,
- .domain_alloc = intel_iommu_domain_alloc,
- .domain_free = intel_iommu_domain_free,
- .attach_dev = intel_iommu_attach_device,
- .detach_dev = intel_iommu_detach_device,
- .map = intel_iommu_map,
- .unmap = intel_iommu_unmap,
- .map_sg = default_iommu_map_sg,
- .iova_to_phys = intel_iommu_iova_to_phys,
- .add_device = intel_iommu_add_device,
- .remove_device = intel_iommu_remove_device,
- .device_group = pci_device_group,
- .pgsize_bitmap = INTEL_IOMMU_PGSIZES,
+const struct iommu_ops intel_iommu_ops = {
+ .capable = intel_iommu_capable,
+ .domain_alloc = intel_iommu_domain_alloc,
+ .domain_free = intel_iommu_domain_free,
+ .attach_dev = intel_iommu_attach_device,
+ .detach_dev = intel_iommu_detach_device,
+ .map = intel_iommu_map,
+ .unmap = intel_iommu_unmap,
+ .map_sg = default_iommu_map_sg,
+ .iova_to_phys = intel_iommu_iova_to_phys,
+ .add_device = intel_iommu_add_device,
+ .remove_device = intel_iommu_remove_device,
+ .get_resv_regions = intel_iommu_get_resv_regions,
+ .put_resv_regions = intel_iommu_put_resv_regions,
+ .device_group = pci_device_group,
+ .pgsize_bitmap = INTEL_IOMMU_PGSIZES,
};
static void quirk_iommu_g4x_gfx(struct pci_dev *dev)
diff --git a/drivers/iommu/io-pgtable-arm-v7s.c b/drivers/iommu/io-pgtable-arm-v7s.c
index 0769276..1c049e2 100644
--- a/drivers/iommu/io-pgtable-arm-v7s.c
+++ b/drivers/iommu/io-pgtable-arm-v7s.c
@@ -265,7 +265,9 @@
if (!(prot & IOMMU_MMIO))
pte |= ARM_V7S_ATTR_TEX(1);
if (ap) {
- pte |= ARM_V7S_PTE_AF | ARM_V7S_PTE_AP_UNPRIV;
+ pte |= ARM_V7S_PTE_AF;
+ if (!(prot & IOMMU_PRIV))
+ pte |= ARM_V7S_PTE_AP_UNPRIV;
if (!(prot & IOMMU_WRITE))
pte |= ARM_V7S_PTE_AP_RDONLY;
}
@@ -288,6 +290,8 @@
if (!(attr & ARM_V7S_PTE_AP_RDONLY))
prot |= IOMMU_WRITE;
+ if (!(attr & ARM_V7S_PTE_AP_UNPRIV))
+ prot |= IOMMU_PRIV;
if ((attr & (ARM_V7S_TEX_MASK << ARM_V7S_TEX_SHIFT)) == 0)
prot |= IOMMU_MMIO;
else if (pte & ARM_V7S_ATTR_C)
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index a40ce34..feacc54 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -350,11 +350,14 @@
if (data->iop.fmt == ARM_64_LPAE_S1 ||
data->iop.fmt == ARM_32_LPAE_S1) {
- pte = ARM_LPAE_PTE_AP_UNPRIV | ARM_LPAE_PTE_nG;
+ pte = ARM_LPAE_PTE_nG;
if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ))
pte |= ARM_LPAE_PTE_AP_RDONLY;
+ if (!(prot & IOMMU_PRIV))
+ pte |= ARM_LPAE_PTE_AP_UNPRIV;
+
if (prot & IOMMU_MMIO)
pte |= (ARM_LPAE_MAIR_ATTR_IDX_DEV
<< ARM_LPAE_PTE_ATTRINDX_SHIFT);
diff --git a/drivers/iommu/iommu-sysfs.c b/drivers/iommu/iommu-sysfs.c
index 39b2d91..c58351e 100644
--- a/drivers/iommu/iommu-sysfs.c
+++ b/drivers/iommu/iommu-sysfs.c
@@ -50,85 +50,76 @@
postcore_initcall(iommu_dev_init);
/*
- * Create an IOMMU device and return a pointer to it. IOMMU specific
- * attributes can be provided as an attribute group, allowing a unique
- * namespace per IOMMU type.
+ * Init the struct device for the IOMMU. IOMMU specific attributes can
+ * be provided as an attribute group, allowing a unique namespace per
+ * IOMMU type.
*/
-struct device *iommu_device_create(struct device *parent, void *drvdata,
- const struct attribute_group **groups,
- const char *fmt, ...)
+int iommu_device_sysfs_add(struct iommu_device *iommu,
+ struct device *parent,
+ const struct attribute_group **groups,
+ const char *fmt, ...)
{
- struct device *dev;
va_list vargs;
int ret;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return ERR_PTR(-ENOMEM);
+ device_initialize(&iommu->dev);
- device_initialize(dev);
-
- dev->class = &iommu_class;
- dev->parent = parent;
- dev->groups = groups;
- dev_set_drvdata(dev, drvdata);
+ iommu->dev.class = &iommu_class;
+ iommu->dev.parent = parent;
+ iommu->dev.groups = groups;
va_start(vargs, fmt);
- ret = kobject_set_name_vargs(&dev->kobj, fmt, vargs);
+ ret = kobject_set_name_vargs(&iommu->dev.kobj, fmt, vargs);
va_end(vargs);
if (ret)
goto error;
- ret = device_add(dev);
+ ret = device_add(&iommu->dev);
if (ret)
goto error;
- return dev;
+ return 0;
error:
- put_device(dev);
- return ERR_PTR(ret);
+ put_device(&iommu->dev);
+ return ret;
}
-void iommu_device_destroy(struct device *dev)
+void iommu_device_sysfs_remove(struct iommu_device *iommu)
{
- if (!dev || IS_ERR(dev))
- return;
-
- device_unregister(dev);
+ device_unregister(&iommu->dev);
}
-
/*
* IOMMU drivers can indicate a device is managed by a given IOMMU using
* this interface. A link to the device will be created in the "devices"
* directory of the IOMMU device in sysfs and an "iommu" link will be
* created under the linked device, pointing back at the IOMMU device.
*/
-int iommu_device_link(struct device *dev, struct device *link)
+int iommu_device_link(struct iommu_device *iommu, struct device *link)
{
int ret;
- if (!dev || IS_ERR(dev))
+ if (!iommu || IS_ERR(iommu))
return -ENODEV;
- ret = sysfs_add_link_to_group(&dev->kobj, "devices",
+ ret = sysfs_add_link_to_group(&iommu->dev.kobj, "devices",
&link->kobj, dev_name(link));
if (ret)
return ret;
- ret = sysfs_create_link_nowarn(&link->kobj, &dev->kobj, "iommu");
+ ret = sysfs_create_link_nowarn(&link->kobj, &iommu->dev.kobj, "iommu");
if (ret)
- sysfs_remove_link_from_group(&dev->kobj, "devices",
+ sysfs_remove_link_from_group(&iommu->dev.kobj, "devices",
dev_name(link));
return ret;
}
-void iommu_device_unlink(struct device *dev, struct device *link)
+void iommu_device_unlink(struct iommu_device *iommu, struct device *link)
{
- if (!dev || IS_ERR(dev))
+ if (!iommu || IS_ERR(iommu))
return;
sysfs_remove_link(&link->kobj, "iommu");
- sysfs_remove_link_from_group(&dev->kobj, "devices", dev_name(link));
+ sysfs_remove_link_from_group(&iommu->dev.kobj, "devices", dev_name(link));
}
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index dbe7f65..8ea14f4 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -55,7 +55,7 @@
struct iommu_domain *domain;
};
-struct iommu_device {
+struct group_device {
struct list_head list;
struct device *dev;
char *name;
@@ -68,6 +68,12 @@
const char *buf, size_t count);
};
+static const char * const iommu_group_resv_type_string[] = {
+ [IOMMU_RESV_DIRECT] = "direct",
+ [IOMMU_RESV_RESERVED] = "reserved",
+ [IOMMU_RESV_MSI] = "msi",
+};
+
#define IOMMU_GROUP_ATTR(_name, _mode, _show, _store) \
struct iommu_group_attribute iommu_group_attr_##_name = \
__ATTR(_name, _mode, _show, _store)
@@ -77,6 +83,25 @@
#define to_iommu_group(_kobj) \
container_of(_kobj, struct iommu_group, kobj)
+static LIST_HEAD(iommu_device_list);
+static DEFINE_SPINLOCK(iommu_device_lock);
+
+int iommu_device_register(struct iommu_device *iommu)
+{
+ spin_lock(&iommu_device_lock);
+ list_add_tail(&iommu->list, &iommu_device_list);
+ spin_unlock(&iommu_device_lock);
+
+ return 0;
+}
+
+void iommu_device_unregister(struct iommu_device *iommu)
+{
+ spin_lock(&iommu_device_lock);
+ list_del(&iommu->list);
+ spin_unlock(&iommu_device_lock);
+}
+
static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus,
unsigned type);
static int __iommu_attach_device(struct iommu_domain *domain,
@@ -133,8 +158,131 @@
return sprintf(buf, "%s\n", group->name);
}
+/**
+ * iommu_insert_resv_region - Insert a new region in the
+ * list of reserved regions.
+ * @new: new region to insert
+ * @regions: list of regions
+ *
+ * The new element is sorted by address with respect to the other
+ * regions of the same type. In case it overlaps with another
+ * region of the same type, regions are merged. In case it
+ * overlaps with another region of different type, regions are
+ * not merged.
+ */
+static int iommu_insert_resv_region(struct iommu_resv_region *new,
+ struct list_head *regions)
+{
+ struct iommu_resv_region *region;
+ phys_addr_t start = new->start;
+ phys_addr_t end = new->start + new->length - 1;
+ struct list_head *pos = regions->next;
+
+ while (pos != regions) {
+ struct iommu_resv_region *entry =
+ list_entry(pos, struct iommu_resv_region, list);
+ phys_addr_t a = entry->start;
+ phys_addr_t b = entry->start + entry->length - 1;
+ int type = entry->type;
+
+ if (end < a) {
+ goto insert;
+ } else if (start > b) {
+ pos = pos->next;
+ } else if ((start >= a) && (end <= b)) {
+ if (new->type == type)
+ goto done;
+ else
+ pos = pos->next;
+ } else {
+ if (new->type == type) {
+ phys_addr_t new_start = min(a, start);
+ phys_addr_t new_end = max(b, end);
+
+ list_del(&entry->list);
+ entry->start = new_start;
+ entry->length = new_end - new_start + 1;
+ iommu_insert_resv_region(entry, regions);
+ } else {
+ pos = pos->next;
+ }
+ }
+ }
+insert:
+ region = iommu_alloc_resv_region(new->start, new->length,
+ new->prot, new->type);
+ if (!region)
+ return -ENOMEM;
+
+ list_add_tail(®ion->list, pos);
+done:
+ return 0;
+}
+
+static int
+iommu_insert_device_resv_regions(struct list_head *dev_resv_regions,
+ struct list_head *group_resv_regions)
+{
+ struct iommu_resv_region *entry;
+ int ret = 0;
+
+ list_for_each_entry(entry, dev_resv_regions, list) {
+ ret = iommu_insert_resv_region(entry, group_resv_regions);
+ if (ret)
+ break;
+ }
+ return ret;
+}
+
+int iommu_get_group_resv_regions(struct iommu_group *group,
+ struct list_head *head)
+{
+ struct group_device *device;
+ int ret = 0;
+
+ mutex_lock(&group->mutex);
+ list_for_each_entry(device, &group->devices, list) {
+ struct list_head dev_resv_regions;
+
+ INIT_LIST_HEAD(&dev_resv_regions);
+ iommu_get_resv_regions(device->dev, &dev_resv_regions);
+ ret = iommu_insert_device_resv_regions(&dev_resv_regions, head);
+ iommu_put_resv_regions(device->dev, &dev_resv_regions);
+ if (ret)
+ break;
+ }
+ mutex_unlock(&group->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iommu_get_group_resv_regions);
+
+static ssize_t iommu_group_show_resv_regions(struct iommu_group *group,
+ char *buf)
+{
+ struct iommu_resv_region *region, *next;
+ struct list_head group_resv_regions;
+ char *str = buf;
+
+ INIT_LIST_HEAD(&group_resv_regions);
+ iommu_get_group_resv_regions(group, &group_resv_regions);
+
+ list_for_each_entry_safe(region, next, &group_resv_regions, list) {
+ str += sprintf(str, "0x%016llx 0x%016llx %s\n",
+ (long long int)region->start,
+ (long long int)(region->start +
+ region->length - 1),
+ iommu_group_resv_type_string[region->type]);
+ kfree(region);
+ }
+
+ return (str - buf);
+}
+
static IOMMU_GROUP_ATTR(name, S_IRUGO, iommu_group_show_name, NULL);
+static IOMMU_GROUP_ATTR(reserved_regions, 0444,
+ iommu_group_show_resv_regions, NULL);
+
static void iommu_group_release(struct kobject *kobj)
{
struct iommu_group *group = to_iommu_group(kobj);
@@ -212,6 +360,11 @@
*/
kobject_put(&group->kobj);
+ ret = iommu_group_create_file(group,
+ &iommu_group_attr_reserved_regions);
+ if (ret)
+ return ERR_PTR(ret);
+
pr_debug("Allocated group %d\n", group->id);
return group;
@@ -318,7 +471,7 @@
struct device *dev)
{
struct iommu_domain *domain = group->default_domain;
- struct iommu_dm_region *entry;
+ struct iommu_resv_region *entry;
struct list_head mappings;
unsigned long pg_size;
int ret = 0;
@@ -331,18 +484,21 @@
pg_size = 1UL << __ffs(domain->pgsize_bitmap);
INIT_LIST_HEAD(&mappings);
- iommu_get_dm_regions(dev, &mappings);
+ iommu_get_resv_regions(dev, &mappings);
/* We need to consider overlapping regions for different devices */
list_for_each_entry(entry, &mappings, list) {
dma_addr_t start, end, addr;
- if (domain->ops->apply_dm_region)
- domain->ops->apply_dm_region(dev, domain, entry);
+ if (domain->ops->apply_resv_region)
+ domain->ops->apply_resv_region(dev, domain, entry);
start = ALIGN(entry->start, pg_size);
end = ALIGN(entry->start + entry->length, pg_size);
+ if (entry->type != IOMMU_RESV_DIRECT)
+ continue;
+
for (addr = start; addr < end; addr += pg_size) {
phys_addr_t phys_addr;
@@ -358,7 +514,7 @@
}
out:
- iommu_put_dm_regions(dev, &mappings);
+ iommu_put_resv_regions(dev, &mappings);
return ret;
}
@@ -374,7 +530,7 @@
int iommu_group_add_device(struct iommu_group *group, struct device *dev)
{
int ret, i = 0;
- struct iommu_device *device;
+ struct group_device *device;
device = kzalloc(sizeof(*device), GFP_KERNEL);
if (!device)
@@ -383,36 +539,30 @@
device->dev = dev;
ret = sysfs_create_link(&dev->kobj, &group->kobj, "iommu_group");
- if (ret) {
- kfree(device);
- return ret;
- }
+ if (ret)
+ goto err_free_device;
device->name = kasprintf(GFP_KERNEL, "%s", kobject_name(&dev->kobj));
rename:
if (!device->name) {
- sysfs_remove_link(&dev->kobj, "iommu_group");
- kfree(device);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_remove_link;
}
ret = sysfs_create_link_nowarn(group->devices_kobj,
&dev->kobj, device->name);
if (ret) {
- kfree(device->name);
if (ret == -EEXIST && i >= 0) {
/*
* Account for the slim chance of collision
* and append an instance to the name.
*/
+ kfree(device->name);
device->name = kasprintf(GFP_KERNEL, "%s.%d",
kobject_name(&dev->kobj), i++);
goto rename;
}
-
- sysfs_remove_link(&dev->kobj, "iommu_group");
- kfree(device);
- return ret;
+ goto err_free_name;
}
kobject_get(group->devices_kobj);
@@ -424,8 +574,10 @@
mutex_lock(&group->mutex);
list_add_tail(&device->list, &group->devices);
if (group->domain)
- __iommu_attach_device(group->domain, dev);
+ ret = __iommu_attach_device(group->domain, dev);
mutex_unlock(&group->mutex);
+ if (ret)
+ goto err_put_group;
/* Notify any listeners about change to group. */
blocking_notifier_call_chain(&group->notifier,
@@ -436,6 +588,21 @@
pr_info("Adding device %s to group %d\n", dev_name(dev), group->id);
return 0;
+
+err_put_group:
+ mutex_lock(&group->mutex);
+ list_del(&device->list);
+ mutex_unlock(&group->mutex);
+ dev->iommu_group = NULL;
+ kobject_put(group->devices_kobj);
+err_free_name:
+ kfree(device->name);
+err_remove_link:
+ sysfs_remove_link(&dev->kobj, "iommu_group");
+err_free_device:
+ kfree(device);
+ pr_err("Failed to add device %s to group %d: %d\n", dev_name(dev), group->id, ret);
+ return ret;
}
EXPORT_SYMBOL_GPL(iommu_group_add_device);
@@ -449,7 +616,7 @@
void iommu_group_remove_device(struct device *dev)
{
struct iommu_group *group = dev->iommu_group;
- struct iommu_device *tmp_device, *device = NULL;
+ struct group_device *tmp_device, *device = NULL;
pr_info("Removing device %s from group %d\n", dev_name(dev), group->id);
@@ -484,7 +651,7 @@
static int iommu_group_device_count(struct iommu_group *group)
{
- struct iommu_device *entry;
+ struct group_device *entry;
int ret = 0;
list_for_each_entry(entry, &group->devices, list)
@@ -507,7 +674,7 @@
static int __iommu_group_for_each_dev(struct iommu_group *group, void *data,
int (*fn)(struct device *, void *))
{
- struct iommu_device *device;
+ struct group_device *device;
int ret = 0;
list_for_each_entry(device, &group->devices, list) {
@@ -1559,20 +1726,38 @@
}
EXPORT_SYMBOL_GPL(iommu_domain_set_attr);
-void iommu_get_dm_regions(struct device *dev, struct list_head *list)
+void iommu_get_resv_regions(struct device *dev, struct list_head *list)
{
const struct iommu_ops *ops = dev->bus->iommu_ops;
- if (ops && ops->get_dm_regions)
- ops->get_dm_regions(dev, list);
+ if (ops && ops->get_resv_regions)
+ ops->get_resv_regions(dev, list);
}
-void iommu_put_dm_regions(struct device *dev, struct list_head *list)
+void iommu_put_resv_regions(struct device *dev, struct list_head *list)
{
const struct iommu_ops *ops = dev->bus->iommu_ops;
- if (ops && ops->put_dm_regions)
- ops->put_dm_regions(dev, list);
+ if (ops && ops->put_resv_regions)
+ ops->put_resv_regions(dev, list);
+}
+
+struct iommu_resv_region *iommu_alloc_resv_region(phys_addr_t start,
+ size_t length,
+ int prot, int type)
+{
+ struct iommu_resv_region *region;
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return NULL;
+
+ INIT_LIST_HEAD(®ion->list);
+ region->start = start;
+ region->length = length;
+ region->prot = prot;
+ region->type = type;
+ return region;
}
/* Request that a device is direct mapped by the IOMMU */
@@ -1628,43 +1813,18 @@
return ret;
}
-struct iommu_instance {
- struct list_head list;
- struct fwnode_handle *fwnode;
- const struct iommu_ops *ops;
-};
-static LIST_HEAD(iommu_instance_list);
-static DEFINE_SPINLOCK(iommu_instance_lock);
-
-void iommu_register_instance(struct fwnode_handle *fwnode,
- const struct iommu_ops *ops)
+const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode)
{
- struct iommu_instance *iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
-
- if (WARN_ON(!iommu))
- return;
-
- of_node_get(to_of_node(fwnode));
- INIT_LIST_HEAD(&iommu->list);
- iommu->fwnode = fwnode;
- iommu->ops = ops;
- spin_lock(&iommu_instance_lock);
- list_add_tail(&iommu->list, &iommu_instance_list);
- spin_unlock(&iommu_instance_lock);
-}
-
-const struct iommu_ops *iommu_get_instance(struct fwnode_handle *fwnode)
-{
- struct iommu_instance *instance;
const struct iommu_ops *ops = NULL;
+ struct iommu_device *iommu;
- spin_lock(&iommu_instance_lock);
- list_for_each_entry(instance, &iommu_instance_list, list)
- if (instance->fwnode == fwnode) {
- ops = instance->ops;
+ spin_lock(&iommu_device_lock);
+ list_for_each_entry(iommu, &iommu_device_list, list)
+ if (iommu->fwnode == fwnode) {
+ ops = iommu->ops;
break;
}
- spin_unlock(&iommu_instance_lock);
+ spin_unlock(&iommu_device_lock);
return ops;
}
@@ -1714,13 +1874,14 @@
fwspec = krealloc(dev->iommu_fwspec, size, GFP_KERNEL);
if (!fwspec)
return -ENOMEM;
+
+ dev->iommu_fwspec = fwspec;
}
for (i = 0; i < num_ids; i++)
fwspec->ids[fwspec->num_ids + i] = ids[i];
fwspec->num_ids += num_ids;
- dev->iommu_fwspec = fwspec;
return 0;
}
EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids);
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
index 080beca..b7268a1 100644
--- a/drivers/iommu/iova.c
+++ b/drivers/iommu/iova.c
@@ -62,7 +62,7 @@
else {
struct rb_node *prev_node = rb_prev(iovad->cached32_node);
struct iova *curr_iova =
- container_of(iovad->cached32_node, struct iova, node);
+ rb_entry(iovad->cached32_node, struct iova, node);
*limit_pfn = curr_iova->pfn_lo - 1;
return prev_node;
}
@@ -86,11 +86,11 @@
if (!iovad->cached32_node)
return;
curr = iovad->cached32_node;
- cached_iova = container_of(curr, struct iova, node);
+ cached_iova = rb_entry(curr, struct iova, node);
if (free->pfn_lo >= cached_iova->pfn_lo) {
struct rb_node *node = rb_next(&free->node);
- struct iova *iova = container_of(node, struct iova, node);
+ struct iova *iova = rb_entry(node, struct iova, node);
/* only cache if it's below 32bit pfn */
if (node && iova->pfn_lo < iovad->dma_32bit_pfn)
@@ -125,7 +125,7 @@
curr = __get_cached_rbnode(iovad, &limit_pfn);
prev = curr;
while (curr) {
- struct iova *curr_iova = container_of(curr, struct iova, node);
+ struct iova *curr_iova = rb_entry(curr, struct iova, node);
if (limit_pfn < curr_iova->pfn_lo)
goto move_left;
@@ -171,8 +171,7 @@
/* Figure out where to put new node */
while (*entry) {
- struct iova *this = container_of(*entry,
- struct iova, node);
+ struct iova *this = rb_entry(*entry, struct iova, node);
parent = *entry;
if (new->pfn_lo < this->pfn_lo)
@@ -201,7 +200,7 @@
struct rb_node **new = &(root->rb_node), *parent = NULL;
/* Figure out where to put new node */
while (*new) {
- struct iova *this = container_of(*new, struct iova, node);
+ struct iova *this = rb_entry(*new, struct iova, node);
parent = *new;
@@ -311,7 +310,7 @@
assert_spin_locked(&iovad->iova_rbtree_lock);
while (node) {
- struct iova *iova = container_of(node, struct iova, node);
+ struct iova *iova = rb_entry(node, struct iova, node);
/* If pfn falls within iova's range, return iova */
if ((pfn >= iova->pfn_lo) && (pfn <= iova->pfn_hi)) {
@@ -463,7 +462,7 @@
spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
node = rb_first(&iovad->rbroot);
while (node) {
- struct iova *iova = container_of(node, struct iova, node);
+ struct iova *iova = rb_entry(node, struct iova, node);
rb_erase(node, &iovad->rbroot);
free_iova_mem(iova);
@@ -477,7 +476,7 @@
__is_range_overlap(struct rb_node *node,
unsigned long pfn_lo, unsigned long pfn_hi)
{
- struct iova *iova = container_of(node, struct iova, node);
+ struct iova *iova = rb_entry(node, struct iova, node);
if ((pfn_lo <= iova->pfn_hi) && (pfn_hi >= iova->pfn_lo))
return 1;
@@ -541,7 +540,7 @@
spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
for (node = rb_first(&iovad->rbroot); node; node = rb_next(node)) {
if (__is_range_overlap(node, pfn_lo, pfn_hi)) {
- iova = container_of(node, struct iova, node);
+ iova = rb_entry(node, struct iova, node);
__adjust_overlap_range(iova, &pfn_lo, &pfn_hi);
if ((pfn_lo >= iova->pfn_lo) &&
(pfn_hi <= iova->pfn_hi))
@@ -578,7 +577,7 @@
spin_lock_irqsave(&from->iova_rbtree_lock, flags);
for (node = rb_first(&from->rbroot); node; node = rb_next(node)) {
- struct iova *iova = container_of(node, struct iova, node);
+ struct iova *iova = rb_entry(node, struct iova, node);
struct iova *new_iova;
new_iova = reserve_iova(to, iova->pfn_lo, iova->pfn_hi);
diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c
index ace331d..b7e14ee 100644
--- a/drivers/iommu/ipmmu-vmsa.c
+++ b/drivers/iommu/ipmmu-vmsa.c
@@ -313,6 +313,8 @@
domain->cfg.ias = 32;
domain->cfg.oas = 40;
domain->cfg.tlb = &ipmmu_gather_ops;
+ domain->io_domain.geometry.aperture_end = DMA_BIT_MASK(32);
+ domain->io_domain.geometry.force_aperture = true;
/*
* TODO: Add support for coherent walk through CCI with DVM and remove
* cache handling. For now, delegate it to the io-pgtable code.
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index b09692b..d044835 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -371,6 +371,58 @@
return 0;
}
+/* Must be called under msm_iommu_lock */
+static struct msm_iommu_dev *find_iommu_for_dev(struct device *dev)
+{
+ struct msm_iommu_dev *iommu, *ret = NULL;
+ struct msm_iommu_ctx_dev *master;
+
+ list_for_each_entry(iommu, &qcom_iommu_devices, dev_node) {
+ master = list_first_entry(&iommu->ctx_list,
+ struct msm_iommu_ctx_dev,
+ list);
+ if (master->of_node == dev->of_node) {
+ ret = iommu;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int msm_iommu_add_device(struct device *dev)
+{
+ struct msm_iommu_dev *iommu;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&msm_iommu_lock, flags);
+
+ iommu = find_iommu_for_dev(dev);
+ if (iommu)
+ iommu_device_link(&iommu->iommu, dev);
+ else
+ ret = -ENODEV;
+
+ spin_unlock_irqrestore(&msm_iommu_lock, flags);
+
+ return ret;
+}
+
+static void msm_iommu_remove_device(struct device *dev)
+{
+ struct msm_iommu_dev *iommu;
+ unsigned long flags;
+
+ spin_lock_irqsave(&msm_iommu_lock, flags);
+
+ iommu = find_iommu_for_dev(dev);
+ if (iommu)
+ iommu_device_unlink(&iommu->iommu, dev);
+
+ spin_unlock_irqrestore(&msm_iommu_lock, flags);
+}
+
static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
{
int ret = 0;
@@ -646,6 +698,8 @@
.unmap = msm_iommu_unmap,
.map_sg = default_iommu_map_sg,
.iova_to_phys = msm_iommu_iova_to_phys,
+ .add_device = msm_iommu_add_device,
+ .remove_device = msm_iommu_remove_device,
.pgsize_bitmap = MSM_IOMMU_PGSIZES,
.of_xlate = qcom_iommu_of_xlate,
};
@@ -653,6 +707,7 @@
static int msm_iommu_probe(struct platform_device *pdev)
{
struct resource *r;
+ resource_size_t ioaddr;
struct msm_iommu_dev *iommu;
int ret, par, val;
@@ -696,6 +751,7 @@
ret = PTR_ERR(iommu->base);
goto fail;
}
+ ioaddr = r->start;
iommu->irq = platform_get_irq(pdev, 0);
if (iommu->irq < 0) {
@@ -737,7 +793,22 @@
}
list_add(&iommu->dev_node, &qcom_iommu_devices);
- of_iommu_set_ops(pdev->dev.of_node, &msm_iommu_ops);
+
+ ret = iommu_device_sysfs_add(&iommu->iommu, iommu->dev, NULL,
+ "msm-smmu.%pa", &ioaddr);
+ if (ret) {
+ pr_err("Could not add msm-smmu at %pa to sysfs\n", &ioaddr);
+ goto fail;
+ }
+
+ iommu_device_set_ops(&iommu->iommu, &msm_iommu_ops);
+ iommu_device_set_fwnode(&iommu->iommu, &pdev->dev.of_node->fwnode);
+
+ ret = iommu_device_register(&iommu->iommu);
+ if (ret) {
+ pr_err("Could not register msm-smmu at %pa\n", &ioaddr);
+ goto fail;
+ }
pr_info("device mapped at %p, irq %d with %d ctx banks\n",
iommu->base, iommu->irq, iommu->ncb);
diff --git a/drivers/iommu/msm_iommu.h b/drivers/iommu/msm_iommu.h
index 4ca25d5..ae92d27 100644
--- a/drivers/iommu/msm_iommu.h
+++ b/drivers/iommu/msm_iommu.h
@@ -19,6 +19,7 @@
#define MSM_IOMMU_H
#include <linux/interrupt.h>
+#include <linux/iommu.h>
#include <linux/clk.h>
/* Sharability attributes of MSM IOMMU mappings */
@@ -68,6 +69,8 @@
struct list_head dom_node;
struct list_head ctx_list;
DECLARE_BITMAP(context_map, IOMMU_MAX_CBS);
+
+ struct iommu_device iommu;
};
/**
diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
index 1479c76..5d14cd1 100644
--- a/drivers/iommu/mtk_iommu.c
+++ b/drivers/iommu/mtk_iommu.c
@@ -360,11 +360,15 @@
static int mtk_iommu_add_device(struct device *dev)
{
+ struct mtk_iommu_data *data;
struct iommu_group *group;
if (!dev->iommu_fwspec || dev->iommu_fwspec->ops != &mtk_iommu_ops)
return -ENODEV; /* Not a iommu client device */
+ data = dev->iommu_fwspec->iommu_priv;
+ iommu_device_link(&data->iommu, dev);
+
group = iommu_group_get_for_dev(dev);
if (IS_ERR(group))
return PTR_ERR(group);
@@ -375,9 +379,14 @@
static void mtk_iommu_remove_device(struct device *dev)
{
+ struct mtk_iommu_data *data;
+
if (!dev->iommu_fwspec || dev->iommu_fwspec->ops != &mtk_iommu_ops)
return;
+ data = dev->iommu_fwspec->iommu_priv;
+ iommu_device_unlink(&data->iommu, dev);
+
iommu_group_remove_device(dev);
iommu_fwspec_free(dev);
}
@@ -497,6 +506,7 @@
struct mtk_iommu_data *data;
struct device *dev = &pdev->dev;
struct resource *res;
+ resource_size_t ioaddr;
struct component_match *match = NULL;
void *protect;
int i, larb_nr, ret;
@@ -519,6 +529,7 @@
data->base = devm_ioremap_resource(dev, res);
if (IS_ERR(data->base))
return PTR_ERR(data->base);
+ ioaddr = res->start;
data->irq = platform_get_irq(pdev, 0);
if (data->irq < 0)
@@ -567,6 +578,18 @@
if (ret)
return ret;
+ ret = iommu_device_sysfs_add(&data->iommu, dev, NULL,
+ "mtk-iommu.%pa", &ioaddr);
+ if (ret)
+ return ret;
+
+ iommu_device_set_ops(&data->iommu, &mtk_iommu_ops);
+ iommu_device_set_fwnode(&data->iommu, &pdev->dev.of_node->fwnode);
+
+ ret = iommu_device_register(&data->iommu);
+ if (ret)
+ return ret;
+
if (!iommu_present(&platform_bus_type))
bus_set_iommu(&platform_bus_type, &mtk_iommu_ops);
@@ -577,6 +600,9 @@
{
struct mtk_iommu_data *data = platform_get_drvdata(pdev);
+ iommu_device_sysfs_remove(&data->iommu);
+ iommu_device_unregister(&data->iommu);
+
if (iommu_present(&platform_bus_type))
bus_set_iommu(&platform_bus_type, NULL);
@@ -655,7 +681,6 @@
return ret;
}
- of_iommu_set_ops(np, &mtk_iommu_ops);
return 0;
}
diff --git a/drivers/iommu/mtk_iommu.h b/drivers/iommu/mtk_iommu.h
index 50177f7..2a28ead 100644
--- a/drivers/iommu/mtk_iommu.h
+++ b/drivers/iommu/mtk_iommu.h
@@ -47,6 +47,8 @@
struct iommu_group *m4u_group;
struct mtk_smi_iommu smi_imu; /* SMI larb iommu info */
bool enable_4GB;
+
+ struct iommu_device iommu;
};
static inline int compare_of(struct device *dev, void *data)
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 0f57ddc..2683e9f 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -127,7 +127,7 @@
"iommu-map-mask", &iommu_spec.np, iommu_spec.args))
return NULL;
- ops = of_iommu_get_ops(iommu_spec.np);
+ ops = iommu_ops_from_fwnode(&iommu_spec.np->fwnode);
if (!ops || !ops->of_xlate ||
iommu_fwspec_init(&pdev->dev, &iommu_spec.np->fwnode, ops) ||
ops->of_xlate(&pdev->dev, &iommu_spec))
@@ -157,7 +157,7 @@
"#iommu-cells", idx,
&iommu_spec)) {
np = iommu_spec.np;
- ops = of_iommu_get_ops(np);
+ ops = iommu_ops_from_fwnode(&np->fwnode);
if (!ops || !ops->of_xlate ||
iommu_fwspec_init(dev, &np->fwnode, ops) ||
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index ae96731..125528f 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -283,3 +283,12 @@
config STM32_EXTI
bool
select IRQ_DOMAIN
+
+config QCOM_IRQ_COMBINER
+ bool "QCOM IRQ combiner support"
+ depends on ARCH_QCOM && ACPI
+ select IRQ_DOMAIN
+ select IRQ_DOMAIN_HIERARCHY
+ help
+ Say yes here to add support for the IRQ combiner devices embedded
+ in Qualcomm Technologies chips.
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 0e55d94..152bc40 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o
obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o
obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o
+obj-$(CONFIG_ARCH_GEMINI) += irq-gemini.o
obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o
obj-$(CONFIG_ARCH_LPC32XX) += irq-lpc32xx.o
obj-$(CONFIG_ARCH_MMP) += irq-mmp.o
@@ -75,3 +76,4 @@
obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o
obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o
obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o
+obj-$(CONFIG_QCOM_IRQ_COMBINER) += qcom-irq-combiner.o
diff --git a/drivers/irqchip/irq-gemini.c b/drivers/irqchip/irq-gemini.c
new file mode 100644
index 0000000..495224c
--- /dev/null
+++ b/drivers/irqchip/irq-gemini.c
@@ -0,0 +1,185 @@
+/*
+ * irqchip for the Cortina Systems Gemini Copyright (C) 2017 Linus
+ * Walleij <linus.walleij@linaro.org>
+ *
+ * Based on arch/arm/mach-gemini/irq.c
+ * Copyright (C) 2001-2006 Storlink, Corp.
+ * Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ */
+#include <linux/bitops.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/versatile-fpga.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/cpu.h>
+
+#include <asm/exception.h>
+#include <asm/mach/irq.h>
+
+#define GEMINI_NUM_IRQS 32
+
+#define GEMINI_IRQ_SOURCE(base_addr) (base_addr + 0x00)
+#define GEMINI_IRQ_MASK(base_addr) (base_addr + 0x04)
+#define GEMINI_IRQ_CLEAR(base_addr) (base_addr + 0x08)
+#define GEMINI_IRQ_MODE(base_addr) (base_addr + 0x0C)
+#define GEMINI_IRQ_POLARITY(base_addr) (base_addr + 0x10)
+#define GEMINI_IRQ_STATUS(base_addr) (base_addr + 0x14)
+#define GEMINI_FIQ_SOURCE(base_addr) (base_addr + 0x20)
+#define GEMINI_FIQ_MASK(base_addr) (base_addr + 0x24)
+#define GEMINI_FIQ_CLEAR(base_addr) (base_addr + 0x28)
+#define GEMINI_FIQ_MODE(base_addr) (base_addr + 0x2C)
+#define GEMINI_FIQ_POLARITY(base_addr) (base_addr + 0x30)
+#define GEMINI_FIQ_STATUS(base_addr) (base_addr + 0x34)
+
+/**
+ * struct gemini_irq_data - irq data container for the Gemini IRQ controller
+ * @base: memory offset in virtual memory
+ * @chip: chip container for this instance
+ * @domain: IRQ domain for this instance
+ */
+struct gemini_irq_data {
+ void __iomem *base;
+ struct irq_chip chip;
+ struct irq_domain *domain;
+};
+
+static void gemini_irq_mask(struct irq_data *d)
+{
+ struct gemini_irq_data *g = irq_data_get_irq_chip_data(d);
+ unsigned int mask;
+
+ mask = readl(GEMINI_IRQ_MASK(g->base));
+ mask &= ~BIT(irqd_to_hwirq(d));
+ writel(mask, GEMINI_IRQ_MASK(g->base));
+}
+
+static void gemini_irq_unmask(struct irq_data *d)
+{
+ struct gemini_irq_data *g = irq_data_get_irq_chip_data(d);
+ unsigned int mask;
+
+ mask = readl(GEMINI_IRQ_MASK(g->base));
+ mask |= BIT(irqd_to_hwirq(d));
+ writel(mask, GEMINI_IRQ_MASK(g->base));
+}
+
+static void gemini_irq_ack(struct irq_data *d)
+{
+ struct gemini_irq_data *g = irq_data_get_irq_chip_data(d);
+
+ writel(BIT(irqd_to_hwirq(d)), GEMINI_IRQ_CLEAR(g->base));
+}
+
+static int gemini_irq_set_type(struct irq_data *d, unsigned int trigger)
+{
+ struct gemini_irq_data *g = irq_data_get_irq_chip_data(d);
+ int offset = irqd_to_hwirq(d);
+ u32 mode, polarity;
+
+ mode = readl(GEMINI_IRQ_MODE(g->base));
+ polarity = readl(GEMINI_IRQ_POLARITY(g->base));
+
+ if (trigger & (IRQ_TYPE_LEVEL_HIGH)) {
+ irq_set_handler_locked(d, handle_level_irq);
+ /* Disable edge detection */
+ mode &= ~BIT(offset);
+ polarity &= ~BIT(offset);
+ } else if (trigger & IRQ_TYPE_EDGE_RISING) {
+ irq_set_handler_locked(d, handle_edge_irq);
+ mode |= BIT(offset);
+ polarity |= BIT(offset);
+ } else if (trigger & IRQ_TYPE_EDGE_FALLING) {
+ irq_set_handler_locked(d, handle_edge_irq);
+ mode |= BIT(offset);
+ polarity &= ~BIT(offset);
+ } else {
+ irq_set_handler_locked(d, handle_bad_irq);
+ pr_warn("GEMINI IRQ: no supported trigger selected for line %d\n",
+ offset);
+ }
+
+ writel(mode, GEMINI_IRQ_MODE(g->base));
+ writel(polarity, GEMINI_IRQ_POLARITY(g->base));
+
+ return 0;
+}
+
+static struct irq_chip gemini_irq_chip = {
+ .name = "GEMINI",
+ .irq_ack = gemini_irq_ack,
+ .irq_mask = gemini_irq_mask,
+ .irq_unmask = gemini_irq_unmask,
+ .irq_set_type = gemini_irq_set_type,
+};
+
+/* Local static for the IRQ entry call */
+static struct gemini_irq_data girq;
+
+asmlinkage void __exception_irq_entry gemini_irqchip_handle_irq(struct pt_regs *regs)
+{
+ struct gemini_irq_data *g = &girq;
+ int irq;
+ u32 status;
+
+ while ((status = readl(GEMINI_IRQ_STATUS(g->base)))) {
+ irq = ffs(status) - 1;
+ handle_domain_irq(g->domain, irq, regs);
+ }
+}
+
+static int gemini_irqdomain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct gemini_irq_data *g = d->host_data;
+
+ irq_set_chip_data(irq, g);
+ /* All IRQs should set up their type, flags as bad by default */
+ irq_set_chip_and_handler(irq, &gemini_irq_chip, handle_bad_irq);
+ irq_set_probe(irq);
+
+ return 0;
+}
+
+static void gemini_irqdomain_unmap(struct irq_domain *d, unsigned int irq)
+{
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+}
+
+static const struct irq_domain_ops gemini_irqdomain_ops = {
+ .map = gemini_irqdomain_map,
+ .unmap = gemini_irqdomain_unmap,
+ .xlate = irq_domain_xlate_onetwocell,
+};
+
+int __init gemini_of_init_irq(struct device_node *node,
+ struct device_node *parent)
+{
+ struct gemini_irq_data *g = &girq;
+
+ /*
+ * Disable the idle handler by default since it is buggy
+ * For more info see arch/arm/mach-gemini/idle.c
+ */
+ cpu_idle_poll_ctrl(true);
+
+ g->base = of_iomap(node, 0);
+ WARN(!g->base, "unable to map gemini irq registers\n");
+
+ /* Disable all interrupts */
+ writel(0, GEMINI_IRQ_MASK(g->base));
+ writel(0, GEMINI_FIQ_MASK(g->base));
+
+ g->domain = irq_domain_add_simple(node, GEMINI_NUM_IRQS, 0,
+ &gemini_irqdomain_ops, g);
+ set_handle_irq(gemini_irqchip_handle_irq);
+
+ return 0;
+}
+IRQCHIP_DECLARE(gemini, "cortina,gemini-interrupt-controller",
+ gemini_of_init_irq);
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 69b040f..2320100 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -161,7 +161,7 @@
struct its_device *dev;
u32 phys_id;
u32 event_id;
- } its_mapvi_cmd;
+ } its_mapti_cmd;
struct {
struct its_device *dev;
@@ -193,58 +193,56 @@
typedef struct its_collection *(*its_cmd_builder_t)(struct its_cmd_block *,
struct its_cmd_desc *);
+static void its_mask_encode(u64 *raw_cmd, u64 val, int h, int l)
+{
+ u64 mask = GENMASK_ULL(h, l);
+ *raw_cmd &= ~mask;
+ *raw_cmd |= (val << l) & mask;
+}
+
static void its_encode_cmd(struct its_cmd_block *cmd, u8 cmd_nr)
{
- cmd->raw_cmd[0] &= ~0xffULL;
- cmd->raw_cmd[0] |= cmd_nr;
+ its_mask_encode(&cmd->raw_cmd[0], cmd_nr, 7, 0);
}
static void its_encode_devid(struct its_cmd_block *cmd, u32 devid)
{
- cmd->raw_cmd[0] &= BIT_ULL(32) - 1;
- cmd->raw_cmd[0] |= ((u64)devid) << 32;
+ its_mask_encode(&cmd->raw_cmd[0], devid, 63, 32);
}
static void its_encode_event_id(struct its_cmd_block *cmd, u32 id)
{
- cmd->raw_cmd[1] &= ~0xffffffffULL;
- cmd->raw_cmd[1] |= id;
+ its_mask_encode(&cmd->raw_cmd[1], id, 31, 0);
}
static void its_encode_phys_id(struct its_cmd_block *cmd, u32 phys_id)
{
- cmd->raw_cmd[1] &= 0xffffffffULL;
- cmd->raw_cmd[1] |= ((u64)phys_id) << 32;
+ its_mask_encode(&cmd->raw_cmd[1], phys_id, 63, 32);
}
static void its_encode_size(struct its_cmd_block *cmd, u8 size)
{
- cmd->raw_cmd[1] &= ~0x1fULL;
- cmd->raw_cmd[1] |= size & 0x1f;
+ its_mask_encode(&cmd->raw_cmd[1], size, 4, 0);
}
static void its_encode_itt(struct its_cmd_block *cmd, u64 itt_addr)
{
- cmd->raw_cmd[2] &= ~0xffffffffffffULL;
- cmd->raw_cmd[2] |= itt_addr & 0xffffffffff00ULL;
+ its_mask_encode(&cmd->raw_cmd[2], itt_addr >> 8, 50, 8);
}
static void its_encode_valid(struct its_cmd_block *cmd, int valid)
{
- cmd->raw_cmd[2] &= ~(1ULL << 63);
- cmd->raw_cmd[2] |= ((u64)!!valid) << 63;
+ its_mask_encode(&cmd->raw_cmd[2], !!valid, 63, 63);
}
static void its_encode_target(struct its_cmd_block *cmd, u64 target_addr)
{
- cmd->raw_cmd[2] &= ~(0xffffffffULL << 16);
- cmd->raw_cmd[2] |= (target_addr & (0xffffffffULL << 16));
+ its_mask_encode(&cmd->raw_cmd[2], target_addr >> 16, 50, 16);
}
static void its_encode_collection(struct its_cmd_block *cmd, u16 col)
{
- cmd->raw_cmd[2] &= ~0xffffULL;
- cmd->raw_cmd[2] |= col;
+ its_mask_encode(&cmd->raw_cmd[2], col, 15, 0);
}
static inline void its_fixup_cmd(struct its_cmd_block *cmd)
@@ -289,18 +287,18 @@
return desc->its_mapc_cmd.col;
}
-static struct its_collection *its_build_mapvi_cmd(struct its_cmd_block *cmd,
+static struct its_collection *its_build_mapti_cmd(struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
struct its_collection *col;
- col = dev_event_to_col(desc->its_mapvi_cmd.dev,
- desc->its_mapvi_cmd.event_id);
+ col = dev_event_to_col(desc->its_mapti_cmd.dev,
+ desc->its_mapti_cmd.event_id);
- its_encode_cmd(cmd, GITS_CMD_MAPVI);
- its_encode_devid(cmd, desc->its_mapvi_cmd.dev->device_id);
- its_encode_event_id(cmd, desc->its_mapvi_cmd.event_id);
- its_encode_phys_id(cmd, desc->its_mapvi_cmd.phys_id);
+ its_encode_cmd(cmd, GITS_CMD_MAPTI);
+ its_encode_devid(cmd, desc->its_mapti_cmd.dev->device_id);
+ its_encode_event_id(cmd, desc->its_mapti_cmd.event_id);
+ its_encode_phys_id(cmd, desc->its_mapti_cmd.phys_id);
its_encode_collection(cmd, col->col_id);
its_fixup_cmd(cmd);
@@ -413,6 +411,12 @@
if (its->cmd_write == (its->cmd_base + ITS_CMD_QUEUE_NR_ENTRIES))
its->cmd_write = its->cmd_base;
+ /* Clear command */
+ cmd->raw_cmd[0] = 0;
+ cmd->raw_cmd[1] = 0;
+ cmd->raw_cmd[2] = 0;
+ cmd->raw_cmd[3] = 0;
+
return cmd;
}
@@ -531,15 +535,15 @@
its_send_single_command(its, its_build_mapc_cmd, &desc);
}
-static void its_send_mapvi(struct its_device *dev, u32 irq_id, u32 id)
+static void its_send_mapti(struct its_device *dev, u32 irq_id, u32 id)
{
struct its_cmd_desc desc;
- desc.its_mapvi_cmd.dev = dev;
- desc.its_mapvi_cmd.phys_id = irq_id;
- desc.its_mapvi_cmd.event_id = id;
+ desc.its_mapti_cmd.dev = dev;
+ desc.its_mapti_cmd.phys_id = irq_id;
+ desc.its_mapti_cmd.event_id = id;
- its_send_single_command(dev->its, its_build_mapvi_cmd, &desc);
+ its_send_single_command(dev->its, its_build_mapti_cmd, &desc);
}
static void its_send_movi(struct its_device *dev,
@@ -824,7 +828,7 @@
static const char *its_base_type_string[] = {
[GITS_BASER_TYPE_DEVICE] = "Devices",
[GITS_BASER_TYPE_VCPU] = "Virtual CPUs",
- [GITS_BASER_TYPE_CPU] = "Physical CPUs",
+ [GITS_BASER_TYPE_RESERVED3] = "Reserved (3)",
[GITS_BASER_TYPE_COLLECTION] = "Interrupt Collections",
[GITS_BASER_TYPE_RESERVED5] = "Reserved (5)",
[GITS_BASER_TYPE_RESERVED6] = "Reserved (6)",
@@ -960,7 +964,7 @@
u32 psz, u32 *order)
{
u64 esz = GITS_BASER_ENTRY_SIZE(its_read_baser(its, baser));
- u64 val = GITS_BASER_InnerShareable | GITS_BASER_WaWb;
+ u64 val = GITS_BASER_InnerShareable | GITS_BASER_RaWaWb;
u32 ids = its->device_ids;
u32 new_order = *order;
bool indirect = false;
@@ -1025,7 +1029,7 @@
u64 typer = gic_read_typer(its->base + GITS_TYPER);
u32 ids = GITS_TYPER_DEVBITS(typer);
u64 shr = GITS_BASER_InnerShareable;
- u64 cache = GITS_BASER_WaWb;
+ u64 cache = GITS_BASER_RaWaWb;
u32 psz = SZ_64K;
int err, i;
@@ -1122,7 +1126,7 @@
/* set PROPBASE */
val = (page_to_phys(gic_rdists->prop_page) |
GICR_PROPBASER_InnerShareable |
- GICR_PROPBASER_WaWb |
+ GICR_PROPBASER_RaWaWb |
((LPI_NRBITS - 1) & GICR_PROPBASER_IDBITS_MASK));
gicr_write_propbaser(val, rbase + GICR_PROPBASER);
@@ -1147,7 +1151,7 @@
/* set PENDBASE */
val = (page_to_phys(pend_page) |
GICR_PENDBASER_InnerShareable |
- GICR_PENDBASER_WaWb);
+ GICR_PENDBASER_RaWaWb);
gicr_write_pendbaser(val, rbase + GICR_PENDBASER);
tmp = gicr_read_pendbaser(rbase + GICR_PENDBASER);
@@ -1498,7 +1502,7 @@
its_dev->event_map.col_map[event] = cpumask_first(cpu_mask);
/* Map the GIC IRQ and event to the device */
- its_send_mapvi(its_dev, d->hwirq, event);
+ its_send_mapti(its_dev, d->hwirq, event);
}
static void its_irq_domain_deactivate(struct irq_domain *domain,
@@ -1642,6 +1646,7 @@
inner_domain->parent = its_parent;
inner_domain->bus_token = DOMAIN_BUS_NEXUS;
+ inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_REMAP;
info->ops = &its_msi_domain_ops;
info->data = its;
inner_domain->host_data = info;
@@ -1693,7 +1698,8 @@
its->ite_size = ((gic_read_typer(its_base + GITS_TYPER) >> 4) & 0xf) + 1;
its->numa_node = numa_node;
- its->cmd_base = kzalloc(ITS_CMD_QUEUE_SZ, GFP_KERNEL);
+ its->cmd_base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
+ get_order(ITS_CMD_QUEUE_SZ));
if (!its->cmd_base) {
err = -ENOMEM;
goto out_free_its;
@@ -1711,7 +1717,7 @@
goto out_free_tables;
baser = (virt_to_phys(its->cmd_base) |
- GITS_CBASER_WaWb |
+ GITS_CBASER_RaWaWb |
GITS_CBASER_InnerShareable |
(ITS_CMD_QUEUE_SZ / SZ_4K - 1) |
GITS_CBASER_VALID);
@@ -1751,7 +1757,7 @@
out_free_tables:
its_free_tables(its);
out_free_cmd:
- kfree(its->cmd_base);
+ free_pages((unsigned long)its->cmd_base, get_order(ITS_CMD_QUEUE_SZ));
out_free_its:
kfree(its);
out_unmap:
diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c
index c01c09e..11d12bc 100644
--- a/drivers/irqchip/irq-mips-gic.c
+++ b/drivers/irqchip/irq-mips-gic.c
@@ -968,6 +968,34 @@
.match = gic_ipi_domain_match,
};
+static void __init gic_map_single_int(struct device_node *node,
+ unsigned int irq)
+{
+ unsigned int linux_irq;
+ struct irq_fwspec local_int_fwspec = {
+ .fwnode = &node->fwnode,
+ .param_count = 3,
+ .param = {
+ [0] = GIC_LOCAL,
+ [1] = irq,
+ [2] = IRQ_TYPE_NONE,
+ },
+ };
+
+ if (!gic_local_irq_is_routable(irq))
+ return;
+
+ linux_irq = irq_create_fwspec_mapping(&local_int_fwspec);
+ WARN_ON(!linux_irq);
+}
+
+static void __init gic_map_interrupts(struct device_node *node)
+{
+ gic_map_single_int(node, GIC_LOCAL_INT_TIMER);
+ gic_map_single_int(node, GIC_LOCAL_INT_PERFCTR);
+ gic_map_single_int(node, GIC_LOCAL_INT_FDC);
+}
+
static void __init __gic_init(unsigned long gic_base_addr,
unsigned long gic_addrspace_size,
unsigned int cpu_vec, unsigned int irqbase,
@@ -1067,6 +1095,7 @@
}
gic_basic_init();
+ gic_map_interrupts(node);
}
void __init gic_init(unsigned long gic_base_addr,
diff --git a/drivers/irqchip/qcom-irq-combiner.c b/drivers/irqchip/qcom-irq-combiner.c
new file mode 100644
index 0000000..2265586
--- /dev/null
+++ b/drivers/irqchip/qcom-irq-combiner.c
@@ -0,0 +1,296 @@
+/* Copyright (c) 2015-2016, The Linux Foundation. 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.
+ */
+
+/*
+ * Driver for interrupt combiners in the Top-level Control and Status
+ * Registers (TCSR) hardware block in Qualcomm Technologies chips.
+ * An interrupt combiner in this block combines a set of interrupts by
+ * OR'ing the individual interrupt signals into a summary interrupt
+ * signal routed to a parent interrupt controller, and provides read-
+ * only, 32-bit registers to query the status of individual interrupts.
+ * The status bit for IRQ n is bit (n % 32) within register (n / 32)
+ * of the given combiner. Thus, each combiner can be described as a set
+ * of register offsets and the number of IRQs managed.
+ */
+
+#define pr_fmt(fmt) "QCOM80B1:" fmt
+
+#include <linux/acpi.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/platform_device.h>
+
+#define REG_SIZE 32
+
+struct combiner_reg {
+ void __iomem *addr;
+ unsigned long enabled;
+};
+
+struct combiner {
+ struct irq_domain *domain;
+ int parent_irq;
+ u32 nirqs;
+ u32 nregs;
+ struct combiner_reg regs[0];
+};
+
+static inline int irq_nr(u32 reg, u32 bit)
+{
+ return reg * REG_SIZE + bit;
+}
+
+/*
+ * Handler for the cascaded IRQ.
+ */
+static void combiner_handle_irq(struct irq_desc *desc)
+{
+ struct combiner *combiner = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ u32 reg;
+
+ chained_irq_enter(chip, desc);
+
+ for (reg = 0; reg < combiner->nregs; reg++) {
+ int virq;
+ int hwirq;
+ u32 bit;
+ u32 status;
+
+ bit = readl_relaxed(combiner->regs[reg].addr);
+ status = bit & combiner->regs[reg].enabled;
+ if (!status)
+ pr_warn_ratelimited("Unexpected IRQ on CPU%d: (%08x %08lx %p)\n",
+ smp_processor_id(), bit,
+ combiner->regs[reg].enabled,
+ combiner->regs[reg].addr);
+
+ while (status) {
+ bit = __ffs(status);
+ status &= ~(1 << bit);
+ hwirq = irq_nr(reg, bit);
+ virq = irq_find_mapping(combiner->domain, hwirq);
+ if (virq > 0)
+ generic_handle_irq(virq);
+
+ }
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static void combiner_irq_chip_mask_irq(struct irq_data *data)
+{
+ struct combiner *combiner = irq_data_get_irq_chip_data(data);
+ struct combiner_reg *reg = combiner->regs + data->hwirq / REG_SIZE;
+
+ clear_bit(data->hwirq % REG_SIZE, ®->enabled);
+}
+
+static void combiner_irq_chip_unmask_irq(struct irq_data *data)
+{
+ struct combiner *combiner = irq_data_get_irq_chip_data(data);
+ struct combiner_reg *reg = combiner->regs + data->hwirq / REG_SIZE;
+
+ set_bit(data->hwirq % REG_SIZE, ®->enabled);
+}
+
+static struct irq_chip irq_chip = {
+ .irq_mask = combiner_irq_chip_mask_irq,
+ .irq_unmask = combiner_irq_chip_unmask_irq,
+ .name = "qcom-irq-combiner"
+};
+
+static int combiner_irq_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_and_handler(irq, &irq_chip, handle_level_irq);
+ irq_set_chip_data(irq, domain->host_data);
+ irq_set_noprobe(irq);
+ return 0;
+}
+
+static void combiner_irq_unmap(struct irq_domain *domain, unsigned int irq)
+{
+ irq_domain_reset_irq_data(irq_get_irq_data(irq));
+}
+
+static int combiner_irq_translate(struct irq_domain *d, struct irq_fwspec *fws,
+ unsigned long *hwirq, unsigned int *type)
+{
+ struct combiner *combiner = d->host_data;
+
+ if (is_acpi_node(fws->fwnode)) {
+ if (WARN_ON((fws->param_count != 2) ||
+ (fws->param[0] >= combiner->nirqs) ||
+ (fws->param[1] & IORESOURCE_IRQ_LOWEDGE) ||
+ (fws->param[1] & IORESOURCE_IRQ_HIGHEDGE)))
+ return -EINVAL;
+
+ *hwirq = fws->param[0];
+ *type = fws->param[1];
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static const struct irq_domain_ops domain_ops = {
+ .map = combiner_irq_map,
+ .unmap = combiner_irq_unmap,
+ .translate = combiner_irq_translate
+};
+
+static acpi_status count_registers_cb(struct acpi_resource *ares, void *context)
+{
+ int *count = context;
+
+ if (ares->type == ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
+ ++(*count);
+ return AE_OK;
+}
+
+static int count_registers(struct platform_device *pdev)
+{
+ acpi_handle ahandle = ACPI_HANDLE(&pdev->dev);
+ acpi_status status;
+ int count = 0;
+
+ if (!acpi_has_method(ahandle, METHOD_NAME__CRS))
+ return -EINVAL;
+
+ status = acpi_walk_resources(ahandle, METHOD_NAME__CRS,
+ count_registers_cb, &count);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+ return count;
+}
+
+struct get_registers_context {
+ struct device *dev;
+ struct combiner *combiner;
+ int err;
+};
+
+static acpi_status get_registers_cb(struct acpi_resource *ares, void *context)
+{
+ struct get_registers_context *ctx = context;
+ struct acpi_resource_generic_register *reg;
+ phys_addr_t paddr;
+ void __iomem *vaddr;
+
+ if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
+ return AE_OK;
+
+ reg = &ares->data.generic_reg;
+ paddr = reg->address;
+ if ((reg->space_id != ACPI_SPACE_MEM) ||
+ (reg->bit_offset != 0) ||
+ (reg->bit_width > REG_SIZE)) {
+ dev_err(ctx->dev, "Bad register resource @%pa\n", &paddr);
+ ctx->err = -EINVAL;
+ return AE_ERROR;
+ }
+
+ vaddr = devm_ioremap(ctx->dev, reg->address, REG_SIZE);
+ if (!vaddr) {
+ dev_err(ctx->dev, "Can't map register @%pa\n", &paddr);
+ ctx->err = -ENOMEM;
+ return AE_ERROR;
+ }
+
+ ctx->combiner->regs[ctx->combiner->nregs].addr = vaddr;
+ ctx->combiner->nirqs += reg->bit_width;
+ ctx->combiner->nregs++;
+ return AE_OK;
+}
+
+static int get_registers(struct platform_device *pdev, struct combiner *comb)
+{
+ acpi_handle ahandle = ACPI_HANDLE(&pdev->dev);
+ acpi_status status;
+ struct get_registers_context ctx;
+
+ if (!acpi_has_method(ahandle, METHOD_NAME__CRS))
+ return -EINVAL;
+
+ ctx.dev = &pdev->dev;
+ ctx.combiner = comb;
+ ctx.err = 0;
+
+ status = acpi_walk_resources(ahandle, METHOD_NAME__CRS,
+ get_registers_cb, &ctx);
+ if (ACPI_FAILURE(status))
+ return ctx.err;
+ return 0;
+}
+
+static int __init combiner_probe(struct platform_device *pdev)
+{
+ struct combiner *combiner;
+ size_t alloc_sz;
+ u32 nregs;
+ int err;
+
+ nregs = count_registers(pdev);
+ if (nregs <= 0) {
+ dev_err(&pdev->dev, "Error reading register resources\n");
+ return -EINVAL;
+ }
+
+ alloc_sz = sizeof(*combiner) + sizeof(struct combiner_reg) * nregs;
+ combiner = devm_kzalloc(&pdev->dev, alloc_sz, GFP_KERNEL);
+ if (!combiner)
+ return -ENOMEM;
+
+ err = get_registers(pdev, combiner);
+ if (err < 0)
+ return err;
+
+ combiner->parent_irq = platform_get_irq(pdev, 0);
+ if (combiner->parent_irq <= 0) {
+ dev_err(&pdev->dev, "Error getting IRQ resource\n");
+ return -EPROBE_DEFER;
+ }
+
+ combiner->domain = irq_domain_create_linear(pdev->dev.fwnode, combiner->nirqs,
+ &domain_ops, combiner);
+ if (!combiner->domain)
+ /* Errors printed by irq_domain_create_linear */
+ return -ENODEV;
+
+ irq_set_chained_handler_and_data(combiner->parent_irq,
+ combiner_handle_irq, combiner);
+
+ dev_info(&pdev->dev, "Initialized with [p=%d,n=%d,r=%p]\n",
+ combiner->parent_irq, combiner->nirqs, combiner->regs[0].addr);
+ return 0;
+}
+
+static const struct acpi_device_id qcom_irq_combiner_ids[] = {
+ { "QCOM80B1", },
+ { }
+};
+
+static struct platform_driver qcom_irq_combiner_probe = {
+ .driver = {
+ .name = "qcom-irq-combiner",
+ .acpi_match_table = ACPI_PTR(qcom_irq_combiner_ids),
+ },
+ .probe = combiner_probe,
+};
+
+static int __init register_qcom_irq_combiner(void)
+{
+ return platform_driver_register(&qcom_irq_combiner_probe);
+}
+device_initcall(register_qcom_irq_combiner);
diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c
index 9cb4b62..b324474 100644
--- a/drivers/isdn/mISDN/stack.c
+++ b/drivers/isdn/mISDN/stack.c
@@ -203,7 +203,7 @@
{
struct mISDNstack *st = data;
#ifdef MISDN_MSG_STATS
- cputime_t utime, stime;
+ u64 utime, stime;
#endif
int err = 0;
@@ -308,7 +308,7 @@
st->stopped_cnt);
task_cputime(st->thread, &utime, &stime);
printk(KERN_DEBUG
- "mISDNStackd daemon for %s utime(%ld) stime(%ld)\n",
+ "mISDNStackd daemon for %s utime(%llu) stime(%llu)\n",
dev_name(&st->dev->dev), utime, stime);
printk(KERN_DEBUG
"mISDNStackd daemon for %s nvcsw(%ld) nivcsw(%ld)\n",
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index c621cbb..275f467 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -29,6 +29,15 @@
for the flash related features of a LED device. It can be built
as a module.
+config LEDS_BRIGHTNESS_HW_CHANGED
+ bool "LED Class brightness_hw_changed attribute support"
+ depends on LEDS_CLASS
+ help
+ This option enables support for the brightness_hw_changed attribute
+ for led sysfs class devices under /sys/class/leds.
+
+ See Documentation/ABI/testing/sysfs-class-led for details.
+
comment "LED drivers"
config LEDS_88PM860X
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 326ee6e..f2b0a80 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -103,6 +103,68 @@
NULL,
};
+#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
+static ssize_t brightness_hw_changed_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+ if (led_cdev->brightness_hw_changed == -1)
+ return -ENODATA;
+
+ return sprintf(buf, "%u\n", led_cdev->brightness_hw_changed);
+}
+
+static DEVICE_ATTR_RO(brightness_hw_changed);
+
+static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
+{
+ struct device *dev = led_cdev->dev;
+ int ret;
+
+ ret = device_create_file(dev, &dev_attr_brightness_hw_changed);
+ if (ret) {
+ dev_err(dev, "Error creating brightness_hw_changed\n");
+ return ret;
+ }
+
+ led_cdev->brightness_hw_changed_kn =
+ sysfs_get_dirent(dev->kobj.sd, "brightness_hw_changed");
+ if (!led_cdev->brightness_hw_changed_kn) {
+ dev_err(dev, "Error getting brightness_hw_changed kn\n");
+ device_remove_file(dev, &dev_attr_brightness_hw_changed);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
+{
+ sysfs_put(led_cdev->brightness_hw_changed_kn);
+ device_remove_file(led_cdev->dev, &dev_attr_brightness_hw_changed);
+}
+
+void led_classdev_notify_brightness_hw_changed(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ if (WARN_ON(!led_cdev->brightness_hw_changed_kn))
+ return;
+
+ led_cdev->brightness_hw_changed = brightness;
+ sysfs_notify_dirent(led_cdev->brightness_hw_changed_kn);
+}
+EXPORT_SYMBOL_GPL(led_classdev_notify_brightness_hw_changed);
+#else
+static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
+{
+ return 0;
+}
+static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
+{
+}
+#endif
+
/**
* led_classdev_suspend - suspend an led_classdev.
* @led_cdev: the led_classdev to suspend.
@@ -204,10 +266,21 @@
dev_warn(parent, "Led %s renamed to %s due to name collision",
led_cdev->name, dev_name(led_cdev->dev));
+ if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
+ ret = led_add_brightness_hw_changed(led_cdev);
+ if (ret) {
+ device_unregister(led_cdev->dev);
+ return ret;
+ }
+ }
+
led_cdev->work_flags = 0;
#ifdef CONFIG_LEDS_TRIGGERS
init_rwsem(&led_cdev->trigger_lock);
#endif
+#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
+ led_cdev->brightness_hw_changed = -1;
+#endif
mutex_init(&led_cdev->led_access);
/* add to the list of leds */
down_write(&leds_list_lock);
@@ -256,6 +329,9 @@
flush_work(&led_cdev->set_brightness_work);
+ if (led_cdev->flags & LED_BRIGHT_HW_CHANGED)
+ led_remove_brightness_hw_changed(led_cdev);
+
device_unregister(led_cdev->dev);
down_write(&leds_list_lock);
diff --git a/drivers/leds/leds-ktd2692.c b/drivers/leds/leds-ktd2692.c
index bf23ba1..45296aa 100644
--- a/drivers/leds/leds-ktd2692.c
+++ b/drivers/leds/leds-ktd2692.c
@@ -270,15 +270,15 @@
return -ENXIO;
led->ctrl_gpio = devm_gpiod_get(dev, "ctrl", GPIOD_ASIS);
- if (IS_ERR(led->ctrl_gpio)) {
- ret = PTR_ERR(led->ctrl_gpio);
+ ret = PTR_ERR_OR_ZERO(led->ctrl_gpio);
+ if (ret) {
dev_err(dev, "cannot get ctrl-gpios %d\n", ret);
return ret;
}
led->aux_gpio = devm_gpiod_get(dev, "aux", GPIOD_ASIS);
- if (IS_ERR(led->aux_gpio)) {
- ret = PTR_ERR(led->aux_gpio);
+ ret = PTR_ERR_OR_ZERO(led->aux_gpio);
+ if (ret) {
dev_err(dev, "cannot get aux-gpios %d\n", ret);
return ret;
}
diff --git a/drivers/leds/trigger/ledtrig-heartbeat.c b/drivers/leds/trigger/ledtrig-heartbeat.c
index c9f3862..e6f2f8b 100644
--- a/drivers/leds/trigger/ledtrig-heartbeat.c
+++ b/drivers/leds/trigger/ledtrig-heartbeat.c
@@ -43,6 +43,9 @@
return;
}
+ if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags))
+ led_cdev->blink_brightness = led_cdev->new_blink_brightness;
+
/* acts like an actual heart beat -- ie thump-thump-pause... */
switch (heartbeat_data->phase) {
case 0:
@@ -59,26 +62,26 @@
delay = msecs_to_jiffies(70);
heartbeat_data->phase++;
if (!heartbeat_data->invert)
- brightness = led_cdev->max_brightness;
+ brightness = led_cdev->blink_brightness;
break;
case 1:
delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
heartbeat_data->phase++;
if (heartbeat_data->invert)
- brightness = led_cdev->max_brightness;
+ brightness = led_cdev->blink_brightness;
break;
case 2:
delay = msecs_to_jiffies(70);
heartbeat_data->phase++;
if (!heartbeat_data->invert)
- brightness = led_cdev->max_brightness;
+ brightness = led_cdev->blink_brightness;
break;
default:
delay = heartbeat_data->period - heartbeat_data->period / 4 -
msecs_to_jiffies(70);
heartbeat_data->phase = 0;
if (heartbeat_data->invert)
- brightness = led_cdev->max_brightness;
+ brightness = led_cdev->blink_brightness;
break;
}
@@ -133,7 +136,10 @@
setup_timer(&heartbeat_data->timer,
led_heartbeat_function, (unsigned long) led_cdev);
heartbeat_data->phase = 0;
+ if (!led_cdev->blink_brightness)
+ led_cdev->blink_brightness = led_cdev->max_brightness;
led_heartbeat_function(heartbeat_data->timer.data);
+ set_bit(LED_BLINK_SW, &led_cdev->work_flags);
led_cdev->activated = true;
}
@@ -145,6 +151,7 @@
del_timer_sync(&heartbeat_data->timer);
device_remove_file(led_cdev->dev, &dev_attr_invert);
kfree(heartbeat_data);
+ clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
led_cdev->activated = false;
}
}
diff --git a/drivers/macintosh/rack-meter.c b/drivers/macintosh/rack-meter.c
index 7755271..e199fd6 100644
--- a/drivers/macintosh/rack-meter.c
+++ b/drivers/macintosh/rack-meter.c
@@ -52,8 +52,8 @@
struct rackmeter_cpu {
struct delayed_work sniffer;
struct rackmeter *rm;
- cputime64_t prev_wall;
- cputime64_t prev_idle;
+ u64 prev_wall;
+ u64 prev_idle;
int zero;
} ____cacheline_aligned;
@@ -81,7 +81,7 @@
/* This is copied from cpufreq_ondemand, maybe we should put it in
* a common header somewhere
*/
-static inline cputime64_t get_cpu_idle_time(unsigned int cpu)
+static inline u64 get_cpu_idle_time(unsigned int cpu)
{
u64 retval;
@@ -217,23 +217,23 @@
container_of(work, struct rackmeter_cpu, sniffer.work);
struct rackmeter *rm = rcpu->rm;
unsigned int cpu = smp_processor_id();
- cputime64_t cur_jiffies, total_idle_ticks;
- unsigned int total_ticks, idle_ticks;
+ u64 cur_nsecs, total_idle_nsecs;
+ u64 total_nsecs, idle_nsecs;
int i, offset, load, cumm, pause;
- cur_jiffies = jiffies64_to_cputime64(get_jiffies_64());
- total_ticks = (unsigned int) (cur_jiffies - rcpu->prev_wall);
- rcpu->prev_wall = cur_jiffies;
+ cur_nsecs = jiffies64_to_nsecs(get_jiffies_64());
+ total_nsecs = cur_nsecs - rcpu->prev_wall;
+ rcpu->prev_wall = cur_nsecs;
- total_idle_ticks = get_cpu_idle_time(cpu);
- idle_ticks = (unsigned int) (total_idle_ticks - rcpu->prev_idle);
- idle_ticks = min(idle_ticks, total_ticks);
- rcpu->prev_idle = total_idle_ticks;
+ total_idle_nsecs = get_cpu_idle_time(cpu);
+ idle_nsecs = total_idle_nsecs - rcpu->prev_idle;
+ idle_nsecs = min(idle_nsecs, total_nsecs);
+ rcpu->prev_idle = total_idle_nsecs;
/* We do a very dumb calculation to update the LEDs for now,
* we'll do better once we have actual PWM implemented
*/
- load = (9 * (total_ticks - idle_ticks)) / total_ticks;
+ load = div64_u64(9 * (total_nsecs - idle_nsecs), total_nsecs);
offset = cpu << 3;
cumm = 0;
@@ -278,7 +278,7 @@
continue;
rcpu = &rm->cpu[cpu];
rcpu->prev_idle = get_cpu_idle_time(cpu);
- rcpu->prev_wall = jiffies64_to_cputime64(get_jiffies_64());
+ rcpu->prev_wall = jiffies64_to_nsecs(get_jiffies_64());
schedule_delayed_work_on(cpu, &rm->cpu[cpu].sniffer,
msecs_to_jiffies(CPU_SAMPLING_RATE));
}
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index 84d2f0e..d36d427 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -794,7 +794,7 @@
DECLARE_WAITQUEUE(wait, current);
add_wait_queue(&c->free_buffer_wait, &wait);
- set_task_state(current, TASK_UNINTERRUPTIBLE);
+ set_current_state(TASK_UNINTERRUPTIBLE);
dm_bufio_unlock(c);
io_schedule();
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 8a9f742..1cb2ca9 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1210,14 +1210,14 @@
spin_unlock_irq(&cc->write_thread_wait.lock);
if (unlikely(kthread_should_stop())) {
- set_task_state(current, TASK_RUNNING);
+ set_current_state(TASK_RUNNING);
remove_wait_queue(&cc->write_thread_wait, &wait);
break;
}
schedule();
- set_task_state(current, TASK_RUNNING);
+ set_current_state(TASK_RUNNING);
spin_lock_irq(&cc->write_thread_wait.lock);
__remove_wait_queue(&cc->write_thread_wait, &wait);
goto continue_locked;
diff --git a/drivers/md/persistent-data/dm-block-manager.c b/drivers/md/persistent-data/dm-block-manager.c
index a6dde7c..758d90c 100644
--- a/drivers/md/persistent-data/dm-block-manager.c
+++ b/drivers/md/persistent-data/dm-block-manager.c
@@ -120,7 +120,7 @@
static void __wait(struct waiter *w)
{
for (;;) {
- set_task_state(current, TASK_UNINTERRUPTIBLE);
+ set_current_state(TASK_UNINTERRUPTIBLE);
if (!w->task)
break;
@@ -128,7 +128,7 @@
schedule();
}
- set_task_state(current, TASK_RUNNING);
+ set_current_state(TASK_RUNNING);
}
static void __wake_waiter(struct waiter *w)
diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c
index 87a6b65..ccda41c 100644
--- a/drivers/media/cec/cec-adap.c
+++ b/drivers/media/cec/cec-adap.c
@@ -612,8 +612,7 @@
}
memset(msg->msg + msg->len, 0, sizeof(msg->msg) - msg->len);
if (msg->len == 1) {
- if (cec_msg_initiator(msg) != 0xf ||
- cec_msg_destination(msg) == 0xf) {
+ if (cec_msg_destination(msg) == 0xf) {
dprintk(1, "cec_transmit_msg: invalid poll message\n");
return -EINVAL;
}
@@ -638,7 +637,7 @@
dprintk(1, "cec_transmit_msg: destination is the adapter itself\n");
return -EINVAL;
}
- if (cec_msg_initiator(msg) != 0xf &&
+ if (msg->len > 1 && adap->is_configured &&
!cec_has_log_addr(adap, cec_msg_initiator(msg))) {
dprintk(1, "cec_transmit_msg: initiator has unknown logical address %d\n",
cec_msg_initiator(msg));
@@ -1072,7 +1071,7 @@
/* Send poll message */
msg.len = 1;
- msg.msg[0] = 0xf0 | log_addr;
+ msg.msg[0] = (log_addr << 4) | log_addr;
err = cec_transmit_msg_fh(adap, &msg, NULL, true);
/*
diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c
index a4dcaec..8c1f926 100644
--- a/drivers/media/usb/siano/smsusb.c
+++ b/drivers/media/usb/siano/smsusb.c
@@ -218,22 +218,30 @@
static int smsusb_sendrequest(void *context, void *buffer, size_t size)
{
struct smsusb_device_t *dev = (struct smsusb_device_t *) context;
- struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) buffer;
- int dummy;
+ struct sms_msg_hdr *phdr;
+ int dummy, ret;
if (dev->state != SMSUSB_ACTIVE) {
pr_debug("Device not active yet\n");
return -ENOENT;
}
+ phdr = kmalloc(size, GFP_KERNEL);
+ if (!phdr)
+ return -ENOMEM;
+ memcpy(phdr, buffer, size);
+
pr_debug("sending %s(%d) size: %d\n",
smscore_translate_msg(phdr->msg_type), phdr->msg_type,
phdr->msg_length);
smsendian_handle_tx_message((struct sms_msg_data *) phdr);
- smsendian_handle_message_header((struct sms_msg_hdr *)buffer);
- return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2),
- buffer, size, &dummy, 1000);
+ smsendian_handle_message_header((struct sms_msg_hdr *)phdr);
+ ret = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2),
+ phdr, size, &dummy, 1000);
+
+ kfree(phdr);
+ return ret;
}
static char *smsusb1_fw_lkup[] = {
diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c
index 1ef7575..be42957 100644
--- a/drivers/mfd/lpc_ich.c
+++ b/drivers/mfd/lpc_ich.c
@@ -56,6 +56,7 @@
* document number TBD : Wildcat Point-LP
* document number TBD : 9 Series
* document number TBD : Lewisburg
+ * document number TBD : Apollo Lake SoC
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -83,6 +84,17 @@
#define ACPIBASE_GCS_OFF 0x3410
#define ACPIBASE_GCS_END 0x3414
+#define SPIBASE_BYT 0x54
+#define SPIBASE_BYT_SZ 512
+#define SPIBASE_BYT_EN BIT(1)
+
+#define SPIBASE_LPT 0x3800
+#define SPIBASE_LPT_SZ 512
+#define BCR 0xdc
+#define BCR_WPD BIT(0)
+
+#define SPIBASE_APL_SZ 4096
+
#define GPIOBASE_ICH0 0x58
#define GPIOCTRL_ICH0 0x5C
#define GPIOBASE_ICH6 0x48
@@ -133,6 +145,12 @@
},
};
+static struct resource intel_spi_res[] = {
+ {
+ .flags = IORESOURCE_MEM,
+ }
+};
+
static struct mfd_cell lpc_ich_wdt_cell = {
.name = "iTCO_wdt",
.num_resources = ARRAY_SIZE(wdt_ich_res),
@@ -147,6 +165,14 @@
.ignore_resource_conflicts = true,
};
+
+static struct mfd_cell lpc_ich_spi_cell = {
+ .name = "intel-spi",
+ .num_resources = ARRAY_SIZE(intel_spi_res),
+ .resources = intel_spi_res,
+ .ignore_resource_conflicts = true,
+};
+
/* chipset related info */
enum lpc_chipsets {
LPC_ICH = 0, /* ICH */
@@ -216,6 +242,7 @@
LPC_BRASWELL, /* Braswell SoC */
LPC_LEWISBURG, /* Lewisburg */
LPC_9S, /* 9 Series */
+ LPC_APL, /* Apollo Lake SoC */
};
static struct lpc_ich_info lpc_chipset_info[] = {
@@ -494,10 +521,12 @@
.name = "Lynx Point",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
+ .spi_type = INTEL_SPI_LPT,
},
[LPC_LPT_LP] = {
.name = "Lynx Point_LP",
.iTCO_version = 2,
+ .spi_type = INTEL_SPI_LPT,
},
[LPC_WBG] = {
.name = "Wellsburg",
@@ -511,6 +540,7 @@
[LPC_BAYTRAIL] = {
.name = "Bay Trail SoC",
.iTCO_version = 3,
+ .spi_type = INTEL_SPI_BYT,
},
[LPC_COLETO] = {
.name = "Coleto Creek",
@@ -519,10 +549,12 @@
[LPC_WPT_LP] = {
.name = "Wildcat Point_LP",
.iTCO_version = 2,
+ .spi_type = INTEL_SPI_LPT,
},
[LPC_BRASWELL] = {
.name = "Braswell SoC",
.iTCO_version = 3,
+ .spi_type = INTEL_SPI_BYT,
},
[LPC_LEWISBURG] = {
.name = "Lewisburg",
@@ -533,6 +565,10 @@
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
+ [LPC_APL] = {
+ .name = "Apollo Lake SoC",
+ .spi_type = INTEL_SPI_BXT,
+ },
};
/*
@@ -681,6 +717,7 @@
{ PCI_VDEVICE(INTEL, 0x3b14), LPC_3420},
{ PCI_VDEVICE(INTEL, 0x3b16), LPC_3450},
{ PCI_VDEVICE(INTEL, 0x5031), LPC_EP80579},
+ { PCI_VDEVICE(INTEL, 0x5ae8), LPC_APL},
{ PCI_VDEVICE(INTEL, 0x8c40), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c41), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c42), LPC_LPT},
@@ -1056,6 +1093,94 @@
return ret;
}
+static int lpc_ich_init_spi(struct pci_dev *dev)
+{
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
+ struct resource *res = &intel_spi_res[0];
+ struct intel_spi_boardinfo *info;
+ u32 spi_base, rcba, bcr;
+
+ info = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->type = lpc_chipset_info[priv->chipset].spi_type;
+
+ switch (info->type) {
+ case INTEL_SPI_BYT:
+ pci_read_config_dword(dev, SPIBASE_BYT, &spi_base);
+ if (spi_base & SPIBASE_BYT_EN) {
+ res->start = spi_base & ~(SPIBASE_BYT_SZ - 1);
+ res->end = res->start + SPIBASE_BYT_SZ - 1;
+ }
+ break;
+
+ case INTEL_SPI_LPT:
+ pci_read_config_dword(dev, RCBABASE, &rcba);
+ if (rcba & 1) {
+ spi_base = round_down(rcba, SPIBASE_LPT_SZ);
+ res->start = spi_base + SPIBASE_LPT;
+ res->end = res->start + SPIBASE_LPT_SZ - 1;
+
+ /*
+ * Try to make the flash chip writeable now by
+ * setting BCR_WPD. It it fails we tell the driver
+ * that it can only read the chip.
+ */
+ pci_read_config_dword(dev, BCR, &bcr);
+ if (!(bcr & BCR_WPD)) {
+ bcr |= BCR_WPD;
+ pci_write_config_dword(dev, BCR, bcr);
+ pci_read_config_dword(dev, BCR, &bcr);
+ }
+ info->writeable = !!(bcr & BCR_WPD);
+ }
+ break;
+
+ case INTEL_SPI_BXT: {
+ unsigned int p2sb = PCI_DEVFN(13, 0);
+ unsigned int spi = PCI_DEVFN(13, 2);
+ struct pci_bus *bus = dev->bus;
+
+ /*
+ * The P2SB is hidden by BIOS and we need to unhide it in
+ * order to read BAR of the SPI flash device. Once that is
+ * done we hide it again.
+ */
+ pci_bus_write_config_byte(bus, p2sb, 0xe1, 0x0);
+ pci_bus_read_config_dword(bus, spi, PCI_BASE_ADDRESS_0,
+ &spi_base);
+ if (spi_base != ~0) {
+ res->start = spi_base & 0xfffffff0;
+ res->end = res->start + SPIBASE_APL_SZ - 1;
+
+ pci_bus_read_config_dword(bus, spi, BCR, &bcr);
+ if (!(bcr & BCR_WPD)) {
+ bcr |= BCR_WPD;
+ pci_bus_write_config_dword(bus, spi, BCR, bcr);
+ pci_bus_read_config_dword(bus, spi, BCR, &bcr);
+ }
+ info->writeable = !!(bcr & BCR_WPD);
+ }
+
+ pci_bus_write_config_byte(bus, p2sb, 0xe1, 0x1);
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+ if (!res->start)
+ return -ENODEV;
+
+ lpc_ich_spi_cell.platform_data = info;
+ lpc_ich_spi_cell.pdata_size = sizeof(*info);
+
+ return mfd_add_devices(&dev->dev, PLATFORM_DEVID_NONE,
+ &lpc_ich_spi_cell, 1, NULL, 0, NULL);
+}
+
static int lpc_ich_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
@@ -1099,6 +1224,12 @@
cell_added = true;
}
+ if (lpc_chipset_info[priv->chipset].spi_type) {
+ ret = lpc_ich_init_spi(dev);
+ if (!ret)
+ cell_added = true;
+ }
+
/*
* We only care if at least one or none of the cells registered
* successfully.
diff --git a/drivers/misc/genwqe/card_dev.c b/drivers/misc/genwqe/card_dev.c
index 7f1b282..cb290b8 100644
--- a/drivers/misc/genwqe/card_dev.c
+++ b/drivers/misc/genwqe/card_dev.c
@@ -1396,7 +1396,7 @@
* application which will decrease this reference from
* 1/unused to 0/illegal and not from 2/used 1/empty.
*/
- rc = atomic_read(&cd->cdev_genwqe.kobj.kref.refcount);
+ rc = kref_read(&cd->cdev_genwqe.kobj.kref);
if (rc != 1) {
dev_err(&pci_dev->dev,
"[%s] err: cdev_genwqe...refcount=%d\n", __func__, rc);
diff --git a/drivers/misc/lkdtm.h b/drivers/misc/lkdtm.h
index cfa1039..67d27be 100644
--- a/drivers/misc/lkdtm.h
+++ b/drivers/misc/lkdtm.h
@@ -19,8 +19,12 @@
void lkdtm_HARDLOCKUP(void);
void lkdtm_SPINLOCKUP(void);
void lkdtm_HUNG_TASK(void);
-void lkdtm_ATOMIC_UNDERFLOW(void);
-void lkdtm_ATOMIC_OVERFLOW(void);
+void lkdtm_REFCOUNT_SATURATE_INC(void);
+void lkdtm_REFCOUNT_SATURATE_ADD(void);
+void lkdtm_REFCOUNT_ZERO_DEC(void);
+void lkdtm_REFCOUNT_ZERO_INC(void);
+void lkdtm_REFCOUNT_ZERO_SUB(void);
+void lkdtm_REFCOUNT_ZERO_ADD(void);
void lkdtm_CORRUPT_LIST_ADD(void);
void lkdtm_CORRUPT_LIST_DEL(void);
diff --git a/drivers/misc/lkdtm_bugs.c b/drivers/misc/lkdtm_bugs.c
index 91edd0b..cba0837 100644
--- a/drivers/misc/lkdtm_bugs.c
+++ b/drivers/misc/lkdtm_bugs.c
@@ -6,6 +6,7 @@
*/
#include "lkdtm.h"
#include <linux/list.h>
+#include <linux/refcount.h>
#include <linux/sched.h>
struct lkdtm_list {
@@ -129,28 +130,86 @@
schedule();
}
-void lkdtm_ATOMIC_UNDERFLOW(void)
+void lkdtm_REFCOUNT_SATURATE_INC(void)
{
- atomic_t under = ATOMIC_INIT(INT_MIN);
+ refcount_t over = REFCOUNT_INIT(UINT_MAX - 1);
- pr_info("attempting good atomic increment\n");
- atomic_inc(&under);
- atomic_dec(&under);
+ pr_info("attempting good refcount decrement\n");
+ refcount_dec(&over);
+ refcount_inc(&over);
- pr_info("attempting bad atomic underflow\n");
- atomic_dec(&under);
+ pr_info("attempting bad refcount inc overflow\n");
+ refcount_inc(&over);
+ refcount_inc(&over);
+ if (refcount_read(&over) == UINT_MAX)
+ pr_err("Correctly stayed saturated, but no BUG?!\n");
+ else
+ pr_err("Fail: refcount wrapped\n");
}
-void lkdtm_ATOMIC_OVERFLOW(void)
+void lkdtm_REFCOUNT_SATURATE_ADD(void)
{
- atomic_t over = ATOMIC_INIT(INT_MAX);
+ refcount_t over = REFCOUNT_INIT(UINT_MAX - 1);
- pr_info("attempting good atomic decrement\n");
- atomic_dec(&over);
- atomic_inc(&over);
+ pr_info("attempting good refcount decrement\n");
+ refcount_dec(&over);
+ refcount_inc(&over);
- pr_info("attempting bad atomic overflow\n");
- atomic_inc(&over);
+ pr_info("attempting bad refcount add overflow\n");
+ refcount_add(2, &over);
+ if (refcount_read(&over) == UINT_MAX)
+ pr_err("Correctly stayed saturated, but no BUG?!\n");
+ else
+ pr_err("Fail: refcount wrapped\n");
+}
+
+void lkdtm_REFCOUNT_ZERO_DEC(void)
+{
+ refcount_t zero = REFCOUNT_INIT(1);
+
+ pr_info("attempting bad refcount decrement to zero\n");
+ refcount_dec(&zero);
+ if (refcount_read(&zero) == 0)
+ pr_err("Stayed at zero, but no BUG?!\n");
+ else
+ pr_err("Fail: refcount went crazy\n");
+}
+
+void lkdtm_REFCOUNT_ZERO_SUB(void)
+{
+ refcount_t zero = REFCOUNT_INIT(1);
+
+ pr_info("attempting bad refcount subtract past zero\n");
+ if (!refcount_sub_and_test(2, &zero))
+ pr_info("wrap attempt was noticed\n");
+ if (refcount_read(&zero) == 1)
+ pr_err("Correctly stayed above 0, but no BUG?!\n");
+ else
+ pr_err("Fail: refcount wrapped\n");
+}
+
+void lkdtm_REFCOUNT_ZERO_INC(void)
+{
+ refcount_t zero = REFCOUNT_INIT(0);
+
+ pr_info("attempting bad refcount increment from zero\n");
+ refcount_inc(&zero);
+ if (refcount_read(&zero) == 0)
+ pr_err("Stayed at zero, but no BUG?!\n");
+ else
+ pr_err("Fail: refcount went past zero\n");
+}
+
+void lkdtm_REFCOUNT_ZERO_ADD(void)
+{
+ refcount_t zero = REFCOUNT_INIT(0);
+
+ pr_info("attempting bad refcount addition from zero\n");
+ refcount_add(2, &zero);
+ if (refcount_read(&zero) == 0)
+ pr_err("Stayed at zero, but no BUG?!\n");
+ else
+ pr_err("Fail: refcount went past zero\n");
}
void lkdtm_CORRUPT_LIST_ADD(void)
diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c
index 7eeb71a..16e4cf1 100644
--- a/drivers/misc/lkdtm_core.c
+++ b/drivers/misc/lkdtm_core.c
@@ -220,8 +220,12 @@
CRASHTYPE(WRITE_RO),
CRASHTYPE(WRITE_RO_AFTER_INIT),
CRASHTYPE(WRITE_KERN),
- CRASHTYPE(ATOMIC_UNDERFLOW),
- CRASHTYPE(ATOMIC_OVERFLOW),
+ CRASHTYPE(REFCOUNT_SATURATE_INC),
+ CRASHTYPE(REFCOUNT_SATURATE_ADD),
+ CRASHTYPE(REFCOUNT_ZERO_DEC),
+ CRASHTYPE(REFCOUNT_ZERO_INC),
+ CRASHTYPE(REFCOUNT_ZERO_SUB),
+ CRASHTYPE(REFCOUNT_ZERO_ADD),
CRASHTYPE(USERCOPY_HEAP_SIZE_TO),
CRASHTYPE(USERCOPY_HEAP_SIZE_FROM),
CRASHTYPE(USERCOPY_HEAP_FLAG_TO),
diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c
index c6217a4..a617aa5 100644
--- a/drivers/misc/mei/debugfs.c
+++ b/drivers/misc/mei/debugfs.c
@@ -67,7 +67,7 @@
me_cl->props.max_number_of_connections,
me_cl->props.max_msg_length,
me_cl->props.single_recv_buf,
- atomic_read(&me_cl->refcnt.refcount));
+ kref_read(&me_cl->refcnt));
mei_me_cl_put(me_cl);
}
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index b61b52f9..0fccca0 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1706,10 +1706,10 @@
err = mmc_select_hs400(card);
if (err)
goto free_card;
- } else if (mmc_card_hs(card)) {
+ } else {
/* Select the desired bus width optionally */
err = mmc_select_bus_width(card);
- if (err > 0) {
+ if (err > 0 && mmc_card_hs(card)) {
err = mmc_select_hs_ddr(card);
if (err)
goto free_card;
diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c
index 283ff7e..d10fa6c 100644
--- a/drivers/mtd/bcm47xxpart.c
+++ b/drivers/mtd/bcm47xxpart.c
@@ -9,6 +9,7 @@
*
*/
+#include <linux/bcm47xx_nvram.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -83,6 +84,91 @@
return "rootfs";
}
+static int bcm47xxpart_parse_trx(struct mtd_info *master,
+ struct mtd_partition *trx,
+ struct mtd_partition *parts,
+ size_t parts_len)
+{
+ struct trx_header header;
+ size_t bytes_read;
+ int curr_part = 0;
+ int i, err;
+
+ if (parts_len < 3) {
+ pr_warn("No enough space to add TRX partitions!\n");
+ return -ENOMEM;
+ }
+
+ err = mtd_read(master, trx->offset, sizeof(header), &bytes_read,
+ (uint8_t *)&header);
+ if (err && !mtd_is_bitflip(err)) {
+ pr_err("mtd_read error while reading TRX header: %d\n", err);
+ return err;
+ }
+
+ i = 0;
+
+ /* We have LZMA loader if offset[2] points to sth */
+ if (header.offset[2]) {
+ bcm47xxpart_add_part(&parts[curr_part++], "loader",
+ trx->offset + header.offset[i], 0);
+ i++;
+ }
+
+ if (header.offset[i]) {
+ bcm47xxpart_add_part(&parts[curr_part++], "linux",
+ trx->offset + header.offset[i], 0);
+ i++;
+ }
+
+ if (header.offset[i]) {
+ size_t offset = trx->offset + header.offset[i];
+ const char *name = bcm47xxpart_trx_data_part_name(master,
+ offset);
+
+ bcm47xxpart_add_part(&parts[curr_part++], name, offset, 0);
+ i++;
+ }
+
+ /*
+ * Assume that every partition ends at the beginning of the one it is
+ * followed by.
+ */
+ for (i = 0; i < curr_part; i++) {
+ u64 next_part_offset = (i < curr_part - 1) ?
+ parts[i + 1].offset :
+ trx->offset + trx->size;
+
+ parts[i].size = next_part_offset - parts[i].offset;
+ }
+
+ return curr_part;
+}
+
+/**
+ * bcm47xxpart_bootpartition - gets index of TRX partition used by bootloader
+ *
+ * Some devices may have more than one TRX partition. In such case one of them
+ * is the main one and another a failsafe one. Bootloader may fallback to the
+ * failsafe firmware if it detects corruption of the main image.
+ *
+ * This function provides info about currently used TRX partition. It's the one
+ * containing kernel started by the bootloader.
+ */
+static int bcm47xxpart_bootpartition(void)
+{
+ char buf[4];
+ int bootpartition;
+
+ /* Check CFE environment variable */
+ if (bcm47xx_nvram_getenv("bootpartition", buf, sizeof(buf)) > 0) {
+ if (!kstrtoint(buf, 0, &bootpartition))
+ return bootpartition;
+ }
+
+ return 0;
+}
+
static int bcm47xxpart_parse(struct mtd_info *master,
const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
@@ -93,9 +179,8 @@
size_t bytes_read;
uint32_t offset;
uint32_t blocksize = master->erasesize;
- struct trx_header *trx;
- int trx_part = -1;
- int last_trx_part = -1;
+ int trx_parts[2]; /* Array with indexes of TRX partitions */
+ int trx_num = 0; /* Number of found TRX partitions */
int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, };
int err;
@@ -182,54 +267,18 @@
/* TRX */
if (buf[0x000 / 4] == TRX_MAGIC) {
- if (BCM47XXPART_MAX_PARTS - curr_part < 4) {
- pr_warn("Not enough partitions left to register trx, scanning stopped!\n");
- break;
- }
+ struct trx_header *trx;
- trx = (struct trx_header *)buf;
-
- trx_part = curr_part;
+ if (trx_num >= ARRAY_SIZE(trx_parts))
+ pr_warn("No enough space to store another TRX found at 0x%X\n",
+ offset);
+ else
+ trx_parts[trx_num++] = curr_part;
bcm47xxpart_add_part(&parts[curr_part++], "firmware",
offset, 0);
- i = 0;
- /* We have LZMA loader if offset[2] points to sth */
- if (trx->offset[2]) {
- bcm47xxpart_add_part(&parts[curr_part++],
- "loader",
- offset + trx->offset[i],
- 0);
- i++;
- }
-
- if (trx->offset[i]) {
- bcm47xxpart_add_part(&parts[curr_part++],
- "linux",
- offset + trx->offset[i],
- 0);
- i++;
- }
-
- /*
- * Pure rootfs size is known and can be calculated as:
- * trx->length - trx->offset[i]. We don't fill it as
- * we want to have jffs2 (overlay) in the same mtd.
- */
- if (trx->offset[i]) {
- const char *name;
-
- name = bcm47xxpart_trx_data_part_name(master, offset + trx->offset[i]);
- bcm47xxpart_add_part(&parts[curr_part++],
- name,
- offset + trx->offset[i],
- 0);
- i++;
- }
-
- last_trx_part = curr_part - 1;
-
/* Jump to the end of TRX */
+ trx = (struct trx_header *)buf;
offset = roundup(offset + trx->length, blocksize);
/* Next loop iteration will increase the offset */
offset -= blocksize;
@@ -307,9 +356,23 @@
parts[i + 1].offset : master->size;
parts[i].size = next_part_offset - parts[i].offset;
- if (i == last_trx_part && trx_part >= 0)
- parts[trx_part].size = next_part_offset -
- parts[trx_part].offset;
+ }
+
+ /* If there was TRX parse it now */
+ for (i = 0; i < trx_num; i++) {
+ struct mtd_partition *trx = &parts[trx_parts[i]];
+
+ if (i == bcm47xxpart_bootpartition()) {
+ int num_parts;
+
+ num_parts = bcm47xxpart_parse_trx(master, trx,
+ parts + curr_part,
+ BCM47XXPART_MAX_PARTS - curr_part);
+ if (num_parts > 0)
+ curr_part += num_parts;
+ } else {
+ trx->name = "failsafe";
+ }
}
*pparts = parts;
diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c
index 514be04..e2bd818 100644
--- a/drivers/mtd/devices/bcm47xxsflash.c
+++ b/drivers/mtd/devices/bcm47xxsflash.c
@@ -105,15 +105,33 @@
size_t *retlen, u_char *buf)
{
struct bcm47xxsflash *b47s = mtd->priv;
+ size_t orig_len = len;
/* Check address range */
if ((from + len) > mtd->size)
return -EINVAL;
- memcpy_fromio(buf, b47s->window + from, len);
- *retlen = len;
+ /* Read as much as possible using fast MMIO window */
+ if (from < BCM47XXSFLASH_WINDOW_SZ) {
+ size_t memcpy_len;
- return len;
+ memcpy_len = min(len, (size_t)(BCM47XXSFLASH_WINDOW_SZ - from));
+ memcpy_fromio(buf, b47s->window + from, memcpy_len);
+ from += memcpy_len;
+ len -= memcpy_len;
+ buf += memcpy_len;
+ }
+
+ /* Use indirect access for content out of the window */
+ for (; len; len--) {
+ b47s->cc_write(b47s, BCMA_CC_FLASHADDR, from++);
+ bcm47xxsflash_cmd(b47s, OPCODE_ST_READ4B);
+ *buf++ = b47s->cc_read(b47s, BCMA_CC_FLASHDATA);
+ }
+
+ *retlen = orig_len;
+
+ return orig_len;
}
static int bcm47xxsflash_write_st(struct mtd_info *mtd, u32 offset, size_t len,
@@ -284,7 +302,6 @@
b47s = devm_kzalloc(dev, sizeof(*b47s), GFP_KERNEL);
if (!b47s)
return -ENOMEM;
- sflash->priv = b47s;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
@@ -334,6 +351,8 @@
b47s->size = sflash->size;
bcm47xxsflash_fill_mtd(b47s, &pdev->dev);
+ platform_set_drvdata(pdev, b47s);
+
err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0);
if (err) {
pr_err("Failed to register MTD device: %d\n", err);
@@ -349,8 +368,7 @@
static int bcm47xxsflash_bcma_remove(struct platform_device *pdev)
{
- struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev);
- struct bcm47xxsflash *b47s = sflash->priv;
+ struct bcm47xxsflash *b47s = platform_get_drvdata(pdev);
mtd_device_unregister(&b47s->mtd);
iounmap(b47s->window);
diff --git a/drivers/mtd/devices/bcm47xxsflash.h b/drivers/mtd/devices/bcm47xxsflash.h
index 1564b62..b2d7b38 100644
--- a/drivers/mtd/devices/bcm47xxsflash.h
+++ b/drivers/mtd/devices/bcm47xxsflash.h
@@ -3,6 +3,8 @@
#include <linux/mtd/mtd.h>
+#define BCM47XXSFLASH_WINDOW_SZ SZ_16M
+
/* Used for ST flashes only. */
#define OPCODE_ST_WREN 0x0006 /* Write Enable */
#define OPCODE_ST_WRDIS 0x0004 /* Write Disable */
@@ -16,6 +18,7 @@
#define OPCODE_ST_RES 0x03ab /* Read Electronic Signature */
#define OPCODE_ST_CSA 0x1000 /* Keep chip select asserted */
#define OPCODE_ST_SSE 0x0220 /* Sub-sector Erase */
+#define OPCODE_ST_READ4B 0x6313 /* Read Data Bytes in 4Byte addressing mode */
/* Used for Atmel flashes only. */
#define OPCODE_AT_READ 0x07e8
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 9cf7fcd..c4df3b1 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -172,7 +172,8 @@
t[1].rx_buf = buf;
t[1].rx_nbits = m25p80_rx_nbits(nor);
- t[1].len = min(len, spi_max_transfer_size(spi));
+ t[1].len = min3(len, spi_max_transfer_size(spi),
+ spi_max_message_size(spi) - t[0].len);
spi_message_add_tail(&t[1], &m);
ret = spi_sync(spi, &m);
@@ -288,7 +289,6 @@
* should be kept for backward compatibility.
*/
{"at25df321a"}, {"at25df641"}, {"at26df081a"},
- {"mr25h256"},
{"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"},
{"mx25l25635e"},{"mx66l51235l"},
{"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"},
@@ -305,6 +305,11 @@
{"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"},
{"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"},
+ /* Everspin MRAMs (non-JEDEC) */
+ { "mr25h256" }, /* 256 Kib, 40 MHz */
+ { "mr25h10" }, /* 1 Mib, 40 MHz */
+ { "mr25h40" }, /* 4 Mib, 40 MHz */
+
{ },
};
MODULE_DEVICE_TABLE(spi, m25p_ids);
diff --git a/drivers/mtd/devices/serial_flash_cmds.h b/drivers/mtd/devices/serial_flash_cmds.h
index f59a125..8b81e15 100644
--- a/drivers/mtd/devices/serial_flash_cmds.h
+++ b/drivers/mtd/devices/serial_flash_cmds.h
@@ -18,19 +18,12 @@
#define SPINOR_OP_RDVCR 0x85
/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
-#define SPINOR_OP_READ_1_2_2 0xbb /* DUAL I/O READ */
-#define SPINOR_OP_READ_1_4_4 0xeb /* QUAD I/O READ */
-
#define SPINOR_OP_WRITE 0x02 /* PAGE PROGRAM */
#define SPINOR_OP_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
#define SPINOR_OP_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
#define SPINOR_OP_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
#define SPINOR_OP_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
-/* READ commands with 32-bit addressing */
-#define SPINOR_OP_READ4_1_2_2 0xbc
-#define SPINOR_OP_READ4_1_4_4 0xec
-
/* Configuration flags */
#define FLASH_FLAG_SINGLE 0x000000ff
#define FLASH_FLAG_READ_WRITE 0x00000001
diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c
index 5454b41..804313a 100644
--- a/drivers/mtd/devices/st_spi_fsm.c
+++ b/drivers/mtd/devices/st_spi_fsm.c
@@ -507,13 +507,13 @@
* - 'FAST' variants configured for 8 dummy cycles (see note above.)
*/
static struct seq_rw_config n25q_read4_configs[] = {
- {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 4, 0x00, 0, 8},
- {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
- {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2, 0, 2, 2, 0x00, 0, 8},
- {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
- {FLASH_FLAG_READ_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
- {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0},
- {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+ {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B, 0, 4, 4, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B, 0, 1, 4, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B, 0, 2, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B, 0, 1, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST_4B, 0, 1, 1, 0x00, 0, 8},
+ {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ_4B, 0, 1, 1, 0x00, 0, 0},
+ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
};
/*
@@ -553,13 +553,13 @@
* entering a state that is incompatible with the SPIBoot Controller.
*/
static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
- {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 4, 0x00, 2, 4},
- {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
- {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2, 0, 2, 2, 0x00, 4, 0},
- {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
- {FLASH_FLAG_READ_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
- {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0},
- {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+ {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B, 0, 4, 4, 0x00, 2, 4},
+ {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B, 0, 1, 4, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B, 0, 2, 2, 0x00, 4, 0},
+ {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B, 0, 1, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST_4B, 0, 1, 1, 0x00, 0, 8},
+ {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ_4B, 0, 1, 1, 0x00, 0, 0},
+ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
};
static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 5bcc896..542fdf8 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -75,7 +75,7 @@
taken from OF device tree.
config MTD_PHYSMAP_OF_VERSATILE
- bool "Support ARM Versatile physmap OF"
+ bool "ARM Versatile OF-based physical memory map handling"
depends on MTD_PHYSMAP_OF
depends on MFD_SYSCON
default y if (ARCH_INTEGRATOR || ARCH_VERSATILE || ARCH_REALVIEW)
@@ -84,6 +84,16 @@
platforms, basically to add a VPP (write protection) callback so
the flash can be taken out of write protection.
+config MTD_PHYSMAP_OF_GEMINI
+ bool "Cortina Gemini OF-based physical memory map handling"
+ depends on MTD_PHYSMAP_OF
+ depends on MFD_SYSCON
+ default ARCH_GEMINI
+ help
+ This provides some extra DT physmap parsing for the Gemini
+ platforms, some detection and setting up parallel mode on the
+ external interface.
+
config MTD_PMC_MSP_EVM
tristate "CFI Flash device mapped on PMC-Sierra MSP"
depends on PMC_MSP && MTD_CFI
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index 644f7d3..aef1846 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -17,10 +17,13 @@
obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o
obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
-obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o
ifdef CONFIG_MTD_PHYSMAP_OF_VERSATILE
-obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of_versatile.o
+physmap_of-objs += physmap_of_versatile.o
endif
+ifdef CONFIG_MTD_PHYSMAP_OF_GEMINI
+physmap_of-objs += physmap_of_gemini.o
+endif
+obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o
obj-$(CONFIG_MTD_PISMO) += pismo.o
obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o
obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o
diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c
index e17d02a..976d42f 100644
--- a/drivers/mtd/maps/ichxrom.c
+++ b/drivers/mtd/maps/ichxrom.c
@@ -57,10 +57,12 @@
{
struct ichxrom_map_info *map, *scratch;
u16 word;
+ int ret;
/* Disable writes through the rom window */
- pci_read_config_word(window->pdev, BIOS_CNTL, &word);
- pci_write_config_word(window->pdev, BIOS_CNTL, word & ~1);
+ ret = pci_read_config_word(window->pdev, BIOS_CNTL, &word);
+ if (!ret)
+ pci_write_config_word(window->pdev, BIOS_CNTL, word & ~1);
pci_dev_put(window->pdev);
/* Free all of the mtd devices */
diff --git a/drivers/mtd/maps/lantiq-flash.c b/drivers/mtd/maps/lantiq-flash.c
index c8febb3..3e33ab6 100644
--- a/drivers/mtd/maps/lantiq-flash.c
+++ b/drivers/mtd/maps/lantiq-flash.c
@@ -4,7 +4,7 @@
* by the Free Software Foundation.
*
* Copyright (C) 2004 Liu Peng Infineon IFAP DC COM CPE
- * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2010 John Crispin <john@phrozen.org>
*/
#include <linux/err.h>
@@ -209,5 +209,5 @@
module_platform_driver(ltq_mtd_driver);
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+MODULE_AUTHOR("John Crispin <john@phrozen.org>");
MODULE_DESCRIPTION("Lantiq SoC NOR");
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index 3fad359..14e8909 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -24,6 +24,7 @@
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
+#include "physmap_of_gemini.h"
#include "physmap_of_versatile.h"
struct of_flash_list {
@@ -241,11 +242,13 @@
info->list[i].map.size = res_size;
info->list[i].map.bankwidth = be32_to_cpup(width);
info->list[i].map.device_node = dp;
- err = of_flash_probe_versatile(dev, dp, &info->list[i].map);
- if (err) {
- dev_err(&dev->dev, "Can't probe Versatile VPP\n");
+
+ err = of_flash_probe_gemini(dev, dp, &info->list[i].map);
+ if (err)
return err;
- }
+ err = of_flash_probe_versatile(dev, dp, &info->list[i].map);
+ if (err)
+ return err;
err = -ENOMEM;
info->list[i].map.virt = ioremap(info->list[i].map.phys,
diff --git a/drivers/mtd/maps/physmap_of_gemini.c b/drivers/mtd/maps/physmap_of_gemini.c
new file mode 100644
index 0000000..9d371cd
--- /dev/null
+++ b/drivers/mtd/maps/physmap_of_gemini.c
@@ -0,0 +1,117 @@
+/*
+ * Cortina Systems Gemini OF physmap add-on
+ * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
+ *
+ * This SoC has an elaborate flash control register, so we need to
+ * detect and set it up when booting on this platform.
+ */
+#include <linux/export.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/mtd/map.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/bitops.h>
+#include "physmap_of_gemini.h"
+
+/*
+ * The Flash-relevant parts of the global status register
+ * These would also be relevant for a NAND driver.
+ */
+#define GLOBAL_STATUS 0x04
+#define FLASH_TYPE_MASK (0x3 << 24)
+#define FLASH_TYPE_NAND_2K (0x3 << 24)
+#define FLASH_TYPE_NAND_512 (0x2 << 24)
+#define FLASH_TYPE_PARALLEL (0x1 << 24)
+#define FLASH_TYPE_SERIAL (0x0 << 24)
+/* if parallel */
+#define FLASH_WIDTH_16BIT (1 << 23) /* else 8 bit */
+/* if serial */
+#define FLASH_ATMEL (1 << 23) /* else STM */
+
+#define FLASH_SIZE_MASK (0x3 << 21)
+#define NAND_256M (0x3 << 21) /* and more */
+#define NAND_128M (0x2 << 21)
+#define NAND_64M (0x1 << 21)
+#define NAND_32M (0x0 << 21)
+#define ATMEL_16M (0x3 << 21) /* and more */
+#define ATMEL_8M (0x2 << 21)
+#define ATMEL_4M_2M (0x1 << 21)
+#define ATMEL_1M (0x0 << 21) /* and less */
+#define STM_32M (1 << 22) /* and more */
+#define STM_16M (0 << 22) /* and less */
+
+#define FLASH_PARALLEL_HIGH_PIN_CNT (1 << 20) /* else low pin cnt */
+
+/* Miscellaneous Control Register */
+#define GLOBAL_MISC_CTRL 0x30
+#define FLASH_PADS_MASK 0x07
+#define NAND_PADS_DISABLE BIT(2)
+#define PFLASH_PADS_DISABLE BIT(1)
+#define SFLASH_PADS_DISABLE BIT(0)
+
+static const struct of_device_id syscon_match[] = {
+ { .compatible = "cortina,gemini-syscon" },
+ { },
+};
+
+int of_flash_probe_gemini(struct platform_device *pdev,
+ struct device_node *np,
+ struct map_info *map)
+{
+ static struct regmap *rmap;
+ struct device *dev = &pdev->dev;
+ u32 val;
+ int ret;
+
+ /* Multiplatform guard */
+ if (!of_device_is_compatible(np, "cortina,gemini-flash"))
+ return 0;
+
+ rmap = syscon_regmap_lookup_by_phandle(np, "syscon");
+ if (IS_ERR(rmap)) {
+ dev_err(dev, "no syscon\n");
+ return PTR_ERR(rmap);
+ }
+
+ ret = regmap_read(rmap, GLOBAL_STATUS, &val);
+ if (ret) {
+ dev_err(dev, "failed to read global status register\n");
+ return -ENODEV;
+ }
+ dev_dbg(dev, "global status reg: %08x\n", val);
+
+ /*
+ * It would be contradictory if a physmap flash was NOT parallel.
+ */
+ if ((val & FLASH_TYPE_MASK) != FLASH_TYPE_PARALLEL) {
+ dev_err(dev, "flash is not parallel\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Complain if DT data and hardware definition is different.
+ */
+ if (val & FLASH_WIDTH_16BIT) {
+ if (map->bankwidth != 2)
+ dev_warn(dev, "flash hardware say flash is 16 bit wide but DT says it is %d bits wide\n",
+ map->bankwidth * 8);
+ } else {
+ if (map->bankwidth != 1)
+ dev_warn(dev, "flash hardware say flash is 8 bit wide but DT says it is %d bits wide\n",
+ map->bankwidth * 8);
+ }
+
+ /* Activate parallel (NOR flash) mode */
+ ret = regmap_update_bits(rmap, GLOBAL_MISC_CTRL,
+ FLASH_PADS_MASK,
+ SFLASH_PADS_DISABLE | NAND_PADS_DISABLE);
+ if (ret) {
+ dev_err(dev, "unable to set up physmap pads\n");
+ return -ENODEV;
+ }
+
+ dev_info(&pdev->dev, "initialized Gemini-specific physmap control\n");
+
+ return 0;
+}
diff --git a/drivers/mtd/maps/physmap_of_gemini.h b/drivers/mtd/maps/physmap_of_gemini.h
new file mode 100644
index 0000000..c675025
--- /dev/null
+++ b/drivers/mtd/maps/physmap_of_gemini.h
@@ -0,0 +1,16 @@
+#include <linux/of.h>
+#include <linux/mtd/map.h>
+
+#ifdef CONFIG_MTD_PHYSMAP_OF_GEMINI
+int of_flash_probe_gemini(struct platform_device *pdev,
+ struct device_node *np,
+ struct map_info *map);
+#else
+static inline
+int of_flash_probe_gemini(struct platform_device *pdev,
+ struct device_node *np,
+ struct map_info *map)
+{
+ return 0;
+}
+#endif
diff --git a/drivers/mtd/maps/physmap_of_versatile.c b/drivers/mtd/maps/physmap_of_versatile.c
index 0f39b2a..8c6ccde 100644
--- a/drivers/mtd/maps/physmap_of_versatile.c
+++ b/drivers/mtd/maps/physmap_of_versatile.c
@@ -252,4 +252,3 @@
return 0;
}
-EXPORT_SYMBOL_GPL(of_flash_probe_versatile);
diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c
index f9fa3fa..2051f28 100644
--- a/drivers/mtd/maps/pmcmsp-flash.c
+++ b/drivers/mtd/maps/pmcmsp-flash.c
@@ -139,15 +139,13 @@
}
msp_maps[i].bankwidth = 1;
- msp_maps[i].name = kmalloc(7, GFP_KERNEL);
+ msp_maps[i].name = kstrndup(flash_name, 7, GFP_KERNEL);
if (!msp_maps[i].name) {
iounmap(msp_maps[i].virt);
kfree(msp_parts[i]);
goto cleanup_loop;
}
- msp_maps[i].name = strncpy(msp_maps[i].name, flash_name, 7);
-
for (j = 0; j < pcnt; j++) {
part_name[5] = '0' + i;
part_name[7] = '0' + j;
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index ce5ccc5..3568294 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -451,7 +451,7 @@
* data. For our userspace tools it is important to dump areas
* with ECC errors!
* For kernel internal usage it also might return -EUCLEAN
- * to signal the caller that a bitflip has occured and has
+ * to signal the caller that a bitflip has occurred and has
* been corrected by the ECC algorithm.
*
* Note: currently the standard NAND function, nand_read_oob_std,
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 052772f..66a9ded 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -1128,7 +1128,7 @@
* @oobecc: OOB region struct filled with the appropriate ECC position
* information
*
- * This functions return ECC section information in the OOB area. I you want
+ * This function returns ECC section information in the OOB area. If you want
* to get all the ECC bytes information, then you should call
* mtd_ooblayout_ecc(mtd, section++, oobecc) until it returns -ERANGE.
*
@@ -1160,7 +1160,7 @@
* @oobfree: OOB region struct filled with the appropriate free position
* information
*
- * This functions return free bytes position in the OOB area. I you want
+ * This function returns free bytes position in the OOB area. If you want
* to get all the free bytes information, then you should call
* mtd_ooblayout_free(mtd, section++, oobfree) until it returns -ERANGE.
*
@@ -1190,7 +1190,7 @@
* @iter: iterator function. Should be either mtd_ooblayout_free or
* mtd_ooblayout_ecc depending on the region type you're searching for
*
- * This functions returns the section id and oobregion information of a
+ * This function returns the section id and oobregion information of a
* specific byte. For example, say you want to know where the 4th ECC byte is
* stored, you'll use:
*
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index fccdd49..ea5e530 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -349,6 +349,14 @@
.free = part_ooblayout_free,
};
+static int part_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+ struct mtd_part *part = mtd_to_part(mtd);
+
+ return part->master->_max_bad_blocks(part->master,
+ ofs + part->offset, len);
+}
+
static inline void free_partition(struct mtd_part *p)
{
kfree(p->mtd.name);
@@ -424,6 +432,7 @@
slave->mtd.dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) ?
&master->dev :
master->dev.parent;
+ slave->mtd.dev.of_node = part->of_node;
slave->mtd._read = part_read;
slave->mtd._write = part_write;
@@ -475,6 +484,8 @@
slave->mtd._block_isbad = part_block_isbad;
if (master->_block_markbad)
slave->mtd._block_markbad = part_block_markbad;
+ if (master->_max_bad_blocks)
+ slave->mtd._max_bad_blocks = part_max_bad_blocks;
if (master->_get_device)
slave->mtd._get_device = part_get_device;
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 9ce5dcb..6d4d567 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -426,6 +426,7 @@
config MTD_NAND_OXNAS
tristate "NAND Flash support for Oxford Semiconductor SoC"
+ depends on ARCH_OXNAS || COMPILE_TEST
depends on HAS_IOMEM
help
This enables the NAND flash controller on Oxford Semiconductor SoCs.
@@ -535,6 +536,7 @@
config MTD_NAND_FSMC
tristate "Support for NAND on ST Micros FSMC"
+ depends on OF
depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300
help
Enables support for NAND Flash chips on the ST Microelectronics
diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
index 0a177b1..d1570f5 100644
--- a/drivers/mtd/nand/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/fsl_ifc_nand.c
@@ -258,9 +258,15 @@
int bufnum = nctrl->page & priv->bufnum_mask;
int sector = bufnum * chip->ecc.steps;
int sector_end = sector + chip->ecc.steps - 1;
+ __be32 *eccstat_regs;
+
+ if (ctrl->version >= FSL_IFC_VERSION_2_0_0)
+ eccstat_regs = ifc->ifc_nand.v2_nand_eccstat;
+ else
+ eccstat_regs = ifc->ifc_nand.v1_nand_eccstat;
for (i = sector / 4; i <= sector_end / 4; i++)
- eccstat[i] = ifc_in32(&ifc->ifc_nand.nand_eccstat[i]);
+ eccstat[i] = ifc_in32(&eccstat_regs[i]);
for (i = sector; i <= sector_end; i++) {
errors = check_read_ecc(mtd, ctrl, eccstat, i);
diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c
index 4924b43..bda1e46 100644
--- a/drivers/mtd/nand/fsmc_nand.c
+++ b/drivers/mtd/nand/fsmc_nand.c
@@ -35,10 +35,133 @@
#include <linux/mtd/partitions.h>
#include <linux/io.h>
#include <linux/slab.h>
-#include <linux/mtd/fsmc.h>
#include <linux/amba/bus.h>
#include <mtd/mtd-abi.h>
+#define FSMC_NAND_BW8 1
+#define FSMC_NAND_BW16 2
+
+#define FSMC_MAX_NOR_BANKS 4
+#define FSMC_MAX_NAND_BANKS 4
+
+#define FSMC_FLASH_WIDTH8 1
+#define FSMC_FLASH_WIDTH16 2
+
+/* fsmc controller registers for NOR flash */
+#define CTRL 0x0
+ /* ctrl register definitions */
+ #define BANK_ENABLE (1 << 0)
+ #define MUXED (1 << 1)
+ #define NOR_DEV (2 << 2)
+ #define WIDTH_8 (0 << 4)
+ #define WIDTH_16 (1 << 4)
+ #define RSTPWRDWN (1 << 6)
+ #define WPROT (1 << 7)
+ #define WRT_ENABLE (1 << 12)
+ #define WAIT_ENB (1 << 13)
+
+#define CTRL_TIM 0x4
+ /* ctrl_tim register definitions */
+
+#define FSMC_NOR_BANK_SZ 0x8
+#define FSMC_NOR_REG_SIZE 0x40
+
+#define FSMC_NOR_REG(base, bank, reg) (base + \
+ FSMC_NOR_BANK_SZ * (bank) + \
+ reg)
+
+/* fsmc controller registers for NAND flash */
+#define PC 0x00
+ /* pc register definitions */
+ #define FSMC_RESET (1 << 0)
+ #define FSMC_WAITON (1 << 1)
+ #define FSMC_ENABLE (1 << 2)
+ #define FSMC_DEVTYPE_NAND (1 << 3)
+ #define FSMC_DEVWID_8 (0 << 4)
+ #define FSMC_DEVWID_16 (1 << 4)
+ #define FSMC_ECCEN (1 << 6)
+ #define FSMC_ECCPLEN_512 (0 << 7)
+ #define FSMC_ECCPLEN_256 (1 << 7)
+ #define FSMC_TCLR_1 (1)
+ #define FSMC_TCLR_SHIFT (9)
+ #define FSMC_TCLR_MASK (0xF)
+ #define FSMC_TAR_1 (1)
+ #define FSMC_TAR_SHIFT (13)
+ #define FSMC_TAR_MASK (0xF)
+#define STS 0x04
+ /* sts register definitions */
+ #define FSMC_CODE_RDY (1 << 15)
+#define COMM 0x08
+ /* comm register definitions */
+ #define FSMC_TSET_0 0
+ #define FSMC_TSET_SHIFT 0
+ #define FSMC_TSET_MASK 0xFF
+ #define FSMC_TWAIT_6 6
+ #define FSMC_TWAIT_SHIFT 8
+ #define FSMC_TWAIT_MASK 0xFF
+ #define FSMC_THOLD_4 4
+ #define FSMC_THOLD_SHIFT 16
+ #define FSMC_THOLD_MASK 0xFF
+ #define FSMC_THIZ_1 1
+ #define FSMC_THIZ_SHIFT 24
+ #define FSMC_THIZ_MASK 0xFF
+#define ATTRIB 0x0C
+#define IOATA 0x10
+#define ECC1 0x14
+#define ECC2 0x18
+#define ECC3 0x1C
+#define FSMC_NAND_BANK_SZ 0x20
+
+#define FSMC_NAND_REG(base, bank, reg) (base + FSMC_NOR_REG_SIZE + \
+ (FSMC_NAND_BANK_SZ * (bank)) + \
+ reg)
+
+#define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ)
+
+struct fsmc_nand_timings {
+ uint8_t tclr;
+ uint8_t tar;
+ uint8_t thiz;
+ uint8_t thold;
+ uint8_t twait;
+ uint8_t tset;
+};
+
+enum access_mode {
+ USE_DMA_ACCESS = 1,
+ USE_WORD_ACCESS,
+};
+
+/**
+ * fsmc_nand_platform_data - platform specific NAND controller config
+ * @nand_timings: timing setup for the physical NAND interface
+ * @partitions: partition table for the platform, use a default fallback
+ * if this is NULL
+ * @nr_partitions: the number of partitions in the previous entry
+ * @options: different options for the driver
+ * @width: bus width
+ * @bank: default bank
+ * @select_bank: callback to select a certain bank, this is
+ * platform-specific. If the controller only supports one bank
+ * this may be set to NULL
+ */
+struct fsmc_nand_platform_data {
+ struct fsmc_nand_timings *nand_timings;
+ struct mtd_partition *partitions;
+ unsigned int nr_partitions;
+ unsigned int options;
+ unsigned int width;
+ unsigned int bank;
+
+ enum access_mode mode;
+
+ void (*select_bank)(uint32_t bank, uint32_t busw);
+
+ /* priv structures for dma accesses */
+ void *read_dma_priv;
+ void *write_dma_priv;
+};
+
static int fsmc_ecc1_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
@@ -714,7 +837,6 @@
return true;
}
-#ifdef CONFIG_OF
static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
struct device_node *np)
{
@@ -757,13 +879,6 @@
}
return 0;
}
-#else
-static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
- struct device_node *np)
-{
- return -ENOSYS;
-}
-#endif
/*
* fsmc_nand_probe - Probe function
@@ -782,19 +897,15 @@
u32 pid;
int i;
- if (np) {
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- pdev->dev.platform_data = pdata;
- ret = fsmc_nand_probe_config_dt(pdev, np);
- if (ret) {
- dev_err(&pdev->dev, "no platform data\n");
- return -ENODEV;
- }
- }
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
- if (!pdata) {
- dev_err(&pdev->dev, "platform data is NULL\n");
- return -EINVAL;
+ pdev->dev.platform_data = pdata;
+ ret = fsmc_nand_probe_config_dt(pdev, np);
+ if (ret) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -ENODEV;
}
/* Allocate memory for the device structure (and zero it) */
diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c
index 53bafe2..a0669a3 100644
--- a/drivers/mtd/nand/lpc32xx_slc.c
+++ b/drivers/mtd/nand/lpc32xx_slc.c
@@ -797,22 +797,17 @@
struct resource *rc;
int res;
- rc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (rc == NULL) {
- dev_err(&pdev->dev, "No memory resource found for device\n");
- return -EBUSY;
- }
-
/* Allocate memory for the device structure (and zero it) */
host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
if (!host)
return -ENOMEM;
- host->io_base_dma = rc->start;
+ rc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
host->io_base = devm_ioremap_resource(&pdev->dev, rc);
if (IS_ERR(host->io_base))
return PTR_ERR(host->io_base);
+ host->io_base_dma = rc->start;
if (pdev->dev.of_node)
host->ncfg = lpc32xx_parse_dt(&pdev->dev);
if (!host->ncfg) {
diff --git a/drivers/mtd/nand/mtk_nand.c b/drivers/mtd/nand/mtk_nand.c
index 6c3eed3..6c517c6 100644
--- a/drivers/mtd/nand/mtk_nand.c
+++ b/drivers/mtd/nand/mtk_nand.c
@@ -1383,7 +1383,6 @@
nfc->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(nfc->regs)) {
ret = PTR_ERR(nfc->regs);
- dev_err(dev, "no nfi base\n");
goto release_ecc;
}
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index ec1c28a..1492c12 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -3263,6 +3263,42 @@
}
/**
+ * nand_max_bad_blocks - [MTD Interface] Max number of bad blocks for an mtd
+ * @mtd: MTD device structure
+ * @ofs: offset relative to mtd start
+ * @len: length of mtd
+ */
+static int nand_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u32 part_start_block;
+ u32 part_end_block;
+ u32 part_start_die;
+ u32 part_end_die;
+
+ /*
+ * max_bb_per_die and blocks_per_die used to determine
+ * the maximum bad block count.
+ */
+ if (!chip->max_bb_per_die || !chip->blocks_per_die)
+ return -ENOTSUPP;
+
+ /* Get the start and end of the partition in erase blocks. */
+ part_start_block = mtd_div_by_eb(ofs, mtd);
+ part_end_block = mtd_div_by_eb(len, mtd) + part_start_block - 1;
+
+ /* Get the start and end LUNs of the partition. */
+ part_start_die = part_start_block / chip->blocks_per_die;
+ part_end_die = part_end_block / chip->blocks_per_die;
+
+ /*
+ * Look up the bad blocks per unit and multiply by the number of units
+ * that the partition spans.
+ */
+ return chip->max_bb_per_die * (part_end_die - part_start_die + 1);
+}
+
+/**
* nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
* @mtd: MTD device structure
* @chip: nand chip info structure
@@ -3592,6 +3628,9 @@
chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
chip->bits_per_cell = p->bits_per_cell;
+ chip->max_bb_per_die = le16_to_cpu(p->bb_per_lun);
+ chip->blocks_per_die = le32_to_cpu(p->blocks_per_lun);
+
if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS)
*busw = NAND_BUSWIDTH_16;
else
@@ -4815,6 +4854,7 @@
mtd->_block_isreserved = nand_block_isreserved;
mtd->_block_isbad = nand_block_isbad;
mtd->_block_markbad = nand_block_markbad;
+ mtd->_max_bad_blocks = nand_max_bad_blocks;
mtd->writebufsize = mtd->writesize;
/*
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index b3a332f..4a2f75b 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -185,6 +185,7 @@
{NAND_MFR_SANDISK, "SanDisk"},
{NAND_MFR_INTEL, "Intel"},
{NAND_MFR_ATO, "ATO"},
+ {NAND_MFR_WINBOND, "Winbond"},
{0x0, "Unknown"}
};
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index e40482a..0eeeb8b 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -321,6 +321,10 @@
ret = wait_for_completion_timeout(&nfc->complete,
msecs_to_jiffies(timeout_ms));
+ if (!ret)
+ ret = -ETIMEDOUT;
+ else
+ ret = 0;
writel(0, nfc->regs + NFC_REG_INT);
} else {
@@ -518,6 +522,8 @@
u32 tmp;
while (len > offs) {
+ bool poll = false;
+
cnt = min(len - offs, NFC_SRAM_SIZE);
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
@@ -528,7 +534,11 @@
tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
writel(tmp, nfc->regs + NFC_REG_CMD);
- ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
+ /* Arbitrary limit for polling mode */
+ if (cnt < 64)
+ poll = true;
+
+ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0);
if (ret)
break;
@@ -551,6 +561,8 @@
u32 tmp;
while (len > offs) {
+ bool poll = false;
+
cnt = min(len - offs, NFC_SRAM_SIZE);
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
@@ -563,7 +575,11 @@
NFC_ACCESS_DIR;
writel(tmp, nfc->regs + NFC_REG_CMD);
- ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
+ /* Arbitrary limit for polling mode */
+ if (cnt < 64)
+ poll = true;
+
+ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0);
if (ret)
break;
@@ -588,10 +604,6 @@
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
int ret;
- ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
- if (ret)
- return;
-
if (dat == NAND_CMD_NONE && (ctrl & NAND_NCE) &&
!(ctrl & (NAND_CLE | NAND_ALE))) {
u32 cmd = 0;
@@ -621,6 +633,10 @@
writel(sunxi_nand->addr[1],
nfc->regs + NFC_REG_ADDR_HIGH);
+ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+ if (ret)
+ return;
+
writel(cmd, nfc->regs + NFC_REG_CMD);
sunxi_nand->addr[0] = 0;
sunxi_nand->addr[1] = 0;
@@ -957,7 +973,7 @@
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
nfc->regs + NFC_REG_CMD);
- ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
+ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
sunxi_nfc_randomizer_disable(mtd);
if (ret)
return ret;
@@ -1069,7 +1085,7 @@
writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS,
nfc->regs + NFC_REG_CMD);
- ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
+ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
if (ret)
dmaengine_terminate_all(nfc->dmac);
@@ -1189,7 +1205,7 @@
NFC_ACCESS_DIR | NFC_ECC_OP,
nfc->regs + NFC_REG_CMD);
- ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
+ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
sunxi_nfc_randomizer_disable(mtd);
if (ret)
return ret;
@@ -1428,7 +1444,7 @@
NFC_DATA_TRANS | NFC_ACCESS_DIR,
nfc->regs + NFC_REG_CMD);
- ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
+ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
if (ret)
dmaengine_terminate_all(nfc->dmac);
diff --git a/drivers/mtd/nand/xway_nand.c b/drivers/mtd/nand/xway_nand.c
index 895101a..ddee400 100644
--- a/drivers/mtd/nand/xway_nand.c
+++ b/drivers/mtd/nand/xway_nand.c
@@ -3,7 +3,7 @@
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
- * Copyright © 2012 John Crispin <blogic@openwrt.org>
+ * Copyright © 2012 John Crispin <john@phrozen.org>
* Copyright © 2016 Hauke Mehrtens <hauke@hauke-m.de>
*/
diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c
index ede407d..4644701 100644
--- a/drivers/mtd/ofpart.c
+++ b/drivers/mtd/ofpart.c
@@ -108,6 +108,7 @@
parts[i].offset = of_read_number(reg, a_cells);
parts[i].size = of_read_number(reg + a_cells, s_cells);
+ parts[i].of_node = pp;
partname = of_get_property(pp, "label", &len);
if (!partname)
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
index 4a682ee..7252087 100644
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -29,6 +29,16 @@
Please note that some tools/drivers/filesystems may not work with
4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum).
+config SPI_ASPEED_SMC
+ tristate "Aspeed flash controllers in SPI mode"
+ depends on ARCH_ASPEED || COMPILE_TEST
+ depends on HAS_IOMEM && OF
+ help
+ This enables support for the Firmware Memory controller (FMC)
+ in the Aspeed AST2500/AST2400 SoCs when attached to SPI NOR chips,
+ and support for the SPI flash memory controller (SPI) for
+ the host firmware. The implementation only supports SPI NOR.
+
config SPI_ATMEL_QUADSPI
tristate "Atmel Quad SPI Controller"
depends on ARCH_AT91 || (ARM && COMPILE_TEST)
@@ -40,7 +50,7 @@
config SPI_CADENCE_QUADSPI
tristate "Cadence Quad SPI controller"
- depends on OF && ARM
+ depends on OF && (ARM || COMPILE_TEST)
help
Enable support for the Cadence Quad SPI Flash controller.
@@ -76,4 +86,24 @@
Flash. Enable this option if you have a device with a SPIFI
controller and want to access the Flash as a mtd device.
+config SPI_INTEL_SPI
+ tristate
+
+config SPI_INTEL_SPI_PLATFORM
+ tristate "Intel PCH/PCU SPI flash platform driver" if EXPERT
+ depends on X86
+ select SPI_INTEL_SPI
+ help
+ This enables platform support for the Intel PCH/PCU SPI
+ controller in master mode. This controller is present in modern
+ Intel hardware and is used to hold BIOS and other persistent
+ settings. Using this driver it is possible to upgrade BIOS
+ directly from Linux.
+
+ Say N here unless you know what you are doing. Overwriting the
+ SPI flash may render the system unbootable.
+
+ To compile this driver as a module, choose M here: the module
+ will be called intel-spi-platform.
+
endif # MTD_SPI_NOR
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile
index 121695e..72238a7 100644
--- a/drivers/mtd/spi-nor/Makefile
+++ b/drivers/mtd/spi-nor/Makefile
@@ -1,7 +1,10 @@
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
+obj-$(CONFIG_SPI_ASPEED_SMC) += aspeed-smc.o
obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o
obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o
obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o
obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o
obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o
+obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o
+obj-$(CONFIG_SPI_INTEL_SPI_PLATFORM) += intel-spi-platform.o
diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
new file mode 100644
index 0000000..56051d3
--- /dev/null
+++ b/drivers/mtd/spi-nor/aspeed-smc.c
@@ -0,0 +1,754 @@
+/*
+ * ASPEED Static Memory Controller driver
+ *
+ * Copyright (c) 2015-2016, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/spi-nor.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/sysfs.h>
+
+#define DEVICE_NAME "aspeed-smc"
+
+/*
+ * The driver only support SPI flash
+ */
+enum aspeed_smc_flash_type {
+ smc_type_nor = 0,
+ smc_type_nand = 1,
+ smc_type_spi = 2,
+};
+
+struct aspeed_smc_chip;
+
+struct aspeed_smc_info {
+ u32 maxsize; /* maximum size of chip window */
+ u8 nce; /* number of chip enables */
+ bool hastype; /* flash type field exists in config reg */
+ u8 we0; /* shift for write enable bit for CE0 */
+ u8 ctl0; /* offset in regs of ctl for CE0 */
+
+ void (*set_4b)(struct aspeed_smc_chip *chip);
+};
+
+static void aspeed_smc_chip_set_4b_spi_2400(struct aspeed_smc_chip *chip);
+static void aspeed_smc_chip_set_4b(struct aspeed_smc_chip *chip);
+
+static const struct aspeed_smc_info fmc_2400_info = {
+ .maxsize = 64 * 1024 * 1024,
+ .nce = 5,
+ .hastype = true,
+ .we0 = 16,
+ .ctl0 = 0x10,
+ .set_4b = aspeed_smc_chip_set_4b,
+};
+
+static const struct aspeed_smc_info spi_2400_info = {
+ .maxsize = 64 * 1024 * 1024,
+ .nce = 1,
+ .hastype = false,
+ .we0 = 0,
+ .ctl0 = 0x04,
+ .set_4b = aspeed_smc_chip_set_4b_spi_2400,
+};
+
+static const struct aspeed_smc_info fmc_2500_info = {
+ .maxsize = 256 * 1024 * 1024,
+ .nce = 3,
+ .hastype = true,
+ .we0 = 16,
+ .ctl0 = 0x10,
+ .set_4b = aspeed_smc_chip_set_4b,
+};
+
+static const struct aspeed_smc_info spi_2500_info = {
+ .maxsize = 128 * 1024 * 1024,
+ .nce = 2,
+ .hastype = false,
+ .we0 = 16,
+ .ctl0 = 0x10,
+ .set_4b = aspeed_smc_chip_set_4b,
+};
+
+enum aspeed_smc_ctl_reg_value {
+ smc_base, /* base value without mode for other commands */
+ smc_read, /* command reg for (maybe fast) reads */
+ smc_write, /* command reg for writes */
+ smc_max,
+};
+
+struct aspeed_smc_controller;
+
+struct aspeed_smc_chip {
+ int cs;
+ struct aspeed_smc_controller *controller;
+ void __iomem *ctl; /* control register */
+ void __iomem *ahb_base; /* base of chip window */
+ u32 ctl_val[smc_max]; /* control settings */
+ enum aspeed_smc_flash_type type; /* what type of flash */
+ struct spi_nor nor;
+};
+
+struct aspeed_smc_controller {
+ struct device *dev;
+
+ struct mutex mutex; /* controller access mutex */
+ const struct aspeed_smc_info *info; /* type info of controller */
+ void __iomem *regs; /* controller registers */
+ void __iomem *ahb_base; /* per-chip windows resource */
+
+ struct aspeed_smc_chip *chips[0]; /* pointers to attached chips */
+};
+
+/*
+ * SPI Flash Configuration Register (AST2500 SPI)
+ * or
+ * Type setting Register (AST2500 FMC).
+ * CE0 and CE1 can only be of type SPI. CE2 can be of type NOR but the
+ * driver does not support it.
+ */
+#define CONFIG_REG 0x0
+#define CONFIG_DISABLE_LEGACY BIT(31) /* 1 */
+
+#define CONFIG_CE2_WRITE BIT(18)
+#define CONFIG_CE1_WRITE BIT(17)
+#define CONFIG_CE0_WRITE BIT(16)
+
+#define CONFIG_CE2_TYPE BIT(4) /* AST2500 FMC only */
+#define CONFIG_CE1_TYPE BIT(2) /* AST2500 FMC only */
+#define CONFIG_CE0_TYPE BIT(0) /* AST2500 FMC only */
+
+/*
+ * CE Control Register
+ */
+#define CE_CONTROL_REG 0x4
+
+/*
+ * CEx Control Register
+ */
+#define CONTROL_AAF_MODE BIT(31)
+#define CONTROL_IO_MODE_MASK GENMASK(30, 28)
+#define CONTROL_IO_DUAL_DATA BIT(29)
+#define CONTROL_IO_DUAL_ADDR_DATA (BIT(29) | BIT(28))
+#define CONTROL_IO_QUAD_DATA BIT(30)
+#define CONTROL_IO_QUAD_ADDR_DATA (BIT(30) | BIT(28))
+#define CONTROL_CE_INACTIVE_SHIFT 24
+#define CONTROL_CE_INACTIVE_MASK GENMASK(27, \
+ CONTROL_CE_INACTIVE_SHIFT)
+/* 0 = 16T ... 15 = 1T T=HCLK */
+#define CONTROL_COMMAND_SHIFT 16
+#define CONTROL_DUMMY_COMMAND_OUT BIT(15)
+#define CONTROL_IO_DUMMY_HI BIT(14)
+#define CONTROL_IO_DUMMY_HI_SHIFT 14
+#define CONTROL_CLK_DIV4 BIT(13) /* others */
+#define CONTROL_IO_ADDRESS_4B BIT(13) /* AST2400 SPI */
+#define CONTROL_RW_MERGE BIT(12)
+#define CONTROL_IO_DUMMY_LO_SHIFT 6
+#define CONTROL_IO_DUMMY_LO GENMASK(7, \
+ CONTROL_IO_DUMMY_LO_SHIFT)
+#define CONTROL_IO_DUMMY_MASK (CONTROL_IO_DUMMY_HI | \
+ CONTROL_IO_DUMMY_LO)
+#define CONTROL_IO_DUMMY_SET(dummy) \
+ (((((dummy) >> 2) & 0x1) << CONTROL_IO_DUMMY_HI_SHIFT) | \
+ (((dummy) & 0x3) << CONTROL_IO_DUMMY_LO_SHIFT))
+
+#define CONTROL_CLOCK_FREQ_SEL_SHIFT 8
+#define CONTROL_CLOCK_FREQ_SEL_MASK GENMASK(11, \
+ CONTROL_CLOCK_FREQ_SEL_SHIFT)
+#define CONTROL_LSB_FIRST BIT(5)
+#define CONTROL_CLOCK_MODE_3 BIT(4)
+#define CONTROL_IN_DUAL_DATA BIT(3)
+#define CONTROL_CE_STOP_ACTIVE_CONTROL BIT(2)
+#define CONTROL_COMMAND_MODE_MASK GENMASK(1, 0)
+#define CONTROL_COMMAND_MODE_NORMAL 0
+#define CONTROL_COMMAND_MODE_FREAD 1
+#define CONTROL_COMMAND_MODE_WRITE 2
+#define CONTROL_COMMAND_MODE_USER 3
+
+#define CONTROL_KEEP_MASK \
+ (CONTROL_AAF_MODE | CONTROL_CE_INACTIVE_MASK | CONTROL_CLK_DIV4 | \
+ CONTROL_IO_DUMMY_MASK | CONTROL_CLOCK_FREQ_SEL_MASK | \
+ CONTROL_LSB_FIRST | CONTROL_CLOCK_MODE_3)
+
+/*
+ * The Segment Register uses a 8MB unit to encode the start address
+ * and the end address of the mapping window of a flash SPI slave :
+ *
+ * | byte 1 | byte 2 | byte 3 | byte 4 |
+ * +--------+--------+--------+--------+
+ * | end | start | 0 | 0 |
+ */
+#define SEGMENT_ADDR_REG0 0x30
+#define SEGMENT_ADDR_START(_r) ((((_r) >> 16) & 0xFF) << 23)
+#define SEGMENT_ADDR_END(_r) ((((_r) >> 24) & 0xFF) << 23)
+
+/*
+ * In user mode all data bytes read or written to the chip decode address
+ * range are transferred to or from the SPI bus. The range is treated as a
+ * fifo of arbitratry 1, 2, or 4 byte width but each write has to be aligned
+ * to its size. The address within the multiple 8kB range is ignored when
+ * sending bytes to the SPI bus.
+ *
+ * On the arm architecture, as of Linux version 4.3, memcpy_fromio and
+ * memcpy_toio on little endian targets use the optimized memcpy routines
+ * that were designed for well behavied memory storage. These routines
+ * have a stutter if the source and destination are not both word aligned,
+ * once with a duplicate access to the source after aligning to the
+ * destination to a word boundary, and again with a duplicate access to
+ * the source when the final byte count is not word aligned.
+ *
+ * When writing or reading the fifo this stutter discards data or sends
+ * too much data to the fifo and can not be used by this driver.
+ *
+ * While the low level io string routines that implement the insl family do
+ * the desired accesses and memory increments, the cross architecture io
+ * macros make them essentially impossible to use on a memory mapped address
+ * instead of a a token from the call to iomap of an io port.
+ *
+ * These fifo routines use readl and friends to a constant io port and update
+ * the memory buffer pointer and count via explicit code. The final updates
+ * to len are optimistically suppressed.
+ */
+static int aspeed_smc_read_from_ahb(void *buf, void __iomem *src, size_t len)
+{
+ size_t offset = 0;
+
+ if (IS_ALIGNED((uintptr_t)src, sizeof(uintptr_t)) &&
+ IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) {
+ ioread32_rep(src, buf, len >> 2);
+ offset = len & ~0x3;
+ len -= offset;
+ }
+ ioread8_rep(src, (u8 *)buf + offset, len);
+ return 0;
+}
+
+static int aspeed_smc_write_to_ahb(void __iomem *dst, const void *buf,
+ size_t len)
+{
+ size_t offset = 0;
+
+ if (IS_ALIGNED((uintptr_t)dst, sizeof(uintptr_t)) &&
+ IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) {
+ iowrite32_rep(dst, buf, len >> 2);
+ offset = len & ~0x3;
+ len -= offset;
+ }
+ iowrite8_rep(dst, (const u8 *)buf + offset, len);
+ return 0;
+}
+
+static inline u32 aspeed_smc_chip_write_bit(struct aspeed_smc_chip *chip)
+{
+ return BIT(chip->controller->info->we0 + chip->cs);
+}
+
+static void aspeed_smc_chip_check_config(struct aspeed_smc_chip *chip)
+{
+ struct aspeed_smc_controller *controller = chip->controller;
+ u32 reg;
+
+ reg = readl(controller->regs + CONFIG_REG);
+
+ if (reg & aspeed_smc_chip_write_bit(chip))
+ return;
+
+ dev_dbg(controller->dev, "config write is not set ! @%p: 0x%08x\n",
+ controller->regs + CONFIG_REG, reg);
+ reg |= aspeed_smc_chip_write_bit(chip);
+ writel(reg, controller->regs + CONFIG_REG);
+}
+
+static void aspeed_smc_start_user(struct spi_nor *nor)
+{
+ struct aspeed_smc_chip *chip = nor->priv;
+ u32 ctl = chip->ctl_val[smc_base];
+
+ /*
+ * When the chip is controlled in user mode, we need write
+ * access to send the opcodes to it. So check the config.
+ */
+ aspeed_smc_chip_check_config(chip);
+
+ ctl |= CONTROL_COMMAND_MODE_USER |
+ CONTROL_CE_STOP_ACTIVE_CONTROL;
+ writel(ctl, chip->ctl);
+
+ ctl &= ~CONTROL_CE_STOP_ACTIVE_CONTROL;
+ writel(ctl, chip->ctl);
+}
+
+static void aspeed_smc_stop_user(struct spi_nor *nor)
+{
+ struct aspeed_smc_chip *chip = nor->priv;
+
+ u32 ctl = chip->ctl_val[smc_read];
+ u32 ctl2 = ctl | CONTROL_COMMAND_MODE_USER |
+ CONTROL_CE_STOP_ACTIVE_CONTROL;
+
+ writel(ctl2, chip->ctl); /* stop user CE control */
+ writel(ctl, chip->ctl); /* default to fread or read mode */
+}
+
+static int aspeed_smc_prep(struct spi_nor *nor, enum spi_nor_ops ops)
+{
+ struct aspeed_smc_chip *chip = nor->priv;
+
+ mutex_lock(&chip->controller->mutex);
+ return 0;
+}
+
+static void aspeed_smc_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
+{
+ struct aspeed_smc_chip *chip = nor->priv;
+
+ mutex_unlock(&chip->controller->mutex);
+}
+
+static int aspeed_smc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
+{
+ struct aspeed_smc_chip *chip = nor->priv;
+
+ aspeed_smc_start_user(nor);
+ aspeed_smc_write_to_ahb(chip->ahb_base, &opcode, 1);
+ aspeed_smc_read_from_ahb(buf, chip->ahb_base, len);
+ aspeed_smc_stop_user(nor);
+ return 0;
+}
+
+static int aspeed_smc_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
+ int len)
+{
+ struct aspeed_smc_chip *chip = nor->priv;
+
+ aspeed_smc_start_user(nor);
+ aspeed_smc_write_to_ahb(chip->ahb_base, &opcode, 1);
+ aspeed_smc_write_to_ahb(chip->ahb_base, buf, len);
+ aspeed_smc_stop_user(nor);
+ return 0;
+}
+
+static void aspeed_smc_send_cmd_addr(struct spi_nor *nor, u8 cmd, u32 addr)
+{
+ struct aspeed_smc_chip *chip = nor->priv;
+ __be32 temp;
+ u32 cmdaddr;
+
+ switch (nor->addr_width) {
+ default:
+ WARN_ONCE(1, "Unexpected address width %u, defaulting to 3\n",
+ nor->addr_width);
+ /* FALLTHROUGH */
+ case 3:
+ cmdaddr = addr & 0xFFFFFF;
+ cmdaddr |= cmd << 24;
+
+ temp = cpu_to_be32(cmdaddr);
+ aspeed_smc_write_to_ahb(chip->ahb_base, &temp, 4);
+ break;
+ case 4:
+ temp = cpu_to_be32(addr);
+ aspeed_smc_write_to_ahb(chip->ahb_base, &cmd, 1);
+ aspeed_smc_write_to_ahb(chip->ahb_base, &temp, 4);
+ break;
+ }
+}
+
+static ssize_t aspeed_smc_read_user(struct spi_nor *nor, loff_t from,
+ size_t len, u_char *read_buf)
+{
+ struct aspeed_smc_chip *chip = nor->priv;
+ int i;
+ u8 dummy = 0xFF;
+
+ aspeed_smc_start_user(nor);
+ aspeed_smc_send_cmd_addr(nor, nor->read_opcode, from);
+ for (i = 0; i < chip->nor.read_dummy / 8; i++)
+ aspeed_smc_write_to_ahb(chip->ahb_base, &dummy, sizeof(dummy));
+
+ aspeed_smc_read_from_ahb(read_buf, chip->ahb_base, len);
+ aspeed_smc_stop_user(nor);
+ return len;
+}
+
+static ssize_t aspeed_smc_write_user(struct spi_nor *nor, loff_t to,
+ size_t len, const u_char *write_buf)
+{
+ struct aspeed_smc_chip *chip = nor->priv;
+
+ aspeed_smc_start_user(nor);
+ aspeed_smc_send_cmd_addr(nor, nor->program_opcode, to);
+ aspeed_smc_write_to_ahb(chip->ahb_base, write_buf, len);
+ aspeed_smc_stop_user(nor);
+ return len;
+}
+
+static int aspeed_smc_unregister(struct aspeed_smc_controller *controller)
+{
+ struct aspeed_smc_chip *chip;
+ int n;
+
+ for (n = 0; n < controller->info->nce; n++) {
+ chip = controller->chips[n];
+ if (chip)
+ mtd_device_unregister(&chip->nor.mtd);
+ }
+
+ return 0;
+}
+
+static int aspeed_smc_remove(struct platform_device *dev)
+{
+ return aspeed_smc_unregister(platform_get_drvdata(dev));
+}
+
+static const struct of_device_id aspeed_smc_matches[] = {
+ { .compatible = "aspeed,ast2400-fmc", .data = &fmc_2400_info },
+ { .compatible = "aspeed,ast2400-spi", .data = &spi_2400_info },
+ { .compatible = "aspeed,ast2500-fmc", .data = &fmc_2500_info },
+ { .compatible = "aspeed,ast2500-spi", .data = &spi_2500_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, aspeed_smc_matches);
+
+/*
+ * Each chip has a mapping window defined by a segment address
+ * register defining a start and an end address on the AHB bus. These
+ * addresses can be configured to fit the chip size and offer a
+ * contiguous memory region across chips. For the moment, we only
+ * check that each chip segment is valid.
+ */
+static void __iomem *aspeed_smc_chip_base(struct aspeed_smc_chip *chip,
+ struct resource *res)
+{
+ struct aspeed_smc_controller *controller = chip->controller;
+ u32 offset = 0;
+ u32 reg;
+
+ if (controller->info->nce > 1) {
+ reg = readl(controller->regs + SEGMENT_ADDR_REG0 +
+ chip->cs * 4);
+
+ if (SEGMENT_ADDR_START(reg) >= SEGMENT_ADDR_END(reg))
+ return NULL;
+
+ offset = SEGMENT_ADDR_START(reg) - res->start;
+ }
+
+ return controller->ahb_base + offset;
+}
+
+static void aspeed_smc_chip_enable_write(struct aspeed_smc_chip *chip)
+{
+ struct aspeed_smc_controller *controller = chip->controller;
+ u32 reg;
+
+ reg = readl(controller->regs + CONFIG_REG);
+
+ reg |= aspeed_smc_chip_write_bit(chip);
+ writel(reg, controller->regs + CONFIG_REG);
+}
+
+static void aspeed_smc_chip_set_type(struct aspeed_smc_chip *chip, int type)
+{
+ struct aspeed_smc_controller *controller = chip->controller;
+ u32 reg;
+
+ chip->type = type;
+
+ reg = readl(controller->regs + CONFIG_REG);
+ reg &= ~(3 << (chip->cs * 2));
+ reg |= chip->type << (chip->cs * 2);
+ writel(reg, controller->regs + CONFIG_REG);
+}
+
+/*
+ * The AST2500 FMC flash controller should be strapped by hardware, or
+ * autodetected, but the AST2500 SPI flash needs to be set.
+ */
+static void aspeed_smc_chip_set_4b(struct aspeed_smc_chip *chip)
+{
+ struct aspeed_smc_controller *controller = chip->controller;
+ u32 reg;
+
+ if (chip->controller->info == &spi_2500_info) {
+ reg = readl(controller->regs + CE_CONTROL_REG);
+ reg |= 1 << chip->cs;
+ writel(reg, controller->regs + CE_CONTROL_REG);
+ }
+}
+
+/*
+ * The AST2400 SPI flash controller does not have a CE Control
+ * register. It uses the CE0 control register to set 4Byte mode at the
+ * controller level.
+ */
+static void aspeed_smc_chip_set_4b_spi_2400(struct aspeed_smc_chip *chip)
+{
+ chip->ctl_val[smc_base] |= CONTROL_IO_ADDRESS_4B;
+ chip->ctl_val[smc_read] |= CONTROL_IO_ADDRESS_4B;
+}
+
+static int aspeed_smc_chip_setup_init(struct aspeed_smc_chip *chip,
+ struct resource *res)
+{
+ struct aspeed_smc_controller *controller = chip->controller;
+ const struct aspeed_smc_info *info = controller->info;
+ u32 reg, base_reg;
+
+ /*
+ * Always turn on the write enable bit to allow opcodes to be
+ * sent in user mode.
+ */
+ aspeed_smc_chip_enable_write(chip);
+
+ /* The driver only supports SPI type flash */
+ if (info->hastype)
+ aspeed_smc_chip_set_type(chip, smc_type_spi);
+
+ /*
+ * Configure chip base address in memory
+ */
+ chip->ahb_base = aspeed_smc_chip_base(chip, res);
+ if (!chip->ahb_base) {
+ dev_warn(chip->nor.dev, "CE segment window closed.\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Get value of the inherited control register. U-Boot usually
+ * does some timing calibration on the FMC chip, so it's good
+ * to keep them. In the future, we should handle calibration
+ * from Linux.
+ */
+ reg = readl(chip->ctl);
+ dev_dbg(controller->dev, "control register: %08x\n", reg);
+
+ base_reg = reg & CONTROL_KEEP_MASK;
+ if (base_reg != reg) {
+ dev_dbg(controller->dev,
+ "control register changed to: %08x\n",
+ base_reg);
+ }
+ chip->ctl_val[smc_base] = base_reg;
+
+ /*
+ * Retain the prior value of the control register as the
+ * default if it was normal access mode. Otherwise start with
+ * the sanitized base value set to read mode.
+ */
+ if ((reg & CONTROL_COMMAND_MODE_MASK) ==
+ CONTROL_COMMAND_MODE_NORMAL)
+ chip->ctl_val[smc_read] = reg;
+ else
+ chip->ctl_val[smc_read] = chip->ctl_val[smc_base] |
+ CONTROL_COMMAND_MODE_NORMAL;
+
+ dev_dbg(controller->dev, "default control register: %08x\n",
+ chip->ctl_val[smc_read]);
+ return 0;
+}
+
+static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
+{
+ struct aspeed_smc_controller *controller = chip->controller;
+ const struct aspeed_smc_info *info = controller->info;
+ u32 cmd;
+
+ if (chip->nor.addr_width == 4 && info->set_4b)
+ info->set_4b(chip);
+
+ /*
+ * base mode has not been optimized yet. use it for writes.
+ */
+ chip->ctl_val[smc_write] = chip->ctl_val[smc_base] |
+ chip->nor.program_opcode << CONTROL_COMMAND_SHIFT |
+ CONTROL_COMMAND_MODE_WRITE;
+
+ dev_dbg(controller->dev, "write control register: %08x\n",
+ chip->ctl_val[smc_write]);
+
+ /*
+ * TODO: Adjust clocks if fast read is supported and interpret
+ * SPI-NOR flags to adjust controller settings.
+ */
+ switch (chip->nor.flash_read) {
+ case SPI_NOR_NORMAL:
+ cmd = CONTROL_COMMAND_MODE_NORMAL;
+ break;
+ case SPI_NOR_FAST:
+ cmd = CONTROL_COMMAND_MODE_FREAD;
+ break;
+ default:
+ dev_err(chip->nor.dev, "unsupported SPI read mode\n");
+ return -EINVAL;
+ }
+
+ chip->ctl_val[smc_read] |= cmd |
+ CONTROL_IO_DUMMY_SET(chip->nor.read_dummy / 8);
+
+ dev_dbg(controller->dev, "base control register: %08x\n",
+ chip->ctl_val[smc_read]);
+ return 0;
+}
+
+static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
+ struct device_node *np, struct resource *r)
+{
+ const struct aspeed_smc_info *info = controller->info;
+ struct device *dev = controller->dev;
+ struct device_node *child;
+ unsigned int cs;
+ int ret = -ENODEV;
+
+ for_each_available_child_of_node(np, child) {
+ struct aspeed_smc_chip *chip;
+ struct spi_nor *nor;
+ struct mtd_info *mtd;
+
+ /* This driver does not support NAND or NOR flash devices. */
+ if (!of_device_is_compatible(child, "jedec,spi-nor"))
+ continue;
+
+ ret = of_property_read_u32(child, "reg", &cs);
+ if (ret) {
+ dev_err(dev, "Couldn't not read chip select.\n");
+ break;
+ }
+
+ if (cs >= info->nce) {
+ dev_err(dev, "Chip select %d out of range.\n",
+ cs);
+ ret = -ERANGE;
+ break;
+ }
+
+ if (controller->chips[cs]) {
+ dev_err(dev, "Chip select %d already in use by %s\n",
+ cs, dev_name(controller->chips[cs]->nor.dev));
+ ret = -EBUSY;
+ break;
+ }
+
+ chip = devm_kzalloc(controller->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ chip->controller = controller;
+ chip->ctl = controller->regs + info->ctl0 + cs * 4;
+ chip->cs = cs;
+
+ nor = &chip->nor;
+ mtd = &nor->mtd;
+
+ nor->dev = dev;
+ nor->priv = chip;
+ spi_nor_set_flash_node(nor, child);
+ nor->read = aspeed_smc_read_user;
+ nor->write = aspeed_smc_write_user;
+ nor->read_reg = aspeed_smc_read_reg;
+ nor->write_reg = aspeed_smc_write_reg;
+ nor->prepare = aspeed_smc_prep;
+ nor->unprepare = aspeed_smc_unprep;
+
+ ret = aspeed_smc_chip_setup_init(chip, r);
+ if (ret)
+ break;
+
+ /*
+ * TODO: Add support for SPI_NOR_QUAD and SPI_NOR_DUAL
+ * attach when board support is present as determined
+ * by of property.
+ */
+ ret = spi_nor_scan(nor, NULL, SPI_NOR_NORMAL);
+ if (ret)
+ break;
+
+ ret = aspeed_smc_chip_setup_finish(chip);
+ if (ret)
+ break;
+
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret)
+ break;
+
+ controller->chips[cs] = chip;
+ }
+
+ if (ret)
+ aspeed_smc_unregister(controller);
+
+ return ret;
+}
+
+static int aspeed_smc_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct aspeed_smc_controller *controller;
+ const struct of_device_id *match;
+ const struct aspeed_smc_info *info;
+ struct resource *res;
+ int ret;
+
+ match = of_match_device(aspeed_smc_matches, &pdev->dev);
+ if (!match || !match->data)
+ return -ENODEV;
+ info = match->data;
+
+ controller = devm_kzalloc(&pdev->dev, sizeof(*controller) +
+ info->nce * sizeof(controller->chips[0]), GFP_KERNEL);
+ if (!controller)
+ return -ENOMEM;
+ controller->info = info;
+ controller->dev = dev;
+
+ mutex_init(&controller->mutex);
+ platform_set_drvdata(pdev, controller);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ controller->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(controller->regs))
+ return PTR_ERR(controller->regs);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ controller->ahb_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(controller->ahb_base))
+ return PTR_ERR(controller->ahb_base);
+
+ ret = aspeed_smc_setup_flash(controller, np, res);
+ if (ret)
+ dev_err(dev, "Aspeed SMC probe failed %d\n", ret);
+
+ return ret;
+}
+
+static struct platform_driver aspeed_smc_driver = {
+ .probe = aspeed_smc_probe,
+ .remove = aspeed_smc_remove,
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = aspeed_smc_matches,
+ }
+};
+
+module_platform_driver(aspeed_smc_driver);
+
+MODULE_DESCRIPTION("ASPEED Static Memory Controller Driver");
+MODULE_AUTHOR("Cedric Le Goater <clg@kaod.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
index d489fbd..9f8102d 100644
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
@@ -526,7 +526,8 @@
bytes_to_read *= cqspi->fifo_width;
bytes_to_read = bytes_to_read > remaining ?
remaining : bytes_to_read;
- readsl(ahb_base, rxbuf, DIV_ROUND_UP(bytes_to_read, 4));
+ ioread32_rep(ahb_base, rxbuf,
+ DIV_ROUND_UP(bytes_to_read, 4));
rxbuf += bytes_to_read;
remaining -= bytes_to_read;
bytes_to_read = cqspi_get_rd_sram_level(cqspi);
@@ -610,7 +611,8 @@
while (remaining > 0) {
write_bytes = remaining > page_size ? page_size : remaining;
- writesl(cqspi->ahb_base, txbuf, DIV_ROUND_UP(write_bytes, 4));
+ iowrite32_rep(cqspi->ahb_base, txbuf,
+ DIV_ROUND_UP(write_bytes, 4));
ret = wait_for_completion_timeout(&cqspi->transfer_complete,
msecs_to_jiffies
@@ -891,7 +893,7 @@
if (ret)
return ret;
- return (ret < 0) ? ret : len;
+ return len;
}
static ssize_t cqspi_read(struct spi_nor *nor, loff_t from,
@@ -911,7 +913,7 @@
if (ret)
return ret;
- return (ret < 0) ? ret : len;
+ return len;
}
static int cqspi_erase(struct spi_nor *nor, loff_t offs)
diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index b4d8953..1476135 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -193,7 +193,7 @@
#define QUADSPI_LUT_NUM 64
/* SEQID -- we can have 16 seqids at most. */
-#define SEQID_QUAD_READ 0
+#define SEQID_READ 0
#define SEQID_WREN 1
#define SEQID_WRDI 2
#define SEQID_RDSR 3
@@ -373,32 +373,26 @@
void __iomem *base = q->iobase;
int rxfifo = q->devtype_data->rxfifo;
u32 lut_base;
- u8 cmd, addrlen, dummy;
int i;
+ struct spi_nor *nor = &q->nor[0];
+ u8 addrlen = (nor->addr_width == 3) ? ADDR24BIT : ADDR32BIT;
+ u8 read_op = nor->read_opcode;
+ u8 read_dm = nor->read_dummy;
+
fsl_qspi_unlock_lut(q);
/* Clear all the LUT table */
for (i = 0; i < QUADSPI_LUT_NUM; i++)
qspi_writel(q, 0, base + QUADSPI_LUT_BASE + i * 4);
- /* Quad Read */
- lut_base = SEQID_QUAD_READ * 4;
+ /* Read */
+ lut_base = SEQID_READ * 4;
- if (q->nor_size <= SZ_16M) {
- cmd = SPINOR_OP_READ_1_1_4;
- addrlen = ADDR24BIT;
- dummy = 8;
- } else {
- /* use the 4-byte address */
- cmd = SPINOR_OP_READ_1_1_4;
- addrlen = ADDR32BIT;
- dummy = 8;
- }
-
- qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
+ qspi_writel(q, LUT0(CMD, PAD1, read_op) | LUT1(ADDR, PAD1, addrlen),
base + QUADSPI_LUT(lut_base));
- qspi_writel(q, LUT0(DUMMY, PAD1, dummy) | LUT1(FSL_READ, PAD4, rxfifo),
+ qspi_writel(q, LUT0(DUMMY, PAD1, read_dm) |
+ LUT1(FSL_READ, PAD4, rxfifo),
base + QUADSPI_LUT(lut_base + 1));
/* Write enable */
@@ -409,16 +403,8 @@
/* Page Program */
lut_base = SEQID_PP * 4;
- if (q->nor_size <= SZ_16M) {
- cmd = SPINOR_OP_PP;
- addrlen = ADDR24BIT;
- } else {
- /* use the 4-byte address */
- cmd = SPINOR_OP_PP;
- addrlen = ADDR32BIT;
- }
-
- qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
+ qspi_writel(q, LUT0(CMD, PAD1, nor->program_opcode) |
+ LUT1(ADDR, PAD1, addrlen),
base + QUADSPI_LUT(lut_base));
qspi_writel(q, LUT0(FSL_WRITE, PAD1, 0),
base + QUADSPI_LUT(lut_base + 1));
@@ -432,10 +418,8 @@
/* Erase a sector */
lut_base = SEQID_SE * 4;
- cmd = q->nor[0].erase_opcode;
- addrlen = q->nor_size <= SZ_16M ? ADDR24BIT : ADDR32BIT;
-
- qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
+ qspi_writel(q, LUT0(CMD, PAD1, nor->erase_opcode) |
+ LUT1(ADDR, PAD1, addrlen),
base + QUADSPI_LUT(lut_base));
/* Erase the whole chip */
@@ -484,7 +468,7 @@
{
switch (cmd) {
case SPINOR_OP_READ_1_1_4:
- return SEQID_QUAD_READ;
+ return SEQID_READ;
case SPINOR_OP_WREN:
return SEQID_WREN;
case SPINOR_OP_WRDI:
diff --git a/drivers/mtd/spi-nor/intel-spi-platform.c b/drivers/mtd/spi-nor/intel-spi-platform.c
new file mode 100644
index 0000000..5c943df
--- /dev/null
+++ b/drivers/mtd/spi-nor/intel-spi-platform.c
@@ -0,0 +1,57 @@
+/*
+ * Intel PCH/PCU SPI flash platform driver.
+ *
+ * Copyright (C) 2016, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.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.
+ */
+
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "intel-spi.h"
+
+static int intel_spi_platform_probe(struct platform_device *pdev)
+{
+ struct intel_spi_boardinfo *info;
+ struct intel_spi *ispi;
+ struct resource *mem;
+
+ info = dev_get_platdata(&pdev->dev);
+ if (!info)
+ return -EINVAL;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ispi = intel_spi_probe(&pdev->dev, mem, info);
+ if (IS_ERR(ispi))
+ return PTR_ERR(ispi);
+
+ platform_set_drvdata(pdev, ispi);
+ return 0;
+}
+
+static int intel_spi_platform_remove(struct platform_device *pdev)
+{
+ struct intel_spi *ispi = platform_get_drvdata(pdev);
+
+ return intel_spi_remove(ispi);
+}
+
+static struct platform_driver intel_spi_platform_driver = {
+ .probe = intel_spi_platform_probe,
+ .remove = intel_spi_platform_remove,
+ .driver = {
+ .name = "intel-spi",
+ },
+};
+
+module_platform_driver(intel_spi_platform_driver);
+
+MODULE_DESCRIPTION("Intel PCH/PCU SPI flash platform driver");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:intel-spi");
diff --git a/drivers/mtd/spi-nor/intel-spi.c b/drivers/mtd/spi-nor/intel-spi.c
new file mode 100644
index 0000000..a10f602
--- /dev/null
+++ b/drivers/mtd/spi-nor/intel-spi.c
@@ -0,0 +1,777 @@
+/*
+ * Intel PCH/PCU SPI flash driver.
+ *
+ * Copyright (C) 2016, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.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.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/spi-nor.h>
+#include <linux/platform_data/intel-spi.h>
+
+#include "intel-spi.h"
+
+/* Offsets are from @ispi->base */
+#define BFPREG 0x00
+
+#define HSFSTS_CTL 0x04
+#define HSFSTS_CTL_FSMIE BIT(31)
+#define HSFSTS_CTL_FDBC_SHIFT 24
+#define HSFSTS_CTL_FDBC_MASK (0x3f << HSFSTS_CTL_FDBC_SHIFT)
+
+#define HSFSTS_CTL_FCYCLE_SHIFT 17
+#define HSFSTS_CTL_FCYCLE_MASK (0x0f << HSFSTS_CTL_FCYCLE_SHIFT)
+/* HW sequencer opcodes */
+#define HSFSTS_CTL_FCYCLE_READ (0x00 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_WRITE (0x02 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_ERASE (0x03 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_ERASE_64K (0x04 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_RDID (0x06 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_WRSR (0x07 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_RDSR (0x08 << HSFSTS_CTL_FCYCLE_SHIFT)
+
+#define HSFSTS_CTL_FGO BIT(16)
+#define HSFSTS_CTL_FLOCKDN BIT(15)
+#define HSFSTS_CTL_FDV BIT(14)
+#define HSFSTS_CTL_SCIP BIT(5)
+#define HSFSTS_CTL_AEL BIT(2)
+#define HSFSTS_CTL_FCERR BIT(1)
+#define HSFSTS_CTL_FDONE BIT(0)
+
+#define FADDR 0x08
+#define DLOCK 0x0c
+#define FDATA(n) (0x10 + ((n) * 4))
+
+#define FRACC 0x50
+
+#define FREG(n) (0x54 + ((n) * 4))
+#define FREG_BASE_MASK 0x3fff
+#define FREG_LIMIT_SHIFT 16
+#define FREG_LIMIT_MASK (0x03fff << FREG_LIMIT_SHIFT)
+
+/* Offset is from @ispi->pregs */
+#define PR(n) ((n) * 4)
+#define PR_WPE BIT(31)
+#define PR_LIMIT_SHIFT 16
+#define PR_LIMIT_MASK (0x3fff << PR_LIMIT_SHIFT)
+#define PR_RPE BIT(15)
+#define PR_BASE_MASK 0x3fff
+/* Last PR is GPR0 */
+#define PR_NUM (5 + 1)
+
+/* Offsets are from @ispi->sregs */
+#define SSFSTS_CTL 0x00
+#define SSFSTS_CTL_FSMIE BIT(23)
+#define SSFSTS_CTL_DS BIT(22)
+#define SSFSTS_CTL_DBC_SHIFT 16
+#define SSFSTS_CTL_SPOP BIT(11)
+#define SSFSTS_CTL_ACS BIT(10)
+#define SSFSTS_CTL_SCGO BIT(9)
+#define SSFSTS_CTL_COP_SHIFT 12
+#define SSFSTS_CTL_FRS BIT(7)
+#define SSFSTS_CTL_DOFRS BIT(6)
+#define SSFSTS_CTL_AEL BIT(4)
+#define SSFSTS_CTL_FCERR BIT(3)
+#define SSFSTS_CTL_FDONE BIT(2)
+#define SSFSTS_CTL_SCIP BIT(0)
+
+#define PREOP_OPTYPE 0x04
+#define OPMENU0 0x08
+#define OPMENU1 0x0c
+
+/* CPU specifics */
+#define BYT_PR 0x74
+#define BYT_SSFSTS_CTL 0x90
+#define BYT_BCR 0xfc
+#define BYT_BCR_WPD BIT(0)
+#define BYT_FREG_NUM 5
+
+#define LPT_PR 0x74
+#define LPT_SSFSTS_CTL 0x90
+#define LPT_FREG_NUM 5
+
+#define BXT_PR 0x84
+#define BXT_SSFSTS_CTL 0xa0
+#define BXT_FREG_NUM 12
+
+#define INTEL_SPI_TIMEOUT 5000 /* ms */
+#define INTEL_SPI_FIFO_SZ 64
+
+/**
+ * struct intel_spi - Driver private data
+ * @dev: Device pointer
+ * @info: Pointer to board specific info
+ * @nor: SPI NOR layer structure
+ * @base: Beginning of MMIO space
+ * @pregs: Start of protection registers
+ * @sregs: Start of software sequencer registers
+ * @nregions: Maximum number of regions
+ * @writeable: Is the chip writeable
+ * @swseq: Use SW sequencer in register reads/writes
+ * @erase_64k: 64k erase supported
+ * @opcodes: Opcodes which are supported. This are programmed by BIOS
+ * before it locks down the controller.
+ * @preopcodes: Preopcodes which are supported.
+ */
+struct intel_spi {
+ struct device *dev;
+ const struct intel_spi_boardinfo *info;
+ struct spi_nor nor;
+ void __iomem *base;
+ void __iomem *pregs;
+ void __iomem *sregs;
+ size_t nregions;
+ bool writeable;
+ bool swseq;
+ bool erase_64k;
+ u8 opcodes[8];
+ u8 preopcodes[2];
+};
+
+static bool writeable;
+module_param(writeable, bool, 0);
+MODULE_PARM_DESC(writeable, "Enable write access to SPI flash chip (default=0)");
+
+static void intel_spi_dump_regs(struct intel_spi *ispi)
+{
+ u32 value;
+ int i;
+
+ dev_dbg(ispi->dev, "BFPREG=0x%08x\n", readl(ispi->base + BFPREG));
+
+ value = readl(ispi->base + HSFSTS_CTL);
+ dev_dbg(ispi->dev, "HSFSTS_CTL=0x%08x\n", value);
+ if (value & HSFSTS_CTL_FLOCKDN)
+ dev_dbg(ispi->dev, "-> Locked\n");
+
+ dev_dbg(ispi->dev, "FADDR=0x%08x\n", readl(ispi->base + FADDR));
+ dev_dbg(ispi->dev, "DLOCK=0x%08x\n", readl(ispi->base + DLOCK));
+
+ for (i = 0; i < 16; i++)
+ dev_dbg(ispi->dev, "FDATA(%d)=0x%08x\n",
+ i, readl(ispi->base + FDATA(i)));
+
+ dev_dbg(ispi->dev, "FRACC=0x%08x\n", readl(ispi->base + FRACC));
+
+ for (i = 0; i < ispi->nregions; i++)
+ dev_dbg(ispi->dev, "FREG(%d)=0x%08x\n", i,
+ readl(ispi->base + FREG(i)));
+ for (i = 0; i < PR_NUM; i++)
+ dev_dbg(ispi->dev, "PR(%d)=0x%08x\n", i,
+ readl(ispi->pregs + PR(i)));
+
+ value = readl(ispi->sregs + SSFSTS_CTL);
+ dev_dbg(ispi->dev, "SSFSTS_CTL=0x%08x\n", value);
+ dev_dbg(ispi->dev, "PREOP_OPTYPE=0x%08x\n",
+ readl(ispi->sregs + PREOP_OPTYPE));
+ dev_dbg(ispi->dev, "OPMENU0=0x%08x\n", readl(ispi->sregs + OPMENU0));
+ dev_dbg(ispi->dev, "OPMENU1=0x%08x\n", readl(ispi->sregs + OPMENU1));
+
+ if (ispi->info->type == INTEL_SPI_BYT)
+ dev_dbg(ispi->dev, "BCR=0x%08x\n", readl(ispi->base + BYT_BCR));
+
+ dev_dbg(ispi->dev, "Protected regions:\n");
+ for (i = 0; i < PR_NUM; i++) {
+ u32 base, limit;
+
+ value = readl(ispi->pregs + PR(i));
+ if (!(value & (PR_WPE | PR_RPE)))
+ continue;
+
+ limit = (value & PR_LIMIT_MASK) >> PR_LIMIT_SHIFT;
+ base = value & PR_BASE_MASK;
+
+ dev_dbg(ispi->dev, " %02d base: 0x%08x limit: 0x%08x [%c%c]\n",
+ i, base << 12, (limit << 12) | 0xfff,
+ value & PR_WPE ? 'W' : '.',
+ value & PR_RPE ? 'R' : '.');
+ }
+
+ dev_dbg(ispi->dev, "Flash regions:\n");
+ for (i = 0; i < ispi->nregions; i++) {
+ u32 region, base, limit;
+
+ region = readl(ispi->base + FREG(i));
+ base = region & FREG_BASE_MASK;
+ limit = (region & FREG_LIMIT_MASK) >> FREG_LIMIT_SHIFT;
+
+ if (base >= limit || (i > 0 && limit == 0))
+ dev_dbg(ispi->dev, " %02d disabled\n", i);
+ else
+ dev_dbg(ispi->dev, " %02d base: 0x%08x limit: 0x%08x\n",
+ i, base << 12, (limit << 12) | 0xfff);
+ }
+
+ dev_dbg(ispi->dev, "Using %cW sequencer for register access\n",
+ ispi->swseq ? 'S' : 'H');
+}
+
+/* Reads max INTEL_SPI_FIFO_SZ bytes from the device fifo */
+static int intel_spi_read_block(struct intel_spi *ispi, void *buf, size_t size)
+{
+ size_t bytes;
+ int i = 0;
+
+ if (size > INTEL_SPI_FIFO_SZ)
+ return -EINVAL;
+
+ while (size > 0) {
+ bytes = min_t(size_t, size, 4);
+ memcpy_fromio(buf, ispi->base + FDATA(i), bytes);
+ size -= bytes;
+ buf += bytes;
+ i++;
+ }
+
+ return 0;
+}
+
+/* Writes max INTEL_SPI_FIFO_SZ bytes to the device fifo */
+static int intel_spi_write_block(struct intel_spi *ispi, const void *buf,
+ size_t size)
+{
+ size_t bytes;
+ int i = 0;
+
+ if (size > INTEL_SPI_FIFO_SZ)
+ return -EINVAL;
+
+ while (size > 0) {
+ bytes = min_t(size_t, size, 4);
+ memcpy_toio(ispi->base + FDATA(i), buf, bytes);
+ size -= bytes;
+ buf += bytes;
+ i++;
+ }
+
+ return 0;
+}
+
+static int intel_spi_wait_hw_busy(struct intel_spi *ispi)
+{
+ u32 val;
+
+ return readl_poll_timeout(ispi->base + HSFSTS_CTL, val,
+ !(val & HSFSTS_CTL_SCIP), 0,
+ INTEL_SPI_TIMEOUT * 1000);
+}
+
+static int intel_spi_wait_sw_busy(struct intel_spi *ispi)
+{
+ u32 val;
+
+ return readl_poll_timeout(ispi->sregs + SSFSTS_CTL, val,
+ !(val & SSFSTS_CTL_SCIP), 0,
+ INTEL_SPI_TIMEOUT * 1000);
+}
+
+static int intel_spi_init(struct intel_spi *ispi)
+{
+ u32 opmenu0, opmenu1, val;
+ int i;
+
+ switch (ispi->info->type) {
+ case INTEL_SPI_BYT:
+ ispi->sregs = ispi->base + BYT_SSFSTS_CTL;
+ ispi->pregs = ispi->base + BYT_PR;
+ ispi->nregions = BYT_FREG_NUM;
+
+ if (writeable) {
+ /* Disable write protection */
+ val = readl(ispi->base + BYT_BCR);
+ if (!(val & BYT_BCR_WPD)) {
+ val |= BYT_BCR_WPD;
+ writel(val, ispi->base + BYT_BCR);
+ val = readl(ispi->base + BYT_BCR);
+ }
+
+ ispi->writeable = !!(val & BYT_BCR_WPD);
+ }
+
+ break;
+
+ case INTEL_SPI_LPT:
+ ispi->sregs = ispi->base + LPT_SSFSTS_CTL;
+ ispi->pregs = ispi->base + LPT_PR;
+ ispi->nregions = LPT_FREG_NUM;
+ break;
+
+ case INTEL_SPI_BXT:
+ ispi->sregs = ispi->base + BXT_SSFSTS_CTL;
+ ispi->pregs = ispi->base + BXT_PR;
+ ispi->nregions = BXT_FREG_NUM;
+ ispi->erase_64k = true;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* Disable #SMI generation */
+ val = readl(ispi->base + HSFSTS_CTL);
+ val &= ~HSFSTS_CTL_FSMIE;
+ writel(val, ispi->base + HSFSTS_CTL);
+
+ /*
+ * BIOS programs allowed opcodes and then locks down the register.
+ * So read back what opcodes it decided to support. That's the set
+ * we are going to support as well.
+ */
+ opmenu0 = readl(ispi->sregs + OPMENU0);
+ opmenu1 = readl(ispi->sregs + OPMENU1);
+
+ /*
+ * Some controllers can only do basic operations using hardware
+ * sequencer. All other operations are supposed to be carried out
+ * using software sequencer. If we find that BIOS has programmed
+ * opcodes for the software sequencer we use that over the hardware
+ * sequencer.
+ */
+ if (opmenu0 && opmenu1) {
+ for (i = 0; i < ARRAY_SIZE(ispi->opcodes) / 2; i++) {
+ ispi->opcodes[i] = opmenu0 >> i * 8;
+ ispi->opcodes[i + 4] = opmenu1 >> i * 8;
+ }
+
+ val = readl(ispi->sregs + PREOP_OPTYPE);
+ ispi->preopcodes[0] = val;
+ ispi->preopcodes[1] = val >> 8;
+
+ /* Disable #SMI generation from SW sequencer */
+ val = readl(ispi->sregs + SSFSTS_CTL);
+ val &= ~SSFSTS_CTL_FSMIE;
+ writel(val, ispi->sregs + SSFSTS_CTL);
+
+ ispi->swseq = true;
+ }
+
+ intel_spi_dump_regs(ispi);
+
+ return 0;
+}
+
+static int intel_spi_opcode_index(struct intel_spi *ispi, u8 opcode)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++)
+ if (ispi->opcodes[i] == opcode)
+ return i;
+ return -EINVAL;
+}
+
+static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf,
+ int len)
+{
+ u32 val, status;
+ int ret;
+
+ val = readl(ispi->base + HSFSTS_CTL);
+ val &= ~(HSFSTS_CTL_FCYCLE_MASK | HSFSTS_CTL_FDBC_MASK);
+
+ switch (opcode) {
+ case SPINOR_OP_RDID:
+ val |= HSFSTS_CTL_FCYCLE_RDID;
+ break;
+ case SPINOR_OP_WRSR:
+ val |= HSFSTS_CTL_FCYCLE_WRSR;
+ break;
+ case SPINOR_OP_RDSR:
+ val |= HSFSTS_CTL_FCYCLE_RDSR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val |= (len - 1) << HSFSTS_CTL_FDBC_SHIFT;
+ val |= HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;
+ val |= HSFSTS_CTL_FGO;
+ writel(val, ispi->base + HSFSTS_CTL);
+
+ ret = intel_spi_wait_hw_busy(ispi);
+ if (ret)
+ return ret;
+
+ status = readl(ispi->base + HSFSTS_CTL);
+ if (status & HSFSTS_CTL_FCERR)
+ return -EIO;
+ else if (status & HSFSTS_CTL_AEL)
+ return -EACCES;
+
+ return 0;
+}
+
+static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf,
+ int len)
+{
+ u32 val, status;
+ int ret;
+
+ ret = intel_spi_opcode_index(ispi, opcode);
+ if (ret < 0)
+ return ret;
+
+ val = (len << SSFSTS_CTL_DBC_SHIFT) | SSFSTS_CTL_DS;
+ val |= ret << SSFSTS_CTL_COP_SHIFT;
+ val |= SSFSTS_CTL_FCERR | SSFSTS_CTL_FDONE;
+ val |= SSFSTS_CTL_SCGO;
+ writel(val, ispi->sregs + SSFSTS_CTL);
+
+ ret = intel_spi_wait_sw_busy(ispi);
+ if (ret)
+ return ret;
+
+ status = readl(ispi->base + SSFSTS_CTL);
+ if (status & SSFSTS_CTL_FCERR)
+ return -EIO;
+ else if (status & SSFSTS_CTL_AEL)
+ return -EACCES;
+
+ return 0;
+}
+
+static int intel_spi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
+{
+ struct intel_spi *ispi = nor->priv;
+ int ret;
+
+ /* Address of the first chip */
+ writel(0, ispi->base + FADDR);
+
+ if (ispi->swseq)
+ ret = intel_spi_sw_cycle(ispi, opcode, buf, len);
+ else
+ ret = intel_spi_hw_cycle(ispi, opcode, buf, len);
+
+ if (ret)
+ return ret;
+
+ return intel_spi_read_block(ispi, buf, len);
+}
+
+static int intel_spi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
+{
+ struct intel_spi *ispi = nor->priv;
+ int ret;
+
+ /*
+ * This is handled with atomic operation and preop code in Intel
+ * controller so skip it here now.
+ */
+ if (opcode == SPINOR_OP_WREN)
+ return 0;
+
+ writel(0, ispi->base + FADDR);
+
+ /* Write the value beforehand */
+ ret = intel_spi_write_block(ispi, buf, len);
+ if (ret)
+ return ret;
+
+ if (ispi->swseq)
+ return intel_spi_sw_cycle(ispi, opcode, buf, len);
+ return intel_spi_hw_cycle(ispi, opcode, buf, len);
+}
+
+static ssize_t intel_spi_read(struct spi_nor *nor, loff_t from, size_t len,
+ u_char *read_buf)
+{
+ struct intel_spi *ispi = nor->priv;
+ size_t block_size, retlen = 0;
+ u32 val, status;
+ ssize_t ret;
+
+ switch (nor->read_opcode) {
+ case SPINOR_OP_READ:
+ case SPINOR_OP_READ_FAST:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ while (len > 0) {
+ block_size = min_t(size_t, len, INTEL_SPI_FIFO_SZ);
+
+ writel(from, ispi->base + FADDR);
+
+ val = readl(ispi->base + HSFSTS_CTL);
+ val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK);
+ val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;
+ val |= (block_size - 1) << HSFSTS_CTL_FDBC_SHIFT;
+ val |= HSFSTS_CTL_FCYCLE_READ;
+ val |= HSFSTS_CTL_FGO;
+ writel(val, ispi->base + HSFSTS_CTL);
+
+ ret = intel_spi_wait_hw_busy(ispi);
+ if (ret)
+ return ret;
+
+ status = readl(ispi->base + HSFSTS_CTL);
+ if (status & HSFSTS_CTL_FCERR)
+ ret = -EIO;
+ else if (status & HSFSTS_CTL_AEL)
+ ret = -EACCES;
+
+ if (ret < 0) {
+ dev_err(ispi->dev, "read error: %llx: %#x\n", from,
+ status);
+ return ret;
+ }
+
+ ret = intel_spi_read_block(ispi, read_buf, block_size);
+ if (ret)
+ return ret;
+
+ len -= block_size;
+ from += block_size;
+ retlen += block_size;
+ read_buf += block_size;
+ }
+
+ return retlen;
+}
+
+static ssize_t intel_spi_write(struct spi_nor *nor, loff_t to, size_t len,
+ const u_char *write_buf)
+{
+ struct intel_spi *ispi = nor->priv;
+ size_t block_size, retlen = 0;
+ u32 val, status;
+ ssize_t ret;
+
+ while (len > 0) {
+ block_size = min_t(size_t, len, INTEL_SPI_FIFO_SZ);
+
+ writel(to, ispi->base + FADDR);
+
+ val = readl(ispi->base + HSFSTS_CTL);
+ val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK);
+ val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;
+ val |= (block_size - 1) << HSFSTS_CTL_FDBC_SHIFT;
+ val |= HSFSTS_CTL_FCYCLE_WRITE;
+
+ /* Write enable */
+ if (ispi->preopcodes[1] == SPINOR_OP_WREN)
+ val |= SSFSTS_CTL_SPOP;
+ val |= SSFSTS_CTL_ACS;
+ writel(val, ispi->base + HSFSTS_CTL);
+
+ ret = intel_spi_write_block(ispi, write_buf, block_size);
+ if (ret) {
+ dev_err(ispi->dev, "failed to write block\n");
+ return ret;
+ }
+
+ /* Start the write now */
+ val = readl(ispi->base + HSFSTS_CTL);
+ writel(val | HSFSTS_CTL_FGO, ispi->base + HSFSTS_CTL);
+
+ ret = intel_spi_wait_hw_busy(ispi);
+ if (ret) {
+ dev_err(ispi->dev, "timeout\n");
+ return ret;
+ }
+
+ status = readl(ispi->base + HSFSTS_CTL);
+ if (status & HSFSTS_CTL_FCERR)
+ ret = -EIO;
+ else if (status & HSFSTS_CTL_AEL)
+ ret = -EACCES;
+
+ if (ret < 0) {
+ dev_err(ispi->dev, "write error: %llx: %#x\n", to,
+ status);
+ return ret;
+ }
+
+ len -= block_size;
+ to += block_size;
+ retlen += block_size;
+ write_buf += block_size;
+ }
+
+ return retlen;
+}
+
+static int intel_spi_erase(struct spi_nor *nor, loff_t offs)
+{
+ size_t erase_size, len = nor->mtd.erasesize;
+ struct intel_spi *ispi = nor->priv;
+ u32 val, status, cmd;
+ int ret;
+
+ /* If the hardware can do 64k erase use that when possible */
+ if (len >= SZ_64K && ispi->erase_64k) {
+ cmd = HSFSTS_CTL_FCYCLE_ERASE_64K;
+ erase_size = SZ_64K;
+ } else {
+ cmd = HSFSTS_CTL_FCYCLE_ERASE;
+ erase_size = SZ_4K;
+ }
+
+ while (len > 0) {
+ writel(offs, ispi->base + FADDR);
+
+ val = readl(ispi->base + HSFSTS_CTL);
+ val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK);
+ val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;
+ val |= cmd;
+ val |= HSFSTS_CTL_FGO;
+ writel(val, ispi->base + HSFSTS_CTL);
+
+ ret = intel_spi_wait_hw_busy(ispi);
+ if (ret)
+ return ret;
+
+ status = readl(ispi->base + HSFSTS_CTL);
+ if (status & HSFSTS_CTL_FCERR)
+ return -EIO;
+ else if (status & HSFSTS_CTL_AEL)
+ return -EACCES;
+
+ offs += erase_size;
+ len -= erase_size;
+ }
+
+ return 0;
+}
+
+static bool intel_spi_is_protected(const struct intel_spi *ispi,
+ unsigned int base, unsigned int limit)
+{
+ int i;
+
+ for (i = 0; i < PR_NUM; i++) {
+ u32 pr_base, pr_limit, pr_value;
+
+ pr_value = readl(ispi->pregs + PR(i));
+ if (!(pr_value & (PR_WPE | PR_RPE)))
+ continue;
+
+ pr_limit = (pr_value & PR_LIMIT_MASK) >> PR_LIMIT_SHIFT;
+ pr_base = pr_value & PR_BASE_MASK;
+
+ if (pr_base >= base && pr_limit <= limit)
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * There will be a single partition holding all enabled flash regions. We
+ * call this "BIOS".
+ */
+static void intel_spi_fill_partition(struct intel_spi *ispi,
+ struct mtd_partition *part)
+{
+ u64 end;
+ int i;
+
+ memset(part, 0, sizeof(*part));
+
+ /* Start from the mandatory descriptor region */
+ part->size = 4096;
+ part->name = "BIOS";
+
+ /*
+ * Now try to find where this partition ends based on the flash
+ * region registers.
+ */
+ for (i = 1; i < ispi->nregions; i++) {
+ u32 region, base, limit;
+
+ region = readl(ispi->base + FREG(i));
+ base = region & FREG_BASE_MASK;
+ limit = (region & FREG_LIMIT_MASK) >> FREG_LIMIT_SHIFT;
+
+ if (base >= limit || limit == 0)
+ continue;
+
+ /*
+ * If any of the regions have protection bits set, make the
+ * whole partition read-only to be on the safe side.
+ */
+ if (intel_spi_is_protected(ispi, base, limit))
+ ispi->writeable = 0;
+
+ end = (limit << 12) + 4096;
+ if (end > part->size)
+ part->size = end;
+ }
+}
+
+struct intel_spi *intel_spi_probe(struct device *dev,
+ struct resource *mem, const struct intel_spi_boardinfo *info)
+{
+ struct mtd_partition part;
+ struct intel_spi *ispi;
+ int ret;
+
+ if (!info || !mem)
+ return ERR_PTR(-EINVAL);
+
+ ispi = devm_kzalloc(dev, sizeof(*ispi), GFP_KERNEL);
+ if (!ispi)
+ return ERR_PTR(-ENOMEM);
+
+ ispi->base = devm_ioremap_resource(dev, mem);
+ if (IS_ERR(ispi->base))
+ return ispi->base;
+
+ ispi->dev = dev;
+ ispi->info = info;
+ ispi->writeable = info->writeable;
+
+ ret = intel_spi_init(ispi);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ispi->nor.dev = ispi->dev;
+ ispi->nor.priv = ispi;
+ ispi->nor.read_reg = intel_spi_read_reg;
+ ispi->nor.write_reg = intel_spi_write_reg;
+ ispi->nor.read = intel_spi_read;
+ ispi->nor.write = intel_spi_write;
+ ispi->nor.erase = intel_spi_erase;
+
+ ret = spi_nor_scan(&ispi->nor, NULL, SPI_NOR_NORMAL);
+ if (ret) {
+ dev_info(dev, "failed to locate the chip\n");
+ return ERR_PTR(ret);
+ }
+
+ intel_spi_fill_partition(ispi, &part);
+
+ /* Prevent writes if not explicitly enabled */
+ if (!ispi->writeable || !writeable)
+ ispi->nor.mtd.flags &= ~MTD_WRITEABLE;
+
+ ret = mtd_device_parse_register(&ispi->nor.mtd, NULL, NULL, &part, 1);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return ispi;
+}
+EXPORT_SYMBOL_GPL(intel_spi_probe);
+
+int intel_spi_remove(struct intel_spi *ispi)
+{
+ return mtd_device_unregister(&ispi->nor.mtd);
+}
+EXPORT_SYMBOL_GPL(intel_spi_remove);
+
+MODULE_DESCRIPTION("Intel PCH/PCU SPI flash core driver");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/spi-nor/intel-spi.h b/drivers/mtd/spi-nor/intel-spi.h
new file mode 100644
index 0000000..5ab7dc2
--- /dev/null
+++ b/drivers/mtd/spi-nor/intel-spi.h
@@ -0,0 +1,24 @@
+/*
+ * Intel PCH/PCU SPI flash driver.
+ *
+ * Copyright (C) 2016, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.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.
+ */
+
+#ifndef INTEL_SPI_H
+#define INTEL_SPI_H
+
+#include <linux/platform_data/intel-spi.h>
+
+struct intel_spi;
+struct resource;
+
+struct intel_spi *intel_spi_probe(struct device *dev,
+ struct resource *mem, const struct intel_spi_boardinfo *info);
+int intel_spi_remove(struct intel_spi *ispi);
+
+#endif /* INTEL_SPI_H */
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index da7cd69..1ae872b 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -75,6 +75,16 @@
* bit. Must be used with
* SPI_NOR_HAS_LOCK.
*/
+#define SPI_S3AN BIT(10) /*
+ * Xilinx Spartan 3AN In-System Flash
+ * (MFR cannot be used for probing
+ * because it has the same value as
+ * ATMEL flashes)
+ */
+#define SPI_NOR_4B_OPCODES BIT(11) /*
+ * Use dedicated 4byte address op codes
+ * to support memory size above 128Mib.
+ */
};
#define JEDEC_MFR(info) ((info)->id[0])
@@ -122,7 +132,7 @@
/*
* Read configuration register, returning its value in the
* location. Return the configuration register value.
- * Returns negative if error occured.
+ * Returns negative if error occurred.
*/
static int read_cr(struct spi_nor *nor)
{
@@ -188,6 +198,78 @@
return mtd->priv;
}
+
+static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ if (table[i][0] == opcode)
+ return table[i][1];
+
+ /* No conversion found, keep input op code. */
+ return opcode;
+}
+
+static inline u8 spi_nor_convert_3to4_read(u8 opcode)
+{
+ static const u8 spi_nor_3to4_read[][2] = {
+ { SPINOR_OP_READ, SPINOR_OP_READ_4B },
+ { SPINOR_OP_READ_FAST, SPINOR_OP_READ_FAST_4B },
+ { SPINOR_OP_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B },
+ { SPINOR_OP_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B },
+ { SPINOR_OP_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B },
+ { SPINOR_OP_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B },
+ };
+
+ return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,
+ ARRAY_SIZE(spi_nor_3to4_read));
+}
+
+static inline u8 spi_nor_convert_3to4_program(u8 opcode)
+{
+ static const u8 spi_nor_3to4_program[][2] = {
+ { SPINOR_OP_PP, SPINOR_OP_PP_4B },
+ { SPINOR_OP_PP_1_1_4, SPINOR_OP_PP_1_1_4_4B },
+ { SPINOR_OP_PP_1_4_4, SPINOR_OP_PP_1_4_4_4B },
+ };
+
+ return spi_nor_convert_opcode(opcode, spi_nor_3to4_program,
+ ARRAY_SIZE(spi_nor_3to4_program));
+}
+
+static inline u8 spi_nor_convert_3to4_erase(u8 opcode)
+{
+ static const u8 spi_nor_3to4_erase[][2] = {
+ { SPINOR_OP_BE_4K, SPINOR_OP_BE_4K_4B },
+ { SPINOR_OP_BE_32K, SPINOR_OP_BE_32K_4B },
+ { SPINOR_OP_SE, SPINOR_OP_SE_4B },
+ };
+
+ return spi_nor_convert_opcode(opcode, spi_nor_3to4_erase,
+ ARRAY_SIZE(spi_nor_3to4_erase));
+}
+
+static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
+ const struct flash_info *info)
+{
+ /* Do some manufacturer fixups first */
+ switch (JEDEC_MFR(info)) {
+ case SNOR_MFR_SPANSION:
+ /* No small sector erase for 4-byte command set */
+ nor->erase_opcode = SPINOR_OP_SE;
+ nor->mtd.erasesize = info->sector_size;
+ break;
+
+ default:
+ break;
+ }
+
+ nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
+ nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode);
+ nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
+}
+
/* Enable/disable 4-byte addressing mode. */
static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
int enable)
@@ -217,6 +299,21 @@
return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
}
}
+
+static int s3an_sr_ready(struct spi_nor *nor)
+{
+ int ret;
+ u8 val;
+
+ ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1);
+ if (ret < 0) {
+ dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);
+ return ret;
+ }
+
+ return !!(val & XSR_RDY);
+}
+
static inline int spi_nor_sr_ready(struct spi_nor *nor)
{
int sr = read_sr(nor);
@@ -238,7 +335,11 @@
static int spi_nor_ready(struct spi_nor *nor)
{
int sr, fsr;
- sr = spi_nor_sr_ready(nor);
+
+ if (nor->flags & SNOR_F_READY_XSR_RDY)
+ sr = s3an_sr_ready(nor);
+ else
+ sr = spi_nor_sr_ready(nor);
if (sr < 0)
return sr;
fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1;
@@ -320,6 +421,27 @@
}
/*
+ * This code converts an address to the Default Address Mode, that has non
+ * power of two page sizes. We must support this mode because it is the default
+ * mode supported by Xilinx tools, it can access the whole flash area and
+ * changing over to the Power-of-two mode is irreversible and corrupts the
+ * original data.
+ * Addr can safely be unsigned int, the biggest S3AN device is smaller than
+ * 4 MiB.
+ */
+static loff_t spi_nor_s3an_addr_convert(struct spi_nor *nor, unsigned int addr)
+{
+ unsigned int offset;
+ unsigned int page;
+
+ offset = addr % nor->page_size;
+ page = addr / nor->page_size;
+ page <<= (nor->page_size > 512) ? 10 : 9;
+
+ return page | offset;
+}
+
+/*
* Initiate the erasure of a single sector
*/
static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
@@ -327,6 +449,9 @@
u8 buf[SPI_NOR_MAX_ADDR_WIDTH];
int i;
+ if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
+ addr = spi_nor_s3an_addr_convert(nor, addr);
+
if (nor->erase)
return nor->erase(nor, addr);
@@ -368,7 +493,7 @@
return ret;
/* whole-chip erase? */
- if (len == mtd->size) {
+ if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
unsigned long timeout;
write_enable(nor);
@@ -782,6 +907,19 @@
.addr_width = (_addr_width), \
.flags = (_flags),
+#define S3AN_INFO(_jedec_id, _n_sectors, _page_size) \
+ .id = { \
+ ((_jedec_id) >> 16) & 0xff, \
+ ((_jedec_id) >> 8) & 0xff, \
+ (_jedec_id) & 0xff \
+ }, \
+ .id_len = 3, \
+ .sector_size = (8*_page_size), \
+ .n_sectors = (_n_sectors), \
+ .page_size = _page_size, \
+ .addr_width = 3, \
+ .flags = SPI_NOR_NO_FR | SPI_S3AN,
+
/* NOTE: double check command sets and memory organization when you add
* more nor chips. This current list focusses on newer chips, which
* have been converging on command sets which including JEDEC ID.
@@ -821,7 +959,7 @@
{ "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, SECT_4K) },
/* ESMT */
- { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) },
+ { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) },
/* Everspin */
{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
@@ -833,6 +971,11 @@
/* GigaDevice */
{
+ "gd25q16", INFO(0xc84015, 0, 64 * 1024, 32,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ },
+ {
"gd25q32", INFO(0xc84016, 0, 64 * 1024, 64,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
@@ -1014,6 +1157,13 @@
{ "cat25c09", CAT25_INFO( 128, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
{ "cat25c17", CAT25_INFO( 256, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
{ "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+
+ /* Xilinx S3AN Internal Flash */
+ { "3S50AN", S3AN_INFO(0x1f2200, 64, 264) },
+ { "3S200AN", S3AN_INFO(0x1f2400, 256, 264) },
+ { "3S400AN", S3AN_INFO(0x1f2400, 256, 264) },
+ { "3S700AN", S3AN_INFO(0x1f2500, 512, 264) },
+ { "3S1400AN", S3AN_INFO(0x1f2600, 512, 528) },
{ },
};
@@ -1054,7 +1204,12 @@
return ret;
while (len) {
- ret = nor->read(nor, from, len, buf);
+ loff_t addr = from;
+
+ if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
+ addr = spi_nor_s3an_addr_convert(nor, addr);
+
+ ret = nor->read(nor, addr, len, buf);
if (ret == 0) {
/* We shouldn't see 0-length reads */
ret = -EIO;
@@ -1175,17 +1330,32 @@
for (i = 0; i < len; ) {
ssize_t written;
+ loff_t addr = to + i;
- page_offset = (to + i) & (nor->page_size - 1);
- WARN_ONCE(page_offset,
- "Writing at offset %zu into a NOR page. Writing partial pages may decrease reliability and increase wear of NOR flash.",
- page_offset);
+ /*
+ * If page_size is a power of two, the offset can be quickly
+ * calculated with an AND operation. On the other cases we
+ * need to do a modulus operation (more expensive).
+ * Power of two numbers have only one bit set and we can use
+ * the instruction hweight32 to detect if we need to do a
+ * modulus (do_div()) or not.
+ */
+ if (hweight32(nor->page_size) == 1) {
+ page_offset = addr & (nor->page_size - 1);
+ } else {
+ uint64_t aux = addr;
+
+ page_offset = do_div(aux, nor->page_size);
+ }
/* the size of data remaining on the first page */
page_remain = min_t(size_t,
nor->page_size - page_offset, len - i);
+ if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
+ addr = spi_nor_s3an_addr_convert(nor, addr);
+
write_enable(nor);
- ret = nor->write(nor, to + i, page_remain, buf + i);
+ ret = nor->write(nor, addr, page_remain, buf + i);
if (ret < 0)
goto write_err;
written = ret;
@@ -1216,6 +1386,9 @@
val = read_sr(nor);
if (val < 0)
return val;
+ if (val & SR_QUAD_EN_MX)
+ return 0;
+
write_enable(nor);
write_sr(nor, val | SR_QUAD_EN_MX);
@@ -1236,7 +1409,7 @@
* Write status Register and configuration register with 2 bytes
* The first byte will be written to the status register, while the
* second byte will be written to the configuration register.
- * Return negative if error occured.
+ * Return negative if error occurred.
*/
static int write_sr_cr(struct spi_nor *nor, u16 val)
{
@@ -1312,6 +1485,47 @@
return 0;
}
+static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor)
+{
+ int ret;
+ u8 val;
+
+ ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1);
+ if (ret < 0) {
+ dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);
+ return ret;
+ }
+
+ nor->erase_opcode = SPINOR_OP_XSE;
+ nor->program_opcode = SPINOR_OP_XPP;
+ nor->read_opcode = SPINOR_OP_READ;
+ nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
+
+ /*
+ * This flashes have a page size of 264 or 528 bytes (known as
+ * Default addressing mode). It can be changed to a more standard
+ * Power of two mode where the page size is 256/512. This comes
+ * with a price: there is 3% less of space, the data is corrupted
+ * and the page size cannot be changed back to default addressing
+ * mode.
+ *
+ * The current addressing mode can be read from the XRDSR register
+ * and should not be changed, because is a destructive operation.
+ */
+ if (val & XSR_PAGESIZE) {
+ /* Flash in Power of 2 mode */
+ nor->page_size = (nor->page_size == 264) ? 256 : 512;
+ nor->mtd.writebufsize = nor->page_size;
+ nor->mtd.size = 8 * nor->page_size * info->n_sectors;
+ nor->mtd.erasesize = 8 * nor->page_size;
+ } else {
+ /* Flash in Default addressing mode */
+ nor->flags |= SNOR_F_S3AN_ADDR_DEFAULT;
+ }
+
+ return 0;
+}
+
int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
{
const struct flash_info *info = NULL;
@@ -1360,6 +1574,14 @@
mutex_init(&nor->lock);
/*
+ * Make sure the XSR_RDY flag is set before calling
+ * spi_nor_wait_till_ready(). Xilinx S3AN share MFR
+ * with Atmel spi-nor
+ */
+ if (info->flags & SPI_S3AN)
+ nor->flags |= SNOR_F_READY_XSR_RDY;
+
+ /*
* Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
* with the software protection bits set
*/
@@ -1483,27 +1705,10 @@
else if (mtd->size > 0x1000000) {
/* enable 4-byte addressing if the device exceeds 16MiB */
nor->addr_width = 4;
- if (JEDEC_MFR(info) == SNOR_MFR_SPANSION) {
- /* Dedicated 4-byte command set */
- switch (nor->flash_read) {
- case SPI_NOR_QUAD:
- nor->read_opcode = SPINOR_OP_READ4_1_1_4;
- break;
- case SPI_NOR_DUAL:
- nor->read_opcode = SPINOR_OP_READ4_1_1_2;
- break;
- case SPI_NOR_FAST:
- nor->read_opcode = SPINOR_OP_READ4_FAST;
- break;
- case SPI_NOR_NORMAL:
- nor->read_opcode = SPINOR_OP_READ4;
- break;
- }
- nor->program_opcode = SPINOR_OP_PP_4B;
- /* No small sector erase for 4-byte command set */
- nor->erase_opcode = SPINOR_OP_SE_4B;
- mtd->erasesize = info->sector_size;
- } else
+ if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
+ info->flags & SPI_NOR_4B_OPCODES)
+ spi_nor_set_4byte_opcodes(nor, info);
+ else
set_4byte(nor, info, 1);
} else {
nor->addr_width = 3;
@@ -1517,6 +1722,12 @@
nor->read_dummy = spi_nor_read_dummy_cycles(nor);
+ if (info->flags & SPI_S3AN) {
+ ret = s3an_nor_scan(info, nor);
+ if (ret)
+ return ret;
+ }
+
dev_info(dev, "%s (%lld Kbytes)\n", info->name,
(long long)mtd->size >> 10);
diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
index c9b7ad6..726b569 100644
--- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
+++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
@@ -1668,7 +1668,7 @@
free_buffers:
/* compensate sw bpool counter changes */
- for (i--; i > 0; i--) {
+ for (i--; i >= 0; i--) {
dpaa_bp = dpaa_bpid2pool(sgt[i].bpid);
if (dpaa_bp) {
count_ptr = this_cpu_ptr(dpaa_bp->percpu_count);
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 38160c2..8be7034 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -2910,6 +2910,7 @@
struct netdev_hw_addr *ha;
unsigned int i, bit, data, crc, tmp;
unsigned char hash;
+ unsigned int hash_high = 0, hash_low = 0;
if (ndev->flags & IFF_PROMISC) {
tmp = readl(fep->hwp + FEC_R_CNTRL);
@@ -2932,11 +2933,7 @@
return;
}
- /* Clear filter and add the addresses in hash register
- */
- writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
- writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
-
+ /* Add the addresses in hash register */
netdev_for_each_mc_addr(ha, ndev) {
/* calculate crc32 value of mac address */
crc = 0xffffffff;
@@ -2954,16 +2951,14 @@
*/
hash = (crc >> (32 - FEC_HASH_BITS)) & 0x3f;
- if (hash > 31) {
- tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
- tmp |= 1 << (hash - 32);
- writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
- } else {
- tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_LOW);
- tmp |= 1 << hash;
- writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
- }
+ if (hash > 31)
+ hash_high |= 1 << (hash - 32);
+ else
+ hash_low |= 1 << hash;
}
+
+ writel(hash_high, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
+ writel(hash_low, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
}
/* Set a MAC change in hardware. */
diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c
index c125966..a07b8d7 100644
--- a/drivers/net/ethernet/ibm/ibmvnic.c
+++ b/drivers/net/ethernet/ibm/ibmvnic.c
@@ -189,9 +189,10 @@
}
ltb->map_id = adapter->map_id;
adapter->map_id++;
+
+ init_completion(&adapter->fw_done);
send_request_map(adapter, ltb->addr,
ltb->size, ltb->map_id);
- init_completion(&adapter->fw_done);
wait_for_completion(&adapter->fw_done);
return 0;
}
@@ -505,7 +506,7 @@
adapter->rx_pool = NULL;
rx_pool_arr_alloc_failed:
for (i = 0; i < adapter->req_rx_queues; i++)
- napi_enable(&adapter->napi[i]);
+ napi_disable(&adapter->napi[i]);
alloc_napi_failed:
return -ENOMEM;
}
@@ -1121,10 +1122,10 @@
crq.request_statistics.ioba = cpu_to_be32(adapter->stats_token);
crq.request_statistics.len =
cpu_to_be32(sizeof(struct ibmvnic_statistics));
- ibmvnic_send_crq(adapter, &crq);
/* Wait for data to be written */
init_completion(&adapter->stats_done);
+ ibmvnic_send_crq(adapter, &crq);
wait_for_completion(&adapter->stats_done);
for (i = 0; i < ARRAY_SIZE(ibmvnic_stats); i++)
@@ -1496,7 +1497,7 @@
adapter->req_rx_queues = adapter->opt_rx_comp_queues;
adapter->req_rx_add_queues = adapter->max_rx_add_queues;
- adapter->req_mtu = adapter->max_mtu;
+ adapter->req_mtu = adapter->netdev->mtu + ETH_HLEN;
}
total_queues = adapter->req_tx_queues + adapter->req_rx_queues;
@@ -2185,12 +2186,12 @@
if (!found) {
dev_err(dev, "Couldn't find error id %x\n",
- crq->request_error_rsp.error_id);
+ be32_to_cpu(crq->request_error_rsp.error_id));
return;
}
dev_err(dev, "Detailed info for error id %x:",
- crq->request_error_rsp.error_id);
+ be32_to_cpu(crq->request_error_rsp.error_id));
for (i = 0; i < error_buff->len; i++) {
pr_cont("%02x", (int)error_buff->buff[i]);
@@ -2269,8 +2270,8 @@
dev_err(dev, "Firmware reports %serror id %x, cause %d\n",
crq->error_indication.
flags & IBMVNIC_FATAL_ERROR ? "FATAL " : "",
- crq->error_indication.error_id,
- crq->error_indication.error_cause);
+ be32_to_cpu(crq->error_indication.error_id),
+ be16_to_cpu(crq->error_indication.error_cause));
error_buff = kmalloc(sizeof(*error_buff), GFP_ATOMIC);
if (!error_buff)
@@ -2388,10 +2389,10 @@
case PARTIALSUCCESS:
dev_info(dev, "req=%lld, rsp=%ld in %s queue, retrying.\n",
*req_value,
- (long int)be32_to_cpu(crq->request_capability_rsp.
+ (long int)be64_to_cpu(crq->request_capability_rsp.
number), name);
release_sub_crqs_no_irqs(adapter);
- *req_value = be32_to_cpu(crq->request_capability_rsp.number);
+ *req_value = be64_to_cpu(crq->request_capability_rsp.number);
init_sub_crqs(adapter, 1);
return;
default:
@@ -2626,12 +2627,12 @@
break;
case MIN_MTU:
adapter->min_mtu = be64_to_cpu(crq->query_capability.number);
- netdev->min_mtu = adapter->min_mtu;
+ netdev->min_mtu = adapter->min_mtu - ETH_HLEN;
netdev_dbg(netdev, "min_mtu = %lld\n", adapter->min_mtu);
break;
case MAX_MTU:
adapter->max_mtu = be64_to_cpu(crq->query_capability.number);
- netdev->max_mtu = adapter->max_mtu;
+ netdev->max_mtu = adapter->max_mtu - ETH_HLEN;
netdev_dbg(netdev, "max_mtu = %lld\n", adapter->max_mtu);
break;
case MAX_MULTICAST_FILTERS:
@@ -2799,9 +2800,9 @@
crq.collect_fw_trace.correlator = adapter->ras_comps[num].correlator;
crq.collect_fw_trace.ioba = cpu_to_be32(trace_tok);
crq.collect_fw_trace.len = adapter->ras_comps[num].trace_buff_size;
- ibmvnic_send_crq(adapter, &crq);
init_completion(&adapter->fw_done);
+ ibmvnic_send_crq(adapter, &crq);
wait_for_completion(&adapter->fw_done);
if (*ppos + len > be32_to_cpu(adapter->ras_comps[num].trace_buff_size))
@@ -3581,9 +3582,9 @@
memset(&crq, 0, sizeof(crq));
crq.request_dump_size.first = IBMVNIC_CRQ_CMD;
crq.request_dump_size.cmd = REQUEST_DUMP_SIZE;
- ibmvnic_send_crq(adapter, &crq);
init_completion(&adapter->fw_done);
+ ibmvnic_send_crq(adapter, &crq);
wait_for_completion(&adapter->fw_done);
seq_write(seq, adapter->dump_data, adapter->dump_data_size);
@@ -3629,8 +3630,8 @@
}
}
- send_version_xchg(adapter);
reinit_completion(&adapter->init_done);
+ send_version_xchg(adapter);
if (!wait_for_completion_timeout(&adapter->init_done, timeout)) {
dev_err(dev, "Passive init timeout\n");
goto task_failed;
@@ -3640,9 +3641,9 @@
if (adapter->renegotiate) {
adapter->renegotiate = false;
release_sub_crqs_no_irqs(adapter);
- send_cap_queries(adapter);
reinit_completion(&adapter->init_done);
+ send_cap_queries(adapter);
if (!wait_for_completion_timeout(&adapter->init_done,
timeout)) {
dev_err(dev, "Passive init timeout\n");
@@ -3656,9 +3657,7 @@
goto task_failed;
netdev->real_num_tx_queues = adapter->req_tx_queues;
- netdev->mtu = adapter->req_mtu;
- netdev->min_mtu = adapter->min_mtu;
- netdev->max_mtu = adapter->max_mtu;
+ netdev->mtu = adapter->req_mtu - ETH_HLEN;
if (adapter->failover) {
adapter->failover = false;
@@ -3772,9 +3771,9 @@
adapter->debugfs_dump = ent;
}
}
- ibmvnic_send_crq_init(adapter);
init_completion(&adapter->init_done);
+ ibmvnic_send_crq_init(adapter);
if (!wait_for_completion_timeout(&adapter->init_done, timeout))
return 0;
@@ -3782,9 +3781,9 @@
if (adapter->renegotiate) {
adapter->renegotiate = false;
release_sub_crqs_no_irqs(adapter);
- send_cap_queries(adapter);
reinit_completion(&adapter->init_done);
+ send_cap_queries(adapter);
if (!wait_for_completion_timeout(&adapter->init_done,
timeout))
return 0;
@@ -3798,7 +3797,7 @@
}
netdev->real_num_tx_queues = adapter->req_tx_queues;
- netdev->mtu = adapter->req_mtu;
+ netdev->mtu = adapter->req_mtu - ETH_HLEN;
rc = register_netdev(netdev);
if (rc) {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
index c5282b6..2ebbe80 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
@@ -1087,10 +1087,14 @@
mlx5_fc_query_cached(counter, &bytes, &packets, &lastuse);
+ preempt_disable();
+
tcf_exts_to_list(f->exts, &actions);
list_for_each_entry(a, &actions, list)
tcf_action_stats_update(a, bytes, packets, lastuse);
+ preempt_enable();
+
return 0;
}
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index b203143..6508822 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -3160,7 +3160,7 @@
{
struct platform_device *pdev = to_platform_device(dev);
struct net_device *ndev = platform_get_drvdata(pdev);
- struct cpsw_common *cpsw = netdev_priv(ndev);
+ struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
/* Select default pin state */
pinctrl_pm_select_default_state(dev);
diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
index 93dc10b..aa02a03 100644
--- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c
+++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
@@ -100,6 +100,14 @@
/* BUFFER_ALIGN(adr) calculates the number of bytes to the next alignment. */
#define BUFFER_ALIGN(adr) ((ALIGNMENT - ((u32) adr)) % ALIGNMENT)
+#ifdef __BIG_ENDIAN
+#define xemaclite_readl ioread32be
+#define xemaclite_writel iowrite32be
+#else
+#define xemaclite_readl ioread32
+#define xemaclite_writel iowrite32
+#endif
+
/**
* struct net_local - Our private per device data
* @ndev: instance of the network device
@@ -156,15 +164,15 @@
u32 reg_data;
/* Enable the Tx interrupts for the first Buffer */
- reg_data = __raw_readl(drvdata->base_addr + XEL_TSR_OFFSET);
- __raw_writel(reg_data | XEL_TSR_XMIT_IE_MASK,
- drvdata->base_addr + XEL_TSR_OFFSET);
+ reg_data = xemaclite_readl(drvdata->base_addr + XEL_TSR_OFFSET);
+ xemaclite_writel(reg_data | XEL_TSR_XMIT_IE_MASK,
+ drvdata->base_addr + XEL_TSR_OFFSET);
/* Enable the Rx interrupts for the first buffer */
- __raw_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + XEL_RSR_OFFSET);
+ xemaclite_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + XEL_RSR_OFFSET);
/* Enable the Global Interrupt Enable */
- __raw_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET);
+ xemaclite_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET);
}
/**
@@ -179,17 +187,17 @@
u32 reg_data;
/* Disable the Global Interrupt Enable */
- __raw_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET);
+ xemaclite_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET);
/* Disable the Tx interrupts for the first buffer */
- reg_data = __raw_readl(drvdata->base_addr + XEL_TSR_OFFSET);
- __raw_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK),
- drvdata->base_addr + XEL_TSR_OFFSET);
+ reg_data = xemaclite_readl(drvdata->base_addr + XEL_TSR_OFFSET);
+ xemaclite_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK),
+ drvdata->base_addr + XEL_TSR_OFFSET);
/* Disable the Rx interrupts for the first buffer */
- reg_data = __raw_readl(drvdata->base_addr + XEL_RSR_OFFSET);
- __raw_writel(reg_data & (~XEL_RSR_RECV_IE_MASK),
- drvdata->base_addr + XEL_RSR_OFFSET);
+ reg_data = xemaclite_readl(drvdata->base_addr + XEL_RSR_OFFSET);
+ xemaclite_writel(reg_data & (~XEL_RSR_RECV_IE_MASK),
+ drvdata->base_addr + XEL_RSR_OFFSET);
}
/**
@@ -321,7 +329,7 @@
byte_count = ETH_FRAME_LEN;
/* Check if the expected buffer is available */
- reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
+ reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET);
if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK |
XEL_TSR_XMIT_ACTIVE_MASK)) == 0) {
@@ -334,7 +342,7 @@
addr = (void __iomem __force *)((u32 __force)addr ^
XEL_BUFFER_OFFSET);
- reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
+ reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET);
if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK |
XEL_TSR_XMIT_ACTIVE_MASK)) != 0)
@@ -345,16 +353,16 @@
/* Write the frame to the buffer */
xemaclite_aligned_write(data, (u32 __force *) addr, byte_count);
- __raw_writel((byte_count & XEL_TPLR_LENGTH_MASK),
- addr + XEL_TPLR_OFFSET);
+ xemaclite_writel((byte_count & XEL_TPLR_LENGTH_MASK),
+ addr + XEL_TPLR_OFFSET);
/* Update the Tx Status Register to indicate that there is a
* frame to send. Set the XEL_TSR_XMIT_ACTIVE_MASK flag which
* is used by the interrupt handler to check whether a frame
* has been transmitted */
- reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
+ reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET);
reg_data |= (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK);
- __raw_writel(reg_data, addr + XEL_TSR_OFFSET);
+ xemaclite_writel(reg_data, addr + XEL_TSR_OFFSET);
return 0;
}
@@ -369,7 +377,7 @@
*
* Return: Total number of bytes received
*/
-static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
+static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data, int maxlen)
{
void __iomem *addr;
u16 length, proto_type;
@@ -379,7 +387,7 @@
addr = (drvdata->base_addr + drvdata->next_rx_buf_to_use);
/* Verify which buffer has valid data */
- reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
+ reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET);
if ((reg_data & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) {
if (drvdata->rx_ping_pong != 0)
@@ -396,27 +404,28 @@
return 0; /* No data was available */
/* Verify that buffer has valid data */
- reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
+ reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET);
if ((reg_data & XEL_RSR_RECV_DONE_MASK) !=
XEL_RSR_RECV_DONE_MASK)
return 0; /* No data was available */
}
/* Get the protocol type of the ethernet frame that arrived */
- proto_type = ((ntohl(__raw_readl(addr + XEL_HEADER_OFFSET +
+ proto_type = ((ntohl(xemaclite_readl(addr + XEL_HEADER_OFFSET +
XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) &
XEL_RPLR_LENGTH_MASK);
/* Check if received ethernet frame is a raw ethernet frame
* or an IP packet or an ARP packet */
- if (proto_type > (ETH_FRAME_LEN + ETH_FCS_LEN)) {
+ if (proto_type > ETH_DATA_LEN) {
if (proto_type == ETH_P_IP) {
- length = ((ntohl(__raw_readl(addr +
+ length = ((ntohl(xemaclite_readl(addr +
XEL_HEADER_IP_LENGTH_OFFSET +
XEL_RXBUFF_OFFSET)) >>
XEL_HEADER_SHIFT) &
XEL_RPLR_LENGTH_MASK);
+ length = min_t(u16, length, ETH_DATA_LEN);
length += ETH_HLEN + ETH_FCS_LEN;
} else if (proto_type == ETH_P_ARP)
@@ -429,14 +438,17 @@
/* Use the length in the frame, plus the header and trailer */
length = proto_type + ETH_HLEN + ETH_FCS_LEN;
+ if (WARN_ON(length > maxlen))
+ length = maxlen;
+
/* Read from the EmacLite device */
xemaclite_aligned_read((u32 __force *) (addr + XEL_RXBUFF_OFFSET),
data, length);
/* Acknowledge the frame */
- reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
+ reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET);
reg_data &= ~XEL_RSR_RECV_DONE_MASK;
- __raw_writel(reg_data, addr + XEL_RSR_OFFSET);
+ xemaclite_writel(reg_data, addr + XEL_RSR_OFFSET);
return length;
}
@@ -463,14 +475,14 @@
xemaclite_aligned_write(address_ptr, (u32 __force *) addr, ETH_ALEN);
- __raw_writel(ETH_ALEN, addr + XEL_TPLR_OFFSET);
+ xemaclite_writel(ETH_ALEN, addr + XEL_TPLR_OFFSET);
/* Update the MAC address in the EmacLite */
- reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
- __raw_writel(reg_data | XEL_TSR_PROG_MAC_ADDR, addr + XEL_TSR_OFFSET);
+ reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET);
+ xemaclite_writel(reg_data | XEL_TSR_PROG_MAC_ADDR, addr + XEL_TSR_OFFSET);
/* Wait for EmacLite to finish with the MAC address update */
- while ((__raw_readl(addr + XEL_TSR_OFFSET) &
+ while ((xemaclite_readl(addr + XEL_TSR_OFFSET) &
XEL_TSR_PROG_MAC_ADDR) != 0)
;
}
@@ -603,7 +615,7 @@
skb_reserve(skb, 2);
- len = xemaclite_recv_data(lp, (u8 *) skb->data);
+ len = xemaclite_recv_data(lp, (u8 *) skb->data, len);
if (!len) {
dev->stats.rx_errors++;
@@ -640,32 +652,32 @@
u32 tx_status;
/* Check if there is Rx Data available */
- if ((__raw_readl(base_addr + XEL_RSR_OFFSET) &
+ if ((xemaclite_readl(base_addr + XEL_RSR_OFFSET) &
XEL_RSR_RECV_DONE_MASK) ||
- (__raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET)
+ (xemaclite_readl(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET)
& XEL_RSR_RECV_DONE_MASK))
xemaclite_rx_handler(dev);
/* Check if the Transmission for the first buffer is completed */
- tx_status = __raw_readl(base_addr + XEL_TSR_OFFSET);
+ tx_status = xemaclite_readl(base_addr + XEL_TSR_OFFSET);
if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) &&
(tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) {
tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK;
- __raw_writel(tx_status, base_addr + XEL_TSR_OFFSET);
+ xemaclite_writel(tx_status, base_addr + XEL_TSR_OFFSET);
tx_complete = true;
}
/* Check if the Transmission for the second buffer is completed */
- tx_status = __raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
+ tx_status = xemaclite_readl(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) &&
(tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) {
tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK;
- __raw_writel(tx_status, base_addr + XEL_BUFFER_OFFSET +
- XEL_TSR_OFFSET);
+ xemaclite_writel(tx_status, base_addr + XEL_BUFFER_OFFSET +
+ XEL_TSR_OFFSET);
tx_complete = true;
}
@@ -698,7 +710,7 @@
/* wait for the MDIO interface to not be busy or timeout
after some time.
*/
- while (__raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET) &
+ while (xemaclite_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET) &
XEL_MDIOCTRL_MDIOSTS_MASK) {
if (time_before_eq(end, jiffies)) {
WARN_ON(1);
@@ -734,17 +746,17 @@
* MDIO Address register. Set the Status bit in the MDIO Control
* register to start a MDIO read transaction.
*/
- ctrl_reg = __raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET);
- __raw_writel(XEL_MDIOADDR_OP_MASK |
- ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg),
- lp->base_addr + XEL_MDIOADDR_OFFSET);
- __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK,
- lp->base_addr + XEL_MDIOCTRL_OFFSET);
+ ctrl_reg = xemaclite_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET);
+ xemaclite_writel(XEL_MDIOADDR_OP_MASK |
+ ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg),
+ lp->base_addr + XEL_MDIOADDR_OFFSET);
+ xemaclite_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK,
+ lp->base_addr + XEL_MDIOCTRL_OFFSET);
if (xemaclite_mdio_wait(lp))
return -ETIMEDOUT;
- rc = __raw_readl(lp->base_addr + XEL_MDIORD_OFFSET);
+ rc = xemaclite_readl(lp->base_addr + XEL_MDIORD_OFFSET);
dev_dbg(&lp->ndev->dev,
"xemaclite_mdio_read(phy_id=%i, reg=%x) == %x\n",
@@ -781,13 +793,13 @@
* Data register. Finally, set the Status bit in the MDIO Control
* register to start a MDIO write transaction.
*/
- ctrl_reg = __raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET);
- __raw_writel(~XEL_MDIOADDR_OP_MASK &
- ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg),
- lp->base_addr + XEL_MDIOADDR_OFFSET);
- __raw_writel(val, lp->base_addr + XEL_MDIOWR_OFFSET);
- __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK,
- lp->base_addr + XEL_MDIOCTRL_OFFSET);
+ ctrl_reg = xemaclite_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET);
+ xemaclite_writel(~XEL_MDIOADDR_OP_MASK &
+ ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg),
+ lp->base_addr + XEL_MDIOADDR_OFFSET);
+ xemaclite_writel(val, lp->base_addr + XEL_MDIOWR_OFFSET);
+ xemaclite_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK,
+ lp->base_addr + XEL_MDIOCTRL_OFFSET);
return 0;
}
@@ -834,8 +846,8 @@
/* Enable the MDIO bus by asserting the enable bit in MDIO Control
* register.
*/
- __raw_writel(XEL_MDIOCTRL_MDIOEN_MASK,
- lp->base_addr + XEL_MDIOCTRL_OFFSET);
+ xemaclite_writel(XEL_MDIOCTRL_MDIOEN_MASK,
+ lp->base_addr + XEL_MDIOCTRL_OFFSET);
bus = mdiobus_alloc();
if (!bus) {
@@ -1140,8 +1152,8 @@
}
/* Clear the Tx CSR's in case this is a restart */
- __raw_writel(0, lp->base_addr + XEL_TSR_OFFSET);
- __raw_writel(0, lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
+ xemaclite_writel(0, lp->base_addr + XEL_TSR_OFFSET);
+ xemaclite_writel(0, lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
/* Set the MAC address in the EmacLite device */
xemaclite_update_address(lp, ndev->dev_addr);
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 50b62db..30b04cf 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -2439,7 +2439,8 @@
rt = vxlan_get_route(vxlan, dev, sock4, skb, 0, info->key.tos,
info->key.u.ipv4.dst,
- &info->key.u.ipv4.src, dport, sport, NULL, info);
+ &info->key.u.ipv4.src, dport, sport,
+ &info->dst_cache, info);
if (IS_ERR(rt))
return PTR_ERR(rt);
ip_rt_put(rt);
@@ -2450,7 +2451,8 @@
ndst = vxlan6_get_route(vxlan, dev, sock6, skb, 0, info->key.tos,
info->key.label, &info->key.u.ipv6.dst,
- &info->key.u.ipv6.src, dport, sport, NULL, info);
+ &info->key.u.ipv6.src, dport, sport,
+ &info->dst_cache, info);
if (IS_ERR(ndst))
return PTR_ERR(ndst);
dst_release(ndst);
diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h
index 3ce1f7d..530586b 100644
--- a/drivers/net/xen-netback/common.h
+++ b/drivers/net/xen-netback/common.h
@@ -113,10 +113,10 @@
* A subset of struct net_device_stats that contains only the
* fields that are updated in netback.c for each queue.
*/
- unsigned int rx_bytes;
- unsigned int rx_packets;
- unsigned int tx_bytes;
- unsigned int tx_packets;
+ u64 rx_bytes;
+ u64 rx_packets;
+ u64 tx_bytes;
+ u64 tx_packets;
/* Additional stats used by xenvif */
unsigned long rx_gso_checksum_fixup;
diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c
index 5795213..50fa169 100644
--- a/drivers/net/xen-netback/interface.c
+++ b/drivers/net/xen-netback/interface.c
@@ -221,10 +221,10 @@
{
struct xenvif *vif = netdev_priv(dev);
struct xenvif_queue *queue = NULL;
- unsigned long rx_bytes = 0;
- unsigned long rx_packets = 0;
- unsigned long tx_bytes = 0;
- unsigned long tx_packets = 0;
+ u64 rx_bytes = 0;
+ u64 rx_packets = 0;
+ u64 tx_bytes = 0;
+ u64 tx_packets = 0;
unsigned int index;
spin_lock(&vif->lock);
diff --git a/drivers/ntb/hw/intel/ntb_hw_intel.c b/drivers/ntb/hw/intel/ntb_hw_intel.c
index eca9688..c002384 100644
--- a/drivers/ntb/hw/intel/ntb_hw_intel.c
+++ b/drivers/ntb/hw/intel/ntb_hw_intel.c
@@ -1629,6 +1629,28 @@
/* Skylake Xeon NTB */
+static int skx_poll_link(struct intel_ntb_dev *ndev)
+{
+ u16 reg_val;
+ int rc;
+
+ ndev->reg->db_iowrite(ndev->db_link_mask,
+ ndev->self_mmio +
+ ndev->self_reg->db_clear);
+
+ rc = pci_read_config_word(ndev->ntb.pdev,
+ SKX_LINK_STATUS_OFFSET, ®_val);
+ if (rc)
+ return 0;
+
+ if (reg_val == ndev->lnk_sta)
+ return 0;
+
+ ndev->lnk_sta = reg_val;
+
+ return 1;
+}
+
static u64 skx_db_ioread(void __iomem *mmio)
{
return ioread64(mmio);
@@ -2852,7 +2874,7 @@
};
static const struct intel_ntb_reg skx_reg = {
- .poll_link = xeon_poll_link,
+ .poll_link = skx_poll_link,
.link_is_up = xeon_link_is_up,
.db_ioread = skx_db_ioread,
.db_iowrite = skx_db_iowrite,
diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index f81aa4b..02ca45f 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -1802,7 +1802,7 @@
node = dev_to_node(&ndev->dev);
- free_queue = ffs(nt->qp_bitmap);
+ free_queue = ffs(nt->qp_bitmap_free);
if (!free_queue)
goto err;
@@ -2273,9 +2273,8 @@
static void __exit ntb_transport_exit(void)
{
- debugfs_remove_recursive(nt_debugfs_dir);
-
ntb_unregister_client(&ntb_transport_client);
bus_unregister(&ntb_transport_bus);
+ debugfs_remove_recursive(nt_debugfs_dir);
}
module_exit(ntb_transport_exit);
diff --git a/drivers/ntb/test/ntb_perf.c b/drivers/ntb/test/ntb_perf.c
index e75d4fd..434e1d4 100644
--- a/drivers/ntb/test/ntb_perf.c
+++ b/drivers/ntb/test/ntb_perf.c
@@ -265,6 +265,8 @@
if (dma_submit_error(cookie))
goto err_set_unmap;
+ dmaengine_unmap_put(unmap);
+
atomic_inc(&pctx->dma_sync);
dma_async_issue_pending(chan);
diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c
index 56efaf7..d2961ef 100644
--- a/drivers/pci/hotplug/pnv_php.c
+++ b/drivers/pci/hotplug/pnv_php.c
@@ -155,7 +155,7 @@
pnv_php_detach_device_nodes(dn);
of_node_put(dn);
- refcount = atomic_read(&dn->kobj.kref.refcount);
+ refcount = kref_read(&dn->kobj.kref);
if (refcount != 1)
pr_warn("Invalid refcount %d on <%s>\n",
refcount, of_node_full_name(dn));
diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c
index 7175293..2dd1c68 100644
--- a/drivers/pci/pcie/pme.c
+++ b/drivers/pci/pcie/pme.c
@@ -433,6 +433,17 @@
return 0;
}
+/**
+ * pcie_pme_remove - Prepare PCIe PME service device for removal.
+ * @srv - PCIe service device to remove.
+ */
+static void pcie_pme_remove(struct pcie_device *srv)
+{
+ pcie_pme_suspend(srv);
+ free_irq(srv->irq, srv);
+ kfree(get_service_data(srv));
+}
+
static struct pcie_port_service_driver pcie_pme_driver = {
.name = "pcie_pme",
.port_type = PCI_EXP_TYPE_ROOT_PORT,
@@ -441,6 +452,7 @@
.probe = pcie_pme_probe,
.suspend = pcie_pme_suspend,
.resume = pcie_pme_resume,
+ .remove = pcie_pme_remove,
};
/**
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
index 429d34c..e429095 100644
--- a/drivers/pci/slot.c
+++ b/drivers/pci/slot.c
@@ -345,7 +345,7 @@
void pci_destroy_slot(struct pci_slot *slot)
{
dev_dbg(&slot->bus->dev, "dev %02x, dec refcount to %d\n",
- slot->number, atomic_read(&slot->kobj.kref.refcount) - 1);
+ slot->number, kref_read(&slot->kobj.kref) - 1);
mutex_lock(&pci_slot_mutex);
kobject_put(&slot->kobj);
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 59aa8e3..49a5948 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -816,13 +816,6 @@
low level access for debug work and updating the firmware. Say
N unless you will be doing this on an Intel MID platform.
-config GPIO_INTEL_PMIC
- bool "Intel PMIC GPIO support"
- depends on INTEL_SCU_IPC && GPIOLIB
- ---help---
- Say Y here to support GPIO via the SCU IPC interface
- on Intel MID platforms.
-
config INTEL_MID_POWER_BUTTON
tristate "power button driver for Intel MID platforms"
depends on INTEL_SCU_IPC && INPUT
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index d4111f0..b2f52a7 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -50,7 +50,6 @@
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
obj-$(CONFIG_INTEL_IPS) += intel_ips.o
-obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o
obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o
obj-$(CONFIG_XO15_EBOOK) += xo15-ebook.o
obj-$(CONFIG_IBM_RTL) += ibm_rtl.o
diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c
deleted file mode 100644
index 91ae585..0000000
--- a/drivers/platform/x86/intel_pmic_gpio.c
+++ /dev/null
@@ -1,326 +0,0 @@
-/* Moorestown PMIC GPIO (access through IPC) driver
- * Copyright (c) 2008 - 2009, Intel Corporation.
- *
- * Author: Alek Du <alek.du@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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/* Supports:
- * Moorestown platform PMIC chip
- */
-
-#define pr_fmt(fmt) "%s: " fmt, __func__
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/stddef.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/gpio/driver.h>
-#include <asm/intel_scu_ipc.h>
-#include <linux/device.h>
-#include <linux/intel_pmic_gpio.h>
-#include <linux/platform_device.h>
-
-#define DRIVER_NAME "pmic_gpio"
-
-/* register offset that IPC driver should use
- * 8 GPIO + 8 GPOSW (6 controllable) + 8GPO
- */
-enum pmic_gpio_register {
- GPIO0 = 0xE0,
- GPIO7 = 0xE7,
- GPIOINT = 0xE8,
- GPOSWCTL0 = 0xEC,
- GPOSWCTL5 = 0xF1,
- GPO = 0xF4,
-};
-
-/* bits definition for GPIO & GPOSW */
-#define GPIO_DRV 0x01
-#define GPIO_DIR 0x02
-#define GPIO_DIN 0x04
-#define GPIO_DOU 0x08
-#define GPIO_INTCTL 0x30
-#define GPIO_DBC 0xc0
-
-#define GPOSW_DRV 0x01
-#define GPOSW_DOU 0x08
-#define GPOSW_RDRV 0x30
-
-#define GPIO_UPDATE_TYPE 0x80000000
-
-#define NUM_GPIO 24
-
-struct pmic_gpio {
- struct mutex buslock;
- struct gpio_chip chip;
- void *gpiointr;
- int irq;
- unsigned irq_base;
- unsigned int update_type;
- u32 trigger_type;
-};
-
-static void pmic_program_irqtype(int gpio, int type)
-{
- if (type & IRQ_TYPE_EDGE_RISING)
- intel_scu_ipc_update_register(GPIO0 + gpio, 0x20, 0x20);
- else
- intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x20);
-
- if (type & IRQ_TYPE_EDGE_FALLING)
- intel_scu_ipc_update_register(GPIO0 + gpio, 0x10, 0x10);
- else
- intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x10);
-};
-
-static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
-{
- if (offset >= 8) {
- pr_err("only pin 0-7 support input\n");
- return -1;/* we only have 8 GPIO can use as input */
- }
- return intel_scu_ipc_update_register(GPIO0 + offset,
- GPIO_DIR, GPIO_DIR);
-}
-
-static int pmic_gpio_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- int rc = 0;
-
- if (offset < 8)/* it is GPIO */
- rc = intel_scu_ipc_update_register(GPIO0 + offset,
- GPIO_DRV | (value ? GPIO_DOU : 0),
- GPIO_DRV | GPIO_DOU | GPIO_DIR);
- else if (offset < 16)/* it is GPOSW */
- rc = intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8,
- GPOSW_DRV | (value ? GPOSW_DOU : 0),
- GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV);
- else if (offset > 15 && offset < 24)/* it is GPO */
- rc = intel_scu_ipc_update_register(GPO,
- value ? 1 << (offset - 16) : 0,
- 1 << (offset - 16));
- else {
- pr_err("invalid PMIC GPIO pin %d!\n", offset);
- WARN_ON(1);
- }
-
- return rc;
-}
-
-static int pmic_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
- u8 r;
- int ret;
-
- /* we only have 8 GPIO pins we can use as input */
- if (offset >= 8)
- return -EOPNOTSUPP;
- ret = intel_scu_ipc_ioread8(GPIO0 + offset, &r);
- if (ret < 0)
- return ret;
- return r & GPIO_DIN;
-}
-
-static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
-{
- if (offset < 8)/* it is GPIO */
- intel_scu_ipc_update_register(GPIO0 + offset,
- GPIO_DRV | (value ? GPIO_DOU : 0),
- GPIO_DRV | GPIO_DOU);
- else if (offset < 16)/* it is GPOSW */
- intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8,
- GPOSW_DRV | (value ? GPOSW_DOU : 0),
- GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV);
- else if (offset > 15 && offset < 24) /* it is GPO */
- intel_scu_ipc_update_register(GPO,
- value ? 1 << (offset - 16) : 0,
- 1 << (offset - 16));
-}
-
-/*
- * This is called from genirq with pg->buslock locked and
- * irq_desc->lock held. We can not access the scu bus here, so we
- * store the change and update in the bus_sync_unlock() function below
- */
-static int pmic_irq_type(struct irq_data *data, unsigned type)
-{
- struct pmic_gpio *pg = irq_data_get_irq_chip_data(data);
- u32 gpio = data->irq - pg->irq_base;
-
- if (gpio >= pg->chip.ngpio)
- return -EINVAL;
-
- pg->trigger_type = type;
- pg->update_type = gpio | GPIO_UPDATE_TYPE;
- return 0;
-}
-
-static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
-{
- struct pmic_gpio *pg = gpiochip_get_data(chip);
-
- return pg->irq_base + offset;
-}
-
-static void pmic_bus_lock(struct irq_data *data)
-{
- struct pmic_gpio *pg = irq_data_get_irq_chip_data(data);
-
- mutex_lock(&pg->buslock);
-}
-
-static void pmic_bus_sync_unlock(struct irq_data *data)
-{
- struct pmic_gpio *pg = irq_data_get_irq_chip_data(data);
-
- if (pg->update_type) {
- unsigned int gpio = pg->update_type & ~GPIO_UPDATE_TYPE;
-
- pmic_program_irqtype(gpio, pg->trigger_type);
- pg->update_type = 0;
- }
- mutex_unlock(&pg->buslock);
-}
-
-/* the gpiointr register is read-clear, so just do nothing. */
-static void pmic_irq_unmask(struct irq_data *data) { }
-
-static void pmic_irq_mask(struct irq_data *data) { }
-
-static struct irq_chip pmic_irqchip = {
- .name = "PMIC-GPIO",
- .irq_mask = pmic_irq_mask,
- .irq_unmask = pmic_irq_unmask,
- .irq_set_type = pmic_irq_type,
- .irq_bus_lock = pmic_bus_lock,
- .irq_bus_sync_unlock = pmic_bus_sync_unlock,
-};
-
-static irqreturn_t pmic_irq_handler(int irq, void *data)
-{
- struct pmic_gpio *pg = data;
- u8 intsts = *((u8 *)pg->gpiointr + 4);
- int gpio;
- irqreturn_t ret = IRQ_NONE;
-
- for (gpio = 0; gpio < 8; gpio++) {
- if (intsts & (1 << gpio)) {
- pr_debug("pmic pin %d triggered\n", gpio);
- generic_handle_irq(pg->irq_base + gpio);
- ret = IRQ_HANDLED;
- }
- }
- return ret;
-}
-
-static int platform_pmic_gpio_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- int irq = platform_get_irq(pdev, 0);
- struct intel_pmic_gpio_platform_data *pdata = dev->platform_data;
-
- struct pmic_gpio *pg;
- int retval;
- int i;
-
- if (irq < 0) {
- dev_dbg(dev, "no IRQ line\n");
- return -EINVAL;
- }
-
- if (!pdata || !pdata->gpio_base || !pdata->irq_base) {
- dev_dbg(dev, "incorrect or missing platform data\n");
- return -EINVAL;
- }
-
- pg = kzalloc(sizeof(*pg), GFP_KERNEL);
- if (!pg)
- return -ENOMEM;
-
- dev_set_drvdata(dev, pg);
-
- pg->irq = irq;
- /* setting up SRAM mapping for GPIOINT register */
- pg->gpiointr = ioremap_nocache(pdata->gpiointr, 8);
- if (!pg->gpiointr) {
- pr_err("Can not map GPIOINT\n");
- retval = -EINVAL;
- goto err2;
- }
- pg->irq_base = pdata->irq_base;
- pg->chip.label = "intel_pmic";
- pg->chip.direction_input = pmic_gpio_direction_input;
- pg->chip.direction_output = pmic_gpio_direction_output;
- pg->chip.get = pmic_gpio_get;
- pg->chip.set = pmic_gpio_set;
- pg->chip.to_irq = pmic_gpio_to_irq;
- pg->chip.base = pdata->gpio_base;
- pg->chip.ngpio = NUM_GPIO;
- pg->chip.can_sleep = 1;
- pg->chip.parent = dev;
-
- mutex_init(&pg->buslock);
-
- pg->chip.parent = dev;
- retval = gpiochip_add_data(&pg->chip, pg);
- if (retval) {
- pr_err("Can not add pmic gpio chip\n");
- goto err;
- }
-
- retval = request_irq(pg->irq, pmic_irq_handler, 0, "pmic", pg);
- if (retval) {
- pr_warn("Interrupt request failed\n");
- goto fail_request_irq;
- }
-
- for (i = 0; i < 8; i++) {
- irq_set_chip_and_handler_name(i + pg->irq_base,
- &pmic_irqchip,
- handle_simple_irq,
- "demux");
- irq_set_chip_data(i + pg->irq_base, pg);
- }
- return 0;
-
-fail_request_irq:
- gpiochip_remove(&pg->chip);
-err:
- iounmap(pg->gpiointr);
-err2:
- kfree(pg);
- return retval;
-}
-
-/* at the same time, register a platform driver
- * this supports the sfi 0.81 fw */
-static struct platform_driver platform_pmic_gpio_driver = {
- .driver = {
- .name = DRIVER_NAME,
- },
- .probe = platform_pmic_gpio_probe,
-};
-
-static int __init platform_pmic_gpio_init(void)
-{
- return platform_driver_register(&platform_pmic_gpio_driver);
-}
-subsys_initcall(platform_pmic_gpio_init);
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index abeb772..b8caccc 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -32,7 +32,7 @@
config POWER_RESET_AT91_SAMA5D2_SHDWC
tristate "Atmel AT91 SAMA5D2-Compatible shutdown controller driver"
- depends on ARCH_AT91 || COMPILE_TEST
+ depends on ARCH_AT91
default SOC_SAMA5
help
This driver supports the alternate shutdown controller for some Atmel
diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c
index a85dd4d..c6c3bee 100644
--- a/drivers/power/reset/at91-poweroff.c
+++ b/drivers/power/reset/at91-poweroff.c
@@ -14,9 +14,12 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
+#include <soc/at91/at91sam9_ddrsdr.h>
+
#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */
#define AT91_SHDW_SHDW BIT(0) /* Shut Down command */
#define AT91_SHDW_KEY (0xa5 << 24) /* KEY Password */
@@ -50,6 +53,7 @@
static void __iomem *at91_shdwc_base;
static struct clk *sclk;
+static void __iomem *mpddrc_base;
static void __init at91_wakeup_status(void)
{
@@ -73,6 +77,29 @@
writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR);
}
+static void at91_lpddr_poweroff(void)
+{
+ asm volatile(
+ /* Align to cache lines */
+ ".balign 32\n\t"
+
+ /* Ensure AT91_SHDW_CR is in the TLB by reading it */
+ " ldr r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
+
+ /* Power down SDRAM0 */
+ " str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
+ /* Shutdown CPU */
+ " str %3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
+
+ " b .\n\t"
+ :
+ : "r" (mpddrc_base),
+ "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
+ "r" (at91_shdwc_base),
+ "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW)
+ : "r0");
+}
+
static int at91_poweroff_get_wakeup_mode(struct device_node *np)
{
const char *pm;
@@ -124,6 +151,8 @@
static int __init at91_poweroff_probe(struct platform_device *pdev)
{
struct resource *res;
+ struct device_node *np;
+ u32 ddr_type;
int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -150,12 +179,30 @@
pm_power_off = at91_poweroff;
+ np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc");
+ if (!np)
+ return 0;
+
+ mpddrc_base = of_iomap(np, 0);
+ of_node_put(np);
+
+ if (!mpddrc_base)
+ return 0;
+
+ ddr_type = readl(mpddrc_base + AT91_DDRSDRC_MDR) & AT91_DDRSDRC_MD;
+ if ((ddr_type == AT91_DDRSDRC_MD_LPDDR2) ||
+ (ddr_type == AT91_DDRSDRC_MD_LPDDR3))
+ pm_power_off = at91_lpddr_poweroff;
+ else
+ iounmap(mpddrc_base);
+
return 0;
}
static int __exit at91_poweroff_remove(struct platform_device *pdev)
{
- if (pm_power_off == at91_poweroff)
+ if (pm_power_off == at91_poweroff ||
+ pm_power_off == at91_lpddr_poweroff)
pm_power_off = NULL;
clk_disable_unprepare(sclk);
@@ -163,6 +210,11 @@
return 0;
}
+static const struct of_device_id at91_ramc_of_match[] = {
+ { .compatible = "atmel,sama5d3-ddramc", },
+ { /* sentinel */ }
+};
+
static const struct of_device_id at91_poweroff_of_match[] = {
{ .compatible = "atmel,at91sam9260-shdwc", },
{ .compatible = "atmel,at91sam9rl-shdwc", },
diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c
index 568580c..b99769f 100644
--- a/drivers/power/reset/at91-reset.c
+++ b/drivers/power/reset/at91-reset.c
@@ -134,6 +134,15 @@
return NOTIFY_DONE;
}
+static int samx7_restart(struct notifier_block *this, unsigned long mode,
+ void *cmd)
+{
+ writel(cpu_to_le32(AT91_RSTC_KEY | AT91_RSTC_PROCRST),
+ at91_rstc_base);
+
+ return NOTIFY_DONE;
+}
+
static void __init at91_reset_status(struct platform_device *pdev)
{
u32 reg = readl(at91_rstc_base + AT91_RSTC_SR);
@@ -173,6 +182,7 @@
{ .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart },
{ .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart },
{ .compatible = "atmel,sama5d3-rstc", .data = sama5d3_restart },
+ { .compatible = "atmel,samx7-rstc", .data = samx7_restart },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, at91_reset_of_match);
@@ -238,20 +248,12 @@
return 0;
}
-static const struct platform_device_id at91_reset_plat_match[] = {
- { "at91-sam9260-reset", (unsigned long)at91sam9260_restart },
- { "at91-sam9g45-reset", (unsigned long)at91sam9g45_restart },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(platform, at91_reset_plat_match);
-
static struct platform_driver at91_reset_driver = {
.remove = __exit_p(at91_reset_remove),
.driver = {
.name = "at91-reset",
.of_match_table = at91_reset_of_match,
},
- .id_table = at91_reset_plat_match,
};
module_platform_driver_probe(at91_reset_driver, at91_reset_probe);
diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c
index 8a5ac97..90b0b5a 100644
--- a/drivers/power/reset/at91-sama5d2_shdwc.c
+++ b/drivers/power/reset/at91-sama5d2_shdwc.c
@@ -22,9 +22,12 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
+#include <soc/at91/at91sam9_ddrsdr.h>
+
#define SLOW_CLOCK_FREQ 32768
#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */
@@ -75,6 +78,7 @@
*/
static struct shdwc *at91_shdwc;
static struct clk *sclk;
+static void __iomem *mpddrc_base;
static const unsigned long long sdwc_dbc_period[] = {
0, 3, 32, 512, 4096, 32768,
@@ -108,6 +112,29 @@
at91_shdwc->at91_shdwc_base + AT91_SHDW_CR);
}
+static void at91_lpddr_poweroff(void)
+{
+ asm volatile(
+ /* Align to cache lines */
+ ".balign 32\n\t"
+
+ /* Ensure AT91_SHDW_CR is in the TLB by reading it */
+ " ldr r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
+
+ /* Power down SDRAM0 */
+ " str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
+ /* Shutdown CPU */
+ " str %3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
+
+ " b .\n\t"
+ :
+ : "r" (mpddrc_base),
+ "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
+ "r" (at91_shdwc->at91_shdwc_base),
+ "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW)
+ : "r0");
+}
+
static u32 at91_shdwc_debouncer_value(struct platform_device *pdev,
u32 in_period_us)
{
@@ -212,6 +239,8 @@
{
struct resource *res;
const struct of_device_id *match;
+ struct device_node *np;
+ u32 ddr_type;
int ret;
if (!pdev->dev.of_node)
@@ -249,6 +278,23 @@
pm_power_off = at91_poweroff;
+ np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc");
+ if (!np)
+ return 0;
+
+ mpddrc_base = of_iomap(np, 0);
+ of_node_put(np);
+
+ if (!mpddrc_base)
+ return 0;
+
+ ddr_type = readl(mpddrc_base + AT91_DDRSDRC_MDR) & AT91_DDRSDRC_MD;
+ if ((ddr_type == AT91_DDRSDRC_MD_LPDDR2) ||
+ (ddr_type == AT91_DDRSDRC_MD_LPDDR3))
+ pm_power_off = at91_lpddr_poweroff;
+ else
+ iounmap(mpddrc_base);
+
return 0;
}
@@ -256,7 +302,8 @@
{
struct shdwc *shdw = platform_get_drvdata(pdev);
- if (pm_power_off == at91_poweroff)
+ if (pm_power_off == at91_poweroff ||
+ pm_power_off == at91_lpddr_poweroff)
pm_power_off = NULL;
/* Reset values to disable wake-up features */
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 76806a0..da54ac8 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -164,6 +164,12 @@
Say Y to include support for SBS battery driver for SBS-compliant
gas gauges.
+config CHARGER_SBS
+ tristate "SBS Compliant charger"
+ depends on I2C
+ help
+ Say Y to include support for SBS compilant battery chargers.
+
config BATTERY_BQ27XXX
tristate "BQ27xxx battery driver"
help
@@ -214,6 +220,18 @@
This driver can also be built as a module. If so, the module will be
called da9150-fg.
+config CHARGER_AXP20X
+ tristate "X-Powers AXP20X and AXP22X AC power supply driver"
+ depends on MFD_AXP20X
+ depends on AXP20X_ADC
+ depends on IIO
+ help
+ Say Y here to enable support for X-Powers AXP20X and AXP22X PMICs' AC
+ power supply.
+
+ This driver can also be built as a module. If so, the module will be
+ called axp20x_ac_power.
+
config AXP288_CHARGER
tristate "X-Powers AXP288 Charger"
depends on MFD_AXP20X && EXTCON_AXP288
@@ -292,13 +310,6 @@
This driver can be build as a module. If so, the module will be
called jz4740-battery.
-config BATTERY_INTEL_MID
- tristate "Battery driver for Intel MID platforms"
- depends on INTEL_SCU_IPC && SPI
- help
- Say Y here to enable the battery driver on Intel MID
- platforms.
-
config BATTERY_RX51
tristate "Nokia RX-51 (N900) battery driver"
depends on TWL4030_MADC
@@ -370,6 +381,16 @@
Say Y to enable support for the battery charger control sysfs and
platform data of MAX14577/77836 MUICs.
+config CHARGER_DETECTOR_MAX14656
+ tristate "Maxim MAX14656 USB charger detector"
+ depends on I2C
+ depends on OF
+ help
+ Say Y to enable support for the Maxim MAX14656 USB charger detector.
+ The device is compliant with the USB Battery Charging Specification
+ Revision 1.2 and can be found e.g. in Kindle 4/5th generation
+ readers and certain LG devices.
+
config CHARGER_MAX77693
tristate "Maxim MAX77693 battery charger driver"
depends on MFD_MAX77693
@@ -395,6 +416,7 @@
depends on MFD_SPMI_PMIC || COMPILE_TEST
depends on OF
depends on EXTCON
+ depends on REGULATOR
help
Say Y to include support for the Switch-Mode Battery Charger and
Boost (SMBB) hardware found in Qualcomm PM8941 PMICs. The charger
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index 36c599d..3789a2c 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -18,6 +18,7 @@
obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o
obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.o
+obj-$(CONFIG_CHARGER_AXP20X) += axp20x_ac_power.o
obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o
@@ -31,6 +32,7 @@
obj-$(CONFIG_BATTERY_IPAQ_MICRO) += ipaq_micro_battery.o
obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o
+obj-$(CONFIG_CHARGER_SBS) += sbs-charger.o
obj-$(CONFIG_BATTERY_BQ27XXX) += bq27xxx_battery.o
obj-$(CONFIG_BATTERY_BQ27XXX_I2C) += bq27xxx_battery_i2c.o
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
@@ -47,7 +49,6 @@
obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
-obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o
obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o
obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o pm2301_charger.o
obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
@@ -58,6 +59,7 @@
obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o
+obj-$(CONFIG_CHARGER_DETECTOR_MAX14656) += max14656_charger_detector.o
obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c
index 6ffdc18..f7a35eb 100644
--- a/drivers/power/supply/ab8500_btemp.c
+++ b/drivers/power/supply/ab8500_btemp.c
@@ -76,8 +76,8 @@
* @dev: Pointer to the structure device
* @node: List of AB8500 BTEMPs, hence prepared for reentrance
* @curr_source: What current source we use, in uA
- * @bat_temp: Dispatched battery temperature in degree Celcius
- * @prev_bat_temp Last measured battery temperature in degree Celcius
+ * @bat_temp: Dispatched battery temperature in degree Celsius
+ * @prev_bat_temp Last measured battery temperature in degree Celsius
* @parent: Pointer to the struct ab8500
* @gpadc: Pointer to the struct gpadc
* @fg: Pointer to the struct fg
@@ -123,10 +123,7 @@
*/
struct ab8500_btemp *ab8500_btemp_get(void)
{
- struct ab8500_btemp *btemp;
- btemp = list_first_entry(&ab8500_btemp_list, struct ab8500_btemp, node);
-
- return btemp;
+ return list_first_entry(&ab8500_btemp_list, struct ab8500_btemp, node);
}
EXPORT_SYMBOL(ab8500_btemp_get);
@@ -464,13 +461,13 @@
* @tbl_size: size of the resistance to temperature table
* @res: resistance to calculate the temperature from
*
- * This function returns the battery temperature in degrees Celcius
+ * This function returns the battery temperature in degrees Celsius
* based on the NTC resistance.
*/
static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
const struct abx500_res_to_temp *tbl, int tbl_size, int res)
{
- int i, temp;
+ int i;
/*
* Calculate the formula for the straight line
* Simple interpolation if we are within
@@ -488,9 +485,8 @@
i++;
}
- temp = tbl[i].temp + ((tbl[i + 1].temp - tbl[i].temp) *
+ return tbl[i].temp + ((tbl[i + 1].temp - tbl[i].temp) *
(res - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist);
- return temp;
}
/**
diff --git a/drivers/power/supply/axp20x_ac_power.c b/drivers/power/supply/axp20x_ac_power.c
new file mode 100644
index 0000000..38f4e87
--- /dev/null
+++ b/drivers/power/supply/axp20x_ac_power.c
@@ -0,0 +1,253 @@
+/*
+ * AXP20X and AXP22X PMICs' ACIN power supply driver
+ *
+ * Copyright (C) 2016 Free Electrons
+ * Quentin Schulz <quentin.schulz@free-electrons.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/device.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/iio/consumer.h>
+
+#define AXP20X_PWR_STATUS_ACIN_PRESENT BIT(7)
+#define AXP20X_PWR_STATUS_ACIN_AVAIL BIT(6)
+
+#define DRVNAME "axp20x-ac-power-supply"
+
+struct axp20x_ac_power {
+ struct regmap *regmap;
+ struct power_supply *supply;
+ struct iio_channel *acin_v;
+ struct iio_channel *acin_i;
+};
+
+static irqreturn_t axp20x_ac_power_irq(int irq, void *devid)
+{
+ struct axp20x_ac_power *power = devid;
+
+ power_supply_changed(power->supply);
+
+ return IRQ_HANDLED;
+}
+
+static int axp20x_ac_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct axp20x_ac_power *power = power_supply_get_drvdata(psy);
+ int ret, reg;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_HEALTH:
+ ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®);
+ if (ret)
+ return ret;
+
+ if (reg & AXP20X_PWR_STATUS_ACIN_PRESENT) {
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ return 0;
+ }
+
+ val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
+ return 0;
+
+ case POWER_SUPPLY_PROP_PRESENT:
+ ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®);
+ if (ret)
+ return ret;
+
+ val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_PRESENT);
+ return 0;
+
+ case POWER_SUPPLY_PROP_ONLINE:
+ ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®);
+ if (ret)
+ return ret;
+
+ val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_AVAIL);
+ return 0;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ ret = iio_read_channel_processed(power->acin_v, &val->intval);
+ if (ret)
+ return ret;
+
+ /* IIO framework gives mV but Power Supply framework gives uV */
+ val->intval *= 1000;
+
+ return 0;
+
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ ret = iio_read_channel_processed(power->acin_i, &val->intval);
+ if (ret)
+ return ret;
+
+ /* IIO framework gives mA but Power Supply framework gives uA */
+ val->intval *= 1000;
+
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static enum power_supply_property axp20x_ac_power_properties[] = {
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+static enum power_supply_property axp22x_ac_power_properties[] = {
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static const struct power_supply_desc axp20x_ac_power_desc = {
+ .name = "axp20x-ac",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .properties = axp20x_ac_power_properties,
+ .num_properties = ARRAY_SIZE(axp20x_ac_power_properties),
+ .get_property = axp20x_ac_power_get_property,
+};
+
+static const struct power_supply_desc axp22x_ac_power_desc = {
+ .name = "axp22x-ac",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .properties = axp22x_ac_power_properties,
+ .num_properties = ARRAY_SIZE(axp22x_ac_power_properties),
+ .get_property = axp20x_ac_power_get_property,
+};
+
+struct axp_data {
+ const struct power_supply_desc *power_desc;
+ bool acin_adc;
+};
+
+static const struct axp_data axp20x_data = {
+ .power_desc = &axp20x_ac_power_desc,
+ .acin_adc = true,
+};
+
+static const struct axp_data axp22x_data = {
+ .power_desc = &axp22x_ac_power_desc,
+ .acin_adc = false,
+};
+
+static int axp20x_ac_power_probe(struct platform_device *pdev)
+{
+ struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+ struct power_supply_config psy_cfg = {};
+ struct axp20x_ac_power *power;
+ struct axp_data *axp_data;
+ static const char * const irq_names[] = { "ACIN_PLUGIN", "ACIN_REMOVAL",
+ NULL };
+ int i, irq, ret;
+
+ if (!of_device_is_available(pdev->dev.of_node))
+ return -ENODEV;
+
+ if (!axp20x) {
+ dev_err(&pdev->dev, "Parent drvdata not set\n");
+ return -EINVAL;
+ }
+
+ power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL);
+ if (!power)
+ return -ENOMEM;
+
+ axp_data = (struct axp_data *)of_device_get_match_data(&pdev->dev);
+
+ if (axp_data->acin_adc) {
+ power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v");
+ if (IS_ERR(power->acin_v)) {
+ if (PTR_ERR(power->acin_v) == -ENODEV)
+ return -EPROBE_DEFER;
+ return PTR_ERR(power->acin_v);
+ }
+
+ power->acin_i = devm_iio_channel_get(&pdev->dev, "acin_i");
+ if (IS_ERR(power->acin_i)) {
+ if (PTR_ERR(power->acin_i) == -ENODEV)
+ return -EPROBE_DEFER;
+ return PTR_ERR(power->acin_i);
+ }
+ }
+
+ power->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+
+ platform_set_drvdata(pdev, power);
+
+ psy_cfg.of_node = pdev->dev.of_node;
+ psy_cfg.drv_data = power;
+
+ power->supply = devm_power_supply_register(&pdev->dev,
+ axp_data->power_desc,
+ &psy_cfg);
+ if (IS_ERR(power->supply))
+ return PTR_ERR(power->supply);
+
+ /* Request irqs after registering, as irqs may trigger immediately */
+ for (i = 0; irq_names[i]; i++) {
+ irq = platform_get_irq_byname(pdev, irq_names[i]);
+ if (irq < 0) {
+ dev_warn(&pdev->dev, "No IRQ for %s: %d\n",
+ irq_names[i], irq);
+ continue;
+ }
+ irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
+ ret = devm_request_any_context_irq(&pdev->dev, irq,
+ axp20x_ac_power_irq, 0,
+ DRVNAME, power);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n",
+ irq_names[i], ret);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id axp20x_ac_power_match[] = {
+ {
+ .compatible = "x-powers,axp202-ac-power-supply",
+ .data = (void *)&axp20x_data,
+ }, {
+ .compatible = "x-powers,axp221-ac-power-supply",
+ .data = (void *)&axp22x_data,
+ }, { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, axp20x_ac_power_match);
+
+static struct platform_driver axp20x_ac_power_driver = {
+ .probe = axp20x_ac_power_probe,
+ .driver = {
+ .name = DRVNAME,
+ .of_match_table = axp20x_ac_power_match,
+ },
+};
+
+module_platform_driver(axp20x_ac_power_driver);
+
+MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
+MODULE_DESCRIPTION("AXP20X and AXP22X PMICs' AC power supply driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
index 6af6feb..2397c48 100644
--- a/drivers/power/supply/axp20x_usb_power.c
+++ b/drivers/power/supply/axp20x_usb_power.c
@@ -17,10 +17,12 @@
#include <linux/mfd/axp20x.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+#include <linux/iio/consumer.h>
#define DRVNAME "axp20x-usb-power-supply"
@@ -30,6 +32,8 @@
#define AXP20X_USB_STATUS_VBUS_VALID BIT(2)
#define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000)
+#define AXP20X_VBUS_VHOLD_MASK GENMASK(5, 3)
+#define AXP20X_VBUS_VHOLD_OFFSET 3
#define AXP20X_VBUS_CLIMIT_MASK 3
#define AXP20X_VBUC_CLIMIT_900mA 0
#define AXP20X_VBUC_CLIMIT_500mA 1
@@ -45,6 +49,9 @@
struct device_node *np;
struct regmap *regmap;
struct power_supply *supply;
+ enum axp20x_variants axp20x_id;
+ struct iio_channel *vbus_v;
+ struct iio_channel *vbus_i;
};
static irqreturn_t axp20x_usb_power_irq(int irq, void *devid)
@@ -72,6 +79,20 @@
val->intval = AXP20X_VBUS_VHOLD_uV(v);
return 0;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ if (IS_ENABLED(CONFIG_AXP20X_ADC)) {
+ ret = iio_read_channel_processed(power->vbus_v,
+ &val->intval);
+ if (ret)
+ return ret;
+
+ /*
+ * IIO framework gives mV but Power Supply framework
+ * gives uV.
+ */
+ val->intval *= 1000;
+ return 0;
+ }
+
ret = axp20x_read_variable_width(power->regmap,
AXP20X_VBUS_V_ADC_H, 12);
if (ret < 0)
@@ -86,12 +107,10 @@
switch (v & AXP20X_VBUS_CLIMIT_MASK) {
case AXP20X_VBUC_CLIMIT_100mA:
- if (of_device_is_compatible(power->np,
- "x-powers,axp202-usb-power-supply")) {
- val->intval = 100000;
- } else {
+ if (power->axp20x_id == AXP221_ID)
val->intval = -1; /* No 100mA limit */
- }
+ else
+ val->intval = 100000;
break;
case AXP20X_VBUC_CLIMIT_500mA:
val->intval = 500000;
@@ -105,6 +124,20 @@
}
return 0;
case POWER_SUPPLY_PROP_CURRENT_NOW:
+ if (IS_ENABLED(CONFIG_AXP20X_ADC)) {
+ ret = iio_read_channel_processed(power->vbus_i,
+ &val->intval);
+ if (ret)
+ return ret;
+
+ /*
+ * IIO framework gives mA but Power Supply framework
+ * gives uA.
+ */
+ val->intval *= 1000;
+ return 0;
+ }
+
ret = axp20x_read_variable_width(power->regmap,
AXP20X_VBUS_I_ADC_H, 12);
if (ret < 0)
@@ -130,8 +163,7 @@
val->intval = POWER_SUPPLY_HEALTH_GOOD;
- if (of_device_is_compatible(power->np,
- "x-powers,axp202-usb-power-supply")) {
+ if (power->axp20x_id == AXP202_ID) {
ret = regmap_read(power->regmap,
AXP20X_USB_OTG_STATUS, &v);
if (ret)
@@ -155,6 +187,81 @@
return 0;
}
+static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power,
+ int intval)
+{
+ int val;
+
+ switch (intval) {
+ case 4000000:
+ case 4100000:
+ case 4200000:
+ case 4300000:
+ case 4400000:
+ case 4500000:
+ case 4600000:
+ case 4700000:
+ val = (intval - 4000000) / 100000;
+ return regmap_update_bits(power->regmap,
+ AXP20X_VBUS_IPSOUT_MGMT,
+ AXP20X_VBUS_VHOLD_MASK,
+ val << AXP20X_VBUS_VHOLD_OFFSET);
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static int axp20x_usb_power_set_current_max(struct axp20x_usb_power *power,
+ int intval)
+{
+ int val;
+
+ switch (intval) {
+ case 100000:
+ if (power->axp20x_id == AXP221_ID)
+ return -EINVAL;
+ case 500000:
+ case 900000:
+ val = (900000 - intval) / 400000;
+ return regmap_update_bits(power->regmap,
+ AXP20X_VBUS_IPSOUT_MGMT,
+ AXP20X_VBUS_CLIMIT_MASK, val);
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static int axp20x_usb_power_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ return axp20x_usb_power_set_voltage_min(power, val->intval);
+
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ return axp20x_usb_power_set_current_max(power, val->intval);
+
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static int axp20x_usb_power_prop_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN ||
+ psp == POWER_SUPPLY_PROP_CURRENT_MAX;
+}
+
static enum power_supply_property axp20x_usb_power_properties[] = {
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
@@ -178,7 +285,9 @@
.type = POWER_SUPPLY_TYPE_USB,
.properties = axp20x_usb_power_properties,
.num_properties = ARRAY_SIZE(axp20x_usb_power_properties),
+ .property_is_writeable = axp20x_usb_power_prop_writeable,
.get_property = axp20x_usb_power_get_property,
+ .set_property = axp20x_usb_power_set_property,
};
static const struct power_supply_desc axp22x_usb_power_desc = {
@@ -186,9 +295,41 @@
.type = POWER_SUPPLY_TYPE_USB,
.properties = axp22x_usb_power_properties,
.num_properties = ARRAY_SIZE(axp22x_usb_power_properties),
+ .property_is_writeable = axp20x_usb_power_prop_writeable,
.get_property = axp20x_usb_power_get_property,
+ .set_property = axp20x_usb_power_set_property,
};
+static int configure_iio_channels(struct platform_device *pdev,
+ struct axp20x_usb_power *power)
+{
+ power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v");
+ if (IS_ERR(power->vbus_v)) {
+ if (PTR_ERR(power->vbus_v) == -ENODEV)
+ return -EPROBE_DEFER;
+ return PTR_ERR(power->vbus_v);
+ }
+
+ power->vbus_i = devm_iio_channel_get(&pdev->dev, "vbus_i");
+ if (IS_ERR(power->vbus_i)) {
+ if (PTR_ERR(power->vbus_i) == -ENODEV)
+ return -EPROBE_DEFER;
+ return PTR_ERR(power->vbus_i);
+ }
+
+ return 0;
+}
+
+static int configure_adc_registers(struct axp20x_usb_power *power)
+{
+ /* Enable vbus voltage and current measurement */
+ return regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
+ AXP20X_ADC_EN1_VBUS_CURR |
+ AXP20X_ADC_EN1_VBUS_VOLT,
+ AXP20X_ADC_EN1_VBUS_CURR |
+ AXP20X_ADC_EN1_VBUS_VOLT);
+}
+
static int axp20x_usb_power_probe(struct platform_device *pdev)
{
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
@@ -214,11 +355,13 @@
if (!power)
return -ENOMEM;
+ power->axp20x_id = (enum axp20x_variants)of_device_get_match_data(
+ &pdev->dev);
+
power->np = pdev->dev.of_node;
power->regmap = axp20x->regmap;
- if (of_device_is_compatible(power->np,
- "x-powers,axp202-usb-power-supply")) {
+ if (power->axp20x_id == AXP202_ID) {
/* Enable vbus valid checking */
ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON,
AXP20X_VBUS_MON_VBUS_VALID,
@@ -226,17 +369,18 @@
if (ret)
return ret;
- /* Enable vbus voltage and current measurement */
- ret = regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
- AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT,
- AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT);
+ if (IS_ENABLED(CONFIG_AXP20X_ADC))
+ ret = configure_iio_channels(pdev, power);
+ else
+ ret = configure_adc_registers(power);
+
if (ret)
return ret;
usb_power_desc = &axp20x_usb_power_desc;
irq_names = axp20x_irq_names;
- } else if (of_device_is_compatible(power->np,
- "x-powers,axp221-usb-power-supply")) {
+ } else if (power->axp20x_id == AXP221_ID ||
+ power->axp20x_id == AXP223_ID) {
usb_power_desc = &axp22x_usb_power_desc;
irq_names = axp22x_irq_names;
} else {
@@ -273,9 +417,16 @@
}
static const struct of_device_id axp20x_usb_power_match[] = {
- { .compatible = "x-powers,axp202-usb-power-supply" },
- { .compatible = "x-powers,axp221-usb-power-supply" },
- { }
+ {
+ .compatible = "x-powers,axp202-usb-power-supply",
+ .data = (void *)AXP202_ID,
+ }, {
+ .compatible = "x-powers,axp221-usb-power-supply",
+ .data = (void *)AXP221_ID,
+ }, {
+ .compatible = "x-powers,axp223-usb-power-supply",
+ .data = (void *)AXP223_ID,
+ }, { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, axp20x_usb_power_match);
diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c
index 75b8e0c..6be2fe2 100644
--- a/drivers/power/supply/axp288_charger.c
+++ b/drivers/power/supply/axp288_charger.c
@@ -90,20 +90,6 @@
#define CHRG_VLTFC_0C 0xA5 /* 0 DegC */
#define CHRG_VHTFC_45C 0x1F /* 45 DegC */
-#define BAT_IRQ_CFG_CHRG_DONE (1 << 2)
-#define BAT_IRQ_CFG_CHRG_START (1 << 3)
-#define BAT_IRQ_CFG_BAT_SAFE_EXIT (1 << 4)
-#define BAT_IRQ_CFG_BAT_SAFE_ENTER (1 << 5)
-#define BAT_IRQ_CFG_BAT_DISCON (1 << 6)
-#define BAT_IRQ_CFG_BAT_CONN (1 << 7)
-#define BAT_IRQ_CFG_BAT_MASK 0xFC
-
-#define TEMP_IRQ_CFG_QCBTU (1 << 4)
-#define TEMP_IRQ_CFG_CBTU (1 << 5)
-#define TEMP_IRQ_CFG_QCBTO (1 << 6)
-#define TEMP_IRQ_CFG_CBTO (1 << 7)
-#define TEMP_IRQ_CFG_MASK 0xF0
-
#define FG_CNTL_OCV_ADJ_EN (1 << 3)
#define CV_4100MV 4100 /* 4100mV */
@@ -127,6 +113,10 @@
#define ILIM_3000MA 3000 /* 3000mA */
#define AXP288_EXTCON_DEV_NAME "axp288_extcon"
+#define USB_HOST_EXTCON_DEV_NAME "INT3496:00"
+
+static const unsigned int cable_ids[] =
+ { EXTCON_CHG_USB_SDP, EXTCON_CHG_USB_CDP, EXTCON_CHG_USB_DCP };
enum {
VBUS_OV_IRQ = 0,
@@ -143,7 +133,6 @@
struct axp288_chrg_info {
struct platform_device *pdev;
- struct axp20x_chrg_pdata *pdata;
struct regmap *regmap;
struct regmap_irq_chip_data *regmap_irqc;
int irq[CHRG_INTR_END];
@@ -163,20 +152,16 @@
struct extcon_dev *edev;
bool connected;
enum power_supply_type chg_type;
- struct notifier_block nb;
+ struct notifier_block nb[ARRAY_SIZE(cable_ids)];
struct work_struct work;
} cable;
- int health;
int inlmt;
int cc;
int cv;
int max_cc;
int max_cv;
- bool online;
- bool present;
- bool enable_charger;
- bool is_charger_enabled;
+ int is_charger_enabled;
};
static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc)
@@ -305,6 +290,9 @@
{
int ret;
+ if ((int)enable == info->is_charger_enabled)
+ return 0;
+
if (enable)
ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN);
@@ -430,8 +418,7 @@
ret = axp288_charger_is_present(info);
if (ret < 0)
goto psy_get_prop_fail;
- info->present = ret;
- val->intval = info->present;
+ val->intval = ret;
break;
case POWER_SUPPLY_PROP_ONLINE:
/* Check for OTG case first */
@@ -442,8 +429,7 @@
ret = axp288_charger_is_online(info);
if (ret < 0)
goto psy_get_prop_fail;
- info->online = ret;
- val->intval = info->online;
+ val->intval = ret;
break;
case POWER_SUPPLY_PROP_HEALTH:
val->intval = axp288_get_charger_health(info);
@@ -576,20 +562,20 @@
struct axp288_chrg_info *info =
container_of(work, struct axp288_chrg_info, cable.work);
int ret, current_limit;
- bool changed = false;
struct extcon_dev *edev = info->cable.edev;
bool old_connected = info->cable.connected;
+ enum power_supply_type old_chg_type = info->cable.chg_type;
/* Determine cable/charger type */
- if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_SDP) > 0) {
+ if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) {
dev_dbg(&info->pdev->dev, "USB SDP charger is connected");
info->cable.connected = true;
info->cable.chg_type = POWER_SUPPLY_TYPE_USB;
- } else if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_CDP) > 0) {
+ } else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) {
dev_dbg(&info->pdev->dev, "USB CDP charger is connected");
info->cable.connected = true;
info->cable.chg_type = POWER_SUPPLY_TYPE_USB_CDP;
- } else if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_DCP) > 0) {
+ } else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) {
dev_dbg(&info->pdev->dev, "USB DCP charger is connected");
info->cable.connected = true;
info->cable.chg_type = POWER_SUPPLY_TYPE_USB_DCP;
@@ -601,22 +587,15 @@
}
/* Cable status changed */
- if (old_connected != info->cable.connected)
- changed = true;
-
- if (!changed)
+ if (old_connected == info->cable.connected &&
+ old_chg_type == info->cable.chg_type)
return;
mutex_lock(&info->lock);
- if (info->is_charger_enabled && !info->cable.connected) {
- info->enable_charger = false;
- ret = axp288_charger_enable_charger(info, info->enable_charger);
- if (ret < 0)
- dev_err(&info->pdev->dev,
- "cannot disable charger (%d)", ret);
+ if (info->cable.connected) {
+ axp288_charger_enable_charger(info, false);
- } else if (!info->is_charger_enabled && info->cable.connected) {
switch (info->cable.chg_type) {
case POWER_SUPPLY_TYPE_USB:
current_limit = ILIM_500MA;
@@ -635,36 +614,49 @@
/* Set vbus current limit first, then enable charger */
ret = axp288_charger_set_vbus_inlmt(info, current_limit);
- if (ret < 0) {
+ if (ret == 0)
+ axp288_charger_enable_charger(info, true);
+ else
dev_err(&info->pdev->dev,
"error setting current limit (%d)", ret);
- } else {
- info->enable_charger = (current_limit > 0);
- ret = axp288_charger_enable_charger(info,
- info->enable_charger);
- if (ret < 0)
- dev_err(&info->pdev->dev,
- "cannot enable charger (%d)", ret);
- }
+ } else {
+ axp288_charger_enable_charger(info, false);
}
- if (changed)
- info->health = axp288_get_charger_health(info);
-
mutex_unlock(&info->lock);
- if (changed)
- power_supply_changed(info->psy_usb);
+ power_supply_changed(info->psy_usb);
}
-static int axp288_charger_handle_cable_evt(struct notifier_block *nb,
- unsigned long event, void *param)
+/*
+ * We need 3 copies of this, because there is no way to find out for which
+ * cable id we are being called from the passed in arguments; and we must
+ * have a separate nb for each extcon_register_notifier call.
+ */
+static int axp288_charger_handle_cable0_evt(struct notifier_block *nb,
+ unsigned long event, void *param)
{
struct axp288_chrg_info *info =
- container_of(nb, struct axp288_chrg_info, cable.nb);
-
+ container_of(nb, struct axp288_chrg_info, cable.nb[0]);
schedule_work(&info->cable.work);
+ return NOTIFY_OK;
+}
+static int axp288_charger_handle_cable1_evt(struct notifier_block *nb,
+ unsigned long event, void *param)
+{
+ struct axp288_chrg_info *info =
+ container_of(nb, struct axp288_chrg_info, cable.nb[1]);
+ schedule_work(&info->cable.work);
+ return NOTIFY_OK;
+}
+
+static int axp288_charger_handle_cable2_evt(struct notifier_block *nb,
+ unsigned long event, void *param)
+{
+ struct axp288_chrg_info *info =
+ container_of(nb, struct axp288_chrg_info, cable.nb[2]);
+ schedule_work(&info->cable.work);
return NOTIFY_OK;
}
@@ -672,7 +664,17 @@
{
struct axp288_chrg_info *info =
container_of(work, struct axp288_chrg_info, otg.work);
- int ret;
+ struct extcon_dev *edev = info->otg.cable;
+ int ret, usb_host = extcon_get_state(edev, EXTCON_USB_HOST);
+
+ dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n",
+ usb_host ? "attached" : "detached");
+
+ /*
+ * Set usb_id_short flag to avoid running charger detection logic
+ * in case usb host.
+ */
+ info->otg.id_short = usb_host;
/* Disable VBUS path before enabling the 5V boost */
ret = axp288_charger_vbus_path_select(info, !info->otg.id_short);
@@ -685,135 +687,109 @@
{
struct axp288_chrg_info *info =
container_of(nb, struct axp288_chrg_info, otg.id_nb);
- struct extcon_dev *edev = info->otg.cable;
- int usb_host = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
- dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n",
- usb_host ? "attached" : "detached");
-
- /*
- * Set usb_id_short flag to avoid running charger detection logic
- * in case usb host.
- */
- info->otg.id_short = usb_host;
schedule_work(&info->otg.work);
return NOTIFY_OK;
}
-static void charger_init_hw_regs(struct axp288_chrg_info *info)
+static int charger_init_hw_regs(struct axp288_chrg_info *info)
{
int ret, cc, cv;
unsigned int val;
/* Program temperature thresholds */
ret = regmap_write(info->regmap, AXP20X_V_LTF_CHRG, CHRG_VLTFC_0C);
- if (ret < 0)
- dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+ if (ret < 0) {
+ dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
AXP20X_V_LTF_CHRG, ret);
+ return ret;
+ }
ret = regmap_write(info->regmap, AXP20X_V_HTF_CHRG, CHRG_VHTFC_45C);
- if (ret < 0)
- dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+ if (ret < 0) {
+ dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
AXP20X_V_HTF_CHRG, ret);
+ return ret;
+ }
/* Do not turn-off charger o/p after charge cycle ends */
ret = regmap_update_bits(info->regmap,
AXP20X_CHRG_CTRL2,
- CNTL2_CHG_OUT_TURNON, 1);
- if (ret < 0)
- dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+ CNTL2_CHG_OUT_TURNON, CNTL2_CHG_OUT_TURNON);
+ if (ret < 0) {
+ dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
AXP20X_CHRG_CTRL2, ret);
-
- /* Enable interrupts */
- ret = regmap_update_bits(info->regmap,
- AXP20X_IRQ2_EN,
- BAT_IRQ_CFG_BAT_MASK, 1);
- if (ret < 0)
- dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
- AXP20X_IRQ2_EN, ret);
-
- ret = regmap_update_bits(info->regmap, AXP20X_IRQ3_EN,
- TEMP_IRQ_CFG_MASK, 1);
- if (ret < 0)
- dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
- AXP20X_IRQ3_EN, ret);
+ return ret;
+ }
/* Setup ending condition for charging to be 10% of I(chrg) */
ret = regmap_update_bits(info->regmap,
AXP20X_CHRG_CTRL1,
CHRG_CCCV_ITERM_20P, 0);
- if (ret < 0)
- dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+ if (ret < 0) {
+ dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
AXP20X_CHRG_CTRL1, ret);
+ return ret;
+ }
/* Disable OCV-SOC curve calibration */
ret = regmap_update_bits(info->regmap,
AXP20X_CC_CTRL,
FG_CNTL_OCV_ADJ_EN, 0);
- if (ret < 0)
- dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+ if (ret < 0) {
+ dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
AXP20X_CC_CTRL, ret);
-
- /* Init charging current and voltage */
- info->max_cc = info->pdata->max_cc;
- info->max_cv = info->pdata->max_cv;
+ return ret;
+ }
/* Read current charge voltage and current limit */
ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val);
if (ret < 0) {
- /* Assume default if cannot read */
- info->cc = info->pdata->def_cc;
- info->cv = info->pdata->def_cv;
- } else {
- /* Determine charge voltage */
- cv = (val & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS;
- switch (cv) {
- case CHRG_CCCV_CV_4100MV:
- info->cv = CV_4100MV;
- break;
- case CHRG_CCCV_CV_4150MV:
- info->cv = CV_4150MV;
- break;
- case CHRG_CCCV_CV_4200MV:
- info->cv = CV_4200MV;
- break;
- case CHRG_CCCV_CV_4350MV:
- info->cv = CV_4350MV;
- break;
- default:
- info->cv = INT_MAX;
- break;
- }
-
- /* Determine charge current limit */
- cc = (ret & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS;
- cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
- info->cc = cc;
-
- /* Program default charging voltage and current */
- cc = min(info->pdata->def_cc, info->max_cc);
- cv = min(info->pdata->def_cv, info->max_cv);
-
- ret = axp288_charger_set_cc(info, cc);
- if (ret < 0)
- dev_warn(&info->pdev->dev,
- "error(%d) in setting CC\n", ret);
-
- ret = axp288_charger_set_cv(info, cv);
- if (ret < 0)
- dev_warn(&info->pdev->dev,
- "error(%d) in setting CV\n", ret);
+ dev_err(&info->pdev->dev, "register(%x) read error(%d)\n",
+ AXP20X_CHRG_CTRL1, ret);
+ return ret;
}
+
+ /* Determine charge voltage */
+ cv = (val & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS;
+ switch (cv) {
+ case CHRG_CCCV_CV_4100MV:
+ info->cv = CV_4100MV;
+ break;
+ case CHRG_CCCV_CV_4150MV:
+ info->cv = CV_4150MV;
+ break;
+ case CHRG_CCCV_CV_4200MV:
+ info->cv = CV_4200MV;
+ break;
+ case CHRG_CCCV_CV_4350MV:
+ info->cv = CV_4350MV;
+ break;
+ }
+
+ /* Determine charge current limit */
+ cc = (ret & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS;
+ cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
+ info->cc = cc;
+
+ /*
+ * Do not allow the user to configure higher settings then those
+ * set by the firmware
+ */
+ info->max_cv = info->cv;
+ info->max_cc = info->cc;
+
+ return 0;
}
static int axp288_charger_probe(struct platform_device *pdev)
{
int ret, i, pirq;
struct axp288_chrg_info *info;
+ struct device *dev = &pdev->dev;
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
struct power_supply_config charger_cfg = {};
-
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
@@ -821,15 +797,8 @@
info->pdev = pdev;
info->regmap = axp20x->regmap;
info->regmap_irqc = axp20x->regmap_irqc;
- info->pdata = pdev->dev.platform_data;
-
- if (!info->pdata) {
- /* Try ACPI provided pdata via device properties */
- if (!device_property_present(&pdev->dev,
- "axp288_charger_data\n"))
- dev_err(&pdev->dev, "failed to get platform data\n");
- return -ENODEV;
- }
+ info->cable.chg_type = -1;
+ info->is_charger_enabled = -1;
info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
if (info->cable.edev == NULL) {
@@ -838,63 +807,55 @@
return -EPROBE_DEFER;
}
- /* Register for extcon notification */
- INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker);
- info->cable.nb.notifier_call = axp288_charger_handle_cable_evt;
- ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_SDP,
- &info->cable.nb);
- if (ret) {
- dev_err(&info->pdev->dev,
- "failed to register extcon notifier for SDP %d\n", ret);
- return ret;
- }
-
- ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_CDP,
- &info->cable.nb);
- if (ret) {
- dev_err(&info->pdev->dev,
- "failed to register extcon notifier for CDP %d\n", ret);
- extcon_unregister_notifier(info->cable.edev,
- EXTCON_CHG_USB_SDP, &info->cable.nb);
- return ret;
- }
-
- ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_DCP,
- &info->cable.nb);
- if (ret) {
- dev_err(&info->pdev->dev,
- "failed to register extcon notifier for DCP %d\n", ret);
- extcon_unregister_notifier(info->cable.edev,
- EXTCON_CHG_USB_SDP, &info->cable.nb);
- extcon_unregister_notifier(info->cable.edev,
- EXTCON_CHG_USB_CDP, &info->cable.nb);
- return ret;
+ info->otg.cable = extcon_get_extcon_dev(USB_HOST_EXTCON_DEV_NAME);
+ if (info->otg.cable == NULL) {
+ dev_dbg(dev, "EXTCON_USB_HOST is not ready, probe deferred\n");
+ return -EPROBE_DEFER;
}
platform_set_drvdata(pdev, info);
mutex_init(&info->lock);
+ ret = charger_init_hw_regs(info);
+ if (ret)
+ return ret;
+
/* Register with power supply class */
charger_cfg.drv_data = info;
- info->psy_usb = power_supply_register(&pdev->dev, &axp288_charger_desc,
- &charger_cfg);
+ info->psy_usb = devm_power_supply_register(dev, &axp288_charger_desc,
+ &charger_cfg);
if (IS_ERR(info->psy_usb)) {
- dev_err(&pdev->dev, "failed to register power supply charger\n");
ret = PTR_ERR(info->psy_usb);
- goto psy_reg_failed;
+ dev_err(dev, "failed to register power supply: %d\n", ret);
+ return ret;
}
+ /* Register for extcon notification */
+ INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker);
+ info->cable.nb[0].notifier_call = axp288_charger_handle_cable0_evt;
+ info->cable.nb[1].notifier_call = axp288_charger_handle_cable1_evt;
+ info->cable.nb[2].notifier_call = axp288_charger_handle_cable2_evt;
+ for (i = 0; i < ARRAY_SIZE(cable_ids); i++) {
+ ret = devm_extcon_register_notifier(dev, info->cable.edev,
+ cable_ids[i], &info->cable.nb[i]);
+ if (ret) {
+ dev_err(dev, "failed to register extcon notifier for %u: %d\n",
+ cable_ids[i], ret);
+ return ret;
+ }
+ }
+ schedule_work(&info->cable.work);
+
/* Register for OTG notification */
INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);
info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt;
- ret = extcon_register_notifier(info->otg.cable, EXTCON_USB_HOST,
- &info->otg.id_nb);
- if (ret)
- dev_warn(&pdev->dev, "failed to register otg notifier\n");
-
- if (info->otg.cable)
- info->otg.id_short = extcon_get_cable_state_(
- info->otg.cable, EXTCON_USB_HOST);
+ ret = devm_extcon_register_notifier(&pdev->dev, info->otg.cable,
+ EXTCON_USB_HOST, &info->otg.id_nb);
+ if (ret) {
+ dev_err(dev, "failed to register EXTCON_USB_HOST notifier\n");
+ return ret;
+ }
+ schedule_work(&info->otg.work);
/* Register charger interrupts */
for (i = 0; i < CHRG_INTR_END; i++) {
@@ -903,8 +864,7 @@
if (info->irq[i] < 0) {
dev_warn(&info->pdev->dev,
"failed to get virtual interrupt=%d\n", pirq);
- ret = info->irq[i];
- goto intr_reg_failed;
+ return info->irq[i];
}
ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i],
NULL, axp288_charger_irq_thread_handler,
@@ -912,51 +872,22 @@
if (ret) {
dev_err(&pdev->dev, "failed to request interrupt=%d\n",
info->irq[i]);
- goto intr_reg_failed;
+ return ret;
}
}
- charger_init_hw_regs(info);
-
- return 0;
-
-intr_reg_failed:
- if (info->otg.cable)
- extcon_unregister_notifier(info->otg.cable, EXTCON_USB_HOST,
- &info->otg.id_nb);
- power_supply_unregister(info->psy_usb);
-psy_reg_failed:
- extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_SDP,
- &info->cable.nb);
- extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_CDP,
- &info->cable.nb);
- extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_DCP,
- &info->cable.nb);
- return ret;
-}
-
-static int axp288_charger_remove(struct platform_device *pdev)
-{
- struct axp288_chrg_info *info = dev_get_drvdata(&pdev->dev);
-
- if (info->otg.cable)
- extcon_unregister_notifier(info->otg.cable, EXTCON_USB_HOST,
- &info->otg.id_nb);
-
- extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_SDP,
- &info->cable.nb);
- extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_CDP,
- &info->cable.nb);
- extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_DCP,
- &info->cable.nb);
- power_supply_unregister(info->psy_usb);
-
return 0;
}
+static const struct platform_device_id axp288_charger_id_table[] = {
+ { .name = "axp288_charger" },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, axp288_charger_id_table);
+
static struct platform_driver axp288_charger_driver = {
.probe = axp288_charger_probe,
- .remove = axp288_charger_remove,
+ .id_table = axp288_charger_id_table,
.driver = {
.name = "axp288_charger",
},
diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c
index 539eb41..a8dcabc 100644
--- a/drivers/power/supply/axp288_fuel_gauge.c
+++ b/drivers/power/supply/axp288_fuel_gauge.c
@@ -29,6 +29,7 @@
#include <linux/iio/consumer.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <asm/unaligned.h>
#define CHRG_STAT_BAT_SAFE_MODE (1 << 3)
#define CHRG_STAT_BAT_VALID (1 << 4)
@@ -49,23 +50,6 @@
#define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */
#define CHRG_CCCV_CHG_EN (1 << 7)
-#define CV_4100 4100 /* 4100mV */
-#define CV_4150 4150 /* 4150mV */
-#define CV_4200 4200 /* 4200mV */
-#define CV_4350 4350 /* 4350mV */
-
-#define TEMP_IRQ_CFG_QWBTU (1 << 0)
-#define TEMP_IRQ_CFG_WBTU (1 << 1)
-#define TEMP_IRQ_CFG_QWBTO (1 << 2)
-#define TEMP_IRQ_CFG_WBTO (1 << 3)
-#define TEMP_IRQ_CFG_MASK 0xf
-
-#define FG_IRQ_CFG_LOWBATT_WL2 (1 << 0)
-#define FG_IRQ_CFG_LOWBATT_WL1 (1 << 1)
-#define FG_IRQ_CFG_LOWBATT_MASK 0x3
-#define LOWBAT_IRQ_STAT_LOWBATT_WL2 (1 << 0)
-#define LOWBAT_IRQ_STAT_LOWBATT_WL1 (1 << 1)
-
#define FG_CNTL_OCV_ADJ_STAT (1 << 2)
#define FG_CNTL_OCV_ADJ_EN (1 << 3)
#define FG_CNTL_CAP_ADJ_STAT (1 << 4)
@@ -73,17 +57,15 @@
#define FG_CNTL_CC_EN (1 << 6)
#define FG_CNTL_GAUGE_EN (1 << 7)
+#define FG_15BIT_WORD_VALID (1 << 15)
+#define FG_15BIT_VAL_MASK 0x7fff
+
#define FG_REP_CAP_VALID (1 << 7)
#define FG_REP_CAP_VAL_MASK 0x7F
#define FG_DES_CAP1_VALID (1 << 7)
-#define FG_DES_CAP1_VAL_MASK 0x7F
-#define FG_DES_CAP0_VAL_MASK 0xFF
#define FG_DES_CAP_RES_LSB 1456 /* 1.456mAhr */
-#define FG_CC_MTR1_VALID (1 << 7)
-#define FG_CC_MTR1_VAL_MASK 0x7F
-#define FG_CC_MTR0_VAL_MASK 0xFF
#define FG_DES_CC_RES_LSB 1456 /* 1.456mAhr */
#define FG_OCV_CAP_VALID (1 << 7)
@@ -104,9 +86,7 @@
/* 1.1mV per LSB expressed in uV */
#define VOLTAGE_FROM_ADC(a) ((a * 11) / 10)
-/* properties converted to tenths of degrees, uV, uA, uW */
-#define PROP_TEMP(a) ((a) * 10)
-#define UNPROP_TEMP(a) ((a) / 10)
+/* properties converted to uV, uA */
#define PROP_VOLT(a) ((a) * 1000)
#define PROP_CURR(a) ((a) * 1000)
@@ -122,13 +102,13 @@
struct axp288_fg_info {
struct platform_device *pdev;
- struct axp20x_fg_pdata *pdata;
struct regmap *regmap;
struct regmap_irq_chip_data *regmap_irqc;
int irq[AXP288_FG_INTR_NUM];
struct power_supply *bat;
struct mutex lock;
int status;
+ int max_volt;
struct delayed_work status_monitor;
struct dentry *debug_file;
};
@@ -138,22 +118,14 @@
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
- POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_OCV,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN,
- POWER_SUPPLY_PROP_TEMP,
- POWER_SUPPLY_PROP_TEMP_MAX,
- POWER_SUPPLY_PROP_TEMP_MIN,
- POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
- POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_NOW,
- POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
- POWER_SUPPLY_PROP_MODEL_NAME,
};
static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg)
@@ -169,8 +141,10 @@
break;
}
- if (ret < 0)
+ if (ret < 0) {
dev_err(&info->pdev->dev, "axp288 reg read err:%d\n", ret);
+ return ret;
+ }
return val;
}
@@ -187,6 +161,44 @@
return ret;
}
+static int fuel_gauge_read_15bit_word(struct axp288_fg_info *info, int reg)
+{
+ unsigned char buf[2];
+ int ret;
+
+ ret = regmap_bulk_read(info->regmap, reg, buf, 2);
+ if (ret < 0) {
+ dev_err(&info->pdev->dev, "Error reading reg 0x%02x err: %d\n",
+ reg, ret);
+ return ret;
+ }
+
+ ret = get_unaligned_be16(buf);
+ if (!(ret & FG_15BIT_WORD_VALID)) {
+ dev_err(&info->pdev->dev, "Error reg 0x%02x contents not valid\n",
+ reg);
+ return -ENXIO;
+ }
+
+ return ret & FG_15BIT_VAL_MASK;
+}
+
+static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg)
+{
+ unsigned char buf[2];
+ int ret;
+
+ ret = regmap_bulk_read(info->regmap, reg, buf, 2);
+ if (ret < 0) {
+ dev_err(&info->pdev->dev, "Error reading reg 0x%02x err: %d\n",
+ reg, ret);
+ return ret;
+ }
+
+ /* 12-bit data values have upper 8 bits in buf[0], lower 4 in buf[1] */
+ return (buf[0] << 4) | ((buf[1] >> 4) & 0x0f);
+}
+
static int pmic_read_adc_val(const char *name, int *raw_val,
struct axp288_fg_info *info)
{
@@ -247,24 +259,15 @@
seq_printf(s, " FG_RDC0[%02x] : %02x\n",
AXP288_FG_RDC0_REG,
fuel_gauge_reg_readb(info, AXP288_FG_RDC0_REG));
- seq_printf(s, " FG_OCVH[%02x] : %02x\n",
+ seq_printf(s, " FG_OCV[%02x] : %04x\n",
AXP288_FG_OCVH_REG,
- fuel_gauge_reg_readb(info, AXP288_FG_OCVH_REG));
- seq_printf(s, " FG_OCVL[%02x] : %02x\n",
- AXP288_FG_OCVL_REG,
- fuel_gauge_reg_readb(info, AXP288_FG_OCVL_REG));
- seq_printf(s, "FG_DES_CAP1[%02x] : %02x\n",
+ fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG));
+ seq_printf(s, " FG_DES_CAP[%02x] : %04x\n",
AXP288_FG_DES_CAP1_REG,
- fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG));
- seq_printf(s, "FG_DES_CAP0[%02x] : %02x\n",
- AXP288_FG_DES_CAP0_REG,
- fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP0_REG));
- seq_printf(s, " FG_CC_MTR1[%02x] : %02x\n",
+ fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG));
+ seq_printf(s, " FG_CC_MTR[%02x] : %04x\n",
AXP288_FG_CC_MTR1_REG,
- fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR1_REG));
- seq_printf(s, " FG_CC_MTR0[%02x] : %02x\n",
- AXP288_FG_CC_MTR0_REG,
- fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR0_REG));
+ fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG));
seq_printf(s, " FG_OCV_CAP[%02x] : %02x\n",
AXP288_FG_OCV_CAP_REG,
fuel_gauge_reg_readb(info, AXP288_FG_OCV_CAP_REG));
@@ -417,143 +420,27 @@
return ret;
}
-static int temp_to_adc(struct axp288_fg_info *info, int tval)
-{
- int rntc = 0, i, ret, adc_val;
- int rmin, rmax, tmin, tmax;
- int tcsz = info->pdata->tcsz;
-
- /* get the Rntc resitance value for this temp */
- if (tval > info->pdata->thermistor_curve[0][1]) {
- rntc = info->pdata->thermistor_curve[0][0];
- } else if (tval <= info->pdata->thermistor_curve[tcsz-1][1]) {
- rntc = info->pdata->thermistor_curve[tcsz-1][0];
- } else {
- for (i = 1; i < tcsz; i++) {
- if (tval > info->pdata->thermistor_curve[i][1]) {
- rmin = info->pdata->thermistor_curve[i-1][0];
- rmax = info->pdata->thermistor_curve[i][0];
- tmin = info->pdata->thermistor_curve[i-1][1];
- tmax = info->pdata->thermistor_curve[i][1];
- rntc = rmin + ((rmax - rmin) *
- (tval - tmin) / (tmax - tmin));
- break;
- }
- }
- }
-
- /* we need the current to calculate the proper adc voltage */
- ret = fuel_gauge_reg_readb(info, AXP20X_ADC_RATE);
- if (ret < 0) {
- dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret);
- ret = 0x30;
- }
-
- /*
- * temperature is proportional to NTS thermistor resistance
- * ADC_RATE[5-4] determines current, 00=20uA,01=40uA,10=60uA,11=80uA
- * [12-bit ADC VAL] = R_NTC(Ω) * current / 800
- */
- adc_val = rntc * (20 + (20 * ((ret >> 4) & 0x3))) / 800;
-
- return adc_val;
-}
-
-static int adc_to_temp(struct axp288_fg_info *info, int adc_val)
-{
- int ret, r, i, tval = 0;
- int rmin, rmax, tmin, tmax;
- int tcsz = info->pdata->tcsz;
-
- ret = fuel_gauge_reg_readb(info, AXP20X_ADC_RATE);
- if (ret < 0) {
- dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret);
- ret = 0x30;
- }
-
- /*
- * temperature is proportional to NTS thermistor resistance
- * ADC_RATE[5-4] determines current, 00=20uA,01=40uA,10=60uA,11=80uA
- * R_NTC(Ω) = [12-bit ADC VAL] * 800 / current
- */
- r = adc_val * 800 / (20 + (20 * ((ret >> 4) & 0x3)));
-
- if (r < info->pdata->thermistor_curve[0][0]) {
- tval = info->pdata->thermistor_curve[0][1];
- } else if (r >= info->pdata->thermistor_curve[tcsz-1][0]) {
- tval = info->pdata->thermistor_curve[tcsz-1][1];
- } else {
- for (i = 1; i < tcsz; i++) {
- if (r < info->pdata->thermistor_curve[i][0]) {
- rmin = info->pdata->thermistor_curve[i-1][0];
- rmax = info->pdata->thermistor_curve[i][0];
- tmin = info->pdata->thermistor_curve[i-1][1];
- tmax = info->pdata->thermistor_curve[i][1];
- tval = tmin + ((tmax - tmin) *
- (r - rmin) / (rmax - rmin));
- break;
- }
- }
- }
-
- return tval;
-}
-
-static int fuel_gauge_get_btemp(struct axp288_fg_info *info, int *btemp)
-{
- int ret, raw_val = 0;
-
- ret = pmic_read_adc_val("axp288-batt-temp", &raw_val, info);
- if (ret < 0)
- goto temp_read_fail;
-
- *btemp = adc_to_temp(info, raw_val);
-
-temp_read_fail:
- return ret;
-}
-
static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv)
{
- int ret, value;
+ int ret;
- /* 12-bit data value, upper 8 in OCVH, lower 4 in OCVL */
- ret = fuel_gauge_reg_readb(info, AXP288_FG_OCVH_REG);
- if (ret < 0)
- goto vocv_read_fail;
- value = ret << 4;
+ ret = fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG);
+ if (ret >= 0)
+ *vocv = VOLTAGE_FROM_ADC(ret);
- ret = fuel_gauge_reg_readb(info, AXP288_FG_OCVL_REG);
- if (ret < 0)
- goto vocv_read_fail;
- value |= (ret & 0xf);
-
- *vocv = VOLTAGE_FROM_ADC(value);
-vocv_read_fail:
return ret;
}
static int fuel_gauge_battery_health(struct axp288_fg_info *info)
{
- int temp, vocv;
- int ret, health = POWER_SUPPLY_HEALTH_UNKNOWN;
-
- ret = fuel_gauge_get_btemp(info, &temp);
- if (ret < 0)
- goto health_read_fail;
+ int ret, vocv, health = POWER_SUPPLY_HEALTH_UNKNOWN;
ret = fuel_gauge_get_vocv(info, &vocv);
if (ret < 0)
goto health_read_fail;
- if (vocv > info->pdata->max_volt)
+ if (vocv > info->max_volt)
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
- else if (temp > info->pdata->max_temp)
- health = POWER_SUPPLY_HEALTH_OVERHEAT;
- else if (temp < info->pdata->min_temp)
- health = POWER_SUPPLY_HEALTH_COLD;
- else if (vocv < info->pdata->min_volt)
- health = POWER_SUPPLY_HEALTH_DEAD;
else
health = POWER_SUPPLY_HEALTH_GOOD;
@@ -561,28 +448,6 @@
return health;
}
-static int fuel_gauge_set_high_btemp_alert(struct axp288_fg_info *info)
-{
- int ret, adc_val;
-
- /* program temperature threshold as 1/16 ADC value */
- adc_val = temp_to_adc(info, info->pdata->max_temp);
- ret = fuel_gauge_reg_writeb(info, AXP20X_V_HTF_DISCHRG, adc_val >> 4);
-
- return ret;
-}
-
-static int fuel_gauge_set_low_btemp_alert(struct axp288_fg_info *info)
-{
- int ret, adc_val;
-
- /* program temperature threshold as 1/16 ADC value */
- adc_val = temp_to_adc(info, info->pdata->min_temp);
- ret = fuel_gauge_reg_writeb(info, AXP20X_V_LTF_DISCHRG, adc_val >> 4);
-
- return ret;
-}
-
static int fuel_gauge_get_property(struct power_supply *ps,
enum power_supply_property prop,
union power_supply_propval *val)
@@ -643,58 +508,25 @@
goto fuel_gauge_read_err;
val->intval = (ret & 0x0f);
break;
- case POWER_SUPPLY_PROP_TEMP:
- ret = fuel_gauge_get_btemp(info, &value);
- if (ret < 0)
- goto fuel_gauge_read_err;
- val->intval = PROP_TEMP(value);
- break;
- case POWER_SUPPLY_PROP_TEMP_MAX:
- case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
- val->intval = PROP_TEMP(info->pdata->max_temp);
- break;
- case POWER_SUPPLY_PROP_TEMP_MIN:
- case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
- val->intval = PROP_TEMP(info->pdata->min_temp);
- break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
- ret = fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR1_REG);
+ ret = fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG);
if (ret < 0)
goto fuel_gauge_read_err;
- value = (ret & FG_CC_MTR1_VAL_MASK) << 8;
- ret = fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR0_REG);
- if (ret < 0)
- goto fuel_gauge_read_err;
- value |= (ret & FG_CC_MTR0_VAL_MASK);
- val->intval = value * FG_DES_CAP_RES_LSB;
+ val->intval = ret * FG_DES_CAP_RES_LSB;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL:
- ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
+ ret = fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG);
if (ret < 0)
goto fuel_gauge_read_err;
- value = (ret & FG_DES_CAP1_VAL_MASK) << 8;
- ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP0_REG);
- if (ret < 0)
- goto fuel_gauge_read_err;
- value |= (ret & FG_DES_CAP0_VAL_MASK);
- val->intval = value * FG_DES_CAP_RES_LSB;
- break;
- case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
- val->intval = PROP_CURR(info->pdata->design_cap);
+ val->intval = ret * FG_DES_CAP_RES_LSB;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
- val->intval = PROP_VOLT(info->pdata->max_volt);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
- val->intval = PROP_VOLT(info->pdata->min_volt);
- break;
- case POWER_SUPPLY_PROP_MODEL_NAME:
- val->strval = info->pdata->battid;
+ val->intval = PROP_VOLT(info->max_volt);
break;
default:
mutex_unlock(&info->lock);
@@ -718,35 +550,6 @@
mutex_lock(&info->lock);
switch (prop) {
- case POWER_SUPPLY_PROP_STATUS:
- info->status = val->intval;
- break;
- case POWER_SUPPLY_PROP_TEMP_MIN:
- case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
- if ((val->intval < PD_DEF_MIN_TEMP) ||
- (val->intval > PD_DEF_MAX_TEMP)) {
- ret = -EINVAL;
- break;
- }
- info->pdata->min_temp = UNPROP_TEMP(val->intval);
- ret = fuel_gauge_set_low_btemp_alert(info);
- if (ret < 0)
- dev_err(&info->pdev->dev,
- "temp alert min set fail:%d\n", ret);
- break;
- case POWER_SUPPLY_PROP_TEMP_MAX:
- case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
- if ((val->intval < PD_DEF_MIN_TEMP) ||
- (val->intval > PD_DEF_MAX_TEMP)) {
- ret = -EINVAL;
- break;
- }
- info->pdata->max_temp = UNPROP_TEMP(val->intval);
- ret = fuel_gauge_set_high_btemp_alert(info);
- if (ret < 0)
- dev_err(&info->pdev->dev,
- "temp alert max set fail:%d\n", ret);
- break;
case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
if ((val->intval < 0) || (val->intval > 15)) {
ret = -EINVAL;
@@ -774,11 +577,6 @@
int ret;
switch (psp) {
- case POWER_SUPPLY_PROP_STATUS:
- case POWER_SUPPLY_PROP_TEMP_MIN:
- case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
- case POWER_SUPPLY_PROP_TEMP_MAX:
- case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
ret = 1;
break;
@@ -863,158 +661,6 @@
.external_power_changed = fuel_gauge_external_power_changed,
};
-static int fuel_gauge_set_lowbatt_thresholds(struct axp288_fg_info *info)
-{
- int ret;
- u8 reg_val;
-
- ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
- if (ret < 0) {
- dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret);
- return ret;
- }
- ret = (ret & FG_REP_CAP_VAL_MASK);
-
- if (ret > FG_LOW_CAP_WARN_THR)
- reg_val = FG_LOW_CAP_WARN_THR;
- else if (ret > FG_LOW_CAP_CRIT_THR)
- reg_val = FG_LOW_CAP_CRIT_THR;
- else
- reg_val = FG_LOW_CAP_SHDN_THR;
-
- reg_val |= FG_LOW_CAP_THR1_VAL;
- ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, reg_val);
- if (ret < 0)
- dev_err(&info->pdev->dev, "%s:write err:%d\n", __func__, ret);
-
- return ret;
-}
-
-static int fuel_gauge_program_vbatt_full(struct axp288_fg_info *info)
-{
- int ret;
- u8 val;
-
- ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1);
- if (ret < 0)
- goto fg_prog_ocv_fail;
- else
- val = (ret & ~CHRG_CCCV_CV_MASK);
-
- switch (info->pdata->max_volt) {
- case CV_4100:
- val |= (CHRG_CCCV_CV_4100MV << CHRG_CCCV_CV_BIT_POS);
- break;
- case CV_4150:
- val |= (CHRG_CCCV_CV_4150MV << CHRG_CCCV_CV_BIT_POS);
- break;
- case CV_4200:
- val |= (CHRG_CCCV_CV_4200MV << CHRG_CCCV_CV_BIT_POS);
- break;
- case CV_4350:
- val |= (CHRG_CCCV_CV_4350MV << CHRG_CCCV_CV_BIT_POS);
- break;
- default:
- val |= (CHRG_CCCV_CV_4200MV << CHRG_CCCV_CV_BIT_POS);
- break;
- }
-
- ret = fuel_gauge_reg_writeb(info, AXP20X_CHRG_CTRL1, val);
-fg_prog_ocv_fail:
- return ret;
-}
-
-static int fuel_gauge_program_design_cap(struct axp288_fg_info *info)
-{
- int ret;
-
- ret = fuel_gauge_reg_writeb(info,
- AXP288_FG_DES_CAP1_REG, info->pdata->cap1);
- if (ret < 0)
- goto fg_prog_descap_fail;
-
- ret = fuel_gauge_reg_writeb(info,
- AXP288_FG_DES_CAP0_REG, info->pdata->cap0);
-
-fg_prog_descap_fail:
- return ret;
-}
-
-static int fuel_gauge_program_ocv_curve(struct axp288_fg_info *info)
-{
- int ret = 0, i;
-
- for (i = 0; i < OCV_CURVE_SIZE; i++) {
- ret = fuel_gauge_reg_writeb(info,
- AXP288_FG_OCV_CURVE_REG + i, info->pdata->ocv_curve[i]);
- if (ret < 0)
- goto fg_prog_ocv_fail;
- }
-
-fg_prog_ocv_fail:
- return ret;
-}
-
-static int fuel_gauge_program_rdc_vals(struct axp288_fg_info *info)
-{
- int ret;
-
- ret = fuel_gauge_reg_writeb(info,
- AXP288_FG_RDC1_REG, info->pdata->rdc1);
- if (ret < 0)
- goto fg_prog_ocv_fail;
-
- ret = fuel_gauge_reg_writeb(info,
- AXP288_FG_RDC0_REG, info->pdata->rdc0);
-
-fg_prog_ocv_fail:
- return ret;
-}
-
-static void fuel_gauge_init_config_regs(struct axp288_fg_info *info)
-{
- int ret;
-
- /*
- * check if the config data is already
- * programmed and if so just return.
- */
-
- ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
- if (ret < 0) {
- dev_warn(&info->pdev->dev, "CAP1 reg read err!!\n");
- } else if (!(ret & FG_DES_CAP1_VALID)) {
- dev_info(&info->pdev->dev, "FG data needs to be initialized\n");
- } else {
- dev_info(&info->pdev->dev, "FG data is already initialized\n");
- return;
- }
-
- ret = fuel_gauge_program_vbatt_full(info);
- if (ret < 0)
- dev_err(&info->pdev->dev, "set vbatt full fail:%d\n", ret);
-
- ret = fuel_gauge_program_design_cap(info);
- if (ret < 0)
- dev_err(&info->pdev->dev, "set design cap fail:%d\n", ret);
-
- ret = fuel_gauge_program_rdc_vals(info);
- if (ret < 0)
- dev_err(&info->pdev->dev, "set rdc fail:%d\n", ret);
-
- ret = fuel_gauge_program_ocv_curve(info);
- if (ret < 0)
- dev_err(&info->pdev->dev, "set ocv curve fail:%d\n", ret);
-
- ret = fuel_gauge_set_lowbatt_thresholds(info);
- if (ret < 0)
- dev_err(&info->pdev->dev, "lowbatt thr set fail:%d\n", ret);
-
- ret = fuel_gauge_reg_writeb(info, AXP20X_CC_CTRL, 0xef);
- if (ret < 0)
- dev_err(&info->pdev->dev, "gauge cntl set fail:%d\n", ret);
-}
-
static void fuel_gauge_init_irq(struct axp288_fg_info *info)
{
int ret, i, pirq;
@@ -1052,29 +698,6 @@
}
}
-static void fuel_gauge_init_hw_regs(struct axp288_fg_info *info)
-{
- int ret;
- unsigned int val;
-
- ret = fuel_gauge_set_high_btemp_alert(info);
- if (ret < 0)
- dev_err(&info->pdev->dev, "high batt temp set fail:%d\n", ret);
-
- ret = fuel_gauge_set_low_btemp_alert(info);
- if (ret < 0)
- dev_err(&info->pdev->dev, "low batt temp set fail:%d\n", ret);
-
- /* enable interrupts */
- val = fuel_gauge_reg_readb(info, AXP20X_IRQ3_EN);
- val |= TEMP_IRQ_CFG_MASK;
- fuel_gauge_reg_writeb(info, AXP20X_IRQ3_EN, val);
-
- val = fuel_gauge_reg_readb(info, AXP20X_IRQ4_EN);
- val |= FG_IRQ_CFG_LOWBATT_MASK;
- val = fuel_gauge_reg_writeb(info, AXP20X_IRQ4_EN, val);
-}
-
static int axp288_fuel_gauge_probe(struct platform_device *pdev)
{
int ret = 0;
@@ -1090,15 +713,39 @@
info->regmap = axp20x->regmap;
info->regmap_irqc = axp20x->regmap_irqc;
info->status = POWER_SUPPLY_STATUS_UNKNOWN;
- info->pdata = pdev->dev.platform_data;
- if (!info->pdata)
- return -ENODEV;
platform_set_drvdata(pdev, info);
mutex_init(&info->lock);
INIT_DELAYED_WORK(&info->status_monitor, fuel_gauge_status_monitor);
+ ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
+ if (ret < 0)
+ return ret;
+
+ if (!(ret & FG_DES_CAP1_VALID)) {
+ dev_err(&pdev->dev, "axp288 not configured by firmware\n");
+ return -ENODEV;
+ }
+
+ ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1);
+ if (ret < 0)
+ return ret;
+ switch ((ret & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS) {
+ case CHRG_CCCV_CV_4100MV:
+ info->max_volt = 4100;
+ break;
+ case CHRG_CCCV_CV_4150MV:
+ info->max_volt = 4150;
+ break;
+ case CHRG_CCCV_CV_4200MV:
+ info->max_volt = 4200;
+ break;
+ case CHRG_CCCV_CV_4350MV:
+ info->max_volt = 4350;
+ break;
+ }
+
psy_cfg.drv_data = info;
info->bat = power_supply_register(&pdev->dev, &fuel_gauge_desc, &psy_cfg);
if (IS_ERR(info->bat)) {
@@ -1108,12 +755,10 @@
}
fuel_gauge_create_debugfs(info);
- fuel_gauge_init_config_regs(info);
fuel_gauge_init_irq(info);
- fuel_gauge_init_hw_regs(info);
schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES);
- return ret;
+ return 0;
}
static const struct platform_device_id axp288_fg_id_table[] = {
diff --git a/drivers/power/supply/bq2415x_charger.c b/drivers/power/supply/bq2415x_charger.c
index 73e2f0b..c4770a9 100644
--- a/drivers/power/supply/bq2415x_charger.c
+++ b/drivers/power/supply/bq2415x_charger.c
@@ -1569,6 +1569,11 @@
acpi_id =
acpi_match_device(client->dev.driver->acpi_match_table,
&client->dev);
+ if (!acpi_id) {
+ dev_err(&client->dev, "failed to match device name\n");
+ ret = -ENODEV;
+ goto error_1;
+ }
name = kasprintf(GFP_KERNEL, "%s-%d", acpi_id->id, num);
}
if (!name) {
diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c
index e958433..a4f0849 100644
--- a/drivers/power/supply/bq24190_charger.c
+++ b/drivers/power/supply/bq24190_charger.c
@@ -144,10 +144,7 @@
* so the first read after a fault returns the latched value and subsequent
* reads return the current value. In order to return the fault status
* to the user, have the interrupt handler save the reg's value and retrieve
- * it in the appropriate health/status routine. Each routine has its own
- * flag indicating whether it should use the value stored by the last run
- * of the interrupt handler or do an actual reg read. That way each routine
- * can report back whatever fault may have occured.
+ * it in the appropriate health/status routine.
*/
struct bq24190_dev_info {
struct i2c_client *client;
@@ -159,10 +156,6 @@
unsigned int gpio_int;
unsigned int irq;
struct mutex f_reg_lock;
- bool first_time;
- bool charger_health_valid;
- bool battery_health_valid;
- bool battery_status_valid;
u8 f_reg;
u8 ss_reg;
u8 watchdog;
@@ -199,7 +192,7 @@
4400000
};
-/* REG06[1:0] (TREG) in tenths of degrees Celcius */
+/* REG06[1:0] (TREG) in tenths of degrees Celsius */
static const int bq24190_ictrc_treg_values[] = {
600, 800, 1000, 1200
};
@@ -636,21 +629,11 @@
union power_supply_propval *val)
{
u8 v;
- int health, ret;
+ int health;
mutex_lock(&bdi->f_reg_lock);
-
- if (bdi->charger_health_valid) {
- v = bdi->f_reg;
- bdi->charger_health_valid = false;
- mutex_unlock(&bdi->f_reg_lock);
- } else {
- mutex_unlock(&bdi->f_reg_lock);
-
- ret = bq24190_read(bdi, BQ24190_REG_F, &v);
- if (ret < 0)
- return ret;
- }
+ v = bdi->f_reg;
+ mutex_unlock(&bdi->f_reg_lock);
if (v & BQ24190_REG_F_BOOST_FAULT_MASK) {
/*
@@ -937,18 +920,8 @@
int status, ret;
mutex_lock(&bdi->f_reg_lock);
-
- if (bdi->battery_status_valid) {
- chrg_fault = bdi->f_reg;
- bdi->battery_status_valid = false;
- mutex_unlock(&bdi->f_reg_lock);
- } else {
- mutex_unlock(&bdi->f_reg_lock);
-
- ret = bq24190_read(bdi, BQ24190_REG_F, &chrg_fault);
- if (ret < 0)
- return ret;
- }
+ chrg_fault = bdi->f_reg;
+ mutex_unlock(&bdi->f_reg_lock);
chrg_fault &= BQ24190_REG_F_CHRG_FAULT_MASK;
chrg_fault >>= BQ24190_REG_F_CHRG_FAULT_SHIFT;
@@ -996,21 +969,11 @@
union power_supply_propval *val)
{
u8 v;
- int health, ret;
+ int health;
mutex_lock(&bdi->f_reg_lock);
-
- if (bdi->battery_health_valid) {
- v = bdi->f_reg;
- bdi->battery_health_valid = false;
- mutex_unlock(&bdi->f_reg_lock);
- } else {
- mutex_unlock(&bdi->f_reg_lock);
-
- ret = bq24190_read(bdi, BQ24190_REG_F, &v);
- if (ret < 0)
- return ret;
- }
+ v = bdi->f_reg;
+ mutex_unlock(&bdi->f_reg_lock);
if (v & BQ24190_REG_F_BAT_FAULT_MASK) {
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
@@ -1197,9 +1160,12 @@
static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)
{
struct bq24190_dev_info *bdi = data;
- bool alert_userspace = false;
+ const u8 battery_mask_ss = BQ24190_REG_SS_CHRG_STAT_MASK;
+ const u8 battery_mask_f = BQ24190_REG_F_BAT_FAULT_MASK
+ | BQ24190_REG_F_NTC_FAULT_MASK;
+ bool alert_charger = false, alert_battery = false;
u8 ss_reg = 0, f_reg = 0;
- int ret;
+ int i, ret;
pm_runtime_get_sync(bdi->dev);
@@ -1209,6 +1175,32 @@
goto out;
}
+ i = 0;
+ do {
+ ret = bq24190_read(bdi, BQ24190_REG_F, &f_reg);
+ if (ret < 0) {
+ dev_err(bdi->dev, "Can't read F reg: %d\n", ret);
+ goto out;
+ }
+ } while (f_reg && ++i < 2);
+
+ if (f_reg != bdi->f_reg) {
+ dev_info(bdi->dev,
+ "Fault: boost %d, charge %d, battery %d, ntc %d\n",
+ !!(f_reg & BQ24190_REG_F_BOOST_FAULT_MASK),
+ !!(f_reg & BQ24190_REG_F_CHRG_FAULT_MASK),
+ !!(f_reg & BQ24190_REG_F_BAT_FAULT_MASK),
+ !!(f_reg & BQ24190_REG_F_NTC_FAULT_MASK));
+
+ mutex_lock(&bdi->f_reg_lock);
+ if ((bdi->f_reg & battery_mask_f) != (f_reg & battery_mask_f))
+ alert_battery = true;
+ if ((bdi->f_reg & ~battery_mask_f) != (f_reg & ~battery_mask_f))
+ alert_charger = true;
+ bdi->f_reg = f_reg;
+ mutex_unlock(&bdi->f_reg_lock);
+ }
+
if (ss_reg != bdi->ss_reg) {
/*
* The device is in host mode so when PG_STAT goes from 1->0
@@ -1225,47 +1217,17 @@
ret);
}
+ if ((bdi->ss_reg & battery_mask_ss) != (ss_reg & battery_mask_ss))
+ alert_battery = true;
+ if ((bdi->ss_reg & ~battery_mask_ss) != (ss_reg & ~battery_mask_ss))
+ alert_charger = true;
bdi->ss_reg = ss_reg;
- alert_userspace = true;
}
- mutex_lock(&bdi->f_reg_lock);
-
- ret = bq24190_read(bdi, BQ24190_REG_F, &f_reg);
- if (ret < 0) {
- mutex_unlock(&bdi->f_reg_lock);
- dev_err(bdi->dev, "Can't read F reg: %d\n", ret);
- goto out;
- }
-
- if (f_reg != bdi->f_reg) {
- bdi->f_reg = f_reg;
- bdi->charger_health_valid = true;
- bdi->battery_health_valid = true;
- bdi->battery_status_valid = true;
-
- alert_userspace = true;
- }
-
- mutex_unlock(&bdi->f_reg_lock);
-
- /*
- * Sometimes bq24190 gives a steady trickle of interrupts even
- * though the watchdog timer is turned off and neither the STATUS
- * nor FAULT registers have changed. Weed out these sprurious
- * interrupts so userspace isn't alerted for no reason.
- * In addition, the chip always generates an interrupt after
- * register reset so we should ignore that one (the very first
- * interrupt received).
- */
- if (alert_userspace) {
- if (!bdi->first_time) {
- power_supply_changed(bdi->charger);
- power_supply_changed(bdi->battery);
- } else {
- bdi->first_time = false;
- }
- }
+ if (alert_charger)
+ power_supply_changed(bdi->charger);
+ if (alert_battery)
+ power_supply_changed(bdi->battery);
out:
pm_runtime_put_sync(bdi->dev);
@@ -1300,6 +1262,10 @@
goto out;
ret = bq24190_set_mode_host(bdi);
+ if (ret < 0)
+ goto out;
+
+ ret = bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg);
out:
pm_runtime_put_sync(bdi->dev);
return ret;
@@ -1375,10 +1341,8 @@
bdi->model = id->driver_data;
strncpy(bdi->model_name, id->name, I2C_NAME_SIZE);
mutex_init(&bdi->f_reg_lock);
- bdi->first_time = true;
- bdi->charger_health_valid = false;
- bdi->battery_health_valid = false;
- bdi->battery_status_valid = false;
+ bdi->f_reg = 0;
+ bdi->ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK; /* impossible state */
i2c_set_clientdata(client, bdi);
@@ -1392,22 +1356,13 @@
return -EINVAL;
}
- ret = devm_request_threaded_irq(dev, bdi->irq, NULL,
- bq24190_irq_handler_thread,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
- "bq24190-charger", bdi);
- if (ret < 0) {
- dev_err(dev, "Can't set up irq handler\n");
- goto out1;
- }
-
pm_runtime_enable(dev);
pm_runtime_resume(dev);
ret = bq24190_hw_init(bdi);
if (ret < 0) {
dev_err(dev, "Hardware init failed\n");
- goto out2;
+ goto out1;
}
charger_cfg.drv_data = bdi;
@@ -1418,7 +1373,7 @@
if (IS_ERR(bdi->charger)) {
dev_err(dev, "Can't register charger\n");
ret = PTR_ERR(bdi->charger);
- goto out2;
+ goto out1;
}
battery_cfg.drv_data = bdi;
@@ -1427,27 +1382,39 @@
if (IS_ERR(bdi->battery)) {
dev_err(dev, "Can't register battery\n");
ret = PTR_ERR(bdi->battery);
- goto out3;
+ goto out2;
}
ret = bq24190_sysfs_create_group(bdi);
if (ret) {
dev_err(dev, "Can't create sysfs entries\n");
+ goto out3;
+ }
+
+ ret = devm_request_threaded_irq(dev, bdi->irq, NULL,
+ bq24190_irq_handler_thread,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "bq24190-charger", bdi);
+ if (ret < 0) {
+ dev_err(dev, "Can't set up irq handler\n");
goto out4;
}
return 0;
out4:
- power_supply_unregister(bdi->battery);
+ bq24190_sysfs_remove_group(bdi);
+
out3:
- power_supply_unregister(bdi->charger);
+ power_supply_unregister(bdi->battery);
+
out2:
- pm_runtime_disable(dev);
+ power_supply_unregister(bdi->charger);
+
out1:
+ pm_runtime_disable(dev);
if (bdi->gpio_int)
gpio_free(bdi->gpio_int);
-
return ret;
}
@@ -1488,12 +1455,13 @@
struct i2c_client *client = to_i2c_client(dev);
struct bq24190_dev_info *bdi = i2c_get_clientdata(client);
- bdi->charger_health_valid = false;
- bdi->battery_health_valid = false;
- bdi->battery_status_valid = false;
+ bdi->f_reg = 0;
+ bdi->ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK; /* impossible state */
pm_runtime_get_sync(bdi->dev);
bq24190_register_reset(bdi);
+ bq24190_set_mode_host(bdi);
+ bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg);
pm_runtime_put_sync(bdi->dev);
/* Things may have changed while suspended so alert upper layer */
diff --git a/drivers/power/supply/bq24735-charger.c b/drivers/power/supply/bq24735-charger.c
index eb7783b..eb01453 100644
--- a/drivers/power/supply/bq24735-charger.c
+++ b/drivers/power/supply/bq24735-charger.c
@@ -50,6 +50,8 @@
struct bq24735_platform *pdata;
struct mutex lock;
struct gpio_desc *status_gpio;
+ struct delayed_work poll;
+ u32 poll_interval;
bool charging;
};
@@ -105,26 +107,6 @@
return bq24735_write_word(client, reg, tmp);
}
-static inline int bq24735_enable_charging(struct bq24735 *charger)
-{
- if (charger->pdata->ext_control)
- return 0;
-
- return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
- BQ24735_CHG_OPT_CHARGE_DISABLE,
- ~BQ24735_CHG_OPT_CHARGE_DISABLE);
-}
-
-static inline int bq24735_disable_charging(struct bq24735 *charger)
-{
- if (charger->pdata->ext_control)
- return 0;
-
- return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
- BQ24735_CHG_OPT_CHARGE_DISABLE,
- BQ24735_CHG_OPT_CHARGE_DISABLE);
-}
-
static int bq24735_config_charger(struct bq24735 *charger)
{
struct bq24735_platform *pdata = charger->pdata;
@@ -176,6 +158,31 @@
return 0;
}
+static inline int bq24735_enable_charging(struct bq24735 *charger)
+{
+ int ret;
+
+ if (charger->pdata->ext_control)
+ return 0;
+
+ ret = bq24735_config_charger(charger);
+ if (ret)
+ return ret;
+
+ return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
+ BQ24735_CHG_OPT_CHARGE_DISABLE, 0);
+}
+
+static inline int bq24735_disable_charging(struct bq24735 *charger)
+{
+ if (charger->pdata->ext_control)
+ return 0;
+
+ return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
+ BQ24735_CHG_OPT_CHARGE_DISABLE,
+ BQ24735_CHG_OPT_CHARGE_DISABLE);
+}
+
static bool bq24735_charger_is_present(struct bq24735 *charger)
{
if (charger->status_gpio) {
@@ -185,7 +192,7 @@
ac = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
if (ac < 0) {
- dev_err(&charger->client->dev,
+ dev_dbg(&charger->client->dev,
"Failed to read charger options : %d\n",
ac);
return false;
@@ -210,11 +217,8 @@
return !(ret & BQ24735_CHG_OPT_CHARGE_DISABLE);
}
-static irqreturn_t bq24735_charger_isr(int irq, void *devid)
+static void bq24735_update(struct bq24735 *charger)
{
- struct power_supply *psy = devid;
- struct bq24735 *charger = to_bq24735(psy);
-
mutex_lock(&charger->lock);
if (charger->charging && bq24735_charger_is_present(charger))
@@ -224,11 +228,29 @@
mutex_unlock(&charger->lock);
- power_supply_changed(psy);
+ power_supply_changed(charger->charger);
+}
+
+static irqreturn_t bq24735_charger_isr(int irq, void *devid)
+{
+ struct power_supply *psy = devid;
+ struct bq24735 *charger = to_bq24735(psy);
+
+ bq24735_update(charger);
return IRQ_HANDLED;
}
+static void bq24735_poll(struct work_struct *work)
+{
+ struct bq24735 *charger = container_of(work, struct bq24735, poll.work);
+
+ bq24735_update(charger);
+
+ schedule_delayed_work(&charger->poll,
+ msecs_to_jiffies(charger->poll_interval));
+}
+
static int bq24735_charger_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@@ -276,7 +298,6 @@
mutex_unlock(&charger->lock);
if (ret)
return ret;
- bq24735_config_charger(charger);
break;
case POWER_SUPPLY_STATUS_DISCHARGING:
case POWER_SUPPLY_STATUS_NOT_CHARGING:
@@ -395,7 +416,7 @@
return ret;
}
- if (!charger->status_gpio || bq24735_charger_is_present(charger)) {
+ if (bq24735_charger_is_present(charger)) {
ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID);
if (ret < 0) {
dev_err(&client->dev, "Failed to read manufacturer id : %d\n",
@@ -416,16 +437,7 @@
"device id mismatch. 0x000b != 0x%04x\n", ret);
return -ENODEV;
}
- }
- ret = bq24735_config_charger(charger);
- if (ret < 0) {
- dev_err(&client->dev, "failed in configuring charger");
- return ret;
- }
-
- /* check for AC adapter presence */
- if (bq24735_charger_is_present(charger)) {
ret = bq24735_enable_charging(charger);
if (ret < 0) {
dev_err(&client->dev, "Failed to enable charging\n");
@@ -456,11 +468,32 @@
client->irq, ret);
return ret;
}
+ } else {
+ ret = device_property_read_u32(&client->dev, "poll-interval",
+ &charger->poll_interval);
+ if (ret)
+ return 0;
+ if (!charger->poll_interval)
+ return 0;
+
+ INIT_DELAYED_WORK(&charger->poll, bq24735_poll);
+ schedule_delayed_work(&charger->poll,
+ msecs_to_jiffies(charger->poll_interval));
}
return 0;
}
+static int bq24735_charger_remove(struct i2c_client *client)
+{
+ struct bq24735 *charger = i2c_get_clientdata(client);
+
+ if (charger->poll_interval)
+ cancel_delayed_work_sync(&charger->poll);
+
+ return 0;
+}
+
static const struct i2c_device_id bq24735_charger_id[] = {
{ "bq24735-charger", 0 },
{}
@@ -479,6 +512,7 @@
.of_match_table = bq24735_match_ids,
},
.probe = bq24735_charger_probe,
+ .remove = bq24735_charger_remove,
.id_table = bq24735_charger_id,
};
diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index 08c36b8..398801a 100644
--- a/drivers/power/supply/bq27xxx_battery.c
+++ b/drivers/power/supply/bq27xxx_battery.c
@@ -22,8 +22,14 @@
* http://www.ti.com/product/bq27010
* http://www.ti.com/product/bq27210
* http://www.ti.com/product/bq27500
+ * http://www.ti.com/product/bq27510-g1
+ * http://www.ti.com/product/bq27510-g2
* http://www.ti.com/product/bq27510-g3
* http://www.ti.com/product/bq27520-g4
+ * http://www.ti.com/product/bq27520-g1
+ * http://www.ti.com/product/bq27520-g2
+ * http://www.ti.com/product/bq27520-g3
+ * http://www.ti.com/product/bq27520-g4
* http://www.ti.com/product/bq27530-g1
* http://www.ti.com/product/bq27531-g1
* http://www.ti.com/product/bq27541-g1
@@ -145,7 +151,7 @@
[BQ27XXX_REG_DCAP] = 0x76,
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
},
- [BQ27500] = {
+ [BQ2750X] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = 0x28,
@@ -164,7 +170,7 @@
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
},
- [BQ27510] = {
+ [BQ2751X] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = 0x28,
@@ -183,6 +189,158 @@
[BQ27XXX_REG_DCAP] = 0x2e,
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
},
+ [BQ27500] = {
+ [BQ27XXX_REG_CTRL] = 0x00,
+ [BQ27XXX_REG_TEMP] = 0x06,
+ [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_VOLT] = 0x08,
+ [BQ27XXX_REG_AI] = 0x14,
+ [BQ27XXX_REG_FLAGS] = 0x0a,
+ [BQ27XXX_REG_TTE] = 0x16,
+ [BQ27XXX_REG_TTF] = 0x18,
+ [BQ27XXX_REG_TTES] = 0x1c,
+ [BQ27XXX_REG_TTECP] = 0x26,
+ [BQ27XXX_REG_NAC] = 0x0c,
+ [BQ27XXX_REG_FCC] = 0x12,
+ [BQ27XXX_REG_CYCT] = 0x2a,
+ [BQ27XXX_REG_AE] = 0x22,
+ [BQ27XXX_REG_SOC] = 0x2c,
+ [BQ27XXX_REG_DCAP] = 0x3c,
+ [BQ27XXX_REG_AP] = 0x24,
+ },
+ [BQ27510G1] = {
+ [BQ27XXX_REG_CTRL] = 0x00,
+ [BQ27XXX_REG_TEMP] = 0x06,
+ [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_VOLT] = 0x08,
+ [BQ27XXX_REG_AI] = 0x14,
+ [BQ27XXX_REG_FLAGS] = 0x0a,
+ [BQ27XXX_REG_TTE] = 0x16,
+ [BQ27XXX_REG_TTF] = 0x18,
+ [BQ27XXX_REG_TTES] = 0x1c,
+ [BQ27XXX_REG_TTECP] = 0x26,
+ [BQ27XXX_REG_NAC] = 0x0c,
+ [BQ27XXX_REG_FCC] = 0x12,
+ [BQ27XXX_REG_CYCT] = 0x2a,
+ [BQ27XXX_REG_AE] = 0x22,
+ [BQ27XXX_REG_SOC] = 0x2c,
+ [BQ27XXX_REG_DCAP] = 0x3c,
+ [BQ27XXX_REG_AP] = 0x24,
+ },
+ [BQ27510G2] = {
+ [BQ27XXX_REG_CTRL] = 0x00,
+ [BQ27XXX_REG_TEMP] = 0x06,
+ [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_VOLT] = 0x08,
+ [BQ27XXX_REG_AI] = 0x14,
+ [BQ27XXX_REG_FLAGS] = 0x0a,
+ [BQ27XXX_REG_TTE] = 0x16,
+ [BQ27XXX_REG_TTF] = 0x18,
+ [BQ27XXX_REG_TTES] = 0x1c,
+ [BQ27XXX_REG_TTECP] = 0x26,
+ [BQ27XXX_REG_NAC] = 0x0c,
+ [BQ27XXX_REG_FCC] = 0x12,
+ [BQ27XXX_REG_CYCT] = 0x2a,
+ [BQ27XXX_REG_AE] = 0x22,
+ [BQ27XXX_REG_SOC] = 0x2c,
+ [BQ27XXX_REG_DCAP] = 0x3c,
+ [BQ27XXX_REG_AP] = 0x24,
+ },
+ [BQ27510G3] = {
+ [BQ27XXX_REG_CTRL] = 0x00,
+ [BQ27XXX_REG_TEMP] = 0x06,
+ [BQ27XXX_REG_INT_TEMP] = 0x28,
+ [BQ27XXX_REG_VOLT] = 0x08,
+ [BQ27XXX_REG_AI] = 0x14,
+ [BQ27XXX_REG_FLAGS] = 0x0a,
+ [BQ27XXX_REG_TTE] = 0x16,
+ [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_TTES] = 0x1a,
+ [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_NAC] = 0x0c,
+ [BQ27XXX_REG_FCC] = 0x12,
+ [BQ27XXX_REG_CYCT] = 0x1e,
+ [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_SOC] = 0x20,
+ [BQ27XXX_REG_DCAP] = 0x2e,
+ [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+ },
+ [BQ27520G1] = {
+ [BQ27XXX_REG_CTRL] = 0x00,
+ [BQ27XXX_REG_TEMP] = 0x06,
+ [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_VOLT] = 0x08,
+ [BQ27XXX_REG_AI] = 0x14,
+ [BQ27XXX_REG_FLAGS] = 0x0a,
+ [BQ27XXX_REG_TTE] = 0x16,
+ [BQ27XXX_REG_TTF] = 0x18,
+ [BQ27XXX_REG_TTES] = 0x1c,
+ [BQ27XXX_REG_TTECP] = 0x26,
+ [BQ27XXX_REG_NAC] = 0x0c,
+ [BQ27XXX_REG_FCC] = 0x12,
+ [BQ27XXX_REG_CYCT] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_AE] = 0x22,
+ [BQ27XXX_REG_SOC] = 0x2c,
+ [BQ27XXX_REG_DCAP] = 0x3c,
+ [BQ27XXX_REG_AP] = 0x24,
+ },
+ [BQ27520G2] = {
+ [BQ27XXX_REG_CTRL] = 0x00,
+ [BQ27XXX_REG_TEMP] = 0x06,
+ [BQ27XXX_REG_INT_TEMP] = 0x36,
+ [BQ27XXX_REG_VOLT] = 0x08,
+ [BQ27XXX_REG_AI] = 0x14,
+ [BQ27XXX_REG_FLAGS] = 0x0a,
+ [BQ27XXX_REG_TTE] = 0x16,
+ [BQ27XXX_REG_TTF] = 0x18,
+ [BQ27XXX_REG_TTES] = 0x1c,
+ [BQ27XXX_REG_TTECP] = 0x26,
+ [BQ27XXX_REG_NAC] = 0x0c,
+ [BQ27XXX_REG_FCC] = 0x12,
+ [BQ27XXX_REG_CYCT] = 0x2a,
+ [BQ27XXX_REG_AE] = 0x22,
+ [BQ27XXX_REG_SOC] = 0x2c,
+ [BQ27XXX_REG_DCAP] = 0x3c,
+ [BQ27XXX_REG_AP] = 0x24,
+ },
+ [BQ27520G3] = {
+ [BQ27XXX_REG_CTRL] = 0x00,
+ [BQ27XXX_REG_TEMP] = 0x06,
+ [BQ27XXX_REG_INT_TEMP] = 0x36,
+ [BQ27XXX_REG_VOLT] = 0x08,
+ [BQ27XXX_REG_AI] = 0x14,
+ [BQ27XXX_REG_FLAGS] = 0x0a,
+ [BQ27XXX_REG_TTE] = 0x16,
+ [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_TTES] = 0x1c,
+ [BQ27XXX_REG_TTECP] = 0x26,
+ [BQ27XXX_REG_NAC] = 0x0c,
+ [BQ27XXX_REG_FCC] = 0x12,
+ [BQ27XXX_REG_CYCT] = 0x2a,
+ [BQ27XXX_REG_AE] = 0x22,
+ [BQ27XXX_REG_SOC] = 0x2c,
+ [BQ27XXX_REG_DCAP] = 0x3c,
+ [BQ27XXX_REG_AP] = 0x24,
+ },
+ [BQ27520G4] = {
+ [BQ27XXX_REG_CTRL] = 0x00,
+ [BQ27XXX_REG_TEMP] = 0x06,
+ [BQ27XXX_REG_INT_TEMP] = 0x28,
+ [BQ27XXX_REG_VOLT] = 0x08,
+ [BQ27XXX_REG_AI] = 0x14,
+ [BQ27XXX_REG_FLAGS] = 0x0a,
+ [BQ27XXX_REG_TTE] = 0x16,
+ [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_TTES] = 0x1c,
+ [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_NAC] = 0x0c,
+ [BQ27XXX_REG_FCC] = 0x12,
+ [BQ27XXX_REG_CYCT] = 0x1e,
+ [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_SOC] = 0x20,
+ [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+ },
[BQ27530] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
@@ -303,7 +461,7 @@
POWER_SUPPLY_PROP_MANUFACTURER,
};
-static enum power_supply_property bq27500_battery_props[] = {
+static enum power_supply_property bq2750x_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
@@ -321,7 +479,7 @@
POWER_SUPPLY_PROP_MANUFACTURER,
};
-static enum power_supply_property bq27510_battery_props[] = {
+static enum power_supply_property bq2751x_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
@@ -339,6 +497,165 @@
POWER_SUPPLY_PROP_MANUFACTURER,
};
+static enum power_supply_property bq27500_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+ POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_POWER_AVG,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27510g1_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+ POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_POWER_AVG,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27510g2_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+ POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_POWER_AVG,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27510g3_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27520g1_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+ POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_POWER_AVG,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27520g2_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+ POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_POWER_AVG,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27520g3_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_POWER_AVG,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27520g4_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
static enum power_supply_property bq27530_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
@@ -421,8 +738,16 @@
} bq27xxx_battery_props[] = {
BQ27XXX_PROP(BQ27000, bq27000_battery_props),
BQ27XXX_PROP(BQ27010, bq27010_battery_props),
+ BQ27XXX_PROP(BQ2750X, bq2750x_battery_props),
+ BQ27XXX_PROP(BQ2751X, bq2751x_battery_props),
BQ27XXX_PROP(BQ27500, bq27500_battery_props),
- BQ27XXX_PROP(BQ27510, bq27510_battery_props),
+ BQ27XXX_PROP(BQ27510G1, bq27510g1_battery_props),
+ BQ27XXX_PROP(BQ27510G2, bq27510g2_battery_props),
+ BQ27XXX_PROP(BQ27510G3, bq27510g3_battery_props),
+ BQ27XXX_PROP(BQ27520G1, bq27520g1_battery_props),
+ BQ27XXX_PROP(BQ27520G2, bq27520g2_battery_props),
+ BQ27XXX_PROP(BQ27520G3, bq27520g3_battery_props),
+ BQ27XXX_PROP(BQ27520G4, bq27520g4_battery_props),
BQ27XXX_PROP(BQ27530, bq27530_battery_props),
BQ27XXX_PROP(BQ27541, bq27541_battery_props),
BQ27XXX_PROP(BQ27545, bq27545_battery_props),
@@ -674,13 +999,26 @@
*/
static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags)
{
- if (di->chip == BQ27500 || di->chip == BQ27510 ||
- di->chip == BQ27541 || di->chip == BQ27545)
+ switch (di->chip) {
+ case BQ2750X:
+ case BQ2751X:
+ case BQ27500:
+ case BQ27510G1:
+ case BQ27510G2:
+ case BQ27510G3:
+ case BQ27520G1:
+ case BQ27520G2:
+ case BQ27520G3:
+ case BQ27520G4:
+ case BQ27541:
+ case BQ27545:
return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD);
- if (di->chip == BQ27530 || di->chip == BQ27421)
+ case BQ27530:
+ case BQ27421:
return flags & BQ27XXX_FLAG_OT;
-
- return false;
+ default:
+ return false;
+ }
}
/*
diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c
index 5c5c3a6..c68fbc3 100644
--- a/drivers/power/supply/bq27xxx_battery_i2c.c
+++ b/drivers/power/supply/bq27xxx_battery_i2c.c
@@ -148,9 +148,17 @@
static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
{ "bq27200", BQ27000 },
{ "bq27210", BQ27010 },
- { "bq27500", BQ27500 },
- { "bq27510", BQ27510 },
- { "bq27520", BQ27510 },
+ { "bq27500", BQ2750X },
+ { "bq27510", BQ2751X },
+ { "bq27520", BQ2751X },
+ { "bq27500-1", BQ27500 },
+ { "bq27510g1", BQ27510G1 },
+ { "bq27510g2", BQ27510G2 },
+ { "bq27510g3", BQ27510G3 },
+ { "bq27520g1", BQ27520G1 },
+ { "bq27520g2", BQ27520G2 },
+ { "bq27520g3", BQ27520G3 },
+ { "bq27520g4", BQ27520G4 },
{ "bq27530", BQ27530 },
{ "bq27531", BQ27530 },
{ "bq27541", BQ27541 },
@@ -173,6 +181,14 @@
{ .compatible = "ti,bq27500" },
{ .compatible = "ti,bq27510" },
{ .compatible = "ti,bq27520" },
+ { .compatible = "ti,bq27500-1" },
+ { .compatible = "ti,bq27510g1" },
+ { .compatible = "ti,bq27510g2" },
+ { .compatible = "ti,bq27510g3" },
+ { .compatible = "ti,bq27520g1" },
+ { .compatible = "ti,bq27520g2" },
+ { .compatible = "ti,bq27520g3" },
+ { .compatible = "ti,bq27520g4" },
{ .compatible = "ti,bq27530" },
{ .compatible = "ti,bq27531" },
{ .compatible = "ti,bq27541" },
diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c
index c5869b1..001731e 100644
--- a/drivers/power/supply/gpio-charger.c
+++ b/drivers/power/supply/gpio-charger.c
@@ -14,7 +14,7 @@
*/
#include <linux/device.h>
-#include <linux/gpio.h>
+#include <linux/gpio.h> /* For legacy platform data */
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
@@ -23,7 +23,7 @@
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/power/gpio-charger.h>
@@ -34,6 +34,8 @@
struct power_supply *charger;
struct power_supply_desc charger_desc;
+ struct gpio_desc *gpiod;
+ bool legacy_gpio_requested;
};
static irqreturn_t gpio_charger_irq(int irq, void *devid)
@@ -58,7 +60,8 @@
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
- val->intval = !!gpio_get_value_cansleep(pdata->gpio);
+ val->intval = gpiod_get_value_cansleep(gpio_charger->gpiod);
+ /* This xor is only ever used with legacy pdata GPIO */
val->intval ^= pdata->gpio_active_low;
break;
default:
@@ -78,7 +81,6 @@
struct device_node *np = dev->of_node;
struct gpio_charger_platform_data *pdata;
const char *chargetype;
- enum of_gpio_flags flags;
int ret;
if (!np)
@@ -89,16 +91,6 @@
return ERR_PTR(-ENOMEM);
pdata->name = np->name;
-
- pdata->gpio = of_get_gpio_flags(np, 0, &flags);
- if (pdata->gpio < 0) {
- if (pdata->gpio != -EPROBE_DEFER)
- dev_err(dev, "could not get charger gpio\n");
- return ERR_PTR(pdata->gpio);
- }
-
- pdata->gpio_active_low = !!(flags & OF_GPIO_ACTIVE_LOW);
-
pdata->type = POWER_SUPPLY_TYPE_UNKNOWN;
ret = of_property_read_string(np, "charger-type", &chargetype);
if (ret >= 0) {
@@ -144,11 +136,6 @@
}
}
- if (!gpio_is_valid(pdata->gpio)) {
- dev_err(&pdev->dev, "Invalid gpio pin\n");
- return -EINVAL;
- }
-
gpio_charger = devm_kzalloc(&pdev->dev, sizeof(*gpio_charger),
GFP_KERNEL);
if (!gpio_charger) {
@@ -156,6 +143,45 @@
return -ENOMEM;
}
+ /*
+ * This will fetch a GPIO descriptor from device tree, ACPI or
+ * boardfile descriptor tables. It's good to try this first.
+ */
+ gpio_charger->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_IN);
+
+ /*
+ * If this fails and we're not using device tree, try the
+ * legacy platform data method.
+ */
+ if (IS_ERR(gpio_charger->gpiod) && !pdev->dev.of_node) {
+ /* Non-DT: use legacy GPIO numbers */
+ if (!gpio_is_valid(pdata->gpio)) {
+ dev_err(&pdev->dev, "Invalid gpio pin in pdata\n");
+ return -EINVAL;
+ }
+ ret = gpio_request(pdata->gpio, dev_name(&pdev->dev));
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request gpio pin: %d\n",
+ ret);
+ return ret;
+ }
+ gpio_charger->legacy_gpio_requested = true;
+ ret = gpio_direction_input(pdata->gpio);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to set gpio to input: %d\n",
+ ret);
+ goto err_gpio_free;
+ }
+ /* Then convert this to gpiod for now */
+ gpio_charger->gpiod = gpio_to_desc(pdata->gpio);
+ } else if (IS_ERR(gpio_charger->gpiod)) {
+ /* Just try again if this happens */
+ if (PTR_ERR(gpio_charger->gpiod) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_err(&pdev->dev, "error getting GPIO descriptor\n");
+ return PTR_ERR(gpio_charger->gpiod);
+ }
+
charger_desc = &gpio_charger->charger_desc;
charger_desc->name = pdata->name ? pdata->name : "gpio-charger";
@@ -169,17 +195,6 @@
psy_cfg.of_node = pdev->dev.of_node;
psy_cfg.drv_data = gpio_charger;
- ret = gpio_request(pdata->gpio, dev_name(&pdev->dev));
- if (ret) {
- dev_err(&pdev->dev, "Failed to request gpio pin: %d\n", ret);
- goto err_free;
- }
- ret = gpio_direction_input(pdata->gpio);
- if (ret) {
- dev_err(&pdev->dev, "Failed to set gpio to input: %d\n", ret);
- goto err_gpio_free;
- }
-
gpio_charger->pdata = pdata;
gpio_charger->charger = power_supply_register(&pdev->dev,
@@ -191,7 +206,7 @@
goto err_gpio_free;
}
- irq = gpio_to_irq(pdata->gpio);
+ irq = gpiod_to_irq(gpio_charger->gpiod);
if (irq > 0) {
ret = request_any_context_irq(irq, gpio_charger_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
@@ -209,8 +224,8 @@
return 0;
err_gpio_free:
- gpio_free(pdata->gpio);
-err_free:
+ if (gpio_charger->legacy_gpio_requested)
+ gpio_free(pdata->gpio);
return ret;
}
@@ -223,7 +238,8 @@
power_supply_unregister(gpio_charger->charger);
- gpio_free(gpio_charger->pdata->gpio);
+ if (gpio_charger->legacy_gpio_requested)
+ gpio_free(gpio_charger->pdata->gpio);
return 0;
}
diff --git a/drivers/power/supply/intel_mid_battery.c b/drivers/power/supply/intel_mid_battery.c
deleted file mode 100644
index dc7feef..0000000
--- a/drivers/power/supply/intel_mid_battery.c
+++ /dev/null
@@ -1,795 +0,0 @@
-/*
- * intel_mid_battery.c - Intel MID PMIC Battery Driver
- *
- * Copyright (C) 2009 Intel Corporation
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * Author: Nithish Mahalingam <nithish.mahalingam@intel.com>
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/jiffies.h>
-#include <linux/param.h>
-#include <linux/device.h>
-#include <linux/spi/spi.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-
-#include <asm/intel_scu_ipc.h>
-
-#define DRIVER_NAME "pmic_battery"
-
-/*********************************************************************
- * Generic defines
- *********************************************************************/
-
-static int debug;
-module_param(debug, int, 0444);
-MODULE_PARM_DESC(debug, "Flag to enable PMIC Battery debug messages.");
-
-#define PMIC_BATT_DRV_INFO_UPDATED 1
-#define PMIC_BATT_PRESENT 1
-#define PMIC_BATT_NOT_PRESENT 0
-#define PMIC_USB_PRESENT PMIC_BATT_PRESENT
-#define PMIC_USB_NOT_PRESENT PMIC_BATT_NOT_PRESENT
-
-/* pmic battery register related */
-#define PMIC_BATT_CHR_SCHRGINT_ADDR 0xD2
-#define PMIC_BATT_CHR_SBATOVP_MASK (1 << 1)
-#define PMIC_BATT_CHR_STEMP_MASK (1 << 2)
-#define PMIC_BATT_CHR_SCOMP_MASK (1 << 3)
-#define PMIC_BATT_CHR_SUSBDET_MASK (1 << 4)
-#define PMIC_BATT_CHR_SBATDET_MASK (1 << 5)
-#define PMIC_BATT_CHR_SDCLMT_MASK (1 << 6)
-#define PMIC_BATT_CHR_SUSBOVP_MASK (1 << 7)
-#define PMIC_BATT_CHR_EXCPT_MASK 0x86
-
-#define PMIC_BATT_ADC_ACCCHRG_MASK (1 << 31)
-#define PMIC_BATT_ADC_ACCCHRGVAL_MASK 0x7FFFFFFF
-
-/* pmic ipc related */
-#define PMIC_BATT_CHR_IPC_FCHRG_SUBID 0x4
-#define PMIC_BATT_CHR_IPC_TCHRG_SUBID 0x6
-
-/* types of battery charging */
-enum batt_charge_type {
- BATT_USBOTG_500MA_CHARGE,
- BATT_USBOTG_TRICKLE_CHARGE,
-};
-
-/* valid battery events */
-enum batt_event {
- BATT_EVENT_BATOVP_EXCPT,
- BATT_EVENT_USBOVP_EXCPT,
- BATT_EVENT_TEMP_EXCPT,
- BATT_EVENT_DCLMT_EXCPT,
- BATT_EVENT_EXCPT
-};
-
-
-/*********************************************************************
- * Battery properties
- *********************************************************************/
-
-/*
- * pmic battery info
- */
-struct pmic_power_module_info {
- bool is_dev_info_updated;
- struct device *dev;
- /* pmic battery data */
- unsigned long update_time; /* jiffies when data read */
- unsigned int usb_is_present;
- unsigned int batt_is_present;
- unsigned int batt_health;
- unsigned int usb_health;
- unsigned int batt_status;
- unsigned int batt_charge_now; /* in mAS */
- unsigned int batt_prev_charge_full; /* in mAS */
- unsigned int batt_charge_rate; /* in units per second */
-
- struct power_supply *usb;
- struct power_supply *batt;
- int irq; /* GPE_ID or IRQ# */
- struct workqueue_struct *monitor_wqueue;
- struct delayed_work monitor_battery;
- struct work_struct handler;
-};
-
-static unsigned int delay_time = 2000; /* in ms */
-
-/*
- * pmic ac properties
- */
-static enum power_supply_property pmic_usb_props[] = {
- POWER_SUPPLY_PROP_PRESENT,
- POWER_SUPPLY_PROP_HEALTH,
-};
-
-/*
- * pmic battery properties
- */
-static enum power_supply_property pmic_battery_props[] = {
- POWER_SUPPLY_PROP_STATUS,
- POWER_SUPPLY_PROP_HEALTH,
- POWER_SUPPLY_PROP_PRESENT,
- POWER_SUPPLY_PROP_CHARGE_NOW,
- POWER_SUPPLY_PROP_CHARGE_FULL,
-};
-
-
-/*
- * Glue functions for talking to the IPC
- */
-
-struct battery_property {
- u32 capacity; /* Charger capacity */
- u8 crnt; /* Quick charge current value*/
- u8 volt; /* Fine adjustment of constant charge voltage */
- u8 prot; /* CHRGPROT register value */
- u8 prot2; /* CHRGPROT1 register value */
- u8 timer; /* Charging timer */
-};
-
-#define IPCMSG_BATTERY 0xEF
-
-/* Battery coulomb counter accumulator commands */
-#define IPC_CMD_CC_WR 0 /* Update coulomb counter value */
-#define IPC_CMD_CC_RD 1 /* Read coulomb counter value */
-#define IPC_CMD_BATTERY_PROPERTY 2 /* Read Battery property */
-
-/**
- * pmic_scu_ipc_battery_cc_read - read battery cc
- * @value: battery coulomb counter read
- *
- * Reads the battery couloumb counter value, returns 0 on success, or
- * an error code
- *
- * This function may sleep. Locking for SCU accesses is handled for
- * the caller.
- */
-static int pmic_scu_ipc_battery_cc_read(u32 *value)
-{
- return intel_scu_ipc_command(IPCMSG_BATTERY, IPC_CMD_CC_RD,
- NULL, 0, value, 1);
-}
-
-/**
- * pmic_scu_ipc_battery_property_get - fetch properties
- * @prop: battery properties
- *
- * Retrieve the battery properties from the power management
- *
- * This function may sleep. Locking for SCU accesses is handled for
- * the caller.
- */
-static int pmic_scu_ipc_battery_property_get(struct battery_property *prop)
-{
- u32 data[3];
- u8 *p = (u8 *)&data[1];
- int err = intel_scu_ipc_command(IPCMSG_BATTERY,
- IPC_CMD_BATTERY_PROPERTY, NULL, 0, data, 3);
-
- prop->capacity = data[0];
- prop->crnt = *p++;
- prop->volt = *p++;
- prop->prot = *p++;
- prop->prot2 = *p++;
- prop->timer = *p++;
-
- return err;
-}
-
-/**
- * pmic_scu_ipc_set_charger - set charger
- * @charger: charger to select
- *
- * Switch the charging mode for the SCU
- */
-
-static int pmic_scu_ipc_set_charger(int charger)
-{
- return intel_scu_ipc_simple_command(IPCMSG_BATTERY, charger);
-}
-
-/**
- * pmic_battery_log_event - log battery events
- * @event: battery event to be logged
- * Context: can sleep
- *
- * There are multiple battery events which may be of interest to users;
- * this battery function logs the different battery events onto the
- * kernel log messages.
- */
-static void pmic_battery_log_event(enum batt_event event)
-{
- printk(KERN_WARNING "pmic-battery: ");
- switch (event) {
- case BATT_EVENT_BATOVP_EXCPT:
- printk(KERN_CONT "battery overvoltage condition\n");
- break;
- case BATT_EVENT_USBOVP_EXCPT:
- printk(KERN_CONT "usb charger overvoltage condition\n");
- break;
- case BATT_EVENT_TEMP_EXCPT:
- printk(KERN_CONT "high battery temperature condition\n");
- break;
- case BATT_EVENT_DCLMT_EXCPT:
- printk(KERN_CONT "over battery charge current condition\n");
- break;
- default:
- printk(KERN_CONT "charger/battery exception %d\n", event);
- break;
- }
-}
-
-/**
- * pmic_battery_read_status - read battery status information
- * @pbi: device info structure to update the read information
- * Context: can sleep
- *
- * PMIC power source information need to be updated based on the data read
- * from the PMIC battery registers.
- *
- */
-static void pmic_battery_read_status(struct pmic_power_module_info *pbi)
-{
- unsigned int update_time_intrvl;
- unsigned int chrg_val;
- u32 ccval;
- u8 r8;
- struct battery_property batt_prop;
- int batt_present = 0;
- int usb_present = 0;
- int batt_exception = 0;
-
- /* make sure the last batt_status read happened delay_time before */
- if (pbi->update_time && time_before(jiffies, pbi->update_time +
- msecs_to_jiffies(delay_time)))
- return;
-
- update_time_intrvl = jiffies_to_msecs(jiffies - pbi->update_time);
- pbi->update_time = jiffies;
-
- /* read coulomb counter registers and schrgint register */
- if (pmic_scu_ipc_battery_cc_read(&ccval)) {
- dev_warn(pbi->dev, "%s(): ipc config cmd failed\n",
- __func__);
- return;
- }
-
- if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) {
- dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
- __func__);
- return;
- }
-
- /*
- * set pmic_power_module_info members based on pmic register values
- * read.
- */
-
- /* set batt_is_present */
- if (r8 & PMIC_BATT_CHR_SBATDET_MASK) {
- pbi->batt_is_present = PMIC_BATT_PRESENT;
- batt_present = 1;
- } else {
- pbi->batt_is_present = PMIC_BATT_NOT_PRESENT;
- pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
- pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN;
- }
-
- /* set batt_health */
- if (batt_present) {
- if (r8 & PMIC_BATT_CHR_SBATOVP_MASK) {
- pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
- pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
- pmic_battery_log_event(BATT_EVENT_BATOVP_EXCPT);
- batt_exception = 1;
- } else if (r8 & PMIC_BATT_CHR_STEMP_MASK) {
- pbi->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT;
- pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
- pmic_battery_log_event(BATT_EVENT_TEMP_EXCPT);
- batt_exception = 1;
- } else {
- pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD;
- if (r8 & PMIC_BATT_CHR_SDCLMT_MASK) {
- /* PMIC will change charging current automatically */
- pmic_battery_log_event(BATT_EVENT_DCLMT_EXCPT);
- }
- }
- }
-
- /* set usb_is_present */
- if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) {
- pbi->usb_is_present = PMIC_USB_PRESENT;
- usb_present = 1;
- } else {
- pbi->usb_is_present = PMIC_USB_NOT_PRESENT;
- pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
- }
-
- if (usb_present) {
- if (r8 & PMIC_BATT_CHR_SUSBOVP_MASK) {
- pbi->usb_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
- pmic_battery_log_event(BATT_EVENT_USBOVP_EXCPT);
- } else {
- pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD;
- }
- }
-
- chrg_val = ccval & PMIC_BATT_ADC_ACCCHRGVAL_MASK;
-
- /* set batt_prev_charge_full to battery capacity the first time */
- if (!pbi->is_dev_info_updated) {
- if (pmic_scu_ipc_battery_property_get(&batt_prop)) {
- dev_warn(pbi->dev, "%s(): ipc config cmd failed\n",
- __func__);
- return;
- }
- pbi->batt_prev_charge_full = batt_prop.capacity;
- }
-
- /* set batt_status */
- if (batt_present && !batt_exception) {
- if (r8 & PMIC_BATT_CHR_SCOMP_MASK) {
- pbi->batt_status = POWER_SUPPLY_STATUS_FULL;
- pbi->batt_prev_charge_full = chrg_val;
- } else if (ccval & PMIC_BATT_ADC_ACCCHRG_MASK) {
- pbi->batt_status = POWER_SUPPLY_STATUS_DISCHARGING;
- } else {
- pbi->batt_status = POWER_SUPPLY_STATUS_CHARGING;
- }
- }
-
- /* set batt_charge_rate */
- if (pbi->is_dev_info_updated && batt_present && !batt_exception) {
- if (pbi->batt_status == POWER_SUPPLY_STATUS_DISCHARGING) {
- if (pbi->batt_charge_now - chrg_val) {
- pbi->batt_charge_rate = ((pbi->batt_charge_now -
- chrg_val) * 1000 * 60) /
- update_time_intrvl;
- }
- } else if (pbi->batt_status == POWER_SUPPLY_STATUS_CHARGING) {
- if (chrg_val - pbi->batt_charge_now) {
- pbi->batt_charge_rate = ((chrg_val -
- pbi->batt_charge_now) * 1000 * 60) /
- update_time_intrvl;
- }
- } else
- pbi->batt_charge_rate = 0;
- } else {
- pbi->batt_charge_rate = -1;
- }
-
- /* batt_charge_now */
- if (batt_present && !batt_exception)
- pbi->batt_charge_now = chrg_val;
- else
- pbi->batt_charge_now = -1;
-
- pbi->is_dev_info_updated = PMIC_BATT_DRV_INFO_UPDATED;
-}
-
-/**
- * pmic_usb_get_property - usb power source get property
- * @psy: usb power supply context
- * @psp: usb power source property
- * @val: usb power source property value
- * Context: can sleep
- *
- * PMIC usb power source property needs to be provided to power_supply
- * subsytem for it to provide the information to users.
- */
-static int pmic_usb_get_property(struct power_supply *psy,
- enum power_supply_property psp,
- union power_supply_propval *val)
-{
- struct pmic_power_module_info *pbi = power_supply_get_drvdata(psy);
-
- /* update pmic_power_module_info members */
- pmic_battery_read_status(pbi);
-
- switch (psp) {
- case POWER_SUPPLY_PROP_PRESENT:
- val->intval = pbi->usb_is_present;
- break;
- case POWER_SUPPLY_PROP_HEALTH:
- val->intval = pbi->usb_health;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static inline unsigned long mAStouAh(unsigned long v)
-{
- /* seconds to hours, mA to µA */
- return (v * 1000) / 3600;
-}
-
-/**
- * pmic_battery_get_property - battery power source get property
- * @psy: battery power supply context
- * @psp: battery power source property
- * @val: battery power source property value
- * Context: can sleep
- *
- * PMIC battery power source property needs to be provided to power_supply
- * subsytem for it to provide the information to users.
- */
-static int pmic_battery_get_property(struct power_supply *psy,
- enum power_supply_property psp,
- union power_supply_propval *val)
-{
- struct pmic_power_module_info *pbi = power_supply_get_drvdata(psy);
-
- /* update pmic_power_module_info members */
- pmic_battery_read_status(pbi);
-
- switch (psp) {
- case POWER_SUPPLY_PROP_STATUS:
- val->intval = pbi->batt_status;
- break;
- case POWER_SUPPLY_PROP_HEALTH:
- val->intval = pbi->batt_health;
- break;
- case POWER_SUPPLY_PROP_PRESENT:
- val->intval = pbi->batt_is_present;
- break;
- case POWER_SUPPLY_PROP_CHARGE_NOW:
- val->intval = mAStouAh(pbi->batt_charge_now);
- break;
- case POWER_SUPPLY_PROP_CHARGE_FULL:
- val->intval = mAStouAh(pbi->batt_prev_charge_full);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/**
- * pmic_battery_monitor - monitor battery status
- * @work: work structure
- * Context: can sleep
- *
- * PMIC battery status needs to be monitored for any change
- * and information needs to be frequently updated.
- */
-static void pmic_battery_monitor(struct work_struct *work)
-{
- struct pmic_power_module_info *pbi = container_of(work,
- struct pmic_power_module_info, monitor_battery.work);
-
- /* update pmic_power_module_info members */
- pmic_battery_read_status(pbi);
- queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 10);
-}
-
-/**
- * pmic_battery_set_charger - set battery charger
- * @pbi: device info structure
- * @chrg: charge mode to set battery charger in
- * Context: can sleep
- *
- * PMIC battery charger needs to be enabled based on the usb charge
- * capabilities connected to the platform.
- */
-static int pmic_battery_set_charger(struct pmic_power_module_info *pbi,
- enum batt_charge_type chrg)
-{
- int retval;
-
- /* set usblmt bits and chrgcntl register bits appropriately */
- switch (chrg) {
- case BATT_USBOTG_500MA_CHARGE:
- retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_FCHRG_SUBID);
- break;
- case BATT_USBOTG_TRICKLE_CHARGE:
- retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_TCHRG_SUBID);
- break;
- default:
- dev_warn(pbi->dev, "%s(): out of range usb charger "
- "charge detected\n", __func__);
- return -EINVAL;
- }
-
- if (retval) {
- dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
- __func__);
- return retval;
- }
-
- return 0;
-}
-
-/**
- * pmic_battery_interrupt_handler - pmic battery interrupt handler
- * Context: interrupt context
- *
- * PMIC battery interrupt handler which will be called with either
- * battery full condition occurs or usb otg & battery connect
- * condition occurs.
- */
-static irqreturn_t pmic_battery_interrupt_handler(int id, void *dev)
-{
- struct pmic_power_module_info *pbi = dev;
-
- schedule_work(&pbi->handler);
-
- return IRQ_HANDLED;
-}
-
-/**
- * pmic_battery_handle_intrpt - pmic battery service interrupt
- * @work: work structure
- * Context: can sleep
- *
- * PMIC battery needs to either update the battery status as full
- * if it detects battery full condition caused the interrupt or needs
- * to enable battery charger if it detects usb and battery detect
- * caused the source of interrupt.
- */
-static void pmic_battery_handle_intrpt(struct work_struct *work)
-{
- struct pmic_power_module_info *pbi = container_of(work,
- struct pmic_power_module_info, handler);
- enum batt_charge_type chrg;
- u8 r8;
-
- if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) {
- dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
- __func__);
- return;
- }
- /* find the cause of the interrupt */
- if (r8 & PMIC_BATT_CHR_SBATDET_MASK) {
- pbi->batt_is_present = PMIC_BATT_PRESENT;
- } else {
- pbi->batt_is_present = PMIC_BATT_NOT_PRESENT;
- pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
- pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN;
- return;
- }
-
- if (r8 & PMIC_BATT_CHR_EXCPT_MASK) {
- pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
- pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
- pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
- pmic_battery_log_event(BATT_EVENT_EXCPT);
- return;
- } else {
- pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD;
- pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD;
- }
-
- if (r8 & PMIC_BATT_CHR_SCOMP_MASK) {
- u32 ccval;
- pbi->batt_status = POWER_SUPPLY_STATUS_FULL;
-
- if (pmic_scu_ipc_battery_cc_read(&ccval)) {
- dev_warn(pbi->dev, "%s(): ipc config cmd "
- "failed\n", __func__);
- return;
- }
- pbi->batt_prev_charge_full = ccval &
- PMIC_BATT_ADC_ACCCHRGVAL_MASK;
- return;
- }
-
- if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) {
- pbi->usb_is_present = PMIC_USB_PRESENT;
- } else {
- pbi->usb_is_present = PMIC_USB_NOT_PRESENT;
- pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
- return;
- }
-
- /* setup battery charging */
-
-#if 0
- /* check usb otg power capability and set charger accordingly */
- retval = langwell_udc_maxpower(&power);
- if (retval) {
- dev_warn(pbi->dev,
- "%s(): usb otg power query failed with error code %d\n",
- __func__, retval);
- return;
- }
-
- if (power >= 500)
- chrg = BATT_USBOTG_500MA_CHARGE;
- else
-#endif
- chrg = BATT_USBOTG_TRICKLE_CHARGE;
-
- /* enable battery charging */
- if (pmic_battery_set_charger(pbi, chrg)) {
- dev_warn(pbi->dev,
- "%s(): failed to set up battery charging\n", __func__);
- return;
- }
-
- dev_dbg(pbi->dev,
- "pmic-battery: %s() - setting up battery charger successful\n",
- __func__);
-}
-
-/*
- * Description of power supplies
- */
-static const struct power_supply_desc pmic_usb_desc = {
- .name = "pmic-usb",
- .type = POWER_SUPPLY_TYPE_USB,
- .properties = pmic_usb_props,
- .num_properties = ARRAY_SIZE(pmic_usb_props),
- .get_property = pmic_usb_get_property,
-};
-
-static const struct power_supply_desc pmic_batt_desc = {
- .name = "pmic-batt",
- .type = POWER_SUPPLY_TYPE_BATTERY,
- .properties = pmic_battery_props,
- .num_properties = ARRAY_SIZE(pmic_battery_props),
- .get_property = pmic_battery_get_property,
-};
-
-/**
- * pmic_battery_probe - pmic battery initialize
- * @irq: pmic battery device irq
- * @dev: pmic battery device structure
- * Context: can sleep
- *
- * PMIC battery initializes its internal data structue and other
- * infrastructure components for it to work as expected.
- */
-static int probe(int irq, struct device *dev)
-{
- int retval = 0;
- struct pmic_power_module_info *pbi;
- struct power_supply_config psy_cfg = {};
-
- dev_dbg(dev, "pmic-battery: found pmic battery device\n");
-
- pbi = kzalloc(sizeof(*pbi), GFP_KERNEL);
- if (!pbi) {
- dev_err(dev, "%s(): memory allocation failed\n",
- __func__);
- return -ENOMEM;
- }
-
- pbi->dev = dev;
- pbi->irq = irq;
- dev_set_drvdata(dev, pbi);
- psy_cfg.drv_data = pbi;
-
- /* initialize all required framework before enabling interrupts */
- INIT_WORK(&pbi->handler, pmic_battery_handle_intrpt);
- INIT_DELAYED_WORK(&pbi->monitor_battery, pmic_battery_monitor);
- pbi->monitor_wqueue = alloc_workqueue(dev_name(dev), WQ_MEM_RECLAIM, 0);
- if (!pbi->monitor_wqueue) {
- dev_err(dev, "%s(): wqueue init failed\n", __func__);
- retval = -ESRCH;
- goto wqueue_failed;
- }
-
- /* register interrupt */
- retval = request_irq(pbi->irq, pmic_battery_interrupt_handler,
- 0, DRIVER_NAME, pbi);
- if (retval) {
- dev_err(dev, "%s(): cannot get IRQ\n", __func__);
- goto requestirq_failed;
- }
-
- /* register pmic-batt with power supply subsystem */
- pbi->batt = power_supply_register(dev, &pmic_usb_desc, &psy_cfg);
- if (IS_ERR(pbi->batt)) {
- dev_err(dev,
- "%s(): failed to register pmic battery device with power supply subsystem\n",
- __func__);
- retval = PTR_ERR(pbi->batt);
- goto power_reg_failed;
- }
-
- dev_dbg(dev, "pmic-battery: %s() - pmic battery device "
- "registration with power supply subsystem successful\n",
- __func__);
-
- queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 1);
-
- /* register pmic-usb with power supply subsystem */
- pbi->usb = power_supply_register(dev, &pmic_batt_desc, &psy_cfg);
- if (IS_ERR(pbi->usb)) {
- dev_err(dev,
- "%s(): failed to register pmic usb device with power supply subsystem\n",
- __func__);
- retval = PTR_ERR(pbi->usb);
- goto power_reg_failed_1;
- }
-
- if (debug)
- printk(KERN_INFO "pmic-battery: %s() - pmic usb device "
- "registration with power supply subsystem successful\n",
- __func__);
-
- return retval;
-
-power_reg_failed_1:
- power_supply_unregister(pbi->batt);
-power_reg_failed:
- cancel_delayed_work_sync(&pbi->monitor_battery);
-requestirq_failed:
- destroy_workqueue(pbi->monitor_wqueue);
-wqueue_failed:
- kfree(pbi);
-
- return retval;
-}
-
-static int platform_pmic_battery_probe(struct platform_device *pdev)
-{
- return probe(pdev->id, &pdev->dev);
-}
-
-/**
- * pmic_battery_remove - pmic battery finalize
- * @dev: pmic battery device structure
- * Context: can sleep
- *
- * PMIC battery finalizes its internal data structue and other
- * infrastructure components that it initialized in
- * pmic_battery_probe.
- */
-
-static int platform_pmic_battery_remove(struct platform_device *pdev)
-{
- struct pmic_power_module_info *pbi = platform_get_drvdata(pdev);
-
- free_irq(pbi->irq, pbi);
- cancel_delayed_work_sync(&pbi->monitor_battery);
- destroy_workqueue(pbi->monitor_wqueue);
-
- power_supply_unregister(pbi->usb);
- power_supply_unregister(pbi->batt);
-
- cancel_work_sync(&pbi->handler);
- kfree(pbi);
- return 0;
-}
-
-static struct platform_driver platform_pmic_battery_driver = {
- .driver = {
- .name = DRIVER_NAME,
- },
- .probe = platform_pmic_battery_probe,
- .remove = platform_pmic_battery_remove,
-};
-
-module_platform_driver(platform_pmic_battery_driver);
-
-MODULE_AUTHOR("Nithish Mahalingam <nithish.mahalingam@intel.com>");
-MODULE_DESCRIPTION("Intel Moorestown PMIC Battery Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/max14656_charger_detector.c b/drivers/power/supply/max14656_charger_detector.c
new file mode 100644
index 0000000..b91b1d2
--- /dev/null
+++ b/drivers/power/supply/max14656_charger_detector.c
@@ -0,0 +1,327 @@
+/*
+ * Maxim MAX14656 / AL32 USB Charger Detector driver
+ *
+ * Copyright (C) 2014 LG Electronics, Inc
+ * Copyright (C) 2016 Alexander Kurz <akurz@blala.de>
+ *
+ * Components from Maxim AL32 Charger detection Driver for MX50 Yoshi Board
+ * Copyright (C) Amazon Technologies Inc. All rights reserved.
+ * Manish Lachwani (lachwani@lab126.com)
+ *
+ * This package 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <linux/workqueue.h>
+#include <linux/power_supply.h>
+
+#define MAX14656_MANUFACTURER "Maxim Integrated"
+#define MAX14656_NAME "max14656"
+
+#define MAX14656_DEVICE_ID 0x00
+#define MAX14656_INTERRUPT_1 0x01
+#define MAX14656_INTERRUPT_2 0x02
+#define MAX14656_STATUS_1 0x03
+#define MAX14656_STATUS_2 0x04
+#define MAX14656_INTMASK_1 0x05
+#define MAX14656_INTMASK_2 0x06
+#define MAX14656_CONTROL_1 0x07
+#define MAX14656_CONTROL_2 0x08
+#define MAX14656_CONTROL_3 0x09
+
+#define DEVICE_VENDOR_MASK 0xf0
+#define DEVICE_REV_MASK 0x0f
+#define INT_EN_REG_MASK BIT(4)
+#define CHG_TYPE_INT_MASK BIT(0)
+#define STATUS1_VB_VALID_MASK BIT(4)
+#define STATUS1_CHG_TYPE_MASK 0xf
+#define INT1_DCD_TIMEOUT_MASK BIT(7)
+#define CONTROL1_DEFAULT 0x0d
+#define CONTROL1_INT_EN BIT(4)
+#define CONTROL1_INT_ACTIVE_HIGH BIT(5)
+#define CONTROL1_EDGE BIT(7)
+#define CONTROL2_DEFAULT 0x8e
+#define CONTROL2_ADC_EN BIT(0)
+#define CONTROL3_DEFAULT 0x8d
+
+enum max14656_chg_type {
+ MAX14656_NO_CHARGER = 0,
+ MAX14656_SDP_CHARGER,
+ MAX14656_CDP_CHARGER,
+ MAX14656_DCP_CHARGER,
+ MAX14656_APPLE_500MA_CHARGER,
+ MAX14656_APPLE_1A_CHARGER,
+ MAX14656_APPLE_2A_CHARGER,
+ MAX14656_SPECIAL_500MA_CHARGER,
+ MAX14656_APPLE_12W,
+ MAX14656_CHARGER_LAST
+};
+
+static const struct max14656_chg_type_props {
+ enum power_supply_type type;
+} chg_type_props[] = {
+ { POWER_SUPPLY_TYPE_UNKNOWN },
+ { POWER_SUPPLY_TYPE_USB },
+ { POWER_SUPPLY_TYPE_USB_CDP },
+ { POWER_SUPPLY_TYPE_USB_DCP },
+ { POWER_SUPPLY_TYPE_USB_DCP },
+ { POWER_SUPPLY_TYPE_USB_DCP },
+ { POWER_SUPPLY_TYPE_USB_DCP },
+ { POWER_SUPPLY_TYPE_USB_DCP },
+ { POWER_SUPPLY_TYPE_USB },
+};
+
+struct max14656_chip {
+ struct i2c_client *client;
+ struct power_supply *detect_psy;
+ struct power_supply_desc psy_desc;
+ struct delayed_work irq_work;
+
+ int irq;
+ int online;
+};
+
+static int max14656_read_reg(struct i2c_client *client, int reg, u8 *val)
+{
+ s32 ret;
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "i2c read fail: can't read from %02x: %d\n",
+ reg, ret);
+ return ret;
+ }
+ *val = ret;
+ return 0;
+}
+
+static int max14656_write_reg(struct i2c_client *client, int reg, u8 val)
+{
+ s32 ret;
+
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "i2c write fail: can't write %02x to %02x: %d\n",
+ val, reg, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int max14656_read_block_reg(struct i2c_client *client, u8 reg,
+ u8 length, u8 *val)
+{
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(client, reg, length, val);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to block read reg 0x%x: %d\n",
+ reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+#define REG_TOTAL_NUM 5
+static void max14656_irq_worker(struct work_struct *work)
+{
+ struct max14656_chip *chip =
+ container_of(work, struct max14656_chip, irq_work.work);
+
+ u8 buf[REG_TOTAL_NUM];
+ u8 chg_type;
+ int ret = 0;
+
+ ret = max14656_read_block_reg(chip->client, MAX14656_DEVICE_ID,
+ REG_TOTAL_NUM, buf);
+
+ if ((buf[MAX14656_STATUS_1] & STATUS1_VB_VALID_MASK) &&
+ (buf[MAX14656_STATUS_1] & STATUS1_CHG_TYPE_MASK)) {
+ chg_type = buf[MAX14656_STATUS_1] & STATUS1_CHG_TYPE_MASK;
+ if (chg_type < MAX14656_CHARGER_LAST)
+ chip->psy_desc.type = chg_type_props[chg_type].type;
+ else
+ chip->psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN;
+ chip->online = 1;
+ } else {
+ chip->online = 0;
+ chip->psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN;
+ }
+
+ power_supply_changed(chip->detect_psy);
+}
+
+static irqreturn_t max14656_irq(int irq, void *dev_id)
+{
+ struct max14656_chip *chip = dev_id;
+
+ schedule_delayed_work(&chip->irq_work, msecs_to_jiffies(100));
+
+ return IRQ_HANDLED;
+}
+
+static int max14656_hw_init(struct max14656_chip *chip)
+{
+ uint8_t val = 0;
+ uint8_t rev;
+ struct i2c_client *client = chip->client;
+
+ if (max14656_read_reg(client, MAX14656_DEVICE_ID, &val))
+ return -ENODEV;
+
+ if ((val & DEVICE_VENDOR_MASK) != 0x20) {
+ dev_err(&client->dev, "wrong vendor ID %d\n",
+ ((val & DEVICE_VENDOR_MASK) >> 4));
+ return -ENODEV;
+ }
+ rev = val & DEVICE_REV_MASK;
+
+ /* Turn on ADC_EN */
+ if (max14656_write_reg(client, MAX14656_CONTROL_2, CONTROL2_ADC_EN))
+ return -EINVAL;
+
+ /* turn on interrupts and low power mode */
+ if (max14656_write_reg(client, MAX14656_CONTROL_1,
+ CONTROL1_DEFAULT |
+ CONTROL1_INT_EN |
+ CONTROL1_INT_ACTIVE_HIGH |
+ CONTROL1_EDGE))
+ return -EINVAL;
+
+ if (max14656_write_reg(client, MAX14656_INTMASK_1, 0x3))
+ return -EINVAL;
+
+ if (max14656_write_reg(client, MAX14656_INTMASK_2, 0x1))
+ return -EINVAL;
+
+ dev_info(&client->dev, "detected revision %d\n", rev);
+ return 0;
+}
+
+static int max14656_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct max14656_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = chip->online;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = MAX14656_NAME;
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = MAX14656_MANUFACTURER;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static enum power_supply_property max14656_battery_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static int max14656_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct device *dev = &client->dev;
+ struct power_supply_config psy_cfg = {};
+ struct max14656_chip *chip;
+ int irq = client->irq;
+ int ret = 0;
+
+ if (irq <= 0) {
+ dev_err(dev, "invalid irq number: %d\n", irq);
+ return -ENODEV;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
+ return -ENODEV;
+ }
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ psy_cfg.drv_data = chip;
+ chip->client = client;
+ chip->online = 0;
+ chip->psy_desc.name = MAX14656_NAME;
+ chip->psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN;
+ chip->psy_desc.properties = max14656_battery_props;
+ chip->psy_desc.num_properties = ARRAY_SIZE(max14656_battery_props);
+ chip->psy_desc.get_property = max14656_get_property;
+ chip->irq = irq;
+
+ ret = max14656_hw_init(chip);
+ if (ret)
+ return -ENODEV;
+
+ INIT_DELAYED_WORK(&chip->irq_work, max14656_irq_worker);
+
+ ret = devm_request_irq(dev, chip->irq, max14656_irq,
+ IRQF_TRIGGER_FALLING,
+ MAX14656_NAME, chip);
+ if (ret) {
+ dev_err(dev, "request_irq %d failed\n", chip->irq);
+ return -EINVAL;
+ }
+ enable_irq_wake(chip->irq);
+
+ chip->detect_psy = devm_power_supply_register(dev,
+ &chip->psy_desc, &psy_cfg);
+ if (IS_ERR(chip->detect_psy)) {
+ dev_err(dev, "power_supply_register failed\n");
+ return -EINVAL;
+ }
+
+ schedule_delayed_work(&chip->irq_work, msecs_to_jiffies(2000));
+
+ return 0;
+}
+
+static const struct i2c_device_id max14656_id[] = {
+ { "max14656", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, max14656_id);
+
+static const struct of_device_id max14656_match_table[] = {
+ { .compatible = "maxim,max14656", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, max14656_match_table);
+
+static struct i2c_driver max14656_i2c_driver = {
+ .driver = {
+ .name = "max14656",
+ .of_match_table = max14656_match_table,
+ },
+ .probe = max14656_probe,
+ .id_table = max14656_id,
+};
+module_i2c_driver(max14656_i2c_driver);
+
+MODULE_DESCRIPTION("MAX14656 USB charger detector");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/supply/max8997_charger.c b/drivers/power/supply/max8997_charger.c
index 290ddc1..fa86100 100644
--- a/drivers/power/supply/max8997_charger.c
+++ b/drivers/power/supply/max8997_charger.c
@@ -148,10 +148,8 @@
charger = devm_kzalloc(&pdev->dev, sizeof(struct charger_data),
GFP_KERNEL);
- if (charger == NULL) {
- dev_err(&pdev->dev, "Cannot allocate memory.\n");
+ if (!charger)
return -ENOMEM;
- }
platform_set_drvdata(pdev, charger);
@@ -161,7 +159,7 @@
psy_cfg.drv_data = charger;
- charger->battery = power_supply_register(&pdev->dev,
+ charger->battery = devm_power_supply_register(&pdev->dev,
&max8997_battery_desc,
&psy_cfg);
if (IS_ERR(charger->battery)) {
@@ -172,14 +170,6 @@
return 0;
}
-static int max8997_battery_remove(struct platform_device *pdev)
-{
- struct charger_data *charger = platform_get_drvdata(pdev);
-
- power_supply_unregister(charger->battery);
- return 0;
-}
-
static const struct platform_device_id max8997_battery_id[] = {
{ "max8997-battery", 0 },
{ }
@@ -191,7 +181,6 @@
.name = "max8997-battery",
},
.probe = max8997_battery_probe,
- .remove = max8997_battery_remove,
.id_table = max8997_battery_id,
};
diff --git a/drivers/power/supply/pcf50633-charger.c b/drivers/power/supply/pcf50633-charger.c
index d05597b..b3c1873 100644
--- a/drivers/power/supply/pcf50633-charger.c
+++ b/drivers/power/supply/pcf50633-charger.c
@@ -393,7 +393,6 @@
{
struct power_supply_config psy_cfg = {};
struct pcf50633_mbc *mbc;
- int ret;
int i;
u8 mbcs1;
@@ -419,8 +418,7 @@
&psy_cfg);
if (IS_ERR(mbc->adapter)) {
dev_err(mbc->pcf->dev, "failed to register adapter\n");
- ret = PTR_ERR(mbc->adapter);
- return ret;
+ return PTR_ERR(mbc->adapter);
}
mbc->usb = power_supply_register(&pdev->dev, &pcf50633_mbc_usb_desc,
@@ -428,8 +426,7 @@
if (IS_ERR(mbc->usb)) {
dev_err(mbc->pcf->dev, "failed to register usb\n");
power_supply_unregister(mbc->adapter);
- ret = PTR_ERR(mbc->usb);
- return ret;
+ return PTR_ERR(mbc->usb);
}
mbc->ac = power_supply_register(&pdev->dev, &pcf50633_mbc_ac_desc,
@@ -438,12 +435,10 @@
dev_err(mbc->pcf->dev, "failed to register ac\n");
power_supply_unregister(mbc->adapter);
power_supply_unregister(mbc->usb);
- ret = PTR_ERR(mbc->ac);
- return ret;
+ return PTR_ERR(mbc->ac);
}
- ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group);
- if (ret)
+ if (sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group))
dev_err(mbc->pcf->dev, "failed to create sysfs entries\n");
mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1);
diff --git a/drivers/power/supply/qcom_smbb.c b/drivers/power/supply/qcom_smbb.c
index b5896ba..f6a0d24 100644
--- a/drivers/power/supply/qcom_smbb.c
+++ b/drivers/power/supply/qcom_smbb.c
@@ -35,6 +35,7 @@
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/extcon.h>
+#include <linux/regulator/driver.h>
#define SMBB_CHG_VMAX 0x040
#define SMBB_CHG_VSAFE 0x041
@@ -72,6 +73,8 @@
#define BTC_CTRL_HOT_EXT_N BIT(0)
#define SMBB_USB_IMAX 0x344
+#define SMBB_USB_OTG_CTL 0x348
+#define OTG_CTL_EN BIT(0)
#define SMBB_USB_ENUM_TIMER_STOP 0x34e
#define ENUM_TIMER_STOP BIT(0)
#define SMBB_USB_SEC_ACCESS 0x3d0
@@ -125,6 +128,9 @@
struct power_supply *dc_psy;
struct power_supply *bat_psy;
struct regmap *regmap;
+
+ struct regulator_desc otg_rdesc;
+ struct regulator_dev *otg_reg;
};
static const unsigned int smbb_usb_extcon_cable[] = {
@@ -378,7 +384,7 @@
struct smbb_charger *chg = _data;
smbb_set_line_flag(chg, irq, STATUS_USBIN_VALID);
- extcon_set_cable_state_(chg->edev, EXTCON_USB,
+ extcon_set_state_sync(chg->edev, EXTCON_USB,
chg->status & STATUS_USBIN_VALID);
power_supply_changed(chg->usb_psy);
@@ -787,12 +793,56 @@
.property_is_writeable = smbb_charger_writable_property,
};
+static int smbb_chg_otg_enable(struct regulator_dev *rdev)
+{
+ struct smbb_charger *chg = rdev_get_drvdata(rdev);
+ int rc;
+
+ rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_USB_OTG_CTL,
+ OTG_CTL_EN, OTG_CTL_EN);
+ if (rc)
+ dev_err(chg->dev, "failed to update OTG_CTL\n");
+ return rc;
+}
+
+static int smbb_chg_otg_disable(struct regulator_dev *rdev)
+{
+ struct smbb_charger *chg = rdev_get_drvdata(rdev);
+ int rc;
+
+ rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_USB_OTG_CTL,
+ OTG_CTL_EN, 0);
+ if (rc)
+ dev_err(chg->dev, "failed to update OTG_CTL\n");
+ return rc;
+}
+
+static int smbb_chg_otg_is_enabled(struct regulator_dev *rdev)
+{
+ struct smbb_charger *chg = rdev_get_drvdata(rdev);
+ unsigned int value = 0;
+ int rc;
+
+ rc = regmap_read(chg->regmap, chg->addr + SMBB_USB_OTG_CTL, &value);
+ if (rc)
+ dev_err(chg->dev, "failed to read OTG_CTL\n");
+
+ return !!(value & OTG_CTL_EN);
+}
+
+static const struct regulator_ops smbb_chg_otg_ops = {
+ .enable = smbb_chg_otg_enable,
+ .disable = smbb_chg_otg_disable,
+ .is_enabled = smbb_chg_otg_is_enabled,
+};
+
static int smbb_charger_probe(struct platform_device *pdev)
{
struct power_supply_config bat_cfg = {};
struct power_supply_config usb_cfg = {};
struct power_supply_config dc_cfg = {};
struct smbb_charger *chg;
+ struct regulator_config config = { };
int rc, i;
chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
@@ -905,6 +955,26 @@
}
}
+ /*
+ * otg regulator is used to control VBUS voltage direction
+ * when USB switches between host and gadget mode
+ */
+ chg->otg_rdesc.id = -1;
+ chg->otg_rdesc.name = "otg-vbus";
+ chg->otg_rdesc.ops = &smbb_chg_otg_ops;
+ chg->otg_rdesc.owner = THIS_MODULE;
+ chg->otg_rdesc.type = REGULATOR_VOLTAGE;
+ chg->otg_rdesc.supply_name = "usb-otg-in";
+ chg->otg_rdesc.of_match = "otg-vbus";
+
+ config.dev = &pdev->dev;
+ config.driver_data = chg;
+
+ chg->otg_reg = devm_regulator_register(&pdev->dev, &chg->otg_rdesc,
+ &config);
+ if (IS_ERR(chg->otg_reg))
+ return PTR_ERR(chg->otg_reg);
+
chg->jeita_ext_temp = of_property_read_bool(pdev->dev.of_node,
"qcom,jeita-extended-temp-range");
diff --git a/drivers/power/supply/sbs-charger.c b/drivers/power/supply/sbs-charger.c
new file mode 100644
index 0000000..353765a
--- /dev/null
+++ b/drivers/power/supply/sbs-charger.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2016, Prodys S.L.
+ *
+ * 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 adds support for sbs-charger compilant chips as defined here:
+ * http://sbs-forum.org/specs/sbc110.pdf
+ *
+ * Implemetation based on sbs-battery.c
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/power_supply.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/regmap.h>
+#include <linux/of_gpio.h>
+#include <linux/bitops.h>
+
+#define SBS_CHARGER_REG_SPEC_INFO 0x11
+#define SBS_CHARGER_REG_STATUS 0x13
+#define SBS_CHARGER_REG_ALARM_WARNING 0x16
+
+#define SBS_CHARGER_STATUS_CHARGE_INHIBITED BIT(1)
+#define SBS_CHARGER_STATUS_RES_COLD BIT(9)
+#define SBS_CHARGER_STATUS_RES_HOT BIT(10)
+#define SBS_CHARGER_STATUS_BATTERY_PRESENT BIT(14)
+#define SBS_CHARGER_STATUS_AC_PRESENT BIT(15)
+
+#define SBS_CHARGER_POLL_TIME 500
+
+struct sbs_info {
+ struct i2c_client *client;
+ struct power_supply *power_supply;
+ struct regmap *regmap;
+ struct delayed_work work;
+ unsigned int last_state;
+};
+
+static int sbs_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct sbs_info *chip = power_supply_get_drvdata(psy);
+ unsigned int reg;
+
+ reg = chip->last_state;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = !!(reg & SBS_CHARGER_STATUS_BATTERY_PRESENT);
+ break;
+
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = !!(reg & SBS_CHARGER_STATUS_AC_PRESENT);
+ break;
+
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+
+ if (!(reg & SBS_CHARGER_STATUS_BATTERY_PRESENT))
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ else if (reg & SBS_CHARGER_STATUS_AC_PRESENT &&
+ !(reg & SBS_CHARGER_STATUS_CHARGE_INHIBITED))
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+
+ break;
+
+ case POWER_SUPPLY_PROP_HEALTH:
+ if (reg & SBS_CHARGER_STATUS_RES_COLD)
+ val->intval = POWER_SUPPLY_HEALTH_COLD;
+ if (reg & SBS_CHARGER_STATUS_RES_HOT)
+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sbs_check_state(struct sbs_info *chip)
+{
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(chip->regmap, SBS_CHARGER_REG_STATUS, ®);
+ if (!ret && reg != chip->last_state) {
+ chip->last_state = reg;
+ power_supply_changed(chip->power_supply);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void sbs_delayed_work(struct work_struct *work)
+{
+ struct sbs_info *chip = container_of(work, struct sbs_info, work.work);
+
+ sbs_check_state(chip);
+
+ schedule_delayed_work(&chip->work,
+ msecs_to_jiffies(SBS_CHARGER_POLL_TIME));
+}
+
+static irqreturn_t sbs_irq_thread(int irq, void *data)
+{
+ struct sbs_info *chip = data;
+ int ret;
+
+ ret = sbs_check_state(chip);
+
+ return ret ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static enum power_supply_property sbs_properties[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_HEALTH,
+};
+
+static bool sbs_readable_reg(struct device *dev, unsigned int reg)
+{
+ if (reg < SBS_CHARGER_REG_SPEC_INFO)
+ return false;
+ else
+ return true;
+}
+
+static bool sbs_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SBS_CHARGER_REG_STATUS:
+ return true;
+ }
+
+ return false;
+}
+
+static const struct regmap_config sbs_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = SBS_CHARGER_REG_ALARM_WARNING,
+ .readable_reg = sbs_readable_reg,
+ .volatile_reg = sbs_volatile_reg,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE, /* since based on SMBus */
+};
+
+static const struct power_supply_desc sbs_desc = {
+ .name = "sbs-charger",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .properties = sbs_properties,
+ .num_properties = ARRAY_SIZE(sbs_properties),
+ .get_property = sbs_get_property,
+};
+
+static int sbs_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct power_supply_config psy_cfg = {};
+ struct sbs_info *chip;
+ int ret, val;
+
+ chip = devm_kzalloc(&client->dev, sizeof(struct sbs_info), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->client = client;
+ psy_cfg.of_node = client->dev.of_node;
+ psy_cfg.drv_data = chip;
+
+ i2c_set_clientdata(client, chip);
+
+ chip->regmap = devm_regmap_init_i2c(client, &sbs_regmap);
+ if (IS_ERR(chip->regmap))
+ return PTR_ERR(chip->regmap);
+
+ /*
+ * Before we register, we need to make sure we can actually talk
+ * to the battery.
+ */
+ ret = regmap_read(chip->regmap, SBS_CHARGER_REG_STATUS, &val);
+ if (ret) {
+ dev_err(&client->dev, "Failed to get device status\n");
+ return ret;
+ }
+ chip->last_state = val;
+
+ chip->power_supply = devm_power_supply_register(&client->dev, &sbs_desc,
+ &psy_cfg);
+ if (IS_ERR(chip->power_supply)) {
+ dev_err(&client->dev, "Failed to register power supply\n");
+ return PTR_ERR(chip->power_supply);
+ }
+
+ /*
+ * The sbs-charger spec doesn't impose the use of an interrupt. So in
+ * the case it wasn't provided we use polling in order get the charger's
+ * status.
+ */
+ if (client->irq) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, sbs_irq_thread,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev_name(&client->dev), chip);
+ if (ret) {
+ dev_err(&client->dev, "Failed to request irq, %d\n", ret);
+ return ret;
+ }
+ } else {
+ INIT_DELAYED_WORK(&chip->work, sbs_delayed_work);
+ schedule_delayed_work(&chip->work,
+ msecs_to_jiffies(SBS_CHARGER_POLL_TIME));
+ }
+
+ dev_info(&client->dev,
+ "%s: smart charger device registered\n", client->name);
+
+ return 0;
+}
+
+static int sbs_remove(struct i2c_client *client)
+{
+ struct sbs_info *chip = i2c_get_clientdata(client);
+
+ cancel_delayed_work_sync(&chip->work);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id sbs_dt_ids[] = {
+ { .compatible = "sbs,sbs-charger" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sbs_dt_ids);
+#endif
+
+static const struct i2c_device_id sbs_id[] = {
+ { "sbs-charger", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sbs_id);
+
+static struct i2c_driver sbs_driver = {
+ .probe = sbs_probe,
+ .remove = sbs_remove,
+ .id_table = sbs_id,
+ .driver = {
+ .name = "sbs-charger",
+ .of_match_table = of_match_ptr(sbs_dt_ids),
+ },
+};
+module_i2c_driver(sbs_driver);
+
+MODULE_AUTHOR("Nicolas Saenz Julienne <nicolassaenzj@gmail.com>");
+MODULE_DESCRIPTION("SBS smart charger driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/supply/tps65217_charger.c b/drivers/power/supply/tps65217_charger.c
index 9fd019f..29b61e8 100644
--- a/drivers/power/supply/tps65217_charger.c
+++ b/drivers/power/supply/tps65217_charger.c
@@ -35,22 +35,22 @@
#include <linux/mfd/core.h>
#include <linux/mfd/tps65217.h>
+#define CHARGER_STATUS_PRESENT (TPS65217_STATUS_ACPWR | TPS65217_STATUS_USBPWR)
+#define NUM_CHARGER_IRQS 2
#define POLL_INTERVAL (HZ * 2)
struct tps65217_charger {
struct tps65217 *tps;
struct device *dev;
- struct power_supply *ac;
+ struct power_supply *psy;
- int ac_online;
- int prev_ac_online;
+ int online;
+ int prev_online;
struct task_struct *poll_task;
-
- int irq;
};
-static enum power_supply_property tps65217_ac_props[] = {
+static enum power_supply_property tps65217_charger_props[] = {
POWER_SUPPLY_PROP_ONLINE,
};
@@ -95,7 +95,7 @@
int ret;
/* charger already enabled */
- if (charger->ac_online)
+ if (charger->online)
return 0;
dev_dbg(charger->dev, "%s: enable charging\n", __func__);
@@ -110,19 +110,19 @@
return ret;
}
- charger->ac_online = 1;
+ charger->online = 1;
return 0;
}
-static int tps65217_ac_get_property(struct power_supply *psy,
- enum power_supply_property psp,
- union power_supply_propval *val)
+static int tps65217_charger_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
{
struct tps65217_charger *charger = power_supply_get_drvdata(psy);
if (psp == POWER_SUPPLY_PROP_ONLINE) {
- val->intval = charger->ac_online;
+ val->intval = charger->online;
return 0;
}
return -EINVAL;
@@ -133,7 +133,7 @@
int ret, val;
struct tps65217_charger *charger = dev;
- charger->prev_ac_online = charger->ac_online;
+ charger->prev_online = charger->online;
ret = tps65217_reg_read(charger->tps, TPS65217_REG_STATUS, &val);
if (ret < 0) {
@@ -144,8 +144,8 @@
dev_dbg(charger->dev, "%s: 0x%x\n", __func__, val);
- /* check for AC status bit */
- if (val & TPS65217_STATUS_ACPWR) {
+ /* check for charger status bit */
+ if (val & CHARGER_STATUS_PRESENT) {
ret = tps65217_enable_charging(charger);
if (ret) {
dev_err(charger->dev,
@@ -153,11 +153,11 @@
return IRQ_HANDLED;
}
} else {
- charger->ac_online = 0;
+ charger->online = 0;
}
- if (charger->prev_ac_online != charger->ac_online)
- power_supply_changed(charger->ac);
+ if (charger->prev_online != charger->online)
+ power_supply_changed(charger->psy);
ret = tps65217_reg_read(charger->tps, TPS65217_REG_CHGCONFIG0, &val);
if (ret < 0) {
@@ -188,11 +188,11 @@
}
static const struct power_supply_desc tps65217_charger_desc = {
- .name = "tps65217-ac",
+ .name = "tps65217-charger",
.type = POWER_SUPPLY_TYPE_MAINS,
- .get_property = tps65217_ac_get_property,
- .properties = tps65217_ac_props,
- .num_properties = ARRAY_SIZE(tps65217_ac_props),
+ .get_property = tps65217_charger_get_property,
+ .properties = tps65217_charger_props,
+ .num_properties = ARRAY_SIZE(tps65217_charger_props),
};
static int tps65217_charger_probe(struct platform_device *pdev)
@@ -200,8 +200,10 @@
struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
struct tps65217_charger *charger;
struct power_supply_config cfg = {};
- int irq;
+ struct task_struct *poll_task;
+ int irq[NUM_CHARGER_IRQS];
int ret;
+ int i;
dev_dbg(&pdev->dev, "%s\n", __func__);
@@ -216,18 +218,16 @@
cfg.of_node = pdev->dev.of_node;
cfg.drv_data = charger;
- charger->ac = devm_power_supply_register(&pdev->dev,
- &tps65217_charger_desc,
- &cfg);
- if (IS_ERR(charger->ac)) {
+ charger->psy = devm_power_supply_register(&pdev->dev,
+ &tps65217_charger_desc,
+ &cfg);
+ if (IS_ERR(charger->psy)) {
dev_err(&pdev->dev, "failed: power supply register\n");
- return PTR_ERR(charger->ac);
+ return PTR_ERR(charger->psy);
}
- irq = platform_get_irq_byname(pdev, "AC");
- if (irq < 0)
- irq = -ENXIO;
- charger->irq = irq;
+ irq[0] = platform_get_irq_byname(pdev, "USB");
+ irq[1] = platform_get_irq_byname(pdev, "AC");
ret = tps65217_config_charger(charger);
if (ret < 0) {
@@ -235,29 +235,36 @@
return ret;
}
- if (irq != -ENXIO) {
- ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ /* Create a polling thread if an interrupt is invalid */
+ if (irq[0] < 0 || irq[1] < 0) {
+ poll_task = kthread_run(tps65217_charger_poll_task,
+ charger, "ktps65217charger");
+ if (IS_ERR(poll_task)) {
+ ret = PTR_ERR(poll_task);
+ dev_err(charger->dev,
+ "Unable to run kthread err %d\n", ret);
+ return ret;
+ }
+
+ charger->poll_task = poll_task;
+ return 0;
+ }
+
+ /* Create IRQ threads for charger interrupts */
+ for (i = 0; i < NUM_CHARGER_IRQS; i++) {
+ ret = devm_request_threaded_irq(&pdev->dev, irq[i], NULL,
tps65217_charger_irq,
0, "tps65217-charger",
charger);
if (ret) {
dev_err(charger->dev,
- "Unable to register irq %d err %d\n", irq,
+ "Unable to register irq %d err %d\n", irq[i],
ret);
return ret;
}
/* Check current state */
- tps65217_charger_irq(irq, charger);
- } else {
- charger->poll_task = kthread_run(tps65217_charger_poll_task,
- charger, "ktps65217charger");
- if (IS_ERR(charger->poll_task)) {
- ret = PTR_ERR(charger->poll_task);
- dev_err(charger->dev,
- "Unable to run kthread err %d\n", ret);
- return ret;
- }
+ tps65217_charger_irq(-1, charger);
}
return 0;
@@ -267,7 +274,7 @@
{
struct tps65217_charger *charger = platform_get_drvdata(pdev);
- if (charger->irq == -ENXIO)
+ if (charger->poll_task)
kthread_stop(charger->poll_task);
return 0;
diff --git a/drivers/power/supply/wm97xx_battery.c b/drivers/power/supply/wm97xx_battery.c
index e3edb31..bd4f666 100644
--- a/drivers/power/supply/wm97xx_battery.c
+++ b/drivers/power/supply/wm97xx_battery.c
@@ -175,11 +175,6 @@
if (dev->id != -1)
return -EINVAL;
- if (!pdata) {
- dev_err(&dev->dev, "No platform_data supplied\n");
- return -EINVAL;
- }
-
if (gpio_is_valid(pdata->charge_gpio)) {
ret = gpio_request(pdata->charge_gpio, "BATT CHRG");
if (ret)
diff --git a/drivers/regulator/88pm800.c b/drivers/regulator/88pm800.c
index a62a896..89bbd6e 100644
--- a/drivers/regulator/88pm800.c
+++ b/drivers/regulator/88pm800.c
@@ -180,7 +180,7 @@
return info->max_ua;
}
-static struct regulator_ops pm800_volt_range_ops = {
+static const struct regulator_ops pm800_volt_range_ops = {
.list_voltage = regulator_list_voltage_linear_range,
.map_voltage = regulator_map_voltage_linear_range,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
@@ -191,7 +191,7 @@
.get_current_limit = pm800_get_current_limit,
};
-static struct regulator_ops pm800_volt_table_ops = {
+static const struct regulator_ops pm800_volt_table_ops = {
.list_voltage = regulator_list_voltage_table,
.map_voltage = regulator_map_voltage_iterate,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c
index b100a63..fd86446 100644
--- a/drivers/regulator/88pm8607.c
+++ b/drivers/regulator/88pm8607.c
@@ -220,7 +220,7 @@
return ret;
}
-static struct regulator_ops pm8607_regulator_ops = {
+static const struct regulator_ops pm8607_regulator_ops = {
.list_voltage = pm8607_list_voltage,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
@@ -229,7 +229,7 @@
.is_enabled = regulator_is_enabled_regmap,
};
-static struct regulator_ops pm8606_preg_ops = {
+static const struct regulator_ops pm8606_preg_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 936f7cc..be06eb2 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -163,6 +163,13 @@
BCM590xx PMUs. This will enable support for the software
controllable LDO/Switching regulators.
+config REGULATOR_CPCAP
+ tristate "Motorola CPCAP regulator"
+ depends on MFD_CPCAP
+ help
+ Say y here for CPCAP regulator found on some Motorola phones
+ and tablets such as Droid 4.
+
config REGULATOR_DA903X
tristate "Dialog Semiconductor DA9030/DA9034 regulators"
depends on PMIC_DA903X
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 1429469..ef7725e 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -11,6 +11,7 @@
obj-$(CONFIG_REGULATOR_88PM800) += 88pm800.o
obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
+obj-$(CONFIG_REGULATOR_CPCAP) += cpcap-regulator.o
obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o
obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
obj-$(CONFIG_REGULATOR_AB8500) += ab8500-ext.o ab8500.o
diff --git a/drivers/regulator/aat2870-regulator.c b/drivers/regulator/aat2870-regulator.c
index 9dfabda..afc5b59 100644
--- a/drivers/regulator/aat2870-regulator.c
+++ b/drivers/regulator/aat2870-regulator.c
@@ -97,7 +97,7 @@
return val & ri->enable_mask ? 1 : 0;
}
-static struct regulator_ops aat2870_ldo_ops = {
+static const struct regulator_ops aat2870_ldo_ops = {
.list_voltage = regulator_list_voltage_table,
.map_voltage = regulator_map_voltage_ascend,
.set_voltage_sel = aat2870_ldo_set_voltage_sel,
diff --git a/drivers/regulator/act8945a-regulator.c b/drivers/regulator/act8945a-regulator.c
index 441864b..43fda8b 100644
--- a/drivers/regulator/act8945a-regulator.c
+++ b/drivers/regulator/act8945a-regulator.c
@@ -69,7 +69,7 @@
REGULATOR_LINEAR_RANGE(2400000, 48, 63, 100000),
};
-static struct regulator_ops act8945a_ops = {
+static const struct regulator_ops act8945a_ops = {
.list_voltage = regulator_list_voltage_linear_range,
.map_voltage = regulator_map_voltage_linear_range,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c
index 8b0f788..11c1f88 100644
--- a/drivers/regulator/ad5398.c
+++ b/drivers/regulator/ad5398.c
@@ -181,7 +181,7 @@
return ret;
}
-static struct regulator_ops ad5398_ops = {
+static const struct regulator_ops ad5398_ops = {
.get_current_limit = ad5398_get_current_limit,
.set_current_limit = ad5398_set_current_limit,
.enable = ad5398_enable,
diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c
index 3a6d029..b041f27 100644
--- a/drivers/regulator/anatop-regulator.c
+++ b/drivers/regulator/anatop-regulator.c
@@ -301,7 +301,19 @@
return -EINVAL;
}
} else {
+ u32 enable_bit;
+
rdesc->ops = &anatop_rops;
+
+ if (!of_property_read_u32(np, "anatop-enable-bit",
+ &enable_bit)) {
+ anatop_rops.enable = regulator_enable_regmap;
+ anatop_rops.disable = regulator_disable_regmap;
+ anatop_rops.is_enabled = regulator_is_enabled_regmap;
+
+ rdesc->enable_reg = sreg->control_reg;
+ rdesc->enable_mask = BIT(enable_bit);
+ }
}
/* register regulator */
diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c
index 302b57c..e76d094 100644
--- a/drivers/regulator/arizona-ldo1.c
+++ b/drivers/regulator/arizona-ldo1.c
@@ -109,7 +109,7 @@
return (val & ARIZONA_LDO1_VSEL_MASK) >> ARIZONA_LDO1_VSEL_SHIFT;
}
-static struct regulator_ops arizona_ldo1_hc_ops = {
+static const struct regulator_ops arizona_ldo1_hc_ops = {
.list_voltage = arizona_ldo1_hc_list_voltage,
.map_voltage = arizona_ldo1_hc_map_voltage,
.get_voltage_sel = arizona_ldo1_hc_get_voltage_sel,
@@ -135,7 +135,7 @@
.owner = THIS_MODULE,
};
-static struct regulator_ops arizona_ldo1_ops = {
+static const struct regulator_ops arizona_ldo1_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c
index fcb98db..22bd714 100644
--- a/drivers/regulator/arizona-micsupp.c
+++ b/drivers/regulator/arizona-micsupp.c
@@ -45,6 +45,7 @@
struct arizona_micsupp *micsupp =
container_of(work, struct arizona_micsupp, check_cp_work);
struct snd_soc_dapm_context *dapm = micsupp->arizona->dapm;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
struct arizona *arizona = micsupp->arizona;
struct regmap *regmap = arizona->regmap;
unsigned int reg;
@@ -59,9 +60,10 @@
if (dapm) {
if ((reg & (ARIZONA_CPMIC_ENA | ARIZONA_CPMIC_BYPASS)) ==
ARIZONA_CPMIC_ENA)
- snd_soc_dapm_force_enable_pin(dapm, "MICSUPP");
+ snd_soc_component_force_enable_pin(component,
+ "MICSUPP");
else
- snd_soc_dapm_disable_pin(dapm, "MICSUPP");
+ snd_soc_component_disable_pin(component, "MICSUPP");
snd_soc_dapm_sync(dapm);
}
@@ -104,7 +106,7 @@
return ret;
}
-static struct regulator_ops arizona_micsupp_ops = {
+static const struct regulator_ops arizona_micsupp_ops = {
.enable = arizona_micsupp_enable,
.disable = arizona_micsupp_disable,
.is_enabled = regulator_is_enabled_regmap,
diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c
index c0e93b1..874d415 100644
--- a/drivers/regulator/as3711-regulator.c
+++ b/drivers/regulator/as3711-regulator.c
@@ -82,7 +82,7 @@
return -EINVAL;
}
-static struct regulator_ops as3711_sd_ops = {
+static const struct regulator_ops as3711_sd_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
@@ -94,7 +94,7 @@
.set_mode = as3711_set_mode_sd,
};
-static struct regulator_ops as3711_aldo_ops = {
+static const struct regulator_ops as3711_aldo_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
@@ -104,7 +104,7 @@
.map_voltage = regulator_map_voltage_linear_range,
};
-static struct regulator_ops as3711_dldo_ops = {
+static const struct regulator_ops as3711_dldo_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c
index a3ade9e..0b9d4e3 100644
--- a/drivers/regulator/axp20x-regulator.c
+++ b/drivers/regulator/axp20x-regulator.c
@@ -128,11 +128,11 @@
.ops = &axp20x_ops_range, \
}
-static struct regulator_ops axp20x_ops_fixed = {
+static const struct regulator_ops axp20x_ops_fixed = {
.list_voltage = regulator_list_voltage_linear,
};
-static struct regulator_ops axp20x_ops_range = {
+static const struct regulator_ops axp20x_ops_range = {
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.list_voltage = regulator_list_voltage_linear_range,
@@ -141,7 +141,7 @@
.is_enabled = regulator_is_enabled_regmap,
};
-static struct regulator_ops axp20x_ops = {
+static const struct regulator_ops axp20x_ops = {
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.list_voltage = regulator_list_voltage_linear,
@@ -150,7 +150,7 @@
.is_enabled = regulator_is_enabled_regmap,
};
-static struct regulator_ops axp20x_ops_sw = {
+static const struct regulator_ops axp20x_ops_sw = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
diff --git a/drivers/regulator/bcm590xx-regulator.c b/drivers/regulator/bcm590xx-regulator.c
index 76b0183..9dd7154 100644
--- a/drivers/regulator/bcm590xx-regulator.c
+++ b/drivers/regulator/bcm590xx-regulator.c
@@ -250,7 +250,7 @@
return reg;
}
-static struct regulator_ops bcm590xx_ops_ldo = {
+static const struct regulator_ops bcm590xx_ops_ldo = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
@@ -260,7 +260,7 @@
.map_voltage = regulator_map_voltage_iterate,
};
-static struct regulator_ops bcm590xx_ops_dcdc = {
+static const struct regulator_ops bcm590xx_ops_dcdc = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
@@ -270,7 +270,7 @@
.map_voltage = regulator_map_voltage_linear_range,
};
-static struct regulator_ops bcm590xx_ops_vbus = {
+static const struct regulator_ops bcm590xx_ops_vbus = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 04baac9..53d4fc7 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -1455,12 +1455,14 @@
* lookup could succeed in the future.
*
* If successful, returns a struct regulator_dev that corresponds to the name
- * @supply and with the embedded struct device refcount incremented by one,
- * or NULL on failure. The refcount must be dropped by calling put_device().
+ * @supply and with the embedded struct device refcount incremented by one.
+ * The refcount must be dropped by calling put_device().
+ * On failure one of the following ERR-PTR-encoded values is returned:
+ * -ENODEV if lookup fails permanently, -EPROBE_DEFER if lookup could succeed
+ * in the future.
*/
static struct regulator_dev *regulator_dev_lookup(struct device *dev,
- const char *supply,
- int *ret)
+ const char *supply)
{
struct regulator_dev *r;
struct device_node *node;
@@ -1476,16 +1478,12 @@
r = of_find_regulator_by_node(node);
if (r)
return r;
- *ret = -EPROBE_DEFER;
- return NULL;
- } else {
+
/*
- * If we couldn't even get the node then it's
- * not just that the device didn't register
- * yet, there's no node and we'll never
- * succeed.
+ * We have a node, but there is no device.
+ * assume it has not registered yet.
*/
- *ret = -ENODEV;
+ return ERR_PTR(-EPROBE_DEFER);
}
}
@@ -1506,13 +1504,16 @@
if (strcmp(map->supply, supply) == 0 &&
get_device(&map->regulator->dev)) {
- mutex_unlock(®ulator_list_mutex);
- return map->regulator;
+ r = map->regulator;
+ break;
}
}
mutex_unlock(®ulator_list_mutex);
- return NULL;
+ if (r)
+ return r;
+
+ return ERR_PTR(-ENODEV);
}
static int regulator_resolve_supply(struct regulator_dev *rdev)
@@ -1529,8 +1530,10 @@
if (rdev->supply)
return 0;
- r = regulator_dev_lookup(dev, rdev->supply_name, &ret);
- if (!r) {
+ r = regulator_dev_lookup(dev, rdev->supply_name);
+ if (IS_ERR(r)) {
+ ret = PTR_ERR(r);
+
if (ret == -ENODEV) {
/*
* No supply was specified for this regulator and
@@ -1553,6 +1556,19 @@
}
}
+ /*
+ * If the supply's parent device is not the same as the
+ * regulator's parent device, then ensure the parent device
+ * is bound before we resolve the supply, in case the parent
+ * device get probe deferred and unregisters the supply.
+ */
+ if (r->dev.parent && r->dev.parent != rdev->dev.parent) {
+ if (!device_is_bound(r->dev.parent)) {
+ put_device(&r->dev);
+ return -EPROBE_DEFER;
+ }
+ }
+
/* Recursively resolve the supply of the supply */
ret = regulator_resolve_supply(r);
if (ret < 0) {
@@ -1580,69 +1596,72 @@
}
/* Internal regulator request function */
-static struct regulator *_regulator_get(struct device *dev, const char *id,
- bool exclusive, bool allow_dummy)
+struct regulator *_regulator_get(struct device *dev, const char *id,
+ enum regulator_get_type get_type)
{
struct regulator_dev *rdev;
- struct regulator *regulator = ERR_PTR(-EPROBE_DEFER);
- const char *devname = NULL;
+ struct regulator *regulator;
+ const char *devname = dev ? dev_name(dev) : "deviceless";
int ret;
+ if (get_type >= MAX_GET_TYPE) {
+ dev_err(dev, "invalid type %d in %s\n", get_type, __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
if (id == NULL) {
pr_err("get() with no identifier\n");
return ERR_PTR(-EINVAL);
}
- if (dev)
- devname = dev_name(dev);
+ rdev = regulator_dev_lookup(dev, id);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
- if (have_full_constraints())
- ret = -ENODEV;
- else
- ret = -EPROBE_DEFER;
+ /*
+ * If regulator_dev_lookup() fails with error other
+ * than -ENODEV our job here is done, we simply return it.
+ */
+ if (ret != -ENODEV)
+ return ERR_PTR(ret);
- rdev = regulator_dev_lookup(dev, id, &ret);
- if (rdev)
- goto found;
+ if (!have_full_constraints()) {
+ dev_warn(dev,
+ "incomplete constraints, dummy supplies not allowed\n");
+ return ERR_PTR(-ENODEV);
+ }
- regulator = ERR_PTR(ret);
+ switch (get_type) {
+ case NORMAL_GET:
+ /*
+ * Assume that a regulator is physically present and
+ * enabled, even if it isn't hooked up, and just
+ * provide a dummy.
+ */
+ dev_warn(dev,
+ "%s supply %s not found, using dummy regulator\n",
+ devname, id);
+ rdev = dummy_regulator_rdev;
+ get_device(&rdev->dev);
+ break;
- /*
- * If we have return value from dev_lookup fail, we do not expect to
- * succeed, so, quit with appropriate error value
- */
- if (ret && ret != -ENODEV)
- return regulator;
+ case EXCLUSIVE_GET:
+ dev_warn(dev,
+ "dummy supplies not allowed for exclusive requests\n");
+ /* fall through */
- if (!devname)
- devname = "deviceless";
-
- /*
- * Assume that a regulator is physically present and enabled
- * even if it isn't hooked up and just provide a dummy.
- */
- if (have_full_constraints() && allow_dummy) {
- pr_warn("%s supply %s not found, using dummy regulator\n",
- devname, id);
-
- rdev = dummy_regulator_rdev;
- get_device(&rdev->dev);
- goto found;
- /* Don't log an error when called from regulator_get_optional() */
- } else if (!have_full_constraints() || exclusive) {
- dev_warn(dev, "dummy supplies not allowed\n");
+ default:
+ return ERR_PTR(-ENODEV);
+ }
}
- return regulator;
-
-found:
if (rdev->exclusive) {
regulator = ERR_PTR(-EPERM);
put_device(&rdev->dev);
return regulator;
}
- if (exclusive && rdev->open_count) {
+ if (get_type == EXCLUSIVE_GET && rdev->open_count) {
regulator = ERR_PTR(-EBUSY);
put_device(&rdev->dev);
return regulator;
@@ -1656,6 +1675,7 @@
}
if (!try_module_get(rdev->owner)) {
+ regulator = ERR_PTR(-EPROBE_DEFER);
put_device(&rdev->dev);
return regulator;
}
@@ -1669,7 +1689,7 @@
}
rdev->open_count++;
- if (exclusive) {
+ if (get_type == EXCLUSIVE_GET) {
rdev->exclusive = 1;
ret = _regulator_is_enabled(rdev);
@@ -1697,7 +1717,7 @@
*/
struct regulator *regulator_get(struct device *dev, const char *id)
{
- return _regulator_get(dev, id, false, true);
+ return _regulator_get(dev, id, NORMAL_GET);
}
EXPORT_SYMBOL_GPL(regulator_get);
@@ -1724,7 +1744,7 @@
*/
struct regulator *regulator_get_exclusive(struct device *dev, const char *id)
{
- return _regulator_get(dev, id, true, false);
+ return _regulator_get(dev, id, EXCLUSIVE_GET);
}
EXPORT_SYMBOL_GPL(regulator_get_exclusive);
@@ -1750,7 +1770,7 @@
*/
struct regulator *regulator_get_optional(struct device *dev, const char *id)
{
- return _regulator_get(dev, id, false, false);
+ return _regulator_get(dev, id, OPTIONAL_GET);
}
EXPORT_SYMBOL_GPL(regulator_get_optional);
@@ -3660,7 +3680,7 @@
for (++i; i < num_consumers; ++i) {
r = regulator_enable(consumers[i].consumer);
if (r != 0)
- pr_err("Failed to reename %s: %d\n",
+ pr_err("Failed to re-enable %s: %d\n",
consumers[i].supply, r);
}
@@ -3686,21 +3706,17 @@
struct regulator_bulk_data *consumers)
{
int i;
- int ret;
+ int ret = 0;
- for (i = 0; i < num_consumers; i++)
+ for (i = 0; i < num_consumers; i++) {
consumers[i].ret =
regulator_force_disable(consumers[i].consumer);
- for (i = 0; i < num_consumers; i++) {
- if (consumers[i].ret != 0) {
+ /* Store first error for reporting */
+ if (consumers[i].ret && !ret)
ret = consumers[i].ret;
- goto out;
- }
}
- return 0;
-out:
return ret;
}
EXPORT_SYMBOL_GPL(regulator_bulk_force_disable);
@@ -4391,12 +4407,13 @@
seq_puts(s, "\n");
list_for_each_entry(consumer, &rdev->consumer_list, list) {
- if (consumer->dev->class == ®ulator_class)
+ if (consumer->dev && consumer->dev->class == ®ulator_class)
continue;
seq_printf(s, "%*s%-*s ",
(level + 1) * 3 + 1, "",
- 30 - (level + 1) * 3, dev_name(consumer->dev));
+ 30 - (level + 1) * 3,
+ consumer->dev ? dev_name(consumer->dev) : "deviceless");
switch (rdev->desc->type) {
case REGULATOR_VOLTAGE:
@@ -4540,6 +4557,16 @@
if (of_have_populated_dt())
has_full_constraints = true;
+ /*
+ * Regulators may had failed to resolve their input supplies
+ * when were registered, either because the input supply was
+ * not registered yet or because its parent device was not
+ * bound yet. So attempt to resolve the input supplies for
+ * pending regulators before trying to disable unused ones.
+ */
+ class_for_each_device(®ulator_class, NULL, NULL,
+ regulator_register_resolve_supply);
+
/* If we have a full configuration then disable any regulators
* we have permission to change the status for and which are
* not in use or always_on. This is effectively the default
diff --git a/drivers/regulator/cpcap-regulator.c b/drivers/regulator/cpcap-regulator.c
new file mode 100644
index 0000000..cc98ace
--- /dev/null
+++ b/drivers/regulator/cpcap-regulator.c
@@ -0,0 +1,464 @@
+/*
+ * Motorola CPCAP PMIC regulator driver
+ *
+ * Based on cpcap-regulator.c from Motorola Linux kernel tree
+ * Copyright (C) 2009-2011 Motorola, Inc.
+ *
+ * Rewritten for mainline kernel to use device tree and regmap
+ * Copyright (C) 2017 Tony Lindgren <tony@atomide.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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/motorola-cpcap.h>
+
+/*
+ * Resource assignment register bits. These seem to control the state
+ * idle modes adn are used at least for omap4.
+ */
+
+/* CPCAP_REG_ASSIGN2 bits - Resource Assignment 2 */
+#define CPCAP_BIT_VSDIO_SEL BIT(15)
+#define CPCAP_BIT_VDIG_SEL BIT(14)
+#define CPCAP_BIT_VCAM_SEL BIT(13)
+#define CPCAP_BIT_SW6_SEL BIT(12)
+#define CPCAP_BIT_SW5_SEL BIT(11)
+#define CPCAP_BIT_SW4_SEL BIT(10)
+#define CPCAP_BIT_SW3_SEL BIT(9)
+#define CPCAP_BIT_SW2_SEL BIT(8)
+#define CPCAP_BIT_SW1_SEL BIT(7)
+
+/* CPCAP_REG_ASSIGN3 bits - Resource Assignment 3 */
+#define CPCAP_BIT_VUSBINT2_SEL BIT(15)
+#define CPCAP_BIT_VUSBINT1_SEL BIT(14)
+#define CPCAP_BIT_VVIB_SEL BIT(13)
+#define CPCAP_BIT_VWLAN1_SEL BIT(12)
+#define CPCAP_BIT_VRF1_SEL BIT(11)
+#define CPCAP_BIT_VHVIO_SEL BIT(10)
+#define CPCAP_BIT_VDAC_SEL BIT(9)
+#define CPCAP_BIT_VUSB_SEL BIT(8)
+#define CPCAP_BIT_VSIM_SEL BIT(7)
+#define CPCAP_BIT_VRFREF_SEL BIT(6)
+#define CPCAP_BIT_VPLL_SEL BIT(5)
+#define CPCAP_BIT_VFUSE_SEL BIT(4)
+#define CPCAP_BIT_VCSI_SEL BIT(3)
+#define CPCAP_BIT_SPARE_14_2 BIT(2)
+#define CPCAP_BIT_VWLAN2_SEL BIT(1)
+#define CPCAP_BIT_VRF2_SEL BIT(0)
+
+/* CPCAP_REG_ASSIGN4 bits - Resource Assignment 4 */
+#define CPCAP_BIT_VAUDIO_SEL BIT(0)
+
+/*
+ * Enable register bits. At least CPCAP_BIT_AUDIO_LOW_PWR is generic,
+ * and not limited to audio regulator. Let's use the Motorola kernel
+ * naming for now until we have a better understanding of the other
+ * enable register bits. No idea why BIT(3) is not defined.
+ */
+#define CPCAP_BIT_AUDIO_LOW_PWR BIT(6)
+#define CPCAP_BIT_AUD_LOWPWR_SPEED BIT(5)
+#define CPCAP_BIT_VAUDIOPRISTBY BIT(4)
+#define CPCAP_BIT_VAUDIO_MODE1 BIT(2)
+#define CPCAP_BIT_VAUDIO_MODE0 BIT(1)
+#define CPCAP_BIT_V_AUDIO_EN BIT(0)
+
+/*
+ * Off mode configuration bit. Used currently only by SW5 on omap4. There's
+ * the following comment in Motorola Linux kernel tree for it:
+ *
+ * When set in the regulator mode, the regulator assignment will be changed
+ * to secondary when the regulator is disabled. The mode will be set back to
+ * primary when the regulator is turned on.
+ */
+#define CPCAP_REG_OFF_MODE_SEC BIT(15)
+
+/**
+ * SoC specific configuraion for CPCAP regulator. There are at least three
+ * different SoCs each with their own parameters: omap3, omap4 and tegra2.
+ *
+ * The assign_reg and assign_mask seem to allow toggling between primary
+ * and secondary mode that at least omap4 uses for off mode.
+ */
+struct cpcap_regulator {
+ struct regulator_desc rdesc;
+ const u16 assign_reg;
+ const u16 assign_mask;
+ const u16 vsel_shift;
+};
+
+#define CPCAP_REG(_ID, reg, assignment_reg, assignment_mask, val_tbl, \
+ mode_mask, volt_mask, volt_shft, \
+ mode_val, off_val, volt_trans_time) { \
+ .rdesc = { \
+ .name = #_ID, \
+ .of_match = of_match_ptr(#_ID), \
+ .ops = &cpcap_regulator_ops, \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = CPCAP_##_ID, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ARRAY_SIZE(val_tbl), \
+ .volt_table = (val_tbl), \
+ .vsel_reg = (reg), \
+ .vsel_mask = (volt_mask), \
+ .enable_reg = (reg), \
+ .enable_mask = (mode_mask), \
+ .enable_val = (mode_val), \
+ .disable_val = (off_val), \
+ .ramp_delay = (volt_trans_time), \
+ }, \
+ .assign_reg = (assignment_reg), \
+ .assign_mask = (assignment_mask), \
+ .vsel_shift = (volt_shft), \
+}
+
+struct cpcap_ddata {
+ struct regmap *reg;
+ struct device *dev;
+ const struct cpcap_regulator *soc;
+};
+
+enum cpcap_regulator_id {
+ CPCAP_SW1,
+ CPCAP_SW2,
+ CPCAP_SW3,
+ CPCAP_SW4,
+ CPCAP_SW5,
+ CPCAP_SW6,
+ CPCAP_VCAM,
+ CPCAP_VCSI,
+ CPCAP_VDAC,
+ CPCAP_VDIG,
+ CPCAP_VFUSE,
+ CPCAP_VHVIO,
+ CPCAP_VSDIO,
+ CPCAP_VPLL,
+ CPCAP_VRF1,
+ CPCAP_VRF2,
+ CPCAP_VRFREF,
+ CPCAP_VWLAN1,
+ CPCAP_VWLAN2,
+ CPCAP_VSIM,
+ CPCAP_VSIMCARD,
+ CPCAP_VVIB,
+ CPCAP_VUSB,
+ CPCAP_VAUDIO,
+ CPCAP_NR_REGULATORS,
+};
+
+/*
+ * We need to also configure regulator idle mode for SoC off mode if
+ * CPCAP_REG_OFF_MODE_SEC is set.
+ */
+static int cpcap_regulator_enable(struct regulator_dev *rdev)
+{
+ struct cpcap_regulator *regulator = rdev_get_drvdata(rdev);
+ int error, ignore;
+
+ error = regulator_enable_regmap(rdev);
+ if (error)
+ return error;
+
+ if (rdev->desc->enable_val & CPCAP_REG_OFF_MODE_SEC) {
+ error = regmap_update_bits(rdev->regmap, regulator->assign_reg,
+ regulator->assign_mask,
+ regulator->assign_mask);
+ if (error)
+ ignore = regulator_disable_regmap(rdev);
+ }
+
+ return error;
+}
+
+/*
+ * We need to also configure regulator idle mode for SoC off mode if
+ * CPCAP_REG_OFF_MODE_SEC is set.
+ */
+static int cpcap_regulator_disable(struct regulator_dev *rdev)
+{
+ struct cpcap_regulator *regulator = rdev_get_drvdata(rdev);
+ int error, ignore;
+
+ if (rdev->desc->enable_val & CPCAP_REG_OFF_MODE_SEC) {
+ error = regmap_update_bits(rdev->regmap, regulator->assign_reg,
+ regulator->assign_mask, 0);
+ if (error)
+ return error;
+ }
+
+ error = regulator_disable_regmap(rdev);
+ if (error && (rdev->desc->enable_val & CPCAP_REG_OFF_MODE_SEC)) {
+ ignore = regmap_update_bits(rdev->regmap, regulator->assign_reg,
+ regulator->assign_mask,
+ regulator->assign_mask);
+ }
+
+ return error;
+}
+
+static unsigned int cpcap_regulator_get_mode(struct regulator_dev *rdev)
+{
+ int value;
+
+ regmap_read(rdev->regmap, rdev->desc->enable_reg, &value);
+
+ if (!(value & CPCAP_BIT_AUDIO_LOW_PWR))
+ return REGULATOR_MODE_STANDBY;
+
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int cpcap_regulator_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ int value;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ value = CPCAP_BIT_AUDIO_LOW_PWR;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ value = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ CPCAP_BIT_AUDIO_LOW_PWR, value);
+}
+
+static struct regulator_ops cpcap_regulator_ops = {
+ .enable = cpcap_regulator_enable,
+ .disable = cpcap_regulator_disable,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_table,
+ .map_voltage = regulator_map_voltage_iterate,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_mode = cpcap_regulator_get_mode,
+ .set_mode = cpcap_regulator_set_mode,
+};
+
+static const unsigned int unknown_val_tbl[] = { 0, };
+static const unsigned int sw5_val_tbl[] = { 0, 5050000, };
+static const unsigned int vcam_val_tbl[] = { 2600000, 2700000, 2800000,
+ 2900000, };
+static const unsigned int vcsi_val_tbl[] = { 1200000, 1800000, };
+static const unsigned int vdac_val_tbl[] = { 1200000, 1500000, 1800000,
+ 2500000,};
+static const unsigned int vdig_val_tbl[] = { 1200000, 1350000, 1500000,
+ 1875000, };
+static const unsigned int vfuse_val_tbl[] = { 1500000, 1600000, 1700000,
+ 1800000, 1900000, 2000000,
+ 2100000, 2200000, 2300000,
+ 2400000, 2500000, 2600000,
+ 2700000, 3150000, };
+static const unsigned int vhvio_val_tbl[] = { 2775000, };
+static const unsigned int vsdio_val_tbl[] = { 1500000, 1600000, 1800000,
+ 2600000, 2700000, 2800000,
+ 2900000, 3000000, };
+static const unsigned int vpll_val_tbl[] = { 1200000, 1300000, 1400000,
+ 1800000, };
+/* Quirk: 2775000 is before 2500000 for vrf1 regulator */
+static const unsigned int vrf1_val_tbl[] = { 2775000, 2500000, };
+static const unsigned int vrf2_val_tbl[] = { 0, 2775000, };
+static const unsigned int vrfref_val_tbl[] = { 2500000, 2775000, };
+static const unsigned int vwlan1_val_tbl[] = { 1800000, 1900000, };
+static const unsigned int vwlan2_val_tbl[] = { 2775000, 3000000, 3300000,
+ 3300000, };
+static const unsigned int vsim_val_tbl[] = { 1800000, 2900000, };
+static const unsigned int vsimcard_val_tbl[] = { 1800000, 2900000, };
+static const unsigned int vvib_val_tbl[] = { 1300000, 1800000, 2000000,
+ 3000000, };
+static const unsigned int vusb_val_tbl[] = { 0, 3300000, };
+static const unsigned int vaudio_val_tbl[] = { 0, 2775000, };
+
+/**
+ * SoC specific configuration for omap4. The data below is comes from Motorola
+ * Linux kernel tree. It's basically the values of cpcap_regltr_data,
+ * cpcap_regulator_mode_values and cpcap_regulator_off_mode_values, see
+ * CPCAP_REG macro above.
+ *
+ * SW1 to SW4 and SW6 seems to be unused for mapphone. Note that VSIM and
+ * VSIMCARD have a shared resource assignment bit.
+ */
+static struct cpcap_regulator omap4_regulators[] = {
+ CPCAP_REG(SW1, CPCAP_REG_S1C1, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW1_SEL, unknown_val_tbl,
+ 0, 0, 0, 0, 0, 0),
+ CPCAP_REG(SW2, CPCAP_REG_S2C1, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW2_SEL, unknown_val_tbl,
+ 0, 0, 0, 0, 0, 0),
+ CPCAP_REG(SW3, CPCAP_REG_S3C, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW3_SEL, unknown_val_tbl,
+ 0, 0, 0, 0, 0, 0),
+ CPCAP_REG(SW4, CPCAP_REG_S4C1, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW4_SEL, unknown_val_tbl,
+ 0, 0, 0, 0, 0, 0),
+ CPCAP_REG(SW5, CPCAP_REG_S5C, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW5_SEL, sw5_val_tbl,
+ 0x28, 0, 0, 0x20 | CPCAP_REG_OFF_MODE_SEC, 0, 0),
+ CPCAP_REG(SW6, CPCAP_REG_S6C, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW6_SEL, unknown_val_tbl,
+ 0, 0, 0, 0, 0, 0),
+ CPCAP_REG(VCAM, CPCAP_REG_VCAMC, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_VCAM_SEL, vcam_val_tbl,
+ 0x87, 0x30, 4, 0x3, 0, 420),
+ CPCAP_REG(VCSI, CPCAP_REG_VCSIC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VCSI_SEL, vcsi_val_tbl,
+ 0x47, 0x10, 4, 0x43, 0x41, 350),
+ CPCAP_REG(VDAC, CPCAP_REG_VDACC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VDAC_SEL, vdac_val_tbl,
+ 0x87, 0x30, 4, 0x3, 0, 420),
+ CPCAP_REG(VDIG, CPCAP_REG_VDIGC, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_VDIG_SEL, vdig_val_tbl,
+ 0x87, 0x30, 4, 0x82, 0, 420),
+ CPCAP_REG(VFUSE, CPCAP_REG_VFUSEC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VFUSE_SEL, vfuse_val_tbl,
+ 0x80, 0xf, 0, 0x80, 0, 420),
+ CPCAP_REG(VHVIO, CPCAP_REG_VHVIOC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VHVIO_SEL, vhvio_val_tbl,
+ 0x17, 0, 0, 0, 0x12, 0),
+ CPCAP_REG(VSDIO, CPCAP_REG_VSDIOC, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_VSDIO_SEL, vsdio_val_tbl,
+ 0x87, 0x38, 3, 0x82, 0, 420),
+ CPCAP_REG(VPLL, CPCAP_REG_VPLLC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VPLL_SEL, vpll_val_tbl,
+ 0x43, 0x18, 3, 0x2, 0, 420),
+ CPCAP_REG(VRF1, CPCAP_REG_VRF1C, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VRF1_SEL, vrf1_val_tbl,
+ 0xac, 0x2, 1, 0x4, 0, 10),
+ CPCAP_REG(VRF2, CPCAP_REG_VRF2C, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VRF2_SEL, vrf2_val_tbl,
+ 0x23, 0x8, 3, 0, 0, 10),
+ CPCAP_REG(VRFREF, CPCAP_REG_VRFREFC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VRFREF_SEL, vrfref_val_tbl,
+ 0x23, 0x8, 3, 0, 0, 420),
+ CPCAP_REG(VWLAN1, CPCAP_REG_VWLAN1C, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VWLAN1_SEL, vwlan1_val_tbl,
+ 0x47, 0x10, 4, 0, 0, 420),
+ CPCAP_REG(VWLAN2, CPCAP_REG_VWLAN2C, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VWLAN2_SEL, vwlan2_val_tbl,
+ 0x20c, 0xc0, 6, 0x20c, 0, 420),
+ CPCAP_REG(VSIM, CPCAP_REG_VSIMC, CPCAP_REG_ASSIGN3,
+ 0xffff, vsim_val_tbl,
+ 0x23, 0x8, 3, 0x3, 0, 420),
+ CPCAP_REG(VSIMCARD, CPCAP_REG_VSIMC, CPCAP_REG_ASSIGN3,
+ 0xffff, vsimcard_val_tbl,
+ 0x1e80, 0x8, 3, 0x1e00, 0, 420),
+ CPCAP_REG(VVIB, CPCAP_REG_VVIBC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VVIB_SEL, vvib_val_tbl,
+ 0x1, 0xc, 2, 0x1, 0, 500),
+ CPCAP_REG(VUSB, CPCAP_REG_VUSBC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VUSB_SEL, vusb_val_tbl,
+ 0x11c, 0x40, 6, 0xc, 0, 0),
+ CPCAP_REG(VAUDIO, CPCAP_REG_VAUDIOC, CPCAP_REG_ASSIGN4,
+ CPCAP_BIT_VAUDIO_SEL, vaudio_val_tbl,
+ 0x16, 0x1, 0, 0x4, 0, 0),
+ { /* sentinel */ },
+};
+
+static const struct of_device_id cpcap_regulator_id_table[] = {
+ {
+ .compatible = "motorola,cpcap-regulator",
+ },
+ {
+ .compatible = "motorola,mapphone-cpcap-regulator",
+ .data = omap4_regulators,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cpcap_regulator_id_table);
+
+static int cpcap_regulator_probe(struct platform_device *pdev)
+{
+ struct cpcap_ddata *ddata;
+ const struct of_device_id *match;
+ struct regulator_config config;
+ struct regulator_init_data init_data;
+ int i;
+
+ match = of_match_device(of_match_ptr(cpcap_regulator_id_table),
+ &pdev->dev);
+ if (!match)
+ return -EINVAL;
+
+ if (!match->data) {
+ dev_err(&pdev->dev, "no configuration data found\n");
+
+ return -ENODEV;
+ }
+
+ ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+ if (!ddata)
+ return -ENOMEM;
+
+ ddata->reg = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!ddata->reg)
+ return -ENODEV;
+
+ ddata->dev = &pdev->dev;
+ ddata->soc = match->data;
+ platform_set_drvdata(pdev, ddata);
+
+ memset(&config, 0, sizeof(config));
+ memset(&init_data, 0, sizeof(init_data));
+ config.dev = &pdev->dev;
+ config.regmap = ddata->reg;
+ config.init_data = &init_data;
+
+ for (i = 0; i < CPCAP_NR_REGULATORS; i++) {
+ const struct cpcap_regulator *regulator = &ddata->soc[i];
+ struct regulator_dev *rdev;
+
+ if (!regulator->rdesc.name)
+ break;
+
+ if (regulator->rdesc.volt_table == unknown_val_tbl)
+ continue;
+
+ config.driver_data = (void *)regulator;
+ rdev = devm_regulator_register(&pdev->dev,
+ ®ulator->rdesc,
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ regulator->rdesc.name);
+
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver cpcap_regulator_driver = {
+ .probe = cpcap_regulator_probe,
+ .driver = {
+ .name = "cpcap-regulator",
+ .of_match_table = of_match_ptr(cpcap_regulator_id_table),
+ },
+};
+
+module_platform_driver(cpcap_regulator_driver);
+
+MODULE_ALIAS("platform:cpcap-regulator");
+MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
+MODULE_DESCRIPTION("CPCAP regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c
index 6ec1d40..784e3bf 100644
--- a/drivers/regulator/devres.c
+++ b/drivers/regulator/devres.c
@@ -19,12 +19,6 @@
#include "internal.h"
-enum {
- NORMAL_GET,
- EXCLUSIVE_GET,
- OPTIONAL_GET,
-};
-
static void devm_regulator_release(struct device *dev, void *res)
{
regulator_put(*(struct regulator **)res);
@@ -39,20 +33,7 @@
if (!ptr)
return ERR_PTR(-ENOMEM);
- switch (get_type) {
- case NORMAL_GET:
- regulator = regulator_get(dev, id);
- break;
- case EXCLUSIVE_GET:
- regulator = regulator_get_exclusive(dev, id);
- break;
- case OPTIONAL_GET:
- regulator = regulator_get_optional(dev, id);
- break;
- default:
- regulator = ERR_PTR(-EINVAL);
- }
-
+ regulator = _regulator_get(dev, id, get_type);
if (!IS_ERR(regulator)) {
*ptr = regulator;
devres_add(dev, ptr);
@@ -139,6 +120,18 @@
}
EXPORT_SYMBOL_GPL(devm_regulator_put);
+struct regulator_bulk_devres {
+ struct regulator_bulk_data *consumers;
+ int num_consumers;
+};
+
+static void devm_regulator_bulk_release(struct device *dev, void *res)
+{
+ struct regulator_bulk_devres *devres = res;
+
+ regulator_bulk_free(devres->num_consumers, devres->consumers);
+}
+
/**
* devm_regulator_bulk_get - managed get multiple regulator consumers
*
@@ -157,30 +150,23 @@
int devm_regulator_bulk_get(struct device *dev, int num_consumers,
struct regulator_bulk_data *consumers)
{
- int i;
+ struct regulator_bulk_devres *devres;
int ret;
- for (i = 0; i < num_consumers; i++)
- consumers[i].consumer = NULL;
+ devres = devres_alloc(devm_regulator_bulk_release,
+ sizeof(*devres), GFP_KERNEL);
+ if (!devres)
+ return -ENOMEM;
- for (i = 0; i < num_consumers; i++) {
- consumers[i].consumer = devm_regulator_get(dev,
- consumers[i].supply);
- if (IS_ERR(consumers[i].consumer)) {
- ret = PTR_ERR(consumers[i].consumer);
- dev_err(dev, "Failed to get supply '%s': %d\n",
- consumers[i].supply, ret);
- consumers[i].consumer = NULL;
- goto err;
- }
+ ret = regulator_bulk_get(dev, num_consumers, consumers);
+ if (!ret) {
+ devres->consumers = consumers;
+ devres->num_consumers = num_consumers;
+ devres_add(dev, devres);
+ } else {
+ devres_free(devres);
}
- return 0;
-
-err:
- for (i = 0; i < num_consumers && consumers[i].consumer; i++)
- devm_regulator_put(consumers[i].consumer);
-
return ret;
}
EXPORT_SYMBOL_GPL(devm_regulator_bulk_get);
diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
index d7da81a..60f4318 100644
--- a/drivers/regulator/fan53555.c
+++ b/drivers/regulator/fan53555.c
@@ -202,7 +202,7 @@
CTL_SLEW_MASK, regval << CTL_SLEW_SHIFT);
}
-static struct regulator_ops fan53555_regulator_ops = {
+static const struct regulator_ops fan53555_regulator_ops = {
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
diff --git a/drivers/regulator/hi655x-regulator.c b/drivers/regulator/hi655x-regulator.c
index aca1846..065c100 100644
--- a/drivers/regulator/hi655x-regulator.c
+++ b/drivers/regulator/hi655x-regulator.c
@@ -96,7 +96,7 @@
return ret;
}
-static struct regulator_ops hi655x_regulator_ops = {
+static const struct regulator_ops hi655x_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = hi655x_disable,
.is_enabled = hi655x_is_enabled,
@@ -105,7 +105,7 @@
.set_voltage_sel = regulator_set_voltage_sel_regmap,
};
-static struct regulator_ops hi655x_ldo_linear_ops = {
+static const struct regulator_ops hi655x_ldo_linear_ops = {
.enable = regulator_enable_regmap,
.disable = hi655x_disable,
.is_enabled = hi655x_is_enabled,
diff --git a/drivers/regulator/internal.h b/drivers/regulator/internal.h
index c74ac87..1dd575b 100644
--- a/drivers/regulator/internal.h
+++ b/drivers/regulator/internal.h
@@ -51,4 +51,14 @@
}
#endif
+enum regulator_get_type {
+ NORMAL_GET,
+ EXCLUSIVE_GET,
+ OPTIONAL_GET,
+ MAX_GET_TYPE
+};
+
+struct regulator *_regulator_get(struct device *dev, const char *id,
+ enum regulator_get_type get_type);
+
#endif
diff --git a/drivers/regulator/lp8755.c b/drivers/regulator/lp8755.c
index d6773da..db34e1d 100644
--- a/drivers/regulator/lp8755.c
+++ b/drivers/regulator/lp8755.c
@@ -227,7 +227,7 @@
return ret;
}
-static struct regulator_ops lp8755_buck_ops = {
+static const struct regulator_ops lp8755_buck_ops = {
.map_voltage = regulator_map_voltage_linear,
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
diff --git a/drivers/regulator/ltc3589.c b/drivers/regulator/ltc3589.c
index 47bef32..a7a1a03 100644
--- a/drivers/regulator/ltc3589.c
+++ b/drivers/regulator/ltc3589.c
@@ -161,7 +161,7 @@
}
/* SW1, SW2, SW3, LDO2 */
-static struct regulator_ops ltc3589_linear_regulator_ops = {
+static const struct regulator_ops ltc3589_linear_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
@@ -175,18 +175,18 @@
};
/* BB_OUT, LDO3 */
-static struct regulator_ops ltc3589_fixed_regulator_ops = {
+static const struct regulator_ops ltc3589_fixed_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
};
/* LDO1 */
-static struct regulator_ops ltc3589_fixed_standby_regulator_ops = {
+static const struct regulator_ops ltc3589_fixed_standby_regulator_ops = {
};
/* LDO4 */
-static struct regulator_ops ltc3589_table_regulator_ops = {
+static const struct regulator_ops ltc3589_table_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
diff --git a/drivers/regulator/ltc3676.c b/drivers/regulator/ltc3676.c
index e2b476c..503cd90 100644
--- a/drivers/regulator/ltc3676.c
+++ b/drivers/regulator/ltc3676.c
@@ -161,7 +161,7 @@
}
/* SW1, SW2, SW3, SW4 linear 0.8V-3.3V with scalar via R1/R2 feeback res */
-static struct regulator_ops ltc3676_linear_regulator_ops = {
+static const struct regulator_ops ltc3676_linear_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
@@ -173,11 +173,11 @@
};
/* LDO1 always on fixed 0.8V-3.3V via scalar via R1/R2 feeback res */
-static struct regulator_ops ltc3676_fixed_standby_regulator_ops = {
+static const struct regulator_ops ltc3676_fixed_standby_regulator_ops = {
};
/* LDO2, LDO3 fixed (LDO2 has external scalar via R1/R2 feedback res) */
-static struct regulator_ops ltc3676_fixed_regulator_ops = {
+static const struct regulator_ops ltc3676_fixed_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
diff --git a/drivers/regulator/max14577-regulator.c b/drivers/regulator/max14577-regulator.c
index c9ff261..0db288c 100644
--- a/drivers/regulator/max14577-regulator.c
+++ b/drivers/regulator/max14577-regulator.c
@@ -85,14 +85,14 @@
reg_data);
}
-static struct regulator_ops max14577_safeout_ops = {
+static const struct regulator_ops max14577_safeout_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.list_voltage = regulator_list_voltage_linear,
};
-static struct regulator_ops max14577_charger_ops = {
+static const struct regulator_ops max14577_charger_ops = {
.is_enabled = max14577_reg_is_enabled,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
@@ -130,7 +130,7 @@
[MAX14577_CHARGER] = MAX14577_CHARGER_REG,
};
-static struct regulator_ops max77836_ldo_ops = {
+static const struct regulator_ops max77836_ldo_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
diff --git a/drivers/regulator/max77620-regulator.c b/drivers/regulator/max77620-regulator.c
index d088a7c..b94e3a7 100644
--- a/drivers/regulator/max77620-regulator.c
+++ b/drivers/regulator/max77620-regulator.c
@@ -644,7 +644,7 @@
return max77620_init_pmic(pmic, desc->id);
}
-static struct regulator_ops max77620_regulator_ops = {
+static const struct regulator_ops max77620_regulator_ops = {
.is_enabled = max77620_regulator_is_enabled,
.enable = max77620_regulator_enable,
.disable = max77620_regulator_disable,
diff --git a/drivers/regulator/max77686-regulator.c b/drivers/regulator/max77686-regulator.c
index ac4fa58..c301f37 100644
--- a/drivers/regulator/max77686-regulator.c
+++ b/drivers/regulator/max77686-regulator.c
@@ -289,7 +289,7 @@
return 0;
}
-static struct regulator_ops max77686_ops = {
+static const struct regulator_ops max77686_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
@@ -301,7 +301,7 @@
.set_suspend_mode = max77686_set_suspend_mode,
};
-static struct regulator_ops max77686_ldo_ops = {
+static const struct regulator_ops max77686_ldo_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
@@ -314,7 +314,7 @@
.set_suspend_disable = max77686_set_suspend_disable,
};
-static struct regulator_ops max77686_buck1_ops = {
+static const struct regulator_ops max77686_buck1_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
@@ -326,7 +326,7 @@
.set_suspend_disable = max77686_set_suspend_disable,
};
-static struct regulator_ops max77686_buck_dvs_ops = {
+static const struct regulator_ops max77686_buck_dvs_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
diff --git a/drivers/regulator/max77693-regulator.c b/drivers/regulator/max77693-regulator.c
index cfbb951..3fce679 100644
--- a/drivers/regulator/max77693-regulator.c
+++ b/drivers/regulator/max77693-regulator.c
@@ -141,7 +141,7 @@
3300000,
};
-static struct regulator_ops max77693_safeout_ops = {
+static const struct regulator_ops max77693_safeout_ops = {
.list_voltage = regulator_list_voltage_table,
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
diff --git a/drivers/regulator/max77802-regulator.c b/drivers/regulator/max77802-regulator.c
index 1d35393..b626190 100644
--- a/drivers/regulator/max77802-regulator.c
+++ b/drivers/regulator/max77802-regulator.c
@@ -288,7 +288,7 @@
/*
* LDOs 2, 4-19, 22-35
*/
-static struct regulator_ops max77802_ldo_ops_logic1 = {
+static const struct regulator_ops max77802_ldo_ops_logic1 = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
@@ -304,7 +304,7 @@
/*
* LDOs 1, 20, 21, 3
*/
-static struct regulator_ops max77802_ldo_ops_logic2 = {
+static const struct regulator_ops max77802_ldo_ops_logic2 = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
@@ -319,7 +319,7 @@
};
/* BUCKS 1, 6 */
-static struct regulator_ops max77802_buck_16_dvs_ops = {
+static const struct regulator_ops max77802_buck_16_dvs_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
@@ -333,7 +333,7 @@
};
/* BUCKs 2-4 */
-static struct regulator_ops max77802_buck_234_ops = {
+static const struct regulator_ops max77802_buck_234_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
@@ -348,7 +348,7 @@
};
/* BUCKs 5, 7-10 */
-static struct regulator_ops max77802_buck_dvs_ops = {
+static const struct regulator_ops max77802_buck_dvs_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
diff --git a/drivers/regulator/max8907-regulator.c b/drivers/regulator/max8907-regulator.c
index 5e941db..860400d 100644
--- a/drivers/regulator/max8907-regulator.c
+++ b/drivers/regulator/max8907-regulator.c
@@ -109,7 +109,7 @@
#define LDO_650_25(id, supply, base) REG_LDO(id, supply, (base), \
650000, 2225000, 25000)
-static struct regulator_ops max8907_mbatt_ops = {
+static const struct regulator_ops max8907_mbatt_ops = {
};
static struct regulator_ops max8907_ldo_ops = {
@@ -121,13 +121,13 @@
.is_enabled = regulator_is_enabled_regmap,
};
-static struct regulator_ops max8907_ldo_hwctl_ops = {
+static const struct regulator_ops max8907_ldo_hwctl_ops = {
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
};
-static struct regulator_ops max8907_fixed_ops = {
+static const struct regulator_ops max8907_fixed_ops = {
.list_voltage = regulator_list_voltage_linear,
};
@@ -138,11 +138,11 @@
.is_enabled = regulator_is_enabled_regmap,
};
-static struct regulator_ops max8907_out5v_hwctl_ops = {
+static const struct regulator_ops max8907_out5v_hwctl_ops = {
.list_voltage = regulator_list_voltage_linear,
};
-static struct regulator_ops max8907_bbat_ops = {
+static const struct regulator_ops max8907_bbat_ops = {
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
diff --git a/drivers/regulator/max8925-regulator.c b/drivers/regulator/max8925-regulator.c
index c802f02..39b63dd 100644
--- a/drivers/regulator/max8925-regulator.c
+++ b/drivers/regulator/max8925-regulator.c
@@ -132,7 +132,7 @@
return max8925_set_bits(info->i2c, info->vol_reg, 1 << SD1_DVM_EN, 0);
}
-static struct regulator_ops max8925_regulator_sdv_ops = {
+static const struct regulator_ops max8925_regulator_sdv_ops = {
.map_voltage = regulator_map_voltage_linear,
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = max8925_set_voltage_sel,
@@ -145,7 +145,7 @@
.set_suspend_disable = max8925_set_dvm_disable,
};
-static struct regulator_ops max8925_regulator_ldo_ops = {
+static const struct regulator_ops max8925_regulator_ldo_ops = {
.map_voltage = regulator_map_voltage_linear,
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = max8925_set_voltage_sel,
diff --git a/drivers/regulator/max8952.c b/drivers/regulator/max8952.c
index 1af8f4a..1096546 100644
--- a/drivers/regulator/max8952.c
+++ b/drivers/regulator/max8952.c
@@ -113,7 +113,7 @@
return 0;
}
-static struct regulator_ops max8952_ops = {
+static const struct regulator_ops max8952_ops = {
.list_voltage = max8952_list_voltage,
.get_voltage_sel = max8952_get_voltage_sel,
.set_voltage_sel = max8952_set_voltage_sel,
diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c
index f11d41d..31ae5ee 100644
--- a/drivers/regulator/palmas-regulator.c
+++ b/drivers/regulator/palmas-regulator.c
@@ -528,7 +528,7 @@
return ret;
}
-static struct regulator_ops palmas_ops_smps = {
+static const struct regulator_ops palmas_ops_smps = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
@@ -542,7 +542,7 @@
.set_ramp_delay = palmas_smps_set_ramp_delay,
};
-static struct regulator_ops palmas_ops_ext_control_smps = {
+static const struct regulator_ops palmas_ops_ext_control_smps = {
.set_mode = palmas_set_mode_smps,
.get_mode = palmas_get_mode_smps,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
@@ -553,7 +553,7 @@
.set_ramp_delay = palmas_smps_set_ramp_delay,
};
-static struct regulator_ops palmas_ops_smps10 = {
+static const struct regulator_ops palmas_ops_smps10 = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
@@ -565,7 +565,7 @@
.get_bypass = regulator_get_bypass_regmap,
};
-static struct regulator_ops tps65917_ops_smps = {
+static const struct regulator_ops tps65917_ops_smps = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
@@ -578,7 +578,7 @@
.set_voltage_time_sel = regulator_set_voltage_time_sel,
};
-static struct regulator_ops tps65917_ops_ext_control_smps = {
+static const struct regulator_ops tps65917_ops_ext_control_smps = {
.set_mode = palmas_set_mode_smps,
.get_mode = palmas_get_mode_smps,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
@@ -602,7 +602,7 @@
return !!(reg);
}
-static struct regulator_ops palmas_ops_ldo = {
+static const struct regulator_ops palmas_ops_ldo = {
.is_enabled = palmas_is_enabled_ldo,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
@@ -612,7 +612,7 @@
.map_voltage = regulator_map_voltage_linear,
};
-static struct regulator_ops palmas_ops_ldo9 = {
+static const struct regulator_ops palmas_ops_ldo9 = {
.is_enabled = palmas_is_enabled_ldo,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
@@ -624,23 +624,23 @@
.get_bypass = regulator_get_bypass_regmap,
};
-static struct regulator_ops palmas_ops_ext_control_ldo = {
+static const struct regulator_ops palmas_ops_ext_control_ldo = {
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
};
-static struct regulator_ops palmas_ops_extreg = {
+static const struct regulator_ops palmas_ops_extreg = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
};
-static struct regulator_ops palmas_ops_ext_control_extreg = {
+static const struct regulator_ops palmas_ops_ext_control_extreg = {
};
-static struct regulator_ops tps65917_ops_ldo = {
+static const struct regulator_ops tps65917_ops_ldo = {
.is_enabled = palmas_is_enabled_ldo,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
@@ -651,7 +651,7 @@
.set_voltage_time_sel = regulator_set_voltage_time_sel,
};
-static struct regulator_ops tps65917_ops_ldo_1_2 = {
+static const struct regulator_ops tps65917_ops_ldo_1_2 = {
.is_enabled = palmas_is_enabled_ldo,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
diff --git a/drivers/regulator/pbias-regulator.c b/drivers/regulator/pbias-regulator.c
index f9d74d6..0cb76ba 100644
--- a/drivers/regulator/pbias-regulator.c
+++ b/drivers/regulator/pbias-regulator.c
@@ -54,7 +54,7 @@
3000000
};
-static struct regulator_ops pbias_regulator_voltage_ops = {
+static const struct regulator_ops pbias_regulator_voltage_ops = {
.list_voltage = regulator_list_voltage_table,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
diff --git a/drivers/regulator/pcap-regulator.c b/drivers/regulator/pcap-regulator.c
index 9b16e61..79cb971 100644
--- a/drivers/regulator/pcap-regulator.c
+++ b/drivers/regulator/pcap-regulator.c
@@ -210,7 +210,7 @@
return (tmp >> vreg->en) & 1;
}
-static struct regulator_ops pcap_regulator_ops = {
+static const struct regulator_ops pcap_regulator_ops = {
.list_voltage = regulator_list_voltage_table,
.set_voltage_sel = pcap_regulator_set_voltage_sel,
.get_voltage_sel = pcap_regulator_get_voltage_sel,
diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c
index 134f90e..762e184 100644
--- a/drivers/regulator/pcf50633-regulator.c
+++ b/drivers/regulator/pcf50633-regulator.c
@@ -41,7 +41,7 @@
.enable_mask = PCF50633_REGULATOR_ON, \
}
-static struct regulator_ops pcf50633_regulator_ops = {
+static const struct regulator_ops pcf50633_regulator_ops = {
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.list_voltage = regulator_list_voltage_linear,
diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c
index cb18b5c..e193bbb 100644
--- a/drivers/regulator/pfuze100-regulator.c
+++ b/drivers/regulator/pfuze100-regulator.c
@@ -126,7 +126,7 @@
return ret;
}
-static struct regulator_ops pfuze100_ldo_regulator_ops = {
+static const struct regulator_ops pfuze100_ldo_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
@@ -135,14 +135,14 @@
.get_voltage_sel = regulator_get_voltage_sel_regmap,
};
-static struct regulator_ops pfuze100_fixed_regulator_ops = {
+static const struct regulator_ops pfuze100_fixed_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
};
-static struct regulator_ops pfuze100_sw_regulator_ops = {
+static const struct regulator_ops pfuze100_sw_regulator_ops = {
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
@@ -150,7 +150,7 @@
.set_ramp_delay = pfuze100_set_ramp_delay,
};
-static struct regulator_ops pfuze100_swb_regulator_ops = {
+static const struct regulator_ops pfuze100_swb_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.list_voltage = regulator_list_voltage_table,
diff --git a/drivers/regulator/pv88060-regulator.c b/drivers/regulator/pv88060-regulator.c
index 6c4afc7..a944605 100644
--- a/drivers/regulator/pv88060-regulator.c
+++ b/drivers/regulator/pv88060-regulator.c
@@ -162,7 +162,7 @@
return info->current_limits[data];
}
-static struct regulator_ops pv88060_buck_ops = {
+static const struct regulator_ops pv88060_buck_ops = {
.get_mode = pv88060_buck_get_mode,
.set_mode = pv88060_buck_set_mode,
.enable = regulator_enable_regmap,
@@ -175,7 +175,7 @@
.get_current_limit = pv88060_get_current_limit,
};
-static struct regulator_ops pv88060_ldo_ops = {
+static const struct regulator_ops pv88060_ldo_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
diff --git a/drivers/regulator/pv88080-regulator.c b/drivers/regulator/pv88080-regulator.c
index 954a20e..9a08cb2 100644
--- a/drivers/regulator/pv88080-regulator.c
+++ b/drivers/regulator/pv88080-regulator.c
@@ -306,7 +306,7 @@
return info->current_limits[data];
}
-static struct regulator_ops pv88080_buck_ops = {
+static const struct regulator_ops pv88080_buck_ops = {
.get_mode = pv88080_buck_get_mode,
.set_mode = pv88080_buck_set_mode,
.enable = regulator_enable_regmap,
@@ -319,7 +319,7 @@
.get_current_limit = pv88080_get_current_limit,
};
-static struct regulator_ops pv88080_hvbuck_ops = {
+static const struct regulator_ops pv88080_hvbuck_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
diff --git a/drivers/regulator/pv88090-regulator.c b/drivers/regulator/pv88090-regulator.c
index 4216411..ab51e25 100644
--- a/drivers/regulator/pv88090-regulator.c
+++ b/drivers/regulator/pv88090-regulator.c
@@ -184,7 +184,7 @@
return info->current_limits[data];
}
-static struct regulator_ops pv88090_buck_ops = {
+static const struct regulator_ops pv88090_buck_ops = {
.get_mode = pv88090_buck_get_mode,
.set_mode = pv88090_buck_set_mode,
.enable = regulator_enable_regmap,
@@ -197,7 +197,7 @@
.get_current_limit = pv88090_get_current_limit,
};
-static struct regulator_ops pv88090_ldo_ops = {
+static const struct regulator_ops pv88090_ldo_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c
index 8ed46a9..f35994a 100644
--- a/drivers/regulator/qcom_smd-regulator.c
+++ b/drivers/regulator/qcom_smd-regulator.c
@@ -305,6 +305,56 @@
.ops = &rpm_smps_ldo_ops,
};
+static const struct regulator_desc pm8994_hfsmps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE( 375000, 0, 95, 12500),
+ REGULATOR_LINEAR_RANGE(1550000, 96, 158, 25000),
+ },
+ .n_linear_ranges = 2,
+ .n_voltages = 159,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8994_ftsmps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(350000, 0, 199, 5000),
+ REGULATOR_LINEAR_RANGE(700000, 200, 349, 10000),
+ },
+ .n_linear_ranges = 2,
+ .n_voltages = 350,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8994_nldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(750000, 0, 63, 12500),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 64,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8994_pldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE( 750000, 0, 63, 12500),
+ REGULATOR_LINEAR_RANGE(1550000, 64, 126, 25000),
+ REGULATOR_LINEAR_RANGE(3100000, 127, 163, 50000),
+ },
+ .n_linear_ranges = 3,
+ .n_voltages = 164,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8994_switch = {
+ .ops = &rpm_switch_ops,
+};
+
+static const struct regulator_desc pm8994_lnldo = {
+ .fixed_uV = 1740000,
+ .n_voltages = 1,
+ .ops = &rpm_smps_ldo_ops_fixed,
+};
+
struct rpm_regulator_data {
const char *name;
u32 type;
@@ -443,10 +493,62 @@
{}
};
+static const struct rpm_regulator_data rpm_pm8994_regulators[] = {
+ { "s1", QCOM_SMD_RPM_SMPA, 1, &pm8994_ftsmps, "vdd_s1" },
+ { "s2", QCOM_SMD_RPM_SMPA, 2, &pm8994_ftsmps, "vdd_s2" },
+ { "s3", QCOM_SMD_RPM_SMPA, 3, &pm8994_hfsmps, "vdd_s3" },
+ { "s4", QCOM_SMD_RPM_SMPA, 4, &pm8994_hfsmps, "vdd_s4" },
+ { "s5", QCOM_SMD_RPM_SMPA, 5, &pm8994_hfsmps, "vdd_s5" },
+ { "s6", QCOM_SMD_RPM_SMPA, 6, &pm8994_ftsmps, "vdd_s6" },
+ { "s7", QCOM_SMD_RPM_SMPA, 7, &pm8994_hfsmps, "vdd_s7" },
+ { "s8", QCOM_SMD_RPM_SMPA, 8, &pm8994_ftsmps, "vdd_s8" },
+ { "s9", QCOM_SMD_RPM_SMPA, 9, &pm8994_ftsmps, "vdd_s9" },
+ { "s10", QCOM_SMD_RPM_SMPA, 10, &pm8994_ftsmps, "vdd_s10" },
+ { "s11", QCOM_SMD_RPM_SMPA, 11, &pm8994_ftsmps, "vdd_s11" },
+ { "s12", QCOM_SMD_RPM_SMPA, 12, &pm8994_ftsmps, "vdd_s12" },
+ { "l1", QCOM_SMD_RPM_LDOA, 1, &pm8994_nldo, "vdd_l1" },
+ { "l2", QCOM_SMD_RPM_LDOA, 2, &pm8994_nldo, "vdd_l2_l26_l28" },
+ { "l3", QCOM_SMD_RPM_LDOA, 3, &pm8994_nldo, "vdd_l3_l11" },
+ { "l4", QCOM_SMD_RPM_LDOA, 4, &pm8994_nldo, "vdd_l4_l27_l31" },
+ { "l5", QCOM_SMD_RPM_LDOA, 5, &pm8994_lnldo, "vdd_l5_l7" },
+ { "l6", QCOM_SMD_RPM_LDOA, 6, &pm8994_pldo, "vdd_l6_l12_l32" },
+ { "l7", QCOM_SMD_RPM_LDOA, 7, &pm8994_lnldo, "vdd_l5_l7" },
+ { "l8", QCOM_SMD_RPM_LDOA, 8, &pm8994_pldo, "vdd_l8_l16_l30" },
+ { "l9", QCOM_SMD_RPM_LDOA, 9, &pm8994_pldo, "vdd_l9_l10_l18_l22" },
+ { "l10", QCOM_SMD_RPM_LDOA, 10, &pm8994_pldo, "vdd_l9_l10_l18_l22" },
+ { "l11", QCOM_SMD_RPM_LDOA, 11, &pm8994_nldo, "vdd_l3_l11" },
+ { "l12", QCOM_SMD_RPM_LDOA, 12, &pm8994_pldo, "vdd_l6_l12_l32" },
+ { "l13", QCOM_SMD_RPM_LDOA, 13, &pm8994_pldo, "vdd_l13_l19_l23_l24" },
+ { "l14", QCOM_SMD_RPM_LDOA, 14, &pm8994_pldo, "vdd_l14_l15" },
+ { "l15", QCOM_SMD_RPM_LDOA, 15, &pm8994_pldo, "vdd_l14_l15" },
+ { "l16", QCOM_SMD_RPM_LDOA, 16, &pm8994_pldo, "vdd_l8_l16_l30" },
+ { "l17", QCOM_SMD_RPM_LDOA, 17, &pm8994_pldo, "vdd_l17_l29" },
+ { "l18", QCOM_SMD_RPM_LDOA, 18, &pm8994_pldo, "vdd_l9_l10_l18_l22" },
+ { "l19", QCOM_SMD_RPM_LDOA, 19, &pm8994_pldo, "vdd_l13_l19_l23_l24" },
+ { "l20", QCOM_SMD_RPM_LDOA, 20, &pm8994_pldo, "vdd_l20_l21" },
+ { "l21", QCOM_SMD_RPM_LDOA, 21, &pm8994_pldo, "vdd_l20_l21" },
+ { "l22", QCOM_SMD_RPM_LDOA, 22, &pm8994_pldo, "vdd_l9_l10_l18_l22" },
+ { "l23", QCOM_SMD_RPM_LDOA, 23, &pm8994_pldo, "vdd_l13_l19_l23_l24" },
+ { "l24", QCOM_SMD_RPM_LDOA, 24, &pm8994_pldo, "vdd_l13_l19_l23_l24" },
+ { "l25", QCOM_SMD_RPM_LDOA, 25, &pm8994_pldo, "vdd_l25" },
+ { "l26", QCOM_SMD_RPM_LDOA, 26, &pm8994_nldo, "vdd_l2_l26_l28" },
+ { "l27", QCOM_SMD_RPM_LDOA, 27, &pm8994_nldo, "vdd_l4_l27_l31" },
+ { "l28", QCOM_SMD_RPM_LDOA, 28, &pm8994_nldo, "vdd_l2_l26_l28" },
+ { "l29", QCOM_SMD_RPM_LDOA, 29, &pm8994_pldo, "vdd_l17_l29" },
+ { "l30", QCOM_SMD_RPM_LDOA, 30, &pm8994_pldo, "vdd_l8_l16_l30" },
+ { "l31", QCOM_SMD_RPM_LDOA, 31, &pm8994_nldo, "vdd_l4_l27_l31" },
+ { "l32", QCOM_SMD_RPM_LDOA, 32, &pm8994_pldo, "vdd_l6_l12_l32" },
+ { "lvs1", QCOM_SMD_RPM_VSA, 1, &pm8994_switch, "vdd_lvs1_2" },
+ { "lvs2", QCOM_SMD_RPM_VSA, 2, &pm8994_switch, "vdd_lvs1_2" },
+
+ {}
+};
+
static const struct of_device_id rpm_of_match[] = {
{ .compatible = "qcom,rpm-pm8841-regulators", .data = &rpm_pm8841_regulators },
{ .compatible = "qcom,rpm-pm8916-regulators", .data = &rpm_pm8916_regulators },
{ .compatible = "qcom,rpm-pm8941-regulators", .data = &rpm_pm8941_regulators },
+ { .compatible = "qcom,rpm-pm8994-regulators", .data = &rpm_pm8994_regulators },
{ .compatible = "qcom,rpm-pma8084-regulators", .data = &rpm_pma8084_regulators },
{}
};
diff --git a/drivers/regulator/rc5t583-regulator.c b/drivers/regulator/rc5t583-regulator.c
index d2e67c5..d0f1340 100644
--- a/drivers/regulator/rc5t583-regulator.c
+++ b/drivers/regulator/rc5t583-regulator.c
@@ -61,7 +61,7 @@
return DIV_ROUND_UP(curr_uV, reg->reg_info->enable_uv_per_us);
}
-static struct regulator_ops rc5t583_ops = {
+static const struct regulator_ops rc5t583_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
diff --git a/drivers/regulator/rn5t618-regulator.c b/drivers/regulator/rn5t618-regulator.c
index 9c930eb..8d2819e 100644
--- a/drivers/regulator/rn5t618-regulator.c
+++ b/drivers/regulator/rn5t618-regulator.c
@@ -19,7 +19,7 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
-static struct regulator_ops rn5t618_reg_ops = {
+static const struct regulator_ops rn5t618_reg_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
diff --git a/drivers/regulator/s2mpa01.c b/drivers/regulator/s2mpa01.c
index 92f8875..38ee97a 100644
--- a/drivers/regulator/s2mpa01.c
+++ b/drivers/regulator/s2mpa01.c
@@ -26,6 +26,7 @@
#define S2MPA01_REGULATOR_CNT ARRAY_SIZE(regulators)
struct s2mpa01_info {
+ struct of_regulator_match rdata[S2MPA01_REGULATOR_MAX];
int ramp_delay24;
int ramp_delay3;
int ramp_delay5;
@@ -341,9 +342,9 @@
{
struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
struct sec_platform_data *pdata = dev_get_platdata(iodev->dev);
- struct of_regulator_match rdata[S2MPA01_REGULATOR_MAX] = { };
struct device_node *reg_np = NULL;
struct regulator_config config = { };
+ struct of_regulator_match *rdata;
struct s2mpa01_info *s2mpa01;
int i;
@@ -351,6 +352,7 @@
if (!s2mpa01)
return -ENOMEM;
+ rdata = s2mpa01->rdata;
for (i = 0; i < S2MPA01_REGULATOR_CNT; i++)
rdata[i].name = regulators[i].name;
diff --git a/drivers/regulator/tps65086-regulator.c b/drivers/regulator/tps65086-regulator.c
index ecb0371..45e96e1 100644
--- a/drivers/regulator/tps65086-regulator.c
+++ b/drivers/regulator/tps65086-regulator.c
@@ -157,19 +157,19 @@
VDOA23_VID_MASK, TPS65086_LDOA3CTRL, BIT(0),
tps65086_ldoa23_ranges, 0, 0),
TPS65086_SWITCH("SWA1", "swa1", SWA1, TPS65086_SWVTT_EN, BIT(5)),
- TPS65086_SWITCH("SWB1", "swa2", SWB1, TPS65086_SWVTT_EN, BIT(6)),
- TPS65086_SWITCH("SWB2", "swa3", SWB2, TPS65086_SWVTT_EN, BIT(7)),
+ TPS65086_SWITCH("SWB1", "swb1", SWB1, TPS65086_SWVTT_EN, BIT(6)),
+ TPS65086_SWITCH("SWB2", "swb2", SWB2, TPS65086_SWVTT_EN, BIT(7)),
TPS65086_SWITCH("VTT", "vtt", VTT, TPS65086_SWVTT_EN, BIT(4)),
};
-static int tps65086_of_parse_cb(struct device_node *dev,
+static int tps65086_of_parse_cb(struct device_node *node,
const struct regulator_desc *desc,
struct regulator_config *config)
{
int ret;
/* Check for 25mV step mode */
- if (of_property_read_bool(config->of_node, "ti,regulator-step-size-25mv")) {
+ if (of_property_read_bool(node, "ti,regulator-step-size-25mv")) {
switch (desc->id) {
case BUCK1:
case BUCK2:
@@ -193,7 +193,7 @@
}
/* Check for decay mode */
- if (desc->id <= BUCK6 && of_property_read_bool(config->of_node, "ti,regulator-decay")) {
+ if (desc->id <= BUCK6 && of_property_read_bool(node, "ti,regulator-decay")) {
ret = regmap_write_bits(config->regmap,
regulators[desc->id].decay_reg,
regulators[desc->id].decay_mask,
diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c
index 2d12b9a..5324dc9 100644
--- a/drivers/regulator/tps65217-regulator.c
+++ b/drivers/regulator/tps65217-regulator.c
@@ -179,7 +179,8 @@
TPS65217_REGULATOR("DCDC1", TPS65217_DCDC_1, "dcdc1",
tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC1,
TPS65217_DEFDCDCX_DCDC_MASK, TPS65217_ENABLE_DC1_EN,
- NULL, tps65217_uv1_ranges, 2, TPS65217_REG_SEQ1,
+ NULL, tps65217_uv1_ranges,
+ ARRAY_SIZE(tps65217_uv1_ranges), TPS65217_REG_SEQ1,
TPS65217_SEQ1_DC1_SEQ_MASK),
TPS65217_REGULATOR("DCDC2", TPS65217_DCDC_2, "dcdc2",
tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC2,
@@ -190,7 +191,8 @@
TPS65217_REGULATOR("DCDC3", TPS65217_DCDC_3, "dcdc3",
tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC3,
TPS65217_DEFDCDCX_DCDC_MASK, TPS65217_ENABLE_DC3_EN,
- NULL, tps65217_uv1_ranges, 1, TPS65217_REG_SEQ2,
+ NULL, tps65217_uv1_ranges,
+ ARRAY_SIZE(tps65217_uv1_ranges), TPS65217_REG_SEQ2,
TPS65217_SEQ2_DC3_SEQ_MASK),
TPS65217_REGULATOR("LDO1", TPS65217_LDO_1, "ldo1",
tps65217_pmic_ldo1_ops, 16, TPS65217_REG_DEFLDO1,
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 10368ed..b6f5f1e 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -163,7 +163,7 @@
}
ret = rstc->rcdev->ops->reset(rstc->rcdev, rstc->id);
- if (rstc->shared && !ret)
+ if (rstc->shared && ret)
atomic_dec(&rstc->triggered_count);
return ret;
diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c
index f501095..898461b 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_io.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_io.c
@@ -74,7 +74,7 @@
&io_req->req_flags)) {
/* Handle internally generated ABTS timeout */
BNX2FC_IO_DBG(io_req, "ABTS timed out refcnt = %d\n",
- io_req->refcount.refcount.counter);
+ kref_read(&io_req->refcount));
if (!(test_and_set_bit(BNX2FC_FLAG_ABTS_DONE,
&io_req->req_flags))) {
/*
@@ -1141,7 +1141,7 @@
return SUCCESS;
}
BNX2FC_IO_DBG(io_req, "eh_abort - refcnt = %d\n",
- io_req->refcount.refcount.counter);
+ kref_read(&io_req->refcount));
/* Hold IO request across abort processing */
kref_get(&io_req->refcount);
@@ -1299,7 +1299,7 @@
{
BNX2FC_IO_DBG(io_req, "Entered process_cleanup_compl "
"refcnt = %d, cmd_type = %d\n",
- io_req->refcount.refcount.counter, io_req->cmd_type);
+ kref_read(&io_req->refcount), io_req->cmd_type);
bnx2fc_scsi_done(io_req, DID_ERROR);
kref_put(&io_req->refcount, bnx2fc_cmd_release);
if (io_req->wait_for_comp)
@@ -1318,7 +1318,7 @@
BNX2FC_IO_DBG(io_req, "Entered process_abts_compl xid = 0x%x"
"refcnt = %d, cmd_type = %d\n",
io_req->xid,
- io_req->refcount.refcount.counter, io_req->cmd_type);
+ kref_read(&io_req->refcount), io_req->cmd_type);
if (test_and_set_bit(BNX2FC_FLAG_ABTS_DONE,
&io_req->req_flags)) {
diff --git a/drivers/scsi/cxgbi/libcxgbi.h b/drivers/scsi/cxgbi/libcxgbi.h
index 95ba990..18e0ea8 100644
--- a/drivers/scsi/cxgbi/libcxgbi.h
+++ b/drivers/scsi/cxgbi/libcxgbi.h
@@ -301,7 +301,7 @@
{
log_debug(1 << CXGBI_DBG_SOCK,
"%s, put csk 0x%p, ref %u-1.\n",
- fn, csk, atomic_read(&csk->refcnt.refcount));
+ fn, csk, kref_read(&csk->refcnt));
kref_put(&csk->refcnt, cxgbi_sock_free);
}
#define cxgbi_sock_put(csk) __cxgbi_sock_put(__func__, csk)
@@ -310,7 +310,7 @@
{
log_debug(1 << CXGBI_DBG_SOCK,
"%s, get csk 0x%p, ref %u+1.\n",
- fn, csk, atomic_read(&csk->refcnt.refcount));
+ fn, csk, kref_read(&csk->refcnt));
kref_get(&csk->refcnt);
}
#define cxgbi_sock_get(csk) __cxgbi_sock_get(__func__, csk)
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c
index a63542b..caa7a7b 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.c
+++ b/drivers/scsi/lpfc/lpfc_debugfs.c
@@ -607,7 +607,7 @@
len += snprintf(buf+len, size-len, "usgmap:%x ",
ndlp->nlp_usg_map);
len += snprintf(buf+len, size-len, "refcnt:%x",
- atomic_read(&ndlp->kref.refcount));
+ kref_read(&ndlp->kref));
len += snprintf(buf+len, size-len, "\n");
}
spin_unlock_irq(shost->host_lock);
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 7b6bd8e..63bef45 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -3690,7 +3690,7 @@
lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE,
"0006 rpi%x DID:%x flg:%x %d map:%x %p\n",
ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag,
- atomic_read(&ndlp->kref.refcount),
+ kref_read(&ndlp->kref),
ndlp->nlp_usg_map, ndlp);
if (NLP_CHK_NODE_ACT(ndlp)) {
lpfc_nlp_put(ndlp);
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index ed22393..8204707 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -3440,7 +3440,7 @@
lpfc_printf_vlog(vport, KERN_INFO, LOG_SLI,
"0002 rpi:%x DID:%x flg:%x %d map:%x %p\n",
ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag,
- atomic_read(&ndlp->kref.refcount),
+ kref_read(&ndlp->kref),
ndlp->nlp_usg_map, ndlp);
if (ndlp->nlp_flag & NLP_REG_LOGIN_SEND)
ndlp->nlp_flag &= ~NLP_REG_LOGIN_SEND;
@@ -3861,7 +3861,7 @@
lpfc_printf_vlog(vport, KERN_INFO, LOG_SLI,
"0003 rpi:%x DID:%x flg:%x %d map%x %p\n",
ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag,
- atomic_read(&ndlp->kref.refcount),
+ kref_read(&ndlp->kref),
ndlp->nlp_usg_map, ndlp);
if (vport->port_state < LPFC_VPORT_READY) {
@@ -4238,7 +4238,7 @@
"0277 lpfc_enable_node: ndlp:x%p "
"usgmap:x%x refcnt:%d\n",
(void *)ndlp, ndlp->nlp_usg_map,
- atomic_read(&ndlp->kref.refcount));
+ kref_read(&ndlp->kref));
return NULL;
}
/* The ndlp should not already be in active mode */
@@ -4248,7 +4248,7 @@
"0278 lpfc_enable_node: ndlp:x%p "
"usgmap:x%x refcnt:%d\n",
(void *)ndlp, ndlp->nlp_usg_map,
- atomic_read(&ndlp->kref.refcount));
+ kref_read(&ndlp->kref));
return NULL;
}
@@ -4272,7 +4272,7 @@
"0008 rpi:%x DID:%x flg:%x refcnt:%d "
"map:%x %p\n", ndlp->nlp_rpi, ndlp->nlp_DID,
ndlp->nlp_flag,
- atomic_read(&ndlp->kref.refcount),
+ kref_read(&ndlp->kref),
ndlp->nlp_usg_map, ndlp);
}
@@ -4546,7 +4546,7 @@
(bf_get(lpfc_sli_intf_if_type,
&phba->sli4_hba.sli_intf) ==
LPFC_SLI_INTF_IF_TYPE_2) &&
- (atomic_read(&ndlp->kref.refcount) > 0)) {
+ (kref_read(&ndlp->kref) > 0)) {
mbox->context1 = lpfc_nlp_get(ndlp);
mbox->mbox_cmpl =
lpfc_sli4_unreg_rpi_cmpl_clr;
@@ -4695,14 +4695,14 @@
"0280 lpfc_cleanup_node: ndlp:x%p "
"usgmap:x%x refcnt:%d\n",
(void *)ndlp, ndlp->nlp_usg_map,
- atomic_read(&ndlp->kref.refcount));
+ kref_read(&ndlp->kref));
lpfc_dequeue_node(vport, ndlp);
} else {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE,
"0281 lpfc_cleanup_node: ndlp:x%p "
"usgmap:x%x refcnt:%d\n",
(void *)ndlp, ndlp->nlp_usg_map,
- atomic_read(&ndlp->kref.refcount));
+ kref_read(&ndlp->kref));
lpfc_disable_node(vport, ndlp);
}
@@ -4791,7 +4791,7 @@
lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE,
"0005 rpi:%x DID:%x flg:%x %d map:%x %p\n",
ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag,
- atomic_read(&ndlp->kref.refcount),
+ kref_read(&ndlp->kref),
ndlp->nlp_usg_map, ndlp);
if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL))
!= NULL) {
@@ -5557,7 +5557,7 @@
lpfc_printf_vlog(vport, KERN_INFO, LOG_SLI,
"0004 rpi:%x DID:%x flg:%x %d map:%x %p\n",
ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag,
- atomic_read(&ndlp->kref.refcount),
+ kref_read(&ndlp->kref),
ndlp->nlp_usg_map, ndlp);
/*
* Start issuing Fabric-Device Management Interface (FDMI) command to
@@ -5728,7 +5728,7 @@
"0007 rpi:%x DID:%x flg:%x refcnt:%d "
"map:%x %p\n", ndlp->nlp_rpi, ndlp->nlp_DID,
ndlp->nlp_flag,
- atomic_read(&ndlp->kref.refcount),
+ kref_read(&ndlp->kref),
ndlp->nlp_usg_map, ndlp);
ndlp->active_rrqs_xri_bitmap =
@@ -5767,7 +5767,7 @@
"0279 lpfc_nlp_release: ndlp:x%p did %x "
"usgmap:x%x refcnt:%d rpi:%x\n",
(void *)ndlp, ndlp->nlp_DID, ndlp->nlp_usg_map,
- atomic_read(&ndlp->kref.refcount), ndlp->nlp_rpi);
+ kref_read(&ndlp->kref), ndlp->nlp_rpi);
/* remove ndlp from action. */
lpfc_nlp_remove(ndlp->vport, ndlp);
@@ -5804,7 +5804,7 @@
lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE,
"node get: did:x%x flg:x%x refcnt:x%x",
ndlp->nlp_DID, ndlp->nlp_flag,
- atomic_read(&ndlp->kref.refcount));
+ kref_read(&ndlp->kref));
/* The check of ndlp usage to prevent incrementing the
* ndlp reference count that is in the process of being
* released.
@@ -5817,7 +5817,7 @@
"0276 lpfc_nlp_get: ndlp:x%p "
"usgmap:x%x refcnt:%d\n",
(void *)ndlp, ndlp->nlp_usg_map,
- atomic_read(&ndlp->kref.refcount));
+ kref_read(&ndlp->kref));
return NULL;
} else
kref_get(&ndlp->kref);
@@ -5844,7 +5844,7 @@
lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE,
"node put: did:x%x flg:x%x refcnt:x%x",
ndlp->nlp_DID, ndlp->nlp_flag,
- atomic_read(&ndlp->kref.refcount));
+ kref_read(&ndlp->kref));
phba = ndlp->phba;
spin_lock_irqsave(&phba->ndlp_lock, flags);
/* Check the ndlp memory free acknowledge flag to avoid the
@@ -5857,7 +5857,7 @@
"0274 lpfc_nlp_put: ndlp:x%p "
"usgmap:x%x refcnt:%d\n",
(void *)ndlp, ndlp->nlp_usg_map,
- atomic_read(&ndlp->kref.refcount));
+ kref_read(&ndlp->kref));
return 1;
}
/* Check the ndlp inactivate log flag to avoid the possible
@@ -5870,7 +5870,7 @@
"0275 lpfc_nlp_put: ndlp:x%p "
"usgmap:x%x refcnt:%d\n",
(void *)ndlp, ndlp->nlp_usg_map,
- atomic_read(&ndlp->kref.refcount));
+ kref_read(&ndlp->kref));
return 1;
}
/* For last put, mark the ndlp usage flags to make sure no
@@ -5878,7 +5878,7 @@
* in between the process when the final kref_put has been
* invoked on this ndlp.
*/
- if (atomic_read(&ndlp->kref.refcount) == 1) {
+ if (kref_read(&ndlp->kref) == 1) {
/* Indicate ndlp is put to inactive state. */
NLP_SET_IACT_REQ(ndlp);
/* Acknowledge ndlp memory free has been seen. */
@@ -5906,8 +5906,8 @@
lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE,
"node not used: did:x%x flg:x%x refcnt:x%x",
ndlp->nlp_DID, ndlp->nlp_flag,
- atomic_read(&ndlp->kref.refcount));
- if (atomic_read(&ndlp->kref.refcount) == 1)
+ kref_read(&ndlp->kref));
+ if (kref_read(&ndlp->kref) == 1)
if (lpfc_nlp_put(ndlp))
return 1;
return 0;
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 4776fd8..64717c1 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -2660,8 +2660,7 @@
"usgmap:x%x refcnt:%d\n",
ndlp->nlp_DID, (void *)ndlp,
ndlp->nlp_usg_map,
- atomic_read(
- &ndlp->kref.refcount));
+ kref_read(&ndlp->kref));
}
break;
}
diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
index d925910..3084983 100644
--- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c
+++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
@@ -371,7 +371,7 @@
*/
pr_debug("write_pending aborted cmd[%p] refcount %d "
"transport_state %x, t_state %x, se_cmd_flags %x\n",
- cmd,cmd->se_cmd.cmd_kref.refcount.counter,
+ cmd, kref_read(&cmd->se_cmd.cmd_kref),
cmd->se_cmd.transport_state,
cmd->se_cmd.t_state,
cmd->se_cmd.se_cmd_flags);
@@ -584,7 +584,7 @@
*/
pr_debug("queue_data_in aborted cmd[%p] refcount %d "
"transport_state %x, t_state %x, se_cmd_flags %x\n",
- cmd,cmd->se_cmd.cmd_kref.refcount.counter,
+ cmd, kref_read(&cmd->se_cmd.cmd_kref),
cmd->se_cmd.transport_state,
cmd->se_cmd.t_state,
cmd->se_cmd.se_cmd_flags);
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index e9e1e14..78db07f 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -1040,7 +1040,8 @@
bool is_mq = (rq->mq_ctx != NULL);
int error;
- BUG_ON(!blk_rq_nr_phys_segments(rq));
+ if (WARN_ON_ONCE(!blk_rq_nr_phys_segments(rq)))
+ return -EINVAL;
error = scsi_init_sgtable(rq, &cmd->sdb);
if (error)
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index dbe5b4b..121de0a 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -1753,6 +1753,10 @@
return res;
iov_iter_truncate(&i, hp->dxfer_len);
+ if (!iov_iter_count(&i)) {
+ kfree(iov);
+ return -EINVAL;
+ }
res = blk_rq_map_user_iov(q, rq, md, &i, GFP_ATOMIC);
kfree(iov);
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 2922a99..25ae7f2e 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -162,7 +162,8 @@
config SPI_BCM_QSPI
tristate "Broadcom BSPI and MSPI controller support"
- depends on ARCH_BRCMSTB || ARCH_BCM || ARCH_BCM_IPROC || COMPILE_TEST
+ depends on ARCH_BRCMSTB || ARCH_BCM || ARCH_BCM_IPROC || \
+ BMIPS_GENERIC || COMPILE_TEST
default ARCH_BCM_IPROC
help
Enables support for the Broadcom SPI flash and MSPI controller.
@@ -263,7 +264,7 @@
mode.
config SPI_FALCON
- tristate "Falcon SPI controller support"
+ bool "Falcon SPI controller support"
depends on SOC_FALCON
help
The external bus unit (EBU) found on the FALC-ON SoC has SPI
@@ -416,6 +417,14 @@
help
SPI driver for Nuvoton NUC900 series ARM SoCs
+config SPI_LANTIQ_SSC
+ tristate "Lantiq SSC SPI controller"
+ depends on LANTIQ || COMPILE_TEST
+ help
+ This driver supports the Lantiq SSC SPI controller in master
+ mode. This controller is found on Intel (former Lantiq) SoCs like
+ the Danube, Falcon, xRX200, xRX300.
+
config SPI_OC_TINY
tristate "OpenCores tiny SPI"
depends on GPIOLIB || COMPILE_TEST
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 7a6b646..b375a7a 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -49,6 +49,7 @@
obj-$(CONFIG_SPI_GPIO) += spi-gpio.o
obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o
obj-$(CONFIG_SPI_IMX) += spi-imx.o
+obj-$(CONFIG_SPI_LANTIQ_SSC) += spi-lantiq-ssc.o
obj-$(CONFIG_SPI_JCORE) += spi-jcore.o
obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o
obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o
diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c
index 0314c6b9..6c7d7a4 100644
--- a/drivers/spi/spi-armada-3700.c
+++ b/drivers/spi/spi-armada-3700.c
@@ -170,12 +170,12 @@
val &= ~(A3700_SPI_DATA_PIN0 | A3700_SPI_DATA_PIN1);
switch (pin_mode) {
- case 1:
+ case SPI_NBITS_SINGLE:
break;
- case 2:
+ case SPI_NBITS_DUAL:
val |= A3700_SPI_DATA_PIN0;
break;
- case 4:
+ case SPI_NBITS_QUAD:
val |= A3700_SPI_DATA_PIN1;
break;
default:
@@ -340,8 +340,7 @@
spireg_write(a3700_spi, A3700_SPI_INT_STAT_REG, cause);
/* Wake up the transfer */
- if (a3700_spi->wait_mask & cause)
- complete(&a3700_spi->done);
+ complete(&a3700_spi->done);
return IRQ_HANDLED;
}
@@ -421,7 +420,7 @@
}
static void a3700_spi_transfer_setup(struct spi_device *spi,
- struct spi_transfer *xfer)
+ struct spi_transfer *xfer)
{
struct a3700_spi *a3700_spi;
unsigned int byte_len;
@@ -562,6 +561,7 @@
val = spireg_read(a3700_spi, A3700_SPI_DATA_IN_REG);
if (a3700_spi->buf_len >= 4) {
u32 data = le32_to_cpu(val);
+
memcpy(a3700_spi->rx_buf, &data, 4);
a3700_spi->buf_len -= 4;
@@ -901,7 +901,6 @@
struct a3700_spi *spi = spi_master_get_devdata(master);
clk_unprepare(spi->clk);
- spi_master_put(master);
return 0;
}
@@ -909,7 +908,6 @@
static struct platform_driver a3700_spi_driver = {
.driver = {
.name = DRIVER_NAME,
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(a3700_spi_dt_ids),
},
.probe = a3700_spi_probe,
diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c
index f369174..b89cee1 100644
--- a/drivers/spi/spi-ath79.c
+++ b/drivers/spi/spi-ath79.c
@@ -78,14 +78,16 @@
ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
}
- if (spi->chip_select) {
+ if (gpio_is_valid(spi->cs_gpio)) {
/* SPI is normally active-low */
- gpio_set_value(spi->cs_gpio, cs_high);
+ gpio_set_value_cansleep(spi->cs_gpio, cs_high);
} else {
+ u32 cs_bit = AR71XX_SPI_IOC_CS(spi->chip_select);
+
if (cs_high)
- sp->ioc_base |= AR71XX_SPI_IOC_CS0;
+ sp->ioc_base |= cs_bit;
else
- sp->ioc_base &= ~AR71XX_SPI_IOC_CS0;
+ sp->ioc_base &= ~cs_bit;
ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
}
@@ -118,11 +120,8 @@
struct ath79_spi *sp = ath79_spidev_to_sp(spi);
int status;
- if (spi->chip_select && !gpio_is_valid(spi->cs_gpio))
- return -EINVAL;
-
status = 0;
- if (spi->chip_select) {
+ if (gpio_is_valid(spi->cs_gpio)) {
unsigned long flags;
flags = GPIOF_DIR_OUT;
@@ -134,10 +133,12 @@
status = gpio_request_one(spi->cs_gpio, flags,
dev_name(&spi->dev));
} else {
+ u32 cs_bit = AR71XX_SPI_IOC_CS(spi->chip_select);
+
if (spi->mode & SPI_CS_HIGH)
- sp->ioc_base &= ~AR71XX_SPI_IOC_CS0;
+ sp->ioc_base &= ~cs_bit;
else
- sp->ioc_base |= AR71XX_SPI_IOC_CS0;
+ sp->ioc_base |= cs_bit;
ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
}
@@ -147,7 +148,7 @@
static void ath79_spi_cleanup_cs(struct spi_device *spi)
{
- if (spi->chip_select) {
+ if (gpio_is_valid(spi->cs_gpio)) {
gpio_free(spi->cs_gpio);
}
}
diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c
index 14f9dea..b19722b 100644
--- a/drivers/spi/spi-bcm-qspi.c
+++ b/drivers/spi/spi-bcm-qspi.c
@@ -89,7 +89,7 @@
#define BSPI_BPP_MODE_SELECT_MASK BIT(8)
#define BSPI_BPP_ADDR_SELECT_MASK BIT(16)
-#define BSPI_READ_LENGTH 256
+#define BSPI_READ_LENGTH 512
/* MSPI register offsets */
#define MSPI_SPCR0_LSB 0x000
@@ -192,9 +192,11 @@
void *dev;
};
+
struct qspi_trans {
struct spi_transfer *trans;
int byte;
+ bool mspi_last_trans;
};
struct bcm_qspi {
@@ -371,7 +373,7 @@
/* default mode, does not need flex_cmd */
flex_mode = 0;
else
- command = SPINOR_OP_READ4_FAST;
+ command = SPINOR_OP_READ_FAST_4B;
break;
case SPI_NBITS_DUAL:
bpc = 0x00000001;
@@ -384,7 +386,7 @@
} else {
command = SPINOR_OP_READ_1_1_2;
if (spans_4byte)
- command = SPINOR_OP_READ4_1_1_2;
+ command = SPINOR_OP_READ_1_1_2_4B;
}
break;
case SPI_NBITS_QUAD:
@@ -399,7 +401,7 @@
} else {
command = SPINOR_OP_READ_1_1_4;
if (spans_4byte)
- command = SPINOR_OP_READ4_1_1_4;
+ command = SPINOR_OP_READ_1_1_4_4B;
}
break;
default:
@@ -616,6 +618,16 @@
return 0;
}
+static bool bcm_qspi_mspi_transfer_is_last(struct bcm_qspi *qspi,
+ struct qspi_trans *qt)
+{
+ if (qt->mspi_last_trans &&
+ spi_transfer_is_last(qspi->master, qt->trans))
+ return true;
+ else
+ return false;
+}
+
static int update_qspi_trans_byte_count(struct bcm_qspi *qspi,
struct qspi_trans *qt, int flags)
{
@@ -629,7 +641,6 @@
if (qt->byte >= qt->trans->len) {
/* we're at the end of the spi_transfer */
-
/* in TX mode, need to pause for a delay or CS change */
if (qt->trans->delay_usecs &&
(flags & TRANS_STATUS_BREAK_DELAY))
@@ -641,7 +652,7 @@
goto done;
dev_dbg(&qspi->pdev->dev, "advance msg exit\n");
- if (spi_transfer_is_last(qspi->master, qt->trans))
+ if (bcm_qspi_mspi_transfer_is_last(qspi, qt))
ret = TRANS_STATUS_BREAK_EOM;
else
ret = TRANS_STATUS_BREAK_NO_BYTES;
@@ -813,7 +824,7 @@
struct spi_flash_read_message *msg)
{
struct bcm_qspi *qspi = spi_master_get_devdata(spi->master);
- u32 addr = 0, len, len_words;
+ u32 addr = 0, len, rdlen, len_words;
int ret = 0;
unsigned long timeo = msecs_to_jiffies(100);
struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc;
@@ -826,7 +837,7 @@
bcm_qspi_write(qspi, MSPI, MSPI_WRITE_LOCK, 0);
/*
- * when using flex mode mode we need to send
+ * when using flex mode we need to send
* the upper address byte to bspi
*/
if (bcm_qspi_bspi_ver_three(qspi) == false) {
@@ -840,48 +851,127 @@
else
addr = msg->from & 0x00ffffff;
- /* set BSPI RAF buffer max read length */
- len = msg->len;
- if (len > BSPI_READ_LENGTH)
- len = BSPI_READ_LENGTH;
-
if (bcm_qspi_bspi_ver_three(qspi) == true)
addr = (addr + 0xc00000) & 0xffffff;
- reinit_completion(&qspi->bspi_done);
- bcm_qspi_enable_bspi(qspi);
- len_words = (len + 3) >> 2;
- qspi->bspi_rf_msg = msg;
- qspi->bspi_rf_msg_status = 0;
+ /*
+ * read into the entire buffer by breaking the reads
+ * into RAF buffer read lengths
+ */
+ len = msg->len;
qspi->bspi_rf_msg_idx = 0;
- qspi->bspi_rf_msg_len = len;
- dev_dbg(&qspi->pdev->dev, "bspi xfr addr 0x%x len 0x%x", addr, len);
- bcm_qspi_write(qspi, BSPI, BSPI_RAF_START_ADDR, addr);
- bcm_qspi_write(qspi, BSPI, BSPI_RAF_NUM_WORDS, len_words);
- bcm_qspi_write(qspi, BSPI, BSPI_RAF_WATERMARK, 0);
+ do {
+ if (len > BSPI_READ_LENGTH)
+ rdlen = BSPI_READ_LENGTH;
+ else
+ rdlen = len;
- if (qspi->soc_intc) {
- /*
- * clear soc MSPI and BSPI interrupts and enable
- * BSPI interrupts.
- */
- soc_intc->bcm_qspi_int_ack(soc_intc, MSPI_BSPI_DONE);
- soc_intc->bcm_qspi_int_set(soc_intc, BSPI_DONE, true);
+ reinit_completion(&qspi->bspi_done);
+ bcm_qspi_enable_bspi(qspi);
+ len_words = (rdlen + 3) >> 2;
+ qspi->bspi_rf_msg = msg;
+ qspi->bspi_rf_msg_status = 0;
+ qspi->bspi_rf_msg_len = rdlen;
+ dev_dbg(&qspi->pdev->dev,
+ "bspi xfr addr 0x%x len 0x%x", addr, rdlen);
+ bcm_qspi_write(qspi, BSPI, BSPI_RAF_START_ADDR, addr);
+ bcm_qspi_write(qspi, BSPI, BSPI_RAF_NUM_WORDS, len_words);
+ bcm_qspi_write(qspi, BSPI, BSPI_RAF_WATERMARK, 0);
+ if (qspi->soc_intc) {
+ /*
+ * clear soc MSPI and BSPI interrupts and enable
+ * BSPI interrupts.
+ */
+ soc_intc->bcm_qspi_int_ack(soc_intc, MSPI_BSPI_DONE);
+ soc_intc->bcm_qspi_int_set(soc_intc, BSPI_DONE, true);
+ }
+
+ /* Must flush previous writes before starting BSPI operation */
+ mb();
+ bcm_qspi_bspi_lr_start(qspi);
+ if (!wait_for_completion_timeout(&qspi->bspi_done, timeo)) {
+ dev_err(&qspi->pdev->dev, "timeout waiting for BSPI\n");
+ ret = -ETIMEDOUT;
+ break;
+ }
+
+ /* set msg return length */
+ msg->retlen += rdlen;
+ addr += rdlen;
+ len -= rdlen;
+ } while (len);
+
+ return ret;
+}
+
+static int bcm_qspi_transfer_one(struct spi_master *master,
+ struct spi_device *spi,
+ struct spi_transfer *trans)
+{
+ struct bcm_qspi *qspi = spi_master_get_devdata(master);
+ int slots;
+ unsigned long timeo = msecs_to_jiffies(100);
+
+ bcm_qspi_chip_select(qspi, spi->chip_select);
+ qspi->trans_pos.trans = trans;
+ qspi->trans_pos.byte = 0;
+
+ while (qspi->trans_pos.byte < trans->len) {
+ reinit_completion(&qspi->mspi_done);
+
+ slots = write_to_hw(qspi, spi);
+ if (!wait_for_completion_timeout(&qspi->mspi_done, timeo)) {
+ dev_err(&qspi->pdev->dev, "timeout waiting for MSPI\n");
+ return -ETIMEDOUT;
+ }
+
+ read_from_hw(qspi, slots);
}
- /* Must flush previous writes before starting BSPI operation */
- mb();
+ return 0;
+}
- bcm_qspi_bspi_lr_start(qspi);
- if (!wait_for_completion_timeout(&qspi->bspi_done, timeo)) {
- dev_err(&qspi->pdev->dev, "timeout waiting for BSPI\n");
- ret = -ETIMEDOUT;
- } else {
- /* set the return length for the caller */
- msg->retlen = len;
+static int bcm_qspi_mspi_flash_read(struct spi_device *spi,
+ struct spi_flash_read_message *msg)
+{
+ struct bcm_qspi *qspi = spi_master_get_devdata(spi->master);
+ struct spi_transfer t[2];
+ u8 cmd[6];
+ int ret;
+
+ memset(cmd, 0, sizeof(cmd));
+ memset(t, 0, sizeof(t));
+
+ /* tx */
+ /* opcode is in cmd[0] */
+ cmd[0] = msg->read_opcode;
+ cmd[1] = msg->from >> (msg->addr_width * 8 - 8);
+ cmd[2] = msg->from >> (msg->addr_width * 8 - 16);
+ cmd[3] = msg->from >> (msg->addr_width * 8 - 24);
+ cmd[4] = msg->from >> (msg->addr_width * 8 - 32);
+ t[0].tx_buf = cmd;
+ t[0].len = msg->addr_width + msg->dummy_bytes + 1;
+ t[0].bits_per_word = spi->bits_per_word;
+ t[0].tx_nbits = msg->opcode_nbits;
+ /* lets mspi know that this is not last transfer */
+ qspi->trans_pos.mspi_last_trans = false;
+ ret = bcm_qspi_transfer_one(spi->master, spi, &t[0]);
+
+ /* rx */
+ qspi->trans_pos.mspi_last_trans = true;
+ if (!ret) {
+ /* rx */
+ t[1].rx_buf = msg->buf;
+ t[1].len = msg->len;
+ t[1].rx_nbits = msg->data_nbits;
+ t[1].bits_per_word = spi->bits_per_word;
+ ret = bcm_qspi_transfer_one(spi->master, spi, &t[1]);
}
+ if (!ret)
+ msg->retlen = msg->len;
+
return ret;
}
@@ -918,8 +1008,7 @@
mspi_read = true;
if (mspi_read)
- /* this will make the m25p80 read to fallback to mspi read */
- return -EAGAIN;
+ return bcm_qspi_mspi_flash_read(spi, msg);
io_width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE;
addrlen = msg->addr_width;
@@ -931,33 +1020,6 @@
return ret;
}
-static int bcm_qspi_transfer_one(struct spi_master *master,
- struct spi_device *spi,
- struct spi_transfer *trans)
-{
- struct bcm_qspi *qspi = spi_master_get_devdata(master);
- int slots;
- unsigned long timeo = msecs_to_jiffies(100);
-
- bcm_qspi_chip_select(qspi, spi->chip_select);
- qspi->trans_pos.trans = trans;
- qspi->trans_pos.byte = 0;
-
- while (qspi->trans_pos.byte < trans->len) {
- reinit_completion(&qspi->mspi_done);
-
- slots = write_to_hw(qspi, spi);
- if (!wait_for_completion_timeout(&qspi->mspi_done, timeo)) {
- dev_err(&qspi->pdev->dev, "timeout waiting for MSPI\n");
- return -ETIMEDOUT;
- }
-
- read_from_hw(qspi, slots);
- }
-
- return 0;
-}
-
static void bcm_qspi_cleanup(struct spi_device *spi)
{
struct bcm_qspi_parms *xp = spi_get_ctldata(spi);
@@ -1187,6 +1249,7 @@
qspi->pdev = pdev;
qspi->trans_pos.trans = NULL;
qspi->trans_pos.byte = 0;
+ qspi->trans_pos.mspi_last_trans = true;
qspi->master = master;
master->bus_num = -1;
@@ -1345,7 +1408,6 @@
{
struct bcm_qspi *qspi = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
bcm_qspi_hw_uninit(qspi);
clk_disable_unprepare(qspi->clk);
kfree(qspi->dev_ids);
diff --git a/drivers/spi/spi-bcm53xx.c b/drivers/spi/spi-bcm53xx.c
index afb5169..6e409ea 100644
--- a/drivers/spi/spi-bcm53xx.c
+++ b/drivers/spi/spi-bcm53xx.c
@@ -1,3 +1,11 @@
+/*
+ * Copyright (C) 2014-2016 Rafał Miłecki <rafal@milecki.pl>
+ *
+ * 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.
+ */
+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
@@ -275,10 +283,6 @@
* BCMA
**************************************************/
-static struct spi_board_info bcm53xx_info = {
- .modalias = "bcm53xxspiflash",
-};
-
static const struct bcma_device_id bcm53xxspi_bcma_tbl[] = {
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_QSPI, BCMA_ANY_REV, BCMA_ANY_CLASS),
{},
@@ -311,6 +315,7 @@
b53spi->bspi = true;
bcm53xxspi_disable_bspi(b53spi);
+ master->dev.of_node = dev->of_node;
master->transfer_one = bcm53xxspi_transfer_one;
if (b53spi->mmio_base)
master->spi_flash_read = bcm53xxspi_flash_read;
@@ -324,9 +329,6 @@
return err;
}
- /* Broadcom SoCs (at least with the CC rev 42) use SPI for flash only */
- spi_new_device(master, &bcm53xx_info);
-
return 0;
}
@@ -361,4 +363,4 @@
MODULE_DESCRIPTION("Broadcom BCM53xx SPI Controller driver");
MODULE_AUTHOR("Rafał Miłecki <zajec5@gmail.com>");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c
index 054012f..b217c22 100644
--- a/drivers/spi/spi-dw.c
+++ b/drivers/spi/spi-dw.c
@@ -107,9 +107,9 @@
static int dw_spi_debugfs_init(struct dw_spi *dws)
{
- char name[128];
+ char name[32];
- snprintf(name, 128, "dw_spi-%s", dev_name(&dws->master->dev));
+ snprintf(name, 32, "dw_spi%d", dws->master->bus_num);
dws->debugfs = debugfs_create_dir(name, NULL);
if (!dws->debugfs)
return -ENOMEM;
@@ -486,9 +486,9 @@
dws->type = SSI_MOTO_SPI;
dws->dma_inited = 0;
dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR);
- snprintf(dws->name, sizeof(dws->name), "dw_spi%d", dws->bus_num);
- ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dws->name, master);
+ ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dev_name(dev),
+ master);
if (ret < 0) {
dev_err(dev, "can not get IRQ\n");
goto err_free_master;
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index c21ca02..da5eab6 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -101,7 +101,6 @@
struct dw_spi {
struct spi_master *master;
enum dw_ssi_type type;
- char name[16];
void __iomem *regs;
unsigned long paddr;
diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c
index 17a6387..b5d7660 100644
--- a/drivers/spi/spi-ep93xx.c
+++ b/drivers/spi/spi-ep93xx.c
@@ -28,6 +28,7 @@
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/scatterlist.h>
+#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/platform_data/dma-ep93xx.h>
@@ -107,16 +108,6 @@
void *zeropage;
};
-/**
- * struct ep93xx_spi_chip - SPI device hardware settings
- * @spi: back pointer to the SPI device
- * @ops: private chip operations
- */
-struct ep93xx_spi_chip {
- const struct spi_device *spi;
- struct ep93xx_spi_chip_ops *ops;
-};
-
/* converts bits per word to CR0.DSS value */
#define bits_per_word_to_dss(bpw) ((bpw) - 1)
@@ -229,104 +220,36 @@
return -EINVAL;
}
-static void ep93xx_spi_cs_control(struct spi_device *spi, bool control)
+static void ep93xx_spi_cs_control(struct spi_device *spi, bool enable)
{
- struct ep93xx_spi_chip *chip = spi_get_ctldata(spi);
- int value = (spi->mode & SPI_CS_HIGH) ? control : !control;
+ if (spi->mode & SPI_CS_HIGH)
+ enable = !enable;
- if (chip->ops && chip->ops->cs_control)
- chip->ops->cs_control(spi, value);
+ if (gpio_is_valid(spi->cs_gpio))
+ gpio_set_value(spi->cs_gpio, !enable);
}
-/**
- * ep93xx_spi_setup() - setup an SPI device
- * @spi: SPI device to setup
- *
- * This function sets up SPI device mode, speed etc. Can be called multiple
- * times for a single device. Returns %0 in case of success, negative error in
- * case of failure. When this function returns success, the device is
- * deselected.
- */
-static int ep93xx_spi_setup(struct spi_device *spi)
-{
- struct ep93xx_spi *espi = spi_master_get_devdata(spi->master);
- struct ep93xx_spi_chip *chip;
-
- chip = spi_get_ctldata(spi);
- if (!chip) {
- dev_dbg(&espi->pdev->dev, "initial setup for %s\n",
- spi->modalias);
-
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
-
- chip->spi = spi;
- chip->ops = spi->controller_data;
-
- if (chip->ops && chip->ops->setup) {
- int ret = chip->ops->setup(spi);
-
- if (ret) {
- kfree(chip);
- return ret;
- }
- }
-
- spi_set_ctldata(spi, chip);
- }
-
- ep93xx_spi_cs_control(spi, false);
- return 0;
-}
-
-/**
- * ep93xx_spi_cleanup() - cleans up master controller specific state
- * @spi: SPI device to cleanup
- *
- * This function releases master controller specific state for given @spi
- * device.
- */
-static void ep93xx_spi_cleanup(struct spi_device *spi)
-{
- struct ep93xx_spi_chip *chip;
-
- chip = spi_get_ctldata(spi);
- if (chip) {
- if (chip->ops && chip->ops->cleanup)
- chip->ops->cleanup(spi);
- spi_set_ctldata(spi, NULL);
- kfree(chip);
- }
-}
-
-/**
- * ep93xx_spi_chip_setup() - configures hardware according to given @chip
- * @espi: ep93xx SPI controller struct
- * @chip: chip specific settings
- * @speed_hz: transfer speed
- * @bits_per_word: transfer bits_per_word
- */
static int ep93xx_spi_chip_setup(const struct ep93xx_spi *espi,
- const struct ep93xx_spi_chip *chip,
- u32 speed_hz, u8 bits_per_word)
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
{
- u8 dss = bits_per_word_to_dss(bits_per_word);
+ u8 dss = bits_per_word_to_dss(xfer->bits_per_word);
u8 div_cpsr = 0;
u8 div_scr = 0;
u16 cr0;
int err;
- err = ep93xx_spi_calc_divisors(espi, speed_hz, &div_cpsr, &div_scr);
+ err = ep93xx_spi_calc_divisors(espi, xfer->speed_hz,
+ &div_cpsr, &div_scr);
if (err)
return err;
cr0 = div_scr << SSPCR0_SCR_SHIFT;
- cr0 |= (chip->spi->mode & (SPI_CPHA|SPI_CPOL)) << SSPCR0_MODE_SHIFT;
+ cr0 |= (spi->mode & (SPI_CPHA | SPI_CPOL)) << SSPCR0_MODE_SHIFT;
cr0 |= dss;
dev_dbg(&espi->pdev->dev, "setup: mode %d, cpsr %d, scr %d, dss %d\n",
- chip->spi->mode, div_cpsr, div_scr, dss);
+ spi->mode, div_cpsr, div_scr, dss);
dev_dbg(&espi->pdev->dev, "setup: cr0 %#x\n", cr0);
ep93xx_spi_write_u8(espi, SSPCPSR, div_cpsr);
@@ -603,12 +526,11 @@
struct spi_message *msg,
struct spi_transfer *t)
{
- struct ep93xx_spi_chip *chip = spi_get_ctldata(msg->spi);
int err;
msg->state = t;
- err = ep93xx_spi_chip_setup(espi, chip, t->speed_hz, t->bits_per_word);
+ err = ep93xx_spi_chip_setup(espi, msg->spi, t);
if (err) {
dev_err(&espi->pdev->dev,
"failed to setup chip for transfer\n");
@@ -863,8 +785,13 @@
struct resource *res;
int irq;
int error;
+ int i;
info = dev_get_platdata(&pdev->dev);
+ if (!info) {
+ dev_err(&pdev->dev, "missing platform data\n");
+ return -EINVAL;
+ }
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
@@ -882,14 +809,36 @@
if (!master)
return -ENOMEM;
- master->setup = ep93xx_spi_setup;
master->transfer_one_message = ep93xx_spi_transfer_one_message;
- master->cleanup = ep93xx_spi_cleanup;
master->bus_num = pdev->id;
- master->num_chipselect = info->num_chipselect;
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
+ master->num_chipselect = info->num_chipselect;
+ master->cs_gpios = devm_kzalloc(&master->dev,
+ sizeof(int) * master->num_chipselect,
+ GFP_KERNEL);
+ if (!master->cs_gpios) {
+ error = -ENOMEM;
+ goto fail_release_master;
+ }
+
+ for (i = 0; i < master->num_chipselect; i++) {
+ master->cs_gpios[i] = info->chipselect[i];
+
+ if (!gpio_is_valid(master->cs_gpios[i]))
+ continue;
+
+ error = devm_gpio_request_one(&pdev->dev, master->cs_gpios[i],
+ GPIOF_OUT_INIT_HIGH,
+ "ep93xx-spi");
+ if (error) {
+ dev_err(&pdev->dev, "could not request cs gpio %d\n",
+ master->cs_gpios[i]);
+ goto fail_release_master;
+ }
+ }
+
platform_set_drvdata(pdev, master);
espi = spi_master_get_devdata(master);
diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c
index 52551f6..cb3c730 100644
--- a/drivers/spi/spi-fsl-lpspi.c
+++ b/drivers/spi/spi-fsl-lpspi.c
@@ -366,7 +366,7 @@
struct spi_transfer *xfer;
bool is_first_xfer = true;
u32 temp;
- int ret;
+ int ret = 0;
msg->status = 0;
msg->actual_length = 0;
@@ -512,9 +512,9 @@
static struct platform_driver fsl_lpspi_driver = {
.driver = {
- .name = DRIVER_NAME,
- .of_match_table = fsl_lpspi_dt_ids,
- },
+ .name = DRIVER_NAME,
+ .of_match_table = fsl_lpspi_dt_ids,
+ },
.probe = fsl_lpspi_probe,
.remove = fsl_lpspi_remove,
};
diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c
index 8b290d9..0fc3452 100644
--- a/drivers/spi/spi-fsl-spi.c
+++ b/drivers/spi/spi-fsl-spi.c
@@ -267,10 +267,9 @@
if ((mpc8xxx_spi->spibrg / hz) > 64) {
cs->hw_mode |= SPMODE_DIV16;
pm = (mpc8xxx_spi->spibrg - 1) / (hz * 64) + 1;
-
- WARN_ONCE(pm > 16, "%s: Requested speed is too low: %d Hz. "
- "Will use %d Hz instead.\n", dev_name(&spi->dev),
- hz, mpc8xxx_spi->spibrg / 1024);
+ WARN_ONCE(pm > 16,
+ "%s: Requested speed is too low: %d Hz. Will use %d Hz instead.\n",
+ dev_name(&spi->dev), hz, mpc8xxx_spi->spibrg / 1024);
if (pm > 16)
pm = 16;
} else {
@@ -727,12 +726,13 @@
return 0;
}
- pinfo->gpios = kmalloc(ngpios * sizeof(*pinfo->gpios), GFP_KERNEL);
+ pinfo->gpios = kmalloc_array(ngpios, sizeof(*pinfo->gpios),
+ GFP_KERNEL);
if (!pinfo->gpios)
return -ENOMEM;
memset(pinfo->gpios, -1, ngpios * sizeof(*pinfo->gpios));
- pinfo->alow_flags = kzalloc(ngpios * sizeof(*pinfo->alow_flags),
+ pinfo->alow_flags = kcalloc(ngpios, sizeof(*pinfo->alow_flags),
GFP_KERNEL);
if (!pinfo->alow_flags) {
ret = -ENOMEM;
@@ -762,8 +762,9 @@
ret = gpio_direction_output(pinfo->gpios[i],
pinfo->alow_flags[i]);
if (ret) {
- dev_err(dev, "can't set output direction for gpio "
- "#%d: %d\n", i, ret);
+ dev_err(dev,
+ "can't set output direction for gpio #%d: %d\n",
+ i, ret);
goto err_loop;
}
}
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index 32ced64..9a7c62f 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -211,7 +211,7 @@
struct spi_transfer *transfer)
{
struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
- unsigned int bpw;
+ unsigned int bpw, i;
if (!master->dma_rx)
return false;
@@ -228,11 +228,15 @@
if (bpw != 1 && bpw != 2 && bpw != 4)
return false;
- if (transfer->len < spi_imx->wml * bpw)
+ for (i = spi_imx_get_fifosize(spi_imx) / 2; i > 0; i--) {
+ if (!(transfer->len % (i * bpw)))
+ break;
+ }
+
+ if (i == 0)
return false;
- if (transfer->len % (spi_imx->wml * bpw))
- return false;
+ spi_imx->wml = i;
return true;
}
@@ -837,10 +841,6 @@
struct dma_slave_config rx = {}, tx = {};
struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
- if (bytes_per_word == spi_imx->bytes_per_word)
- /* Same as last time */
- return 0;
-
switch (bytes_per_word) {
case 4:
buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
diff --git a/drivers/spi/spi-lantiq-ssc.c b/drivers/spi/spi-lantiq-ssc.c
new file mode 100644
index 0000000..8a626f7
--- /dev/null
+++ b/drivers/spi/spi-lantiq-ssc.c
@@ -0,0 +1,983 @@
+/*
+ * Copyright (C) 2011-2015 Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
+ * Copyright (C) 2016 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can distribute 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/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/spi/spi.h>
+
+#ifdef CONFIG_LANTIQ
+#include <lantiq_soc.h>
+#endif
+
+#define SPI_RX_IRQ_NAME "spi_rx"
+#define SPI_TX_IRQ_NAME "spi_tx"
+#define SPI_ERR_IRQ_NAME "spi_err"
+#define SPI_FRM_IRQ_NAME "spi_frm"
+
+#define SPI_CLC 0x00
+#define SPI_PISEL 0x04
+#define SPI_ID 0x08
+#define SPI_CON 0x10
+#define SPI_STAT 0x14
+#define SPI_WHBSTATE 0x18
+#define SPI_TB 0x20
+#define SPI_RB 0x24
+#define SPI_RXFCON 0x30
+#define SPI_TXFCON 0x34
+#define SPI_FSTAT 0x38
+#define SPI_BRT 0x40
+#define SPI_BRSTAT 0x44
+#define SPI_SFCON 0x60
+#define SPI_SFSTAT 0x64
+#define SPI_GPOCON 0x70
+#define SPI_GPOSTAT 0x74
+#define SPI_FPGO 0x78
+#define SPI_RXREQ 0x80
+#define SPI_RXCNT 0x84
+#define SPI_DMACON 0xec
+#define SPI_IRNEN 0xf4
+#define SPI_IRNICR 0xf8
+#define SPI_IRNCR 0xfc
+
+#define SPI_CLC_SMC_S 16 /* Clock divider for sleep mode */
+#define SPI_CLC_SMC_M (0xFF << SPI_CLC_SMC_S)
+#define SPI_CLC_RMC_S 8 /* Clock divider for normal run mode */
+#define SPI_CLC_RMC_M (0xFF << SPI_CLC_RMC_S)
+#define SPI_CLC_DISS BIT(1) /* Disable status bit */
+#define SPI_CLC_DISR BIT(0) /* Disable request bit */
+
+#define SPI_ID_TXFS_S 24 /* Implemented TX FIFO size */
+#define SPI_ID_TXFS_M (0x3F << SPI_ID_TXFS_S)
+#define SPI_ID_RXFS_S 16 /* Implemented RX FIFO size */
+#define SPI_ID_RXFS_M (0x3F << SPI_ID_RXFS_S)
+#define SPI_ID_MOD_S 8 /* Module ID */
+#define SPI_ID_MOD_M (0xff << SPI_ID_MOD_S)
+#define SPI_ID_CFG_S 5 /* DMA interface support */
+#define SPI_ID_CFG_M (1 << SPI_ID_CFG_S)
+#define SPI_ID_REV_M 0x1F /* Hardware revision number */
+
+#define SPI_CON_BM_S 16 /* Data width selection */
+#define SPI_CON_BM_M (0x1F << SPI_CON_BM_S)
+#define SPI_CON_EM BIT(24) /* Echo mode */
+#define SPI_CON_IDLE BIT(23) /* Idle bit value */
+#define SPI_CON_ENBV BIT(22) /* Enable byte valid control */
+#define SPI_CON_RUEN BIT(12) /* Receive underflow error enable */
+#define SPI_CON_TUEN BIT(11) /* Transmit underflow error enable */
+#define SPI_CON_AEN BIT(10) /* Abort error enable */
+#define SPI_CON_REN BIT(9) /* Receive overflow error enable */
+#define SPI_CON_TEN BIT(8) /* Transmit overflow error enable */
+#define SPI_CON_LB BIT(7) /* Loopback control */
+#define SPI_CON_PO BIT(6) /* Clock polarity control */
+#define SPI_CON_PH BIT(5) /* Clock phase control */
+#define SPI_CON_HB BIT(4) /* Heading control */
+#define SPI_CON_RXOFF BIT(1) /* Switch receiver off */
+#define SPI_CON_TXOFF BIT(0) /* Switch transmitter off */
+
+#define SPI_STAT_RXBV_S 28
+#define SPI_STAT_RXBV_M (0x7 << SPI_STAT_RXBV_S)
+#define SPI_STAT_BSY BIT(13) /* Busy flag */
+#define SPI_STAT_RUE BIT(12) /* Receive underflow error flag */
+#define SPI_STAT_TUE BIT(11) /* Transmit underflow error flag */
+#define SPI_STAT_AE BIT(10) /* Abort error flag */
+#define SPI_STAT_RE BIT(9) /* Receive error flag */
+#define SPI_STAT_TE BIT(8) /* Transmit error flag */
+#define SPI_STAT_ME BIT(7) /* Mode error flag */
+#define SPI_STAT_MS BIT(1) /* Master/slave select bit */
+#define SPI_STAT_EN BIT(0) /* Enable bit */
+#define SPI_STAT_ERRORS (SPI_STAT_ME | SPI_STAT_TE | SPI_STAT_RE | \
+ SPI_STAT_AE | SPI_STAT_TUE | SPI_STAT_RUE)
+
+#define SPI_WHBSTATE_SETTUE BIT(15) /* Set transmit underflow error flag */
+#define SPI_WHBSTATE_SETAE BIT(14) /* Set abort error flag */
+#define SPI_WHBSTATE_SETRE BIT(13) /* Set receive error flag */
+#define SPI_WHBSTATE_SETTE BIT(12) /* Set transmit error flag */
+#define SPI_WHBSTATE_CLRTUE BIT(11) /* Clear transmit underflow error flag */
+#define SPI_WHBSTATE_CLRAE BIT(10) /* Clear abort error flag */
+#define SPI_WHBSTATE_CLRRE BIT(9) /* Clear receive error flag */
+#define SPI_WHBSTATE_CLRTE BIT(8) /* Clear transmit error flag */
+#define SPI_WHBSTATE_SETME BIT(7) /* Set mode error flag */
+#define SPI_WHBSTATE_CLRME BIT(6) /* Clear mode error flag */
+#define SPI_WHBSTATE_SETRUE BIT(5) /* Set receive underflow error flag */
+#define SPI_WHBSTATE_CLRRUE BIT(4) /* Clear receive underflow error flag */
+#define SPI_WHBSTATE_SETMS BIT(3) /* Set master select bit */
+#define SPI_WHBSTATE_CLRMS BIT(2) /* Clear master select bit */
+#define SPI_WHBSTATE_SETEN BIT(1) /* Set enable bit (operational mode) */
+#define SPI_WHBSTATE_CLREN BIT(0) /* Clear enable bit (config mode */
+#define SPI_WHBSTATE_CLR_ERRORS (SPI_WHBSTATE_CLRRUE | SPI_WHBSTATE_CLRME | \
+ SPI_WHBSTATE_CLRTE | SPI_WHBSTATE_CLRRE | \
+ SPI_WHBSTATE_CLRAE | SPI_WHBSTATE_CLRTUE)
+
+#define SPI_RXFCON_RXFITL_S 8 /* FIFO interrupt trigger level */
+#define SPI_RXFCON_RXFITL_M (0x3F << SPI_RXFCON_RXFITL_S)
+#define SPI_RXFCON_RXFLU BIT(1) /* FIFO flush */
+#define SPI_RXFCON_RXFEN BIT(0) /* FIFO enable */
+
+#define SPI_TXFCON_TXFITL_S 8 /* FIFO interrupt trigger level */
+#define SPI_TXFCON_TXFITL_M (0x3F << SPI_TXFCON_TXFITL_S)
+#define SPI_TXFCON_TXFLU BIT(1) /* FIFO flush */
+#define SPI_TXFCON_TXFEN BIT(0) /* FIFO enable */
+
+#define SPI_FSTAT_RXFFL_S 0
+#define SPI_FSTAT_RXFFL_M (0x3f << SPI_FSTAT_RXFFL_S)
+#define SPI_FSTAT_TXFFL_S 8
+#define SPI_FSTAT_TXFFL_M (0x3f << SPI_FSTAT_TXFFL_S)
+
+#define SPI_GPOCON_ISCSBN_S 8
+#define SPI_GPOCON_INVOUTN_S 0
+
+#define SPI_FGPO_SETOUTN_S 8
+#define SPI_FGPO_CLROUTN_S 0
+
+#define SPI_RXREQ_RXCNT_M 0xFFFF /* Receive count value */
+#define SPI_RXCNT_TODO_M 0xFFFF /* Recevie to-do value */
+
+#define SPI_IRNEN_TFI BIT(4) /* TX finished interrupt */
+#define SPI_IRNEN_F BIT(3) /* Frame end interrupt request */
+#define SPI_IRNEN_E BIT(2) /* Error end interrupt request */
+#define SPI_IRNEN_T_XWAY BIT(1) /* Transmit end interrupt request */
+#define SPI_IRNEN_R_XWAY BIT(0) /* Receive end interrupt request */
+#define SPI_IRNEN_R_XRX BIT(1) /* Transmit end interrupt request */
+#define SPI_IRNEN_T_XRX BIT(0) /* Receive end interrupt request */
+#define SPI_IRNEN_ALL 0x1F
+
+struct lantiq_ssc_hwcfg {
+ unsigned int irnen_r;
+ unsigned int irnen_t;
+};
+
+struct lantiq_ssc_spi {
+ struct spi_master *master;
+ struct device *dev;
+ void __iomem *regbase;
+ struct clk *spi_clk;
+ struct clk *fpi_clk;
+ const struct lantiq_ssc_hwcfg *hwcfg;
+
+ spinlock_t lock;
+ struct workqueue_struct *wq;
+ struct work_struct work;
+
+ const u8 *tx;
+ u8 *rx;
+ unsigned int tx_todo;
+ unsigned int rx_todo;
+ unsigned int bits_per_word;
+ unsigned int speed_hz;
+ unsigned int tx_fifo_size;
+ unsigned int rx_fifo_size;
+ unsigned int base_cs;
+};
+
+static u32 lantiq_ssc_readl(const struct lantiq_ssc_spi *spi, u32 reg)
+{
+ return __raw_readl(spi->regbase + reg);
+}
+
+static void lantiq_ssc_writel(const struct lantiq_ssc_spi *spi, u32 val,
+ u32 reg)
+{
+ __raw_writel(val, spi->regbase + reg);
+}
+
+static void lantiq_ssc_maskl(const struct lantiq_ssc_spi *spi, u32 clr,
+ u32 set, u32 reg)
+{
+ u32 val = __raw_readl(spi->regbase + reg);
+
+ val &= ~clr;
+ val |= set;
+ __raw_writel(val, spi->regbase + reg);
+}
+
+static unsigned int tx_fifo_level(const struct lantiq_ssc_spi *spi)
+{
+ u32 fstat = lantiq_ssc_readl(spi, SPI_FSTAT);
+
+ return (fstat & SPI_FSTAT_TXFFL_M) >> SPI_FSTAT_TXFFL_S;
+}
+
+static unsigned int rx_fifo_level(const struct lantiq_ssc_spi *spi)
+{
+ u32 fstat = lantiq_ssc_readl(spi, SPI_FSTAT);
+
+ return fstat & SPI_FSTAT_RXFFL_M;
+}
+
+static unsigned int tx_fifo_free(const struct lantiq_ssc_spi *spi)
+{
+ return spi->tx_fifo_size - tx_fifo_level(spi);
+}
+
+static void rx_fifo_reset(const struct lantiq_ssc_spi *spi)
+{
+ u32 val = spi->rx_fifo_size << SPI_RXFCON_RXFITL_S;
+
+ val |= SPI_RXFCON_RXFEN | SPI_RXFCON_RXFLU;
+ lantiq_ssc_writel(spi, val, SPI_RXFCON);
+}
+
+static void tx_fifo_reset(const struct lantiq_ssc_spi *spi)
+{
+ u32 val = 1 << SPI_TXFCON_TXFITL_S;
+
+ val |= SPI_TXFCON_TXFEN | SPI_TXFCON_TXFLU;
+ lantiq_ssc_writel(spi, val, SPI_TXFCON);
+}
+
+static void rx_fifo_flush(const struct lantiq_ssc_spi *spi)
+{
+ lantiq_ssc_maskl(spi, 0, SPI_RXFCON_RXFLU, SPI_RXFCON);
+}
+
+static void tx_fifo_flush(const struct lantiq_ssc_spi *spi)
+{
+ lantiq_ssc_maskl(spi, 0, SPI_TXFCON_TXFLU, SPI_TXFCON);
+}
+
+static void hw_enter_config_mode(const struct lantiq_ssc_spi *spi)
+{
+ lantiq_ssc_writel(spi, SPI_WHBSTATE_CLREN, SPI_WHBSTATE);
+}
+
+static void hw_enter_active_mode(const struct lantiq_ssc_spi *spi)
+{
+ lantiq_ssc_writel(spi, SPI_WHBSTATE_SETEN, SPI_WHBSTATE);
+}
+
+static void hw_setup_speed_hz(const struct lantiq_ssc_spi *spi,
+ unsigned int max_speed_hz)
+{
+ u32 spi_clk, brt;
+
+ /*
+ * SPI module clock is derived from FPI bus clock dependent on
+ * divider value in CLC.RMS which is always set to 1.
+ *
+ * f_SPI
+ * baudrate = --------------
+ * 2 * (BR + 1)
+ */
+ spi_clk = clk_get_rate(spi->fpi_clk) / 2;
+
+ if (max_speed_hz > spi_clk)
+ brt = 0;
+ else
+ brt = spi_clk / max_speed_hz - 1;
+
+ if (brt > 0xFFFF)
+ brt = 0xFFFF;
+
+ dev_dbg(spi->dev, "spi_clk %u, max_speed_hz %u, brt %u\n",
+ spi_clk, max_speed_hz, brt);
+
+ lantiq_ssc_writel(spi, brt, SPI_BRT);
+}
+
+static void hw_setup_bits_per_word(const struct lantiq_ssc_spi *spi,
+ unsigned int bits_per_word)
+{
+ u32 bm;
+
+ /* CON.BM value = bits_per_word - 1 */
+ bm = (bits_per_word - 1) << SPI_CON_BM_S;
+
+ lantiq_ssc_maskl(spi, SPI_CON_BM_M, bm, SPI_CON);
+}
+
+static void hw_setup_clock_mode(const struct lantiq_ssc_spi *spi,
+ unsigned int mode)
+{
+ u32 con_set = 0, con_clr = 0;
+
+ /*
+ * SPI mode mapping in CON register:
+ * Mode CPOL CPHA CON.PO CON.PH
+ * 0 0 0 0 1
+ * 1 0 1 0 0
+ * 2 1 0 1 1
+ * 3 1 1 1 0
+ */
+ if (mode & SPI_CPHA)
+ con_clr |= SPI_CON_PH;
+ else
+ con_set |= SPI_CON_PH;
+
+ if (mode & SPI_CPOL)
+ con_set |= SPI_CON_PO | SPI_CON_IDLE;
+ else
+ con_clr |= SPI_CON_PO | SPI_CON_IDLE;
+
+ /* Set heading control */
+ if (mode & SPI_LSB_FIRST)
+ con_clr |= SPI_CON_HB;
+ else
+ con_set |= SPI_CON_HB;
+
+ /* Set loopback mode */
+ if (mode & SPI_LOOP)
+ con_set |= SPI_CON_LB;
+ else
+ con_clr |= SPI_CON_LB;
+
+ lantiq_ssc_maskl(spi, con_clr, con_set, SPI_CON);
+}
+
+static void lantiq_ssc_hw_init(const struct lantiq_ssc_spi *spi)
+{
+ const struct lantiq_ssc_hwcfg *hwcfg = spi->hwcfg;
+
+ /*
+ * Set clock divider for run mode to 1 to
+ * run at same frequency as FPI bus
+ */
+ lantiq_ssc_writel(spi, 1 << SPI_CLC_RMC_S, SPI_CLC);
+
+ /* Put controller into config mode */
+ hw_enter_config_mode(spi);
+
+ /* Clear error flags */
+ lantiq_ssc_maskl(spi, 0, SPI_WHBSTATE_CLR_ERRORS, SPI_WHBSTATE);
+
+ /* Enable error checking, disable TX/RX */
+ lantiq_ssc_writel(spi, SPI_CON_RUEN | SPI_CON_AEN | SPI_CON_TEN |
+ SPI_CON_REN | SPI_CON_TXOFF | SPI_CON_RXOFF, SPI_CON);
+
+ /* Setup default SPI mode */
+ hw_setup_bits_per_word(spi, spi->bits_per_word);
+ hw_setup_clock_mode(spi, SPI_MODE_0);
+
+ /* Enable master mode and clear error flags */
+ lantiq_ssc_writel(spi, SPI_WHBSTATE_SETMS | SPI_WHBSTATE_CLR_ERRORS,
+ SPI_WHBSTATE);
+
+ /* Reset GPIO/CS registers */
+ lantiq_ssc_writel(spi, 0, SPI_GPOCON);
+ lantiq_ssc_writel(spi, 0xFF00, SPI_FPGO);
+
+ /* Enable and flush FIFOs */
+ rx_fifo_reset(spi);
+ tx_fifo_reset(spi);
+
+ /* Enable interrupts */
+ lantiq_ssc_writel(spi, hwcfg->irnen_t | hwcfg->irnen_r | SPI_IRNEN_E,
+ SPI_IRNEN);
+}
+
+static int lantiq_ssc_setup(struct spi_device *spidev)
+{
+ struct spi_master *master = spidev->master;
+ struct lantiq_ssc_spi *spi = spi_master_get_devdata(master);
+ unsigned int cs = spidev->chip_select;
+ u32 gpocon;
+
+ /* GPIOs are used for CS */
+ if (gpio_is_valid(spidev->cs_gpio))
+ return 0;
+
+ dev_dbg(spi->dev, "using internal chipselect %u\n", cs);
+
+ if (cs < spi->base_cs) {
+ dev_err(spi->dev,
+ "chipselect %i too small (min %i)\n", cs, spi->base_cs);
+ return -EINVAL;
+ }
+
+ /* set GPO pin to CS mode */
+ gpocon = 1 << ((cs - spi->base_cs) + SPI_GPOCON_ISCSBN_S);
+
+ /* invert GPO pin */
+ if (spidev->mode & SPI_CS_HIGH)
+ gpocon |= 1 << (cs - spi->base_cs);
+
+ lantiq_ssc_maskl(spi, 0, gpocon, SPI_GPOCON);
+
+ return 0;
+}
+
+static int lantiq_ssc_prepare_message(struct spi_master *master,
+ struct spi_message *message)
+{
+ struct lantiq_ssc_spi *spi = spi_master_get_devdata(master);
+
+ hw_enter_config_mode(spi);
+ hw_setup_clock_mode(spi, message->spi->mode);
+ hw_enter_active_mode(spi);
+
+ return 0;
+}
+
+static void hw_setup_transfer(struct lantiq_ssc_spi *spi,
+ struct spi_device *spidev, struct spi_transfer *t)
+{
+ unsigned int speed_hz = t->speed_hz;
+ unsigned int bits_per_word = t->bits_per_word;
+ u32 con;
+
+ if (bits_per_word != spi->bits_per_word ||
+ speed_hz != spi->speed_hz) {
+ hw_enter_config_mode(spi);
+ hw_setup_speed_hz(spi, speed_hz);
+ hw_setup_bits_per_word(spi, bits_per_word);
+ hw_enter_active_mode(spi);
+
+ spi->speed_hz = speed_hz;
+ spi->bits_per_word = bits_per_word;
+ }
+
+ /* Configure transmitter and receiver */
+ con = lantiq_ssc_readl(spi, SPI_CON);
+ if (t->tx_buf)
+ con &= ~SPI_CON_TXOFF;
+ else
+ con |= SPI_CON_TXOFF;
+
+ if (t->rx_buf)
+ con &= ~SPI_CON_RXOFF;
+ else
+ con |= SPI_CON_RXOFF;
+
+ lantiq_ssc_writel(spi, con, SPI_CON);
+}
+
+static int lantiq_ssc_unprepare_message(struct spi_master *master,
+ struct spi_message *message)
+{
+ struct lantiq_ssc_spi *spi = spi_master_get_devdata(master);
+
+ flush_workqueue(spi->wq);
+
+ /* Disable transmitter and receiver while idle */
+ lantiq_ssc_maskl(spi, 0, SPI_CON_TXOFF | SPI_CON_RXOFF, SPI_CON);
+
+ return 0;
+}
+
+static void tx_fifo_write(struct lantiq_ssc_spi *spi)
+{
+ const u8 *tx8;
+ const u16 *tx16;
+ const u32 *tx32;
+ u32 data;
+ unsigned int tx_free = tx_fifo_free(spi);
+
+ while (spi->tx_todo && tx_free) {
+ switch (spi->bits_per_word) {
+ case 2 ... 8:
+ tx8 = spi->tx;
+ data = *tx8;
+ spi->tx_todo--;
+ spi->tx++;
+ break;
+ case 16:
+ tx16 = (u16 *) spi->tx;
+ data = *tx16;
+ spi->tx_todo -= 2;
+ spi->tx += 2;
+ break;
+ case 32:
+ tx32 = (u32 *) spi->tx;
+ data = *tx32;
+ spi->tx_todo -= 4;
+ spi->tx += 4;
+ break;
+ default:
+ WARN_ON(1);
+ data = 0;
+ break;
+ }
+
+ lantiq_ssc_writel(spi, data, SPI_TB);
+ tx_free--;
+ }
+}
+
+static void rx_fifo_read_full_duplex(struct lantiq_ssc_spi *spi)
+{
+ u8 *rx8;
+ u16 *rx16;
+ u32 *rx32;
+ u32 data;
+ unsigned int rx_fill = rx_fifo_level(spi);
+
+ while (rx_fill) {
+ data = lantiq_ssc_readl(spi, SPI_RB);
+
+ switch (spi->bits_per_word) {
+ case 2 ... 8:
+ rx8 = spi->rx;
+ *rx8 = data;
+ spi->rx_todo--;
+ spi->rx++;
+ break;
+ case 16:
+ rx16 = (u16 *) spi->rx;
+ *rx16 = data;
+ spi->rx_todo -= 2;
+ spi->rx += 2;
+ break;
+ case 32:
+ rx32 = (u32 *) spi->rx;
+ *rx32 = data;
+ spi->rx_todo -= 4;
+ spi->rx += 4;
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ rx_fill--;
+ }
+}
+
+static void rx_fifo_read_half_duplex(struct lantiq_ssc_spi *spi)
+{
+ u32 data, *rx32;
+ u8 *rx8;
+ unsigned int rxbv, shift;
+ unsigned int rx_fill = rx_fifo_level(spi);
+
+ /*
+ * In RX-only mode the bits per word value is ignored by HW. A value
+ * of 32 is used instead. Thus all 4 bytes per FIFO must be read.
+ * If remaining RX bytes are less than 4, the FIFO must be read
+ * differently. The amount of received and valid bytes is indicated
+ * by STAT.RXBV register value.
+ */
+ while (rx_fill) {
+ if (spi->rx_todo < 4) {
+ rxbv = (lantiq_ssc_readl(spi, SPI_STAT) &
+ SPI_STAT_RXBV_M) >> SPI_STAT_RXBV_S;
+ data = lantiq_ssc_readl(spi, SPI_RB);
+
+ shift = (rxbv - 1) * 8;
+ rx8 = spi->rx;
+
+ while (rxbv) {
+ *rx8++ = (data >> shift) & 0xFF;
+ rxbv--;
+ shift -= 8;
+ spi->rx_todo--;
+ spi->rx++;
+ }
+ } else {
+ data = lantiq_ssc_readl(spi, SPI_RB);
+ rx32 = (u32 *) spi->rx;
+
+ *rx32++ = data;
+ spi->rx_todo -= 4;
+ spi->rx += 4;
+ }
+ rx_fill--;
+ }
+}
+
+static void rx_request(struct lantiq_ssc_spi *spi)
+{
+ unsigned int rxreq, rxreq_max;
+
+ /*
+ * To avoid receive overflows at high clocks it is better to request
+ * only the amount of bytes that fits into all FIFOs. This value
+ * depends on the FIFO size implemented in hardware.
+ */
+ rxreq = spi->rx_todo;
+ rxreq_max = spi->rx_fifo_size * 4;
+ if (rxreq > rxreq_max)
+ rxreq = rxreq_max;
+
+ lantiq_ssc_writel(spi, rxreq, SPI_RXREQ);
+}
+
+static irqreturn_t lantiq_ssc_xmit_interrupt(int irq, void *data)
+{
+ struct lantiq_ssc_spi *spi = data;
+
+ if (spi->tx) {
+ if (spi->rx && spi->rx_todo)
+ rx_fifo_read_full_duplex(spi);
+
+ if (spi->tx_todo)
+ tx_fifo_write(spi);
+ else if (!tx_fifo_level(spi))
+ goto completed;
+ } else if (spi->rx) {
+ if (spi->rx_todo) {
+ rx_fifo_read_half_duplex(spi);
+
+ if (spi->rx_todo)
+ rx_request(spi);
+ else
+ goto completed;
+ } else {
+ goto completed;
+ }
+ }
+
+ return IRQ_HANDLED;
+
+completed:
+ queue_work(spi->wq, &spi->work);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t lantiq_ssc_err_interrupt(int irq, void *data)
+{
+ struct lantiq_ssc_spi *spi = data;
+ u32 stat = lantiq_ssc_readl(spi, SPI_STAT);
+
+ if (!(stat & SPI_STAT_ERRORS))
+ return IRQ_NONE;
+
+ if (stat & SPI_STAT_RUE)
+ dev_err(spi->dev, "receive underflow error\n");
+ if (stat & SPI_STAT_TUE)
+ dev_err(spi->dev, "transmit underflow error\n");
+ if (stat & SPI_STAT_AE)
+ dev_err(spi->dev, "abort error\n");
+ if (stat & SPI_STAT_RE)
+ dev_err(spi->dev, "receive overflow error\n");
+ if (stat & SPI_STAT_TE)
+ dev_err(spi->dev, "transmit overflow error\n");
+ if (stat & SPI_STAT_ME)
+ dev_err(spi->dev, "mode error\n");
+
+ /* Clear error flags */
+ lantiq_ssc_maskl(spi, 0, SPI_WHBSTATE_CLR_ERRORS, SPI_WHBSTATE);
+
+ /* set bad status so it can be retried */
+ if (spi->master->cur_msg)
+ spi->master->cur_msg->status = -EIO;
+ queue_work(spi->wq, &spi->work);
+
+ return IRQ_HANDLED;
+}
+
+static int transfer_start(struct lantiq_ssc_spi *spi, struct spi_device *spidev,
+ struct spi_transfer *t)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&spi->lock, flags);
+
+ spi->tx = t->tx_buf;
+ spi->rx = t->rx_buf;
+
+ if (t->tx_buf) {
+ spi->tx_todo = t->len;
+
+ /* initially fill TX FIFO */
+ tx_fifo_write(spi);
+ }
+
+ if (spi->rx) {
+ spi->rx_todo = t->len;
+
+ /* start shift clock in RX-only mode */
+ if (!spi->tx)
+ rx_request(spi);
+ }
+
+ spin_unlock_irqrestore(&spi->lock, flags);
+
+ return t->len;
+}
+
+/*
+ * The driver only gets an interrupt when the FIFO is empty, but there
+ * is an additional shift register from which the data is written to
+ * the wire. We get the last interrupt when the controller starts to
+ * write the last word to the wire, not when it is finished. Do busy
+ * waiting till it finishes.
+ */
+static void lantiq_ssc_bussy_work(struct work_struct *work)
+{
+ struct lantiq_ssc_spi *spi;
+ unsigned long long timeout = 8LL * 1000LL;
+ unsigned long end;
+
+ spi = container_of(work, typeof(*spi), work);
+
+ do_div(timeout, spi->speed_hz);
+ timeout += timeout + 100; /* some tolerance */
+
+ end = jiffies + msecs_to_jiffies(timeout);
+ do {
+ u32 stat = lantiq_ssc_readl(spi, SPI_STAT);
+
+ if (!(stat & SPI_STAT_BSY)) {
+ spi_finalize_current_transfer(spi->master);
+ return;
+ }
+
+ cond_resched();
+ } while (!time_after_eq(jiffies, end));
+
+ if (spi->master->cur_msg)
+ spi->master->cur_msg->status = -EIO;
+ spi_finalize_current_transfer(spi->master);
+}
+
+static void lantiq_ssc_handle_err(struct spi_master *master,
+ struct spi_message *message)
+{
+ struct lantiq_ssc_spi *spi = spi_master_get_devdata(master);
+
+ /* flush FIFOs on timeout */
+ rx_fifo_flush(spi);
+ tx_fifo_flush(spi);
+}
+
+static void lantiq_ssc_set_cs(struct spi_device *spidev, bool enable)
+{
+ struct lantiq_ssc_spi *spi = spi_master_get_devdata(spidev->master);
+ unsigned int cs = spidev->chip_select;
+ u32 fgpo;
+
+ if (!!(spidev->mode & SPI_CS_HIGH) == enable)
+ fgpo = (1 << (cs - spi->base_cs));
+ else
+ fgpo = (1 << (cs - spi->base_cs + SPI_FGPO_SETOUTN_S));
+
+ lantiq_ssc_writel(spi, fgpo, SPI_FPGO);
+}
+
+static int lantiq_ssc_transfer_one(struct spi_master *master,
+ struct spi_device *spidev,
+ struct spi_transfer *t)
+{
+ struct lantiq_ssc_spi *spi = spi_master_get_devdata(master);
+
+ hw_setup_transfer(spi, spidev, t);
+
+ return transfer_start(spi, spidev, t);
+}
+
+static const struct lantiq_ssc_hwcfg lantiq_ssc_xway = {
+ .irnen_r = SPI_IRNEN_R_XWAY,
+ .irnen_t = SPI_IRNEN_T_XWAY,
+};
+
+static const struct lantiq_ssc_hwcfg lantiq_ssc_xrx = {
+ .irnen_r = SPI_IRNEN_R_XRX,
+ .irnen_t = SPI_IRNEN_T_XRX,
+};
+
+static const struct of_device_id lantiq_ssc_match[] = {
+ { .compatible = "lantiq,ase-spi", .data = &lantiq_ssc_xway, },
+ { .compatible = "lantiq,falcon-spi", .data = &lantiq_ssc_xrx, },
+ { .compatible = "lantiq,xrx100-spi", .data = &lantiq_ssc_xrx, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, lantiq_ssc_match);
+
+static int lantiq_ssc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct spi_master *master;
+ struct resource *res;
+ struct lantiq_ssc_spi *spi;
+ const struct lantiq_ssc_hwcfg *hwcfg;
+ const struct of_device_id *match;
+ int err, rx_irq, tx_irq, err_irq;
+ u32 id, supports_dma, revision;
+ unsigned int num_cs;
+
+ match = of_match_device(lantiq_ssc_match, dev);
+ if (!match) {
+ dev_err(dev, "no device match\n");
+ return -EINVAL;
+ }
+ hwcfg = match->data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get resources\n");
+ return -ENXIO;
+ }
+
+ rx_irq = platform_get_irq_byname(pdev, SPI_RX_IRQ_NAME);
+ if (rx_irq < 0) {
+ dev_err(dev, "failed to get %s\n", SPI_RX_IRQ_NAME);
+ return -ENXIO;
+ }
+
+ tx_irq = platform_get_irq_byname(pdev, SPI_TX_IRQ_NAME);
+ if (tx_irq < 0) {
+ dev_err(dev, "failed to get %s\n", SPI_TX_IRQ_NAME);
+ return -ENXIO;
+ }
+
+ err_irq = platform_get_irq_byname(pdev, SPI_ERR_IRQ_NAME);
+ if (err_irq < 0) {
+ dev_err(dev, "failed to get %s\n", SPI_ERR_IRQ_NAME);
+ return -ENXIO;
+ }
+
+ master = spi_alloc_master(dev, sizeof(struct lantiq_ssc_spi));
+ if (!master)
+ return -ENOMEM;
+
+ spi = spi_master_get_devdata(master);
+ spi->master = master;
+ spi->dev = dev;
+ spi->hwcfg = hwcfg;
+ platform_set_drvdata(pdev, spi);
+
+ spi->regbase = devm_ioremap_resource(dev, res);
+ if (IS_ERR(spi->regbase)) {
+ err = PTR_ERR(spi->regbase);
+ goto err_master_put;
+ }
+
+ err = devm_request_irq(dev, rx_irq, lantiq_ssc_xmit_interrupt,
+ 0, SPI_RX_IRQ_NAME, spi);
+ if (err)
+ goto err_master_put;
+
+ err = devm_request_irq(dev, tx_irq, lantiq_ssc_xmit_interrupt,
+ 0, SPI_TX_IRQ_NAME, spi);
+ if (err)
+ goto err_master_put;
+
+ err = devm_request_irq(dev, err_irq, lantiq_ssc_err_interrupt,
+ 0, SPI_ERR_IRQ_NAME, spi);
+ if (err)
+ goto err_master_put;
+
+ spi->spi_clk = devm_clk_get(dev, "gate");
+ if (IS_ERR(spi->spi_clk)) {
+ err = PTR_ERR(spi->spi_clk);
+ goto err_master_put;
+ }
+ err = clk_prepare_enable(spi->spi_clk);
+ if (err)
+ goto err_master_put;
+
+ /*
+ * Use the old clk_get_fpi() function on Lantiq platform, till it
+ * supports common clk.
+ */
+#if defined(CONFIG_LANTIQ) && !defined(CONFIG_COMMON_CLK)
+ spi->fpi_clk = clk_get_fpi();
+#else
+ spi->fpi_clk = clk_get(dev, "freq");
+#endif
+ if (IS_ERR(spi->fpi_clk)) {
+ err = PTR_ERR(spi->fpi_clk);
+ goto err_clk_disable;
+ }
+
+ num_cs = 8;
+ of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs);
+
+ spi->base_cs = 1;
+ of_property_read_u32(pdev->dev.of_node, "base-cs", &spi->base_cs);
+
+ spin_lock_init(&spi->lock);
+ spi->bits_per_word = 8;
+ spi->speed_hz = 0;
+
+ master->dev.of_node = pdev->dev.of_node;
+ master->num_chipselect = num_cs;
+ master->setup = lantiq_ssc_setup;
+ master->set_cs = lantiq_ssc_set_cs;
+ master->handle_err = lantiq_ssc_handle_err;
+ master->prepare_message = lantiq_ssc_prepare_message;
+ master->unprepare_message = lantiq_ssc_unprepare_message;
+ master->transfer_one = lantiq_ssc_transfer_one;
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH |
+ SPI_LOOP;
+ master->bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 8) |
+ SPI_BPW_MASK(16) | SPI_BPW_MASK(32);
+
+ spi->wq = alloc_ordered_workqueue(dev_name(dev), 0);
+ if (!spi->wq) {
+ err = -ENOMEM;
+ goto err_clk_put;
+ }
+ INIT_WORK(&spi->work, lantiq_ssc_bussy_work);
+
+ id = lantiq_ssc_readl(spi, SPI_ID);
+ spi->tx_fifo_size = (id & SPI_ID_TXFS_M) >> SPI_ID_TXFS_S;
+ spi->rx_fifo_size = (id & SPI_ID_RXFS_M) >> SPI_ID_RXFS_S;
+ supports_dma = (id & SPI_ID_CFG_M) >> SPI_ID_CFG_S;
+ revision = id & SPI_ID_REV_M;
+
+ lantiq_ssc_hw_init(spi);
+
+ dev_info(dev,
+ "Lantiq SSC SPI controller (Rev %i, TXFS %u, RXFS %u, DMA %u)\n",
+ revision, spi->tx_fifo_size, spi->rx_fifo_size, supports_dma);
+
+ err = devm_spi_register_master(dev, master);
+ if (err) {
+ dev_err(dev, "failed to register spi_master\n");
+ goto err_wq_destroy;
+ }
+
+ return 0;
+
+err_wq_destroy:
+ destroy_workqueue(spi->wq);
+err_clk_put:
+ clk_put(spi->fpi_clk);
+err_clk_disable:
+ clk_disable_unprepare(spi->spi_clk);
+err_master_put:
+ spi_master_put(master);
+
+ return err;
+}
+
+static int lantiq_ssc_remove(struct platform_device *pdev)
+{
+ struct lantiq_ssc_spi *spi = platform_get_drvdata(pdev);
+
+ lantiq_ssc_writel(spi, 0, SPI_IRNEN);
+ lantiq_ssc_writel(spi, 0, SPI_CLC);
+ rx_fifo_flush(spi);
+ tx_fifo_flush(spi);
+ hw_enter_config_mode(spi);
+
+ destroy_workqueue(spi->wq);
+ clk_disable_unprepare(spi->spi_clk);
+ clk_put(spi->fpi_clk);
+
+ return 0;
+}
+
+static struct platform_driver lantiq_ssc_driver = {
+ .probe = lantiq_ssc_probe,
+ .remove = lantiq_ssc_remove,
+ .driver = {
+ .name = "spi-lantiq-ssc",
+ .owner = THIS_MODULE,
+ .of_match_table = lantiq_ssc_match,
+ },
+};
+module_platform_driver(lantiq_ssc_driver);
+
+MODULE_DESCRIPTION("Lantiq SSC SPI controller driver");
+MODULE_AUTHOR("Daniel Schwierzeck <daniel.schwierzeck@gmail.com>");
+MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:spi-lantiq-ssc");
diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c
index c360021..e8b59ce 100644
--- a/drivers/spi/spi-mpc52xx.c
+++ b/drivers/spi/spi-mpc52xx.c
@@ -437,8 +437,9 @@
ms->gpio_cs_count = of_gpio_count(op->dev.of_node);
if (ms->gpio_cs_count > 0) {
master->num_chipselect = ms->gpio_cs_count;
- ms->gpio_cs = kmalloc(ms->gpio_cs_count * sizeof(unsigned int),
- GFP_KERNEL);
+ ms->gpio_cs = kmalloc_array(ms->gpio_cs_count,
+ sizeof(*ms->gpio_cs),
+ GFP_KERNEL);
if (!ms->gpio_cs) {
rc = -ENOMEM;
goto err_alloc_gpio;
@@ -448,8 +449,7 @@
gpio_cs = of_get_gpio(op->dev.of_node, i);
if (gpio_cs < 0) {
dev_err(&op->dev,
- "could not parse the gpio field "
- "in oftree\n");
+ "could not parse the gpio field in oftree\n");
rc = -ENODEV;
goto err_gpio;
}
@@ -457,8 +457,8 @@
rc = gpio_request(gpio_cs, dev_name(&op->dev));
if (rc) {
dev_err(&op->dev,
- "can't request spi cs gpio #%d "
- "on gpio line %d\n", i, gpio_cs);
+ "can't request spi cs gpio #%d on gpio line %d\n",
+ i, gpio_cs);
goto err_gpio;
}
diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c
index 899d7a8..278867a 100644
--- a/drivers/spi/spi-mt65xx.c
+++ b/drivers/spi/spi-mt65xx.c
@@ -73,7 +73,7 @@
#define MTK_SPI_IDLE 0
#define MTK_SPI_PAUSED 1
-#define MTK_SPI_MAX_FIFO_SIZE 32
+#define MTK_SPI_MAX_FIFO_SIZE 32U
#define MTK_SPI_PACKET_SIZE 1024
struct mtk_spi_compatible {
@@ -333,7 +333,7 @@
struct mtk_spi *mdata = spi_master_get_devdata(master);
mdata->cur_transfer = xfer;
- mdata->xfer_len = xfer->len;
+ mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, xfer->len);
mtk_spi_prepare_transfer(master, xfer);
mtk_spi_setup_packet(master);
@@ -410,7 +410,10 @@
struct spi_device *spi,
struct spi_transfer *xfer)
{
- return xfer->len > MTK_SPI_MAX_FIFO_SIZE;
+ /* Buffers for DMA transactions must be 4-byte aligned */
+ return (xfer->len > MTK_SPI_MAX_FIFO_SIZE &&
+ (unsigned long)xfer->tx_buf % 4 == 0 &&
+ (unsigned long)xfer->rx_buf % 4 == 0);
}
static int mtk_spi_setup(struct spi_device *spi)
@@ -451,7 +454,33 @@
®_val, remainder);
}
}
- spi_finalize_current_transfer(master);
+
+ trans->len -= mdata->xfer_len;
+ if (!trans->len) {
+ spi_finalize_current_transfer(master);
+ return IRQ_HANDLED;
+ }
+
+ if (trans->tx_buf)
+ trans->tx_buf += mdata->xfer_len;
+ if (trans->rx_buf)
+ trans->rx_buf += mdata->xfer_len;
+
+ mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, trans->len);
+ mtk_spi_setup_packet(master);
+
+ cnt = trans->len / 4;
+ iowrite32_rep(mdata->base + SPI_TX_DATA_REG, trans->tx_buf, cnt);
+
+ remainder = trans->len % 4;
+ if (remainder > 0) {
+ reg_val = 0;
+ memcpy(®_val, trans->tx_buf + (cnt * 4), remainder);
+ writel(reg_val, mdata->base + SPI_TX_DATA_REG);
+ }
+
+ mtk_spi_enable_transfer(master);
+
return IRQ_HANDLED;
}
diff --git a/drivers/spi/spi-ppc4xx.c b/drivers/spi/spi-ppc4xx.c
index dd3d0a2..967d948 100644
--- a/drivers/spi/spi-ppc4xx.c
+++ b/drivers/spi/spi-ppc4xx.c
@@ -411,7 +411,7 @@
if (num_gpios > 0) {
int i;
- hw->gpios = kzalloc(sizeof(int) * num_gpios, GFP_KERNEL);
+ hw->gpios = kcalloc(num_gpios, sizeof(*hw->gpios), GFP_KERNEL);
if (!hw->gpios) {
ret = -ENOMEM;
goto free_master;
@@ -428,8 +428,9 @@
/* Real CS - set the initial state. */
ret = gpio_request(gpio, np->name);
if (ret < 0) {
- dev_err(dev, "can't request gpio "
- "#%d: %d\n", i, ret);
+ dev_err(dev,
+ "can't request gpio #%d: %d\n",
+ i, ret);
goto free_gpios;
}
diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c
index 58d2d48..869f188 100644
--- a/drivers/spi/spi-pxa2xx-pci.c
+++ b/drivers/spi/spi-pxa2xx-pci.c
@@ -41,6 +41,13 @@
static struct dw_dma_slave byt_tx_param = { .dst_id = 0 };
static struct dw_dma_slave byt_rx_param = { .src_id = 1 };
+static struct dw_dma_slave mrfld3_tx_param = { .dst_id = 15 };
+static struct dw_dma_slave mrfld3_rx_param = { .src_id = 14 };
+static struct dw_dma_slave mrfld5_tx_param = { .dst_id = 13 };
+static struct dw_dma_slave mrfld5_rx_param = { .src_id = 12 };
+static struct dw_dma_slave mrfld6_tx_param = { .dst_id = 11 };
+static struct dw_dma_slave mrfld6_rx_param = { .src_id = 10 };
+
static struct dw_dma_slave bsw0_tx_param = { .dst_id = 0 };
static struct dw_dma_slave bsw0_rx_param = { .src_id = 1 };
static struct dw_dma_slave bsw1_tx_param = { .dst_id = 6 };
@@ -93,22 +100,39 @@
static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c)
{
+ struct pci_dev *dma_dev = pci_get_slot(dev->bus, PCI_DEVFN(21, 0));
+ struct dw_dma_slave *tx, *rx;
+
switch (PCI_FUNC(dev->devfn)) {
case 0:
c->port_id = 3;
c->num_chipselect = 1;
+ c->tx_param = &mrfld3_tx_param;
+ c->rx_param = &mrfld3_rx_param;
break;
case 1:
c->port_id = 5;
c->num_chipselect = 4;
+ c->tx_param = &mrfld5_tx_param;
+ c->rx_param = &mrfld5_rx_param;
break;
case 2:
c->port_id = 6;
c->num_chipselect = 1;
+ c->tx_param = &mrfld6_tx_param;
+ c->rx_param = &mrfld6_rx_param;
break;
default:
return -ENODEV;
}
+
+ tx = c->tx_param;
+ tx->dma_dev = &dma_dev->dev;
+
+ rx = c->rx_param;
+ rx->dma_dev = &dma_dev->dev;
+
+ c->dma_filter = lpss_dma_filter;
return 0;
}
@@ -203,10 +227,16 @@
ssp = &spi_pdata.ssp;
ssp->phys_base = pci_resource_start(dev, 0);
ssp->mmio_base = pcim_iomap_table(dev)[0];
- ssp->irq = dev->irq;
ssp->port_id = (c->port_id >= 0) ? c->port_id : dev->devfn;
ssp->type = c->type;
+ pci_set_master(dev);
+
+ ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_ALL_TYPES);
+ if (ret < 0)
+ return ret;
+ ssp->irq = pci_irq_vector(dev, 0);
+
snprintf(buf, sizeof(buf), "pxa2xx-spi.%d", ssp->port_id);
ssp->clk = clk_register_fixed_rate(&dev->dev, buf , NULL, 0,
c->max_clk_rate);
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index d6239fa..47b65d7 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -732,6 +732,20 @@
return IRQ_HANDLED;
}
+static void handle_bad_msg(struct driver_data *drv_data)
+{
+ pxa2xx_spi_write(drv_data, SSCR0,
+ pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE);
+ pxa2xx_spi_write(drv_data, SSCR1,
+ pxa2xx_spi_read(drv_data, SSCR1) & ~drv_data->int_cr1);
+ if (!pxa25x_ssp_comp(drv_data))
+ pxa2xx_spi_write(drv_data, SSTO, 0);
+ write_SSSR_CS(drv_data, drv_data->clear_sr);
+
+ dev_err(&drv_data->pdev->dev,
+ "bad message state in interrupt handler\n");
+}
+
static irqreturn_t ssp_int(int irq, void *dev_id)
{
struct driver_data *drv_data = dev_id;
@@ -771,21 +785,11 @@
if (!(status & mask))
return IRQ_NONE;
+ pxa2xx_spi_write(drv_data, SSCR1, sccr1_reg & ~drv_data->int_cr1);
+ pxa2xx_spi_write(drv_data, SSCR1, sccr1_reg);
+
if (!drv_data->master->cur_msg) {
-
- pxa2xx_spi_write(drv_data, SSCR0,
- pxa2xx_spi_read(drv_data, SSCR0)
- & ~SSCR0_SSE);
- pxa2xx_spi_write(drv_data, SSCR1,
- pxa2xx_spi_read(drv_data, SSCR1)
- & ~drv_data->int_cr1);
- if (!pxa25x_ssp_comp(drv_data))
- pxa2xx_spi_write(drv_data, SSTO, 0);
- write_SSSR_CS(drv_data, drv_data->clear_sr);
-
- dev_err(&drv_data->pdev->dev,
- "bad message state in interrupt handler\n");
-
+ handle_bad_msg(drv_data);
/* Never fail */
return IRQ_HANDLED;
}
@@ -1458,6 +1462,10 @@
{ PCI_VDEVICE(INTEL, 0x1ac2), LPSS_BXT_SSP },
{ PCI_VDEVICE(INTEL, 0x1ac4), LPSS_BXT_SSP },
{ PCI_VDEVICE(INTEL, 0x1ac6), LPSS_BXT_SSP },
+ /* GLK */
+ { PCI_VDEVICE(INTEL, 0x31c2), LPSS_BXT_SSP },
+ { PCI_VDEVICE(INTEL, 0x31c4), LPSS_BXT_SSP },
+ { PCI_VDEVICE(INTEL, 0x31c6), LPSS_BXT_SSP },
/* APL */
{ PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP },
{ PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP },
diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
index 0f89c21..acf31f3 100644
--- a/drivers/spi/spi-rockchip.c
+++ b/drivers/spi/spi-rockchip.c
@@ -17,6 +17,7 @@
#include <linux/dmaengine.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/pm_runtime.h>
@@ -843,6 +844,8 @@
clk_disable_unprepare(rs->apb_pclk);
}
+ pinctrl_pm_select_sleep_state(dev);
+
return ret;
}
@@ -852,6 +855,8 @@
struct spi_master *master = dev_get_drvdata(dev);
struct rockchip_spi *rs = spi_master_get_devdata(master);
+ pinctrl_pm_select_default_state(dev);
+
if (!pm_runtime_suspended(dev)) {
ret = clk_prepare_enable(rs->apb_pclk);
if (ret < 0)
diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c
index 9daf500..2a10b3f9 100644
--- a/drivers/spi/spi-rspi.c
+++ b/drivers/spi/spi-rspi.c
@@ -808,7 +808,7 @@
for (i = 0; i < len; i++)
rspi_write_data(rspi, *tx++);
} else {
- ret = rspi_pio_transfer(rspi, tx, NULL, n);
+ ret = rspi_pio_transfer(rspi, tx, NULL, len);
if (ret < 0)
return ret;
}
@@ -845,10 +845,9 @@
for (i = 0; i < len; i++)
*rx++ = rspi_read_data(rspi);
} else {
- ret = rspi_pio_transfer(rspi, NULL, rx, n);
+ ret = rspi_pio_transfer(rspi, NULL, rx, len);
if (ret < 0)
return ret;
- *rx++ = ret;
}
n -= len;
}
@@ -1227,10 +1226,8 @@
const struct spi_ops *ops;
master = spi_alloc_master(&pdev->dev, sizeof(struct rspi_data));
- if (master == NULL) {
- dev_err(&pdev->dev, "spi_alloc_master error.\n");
+ if (master == NULL)
return -ENOMEM;
- }
of_id = of_match_device(rspi_of_match, &pdev->dev);
if (of_id) {
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index 28dfdce..b392cca 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -341,43 +341,16 @@
static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
{
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
- struct device *dev = &sdd->pdev->dev;
if (is_polling(sdd))
return 0;
- /* Acquire DMA channels */
- sdd->rx_dma.ch = dma_request_slave_channel(dev, "rx");
- if (!sdd->rx_dma.ch) {
- dev_err(dev, "Failed to get RX DMA channel\n");
- return -EBUSY;
- }
spi->dma_rx = sdd->rx_dma.ch;
-
- sdd->tx_dma.ch = dma_request_slave_channel(dev, "tx");
- if (!sdd->tx_dma.ch) {
- dev_err(dev, "Failed to get TX DMA channel\n");
- dma_release_channel(sdd->rx_dma.ch);
- return -EBUSY;
- }
spi->dma_tx = sdd->tx_dma.ch;
return 0;
}
-static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi)
-{
- struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
-
- /* Free DMA channels */
- if (!is_polling(sdd)) {
- dma_release_channel(sdd->rx_dma.ch);
- dma_release_channel(sdd->tx_dma.ch);
- }
-
- return 0;
-}
-
static bool s3c64xx_spi_can_dma(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *xfer)
@@ -996,7 +969,7 @@
sci->num_cs = temp;
}
- sci->no_cs = of_property_read_bool(dev->of_node, "broken-cs");
+ sci->no_cs = of_property_read_bool(dev->of_node, "no-cs-readback");
return sci;
}
@@ -1094,7 +1067,6 @@
master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer;
master->prepare_message = s3c64xx_spi_prepare_message;
master->transfer_one = s3c64xx_spi_transfer_one;
- master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer;
master->num_chipselect = sci->num_cs;
master->dma_alignment = 8;
master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
@@ -1161,6 +1133,24 @@
}
}
+ if (!is_polling(sdd)) {
+ /* Acquire DMA channels */
+ sdd->rx_dma.ch = dma_request_slave_channel_reason(&pdev->dev,
+ "rx");
+ if (IS_ERR(sdd->rx_dma.ch)) {
+ dev_err(&pdev->dev, "Failed to get RX DMA channel\n");
+ ret = PTR_ERR(sdd->rx_dma.ch);
+ goto err_disable_io_clk;
+ }
+ sdd->tx_dma.ch = dma_request_slave_channel_reason(&pdev->dev,
+ "tx");
+ if (IS_ERR(sdd->tx_dma.ch)) {
+ dev_err(&pdev->dev, "Failed to get TX DMA channel\n");
+ ret = PTR_ERR(sdd->tx_dma.ch);
+ goto err_release_rx_dma;
+ }
+ }
+
pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
@@ -1206,6 +1196,12 @@
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
+ if (!is_polling(sdd))
+ dma_release_channel(sdd->tx_dma.ch);
+err_release_rx_dma:
+ if (!is_polling(sdd))
+ dma_release_channel(sdd->rx_dma.ch);
+err_disable_io_clk:
clk_disable_unprepare(sdd->ioclk);
err_disable_src_clk:
clk_disable_unprepare(sdd->src_clk);
@@ -1226,6 +1222,11 @@
writel(0, sdd->regs + S3C64XX_SPI_INT_EN);
+ if (!is_polling(sdd)) {
+ dma_release_channel(sdd->rx_dma.ch);
+ dma_release_channel(sdd->tx_dma.ch);
+ }
+
clk_disable_unprepare(sdd->ioclk);
clk_disable_unprepare(sdd->src_clk);
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 1f00eeb..2ce15ca 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -1164,10 +1164,8 @@
int ret;
master = spi_alloc_master(&pdev->dev, sizeof(struct sh_msiof_spi_priv));
- if (master == NULL) {
- dev_err(&pdev->dev, "failed to allocate spi master\n");
+ if (master == NULL)
return -ENOMEM;
- }
p = spi_master_get_devdata(master);
diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
index ec6fb09..ad76a44 100644
--- a/drivers/spi/spi-ti-qspi.c
+++ b/drivers/spi/spi-ti-qspi.c
@@ -652,7 +652,8 @@
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (r == NULL) {
dev_err(&pdev->dev, "missing platform data\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto free_master;
}
}
@@ -669,7 +670,8 @@
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq resource?\n");
- return irq;
+ ret = irq;
+ goto free_master;
}
mutex_init(&qspi->list_lock);
@@ -685,15 +687,17 @@
qspi->ctrl_base =
syscon_regmap_lookup_by_phandle(np,
"syscon-chipselects");
- if (IS_ERR(qspi->ctrl_base))
- return PTR_ERR(qspi->ctrl_base);
+ if (IS_ERR(qspi->ctrl_base)) {
+ ret = PTR_ERR(qspi->ctrl_base);
+ goto free_master;
+ }
ret = of_property_read_u32_index(np,
"syscon-chipselects",
1, &qspi->ctrl_reg);
if (ret) {
dev_err(&pdev->dev,
"couldn't get ctrl_mod reg index\n");
- return ret;
+ goto free_master;
}
}
@@ -714,9 +718,10 @@
dma_cap_set(DMA_MEMCPY, mask);
qspi->rx_chan = dma_request_chan_by_mask(&mask);
- if (!qspi->rx_chan) {
+ if (IS_ERR(qspi->rx_chan)) {
dev_err(qspi->dev,
"No Rx DMA available, trying mmap mode\n");
+ qspi->rx_chan = NULL;
ret = 0;
goto no_dma;
}
@@ -742,6 +747,7 @@
if (!ret)
return 0;
+ pm_runtime_disable(&pdev->dev);
free_master:
spi_master_put(master);
return ret;
diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c
index fcb9910..97d1375 100644
--- a/drivers/spi/spi-topcliff-pch.c
+++ b/drivers/spi/spi-topcliff-pch.c
@@ -591,7 +591,6 @@
if (!data->pkt_rx_buff) {
/* flush queue and set status of all transfers to -ENOMEM */
- dev_err(&data->master->dev, "%s :kzalloc failed\n", __func__);
list_for_each_entry_safe(pmsg, tmp, data->queue.next, queue) {
pmsg->status = -ENOMEM;
@@ -622,8 +621,9 @@
if (n_writes > PCH_MAX_FIFO_DEPTH)
n_writes = PCH_MAX_FIFO_DEPTH;
- dev_dbg(&data->master->dev, "\n%s:Pulling down SSN low - writing "
- "0x2 to SSNXCR\n", __func__);
+ dev_dbg(&data->master->dev,
+ "\n%s:Pulling down SSN low - writing 0x2 to SSNXCR\n",
+ __func__);
pch_spi_writereg(data->master, PCH_SSNXCR, SSN_LOW);
for (j = 0; j < n_writes; j++)
@@ -915,7 +915,6 @@
dma_release_channel(dma->chan_rx);
dma->chan_rx = NULL;
}
- return;
}
static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
@@ -1008,7 +1007,7 @@
spin_unlock_irqrestore(&data->lock, flags);
/* RX */
- dma->sg_rx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
+ dma->sg_rx_p = kcalloc(num, sizeof(*dma->sg_rx_p), GFP_ATOMIC);
sg_init_table(dma->sg_rx_p, num); /* Initialize SG table */
/* offset, length setting */
sg = dma->sg_rx_p;
@@ -1068,7 +1067,7 @@
head = 0;
}
- dma->sg_tx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
+ dma->sg_tx_p = kcalloc(num, sizeof(*dma->sg_tx_p), GFP_ATOMIC);
sg_init_table(dma->sg_tx_p, num); /* Initialize SG table */
/* offset, length setting */
sg = dma->sg_tx_p;
@@ -1181,14 +1180,16 @@
data->cur_trans =
list_entry(data->current_msg->transfers.next,
struct spi_transfer, transfer_list);
- dev_dbg(&data->master->dev, "%s "
- ":Getting 1st transfer message\n", __func__);
+ dev_dbg(&data->master->dev,
+ "%s :Getting 1st transfer message\n",
+ __func__);
} else {
data->cur_trans =
list_entry(data->cur_trans->transfer_list.next,
struct spi_transfer, transfer_list);
- dev_dbg(&data->master->dev, "%s "
- ":Getting next transfer message\n", __func__);
+ dev_dbg(&data->master->dev,
+ "%s :Getting next transfer message\n",
+ __func__);
}
spin_unlock(&data->lock);
@@ -1233,9 +1234,8 @@
/* check for delay */
if (data->cur_trans->delay_usecs) {
- dev_dbg(&data->master->dev, "%s:"
- "delay in usec=%d\n", __func__,
- data->cur_trans->delay_usecs);
+ dev_dbg(&data->master->dev, "%s:delay in usec=%d\n",
+ __func__, data->cur_trans->delay_usecs);
udelay(data->cur_trans->delay_usecs);
}
@@ -1292,7 +1292,6 @@
if (dma->rx_buf_dma)
dma_free_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE,
dma->rx_buf_virt, dma->rx_buf_dma);
- return;
}
static void pch_alloc_dma_buf(struct pch_spi_board_data *board_dat,
@@ -1541,11 +1540,11 @@
int i;
struct pch_pd_dev_save *pd_dev_save;
- pd_dev_save = kzalloc(sizeof(struct pch_pd_dev_save), GFP_KERNEL);
+ pd_dev_save = kzalloc(sizeof(*pd_dev_save), GFP_KERNEL);
if (!pd_dev_save)
return -ENOMEM;
- board_dat = kzalloc(sizeof(struct pch_spi_board_data), GFP_KERNEL);
+ board_dat = kzalloc(sizeof(*board_dat), GFP_KERNEL);
if (!board_dat) {
retval = -ENOMEM;
goto err_no_mem;
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 656dd3e..f274df9 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -621,8 +621,10 @@
if (!spi)
return;
- if (spi->dev.of_node)
+ if (spi->dev.of_node) {
of_node_clear_flag(spi->dev.of_node, OF_POPULATED);
+ of_node_put(spi->dev.of_node);
+ }
if (ACPI_COMPANION(&spi->dev))
acpi_device_clear_enumerated(ACPI_COMPANION(&spi->dev));
device_unregister(&spi->dev);
@@ -672,7 +674,7 @@
if (!n)
return -EINVAL;
- bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);
+ bi = kcalloc(n, sizeof(*bi), GFP_KERNEL);
if (!bi)
return -ENOMEM;
@@ -805,12 +807,12 @@
if (master->dma_tx)
tx_dev = master->dma_tx->device->dev;
else
- tx_dev = &master->dev;
+ tx_dev = master->dev.parent;
if (master->dma_rx)
rx_dev = master->dma_rx->device->dev;
else
- rx_dev = &master->dev;
+ rx_dev = master->dev.parent;
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
if (!master->can_dma(master, msg->spi, xfer))
@@ -852,12 +854,12 @@
if (master->dma_tx)
tx_dev = master->dma_tx->device->dev;
else
- tx_dev = &master->dev;
+ tx_dev = master->dev.parent;
if (master->dma_rx)
rx_dev = master->dma_rx->device->dev;
else
- rx_dev = &master->dev;
+ rx_dev = master->dev.parent;
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
if (!master->can_dma(master, msg->spi, xfer))
@@ -1502,37 +1504,18 @@
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_OF)
-static struct spi_device *
-of_register_spi_device(struct spi_master *master, struct device_node *nc)
+static int of_spi_parse_dt(struct spi_master *master, struct spi_device *spi,
+ struct device_node *nc)
{
- struct spi_device *spi;
- int rc;
u32 value;
-
- /* Alloc an spi_device */
- spi = spi_alloc_device(master);
- if (!spi) {
- dev_err(&master->dev, "spi_device alloc error for %s\n",
- nc->full_name);
- rc = -ENOMEM;
- goto err_out;
- }
-
- /* Select device driver */
- rc = of_modalias_node(nc, spi->modalias,
- sizeof(spi->modalias));
- if (rc < 0) {
- dev_err(&master->dev, "cannot find modalias for %s\n",
- nc->full_name);
- goto err_out;
- }
+ int rc;
/* Device address */
rc = of_property_read_u32(nc, "reg", &value);
if (rc) {
dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",
nc->full_name, rc);
- goto err_out;
+ return rc;
}
spi->chip_select = value;
@@ -1590,10 +1573,41 @@
if (rc) {
dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n",
nc->full_name, rc);
- goto err_out;
+ return rc;
}
spi->max_speed_hz = value;
+ return 0;
+}
+
+static struct spi_device *
+of_register_spi_device(struct spi_master *master, struct device_node *nc)
+{
+ struct spi_device *spi;
+ int rc;
+
+ /* Alloc an spi_device */
+ spi = spi_alloc_device(master);
+ if (!spi) {
+ dev_err(&master->dev, "spi_device alloc error for %s\n",
+ nc->full_name);
+ rc = -ENOMEM;
+ goto err_out;
+ }
+
+ /* Select device driver */
+ rc = of_modalias_node(nc, spi->modalias,
+ sizeof(spi->modalias));
+ if (rc < 0) {
+ dev_err(&master->dev, "cannot find modalias for %s\n",
+ nc->full_name);
+ goto err_out;
+ }
+
+ rc = of_spi_parse_dt(master, spi, nc);
+ if (rc)
+ goto err_out;
+
/* Store a pointer to the node in the device structure */
of_node_get(nc);
spi->dev.of_node = nc;
@@ -1603,11 +1617,13 @@
if (rc) {
dev_err(&master->dev, "spi_device register error %s\n",
nc->full_name);
- goto err_out;
+ goto err_of_node_put;
}
return spi;
+err_of_node_put:
+ of_node_put(nc);
err_out:
spi_dev_put(spi);
return ERR_PTR(rc);
diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c
index b653451..937c2d5 100644
--- a/drivers/staging/android/ion/ion.c
+++ b/drivers/staging/android/ion/ion.c
@@ -1300,7 +1300,7 @@
seq_printf(s, "%16s %16u %16zu %d %d\n",
buffer->task_comm, buffer->pid,
buffer->size, buffer->kmap_cnt,
- atomic_read(&buffer->ref.refcount));
+ kref_read(&buffer->ref));
total_orphaned_size += buffer->size;
}
}
diff --git a/drivers/staging/comedi/comedi_buf.c b/drivers/staging/comedi/comedi_buf.c
index c7d7682..1e1df89 100644
--- a/drivers/staging/comedi/comedi_buf.c
+++ b/drivers/staging/comedi/comedi_buf.c
@@ -188,7 +188,7 @@
{
struct comedi_buf_map *bm = s->async->buf_map;
- return bm && (atomic_read(&bm->refcount.refcount) > 1);
+ return bm && (kref_read(&bm->refcount) > 1);
}
int comedi_buf_alloc(struct comedi_device *dev, struct comedi_subdevice *s,
diff --git a/drivers/staging/lustre/lnet/libcfs/linux/linux-debug.c b/drivers/staging/lustre/lnet/libcfs/linux/linux-debug.c
index 39a72e3..7035356 100644
--- a/drivers/staging/lustre/lnet/libcfs/linux/linux-debug.c
+++ b/drivers/staging/lustre/lnet/libcfs/linux/linux-debug.c
@@ -107,7 +107,7 @@
libcfs_debug_dumplog();
if (libcfs_panic_on_lbug)
panic("LBUG");
- set_task_state(current, TASK_UNINTERRUPTIBLE);
+ set_current_state(TASK_UNINTERRUPTIBLE);
while (1)
schedule();
}
diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c
index d761025..e180511 100644
--- a/drivers/target/target_core_pr.c
+++ b/drivers/target/target_core_pr.c
@@ -788,7 +788,7 @@
* __core_scsi3_add_registration()
*/
dest_lun = rcu_dereference_check(deve_tmp->se_lun,
- atomic_read(&deve_tmp->pr_kref.refcount) != 0);
+ kref_read(&deve_tmp->pr_kref) != 0);
pr_reg_atp = __core_scsi3_do_alloc_registration(dev,
nacl_tmp, dest_lun, deve_tmp,
@@ -1463,7 +1463,7 @@
* For nacl->dynamic_node_acl=1
*/
lun_acl = rcu_dereference_check(se_deve->se_lun_acl,
- atomic_read(&se_deve->pr_kref.refcount) != 0);
+ kref_read(&se_deve->pr_kref) != 0);
if (!lun_acl)
return 0;
@@ -1478,7 +1478,7 @@
* For nacl->dynamic_node_acl=1
*/
lun_acl = rcu_dereference_check(se_deve->se_lun_acl,
- atomic_read(&se_deve->pr_kref.refcount) != 0);
+ kref_read(&se_deve->pr_kref) != 0);
if (!lun_acl) {
kref_put(&se_deve->pr_kref, target_pr_kref_release);
return;
@@ -1759,7 +1759,7 @@
* 2nd loop which will never fail.
*/
dest_lun = rcu_dereference_check(dest_se_deve->se_lun,
- atomic_read(&dest_se_deve->pr_kref.refcount) != 0);
+ kref_read(&dest_se_deve->pr_kref) != 0);
dest_pr_reg = __core_scsi3_alloc_registration(cmd->se_dev,
dest_node_acl, dest_lun, dest_se_deve,
@@ -3466,7 +3466,7 @@
iport_ptr);
if (!dest_pr_reg) {
struct se_lun *dest_lun = rcu_dereference_check(dest_se_deve->se_lun,
- atomic_read(&dest_se_deve->pr_kref.refcount) != 0);
+ kref_read(&dest_se_deve->pr_kref) != 0);
spin_unlock(&dev->dev_reservation_lock);
if (core_scsi3_alloc_registration(cmd->se_dev, dest_node_acl,
diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c
index fd5c3de..c91979c 100644
--- a/drivers/target/tcm_fc/tfc_sess.c
+++ b/drivers/target/tcm_fc/tfc_sess.c
@@ -454,7 +454,7 @@
void ft_sess_put(struct ft_sess *sess)
{
- int sess_held = atomic_read(&sess->kref.refcount);
+ int sess_held = kref_read(&sess->kref);
BUG_ON(!sess_held);
kref_put(&sess->kref, ft_sess_free);
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index 9ce0e9e..85fdbf7 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -297,8 +297,6 @@
if (!power_table)
return -ENOMEM;
- rcu_read_lock();
-
for (freq = 0, i = 0;
opp = dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp);
freq++, i++) {
@@ -306,13 +304,13 @@
u64 power;
if (i >= num_opps) {
- rcu_read_unlock();
ret = -EAGAIN;
goto free_power_table;
}
freq_mhz = freq / 1000000;
voltage_mv = dev_pm_opp_get_voltage(opp) / 1000;
+ dev_pm_opp_put(opp);
/*
* Do the multiplication with MHz and millivolt so as
@@ -328,8 +326,6 @@
power_table[i].power = power;
}
- rcu_read_unlock();
-
if (i != num_opps) {
ret = PTR_ERR(opp);
goto free_power_table;
@@ -433,13 +429,10 @@
return 0;
}
- rcu_read_lock();
-
opp = dev_pm_opp_find_freq_exact(cpufreq_device->cpu_dev, freq_hz,
true);
voltage = dev_pm_opp_get_voltage(opp);
-
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
if (voltage == 0) {
dev_warn_ratelimited(cpufreq_device->cpu_dev,
diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c
index 5a737fd..ba7a5cd 100644
--- a/drivers/thermal/devfreq_cooling.c
+++ b/drivers/thermal/devfreq_cooling.c
@@ -113,15 +113,15 @@
unsigned int freq = dfc->freq_table[i];
bool want_enable = i >= cdev_state ? true : false;
- rcu_read_lock();
opp = dev_pm_opp_find_freq_exact(dev, freq, !want_enable);
- rcu_read_unlock();
if (PTR_ERR(opp) == -ERANGE)
continue;
else if (IS_ERR(opp))
return PTR_ERR(opp);
+ dev_pm_opp_put(opp);
+
if (want_enable)
ret = dev_pm_opp_enable(dev, freq);
else
@@ -221,15 +221,12 @@
if (!dfc->power_ops->get_static_power)
return 0;
- rcu_read_lock();
-
opp = dev_pm_opp_find_freq_exact(dev, freq, true);
if (IS_ERR(opp) && (PTR_ERR(opp) == -ERANGE))
opp = dev_pm_opp_find_freq_exact(dev, freq, false);
voltage = dev_pm_opp_get_voltage(opp) / 1000; /* mV */
-
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
if (voltage == 0) {
dev_warn_ratelimited(dev,
@@ -412,18 +409,14 @@
unsigned long power_dyn, voltage;
struct dev_pm_opp *opp;
- rcu_read_lock();
-
opp = dev_pm_opp_find_freq_floor(dev, &freq);
if (IS_ERR(opp)) {
- rcu_read_unlock();
ret = PTR_ERR(opp);
goto free_tables;
}
voltage = dev_pm_opp_get_voltage(opp) / 1000; /* mV */
-
- rcu_read_unlock();
+ dev_pm_opp_put(opp);
if (dfc->power_ops) {
power_dyn = get_dynamic_power(dfc, freq, voltage);
diff --git a/drivers/tty/tty_ldsem.c b/drivers/tty/tty_ldsem.c
index 1bf8ed1..9229de4 100644
--- a/drivers/tty/tty_ldsem.c
+++ b/drivers/tty/tty_ldsem.c
@@ -200,7 +200,6 @@
down_read_failed(struct ld_semaphore *sem, long count, long timeout)
{
struct ldsem_waiter waiter;
- struct task_struct *tsk = current;
long adjust = -LDSEM_ACTIVE_BIAS + LDSEM_WAIT_BIAS;
/* set up my own style of waitqueue */
@@ -221,8 +220,8 @@
list_add_tail(&waiter.list, &sem->read_wait);
sem->wait_readers++;
- waiter.task = tsk;
- get_task_struct(tsk);
+ waiter.task = current;
+ get_task_struct(current);
/* if there are no active locks, wake the new lock owner(s) */
if ((count & LDSEM_ACTIVE_MASK) == 0)
@@ -232,7 +231,7 @@
/* wait to be given the lock */
for (;;) {
- set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ set_current_state(TASK_UNINTERRUPTIBLE);
if (!waiter.task)
break;
@@ -241,7 +240,7 @@
timeout = schedule_timeout(timeout);
}
- __set_task_state(tsk, TASK_RUNNING);
+ __set_current_state(TASK_RUNNING);
if (!timeout) {
/* lock timed out but check if this task was just
@@ -268,7 +267,6 @@
down_write_failed(struct ld_semaphore *sem, long count, long timeout)
{
struct ldsem_waiter waiter;
- struct task_struct *tsk = current;
long adjust = -LDSEM_ACTIVE_BIAS;
int locked = 0;
@@ -289,16 +287,16 @@
list_add_tail(&waiter.list, &sem->write_wait);
- waiter.task = tsk;
+ waiter.task = current;
- set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ set_current_state(TASK_UNINTERRUPTIBLE);
for (;;) {
if (!timeout)
break;
raw_spin_unlock_irq(&sem->wait_lock);
timeout = schedule_timeout(timeout);
raw_spin_lock_irq(&sem->wait_lock);
- set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ set_current_state(TASK_UNINTERRUPTIBLE);
locked = writer_trylock(sem);
if (locked)
break;
@@ -309,7 +307,7 @@
list_del(&waiter.list);
raw_spin_unlock_irq(&sem->wait_lock);
- __set_task_state(tsk, TASK_RUNNING);
+ __set_current_state(TASK_RUNNING);
/* lock wait may have timed out */
if (!locked)
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index fd80c1b..e6a1745 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -3698,7 +3698,7 @@
goto done;
if (opts->no_configfs || !opts->func_inst.group.cg_item.ci_parent
- || !atomic_read(&opts->func_inst.group.cg_item.ci_kref.refcount))
+ || !kref_read(&opts->func_inst.group.cg_item.ci_kref))
goto done;
ci = opts->func_inst.group.cg_item.ci_parent->ci_parent;
diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c
index 33ff49c..4684734 100644
--- a/drivers/usb/mon/mon_main.c
+++ b/drivers/usb/mon/mon_main.c
@@ -409,7 +409,7 @@
printk(KERN_ERR TAG
": Outstanding opens (%d) on usb%d, leaking...\n",
mbus->nreaders, mbus->u_bus->busnum);
- atomic_set(&mbus->ref.refcount, 2); /* Force leak */
+ kref_get(&mbus->ref); /* Force leak */
}
mon_dissolve(mbus, mbus->u_bus);
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index b3cc33f..bd6f293 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -38,6 +38,8 @@
#include <linux/workqueue.h>
#include <linux/mdev.h>
#include <linux/notifier.h>
+#include <linux/dma-iommu.h>
+#include <linux/irqdomain.h>
#define DRIVER_VERSION "0.2"
#define DRIVER_AUTHOR "Alex Williamson <alex.williamson@redhat.com>"
@@ -1179,6 +1181,28 @@
return NULL;
}
+static bool vfio_iommu_has_resv_msi(struct iommu_group *group,
+ phys_addr_t *base)
+{
+ struct list_head group_resv_regions;
+ struct iommu_resv_region *region, *next;
+ bool ret = false;
+
+ INIT_LIST_HEAD(&group_resv_regions);
+ iommu_get_group_resv_regions(group, &group_resv_regions);
+ list_for_each_entry(region, &group_resv_regions, list) {
+ if (region->type & IOMMU_RESV_MSI) {
+ *base = region->start;
+ ret = true;
+ goto out;
+ }
+ }
+out:
+ list_for_each_entry_safe(region, next, &group_resv_regions, list)
+ kfree(region);
+ return ret;
+}
+
static int vfio_iommu_type1_attach_group(void *iommu_data,
struct iommu_group *iommu_group)
{
@@ -1187,6 +1211,8 @@
struct vfio_domain *domain, *d;
struct bus_type *bus = NULL, *mdev_bus;
int ret;
+ bool resv_msi, msi_remap;
+ phys_addr_t resv_msi_base;
mutex_lock(&iommu->lock);
@@ -1256,11 +1282,15 @@
if (ret)
goto out_domain;
+ resv_msi = vfio_iommu_has_resv_msi(iommu_group, &resv_msi_base);
+
INIT_LIST_HEAD(&domain->group_list);
list_add(&group->next, &domain->group_list);
- if (!allow_unsafe_interrupts &&
- !iommu_capable(bus, IOMMU_CAP_INTR_REMAP)) {
+ msi_remap = resv_msi ? irq_domain_check_msi_remap() :
+ iommu_capable(bus, IOMMU_CAP_INTR_REMAP);
+
+ if (!allow_unsafe_interrupts && !msi_remap) {
pr_warn("%s: No interrupt remapping support. Use the module param \"allow_unsafe_interrupts\" to enable VFIO IOMMU support on this platform\n",
__func__);
ret = -EPERM;
@@ -1302,6 +1332,12 @@
if (ret)
goto out_detach;
+ if (resv_msi) {
+ ret = iommu_get_msi_cookie(domain->domain, resv_msi_base);
+ if (ret)
+ goto out_detach;
+ }
+
list_add(&domain->next, &iommu->domain_list);
mutex_unlock(&iommu->lock);
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 4223702..e7bf013 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -1428,17 +1428,18 @@
* group-wide total, not its individual thread total.
*/
thread_group_cputime(p, &cputime);
- cputime_to_timeval(cputime.utime, &prstatus->pr_utime);
- cputime_to_timeval(cputime.stime, &prstatus->pr_stime);
+ prstatus->pr_utime = ns_to_timeval(cputime.utime);
+ prstatus->pr_stime = ns_to_timeval(cputime.stime);
} else {
- cputime_t utime, stime;
+ u64 utime, stime;
task_cputime(p, &utime, &stime);
- cputime_to_timeval(utime, &prstatus->pr_utime);
- cputime_to_timeval(stime, &prstatus->pr_stime);
+ prstatus->pr_utime = ns_to_timeval(utime);
+ prstatus->pr_stime = ns_to_timeval(stime);
}
- cputime_to_timeval(p->signal->cutime, &prstatus->pr_cutime);
- cputime_to_timeval(p->signal->cstime, &prstatus->pr_cstime);
+
+ prstatus->pr_cutime = ns_to_timeval(p->signal->cutime);
+ prstatus->pr_cstime = ns_to_timeval(p->signal->cstime);
}
static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index d2e36f8..ffca4bb 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -1349,17 +1349,17 @@
* group-wide total, not its individual thread total.
*/
thread_group_cputime(p, &cputime);
- cputime_to_timeval(cputime.utime, &prstatus->pr_utime);
- cputime_to_timeval(cputime.stime, &prstatus->pr_stime);
+ prstatus->pr_utime = ns_to_timeval(cputime.utime);
+ prstatus->pr_stime = ns_to_timeval(cputime.stime);
} else {
- cputime_t utime, stime;
+ u64 utime, stime;
task_cputime(p, &utime, &stime);
- cputime_to_timeval(utime, &prstatus->pr_utime);
- cputime_to_timeval(stime, &prstatus->pr_stime);
+ prstatus->pr_utime = ns_to_timeval(utime);
+ prstatus->pr_stime = ns_to_timeval(stime);
}
- cputime_to_timeval(p->signal->cutime, &prstatus->pr_cutime);
- cputime_to_timeval(p->signal->cstime, &prstatus->pr_cstime);
+ prstatus->pr_cutime = ns_to_timeval(p->signal->cutime);
+ prstatus->pr_cstime = ns_to_timeval(p->signal->cstime);
prstatus->pr_exec_fdpic_loadmap = p->mm->context.exec_fdpic_loadmap;
prstatus->pr_interp_fdpic_loadmap = p->mm->context.interp_fdpic_loadmap;
diff --git a/fs/compat_binfmt_elf.c b/fs/compat_binfmt_elf.c
index 4d24d17..504b3c35 100644
--- a/fs/compat_binfmt_elf.c
+++ b/fs/compat_binfmt_elf.c
@@ -51,22 +51,8 @@
#define elf_prstatus compat_elf_prstatus
#define elf_prpsinfo compat_elf_prpsinfo
-/*
- * Compat version of cputime_to_compat_timeval, perhaps this
- * should be an inline in <linux/compat.h>.
- */
-static void cputime_to_compat_timeval(const cputime_t cputime,
- struct compat_timeval *value)
-{
- struct timeval tv;
- cputime_to_timeval(cputime, &tv);
- value->tv_sec = tv.tv_sec;
- value->tv_usec = tv.tv_usec;
-}
-
-#undef cputime_to_timeval
-#define cputime_to_timeval cputime_to_compat_timeval
-
+#undef ns_to_timeval
+#define ns_to_timeval ns_to_compat_timeval
/*
* To use this file, asm/elf.h must define compat_elf_check_arch.
diff --git a/fs/exofs/sys.c b/fs/exofs/sys.c
index 5e6a2c0..1f7d5e4 100644
--- a/fs/exofs/sys.c
+++ b/fs/exofs/sys.c
@@ -122,7 +122,7 @@
list_for_each_entry_safe(k_name, k_tmp, &exofs_kset->list, entry) {
printk(KERN_INFO "%s: name %s ref %d\n",
__func__, kobject_name(k_name),
- (int)atomic_read(&k_name->kref.refcount));
+ (int)kref_read(&k_name->kref));
}
#endif
}
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 4e06a27..f117926 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -399,6 +399,10 @@
static void queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req)
{
spin_lock(&fiq->waitq.lock);
+ if (test_bit(FR_FINISHED, &req->flags)) {
+ spin_unlock(&fiq->waitq.lock);
+ return;
+ }
if (list_empty(&req->intr_entry)) {
list_add_tail(&req->intr_entry, &fiq->interrupts);
wake_up_locked(&fiq->waitq);
@@ -1372,6 +1376,7 @@
* code can Oops if the buffer persists after module unload.
*/
bufs[page_nr].ops = &nosteal_pipe_buf_ops;
+ bufs[page_nr].flags = 0;
ret = add_to_pipe(pipe, &bufs[page_nr++]);
if (unlikely(ret < 0))
break;
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 9130794..052f8d3 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -256,7 +256,7 @@
#define FUSE_IO_PRIV_SYNC(f) \
{ \
- .refcnt = { ATOMIC_INIT(1) }, \
+ .refcnt = KREF_INIT(1), \
.async = 0, \
.file = f, \
}
diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c
index 8c51436..b6b194e 100644
--- a/fs/jbd2/commit.c
+++ b/fs/jbd2/commit.c
@@ -393,7 +393,7 @@
/* Do we need to erase the effects of a prior jbd2_journal_flush? */
if (journal->j_flags & JBD2_FLUSHED) {
jbd_debug(3, "super block updated\n");
- mutex_lock(&journal->j_checkpoint_mutex);
+ mutex_lock_io(&journal->j_checkpoint_mutex);
/*
* We hold j_checkpoint_mutex so tail cannot change under us.
* We don't need any special data guarantees for writing sb
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index a097048..d8a5d0a 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -944,7 +944,7 @@
*/
void jbd2_update_log_tail(journal_t *journal, tid_t tid, unsigned long block)
{
- mutex_lock(&journal->j_checkpoint_mutex);
+ mutex_lock_io(&journal->j_checkpoint_mutex);
if (tid_gt(tid, journal->j_tail_sequence))
__jbd2_update_log_tail(journal, tid, block);
mutex_unlock(&journal->j_checkpoint_mutex);
@@ -1304,7 +1304,7 @@
journal->j_flags |= JBD2_FLUSHED;
} else {
/* Lock here to make assertions happy... */
- mutex_lock(&journal->j_checkpoint_mutex);
+ mutex_lock_io(&journal->j_checkpoint_mutex);
/*
* Update log tail information. We use REQ_FUA since new
* transaction will start reusing journal space and so we
@@ -1691,7 +1691,7 @@
spin_lock(&journal->j_list_lock);
while (journal->j_checkpoint_transactions != NULL) {
spin_unlock(&journal->j_list_lock);
- mutex_lock(&journal->j_checkpoint_mutex);
+ mutex_lock_io(&journal->j_checkpoint_mutex);
err = jbd2_log_do_checkpoint(journal);
mutex_unlock(&journal->j_checkpoint_mutex);
/*
@@ -1713,7 +1713,7 @@
if (journal->j_sb_buffer) {
if (!is_journal_aborted(journal)) {
- mutex_lock(&journal->j_checkpoint_mutex);
+ mutex_lock_io(&journal->j_checkpoint_mutex);
write_lock(&journal->j_state_lock);
journal->j_tail_sequence =
@@ -1955,7 +1955,7 @@
spin_lock(&journal->j_list_lock);
while (!err && journal->j_checkpoint_transactions != NULL) {
spin_unlock(&journal->j_list_lock);
- mutex_lock(&journal->j_checkpoint_mutex);
+ mutex_lock_io(&journal->j_checkpoint_mutex);
err = jbd2_log_do_checkpoint(journal);
mutex_unlock(&journal->j_checkpoint_mutex);
spin_lock(&journal->j_list_lock);
@@ -1965,7 +1965,7 @@
if (is_journal_aborted(journal))
return -EIO;
- mutex_lock(&journal->j_checkpoint_mutex);
+ mutex_lock_io(&journal->j_checkpoint_mutex);
if (!err) {
err = jbd2_cleanup_journal_tail(journal);
if (err < 0) {
diff --git a/fs/ocfs2/cluster/netdebug.c b/fs/ocfs2/cluster/netdebug.c
index 27d1242..564c504 100644
--- a/fs/ocfs2/cluster/netdebug.c
+++ b/fs/ocfs2/cluster/netdebug.c
@@ -349,7 +349,7 @@
" func key: 0x%08x\n"
" func type: %u\n",
sc,
- atomic_read(&sc->sc_kref.refcount),
+ kref_read(&sc->sc_kref),
&saddr, inet ? ntohs(sport) : 0,
&daddr, inet ? ntohs(dport) : 0,
sc->sc_node->nd_name,
diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c
index d4b5c81..ec00057 100644
--- a/fs/ocfs2/cluster/tcp.c
+++ b/fs/ocfs2/cluster/tcp.c
@@ -97,7 +97,7 @@
typeof(sc) __sc = (sc); \
mlog(ML_SOCKET, "[sc %p refs %d sock %p node %u page %p " \
"pg_off %zu] " fmt, __sc, \
- atomic_read(&__sc->sc_kref.refcount), __sc->sc_sock, \
+ kref_read(&__sc->sc_kref), __sc->sc_sock, \
__sc->sc_node->nd_num, __sc->sc_page, __sc->sc_page_off , \
##args); \
} while (0)
diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c
index e7b760d..9b984ca 100644
--- a/fs/ocfs2/dlm/dlmdebug.c
+++ b/fs/ocfs2/dlm/dlmdebug.c
@@ -81,7 +81,7 @@
lock->ml.type, lock->ml.convert_type, lock->ml.node,
dlm_get_lock_cookie_node(be64_to_cpu(lock->ml.cookie)),
dlm_get_lock_cookie_seq(be64_to_cpu(lock->ml.cookie)),
- atomic_read(&lock->lock_refs.refcount),
+ kref_read(&lock->lock_refs),
(list_empty(&lock->ast_list) ? 'y' : 'n'),
(lock->ast_pending ? 'y' : 'n'),
(list_empty(&lock->bast_list) ? 'y' : 'n'),
@@ -106,7 +106,7 @@
printk("lockres: %s, owner=%u, state=%u\n",
buf, res->owner, res->state);
printk(" last used: %lu, refcnt: %u, on purge list: %s\n",
- res->last_used, atomic_read(&res->refs.refcount),
+ res->last_used, kref_read(&res->refs),
list_empty(&res->purge) ? "no" : "yes");
printk(" on dirty list: %s, on reco list: %s, "
"migrating pending: %s\n",
@@ -298,7 +298,7 @@
mle_type, mle->master, mle->new_master,
!list_empty(&mle->hb_events),
!!mle->inuse,
- atomic_read(&mle->mle_refs.refcount));
+ kref_read(&mle->mle_refs));
out += snprintf(buf + out, len - out, "Maybe=");
out += stringify_nodemap(mle->maybe_map, O2NM_MAX_NODES,
@@ -494,7 +494,7 @@
lock->ast_pending, lock->bast_pending,
lock->convert_pending, lock->lock_pending,
lock->cancel_pending, lock->unlock_pending,
- atomic_read(&lock->lock_refs.refcount));
+ kref_read(&lock->lock_refs));
spin_unlock(&lock->spinlock);
return out;
@@ -521,7 +521,7 @@
!list_empty(&res->recovering),
res->inflight_locks, res->migration_pending,
atomic_read(&res->asts_reserved),
- atomic_read(&res->refs.refcount));
+ kref_read(&res->refs));
/* refmap */
out += snprintf(buf + out, len - out, "RMAP:");
@@ -777,7 +777,7 @@
/* Purge Count: xxx Refs: xxx */
out += snprintf(buf + out, len - out,
"Purge Count: %d Refs: %d\n", dlm->purge_count,
- atomic_read(&dlm->dlm_refs.refcount));
+ kref_read(&dlm->dlm_refs));
/* Dead Node: xxx */
out += snprintf(buf + out, len - out,
diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c
index 733e4e7..32fd261 100644
--- a/fs/ocfs2/dlm/dlmdomain.c
+++ b/fs/ocfs2/dlm/dlmdomain.c
@@ -2072,7 +2072,7 @@
INIT_LIST_HEAD(&dlm->dlm_eviction_callbacks);
mlog(0, "context init: refcount %u\n",
- atomic_read(&dlm->dlm_refs.refcount));
+ kref_read(&dlm->dlm_refs));
leave:
if (ret < 0 && dlm) {
diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c
index a464c80..7025d8c 100644
--- a/fs/ocfs2/dlm/dlmmaster.c
+++ b/fs/ocfs2/dlm/dlmmaster.c
@@ -233,7 +233,7 @@
assert_spin_locked(&dlm->spinlock);
assert_spin_locked(&dlm->master_lock);
- if (!atomic_read(&mle->mle_refs.refcount)) {
+ if (!kref_read(&mle->mle_refs)) {
/* this may or may not crash, but who cares.
* it's a BUG. */
mlog(ML_ERROR, "bad mle: %p\n", mle);
@@ -1124,9 +1124,9 @@
unsigned long timeo = msecs_to_jiffies(DLM_MASTERY_TIMEOUT_MS);
/*
- if (atomic_read(&mle->mle_refs.refcount) < 2)
+ if (kref_read(&mle->mle_refs) < 2)
mlog(ML_ERROR, "mle (%p) refs=%d, name=%.*s\n", mle,
- atomic_read(&mle->mle_refs.refcount),
+ kref_read(&mle->mle_refs),
res->lockname.len, res->lockname.name);
*/
atomic_set(&mle->woken, 0);
@@ -1979,7 +1979,7 @@
* on this mle. */
spin_lock(&dlm->master_lock);
- rr = atomic_read(&mle->mle_refs.refcount);
+ rr = kref_read(&mle->mle_refs);
if (mle->inuse > 0) {
if (extra_ref && rr < 3)
err = 1;
diff --git a/fs/ocfs2/dlm/dlmunlock.c b/fs/ocfs2/dlm/dlmunlock.c
index 1082b2c..63d701c 100644
--- a/fs/ocfs2/dlm/dlmunlock.c
+++ b/fs/ocfs2/dlm/dlmunlock.c
@@ -251,7 +251,7 @@
mlog(0, "lock %u:%llu should be gone now! refs=%d\n",
dlm_get_lock_cookie_node(be64_to_cpu(lock->ml.cookie)),
dlm_get_lock_cookie_seq(be64_to_cpu(lock->ml.cookie)),
- atomic_read(&lock->lock_refs.refcount)-1);
+ kref_read(&lock->lock_refs)-1);
dlm_lock_put(lock);
}
if (actions & DLM_UNLOCK_CALL_AST)
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 51a4213..fe12b51 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -401,8 +401,8 @@
unsigned long long start_time;
unsigned long cmin_flt = 0, cmaj_flt = 0;
unsigned long min_flt = 0, maj_flt = 0;
- cputime_t cutime, cstime, utime, stime;
- cputime_t cgtime, gtime;
+ u64 cutime, cstime, utime, stime;
+ u64 cgtime, gtime;
unsigned long rsslim = 0;
char tcomm[sizeof(task->comm)];
unsigned long flags;
@@ -497,10 +497,10 @@
seq_put_decimal_ull(m, " ", cmin_flt);
seq_put_decimal_ull(m, " ", maj_flt);
seq_put_decimal_ull(m, " ", cmaj_flt);
- seq_put_decimal_ull(m, " ", cputime_to_clock_t(utime));
- seq_put_decimal_ull(m, " ", cputime_to_clock_t(stime));
- seq_put_decimal_ll(m, " ", cputime_to_clock_t(cutime));
- seq_put_decimal_ll(m, " ", cputime_to_clock_t(cstime));
+ seq_put_decimal_ull(m, " ", nsec_to_clock_t(utime));
+ seq_put_decimal_ull(m, " ", nsec_to_clock_t(stime));
+ seq_put_decimal_ll(m, " ", nsec_to_clock_t(cutime));
+ seq_put_decimal_ll(m, " ", nsec_to_clock_t(cstime));
seq_put_decimal_ll(m, " ", priority);
seq_put_decimal_ll(m, " ", nice);
seq_put_decimal_ll(m, " ", num_threads);
@@ -542,8 +542,8 @@
seq_put_decimal_ull(m, " ", task->rt_priority);
seq_put_decimal_ull(m, " ", task->policy);
seq_put_decimal_ull(m, " ", delayacct_blkio_ticks(task));
- seq_put_decimal_ull(m, " ", cputime_to_clock_t(gtime));
- seq_put_decimal_ll(m, " ", cputime_to_clock_t(cgtime));
+ seq_put_decimal_ull(m, " ", nsec_to_clock_t(gtime));
+ seq_put_decimal_ll(m, " ", nsec_to_clock_t(cgtime));
if (mm && permitted) {
seq_put_decimal_ull(m, " ", mm->start_data);
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 87c9a9a..b1f7d30 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2179,7 +2179,7 @@
.llseek = generic_file_llseek,
};
-#ifdef CONFIG_CHECKPOINT_RESTORE
+#if defined(CONFIG_CHECKPOINT_RESTORE) && defined(CONFIG_POSIX_TIMERS)
struct timers_private {
struct pid *pid;
struct task_struct *task;
@@ -2936,7 +2936,7 @@
REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations),
REG("setgroups", S_IRUGO|S_IWUSR, proc_setgroups_operations),
#endif
-#ifdef CONFIG_CHECKPOINT_RESTORE
+#if defined(CONFIG_CHECKPOINT_RESTORE) && defined(CONFIG_POSIX_TIMERS)
REG("timers", S_IRUGO, proc_timers_operations),
#endif
REG("timerslack_ns", S_IRUGO|S_IWUGO, proc_pid_set_timerslack_ns_operations),
diff --git a/fs/proc/stat.c b/fs/proc/stat.c
index d700c42..e47c3e8 100644
--- a/fs/proc/stat.c
+++ b/fs/proc/stat.c
@@ -21,9 +21,9 @@
#ifdef arch_idle_time
-static cputime64_t get_idle_time(int cpu)
+static u64 get_idle_time(int cpu)
{
- cputime64_t idle;
+ u64 idle;
idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE];
if (cpu_online(cpu) && !nr_iowait_cpu(cpu))
@@ -31,9 +31,9 @@
return idle;
}
-static cputime64_t get_iowait_time(int cpu)
+static u64 get_iowait_time(int cpu)
{
- cputime64_t iowait;
+ u64 iowait;
iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT];
if (cpu_online(cpu) && nr_iowait_cpu(cpu))
@@ -45,32 +45,32 @@
static u64 get_idle_time(int cpu)
{
- u64 idle, idle_time = -1ULL;
+ u64 idle, idle_usecs = -1ULL;
if (cpu_online(cpu))
- idle_time = get_cpu_idle_time_us(cpu, NULL);
+ idle_usecs = get_cpu_idle_time_us(cpu, NULL);
- if (idle_time == -1ULL)
+ if (idle_usecs == -1ULL)
/* !NO_HZ or cpu offline so we can rely on cpustat.idle */
idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE];
else
- idle = usecs_to_cputime64(idle_time);
+ idle = idle_usecs * NSEC_PER_USEC;
return idle;
}
static u64 get_iowait_time(int cpu)
{
- u64 iowait, iowait_time = -1ULL;
+ u64 iowait, iowait_usecs = -1ULL;
if (cpu_online(cpu))
- iowait_time = get_cpu_iowait_time_us(cpu, NULL);
+ iowait_usecs = get_cpu_iowait_time_us(cpu, NULL);
- if (iowait_time == -1ULL)
+ if (iowait_usecs == -1ULL)
/* !NO_HZ or cpu offline so we can rely on cpustat.iowait */
iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT];
else
- iowait = usecs_to_cputime64(iowait_time);
+ iowait = iowait_usecs * NSEC_PER_USEC;
return iowait;
}
@@ -115,16 +115,16 @@
}
sum += arch_irq_stat();
- seq_put_decimal_ull(p, "cpu ", cputime64_to_clock_t(user));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(nice));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(system));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(idle));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(iowait));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(irq));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(softirq));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(steal));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(guest));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(guest_nice));
+ seq_put_decimal_ull(p, "cpu ", nsec_to_clock_t(user));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(nice));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(system));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(idle));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(iowait));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(irq));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(softirq));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(steal));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest_nice));
seq_putc(p, '\n');
for_each_online_cpu(i) {
@@ -140,16 +140,16 @@
guest = kcpustat_cpu(i).cpustat[CPUTIME_GUEST];
guest_nice = kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE];
seq_printf(p, "cpu%d", i);
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(user));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(nice));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(system));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(idle));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(iowait));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(irq));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(softirq));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(steal));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(guest));
- seq_put_decimal_ull(p, " ", cputime64_to_clock_t(guest_nice));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(user));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(nice));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(system));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(idle));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(iowait));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(irq));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(softirq));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(steal));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest));
+ seq_put_decimal_ull(p, " ", nsec_to_clock_t(guest_nice));
seq_putc(p, '\n');
}
seq_put_decimal_ull(p, "intr ", (unsigned long long)sum);
diff --git a/fs/proc/uptime.c b/fs/proc/uptime.c
index 33de567..7981c4f 100644
--- a/fs/proc/uptime.c
+++ b/fs/proc/uptime.c
@@ -5,23 +5,20 @@
#include <linux/seq_file.h>
#include <linux/time.h>
#include <linux/kernel_stat.h>
-#include <linux/cputime.h>
static int uptime_proc_show(struct seq_file *m, void *v)
{
struct timespec uptime;
struct timespec idle;
- u64 idletime;
u64 nsec;
u32 rem;
int i;
- idletime = 0;
+ nsec = 0;
for_each_possible_cpu(i)
- idletime += (__force u64) kcpustat_cpu(i).cpustat[CPUTIME_IDLE];
+ nsec += (__force u64) kcpustat_cpu(i).cpustat[CPUTIME_IDLE];
get_monotonic_boottime(&uptime);
- nsec = cputime64_to_jiffies64(idletime) * TICK_NSEC;
idle.tv_sec = div_u64_rem(nsec, NSEC_PER_SEC, &rem);
idle.tv_nsec = rem;
seq_printf(m, "%lu.%02lu %lu.%02lu\n",
diff --git a/fs/splice.c b/fs/splice.c
index 873d831..4ef78aa 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -204,6 +204,7 @@
buf->len = spd->partial[page_nr].len;
buf->private = spd->partial[page_nr].private;
buf->ops = spd->ops;
+ buf->flags = 0;
pipe->nrbufs++;
page_nr++;
diff --git a/fs/timerfd.c b/fs/timerfd.c
index c173cc1..384fa75 100644
--- a/fs/timerfd.c
+++ b/fs/timerfd.c
@@ -40,6 +40,7 @@
short unsigned settime_flags; /* to show in fdinfo */
struct rcu_head rcu;
struct list_head clist;
+ spinlock_t cancel_lock;
bool might_cancel;
};
@@ -112,7 +113,7 @@
rcu_read_unlock();
}
-static void timerfd_remove_cancel(struct timerfd_ctx *ctx)
+static void __timerfd_remove_cancel(struct timerfd_ctx *ctx)
{
if (ctx->might_cancel) {
ctx->might_cancel = false;
@@ -122,6 +123,13 @@
}
}
+static void timerfd_remove_cancel(struct timerfd_ctx *ctx)
+{
+ spin_lock(&ctx->cancel_lock);
+ __timerfd_remove_cancel(ctx);
+ spin_unlock(&ctx->cancel_lock);
+}
+
static bool timerfd_canceled(struct timerfd_ctx *ctx)
{
if (!ctx->might_cancel || ctx->moffs != KTIME_MAX)
@@ -132,6 +140,7 @@
static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags)
{
+ spin_lock(&ctx->cancel_lock);
if ((ctx->clockid == CLOCK_REALTIME ||
ctx->clockid == CLOCK_REALTIME_ALARM) &&
(flags & TFD_TIMER_ABSTIME) && (flags & TFD_TIMER_CANCEL_ON_SET)) {
@@ -141,9 +150,10 @@
list_add_rcu(&ctx->clist, &cancel_list);
spin_unlock(&cancel_lock);
}
- } else if (ctx->might_cancel) {
- timerfd_remove_cancel(ctx);
+ } else {
+ __timerfd_remove_cancel(ctx);
}
+ spin_unlock(&ctx->cancel_lock);
}
static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx)
@@ -400,6 +410,7 @@
return -ENOMEM;
init_waitqueue_head(&ctx->wqh);
+ spin_lock_init(&ctx->cancel_lock);
ctx->clockid = clockid;
if (isalarm(ctx))
diff --git a/include/asm-generic/cputime.h b/include/asm-generic/cputime.h
deleted file mode 100644
index 5196943..0000000
--- a/include/asm-generic/cputime.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef _ASM_GENERIC_CPUTIME_H
-#define _ASM_GENERIC_CPUTIME_H
-
-#include <linux/time.h>
-#include <linux/jiffies.h>
-
-#ifndef CONFIG_VIRT_CPU_ACCOUNTING
-# include <asm-generic/cputime_jiffies.h>
-#endif
-
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
-# include <asm-generic/cputime_nsecs.h>
-#endif
-
-#endif
diff --git a/include/asm-generic/cputime_jiffies.h b/include/asm-generic/cputime_jiffies.h
deleted file mode 100644
index 6bb8cd4..0000000
--- a/include/asm-generic/cputime_jiffies.h
+++ /dev/null
@@ -1,75 +0,0 @@
-#ifndef _ASM_GENERIC_CPUTIME_JIFFIES_H
-#define _ASM_GENERIC_CPUTIME_JIFFIES_H
-
-typedef unsigned long __nocast cputime_t;
-
-#define cmpxchg_cputime(ptr, old, new) cmpxchg(ptr, old, new)
-
-#define cputime_one_jiffy jiffies_to_cputime(1)
-#define cputime_to_jiffies(__ct) (__force unsigned long)(__ct)
-#define jiffies_to_cputime(__hz) (__force cputime_t)(__hz)
-
-typedef u64 __nocast cputime64_t;
-
-#define cputime64_to_jiffies64(__ct) (__force u64)(__ct)
-#define jiffies64_to_cputime64(__jif) (__force cputime64_t)(__jif)
-
-
-/*
- * Convert nanoseconds <-> cputime
- */
-#define cputime_to_nsecs(__ct) \
- jiffies_to_nsecs(cputime_to_jiffies(__ct))
-#define nsecs_to_cputime64(__nsec) \
- jiffies64_to_cputime64(nsecs_to_jiffies64(__nsec))
-#define nsecs_to_cputime(__nsec) \
- jiffies_to_cputime(nsecs_to_jiffies(__nsec))
-
-
-/*
- * Convert cputime to microseconds and back.
- */
-#define cputime_to_usecs(__ct) \
- jiffies_to_usecs(cputime_to_jiffies(__ct))
-#define usecs_to_cputime(__usec) \
- jiffies_to_cputime(usecs_to_jiffies(__usec))
-#define usecs_to_cputime64(__usec) \
- jiffies64_to_cputime64(nsecs_to_jiffies64((__usec) * 1000))
-
-/*
- * Convert cputime to seconds and back.
- */
-#define cputime_to_secs(jif) (cputime_to_jiffies(jif) / HZ)
-#define secs_to_cputime(sec) jiffies_to_cputime((sec) * HZ)
-
-/*
- * Convert cputime to timespec and back.
- */
-#define timespec_to_cputime(__val) \
- jiffies_to_cputime(timespec_to_jiffies(__val))
-#define cputime_to_timespec(__ct,__val) \
- jiffies_to_timespec(cputime_to_jiffies(__ct),__val)
-
-/*
- * Convert cputime to timeval and back.
- */
-#define timeval_to_cputime(__val) \
- jiffies_to_cputime(timeval_to_jiffies(__val))
-#define cputime_to_timeval(__ct,__val) \
- jiffies_to_timeval(cputime_to_jiffies(__ct),__val)
-
-/*
- * Convert cputime to clock and back.
- */
-#define cputime_to_clock_t(__ct) \
- jiffies_to_clock_t(cputime_to_jiffies(__ct))
-#define clock_t_to_cputime(__x) \
- jiffies_to_cputime(clock_t_to_jiffies(__x))
-
-/*
- * Convert cputime64 to clock.
- */
-#define cputime64_to_clock_t(__ct) \
- jiffies_64_to_clock_t(cputime64_to_jiffies64(__ct))
-
-#endif
diff --git a/include/asm-generic/cputime_nsecs.h b/include/asm-generic/cputime_nsecs.h
deleted file mode 100644
index 4e3b18e..0000000
--- a/include/asm-generic/cputime_nsecs.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Definitions for measuring cputime in nsecs resolution.
- *
- * Based on <arch/ia64/include/asm/cputime.h>
- *
- * Copyright (C) 2007 FUJITSU LIMITED
- * Copyright (C) 2007 Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- */
-
-#ifndef _ASM_GENERIC_CPUTIME_NSECS_H
-#define _ASM_GENERIC_CPUTIME_NSECS_H
-
-#include <linux/math64.h>
-
-typedef u64 __nocast cputime_t;
-typedef u64 __nocast cputime64_t;
-
-#define cmpxchg_cputime(ptr, old, new) cmpxchg64(ptr, old, new)
-
-#define cputime_one_jiffy jiffies_to_cputime(1)
-
-#define cputime_div(__ct, divisor) div_u64((__force u64)__ct, divisor)
-#define cputime_div_rem(__ct, divisor, remainder) \
- div_u64_rem((__force u64)__ct, divisor, remainder);
-
-/*
- * Convert cputime <-> jiffies (HZ)
- */
-#define cputime_to_jiffies(__ct) \
- cputime_div(__ct, NSEC_PER_SEC / HZ)
-#define jiffies_to_cputime(__jif) \
- (__force cputime_t)((__jif) * (NSEC_PER_SEC / HZ))
-#define cputime64_to_jiffies64(__ct) \
- cputime_div(__ct, NSEC_PER_SEC / HZ)
-#define jiffies64_to_cputime64(__jif) \
- (__force cputime64_t)((__jif) * (NSEC_PER_SEC / HZ))
-
-
-/*
- * Convert cputime <-> nanoseconds
- */
-#define cputime_to_nsecs(__ct) \
- (__force u64)(__ct)
-#define nsecs_to_cputime(__nsecs) \
- (__force cputime_t)(__nsecs)
-#define nsecs_to_cputime64(__nsecs) \
- (__force cputime64_t)(__nsecs)
-
-
-/*
- * Convert cputime <-> microseconds
- */
-#define cputime_to_usecs(__ct) \
- cputime_div(__ct, NSEC_PER_USEC)
-#define usecs_to_cputime(__usecs) \
- (__force cputime_t)((__usecs) * NSEC_PER_USEC)
-#define usecs_to_cputime64(__usecs) \
- (__force cputime64_t)((__usecs) * NSEC_PER_USEC)
-
-/*
- * Convert cputime <-> seconds
- */
-#define cputime_to_secs(__ct) \
- cputime_div(__ct, NSEC_PER_SEC)
-#define secs_to_cputime(__secs) \
- (__force cputime_t)((__secs) * NSEC_PER_SEC)
-
-/*
- * Convert cputime <-> timespec (nsec)
- */
-static inline cputime_t timespec_to_cputime(const struct timespec *val)
-{
- u64 ret = (u64)val->tv_sec * NSEC_PER_SEC + val->tv_nsec;
- return (__force cputime_t) ret;
-}
-static inline void cputime_to_timespec(const cputime_t ct, struct timespec *val)
-{
- u32 rem;
-
- val->tv_sec = cputime_div_rem(ct, NSEC_PER_SEC, &rem);
- val->tv_nsec = rem;
-}
-
-/*
- * Convert cputime <-> timeval (msec)
- */
-static inline cputime_t timeval_to_cputime(const struct timeval *val)
-{
- u64 ret = (u64)val->tv_sec * NSEC_PER_SEC +
- val->tv_usec * NSEC_PER_USEC;
- return (__force cputime_t) ret;
-}
-static inline void cputime_to_timeval(const cputime_t ct, struct timeval *val)
-{
- u32 rem;
-
- val->tv_sec = cputime_div_rem(ct, NSEC_PER_SEC, &rem);
- val->tv_usec = rem / NSEC_PER_USEC;
-}
-
-/*
- * Convert cputime <-> clock (USER_HZ)
- */
-#define cputime_to_clock_t(__ct) \
- cputime_div(__ct, (NSEC_PER_SEC / USER_HZ))
-#define clock_t_to_cputime(__x) \
- (__force cputime_t)((__x) * (NSEC_PER_SEC / USER_HZ))
-
-/*
- * Convert cputime64 to clock.
- */
-#define cputime64_to_clock_t(__ct) \
- cputime_to_clock_t((__force cputime_t)__ct)
-
-#endif
diff --git a/include/asm-generic/rwsem.h b/include/asm-generic/rwsem.h
index 5be122e..6c6a214 100644
--- a/include/asm-generic/rwsem.h
+++ b/include/asm-generic/rwsem.h
@@ -33,7 +33,7 @@
*/
static inline void __down_read(struct rw_semaphore *sem)
{
- if (unlikely(atomic_long_inc_return_acquire((atomic_long_t *)&sem->count) <= 0))
+ if (unlikely(atomic_long_inc_return_acquire(&sem->count) <= 0))
rwsem_down_read_failed(sem);
}
@@ -58,7 +58,7 @@
long tmp;
tmp = atomic_long_add_return_acquire(RWSEM_ACTIVE_WRITE_BIAS,
- (atomic_long_t *)&sem->count);
+ &sem->count);
if (unlikely(tmp != RWSEM_ACTIVE_WRITE_BIAS))
rwsem_down_write_failed(sem);
}
@@ -68,7 +68,7 @@
long tmp;
tmp = atomic_long_add_return_acquire(RWSEM_ACTIVE_WRITE_BIAS,
- (atomic_long_t *)&sem->count);
+ &sem->count);
if (unlikely(tmp != RWSEM_ACTIVE_WRITE_BIAS))
if (IS_ERR(rwsem_down_write_failed_killable(sem)))
return -EINTR;
@@ -91,7 +91,7 @@
{
long tmp;
- tmp = atomic_long_dec_return_release((atomic_long_t *)&sem->count);
+ tmp = atomic_long_dec_return_release(&sem->count);
if (unlikely(tmp < -1 && (tmp & RWSEM_ACTIVE_MASK) == 0))
rwsem_wake(sem);
}
@@ -102,7 +102,7 @@
static inline void __up_write(struct rw_semaphore *sem)
{
if (unlikely(atomic_long_sub_return_release(RWSEM_ACTIVE_WRITE_BIAS,
- (atomic_long_t *)&sem->count) < 0))
+ &sem->count) < 0))
rwsem_wake(sem);
}
@@ -120,8 +120,7 @@
* read-locked region is ok to be re-ordered into the
* write side. As such, rely on RELEASE semantics.
*/
- tmp = atomic_long_add_return_release(-RWSEM_WAITING_BIAS,
- (atomic_long_t *)&sem->count);
+ tmp = atomic_long_add_return_release(-RWSEM_WAITING_BIAS, &sem->count);
if (tmp < 0)
rwsem_downgrade_wake(sem);
}
diff --git a/include/drm/drm_framebuffer.h b/include/drm/drm_framebuffer.h
index 1ddfa29..a232e7f 100644
--- a/include/drm/drm_framebuffer.h
+++ b/include/drm/drm_framebuffer.h
@@ -247,7 +247,7 @@
*/
static inline uint32_t drm_framebuffer_read_refcount(struct drm_framebuffer *fb)
{
- return atomic_read(&fb->base.refcount.refcount);
+ return kref_read(&fb->base.refcount);
}
/**
diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
index 652e45b..9a46531 100644
--- a/include/drm/ttm/ttm_bo_api.h
+++ b/include/drm/ttm/ttm_bo_api.h
@@ -332,19 +332,6 @@
*/
extern void ttm_bo_unref(struct ttm_buffer_object **bo);
-
-/**
- * ttm_bo_list_ref_sub
- *
- * @bo: The buffer object.
- * @count: The number of references with which to decrease @bo::list_kref;
- * @never_free: The refcount should not reach zero with this operation.
- *
- * Release @count lru list references to this buffer object.
- */
-extern void ttm_bo_list_ref_sub(struct ttm_buffer_object *bo, int count,
- bool never_free);
-
/**
* ttm_bo_add_to_lru
*
@@ -367,7 +354,7 @@
* and is usually called just immediately after the bo has been reserved to
* avoid recursive reservation from lru lists.
*/
-extern int ttm_bo_del_from_lru(struct ttm_buffer_object *bo);
+extern void ttm_bo_del_from_lru(struct ttm_buffer_object *bo);
/**
* ttm_bo_move_to_lru_tail
diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h
index cdbdb40..feecf33 100644
--- a/include/drm/ttm/ttm_bo_driver.h
+++ b/include/drm/ttm/ttm_bo_driver.h
@@ -878,7 +878,7 @@
{
int ret;
- WARN_ON(!atomic_read(&bo->kref.refcount));
+ WARN_ON(!kref_read(&bo->kref));
ret = __ttm_bo_reserve(bo, interruptible, no_wait, ticket);
if (likely(ret == 0))
@@ -903,7 +903,7 @@
{
int ret = 0;
- WARN_ON(!atomic_read(&bo->kref.refcount));
+ WARN_ON(!kref_read(&bo->kref));
if (interruptible)
ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock,
diff --git a/include/dt-bindings/thermal/lm90.h b/include/dt-bindings/thermal/lm90.h
new file mode 100644
index 0000000..8c2e309
--- /dev/null
+++ b/include/dt-bindings/thermal/lm90.h
@@ -0,0 +1,12 @@
+/*
+ * This header provides constants for the LM90 thermal bindings.
+ */
+
+#ifndef _DT_BINDINGS_THERMAL_LM90_H_
+#define _DT_BINDINGS_THERMAL_LM90_H_
+
+#define LM90_LOCAL_TEMPERATURE 0
+#define LM90_REMOTE_TEMPERATURE 1
+#define LM90_REMOTE2_TEMPERATURE 2
+
+#endif
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 5b36974..8e577c2 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -1153,4 +1153,14 @@
static inline int parse_spcr(bool earlycon) { return 0; }
#endif
+#if IS_ENABLED(CONFIG_ACPI_GENERIC_GSI)
+int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res);
+#else
+static inline
+int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res)
+{
+ return -EINVAL;
+}
+#endif
+
#endif /*_LINUX_ACPI_H*/
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index b20e3d5..2f1c690 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -593,9 +593,6 @@
u32 blocksize;
u16 numblocks;
u32 size;
-
- struct mtd_info *mtd;
- void *priv;
};
#endif
diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h
index 92bc89a..c970a25 100644
--- a/include/linux/bpf-cgroup.h
+++ b/include/linux/bpf-cgroup.h
@@ -21,20 +21,19 @@
*/
struct bpf_prog *prog[MAX_BPF_ATTACH_TYPE];
struct bpf_prog __rcu *effective[MAX_BPF_ATTACH_TYPE];
+ bool disallow_override[MAX_BPF_ATTACH_TYPE];
};
void cgroup_bpf_put(struct cgroup *cgrp);
void cgroup_bpf_inherit(struct cgroup *cgrp, struct cgroup *parent);
-void __cgroup_bpf_update(struct cgroup *cgrp,
- struct cgroup *parent,
- struct bpf_prog *prog,
- enum bpf_attach_type type);
+int __cgroup_bpf_update(struct cgroup *cgrp, struct cgroup *parent,
+ struct bpf_prog *prog, enum bpf_attach_type type,
+ bool overridable);
/* Wrapper for __cgroup_bpf_update() protected by cgroup_mutex */
-void cgroup_bpf_update(struct cgroup *cgrp,
- struct bpf_prog *prog,
- enum bpf_attach_type type);
+int cgroup_bpf_update(struct cgroup *cgrp, struct bpf_prog *prog,
+ enum bpf_attach_type type, bool overridable);
int __cgroup_bpf_run_filter_skb(struct sock *sk,
struct sk_buff *skb,
diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h
index 0d442e3..5d3053c 100644
--- a/include/linux/clockchips.h
+++ b/include/linux/clockchips.h
@@ -224,4 +224,13 @@
#endif /* !CONFIG_GENERIC_CLOCKEVENTS */
+#define CLOCKEVENT_OF_DECLARE(name, compat, fn) \
+ OF_DECLARE_1_RET(clkevt, name, compat, fn)
+
+#ifdef CONFIG_CLKEVT_PROBE
+extern int clockevent_probe(void);
+#els
+static inline int clockevent_probe(void) { return 0; }
+#endif
+
#endif /* _LINUX_CLOCKCHIPS_H */
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
index e315d04..cfc7584 100644
--- a/include/linux/clocksource.h
+++ b/include/linux/clocksource.h
@@ -62,6 +62,8 @@
* @archdata: arch-specific data
* @suspend: suspend function for the clocksource, if necessary
* @resume: resume function for the clocksource, if necessary
+ * @mark_unstable: Optional function to inform the clocksource driver that
+ * the watchdog marked the clocksource unstable
* @owner: module reference, must be set by clocksource in modules
*
* Note: This struct is not used in hotpathes of the timekeeping code
@@ -93,6 +95,7 @@
unsigned long flags;
void (*suspend)(struct clocksource *cs);
void (*resume)(struct clocksource *cs);
+ void (*mark_unstable)(struct clocksource *cs);
/* private: */
#ifdef CONFIG_CLOCKSOURCE_WATCHDOG
diff --git a/include/linux/compat.h b/include/linux/compat.h
index 6360939..9e40be5 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -731,7 +731,25 @@
static inline bool in_compat_syscall(void) { return is_compat_task(); }
#endif
-#else
+/**
+ * ns_to_compat_timeval - Compat version of ns_to_timeval
+ * @nsec: the nanoseconds value to be converted
+ *
+ * Returns the compat_timeval representation of the nsec parameter.
+ */
+static inline struct compat_timeval ns_to_compat_timeval(s64 nsec)
+{
+ struct timeval tv;
+ struct compat_timeval ctv;
+
+ tv = ns_to_timeval(nsec);
+ ctv.tv_sec = tv.tv_sec;
+ ctv.tv_usec = tv.tv_usec;
+
+ return ctv;
+}
+
+#else /* !CONFIG_COMPAT */
#define is_compat_task() (0)
static inline bool in_compat_syscall(void) { return false; }
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 7e05c5e..87165f0 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -31,7 +31,7 @@
#define CPUFREQ_ETERNAL (-1)
#define CPUFREQ_NAME_LEN 16
-/* Print length for names. Extra 1 space for accomodating '\n' in prints */
+/* Print length for names. Extra 1 space for accommodating '\n' in prints */
#define CPUFREQ_NAME_PLEN (CPUFREQ_NAME_LEN + 1)
struct cpufreq_governor;
@@ -115,7 +115,7 @@
* guarantee that frequency can be changed on any CPU sharing the
* policy and that the change will affect all of the policy CPUs then.
* - fast_switch_enabled is to be set by governors that support fast
- * freqnency switching with the help of cpufreq_enable_fast_switch().
+ * frequency switching with the help of cpufreq_enable_fast_switch().
*/
bool fast_switch_possible;
bool fast_switch_enabled;
@@ -415,9 +415,6 @@
/* Policy Notifiers */
#define CPUFREQ_ADJUST (0)
#define CPUFREQ_NOTIFY (1)
-#define CPUFREQ_START (2)
-#define CPUFREQ_CREATE_POLICY (3)
-#define CPUFREQ_REMOVE_POLICY (4)
#ifdef CONFIG_CPU_FREQ
int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list);
diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
index b3d2c1a..96f1e88 100644
--- a/include/linux/cpumask.h
+++ b/include/linux/cpumask.h
@@ -649,11 +649,15 @@
* used. Please use this_cpu_cpumask_var_t in those cases. The direct use
* of this_cpu_ptr() or this_cpu_read() will lead to failures when the
* other type of cpumask_var_t implementation is configured.
+ *
+ * Please also note that __cpumask_var_read_mostly can be used to declare
+ * a cpumask_var_t variable itself (not its content) as read mostly.
*/
#ifdef CONFIG_CPUMASK_OFFSTACK
typedef struct cpumask *cpumask_var_t;
-#define this_cpu_cpumask_var_ptr(x) this_cpu_read(x)
+#define this_cpu_cpumask_var_ptr(x) this_cpu_read(x)
+#define __cpumask_var_read_mostly __read_mostly
bool alloc_cpumask_var_node(cpumask_var_t *mask, gfp_t flags, int node);
bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags);
@@ -667,6 +671,7 @@
typedef struct cpumask cpumask_var_t[1];
#define this_cpu_cpumask_var_ptr(x) this_cpu_ptr(x)
+#define __cpumask_var_read_mostly
static inline bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags)
{
diff --git a/include/linux/cputime.h b/include/linux/cputime.h
index f2eb2ee..a691dc4 100644
--- a/include/linux/cputime.h
+++ b/include/linux/cputime.h
@@ -1,6 +1,7 @@
#ifndef __LINUX_CPUTIME_H
#define __LINUX_CPUTIME_H
+#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
#include <asm/cputime.h>
#ifndef cputime_to_nsecs
@@ -8,9 +9,5 @@
(cputime_to_usecs(__ct) * NSEC_PER_USEC)
#endif
-#ifndef nsecs_to_cputime
-# define nsecs_to_cputime(__nsecs) \
- usecs_to_cputime((__nsecs) / NSEC_PER_USEC)
-#endif
-
+#endif /* CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
#endif /* __LINUX_CPUTIME_H */
diff --git a/include/linux/delay.h b/include/linux/delay.h
index a6ecb34..2ecb3c4 100644
--- a/include/linux/delay.h
+++ b/include/linux/delay.h
@@ -5,6 +5,17 @@
* Copyright (C) 1993 Linus Torvalds
*
* Delay routines, using a pre-computed "loops_per_jiffy" value.
+ *
+ * Please note that ndelay(), udelay() and mdelay() may return early for
+ * several reasons:
+ * 1. computed loops_per_jiffy too low (due to the time taken to
+ * execute the timer interrupt.)
+ * 2. cache behaviour affecting the time it takes to execute the
+ * loop function.
+ * 3. CPU clock rate changes.
+ *
+ * Please see this thread:
+ * http://lists.openwall.net/linux-kernel/2011/01/09/56
*/
#include <linux/kernel.h>
diff --git a/include/linux/delayacct.h b/include/linux/delayacct.h
index 6cee17c..00e60f7 100644
--- a/include/linux/delayacct.h
+++ b/include/linux/delayacct.h
@@ -17,6 +17,7 @@
#ifndef _LINUX_DELAYACCT_H
#define _LINUX_DELAYACCT_H
+#include <uapi/linux/taskstats.h>
#include <linux/sched.h>
#include <linux/slab.h>
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
index 2de4e2e..e0acb0e 100644
--- a/include/linux/devfreq.h
+++ b/include/linux/devfreq.h
@@ -104,6 +104,8 @@
* struct devfreq_governor - Devfreq policy governor
* @node: list node - contains registered devfreq governors
* @name: Governor's name
+ * @immutable: Immutable flag for governor. If the value is 1,
+ * this govenror is never changeable to other governor.
* @get_target_freq: Returns desired operating frequency for the device.
* Basically, get_target_freq will run
* devfreq_dev_profile.get_dev_status() to get the
@@ -121,6 +123,7 @@
struct list_head node;
const char name[DEVFREQ_NAME_LEN];
+ const unsigned int immutable;
int (*get_target_freq)(struct devfreq *this, unsigned long *freq);
int (*event_handler)(struct devfreq *devfreq,
unsigned int event, void *data);
diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
index 7f7e9a7..5725c94 100644
--- a/include/linux/dma-iommu.h
+++ b/include/linux/dma-iommu.h
@@ -27,6 +27,7 @@
/* Domain management interface for IOMMU drivers */
int iommu_get_dma_cookie(struct iommu_domain *domain);
+int iommu_get_msi_cookie(struct iommu_domain *domain, dma_addr_t base);
void iommu_put_dma_cookie(struct iommu_domain *domain);
/* Setup call for arch DMA mapping code */
@@ -34,7 +35,8 @@
u64 size, struct device *dev);
/* General helpers for DMA-API <-> IOMMU-API interaction */
-int dma_direction_to_prot(enum dma_data_direction dir, bool coherent);
+int dma_info_to_prot(enum dma_data_direction dir, bool coherent,
+ unsigned long attrs);
/*
* These implement the bulk of the relevant DMA mapping callbacks, but require
@@ -65,7 +67,6 @@
size_t size, enum dma_data_direction dir, unsigned long attrs);
void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle,
size_t size, enum dma_data_direction dir, unsigned long attrs);
-int iommu_dma_supported(struct device *dev, u64 mask);
int iommu_dma_mapping_error(struct device *dev, dma_addr_t dma_addr);
/* The DMA API isn't _quite_ the whole story, though... */
@@ -86,6 +87,11 @@
return -ENODEV;
}
+static inline int iommu_get_msi_cookie(struct iommu_domain *domain, dma_addr_t base)
+{
+ return -ENODEV;
+}
+
static inline void iommu_put_dma_cookie(struct iommu_domain *domain)
{
}
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index 10c5a17..c24721a 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -63,6 +63,13 @@
#define DMA_ATTR_NO_WARN (1UL << 8)
/*
+ * DMA_ATTR_PRIVILEGED: used to indicate that the buffer is fully
+ * accessible at an elevated privilege level (and ideally inaccessible or
+ * at least read-only at lesser-privileged levels).
+ */
+#define DMA_ATTR_PRIVILEGED (1UL << 9)
+
+/*
* A dma_addr_t can hold any valid DMA or bus address for the platform.
* It can be given to a device to use as a DMA source or target. A CPU cannot
* reference a dma_addr_t directly because there may be translation between
diff --git a/include/linux/edac.h b/include/linux/edac.h
index 07c52c0..5b6adf9 100644
--- a/include/linux/edac.h
+++ b/include/linux/edac.h
@@ -190,8 +190,8 @@
* part of the memory details to the memory controller.
* @MEM_RMBS: Rambus DRAM, used on a few Pentium III/IV controllers.
* @MEM_DDR2: DDR2 RAM, as described at JEDEC JESD79-2F.
- * Those memories are labed as "PC2-" instead of "PC" to
- * differenciate from DDR.
+ * Those memories are labeled as "PC2-" instead of "PC" to
+ * differentiate from DDR.
* @MEM_FB_DDR2: Fully-Buffered DDR2, as described at JEDEC Std No. 205
* and JESD206.
* Those memories are accessed per DIMM slot, and not by
diff --git a/include/linux/efi-bgrt.h b/include/linux/efi-bgrt.h
index 051b21f..2fd3993 100644
--- a/include/linux/efi-bgrt.h
+++ b/include/linux/efi-bgrt.h
@@ -1,20 +1,19 @@
#ifndef _LINUX_EFI_BGRT_H
#define _LINUX_EFI_BGRT_H
-#ifdef CONFIG_ACPI_BGRT
-
#include <linux/acpi.h>
-void efi_bgrt_init(void);
+#ifdef CONFIG_ACPI_BGRT
+
+void efi_bgrt_init(struct acpi_table_header *table);
/* The BGRT data itself; only valid if bgrt_image != NULL. */
-extern void *bgrt_image;
extern size_t bgrt_image_size;
-extern struct acpi_table_bgrt *bgrt_tab;
+extern struct acpi_table_bgrt bgrt_tab;
#else /* !CONFIG_ACPI_BGRT */
-static inline void efi_bgrt_init(void) {}
+static inline void efi_bgrt_init(struct acpi_table_header *table) {}
#endif /* !CONFIG_ACPI_BGRT */
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 5b1af30..94d34e0 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -509,24 +509,6 @@
u64 query_variable_info;
} efi_runtime_services_64_t;
-typedef struct {
- efi_table_hdr_t hdr;
- void *get_time;
- void *set_time;
- void *get_wakeup_time;
- void *set_wakeup_time;
- void *set_virtual_address_map;
- void *convert_pointer;
- void *get_variable;
- void *get_next_variable;
- void *set_variable;
- void *get_next_high_mono_count;
- void *reset_system;
- void *update_capsule;
- void *query_capsule_caps;
- void *query_variable_info;
-} efi_runtime_services_t;
-
typedef efi_status_t efi_get_time_t (efi_time_t *tm, efi_time_cap_t *tc);
typedef efi_status_t efi_set_time_t (efi_time_t *tm);
typedef efi_status_t efi_get_wakeup_time_t (efi_bool_t *enabled, efi_bool_t *pending,
@@ -561,6 +543,24 @@
unsigned long size,
bool nonblocking);
+typedef struct {
+ efi_table_hdr_t hdr;
+ efi_get_time_t *get_time;
+ efi_set_time_t *set_time;
+ efi_get_wakeup_time_t *get_wakeup_time;
+ efi_set_wakeup_time_t *set_wakeup_time;
+ efi_set_virtual_address_map_t *set_virtual_address_map;
+ void *convert_pointer;
+ efi_get_variable_t *get_variable;
+ efi_get_next_variable_t *get_next_variable;
+ efi_set_variable_t *set_variable;
+ efi_get_next_high_mono_count_t *get_next_high_mono_count;
+ efi_reset_system_t *reset_system;
+ efi_update_capsule_t *update_capsule;
+ efi_query_capsule_caps_t *query_capsule_caps;
+ efi_query_variable_info_t *query_variable_info;
+} efi_runtime_services_t;
+
void efi_native_runtime_setup(void);
/*
@@ -611,6 +611,9 @@
#define EFI_CONSOLE_OUT_DEVICE_GUID EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d)
#define APPLE_PROPERTIES_PROTOCOL_GUID EFI_GUID(0x91bd12fe, 0xf6c3, 0x44fb, 0xa5, 0xb7, 0x51, 0x22, 0xab, 0x30, 0x3a, 0xe0)
+#define EFI_IMAGE_SECURITY_DATABASE_GUID EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f)
+#define EFI_SHIM_LOCK_GUID EFI_GUID(0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23)
+
/*
* This GUID is used to pass to the kernel proper the struct screen_info
* structure that was populated by the stub based on the GOP protocol instance
@@ -1065,6 +1068,7 @@
#define EFI_ARCH_1 7 /* First arch-specific bit */
#define EFI_DBG 8 /* Print additional debug info at runtime */
#define EFI_NX_PE_DATA 9 /* Can runtime data regions be mapped non-executable? */
+#define EFI_MEM_ATTR 10 /* Did firmware publish an EFI_MEMORY_ATTRIBUTES table? */
#ifdef CONFIG_EFI
/*
@@ -1240,17 +1244,17 @@
bool deleting;
};
-struct efi_simple_text_output_protocol_32 {
+typedef struct {
u32 reset;
u32 output_string;
u32 test_string;
-};
+} efi_simple_text_output_protocol_32_t;
-struct efi_simple_text_output_protocol_64 {
+typedef struct {
u64 reset;
u64 output_string;
u64 test_string;
-};
+} efi_simple_text_output_protocol_64_t;
struct efi_simple_text_output_protocol {
void *reset;
@@ -1476,6 +1480,14 @@
bool efi_runtime_disabled(void);
extern void efi_call_virt_check_flags(unsigned long flags, const char *call);
+enum efi_secureboot_mode {
+ efi_secureboot_mode_unset,
+ efi_secureboot_mode_unknown,
+ efi_secureboot_mode_disabled,
+ efi_secureboot_mode_enabled,
+};
+enum efi_secureboot_mode efi_get_secureboot(efi_system_table_t *sys_table);
+
/*
* Arch code can implement the following three template macros, avoiding
* reptition for the void/non-void return cases of {__,}efi_call_virt():
diff --git a/include/linux/fsl_ifc.h b/include/linux/fsl_ifc.h
index 3f9778c..c332f0a 100644
--- a/include/linux/fsl_ifc.h
+++ b/include/linux/fsl_ifc.h
@@ -733,8 +733,12 @@
__be32 nand_erattr1;
u32 res19[0x10];
__be32 nand_fsr;
- u32 res20[0x3];
- __be32 nand_eccstat[6];
+ u32 res20;
+ /* The V1 nand_eccstat is actually 4 words that overlaps the
+ * V2 nand_eccstat.
+ */
+ __be32 v1_nand_eccstat[2];
+ __be32 v2_nand_eccstat[6];
u32 res21[0x1c];
__be32 nanndcr;
u32 res22[0x2];
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
index cdab81b..e52b427 100644
--- a/include/linux/hrtimer.h
+++ b/include/linux/hrtimer.h
@@ -88,12 +88,6 @@
* @base: pointer to the timer base (per cpu and per clock)
* @state: state information (See bit values above)
* @is_rel: Set if the timer was armed relative
- * @start_pid: timer statistics field to store the pid of the task which
- * started the timer
- * @start_site: timer statistics field to store the site where the timer
- * was started
- * @start_comm: timer statistics field to store the name of the process which
- * started the timer
*
* The hrtimer structure must be initialized by hrtimer_init()
*/
@@ -104,11 +98,6 @@
struct hrtimer_clock_base *base;
u8 state;
u8 is_rel;
-#ifdef CONFIG_TIMER_STATS
- int start_pid;
- void *start_site;
- char start_comm[16];
-#endif
};
/**
diff --git a/include/linux/init_task.h b/include/linux/init_task.h
index 325f649..3a85d61 100644
--- a/include/linux/init_task.h
+++ b/include/linux/init_task.h
@@ -42,6 +42,27 @@
#define INIT_PREV_CPUTIME(x)
#endif
+#ifdef CONFIG_POSIX_TIMERS
+#define INIT_POSIX_TIMERS(s) \
+ .posix_timers = LIST_HEAD_INIT(s.posix_timers),
+#define INIT_CPU_TIMERS(s) \
+ .cpu_timers = { \
+ LIST_HEAD_INIT(s.cpu_timers[0]), \
+ LIST_HEAD_INIT(s.cpu_timers[1]), \
+ LIST_HEAD_INIT(s.cpu_timers[2]), \
+ },
+#define INIT_CPUTIMER(s) \
+ .cputimer = { \
+ .cputime_atomic = INIT_CPUTIME_ATOMIC, \
+ .running = false, \
+ .checking_timer = false, \
+ },
+#else
+#define INIT_POSIX_TIMERS(s)
+#define INIT_CPU_TIMERS(s)
+#define INIT_CPUTIMER(s)
+#endif
+
#define INIT_SIGNALS(sig) { \
.nr_threads = 1, \
.thread_head = LIST_HEAD_INIT(init_task.thread_node), \
@@ -49,14 +70,10 @@
.shared_pending = { \
.list = LIST_HEAD_INIT(sig.shared_pending.list), \
.signal = {{0}}}, \
- .posix_timers = LIST_HEAD_INIT(sig.posix_timers), \
- .cpu_timers = INIT_CPU_TIMERS(sig.cpu_timers), \
+ INIT_POSIX_TIMERS(sig) \
+ INIT_CPU_TIMERS(sig) \
.rlim = INIT_RLIMITS, \
- .cputimer = { \
- .cputime_atomic = INIT_CPUTIME_ATOMIC, \
- .running = false, \
- .checking_timer = false, \
- }, \
+ INIT_CPUTIMER(sig) \
INIT_PREV_CPUTIME(sig) \
.cred_guard_mutex = \
__MUTEX_INITIALIZER(sig.cred_guard_mutex), \
@@ -247,7 +264,7 @@
.blocked = {{0}}, \
.alloc_lock = __SPIN_LOCK_UNLOCKED(tsk.alloc_lock), \
.journal_info = NULL, \
- .cpu_timers = INIT_CPU_TIMERS(tsk.cpu_timers), \
+ INIT_CPU_TIMERS(tsk) \
.pi_lock = __RAW_SPIN_LOCK_UNLOCKED(tsk.pi_lock), \
.timer_slack_ns = 50000, /* 50 usec default slack */ \
.pids = { \
@@ -274,13 +291,6 @@
}
-#define INIT_CPU_TIMERS(cpu_timers) \
-{ \
- LIST_HEAD_INIT(cpu_timers[0]), \
- LIST_HEAD_INIT(cpu_timers[1]), \
- LIST_HEAD_INIT(cpu_timers[2]), \
-}
-
/* Attach to the init_task data structure for proper alignment */
#define __init_task_data __attribute__((__section__(".data..init_task")))
diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h
index d49e26c..c573a52 100644
--- a/include/linux/intel-iommu.h
+++ b/include/linux/intel-iommu.h
@@ -29,6 +29,7 @@
#include <linux/dma_remapping.h>
#include <linux/mmu_notifier.h>
#include <linux/list.h>
+#include <linux/iommu.h>
#include <asm/cacheflush.h>
#include <asm/iommu.h>
@@ -153,8 +154,8 @@
#define DMA_TLB_GLOBAL_FLUSH (((u64)1) << 60)
#define DMA_TLB_DSI_FLUSH (((u64)2) << 60)
#define DMA_TLB_PSI_FLUSH (((u64)3) << 60)
-#define DMA_TLB_IIRG(type) ((type >> 60) & 7)
-#define DMA_TLB_IAIG(val) (((val) >> 57) & 7)
+#define DMA_TLB_IIRG(type) ((type >> 60) & 3)
+#define DMA_TLB_IAIG(val) (((val) >> 57) & 3)
#define DMA_TLB_READ_DRAIN (((u64)1) << 49)
#define DMA_TLB_WRITE_DRAIN (((u64)1) << 48)
#define DMA_TLB_DID(id) (((u64)((id) & 0xffff)) << 32)
@@ -164,9 +165,9 @@
/* INVALID_DESC */
#define DMA_CCMD_INVL_GRANU_OFFSET 61
-#define DMA_ID_TLB_GLOBAL_FLUSH (((u64)1) << 3)
-#define DMA_ID_TLB_DSI_FLUSH (((u64)2) << 3)
-#define DMA_ID_TLB_PSI_FLUSH (((u64)3) << 3)
+#define DMA_ID_TLB_GLOBAL_FLUSH (((u64)1) << 4)
+#define DMA_ID_TLB_DSI_FLUSH (((u64)2) << 4)
+#define DMA_ID_TLB_PSI_FLUSH (((u64)3) << 4)
#define DMA_ID_TLB_READ_DRAIN (((u64)1) << 7)
#define DMA_ID_TLB_WRITE_DRAIN (((u64)1) << 6)
#define DMA_ID_TLB_DID(id) (((u64)((id & 0xffff) << 16)))
@@ -316,8 +317,8 @@
#define QI_DEV_EIOTLB_SIZE (((u64)1) << 11)
#define QI_DEV_EIOTLB_GLOB(g) ((u64)g)
#define QI_DEV_EIOTLB_PASID(p) (((u64)p) << 32)
-#define QI_DEV_EIOTLB_SID(sid) ((u64)((sid) & 0xffff) << 32)
-#define QI_DEV_EIOTLB_QDEP(qd) (((qd) & 0x1f) << 16)
+#define QI_DEV_EIOTLB_SID(sid) ((u64)((sid) & 0xffff) << 16)
+#define QI_DEV_EIOTLB_QDEP(qd) ((u64)((qd) & 0x1f) << 4)
#define QI_DEV_EIOTLB_MAX_INVS 32
#define QI_PGRP_IDX(idx) (((u64)(idx)) << 55)
@@ -439,7 +440,7 @@
struct irq_domain *ir_domain;
struct irq_domain *ir_msi_domain;
#endif
- struct device *iommu_dev; /* IOMMU-sysfs device */
+ struct iommu_device iommu; /* IOMMU core code handle */
int node;
u32 flags; /* Software defined flags */
};
diff --git a/include/linux/intel_pmic_gpio.h b/include/linux/intel_pmic_gpio.h
deleted file mode 100644
index 920109a..0000000
--- a/include/linux/intel_pmic_gpio.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef LINUX_INTEL_PMIC_H
-#define LINUX_INTEL_PMIC_H
-
-struct intel_pmic_gpio_platform_data {
- /* the first IRQ of the chip */
- unsigned irq_base;
- /* number assigned to the first GPIO */
- unsigned gpio_base;
- /* sram address for gpiointr register, the langwell chip will map
- * the PMIC spi GPIO expander's GPIOINTR register in sram.
- */
- unsigned gpiointr;
-};
-
-#endif
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 0ff5111..6a6de18 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -31,6 +31,13 @@
#define IOMMU_CACHE (1 << 2) /* DMA cache coherency */
#define IOMMU_NOEXEC (1 << 3)
#define IOMMU_MMIO (1 << 4) /* e.g. things like MSI doorbells */
+/*
+ * This is to make the IOMMU API setup privileged
+ * mapppings accessible by the master only at higher
+ * privileged execution level and inaccessible at
+ * less privileged levels.
+ */
+#define IOMMU_PRIV (1 << 5)
struct iommu_ops;
struct iommu_group;
@@ -117,18 +124,25 @@
DOMAIN_ATTR_MAX,
};
+/* These are the possible reserved region types */
+#define IOMMU_RESV_DIRECT (1 << 0)
+#define IOMMU_RESV_RESERVED (1 << 1)
+#define IOMMU_RESV_MSI (1 << 2)
+
/**
- * struct iommu_dm_region - descriptor for a direct mapped memory region
+ * struct iommu_resv_region - descriptor for a reserved memory region
* @list: Linked list pointers
* @start: System physical start address of the region
* @length: Length of the region in bytes
* @prot: IOMMU Protection flags (READ/WRITE/...)
+ * @type: Type of the reserved region
*/
-struct iommu_dm_region {
+struct iommu_resv_region {
struct list_head list;
phys_addr_t start;
size_t length;
int prot;
+ int type;
};
#ifdef CONFIG_IOMMU_API
@@ -150,9 +164,9 @@
* @device_group: find iommu group for a particular device
* @domain_get_attr: Query domain attributes
* @domain_set_attr: Change domain attributes
- * @get_dm_regions: Request list of direct mapping requirements for a device
- * @put_dm_regions: Free list of direct mapping requirements for a device
- * @apply_dm_region: Temporary helper call-back for iova reserved ranges
+ * @get_resv_regions: Request list of reserved regions for a device
+ * @put_resv_regions: Free list of reserved regions for a device
+ * @apply_resv_region: Temporary helper call-back for iova reserved ranges
* @domain_window_enable: Configure and enable a particular window for a domain
* @domain_window_disable: Disable a particular window for a domain
* @domain_set_windows: Set the number of windows for a domain
@@ -184,11 +198,12 @@
int (*domain_set_attr)(struct iommu_domain *domain,
enum iommu_attr attr, void *data);
- /* Request/Free a list of direct mapping requirements for a device */
- void (*get_dm_regions)(struct device *dev, struct list_head *list);
- void (*put_dm_regions)(struct device *dev, struct list_head *list);
- void (*apply_dm_region)(struct device *dev, struct iommu_domain *domain,
- struct iommu_dm_region *region);
+ /* Request/Free a list of reserved regions for a device */
+ void (*get_resv_regions)(struct device *dev, struct list_head *list);
+ void (*put_resv_regions)(struct device *dev, struct list_head *list);
+ void (*apply_resv_region)(struct device *dev,
+ struct iommu_domain *domain,
+ struct iommu_resv_region *region);
/* Window handling functions */
int (*domain_window_enable)(struct iommu_domain *domain, u32 wnd_nr,
@@ -204,6 +219,42 @@
unsigned long pgsize_bitmap;
};
+/**
+ * struct iommu_device - IOMMU core representation of one IOMMU hardware
+ * instance
+ * @list: Used by the iommu-core to keep a list of registered iommus
+ * @ops: iommu-ops for talking to this iommu
+ * @dev: struct device for sysfs handling
+ */
+struct iommu_device {
+ struct list_head list;
+ const struct iommu_ops *ops;
+ struct fwnode_handle *fwnode;
+ struct device dev;
+};
+
+int iommu_device_register(struct iommu_device *iommu);
+void iommu_device_unregister(struct iommu_device *iommu);
+int iommu_device_sysfs_add(struct iommu_device *iommu,
+ struct device *parent,
+ const struct attribute_group **groups,
+ const char *fmt, ...) __printf(4, 5);
+void iommu_device_sysfs_remove(struct iommu_device *iommu);
+int iommu_device_link(struct iommu_device *iommu, struct device *link);
+void iommu_device_unlink(struct iommu_device *iommu, struct device *link);
+
+static inline void iommu_device_set_ops(struct iommu_device *iommu,
+ const struct iommu_ops *ops)
+{
+ iommu->ops = ops;
+}
+
+static inline void iommu_device_set_fwnode(struct iommu_device *iommu,
+ struct fwnode_handle *fwnode)
+{
+ iommu->fwnode = fwnode;
+}
+
#define IOMMU_GROUP_NOTIFY_ADD_DEVICE 1 /* Device added */
#define IOMMU_GROUP_NOTIFY_DEL_DEVICE 2 /* Pre Device removed */
#define IOMMU_GROUP_NOTIFY_BIND_DRIVER 3 /* Pre Driver bind */
@@ -233,9 +284,13 @@
extern void iommu_set_fault_handler(struct iommu_domain *domain,
iommu_fault_handler_t handler, void *token);
-extern void iommu_get_dm_regions(struct device *dev, struct list_head *list);
-extern void iommu_put_dm_regions(struct device *dev, struct list_head *list);
+extern void iommu_get_resv_regions(struct device *dev, struct list_head *list);
+extern void iommu_put_resv_regions(struct device *dev, struct list_head *list);
extern int iommu_request_dm_for_dev(struct device *dev);
+extern struct iommu_resv_region *
+iommu_alloc_resv_region(phys_addr_t start, size_t length, int prot, int type);
+extern int iommu_get_group_resv_regions(struct iommu_group *group,
+ struct list_head *head);
extern int iommu_attach_group(struct iommu_domain *domain,
struct iommu_group *group);
@@ -267,12 +322,6 @@
void *data);
extern int iommu_domain_set_attr(struct iommu_domain *domain, enum iommu_attr,
void *data);
-struct device *iommu_device_create(struct device *parent, void *drvdata,
- const struct attribute_group **groups,
- const char *fmt, ...) __printf(4, 5);
-void iommu_device_destroy(struct device *dev);
-int iommu_device_link(struct device *dev, struct device *link);
-void iommu_device_unlink(struct device *dev, struct device *link);
/* Window handling function prototypes */
extern int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr,
@@ -352,15 +401,14 @@
const struct iommu_ops *ops);
void iommu_fwspec_free(struct device *dev);
int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
-void iommu_register_instance(struct fwnode_handle *fwnode,
- const struct iommu_ops *ops);
-const struct iommu_ops *iommu_get_instance(struct fwnode_handle *fwnode);
+const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode);
#else /* CONFIG_IOMMU_API */
struct iommu_ops {};
struct iommu_group {};
struct iommu_fwspec {};
+struct iommu_device {};
static inline bool iommu_present(struct bus_type *bus)
{
@@ -443,16 +491,22 @@
{
}
-static inline void iommu_get_dm_regions(struct device *dev,
+static inline void iommu_get_resv_regions(struct device *dev,
struct list_head *list)
{
}
-static inline void iommu_put_dm_regions(struct device *dev,
+static inline void iommu_put_resv_regions(struct device *dev,
struct list_head *list)
{
}
+static inline int iommu_get_group_resv_regions(struct iommu_group *group,
+ struct list_head *head)
+{
+ return -ENODEV;
+}
+
static inline int iommu_request_dm_for_dev(struct device *dev)
{
return -ENODEV;
@@ -546,15 +600,34 @@
return -EINVAL;
}
-static inline struct device *iommu_device_create(struct device *parent,
- void *drvdata,
- const struct attribute_group **groups,
- const char *fmt, ...)
+static inline int iommu_device_register(struct iommu_device *iommu)
{
- return ERR_PTR(-ENODEV);
+ return -ENODEV;
}
-static inline void iommu_device_destroy(struct device *dev)
+static inline void iommu_device_set_ops(struct iommu_device *iommu,
+ const struct iommu_ops *ops)
+{
+}
+
+static inline void iommu_device_set_fwnode(struct iommu_device *iommu,
+ struct fwnode_handle *fwnode)
+{
+}
+
+static inline void iommu_device_unregister(struct iommu_device *iommu)
+{
+}
+
+static inline int iommu_device_sysfs_add(struct iommu_device *iommu,
+ struct device *parent,
+ const struct attribute_group **groups,
+ const char *fmt, ...)
+{
+ return -ENODEV;
+}
+
+static inline void iommu_device_sysfs_remove(struct iommu_device *iommu)
{
}
@@ -584,13 +657,8 @@
return -ENODEV;
}
-static inline void iommu_register_instance(struct fwnode_handle *fwnode,
- const struct iommu_ops *ops)
-{
-}
-
static inline
-const struct iommu_ops *iommu_get_instance(struct fwnode_handle *fwnode)
+const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode)
{
return NULL;
}
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 39e3254..f887351 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -732,6 +732,10 @@
int __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
struct module *owner, const struct cpumask *affinity);
+int __devm_irq_alloc_descs(struct device *dev, int irq, unsigned int from,
+ unsigned int cnt, int node, struct module *owner,
+ const struct cpumask *affinity);
+
/* use macros to avoid needing export.h for THIS_MODULE */
#define irq_alloc_descs(irq, from, cnt, node) \
__irq_alloc_descs(irq, from, cnt, node, THIS_MODULE, NULL)
@@ -748,6 +752,21 @@
#define irq_alloc_descs_from(from, cnt, node) \
irq_alloc_descs(-1, from, cnt, node)
+#define devm_irq_alloc_descs(dev, irq, from, cnt, node) \
+ __devm_irq_alloc_descs(dev, irq, from, cnt, node, THIS_MODULE, NULL)
+
+#define devm_irq_alloc_desc(dev, node) \
+ devm_irq_alloc_descs(dev, -1, 0, 1, node)
+
+#define devm_irq_alloc_desc_at(dev, at, node) \
+ devm_irq_alloc_descs(dev, at, at, 1, node)
+
+#define devm_irq_alloc_desc_from(dev, from, node) \
+ devm_irq_alloc_descs(dev, -1, from, 1, node)
+
+#define devm_irq_alloc_descs_from(dev, from, cnt, node) \
+ devm_irq_alloc_descs(dev, -1, from, cnt, node)
+
void irq_free_descs(unsigned int irq, unsigned int cnt);
static inline void irq_free_desc(unsigned int irq)
{
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index e808f8a..725e86b 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -73,7 +73,6 @@
#define GICD_TYPER_ID_BITS(typer) ((((typer) >> 19) & 0x1f) + 1)
#define GICD_TYPER_IRQS(typer) ((((typer) & 0x1f) + 1) * 32)
-#define GICD_TYPER_LPIS (1U << 17)
#define GICD_IROUTER_SPI_MODE_ONE (0U << 31)
#define GICD_IROUTER_SPI_MODE_ANY (1U << 31)
@@ -306,7 +305,7 @@
#define GITS_BASER_TYPE_NONE 0
#define GITS_BASER_TYPE_DEVICE 1
#define GITS_BASER_TYPE_VCPU 2
-#define GITS_BASER_TYPE_CPU 3
+#define GITS_BASER_TYPE_RESERVED3 3
#define GITS_BASER_TYPE_COLLECTION 4
#define GITS_BASER_TYPE_RESERVED5 5
#define GITS_BASER_TYPE_RESERVED6 6
@@ -320,8 +319,6 @@
#define GITS_CMD_MAPD 0x08
#define GITS_CMD_MAPC 0x09
#define GITS_CMD_MAPTI 0x0a
-/* older GIC documentation used MAPVI for this command */
-#define GITS_CMD_MAPVI GITS_CMD_MAPTI
#define GITS_CMD_MAPI 0x0b
#define GITS_CMD_MOVI 0x01
#define GITS_CMD_DISCARD 0x0f
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index ffb8460..188eced 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -183,6 +183,12 @@
/* Irq domain is an IPI domain with single virq */
IRQ_DOMAIN_FLAG_IPI_SINGLE = (1 << 3),
+ /* Irq domain implements MSIs */
+ IRQ_DOMAIN_FLAG_MSI = (1 << 4),
+
+ /* Irq domain implements MSI remapping */
+ IRQ_DOMAIN_FLAG_MSI_REMAP = (1 << 5),
+
/*
* Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved
* for implementation specific purposes and ignored by the
@@ -216,6 +222,7 @@
void *host_data);
extern struct irq_domain *irq_find_matching_fwspec(struct irq_fwspec *fwspec,
enum irq_domain_bus_token bus_token);
+extern bool irq_domain_check_msi_remap(void);
extern void irq_set_default_host(struct irq_domain *host);
extern int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
irq_hw_number_t hwirq, int node,
@@ -446,6 +453,19 @@
{
return domain->flags & IRQ_DOMAIN_FLAG_IPI_SINGLE;
}
+
+static inline bool irq_domain_is_msi(struct irq_domain *domain)
+{
+ return domain->flags & IRQ_DOMAIN_FLAG_MSI;
+}
+
+static inline bool irq_domain_is_msi_remap(struct irq_domain *domain)
+{
+ return domain->flags & IRQ_DOMAIN_FLAG_MSI_REMAP;
+}
+
+extern bool irq_domain_hierarchical_is_msi_remap(struct irq_domain *domain);
+
#else /* CONFIG_IRQ_DOMAIN_HIERARCHY */
static inline void irq_domain_activate_irq(struct irq_data *data) { }
static inline void irq_domain_deactivate_irq(struct irq_data *data) { }
@@ -477,6 +497,22 @@
{
return false;
}
+
+static inline bool irq_domain_is_msi(struct irq_domain *domain)
+{
+ return false;
+}
+
+static inline bool irq_domain_is_msi_remap(struct irq_domain *domain)
+{
+ return false;
+}
+
+static inline bool
+irq_domain_hierarchical_is_msi_remap(struct irq_domain *domain)
+{
+ return false;
+}
#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */
#else /* CONFIG_IRQ_DOMAIN */
diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h
index 589d14e..624215c 100644
--- a/include/linux/jiffies.h
+++ b/include/linux/jiffies.h
@@ -293,6 +293,8 @@
return (u64)jiffies_to_usecs(j) * NSEC_PER_USEC;
}
+extern u64 jiffies64_to_nsecs(u64 j);
+
extern unsigned long __msecs_to_jiffies(const unsigned int m);
#if HZ <= MSEC_PER_SEC && !(MSEC_PER_SEC % HZ)
/*
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h
index a0547c5..b63d6b7 100644
--- a/include/linux/jump_label.h
+++ b/include/linux/jump_label.h
@@ -402,6 +402,6 @@
#define static_branch_enable(x) static_key_enable(&(x)->key)
#define static_branch_disable(x) static_key_disable(&(x)->key)
-#endif /* _LINUX_JUMP_LABEL_H */
-
#endif /* __ASSEMBLY__ */
+
+#endif /* _LINUX_JUMP_LABEL_H */
diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h
index 00f7768..66be8b6 100644
--- a/include/linux/kernel_stat.h
+++ b/include/linux/kernel_stat.h
@@ -9,7 +9,6 @@
#include <linux/sched.h>
#include <linux/vtime.h>
#include <asm/irq.h>
-#include <linux/cputime.h>
/*
* 'kernel_stat.h' contains the definitions needed for doing
@@ -78,15 +77,18 @@
return kstat_cpu(cpu).irqs_sum;
}
-extern void account_user_time(struct task_struct *, cputime_t);
-extern void account_system_time(struct task_struct *, int, cputime_t);
-extern void account_steal_time(cputime_t);
-extern void account_idle_time(cputime_t);
+extern void account_user_time(struct task_struct *, u64);
+extern void account_guest_time(struct task_struct *, u64);
+extern void account_system_time(struct task_struct *, int, u64);
+extern void account_system_index_time(struct task_struct *, u64,
+ enum cpu_usage_stat);
+extern void account_steal_time(u64);
+extern void account_idle_time(u64);
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
static inline void account_process_tick(struct task_struct *tsk, int user)
{
- vtime_account_user(tsk);
+ vtime_flush(tsk);
}
#else
extern void account_process_tick(struct task_struct *, int user);
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index 8f68490..16ddfb8 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -278,9 +278,13 @@
int nr_garbage;
};
+#ifdef __ARCH_WANT_KPROBES_INSN_SLOT
extern kprobe_opcode_t *__get_insn_slot(struct kprobe_insn_cache *c);
extern void __free_insn_slot(struct kprobe_insn_cache *c,
kprobe_opcode_t *slot, int dirty);
+/* sleep-less address checking routine */
+extern bool __is_insn_slot_addr(struct kprobe_insn_cache *c,
+ unsigned long addr);
#define DEFINE_INSN_CACHE_OPS(__name) \
extern struct kprobe_insn_cache kprobe_##__name##_slots; \
@@ -294,6 +298,18 @@
{ \
__free_insn_slot(&kprobe_##__name##_slots, slot, dirty); \
} \
+ \
+static inline bool is_kprobe_##__name##_slot(unsigned long addr) \
+{ \
+ return __is_insn_slot_addr(&kprobe_##__name##_slots, addr); \
+}
+#else /* __ARCH_WANT_KPROBES_INSN_SLOT */
+#define DEFINE_INSN_CACHE_OPS(__name) \
+static inline bool is_kprobe_##__name##_slot(unsigned long addr) \
+{ \
+ return 0; \
+}
+#endif
DEFINE_INSN_CACHE_OPS(insn);
@@ -330,7 +346,6 @@
int write, void __user *buffer,
size_t *length, loff_t *ppos);
#endif
-
#endif /* CONFIG_OPTPROBES */
#ifdef CONFIG_KPROBES_ON_FTRACE
extern void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
@@ -481,6 +496,19 @@
return enable_kprobe(&jp->kp);
}
+#ifndef CONFIG_KPROBES
+static inline bool is_kprobe_insn_slot(unsigned long addr)
+{
+ return false;
+}
+#endif
+#ifndef CONFIG_OPTPROBES
+static inline bool is_kprobe_optinsn_slot(unsigned long addr)
+{
+ return false;
+}
+#endif
+
#ifdef CONFIG_KPROBES
/*
* Blacklist ganerating macro. Specify functions which is not probed
diff --git a/include/linux/kref.h b/include/linux/kref.h
index e15828f..f4156f8 100644
--- a/include/linux/kref.h
+++ b/include/linux/kref.h
@@ -15,22 +15,27 @@
#ifndef _KREF_H_
#define _KREF_H_
-#include <linux/bug.h>
-#include <linux/atomic.h>
-#include <linux/kernel.h>
-#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/refcount.h>
struct kref {
- atomic_t refcount;
+ refcount_t refcount;
};
+#define KREF_INIT(n) { .refcount = REFCOUNT_INIT(n), }
+
/**
* kref_init - initialize object.
* @kref: object in question.
*/
static inline void kref_init(struct kref *kref)
{
- atomic_set(&kref->refcount, 1);
+ refcount_set(&kref->refcount, 1);
+}
+
+static inline unsigned int kref_read(const struct kref *kref)
+{
+ return refcount_read(&kref->refcount);
}
/**
@@ -39,41 +44,7 @@
*/
static inline void kref_get(struct kref *kref)
{
- /* If refcount was 0 before incrementing then we have a race
- * condition when this kref is freeing by some other thread right now.
- * In this case one should use kref_get_unless_zero()
- */
- WARN_ON_ONCE(atomic_inc_return(&kref->refcount) < 2);
-}
-
-/**
- * kref_sub - subtract a number of refcounts for object.
- * @kref: object.
- * @count: Number of recounts to subtract.
- * @release: pointer to the function that will clean up the object when the
- * last reference to the object is released.
- * This pointer is required, and it is not acceptable to pass kfree
- * in as this function. If the caller does pass kfree to this
- * function, you will be publicly mocked mercilessly by the kref
- * maintainer, and anyone else who happens to notice it. You have
- * been warned.
- *
- * Subtract @count from the refcount, and if 0, call release().
- * Return 1 if the object was removed, otherwise return 0. Beware, if this
- * function returns 0, you still can not count on the kref from remaining in
- * memory. Only use the return value if you want to see if the kref is now
- * gone, not present.
- */
-static inline int kref_sub(struct kref *kref, unsigned int count,
- void (*release)(struct kref *kref))
-{
- WARN_ON(release == NULL);
-
- if (atomic_sub_and_test((int) count, &kref->refcount)) {
- release(kref);
- return 1;
- }
- return 0;
+ refcount_inc(&kref->refcount);
}
/**
@@ -95,7 +66,13 @@
*/
static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref))
{
- return kref_sub(kref, 1, release);
+ WARN_ON(release == NULL);
+
+ if (refcount_dec_and_test(&kref->refcount)) {
+ release(kref);
+ return 1;
+ }
+ return 0;
}
static inline int kref_put_mutex(struct kref *kref,
@@ -103,12 +80,21 @@
struct mutex *lock)
{
WARN_ON(release == NULL);
- if (unlikely(!atomic_add_unless(&kref->refcount, -1, 1))) {
- mutex_lock(lock);
- if (unlikely(!atomic_dec_and_test(&kref->refcount))) {
- mutex_unlock(lock);
- return 0;
- }
+
+ if (refcount_dec_and_mutex_lock(&kref->refcount, lock)) {
+ release(kref);
+ return 1;
+ }
+ return 0;
+}
+
+static inline int kref_put_lock(struct kref *kref,
+ void (*release)(struct kref *kref),
+ spinlock_t *lock)
+{
+ WARN_ON(release == NULL);
+
+ if (refcount_dec_and_lock(&kref->refcount, lock)) {
release(kref);
return 1;
}
@@ -133,6 +119,6 @@
*/
static inline int __must_check kref_get_unless_zero(struct kref *kref)
{
- return atomic_add_unless(&kref->refcount, 1, 0);
+ return refcount_inc_not_zero(&kref->refcount);
}
#endif /* _KREF_H_ */
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 569cb53..38c0bd7 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -13,6 +13,7 @@
#define __LINUX_LEDS_H_INCLUDED
#include <linux/device.h>
+#include <linux/kernfs.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/rwsem.h>
@@ -27,6 +28,7 @@
enum led_brightness {
LED_OFF = 0,
+ LED_ON = 1,
LED_HALF = 127,
LED_FULL = 255,
};
@@ -46,6 +48,7 @@
#define LED_DEV_CAP_FLASH (1 << 18)
#define LED_HW_PLUGGABLE (1 << 19)
#define LED_PANIC_INDICATOR (1 << 20)
+#define LED_BRIGHT_HW_CHANGED (1 << 21)
/* set_brightness_work / blink_timer flags, atomic, private. */
unsigned long work_flags;
@@ -110,6 +113,11 @@
bool activated;
#endif
+#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
+ int brightness_hw_changed;
+ struct kernfs_node *brightness_hw_changed_kn;
+#endif
+
/* Ensures consistent access to the LED Flash Class device */
struct mutex led_access;
};
@@ -422,4 +430,12 @@
}
#endif
+#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
+extern void led_classdev_notify_brightness_hw_changed(
+ struct led_classdev *led_cdev, enum led_brightness brightness);
+#else
+static inline void led_classdev_notify_brightness_hw_changed(
+ struct led_classdev *led_cdev, enum led_brightness brightness) { }
+#endif
+
#endif /* __LINUX_LEDS_H_INCLUDED */
diff --git a/include/linux/llist.h b/include/linux/llist.h
index fd4ca0b..171baa9 100644
--- a/include/linux/llist.h
+++ b/include/linux/llist.h
@@ -3,28 +3,33 @@
/*
* 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.
+ * Cases where locking is not needed:
+ * If there are multiple producers and multiple consumers, llist_add can be
+ * used in producers and llist_del_all can be used in consumers simultaneously
+ * without locking. Also a single consumer can use llist_del_first while
+ * multiple producers simultaneously use llist_add, without any locking.
*
- * 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.
+ * Cases where locking is needed:
+ * If we have multiple consumers with llist_del_first used in one consumer, and
+ * llist_del_first or llist_del_all used in other consumers, then a lock is
+ * needed. This is because llist_del_first depends on list->first->next not
+ * changing, but without lock protection, there's no way to be sure about that
+ * if a preemption happens in the middle of the delete operation and on being
+ * preempted back, the list->first is the same as before causing the cmpxchg in
+ * llist_del_first to succeed. For example, while a llist_del_first operation
+ * is in progress in one consumer, then a llist_del_first, llist_add,
+ * llist_add (or llist_del_all, llist_add, llist_add) sequence in another
+ * consumer may cause violations.
*
- * This can be summarized as follow:
+ * This can be summarized as follows:
*
* | 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.
+ * Where, a particular row's operation can happen concurrently with a column's
+ * operation, with "-" being no lock needed, while "L" being 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
diff --git a/include/linux/math64.h b/include/linux/math64.h
index 6e8b5b2..80690c9 100644
--- a/include/linux/math64.h
+++ b/include/linux/math64.h
@@ -133,6 +133,16 @@
return ret;
}
+#ifndef mul_u32_u32
+/*
+ * Many a GCC version messes this up and generates a 64x64 mult :-(
+ */
+static inline u64 mul_u32_u32(u32 a, u32 b)
+{
+ return (u64)a * b;
+}
+#endif
+
#if defined(CONFIG_ARCH_SUPPORTS_INT128) && defined(__SIZEOF_INT128__)
#ifndef mul_u64_u32_shr
@@ -160,9 +170,9 @@
al = a;
ah = a >> 32;
- ret = ((u64)al * mul) >> shift;
+ ret = mul_u32_u32(al, mul) >> shift;
if (ah)
- ret += ((u64)ah * mul) << (32 - shift);
+ ret += mul_u32_u32(ah, mul) << (32 - shift);
return ret;
}
@@ -186,10 +196,10 @@
a0.ll = a;
b0.ll = b;
- rl.ll = (u64)a0.l.low * b0.l.low;
- rm.ll = (u64)a0.l.low * b0.l.high;
- rn.ll = (u64)a0.l.high * b0.l.low;
- rh.ll = (u64)a0.l.high * b0.l.high;
+ rl.ll = mul_u32_u32(a0.l.low, b0.l.low);
+ rm.ll = mul_u32_u32(a0.l.low, b0.l.high);
+ rn.ll = mul_u32_u32(a0.l.high, b0.l.low);
+ rh.ll = mul_u32_u32(a0.l.high, b0.l.high);
/*
* Each of these lines computes a 64-bit intermediate result into "c",
@@ -229,8 +239,8 @@
} u, rl, rh;
u.ll = a;
- rl.ll = (u64)u.l.low * mul;
- rh.ll = (u64)u.l.high * mul + rl.l.high;
+ rl.ll = mul_u32_u32(u.l.low, mul);
+ rh.ll = mul_u32_u32(u.l.high, mul) + rl.l.high;
/* Bits 32-63 of the result will be in rh.l.low. */
rl.l.high = do_div(rh.ll, divisor);
diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
index a4860bc..f848ee8 100644
--- a/include/linux/mfd/axp20x.h
+++ b/include/linux/mfd/axp20x.h
@@ -13,7 +13,7 @@
#include <linux/regmap.h>
-enum {
+enum axp20x_variants {
AXP152_ID = 0,
AXP202_ID,
AXP209_ID,
@@ -532,35 +532,6 @@
const struct regmap_irq_chip *regmap_irq_chip;
};
-#define BATTID_LEN 64
-#define OCV_CURVE_SIZE 32
-#define MAX_THERM_CURVE_SIZE 25
-#define PD_DEF_MIN_TEMP 0
-#define PD_DEF_MAX_TEMP 55
-
-struct axp20x_fg_pdata {
- char battid[BATTID_LEN + 1];
- int design_cap;
- int min_volt;
- int max_volt;
- int max_temp;
- int min_temp;
- int cap1;
- int cap0;
- int rdc1;
- int rdc0;
- int ocv_curve[OCV_CURVE_SIZE];
- int tcsz;
- int thermistor_curve[MAX_THERM_CURVE_SIZE][2];
-};
-
-struct axp20x_chrg_pdata {
- int max_cc;
- int max_cv;
- int def_cc;
- int def_cv;
-};
-
struct axp288_extcon_pdata {
/* GPIO pin control to switch D+/D- lines b/w PMIC and SOC */
struct gpio_desc *gpio_mux_cntl;
diff --git a/include/linux/mfd/lpc_ich.h b/include/linux/mfd/lpc_ich.h
index 2b300b4..fba8fcb 100644
--- a/include/linux/mfd/lpc_ich.h
+++ b/include/linux/mfd/lpc_ich.h
@@ -20,6 +20,8 @@
#ifndef LPC_ICH_H
#define LPC_ICH_H
+#include <linux/platform_data/intel-spi.h>
+
/* GPIO resources */
#define ICH_RES_GPIO 0
#define ICH_RES_GPE0 1
@@ -40,6 +42,7 @@
char name[32];
unsigned int iTCO_version;
unsigned int gpio_version;
+ enum intel_spi_type spi_type;
u8 use_gpio;
};
diff --git a/include/linux/msi.h b/include/linux/msi.h
index 0db320b..a83b84f 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -17,7 +17,13 @@
struct pci_dev;
struct platform_msi_priv_data;
void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
+#ifdef CONFIG_GENERIC_MSI_IRQ
void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg);
+#else
+static inline void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg)
+{
+}
+#endif
typedef void (*irq_write_msi_msg_t)(struct msi_desc *desc,
struct msi_msg *msg);
@@ -116,11 +122,15 @@
struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc);
void *msi_desc_to_pci_sysdata(struct msi_desc *desc);
+void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg);
#else /* CONFIG_PCI_MSI */
static inline void *msi_desc_to_pci_sysdata(struct msi_desc *desc)
{
return NULL;
}
+static inline void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg)
+{
+}
#endif /* CONFIG_PCI_MSI */
struct msi_desc *alloc_msi_entry(struct device *dev, int nvec,
@@ -128,7 +138,6 @@
void free_msi_entry(struct msi_desc *entry);
void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
-void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg);
u32 __pci_msix_desc_mask_irq(struct msi_desc *desc, u32 flag);
u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag);
diff --git a/include/linux/mtd/fsmc.h b/include/linux/mtd/fsmc.h
deleted file mode 100644
index ad3c348..0000000
--- a/include/linux/mtd/fsmc.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * incude/mtd/fsmc.h
- *
- * ST Microelectronics
- * Flexible Static Memory Controller (FSMC)
- * platform data interface and header file
- *
- * Copyright © 2010 ST Microelectronics
- * Vipin Kumar <vipin.kumar@st.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#ifndef __MTD_FSMC_H
-#define __MTD_FSMC_H
-
-#include <linux/io.h>
-#include <linux/platform_device.h>
-#include <linux/mtd/physmap.h>
-#include <linux/types.h>
-#include <linux/mtd/partitions.h>
-#include <asm/param.h>
-
-#define FSMC_NAND_BW8 1
-#define FSMC_NAND_BW16 2
-
-#define FSMC_MAX_NOR_BANKS 4
-#define FSMC_MAX_NAND_BANKS 4
-
-#define FSMC_FLASH_WIDTH8 1
-#define FSMC_FLASH_WIDTH16 2
-
-/* fsmc controller registers for NOR flash */
-#define CTRL 0x0
- /* ctrl register definitions */
- #define BANK_ENABLE (1 << 0)
- #define MUXED (1 << 1)
- #define NOR_DEV (2 << 2)
- #define WIDTH_8 (0 << 4)
- #define WIDTH_16 (1 << 4)
- #define RSTPWRDWN (1 << 6)
- #define WPROT (1 << 7)
- #define WRT_ENABLE (1 << 12)
- #define WAIT_ENB (1 << 13)
-
-#define CTRL_TIM 0x4
- /* ctrl_tim register definitions */
-
-#define FSMC_NOR_BANK_SZ 0x8
-#define FSMC_NOR_REG_SIZE 0x40
-
-#define FSMC_NOR_REG(base, bank, reg) (base + \
- FSMC_NOR_BANK_SZ * (bank) + \
- reg)
-
-/* fsmc controller registers for NAND flash */
-#define PC 0x00
- /* pc register definitions */
- #define FSMC_RESET (1 << 0)
- #define FSMC_WAITON (1 << 1)
- #define FSMC_ENABLE (1 << 2)
- #define FSMC_DEVTYPE_NAND (1 << 3)
- #define FSMC_DEVWID_8 (0 << 4)
- #define FSMC_DEVWID_16 (1 << 4)
- #define FSMC_ECCEN (1 << 6)
- #define FSMC_ECCPLEN_512 (0 << 7)
- #define FSMC_ECCPLEN_256 (1 << 7)
- #define FSMC_TCLR_1 (1)
- #define FSMC_TCLR_SHIFT (9)
- #define FSMC_TCLR_MASK (0xF)
- #define FSMC_TAR_1 (1)
- #define FSMC_TAR_SHIFT (13)
- #define FSMC_TAR_MASK (0xF)
-#define STS 0x04
- /* sts register definitions */
- #define FSMC_CODE_RDY (1 << 15)
-#define COMM 0x08
- /* comm register definitions */
- #define FSMC_TSET_0 0
- #define FSMC_TSET_SHIFT 0
- #define FSMC_TSET_MASK 0xFF
- #define FSMC_TWAIT_6 6
- #define FSMC_TWAIT_SHIFT 8
- #define FSMC_TWAIT_MASK 0xFF
- #define FSMC_THOLD_4 4
- #define FSMC_THOLD_SHIFT 16
- #define FSMC_THOLD_MASK 0xFF
- #define FSMC_THIZ_1 1
- #define FSMC_THIZ_SHIFT 24
- #define FSMC_THIZ_MASK 0xFF
-#define ATTRIB 0x0C
-#define IOATA 0x10
-#define ECC1 0x14
-#define ECC2 0x18
-#define ECC3 0x1C
-#define FSMC_NAND_BANK_SZ 0x20
-
-#define FSMC_NAND_REG(base, bank, reg) (base + FSMC_NOR_REG_SIZE + \
- (FSMC_NAND_BANK_SZ * (bank)) + \
- reg)
-
-#define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ)
-
-struct fsmc_nand_timings {
- uint8_t tclr;
- uint8_t tar;
- uint8_t thiz;
- uint8_t thold;
- uint8_t twait;
- uint8_t tset;
-};
-
-enum access_mode {
- USE_DMA_ACCESS = 1,
- USE_WORD_ACCESS,
-};
-
-/**
- * fsmc_nand_platform_data - platform specific NAND controller config
- * @nand_timings: timing setup for the physical NAND interface
- * @partitions: partition table for the platform, use a default fallback
- * if this is NULL
- * @nr_partitions: the number of partitions in the previous entry
- * @options: different options for the driver
- * @width: bus width
- * @bank: default bank
- * @select_bank: callback to select a certain bank, this is
- * platform-specific. If the controller only supports one bank
- * this may be set to NULL
- */
-struct fsmc_nand_platform_data {
- struct fsmc_nand_timings *nand_timings;
- struct mtd_partition *partitions;
- unsigned int nr_partitions;
- unsigned int options;
- unsigned int width;
- unsigned int bank;
-
- enum access_mode mode;
-
- void (*select_bank)(uint32_t bank, uint32_t busw);
-
- /* priv structures for dma accesses */
- void *read_dma_priv;
- void *write_dma_priv;
-};
-
-extern int __init fsmc_nor_init(struct platform_device *pdev,
- unsigned long base, uint32_t bank, uint32_t width);
-extern void __init fsmc_init_board_info(struct platform_device *pdev,
- struct mtd_partition *partitions, unsigned int nr_partitions,
- unsigned int width);
-
-#endif /* __MTD_FSMC_H */
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 13f8052..eebdc63 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -24,6 +24,7 @@
#include <linux/uio.h>
#include <linux/notifier.h>
#include <linux/device.h>
+#include <linux/of.h>
#include <mtd/mtd-abi.h>
@@ -322,6 +323,7 @@
int (*_block_isreserved) (struct mtd_info *mtd, loff_t ofs);
int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
+ int (*_max_bad_blocks) (struct mtd_info *mtd, loff_t ofs, size_t len);
int (*_suspend) (struct mtd_info *mtd);
void (*_resume) (struct mtd_info *mtd);
void (*_reboot) (struct mtd_info *mtd);
@@ -385,6 +387,8 @@
struct device_node *np)
{
mtd->dev.of_node = np;
+ if (!mtd->name)
+ of_property_read_string(np, "label", &mtd->name);
}
static inline struct device_node *mtd_get_of_node(struct mtd_info *mtd)
@@ -397,6 +401,18 @@
return ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize;
}
+static inline int mtd_max_bad_blocks(struct mtd_info *mtd,
+ loff_t ofs, size_t len)
+{
+ if (!mtd->_max_bad_blocks)
+ return -ENOTSUPP;
+
+ if (mtd->size < (len + ofs) || ofs < 0)
+ return -EINVAL;
+
+ return mtd->_max_bad_blocks(mtd, ofs, len);
+}
+
int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit,
struct mtd_pairing_info *info);
int mtd_pairing_info_to_wunit(struct mtd_info *mtd,
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index c5f3a01..9591e0f 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -615,7 +615,7 @@
* @tALS_min: ALE setup time
* @tAR_min: ALE to RE# delay
* @tCEA_max: CE# access time
- * @tCEH_min:
+ * @tCEH_min: CE# high hold time
* @tCH_min: CE# hold time
* @tCHZ_max: CE# high to output hi-Z
* @tCLH_min: CLE hold time
@@ -801,6 +801,10 @@
* supported, 0 otherwise.
* @jedec_params: [INTERN] holds the JEDEC parameter page when JEDEC is
* supported, 0 otherwise.
+ * @max_bb_per_die: [INTERN] the max number of bad blocks each die of a
+ * this nand device will encounter their life times.
+ * @blocks_per_die: [INTERN] The number of PEBs in a die
+ * @data_interface: [INTERN] NAND interface timing information
* @read_retries: [INTERN] the number of read retry modes supported
* @onfi_set_features: [REPLACEABLE] set the features for ONFI nand
* @onfi_get_features: [REPLACEABLE] get the features for ONFI nand
@@ -883,6 +887,8 @@
struct nand_onfi_params onfi_params;
struct nand_jedec_params jedec_params;
};
+ u16 max_bb_per_die;
+ u32 blocks_per_die;
struct nand_data_interface *data_interface;
@@ -958,6 +964,7 @@
#define NAND_MFR_SANDISK 0x45
#define NAND_MFR_INTEL 0x89
#define NAND_MFR_ATO 0x9b
+#define NAND_MFR_WINBOND 0xef
/* The maximum expected count of bytes in the NAND ID sequence */
#define NAND_MAX_ID_LEN 8
diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h
index 70736e1..06df1e0 100644
--- a/include/linux/mtd/partitions.h
+++ b/include/linux/mtd/partitions.h
@@ -41,6 +41,7 @@
uint64_t size; /* partition size */
uint64_t offset; /* offset within the master MTD space */
uint32_t mask_flags; /* master MTD flags to mask out for this partition */
+ struct device_node *of_node;
};
#define MTDPART_OFS_RETAIN (-3)
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index c425c7b..f2a7180 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -43,9 +43,13 @@
#define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */
#define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */
#define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */
-#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */
-#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */
+#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual Output SPI) */
+#define SPINOR_OP_READ_1_2_2 0xbb /* Read data bytes (Dual I/O SPI) */
+#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad Output SPI) */
+#define SPINOR_OP_READ_1_4_4 0xeb /* Read data bytes (Quad I/O SPI) */
#define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */
+#define SPINOR_OP_PP_1_1_4 0x32 /* Quad page program */
+#define SPINOR_OP_PP_1_4_4 0x38 /* Quad page program */
#define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */
#define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */
#define SPINOR_OP_BE_32K 0x52 /* Erase 32KiB block */
@@ -56,11 +60,17 @@
#define SPINOR_OP_RDFSR 0x70 /* Read flag status register */
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
-#define SPINOR_OP_READ4 0x13 /* Read data bytes (low frequency) */
-#define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */
-#define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */
-#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */
+#define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */
+#define SPINOR_OP_READ_FAST_4B 0x0c /* Read data bytes (high frequency) */
+#define SPINOR_OP_READ_1_1_2_4B 0x3c /* Read data bytes (Dual Output SPI) */
+#define SPINOR_OP_READ_1_2_2_4B 0xbc /* Read data bytes (Dual I/O SPI) */
+#define SPINOR_OP_READ_1_1_4_4B 0x6c /* Read data bytes (Quad Output SPI) */
+#define SPINOR_OP_READ_1_4_4_4B 0xec /* Read data bytes (Quad I/O SPI) */
#define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */
+#define SPINOR_OP_PP_1_1_4_4B 0x34 /* Quad page program */
+#define SPINOR_OP_PP_1_4_4_4B 0x3e /* Quad page program */
+#define SPINOR_OP_BE_4K_4B 0x21 /* Erase 4KiB block */
+#define SPINOR_OP_BE_32K_4B 0x5c /* Erase 32KiB block */
#define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */
/* Used for SST flashes only. */
@@ -68,6 +78,15 @@
#define SPINOR_OP_WRDI 0x04 /* Write disable */
#define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */
+/* Used for S3AN flashes only */
+#define SPINOR_OP_XSE 0x50 /* Sector erase */
+#define SPINOR_OP_XPP 0x82 /* Page program */
+#define SPINOR_OP_XRDSR 0xd7 /* Read status register */
+
+#define XSR_PAGESIZE BIT(0) /* Page size in Po2 or Linear */
+#define XSR_RDY BIT(7) /* Ready */
+
+
/* Used for Macronix and Winbond flashes. */
#define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */
#define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */
@@ -119,6 +138,9 @@
enum spi_nor_option_flags {
SNOR_F_USE_FSR = BIT(0),
SNOR_F_HAS_SR_TB = BIT(1),
+ SNOR_F_NO_OP_CHIP_ERASE = BIT(2),
+ SNOR_F_S3AN_ADDR_DEFAULT = BIT(3),
+ SNOR_F_READY_XSR_RDY = BIT(4),
};
/**
diff --git a/include/linux/mutex.h b/include/linux/mutex.h
index b97870f..1127fe3 100644
--- a/include/linux/mutex.h
+++ b/include/linux/mutex.h
@@ -20,6 +20,8 @@
#include <linux/osq_lock.h>
#include <linux/debug_locks.h>
+struct ww_acquire_ctx;
+
/*
* Simple, straightforward mutexes with strict semantics:
*
@@ -65,7 +67,7 @@
static inline struct task_struct *__mutex_owner(struct mutex *lock)
{
- return (struct task_struct *)(atomic_long_read(&lock->owner) & ~0x03);
+ return (struct task_struct *)(atomic_long_read(&lock->owner) & ~0x07);
}
/*
@@ -75,6 +77,7 @@
struct mutex_waiter {
struct list_head list;
struct task_struct *task;
+ struct ww_acquire_ctx *ww_ctx;
#ifdef CONFIG_DEBUG_MUTEXES
void *magic;
#endif
@@ -156,10 +159,12 @@
unsigned int subclass);
extern int __must_check mutex_lock_killable_nested(struct mutex *lock,
unsigned int subclass);
+extern void mutex_lock_io_nested(struct mutex *lock, unsigned int subclass);
#define mutex_lock(lock) mutex_lock_nested(lock, 0)
#define mutex_lock_interruptible(lock) mutex_lock_interruptible_nested(lock, 0)
#define mutex_lock_killable(lock) mutex_lock_killable_nested(lock, 0)
+#define mutex_lock_io(lock) mutex_lock_io_nested(lock, 0)
#define mutex_lock_nest_lock(lock, nest_lock) \
do { \
@@ -171,11 +176,13 @@
extern void mutex_lock(struct mutex *lock);
extern int __must_check mutex_lock_interruptible(struct mutex *lock);
extern int __must_check mutex_lock_killable(struct mutex *lock);
+extern void mutex_lock_io(struct mutex *lock);
# define mutex_lock_nested(lock, subclass) mutex_lock(lock)
# define mutex_lock_interruptible_nested(lock, subclass) mutex_lock_interruptible(lock)
# define mutex_lock_killable_nested(lock, subclass) mutex_lock_killable(lock)
# define mutex_lock_nest_lock(lock, nest_lock) mutex_lock(lock)
+# define mutex_lock_io_nested(lock, subclass) mutex_lock(lock)
#endif
/*
diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h
index 6a7fc50..13394ac 100644
--- a/include/linux/of_iommu.h
+++ b/include/linux/of_iommu.h
@@ -31,17 +31,6 @@
#endif /* CONFIG_OF_IOMMU */
-static inline void of_iommu_set_ops(struct device_node *np,
- const struct iommu_ops *ops)
-{
- iommu_register_instance(&np->fwnode, ops);
-}
-
-static inline const struct iommu_ops *of_iommu_get_ops(struct device_node *np)
-{
- return iommu_get_instance(&np->fwnode);
-}
-
extern struct of_device_id __iommu_of_table;
typedef int (*of_iommu_init_fn)(struct device_node *);
diff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h
index 5b2e615..93664f0 100644
--- a/include/linux/percpu-rwsem.h
+++ b/include/linux/percpu-rwsem.h
@@ -4,15 +4,15 @@
#include <linux/atomic.h>
#include <linux/rwsem.h>
#include <linux/percpu.h>
-#include <linux/wait.h>
+#include <linux/rcuwait.h>
#include <linux/rcu_sync.h>
#include <linux/lockdep.h>
struct percpu_rw_semaphore {
struct rcu_sync rss;
unsigned int __percpu *read_count;
- struct rw_semaphore rw_sem;
- wait_queue_head_t writer;
+ struct rw_semaphore rw_sem; /* slowpath */
+ struct rcuwait writer; /* blocked writer */
int readers_block;
};
@@ -22,7 +22,7 @@
.rss = __RCU_SYNC_INITIALIZER(name.rss, RCU_SCHED_SYNC), \
.read_count = &__percpu_rwsem_rc_##name, \
.rw_sem = __RWSEM_INITIALIZER(name.rw_sem), \
- .writer = __WAIT_QUEUE_HEAD_INITIALIZER(name.writer), \
+ .writer = __RCUWAIT_INITIALIZER(name.writer), \
}
extern int __percpu_down_read(struct percpu_rw_semaphore *, int);
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 78ed810..000fdb2 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -482,6 +482,7 @@
* @list: list of filters for this event
* @lock: spinlock that serializes accesses to the @list and event's
* (and its children's) filter generations.
+ * @nr_file_filters: number of file-based filters
*
* A child event will use parent's @list (and therefore @lock), so they are
* bundled together; see perf_event_addr_filters().
@@ -489,6 +490,7 @@
struct perf_addr_filters_head {
struct list_head list;
raw_spinlock_t lock;
+ unsigned int nr_file_filters;
};
/**
@@ -785,9 +787,9 @@
ktime_t hrtimer_interval;
unsigned int hrtimer_active;
- struct pmu *unique_pmu;
#ifdef CONFIG_CGROUP_PERF
struct perf_cgroup *cgrp;
+ struct list_head cgrp_cpuctx_entry;
#endif
struct list_head sched_cb_entry;
diff --git a/include/linux/platform_data/intel-spi.h b/include/linux/platform_data/intel-spi.h
new file mode 100644
index 0000000..942b0c3
--- /dev/null
+++ b/include/linux/platform_data/intel-spi.h
@@ -0,0 +1,31 @@
+/*
+ * Intel PCH/PCU SPI flash driver.
+ *
+ * Copyright (C) 2016, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.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.
+ */
+
+#ifndef INTEL_SPI_PDATA_H
+#define INTEL_SPI_PDATA_H
+
+enum intel_spi_type {
+ INTEL_SPI_BYT = 1,
+ INTEL_SPI_LPT,
+ INTEL_SPI_BXT,
+};
+
+/**
+ * struct intel_spi_boardinfo - Board specific data for Intel SPI driver
+ * @type: Type which this controller is compatible with
+ * @writeable: The chip is writeable
+ */
+struct intel_spi_boardinfo {
+ enum intel_spi_type type;
+ bool writeable;
+};
+
+#endif /* INTEL_SPI_PDATA_H */
diff --git a/include/linux/platform_data/spi-ep93xx.h b/include/linux/platform_data/spi-ep93xx.h
index 9bb63ac..171a271 100644
--- a/include/linux/platform_data/spi-ep93xx.h
+++ b/include/linux/platform_data/spi-ep93xx.h
@@ -5,25 +5,14 @@
/**
* struct ep93xx_spi_info - EP93xx specific SPI descriptor
- * @num_chipselect: number of chip selects on this board, must be
- * at least one
+ * @chipselect: array of gpio numbers to use as chip selects
+ * @num_chipselect: ARRAY_SIZE(chipselect)
* @use_dma: use DMA for the transfers
*/
struct ep93xx_spi_info {
+ int *chipselect;
int num_chipselect;
bool use_dma;
};
-/**
- * struct ep93xx_spi_chip_ops - operation callbacks for SPI slave device
- * @setup: setup the chip select mechanism
- * @cleanup: cleanup the chip select mechanism
- * @cs_control: control the device chip select
- */
-struct ep93xx_spi_chip_ops {
- int (*setup)(struct spi_device *spi);
- void (*cleanup)(struct spi_device *spi);
- void (*cs_control)(struct spi_device *spi, int value);
-};
-
#endif /* __ASM_MACH_EP93XX_SPI_H */
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 81ece61..5339ed5 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -182,6 +182,9 @@
{
return -ENOTSUPP;
}
+
+#define simple_qos_governor (*(struct dev_power_governor *)(NULL))
+#define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL))
#endif
static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index 0edd88f..a6685b3 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -78,6 +78,9 @@
#if defined(CONFIG_PM_OPP)
+struct opp_table *dev_pm_opp_get_opp_table(struct device *dev);
+void dev_pm_opp_put_opp_table(struct opp_table *opp_table);
+
unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp);
unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp);
@@ -88,7 +91,7 @@
unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev);
unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev);
unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev);
-struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev);
+unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev);
struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
unsigned long freq,
@@ -99,6 +102,7 @@
struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev,
unsigned long *freq);
+void dev_pm_opp_put(struct dev_pm_opp *opp);
int dev_pm_opp_add(struct device *dev, unsigned long freq,
unsigned long u_volt);
@@ -108,22 +112,30 @@
int dev_pm_opp_disable(struct device *dev, unsigned long freq);
-struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev);
-int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions,
- unsigned int count);
-void dev_pm_opp_put_supported_hw(struct device *dev);
-int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
-void dev_pm_opp_put_prop_name(struct device *dev);
+int dev_pm_opp_register_notifier(struct device *dev, struct notifier_block *nb);
+int dev_pm_opp_unregister_notifier(struct device *dev, struct notifier_block *nb);
+
+struct opp_table *dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, unsigned int count);
+void dev_pm_opp_put_supported_hw(struct opp_table *opp_table);
+struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name);
+void dev_pm_opp_put_prop_name(struct opp_table *opp_table);
struct opp_table *dev_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count);
void dev_pm_opp_put_regulators(struct opp_table *opp_table);
-int dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data));
-void dev_pm_opp_register_put_opp_helper(struct device *dev);
+struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data));
+void dev_pm_opp_register_put_opp_helper(struct opp_table *opp_table);
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
void dev_pm_opp_remove_table(struct device *dev);
void dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask);
#else
+static inline struct opp_table *dev_pm_opp_get_opp_table(struct device *dev)
+{
+ return ERR_PTR(-ENOTSUPP);
+}
+
+static inline void dev_pm_opp_put_opp_table(struct opp_table *opp_table) {}
+
static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
{
return 0;
@@ -159,9 +171,9 @@
return 0;
}
-static inline struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev)
+static inline unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev)
{
- return NULL;
+ return 0;
}
static inline struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
@@ -182,6 +194,8 @@
return ERR_PTR(-ENOTSUPP);
}
+static inline void dev_pm_opp_put(struct dev_pm_opp *opp) {}
+
static inline int dev_pm_opp_add(struct device *dev, unsigned long freq,
unsigned long u_volt)
{
@@ -202,35 +216,39 @@
return 0;
}
-static inline struct srcu_notifier_head *dev_pm_opp_get_notifier(
- struct device *dev)
+static inline int dev_pm_opp_register_notifier(struct device *dev, struct notifier_block *nb)
+{
+ return -ENOTSUPP;
+}
+
+static inline int dev_pm_opp_unregister_notifier(struct device *dev, struct notifier_block *nb)
+{
+ return -ENOTSUPP;
+}
+
+static inline struct opp_table *dev_pm_opp_set_supported_hw(struct device *dev,
+ const u32 *versions,
+ unsigned int count)
{
return ERR_PTR(-ENOTSUPP);
}
-static inline int dev_pm_opp_set_supported_hw(struct device *dev,
- const u32 *versions,
- unsigned int count)
-{
- return -ENOTSUPP;
-}
+static inline void dev_pm_opp_put_supported_hw(struct opp_table *opp_table) {}
-static inline void dev_pm_opp_put_supported_hw(struct device *dev) {}
-
-static inline int dev_pm_opp_register_set_opp_helper(struct device *dev,
+static inline struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev,
int (*set_opp)(struct dev_pm_set_opp_data *data))
{
- return -ENOTSUPP;
+ return ERR_PTR(-ENOTSUPP);
}
-static inline void dev_pm_opp_register_put_opp_helper(struct device *dev) {}
+static inline void dev_pm_opp_register_put_opp_helper(struct opp_table *opp_table) {}
-static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name)
+static inline struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name)
{
- return -ENOTSUPP;
+ return ERR_PTR(-ENOTSUPP);
}
-static inline void dev_pm_opp_put_prop_name(struct device *dev) {}
+static inline void dev_pm_opp_put_prop_name(struct opp_table *opp_table) {}
static inline struct opp_table *dev_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count)
{
@@ -270,6 +288,7 @@
int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask);
void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpumask);
int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
+struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev);
#else
static inline int dev_pm_opp_of_add_table(struct device *dev)
{
@@ -293,6 +312,11 @@
{
return -ENOTSUPP;
}
+
+static inline struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev)
+{
+ return NULL;
+}
#endif
#endif /* __LINUX_OPP_H__ */
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 0f65d36..3e2547d 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -6,7 +6,6 @@
*/
#include <linux/plist.h>
#include <linux/notifier.h>
-#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/workqueue.h>
@@ -147,8 +146,6 @@
struct notifier_block *notifier);
int dev_pm_qos_remove_notifier(struct device *dev,
struct notifier_block *notifier);
-int dev_pm_qos_add_global_notifier(struct notifier_block *notifier);
-int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier);
void dev_pm_qos_constraints_init(struct device *dev);
void dev_pm_qos_constraints_destroy(struct device *dev);
int dev_pm_qos_add_ancestor_request(struct device *dev,
@@ -200,12 +197,6 @@
static inline int dev_pm_qos_remove_notifier(struct device *dev,
struct notifier_block *notifier)
{ return 0; }
-static inline int dev_pm_qos_add_global_notifier(
- struct notifier_block *notifier)
- { return 0; }
-static inline int dev_pm_qos_remove_global_notifier(
- struct notifier_block *notifier)
- { return 0; }
static inline void dev_pm_qos_constraints_init(struct device *dev)
{
dev->power.power_state = PMSG_ON;
diff --git a/include/linux/poison.h b/include/linux/poison.h
index 51334ed..a395403 100644
--- a/include/linux/poison.h
+++ b/include/linux/poison.h
@@ -80,6 +80,7 @@
/********** kernel/mutexes **********/
#define MUTEX_DEBUG_INIT 0x11
#define MUTEX_DEBUG_FREE 0x22
+#define MUTEX_POISON_WW_CTX ((void *) 0x500 + POISON_POINTER_DELTA)
/********** lib/flex_array.c **********/
#define FLEX_ARRAY_FREE 0x6c /* for use-after-free poisoning */
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 62d44c1..64aa189 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -8,19 +8,9 @@
#include <linux/alarmtimer.h>
-static inline unsigned long long cputime_to_expires(cputime_t expires)
-{
- return (__force unsigned long long)expires;
-}
-
-static inline cputime_t expires_to_cputime(unsigned long long expires)
-{
- return (__force cputime_t)expires;
-}
-
struct cpu_timer_list {
struct list_head entry;
- unsigned long long expires, incr;
+ u64 expires, incr;
struct task_struct *task;
int firing;
};
@@ -129,7 +119,7 @@
void posix_cpu_timers_exit(struct task_struct *task);
void posix_cpu_timers_exit_group(struct task_struct *task);
void set_process_cpu_timer(struct task_struct *task, unsigned int clock_idx,
- cputime_t *newval, cputime_t *oldval);
+ u64 *newval, u64 *oldval);
long clock_nanosleep_restart(struct restart_block *restart_block);
diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h
index bed9557..b312bce 100644
--- a/include/linux/power/bq27xxx_battery.h
+++ b/include/linux/power/bq27xxx_battery.h
@@ -4,8 +4,16 @@
enum bq27xxx_chip {
BQ27000 = 1, /* bq27000, bq27200 */
BQ27010, /* bq27010, bq27210 */
- BQ27500, /* bq27500 */
- BQ27510, /* bq27510, bq27520 */
+ BQ2750X, /* bq27500 deprecated alias */
+ BQ2751X, /* bq27510, bq27520 deprecated alias */
+ BQ27500, /* bq27500/1 */
+ BQ27510G1, /* bq27510G1 */
+ BQ27510G2, /* bq27510G2 */
+ BQ27510G3, /* bq27510G3 */
+ BQ27520G1, /* bq27520G1 */
+ BQ27520G2, /* bq27520G2 */
+ BQ27520G3, /* bq27520G3 */
+ BQ27520G4, /* bq27520G4 */
BQ27530, /* bq27530, bq27531 */
BQ27541, /* bq27541, bq27542, bq27546, bq27742 */
BQ27545, /* bq27545 */
diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h
index 2d6f0c39..a052232 100644
--- a/include/linux/pxa2xx_ssp.h
+++ b/include/linux/pxa2xx_ssp.h
@@ -90,9 +90,9 @@
#define SSSR_RFL_MASK (0xf << 12) /* Receive FIFO Level mask */
#define SSCR1_TFT (0x000003c0) /* Transmit FIFO Threshold (mask) */
-#define SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..16] */
+#define SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..16] */
#define SSCR1_RFT (0x00003c00) /* Receive FIFO Threshold (mask) */
-#define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..16] */
+#define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..16] */
#define RX_THRESH_CE4100_DFLT 2
#define TX_THRESH_CE4100_DFLT 2
@@ -106,9 +106,9 @@
#define CE4100_SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..4] */
/* QUARK_X1000 SSCR0 bit definition */
-#define QUARK_X1000_SSCR0_DSS (0x1F) /* Data Size Select (mask) */
-#define QUARK_X1000_SSCR0_DataSize(x) ((x) - 1) /* Data Size Select [4..32] */
-#define QUARK_X1000_SSCR0_FRF (0x3 << 5) /* FRame Format (mask) */
+#define QUARK_X1000_SSCR0_DSS (0x1F << 0) /* Data Size Select (mask) */
+#define QUARK_X1000_SSCR0_DataSize(x) ((x) - 1) /* Data Size Select [4..32] */
+#define QUARK_X1000_SSCR0_FRF (0x3 << 5) /* FRame Format (mask) */
#define QUARK_X1000_SSCR0_Motorola (0x0 << 5) /* Motorola's Serial Peripheral Interface (SPI) */
#define RX_THRESH_QUARK_X1000_DFLT 1
@@ -121,8 +121,8 @@
#define QUARK_X1000_SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..32] */
#define QUARK_X1000_SSCR1_RFT (0x1F << 11) /* Receive FIFO Threshold (mask) */
#define QUARK_X1000_SSCR1_RxTresh(x) (((x) - 1) << 11) /* level [1..32] */
-#define QUARK_X1000_SSCR1_STRF (1 << 17) /* Select FIFO or EFWR */
-#define QUARK_X1000_SSCR1_EFWR (1 << 16) /* Enable FIFO Write/Read */
+#define QUARK_X1000_SSCR1_STRF (1 << 17) /* Select FIFO or EFWR */
+#define QUARK_X1000_SSCR1_EFWR (1 << 16) /* Enable FIFO Write/Read */
/* extra bits in PXA255, PXA26x and PXA27x SSP ports */
#define SSCR0_TISSP (1 << 4) /* TI Sync Serial Protocol */
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h
index 01f71e1..6ade6a5 100644
--- a/include/linux/rcupdate.h
+++ b/include/linux/rcupdate.h
@@ -1161,5 +1161,17 @@
ftrace_dump(oops_dump_mode); \
} while (0)
+/*
+ * Place this after a lock-acquisition primitive to guarantee that
+ * an UNLOCK+LOCK pair acts as a full barrier. This guarantee applies
+ * if the UNLOCK and LOCK are executed by the same CPU or if the
+ * UNLOCK and LOCK operate on the same lock variable.
+ */
+#ifdef CONFIG_PPC
+#define smp_mb__after_unlock_lock() smp_mb() /* Full ordering for lock. */
+#else /* #ifdef CONFIG_PPC */
+#define smp_mb__after_unlock_lock() do { } while (0)
+#endif /* #else #ifdef CONFIG_PPC */
+
#endif /* __LINUX_RCUPDATE_H */
diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h
index ac81e40..4f9b2fa 100644
--- a/include/linux/rcutiny.h
+++ b/include/linux/rcutiny.h
@@ -27,6 +27,12 @@
#include <linux/cache.h>
+struct rcu_dynticks;
+static inline int rcu_dynticks_snap(struct rcu_dynticks *rdtp)
+{
+ return 0;
+}
+
static inline unsigned long get_state_synchronize_rcu(void)
{
return 0;
diff --git a/include/linux/rcuwait.h b/include/linux/rcuwait.h
new file mode 100644
index 0000000..a4ede51
--- /dev/null
+++ b/include/linux/rcuwait.h
@@ -0,0 +1,63 @@
+#ifndef _LINUX_RCUWAIT_H_
+#define _LINUX_RCUWAIT_H_
+
+#include <linux/rcupdate.h>
+
+/*
+ * rcuwait provides a way of blocking and waking up a single
+ * task in an rcu-safe manner; where it is forbidden to use
+ * after exit_notify(). task_struct is not properly rcu protected,
+ * unless dealing with rcu-aware lists, ie: find_task_by_*().
+ *
+ * Alternatively we have task_rcu_dereference(), but the return
+ * semantics have different implications which would break the
+ * wakeup side. The only time @task is non-nil is when a user is
+ * blocked (or checking if it needs to) on a condition, and reset
+ * as soon as we know that the condition has succeeded and are
+ * awoken.
+ */
+struct rcuwait {
+ struct task_struct *task;
+};
+
+#define __RCUWAIT_INITIALIZER(name) \
+ { .task = NULL, }
+
+static inline void rcuwait_init(struct rcuwait *w)
+{
+ w->task = NULL;
+}
+
+extern void rcuwait_wake_up(struct rcuwait *w);
+
+/*
+ * The caller is responsible for locking around rcuwait_wait_event(),
+ * such that writes to @task are properly serialized.
+ */
+#define rcuwait_wait_event(w, condition) \
+({ \
+ /* \
+ * Complain if we are called after do_exit()/exit_notify(), \
+ * as we cannot rely on the rcu critical region for the \
+ * wakeup side. \
+ */ \
+ WARN_ON(current->exit_state); \
+ \
+ rcu_assign_pointer((w)->task, current); \
+ for (;;) { \
+ /* \
+ * Implicit barrier (A) pairs with (B) in \
+ * rcuwait_wake_up(). \
+ */ \
+ set_current_state(TASK_UNINTERRUPTIBLE); \
+ if (condition) \
+ break; \
+ \
+ schedule(); \
+ } \
+ \
+ WRITE_ONCE((w)->task, NULL); \
+ __set_current_state(TASK_RUNNING); \
+})
+
+#endif /* _LINUX_RCUWAIT_H_ */
diff --git a/include/linux/refcount.h b/include/linux/refcount.h
new file mode 100644
index 0000000..600aadf
--- /dev/null
+++ b/include/linux/refcount.h
@@ -0,0 +1,294 @@
+#ifndef _LINUX_REFCOUNT_H
+#define _LINUX_REFCOUNT_H
+
+/*
+ * Variant of atomic_t specialized for reference counts.
+ *
+ * The interface matches the atomic_t interface (to aid in porting) but only
+ * provides the few functions one should use for reference counting.
+ *
+ * It differs in that the counter saturates at UINT_MAX and will not move once
+ * there. This avoids wrapping the counter and causing 'spurious'
+ * use-after-free issues.
+ *
+ * Memory ordering rules are slightly relaxed wrt regular atomic_t functions
+ * and provide only what is strictly required for refcounts.
+ *
+ * The increments are fully relaxed; these will not provide ordering. The
+ * rationale is that whatever is used to obtain the object we're increasing the
+ * reference count on will provide the ordering. For locked data structures,
+ * its the lock acquire, for RCU/lockless data structures its the dependent
+ * load.
+ *
+ * Do note that inc_not_zero() provides a control dependency which will order
+ * future stores against the inc, this ensures we'll never modify the object
+ * if we did not in fact acquire a reference.
+ *
+ * The decrements will provide release order, such that all the prior loads and
+ * stores will be issued before, it also provides a control dependency, which
+ * will order us against the subsequent free().
+ *
+ * The control dependency is against the load of the cmpxchg (ll/sc) that
+ * succeeded. This means the stores aren't fully ordered, but this is fine
+ * because the 1->0 transition indicates no concurrency.
+ *
+ * Note that the allocator is responsible for ordering things between free()
+ * and alloc().
+ *
+ */
+
+#include <linux/atomic.h>
+#include <linux/bug.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_DEBUG_REFCOUNT
+#define REFCOUNT_WARN(cond, str) WARN_ON(cond)
+#define __refcount_check __must_check
+#else
+#define REFCOUNT_WARN(cond, str) (void)(cond)
+#define __refcount_check
+#endif
+
+typedef struct refcount_struct {
+ atomic_t refs;
+} refcount_t;
+
+#define REFCOUNT_INIT(n) { .refs = ATOMIC_INIT(n), }
+
+static inline void refcount_set(refcount_t *r, unsigned int n)
+{
+ atomic_set(&r->refs, n);
+}
+
+static inline unsigned int refcount_read(const refcount_t *r)
+{
+ return atomic_read(&r->refs);
+}
+
+static inline __refcount_check
+bool refcount_add_not_zero(unsigned int i, refcount_t *r)
+{
+ unsigned int old, new, val = atomic_read(&r->refs);
+
+ for (;;) {
+ if (!val)
+ return false;
+
+ if (unlikely(val == UINT_MAX))
+ return true;
+
+ new = val + i;
+ if (new < val)
+ new = UINT_MAX;
+ old = atomic_cmpxchg_relaxed(&r->refs, val, new);
+ if (old == val)
+ break;
+
+ val = old;
+ }
+
+ REFCOUNT_WARN(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
+
+ return true;
+}
+
+static inline void refcount_add(unsigned int i, refcount_t *r)
+{
+ REFCOUNT_WARN(!refcount_add_not_zero(i, r), "refcount_t: addition on 0; use-after-free.\n");
+}
+
+/*
+ * Similar to atomic_inc_not_zero(), will saturate at UINT_MAX and WARN.
+ *
+ * Provides no memory ordering, it is assumed the caller has guaranteed the
+ * object memory to be stable (RCU, etc.). It does provide a control dependency
+ * and thereby orders future stores. See the comment on top.
+ */
+static inline __refcount_check
+bool refcount_inc_not_zero(refcount_t *r)
+{
+ unsigned int old, new, val = atomic_read(&r->refs);
+
+ for (;;) {
+ new = val + 1;
+
+ if (!val)
+ return false;
+
+ if (unlikely(!new))
+ return true;
+
+ old = atomic_cmpxchg_relaxed(&r->refs, val, new);
+ if (old == val)
+ break;
+
+ val = old;
+ }
+
+ REFCOUNT_WARN(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
+
+ return true;
+}
+
+/*
+ * Similar to atomic_inc(), will saturate at UINT_MAX and WARN.
+ *
+ * Provides no memory ordering, it is assumed the caller already has a
+ * reference on the object, will WARN when this is not so.
+ */
+static inline void refcount_inc(refcount_t *r)
+{
+ REFCOUNT_WARN(!refcount_inc_not_zero(r), "refcount_t: increment on 0; use-after-free.\n");
+}
+
+/*
+ * Similar to atomic_dec_and_test(), it will WARN on underflow and fail to
+ * decrement when saturated at UINT_MAX.
+ *
+ * Provides release memory ordering, such that prior loads and stores are done
+ * before, and provides a control dependency such that free() must come after.
+ * See the comment on top.
+ */
+static inline __refcount_check
+bool refcount_sub_and_test(unsigned int i, refcount_t *r)
+{
+ unsigned int old, new, val = atomic_read(&r->refs);
+
+ for (;;) {
+ if (unlikely(val == UINT_MAX))
+ return false;
+
+ new = val - i;
+ if (new > val) {
+ REFCOUNT_WARN(new > val, "refcount_t: underflow; use-after-free.\n");
+ return false;
+ }
+
+ old = atomic_cmpxchg_release(&r->refs, val, new);
+ if (old == val)
+ break;
+
+ val = old;
+ }
+
+ return !new;
+}
+
+static inline __refcount_check
+bool refcount_dec_and_test(refcount_t *r)
+{
+ return refcount_sub_and_test(1, r);
+}
+
+/*
+ * Similar to atomic_dec(), it will WARN on underflow and fail to decrement
+ * when saturated at UINT_MAX.
+ *
+ * Provides release memory ordering, such that prior loads and stores are done
+ * before.
+ */
+static inline
+void refcount_dec(refcount_t *r)
+{
+ REFCOUNT_WARN(refcount_dec_and_test(r), "refcount_t: decrement hit 0; leaking memory.\n");
+}
+
+/*
+ * No atomic_t counterpart, it attempts a 1 -> 0 transition and returns the
+ * success thereof.
+ *
+ * Like all decrement operations, it provides release memory order and provides
+ * a control dependency.
+ *
+ * It can be used like a try-delete operator; this explicit case is provided
+ * and not cmpxchg in generic, because that would allow implementing unsafe
+ * operations.
+ */
+static inline __refcount_check
+bool refcount_dec_if_one(refcount_t *r)
+{
+ return atomic_cmpxchg_release(&r->refs, 1, 0) == 1;
+}
+
+/*
+ * No atomic_t counterpart, it decrements unless the value is 1, in which case
+ * it will return false.
+ *
+ * Was often done like: atomic_add_unless(&var, -1, 1)
+ */
+static inline __refcount_check
+bool refcount_dec_not_one(refcount_t *r)
+{
+ unsigned int old, new, val = atomic_read(&r->refs);
+
+ for (;;) {
+ if (unlikely(val == UINT_MAX))
+ return true;
+
+ if (val == 1)
+ return false;
+
+ new = val - 1;
+ if (new > val) {
+ REFCOUNT_WARN(new > val, "refcount_t: underflow; use-after-free.\n");
+ return true;
+ }
+
+ old = atomic_cmpxchg_release(&r->refs, val, new);
+ if (old == val)
+ break;
+
+ val = old;
+ }
+
+ return true;
+}
+
+/*
+ * Similar to atomic_dec_and_mutex_lock(), it will WARN on underflow and fail
+ * to decrement when saturated at UINT_MAX.
+ *
+ * Provides release memory ordering, such that prior loads and stores are done
+ * before, and provides a control dependency such that free() must come after.
+ * See the comment on top.
+ */
+static inline __refcount_check
+bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock)
+{
+ if (refcount_dec_not_one(r))
+ return false;
+
+ mutex_lock(lock);
+ if (!refcount_dec_and_test(r)) {
+ mutex_unlock(lock);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Similar to atomic_dec_and_lock(), it will WARN on underflow and fail to
+ * decrement when saturated at UINT_MAX.
+ *
+ * Provides release memory ordering, such that prior loads and stores are done
+ * before, and provides a control dependency such that free() must come after.
+ * See the comment on top.
+ */
+static inline __refcount_check
+bool refcount_dec_and_lock(refcount_t *r, spinlock_t *lock)
+{
+ if (refcount_dec_not_one(r))
+ return false;
+
+ spin_lock(lock);
+ if (!refcount_dec_and_test(r)) {
+ spin_unlock(lock);
+ return false;
+ }
+
+ return true;
+}
+
+#endif /* _LINUX_REFCOUNT_H */
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index f667313..e886492 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -40,12 +40,13 @@
};
/**
- * Default value for a register. We use an array of structs rather
- * than a simple array as many modern devices have very sparse
- * register maps.
+ * struct reg_default - Default value for a register.
*
* @reg: Register address.
* @def: Register default value.
+ *
+ * We use an array of structs rather than a simple array as many modern devices
+ * have very sparse register maps.
*/
struct reg_default {
unsigned int reg;
@@ -53,12 +54,14 @@
};
/**
- * Register/value pairs for sequences of writes with an optional delay in
- * microseconds to be applied after each write.
+ * struct reg_sequence - An individual write from a sequence of writes.
*
* @reg: Register address.
* @def: Register value.
* @delay_us: Delay to be applied after the register write in microseconds
+ *
+ * Register/value pairs for sequences of writes with an optional delay in
+ * microseconds to be applied after each write.
*/
struct reg_sequence {
unsigned int reg;
@@ -98,6 +101,7 @@
/**
* regmap_read_poll_timeout - Poll until a condition is met or a timeout occurs
+ *
* @map: Regmap to read from
* @addr: Address to poll
* @val: Unsigned integer variable to read the value into
@@ -146,8 +150,8 @@
};
/**
- * A register range, used for access related checks
- * (readable/writeable/volatile/precious checks)
+ * struct regmap_range - A register range, used for access related checks
+ * (readable/writeable/volatile/precious checks)
*
* @range_min: address of first register
* @range_max: address of last register
@@ -159,16 +163,18 @@
#define regmap_reg_range(low, high) { .range_min = low, .range_max = high, }
-/*
- * A table of ranges including some yes ranges and some no ranges.
- * If a register belongs to a no_range, the corresponding check function
- * will return false. If a register belongs to a yes range, the corresponding
- * check function will return true. "no_ranges" are searched first.
+/**
+ * struct regmap_access_table - A table of register ranges for access checks
*
* @yes_ranges : pointer to an array of regmap ranges used as "yes ranges"
* @n_yes_ranges: size of the above array
* @no_ranges: pointer to an array of regmap ranges used as "no ranges"
* @n_no_ranges: size of the above array
+ *
+ * A table of ranges including some yes ranges and some no ranges.
+ * If a register belongs to a no_range, the corresponding check function
+ * will return false. If a register belongs to a yes range, the corresponding
+ * check function will return true. "no_ranges" are searched first.
*/
struct regmap_access_table {
const struct regmap_range *yes_ranges;
@@ -181,7 +187,7 @@
typedef void (*regmap_unlock)(void *);
/**
- * Configuration for the register map of a device.
+ * struct regmap_config - Configuration for the register map of a device.
*
* @name: Optional name of the regmap. Useful when a device has multiple
* register regions.
@@ -314,22 +320,24 @@
};
/**
- * Configuration for indirectly accessed or paged registers.
- * Registers, mapped to this virtual range, are accessed in two steps:
- * 1. page selector register update;
- * 2. access through data window registers.
+ * struct regmap_range_cfg - Configuration for indirectly accessed or paged
+ * registers.
*
* @name: Descriptive name for diagnostics
*
* @range_min: Address of the lowest register address in virtual range.
* @range_max: Address of the highest register in virtual range.
*
- * @page_sel_reg: Register with selector field.
- * @page_sel_mask: Bit shift for selector value.
- * @page_sel_shift: Bit mask for selector value.
+ * @selector_reg: Register with selector field.
+ * @selector_mask: Bit shift for selector value.
+ * @selector_shift: Bit mask for selector value.
*
* @window_start: Address of first (lowest) register in data window.
* @window_len: Number of registers in data window.
+ *
+ * Registers, mapped to this virtual range, are accessed in two steps:
+ * 1. page selector register update;
+ * 2. access through data window registers.
*/
struct regmap_range_cfg {
const char *name;
@@ -372,7 +380,8 @@
typedef void (*regmap_hw_free_context)(void *context);
/**
- * Description of a hardware bus for the register map infrastructure.
+ * struct regmap_bus - Description of a hardware bus for the register map
+ * infrastructure.
*
* @fast_io: Register IO is fast. Use a spinlock instead of a mutex
* to perform locking. This field is ignored if custom lock/unlock
@@ -385,6 +394,10 @@
* must serialise with respect to non-async I/O.
* @reg_write: Write a single register value to the given register address. This
* write operation has to complete when returning from the function.
+ * @reg_update_bits: Update bits operation to be used against volatile
+ * registers, intended for devices supporting some mechanism
+ * for setting clearing bits without having to
+ * read/modify/write.
* @read: Read operation. Data is returned in the buffer used to transmit
* data.
* @reg_read: Read a single register value from a given register address.
@@ -514,7 +527,7 @@
#endif
/**
- * regmap_init(): Initialise register map
+ * regmap_init() - Initialise register map
*
* @dev: Device that will be interacted with
* @bus: Bus-specific callbacks to use with device
@@ -532,7 +545,7 @@
const struct regmap_config *config);
/**
- * regmap_init_i2c(): Initialise register map
+ * regmap_init_i2c() - Initialise register map
*
* @i2c: Device that will be interacted with
* @config: Configuration for register map
@@ -545,9 +558,9 @@
i2c, config)
/**
- * regmap_init_spi(): Initialise register map
+ * regmap_init_spi() - Initialise register map
*
- * @spi: Device that will be interacted with
+ * @dev: Device that will be interacted with
* @config: Configuration for register map
*
* The return value will be an ERR_PTR() on error or a valid pointer to
@@ -558,8 +571,9 @@
dev, config)
/**
- * regmap_init_spmi_base(): Create regmap for the Base register space
- * @sdev: SPMI device that will be interacted with
+ * regmap_init_spmi_base() - Create regmap for the Base register space
+ *
+ * @dev: SPMI device that will be interacted with
* @config: Configuration for register map
*
* The return value will be an ERR_PTR() on error or a valid pointer to
@@ -570,8 +584,9 @@
dev, config)
/**
- * regmap_init_spmi_ext(): Create regmap for Ext register space
- * @sdev: Device that will be interacted with
+ * regmap_init_spmi_ext() - Create regmap for Ext register space
+ *
+ * @dev: Device that will be interacted with
* @config: Configuration for register map
*
* The return value will be an ERR_PTR() on error or a valid pointer to
@@ -582,7 +597,7 @@
dev, config)
/**
- * regmap_init_mmio_clk(): Initialise register map with register clock
+ * regmap_init_mmio_clk() - Initialise register map with register clock
*
* @dev: Device that will be interacted with
* @clk_id: register clock consumer ID
@@ -597,7 +612,7 @@
dev, clk_id, regs, config)
/**
- * regmap_init_mmio(): Initialise register map
+ * regmap_init_mmio() - Initialise register map
*
* @dev: Device that will be interacted with
* @regs: Pointer to memory-mapped IO region
@@ -610,7 +625,7 @@
regmap_init_mmio_clk(dev, NULL, regs, config)
/**
- * regmap_init_ac97(): Initialise AC'97 register map
+ * regmap_init_ac97() - Initialise AC'97 register map
*
* @ac97: Device that will be interacted with
* @config: Configuration for register map
@@ -624,7 +639,7 @@
bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
/**
- * devm_regmap_init(): Initialise managed register map
+ * devm_regmap_init() - Initialise managed register map
*
* @dev: Device that will be interacted with
* @bus: Bus-specific callbacks to use with device
@@ -641,7 +656,7 @@
dev, bus, bus_context, config)
/**
- * devm_regmap_init_i2c(): Initialise managed register map
+ * devm_regmap_init_i2c() - Initialise managed register map
*
* @i2c: Device that will be interacted with
* @config: Configuration for register map
@@ -655,9 +670,9 @@
i2c, config)
/**
- * devm_regmap_init_spi(): Initialise register map
+ * devm_regmap_init_spi() - Initialise register map
*
- * @spi: Device that will be interacted with
+ * @dev: Device that will be interacted with
* @config: Configuration for register map
*
* The return value will be an ERR_PTR() on error or a valid pointer
@@ -669,8 +684,9 @@
dev, config)
/**
- * devm_regmap_init_spmi_base(): Create managed regmap for Base register space
- * @sdev: SPMI device that will be interacted with
+ * devm_regmap_init_spmi_base() - Create managed regmap for Base register space
+ *
+ * @dev: SPMI device that will be interacted with
* @config: Configuration for register map
*
* The return value will be an ERR_PTR() on error or a valid pointer
@@ -682,8 +698,9 @@
dev, config)
/**
- * devm_regmap_init_spmi_ext(): Create managed regmap for Ext register space
- * @sdev: SPMI device that will be interacted with
+ * devm_regmap_init_spmi_ext() - Create managed regmap for Ext register space
+ *
+ * @dev: SPMI device that will be interacted with
* @config: Configuration for register map
*
* The return value will be an ERR_PTR() on error or a valid pointer
@@ -695,7 +712,7 @@
dev, config)
/**
- * devm_regmap_init_mmio_clk(): Initialise managed register map with clock
+ * devm_regmap_init_mmio_clk() - Initialise managed register map with clock
*
* @dev: Device that will be interacted with
* @clk_id: register clock consumer ID
@@ -711,7 +728,7 @@
dev, clk_id, regs, config)
/**
- * devm_regmap_init_mmio(): Initialise managed register map
+ * devm_regmap_init_mmio() - Initialise managed register map
*
* @dev: Device that will be interacted with
* @regs: Pointer to memory-mapped IO region
@@ -725,7 +742,7 @@
devm_regmap_init_mmio_clk(dev, NULL, regs, config)
/**
- * devm_regmap_init_ac97(): Initialise AC'97 register map
+ * devm_regmap_init_ac97() - Initialise AC'97 register map
*
* @ac97: Device that will be interacted with
* @config: Configuration for register map
@@ -800,7 +817,7 @@
unsigned int nranges);
/**
- * Description of an register field
+ * struct reg_field - Description of an register field
*
* @reg: Offset of the register within the regmap bank
* @lsb: lsb of the register field.
@@ -841,7 +858,7 @@
bool *change, bool async, bool force);
/**
- * Description of an IRQ for the generic regmap irq_chip.
+ * struct regmap_irq - Description of an IRQ for the generic regmap irq_chip.
*
* @reg_offset: Offset of the status/mask register within the bank
* @mask: Mask used to flag/control the register.
@@ -861,9 +878,7 @@
[_irq] = { .reg_offset = (_off), .mask = (_mask) }
/**
- * Description of a generic regmap irq_chip. This is not intended to
- * handle every possible interrupt controller, but it should handle a
- * substantial proportion of those that are found in the wild.
+ * struct regmap_irq_chip - Description of a generic regmap irq_chip.
*
* @name: Descriptive name for IRQ controller.
*
@@ -897,6 +912,10 @@
* after handling the interrupts in regmap_irq_handler().
* @irq_drv_data: Driver specific IRQ data which is passed as parameter when
* driver specific pre/post interrupt handler is called.
+ *
+ * This is not intended to handle every possible interrupt controller, but
+ * it should handle a substantial proportion of those that are found in the
+ * wild.
*/
struct regmap_irq_chip {
const char *name;
diff --git a/include/linux/sched.h b/include/linux/sched.h
index ad3ec9e..c8e519d 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -29,7 +29,6 @@
#include <asm/page.h>
#include <asm/ptrace.h>
-#include <linux/cputime.h>
#include <linux/smp.h>
#include <linux/sem.h>
@@ -227,7 +226,7 @@
extern char ___assert_task_state[1 - 2*!!(
sizeof(TASK_STATE_TO_CHAR_STR)-1 != ilog2(TASK_STATE_MAX)+1)];
-/* Convenience macros for the sake of set_task_state */
+/* Convenience macros for the sake of set_current_state */
#define TASK_KILLABLE (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)
#define TASK_STOPPED (TASK_WAKEKILL | __TASK_STOPPED)
#define TASK_TRACED (TASK_WAKEKILL | __TASK_TRACED)
@@ -254,17 +253,6 @@
#ifdef CONFIG_DEBUG_ATOMIC_SLEEP
-#define __set_task_state(tsk, state_value) \
- do { \
- (tsk)->task_state_change = _THIS_IP_; \
- (tsk)->state = (state_value); \
- } while (0)
-#define set_task_state(tsk, state_value) \
- do { \
- (tsk)->task_state_change = _THIS_IP_; \
- smp_store_mb((tsk)->state, (state_value)); \
- } while (0)
-
#define __set_current_state(state_value) \
do { \
current->task_state_change = _THIS_IP_; \
@@ -277,20 +265,6 @@
} while (0)
#else
-
-/*
- * @tsk had better be current, or you get to keep the pieces.
- *
- * The only reason is that computing current can be more expensive than
- * using a pointer that's already available.
- *
- * Therefore, see set_current_state().
- */
-#define __set_task_state(tsk, state_value) \
- do { (tsk)->state = (state_value); } while (0)
-#define set_task_state(tsk, state_value) \
- smp_store_mb((tsk)->state, (state_value))
-
/*
* set_current_state() includes a barrier so that the write of current->state
* is correctly serialised wrt the caller's subsequent test of whether to
@@ -461,12 +435,10 @@
asmlinkage void schedule(void);
extern void schedule_preempt_disabled(void);
+extern int __must_check io_schedule_prepare(void);
+extern void io_schedule_finish(int token);
extern long io_schedule_timeout(long timeout);
-
-static inline void io_schedule(void)
-{
- io_schedule_timeout(MAX_SCHEDULE_TIMEOUT);
-}
+extern void io_schedule(void);
void __noreturn do_task_dead(void);
@@ -565,15 +537,13 @@
int ac_flag;
long ac_exitcode;
unsigned long ac_mem;
- cputime_t ac_utime, ac_stime;
+ u64 ac_utime, ac_stime;
unsigned long ac_minflt, ac_majflt;
};
struct cpu_itimer {
- cputime_t expires;
- cputime_t incr;
- u32 error;
- u32 incr_error;
+ u64 expires;
+ u64 incr;
};
/**
@@ -587,8 +557,8 @@
*/
struct prev_cputime {
#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
- cputime_t utime;
- cputime_t stime;
+ u64 utime;
+ u64 stime;
raw_spinlock_t lock;
#endif
};
@@ -603,8 +573,8 @@
/**
* struct task_cputime - collected CPU time counts
- * @utime: time spent in user mode, in &cputime_t units
- * @stime: time spent in kernel mode, in &cputime_t units
+ * @utime: time spent in user mode, in nanoseconds
+ * @stime: time spent in kernel mode, in nanoseconds
* @sum_exec_runtime: total time spent on the CPU, in nanoseconds
*
* This structure groups together three kinds of CPU time that are tracked for
@@ -612,8 +582,8 @@
* these counts together and treat all three of them in parallel.
*/
struct task_cputime {
- cputime_t utime;
- cputime_t stime;
+ u64 utime;
+ u64 stime;
unsigned long long sum_exec_runtime;
};
@@ -622,13 +592,6 @@
#define prof_exp stime
#define sched_exp sum_exec_runtime
-#define INIT_CPUTIME \
- (struct task_cputime) { \
- .utime = 0, \
- .stime = 0, \
- .sum_exec_runtime = 0, \
- }
-
/*
* This is the atomic variant of task_cputime, which can be used for
* storing and updating task_cputime statistics without locking.
@@ -734,13 +697,14 @@
unsigned int is_child_subreaper:1;
unsigned int has_child_subreaper:1;
+#ifdef CONFIG_POSIX_TIMERS
+
/* POSIX.1b Interval Timers */
int posix_timer_id;
struct list_head posix_timers;
/* ITIMER_REAL timer for the process */
struct hrtimer real_timer;
- struct pid *leader_pid;
ktime_t it_real_incr;
/*
@@ -759,12 +723,16 @@
/* Earliest-expiration cache. */
struct task_cputime cputime_expires;
+ struct list_head cpu_timers[3];
+
+#endif
+
+ struct pid *leader_pid;
+
#ifdef CONFIG_NO_HZ_FULL
atomic_t tick_dep_mask;
#endif
- struct list_head cpu_timers[3];
-
struct pid *tty_old_pgrp;
/* boolean value for session group leader */
@@ -782,9 +750,9 @@
* in __exit_signal, except for the group leader.
*/
seqlock_t stats_lock;
- cputime_t utime, stime, cutime, cstime;
- cputime_t gtime;
- cputime_t cgtime;
+ u64 utime, stime, cutime, cstime;
+ u64 gtime;
+ u64 cgtime;
struct prev_cputime prev_cputime;
unsigned long nvcsw, nivcsw, cnvcsw, cnivcsw;
unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt;
@@ -1025,8 +993,8 @@
*
* The DEFINE_WAKE_Q macro declares and initializes the list head.
* wake_up_q() does NOT reinitialize the list; it's expected to be
- * called near the end of a function, where the fact that the queue is
- * not used again will be easy to see by inspection.
+ * called near the end of a function. Otherwise, the list can be
+ * re-initialized for later re-use by wake_q_init().
*
* Note that this can cause spurious wakeups. schedule() callers
* must ensure the call is done inside a loop, confirming that the
@@ -1046,6 +1014,12 @@
#define DEFINE_WAKE_Q(name) \
struct wake_q_head name = { WAKE_Q_TAIL, &name.first }
+static inline void wake_q_init(struct wake_q_head *head)
+{
+ head->first = WAKE_Q_TAIL;
+ head->lastp = &head->first;
+}
+
extern void wake_q_add(struct wake_q_head *head,
struct task_struct *task);
extern void wake_up_q(struct wake_q_head *head);
@@ -1663,11 +1637,11 @@
int __user *set_child_tid; /* CLONE_CHILD_SETTID */
int __user *clear_child_tid; /* CLONE_CHILD_CLEARTID */
- cputime_t utime, stime;
+ u64 utime, stime;
#ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME
- cputime_t utimescaled, stimescaled;
+ u64 utimescaled, stimescaled;
#endif
- cputime_t gtime;
+ u64 gtime;
struct prev_cputime prev_cputime;
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
seqcount_t vtime_seqcount;
@@ -1691,8 +1665,10 @@
/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
unsigned long min_flt, maj_flt;
+#ifdef CONFIG_POSIX_TIMERS
struct task_cputime cputime_expires;
struct list_head cpu_timers[3];
+#endif
/* process credentials */
const struct cred __rcu *ptracer_cred; /* Tracer's credentials at attach */
@@ -1817,7 +1793,7 @@
#if defined(CONFIG_TASK_XACCT)
u64 acct_rss_mem1; /* accumulated rss usage */
u64 acct_vm_mem1; /* accumulated virtual memory usage */
- cputime_t acct_timexpd; /* stime + utime since last update */
+ u64 acct_timexpd; /* stime + utime since last update */
#endif
#ifdef CONFIG_CPUSETS
nodemask_t mems_allowed; /* Protected by alloc_lock */
@@ -2262,17 +2238,17 @@
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
extern void task_cputime(struct task_struct *t,
- cputime_t *utime, cputime_t *stime);
-extern cputime_t task_gtime(struct task_struct *t);
+ u64 *utime, u64 *stime);
+extern u64 task_gtime(struct task_struct *t);
#else
static inline void task_cputime(struct task_struct *t,
- cputime_t *utime, cputime_t *stime)
+ u64 *utime, u64 *stime)
{
*utime = t->utime;
*stime = t->stime;
}
-static inline cputime_t task_gtime(struct task_struct *t)
+static inline u64 task_gtime(struct task_struct *t)
{
return t->gtime;
}
@@ -2280,23 +2256,23 @@
#ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME
static inline void task_cputime_scaled(struct task_struct *t,
- cputime_t *utimescaled,
- cputime_t *stimescaled)
+ u64 *utimescaled,
+ u64 *stimescaled)
{
*utimescaled = t->utimescaled;
*stimescaled = t->stimescaled;
}
#else
static inline void task_cputime_scaled(struct task_struct *t,
- cputime_t *utimescaled,
- cputime_t *stimescaled)
+ u64 *utimescaled,
+ u64 *stimescaled)
{
task_cputime(t, utimescaled, stimescaled);
}
#endif
-extern void task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st);
-extern void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st);
+extern void task_cputime_adjusted(struct task_struct *p, u64 *ut, u64 *st);
+extern void thread_group_cputime_adjusted(struct task_struct *p, u64 *ut, u64 *st);
/*
* Per process flags
@@ -2515,10 +2491,18 @@
extern void sched_clock_init(void);
#ifndef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK
+static inline void sched_clock_init_late(void)
+{
+}
+
static inline void sched_clock_tick(void)
{
}
+static inline void clear_sched_clock_stable(void)
+{
+}
+
static inline void sched_clock_idle_sleep_event(void)
{
}
@@ -2537,6 +2521,7 @@
return sched_clock();
}
#else
+extern void sched_clock_init_late(void);
/*
* Architectures can set this to 1 if they have specified
* CONFIG_HAVE_UNSTABLE_SCHED_CLOCK in their arch Kconfig,
@@ -2544,7 +2529,6 @@
* is reliable after all:
*/
extern int sched_clock_stable(void);
-extern void set_sched_clock_stable(void);
extern void clear_sched_clock_stable(void);
extern void sched_clock_tick(void);
diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h
index 4411453..49308e1 100644
--- a/include/linux/sched/sysctl.h
+++ b/include/linux/sched/sysctl.h
@@ -59,6 +59,7 @@
extern unsigned int sysctl_sched_autogroup_enabled;
#endif
+extern int sysctl_sched_rr_timeslice;
extern int sched_rr_timeslice;
extern int sched_rr_handler(struct ctl_table *table, int write,
diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h
index 47dd0ce..59248dc 100644
--- a/include/linux/spinlock.h
+++ b/include/linux/spinlock.h
@@ -180,8 +180,6 @@
#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define raw_spin_lock_nested(lock, subclass) \
_raw_spin_lock_nested(lock, subclass)
-# define raw_spin_lock_bh_nested(lock, subclass) \
- _raw_spin_lock_bh_nested(lock, subclass)
# define raw_spin_lock_nest_lock(lock, nest_lock) \
do { \
@@ -197,7 +195,6 @@
# define raw_spin_lock_nested(lock, subclass) \
_raw_spin_lock(((void)(subclass), (lock)))
# define raw_spin_lock_nest_lock(lock, nest_lock) _raw_spin_lock(lock)
-# define raw_spin_lock_bh_nested(lock, subclass) _raw_spin_lock_bh(lock)
#endif
#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)
@@ -317,11 +314,6 @@
raw_spin_lock_nested(spinlock_check(lock), subclass); \
} while (0)
-#define spin_lock_bh_nested(lock, subclass) \
-do { \
- raw_spin_lock_bh_nested(spinlock_check(lock), subclass);\
-} while (0)
-
#define spin_lock_nest_lock(lock, nest_lock) \
do { \
raw_spin_lock_nest_lock(spinlock_check(lock), nest_lock); \
diff --git a/include/linux/spinlock_api_smp.h b/include/linux/spinlock_api_smp.h
index 5344268..42dfab8 100644
--- a/include/linux/spinlock_api_smp.h
+++ b/include/linux/spinlock_api_smp.h
@@ -22,8 +22,6 @@
void __lockfunc _raw_spin_lock(raw_spinlock_t *lock) __acquires(lock);
void __lockfunc _raw_spin_lock_nested(raw_spinlock_t *lock, int subclass)
__acquires(lock);
-void __lockfunc _raw_spin_lock_bh_nested(raw_spinlock_t *lock, int subclass)
- __acquires(lock);
void __lockfunc
_raw_spin_lock_nest_lock(raw_spinlock_t *lock, struct lockdep_map *map)
__acquires(lock);
diff --git a/include/linux/spinlock_api_up.h b/include/linux/spinlock_api_up.h
index d3afef9..d0d1888 100644
--- a/include/linux/spinlock_api_up.h
+++ b/include/linux/spinlock_api_up.h
@@ -57,7 +57,6 @@
#define _raw_spin_lock(lock) __LOCK(lock)
#define _raw_spin_lock_nested(lock, subclass) __LOCK(lock)
-#define _raw_spin_lock_bh_nested(lock, subclass) __LOCK(lock)
#define _raw_read_lock(lock) __LOCK(lock)
#define _raw_write_lock(lock) __LOCK(lock)
#define _raw_spin_lock_bh(lock) __LOCK_BH(lock)
diff --git a/include/linux/srcu.h b/include/linux/srcu.h
index dc8eb63..a598cf3 100644
--- a/include/linux/srcu.h
+++ b/include/linux/srcu.h
@@ -33,9 +33,9 @@
#include <linux/rcupdate.h>
#include <linux/workqueue.h>
-struct srcu_struct_array {
- unsigned long c[2];
- unsigned long seq[2];
+struct srcu_array {
+ unsigned long lock_count[2];
+ unsigned long unlock_count[2];
};
struct rcu_batch {
@@ -46,7 +46,7 @@
struct srcu_struct {
unsigned long completed;
- struct srcu_struct_array __percpu *per_cpu_ref;
+ struct srcu_array __percpu *per_cpu_ref;
spinlock_t queue_lock; /* protect ->batch_queue, ->running */
bool running;
/* callbacks just queued */
@@ -118,7 +118,7 @@
* See include/linux/percpu-defs.h for the rules on per-CPU variables.
*/
#define __DEFINE_SRCU(name, is_static) \
- static DEFINE_PER_CPU(struct srcu_struct_array, name##_srcu_array);\
+ static DEFINE_PER_CPU(struct srcu_array, name##_srcu_array);\
is_static struct srcu_struct name = __SRCU_STRUCT_INIT(name)
#define DEFINE_SRCU(name) __DEFINE_SRCU(name, /* not static */)
#define DEFINE_STATIC_SRCU(name) __DEFINE_SRCU(name, static)
diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h
index 62a60ee..8a511c0 100644
--- a/include/linux/sunrpc/cache.h
+++ b/include/linux/sunrpc/cache.h
@@ -198,7 +198,7 @@
static inline void cache_put(struct cache_head *h, struct cache_detail *cd)
{
- if (atomic_read(&h->ref.refcount) <= 2 &&
+ if (kref_read(&h->ref) <= 2 &&
h->expiry_time < cd->nextcheck)
cd->nextcheck = h->expiry_time;
kref_put(&h->ref, cd->cache_put);
diff --git a/include/linux/timer.h b/include/linux/timer.h
index 51d601f..5a209b8 100644
--- a/include/linux/timer.h
+++ b/include/linux/timer.h
@@ -20,11 +20,6 @@
unsigned long data;
u32 flags;
-#ifdef CONFIG_TIMER_STATS
- int start_pid;
- void *start_site;
- char start_comm[16];
-#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
@@ -197,46 +192,6 @@
*/
#define NEXT_TIMER_MAX_DELTA ((1UL << 30) - 1)
-/*
- * Timer-statistics info:
- */
-#ifdef CONFIG_TIMER_STATS
-
-extern int timer_stats_active;
-
-extern void init_timer_stats(void);
-
-extern void timer_stats_update_stats(void *timer, pid_t pid, void *startf,
- void *timerf, char *comm, u32 flags);
-
-extern void __timer_stats_timer_set_start_info(struct timer_list *timer,
- void *addr);
-
-static inline void timer_stats_timer_set_start_info(struct timer_list *timer)
-{
- if (likely(!timer_stats_active))
- return;
- __timer_stats_timer_set_start_info(timer, __builtin_return_address(0));
-}
-
-static inline void timer_stats_timer_clear_start_info(struct timer_list *timer)
-{
- timer->start_site = NULL;
-}
-#else
-static inline void init_timer_stats(void)
-{
-}
-
-static inline void timer_stats_timer_set_start_info(struct timer_list *timer)
-{
-}
-
-static inline void timer_stats_timer_clear_start_info(struct timer_list *timer)
-{
-}
-#endif
-
extern void add_timer(struct timer_list *timer);
extern int try_to_del_timer_sync(struct timer_list *timer);
diff --git a/include/linux/vtime.h b/include/linux/vtime.h
index aa9bfea..0681fe2 100644
--- a/include/linux/vtime.h
+++ b/include/linux/vtime.h
@@ -58,27 +58,28 @@
extern void vtime_account_system(struct task_struct *tsk);
extern void vtime_account_idle(struct task_struct *tsk);
-extern void vtime_account_user(struct task_struct *tsk);
#else /* !CONFIG_VIRT_CPU_ACCOUNTING */
static inline void vtime_task_switch(struct task_struct *prev) { }
static inline void vtime_account_system(struct task_struct *tsk) { }
-static inline void vtime_account_user(struct task_struct *tsk) { }
#endif /* !CONFIG_VIRT_CPU_ACCOUNTING */
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
extern void arch_vtime_task_switch(struct task_struct *tsk);
+extern void vtime_account_user(struct task_struct *tsk);
extern void vtime_user_enter(struct task_struct *tsk);
static inline void vtime_user_exit(struct task_struct *tsk)
{
vtime_account_user(tsk);
}
+
extern void vtime_guest_enter(struct task_struct *tsk);
extern void vtime_guest_exit(struct task_struct *tsk);
extern void vtime_init_idle(struct task_struct *tsk, int cpu);
#else /* !CONFIG_VIRT_CPU_ACCOUNTING_GEN */
+static inline void vtime_account_user(struct task_struct *tsk) { }
static inline void vtime_user_enter(struct task_struct *tsk) { }
static inline void vtime_user_exit(struct task_struct *tsk) { }
static inline void vtime_guest_enter(struct task_struct *tsk) { }
@@ -93,9 +94,11 @@
/* On hard|softirq exit we always account to hard|softirq cputime */
vtime_account_system(tsk);
}
+extern void vtime_flush(struct task_struct *tsk);
#else /* !CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
static inline void vtime_account_irq_enter(struct task_struct *tsk) { }
static inline void vtime_account_irq_exit(struct task_struct *tsk) { }
+static inline void vtime_flush(struct task_struct *tsk) { }
#endif
diff --git a/include/linux/ww_mutex.h b/include/linux/ww_mutex.h
index 7b00668..5dd9a76 100644
--- a/include/linux/ww_mutex.h
+++ b/include/linux/ww_mutex.h
@@ -51,10 +51,10 @@
};
#ifdef CONFIG_DEBUG_LOCK_ALLOC
-# define __WW_CLASS_MUTEX_INITIALIZER(lockname, ww_class) \
- , .ww_class = &ww_class
+# define __WW_CLASS_MUTEX_INITIALIZER(lockname, class) \
+ , .ww_class = class
#else
-# define __WW_CLASS_MUTEX_INITIALIZER(lockname, ww_class)
+# define __WW_CLASS_MUTEX_INITIALIZER(lockname, class)
#endif
#define __WW_CLASS_INITIALIZER(ww_class) \
@@ -63,7 +63,7 @@
, .mutex_name = #ww_class "_mutex" }
#define __WW_MUTEX_INITIALIZER(lockname, class) \
- { .base = { \__MUTEX_INITIALIZER(lockname) } \
+ { .base = __MUTEX_INITIALIZER(lockname.base) \
__WW_CLASS_MUTEX_INITIALIZER(lockname, class) }
#define DEFINE_WW_CLASS(classname) \
@@ -186,11 +186,6 @@
#endif
}
-extern int __must_check __ww_mutex_lock(struct ww_mutex *lock,
- struct ww_acquire_ctx *ctx);
-extern int __must_check __ww_mutex_lock_interruptible(struct ww_mutex *lock,
- struct ww_acquire_ctx *ctx);
-
/**
* ww_mutex_lock - acquire the w/w mutex
* @lock: the mutex to be acquired
@@ -220,14 +215,7 @@
*
* A mutex acquired with this function must be released with ww_mutex_unlock.
*/
-static inline int ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
-{
- if (ctx)
- return __ww_mutex_lock(lock, ctx);
-
- mutex_lock(&lock->base);
- return 0;
-}
+extern int /* __must_check */ ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx);
/**
* ww_mutex_lock_interruptible - acquire the w/w mutex, interruptible
@@ -259,14 +247,8 @@
*
* A mutex acquired with this function must be released with ww_mutex_unlock.
*/
-static inline int __must_check ww_mutex_lock_interruptible(struct ww_mutex *lock,
- struct ww_acquire_ctx *ctx)
-{
- if (ctx)
- return __ww_mutex_lock_interruptible(lock, ctx);
- else
- return mutex_lock_interruptible(&lock->base);
-}
+extern int __must_check ww_mutex_lock_interruptible(struct ww_mutex *lock,
+ struct ww_acquire_ctx *ctx);
/**
* ww_mutex_lock_slow - slowpath acquiring of the w/w mutex
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 554671c..90708f6 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -987,7 +987,7 @@
static inline void hci_dev_put(struct hci_dev *d)
{
BT_DBG("%s orig refcnt %d", d->name,
- atomic_read(&d->dev.kobj.kref.refcount));
+ kref_read(&d->dev.kobj.kref));
put_device(&d->dev);
}
@@ -995,7 +995,7 @@
static inline struct hci_dev *hci_dev_hold(struct hci_dev *d)
{
BT_DBG("%s orig refcnt %d", d->name,
- atomic_read(&d->dev.kobj.kref.refcount));
+ kref_read(&d->dev.kobj.kref));
get_device(&d->dev);
return d;
diff --git a/include/soc/at91/at91sam9_ddrsdr.h b/include/soc/at91/at91sam9_ddrsdr.h
index dc10c52..393362b 100644
--- a/include/soc/at91/at91sam9_ddrsdr.h
+++ b/include/soc/at91/at91sam9_ddrsdr.h
@@ -81,6 +81,7 @@
#define AT91_DDRSDRC_LPCB_POWER_DOWN 2
#define AT91_DDRSDRC_LPCB_DEEP_POWER_DOWN 3
#define AT91_DDRSDRC_CLKFR (1 << 2) /* Clock Frozen */
+#define AT91_DDRSDRC_LPDDR2_PWOFF (1 << 3) /* LPDDR Power Off */
#define AT91_DDRSDRC_PASR (7 << 4) /* Partial Array Self Refresh */
#define AT91_DDRSDRC_TCSR (3 << 8) /* Temperature Compensated Self Refresh */
#define AT91_DDRSDRC_DS (3 << 10) /* Drive Strength */
@@ -96,7 +97,9 @@
#define AT91_DDRSDRC_MD_SDR 0
#define AT91_DDRSDRC_MD_LOW_POWER_SDR 1
#define AT91_DDRSDRC_MD_LOW_POWER_DDR 3
+#define AT91_DDRSDRC_MD_LPDDR3 5
#define AT91_DDRSDRC_MD_DDR2 6 /* [SAM9 Only] */
+#define AT91_DDRSDRC_MD_LPDDR2 7
#define AT91_DDRSDRC_DBW (1 << 4) /* Data Bus Width */
#define AT91_DDRSDRC_DBW_32BITS (0 << 4)
#define AT91_DDRSDRC_DBW_16BITS (1 << 4)
diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h
index 9d4f9b3..e3facb3 100644
--- a/include/trace/events/rcu.h
+++ b/include/trace/events/rcu.h
@@ -385,11 +385,11 @@
/*
* Tracepoint for quiescent states detected by force_quiescent_state().
- * These trace events include the type of RCU, the grace-period number
- * that was blocked by the CPU, the CPU itself, and the type of quiescent
- * state, which can be "dti" for dyntick-idle mode, "ofl" for CPU offline,
- * or "kick" when kicking a CPU that has been in dyntick-idle mode for
- * too long.
+ * These trace events include the type of RCU, the grace-period number that
+ * was blocked by the CPU, the CPU itself, and the type of quiescent state,
+ * which can be "dti" for dyntick-idle mode, "ofl" for CPU offline, "kick"
+ * when kicking a CPU that has been in dyntick-idle mode for too long, or
+ * "rqc" if the CPU got a quiescent state via its rcu_qs_ctr.
*/
TRACE_EVENT(rcu_fqs,
diff --git a/include/trace/events/timer.h b/include/trace/events/timer.h
index 1448637..1bca99d 100644
--- a/include/trace/events/timer.h
+++ b/include/trace/events/timer.h
@@ -269,17 +269,17 @@
TRACE_EVENT(itimer_state,
TP_PROTO(int which, const struct itimerval *const value,
- cputime_t expires),
+ unsigned long long expires),
TP_ARGS(which, value, expires),
TP_STRUCT__entry(
- __field( int, which )
- __field( cputime_t, expires )
- __field( long, value_sec )
- __field( long, value_usec )
- __field( long, interval_sec )
- __field( long, interval_usec )
+ __field( int, which )
+ __field( unsigned long long, expires )
+ __field( long, value_sec )
+ __field( long, value_usec )
+ __field( long, interval_sec )
+ __field( long, interval_usec )
),
TP_fast_assign(
@@ -292,7 +292,7 @@
),
TP_printk("which=%d expires=%llu it_value=%ld.%ld it_interval=%ld.%ld",
- __entry->which, (unsigned long long)__entry->expires,
+ __entry->which, __entry->expires,
__entry->value_sec, __entry->value_usec,
__entry->interval_sec, __entry->interval_usec)
);
@@ -305,14 +305,14 @@
*/
TRACE_EVENT(itimer_expire,
- TP_PROTO(int which, struct pid *pid, cputime_t now),
+ TP_PROTO(int which, struct pid *pid, unsigned long long now),
TP_ARGS(which, pid, now),
TP_STRUCT__entry(
- __field( int , which )
- __field( pid_t, pid )
- __field( cputime_t, now )
+ __field( int , which )
+ __field( pid_t, pid )
+ __field( unsigned long long, now )
),
TP_fast_assign(
@@ -322,7 +322,7 @@
),
TP_printk("which=%d pid=%d now=%llu", __entry->which,
- (int) __entry->pid, (unsigned long long)__entry->now)
+ (int) __entry->pid, __entry->now)
);
#ifdef CONFIG_NO_HZ_COMMON
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 0eb0e87..d2b0ac7 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -116,6 +116,12 @@
#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
+/* If BPF_F_ALLOW_OVERRIDE flag is used in BPF_PROG_ATTACH command
+ * to the given target_fd cgroup the descendent cgroup will be able to
+ * override effective bpf program that was inherited from this cgroup
+ */
+#define BPF_F_ALLOW_OVERRIDE (1U << 0)
+
#define BPF_PSEUDO_MAP_FD 1
/* flags for BPF_MAP_UPDATE_ELEM command */
@@ -171,6 +177,7 @@
__u32 target_fd; /* container object to attach to */
__u32 attach_bpf_fd; /* eBPF program to attach */
__u32 attach_type;
+ __u32 attach_flags;
};
} __attribute__((aligned(8)));
diff --git a/include/uapi/linux/l2tp.h b/include/uapi/linux/l2tp.h
index 85ddb74..b23c191 100644
--- a/include/uapi/linux/l2tp.h
+++ b/include/uapi/linux/l2tp.h
@@ -9,9 +9,8 @@
#include <linux/types.h>
#include <linux/socket.h>
-#ifndef __KERNEL__
-#include <netinet/in.h>
-#endif
+#include <linux/in.h>
+#include <linux/in6.h>
#define IPPROTO_L2TP 115
@@ -31,7 +30,7 @@
__u32 l2tp_conn_id; /* Connection ID of tunnel */
/* Pad to size of `struct sockaddr'. */
- unsigned char __pad[sizeof(struct sockaddr) -
+ unsigned char __pad[__SOCK_SIZE__ -
sizeof(__kernel_sa_family_t) -
sizeof(__be16) - sizeof(struct in_addr) -
sizeof(__u32)];
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 46e8a2e3..45184a2 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -362,8 +362,8 @@
/*
* The default for R'G'B' quantization is always full range, except
* for the BT2020 colorspace. For Y'CbCr the quantization is always
- * limited range, except for COLORSPACE_JPEG, SRGB, ADOBERGB,
- * XV601 or XV709: those are full range.
+ * limited range, except for COLORSPACE_JPEG, XV601 or XV709: those
+ * are full range.
*/
V4L2_QUANTIZATION_DEFAULT = 0,
V4L2_QUANTIZATION_FULL_RANGE = 1,
@@ -379,8 +379,7 @@
(((is_rgb_or_hsv) && (colsp) == V4L2_COLORSPACE_BT2020) ? \
V4L2_QUANTIZATION_LIM_RANGE : \
(((is_rgb_or_hsv) || (ycbcr_enc) == V4L2_YCBCR_ENC_XV601 || \
- (ycbcr_enc) == V4L2_YCBCR_ENC_XV709 || (colsp) == V4L2_COLORSPACE_JPEG) || \
- (colsp) == V4L2_COLORSPACE_ADOBERGB || (colsp) == V4L2_COLORSPACE_SRGB ? \
+ (ycbcr_enc) == V4L2_YCBCR_ENC_XV709 || (colsp) == V4L2_COLORSPACE_JPEG) ? \
V4L2_QUANTIZATION_FULL_RANGE : V4L2_QUANTIZATION_LIM_RANGE))
enum v4l2_priority {
diff --git a/init/Kconfig b/init/Kconfig
index 4dd8bd2..2655abb 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -529,7 +529,6 @@
config TASKS_RCU
bool
default n
- depends on !UML
select SRCU
help
This option enables a task-based RCU implementation that uses
@@ -781,19 +780,6 @@
endchoice
-config RCU_EXPEDITE_BOOT
- bool
- default n
- help
- This option enables expedited grace periods at boot time,
- as if rcu_expedite_gp() had been invoked early in boot.
- The corresponding rcu_unexpedite_gp() is invoked from
- rcu_end_inkernel_boot(), which is intended to be invoked
- at the end of the kernel-only boot sequence, just before
- init is exec'ed.
-
- Accept the default if unsure.
-
endmenu # "RCU Subsystem"
config BUILD_BIN2C
diff --git a/init/main.c b/init/main.c
index b0c9d6f..6ced14a 100644
--- a/init/main.c
+++ b/init/main.c
@@ -625,7 +625,6 @@
numa_policy_init();
if (late_time_init)
late_time_init();
- sched_clock_init();
calibrate_delay();
pidmap_init();
anon_vma_init();
@@ -663,7 +662,6 @@
sfi_init_late();
if (efi_enabled(EFI_RUNTIME_SERVICES)) {
- efi_late_init();
efi_free_boot_services();
}
diff --git a/init/version.c b/init/version.c
index fe41a63..5606341 100644
--- a/init/version.c
+++ b/init/version.c
@@ -23,9 +23,7 @@
#endif
struct uts_namespace init_uts_ns = {
- .kref = {
- .refcount = ATOMIC_INIT(2),
- },
+ .kref = KREF_INIT(2),
.name = {
.sysname = UTS_SYSNAME,
.nodename = UTS_NODENAME,
diff --git a/kernel/acct.c b/kernel/acct.c
index 74963d1..ca9cb55 100644
--- a/kernel/acct.c
+++ b/kernel/acct.c
@@ -453,8 +453,8 @@
spin_lock_irq(¤t->sighand->siglock);
tty = current->signal->tty; /* Safe as we hold the siglock */
ac->ac_tty = tty ? old_encode_dev(tty_devnum(tty)) : 0;
- ac->ac_utime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_utime)));
- ac->ac_stime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_stime)));
+ ac->ac_utime = encode_comp_t(nsec_to_AHZ(pacct->ac_utime));
+ ac->ac_stime = encode_comp_t(nsec_to_AHZ(pacct->ac_stime));
ac->ac_flag = pacct->ac_flag;
ac->ac_mem = encode_comp_t(pacct->ac_mem);
ac->ac_minflt = encode_comp_t(pacct->ac_minflt);
@@ -530,7 +530,7 @@
void acct_collect(long exitcode, int group_dead)
{
struct pacct_struct *pacct = ¤t->signal->pacct;
- cputime_t utime, stime;
+ u64 utime, stime;
unsigned long vsize = 0;
if (group_dead && current->mm) {
@@ -559,6 +559,7 @@
pacct->ac_flag |= ACORE;
if (current->flags & PF_SIGNALED)
pacct->ac_flag |= AXSIG;
+
task_cputime(current, &utime, &stime);
pacct->ac_utime += utime;
pacct->ac_stime += stime;
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
index a515f7b..da0f536 100644
--- a/kernel/bpf/cgroup.c
+++ b/kernel/bpf/cgroup.c
@@ -52,6 +52,7 @@
e = rcu_dereference_protected(parent->bpf.effective[type],
lockdep_is_held(&cgroup_mutex));
rcu_assign_pointer(cgrp->bpf.effective[type], e);
+ cgrp->bpf.disallow_override[type] = parent->bpf.disallow_override[type];
}
}
@@ -82,30 +83,63 @@
*
* Must be called with cgroup_mutex held.
*/
-void __cgroup_bpf_update(struct cgroup *cgrp,
- struct cgroup *parent,
- struct bpf_prog *prog,
- enum bpf_attach_type type)
+int __cgroup_bpf_update(struct cgroup *cgrp, struct cgroup *parent,
+ struct bpf_prog *prog, enum bpf_attach_type type,
+ bool new_overridable)
{
- struct bpf_prog *old_prog, *effective;
+ struct bpf_prog *old_prog, *effective = NULL;
struct cgroup_subsys_state *pos;
+ bool overridable = true;
- old_prog = xchg(cgrp->bpf.prog + type, prog);
+ if (parent) {
+ overridable = !parent->bpf.disallow_override[type];
+ effective = rcu_dereference_protected(parent->bpf.effective[type],
+ lockdep_is_held(&cgroup_mutex));
+ }
- effective = (!prog && parent) ?
- rcu_dereference_protected(parent->bpf.effective[type],
- lockdep_is_held(&cgroup_mutex)) :
- prog;
+ if (prog && effective && !overridable)
+ /* if parent has non-overridable prog attached, disallow
+ * attaching new programs to descendent cgroup
+ */
+ return -EPERM;
+
+ if (prog && effective && overridable != new_overridable)
+ /* if parent has overridable prog attached, only
+ * allow overridable programs in descendent cgroup
+ */
+ return -EPERM;
+
+ old_prog = cgrp->bpf.prog[type];
+
+ if (prog) {
+ overridable = new_overridable;
+ effective = prog;
+ if (old_prog &&
+ cgrp->bpf.disallow_override[type] == new_overridable)
+ /* disallow attaching non-overridable on top
+ * of existing overridable in this cgroup
+ * and vice versa
+ */
+ return -EPERM;
+ }
+
+ if (!prog && !old_prog)
+ /* report error when trying to detach and nothing is attached */
+ return -ENOENT;
+
+ cgrp->bpf.prog[type] = prog;
css_for_each_descendant_pre(pos, &cgrp->self) {
struct cgroup *desc = container_of(pos, struct cgroup, self);
/* skip the subtree if the descendant has its own program */
- if (desc->bpf.prog[type] && desc != cgrp)
+ if (desc->bpf.prog[type] && desc != cgrp) {
pos = css_rightmost_descendant(pos);
- else
+ } else {
rcu_assign_pointer(desc->bpf.effective[type],
effective);
+ desc->bpf.disallow_override[type] = !overridable;
+ }
}
if (prog)
@@ -115,6 +149,7 @@
bpf_prog_put(old_prog);
static_branch_dec(&cgroup_bpf_enabled_key);
}
+ return 0;
}
/**
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 19b6129..bbb016a 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -920,13 +920,14 @@
#ifdef CONFIG_CGROUP_BPF
-#define BPF_PROG_ATTACH_LAST_FIELD attach_type
+#define BPF_PROG_ATTACH_LAST_FIELD attach_flags
static int bpf_prog_attach(const union bpf_attr *attr)
{
+ enum bpf_prog_type ptype;
struct bpf_prog *prog;
struct cgroup *cgrp;
- enum bpf_prog_type ptype;
+ int ret;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
@@ -934,6 +935,9 @@
if (CHECK_ATTR(BPF_PROG_ATTACH))
return -EINVAL;
+ if (attr->attach_flags & ~BPF_F_ALLOW_OVERRIDE)
+ return -EINVAL;
+
switch (attr->attach_type) {
case BPF_CGROUP_INET_INGRESS:
case BPF_CGROUP_INET_EGRESS:
@@ -956,10 +960,13 @@
return PTR_ERR(cgrp);
}
- cgroup_bpf_update(cgrp, prog, attr->attach_type);
+ ret = cgroup_bpf_update(cgrp, prog, attr->attach_type,
+ attr->attach_flags & BPF_F_ALLOW_OVERRIDE);
+ if (ret)
+ bpf_prog_put(prog);
cgroup_put(cgrp);
- return 0;
+ return ret;
}
#define BPF_PROG_DETACH_LAST_FIELD attach_type
@@ -967,6 +974,7 @@
static int bpf_prog_detach(const union bpf_attr *attr)
{
struct cgroup *cgrp;
+ int ret;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
@@ -982,7 +990,7 @@
if (IS_ERR(cgrp))
return PTR_ERR(cgrp);
- cgroup_bpf_update(cgrp, NULL, attr->attach_type);
+ ret = cgroup_bpf_update(cgrp, NULL, attr->attach_type, false);
cgroup_put(cgrp);
break;
@@ -990,7 +998,7 @@
return -EINVAL;
}
- return 0;
+ return ret;
}
#endif /* CONFIG_CGROUP_BPF */
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 688dd02..53bbca7 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -6498,15 +6498,16 @@
subsys_initcall(cgroup_namespaces_init);
#ifdef CONFIG_CGROUP_BPF
-void cgroup_bpf_update(struct cgroup *cgrp,
- struct bpf_prog *prog,
- enum bpf_attach_type type)
+int cgroup_bpf_update(struct cgroup *cgrp, struct bpf_prog *prog,
+ enum bpf_attach_type type, bool overridable)
{
struct cgroup *parent = cgroup_parent(cgrp);
+ int ret;
mutex_lock(&cgroup_mutex);
- __cgroup_bpf_update(cgrp, parent, prog, type);
+ ret = __cgroup_bpf_update(cgrp, parent, prog, type, overridable);
mutex_unlock(&cgroup_mutex);
+ return ret;
}
#endif /* CONFIG_CGROUP_BPF */
diff --git a/kernel/delayacct.c b/kernel/delayacct.c
index 435c14a..6605496 100644
--- a/kernel/delayacct.c
+++ b/kernel/delayacct.c
@@ -82,19 +82,19 @@
int __delayacct_add_tsk(struct taskstats *d, struct task_struct *tsk)
{
- cputime_t utime, stime, stimescaled, utimescaled;
+ u64 utime, stime, stimescaled, utimescaled;
unsigned long long t2, t3;
unsigned long flags, t1;
s64 tmp;
task_cputime(tsk, &utime, &stime);
tmp = (s64)d->cpu_run_real_total;
- tmp += cputime_to_nsecs(utime + stime);
+ tmp += utime + stime;
d->cpu_run_real_total = (tmp < (s64)d->cpu_run_real_total) ? 0 : tmp;
task_cputime_scaled(tsk, &utimescaled, &stimescaled);
tmp = (s64)d->cpu_scaled_run_real_total;
- tmp += cputime_to_nsecs(utimescaled + stimescaled);
+ tmp += utimescaled + stimescaled;
d->cpu_scaled_run_real_total =
(tmp < (s64)d->cpu_scaled_run_real_total) ? 0 : tmp;
diff --git a/kernel/events/core.c b/kernel/events/core.c
index e235bb9..77a932b 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -355,6 +355,8 @@
EVENT_FLEXIBLE = 0x1,
EVENT_PINNED = 0x2,
EVENT_TIME = 0x4,
+ /* see ctx_resched() for details */
+ EVENT_CPU = 0x8,
EVENT_ALL = EVENT_FLEXIBLE | EVENT_PINNED,
};
@@ -678,6 +680,8 @@
info->timestamp = ctx->timestamp;
}
+static DEFINE_PER_CPU(struct list_head, cgrp_cpuctx_list);
+
#define PERF_CGROUP_SWOUT 0x1 /* cgroup switch out every event */
#define PERF_CGROUP_SWIN 0x2 /* cgroup switch in events based on task */
@@ -690,61 +694,46 @@
static void perf_cgroup_switch(struct task_struct *task, int mode)
{
struct perf_cpu_context *cpuctx;
- struct pmu *pmu;
+ struct list_head *list;
unsigned long flags;
/*
- * disable interrupts to avoid geting nr_cgroup
- * changes via __perf_event_disable(). Also
- * avoids preemption.
+ * Disable interrupts and preemption to avoid this CPU's
+ * cgrp_cpuctx_entry to change under us.
*/
local_irq_save(flags);
- /*
- * we reschedule only in the presence of cgroup
- * constrained events.
- */
+ list = this_cpu_ptr(&cgrp_cpuctx_list);
+ list_for_each_entry(cpuctx, list, cgrp_cpuctx_entry) {
+ WARN_ON_ONCE(cpuctx->ctx.nr_cgroups == 0);
- list_for_each_entry_rcu(pmu, &pmus, entry) {
- cpuctx = this_cpu_ptr(pmu->pmu_cpu_context);
- if (cpuctx->unique_pmu != pmu)
- continue; /* ensure we process each cpuctx once */
+ perf_ctx_lock(cpuctx, cpuctx->task_ctx);
+ perf_pmu_disable(cpuctx->ctx.pmu);
- /*
- * perf_cgroup_events says at least one
- * context on this CPU has cgroup events.
- *
- * ctx->nr_cgroups reports the number of cgroup
- * events for a context.
- */
- if (cpuctx->ctx.nr_cgroups > 0) {
- perf_ctx_lock(cpuctx, cpuctx->task_ctx);
- perf_pmu_disable(cpuctx->ctx.pmu);
-
- if (mode & PERF_CGROUP_SWOUT) {
- cpu_ctx_sched_out(cpuctx, EVENT_ALL);
- /*
- * must not be done before ctxswout due
- * to event_filter_match() in event_sched_out()
- */
- cpuctx->cgrp = NULL;
- }
-
- if (mode & PERF_CGROUP_SWIN) {
- WARN_ON_ONCE(cpuctx->cgrp);
- /*
- * set cgrp before ctxsw in to allow
- * event_filter_match() to not have to pass
- * task around
- * we pass the cpuctx->ctx to perf_cgroup_from_task()
- * because cgorup events are only per-cpu
- */
- cpuctx->cgrp = perf_cgroup_from_task(task, &cpuctx->ctx);
- cpu_ctx_sched_in(cpuctx, EVENT_ALL, task);
- }
- perf_pmu_enable(cpuctx->ctx.pmu);
- perf_ctx_unlock(cpuctx, cpuctx->task_ctx);
+ if (mode & PERF_CGROUP_SWOUT) {
+ cpu_ctx_sched_out(cpuctx, EVENT_ALL);
+ /*
+ * must not be done before ctxswout due
+ * to event_filter_match() in event_sched_out()
+ */
+ cpuctx->cgrp = NULL;
}
+
+ if (mode & PERF_CGROUP_SWIN) {
+ WARN_ON_ONCE(cpuctx->cgrp);
+ /*
+ * set cgrp before ctxsw in to allow
+ * event_filter_match() to not have to pass
+ * task around
+ * we pass the cpuctx->ctx to perf_cgroup_from_task()
+ * because cgorup events are only per-cpu
+ */
+ cpuctx->cgrp = perf_cgroup_from_task(task,
+ &cpuctx->ctx);
+ cpu_ctx_sched_in(cpuctx, EVENT_ALL, task);
+ }
+ perf_pmu_enable(cpuctx->ctx.pmu);
+ perf_ctx_unlock(cpuctx, cpuctx->task_ctx);
}
local_irq_restore(flags);
@@ -889,6 +878,7 @@
struct perf_event_context *ctx, bool add)
{
struct perf_cpu_context *cpuctx;
+ struct list_head *cpuctx_entry;
if (!is_cgroup_event(event))
return;
@@ -902,15 +892,16 @@
* this will always be called from the right CPU.
*/
cpuctx = __get_cpu_context(ctx);
-
- /*
- * cpuctx->cgrp is NULL until a cgroup event is sched in or
- * ctx->nr_cgroup == 0 .
- */
- if (add && perf_cgroup_from_task(current, ctx) == event->cgrp)
- cpuctx->cgrp = event->cgrp;
- else if (!add)
+ cpuctx_entry = &cpuctx->cgrp_cpuctx_entry;
+ /* cpuctx->cgrp is NULL unless a cgroup event is active in this CPU .*/
+ if (add) {
+ list_add(cpuctx_entry, this_cpu_ptr(&cgrp_cpuctx_list));
+ if (perf_cgroup_from_task(current, ctx) == event->cgrp)
+ cpuctx->cgrp = event->cgrp;
+ } else {
+ list_del(cpuctx_entry);
cpuctx->cgrp = NULL;
+ }
}
#else /* !CONFIG_CGROUP_PERF */
@@ -1453,6 +1444,20 @@
update_event_times(event);
}
+static enum event_type_t get_event_type(struct perf_event *event)
+{
+ struct perf_event_context *ctx = event->ctx;
+ enum event_type_t event_type;
+
+ lockdep_assert_held(&ctx->lock);
+
+ event_type = event->attr.pinned ? EVENT_PINNED : EVENT_FLEXIBLE;
+ if (!ctx->task)
+ event_type |= EVENT_CPU;
+
+ return event_type;
+}
+
static struct list_head *
ctx_group_list(struct perf_event *event, struct perf_event_context *ctx)
{
@@ -2226,7 +2231,8 @@
struct task_struct *task);
static void task_ctx_sched_out(struct perf_cpu_context *cpuctx,
- struct perf_event_context *ctx)
+ struct perf_event_context *ctx,
+ enum event_type_t event_type)
{
if (!cpuctx->task_ctx)
return;
@@ -2234,7 +2240,7 @@
if (WARN_ON_ONCE(ctx != cpuctx->task_ctx))
return;
- ctx_sched_out(ctx, cpuctx, EVENT_ALL);
+ ctx_sched_out(ctx, cpuctx, event_type);
}
static void perf_event_sched_in(struct perf_cpu_context *cpuctx,
@@ -2249,13 +2255,51 @@
ctx_sched_in(ctx, cpuctx, EVENT_FLEXIBLE, task);
}
+/*
+ * We want to maintain the following priority of scheduling:
+ * - CPU pinned (EVENT_CPU | EVENT_PINNED)
+ * - task pinned (EVENT_PINNED)
+ * - CPU flexible (EVENT_CPU | EVENT_FLEXIBLE)
+ * - task flexible (EVENT_FLEXIBLE).
+ *
+ * In order to avoid unscheduling and scheduling back in everything every
+ * time an event is added, only do it for the groups of equal priority and
+ * below.
+ *
+ * This can be called after a batch operation on task events, in which case
+ * event_type is a bit mask of the types of events involved. For CPU events,
+ * event_type is only either EVENT_PINNED or EVENT_FLEXIBLE.
+ */
static void ctx_resched(struct perf_cpu_context *cpuctx,
- struct perf_event_context *task_ctx)
+ struct perf_event_context *task_ctx,
+ enum event_type_t event_type)
{
+ enum event_type_t ctx_event_type = event_type & EVENT_ALL;
+ bool cpu_event = !!(event_type & EVENT_CPU);
+
+ /*
+ * If pinned groups are involved, flexible groups also need to be
+ * scheduled out.
+ */
+ if (event_type & EVENT_PINNED)
+ event_type |= EVENT_FLEXIBLE;
+
perf_pmu_disable(cpuctx->ctx.pmu);
if (task_ctx)
- task_ctx_sched_out(cpuctx, task_ctx);
- cpu_ctx_sched_out(cpuctx, EVENT_ALL);
+ task_ctx_sched_out(cpuctx, task_ctx, event_type);
+
+ /*
+ * Decide which cpu ctx groups to schedule out based on the types
+ * of events that caused rescheduling:
+ * - EVENT_CPU: schedule out corresponding groups;
+ * - EVENT_PINNED task events: schedule out EVENT_FLEXIBLE groups;
+ * - otherwise, do nothing more.
+ */
+ if (cpu_event)
+ cpu_ctx_sched_out(cpuctx, ctx_event_type);
+ else if (ctx_event_type & EVENT_PINNED)
+ cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE);
+
perf_event_sched_in(cpuctx, task_ctx, current);
perf_pmu_enable(cpuctx->ctx.pmu);
}
@@ -2302,7 +2346,7 @@
if (reprogram) {
ctx_sched_out(ctx, cpuctx, EVENT_TIME);
add_event_to_ctx(event, ctx);
- ctx_resched(cpuctx, task_ctx);
+ ctx_resched(cpuctx, task_ctx, get_event_type(event));
} else {
add_event_to_ctx(event, ctx);
}
@@ -2469,7 +2513,7 @@
if (ctx->task)
WARN_ON_ONCE(task_ctx != ctx);
- ctx_resched(cpuctx, task_ctx);
+ ctx_resched(cpuctx, task_ctx, get_event_type(event));
}
/*
@@ -2896,7 +2940,7 @@
if (do_switch) {
raw_spin_lock(&ctx->lock);
- task_ctx_sched_out(cpuctx, ctx);
+ task_ctx_sched_out(cpuctx, ctx, EVENT_ALL);
raw_spin_unlock(&ctx->lock);
}
}
@@ -2943,7 +2987,7 @@
return;
list_for_each_entry(cpuctx, this_cpu_ptr(&sched_cb_list), sched_cb_entry) {
- pmu = cpuctx->unique_pmu; /* software PMUs will not have sched_task */
+ pmu = cpuctx->ctx.pmu; /* software PMUs will not have sched_task */
if (WARN_ON_ONCE(!pmu->sched_task))
continue;
@@ -3133,8 +3177,12 @@
* We want to keep the following priority order:
* cpu pinned (that don't need to move), task pinned,
* cpu flexible, task flexible.
+ *
+ * However, if task's ctx is not carrying any pinned
+ * events, no need to flip the cpuctx's events around.
*/
- cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE);
+ if (!list_empty(&ctx->pinned_groups))
+ cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE);
perf_event_sched_in(cpuctx, ctx, task);
perf_pmu_enable(ctx->pmu);
perf_ctx_unlock(cpuctx, ctx);
@@ -3449,6 +3497,7 @@
static void perf_event_enable_on_exec(int ctxn)
{
struct perf_event_context *ctx, *clone_ctx = NULL;
+ enum event_type_t event_type = 0;
struct perf_cpu_context *cpuctx;
struct perf_event *event;
unsigned long flags;
@@ -3462,15 +3511,17 @@
cpuctx = __get_cpu_context(ctx);
perf_ctx_lock(cpuctx, ctx);
ctx_sched_out(ctx, cpuctx, EVENT_TIME);
- list_for_each_entry(event, &ctx->event_list, event_entry)
+ list_for_each_entry(event, &ctx->event_list, event_entry) {
enabled |= event_enable_on_exec(event, ctx);
+ event_type |= get_event_type(event);
+ }
/*
* Unclone and reschedule this context if we enabled any event.
*/
if (enabled) {
clone_ctx = unclone_ctx(ctx);
- ctx_resched(cpuctx, ctx);
+ ctx_resched(cpuctx, ctx, event_type);
}
perf_ctx_unlock(cpuctx, ctx);
@@ -8044,6 +8095,9 @@
if (task == TASK_TOMBSTONE)
return;
+ if (!ifh->nr_file_filters)
+ return;
+
mm = get_task_mm(event->ctx->task);
if (!mm)
goto restart;
@@ -8214,6 +8268,7 @@
* attribute.
*/
if (state == IF_STATE_END) {
+ ret = -EINVAL;
if (kernel && event->attr.exclude_kernel)
goto fail;
@@ -8221,6 +8276,18 @@
if (!filename)
goto fail;
+ /*
+ * For now, we only support file-based filters
+ * in per-task events; doing so for CPU-wide
+ * events requires additional context switching
+ * trickery, since same object code will be
+ * mapped at different virtual addresses in
+ * different processes.
+ */
+ ret = -EOPNOTSUPP;
+ if (!event->ctx->task)
+ goto fail_free_name;
+
/* look up the path and grab its inode */
ret = kern_path(filename, LOOKUP_FOLLOW, &path);
if (ret)
@@ -8236,6 +8303,8 @@
!S_ISREG(filter->inode->i_mode))
/* free_filters_list() will iput() */
goto fail;
+
+ event->addr_filters.nr_file_filters++;
}
/* ready to consume more filters */
@@ -8275,24 +8344,13 @@
if (WARN_ON_ONCE(event->parent))
return -EINVAL;
- /*
- * For now, we only support filtering in per-task events; doing so
- * for CPU-wide events requires additional context switching trickery,
- * since same object code will be mapped at different virtual
- * addresses in different processes.
- */
- if (!event->ctx->task)
- return -EOPNOTSUPP;
-
ret = perf_event_parse_addr_filter(event, filter_str, &filters);
if (ret)
- return ret;
+ goto fail_clear_files;
ret = event->pmu->addr_filters_validate(&filters);
- if (ret) {
- free_filters_list(&filters);
- return ret;
- }
+ if (ret)
+ goto fail_free_filters;
/* remove existing filters, if any */
perf_addr_filters_splice(event, &filters);
@@ -8301,6 +8359,14 @@
perf_event_for_each_child(event, perf_event_addr_filters_apply);
return ret;
+
+fail_free_filters:
+ free_filters_list(&filters);
+
+fail_clear_files:
+ event->addr_filters.nr_file_filters = 0;
+
+ return ret;
}
static int perf_event_set_filter(struct perf_event *event, void __user *arg)
@@ -8652,37 +8718,10 @@
return NULL;
}
-static void update_pmu_context(struct pmu *pmu, struct pmu *old_pmu)
-{
- int cpu;
-
- for_each_possible_cpu(cpu) {
- struct perf_cpu_context *cpuctx;
-
- cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu);
-
- if (cpuctx->unique_pmu == old_pmu)
- cpuctx->unique_pmu = pmu;
- }
-}
-
static void free_pmu_context(struct pmu *pmu)
{
- struct pmu *i;
-
mutex_lock(&pmus_lock);
- /*
- * Like a real lame refcount.
- */
- list_for_each_entry(i, &pmus, entry) {
- if (i->pmu_cpu_context == pmu->pmu_cpu_context) {
- update_pmu_context(i, pmu);
- goto out;
- }
- }
-
free_percpu(pmu->pmu_cpu_context);
-out:
mutex_unlock(&pmus_lock);
}
@@ -8886,8 +8925,6 @@
cpuctx->ctx.pmu = pmu;
__perf_mux_hrtimer_init(cpuctx, cpu);
-
- cpuctx->unique_pmu = pmu;
}
got_cpu_context:
@@ -9005,6 +9042,14 @@
idx = srcu_read_lock(&pmus_srcu);
+ /* Try parent's PMU first: */
+ if (event->parent && event->parent->pmu) {
+ pmu = event->parent->pmu;
+ ret = perf_try_init_event(pmu, event);
+ if (!ret)
+ goto unlock;
+ }
+
rcu_read_lock();
pmu = idr_find(&pmu_idr, event->attr.type);
rcu_read_unlock();
@@ -10265,7 +10310,7 @@
* in.
*/
raw_spin_lock_irq(&child_ctx->lock);
- task_ctx_sched_out(__get_cpu_context(child_ctx), child_ctx);
+ task_ctx_sched_out(__get_cpu_context(child_ctx), child_ctx, EVENT_ALL);
/*
* Now that the context is inactive, destroy the task <-> ctx relation
@@ -10714,6 +10759,9 @@
INIT_LIST_HEAD(&per_cpu(pmu_sb_events.list, cpu));
raw_spin_lock_init(&per_cpu(pmu_sb_events.lock, cpu));
+#ifdef CONFIG_CGROUP_PERF
+ INIT_LIST_HEAD(&per_cpu(cgrp_cpuctx_list, cpu));
+#endif
INIT_LIST_HEAD(&per_cpu(sched_cb_list, cpu));
}
}
diff --git a/kernel/exit.c b/kernel/exit.c
index 8f14b86..b67c57f 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -55,6 +55,7 @@
#include <linux/shm.h>
#include <linux/kcov.h>
#include <linux/random.h>
+#include <linux/rcuwait.h>
#include <linux/uaccess.h>
#include <asm/unistd.h>
@@ -86,7 +87,7 @@
bool group_dead = thread_group_leader(tsk);
struct sighand_struct *sighand;
struct tty_struct *uninitialized_var(tty);
- cputime_t utime, stime;
+ u64 utime, stime;
sighand = rcu_dereference_check(tsk->sighand,
lockdep_tasklist_lock_is_held());
@@ -282,6 +283,35 @@
return task;
}
+void rcuwait_wake_up(struct rcuwait *w)
+{
+ struct task_struct *task;
+
+ rcu_read_lock();
+
+ /*
+ * Order condition vs @task, such that everything prior to the load
+ * of @task is visible. This is the condition as to why the user called
+ * rcuwait_trywake() in the first place. Pairs with set_current_state()
+ * barrier (A) in rcuwait_wait_event().
+ *
+ * WAIT WAKE
+ * [S] tsk = current [S] cond = true
+ * MB (A) MB (B)
+ * [L] cond [L] tsk
+ */
+ smp_rmb(); /* (B) */
+
+ /*
+ * Avoid using task_rcu_dereference() magic as long as we are careful,
+ * see comment in rcuwait_wait_event() regarding ->exit_state.
+ */
+ task = rcu_dereference(w->task);
+ if (task)
+ wake_up_process(task);
+ rcu_read_unlock();
+}
+
struct task_struct *try_get_task_struct(struct task_struct **ptask)
{
struct task_struct *task;
@@ -468,12 +498,12 @@
* Turn us into a lazy TLB process if we
* aren't already..
*/
-static void exit_mm(struct task_struct *tsk)
+static void exit_mm(void)
{
- struct mm_struct *mm = tsk->mm;
+ struct mm_struct *mm = current->mm;
struct core_state *core_state;
- mm_release(tsk, mm);
+ mm_release(current, mm);
if (!mm)
return;
sync_mm_rss(mm);
@@ -491,7 +521,7 @@
up_read(&mm->mmap_sem);
- self.task = tsk;
+ self.task = current;
self.next = xchg(&core_state->dumper.next, &self);
/*
* Implies mb(), the result of xchg() must be visible
@@ -501,22 +531,22 @@
complete(&core_state->startup);
for (;;) {
- set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ set_current_state(TASK_UNINTERRUPTIBLE);
if (!self.task) /* see coredump_finish() */
break;
freezable_schedule();
}
- __set_task_state(tsk, TASK_RUNNING);
+ __set_current_state(TASK_RUNNING);
down_read(&mm->mmap_sem);
}
atomic_inc(&mm->mm_count);
- BUG_ON(mm != tsk->active_mm);
+ BUG_ON(mm != current->active_mm);
/* more a memory barrier than a real lock */
- task_lock(tsk);
- tsk->mm = NULL;
+ task_lock(current);
+ current->mm = NULL;
up_read(&mm->mmap_sem);
enter_lazy_tlb(mm, current);
- task_unlock(tsk);
+ task_unlock(current);
mm_update_next_owner(mm);
mmput(mm);
if (test_thread_flag(TIF_MEMDIE))
@@ -823,7 +853,7 @@
tsk->exit_code = code;
taskstats_exit(tsk, group_dead);
- exit_mm(tsk);
+ exit_mm();
if (group_dead)
acct_process();
@@ -1091,7 +1121,7 @@
struct signal_struct *sig = p->signal;
struct signal_struct *psig = current->signal;
unsigned long maxrss;
- cputime_t tgutime, tgstime;
+ u64 tgutime, tgstime;
/*
* The resource counters for the group leader are in its
diff --git a/kernel/extable.c b/kernel/extable.c
index e3beec4..e135947 100644
--- a/kernel/extable.c
+++ b/kernel/extable.c
@@ -20,6 +20,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/init.h>
+#include <linux/kprobes.h>
#include <asm/sections.h>
#include <linux/uaccess.h>
@@ -104,6 +105,8 @@
return 1;
if (is_ftrace_trampoline(addr))
return 1;
+ if (is_kprobe_optinsn_slot(addr) || is_kprobe_insn_slot(addr))
+ return 1;
/*
* There might be init symbols in saved stacktraces.
* Give those symbols a chance to be printed in
@@ -123,7 +126,11 @@
return 1;
if (is_module_text_address(addr))
return 1;
- return is_ftrace_trampoline(addr);
+ if (is_ftrace_trampoline(addr))
+ return 1;
+ if (is_kprobe_optinsn_slot(addr) || is_kprobe_insn_slot(addr))
+ return 1;
+ return 0;
}
/*
diff --git a/kernel/fork.c b/kernel/fork.c
index 11c5c8a..ff82e24 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -432,11 +432,13 @@
int i;
#ifndef CONFIG_ARCH_TASK_STRUCT_ALLOCATOR
#ifndef ARCH_MIN_TASKALIGN
-#define ARCH_MIN_TASKALIGN L1_CACHE_BYTES
+#define ARCH_MIN_TASKALIGN 0
#endif
+ int align = max_t(int, L1_CACHE_BYTES, ARCH_MIN_TASKALIGN);
+
/* create a slab on which task_structs can be allocated */
task_struct_cachep = kmem_cache_create("task_struct",
- arch_task_struct_size, ARCH_MIN_TASKALIGN,
+ arch_task_struct_size, align,
SLAB_PANIC|SLAB_NOTRACK|SLAB_ACCOUNT, NULL);
#endif
@@ -1304,6 +1306,7 @@
}
}
+#ifdef CONFIG_POSIX_TIMERS
/*
* Initialize POSIX timer handling for a thread group.
*/
@@ -1313,7 +1316,7 @@
cpu_limit = READ_ONCE(sig->rlim[RLIMIT_CPU].rlim_cur);
if (cpu_limit != RLIM_INFINITY) {
- sig->cputime_expires.prof_exp = secs_to_cputime(cpu_limit);
+ sig->cputime_expires.prof_exp = cpu_limit * NSEC_PER_SEC;
sig->cputimer.running = true;
}
@@ -1322,6 +1325,9 @@
INIT_LIST_HEAD(&sig->cpu_timers[1]);
INIT_LIST_HEAD(&sig->cpu_timers[2]);
}
+#else
+static inline void posix_cpu_timers_init_group(struct signal_struct *sig) { }
+#endif
static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
{
@@ -1346,11 +1352,11 @@
init_waitqueue_head(&sig->wait_chldexit);
sig->curr_target = tsk;
init_sigpending(&sig->shared_pending);
- INIT_LIST_HEAD(&sig->posix_timers);
seqlock_init(&sig->stats_lock);
prev_cputime_init(&sig->prev_cputime);
#ifdef CONFIG_POSIX_TIMERS
+ INIT_LIST_HEAD(&sig->posix_timers);
hrtimer_init(&sig->real_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
sig->real_timer.function = it_real_fn;
#endif
@@ -1425,6 +1431,7 @@
#endif
}
+#ifdef CONFIG_POSIX_TIMERS
/*
* Initialize POSIX timer handling for a single task.
*/
@@ -1437,6 +1444,9 @@
INIT_LIST_HEAD(&tsk->cpu_timers[1]);
INIT_LIST_HEAD(&tsk->cpu_timers[2]);
}
+#else
+static inline void posix_cpu_timers_init(struct task_struct *tsk) { }
+#endif
static inline void
init_task_pid(struct task_struct *task, enum pid_type type, struct pid *pid)
diff --git a/kernel/futex.c b/kernel/futex.c
index 0842c8ca..cdf3650 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -3323,4 +3323,4 @@
return 0;
}
-__initcall(futex_init);
+core_initcall(futex_init);
diff --git a/kernel/irq/devres.c b/kernel/irq/devres.c
index 74d90a7..1613bfd 100644
--- a/kernel/irq/devres.c
+++ b/kernel/irq/devres.c
@@ -2,6 +2,7 @@
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/gfp.h>
+#include <linux/irq.h>
/*
* Device resource management aware IRQ request/free implementation.
@@ -33,7 +34,7 @@
* @thread_fn: function to be called in a threaded interrupt context. NULL
* for devices which handle everything in @handler
* @irqflags: Interrupt type flags
- * @devname: An ascii name for the claiming device
+ * @devname: An ascii name for the claiming device, dev_name(dev) if NULL
* @dev_id: A cookie passed back to the handler function
*
* Except for the extra @dev argument, this function takes the
@@ -57,6 +58,9 @@
if (!dr)
return -ENOMEM;
+ if (!devname)
+ devname = dev_name(dev);
+
rc = request_threaded_irq(irq, handler, thread_fn, irqflags, devname,
dev_id);
if (rc) {
@@ -80,7 +84,7 @@
* @thread_fn: function to be called in a threaded interrupt context. NULL
* for devices which handle everything in @handler
* @irqflags: Interrupt type flags
- * @devname: An ascii name for the claiming device
+ * @devname: An ascii name for the claiming device, dev_name(dev) if NULL
* @dev_id: A cookie passed back to the handler function
*
* Except for the extra @dev argument, this function takes the
@@ -103,6 +107,9 @@
if (!dr)
return -ENOMEM;
+ if (!devname)
+ devname = dev_name(dev);
+
rc = request_any_context_irq(irq, handler, irqflags, devname, dev_id);
if (rc < 0) {
devres_free(dr);
@@ -137,3 +144,57 @@
free_irq(irq, dev_id);
}
EXPORT_SYMBOL(devm_free_irq);
+
+struct irq_desc_devres {
+ unsigned int from;
+ unsigned int cnt;
+};
+
+static void devm_irq_desc_release(struct device *dev, void *res)
+{
+ struct irq_desc_devres *this = res;
+
+ irq_free_descs(this->from, this->cnt);
+}
+
+/**
+ * __devm_irq_alloc_descs - Allocate and initialize a range of irq descriptors
+ * for a managed device
+ * @dev: Device to allocate the descriptors for
+ * @irq: Allocate for specific irq number if irq >= 0
+ * @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)
+ * @affinity: Optional pointer to an affinity mask array of size @cnt
+ * which hints where the irq descriptors should be allocated
+ * and which default affinities to use
+ *
+ * Returns the first irq number or error code.
+ *
+ * Note: Use the provided wrappers (devm_irq_alloc_desc*) for simplicity.
+ */
+int __devm_irq_alloc_descs(struct device *dev, int irq, unsigned int from,
+ unsigned int cnt, int node, struct module *owner,
+ const struct cpumask *affinity)
+{
+ struct irq_desc_devres *dr;
+ int base;
+
+ dr = devres_alloc(devm_irq_desc_release, sizeof(*dr), GFP_KERNEL);
+ if (!dr)
+ return -ENOMEM;
+
+ base = __irq_alloc_descs(irq, from, cnt, node, owner, affinity);
+ if (base < 0) {
+ devres_free(dr);
+ return base;
+ }
+
+ dr->from = base;
+ dr->cnt = cnt;
+ devres_add(dev, dr);
+
+ return base;
+}
+EXPORT_SYMBOL_GPL(__devm_irq_alloc_descs);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index b59e676..31805f2 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -278,6 +278,31 @@
EXPORT_SYMBOL_GPL(irq_find_matching_fwspec);
/**
+ * irq_domain_check_msi_remap - Check whether all MSI irq domains implement
+ * IRQ remapping
+ *
+ * Return: false if any MSI irq domain does not support IRQ remapping,
+ * true otherwise (including if there is no MSI irq domain)
+ */
+bool irq_domain_check_msi_remap(void)
+{
+ struct irq_domain *h;
+ bool ret = true;
+
+ mutex_lock(&irq_domain_mutex);
+ list_for_each_entry(h, &irq_domain_list, link) {
+ if (irq_domain_is_msi(h) &&
+ !irq_domain_hierarchical_is_msi_remap(h)) {
+ ret = false;
+ break;
+ }
+ }
+ mutex_unlock(&irq_domain_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(irq_domain_check_msi_remap);
+
+/**
* irq_set_default_host() - Set a "default" irq domain
* @domain: default domain pointer
*
@@ -1408,6 +1433,20 @@
if (domain->ops->alloc)
domain->flags |= IRQ_DOMAIN_FLAG_HIERARCHY;
}
+
+/**
+ * irq_domain_hierarchical_is_msi_remap - Check if the domain or any
+ * parent has MSI remapping support
+ * @domain: domain pointer
+ */
+bool irq_domain_hierarchical_is_msi_remap(struct irq_domain *domain)
+{
+ for (; domain; domain = domain->parent) {
+ if (irq_domain_is_msi_remap(domain))
+ return true;
+ }
+ return false;
+}
#else /* CONFIG_IRQ_DOMAIN_HIERARCHY */
/**
* irq_domain_get_irq_data - Get irq_data associated with @virq and @domain
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index ee23006..ddc2f54 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -270,8 +270,8 @@
if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
msi_domain_update_chip_ops(info);
- return irq_domain_create_hierarchy(parent, 0, 0, fwnode,
- &msi_domain_ops, info);
+ return irq_domain_create_hierarchy(parent, IRQ_DOMAIN_FLAG_MSI, 0,
+ fwnode, &msi_domain_ops, info);
}
int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c
index feaa813..c53edad 100644
--- a/kernel/irq/proc.c
+++ b/kernel/irq/proc.c
@@ -487,6 +487,8 @@
}
if (desc->irq_data.domain)
seq_printf(p, " %*d", prec, (int) desc->irq_data.hwirq);
+ else
+ seq_printf(p, " %*s", prec, "");
#ifdef CONFIG_GENERIC_IRQ_SHOW_LEVEL
seq_printf(p, " %-8s", irqd_is_level_type(&desc->irq_data) ? "Level" : "Edge");
#endif
diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c
index 5707f97..061ba7e 100644
--- a/kernel/irq/spurious.c
+++ b/kernel/irq/spurious.c
@@ -175,7 +175,9 @@
static inline int bad_action_ret(irqreturn_t action_ret)
{
- if (likely(action_ret <= (IRQ_HANDLED | IRQ_WAKE_THREAD)))
+ unsigned int r = action_ret;
+
+ if (likely(r <= (IRQ_HANDLED | IRQ_WAKE_THREAD)))
return 0;
return 1;
}
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 4346010..ebb4dad 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -149,9 +149,11 @@
struct kprobe_insn_page *kip;
kprobe_opcode_t *slot = NULL;
+ /* Since the slot array is not protected by rcu, we need a mutex */
mutex_lock(&c->mutex);
retry:
- list_for_each_entry(kip, &c->pages, list) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(kip, &c->pages, list) {
if (kip->nused < slots_per_page(c)) {
int i;
for (i = 0; i < slots_per_page(c); i++) {
@@ -159,6 +161,7 @@
kip->slot_used[i] = SLOT_USED;
kip->nused++;
slot = kip->insns + (i * c->insn_size);
+ rcu_read_unlock();
goto out;
}
}
@@ -167,6 +170,7 @@
WARN_ON(1);
}
}
+ rcu_read_unlock();
/* If there are any garbage slots, collect it and try again. */
if (c->nr_garbage && collect_garbage_slots(c) == 0)
@@ -193,7 +197,7 @@
kip->nused = 1;
kip->ngarbage = 0;
kip->cache = c;
- list_add(&kip->list, &c->pages);
+ list_add_rcu(&kip->list, &c->pages);
slot = kip->insns;
out:
mutex_unlock(&c->mutex);
@@ -213,7 +217,8 @@
* next time somebody inserts a probe.
*/
if (!list_is_singular(&kip->list)) {
- list_del(&kip->list);
+ list_del_rcu(&kip->list);
+ synchronize_rcu();
kip->cache->free(kip->insns);
kfree(kip);
}
@@ -235,8 +240,7 @@
continue;
kip->ngarbage = 0; /* we will collect all garbages */
for (i = 0; i < slots_per_page(c); i++) {
- if (kip->slot_used[i] == SLOT_DIRTY &&
- collect_one_slot(kip, i))
+ if (kip->slot_used[i] == SLOT_DIRTY && collect_one_slot(kip, i))
break;
}
}
@@ -248,29 +252,60 @@
kprobe_opcode_t *slot, int dirty)
{
struct kprobe_insn_page *kip;
+ long idx;
mutex_lock(&c->mutex);
- list_for_each_entry(kip, &c->pages, list) {
- long idx = ((long)slot - (long)kip->insns) /
- (c->insn_size * sizeof(kprobe_opcode_t));
- if (idx >= 0 && idx < slots_per_page(c)) {
- WARN_ON(kip->slot_used[idx] != SLOT_USED);
- if (dirty) {
- kip->slot_used[idx] = SLOT_DIRTY;
- kip->ngarbage++;
- if (++c->nr_garbage > slots_per_page(c))
- collect_garbage_slots(c);
- } else
- collect_one_slot(kip, idx);
+ rcu_read_lock();
+ list_for_each_entry_rcu(kip, &c->pages, list) {
+ idx = ((long)slot - (long)kip->insns) /
+ (c->insn_size * sizeof(kprobe_opcode_t));
+ if (idx >= 0 && idx < slots_per_page(c))
goto out;
+ }
+ /* Could not find this slot. */
+ WARN_ON(1);
+ kip = NULL;
+out:
+ rcu_read_unlock();
+ /* Mark and sweep: this may sleep */
+ if (kip) {
+ /* Check double free */
+ WARN_ON(kip->slot_used[idx] != SLOT_USED);
+ if (dirty) {
+ kip->slot_used[idx] = SLOT_DIRTY;
+ kip->ngarbage++;
+ if (++c->nr_garbage > slots_per_page(c))
+ collect_garbage_slots(c);
+ } else {
+ collect_one_slot(kip, idx);
}
}
- /* Could not free this slot. */
- WARN_ON(1);
-out:
mutex_unlock(&c->mutex);
}
+/*
+ * Check given address is on the page of kprobe instruction slots.
+ * This will be used for checking whether the address on a stack
+ * is on a text area or not.
+ */
+bool __is_insn_slot_addr(struct kprobe_insn_cache *c, unsigned long addr)
+{
+ struct kprobe_insn_page *kip;
+ bool ret = false;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(kip, &c->pages, list) {
+ if (addr >= (unsigned long)kip->insns &&
+ addr < (unsigned long)kip->insns + PAGE_SIZE) {
+ ret = true;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return ret;
+}
+
#ifdef CONFIG_OPTPROBES
/* For optimized_kprobe buffer */
struct kprobe_insn_cache kprobe_optinsn_slots = {
diff --git a/kernel/kthread.c b/kernel/kthread.c
index 2318fba..8461a43 100644
--- a/kernel/kthread.c
+++ b/kernel/kthread.c
@@ -850,7 +850,6 @@
list_add(&work->node, &worker->delayed_work_list);
work->worker = worker;
- timer_stats_timer_set_start_info(&dwork->timer);
timer->expires = jiffies + delay;
add_timer(timer);
}
diff --git a/kernel/locking/Makefile b/kernel/locking/Makefile
index 6f88e35..760158d 100644
--- a/kernel/locking/Makefile
+++ b/kernel/locking/Makefile
@@ -28,3 +28,4 @@
obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem-xadd.o
obj-$(CONFIG_QUEUED_RWLOCKS) += qrwlock.o
obj-$(CONFIG_LOCK_TORTURE_TEST) += locktorture.o
+obj-$(CONFIG_WW_MUTEX_SELFTEST) += test-ww_mutex.o
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 7c38f8f..9812e5d 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -2203,7 +2203,7 @@
* Important for check_no_collision().
*/
if (unlikely(nr_chain_hlocks > MAX_LOCKDEP_CHAIN_HLOCKS)) {
- if (debug_locks_off_graph_unlock())
+ if (!debug_locks_off_graph_unlock())
return 0;
print_lockdep_off("BUG: MAX_LOCKDEP_CHAIN_HLOCKS too low!");
@@ -4412,13 +4412,13 @@
#endif /* #ifdef CONFIG_PROVE_RCU_REPEATEDLY */
/* Note: the following can be executed concurrently, so be careful. */
printk("\n");
- printk("===============================\n");
- printk("[ INFO: suspicious RCU usage. ]\n");
+ pr_err("===============================\n");
+ pr_err("[ ERR: suspicious RCU usage. ]\n");
print_kernel_ident();
- printk("-------------------------------\n");
- printk("%s:%d %s!\n", file, line, s);
- printk("\nother info that might help us debug this:\n\n");
- printk("\n%srcu_scheduler_active = %d, debug_locks = %d\n",
+ pr_err("-------------------------------\n");
+ pr_err("%s:%d %s!\n", file, line, s);
+ pr_err("\nother info that might help us debug this:\n\n");
+ pr_err("\n%srcu_scheduler_active = %d, debug_locks = %d\n",
!rcu_lockdep_current_cpu_online()
? "RCU used illegally from offline CPU!\n"
: !rcu_is_watching()
diff --git a/kernel/locking/locktorture.c b/kernel/locking/locktorture.c
index f8c5af5..28350dc 100644
--- a/kernel/locking/locktorture.c
+++ b/kernel/locking/locktorture.c
@@ -372,6 +372,78 @@
.name = "mutex_lock"
};
+#include <linux/ww_mutex.h>
+static DEFINE_WW_CLASS(torture_ww_class);
+static DEFINE_WW_MUTEX(torture_ww_mutex_0, &torture_ww_class);
+static DEFINE_WW_MUTEX(torture_ww_mutex_1, &torture_ww_class);
+static DEFINE_WW_MUTEX(torture_ww_mutex_2, &torture_ww_class);
+
+static int torture_ww_mutex_lock(void)
+__acquires(torture_ww_mutex_0)
+__acquires(torture_ww_mutex_1)
+__acquires(torture_ww_mutex_2)
+{
+ LIST_HEAD(list);
+ struct reorder_lock {
+ struct list_head link;
+ struct ww_mutex *lock;
+ } locks[3], *ll, *ln;
+ struct ww_acquire_ctx ctx;
+
+ locks[0].lock = &torture_ww_mutex_0;
+ list_add(&locks[0].link, &list);
+
+ locks[1].lock = &torture_ww_mutex_1;
+ list_add(&locks[1].link, &list);
+
+ locks[2].lock = &torture_ww_mutex_2;
+ list_add(&locks[2].link, &list);
+
+ ww_acquire_init(&ctx, &torture_ww_class);
+
+ list_for_each_entry(ll, &list, link) {
+ int err;
+
+ err = ww_mutex_lock(ll->lock, &ctx);
+ if (!err)
+ continue;
+
+ ln = ll;
+ list_for_each_entry_continue_reverse(ln, &list, link)
+ ww_mutex_unlock(ln->lock);
+
+ if (err != -EDEADLK)
+ return err;
+
+ ww_mutex_lock_slow(ll->lock, &ctx);
+ list_move(&ll->link, &list);
+ }
+
+ ww_acquire_fini(&ctx);
+ return 0;
+}
+
+static void torture_ww_mutex_unlock(void)
+__releases(torture_ww_mutex_0)
+__releases(torture_ww_mutex_1)
+__releases(torture_ww_mutex_2)
+{
+ ww_mutex_unlock(&torture_ww_mutex_0);
+ ww_mutex_unlock(&torture_ww_mutex_1);
+ ww_mutex_unlock(&torture_ww_mutex_2);
+}
+
+static struct lock_torture_ops ww_mutex_lock_ops = {
+ .writelock = torture_ww_mutex_lock,
+ .write_delay = torture_mutex_delay,
+ .task_boost = torture_boost_dummy,
+ .writeunlock = torture_ww_mutex_unlock,
+ .readlock = NULL,
+ .read_delay = NULL,
+ .readunlock = NULL,
+ .name = "ww_mutex_lock"
+};
+
#ifdef CONFIG_RT_MUTEXES
static DEFINE_RT_MUTEX(torture_rtmutex);
@@ -780,6 +852,10 @@
else
lock_torture_print_module_parms(cxt.cur_ops,
"End of test: SUCCESS");
+
+ kfree(cxt.lwsa);
+ kfree(cxt.lrsa);
+
end:
torture_cleanup_end();
}
@@ -793,6 +869,7 @@
&spin_lock_ops, &spin_lock_irq_ops,
&rw_lock_ops, &rw_lock_irq_ops,
&mutex_lock_ops,
+ &ww_mutex_lock_ops,
#ifdef CONFIG_RT_MUTEXES
&rtmutex_lock_ops,
#endif
@@ -924,6 +1001,8 @@
GFP_KERNEL);
if (reader_tasks == NULL) {
VERBOSE_TOROUT_ERRSTRING("reader_tasks: Out of memory");
+ kfree(writer_tasks);
+ writer_tasks = NULL;
firsterr = -ENOMEM;
goto unwind;
}
diff --git a/kernel/locking/mutex-debug.h b/kernel/locking/mutex-debug.h
index a459faa..4174417 100644
--- a/kernel/locking/mutex-debug.h
+++ b/kernel/locking/mutex-debug.h
@@ -26,20 +26,3 @@
extern void debug_mutex_unlock(struct mutex *lock);
extern void debug_mutex_init(struct mutex *lock, const char *name,
struct lock_class_key *key);
-
-#define spin_lock_mutex(lock, flags) \
- do { \
- struct mutex *l = container_of(lock, struct mutex, wait_lock); \
- \
- DEBUG_LOCKS_WARN_ON(in_interrupt()); \
- local_irq_save(flags); \
- arch_spin_lock(&(lock)->rlock.raw_lock);\
- DEBUG_LOCKS_WARN_ON(l->magic != l); \
- } while (0)
-
-#define spin_unlock_mutex(lock, flags) \
- do { \
- arch_spin_unlock(&(lock)->rlock.raw_lock); \
- local_irq_restore(flags); \
- preempt_check_resched(); \
- } while (0)
diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c
index 9b34961..ad2d9e2 100644
--- a/kernel/locking/mutex.c
+++ b/kernel/locking/mutex.c
@@ -50,16 +50,17 @@
/*
* @owner: contains: 'struct task_struct *' to the current lock owner,
* NULL means not owned. Since task_struct pointers are aligned at
- * ARCH_MIN_TASKALIGN (which is at least sizeof(void *)), we have low
- * bits to store extra state.
+ * at least L1_CACHE_BYTES, we have low bits to store extra state.
*
* Bit0 indicates a non-empty waiter list; unlock must issue a wakeup.
* Bit1 indicates unlock needs to hand the lock to the top-waiter
+ * Bit2 indicates handoff has been done and we're waiting for pickup.
*/
#define MUTEX_FLAG_WAITERS 0x01
#define MUTEX_FLAG_HANDOFF 0x02
+#define MUTEX_FLAG_PICKUP 0x04
-#define MUTEX_FLAGS 0x03
+#define MUTEX_FLAGS 0x07
static inline struct task_struct *__owner_task(unsigned long owner)
{
@@ -72,38 +73,29 @@
}
/*
- * Actual trylock that will work on any unlocked state.
- *
- * When setting the owner field, we must preserve the low flag bits.
- *
- * Be careful with @handoff, only set that in a wait-loop (where you set
- * HANDOFF) to avoid recursive lock attempts.
+ * Trylock variant that retuns the owning task on failure.
*/
-static inline bool __mutex_trylock(struct mutex *lock, const bool handoff)
+static inline struct task_struct *__mutex_trylock_or_owner(struct mutex *lock)
{
unsigned long owner, curr = (unsigned long)current;
owner = atomic_long_read(&lock->owner);
for (;;) { /* must loop, can race against a flag */
unsigned long old, flags = __owner_flags(owner);
+ unsigned long task = owner & ~MUTEX_FLAGS;
- if (__owner_task(owner)) {
- if (handoff && unlikely(__owner_task(owner) == current)) {
- /*
- * Provide ACQUIRE semantics for the lock-handoff.
- *
- * We cannot easily use load-acquire here, since
- * the actual load is a failed cmpxchg, which
- * doesn't imply any barriers.
- *
- * Also, this is a fairly unlikely scenario, and
- * this contains the cost.
- */
- smp_mb(); /* ACQUIRE */
- return true;
- }
+ if (task) {
+ if (likely(task != curr))
+ break;
- return false;
+ if (likely(!(flags & MUTEX_FLAG_PICKUP)))
+ break;
+
+ flags &= ~MUTEX_FLAG_PICKUP;
+ } else {
+#ifdef CONFIG_DEBUG_MUTEXES
+ DEBUG_LOCKS_WARN_ON(flags & MUTEX_FLAG_PICKUP);
+#endif
}
/*
@@ -111,15 +103,24 @@
* past the point where we acquire it. This would be possible
* if we (accidentally) set the bit on an unlocked mutex.
*/
- if (handoff)
- flags &= ~MUTEX_FLAG_HANDOFF;
+ flags &= ~MUTEX_FLAG_HANDOFF;
old = atomic_long_cmpxchg_acquire(&lock->owner, owner, curr | flags);
if (old == owner)
- return true;
+ return NULL;
owner = old;
}
+
+ return __owner_task(owner);
+}
+
+/*
+ * Actual trylock that will work on any unlocked state.
+ */
+static inline bool __mutex_trylock(struct mutex *lock)
+{
+ return !__mutex_trylock_or_owner(lock);
}
#ifndef CONFIG_DEBUG_LOCK_ALLOC
@@ -171,9 +172,9 @@
/*
* Give up ownership to a specific task, when @task = NULL, this is equivalent
- * to a regular unlock. Clears HANDOFF, preserves WAITERS. Provides RELEASE
- * semantics like a regular unlock, the __mutex_trylock() provides matching
- * ACQUIRE semantics for the handoff.
+ * to a regular unlock. Sets PICKUP on a handoff, clears HANDOF, preserves
+ * WAITERS. Provides RELEASE semantics like a regular unlock, the
+ * __mutex_trylock() provides a matching ACQUIRE semantics for the handoff.
*/
static void __mutex_handoff(struct mutex *lock, struct task_struct *task)
{
@@ -184,10 +185,13 @@
#ifdef CONFIG_DEBUG_MUTEXES
DEBUG_LOCKS_WARN_ON(__owner_task(owner) != current);
+ DEBUG_LOCKS_WARN_ON(owner & MUTEX_FLAG_PICKUP);
#endif
new = (owner & MUTEX_FLAG_WAITERS);
new |= (unsigned long)task;
+ if (task)
+ new |= MUTEX_FLAG_PICKUP;
old = atomic_long_cmpxchg_release(&lock->owner, owner, new);
if (old == owner)
@@ -237,8 +241,8 @@
EXPORT_SYMBOL(mutex_lock);
#endif
-static __always_inline void ww_mutex_lock_acquired(struct ww_mutex *ww,
- struct ww_acquire_ctx *ww_ctx)
+static __always_inline void
+ww_mutex_lock_acquired(struct ww_mutex *ww, struct ww_acquire_ctx *ww_ctx)
{
#ifdef CONFIG_DEBUG_MUTEXES
/*
@@ -277,17 +281,50 @@
ww_ctx->acquired++;
}
+static inline bool __sched
+__ww_ctx_stamp_after(struct ww_acquire_ctx *a, struct ww_acquire_ctx *b)
+{
+ return a->stamp - b->stamp <= LONG_MAX &&
+ (a->stamp != b->stamp || a > b);
+}
+
+/*
+ * Wake up any waiters that may have to back off when the lock is held by the
+ * given context.
+ *
+ * Due to the invariants on the wait list, this can only affect the first
+ * waiter with a context.
+ *
+ * The current task must not be on the wait list.
+ */
+static void __sched
+__ww_mutex_wakeup_for_backoff(struct mutex *lock, struct ww_acquire_ctx *ww_ctx)
+{
+ struct mutex_waiter *cur;
+
+ lockdep_assert_held(&lock->wait_lock);
+
+ list_for_each_entry(cur, &lock->wait_list, list) {
+ if (!cur->ww_ctx)
+ continue;
+
+ if (cur->ww_ctx->acquired > 0 &&
+ __ww_ctx_stamp_after(cur->ww_ctx, ww_ctx)) {
+ debug_mutex_wake_waiter(lock, cur);
+ wake_up_process(cur->task);
+ }
+
+ break;
+ }
+}
+
/*
* After acquiring lock with fastpath or when we lost out in contested
* slowpath, set ctx and wake up any waiters so they can recheck.
*/
static __always_inline void
-ww_mutex_set_context_fastpath(struct ww_mutex *lock,
- struct ww_acquire_ctx *ctx)
+ww_mutex_set_context_fastpath(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
{
- unsigned long flags;
- struct mutex_waiter *cur;
-
ww_mutex_lock_acquired(lock, ctx);
lock->ctx = ctx;
@@ -311,46 +348,79 @@
* Uh oh, we raced in fastpath, wake up everyone in this case,
* so they can see the new lock->ctx.
*/
- spin_lock_mutex(&lock->base.wait_lock, flags);
- list_for_each_entry(cur, &lock->base.wait_list, list) {
- debug_mutex_wake_waiter(&lock->base, cur);
- wake_up_process(cur->task);
- }
- spin_unlock_mutex(&lock->base.wait_lock, flags);
+ spin_lock(&lock->base.wait_lock);
+ __ww_mutex_wakeup_for_backoff(&lock->base, ctx);
+ spin_unlock(&lock->base.wait_lock);
}
/*
- * After acquiring lock in the slowpath set ctx and wake up any
- * waiters so they can recheck.
+ * After acquiring lock in the slowpath set ctx.
+ *
+ * Unlike for the fast path, the caller ensures that waiters are woken up where
+ * necessary.
*
* Callers must hold the mutex wait_lock.
*/
static __always_inline void
-ww_mutex_set_context_slowpath(struct ww_mutex *lock,
- struct ww_acquire_ctx *ctx)
+ww_mutex_set_context_slowpath(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
{
- struct mutex_waiter *cur;
-
ww_mutex_lock_acquired(lock, ctx);
lock->ctx = ctx;
-
- /*
- * Give any possible sleeping processes the chance to wake up,
- * so they can recheck if they have to back off.
- */
- list_for_each_entry(cur, &lock->base.wait_list, list) {
- debug_mutex_wake_waiter(&lock->base, cur);
- wake_up_process(cur->task);
- }
}
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
+
+static inline
+bool ww_mutex_spin_on_owner(struct mutex *lock, struct ww_acquire_ctx *ww_ctx,
+ struct mutex_waiter *waiter)
+{
+ struct ww_mutex *ww;
+
+ ww = container_of(lock, struct ww_mutex, base);
+
+ /*
+ * If ww->ctx is set the contents are undefined, only
+ * by acquiring wait_lock there is a guarantee that
+ * they are not invalid when reading.
+ *
+ * As such, when deadlock detection needs to be
+ * performed the optimistic spinning cannot be done.
+ *
+ * Check this in every inner iteration because we may
+ * be racing against another thread's ww_mutex_lock.
+ */
+ if (ww_ctx->acquired > 0 && READ_ONCE(ww->ctx))
+ return false;
+
+ /*
+ * If we aren't on the wait list yet, cancel the spin
+ * if there are waiters. We want to avoid stealing the
+ * lock from a waiter with an earlier stamp, since the
+ * other thread may already own a lock that we also
+ * need.
+ */
+ if (!waiter && (atomic_long_read(&lock->owner) & MUTEX_FLAG_WAITERS))
+ return false;
+
+ /*
+ * Similarly, stop spinning if we are no longer the
+ * first waiter.
+ */
+ if (waiter && !__mutex_waiter_is_first(lock, waiter))
+ return false;
+
+ return true;
+}
+
/*
- * Look out! "owner" is an entirely speculative pointer
- * access and not reliable.
+ * Look out! "owner" is an entirely speculative pointer access and not
+ * reliable.
+ *
+ * "noinline" so that this function shows up on perf profiles.
*/
static noinline
-bool mutex_spin_on_owner(struct mutex *lock, struct task_struct *owner)
+bool mutex_spin_on_owner(struct mutex *lock, struct task_struct *owner,
+ struct ww_acquire_ctx *ww_ctx, struct mutex_waiter *waiter)
{
bool ret = true;
@@ -373,6 +443,11 @@
break;
}
+ if (ww_ctx && !ww_mutex_spin_on_owner(lock, ww_ctx, waiter)) {
+ ret = false;
+ break;
+ }
+
cpu_relax();
}
rcu_read_unlock();
@@ -431,12 +506,10 @@
* with the spinner at the head of the OSQ, if present, until the owner is
* changed to itself.
*/
-static bool mutex_optimistic_spin(struct mutex *lock,
- struct ww_acquire_ctx *ww_ctx,
- const bool use_ww_ctx, const bool waiter)
+static __always_inline bool
+mutex_optimistic_spin(struct mutex *lock, struct ww_acquire_ctx *ww_ctx,
+ const bool use_ww_ctx, struct mutex_waiter *waiter)
{
- struct task_struct *task = current;
-
if (!waiter) {
/*
* The purpose of the mutex_can_spin_on_owner() function is
@@ -460,40 +533,17 @@
for (;;) {
struct task_struct *owner;
- if (use_ww_ctx && ww_ctx->acquired > 0) {
- struct ww_mutex *ww;
-
- ww = container_of(lock, struct ww_mutex, base);
- /*
- * If ww->ctx is set the contents are undefined, only
- * by acquiring wait_lock there is a guarantee that
- * they are not invalid when reading.
- *
- * As such, when deadlock detection needs to be
- * performed the optimistic spinning cannot be done.
- */
- if (READ_ONCE(ww->ctx))
- goto fail_unlock;
- }
+ /* Try to acquire the mutex... */
+ owner = __mutex_trylock_or_owner(lock);
+ if (!owner)
+ break;
/*
- * If there's an owner, wait for it to either
+ * There's an owner, wait for it to either
* release the lock or go to sleep.
*/
- owner = __mutex_owner(lock);
- if (owner) {
- if (waiter && owner == task) {
- smp_mb(); /* ACQUIRE */
- break;
- }
-
- if (!mutex_spin_on_owner(lock, owner))
- goto fail_unlock;
- }
-
- /* Try to acquire the mutex if it is unlocked. */
- if (__mutex_trylock(lock, waiter))
- break;
+ if (!mutex_spin_on_owner(lock, owner, ww_ctx, waiter))
+ goto fail_unlock;
/*
* The cpu_relax() call is a compiler barrier which forces
@@ -532,9 +582,9 @@
return false;
}
#else
-static bool mutex_optimistic_spin(struct mutex *lock,
- struct ww_acquire_ctx *ww_ctx,
- const bool use_ww_ctx, const bool waiter)
+static __always_inline bool
+mutex_optimistic_spin(struct mutex *lock, struct ww_acquire_ctx *ww_ctx,
+ const bool use_ww_ctx, struct mutex_waiter *waiter)
{
return false;
}
@@ -594,24 +644,89 @@
EXPORT_SYMBOL(ww_mutex_unlock);
static inline int __sched
-__ww_mutex_lock_check_stamp(struct mutex *lock, struct ww_acquire_ctx *ctx)
+__ww_mutex_lock_check_stamp(struct mutex *lock, struct mutex_waiter *waiter,
+ struct ww_acquire_ctx *ctx)
{
struct ww_mutex *ww = container_of(lock, struct ww_mutex, base);
struct ww_acquire_ctx *hold_ctx = READ_ONCE(ww->ctx);
+ struct mutex_waiter *cur;
- if (!hold_ctx)
- return 0;
+ if (hold_ctx && __ww_ctx_stamp_after(ctx, hold_ctx))
+ goto deadlock;
- if (ctx->stamp - hold_ctx->stamp <= LONG_MAX &&
- (ctx->stamp != hold_ctx->stamp || ctx > hold_ctx)) {
-#ifdef CONFIG_DEBUG_MUTEXES
- DEBUG_LOCKS_WARN_ON(ctx->contending_lock);
- ctx->contending_lock = ww;
-#endif
- return -EDEADLK;
+ /*
+ * If there is a waiter in front of us that has a context, then its
+ * stamp is earlier than ours and we must back off.
+ */
+ cur = waiter;
+ list_for_each_entry_continue_reverse(cur, &lock->wait_list, list) {
+ if (cur->ww_ctx)
+ goto deadlock;
}
return 0;
+
+deadlock:
+#ifdef CONFIG_DEBUG_MUTEXES
+ DEBUG_LOCKS_WARN_ON(ctx->contending_lock);
+ ctx->contending_lock = ww;
+#endif
+ return -EDEADLK;
+}
+
+static inline int __sched
+__ww_mutex_add_waiter(struct mutex_waiter *waiter,
+ struct mutex *lock,
+ struct ww_acquire_ctx *ww_ctx)
+{
+ struct mutex_waiter *cur;
+ struct list_head *pos;
+
+ if (!ww_ctx) {
+ list_add_tail(&waiter->list, &lock->wait_list);
+ return 0;
+ }
+
+ /*
+ * Add the waiter before the first waiter with a higher stamp.
+ * Waiters without a context are skipped to avoid starving
+ * them.
+ */
+ pos = &lock->wait_list;
+ list_for_each_entry_reverse(cur, &lock->wait_list, list) {
+ if (!cur->ww_ctx)
+ continue;
+
+ if (__ww_ctx_stamp_after(ww_ctx, cur->ww_ctx)) {
+ /* Back off immediately if necessary. */
+ if (ww_ctx->acquired > 0) {
+#ifdef CONFIG_DEBUG_MUTEXES
+ struct ww_mutex *ww;
+
+ ww = container_of(lock, struct ww_mutex, base);
+ DEBUG_LOCKS_WARN_ON(ww_ctx->contending_lock);
+ ww_ctx->contending_lock = ww;
+#endif
+ return -EDEADLK;
+ }
+
+ break;
+ }
+
+ pos = &cur->list;
+
+ /*
+ * Wake up the waiter so that it gets a chance to back
+ * off.
+ */
+ if (cur->ww_ctx->acquired > 0) {
+ debug_mutex_wake_waiter(lock, cur);
+ wake_up_process(cur->task);
+ }
+ }
+
+ list_add_tail(&waiter->list, pos);
+ return 0;
}
/*
@@ -622,15 +737,15 @@
struct lockdep_map *nest_lock, unsigned long ip,
struct ww_acquire_ctx *ww_ctx, const bool use_ww_ctx)
{
- struct task_struct *task = current;
struct mutex_waiter waiter;
- unsigned long flags;
bool first = false;
struct ww_mutex *ww;
int ret;
- if (use_ww_ctx) {
- ww = container_of(lock, struct ww_mutex, base);
+ might_sleep();
+
+ ww = container_of(lock, struct ww_mutex, base);
+ if (use_ww_ctx && ww_ctx) {
if (unlikely(ww_ctx == READ_ONCE(ww->ctx)))
return -EALREADY;
}
@@ -638,36 +753,54 @@
preempt_disable();
mutex_acquire_nest(&lock->dep_map, subclass, 0, nest_lock, ip);
- if (__mutex_trylock(lock, false) ||
- mutex_optimistic_spin(lock, ww_ctx, use_ww_ctx, false)) {
+ if (__mutex_trylock(lock) ||
+ mutex_optimistic_spin(lock, ww_ctx, use_ww_ctx, NULL)) {
/* got the lock, yay! */
lock_acquired(&lock->dep_map, ip);
- if (use_ww_ctx)
+ if (use_ww_ctx && ww_ctx)
ww_mutex_set_context_fastpath(ww, ww_ctx);
preempt_enable();
return 0;
}
- spin_lock_mutex(&lock->wait_lock, flags);
+ spin_lock(&lock->wait_lock);
/*
* After waiting to acquire the wait_lock, try again.
*/
- if (__mutex_trylock(lock, false))
+ if (__mutex_trylock(lock)) {
+ if (use_ww_ctx && ww_ctx)
+ __ww_mutex_wakeup_for_backoff(lock, ww_ctx);
+
goto skip_wait;
+ }
debug_mutex_lock_common(lock, &waiter);
- debug_mutex_add_waiter(lock, &waiter, task);
+ debug_mutex_add_waiter(lock, &waiter, current);
- /* add waiting tasks to the end of the waitqueue (FIFO): */
- list_add_tail(&waiter.list, &lock->wait_list);
- waiter.task = task;
+ lock_contended(&lock->dep_map, ip);
+
+ if (!use_ww_ctx) {
+ /* add waiting tasks to the end of the waitqueue (FIFO): */
+ list_add_tail(&waiter.list, &lock->wait_list);
+
+#ifdef CONFIG_DEBUG_MUTEXES
+ waiter.ww_ctx = MUTEX_POISON_WW_CTX;
+#endif
+ } else {
+ /* Add in stamp order, waking up waiters that must back off. */
+ ret = __ww_mutex_add_waiter(&waiter, lock, ww_ctx);
+ if (ret)
+ goto err_early_backoff;
+
+ waiter.ww_ctx = ww_ctx;
+ }
+
+ waiter.task = current;
if (__mutex_waiter_is_first(lock, &waiter))
__mutex_set_flag(lock, MUTEX_FLAG_WAITERS);
- lock_contended(&lock->dep_map, ip);
-
- set_task_state(task, state);
+ set_current_state(state);
for (;;) {
/*
* Once we hold wait_lock, we're serialized against
@@ -675,7 +808,7 @@
* before testing the error conditions to make sure we pick up
* the handoff.
*/
- if (__mutex_trylock(lock, first))
+ if (__mutex_trylock(lock))
goto acquired;
/*
@@ -683,42 +816,47 @@
* wait_lock. This ensures the lock cancellation is ordered
* against mutex_unlock() and wake-ups do not go missing.
*/
- if (unlikely(signal_pending_state(state, task))) {
+ if (unlikely(signal_pending_state(state, current))) {
ret = -EINTR;
goto err;
}
- if (use_ww_ctx && ww_ctx->acquired > 0) {
- ret = __ww_mutex_lock_check_stamp(lock, ww_ctx);
+ if (use_ww_ctx && ww_ctx && ww_ctx->acquired > 0) {
+ ret = __ww_mutex_lock_check_stamp(lock, &waiter, ww_ctx);
if (ret)
goto err;
}
- spin_unlock_mutex(&lock->wait_lock, flags);
+ spin_unlock(&lock->wait_lock);
schedule_preempt_disabled();
- if (!first && __mutex_waiter_is_first(lock, &waiter)) {
- first = true;
- __mutex_set_flag(lock, MUTEX_FLAG_HANDOFF);
+ /*
+ * ww_mutex needs to always recheck its position since its waiter
+ * list is not FIFO ordered.
+ */
+ if ((use_ww_ctx && ww_ctx) || !first) {
+ first = __mutex_waiter_is_first(lock, &waiter);
+ if (first)
+ __mutex_set_flag(lock, MUTEX_FLAG_HANDOFF);
}
- set_task_state(task, state);
+ set_current_state(state);
/*
* Here we order against unlock; we must either see it change
* state back to RUNNING and fall through the next schedule(),
* or we must see its unlock and acquire.
*/
- if ((first && mutex_optimistic_spin(lock, ww_ctx, use_ww_ctx, true)) ||
- __mutex_trylock(lock, first))
+ if (__mutex_trylock(lock) ||
+ (first && mutex_optimistic_spin(lock, ww_ctx, use_ww_ctx, &waiter)))
break;
- spin_lock_mutex(&lock->wait_lock, flags);
+ spin_lock(&lock->wait_lock);
}
- spin_lock_mutex(&lock->wait_lock, flags);
+ spin_lock(&lock->wait_lock);
acquired:
- __set_task_state(task, TASK_RUNNING);
+ __set_current_state(TASK_RUNNING);
- mutex_remove_waiter(lock, &waiter, task);
+ mutex_remove_waiter(lock, &waiter, current);
if (likely(list_empty(&lock->wait_list)))
__mutex_clear_flag(lock, MUTEX_FLAGS);
@@ -728,30 +866,44 @@
/* got the lock - cleanup and rejoice! */
lock_acquired(&lock->dep_map, ip);
- if (use_ww_ctx)
+ if (use_ww_ctx && ww_ctx)
ww_mutex_set_context_slowpath(ww, ww_ctx);
- spin_unlock_mutex(&lock->wait_lock, flags);
+ spin_unlock(&lock->wait_lock);
preempt_enable();
return 0;
err:
- __set_task_state(task, TASK_RUNNING);
- mutex_remove_waiter(lock, &waiter, task);
- spin_unlock_mutex(&lock->wait_lock, flags);
+ __set_current_state(TASK_RUNNING);
+ mutex_remove_waiter(lock, &waiter, current);
+err_early_backoff:
+ spin_unlock(&lock->wait_lock);
debug_mutex_free_waiter(&waiter);
mutex_release(&lock->dep_map, 1, ip);
preempt_enable();
return ret;
}
+static int __sched
+__mutex_lock(struct mutex *lock, long state, unsigned int subclass,
+ struct lockdep_map *nest_lock, unsigned long ip)
+{
+ return __mutex_lock_common(lock, state, subclass, nest_lock, ip, NULL, false);
+}
+
+static int __sched
+__ww_mutex_lock(struct mutex *lock, long state, unsigned int subclass,
+ struct lockdep_map *nest_lock, unsigned long ip,
+ struct ww_acquire_ctx *ww_ctx)
+{
+ return __mutex_lock_common(lock, state, subclass, nest_lock, ip, ww_ctx, true);
+}
+
#ifdef CONFIG_DEBUG_LOCK_ALLOC
void __sched
mutex_lock_nested(struct mutex *lock, unsigned int subclass)
{
- might_sleep();
- __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE,
- subclass, NULL, _RET_IP_, NULL, 0);
+ __mutex_lock(lock, TASK_UNINTERRUPTIBLE, subclass, NULL, _RET_IP_);
}
EXPORT_SYMBOL_GPL(mutex_lock_nested);
@@ -759,30 +911,38 @@
void __sched
_mutex_lock_nest_lock(struct mutex *lock, struct lockdep_map *nest)
{
- might_sleep();
- __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE,
- 0, nest, _RET_IP_, NULL, 0);
+ __mutex_lock(lock, TASK_UNINTERRUPTIBLE, 0, nest, _RET_IP_);
}
EXPORT_SYMBOL_GPL(_mutex_lock_nest_lock);
int __sched
mutex_lock_killable_nested(struct mutex *lock, unsigned int subclass)
{
- might_sleep();
- return __mutex_lock_common(lock, TASK_KILLABLE,
- subclass, NULL, _RET_IP_, NULL, 0);
+ return __mutex_lock(lock, TASK_KILLABLE, subclass, NULL, _RET_IP_);
}
EXPORT_SYMBOL_GPL(mutex_lock_killable_nested);
int __sched
mutex_lock_interruptible_nested(struct mutex *lock, unsigned int subclass)
{
- might_sleep();
- return __mutex_lock_common(lock, TASK_INTERRUPTIBLE,
- subclass, NULL, _RET_IP_, NULL, 0);
+ return __mutex_lock(lock, TASK_INTERRUPTIBLE, subclass, NULL, _RET_IP_);
}
EXPORT_SYMBOL_GPL(mutex_lock_interruptible_nested);
+void __sched
+mutex_lock_io_nested(struct mutex *lock, unsigned int subclass)
+{
+ int token;
+
+ might_sleep();
+
+ token = io_schedule_prepare();
+ __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE,
+ subclass, NULL, _RET_IP_, NULL, 0);
+ io_schedule_finish(token);
+}
+EXPORT_SYMBOL_GPL(mutex_lock_io_nested);
+
static inline int
ww_mutex_deadlock_injection(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
{
@@ -810,35 +970,37 @@
}
int __sched
-__ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
+ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
{
int ret;
might_sleep();
- ret = __mutex_lock_common(&lock->base, TASK_UNINTERRUPTIBLE,
- 0, &ctx->dep_map, _RET_IP_, ctx, 1);
- if (!ret && ctx->acquired > 1)
+ ret = __ww_mutex_lock(&lock->base, TASK_UNINTERRUPTIBLE,
+ 0, ctx ? &ctx->dep_map : NULL, _RET_IP_,
+ ctx);
+ if (!ret && ctx && ctx->acquired > 1)
return ww_mutex_deadlock_injection(lock, ctx);
return ret;
}
-EXPORT_SYMBOL_GPL(__ww_mutex_lock);
+EXPORT_SYMBOL_GPL(ww_mutex_lock);
int __sched
-__ww_mutex_lock_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
+ww_mutex_lock_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
{
int ret;
might_sleep();
- ret = __mutex_lock_common(&lock->base, TASK_INTERRUPTIBLE,
- 0, &ctx->dep_map, _RET_IP_, ctx, 1);
+ ret = __ww_mutex_lock(&lock->base, TASK_INTERRUPTIBLE,
+ 0, ctx ? &ctx->dep_map : NULL, _RET_IP_,
+ ctx);
- if (!ret && ctx->acquired > 1)
+ if (!ret && ctx && ctx->acquired > 1)
return ww_mutex_deadlock_injection(lock, ctx);
return ret;
}
-EXPORT_SYMBOL_GPL(__ww_mutex_lock_interruptible);
+EXPORT_SYMBOL_GPL(ww_mutex_lock_interruptible);
#endif
@@ -848,8 +1010,8 @@
static noinline void __sched __mutex_unlock_slowpath(struct mutex *lock, unsigned long ip)
{
struct task_struct *next = NULL;
- unsigned long owner, flags;
DEFINE_WAKE_Q(wake_q);
+ unsigned long owner;
mutex_release(&lock->dep_map, 1, ip);
@@ -866,6 +1028,7 @@
#ifdef CONFIG_DEBUG_MUTEXES
DEBUG_LOCKS_WARN_ON(__owner_task(owner) != current);
+ DEBUG_LOCKS_WARN_ON(owner & MUTEX_FLAG_PICKUP);
#endif
if (owner & MUTEX_FLAG_HANDOFF)
@@ -883,7 +1046,7 @@
owner = old;
}
- spin_lock_mutex(&lock->wait_lock, flags);
+ spin_lock(&lock->wait_lock);
debug_mutex_unlock(lock);
if (!list_empty(&lock->wait_list)) {
/* get the first entry from the wait-list: */
@@ -900,7 +1063,7 @@
if (owner & MUTEX_FLAG_HANDOFF)
__mutex_handoff(lock, next);
- spin_unlock_mutex(&lock->wait_lock, flags);
+ spin_unlock(&lock->wait_lock);
wake_up_q(&wake_q);
}
@@ -950,40 +1113,47 @@
}
EXPORT_SYMBOL(mutex_lock_killable);
+void __sched mutex_lock_io(struct mutex *lock)
+{
+ int token;
+
+ token = io_schedule_prepare();
+ mutex_lock(lock);
+ io_schedule_finish(token);
+}
+EXPORT_SYMBOL_GPL(mutex_lock_io);
+
static noinline void __sched
__mutex_lock_slowpath(struct mutex *lock)
{
- __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, 0,
- NULL, _RET_IP_, NULL, 0);
+ __mutex_lock(lock, TASK_UNINTERRUPTIBLE, 0, NULL, _RET_IP_);
}
static noinline int __sched
__mutex_lock_killable_slowpath(struct mutex *lock)
{
- return __mutex_lock_common(lock, TASK_KILLABLE, 0,
- NULL, _RET_IP_, NULL, 0);
+ return __mutex_lock(lock, TASK_KILLABLE, 0, NULL, _RET_IP_);
}
static noinline int __sched
__mutex_lock_interruptible_slowpath(struct mutex *lock)
{
- return __mutex_lock_common(lock, TASK_INTERRUPTIBLE, 0,
- NULL, _RET_IP_, NULL, 0);
+ return __mutex_lock(lock, TASK_INTERRUPTIBLE, 0, NULL, _RET_IP_);
}
static noinline int __sched
__ww_mutex_lock_slowpath(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
{
- return __mutex_lock_common(&lock->base, TASK_UNINTERRUPTIBLE, 0,
- NULL, _RET_IP_, ctx, 1);
+ return __ww_mutex_lock(&lock->base, TASK_UNINTERRUPTIBLE, 0, NULL,
+ _RET_IP_, ctx);
}
static noinline int __sched
__ww_mutex_lock_interruptible_slowpath(struct ww_mutex *lock,
struct ww_acquire_ctx *ctx)
{
- return __mutex_lock_common(&lock->base, TASK_INTERRUPTIBLE, 0,
- NULL, _RET_IP_, ctx, 1);
+ return __ww_mutex_lock(&lock->base, TASK_INTERRUPTIBLE, 0, NULL,
+ _RET_IP_, ctx);
}
#endif
@@ -1004,7 +1174,7 @@
*/
int __sched mutex_trylock(struct mutex *lock)
{
- bool locked = __mutex_trylock(lock, false);
+ bool locked = __mutex_trylock(lock);
if (locked)
mutex_acquire(&lock->dep_map, 0, 1, _RET_IP_);
@@ -1015,32 +1185,34 @@
#ifndef CONFIG_DEBUG_LOCK_ALLOC
int __sched
-__ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
+ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
{
might_sleep();
if (__mutex_trylock_fast(&lock->base)) {
- ww_mutex_set_context_fastpath(lock, ctx);
+ if (ctx)
+ ww_mutex_set_context_fastpath(lock, ctx);
return 0;
}
return __ww_mutex_lock_slowpath(lock, ctx);
}
-EXPORT_SYMBOL(__ww_mutex_lock);
+EXPORT_SYMBOL(ww_mutex_lock);
int __sched
-__ww_mutex_lock_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
+ww_mutex_lock_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
{
might_sleep();
if (__mutex_trylock_fast(&lock->base)) {
- ww_mutex_set_context_fastpath(lock, ctx);
+ if (ctx)
+ ww_mutex_set_context_fastpath(lock, ctx);
return 0;
}
return __ww_mutex_lock_interruptible_slowpath(lock, ctx);
}
-EXPORT_SYMBOL(__ww_mutex_lock_interruptible);
+EXPORT_SYMBOL(ww_mutex_lock_interruptible);
#endif
diff --git a/kernel/locking/mutex.h b/kernel/locking/mutex.h
index 4410a4a..6ebc190 100644
--- a/kernel/locking/mutex.h
+++ b/kernel/locking/mutex.h
@@ -9,10 +9,6 @@
* !CONFIG_DEBUG_MUTEXES case. Most of them are NOPs:
*/
-#define spin_lock_mutex(lock, flags) \
- do { spin_lock(lock); (void)(flags); } while (0)
-#define spin_unlock_mutex(lock, flags) \
- do { spin_unlock(lock); (void)(flags); } while (0)
#define mutex_remove_waiter(lock, waiter, task) \
__list_del((waiter)->list.prev, (waiter)->list.next)
diff --git a/kernel/locking/percpu-rwsem.c b/kernel/locking/percpu-rwsem.c
index ce18259..883cf1b 100644
--- a/kernel/locking/percpu-rwsem.c
+++ b/kernel/locking/percpu-rwsem.c
@@ -1,7 +1,6 @@
#include <linux/atomic.h>
#include <linux/rwsem.h>
#include <linux/percpu.h>
-#include <linux/wait.h>
#include <linux/lockdep.h>
#include <linux/percpu-rwsem.h>
#include <linux/rcupdate.h>
@@ -18,7 +17,7 @@
/* ->rw_sem represents the whole percpu_rw_semaphore for lockdep */
rcu_sync_init(&sem->rss, RCU_SCHED_SYNC);
__init_rwsem(&sem->rw_sem, name, rwsem_key);
- init_waitqueue_head(&sem->writer);
+ rcuwait_init(&sem->writer);
sem->readers_block = 0;
return 0;
}
@@ -103,7 +102,7 @@
__this_cpu_dec(*sem->read_count);
/* Prod writer to recheck readers_active */
- wake_up(&sem->writer);
+ rcuwait_wake_up(&sem->writer);
}
EXPORT_SYMBOL_GPL(__percpu_up_read);
@@ -160,7 +159,7 @@
*/
/* Wait for all now active readers to complete. */
- wait_event(sem->writer, readers_active_check(sem));
+ rcuwait_wait_event(&sem->writer, readers_active_check(sem));
}
EXPORT_SYMBOL_GPL(percpu_down_write);
diff --git a/kernel/locking/qspinlock_paravirt.h b/kernel/locking/qspinlock_paravirt.h
index e3b5520..e6b2f7a 100644
--- a/kernel/locking/qspinlock_paravirt.h
+++ b/kernel/locking/qspinlock_paravirt.h
@@ -263,7 +263,7 @@
if ((loop & PV_PREV_CHECK_MASK) != 0)
return false;
- return READ_ONCE(prev->state) != vcpu_running;
+ return READ_ONCE(prev->state) != vcpu_running || vcpu_is_preempted(prev->cpu);
}
/*
diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
index 2f443ed..d340be3 100644
--- a/kernel/locking/rtmutex.c
+++ b/kernel/locking/rtmutex.c
@@ -1179,7 +1179,7 @@
* TASK_INTERRUPTIBLE checks for signals and
* timeout. Ignored otherwise.
*/
- if (unlikely(state == TASK_INTERRUPTIBLE)) {
+ if (likely(state == TASK_INTERRUPTIBLE)) {
/* Signal pending? */
if (signal_pending(current))
ret = -EINTR;
diff --git a/kernel/locking/rwsem-spinlock.c b/kernel/locking/rwsem-spinlock.c
index 1591f6b..5eacab8 100644
--- a/kernel/locking/rwsem-spinlock.c
+++ b/kernel/locking/rwsem-spinlock.c
@@ -128,7 +128,6 @@
void __sched __down_read(struct rw_semaphore *sem)
{
struct rwsem_waiter waiter;
- struct task_struct *tsk;
unsigned long flags;
raw_spin_lock_irqsave(&sem->wait_lock, flags);
@@ -140,13 +139,12 @@
goto out;
}
- tsk = current;
- set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ set_current_state(TASK_UNINTERRUPTIBLE);
/* set up my own style of waitqueue */
- waiter.task = tsk;
+ waiter.task = current;
waiter.type = RWSEM_WAITING_FOR_READ;
- get_task_struct(tsk);
+ get_task_struct(current);
list_add_tail(&waiter.list, &sem->wait_list);
@@ -158,10 +156,10 @@
if (!waiter.task)
break;
schedule();
- set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ set_current_state(TASK_UNINTERRUPTIBLE);
}
- __set_task_state(tsk, TASK_RUNNING);
+ __set_current_state(TASK_RUNNING);
out:
;
}
@@ -194,15 +192,13 @@
int __sched __down_write_common(struct rw_semaphore *sem, int state)
{
struct rwsem_waiter waiter;
- struct task_struct *tsk;
unsigned long flags;
int ret = 0;
raw_spin_lock_irqsave(&sem->wait_lock, flags);
/* set up my own style of waitqueue */
- tsk = current;
- waiter.task = tsk;
+ waiter.task = current;
waiter.type = RWSEM_WAITING_FOR_WRITE;
list_add_tail(&waiter.list, &sem->wait_list);
@@ -220,7 +216,7 @@
ret = -EINTR;
goto out;
}
- set_task_state(tsk, state);
+ set_current_state(state);
raw_spin_unlock_irqrestore(&sem->wait_lock, flags);
schedule();
raw_spin_lock_irqsave(&sem->wait_lock, flags);
diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c
index 6315060..2ad8d8d 100644
--- a/kernel/locking/rwsem-xadd.c
+++ b/kernel/locking/rwsem-xadd.c
@@ -224,10 +224,9 @@
{
long count, adjustment = -RWSEM_ACTIVE_READ_BIAS;
struct rwsem_waiter waiter;
- struct task_struct *tsk = current;
DEFINE_WAKE_Q(wake_q);
- waiter.task = tsk;
+ waiter.task = current;
waiter.type = RWSEM_WAITING_FOR_READ;
raw_spin_lock_irq(&sem->wait_lock);
@@ -254,13 +253,13 @@
/* wait to be given the lock */
while (true) {
- set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ set_current_state(TASK_UNINTERRUPTIBLE);
if (!waiter.task)
break;
schedule();
}
- __set_task_state(tsk, TASK_RUNNING);
+ __set_current_state(TASK_RUNNING);
return sem;
}
EXPORT_SYMBOL(rwsem_down_read_failed);
@@ -503,8 +502,6 @@
* wake any read locks that were queued ahead of us.
*/
if (count > RWSEM_WAITING_BIAS) {
- DEFINE_WAKE_Q(wake_q);
-
__rwsem_mark_wake(sem, RWSEM_WAKE_READERS, &wake_q);
/*
* The wakeup is normally called _after_ the wait_lock
@@ -514,6 +511,11 @@
* for attempting rwsem_try_write_lock().
*/
wake_up_q(&wake_q);
+
+ /*
+ * Reinitialize wake_q after use.
+ */
+ wake_q_init(&wake_q);
}
} else
diff --git a/kernel/locking/semaphore.c b/kernel/locking/semaphore.c
index b8120ab..9512e37 100644
--- a/kernel/locking/semaphore.c
+++ b/kernel/locking/semaphore.c
@@ -204,19 +204,18 @@
static inline int __sched __down_common(struct semaphore *sem, long state,
long timeout)
{
- struct task_struct *task = current;
struct semaphore_waiter waiter;
list_add_tail(&waiter.list, &sem->wait_list);
- waiter.task = task;
+ waiter.task = current;
waiter.up = false;
for (;;) {
- if (signal_pending_state(state, task))
+ if (signal_pending_state(state, current))
goto interrupted;
if (unlikely(timeout <= 0))
goto timed_out;
- __set_task_state(task, state);
+ __set_current_state(state);
raw_spin_unlock_irq(&sem->lock);
timeout = schedule_timeout(timeout);
raw_spin_lock_irq(&sem->lock);
diff --git a/kernel/locking/spinlock.c b/kernel/locking/spinlock.c
index db3ccb1..4b082b5 100644
--- a/kernel/locking/spinlock.c
+++ b/kernel/locking/spinlock.c
@@ -363,14 +363,6 @@
}
EXPORT_SYMBOL(_raw_spin_lock_nested);
-void __lockfunc _raw_spin_lock_bh_nested(raw_spinlock_t *lock, int subclass)
-{
- __local_bh_disable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET);
- spin_acquire(&lock->dep_map, subclass, 0, _RET_IP_);
- LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
-}
-EXPORT_SYMBOL(_raw_spin_lock_bh_nested);
-
unsigned long __lockfunc _raw_spin_lock_irqsave_nested(raw_spinlock_t *lock,
int subclass)
{
diff --git a/kernel/locking/spinlock_debug.c b/kernel/locking/spinlock_debug.c
index 0374a59..9aa0fcc 100644
--- a/kernel/locking/spinlock_debug.c
+++ b/kernel/locking/spinlock_debug.c
@@ -103,38 +103,14 @@
lock->owner_cpu = -1;
}
-static void __spin_lock_debug(raw_spinlock_t *lock)
-{
- u64 i;
- u64 loops = loops_per_jiffy * HZ;
-
- for (i = 0; i < loops; i++) {
- if (arch_spin_trylock(&lock->raw_lock))
- return;
- __delay(1);
- }
- /* lockup suspected: */
- spin_dump(lock, "lockup suspected");
-#ifdef CONFIG_SMP
- trigger_all_cpu_backtrace();
-#endif
-
- /*
- * The trylock above was causing a livelock. Give the lower level arch
- * specific lock code a chance to acquire the lock. We have already
- * printed a warning/backtrace at this point. The non-debug arch
- * specific code might actually succeed in acquiring the lock. If it is
- * not successful, the end-result is the same - there is no forward
- * progress.
- */
- arch_spin_lock(&lock->raw_lock);
-}
-
+/*
+ * We are now relying on the NMI watchdog to detect lockup instead of doing
+ * the detection here with an unfair lock which can cause problem of its own.
+ */
void do_raw_spin_lock(raw_spinlock_t *lock)
{
debug_spin_lock_before(lock);
- if (unlikely(!arch_spin_trylock(&lock->raw_lock)))
- __spin_lock_debug(lock);
+ arch_spin_lock(&lock->raw_lock);
debug_spin_lock_after(lock);
}
@@ -172,32 +148,6 @@
#define RWLOCK_BUG_ON(cond, lock, msg) if (unlikely(cond)) rwlock_bug(lock, msg)
-#if 0 /* __write_lock_debug() can lock up - maybe this can too? */
-static void __read_lock_debug(rwlock_t *lock)
-{
- u64 i;
- u64 loops = loops_per_jiffy * HZ;
- int print_once = 1;
-
- for (;;) {
- for (i = 0; i < loops; i++) {
- if (arch_read_trylock(&lock->raw_lock))
- return;
- __delay(1);
- }
- /* lockup suspected: */
- if (print_once) {
- print_once = 0;
- printk(KERN_EMERG "BUG: read-lock lockup on CPU#%d, "
- "%s/%d, %p\n",
- raw_smp_processor_id(), current->comm,
- current->pid, lock);
- dump_stack();
- }
- }
-}
-#endif
-
void do_raw_read_lock(rwlock_t *lock)
{
RWLOCK_BUG_ON(lock->magic != RWLOCK_MAGIC, lock, "bad magic");
@@ -247,32 +197,6 @@
lock->owner_cpu = -1;
}
-#if 0 /* This can cause lockups */
-static void __write_lock_debug(rwlock_t *lock)
-{
- u64 i;
- u64 loops = loops_per_jiffy * HZ;
- int print_once = 1;
-
- for (;;) {
- for (i = 0; i < loops; i++) {
- if (arch_write_trylock(&lock->raw_lock))
- return;
- __delay(1);
- }
- /* lockup suspected: */
- if (print_once) {
- print_once = 0;
- printk(KERN_EMERG "BUG: write-lock lockup on CPU#%d, "
- "%s/%d, %p\n",
- raw_smp_processor_id(), current->comm,
- current->pid, lock);
- dump_stack();
- }
- }
-}
-#endif
-
void do_raw_write_lock(rwlock_t *lock)
{
debug_write_lock_before(lock);
diff --git a/kernel/locking/test-ww_mutex.c b/kernel/locking/test-ww_mutex.c
new file mode 100644
index 0000000..da6c9a3
--- /dev/null
+++ b/kernel/locking/test-ww_mutex.c
@@ -0,0 +1,646 @@
+/*
+ * Module-based API test facility for ww_mutexes
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ */
+
+#include <linux/kernel.h>
+
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/ww_mutex.h>
+
+static DEFINE_WW_CLASS(ww_class);
+struct workqueue_struct *wq;
+
+struct test_mutex {
+ struct work_struct work;
+ struct ww_mutex mutex;
+ struct completion ready, go, done;
+ unsigned int flags;
+};
+
+#define TEST_MTX_SPIN BIT(0)
+#define TEST_MTX_TRY BIT(1)
+#define TEST_MTX_CTX BIT(2)
+#define __TEST_MTX_LAST BIT(3)
+
+static void test_mutex_work(struct work_struct *work)
+{
+ struct test_mutex *mtx = container_of(work, typeof(*mtx), work);
+
+ complete(&mtx->ready);
+ wait_for_completion(&mtx->go);
+
+ if (mtx->flags & TEST_MTX_TRY) {
+ while (!ww_mutex_trylock(&mtx->mutex))
+ cpu_relax();
+ } else {
+ ww_mutex_lock(&mtx->mutex, NULL);
+ }
+ complete(&mtx->done);
+ ww_mutex_unlock(&mtx->mutex);
+}
+
+static int __test_mutex(unsigned int flags)
+{
+#define TIMEOUT (HZ / 16)
+ struct test_mutex mtx;
+ struct ww_acquire_ctx ctx;
+ int ret;
+
+ ww_mutex_init(&mtx.mutex, &ww_class);
+ ww_acquire_init(&ctx, &ww_class);
+
+ INIT_WORK_ONSTACK(&mtx.work, test_mutex_work);
+ init_completion(&mtx.ready);
+ init_completion(&mtx.go);
+ init_completion(&mtx.done);
+ mtx.flags = flags;
+
+ schedule_work(&mtx.work);
+
+ wait_for_completion(&mtx.ready);
+ ww_mutex_lock(&mtx.mutex, (flags & TEST_MTX_CTX) ? &ctx : NULL);
+ complete(&mtx.go);
+ if (flags & TEST_MTX_SPIN) {
+ unsigned long timeout = jiffies + TIMEOUT;
+
+ ret = 0;
+ do {
+ if (completion_done(&mtx.done)) {
+ ret = -EINVAL;
+ break;
+ }
+ cpu_relax();
+ } while (time_before(jiffies, timeout));
+ } else {
+ ret = wait_for_completion_timeout(&mtx.done, TIMEOUT);
+ }
+ ww_mutex_unlock(&mtx.mutex);
+ ww_acquire_fini(&ctx);
+
+ if (ret) {
+ pr_err("%s(flags=%x): mutual exclusion failure\n",
+ __func__, flags);
+ ret = -EINVAL;
+ }
+
+ flush_work(&mtx.work);
+ destroy_work_on_stack(&mtx.work);
+ return ret;
+#undef TIMEOUT
+}
+
+static int test_mutex(void)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < __TEST_MTX_LAST; i++) {
+ ret = __test_mutex(i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int test_aa(void)
+{
+ struct ww_mutex mutex;
+ struct ww_acquire_ctx ctx;
+ int ret;
+
+ ww_mutex_init(&mutex, &ww_class);
+ ww_acquire_init(&ctx, &ww_class);
+
+ ww_mutex_lock(&mutex, &ctx);
+
+ if (ww_mutex_trylock(&mutex)) {
+ pr_err("%s: trylocked itself!\n", __func__);
+ ww_mutex_unlock(&mutex);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = ww_mutex_lock(&mutex, &ctx);
+ if (ret != -EALREADY) {
+ pr_err("%s: missed deadlock for recursing, ret=%d\n",
+ __func__, ret);
+ if (!ret)
+ ww_mutex_unlock(&mutex);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = 0;
+out:
+ ww_mutex_unlock(&mutex);
+ ww_acquire_fini(&ctx);
+ return ret;
+}
+
+struct test_abba {
+ struct work_struct work;
+ struct ww_mutex a_mutex;
+ struct ww_mutex b_mutex;
+ struct completion a_ready;
+ struct completion b_ready;
+ bool resolve;
+ int result;
+};
+
+static void test_abba_work(struct work_struct *work)
+{
+ struct test_abba *abba = container_of(work, typeof(*abba), work);
+ struct ww_acquire_ctx ctx;
+ int err;
+
+ ww_acquire_init(&ctx, &ww_class);
+ ww_mutex_lock(&abba->b_mutex, &ctx);
+
+ complete(&abba->b_ready);
+ wait_for_completion(&abba->a_ready);
+
+ err = ww_mutex_lock(&abba->a_mutex, &ctx);
+ if (abba->resolve && err == -EDEADLK) {
+ ww_mutex_unlock(&abba->b_mutex);
+ ww_mutex_lock_slow(&abba->a_mutex, &ctx);
+ err = ww_mutex_lock(&abba->b_mutex, &ctx);
+ }
+
+ if (!err)
+ ww_mutex_unlock(&abba->a_mutex);
+ ww_mutex_unlock(&abba->b_mutex);
+ ww_acquire_fini(&ctx);
+
+ abba->result = err;
+}
+
+static int test_abba(bool resolve)
+{
+ struct test_abba abba;
+ struct ww_acquire_ctx ctx;
+ int err, ret;
+
+ ww_mutex_init(&abba.a_mutex, &ww_class);
+ ww_mutex_init(&abba.b_mutex, &ww_class);
+ INIT_WORK_ONSTACK(&abba.work, test_abba_work);
+ init_completion(&abba.a_ready);
+ init_completion(&abba.b_ready);
+ abba.resolve = resolve;
+
+ schedule_work(&abba.work);
+
+ ww_acquire_init(&ctx, &ww_class);
+ ww_mutex_lock(&abba.a_mutex, &ctx);
+
+ complete(&abba.a_ready);
+ wait_for_completion(&abba.b_ready);
+
+ err = ww_mutex_lock(&abba.b_mutex, &ctx);
+ if (resolve && err == -EDEADLK) {
+ ww_mutex_unlock(&abba.a_mutex);
+ ww_mutex_lock_slow(&abba.b_mutex, &ctx);
+ err = ww_mutex_lock(&abba.a_mutex, &ctx);
+ }
+
+ if (!err)
+ ww_mutex_unlock(&abba.b_mutex);
+ ww_mutex_unlock(&abba.a_mutex);
+ ww_acquire_fini(&ctx);
+
+ flush_work(&abba.work);
+ destroy_work_on_stack(&abba.work);
+
+ ret = 0;
+ if (resolve) {
+ if (err || abba.result) {
+ pr_err("%s: failed to resolve ABBA deadlock, A err=%d, B err=%d\n",
+ __func__, err, abba.result);
+ ret = -EINVAL;
+ }
+ } else {
+ if (err != -EDEADLK && abba.result != -EDEADLK) {
+ pr_err("%s: missed ABBA deadlock, A err=%d, B err=%d\n",
+ __func__, err, abba.result);
+ ret = -EINVAL;
+ }
+ }
+ return ret;
+}
+
+struct test_cycle {
+ struct work_struct work;
+ struct ww_mutex a_mutex;
+ struct ww_mutex *b_mutex;
+ struct completion *a_signal;
+ struct completion b_signal;
+ int result;
+};
+
+static void test_cycle_work(struct work_struct *work)
+{
+ struct test_cycle *cycle = container_of(work, typeof(*cycle), work);
+ struct ww_acquire_ctx ctx;
+ int err;
+
+ ww_acquire_init(&ctx, &ww_class);
+ ww_mutex_lock(&cycle->a_mutex, &ctx);
+
+ complete(cycle->a_signal);
+ wait_for_completion(&cycle->b_signal);
+
+ err = ww_mutex_lock(cycle->b_mutex, &ctx);
+ if (err == -EDEADLK) {
+ ww_mutex_unlock(&cycle->a_mutex);
+ ww_mutex_lock_slow(cycle->b_mutex, &ctx);
+ err = ww_mutex_lock(&cycle->a_mutex, &ctx);
+ }
+
+ if (!err)
+ ww_mutex_unlock(cycle->b_mutex);
+ ww_mutex_unlock(&cycle->a_mutex);
+ ww_acquire_fini(&ctx);
+
+ cycle->result = err;
+}
+
+static int __test_cycle(unsigned int nthreads)
+{
+ struct test_cycle *cycles;
+ unsigned int n, last = nthreads - 1;
+ int ret;
+
+ cycles = kmalloc_array(nthreads, sizeof(*cycles), GFP_KERNEL);
+ if (!cycles)
+ return -ENOMEM;
+
+ for (n = 0; n < nthreads; n++) {
+ struct test_cycle *cycle = &cycles[n];
+
+ ww_mutex_init(&cycle->a_mutex, &ww_class);
+ if (n == last)
+ cycle->b_mutex = &cycles[0].a_mutex;
+ else
+ cycle->b_mutex = &cycles[n + 1].a_mutex;
+
+ if (n == 0)
+ cycle->a_signal = &cycles[last].b_signal;
+ else
+ cycle->a_signal = &cycles[n - 1].b_signal;
+ init_completion(&cycle->b_signal);
+
+ INIT_WORK(&cycle->work, test_cycle_work);
+ cycle->result = 0;
+ }
+
+ for (n = 0; n < nthreads; n++)
+ queue_work(wq, &cycles[n].work);
+
+ flush_workqueue(wq);
+
+ ret = 0;
+ for (n = 0; n < nthreads; n++) {
+ struct test_cycle *cycle = &cycles[n];
+
+ if (!cycle->result)
+ continue;
+
+ pr_err("cylic deadlock not resolved, ret[%d/%d] = %d\n",
+ n, nthreads, cycle->result);
+ ret = -EINVAL;
+ break;
+ }
+
+ for (n = 0; n < nthreads; n++)
+ ww_mutex_destroy(&cycles[n].a_mutex);
+ kfree(cycles);
+ return ret;
+}
+
+static int test_cycle(unsigned int ncpus)
+{
+ unsigned int n;
+ int ret;
+
+ for (n = 2; n <= ncpus + 1; n++) {
+ ret = __test_cycle(n);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+struct stress {
+ struct work_struct work;
+ struct ww_mutex *locks;
+ int nlocks;
+ int nloops;
+};
+
+static int *get_random_order(int count)
+{
+ int *order;
+ int n, r, tmp;
+
+ order = kmalloc_array(count, sizeof(*order), GFP_TEMPORARY);
+ if (!order)
+ return order;
+
+ for (n = 0; n < count; n++)
+ order[n] = n;
+
+ for (n = count - 1; n > 1; n--) {
+ r = get_random_int() % (n + 1);
+ if (r != n) {
+ tmp = order[n];
+ order[n] = order[r];
+ order[r] = tmp;
+ }
+ }
+
+ return order;
+}
+
+static void dummy_load(struct stress *stress)
+{
+ usleep_range(1000, 2000);
+}
+
+static void stress_inorder_work(struct work_struct *work)
+{
+ struct stress *stress = container_of(work, typeof(*stress), work);
+ const int nlocks = stress->nlocks;
+ struct ww_mutex *locks = stress->locks;
+ struct ww_acquire_ctx ctx;
+ int *order;
+
+ order = get_random_order(nlocks);
+ if (!order)
+ return;
+
+ ww_acquire_init(&ctx, &ww_class);
+
+ do {
+ int contended = -1;
+ int n, err;
+
+retry:
+ err = 0;
+ for (n = 0; n < nlocks; n++) {
+ if (n == contended)
+ continue;
+
+ err = ww_mutex_lock(&locks[order[n]], &ctx);
+ if (err < 0)
+ break;
+ }
+ if (!err)
+ dummy_load(stress);
+
+ if (contended > n)
+ ww_mutex_unlock(&locks[order[contended]]);
+ contended = n;
+ while (n--)
+ ww_mutex_unlock(&locks[order[n]]);
+
+ if (err == -EDEADLK) {
+ ww_mutex_lock_slow(&locks[order[contended]], &ctx);
+ goto retry;
+ }
+
+ if (err) {
+ pr_err_once("stress (%s) failed with %d\n",
+ __func__, err);
+ break;
+ }
+ } while (--stress->nloops);
+
+ ww_acquire_fini(&ctx);
+
+ kfree(order);
+ kfree(stress);
+}
+
+struct reorder_lock {
+ struct list_head link;
+ struct ww_mutex *lock;
+};
+
+static void stress_reorder_work(struct work_struct *work)
+{
+ struct stress *stress = container_of(work, typeof(*stress), work);
+ LIST_HEAD(locks);
+ struct ww_acquire_ctx ctx;
+ struct reorder_lock *ll, *ln;
+ int *order;
+ int n, err;
+
+ order = get_random_order(stress->nlocks);
+ if (!order)
+ return;
+
+ for (n = 0; n < stress->nlocks; n++) {
+ ll = kmalloc(sizeof(*ll), GFP_KERNEL);
+ if (!ll)
+ goto out;
+
+ ll->lock = &stress->locks[order[n]];
+ list_add(&ll->link, &locks);
+ }
+ kfree(order);
+ order = NULL;
+
+ ww_acquire_init(&ctx, &ww_class);
+
+ do {
+ list_for_each_entry(ll, &locks, link) {
+ err = ww_mutex_lock(ll->lock, &ctx);
+ if (!err)
+ continue;
+
+ ln = ll;
+ list_for_each_entry_continue_reverse(ln, &locks, link)
+ ww_mutex_unlock(ln->lock);
+
+ if (err != -EDEADLK) {
+ pr_err_once("stress (%s) failed with %d\n",
+ __func__, err);
+ break;
+ }
+
+ ww_mutex_lock_slow(ll->lock, &ctx);
+ list_move(&ll->link, &locks); /* restarts iteration */
+ }
+
+ dummy_load(stress);
+ list_for_each_entry(ll, &locks, link)
+ ww_mutex_unlock(ll->lock);
+ } while (--stress->nloops);
+
+ ww_acquire_fini(&ctx);
+
+out:
+ list_for_each_entry_safe(ll, ln, &locks, link)
+ kfree(ll);
+ kfree(order);
+ kfree(stress);
+}
+
+static void stress_one_work(struct work_struct *work)
+{
+ struct stress *stress = container_of(work, typeof(*stress), work);
+ const int nlocks = stress->nlocks;
+ struct ww_mutex *lock = stress->locks + (get_random_int() % nlocks);
+ int err;
+
+ do {
+ err = ww_mutex_lock(lock, NULL);
+ if (!err) {
+ dummy_load(stress);
+ ww_mutex_unlock(lock);
+ } else {
+ pr_err_once("stress (%s) failed with %d\n",
+ __func__, err);
+ break;
+ }
+ } while (--stress->nloops);
+
+ kfree(stress);
+}
+
+#define STRESS_INORDER BIT(0)
+#define STRESS_REORDER BIT(1)
+#define STRESS_ONE BIT(2)
+#define STRESS_ALL (STRESS_INORDER | STRESS_REORDER | STRESS_ONE)
+
+static int stress(int nlocks, int nthreads, int nloops, unsigned int flags)
+{
+ struct ww_mutex *locks;
+ int n;
+
+ locks = kmalloc_array(nlocks, sizeof(*locks), GFP_KERNEL);
+ if (!locks)
+ return -ENOMEM;
+
+ for (n = 0; n < nlocks; n++)
+ ww_mutex_init(&locks[n], &ww_class);
+
+ for (n = 0; nthreads; n++) {
+ struct stress *stress;
+ void (*fn)(struct work_struct *work);
+
+ fn = NULL;
+ switch (n & 3) {
+ case 0:
+ if (flags & STRESS_INORDER)
+ fn = stress_inorder_work;
+ break;
+ case 1:
+ if (flags & STRESS_REORDER)
+ fn = stress_reorder_work;
+ break;
+ case 2:
+ if (flags & STRESS_ONE)
+ fn = stress_one_work;
+ break;
+ }
+
+ if (!fn)
+ continue;
+
+ stress = kmalloc(sizeof(*stress), GFP_KERNEL);
+ if (!stress)
+ break;
+
+ INIT_WORK(&stress->work, fn);
+ stress->locks = locks;
+ stress->nlocks = nlocks;
+ stress->nloops = nloops;
+
+ queue_work(wq, &stress->work);
+ nthreads--;
+ }
+
+ flush_workqueue(wq);
+
+ for (n = 0; n < nlocks; n++)
+ ww_mutex_destroy(&locks[n]);
+ kfree(locks);
+
+ return 0;
+}
+
+static int __init test_ww_mutex_init(void)
+{
+ int ncpus = num_online_cpus();
+ int ret;
+
+ wq = alloc_workqueue("test-ww_mutex", WQ_UNBOUND, 0);
+ if (!wq)
+ return -ENOMEM;
+
+ ret = test_mutex();
+ if (ret)
+ return ret;
+
+ ret = test_aa();
+ if (ret)
+ return ret;
+
+ ret = test_abba(false);
+ if (ret)
+ return ret;
+
+ ret = test_abba(true);
+ if (ret)
+ return ret;
+
+ ret = test_cycle(ncpus);
+ if (ret)
+ return ret;
+
+ ret = stress(16, 2*ncpus, 1<<10, STRESS_INORDER);
+ if (ret)
+ return ret;
+
+ ret = stress(16, 2*ncpus, 1<<10, STRESS_REORDER);
+ if (ret)
+ return ret;
+
+ ret = stress(4096, hweight32(STRESS_ALL)*ncpus, 1<<12, STRESS_ALL);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void __exit test_ww_mutex_exit(void)
+{
+ destroy_workqueue(wq);
+}
+
+module_init(test_ww_mutex_init);
+module_exit(test_ww_mutex_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Intel Corporation");
diff --git a/kernel/membarrier.c b/kernel/membarrier.c
index 536c727..9f9284f 100644
--- a/kernel/membarrier.c
+++ b/kernel/membarrier.c
@@ -16,6 +16,7 @@
#include <linux/syscalls.h>
#include <linux/membarrier.h>
+#include <linux/tick.h>
/*
* Bitmask made from a "or" of all commands within enum membarrier_cmd,
@@ -51,6 +52,9 @@
*/
SYSCALL_DEFINE2(membarrier, int, cmd, int, flags)
{
+ /* MEMBARRIER_CMD_SHARED is not compatible with nohz_full. */
+ if (tick_nohz_full_enabled())
+ return -ENOSYS;
if (unlikely(flags))
return -EINVAL;
switch (cmd) {
diff --git a/kernel/pid.c b/kernel/pid.c
index f66162f..0291804 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -68,9 +68,7 @@
* the scheme scales to up to 4 million PIDs, runtime.
*/
struct pid_namespace init_pid_ns = {
- .kref = {
- .refcount = ATOMIC_INIT(2),
- },
+ .kref = KREF_INIT(2),
.pidmap = {
[ 0 ... PIDMAP_ENTRIES-1] = { ATOMIC_INIT(BITS_PER_PAGE), NULL }
},
diff --git a/kernel/power/suspend_test.c b/kernel/power/suspend_test.c
index bdff5ed..5db2170 100644
--- a/kernel/power/suspend_test.c
+++ b/kernel/power/suspend_test.c
@@ -166,7 +166,7 @@
return 0;
}
- for (i = 0; pm_labels[i]; i++)
+ for (i = PM_SUSPEND_MIN; i < PM_SUSPEND_MAX; i++)
if (!strcmp(pm_labels[i], suspend_type)) {
test_state_label = pm_labels[i];
return 0;
diff --git a/kernel/power/swap.c b/kernel/power/swap.c
index 32e0c23..f80fd33 100644
--- a/kernel/power/swap.c
+++ b/kernel/power/swap.c
@@ -201,7 +201,7 @@
struct swsusp_extent *ext;
unsigned long offset;
- ext = container_of(node, struct swsusp_extent, node);
+ ext = rb_entry(node, struct swsusp_extent, node);
rb_erase(node, &swsusp_extents);
for (offset = ext->start; offset <= ext->end; offset++)
swap_free(swp_entry(swap, offset));
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 8b26964..4ba3d34 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -1516,7 +1516,7 @@
{
struct console *con;
- trace_console(text, len);
+ trace_console_rcuidle(text, len);
if (!console_drivers)
return;
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
index 87c5122..d81345b 100644
--- a/kernel/rcu/rcutorture.c
+++ b/kernel/rcu/rcutorture.c
@@ -564,10 +564,25 @@
pr_alert("%s%s per-CPU(idx=%d):",
torture_type, TORTURE_FLAG, idx);
for_each_possible_cpu(cpu) {
+ unsigned long l0, l1;
+ unsigned long u0, u1;
long c0, c1;
+ struct srcu_array *counts = per_cpu_ptr(srcu_ctlp->per_cpu_ref, cpu);
- c0 = (long)per_cpu_ptr(srcu_ctlp->per_cpu_ref, cpu)->c[!idx];
- c1 = (long)per_cpu_ptr(srcu_ctlp->per_cpu_ref, cpu)->c[idx];
+ u0 = counts->unlock_count[!idx];
+ u1 = counts->unlock_count[idx];
+
+ /*
+ * Make sure that a lock is always counted if the corresponding
+ * unlock is counted.
+ */
+ smp_rmb();
+
+ l0 = counts->lock_count[!idx];
+ l1 = counts->lock_count[idx];
+
+ c0 = l0 - u0;
+ c1 = l1 - u1;
pr_cont(" %d(%ld,%ld)", cpu, c0, c1);
}
pr_cont("\n");
diff --git a/kernel/rcu/srcu.c b/kernel/rcu/srcu.c
index 9b9cdd5..e773129 100644
--- a/kernel/rcu/srcu.c
+++ b/kernel/rcu/srcu.c
@@ -106,7 +106,7 @@
rcu_batch_init(&sp->batch_check1);
rcu_batch_init(&sp->batch_done);
INIT_DELAYED_WORK(&sp->work, process_srcu);
- sp->per_cpu_ref = alloc_percpu(struct srcu_struct_array);
+ sp->per_cpu_ref = alloc_percpu(struct srcu_array);
return sp->per_cpu_ref ? 0 : -ENOMEM;
}
@@ -141,114 +141,77 @@
#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
/*
- * Returns approximate total of the readers' ->seq[] values for the
+ * Returns approximate total of the readers' ->lock_count[] values for the
* rank of per-CPU counters specified by idx.
*/
-static unsigned long srcu_readers_seq_idx(struct srcu_struct *sp, int idx)
+static unsigned long srcu_readers_lock_idx(struct srcu_struct *sp, int idx)
{
int cpu;
unsigned long sum = 0;
- unsigned long t;
for_each_possible_cpu(cpu) {
- t = READ_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->seq[idx]);
- sum += t;
+ struct srcu_array *cpuc = per_cpu_ptr(sp->per_cpu_ref, cpu);
+
+ sum += READ_ONCE(cpuc->lock_count[idx]);
}
return sum;
}
/*
- * Returns approximate number of readers active on the specified rank
- * of the per-CPU ->c[] counters.
+ * Returns approximate total of the readers' ->unlock_count[] values for the
+ * rank of per-CPU counters specified by idx.
*/
-static unsigned long srcu_readers_active_idx(struct srcu_struct *sp, int idx)
+static unsigned long srcu_readers_unlock_idx(struct srcu_struct *sp, int idx)
{
int cpu;
unsigned long sum = 0;
- unsigned long t;
for_each_possible_cpu(cpu) {
- t = READ_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->c[idx]);
- sum += t;
+ struct srcu_array *cpuc = per_cpu_ptr(sp->per_cpu_ref, cpu);
+
+ sum += READ_ONCE(cpuc->unlock_count[idx]);
}
return sum;
}
/*
* Return true if the number of pre-existing readers is determined to
- * be stably zero. An example unstable zero can occur if the call
- * to srcu_readers_active_idx() misses an __srcu_read_lock() increment,
- * but due to task migration, sees the corresponding __srcu_read_unlock()
- * decrement. This can happen because srcu_readers_active_idx() takes
- * time to sum the array, and might in fact be interrupted or preempted
- * partway through the summation.
+ * be zero.
*/
static bool srcu_readers_active_idx_check(struct srcu_struct *sp, int idx)
{
- unsigned long seq;
+ unsigned long unlocks;
- seq = srcu_readers_seq_idx(sp, idx);
+ unlocks = srcu_readers_unlock_idx(sp, idx);
/*
- * The following smp_mb() A pairs with the smp_mb() B located in
- * __srcu_read_lock(). This pairing ensures that if an
- * __srcu_read_lock() increments its counter after the summation
- * in srcu_readers_active_idx(), then the corresponding SRCU read-side
- * critical section will see any changes made prior to the start
- * of the current SRCU grace period.
+ * Make sure that a lock is always counted if the corresponding unlock
+ * is counted. Needs to be a smp_mb() as the read side may contain a
+ * read from a variable that is written to before the synchronize_srcu()
+ * in the write side. In this case smp_mb()s A and B act like the store
+ * buffering pattern.
*
- * Also, if the above call to srcu_readers_seq_idx() saw the
- * increment of ->seq[], then the call to srcu_readers_active_idx()
- * must see the increment of ->c[].
+ * This smp_mb() also pairs with smp_mb() C to prevent accesses after the
+ * synchronize_srcu() from being executed before the grace period ends.
*/
smp_mb(); /* A */
/*
- * Note that srcu_readers_active_idx() can incorrectly return
- * zero even though there is a pre-existing reader throughout.
- * To see this, suppose that task A is in a very long SRCU
- * read-side critical section that started on CPU 0, and that
- * no other reader exists, so that the sum of the counters
- * is equal to one. Then suppose that task B starts executing
- * srcu_readers_active_idx(), summing up to CPU 1, and then that
- * task C starts reading on CPU 0, so that its increment is not
- * summed, but finishes reading on CPU 2, so that its decrement
- * -is- summed. Then when task B completes its sum, it will
- * incorrectly get zero, despite the fact that task A has been
- * in its SRCU read-side critical section the whole time.
+ * If the locks are the same as the unlocks, then there must have
+ * been no readers on this index at some time in between. This does not
+ * mean that there are no more readers, as one could have read the
+ * current index but not have incremented the lock counter yet.
*
- * We therefore do a validation step should srcu_readers_active_idx()
- * return zero.
+ * Possible bug: There is no guarantee that there haven't been ULONG_MAX
+ * increments of ->lock_count[] since the unlocks were counted, meaning
+ * that this could return true even if there are still active readers.
+ * Since there are no memory barriers around srcu_flip(), the CPU is not
+ * required to increment ->completed before running
+ * srcu_readers_unlock_idx(), which means that there could be an
+ * arbitrarily large number of critical sections that execute after
+ * srcu_readers_unlock_idx() but use the old value of ->completed.
*/
- if (srcu_readers_active_idx(sp, idx) != 0)
- return false;
-
- /*
- * The remainder of this function is the validation step.
- * The following smp_mb() D pairs with the smp_mb() C in
- * __srcu_read_unlock(). If the __srcu_read_unlock() was seen
- * by srcu_readers_active_idx() above, then any destructive
- * operation performed after the grace period will happen after
- * the corresponding SRCU read-side critical section.
- *
- * Note that there can be at most NR_CPUS worth of readers using
- * the old index, which is not enough to overflow even a 32-bit
- * integer. (Yes, this does mean that systems having more than
- * a billion or so CPUs need to be 64-bit systems.) Therefore,
- * the sum of the ->seq[] counters cannot possibly overflow.
- * Therefore, the only way that the return values of the two
- * calls to srcu_readers_seq_idx() can be equal is if there were
- * no increments of the corresponding rank of ->seq[] counts
- * in the interim. But the missed-increment scenario laid out
- * above includes an increment of the ->seq[] counter by
- * the corresponding __srcu_read_lock(). Therefore, if this
- * scenario occurs, the return values from the two calls to
- * srcu_readers_seq_idx() will differ, and thus the validation
- * step below suffices.
- */
- smp_mb(); /* D */
-
- return srcu_readers_seq_idx(sp, idx) == seq;
+ return srcu_readers_lock_idx(sp, idx) == unlocks;
}
/**
@@ -266,8 +229,12 @@
unsigned long sum = 0;
for_each_possible_cpu(cpu) {
- sum += READ_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->c[0]);
- sum += READ_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->c[1]);
+ struct srcu_array *cpuc = per_cpu_ptr(sp->per_cpu_ref, cpu);
+
+ sum += READ_ONCE(cpuc->lock_count[0]);
+ sum += READ_ONCE(cpuc->lock_count[1]);
+ sum -= READ_ONCE(cpuc->unlock_count[0]);
+ sum -= READ_ONCE(cpuc->unlock_count[1]);
}
return sum;
}
@@ -298,9 +265,8 @@
int idx;
idx = READ_ONCE(sp->completed) & 0x1;
- __this_cpu_inc(sp->per_cpu_ref->c[idx]);
+ __this_cpu_inc(sp->per_cpu_ref->lock_count[idx]);
smp_mb(); /* B */ /* Avoid leaking the critical section. */
- __this_cpu_inc(sp->per_cpu_ref->seq[idx]);
return idx;
}
EXPORT_SYMBOL_GPL(__srcu_read_lock);
@@ -314,7 +280,7 @@
void __srcu_read_unlock(struct srcu_struct *sp, int idx)
{
smp_mb(); /* C */ /* Avoid leaking the critical section. */
- this_cpu_dec(sp->per_cpu_ref->c[idx]);
+ this_cpu_inc(sp->per_cpu_ref->unlock_count[idx]);
}
EXPORT_SYMBOL_GPL(__srcu_read_unlock);
@@ -349,12 +315,21 @@
/*
* Increment the ->completed counter so that future SRCU readers will
- * use the other rank of the ->c[] and ->seq[] arrays. This allows
+ * use the other rank of the ->(un)lock_count[] arrays. This allows
* us to wait for pre-existing readers in a starvation-free manner.
*/
static void srcu_flip(struct srcu_struct *sp)
{
- sp->completed++;
+ WRITE_ONCE(sp->completed, sp->completed + 1);
+
+ /*
+ * Ensure that if the updater misses an __srcu_read_unlock()
+ * increment, that task's next __srcu_read_lock() will see the
+ * above counter update. Note that both this memory barrier
+ * and the one in srcu_readers_active_idx_check() provide the
+ * guarantee for __srcu_read_lock().
+ */
+ smp_mb(); /* D */ /* Pairs with C. */
}
/*
@@ -392,6 +367,7 @@
head->next = NULL;
head->func = func;
spin_lock_irqsave(&sp->queue_lock, flags);
+ smp_mb__after_unlock_lock(); /* Caller's prior accesses before GP. */
rcu_batch_queue(&sp->batch_queue, head);
if (!sp->running) {
sp->running = true;
@@ -425,6 +401,7 @@
head->next = NULL;
head->func = wakeme_after_rcu;
spin_lock_irq(&sp->queue_lock);
+ smp_mb__after_unlock_lock(); /* Caller's prior accesses before GP. */
if (!sp->running) {
/* steal the processing owner */
sp->running = true;
@@ -444,8 +421,11 @@
spin_unlock_irq(&sp->queue_lock);
}
- if (!done)
+ if (!done) {
wait_for_completion(&rcu.completion);
+ smp_mb(); /* Caller's later accesses after GP. */
+ }
+
}
/**
@@ -613,7 +593,8 @@
/*
* Invoke a limited number of SRCU callbacks that have passed through
* their grace period. If there are more to do, SRCU will reschedule
- * the workqueue.
+ * the workqueue. Note that needed memory barriers have been executed
+ * in this task's context by srcu_readers_active_idx_check().
*/
static void srcu_invoke_callbacks(struct srcu_struct *sp)
{
diff --git a/kernel/rcu/tiny.c b/kernel/rcu/tiny.c
index b23a4d0..fa6a48d 100644
--- a/kernel/rcu/tiny.c
+++ b/kernel/rcu/tiny.c
@@ -41,8 +41,6 @@
/* Forward declarations for tiny_plugin.h. */
struct rcu_ctrlblk;
-static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp);
-static void rcu_process_callbacks(struct softirq_action *unused);
static void __call_rcu(struct rcu_head *head,
rcu_callback_t func,
struct rcu_ctrlblk *rcp);
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index cb4e205..d80e0d2 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -281,6 +281,116 @@
#endif /* #ifdef CONFIG_NO_HZ_FULL_SYSIDLE */
};
+/*
+ * Record entry into an extended quiescent state. This is only to be
+ * called when not already in an extended quiescent state.
+ */
+static void rcu_dynticks_eqs_enter(void)
+{
+ struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks);
+ int special;
+
+ /*
+ * CPUs seeing atomic_inc_return() must see prior RCU read-side
+ * critical sections, and we also must force ordering with the
+ * next idle sojourn.
+ */
+ special = atomic_inc_return(&rdtp->dynticks);
+ WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && special & 0x1);
+}
+
+/*
+ * Record exit from an extended quiescent state. This is only to be
+ * called from an extended quiescent state.
+ */
+static void rcu_dynticks_eqs_exit(void)
+{
+ struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks);
+ int special;
+
+ /*
+ * CPUs seeing atomic_inc_return() must see prior idle sojourns,
+ * and we also must force ordering with the next RCU read-side
+ * critical section.
+ */
+ special = atomic_inc_return(&rdtp->dynticks);
+ WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && !(special & 0x1));
+}
+
+/*
+ * Reset the current CPU's ->dynticks counter to indicate that the
+ * newly onlined CPU is no longer in an extended quiescent state.
+ * This will either leave the counter unchanged, or increment it
+ * to the next non-quiescent value.
+ *
+ * The non-atomic test/increment sequence works because the upper bits
+ * of the ->dynticks counter are manipulated only by the corresponding CPU,
+ * or when the corresponding CPU is offline.
+ */
+static void rcu_dynticks_eqs_online(void)
+{
+ struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks);
+
+ if (atomic_read(&rdtp->dynticks) & 0x1)
+ return;
+ atomic_add(0x1, &rdtp->dynticks);
+}
+
+/*
+ * Is the current CPU in an extended quiescent state?
+ *
+ * No ordering, as we are sampling CPU-local information.
+ */
+bool rcu_dynticks_curr_cpu_in_eqs(void)
+{
+ struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks);
+
+ return !(atomic_read(&rdtp->dynticks) & 0x1);
+}
+
+/*
+ * Snapshot the ->dynticks counter with full ordering so as to allow
+ * stable comparison of this counter with past and future snapshots.
+ */
+int rcu_dynticks_snap(struct rcu_dynticks *rdtp)
+{
+ int snap = atomic_add_return(0, &rdtp->dynticks);
+
+ return snap;
+}
+
+/*
+ * Return true if the snapshot returned from rcu_dynticks_snap()
+ * indicates that RCU is in an extended quiescent state.
+ */
+static bool rcu_dynticks_in_eqs(int snap)
+{
+ return !(snap & 0x1);
+}
+
+/*
+ * Return true if the CPU corresponding to the specified rcu_dynticks
+ * structure has spent some time in an extended quiescent state since
+ * rcu_dynticks_snap() returned the specified snapshot.
+ */
+static bool rcu_dynticks_in_eqs_since(struct rcu_dynticks *rdtp, int snap)
+{
+ return snap != rcu_dynticks_snap(rdtp);
+}
+
+/*
+ * Do a double-increment of the ->dynticks counter to emulate a
+ * momentary idle-CPU quiescent state.
+ */
+static void rcu_dynticks_momentary_idle(void)
+{
+ struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks);
+ int special = atomic_add_return(2, &rdtp->dynticks);
+
+ /* It is illegal to call this from idle state. */
+ WARN_ON_ONCE(!(special & 0x1));
+}
+
DEFINE_PER_CPU_SHARED_ALIGNED(unsigned long, rcu_qs_ctr);
EXPORT_PER_CPU_SYMBOL_GPL(rcu_qs_ctr);
@@ -300,7 +410,6 @@
static void rcu_momentary_dyntick_idle(void)
{
struct rcu_data *rdp;
- struct rcu_dynticks *rdtp;
int resched_mask;
struct rcu_state *rsp;
@@ -327,10 +436,7 @@
* quiescent state, with no need for this CPU to do anything
* further.
*/
- rdtp = this_cpu_ptr(&rcu_dynticks);
- smp_mb__before_atomic(); /* Earlier stuff before QS. */
- atomic_add(2, &rdtp->dynticks); /* QS. */
- smp_mb__after_atomic(); /* Later stuff after QS. */
+ rcu_dynticks_momentary_idle();
break;
}
}
@@ -611,7 +717,7 @@
cpu_has_callbacks_ready_to_invoke(struct rcu_data *rdp)
{
return &rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL] &&
- rdp->nxttail[RCU_DONE_TAIL] != NULL;
+ rdp->nxttail[RCU_NEXT_TAIL] != NULL;
}
/*
@@ -673,7 +779,7 @@
{
struct rcu_state *rsp;
struct rcu_data *rdp;
- struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks);
+ RCU_TRACE(struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks);)
trace_rcu_dyntick(TPS("Start"), oldval, rdtp->dynticks_nesting);
if (IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
@@ -692,12 +798,7 @@
do_nocb_deferred_wakeup(rdp);
}
rcu_prepare_for_idle();
- /* CPUs seeing atomic_inc() must see prior RCU read-side crit sects */
- smp_mb__before_atomic(); /* See above. */
- atomic_inc(&rdtp->dynticks);
- smp_mb__after_atomic(); /* Force ordering with next sojourn. */
- WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
- atomic_read(&rdtp->dynticks) & 0x1);
+ rcu_dynticks_eqs_enter();
rcu_dynticks_task_enter();
/*
@@ -826,15 +927,10 @@
*/
static void rcu_eqs_exit_common(long long oldval, int user)
{
- struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks);
+ RCU_TRACE(struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks);)
rcu_dynticks_task_exit();
- smp_mb__before_atomic(); /* Force ordering w/previous sojourn. */
- atomic_inc(&rdtp->dynticks);
- /* CPUs seeing atomic_inc() must see later RCU read-side crit sects */
- smp_mb__after_atomic(); /* See above. */
- WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
- !(atomic_read(&rdtp->dynticks) & 0x1));
+ rcu_dynticks_eqs_exit();
rcu_cleanup_after_idle();
trace_rcu_dyntick(TPS("End"), oldval, rdtp->dynticks_nesting);
if (IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
@@ -980,12 +1076,8 @@
* to be in the outermost NMI handler that interrupted an RCU-idle
* period (observation due to Andy Lutomirski).
*/
- if (!(atomic_read(&rdtp->dynticks) & 0x1)) {
- smp_mb__before_atomic(); /* Force delay from prior write. */
- atomic_inc(&rdtp->dynticks);
- /* atomic_inc() before later RCU read-side crit sects */
- smp_mb__after_atomic(); /* See above. */
- WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks) & 0x1));
+ if (rcu_dynticks_curr_cpu_in_eqs()) {
+ rcu_dynticks_eqs_exit();
incby = 1;
}
rdtp->dynticks_nmi_nesting += incby;
@@ -1010,7 +1102,7 @@
* to us!)
*/
WARN_ON_ONCE(rdtp->dynticks_nmi_nesting <= 0);
- WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks) & 0x1));
+ WARN_ON_ONCE(rcu_dynticks_curr_cpu_in_eqs());
/*
* If the nesting level is not 1, the CPU wasn't RCU-idle, so
@@ -1023,11 +1115,7 @@
/* This NMI interrupted an RCU-idle CPU, restore RCU-idleness. */
rdtp->dynticks_nmi_nesting = 0;
- /* CPUs seeing atomic_inc() must see prior RCU read-side crit sects */
- smp_mb__before_atomic(); /* See above. */
- atomic_inc(&rdtp->dynticks);
- smp_mb__after_atomic(); /* Force delay to next write. */
- WARN_ON_ONCE(atomic_read(&rdtp->dynticks) & 0x1);
+ rcu_dynticks_eqs_enter();
}
/**
@@ -1040,7 +1128,7 @@
*/
bool notrace __rcu_is_watching(void)
{
- return atomic_read(this_cpu_ptr(&rcu_dynticks.dynticks)) & 0x1;
+ return !rcu_dynticks_curr_cpu_in_eqs();
}
/**
@@ -1123,9 +1211,9 @@
static int dyntick_save_progress_counter(struct rcu_data *rdp,
bool *isidle, unsigned long *maxj)
{
- rdp->dynticks_snap = atomic_add_return(0, &rdp->dynticks->dynticks);
+ rdp->dynticks_snap = rcu_dynticks_snap(rdp->dynticks);
rcu_sysidle_check_cpu(rdp, isidle, maxj);
- if ((rdp->dynticks_snap & 0x1) == 0) {
+ if (rcu_dynticks_in_eqs(rdp->dynticks_snap)) {
trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, TPS("dti"));
if (ULONG_CMP_LT(READ_ONCE(rdp->gpnum) + ULONG_MAX / 4,
rdp->mynode->gpnum))
@@ -1144,12 +1232,10 @@
static int rcu_implicit_dynticks_qs(struct rcu_data *rdp,
bool *isidle, unsigned long *maxj)
{
- unsigned int curr;
+ unsigned long jtsq;
int *rcrmp;
- unsigned int snap;
-
- curr = (unsigned int)atomic_add_return(0, &rdp->dynticks->dynticks);
- snap = (unsigned int)rdp->dynticks_snap;
+ unsigned long rjtsc;
+ struct rcu_node *rnp;
/*
* If the CPU passed through or entered a dynticks idle phase with
@@ -1159,27 +1245,39 @@
* read-side critical section that started before the beginning
* of the current RCU grace period.
*/
- if ((curr & 0x1) == 0 || UINT_CMP_GE(curr, snap + 2)) {
+ if (rcu_dynticks_in_eqs_since(rdp->dynticks, rdp->dynticks_snap)) {
trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, TPS("dti"));
rdp->dynticks_fqs++;
return 1;
}
+ /* Compute and saturate jiffies_till_sched_qs. */
+ jtsq = jiffies_till_sched_qs;
+ rjtsc = rcu_jiffies_till_stall_check();
+ if (jtsq > rjtsc / 2) {
+ WRITE_ONCE(jiffies_till_sched_qs, rjtsc);
+ jtsq = rjtsc / 2;
+ } else if (jtsq < 1) {
+ WRITE_ONCE(jiffies_till_sched_qs, 1);
+ jtsq = 1;
+ }
+
/*
- * Check for the CPU being offline, but only if the grace period
- * is old enough. We don't need to worry about the CPU changing
- * state: If we see it offline even once, it has been through a
- * quiescent state.
- *
- * The reason for insisting that the grace period be at least
- * one jiffy old is that CPUs that are not quite online and that
- * have just gone offline can still execute RCU read-side critical
- * sections.
+ * Has this CPU encountered a cond_resched_rcu_qs() since the
+ * beginning of the grace period? For this to be the case,
+ * the CPU has to have noticed the current grace period. This
+ * might not be the case for nohz_full CPUs looping in the kernel.
*/
- if (ULONG_CMP_GE(rdp->rsp->gp_start + 2, jiffies))
- return 0; /* Grace period is not old enough. */
- barrier();
- if (cpu_is_offline(rdp->cpu)) {
+ rnp = rdp->mynode;
+ if (time_after(jiffies, rdp->rsp->gp_start + jtsq) &&
+ READ_ONCE(rdp->rcu_qs_ctr_snap) != per_cpu(rcu_qs_ctr, rdp->cpu) &&
+ READ_ONCE(rdp->gpnum) == rnp->gpnum && !rdp->gpwrap) {
+ trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, TPS("rqc"));
+ return 1;
+ }
+
+ /* Check for the CPU being offline. */
+ if (!(rdp->grpmask & rcu_rnp_online_cpus(rnp))) {
trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, TPS("ofl"));
rdp->offline_fqs++;
return 1;
@@ -1207,9 +1305,8 @@
* warning delay.
*/
rcrmp = &per_cpu(rcu_sched_qs_mask, rdp->cpu);
- if (ULONG_CMP_GE(jiffies,
- rdp->rsp->gp_start + jiffies_till_sched_qs) ||
- ULONG_CMP_GE(jiffies, rdp->rsp->jiffies_resched)) {
+ if (time_after(jiffies, rdp->rsp->gp_start + jtsq) ||
+ time_after(jiffies, rdp->rsp->jiffies_resched)) {
if (!(READ_ONCE(*rcrmp) & rdp->rsp->flavor_mask)) {
WRITE_ONCE(rdp->cond_resched_completed,
READ_ONCE(rdp->mynode->completed));
@@ -1220,11 +1317,12 @@
rdp->rsp->jiffies_resched += 5; /* Re-enable beating. */
}
- /* And if it has been a really long time, kick the CPU as well. */
- if (ULONG_CMP_GE(jiffies,
- rdp->rsp->gp_start + 2 * jiffies_till_sched_qs) ||
- ULONG_CMP_GE(jiffies, rdp->rsp->gp_start + jiffies_till_sched_qs))
- resched_cpu(rdp->cpu); /* Force CPU into scheduler. */
+ /*
+ * If more than halfway to RCU CPU stall-warning time, do
+ * a resched_cpu() to try to loosen things up a bit.
+ */
+ if (jiffies - rdp->rsp->gp_start > rcu_jiffies_till_stall_check() / 2)
+ resched_cpu(rdp->cpu);
return 0;
}
@@ -1277,7 +1375,10 @@
}
/*
- * Dump stacks of all tasks running on stalled CPUs.
+ * Dump stacks of all tasks running on stalled CPUs. First try using
+ * NMIs, but fall back to manual remote stack tracing on architectures
+ * that don't support NMI-based stack dumps. The NMI-triggered stack
+ * traces are more accurate because they are printed by the target CPU.
*/
static void rcu_dump_cpu_stacks(struct rcu_state *rsp)
{
@@ -1287,11 +1388,10 @@
rcu_for_each_leaf_node(rsp, rnp) {
raw_spin_lock_irqsave_rcu_node(rnp, flags);
- if (rnp->qsmask != 0) {
- for_each_leaf_node_possible_cpu(rnp, cpu)
- if (rnp->qsmask & leaf_node_cpu_bit(rnp, cpu))
+ for_each_leaf_node_possible_cpu(rnp, cpu)
+ if (rnp->qsmask & leaf_node_cpu_bit(rnp, cpu))
+ if (!trigger_single_cpu_backtrace(cpu))
dump_cpu_task(cpu);
- }
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
}
}
@@ -1379,6 +1479,9 @@
(long)rsp->gpnum, (long)rsp->completed, totqlen);
if (ndetected) {
rcu_dump_cpu_stacks(rsp);
+
+ /* Complain about tasks blocking the grace period. */
+ rcu_print_detail_task_stall(rsp);
} else {
if (READ_ONCE(rsp->gpnum) != gpnum ||
READ_ONCE(rsp->completed) == gpnum) {
@@ -1395,9 +1498,6 @@
}
}
- /* Complain about tasks blocking the grace period. */
- rcu_print_detail_task_stall(rsp);
-
rcu_check_gp_kthread_starvation(rsp);
panic_on_rcu_stall();
@@ -2467,10 +2567,8 @@
rnp = rdp->mynode;
raw_spin_lock_irqsave_rcu_node(rnp, flags);
- if ((rdp->cpu_no_qs.b.norm &&
- rdp->rcu_qs_ctr_snap == __this_cpu_read(rcu_qs_ctr)) ||
- rdp->gpnum != rnp->gpnum || rnp->completed == rnp->gpnum ||
- rdp->gpwrap) {
+ if (rdp->cpu_no_qs.b.norm || rdp->gpnum != rnp->gpnum ||
+ rnp->completed == rnp->gpnum || rdp->gpwrap) {
/*
* The grace period in which this quiescent state was
@@ -2525,8 +2623,7 @@
* Was there a quiescent state since the beginning of the grace
* period? If no, then exit and wait for the next call.
*/
- if (rdp->cpu_no_qs.b.norm &&
- rdp->rcu_qs_ctr_snap == __this_cpu_read(rcu_qs_ctr))
+ if (rdp->cpu_no_qs.b.norm)
return;
/*
@@ -3480,9 +3577,7 @@
rdp->core_needs_qs && rdp->cpu_no_qs.b.norm &&
rdp->rcu_qs_ctr_snap == __this_cpu_read(rcu_qs_ctr)) {
rdp->n_rp_core_needs_qs++;
- } else if (rdp->core_needs_qs &&
- (!rdp->cpu_no_qs.b.norm ||
- rdp->rcu_qs_ctr_snap != __this_cpu_read(rcu_qs_ctr))) {
+ } else if (rdp->core_needs_qs && !rdp->cpu_no_qs.b.norm) {
rdp->n_rp_report_qs++;
return 1;
}
@@ -3748,7 +3843,7 @@
rdp->grpmask = leaf_node_cpu_bit(rdp->mynode, cpu);
rdp->dynticks = &per_cpu(rcu_dynticks, cpu);
WARN_ON_ONCE(rdp->dynticks->dynticks_nesting != DYNTICK_TASK_EXIT_IDLE);
- WARN_ON_ONCE(atomic_read(&rdp->dynticks->dynticks) != 1);
+ WARN_ON_ONCE(rcu_dynticks_in_eqs(rcu_dynticks_snap(rdp->dynticks)));
rdp->cpu = cpu;
rdp->rsp = rsp;
rcu_boot_init_nocb_percpu_data(rdp);
@@ -3765,7 +3860,6 @@
rcu_init_percpu_data(int cpu, struct rcu_state *rsp)
{
unsigned long flags;
- unsigned long mask;
struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu);
struct rcu_node *rnp = rcu_get_root(rsp);
@@ -3778,8 +3872,7 @@
init_callback_list(rdp); /* Re-enable callbacks on this CPU. */
rdp->dynticks->dynticks_nesting = DYNTICK_TASK_EXIT_IDLE;
rcu_sysidle_init_percpu_data(rdp->dynticks);
- atomic_set(&rdp->dynticks->dynticks,
- (atomic_read(&rdp->dynticks->dynticks) & ~0x1) + 1);
+ rcu_dynticks_eqs_online();
raw_spin_unlock_rcu_node(rnp); /* irqs remain disabled. */
/*
@@ -3788,7 +3881,6 @@
* of the next grace period.
*/
rnp = rdp->mynode;
- mask = rdp->grpmask;
raw_spin_lock_rcu_node(rnp); /* irqs already disabled. */
if (!rdp->beenonline)
WRITE_ONCE(rsp->ncpus, READ_ONCE(rsp->ncpus) + 1);
@@ -3872,7 +3964,7 @@
struct rcu_state *rsp;
for_each_rcu_flavor(rsp) {
- rdp = this_cpu_ptr(rsp->rda);
+ rdp = per_cpu_ptr(rsp->rda, cpu);
rnp = rdp->mynode;
mask = rdp->grpmask;
raw_spin_lock_irqsave_rcu_node(rnp, flags);
diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h
index fe98dd2..b60f2b6 100644
--- a/kernel/rcu/tree.h
+++ b/kernel/rcu/tree.h
@@ -521,7 +521,6 @@
struct mutex exp_mutex; /* Serialize expedited GP. */
struct mutex exp_wake_mutex; /* Serialize wakeup. */
unsigned long expedited_sequence; /* Take a ticket. */
- atomic_long_t expedited_normal; /* # fallbacks to normal. */
atomic_t expedited_need_qs; /* # CPUs left to check in. */
struct swait_queue_head expedited_wq; /* Wait for check-ins. */
int ncpus_snap; /* # CPUs seen last time. */
@@ -595,6 +594,8 @@
extern struct rcu_state rcu_preempt_state;
#endif /* #ifdef CONFIG_PREEMPT_RCU */
+int rcu_dynticks_snap(struct rcu_dynticks *rdtp);
+
#ifdef CONFIG_RCU_BOOST
DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_status);
DECLARE_PER_CPU(int, rcu_cpu_kthread_cpu);
@@ -688,18 +689,6 @@
#endif /* #ifdef CONFIG_RCU_TRACE */
/*
- * Place this after a lock-acquisition primitive to guarantee that
- * an UNLOCK+LOCK pair act as a full barrier. This guarantee applies
- * if the UNLOCK and LOCK are executed by the same CPU or if the
- * UNLOCK and LOCK operate on the same lock variable.
- */
-#ifdef CONFIG_PPC
-#define smp_mb__after_unlock_lock() smp_mb() /* Full ordering for lock. */
-#else /* #ifdef CONFIG_PPC */
-#define smp_mb__after_unlock_lock() do { } while (0)
-#endif /* #else #ifdef CONFIG_PPC */
-
-/*
* Wrappers for the rcu_node::lock acquire and release.
*
* Because the rcu_nodes form a tree, the tree traversal locking will observe
diff --git a/kernel/rcu/tree_exp.h b/kernel/rcu/tree_exp.h
index e59e184..a7b639c 100644
--- a/kernel/rcu/tree_exp.h
+++ b/kernel/rcu/tree_exp.h
@@ -20,16 +20,26 @@
* Authors: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
*/
-/* Wrapper functions for expedited grace periods. */
+/*
+ * Record the start of an expedited grace period.
+ */
static void rcu_exp_gp_seq_start(struct rcu_state *rsp)
{
rcu_seq_start(&rsp->expedited_sequence);
}
+
+/*
+ * Record the end of an expedited grace period.
+ */
static void rcu_exp_gp_seq_end(struct rcu_state *rsp)
{
rcu_seq_end(&rsp->expedited_sequence);
smp_mb(); /* Ensure that consecutive grace periods serialize. */
}
+
+/*
+ * Take a snapshot of the expedited-grace-period counter.
+ */
static unsigned long rcu_exp_gp_seq_snap(struct rcu_state *rsp)
{
unsigned long s;
@@ -39,6 +49,12 @@
trace_rcu_exp_grace_period(rsp->name, s, TPS("snap"));
return s;
}
+
+/*
+ * Given a counter snapshot from rcu_exp_gp_seq_snap(), return true
+ * if a full expedited grace period has elapsed since that snapshot
+ * was taken.
+ */
static bool rcu_exp_gp_seq_done(struct rcu_state *rsp, unsigned long s)
{
return rcu_seq_done(&rsp->expedited_sequence, s);
@@ -356,12 +372,11 @@
mask_ofl_test = 0;
for_each_leaf_node_possible_cpu(rnp, cpu) {
struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu);
- struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu);
rdp->exp_dynticks_snap =
- atomic_add_return(0, &rdtp->dynticks);
+ rcu_dynticks_snap(rdp->dynticks);
if (raw_smp_processor_id() == cpu ||
- !(rdp->exp_dynticks_snap & 0x1) ||
+ rcu_dynticks_in_eqs(rdp->exp_dynticks_snap) ||
!(rnp->qsmaskinitnext & rdp->grpmask))
mask_ofl_test |= rdp->grpmask;
}
@@ -380,13 +395,12 @@
for_each_leaf_node_possible_cpu(rnp, cpu) {
unsigned long mask = leaf_node_cpu_bit(rnp, cpu);
struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu);
- struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu);
if (!(mask_ofl_ipi & mask))
continue;
retry_ipi:
- if (atomic_add_return(0, &rdtp->dynticks) !=
- rdp->exp_dynticks_snap) {
+ if (rcu_dynticks_in_eqs_since(rdp->dynticks,
+ rdp->exp_dynticks_snap)) {
mask_ofl_test |= mask;
continue;
}
@@ -623,6 +637,11 @@
{
struct rcu_state *rsp = &rcu_sched_state;
+ RCU_LOCKDEP_WARN(lock_is_held(&rcu_bh_lock_map) ||
+ lock_is_held(&rcu_lock_map) ||
+ lock_is_held(&rcu_sched_lock_map),
+ "Illegal synchronize_sched_expedited() in RCU read-side critical section");
+
/* If only one CPU, this is automatically a grace period. */
if (rcu_blocking_is_gp())
return;
@@ -692,6 +711,11 @@
{
struct rcu_state *rsp = rcu_state_p;
+ RCU_LOCKDEP_WARN(lock_is_held(&rcu_bh_lock_map) ||
+ lock_is_held(&rcu_lock_map) ||
+ lock_is_held(&rcu_sched_lock_map),
+ "Illegal synchronize_rcu_expedited() in RCU read-side critical section");
+
if (rcu_scheduler_active == RCU_SCHEDULER_INACTIVE)
return;
_synchronize_rcu_expedited(rsp, sync_rcu_exp_handler);
diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h
index 56583e7..a240f33 100644
--- a/kernel/rcu/tree_plugin.h
+++ b/kernel/rcu/tree_plugin.h
@@ -1643,7 +1643,7 @@
"o."[!!(rdp->grpmask & rdp->mynode->qsmaskinit)],
"N."[!!(rdp->grpmask & rdp->mynode->qsmaskinitnext)],
ticks_value, ticks_title,
- atomic_read(&rdtp->dynticks) & 0xfff,
+ rcu_dynticks_snap(rdtp) & 0xfff,
rdtp->dynticks_nesting, rdtp->dynticks_nmi_nesting,
rdp->softirq_snap, kstat_softirqs_cpu(RCU_SOFTIRQ, cpu),
READ_ONCE(rsp->n_force_qs) - rsp->n_force_qs_gpstart,
@@ -2366,8 +2366,9 @@
}
/*
- * Each pass through this loop sets up one rcu_data structure and
- * spawns one rcu_nocb_kthread().
+ * Each pass through this loop sets up one rcu_data structure.
+ * Should the corresponding CPU come online in the future, then
+ * we will spawn the needed set of rcu_nocb_kthread() kthreads.
*/
for_each_cpu(cpu, rcu_nocb_mask) {
rdp = per_cpu_ptr(rsp->rda, cpu);
diff --git a/kernel/rcu/tree_trace.c b/kernel/rcu/tree_trace.c
index b1f2897..8751a74 100644
--- a/kernel/rcu/tree_trace.c
+++ b/kernel/rcu/tree_trace.c
@@ -124,7 +124,7 @@
rdp->rcu_qs_ctr_snap == per_cpu(rcu_qs_ctr, rdp->cpu),
rdp->core_needs_qs);
seq_printf(m, " dt=%d/%llx/%d df=%lu",
- atomic_read(&rdp->dynticks->dynticks),
+ rcu_dynticks_snap(rdp->dynticks),
rdp->dynticks->dynticks_nesting,
rdp->dynticks->dynticks_nmi_nesting,
rdp->dynticks_fqs);
@@ -194,9 +194,8 @@
s2 += atomic_long_read(&rdp->exp_workdone2);
s3 += atomic_long_read(&rdp->exp_workdone3);
}
- seq_printf(m, "s=%lu wd0=%lu wd1=%lu wd2=%lu wd3=%lu n=%lu enq=%d sc=%lu\n",
+ seq_printf(m, "s=%lu wd0=%lu wd1=%lu wd2=%lu wd3=%lu enq=%d sc=%lu\n",
rsp->expedited_sequence, s0, s1, s2, s3,
- atomic_long_read(&rsp->expedited_normal),
atomic_read(&rsp->expedited_need_qs),
rsp->expedited_sequence / 2);
return 0;
diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c
index 4f6db7e..9e03db9 100644
--- a/kernel/rcu/update.c
+++ b/kernel/rcu/update.c
@@ -132,8 +132,7 @@
}
EXPORT_SYMBOL_GPL(rcu_gp_is_normal);
-static atomic_t rcu_expedited_nesting =
- ATOMIC_INIT(IS_ENABLED(CONFIG_RCU_EXPEDITE_BOOT) ? 1 : 0);
+static atomic_t rcu_expedited_nesting = ATOMIC_INIT(1);
/*
* Should normal grace-period primitives be expedited? Intended for
@@ -182,8 +181,7 @@
*/
void rcu_end_inkernel_boot(void)
{
- if (IS_ENABLED(CONFIG_RCU_EXPEDITE_BOOT))
- rcu_unexpedite_gp();
+ rcu_unexpedite_gp();
if (rcu_normal_after_boot)
WRITE_ONCE(rcu_normal, 1);
}
diff --git a/kernel/sched/Makefile b/kernel/sched/Makefile
index 5e59b83..89ab675 100644
--- a/kernel/sched/Makefile
+++ b/kernel/sched/Makefile
@@ -18,8 +18,8 @@
obj-y += core.o loadavg.o clock.o cputime.o
obj-y += idle_task.o fair.o rt.o deadline.o stop_task.o
obj-y += wait.o swait.o completion.o idle.o
-obj-$(CONFIG_SMP) += cpupri.o cpudeadline.o
-obj-$(CONFIG_SCHED_AUTOGROUP) += auto_group.o
+obj-$(CONFIG_SMP) += cpupri.o cpudeadline.o topology.o
+obj-$(CONFIG_SCHED_AUTOGROUP) += autogroup.o
obj-$(CONFIG_SCHEDSTATS) += stats.o
obj-$(CONFIG_SCHED_DEBUG) += debug.o
obj-$(CONFIG_CGROUP_CPUACCT) += cpuacct.o
diff --git a/kernel/sched/auto_group.c b/kernel/sched/autogroup.c
similarity index 100%
rename from kernel/sched/auto_group.c
rename to kernel/sched/autogroup.c
diff --git a/kernel/sched/auto_group.h b/kernel/sched/autogroup.h
similarity index 100%
rename from kernel/sched/auto_group.h
rename to kernel/sched/autogroup.h
diff --git a/kernel/sched/clock.c b/kernel/sched/clock.c
index e85a725..ad64efe 100644
--- a/kernel/sched/clock.c
+++ b/kernel/sched/clock.c
@@ -77,57 +77,27 @@
__read_mostly int sched_clock_running;
+void sched_clock_init(void)
+{
+ sched_clock_running = 1;
+}
+
#ifdef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK
-static struct static_key __sched_clock_stable = STATIC_KEY_INIT;
-static int __sched_clock_stable_early;
+/*
+ * We must start with !__sched_clock_stable because the unstable -> stable
+ * transition is accurate, while the stable -> unstable transition is not.
+ *
+ * Similarly we start with __sched_clock_stable_early, thereby assuming we
+ * will become stable, such that there's only a single 1 -> 0 transition.
+ */
+static DEFINE_STATIC_KEY_FALSE(__sched_clock_stable);
+static int __sched_clock_stable_early = 1;
-int sched_clock_stable(void)
-{
- return static_key_false(&__sched_clock_stable);
-}
-
-static void __set_sched_clock_stable(void)
-{
- if (!sched_clock_stable())
- static_key_slow_inc(&__sched_clock_stable);
-
- tick_dep_clear(TICK_DEP_BIT_CLOCK_UNSTABLE);
-}
-
-void set_sched_clock_stable(void)
-{
- __sched_clock_stable_early = 1;
-
- smp_mb(); /* matches sched_clock_init() */
-
- if (!sched_clock_running)
- return;
-
- __set_sched_clock_stable();
-}
-
-static void __clear_sched_clock_stable(struct work_struct *work)
-{
- /* XXX worry about clock continuity */
- if (sched_clock_stable())
- static_key_slow_dec(&__sched_clock_stable);
-
- tick_dep_set(TICK_DEP_BIT_CLOCK_UNSTABLE);
-}
-
-static DECLARE_WORK(sched_clock_work, __clear_sched_clock_stable);
-
-void clear_sched_clock_stable(void)
-{
- __sched_clock_stable_early = 0;
-
- smp_mb(); /* matches sched_clock_init() */
-
- if (!sched_clock_running)
- return;
-
- schedule_work(&sched_clock_work);
-}
+/*
+ * We want: ktime_get_ns() + gtod_offset == sched_clock() + raw_offset
+ */
+static __read_mostly u64 raw_offset;
+static __read_mostly u64 gtod_offset;
struct sched_clock_data {
u64 tick_raw;
@@ -147,21 +117,66 @@
return &per_cpu(sched_clock_data, cpu);
}
-void sched_clock_init(void)
+int sched_clock_stable(void)
{
- u64 ktime_now = ktime_to_ns(ktime_get());
- int cpu;
+ return static_branch_likely(&__sched_clock_stable);
+}
- for_each_possible_cpu(cpu) {
- struct sched_clock_data *scd = cpu_sdc(cpu);
+static void __set_sched_clock_stable(void)
+{
+ struct sched_clock_data *scd = this_scd();
- scd->tick_raw = 0;
- scd->tick_gtod = ktime_now;
- scd->clock = ktime_now;
- }
+ /*
+ * Attempt to make the (initial) unstable->stable transition continuous.
+ */
+ raw_offset = (scd->tick_gtod + gtod_offset) - (scd->tick_raw);
- sched_clock_running = 1;
+ printk(KERN_INFO "sched_clock: Marking stable (%lld, %lld)->(%lld, %lld)\n",
+ scd->tick_gtod, gtod_offset,
+ scd->tick_raw, raw_offset);
+ static_branch_enable(&__sched_clock_stable);
+ tick_dep_clear(TICK_DEP_BIT_CLOCK_UNSTABLE);
+}
+
+static void __clear_sched_clock_stable(struct work_struct *work)
+{
+ struct sched_clock_data *scd = this_scd();
+
+ /*
+ * Attempt to make the stable->unstable transition continuous.
+ *
+ * Trouble is, this is typically called from the TSC watchdog
+ * timer, which is late per definition. This means the tick
+ * values can already be screwy.
+ *
+ * Still do what we can.
+ */
+ gtod_offset = (scd->tick_raw + raw_offset) - (scd->tick_gtod);
+
+ printk(KERN_INFO "sched_clock: Marking unstable (%lld, %lld)<-(%lld, %lld)\n",
+ scd->tick_gtod, gtod_offset,
+ scd->tick_raw, raw_offset);
+
+ static_branch_disable(&__sched_clock_stable);
+ tick_dep_set(TICK_DEP_BIT_CLOCK_UNSTABLE);
+}
+
+static DECLARE_WORK(sched_clock_work, __clear_sched_clock_stable);
+
+void clear_sched_clock_stable(void)
+{
+ __sched_clock_stable_early = 0;
+
+ smp_mb(); /* matches sched_clock_init_late() */
+
+ if (sched_clock_running == 2)
+ schedule_work(&sched_clock_work);
+}
+
+void sched_clock_init_late(void)
+{
+ sched_clock_running = 2;
/*
* Ensure that it is impossible to not do a static_key update.
*
@@ -173,8 +188,6 @@
if (__sched_clock_stable_early)
__set_sched_clock_stable();
- else
- __clear_sched_clock_stable(NULL);
}
/*
@@ -216,7 +229,7 @@
* scd->tick_gtod + TICK_NSEC);
*/
- clock = scd->tick_gtod + delta;
+ clock = scd->tick_gtod + gtod_offset + delta;
min_clock = wrap_max(scd->tick_gtod, old_clock);
max_clock = wrap_max(old_clock, scd->tick_gtod + TICK_NSEC);
@@ -302,7 +315,7 @@
u64 clock;
if (sched_clock_stable())
- return sched_clock();
+ return sched_clock() + raw_offset;
if (unlikely(!sched_clock_running))
return 0ull;
@@ -323,23 +336,22 @@
void sched_clock_tick(void)
{
struct sched_clock_data *scd;
- u64 now, now_gtod;
-
- if (sched_clock_stable())
- return;
-
- if (unlikely(!sched_clock_running))
- return;
WARN_ON_ONCE(!irqs_disabled());
+ /*
+ * Update these values even if sched_clock_stable(), because it can
+ * become unstable at any point in time at which point we need some
+ * values to fall back on.
+ *
+ * XXX arguably we can skip this if we expose tsc_clocksource_reliable
+ */
scd = this_scd();
- now_gtod = ktime_to_ns(ktime_get());
- now = sched_clock();
+ scd->tick_raw = sched_clock();
+ scd->tick_gtod = ktime_get_ns();
- scd->tick_raw = now;
- scd->tick_gtod = now_gtod;
- sched_clock_local(scd);
+ if (!sched_clock_stable() && likely(sched_clock_running))
+ sched_clock_local(scd);
}
/*
@@ -366,11 +378,6 @@
#else /* CONFIG_HAVE_UNSTABLE_SCHED_CLOCK */
-void sched_clock_init(void)
-{
- sched_clock_running = 1;
-}
-
u64 sched_clock_cpu(int cpu)
{
if (unlikely(!sched_clock_running))
@@ -378,6 +385,7 @@
return sched_clock();
}
+
#endif /* CONFIG_HAVE_UNSTABLE_SCHED_CLOCK */
/*
diff --git a/kernel/sched/completion.c b/kernel/sched/completion.c
index 8d0f35d..f063a25 100644
--- a/kernel/sched/completion.c
+++ b/kernel/sched/completion.c
@@ -31,7 +31,8 @@
unsigned long flags;
spin_lock_irqsave(&x->wait.lock, flags);
- x->done++;
+ if (x->done != UINT_MAX)
+ x->done++;
__wake_up_locked(&x->wait, TASK_NORMAL, 1);
spin_unlock_irqrestore(&x->wait.lock, flags);
}
@@ -51,7 +52,7 @@
unsigned long flags;
spin_lock_irqsave(&x->wait.lock, flags);
- x->done += UINT_MAX/2;
+ x->done = UINT_MAX;
__wake_up_locked(&x->wait, TASK_NORMAL, 0);
spin_unlock_irqrestore(&x->wait.lock, flags);
}
@@ -79,7 +80,8 @@
if (!x->done)
return timeout;
}
- x->done--;
+ if (x->done != UINT_MAX)
+ x->done--;
return timeout ?: 1;
}
@@ -280,7 +282,7 @@
spin_lock_irqsave(&x->wait.lock, flags);
if (!x->done)
ret = 0;
- else
+ else if (x->done != UINT_MAX)
x->done--;
spin_unlock_irqrestore(&x->wait.lock, flags);
return ret;
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index c56fb57..34e2291 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -1,88 +1,28 @@
/*
* kernel/sched/core.c
*
- * Kernel scheduler and related syscalls
+ * Core kernel scheduler code and related syscalls
*
* Copyright (C) 1991-2002 Linus Torvalds
- *
- * 1996-12-23 Modified by Dave Grothe to fix bugs in semaphores and
- * make semaphores SMP safe
- * 1998-11-19 Implemented schedule_timeout() and related stuff
- * by Andrea Arcangeli
- * 2002-01-04 New ultra-scalable O(1) scheduler by Ingo Molnar:
- * hybrid priority-list and round-robin design with
- * an array-switch method of distributing timeslices
- * and per-CPU runqueues. Cleanups and useful suggestions
- * by Davide Libenzi, preemptible kernel bits by Robert Love.
- * 2003-09-03 Interactivity tuning by Con Kolivas.
- * 2004-04-02 Scheduler domains code by Nick Piggin
- * 2007-04-15 Work begun on replacing all interactivity tuning with a
- * fair scheduling design by Con Kolivas.
- * 2007-05-05 Load balancing (smp-nice) and other improvements
- * by Peter Williams
- * 2007-05-06 Interactivity improvements to CFS by Mike Galbraith
- * 2007-07-01 Group scheduling enhancements by Srivatsa Vaddagiri
- * 2007-11-29 RT balancing improvements by Steven Rostedt, Gregory Haskins,
- * Thomas Gleixner, Mike Kravetz
*/
-
-#include <linux/kasan.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/nmi.h>
-#include <linux/init.h>
-#include <linux/uaccess.h>
-#include <linux/highmem.h>
-#include <linux/mmu_context.h>
-#include <linux/interrupt.h>
-#include <linux/capability.h>
-#include <linux/completion.h>
-#include <linux/kernel_stat.h>
-#include <linux/debug_locks.h>
-#include <linux/perf_event.h>
-#include <linux/security.h>
-#include <linux/notifier.h>
-#include <linux/profile.h>
-#include <linux/freezer.h>
-#include <linux/vmalloc.h>
-#include <linux/blkdev.h>
-#include <linux/delay.h>
-#include <linux/pid_namespace.h>
-#include <linux/smp.h>
-#include <linux/threads.h>
-#include <linux/timer.h>
-#include <linux/rcupdate.h>
-#include <linux/cpu.h>
+#include <linux/sched.h>
#include <linux/cpuset.h>
-#include <linux/percpu.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/sysctl.h>
-#include <linux/syscalls.h>
-#include <linux/times.h>
-#include <linux/tsacct_kern.h>
-#include <linux/kprobes.h>
#include <linux/delayacct.h>
-#include <linux/unistd.h>
-#include <linux/pagemap.h>
-#include <linux/hrtimer.h>
-#include <linux/tick.h>
-#include <linux/ctype.h>
-#include <linux/ftrace.h>
-#include <linux/slab.h>
#include <linux/init_task.h>
#include <linux/context_tracking.h>
-#include <linux/compiler.h>
-#include <linux/frame.h>
+
+#include <linux/blkdev.h>
+#include <linux/kprobes.h>
+#include <linux/mmu_context.h>
+#include <linux/module.h>
+#include <linux/nmi.h>
#include <linux/prefetch.h>
-#include <linux/mutex.h>
+#include <linux/profile.h>
+#include <linux/security.h>
+#include <linux/syscalls.h>
#include <asm/switch_to.h>
#include <asm/tlb.h>
-#include <asm/irq_regs.h>
-#ifdef CONFIG_PARAVIRT
-#include <asm/paravirt.h>
-#endif
#include "sched.h"
#include "../workqueue_internal.h"
@@ -91,27 +31,8 @@
#define CREATE_TRACE_POINTS
#include <trace/events/sched.h>
-DEFINE_MUTEX(sched_domains_mutex);
DEFINE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues);
-static void update_rq_clock_task(struct rq *rq, s64 delta);
-
-void update_rq_clock(struct rq *rq)
-{
- s64 delta;
-
- lockdep_assert_held(&rq->lock);
-
- if (rq->clock_skip_update & RQCF_ACT_SKIP)
- return;
-
- delta = sched_clock_cpu(cpu_of(rq)) - rq->clock;
- if (delta < 0)
- return;
- rq->clock += delta;
- update_rq_clock_task(rq, delta);
-}
-
/*
* Debugging: various feature bits
*/
@@ -140,7 +61,7 @@
const_debug unsigned int sysctl_sched_time_avg = MSEC_PER_SEC;
/*
- * period over which we measure -rt task cpu usage in us.
+ * period over which we measure -rt task CPU usage in us.
* default: 1s
*/
unsigned int sysctl_sched_rt_period = 1000000;
@@ -153,7 +74,7 @@
*/
int sysctl_sched_rt_runtime = 950000;
-/* cpus with isolated domains */
+/* CPUs with isolated domains */
cpumask_var_t cpu_isolated_map;
/*
@@ -185,7 +106,7 @@
rq = task_rq(p);
raw_spin_lock(&rq->lock);
if (likely(rq == task_rq(p) && !task_on_rq_migrating(p))) {
- rf->cookie = lockdep_pin_lock(&rq->lock);
+ rq_pin_lock(rq, rf);
return rq;
}
raw_spin_unlock(&rq->lock);
@@ -221,11 +142,11 @@
* If we observe the old cpu in task_rq_lock, the acquire of
* the old rq->lock will fully serialize against the stores.
*
- * If we observe the new cpu in task_rq_lock, the acquire will
+ * If we observe the new CPU in task_rq_lock, the acquire will
* pair with the WMB to ensure we must then also see migrating.
*/
if (likely(rq == task_rq(p) && !task_on_rq_migrating(p))) {
- rf->cookie = lockdep_pin_lock(&rq->lock);
+ rq_pin_lock(rq, rf);
return rq;
}
raw_spin_unlock(&rq->lock);
@@ -236,6 +157,84 @@
}
}
+/*
+ * RQ-clock updating methods:
+ */
+
+static void update_rq_clock_task(struct rq *rq, s64 delta)
+{
+/*
+ * In theory, the compile should just see 0 here, and optimize out the call
+ * to sched_rt_avg_update. But I don't trust it...
+ */
+#if defined(CONFIG_IRQ_TIME_ACCOUNTING) || defined(CONFIG_PARAVIRT_TIME_ACCOUNTING)
+ s64 steal = 0, irq_delta = 0;
+#endif
+#ifdef CONFIG_IRQ_TIME_ACCOUNTING
+ irq_delta = irq_time_read(cpu_of(rq)) - rq->prev_irq_time;
+
+ /*
+ * Since irq_time is only updated on {soft,}irq_exit, we might run into
+ * this case when a previous update_rq_clock() happened inside a
+ * {soft,}irq region.
+ *
+ * When this happens, we stop ->clock_task and only update the
+ * prev_irq_time stamp to account for the part that fit, so that a next
+ * update will consume the rest. This ensures ->clock_task is
+ * monotonic.
+ *
+ * It does however cause some slight miss-attribution of {soft,}irq
+ * time, a more accurate solution would be to update the irq_time using
+ * the current rq->clock timestamp, except that would require using
+ * atomic ops.
+ */
+ if (irq_delta > delta)
+ irq_delta = delta;
+
+ rq->prev_irq_time += irq_delta;
+ delta -= irq_delta;
+#endif
+#ifdef CONFIG_PARAVIRT_TIME_ACCOUNTING
+ if (static_key_false((¶virt_steal_rq_enabled))) {
+ steal = paravirt_steal_clock(cpu_of(rq));
+ steal -= rq->prev_steal_time_rq;
+
+ if (unlikely(steal > delta))
+ steal = delta;
+
+ rq->prev_steal_time_rq += steal;
+ delta -= steal;
+ }
+#endif
+
+ rq->clock_task += delta;
+
+#if defined(CONFIG_IRQ_TIME_ACCOUNTING) || defined(CONFIG_PARAVIRT_TIME_ACCOUNTING)
+ if ((irq_delta + steal) && sched_feat(NONTASK_CAPACITY))
+ sched_rt_avg_update(rq, irq_delta + steal);
+#endif
+}
+
+void update_rq_clock(struct rq *rq)
+{
+ s64 delta;
+
+ lockdep_assert_held(&rq->lock);
+
+ if (rq->clock_update_flags & RQCF_ACT_SKIP)
+ return;
+
+#ifdef CONFIG_SCHED_DEBUG
+ rq->clock_update_flags |= RQCF_UPDATED;
+#endif
+ delta = sched_clock_cpu(cpu_of(rq)) - rq->clock;
+ if (delta < 0)
+ return;
+ rq->clock += delta;
+ update_rq_clock_task(rq, delta);
+}
+
+
#ifdef CONFIG_SCHED_HRTICK
/*
* Use HR-timers to deliver accurate preemption points.
@@ -458,7 +457,7 @@
task = container_of(node, struct task_struct, wake_q);
BUG_ON(!task);
- /* task can safely be re-inserted now */
+ /* Task can safely be re-inserted now: */
node = node->next;
task->wake_q.next = NULL;
@@ -516,12 +515,12 @@
#ifdef CONFIG_SMP
#ifdef CONFIG_NO_HZ_COMMON
/*
- * In the semi idle case, use the nearest busy cpu for migrating timers
- * from an idle cpu. This is good for power-savings.
+ * In the semi idle case, use the nearest busy CPU for migrating timers
+ * from an idle CPU. This is good for power-savings.
*
* We don't do similar optimization for completely idle system, as
- * selecting an idle cpu will add more delays to the timers than intended
- * (as that cpu's timer base may not be uptodate wrt jiffies etc).
+ * selecting an idle CPU will add more delays to the timers than intended
+ * (as that CPU's timer base may not be uptodate wrt jiffies etc).
*/
int get_nohz_timer_target(void)
{
@@ -550,6 +549,7 @@
rcu_read_unlock();
return cpu;
}
+
/*
* When add_timer_on() enqueues a timer into the timer wheel of an
* idle CPU then this timer might expire before the next timer event
@@ -784,60 +784,6 @@
dequeue_task(rq, p, flags);
}
-static void update_rq_clock_task(struct rq *rq, s64 delta)
-{
-/*
- * In theory, the compile should just see 0 here, and optimize out the call
- * to sched_rt_avg_update. But I don't trust it...
- */
-#if defined(CONFIG_IRQ_TIME_ACCOUNTING) || defined(CONFIG_PARAVIRT_TIME_ACCOUNTING)
- s64 steal = 0, irq_delta = 0;
-#endif
-#ifdef CONFIG_IRQ_TIME_ACCOUNTING
- irq_delta = irq_time_read(cpu_of(rq)) - rq->prev_irq_time;
-
- /*
- * Since irq_time is only updated on {soft,}irq_exit, we might run into
- * this case when a previous update_rq_clock() happened inside a
- * {soft,}irq region.
- *
- * When this happens, we stop ->clock_task and only update the
- * prev_irq_time stamp to account for the part that fit, so that a next
- * update will consume the rest. This ensures ->clock_task is
- * monotonic.
- *
- * It does however cause some slight miss-attribution of {soft,}irq
- * time, a more accurate solution would be to update the irq_time using
- * the current rq->clock timestamp, except that would require using
- * atomic ops.
- */
- if (irq_delta > delta)
- irq_delta = delta;
-
- rq->prev_irq_time += irq_delta;
- delta -= irq_delta;
-#endif
-#ifdef CONFIG_PARAVIRT_TIME_ACCOUNTING
- if (static_key_false((¶virt_steal_rq_enabled))) {
- steal = paravirt_steal_clock(cpu_of(rq));
- steal -= rq->prev_steal_time_rq;
-
- if (unlikely(steal > delta))
- steal = delta;
-
- rq->prev_steal_time_rq += steal;
- delta -= steal;
- }
-#endif
-
- rq->clock_task += delta;
-
-#if defined(CONFIG_IRQ_TIME_ACCOUNTING) || defined(CONFIG_PARAVIRT_TIME_ACCOUNTING)
- if ((irq_delta + steal) && sched_feat(NONTASK_CAPACITY))
- sched_rt_avg_update(rq, irq_delta + steal);
-#endif
-}
-
void sched_set_stop_task(int cpu, struct task_struct *stop)
{
struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
@@ -1018,7 +964,7 @@
};
/*
- * Move (not current) task off this cpu, onto dest cpu. We're doing
+ * Move (not current) task off this CPU, onto the destination CPU. We're doing
* this because either it can't run here any more (set_cpus_allowed()
* away from this CPU, or CPU going down), or because we're
* attempting to rebalance this task on exec (sched_exec).
@@ -1052,8 +998,8 @@
struct rq *rq = this_rq();
/*
- * The original target cpu might have gone down and we might
- * be on another cpu but it doesn't matter.
+ * The original target CPU might have gone down and we might
+ * be on another CPU but it doesn't matter.
*/
local_irq_disable();
/*
@@ -1171,7 +1117,7 @@
if (p->flags & PF_KTHREAD) {
/*
* For kernel threads that do indeed end up on online &&
- * !active we want to ensure they are strict per-cpu threads.
+ * !active we want to ensure they are strict per-CPU threads.
*/
WARN_ON(cpumask_intersects(new_mask, cpu_online_mask) &&
!cpumask_intersects(new_mask, cpu_active_mask) &&
@@ -1195,9 +1141,9 @@
* OK, since we're going to drop the lock immediately
* afterwards anyway.
*/
- lockdep_unpin_lock(&rq->lock, rf.cookie);
+ rq_unpin_lock(rq, &rf);
rq = move_queued_task(rq, p, dest_cpu);
- lockdep_repin_lock(&rq->lock, rf.cookie);
+ rq_repin_lock(rq, &rf);
}
out:
task_rq_unlock(rq, p, &rf);
@@ -1276,7 +1222,7 @@
/*
* Task isn't running anymore; make it appear like we migrated
* it before it went to sleep. This means on wakeup we make the
- * previous cpu our target instead of where it really is.
+ * previous CPU our target instead of where it really is.
*/
p->wake_cpu = cpu;
}
@@ -1508,12 +1454,12 @@
*
* - on cpu-up we allow per-cpu kthreads on the online && !active cpu,
* see __set_cpus_allowed_ptr(). At this point the newly online
- * cpu isn't yet part of the sched domains, and balancing will not
+ * CPU isn't yet part of the sched domains, and balancing will not
* see it.
*
- * - on cpu-down we clear cpu_active() to mask the sched domains and
+ * - on CPU-down we clear cpu_active() to mask the sched domains and
* avoid the load balancer to place new tasks on the to be removed
- * cpu. Existing tasks will remain running there and will be taken
+ * CPU. Existing tasks will remain running there and will be taken
* off.
*
* This means that fallback selection must not select !active CPUs.
@@ -1529,9 +1475,9 @@
int dest_cpu;
/*
- * If the node that the cpu is on has been offlined, cpu_to_node()
- * will return -1. There is no cpu on the node, and we should
- * select the cpu on the other node.
+ * If the node that the CPU is on has been offlined, cpu_to_node()
+ * will return -1. There is no CPU on the node, and we should
+ * select the CPU on the other node.
*/
if (nid != -1) {
nodemask = cpumask_of_node(nid);
@@ -1563,7 +1509,7 @@
state = possible;
break;
}
- /* fall-through */
+ /* Fall-through */
case possible:
do_set_cpus_allowed(p, cpu_possible_mask);
state = fail;
@@ -1607,7 +1553,7 @@
/*
* In order not to call set_task_cpu() on a blocking task we need
* to rely on ttwu() to place the task on a valid ->cpus_allowed
- * cpu.
+ * CPU.
*
* Since this is common to all placement strategies, this lives here.
*
@@ -1681,7 +1627,7 @@
activate_task(rq, p, en_flags);
p->on_rq = TASK_ON_RQ_QUEUED;
- /* if a worker is waking up, notify workqueue */
+ /* If a worker is waking up, notify the workqueue: */
if (p->flags & PF_WQ_WORKER)
wq_worker_waking_up(p, cpu_of(rq));
}
@@ -1690,7 +1636,7 @@
* Mark the task runnable and perform wakeup-preemption.
*/
static void ttwu_do_wakeup(struct rq *rq, struct task_struct *p, int wake_flags,
- struct pin_cookie cookie)
+ struct rq_flags *rf)
{
check_preempt_curr(rq, p, wake_flags);
p->state = TASK_RUNNING;
@@ -1702,9 +1648,9 @@
* Our task @p is fully woken up and running; so its safe to
* drop the rq->lock, hereafter rq is only used for statistics.
*/
- lockdep_unpin_lock(&rq->lock, cookie);
+ rq_unpin_lock(rq, rf);
p->sched_class->task_woken(rq, p);
- lockdep_repin_lock(&rq->lock, cookie);
+ rq_repin_lock(rq, rf);
}
if (rq->idle_stamp) {
@@ -1723,7 +1669,7 @@
static void
ttwu_do_activate(struct rq *rq, struct task_struct *p, int wake_flags,
- struct pin_cookie cookie)
+ struct rq_flags *rf)
{
int en_flags = ENQUEUE_WAKEUP;
@@ -1738,7 +1684,7 @@
#endif
ttwu_activate(rq, p, en_flags);
- ttwu_do_wakeup(rq, p, wake_flags, cookie);
+ ttwu_do_wakeup(rq, p, wake_flags, rf);
}
/*
@@ -1757,7 +1703,7 @@
if (task_on_rq_queued(p)) {
/* check_preempt_curr() may use rq clock */
update_rq_clock(rq);
- ttwu_do_wakeup(rq, p, wake_flags, rf.cookie);
+ ttwu_do_wakeup(rq, p, wake_flags, &rf);
ret = 1;
}
__task_rq_unlock(rq, &rf);
@@ -1770,15 +1716,15 @@
{
struct rq *rq = this_rq();
struct llist_node *llist = llist_del_all(&rq->wake_list);
- struct pin_cookie cookie;
struct task_struct *p;
unsigned long flags;
+ struct rq_flags rf;
if (!llist)
return;
raw_spin_lock_irqsave(&rq->lock, flags);
- cookie = lockdep_pin_lock(&rq->lock);
+ rq_pin_lock(rq, &rf);
while (llist) {
int wake_flags = 0;
@@ -1789,10 +1735,10 @@
if (p->sched_remote_wakeup)
wake_flags = WF_MIGRATED;
- ttwu_do_activate(rq, p, wake_flags, cookie);
+ ttwu_do_activate(rq, p, wake_flags, &rf);
}
- lockdep_unpin_lock(&rq->lock, cookie);
+ rq_unpin_lock(rq, &rf);
raw_spin_unlock_irqrestore(&rq->lock, flags);
}
@@ -1864,7 +1810,7 @@
raw_spin_lock_irqsave(&rq->lock, flags);
if (is_idle_task(rq->curr))
smp_send_reschedule(cpu);
- /* Else cpu is not in idle, do nothing here */
+ /* Else CPU is not idle, do nothing here: */
raw_spin_unlock_irqrestore(&rq->lock, flags);
}
@@ -1881,20 +1827,20 @@
static void ttwu_queue(struct task_struct *p, int cpu, int wake_flags)
{
struct rq *rq = cpu_rq(cpu);
- struct pin_cookie cookie;
+ struct rq_flags rf;
#if defined(CONFIG_SMP)
if (sched_feat(TTWU_QUEUE) && !cpus_share_cache(smp_processor_id(), cpu)) {
- sched_clock_cpu(cpu); /* sync clocks x-cpu */
+ sched_clock_cpu(cpu); /* Sync clocks across CPUs */
ttwu_queue_remote(p, cpu, wake_flags);
return;
}
#endif
raw_spin_lock(&rq->lock);
- cookie = lockdep_pin_lock(&rq->lock);
- ttwu_do_activate(rq, p, wake_flags, cookie);
- lockdep_unpin_lock(&rq->lock, cookie);
+ rq_pin_lock(rq, &rf);
+ ttwu_do_activate(rq, p, wake_flags, &rf);
+ rq_unpin_lock(rq, &rf);
raw_spin_unlock(&rq->lock);
}
@@ -1904,8 +1850,8 @@
* MIGRATION
*
* The basic program-order guarantee on SMP systems is that when a task [t]
- * migrates, all its activity on its old cpu [c0] happens-before any subsequent
- * execution on its new cpu [c1].
+ * migrates, all its activity on its old CPU [c0] happens-before any subsequent
+ * execution on its new CPU [c1].
*
* For migration (of runnable tasks) this is provided by the following means:
*
@@ -1916,7 +1862,7 @@
*
* Transitivity guarantees that B happens after A and C after B.
* Note: we only require RCpc transitivity.
- * Note: the cpu doing B need not be c0 or c1
+ * Note: the CPU doing B need not be c0 or c1
*
* Example:
*
@@ -2024,7 +1970,8 @@
trace_sched_waking(p);
- success = 1; /* we're going to change ->state */
+ /* We're going to change ->state: */
+ success = 1;
cpu = task_cpu(p);
/*
@@ -2073,7 +2020,7 @@
smp_rmb();
/*
- * If the owning (remote) cpu is still in the middle of schedule() with
+ * If the owning (remote) CPU is still in the middle of schedule() with
* this task as prev, wait until its done referencing the task.
*
* Pairs with the smp_store_release() in finish_lock_switch().
@@ -2086,11 +2033,24 @@
p->sched_contributes_to_load = !!task_contributes_to_load(p);
p->state = TASK_WAKING;
+ if (p->in_iowait) {
+ delayacct_blkio_end();
+ atomic_dec(&task_rq(p)->nr_iowait);
+ }
+
cpu = select_task_rq(p, p->wake_cpu, SD_BALANCE_WAKE, wake_flags);
if (task_cpu(p) != cpu) {
wake_flags |= WF_MIGRATED;
set_task_cpu(p, cpu);
}
+
+#else /* CONFIG_SMP */
+
+ if (p->in_iowait) {
+ delayacct_blkio_end();
+ atomic_dec(&task_rq(p)->nr_iowait);
+ }
+
#endif /* CONFIG_SMP */
ttwu_queue(p, cpu, wake_flags);
@@ -2111,7 +2071,7 @@
* ensure that this_rq() is locked, @p is bound to this_rq() and not
* the current task.
*/
-static void try_to_wake_up_local(struct task_struct *p, struct pin_cookie cookie)
+static void try_to_wake_up_local(struct task_struct *p, struct rq_flags *rf)
{
struct rq *rq = task_rq(p);
@@ -2128,11 +2088,11 @@
* disabled avoiding further scheduler activity on it and we've
* not yet picked a replacement task.
*/
- lockdep_unpin_lock(&rq->lock, cookie);
+ rq_unpin_lock(rq, rf);
raw_spin_unlock(&rq->lock);
raw_spin_lock(&p->pi_lock);
raw_spin_lock(&rq->lock);
- lockdep_repin_lock(&rq->lock, cookie);
+ rq_repin_lock(rq, rf);
}
if (!(p->state & TASK_NORMAL))
@@ -2140,10 +2100,15 @@
trace_sched_waking(p);
- if (!task_on_rq_queued(p))
+ if (!task_on_rq_queued(p)) {
+ if (p->in_iowait) {
+ delayacct_blkio_end();
+ atomic_dec(&rq->nr_iowait);
+ }
ttwu_activate(rq, p, ENQUEUE_WAKEUP);
+ }
- ttwu_do_wakeup(rq, p, 0, cookie);
+ ttwu_do_wakeup(rq, p, 0, rf);
ttwu_stat(p, smp_processor_id(), 0);
out:
raw_spin_unlock(&p->pi_lock);
@@ -2427,7 +2392,7 @@
*/
raw_spin_lock_irqsave(&p->pi_lock, flags);
/*
- * We're setting the cpu for the first time, we don't migrate,
+ * We're setting the CPU for the first time, we don't migrate,
* so use __set_task_cpu().
*/
__set_task_cpu(p, cpu);
@@ -2570,7 +2535,7 @@
/*
* Fork balancing, do it here and not earlier because:
* - cpus_allowed can change in the fork path
- * - any previously selected cpu might disappear through hotplug
+ * - any previously selected CPU might disappear through hotplug
*
* Use __set_task_cpu() to avoid calling sched_class::migrate_task_rq,
* as we're not fully set-up yet.
@@ -2578,6 +2543,7 @@
__set_task_cpu(p, select_task_rq(p, task_cpu(p), SD_BALANCE_FORK, 0));
#endif
rq = __task_rq_lock(p, &rf);
+ update_rq_clock(rq);
post_init_entity_util_avg(&p->se);
activate_task(rq, p, 0);
@@ -2590,9 +2556,9 @@
* Nothing relies on rq->lock after this, so its fine to
* drop it.
*/
- lockdep_unpin_lock(&rq->lock, rf.cookie);
+ rq_unpin_lock(rq, &rf);
p->sched_class->task_woken(rq, p);
- lockdep_repin_lock(&rq->lock, rf.cookie);
+ rq_repin_lock(rq, &rf);
}
#endif
task_rq_unlock(rq, p, &rf);
@@ -2861,7 +2827,7 @@
*/
static __always_inline struct rq *
context_switch(struct rq *rq, struct task_struct *prev,
- struct task_struct *next, struct pin_cookie cookie)
+ struct task_struct *next, struct rq_flags *rf)
{
struct mm_struct *mm, *oldmm;
@@ -2887,13 +2853,16 @@
prev->active_mm = NULL;
rq->prev_mm = oldmm;
}
+
+ rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);
+
/*
* Since the runqueue lock will be released by the next
* task (which is an invalid locking op but in the case
* of the scheduler it's an obvious special-case), so we
* do an early lockdep release here:
*/
- lockdep_unpin_lock(&rq->lock, cookie);
+ rq_unpin_lock(rq, rf);
spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
/* Here we just switch the register state and the stack. */
@@ -2920,7 +2889,7 @@
}
/*
- * Check if only the current task is running on the cpu.
+ * Check if only the current task is running on the CPU.
*
* Caution: this function does not check that the caller has disabled
* preemption, thus the result might have a time-of-check-to-time-of-use
@@ -2949,6 +2918,36 @@
return sum;
}
+/*
+ * IO-wait accounting, and how its mostly bollocks (on SMP).
+ *
+ * The idea behind IO-wait account is to account the idle time that we could
+ * have spend running if it were not for IO. That is, if we were to improve the
+ * storage performance, we'd have a proportional reduction in IO-wait time.
+ *
+ * This all works nicely on UP, where, when a task blocks on IO, we account
+ * idle time as IO-wait, because if the storage were faster, it could've been
+ * running and we'd not be idle.
+ *
+ * This has been extended to SMP, by doing the same for each CPU. This however
+ * is broken.
+ *
+ * Imagine for instance the case where two tasks block on one CPU, only the one
+ * CPU will have IO-wait accounted, while the other has regular idle. Even
+ * though, if the storage were faster, both could've ran at the same time,
+ * utilising both CPUs.
+ *
+ * This means, that when looking globally, the current IO-wait accounting on
+ * SMP is a lower bound, by reason of under accounting.
+ *
+ * Worse, since the numbers are provided per CPU, they are sometimes
+ * interpreted per CPU, and that is nonsensical. A blocked task isn't strictly
+ * associated with any one particular CPU, it can wake to another CPU than it
+ * blocked on. This means the per CPU IO-wait number is meaningless.
+ *
+ * Task CPU affinities can make all that even more 'interesting'.
+ */
+
unsigned long nr_iowait(void)
{
unsigned long i, sum = 0;
@@ -2959,6 +2958,13 @@
return sum;
}
+/*
+ * Consumers of these two interfaces, like for example the cpufreq menu
+ * governor are using nonsensical data. Boosting frequency for a CPU that has
+ * IO-wait which might not even end up running the task when it does become
+ * runnable.
+ */
+
unsigned long nr_iowait_cpu(int cpu)
{
struct rq *this = cpu_rq(cpu);
@@ -3042,8 +3048,8 @@
* So we have a optimization chance when the task's delta_exec is 0.
* Reading ->on_cpu is racy, but this is ok.
*
- * If we race with it leaving cpu, we'll take a lock. So we're correct.
- * If we race with it entering cpu, unaccounted time is 0. This is
+ * If we race with it leaving CPU, we'll take a lock. So we're correct.
+ * If we race with it entering CPU, unaccounted time is 0. This is
* indistinguishable from the read occurring a few cycles earlier.
* If we see ->on_cpu without ->on_rq, the task is leaving, and has
* been accounted, so we're correct here as well.
@@ -3257,31 +3263,30 @@
* Pick up the highest-prio task:
*/
static inline struct task_struct *
-pick_next_task(struct rq *rq, struct task_struct *prev, struct pin_cookie cookie)
+pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
{
- const struct sched_class *class = &fair_sched_class;
+ const struct sched_class *class;
struct task_struct *p;
/*
* Optimization: we know that if all tasks are in
* the fair class we can call that function directly:
*/
- if (likely(prev->sched_class == class &&
- rq->nr_running == rq->cfs.h_nr_running)) {
- p = fair_sched_class.pick_next_task(rq, prev, cookie);
+ if (likely(rq->nr_running == rq->cfs.h_nr_running)) {
+ p = fair_sched_class.pick_next_task(rq, prev, rf);
if (unlikely(p == RETRY_TASK))
goto again;
- /* assumes fair_sched_class->next == idle_sched_class */
+ /* Assumes fair_sched_class->next == idle_sched_class */
if (unlikely(!p))
- p = idle_sched_class.pick_next_task(rq, prev, cookie);
+ p = idle_sched_class.pick_next_task(rq, prev, rf);
return p;
}
again:
for_each_class(class) {
- p = class->pick_next_task(rq, prev, cookie);
+ p = class->pick_next_task(rq, prev, rf);
if (p) {
if (unlikely(p == RETRY_TASK))
goto again;
@@ -3289,7 +3294,8 @@
}
}
- BUG(); /* the idle class will always have a runnable task */
+ /* The idle class should always have a runnable task: */
+ BUG();
}
/*
@@ -3335,7 +3341,7 @@
{
struct task_struct *prev, *next;
unsigned long *switch_count;
- struct pin_cookie cookie;
+ struct rq_flags rf;
struct rq *rq;
int cpu;
@@ -3358,9 +3364,10 @@
*/
smp_mb__before_spinlock();
raw_spin_lock(&rq->lock);
- cookie = lockdep_pin_lock(&rq->lock);
+ rq_pin_lock(rq, &rf);
- rq->clock_skip_update <<= 1; /* promote REQ to ACT */
+ /* Promote REQ to ACT */
+ rq->clock_update_flags <<= 1;
switch_count = &prev->nivcsw;
if (!preempt && prev->state) {
@@ -3370,6 +3377,11 @@
deactivate_task(rq, prev, DEQUEUE_SLEEP);
prev->on_rq = 0;
+ if (prev->in_iowait) {
+ atomic_inc(&rq->nr_iowait);
+ delayacct_blkio_start();
+ }
+
/*
* If a worker went to sleep, notify and ask workqueue
* whether it wants to wake up a task to maintain
@@ -3380,7 +3392,7 @@
to_wakeup = wq_worker_sleeping(prev);
if (to_wakeup)
- try_to_wake_up_local(to_wakeup, cookie);
+ try_to_wake_up_local(to_wakeup, &rf);
}
}
switch_count = &prev->nvcsw;
@@ -3389,10 +3401,9 @@
if (task_on_rq_queued(prev))
update_rq_clock(rq);
- next = pick_next_task(rq, prev, cookie);
+ next = pick_next_task(rq, prev, &rf);
clear_tsk_need_resched(prev);
clear_preempt_need_resched();
- rq->clock_skip_update = 0;
if (likely(prev != next)) {
rq->nr_switches++;
@@ -3400,9 +3411,12 @@
++*switch_count;
trace_sched_switch(preempt, prev, next);
- rq = context_switch(rq, prev, next, cookie); /* unlocks the rq */
+
+ /* Also unlocks the rq: */
+ rq = context_switch(rq, prev, next, &rf);
} else {
- lockdep_unpin_lock(&rq->lock, cookie);
+ rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);
+ rq_unpin_lock(rq, &rf);
raw_spin_unlock_irq(&rq->lock);
}
@@ -3426,14 +3440,18 @@
smp_mb();
raw_spin_unlock_wait(¤t->pi_lock);
- /* causes final put_task_struct in finish_task_switch(). */
+ /* Causes final put_task_struct in finish_task_switch(): */
__set_current_state(TASK_DEAD);
- current->flags |= PF_NOFREEZE; /* tell freezer to ignore us */
+
+ /* Tell freezer to ignore us: */
+ current->flags |= PF_NOFREEZE;
+
__schedule(false);
BUG();
- /* Avoid "noreturn function does return". */
+
+ /* Avoid "noreturn function does return" - but don't continue if BUG() is a NOP: */
for (;;)
- cpu_relax(); /* For when BUG is null */
+ cpu_relax();
}
static inline void sched_submit_work(struct task_struct *tsk)
@@ -3651,6 +3669,7 @@
BUG_ON(prio > MAX_PRIO);
rq = __task_rq_lock(p, &rf);
+ update_rq_clock(rq);
/*
* Idle task boosting is a nono in general. There is one
@@ -3725,7 +3744,8 @@
check_class_changed(rq, p, prev_class, oldprio);
out_unlock:
- preempt_disable(); /* avoid rq from going away on us */
+ /* Avoid rq from going away on us: */
+ preempt_disable();
__task_rq_unlock(rq, &rf);
balance_callback(rq);
@@ -3747,6 +3767,8 @@
* the task might be in the middle of scheduling on another CPU.
*/
rq = task_rq_lock(p, &rf);
+ update_rq_clock(rq);
+
/*
* The RT priorities are set via sched_setscheduler(), but we still
* allow the 'normal' nice value to be set - but as expected
@@ -3793,7 +3815,7 @@
*/
int can_nice(const struct task_struct *p, const int nice)
{
- /* convert nice value [19,-20] to rlimit style value [1,40] */
+ /* Convert nice value [19,-20] to rlimit style value [1,40]: */
int nice_rlim = nice_to_rlimit(nice);
return (nice_rlim <= task_rlimit(p, RLIMIT_NICE) ||
@@ -3849,7 +3871,7 @@
}
/**
- * idle_cpu - is a given cpu idle currently?
+ * idle_cpu - is a given CPU idle currently?
* @cpu: the processor in question.
*
* Return: 1 if the CPU is currently idle. 0 otherwise.
@@ -3873,10 +3895,10 @@
}
/**
- * idle_task - return the idle task for a given cpu.
+ * idle_task - return the idle task for a given CPU.
* @cpu: the processor in question.
*
- * Return: The idle task for the cpu @cpu.
+ * Return: The idle task for the CPU @cpu.
*/
struct task_struct *idle_task(int cpu)
{
@@ -4042,7 +4064,7 @@
}
/*
- * check the target process has a UID that matches the current process's
+ * Check the target process has a UID that matches the current process's:
*/
static bool check_same_owner(struct task_struct *p)
{
@@ -4057,8 +4079,7 @@
return match;
}
-static bool dl_param_changed(struct task_struct *p,
- const struct sched_attr *attr)
+static bool dl_param_changed(struct task_struct *p, const struct sched_attr *attr)
{
struct sched_dl_entity *dl_se = &p->dl;
@@ -4085,10 +4106,10 @@
int queue_flags = DEQUEUE_SAVE | DEQUEUE_MOVE;
struct rq *rq;
- /* may grab non-irq protected spin_locks */
+ /* May grab non-irq protected spin_locks: */
BUG_ON(in_interrupt());
recheck:
- /* double check policy once rq lock held */
+ /* Double check policy once rq lock held: */
if (policy < 0) {
reset_on_fork = p->sched_reset_on_fork;
policy = oldpolicy = p->policy;
@@ -4128,11 +4149,11 @@
unsigned long rlim_rtprio =
task_rlimit(p, RLIMIT_RTPRIO);
- /* can't set/change the rt policy */
+ /* Can't set/change the rt policy: */
if (policy != p->policy && !rlim_rtprio)
return -EPERM;
- /* can't increase priority */
+ /* Can't increase priority: */
if (attr->sched_priority > p->rt_priority &&
attr->sched_priority > rlim_rtprio)
return -EPERM;
@@ -4156,11 +4177,11 @@
return -EPERM;
}
- /* can't change other user's priorities */
+ /* Can't change other user's priorities: */
if (!check_same_owner(p))
return -EPERM;
- /* Normal users shall not reset the sched_reset_on_fork flag */
+ /* Normal users shall not reset the sched_reset_on_fork flag: */
if (p->sched_reset_on_fork && !reset_on_fork)
return -EPERM;
}
@@ -4172,16 +4193,17 @@
}
/*
- * make sure no PI-waiters arrive (or leave) while we are
+ * Make sure no PI-waiters arrive (or leave) while we are
* changing the priority of the task:
*
* To be able to change p->policy safely, the appropriate
* runqueue lock must be held.
*/
rq = task_rq_lock(p, &rf);
+ update_rq_clock(rq);
/*
- * Changing the policy of the stop threads its a very bad idea
+ * Changing the policy of the stop threads its a very bad idea:
*/
if (p == rq->stop) {
task_rq_unlock(rq, p, &rf);
@@ -4237,7 +4259,7 @@
#endif
}
- /* recheck policy now with rq lock held */
+ /* Re-check policy now with rq lock held: */
if (unlikely(oldpolicy != -1 && oldpolicy != p->policy)) {
policy = oldpolicy = -1;
task_rq_unlock(rq, p, &rf);
@@ -4294,15 +4316,15 @@
set_curr_task(rq, p);
check_class_changed(rq, p, prev_class, oldprio);
- preempt_disable(); /* avoid rq from going away on us */
+
+ /* Avoid rq from going away on us: */
+ preempt_disable();
task_rq_unlock(rq, p, &rf);
if (pi)
rt_mutex_adjust_pi(p);
- /*
- * Run balance callbacks after we've adjusted the PI chain.
- */
+ /* Run balance callbacks after we've adjusted the PI chain: */
balance_callback(rq);
preempt_enable();
@@ -4395,8 +4417,7 @@
/*
* Mimics kernel/events/core.c perf_copy_attr().
*/
-static int sched_copy_attr(struct sched_attr __user *uattr,
- struct sched_attr *attr)
+static int sched_copy_attr(struct sched_attr __user *uattr, struct sched_attr *attr)
{
u32 size;
int ret;
@@ -4404,19 +4425,19 @@
if (!access_ok(VERIFY_WRITE, uattr, SCHED_ATTR_SIZE_VER0))
return -EFAULT;
- /*
- * zero the full structure, so that a short copy will be nice.
- */
+ /* Zero the full structure, so that a short copy will be nice: */
memset(attr, 0, sizeof(*attr));
ret = get_user(size, &uattr->size);
if (ret)
return ret;
- if (size > PAGE_SIZE) /* silly large */
+ /* Bail out on silly large: */
+ if (size > PAGE_SIZE)
goto err_size;
- if (!size) /* abi compat */
+ /* ABI compatibility quirk: */
+ if (!size)
size = SCHED_ATTR_SIZE_VER0;
if (size < SCHED_ATTR_SIZE_VER0)
@@ -4451,7 +4472,7 @@
return -EFAULT;
/*
- * XXX: do we want to be lenient like existing syscalls; or do we want
+ * XXX: Do we want to be lenient like existing syscalls; or do we want
* to be strict and return an error on out-of-bounds values?
*/
attr->sched_nice = clamp(attr->sched_nice, MIN_NICE, MAX_NICE);
@@ -4471,10 +4492,8 @@
*
* Return: 0 on success. An error code otherwise.
*/
-SYSCALL_DEFINE3(sched_setscheduler, pid_t, pid, int, policy,
- struct sched_param __user *, param)
+SYSCALL_DEFINE3(sched_setscheduler, pid_t, pid, int, policy, struct sched_param __user *, param)
{
- /* negative values for policy are not valid */
if (policy < 0)
return -EINVAL;
@@ -4784,10 +4803,10 @@
}
/**
- * sys_sched_setaffinity - set the cpu affinity of a process
+ * sys_sched_setaffinity - set the CPU affinity of a process
* @pid: pid of the process
* @len: length in bytes of the bitmask pointed to by user_mask_ptr
- * @user_mask_ptr: user-space pointer to the new cpu mask
+ * @user_mask_ptr: user-space pointer to the new CPU mask
*
* Return: 0 on success. An error code otherwise.
*/
@@ -4835,10 +4854,10 @@
}
/**
- * sys_sched_getaffinity - get the cpu affinity of a process
+ * sys_sched_getaffinity - get the CPU affinity of a process
* @pid: pid of the process
* @len: length in bytes of the bitmask pointed to by user_mask_ptr
- * @user_mask_ptr: user-space pointer to hold the current cpu mask
+ * @user_mask_ptr: user-space pointer to hold the current CPU mask
*
* Return: size of CPU mask copied to user_mask_ptr on success. An
* error code otherwise.
@@ -4966,7 +4985,7 @@
* Typical broken usage is:
*
* while (!event)
- * yield();
+ * yield();
*
* where one assumes that yield() will let 'the other' process run that will
* make event true. If the current task is a SCHED_FIFO task that will never
@@ -5057,31 +5076,48 @@
}
EXPORT_SYMBOL_GPL(yield_to);
+int io_schedule_prepare(void)
+{
+ int old_iowait = current->in_iowait;
+
+ current->in_iowait = 1;
+ blk_schedule_flush_plug(current);
+
+ return old_iowait;
+}
+
+void io_schedule_finish(int token)
+{
+ current->in_iowait = token;
+}
+
/*
* This task is about to go to sleep on IO. Increment rq->nr_iowait so
* that process accounting knows that this is a task in IO wait state.
*/
long __sched io_schedule_timeout(long timeout)
{
- int old_iowait = current->in_iowait;
- struct rq *rq;
+ int token;
long ret;
- current->in_iowait = 1;
- blk_schedule_flush_plug(current);
-
- delayacct_blkio_start();
- rq = raw_rq();
- atomic_inc(&rq->nr_iowait);
+ token = io_schedule_prepare();
ret = schedule_timeout(timeout);
- current->in_iowait = old_iowait;
- atomic_dec(&rq->nr_iowait);
- delayacct_blkio_end();
+ io_schedule_finish(token);
return ret;
}
EXPORT_SYMBOL(io_schedule_timeout);
+void io_schedule(void)
+{
+ int token;
+
+ token = io_schedule_prepare();
+ schedule();
+ io_schedule_finish(token);
+}
+EXPORT_SYMBOL(io_schedule);
+
/**
* sys_sched_get_priority_max - return maximum RT priority.
* @policy: scheduling class.
@@ -5264,7 +5300,7 @@
/**
* init_idle - set up an idle thread for a given CPU
* @idle: task in question
- * @cpu: cpu the idle task belongs to
+ * @cpu: CPU the idle task belongs to
*
* NOTE: this function does not set the idle thread's NEED_RESCHED
* flag, to make booting more robust.
@@ -5295,7 +5331,7 @@
#endif
/*
* We're having a chicken and egg problem, even though we are
- * holding rq->lock, the cpu isn't yet set to this cpu so the
+ * holding rq->lock, the CPU isn't yet set to this CPU so the
* lockdep check in task_group() will fail.
*
* Similar case to sched_fork(). / Alternatively we could
@@ -5360,7 +5396,7 @@
/*
* Kthreads which disallow setaffinity shouldn't be moved
- * to a new cpuset; we don't want to change their cpu
+ * to a new cpuset; we don't want to change their CPU
* affinity and isolating such threads by their set of
* allowed nodes is unnecessary. Thus, cpusets are not
* applicable for such threads. This prevents checking for
@@ -5409,7 +5445,7 @@
#ifdef CONFIG_SMP
-static bool sched_smp_initialized __read_mostly;
+bool sched_smp_initialized __read_mostly;
#ifdef CONFIG_NUMA_BALANCING
/* Migrate current task p to target_cpu */
@@ -5461,7 +5497,7 @@
#ifdef CONFIG_HOTPLUG_CPU
/*
- * Ensures that the idle task is using init_mm right before its cpu goes
+ * Ensure that the idle task is using init_mm right before its CPU goes
* offline.
*/
void idle_task_exit(void)
@@ -5521,7 +5557,7 @@
{
struct rq *rq = dead_rq;
struct task_struct *next, *stop = rq->stop;
- struct pin_cookie cookie;
+ struct rq_flags rf, old_rf;
int dest_cpu;
/*
@@ -5545,16 +5581,16 @@
for (;;) {
/*
* There's this thread running, bail when that's the only
- * remaining thread.
+ * remaining thread:
*/
if (rq->nr_running == 1)
break;
/*
- * pick_next_task assumes pinned rq->lock.
+ * pick_next_task() assumes pinned rq->lock:
*/
- cookie = lockdep_pin_lock(&rq->lock);
- next = pick_next_task(rq, &fake_task, cookie);
+ rq_pin_lock(rq, &rf);
+ next = pick_next_task(rq, &fake_task, &rf);
BUG_ON(!next);
next->sched_class->put_prev_task(rq, next);
@@ -5567,7 +5603,7 @@
* because !cpu_active at this point, which means load-balance
* will not interfere. Also, stop-machine.
*/
- lockdep_unpin_lock(&rq->lock, cookie);
+ rq_unpin_lock(rq, &rf);
raw_spin_unlock(&rq->lock);
raw_spin_lock(&next->pi_lock);
raw_spin_lock(&rq->lock);
@@ -5582,6 +5618,13 @@
continue;
}
+ /*
+ * __migrate_task() may return with a different
+ * rq->lock held and a new cookie in 'rf', but we need
+ * to preserve rf::clock_update_flags for 'dead_rq'.
+ */
+ old_rf = rf;
+
/* Find suitable destination for @next, with force if needed. */
dest_cpu = select_fallback_rq(dead_rq->cpu, next);
@@ -5590,6 +5633,7 @@
raw_spin_unlock(&rq->lock);
rq = dead_rq;
raw_spin_lock(&rq->lock);
+ rf = old_rf;
}
raw_spin_unlock(&next->pi_lock);
}
@@ -5598,7 +5642,7 @@
}
#endif /* CONFIG_HOTPLUG_CPU */
-static void set_rq_online(struct rq *rq)
+void set_rq_online(struct rq *rq)
{
if (!rq->online) {
const struct sched_class *class;
@@ -5613,7 +5657,7 @@
}
}
-static void set_rq_offline(struct rq *rq)
+void set_rq_offline(struct rq *rq)
{
if (rq->online) {
const struct sched_class *class;
@@ -5635,1647 +5679,10 @@
rq->age_stamp = sched_clock_cpu(cpu);
}
-static cpumask_var_t sched_domains_tmpmask; /* sched_domains_mutex */
-
-#ifdef CONFIG_SCHED_DEBUG
-
-static __read_mostly int sched_debug_enabled;
-
-static int __init sched_debug_setup(char *str)
-{
- sched_debug_enabled = 1;
-
- return 0;
-}
-early_param("sched_debug", sched_debug_setup);
-
-static inline bool sched_debug(void)
-{
- return sched_debug_enabled;
-}
-
-static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level,
- struct cpumask *groupmask)
-{
- struct sched_group *group = sd->groups;
-
- cpumask_clear(groupmask);
-
- printk(KERN_DEBUG "%*s domain %d: ", level, "", level);
-
- if (!(sd->flags & SD_LOAD_BALANCE)) {
- printk("does not load-balance\n");
- if (sd->parent)
- printk(KERN_ERR "ERROR: !SD_LOAD_BALANCE domain"
- " has parent");
- return -1;
- }
-
- printk(KERN_CONT "span %*pbl level %s\n",
- cpumask_pr_args(sched_domain_span(sd)), sd->name);
-
- if (!cpumask_test_cpu(cpu, sched_domain_span(sd))) {
- printk(KERN_ERR "ERROR: domain->span does not contain "
- "CPU%d\n", cpu);
- }
- if (!cpumask_test_cpu(cpu, sched_group_cpus(group))) {
- printk(KERN_ERR "ERROR: domain->groups does not contain"
- " CPU%d\n", cpu);
- }
-
- printk(KERN_DEBUG "%*s groups:", level + 1, "");
- do {
- if (!group) {
- printk("\n");
- printk(KERN_ERR "ERROR: group is NULL\n");
- break;
- }
-
- if (!cpumask_weight(sched_group_cpus(group))) {
- printk(KERN_CONT "\n");
- printk(KERN_ERR "ERROR: empty group\n");
- break;
- }
-
- if (!(sd->flags & SD_OVERLAP) &&
- cpumask_intersects(groupmask, sched_group_cpus(group))) {
- printk(KERN_CONT "\n");
- printk(KERN_ERR "ERROR: repeated CPUs\n");
- break;
- }
-
- cpumask_or(groupmask, groupmask, sched_group_cpus(group));
-
- printk(KERN_CONT " %*pbl",
- cpumask_pr_args(sched_group_cpus(group)));
- if (group->sgc->capacity != SCHED_CAPACITY_SCALE) {
- printk(KERN_CONT " (cpu_capacity = %lu)",
- group->sgc->capacity);
- }
-
- group = group->next;
- } while (group != sd->groups);
- printk(KERN_CONT "\n");
-
- if (!cpumask_equal(sched_domain_span(sd), groupmask))
- printk(KERN_ERR "ERROR: groups don't span domain->span\n");
-
- if (sd->parent &&
- !cpumask_subset(groupmask, sched_domain_span(sd->parent)))
- printk(KERN_ERR "ERROR: parent span is not a superset "
- "of domain->span\n");
- return 0;
-}
-
-static void sched_domain_debug(struct sched_domain *sd, int cpu)
-{
- int level = 0;
-
- if (!sched_debug_enabled)
- return;
-
- if (!sd) {
- printk(KERN_DEBUG "CPU%d attaching NULL sched-domain.\n", cpu);
- return;
- }
-
- printk(KERN_DEBUG "CPU%d attaching sched-domain:\n", cpu);
-
- for (;;) {
- if (sched_domain_debug_one(sd, cpu, level, sched_domains_tmpmask))
- break;
- level++;
- sd = sd->parent;
- if (!sd)
- break;
- }
-}
-#else /* !CONFIG_SCHED_DEBUG */
-
-# define sched_debug_enabled 0
-# define sched_domain_debug(sd, cpu) do { } while (0)
-static inline bool sched_debug(void)
-{
- return false;
-}
-#endif /* CONFIG_SCHED_DEBUG */
-
-static int sd_degenerate(struct sched_domain *sd)
-{
- if (cpumask_weight(sched_domain_span(sd)) == 1)
- return 1;
-
- /* Following flags need at least 2 groups */
- if (sd->flags & (SD_LOAD_BALANCE |
- SD_BALANCE_NEWIDLE |
- SD_BALANCE_FORK |
- SD_BALANCE_EXEC |
- SD_SHARE_CPUCAPACITY |
- SD_ASYM_CPUCAPACITY |
- SD_SHARE_PKG_RESOURCES |
- SD_SHARE_POWERDOMAIN)) {
- if (sd->groups != sd->groups->next)
- return 0;
- }
-
- /* Following flags don't use groups */
- if (sd->flags & (SD_WAKE_AFFINE))
- return 0;
-
- return 1;
-}
-
-static int
-sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent)
-{
- unsigned long cflags = sd->flags, pflags = parent->flags;
-
- if (sd_degenerate(parent))
- return 1;
-
- if (!cpumask_equal(sched_domain_span(sd), sched_domain_span(parent)))
- return 0;
-
- /* Flags needing groups don't count if only 1 group in parent */
- if (parent->groups == parent->groups->next) {
- pflags &= ~(SD_LOAD_BALANCE |
- SD_BALANCE_NEWIDLE |
- SD_BALANCE_FORK |
- SD_BALANCE_EXEC |
- SD_ASYM_CPUCAPACITY |
- SD_SHARE_CPUCAPACITY |
- SD_SHARE_PKG_RESOURCES |
- SD_PREFER_SIBLING |
- SD_SHARE_POWERDOMAIN);
- if (nr_node_ids == 1)
- pflags &= ~SD_SERIALIZE;
- }
- if (~cflags & pflags)
- return 0;
-
- return 1;
-}
-
-static void free_rootdomain(struct rcu_head *rcu)
-{
- struct root_domain *rd = container_of(rcu, struct root_domain, rcu);
-
- cpupri_cleanup(&rd->cpupri);
- cpudl_cleanup(&rd->cpudl);
- free_cpumask_var(rd->dlo_mask);
- free_cpumask_var(rd->rto_mask);
- free_cpumask_var(rd->online);
- free_cpumask_var(rd->span);
- kfree(rd);
-}
-
-static void rq_attach_root(struct rq *rq, struct root_domain *rd)
-{
- struct root_domain *old_rd = NULL;
- unsigned long flags;
-
- raw_spin_lock_irqsave(&rq->lock, flags);
-
- if (rq->rd) {
- old_rd = rq->rd;
-
- if (cpumask_test_cpu(rq->cpu, old_rd->online))
- set_rq_offline(rq);
-
- cpumask_clear_cpu(rq->cpu, old_rd->span);
-
- /*
- * If we dont want to free the old_rd yet then
- * set old_rd to NULL to skip the freeing later
- * in this function:
- */
- if (!atomic_dec_and_test(&old_rd->refcount))
- old_rd = NULL;
- }
-
- atomic_inc(&rd->refcount);
- rq->rd = rd;
-
- cpumask_set_cpu(rq->cpu, rd->span);
- if (cpumask_test_cpu(rq->cpu, cpu_active_mask))
- set_rq_online(rq);
-
- raw_spin_unlock_irqrestore(&rq->lock, flags);
-
- if (old_rd)
- call_rcu_sched(&old_rd->rcu, free_rootdomain);
-}
-
-static int init_rootdomain(struct root_domain *rd)
-{
- memset(rd, 0, sizeof(*rd));
-
- if (!zalloc_cpumask_var(&rd->span, GFP_KERNEL))
- goto out;
- if (!zalloc_cpumask_var(&rd->online, GFP_KERNEL))
- goto free_span;
- if (!zalloc_cpumask_var(&rd->dlo_mask, GFP_KERNEL))
- goto free_online;
- if (!zalloc_cpumask_var(&rd->rto_mask, GFP_KERNEL))
- goto free_dlo_mask;
-
- init_dl_bw(&rd->dl_bw);
- if (cpudl_init(&rd->cpudl) != 0)
- goto free_dlo_mask;
-
- if (cpupri_init(&rd->cpupri) != 0)
- goto free_rto_mask;
- return 0;
-
-free_rto_mask:
- free_cpumask_var(rd->rto_mask);
-free_dlo_mask:
- free_cpumask_var(rd->dlo_mask);
-free_online:
- free_cpumask_var(rd->online);
-free_span:
- free_cpumask_var(rd->span);
-out:
- return -ENOMEM;
-}
-
/*
- * By default the system creates a single root-domain with all cpus as
- * members (mimicking the global state we have today).
+ * used to mark begin/end of suspend/resume:
*/
-struct root_domain def_root_domain;
-
-static void init_defrootdomain(void)
-{
- init_rootdomain(&def_root_domain);
-
- atomic_set(&def_root_domain.refcount, 1);
-}
-
-static struct root_domain *alloc_rootdomain(void)
-{
- struct root_domain *rd;
-
- rd = kmalloc(sizeof(*rd), GFP_KERNEL);
- if (!rd)
- return NULL;
-
- if (init_rootdomain(rd) != 0) {
- kfree(rd);
- return NULL;
- }
-
- return rd;
-}
-
-static void free_sched_groups(struct sched_group *sg, int free_sgc)
-{
- struct sched_group *tmp, *first;
-
- if (!sg)
- return;
-
- first = sg;
- do {
- tmp = sg->next;
-
- if (free_sgc && atomic_dec_and_test(&sg->sgc->ref))
- kfree(sg->sgc);
-
- kfree(sg);
- sg = tmp;
- } while (sg != first);
-}
-
-static void destroy_sched_domain(struct sched_domain *sd)
-{
- /*
- * If its an overlapping domain it has private groups, iterate and
- * nuke them all.
- */
- if (sd->flags & SD_OVERLAP) {
- free_sched_groups(sd->groups, 1);
- } else if (atomic_dec_and_test(&sd->groups->ref)) {
- kfree(sd->groups->sgc);
- kfree(sd->groups);
- }
- if (sd->shared && atomic_dec_and_test(&sd->shared->ref))
- kfree(sd->shared);
- kfree(sd);
-}
-
-static void destroy_sched_domains_rcu(struct rcu_head *rcu)
-{
- struct sched_domain *sd = container_of(rcu, struct sched_domain, rcu);
-
- while (sd) {
- struct sched_domain *parent = sd->parent;
- destroy_sched_domain(sd);
- sd = parent;
- }
-}
-
-static void destroy_sched_domains(struct sched_domain *sd)
-{
- if (sd)
- call_rcu(&sd->rcu, destroy_sched_domains_rcu);
-}
-
-/*
- * Keep a special pointer to the highest sched_domain that has
- * SD_SHARE_PKG_RESOURCE set (Last Level Cache Domain) for this
- * allows us to avoid some pointer chasing select_idle_sibling().
- *
- * Also keep a unique ID per domain (we use the first cpu number in
- * the cpumask of the domain), this allows us to quickly tell if
- * two cpus are in the same cache domain, see cpus_share_cache().
- */
-DEFINE_PER_CPU(struct sched_domain *, sd_llc);
-DEFINE_PER_CPU(int, sd_llc_size);
-DEFINE_PER_CPU(int, sd_llc_id);
-DEFINE_PER_CPU(struct sched_domain_shared *, sd_llc_shared);
-DEFINE_PER_CPU(struct sched_domain *, sd_numa);
-DEFINE_PER_CPU(struct sched_domain *, sd_asym);
-
-static void update_top_cache_domain(int cpu)
-{
- struct sched_domain_shared *sds = NULL;
- struct sched_domain *sd;
- int id = cpu;
- int size = 1;
-
- sd = highest_flag_domain(cpu, SD_SHARE_PKG_RESOURCES);
- if (sd) {
- id = cpumask_first(sched_domain_span(sd));
- size = cpumask_weight(sched_domain_span(sd));
- sds = sd->shared;
- }
-
- rcu_assign_pointer(per_cpu(sd_llc, cpu), sd);
- per_cpu(sd_llc_size, cpu) = size;
- per_cpu(sd_llc_id, cpu) = id;
- rcu_assign_pointer(per_cpu(sd_llc_shared, cpu), sds);
-
- sd = lowest_flag_domain(cpu, SD_NUMA);
- rcu_assign_pointer(per_cpu(sd_numa, cpu), sd);
-
- sd = highest_flag_domain(cpu, SD_ASYM_PACKING);
- rcu_assign_pointer(per_cpu(sd_asym, cpu), sd);
-}
-
-/*
- * Attach the domain 'sd' to 'cpu' as its base domain. Callers must
- * hold the hotplug lock.
- */
-static void
-cpu_attach_domain(struct sched_domain *sd, struct root_domain *rd, int cpu)
-{
- struct rq *rq = cpu_rq(cpu);
- struct sched_domain *tmp;
-
- /* Remove the sched domains which do not contribute to scheduling. */
- for (tmp = sd; tmp; ) {
- struct sched_domain *parent = tmp->parent;
- if (!parent)
- break;
-
- if (sd_parent_degenerate(tmp, parent)) {
- tmp->parent = parent->parent;
- if (parent->parent)
- parent->parent->child = tmp;
- /*
- * Transfer SD_PREFER_SIBLING down in case of a
- * degenerate parent; the spans match for this
- * so the property transfers.
- */
- if (parent->flags & SD_PREFER_SIBLING)
- tmp->flags |= SD_PREFER_SIBLING;
- destroy_sched_domain(parent);
- } else
- tmp = tmp->parent;
- }
-
- if (sd && sd_degenerate(sd)) {
- tmp = sd;
- sd = sd->parent;
- destroy_sched_domain(tmp);
- if (sd)
- sd->child = NULL;
- }
-
- sched_domain_debug(sd, cpu);
-
- rq_attach_root(rq, rd);
- tmp = rq->sd;
- rcu_assign_pointer(rq->sd, sd);
- destroy_sched_domains(tmp);
-
- update_top_cache_domain(cpu);
-}
-
-/* Setup the mask of cpus configured for isolated domains */
-static int __init isolated_cpu_setup(char *str)
-{
- int ret;
-
- alloc_bootmem_cpumask_var(&cpu_isolated_map);
- ret = cpulist_parse(str, cpu_isolated_map);
- if (ret) {
- pr_err("sched: Error, all isolcpus= values must be between 0 and %d\n", nr_cpu_ids);
- return 0;
- }
- return 1;
-}
-__setup("isolcpus=", isolated_cpu_setup);
-
-struct s_data {
- struct sched_domain ** __percpu sd;
- struct root_domain *rd;
-};
-
-enum s_alloc {
- sa_rootdomain,
- sa_sd,
- sa_sd_storage,
- sa_none,
-};
-
-/*
- * Build an iteration mask that can exclude certain CPUs from the upwards
- * domain traversal.
- *
- * Asymmetric node setups can result in situations where the domain tree is of
- * unequal depth, make sure to skip domains that already cover the entire
- * range.
- *
- * In that case build_sched_domains() will have terminated the iteration early
- * and our sibling sd spans will be empty. Domains should always include the
- * cpu they're built on, so check that.
- *
- */
-static void build_group_mask(struct sched_domain *sd, struct sched_group *sg)
-{
- const struct cpumask *span = sched_domain_span(sd);
- struct sd_data *sdd = sd->private;
- struct sched_domain *sibling;
- int i;
-
- for_each_cpu(i, span) {
- sibling = *per_cpu_ptr(sdd->sd, i);
- if (!cpumask_test_cpu(i, sched_domain_span(sibling)))
- continue;
-
- cpumask_set_cpu(i, sched_group_mask(sg));
- }
-}
-
-/*
- * Return the canonical balance cpu for this group, this is the first cpu
- * of this group that's also in the iteration mask.
- */
-int group_balance_cpu(struct sched_group *sg)
-{
- return cpumask_first_and(sched_group_cpus(sg), sched_group_mask(sg));
-}
-
-static int
-build_overlap_sched_groups(struct sched_domain *sd, int cpu)
-{
- struct sched_group *first = NULL, *last = NULL, *groups = NULL, *sg;
- const struct cpumask *span = sched_domain_span(sd);
- struct cpumask *covered = sched_domains_tmpmask;
- struct sd_data *sdd = sd->private;
- struct sched_domain *sibling;
- int i;
-
- cpumask_clear(covered);
-
- for_each_cpu(i, span) {
- struct cpumask *sg_span;
-
- if (cpumask_test_cpu(i, covered))
- continue;
-
- sibling = *per_cpu_ptr(sdd->sd, i);
-
- /* See the comment near build_group_mask(). */
- if (!cpumask_test_cpu(i, sched_domain_span(sibling)))
- continue;
-
- sg = kzalloc_node(sizeof(struct sched_group) + cpumask_size(),
- GFP_KERNEL, cpu_to_node(cpu));
-
- if (!sg)
- goto fail;
-
- sg_span = sched_group_cpus(sg);
- if (sibling->child)
- cpumask_copy(sg_span, sched_domain_span(sibling->child));
- else
- cpumask_set_cpu(i, sg_span);
-
- cpumask_or(covered, covered, sg_span);
-
- sg->sgc = *per_cpu_ptr(sdd->sgc, i);
- if (atomic_inc_return(&sg->sgc->ref) == 1)
- build_group_mask(sd, sg);
-
- /*
- * Initialize sgc->capacity such that even if we mess up the
- * domains and no possible iteration will get us here, we won't
- * die on a /0 trap.
- */
- sg->sgc->capacity = SCHED_CAPACITY_SCALE * cpumask_weight(sg_span);
- sg->sgc->min_capacity = SCHED_CAPACITY_SCALE;
-
- /*
- * Make sure the first group of this domain contains the
- * canonical balance cpu. Otherwise the sched_domain iteration
- * breaks. See update_sg_lb_stats().
- */
- if ((!groups && cpumask_test_cpu(cpu, sg_span)) ||
- group_balance_cpu(sg) == cpu)
- groups = sg;
-
- if (!first)
- first = sg;
- if (last)
- last->next = sg;
- last = sg;
- last->next = first;
- }
- sd->groups = groups;
-
- return 0;
-
-fail:
- free_sched_groups(first, 0);
-
- return -ENOMEM;
-}
-
-static int get_group(int cpu, struct sd_data *sdd, struct sched_group **sg)
-{
- struct sched_domain *sd = *per_cpu_ptr(sdd->sd, cpu);
- struct sched_domain *child = sd->child;
-
- if (child)
- cpu = cpumask_first(sched_domain_span(child));
-
- if (sg) {
- *sg = *per_cpu_ptr(sdd->sg, cpu);
- (*sg)->sgc = *per_cpu_ptr(sdd->sgc, cpu);
- atomic_set(&(*sg)->sgc->ref, 1); /* for claim_allocations */
- }
-
- return cpu;
-}
-
-/*
- * build_sched_groups will build a circular linked list of the groups
- * covered by the given span, and will set each group's ->cpumask correctly,
- * and ->cpu_capacity to 0.
- *
- * Assumes the sched_domain tree is fully constructed
- */
-static int
-build_sched_groups(struct sched_domain *sd, int cpu)
-{
- struct sched_group *first = NULL, *last = NULL;
- struct sd_data *sdd = sd->private;
- const struct cpumask *span = sched_domain_span(sd);
- struct cpumask *covered;
- int i;
-
- get_group(cpu, sdd, &sd->groups);
- atomic_inc(&sd->groups->ref);
-
- if (cpu != cpumask_first(span))
- return 0;
-
- lockdep_assert_held(&sched_domains_mutex);
- covered = sched_domains_tmpmask;
-
- cpumask_clear(covered);
-
- for_each_cpu(i, span) {
- struct sched_group *sg;
- int group, j;
-
- if (cpumask_test_cpu(i, covered))
- continue;
-
- group = get_group(i, sdd, &sg);
- cpumask_setall(sched_group_mask(sg));
-
- for_each_cpu(j, span) {
- if (get_group(j, sdd, NULL) != group)
- continue;
-
- cpumask_set_cpu(j, covered);
- cpumask_set_cpu(j, sched_group_cpus(sg));
- }
-
- if (!first)
- first = sg;
- if (last)
- last->next = sg;
- last = sg;
- }
- last->next = first;
-
- return 0;
-}
-
-/*
- * Initialize sched groups cpu_capacity.
- *
- * cpu_capacity indicates the capacity of sched group, which is used while
- * distributing the load between different sched groups in a sched domain.
- * Typically cpu_capacity for all the groups in a sched domain will be same
- * unless there are asymmetries in the topology. If there are asymmetries,
- * group having more cpu_capacity will pickup more load compared to the
- * group having less cpu_capacity.
- */
-static void init_sched_groups_capacity(int cpu, struct sched_domain *sd)
-{
- struct sched_group *sg = sd->groups;
-
- WARN_ON(!sg);
-
- do {
- int cpu, max_cpu = -1;
-
- sg->group_weight = cpumask_weight(sched_group_cpus(sg));
-
- if (!(sd->flags & SD_ASYM_PACKING))
- goto next;
-
- for_each_cpu(cpu, sched_group_cpus(sg)) {
- if (max_cpu < 0)
- max_cpu = cpu;
- else if (sched_asym_prefer(cpu, max_cpu))
- max_cpu = cpu;
- }
- sg->asym_prefer_cpu = max_cpu;
-
-next:
- sg = sg->next;
- } while (sg != sd->groups);
-
- if (cpu != group_balance_cpu(sg))
- return;
-
- update_group_capacity(sd, cpu);
-}
-
-/*
- * Initializers for schedule domains
- * Non-inlined to reduce accumulated stack pressure in build_sched_domains()
- */
-
-static int default_relax_domain_level = -1;
-int sched_domain_level_max;
-
-static int __init setup_relax_domain_level(char *str)
-{
- if (kstrtoint(str, 0, &default_relax_domain_level))
- pr_warn("Unable to set relax_domain_level\n");
-
- return 1;
-}
-__setup("relax_domain_level=", setup_relax_domain_level);
-
-static void set_domain_attribute(struct sched_domain *sd,
- struct sched_domain_attr *attr)
-{
- int request;
-
- if (!attr || attr->relax_domain_level < 0) {
- if (default_relax_domain_level < 0)
- return;
- else
- request = default_relax_domain_level;
- } else
- request = attr->relax_domain_level;
- if (request < sd->level) {
- /* turn off idle balance on this domain */
- sd->flags &= ~(SD_BALANCE_WAKE|SD_BALANCE_NEWIDLE);
- } else {
- /* turn on idle balance on this domain */
- sd->flags |= (SD_BALANCE_WAKE|SD_BALANCE_NEWIDLE);
- }
-}
-
-static void __sdt_free(const struct cpumask *cpu_map);
-static int __sdt_alloc(const struct cpumask *cpu_map);
-
-static void __free_domain_allocs(struct s_data *d, enum s_alloc what,
- const struct cpumask *cpu_map)
-{
- switch (what) {
- case sa_rootdomain:
- if (!atomic_read(&d->rd->refcount))
- free_rootdomain(&d->rd->rcu); /* fall through */
- case sa_sd:
- free_percpu(d->sd); /* fall through */
- case sa_sd_storage:
- __sdt_free(cpu_map); /* fall through */
- case sa_none:
- break;
- }
-}
-
-static enum s_alloc __visit_domain_allocation_hell(struct s_data *d,
- const struct cpumask *cpu_map)
-{
- memset(d, 0, sizeof(*d));
-
- if (__sdt_alloc(cpu_map))
- return sa_sd_storage;
- d->sd = alloc_percpu(struct sched_domain *);
- if (!d->sd)
- return sa_sd_storage;
- d->rd = alloc_rootdomain();
- if (!d->rd)
- return sa_sd;
- return sa_rootdomain;
-}
-
-/*
- * NULL the sd_data elements we've used to build the sched_domain and
- * sched_group structure so that the subsequent __free_domain_allocs()
- * will not free the data we're using.
- */
-static void claim_allocations(int cpu, struct sched_domain *sd)
-{
- struct sd_data *sdd = sd->private;
-
- WARN_ON_ONCE(*per_cpu_ptr(sdd->sd, cpu) != sd);
- *per_cpu_ptr(sdd->sd, cpu) = NULL;
-
- if (atomic_read(&(*per_cpu_ptr(sdd->sds, cpu))->ref))
- *per_cpu_ptr(sdd->sds, cpu) = NULL;
-
- if (atomic_read(&(*per_cpu_ptr(sdd->sg, cpu))->ref))
- *per_cpu_ptr(sdd->sg, cpu) = NULL;
-
- if (atomic_read(&(*per_cpu_ptr(sdd->sgc, cpu))->ref))
- *per_cpu_ptr(sdd->sgc, cpu) = NULL;
-}
-
-#ifdef CONFIG_NUMA
-static int sched_domains_numa_levels;
-enum numa_topology_type sched_numa_topology_type;
-static int *sched_domains_numa_distance;
-int sched_max_numa_distance;
-static struct cpumask ***sched_domains_numa_masks;
-static int sched_domains_curr_level;
-#endif
-
-/*
- * SD_flags allowed in topology descriptions.
- *
- * These flags are purely descriptive of the topology and do not prescribe
- * behaviour. Behaviour is artificial and mapped in the below sd_init()
- * function:
- *
- * SD_SHARE_CPUCAPACITY - describes SMT topologies
- * SD_SHARE_PKG_RESOURCES - describes shared caches
- * SD_NUMA - describes NUMA topologies
- * SD_SHARE_POWERDOMAIN - describes shared power domain
- * SD_ASYM_CPUCAPACITY - describes mixed capacity topologies
- *
- * Odd one out, which beside describing the topology has a quirk also
- * prescribes the desired behaviour that goes along with it:
- *
- * SD_ASYM_PACKING - describes SMT quirks
- */
-#define TOPOLOGY_SD_FLAGS \
- (SD_SHARE_CPUCAPACITY | \
- SD_SHARE_PKG_RESOURCES | \
- SD_NUMA | \
- SD_ASYM_PACKING | \
- SD_ASYM_CPUCAPACITY | \
- SD_SHARE_POWERDOMAIN)
-
-static struct sched_domain *
-sd_init(struct sched_domain_topology_level *tl,
- const struct cpumask *cpu_map,
- struct sched_domain *child, int cpu)
-{
- struct sd_data *sdd = &tl->data;
- struct sched_domain *sd = *per_cpu_ptr(sdd->sd, cpu);
- int sd_id, sd_weight, sd_flags = 0;
-
-#ifdef CONFIG_NUMA
- /*
- * Ugly hack to pass state to sd_numa_mask()...
- */
- sched_domains_curr_level = tl->numa_level;
-#endif
-
- sd_weight = cpumask_weight(tl->mask(cpu));
-
- if (tl->sd_flags)
- sd_flags = (*tl->sd_flags)();
- if (WARN_ONCE(sd_flags & ~TOPOLOGY_SD_FLAGS,
- "wrong sd_flags in topology description\n"))
- sd_flags &= ~TOPOLOGY_SD_FLAGS;
-
- *sd = (struct sched_domain){
- .min_interval = sd_weight,
- .max_interval = 2*sd_weight,
- .busy_factor = 32,
- .imbalance_pct = 125,
-
- .cache_nice_tries = 0,
- .busy_idx = 0,
- .idle_idx = 0,
- .newidle_idx = 0,
- .wake_idx = 0,
- .forkexec_idx = 0,
-
- .flags = 1*SD_LOAD_BALANCE
- | 1*SD_BALANCE_NEWIDLE
- | 1*SD_BALANCE_EXEC
- | 1*SD_BALANCE_FORK
- | 0*SD_BALANCE_WAKE
- | 1*SD_WAKE_AFFINE
- | 0*SD_SHARE_CPUCAPACITY
- | 0*SD_SHARE_PKG_RESOURCES
- | 0*SD_SERIALIZE
- | 0*SD_PREFER_SIBLING
- | 0*SD_NUMA
- | sd_flags
- ,
-
- .last_balance = jiffies,
- .balance_interval = sd_weight,
- .smt_gain = 0,
- .max_newidle_lb_cost = 0,
- .next_decay_max_lb_cost = jiffies,
- .child = child,
-#ifdef CONFIG_SCHED_DEBUG
- .name = tl->name,
-#endif
- };
-
- cpumask_and(sched_domain_span(sd), cpu_map, tl->mask(cpu));
- sd_id = cpumask_first(sched_domain_span(sd));
-
- /*
- * Convert topological properties into behaviour.
- */
-
- if (sd->flags & SD_ASYM_CPUCAPACITY) {
- struct sched_domain *t = sd;
-
- for_each_lower_domain(t)
- t->flags |= SD_BALANCE_WAKE;
- }
-
- if (sd->flags & SD_SHARE_CPUCAPACITY) {
- sd->flags |= SD_PREFER_SIBLING;
- sd->imbalance_pct = 110;
- sd->smt_gain = 1178; /* ~15% */
-
- } else if (sd->flags & SD_SHARE_PKG_RESOURCES) {
- sd->imbalance_pct = 117;
- sd->cache_nice_tries = 1;
- sd->busy_idx = 2;
-
-#ifdef CONFIG_NUMA
- } else if (sd->flags & SD_NUMA) {
- sd->cache_nice_tries = 2;
- sd->busy_idx = 3;
- sd->idle_idx = 2;
-
- sd->flags |= SD_SERIALIZE;
- if (sched_domains_numa_distance[tl->numa_level] > RECLAIM_DISTANCE) {
- sd->flags &= ~(SD_BALANCE_EXEC |
- SD_BALANCE_FORK |
- SD_WAKE_AFFINE);
- }
-
-#endif
- } else {
- sd->flags |= SD_PREFER_SIBLING;
- sd->cache_nice_tries = 1;
- sd->busy_idx = 2;
- sd->idle_idx = 1;
- }
-
- /*
- * For all levels sharing cache; connect a sched_domain_shared
- * instance.
- */
- if (sd->flags & SD_SHARE_PKG_RESOURCES) {
- sd->shared = *per_cpu_ptr(sdd->sds, sd_id);
- atomic_inc(&sd->shared->ref);
- atomic_set(&sd->shared->nr_busy_cpus, sd_weight);
- }
-
- sd->private = sdd;
-
- return sd;
-}
-
-/*
- * Topology list, bottom-up.
- */
-static struct sched_domain_topology_level default_topology[] = {
-#ifdef CONFIG_SCHED_SMT
- { cpu_smt_mask, cpu_smt_flags, SD_INIT_NAME(SMT) },
-#endif
-#ifdef CONFIG_SCHED_MC
- { cpu_coregroup_mask, cpu_core_flags, SD_INIT_NAME(MC) },
-#endif
- { cpu_cpu_mask, SD_INIT_NAME(DIE) },
- { NULL, },
-};
-
-static struct sched_domain_topology_level *sched_domain_topology =
- default_topology;
-
-#define for_each_sd_topology(tl) \
- for (tl = sched_domain_topology; tl->mask; tl++)
-
-void set_sched_topology(struct sched_domain_topology_level *tl)
-{
- if (WARN_ON_ONCE(sched_smp_initialized))
- return;
-
- sched_domain_topology = tl;
-}
-
-#ifdef CONFIG_NUMA
-
-static const struct cpumask *sd_numa_mask(int cpu)
-{
- return sched_domains_numa_masks[sched_domains_curr_level][cpu_to_node(cpu)];
-}
-
-static void sched_numa_warn(const char *str)
-{
- static int done = false;
- int i,j;
-
- if (done)
- return;
-
- done = true;
-
- printk(KERN_WARNING "ERROR: %s\n\n", str);
-
- for (i = 0; i < nr_node_ids; i++) {
- printk(KERN_WARNING " ");
- for (j = 0; j < nr_node_ids; j++)
- printk(KERN_CONT "%02d ", node_distance(i,j));
- printk(KERN_CONT "\n");
- }
- printk(KERN_WARNING "\n");
-}
-
-bool find_numa_distance(int distance)
-{
- int i;
-
- if (distance == node_distance(0, 0))
- return true;
-
- for (i = 0; i < sched_domains_numa_levels; i++) {
- if (sched_domains_numa_distance[i] == distance)
- return true;
- }
-
- return false;
-}
-
-/*
- * A system can have three types of NUMA topology:
- * NUMA_DIRECT: all nodes are directly connected, or not a NUMA system
- * NUMA_GLUELESS_MESH: some nodes reachable through intermediary nodes
- * NUMA_BACKPLANE: nodes can reach other nodes through a backplane
- *
- * The difference between a glueless mesh topology and a backplane
- * topology lies in whether communication between not directly
- * connected nodes goes through intermediary nodes (where programs
- * could run), or through backplane controllers. This affects
- * placement of programs.
- *
- * The type of topology can be discerned with the following tests:
- * - If the maximum distance between any nodes is 1 hop, the system
- * is directly connected.
- * - If for two nodes A and B, located N > 1 hops away from each other,
- * there is an intermediary node C, which is < N hops away from both
- * nodes A and B, the system is a glueless mesh.
- */
-static void init_numa_topology_type(void)
-{
- int a, b, c, n;
-
- n = sched_max_numa_distance;
-
- if (sched_domains_numa_levels <= 1) {
- sched_numa_topology_type = NUMA_DIRECT;
- return;
- }
-
- for_each_online_node(a) {
- for_each_online_node(b) {
- /* Find two nodes furthest removed from each other. */
- if (node_distance(a, b) < n)
- continue;
-
- /* Is there an intermediary node between a and b? */
- for_each_online_node(c) {
- if (node_distance(a, c) < n &&
- node_distance(b, c) < n) {
- sched_numa_topology_type =
- NUMA_GLUELESS_MESH;
- return;
- }
- }
-
- sched_numa_topology_type = NUMA_BACKPLANE;
- return;
- }
- }
-}
-
-static void sched_init_numa(void)
-{
- int next_distance, curr_distance = node_distance(0, 0);
- struct sched_domain_topology_level *tl;
- int level = 0;
- int i, j, k;
-
- sched_domains_numa_distance = kzalloc(sizeof(int) * nr_node_ids, GFP_KERNEL);
- if (!sched_domains_numa_distance)
- return;
-
- /*
- * O(nr_nodes^2) deduplicating selection sort -- in order to find the
- * unique distances in the node_distance() table.
- *
- * Assumes node_distance(0,j) includes all distances in
- * node_distance(i,j) in order to avoid cubic time.
- */
- next_distance = curr_distance;
- for (i = 0; i < nr_node_ids; i++) {
- for (j = 0; j < nr_node_ids; j++) {
- for (k = 0; k < nr_node_ids; k++) {
- int distance = node_distance(i, k);
-
- if (distance > curr_distance &&
- (distance < next_distance ||
- next_distance == curr_distance))
- next_distance = distance;
-
- /*
- * While not a strong assumption it would be nice to know
- * about cases where if node A is connected to B, B is not
- * equally connected to A.
- */
- if (sched_debug() && node_distance(k, i) != distance)
- sched_numa_warn("Node-distance not symmetric");
-
- if (sched_debug() && i && !find_numa_distance(distance))
- sched_numa_warn("Node-0 not representative");
- }
- if (next_distance != curr_distance) {
- sched_domains_numa_distance[level++] = next_distance;
- sched_domains_numa_levels = level;
- curr_distance = next_distance;
- } else break;
- }
-
- /*
- * In case of sched_debug() we verify the above assumption.
- */
- if (!sched_debug())
- break;
- }
-
- if (!level)
- return;
-
- /*
- * 'level' contains the number of unique distances, excluding the
- * identity distance node_distance(i,i).
- *
- * The sched_domains_numa_distance[] array includes the actual distance
- * numbers.
- */
-
- /*
- * Here, we should temporarily reset sched_domains_numa_levels to 0.
- * If it fails to allocate memory for array sched_domains_numa_masks[][],
- * the array will contain less then 'level' members. This could be
- * dangerous when we use it to iterate array sched_domains_numa_masks[][]
- * in other functions.
- *
- * We reset it to 'level' at the end of this function.
- */
- sched_domains_numa_levels = 0;
-
- sched_domains_numa_masks = kzalloc(sizeof(void *) * level, GFP_KERNEL);
- if (!sched_domains_numa_masks)
- return;
-
- /*
- * Now for each level, construct a mask per node which contains all
- * cpus of nodes that are that many hops away from us.
- */
- for (i = 0; i < level; i++) {
- sched_domains_numa_masks[i] =
- kzalloc(nr_node_ids * sizeof(void *), GFP_KERNEL);
- if (!sched_domains_numa_masks[i])
- return;
-
- for (j = 0; j < nr_node_ids; j++) {
- struct cpumask *mask = kzalloc(cpumask_size(), GFP_KERNEL);
- if (!mask)
- return;
-
- sched_domains_numa_masks[i][j] = mask;
-
- for_each_node(k) {
- if (node_distance(j, k) > sched_domains_numa_distance[i])
- continue;
-
- cpumask_or(mask, mask, cpumask_of_node(k));
- }
- }
- }
-
- /* Compute default topology size */
- for (i = 0; sched_domain_topology[i].mask; i++);
-
- tl = kzalloc((i + level + 1) *
- sizeof(struct sched_domain_topology_level), GFP_KERNEL);
- if (!tl)
- return;
-
- /*
- * Copy the default topology bits..
- */
- for (i = 0; sched_domain_topology[i].mask; i++)
- tl[i] = sched_domain_topology[i];
-
- /*
- * .. and append 'j' levels of NUMA goodness.
- */
- for (j = 0; j < level; i++, j++) {
- tl[i] = (struct sched_domain_topology_level){
- .mask = sd_numa_mask,
- .sd_flags = cpu_numa_flags,
- .flags = SDTL_OVERLAP,
- .numa_level = j,
- SD_INIT_NAME(NUMA)
- };
- }
-
- sched_domain_topology = tl;
-
- sched_domains_numa_levels = level;
- sched_max_numa_distance = sched_domains_numa_distance[level - 1];
-
- init_numa_topology_type();
-}
-
-static void sched_domains_numa_masks_set(unsigned int cpu)
-{
- int node = cpu_to_node(cpu);
- int i, j;
-
- for (i = 0; i < sched_domains_numa_levels; i++) {
- for (j = 0; j < nr_node_ids; j++) {
- if (node_distance(j, node) <= sched_domains_numa_distance[i])
- cpumask_set_cpu(cpu, sched_domains_numa_masks[i][j]);
- }
- }
-}
-
-static void sched_domains_numa_masks_clear(unsigned int cpu)
-{
- int i, j;
-
- for (i = 0; i < sched_domains_numa_levels; i++) {
- for (j = 0; j < nr_node_ids; j++)
- cpumask_clear_cpu(cpu, sched_domains_numa_masks[i][j]);
- }
-}
-
-#else
-static inline void sched_init_numa(void) { }
-static void sched_domains_numa_masks_set(unsigned int cpu) { }
-static void sched_domains_numa_masks_clear(unsigned int cpu) { }
-#endif /* CONFIG_NUMA */
-
-static int __sdt_alloc(const struct cpumask *cpu_map)
-{
- struct sched_domain_topology_level *tl;
- int j;
-
- for_each_sd_topology(tl) {
- struct sd_data *sdd = &tl->data;
-
- sdd->sd = alloc_percpu(struct sched_domain *);
- if (!sdd->sd)
- return -ENOMEM;
-
- sdd->sds = alloc_percpu(struct sched_domain_shared *);
- if (!sdd->sds)
- return -ENOMEM;
-
- sdd->sg = alloc_percpu(struct sched_group *);
- if (!sdd->sg)
- return -ENOMEM;
-
- sdd->sgc = alloc_percpu(struct sched_group_capacity *);
- if (!sdd->sgc)
- return -ENOMEM;
-
- for_each_cpu(j, cpu_map) {
- struct sched_domain *sd;
- struct sched_domain_shared *sds;
- struct sched_group *sg;
- struct sched_group_capacity *sgc;
-
- sd = kzalloc_node(sizeof(struct sched_domain) + cpumask_size(),
- GFP_KERNEL, cpu_to_node(j));
- if (!sd)
- return -ENOMEM;
-
- *per_cpu_ptr(sdd->sd, j) = sd;
-
- sds = kzalloc_node(sizeof(struct sched_domain_shared),
- GFP_KERNEL, cpu_to_node(j));
- if (!sds)
- return -ENOMEM;
-
- *per_cpu_ptr(sdd->sds, j) = sds;
-
- sg = kzalloc_node(sizeof(struct sched_group) + cpumask_size(),
- GFP_KERNEL, cpu_to_node(j));
- if (!sg)
- return -ENOMEM;
-
- sg->next = sg;
-
- *per_cpu_ptr(sdd->sg, j) = sg;
-
- sgc = kzalloc_node(sizeof(struct sched_group_capacity) + cpumask_size(),
- GFP_KERNEL, cpu_to_node(j));
- if (!sgc)
- return -ENOMEM;
-
- *per_cpu_ptr(sdd->sgc, j) = sgc;
- }
- }
-
- return 0;
-}
-
-static void __sdt_free(const struct cpumask *cpu_map)
-{
- struct sched_domain_topology_level *tl;
- int j;
-
- for_each_sd_topology(tl) {
- struct sd_data *sdd = &tl->data;
-
- for_each_cpu(j, cpu_map) {
- struct sched_domain *sd;
-
- if (sdd->sd) {
- sd = *per_cpu_ptr(sdd->sd, j);
- if (sd && (sd->flags & SD_OVERLAP))
- free_sched_groups(sd->groups, 0);
- kfree(*per_cpu_ptr(sdd->sd, j));
- }
-
- if (sdd->sds)
- kfree(*per_cpu_ptr(sdd->sds, j));
- if (sdd->sg)
- kfree(*per_cpu_ptr(sdd->sg, j));
- if (sdd->sgc)
- kfree(*per_cpu_ptr(sdd->sgc, j));
- }
- free_percpu(sdd->sd);
- sdd->sd = NULL;
- free_percpu(sdd->sds);
- sdd->sds = NULL;
- free_percpu(sdd->sg);
- sdd->sg = NULL;
- free_percpu(sdd->sgc);
- sdd->sgc = NULL;
- }
-}
-
-struct sched_domain *build_sched_domain(struct sched_domain_topology_level *tl,
- const struct cpumask *cpu_map, struct sched_domain_attr *attr,
- struct sched_domain *child, int cpu)
-{
- struct sched_domain *sd = sd_init(tl, cpu_map, child, cpu);
-
- if (child) {
- sd->level = child->level + 1;
- sched_domain_level_max = max(sched_domain_level_max, sd->level);
- child->parent = sd;
-
- if (!cpumask_subset(sched_domain_span(child),
- sched_domain_span(sd))) {
- pr_err("BUG: arch topology borken\n");
-#ifdef CONFIG_SCHED_DEBUG
- pr_err(" the %s domain not a subset of the %s domain\n",
- child->name, sd->name);
-#endif
- /* Fixup, ensure @sd has at least @child cpus. */
- cpumask_or(sched_domain_span(sd),
- sched_domain_span(sd),
- sched_domain_span(child));
- }
-
- }
- set_domain_attribute(sd, attr);
-
- return sd;
-}
-
-/*
- * Build sched domains for a given set of cpus and attach the sched domains
- * to the individual cpus
- */
-static int build_sched_domains(const struct cpumask *cpu_map,
- struct sched_domain_attr *attr)
-{
- enum s_alloc alloc_state;
- struct sched_domain *sd;
- struct s_data d;
- struct rq *rq = NULL;
- int i, ret = -ENOMEM;
-
- alloc_state = __visit_domain_allocation_hell(&d, cpu_map);
- if (alloc_state != sa_rootdomain)
- goto error;
-
- /* Set up domains for cpus specified by the cpu_map. */
- for_each_cpu(i, cpu_map) {
- struct sched_domain_topology_level *tl;
-
- sd = NULL;
- for_each_sd_topology(tl) {
- sd = build_sched_domain(tl, cpu_map, attr, sd, i);
- if (tl == sched_domain_topology)
- *per_cpu_ptr(d.sd, i) = sd;
- if (tl->flags & SDTL_OVERLAP || sched_feat(FORCE_SD_OVERLAP))
- sd->flags |= SD_OVERLAP;
- if (cpumask_equal(cpu_map, sched_domain_span(sd)))
- break;
- }
- }
-
- /* Build the groups for the domains */
- for_each_cpu(i, cpu_map) {
- for (sd = *per_cpu_ptr(d.sd, i); sd; sd = sd->parent) {
- sd->span_weight = cpumask_weight(sched_domain_span(sd));
- if (sd->flags & SD_OVERLAP) {
- if (build_overlap_sched_groups(sd, i))
- goto error;
- } else {
- if (build_sched_groups(sd, i))
- goto error;
- }
- }
- }
-
- /* Calculate CPU capacity for physical packages and nodes */
- for (i = nr_cpumask_bits-1; i >= 0; i--) {
- if (!cpumask_test_cpu(i, cpu_map))
- continue;
-
- for (sd = *per_cpu_ptr(d.sd, i); sd; sd = sd->parent) {
- claim_allocations(i, sd);
- init_sched_groups_capacity(i, sd);
- }
- }
-
- /* Attach the domains */
- rcu_read_lock();
- for_each_cpu(i, cpu_map) {
- rq = cpu_rq(i);
- sd = *per_cpu_ptr(d.sd, i);
-
- /* Use READ_ONCE()/WRITE_ONCE() to avoid load/store tearing: */
- if (rq->cpu_capacity_orig > READ_ONCE(d.rd->max_cpu_capacity))
- WRITE_ONCE(d.rd->max_cpu_capacity, rq->cpu_capacity_orig);
-
- cpu_attach_domain(sd, d.rd, i);
- }
- rcu_read_unlock();
-
- if (rq && sched_debug_enabled) {
- pr_info("span: %*pbl (max cpu_capacity = %lu)\n",
- cpumask_pr_args(cpu_map), rq->rd->max_cpu_capacity);
- }
-
- ret = 0;
-error:
- __free_domain_allocs(&d, alloc_state, cpu_map);
- return ret;
-}
-
-static cpumask_var_t *doms_cur; /* current sched domains */
-static int ndoms_cur; /* number of sched domains in 'doms_cur' */
-static struct sched_domain_attr *dattr_cur;
- /* attribues of custom domains in 'doms_cur' */
-
-/*
- * Special case: If a kmalloc of a doms_cur partition (array of
- * cpumask) fails, then fallback to a single sched domain,
- * as determined by the single cpumask fallback_doms.
- */
-static cpumask_var_t fallback_doms;
-
-/*
- * arch_update_cpu_topology lets virtualized architectures update the
- * cpu core maps. It is supposed to return 1 if the topology changed
- * or 0 if it stayed the same.
- */
-int __weak arch_update_cpu_topology(void)
-{
- return 0;
-}
-
-cpumask_var_t *alloc_sched_domains(unsigned int ndoms)
-{
- int i;
- cpumask_var_t *doms;
-
- doms = kmalloc(sizeof(*doms) * ndoms, GFP_KERNEL);
- if (!doms)
- return NULL;
- for (i = 0; i < ndoms; i++) {
- if (!alloc_cpumask_var(&doms[i], GFP_KERNEL)) {
- free_sched_domains(doms, i);
- return NULL;
- }
- }
- return doms;
-}
-
-void free_sched_domains(cpumask_var_t doms[], unsigned int ndoms)
-{
- unsigned int i;
- for (i = 0; i < ndoms; i++)
- free_cpumask_var(doms[i]);
- kfree(doms);
-}
-
-/*
- * Set up scheduler domains and groups. Callers must hold the hotplug lock.
- * For now this just excludes isolated cpus, but could be used to
- * exclude other special cases in the future.
- */
-static int init_sched_domains(const struct cpumask *cpu_map)
-{
- int err;
-
- arch_update_cpu_topology();
- ndoms_cur = 1;
- doms_cur = alloc_sched_domains(ndoms_cur);
- if (!doms_cur)
- doms_cur = &fallback_doms;
- cpumask_andnot(doms_cur[0], cpu_map, cpu_isolated_map);
- err = build_sched_domains(doms_cur[0], NULL);
- register_sched_domain_sysctl();
-
- return err;
-}
-
-/*
- * Detach sched domains from a group of cpus specified in cpu_map
- * These cpus will now be attached to the NULL domain
- */
-static void detach_destroy_domains(const struct cpumask *cpu_map)
-{
- int i;
-
- rcu_read_lock();
- for_each_cpu(i, cpu_map)
- cpu_attach_domain(NULL, &def_root_domain, i);
- rcu_read_unlock();
-}
-
-/* handle null as "default" */
-static int dattrs_equal(struct sched_domain_attr *cur, int idx_cur,
- struct sched_domain_attr *new, int idx_new)
-{
- struct sched_domain_attr tmp;
-
- /* fast path */
- if (!new && !cur)
- return 1;
-
- tmp = SD_ATTR_INIT;
- return !memcmp(cur ? (cur + idx_cur) : &tmp,
- new ? (new + idx_new) : &tmp,
- sizeof(struct sched_domain_attr));
-}
-
-/*
- * Partition sched domains as specified by the 'ndoms_new'
- * cpumasks in the array doms_new[] of cpumasks. This compares
- * doms_new[] to the current sched domain partitioning, doms_cur[].
- * It destroys each deleted domain and builds each new domain.
- *
- * 'doms_new' is an array of cpumask_var_t's of length 'ndoms_new'.
- * The masks don't intersect (don't overlap.) We should setup one
- * sched domain for each mask. CPUs not in any of the cpumasks will
- * not be load balanced. If the same cpumask appears both in the
- * current 'doms_cur' domains and in the new 'doms_new', we can leave
- * it as it is.
- *
- * The passed in 'doms_new' should be allocated using
- * alloc_sched_domains. This routine takes ownership of it and will
- * free_sched_domains it when done with it. If the caller failed the
- * alloc call, then it can pass in doms_new == NULL && ndoms_new == 1,
- * and partition_sched_domains() will fallback to the single partition
- * 'fallback_doms', it also forces the domains to be rebuilt.
- *
- * If doms_new == NULL it will be replaced with cpu_online_mask.
- * ndoms_new == 0 is a special case for destroying existing domains,
- * and it will not create the default domain.
- *
- * Call with hotplug lock held
- */
-void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[],
- struct sched_domain_attr *dattr_new)
-{
- int i, j, n;
- int new_topology;
-
- mutex_lock(&sched_domains_mutex);
-
- /* always unregister in case we don't destroy any domains */
- unregister_sched_domain_sysctl();
-
- /* Let architecture update cpu core mappings. */
- new_topology = arch_update_cpu_topology();
-
- n = doms_new ? ndoms_new : 0;
-
- /* Destroy deleted domains */
- for (i = 0; i < ndoms_cur; i++) {
- for (j = 0; j < n && !new_topology; j++) {
- if (cpumask_equal(doms_cur[i], doms_new[j])
- && dattrs_equal(dattr_cur, i, dattr_new, j))
- goto match1;
- }
- /* no match - a current sched domain not in new doms_new[] */
- detach_destroy_domains(doms_cur[i]);
-match1:
- ;
- }
-
- n = ndoms_cur;
- if (doms_new == NULL) {
- n = 0;
- doms_new = &fallback_doms;
- cpumask_andnot(doms_new[0], cpu_active_mask, cpu_isolated_map);
- WARN_ON_ONCE(dattr_new);
- }
-
- /* Build new domains */
- for (i = 0; i < ndoms_new; i++) {
- for (j = 0; j < n && !new_topology; j++) {
- if (cpumask_equal(doms_new[i], doms_cur[j])
- && dattrs_equal(dattr_new, i, dattr_cur, j))
- goto match2;
- }
- /* no match - add a new doms_new */
- build_sched_domains(doms_new[i], dattr_new ? dattr_new + i : NULL);
-match2:
- ;
- }
-
- /* Remember the new sched domains */
- if (doms_cur != &fallback_doms)
- free_sched_domains(doms_cur, ndoms_cur);
- kfree(dattr_cur); /* kfree(NULL) is safe */
- doms_cur = doms_new;
- dattr_cur = dattr_new;
- ndoms_cur = ndoms_new;
-
- register_sched_domain_sysctl();
-
- mutex_unlock(&sched_domains_mutex);
-}
-
-static int num_cpus_frozen; /* used to mark begin/end of suspend/resume */
+static int num_cpus_frozen;
/*
* Update cpusets according to cpu_active mask. If cpusets are
@@ -7352,7 +5759,7 @@
* Put the rq online, if not already. This happens:
*
* 1) In the early boot process, because we build the real domains
- * after all cpus have been brought up.
+ * after all CPUs have been brought up.
*
* 2) At runtime, if cpuset_cpu_active() fails to rebuild the
* domains.
@@ -7467,7 +5874,7 @@
/*
* There's no userspace yet to cause hotplug operations; hence all the
- * cpu masks are stable and all blatant races in the below code cannot
+ * CPU masks are stable and all blatant races in the below code cannot
* happen.
*/
mutex_lock(&sched_domains_mutex);
@@ -7487,6 +5894,7 @@
init_sched_dl_class();
sched_init_smt();
+ sched_clock_init_late();
sched_smp_initialized = true;
}
@@ -7502,6 +5910,7 @@
void __init sched_init_smp(void)
{
sched_init_granularity();
+ sched_clock_init_late();
}
#endif /* CONFIG_SMP */
@@ -7545,6 +5954,8 @@
int i, j;
unsigned long alloc_size = 0, ptr;
+ sched_clock_init();
+
for (i = 0; i < WAIT_TABLE_SIZE; i++)
init_waitqueue_head(bit_wait_table + i);
@@ -7583,10 +5994,8 @@
}
#endif /* CONFIG_CPUMASK_OFFSTACK */
- init_rt_bandwidth(&def_rt_bandwidth,
- global_rt_period(), global_rt_runtime());
- init_dl_bandwidth(&def_dl_bandwidth,
- global_rt_period(), global_rt_runtime());
+ init_rt_bandwidth(&def_rt_bandwidth, global_rt_period(), global_rt_runtime());
+ init_dl_bandwidth(&def_dl_bandwidth, global_rt_period(), global_rt_runtime());
#ifdef CONFIG_SMP
init_defrootdomain();
@@ -7622,18 +6031,18 @@
INIT_LIST_HEAD(&rq->leaf_cfs_rq_list);
rq->tmp_alone_branch = &rq->leaf_cfs_rq_list;
/*
- * How much cpu bandwidth does root_task_group get?
+ * How much CPU bandwidth does root_task_group get?
*
* In case of task-groups formed thr' the cgroup filesystem, it
- * gets 100% of the cpu resources in the system. This overall
- * system cpu resource is divided among the tasks of
+ * gets 100% of the CPU resources in the system. This overall
+ * system CPU resource is divided among the tasks of
* root_task_group and its child task-groups in a fair manner,
* based on each entity's (task or task-group's) weight
* (se->load.weight).
*
* In other words, if root_task_group has 10 tasks of weight
* 1024) and two child groups A0 and A1 (of weight 1024 each),
- * then A0's share of the cpu resource is:
+ * then A0's share of the CPU resource is:
*
* A0's bandwidth = 1024 / (10*1024 + 1024 + 1024) = 8.33%
*
@@ -7742,10 +6151,14 @@
void ___might_sleep(const char *file, int line, int preempt_offset)
{
- static unsigned long prev_jiffy; /* ratelimiting */
+ /* Ratelimiting timestamp: */
+ static unsigned long prev_jiffy;
+
unsigned long preempt_disable_ip;
- rcu_sleep_check(); /* WARN_ON_ONCE() by default, no rate limit reqd. */
+ /* WARN_ON_ONCE() by default, no rate limit required: */
+ rcu_sleep_check();
+
if ((preempt_count_equals(preempt_offset) && !irqs_disabled() &&
!is_idle_task(current)) ||
system_state != SYSTEM_RUNNING || oops_in_progress)
@@ -7754,7 +6167,7 @@
return;
prev_jiffy = jiffies;
- /* Save this before calling printk(), since that will clobber it */
+ /* Save this before calling printk(), since that will clobber it: */
preempt_disable_ip = get_preempt_disable_ip(current);
printk(KERN_ERR
@@ -7833,7 +6246,7 @@
*/
/**
- * curr_task - return the current task for a given cpu.
+ * curr_task - return the current task for a given CPU.
* @cpu: the processor in question.
*
* ONLY VALID WHEN THE WHOLE SYSTEM IS STOPPED!
@@ -7849,13 +6262,13 @@
#ifdef CONFIG_IA64
/**
- * set_curr_task - set the current task for a given cpu.
+ * set_curr_task - set the current task for a given CPU.
* @cpu: the processor in question.
* @p: the task pointer to set.
*
* Description: This function must only be used when non-maskable interrupts
* are serviced on a separate stack. It allows the architecture to switch the
- * notion of the current task on a cpu in a non-blocking manner. This function
+ * notion of the current task on a CPU in a non-blocking manner. This function
* must be called with all CPU's synchronized, and interrupts disabled, the
* and caller must save the original value of the current task (see
* curr_task() above) and restore that value before reenabling interrupts and
@@ -7911,7 +6324,8 @@
spin_lock_irqsave(&task_group_lock, flags);
list_add_rcu(&tg->list, &task_groups);
- WARN_ON(!parent); /* root should already exist */
+ /* Root should already exist: */
+ WARN_ON(!parent);
tg->parent = parent;
INIT_LIST_HEAD(&tg->children);
@@ -7924,13 +6338,13 @@
/* rcu callback to free various structures associated with a task group */
static void sched_free_group_rcu(struct rcu_head *rhp)
{
- /* now it should be safe to free those cfs_rqs */
+ /* Now it should be safe to free those cfs_rqs: */
sched_free_group(container_of(rhp, struct task_group, rcu));
}
void sched_destroy_group(struct task_group *tg)
{
- /* wait for possible concurrent references to cfs_rqs complete */
+ /* Wait for possible concurrent references to cfs_rqs complete: */
call_rcu(&tg->rcu, sched_free_group_rcu);
}
@@ -7938,7 +6352,7 @@
{
unsigned long flags;
- /* end participation in shares distribution */
+ /* End participation in shares distribution: */
unregister_fair_sched_group(tg);
spin_lock_irqsave(&task_group_lock, flags);
@@ -7983,20 +6397,21 @@
struct rq *rq;
rq = task_rq_lock(tsk, &rf);
+ update_rq_clock(rq);
running = task_current(rq, tsk);
queued = task_on_rq_queued(tsk);
if (queued)
dequeue_task(rq, tsk, DEQUEUE_SAVE | DEQUEUE_MOVE);
- if (unlikely(running))
+ if (running)
put_prev_task(rq, tsk);
sched_change_group(tsk, TASK_MOVE_GROUP);
if (queued)
enqueue_task(rq, tsk, ENQUEUE_RESTORE | ENQUEUE_MOVE);
- if (unlikely(running))
+ if (running)
set_curr_task(rq, tsk);
task_rq_unlock(rq, tsk, &rf);
@@ -8366,11 +6781,14 @@
mutex_lock(&mutex);
ret = proc_dointvec(table, write, buffer, lenp, ppos);
- /* make sure that internally we keep jiffies */
- /* also, writing zero resets timeslice to default */
+ /*
+ * Make sure that internally we keep jiffies.
+ * Also, writing zero resets the timeslice to default:
+ */
if (!ret && write) {
- sched_rr_timeslice = sched_rr_timeslice <= 0 ?
- RR_TIMESLICE : msecs_to_jiffies(sched_rr_timeslice);
+ sched_rr_timeslice =
+ sysctl_sched_rr_timeslice <= 0 ? RR_TIMESLICE :
+ msecs_to_jiffies(sysctl_sched_rr_timeslice);
}
mutex_unlock(&mutex);
return ret;
@@ -8431,6 +6849,7 @@
rq = task_rq_lock(task, &rf);
+ update_rq_clock(rq);
sched_change_group(task, TASK_SET_GROUP);
task_rq_unlock(rq, task, &rf);
@@ -8550,9 +6969,11 @@
cfs_b->quota = quota;
__refill_cfs_bandwidth_runtime(cfs_b);
- /* restart the period timer (if active) to handle new period expiry */
+
+ /* Restart the period timer (if active) to handle new period expiry: */
if (runtime_enabled)
start_cfs_bandwidth(cfs_b);
+
raw_spin_unlock_irq(&cfs_b->lock);
for_each_online_cpu(i) {
@@ -8690,8 +7111,8 @@
parent_quota = parent_b->hierarchical_quota;
/*
- * ensure max(child_quota) <= parent_quota, inherit when no
- * limit is set
+ * Ensure max(child_quota) <= parent_quota, inherit when no
+ * limit is set:
*/
if (quota == RUNTIME_INF)
quota = parent_quota;
@@ -8800,7 +7221,7 @@
.write_u64 = cpu_rt_period_write_uint,
},
#endif
- { } /* terminate */
+ { } /* Terminate */
};
struct cgroup_subsys cpu_cgrp_subsys = {
diff --git a/kernel/sched/cpuacct.c b/kernel/sched/cpuacct.c
index 9add206..f95ab29 100644
--- a/kernel/sched/cpuacct.c
+++ b/kernel/sched/cpuacct.c
@@ -297,7 +297,7 @@
for (stat = 0; stat < CPUACCT_STAT_NSTATS; stat++) {
seq_printf(sf, "%s %lld\n",
cpuacct_stat_desc[stat],
- (long long)cputime64_to_clock_t(val[stat]));
+ (long long)nsec_to_clock_t(val[stat]));
}
return 0;
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index 7700a9c..2ecec3a 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -4,6 +4,7 @@
#include <linux/kernel_stat.h>
#include <linux/static_key.h>
#include <linux/context_tracking.h>
+#include <linux/cputime.h>
#include "sched.h"
#ifdef CONFIG_PARAVIRT
#include <asm/paravirt.h>
@@ -44,6 +45,7 @@
void irqtime_account_irq(struct task_struct *curr)
{
struct irqtime *irqtime = this_cpu_ptr(&cpu_irqtime);
+ u64 *cpustat = kcpustat_this_cpu->cpustat;
s64 delta;
int cpu;
@@ -61,49 +63,34 @@
* in that case, so as not to confuse scheduler with a special task
* that do not consume any time, but still wants to run.
*/
- if (hardirq_count())
- irqtime->hardirq_time += delta;
- else if (in_serving_softirq() && curr != this_cpu_ksoftirqd())
- irqtime->softirq_time += delta;
+ if (hardirq_count()) {
+ cpustat[CPUTIME_IRQ] += delta;
+ irqtime->tick_delta += delta;
+ } else if (in_serving_softirq() && curr != this_cpu_ksoftirqd()) {
+ cpustat[CPUTIME_SOFTIRQ] += delta;
+ irqtime->tick_delta += delta;
+ }
u64_stats_update_end(&irqtime->sync);
}
EXPORT_SYMBOL_GPL(irqtime_account_irq);
-static cputime_t irqtime_account_update(u64 irqtime, int idx, cputime_t maxtime)
+static u64 irqtime_tick_accounted(u64 maxtime)
{
- u64 *cpustat = kcpustat_this_cpu->cpustat;
- cputime_t irq_cputime;
+ struct irqtime *irqtime = this_cpu_ptr(&cpu_irqtime);
+ u64 delta;
- irq_cputime = nsecs_to_cputime64(irqtime) - cpustat[idx];
- irq_cputime = min(irq_cputime, maxtime);
- cpustat[idx] += irq_cputime;
+ delta = min(irqtime->tick_delta, maxtime);
+ irqtime->tick_delta -= delta;
- return irq_cputime;
-}
-
-static cputime_t irqtime_account_hi_update(cputime_t maxtime)
-{
- return irqtime_account_update(__this_cpu_read(cpu_irqtime.hardirq_time),
- CPUTIME_IRQ, maxtime);
-}
-
-static cputime_t irqtime_account_si_update(cputime_t maxtime)
-{
- return irqtime_account_update(__this_cpu_read(cpu_irqtime.softirq_time),
- CPUTIME_SOFTIRQ, maxtime);
+ return delta;
}
#else /* CONFIG_IRQ_TIME_ACCOUNTING */
#define sched_clock_irqtime (0)
-static cputime_t irqtime_account_hi_update(cputime_t dummy)
-{
- return 0;
-}
-
-static cputime_t irqtime_account_si_update(cputime_t dummy)
+static u64 irqtime_tick_accounted(u64 dummy)
{
return 0;
}
@@ -129,7 +116,7 @@
* @p: the process that the cpu time gets accounted to
* @cputime: the cpu time spent in user space since the last update
*/
-void account_user_time(struct task_struct *p, cputime_t cputime)
+void account_user_time(struct task_struct *p, u64 cputime)
{
int index;
@@ -140,7 +127,7 @@
index = (task_nice(p) > 0) ? CPUTIME_NICE : CPUTIME_USER;
/* Add user time to cpustat. */
- task_group_account_field(p, index, (__force u64) cputime);
+ task_group_account_field(p, index, cputime);
/* Account for user time used */
acct_account_cputime(p);
@@ -151,7 +138,7 @@
* @p: the process that the cpu time gets accounted to
* @cputime: the cpu time spent in virtual machine since the last update
*/
-static void account_guest_time(struct task_struct *p, cputime_t cputime)
+void account_guest_time(struct task_struct *p, u64 cputime)
{
u64 *cpustat = kcpustat_this_cpu->cpustat;
@@ -162,11 +149,11 @@
/* Add guest time to cpustat. */
if (task_nice(p) > 0) {
- cpustat[CPUTIME_NICE] += (__force u64) cputime;
- cpustat[CPUTIME_GUEST_NICE] += (__force u64) cputime;
+ cpustat[CPUTIME_NICE] += cputime;
+ cpustat[CPUTIME_GUEST_NICE] += cputime;
} else {
- cpustat[CPUTIME_USER] += (__force u64) cputime;
- cpustat[CPUTIME_GUEST] += (__force u64) cputime;
+ cpustat[CPUTIME_USER] += cputime;
+ cpustat[CPUTIME_GUEST] += cputime;
}
}
@@ -176,15 +163,15 @@
* @cputime: the cpu time spent in kernel space since the last update
* @index: pointer to cpustat field that has to be updated
*/
-static inline
-void __account_system_time(struct task_struct *p, cputime_t cputime, int index)
+void account_system_index_time(struct task_struct *p,
+ u64 cputime, enum cpu_usage_stat index)
{
/* Add system time to process. */
p->stime += cputime;
account_group_system_time(p, cputime);
/* Add system time to cpustat. */
- task_group_account_field(p, index, (__force u64) cputime);
+ task_group_account_field(p, index, cputime);
/* Account for system time used */
acct_account_cputime(p);
@@ -196,8 +183,7 @@
* @hardirq_offset: the offset to subtract from hardirq_count()
* @cputime: the cpu time spent in kernel space since the last update
*/
-void account_system_time(struct task_struct *p, int hardirq_offset,
- cputime_t cputime)
+void account_system_time(struct task_struct *p, int hardirq_offset, u64 cputime)
{
int index;
@@ -213,33 +199,33 @@
else
index = CPUTIME_SYSTEM;
- __account_system_time(p, cputime, index);
+ account_system_index_time(p, cputime, index);
}
/*
* Account for involuntary wait time.
* @cputime: the cpu time spent in involuntary wait
*/
-void account_steal_time(cputime_t cputime)
+void account_steal_time(u64 cputime)
{
u64 *cpustat = kcpustat_this_cpu->cpustat;
- cpustat[CPUTIME_STEAL] += (__force u64) cputime;
+ cpustat[CPUTIME_STEAL] += cputime;
}
/*
* Account for idle time.
* @cputime: the cpu time spent in idle wait
*/
-void account_idle_time(cputime_t cputime)
+void account_idle_time(u64 cputime)
{
u64 *cpustat = kcpustat_this_cpu->cpustat;
struct rq *rq = this_rq();
if (atomic_read(&rq->nr_iowait) > 0)
- cpustat[CPUTIME_IOWAIT] += (__force u64) cputime;
+ cpustat[CPUTIME_IOWAIT] += cputime;
else
- cpustat[CPUTIME_IDLE] += (__force u64) cputime;
+ cpustat[CPUTIME_IDLE] += cputime;
}
/*
@@ -247,21 +233,19 @@
* ticks are not redelivered later. Due to that, this function may on
* occasion account more time than the calling functions think elapsed.
*/
-static __always_inline cputime_t steal_account_process_time(cputime_t maxtime)
+static __always_inline u64 steal_account_process_time(u64 maxtime)
{
#ifdef CONFIG_PARAVIRT
if (static_key_false(¶virt_steal_enabled)) {
- cputime_t steal_cputime;
u64 steal;
steal = paravirt_steal_clock(smp_processor_id());
steal -= this_rq()->prev_steal_time;
+ steal = min(steal, maxtime);
+ account_steal_time(steal);
+ this_rq()->prev_steal_time += steal;
- steal_cputime = min(nsecs_to_cputime(steal), maxtime);
- account_steal_time(steal_cputime);
- this_rq()->prev_steal_time += cputime_to_nsecs(steal_cputime);
-
- return steal_cputime;
+ return steal;
}
#endif
return 0;
@@ -270,9 +254,9 @@
/*
* Account how much elapsed time was spent in steal, irq, or softirq time.
*/
-static inline cputime_t account_other_time(cputime_t max)
+static inline u64 account_other_time(u64 max)
{
- cputime_t accounted;
+ u64 accounted;
/* Shall be converted to a lockdep-enabled lightweight check */
WARN_ON_ONCE(!irqs_disabled());
@@ -280,10 +264,7 @@
accounted = steal_account_process_time(max);
if (accounted < max)
- accounted += irqtime_account_hi_update(max - accounted);
-
- if (accounted < max)
- accounted += irqtime_account_si_update(max - accounted);
+ accounted += irqtime_tick_accounted(max - accounted);
return accounted;
}
@@ -315,7 +296,7 @@
void thread_group_cputime(struct task_struct *tsk, struct task_cputime *times)
{
struct signal_struct *sig = tsk->signal;
- cputime_t utime, stime;
+ u64 utime, stime;
struct task_struct *t;
unsigned int seq, nextseq;
unsigned long flags;
@@ -379,8 +360,7 @@
static void irqtime_account_process_tick(struct task_struct *p, int user_tick,
struct rq *rq, int ticks)
{
- u64 cputime = (__force u64) cputime_one_jiffy * ticks;
- cputime_t other;
+ u64 other, cputime = TICK_NSEC * ticks;
/*
* When returning from idle, many ticks can get accounted at
@@ -392,6 +372,7 @@
other = account_other_time(ULONG_MAX);
if (other >= cputime)
return;
+
cputime -= other;
if (this_cpu_ksoftirqd() == p) {
@@ -400,7 +381,7 @@
* So, we have to handle it separately here.
* Also, p->stime needs to be updated for ksoftirqd.
*/
- __account_system_time(p, cputime, CPUTIME_SOFTIRQ);
+ account_system_index_time(p, cputime, CPUTIME_SOFTIRQ);
} else if (user_tick) {
account_user_time(p, cputime);
} else if (p == rq->idle) {
@@ -408,7 +389,7 @@
} else if (p->flags & PF_VCPU) { /* System time or guest time */
account_guest_time(p, cputime);
} else {
- __account_system_time(p, cputime, CPUTIME_SYSTEM);
+ account_system_index_time(p, cputime, CPUTIME_SYSTEM);
}
}
@@ -437,9 +418,7 @@
else
vtime_account_system(prev);
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
- vtime_account_user(prev);
-#endif
+ vtime_flush(prev);
arch_vtime_task_switch(prev);
}
#endif
@@ -467,14 +446,14 @@
EXPORT_SYMBOL_GPL(vtime_account_irq_enter);
#endif /* __ARCH_HAS_VTIME_ACCOUNT */
-void task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st)
+void task_cputime_adjusted(struct task_struct *p, u64 *ut, u64 *st)
{
*ut = p->utime;
*st = p->stime;
}
EXPORT_SYMBOL_GPL(task_cputime_adjusted);
-void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st)
+void thread_group_cputime_adjusted(struct task_struct *p, u64 *ut, u64 *st)
{
struct task_cputime cputime;
@@ -491,7 +470,7 @@
*/
void account_process_tick(struct task_struct *p, int user_tick)
{
- cputime_t cputime, steal;
+ u64 cputime, steal;
struct rq *rq = this_rq();
if (vtime_accounting_cpu_enabled())
@@ -502,7 +481,7 @@
return;
}
- cputime = cputime_one_jiffy;
+ cputime = TICK_NSEC;
steal = steal_account_process_time(ULONG_MAX);
if (steal >= cputime)
@@ -524,14 +503,14 @@
*/
void account_idle_ticks(unsigned long ticks)
{
- cputime_t cputime, steal;
+ u64 cputime, steal;
if (sched_clock_irqtime) {
irqtime_account_idle_ticks(ticks);
return;
}
- cputime = jiffies_to_cputime(ticks);
+ cputime = ticks * TICK_NSEC;
steal = steal_account_process_time(ULONG_MAX);
if (steal >= cputime)
@@ -545,7 +524,7 @@
* Perform (stime * rtime) / total, but avoid multiplication overflow by
* loosing precision when the numbers are big.
*/
-static cputime_t scale_stime(u64 stime, u64 rtime, u64 total)
+static u64 scale_stime(u64 stime, u64 rtime, u64 total)
{
u64 scaled;
@@ -582,7 +561,7 @@
* followed by a 64/32->64 divide.
*/
scaled = div_u64((u64) (u32) stime * (u64) (u32) rtime, (u32)total);
- return (__force cputime_t) scaled;
+ return scaled;
}
/*
@@ -607,14 +586,14 @@
*/
static void cputime_adjust(struct task_cputime *curr,
struct prev_cputime *prev,
- cputime_t *ut, cputime_t *st)
+ u64 *ut, u64 *st)
{
- cputime_t rtime, stime, utime;
+ u64 rtime, stime, utime;
unsigned long flags;
/* Serialize concurrent callers such that we can honour our guarantees */
raw_spin_lock_irqsave(&prev->lock, flags);
- rtime = nsecs_to_cputime(curr->sum_exec_runtime);
+ rtime = curr->sum_exec_runtime;
/*
* This is possible under two circumstances:
@@ -645,8 +624,7 @@
goto update;
}
- stime = scale_stime((__force u64)stime, (__force u64)rtime,
- (__force u64)(stime + utime));
+ stime = scale_stime(stime, rtime, stime + utime);
update:
/*
@@ -679,7 +657,7 @@
raw_spin_unlock_irqrestore(&prev->lock, flags);
}
-void task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st)
+void task_cputime_adjusted(struct task_struct *p, u64 *ut, u64 *st)
{
struct task_cputime cputime = {
.sum_exec_runtime = p->se.sum_exec_runtime,
@@ -690,7 +668,7 @@
}
EXPORT_SYMBOL_GPL(task_cputime_adjusted);
-void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st)
+void thread_group_cputime_adjusted(struct task_struct *p, u64 *ut, u64 *st)
{
struct task_cputime cputime;
@@ -700,20 +678,20 @@
#endif /* !CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
-static cputime_t vtime_delta(struct task_struct *tsk)
+static u64 vtime_delta(struct task_struct *tsk)
{
unsigned long now = READ_ONCE(jiffies);
if (time_before(now, (unsigned long)tsk->vtime_snap))
return 0;
- return jiffies_to_cputime(now - tsk->vtime_snap);
+ return jiffies_to_nsecs(now - tsk->vtime_snap);
}
-static cputime_t get_vtime_delta(struct task_struct *tsk)
+static u64 get_vtime_delta(struct task_struct *tsk)
{
unsigned long now = READ_ONCE(jiffies);
- cputime_t delta, other;
+ u64 delta, other;
/*
* Unlike tick based timing, vtime based timing never has lost
@@ -722,7 +700,7 @@
* elapsed time. Limit account_other_time to prevent rounding
* errors from causing elapsed vtime to go negative.
*/
- delta = jiffies_to_cputime(now - tsk->vtime_snap);
+ delta = jiffies_to_nsecs(now - tsk->vtime_snap);
other = account_other_time(delta);
WARN_ON_ONCE(tsk->vtime_snap_whence == VTIME_INACTIVE);
tsk->vtime_snap = now;
@@ -732,9 +710,7 @@
static void __vtime_account_system(struct task_struct *tsk)
{
- cputime_t delta_cpu = get_vtime_delta(tsk);
-
- account_system_time(tsk, irq_count(), delta_cpu);
+ account_system_time(tsk, irq_count(), get_vtime_delta(tsk));
}
void vtime_account_system(struct task_struct *tsk)
@@ -749,14 +725,10 @@
void vtime_account_user(struct task_struct *tsk)
{
- cputime_t delta_cpu;
-
write_seqcount_begin(&tsk->vtime_seqcount);
tsk->vtime_snap_whence = VTIME_SYS;
- if (vtime_delta(tsk)) {
- delta_cpu = get_vtime_delta(tsk);
- account_user_time(tsk, delta_cpu);
- }
+ if (vtime_delta(tsk))
+ account_user_time(tsk, get_vtime_delta(tsk));
write_seqcount_end(&tsk->vtime_seqcount);
}
@@ -797,9 +769,7 @@
void vtime_account_idle(struct task_struct *tsk)
{
- cputime_t delta_cpu = get_vtime_delta(tsk);
-
- account_idle_time(delta_cpu);
+ account_idle_time(get_vtime_delta(tsk));
}
void arch_vtime_task_switch(struct task_struct *prev)
@@ -826,10 +796,10 @@
local_irq_restore(flags);
}
-cputime_t task_gtime(struct task_struct *t)
+u64 task_gtime(struct task_struct *t)
{
unsigned int seq;
- cputime_t gtime;
+ u64 gtime;
if (!vtime_accounting_enabled())
return t->gtime;
@@ -851,9 +821,9 @@
* add up the pending nohz execution time since the last
* cputime snapshot.
*/
-void task_cputime(struct task_struct *t, cputime_t *utime, cputime_t *stime)
+void task_cputime(struct task_struct *t, u64 *utime, u64 *stime)
{
- cputime_t delta;
+ u64 delta;
unsigned int seq;
if (!vtime_accounting_enabled()) {
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index 70ef2b1..27737f3 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -663,9 +663,9 @@
* Nothing relies on rq->lock after this, so its safe to drop
* rq->lock.
*/
- lockdep_unpin_lock(&rq->lock, rf.cookie);
+ rq_unpin_lock(rq, &rf);
push_dl_task(rq);
- lockdep_repin_lock(&rq->lock, rf.cookie);
+ rq_repin_lock(rq, &rf);
}
#endif
@@ -1118,7 +1118,7 @@
}
struct task_struct *
-pick_next_task_dl(struct rq *rq, struct task_struct *prev, struct pin_cookie cookie)
+pick_next_task_dl(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
{
struct sched_dl_entity *dl_se;
struct task_struct *p;
@@ -1133,9 +1133,9 @@
* disabled avoiding further scheduler activity on it and we're
* being very careful to re-start the picking loop.
*/
- lockdep_unpin_lock(&rq->lock, cookie);
+ rq_unpin_lock(rq, rf);
pull_dl_task(rq);
- lockdep_repin_lock(&rq->lock, cookie);
+ rq_repin_lock(rq, rf);
/*
* pull_dl_task() can drop (and re-acquire) rq->lock; this
* means a stop task can slip in, in which case we need to
@@ -1729,12 +1729,11 @@
#ifdef CONFIG_SMP
if (tsk_nr_cpus_allowed(p) > 1 && rq->dl.overloaded)
queue_push_tasks(rq);
-#else
+#endif
if (dl_task(rq->curr))
check_preempt_curr_dl(rq, p, 0);
else
resched_curr(rq);
-#endif
}
}
diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c
index fa178b6..109adc0 100644
--- a/kernel/sched/debug.c
+++ b/kernel/sched/debug.c
@@ -953,6 +953,10 @@
#endif
P(policy);
P(prio);
+ if (p->policy == SCHED_DEADLINE) {
+ P(dl.runtime);
+ P(dl.deadline);
+ }
#undef PN_SCHEDSTAT
#undef PN
#undef __PN
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 6559d19..274c747 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -2657,6 +2657,18 @@
if (tg_weight)
shares /= tg_weight;
+ /*
+ * MIN_SHARES has to be unscaled here to support per-CPU partitioning
+ * of a group with small tg->shares value. It is a floor value which is
+ * assigned as a minimum load.weight to the sched_entity representing
+ * the group on a CPU.
+ *
+ * E.g. on 64-bit for a group with tg->shares of scale_load(15)=15*1024
+ * on an 8-core system with 8 tasks each runnable on one CPU shares has
+ * to be 15*1024*1/8=1920 instead of scale_load(MIN_SHARES)=2*1024. In
+ * case no task is runnable on a CPU MIN_SHARES=2 should be returned
+ * instead of 0.
+ */
if (shares < MIN_SHARES)
shares = MIN_SHARES;
if (shares > tg->shares)
@@ -2689,16 +2701,20 @@
static inline int throttled_hierarchy(struct cfs_rq *cfs_rq);
-static void update_cfs_shares(struct cfs_rq *cfs_rq)
+static void update_cfs_shares(struct sched_entity *se)
{
+ struct cfs_rq *cfs_rq = group_cfs_rq(se);
struct task_group *tg;
- struct sched_entity *se;
long shares;
- tg = cfs_rq->tg;
- se = tg->se[cpu_of(rq_of(cfs_rq))];
- if (!se || throttled_hierarchy(cfs_rq))
+ if (!cfs_rq)
return;
+
+ if (throttled_hierarchy(cfs_rq))
+ return;
+
+ tg = cfs_rq->tg;
+
#ifndef CONFIG_SMP
if (likely(se->load.weight == tg->shares))
return;
@@ -2707,8 +2723,9 @@
reweight_entity(cfs_rq_of(se), se, shares);
}
+
#else /* CONFIG_FAIR_GROUP_SCHED */
-static inline void update_cfs_shares(struct cfs_rq *cfs_rq)
+static inline void update_cfs_shares(struct sched_entity *se)
{
}
#endif /* CONFIG_FAIR_GROUP_SCHED */
@@ -3424,7 +3441,7 @@
return cfs_rq->avg.load_avg;
}
-static int idle_balance(struct rq *this_rq);
+static int idle_balance(struct rq *this_rq, struct rq_flags *rf);
#else /* CONFIG_SMP */
@@ -3453,7 +3470,7 @@
static inline void
detach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) {}
-static inline int idle_balance(struct rq *rq)
+static inline int idle_balance(struct rq *rq, struct rq_flags *rf)
{
return 0;
}
@@ -3582,10 +3599,18 @@
if (renorm && !curr)
se->vruntime += cfs_rq->min_vruntime;
+ /*
+ * When enqueuing a sched_entity, we must:
+ * - Update loads to have both entity and cfs_rq synced with now.
+ * - Add its load to cfs_rq->runnable_avg
+ * - For group_entity, update its weight to reflect the new share of
+ * its group cfs_rq
+ * - Add its new weight to cfs_rq->load.weight
+ */
update_load_avg(se, UPDATE_TG);
enqueue_entity_load_avg(cfs_rq, se);
+ update_cfs_shares(se);
account_entity_enqueue(cfs_rq, se);
- update_cfs_shares(cfs_rq);
if (flags & ENQUEUE_WAKEUP)
place_entity(cfs_rq, se, 0);
@@ -3657,6 +3682,15 @@
* Update run-time statistics of the 'current'.
*/
update_curr(cfs_rq);
+
+ /*
+ * When dequeuing a sched_entity, we must:
+ * - Update loads to have both entity and cfs_rq synced with now.
+ * - Substract its load from the cfs_rq->runnable_avg.
+ * - Substract its previous weight from cfs_rq->load.weight.
+ * - For group entity, update its weight to reflect the new share
+ * of its group cfs_rq.
+ */
update_load_avg(se, UPDATE_TG);
dequeue_entity_load_avg(cfs_rq, se);
@@ -3681,7 +3715,7 @@
/* return excess runtime on last dequeue */
return_cfs_rq_runtime(cfs_rq);
- update_cfs_shares(cfs_rq);
+ update_cfs_shares(se);
/*
* Now advance min_vruntime if @se was the entity holding it back,
@@ -3864,7 +3898,7 @@
* Ensure that runnable average is periodically updated.
*/
update_load_avg(curr, UPDATE_TG);
- update_cfs_shares(cfs_rq);
+ update_cfs_shares(curr);
#ifdef CONFIG_SCHED_HRTICK
/*
@@ -4761,7 +4795,7 @@
break;
update_load_avg(se, UPDATE_TG);
- update_cfs_shares(cfs_rq);
+ update_cfs_shares(se);
}
if (!se)
@@ -4820,7 +4854,7 @@
break;
update_load_avg(se, UPDATE_TG);
- update_cfs_shares(cfs_rq);
+ update_cfs_shares(se);
}
if (!se)
@@ -6213,7 +6247,7 @@
}
static struct task_struct *
-pick_next_task_fair(struct rq *rq, struct task_struct *prev, struct pin_cookie cookie)
+pick_next_task_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
{
struct cfs_rq *cfs_rq = &rq->cfs;
struct sched_entity *se;
@@ -6320,15 +6354,8 @@
return p;
idle:
- /*
- * This is OK, because current is on_cpu, which avoids it being picked
- * for load-balance and preemption/IRQs are still disabled avoiding
- * further scheduler activity on it and we're being very careful to
- * re-start the picking loop.
- */
- lockdep_unpin_lock(&rq->lock, cookie);
- new_tasks = idle_balance(rq);
- lockdep_repin_lock(&rq->lock, cookie);
+ new_tasks = idle_balance(rq, rf);
+
/*
* Because idle_balance() releases (and re-acquires) rq->lock, it is
* possible for any higher priority task to appear. In that case we
@@ -8077,6 +8104,7 @@
more_balance:
raw_spin_lock_irqsave(&busiest->lock, flags);
+ update_rq_clock(busiest);
/*
* cur_ld_moved - load moved in current iteration
@@ -8297,7 +8325,7 @@
* idle_balance is called by schedule() if this_cpu is about to become
* idle. Attempts to pull tasks from other CPUs.
*/
-static int idle_balance(struct rq *this_rq)
+static int idle_balance(struct rq *this_rq, struct rq_flags *rf)
{
unsigned long next_balance = jiffies + HZ;
int this_cpu = this_rq->cpu;
@@ -8311,6 +8339,14 @@
*/
this_rq->idle_stamp = rq_clock(this_rq);
+ /*
+ * This is OK, because current is on_cpu, which avoids it being picked
+ * for load-balance and preemption/IRQs are still disabled avoiding
+ * further scheduler activity on it and we're being very careful to
+ * re-start the picking loop.
+ */
+ rq_unpin_lock(this_rq, rf);
+
if (this_rq->avg_idle < sysctl_sched_migration_cost ||
!this_rq->rd->overload) {
rcu_read_lock();
@@ -8388,6 +8424,8 @@
if (pulled_task)
this_rq->idle_stamp = 0;
+ rq_repin_lock(this_rq, rf);
+
return pulled_task;
}
@@ -8443,6 +8481,7 @@
};
schedstat_inc(sd->alb_count);
+ update_rq_clock(busiest_rq);
p = detach_one_task(&env);
if (p) {
@@ -9264,6 +9303,7 @@
se = tg->se[i];
raw_spin_lock_irq(&rq->lock);
+ update_rq_clock(rq);
attach_entity_cfs_rq(se);
sync_throttle(tg, i);
raw_spin_unlock_irq(&rq->lock);
@@ -9356,8 +9396,10 @@
/* Possible calls to update_curr() need rq clock */
update_rq_clock(rq);
- for_each_sched_entity(se)
- update_cfs_shares(group_cfs_rq(se));
+ for_each_sched_entity(se) {
+ update_load_avg(se, UPDATE_TG);
+ update_cfs_shares(se);
+ }
raw_spin_unlock_irqrestore(&rq->lock, flags);
}
diff --git a/kernel/sched/idle_task.c b/kernel/sched/idle_task.c
index 5405d3f..0c00172 100644
--- a/kernel/sched/idle_task.c
+++ b/kernel/sched/idle_task.c
@@ -24,7 +24,7 @@
}
static struct task_struct *
-pick_next_task_idle(struct rq *rq, struct task_struct *prev, struct pin_cookie cookie)
+pick_next_task_idle(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
{
put_prev_task(rq, prev);
update_idle_core(rq);
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 2516b8d..e8836cf 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -9,6 +9,7 @@
#include <linux/irq_work.h>
int sched_rr_timeslice = RR_TIMESLICE;
+int sysctl_sched_rr_timeslice = (MSEC_PER_SEC / HZ) * RR_TIMESLICE;
static int do_sched_rt_period_timer(struct rt_bandwidth *rt_b, int overrun);
@@ -1523,7 +1524,7 @@
}
static struct task_struct *
-pick_next_task_rt(struct rq *rq, struct task_struct *prev, struct pin_cookie cookie)
+pick_next_task_rt(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
{
struct task_struct *p;
struct rt_rq *rt_rq = &rq->rt;
@@ -1535,9 +1536,9 @@
* disabled avoiding further scheduler activity on it and we're
* being very careful to re-start the picking loop.
*/
- lockdep_unpin_lock(&rq->lock, cookie);
+ rq_unpin_lock(rq, rf);
pull_rt_task(rq);
- lockdep_repin_lock(&rq->lock, cookie);
+ rq_repin_lock(rq, rf);
/*
* pull_rt_task() can drop (and re-acquire) rq->lock; this
* means a dl or stop task can slip in, in which case we need
@@ -2198,10 +2199,9 @@
#ifdef CONFIG_SMP
if (tsk_nr_cpus_allowed(p) > 1 && rq->rt.overloaded)
queue_push_tasks(rq);
-#else
+#endif /* CONFIG_SMP */
if (p->prio < rq->curr->prio)
resched_curr(rq);
-#endif /* CONFIG_SMP */
}
}
@@ -2246,6 +2246,7 @@
}
}
+#ifdef CONFIG_POSIX_TIMERS
static void watchdog(struct rq *rq, struct task_struct *p)
{
unsigned long soft, hard;
@@ -2267,6 +2268,9 @@
p->cputime_expires.sched_exp = p->se.sum_exec_runtime;
}
}
+#else
+static inline void watchdog(struct rq *rq, struct task_struct *p) { }
+#endif
static void task_tick_rt(struct rq *rq, struct task_struct *p, int queued)
{
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 7b34c78..71b10a9 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -4,6 +4,7 @@
#include <linux/sched/rt.h>
#include <linux/u64_stats_sync.h>
#include <linux/sched/deadline.h>
+#include <linux/kernel_stat.h>
#include <linux/binfmts.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
@@ -222,7 +223,7 @@
dl_b->bw * cpus < dl_b->total_bw - old_bw + new_bw;
}
-extern struct mutex sched_domains_mutex;
+extern void init_dl_bw(struct dl_bw *dl_b);
#ifdef CONFIG_CGROUP_SCHED
@@ -583,6 +584,13 @@
};
extern struct root_domain def_root_domain;
+extern struct mutex sched_domains_mutex;
+extern cpumask_var_t fallback_doms;
+extern cpumask_var_t sched_domains_tmpmask;
+
+extern void init_defrootdomain(void);
+extern int init_sched_domains(const struct cpumask *cpu_map);
+extern void rq_attach_root(struct rq *rq, struct root_domain *rd);
#endif /* CONFIG_SMP */
@@ -644,7 +652,7 @@
unsigned long next_balance;
struct mm_struct *prev_mm;
- unsigned int clock_skip_update;
+ unsigned int clock_update_flags;
u64 clock;
u64 clock_task;
@@ -768,28 +776,110 @@
return READ_ONCE(rq->clock);
}
+/*
+ * rq::clock_update_flags bits
+ *
+ * %RQCF_REQ_SKIP - will request skipping of clock update on the next
+ * call to __schedule(). This is an optimisation to avoid
+ * neighbouring rq clock updates.
+ *
+ * %RQCF_ACT_SKIP - is set from inside of __schedule() when skipping is
+ * in effect and calls to update_rq_clock() are being ignored.
+ *
+ * %RQCF_UPDATED - is a debug flag that indicates whether a call has been
+ * made to update_rq_clock() since the last time rq::lock was pinned.
+ *
+ * If inside of __schedule(), clock_update_flags will have been
+ * shifted left (a left shift is a cheap operation for the fast path
+ * to promote %RQCF_REQ_SKIP to %RQCF_ACT_SKIP), so you must use,
+ *
+ * if (rq-clock_update_flags >= RQCF_UPDATED)
+ *
+ * to check if %RQCF_UPADTED is set. It'll never be shifted more than
+ * one position though, because the next rq_unpin_lock() will shift it
+ * back.
+ */
+#define RQCF_REQ_SKIP 0x01
+#define RQCF_ACT_SKIP 0x02
+#define RQCF_UPDATED 0x04
+
+static inline void assert_clock_updated(struct rq *rq)
+{
+ /*
+ * The only reason for not seeing a clock update since the
+ * last rq_pin_lock() is if we're currently skipping updates.
+ */
+ SCHED_WARN_ON(rq->clock_update_flags < RQCF_ACT_SKIP);
+}
+
static inline u64 rq_clock(struct rq *rq)
{
lockdep_assert_held(&rq->lock);
+ assert_clock_updated(rq);
+
return rq->clock;
}
static inline u64 rq_clock_task(struct rq *rq)
{
lockdep_assert_held(&rq->lock);
+ assert_clock_updated(rq);
+
return rq->clock_task;
}
-#define RQCF_REQ_SKIP 0x01
-#define RQCF_ACT_SKIP 0x02
-
static inline void rq_clock_skip_update(struct rq *rq, bool skip)
{
lockdep_assert_held(&rq->lock);
if (skip)
- rq->clock_skip_update |= RQCF_REQ_SKIP;
+ rq->clock_update_flags |= RQCF_REQ_SKIP;
else
- rq->clock_skip_update &= ~RQCF_REQ_SKIP;
+ rq->clock_update_flags &= ~RQCF_REQ_SKIP;
+}
+
+struct rq_flags {
+ unsigned long flags;
+ struct pin_cookie cookie;
+#ifdef CONFIG_SCHED_DEBUG
+ /*
+ * A copy of (rq::clock_update_flags & RQCF_UPDATED) for the
+ * current pin context is stashed here in case it needs to be
+ * restored in rq_repin_lock().
+ */
+ unsigned int clock_update_flags;
+#endif
+};
+
+static inline void rq_pin_lock(struct rq *rq, struct rq_flags *rf)
+{
+ rf->cookie = lockdep_pin_lock(&rq->lock);
+
+#ifdef CONFIG_SCHED_DEBUG
+ rq->clock_update_flags &= (RQCF_REQ_SKIP|RQCF_ACT_SKIP);
+ rf->clock_update_flags = 0;
+#endif
+}
+
+static inline void rq_unpin_lock(struct rq *rq, struct rq_flags *rf)
+{
+#ifdef CONFIG_SCHED_DEBUG
+ if (rq->clock_update_flags > RQCF_ACT_SKIP)
+ rf->clock_update_flags = RQCF_UPDATED;
+#endif
+
+ lockdep_unpin_lock(&rq->lock, rf->cookie);
+}
+
+static inline void rq_repin_lock(struct rq *rq, struct rq_flags *rf)
+{
+ lockdep_repin_lock(&rq->lock, rf->cookie);
+
+#ifdef CONFIG_SCHED_DEBUG
+ /*
+ * Restore the value we stashed in @rf for this pin context.
+ */
+ rq->clock_update_flags |= rf->clock_update_flags;
+#endif
}
#ifdef CONFIG_NUMA
@@ -803,6 +893,16 @@
extern bool find_numa_distance(int distance);
#endif
+#ifdef CONFIG_NUMA
+extern void sched_init_numa(void);
+extern void sched_domains_numa_masks_set(unsigned int cpu);
+extern void sched_domains_numa_masks_clear(unsigned int cpu);
+#else
+static inline void sched_init_numa(void) { }
+static inline void sched_domains_numa_masks_set(unsigned int cpu) { }
+static inline void sched_domains_numa_masks_clear(unsigned int cpu) { }
+#endif
+
#ifdef CONFIG_NUMA_BALANCING
/* The regions in numa_faults array from task_struct */
enum numa_faults_stats {
@@ -969,7 +1069,7 @@
#endif /* CONFIG_SMP */
#include "stats.h"
-#include "auto_group.h"
+#include "autogroup.h"
#ifdef CONFIG_CGROUP_SCHED
@@ -1245,7 +1345,7 @@
*/
struct task_struct * (*pick_next_task) (struct rq *rq,
struct task_struct *prev,
- struct pin_cookie cookie);
+ struct rq_flags *rf);
void (*put_prev_task) (struct rq *rq, struct task_struct *p);
#ifdef CONFIG_SMP
@@ -1501,11 +1601,6 @@
static inline void sched_avg_update(struct rq *rq) { }
#endif
-struct rq_flags {
- unsigned long flags;
- struct pin_cookie cookie;
-};
-
struct rq *__task_rq_lock(struct task_struct *p, struct rq_flags *rf)
__acquires(rq->lock);
struct rq *task_rq_lock(struct task_struct *p, struct rq_flags *rf)
@@ -1515,7 +1610,7 @@
static inline void __task_rq_unlock(struct rq *rq, struct rq_flags *rf)
__releases(rq->lock)
{
- lockdep_unpin_lock(&rq->lock, rf->cookie);
+ rq_unpin_lock(rq, rf);
raw_spin_unlock(&rq->lock);
}
@@ -1524,7 +1619,7 @@
__releases(rq->lock)
__releases(p->pi_lock)
{
- lockdep_unpin_lock(&rq->lock, rf->cookie);
+ rq_unpin_lock(rq, rf);
raw_spin_unlock(&rq->lock);
raw_spin_unlock_irqrestore(&p->pi_lock, rf->flags);
}
@@ -1674,6 +1769,10 @@
__release(rq2->lock);
}
+extern void set_rq_online (struct rq *rq);
+extern void set_rq_offline(struct rq *rq);
+extern bool sched_smp_initialized;
+
#else /* CONFIG_SMP */
/*
@@ -1750,8 +1849,7 @@
#ifdef CONFIG_IRQ_TIME_ACCOUNTING
struct irqtime {
- u64 hardirq_time;
- u64 softirq_time;
+ u64 tick_delta;
u64 irq_start_time;
struct u64_stats_sync sync;
};
@@ -1761,12 +1859,13 @@
static inline u64 irq_time_read(int cpu)
{
struct irqtime *irqtime = &per_cpu(cpu_irqtime, cpu);
+ u64 *cpustat = kcpustat_cpu(cpu).cpustat;
unsigned int seq;
u64 total;
do {
seq = __u64_stats_fetch_begin(&irqtime->sync);
- total = irqtime->softirq_time + irqtime->hardirq_time;
+ total = cpustat[CPUTIME_SOFTIRQ] + cpustat[CPUTIME_IRQ];
} while (__u64_stats_fetch_retry(&irqtime->sync, seq));
return total;
diff --git a/kernel/sched/stats.h b/kernel/sched/stats.h
index 34659a8..bf0da0a 100644
--- a/kernel/sched/stats.h
+++ b/kernel/sched/stats.h
@@ -172,18 +172,19 @@
*/
/**
- * cputimer_running - return true if cputimer is running
+ * get_running_cputimer - return &tsk->signal->cputimer if cputimer is running
*
* @tsk: Pointer to target task.
*/
-static inline bool cputimer_running(struct task_struct *tsk)
-
+#ifdef CONFIG_POSIX_TIMERS
+static inline
+struct thread_group_cputimer *get_running_cputimer(struct task_struct *tsk)
{
struct thread_group_cputimer *cputimer = &tsk->signal->cputimer;
/* Check if cputimer isn't running. This is accessed without locking. */
if (!READ_ONCE(cputimer->running))
- return false;
+ return NULL;
/*
* After we flush the task's sum_exec_runtime to sig->sum_sched_runtime
@@ -200,10 +201,17 @@
* clock delta is behind the expiring timer value.
*/
if (unlikely(!tsk->sighand))
- return false;
+ return NULL;
- return true;
+ return cputimer;
}
+#else
+static inline
+struct thread_group_cputimer *get_running_cputimer(struct task_struct *tsk)
+{
+ return NULL;
+}
+#endif
/**
* account_group_user_time - Maintain utime for a thread group.
@@ -216,11 +224,11 @@
* running CPU and update the utime field there.
*/
static inline void account_group_user_time(struct task_struct *tsk,
- cputime_t cputime)
+ u64 cputime)
{
- struct thread_group_cputimer *cputimer = &tsk->signal->cputimer;
+ struct thread_group_cputimer *cputimer = get_running_cputimer(tsk);
- if (!cputimer_running(tsk))
+ if (!cputimer)
return;
atomic64_add(cputime, &cputimer->cputime_atomic.utime);
@@ -237,11 +245,11 @@
* running CPU and update the stime field there.
*/
static inline void account_group_system_time(struct task_struct *tsk,
- cputime_t cputime)
+ u64 cputime)
{
- struct thread_group_cputimer *cputimer = &tsk->signal->cputimer;
+ struct thread_group_cputimer *cputimer = get_running_cputimer(tsk);
- if (!cputimer_running(tsk))
+ if (!cputimer)
return;
atomic64_add(cputime, &cputimer->cputime_atomic.stime);
@@ -260,9 +268,9 @@
static inline void account_group_exec_runtime(struct task_struct *tsk,
unsigned long long ns)
{
- struct thread_group_cputimer *cputimer = &tsk->signal->cputimer;
+ struct thread_group_cputimer *cputimer = get_running_cputimer(tsk);
- if (!cputimer_running(tsk))
+ if (!cputimer)
return;
atomic64_add(ns, &cputimer->cputime_atomic.sum_exec_runtime);
diff --git a/kernel/sched/stop_task.c b/kernel/sched/stop_task.c
index 604297a..9f69fb6 100644
--- a/kernel/sched/stop_task.c
+++ b/kernel/sched/stop_task.c
@@ -24,7 +24,7 @@
}
static struct task_struct *
-pick_next_task_stop(struct rq *rq, struct task_struct *prev, struct pin_cookie cookie)
+pick_next_task_stop(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
{
struct task_struct *stop = rq->stop;
diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
new file mode 100644
index 0000000..1b0b4fb
--- /dev/null
+++ b/kernel/sched/topology.c
@@ -0,0 +1,1658 @@
+/*
+ * Scheduler topology setup/handling methods
+ */
+#include <linux/sched.h>
+#include <linux/mutex.h>
+
+#include "sched.h"
+
+DEFINE_MUTEX(sched_domains_mutex);
+
+/* Protected by sched_domains_mutex: */
+cpumask_var_t sched_domains_tmpmask;
+
+#ifdef CONFIG_SCHED_DEBUG
+
+static __read_mostly int sched_debug_enabled;
+
+static int __init sched_debug_setup(char *str)
+{
+ sched_debug_enabled = 1;
+
+ return 0;
+}
+early_param("sched_debug", sched_debug_setup);
+
+static inline bool sched_debug(void)
+{
+ return sched_debug_enabled;
+}
+
+static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level,
+ struct cpumask *groupmask)
+{
+ struct sched_group *group = sd->groups;
+
+ cpumask_clear(groupmask);
+
+ printk(KERN_DEBUG "%*s domain %d: ", level, "", level);
+
+ if (!(sd->flags & SD_LOAD_BALANCE)) {
+ printk("does not load-balance\n");
+ if (sd->parent)
+ printk(KERN_ERR "ERROR: !SD_LOAD_BALANCE domain"
+ " has parent");
+ return -1;
+ }
+
+ printk(KERN_CONT "span %*pbl level %s\n",
+ cpumask_pr_args(sched_domain_span(sd)), sd->name);
+
+ if (!cpumask_test_cpu(cpu, sched_domain_span(sd))) {
+ printk(KERN_ERR "ERROR: domain->span does not contain "
+ "CPU%d\n", cpu);
+ }
+ if (!cpumask_test_cpu(cpu, sched_group_cpus(group))) {
+ printk(KERN_ERR "ERROR: domain->groups does not contain"
+ " CPU%d\n", cpu);
+ }
+
+ printk(KERN_DEBUG "%*s groups:", level + 1, "");
+ do {
+ if (!group) {
+ printk("\n");
+ printk(KERN_ERR "ERROR: group is NULL\n");
+ break;
+ }
+
+ if (!cpumask_weight(sched_group_cpus(group))) {
+ printk(KERN_CONT "\n");
+ printk(KERN_ERR "ERROR: empty group\n");
+ break;
+ }
+
+ if (!(sd->flags & SD_OVERLAP) &&
+ cpumask_intersects(groupmask, sched_group_cpus(group))) {
+ printk(KERN_CONT "\n");
+ printk(KERN_ERR "ERROR: repeated CPUs\n");
+ break;
+ }
+
+ cpumask_or(groupmask, groupmask, sched_group_cpus(group));
+
+ printk(KERN_CONT " %*pbl",
+ cpumask_pr_args(sched_group_cpus(group)));
+ if (group->sgc->capacity != SCHED_CAPACITY_SCALE) {
+ printk(KERN_CONT " (cpu_capacity = %lu)",
+ group->sgc->capacity);
+ }
+
+ group = group->next;
+ } while (group != sd->groups);
+ printk(KERN_CONT "\n");
+
+ if (!cpumask_equal(sched_domain_span(sd), groupmask))
+ printk(KERN_ERR "ERROR: groups don't span domain->span\n");
+
+ if (sd->parent &&
+ !cpumask_subset(groupmask, sched_domain_span(sd->parent)))
+ printk(KERN_ERR "ERROR: parent span is not a superset "
+ "of domain->span\n");
+ return 0;
+}
+
+static void sched_domain_debug(struct sched_domain *sd, int cpu)
+{
+ int level = 0;
+
+ if (!sched_debug_enabled)
+ return;
+
+ if (!sd) {
+ printk(KERN_DEBUG "CPU%d attaching NULL sched-domain.\n", cpu);
+ return;
+ }
+
+ printk(KERN_DEBUG "CPU%d attaching sched-domain:\n", cpu);
+
+ for (;;) {
+ if (sched_domain_debug_one(sd, cpu, level, sched_domains_tmpmask))
+ break;
+ level++;
+ sd = sd->parent;
+ if (!sd)
+ break;
+ }
+}
+#else /* !CONFIG_SCHED_DEBUG */
+
+# define sched_debug_enabled 0
+# define sched_domain_debug(sd, cpu) do { } while (0)
+static inline bool sched_debug(void)
+{
+ return false;
+}
+#endif /* CONFIG_SCHED_DEBUG */
+
+static int sd_degenerate(struct sched_domain *sd)
+{
+ if (cpumask_weight(sched_domain_span(sd)) == 1)
+ return 1;
+
+ /* Following flags need at least 2 groups */
+ if (sd->flags & (SD_LOAD_BALANCE |
+ SD_BALANCE_NEWIDLE |
+ SD_BALANCE_FORK |
+ SD_BALANCE_EXEC |
+ SD_SHARE_CPUCAPACITY |
+ SD_ASYM_CPUCAPACITY |
+ SD_SHARE_PKG_RESOURCES |
+ SD_SHARE_POWERDOMAIN)) {
+ if (sd->groups != sd->groups->next)
+ return 0;
+ }
+
+ /* Following flags don't use groups */
+ if (sd->flags & (SD_WAKE_AFFINE))
+ return 0;
+
+ return 1;
+}
+
+static int
+sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent)
+{
+ unsigned long cflags = sd->flags, pflags = parent->flags;
+
+ if (sd_degenerate(parent))
+ return 1;
+
+ if (!cpumask_equal(sched_domain_span(sd), sched_domain_span(parent)))
+ return 0;
+
+ /* Flags needing groups don't count if only 1 group in parent */
+ if (parent->groups == parent->groups->next) {
+ pflags &= ~(SD_LOAD_BALANCE |
+ SD_BALANCE_NEWIDLE |
+ SD_BALANCE_FORK |
+ SD_BALANCE_EXEC |
+ SD_ASYM_CPUCAPACITY |
+ SD_SHARE_CPUCAPACITY |
+ SD_SHARE_PKG_RESOURCES |
+ SD_PREFER_SIBLING |
+ SD_SHARE_POWERDOMAIN);
+ if (nr_node_ids == 1)
+ pflags &= ~SD_SERIALIZE;
+ }
+ if (~cflags & pflags)
+ return 0;
+
+ return 1;
+}
+
+static void free_rootdomain(struct rcu_head *rcu)
+{
+ struct root_domain *rd = container_of(rcu, struct root_domain, rcu);
+
+ cpupri_cleanup(&rd->cpupri);
+ cpudl_cleanup(&rd->cpudl);
+ free_cpumask_var(rd->dlo_mask);
+ free_cpumask_var(rd->rto_mask);
+ free_cpumask_var(rd->online);
+ free_cpumask_var(rd->span);
+ kfree(rd);
+}
+
+void rq_attach_root(struct rq *rq, struct root_domain *rd)
+{
+ struct root_domain *old_rd = NULL;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&rq->lock, flags);
+
+ if (rq->rd) {
+ old_rd = rq->rd;
+
+ if (cpumask_test_cpu(rq->cpu, old_rd->online))
+ set_rq_offline(rq);
+
+ cpumask_clear_cpu(rq->cpu, old_rd->span);
+
+ /*
+ * If we dont want to free the old_rd yet then
+ * set old_rd to NULL to skip the freeing later
+ * in this function:
+ */
+ if (!atomic_dec_and_test(&old_rd->refcount))
+ old_rd = NULL;
+ }
+
+ atomic_inc(&rd->refcount);
+ rq->rd = rd;
+
+ cpumask_set_cpu(rq->cpu, rd->span);
+ if (cpumask_test_cpu(rq->cpu, cpu_active_mask))
+ set_rq_online(rq);
+
+ raw_spin_unlock_irqrestore(&rq->lock, flags);
+
+ if (old_rd)
+ call_rcu_sched(&old_rd->rcu, free_rootdomain);
+}
+
+static int init_rootdomain(struct root_domain *rd)
+{
+ memset(rd, 0, sizeof(*rd));
+
+ if (!zalloc_cpumask_var(&rd->span, GFP_KERNEL))
+ goto out;
+ if (!zalloc_cpumask_var(&rd->online, GFP_KERNEL))
+ goto free_span;
+ if (!zalloc_cpumask_var(&rd->dlo_mask, GFP_KERNEL))
+ goto free_online;
+ if (!zalloc_cpumask_var(&rd->rto_mask, GFP_KERNEL))
+ goto free_dlo_mask;
+
+ init_dl_bw(&rd->dl_bw);
+ if (cpudl_init(&rd->cpudl) != 0)
+ goto free_rto_mask;
+
+ if (cpupri_init(&rd->cpupri) != 0)
+ goto free_cpudl;
+ return 0;
+
+free_cpudl:
+ cpudl_cleanup(&rd->cpudl);
+free_rto_mask:
+ free_cpumask_var(rd->rto_mask);
+free_dlo_mask:
+ free_cpumask_var(rd->dlo_mask);
+free_online:
+ free_cpumask_var(rd->online);
+free_span:
+ free_cpumask_var(rd->span);
+out:
+ return -ENOMEM;
+}
+
+/*
+ * By default the system creates a single root-domain with all CPUs as
+ * members (mimicking the global state we have today).
+ */
+struct root_domain def_root_domain;
+
+void init_defrootdomain(void)
+{
+ init_rootdomain(&def_root_domain);
+
+ atomic_set(&def_root_domain.refcount, 1);
+}
+
+static struct root_domain *alloc_rootdomain(void)
+{
+ struct root_domain *rd;
+
+ rd = kmalloc(sizeof(*rd), GFP_KERNEL);
+ if (!rd)
+ return NULL;
+
+ if (init_rootdomain(rd) != 0) {
+ kfree(rd);
+ return NULL;
+ }
+
+ return rd;
+}
+
+static void free_sched_groups(struct sched_group *sg, int free_sgc)
+{
+ struct sched_group *tmp, *first;
+
+ if (!sg)
+ return;
+
+ first = sg;
+ do {
+ tmp = sg->next;
+
+ if (free_sgc && atomic_dec_and_test(&sg->sgc->ref))
+ kfree(sg->sgc);
+
+ kfree(sg);
+ sg = tmp;
+ } while (sg != first);
+}
+
+static void destroy_sched_domain(struct sched_domain *sd)
+{
+ /*
+ * If its an overlapping domain it has private groups, iterate and
+ * nuke them all.
+ */
+ if (sd->flags & SD_OVERLAP) {
+ free_sched_groups(sd->groups, 1);
+ } else if (atomic_dec_and_test(&sd->groups->ref)) {
+ kfree(sd->groups->sgc);
+ kfree(sd->groups);
+ }
+ if (sd->shared && atomic_dec_and_test(&sd->shared->ref))
+ kfree(sd->shared);
+ kfree(sd);
+}
+
+static void destroy_sched_domains_rcu(struct rcu_head *rcu)
+{
+ struct sched_domain *sd = container_of(rcu, struct sched_domain, rcu);
+
+ while (sd) {
+ struct sched_domain *parent = sd->parent;
+ destroy_sched_domain(sd);
+ sd = parent;
+ }
+}
+
+static void destroy_sched_domains(struct sched_domain *sd)
+{
+ if (sd)
+ call_rcu(&sd->rcu, destroy_sched_domains_rcu);
+}
+
+/*
+ * Keep a special pointer to the highest sched_domain that has
+ * SD_SHARE_PKG_RESOURCE set (Last Level Cache Domain) for this
+ * allows us to avoid some pointer chasing select_idle_sibling().
+ *
+ * Also keep a unique ID per domain (we use the first CPU number in
+ * the cpumask of the domain), this allows us to quickly tell if
+ * two CPUs are in the same cache domain, see cpus_share_cache().
+ */
+DEFINE_PER_CPU(struct sched_domain *, sd_llc);
+DEFINE_PER_CPU(int, sd_llc_size);
+DEFINE_PER_CPU(int, sd_llc_id);
+DEFINE_PER_CPU(struct sched_domain_shared *, sd_llc_shared);
+DEFINE_PER_CPU(struct sched_domain *, sd_numa);
+DEFINE_PER_CPU(struct sched_domain *, sd_asym);
+
+static void update_top_cache_domain(int cpu)
+{
+ struct sched_domain_shared *sds = NULL;
+ struct sched_domain *sd;
+ int id = cpu;
+ int size = 1;
+
+ sd = highest_flag_domain(cpu, SD_SHARE_PKG_RESOURCES);
+ if (sd) {
+ id = cpumask_first(sched_domain_span(sd));
+ size = cpumask_weight(sched_domain_span(sd));
+ sds = sd->shared;
+ }
+
+ rcu_assign_pointer(per_cpu(sd_llc, cpu), sd);
+ per_cpu(sd_llc_size, cpu) = size;
+ per_cpu(sd_llc_id, cpu) = id;
+ rcu_assign_pointer(per_cpu(sd_llc_shared, cpu), sds);
+
+ sd = lowest_flag_domain(cpu, SD_NUMA);
+ rcu_assign_pointer(per_cpu(sd_numa, cpu), sd);
+
+ sd = highest_flag_domain(cpu, SD_ASYM_PACKING);
+ rcu_assign_pointer(per_cpu(sd_asym, cpu), sd);
+}
+
+/*
+ * Attach the domain 'sd' to 'cpu' as its base domain. Callers must
+ * hold the hotplug lock.
+ */
+static void
+cpu_attach_domain(struct sched_domain *sd, struct root_domain *rd, int cpu)
+{
+ struct rq *rq = cpu_rq(cpu);
+ struct sched_domain *tmp;
+
+ /* Remove the sched domains which do not contribute to scheduling. */
+ for (tmp = sd; tmp; ) {
+ struct sched_domain *parent = tmp->parent;
+ if (!parent)
+ break;
+
+ if (sd_parent_degenerate(tmp, parent)) {
+ tmp->parent = parent->parent;
+ if (parent->parent)
+ parent->parent->child = tmp;
+ /*
+ * Transfer SD_PREFER_SIBLING down in case of a
+ * degenerate parent; the spans match for this
+ * so the property transfers.
+ */
+ if (parent->flags & SD_PREFER_SIBLING)
+ tmp->flags |= SD_PREFER_SIBLING;
+ destroy_sched_domain(parent);
+ } else
+ tmp = tmp->parent;
+ }
+
+ if (sd && sd_degenerate(sd)) {
+ tmp = sd;
+ sd = sd->parent;
+ destroy_sched_domain(tmp);
+ if (sd)
+ sd->child = NULL;
+ }
+
+ sched_domain_debug(sd, cpu);
+
+ rq_attach_root(rq, rd);
+ tmp = rq->sd;
+ rcu_assign_pointer(rq->sd, sd);
+ destroy_sched_domains(tmp);
+
+ update_top_cache_domain(cpu);
+}
+
+/* Setup the mask of CPUs configured for isolated domains */
+static int __init isolated_cpu_setup(char *str)
+{
+ int ret;
+
+ alloc_bootmem_cpumask_var(&cpu_isolated_map);
+ ret = cpulist_parse(str, cpu_isolated_map);
+ if (ret) {
+ pr_err("sched: Error, all isolcpus= values must be between 0 and %d\n", nr_cpu_ids);
+ return 0;
+ }
+ return 1;
+}
+__setup("isolcpus=", isolated_cpu_setup);
+
+struct s_data {
+ struct sched_domain ** __percpu sd;
+ struct root_domain *rd;
+};
+
+enum s_alloc {
+ sa_rootdomain,
+ sa_sd,
+ sa_sd_storage,
+ sa_none,
+};
+
+/*
+ * Build an iteration mask that can exclude certain CPUs from the upwards
+ * domain traversal.
+ *
+ * Asymmetric node setups can result in situations where the domain tree is of
+ * unequal depth, make sure to skip domains that already cover the entire
+ * range.
+ *
+ * In that case build_sched_domains() will have terminated the iteration early
+ * and our sibling sd spans will be empty. Domains should always include the
+ * CPU they're built on, so check that.
+ */
+static void build_group_mask(struct sched_domain *sd, struct sched_group *sg)
+{
+ const struct cpumask *span = sched_domain_span(sd);
+ struct sd_data *sdd = sd->private;
+ struct sched_domain *sibling;
+ int i;
+
+ for_each_cpu(i, span) {
+ sibling = *per_cpu_ptr(sdd->sd, i);
+ if (!cpumask_test_cpu(i, sched_domain_span(sibling)))
+ continue;
+
+ cpumask_set_cpu(i, sched_group_mask(sg));
+ }
+}
+
+/*
+ * Return the canonical balance CPU for this group, this is the first CPU
+ * of this group that's also in the iteration mask.
+ */
+int group_balance_cpu(struct sched_group *sg)
+{
+ return cpumask_first_and(sched_group_cpus(sg), sched_group_mask(sg));
+}
+
+static int
+build_overlap_sched_groups(struct sched_domain *sd, int cpu)
+{
+ struct sched_group *first = NULL, *last = NULL, *groups = NULL, *sg;
+ const struct cpumask *span = sched_domain_span(sd);
+ struct cpumask *covered = sched_domains_tmpmask;
+ struct sd_data *sdd = sd->private;
+ struct sched_domain *sibling;
+ int i;
+
+ cpumask_clear(covered);
+
+ for_each_cpu(i, span) {
+ struct cpumask *sg_span;
+
+ if (cpumask_test_cpu(i, covered))
+ continue;
+
+ sibling = *per_cpu_ptr(sdd->sd, i);
+
+ /* See the comment near build_group_mask(). */
+ if (!cpumask_test_cpu(i, sched_domain_span(sibling)))
+ continue;
+
+ sg = kzalloc_node(sizeof(struct sched_group) + cpumask_size(),
+ GFP_KERNEL, cpu_to_node(cpu));
+
+ if (!sg)
+ goto fail;
+
+ sg_span = sched_group_cpus(sg);
+ if (sibling->child)
+ cpumask_copy(sg_span, sched_domain_span(sibling->child));
+ else
+ cpumask_set_cpu(i, sg_span);
+
+ cpumask_or(covered, covered, sg_span);
+
+ sg->sgc = *per_cpu_ptr(sdd->sgc, i);
+ if (atomic_inc_return(&sg->sgc->ref) == 1)
+ build_group_mask(sd, sg);
+
+ /*
+ * Initialize sgc->capacity such that even if we mess up the
+ * domains and no possible iteration will get us here, we won't
+ * die on a /0 trap.
+ */
+ sg->sgc->capacity = SCHED_CAPACITY_SCALE * cpumask_weight(sg_span);
+ sg->sgc->min_capacity = SCHED_CAPACITY_SCALE;
+
+ /*
+ * Make sure the first group of this domain contains the
+ * canonical balance CPU. Otherwise the sched_domain iteration
+ * breaks. See update_sg_lb_stats().
+ */
+ if ((!groups && cpumask_test_cpu(cpu, sg_span)) ||
+ group_balance_cpu(sg) == cpu)
+ groups = sg;
+
+ if (!first)
+ first = sg;
+ if (last)
+ last->next = sg;
+ last = sg;
+ last->next = first;
+ }
+ sd->groups = groups;
+
+ return 0;
+
+fail:
+ free_sched_groups(first, 0);
+
+ return -ENOMEM;
+}
+
+static int get_group(int cpu, struct sd_data *sdd, struct sched_group **sg)
+{
+ struct sched_domain *sd = *per_cpu_ptr(sdd->sd, cpu);
+ struct sched_domain *child = sd->child;
+
+ if (child)
+ cpu = cpumask_first(sched_domain_span(child));
+
+ if (sg) {
+ *sg = *per_cpu_ptr(sdd->sg, cpu);
+ (*sg)->sgc = *per_cpu_ptr(sdd->sgc, cpu);
+
+ /* For claim_allocations: */
+ atomic_set(&(*sg)->sgc->ref, 1);
+ }
+
+ return cpu;
+}
+
+/*
+ * build_sched_groups will build a circular linked list of the groups
+ * covered by the given span, and will set each group's ->cpumask correctly,
+ * and ->cpu_capacity to 0.
+ *
+ * Assumes the sched_domain tree is fully constructed
+ */
+static int
+build_sched_groups(struct sched_domain *sd, int cpu)
+{
+ struct sched_group *first = NULL, *last = NULL;
+ struct sd_data *sdd = sd->private;
+ const struct cpumask *span = sched_domain_span(sd);
+ struct cpumask *covered;
+ int i;
+
+ get_group(cpu, sdd, &sd->groups);
+ atomic_inc(&sd->groups->ref);
+
+ if (cpu != cpumask_first(span))
+ return 0;
+
+ lockdep_assert_held(&sched_domains_mutex);
+ covered = sched_domains_tmpmask;
+
+ cpumask_clear(covered);
+
+ for_each_cpu(i, span) {
+ struct sched_group *sg;
+ int group, j;
+
+ if (cpumask_test_cpu(i, covered))
+ continue;
+
+ group = get_group(i, sdd, &sg);
+ cpumask_setall(sched_group_mask(sg));
+
+ for_each_cpu(j, span) {
+ if (get_group(j, sdd, NULL) != group)
+ continue;
+
+ cpumask_set_cpu(j, covered);
+ cpumask_set_cpu(j, sched_group_cpus(sg));
+ }
+
+ if (!first)
+ first = sg;
+ if (last)
+ last->next = sg;
+ last = sg;
+ }
+ last->next = first;
+
+ return 0;
+}
+
+/*
+ * Initialize sched groups cpu_capacity.
+ *
+ * cpu_capacity indicates the capacity of sched group, which is used while
+ * distributing the load between different sched groups in a sched domain.
+ * Typically cpu_capacity for all the groups in a sched domain will be same
+ * unless there are asymmetries in the topology. If there are asymmetries,
+ * group having more cpu_capacity will pickup more load compared to the
+ * group having less cpu_capacity.
+ */
+static void init_sched_groups_capacity(int cpu, struct sched_domain *sd)
+{
+ struct sched_group *sg = sd->groups;
+
+ WARN_ON(!sg);
+
+ do {
+ int cpu, max_cpu = -1;
+
+ sg->group_weight = cpumask_weight(sched_group_cpus(sg));
+
+ if (!(sd->flags & SD_ASYM_PACKING))
+ goto next;
+
+ for_each_cpu(cpu, sched_group_cpus(sg)) {
+ if (max_cpu < 0)
+ max_cpu = cpu;
+ else if (sched_asym_prefer(cpu, max_cpu))
+ max_cpu = cpu;
+ }
+ sg->asym_prefer_cpu = max_cpu;
+
+next:
+ sg = sg->next;
+ } while (sg != sd->groups);
+
+ if (cpu != group_balance_cpu(sg))
+ return;
+
+ update_group_capacity(sd, cpu);
+}
+
+/*
+ * Initializers for schedule domains
+ * Non-inlined to reduce accumulated stack pressure in build_sched_domains()
+ */
+
+static int default_relax_domain_level = -1;
+int sched_domain_level_max;
+
+static int __init setup_relax_domain_level(char *str)
+{
+ if (kstrtoint(str, 0, &default_relax_domain_level))
+ pr_warn("Unable to set relax_domain_level\n");
+
+ return 1;
+}
+__setup("relax_domain_level=", setup_relax_domain_level);
+
+static void set_domain_attribute(struct sched_domain *sd,
+ struct sched_domain_attr *attr)
+{
+ int request;
+
+ if (!attr || attr->relax_domain_level < 0) {
+ if (default_relax_domain_level < 0)
+ return;
+ else
+ request = default_relax_domain_level;
+ } else
+ request = attr->relax_domain_level;
+ if (request < sd->level) {
+ /* Turn off idle balance on this domain: */
+ sd->flags &= ~(SD_BALANCE_WAKE|SD_BALANCE_NEWIDLE);
+ } else {
+ /* Turn on idle balance on this domain: */
+ sd->flags |= (SD_BALANCE_WAKE|SD_BALANCE_NEWIDLE);
+ }
+}
+
+static void __sdt_free(const struct cpumask *cpu_map);
+static int __sdt_alloc(const struct cpumask *cpu_map);
+
+static void __free_domain_allocs(struct s_data *d, enum s_alloc what,
+ const struct cpumask *cpu_map)
+{
+ switch (what) {
+ case sa_rootdomain:
+ if (!atomic_read(&d->rd->refcount))
+ free_rootdomain(&d->rd->rcu);
+ /* Fall through */
+ case sa_sd:
+ free_percpu(d->sd);
+ /* Fall through */
+ case sa_sd_storage:
+ __sdt_free(cpu_map);
+ /* Fall through */
+ case sa_none:
+ break;
+ }
+}
+
+static enum s_alloc
+__visit_domain_allocation_hell(struct s_data *d, const struct cpumask *cpu_map)
+{
+ memset(d, 0, sizeof(*d));
+
+ if (__sdt_alloc(cpu_map))
+ return sa_sd_storage;
+ d->sd = alloc_percpu(struct sched_domain *);
+ if (!d->sd)
+ return sa_sd_storage;
+ d->rd = alloc_rootdomain();
+ if (!d->rd)
+ return sa_sd;
+ return sa_rootdomain;
+}
+
+/*
+ * NULL the sd_data elements we've used to build the sched_domain and
+ * sched_group structure so that the subsequent __free_domain_allocs()
+ * will not free the data we're using.
+ */
+static void claim_allocations(int cpu, struct sched_domain *sd)
+{
+ struct sd_data *sdd = sd->private;
+
+ WARN_ON_ONCE(*per_cpu_ptr(sdd->sd, cpu) != sd);
+ *per_cpu_ptr(sdd->sd, cpu) = NULL;
+
+ if (atomic_read(&(*per_cpu_ptr(sdd->sds, cpu))->ref))
+ *per_cpu_ptr(sdd->sds, cpu) = NULL;
+
+ if (atomic_read(&(*per_cpu_ptr(sdd->sg, cpu))->ref))
+ *per_cpu_ptr(sdd->sg, cpu) = NULL;
+
+ if (atomic_read(&(*per_cpu_ptr(sdd->sgc, cpu))->ref))
+ *per_cpu_ptr(sdd->sgc, cpu) = NULL;
+}
+
+#ifdef CONFIG_NUMA
+static int sched_domains_numa_levels;
+enum numa_topology_type sched_numa_topology_type;
+static int *sched_domains_numa_distance;
+int sched_max_numa_distance;
+static struct cpumask ***sched_domains_numa_masks;
+static int sched_domains_curr_level;
+#endif
+
+/*
+ * SD_flags allowed in topology descriptions.
+ *
+ * These flags are purely descriptive of the topology and do not prescribe
+ * behaviour. Behaviour is artificial and mapped in the below sd_init()
+ * function:
+ *
+ * SD_SHARE_CPUCAPACITY - describes SMT topologies
+ * SD_SHARE_PKG_RESOURCES - describes shared caches
+ * SD_NUMA - describes NUMA topologies
+ * SD_SHARE_POWERDOMAIN - describes shared power domain
+ * SD_ASYM_CPUCAPACITY - describes mixed capacity topologies
+ *
+ * Odd one out, which beside describing the topology has a quirk also
+ * prescribes the desired behaviour that goes along with it:
+ *
+ * SD_ASYM_PACKING - describes SMT quirks
+ */
+#define TOPOLOGY_SD_FLAGS \
+ (SD_SHARE_CPUCAPACITY | \
+ SD_SHARE_PKG_RESOURCES | \
+ SD_NUMA | \
+ SD_ASYM_PACKING | \
+ SD_ASYM_CPUCAPACITY | \
+ SD_SHARE_POWERDOMAIN)
+
+static struct sched_domain *
+sd_init(struct sched_domain_topology_level *tl,
+ const struct cpumask *cpu_map,
+ struct sched_domain *child, int cpu)
+{
+ struct sd_data *sdd = &tl->data;
+ struct sched_domain *sd = *per_cpu_ptr(sdd->sd, cpu);
+ int sd_id, sd_weight, sd_flags = 0;
+
+#ifdef CONFIG_NUMA
+ /*
+ * Ugly hack to pass state to sd_numa_mask()...
+ */
+ sched_domains_curr_level = tl->numa_level;
+#endif
+
+ sd_weight = cpumask_weight(tl->mask(cpu));
+
+ if (tl->sd_flags)
+ sd_flags = (*tl->sd_flags)();
+ if (WARN_ONCE(sd_flags & ~TOPOLOGY_SD_FLAGS,
+ "wrong sd_flags in topology description\n"))
+ sd_flags &= ~TOPOLOGY_SD_FLAGS;
+
+ *sd = (struct sched_domain){
+ .min_interval = sd_weight,
+ .max_interval = 2*sd_weight,
+ .busy_factor = 32,
+ .imbalance_pct = 125,
+
+ .cache_nice_tries = 0,
+ .busy_idx = 0,
+ .idle_idx = 0,
+ .newidle_idx = 0,
+ .wake_idx = 0,
+ .forkexec_idx = 0,
+
+ .flags = 1*SD_LOAD_BALANCE
+ | 1*SD_BALANCE_NEWIDLE
+ | 1*SD_BALANCE_EXEC
+ | 1*SD_BALANCE_FORK
+ | 0*SD_BALANCE_WAKE
+ | 1*SD_WAKE_AFFINE
+ | 0*SD_SHARE_CPUCAPACITY
+ | 0*SD_SHARE_PKG_RESOURCES
+ | 0*SD_SERIALIZE
+ | 0*SD_PREFER_SIBLING
+ | 0*SD_NUMA
+ | sd_flags
+ ,
+
+ .last_balance = jiffies,
+ .balance_interval = sd_weight,
+ .smt_gain = 0,
+ .max_newidle_lb_cost = 0,
+ .next_decay_max_lb_cost = jiffies,
+ .child = child,
+#ifdef CONFIG_SCHED_DEBUG
+ .name = tl->name,
+#endif
+ };
+
+ cpumask_and(sched_domain_span(sd), cpu_map, tl->mask(cpu));
+ sd_id = cpumask_first(sched_domain_span(sd));
+
+ /*
+ * Convert topological properties into behaviour.
+ */
+
+ if (sd->flags & SD_ASYM_CPUCAPACITY) {
+ struct sched_domain *t = sd;
+
+ for_each_lower_domain(t)
+ t->flags |= SD_BALANCE_WAKE;
+ }
+
+ if (sd->flags & SD_SHARE_CPUCAPACITY) {
+ sd->flags |= SD_PREFER_SIBLING;
+ sd->imbalance_pct = 110;
+ sd->smt_gain = 1178; /* ~15% */
+
+ } else if (sd->flags & SD_SHARE_PKG_RESOURCES) {
+ sd->imbalance_pct = 117;
+ sd->cache_nice_tries = 1;
+ sd->busy_idx = 2;
+
+#ifdef CONFIG_NUMA
+ } else if (sd->flags & SD_NUMA) {
+ sd->cache_nice_tries = 2;
+ sd->busy_idx = 3;
+ sd->idle_idx = 2;
+
+ sd->flags |= SD_SERIALIZE;
+ if (sched_domains_numa_distance[tl->numa_level] > RECLAIM_DISTANCE) {
+ sd->flags &= ~(SD_BALANCE_EXEC |
+ SD_BALANCE_FORK |
+ SD_WAKE_AFFINE);
+ }
+
+#endif
+ } else {
+ sd->flags |= SD_PREFER_SIBLING;
+ sd->cache_nice_tries = 1;
+ sd->busy_idx = 2;
+ sd->idle_idx = 1;
+ }
+
+ /*
+ * For all levels sharing cache; connect a sched_domain_shared
+ * instance.
+ */
+ if (sd->flags & SD_SHARE_PKG_RESOURCES) {
+ sd->shared = *per_cpu_ptr(sdd->sds, sd_id);
+ atomic_inc(&sd->shared->ref);
+ atomic_set(&sd->shared->nr_busy_cpus, sd_weight);
+ }
+
+ sd->private = sdd;
+
+ return sd;
+}
+
+/*
+ * Topology list, bottom-up.
+ */
+static struct sched_domain_topology_level default_topology[] = {
+#ifdef CONFIG_SCHED_SMT
+ { cpu_smt_mask, cpu_smt_flags, SD_INIT_NAME(SMT) },
+#endif
+#ifdef CONFIG_SCHED_MC
+ { cpu_coregroup_mask, cpu_core_flags, SD_INIT_NAME(MC) },
+#endif
+ { cpu_cpu_mask, SD_INIT_NAME(DIE) },
+ { NULL, },
+};
+
+static struct sched_domain_topology_level *sched_domain_topology =
+ default_topology;
+
+#define for_each_sd_topology(tl) \
+ for (tl = sched_domain_topology; tl->mask; tl++)
+
+void set_sched_topology(struct sched_domain_topology_level *tl)
+{
+ if (WARN_ON_ONCE(sched_smp_initialized))
+ return;
+
+ sched_domain_topology = tl;
+}
+
+#ifdef CONFIG_NUMA
+
+static const struct cpumask *sd_numa_mask(int cpu)
+{
+ return sched_domains_numa_masks[sched_domains_curr_level][cpu_to_node(cpu)];
+}
+
+static void sched_numa_warn(const char *str)
+{
+ static int done = false;
+ int i,j;
+
+ if (done)
+ return;
+
+ done = true;
+
+ printk(KERN_WARNING "ERROR: %s\n\n", str);
+
+ for (i = 0; i < nr_node_ids; i++) {
+ printk(KERN_WARNING " ");
+ for (j = 0; j < nr_node_ids; j++)
+ printk(KERN_CONT "%02d ", node_distance(i,j));
+ printk(KERN_CONT "\n");
+ }
+ printk(KERN_WARNING "\n");
+}
+
+bool find_numa_distance(int distance)
+{
+ int i;
+
+ if (distance == node_distance(0, 0))
+ return true;
+
+ for (i = 0; i < sched_domains_numa_levels; i++) {
+ if (sched_domains_numa_distance[i] == distance)
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * A system can have three types of NUMA topology:
+ * NUMA_DIRECT: all nodes are directly connected, or not a NUMA system
+ * NUMA_GLUELESS_MESH: some nodes reachable through intermediary nodes
+ * NUMA_BACKPLANE: nodes can reach other nodes through a backplane
+ *
+ * The difference between a glueless mesh topology and a backplane
+ * topology lies in whether communication between not directly
+ * connected nodes goes through intermediary nodes (where programs
+ * could run), or through backplane controllers. This affects
+ * placement of programs.
+ *
+ * The type of topology can be discerned with the following tests:
+ * - If the maximum distance between any nodes is 1 hop, the system
+ * is directly connected.
+ * - If for two nodes A and B, located N > 1 hops away from each other,
+ * there is an intermediary node C, which is < N hops away from both
+ * nodes A and B, the system is a glueless mesh.
+ */
+static void init_numa_topology_type(void)
+{
+ int a, b, c, n;
+
+ n = sched_max_numa_distance;
+
+ if (sched_domains_numa_levels <= 1) {
+ sched_numa_topology_type = NUMA_DIRECT;
+ return;
+ }
+
+ for_each_online_node(a) {
+ for_each_online_node(b) {
+ /* Find two nodes furthest removed from each other. */
+ if (node_distance(a, b) < n)
+ continue;
+
+ /* Is there an intermediary node between a and b? */
+ for_each_online_node(c) {
+ if (node_distance(a, c) < n &&
+ node_distance(b, c) < n) {
+ sched_numa_topology_type =
+ NUMA_GLUELESS_MESH;
+ return;
+ }
+ }
+
+ sched_numa_topology_type = NUMA_BACKPLANE;
+ return;
+ }
+ }
+}
+
+void sched_init_numa(void)
+{
+ int next_distance, curr_distance = node_distance(0, 0);
+ struct sched_domain_topology_level *tl;
+ int level = 0;
+ int i, j, k;
+
+ sched_domains_numa_distance = kzalloc(sizeof(int) * nr_node_ids, GFP_KERNEL);
+ if (!sched_domains_numa_distance)
+ return;
+
+ /*
+ * O(nr_nodes^2) deduplicating selection sort -- in order to find the
+ * unique distances in the node_distance() table.
+ *
+ * Assumes node_distance(0,j) includes all distances in
+ * node_distance(i,j) in order to avoid cubic time.
+ */
+ next_distance = curr_distance;
+ for (i = 0; i < nr_node_ids; i++) {
+ for (j = 0; j < nr_node_ids; j++) {
+ for (k = 0; k < nr_node_ids; k++) {
+ int distance = node_distance(i, k);
+
+ if (distance > curr_distance &&
+ (distance < next_distance ||
+ next_distance == curr_distance))
+ next_distance = distance;
+
+ /*
+ * While not a strong assumption it would be nice to know
+ * about cases where if node A is connected to B, B is not
+ * equally connected to A.
+ */
+ if (sched_debug() && node_distance(k, i) != distance)
+ sched_numa_warn("Node-distance not symmetric");
+
+ if (sched_debug() && i && !find_numa_distance(distance))
+ sched_numa_warn("Node-0 not representative");
+ }
+ if (next_distance != curr_distance) {
+ sched_domains_numa_distance[level++] = next_distance;
+ sched_domains_numa_levels = level;
+ curr_distance = next_distance;
+ } else break;
+ }
+
+ /*
+ * In case of sched_debug() we verify the above assumption.
+ */
+ if (!sched_debug())
+ break;
+ }
+
+ if (!level)
+ return;
+
+ /*
+ * 'level' contains the number of unique distances, excluding the
+ * identity distance node_distance(i,i).
+ *
+ * The sched_domains_numa_distance[] array includes the actual distance
+ * numbers.
+ */
+
+ /*
+ * Here, we should temporarily reset sched_domains_numa_levels to 0.
+ * If it fails to allocate memory for array sched_domains_numa_masks[][],
+ * the array will contain less then 'level' members. This could be
+ * dangerous when we use it to iterate array sched_domains_numa_masks[][]
+ * in other functions.
+ *
+ * We reset it to 'level' at the end of this function.
+ */
+ sched_domains_numa_levels = 0;
+
+ sched_domains_numa_masks = kzalloc(sizeof(void *) * level, GFP_KERNEL);
+ if (!sched_domains_numa_masks)
+ return;
+
+ /*
+ * Now for each level, construct a mask per node which contains all
+ * CPUs of nodes that are that many hops away from us.
+ */
+ for (i = 0; i < level; i++) {
+ sched_domains_numa_masks[i] =
+ kzalloc(nr_node_ids * sizeof(void *), GFP_KERNEL);
+ if (!sched_domains_numa_masks[i])
+ return;
+
+ for (j = 0; j < nr_node_ids; j++) {
+ struct cpumask *mask = kzalloc(cpumask_size(), GFP_KERNEL);
+ if (!mask)
+ return;
+
+ sched_domains_numa_masks[i][j] = mask;
+
+ for_each_node(k) {
+ if (node_distance(j, k) > sched_domains_numa_distance[i])
+ continue;
+
+ cpumask_or(mask, mask, cpumask_of_node(k));
+ }
+ }
+ }
+
+ /* Compute default topology size */
+ for (i = 0; sched_domain_topology[i].mask; i++);
+
+ tl = kzalloc((i + level + 1) *
+ sizeof(struct sched_domain_topology_level), GFP_KERNEL);
+ if (!tl)
+ return;
+
+ /*
+ * Copy the default topology bits..
+ */
+ for (i = 0; sched_domain_topology[i].mask; i++)
+ tl[i] = sched_domain_topology[i];
+
+ /*
+ * .. and append 'j' levels of NUMA goodness.
+ */
+ for (j = 0; j < level; i++, j++) {
+ tl[i] = (struct sched_domain_topology_level){
+ .mask = sd_numa_mask,
+ .sd_flags = cpu_numa_flags,
+ .flags = SDTL_OVERLAP,
+ .numa_level = j,
+ SD_INIT_NAME(NUMA)
+ };
+ }
+
+ sched_domain_topology = tl;
+
+ sched_domains_numa_levels = level;
+ sched_max_numa_distance = sched_domains_numa_distance[level - 1];
+
+ init_numa_topology_type();
+}
+
+void sched_domains_numa_masks_set(unsigned int cpu)
+{
+ int node = cpu_to_node(cpu);
+ int i, j;
+
+ for (i = 0; i < sched_domains_numa_levels; i++) {
+ for (j = 0; j < nr_node_ids; j++) {
+ if (node_distance(j, node) <= sched_domains_numa_distance[i])
+ cpumask_set_cpu(cpu, sched_domains_numa_masks[i][j]);
+ }
+ }
+}
+
+void sched_domains_numa_masks_clear(unsigned int cpu)
+{
+ int i, j;
+
+ for (i = 0; i < sched_domains_numa_levels; i++) {
+ for (j = 0; j < nr_node_ids; j++)
+ cpumask_clear_cpu(cpu, sched_domains_numa_masks[i][j]);
+ }
+}
+
+#endif /* CONFIG_NUMA */
+
+static int __sdt_alloc(const struct cpumask *cpu_map)
+{
+ struct sched_domain_topology_level *tl;
+ int j;
+
+ for_each_sd_topology(tl) {
+ struct sd_data *sdd = &tl->data;
+
+ sdd->sd = alloc_percpu(struct sched_domain *);
+ if (!sdd->sd)
+ return -ENOMEM;
+
+ sdd->sds = alloc_percpu(struct sched_domain_shared *);
+ if (!sdd->sds)
+ return -ENOMEM;
+
+ sdd->sg = alloc_percpu(struct sched_group *);
+ if (!sdd->sg)
+ return -ENOMEM;
+
+ sdd->sgc = alloc_percpu(struct sched_group_capacity *);
+ if (!sdd->sgc)
+ return -ENOMEM;
+
+ for_each_cpu(j, cpu_map) {
+ struct sched_domain *sd;
+ struct sched_domain_shared *sds;
+ struct sched_group *sg;
+ struct sched_group_capacity *sgc;
+
+ sd = kzalloc_node(sizeof(struct sched_domain) + cpumask_size(),
+ GFP_KERNEL, cpu_to_node(j));
+ if (!sd)
+ return -ENOMEM;
+
+ *per_cpu_ptr(sdd->sd, j) = sd;
+
+ sds = kzalloc_node(sizeof(struct sched_domain_shared),
+ GFP_KERNEL, cpu_to_node(j));
+ if (!sds)
+ return -ENOMEM;
+
+ *per_cpu_ptr(sdd->sds, j) = sds;
+
+ sg = kzalloc_node(sizeof(struct sched_group) + cpumask_size(),
+ GFP_KERNEL, cpu_to_node(j));
+ if (!sg)
+ return -ENOMEM;
+
+ sg->next = sg;
+
+ *per_cpu_ptr(sdd->sg, j) = sg;
+
+ sgc = kzalloc_node(sizeof(struct sched_group_capacity) + cpumask_size(),
+ GFP_KERNEL, cpu_to_node(j));
+ if (!sgc)
+ return -ENOMEM;
+
+ *per_cpu_ptr(sdd->sgc, j) = sgc;
+ }
+ }
+
+ return 0;
+}
+
+static void __sdt_free(const struct cpumask *cpu_map)
+{
+ struct sched_domain_topology_level *tl;
+ int j;
+
+ for_each_sd_topology(tl) {
+ struct sd_data *sdd = &tl->data;
+
+ for_each_cpu(j, cpu_map) {
+ struct sched_domain *sd;
+
+ if (sdd->sd) {
+ sd = *per_cpu_ptr(sdd->sd, j);
+ if (sd && (sd->flags & SD_OVERLAP))
+ free_sched_groups(sd->groups, 0);
+ kfree(*per_cpu_ptr(sdd->sd, j));
+ }
+
+ if (sdd->sds)
+ kfree(*per_cpu_ptr(sdd->sds, j));
+ if (sdd->sg)
+ kfree(*per_cpu_ptr(sdd->sg, j));
+ if (sdd->sgc)
+ kfree(*per_cpu_ptr(sdd->sgc, j));
+ }
+ free_percpu(sdd->sd);
+ sdd->sd = NULL;
+ free_percpu(sdd->sds);
+ sdd->sds = NULL;
+ free_percpu(sdd->sg);
+ sdd->sg = NULL;
+ free_percpu(sdd->sgc);
+ sdd->sgc = NULL;
+ }
+}
+
+struct sched_domain *build_sched_domain(struct sched_domain_topology_level *tl,
+ const struct cpumask *cpu_map, struct sched_domain_attr *attr,
+ struct sched_domain *child, int cpu)
+{
+ struct sched_domain *sd = sd_init(tl, cpu_map, child, cpu);
+
+ if (child) {
+ sd->level = child->level + 1;
+ sched_domain_level_max = max(sched_domain_level_max, sd->level);
+ child->parent = sd;
+
+ if (!cpumask_subset(sched_domain_span(child),
+ sched_domain_span(sd))) {
+ pr_err("BUG: arch topology borken\n");
+#ifdef CONFIG_SCHED_DEBUG
+ pr_err(" the %s domain not a subset of the %s domain\n",
+ child->name, sd->name);
+#endif
+ /* Fixup, ensure @sd has at least @child cpus. */
+ cpumask_or(sched_domain_span(sd),
+ sched_domain_span(sd),
+ sched_domain_span(child));
+ }
+
+ }
+ set_domain_attribute(sd, attr);
+
+ return sd;
+}
+
+/*
+ * Build sched domains for a given set of CPUs and attach the sched domains
+ * to the individual CPUs
+ */
+static int
+build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *attr)
+{
+ enum s_alloc alloc_state;
+ struct sched_domain *sd;
+ struct s_data d;
+ struct rq *rq = NULL;
+ int i, ret = -ENOMEM;
+
+ alloc_state = __visit_domain_allocation_hell(&d, cpu_map);
+ if (alloc_state != sa_rootdomain)
+ goto error;
+
+ /* Set up domains for CPUs specified by the cpu_map: */
+ for_each_cpu(i, cpu_map) {
+ struct sched_domain_topology_level *tl;
+
+ sd = NULL;
+ for_each_sd_topology(tl) {
+ sd = build_sched_domain(tl, cpu_map, attr, sd, i);
+ if (tl == sched_domain_topology)
+ *per_cpu_ptr(d.sd, i) = sd;
+ if (tl->flags & SDTL_OVERLAP || sched_feat(FORCE_SD_OVERLAP))
+ sd->flags |= SD_OVERLAP;
+ if (cpumask_equal(cpu_map, sched_domain_span(sd)))
+ break;
+ }
+ }
+
+ /* Build the groups for the domains */
+ for_each_cpu(i, cpu_map) {
+ for (sd = *per_cpu_ptr(d.sd, i); sd; sd = sd->parent) {
+ sd->span_weight = cpumask_weight(sched_domain_span(sd));
+ if (sd->flags & SD_OVERLAP) {
+ if (build_overlap_sched_groups(sd, i))
+ goto error;
+ } else {
+ if (build_sched_groups(sd, i))
+ goto error;
+ }
+ }
+ }
+
+ /* Calculate CPU capacity for physical packages and nodes */
+ for (i = nr_cpumask_bits-1; i >= 0; i--) {
+ if (!cpumask_test_cpu(i, cpu_map))
+ continue;
+
+ for (sd = *per_cpu_ptr(d.sd, i); sd; sd = sd->parent) {
+ claim_allocations(i, sd);
+ init_sched_groups_capacity(i, sd);
+ }
+ }
+
+ /* Attach the domains */
+ rcu_read_lock();
+ for_each_cpu(i, cpu_map) {
+ rq = cpu_rq(i);
+ sd = *per_cpu_ptr(d.sd, i);
+
+ /* Use READ_ONCE()/WRITE_ONCE() to avoid load/store tearing: */
+ if (rq->cpu_capacity_orig > READ_ONCE(d.rd->max_cpu_capacity))
+ WRITE_ONCE(d.rd->max_cpu_capacity, rq->cpu_capacity_orig);
+
+ cpu_attach_domain(sd, d.rd, i);
+ }
+ rcu_read_unlock();
+
+ if (rq && sched_debug_enabled) {
+ pr_info("span: %*pbl (max cpu_capacity = %lu)\n",
+ cpumask_pr_args(cpu_map), rq->rd->max_cpu_capacity);
+ }
+
+ ret = 0;
+error:
+ __free_domain_allocs(&d, alloc_state, cpu_map);
+ return ret;
+}
+
+/* Current sched domains: */
+static cpumask_var_t *doms_cur;
+
+/* Number of sched domains in 'doms_cur': */
+static int ndoms_cur;
+
+/* Attribues of custom domains in 'doms_cur' */
+static struct sched_domain_attr *dattr_cur;
+
+/*
+ * Special case: If a kmalloc() of a doms_cur partition (array of
+ * cpumask) fails, then fallback to a single sched domain,
+ * as determined by the single cpumask fallback_doms.
+ */
+cpumask_var_t fallback_doms;
+
+/*
+ * arch_update_cpu_topology lets virtualized architectures update the
+ * CPU core maps. It is supposed to return 1 if the topology changed
+ * or 0 if it stayed the same.
+ */
+int __weak arch_update_cpu_topology(void)
+{
+ return 0;
+}
+
+cpumask_var_t *alloc_sched_domains(unsigned int ndoms)
+{
+ int i;
+ cpumask_var_t *doms;
+
+ doms = kmalloc(sizeof(*doms) * ndoms, GFP_KERNEL);
+ if (!doms)
+ return NULL;
+ for (i = 0; i < ndoms; i++) {
+ if (!alloc_cpumask_var(&doms[i], GFP_KERNEL)) {
+ free_sched_domains(doms, i);
+ return NULL;
+ }
+ }
+ return doms;
+}
+
+void free_sched_domains(cpumask_var_t doms[], unsigned int ndoms)
+{
+ unsigned int i;
+ for (i = 0; i < ndoms; i++)
+ free_cpumask_var(doms[i]);
+ kfree(doms);
+}
+
+/*
+ * Set up scheduler domains and groups. Callers must hold the hotplug lock.
+ * For now this just excludes isolated CPUs, but could be used to
+ * exclude other special cases in the future.
+ */
+int init_sched_domains(const struct cpumask *cpu_map)
+{
+ int err;
+
+ arch_update_cpu_topology();
+ ndoms_cur = 1;
+ doms_cur = alloc_sched_domains(ndoms_cur);
+ if (!doms_cur)
+ doms_cur = &fallback_doms;
+ cpumask_andnot(doms_cur[0], cpu_map, cpu_isolated_map);
+ err = build_sched_domains(doms_cur[0], NULL);
+ register_sched_domain_sysctl();
+
+ return err;
+}
+
+/*
+ * Detach sched domains from a group of CPUs specified in cpu_map
+ * These CPUs will now be attached to the NULL domain
+ */
+static void detach_destroy_domains(const struct cpumask *cpu_map)
+{
+ int i;
+
+ rcu_read_lock();
+ for_each_cpu(i, cpu_map)
+ cpu_attach_domain(NULL, &def_root_domain, i);
+ rcu_read_unlock();
+}
+
+/* handle null as "default" */
+static int dattrs_equal(struct sched_domain_attr *cur, int idx_cur,
+ struct sched_domain_attr *new, int idx_new)
+{
+ struct sched_domain_attr tmp;
+
+ /* Fast path: */
+ if (!new && !cur)
+ return 1;
+
+ tmp = SD_ATTR_INIT;
+ return !memcmp(cur ? (cur + idx_cur) : &tmp,
+ new ? (new + idx_new) : &tmp,
+ sizeof(struct sched_domain_attr));
+}
+
+/*
+ * Partition sched domains as specified by the 'ndoms_new'
+ * cpumasks in the array doms_new[] of cpumasks. This compares
+ * doms_new[] to the current sched domain partitioning, doms_cur[].
+ * It destroys each deleted domain and builds each new domain.
+ *
+ * 'doms_new' is an array of cpumask_var_t's of length 'ndoms_new'.
+ * The masks don't intersect (don't overlap.) We should setup one
+ * sched domain for each mask. CPUs not in any of the cpumasks will
+ * not be load balanced. If the same cpumask appears both in the
+ * current 'doms_cur' domains and in the new 'doms_new', we can leave
+ * it as it is.
+ *
+ * The passed in 'doms_new' should be allocated using
+ * alloc_sched_domains. This routine takes ownership of it and will
+ * free_sched_domains it when done with it. If the caller failed the
+ * alloc call, then it can pass in doms_new == NULL && ndoms_new == 1,
+ * and partition_sched_domains() will fallback to the single partition
+ * 'fallback_doms', it also forces the domains to be rebuilt.
+ *
+ * If doms_new == NULL it will be replaced with cpu_online_mask.
+ * ndoms_new == 0 is a special case for destroying existing domains,
+ * and it will not create the default domain.
+ *
+ * Call with hotplug lock held
+ */
+void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[],
+ struct sched_domain_attr *dattr_new)
+{
+ int i, j, n;
+ int new_topology;
+
+ mutex_lock(&sched_domains_mutex);
+
+ /* Always unregister in case we don't destroy any domains: */
+ unregister_sched_domain_sysctl();
+
+ /* Let the architecture update CPU core mappings: */
+ new_topology = arch_update_cpu_topology();
+
+ n = doms_new ? ndoms_new : 0;
+
+ /* Destroy deleted domains: */
+ for (i = 0; i < ndoms_cur; i++) {
+ for (j = 0; j < n && !new_topology; j++) {
+ if (cpumask_equal(doms_cur[i], doms_new[j])
+ && dattrs_equal(dattr_cur, i, dattr_new, j))
+ goto match1;
+ }
+ /* No match - a current sched domain not in new doms_new[] */
+ detach_destroy_domains(doms_cur[i]);
+match1:
+ ;
+ }
+
+ n = ndoms_cur;
+ if (doms_new == NULL) {
+ n = 0;
+ doms_new = &fallback_doms;
+ cpumask_andnot(doms_new[0], cpu_active_mask, cpu_isolated_map);
+ WARN_ON_ONCE(dattr_new);
+ }
+
+ /* Build new domains: */
+ for (i = 0; i < ndoms_new; i++) {
+ for (j = 0; j < n && !new_topology; j++) {
+ if (cpumask_equal(doms_new[i], doms_cur[j])
+ && dattrs_equal(dattr_new, i, dattr_cur, j))
+ goto match2;
+ }
+ /* No match - add a new doms_new */
+ build_sched_domains(doms_new[i], dattr_new ? dattr_new + i : NULL);
+match2:
+ ;
+ }
+
+ /* Remember the new sched domains: */
+ if (doms_cur != &fallback_doms)
+ free_sched_domains(doms_cur, ndoms_cur);
+
+ kfree(dattr_cur);
+ doms_cur = doms_new;
+ dattr_cur = dattr_new;
+ ndoms_cur = ndoms_new;
+
+ register_sched_domain_sysctl();
+
+ mutex_unlock(&sched_domains_mutex);
+}
+
diff --git a/kernel/signal.c b/kernel/signal.c
index 3603d93..13f9def 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1581,7 +1581,7 @@
unsigned long flags;
struct sighand_struct *psig;
bool autoreap = false;
- cputime_t utime, stime;
+ u64 utime, stime;
BUG_ON(sig == -1);
@@ -1620,8 +1620,8 @@
rcu_read_unlock();
task_cputime(tsk, &utime, &stime);
- info.si_utime = cputime_to_clock_t(utime + tsk->signal->utime);
- info.si_stime = cputime_to_clock_t(stime + tsk->signal->stime);
+ info.si_utime = nsec_to_clock_t(utime + tsk->signal->utime);
+ info.si_stime = nsec_to_clock_t(stime + tsk->signal->stime);
info.si_status = tsk->exit_code & 0x7f;
if (tsk->exit_code & 0x80)
@@ -1685,7 +1685,7 @@
unsigned long flags;
struct task_struct *parent;
struct sighand_struct *sighand;
- cputime_t utime, stime;
+ u64 utime, stime;
if (for_ptracer) {
parent = tsk->parent;
@@ -1705,8 +1705,8 @@
rcu_read_unlock();
task_cputime(tsk, &utime, &stime);
- info.si_utime = cputime_to_clock_t(utime);
- info.si_stime = cputime_to_clock_t(stime);
+ info.si_utime = nsec_to_clock_t(utime);
+ info.si_stime = nsec_to_clock_t(stime);
info.si_code = why;
switch (why) {
diff --git a/kernel/sys.c b/kernel/sys.c
index 842914e..7d4a9a6 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -881,15 +881,15 @@
void do_sys_times(struct tms *tms)
{
- cputime_t tgutime, tgstime, cutime, cstime;
+ u64 tgutime, tgstime, cutime, cstime;
thread_group_cputime_adjusted(current, &tgutime, &tgstime);
cutime = current->signal->cutime;
cstime = current->signal->cstime;
- tms->tms_utime = cputime_to_clock_t(tgutime);
- tms->tms_stime = cputime_to_clock_t(tgstime);
- tms->tms_cutime = cputime_to_clock_t(cutime);
- tms->tms_cstime = cputime_to_clock_t(cstime);
+ tms->tms_utime = nsec_to_clock_t(tgutime);
+ tms->tms_stime = nsec_to_clock_t(tgstime);
+ tms->tms_cutime = nsec_to_clock_t(cutime);
+ tms->tms_cstime = nsec_to_clock_t(cstime);
}
SYSCALL_DEFINE1(times, struct tms __user *, tbuf)
@@ -1544,7 +1544,7 @@
{
struct task_struct *t;
unsigned long flags;
- cputime_t tgutime, tgstime, utime, stime;
+ u64 tgutime, tgstime, utime, stime;
unsigned long maxrss = 0;
memset((char *)r, 0, sizeof (*r));
@@ -1600,8 +1600,8 @@
unlock_task_sighand(p, &flags);
out:
- cputime_to_timeval(utime, &r->ru_utime);
- cputime_to_timeval(stime, &r->ru_stime);
+ r->ru_utime = ns_to_timeval(utime);
+ r->ru_stime = ns_to_timeval(stime);
if (who != RUSAGE_CHILDREN) {
struct mm_struct *mm = get_task_mm(p);
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 1aea594..bb260ce 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -416,7 +416,7 @@
},
{
.procname = "sched_rr_timeslice_ms",
- .data = &sched_rr_timeslice,
+ .data = &sysctl_sched_rr_timeslice,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = sched_rr_handler,
diff --git a/kernel/time/Makefile b/kernel/time/Makefile
index 976840d..938dbf3 100644
--- a/kernel/time/Makefile
+++ b/kernel/time/Makefile
@@ -15,6 +15,5 @@
endif
obj-$(CONFIG_GENERIC_SCHED_CLOCK) += sched_clock.o
obj-$(CONFIG_TICK_ONESHOT) += tick-oneshot.o tick-sched.o
-obj-$(CONFIG_TIMER_STATS) += timer_stats.o
obj-$(CONFIG_DEBUG_FS) += timekeeping_debug.o
obj-$(CONFIG_TEST_UDELAY) += test_udelay.o
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
index 665985b..93621ae 100644
--- a/kernel/time/clocksource.c
+++ b/kernel/time/clocksource.c
@@ -141,6 +141,10 @@
{
cs->flags &= ~(CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_WATCHDOG);
cs->flags |= CLOCK_SOURCE_UNSTABLE;
+
+ if (cs->mark_unstable)
+ cs->mark_unstable(cs);
+
if (finished_booting)
schedule_work(&watchdog_work);
}
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index c6ecedd..8e11d8d 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -94,17 +94,15 @@
};
static const int hrtimer_clock_to_base_table[MAX_CLOCKS] = {
+ /* Make sure we catch unsupported clockids */
+ [0 ... MAX_CLOCKS - 1] = HRTIMER_MAX_CLOCK_BASES,
+
[CLOCK_REALTIME] = HRTIMER_BASE_REALTIME,
[CLOCK_MONOTONIC] = HRTIMER_BASE_MONOTONIC,
[CLOCK_BOOTTIME] = HRTIMER_BASE_BOOTTIME,
[CLOCK_TAI] = HRTIMER_BASE_TAI,
};
-static inline int hrtimer_clockid_to_base(clockid_t clock_id)
-{
- return hrtimer_clock_to_base_table[clock_id];
-}
-
/*
* Functions and macros which are different for UP/SMP systems are kept in a
* single place
@@ -766,34 +764,6 @@
clock_was_set_delayed();
}
-static inline void timer_stats_hrtimer_set_start_info(struct hrtimer *timer)
-{
-#ifdef CONFIG_TIMER_STATS
- if (timer->start_site)
- return;
- timer->start_site = __builtin_return_address(0);
- memcpy(timer->start_comm, current->comm, TASK_COMM_LEN);
- timer->start_pid = current->pid;
-#endif
-}
-
-static inline void timer_stats_hrtimer_clear_start_info(struct hrtimer *timer)
-{
-#ifdef CONFIG_TIMER_STATS
- timer->start_site = NULL;
-#endif
-}
-
-static inline void timer_stats_account_hrtimer(struct hrtimer *timer)
-{
-#ifdef CONFIG_TIMER_STATS
- if (likely(!timer_stats_active))
- return;
- timer_stats_update_stats(timer, timer->start_pid, timer->start_site,
- timer->function, timer->start_comm, 0);
-#endif
-}
-
/*
* Counterpart to lock_hrtimer_base above:
*/
@@ -932,7 +902,6 @@
* rare case and less expensive than a smp call.
*/
debug_deactivate(timer);
- timer_stats_hrtimer_clear_start_info(timer);
reprogram = base->cpu_base == this_cpu_ptr(&hrtimer_bases);
if (!restart)
@@ -990,8 +959,6 @@
/* Switch the timer base, if necessary: */
new_base = switch_hrtimer_base(timer, base, mode & HRTIMER_MODE_PINNED);
- timer_stats_hrtimer_set_start_info(timer);
-
leftmost = enqueue_hrtimer(timer, new_base);
if (!leftmost)
goto unlock;
@@ -1112,6 +1079,18 @@
}
#endif
+static inline int hrtimer_clockid_to_base(clockid_t clock_id)
+{
+ if (likely(clock_id < MAX_CLOCKS)) {
+ int base = hrtimer_clock_to_base_table[clock_id];
+
+ if (likely(base != HRTIMER_MAX_CLOCK_BASES))
+ return base;
+ }
+ WARN(1, "Invalid clockid %d. Using MONOTONIC\n", clock_id);
+ return HRTIMER_BASE_MONOTONIC;
+}
+
static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
enum hrtimer_mode mode)
{
@@ -1128,12 +1107,6 @@
base = hrtimer_clockid_to_base(clock_id);
timer->base = &cpu_base->clock_base[base];
timerqueue_init(&timer->node);
-
-#ifdef CONFIG_TIMER_STATS
- timer->start_site = NULL;
- timer->start_pid = -1;
- memset(timer->start_comm, 0, TASK_COMM_LEN);
-#endif
}
/**
@@ -1217,7 +1190,6 @@
raw_write_seqcount_barrier(&cpu_base->seq);
__remove_hrtimer(timer, base, HRTIMER_STATE_INACTIVE, 0);
- timer_stats_account_hrtimer(timer);
fn = timer->function;
/*
diff --git a/kernel/time/itimer.c b/kernel/time/itimer.c
index 8c89143..a95f13c 100644
--- a/kernel/time/itimer.c
+++ b/kernel/time/itimer.c
@@ -45,16 +45,16 @@
static void get_cpu_itimer(struct task_struct *tsk, unsigned int clock_id,
struct itimerval *const value)
{
- cputime_t cval, cinterval;
+ u64 val, interval;
struct cpu_itimer *it = &tsk->signal->it[clock_id];
spin_lock_irq(&tsk->sighand->siglock);
- cval = it->expires;
- cinterval = it->incr;
- if (cval) {
+ val = it->expires;
+ interval = it->incr;
+ if (val) {
struct task_cputime cputime;
- cputime_t t;
+ u64 t;
thread_group_cputimer(tsk, &cputime);
if (clock_id == CPUCLOCK_PROF)
@@ -63,17 +63,17 @@
/* CPUCLOCK_VIRT */
t = cputime.utime;
- if (cval < t)
+ if (val < t)
/* about to fire */
- cval = cputime_one_jiffy;
+ val = TICK_NSEC;
else
- cval = cval - t;
+ val -= t;
}
spin_unlock_irq(&tsk->sighand->siglock);
- cputime_to_timeval(cval, &value->it_value);
- cputime_to_timeval(cinterval, &value->it_interval);
+ value->it_value = ns_to_timeval(val);
+ value->it_interval = ns_to_timeval(interval);
}
int do_getitimer(int which, struct itimerval *value)
@@ -129,55 +129,35 @@
return HRTIMER_NORESTART;
}
-static inline u32 cputime_sub_ns(cputime_t ct, s64 real_ns)
-{
- struct timespec ts;
- s64 cpu_ns;
-
- cputime_to_timespec(ct, &ts);
- cpu_ns = timespec_to_ns(&ts);
-
- return (cpu_ns <= real_ns) ? 0 : cpu_ns - real_ns;
-}
-
static void set_cpu_itimer(struct task_struct *tsk, unsigned int clock_id,
const struct itimerval *const value,
struct itimerval *const ovalue)
{
- cputime_t cval, nval, cinterval, ninterval;
- s64 ns_ninterval, ns_nval;
- u32 error, incr_error;
+ u64 oval, nval, ointerval, ninterval;
struct cpu_itimer *it = &tsk->signal->it[clock_id];
- nval = timeval_to_cputime(&value->it_value);
- ns_nval = timeval_to_ns(&value->it_value);
- ninterval = timeval_to_cputime(&value->it_interval);
- ns_ninterval = timeval_to_ns(&value->it_interval);
-
- error = cputime_sub_ns(nval, ns_nval);
- incr_error = cputime_sub_ns(ninterval, ns_ninterval);
+ nval = timeval_to_ns(&value->it_value);
+ ninterval = timeval_to_ns(&value->it_interval);
spin_lock_irq(&tsk->sighand->siglock);
- cval = it->expires;
- cinterval = it->incr;
- if (cval || nval) {
+ oval = it->expires;
+ ointerval = it->incr;
+ if (oval || nval) {
if (nval > 0)
- nval += cputime_one_jiffy;
- set_process_cpu_timer(tsk, clock_id, &nval, &cval);
+ nval += TICK_NSEC;
+ set_process_cpu_timer(tsk, clock_id, &nval, &oval);
}
it->expires = nval;
it->incr = ninterval;
- it->error = error;
- it->incr_error = incr_error;
trace_itimer_state(clock_id == CPUCLOCK_VIRT ?
ITIMER_VIRTUAL : ITIMER_PROF, value, nval);
spin_unlock_irq(&tsk->sighand->siglock);
if (ovalue) {
- cputime_to_timeval(cval, &ovalue->it_value);
- cputime_to_timeval(cinterval, &ovalue->it_interval);
+ ovalue->it_value = ns_to_timeval(oval);
+ ovalue->it_interval = ns_to_timeval(ointerval);
}
}
diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c
index a4a0e47..7906b3f 100644
--- a/kernel/time/jiffies.c
+++ b/kernel/time/jiffies.c
@@ -27,19 +27,8 @@
#include "timekeeping.h"
-/* The Jiffies based clocksource is the lowest common
- * denominator clock source which should function on
- * all systems. It has the same coarse resolution as
- * the timer interrupt frequency HZ and it suffers
- * inaccuracies caused by missed or lost timer
- * interrupts and the inability for the timer
- * interrupt hardware to accuratly tick at the
- * requested HZ value. It is also not recommended
- * for "tick-less" systems.
- */
-#define NSEC_PER_JIFFY ((NSEC_PER_SEC+HZ/2)/HZ)
-/* Since jiffies uses a simple NSEC_PER_JIFFY multiplier
+/* Since jiffies uses a simple TICK_NSEC multiplier
* conversion, the .shift value could be zero. However
* this would make NTP adjustments impossible as they are
* in units of 1/2^.shift. Thus we use JIFFIES_SHIFT to
@@ -47,8 +36,8 @@
* amount, and give ntp adjustments in units of 1/2^8
*
* The value 8 is somewhat carefully chosen, as anything
- * larger can result in overflows. NSEC_PER_JIFFY grows as
- * HZ shrinks, so values greater than 8 overflow 32bits when
+ * larger can result in overflows. TICK_NSEC grows as HZ
+ * shrinks, so values greater than 8 overflow 32bits when
* HZ=100.
*/
#if HZ < 34
@@ -64,12 +53,23 @@
return (u64) jiffies;
}
+/*
+ * The Jiffies based clocksource is the lowest common
+ * denominator clock source which should function on
+ * all systems. It has the same coarse resolution as
+ * the timer interrupt frequency HZ and it suffers
+ * inaccuracies caused by missed or lost timer
+ * interrupts and the inability for the timer
+ * interrupt hardware to accuratly tick at the
+ * requested HZ value. It is also not recommended
+ * for "tick-less" systems.
+ */
static struct clocksource clocksource_jiffies = {
.name = "jiffies",
.rating = 1, /* lowest valid rating*/
.read = jiffies_read,
.mask = CLOCKSOURCE_MASK(32),
- .mult = NSEC_PER_JIFFY << JIFFIES_SHIFT, /* details above */
+ .mult = TICK_NSEC << JIFFIES_SHIFT, /* details above */
.shift = JIFFIES_SHIFT,
.max_cycles = 10,
};
@@ -125,7 +125,7 @@
shift_hz += cycles_per_tick/2;
do_div(shift_hz, cycles_per_tick);
/* Calculate nsec_per_tick using shift_hz */
- nsec_per_tick = (u64)NSEC_PER_SEC << 8;
+ nsec_per_tick = (u64)TICK_NSEC << 8;
nsec_per_tick += (u32)shift_hz/2;
do_div(nsec_per_tick, (u32)shift_hz);
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
index e9e8c10..b4377a5 100644
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -20,10 +20,10 @@
*/
void update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new)
{
- cputime_t cputime = secs_to_cputime(rlim_new);
+ u64 nsecs = rlim_new * NSEC_PER_SEC;
spin_lock_irq(&task->sighand->siglock);
- set_process_cpu_timer(task, CPUCLOCK_PROF, &cputime, NULL);
+ set_process_cpu_timer(task, CPUCLOCK_PROF, &nsecs, NULL);
spin_unlock_irq(&task->sighand->siglock);
}
@@ -50,39 +50,14 @@
return error;
}
-static inline unsigned long long
-timespec_to_sample(const clockid_t which_clock, const struct timespec *tp)
-{
- unsigned long long ret;
-
- ret = 0; /* high half always zero when .cpu used */
- if (CPUCLOCK_WHICH(which_clock) == CPUCLOCK_SCHED) {
- ret = (unsigned long long)tp->tv_sec * NSEC_PER_SEC + tp->tv_nsec;
- } else {
- ret = cputime_to_expires(timespec_to_cputime(tp));
- }
- return ret;
-}
-
-static void sample_to_timespec(const clockid_t which_clock,
- unsigned long long expires,
- struct timespec *tp)
-{
- if (CPUCLOCK_WHICH(which_clock) == CPUCLOCK_SCHED)
- *tp = ns_to_timespec(expires);
- else
- cputime_to_timespec((__force cputime_t)expires, tp);
-}
-
/*
* Update expiry time from increment, and increase overrun count,
* given the current clock sample.
*/
-static void bump_cpu_timer(struct k_itimer *timer,
- unsigned long long now)
+static void bump_cpu_timer(struct k_itimer *timer, u64 now)
{
int i;
- unsigned long long delta, incr;
+ u64 delta, incr;
if (timer->it.cpu.incr == 0)
return;
@@ -122,21 +97,21 @@
return 0;
}
-static inline unsigned long long prof_ticks(struct task_struct *p)
+static inline u64 prof_ticks(struct task_struct *p)
{
- cputime_t utime, stime;
+ u64 utime, stime;
task_cputime(p, &utime, &stime);
- return cputime_to_expires(utime + stime);
+ return utime + stime;
}
-static inline unsigned long long virt_ticks(struct task_struct *p)
+static inline u64 virt_ticks(struct task_struct *p)
{
- cputime_t utime, stime;
+ u64 utime, stime;
task_cputime(p, &utime, &stime);
- return cputime_to_expires(utime);
+ return utime;
}
static int
@@ -176,8 +151,8 @@
/*
* Sample a per-thread clock for the given task.
*/
-static int cpu_clock_sample(const clockid_t which_clock, struct task_struct *p,
- unsigned long long *sample)
+static int cpu_clock_sample(const clockid_t which_clock,
+ struct task_struct *p, u64 *sample)
{
switch (CPUCLOCK_WHICH(which_clock)) {
default:
@@ -260,7 +235,7 @@
*/
static int cpu_clock_sample_group(const clockid_t which_clock,
struct task_struct *p,
- unsigned long long *sample)
+ u64 *sample)
{
struct task_cputime cputime;
@@ -269,11 +244,11 @@
return -EINVAL;
case CPUCLOCK_PROF:
thread_group_cputime(p, &cputime);
- *sample = cputime_to_expires(cputime.utime + cputime.stime);
+ *sample = cputime.utime + cputime.stime;
break;
case CPUCLOCK_VIRT:
thread_group_cputime(p, &cputime);
- *sample = cputime_to_expires(cputime.utime);
+ *sample = cputime.utime;
break;
case CPUCLOCK_SCHED:
thread_group_cputime(p, &cputime);
@@ -288,7 +263,7 @@
struct timespec *tp)
{
int err = -EINVAL;
- unsigned long long rtn;
+ u64 rtn;
if (CPUCLOCK_PERTHREAD(which_clock)) {
if (same_thread_group(tsk, current))
@@ -299,7 +274,7 @@
}
if (!err)
- sample_to_timespec(which_clock, rtn, tp);
+ *tp = ns_to_timespec(rtn);
return err;
}
@@ -453,7 +428,7 @@
cleanup_timers(tsk->signal->cpu_timers);
}
-static inline int expires_gt(cputime_t expires, cputime_t new_exp)
+static inline int expires_gt(u64 expires, u64 new_exp)
{
return expires == 0 || expires > new_exp;
}
@@ -488,7 +463,7 @@
list_add(&nt->entry, listpos);
if (listpos == head) {
- unsigned long long exp = nt->expires;
+ u64 exp = nt->expires;
/*
* We are the new earliest-expiring POSIX 1.b timer, hence
@@ -499,16 +474,15 @@
switch (CPUCLOCK_WHICH(timer->it_clock)) {
case CPUCLOCK_PROF:
- if (expires_gt(cputime_expires->prof_exp, expires_to_cputime(exp)))
- cputime_expires->prof_exp = expires_to_cputime(exp);
+ if (expires_gt(cputime_expires->prof_exp, exp))
+ cputime_expires->prof_exp = exp;
break;
case CPUCLOCK_VIRT:
- if (expires_gt(cputime_expires->virt_exp, expires_to_cputime(exp)))
- cputime_expires->virt_exp = expires_to_cputime(exp);
+ if (expires_gt(cputime_expires->virt_exp, exp))
+ cputime_expires->virt_exp = exp;
break;
case CPUCLOCK_SCHED:
- if (cputime_expires->sched_exp == 0 ||
- cputime_expires->sched_exp > exp)
+ if (expires_gt(cputime_expires->sched_exp, exp))
cputime_expires->sched_exp = exp;
break;
}
@@ -559,8 +533,7 @@
* traversal.
*/
static int cpu_timer_sample_group(const clockid_t which_clock,
- struct task_struct *p,
- unsigned long long *sample)
+ struct task_struct *p, u64 *sample)
{
struct task_cputime cputime;
@@ -569,10 +542,10 @@
default:
return -EINVAL;
case CPUCLOCK_PROF:
- *sample = cputime_to_expires(cputime.utime + cputime.stime);
+ *sample = cputime.utime + cputime.stime;
break;
case CPUCLOCK_VIRT:
- *sample = cputime_to_expires(cputime.utime);
+ *sample = cputime.utime;
break;
case CPUCLOCK_SCHED:
*sample = cputime.sum_exec_runtime;
@@ -593,12 +566,12 @@
unsigned long flags;
struct sighand_struct *sighand;
struct task_struct *p = timer->it.cpu.task;
- unsigned long long old_expires, new_expires, old_incr, val;
+ u64 old_expires, new_expires, old_incr, val;
int ret;
WARN_ON_ONCE(p == NULL);
- new_expires = timespec_to_sample(timer->it_clock, &new->it_value);
+ new_expires = timespec_to_ns(&new->it_value);
/*
* Protect against sighand release/switch in exit/exec and p->cpu_timers
@@ -659,9 +632,7 @@
bump_cpu_timer(timer, val);
if (val < timer->it.cpu.expires) {
old_expires = timer->it.cpu.expires - val;
- sample_to_timespec(timer->it_clock,
- old_expires,
- &old->it_value);
+ old->it_value = ns_to_timespec(old_expires);
} else {
old->it_value.tv_nsec = 1;
old->it_value.tv_sec = 0;
@@ -699,8 +670,7 @@
* Install the new reload setting, and
* set up the signal and overrun bookkeeping.
*/
- timer->it.cpu.incr = timespec_to_sample(timer->it_clock,
- &new->it_interval);
+ timer->it.cpu.incr = timespec_to_ns(&new->it_interval);
/*
* This acts as a modification timestamp for the timer,
@@ -723,17 +693,15 @@
ret = 0;
out:
- if (old) {
- sample_to_timespec(timer->it_clock,
- old_incr, &old->it_interval);
- }
+ if (old)
+ old->it_interval = ns_to_timespec(old_incr);
return ret;
}
static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp)
{
- unsigned long long now;
+ u64 now;
struct task_struct *p = timer->it.cpu.task;
WARN_ON_ONCE(p == NULL);
@@ -741,8 +709,7 @@
/*
* Easy part: convert the reload time.
*/
- sample_to_timespec(timer->it_clock,
- timer->it.cpu.incr, &itp->it_interval);
+ itp->it_interval = ns_to_timespec(timer->it.cpu.incr);
if (timer->it.cpu.expires == 0) { /* Timer not armed at all. */
itp->it_value.tv_sec = itp->it_value.tv_nsec = 0;
@@ -771,8 +738,7 @@
* Call the timer disarmed, nothing else to do.
*/
timer->it.cpu.expires = 0;
- sample_to_timespec(timer->it_clock, timer->it.cpu.expires,
- &itp->it_value);
+ itp->it_value = ns_to_timespec(timer->it.cpu.expires);
return;
} else {
cpu_timer_sample_group(timer->it_clock, p, &now);
@@ -781,9 +747,7 @@
}
if (now < timer->it.cpu.expires) {
- sample_to_timespec(timer->it_clock,
- timer->it.cpu.expires - now,
- &itp->it_value);
+ itp->it_value = ns_to_timespec(timer->it.cpu.expires - now);
} else {
/*
* The timer should have expired already, but the firing
@@ -827,7 +791,7 @@
struct list_head *timers = tsk->cpu_timers;
struct signal_struct *const sig = tsk->signal;
struct task_cputime *tsk_expires = &tsk->cputime_expires;
- unsigned long long expires;
+ u64 expires;
unsigned long soft;
/*
@@ -838,10 +802,10 @@
return;
expires = check_timers_list(timers, firing, prof_ticks(tsk));
- tsk_expires->prof_exp = expires_to_cputime(expires);
+ tsk_expires->prof_exp = expires;
expires = check_timers_list(++timers, firing, virt_ticks(tsk));
- tsk_expires->virt_exp = expires_to_cputime(expires);
+ tsk_expires->virt_exp = expires;
tsk_expires->sched_exp = check_timers_list(++timers, firing,
tsk->se.sum_exec_runtime);
@@ -890,26 +854,17 @@
tick_dep_clear_signal(sig, TICK_DEP_BIT_POSIX_TIMER);
}
-static u32 onecputick;
-
static void check_cpu_itimer(struct task_struct *tsk, struct cpu_itimer *it,
- unsigned long long *expires,
- unsigned long long cur_time, int signo)
+ u64 *expires, u64 cur_time, int signo)
{
if (!it->expires)
return;
if (cur_time >= it->expires) {
- if (it->incr) {
+ if (it->incr)
it->expires += it->incr;
- it->error += it->incr_error;
- if (it->error >= onecputick) {
- it->expires -= cputime_one_jiffy;
- it->error -= onecputick;
- }
- } else {
+ else
it->expires = 0;
- }
trace_itimer_expire(signo == SIGPROF ?
ITIMER_PROF : ITIMER_VIRTUAL,
@@ -917,9 +872,8 @@
__group_send_sig_info(signo, SEND_SIG_PRIV, tsk);
}
- if (it->expires && (!*expires || it->expires < *expires)) {
+ if (it->expires && (!*expires || it->expires < *expires))
*expires = it->expires;
- }
}
/*
@@ -931,8 +885,8 @@
struct list_head *firing)
{
struct signal_struct *const sig = tsk->signal;
- unsigned long long utime, ptime, virt_expires, prof_expires;
- unsigned long long sum_sched_runtime, sched_expires;
+ u64 utime, ptime, virt_expires, prof_expires;
+ u64 sum_sched_runtime, sched_expires;
struct list_head *timers = sig->cpu_timers;
struct task_cputime cputime;
unsigned long soft;
@@ -954,8 +908,8 @@
* Collect the current process totals.
*/
thread_group_cputimer(tsk, &cputime);
- utime = cputime_to_expires(cputime.utime);
- ptime = utime + cputime_to_expires(cputime.stime);
+ utime = cputime.utime;
+ ptime = utime + cputime.stime;
sum_sched_runtime = cputime.sum_exec_runtime;
prof_expires = check_timers_list(timers, firing, ptime);
@@ -971,10 +925,10 @@
SIGVTALRM);
soft = READ_ONCE(sig->rlim[RLIMIT_CPU].rlim_cur);
if (soft != RLIM_INFINITY) {
- unsigned long psecs = cputime_to_secs(ptime);
+ unsigned long psecs = div_u64(ptime, NSEC_PER_SEC);
unsigned long hard =
READ_ONCE(sig->rlim[RLIMIT_CPU].rlim_max);
- cputime_t x;
+ u64 x;
if (psecs >= hard) {
/*
* At the hard limit, we just die.
@@ -993,14 +947,13 @@
sig->rlim[RLIMIT_CPU].rlim_cur = soft;
}
}
- x = secs_to_cputime(soft);
- if (!prof_expires || x < prof_expires) {
+ x = soft * NSEC_PER_SEC;
+ if (!prof_expires || x < prof_expires)
prof_expires = x;
- }
}
- sig->cputime_expires.prof_exp = expires_to_cputime(prof_expires);
- sig->cputime_expires.virt_exp = expires_to_cputime(virt_expires);
+ sig->cputime_expires.prof_exp = prof_expires;
+ sig->cputime_expires.virt_exp = virt_expires;
sig->cputime_expires.sched_exp = sched_expires;
if (task_cputime_zero(&sig->cputime_expires))
stop_process_timers(sig);
@@ -1017,7 +970,7 @@
struct sighand_struct *sighand;
unsigned long flags;
struct task_struct *p = timer->it.cpu.task;
- unsigned long long now;
+ u64 now;
WARN_ON_ONCE(p == NULL);
@@ -1214,9 +1167,9 @@
* The tsk->sighand->siglock must be held by the caller.
*/
void set_process_cpu_timer(struct task_struct *tsk, unsigned int clock_idx,
- cputime_t *newval, cputime_t *oldval)
+ u64 *newval, u64 *oldval)
{
- unsigned long long now;
+ u64 now;
WARN_ON_ONCE(clock_idx == CPUCLOCK_SCHED);
cpu_timer_sample_group(clock_idx, tsk, &now);
@@ -1230,7 +1183,7 @@
if (*oldval) {
if (*oldval <= now) {
/* Just about to fire. */
- *oldval = cputime_one_jiffy;
+ *oldval = TICK_NSEC;
} else {
*oldval -= now;
}
@@ -1310,7 +1263,7 @@
/*
* We were interrupted by a signal.
*/
- sample_to_timespec(which_clock, timer.it.cpu.expires, rqtp);
+ *rqtp = ns_to_timespec(timer.it.cpu.expires);
error = posix_cpu_timer_set(&timer, 0, &zero_it, it);
if (!error) {
/*
@@ -1476,15 +1429,10 @@
.clock_get = thread_cpu_clock_get,
.timer_create = thread_cpu_timer_create,
};
- struct timespec ts;
posix_timers_register_clock(CLOCK_PROCESS_CPUTIME_ID, &process);
posix_timers_register_clock(CLOCK_THREAD_CPUTIME_ID, &thread);
- cputime_to_timespec(cputime_one_jiffy, &ts);
- onecputick = ts.tv_nsec;
- WARN_ON(ts.tv_sec != 0);
-
return 0;
}
__initcall(init_posix_cpu_timers);
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c
index 3109204..987e496 100644
--- a/kernel/time/tick-broadcast.c
+++ b/kernel/time/tick-broadcast.c
@@ -29,12 +29,13 @@
*/
static struct tick_device tick_broadcast_device;
-static cpumask_var_t tick_broadcast_mask;
-static cpumask_var_t tick_broadcast_on;
-static cpumask_var_t tmpmask;
-static DEFINE_RAW_SPINLOCK(tick_broadcast_lock);
+static cpumask_var_t tick_broadcast_mask __cpumask_var_read_mostly;
+static cpumask_var_t tick_broadcast_on __cpumask_var_read_mostly;
+static cpumask_var_t tmpmask __cpumask_var_read_mostly;
static int tick_broadcast_forced;
+static __cacheline_aligned_in_smp DEFINE_RAW_SPINLOCK(tick_broadcast_lock);
+
#ifdef CONFIG_TICK_ONESHOT
static void tick_broadcast_clear_oneshot(int cpu);
static void tick_resume_broadcast_oneshot(struct clock_event_device *bc);
@@ -347,17 +348,16 @@
*
* Called when the system enters a state where affected tick devices
* might stop. Note: TICK_BROADCAST_FORCE cannot be undone.
- *
- * Called with interrupts disabled, so clockevents_lock is not
- * required here because the local clock event device cannot go away
- * under us.
*/
void tick_broadcast_control(enum tick_broadcast_mode mode)
{
struct clock_event_device *bc, *dev;
struct tick_device *td;
int cpu, bc_stopped;
+ unsigned long flags;
+ /* Protects also the local clockevent device. */
+ raw_spin_lock_irqsave(&tick_broadcast_lock, flags);
td = this_cpu_ptr(&tick_cpu_device);
dev = td->evtdev;
@@ -365,12 +365,11 @@
* Is the device not affected by the powerstate ?
*/
if (!dev || !(dev->features & CLOCK_EVT_FEAT_C3STOP))
- return;
+ goto out;
if (!tick_device_is_functional(dev))
- return;
+ goto out;
- raw_spin_lock(&tick_broadcast_lock);
cpu = smp_processor_id();
bc = tick_broadcast_device.evtdev;
bc_stopped = cpumask_empty(tick_broadcast_mask);
@@ -420,7 +419,8 @@
tick_broadcast_setup_oneshot(bc);
}
}
- raw_spin_unlock(&tick_broadcast_lock);
+out:
+ raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags);
}
EXPORT_SYMBOL_GPL(tick_broadcast_control);
@@ -517,9 +517,9 @@
#ifdef CONFIG_TICK_ONESHOT
-static cpumask_var_t tick_broadcast_oneshot_mask;
-static cpumask_var_t tick_broadcast_pending_mask;
-static cpumask_var_t tick_broadcast_force_mask;
+static cpumask_var_t tick_broadcast_oneshot_mask __cpumask_var_read_mostly;
+static cpumask_var_t tick_broadcast_pending_mask __cpumask_var_read_mostly;
+static cpumask_var_t tick_broadcast_force_mask __cpumask_var_read_mostly;
/*
* Exposed for debugging: see timer_list.c
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index fc6f740..2c115fd 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -725,11 +725,6 @@
*/
if (delta == 0) {
tick_nohz_restart(ts, now);
- /*
- * Make sure next tick stop doesn't get fooled by past
- * clock deadline
- */
- ts->next_tick = 0;
goto out;
}
}
@@ -772,7 +767,7 @@
tick = expires;
/* Skip reprogram of event if its not changed */
- if (ts->tick_stopped && (expires == ts->next_tick))
+ if (ts->tick_stopped && (expires == dev->next_event))
goto out;
/*
@@ -792,8 +787,6 @@
trace_tick_stop(1, TICK_DEP_MASK_NONE);
}
- ts->next_tick = tick;
-
/*
* If the expiration time == KTIME_MAX, then we simply stop
* the tick timer.
@@ -809,10 +802,7 @@
else
tick_program_event(tick, 1);
out:
- /*
- * Update the estimated sleep length until the next timer
- * (not only the tick).
- */
+ /* Update the estimated sleep length */
ts->sleep_length = ktime_sub(dev->next_event, now);
return tick;
}
diff --git a/kernel/time/tick-sched.h b/kernel/time/tick-sched.h
index 075444e..bf38226 100644
--- a/kernel/time/tick-sched.h
+++ b/kernel/time/tick-sched.h
@@ -27,7 +27,6 @@
* timer is modified for nohz sleeps. This is necessary
* to resume the tick timer operation in the timeline
* when the CPU returns from nohz sleep.
- * @next_tick: Next tick to be fired when in dynticks mode.
* @tick_stopped: Indicator that the idle tick has been stopped
* @idle_jiffies: jiffies at the entry to idle for idle time accounting
* @idle_calls: Total number of idle calls
@@ -45,7 +44,6 @@
unsigned long check_clocks;
enum tick_nohz_mode nohz_mode;
ktime_t last_tick;
- ktime_t next_tick;
int inidle;
int tick_stopped;
unsigned long idle_jiffies;
diff --git a/kernel/time/time.c b/kernel/time/time.c
index a3a9a8a..25bdd25 100644
--- a/kernel/time/time.c
+++ b/kernel/time/time.c
@@ -702,6 +702,16 @@
#endif
}
+u64 jiffies64_to_nsecs(u64 j)
+{
+#if !(NSEC_PER_SEC % HZ)
+ return (NSEC_PER_SEC / HZ) * j;
+# else
+ return div_u64(j * HZ_TO_NSEC_NUM, HZ_TO_NSEC_DEN);
+#endif
+}
+EXPORT_SYMBOL(jiffies64_to_nsecs);
+
/**
* nsecs_to_jiffies64 - Convert nsecs in u64 to jiffies64
*
diff --git a/kernel/time/timeconst.bc b/kernel/time/timeconst.bc
index c486889..f83bbb8 100644
--- a/kernel/time/timeconst.bc
+++ b/kernel/time/timeconst.bc
@@ -98,6 +98,12 @@
print "#define HZ_TO_USEC_DEN\t\t", hz/cd, "\n"
print "#define USEC_TO_HZ_NUM\t\t", hz/cd, "\n"
print "#define USEC_TO_HZ_DEN\t\t", 1000000/cd, "\n"
+
+ cd=gcd(hz,1000000000)
+ print "#define HZ_TO_NSEC_NUM\t\t", 1000000000/cd, "\n"
+ print "#define HZ_TO_NSEC_DEN\t\t", hz/cd, "\n"
+ print "#define NSEC_TO_HZ_NUM\t\t", hz/cd, "\n"
+ print "#define NSEC_TO_HZ_DEN\t\t", 1000000000/cd, "\n"
print "\n"
print "#endif /* KERNEL_TIMECONST_H */\n"
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index db087d7e1..95b258d 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1275,27 +1275,8 @@
}
EXPORT_SYMBOL(timekeeping_inject_offset);
-
/**
- * timekeeping_get_tai_offset - Returns current TAI offset from UTC
- *
- */
-s32 timekeeping_get_tai_offset(void)
-{
- struct timekeeper *tk = &tk_core.timekeeper;
- unsigned int seq;
- s32 ret;
-
- do {
- seq = read_seqcount_begin(&tk_core.seq);
- ret = tk->tai_offset;
- } while (read_seqcount_retry(&tk_core.seq, seq));
-
- return ret;
-}
-
-/**
- * __timekeeping_set_tai_offset - Lock free worker function
+ * __timekeeping_set_tai_offset - Sets the TAI offset from UTC and monotonic
*
*/
static void __timekeeping_set_tai_offset(struct timekeeper *tk, s32 tai_offset)
@@ -1305,24 +1286,6 @@
}
/**
- * timekeeping_set_tai_offset - Sets the current TAI offset from UTC
- *
- */
-void timekeeping_set_tai_offset(s32 tai_offset)
-{
- struct timekeeper *tk = &tk_core.timekeeper;
- unsigned long flags;
-
- raw_spin_lock_irqsave(&timekeeper_lock, flags);
- write_seqcount_begin(&tk_core.seq);
- __timekeeping_set_tai_offset(tk, tai_offset);
- timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET);
- write_seqcount_end(&tk_core.seq);
- raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
- clock_was_set();
-}
-
-/**
* change_clocksource - Swaps clocksources if a new one is available
*
* Accumulates current time interval and initializes new clocksource
diff --git a/kernel/time/timekeeping.h b/kernel/time/timekeeping.h
index 704f595..d091467 100644
--- a/kernel/time/timekeeping.h
+++ b/kernel/time/timekeeping.h
@@ -11,8 +11,6 @@
extern int timekeeping_valid_for_hres(void);
extern u64 timekeeping_max_deferment(void);
extern int timekeeping_inject_offset(struct timespec *ts);
-extern s32 timekeeping_get_tai_offset(void);
-extern void timekeeping_set_tai_offset(s32 tai_offset);
extern int timekeeping_suspend(void);
extern void timekeeping_resume(void);
diff --git a/kernel/time/timekeeping_debug.c b/kernel/time/timekeeping_debug.c
index ca9fb80..38bc4d2 100644
--- a/kernel/time/timekeeping_debug.c
+++ b/kernel/time/timekeeping_debug.c
@@ -75,7 +75,7 @@
int bin = min(fls(t->tv_sec), NUM_BINS-1);
sleep_time_bin[bin]++;
- pr_info("Suspended for %lld.%03lu seconds\n", (s64)t->tv_sec,
- t->tv_nsec / NSEC_PER_MSEC);
+ printk_deferred(KERN_INFO "Suspended for %lld.%03lu seconds\n",
+ (s64)t->tv_sec, t->tv_nsec / NSEC_PER_MSEC);
}
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index ec33a69..82a6bfa 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -571,38 +571,6 @@
trigger_dyntick_cpu(base, timer);
}
-#ifdef CONFIG_TIMER_STATS
-void __timer_stats_timer_set_start_info(struct timer_list *timer, void *addr)
-{
- if (timer->start_site)
- return;
-
- timer->start_site = addr;
- memcpy(timer->start_comm, current->comm, TASK_COMM_LEN);
- timer->start_pid = current->pid;
-}
-
-static void timer_stats_account_timer(struct timer_list *timer)
-{
- void *site;
-
- /*
- * start_site can be concurrently reset by
- * timer_stats_timer_clear_start_info()
- */
- site = READ_ONCE(timer->start_site);
- if (likely(!site))
- return;
-
- timer_stats_update_stats(timer, timer->start_pid, site,
- timer->function, timer->start_comm,
- timer->flags);
-}
-
-#else
-static void timer_stats_account_timer(struct timer_list *timer) {}
-#endif
-
#ifdef CONFIG_DEBUG_OBJECTS_TIMERS
static struct debug_obj_descr timer_debug_descr;
@@ -789,11 +757,6 @@
{
timer->entry.pprev = NULL;
timer->flags = flags | raw_smp_processor_id();
-#ifdef CONFIG_TIMER_STATS
- timer->start_site = NULL;
- timer->start_pid = -1;
- memset(timer->start_comm, 0, TASK_COMM_LEN);
-#endif
lockdep_init_map(&timer->lockdep_map, name, key, 0);
}
@@ -1001,8 +964,6 @@
base = lock_timer_base(timer, &flags);
}
- timer_stats_timer_set_start_info(timer);
-
ret = detach_if_pending(timer, base, false);
if (!ret && pending_only)
goto out_unlock;
@@ -1130,7 +1091,6 @@
struct timer_base *new_base, *base;
unsigned long flags;
- timer_stats_timer_set_start_info(timer);
BUG_ON(timer_pending(timer) || !timer->function);
new_base = get_timer_cpu_base(timer->flags, cpu);
@@ -1176,7 +1136,6 @@
debug_assert_init(timer);
- timer_stats_timer_clear_start_info(timer);
if (timer_pending(timer)) {
base = lock_timer_base(timer, &flags);
ret = detach_if_pending(timer, base, true);
@@ -1204,10 +1163,9 @@
base = lock_timer_base(timer, &flags);
- if (base->running_timer != timer) {
- timer_stats_timer_clear_start_info(timer);
+ if (base->running_timer != timer)
ret = detach_if_pending(timer, base, true);
- }
+
spin_unlock_irqrestore(&base->lock, flags);
return ret;
@@ -1331,7 +1289,6 @@
unsigned long data;
timer = hlist_entry(head->first, struct timer_list, entry);
- timer_stats_account_timer(timer);
base->running_timer = timer;
detach_timer(timer, true);
@@ -1868,7 +1825,6 @@
void __init init_timers(void)
{
init_timer_cpus();
- init_timer_stats();
open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
}
diff --git a/kernel/time/timer_list.c b/kernel/time/timer_list.c
index afe6cd1..ff8d5c1 100644
--- a/kernel/time/timer_list.c
+++ b/kernel/time/timer_list.c
@@ -62,21 +62,11 @@
print_timer(struct seq_file *m, struct hrtimer *taddr, struct hrtimer *timer,
int idx, u64 now)
{
-#ifdef CONFIG_TIMER_STATS
- char tmp[TASK_COMM_LEN + 1];
-#endif
SEQ_printf(m, " #%d: ", idx);
print_name_offset(m, taddr);
SEQ_printf(m, ", ");
print_name_offset(m, timer->function);
SEQ_printf(m, ", S:%02x", timer->state);
-#ifdef CONFIG_TIMER_STATS
- SEQ_printf(m, ", ");
- print_name_offset(m, timer->start_site);
- memcpy(tmp, timer->start_comm, TASK_COMM_LEN);
- tmp[TASK_COMM_LEN] = 0;
- SEQ_printf(m, ", %s/%d", tmp, timer->start_pid);
-#endif
SEQ_printf(m, "\n");
SEQ_printf(m, " # expires at %Lu-%Lu nsecs [in %Ld to %Ld nsecs]\n",
(unsigned long long)ktime_to_ns(hrtimer_get_softexpires(timer)),
@@ -127,7 +117,7 @@
SEQ_printf(m, " .base: %pK\n", base);
SEQ_printf(m, " .index: %d\n", base->index);
- SEQ_printf(m, " .resolution: %u nsecs\n", (unsigned) hrtimer_resolution);
+ SEQ_printf(m, " .resolution: %u nsecs\n", hrtimer_resolution);
SEQ_printf(m, " .get_time: ");
print_name_offset(m, base->get_time);
diff --git a/kernel/time/timer_stats.c b/kernel/time/timer_stats.c
deleted file mode 100644
index afddded..0000000
--- a/kernel/time/timer_stats.c
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * kernel/time/timer_stats.c
- *
- * Collect timer usage statistics.
- *
- * Copyright(C) 2006, Red Hat, Inc., Ingo Molnar
- * Copyright(C) 2006 Timesys Corp., Thomas Gleixner <tglx@timesys.com>
- *
- * timer_stats is based on timer_top, a similar functionality which was part of
- * Con Kolivas dyntick patch set. It was developed by Daniel Petrini at the
- * Instituto Nokia de Tecnologia - INdT - Manaus. timer_top's design was based
- * on dynamic allocation of the statistics entries and linear search based
- * lookup combined with a global lock, rather than the static array, hash
- * and per-CPU locking which is used by timer_stats. It was written for the
- * pre hrtimer kernel code and therefore did not take hrtimers into account.
- * Nevertheless it provided the base for the timer_stats implementation and
- * was a helpful source of inspiration. Kudos to Daniel and the Nokia folks
- * for this effort.
- *
- * timer_top.c is
- * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus
- * Written by Daniel Petrini <d.pensator@gmail.com>
- * timer_top.c was released under the GNU General Public License version 2
- *
- * We export the addresses and counting of timer functions being called,
- * the pid and cmdline from the owner process if applicable.
- *
- * Start/stop data collection:
- * # echo [1|0] >/proc/timer_stats
- *
- * Display the information collected so far:
- * # cat /proc/timer_stats
- *
- * 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/proc_fs.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/sched.h>
-#include <linux/seq_file.h>
-#include <linux/kallsyms.h>
-
-#include <linux/uaccess.h>
-
-/*
- * This is our basic unit of interest: a timer expiry event identified
- * by the timer, its start/expire functions and the PID of the task that
- * started the timer. We count the number of times an event happens:
- */
-struct entry {
- /*
- * Hash list:
- */
- struct entry *next;
-
- /*
- * Hash keys:
- */
- void *timer;
- void *start_func;
- void *expire_func;
- pid_t pid;
-
- /*
- * Number of timeout events:
- */
- unsigned long count;
- u32 flags;
-
- /*
- * We save the command-line string to preserve
- * this information past task exit:
- */
- char comm[TASK_COMM_LEN + 1];
-
-} ____cacheline_aligned_in_smp;
-
-/*
- * Spinlock protecting the tables - not taken during lookup:
- */
-static DEFINE_RAW_SPINLOCK(table_lock);
-
-/*
- * Per-CPU lookup locks for fast hash lookup:
- */
-static DEFINE_PER_CPU(raw_spinlock_t, tstats_lookup_lock);
-
-/*
- * Mutex to serialize state changes with show-stats activities:
- */
-static DEFINE_MUTEX(show_mutex);
-
-/*
- * Collection status, active/inactive:
- */
-int __read_mostly timer_stats_active;
-
-/*
- * Beginning/end timestamps of measurement:
- */
-static ktime_t time_start, time_stop;
-
-/*
- * tstat entry structs only get allocated while collection is
- * active and never freed during that time - this simplifies
- * things quite a bit.
- *
- * They get freed when a new collection period is started.
- */
-#define MAX_ENTRIES_BITS 10
-#define MAX_ENTRIES (1UL << MAX_ENTRIES_BITS)
-
-static unsigned long nr_entries;
-static struct entry entries[MAX_ENTRIES];
-
-static atomic_t overflow_count;
-
-/*
- * The entries are in a hash-table, for fast lookup:
- */
-#define TSTAT_HASH_BITS (MAX_ENTRIES_BITS - 1)
-#define TSTAT_HASH_SIZE (1UL << TSTAT_HASH_BITS)
-#define TSTAT_HASH_MASK (TSTAT_HASH_SIZE - 1)
-
-#define __tstat_hashfn(entry) \
- (((unsigned long)(entry)->timer ^ \
- (unsigned long)(entry)->start_func ^ \
- (unsigned long)(entry)->expire_func ^ \
- (unsigned long)(entry)->pid ) & TSTAT_HASH_MASK)
-
-#define tstat_hashentry(entry) (tstat_hash_table + __tstat_hashfn(entry))
-
-static struct entry *tstat_hash_table[TSTAT_HASH_SIZE] __read_mostly;
-
-static void reset_entries(void)
-{
- nr_entries = 0;
- memset(entries, 0, sizeof(entries));
- memset(tstat_hash_table, 0, sizeof(tstat_hash_table));
- atomic_set(&overflow_count, 0);
-}
-
-static struct entry *alloc_entry(void)
-{
- if (nr_entries >= MAX_ENTRIES)
- return NULL;
-
- return entries + nr_entries++;
-}
-
-static int match_entries(struct entry *entry1, struct entry *entry2)
-{
- return entry1->timer == entry2->timer &&
- entry1->start_func == entry2->start_func &&
- entry1->expire_func == entry2->expire_func &&
- entry1->pid == entry2->pid;
-}
-
-/*
- * Look up whether an entry matching this item is present
- * in the hash already. Must be called with irqs off and the
- * lookup lock held:
- */
-static struct entry *tstat_lookup(struct entry *entry, char *comm)
-{
- struct entry **head, *curr, *prev;
-
- head = tstat_hashentry(entry);
- curr = *head;
-
- /*
- * The fastpath is when the entry is already hashed,
- * we do this with the lookup lock held, but with the
- * table lock not held:
- */
- while (curr) {
- if (match_entries(curr, entry))
- return curr;
-
- curr = curr->next;
- }
- /*
- * Slowpath: allocate, set up and link a new hash entry:
- */
- prev = NULL;
- curr = *head;
-
- raw_spin_lock(&table_lock);
- /*
- * Make sure we have not raced with another CPU:
- */
- while (curr) {
- if (match_entries(curr, entry))
- goto out_unlock;
-
- prev = curr;
- curr = curr->next;
- }
-
- curr = alloc_entry();
- if (curr) {
- *curr = *entry;
- curr->count = 0;
- curr->next = NULL;
- memcpy(curr->comm, comm, TASK_COMM_LEN);
-
- smp_mb(); /* Ensure that curr is initialized before insert */
-
- if (prev)
- prev->next = curr;
- else
- *head = curr;
- }
- out_unlock:
- raw_spin_unlock(&table_lock);
-
- return curr;
-}
-
-/**
- * timer_stats_update_stats - Update the statistics for a timer.
- * @timer: pointer to either a timer_list or a hrtimer
- * @pid: the pid of the task which set up the timer
- * @startf: pointer to the function which did the timer setup
- * @timerf: pointer to the timer callback function of the timer
- * @comm: name of the process which set up the timer
- * @tflags: The flags field of the timer
- *
- * When the timer is already registered, then the event counter is
- * incremented. Otherwise the timer is registered in a free slot.
- */
-void timer_stats_update_stats(void *timer, pid_t pid, void *startf,
- void *timerf, char *comm, u32 tflags)
-{
- /*
- * It doesn't matter which lock we take:
- */
- raw_spinlock_t *lock;
- struct entry *entry, input;
- unsigned long flags;
-
- if (likely(!timer_stats_active))
- return;
-
- lock = &per_cpu(tstats_lookup_lock, raw_smp_processor_id());
-
- input.timer = timer;
- input.start_func = startf;
- input.expire_func = timerf;
- input.pid = pid;
- input.flags = tflags;
-
- raw_spin_lock_irqsave(lock, flags);
- if (!timer_stats_active)
- goto out_unlock;
-
- entry = tstat_lookup(&input, comm);
- if (likely(entry))
- entry->count++;
- else
- atomic_inc(&overflow_count);
-
- out_unlock:
- raw_spin_unlock_irqrestore(lock, flags);
-}
-
-static void print_name_offset(struct seq_file *m, unsigned long addr)
-{
- char symname[KSYM_NAME_LEN];
-
- if (lookup_symbol_name(addr, symname) < 0)
- seq_printf(m, "<%p>", (void *)addr);
- else
- seq_printf(m, "%s", symname);
-}
-
-static int tstats_show(struct seq_file *m, void *v)
-{
- struct timespec64 period;
- struct entry *entry;
- unsigned long ms;
- long events = 0;
- ktime_t time;
- int i;
-
- mutex_lock(&show_mutex);
- /*
- * If still active then calculate up to now:
- */
- if (timer_stats_active)
- time_stop = ktime_get();
-
- time = ktime_sub(time_stop, time_start);
-
- period = ktime_to_timespec64(time);
- ms = period.tv_nsec / 1000000;
-
- seq_puts(m, "Timer Stats Version: v0.3\n");
- seq_printf(m, "Sample period: %ld.%03ld s\n", (long)period.tv_sec, ms);
- if (atomic_read(&overflow_count))
- seq_printf(m, "Overflow: %d entries\n", atomic_read(&overflow_count));
- seq_printf(m, "Collection: %s\n", timer_stats_active ? "active" : "inactive");
-
- for (i = 0; i < nr_entries; i++) {
- entry = entries + i;
- if (entry->flags & TIMER_DEFERRABLE) {
- seq_printf(m, "%4luD, %5d %-16s ",
- entry->count, entry->pid, entry->comm);
- } else {
- seq_printf(m, " %4lu, %5d %-16s ",
- entry->count, entry->pid, entry->comm);
- }
-
- print_name_offset(m, (unsigned long)entry->start_func);
- seq_puts(m, " (");
- print_name_offset(m, (unsigned long)entry->expire_func);
- seq_puts(m, ")\n");
-
- events += entry->count;
- }
-
- ms += period.tv_sec * 1000;
- if (!ms)
- ms = 1;
-
- if (events && period.tv_sec)
- seq_printf(m, "%ld total events, %ld.%03ld events/sec\n",
- events, events * 1000 / ms,
- (events * 1000000 / ms) % 1000);
- else
- seq_printf(m, "%ld total events\n", events);
-
- mutex_unlock(&show_mutex);
-
- return 0;
-}
-
-/*
- * After a state change, make sure all concurrent lookup/update
- * activities have stopped:
- */
-static void sync_access(void)
-{
- unsigned long flags;
- int cpu;
-
- for_each_online_cpu(cpu) {
- raw_spinlock_t *lock = &per_cpu(tstats_lookup_lock, cpu);
-
- raw_spin_lock_irqsave(lock, flags);
- /* nothing */
- raw_spin_unlock_irqrestore(lock, flags);
- }
-}
-
-static ssize_t tstats_write(struct file *file, const char __user *buf,
- size_t count, loff_t *offs)
-{
- char ctl[2];
-
- if (count != 2 || *offs)
- return -EINVAL;
-
- if (copy_from_user(ctl, buf, count))
- return -EFAULT;
-
- mutex_lock(&show_mutex);
- switch (ctl[0]) {
- case '0':
- if (timer_stats_active) {
- timer_stats_active = 0;
- time_stop = ktime_get();
- sync_access();
- }
- break;
- case '1':
- if (!timer_stats_active) {
- reset_entries();
- time_start = ktime_get();
- smp_mb();
- timer_stats_active = 1;
- }
- break;
- default:
- count = -EINVAL;
- }
- mutex_unlock(&show_mutex);
-
- return count;
-}
-
-static int tstats_open(struct inode *inode, struct file *filp)
-{
- return single_open(filp, tstats_show, NULL);
-}
-
-static const struct file_operations tstats_fops = {
- .open = tstats_open,
- .read = seq_read,
- .write = tstats_write,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-void __init init_timer_stats(void)
-{
- int cpu;
-
- for_each_possible_cpu(cpu)
- raw_spin_lock_init(&per_cpu(tstats_lookup_lock, cpu));
-}
-
-static int __init init_tstats_procfs(void)
-{
- struct proc_dir_entry *pe;
-
- pe = proc_create("timer_stats", 0644, NULL, &tstats_fops);
- if (!pe)
- return -ENOMEM;
- return 0;
-}
-__initcall(init_tstats_procfs);
diff --git a/kernel/tsacct.c b/kernel/tsacct.c
index f8e26ab..5c21f05 100644
--- a/kernel/tsacct.c
+++ b/kernel/tsacct.c
@@ -31,7 +31,7 @@
struct taskstats *stats, struct task_struct *tsk)
{
const struct cred *tcred;
- cputime_t utime, stime, utimescaled, stimescaled;
+ u64 utime, stime, utimescaled, stimescaled;
u64 delta;
BUILD_BUG_ON(TS_COMM_LEN < TASK_COMM_LEN);
@@ -67,12 +67,12 @@
rcu_read_unlock();
task_cputime(tsk, &utime, &stime);
- stats->ac_utime = cputime_to_usecs(utime);
- stats->ac_stime = cputime_to_usecs(stime);
+ stats->ac_utime = div_u64(utime, NSEC_PER_USEC);
+ stats->ac_stime = div_u64(stime, NSEC_PER_USEC);
task_cputime_scaled(tsk, &utimescaled, &stimescaled);
- stats->ac_utimescaled = cputime_to_usecs(utimescaled);
- stats->ac_stimescaled = cputime_to_usecs(stimescaled);
+ stats->ac_utimescaled = div_u64(utimescaled, NSEC_PER_USEC);
+ stats->ac_stimescaled = div_u64(stimescaled, NSEC_PER_USEC);
stats->ac_minflt = tsk->min_flt;
stats->ac_majflt = tsk->maj_flt;
@@ -123,18 +123,15 @@
#undef MB
static void __acct_update_integrals(struct task_struct *tsk,
- cputime_t utime, cputime_t stime)
+ u64 utime, u64 stime)
{
- cputime_t time, dtime;
- u64 delta;
+ u64 time, delta;
if (!likely(tsk->mm))
return;
time = stime + utime;
- dtime = time - tsk->acct_timexpd;
- /* Avoid division: cputime_t is often in nanoseconds already. */
- delta = cputime_to_nsecs(dtime);
+ delta = time - tsk->acct_timexpd;
if (delta < TICK_NSEC)
return;
@@ -155,7 +152,7 @@
*/
void acct_update_integrals(struct task_struct *tsk)
{
- cputime_t utime, stime;
+ u64 utime, stime;
unsigned long flags;
local_irq_save(flags);
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 1d9fb65..072cbc9 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -1523,8 +1523,6 @@
return;
}
- timer_stats_timer_set_start_info(&dwork->timer);
-
dwork->wq = wq;
dwork->cpu = cpu;
timer->expires = jiffies + delay;
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index eb9e9a7..acedbe6 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -716,6 +716,19 @@
source "lib/Kconfig.kasan"
+config DEBUG_REFCOUNT
+ bool "Verbose refcount checks"
+ help
+ Say Y here if you want reference counters (refcount_t and kref) to
+ generate WARNs on dubious usage. Without this refcount_t will still
+ be a saturating counter and avoid Use-After-Free by turning it into
+ a resource leak Denial-Of-Service.
+
+ Use of this option will increase kernel text size but will alert the
+ admin of potential abuse.
+
+ If in doubt, say "N".
+
endmenu # "Memory Debugging"
config ARCH_HAS_KCOV
@@ -980,20 +993,6 @@
If unsure, say N.
-config TIMER_STATS
- bool "Collect kernel timers statistics"
- depends on DEBUG_KERNEL && PROC_FS
- help
- If you say Y here, additional code will be inserted into the
- timer routines to collect statistics about kernel timers being
- reprogrammed. The statistics can be read from /proc/timer_stats.
- The statistics collection is started by writing 1 to /proc/timer_stats,
- writing 0 stops it. This feature is useful to collect information
- about timer usage patterns in kernel and userspace. This feature
- is lightweight if enabled in the kernel config but not activated
- (it defaults to deactivated on bootup and will only be activated
- if some application like powertop activates it explicitly).
-
config DEBUG_PREEMPT
bool "Debug preemptible kernel"
depends on DEBUG_KERNEL && PREEMPT && TRACE_IRQFLAGS_SUPPORT
@@ -1180,6 +1179,18 @@
Say M if you want these torture tests to build as a module.
Say N if you are unsure.
+config WW_MUTEX_SELFTEST
+ tristate "Wait/wound mutex selftests"
+ help
+ This option provides a kernel module that runs tests on the
+ on the struct ww_mutex locking API.
+
+ It is recommended to enable DEBUG_WW_MUTEX_SLOWPATH in conjunction
+ with this test harness.
+
+ Say M if you want these self tests to build as a module.
+ Say N if you are unsure.
+
endmenu # lock debugging
config TRACE_IRQFLAGS
@@ -1450,6 +1461,7 @@
config RCU_TRACE
bool "Enable tracing for RCU"
depends on DEBUG_KERNEL
+ default y if TREE_RCU
select TRACE_CLOCK
help
This option provides tracing in RCU which presents stats
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index 04c1ef7..8c28cbd 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -52,9 +52,18 @@
static int debug_objects_warnings __read_mostly;
static int debug_objects_enabled __read_mostly
= CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT;
-
+static int debug_objects_pool_size __read_mostly
+ = ODEBUG_POOL_SIZE;
+static int debug_objects_pool_min_level __read_mostly
+ = ODEBUG_POOL_MIN_LEVEL;
static struct debug_obj_descr *descr_test __read_mostly;
+/*
+ * Track numbers of kmem_cache_alloc()/free() calls done.
+ */
+static int debug_objects_allocated;
+static int debug_objects_freed;
+
static void free_obj_work(struct work_struct *work);
static DECLARE_WORK(debug_obj_work, free_obj_work);
@@ -88,13 +97,13 @@
struct debug_obj *new;
unsigned long flags;
- if (likely(obj_pool_free >= ODEBUG_POOL_MIN_LEVEL))
+ if (likely(obj_pool_free >= debug_objects_pool_min_level))
return;
if (unlikely(!obj_cache))
return;
- while (obj_pool_free < ODEBUG_POOL_MIN_LEVEL) {
+ while (obj_pool_free < debug_objects_pool_min_level) {
new = kmem_cache_zalloc(obj_cache, gfp);
if (!new)
@@ -102,6 +111,7 @@
raw_spin_lock_irqsave(&pool_lock, flags);
hlist_add_head(&new->node, &obj_pool);
+ debug_objects_allocated++;
obj_pool_free++;
raw_spin_unlock_irqrestore(&pool_lock, flags);
}
@@ -162,24 +172,39 @@
/*
* workqueue function to free objects.
+ *
+ * To reduce contention on the global pool_lock, the actual freeing of
+ * debug objects will be delayed if the pool_lock is busy. We also free
+ * the objects in a batch of 4 for each lock/unlock cycle.
*/
+#define ODEBUG_FREE_BATCH 4
+
static void free_obj_work(struct work_struct *work)
{
- struct debug_obj *obj;
+ struct debug_obj *objs[ODEBUG_FREE_BATCH];
unsigned long flags;
+ int i;
- raw_spin_lock_irqsave(&pool_lock, flags);
- while (obj_pool_free > ODEBUG_POOL_SIZE) {
- obj = hlist_entry(obj_pool.first, typeof(*obj), node);
- hlist_del(&obj->node);
- obj_pool_free--;
+ if (!raw_spin_trylock_irqsave(&pool_lock, flags))
+ return;
+ while (obj_pool_free >= debug_objects_pool_size + ODEBUG_FREE_BATCH) {
+ for (i = 0; i < ODEBUG_FREE_BATCH; i++) {
+ objs[i] = hlist_entry(obj_pool.first,
+ typeof(*objs[0]), node);
+ hlist_del(&objs[i]->node);
+ }
+
+ obj_pool_free -= ODEBUG_FREE_BATCH;
+ debug_objects_freed += ODEBUG_FREE_BATCH;
/*
* We release pool_lock across kmem_cache_free() to
* avoid contention on pool_lock.
*/
raw_spin_unlock_irqrestore(&pool_lock, flags);
- kmem_cache_free(obj_cache, obj);
- raw_spin_lock_irqsave(&pool_lock, flags);
+ for (i = 0; i < ODEBUG_FREE_BATCH; i++)
+ kmem_cache_free(obj_cache, objs[i]);
+ if (!raw_spin_trylock_irqsave(&pool_lock, flags))
+ return;
}
raw_spin_unlock_irqrestore(&pool_lock, flags);
}
@@ -198,7 +223,7 @@
* schedule work when the pool is filled and the cache is
* initialized:
*/
- if (obj_pool_free > ODEBUG_POOL_SIZE && obj_cache)
+ if (obj_pool_free > debug_objects_pool_size && obj_cache)
sched = 1;
hlist_add_head(&obj->node, &obj_pool);
obj_pool_free++;
@@ -758,6 +783,8 @@
seq_printf(m, "pool_min_free :%d\n", obj_pool_min_free);
seq_printf(m, "pool_used :%d\n", obj_pool_used);
seq_printf(m, "pool_max_used :%d\n", obj_pool_max_used);
+ seq_printf(m, "objs_allocated:%d\n", debug_objects_allocated);
+ seq_printf(m, "objs_freed :%d\n", debug_objects_freed);
return 0;
}
@@ -1116,4 +1143,11 @@
pr_warn("out of memory.\n");
} else
debug_objects_selftest();
+
+ /*
+ * Increase the thresholds for allocating and freeing objects
+ * according to the number of possible CPUs available in the system.
+ */
+ debug_objects_pool_size += num_possible_cpus() * 32;
+ debug_objects_pool_min_level += num_possible_cpus() * 4;
}
diff --git a/lib/timerqueue.c b/lib/timerqueue.c
index adc6ee0..4a720ed 100644
--- a/lib/timerqueue.c
+++ b/lib/timerqueue.c
@@ -80,8 +80,7 @@
if (head->next == node) {
struct rb_node *rbn = rb_next(&node->node);
- head->next = rbn ?
- rb_entry(rbn, struct timerqueue_node, node) : NULL;
+ head->next = rb_entry_safe(rbn, struct timerqueue_node, node);
}
rb_erase(&node->node, &head->head);
RB_CLEAR_NODE(&node->node);
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 1904a93..d491529 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -920,7 +920,7 @@
BT_DBG("dev %p removing %speer %p", dev,
last ? "last " : "1 ", peer);
BT_DBG("chan %p orig refcnt %d", chan,
- atomic_read(&chan->kref.refcount));
+ kref_read(&chan->kref));
l2cap_chan_put(chan);
break;
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 5f123c3..f0095fd 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -810,7 +810,7 @@
/* AMP Manager functions */
struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr)
{
- BT_DBG("mgr %p orig refcnt %d", mgr, atomic_read(&mgr->kref.refcount));
+ BT_DBG("mgr %p orig refcnt %d", mgr, kref_read(&mgr->kref));
kref_get(&mgr->kref);
@@ -833,7 +833,7 @@
int amp_mgr_put(struct amp_mgr *mgr)
{
- BT_DBG("mgr %p orig refcnt %d", mgr, atomic_read(&mgr->kref.refcount));
+ BT_DBG("mgr %p orig refcnt %d", mgr, kref_read(&mgr->kref));
return kref_put(&mgr->kref, &_mgr_destroy);
}
diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c
index e32f341..02a4ccc 100644
--- a/net/bluetooth/amp.c
+++ b/net/bluetooth/amp.c
@@ -24,7 +24,7 @@
void amp_ctrl_get(struct amp_ctrl *ctrl)
{
BT_DBG("ctrl %p orig refcnt %d", ctrl,
- atomic_read(&ctrl->kref.refcount));
+ kref_read(&ctrl->kref));
kref_get(&ctrl->kref);
}
@@ -42,7 +42,7 @@
int amp_ctrl_put(struct amp_ctrl *ctrl)
{
BT_DBG("ctrl %p orig refcnt %d", ctrl,
- atomic_read(&ctrl->kref.refcount));
+ kref_read(&ctrl->kref));
return kref_put(&ctrl->kref, &_ctrl_destroy);
}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ce0b5dd..fc7f321 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -481,14 +481,14 @@
void l2cap_chan_hold(struct l2cap_chan *c)
{
- BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->kref.refcount));
+ BT_DBG("chan %p orig refcnt %d", c, kref_read(&c->kref));
kref_get(&c->kref);
}
void l2cap_chan_put(struct l2cap_chan *c)
{
- BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->kref.refcount));
+ BT_DBG("chan %p orig refcnt %d", c, kref_read(&c->kref));
kref_put(&c->kref, l2cap_chan_destroy);
}
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index 770c527..bad3d4a 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -3425,7 +3425,7 @@
struct ceph_msg *ceph_msg_get(struct ceph_msg *msg)
{
dout("%s %p (was %d)\n", __func__, msg,
- atomic_read(&msg->kref.refcount));
+ kref_read(&msg->kref));
kref_get(&msg->kref);
return msg;
}
@@ -3434,7 +3434,7 @@
void ceph_msg_put(struct ceph_msg *msg)
{
dout("%s %p (was %d)\n", __func__, msg,
- atomic_read(&msg->kref.refcount));
+ kref_read(&msg->kref));
kref_put(&msg->kref, ceph_msg_release);
}
EXPORT_SYMBOL(ceph_msg_put);
diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
index 842f049..f3378ba 100644
--- a/net/ceph/osd_client.c
+++ b/net/ceph/osd_client.c
@@ -438,7 +438,7 @@
void ceph_osdc_get_request(struct ceph_osd_request *req)
{
dout("%s %p (was %d)\n", __func__, req,
- atomic_read(&req->r_kref.refcount));
+ kref_read(&req->r_kref));
kref_get(&req->r_kref);
}
EXPORT_SYMBOL(ceph_osdc_get_request);
@@ -447,7 +447,7 @@
{
if (req) {
dout("%s %p (was %d)\n", __func__, req,
- atomic_read(&req->r_kref.refcount));
+ kref_read(&req->r_kref));
kref_put(&req->r_kref, ceph_osdc_release_request);
}
}
@@ -487,11 +487,11 @@
struct ceph_msg *reply_msg = req->r_reply;
dout("%s req %p\n", __func__, req);
- WARN_ON(atomic_read(&req->r_kref.refcount) != 1);
+ WARN_ON(kref_read(&req->r_kref) != 1);
request_release_checks(req);
- WARN_ON(atomic_read(&request_msg->kref.refcount) != 1);
- WARN_ON(atomic_read(&reply_msg->kref.refcount) != 1);
+ WARN_ON(kref_read(&request_msg->kref) != 1);
+ WARN_ON(kref_read(&reply_msg->kref) != 1);
target_destroy(&req->r_t);
request_init(req);
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 7bb12e0..e7c12ca 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -2923,7 +2923,8 @@
return;
set_bit(index, p->data_state);
- call_netevent_notifiers(NETEVENT_DELAY_PROBE_TIME_UPDATE, p);
+ if (index == NEIGH_VAR_DELAY_PROBE_TIME)
+ call_netevent_notifiers(NETEVENT_DELAY_PROBE_TIME_UPDATE, p);
if (!dev) /* NULL dev means this is default value */
neigh_copy_dflt_parms(net, p, index);
}
diff --git a/net/dccp/input.c b/net/dccp/input.c
index ba34718..8fedc2d 100644
--- a/net/dccp/input.c
+++ b/net/dccp/input.c
@@ -606,7 +606,8 @@
if (inet_csk(sk)->icsk_af_ops->conn_request(sk,
skb) < 0)
return 1;
- goto discard;
+ consume_skb(skb);
+ return 0;
}
if (dh->dccph_type == DCCP_PKT_RESET)
goto discard;
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index 89a8cac4..51b27ae 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -1263,7 +1263,7 @@
/*
* ax25 -> ASCII conversion
*/
-static char *ax2asc2(ax25_address *a, char *buf)
+static void ax2asc2(ax25_address *a, char *buf)
{
char c, *s;
int n;
@@ -1285,10 +1285,10 @@
*s++ = n + '0';
*s++ = '\0';
- if (*buf == '\0' || *buf == '-')
- return "*";
-
- return buf;
+ if (*buf == '\0' || *buf == '-') {
+ buf[0] = '*';
+ buf[1] = '\0';
+ }
}
#endif /* CONFIG_AX25 */
@@ -1322,7 +1322,7 @@
}
#endif
sprintf(tbuf, "%pI4", n->primary_key);
- seq_printf(seq, "%-16s 0x%-10x0x%-10x%s * %s\n",
+ seq_printf(seq, "%-16s 0x%-10x0x%-10x%-17s * %s\n",
tbuf, hatype, arp_state_to_flags(n), hbuffer, dev->name);
read_unlock(&n->lock);
}
diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c
index f6c50af..3d063eb 100644
--- a/net/ipv4/tcp_probe.c
+++ b/net/ipv4/tcp_probe.c
@@ -117,7 +117,7 @@
(fwmark > 0 && skb->mark == fwmark)) &&
(full || tp->snd_cwnd != tcp_probe.lastcwnd)) {
- spin_lock(&tcp_probe.lock);
+ spin_lock_bh(&tcp_probe.lock);
/* If log fills, just silently drop */
if (tcp_probe_avail() > 1) {
struct tcp_log *p = tcp_probe.log + tcp_probe.head;
@@ -157,7 +157,7 @@
tcp_probe.head = (tcp_probe.head + 1) & (bufsize - 1);
}
tcp_probe.lastcwnd = tp->snd_cwnd;
- spin_unlock(&tcp_probe.lock);
+ spin_unlock_bh(&tcp_probe.lock);
wake_up(&tcp_probe.wait);
}
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index a3eaafd..eec27f8 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -167,18 +167,22 @@
if (np->sndflow)
fl6_flowlabel = usin->sin6_flowinfo & IPV6_FLOWINFO_MASK;
- addr_type = ipv6_addr_type(&usin->sin6_addr);
-
- if (addr_type == IPV6_ADDR_ANY) {
+ if (ipv6_addr_any(&usin->sin6_addr)) {
/*
* connect to self
*/
- usin->sin6_addr.s6_addr[15] = 0x01;
+ if (ipv6_addr_v4mapped(&sk->sk_v6_rcv_saddr))
+ ipv6_addr_set_v4mapped(htonl(INADDR_LOOPBACK),
+ &usin->sin6_addr);
+ else
+ usin->sin6_addr = in6addr_loopback;
}
+ addr_type = ipv6_addr_type(&usin->sin6_addr);
+
daddr = &usin->sin6_addr;
- if (addr_type == IPV6_ADDR_MAPPED) {
+ if (addr_type & IPV6_ADDR_MAPPED) {
struct sockaddr_in sin;
if (__ipv6_only_sock(sk)) {
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index b6a94ff..7cebee5 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1021,6 +1021,11 @@
}
}
#endif
+ if (ipv6_addr_v4mapped(&fl6->saddr) &&
+ !(ipv6_addr_v4mapped(&fl6->daddr) || ipv6_addr_any(&fl6->daddr))) {
+ err = -EAFNOSUPPORT;
+ goto out_err_release;
+ }
return 0;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index eaad72c..4c60c6f 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -148,8 +148,13 @@
* connect() to INADDR_ANY means loopback (BSD'ism).
*/
- if (ipv6_addr_any(&usin->sin6_addr))
- usin->sin6_addr.s6_addr[15] = 0x1;
+ if (ipv6_addr_any(&usin->sin6_addr)) {
+ if (ipv6_addr_v4mapped(&sk->sk_v6_rcv_saddr))
+ ipv6_addr_set_v4mapped(htonl(INADDR_LOOPBACK),
+ &usin->sin6_addr);
+ else
+ usin->sin6_addr = in6addr_loopback;
+ }
addr_type = ipv6_addr_type(&usin->sin6_addr);
@@ -188,7 +193,7 @@
* TCP over IPv4
*/
- if (addr_type == IPV6_ADDR_MAPPED) {
+ if (addr_type & IPV6_ADDR_MAPPED) {
u32 exthdrlen = icsk->icsk_ext_hdr_len;
struct sockaddr_in sin;
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 8990856..221825a 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1033,6 +1033,10 @@
if (addr_len < SIN6_LEN_RFC2133)
return -EINVAL;
daddr = &sin6->sin6_addr;
+ if (ipv6_addr_any(daddr) &&
+ ipv6_addr_v4mapped(&np->saddr))
+ ipv6_addr_set_v4mapped(htonl(INADDR_LOOPBACK),
+ daddr);
break;
case AF_INET:
goto do_udp_sendmsg;
diff --git a/net/irda/irqueue.c b/net/irda/irqueue.c
index acbe61c..160dc89 100644
--- a/net/irda/irqueue.c
+++ b/net/irda/irqueue.c
@@ -383,9 +383,6 @@
* for deallocating this structure if it's complex. If not the user can
* just supply kfree, which should take care of the job.
*/
-#ifdef CONFIG_LOCKDEP
-static int hashbin_lock_depth = 0;
-#endif
int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func)
{
irda_queue_t* queue;
@@ -396,22 +393,27 @@
IRDA_ASSERT(hashbin->magic == HB_MAGIC, return -1;);
/* Synchronize */
- if ( hashbin->hb_type & HB_LOCK ) {
- spin_lock_irqsave_nested(&hashbin->hb_spinlock, flags,
- hashbin_lock_depth++);
- }
+ if (hashbin->hb_type & HB_LOCK)
+ spin_lock_irqsave(&hashbin->hb_spinlock, flags);
/*
* Free the entries in the hashbin, TODO: use hashbin_clear when
* it has been shown to work
*/
for (i = 0; i < HASHBIN_SIZE; i ++ ) {
- queue = dequeue_first((irda_queue_t**) &hashbin->hb_queue[i]);
- while (queue ) {
- if (free_func)
- (*free_func)(queue);
- queue = dequeue_first(
- (irda_queue_t**) &hashbin->hb_queue[i]);
+ while (1) {
+ queue = dequeue_first((irda_queue_t**) &hashbin->hb_queue[i]);
+
+ if (!queue)
+ break;
+
+ if (free_func) {
+ if (hashbin->hb_type & HB_LOCK)
+ spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
+ free_func(queue);
+ if (hashbin->hb_type & HB_LOCK)
+ spin_lock_irqsave(&hashbin->hb_spinlock, flags);
+ }
}
}
@@ -420,12 +422,8 @@
hashbin->magic = ~HB_MAGIC;
/* Release lock */
- if ( hashbin->hb_type & HB_LOCK) {
+ if (hashbin->hb_type & HB_LOCK)
spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
-#ifdef CONFIG_LOCKDEP
- hashbin_lock_depth--;
-#endif
- }
/*
* Free the hashbin structure
diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c
index 64f0e85..a646f34 100644
--- a/net/kcm/kcmsock.c
+++ b/net/kcm/kcmsock.c
@@ -1044,8 +1044,10 @@
} else {
/* Message not complete, save state */
partial_message:
- kcm->seq_skb = head;
- kcm_tx_msg(head)->last_skb = skb;
+ if (head) {
+ kcm->seq_skb = head;
+ kcm_tx_msg(head)->last_skb = skb;
+ }
}
KCM_STATS_ADD(kcm->stats.tx_bytes, copied);
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c
index 3e821da..8bc5a1b 100644
--- a/net/llc/llc_conn.c
+++ b/net/llc/llc_conn.c
@@ -821,7 +821,10 @@
* another trick required to cope with how the PROCOM state
* machine works. -acme
*/
+ skb_orphan(skb);
+ sock_hold(sk);
skb->sk = sk;
+ skb->destructor = sock_efree;
}
if (!sock_owned_by_user(sk))
llc_conn_rcv(sk, skb);
diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c
index d0e1e80..5404d0d 100644
--- a/net/llc/llc_sap.c
+++ b/net/llc/llc_sap.c
@@ -290,7 +290,10 @@
ev->type = LLC_SAP_EV_TYPE_PDU;
ev->reason = 0;
+ skb_orphan(skb);
+ sock_hold(sk);
skb->sk = sk;
+ skb->destructor = sock_efree;
llc_sap_state_process(sap, skb);
}
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index d56ee46..70f5b6a 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1497,6 +1497,8 @@
f->arr[f->num_members] = sk;
smp_wmb();
f->num_members++;
+ if (f->num_members == 1)
+ dev_add_pack(&f->prot_hook);
spin_unlock(&f->lock);
}
@@ -1513,6 +1515,8 @@
BUG_ON(i >= f->num_members);
f->arr[i] = f->arr[f->num_members - 1];
f->num_members--;
+ if (f->num_members == 0)
+ __dev_remove_pack(&f->prot_hook);
spin_unlock(&f->lock);
}
@@ -1619,6 +1623,7 @@
static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
{
+ struct packet_rollover *rollover = NULL;
struct packet_sock *po = pkt_sk(sk);
struct packet_fanout *f, *match;
u8 type = type_flags & 0xff;
@@ -1641,23 +1646,28 @@
return -EINVAL;
}
- if (!po->running)
- return -EINVAL;
+ mutex_lock(&fanout_mutex);
+ err = -EINVAL;
+ if (!po->running)
+ goto out;
+
+ err = -EALREADY;
if (po->fanout)
- return -EALREADY;
+ goto out;
if (type == PACKET_FANOUT_ROLLOVER ||
(type_flags & PACKET_FANOUT_FLAG_ROLLOVER)) {
- po->rollover = kzalloc(sizeof(*po->rollover), GFP_KERNEL);
- if (!po->rollover)
- return -ENOMEM;
- atomic_long_set(&po->rollover->num, 0);
- atomic_long_set(&po->rollover->num_huge, 0);
- atomic_long_set(&po->rollover->num_failed, 0);
+ err = -ENOMEM;
+ rollover = kzalloc(sizeof(*rollover), GFP_KERNEL);
+ if (!rollover)
+ goto out;
+ atomic_long_set(&rollover->num, 0);
+ atomic_long_set(&rollover->num_huge, 0);
+ atomic_long_set(&rollover->num_failed, 0);
+ po->rollover = rollover;
}
- mutex_lock(&fanout_mutex);
match = NULL;
list_for_each_entry(f, &fanout_list, list) {
if (f->id == id &&
@@ -1687,7 +1697,6 @@
match->prot_hook.func = packet_rcv_fanout;
match->prot_hook.af_packet_priv = match;
match->prot_hook.id_match = match_fanout_group;
- dev_add_pack(&match->prot_hook);
list_add(&match->list, &fanout_list);
}
err = -EINVAL;
@@ -1704,36 +1713,40 @@
}
}
out:
- mutex_unlock(&fanout_mutex);
- if (err) {
- kfree(po->rollover);
+ if (err && rollover) {
+ kfree(rollover);
po->rollover = NULL;
}
+ mutex_unlock(&fanout_mutex);
return err;
}
-static void fanout_release(struct sock *sk)
+/* If pkt_sk(sk)->fanout->sk_ref is zero, this function removes
+ * pkt_sk(sk)->fanout from fanout_list and returns pkt_sk(sk)->fanout.
+ * It is the responsibility of the caller to call fanout_release_data() and
+ * free the returned packet_fanout (after synchronize_net())
+ */
+static struct packet_fanout *fanout_release(struct sock *sk)
{
struct packet_sock *po = pkt_sk(sk);
struct packet_fanout *f;
- f = po->fanout;
- if (!f)
- return;
-
mutex_lock(&fanout_mutex);
- po->fanout = NULL;
+ f = po->fanout;
+ if (f) {
+ po->fanout = NULL;
- if (atomic_dec_and_test(&f->sk_ref)) {
- list_del(&f->list);
- dev_remove_pack(&f->prot_hook);
- fanout_release_data(f);
- kfree(f);
+ if (atomic_dec_and_test(&f->sk_ref))
+ list_del(&f->list);
+ else
+ f = NULL;
+
+ if (po->rollover)
+ kfree_rcu(po->rollover, rcu);
}
mutex_unlock(&fanout_mutex);
- if (po->rollover)
- kfree_rcu(po->rollover, rcu);
+ return f;
}
static bool packet_extra_vlan_len_allowed(const struct net_device *dev,
@@ -2907,6 +2920,7 @@
{
struct sock *sk = sock->sk;
struct packet_sock *po;
+ struct packet_fanout *f;
struct net *net;
union tpacket_req_u req_u;
@@ -2946,9 +2960,14 @@
packet_set_ring(sk, &req_u, 1, 1);
}
- fanout_release(sk);
+ f = fanout_release(sk);
synchronize_net();
+
+ if (f) {
+ fanout_release_data(f);
+ kfree(f);
+ }
/*
* Now the socket is dead. No more input will appear.
*/
@@ -3900,7 +3919,6 @@
}
if (msg == NETDEV_UNREGISTER) {
packet_cached_dev_reset(po);
- fanout_release(sk);
po->ifindex = -1;
if (po->prot_hook.dev)
dev_put(po->prot_hook.dev);
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index 8147e8d..f39e3e1 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -1358,7 +1358,7 @@
ifdebug(CACHE)
seq_printf(m, "# expiry=%ld refcnt=%d flags=%lx\n",
convert_to_wallclock(cp->expiry_time),
- atomic_read(&cp->ref.refcount), cp->flags);
+ kref_read(&cp->ref), cp->flags);
cache_get(cp);
if (cache_check(cd, cp, NULL))
/* cache_check does a cache_put on failure */
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index 9c9db55..7bfe1fb 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -490,7 +490,7 @@
svc_xprt_get(xprt);
dprintk("svc: transport %p dequeued, inuse=%d\n",
- xprt, atomic_read(&xprt->xpt_ref.refcount));
+ xprt, kref_read(&xprt->xpt_ref));
}
spin_unlock_bh(&pool->sp_lock);
out:
@@ -822,7 +822,7 @@
/* XPT_DATA|XPT_DEFERRED case: */
dprintk("svc: server %p, pool %u, transport %p, inuse=%d\n",
rqstp, rqstp->rq_pool->sp_id, xprt,
- atomic_read(&xprt->xpt_ref.refcount));
+ kref_read(&xprt->xpt_ref));
rqstp->rq_deferred = svc_deferred_dequeue(xprt);
if (rqstp->rq_deferred)
len = svc_deferred_recv(rqstp);
@@ -980,7 +980,7 @@
* through, close it. */
if (!test_and_set_bit(XPT_OLD, &xprt->xpt_flags))
continue;
- if (atomic_read(&xprt->xpt_ref.refcount) > 1 ||
+ if (kref_read(&xprt->xpt_ref) > 1 ||
test_bit(XPT_BUSY, &xprt->xpt_flags))
continue;
list_del_init(le);
diff --git a/net/sunrpc/svcauth.c b/net/sunrpc/svcauth.c
index e112da8..bb8db3c 100644
--- a/net/sunrpc/svcauth.c
+++ b/net/sunrpc/svcauth.c
@@ -126,13 +126,18 @@
static struct hlist_head auth_domain_table[DN_HASHMAX];
static DEFINE_SPINLOCK(auth_domain_lock);
+static void auth_domain_release(struct kref *kref)
+{
+ struct auth_domain *dom = container_of(kref, struct auth_domain, ref);
+
+ hlist_del(&dom->hash);
+ dom->flavour->domain_release(dom);
+ spin_unlock(&auth_domain_lock);
+}
+
void auth_domain_put(struct auth_domain *dom)
{
- if (atomic_dec_and_lock(&dom->ref.refcount, &auth_domain_lock)) {
- hlist_del(&dom->hash);
- dom->flavour->domain_release(dom);
- spin_unlock(&auth_domain_lock);
- }
+ kref_put_lock(&dom->ref, auth_domain_release, &auth_domain_lock);
}
EXPORT_SYMBOL_GPL(auth_domain_put);
diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c
index ca2799a..39652d3 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_transport.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c
@@ -1201,9 +1201,9 @@
ib_drain_qp(rdma->sc_qp);
/* We should only be called from kref_put */
- if (atomic_read(&xprt->xpt_ref.refcount) != 0)
+ if (kref_read(&xprt->xpt_ref) != 0)
pr_err("svcrdma: sc_xprt still in use? (%d)\n",
- atomic_read(&xprt->xpt_ref.refcount));
+ kref_read(&xprt->xpt_ref));
/*
* Destroy queued, but not processed read completions. Note
diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
index 396e204..b86ee54 100644
--- a/samples/bpf/bpf_load.c
+++ b/samples/bpf/bpf_load.c
@@ -277,6 +277,11 @@
Elf_Data *data, *data_prog, *symbols = NULL;
char *shname, *shname_prog;
+ /* reset global variables */
+ kern_version = 0;
+ memset(license, 0, sizeof(license));
+ memset(processed_sec, 0, sizeof(processed_sec));
+
if (elf_version(EV_CURRENT) == EV_NONE)
return 1;
@@ -328,6 +333,8 @@
/* load programs that need map fixup (relocations) */
for (i = 1; i < ehdr.e_shnum; i++) {
+ if (processed_sec[i])
+ continue;
if (get_sec(elf, i, &ehdr, &shname, &shdr, &data))
continue;
diff --git a/samples/bpf/test_cgrp2_attach.c b/samples/bpf/test_cgrp2_attach.c
index 5040586..4bfcaf9 100644
--- a/samples/bpf/test_cgrp2_attach.c
+++ b/samples/bpf/test_cgrp2_attach.c
@@ -104,7 +104,7 @@
return EXIT_FAILURE;
}
- ret = bpf_prog_attach(prog_fd, cg_fd, type);
+ ret = bpf_prog_attach(prog_fd, cg_fd, type, 0);
if (ret < 0) {
printf("Failed to attach prog to cgroup: '%s'\n",
strerror(errno));
diff --git a/samples/bpf/test_cgrp2_attach2.c b/samples/bpf/test_cgrp2_attach2.c
index 6e69be3..3049b1f2 100644
--- a/samples/bpf/test_cgrp2_attach2.c
+++ b/samples/bpf/test_cgrp2_attach2.c
@@ -79,11 +79,12 @@
if (join_cgroup(FOO))
goto err;
- if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS)) {
+ if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, 1)) {
log_err("Attaching prog to /foo");
goto err;
}
+ printf("Attached DROP prog. This ping in cgroup /foo should fail...\n");
assert(system(PING_CMD) != 0);
/* Create cgroup /foo/bar, get fd, and join it */
@@ -94,24 +95,27 @@
if (join_cgroup(BAR))
goto err;
+ printf("Attached DROP prog. This ping in cgroup /foo/bar should fail...\n");
assert(system(PING_CMD) != 0);
- if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS)) {
+ if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 1)) {
log_err("Attaching prog to /foo/bar");
goto err;
}
+ printf("Attached PASS prog. This ping in cgroup /foo/bar should pass...\n");
assert(system(PING_CMD) == 0);
-
if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) {
log_err("Detaching program from /foo/bar");
goto err;
}
+ printf("Detached PASS from /foo/bar while DROP is attached to /foo.\n"
+ "This ping in cgroup /foo/bar should fail...\n");
assert(system(PING_CMD) != 0);
- if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS)) {
+ if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 1)) {
log_err("Attaching prog to /foo/bar");
goto err;
}
@@ -121,8 +125,60 @@
goto err;
}
+ printf("Attached PASS from /foo/bar and detached DROP from /foo.\n"
+ "This ping in cgroup /foo/bar should pass...\n");
assert(system(PING_CMD) == 0);
+ if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 1)) {
+ log_err("Attaching prog to /foo/bar");
+ goto err;
+ }
+
+ if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) {
+ errno = 0;
+ log_err("Unexpected success attaching prog to /foo/bar");
+ goto err;
+ }
+
+ if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) {
+ log_err("Detaching program from /foo/bar");
+ goto err;
+ }
+
+ if (!bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) {
+ errno = 0;
+ log_err("Unexpected success in double detach from /foo");
+ goto err;
+ }
+
+ if (bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) {
+ log_err("Attaching non-overridable prog to /foo");
+ goto err;
+ }
+
+ if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) {
+ errno = 0;
+ log_err("Unexpected success attaching non-overridable prog to /foo/bar");
+ goto err;
+ }
+
+ if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 1)) {
+ errno = 0;
+ log_err("Unexpected success attaching overridable prog to /foo/bar");
+ goto err;
+ }
+
+ if (!bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, 1)) {
+ errno = 0;
+ log_err("Unexpected success attaching overridable prog to /foo");
+ goto err;
+ }
+
+ if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) {
+ log_err("Attaching different non-overridable prog to /foo");
+ goto err;
+ }
+
goto out;
err:
@@ -132,5 +188,9 @@
close(foo);
close(bar);
cleanup_cgroup_environment();
+ if (!rc)
+ printf("PASS\n");
+ else
+ printf("FAIL\n");
return rc;
}
diff --git a/samples/bpf/test_cgrp2_sock.c b/samples/bpf/test_cgrp2_sock.c
index 0791b94..c3cfb23e 100644
--- a/samples/bpf/test_cgrp2_sock.c
+++ b/samples/bpf/test_cgrp2_sock.c
@@ -75,7 +75,7 @@
return EXIT_FAILURE;
}
- ret = bpf_prog_attach(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE);
+ ret = bpf_prog_attach(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE, 0);
if (ret < 0) {
printf("Failed to attach prog to cgroup: '%s'\n",
strerror(errno));
diff --git a/samples/bpf/test_cgrp2_sock2.c b/samples/bpf/test_cgrp2_sock2.c
index 455ef0d..db03607 100644
--- a/samples/bpf/test_cgrp2_sock2.c
+++ b/samples/bpf/test_cgrp2_sock2.c
@@ -55,7 +55,7 @@
}
ret = bpf_prog_attach(prog_fd[filter_id], cg_fd,
- BPF_CGROUP_INET_SOCK_CREATE);
+ BPF_CGROUP_INET_SOCK_CREATE, 0);
if (ret < 0) {
printf("Failed to attach prog to cgroup: '%s'\n",
strerror(errno));
diff --git a/samples/bpf/tracex5_kern.c b/samples/bpf/tracex5_kern.c
index fd12d71..7e4cf74 100644
--- a/samples/bpf/tracex5_kern.c
+++ b/samples/bpf/tracex5_kern.c
@@ -8,6 +8,7 @@
#include <linux/version.h>
#include <uapi/linux/bpf.h>
#include <uapi/linux/seccomp.h>
+#include <uapi/linux/unistd.h>
#include "bpf_helpers.h"
#define PROG(F) SEC("kprobe/"__stringify(F)) int bpf_func_##F
diff --git a/scripts/analyze_suspend.py b/scripts/analyze_suspend.py
index a0ba48f..20cdb2b 100755
--- a/scripts/analyze_suspend.py
+++ b/scripts/analyze_suspend.py
@@ -24,11 +24,6 @@
# https://01.org/suspendresume
# Source repo
# https://github.com/01org/suspendresume
-# Documentation
-# Getting Started
-# https://01.org/suspendresume/documentation/getting-started
-# Command List:
-# https://01.org/suspendresume/documentation/command-list
#
# Description:
# This tool is designed to assist kernel and OS developers in optimizing
@@ -66,6 +61,8 @@
from datetime import datetime
import struct
import ConfigParser
+from threading import Thread
+from subprocess import call, Popen, PIPE
# ----------------- CLASSES --------------------
@@ -75,11 +72,15 @@
# store system values and test parameters
class SystemValues:
ansi = False
- version = '4.2'
+ version = '4.5'
verbose = False
addlogs = False
- mindevlen = 0.001
- mincglen = 1.0
+ mindevlen = 0.0
+ mincglen = 0.0
+ cgphase = ''
+ cgtest = -1
+ callloopmaxgap = 0.0001
+ callloopmaxlen = 0.005
srgap = 0
cgexp = False
outdir = ''
@@ -92,6 +93,7 @@
'device_pm_callback_end',
'device_pm_callback_start'
]
+ logmsg = ''
testcommand = ''
mempath = '/dev/mem'
powerfile = '/sys/power/state'
@@ -117,19 +119,19 @@
usetracemarkers = True
usekprobes = True
usedevsrc = False
+ useprocmon = False
notestrun = False
+ mixedphaseheight = True
devprops = dict()
- postresumetime = 0
+ predelay = 0
+ postdelay = 0
+ procexecfmt = 'ps - (?P<ps>.*)$'
devpropfmt = '# Device Properties: .*'
tracertypefmt = '# tracer: (?P<t>.*)'
firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$'
- postresumefmt = '# post resume time (?P<t>[0-9]*)$'
stampfmt = '# suspend-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\
'(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\
' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$'
- kprobecolor = 'rgba(204,204,204,0.5)'
- synccolor = 'rgba(204,204,204,0.5)'
- debugfuncs = []
tracefuncs = {
'sys_sync': dict(),
'pm_prepare_console': dict(),
@@ -152,44 +154,66 @@
'CPU_OFF': {
'func':'_cpu_down',
'args_x86_64': {'cpu':'%di:s32'},
- 'format': 'CPU_OFF[{cpu}]',
- 'mask': 'CPU_.*_DOWN'
+ 'format': 'CPU_OFF[{cpu}]'
},
'CPU_ON': {
'func':'_cpu_up',
'args_x86_64': {'cpu':'%di:s32'},
- 'format': 'CPU_ON[{cpu}]',
- 'mask': 'CPU_.*_UP'
+ 'format': 'CPU_ON[{cpu}]'
},
}
dev_tracefuncs = {
# general wait/delay/sleep
- 'msleep': { 'args_x86_64': {'time':'%di:s32'} },
- 'udelay': { 'func':'__const_udelay', 'args_x86_64': {'loops':'%di:s32'} },
- 'acpi_os_stall': dict(),
+ 'msleep': { 'args_x86_64': {'time':'%di:s32'}, 'ub': 1 },
+ 'schedule_timeout_uninterruptible': { 'args_x86_64': {'timeout':'%di:s32'}, 'ub': 1 },
+ 'schedule_timeout': { 'args_x86_64': {'timeout':'%di:s32'}, 'ub': 1 },
+ 'udelay': { 'func':'__const_udelay', 'args_x86_64': {'loops':'%di:s32'}, 'ub': 1 },
+ 'usleep_range': { 'args_x86_64': {'min':'%di:s32', 'max':'%si:s32'}, 'ub': 1 },
+ 'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 },
+ 'acpi_os_stall': {'ub': 1},
# ACPI
'acpi_resume_power_resources': dict(),
'acpi_ps_parse_aml': dict(),
# filesystem
'ext4_sync_fs': dict(),
+ # 80211
+ 'iwlagn_mac_start': dict(),
+ 'iwlagn_alloc_bcast_station': dict(),
+ 'iwl_trans_pcie_start_hw': dict(),
+ 'iwl_trans_pcie_start_fw': dict(),
+ 'iwl_run_init_ucode': dict(),
+ 'iwl_load_ucode_wait_alive': dict(),
+ 'iwl_alive_start': dict(),
+ 'iwlagn_mac_stop': dict(),
+ 'iwlagn_mac_suspend': dict(),
+ 'iwlagn_mac_resume': dict(),
+ 'iwlagn_mac_add_interface': dict(),
+ 'iwlagn_mac_remove_interface': dict(),
+ 'iwlagn_mac_change_interface': dict(),
+ 'iwlagn_mac_config': dict(),
+ 'iwlagn_configure_filter': dict(),
+ 'iwlagn_mac_hw_scan': dict(),
+ 'iwlagn_bss_info_changed': dict(),
+ 'iwlagn_mac_channel_switch': dict(),
+ 'iwlagn_mac_flush': dict(),
# ATA
'ata_eh_recover': { 'args_x86_64': {'port':'+36(%di):s32'} },
# i915
- 'i915_gem_restore_gtt_mappings': dict(),
+ 'i915_gem_resume': dict(),
+ 'i915_restore_state': dict(),
'intel_opregion_setup': dict(),
+ 'g4x_pre_enable_dp': dict(),
+ 'vlv_pre_enable_dp': dict(),
+ 'chv_pre_enable_dp': dict(),
+ 'g4x_enable_dp': dict(),
+ 'vlv_enable_dp': dict(),
+ 'intel_hpd_init': dict(),
+ 'intel_opregion_register': dict(),
'intel_dp_detect': dict(),
'intel_hdmi_detect': dict(),
'intel_opregion_init': dict(),
+ 'intel_fbdev_set_suspend': dict(),
}
- kprobes_postresume = [
- {
- 'name': 'ataportrst',
- 'func': 'ata_eh_recover',
- 'args': {'port':'+36(%di):s32'},
- 'format': 'ata{port}_port_reset',
- 'mask': 'ata.*_port_reset'
- }
- ]
kprobes = dict()
timeformat = '%.3f'
def __init__(self):
@@ -198,6 +222,7 @@
self.embedded = True
self.addlogs = True
self.htmlfile = os.environ['LOG_FILE']
+ self.archargs = 'args_'+platform.machine()
self.hostname = platform.node()
if(self.hostname == ''):
self.hostname = 'localhost'
@@ -214,6 +239,13 @@
if num < 0 or num > 6:
return
self.timeformat = '%.{0}f'.format(num)
+ def setOutputFolder(self, value):
+ args = dict()
+ n = datetime.now()
+ args['date'] = n.strftime('%y%m%d')
+ args['time'] = n.strftime('%H%M%S')
+ args['hostname'] = self.hostname
+ self.outdir = value.format(**args)
def setOutputFile(self):
if((self.htmlfile == '') and (self.dmesgfile != '')):
m = re.match('(?P<name>.*)_dmesg\.txt$', self.dmesgfile)
@@ -253,10 +285,14 @@
self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html'
if not os.path.isdir(self.testdir):
os.mkdir(self.testdir)
- def setDeviceFilter(self, devnames):
- self.devicefilter = string.split(devnames)
+ def setDeviceFilter(self, value):
+ self.devicefilter = []
+ if value:
+ value = value.split(',')
+ for i in value:
+ self.devicefilter.append(i.strip())
def rtcWakeAlarmOn(self):
- os.system('echo 0 > '+self.rtcpath+'/wakealarm')
+ call('echo 0 > '+self.rtcpath+'/wakealarm', shell=True)
outD = open(self.rtcpath+'/date', 'r').read().strip()
outT = open(self.rtcpath+'/time', 'r').read().strip()
mD = re.match('^(?P<y>[0-9]*)-(?P<m>[0-9]*)-(?P<d>[0-9]*)', outD)
@@ -272,12 +308,12 @@
# if hardware time fails, use the software time
nowtime = int(datetime.now().strftime('%s'))
alarm = nowtime + self.rtcwaketime
- os.system('echo %d > %s/wakealarm' % (alarm, self.rtcpath))
+ call('echo %d > %s/wakealarm' % (alarm, self.rtcpath), shell=True)
def rtcWakeAlarmOff(self):
- os.system('echo 0 > %s/wakealarm' % self.rtcpath)
+ call('echo 0 > %s/wakealarm' % self.rtcpath, shell=True)
def initdmesg(self):
# get the latest time stamp from the dmesg log
- fp = os.popen('dmesg')
+ fp = Popen('dmesg', stdout=PIPE).stdout
ktime = '0'
for line in fp:
line = line.replace('\r\n', '')
@@ -291,7 +327,7 @@
self.dmesgstart = float(ktime)
def getdmesg(self):
# store all new dmesg lines since initdmesg was called
- fp = os.popen('dmesg')
+ fp = Popen('dmesg', stdout=PIPE).stdout
op = open(self.dmesgfile, 'a')
for line in fp:
line = line.replace('\r\n', '')
@@ -317,25 +353,18 @@
def getFtraceFilterFunctions(self, current):
rootCheck(True)
if not current:
- os.system('cat '+self.tpath+'available_filter_functions')
+ call('cat '+self.tpath+'available_filter_functions', shell=True)
return
fp = open(self.tpath+'available_filter_functions')
master = fp.read().split('\n')
fp.close()
- if len(self.debugfuncs) > 0:
- for i in self.debugfuncs:
- if i in master:
- print i
- else:
- print self.colorText(i)
- else:
- for i in self.tracefuncs:
- if 'func' in self.tracefuncs[i]:
- i = self.tracefuncs[i]['func']
- if i in master:
- print i
- else:
- print self.colorText(i)
+ for i in self.tracefuncs:
+ if 'func' in self.tracefuncs[i]:
+ i = self.tracefuncs[i]['func']
+ if i in master:
+ print i
+ else:
+ print self.colorText(i)
def setFtraceFilterFunctions(self, list):
fp = open(self.tpath+'available_filter_functions')
master = fp.read().split('\n')
@@ -351,22 +380,15 @@
fp = open(self.tpath+'set_graph_function', 'w')
fp.write(flist)
fp.close()
- def kprobeMatch(self, name, target):
- if name not in self.kprobes:
- return False
- if re.match(self.kprobes[name]['mask'], target):
- return True
- return False
def basicKprobe(self, name):
- self.kprobes[name] = {'name': name,'func': name,'args': dict(),'format': name,'mask': name}
+ self.kprobes[name] = {'name': name,'func': name,'args': dict(),'format': name}
def defaultKprobe(self, name, kdata):
k = kdata
- for field in ['name', 'format', 'mask', 'func']:
+ for field in ['name', 'format', 'func']:
if field not in k:
k[field] = name
- archargs = 'args_'+platform.machine()
- if archargs in k:
- k['args'] = k[archargs]
+ if self.archargs in k:
+ k['args'] = k[self.archargs]
else:
k['args'] = dict()
k['format'] = name
@@ -403,49 +425,80 @@
out = fmt.format(**arglist)
out = out.replace(' ', '_').replace('"', '')
return out
- def kprobeText(self, kprobe):
- name, fmt, func, args = kprobe['name'], kprobe['format'], kprobe['func'], kprobe['args']
+ def kprobeText(self, kname, kprobe):
+ name = fmt = func = kname
+ args = dict()
+ if 'name' in kprobe:
+ name = kprobe['name']
+ if 'format' in kprobe:
+ fmt = kprobe['format']
+ if 'func' in kprobe:
+ func = kprobe['func']
+ if self.archargs in kprobe:
+ args = kprobe[self.archargs]
+ if 'args' in kprobe:
+ args = kprobe['args']
if re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', func):
- doError('Kprobe "%s" has format info in the function name "%s"' % (name, func), False)
+ doError('Kprobe "%s" has format info in the function name "%s"' % (name, func))
for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', fmt):
if arg not in args:
- doError('Kprobe "%s" is missing argument "%s"' % (name, arg), False)
+ doError('Kprobe "%s" is missing argument "%s"' % (name, arg))
val = 'p:%s_cal %s' % (name, func)
for i in sorted(args):
val += ' %s=%s' % (i, args[i])
val += '\nr:%s_ret %s $retval\n' % (name, func)
return val
- def addKprobes(self):
+ def addKprobes(self, output=False):
+ if len(sysvals.kprobes) < 1:
+ return
+ if output:
+ print(' kprobe functions in this kernel:')
# first test each kprobe
- print('INITIALIZING KPROBES...')
rejects = []
+ # sort kprobes: trace, ub-dev, custom, dev
+ kpl = [[], [], [], []]
for name in sorted(self.kprobes):
- if not self.testKprobe(self.kprobes[name]):
+ res = self.colorText('YES', 32)
+ if not self.testKprobe(name, self.kprobes[name]):
+ res = self.colorText('NO')
rejects.append(name)
+ else:
+ if name in self.tracefuncs:
+ kpl[0].append(name)
+ elif name in self.dev_tracefuncs:
+ if 'ub' in self.dev_tracefuncs[name]:
+ kpl[1].append(name)
+ else:
+ kpl[3].append(name)
+ else:
+ kpl[2].append(name)
+ if output:
+ print(' %s: %s' % (name, res))
+ kplist = kpl[0] + kpl[1] + kpl[2] + kpl[3]
# remove all failed ones from the list
for name in rejects:
- vprint('Skipping KPROBE: %s' % name)
self.kprobes.pop(name)
+ # set the kprobes all at once
self.fsetVal('', 'kprobe_events')
kprobeevents = ''
- # set the kprobes all at once
- for kp in self.kprobes:
- val = self.kprobeText(self.kprobes[kp])
- vprint('Adding KPROBE: %s\n%s' % (kp, val.strip()))
- kprobeevents += self.kprobeText(self.kprobes[kp])
+ for kp in kplist:
+ kprobeevents += self.kprobeText(kp, self.kprobes[kp])
self.fsetVal(kprobeevents, 'kprobe_events')
# verify that the kprobes were set as ordered
check = self.fgetVal('kprobe_events')
- linesout = len(kprobeevents.split('\n'))
- linesack = len(check.split('\n'))
- if linesack < linesout:
- # if not, try appending the kprobes 1 by 1
- for kp in self.kprobes:
- kprobeevents = self.kprobeText(self.kprobes[kp])
- self.fsetVal(kprobeevents, 'kprobe_events', 'a')
+ linesout = len(kprobeevents.split('\n')) - 1
+ linesack = len(check.split('\n')) - 1
+ if output:
+ res = '%d/%d' % (linesack, linesout)
+ if linesack < linesout:
+ res = self.colorText(res, 31)
+ else:
+ res = self.colorText(res, 32)
+ print(' working kprobe functions enabled: %s' % res)
self.fsetVal('1', 'events/kprobes/enable')
- def testKprobe(self, kprobe):
- kprobeevents = self.kprobeText(kprobe)
+ def testKprobe(self, kname, kprobe):
+ self.fsetVal('0', 'events/kprobes/enable')
+ kprobeevents = self.kprobeText(kname, kprobe)
if not kprobeevents:
return False
try:
@@ -463,8 +516,9 @@
if not os.path.exists(file):
return False
try:
- fp = open(file, mode)
+ fp = open(file, mode, 0)
fp.write(val)
+ fp.flush()
fp.close()
except:
pass
@@ -491,21 +545,17 @@
for name in self.dev_tracefuncs:
self.defaultKprobe(name, self.dev_tracefuncs[name])
def isCallgraphFunc(self, name):
- if len(self.debugfuncs) < 1 and self.suspendmode == 'command':
+ if len(self.tracefuncs) < 1 and self.suspendmode == 'command':
return True
- if name in self.debugfuncs:
- return True
- funclist = []
for i in self.tracefuncs:
if 'func' in self.tracefuncs[i]:
- funclist.append(self.tracefuncs[i]['func'])
+ f = self.tracefuncs[i]['func']
else:
- funclist.append(i)
- if name in funclist:
- return True
+ f = i
+ if name == f:
+ return True
return False
def initFtrace(self, testing=False):
- tp = self.tpath
print('INITIALIZING FTRACE...')
# turn trace off
self.fsetVal('0', 'tracing_on')
@@ -518,18 +568,7 @@
# go no further if this is just a status check
if testing:
return
- if self.usekprobes:
- # add tracefunc kprobes so long as were not using full callgraph
- if(not self.usecallgraph or len(self.debugfuncs) > 0):
- for name in self.tracefuncs:
- self.defaultKprobe(name, self.tracefuncs[name])
- if self.usedevsrc:
- for name in self.dev_tracefuncs:
- self.defaultKprobe(name, self.dev_tracefuncs[name])
- else:
- self.usedevsrc = False
- self.addKprobes()
- # initialize the callgraph trace, unless this is an x2 run
+ # initialize the callgraph trace
if(self.usecallgraph):
# set trace type
self.fsetVal('function_graph', 'current_tracer')
@@ -545,20 +584,24 @@
self.fsetVal('context-info', 'trace_options')
self.fsetVal('graph-time', 'trace_options')
self.fsetVal('0', 'max_graph_depth')
- if len(self.debugfuncs) > 0:
- self.setFtraceFilterFunctions(self.debugfuncs)
- elif self.suspendmode == 'command':
- self.fsetVal('', 'set_graph_function')
- else:
- cf = ['dpm_run_callback']
- if(self.usetraceeventsonly):
- cf += ['dpm_prepare', 'dpm_complete']
- for fn in self.tracefuncs:
- if 'func' in self.tracefuncs[fn]:
- cf.append(self.tracefuncs[fn]['func'])
- else:
- cf.append(fn)
- self.setFtraceFilterFunctions(cf)
+ cf = ['dpm_run_callback']
+ if(self.usetraceeventsonly):
+ cf += ['dpm_prepare', 'dpm_complete']
+ for fn in self.tracefuncs:
+ if 'func' in self.tracefuncs[fn]:
+ cf.append(self.tracefuncs[fn]['func'])
+ else:
+ cf.append(fn)
+ self.setFtraceFilterFunctions(cf)
+ # initialize the kprobe trace
+ elif self.usekprobes:
+ for name in self.tracefuncs:
+ self.defaultKprobe(name, self.tracefuncs[name])
+ if self.usedevsrc:
+ for name in self.dev_tracefuncs:
+ self.defaultKprobe(name, self.dev_tracefuncs[name])
+ print('INITIALIZING KPROBES...')
+ self.addKprobes(self.verbose)
if(self.usetraceevents):
# turn trace events on
events = iter(self.traceevents)
@@ -590,10 +633,10 @@
if(os.path.exists(tp+f) == False):
return False
return True
- def colorText(self, str):
+ def colorText(self, str, color=31):
if not self.ansi:
return str
- return '\x1B[31;40m'+str+'\x1B[m'
+ return '\x1B[%d;40m%s\x1B[m' % (color, str)
sysvals = SystemValues()
@@ -625,8 +668,8 @@
if self.xtraclass:
return ' '+self.xtraclass
if self.async:
- return ' async'
- return ' sync'
+ return ' async_device'
+ return ' sync_device'
# Class: DeviceNode
# Description:
@@ -646,8 +689,6 @@
# The primary container for suspend/resume test data. There is one for
# each test run. The data is organized into a cronological hierarchy:
# Data.dmesg {
-# root structure, started as dmesg & ftrace, but now only ftrace
-# contents: times for suspend start/end, resume start/end, fwdata
# phases {
# 10 sequential, non-overlapping phases of S/R
# contents: times for phase start/end, order/color data for html
@@ -658,7 +699,7 @@
# contents: start/stop times, pid/cpu/driver info
# parents/children, html id for timeline/callgraph
# optionally includes an ftrace callgraph
-# optionally includes intradev trace events
+# optionally includes dev/ps data
# }
# }
# }
@@ -671,19 +712,24 @@
end = 0.0 # test end
tSuspended = 0.0 # low-level suspend start
tResumed = 0.0 # low-level resume start
+ tKernSus = 0.0 # kernel level suspend start
+ tKernRes = 0.0 # kernel level resume end
tLow = 0.0 # time spent in low-level suspend (standby/freeze)
fwValid = False # is firmware data available
fwSuspend = 0 # time spent in firmware suspend
fwResume = 0 # time spent in firmware resume
dmesgtext = [] # dmesg text file in memory
+ pstl = 0 # process timeline
testnumber = 0
idstr = ''
html_device_id = 0
stamp = 0
outfile = ''
- dev_ubiquitous = ['msleep', 'udelay']
+ devpids = []
+ kerror = False
def __init__(self, num):
- idchar = 'abcdefghijklmnopqrstuvwxyz'
+ idchar = 'abcdefghij'
+ self.pstl = dict()
self.testnumber = num
self.idstr = idchar[num]
self.dmesgtext = []
@@ -714,16 +760,39 @@
self.devicegroups = []
for phase in self.phases:
self.devicegroups.append([phase])
- def getStart(self):
- return self.dmesg[self.phases[0]]['start']
+ self.errorinfo = {'suspend':[],'resume':[]}
+ def extractErrorInfo(self, dmesg):
+ error = ''
+ tm = 0.0
+ for i in range(len(dmesg)):
+ if 'Call Trace:' in dmesg[i]:
+ m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) .*', dmesg[i])
+ if not m:
+ continue
+ tm = float(m.group('ktime'))
+ if tm < self.start or tm > self.end:
+ continue
+ for j in range(i-10, i+1):
+ error += dmesg[j]
+ continue
+ if error:
+ m = re.match('[ \t]*\[ *[0-9\.]*\] \[\<[0-9a-fA-F]*\>\] .*', dmesg[i])
+ if m:
+ error += dmesg[i]
+ else:
+ if tm < self.tSuspended:
+ dir = 'suspend'
+ else:
+ dir = 'resume'
+ error = error.replace('<', '<').replace('>', '>')
+ vprint('kernel error found in %s at %f' % (dir, tm))
+ self.errorinfo[dir].append((tm, error))
+ self.kerror = True
+ error = ''
def setStart(self, time):
self.start = time
- self.dmesg[self.phases[0]]['start'] = time
- def getEnd(self):
- return self.dmesg[self.phases[-1]]['end']
def setEnd(self, time):
self.end = time
- self.dmesg[self.phases[-1]]['end'] = time
def isTraceEventOutsideDeviceCalls(self, pid, time):
for phase in self.phases:
list = self.dmesg[phase]['list']
@@ -733,39 +802,67 @@
time < d['end']):
return False
return True
- def targetDevice(self, phaselist, start, end, pid=-1):
+ def sourcePhase(self, start):
+ for phase in self.phases:
+ pend = self.dmesg[phase]['end']
+ if start <= pend:
+ return phase
+ return 'resume_complete'
+ def sourceDevice(self, phaselist, start, end, pid, type):
tgtdev = ''
for phase in phaselist:
list = self.dmesg[phase]['list']
for devname in list:
dev = list[devname]
- if(pid >= 0 and dev['pid'] != pid):
+ # pid must match
+ if dev['pid'] != pid:
continue
devS = dev['start']
devE = dev['end']
- if(start < devS or start >= devE or end <= devS or end > devE):
- continue
+ if type == 'device':
+ # device target event is entirely inside the source boundary
+ if(start < devS or start >= devE or end <= devS or end > devE):
+ continue
+ elif type == 'thread':
+ # thread target event will expand the source boundary
+ if start < devS:
+ dev['start'] = start
+ if end > devE:
+ dev['end'] = end
tgtdev = dev
break
return tgtdev
def addDeviceFunctionCall(self, displayname, kprobename, proc, pid, start, end, cdata, rdata):
- machstart = self.dmesg['suspend_machine']['start']
- machend = self.dmesg['resume_machine']['end']
- tgtdev = self.targetDevice(self.phases, start, end, pid)
- if not tgtdev and start >= machstart and end < machend:
- # device calls in machine phases should be serial
- tgtdev = self.targetDevice(['suspend_machine', 'resume_machine'], start, end)
- if not tgtdev:
- if 'scsi_eh' in proc:
- self.newActionGlobal(proc, start, end, pid)
- self.addDeviceFunctionCall(displayname, kprobename, proc, pid, start, end, cdata, rdata)
- else:
- vprint('IGNORE: %s[%s](%d) [%f - %f] | %s | %s | %s' % (displayname, kprobename,
- pid, start, end, cdata, rdata, proc))
+ # try to place the call in a device
+ tgtdev = self.sourceDevice(self.phases, start, end, pid, 'device')
+ # calls with device pids that occur outside device bounds are dropped
+ # TODO: include these somehow
+ if not tgtdev and pid in self.devpids:
return False
- # detail block fits within tgtdev
+ # try to place the call in a thread
+ if not tgtdev:
+ tgtdev = self.sourceDevice(self.phases, start, end, pid, 'thread')
+ # create new thread blocks, expand as new calls are found
+ if not tgtdev:
+ if proc == '<...>':
+ threadname = 'kthread-%d' % (pid)
+ else:
+ threadname = '%s-%d' % (proc, pid)
+ tgtphase = self.sourcePhase(start)
+ self.newAction(tgtphase, threadname, pid, '', start, end, '', ' kth', '')
+ return self.addDeviceFunctionCall(displayname, kprobename, proc, pid, start, end, cdata, rdata)
+ # this should not happen
+ if not tgtdev:
+ vprint('[%f - %f] %s-%d %s %s %s' % \
+ (start, end, proc, pid, kprobename, cdata, rdata))
+ return False
+ # place the call data inside the src element of the tgtdev
if('src' not in tgtdev):
tgtdev['src'] = []
+ dtf = sysvals.dev_tracefuncs
+ ubiquitous = False
+ if kprobename in dtf and 'ub' in dtf[kprobename]:
+ ubiquitous = True
title = cdata+' '+rdata
mstr = '\(.*\) *(?P<args>.*) *\((?P<caller>.*)\+.* arg1=(?P<ret>.*)'
m = re.match(mstr, title)
@@ -777,14 +874,81 @@
r = ''
else:
r = 'ret=%s ' % r
- l = '%0.3fms' % ((end - start) * 1000)
- if kprobename in self.dev_ubiquitous:
- title = '%s(%s) <- %s, %s(%s)' % (displayname, a, c, r, l)
- else:
- title = '%s(%s) %s(%s)' % (displayname, a, r, l)
- e = TraceEvent(title, kprobename, start, end - start)
+ if ubiquitous and c in dtf and 'ub' in dtf[c]:
+ return False
+ color = sysvals.kprobeColor(kprobename)
+ e = DevFunction(displayname, a, c, r, start, end, ubiquitous, proc, pid, color)
tgtdev['src'].append(e)
return True
+ def overflowDevices(self):
+ # get a list of devices that extend beyond the end of this test run
+ devlist = []
+ for phase in self.phases:
+ list = self.dmesg[phase]['list']
+ for devname in list:
+ dev = list[devname]
+ if dev['end'] > self.end:
+ devlist.append(dev)
+ return devlist
+ def mergeOverlapDevices(self, devlist):
+ # merge any devices that overlap devlist
+ for dev in devlist:
+ devname = dev['name']
+ for phase in self.phases:
+ list = self.dmesg[phase]['list']
+ if devname not in list:
+ continue
+ tdev = list[devname]
+ o = min(dev['end'], tdev['end']) - max(dev['start'], tdev['start'])
+ if o <= 0:
+ continue
+ dev['end'] = tdev['end']
+ if 'src' not in dev or 'src' not in tdev:
+ continue
+ dev['src'] += tdev['src']
+ del list[devname]
+ def usurpTouchingThread(self, name, dev):
+ # the caller test has priority of this thread, give it to him
+ for phase in self.phases:
+ list = self.dmesg[phase]['list']
+ if name in list:
+ tdev = list[name]
+ if tdev['start'] - dev['end'] < 0.1:
+ dev['end'] = tdev['end']
+ if 'src' not in dev:
+ dev['src'] = []
+ if 'src' in tdev:
+ dev['src'] += tdev['src']
+ del list[name]
+ break
+ def stitchTouchingThreads(self, testlist):
+ # merge any threads between tests that touch
+ for phase in self.phases:
+ list = self.dmesg[phase]['list']
+ for devname in list:
+ dev = list[devname]
+ if 'htmlclass' not in dev or 'kth' not in dev['htmlclass']:
+ continue
+ for data in testlist:
+ data.usurpTouchingThread(devname, dev)
+ def optimizeDevSrc(self):
+ # merge any src call loops to reduce timeline size
+ for phase in self.phases:
+ list = self.dmesg[phase]['list']
+ for dev in list:
+ if 'src' not in list[dev]:
+ continue
+ src = list[dev]['src']
+ p = 0
+ for e in sorted(src, key=lambda event: event.time):
+ if not p or not e.repeat(p):
+ p = e
+ continue
+ # e is another iteration of p, move it into p
+ p.end = e.end
+ p.length = p.end - p.time
+ p.count += 1
+ src.remove(e)
def trimTimeVal(self, t, t0, dT, left):
if left:
if(t > t0):
@@ -804,6 +968,8 @@
self.tSuspended = self.trimTimeVal(self.tSuspended, t0, dT, left)
self.tResumed = self.trimTimeVal(self.tResumed, t0, dT, left)
self.start = self.trimTimeVal(self.start, t0, dT, left)
+ self.tKernSus = self.trimTimeVal(self.tKernSus, t0, dT, left)
+ self.tKernRes = self.trimTimeVal(self.tKernRes, t0, dT, left)
self.end = self.trimTimeVal(self.end, t0, dT, left)
for phase in self.phases:
p = self.dmesg[phase]
@@ -832,36 +998,6 @@
else:
self.trimTime(self.tSuspended, \
self.tResumed-self.tSuspended, False)
- def newPhaseWithSingleAction(self, phasename, devname, start, end, color):
- for phase in self.phases:
- self.dmesg[phase]['order'] += 1
- self.html_device_id += 1
- devid = '%s%d' % (self.idstr, self.html_device_id)
- list = dict()
- list[devname] = \
- {'start': start, 'end': end, 'pid': 0, 'par': '',
- 'length': (end-start), 'row': 0, 'id': devid, 'drv': '' };
- self.dmesg[phasename] = \
- {'list': list, 'start': start, 'end': end,
- 'row': 0, 'color': color, 'order': 0}
- self.phases = self.sortedPhases()
- def newPhase(self, phasename, start, end, color, order):
- if(order < 0):
- order = len(self.phases)
- for phase in self.phases[order:]:
- self.dmesg[phase]['order'] += 1
- if(order > 0):
- p = self.phases[order-1]
- self.dmesg[p]['end'] = start
- if(order < len(self.phases)):
- p = self.phases[order]
- self.dmesg[p]['start'] = end
- list = dict()
- self.dmesg[phasename] = \
- {'list': list, 'start': start, 'end': end,
- 'row': 0, 'color': color, 'order': order}
- self.phases = self.sortedPhases()
- self.devicegroups.append([phasename])
def setPhase(self, phase, ktime, isbegin):
if(isbegin):
self.dmesg[phase]['start'] = ktime
@@ -881,7 +1017,7 @@
for t in sorted(tmp):
slist.append(tmp[t])
return slist
- def fixupInitcalls(self, phase, end):
+ def fixupInitcalls(self, phase):
# if any calls never returned, clip them at system resume end
phaselist = self.dmesg[phase]['list']
for devname in phaselist:
@@ -893,37 +1029,23 @@
break
vprint('%s (%s): callback didnt return' % (devname, phase))
def deviceFilter(self, devicefilter):
- # remove all by the relatives of the filter devnames
- filter = []
- for phase in self.phases:
- list = self.dmesg[phase]['list']
- for name in devicefilter:
- dev = name
- while(dev in list):
- if(dev not in filter):
- filter.append(dev)
- dev = list[dev]['par']
- children = self.deviceDescendants(name, phase)
- for dev in children:
- if(dev not in filter):
- filter.append(dev)
for phase in self.phases:
list = self.dmesg[phase]['list']
rmlist = []
for name in list:
- pid = list[name]['pid']
- if(name not in filter and pid >= 0):
+ keep = False
+ for filter in devicefilter:
+ if filter in name or \
+ ('drv' in list[name] and filter in list[name]['drv']):
+ keep = True
+ if not keep:
rmlist.append(name)
for name in rmlist:
del list[name]
def fixupInitcallsThatDidntReturn(self):
# if any calls never returned, clip them at system resume end
for phase in self.phases:
- self.fixupInitcalls(phase, self.getEnd())
- def isInsideTimeline(self, start, end):
- if(self.start <= start and self.end > start):
- return True
- return False
+ self.fixupInitcalls(phase)
def phaseOverlap(self, phases):
rmgroups = []
newgroup = []
@@ -940,30 +1062,35 @@
self.devicegroups.remove(group)
self.devicegroups.append(newgroup)
def newActionGlobal(self, name, start, end, pid=-1, color=''):
- # if event starts before timeline start, expand timeline
- if(start < self.start):
- self.setStart(start)
- # if event ends after timeline end, expand the timeline
- if(end > self.end):
- self.setEnd(end)
- # which phase is this device callback or action "in"
- targetphase = "none"
+ # which phase is this device callback or action in
+ targetphase = 'none'
htmlclass = ''
overlap = 0.0
phases = []
for phase in self.phases:
pstart = self.dmesg[phase]['start']
pend = self.dmesg[phase]['end']
+ # see if the action overlaps this phase
o = max(0, min(end, pend) - max(start, pstart))
if o > 0:
phases.append(phase)
+ # set the target phase to the one that overlaps most
if o > overlap:
if overlap > 0 and phase == 'post_resume':
continue
targetphase = phase
overlap = o
+ # if no target phase was found, pin it to the edge
+ if targetphase == 'none':
+ p0start = self.dmesg[self.phases[0]]['start']
+ if start <= p0start:
+ targetphase = self.phases[0]
+ else:
+ targetphase = self.phases[-1]
if pid == -2:
htmlclass = ' bg'
+ elif pid == -3:
+ htmlclass = ' ps'
if len(phases) > 1:
htmlclass = ' bg'
self.phaseOverlap(phases)
@@ -985,29 +1112,13 @@
while(name in list):
name = '%s[%d]' % (origname, i)
i += 1
- list[name] = {'start': start, 'end': end, 'pid': pid, 'par': parent,
- 'length': length, 'row': 0, 'id': devid, 'drv': drv }
+ list[name] = {'name': name, 'start': start, 'end': end, 'pid': pid,
+ 'par': parent, 'length': length, 'row': 0, 'id': devid, 'drv': drv }
if htmlclass:
list[name]['htmlclass'] = htmlclass
if color:
list[name]['color'] = color
return name
- def deviceIDs(self, devlist, phase):
- idlist = []
- list = self.dmesg[phase]['list']
- for devname in list:
- if devname in devlist:
- idlist.append(list[devname]['id'])
- return idlist
- def deviceParentID(self, devname, phase):
- pdev = ''
- pdevid = ''
- list = self.dmesg[phase]['list']
- if devname in list:
- pdev = list[devname]['par']
- if pdev in list:
- return list[pdev]['id']
- return pdev
def deviceChildren(self, devname, phase):
devlist = []
list = self.dmesg[phase]['list']
@@ -1015,21 +1126,15 @@
if(list[child]['par'] == devname):
devlist.append(child)
return devlist
- def deviceDescendants(self, devname, phase):
- children = self.deviceChildren(devname, phase)
- family = children
- for child in children:
- family += self.deviceDescendants(child, phase)
- return family
- def deviceChildrenIDs(self, devname, phase):
- devlist = self.deviceChildren(devname, phase)
- return self.deviceIDs(devlist, phase)
def printDetails(self):
+ vprint('Timeline Details:')
vprint(' test start: %f' % self.start)
+ vprint('kernel suspend start: %f' % self.tKernSus)
for phase in self.phases:
dc = len(self.dmesg[phase]['list'])
vprint(' %16s: %f - %f (%d devices)' % (phase, \
self.dmesg[phase]['start'], self.dmesg[phase]['end'], dc))
+ vprint(' kernel resume end: %f' % self.tKernRes)
vprint(' test end: %f' % self.end)
def deviceChildrenAllPhases(self, devname):
devlist = []
@@ -1108,21 +1213,134 @@
if width != '0.000000' and length >= mindevlen:
devlist.append(dev)
self.tdevlist[phase] = devlist
+ def addHorizontalDivider(self, devname, devend):
+ phase = 'suspend_prepare'
+ self.newAction(phase, devname, -2, '', \
+ self.start, devend, '', ' sec', '')
+ if phase not in self.tdevlist:
+ self.tdevlist[phase] = []
+ self.tdevlist[phase].append(devname)
+ d = DevItem(0, phase, self.dmesg[phase]['list'][devname])
+ return d
+ def addProcessUsageEvent(self, name, times):
+ # get the start and end times for this process
+ maxC = 0
+ tlast = 0
+ start = -1
+ end = -1
+ for t in sorted(times):
+ if tlast == 0:
+ tlast = t
+ continue
+ if name in self.pstl[t]:
+ if start == -1 or tlast < start:
+ start = tlast
+ if end == -1 or t > end:
+ end = t
+ tlast = t
+ if start == -1 or end == -1:
+ return 0
+ # add a new action for this process and get the object
+ out = self.newActionGlobal(name, start, end, -3)
+ if not out:
+ return 0
+ phase, devname = out
+ dev = self.dmesg[phase]['list'][devname]
+ # get the cpu exec data
+ tlast = 0
+ clast = 0
+ cpuexec = dict()
+ for t in sorted(times):
+ if tlast == 0 or t <= start or t > end:
+ tlast = t
+ continue
+ list = self.pstl[t]
+ c = 0
+ if name in list:
+ c = list[name]
+ if c > maxC:
+ maxC = c
+ if c != clast:
+ key = (tlast, t)
+ cpuexec[key] = c
+ tlast = t
+ clast = c
+ dev['cpuexec'] = cpuexec
+ return maxC
+ def createProcessUsageEvents(self):
+ # get an array of process names
+ proclist = []
+ for t in self.pstl:
+ pslist = self.pstl[t]
+ for ps in pslist:
+ if ps not in proclist:
+ proclist.append(ps)
+ # get a list of data points for suspend and resume
+ tsus = []
+ tres = []
+ for t in sorted(self.pstl):
+ if t < self.tSuspended:
+ tsus.append(t)
+ else:
+ tres.append(t)
+ # process the events for suspend and resume
+ if len(proclist) > 0:
+ vprint('Process Execution:')
+ for ps in proclist:
+ c = self.addProcessUsageEvent(ps, tsus)
+ if c > 0:
+ vprint('%25s (sus): %d' % (ps, c))
+ c = self.addProcessUsageEvent(ps, tres)
+ if c > 0:
+ vprint('%25s (res): %d' % (ps, c))
-# Class: TraceEvent
+# Class: DevFunction
# Description:
-# A container for trace event data found in the ftrace file
-class TraceEvent:
- text = ''
- time = 0.0
- length = 0.0
- title = ''
+# A container for kprobe function data we want in the dev timeline
+class DevFunction:
row = 0
- def __init__(self, a, n, t, l):
- self.title = a
- self.text = n
- self.time = t
- self.length = l
+ count = 1
+ def __init__(self, name, args, caller, ret, start, end, u, proc, pid, color):
+ self.name = name
+ self.args = args
+ self.caller = caller
+ self.ret = ret
+ self.time = start
+ self.length = end - start
+ self.end = end
+ self.ubiquitous = u
+ self.proc = proc
+ self.pid = pid
+ self.color = color
+ def title(self):
+ cnt = ''
+ if self.count > 1:
+ cnt = '(x%d)' % self.count
+ l = '%0.3fms' % (self.length * 1000)
+ if self.ubiquitous:
+ title = '%s(%s)%s <- %s, %s(%s)' % \
+ (self.name, self.args, cnt, self.caller, self.ret, l)
+ else:
+ title = '%s(%s) %s%s(%s)' % (self.name, self.args, self.ret, cnt, l)
+ return title.replace('"', '')
+ def text(self):
+ if self.count > 1:
+ text = '%s(x%d)' % (self.name, self.count)
+ else:
+ text = self.name
+ return text
+ def repeat(self, tgt):
+ # is the tgt call just a repeat of this call (e.g. are we in a loop)
+ dt = self.time - tgt.end
+ # only combine calls if -all- attributes are identical
+ if tgt.caller == self.caller and \
+ tgt.name == self.name and tgt.args == self.args and \
+ tgt.proc == self.proc and tgt.pid == self.pid and \
+ tgt.ret == self.ret and dt >= 0 and \
+ dt <= sysvals.callloopmaxgap and \
+ self.length < sysvals.callloopmaxlen:
+ return True
+ return False
# Class: FTraceLine
# Description:
@@ -1226,7 +1444,6 @@
print('%s -- %f (%02d): %s() { (%.3f us)' % (dev, self.time, \
self.depth, self.name, self.length*1000000))
def startMarker(self):
- global sysvals
# Is this the starting line of a suspend?
if not self.fevent:
return False
@@ -1506,6 +1723,16 @@
l.depth, l.name, l.length*1000000))
print(' ')
+class DevItem:
+ def __init__(self, test, phase, dev):
+ self.test = test
+ self.phase = phase
+ self.dev = dev
+ def isa(self, cls):
+ if 'htmlclass' in self.dev and cls in self.dev['htmlclass']:
+ return True
+ return False
+
# Class: Timeline
# Description:
# A container for a device timeline which calculates
@@ -1517,12 +1744,11 @@
rowH = 30 # device row height
bodyH = 0 # body height
rows = 0 # total timeline rows
- phases = []
- rowmaxlines = dict()
- rowcount = dict()
+ rowlines = dict()
rowheight = dict()
- def __init__(self, rowheight):
+ def __init__(self, rowheight, scaleheight):
self.rowH = rowheight
+ self.scaleH = scaleheight
self.html = {
'header': '',
'timeline': '',
@@ -1537,21 +1763,19 @@
# The total number of rows needed to display this phase of the timeline
def getDeviceRows(self, rawlist):
# clear all rows and set them to undefined
- lendict = dict()
+ sortdict = dict()
for item in rawlist:
item.row = -1
- lendict[item] = item.length
- list = []
- for i in sorted(lendict, key=lendict.get, reverse=True):
- list.append(i)
- remaining = len(list)
+ sortdict[item] = item.length
+ sortlist = sorted(sortdict, key=sortdict.get, reverse=True)
+ remaining = len(sortlist)
rowdata = dict()
row = 1
# try to pack each row with as many ranges as possible
while(remaining > 0):
if(row not in rowdata):
rowdata[row] = []
- for i in list:
+ for i in sortlist:
if(i.row >= 0):
continue
s = i.time
@@ -1575,81 +1799,86 @@
# Organize the timeline entries into the smallest
# number of rows possible, with no entry overlapping
# Arguments:
- # list: the list of devices/actions for a single phase
- # devlist: string list of device names to use
+ # devlist: the list of devices/actions in a group of contiguous phases
# Output:
# The total number of rows needed to display this phase of the timeline
- def getPhaseRows(self, dmesg, devlist):
+ def getPhaseRows(self, devlist, row=0):
# clear all rows and set them to undefined
remaining = len(devlist)
rowdata = dict()
- row = 0
- lendict = dict()
+ sortdict = dict()
myphases = []
+ # initialize all device rows to -1 and calculate devrows
for item in devlist:
- if item[0] not in self.phases:
- self.phases.append(item[0])
- if item[0] not in myphases:
- myphases.append(item[0])
- self.rowmaxlines[item[0]] = dict()
- self.rowheight[item[0]] = dict()
- dev = dmesg[item[0]]['list'][item[1]]
+ dev = item.dev
+ tp = (item.test, item.phase)
+ if tp not in myphases:
+ myphases.append(tp)
dev['row'] = -1
- lendict[item] = float(dev['end']) - float(dev['start'])
+ # sort by length 1st, then name 2nd
+ sortdict[item] = (float(dev['end']) - float(dev['start']), item.dev['name'])
if 'src' in dev:
dev['devrows'] = self.getDeviceRows(dev['src'])
- lenlist = []
- for i in sorted(lendict, key=lendict.get, reverse=True):
- lenlist.append(i)
+ # sort the devlist by length so that large items graph on top
+ sortlist = sorted(sortdict, key=sortdict.get, reverse=True)
orderedlist = []
- for item in lenlist:
- dev = dmesg[item[0]]['list'][item[1]]
- if dev['pid'] == -2:
+ for item in sortlist:
+ if item.dev['pid'] == -2:
orderedlist.append(item)
- for item in lenlist:
+ for item in sortlist:
if item not in orderedlist:
orderedlist.append(item)
- # try to pack each row with as many ranges as possible
+ # try to pack each row with as many devices as possible
while(remaining > 0):
rowheight = 1
if(row not in rowdata):
rowdata[row] = []
for item in orderedlist:
- dev = dmesg[item[0]]['list'][item[1]]
+ dev = item.dev
if(dev['row'] < 0):
s = dev['start']
e = dev['end']
valid = True
for ritem in rowdata[row]:
- rs = ritem['start']
- re = ritem['end']
+ rs = ritem.dev['start']
+ re = ritem.dev['end']
if(not (((s <= rs) and (e <= rs)) or
((s >= re) and (e >= re)))):
valid = False
break
if(valid):
- rowdata[row].append(dev)
+ rowdata[row].append(item)
dev['row'] = row
remaining -= 1
if 'devrows' in dev and dev['devrows'] > rowheight:
rowheight = dev['devrows']
- for phase in myphases:
- self.rowmaxlines[phase][row] = rowheight
- self.rowheight[phase][row] = rowheight * self.rowH
+ for t, p in myphases:
+ if t not in self.rowlines or t not in self.rowheight:
+ self.rowlines[t] = dict()
+ self.rowheight[t] = dict()
+ if p not in self.rowlines[t] or p not in self.rowheight[t]:
+ self.rowlines[t][p] = dict()
+ self.rowheight[t][p] = dict()
+ rh = self.rowH
+ # section headers should use a different row height
+ if len(rowdata[row]) == 1 and \
+ 'htmlclass' in rowdata[row][0].dev and \
+ 'sec' in rowdata[row][0].dev['htmlclass']:
+ rh = 15
+ self.rowlines[t][p][row] = rowheight
+ self.rowheight[t][p][row] = rowheight * rh
row += 1
if(row > self.rows):
self.rows = int(row)
- for phase in myphases:
- self.rowcount[phase] = row
return row
- def phaseRowHeight(self, phase, row):
- return self.rowheight[phase][row]
- def phaseRowTop(self, phase, row):
+ def phaseRowHeight(self, test, phase, row):
+ return self.rowheight[test][phase][row]
+ def phaseRowTop(self, test, phase, row):
top = 0
- for i in sorted(self.rowheight[phase]):
+ for i in sorted(self.rowheight[test][phase]):
if i >= row:
break
- top += self.rowheight[phase][i]
+ top += self.rowheight[test][phase][i]
return top
# Function: calcTotalRows
# Description:
@@ -1657,19 +1886,21 @@
def calcTotalRows(self):
maxrows = 0
standardphases = []
- for phase in self.phases:
- total = 0
- for i in sorted(self.rowmaxlines[phase]):
- total += self.rowmaxlines[phase][i]
- if total > maxrows:
- maxrows = total
- if total == self.rowcount[phase]:
- standardphases.append(phase)
+ for t in self.rowlines:
+ for p in self.rowlines[t]:
+ total = 0
+ for i in sorted(self.rowlines[t][p]):
+ total += self.rowlines[t][p][i]
+ if total > maxrows:
+ maxrows = total
+ if total == len(self.rowlines[t][p]):
+ standardphases.append((t, p))
self.height = self.scaleH + (maxrows*self.rowH)
self.bodyH = self.height - self.scaleH
- for phase in standardphases:
- for i in sorted(self.rowheight[phase]):
- self.rowheight[phase][i] = self.bodyH/self.rowcount[phase]
+ # if there is 1 line per row, draw them the standard way
+ for t, p in standardphases:
+ for i in sorted(self.rowheight[t][p]):
+ self.rowheight[t][p][i] = self.bodyH/len(self.rowlines[t][p])
# Function: createTimeScale
# Description:
# Create the timescale for a timeline block
@@ -1716,7 +1947,6 @@
# A list of values describing the properties of these test runs
class TestProps:
stamp = ''
- tracertype = ''
S0i3 = False
fwdata = []
ftrace_line_fmt_fg = \
@@ -1734,14 +1964,13 @@
def __init__(self):
self.ktemp = dict()
def setTracerType(self, tracer):
- self.tracertype = tracer
if(tracer == 'function_graph'):
self.cgformat = True
self.ftrace_line_fmt = self.ftrace_line_fmt_fg
elif(tracer == 'nop'):
self.ftrace_line_fmt = self.ftrace_line_fmt_nop
else:
- doError('Invalid tracer format: [%s]' % tracer, False)
+ doError('Invalid tracer format: [%s]' % tracer)
# Class: TestRun
# Description:
@@ -1756,6 +1985,51 @@
self.ftemp = dict()
self.ttemp = dict()
+class ProcessMonitor:
+ proclist = dict()
+ running = False
+ def procstat(self):
+ c = ['cat /proc/[1-9]*/stat 2>/dev/null']
+ process = Popen(c, shell=True, stdout=PIPE)
+ running = dict()
+ for line in process.stdout:
+ data = line.split()
+ pid = data[0]
+ name = re.sub('[()]', '', data[1])
+ user = int(data[13])
+ kern = int(data[14])
+ kjiff = ujiff = 0
+ if pid not in self.proclist:
+ self.proclist[pid] = {'name' : name, 'user' : user, 'kern' : kern}
+ else:
+ val = self.proclist[pid]
+ ujiff = user - val['user']
+ kjiff = kern - val['kern']
+ val['user'] = user
+ val['kern'] = kern
+ if ujiff > 0 or kjiff > 0:
+ running[pid] = ujiff + kjiff
+ result = process.wait()
+ out = ''
+ for pid in running:
+ jiffies = running[pid]
+ val = self.proclist[pid]
+ if out:
+ out += ','
+ out += '%s-%s %d' % (val['name'], pid, jiffies)
+ return 'ps - '+out
+ def processMonitor(self, tid):
+ while self.running:
+ out = self.procstat()
+ if out:
+ sysvals.fsetVal(out, 'trace_marker')
+ def start(self):
+ self.thread = Thread(target=self.processMonitor, args=(0,))
+ self.running = True
+ self.thread.start()
+ def stop(self):
+ self.running = False
+
# ----------------- FUNCTIONS --------------------
# Function: vprint
@@ -1764,7 +2038,7 @@
# Arguments:
# msg: the debug/log message to print
def vprint(msg):
- global sysvals
+ sysvals.logmsg += msg+'\n'
if(sysvals.verbose):
print(msg)
@@ -1775,8 +2049,6 @@
# Arguments:
# m: the valid re.match output for the stamp line
def parseStamp(line, data):
- global sysvals
-
m = re.match(sysvals.stampfmt, line)
data.stamp = {'time': '', 'host': '', 'mode': ''}
dt = datetime(int(m.group('y'))+2000, int(m.group('m')),
@@ -1788,6 +2060,14 @@
data.stamp['kernel'] = m.group('kernel')
sysvals.hostname = data.stamp['host']
sysvals.suspendmode = data.stamp['mode']
+ if sysvals.suspendmode == 'command' and sysvals.ftracefile != '':
+ modes = ['on', 'freeze', 'standby', 'mem']
+ out = Popen(['grep', 'suspend_enter', sysvals.ftracefile],
+ stderr=PIPE, stdout=PIPE).stdout.read()
+ m = re.match('.* suspend_enter\[(?P<mode>.*)\]', out)
+ if m and m.group('mode') in ['1', '2', '3']:
+ sysvals.suspendmode = modes[int(m.group('mode'))]
+ data.stamp['mode'] = sysvals.suspendmode
if not sysvals.stamp:
sysvals.stamp = data.stamp
@@ -1817,18 +2097,17 @@
# required for primary parsing. Set the usetraceevents and/or
# usetraceeventsonly flags in the global sysvals object
def doesTraceLogHaveTraceEvents():
- global sysvals
-
# check for kprobes
sysvals.usekprobes = False
- out = os.system('grep -q "_cal: (" '+sysvals.ftracefile)
+ out = call('grep -q "_cal: (" '+sysvals.ftracefile, shell=True)
if(out == 0):
sysvals.usekprobes = True
# check for callgraph data on trace event blocks
- out = os.system('grep -q "_cpu_down()" '+sysvals.ftracefile)
+ out = call('grep -q "_cpu_down()" '+sysvals.ftracefile, shell=True)
if(out == 0):
sysvals.usekprobes = True
- out = os.popen('head -1 '+sysvals.ftracefile).read().replace('\n', '')
+ out = Popen(['head', '-1', sysvals.ftracefile],
+ stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '')
m = re.match(sysvals.stampfmt, out)
if m and m.group('mode') == 'command':
sysvals.usetraceeventsonly = True
@@ -1838,14 +2117,14 @@
sysvals.usetraceeventsonly = True
sysvals.usetraceevents = False
for e in sysvals.traceevents:
- out = os.system('grep -q "'+e+': " '+sysvals.ftracefile)
+ out = call('grep -q "'+e+': " '+sysvals.ftracefile, shell=True)
if(out != 0):
sysvals.usetraceeventsonly = False
if(e == 'suspend_resume' and out == 0):
sysvals.usetraceevents = True
# determine is this log is properly formatted
for e in ['SUSPEND START', 'RESUME COMPLETE']:
- out = os.system('grep -q "'+e+'" '+sysvals.ftracefile)
+ out = call('grep -q "'+e+'" '+sysvals.ftracefile, shell=True)
if(out != 0):
sysvals.usetracemarkers = False
@@ -1860,8 +2139,6 @@
# Arguments:
# testruns: the array of Data objects obtained from parseKernelLog
def appendIncompleteTraceLog(testruns):
- global sysvals
-
# create TestRun vessels for ftrace parsing
testcnt = len(testruns)
testidx = 0
@@ -2052,8 +2329,7 @@
dev['ftrace'] = cg
break
- if(sysvals.verbose):
- test.data.printDetails()
+ test.data.printDetails()
# Function: parseTraceLog
# Description:
@@ -2064,14 +2340,12 @@
# Output:
# An array of Data objects
def parseTraceLog():
- global sysvals
-
vprint('Analyzing the ftrace data...')
if(os.path.exists(sysvals.ftracefile) == False):
- doError('%s does not exist' % sysvals.ftracefile, False)
+ doError('%s does not exist' % sysvals.ftracefile)
sysvals.setupAllKprobes()
- tracewatch = ['suspend_enter']
+ tracewatch = []
if sysvals.usekprobes:
tracewatch += ['sync_filesystems', 'freeze_processes', 'syscore_suspend',
'syscore_resume', 'resume_console', 'thaw_processes', 'CPU_ON', 'CPU_OFF']
@@ -2102,17 +2376,13 @@
if(m):
tp.setTracerType(m.group('t'))
continue
- # post resume time line: did this test run include post-resume data
- m = re.match(sysvals.postresumefmt, line)
- if(m):
- t = int(m.group('t'))
- if(t > 0):
- sysvals.postresumetime = t
- continue
# device properties line
if(re.match(sysvals.devpropfmt, line)):
devProps(line)
continue
+ # ignore all other commented lines
+ if line[0] == '#':
+ continue
# ftrace line: parse only valid lines
m = re.match(tp.ftrace_line_fmt, line)
if(not m):
@@ -2142,20 +2412,36 @@
testrun = TestRun(data)
testruns.append(testrun)
parseStamp(tp.stamp, data)
- if len(tp.fwdata) > data.testnumber:
- data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber]
- if(data.fwSuspend > 0 or data.fwResume > 0):
- data.fwValid = True
data.setStart(t.time)
+ data.tKernSus = t.time
continue
if(not data):
continue
+ # process cpu exec line
+ if t.type == 'tracing_mark_write':
+ m = re.match(sysvals.procexecfmt, t.name)
+ if(m):
+ proclist = dict()
+ for ps in m.group('ps').split(','):
+ val = ps.split()
+ if not val:
+ continue
+ name = val[0].replace('--', '-')
+ proclist[name] = int(val[1])
+ data.pstl[t.time] = proclist
+ continue
# find the end of resume
if(t.endMarker()):
- if(sysvals.usetracemarkers and sysvals.postresumetime > 0):
- phase = 'post_resume'
- data.newPhase(phase, t.time, t.time, '#F0F0F0', -1)
data.setEnd(t.time)
+ if data.tKernRes == 0.0:
+ data.tKernRes = t.time
+ if data.dmesg['resume_complete']['end'] < 0:
+ data.dmesg['resume_complete']['end'] = t.time
+ if sysvals.suspendmode == 'mem' and len(tp.fwdata) > data.testnumber:
+ data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber]
+ if(data.tSuspended != 0 and data.tResumed != 0 and \
+ (data.fwSuspend > 0 or data.fwResume > 0)):
+ data.fwValid = True
if(not sysvals.usetracemarkers):
# no trace markers? then quit and be sure to finish recording
# the event we used to trigger resume end
@@ -2190,8 +2476,14 @@
if(name.split('[')[0] in tracewatch):
continue
# -- phase changes --
+ # start of kernel suspend
+ if(re.match('suspend_enter\[.*', t.name)):
+ if(isbegin):
+ data.dmesg[phase]['start'] = t.time
+ data.tKernSus = t.time
+ continue
# suspend_prepare start
- if(re.match('dpm_prepare\[.*', t.name)):
+ elif(re.match('dpm_prepare\[.*', t.name)):
phase = 'suspend_prepare'
if(not isbegin):
data.dmesg[phase]['end'] = t.time
@@ -2291,6 +2583,8 @@
p = m.group('p')
if(n and p):
data.newAction(phase, n, pid, p, t.time, -1, drv)
+ if pid not in data.devpids:
+ data.devpids.append(pid)
# device callback finish
elif(t.type == 'device_pm_callback_end'):
m = re.match('(?P<drv>.*) (?P<d>.*), err.*', t.name);
@@ -2332,6 +2626,12 @@
else:
e['end'] = t.time
e['rdata'] = kprobedata
+ # end of kernel resume
+ if(kprobename == 'pm_notifier_call_chain' or \
+ kprobename == 'pm_restore_console'):
+ data.dmesg[phase]['end'] = t.time
+ data.tKernRes = t.time
+
# callgraph processing
elif sysvals.usecallgraph:
# create a callgraph object for the data
@@ -2348,24 +2648,37 @@
if sysvals.suspendmode == 'command':
for test in testruns:
for p in test.data.phases:
- if p == 'resume_complete':
+ if p == 'suspend_prepare':
test.data.dmesg[p]['start'] = test.data.start
test.data.dmesg[p]['end'] = test.data.end
else:
- test.data.dmesg[p]['start'] = test.data.start
- test.data.dmesg[p]['end'] = test.data.start
- test.data.tSuspended = test.data.start
- test.data.tResumed = test.data.start
+ test.data.dmesg[p]['start'] = test.data.end
+ test.data.dmesg[p]['end'] = test.data.end
+ test.data.tSuspended = test.data.end
+ test.data.tResumed = test.data.end
test.data.tLow = 0
test.data.fwValid = False
- for test in testruns:
+ # dev source and procmon events can be unreadable with mixed phase height
+ if sysvals.usedevsrc or sysvals.useprocmon:
+ sysvals.mixedphaseheight = False
+
+ for i in range(len(testruns)):
+ test = testruns[i]
+ data = test.data
+ # find the total time range for this test (begin, end)
+ tlb, tle = data.start, data.end
+ if i < len(testruns) - 1:
+ tle = testruns[i+1].data.start
+ # add the process usage data to the timeline
+ if sysvals.useprocmon:
+ data.createProcessUsageEvents()
# add the traceevent data to the device hierarchy
if(sysvals.usetraceevents):
# add actual trace funcs
for name in test.ttemp:
for event in test.ttemp[name]:
- test.data.newActionGlobal(name, event['begin'], event['end'], event['pid'])
+ data.newActionGlobal(name, event['begin'], event['end'], event['pid'])
# add the kprobe based virtual tracefuncs as actual devices
for key in tp.ktemp:
name, pid = key
@@ -2373,24 +2686,20 @@
continue
for e in tp.ktemp[key]:
kb, ke = e['begin'], e['end']
- if kb == ke or not test.data.isInsideTimeline(kb, ke):
+ if kb == ke or tlb > kb or tle <= kb:
continue
- test.data.newActionGlobal(e['name'], kb, ke, pid)
+ color = sysvals.kprobeColor(name)
+ data.newActionGlobal(e['name'], kb, ke, pid, color)
# add config base kprobes and dev kprobes
- for key in tp.ktemp:
- name, pid = key
- if name in sysvals.tracefuncs:
- continue
- for e in tp.ktemp[key]:
- kb, ke = e['begin'], e['end']
- if kb == ke or not test.data.isInsideTimeline(kb, ke):
+ if sysvals.usedevsrc:
+ for key in tp.ktemp:
+ name, pid = key
+ if name in sysvals.tracefuncs or name not in sysvals.dev_tracefuncs:
continue
- color = sysvals.kprobeColor(e['name'])
- if name not in sysvals.dev_tracefuncs:
- # config base kprobe
- test.data.newActionGlobal(e['name'], kb, ke, -2, color)
- elif sysvals.usedevsrc:
- # dev kprobe
+ for e in tp.ktemp[key]:
+ kb, ke = e['begin'], e['end']
+ if kb == ke or tlb > kb or tle <= kb:
+ continue
data.addDeviceFunctionCall(e['name'], name, e['proc'], pid, kb,
ke, e['cdata'], e['rdata'])
if sysvals.usecallgraph:
@@ -2407,7 +2716,7 @@
id+', ignoring this callback')
continue
# match cg data to devices
- if sysvals.suspendmode == 'command' or not cg.deviceMatch(pid, test.data):
+ if sysvals.suspendmode == 'command' or not cg.deviceMatch(pid, data):
sortkey = '%f%f%d' % (cg.start, cg.end, pid)
sortlist[sortkey] = cg
# create blocks for orphan cg data
@@ -2416,12 +2725,11 @@
name = cg.list[0].name
if sysvals.isCallgraphFunc(name):
vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name))
- cg.newActionFromFunction(test.data)
+ cg.newActionFromFunction(data)
if sysvals.suspendmode == 'command':
- if(sysvals.verbose):
- for data in testdata:
- data.printDetails()
+ for data in testdata:
+ data.printDetails()
return testdata
# fill in any missing phases
@@ -2429,7 +2737,7 @@
lp = data.phases[0]
for p in data.phases:
if(data.dmesg[p]['start'] < 0 and data.dmesg[p]['end'] < 0):
- print('WARNING: phase "%s" is missing!' % p)
+ vprint('WARNING: phase "%s" is missing!' % p)
if(data.dmesg[p]['start'] < 0):
data.dmesg[p]['start'] = data.dmesg[lp]['end']
if(p == 'resume_machine'):
@@ -2438,60 +2746,27 @@
data.tLow = 0
if(data.dmesg[p]['end'] < 0):
data.dmesg[p]['end'] = data.dmesg[p]['start']
+ if(p != lp and not ('machine' in p and 'machine' in lp)):
+ data.dmesg[lp]['end'] = data.dmesg[p]['start']
lp = p
if(len(sysvals.devicefilter) > 0):
data.deviceFilter(sysvals.devicefilter)
data.fixupInitcallsThatDidntReturn()
- if(sysvals.verbose):
- data.printDetails()
+ if sysvals.usedevsrc:
+ data.optimizeDevSrc()
+ data.printDetails()
+ # x2: merge any overlapping devices between test runs
+ if sysvals.usedevsrc and len(testdata) > 1:
+ tc = len(testdata)
+ for i in range(tc - 1):
+ devlist = testdata[i].overflowDevices()
+ for j in range(i + 1, tc):
+ testdata[j].mergeOverlapDevices(devlist)
+ testdata[0].stitchTouchingThreads(testdata[1:])
return testdata
-# Function: loadRawKernelLog
-# Description:
-# Load a raw kernel log that wasn't created by this tool, it might be
-# possible to extract a valid suspend/resume log
-def loadRawKernelLog(dmesgfile):
- global sysvals
-
- stamp = {'time': '', 'host': '', 'mode': 'mem', 'kernel': ''}
- stamp['time'] = datetime.now().strftime('%B %d %Y, %I:%M:%S %p')
- stamp['host'] = sysvals.hostname
-
- testruns = []
- data = 0
- lf = open(dmesgfile, 'r')
- for line in lf:
- line = line.replace('\r\n', '')
- idx = line.find('[')
- if idx > 1:
- line = line[idx:]
- m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
- if(not m):
- continue
- msg = m.group("msg")
- m = re.match('PM: Syncing filesystems.*', msg)
- if(m):
- if(data):
- testruns.append(data)
- data = Data(len(testruns))
- data.stamp = stamp
- if(data):
- m = re.match('.* *(?P<k>[0-9]\.[0-9]{2}\.[0-9]-.*) .*', msg)
- if(m):
- stamp['kernel'] = m.group('k')
- m = re.match('PM: Preparing system for (?P<m>.*) sleep', msg)
- if(m):
- stamp['mode'] = m.group('m')
- data.dmesgtext.append(line)
- if(data):
- testruns.append(data)
- sysvals.stamp = stamp
- sysvals.suspendmode = stamp['mode']
- lf.close()
- return testruns
-
# Function: loadKernelLog
# Description:
# [deprecated for kernel 3.15.0 or newer]
@@ -2499,15 +2774,16 @@
# The dmesg filename is taken from sysvals
# Output:
# An array of empty Data objects with only their dmesgtext attributes set
-def loadKernelLog():
- global sysvals
-
+def loadKernelLog(justtext=False):
vprint('Analyzing the dmesg data...')
if(os.path.exists(sysvals.dmesgfile) == False):
- doError('%s does not exist' % sysvals.dmesgfile, False)
+ doError('%s does not exist' % sysvals.dmesgfile)
+ if justtext:
+ dmesgtext = []
# there can be multiple test runs in a single file
tp = TestProps()
+ tp.stamp = datetime.now().strftime('# suspend-%m%d%y-%H%M%S localhost mem unknown')
testruns = []
data = 0
lf = open(sysvals.dmesgfile, 'r')
@@ -2528,6 +2804,9 @@
if(not m):
continue
msg = m.group("msg")
+ if justtext:
+ dmesgtext.append(line)
+ continue
if(re.match('PM: Syncing filesystems.*', msg)):
if(data):
testruns.append(data)
@@ -2537,24 +2816,24 @@
data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber]
if(data.fwSuspend > 0 or data.fwResume > 0):
data.fwValid = True
- if(re.match('ACPI: resume from mwait', msg)):
- print('NOTE: This suspend appears to be freeze rather than'+\
- ' %s, it will be treated as such' % sysvals.suspendmode)
- sysvals.suspendmode = 'freeze'
if(not data):
continue
+ m = re.match('.* *(?P<k>[0-9]\.[0-9]{2}\.[0-9]-.*) .*', msg)
+ if(m):
+ sysvals.stamp['kernel'] = m.group('k')
+ m = re.match('PM: Preparing system for (?P<m>.*) sleep', msg)
+ if(m):
+ sysvals.stamp['mode'] = sysvals.suspendmode = m.group('m')
data.dmesgtext.append(line)
- if(data):
- testruns.append(data)
lf.close()
- if(len(testruns) < 1):
- # bad log, but see if you can extract something meaningful anyway
- testruns = loadRawKernelLog(sysvals.dmesgfile)
-
- if(len(testruns) < 1):
- doError(' dmesg log is completely unreadable: %s' \
- % sysvals.dmesgfile, False)
+ if justtext:
+ return dmesgtext
+ if data:
+ testruns.append(data)
+ if len(testruns) < 1:
+ doError(' dmesg log has no suspend/resume data: %s' \
+ % sysvals.dmesgfile)
# fix lines with same timestamp/function with the call and return swapped
for data in testruns:
@@ -2586,8 +2865,6 @@
# Output:
# The filled Data object
def parseKernelLog(data):
- global sysvals
-
phase = 'suspend_runtime'
if(data.fwValid):
@@ -2645,7 +2922,6 @@
prevktime = -1.0
actions = dict()
for line in data.dmesgtext:
- # -- preprocessing --
# parse each dmesg line into the time and message
m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
if(m):
@@ -2653,8 +2929,6 @@
try:
ktime = float(val)
except:
- doWarning('INVALID DMESG LINE: '+\
- line.replace('\n', ''), 'dmesg')
continue
msg = m.group('msg')
# initialize data start to first line time
@@ -2672,12 +2946,12 @@
phase = 'resume_noirq'
data.dmesg[phase]['start'] = ktime
- # -- phase changes --
# suspend start
if(re.match(dm['suspend_prepare'], msg)):
phase = 'suspend_prepare'
data.dmesg[phase]['start'] = ktime
data.setStart(ktime)
+ data.tKernSus = ktime
# suspend start
elif(re.match(dm['suspend'], msg)):
data.dmesg['suspend_prepare']['end'] = ktime
@@ -2734,7 +3008,7 @@
elif(re.match(dm['post_resume'], msg)):
data.dmesg['resume_complete']['end'] = ktime
data.setEnd(ktime)
- phase = 'post_resume'
+ data.tKernRes = ktime
break
# -- device callbacks --
@@ -2761,7 +3035,6 @@
dev['length'] = int(t)
dev['end'] = ktime
- # -- non-devicecallback actions --
# if trace events are not available, these are better than nothing
if(not sysvals.usetraceevents):
# look for known actions
@@ -2821,8 +3094,7 @@
for event in actions[name]:
data.newActionGlobal(name, event['begin'], event['end'])
- if(sysvals.verbose):
- data.printDetails()
+ data.printDetails()
if(len(sysvals.devicefilter) > 0):
data.deviceFilter(sysvals.devicefilter)
data.fixupInitcallsThatDidntReturn()
@@ -2834,8 +3106,6 @@
# Arguments:
# testruns: array of Data objects from parseTraceLog
def createHTMLSummarySimple(testruns, htmlfile):
- global sysvals
-
# print out the basic summary of all the tests
hf = open(htmlfile, 'w')
@@ -2960,7 +3230,6 @@
hf.close()
def htmlTitle():
- global sysvals
modename = {
'freeze': 'Freeze (S0)',
'standby': 'Standby (S1)',
@@ -2993,13 +3262,14 @@
# Output:
# True if the html file was created, false if it failed
def createHTML(testruns):
- global sysvals
-
if len(testruns) < 1:
print('ERROR: Not enough test data to build a timeline')
return
+ kerror = False
for data in testruns:
+ if data.kerror:
+ kerror = True
data.normalizeTime(testruns[-1].tSuspended)
x2changes = ['', 'absolute']
@@ -3009,53 +3279,59 @@
headline_version = '<div class="version"><a href="https://01.org/suspendresume">AnalyzeSuspend v%s</a></div>' % sysvals.version
headline_stamp = '<div class="stamp">{0} {1} {2} {3}</div>\n'
html_devlist1 = '<button id="devlist1" class="devlist" style="float:left;">Device Detail%s</button>' % x2changes[0]
- html_zoombox = '<center><button id="zoomin">ZOOM IN</button><button id="zoomout">ZOOM OUT</button><button id="zoomdef">ZOOM 1:1</button></center>\n'
+ html_zoombox = '<center><button id="zoomin">ZOOM IN +</button><button id="zoomout">ZOOM OUT -</button><button id="zoomdef">ZOOM 1:1</button></center>\n'
html_devlist2 = '<button id="devlist2" class="devlist" style="float:right;">Device Detail2</button>\n'
html_timeline = '<div id="dmesgzoombox" class="zoombox">\n<div id="{0}" class="timeline" style="height:{1}px">\n'
- html_tblock = '<div id="block{0}" class="tblock" style="left:{1}%;width:{2}%;">\n'
+ html_tblock = '<div id="block{0}" class="tblock" style="left:{1}%;width:{2}%;"><div class="tback" style="height:{3}px"></div>\n'
html_device = '<div id="{0}" title="{1}" class="thread{7}" style="left:{2}%;top:{3}px;height:{4}px;width:{5}%;{8}">{6}</div>\n'
- html_traceevent = '<div title="{0}" class="traceevent" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;">{5}</div>\n'
+ html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">ERROR→</div>\n'
+ html_traceevent = '<div title="{0}" class="traceevent{6}" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;{7}">{5}</div>\n'
+ html_cpuexec = '<div class="jiffie" style="left:{0}%;top:{1}px;height:{2}px;width:{3}%;background:{4};"></div>\n'
html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}px;height:{3}px;background-color:{4}">{5}</div>\n'
- html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background-color:{3}"></div>\n'
+ html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background:{3}"></div>\n'
html_legend = '<div id="p{3}" class="square" style="left:{0}%;background-color:{1}"> {2}</div>\n'
html_timetotal = '<table class="time1">\n<tr>'\
- '<td class="green">{2} Suspend Time: <b>{0} ms</b></td>'\
- '<td class="yellow">{2} Resume Time: <b>{1} ms</b></td>'\
+ '<td class="green" title="{3}">{2} Suspend Time: <b>{0} ms</b></td>'\
+ '<td class="yellow" title="{4}">{2} Resume Time: <b>{1} ms</b></td>'\
'</tr>\n</table>\n'
html_timetotal2 = '<table class="time1">\n<tr>'\
- '<td class="green">{3} Suspend Time: <b>{0} ms</b></td>'\
- '<td class="gray">'+sysvals.suspendmode+' time: <b>{1} ms</b></td>'\
- '<td class="yellow">{3} Resume Time: <b>{2} ms</b></td>'\
+ '<td class="green" title="{4}">{3} Suspend Time: <b>{0} ms</b></td>'\
+ '<td class="gray" title="time spent in low-power mode with clock running">'+sysvals.suspendmode+' time: <b>{1} ms</b></td>'\
+ '<td class="yellow" title="{5}">{3} Resume Time: <b>{2} ms</b></td>'\
'</tr>\n</table>\n'
html_timetotal3 = '<table class="time1">\n<tr>'\
'<td class="green">Execution Time: <b>{0} ms</b></td>'\
'<td class="yellow">Command: <b>{1}</b></td>'\
'</tr>\n</table>\n'
html_timegroups = '<table class="time2">\n<tr>'\
- '<td class="green">{4}Kernel Suspend: {0} ms</td>'\
+ '<td class="green" title="time from kernel enter_state({5}) to firmware mode [kernel time only]">{4}Kernel Suspend: {0} ms</td>'\
'<td class="purple">{4}Firmware Suspend: {1} ms</td>'\
'<td class="purple">{4}Firmware Resume: {2} ms</td>'\
- '<td class="yellow">{4}Kernel Resume: {3} ms</td>'\
+ '<td class="yellow" title="time from firmware mode to return from kernel enter_state({5}) [kernel time only]">{4}Kernel Resume: {3} ms</td>'\
'</tr>\n</table>\n'
# html format variables
- rowheight = 30
- devtextS = '14px'
- devtextH = '30px'
- hoverZ = 'z-index:10;'
-
+ hoverZ = 'z-index:8;'
if sysvals.usedevsrc:
hoverZ = ''
+ scaleH = 20
+ scaleTH = 20
+ if kerror:
+ scaleH = 40
+ scaleTH = 60
# device timeline
vprint('Creating Device Timeline...')
- devtl = Timeline(rowheight)
+ devtl = Timeline(30, scaleH)
# Generate the header for this timeline
for data in testruns:
tTotal = data.end - data.start
- tEnd = data.dmesg['resume_complete']['end']
+ sktime = (data.dmesg['suspend_machine']['end'] - \
+ data.tKernSus) * 1000
+ rktime = (data.dmesg['resume_complete']['end'] - \
+ data.dmesg['resume_machine']['start']) * 1000
if(tTotal == 0):
print('ERROR: No timeline data')
sys.exit()
@@ -3072,59 +3348,85 @@
thtml = html_timetotal3.format(run_time, testdesc)
devtl.html['header'] += thtml
elif data.fwValid:
- suspend_time = '%.0f'%((data.tSuspended-data.start)*1000 + \
- (data.fwSuspend/1000000.0))
- resume_time = '%.0f'%((tEnd-data.tSuspended)*1000 + \
- (data.fwResume/1000000.0))
+ suspend_time = '%.0f'%(sktime + (data.fwSuspend/1000000.0))
+ resume_time = '%.0f'%(rktime + (data.fwResume/1000000.0))
testdesc1 = 'Total'
testdesc2 = ''
+ stitle = 'time from kernel enter_state(%s) to low-power mode [kernel & firmware time]' % sysvals.suspendmode
+ rtitle = 'time from low-power mode to return from kernel enter_state(%s) [firmware & kernel time]' % sysvals.suspendmode
if(len(testruns) > 1):
testdesc1 = testdesc2 = ordinal(data.testnumber+1)
testdesc2 += ' '
if(data.tLow == 0):
thtml = html_timetotal.format(suspend_time, \
- resume_time, testdesc1)
+ resume_time, testdesc1, stitle, rtitle)
else:
thtml = html_timetotal2.format(suspend_time, low_time, \
- resume_time, testdesc1)
+ resume_time, testdesc1, stitle, rtitle)
devtl.html['header'] += thtml
- sktime = '%.3f'%((data.dmesg['suspend_machine']['end'] - \
- data.getStart())*1000)
sftime = '%.3f'%(data.fwSuspend / 1000000.0)
rftime = '%.3f'%(data.fwResume / 1000000.0)
- rktime = '%.3f'%((data.dmesg['resume_complete']['end'] - \
- data.dmesg['resume_machine']['start'])*1000)
- devtl.html['header'] += html_timegroups.format(sktime, \
- sftime, rftime, rktime, testdesc2)
+ devtl.html['header'] += html_timegroups.format('%.3f'%sktime, \
+ sftime, rftime, '%.3f'%rktime, testdesc2, sysvals.suspendmode)
else:
- suspend_time = '%.0f'%((data.tSuspended-data.start)*1000)
- resume_time = '%.0f'%((tEnd-data.tSuspended)*1000)
+ suspend_time = '%.3f' % sktime
+ resume_time = '%.3f' % rktime
testdesc = 'Kernel'
+ stitle = 'time from kernel enter_state(%s) to firmware mode [kernel time only]' % sysvals.suspendmode
+ rtitle = 'time from firmware mode to return from kernel enter_state(%s) [kernel time only]' % sysvals.suspendmode
if(len(testruns) > 1):
testdesc = ordinal(data.testnumber+1)+' '+testdesc
if(data.tLow == 0):
thtml = html_timetotal.format(suspend_time, \
- resume_time, testdesc)
+ resume_time, testdesc, stitle, rtitle)
else:
thtml = html_timetotal2.format(suspend_time, low_time, \
- resume_time, testdesc)
+ resume_time, testdesc, stitle, rtitle)
devtl.html['header'] += thtml
# time scale for potentially multiple datasets
t0 = testruns[0].start
tMax = testruns[-1].end
- tSuspended = testruns[-1].tSuspended
tTotal = tMax - t0
# determine the maximum number of rows we need to draw
+ fulllist = []
+ threadlist = []
+ pscnt = 0
+ devcnt = 0
for data in testruns:
data.selectTimelineDevices('%f', tTotal, sysvals.mindevlen)
for group in data.devicegroups:
devlist = []
for phase in group:
for devname in data.tdevlist[phase]:
- devlist.append((phase,devname))
- devtl.getPhaseRows(data.dmesg, devlist)
+ d = DevItem(data.testnumber, phase, data.dmesg[phase]['list'][devname])
+ devlist.append(d)
+ if d.isa('kth'):
+ threadlist.append(d)
+ else:
+ if d.isa('ps'):
+ pscnt += 1
+ else:
+ devcnt += 1
+ fulllist.append(d)
+ if sysvals.mixedphaseheight:
+ devtl.getPhaseRows(devlist)
+ if not sysvals.mixedphaseheight:
+ if len(threadlist) > 0 and len(fulllist) > 0:
+ if pscnt > 0 and devcnt > 0:
+ msg = 'user processes & device pm callbacks'
+ elif pscnt > 0:
+ msg = 'user processes'
+ else:
+ msg = 'device pm callbacks'
+ d = testruns[0].addHorizontalDivider(msg, testruns[-1].end)
+ fulllist.insert(0, d)
+ devtl.getPhaseRows(fulllist)
+ if len(threadlist) > 0:
+ d = testruns[0].addHorizontalDivider('asynchronous kernel threads', testruns[-1].end)
+ threadlist.insert(0, d)
+ devtl.getPhaseRows(threadlist, devtl.rows)
devtl.calcTotalRows()
# create bounding box, add buttons
@@ -3145,18 +3447,6 @@
# draw each test run chronologically
for data in testruns:
- # if nore than one test, draw a block to represent user mode
- if(data.testnumber > 0):
- m0 = testruns[data.testnumber-1].end
- mMax = testruns[data.testnumber].start
- mTotal = mMax - m0
- name = 'usermode%d' % data.testnumber
- top = '%d' % devtl.scaleH
- left = '%f' % (((m0-t0)*100.0)/tTotal)
- width = '%f' % ((mTotal*100.0)/tTotal)
- title = 'user mode (%0.3f ms) ' % (mTotal*1000)
- devtl.html['timeline'] += html_device.format(name, \
- title, left, top, '%d'%devtl.bodyH, width, '', '', '')
# now draw the actual timeline blocks
for dir in phases:
# draw suspend and resume blocks separately
@@ -3169,13 +3459,16 @@
else:
m0 = testruns[data.testnumber].tSuspended
mMax = testruns[data.testnumber].end
+ # in an x2 run, remove any gap between blocks
+ if len(testruns) > 1 and data.testnumber == 0:
+ mMax = testruns[1].start
mTotal = mMax - m0
left = '%f' % ((((m0-t0)*100.0)+sysvals.srgap/2)/tTotal)
# if a timeline block is 0 length, skip altogether
if mTotal == 0:
continue
width = '%f' % (((mTotal*100.0)-sysvals.srgap/2)/tTotal)
- devtl.html['timeline'] += html_tblock.format(bname, left, width)
+ devtl.html['timeline'] += html_tblock.format(bname, left, width, devtl.scaleH)
for b in sorted(phases[dir]):
# draw the phase color background
phase = data.dmesg[b]
@@ -3185,6 +3478,12 @@
devtl.html['timeline'] += html_phase.format(left, width, \
'%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \
data.dmesg[b]['color'], '')
+ for e in data.errorinfo[dir]:
+ # draw red lines for any kernel errors found
+ t, err = e
+ right = '%f' % (((mMax-t)*100.0)/mTotal)
+ devtl.html['timeline'] += html_error.format(right, err)
+ for b in sorted(phases[dir]):
# draw the devices for this phase
phaselist = data.dmesg[b]['list']
for d in data.tdevlist[b]:
@@ -3196,46 +3495,62 @@
xtrastyle = ''
if 'htmlclass' in dev:
xtraclass = dev['htmlclass']
- xtrainfo = dev['htmlclass']
if 'color' in dev:
xtrastyle = 'background-color:%s;' % dev['color']
if(d in sysvals.devprops):
name = sysvals.devprops[d].altName(d)
xtraclass = sysvals.devprops[d].xtraClass()
xtrainfo = sysvals.devprops[d].xtraInfo()
+ elif xtraclass == ' kth':
+ xtrainfo = ' kernel_thread'
if('drv' in dev and dev['drv']):
drv = ' {%s}' % dev['drv']
- rowheight = devtl.phaseRowHeight(b, dev['row'])
- rowtop = devtl.phaseRowTop(b, dev['row'])
+ rowheight = devtl.phaseRowHeight(data.testnumber, b, dev['row'])
+ rowtop = devtl.phaseRowTop(data.testnumber, b, dev['row'])
top = '%.3f' % (rowtop + devtl.scaleH)
left = '%f' % (((dev['start']-m0)*100)/mTotal)
width = '%f' % (((dev['end']-dev['start'])*100)/mTotal)
length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000)
+ title = name+drv+xtrainfo+length
if sysvals.suspendmode == 'command':
- title = name+drv+xtrainfo+length+'cmdexec'
+ title += sysvals.testcommand
+ elif xtraclass == ' ps':
+ if 'suspend' in b:
+ title += 'pre_suspend_process'
+ else:
+ title += 'post_resume_process'
else:
- title = name+drv+xtrainfo+length+b
+ title += b
devtl.html['timeline'] += html_device.format(dev['id'], \
title, left, top, '%.3f'%rowheight, width, \
d+drv, xtraclass, xtrastyle)
+ if('cpuexec' in dev):
+ for t in sorted(dev['cpuexec']):
+ start, end = t
+ j = float(dev['cpuexec'][t]) / 5
+ if j > 1.0:
+ j = 1.0
+ height = '%.3f' % (rowheight/3)
+ top = '%.3f' % (rowtop + devtl.scaleH + 2*rowheight/3)
+ left = '%f' % (((start-m0)*100)/mTotal)
+ width = '%f' % ((end-start)*100/mTotal)
+ color = 'rgba(255, 0, 0, %f)' % j
+ devtl.html['timeline'] += \
+ html_cpuexec.format(left, top, height, width, color)
if('src' not in dev):
continue
# draw any trace events for this device
- vprint('Debug trace events found for device %s' % d)
- vprint('%20s %20s %10s %8s' % ('title', \
- 'name', 'time(ms)', 'length(ms)'))
for e in dev['src']:
- vprint('%20s %20s %10.3f %8.3f' % (e.title, \
- e.text, e.time*1000, e.length*1000))
- height = devtl.rowH
+ height = '%.3f' % devtl.rowH
top = '%.3f' % (rowtop + devtl.scaleH + (e.row*devtl.rowH))
left = '%f' % (((e.time-m0)*100)/mTotal)
width = '%f' % (e.length*100/mTotal)
- color = 'rgba(204,204,204,0.5)'
+ xtrastyle = ''
+ if e.color:
+ xtrastyle = 'background:%s;' % e.color
devtl.html['timeline'] += \
- html_traceevent.format(e.title, \
- left, top, '%.3f'%height, \
- width, e.text)
+ html_traceevent.format(e.title(), \
+ left, top, height, width, e.text(), '', xtrastyle)
# draw the time scale, try to make the number of labels readable
devtl.html['timeline'] += devtl.createTimeScale(m0, mMax, tTotal, dir)
devtl.html['timeline'] += '</div>\n'
@@ -3284,8 +3599,7 @@
t2 {color:black;font:25px Times;}\n\
t3 {color:black;font:20px Times;white-space:nowrap;}\n\
t4 {color:black;font:bold 30px Times;line-height:60px;white-space:nowrap;}\n\
- cS {color:blue;font:bold 11px Times;}\n\
- cR {color:red;font:bold 11px Times;}\n\
+ cS {font:bold 13px Times;}\n\
table {width:100%;}\n\
.gray {background-color:rgba(80,80,80,0.1);}\n\
.green {background-color:rgba(204,255,204,0.4);}\n\
@@ -3302,20 +3616,22 @@
.pf:'+cgchk+' + label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/><rect x="8" y="4" width="2" height="10" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
.pf:'+cgnchk+' ~ label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
.pf:'+cgchk+' ~ *:not(:nth-child(2)) {display:none;}\n\
- .zoombox {position:relative;width:100%;overflow-x:scroll;}\n\
+ .zoombox {position:relative;width:100%;overflow-x:scroll;-webkit-user-select:none;-moz-user-select:none;user-select:none;}\n\
.timeline {position:relative;font-size:14px;cursor:pointer;width:100%; overflow:hidden;background:linear-gradient(#cccccc, white);}\n\
- .thread {position:absolute;height:0%;overflow:hidden;line-height:'+devtextH+';font-size:'+devtextS+';border:1px solid;text-align:center;white-space:nowrap;background-color:rgba(204,204,204,0.5);}\n\
- .thread.sync {background-color:'+sysvals.synccolor+';}\n\
- .thread.bg {background-color:'+sysvals.kprobecolor+';}\n\
+ .thread {position:absolute;height:0%;overflow:hidden;z-index:7;line-height:30px;font-size:14px;border:1px solid;text-align:center;white-space:nowrap;}\n\
+ .thread.ps {border-radius:3px;background:linear-gradient(to top, #ccc, #eee);}\n\
.thread:hover {background-color:white;border:1px solid red;'+hoverZ+'}\n\
+ .thread.sec,.thread.sec:hover {background-color:black;border:0;color:white;line-height:15px;font-size:10px;}\n\
.hover {background-color:white;border:1px solid red;'+hoverZ+'}\n\
.hover.sync {background-color:white;}\n\
- .hover.bg {background-color:white;}\n\
- .traceevent {position:absolute;font-size:10px;overflow:hidden;color:black;text-align:center;white-space:nowrap;border-radius:5px;border:1px solid black;background:linear-gradient(to bottom right,rgba(204,204,204,1),rgba(150,150,150,1));}\n\
- .traceevent:hover {background:white;}\n\
+ .hover.bg,.hover.kth,.hover.sync,.hover.ps {background-color:white;}\n\
+ .jiffie {position:absolute;pointer-events: none;z-index:8;}\n\
+ .traceevent {position:absolute;font-size:10px;z-index:7;overflow:hidden;color:black;text-align:center;white-space:nowrap;border-radius:5px;border:1px solid black;background:linear-gradient(to bottom right,#CCC,#969696);}\n\
+ .traceevent:hover {color:white;font-weight:bold;border:1px solid white;}\n\
.phase {position:absolute;overflow:hidden;border:0px;text-align:center;}\n\
.phaselet {position:absolute;overflow:hidden;border:0px;text-align:center;height:100px;font-size:24px;}\n\
- .t {z-index:2;position:absolute;pointer-events:none;top:0%;height:100%;border-right:1px solid black;}\n\
+ .t {position:absolute;line-height:'+('%d'%scaleTH)+'px;pointer-events:none;top:0;height:100%;border-right:1px solid black;z-index:6;}\n\
+ .err {position:absolute;top:0%;height:100%;border-right:3px solid red;color:red;font:bold 14px Times;line-height:18px;}\n\
.legend {position:relative; width:100%; height:40px; text-align:center;margin-bottom:20px}\n\
.legend .square {position:absolute;cursor:pointer;top:10px; width:0px;height:20px;border:1px solid;padding-left:20px;}\n\
button {height:40px;width:200px;margin-bottom:20px;margin-top:20px;font-size:24px;}\n\
@@ -3327,7 +3643,8 @@
a:active {color:white;}\n\
.version {position:relative;float:left;color:white;font-size:10px;line-height:30px;margin-left:10px;}\n\
#devicedetail {height:100px;box-shadow:5px 5px 20px black;}\n\
- .tblock {position:absolute;height:100%;}\n\
+ .tblock {position:absolute;height:100%;background-color:#ddd;}\n\
+ .tback {position:absolute;width:100%;background:linear-gradient(#ccc, #ddd);}\n\
.bg {z-index:1;}\n\
</style>\n</head>\n<body>\n'
@@ -3342,6 +3659,8 @@
# write the test title and general info header
if(sysvals.stamp['time'] != ""):
hf.write(headline_version)
+ if sysvals.logmsg:
+ hf.write('<button id="showtest" class="logbtn">log</button>')
if sysvals.addlogs and sysvals.dmesgfile:
hf.write('<button id="showdmesg" class="logbtn">dmesg</button>')
if sysvals.addlogs and sysvals.ftracefile:
@@ -3359,6 +3678,9 @@
# draw the colored boxes for the device detail section
for data in testruns:
hf.write('<div id="devicedetail%d">\n' % data.testnumber)
+ pscolor = 'linear-gradient(to top left, #ccc, #eee)'
+ hf.write(html_phaselet.format('pre_suspend_process', \
+ '0', '0', pscolor))
for b in data.phases:
phase = data.dmesg[b]
length = phase['end']-phase['start']
@@ -3366,14 +3688,18 @@
width = '%.3f' % ((length*100.0)/tTotal)
hf.write(html_phaselet.format(b, left, width, \
data.dmesg[b]['color']))
+ hf.write(html_phaselet.format('post_resume_process', \
+ '0', '0', pscolor))
if sysvals.suspendmode == 'command':
- hf.write(html_phaselet.format('cmdexec', '0', '0', \
- data.dmesg['resume_complete']['color']))
+ hf.write(html_phaselet.format('cmdexec', '0', '0', pscolor))
hf.write('</div>\n')
hf.write('</div>\n')
# write the ftrace data (callgraph)
- data = testruns[-1]
+ if sysvals.cgtest >= 0 and len(testruns) > sysvals.cgtest:
+ data = testruns[sysvals.cgtest]
+ else:
+ data = testruns[-1]
if(sysvals.usecallgraph and not sysvals.embedded):
hf.write('<section id="callgraphs" class="callgraph">\n')
# write out the ftrace data converted to html
@@ -3383,6 +3709,8 @@
html_func_leaf = '<article>{0} {1}</article>\n'
num = 0
for p in data.phases:
+ if sysvals.cgphase and p != sysvals.cgphase:
+ continue
list = data.dmesg[p]['list']
for devname in data.sortedDevices(p):
if('ftrace' not in list[devname]):
@@ -3420,11 +3748,15 @@
hf.write(html_func_end)
hf.write('\n\n </section>\n')
+ # add the test log as a hidden div
+ if sysvals.logmsg:
+ hf.write('<div id="testlog" style="display:none;">\n'+sysvals.logmsg+'</div>\n')
# add the dmesg log as a hidden div
if sysvals.addlogs and sysvals.dmesgfile:
hf.write('<div id="dmesglog" style="display:none;">\n')
lf = open(sysvals.dmesgfile, 'r')
for line in lf:
+ line = line.replace('<', '<').replace('>', '>')
hf.write(line)
lf.close()
hf.write('</div>\n')
@@ -3475,8 +3807,9 @@
script_code = \
'<script type="text/javascript">\n'+detail+\
' var resolution = -1;\n'\
+ ' var dragval = [0, 0];\n'\
' function redrawTimescale(t0, tMax, tS) {\n'\
- ' var rline = \'<div class="t" style="left:0;border-left:1px solid black;border-right:0;"><cR><-R</cR></div>\';\n'\
+ ' var rline = \'<div class="t" style="left:0;border-left:1px solid black;border-right:0;"><cS>←R</cS></div>\';\n'\
' var tTotal = tMax - t0;\n'\
' var list = document.getElementsByClassName("tblock");\n'\
' for (var i = 0; i < list.length; i++) {\n'\
@@ -3501,7 +3834,7 @@
' pos = 100 - (((j)*tS*100)/mTotal) - divEdge;\n'\
' val = (j-divTotal+1)*tS;\n'\
' if(j == divTotal - 1)\n'\
- ' htmlline = \'<div class="t" style="right:\'+pos+\'%"><cS>S-></cS></div>\';\n'\
+ ' htmlline = \'<div class="t" style="right:\'+pos+\'%"><cS>S→</cS></div>\';\n'\
' else\n'\
' htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\
' }\n'\
@@ -3513,6 +3846,7 @@
' function zoomTimeline() {\n'\
' var dmesg = document.getElementById("dmesg");\n'\
' var zoombox = document.getElementById("dmesgzoombox");\n'\
+ ' var left = zoombox.scrollLeft;\n'\
' var val = parseFloat(dmesg.style.width);\n'\
' var newval = 100;\n'\
' var sh = window.outerWidth / 2;\n'\
@@ -3520,12 +3854,12 @@
' newval = val * 1.2;\n'\
' if(newval > 910034) newval = 910034;\n'\
' dmesg.style.width = newval+"%";\n'\
- ' zoombox.scrollLeft = ((zoombox.scrollLeft + sh) * newval / val) - sh;\n'\
+ ' zoombox.scrollLeft = ((left + sh) * newval / val) - sh;\n'\
' } else if (this.id == "zoomout") {\n'\
' newval = val / 1.2;\n'\
' if(newval < 100) newval = 100;\n'\
' dmesg.style.width = newval+"%";\n'\
- ' zoombox.scrollLeft = ((zoombox.scrollLeft + sh) * newval / val) - sh;\n'\
+ ' zoombox.scrollLeft = ((left + sh) * newval / val) - sh;\n'\
' } else {\n'\
' zoombox.scrollLeft = 0;\n'\
' dmesg.style.width = "100%";\n'\
@@ -3542,8 +3876,12 @@
' resolution = tS[i];\n'\
' redrawTimescale(t0, tMax, tS[i]);\n'\
' }\n'\
+ ' function deviceName(title) {\n'\
+ ' var name = title.slice(0, title.indexOf(" ("));\n'\
+ ' return name;\n'\
+ ' }\n'\
' function deviceHover() {\n'\
- ' var name = this.title.slice(0, this.title.indexOf(" ("));\n'\
+ ' var name = deviceName(this.title);\n'\
' var dmesg = document.getElementById("dmesg");\n'\
' var dev = dmesg.getElementsByClassName("thread");\n'\
' var cpu = -1;\n'\
@@ -3552,7 +3890,7 @@
' else if(name.match("CPU_OFF\[[0-9]*\]"))\n'\
' cpu = parseInt(name.slice(8));\n'\
' for (var i = 0; i < dev.length; i++) {\n'\
- ' dname = dev[i].title.slice(0, dev[i].title.indexOf(" ("));\n'\
+ ' dname = deviceName(dev[i].title);\n'\
' var cname = dev[i].className.slice(dev[i].className.indexOf("thread"));\n'\
' if((cpu >= 0 && dname.match("CPU_O[NF]*\\\[*"+cpu+"\\\]")) ||\n'\
' (name == dname))\n'\
@@ -3578,7 +3916,7 @@
' total[2] = (total[2]+total[4])/2;\n'\
' }\n'\
' var devtitle = document.getElementById("devicedetailtitle");\n'\
- ' var name = title.slice(0, title.indexOf(" ("));\n'\
+ ' var name = deviceName(title);\n'\
' if(cpu >= 0) name = "CPU"+cpu;\n'\
' var driver = "";\n'\
' var tS = "<t2>(</t2>";\n'\
@@ -3600,7 +3938,7 @@
' function deviceDetail() {\n'\
' var devinfo = document.getElementById("devicedetail");\n'\
' devinfo.style.display = "block";\n'\
- ' var name = this.title.slice(0, this.title.indexOf(" ("));\n'\
+ ' var name = deviceName(this.title);\n'\
' var cpu = -1;\n'\
' if(name.match("CPU_ON\[[0-9]*\]"))\n'\
' cpu = parseInt(name.slice(7));\n'\
@@ -3615,7 +3953,7 @@
' var pd = pdata[0];\n'\
' var total = [0.0, 0.0, 0.0];\n'\
' for (var i = 0; i < dev.length; i++) {\n'\
- ' dname = dev[i].title.slice(0, dev[i].title.indexOf(" ("));\n'\
+ ' dname = deviceName(dev[i].title);\n'\
' if((cpu >= 0 && dname.match("CPU_O[NF]*\\\[*"+cpu+"\\\]")) ||\n'\
' (name == dname))\n'\
' {\n'\
@@ -3656,7 +3994,7 @@
' phases[i].title = phases[i].id+" "+pd[phases[i].id]+" ms";\n'\
' left += w;\n'\
' var time = "<t4 style=\\"font-size:"+fs+"px\\">"+pd[phases[i].id]+" ms<br></t4>";\n'\
- ' var pname = "<t3 style=\\"font-size:"+fs2+"px\\">"+phases[i].id.replace("_", " ")+"</t3>";\n'\
+ ' var pname = "<t3 style=\\"font-size:"+fs2+"px\\">"+phases[i].id.replace(new RegExp("_", "g"), " ")+"</t3>";\n'\
' phases[i].innerHTML = time+pname;\n'\
' } else {\n'\
' phases[i].style.width = "0%";\n'\
@@ -3677,12 +4015,7 @@
' }\n'\
' }\n'\
' function devListWindow(e) {\n'\
- ' var sx = e.clientX;\n'\
- ' if(sx > window.innerWidth - 440)\n'\
- ' sx = window.innerWidth - 440;\n'\
- ' var cfg="top="+e.screenY+", left="+sx+", width=440, height=720, scrollbars=yes";\n'\
- ' var win = window.open("", "_blank", cfg);\n'\
- ' if(window.chrome) win.moveBy(sx, 0);\n'\
+ ' var win = window.open();\n'\
' var html = "<title>"+e.target.innerHTML+"</title>"+\n'\
' "<style type=\\"text/css\\">"+\n'\
' " ul {list-style-type:circle;padding-left:10px;margin-left:10px;}"+\n'\
@@ -3692,6 +4025,12 @@
' dt = devtable[1];\n'\
' win.document.write(html+dt);\n'\
' }\n'\
+ ' function errWindow() {\n'\
+ ' var text = this.id;\n'\
+ ' var win = window.open();\n'\
+ ' win.document.write("<pre>"+text+"</pre>");\n'\
+ ' win.document.close();\n'\
+ ' }\n'\
' function logWindow(e) {\n'\
' var name = e.target.id.slice(4);\n'\
' var win = window.open();\n'\
@@ -3702,16 +4041,46 @@
' }\n'\
' function onClickPhase(e) {\n'\
' }\n'\
+ ' function onMouseDown(e) {\n'\
+ ' dragval[0] = e.clientX;\n'\
+ ' dragval[1] = document.getElementById("dmesgzoombox").scrollLeft;\n'\
+ ' document.onmousemove = onMouseMove;\n'\
+ ' }\n'\
+ ' function onMouseMove(e) {\n'\
+ ' var zoombox = document.getElementById("dmesgzoombox");\n'\
+ ' zoombox.scrollLeft = dragval[1] + dragval[0] - e.clientX;\n'\
+ ' }\n'\
+ ' function onMouseUp(e) {\n'\
+ ' document.onmousemove = null;\n'\
+ ' }\n'\
+ ' function onKeyPress(e) {\n'\
+ ' var c = e.charCode;\n'\
+ ' if(c != 42 && c != 43 && c != 45) return;\n'\
+ ' var click = document.createEvent("Events");\n'\
+ ' click.initEvent("click", true, false);\n'\
+ ' if(c == 43) \n'\
+ ' document.getElementById("zoomin").dispatchEvent(click);\n'\
+ ' else if(c == 45)\n'\
+ ' document.getElementById("zoomout").dispatchEvent(click);\n'\
+ ' else if(c == 42)\n'\
+ ' document.getElementById("zoomdef").dispatchEvent(click);\n'\
+ ' }\n'\
' window.addEventListener("resize", function () {zoomTimeline();});\n'\
' window.addEventListener("load", function () {\n'\
' var dmesg = document.getElementById("dmesg");\n'\
' dmesg.style.width = "100%"\n'\
+ ' dmesg.onmousedown = onMouseDown;\n'\
+ ' document.onmouseup = onMouseUp;\n'\
+ ' document.onkeypress = onKeyPress;\n'\
' document.getElementById("zoomin").onclick = zoomTimeline;\n'\
' document.getElementById("zoomout").onclick = zoomTimeline;\n'\
' document.getElementById("zoomdef").onclick = zoomTimeline;\n'\
' var list = document.getElementsByClassName("square");\n'\
' for (var i = 0; i < list.length; i++)\n'\
' list[i].onclick = onClickPhase;\n'\
+ ' var list = document.getElementsByClassName("err");\n'\
+ ' for (var i = 0; i < list.length; i++)\n'\
+ ' list[i].onclick = errWindow;\n'\
' var list = document.getElementsByClassName("logbtn");\n'\
' for (var i = 0; i < list.length; i++)\n'\
' list[i].onclick = logWindow;\n'\
@@ -3734,9 +4103,7 @@
# Execute system suspend through the sysfs interface, then copy the output
# dmesg and ftrace files to the test output directory.
def executeSuspend():
- global sysvals
-
- t0 = time.time()*1000
+ pm = ProcessMonitor()
tp = sysvals.tpath
fwdata = []
# mark the start point in the kernel ring buffer just as we start
@@ -3745,30 +4112,39 @@
if(sysvals.usecallgraph or sysvals.usetraceevents):
print('START TRACING')
sysvals.fsetVal('1', 'tracing_on')
+ if sysvals.useprocmon:
+ pm.start()
# execute however many s/r runs requested
for count in range(1,sysvals.execcount+1):
- # if this is test2 and there's a delay, start here
+ # x2delay in between test runs
if(count > 1 and sysvals.x2delay > 0):
- tN = time.time()*1000
- while (tN - t0) < sysvals.x2delay:
- tN = time.time()*1000
- time.sleep(0.001)
- # initiate suspend
- if(sysvals.usecallgraph or sysvals.usetraceevents):
- sysvals.fsetVal('SUSPEND START', 'trace_marker')
- if sysvals.suspendmode == 'command':
+ sysvals.fsetVal('WAIT %d' % sysvals.x2delay, 'trace_marker')
+ time.sleep(sysvals.x2delay/1000.0)
+ sysvals.fsetVal('WAIT END', 'trace_marker')
+ # start message
+ if sysvals.testcommand != '':
print('COMMAND START')
- if(sysvals.rtcwake):
- print('will issue an rtcwake in %d seconds' % sysvals.rtcwaketime)
- sysvals.rtcWakeAlarmOn()
- os.system(sysvals.testcommand)
else:
if(sysvals.rtcwake):
print('SUSPEND START')
- print('will autoresume in %d seconds' % sysvals.rtcwaketime)
- sysvals.rtcWakeAlarmOn()
else:
print('SUSPEND START (press a key to resume)')
+ # set rtcwake
+ if(sysvals.rtcwake):
+ print('will issue an rtcwake in %d seconds' % sysvals.rtcwaketime)
+ sysvals.rtcWakeAlarmOn()
+ # start of suspend trace marker
+ if(sysvals.usecallgraph or sysvals.usetraceevents):
+ sysvals.fsetVal('SUSPEND START', 'trace_marker')
+ # predelay delay
+ if(count == 1 and sysvals.predelay > 0):
+ sysvals.fsetVal('WAIT %d' % sysvals.predelay, 'trace_marker')
+ time.sleep(sysvals.predelay/1000.0)
+ sysvals.fsetVal('WAIT END', 'trace_marker')
+ # initiate suspend or command
+ if sysvals.testcommand != '':
+ call(sysvals.testcommand+' 2>&1', shell=True);
+ else:
pf = open(sysvals.powerfile, 'w')
pf.write(sysvals.suspendmode)
# execution will pause here
@@ -3776,26 +4152,27 @@
pf.close()
except:
pass
- t0 = time.time()*1000
if(sysvals.rtcwake):
sysvals.rtcWakeAlarmOff()
+ # postdelay delay
+ if(count == sysvals.execcount and sysvals.postdelay > 0):
+ sysvals.fsetVal('WAIT %d' % sysvals.postdelay, 'trace_marker')
+ time.sleep(sysvals.postdelay/1000.0)
+ sysvals.fsetVal('WAIT END', 'trace_marker')
# return from suspend
print('RESUME COMPLETE')
if(sysvals.usecallgraph or sysvals.usetraceevents):
sysvals.fsetVal('RESUME COMPLETE', 'trace_marker')
- if(sysvals.suspendmode == 'mem'):
+ if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'):
fwdata.append(getFPDT(False))
- # look for post resume events after the last test run
- t = sysvals.postresumetime
- if(t > 0):
- print('Waiting %d seconds for POST-RESUME trace events...' % t)
- time.sleep(t)
# stop ftrace
if(sysvals.usecallgraph or sysvals.usetraceevents):
+ if sysvals.useprocmon:
+ pm.stop()
sysvals.fsetVal('0', 'tracing_on')
print('CAPTURING TRACE')
writeDatafileHeader(sysvals.ftracefile, fwdata)
- os.system('cat '+tp+'trace >> '+sysvals.ftracefile)
+ call('cat '+tp+'trace >> '+sysvals.ftracefile, shell=True)
sysvals.fsetVal('', 'trace')
devProps()
# grab a copy of the dmesg output
@@ -3804,17 +4181,12 @@
sysvals.getdmesg()
def writeDatafileHeader(filename, fwdata):
- global sysvals
-
- prt = sysvals.postresumetime
fp = open(filename, 'a')
fp.write(sysvals.teststamp+'\n')
- if(sysvals.suspendmode == 'mem'):
+ if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'):
for fw in fwdata:
if(fw):
fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1]))
- if(prt > 0):
- fp.write('# post resume time %u\n' % prt)
fp.close()
# Function: setUSBDevicesAuto
@@ -3824,18 +4196,16 @@
# to always-on since the kernel cant determine if the device can
# properly autosuspend
def setUSBDevicesAuto():
- global sysvals
-
rootCheck(True)
for dirname, dirnames, filenames in os.walk('/sys/devices'):
if(re.match('.*/usb[0-9]*.*', dirname) and
'idVendor' in filenames and 'idProduct' in filenames):
- os.system('echo auto > %s/power/control' % dirname)
+ call('echo auto > %s/power/control' % dirname, shell=True)
name = dirname.split('/')[-1]
- desc = os.popen('cat %s/product 2>/dev/null' % \
- dirname).read().replace('\n', '')
- ctrl = os.popen('cat %s/power/control 2>/dev/null' % \
- dirname).read().replace('\n', '')
+ desc = Popen(['cat', '%s/product' % dirname],
+ stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '')
+ ctrl = Popen(['cat', '%s/power/control' % dirname],
+ stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '')
print('control is %s for %6s: %s' % (ctrl, name, desc))
# Function: yesno
@@ -3872,8 +4242,6 @@
# Detect all the USB hosts and devices currently connected and add
# a list of USB device names to sysvals for better timeline readability
def detectUSB():
- global sysvals
-
field = {'idVendor':'', 'idProduct':'', 'product':'', 'speed':''}
power = {'async':'', 'autosuspend':'', 'autosuspend_delay_ms':'',
'control':'', 'persist':'', 'runtime_enabled':'',
@@ -3899,12 +4267,12 @@
if(re.match('.*/usb[0-9]*.*', dirname) and
'idVendor' in filenames and 'idProduct' in filenames):
for i in field:
- field[i] = os.popen('cat %s/%s 2>/dev/null' % \
- (dirname, i)).read().replace('\n', '')
+ field[i] = Popen(['cat', '%s/%s' % (dirname, i)],
+ stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '')
name = dirname.split('/')[-1]
for i in power:
- power[i] = os.popen('cat %s/power/%s 2>/dev/null' % \
- (dirname, i)).read().replace('\n', '')
+ power[i] = Popen(['cat', '%s/power/%s' % (dirname, i)],
+ stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '')
if(re.match('usb[0-9]*', name)):
first = '%-8s' % name
else:
@@ -3928,7 +4296,6 @@
# Description:
# Retrieve a list of properties for all devices in the trace log
def devProps(data=0):
- global sysvals
props = dict()
if data:
@@ -3953,7 +4320,7 @@
return
if(os.path.exists(sysvals.ftracefile) == False):
- doError('%s does not exist' % sysvals.ftracefile, False)
+ doError('%s does not exist' % sysvals.ftracefile)
# first get the list of devices we need properties for
msghead = 'Additional data added by AnalyzeSuspend'
@@ -3976,7 +4343,7 @@
m = re.match('.*: (?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*', m.group('msg'));
if(not m):
continue
- drv, dev, par = m.group('drv'), m.group('d'), m.group('p')
+ dev = m.group('d')
if dev not in props:
props[dev] = DevProps()
tf.close()
@@ -4052,7 +4419,6 @@
# Output:
# A string list of the available modes
def getModes():
- global sysvals
modes = ''
if(os.path.exists(sysvals.powerfile)):
fp = open(sysvals.powerfile, 'r')
@@ -4066,8 +4432,6 @@
# Arguments:
# output: True to output the info to stdout, False otherwise
def getFPDT(output):
- global sysvals
-
rectype = {}
rectype[0] = 'Firmware Basic Boot Performance Record'
rectype[1] = 'S3 Performance Table Record'
@@ -4078,19 +4442,19 @@
rootCheck(True)
if(not os.path.exists(sysvals.fpdtpath)):
if(output):
- doError('file does not exist: %s' % sysvals.fpdtpath, False)
+ doError('file does not exist: %s' % sysvals.fpdtpath)
return False
if(not os.access(sysvals.fpdtpath, os.R_OK)):
if(output):
- doError('file is not readable: %s' % sysvals.fpdtpath, False)
+ doError('file is not readable: %s' % sysvals.fpdtpath)
return False
if(not os.path.exists(sysvals.mempath)):
if(output):
- doError('file does not exist: %s' % sysvals.mempath, False)
+ doError('file does not exist: %s' % sysvals.mempath)
return False
if(not os.access(sysvals.mempath, os.R_OK)):
if(output):
- doError('file is not readable: %s' % sysvals.mempath, False)
+ doError('file is not readable: %s' % sysvals.mempath)
return False
fp = open(sysvals.fpdtpath, 'rb')
@@ -4100,7 +4464,7 @@
if(len(buf) < 36):
if(output):
doError('Invalid FPDT table data, should '+\
- 'be at least 36 bytes', False)
+ 'be at least 36 bytes')
return False
table = struct.unpack('4sIBB6s8sI4sI', buf[0:36])
@@ -4199,7 +4563,6 @@
# Output:
# True if the test will work, False if not
def statusCheck(probecheck=False):
- global sysvals
status = True
print('Checking this system (%s)...' % platform.node())
@@ -4282,37 +4645,14 @@
if not probecheck:
return status
- if (sysvals.usecallgraph and len(sysvals.debugfuncs) > 0) or len(sysvals.kprobes) > 0:
- sysvals.initFtrace(True)
-
- # verify callgraph debugfuncs
- if sysvals.usecallgraph and len(sysvals.debugfuncs) > 0:
- print(' verifying these ftrace callgraph functions work:')
- sysvals.setFtraceFilterFunctions(sysvals.debugfuncs)
- fp = open(sysvals.tpath+'set_graph_function', 'r')
- flist = fp.read().split('\n')
- fp.close()
- for func in sysvals.debugfuncs:
- res = sysvals.colorText('NO')
- if func in flist:
- res = 'YES'
- else:
- for i in flist:
- if ' [' in i and func == i.split(' ')[0]:
- res = 'YES'
- break
- print(' %s: %s' % (func, res))
-
# verify kprobes
- if len(sysvals.kprobes) > 0:
- print(' verifying these kprobes work:')
- for name in sorted(sysvals.kprobes):
- if name in sysvals.tracefuncs:
- continue
- res = sysvals.colorText('NO')
- if sysvals.testKprobe(sysvals.kprobes[name]):
- res = 'YES'
- print(' %s: %s' % (name, res))
+ if sysvals.usekprobes:
+ for name in sysvals.tracefuncs:
+ sysvals.defaultKprobe(name, sysvals.tracefuncs[name])
+ if sysvals.usedevsrc:
+ for name in sysvals.dev_tracefuncs:
+ sysvals.defaultKprobe(name, sysvals.dev_tracefuncs[name])
+ sysvals.addKprobes(True)
return status
@@ -4322,33 +4662,20 @@
# Arguments:
# msg: the error message to print
# help: True if printHelp should be called after, False otherwise
-def doError(msg, help):
+def doError(msg, help=False):
if(help == True):
printHelp()
print('ERROR: %s\n') % msg
sys.exit()
-# Function: doWarning
-# Description:
-# generic warning function for non-catastrophic anomalies
-# Arguments:
-# msg: the warning message to print
-# file: If not empty, a filename to request be sent to the owner for debug
-def doWarning(msg, file=''):
- print('/* %s */') % msg
- if(file):
- print('/* For a fix, please send this'+\
- ' %s file to <todd.e.brandt@intel.com> */' % file)
-
# Function: rootCheck
# Description:
# quick check to see if we have root access
def rootCheck(fatal):
- global sysvals
if(os.access(sysvals.powerfile, os.W_OK)):
return True
if fatal:
- doError('This command must be run as root', False)
+ doError('This command must be run as root')
return False
# Function: getArgInt
@@ -4389,71 +4716,61 @@
doError(name+': value should be between %f and %f' % (min, max), True)
return val
-# Function: rerunTest
-# Description:
-# generate an output from an existing set of ftrace/dmesg logs
-def rerunTest():
- global sysvals
-
- if(sysvals.ftracefile != ''):
- doesTraceLogHaveTraceEvents()
- if(sysvals.dmesgfile == '' and not sysvals.usetraceeventsonly):
- doError('recreating this html output '+\
- 'requires a dmesg file', False)
- sysvals.setOutputFile()
- vprint('Output file: %s' % sysvals.htmlfile)
+def processData():
print('PROCESSING DATA')
if(sysvals.usetraceeventsonly):
testruns = parseTraceLog()
+ if sysvals.dmesgfile:
+ dmesgtext = loadKernelLog(True)
+ for data in testruns:
+ data.extractErrorInfo(dmesgtext)
else:
testruns = loadKernelLog()
for data in testruns:
parseKernelLog(data)
- if(sysvals.ftracefile != ''):
+ if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)):
appendIncompleteTraceLog(testruns)
createHTML(testruns)
+# Function: rerunTest
+# Description:
+# generate an output from an existing set of ftrace/dmesg logs
+def rerunTest():
+ if sysvals.ftracefile:
+ doesTraceLogHaveTraceEvents()
+ if not sysvals.dmesgfile and not sysvals.usetraceeventsonly:
+ doError('recreating this html output requires a dmesg file')
+ sysvals.setOutputFile()
+ vprint('Output file: %s' % sysvals.htmlfile)
+ if(os.path.exists(sysvals.htmlfile) and not os.access(sysvals.htmlfile, os.W_OK)):
+ doError('missing permission to write to %s' % sysvals.htmlfile)
+ processData()
+
# Function: runTest
# Description:
# execute a suspend/resume, gather the logs, and generate the output
def runTest(subdir, testpath=''):
- global sysvals
-
# prepare for the test
sysvals.initFtrace()
sysvals.initTestOutput(subdir, testpath)
-
- vprint('Output files:\n %s' % sysvals.dmesgfile)
- if(sysvals.usecallgraph or
- sysvals.usetraceevents or
- sysvals.usetraceeventsonly):
- vprint(' %s' % sysvals.ftracefile)
- vprint(' %s' % sysvals.htmlfile)
+ vprint('Output files:\n\t%s\n\t%s\n\t%s' % \
+ (sysvals.dmesgfile, sysvals.ftracefile, sysvals.htmlfile))
# execute the test
executeSuspend()
sysvals.cleanupFtrace()
+ processData()
- # analyze the data and create the html output
- print('PROCESSING DATA')
- if(sysvals.usetraceeventsonly):
- # data for kernels 3.15 or newer is entirely in ftrace
- testruns = parseTraceLog()
- else:
- # data for kernels older than 3.15 is primarily in dmesg
- testruns = loadKernelLog()
- for data in testruns:
- parseKernelLog(data)
- if(sysvals.usecallgraph or sysvals.usetraceevents):
- appendIncompleteTraceLog(testruns)
- createHTML(testruns)
+ # if running as root, change output dir owner to sudo_user
+ if os.path.isdir(sysvals.testdir) and os.getuid() == 0 and \
+ 'SUDO_USER' in os.environ:
+ cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1'
+ call(cmd.format(os.environ['SUDO_USER'], sysvals.testdir), shell=True)
# Function: runSummary
# Description:
# create a summary of tests in a sub-directory
def runSummary(subdir, output):
- global sysvals
-
# get a list of ftrace output files
files = []
for dirname, dirnames, filenames in os.walk(subdir):
@@ -4509,12 +4826,12 @@
# Description:
# Configure the script via the info in a config file
def configFromFile(file):
- global sysvals
Config = ConfigParser.ConfigParser()
- ignorekprobes = False
Config.read(file)
sections = Config.sections()
+ overridekprobes = False
+ overridedevkprobes = False
if 'Settings' in sections:
for opt in Config.options('Settings'):
value = Config.get('Settings', opt).lower()
@@ -4524,19 +4841,19 @@
sysvals.addlogs = checkArgBool(value)
elif(opt.lower() == 'dev'):
sysvals.usedevsrc = checkArgBool(value)
- elif(opt.lower() == 'ignorekprobes'):
- ignorekprobes = checkArgBool(value)
+ elif(opt.lower() == 'proc'):
+ sysvals.useprocmon = checkArgBool(value)
elif(opt.lower() == 'x2'):
if checkArgBool(value):
sysvals.execcount = 2
elif(opt.lower() == 'callgraph'):
sysvals.usecallgraph = checkArgBool(value)
- elif(opt.lower() == 'callgraphfunc'):
- sysvals.debugfuncs = []
- if value:
- value = value.split(',')
- for i in value:
- sysvals.debugfuncs.append(i.strip())
+ elif(opt.lower() == 'override-timeline-functions'):
+ overridekprobes = checkArgBool(value)
+ elif(opt.lower() == 'override-dev-timeline-functions'):
+ overridedevkprobes = checkArgBool(value)
+ elif(opt.lower() == 'devicefilter'):
+ sysvals.setDeviceFilter(value)
elif(opt.lower() == 'expandcg'):
sysvals.cgexp = checkArgBool(value)
elif(opt.lower() == 'srgap'):
@@ -4548,8 +4865,10 @@
sysvals.testcommand = value
elif(opt.lower() == 'x2delay'):
sysvals.x2delay = getArgInt('-x2delay', value, 0, 60000, False)
- elif(opt.lower() == 'postres'):
- sysvals.postresumetime = getArgInt('-postres', value, 0, 3600, False)
+ elif(opt.lower() == 'predelay'):
+ sysvals.predelay = getArgInt('-predelay', value, 0, 60000, False)
+ elif(opt.lower() == 'postdelay'):
+ sysvals.postdelay = getArgInt('-postdelay', value, 0, 60000, False)
elif(opt.lower() == 'rtcwake'):
sysvals.rtcwake = True
sysvals.rtcwaketime = getArgInt('-rtcwake', value, 0, 3600, False)
@@ -4557,53 +4876,50 @@
sysvals.setPrecision(getArgInt('-timeprec', value, 0, 6, False))
elif(opt.lower() == 'mindev'):
sysvals.mindevlen = getArgFloat('-mindev', value, 0.0, 10000.0, False)
+ elif(opt.lower() == 'callloop-maxgap'):
+ sysvals.callloopmaxgap = getArgFloat('-callloop-maxgap', value, 0.0, 1.0, False)
+ elif(opt.lower() == 'callloop-maxlen'):
+ sysvals.callloopmaxgap = getArgFloat('-callloop-maxlen', value, 0.0, 1.0, False)
elif(opt.lower() == 'mincg'):
sysvals.mincglen = getArgFloat('-mincg', value, 0.0, 10000.0, False)
- elif(opt.lower() == 'kprobecolor'):
- try:
- val = int(value, 16)
- sysvals.kprobecolor = '#'+value
- except:
- sysvals.kprobecolor = value
- elif(opt.lower() == 'synccolor'):
- try:
- val = int(value, 16)
- sysvals.synccolor = '#'+value
- except:
- sysvals.synccolor = value
elif(opt.lower() == 'output-dir'):
- args = dict()
- n = datetime.now()
- args['date'] = n.strftime('%y%m%d')
- args['time'] = n.strftime('%H%M%S')
- args['hostname'] = sysvals.hostname
- sysvals.outdir = value.format(**args)
+ sysvals.setOutputFolder(value)
if sysvals.suspendmode == 'command' and not sysvals.testcommand:
- doError('No command supplied for mode "command"', False)
- if sysvals.usedevsrc and sysvals.usecallgraph:
- doError('dev and callgraph cannot both be true', False)
- if sysvals.usecallgraph and sysvals.execcount > 1:
- doError('-x2 is not compatible with -f', False)
+ doError('No command supplied for mode "command"')
- if ignorekprobes:
- return
+ # compatibility errors
+ if sysvals.usedevsrc and sysvals.usecallgraph:
+ doError('-dev is not compatible with -f')
+ if sysvals.usecallgraph and sysvals.useprocmon:
+ doError('-proc is not compatible with -f')
+
+ if overridekprobes:
+ sysvals.tracefuncs = dict()
+ if overridedevkprobes:
+ sysvals.dev_tracefuncs = dict()
kprobes = dict()
- archkprobe = 'Kprobe_'+platform.machine()
- if archkprobe in sections:
- for name in Config.options(archkprobe):
- kprobes[name] = Config.get(archkprobe, name)
- if 'Kprobe' in sections:
- for name in Config.options('Kprobe'):
- kprobes[name] = Config.get('Kprobe', name)
+ kprobesec = 'dev_timeline_functions_'+platform.machine()
+ if kprobesec in sections:
+ for name in Config.options(kprobesec):
+ text = Config.get(kprobesec, name)
+ kprobes[name] = (text, True)
+ kprobesec = 'timeline_functions_'+platform.machine()
+ if kprobesec in sections:
+ for name in Config.options(kprobesec):
+ if name in kprobes:
+ doError('Duplicate timeline function found "%s"' % (name))
+ text = Config.get(kprobesec, name)
+ kprobes[name] = (text, False)
for name in kprobes:
function = name
format = name
color = ''
args = dict()
- data = kprobes[name].split()
+ text, dev = kprobes[name]
+ data = text.split()
i = 0
for val in data:
# bracketted strings are special formatting, read them separately
@@ -4626,28 +4942,30 @@
args[d[0]] = d[1]
i += 1
if not function or not format:
- doError('Invalid kprobe: %s' % name, False)
+ doError('Invalid kprobe: %s' % name)
for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', format):
if arg not in args:
- doError('Kprobe "%s" is missing argument "%s"' % (name, arg), False)
- if name in sysvals.kprobes:
- doError('Duplicate kprobe found "%s"' % (name), False)
- vprint('Adding KPROBE: %s %s %s %s' % (name, function, format, args))
- sysvals.kprobes[name] = {
+ doError('Kprobe "%s" is missing argument "%s"' % (name, arg))
+ if (dev and name in sysvals.dev_tracefuncs) or (not dev and name in sysvals.tracefuncs):
+ doError('Duplicate timeline function found "%s"' % (name))
+
+ kp = {
'name': name,
'func': function,
'format': format,
- 'args': args,
- 'mask': re.sub('{(?P<n>[a-z,A-Z,0-9]*)}', '.*', format)
+ sysvals.archargs: args
}
if color:
- sysvals.kprobes[name]['color'] = color
+ kp['color'] = color
+ if dev:
+ sysvals.dev_tracefuncs[name] = kp
+ else:
+ sysvals.tracefuncs[name] = kp
# Function: printHelp
# Description:
# print out the help text
def printHelp():
- global sysvals
modes = getModes()
print('')
@@ -4670,44 +4988,47 @@
print('')
print('Options:')
print(' [general]')
- print(' -h Print this help text')
- print(' -v Print the current tool version')
- print(' -config file Pull arguments and config options from a file')
- print(' -verbose Print extra information during execution and analysis')
- print(' -status Test to see if the system is enabled to run this tool')
- print(' -modes List available suspend modes')
- print(' -m mode Mode to initiate for suspend %s (default: %s)') % (modes, sysvals.suspendmode)
- print(' -o subdir Override the output subdirectory')
+ print(' -h Print this help text')
+ print(' -v Print the current tool version')
+ print(' -config fn Pull arguments and config options from file fn')
+ print(' -verbose Print extra information during execution and analysis')
+ print(' -status Test to see if the system is enabled to run this tool')
+ print(' -modes List available suspend modes')
+ print(' -m mode Mode to initiate for suspend %s (default: %s)') % (modes, sysvals.suspendmode)
+ print(' -o subdir Override the output subdirectory')
+ print(' -rtcwake t Use rtcwake to autoresume after <t> seconds (default: disabled)')
+ print(' -addlogs Add the dmesg and ftrace logs to the html output')
+ print(' -srgap Add a visible gap in the timeline between sus/res (default: disabled)')
print(' [advanced]')
- print(' -rtcwake t Use rtcwake to autoresume after <t> seconds (default: disabled)')
- print(' -addlogs Add the dmesg and ftrace logs to the html output')
- print(' -multi n d Execute <n> consecutive tests at <d> seconds intervals. The outputs will')
+ print(' -cmd {s} Run the timeline over a custom command, e.g. "sync -d"')
+ print(' -proc Add usermode process info into the timeline (default: disabled)')
+ print(' -dev Add kernel function calls and threads to the timeline (default: disabled)')
+ print(' -x2 Run two suspend/resumes back to back (default: disabled)')
+ print(' -x2delay t Include t ms delay between multiple test runs (default: 0 ms)')
+ print(' -predelay t Include t ms delay before 1st suspend (default: 0 ms)')
+ print(' -postdelay t Include t ms delay after last resume (default: 0 ms)')
+ print(' -mindev ms Discard all device blocks shorter than ms milliseconds (e.g. 0.001 for us)')
+ print(' -multi n d Execute <n> consecutive tests at <d> seconds intervals. The outputs will')
print(' be created in a new subdirectory with a summary page.')
- print(' -srgap Add a visible gap in the timeline between sus/res (default: disabled)')
- print(' -cmd {s} Instead of suspend/resume, run a command, e.g. "sync -d"')
- print(' -mindev ms Discard all device blocks shorter than ms milliseconds (e.g. 0.001 for us)')
- print(' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)')
- print(' -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)')
print(' [debug]')
- print(' -f Use ftrace to create device callgraphs (default: disabled)')
- print(' -expandcg pre-expand the callgraph data in the html output (default: disabled)')
- print(' -flist Print the list of functions currently being captured in ftrace')
- print(' -flistall Print all functions capable of being captured in ftrace')
- print(' -fadd file Add functions to be graphed in the timeline from a list in a text file')
- print(' -filter "d1 d2 ..." Filter out all but this list of device names')
- print(' -dev Display common low level functions in the timeline')
- print(' [post-resume task analysis]')
- print(' -x2 Run two suspend/resumes back to back (default: disabled)')
- print(' -x2delay t Minimum millisecond delay <t> between the two test runs (default: 0 ms)')
- print(' -postres t Time after resume completion to wait for post-resume events (default: 0 S)')
+ print(' -f Use ftrace to create device callgraphs (default: disabled)')
+ print(' -expandcg pre-expand the callgraph data in the html output (default: disabled)')
+ print(' -flist Print the list of functions currently being captured in ftrace')
+ print(' -flistall Print all functions capable of being captured in ftrace')
+ print(' -fadd file Add functions to be graphed in the timeline from a list in a text file')
+ print(' -filter "d1,d2,..." Filter out all but this comma-delimited list of device names')
+ print(' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)')
+ print(' -cgphase P Only show callgraph data for phase P (e.g. suspend_late)')
+ print(' -cgtest N Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)')
+ print(' -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)')
print(' [utilities]')
- print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table')
- print(' -usbtopo Print out the current USB topology with power info')
- print(' -usbauto Enable autosuspend for all connected USB devices')
+ print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table')
+ print(' -usbtopo Print out the current USB topology with power info')
+ print(' -usbauto Enable autosuspend for all connected USB devices')
print(' [re-analyze data from previous runs]')
- print(' -ftrace ftracefile Create HTML output using ftrace input')
- print(' -dmesg dmesgfile Create HTML output using dmesg (not needed for kernel >= 3.15)')
- print(' -summary directory Create a summary of all test in this dir')
+ print(' -ftrace ftracefile Create HTML output using ftrace input')
+ print(' -dmesg dmesgfile Create HTML output using dmesg (not needed for kernel >= 3.15)')
+ print(' -summary directory Create a summary of all test in this dir')
print('')
return True
@@ -4739,26 +5060,22 @@
sys.exit()
elif(arg == '-x2'):
sysvals.execcount = 2
- if(sysvals.usecallgraph):
- doError('-x2 is not compatible with -f', False)
elif(arg == '-x2delay'):
sysvals.x2delay = getArgInt('-x2delay', args, 0, 60000)
- elif(arg == '-postres'):
- sysvals.postresumetime = getArgInt('-postres', args, 0, 3600)
+ elif(arg == '-predelay'):
+ sysvals.predelay = getArgInt('-predelay', args, 0, 60000)
+ elif(arg == '-postdelay'):
+ sysvals.postdelay = getArgInt('-postdelay', args, 0, 60000)
elif(arg == '-f'):
sysvals.usecallgraph = True
- if(sysvals.execcount > 1):
- doError('-x2 is not compatible with -f', False)
- if(sysvals.usedevsrc):
- doError('-dev is not compatible with -f', False)
elif(arg == '-addlogs'):
sysvals.addlogs = True
elif(arg == '-verbose'):
sysvals.verbose = True
+ elif(arg == '-proc'):
+ sysvals.useprocmon = True
elif(arg == '-dev'):
sysvals.usedevsrc = True
- if(sysvals.usecallgraph):
- doError('-dev is not compatible with -f', False)
elif(arg == '-rtcwake'):
sysvals.rtcwake = True
sysvals.rtcwaketime = getArgInt('-rtcwake', args, 0, 3600)
@@ -4768,6 +5085,21 @@
sysvals.mindevlen = getArgFloat('-mindev', args, 0.0, 10000.0)
elif(arg == '-mincg'):
sysvals.mincglen = getArgFloat('-mincg', args, 0.0, 10000.0)
+ elif(arg == '-cgtest'):
+ sysvals.cgtest = getArgInt('-cgtest', args, 0, 1)
+ elif(arg == '-cgphase'):
+ try:
+ val = args.next()
+ except:
+ doError('No phase name supplied', True)
+ d = Data(0)
+ if val not in d.phases:
+ doError('Invalid phase, valid phaess are %s' % d.phases, True)
+ sysvals.cgphase = val
+ elif(arg == '-callloop-maxgap'):
+ sysvals.callloopmaxgap = getArgFloat('-callloop-maxgap', args, 0.0, 1.0)
+ elif(arg == '-callloop-maxlen'):
+ sysvals.callloopmaxlen = getArgFloat('-callloop-maxlen', args, 0.0, 1.0)
elif(arg == '-cmd'):
try:
val = args.next()
@@ -4788,14 +5120,14 @@
val = args.next()
except:
doError('No subdirectory name supplied', True)
- sysvals.outdir = val
+ sysvals.setOutputFolder(val)
elif(arg == '-config'):
try:
val = args.next()
except:
doError('No text file supplied', True)
if(os.path.exists(val) == False):
- doError('%s does not exist' % val, False)
+ doError('%s does not exist' % val)
configFromFile(val)
elif(arg == '-fadd'):
try:
@@ -4803,7 +5135,7 @@
except:
doError('No text file supplied', True)
if(os.path.exists(val) == False):
- doError('%s does not exist' % val, False)
+ doError('%s does not exist' % val)
sysvals.addFtraceFilterFunctions(val)
elif(arg == '-dmesg'):
try:
@@ -4813,7 +5145,7 @@
sysvals.notestrun = True
sysvals.dmesgfile = val
if(os.path.exists(sysvals.dmesgfile) == False):
- doError('%s does not exist' % sysvals.dmesgfile, False)
+ doError('%s does not exist' % sysvals.dmesgfile)
elif(arg == '-ftrace'):
try:
val = args.next()
@@ -4822,7 +5154,7 @@
sysvals.notestrun = True
sysvals.ftracefile = val
if(os.path.exists(sysvals.ftracefile) == False):
- doError('%s does not exist' % sysvals.ftracefile, False)
+ doError('%s does not exist' % sysvals.ftracefile)
elif(arg == '-summary'):
try:
val = args.next()
@@ -4832,7 +5164,7 @@
cmdarg = val
sysvals.notestrun = True
if(os.path.isdir(val) == False):
- doError('%s is not accesible' % val, False)
+ doError('%s is not accesible' % val)
elif(arg == '-filter'):
try:
val = args.next()
@@ -4842,6 +5174,12 @@
else:
doError('Invalid argument: '+arg, True)
+ # compatibility errors
+ if(sysvals.usecallgraph and sysvals.usedevsrc):
+ doError('-dev is not compatible with -f')
+ if(sysvals.usecallgraph and sysvals.useprocmon):
+ doError('-proc is not compatible with -f')
+
# callgraph size cannot exceed device size
if sysvals.mincglen < sysvals.mindevlen:
sysvals.mincglen = sysvals.mindevlen
@@ -4855,8 +5193,7 @@
elif(cmd == 'usbtopo'):
detectUSB()
elif(cmd == 'modes'):
- modes = getModes()
- print modes
+ print getModes()
elif(cmd == 'flist'):
sysvals.getFtraceFilterFunctions(True)
elif(cmd == 'flistall'):
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
index 5d721e9..f067be8 100644
--- a/security/apparmor/include/apparmor.h
+++ b/security/apparmor/include/apparmor.h
@@ -78,12 +78,6 @@
return __aa_kvmalloc(size, __GFP_ZERO);
}
-/* returns 0 if kref not incremented */
-static inline int kref_get_not0(struct kref *kref)
-{
- return atomic_inc_not_zero(&kref->refcount);
-}
-
/**
* aa_strneq - compare null terminated @str to a non null terminated substring
* @str: a null terminated string
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index 52275f0..46467aa 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -287,7 +287,7 @@
*/
static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p)
{
- if (p && kref_get_not0(&p->count))
+ if (p && kref_get_unless_zero(&p->count))
return p;
return NULL;
@@ -307,7 +307,7 @@
rcu_read_lock();
do {
c = rcu_dereference(*p);
- } while (c && !kref_get_not0(&c->count));
+ } while (c && !kref_get_unless_zero(&c->count));
rcu_read_unlock();
return c;
diff --git a/tools/arch/arm/include/uapi/asm/kvm.h b/tools/arch/arm/include/uapi/asm/kvm.h
index a2b3eb3..af05f8e 100644
--- a/tools/arch/arm/include/uapi/asm/kvm.h
+++ b/tools/arch/arm/include/uapi/asm/kvm.h
@@ -84,6 +84,15 @@
#define KVM_VGIC_V2_DIST_SIZE 0x1000
#define KVM_VGIC_V2_CPU_SIZE 0x2000
+/* Supported VGICv3 address types */
+#define KVM_VGIC_V3_ADDR_TYPE_DIST 2
+#define KVM_VGIC_V3_ADDR_TYPE_REDIST 3
+#define KVM_VGIC_ITS_ADDR_TYPE 4
+
+#define KVM_VGIC_V3_DIST_SIZE SZ_64K
+#define KVM_VGIC_V3_REDIST_SIZE (2 * SZ_64K)
+#define KVM_VGIC_V3_ITS_SIZE (2 * SZ_64K)
+
#define KVM_ARM_VCPU_POWER_OFF 0 /* CPU is started in OFF state */
#define KVM_ARM_VCPU_PSCI_0_2 1 /* CPU uses PSCI v0.2 */
diff --git a/tools/arch/powerpc/include/uapi/asm/kvm.h b/tools/arch/powerpc/include/uapi/asm/kvm.h
index c93cf35..3603b6f 100644
--- a/tools/arch/powerpc/include/uapi/asm/kvm.h
+++ b/tools/arch/powerpc/include/uapi/asm/kvm.h
@@ -573,6 +573,10 @@
#define KVM_REG_PPC_SPRG9 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xba)
#define KVM_REG_PPC_DBSR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xbb)
+/* POWER9 registers */
+#define KVM_REG_PPC_TIDR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbc)
+#define KVM_REG_PPC_PSSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbd)
+
/* Transactional Memory checkpointed state:
* This is all GPRs, all VSX regs and a subset of SPRs
*/
@@ -596,6 +600,7 @@
#define KVM_REG_PPC_TM_VSCR (KVM_REG_PPC_TM | KVM_REG_SIZE_U32 | 0x67)
#define KVM_REG_PPC_TM_DSCR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x68)
#define KVM_REG_PPC_TM_TAR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x69)
+#define KVM_REG_PPC_TM_XER (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x6a)
/* PPC64 eXternal Interrupt Controller Specification */
#define KVM_DEV_XICS_GRP_SOURCES 1 /* 64-bit source attributes */
diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h
index cddd5d0..293149a 100644
--- a/tools/arch/x86/include/asm/cpufeatures.h
+++ b/tools/arch/x86/include/asm/cpufeatures.h
@@ -105,6 +105,7 @@
#define X86_FEATURE_AMD_DCM ( 3*32+27) /* multi-node processor */
#define X86_FEATURE_APERFMPERF ( 3*32+28) /* APERFMPERF */
#define X86_FEATURE_NONSTOP_TSC_S3 ( 3*32+30) /* TSC doesn't stop in S3 state */
+#define X86_FEATURE_TSC_KNOWN_FREQ ( 3*32+31) /* TSC has known frequency */
/* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */
#define X86_FEATURE_XMM3 ( 4*32+ 0) /* "pni" SSE-3 */
@@ -188,10 +189,14 @@
#define X86_FEATURE_CPB ( 7*32+ 2) /* AMD Core Performance Boost */
#define X86_FEATURE_EPB ( 7*32+ 3) /* IA32_ENERGY_PERF_BIAS support */
+#define X86_FEATURE_CAT_L3 ( 7*32+ 4) /* Cache Allocation Technology L3 */
+#define X86_FEATURE_CAT_L2 ( 7*32+ 5) /* Cache Allocation Technology L2 */
+#define X86_FEATURE_CDP_L3 ( 7*32+ 6) /* Code and Data Prioritization L3 */
#define X86_FEATURE_HW_PSTATE ( 7*32+ 8) /* AMD HW-PState */
#define X86_FEATURE_PROC_FEEDBACK ( 7*32+ 9) /* AMD ProcFeedbackInterface */
+#define X86_FEATURE_INTEL_PPIN ( 7*32+14) /* Intel Processor Inventory Number */
#define X86_FEATURE_INTEL_PT ( 7*32+15) /* Intel Processor Trace */
#define X86_FEATURE_AVX512_4VNNIW (7*32+16) /* AVX-512 Neural Network Instructions */
#define X86_FEATURE_AVX512_4FMAPS (7*32+17) /* AVX-512 Multiply Accumulation Single precision */
@@ -220,11 +225,13 @@
#define X86_FEATURE_RTM ( 9*32+11) /* Restricted Transactional Memory */
#define X86_FEATURE_CQM ( 9*32+12) /* Cache QoS Monitoring */
#define X86_FEATURE_MPX ( 9*32+14) /* Memory Protection Extension */
+#define X86_FEATURE_RDT_A ( 9*32+15) /* Resource Director Technology Allocation */
#define X86_FEATURE_AVX512F ( 9*32+16) /* AVX-512 Foundation */
#define X86_FEATURE_AVX512DQ ( 9*32+17) /* AVX-512 DQ (Double/Quad granular) Instructions */
#define X86_FEATURE_RDSEED ( 9*32+18) /* The RDSEED instruction */
#define X86_FEATURE_ADX ( 9*32+19) /* The ADCX and ADOX instructions */
#define X86_FEATURE_SMAP ( 9*32+20) /* Supervisor Mode Access Prevention */
+#define X86_FEATURE_AVX512IFMA ( 9*32+21) /* AVX-512 Integer Fused Multiply-Add instructions */
#define X86_FEATURE_CLFLUSHOPT ( 9*32+23) /* CLFLUSHOPT instruction */
#define X86_FEATURE_CLWB ( 9*32+24) /* CLWB instruction */
#define X86_FEATURE_AVX512PF ( 9*32+26) /* AVX-512 Prefetch */
@@ -278,8 +285,11 @@
#define X86_FEATURE_AVIC (15*32+13) /* Virtual Interrupt Controller */
/* Intel-defined CPU features, CPUID level 0x00000007:0 (ecx), word 16 */
+#define X86_FEATURE_AVX512VBMI (16*32+ 1) /* AVX512 Vector Bit Manipulation instructions*/
#define X86_FEATURE_PKU (16*32+ 3) /* Protection Keys for Userspace */
#define X86_FEATURE_OSPKE (16*32+ 4) /* OS Protection Keys Enable */
+#define X86_FEATURE_AVX512_VPOPCNTDQ (16*32+14) /* POPCNT for vectors of DW/QW */
+#define X86_FEATURE_RDPID (16*32+ 22) /* RDPID instruction */
/* AMD-defined CPU features, CPUID level 0x80000007 (ebx), word 17 */
#define X86_FEATURE_OVERFLOW_RECOV (17*32+0) /* MCA overflow recovery support */
@@ -310,4 +320,6 @@
#define X86_BUG_NULL_SEG X86_BUG(10) /* Nulling a selector preserves the base */
#define X86_BUG_SWAPGS_FENCE X86_BUG(11) /* SWAPGS without input dep on GS */
#define X86_BUG_MONITOR X86_BUG(12) /* IPI required to wake up remote CPU */
+#define X86_BUG_AMD_E400 X86_BUG(13) /* CPU is among the affected by Erratum 400 */
+
#endif /* _ASM_X86_CPUFEATURES_H */
diff --git a/tools/arch/x86/include/uapi/asm/vmx.h b/tools/arch/x86/include/uapi/asm/vmx.h
index 37fee27..1445865 100644
--- a/tools/arch/x86/include/uapi/asm/vmx.h
+++ b/tools/arch/x86/include/uapi/asm/vmx.h
@@ -65,6 +65,8 @@
#define EXIT_REASON_TPR_BELOW_THRESHOLD 43
#define EXIT_REASON_APIC_ACCESS 44
#define EXIT_REASON_EOI_INDUCED 45
+#define EXIT_REASON_GDTR_IDTR 46
+#define EXIT_REASON_LDTR_TR 47
#define EXIT_REASON_EPT_VIOLATION 48
#define EXIT_REASON_EPT_MISCONFIG 49
#define EXIT_REASON_INVEPT 50
@@ -113,6 +115,8 @@
{ EXIT_REASON_MCE_DURING_VMENTRY, "MCE_DURING_VMENTRY" }, \
{ EXIT_REASON_TPR_BELOW_THRESHOLD, "TPR_BELOW_THRESHOLD" }, \
{ EXIT_REASON_APIC_ACCESS, "APIC_ACCESS" }, \
+ { EXIT_REASON_GDTR_IDTR, "GDTR_IDTR" }, \
+ { EXIT_REASON_LDTR_TR, "LDTR_TR" }, \
{ EXIT_REASON_EPT_VIOLATION, "EPT_VIOLATION" }, \
{ EXIT_REASON_EPT_MISCONFIG, "EPT_MISCONFIG" }, \
{ EXIT_REASON_INVEPT, "INVEPT" }, \
@@ -129,6 +133,7 @@
{ EXIT_REASON_XRSTORS, "XRSTORS" }
#define VMX_ABORT_SAVE_GUEST_MSR_FAIL 1
+#define VMX_ABORT_LOAD_HOST_PDPTE_FAIL 2
#define VMX_ABORT_LOAD_HOST_MSR_FAIL 4
#endif /* _UAPIVMX_H */
diff --git a/tools/build/Makefile.build b/tools/build/Makefile.build
index 99c0ccd..e279a71 100644
--- a/tools/build/Makefile.build
+++ b/tools/build/Makefile.build
@@ -19,6 +19,16 @@
Q=@
endif
+ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
+ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
+ quiet=silent_
+endif
+else # make-3.8x
+ifneq ($(filter s% -s%,$(MAKEFLAGS)),)
+ quiet=silent_
+endif
+endif
+
build-dir := $(srctree)/tools/build
# Define $(fixdep) for dep-cmd function
diff --git a/tools/include/linux/compiler-gcc.h b/tools/include/linux/compiler-gcc.h
new file mode 100644
index 0000000..48af2f1
--- /dev/null
+++ b/tools/include/linux/compiler-gcc.h
@@ -0,0 +1,14 @@
+#ifndef _TOOLS_LINUX_COMPILER_H_
+#error "Please don't include <linux/compiler-gcc.h> directly, include <linux/compiler.h> instead."
+#endif
+
+/*
+ * Common definitions for all gcc versions go here.
+ */
+#define GCC_VERSION (__GNUC__ * 10000 \
+ + __GNUC_MINOR__ * 100 \
+ + __GNUC_PATCHLEVEL__)
+
+#if GCC_VERSION >= 70000 && !defined(__CHECKER__)
+# define __fallthrough __attribute__ ((fallthrough))
+#endif
diff --git a/tools/include/linux/compiler.h b/tools/include/linux/compiler.h
index e33fc1d..6326ede 100644
--- a/tools/include/linux/compiler.h
+++ b/tools/include/linux/compiler.h
@@ -1,6 +1,10 @@
#ifndef _TOOLS_LINUX_COMPILER_H_
#define _TOOLS_LINUX_COMPILER_H_
+#ifdef __GNUC__
+#include <linux/compiler-gcc.h>
+#endif
+
/* Optimization barrier */
/* The "volatile" is due to gcc bugs */
#define barrier() __asm__ __volatile__("": : :"memory")
@@ -126,4 +130,9 @@
#define WRITE_ONCE(x, val) \
({ union { typeof(x) __val; char __c[1]; } __u = { .__val = (val) }; __write_once_size(&(x), __u.__c, sizeof(x)); __u.__val; })
+
+#ifndef __fallthrough
+# define __fallthrough
+#endif
+
#endif /* _TOOLS_LINUX_COMPILER_H */
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 0eb0e87..d2b0ac7 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -116,6 +116,12 @@
#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
+/* If BPF_F_ALLOW_OVERRIDE flag is used in BPF_PROG_ATTACH command
+ * to the given target_fd cgroup the descendent cgroup will be able to
+ * override effective bpf program that was inherited from this cgroup
+ */
+#define BPF_F_ALLOW_OVERRIDE (1U << 0)
+
#define BPF_PSEUDO_MAP_FD 1
/* flags for BPF_MAP_UPDATE_ELEM command */
@@ -171,6 +177,7 @@
__u32 target_fd; /* container object to attach to */
__u32 attach_bpf_fd; /* eBPF program to attach */
__u32 attach_type;
+ __u32 attach_flags;
};
} __attribute__((aligned(8)));
diff --git a/tools/leds/Makefile b/tools/leds/Makefile
index c03a79e..078b666 100644
--- a/tools/leds/Makefile
+++ b/tools/leds/Makefile
@@ -3,11 +3,11 @@
CC = $(CROSS_COMPILE)gcc
CFLAGS = -Wall -Wextra -g -I../../include/uapi
-all: uledmon
+all: uledmon led_hw_brightness_mon
%: %.c
$(CC) $(CFLAGS) -o $@ $^
clean:
- $(RM) uledmon
+ $(RM) uledmon led_hw_brightness_mon
.PHONY: all clean
diff --git a/tools/leds/led_hw_brightness_mon.c b/tools/leds/led_hw_brightness_mon.c
new file mode 100644
index 0000000..64642cc
--- /dev/null
+++ b/tools/leds/led_hw_brightness_mon.c
@@ -0,0 +1,84 @@
+/*
+ * led_hw_brightness_mon.c
+ *
+ * This program monitors LED brightness level changes having its origin
+ * in hardware/firmware, i.e. outside of kernel control.
+ * A timestamp and brightness value is printed each time the brightness changes.
+ *
+ * Usage: led_hw_brightness_mon <device-name>
+ *
+ * <device-name> is the name of the LED class device to be monitored. Pressing
+ * CTRL+C will exit.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <linux/uleds.h>
+
+int main(int argc, char const *argv[])
+{
+ int fd, ret;
+ char brightness_file_path[LED_MAX_NAME_SIZE + 11];
+ struct pollfd pollfd;
+ struct timespec ts;
+ char buf[11];
+
+ if (argc != 2) {
+ fprintf(stderr, "Requires <device-name> argument\n");
+ return 1;
+ }
+
+ snprintf(brightness_file_path, LED_MAX_NAME_SIZE,
+ "/sys/class/leds/%s/brightness_hw_changed", argv[1]);
+
+ fd = open(brightness_file_path, O_RDONLY);
+ if (fd == -1) {
+ printf("Failed to open %s file\n", brightness_file_path);
+ return 1;
+ }
+
+ /*
+ * read may fail if no hw brightness change has occurred so far,
+ * but it is required to avoid spurious poll notifications in
+ * the opposite case.
+ */
+ read(fd, buf, sizeof(buf));
+
+ pollfd.fd = fd;
+ pollfd.events = POLLPRI;
+
+ while (1) {
+ ret = poll(&pollfd, 1, -1);
+ if (ret == -1) {
+ printf("Failed to poll %s file (%d)\n",
+ brightness_file_path, ret);
+ ret = 1;
+ break;
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ ret = read(fd, buf, sizeof(buf));
+ if (ret < 0)
+ break;
+
+ ret = lseek(pollfd.fd, 0, SEEK_SET);
+ if (ret < 0) {
+ printf("lseek failed (%d)\n", ret);
+ break;
+ }
+
+ printf("[%ld.%09ld] %d\n", ts.tv_sec, ts.tv_nsec, atoi(buf));
+ }
+
+ close(fd);
+
+ return ret;
+}
diff --git a/tools/lib/api/Makefile b/tools/lib/api/Makefile
index adba83b..eb6e0b3 100644
--- a/tools/lib/api/Makefile
+++ b/tools/lib/api/Makefile
@@ -17,7 +17,13 @@
LIBFILE = $(OUTPUT)libapi.a
CFLAGS := $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
-CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -O6 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC
+CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC
+
+ifeq ($(CC), clang)
+ CFLAGS += -O3
+else
+ CFLAGS += -O6
+endif
# Treat warnings as errors unless directed not to
ifneq ($(WERROR),0)
diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c
index f99f49e4..4b6bfc4 100644
--- a/tools/lib/api/fs/fs.c
+++ b/tools/lib/api/fs/fs.c
@@ -38,6 +38,10 @@
#define HUGETLBFS_MAGIC 0x958458f6
#endif
+#ifndef BPF_FS_MAGIC
+#define BPF_FS_MAGIC 0xcafe4a11
+#endif
+
static const char * const sysfs__fs_known_mountpoints[] = {
"/sys",
0,
@@ -75,6 +79,11 @@
0,
};
+static const char * const bpf_fs__known_mountpoints[] = {
+ "/sys/fs/bpf",
+ 0,
+};
+
struct fs {
const char *name;
const char * const *mounts;
@@ -89,6 +98,7 @@
FS__DEBUGFS = 2,
FS__TRACEFS = 3,
FS__HUGETLBFS = 4,
+ FS__BPF_FS = 5,
};
#ifndef TRACEFS_MAGIC
@@ -121,6 +131,11 @@
.mounts = hugetlbfs__known_mountpoints,
.magic = HUGETLBFS_MAGIC,
},
+ [FS__BPF_FS] = {
+ .name = "bpf",
+ .mounts = bpf_fs__known_mountpoints,
+ .magic = BPF_FS_MAGIC,
+ },
};
static bool fs__read_mounts(struct fs *fs)
@@ -280,6 +295,7 @@
FS(debugfs, FS__DEBUGFS);
FS(tracefs, FS__TRACEFS);
FS(hugetlbfs, FS__HUGETLBFS);
+FS(bpf_fs, FS__BPF_FS);
int filename__read_int(const char *filename, int *value)
{
diff --git a/tools/lib/api/fs/fs.h b/tools/lib/api/fs/fs.h
index a63269f..6b332dc 100644
--- a/tools/lib/api/fs/fs.h
+++ b/tools/lib/api/fs/fs.h
@@ -22,6 +22,7 @@
FS(debugfs)
FS(tracefs)
FS(hugetlbfs)
+FS(bpf_fs)
#undef FS
diff --git a/tools/lib/api/fs/tracing_path.c b/tools/lib/api/fs/tracing_path.c
index 251b7c3..3e606b9 100644
--- a/tools/lib/api/fs/tracing_path.c
+++ b/tools/lib/api/fs/tracing_path.c
@@ -86,9 +86,13 @@
free(file);
}
-static int strerror_open(int err, char *buf, size_t size, const char *filename)
+int tracing_path__strerror_open_tp(int err, char *buf, size_t size,
+ const char *sys, const char *name)
{
char sbuf[128];
+ char filename[PATH_MAX];
+
+ snprintf(filename, PATH_MAX, "%s/%s", sys, name ?: "*");
switch (err) {
case ENOENT:
@@ -99,10 +103,19 @@
* - jirka
*/
if (debugfs__configured() || tracefs__configured()) {
- snprintf(buf, size,
- "Error:\tFile %s/%s not found.\n"
- "Hint:\tPerhaps this kernel misses some CONFIG_ setting to enable this feature?.\n",
- tracing_events_path, filename);
+ /* sdt markers */
+ if (!strncmp(filename, "sdt_", 4)) {
+ snprintf(buf, size,
+ "Error:\tFile %s/%s not found.\n"
+ "Hint:\tSDT event cannot be directly recorded on.\n"
+ "\tPlease first use 'perf probe %s:%s' before recording it.\n",
+ tracing_events_path, filename, sys, name);
+ } else {
+ snprintf(buf, size,
+ "Error:\tFile %s/%s not found.\n"
+ "Hint:\tPerhaps this kernel misses some CONFIG_ setting to enable this feature?.\n",
+ tracing_events_path, filename);
+ }
break;
}
snprintf(buf, size, "%s",
@@ -125,12 +138,3 @@
return 0;
}
-
-int tracing_path__strerror_open_tp(int err, char *buf, size_t size, const char *sys, const char *name)
-{
- char path[PATH_MAX];
-
- snprintf(path, PATH_MAX, "%s/%s", sys, name ?: "*");
-
- return strerror_open(err, buf, size, path);
-}
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index 3ddb58a..ae752fa 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -168,7 +168,8 @@
return sys_bpf(BPF_OBJ_GET, &attr, sizeof(attr));
}
-int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type)
+int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type,
+ unsigned int flags)
{
union bpf_attr attr;
@@ -176,6 +177,7 @@
attr.target_fd = target_fd;
attr.attach_bpf_fd = prog_fd;
attr.attach_type = type;
+ attr.attach_flags = flags;
return sys_bpf(BPF_PROG_ATTACH, &attr, sizeof(attr));
}
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index a2f9853..44fb7c5 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -22,6 +22,7 @@
#define __BPF_BPF_H
#include <linux/bpf.h>
+#include <stddef.h>
int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
int max_entries, __u32 map_flags);
@@ -41,7 +42,8 @@
int bpf_map_get_next_key(int fd, void *key, void *next_key);
int bpf_obj_pin(int fd, const char *pathname);
int bpf_obj_get(const char *pathname);
-int bpf_prog_attach(int prog_fd, int attachable_fd, enum bpf_attach_type type);
+int bpf_prog_attach(int prog_fd, int attachable_fd, enum bpf_attach_type type,
+ unsigned int flags);
int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type);
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 84e6b35..ac6eb86 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -4,6 +4,7 @@
* Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
* Copyright (C) 2015 Huawei Inc.
+ * Copyright (C) 2017 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -22,15 +23,21 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
+#include <libgen.h>
#include <inttypes.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <asm/unistd.h>
+#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/bpf.h>
#include <linux/list.h>
+#include <linux/limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
#include <libelf.h>
#include <gelf.h>
@@ -41,6 +48,10 @@
#define EM_BPF 247
#endif
+#ifndef BPF_FS_MAGIC
+#define BPF_FS_MAGIC 0xcafe4a11
+#endif
+
#define __printf(a, b) __attribute__((format(printf, a, b)))
__printf(1, 2)
@@ -779,7 +790,7 @@
bpf_program__collect_reloc(struct bpf_program *prog,
size_t nr_maps, GElf_Shdr *shdr,
Elf_Data *data, Elf_Data *symbols,
- int maps_shndx)
+ int maps_shndx, struct bpf_map *maps)
{
int i, nrels;
@@ -829,7 +840,15 @@
return -LIBBPF_ERRNO__RELOC;
}
- map_idx = sym.st_value / sizeof(struct bpf_map_def);
+ /* TODO: 'maps' is sorted. We can use bsearch to make it faster. */
+ for (map_idx = 0; map_idx < nr_maps; map_idx++) {
+ if (maps[map_idx].offset == sym.st_value) {
+ pr_debug("relocation: find map %zd (%s) for insn %u\n",
+ map_idx, maps[map_idx].name, insn_idx);
+ break;
+ }
+ }
+
if (map_idx >= nr_maps) {
pr_warning("bpf relocation: map_idx %d large than %d\n",
(int)map_idx, (int)nr_maps - 1);
@@ -953,7 +972,8 @@
err = bpf_program__collect_reloc(prog, nr_maps,
shdr, data,
obj->efile.symbols,
- obj->efile.maps_shndx);
+ obj->efile.maps_shndx,
+ obj->maps);
if (err)
return err;
}
@@ -1227,6 +1247,191 @@
return err;
}
+static int check_path(const char *path)
+{
+ struct statfs st_fs;
+ char *dname, *dir;
+ int err = 0;
+
+ if (path == NULL)
+ return -EINVAL;
+
+ dname = strdup(path);
+ if (dname == NULL)
+ return -ENOMEM;
+
+ dir = dirname(dname);
+ if (statfs(dir, &st_fs)) {
+ pr_warning("failed to statfs %s: %s\n", dir, strerror(errno));
+ err = -errno;
+ }
+ free(dname);
+
+ if (!err && st_fs.f_type != BPF_FS_MAGIC) {
+ pr_warning("specified path %s is not on BPF FS\n", path);
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+int bpf_program__pin_instance(struct bpf_program *prog, const char *path,
+ int instance)
+{
+ int err;
+
+ err = check_path(path);
+ if (err)
+ return err;
+
+ if (prog == NULL) {
+ pr_warning("invalid program pointer\n");
+ return -EINVAL;
+ }
+
+ if (instance < 0 || instance >= prog->instances.nr) {
+ pr_warning("invalid prog instance %d of prog %s (max %d)\n",
+ instance, prog->section_name, prog->instances.nr);
+ return -EINVAL;
+ }
+
+ if (bpf_obj_pin(prog->instances.fds[instance], path)) {
+ pr_warning("failed to pin program: %s\n", strerror(errno));
+ return -errno;
+ }
+ pr_debug("pinned program '%s'\n", path);
+
+ return 0;
+}
+
+static int make_dir(const char *path)
+{
+ int err = 0;
+
+ if (mkdir(path, 0700) && errno != EEXIST)
+ err = -errno;
+
+ if (err)
+ pr_warning("failed to mkdir %s: %s\n", path, strerror(-err));
+ return err;
+}
+
+int bpf_program__pin(struct bpf_program *prog, const char *path)
+{
+ int i, err;
+
+ err = check_path(path);
+ if (err)
+ return err;
+
+ if (prog == NULL) {
+ pr_warning("invalid program pointer\n");
+ return -EINVAL;
+ }
+
+ if (prog->instances.nr <= 0) {
+ pr_warning("no instances of prog %s to pin\n",
+ prog->section_name);
+ return -EINVAL;
+ }
+
+ err = make_dir(path);
+ if (err)
+ return err;
+
+ for (i = 0; i < prog->instances.nr; i++) {
+ char buf[PATH_MAX];
+ int len;
+
+ len = snprintf(buf, PATH_MAX, "%s/%d", path, i);
+ if (len < 0)
+ return -EINVAL;
+ else if (len >= PATH_MAX)
+ return -ENAMETOOLONG;
+
+ err = bpf_program__pin_instance(prog, buf, i);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+int bpf_map__pin(struct bpf_map *map, const char *path)
+{
+ int err;
+
+ err = check_path(path);
+ if (err)
+ return err;
+
+ if (map == NULL) {
+ pr_warning("invalid map pointer\n");
+ return -EINVAL;
+ }
+
+ if (bpf_obj_pin(map->fd, path)) {
+ pr_warning("failed to pin map: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ pr_debug("pinned map '%s'\n", path);
+ return 0;
+}
+
+int bpf_object__pin(struct bpf_object *obj, const char *path)
+{
+ struct bpf_program *prog;
+ struct bpf_map *map;
+ int err;
+
+ if (!obj)
+ return -ENOENT;
+
+ if (!obj->loaded) {
+ pr_warning("object not yet loaded; load it first\n");
+ return -ENOENT;
+ }
+
+ err = make_dir(path);
+ if (err)
+ return err;
+
+ bpf_map__for_each(map, obj) {
+ char buf[PATH_MAX];
+ int len;
+
+ len = snprintf(buf, PATH_MAX, "%s/%s", path,
+ bpf_map__name(map));
+ if (len < 0)
+ return -EINVAL;
+ else if (len >= PATH_MAX)
+ return -ENAMETOOLONG;
+
+ err = bpf_map__pin(map, buf);
+ if (err)
+ return err;
+ }
+
+ bpf_object__for_each_program(prog, obj) {
+ char buf[PATH_MAX];
+ int len;
+
+ len = snprintf(buf, PATH_MAX, "%s/%s", path,
+ prog->section_name);
+ if (len < 0)
+ return -EINVAL;
+ else if (len >= PATH_MAX)
+ return -ENAMETOOLONG;
+
+ err = bpf_program__pin(prog, buf);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
void bpf_object__close(struct bpf_object *obj)
{
size_t i;
@@ -1419,37 +1624,33 @@
prog->type = type;
}
-int bpf_program__set_tracepoint(struct bpf_program *prog)
-{
- if (!prog)
- return -EINVAL;
- bpf_program__set_type(prog, BPF_PROG_TYPE_TRACEPOINT);
- return 0;
-}
-
-int bpf_program__set_kprobe(struct bpf_program *prog)
-{
- if (!prog)
- return -EINVAL;
- bpf_program__set_type(prog, BPF_PROG_TYPE_KPROBE);
- return 0;
-}
-
static bool bpf_program__is_type(struct bpf_program *prog,
enum bpf_prog_type type)
{
return prog ? (prog->type == type) : false;
}
-bool bpf_program__is_tracepoint(struct bpf_program *prog)
-{
- return bpf_program__is_type(prog, BPF_PROG_TYPE_TRACEPOINT);
-}
+#define BPF_PROG_TYPE_FNS(NAME, TYPE) \
+int bpf_program__set_##NAME(struct bpf_program *prog) \
+{ \
+ if (!prog) \
+ return -EINVAL; \
+ bpf_program__set_type(prog, TYPE); \
+ return 0; \
+} \
+ \
+bool bpf_program__is_##NAME(struct bpf_program *prog) \
+{ \
+ return bpf_program__is_type(prog, TYPE); \
+} \
-bool bpf_program__is_kprobe(struct bpf_program *prog)
-{
- return bpf_program__is_type(prog, BPF_PROG_TYPE_KPROBE);
-}
+BPF_PROG_TYPE_FNS(socket_filter, BPF_PROG_TYPE_SOCKET_FILTER);
+BPF_PROG_TYPE_FNS(kprobe, BPF_PROG_TYPE_KPROBE);
+BPF_PROG_TYPE_FNS(sched_cls, BPF_PROG_TYPE_SCHED_CLS);
+BPF_PROG_TYPE_FNS(sched_act, BPF_PROG_TYPE_SCHED_ACT);
+BPF_PROG_TYPE_FNS(tracepoint, BPF_PROG_TYPE_TRACEPOINT);
+BPF_PROG_TYPE_FNS(xdp, BPF_PROG_TYPE_XDP);
+BPF_PROG_TYPE_FNS(perf_event, BPF_PROG_TYPE_PERF_EVENT);
int bpf_map__fd(struct bpf_map *map)
{
@@ -1537,3 +1738,10 @@
}
return ERR_PTR(-ENOENT);
}
+
+long libbpf_get_error(const void *ptr)
+{
+ if (IS_ERR(ptr))
+ return PTR_ERR(ptr);
+ return 0;
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index a5a8b86..b30394f 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -22,8 +22,8 @@
#define __BPF_LIBBPF_H
#include <stdio.h>
+#include <stdint.h>
#include <stdbool.h>
-#include <linux/err.h>
#include <sys/types.h> // for size_t
enum libbpf_errno {
@@ -65,6 +65,7 @@
struct bpf_object *bpf_object__open_buffer(void *obj_buf,
size_t obj_buf_sz,
const char *name);
+int bpf_object__pin(struct bpf_object *object, const char *path);
void bpf_object__close(struct bpf_object *object);
/* Load/unload object into/from kernel */
@@ -106,6 +107,9 @@
const char *bpf_program__title(struct bpf_program *prog, bool needs_copy);
int bpf_program__fd(struct bpf_program *prog);
+int bpf_program__pin_instance(struct bpf_program *prog, const char *path,
+ int instance);
+int bpf_program__pin(struct bpf_program *prog, const char *path);
struct bpf_insn;
@@ -174,11 +178,21 @@
/*
* Adjust type of bpf program. Default is kprobe.
*/
+int bpf_program__set_socket_filter(struct bpf_program *prog);
int bpf_program__set_tracepoint(struct bpf_program *prog);
int bpf_program__set_kprobe(struct bpf_program *prog);
+int bpf_program__set_sched_cls(struct bpf_program *prog);
+int bpf_program__set_sched_act(struct bpf_program *prog);
+int bpf_program__set_xdp(struct bpf_program *prog);
+int bpf_program__set_perf_event(struct bpf_program *prog);
+bool bpf_program__is_socket_filter(struct bpf_program *prog);
bool bpf_program__is_tracepoint(struct bpf_program *prog);
bool bpf_program__is_kprobe(struct bpf_program *prog);
+bool bpf_program__is_sched_cls(struct bpf_program *prog);
+bool bpf_program__is_sched_act(struct bpf_program *prog);
+bool bpf_program__is_xdp(struct bpf_program *prog);
+bool bpf_program__is_perf_event(struct bpf_program *prog);
/*
* We don't need __attribute__((packed)) now since it is
@@ -223,5 +237,8 @@
int bpf_map__set_priv(struct bpf_map *map, void *priv,
bpf_map_clear_priv_t clear_priv);
void *bpf_map__priv(struct bpf_map *map);
+int bpf_map__pin(struct bpf_map *map, const char *path);
+
+long libbpf_get_error(const void *ptr);
#endif
diff --git a/tools/lib/subcmd/Makefile b/tools/lib/subcmd/Makefile
index 3f8cc44..3d1c3b5 100644
--- a/tools/lib/subcmd/Makefile
+++ b/tools/lib/subcmd/Makefile
@@ -19,7 +19,13 @@
LIBFILE = $(OUTPUT)libsubcmd.a
CFLAGS := $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
-CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -O6 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC
+CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC
+
+ifeq ($(CC), clang)
+ CFLAGS += -O3
+else
+ CFLAGS += -O6
+endif
# Treat warnings as errors unless directed not to
ifneq ($(WERROR),0)
diff --git a/tools/lib/subcmd/parse-options.c b/tools/lib/subcmd/parse-options.c
index 8aad811..6bc2402 100644
--- a/tools/lib/subcmd/parse-options.c
+++ b/tools/lib/subcmd/parse-options.c
@@ -270,6 +270,8 @@
}
if (get_arg(p, opt, flags, &arg))
return -1;
+ if (arg[0] == '-')
+ return opterror(opt, "expects an unsigned numerical value", flags);
*(unsigned int *)opt->value = strtol(arg, (char **)&s, 10);
if (*s)
return opterror(opt, "expects a numerical value", flags);
@@ -302,6 +304,8 @@
}
if (get_arg(p, opt, flags, &arg))
return -1;
+ if (arg[0] == '-')
+ return opterror(opt, "expects an unsigned numerical value", flags);
*(u64 *)opt->value = strtoull(arg, (char **)&s, 10);
if (*s)
return opterror(opt, "expects a numerical value", flags);
diff --git a/tools/lib/subcmd/parse-options.h b/tools/lib/subcmd/parse-options.h
index 11c3be3..f054ca1 100644
--- a/tools/lib/subcmd/parse-options.h
+++ b/tools/lib/subcmd/parse-options.h
@@ -1,6 +1,7 @@
#ifndef __SUBCMD_PARSE_OPTIONS_H
#define __SUBCMD_PARSE_OPTIONS_H
+#include <linux/kernel.h>
#include <stdbool.h>
#include <stdint.h>
@@ -132,32 +133,32 @@
#define OPT_UINTEGER(s, l, v, h) { .type = OPTION_UINTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h) }
#define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = check_vtype(v, long *), .help = (h) }
#define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = check_vtype(v, u64 *), .help = (h) }
-#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h) }
+#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), .argh = (a), .help = (h) }
#define OPT_STRING_OPTARG(s, l, v, a, h, d) \
{ .type = OPTION_STRING, .short_name = (s), .long_name = (l), \
- .value = check_vtype(v, const char **), (a), .help = (h), \
+ .value = check_vtype(v, const char **), .argh =(a), .help = (h), \
.flags = PARSE_OPT_OPTARG, .defval = (intptr_t)(d) }
#define OPT_STRING_OPTARG_SET(s, l, v, os, a, h, d) \
{ .type = OPTION_STRING, .short_name = (s), .long_name = (l), \
- .value = check_vtype(v, const char **), (a), .help = (h), \
+ .value = check_vtype(v, const char **), .argh = (a), .help = (h), \
.flags = PARSE_OPT_OPTARG, .defval = (intptr_t)(d), \
.set = check_vtype(os, bool *)}
-#define OPT_STRING_NOEMPTY(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h), .flags = PARSE_OPT_NOEMPTY}
+#define OPT_STRING_NOEMPTY(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), .argh = (a), .help = (h), .flags = PARSE_OPT_NOEMPTY}
#define OPT_DATE(s, l, v, h) \
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb }
#define OPT_CALLBACK(s, l, v, a, h, f) \
- { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f) }
+ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f) }
#define OPT_CALLBACK_NOOPT(s, l, v, a, h, f) \
- { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .flags = PARSE_OPT_NOARG }
+ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f), .flags = PARSE_OPT_NOARG }
#define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \
- { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d, .flags = PARSE_OPT_LASTARG_DEFAULT }
+ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f), .defval = (intptr_t)d, .flags = PARSE_OPT_LASTARG_DEFAULT }
#define OPT_CALLBACK_DEFAULT_NOOPT(s, l, v, a, h, f, d) \
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l),\
- .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d,\
+ .value = (v), .arg = (a), .help = (h), .callback = (f), .defval = (intptr_t)d,\
.flags = PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NOARG}
#define OPT_CALLBACK_OPTARG(s, l, v, d, a, h, f) \
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), \
- .value = (v), (a), .help = (h), .callback = (f), \
+ .value = (v), .argh = (a), .help = (h), .callback = (f), \
.flags = PARSE_OPT_OPTARG, .data = (d) }
/* parse_options() will filter out the processed options and leave the
diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile
index 2616c66..47076b1 100644
--- a/tools/lib/traceevent/Makefile
+++ b/tools/lib/traceevent/Makefile
@@ -257,10 +257,16 @@
endef
define do_generate_dynamic_list_file
- (echo '{'; \
- $(NM) -u -D $1 | awk 'NF>1 {print "\t"$$2";"}' | sort -u; \
- echo '};'; \
- ) > $2
+ symbol_type=`$(NM) -u -D $1 | awk 'NF>1 {print $$1}' | \
+ xargs echo "U W w" | tr ' ' '\n' | sort -u | xargs echo`;\
+ if [ "$$symbol_type" = "U W w" ];then \
+ (echo '{'; \
+ $(NM) -u -D $1 | awk 'NF>1 {print "\t"$$2";"}' | sort -u;\
+ echo '};'; \
+ ) > $2; \
+ else \
+ (echo Either missing one of [$1] or bad version of $(NM)) 1>&2;\
+ fi
endef
install_lib: all_cmd install_plugins
diff --git a/tools/lib/traceevent/kbuffer-parse.c b/tools/lib/traceevent/kbuffer-parse.c
index 65984f1..c94e364 100644
--- a/tools/lib/traceevent/kbuffer-parse.c
+++ b/tools/lib/traceevent/kbuffer-parse.c
@@ -315,6 +315,7 @@
extend += delta;
delta = extend;
ptr += 4;
+ length = 0;
break;
case OLD_RINGBUF_TYPE_TIME_STAMP:
diff --git a/tools/lib/traceevent/plugin_function.c b/tools/lib/traceevent/plugin_function.c
index a00ec19..42dbf73 100644
--- a/tools/lib/traceevent/plugin_function.c
+++ b/tools/lib/traceevent/plugin_function.c
@@ -130,7 +130,7 @@
unsigned long long pfunction;
const char *func;
const char *parent;
- int index;
+ int index = 0;
if (pevent_get_field_val(s, event, "ip", record, &function, 1))
return trace_seq_putc(s, '!');
diff --git a/tools/perf/Build b/tools/perf/Build
index b12d5d1..9b79f8d 100644
--- a/tools/perf/Build
+++ b/tools/perf/Build
@@ -3,10 +3,12 @@
perf-y += builtin-config.o
perf-y += builtin-diff.o
perf-y += builtin-evlist.o
+perf-y += builtin-ftrace.o
perf-y += builtin-help.o
perf-y += builtin-sched.o
perf-y += builtin-buildid-list.o
perf-y += builtin-buildid-cache.o
+perf-y += builtin-kallsyms.o
perf-y += builtin-list.o
perf-y += builtin-record.o
perf-y += builtin-report.o
@@ -39,8 +41,7 @@
CFLAGS_builtin-timechart.o += $(paths)
CFLAGS_perf.o += -DPERF_HTML_PATH="BUILD_STR($(htmldir_SQ))" \
-DPERF_EXEC_PATH="BUILD_STR($(perfexecdir_SQ))" \
- -DPREFIX="BUILD_STR($(prefix_SQ))" \
- -include $(OUTPUT)PERF-VERSION-FILE
+ -DPREFIX="BUILD_STR($(prefix_SQ))"
CFLAGS_builtin-trace.o += -DSTRACE_GROUPS_DIR="BUILD_STR($(STRACE_GROUPS_DIR_SQ))"
CFLAGS_builtin-report.o += -DTIPDIR="BUILD_STR($(tipdir_SQ))"
CFLAGS_builtin-report.o += -DDOCDIR="BUILD_STR($(srcdir_SQ)/Documentation)"
diff --git a/tools/perf/Documentation/perf-c2c.txt b/tools/perf/Documentation/perf-c2c.txt
index 3f06730..2da07e5 100644
--- a/tools/perf/Documentation/perf-c2c.txt
+++ b/tools/perf/Documentation/perf-c2c.txt
@@ -248,7 +248,7 @@
Code address, Code symbol, Shared Object, Source line
dso - coalesced by shared object
-By default the coalescing is setup with 'pid,tid,iaddr'.
+By default the coalescing is setup with 'pid,iaddr'.
STDIO OUTPUT
------------
diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt
index 9365b75..5b4fff3 100644
--- a/tools/perf/Documentation/perf-config.txt
+++ b/tools/perf/Documentation/perf-config.txt
@@ -498,6 +498,18 @@
But if this option is 'no-cache', it will not update the build-id cache.
'skip' skips post-processing and does not update the cache.
+diff.*::
+ diff.order::
+ This option sets the number of columns to sort the result.
+ The default is 0, which means sorting by baseline.
+ Setting it to 1 will sort the result by delta (or other
+ compute method selected).
+
+ diff.compute::
+ This options sets the method for computing the diff result.
+ Possible values are 'delta', 'delta-abs', 'ratio' and
+ 'wdiff'. Default is 'delta'.
+
SEE ALSO
--------
linkperf:perf[1]
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt
index 3e9490b..66dbe3de 100644
--- a/tools/perf/Documentation/perf-diff.txt
+++ b/tools/perf/Documentation/perf-diff.txt
@@ -86,8 +86,9 @@
-c::
--compute::
- Differential computation selection - delta,ratio,wdiff (default is delta).
- See COMPARISON METHODS section for more info.
+ Differential computation selection - delta, ratio, wdiff, delta-abs
+ (default is delta-abs). Default can be changed using diff.compute
+ config option. See COMPARISON METHODS section for more info.
-p::
--period::
@@ -99,7 +100,11 @@
-o::
--order::
- Specify compute sorting column number.
+ Specify compute sorting column number. 0 means sorting by baseline
+ overhead and 1 (default) means sorting by computed value of column 1
+ (data from the first file other base baseline). Values more than 1
+ can be used only if enough data files are provided.
+ The default value can be set using the diff.order config option.
--percentage::
Determine how to display the overhead percentage of filtered entries.
@@ -181,6 +186,10 @@
relative to how entries are filtered. Use --percentage=absolute to
prevent such fluctuation.
+delta-abs
+~~~~~~~~~
+Same as 'delta` method, but sort the result with the absolute values.
+
ratio
~~~~~
If specified the 'Ratio' column is displayed with value 'r' computed as:
diff --git a/tools/perf/Documentation/perf-ftrace.txt b/tools/perf/Documentation/perf-ftrace.txt
new file mode 100644
index 0000000..2d96de6
--- /dev/null
+++ b/tools/perf/Documentation/perf-ftrace.txt
@@ -0,0 +1,36 @@
+perf-ftrace(1)
+=============
+
+NAME
+----
+perf-ftrace - simple wrapper for kernel's ftrace functionality
+
+
+SYNOPSIS
+--------
+[verse]
+'perf ftrace' <command>
+
+DESCRIPTION
+-----------
+The 'perf ftrace' command is a simple wrapper of kernel's ftrace
+functionality. It only supports single thread tracing currently and
+just reads trace_pipe in text and then write it to stdout.
+
+The following options apply to perf ftrace.
+
+OPTIONS
+-------
+
+-t::
+--tracer=::
+ Tracer to use: function_graph or function.
+
+-v::
+--verbose=::
+ Verbosity level.
+
+
+SEE ALSO
+--------
+linkperf:perf-record[1], linkperf:perf-trace[1]
diff --git a/tools/perf/Documentation/perf-kallsyms.txt b/tools/perf/Documentation/perf-kallsyms.txt
new file mode 100644
index 0000000..954ea9e
--- /dev/null
+++ b/tools/perf/Documentation/perf-kallsyms.txt
@@ -0,0 +1,24 @@
+perf-kallsyms(1)
+==============
+
+NAME
+----
+perf-kallsyms - Searches running kernel for symbols
+
+SYNOPSIS
+--------
+[verse]
+'perf kallsyms <options> symbol_name[,symbol_name...]'
+
+DESCRIPTION
+-----------
+This command searches the running kernel kallsyms file for the given symbol(s)
+and prints information about it, including the DSO, the kallsyms begin/end
+addresses and the addresses in the ELF kallsyms symbol table (for symbols in
+modules).
+
+OPTIONS
+-------
+-v::
+--verbose=::
+ Increase verbosity level, showing details about symbol table loading, etc.
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index 5054d91..27256bc 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -421,9 +421,19 @@
--timestamp-filename
Append timestamp to output file name.
---switch-output::
+--switch-output[=mode]::
Generate multiple perf.data files, timestamp prefixed, switching to a new one
-when receiving a SIGUSR2.
+based on 'mode' value:
+ "signal" - when receiving a SIGUSR2 (default value) or
+ <size> - when reaching the size threshold, size is expected to
+ be a number with appended unit character - B/K/M/G
+ <time> - when reaching the time threshold, size is expected to
+ be a number with appended unit character - s/m/h/d
+
+ Note: the precision of the size threshold hugely depends
+ on your configuration - the number and size of your ring
+ buffers (-m). It is generally more precise for higher sizes
+ (like >5M), for lower values expect different sizes.
A possible use case is to, given an external event, slice the perf.data file
that gets then processed, possibly via a perf script, to decide if that
diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt
index 7617396..d33dedd 100644
--- a/tools/perf/Documentation/perf-sched.txt
+++ b/tools/perf/Documentation/perf-sched.txt
@@ -143,6 +143,8 @@
stop time is not given (i.e, time string is 'x.y,') then analysis goes
to end of file.
+--state::
+ Show task state when it switched out.
SEE ALSO
--------
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
index 5dc5c6a..4ed5f23 100644
--- a/tools/perf/Documentation/perf-script.txt
+++ b/tools/perf/Documentation/perf-script.txt
@@ -36,7 +36,7 @@
'perf script report <script> [args]' to run and display the results
of <script>. <script> is the name displayed in the output of 'perf
- trace --list' i.e. the actual script name minus any language
+ script --list' i.e. the actual script name minus any language
extension. The perf.data output from a previous run of 'perf script
record <script>' is used and should be present for this command to
succeed. [args] refers to the (mainly optional) args expected by
@@ -76,7 +76,7 @@
Any command you can specify in a shell.
-D::
---dump-raw-script=::
+--dump-raw-trace=::
Display verbose dump of the trace data.
-L::
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt
index 781b019..afd7286 100644
--- a/tools/perf/Documentation/perf-trace.txt
+++ b/tools/perf/Documentation/perf-trace.txt
@@ -35,7 +35,10 @@
-e::
--expr::
- List of syscalls to show, currently only syscall names.
+--event::
+ List of syscalls and other perf events (tracepoints, HW cache events,
+ etc) to show.
+ See 'perf list' for a complete list of events.
Prefixing with ! shows all syscalls but the ones specified. You may
need to escape it.
@@ -135,9 +138,6 @@
--kernel-syscall-graph::
Show the kernel callchains on the syscall exit path.
---event::
- Trace other events, see 'perf list' for a complete list.
-
--max-stack::
Set the stack depth limit when parsing the callchain, anything
beyond the specified depth will be ignored. Note that at this point
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST
index a511e5f..8672f83 100644
--- a/tools/perf/MANIFEST
+++ b/tools/perf/MANIFEST
@@ -61,6 +61,7 @@
tools/include/linux/atomic.h
tools/include/linux/bitops.h
tools/include/linux/compiler.h
+tools/include/linux/compiler-gcc.h
tools/include/linux/coresight-pmu.h
tools/include/linux/filter.h
tools/include/linux/hash.h
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index 76c84f0..2b941ef 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -144,8 +144,12 @@
endif
ifeq ($(DEBUG),0)
+ifeq ($(CC), clang)
+ CFLAGS += -O3
+else
CFLAGS += -O6
endif
+endif
ifdef PARSER_DEBUG
PARSER_DEBUG_BISON := -t
@@ -291,8 +295,10 @@
endif
endif
ifneq ($(feature-dwarf), 1)
- msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev);
- NO_DWARF := 1
+ ifndef NO_DWARF
+ msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev);
+ NO_DWARF := 1
+ endif
else
ifneq ($(feature-dwarf_getlocations), 1)
msg := $(warning Old libdw.h, finding variables at given 'perf probe' point will not work, install elfutils-devel/libdw-dev >= 0.157);
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 8bb16aa..4da19b6 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -661,6 +661,7 @@
endif
ifndef NO_JVMTI
$(call QUIET_INSTALL, $(LIBJVMTI)) \
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(libdir_SQ)'; \
$(INSTALL) $(OUTPUT)$(LIBJVMTI) '$(DESTDIR_SQ)$(libdir_SQ)';
endif
$(call QUIET_INSTALL, libexec) \
diff --git a/tools/perf/arch/arm64/Makefile b/tools/perf/arch/arm64/Makefile
index 18b1351..eebe1ec 100644
--- a/tools/perf/arch/arm64/Makefile
+++ b/tools/perf/arch/arm64/Makefile
@@ -2,3 +2,4 @@
PERF_HAVE_DWARF_REGS := 1
endif
PERF_HAVE_JITDUMP := 1
+PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
diff --git a/tools/perf/arch/arm64/include/dwarf-regs-table.h b/tools/perf/arch/arm64/include/dwarf-regs-table.h
index 2675936..36e375f 100644
--- a/tools/perf/arch/arm64/include/dwarf-regs-table.h
+++ b/tools/perf/arch/arm64/include/dwarf-regs-table.h
@@ -2,12 +2,12 @@
/* This is included in perf/util/dwarf-regs.c */
static const char * const aarch64_regstr_tbl[] = {
- "%r0", "%r1", "%r2", "%r3", "%r4",
- "%r5", "%r6", "%r7", "%r8", "%r9",
- "%r10", "%r11", "%r12", "%r13", "%r14",
- "%r15", "%r16", "%r17", "%r18", "%r19",
- "%r20", "%r21", "%r22", "%r23", "%r24",
- "%r25", "%r26", "%r27", "%r28", "%r29",
+ "%x0", "%x1", "%x2", "%x3", "%x4",
+ "%x5", "%x6", "%x7", "%x8", "%x9",
+ "%x10", "%x11", "%x12", "%x13", "%x14",
+ "%x15", "%x16", "%x17", "%x18", "%x19",
+ "%x20", "%x21", "%x22", "%x23", "%x24",
+ "%x25", "%x26", "%x27", "%x28", "%x29",
"%lr", "%sp",
};
#endif
diff --git a/tools/perf/arch/arm64/util/dwarf-regs.c b/tools/perf/arch/arm64/util/dwarf-regs.c
index d49efeb..068b618 100644
--- a/tools/perf/arch/arm64/util/dwarf-regs.c
+++ b/tools/perf/arch/arm64/util/dwarf-regs.c
@@ -10,17 +10,20 @@
#include <stddef.h>
#include <dwarf-regs.h>
+#include <linux/ptrace.h> /* for struct user_pt_regs */
+#include "util.h"
struct pt_regs_dwarfnum {
const char *name;
unsigned int dwarfnum;
};
-#define STR(s) #s
#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num}
#define GPR_DWARFNUM_NAME(num) \
{.name = STR(%x##num), .dwarfnum = num}
#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0}
+#define DWARFNUM2OFFSET(index) \
+ (index * sizeof((struct user_pt_regs *)0)->regs[0])
/*
* Reference:
@@ -78,3 +81,13 @@
return roff->name;
return NULL;
}
+
+int regs_query_register_offset(const char *name)
+{
+ const struct pt_regs_dwarfnum *roff;
+
+ for (roff = regdwarfnum_table; roff->name != NULL; roff++)
+ if (!strcmp(roff->name, name))
+ return DWARFNUM2OFFSET(roff->dwarfnum);
+ return -EINVAL;
+}
diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c
index bfbb6b5..da04b8c 100644
--- a/tools/perf/bench/futex-hash.c
+++ b/tools/perf/bench/futex-hash.c
@@ -130,8 +130,6 @@
}
ncpus = sysconf(_SC_NPROCESSORS_ONLN);
- nsecs = futexbench_sanitize_numeric(nsecs);
- nfutexes = futexbench_sanitize_numeric(nfutexes);
sigfillset(&act.sa_mask);
act.sa_sigaction = toggle_done;
@@ -139,8 +137,6 @@
if (!nthreads) /* default to the number of CPUs */
nthreads = ncpus;
- else
- nthreads = futexbench_sanitize_numeric(nthreads);
worker = calloc(nthreads, sizeof(*worker));
if (!worker)
diff --git a/tools/perf/bench/futex-lock-pi.c b/tools/perf/bench/futex-lock-pi.c
index 6d9d6c4..9187777 100644
--- a/tools/perf/bench/futex-lock-pi.c
+++ b/tools/perf/bench/futex-lock-pi.c
@@ -152,7 +152,6 @@
goto err;
ncpus = sysconf(_SC_NPROCESSORS_ONLN);
- nsecs = futexbench_sanitize_numeric(nsecs);
sigfillset(&act.sa_mask);
act.sa_sigaction = toggle_done;
@@ -160,8 +159,6 @@
if (!nthreads)
nthreads = ncpus;
- else
- nthreads = futexbench_sanitize_numeric(nthreads);
worker = calloc(nthreads, sizeof(*worker));
if (!worker)
diff --git a/tools/perf/bench/futex-requeue.c b/tools/perf/bench/futex-requeue.c
index fd4ee95..2b9705a 100644
--- a/tools/perf/bench/futex-requeue.c
+++ b/tools/perf/bench/futex-requeue.c
@@ -128,8 +128,6 @@
if (!nthreads)
nthreads = ncpus;
- else
- nthreads = futexbench_sanitize_numeric(nthreads);
worker = calloc(nthreads, sizeof(*worker));
if (!worker)
diff --git a/tools/perf/bench/futex-wake-parallel.c b/tools/perf/bench/futex-wake-parallel.c
index beaa6c1..2c8fa67 100644
--- a/tools/perf/bench/futex-wake-parallel.c
+++ b/tools/perf/bench/futex-wake-parallel.c
@@ -217,12 +217,8 @@
sigaction(SIGINT, &act, NULL);
ncpus = sysconf(_SC_NPROCESSORS_ONLN);
- nwaking_threads = futexbench_sanitize_numeric(nwaking_threads);
-
if (!nblocked_threads)
nblocked_threads = ncpus;
- else
- nblocked_threads = futexbench_sanitize_numeric(nblocked_threads);
/* some sanity checks */
if (nwaking_threads > nblocked_threads || !nwaking_threads)
diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c
index 46efcb9..e246b1b 100644
--- a/tools/perf/bench/futex-wake.c
+++ b/tools/perf/bench/futex-wake.c
@@ -129,7 +129,6 @@
}
ncpus = sysconf(_SC_NPROCESSORS_ONLN);
- nwakes = futexbench_sanitize_numeric(nwakes);
sigfillset(&act.sa_mask);
act.sa_sigaction = toggle_done;
@@ -137,8 +136,6 @@
if (!nthreads)
nthreads = ncpus;
- else
- nthreads = futexbench_sanitize_numeric(nthreads);
worker = calloc(nthreads, sizeof(*worker));
if (!worker)
diff --git a/tools/perf/bench/futex.h b/tools/perf/bench/futex.h
index ba7c735..b2e06d1 100644
--- a/tools/perf/bench/futex.h
+++ b/tools/perf/bench/futex.h
@@ -7,7 +7,6 @@
#ifndef _FUTEX_H
#define _FUTEX_H
-#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
@@ -100,7 +99,4 @@
}
#endif
-/* User input sanitation */
-#define futexbench_sanitize_numeric(__n) abs((__n))
-
#endif /* _FUTEX_H */
diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c
index 8efe904..3083fc3 100644
--- a/tools/perf/bench/numa.c
+++ b/tools/perf/bench/numa.c
@@ -43,6 +43,7 @@
/*
* Debug printf:
*/
+#undef dprintf
#define dprintf(x...) do { if (g && g->p.show_details >= 1) printf(x); } while (0)
struct thread_data {
@@ -1573,13 +1574,13 @@
"GB/sec,", "total-speed", "GB/sec total speed");
if (g->p.show_details >= 2) {
- char tname[32];
+ char tname[14 + 2 * 10 + 1];
struct thread_data *td;
for (p = 0; p < g->p.nr_proc; p++) {
for (t = 0; t < g->p.nr_threads; t++) {
- memset(tname, 0, 32);
+ memset(tname, 0, sizeof(tname));
td = g->threads + p*g->p.nr_threads + t;
- snprintf(tname, 32, "process%d:thread%d", p, t);
+ snprintf(tname, sizeof(tname), "process%d:thread%d", p, t);
print_res(tname, td->speed_gbs,
"GB/sec", "thread-speed", "GB/sec/thread speed");
print_res(tname, td->system_time_ns / NSEC_PER_SEC,
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index f8ca7a4..e2b2172 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -58,7 +58,7 @@
struct hist_entry he;
};
-static char const *coalesce_default = "pid,tid,iaddr";
+static char const *coalesce_default = "pid,iaddr";
struct perf_c2c {
struct perf_tool tool;
@@ -2476,6 +2476,7 @@
"mean_rmt,"
"mean_lcl,"
"mean_load,"
+ "tot_recs,"
"cpucnt,",
add_sym ? "symbol," : "",
add_dso ? "dso," : "",
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 933aeec..70a2893 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -17,6 +17,7 @@
#include "util/symbol.h"
#include "util/util.h"
#include "util/data.h"
+#include "util/config.h"
#include <stdlib.h>
#include <math.h>
@@ -30,6 +31,7 @@
PERF_HPP_DIFF__RATIO,
PERF_HPP_DIFF__WEIGHTED_DIFF,
PERF_HPP_DIFF__FORMULA,
+ PERF_HPP_DIFF__DELTA_ABS,
PERF_HPP_DIFF__MAX_INDEX
};
@@ -64,7 +66,7 @@
static bool show_period;
static bool show_formula;
static bool show_baseline_only;
-static unsigned int sort_compute;
+static unsigned int sort_compute = 1;
static s64 compute_wdiff_w1;
static s64 compute_wdiff_w2;
@@ -73,19 +75,22 @@
COMPUTE_DELTA,
COMPUTE_RATIO,
COMPUTE_WEIGHTED_DIFF,
+ COMPUTE_DELTA_ABS,
COMPUTE_MAX,
};
const char *compute_names[COMPUTE_MAX] = {
[COMPUTE_DELTA] = "delta",
+ [COMPUTE_DELTA_ABS] = "delta-abs",
[COMPUTE_RATIO] = "ratio",
[COMPUTE_WEIGHTED_DIFF] = "wdiff",
};
-static int compute;
+static int compute = COMPUTE_DELTA_ABS;
static int compute_2_hpp[COMPUTE_MAX] = {
[COMPUTE_DELTA] = PERF_HPP_DIFF__DELTA,
+ [COMPUTE_DELTA_ABS] = PERF_HPP_DIFF__DELTA_ABS,
[COMPUTE_RATIO] = PERF_HPP_DIFF__RATIO,
[COMPUTE_WEIGHTED_DIFF] = PERF_HPP_DIFF__WEIGHTED_DIFF,
};
@@ -111,6 +116,10 @@
.name = "Delta",
.width = 7,
},
+ [PERF_HPP_DIFF__DELTA_ABS] = {
+ .name = "Delta Abs",
+ .width = 7,
+ },
[PERF_HPP_DIFF__RATIO] = {
.name = "Ratio",
.width = 14,
@@ -298,6 +307,7 @@
{
switch (compute) {
case COMPUTE_DELTA:
+ case COMPUTE_DELTA_ABS:
return formula_delta(he, pair, buf, size);
case COMPUTE_RATIO:
return formula_ratio(he, pair, buf, size);
@@ -461,6 +471,7 @@
switch (compute) {
case COMPUTE_DELTA:
+ case COMPUTE_DELTA_ABS:
compute_delta(he, pair);
break;
case COMPUTE_RATIO:
@@ -498,6 +509,13 @@
return cmp_doubles(l, r);
}
+ case COMPUTE_DELTA_ABS:
+ {
+ double l = fabs(left->diff.period_ratio_delta);
+ double r = fabs(right->diff.period_ratio_delta);
+
+ return cmp_doubles(l, r);
+ }
case COMPUTE_RATIO:
{
double l = left->diff.period_ratio;
@@ -564,7 +582,7 @@
if (!p_left || !p_right)
return p_left ? -1 : 1;
- if (c != COMPUTE_DELTA) {
+ if (c != COMPUTE_DELTA && c != COMPUTE_DELTA_ABS) {
/*
* The delta can be computed without the baseline, but
* others are not. Put those entries which have no
@@ -607,6 +625,15 @@
}
static int64_t
+hist_entry__cmp_delta_abs(struct perf_hpp_fmt *fmt,
+ struct hist_entry *left, struct hist_entry *right)
+{
+ struct data__file *d = fmt_to_data_file(fmt);
+
+ return hist_entry__cmp_compute(right, left, COMPUTE_DELTA_ABS, d->idx);
+}
+
+static int64_t
hist_entry__cmp_ratio(struct perf_hpp_fmt *fmt,
struct hist_entry *left, struct hist_entry *right)
{
@@ -633,6 +660,14 @@
}
static int64_t
+hist_entry__cmp_delta_abs_idx(struct perf_hpp_fmt *fmt __maybe_unused,
+ struct hist_entry *left, struct hist_entry *right)
+{
+ return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA_ABS,
+ sort_compute);
+}
+
+static int64_t
hist_entry__cmp_ratio_idx(struct perf_hpp_fmt *fmt __maybe_unused,
struct hist_entry *left, struct hist_entry *right)
{
@@ -775,7 +810,7 @@
OPT_BOOLEAN('b', "baseline-only", &show_baseline_only,
"Show only items with match in baseline"),
OPT_CALLBACK('c', "compute", &compute,
- "delta,ratio,wdiff:w1,w2 (default delta)",
+ "delta,delta-abs,ratio,wdiff:w1,w2 (default delta-abs)",
"Entries differential computation selection",
setup_compute),
OPT_BOOLEAN('p', "period", &show_period,
@@ -945,6 +980,7 @@
switch (idx) {
case PERF_HPP_DIFF__DELTA:
+ case PERF_HPP_DIFF__DELTA_ABS:
if (pair->diff.computed)
diff = pair->diff.period_ratio_delta;
else
@@ -1118,6 +1154,10 @@
fmt->color = hpp__color_wdiff;
fmt->sort = hist_entry__cmp_wdiff;
break;
+ case PERF_HPP_DIFF__DELTA_ABS:
+ fmt->color = hpp__color_delta;
+ fmt->sort = hist_entry__cmp_delta_abs;
+ break;
default:
fmt->sort = hist_entry__cmp_nop;
break;
@@ -1195,6 +1235,9 @@
case COMPUTE_WEIGHTED_DIFF:
fmt->sort = hist_entry__cmp_wdiff_idx;
break;
+ case COMPUTE_DELTA_ABS:
+ fmt->sort = hist_entry__cmp_delta_abs_idx;
+ break;
default:
BUG_ON(1);
}
@@ -1249,6 +1292,31 @@
return 0;
}
+static int diff__config(const char *var, const char *value,
+ void *cb __maybe_unused)
+{
+ if (!strcmp(var, "diff.order")) {
+ sort_compute = perf_config_int(var, value);
+ return 0;
+ }
+ if (!strcmp(var, "diff.compute")) {
+ if (!strcmp(value, "delta")) {
+ compute = COMPUTE_DELTA;
+ } else if (!strcmp(value, "delta-abs")) {
+ compute = COMPUTE_DELTA_ABS;
+ } else if (!strcmp(value, "ratio")) {
+ compute = COMPUTE_RATIO;
+ } else if (!strcmp(value, "wdiff")) {
+ compute = COMPUTE_WEIGHTED_DIFF;
+ } else {
+ pr_err("Invalid compute method: %s\n", value);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
{
int ret = hists__init();
@@ -1256,6 +1324,8 @@
if (ret < 0)
return ret;
+ perf_config(diff__config, NULL);
+
argc = parse_options(argc, argv, options, diff_usage, 0);
if (symbol__init(NULL) < 0)
diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c
new file mode 100644
index 0000000..c3e6436
--- /dev/null
+++ b/tools/perf/builtin-ftrace.c
@@ -0,0 +1,265 @@
+/*
+ * builtin-ftrace.c
+ *
+ * Copyright (c) 2013 LG Electronics, Namhyung Kim <namhyung@kernel.org>
+ *
+ * Released under the GPL v2.
+ */
+
+#include "builtin.h"
+#include "perf.h"
+
+#include <unistd.h>
+#include <signal.h>
+
+#include "debug.h"
+#include <subcmd/parse-options.h>
+#include "evlist.h"
+#include "target.h"
+#include "thread_map.h"
+#include "util/config.h"
+
+
+#define DEFAULT_TRACER "function_graph"
+
+struct perf_ftrace {
+ struct perf_evlist *evlist;
+ struct target target;
+ const char *tracer;
+};
+
+static bool done;
+
+static void sig_handler(int sig __maybe_unused)
+{
+ done = true;
+}
+
+/*
+ * perf_evlist__prepare_workload will send a SIGUSR1 if the fork fails, since
+ * we asked by setting its exec_error to the function below,
+ * ftrace__workload_exec_failed_signal.
+ *
+ * XXX We need to handle this more appropriately, emitting an error, etc.
+ */
+static void ftrace__workload_exec_failed_signal(int signo __maybe_unused,
+ siginfo_t *info __maybe_unused,
+ void *ucontext __maybe_unused)
+{
+ /* workload_exec_errno = info->si_value.sival_int; */
+ done = true;
+}
+
+static int write_tracing_file(const char *name, const char *val)
+{
+ char *file;
+ int fd, ret = -1;
+ ssize_t size = strlen(val);
+
+ file = get_tracing_file(name);
+ if (!file) {
+ pr_debug("cannot get tracing file: %s\n", name);
+ return -1;
+ }
+
+ fd = open(file, O_WRONLY);
+ if (fd < 0) {
+ pr_debug("cannot open tracing file: %s\n", name);
+ goto out;
+ }
+
+ if (write(fd, val, size) == size)
+ ret = 0;
+ else
+ pr_debug("write '%s' to tracing/%s failed\n", val, name);
+
+ close(fd);
+out:
+ put_tracing_file(file);
+ return ret;
+}
+
+static int reset_tracing_files(struct perf_ftrace *ftrace __maybe_unused)
+{
+ if (write_tracing_file("tracing_on", "0") < 0)
+ return -1;
+
+ if (write_tracing_file("current_tracer", "nop") < 0)
+ return -1;
+
+ if (write_tracing_file("set_ftrace_pid", " ") < 0)
+ return -1;
+
+ return 0;
+}
+
+static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv)
+{
+ char *trace_file;
+ int trace_fd;
+ char *trace_pid;
+ char buf[4096];
+ struct pollfd pollfd = {
+ .events = POLLIN,
+ };
+
+ if (geteuid() != 0) {
+ pr_err("ftrace only works for root!\n");
+ return -1;
+ }
+
+ if (argc < 1)
+ return -1;
+
+ signal(SIGINT, sig_handler);
+ signal(SIGUSR1, sig_handler);
+ signal(SIGCHLD, sig_handler);
+
+ reset_tracing_files(ftrace);
+
+ /* reset ftrace buffer */
+ if (write_tracing_file("trace", "0") < 0)
+ goto out;
+
+ if (perf_evlist__prepare_workload(ftrace->evlist, &ftrace->target,
+ argv, false, ftrace__workload_exec_failed_signal) < 0)
+ goto out;
+
+ if (write_tracing_file("current_tracer", ftrace->tracer) < 0) {
+ pr_err("failed to set current_tracer to %s\n", ftrace->tracer);
+ goto out;
+ }
+
+ if (asprintf(&trace_pid, "%d", thread_map__pid(ftrace->evlist->threads, 0)) < 0) {
+ pr_err("failed to allocate pid string\n");
+ goto out;
+ }
+
+ if (write_tracing_file("set_ftrace_pid", trace_pid) < 0) {
+ pr_err("failed to set pid: %s\n", trace_pid);
+ goto out_free_pid;
+ }
+
+ trace_file = get_tracing_file("trace_pipe");
+ if (!trace_file) {
+ pr_err("failed to open trace_pipe\n");
+ goto out_free_pid;
+ }
+
+ trace_fd = open(trace_file, O_RDONLY);
+
+ put_tracing_file(trace_file);
+
+ if (trace_fd < 0) {
+ pr_err("failed to open trace_pipe\n");
+ goto out_free_pid;
+ }
+
+ fcntl(trace_fd, F_SETFL, O_NONBLOCK);
+ pollfd.fd = trace_fd;
+
+ if (write_tracing_file("tracing_on", "1") < 0) {
+ pr_err("can't enable tracing\n");
+ goto out_close_fd;
+ }
+
+ perf_evlist__start_workload(ftrace->evlist);
+
+ while (!done) {
+ if (poll(&pollfd, 1, -1) < 0)
+ break;
+
+ if (pollfd.revents & POLLIN) {
+ int n = read(trace_fd, buf, sizeof(buf));
+ if (n < 0)
+ break;
+ if (fwrite(buf, n, 1, stdout) != 1)
+ break;
+ }
+ }
+
+ write_tracing_file("tracing_on", "0");
+
+ /* read remaining buffer contents */
+ while (true) {
+ int n = read(trace_fd, buf, sizeof(buf));
+ if (n <= 0)
+ break;
+ if (fwrite(buf, n, 1, stdout) != 1)
+ break;
+ }
+
+out_close_fd:
+ close(trace_fd);
+out_free_pid:
+ free(trace_pid);
+out:
+ reset_tracing_files(ftrace);
+
+ return done ? 0 : -1;
+}
+
+static int perf_ftrace_config(const char *var, const char *value, void *cb)
+{
+ struct perf_ftrace *ftrace = cb;
+
+ if (prefixcmp(var, "ftrace."))
+ return 0;
+
+ if (strcmp(var, "ftrace.tracer"))
+ return -1;
+
+ if (!strcmp(value, "function_graph") ||
+ !strcmp(value, "function")) {
+ ftrace->tracer = value;
+ return 0;
+ }
+
+ pr_err("Please select \"function_graph\" (default) or \"function\"\n");
+ return -1;
+}
+
+int cmd_ftrace(int argc, const char **argv, const char *prefix __maybe_unused)
+{
+ int ret;
+ struct perf_ftrace ftrace = {
+ .tracer = DEFAULT_TRACER,
+ .target = { .uid = UINT_MAX, },
+ };
+ const char * const ftrace_usage[] = {
+ "perf ftrace [<options>] <command>",
+ "perf ftrace [<options>] -- <command> [<options>]",
+ NULL
+ };
+ const struct option ftrace_options[] = {
+ OPT_STRING('t', "tracer", &ftrace.tracer, "tracer",
+ "tracer to use: function_graph(default) or function"),
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose"),
+ OPT_END()
+ };
+
+ ret = perf_config(perf_ftrace_config, &ftrace);
+ if (ret < 0)
+ return -1;
+
+ argc = parse_options(argc, argv, ftrace_options, ftrace_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ if (!argc)
+ usage_with_options(ftrace_usage, ftrace_options);
+
+ ftrace.evlist = perf_evlist__new();
+ if (ftrace.evlist == NULL)
+ return -ENOMEM;
+
+ ret = perf_evlist__create_maps(ftrace.evlist, &ftrace.target);
+ if (ret < 0)
+ goto out_delete_evlist;
+
+ ret = __cmd_ftrace(&ftrace, argc, argv);
+
+out_delete_evlist:
+ perf_evlist__delete(ftrace.evlist);
+
+ return ret;
+}
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c
index 3bdb2c7..aed0d84 100644
--- a/tools/perf/builtin-help.c
+++ b/tools/perf/builtin-help.c
@@ -434,7 +434,7 @@
const char * const builtin_help_subcommands[] = {
"buildid-cache", "buildid-list", "diff", "evlist", "help", "list",
"record", "report", "bench", "stat", "timechart", "top", "annotate",
- "script", "sched", "kmem", "lock", "kvm", "test", "inject", "mem", "data",
+ "script", "sched", "kallsyms", "kmem", "lock", "kvm", "test", "inject", "mem", "data",
#ifdef HAVE_LIBELF_SUPPORT
"probe",
#endif
@@ -447,11 +447,13 @@
NULL
};
const char *alias;
- int rc = 0;
+ int rc;
load_command_list("perf-", &main_cmds, &other_cmds);
- perf_config(perf_help_config, &help_format);
+ rc = perf_config(perf_help_config, &help_format);
+ if (rc)
+ return rc;
argc = parse_options_subcommand(argc, argv, builtin_help_options,
builtin_help_subcommands, builtin_help_usage, 0);
diff --git a/tools/perf/builtin-kallsyms.c b/tools/perf/builtin-kallsyms.c
new file mode 100644
index 0000000..224bfc4
--- /dev/null
+++ b/tools/perf/builtin-kallsyms.c
@@ -0,0 +1,67 @@
+/*
+ * builtin-kallsyms.c
+ *
+ * Builtin command: Look for a symbol in the running kernel and its modules
+ *
+ * Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+ *
+ * Released under the GPL v2. (and only v2, not any later version)
+ */
+#include "builtin.h"
+#include <linux/compiler.h>
+#include <subcmd/parse-options.h>
+#include "debug.h"
+#include "machine.h"
+#include "symbol.h"
+
+static int __cmd_kallsyms(int argc, const char **argv)
+{
+ int i;
+ struct machine *machine = machine__new_kallsyms();
+
+ if (machine == NULL) {
+ pr_err("Couldn't read /proc/kallsyms\n");
+ return -1;
+ }
+
+ for (i = 0; i < argc; ++i) {
+ struct map *map;
+ struct symbol *symbol = machine__find_kernel_function_by_name(machine, argv[i], &map);
+
+ if (symbol == NULL) {
+ printf("%s: not found\n", argv[i]);
+ continue;
+ }
+
+ printf("%s: %s %s %#" PRIx64 "-%#" PRIx64 " (%#" PRIx64 "-%#" PRIx64")\n",
+ symbol->name, map->dso->short_name, map->dso->long_name,
+ map->unmap_ip(map, symbol->start), map->unmap_ip(map, symbol->end),
+ symbol->start, symbol->end);
+ }
+
+ machine__delete(machine);
+ return 0;
+}
+
+int cmd_kallsyms(int argc, const char **argv, const char *prefix __maybe_unused)
+{
+ const struct option options[] = {
+ OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"),
+ OPT_END()
+ };
+ const char * const kallsyms_usage[] = {
+ "perf kallsyms [<options>] symbol_name",
+ NULL
+ };
+
+ argc = parse_options(argc, argv, options, kallsyms_usage, 0);
+ if (argc < 1)
+ usage_with_options(kallsyms_usage, options);
+
+ symbol_conf.sort_by_name = true;
+ symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
+ if (symbol__init(NULL) < 0)
+ return -1;
+
+ return __cmd_kallsyms(argc, argv);
+}
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 915869e..6da8d08 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -1065,7 +1065,7 @@
data = rb_entry(next, struct page_stat, node);
sym = machine__find_kernel_function(machine, data->callsite, &map);
- if (sym && sym->name)
+ if (sym)
caller = sym->name;
else
scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
@@ -1107,7 +1107,7 @@
data = rb_entry(next, struct page_stat, node);
sym = machine__find_kernel_function(machine, data->callsite, &map);
- if (sym && sym->name)
+ if (sym)
caller = sym->name;
else
scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
@@ -1920,10 +1920,12 @@
NULL
};
struct perf_session *session;
- int ret = -1;
const char errmsg[] = "No %s allocation events found. Have you run 'perf kmem record --%s'?\n";
+ int ret = perf_config(kmem_config, NULL);
- perf_config(kmem_config, NULL);
+ if (ret)
+ return ret;
+
argc = parse_options_subcommand(argc, argv, kmem_options,
kmem_subcommands, kmem_usage, 0);
@@ -1948,6 +1950,8 @@
if (session == NULL)
return -1;
+ ret = -1;
+
if (kmem_slab) {
if (!perf_evlist__find_tracepoint_by_name(session->evlist,
"kmem:kmalloc")) {
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index ba9322f..3b9d98b 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -14,6 +14,7 @@
#include "util/parse-events.h"
#include "util/cache.h"
#include "util/pmu.h"
+#include "util/debug.h"
#include <subcmd/parse-options.h>
static bool desc_flag = true;
@@ -29,6 +30,8 @@
"Print extra event descriptions. --no-desc to not print."),
OPT_BOOLEAN('v', "long-desc", &long_desc_flag,
"Print longer event descriptions."),
+ OPT_INCR(0, "debug", &verbose,
+ "Enable debugging output"),
OPT_END()
};
const char * const list_usage[] = {
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index f87996b..1fcebc3 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -552,6 +552,8 @@
OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
"Enable kernel symbol demangling"),
OPT_BOOLEAN(0, "cache", &probe_conf.cache, "Manipulate probe cache"),
+ OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+ "Look for files with symbols relative to this directory"),
OPT_END()
};
int ret;
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 4ec10e9..6cd6776 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -46,6 +46,15 @@
#include <asm/bug.h>
#include <linux/time64.h>
+struct switch_output {
+ bool enabled;
+ bool signal;
+ unsigned long size;
+ unsigned long time;
+ const char *str;
+ bool set;
+};
+
struct record {
struct perf_tool tool;
struct record_opts opts;
@@ -62,10 +71,33 @@
bool no_buildid_cache_set;
bool buildid_all;
bool timestamp_filename;
- bool switch_output;
+ struct switch_output switch_output;
unsigned long long samples;
};
+static volatile int auxtrace_record__snapshot_started;
+static DEFINE_TRIGGER(auxtrace_snapshot_trigger);
+static DEFINE_TRIGGER(switch_output_trigger);
+
+static bool switch_output_signal(struct record *rec)
+{
+ return rec->switch_output.signal &&
+ trigger_is_ready(&switch_output_trigger);
+}
+
+static bool switch_output_size(struct record *rec)
+{
+ return rec->switch_output.size &&
+ trigger_is_ready(&switch_output_trigger) &&
+ (rec->bytes_written >= rec->switch_output.size);
+}
+
+static bool switch_output_time(struct record *rec)
+{
+ return rec->switch_output.time &&
+ trigger_is_ready(&switch_output_trigger);
+}
+
static int record__write(struct record *rec, void *bf, size_t size)
{
if (perf_data_file__write(rec->session->file, bf, size) < 0) {
@@ -74,6 +106,10 @@
}
rec->bytes_written += size;
+
+ if (switch_output_size(rec))
+ trigger_hit(&switch_output_trigger);
+
return 0;
}
@@ -193,10 +229,6 @@
static volatile int signr = -1;
static volatile int child_finished;
-static volatile int auxtrace_record__snapshot_started;
-static DEFINE_TRIGGER(auxtrace_snapshot_trigger);
-static DEFINE_TRIGGER(switch_output_trigger);
-
static void sig_handler(int sig)
{
if (sig == SIGCHLD)
@@ -386,7 +418,7 @@
static int record__open(struct record *rec)
{
- char msg[512];
+ char msg[BUFSIZ];
struct perf_evsel *pos;
struct perf_evlist *evlist = rec->evlist;
struct perf_session *session = rec->session;
@@ -623,22 +655,23 @@
static int record__synthesize_workload(struct record *rec, bool tail)
{
- struct {
- struct thread_map map;
- struct thread_map_data map_data;
- } thread_map;
+ int err;
+ struct thread_map *thread_map;
if (rec->opts.tail_synthesize != tail)
return 0;
- thread_map.map.nr = 1;
- thread_map.map.map[0].pid = rec->evlist->workload.pid;
- thread_map.map.map[0].comm = NULL;
- return perf_event__synthesize_thread_map(&rec->tool, &thread_map.map,
+ thread_map = thread_map__new_by_tid(rec->evlist->workload.pid);
+ if (thread_map == NULL)
+ return -1;
+
+ err = perf_event__synthesize_thread_map(&rec->tool, thread_map,
process_synthesized_event,
&rec->session->machines.host,
rec->opts.sample_address,
rec->opts.proc_map_timeout);
+ thread_map__put(thread_map);
+ return err;
}
static int record__synthesize(struct record *rec, bool tail);
@@ -712,6 +745,7 @@
}
static void snapshot_sig_handler(int sig);
+static void alarm_sig_handler(int sig);
int __weak
perf_event__synth_time_conv(const struct perf_event_mmap_page *pc __maybe_unused,
@@ -842,11 +876,11 @@
signal(SIGTERM, sig_handler);
signal(SIGSEGV, sigsegv_handler);
- if (rec->opts.auxtrace_snapshot_mode || rec->switch_output) {
+ if (rec->opts.auxtrace_snapshot_mode || rec->switch_output.enabled) {
signal(SIGUSR2, snapshot_sig_handler);
if (rec->opts.auxtrace_snapshot_mode)
trigger_on(&auxtrace_snapshot_trigger);
- if (rec->switch_output)
+ if (rec->switch_output.enabled)
trigger_on(&switch_output_trigger);
} else {
signal(SIGUSR2, SIG_IGN);
@@ -1043,6 +1077,10 @@
err = fd;
goto out_child;
}
+
+ /* re-arm the alarm */
+ if (rec->switch_output.time)
+ alarm(rec->switch_output.time);
}
if (hits == rec->samples) {
@@ -1352,6 +1390,78 @@
return ret;
}
+static void switch_output_size_warn(struct record *rec)
+{
+ u64 wakeup_size = perf_evlist__mmap_size(rec->opts.mmap_pages);
+ struct switch_output *s = &rec->switch_output;
+
+ wakeup_size /= 2;
+
+ if (s->size < wakeup_size) {
+ char buf[100];
+
+ unit_number__scnprintf(buf, sizeof(buf), wakeup_size);
+ pr_warning("WARNING: switch-output data size lower than "
+ "wakeup kernel buffer size (%s) "
+ "expect bigger perf.data sizes\n", buf);
+ }
+}
+
+static int switch_output_setup(struct record *rec)
+{
+ struct switch_output *s = &rec->switch_output;
+ static struct parse_tag tags_size[] = {
+ { .tag = 'B', .mult = 1 },
+ { .tag = 'K', .mult = 1 << 10 },
+ { .tag = 'M', .mult = 1 << 20 },
+ { .tag = 'G', .mult = 1 << 30 },
+ { .tag = 0 },
+ };
+ static struct parse_tag tags_time[] = {
+ { .tag = 's', .mult = 1 },
+ { .tag = 'm', .mult = 60 },
+ { .tag = 'h', .mult = 60*60 },
+ { .tag = 'd', .mult = 60*60*24 },
+ { .tag = 0 },
+ };
+ unsigned long val;
+
+ if (!s->set)
+ return 0;
+
+ if (!strcmp(s->str, "signal")) {
+ s->signal = true;
+ pr_debug("switch-output with SIGUSR2 signal\n");
+ goto enabled;
+ }
+
+ val = parse_tag_value(s->str, tags_size);
+ if (val != (unsigned long) -1) {
+ s->size = val;
+ pr_debug("switch-output with %s size threshold\n", s->str);
+ goto enabled;
+ }
+
+ val = parse_tag_value(s->str, tags_time);
+ if (val != (unsigned long) -1) {
+ s->time = val;
+ pr_debug("switch-output with %s time threshold (%lu seconds)\n",
+ s->str, s->time);
+ goto enabled;
+ }
+
+ return -1;
+
+enabled:
+ rec->timestamp_filename = true;
+ s->enabled = true;
+
+ if (s->size && !rec->opts.no_buffering)
+ switch_output_size_warn(rec);
+
+ return 0;
+}
+
static const char * const __record_usage[] = {
"perf record [<options>] [<command>]",
"perf record [<options>] -- <command> [<options>]",
@@ -1519,8 +1629,10 @@
"Record build-id of all DSOs regardless of hits"),
OPT_BOOLEAN(0, "timestamp-filename", &record.timestamp_filename,
"append timestamp to output filename"),
- OPT_BOOLEAN(0, "switch-output", &record.switch_output,
- "Switch output when receive SIGUSR2"),
+ OPT_STRING_OPTARG_SET(0, "switch-output", &record.switch_output.str,
+ &record.switch_output.set, "signal,size,time",
+ "Switch output when receive SIGUSR2 or cross size,time threshold",
+ "signal"),
OPT_BOOLEAN(0, "dry-run", &dry_run,
"Parse options then exit"),
OPT_END()
@@ -1559,7 +1671,9 @@
if (rec->evlist == NULL)
return -ENOMEM;
- perf_config(perf_record_config, rec);
+ err = perf_config(perf_record_config, rec);
+ if (err)
+ return err;
argc = parse_options(argc, argv, record_options, record_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
@@ -1578,8 +1692,15 @@
return -EINVAL;
}
- if (rec->switch_output)
- rec->timestamp_filename = true;
+ if (switch_output_setup(rec)) {
+ parse_options_usage(record_usage, record_options, "switch-output", 0);
+ return -EINVAL;
+ }
+
+ if (rec->switch_output.time) {
+ signal(SIGALRM, alarm_sig_handler);
+ alarm(rec->switch_output.time);
+ }
if (!rec->itr) {
rec->itr = auxtrace_record__init(rec->evlist, &err);
@@ -1629,7 +1750,7 @@
if (rec->no_buildid_cache || rec->no_buildid) {
disable_buildid_cache();
- } else if (rec->switch_output) {
+ } else if (rec->switch_output.enabled) {
/*
* In 'perf record --switch-output', disable buildid
* generation by default to reduce data file switching
@@ -1721,6 +1842,8 @@
static void snapshot_sig_handler(int sig __maybe_unused)
{
+ struct record *rec = &record;
+
if (trigger_is_ready(&auxtrace_snapshot_trigger)) {
trigger_hit(&auxtrace_snapshot_trigger);
auxtrace_record__snapshot_started = 1;
@@ -1728,6 +1851,14 @@
trigger_error(&auxtrace_snapshot_trigger);
}
- if (trigger_is_ready(&switch_output_trigger))
+ if (switch_output_signal(rec))
+ trigger_hit(&switch_output_trigger);
+}
+
+static void alarm_sig_handler(int sig __maybe_unused)
+{
+ struct record *rec = &record;
+
+ if (switch_output_time(rec))
trigger_hit(&switch_output_trigger);
}
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 06cc759..dbd7fa0 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -847,7 +847,9 @@
if (ret < 0)
return ret;
- perf_config(report__config, &report);
+ ret = perf_config(report__config, &report);
+ if (ret)
+ return ret;
argc = parse_options(argc, argv, options, report_usage, 0);
if (argc) {
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 5b134b0..270eb2d 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -77,6 +77,22 @@
#define TASK_STATE_TO_CHAR_STR "RSDTtZXxKWP"
+/* task state bitmask, copied from include/linux/sched.h */
+#define TASK_RUNNING 0
+#define TASK_INTERRUPTIBLE 1
+#define TASK_UNINTERRUPTIBLE 2
+#define __TASK_STOPPED 4
+#define __TASK_TRACED 8
+/* in tsk->exit_state */
+#define EXIT_DEAD 16
+#define EXIT_ZOMBIE 32
+#define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD)
+/* in tsk->state again */
+#define TASK_DEAD 64
+#define TASK_WAKEKILL 128
+#define TASK_WAKING 256
+#define TASK_PARKED 512
+
enum thread_state {
THREAD_SLEEPING = 0,
THREAD_WAIT_CPU,
@@ -206,6 +222,7 @@
bool show_cpu_visual;
bool show_wakeups;
bool show_migrations;
+ bool show_state;
u64 skipped_samples;
const char *time_str;
struct perf_time_interval ptime;
@@ -216,13 +233,20 @@
struct thread_runtime {
u64 last_time; /* time of previous sched in/out event */
u64 dt_run; /* run time */
- u64 dt_wait; /* time between CPU access (off cpu) */
+ u64 dt_sleep; /* time between CPU access by sleep (off cpu) */
+ u64 dt_iowait; /* time between CPU access by iowait (off cpu) */
+ u64 dt_preempt; /* time between CPU access by preempt (off cpu) */
u64 dt_delay; /* time between wakeup and sched-in */
u64 ready_to_run; /* time of wakeup */
struct stats run_stats;
u64 total_run_time;
+ u64 total_sleep_time;
+ u64 total_iowait_time;
+ u64 total_preempt_time;
+ u64 total_delay_time;
+ int last_state;
u64 migrations;
};
@@ -1821,6 +1845,9 @@
printf(" %-*s %9s %9s %9s", comm_width,
"task name", "wait time", "sch delay", "run time");
+ if (sched->show_state)
+ printf(" %s", "state");
+
printf("\n");
/*
@@ -1831,9 +1858,14 @@
if (sched->show_cpu_visual)
printf(" %*s ", ncpus, "");
- printf(" %-*s %9s %9s %9s\n", comm_width,
+ printf(" %-*s %9s %9s %9s", comm_width,
"[tid/pid]", "(msec)", "(msec)", "(msec)");
+ if (sched->show_state)
+ printf(" %5s", "");
+
+ printf("\n");
+
/*
* separator
*/
@@ -1846,18 +1878,34 @@
graph_dotted_line, graph_dotted_line, graph_dotted_line,
graph_dotted_line);
+ if (sched->show_state)
+ printf(" %.5s", graph_dotted_line);
+
printf("\n");
}
+static char task_state_char(struct thread *thread, int state)
+{
+ static const char state_to_char[] = TASK_STATE_TO_CHAR_STR;
+ unsigned bit = state ? ffs(state) : 0;
+
+ /* 'I' for idle */
+ if (thread->tid == 0)
+ return 'I';
+
+ return bit < sizeof(state_to_char) - 1 ? state_to_char[bit] : '?';
+}
+
static void timehist_print_sample(struct perf_sched *sched,
struct perf_sample *sample,
struct addr_location *al,
struct thread *thread,
- u64 t)
+ u64 t, int state)
{
struct thread_runtime *tr = thread__priv(thread);
u32 max_cpus = sched->max_cpu + 1;
char tstr[64];
+ u64 wait_time;
timestamp__scnprintf_usec(t, tstr, sizeof(tstr));
printf("%15s [%04d] ", tstr, sample->cpu);
@@ -1880,10 +1928,15 @@
printf(" %-*s ", comm_width, timehist_get_commstr(thread));
- print_sched_time(tr->dt_wait, 6);
+ wait_time = tr->dt_sleep + tr->dt_iowait + tr->dt_preempt;
+ print_sched_time(wait_time, 6);
+
print_sched_time(tr->dt_delay, 6);
print_sched_time(tr->dt_run, 6);
+ if (sched->show_state)
+ printf(" %5c ", task_state_char(thread, state));
+
if (sched->show_wakeups)
printf(" %-*s", comm_width, "");
@@ -1930,8 +1983,11 @@
u64 t, u64 tprev)
{
r->dt_delay = 0;
- r->dt_wait = 0;
+ r->dt_sleep = 0;
+ r->dt_iowait = 0;
+ r->dt_preempt = 0;
r->dt_run = 0;
+
if (tprev) {
r->dt_run = t - tprev;
if (r->ready_to_run) {
@@ -1943,12 +1999,25 @@
if (r->last_time > tprev)
pr_debug("time travel: last sched out time for task > previous sched_switch event\n");
- else if (r->last_time)
- r->dt_wait = tprev - r->last_time;
+ else if (r->last_time) {
+ u64 dt_wait = tprev - r->last_time;
+
+ if (r->last_state == TASK_RUNNING)
+ r->dt_preempt = dt_wait;
+ else if (r->last_state == TASK_UNINTERRUPTIBLE)
+ r->dt_iowait = dt_wait;
+ else
+ r->dt_sleep = dt_wait;
+ }
}
update_stats(&r->run_stats, r->dt_run);
- r->total_run_time += r->dt_run;
+
+ r->total_run_time += r->dt_run;
+ r->total_delay_time += r->dt_delay;
+ r->total_sleep_time += r->dt_sleep;
+ r->total_iowait_time += r->dt_iowait;
+ r->total_preempt_time += r->dt_preempt;
}
static bool is_idle_sample(struct perf_sample *sample,
@@ -1998,7 +2067,7 @@
break;
sym = node->sym;
- if (sym && sym->name) {
+ if (sym) {
if (!strcmp(sym->name, "schedule") ||
!strcmp(sym->name, "__schedule") ||
!strcmp(sym->name, "preempt_schedule"))
@@ -2373,6 +2442,8 @@
struct thread_runtime *tr = NULL;
u64 tprev, t = sample->time;
int rc = 0;
+ int state = perf_evsel__intval(evsel, sample, "prev_state");
+
if (machine__resolve(machine, &al, sample) < 0) {
pr_err("problem processing %d event. skipping it\n",
@@ -2447,8 +2518,10 @@
* time. we only care total run time and run stat.
*/
last_tr->dt_run = 0;
- last_tr->dt_wait = 0;
last_tr->dt_delay = 0;
+ last_tr->dt_sleep = 0;
+ last_tr->dt_iowait = 0;
+ last_tr->dt_preempt = 0;
if (itr->cursor.nr)
callchain_append(&itr->callchain, &itr->cursor, t - tprev);
@@ -2458,7 +2531,7 @@
}
if (!sched->summary_only)
- timehist_print_sample(sched, sample, &al, thread, t);
+ timehist_print_sample(sched, sample, &al, thread, t, state);
out:
if (sched->hist_time.start == 0 && t >= ptime->start)
@@ -2470,6 +2543,9 @@
/* time of this sched_switch event becomes last time task seen */
tr->last_time = sample->time;
+ /* last state is used to determine where to account wait time */
+ tr->last_state = state;
+
/* sched out event for task so reset ready to run time */
tr->ready_to_run = 0;
}
@@ -2526,7 +2602,26 @@
printf("\n");
}
+static void print_thread_waittime(struct thread *t,
+ struct thread_runtime *r)
+{
+ printf("%*s %5d %9" PRIu64 " ",
+ comm_width, timehist_get_commstr(t), t->ppid,
+ (u64) r->run_stats.n);
+
+ print_sched_time(r->total_run_time, 8);
+ print_sched_time(r->total_sleep_time, 6);
+ printf(" ");
+ print_sched_time(r->total_iowait_time, 6);
+ printf(" ");
+ print_sched_time(r->total_preempt_time, 6);
+ printf(" ");
+ print_sched_time(r->total_delay_time, 6);
+ printf("\n");
+}
+
struct total_run_stats {
+ struct perf_sched *sched;
u64 sched_count;
u64 task_count;
u64 total_run_time;
@@ -2545,7 +2640,11 @@
stats->task_count++;
stats->sched_count += r->run_stats.n;
stats->total_run_time += r->total_run_time;
- print_thread_runtime(t, r);
+
+ if (stats->sched->show_state)
+ print_thread_waittime(t, r);
+ else
+ print_thread_runtime(t, r);
}
return 0;
@@ -2633,18 +2732,24 @@
u64 hist_time = sched->hist_time.end - sched->hist_time.start;
memset(&totals, 0, sizeof(totals));
+ totals.sched = sched;
if (sched->idle_hist) {
printf("\nIdle-time summary\n");
printf("%*s parent sched-out ", comm_width, "comm");
printf(" idle-time min-idle avg-idle max-idle stddev migrations\n");
+ } else if (sched->show_state) {
+ printf("\nWait-time summary\n");
+ printf("%*s parent sched-in ", comm_width, "comm");
+ printf(" run-time sleep iowait preempt delay\n");
} else {
printf("\nRuntime summary\n");
printf("%*s parent sched-in ", comm_width, "comm");
printf(" run-time min-run avg-run max-run stddev migrations\n");
}
printf("%*s (count) ", comm_width, "");
- printf(" (msec) (msec) (msec) (msec) %%\n");
+ printf(" (msec) (msec) (msec) (msec) %s\n",
+ sched->show_state ? "(msec)" : "%");
printf("%.117s\n", graph_dotted_line);
machine__for_each_thread(m, show_thread_runtime, &totals);
@@ -3240,6 +3345,7 @@
OPT_BOOLEAN('I', "idle-hist", &sched.idle_hist, "Show idle events only"),
OPT_STRING(0, "time", &sched.time_str, "str",
"Time span for analysis (start,stop)"),
+ OPT_BOOLEAN(0, "state", &sched.show_state, "Show task state when sched-out"),
OPT_PARENT(sched_options)
};
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 2f3ff69..c0783b4 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -2180,7 +2180,7 @@
"Show the mmap events"),
OPT_BOOLEAN('\0', "show-switch-events", &script.show_switch_events,
"Show context switch events (if recorded)"),
- OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"),
+ OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
OPT_BOOLEAN(0, "ns", &nanosecs,
"Use 9 decimal places when displaying time"),
OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts",
@@ -2212,6 +2212,7 @@
PARSE_OPT_STOP_AT_NON_OPTION);
file.path = input_name;
+ file.force = symbol_conf.force;
if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) {
rec_script_path = get_script_path(argv[1], RECORD_SUFFIX);
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index a02f2e9..f287191 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -533,7 +533,7 @@
static int __run_perf_stat(int argc, const char **argv)
{
int interval = stat_config.interval;
- char msg[512];
+ char msg[BUFSIZ];
unsigned long long t0, t1;
struct perf_evsel *counter;
struct timespec ts;
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 3df4178..5a7fd7a 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -643,7 +643,7 @@
case -1:
if (errno == EINTR)
continue;
- /* Fall trhu */
+ __fallthrough;
default:
c = getc(stdin);
tcsetattr(0, TCSAFLUSH, &save);
@@ -859,7 +859,7 @@
static int perf_top__start_counters(struct perf_top *top)
{
- char msg[512];
+ char msg[BUFSIZ];
struct perf_evsel *counter;
struct perf_evlist *evlist = top->evlist;
struct record_opts *opts = &top->record_opts;
@@ -1216,7 +1216,9 @@
if (top.evlist == NULL)
return -ENOMEM;
- perf_config(perf_top_config, &top);
+ status = perf_config(perf_top_config, &top);
+ if (status)
+ return status;
argc = parse_options(argc, argv, options, top_usage, 0);
if (argc)
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 206bf72..40ef9b2 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -40,6 +40,7 @@
#include <libaudit.h> /* FIXME: Still needed for audit_errno_to_name */
#include <stdlib.h>
+#include <string.h>
#include <linux/err.h>
#include <linux/filter.h>
#include <linux/audit.h>
@@ -2699,6 +2700,91 @@
evsel->handler = handler;
}
+/*
+ * XXX: Hackish, just splitting the combined -e+--event (syscalls
+ * (raw_syscalls:{sys_{enter,exit}} + events (tracepoints, HW, SW, etc) to use
+ * existing facilities unchanged (trace->ev_qualifier + parse_options()).
+ *
+ * It'd be better to introduce a parse_options() variant that would return a
+ * list with the terms it didn't match to an event...
+ */
+static int trace__parse_events_option(const struct option *opt, const char *str,
+ int unset __maybe_unused)
+{
+ struct trace *trace = (struct trace *)opt->value;
+ const char *s = str;
+ char *sep = NULL, *lists[2] = { NULL, NULL, };
+ int len = strlen(str), err = -1, list;
+ char *strace_groups_dir = system_path(STRACE_GROUPS_DIR);
+ char group_name[PATH_MAX];
+
+ if (strace_groups_dir == NULL)
+ return -1;
+
+ if (*s == '!') {
+ ++s;
+ trace->not_ev_qualifier = true;
+ }
+
+ while (1) {
+ if ((sep = strchr(s, ',')) != NULL)
+ *sep = '\0';
+
+ list = 0;
+ if (syscalltbl__id(trace->sctbl, s) >= 0) {
+ list = 1;
+ } else {
+ path__join(group_name, sizeof(group_name), strace_groups_dir, s);
+ if (access(group_name, R_OK) == 0)
+ list = 1;
+ }
+
+ if (lists[list]) {
+ sprintf(lists[list] + strlen(lists[list]), ",%s", s);
+ } else {
+ lists[list] = malloc(len);
+ if (lists[list] == NULL)
+ goto out;
+ strcpy(lists[list], s);
+ }
+
+ if (!sep)
+ break;
+
+ *sep = ',';
+ s = sep + 1;
+ }
+
+ if (lists[1] != NULL) {
+ struct strlist_config slist_config = {
+ .dirname = strace_groups_dir,
+ };
+
+ trace->ev_qualifier = strlist__new(lists[1], &slist_config);
+ if (trace->ev_qualifier == NULL) {
+ fputs("Not enough memory to parse event qualifier", trace->output);
+ goto out;
+ }
+
+ if (trace__validate_ev_qualifier(trace))
+ goto out;
+ }
+
+ err = 0;
+
+ if (lists[0]) {
+ struct option o = OPT_CALLBACK('e', "event", &trace->evlist, "event",
+ "event selector. use 'perf list' to list available events",
+ parse_events_option);
+ err = parse_events_option(&o, lists[0], 0);
+ }
+out:
+ if (sep)
+ *sep = ',';
+
+ return err;
+}
+
int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
{
const char *trace_usage[] = {
@@ -2730,15 +2816,15 @@
.max_stack = UINT_MAX,
};
const char *output_name = NULL;
- const char *ev_qualifier_str = NULL;
const struct option trace_options[] = {
- OPT_CALLBACK(0, "event", &trace.evlist, "event",
- "event selector. use 'perf list' to list available events",
- parse_events_option),
+ OPT_CALLBACK('e', "event", &trace, "event",
+ "event/syscall selector. use 'perf list' to list available events",
+ trace__parse_events_option),
OPT_BOOLEAN(0, "comm", &trace.show_comm,
"show the thread COMM next to its id"),
OPT_BOOLEAN(0, "tool_stats", &trace.show_tool_stats, "show tool stats"),
- OPT_STRING('e', "expr", &ev_qualifier_str, "expr", "list of syscalls to trace"),
+ OPT_CALLBACK(0, "expr", &trace, "expr", "list of syscalls/events to trace",
+ trace__parse_events_option),
OPT_STRING('o', "output", &output_name, "file", "output file name"),
OPT_STRING('i', "input", &input_name, "file", "Analyze events in file"),
OPT_STRING('p', "pid", &trace.opts.target.pid, "pid",
@@ -2863,7 +2949,7 @@
return -1;
}
- if (!trace.trace_syscalls && ev_qualifier_str) {
+ if (!trace.trace_syscalls && trace.ev_qualifier) {
pr_err("The -e option can't be used with --no-syscalls.\n");
goto out;
}
@@ -2878,28 +2964,6 @@
trace.open_id = syscalltbl__id(trace.sctbl, "open");
- if (ev_qualifier_str != NULL) {
- const char *s = ev_qualifier_str;
- struct strlist_config slist_config = {
- .dirname = system_path(STRACE_GROUPS_DIR),
- };
-
- trace.not_ev_qualifier = *s == '!';
- if (trace.not_ev_qualifier)
- ++s;
- trace.ev_qualifier = strlist__new(s, &slist_config);
- if (trace.ev_qualifier == NULL) {
- fputs("Not enough memory to parse event qualifier",
- trace.output);
- err = -ENOMEM;
- goto out_close;
- }
-
- err = trace__validate_ev_qualifier(&trace);
- if (err)
- goto out_close;
- }
-
err = target__validate(&trace.opts.target);
if (err) {
target__strerror(&trace.opts.target, err, bf, sizeof(bf));
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
index 0bcf68e..036e1e3 100644
--- a/tools/perf/builtin.h
+++ b/tools/perf/builtin.h
@@ -23,6 +23,7 @@
int cmd_evlist(int argc, const char **argv, const char *prefix);
int cmd_help(int argc, const char **argv, const char *prefix);
int cmd_sched(int argc, const char **argv, const char *prefix);
+int cmd_kallsyms(int argc, const char **argv, const char *prefix);
int cmd_list(int argc, const char **argv, const char *prefix);
int cmd_record(int argc, const char **argv, const char *prefix);
int cmd_report(int argc, const char **argv, const char *prefix);
@@ -40,6 +41,7 @@
int cmd_inject(int argc, const char **argv, const char *prefix);
int cmd_mem(int argc, const char **argv, const char *prefix);
int cmd_data(int argc, const char **argv, const char *prefix);
+int cmd_ftrace(int argc, const char **argv, const char *prefix);
int find_scripts(char **scripts_array, char **scripts_path_array);
#endif
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt
index ab5cbaa..ac3efd3 100644
--- a/tools/perf/command-list.txt
+++ b/tools/perf/command-list.txt
@@ -11,7 +11,9 @@
perf-diff mainporcelain common
perf-config mainporcelain common
perf-evlist mainporcelain common
+perf-ftrace mainporcelain common
perf-inject mainporcelain common
+perf-kallsyms mainporcelain common
perf-kmem mainporcelain common
perf-kvm mainporcelain common
perf-list mainporcelain common
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index aa23b33..6d5479e 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -29,7 +29,6 @@
const char perf_more_info_string[] =
"See 'perf help COMMAND' for more information on a specific command.";
-int use_browser = -1;
static int use_pager = -1;
const char *input_name;
@@ -47,6 +46,7 @@
{ "diff", cmd_diff, 0 },
{ "evlist", cmd_evlist, 0 },
{ "help", cmd_help, 0 },
+ { "kallsyms", cmd_kallsyms, 0 },
{ "list", cmd_list, 0 },
{ "record", cmd_record, 0 },
{ "report", cmd_report, 0 },
@@ -71,6 +71,7 @@
{ "inject", cmd_inject, 0 },
{ "mem", cmd_mem, 0 },
{ "data", cmd_data, 0 },
+ { "ftrace", cmd_ftrace, 0 },
};
struct pager_config {
@@ -89,11 +90,12 @@
/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
int check_pager_config(const char *cmd)
{
+ int err;
struct pager_config c;
c.cmd = cmd;
c.val = -1;
- perf_config(pager_command_config, &c);
- return c.val;
+ err = perf_config(pager_command_config, &c);
+ return err ?: c.val;
}
static int browser_command_config(const char *var, const char *value, void *data)
@@ -112,11 +114,12 @@
*/
static int check_browser_config(const char *cmd)
{
+ int err;
struct pager_config c;
c.cmd = cmd;
c.val = -1;
- perf_config(browser_command_config, &c);
- return c.val;
+ err = perf_config(browser_command_config, &c);
+ return err ?: c.val;
}
static void commit_pager_choice(void)
@@ -329,8 +332,6 @@
return ret;
}
-const char perf_version_string[] = PERF_VERSION;
-
#define RUN_SETUP (1<<0)
#define USE_PAGER (1<<1)
@@ -510,6 +511,7 @@
int main(int argc, const char **argv)
{
+ int err;
const char *cmd;
char sbuf[STRERR_BUFSIZE];
int value;
@@ -535,7 +537,9 @@
srandom(time(NULL));
perf_config__init();
- perf_config(perf_default_config, NULL);
+ err = perf_config(perf_default_config, NULL);
+ if (err)
+ return err;
set_buildid_dir(NULL);
/* get debugfs/tracefs mount point from /proc/mounts */
diff --git a/tools/perf/pmu-events/arch/x86/broadwellde/uncore-cache.json b/tools/perf/pmu-events/arch/x86/broadwellde/uncore-cache.json
new file mode 100644
index 0000000..076459c
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/broadwellde/uncore-cache.json
@@ -0,0 +1,317 @@
+[
+ {
+ "BriefDescription": "Uncore cache clock ticks. Derived from unc_c_clockticks",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_C_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "All LLC Misses (code+ data rd + data wr - including demand and prefetch). Derived from unc_c_llc_lookup.any",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x34",
+ "EventName": "UNC_C_LLC_LOOKUP.ANY",
+ "Filter": "filter_state=0x1",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x11",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "M line evictions from LLC (writebacks to memory). Derived from unc_c_llc_victims.m_state",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x37",
+ "EventName": "UNC_C_LLC_VICTIMS.M_STATE",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses - demand and prefetch data reads - excludes LLC prefetches. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.DATA_READ",
+ "Filter": "filter_opc=0x182",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses - Uncacheable reads (from cpu) . Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.UNCACHEABLE",
+ "Filter": "filter_opc=0x187",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "MMIO reads. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.MMIO_READ",
+ "Filter": "filter_opc=0x187,filter_nc=1",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "MMIO writes. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.MMIO_WRITE",
+ "Filter": "filter_opc=0x18f,filter_nc=1",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC prefetch misses for RFO. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.RFO_LLC_PREFETCH",
+ "Filter": "filter_opc=0x190",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC prefetch misses for code reads. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.CODE_LLC_PREFETCH",
+ "Filter": "filter_opc=0x191",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC prefetch misses for data reads. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.DATA_LLC_PREFETCH",
+ "Filter": "filter_opc=0x192",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses for PCIe read current. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.PCIE_READ",
+ "Filter": "filter_opc=0x19e",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "ItoM write misses (as part of fast string memcpy stores) + PCIe full line writes. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.PCIE_WRITE",
+ "Filter": "filter_opc=0x1c8",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe write misses (full cache line). Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.PCIE_NON_SNOOP_WRITE",
+ "Filter": "filter_opc=0x1c8,filter_tid=0x3e",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe writes (partial cache line). Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_NS_PARTIAL_WRITE",
+ "Filter": "filter_opc=0x180,filter_tid=0x3e",
+ "PerPkg": "1",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "L2 demand and L2 prefetch code references to LLC. Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.CODE_LLC_PREFETCH",
+ "Filter": "filter_opc=0x181",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Streaming stores (full cache line). Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.STREAMING_FULL",
+ "Filter": "filter_opc=0x18c",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Streaming stores (partial cache line). Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.STREAMING_PARTIAL",
+ "Filter": "filter_opc=0x18d",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe read current. Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_READ",
+ "Filter": "filter_opc=0x19e",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe write references (full cache line). Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_WRITE",
+ "Filter": "filter_opc=0x1c8,filter_tid=0x3e",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Occupancy counter for LLC data reads (demand and L2 prefetch). Derived from unc_c_tor_occupancy.miss_opcode",
+ "EventCode": "0x36",
+ "EventName": "UNC_C_TOR_OCCUPANCY.LLC_DATA_READ",
+ "Filter": "filter_opc=0x182",
+ "PerPkg": "1",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "read requests to home agent. Derived from unc_h_requests.reads",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.READS",
+ "PerPkg": "1",
+ "UMask": "0x3",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "read requests to local home agent. Derived from unc_h_requests.reads_local",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.READS_LOCAL",
+ "PerPkg": "1",
+ "UMask": "0x1",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "read requests to remote home agent. Derived from unc_h_requests.reads_remote",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.READS_REMOTE",
+ "PerPkg": "1",
+ "UMask": "0x2",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "write requests to home agent. Derived from unc_h_requests.writes",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.WRITES",
+ "PerPkg": "1",
+ "UMask": "0xC",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "write requests to local home agent. Derived from unc_h_requests.writes_local",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.WRITES_LOCAL",
+ "PerPkg": "1",
+ "UMask": "0x4",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "write requests to remote home agent. Derived from unc_h_requests.writes_remote",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.WRITES_REMOTE",
+ "PerPkg": "1",
+ "UMask": "0x8",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "Conflict requests (requests for same address from multiple agents simultaneously). Derived from unc_h_snoop_resp.rspcnflct",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSPCNFLCT",
+ "PerPkg": "1",
+ "UMask": "0x40",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "M line forwarded from remote cache along with writeback to memory. Derived from unc_h_snoop_resp.rsp_fwd_wb",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSP_FWD_WB",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x20",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "M line forwarded from remote cache with no writeback to memory. Derived from unc_h_snoop_resp.rspifwd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSPIFWD",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x4",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "Shared line response from remote cache. Derived from unc_h_snoop_resp.rsps",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSPS",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x2",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "Shared line forwarded from remote cache. Derived from unc_h_snoop_resp.rspsfwd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSPSFWD",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x8",
+ "Unit": "HA"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/broadwellde/uncore-memory.json b/tools/perf/pmu-events/arch/x86/broadwellde/uncore-memory.json
new file mode 100644
index 0000000..d17dc23
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/broadwellde/uncore-memory.json
@@ -0,0 +1,83 @@
+[
+ {
+ "BriefDescription": "read requests to memory controller. Derived from unc_m_cas_count.rd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x4",
+ "EventName": "UNC_M_CAS_COUNT.RD",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "write requests to memory controller. Derived from unc_m_cas_count.wr",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x4",
+ "EventName": "UNC_M_CAS_COUNT.WR",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0xC",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Memory controller clock ticks. Derived from unc_m_clockticks",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_M_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Cycles where DRAM ranks are in power down (CKE) mode. Derived from unc_m_power_channel_ppd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x85",
+ "EventName": "UNC_M_POWER_CHANNEL_PPD",
+ "MetricExpr": "(UNC_M_POWER_CHANNEL_PPD / UNC_M_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Cycles all ranks are in critical thermal throttle. Derived from unc_m_power_critical_throttle_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x86",
+ "EventName": "UNC_M_POWER_CRITICAL_THROTTLE_CYCLES",
+ "MetricExpr": "(UNC_M_POWER_CRITICAL_THROTTLE_CYCLES / UNC_M_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Cycles Memory is in self refresh power mode. Derived from unc_m_power_self_refresh",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x43",
+ "EventName": "UNC_M_POWER_SELF_REFRESH",
+ "MetricExpr": "(UNC_M_POWER_SELF_REFRESH / UNC_M_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Pre-charges due to page misses. Derived from unc_m_pre_count.page_miss",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x2",
+ "EventName": "UNC_M_PRE_COUNT.PAGE_MISS",
+ "PerPkg": "1",
+ "UMask": "0x1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Pre-charge for reads. Derived from unc_m_pre_count.rd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x2",
+ "EventName": "UNC_M_PRE_COUNT.RD",
+ "PerPkg": "1",
+ "UMask": "0x4",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Pre-charge for writes. Derived from unc_m_pre_count.wr",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x2",
+ "EventName": "UNC_M_PRE_COUNT.WR",
+ "PerPkg": "1",
+ "UMask": "0x8",
+ "Unit": "iMC"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/broadwellde/uncore-power.json b/tools/perf/pmu-events/arch/x86/broadwellde/uncore-power.json
new file mode 100644
index 0000000..b44d430
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/broadwellde/uncore-power.json
@@ -0,0 +1,84 @@
+[
+ {
+ "BriefDescription": "PCU clock ticks. Use to get percentages of PCU cycles events. Derived from unc_p_clockticks",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_P_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "C0 and C1. Derived from unc_p_power_state_occupancy.cores_c0",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x80",
+ "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C0",
+ "Filter": "occ_sel=1",
+ "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C0 / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "C3. Derived from unc_p_power_state_occupancy.cores_c3",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x80",
+ "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C3",
+ "Filter": "occ_sel=2",
+ "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C3 / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "C6 and C7. Derived from unc_p_power_state_occupancy.cores_c6",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x80",
+ "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C6",
+ "Filter": "occ_sel=3",
+ "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C6 / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "External Prochot. Derived from unc_p_prochot_external_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xA",
+ "EventName": "UNC_P_PROCHOT_EXTERNAL_CYCLES",
+ "MetricExpr": "(UNC_P_PROCHOT_EXTERNAL_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Thermal Strongest Upper Limit Cycles. Derived from unc_p_freq_max_limit_thermal_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x4",
+ "EventName": "UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "OS Strongest Upper Limit Cycles. Derived from unc_p_freq_max_os_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x6",
+ "EventName": "UNC_P_FREQ_MAX_OS_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_MAX_OS_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Power Strongest Upper Limit Cycles. Derived from unc_p_freq_max_power_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x5",
+ "EventName": "UNC_P_FREQ_MAX_POWER_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_MAX_POWER_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Cycles spent changing Frequency. Derived from unc_p_freq_trans_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x74",
+ "EventName": "UNC_P_FREQ_TRANS_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_TRANS_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/broadwellx/uncore-cache.json b/tools/perf/pmu-events/arch/x86/broadwellx/uncore-cache.json
new file mode 100644
index 0000000..076459c
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/broadwellx/uncore-cache.json
@@ -0,0 +1,317 @@
+[
+ {
+ "BriefDescription": "Uncore cache clock ticks. Derived from unc_c_clockticks",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_C_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "All LLC Misses (code+ data rd + data wr - including demand and prefetch). Derived from unc_c_llc_lookup.any",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x34",
+ "EventName": "UNC_C_LLC_LOOKUP.ANY",
+ "Filter": "filter_state=0x1",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x11",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "M line evictions from LLC (writebacks to memory). Derived from unc_c_llc_victims.m_state",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x37",
+ "EventName": "UNC_C_LLC_VICTIMS.M_STATE",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses - demand and prefetch data reads - excludes LLC prefetches. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.DATA_READ",
+ "Filter": "filter_opc=0x182",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses - Uncacheable reads (from cpu) . Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.UNCACHEABLE",
+ "Filter": "filter_opc=0x187",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "MMIO reads. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.MMIO_READ",
+ "Filter": "filter_opc=0x187,filter_nc=1",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "MMIO writes. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.MMIO_WRITE",
+ "Filter": "filter_opc=0x18f,filter_nc=1",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC prefetch misses for RFO. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.RFO_LLC_PREFETCH",
+ "Filter": "filter_opc=0x190",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC prefetch misses for code reads. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.CODE_LLC_PREFETCH",
+ "Filter": "filter_opc=0x191",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC prefetch misses for data reads. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.DATA_LLC_PREFETCH",
+ "Filter": "filter_opc=0x192",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses for PCIe read current. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.PCIE_READ",
+ "Filter": "filter_opc=0x19e",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "ItoM write misses (as part of fast string memcpy stores) + PCIe full line writes. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.PCIE_WRITE",
+ "Filter": "filter_opc=0x1c8",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe write misses (full cache line). Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.PCIE_NON_SNOOP_WRITE",
+ "Filter": "filter_opc=0x1c8,filter_tid=0x3e",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe writes (partial cache line). Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_NS_PARTIAL_WRITE",
+ "Filter": "filter_opc=0x180,filter_tid=0x3e",
+ "PerPkg": "1",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "L2 demand and L2 prefetch code references to LLC. Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.CODE_LLC_PREFETCH",
+ "Filter": "filter_opc=0x181",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Streaming stores (full cache line). Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.STREAMING_FULL",
+ "Filter": "filter_opc=0x18c",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Streaming stores (partial cache line). Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.STREAMING_PARTIAL",
+ "Filter": "filter_opc=0x18d",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe read current. Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_READ",
+ "Filter": "filter_opc=0x19e",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe write references (full cache line). Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_WRITE",
+ "Filter": "filter_opc=0x1c8,filter_tid=0x3e",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Occupancy counter for LLC data reads (demand and L2 prefetch). Derived from unc_c_tor_occupancy.miss_opcode",
+ "EventCode": "0x36",
+ "EventName": "UNC_C_TOR_OCCUPANCY.LLC_DATA_READ",
+ "Filter": "filter_opc=0x182",
+ "PerPkg": "1",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "read requests to home agent. Derived from unc_h_requests.reads",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.READS",
+ "PerPkg": "1",
+ "UMask": "0x3",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "read requests to local home agent. Derived from unc_h_requests.reads_local",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.READS_LOCAL",
+ "PerPkg": "1",
+ "UMask": "0x1",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "read requests to remote home agent. Derived from unc_h_requests.reads_remote",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.READS_REMOTE",
+ "PerPkg": "1",
+ "UMask": "0x2",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "write requests to home agent. Derived from unc_h_requests.writes",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.WRITES",
+ "PerPkg": "1",
+ "UMask": "0xC",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "write requests to local home agent. Derived from unc_h_requests.writes_local",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.WRITES_LOCAL",
+ "PerPkg": "1",
+ "UMask": "0x4",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "write requests to remote home agent. Derived from unc_h_requests.writes_remote",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.WRITES_REMOTE",
+ "PerPkg": "1",
+ "UMask": "0x8",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "Conflict requests (requests for same address from multiple agents simultaneously). Derived from unc_h_snoop_resp.rspcnflct",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSPCNFLCT",
+ "PerPkg": "1",
+ "UMask": "0x40",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "M line forwarded from remote cache along with writeback to memory. Derived from unc_h_snoop_resp.rsp_fwd_wb",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSP_FWD_WB",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x20",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "M line forwarded from remote cache with no writeback to memory. Derived from unc_h_snoop_resp.rspifwd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSPIFWD",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x4",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "Shared line response from remote cache. Derived from unc_h_snoop_resp.rsps",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSPS",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x2",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "Shared line forwarded from remote cache. Derived from unc_h_snoop_resp.rspsfwd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSPSFWD",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x8",
+ "Unit": "HA"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/broadwellx/uncore-interconnect.json b/tools/perf/pmu-events/arch/x86/broadwellx/uncore-interconnect.json
new file mode 100644
index 0000000..39387f7
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/broadwellx/uncore-interconnect.json
@@ -0,0 +1,28 @@
+[
+ {
+ "BriefDescription": "QPI clock ticks. Derived from unc_q_clockticks",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x14",
+ "EventName": "UNC_Q_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "QPI LL"
+ },
+ {
+ "BriefDescription": "Number of data flits transmitted . Derived from unc_q_txl_flits_g0.data",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_Q_TxL_FLITS_G0.DATA",
+ "PerPkg": "1",
+ "ScaleUnit": "8Bytes",
+ "UMask": "0x2",
+ "Unit": "QPI LL"
+ },
+ {
+ "BriefDescription": "Number of non data (control) flits transmitted . Derived from unc_q_txl_flits_g0.non_data",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_Q_TxL_FLITS_G0.NON_DATA",
+ "PerPkg": "1",
+ "ScaleUnit": "8Bytes",
+ "UMask": "0x4",
+ "Unit": "QPI LL"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/broadwellx/uncore-memory.json b/tools/perf/pmu-events/arch/x86/broadwellx/uncore-memory.json
new file mode 100644
index 0000000..d17dc23
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/broadwellx/uncore-memory.json
@@ -0,0 +1,83 @@
+[
+ {
+ "BriefDescription": "read requests to memory controller. Derived from unc_m_cas_count.rd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x4",
+ "EventName": "UNC_M_CAS_COUNT.RD",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "write requests to memory controller. Derived from unc_m_cas_count.wr",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x4",
+ "EventName": "UNC_M_CAS_COUNT.WR",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0xC",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Memory controller clock ticks. Derived from unc_m_clockticks",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_M_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Cycles where DRAM ranks are in power down (CKE) mode. Derived from unc_m_power_channel_ppd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x85",
+ "EventName": "UNC_M_POWER_CHANNEL_PPD",
+ "MetricExpr": "(UNC_M_POWER_CHANNEL_PPD / UNC_M_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Cycles all ranks are in critical thermal throttle. Derived from unc_m_power_critical_throttle_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x86",
+ "EventName": "UNC_M_POWER_CRITICAL_THROTTLE_CYCLES",
+ "MetricExpr": "(UNC_M_POWER_CRITICAL_THROTTLE_CYCLES / UNC_M_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Cycles Memory is in self refresh power mode. Derived from unc_m_power_self_refresh",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x43",
+ "EventName": "UNC_M_POWER_SELF_REFRESH",
+ "MetricExpr": "(UNC_M_POWER_SELF_REFRESH / UNC_M_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Pre-charges due to page misses. Derived from unc_m_pre_count.page_miss",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x2",
+ "EventName": "UNC_M_PRE_COUNT.PAGE_MISS",
+ "PerPkg": "1",
+ "UMask": "0x1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Pre-charge for reads. Derived from unc_m_pre_count.rd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x2",
+ "EventName": "UNC_M_PRE_COUNT.RD",
+ "PerPkg": "1",
+ "UMask": "0x4",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Pre-charge for writes. Derived from unc_m_pre_count.wr",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x2",
+ "EventName": "UNC_M_PRE_COUNT.WR",
+ "PerPkg": "1",
+ "UMask": "0x8",
+ "Unit": "iMC"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/broadwellx/uncore-power.json b/tools/perf/pmu-events/arch/x86/broadwellx/uncore-power.json
new file mode 100644
index 0000000..b44d430
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/broadwellx/uncore-power.json
@@ -0,0 +1,84 @@
+[
+ {
+ "BriefDescription": "PCU clock ticks. Use to get percentages of PCU cycles events. Derived from unc_p_clockticks",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_P_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "C0 and C1. Derived from unc_p_power_state_occupancy.cores_c0",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x80",
+ "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C0",
+ "Filter": "occ_sel=1",
+ "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C0 / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "C3. Derived from unc_p_power_state_occupancy.cores_c3",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x80",
+ "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C3",
+ "Filter": "occ_sel=2",
+ "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C3 / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "C6 and C7. Derived from unc_p_power_state_occupancy.cores_c6",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x80",
+ "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C6",
+ "Filter": "occ_sel=3",
+ "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C6 / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "External Prochot. Derived from unc_p_prochot_external_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xA",
+ "EventName": "UNC_P_PROCHOT_EXTERNAL_CYCLES",
+ "MetricExpr": "(UNC_P_PROCHOT_EXTERNAL_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Thermal Strongest Upper Limit Cycles. Derived from unc_p_freq_max_limit_thermal_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x4",
+ "EventName": "UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "OS Strongest Upper Limit Cycles. Derived from unc_p_freq_max_os_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x6",
+ "EventName": "UNC_P_FREQ_MAX_OS_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_MAX_OS_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Power Strongest Upper Limit Cycles. Derived from unc_p_freq_max_power_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x5",
+ "EventName": "UNC_P_FREQ_MAX_POWER_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_MAX_POWER_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Cycles spent changing Frequency. Derived from unc_p_freq_trans_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x74",
+ "EventName": "UNC_P_FREQ_TRANS_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_TRANS_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/haswellx/uncore-cache.json b/tools/perf/pmu-events/arch/x86/haswellx/uncore-cache.json
new file mode 100644
index 0000000..076459c
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/haswellx/uncore-cache.json
@@ -0,0 +1,317 @@
+[
+ {
+ "BriefDescription": "Uncore cache clock ticks. Derived from unc_c_clockticks",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_C_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "All LLC Misses (code+ data rd + data wr - including demand and prefetch). Derived from unc_c_llc_lookup.any",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x34",
+ "EventName": "UNC_C_LLC_LOOKUP.ANY",
+ "Filter": "filter_state=0x1",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x11",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "M line evictions from LLC (writebacks to memory). Derived from unc_c_llc_victims.m_state",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x37",
+ "EventName": "UNC_C_LLC_VICTIMS.M_STATE",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses - demand and prefetch data reads - excludes LLC prefetches. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.DATA_READ",
+ "Filter": "filter_opc=0x182",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses - Uncacheable reads (from cpu) . Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.UNCACHEABLE",
+ "Filter": "filter_opc=0x187",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "MMIO reads. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.MMIO_READ",
+ "Filter": "filter_opc=0x187,filter_nc=1",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "MMIO writes. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.MMIO_WRITE",
+ "Filter": "filter_opc=0x18f,filter_nc=1",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC prefetch misses for RFO. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.RFO_LLC_PREFETCH",
+ "Filter": "filter_opc=0x190",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC prefetch misses for code reads. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.CODE_LLC_PREFETCH",
+ "Filter": "filter_opc=0x191",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC prefetch misses for data reads. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.DATA_LLC_PREFETCH",
+ "Filter": "filter_opc=0x192",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses for PCIe read current. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.PCIE_READ",
+ "Filter": "filter_opc=0x19e",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "ItoM write misses (as part of fast string memcpy stores) + PCIe full line writes. Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.PCIE_WRITE",
+ "Filter": "filter_opc=0x1c8",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe write misses (full cache line). Derived from unc_c_tor_inserts.miss_opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.PCIE_NON_SNOOP_WRITE",
+ "Filter": "filter_opc=0x1c8,filter_tid=0x3e",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe writes (partial cache line). Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_NS_PARTIAL_WRITE",
+ "Filter": "filter_opc=0x180,filter_tid=0x3e",
+ "PerPkg": "1",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "L2 demand and L2 prefetch code references to LLC. Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.CODE_LLC_PREFETCH",
+ "Filter": "filter_opc=0x181",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Streaming stores (full cache line). Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.STREAMING_FULL",
+ "Filter": "filter_opc=0x18c",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Streaming stores (partial cache line). Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.STREAMING_PARTIAL",
+ "Filter": "filter_opc=0x18d",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe read current. Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_READ",
+ "Filter": "filter_opc=0x19e",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe write references (full cache line). Derived from unc_c_tor_inserts.opcode",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_WRITE",
+ "Filter": "filter_opc=0x1c8,filter_tid=0x3e",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Occupancy counter for LLC data reads (demand and L2 prefetch). Derived from unc_c_tor_occupancy.miss_opcode",
+ "EventCode": "0x36",
+ "EventName": "UNC_C_TOR_OCCUPANCY.LLC_DATA_READ",
+ "Filter": "filter_opc=0x182",
+ "PerPkg": "1",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "read requests to home agent. Derived from unc_h_requests.reads",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.READS",
+ "PerPkg": "1",
+ "UMask": "0x3",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "read requests to local home agent. Derived from unc_h_requests.reads_local",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.READS_LOCAL",
+ "PerPkg": "1",
+ "UMask": "0x1",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "read requests to remote home agent. Derived from unc_h_requests.reads_remote",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.READS_REMOTE",
+ "PerPkg": "1",
+ "UMask": "0x2",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "write requests to home agent. Derived from unc_h_requests.writes",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.WRITES",
+ "PerPkg": "1",
+ "UMask": "0xC",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "write requests to local home agent. Derived from unc_h_requests.writes_local",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.WRITES_LOCAL",
+ "PerPkg": "1",
+ "UMask": "0x4",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "write requests to remote home agent. Derived from unc_h_requests.writes_remote",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.WRITES_REMOTE",
+ "PerPkg": "1",
+ "UMask": "0x8",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "Conflict requests (requests for same address from multiple agents simultaneously). Derived from unc_h_snoop_resp.rspcnflct",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSPCNFLCT",
+ "PerPkg": "1",
+ "UMask": "0x40",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "M line forwarded from remote cache along with writeback to memory. Derived from unc_h_snoop_resp.rsp_fwd_wb",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSP_FWD_WB",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x20",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "M line forwarded from remote cache with no writeback to memory. Derived from unc_h_snoop_resp.rspifwd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSPIFWD",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x4",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "Shared line response from remote cache. Derived from unc_h_snoop_resp.rsps",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSPS",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x2",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "Shared line forwarded from remote cache. Derived from unc_h_snoop_resp.rspsfwd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSPSFWD",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x8",
+ "Unit": "HA"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/haswellx/uncore-interconnect.json b/tools/perf/pmu-events/arch/x86/haswellx/uncore-interconnect.json
new file mode 100644
index 0000000..39387f7
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/haswellx/uncore-interconnect.json
@@ -0,0 +1,28 @@
+[
+ {
+ "BriefDescription": "QPI clock ticks. Derived from unc_q_clockticks",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x14",
+ "EventName": "UNC_Q_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "QPI LL"
+ },
+ {
+ "BriefDescription": "Number of data flits transmitted . Derived from unc_q_txl_flits_g0.data",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_Q_TxL_FLITS_G0.DATA",
+ "PerPkg": "1",
+ "ScaleUnit": "8Bytes",
+ "UMask": "0x2",
+ "Unit": "QPI LL"
+ },
+ {
+ "BriefDescription": "Number of non data (control) flits transmitted . Derived from unc_q_txl_flits_g0.non_data",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_Q_TxL_FLITS_G0.NON_DATA",
+ "PerPkg": "1",
+ "ScaleUnit": "8Bytes",
+ "UMask": "0x4",
+ "Unit": "QPI LL"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/haswellx/uncore-memory.json b/tools/perf/pmu-events/arch/x86/haswellx/uncore-memory.json
new file mode 100644
index 0000000..d17dc23
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/haswellx/uncore-memory.json
@@ -0,0 +1,83 @@
+[
+ {
+ "BriefDescription": "read requests to memory controller. Derived from unc_m_cas_count.rd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x4",
+ "EventName": "UNC_M_CAS_COUNT.RD",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "write requests to memory controller. Derived from unc_m_cas_count.wr",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x4",
+ "EventName": "UNC_M_CAS_COUNT.WR",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0xC",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Memory controller clock ticks. Derived from unc_m_clockticks",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_M_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Cycles where DRAM ranks are in power down (CKE) mode. Derived from unc_m_power_channel_ppd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x85",
+ "EventName": "UNC_M_POWER_CHANNEL_PPD",
+ "MetricExpr": "(UNC_M_POWER_CHANNEL_PPD / UNC_M_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Cycles all ranks are in critical thermal throttle. Derived from unc_m_power_critical_throttle_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x86",
+ "EventName": "UNC_M_POWER_CRITICAL_THROTTLE_CYCLES",
+ "MetricExpr": "(UNC_M_POWER_CRITICAL_THROTTLE_CYCLES / UNC_M_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Cycles Memory is in self refresh power mode. Derived from unc_m_power_self_refresh",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x43",
+ "EventName": "UNC_M_POWER_SELF_REFRESH",
+ "MetricExpr": "(UNC_M_POWER_SELF_REFRESH / UNC_M_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Pre-charges due to page misses. Derived from unc_m_pre_count.page_miss",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x2",
+ "EventName": "UNC_M_PRE_COUNT.PAGE_MISS",
+ "PerPkg": "1",
+ "UMask": "0x1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Pre-charge for reads. Derived from unc_m_pre_count.rd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x2",
+ "EventName": "UNC_M_PRE_COUNT.RD",
+ "PerPkg": "1",
+ "UMask": "0x4",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Pre-charge for writes. Derived from unc_m_pre_count.wr",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x2",
+ "EventName": "UNC_M_PRE_COUNT.WR",
+ "PerPkg": "1",
+ "UMask": "0x8",
+ "Unit": "iMC"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/haswellx/uncore-power.json b/tools/perf/pmu-events/arch/x86/haswellx/uncore-power.json
new file mode 100644
index 0000000..b44d430
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/haswellx/uncore-power.json
@@ -0,0 +1,84 @@
+[
+ {
+ "BriefDescription": "PCU clock ticks. Use to get percentages of PCU cycles events. Derived from unc_p_clockticks",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_P_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "C0 and C1. Derived from unc_p_power_state_occupancy.cores_c0",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x80",
+ "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C0",
+ "Filter": "occ_sel=1",
+ "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C0 / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "C3. Derived from unc_p_power_state_occupancy.cores_c3",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x80",
+ "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C3",
+ "Filter": "occ_sel=2",
+ "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C3 / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "C6 and C7. Derived from unc_p_power_state_occupancy.cores_c6",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x80",
+ "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C6",
+ "Filter": "occ_sel=3",
+ "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C6 / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "External Prochot. Derived from unc_p_prochot_external_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xA",
+ "EventName": "UNC_P_PROCHOT_EXTERNAL_CYCLES",
+ "MetricExpr": "(UNC_P_PROCHOT_EXTERNAL_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Thermal Strongest Upper Limit Cycles. Derived from unc_p_freq_max_limit_thermal_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x4",
+ "EventName": "UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "OS Strongest Upper Limit Cycles. Derived from unc_p_freq_max_os_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x6",
+ "EventName": "UNC_P_FREQ_MAX_OS_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_MAX_OS_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Power Strongest Upper Limit Cycles. Derived from unc_p_freq_max_power_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x5",
+ "EventName": "UNC_P_FREQ_MAX_POWER_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_MAX_POWER_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Cycles spent changing Frequency. Derived from unc_p_freq_trans_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x74",
+ "EventName": "UNC_P_FREQ_TRANS_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_TRANS_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/ivytown/uncore-cache.json b/tools/perf/pmu-events/arch/x86/ivytown/uncore-cache.json
new file mode 100644
index 0000000..2efdc67
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/ivytown/uncore-cache.json
@@ -0,0 +1,322 @@
+[
+ {
+ "BriefDescription": "Uncore cache clock ticks. Derived from unc_c_clockticks",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_C_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "All LLC Misses (code+ data rd + data wr - including demand and prefetch). Derived from unc_c_llc_lookup.any",
+ "Counter": "0,1",
+ "EventCode": "0x34",
+ "EventName": "UNC_C_LLC_LOOKUP.ANY",
+ "Filter": "filter_state=0x1",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x11",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "M line evictions from LLC (writebacks to memory). Derived from unc_c_llc_victims.m_state",
+ "Counter": "0,1",
+ "EventCode": "0x37",
+ "EventName": "UNC_C_LLC_VICTIMS.M_STATE",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses - demand and prefetch data reads - excludes LLC prefetches. Derived from unc_c_tor_inserts.miss_opcode.demand",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.DATA_READ",
+ "Filter": "filter_opc=0x182",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses - Uncacheable reads. Derived from unc_c_tor_inserts.miss_opcode.uncacheable",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.UNCACHEABLE",
+ "Filter": "filter_opc=0x187",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC prefetch misses for RFO. Derived from unc_c_tor_inserts.miss_opcode.rfo_prefetch",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.RFO_LLC_PREFETCH",
+ "Filter": "filter_opc=0x190",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC prefetch misses for code reads. Derived from unc_c_tor_inserts.miss_opcode.code",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.CODE_LLC_PREFETCH",
+ "Filter": "filter_opc=0x191",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC prefetch misses for data reads. Derived from unc_c_tor_inserts.miss_opcode.data_read",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.DATA_LLC_PREFETCH",
+ "Filter": "filter_opc=0x192",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe allocating writes that miss LLC - DDIO misses. Derived from unc_c_tor_inserts.miss_opcode.ddio_miss",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.PCIE_WRITE",
+ "Filter": "filter_opc=0x19c",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses for PCIe read current. Derived from unc_c_tor_inserts.miss_opcode.pcie_read",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.PCIE_READ",
+ "Filter": "filter_opc=0x19e",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses for ItoM writes (as part of fast string memcpy stores). Derived from unc_c_tor_inserts.miss_opcode.itom_write",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.ITOM_WRITE",
+ "Filter": "filter_opc=0x1c8",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses for PCIe non-snoop reads. Derived from unc_c_tor_inserts.miss_opcode.pcie_read",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.PCIE_NON_SNOOP_READ",
+ "Filter": "filter_opc=0x1e4",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses for PCIe non-snoop writes (full line). Derived from unc_c_tor_inserts.miss_opcode.pcie_write",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.PCIE_NON_SNOOP_WRITE",
+ "Filter": "filter_opc=0x1e6",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Streaming stores (full cache line). Derived from unc_c_tor_inserts.opcode.streaming_full",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.STREAMING_FULL",
+ "Filter": "filter_opc=0x18c",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Streaming stores (partial cache line). Derived from unc_c_tor_inserts.opcode.streaming_partial",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.STREAMING_PARTIAL",
+ "Filter": "filter_opc=0x18d",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Partial PCIe reads. Derived from unc_c_tor_inserts.opcode.pcie_partial",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_PARTIAL_READ",
+ "Filter": "filter_opc=0x195",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe allocating writes that hit in LLC (DDIO hits). Derived from unc_c_tor_inserts.opcode.ddio_hit",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_WRITE",
+ "Filter": "filter_opc=0x19c",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe read current. Derived from unc_c_tor_inserts.opcode.pcie_read_current",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_READ",
+ "Filter": "filter_opc=0x19e",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "ItoM write hits (as part of fast string memcpy stores). Derived from unc_c_tor_inserts.opcode.itom_write_hit",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.ITOM_WRITE",
+ "Filter": "filter_opc=0x1c8",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe non-snoop reads. Derived from unc_c_tor_inserts.opcode.pcie_read",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_NS_READ",
+ "Filter": "filter_opc=0x1e4",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe non-snoop writes (partial). Derived from unc_c_tor_inserts.opcode.pcie_partial_write",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_NS_PARTIAL_WRITE",
+ "Filter": "filter_opc=0x1e5",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe non-snoop writes (full line). Derived from unc_c_tor_inserts.opcode.pcie_full_write",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_NS_WRITE",
+ "Filter": "filter_opc=0x1e6",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Occupancy for all LLC misses that are addressed to local memory. Derived from unc_c_tor_occupancy.miss_local",
+ "EventCode": "0x36",
+ "EventName": "UNC_C_TOR_OCCUPANCY.MISS_LOCAL",
+ "PerPkg": "1",
+ "UMask": "0x2A",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Occupancy counter for LLC data reads (demand and L2 prefetch). Derived from unc_c_tor_occupancy.miss_opcode.llc_data_read",
+ "EventCode": "0x36",
+ "EventName": "UNC_C_TOR_OCCUPANCY.LLC_DATA_READ",
+ "Filter": "filter_opc=0x182",
+ "PerPkg": "1",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Occupancy for all LLC misses that are addressed to remote memory. Derived from unc_c_tor_occupancy.miss_remote",
+ "EventCode": "0x36",
+ "EventName": "UNC_C_TOR_OCCUPANCY.MISS_REMOTE",
+ "PerPkg": "1",
+ "UMask": "0x8A",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Read requests to home agent. Derived from unc_h_requests.reads",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.READS",
+ "PerPkg": "1",
+ "UMask": "0x3",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "Write requests to home agent. Derived from unc_h_requests.writes",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.WRITES",
+ "PerPkg": "1",
+ "UMask": "0xC",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "M line forwarded from remote cache along with writeback to memory. Derived from unc_h_snoop_resp.rsp_fwd_wb",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSP_FWD_WB",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x20",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "M line forwarded from remote cache with no writeback to memory. Derived from unc_h_snoop_resp.rspifwd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSPIFWD",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x4",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "Shared line response from remote cache. Derived from unc_h_snoop_resp.rsps",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSPS",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x2",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "Shared line forwarded from remote cache. Derived from unc_h_snoop_resp.rspsfwd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x21",
+ "EventName": "UNC_H_SNOOP_RESP.RSPSFWD",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x8",
+ "Unit": "HA"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/ivytown/uncore-interconnect.json b/tools/perf/pmu-events/arch/x86/ivytown/uncore-interconnect.json
new file mode 100644
index 0000000..d7e2fda
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/ivytown/uncore-interconnect.json
@@ -0,0 +1,46 @@
+[
+ {
+ "BriefDescription": "QPI clock ticks. Use to get percentages for QPI cycles events. Derived from unc_q_clockticks",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x14",
+ "EventName": "UNC_Q_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "QPI LL"
+ },
+ {
+ "BriefDescription": "Cycles where receiving QPI link is in half-width mode. Derived from unc_q_rxl0p_power_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x10",
+ "EventName": "UNC_Q_RxL0P_POWER_CYCLES",
+ "MetricExpr": "(UNC_Q_RxL0P_POWER_CYCLES / UNC_Q_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "QPI LL"
+ },
+ {
+ "BriefDescription": "Cycles where transmitting QPI link is in half-width mode. Derived from unc_q_txl0p_power_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xd",
+ "EventName": "UNC_Q_TxL0P_POWER_CYCLES",
+ "MetricExpr": "(UNC_Q_TxL0P_POWER_CYCLES / UNC_Q_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "QPI LL"
+ },
+ {
+ "BriefDescription": "Number of data flits transmitted . Derived from unc_q_txl_flits_g0.data",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_Q_TxL_FLITS_G0.DATA",
+ "PerPkg": "1",
+ "ScaleUnit": "8Bytes",
+ "UMask": "0x2",
+ "Unit": "QPI LL"
+ },
+ {
+ "BriefDescription": "Number of non data (control) flits transmitted . Derived from unc_q_txl_flits_g0.non_data",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_Q_TxL_FLITS_G0.NON_DATA",
+ "PerPkg": "1",
+ "ScaleUnit": "8Bytes",
+ "UMask": "0x4",
+ "Unit": "QPI LL"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/ivytown/uncore-memory.json b/tools/perf/pmu-events/arch/x86/ivytown/uncore-memory.json
new file mode 100644
index 0000000..ac4ad4d
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/ivytown/uncore-memory.json
@@ -0,0 +1,75 @@
+[
+ {
+ "BriefDescription": "Memory page activates for reads and writes. Derived from unc_m_act_count.rd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_M_ACT_COUNT.RD",
+ "PerPkg": "1",
+ "UMask": "0x1",
+ "Umask": "0x3",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Read requests to memory controller. Derived from unc_m_cas_count.rd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x4",
+ "EventName": "UNC_M_CAS_COUNT.RD",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Write requests to memory controller. Derived from unc_m_cas_count.wr",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x4",
+ "EventName": "UNC_M_CAS_COUNT.WR",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0xC",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Memory controller clock ticks. Use to generate percentages for memory controller CYCLES events. Derived from unc_m_clockticks",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_M_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Cycles where DRAM ranks are in power down (CKE) mode. Derived from unc_m_power_channel_ppd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x85",
+ "EventName": "UNC_M_POWER_CHANNEL_PPD",
+ "MetricExpr": "(UNC_M_POWER_CHANNEL_PPD / UNC_M_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Cycles all ranks are in critical thermal throttle. Derived from unc_m_power_critical_throttle_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x86",
+ "EventName": "UNC_M_POWER_CRITICAL_THROTTLE_CYCLES",
+ "MetricExpr": "(UNC_M_POWER_CRITICAL_THROTTLE_CYCLES / UNC_M_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Cycles Memory is in self refresh power mode. Derived from unc_m_power_self_refresh",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x43",
+ "EventName": "UNC_M_POWER_SELF_REFRESH",
+ "MetricExpr": "(UNC_M_POWER_SELF_REFRESH / UNC_M_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Memory page conflicts. Derived from unc_m_pre_count.page_miss",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x2",
+ "EventName": "UNC_M_PRE_COUNT.PAGE_MISS",
+ "PerPkg": "1",
+ "UMask": "0x1",
+ "Unit": "iMC"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/ivytown/uncore-power.json b/tools/perf/pmu-events/arch/x86/ivytown/uncore-power.json
new file mode 100644
index 0000000..dc2586d
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/ivytown/uncore-power.json
@@ -0,0 +1,249 @@
+[
+ {
+ "BriefDescription": "PCU clock ticks. Use to get percentages of PCU cycles events. Derived from unc_p_clockticks",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_P_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to the frequency that is configured in the filter. (filter_band0=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band0_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xb",
+ "EventName": "UNC_P_FREQ_BAND0_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_BAND0_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to the frequency that is configured in the filter. (filter_band1=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band1_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xc",
+ "EventName": "UNC_P_FREQ_BAND1_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_BAND1_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to the frequency that is configured in the filter. (filter_band2=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band2_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xd",
+ "EventName": "UNC_P_FREQ_BAND2_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_BAND2_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to the frequency that is configured in the filter. (filter_band3=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band3_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xe",
+ "EventName": "UNC_P_FREQ_BAND3_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_BAND3_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of times that the uncore transitioned a frequency greater than or equal to the frequency that is configured in the filter. (filter_band0=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band0_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xb",
+ "EventName": "UNC_P_FREQ_BAND0_TRANSITIONS",
+ "Filter": "edge=1",
+ "MetricExpr": "(UNC_P_FREQ_BAND0_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of times that the uncore transitioned to a frequency greater than or equal to the frequency that is configured in the filter. (filter_band1=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band1_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xc",
+ "EventName": "UNC_P_FREQ_BAND1_TRANSITIONS",
+ "Filter": "edge=1",
+ "MetricExpr": "(UNC_P_FREQ_BAND1_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore transitioned to a frequency greater than or equal to the frequency that is configured in the filter. (filter_band2=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band2_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xd",
+ "EventName": "UNC_P_FREQ_BAND2_TRANSITIONS",
+ "Filter": "edge=1",
+ "MetricExpr": "(UNC_P_FREQ_BAND2_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore transitioned to a frequency greater than or equal to the frequency that is configured in the filter. (filter_band3=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band3_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xe",
+ "EventName": "UNC_P_FREQ_BAND3_TRANSITIONS",
+ "Filter": "edge=1",
+ "MetricExpr": "(UNC_P_FREQ_BAND3_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "This is an occupancy event that tracks the number of cores that are in the chosen C-State. It can be used by itself to get the average number of cores in that C-state with threshholding to generate histograms, or with other PCU events and occupancy triggering to capture other details. Derived from unc_p_power_state_occupancy.cores_c0",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x80",
+ "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C0",
+ "Filter": "occ_sel=1",
+ "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C0 / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "This is an occupancy event that tracks the number of cores that are in the chosen C-State. It can be used by itself to get the average number of cores in that C-state with threshholding to generate histograms, or with other PCU events and occupancy triggering to capture other details. Derived from unc_p_power_state_occupancy.cores_c3",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x80",
+ "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C3",
+ "Filter": "occ_sel=2",
+ "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C3 / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "This is an occupancy event that tracks the number of cores that are in the chosen C-State. It can be used by itself to get the average number of cores in that C-state with threshholding to generate histograms, or with other PCU events and occupancy triggering to capture other details. Derived from unc_p_power_state_occupancy.cores_c6",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x80",
+ "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C6",
+ "Filter": "occ_sel=3",
+ "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C6 / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that we are in external PROCHOT mode. This mode is triggered when a sensor off the die determines that something off-die (like DRAM) is too hot and must throttle to avoid damaging the chip. Derived from unc_p_prochot_external_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xa",
+ "EventName": "UNC_P_PROCHOT_EXTERNAL_CYCLES",
+ "MetricExpr": "(UNC_P_PROCHOT_EXTERNAL_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles when thermal conditions are the upper limit on frequency. This is related to the THERMAL_THROTTLE CYCLES_ABOVE_TEMP event, which always counts cycles when we are above the thermal temperature. This event (STRONGEST_UPPER_LIMIT) is sampled at the output of the algorithm that determines the actual frequency, while THERMAL_THROTTLE looks at the input. Derived from unc_p_freq_max_limit_thermal_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x4",
+ "EventName": "UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles when the OS is the upper limit on frequency. Derived from unc_p_freq_max_os_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x6",
+ "EventName": "UNC_P_FREQ_MAX_OS_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_MAX_OS_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles when power is the upper limit on frequency. Derived from unc_p_freq_max_power_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x5",
+ "EventName": "UNC_P_FREQ_MAX_POWER_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_MAX_POWER_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles when current is the upper limit on frequency. Derived from unc_p_freq_max_current_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x7",
+ "EventName": "UNC_P_FREQ_MAX_CURRENT_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_MAX_CURRENT_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles when the system is changing frequency. This can not be filtered by thread ID. One can also use it with the occupancy counter that monitors number of threads in C0 to estimate the performance impact that frequency transitions had on the system. Derived from unc_p_freq_trans_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x60",
+ "EventName": "UNC_P_FREQ_TRANS_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_TRANS_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to 1.2Ghz. Derived from unc_p_freq_band0_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xb",
+ "EventName": "UNC_P_FREQ_GE_1200MHZ_CYCLES",
+ "Filter": "filter_band0=1200",
+ "MetricExpr": "(UNC_P_FREQ_GE_1200MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to 2Ghz. Derived from unc_p_freq_band1_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xc",
+ "EventName": "UNC_P_FREQ_GE_2000MHZ_CYCLES",
+ "Filter": "filter_band1=2000",
+ "MetricExpr": "(UNC_P_FREQ_GE_2000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to 3Ghz. Derived from unc_p_freq_band2_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xd",
+ "EventName": "UNC_P_FREQ_GE_3000MHZ_CYCLES",
+ "Filter": "filter_band2=3000",
+ "MetricExpr": "(UNC_P_FREQ_GE_3000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to 4Ghz. Derived from unc_p_freq_band3_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xe",
+ "EventName": "UNC_P_FREQ_GE_4000MHZ_CYCLES",
+ "Filter": "filter_band3=4000",
+ "MetricExpr": "(UNC_P_FREQ_GE_4000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of times that the uncore transitioned to a frequency greater than or equal to 1.2Ghz. Derived from unc_p_freq_band0_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xb",
+ "EventName": "UNC_P_FREQ_GE_1200MHZ_TRANSITIONS",
+ "Filter": "edge=1,filter_band0=1200",
+ "MetricExpr": "(UNC_P_FREQ_GE_1200MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of times that the uncore transitioned to a frequency greater than or equal to 2Ghz. Derived from unc_p_freq_band1_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xc",
+ "EventName": "UNC_P_FREQ_GE_2000MHZ_TRANSITIONS",
+ "Filter": "edge=1,filter_band1=2000",
+ "MetricExpr": "(UNC_P_FREQ_GE_2000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore transitioned to a frequency greater than or equal to 3Ghz. Derived from unc_p_freq_band2_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xd",
+ "EventName": "UNC_P_FREQ_GE_3000MHZ_TRANSITIONS",
+ "Filter": "edge=1,filter_band2=4000",
+ "MetricExpr": "(UNC_P_FREQ_GE_3000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore transitioned to a frequency greater than or equal to 4Ghz. Derived from unc_p_freq_band3_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xe",
+ "EventName": "UNC_P_FREQ_GE_4000MHZ_TRANSITIONS",
+ "Filter": "edge=1,filter_band3=4000",
+ "MetricExpr": "(UNC_P_FREQ_GE_4000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/jaketown/uncore-cache.json b/tools/perf/pmu-events/arch/x86/jaketown/uncore-cache.json
new file mode 100644
index 0000000..2f23cf0
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/jaketown/uncore-cache.json
@@ -0,0 +1,209 @@
+[
+ {
+ "BriefDescription": "Uncore cache clock ticks. Derived from unc_c_clockticks",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_C_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "All LLC Misses (code+ data rd + data wr - including demand and prefetch). Derived from unc_c_llc_lookup.any",
+ "Counter": "0,1",
+ "EventCode": "0x34",
+ "EventName": "UNC_C_LLC_LOOKUP.ANY",
+ "Filter": "filter_state=0x1",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x11",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "M line evictions from LLC (writebacks to memory). Derived from unc_c_llc_victims.m_state",
+ "Counter": "0,1",
+ "EventCode": "0x37",
+ "EventName": "UNC_C_LLC_VICTIMS.M_STATE",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses - demand and prefetch data reads - excludes LLC prefetches. Derived from unc_c_tor_inserts.miss_opcode.demand",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.DATA_READ",
+ "Filter": "filter_opc=0x182",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses - Uncacheable reads. Derived from unc_c_tor_inserts.miss_opcode.uncacheable",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.UNCACHEABLE",
+ "Filter": "filter_opc=0x187",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe allocating writes that miss LLC - DDIO misses. Derived from unc_c_tor_inserts.miss_opcode.ddio_miss",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.PCIE_WRITE",
+ "Filter": "filter_opc=0x19c",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "LLC misses for ItoM writes (as part of fast string memcpy stores). Derived from unc_c_tor_inserts.miss_opcode.itom_write",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.ITOM_WRITE",
+ "Filter": "filter_opc=0x1c8",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Streaming stores (full cache line). Derived from unc_c_tor_inserts.opcode.streaming_full",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.STREAMING_FULL",
+ "Filter": "filter_opc=0x18c",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Streaming stores (partial cache line). Derived from unc_c_tor_inserts.opcode.streaming_partial",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.STREAMING_PARTIAL",
+ "Filter": "filter_opc=0x18d",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Partial PCIe reads. Derived from unc_c_tor_inserts.opcode.pcie_partial",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_PARTIAL_READ",
+ "Filter": "filter_opc=0x195",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe allocating writes that hit in LLC (DDIO hits). Derived from unc_c_tor_inserts.opcode.ddio_hit",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_WRITE",
+ "Filter": "filter_opc=0x19c",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe read current. Derived from unc_c_tor_inserts.opcode.pcie_read_current",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_READ",
+ "Filter": "filter_opc=0x19e",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "ItoM write hits (as part of fast string memcpy stores). Derived from unc_c_tor_inserts.opcode.itom_write_hit",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.ITOM_WRITE",
+ "Filter": "filter_opc=0x1c8",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe non-snoop reads. Derived from unc_c_tor_inserts.opcode.pcie_read",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_NS_READ",
+ "Filter": "filter_opc=0x1e4",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe non-snoop writes (partial). Derived from unc_c_tor_inserts.opcode.pcie_partial_write",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_NS_PARTIAL_WRITE",
+ "Filter": "filter_opc=0x1e5",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "PCIe non-snoop writes (full line). Derived from unc_c_tor_inserts.opcode.pcie_full_write",
+ "Counter": "0,1",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.PCIE_NS_WRITE",
+ "Filter": "filter_opc=0x1e6",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x1",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Occupancy counter for all LLC misses; we divide this by UNC_C_CLOCKTICKS to get average Q depth. Derived from unc_c_tor_occupancy.miss_all",
+ "EventCode": "0x36",
+ "EventName": "UNC_C_TOR_OCCUPANCY.MISS_ALL",
+ "Filter": "filter_opc=0x182",
+ "MetricExpr": "(UNC_C_TOR_OCCUPANCY.MISS_ALL / UNC_C_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "UMask": "0xa",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "Occupancy counter for LLC data reads (demand and L2 prefetch). Derived from unc_c_tor_occupancy.miss_opcode.llc_data_read",
+ "EventCode": "0x36",
+ "EventName": "UNC_C_TOR_OCCUPANCY.LLC_DATA_READ",
+ "PerPkg": "1",
+ "UMask": "0x3",
+ "Unit": "CBO"
+ },
+ {
+ "BriefDescription": "read requests to home agent. Derived from unc_h_requests.reads",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.READS",
+ "PerPkg": "1",
+ "UMask": "0x3",
+ "Unit": "HA"
+ },
+ {
+ "BriefDescription": "write requests to home agent. Derived from unc_h_requests.writes",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_H_REQUESTS.WRITES",
+ "PerPkg": "1",
+ "UMask": "0xc",
+ "Unit": "HA"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/jaketown/uncore-interconnect.json b/tools/perf/pmu-events/arch/x86/jaketown/uncore-interconnect.json
new file mode 100644
index 0000000..6335187
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/jaketown/uncore-interconnect.json
@@ -0,0 +1,46 @@
+[
+ {
+ "BriefDescription": "QPI clock ticks. Used to get percentages of QPI cycles events. Derived from unc_q_clockticks",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x14",
+ "EventName": "UNC_Q_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "QPI LL"
+ },
+ {
+ "BriefDescription": "Cycles where receiving QPI link is in half-width mode. Derived from unc_q_rxl0p_power_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x10",
+ "EventName": "UNC_Q_RxL0P_POWER_CYCLES",
+ "MetricExpr": "(UNC_Q_RxL0P_POWER_CYCLES / UNC_Q_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "QPI LL"
+ },
+ {
+ "BriefDescription": "Cycles where transmitting QPI link is in half-width mode. Derived from unc_q_txl0p_power_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xd",
+ "EventName": "UNC_Q_TxL0P_POWER_CYCLES",
+ "MetricExpr": "(UNC_Q_TxL0P_POWER_CYCLES / UNC_Q_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "QPI LL"
+ },
+ {
+ "BriefDescription": "Number of data flits transmitted . Derived from unc_q_txl_flits_g0.data",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_Q_TxL_FLITS_G0.DATA",
+ "PerPkg": "1",
+ "ScaleUnit": "8Bytes",
+ "UMask": "0x2",
+ "Unit": "QPI LL"
+ },
+ {
+ "BriefDescription": "Number of non data (control) flits transmitted . Derived from unc_q_txl_flits_g0.non_data",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_Q_TxL_FLITS_G0.NON_DATA",
+ "PerPkg": "1",
+ "ScaleUnit": "8Bytes",
+ "UMask": "0x4",
+ "Unit": "QPI LL"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/jaketown/uncore-memory.json b/tools/perf/pmu-events/arch/x86/jaketown/uncore-memory.json
new file mode 100644
index 0000000..e2cf6da
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/jaketown/uncore-memory.json
@@ -0,0 +1,79 @@
+[
+ {
+ "BriefDescription": "Memory page activates. Derived from unc_m_act_count",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x1",
+ "EventName": "UNC_M_ACT_COUNT",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "read requests to memory controller. Derived from unc_m_cas_count.rd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x4",
+ "EventName": "UNC_M_CAS_COUNT.RD",
+ "PerPkg": "1",
+ "UMask": "0x3",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "write requests to memory controller. Derived from unc_m_cas_count.wr",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x4",
+ "EventName": "UNC_M_CAS_COUNT.WR",
+ "PerPkg": "1",
+ "UMask": "0xc",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Memory controller clock ticks. Used to get percentages of memory controller cycles events. Derived from unc_m_clockticks",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_M_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Cycles where DRAM ranks are in power down (CKE) mode. Derived from unc_m_power_channel_ppd",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x85",
+ "EventName": "UNC_M_POWER_CHANNEL_PPD",
+ "MetricExpr": "(UNC_M_POWER_CHANNEL_PPD / UNC_M_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Cycles all ranks are in critical thermal throttle. Derived from unc_m_power_critical_throttle_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x86",
+ "EventName": "UNC_M_POWER_CRITICAL_THROTTLE_CYCLES",
+ "MetricExpr": "(UNC_M_POWER_CRITICAL_THROTTLE_CYCLES / UNC_M_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Cycles Memory is in self refresh power mode. Derived from unc_m_power_self_refresh",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x43",
+ "EventName": "UNC_M_POWER_SELF_REFRESH",
+ "MetricExpr": "(UNC_M_POWER_SELF_REFRESH / UNC_M_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Memory page conflicts. Derived from unc_m_pre_count.page_miss",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x2",
+ "EventName": "UNC_M_PRE_COUNT.PAGE_MISS",
+ "PerPkg": "1",
+ "UMask": "0x1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Occupancy counter for memory read queue. Derived from unc_m_rpq_occupancy",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x80",
+ "EventName": "UNC_M_RPQ_OCCUPANCY",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/jaketown/uncore-power.json b/tools/perf/pmu-events/arch/x86/jaketown/uncore-power.json
new file mode 100644
index 0000000..bbe36d5
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/jaketown/uncore-power.json
@@ -0,0 +1,248 @@
+[
+ {
+ "BriefDescription": "PCU clock ticks. Use to get percentages of PCU cycles events. Derived from unc_p_clockticks",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_P_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to the frequency that is configured in the filter. (filter_band0=XXX with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band0_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xb",
+ "EventName": "UNC_P_FREQ_BAND0_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_BAND0_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to the frequency that is configured in the filter. (filter_band1=XXX with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band1_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xc",
+ "EventName": "UNC_P_FREQ_BAND1_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_BAND1_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to the frequency that is configured in the filter. (filter_band2=XXX with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band2_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xd",
+ "EventName": "UNC_P_FREQ_BAND2_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_BAND2_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to the frequency that is configured in the filter. (filter_band3=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band3_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xe",
+ "EventName": "UNC_P_FREQ_BAND3_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_BAND3_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of times that the uncore transitioned a frequency greater than or equal to the frequency that is configured in the filter. (filter_band0=XXX with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band0_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xb",
+ "EventName": "UNC_P_FREQ_BAND0_TRANSITIONS",
+ "Filter": "edge=1",
+ "MetricExpr": "(UNC_P_FREQ_BAND0_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of times that the uncore transistioned to a frequency greater than or equal to the frequency that is configured in the filter. (filter_band1=XXX with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band1_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xc",
+ "EventName": "UNC_P_FREQ_BAND1_TRANSITIONS",
+ "Filter": "edge=1",
+ "MetricExpr": "(UNC_P_FREQ_BAND1_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore transitioned to a frequency greater than or equal to the frequency that is configured in the filter. (filter_band2=XXX with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band2_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xd",
+ "EventName": "UNC_P_FREQ_BAND2_TRANSITIONS",
+ "Filter": "edge=1",
+ "MetricExpr": "(UNC_P_FREQ_BAND2_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore transitioned to a frequency greater than or equal to the frequency that is configured in the filter. (filter_band3=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band3_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xe",
+ "EventName": "UNC_P_FREQ_BAND3_TRANSITIONS",
+ "Filter": "edge=1",
+ "MetricExpr": "(UNC_P_FREQ_BAND3_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "This is an occupancy event that tracks the number of cores that are in C0. It can be used by itself to get the average number of cores in C0, with threshholding to generate histograms, or with other PCU events and occupancy triggering to capture other details. Derived from unc_p_power_state_occupancy.cores_c0",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x80",
+ "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C0",
+ "Filter": "occ_sel=1",
+ "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C0 / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "This is an occupancy event that tracks the number of cores that are in C3. It can be used by itself to get the average number of cores in C0, with threshholding to generate histograms, or with other PCU events and occupancy triggering to capture other details. Derived from unc_p_power_state_occupancy.cores_c3",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x80",
+ "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C3",
+ "Filter": "occ_sel=2",
+ "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C3 / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "This is an occupancy event that tracks the number of cores that are in C6. It can be used by itself to get the average number of cores in C0, with threshholding to generate histograms, or with other PCU events . Derived from unc_p_power_state_occupancy.cores_c6",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x80",
+ "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C6",
+ "Filter": "occ_sel=3",
+ "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C6 / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that we are in external PROCHOT mode. This mode is triggered when a sensor off the die determines that something off-die (like DRAM) is too hot and must throttle to avoid damaging the chip. Derived from unc_p_prochot_external_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xa",
+ "EventName": "UNC_P_PROCHOT_EXTERNAL_CYCLES",
+ "MetricExpr": "(UNC_P_PROCHOT_EXTERNAL_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles when temperature is the upper limit on frequency. Derived from unc_p_freq_max_limit_thermal_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x4",
+ "EventName": "UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles when the OS is the upper limit on frequency. Derived from unc_p_freq_max_os_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x6",
+ "EventName": "UNC_P_FREQ_MAX_OS_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_MAX_OS_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles when power is the upper limit on frequency. Derived from unc_p_freq_max_power_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x5",
+ "EventName": "UNC_P_FREQ_MAX_POWER_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_MAX_POWER_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles when current is the upper limit on frequency. Derived from unc_p_freq_max_current_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x7",
+ "EventName": "UNC_P_FREQ_MAX_CURRENT_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_MAX_CURRENT_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Cycles spent changing Frequency. Derived from unc_p_freq_trans_cycles",
+ "Counter": "0,1,2,3",
+ "EventName": "UNC_P_FREQ_TRANS_CYCLES",
+ "MetricExpr": "(UNC_P_FREQ_TRANS_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to 1.2Ghz. Derived from unc_p_freq_band0_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xb",
+ "EventName": "UNC_P_FREQ_GE_1200MHZ_CYCLES",
+ "Filter": "filter_band0=1200",
+ "MetricExpr": "(UNC_P_FREQ_GE_1200MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to 2Ghz. Derived from unc_p_freq_band1_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xc",
+ "EventName": "UNC_P_FREQ_GE_2000MHZ_CYCLES",
+ "Filter": "filter_band1=2000",
+ "MetricExpr": "(UNC_P_FREQ_GE_2000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to 3Ghz. Derived from unc_p_freq_band2_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xd",
+ "EventName": "UNC_P_FREQ_GE_3000MHZ_CYCLES",
+ "Filter": "filter_band2=3000",
+ "MetricExpr": "(UNC_P_FREQ_GE_3000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to 4Ghz. Derived from unc_p_freq_band3_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xe",
+ "EventName": "UNC_P_FREQ_GE_4000MHZ_CYCLES",
+ "Filter": "filter_band3=4000",
+ "MetricExpr": "(UNC_P_FREQ_GE_4000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of times that the uncore transitioned to a frequency greater than or equal to 1.2Ghz. Derived from unc_p_freq_band0_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xb",
+ "EventName": "UNC_P_FREQ_GE_1200MHZ_TRANSITIONS",
+ "Filter": "edge=1,filter_band0=1200",
+ "MetricExpr": "(UNC_P_FREQ_GE_1200MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of times that the uncore transitioned to a frequency greater than or equal to 2Ghz. Derived from unc_p_freq_band1_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xc",
+ "EventName": "UNC_P_FREQ_GE_2000MHZ_TRANSITIONS",
+ "Filter": "edge=1,filter_band1=2000",
+ "MetricExpr": "(UNC_P_FREQ_GE_2000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore transitioned to a frequency greater than or equal to 3Ghz. Derived from unc_p_freq_band2_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xd",
+ "EventName": "UNC_P_FREQ_GE_3000MHZ_TRANSITIONS",
+ "Filter": "edge=1,filter_band2=4000",
+ "MetricExpr": "(UNC_P_FREQ_GE_3000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ },
+ {
+ "BriefDescription": "Counts the number of cycles that the uncore transitioned to a frequency greater than or equal to 4Ghz. Derived from unc_p_freq_band3_cycles",
+ "Counter": "0,1,2,3",
+ "EventCode": "0xe",
+ "EventName": "UNC_P_FREQ_GE_4000MHZ_TRANSITIONS",
+ "Filter": "edge=1,filter_band3=4000",
+ "MetricExpr": "(UNC_P_FREQ_GE_4000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.",
+ "PerPkg": "1",
+ "Unit": "PCU"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/knightslanding/uncore-memory.json b/tools/perf/pmu-events/arch/x86/knightslanding/uncore-memory.json
new file mode 100644
index 0000000..e3bcd86
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/knightslanding/uncore-memory.json
@@ -0,0 +1,42 @@
+[
+ {
+ "BriefDescription": "ddr bandwidth read (CPU traffic only) (MB/sec). ",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x03",
+ "EventName": "UNC_M_CAS_COUNT.RD",
+ "PerPkg": "1",
+ "ScaleUnit": "6.4e-05MiB",
+ "UMask": "0x01",
+ "Unit": "imc"
+ },
+ {
+ "BriefDescription": "ddr bandwidth write (CPU traffic only) (MB/sec). ",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x03",
+ "EventName": "UNC_M_CAS_COUNT.WR",
+ "PerPkg": "1",
+ "ScaleUnit": "6.4e-05MiB",
+ "UMask": "0x02",
+ "Unit": "imc"
+ },
+ {
+ "BriefDescription": "mcdram bandwidth read (CPU traffic only) (MB/sec). ",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x01",
+ "EventName": "UNC_E_RPQ_INSERTS",
+ "PerPkg": "1",
+ "ScaleUnit": "6.4e-05MiB",
+ "UMask": "0x01",
+ "Unit": "edc_eclk"
+ },
+ {
+ "BriefDescription": "mcdram bandwidth write (CPU traffic only) (MB/sec). ",
+ "Counter": "0,1,2,3",
+ "EventCode": "0x02",
+ "EventName": "UNC_E_WPQ_INSERTS",
+ "PerPkg": "1",
+ "ScaleUnit": "6.4e-05MiB",
+ "UMask": "0x01",
+ "Unit": "edc_eclk"
+ }
+]
diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c
index 41611d7..eed0934 100644
--- a/tools/perf/pmu-events/jevents.c
+++ b/tools/perf/pmu-events/jevents.c
@@ -135,7 +135,6 @@
const char *field;
const char *kernel;
} fields[] = {
- { "EventCode", "event=" },
{ "UMask", "umask=" },
{ "CounterMask", "cmask=" },
{ "Invert", "inv=" },
@@ -189,6 +188,27 @@
return NULL;
}
+static struct map {
+ const char *json;
+ const char *perf;
+} unit_to_pmu[] = {
+ { "CBO", "uncore_cbox" },
+ { "QPI LL", "uncore_qpi" },
+ { "SBO", "uncore_sbox" },
+ {}
+};
+
+static const char *field_to_perf(struct map *table, char *map, jsmntok_t *val)
+{
+ int i;
+
+ for (i = 0; table[i].json; i++) {
+ if (json_streq(map, val, table[i].json))
+ return table[i].perf;
+ }
+ return NULL;
+}
+
#define EXPECT(e, t, m) do { if (!(e)) { \
jsmntok_t *loc = (t); \
if (!(t)->start && (t) > tokens) \
@@ -270,7 +290,8 @@
}
static int print_events_table_entry(void *data, char *name, char *event,
- char *desc, char *long_desc)
+ char *desc, char *long_desc,
+ char *pmu, char *unit, char *perpkg)
{
struct perf_entry_data *pd = data;
FILE *outfp = pd->outfp;
@@ -288,7 +309,12 @@
fprintf(outfp, "\t.topic = \"%s\",\n", topic);
if (long_desc && long_desc[0])
fprintf(outfp, "\t.long_desc = \"%s\",\n", long_desc);
-
+ if (pmu)
+ fprintf(outfp, "\t.pmu = \"%s\",\n", pmu);
+ if (unit)
+ fprintf(outfp, "\t.unit = \"%s\",\n", unit);
+ if (perpkg)
+ fprintf(outfp, "\t.perpkg = \"%s\",\n", perpkg);
fprintf(outfp, "},\n");
return 0;
@@ -335,7 +361,8 @@
/* Call func with each event in the json file */
int json_events(const char *fn,
int (*func)(void *data, char *name, char *event, char *desc,
- char *long_desc),
+ char *long_desc,
+ char *pmu, char *unit, char *perpkg),
void *data)
{
int err = -EIO;
@@ -343,6 +370,7 @@
jsmntok_t *tokens, *tok;
int i, j, len;
char *map;
+ char buf[128];
if (!fn)
return -ENOENT;
@@ -356,6 +384,11 @@
char *event = NULL, *desc = NULL, *name = NULL;
char *long_desc = NULL;
char *extra_desc = NULL;
+ char *pmu = NULL;
+ char *filter = NULL;
+ char *perpkg = NULL;
+ char *unit = NULL;
+ unsigned long long eventcode = 0;
struct msrmap *msr = NULL;
jsmntok_t *msrval = NULL;
jsmntok_t *precise = NULL;
@@ -376,6 +409,16 @@
nz = !json_streq(map, val, "0");
if (match_field(map, field, nz, &event, val)) {
/* ok */
+ } else if (json_streq(map, field, "EventCode")) {
+ char *code = NULL;
+ addfield(map, &code, "", "", val);
+ eventcode |= strtoul(code, NULL, 0);
+ free(code);
+ } else if (json_streq(map, field, "ExtSel")) {
+ char *code = NULL;
+ addfield(map, &code, "", "", val);
+ eventcode |= strtoul(code, NULL, 0) << 21;
+ free(code);
} else if (json_streq(map, field, "EventName")) {
addfield(map, &name, "", "", val);
} else if (json_streq(map, field, "BriefDescription")) {
@@ -399,6 +442,28 @@
addfield(map, &extra_desc, ". ",
" Supports address when precise",
NULL);
+ } else if (json_streq(map, field, "Unit")) {
+ const char *ppmu;
+ char *s;
+
+ ppmu = field_to_perf(unit_to_pmu, map, val);
+ if (ppmu) {
+ pmu = strdup(ppmu);
+ } else {
+ if (!pmu)
+ pmu = strdup("uncore_");
+ addfield(map, &pmu, "", "", val);
+ for (s = pmu; *s; s++)
+ *s = tolower(*s);
+ }
+ addfield(map, &desc, ". ", "Unit: ", NULL);
+ addfield(map, &desc, "", pmu, NULL);
+ } else if (json_streq(map, field, "Filter")) {
+ addfield(map, &filter, "", "", val);
+ } else if (json_streq(map, field, "ScaleUnit")) {
+ addfield(map, &unit, "", "", val);
+ } else if (json_streq(map, field, "PerPkg")) {
+ addfield(map, &perpkg, "", "", val);
}
/* ignore unknown fields */
}
@@ -410,20 +475,29 @@
addfield(map, &extra_desc, " ",
"(Precise event)", NULL);
}
+ snprintf(buf, sizeof buf, "event=%#llx", eventcode);
+ addfield(map, &event, ",", buf, NULL);
if (desc && extra_desc)
addfield(map, &desc, " ", extra_desc, NULL);
if (long_desc && extra_desc)
addfield(map, &long_desc, " ", extra_desc, NULL);
+ if (filter)
+ addfield(map, &event, ",", filter, NULL);
if (msr != NULL)
addfield(map, &event, ",", msr->pname, msrval);
fixname(name);
- err = func(data, name, real_event(name, event), desc, long_desc);
+ err = func(data, name, real_event(name, event), desc, long_desc,
+ pmu, unit, perpkg);
free(event);
free(desc);
free(name);
free(long_desc);
free(extra_desc);
+ free(pmu);
+ free(filter);
+ free(perpkg);
+ free(unit);
if (err)
break;
tok += j;
diff --git a/tools/perf/pmu-events/jevents.h b/tools/perf/pmu-events/jevents.h
index b0eb274..71e13de 100644
--- a/tools/perf/pmu-events/jevents.h
+++ b/tools/perf/pmu-events/jevents.h
@@ -3,7 +3,9 @@
int json_events(const char *fn,
int (*func)(void *data, char *name, char *event, char *desc,
- char *long_desc),
+ char *long_desc,
+ char *pmu,
+ char *unit, char *perpkg),
void *data);
char *get_cpu_str(void);
diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h
index 2eaef59..c669a3c 100644
--- a/tools/perf/pmu-events/pmu-events.h
+++ b/tools/perf/pmu-events/pmu-events.h
@@ -10,6 +10,9 @@
const char *desc;
const char *topic;
const char *long_desc;
+ const char *pmu;
+ const char *unit;
+ const char *perpkg;
};
/*
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 6676c2d..1cb3d9b 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -44,6 +44,7 @@
perf-y += bitmap.o
perf-y += perf-hooks.o
perf-y += clang.o
+perf-y += unit_number__scnprintf.o
$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
$(call rule_mkdir)
diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
index 92343f4..1a04fe7 100644
--- a/tools/perf/tests/bpf.c
+++ b/tools/perf/tests/bpf.c
@@ -5,11 +5,13 @@
#include <util/evlist.h>
#include <linux/bpf.h>
#include <linux/filter.h>
+#include <api/fs/fs.h>
#include <bpf/bpf.h>
#include "tests.h"
#include "llvm.h"
#include "debug.h"
#define NR_ITERS 111
+#define PERF_TEST_BPF_PATH "/sys/fs/bpf/perf_test"
#ifdef HAVE_LIBBPF_SUPPORT
@@ -54,6 +56,7 @@
const char *msg_load_fail;
int (*target_func)(void);
int expect_result;
+ bool pin;
} bpf_testcase_table[] = {
{
LLVM_TESTCASE_BASE,
@@ -63,6 +66,17 @@
"load bpf object failed",
&epoll_wait_loop,
(NR_ITERS + 1) / 2,
+ false,
+ },
+ {
+ LLVM_TESTCASE_BASE,
+ "BPF pinning",
+ "[bpf_pinning]",
+ "fix kbuild first",
+ "check your vmlinux setting?",
+ &epoll_wait_loop,
+ (NR_ITERS + 1) / 2,
+ true,
},
#ifdef HAVE_BPF_PROLOGUE
{
@@ -73,6 +87,7 @@
"check your vmlinux setting?",
&llseek_loop,
(NR_ITERS + 1) / 4,
+ false,
},
#endif
{
@@ -83,6 +98,7 @@
"libbpf error when dealing with relocation",
NULL,
0,
+ false,
},
};
@@ -226,10 +242,34 @@
goto out;
}
- if (obj)
+ if (obj) {
ret = do_test(obj,
bpf_testcase_table[idx].target_func,
bpf_testcase_table[idx].expect_result);
+ if (ret != TEST_OK)
+ goto out;
+ if (bpf_testcase_table[idx].pin) {
+ int err;
+
+ if (!bpf_fs__mount()) {
+ pr_debug("BPF filesystem not mounted\n");
+ ret = TEST_FAIL;
+ goto out;
+ }
+ err = mkdir(PERF_TEST_BPF_PATH, 0777);
+ if (err && errno != EEXIST) {
+ pr_debug("Failed to make perf_test dir: %s\n",
+ strerror(errno));
+ ret = TEST_FAIL;
+ goto out;
+ }
+ if (bpf_object__pin(obj, PERF_TEST_BPF_PATH))
+ ret = TEST_FAIL;
+ if (rm_rf(PERF_TEST_BPF_PATH))
+ ret = TEST_FAIL;
+ }
+ }
+
out:
bpf__clear();
return ret;
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index a77dcc0..37e326b 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -247,6 +247,10 @@
}
},
{
+ .desc = "unit_number__scnprintf",
+ .func = test__unit_number__scnprint,
+ },
+ {
.func = NULL,
},
};
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index 02a33eb..d357dab 100644
--- a/tools/perf/tests/llvm.c
+++ b/tools/perf/tests/llvm.c
@@ -13,7 +13,7 @@
struct bpf_object *obj;
obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, NULL);
- if (IS_ERR(obj))
+ if (libbpf_get_error(obj))
return TEST_FAIL;
bpf_object__close(obj);
return TEST_OK;
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c
index 20c2e64..aa9276b 100644
--- a/tools/perf/tests/parse-events.c
+++ b/tools/perf/tests/parse-events.c
@@ -1779,15 +1779,14 @@
}
while (!ret && (ent = readdir(dir))) {
-#define MAX_NAME 100
struct evlist_test e;
- char name[MAX_NAME];
+ char name[2 * NAME_MAX + 1 + 12 + 3];
/* Names containing . are special and cannot be used directly */
if (strchr(ent->d_name, '.'))
continue;
- snprintf(name, MAX_NAME, "cpu/event=%s/u", ent->d_name);
+ snprintf(name, sizeof(name), "cpu/event=%s/u", ent->d_name);
e.name = name;
e.check = test__checkevent_pmu_events;
@@ -1795,11 +1794,10 @@
ret = test_event(&e);
if (ret)
break;
- snprintf(name, MAX_NAME, "%s:u,cpu/event=%s/u", ent->d_name, ent->d_name);
+ snprintf(name, sizeof(name), "%s:u,cpu/event=%s/u", ent->d_name, ent->d_name);
e.name = name;
e.check = test__checkevent_pmu_events_mix;
ret = test_event(&e);
-#undef MAX_NAME
}
closedir(dir);
diff --git a/tools/perf/tests/parse-no-sample-id-all.c b/tools/perf/tests/parse-no-sample-id-all.c
index 81c6eea..65dcf48 100644
--- a/tools/perf/tests/parse-no-sample-id-all.c
+++ b/tools/perf/tests/parse-no-sample-id-all.c
@@ -50,7 +50,8 @@
}
struct test_attr_event {
- struct attr_event attr;
+ struct perf_event_header header;
+ struct perf_event_attr attr;
u64 id;
};
@@ -71,20 +72,16 @@
int err;
struct test_attr_event event1 = {
- .attr = {
- .header = {
- .type = PERF_RECORD_HEADER_ATTR,
- .size = sizeof(struct test_attr_event),
- },
+ .header = {
+ .type = PERF_RECORD_HEADER_ATTR,
+ .size = sizeof(struct test_attr_event),
},
.id = 1,
};
struct test_attr_event event2 = {
- .attr = {
- .header = {
- .type = PERF_RECORD_HEADER_ATTR,
- .size = sizeof(struct test_attr_event),
- },
+ .header = {
+ .type = PERF_RECORD_HEADER_ATTR,
+ .size = sizeof(struct test_attr_event),
},
.id = 2,
};
diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c
index 8f2e1de..541da7a 100644
--- a/tools/perf/tests/perf-record.c
+++ b/tools/perf/tests/perf-record.c
@@ -66,7 +66,7 @@
if (evlist == NULL) /* Fallback for kernels lacking PERF_COUNT_SW_DUMMY */
evlist = perf_evlist__new_default();
- if (evlist == NULL || argv == NULL) {
+ if (evlist == NULL) {
pr_debug("Not enough memory to create evlist\n");
goto out;
}
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index a512f0c..1fa9b9d 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -96,6 +96,7 @@
int test__clang(int subtest);
const char *test__clang_subtest_get_desc(int subtest);
int test__clang_subtest_get_nr(void);
+int test__unit_number__scnprint(int subtest);
#if defined(__arm__) || defined(__aarch64__)
#ifdef HAVE_DWARF_UNWIND_SUPPORT
diff --git a/tools/perf/tests/unit_number__scnprintf.c b/tools/perf/tests/unit_number__scnprintf.c
new file mode 100644
index 0000000..623c2aa
--- /dev/null
+++ b/tools/perf/tests/unit_number__scnprintf.c
@@ -0,0 +1,37 @@
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include "tests.h"
+#include "util.h"
+#include "debug.h"
+
+int test__unit_number__scnprint(int subtest __maybe_unused)
+{
+ struct {
+ u64 n;
+ const char *str;
+ } test[] = {
+ { 1, "1B" },
+ { 10*1024, "10K" },
+ { 20*1024*1024, "20M" },
+ { 30*1024*1024*1024ULL, "30G" },
+ { 0, "0B" },
+ { 0, NULL },
+ };
+ unsigned i = 0;
+
+ while (test[i].str) {
+ char buf[100];
+
+ unit_number__scnprintf(buf, sizeof(buf), test[i].n);
+
+ pr_debug("n %" PRIu64 ", str '%s', buf '%s'\n",
+ test[i].n, test[i].str, buf);
+
+ if (strcmp(test[i].str, buf))
+ return TEST_FAIL;
+
+ i++;
+ }
+
+ return TEST_OK;
+}
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 641b402..fc4fb66 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -501,8 +501,8 @@
return n;
}
-static void hist_entry__set_folding(struct hist_entry *he,
- struct hist_browser *hb, bool unfold)
+static void __hist_entry__set_folding(struct hist_entry *he,
+ struct hist_browser *hb, bool unfold)
{
hist_entry__init_have_children(he);
he->unfolded = unfold ? he->has_children : false;
@@ -520,12 +520,34 @@
he->nr_rows = 0;
}
+static void hist_entry__set_folding(struct hist_entry *he,
+ struct hist_browser *browser, bool unfold)
+{
+ double percent;
+
+ percent = hist_entry__get_percent_limit(he);
+ if (he->filtered || percent < browser->min_pcnt)
+ return;
+
+ __hist_entry__set_folding(he, browser, unfold);
+
+ if (!he->depth || unfold)
+ browser->nr_hierarchy_entries++;
+ if (he->leaf)
+ browser->nr_callchain_rows += he->nr_rows;
+ else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
+ browser->nr_hierarchy_entries++;
+ he->has_no_entry = true;
+ he->nr_rows = 1;
+ } else
+ he->has_no_entry = false;
+}
+
static void
__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
{
struct rb_node *nd;
struct hist_entry *he;
- double percent;
nd = rb_first(&browser->hists->entries);
while (nd) {
@@ -535,21 +557,6 @@
nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
hist_entry__set_folding(he, browser, unfold);
-
- percent = hist_entry__get_percent_limit(he);
- if (he->filtered || percent < browser->min_pcnt)
- continue;
-
- if (!he->depth || unfold)
- browser->nr_hierarchy_entries++;
- if (he->leaf)
- browser->nr_callchain_rows += he->nr_rows;
- else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
- browser->nr_hierarchy_entries++;
- he->has_no_entry = true;
- he->nr_rows = 1;
- } else
- he->has_no_entry = false;
}
}
@@ -564,6 +571,15 @@
ui_browser__reset_index(&browser->b);
}
+static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
+{
+ if (!browser->he_selection)
+ return;
+
+ hist_entry__set_folding(browser->he_selection, browser, unfold);
+ browser->b.nr_entries = hist_browser__nr_entries(browser);
+}
+
static void ui_browser__warn_lost_events(struct ui_browser *browser)
{
ui_browser__warning(browser, 4,
@@ -637,10 +653,18 @@
/* Collapse the whole world. */
hist_browser__set_folding(browser, false);
break;
+ case 'c':
+ /* Collapse the selected entry. */
+ hist_browser__set_folding_selected(browser, false);
+ break;
case 'E':
/* Expand the whole world. */
hist_browser__set_folding(browser, true);
break;
+ case 'e':
+ /* Expand the selected entry. */
+ hist_browser__set_folding_selected(browser, true);
+ break;
case 'H':
browser->show_headers = !browser->show_headers;
hist_browser__update_rows(browser);
diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c
index 1f6b099..50d13e58 100644
--- a/tools/perf/ui/setup.c
+++ b/tools/perf/ui/setup.c
@@ -7,6 +7,7 @@
pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
void *perf_gtk_handle;
+int use_browser = -1;
#ifdef HAVE_GTK2_SUPPORT
static int setup_gtk_browser(void)
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 3840e3a..5da376b 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -162,6 +162,7 @@
CFLAGS_libstring.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
CFLAGS_hweight.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
CFLAGS_parse-events.o += -Wno-redundant-decls
+CFLAGS_header.o += -include $(OUTPUT)PERF-VERSION-FILE
$(OUTPUT)util/kallsyms.o: ../lib/symbol/kallsyms.c FORCE
$(call rule_mkdir)
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 36c8611..bc6bc70 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -670,13 +670,13 @@
err = convert_perf_probe_events(pev, 1);
if (err < 0) {
- pr_debug("bpf_probe: failed to convert perf probe events");
+ pr_debug("bpf_probe: failed to convert perf probe events\n");
goto out;
}
err = apply_perf_probe_events(pev, 1);
if (err < 0) {
- pr_debug("bpf_probe: failed to apply perf probe events");
+ pr_debug("bpf_probe: failed to apply perf probe events\n");
goto out;
}
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 8b610dd..aba9534 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -48,6 +48,8 @@
callchain_param.mode = CHAIN_FOLDED;
return 0;
}
+
+ pr_err("Invalid callchain mode: %s\n", value);
return -1;
}
@@ -63,6 +65,8 @@
callchain_param.order_set = true;
return 0;
}
+
+ pr_err("Invalid callchain order: %s\n", value);
return -1;
}
@@ -80,6 +84,8 @@
callchain_param.branch_callstack = 1;
return 0;
}
+
+ pr_err("Invalid callchain sort key: %s\n", value);
return -1;
}
@@ -97,6 +103,8 @@
callchain_param.value = CCVAL_COUNT;
return 0;
}
+
+ pr_err("Invalid callchain config key: %s\n", value);
return -1;
}
@@ -210,13 +218,17 @@
return parse_callchain_sort_key(value);
if (!strcmp(var, "threshold")) {
callchain_param.min_percent = strtod(value, &endptr);
- if (value == endptr)
+ if (value == endptr) {
+ pr_err("Invalid callchain threshold: %s\n", value);
return -1;
+ }
}
if (!strcmp(var, "print-limit")) {
callchain_param.print_limit = strtod(value, &endptr);
- if (value == endptr)
+ if (value == endptr) {
+ pr_err("Invalid callchain print limit: %s\n", value);
return -1;
+ }
}
return 0;
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index 3d906db..0c7d5a4 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -386,8 +386,10 @@
if (!strcmp(var, "buildid.dir")) {
const char *dir = perf_config_dirname(var, value);
- if (!dir)
+ if (!dir) {
+ pr_err("Invalid buildid directory!\n");
return -1;
+ }
strncpy(buildid_dir, dir, MAXPATHLEN-1);
buildid_dir[MAXPATHLEN-1] = '\0';
}
@@ -405,10 +407,9 @@
static int perf_ui_config(const char *var, const char *value)
{
/* Add other config variables here. */
- if (!strcmp(var, "ui.show-headers")) {
+ if (!strcmp(var, "ui.show-headers"))
symbol_conf.show_hist_headers = perf_config_bool(var, value);
- return 0;
- }
+
return 0;
}
@@ -646,8 +647,13 @@
goto out;
}
- if (stat(user_config, &st) < 0)
+ if (stat(user_config, &st) < 0) {
+ if (errno == ENOENT)
+ ret = 0;
goto out_free;
+ }
+
+ ret = 0;
if (st.st_uid && (st.st_uid != geteuid())) {
warning("File %s not owned by current user or root, "
@@ -655,11 +661,8 @@
goto out_free;
}
- if (!st.st_size)
- goto out_free;
-
- ret = perf_config_from_file(collect_config, user_config, set);
-
+ if (st.st_size)
+ ret = perf_config_from_file(collect_config, user_config, set);
out_free:
free(user_config);
}
diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c
index 7123f4d..4e6cbc9 100644
--- a/tools/perf/util/data-convert-bt.c
+++ b/tools/perf/util/data-convert-bt.c
@@ -1473,7 +1473,7 @@
},
};
struct ctf_writer *cw = &c.writer;
- int err = -1;
+ int err;
if (opts->all) {
c.tool.comm = process_comm_event;
@@ -1481,12 +1481,15 @@
c.tool.fork = process_fork_event;
}
- perf_config(convert__config, &c);
+ err = perf_config(convert__config, &c);
+ if (err)
+ return err;
/* CTF writer */
if (ctf_writer__init(cw, path))
return -1;
+ err = -1;
/* perf.data session */
session = perf_session__new(&file, 0, &c.tool);
if (!session)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index d2c6cdd..28d41e7 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -9,6 +9,13 @@
#include "debug.h"
#include "vdso.h"
+static const char * const debuglink_paths[] = {
+ "%.0s%s",
+ "%s/%s",
+ "%s/.debug/%s",
+ "/usr/lib/debug%s/%s"
+};
+
char dso__symtab_origin(const struct dso *dso)
{
static const char origin[] = {
@@ -44,24 +51,43 @@
size_t len;
switch (type) {
- case DSO_BINARY_TYPE__DEBUGLINK: {
- char *debuglink;
+ case DSO_BINARY_TYPE__DEBUGLINK:
+ {
+ const char *last_slash;
+ char dso_dir[PATH_MAX];
+ char symfile[PATH_MAX];
+ unsigned int i;
len = __symbol__join_symfs(filename, size, dso->long_name);
- debuglink = filename + len;
- while (debuglink != filename && *debuglink != '/')
- debuglink--;
- if (*debuglink == '/')
- debuglink++;
+ last_slash = filename + len;
+ while (last_slash != filename && *last_slash != '/')
+ last_slash--;
- ret = -1;
- if (!is_regular_file(filename))
+ strncpy(dso_dir, filename, last_slash - filename);
+ dso_dir[last_slash-filename] = '\0';
+
+ if (!is_regular_file(filename)) {
+ ret = -1;
+ break;
+ }
+
+ ret = filename__read_debuglink(filename, symfile, PATH_MAX);
+ if (ret)
break;
- ret = filename__read_debuglink(filename, debuglink,
- size - (debuglink - filename));
+ /* Check predefined locations where debug file might reside */
+ ret = -1;
+ for (i = 0; i < ARRAY_SIZE(debuglink_paths); i++) {
+ snprintf(filename, size,
+ debuglink_paths[i], dso_dir, symfile);
+ if (is_regular_file(filename)) {
+ ret = 0;
+ break;
+ }
}
+
break;
+ }
case DSO_BINARY_TYPE__BUILD_ID_CACHE:
if (dso__build_id_filename(dso, filename, size) == NULL)
ret = -1;
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 8ab0d7d..4ea7ce7 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -1,5 +1,5 @@
#include <linux/types.h>
-#include <uapi/linux/mman.h> /* To get things like MAP_HUGETLB even on older libc headers */
+#include <linux/mman.h> /* To get things like MAP_HUGETLB even on older libc headers */
#include <api/fs/fs.h>
#include "event.h"
#include "debug.h"
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index d92e020..b601f28 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -1184,7 +1184,7 @@
return pages;
}
-static size_t perf_evlist__mmap_size(unsigned long pages)
+size_t perf_evlist__mmap_size(unsigned long pages)
{
if (pages == UINT_MAX)
pages = perf_event_mlock_kb_in_pages();
@@ -1224,12 +1224,16 @@
if (pages == 0 && min == 0) {
/* leave number of pages at 0 */
} else if (!is_power_of_2(pages)) {
+ char buf[100];
+
/* round pages up to next power of 2 */
pages = roundup_pow_of_two(pages);
if (!pages)
return -EINVAL;
- pr_info("rounding mmap pages size to %lu bytes (%lu pages)\n",
- pages * page_size, pages);
+
+ unit_number__scnprintf(buf, sizeof(buf), pages * page_size);
+ pr_info("rounding mmap pages size to %s (%lu pages)\n",
+ buf, pages);
}
if (pages > max)
@@ -1797,7 +1801,7 @@
*/
ret = write(evlist->workload.cork_fd, &bf, 1);
if (ret < 0)
- perror("enable to write to pipe");
+ perror("unable to write to pipe");
close(evlist->workload.cork_fd);
return ret;
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 4fd034f..389b9cc 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -218,6 +218,8 @@
bool overwrite);
void perf_evlist__munmap(struct perf_evlist *evlist);
+size_t perf_evlist__mmap_size(unsigned long pages);
+
void perf_evlist__disable(struct perf_evlist *evlist);
void perf_evlist__enable(struct perf_evlist *evlist);
void perf_evlist__toggle_enable(struct perf_evlist *evlist);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 04e536a..ac59710 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -1448,8 +1448,8 @@
return true;
}
-static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
- struct thread_map *threads)
+int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
+ struct thread_map *threads)
{
int cpu, thread, nthreads;
unsigned long flags = PERF_FLAG_FD_CLOEXEC;
@@ -1459,6 +1459,30 @@
if (perf_missing_features.write_backward && evsel->attr.write_backward)
return -EINVAL;
+ if (cpus == NULL) {
+ static struct cpu_map *empty_cpu_map;
+
+ if (empty_cpu_map == NULL) {
+ empty_cpu_map = cpu_map__dummy_new();
+ if (empty_cpu_map == NULL)
+ return -ENOMEM;
+ }
+
+ cpus = empty_cpu_map;
+ }
+
+ if (threads == NULL) {
+ static struct thread_map *empty_thread_map;
+
+ if (empty_thread_map == NULL) {
+ empty_thread_map = thread_map__new_by_tid(-1);
+ if (empty_thread_map == NULL)
+ return -ENOMEM;
+ }
+
+ threads = empty_thread_map;
+ }
+
if (evsel->system_wide)
nthreads = 1;
else
@@ -1655,46 +1679,16 @@
perf_evsel__free_fd(evsel);
}
-static struct {
- struct cpu_map map;
- int cpus[1];
-} empty_cpu_map = {
- .map.nr = 1,
- .cpus = { -1, },
-};
-
-static struct {
- struct thread_map map;
- int threads[1];
-} empty_thread_map = {
- .map.nr = 1,
- .threads = { -1, },
-};
-
-int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
- struct thread_map *threads)
-{
- if (cpus == NULL) {
- /* Work around old compiler warnings about strict aliasing */
- cpus = &empty_cpu_map.map;
- }
-
- if (threads == NULL)
- threads = &empty_thread_map.map;
-
- return __perf_evsel__open(evsel, cpus, threads);
-}
-
int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
struct cpu_map *cpus)
{
- return __perf_evsel__open(evsel, cpus, &empty_thread_map.map);
+ return perf_evsel__open(evsel, cpus, NULL);
}
int perf_evsel__open_per_thread(struct perf_evsel *evsel,
struct thread_map *threads)
{
- return __perf_evsel__open(evsel, &empty_cpu_map.map, threads);
+ return perf_evsel__open(evsel, NULL, threads);
}
static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel,
@@ -2469,7 +2463,9 @@
" -1: Allow use of (almost) all events by all users\n"
">= 0: Disallow raw tracepoint access by users without CAP_IOC_LOCK\n"
">= 1: Disallow CPU event access by users without CAP_SYS_ADMIN\n"
- ">= 2: Disallow kernel profiling by users without CAP_SYS_ADMIN",
+ ">= 2: Disallow kernel profiling by users without CAP_SYS_ADMIN\n\n"
+ "To make this setting permanent, edit /etc/sysctl.conf too, e.g.:\n\n"
+ " kernel.perf_event_paranoid = -1\n" ,
target->system_wide ? "system-wide " : "",
perf_event_paranoid());
case ENOENT:
diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c
index 6b29255..4ef5184 100644
--- a/tools/perf/util/evsel_fprintf.c
+++ b/tools/perf/util/evsel_fprintf.c
@@ -168,7 +168,6 @@
if (symbol_conf.bt_stop_list &&
node->sym &&
- node->sym->name &&
strlist__has_entry(symbol_conf.bt_stop_list,
node->sym->name)) {
break;
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index d89c9c7..3d12c16 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -41,6 +41,8 @@
#define PERF_MAGIC __perf_magic2
+const char perf_version_string[] = PERF_VERSION;
+
struct perf_file_attr {
struct perf_event_attr attr;
struct perf_file_section ids;
@@ -2801,8 +2803,10 @@
}
event = pevent_find_event(pevent, evsel->attr.config);
- if (event == NULL)
+ if (event == NULL) {
+ pr_debug("cannot find event format for %d\n", (int)evsel->attr.config);
return -1;
+ }
if (!evsel->name) {
snprintf(bf, sizeof(bf), "%s:%s", event->system, event->name);
@@ -3201,6 +3205,7 @@
case PERF_EVENT_UPDATE__SCALE:
ev_scale = (struct event_update_event_scale *) ev->data;
evsel->scale = ev_scale->scale;
+ break;
case PERF_EVENT_UPDATE__CPUS:
ev_cpus = (struct event_update_event_cpus *) ev->data;
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 7d1b7d3..32c6a93 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -2446,8 +2446,10 @@
symbol_conf.filter_relative = true;
else if (!strcmp(arg, "absolute"))
symbol_conf.filter_relative = false;
- else
+ else {
+ pr_debug("Invalud percentage: %s\n", arg);
return -1;
+ }
return 0;
}
diff --git a/tools/perf/util/intel-pt-decoder/Build b/tools/perf/util/intel-pt-decoder/Build
index 9b742ea..7aca5d6 100644
--- a/tools/perf/util/intel-pt-decoder/Build
+++ b/tools/perf/util/intel-pt-decoder/Build
@@ -23,4 +23,8 @@
$(call rule_mkdir)
$(call if_changed_dep,cc_o_c)
-CFLAGS_intel-pt-insn-decoder.o += -I$(OUTPUT)util/intel-pt-decoder -Wno-override-init
+CFLAGS_intel-pt-insn-decoder.o += -I$(OUTPUT)util/intel-pt-decoder
+
+ifneq ($(CC), clang)
+ CFLAGS_intel-pt-insn-decoder.o += -Wno-override-init
+endif
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
index e4e7dc7..7cf7f7a 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
@@ -22,6 +22,7 @@
#include <errno.h>
#include <stdint.h>
#include <inttypes.h>
+#include <linux/compiler.h>
#include "../cache.h"
#include "../util.h"
@@ -1746,6 +1747,7 @@
switch (decoder->packet.type) {
case INTEL_PT_TIP_PGD:
decoder->continuous_period = false;
+ __fallthrough;
case INTEL_PT_TIP_PGE:
case INTEL_PT_TIP:
intel_pt_log("ERROR: Unexpected packet\n");
@@ -1799,6 +1801,8 @@
decoder->pge = false;
decoder->continuous_period = false;
intel_pt_clear_tx_flags(decoder);
+ __fallthrough;
+
case INTEL_PT_TNT:
decoder->have_tma = false;
intel_pt_log("ERROR: Unexpected packet\n");
@@ -1839,6 +1843,7 @@
switch (decoder->packet.type) {
case INTEL_PT_TIP_PGD:
decoder->continuous_period = false;
+ __fallthrough;
case INTEL_PT_TIP_PGE:
case INTEL_PT_TIP:
decoder->pge = decoder->packet.type != INTEL_PT_TIP_PGD;
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c
index 4f7b320..7528ae4 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c
@@ -17,6 +17,7 @@
#include <string.h>
#include <endian.h>
#include <byteswap.h>
+#include <linux/compiler.h>
#include "intel-pt-pkt-decoder.h"
@@ -498,6 +499,7 @@
case INTEL_PT_FUP:
if (!(packet->count))
return snprintf(buf, buf_len, "%s no ip", name);
+ __fallthrough;
case INTEL_PT_CYC:
case INTEL_PT_VMCS:
case INTEL_PT_MTC:
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c
index 85d5eeb..da20cd5 100644
--- a/tools/perf/util/intel-pt.c
+++ b/tools/perf/util/intel-pt.c
@@ -2159,7 +2159,9 @@
addr_filters__init(&pt->filts);
- perf_config(intel_pt_perf_config, pt);
+ err = perf_config(intel_pt_perf_config, pt);
+ if (err)
+ goto err_free;
err = auxtrace_queues__init(&pt->queues);
if (err)
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
index b23ff44..8243564 100644
--- a/tools/perf/util/llvm-utils.c
+++ b/tools/perf/util/llvm-utils.c
@@ -48,8 +48,10 @@
llvm_param.kbuild_opts = strdup(value);
else if (!strcmp(var, "dump-obj"))
llvm_param.dump_obj = !!perf_config_bool(var, value);
- else
+ else {
+ pr_debug("Invalid LLVM config option: %s\n", value);
return -1;
+ }
llvm_param.user_set_param = true;
return 0;
}
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 9b33bef..71c9720 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -87,6 +87,25 @@
return NULL;
}
+struct machine *machine__new_kallsyms(void)
+{
+ struct machine *machine = machine__new_host();
+ /*
+ * FIXME:
+ * 1) MAP__FUNCTION will go away when we stop loading separate maps for
+ * functions and data objects.
+ * 2) We should switch to machine__load_kallsyms(), i.e. not explicitely
+ * ask for not using the kcore parsing code, once this one is fixed
+ * to create a map per module.
+ */
+ if (machine && __machine__load_kallsyms(machine, "/proc/kallsyms", MAP__FUNCTION, true) <= 0) {
+ machine__delete(machine);
+ machine = NULL;
+ }
+
+ return machine;
+}
+
static void dsos__purge(struct dsos *dsos)
{
struct dso *pos, *n;
@@ -763,7 +782,7 @@
int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel)
{
- enum map_type type;
+ int type;
u64 start = machine__get_running_kernel_start(machine, NULL);
/* In case of renewal the kernel map, destroy previous one */
@@ -794,7 +813,7 @@
void machine__destroy_kernel_maps(struct machine *machine)
{
- enum map_type type;
+ int type;
for (type = 0; type < MAP__NR_TYPES; ++type) {
struct kmap *kmap;
@@ -1546,7 +1565,7 @@
static bool symbol__match_regex(struct symbol *sym, regex_t *regex)
{
- if (sym->name && !regexec(regex, sym->name, 0, NULL, 0))
+ if (!regexec(regex, sym->name, 0, NULL, 0))
return 1;
return 0;
}
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 354de6e..a283050 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -129,6 +129,7 @@
void machines__set_comm_exec(struct machines *machines, bool comm_exec);
struct machine *machine__new_host(void);
+struct machine *machine__new_kallsyms(void);
int machine__init(struct machine *machine, const char *root_dir, pid_t pid);
void machine__exit(struct machine *machine);
void machine__delete_threads(struct machine *machine);
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 4f9a71c..0a943e7 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -387,10 +387,10 @@
{
const char *dsoname = "[unknown]";
- if (map && map->dso && (map->dso->name || map->dso->long_name)) {
+ if (map && map->dso) {
if (symbol_conf.show_kernel_path && map->dso->long_name)
dsoname = map->dso->long_name;
- else if (map->dso->name)
+ else
dsoname = map->dso->name;
}
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 3c876b8..281e44a 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -211,6 +211,8 @@
closedir(evt_dir);
closedir(sys_dir);
path = zalloc(sizeof(*path));
+ if (!path)
+ return NULL;
path->system = malloc(MAX_EVENT_LENGTH);
if (!path->system) {
free(path);
@@ -252,8 +254,7 @@
if (path->system == NULL || path->name == NULL) {
zfree(&path->system);
zfree(&path->name);
- free(path);
- path = NULL;
+ zfree(&path);
}
return path;
@@ -310,10 +311,11 @@
event_attr_init(attr);
- evsel = perf_evsel__new_idx(attr, (*idx)++);
+ evsel = perf_evsel__new_idx(attr, *idx);
if (!evsel)
return NULL;
+ (*idx)++;
evsel->cpus = cpu_map__get(cpus);
evsel->own_cpus = cpu_map__get(cpus);
@@ -1477,10 +1479,9 @@
for (i = 0; i < perf_pmu_events_list_num; i++) {
p = perf_pmu_events_list + i;
- free(p->symbol);
+ zfree(&p->symbol);
}
- free(perf_pmu_events_list);
- perf_pmu_events_list = NULL;
+ zfree(&perf_pmu_events_list);
perf_pmu_events_list_num = 0;
}
}
@@ -1504,35 +1505,41 @@
struct perf_pmu_alias *alias;
int len = 0;
- pmu = perf_pmu__find("cpu");
- if ((pmu == NULL) || list_empty(&pmu->aliases)) {
+ pmu = NULL;
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ list_for_each_entry(alias, &pmu->aliases, list) {
+ if (strchr(alias->name, '-'))
+ len++;
+ len++;
+ }
+ }
+
+ if (len == 0) {
perf_pmu_events_list_num = -1;
return;
}
- list_for_each_entry(alias, &pmu->aliases, list) {
- if (strchr(alias->name, '-'))
- len++;
- len++;
- }
perf_pmu_events_list = malloc(sizeof(struct perf_pmu_event_symbol) * len);
if (!perf_pmu_events_list)
return;
perf_pmu_events_list_num = len;
len = 0;
- list_for_each_entry(alias, &pmu->aliases, list) {
- struct perf_pmu_event_symbol *p = perf_pmu_events_list + len;
- char *tmp = strchr(alias->name, '-');
+ pmu = NULL;
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ list_for_each_entry(alias, &pmu->aliases, list) {
+ struct perf_pmu_event_symbol *p = perf_pmu_events_list + len;
+ char *tmp = strchr(alias->name, '-');
- if (tmp != NULL) {
- SET_SYMBOL(strndup(alias->name, tmp - alias->name),
- PMU_EVENT_SYMBOL_PREFIX);
- p++;
- SET_SYMBOL(strdup(++tmp), PMU_EVENT_SYMBOL_SUFFIX);
- len += 2;
- } else {
- SET_SYMBOL(strdup(alias->name), PMU_EVENT_SYMBOL);
- len++;
+ if (tmp != NULL) {
+ SET_SYMBOL(strndup(alias->name, tmp - alias->name),
+ PMU_EVENT_SYMBOL_PREFIX);
+ p++;
+ SET_SYMBOL(strdup(++tmp), PMU_EVENT_SYMBOL_SUFFIX);
+ len += 2;
+ } else {
+ SET_SYMBOL(strdup(alias->name), PMU_EVENT_SYMBOL);
+ len++;
+ }
}
}
qsort(perf_pmu_events_list, len,
@@ -1563,7 +1570,7 @@
r = bsearch(&p, perf_pmu_events_list,
(size_t) perf_pmu_events_list_num,
sizeof(struct perf_pmu_event_symbol), comp_pmu);
- free(p.symbol);
+ zfree(&p.symbol);
return r ? r->type : PMU_EVENT_SYMBOL_ERR;
}
@@ -1710,8 +1717,8 @@
fprintf(stderr, "%*s\\___ %s\n", idx + 1, "", err->str);
if (err->help)
fprintf(stderr, "\n%s\n", err->help);
- free(err->str);
- free(err->help);
+ zfree(&err->str);
+ zfree(&err->help);
}
fprintf(stderr, "Run 'perf list' for a list of valid events\n");
@@ -2013,17 +2020,14 @@
.config = config,
.disabled = 1,
};
- struct {
- struct thread_map map;
- int threads[1];
- } tmap = {
- .map.nr = 1,
- .threads = { 0 },
- };
+ struct thread_map *tmap = thread_map__new_by_tid(0);
+
+ if (tmap == NULL)
+ return false;
evsel = perf_evsel__new(&attr);
if (evsel) {
- open_return = perf_evsel__open(evsel, NULL, &tmap.map);
+ open_return = perf_evsel__open(evsel, NULL, tmap);
ret = open_return >= 0;
if (open_return == -EACCES) {
@@ -2035,7 +2039,7 @@
*
*/
evsel->attr.exclude_kernel = 1;
- ret = perf_evsel__open(evsel, NULL, &tmap.map) >= 0;
+ ret = perf_evsel__open(evsel, NULL, tmap) >= 0;
}
perf_evsel__delete(evsel);
}
@@ -2406,7 +2410,7 @@
list_for_each_entry_safe(term, h, terms, list) {
if (term->array.nr_ranges)
- free(term->array.ranges);
+ zfree(&term->array.ranges);
list_del_init(&term->list);
free(term);
}
@@ -2422,7 +2426,7 @@
void parse_events__clear_array(struct parse_events_array *a)
{
- free(a->ranges);
+ zfree(&a->ranges);
}
void parse_events_evlist_error(struct parse_events_evlist *data,
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 879115f..a14b47a 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -12,9 +12,13 @@
#include <linux/list.h>
#include <linux/types.h>
#include "util.h"
+#include "pmu.h"
+#include "debug.h"
#include "parse-events.h"
#include "parse-events-bison.h"
+void parse_events_error(YYLTYPE *loc, void *data, void *scanner, char const *msg);
+
#define ABORT_ON(val) \
do { \
if (val) \
@@ -236,15 +240,34 @@
struct list_head *head;
struct parse_events_term *term;
struct list_head *list;
+ struct perf_pmu *pmu = NULL;
+ int ok = 0;
- ALLOC_LIST(head);
- ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
- $1, 1, &@1, NULL));
- list_add_tail(&term->list, head);
-
+ /* Add it for all PMUs that support the alias */
ALLOC_LIST(list);
- ABORT_ON(parse_events_add_pmu(data, list, "cpu", head));
- parse_events_terms__delete(head);
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ struct perf_pmu_alias *alias;
+
+ list_for_each_entry(alias, &pmu->aliases, list) {
+ if (!strcasecmp(alias->name, $1)) {
+ ALLOC_LIST(head);
+ ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
+ $1, 1, &@1, NULL));
+ list_add_tail(&term->list, head);
+
+ if (!parse_events_add_pmu(data, list,
+ pmu->name, head)) {
+ pr_debug("%s -> %s/%s/\n", $1,
+ pmu->name, alias->str);
+ ok++;
+ }
+
+ parse_events_terms__delete(head);
+ }
+ }
+ }
+ if (!ok)
+ YYABORT;
$$ = list;
}
|
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index dc6ccaa..49bfee0 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -94,6 +94,43 @@
return 0;
}
+static int convert_scale(const char *scale, char **end, double *sval)
+{
+ char *lc;
+ int ret = 0;
+
+ /*
+ * save current locale
+ */
+ lc = setlocale(LC_NUMERIC, NULL);
+
+ /*
+ * The lc string may be allocated in static storage,
+ * so get a dynamic copy to make it survive setlocale
+ * call below.
+ */
+ lc = strdup(lc);
+ if (!lc) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * force to C locale to ensure kernel
+ * scale string is converted correctly.
+ * kernel uses default C locale.
+ */
+ setlocale(LC_NUMERIC, "C");
+
+ *sval = strtod(scale, end);
+
+out:
+ /* restore locale */
+ setlocale(LC_NUMERIC, lc);
+ free(lc);
+ return ret;
+}
+
static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *name)
{
struct stat st;
@@ -101,7 +138,6 @@
char scale[128];
int fd, ret = -1;
char path[PATH_MAX];
- char *lc;
snprintf(path, PATH_MAX, "%s/%s.scale", dir, name);
@@ -121,37 +157,7 @@
else
scale[sret] = '\0';
- /*
- * save current locale
- */
- lc = setlocale(LC_NUMERIC, NULL);
-
- /*
- * The lc string may be allocated in static storage,
- * so get a dynamic copy to make it survive setlocale
- * call below.
- */
- lc = strdup(lc);
- if (!lc) {
- ret = -ENOMEM;
- goto error;
- }
-
- /*
- * force to C locale to ensure kernel
- * scale string is converted correctly.
- * kernel uses default C locale.
- */
- setlocale(LC_NUMERIC, "C");
-
- alias->scale = strtod(scale, NULL);
-
- /* restore locale */
- setlocale(LC_NUMERIC, lc);
-
- free(lc);
-
- ret = 0;
+ ret = convert_scale(scale, NULL, &alias->scale);
error:
close(fd);
return ret;
@@ -223,11 +229,13 @@
}
static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,
- char *desc, char *val, char *long_desc,
- char *topic)
+ char *desc, char *val,
+ char *long_desc, char *topic,
+ char *unit, char *perpkg)
{
struct perf_pmu_alias *alias;
int ret;
+ int num;
alias = malloc(sizeof(*alias));
if (!alias)
@@ -261,6 +269,13 @@
alias->long_desc = long_desc ? strdup(long_desc) :
desc ? strdup(desc) : NULL;
alias->topic = topic ? strdup(topic) : NULL;
+ if (unit) {
+ if (convert_scale(unit, &unit, &alias->scale) < 0)
+ return -1;
+ snprintf(alias->unit, sizeof(alias->unit), "%s", unit);
+ }
+ alias->per_pkg = perpkg && sscanf(perpkg, "%d", &num) == 1 && num == 1;
+ alias->str = strdup(val);
list_add_tail(&alias->list, list);
@@ -278,7 +293,8 @@
buf[ret] = 0;
- return __perf_pmu__new_alias(list, dir, name, NULL, buf, NULL, NULL);
+ return __perf_pmu__new_alias(list, dir, name, NULL, buf, NULL, NULL, NULL,
+ NULL);
}
static inline bool pmu_alias_info_file(char *name)
@@ -498,7 +514,7 @@
* to the current running CPU. Then, add all PMU events from that table
* as aliases.
*/
-static void pmu_add_cpu_aliases(struct list_head *head)
+static void pmu_add_cpu_aliases(struct list_head *head, const char *name)
{
int i;
struct pmu_events_map *map;
@@ -534,14 +550,21 @@
*/
i = 0;
while (1) {
+ const char *pname;
+
pe = &map->table[i++];
if (!pe->name)
break;
+ pname = pe->pmu ? pe->pmu : "cpu";
+ if (strncmp(pname, name, strlen(pname)))
+ continue;
+
/* need type casts to override 'const' */
__perf_pmu__new_alias(head, NULL, (char *)pe->name,
(char *)pe->desc, (char *)pe->event,
- (char *)pe->long_desc, (char *)pe->topic);
+ (char *)pe->long_desc, (char *)pe->topic,
+ (char *)pe->unit, (char *)pe->perpkg);
}
out:
@@ -569,15 +592,16 @@
if (pmu_format(name, &format))
return NULL;
- if (pmu_aliases(name, &aliases))
- return NULL;
-
- if (!strcmp(name, "cpu"))
- pmu_add_cpu_aliases(&aliases);
-
+ /*
+ * Check the type first to avoid unnecessary work.
+ */
if (pmu_type(name, &type))
return NULL;
+ if (pmu_aliases(name, &aliases))
+ return NULL;
+
+ pmu_add_cpu_aliases(&aliases, name);
pmu = zalloc(sizeof(*pmu));
if (!pmu)
return NULL;
@@ -921,12 +945,12 @@
* define unit, scale and snapshot, fail
* if there's more than one.
*/
- if ((info->unit && alias->unit) ||
+ if ((info->unit && alias->unit[0]) ||
(info->scale && alias->scale) ||
(info->snapshot && alias->snapshot))
return -EINVAL;
- if (alias->unit)
+ if (alias->unit[0])
info->unit = alias->unit;
if (alias->scale)
@@ -1065,6 +1089,8 @@
char *name;
char *desc;
char *topic;
+ char *str;
+ char *pmu;
};
static int cmp_sevent(const void *a, const void *b)
@@ -1161,6 +1187,8 @@
aliases[j].desc = long_desc ? alias->long_desc :
alias->desc;
aliases[j].topic = alias->topic;
+ aliases[j].str = alias->str;
+ aliases[j].pmu = pmu->name;
j++;
}
if (pmu->selectable &&
@@ -1175,6 +1203,9 @@
len = j;
qsort(aliases, len, sizeof(struct sevent), cmp_sevent);
for (j = 0; j < len; j++) {
+ /* Skip duplicates */
+ if (j > 0 && !strcmp(aliases[j].name, aliases[j - 1].name))
+ continue;
if (name_only) {
printf("%s ", aliases[j].name);
continue;
@@ -1192,6 +1223,8 @@
printf("%*s", 8, "[");
wordwrap(aliases[j].desc, 8, columns, 0);
printf("]\n");
+ if (verbose)
+ printf("%*s%s/%s/\n", 8, "", aliases[j].pmu, aliases[j].str);
} else
printf(" %-50s [Kernel PMU event]\n", aliases[j].name);
printed++;
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 2571203..00852dd 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -43,6 +43,7 @@
char *desc;
char *long_desc;
char *topic;
+ char *str;
struct list_head terms; /* HEAD struct parse_events_term -> list */
struct list_head list; /* ELEM */
char unit[UNIT_MAX_LEN+1];
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 6a6f44d..35f5b7b 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -2061,7 +2061,7 @@
bool is_kprobe)
{
struct symbol *sym = NULL;
- struct map *map;
+ struct map *map = NULL;
u64 addr = tp->address;
int ret = -ENOENT;
@@ -3023,20 +3023,17 @@
tev->nargs = pev->nargs;
tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
- if (!tev->args) {
- err = -ENOMEM;
+ if (!tev->args)
goto errout;
- }
+
for (i = 0; i < tev->nargs; i++)
copy_to_probe_trace_arg(&tev->args[i], &pev->args[i]);
return 1;
errout:
- if (*tevs) {
- clear_probe_trace_events(*tevs, 1);
- *tevs = NULL;
- }
+ clear_probe_trace_events(*tevs, 1);
+ *tevs = NULL;
return err;
}
diff --git a/tools/perf/util/scripting-engines/Build b/tools/perf/util/scripting-engines/Build
index 6516e22..82d28c6 100644
--- a/tools/perf/util/scripting-engines/Build
+++ b/tools/perf/util/scripting-engines/Build
@@ -1,6 +1,6 @@
libperf-$(CONFIG_LIBPERL) += trace-event-perl.o
libperf-$(CONFIG_LIBPYTHON) += trace-event-python.o
-CFLAGS_trace-event-perl.o += $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-undef -Wno-switch-default
+CFLAGS_trace-event-perl.o += $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-nested-externs -Wno-undef -Wno-switch-default
CFLAGS_trace-event-python.o += $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c
index e55a132..c1555fd 100644
--- a/tools/perf/util/scripting-engines/trace-event-perl.c
+++ b/tools/perf/util/scripting-engines/trace-event-perl.c
@@ -309,10 +309,10 @@
if (node->map) {
struct map *map = node->map;
const char *dsoname = "[unknown]";
- if (map && map->dso && (map->dso->name || map->dso->long_name)) {
+ if (map && map->dso) {
if (symbol_conf.show_kernel_path && map->dso->long_name)
dsoname = map->dso->long_name;
- else if (map->dso->name)
+ else
dsoname = map->dso->name;
}
if (!hv_stores(elem, "dso", newSVpv(dsoname,0))) {
@@ -350,8 +350,10 @@
if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
return;
- if (!event)
- die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config);
+ if (!event) {
+ pr_debug("ug! no event found for type %" PRIu64, (u64)evsel->attr.config);
+ return;
+ }
pid = raw_field_value(event, "common_pid", data);
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index f268201..4cdbc8f 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -1191,7 +1191,7 @@
u64 sample_type = evsel->attr.sample_type;
u64 read_format = evsel->attr.read_format;
- /* Standard sample delievery. */
+ /* Standard sample delivery. */
if (!(sample_type & PERF_SAMPLE_READ))
return tool->sample(tool, event, sample, evsel, machine);
@@ -1901,7 +1901,7 @@
const char *symbol_name, u64 addr)
{
char *bracket;
- enum map_type i;
+ int i;
struct ref_reloc_sym *ref;
ref = zalloc(sizeof(struct ref_reloc_sym));
diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c
index bcae659..efb5377 100644
--- a/tools/perf/util/strfilter.c
+++ b/tools/perf/util/strfilter.c
@@ -269,6 +269,7 @@
len = strfilter_node__sprint_pt(node->l, buf);
if (len < 0)
return len;
+ __fallthrough;
case '!':
if (buf) {
*(buf + len++) = *node->p;
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index d8dfaf6..bddca51 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -21,6 +21,8 @@
case 'b': case 'B':
if (*p)
goto out_err;
+
+ __fallthrough;
case '\0':
return length;
default:
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index dc93940..70e389b 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1460,9 +1460,11 @@
* DSO_BINARY_TYPE__BUILDID_DEBUGINFO to work
*/
if (!dso->has_build_id &&
- is_regular_file(dso->long_name) &&
- filename__read_build_id(dso->long_name, build_id, BUILD_ID_SIZE) > 0)
+ is_regular_file(dso->long_name)) {
+ __symbol__join_symfs(name, PATH_MAX, dso->long_name);
+ if (filename__read_build_id(name, build_id, BUILD_ID_SIZE) > 0)
dso__set_build_id(dso, build_id);
+ }
/*
* Iterate over candidate debug images.
diff --git a/tools/perf/util/symbol_fprintf.c b/tools/perf/util/symbol_fprintf.c
index 7c6b33e..63694e1 100644
--- a/tools/perf/util/symbol_fprintf.c
+++ b/tools/perf/util/symbol_fprintf.c
@@ -21,7 +21,7 @@
unsigned long offset;
size_t length;
- if (sym && sym->name) {
+ if (sym) {
length = fprintf(fp, "%s", sym->name);
if (al && print_offsets) {
if (al->addr < sym->end)
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c
index f9eab20..7c3fcc5 100644
--- a/tools/perf/util/thread_map.c
+++ b/tools/perf/util/thread_map.c
@@ -93,7 +93,7 @@
{
DIR *proc;
int max_threads = 32, items, i;
- char path[256];
+ char path[NAME_MAX + 1 + 6];
struct dirent *dirent, **namelist = NULL;
struct thread_map *threads = thread_map__alloc(max_threads);
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
index d995743..e7d60d0 100644
--- a/tools/perf/util/trace-event-info.c
+++ b/tools/perf/util/trace-event-info.c
@@ -42,7 +42,7 @@
#include "evsel.h"
#include "debug.h"
-#define VERSION "0.5"
+#define VERSION "0.6"
static int output_fd;
@@ -170,6 +170,12 @@
return false;
}
+#define for_each_event(dir, dent, tps) \
+ while ((dent = readdir(dir))) \
+ if (dent->d_type == DT_DIR && \
+ (strcmp(dent->d_name, ".")) && \
+ (strcmp(dent->d_name, ".."))) \
+
static int copy_event_system(const char *sys, struct tracepoint_path *tps)
{
struct dirent *dent;
@@ -186,12 +192,10 @@
return -errno;
}
- while ((dent = readdir(dir))) {
- if (dent->d_type != DT_DIR ||
- strcmp(dent->d_name, ".") == 0 ||
- strcmp(dent->d_name, "..") == 0 ||
- !name_in_tp_list(dent->d_name, tps))
+ for_each_event(dir, dent, tps) {
+ if (!name_in_tp_list(dent->d_name, tps))
continue;
+
if (asprintf(&format, "%s/%s/format", sys, dent->d_name) < 0) {
err = -ENOMEM;
goto out;
@@ -210,12 +214,10 @@
}
rewinddir(dir);
- while ((dent = readdir(dir))) {
- if (dent->d_type != DT_DIR ||
- strcmp(dent->d_name, ".") == 0 ||
- strcmp(dent->d_name, "..") == 0 ||
- !name_in_tp_list(dent->d_name, tps))
+ for_each_event(dir, dent, tps) {
+ if (!name_in_tp_list(dent->d_name, tps))
continue;
+
if (asprintf(&format, "%s/%s/format", sys, dent->d_name) < 0) {
err = -ENOMEM;
goto out;
@@ -290,13 +292,11 @@
goto out;
}
- while ((dent = readdir(dir))) {
- if (dent->d_type != DT_DIR ||
- strcmp(dent->d_name, ".") == 0 ||
- strcmp(dent->d_name, "..") == 0 ||
- strcmp(dent->d_name, "ftrace") == 0 ||
+ for_each_event(dir, dent, tps) {
+ if (strcmp(dent->d_name, "ftrace") == 0 ||
!system_in_tp_list(dent->d_name, tps))
continue;
+
count++;
}
@@ -307,13 +307,11 @@
}
rewinddir(dir);
- while ((dent = readdir(dir))) {
- if (dent->d_type != DT_DIR ||
- strcmp(dent->d_name, ".") == 0 ||
- strcmp(dent->d_name, "..") == 0 ||
- strcmp(dent->d_name, "ftrace") == 0 ||
+ for_each_event(dir, dent, tps) {
+ if (strcmp(dent->d_name, "ftrace") == 0 ||
!system_in_tp_list(dent->d_name, tps))
continue;
+
if (asprintf(&sys, "%s/%s", path, dent->d_name) < 0) {
err = -ENOMEM;
goto out;
@@ -379,6 +377,34 @@
return err;
}
+static int record_saved_cmdline(void)
+{
+ unsigned int size;
+ char *path;
+ struct stat st;
+ int ret, err = 0;
+
+ path = get_tracing_file("saved_cmdlines");
+ if (!path) {
+ pr_debug("can't get tracing/saved_cmdline");
+ return -ENOMEM;
+ }
+
+ ret = stat(path, &st);
+ if (ret < 0) {
+ /* not found */
+ size = 0;
+ if (write(output_fd, &size, 8) != 8)
+ err = -EIO;
+ goto out;
+ }
+ err = record_file(path, 8);
+
+out:
+ put_tracing_file(path);
+ return err;
+}
+
static void
put_tracepoints_path(struct tracepoint_path *tps)
{
@@ -539,6 +565,9 @@
if (err)
goto out;
err = record_ftrace_printk();
+ if (err)
+ goto out;
+ err = record_saved_cmdline();
out:
/*
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
index 33b52ea..de0078e 100644
--- a/tools/perf/util/trace-event-parse.c
+++ b/tools/perf/util/trace-event-parse.c
@@ -160,6 +160,23 @@
}
}
+void parse_saved_cmdline(struct pevent *pevent,
+ char *file, unsigned int size __maybe_unused)
+{
+ char *comm;
+ char *line;
+ char *next = NULL;
+ int pid;
+
+ line = strtok_r(file, "\n", &next);
+ while (line) {
+ sscanf(line, "%d %ms", &pid, &comm);
+ pevent_register_comm(pevent, comm, pid);
+ free(comm);
+ line = strtok_r(NULL, "\n", &next);
+ }
+}
+
int parse_ftrace_file(struct pevent *pevent, char *buf, unsigned long size)
{
return pevent_parse_event(pevent, buf, size, "ftrace");
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c
index b67a0cc..2742015 100644
--- a/tools/perf/util/trace-event-read.c
+++ b/tools/perf/util/trace-event-read.c
@@ -260,39 +260,53 @@
static int read_ftrace_file(struct pevent *pevent, unsigned long long size)
{
+ int ret;
char *buf;
buf = malloc(size);
- if (buf == NULL)
- return -1;
-
- if (do_read(buf, size) < 0) {
- free(buf);
+ if (buf == NULL) {
+ pr_debug("memory allocation failure\n");
return -1;
}
- parse_ftrace_file(pevent, buf, size);
+ ret = do_read(buf, size);
+ if (ret < 0) {
+ pr_debug("error reading ftrace file.\n");
+ goto out;
+ }
+
+ ret = parse_ftrace_file(pevent, buf, size);
+ if (ret < 0)
+ pr_debug("error parsing ftrace file.\n");
+out:
free(buf);
- return 0;
+ return ret;
}
static int read_event_file(struct pevent *pevent, char *sys,
unsigned long long size)
{
+ int ret;
char *buf;
buf = malloc(size);
- if (buf == NULL)
- return -1;
-
- if (do_read(buf, size) < 0) {
- free(buf);
+ if (buf == NULL) {
+ pr_debug("memory allocation failure\n");
return -1;
}
- parse_event_file(pevent, buf, size, sys);
+ ret = do_read(buf, size);
+ if (ret < 0) {
+ free(buf);
+ goto out;
+ }
+
+ ret = parse_event_file(pevent, buf, size, sys);
+ if (ret < 0)
+ pr_debug("error parsing event file.\n");
+out:
free(buf);
- return 0;
+ return ret;
}
static int read_ftrace_files(struct pevent *pevent)
@@ -341,6 +355,36 @@
return 0;
}
+static int read_saved_cmdline(struct pevent *pevent)
+{
+ unsigned long long size;
+ char *buf;
+ int ret;
+
+ /* it can have 0 size */
+ size = read8(pevent);
+ if (!size)
+ return 0;
+
+ buf = malloc(size + 1);
+ if (buf == NULL) {
+ pr_debug("memory allocation failure\n");
+ return -1;
+ }
+
+ ret = do_read(buf, size);
+ if (ret < 0) {
+ pr_debug("error reading saved cmdlines\n");
+ goto out;
+ }
+
+ parse_saved_cmdline(pevent, buf, size);
+ ret = 0;
+out:
+ free(buf);
+ return ret;
+}
+
ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)
{
char buf[BUFSIZ];
@@ -379,10 +423,11 @@
return -1;
if (show_version)
printf("version = %s\n", version);
- free(version);
- if (do_read(buf, 1) < 0)
+ if (do_read(buf, 1) < 0) {
+ free(version);
return -1;
+ }
file_bigendian = buf[0];
host_bigendian = bigendian();
@@ -423,6 +468,11 @@
err = read_ftrace_printk(pevent);
if (err)
goto out;
+ if (atof(version) >= 0.6) {
+ err = read_saved_cmdline(pevent);
+ if (err)
+ goto out;
+ }
size = trace_data_size;
repipe = false;
@@ -438,5 +488,6 @@
out:
if (pevent)
trace_event__cleanup(tevent);
+ free(version);
return size;
}
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index b0af9c8..1fbc044 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -42,6 +42,7 @@
void parse_proc_kallsyms(struct pevent *pevent, char *file, unsigned int size);
void parse_ftrace_printk(struct pevent *pevent, char *file, unsigned int size);
+void parse_saved_cmdline(struct pevent *pevent, char *file, unsigned int size);
ssize_t trace_report(int fd, struct trace_event *tevent, bool repipe);
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index 6fec84d..bfb9b79 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -35,6 +35,7 @@
#include "util.h"
#include "debug.h"
#include "asm/bug.h"
+#include "dso.h"
extern int
UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
@@ -297,15 +298,58 @@
int fd;
u64 ofs = dso->data.debug_frame_offset;
+ /* debug_frame can reside in:
+ * - dso
+ * - debug pointed by symsrc_filename
+ * - gnu_debuglink, which doesn't necessary
+ * has to be pointed by symsrc_filename
+ */
if (ofs == 0) {
fd = dso__data_get_fd(dso, machine);
- if (fd < 0)
- return -EINVAL;
+ if (fd >= 0) {
+ ofs = elf_section_offset(fd, ".debug_frame");
+ dso__data_put_fd(dso);
+ }
- /* Check the .debug_frame section for unwinding info */
- ofs = elf_section_offset(fd, ".debug_frame");
+ if (ofs <= 0) {
+ fd = open(dso->symsrc_filename, O_RDONLY);
+ if (fd >= 0) {
+ ofs = elf_section_offset(fd, ".debug_frame");
+ close(fd);
+ }
+ }
+
+ if (ofs <= 0) {
+ char *debuglink = malloc(PATH_MAX);
+ int ret = 0;
+
+ ret = dso__read_binary_type_filename(
+ dso, DSO_BINARY_TYPE__DEBUGLINK,
+ machine->root_dir, debuglink, PATH_MAX);
+ if (!ret) {
+ fd = open(debuglink, O_RDONLY);
+ if (fd >= 0) {
+ ofs = elf_section_offset(fd,
+ ".debug_frame");
+ close(fd);
+ }
+ }
+ if (ofs > 0) {
+ if (dso->symsrc_filename != NULL) {
+ pr_warning(
+ "%s: overwrite symsrc(%s,%s)\n",
+ __func__,
+ dso->symsrc_filename,
+ debuglink);
+ free(dso->symsrc_filename);
+ }
+ dso->symsrc_filename = debuglink;
+ } else {
+ free(debuglink);
+ }
+ }
+
dso->data.debug_frame_offset = ofs;
- dso__data_put_fd(dso);
}
*offset = ofs;
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index 9ddd988..d8b45ce 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -85,7 +85,7 @@
return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0;
}
-int rm_rf(char *path)
+int rm_rf(const char *path)
{
DIR *dir;
int ret = 0;
@@ -789,3 +789,16 @@
}
return 1;
}
+
+int unit_number__scnprintf(char *buf, size_t size, u64 n)
+{
+ char unit[4] = "BKMG";
+ int i = 0;
+
+ while (((n / 1024) > 1) && (i < 3)) {
+ n /= 1024;
+ i++;
+ }
+
+ return scnprintf(buf, size, "%" PRIu64 "%c", n, unit[i]);
+}
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 1d639e3..c74708d 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -209,7 +209,7 @@
}
int mkdir_p(char *path, mode_t mode);
-int rm_rf(char *path);
+int rm_rf(const char *path);
struct strlist *lsdir(const char *name, bool (*filter)(const char *, struct dirent *));
bool lsdir_no_dot_filter(const char *name, struct dirent *d);
int copyfile(const char *from, const char *to);
@@ -363,4 +363,5 @@
int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz);
+int unit_number__scnprintf(char *buf, size_t size, u64 n);
#endif /* GIT_COMPAT_UTIL_H */
diff --git a/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py
new file mode 100755
index 0000000..fd706ac
--- /dev/null
+++ b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py
@@ -0,0 +1,569 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+""" This utility can be used to debug and tune the performance of the
+intel_pstate driver. This utility can be used in two ways:
+- If there is Linux trace file with pstate_sample events enabled, then
+this utility can parse the trace file and generate performance plots.
+- If user has not specified a trace file as input via command line parameters,
+then this utility enables and collects trace data for a user specified interval
+and generates performance plots.
+
+Prerequisites:
+ Python version 2.7.x
+ gnuplot 5.0 or higher
+ gnuplot-py 1.8
+ (Most of the distributions have these required packages. They may be called
+ gnuplot-py, phython-gnuplot. )
+
+ HWP (Hardware P-States are disabled)
+ Kernel config for Linux trace is enabled
+
+ see print_help(): for Usage and Output details
+
+"""
+from __future__ import print_function
+from datetime import datetime
+import subprocess
+import os
+import time
+import re
+import sys
+import getopt
+import Gnuplot
+from numpy import *
+from decimal import *
+
+__author__ = "Srinivas Pandruvada"
+__copyright__ = " Copyright (c) 2017, Intel Corporation. "
+__license__ = "GPL version 2"
+
+
+MAX_CPUS = 256
+
+# Define the csv file columns
+C_COMM = 18
+C_GHZ = 17
+C_ELAPSED = 16
+C_SAMPLE = 15
+C_DURATION = 14
+C_LOAD = 13
+C_BOOST = 12
+C_FREQ = 11
+C_TSC = 10
+C_APERF = 9
+C_MPERF = 8
+C_TO = 7
+C_FROM = 6
+C_SCALED = 5
+C_CORE = 4
+C_USEC = 3
+C_SEC = 2
+C_CPU = 1
+
+global sample_num, last_sec_cpu, last_usec_cpu, start_time, testname
+
+# 11 digits covers uptime to 115 days
+getcontext().prec = 11
+
+sample_num =0
+last_sec_cpu = [0] * MAX_CPUS
+last_usec_cpu = [0] * MAX_CPUS
+
+def print_help():
+ print('intel_pstate_tracer.py:')
+ print(' Usage:')
+ print(' If the trace file is available, then to simply parse and plot, use (sudo not required):')
+ print(' ./intel_pstate_tracer.py [-c cpus] -t <trace_file> -n <test_name>')
+ print(' Or')
+ print(' ./intel_pstate_tracer.py [--cpu cpus] ---trace_file <trace_file> --name <test_name>')
+ print(' To generate trace file, parse and plot, use (sudo required):')
+ print(' sudo ./intel_pstate_tracer.py [-c cpus] -i <interval> -n <test_name>')
+ print(' Or')
+ print(' sudo ./intel_pstate_tracer.py [--cpu cpus] --interval <interval> --name <test_name>')
+ print(' Optional argument:')
+ print(' cpus: comma separated list of CPUs')
+ print(' Output:')
+ print(' If not already present, creates a "results/test_name" folder in the current working directory with:')
+ print(' cpu.csv - comma seperated values file with trace contents and some additional calculations.')
+ print(' cpu???.csv - comma seperated values file for CPU number ???.')
+ print(' *.png - a variety of PNG format plot files created from the trace contents and the additional calculations.')
+ print(' Notes:')
+ print(' Avoid the use of _ (underscore) in test names, because in gnuplot it is a subscript directive.')
+ print(' Maximum number of CPUs is {0:d}. If there are more the script will abort with an error.'.format(MAX_CPUS))
+ print(' Off-line CPUs cause the script to list some warnings, and create some empty files. Use the CPU mask feature for a clean run.')
+ print(' Empty y range warnings for autoscaled plots can occur and can be ignored.')
+
+def plot_perf_busy_with_sample(cpu_index):
+ """ Plot method to per cpu information """
+
+ file_name = 'cpu{:0>3}.csv'.format(cpu_index)
+ if os.path.exists(file_name):
+ output_png = "cpu%03d_perf_busy_vs_samples.png" % cpu_index
+ g_plot = common_all_gnuplot_settings(output_png)
+ g_plot('set yrange [0:40]')
+ g_plot('set y2range [0:200]')
+ g_plot('set y2tics 0, 10')
+ g_plot('set title "{} : cpu perf busy vs. sample : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now()))
+# Override common
+ g_plot('set xlabel "Samples"')
+ g_plot('set ylabel "P-State"')
+ g_plot('set y2label "Scaled Busy/performance/io-busy(%)"')
+ set_4_plot_linestyles(g_plot)
+ g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y2 title "performance",\\'.format(C_SAMPLE, C_CORE))
+ g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 2 axis x1y2 title "scaled-busy",\\'.format(C_SAMPLE, C_SCALED))
+ g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 3 axis x1y2 title "io-boost",\\'.format(C_SAMPLE, C_BOOST))
+ g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 4 axis x1y1 title "P-State"'.format(C_SAMPLE, C_TO))
+
+def plot_perf_busy(cpu_index):
+ """ Plot some per cpu information """
+
+ file_name = 'cpu{:0>3}.csv'.format(cpu_index)
+ if os.path.exists(file_name):
+ output_png = "cpu%03d_perf_busy.png" % cpu_index
+ g_plot = common_all_gnuplot_settings(output_png)
+ g_plot('set yrange [0:40]')
+ g_plot('set y2range [0:200]')
+ g_plot('set y2tics 0, 10')
+ g_plot('set title "{} : perf busy : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now()))
+ g_plot('set ylabel "P-State"')
+ g_plot('set y2label "Scaled Busy/performance/io-busy(%)"')
+ set_4_plot_linestyles(g_plot)
+ g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y2 title "performance",\\'.format(C_ELAPSED, C_CORE))
+ g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 2 axis x1y2 title "scaled-busy",\\'.format(C_ELAPSED, C_SCALED))
+ g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 3 axis x1y2 title "io-boost",\\'.format(C_ELAPSED, C_BOOST))
+ g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 4 axis x1y1 title "P-State"'.format(C_ELAPSED, C_TO))
+
+def plot_durations(cpu_index):
+ """ Plot per cpu durations """
+
+ file_name = 'cpu{:0>3}.csv'.format(cpu_index)
+ if os.path.exists(file_name):
+ output_png = "cpu%03d_durations.png" % cpu_index
+ g_plot = common_all_gnuplot_settings(output_png)
+# Should autoscale be used here? Should seconds be used here?
+ g_plot('set yrange [0:5000]')
+ g_plot('set ytics 0, 500')
+ g_plot('set title "{} : durations : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now()))
+ g_plot('set ylabel "Timer Duration (MilliSeconds)"')
+# override common
+ g_plot('set key off')
+ set_4_plot_linestyles(g_plot)
+ g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_DURATION))
+
+def plot_loads(cpu_index):
+ """ Plot per cpu loads """
+
+ file_name = 'cpu{:0>3}.csv'.format(cpu_index)
+ if os.path.exists(file_name):
+ output_png = "cpu%03d_loads.png" % cpu_index
+ g_plot = common_all_gnuplot_settings(output_png)
+ g_plot('set yrange [0:100]')
+ g_plot('set ytics 0, 10')
+ g_plot('set title "{} : loads : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now()))
+ g_plot('set ylabel "CPU load (percent)"')
+# override common
+ g_plot('set key off')
+ set_4_plot_linestyles(g_plot)
+ g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_LOAD))
+
+def plot_pstate_cpu_with_sample():
+ """ Plot all cpu information """
+
+ if os.path.exists('cpu.csv'):
+ output_png = 'all_cpu_pstates_vs_samples.png'
+ g_plot = common_all_gnuplot_settings(output_png)
+ g_plot('set yrange [0:40]')
+# override common
+ g_plot('set xlabel "Samples"')
+ g_plot('set ylabel "P-State"')
+ g_plot('set title "{} : cpu pstate vs. sample : {:%F %H:%M}"'.format(testname, datetime.now()))
+ title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).replace('\n', ' ')
+ plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_SAMPLE, C_TO)
+ g_plot('title_list = "{}"'.format(title_list))
+ g_plot(plot_str)
+
+def plot_pstate_cpu():
+ """ Plot all cpu information from csv files """
+
+ output_png = 'all_cpu_pstates.png'
+ g_plot = common_all_gnuplot_settings(output_png)
+ g_plot('set yrange [0:40]')
+ g_plot('set ylabel "P-State"')
+ g_plot('set title "{} : cpu pstates : {:%F %H:%M}"'.format(testname, datetime.now()))
+
+# the following command is really cool, but doesn't work with the CPU masking option because it aborts on the first missing file.
+# plot_str = 'plot for [i=0:*] file=sprintf("cpu%03d.csv",i) title_s=sprintf("cpu%03d",i) file using 16:7 pt 7 ps 1 title title_s'
+#
+ title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).replace('\n', ' ')
+ plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_TO)
+ g_plot('title_list = "{}"'.format(title_list))
+ g_plot(plot_str)
+
+def plot_load_cpu():
+ """ Plot all cpu loads """
+
+ output_png = 'all_cpu_loads.png'
+ g_plot = common_all_gnuplot_settings(output_png)
+ g_plot('set yrange [0:100]')
+ g_plot('set ylabel "CPU load (percent)"')
+ g_plot('set title "{} : cpu loads : {:%F %H:%M}"'.format(testname, datetime.now()))
+
+ title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).replace('\n', ' ')
+ plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_LOAD)
+ g_plot('title_list = "{}"'.format(title_list))
+ g_plot(plot_str)
+
+def plot_frequency_cpu():
+ """ Plot all cpu frequencies """
+
+ output_png = 'all_cpu_frequencies.png'
+ g_plot = common_all_gnuplot_settings(output_png)
+ g_plot('set yrange [0:4]')
+ g_plot('set ylabel "CPU Frequency (GHz)"')
+ g_plot('set title "{} : cpu frequencies : {:%F %H:%M}"'.format(testname, datetime.now()))
+
+ title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).replace('\n', ' ')
+ plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_FREQ)
+ g_plot('title_list = "{}"'.format(title_list))
+ g_plot(plot_str)
+
+def plot_duration_cpu():
+ """ Plot all cpu durations """
+
+ output_png = 'all_cpu_durations.png'
+ g_plot = common_all_gnuplot_settings(output_png)
+ g_plot('set yrange [0:5000]')
+ g_plot('set ytics 0, 500')
+ g_plot('set ylabel "Timer Duration (MilliSeconds)"')
+ g_plot('set title "{} : cpu durations : {:%F %H:%M}"'.format(testname, datetime.now()))
+
+ title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).replace('\n', ' ')
+ plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_DURATION)
+ g_plot('title_list = "{}"'.format(title_list))
+ g_plot(plot_str)
+
+def plot_scaled_cpu():
+ """ Plot all cpu scaled busy """
+
+ output_png = 'all_cpu_scaled.png'
+ g_plot = common_all_gnuplot_settings(output_png)
+# autoscale this one, no set y range
+ g_plot('set ylabel "Scaled Busy (Unitless)"')
+ g_plot('set title "{} : cpu scaled busy : {:%F %H:%M}"'.format(testname, datetime.now()))
+
+ title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).replace('\n', ' ')
+ plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_SCALED)
+ g_plot('title_list = "{}"'.format(title_list))
+ g_plot(plot_str)
+
+def plot_boost_cpu():
+ """ Plot all cpu IO Boosts """
+
+ output_png = 'all_cpu_boost.png'
+ g_plot = common_all_gnuplot_settings(output_png)
+ g_plot('set yrange [0:100]')
+ g_plot('set ylabel "CPU IO Boost (percent)"')
+ g_plot('set title "{} : cpu io boost : {:%F %H:%M}"'.format(testname, datetime.now()))
+
+ title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).replace('\n', ' ')
+ plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_BOOST)
+ g_plot('title_list = "{}"'.format(title_list))
+ g_plot(plot_str)
+
+def plot_ghz_cpu():
+ """ Plot all cpu tsc ghz """
+
+ output_png = 'all_cpu_ghz.png'
+ g_plot = common_all_gnuplot_settings(output_png)
+# autoscale this one, no set y range
+ g_plot('set ylabel "TSC Frequency (GHz)"')
+ g_plot('set title "{} : cpu TSC Frequencies (Sanity check calculation) : {:%F %H:%M}"'.format(testname, datetime.now()))
+
+ title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).replace('\n', ' ')
+ plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_GHZ)
+ g_plot('title_list = "{}"'.format(title_list))
+ g_plot(plot_str)
+
+def common_all_gnuplot_settings(output_png):
+ """ common gnuplot settings for multiple CPUs one one graph. """
+
+ g_plot = common_gnuplot_settings()
+ g_plot('set output "' + output_png + '"')
+ return(g_plot)
+
+def common_gnuplot_settings():
+ """ common gnuplot settings. """
+
+ g_plot = Gnuplot.Gnuplot(persist=1)
+# The following line is for rigor only. It seems to be assumed for .csv files
+ g_plot('set datafile separator \",\"')
+ g_plot('set ytics nomirror')
+ g_plot('set xtics nomirror')
+ g_plot('set xtics font ", 10"')
+ g_plot('set ytics font ", 10"')
+ g_plot('set tics out scale 1.0')
+ g_plot('set grid')
+ g_plot('set key out horiz')
+ g_plot('set key bot center')
+ g_plot('set key samplen 2 spacing .8 font ", 9"')
+ g_plot('set term png size 1200, 600')
+ g_plot('set title font ", 11"')
+ g_plot('set ylabel font ", 10"')
+ g_plot('set xlabel font ", 10"')
+ g_plot('set xlabel offset 0, 0.5')
+ g_plot('set xlabel "Elapsed Time (Seconds)"')
+ return(g_plot)
+
+def set_4_plot_linestyles(g_plot):
+ """ set the linestyles used for 4 plots in 1 graphs. """
+
+ g_plot('set style line 1 linetype 1 linecolor rgb "green" pointtype -1')
+ g_plot('set style line 2 linetype 1 linecolor rgb "red" pointtype -1')
+ g_plot('set style line 3 linetype 1 linecolor rgb "purple" pointtype -1')
+ g_plot('set style line 4 linetype 1 linecolor rgb "blue" pointtype -1')
+
+def store_csv(cpu_int, time_pre_dec, time_post_dec, core_busy, scaled, _from, _to, mperf, aperf, tsc, freq_ghz, io_boost, common_comm, load, duration_ms, sample_num, elapsed_time, tsc_ghz):
+ """ Store master csv file information """
+
+ global graph_data_present
+
+ if cpu_mask[cpu_int] == 0:
+ return
+
+ try:
+ f_handle = open('cpu.csv', 'a')
+ string_buffer = "CPU_%03u, %05u, %06u, %u, %u, %u, %u, %u, %u, %u, %.4f, %u, %.2f, %.3f, %u, %.3f, %.3f, %s\n" % (cpu_int, int(time_pre_dec), int(time_post_dec), int(core_busy), int(scaled), int(_from), int(_to), int(mperf), int(aperf), int(tsc), freq_ghz, int(io_boost), load, duration_ms, sample_num, elapsed_time, tsc_ghz, common_comm)
+ f_handle.write(string_buffer);
+ f_handle.close()
+ except:
+ print('IO error cpu.csv')
+ return
+
+ graph_data_present = True;
+
+def split_csv():
+ """ seperate the all csv file into per CPU csv files. """
+
+ global current_max_cpu
+
+ if os.path.exists('cpu.csv'):
+ for index in range(0, current_max_cpu + 1):
+ if cpu_mask[int(index)] != 0:
+ os.system('grep -m 1 common_cpu cpu.csv > cpu{:0>3}.csv'.format(index))
+ os.system('grep CPU_{:0>3} cpu.csv >> cpu{:0>3}.csv'.format(index, index))
+
+def cleanup_data_files():
+ """ clean up existing data files """
+
+ if os.path.exists('cpu.csv'):
+ os.remove('cpu.csv')
+ f_handle = open('cpu.csv', 'a')
+ f_handle.write('common_cpu, common_secs, common_usecs, core_busy, scaled_busy, from, to, mperf, aperf, tsc, freq, boost, load, duration_ms, sample_num, elapsed_time, tsc_ghz, common_comm')
+ f_handle.write('\n')
+ f_handle.close()
+
+def clear_trace_file():
+ """ Clear trace file """
+
+ try:
+ f_handle = open('/sys/kernel/debug/tracing/trace', 'w')
+ f_handle.close()
+ except:
+ print('IO error clearing trace file ')
+ quit()
+
+def enable_trace():
+ """ Enable trace """
+
+ try:
+ open('/sys/kernel/debug/tracing/events/power/pstate_sample/enable'
+ , 'w').write("1")
+ except:
+ print('IO error enabling trace ')
+ quit()
+
+def disable_trace():
+ """ Disable trace """
+
+ try:
+ open('/sys/kernel/debug/tracing/events/power/pstate_sample/enable'
+ , 'w').write("0")
+ except:
+ print('IO error disabling trace ')
+ quit()
+
+def set_trace_buffer_size():
+ """ Set trace buffer size """
+
+ try:
+ open('/sys/kernel/debug/tracing/buffer_size_kb'
+ , 'w').write("10240")
+ except:
+ print('IO error setting trace buffer size ')
+ quit()
+
+def read_trace_data(filename):
+ """ Read and parse trace data """
+
+ global current_max_cpu
+ global sample_num, last_sec_cpu, last_usec_cpu, start_time
+
+ try:
+ data = open(filename, 'r').read()
+ except:
+ print('Error opening ', filename)
+ quit()
+
+ for line in data.splitlines():
+ search_obj = \
+ re.search(r'(^(.*?)\[)((\d+)[^\]])(.*?)(\d+)([.])(\d+)(.*?core_busy=)(\d+)(.*?scaled=)(\d+)(.*?from=)(\d+)(.*?to=)(\d+)(.*?mperf=)(\d+)(.*?aperf=)(\d+)(.*?tsc=)(\d+)(.*?freq=)(\d+)'
+ , line)
+
+ if search_obj:
+ cpu = search_obj.group(3)
+ cpu_int = int(cpu)
+ cpu = str(cpu_int)
+
+ time_pre_dec = search_obj.group(6)
+ time_post_dec = search_obj.group(8)
+ core_busy = search_obj.group(10)
+ scaled = search_obj.group(12)
+ _from = search_obj.group(14)
+ _to = search_obj.group(16)
+ mperf = search_obj.group(18)
+ aperf = search_obj.group(20)
+ tsc = search_obj.group(22)
+ freq = search_obj.group(24)
+ common_comm = search_obj.group(2).replace(' ', '')
+
+ # Not all kernel versions have io_boost field
+ io_boost = '0'
+ search_obj = re.search(r'.*?io_boost=(\d+)', line)
+ if search_obj:
+ io_boost = search_obj.group(1)
+
+ if sample_num == 0 :
+ start_time = Decimal(time_pre_dec) + Decimal(time_post_dec) / Decimal(1000000)
+ sample_num += 1
+
+ if last_sec_cpu[cpu_int] == 0 :
+ last_sec_cpu[cpu_int] = time_pre_dec
+ last_usec_cpu[cpu_int] = time_post_dec
+ else :
+ duration_us = (int(time_pre_dec) - int(last_sec_cpu[cpu_int])) * 1000000 + (int(time_post_dec) - int(last_usec_cpu[cpu_int]))
+ duration_ms = Decimal(duration_us) / Decimal(1000)
+ last_sec_cpu[cpu_int] = time_pre_dec
+ last_usec_cpu[cpu_int] = time_post_dec
+ elapsed_time = Decimal(time_pre_dec) + Decimal(time_post_dec) / Decimal(1000000) - start_time
+ load = Decimal(int(mperf)*100)/ Decimal(tsc)
+ freq_ghz = Decimal(freq)/Decimal(1000000)
+# Sanity check calculation, typically anomalies indicate missed samples
+# However, check for 0 (should never occur)
+ tsc_ghz = Decimal(0)
+ if duration_ms != Decimal(0) :
+ tsc_ghz = Decimal(tsc)/duration_ms/Decimal(1000000)
+ store_csv(cpu_int, time_pre_dec, time_post_dec, core_busy, scaled, _from, _to, mperf, aperf, tsc, freq_ghz, io_boost, common_comm, load, duration_ms, sample_num, elapsed_time, tsc_ghz)
+
+ if cpu_int > current_max_cpu:
+ current_max_cpu = cpu_int
+# End of for each trace line loop
+# Now seperate the main overall csv file into per CPU csv files.
+ split_csv()
+
+interval = ""
+filename = ""
+cpu_list = ""
+testname = ""
+graph_data_present = False;
+
+valid1 = False
+valid2 = False
+
+cpu_mask = zeros((MAX_CPUS,), dtype=int)
+
+try:
+ opts, args = getopt.getopt(sys.argv[1:],"ht:i:c:n:",["help","trace_file=","interval=","cpu=","name="])
+except getopt.GetoptError:
+ print_help()
+ sys.exit(2)
+for opt, arg in opts:
+ if opt == '-h':
+ print()
+ sys.exit()
+ elif opt in ("-t", "--trace_file"):
+ valid1 = True
+ location = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
+ filename = os.path.join(location, arg)
+ elif opt in ("-i", "--interval"):
+ valid1 = True
+ interval = arg
+ elif opt in ("-c", "--cpu"):
+ cpu_list = arg
+ elif opt in ("-n", "--name"):
+ valid2 = True
+ testname = arg
+
+if not (valid1 and valid2):
+ print_help()
+ sys.exit()
+
+if cpu_list:
+ for p in re.split("[,]", cpu_list):
+ if int(p) < MAX_CPUS :
+ cpu_mask[int(p)] = 1
+else:
+ for i in range (0, MAX_CPUS):
+ cpu_mask[i] = 1
+
+if not os.path.exists('results'):
+ os.mkdir('results')
+
+os.chdir('results')
+if os.path.exists(testname):
+ print('The test name directory already exists. Please provide a unique test name. Test re-run not supported, yet.')
+ sys.exit()
+os.mkdir(testname)
+os.chdir(testname)
+
+# Temporary (or perhaps not)
+cur_version = sys.version_info
+print('python version (should be >= 2.7):')
+print(cur_version)
+
+# Left as "cleanup" for potential future re-run ability.
+cleanup_data_files()
+
+if interval:
+ filename = "/sys/kernel/debug/tracing/trace"
+ clear_trace_file()
+ set_trace_buffer_size()
+ enable_trace()
+ print('Sleeping for ', interval, 'seconds')
+ time.sleep(int(interval))
+ disable_trace()
+
+current_max_cpu = 0
+
+read_trace_data(filename)
+
+if graph_data_present == False:
+ print('No valid data to plot')
+ sys.exit(2)
+
+for cpu_no in range(0, current_max_cpu + 1):
+ plot_perf_busy_with_sample(cpu_no)
+ plot_perf_busy(cpu_no)
+ plot_durations(cpu_no)
+ plot_loads(cpu_no)
+
+plot_pstate_cpu_with_sample()
+plot_pstate_cpu()
+plot_load_cpu()
+plot_frequency_cpu()
+plot_duration_cpu()
+plot_scaled_cpu()
+plot_boost_cpu()
+plot_ghz_cpu()
+
+os.chdir('../../')
diff --git a/tools/scripts/Makefile.include b/tools/scripts/Makefile.include
index 8abbef1..621578a 100644
--- a/tools/scripts/Makefile.include
+++ b/tools/scripts/Makefile.include
@@ -32,7 +32,6 @@
EXTRA_WARNINGS += -Wpacked
EXTRA_WARNINGS += -Wredundant-decls
EXTRA_WARNINGS += -Wshadow
-EXTRA_WARNINGS += -Wstrict-aliasing=3
EXTRA_WARNINGS += -Wstrict-prototypes
EXTRA_WARNINGS += -Wswitch-default
EXTRA_WARNINGS += -Wswitch-enum
@@ -40,12 +39,26 @@
EXTRA_WARNINGS += -Wwrite-strings
EXTRA_WARNINGS += -Wformat
+ifneq ($(CC), clang)
+EXTRA_WARNINGS += -Wstrict-aliasing=3
+endif
+
ifneq ($(findstring $(MAKEFLAGS), w),w)
PRINT_DIR = --no-print-directory
else
NO_SUBDIR = :
endif
+ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
+ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
+ silent=1
+endif
+else # make-3.8x
+ifneq ($(filter s% -s%,$(MAKEFLAGS)),)
+ silent=1
+endif
+endif
+
#
# Define a callable command for descending to a new directory
#
@@ -58,7 +71,7 @@
QUIET_SUBDIR0 = +$(MAKE) $(COMMAND_O) -C # space to separate -C and subdir
QUIET_SUBDIR1 =
-ifneq ($(findstring $(MAKEFLAGS),s),s)
+ifneq ($(silent),1)
ifneq ($(V),1)
QUIET_CC = @echo ' CC '$@;
QUIET_CC_FPIC = @echo ' CC FPIC '$@;
diff --git a/tools/testing/selftests/locking/ww_mutex.sh b/tools/testing/selftests/locking/ww_mutex.sh
new file mode 100644
index 0000000..6905da9
--- /dev/null
+++ b/tools/testing/selftests/locking/ww_mutex.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Runs API tests for struct ww_mutex (Wait/Wound mutexes)
+
+if /sbin/modprobe -q test-ww_mutex; then
+ /sbin/modprobe -q -r test-ww_mutex
+ echo "locking/ww_mutex: ok"
+else
+ echo "locking/ww_mutex: [FAIL]"
+ exit 1
+fi
diff --git a/tools/testing/selftests/rcutorture/configs/lock/CFLIST b/tools/testing/selftests/rcutorture/configs/lock/CFLIST
index b9611c5..41bae58 100644
--- a/tools/testing/selftests/rcutorture/configs/lock/CFLIST
+++ b/tools/testing/selftests/rcutorture/configs/lock/CFLIST
@@ -4,3 +4,4 @@
LOCK04
LOCK05
LOCK06
+LOCK07
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK07 b/tools/testing/selftests/rcutorture/configs/lock/LOCK07
new file mode 100644
index 0000000..1d1da14
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/lock/LOCK07
@@ -0,0 +1,6 @@
+CONFIG_SMP=y
+CONFIG_NR_CPUS=4
+CONFIG_HOTPLUG_CPU=y
+CONFIG_PREEMPT_NONE=n
+CONFIG_PREEMPT_VOLUNTARY=n
+CONFIG_PREEMPT=y
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK07.boot b/tools/testing/selftests/rcutorture/configs/lock/LOCK07.boot
new file mode 100644
index 0000000..97dadd1
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/lock/LOCK07.boot
@@ -0,0 +1 @@
+locktorture.torture_type=ww_mutex_lock
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/CFcommon b/tools/testing/selftests/rcutorture/configs/rcu/CFcommon
index f824b4c..d2d2a86 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/CFcommon
+++ b/tools/testing/selftests/rcutorture/configs/rcu/CFcommon
@@ -1,5 +1,2 @@
CONFIG_RCU_TORTURE_TEST=y
CONFIG_PRINTK_TIME=y
-CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y
-CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y
-CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TINY01 b/tools/testing/selftests/rcutorture/configs/rcu/TINY01
index 0a63e07..6db705e 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TINY01
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TINY01
@@ -7,6 +7,7 @@
CONFIG_NO_HZ_IDLE=y
CONFIG_NO_HZ_FULL=n
CONFIG_RCU_TRACE=n
+#CHECK#CONFIG_RCU_STALL_COMMON=n
CONFIG_DEBUG_LOCK_ALLOC=n
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
CONFIG_PREEMPT_COUNT=n
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TINY02 b/tools/testing/selftests/rcutorture/configs/rcu/TINY02
index f1892e0..a59f768 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TINY02
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TINY02
@@ -8,7 +8,8 @@
CONFIG_NO_HZ_FULL=n
CONFIG_RCU_TRACE=y
CONFIG_PROVE_LOCKING=y
+CONFIG_PROVE_RCU_REPEATEDLY=y
#CHECK#CONFIG_PROVE_RCU=y
CONFIG_DEBUG_LOCK_ALLOC=y
-CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
+CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
CONFIG_PREEMPT_COUNT=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE01 b/tools/testing/selftests/rcutorture/configs/rcu/TREE01
index f572b87..359cb25 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE01
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE01
@@ -16,3 +16,6 @@
CONFIG_RCU_BOOST=n
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
CONFIG_RCU_EXPERT=y
+CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y
+CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y
+CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE02 b/tools/testing/selftests/rcutorture/configs/rcu/TREE02
index ef6a22c..c1ab592 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE02
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE02
@@ -20,3 +20,7 @@
CONFIG_RCU_BOOST=n
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
CONFIG_RCU_EXPERT=y
+CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y
+CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y
+CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y
+CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE03 b/tools/testing/selftests/rcutorture/configs/rcu/TREE03
index 7a17c50..3b93ee5 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE03
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE03
@@ -17,3 +17,6 @@
CONFIG_RCU_KTHREAD_PRIO=2
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
CONFIG_RCU_EXPERT=y
+CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y
+CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y
+CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE04 b/tools/testing/selftests/rcutorture/configs/rcu/TREE04
index 17cbe09..5af758e 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE04
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE04
@@ -19,3 +19,7 @@
CONFIG_DEBUG_LOCK_ALLOC=n
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
CONFIG_RCU_EXPERT=y
+CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y
+CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y
+CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y
+CONFIG_RCU_EQS_DEBUG=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE05 b/tools/testing/selftests/rcutorture/configs/rcu/TREE05
index 1257d32..d4cdc0d 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE05
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE05
@@ -19,3 +19,6 @@
#CHECK#CONFIG_PROVE_RCU=y
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
CONFIG_RCU_EXPERT=y
+CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y
+CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y
+CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE06 b/tools/testing/selftests/rcutorture/configs/rcu/TREE06
index d3e456b..4cb02bd 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE06
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE06
@@ -20,3 +20,6 @@
#CHECK#CONFIG_PROVE_RCU=y
CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
CONFIG_RCU_EXPERT=y
+CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y
+CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y
+CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE07 b/tools/testing/selftests/rcutorture/configs/rcu/TREE07
index 3956b41..b12a3ea 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE07
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE07
@@ -19,3 +19,6 @@
CONFIG_DEBUG_LOCK_ALLOC=n
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
CONFIG_RCU_EXPERT=y
+CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y
+CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y
+CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE08 b/tools/testing/selftests/rcutorture/configs/rcu/TREE08
index bb9b0c1..099cc63 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE08
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE08
@@ -17,8 +17,8 @@
CONFIG_RCU_NOCB_CPU=y
CONFIG_RCU_NOCB_CPU_ALL=y
CONFIG_DEBUG_LOCK_ALLOC=n
-CONFIG_PROVE_LOCKING=y
-#CHECK#CONFIG_PROVE_RCU=y
+CONFIG_PROVE_LOCKING=n
CONFIG_RCU_BOOST=n
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
CONFIG_RCU_EXPERT=y
+CONFIG_RCU_EQS_DEBUG=y
diff --git a/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt b/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt
index 4e2b189..364801b 100644
--- a/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt
+++ b/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt
@@ -14,6 +14,7 @@
CONFIG_PREEMPT -- Do half. (First three and #8.)
CONFIG_PROVE_LOCKING -- Do several, covering CONFIG_DEBUG_LOCK_ALLOC=y and not.
CONFIG_PROVE_RCU -- Hardwired to CONFIG_PROVE_LOCKING.
+CONFIG_PROVE_RCU_REPEATEDLY -- Do one.
CONFIG_RCU_BOOST -- one of PREEMPT_RCU.
CONFIG_RCU_KTHREAD_PRIO -- set to 2 for _BOOST testing.
CONFIG_RCU_FANOUT -- Cover hierarchy, but overlap with others.
@@ -25,7 +26,12 @@
CONFIG_RCU_NOCB_CPU_ZERO -- Do one.
CONFIG_RCU_TRACE -- Do half.
CONFIG_SMP -- Need one !SMP for PREEMPT_RCU.
-!RCU_EXPERT -- Do a few, but these have to be vanilla configurations.
+CONFIG_RCU_EXPERT=n -- Do a few, but these have to be vanilla configurations.
+CONFIG_RCU_EQS_DEBUG -- Do at least one for CONFIG_NO_HZ_FULL and not.
+CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP -- Do for all but a couple TREE scenarios.
+CONFIG_RCU_TORTURE_TEST_SLOW_INIT -- Do for all but a couple TREE scenarios.
+CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT -- Do for all but a couple TREE scenarios.
+
RCU-bh: Do one with PREEMPT and one with !PREEMPT.
RCU-sched: Do one with PREEMPT but not BOOST.
@@ -72,7 +78,30 @@
Always used in KVM testing.
+CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT_DELAY
+CONFIG_RCU_TORTURE_TEST_SLOW_INIT_DELAY
+CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP_DELAY
+
+ Inspection suffices, ignore.
+
CONFIG_PREEMPT_RCU
CONFIG_TREE_RCU
+CONFIG_TINY_RCU
- These are controlled by CONFIG_PREEMPT.
+ These are controlled by CONFIG_PREEMPT and/or CONFIG_SMP.
+
+CONFIG_SPARSE_RCU_POINTER
+
+ Makes sense only for sparse runs, not for kernel builds.
+
+CONFIG_SRCU
+CONFIG_TASKS_RCU
+
+ Selected by CONFIG_RCU_TORTURE_TEST, so cannot disable.
+
+CONFIG_RCU_TRACE
+
+ Implied by CONFIG_RCU_TRACE for Tree RCU.
+
+
+boot parameters ignored: TBD
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/.gitignore b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/.gitignore
new file mode 100644
index 0000000..712a3d4
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/.gitignore
@@ -0,0 +1 @@
+srcu.c
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/Makefile b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/Makefile
new file mode 100644
index 0000000..16b0155
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/Makefile
@@ -0,0 +1,16 @@
+all: srcu.c store_buffering
+
+LINUX_SOURCE = ../../../../../..
+
+modified_srcu_input = $(LINUX_SOURCE)/include/linux/srcu.h \
+ $(LINUX_SOURCE)/kernel/rcu/srcu.c
+
+modified_srcu_output = include/linux/srcu.h srcu.c
+
+include/linux/srcu.h: srcu.c
+
+srcu.c: modify_srcu.awk Makefile $(modified_srcu_input)
+ awk -f modify_srcu.awk $(modified_srcu_input) $(modified_srcu_output)
+
+store_buffering:
+ @cd tests/store_buffering; make
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/delay.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/delay.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/delay.h
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/export.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/export.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/export.h
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/mutex.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/mutex.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/mutex.h
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/percpu.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/percpu.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/percpu.h
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/preempt.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/preempt.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/preempt.h
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/rcupdate.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/rcupdate.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/rcupdate.h
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/sched.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/sched.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/sched.h
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/smp.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/smp.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/smp.h
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/workqueue.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/workqueue.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/workqueue.h
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/uapi/linux/types.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/uapi/linux/types.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/uapi/linux/types.h
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/.gitignore b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/.gitignore
new file mode 100644
index 0000000..1d016e6
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/.gitignore
@@ -0,0 +1 @@
+srcu.h
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/kconfig.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/kconfig.h
new file mode 100644
index 0000000..f2860dd
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/kconfig.h
@@ -0,0 +1 @@
+#include <LINUX_SOURCE/linux/kconfig.h>
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/types.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/types.h
new file mode 100644
index 0000000..4a3d538
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/types.h
@@ -0,0 +1,155 @@
+/*
+ * This header has been modifies to remove definitions of types that
+ * are defined in standard userspace headers or are problematic for some
+ * other reason.
+ */
+
+#ifndef _LINUX_TYPES_H
+#define _LINUX_TYPES_H
+
+#define __EXPORTED_HEADERS__
+#include <uapi/linux/types.h>
+
+#ifndef __ASSEMBLY__
+
+#define DECLARE_BITMAP(name, bits) \
+ unsigned long name[BITS_TO_LONGS(bits)]
+
+typedef __u32 __kernel_dev_t;
+
+/* bsd */
+typedef unsigned char u_char;
+typedef unsigned short u_short;
+typedef unsigned int u_int;
+typedef unsigned long u_long;
+
+/* sysv */
+typedef unsigned char unchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+
+#ifndef __BIT_TYPES_DEFINED__
+#define __BIT_TYPES_DEFINED__
+
+typedef __u8 u_int8_t;
+typedef __s8 int8_t;
+typedef __u16 u_int16_t;
+typedef __s16 int16_t;
+typedef __u32 u_int32_t;
+typedef __s32 int32_t;
+
+#endif /* !(__BIT_TYPES_DEFINED__) */
+
+typedef __u8 uint8_t;
+typedef __u16 uint16_t;
+typedef __u32 uint32_t;
+
+/* this is a special 64bit data type that is 8-byte aligned */
+#define aligned_u64 __u64 __attribute__((aligned(8)))
+#define aligned_be64 __be64 __attribute__((aligned(8)))
+#define aligned_le64 __le64 __attribute__((aligned(8)))
+
+/**
+ * The type used for indexing onto a disc or disc partition.
+ *
+ * Linux always considers sectors to be 512 bytes long independently
+ * of the devices real block size.
+ *
+ * blkcnt_t is the type of the inode's block count.
+ */
+#ifdef CONFIG_LBDAF
+typedef u64 sector_t;
+#else
+typedef unsigned long sector_t;
+#endif
+
+/*
+ * The type of an index into the pagecache.
+ */
+#define pgoff_t unsigned long
+
+/*
+ * A dma_addr_t can hold any valid DMA address, i.e., any address returned
+ * by the DMA API.
+ *
+ * If the DMA API only uses 32-bit addresses, dma_addr_t need only be 32
+ * bits wide. Bus addresses, e.g., PCI BARs, may be wider than 32 bits,
+ * but drivers do memory-mapped I/O to ioremapped kernel virtual addresses,
+ * so they don't care about the size of the actual bus addresses.
+ */
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+typedef u64 dma_addr_t;
+#else
+typedef u32 dma_addr_t;
+#endif
+
+#ifdef CONFIG_PHYS_ADDR_T_64BIT
+typedef u64 phys_addr_t;
+#else
+typedef u32 phys_addr_t;
+#endif
+
+typedef phys_addr_t resource_size_t;
+
+/*
+ * This type is the placeholder for a hardware interrupt number. It has to be
+ * big enough to enclose whatever representation is used by a given platform.
+ */
+typedef unsigned long irq_hw_number_t;
+
+typedef struct {
+ int counter;
+} atomic_t;
+
+#ifdef CONFIG_64BIT
+typedef struct {
+ long counter;
+} atomic64_t;
+#endif
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+struct hlist_head {
+ struct hlist_node *first;
+};
+
+struct hlist_node {
+ struct hlist_node *next, **pprev;
+};
+
+/**
+ * struct callback_head - callback structure for use with RCU and task_work
+ * @next: next update requests in a list
+ * @func: actual update function to call after the grace period.
+ *
+ * The struct is aligned to size of pointer. On most architectures it happens
+ * naturally due ABI requirements, but some architectures (like CRIS) have
+ * weird ABI and we need to ask it explicitly.
+ *
+ * The alignment is required to guarantee that bits 0 and 1 of @next will be
+ * clear under normal conditions -- as long as we use call_rcu(),
+ * call_rcu_bh(), call_rcu_sched(), or call_srcu() to queue callback.
+ *
+ * This guarantee is important for few reasons:
+ * - future call_rcu_lazy() will make use of lower bits in the pointer;
+ * - the structure shares storage spacer in struct page with @compound_head,
+ * which encode PageTail() in bit 0. The guarantee is needed to avoid
+ * false-positive PageTail().
+ */
+struct callback_head {
+ struct callback_head *next;
+ void (*func)(struct callback_head *head);
+} __attribute__((aligned(sizeof(void *))));
+#define rcu_head callback_head
+
+typedef void (*rcu_callback_t)(struct rcu_head *head);
+typedef void (*call_rcu_func_t)(struct rcu_head *head, rcu_callback_t func);
+
+/* clocksource cycle base type */
+typedef u64 cycle_t;
+
+#endif /* __ASSEMBLY__ */
+#endif /* _LINUX_TYPES_H */
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/modify_srcu.awk b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/modify_srcu.awk
new file mode 100755
index 0000000..8ff8904
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/modify_srcu.awk
@@ -0,0 +1,375 @@
+#!/bin/awk -f
+
+# Modify SRCU for formal verification. The first argument should be srcu.h and
+# the second should be srcu.c. Outputs modified srcu.h and srcu.c into the
+# current directory.
+
+BEGIN {
+ if (ARGC != 5) {
+ print "Usange: input.h input.c output.h output.c" > "/dev/stderr";
+ exit 1;
+ }
+ h_output = ARGV[3];
+ c_output = ARGV[4];
+ ARGC = 3;
+
+ # Tokenize using FS and not RS as FS supports regular expressions. Each
+ # record is one line of source, except that backslashed lines are
+ # combined. Comments are treated as field separators, as are quotes.
+ quote_regexp="\"([^\\\\\"]|\\\\.)*\"";
+ comment_regexp="\\/\\*([^*]|\\*+[^*/])*\\*\\/|\\/\\/.*(\n|$)";
+ FS="([ \\\\\t\n\v\f;,.=(){}+*/<>&|^-]|\\[|\\]|" comment_regexp "|" quote_regexp ")+";
+
+ inside_srcu_struct = 0;
+ inside_srcu_init_def = 0;
+ srcu_init_param_name = "";
+ in_macro = 0;
+ brace_nesting = 0;
+ paren_nesting = 0;
+
+ # Allow the manipulation of the last field separator after has been
+ # seen.
+ last_fs = "";
+ # Whether the last field separator was intended to be output.
+ last_fs_print = 0;
+
+ # rcu_batches stores the initialization for each instance of struct
+ # rcu_batch
+
+ in_comment = 0;
+
+ outputfile = "";
+}
+
+{
+ prev_outputfile = outputfile;
+ if (FILENAME ~ /\.h$/) {
+ outputfile = h_output;
+ if (FNR != NR) {
+ print "Incorrect file order" > "/dev/stderr";
+ exit 1;
+ }
+ }
+ else
+ outputfile = c_output;
+
+ if (prev_outputfile && outputfile != prev_outputfile) {
+ new_outputfile = outputfile;
+ outputfile = prev_outputfile;
+ update_fieldsep("", 0);
+ outputfile = new_outputfile;
+ }
+}
+
+# Combine the next line into $0.
+function combine_line() {
+ ret = getline next_line;
+ if (ret == 0) {
+ # Don't allow two consecutive getlines at the end of the file
+ if (eof_found) {
+ print "Error: expected more input." > "/dev/stderr";
+ exit 1;
+ } else {
+ eof_found = 1;
+ }
+ } else if (ret == -1) {
+ print "Error reading next line of file" FILENAME > "/dev/stderr";
+ exit 1;
+ }
+ $0 = $0 "\n" next_line;
+}
+
+# Combine backslashed lines and multiline comments.
+function combine_backslashes() {
+ while (/\\$|\/\*([^*]|\*+[^*\/])*\**$/) {
+ combine_line();
+ }
+}
+
+function read_line() {
+ combine_line();
+ combine_backslashes();
+}
+
+# Print out field separators and update variables that depend on them. Only
+# print if p is true. Call with sep="" and p=0 to print out the last field
+# separator.
+function update_fieldsep(sep, p) {
+ # Count braces
+ sep_tmp = sep;
+ gsub(quote_regexp "|" comment_regexp, "", sep_tmp);
+ while (1)
+ {
+ if (sub("[^{}()]*\\{", "", sep_tmp)) {
+ brace_nesting++;
+ continue;
+ }
+ if (sub("[^{}()]*\\}", "", sep_tmp)) {
+ brace_nesting--;
+ if (brace_nesting < 0) {
+ print "Unbalanced braces!" > "/dev/stderr";
+ exit 1;
+ }
+ continue;
+ }
+ if (sub("[^{}()]*\\(", "", sep_tmp)) {
+ paren_nesting++;
+ continue;
+ }
+ if (sub("[^{}()]*\\)", "", sep_tmp)) {
+ paren_nesting--;
+ if (paren_nesting < 0) {
+ print "Unbalanced parenthesis!" > "/dev/stderr";
+ exit 1;
+ }
+ continue;
+ }
+
+ break;
+ }
+
+ if (last_fs_print)
+ printf("%s", last_fs) > outputfile;
+ last_fs = sep;
+ last_fs_print = p;
+}
+
+# Shifts the fields down by n positions. Calls next if there are no more. If p
+# is true then print out field separators.
+function shift_fields(n, p) {
+ do {
+ if (match($0, FS) > 0) {
+ update_fieldsep(substr($0, RSTART, RLENGTH), p);
+ if (RSTART + RLENGTH <= length())
+ $0 = substr($0, RSTART + RLENGTH);
+ else
+ $0 = "";
+ } else {
+ update_fieldsep("", 0);
+ print "" > outputfile;
+ next;
+ }
+ } while (--n > 0);
+}
+
+# Shifts and prints the first n fields.
+function print_fields(n) {
+ do {
+ update_fieldsep("", 0);
+ printf("%s", $1) > outputfile;
+ shift_fields(1, 1);
+ } while (--n > 0);
+}
+
+{
+ combine_backslashes();
+}
+
+# Print leading FS
+{
+ if (match($0, "^(" FS ")+") > 0) {
+ update_fieldsep(substr($0, RSTART, RLENGTH), 1);
+ if (RSTART + RLENGTH <= length())
+ $0 = substr($0, RSTART + RLENGTH);
+ else
+ $0 = "";
+ }
+}
+
+# Parse the line.
+{
+ while (NF > 0) {
+ if ($1 == "struct" && NF < 3) {
+ read_line();
+ continue;
+ }
+
+ if (FILENAME ~ /\.h$/ && !inside_srcu_struct &&
+ brace_nesting == 0 && paren_nesting == 0 &&
+ $1 == "struct" && $2 == "srcu_struct" &&
+ $0 ~ "^struct(" FS ")+srcu_struct(" FS ")+\\{") {
+ inside_srcu_struct = 1;
+ print_fields(2);
+ continue;
+ }
+ if (inside_srcu_struct && brace_nesting == 0 &&
+ paren_nesting == 0) {
+ inside_srcu_struct = 0;
+ update_fieldsep("", 0);
+ for (name in rcu_batches)
+ print "extern struct rcu_batch " name ";" > outputfile;
+ }
+
+ if (inside_srcu_struct && $1 == "struct" && $2 == "rcu_batch") {
+ # Move rcu_batches outside of the struct.
+ rcu_batches[$3] = "";
+ shift_fields(3, 1);
+ sub(/;[[:space:]]*$/, "", last_fs);
+ continue;
+ }
+
+ if (FILENAME ~ /\.h$/ && !inside_srcu_init_def &&
+ $1 == "#define" && $2 == "__SRCU_STRUCT_INIT") {
+ inside_srcu_init_def = 1;
+ srcu_init_param_name = $3;
+ in_macro = 1;
+ print_fields(3);
+ continue;
+ }
+ if (inside_srcu_init_def && brace_nesting == 0 &&
+ paren_nesting == 0) {
+ inside_srcu_init_def = 0;
+ in_macro = 0;
+ continue;
+ }
+
+ if (inside_srcu_init_def && brace_nesting == 1 &&
+ paren_nesting == 0 && last_fs ~ /\.[[:space:]]*$/ &&
+ $1 ~ /^[[:alnum:]_]+$/) {
+ name = $1;
+ if (name in rcu_batches) {
+ # Remove the dot.
+ sub(/\.[[:space:]]*$/, "", last_fs);
+
+ old_record = $0;
+ do
+ shift_fields(1, 0);
+ while (last_fs !~ /,/ || paren_nesting > 0);
+ end_loc = length(old_record) - length($0);
+ end_loc += index(last_fs, ",") - length(last_fs);
+
+ last_fs = substr(last_fs, index(last_fs, ",") + 1);
+ last_fs_print = 1;
+
+ match(old_record, "^"name"("FS")+=");
+ start_loc = RSTART + RLENGTH;
+
+ len = end_loc - start_loc;
+ initializer = substr(old_record, start_loc, len);
+ gsub(srcu_init_param_name "\\.", "", initializer);
+ rcu_batches[name] = initializer;
+ continue;
+ }
+ }
+
+ # Don't include a nonexistent file
+ if (!in_macro && $1 == "#include" && /^#include[[:space:]]+"rcu\.h"/) {
+ update_fieldsep("", 0);
+ next;
+ }
+
+ # Ignore most preprocessor stuff.
+ if (!in_macro && $1 ~ /#/) {
+ break;
+ }
+
+ if (brace_nesting > 0 && $1 ~ "^[[:alnum:]_]+$" && NF < 2) {
+ read_line();
+ continue;
+ }
+ if (brace_nesting > 0 &&
+ $0 ~ "^[[:alnum:]_]+[[:space:]]*(\\.|->)[[:space:]]*[[:alnum:]_]+" &&
+ $2 in rcu_batches) {
+ # Make uses of rcu_batches global. Somewhat unreliable.
+ shift_fields(1, 0);
+ print_fields(1);
+ continue;
+ }
+
+ if ($1 == "static" && NF < 3) {
+ read_line();
+ continue;
+ }
+ if ($1 == "static" && ($2 == "bool" && $3 == "try_check_zero" ||
+ $2 == "void" && $3 == "srcu_flip")) {
+ shift_fields(1, 1);
+ print_fields(2);
+ continue;
+ }
+
+ # Distinguish between read-side and write-side memory barriers.
+ if ($1 == "smp_mb" && NF < 2) {
+ read_line();
+ continue;
+ }
+ if (match($0, /^smp_mb[[:space:]();\/*]*[[:alnum:]]/)) {
+ barrier_letter = substr($0, RLENGTH, 1);
+ if (barrier_letter ~ /A|D/)
+ new_barrier_name = "sync_smp_mb";
+ else if (barrier_letter ~ /B|C/)
+ new_barrier_name = "rs_smp_mb";
+ else {
+ print "Unrecognized memory barrier." > "/dev/null";
+ exit 1;
+ }
+
+ shift_fields(1, 1);
+ printf("%s", new_barrier_name) > outputfile;
+ continue;
+ }
+
+ # Skip definition of rcu_synchronize, since it is already
+ # defined in misc.h. Only present in old versions of srcu.
+ if (brace_nesting == 0 && paren_nesting == 0 &&
+ $1 == "struct" && $2 == "rcu_synchronize" &&
+ $0 ~ "^struct(" FS ")+rcu_synchronize(" FS ")+\\{") {
+ shift_fields(2, 0);
+ while (brace_nesting) {
+ if (NF < 2)
+ read_line();
+ shift_fields(1, 0);
+ }
+ }
+
+ # Skip definition of wakeme_after_rcu for the same reason
+ if (brace_nesting == 0 && $1 == "static" && $2 == "void" &&
+ $3 == "wakeme_after_rcu") {
+ while (NF < 5)
+ read_line();
+ shift_fields(3, 0);
+ do {
+ while (NF < 3)
+ read_line();
+ shift_fields(1, 0);
+ } while (paren_nesting || brace_nesting);
+ }
+
+ if ($1 ~ /^(unsigned|long)$/ && NF < 3) {
+ read_line();
+ continue;
+ }
+
+ # Give srcu_batches_completed the correct type for old SRCU.
+ if (brace_nesting == 0 && $1 == "long" &&
+ $2 == "srcu_batches_completed") {
+ update_fieldsep("", 0);
+ printf("unsigned ") > outputfile;
+ print_fields(2);
+ continue;
+ }
+ if (brace_nesting == 0 && $1 == "unsigned" && $2 == "long" &&
+ $3 == "srcu_batches_completed") {
+ print_fields(3);
+ continue;
+ }
+
+ # Just print out the input code by default.
+ print_fields(1);
+ }
+ update_fieldsep("", 0);
+ print > outputfile;
+ next;
+}
+
+END {
+ update_fieldsep("", 0);
+
+ if (brace_nesting != 0) {
+ print "Unbalanced braces!" > "/dev/stderr";
+ exit 1;
+ }
+
+ # Define the rcu_batches
+ for (name in rcu_batches)
+ print "struct rcu_batch " name " = " rcu_batches[name] ";" > c_output;
+}
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/assume.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/assume.h
new file mode 100644
index 0000000..a649554
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/assume.h
@@ -0,0 +1,16 @@
+#ifndef ASSUME_H
+#define ASSUME_H
+
+/* Provide an assumption macro that can be disabled for gcc. */
+#ifdef RUN
+#define assume(x) \
+ do { \
+ /* Evaluate x to suppress warnings. */ \
+ (void) (x); \
+ } while (0)
+
+#else
+#define assume(x) __CPROVER_assume(x)
+#endif
+
+#endif
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/barriers.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/barriers.h
new file mode 100644
index 0000000..6687acc
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/barriers.h
@@ -0,0 +1,41 @@
+#ifndef BARRIERS_H
+#define BARRIERS_H
+
+#define barrier() __asm__ __volatile__("" : : : "memory")
+
+#ifdef RUN
+#define smp_mb() __sync_synchronize()
+#define smp_mb__after_unlock_lock() __sync_synchronize()
+#else
+/*
+ * Copied from CBMC's implementation of __sync_synchronize(), which
+ * seems to be disabled by default.
+ */
+#define smp_mb() __CPROVER_fence("WWfence", "RRfence", "RWfence", "WRfence", \
+ "WWcumul", "RRcumul", "RWcumul", "WRcumul")
+#define smp_mb__after_unlock_lock() __CPROVER_fence("WWfence", "RRfence", "RWfence", "WRfence", \
+ "WWcumul", "RRcumul", "RWcumul", "WRcumul")
+#endif
+
+/*
+ * Allow memory barriers to be disabled in either the read or write side
+ * of SRCU individually.
+ */
+
+#ifndef NO_SYNC_SMP_MB
+#define sync_smp_mb() smp_mb()
+#else
+#define sync_smp_mb() do {} while (0)
+#endif
+
+#ifndef NO_READ_SIDE_SMP_MB
+#define rs_smp_mb() smp_mb()
+#else
+#define rs_smp_mb() do {} while (0)
+#endif
+
+#define ACCESS_ONCE(x) (*(volatile typeof(x) *) &(x))
+#define READ_ONCE(x) ACCESS_ONCE(x)
+#define WRITE_ONCE(x, val) (ACCESS_ONCE(x) = (val))
+
+#endif
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/bug_on.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/bug_on.h
new file mode 100644
index 0000000..2a80e91
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/bug_on.h
@@ -0,0 +1,13 @@
+#ifndef BUG_ON_H
+#define BUG_ON_H
+
+#include <assert.h>
+
+#define BUG() assert(0)
+#define BUG_ON(x) assert(!(x))
+
+/* Does it make sense to treat warnings as errors? */
+#define WARN() BUG()
+#define WARN_ON(x) (BUG_ON(x), false)
+
+#endif
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/combined_source.c b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/combined_source.c
new file mode 100644
index 0000000..29eb5d26
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/combined_source.c
@@ -0,0 +1,13 @@
+#include <config.h>
+
+/* Include all source files. */
+
+#include "include_srcu.c"
+
+#include "preempt.c"
+#include "misc.c"
+
+/* Used by test.c files */
+#include <pthread.h>
+#include <stdlib.h>
+#include <linux/srcu.h>
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/config.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/config.h
new file mode 100644
index 0000000..a60038a
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/config.h
@@ -0,0 +1,27 @@
+/* "Cheater" definitions based on restricted Kconfig choices. */
+
+#undef CONFIG_TINY_RCU
+#undef __CHECKER__
+#undef CONFIG_DEBUG_LOCK_ALLOC
+#undef CONFIG_DEBUG_OBJECTS_RCU_HEAD
+#undef CONFIG_HOTPLUG_CPU
+#undef CONFIG_MODULES
+#undef CONFIG_NO_HZ_FULL_SYSIDLE
+#undef CONFIG_PREEMPT_COUNT
+#undef CONFIG_PREEMPT_RCU
+#undef CONFIG_PROVE_RCU
+#undef CONFIG_RCU_NOCB_CPU
+#undef CONFIG_RCU_NOCB_CPU_ALL
+#undef CONFIG_RCU_STALL_COMMON
+#undef CONFIG_RCU_TRACE
+#undef CONFIG_RCU_USER_QS
+#undef CONFIG_TASKS_RCU
+#define CONFIG_TREE_RCU
+
+#define CONFIG_GENERIC_ATOMIC64
+
+#if NR_CPUS > 1
+#define CONFIG_SMP
+#else
+#undef CONFIG_SMP
+#endif
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/include_srcu.c b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/include_srcu.c
new file mode 100644
index 0000000..5ec582a
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/include_srcu.c
@@ -0,0 +1,31 @@
+#include <config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "int_typedefs.h"
+
+#include "barriers.h"
+#include "bug_on.h"
+#include "locks.h"
+#include "misc.h"
+#include "preempt.h"
+#include "percpu.h"
+#include "workqueues.h"
+
+#ifdef USE_SIMPLE_SYNC_SRCU
+#define synchronize_srcu(sp) synchronize_srcu_original(sp)
+#endif
+
+#include <srcu.c>
+
+#ifdef USE_SIMPLE_SYNC_SRCU
+#undef synchronize_srcu
+
+#include "simple_sync_srcu.c"
+#endif
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/int_typedefs.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/int_typedefs.h
new file mode 100644
index 0000000..3aad639
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/int_typedefs.h
@@ -0,0 +1,33 @@
+#ifndef INT_TYPEDEFS_H
+#define INT_TYPEDEFS_H
+
+#include <inttypes.h>
+
+typedef int8_t s8;
+typedef uint8_t u8;
+typedef int16_t s16;
+typedef uint16_t u16;
+typedef int32_t s32;
+typedef uint32_t u32;
+typedef int64_t s64;
+typedef uint64_t u64;
+
+typedef int8_t __s8;
+typedef uint8_t __u8;
+typedef int16_t __s16;
+typedef uint16_t __u16;
+typedef int32_t __s32;
+typedef uint32_t __u32;
+typedef int64_t __s64;
+typedef uint64_t __u64;
+
+#define S8_C(x) INT8_C(x)
+#define U8_C(x) UINT8_C(x)
+#define S16_C(x) INT16_C(x)
+#define U16_C(x) UINT16_C(x)
+#define S32_C(x) INT32_C(x)
+#define U32_C(x) UINT32_C(x)
+#define S64_C(x) INT64_C(x)
+#define U64_C(x) UINT64_C(x)
+
+#endif
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/locks.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/locks.h
new file mode 100644
index 0000000..3560046
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/locks.h
@@ -0,0 +1,220 @@
+#ifndef LOCKS_H
+#define LOCKS_H
+
+#include <limits.h>
+#include <pthread.h>
+#include <stdbool.h>
+
+#include "assume.h"
+#include "bug_on.h"
+#include "preempt.h"
+
+int nondet_int(void);
+
+#define __acquire(x)
+#define __acquires(x)
+#define __release(x)
+#define __releases(x)
+
+/* Only use one lock mechanism. Select which one. */
+#ifdef PTHREAD_LOCK
+struct lock_impl {
+ pthread_mutex_t mutex;
+};
+
+static inline void lock_impl_lock(struct lock_impl *lock)
+{
+ BUG_ON(pthread_mutex_lock(&lock->mutex));
+}
+
+static inline void lock_impl_unlock(struct lock_impl *lock)
+{
+ BUG_ON(pthread_mutex_unlock(&lock->mutex));
+}
+
+static inline bool lock_impl_trylock(struct lock_impl *lock)
+{
+ int err = pthread_mutex_trylock(&lock->mutex);
+
+ if (!err)
+ return true;
+ else if (err == EBUSY)
+ return false;
+ BUG();
+}
+
+static inline void lock_impl_init(struct lock_impl *lock)
+{
+ pthread_mutex_init(&lock->mutex, NULL);
+}
+
+#define LOCK_IMPL_INITIALIZER {.mutex = PTHREAD_MUTEX_INITIALIZER}
+
+#else /* !defined(PTHREAD_LOCK) */
+/* Spinlock that assumes that it always gets the lock immediately. */
+
+struct lock_impl {
+ bool locked;
+};
+
+static inline bool lock_impl_trylock(struct lock_impl *lock)
+{
+#ifdef RUN
+ /* TODO: Should this be a test and set? */
+ return __sync_bool_compare_and_swap(&lock->locked, false, true);
+#else
+ __CPROVER_atomic_begin();
+ bool old_locked = lock->locked;
+ lock->locked = true;
+ __CPROVER_atomic_end();
+
+ /* Minimal barrier to prevent accesses leaking out of lock. */
+ __CPROVER_fence("RRfence", "RWfence");
+
+ return !old_locked;
+#endif
+}
+
+static inline void lock_impl_lock(struct lock_impl *lock)
+{
+ /*
+ * CBMC doesn't support busy waiting, so just assume that the
+ * lock is available.
+ */
+ assume(lock_impl_trylock(lock));
+
+ /*
+ * If the lock was already held by this thread then the assumption
+ * is unsatisfiable (deadlock).
+ */
+}
+
+static inline void lock_impl_unlock(struct lock_impl *lock)
+{
+#ifdef RUN
+ BUG_ON(!__sync_bool_compare_and_swap(&lock->locked, true, false));
+#else
+ /* Minimal barrier to prevent accesses leaking out of lock. */
+ __CPROVER_fence("RWfence", "WWfence");
+
+ __CPROVER_atomic_begin();
+ bool old_locked = lock->locked;
+ lock->locked = false;
+ __CPROVER_atomic_end();
+
+ BUG_ON(!old_locked);
+#endif
+}
+
+static inline void lock_impl_init(struct lock_impl *lock)
+{
+ lock->locked = false;
+}
+
+#define LOCK_IMPL_INITIALIZER {.locked = false}
+
+#endif /* !defined(PTHREAD_LOCK) */
+
+/*
+ * Implement spinlocks using the lock mechanism. Wrap the lock to prevent mixing
+ * locks of different types.
+ */
+typedef struct {
+ struct lock_impl internal_lock;
+} spinlock_t;
+
+#define SPIN_LOCK_UNLOCKED {.internal_lock = LOCK_IMPL_INITIALIZER}
+#define __SPIN_LOCK_UNLOCKED(x) SPIN_LOCK_UNLOCKED
+#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED
+
+static inline void spin_lock_init(spinlock_t *lock)
+{
+ lock_impl_init(&lock->internal_lock);
+}
+
+static inline void spin_lock(spinlock_t *lock)
+{
+ /*
+ * Spin locks also need to be removed in order to eliminate all
+ * memory barriers. They are only used by the write side anyway.
+ */
+#ifndef NO_SYNC_SMP_MB
+ preempt_disable();
+ lock_impl_lock(&lock->internal_lock);
+#endif
+}
+
+static inline void spin_unlock(spinlock_t *lock)
+{
+#ifndef NO_SYNC_SMP_MB
+ lock_impl_unlock(&lock->internal_lock);
+ preempt_enable();
+#endif
+}
+
+/* Don't bother with interrupts */
+#define spin_lock_irq(lock) spin_lock(lock)
+#define spin_unlock_irq(lock) spin_unlock(lock)
+#define spin_lock_irqsave(lock, flags) spin_lock(lock)
+#define spin_unlock_irqrestore(lock, flags) spin_unlock(lock)
+
+/*
+ * This is supposed to return an int, but I think that a bool should work as
+ * well.
+ */
+static inline bool spin_trylock(spinlock_t *lock)
+{
+#ifndef NO_SYNC_SMP_MB
+ preempt_disable();
+ return lock_impl_trylock(&lock->internal_lock);
+#else
+ return true;
+#endif
+}
+
+struct completion {
+ /* Hopefuly this won't overflow. */
+ unsigned int count;
+};
+
+#define COMPLETION_INITIALIZER(x) {.count = 0}
+#define DECLARE_COMPLETION(x) struct completion x = COMPLETION_INITIALIZER(x)
+#define DECLARE_COMPLETION_ONSTACK(x) DECLARE_COMPLETION(x)
+
+static inline void init_completion(struct completion *c)
+{
+ c->count = 0;
+}
+
+static inline void wait_for_completion(struct completion *c)
+{
+ unsigned int prev_count = __sync_fetch_and_sub(&c->count, 1);
+
+ assume(prev_count);
+}
+
+static inline void complete(struct completion *c)
+{
+ unsigned int prev_count = __sync_fetch_and_add(&c->count, 1);
+
+ BUG_ON(prev_count == UINT_MAX);
+}
+
+/* This function probably isn't very useful for CBMC. */
+static inline bool try_wait_for_completion(struct completion *c)
+{
+ BUG();
+}
+
+static inline bool completion_done(struct completion *c)
+{
+ return c->count;
+}
+
+/* TODO: Implement complete_all */
+static inline void complete_all(struct completion *c)
+{
+ BUG();
+}
+
+#endif
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/misc.c b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/misc.c
new file mode 100644
index 0000000..ca892e3
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/misc.c
@@ -0,0 +1,11 @@
+#include <config.h>
+
+#include "misc.h"
+#include "bug_on.h"
+
+struct rcu_head;
+
+void wakeme_after_rcu(struct rcu_head *head)
+{
+ BUG();
+}
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/misc.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/misc.h
new file mode 100644
index 0000000..aca5003
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/misc.h
@@ -0,0 +1,58 @@
+#ifndef MISC_H
+#define MISC_H
+
+#include "assume.h"
+#include "int_typedefs.h"
+#include "locks.h"
+
+#include <linux/types.h>
+
+/* Probably won't need to deal with bottom halves. */
+static inline void local_bh_disable(void) {}
+static inline void local_bh_enable(void) {}
+
+#define MODULE_ALIAS(X)
+#define module_param(...)
+#define EXPORT_SYMBOL_GPL(x)
+
+#define container_of(ptr, type, member) ({ \
+ const typeof(((type *)0)->member) *__mptr = (ptr); \
+ (type *)((char *)__mptr - offsetof(type, member)); \
+})
+
+#ifndef USE_SIMPLE_SYNC_SRCU
+/* Abuse udelay to make sure that busy loops terminate. */
+#define udelay(x) assume(0)
+
+#else
+
+/* The simple custom synchronize_srcu is ok with try_check_zero failing. */
+#define udelay(x) do { } while (0)
+#endif
+
+#define trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c) \
+ do { } while (0)
+
+#define notrace
+
+/* Avoid including rcupdate.h */
+struct rcu_synchronize {
+ struct rcu_head head;
+ struct completion completion;
+};
+
+void wakeme_after_rcu(struct rcu_head *head);
+
+#define rcu_lock_acquire(a) do { } while (0)
+#define rcu_lock_release(a) do { } while (0)
+#define rcu_lockdep_assert(c, s) do { } while (0)
+#define RCU_LOCKDEP_WARN(c, s) do { } while (0)
+
+/* Let CBMC non-deterministically choose switch between normal and expedited. */
+bool rcu_gp_is_normal(void);
+bool rcu_gp_is_expedited(void);
+
+/* Do the same for old versions of rcu. */
+#define rcu_expedited (rcu_gp_is_expedited())
+
+#endif
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/percpu.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/percpu.h
new file mode 100644
index 0000000..3de5a49
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/percpu.h
@@ -0,0 +1,92 @@
+#ifndef PERCPU_H
+#define PERCPU_H
+
+#include <stddef.h>
+#include "bug_on.h"
+#include "preempt.h"
+
+#define __percpu
+
+/* Maximum size of any percpu data. */
+#define PERCPU_OFFSET (4 * sizeof(long))
+
+/* Ignore alignment, as CBMC doesn't care about false sharing. */
+#define alloc_percpu(type) __alloc_percpu(sizeof(type), 1)
+
+static inline void *__alloc_percpu(size_t size, size_t align)
+{
+ BUG();
+ return NULL;
+}
+
+static inline void free_percpu(void *ptr)
+{
+ BUG();
+}
+
+#define per_cpu_ptr(ptr, cpu) \
+ ((typeof(ptr)) ((char *) (ptr) + PERCPU_OFFSET * cpu))
+
+#define __this_cpu_inc(pcp) __this_cpu_add(pcp, 1)
+#define __this_cpu_dec(pcp) __this_cpu_sub(pcp, 1)
+#define __this_cpu_sub(pcp, n) __this_cpu_add(pcp, -(typeof(pcp)) (n))
+
+#define this_cpu_inc(pcp) this_cpu_add(pcp, 1)
+#define this_cpu_dec(pcp) this_cpu_sub(pcp, 1)
+#define this_cpu_sub(pcp, n) this_cpu_add(pcp, -(typeof(pcp)) (n))
+
+/* Make CBMC use atomics to work around bug. */
+#ifdef RUN
+#define THIS_CPU_ADD_HELPER(ptr, x) (*(ptr) += (x))
+#else
+/*
+ * Split the atomic into a read and a write so that it has the least
+ * possible ordering.
+ */
+#define THIS_CPU_ADD_HELPER(ptr, x) \
+ do { \
+ typeof(ptr) this_cpu_add_helper_ptr = (ptr); \
+ typeof(ptr) this_cpu_add_helper_x = (x); \
+ typeof(*ptr) this_cpu_add_helper_temp; \
+ __CPROVER_atomic_begin(); \
+ this_cpu_add_helper_temp = *(this_cpu_add_helper_ptr); \
+ __CPROVER_atomic_end(); \
+ this_cpu_add_helper_temp += this_cpu_add_helper_x; \
+ __CPROVER_atomic_begin(); \
+ *(this_cpu_add_helper_ptr) = this_cpu_add_helper_temp; \
+ __CPROVER_atomic_end(); \
+ } while (0)
+#endif
+
+/*
+ * For some reason CBMC needs an atomic operation even though this is percpu
+ * data.
+ */
+#define __this_cpu_add(pcp, n) \
+ do { \
+ BUG_ON(preemptible()); \
+ THIS_CPU_ADD_HELPER(per_cpu_ptr(&(pcp), thread_cpu_id), \
+ (typeof(pcp)) (n)); \
+ } while (0)
+
+#define this_cpu_add(pcp, n) \
+ do { \
+ int this_cpu_add_impl_cpu = get_cpu(); \
+ THIS_CPU_ADD_HELPER(per_cpu_ptr(&(pcp), this_cpu_add_impl_cpu), \
+ (typeof(pcp)) (n)); \
+ put_cpu(); \
+ } while (0)
+
+/*
+ * This will cause a compiler warning because of the cast from char[][] to
+ * type*. This will cause a compile time error if type is too big.
+ */
+#define DEFINE_PER_CPU(type, name) \
+ char name[NR_CPUS][PERCPU_OFFSET]; \
+ typedef char percpu_too_big_##name \
+ [sizeof(type) > PERCPU_OFFSET ? -1 : 1]
+
+#define for_each_possible_cpu(cpu) \
+ for ((cpu) = 0; (cpu) < NR_CPUS; ++(cpu))
+
+#endif
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/preempt.c b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/preempt.c
new file mode 100644
index 0000000..4f1b068
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/preempt.c
@@ -0,0 +1,78 @@
+#include <config.h>
+
+#include "preempt.h"
+
+#include "assume.h"
+#include "locks.h"
+
+/* Support NR_CPUS of at most 64 */
+#define CPU_PREEMPTION_LOCKS_INIT0 LOCK_IMPL_INITIALIZER
+#define CPU_PREEMPTION_LOCKS_INIT1 \
+ CPU_PREEMPTION_LOCKS_INIT0, CPU_PREEMPTION_LOCKS_INIT0
+#define CPU_PREEMPTION_LOCKS_INIT2 \
+ CPU_PREEMPTION_LOCKS_INIT1, CPU_PREEMPTION_LOCKS_INIT1
+#define CPU_PREEMPTION_LOCKS_INIT3 \
+ CPU_PREEMPTION_LOCKS_INIT2, CPU_PREEMPTION_LOCKS_INIT2
+#define CPU_PREEMPTION_LOCKS_INIT4 \
+ CPU_PREEMPTION_LOCKS_INIT3, CPU_PREEMPTION_LOCKS_INIT3
+#define CPU_PREEMPTION_LOCKS_INIT5 \
+ CPU_PREEMPTION_LOCKS_INIT4, CPU_PREEMPTION_LOCKS_INIT4
+
+/*
+ * Simulate disabling preemption by locking a particular cpu. NR_CPUS
+ * should be the actual number of cpus, not just the maximum.
+ */
+struct lock_impl cpu_preemption_locks[NR_CPUS] = {
+ CPU_PREEMPTION_LOCKS_INIT0
+#if (NR_CPUS - 1) & 1
+ , CPU_PREEMPTION_LOCKS_INIT0
+#endif
+#if (NR_CPUS - 1) & 2
+ , CPU_PREEMPTION_LOCKS_INIT1
+#endif
+#if (NR_CPUS - 1) & 4
+ , CPU_PREEMPTION_LOCKS_INIT2
+#endif
+#if (NR_CPUS - 1) & 8
+ , CPU_PREEMPTION_LOCKS_INIT3
+#endif
+#if (NR_CPUS - 1) & 16
+ , CPU_PREEMPTION_LOCKS_INIT4
+#endif
+#if (NR_CPUS - 1) & 32
+ , CPU_PREEMPTION_LOCKS_INIT5
+#endif
+};
+
+#undef CPU_PREEMPTION_LOCKS_INIT0
+#undef CPU_PREEMPTION_LOCKS_INIT1
+#undef CPU_PREEMPTION_LOCKS_INIT2
+#undef CPU_PREEMPTION_LOCKS_INIT3
+#undef CPU_PREEMPTION_LOCKS_INIT4
+#undef CPU_PREEMPTION_LOCKS_INIT5
+
+__thread int thread_cpu_id;
+__thread int preempt_disable_count;
+
+void preempt_disable(void)
+{
+ BUG_ON(preempt_disable_count < 0 || preempt_disable_count == INT_MAX);
+
+ if (preempt_disable_count++)
+ return;
+
+ thread_cpu_id = nondet_int();
+ assume(thread_cpu_id >= 0);
+ assume(thread_cpu_id < NR_CPUS);
+ lock_impl_lock(&cpu_preemption_locks[thread_cpu_id]);
+}
+
+void preempt_enable(void)
+{
+ BUG_ON(preempt_disable_count < 1);
+
+ if (--preempt_disable_count)
+ return;
+
+ lock_impl_unlock(&cpu_preemption_locks[thread_cpu_id]);
+}
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/preempt.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/preempt.h
new file mode 100644
index 0000000..2f95ee0
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/preempt.h
@@ -0,0 +1,58 @@
+#ifndef PREEMPT_H
+#define PREEMPT_H
+
+#include <stdbool.h>
+
+#include "bug_on.h"
+
+/* This flag contains garbage if preempt_disable_count is 0. */
+extern __thread int thread_cpu_id;
+
+/* Support recursive preemption disabling. */
+extern __thread int preempt_disable_count;
+
+void preempt_disable(void);
+void preempt_enable(void);
+
+static inline void preempt_disable_notrace(void)
+{
+ preempt_disable();
+}
+
+static inline void preempt_enable_no_resched(void)
+{
+ preempt_enable();
+}
+
+static inline void preempt_enable_notrace(void)
+{
+ preempt_enable();
+}
+
+static inline int preempt_count(void)
+{
+ return preempt_disable_count;
+}
+
+static inline bool preemptible(void)
+{
+ return !preempt_count();
+}
+
+static inline int get_cpu(void)
+{
+ preempt_disable();
+ return thread_cpu_id;
+}
+
+static inline void put_cpu(void)
+{
+ preempt_enable();
+}
+
+static inline void might_sleep(void)
+{
+ BUG_ON(preempt_disable_count);
+}
+
+#endif
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/simple_sync_srcu.c b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/simple_sync_srcu.c
new file mode 100644
index 0000000..ac9cbc6
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/simple_sync_srcu.c
@@ -0,0 +1,50 @@
+#include <config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "int_typedefs.h"
+
+#include "barriers.h"
+#include "bug_on.h"
+#include "locks.h"
+#include "misc.h"
+#include "preempt.h"
+#include "percpu.h"
+#include "workqueues.h"
+
+#include <linux/srcu.h>
+
+/* Functions needed from modify_srcu.c */
+bool try_check_zero(struct srcu_struct *sp, int idx, int trycount);
+void srcu_flip(struct srcu_struct *sp);
+
+/* Simpler implementation of synchronize_srcu that ignores batching. */
+void synchronize_srcu(struct srcu_struct *sp)
+{
+ int idx;
+ /*
+ * This code assumes that try_check_zero will succeed anyway,
+ * so there is no point in multiple tries.
+ */
+ const int trycount = 1;
+
+ might_sleep();
+
+ /* Ignore the lock, as multiple writers aren't working yet anyway. */
+
+ idx = 1 ^ (sp->completed & 1);
+
+ /* For comments see srcu_advance_batches. */
+
+ assume(try_check_zero(sp, idx, trycount));
+
+ srcu_flip(sp);
+
+ assume(try_check_zero(sp, idx^1, trycount));
+}
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/workqueues.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/workqueues.h
new file mode 100644
index 0000000..e58c8df
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/workqueues.h
@@ -0,0 +1,102 @@
+#ifndef WORKQUEUES_H
+#define WORKQUEUES_H
+
+#include <stdbool.h>
+
+#include "barriers.h"
+#include "bug_on.h"
+#include "int_typedefs.h"
+
+#include <linux/types.h>
+
+/* Stub workqueue implementation. */
+
+struct work_struct;
+typedef void (*work_func_t)(struct work_struct *work);
+void delayed_work_timer_fn(unsigned long __data);
+
+struct work_struct {
+/* atomic_long_t data; */
+ unsigned long data;
+
+ struct list_head entry;
+ work_func_t func;
+#ifdef CONFIG_LOCKDEP
+ struct lockdep_map lockdep_map;
+#endif
+};
+
+struct timer_list {
+ struct hlist_node entry;
+ unsigned long expires;
+ void (*function)(unsigned long);
+ unsigned long data;
+ u32 flags;
+ int slack;
+};
+
+struct delayed_work {
+ struct work_struct work;
+ struct timer_list timer;
+
+ /* target workqueue and CPU ->timer uses to queue ->work */
+ struct workqueue_struct *wq;
+ int cpu;
+};
+
+
+static inline bool schedule_work(struct work_struct *work)
+{
+ BUG();
+ return true;
+}
+
+static inline bool schedule_work_on(int cpu, struct work_struct *work)
+{
+ BUG();
+ return true;
+}
+
+static inline bool queue_work(struct workqueue_struct *wq,
+ struct work_struct *work)
+{
+ BUG();
+ return true;
+}
+
+static inline bool queue_delayed_work(struct workqueue_struct *wq,
+ struct delayed_work *dwork,
+ unsigned long delay)
+{
+ BUG();
+ return true;
+}
+
+#define INIT_WORK(w, f) \
+ do { \
+ (w)->data = 0; \
+ (w)->func = (f); \
+ } while (0)
+
+#define INIT_DELAYED_WORK(w, f) INIT_WORK(&(w)->work, (f))
+
+#define __WORK_INITIALIZER(n, f) { \
+ .data = 0, \
+ .entry = { &(n).entry, &(n).entry }, \
+ .func = f \
+ }
+
+/* Don't bother initializing timer. */
+#define __DELAYED_WORK_INITIALIZER(n, f, tflags) { \
+ .work = __WORK_INITIALIZER((n).work, (f)), \
+ }
+
+#define DECLARE_WORK(n, f) \
+ struct workqueue_struct n = __WORK_INITIALIZER
+
+#define DECLARE_DELAYED_WORK(n, f) \
+ struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f, 0)
+
+#define system_power_efficient_wq ((struct workqueue_struct *) NULL)
+
+#endif
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/.gitignore b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/.gitignore
new file mode 100644
index 0000000..f47cb20
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/.gitignore
@@ -0,0 +1 @@
+*.out
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/Makefile b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/Makefile
new file mode 100644
index 0000000..3a3aee1
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/Makefile
@@ -0,0 +1,11 @@
+CBMC_FLAGS = -I../.. -I../../src -I../../include -I../../empty_includes -32 -pointer-check -mm pso
+
+all:
+ for i in ./*.pass; do \
+ echo $$i ; \
+ CBMC_FLAGS="$(CBMC_FLAGS)" sh ../test_script.sh --should-pass $$i > $$i.out 2>&1 ; \
+ done
+ for i in ./*.fail; do \
+ echo $$i ; \
+ CBMC_FLAGS="$(CBMC_FLAGS)" sh ../test_script.sh --should-fail $$i > $$i.out 2>&1 ; \
+ done
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/assert_end.fail b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/assert_end.fail
new file mode 100644
index 0000000..40c8075
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/assert_end.fail
@@ -0,0 +1 @@
+test_cbmc_options="-DASSERT_END"
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force.fail b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force.fail
new file mode 100644
index 0000000..ada5baf
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force.fail
@@ -0,0 +1 @@
+test_cbmc_options="-DFORCE_FAILURE"
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force2.fail b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force2.fail
new file mode 100644
index 0000000..8fe00c8
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force2.fail
@@ -0,0 +1 @@
+test_cbmc_options="-DFORCE_FAILURE_2"
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force3.fail b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force3.fail
new file mode 100644
index 0000000..612ed67
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force3.fail
@@ -0,0 +1 @@
+test_cbmc_options="-DFORCE_FAILURE_3"
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/main.pass b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/main.pass
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/main.pass
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/test.c b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/test.c
new file mode 100644
index 0000000..470b110
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/test.c
@@ -0,0 +1,72 @@
+#include <src/combined_source.c>
+
+int x;
+int y;
+
+int __unbuffered_tpr_x;
+int __unbuffered_tpr_y;
+
+DEFINE_SRCU(ss);
+
+void rcu_reader(void)
+{
+ int idx;
+
+#ifndef FORCE_FAILURE_3
+ idx = srcu_read_lock(&ss);
+#endif
+ might_sleep();
+
+ __unbuffered_tpr_y = READ_ONCE(y);
+#ifdef FORCE_FAILURE
+ srcu_read_unlock(&ss, idx);
+ idx = srcu_read_lock(&ss);
+#endif
+ WRITE_ONCE(x, 1);
+
+#ifndef FORCE_FAILURE_3
+ srcu_read_unlock(&ss, idx);
+#endif
+ might_sleep();
+}
+
+void *thread_update(void *arg)
+{
+ WRITE_ONCE(y, 1);
+#ifndef FORCE_FAILURE_2
+ synchronize_srcu(&ss);
+#endif
+ might_sleep();
+ __unbuffered_tpr_x = READ_ONCE(x);
+
+ return NULL;
+}
+
+void *thread_process_reader(void *arg)
+{
+ rcu_reader();
+
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ pthread_t tu;
+ pthread_t tpr;
+
+ if (pthread_create(&tu, NULL, thread_update, NULL))
+ abort();
+ if (pthread_create(&tpr, NULL, thread_process_reader, NULL))
+ abort();
+ if (pthread_join(tu, NULL))
+ abort();
+ if (pthread_join(tpr, NULL))
+ abort();
+ assert(__unbuffered_tpr_y != 0 || __unbuffered_tpr_x != 0);
+
+#ifdef ASSERT_END
+ assert(0);
+#endif
+
+ return 0;
+}
diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/test_script.sh b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/test_script.sh
new file mode 100755
index 0000000..d154597
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/test_script.sh
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+# This script expects a mode (either --should-pass or --should-fail) followed by
+# an input file. The script uses the following environment variables. The test C
+# source file is expected to be named test.c in the directory containing the
+# input file.
+#
+# CBMC: The command to run CBMC. Default: cbmc
+# CBMC_FLAGS: Additional flags to pass to CBMC
+# NR_CPUS: Number of cpus to run tests with. Default specified by the test
+# SYNC_SRCU_MODE: Choose implementation of synchronize_srcu. Defaults to simple.
+# kernel: Version included in the linux kernel source.
+# simple: Use try_check_zero directly.
+#
+# The input file is a script that is sourced by this file. It can define any of
+# the following variables to configure the test.
+#
+# test_cbmc_options: Extra options to pass to CBMC.
+# min_cpus_fail: Minimum number of CPUs (NR_CPUS) for verification to fail.
+# The test is expected to pass if it is run with fewer. (Only
+# useful for .fail files)
+# default_cpus: Quantity of CPUs to use for the test, if not specified on the
+# command line. Default: Larger of 2 and MIN_CPUS_FAIL.
+
+set -e
+
+if test "$#" -ne 2; then
+ echo "Expected one option followed by an input file" 1>&2
+ exit 99
+fi
+
+if test "x$1" = "x--should-pass"; then
+ should_pass="yes"
+elif test "x$1" = "x--should-fail"; then
+ should_pass="no"
+else
+ echo "Unrecognized argument '$1'" 1>&2
+
+ # Exit code 99 indicates a hard error.
+ exit 99
+fi
+
+CBMC=${CBMC:-cbmc}
+
+SYNC_SRCU_MODE=${SYNC_SRCU_MODE:-simple}
+
+case ${SYNC_SRCU_MODE} in
+kernel) sync_srcu_mode_flags="" ;;
+simple) sync_srcu_mode_flags="-DUSE_SIMPLE_SYNC_SRCU" ;;
+
+*)
+ echo "Unrecognized argument '${SYNC_SRCU_MODE}'" 1>&2
+ exit 99
+ ;;
+esac
+
+min_cpus_fail=1
+
+c_file=`dirname "$2"`/test.c
+
+# Source the input file.
+. $2
+
+if test ${min_cpus_fail} -gt 2; then
+ default_default_cpus=${min_cpus_fail}
+else
+ default_default_cpus=2
+fi
+default_cpus=${default_cpus:-${default_default_cpus}}
+cpus=${NR_CPUS:-${default_cpus}}
+
+# Check if there are two few cpus to make the test fail.
+if test $cpus -lt ${min_cpus_fail:-0}; then
+ should_pass="yes"
+fi
+
+cbmc_opts="-DNR_CPUS=${cpus} ${sync_srcu_mode_flags} ${test_cbmc_options} ${CBMC_FLAGS}"
+
+echo "Running CBMC: ${CBMC} ${cbmc_opts} ${c_file}"
+if ${CBMC} ${cbmc_opts} "${c_file}"; then
+ # Verification successful. Make sure that it was supposed to verify.
+ test "x${should_pass}" = xyes
+else
+ cbmc_exit_status=$?
+
+ # An exit status of 10 indicates a failed verification.
+ # (see cbmc_parse_optionst::do_bmc in the CBMC source code)
+ if test ${cbmc_exit_status} -eq 10 && test "x${should_pass}" = xno; then
+ :
+ else
+ echo "CBMC returned ${cbmc_exit_status} exit status" 1>&2
+
+ # Parse errors have exit status 6. Any other type of error
+ # should be considered a hard error.
+ if test ${cbmc_exit_status} -ne 6 && \
+ test ${cbmc_exit_status} -ne 10; then
+ exit 99
+ else
+ exit 1
+ fi
+ fi
+fi
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 8c1cb42..83d8b1c 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -5,12 +5,12 @@
.PHONY: all all_32 all_64 warn_32bit_failure clean
TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_syscall test_mremap_vdso \
- check_initial_reg_state sigreturn ldt_gdt iopl \
+ check_initial_reg_state sigreturn ldt_gdt iopl mpx-mini-test \
protection_keys test_vdso
TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \
test_FCMOV test_FCOMI test_FISTTP \
vdso_restorer
-TARGETS_C_64BIT_ONLY := fsgsbase
+TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip
TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY)
TARGETS_C_64BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_64BIT_ONLY)
diff --git a/tools/testing/selftests/x86/sysret_rip.c b/tools/testing/selftests/x86/sysret_rip.c
new file mode 100644
index 0000000..d85ec5b
--- /dev/null
+++ b/tools/testing/selftests/x86/sysret_rip.c
@@ -0,0 +1,195 @@
+/*
+ * sigreturn.c - tests that x86 avoids Intel SYSRET pitfalls
+ * Copyright (c) 2014-2016 Andrew Lutomirski
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <sys/signal.h>
+#include <sys/ucontext.h>
+#include <sys/syscall.h>
+#include <err.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <setjmp.h>
+#include <sys/user.h>
+#include <sys/mman.h>
+#include <assert.h>
+
+
+asm (
+ ".pushsection \".text\", \"ax\"\n\t"
+ ".balign 4096\n\t"
+ "test_page: .globl test_page\n\t"
+ ".fill 4094,1,0xcc\n\t"
+ "test_syscall_insn:\n\t"
+ "syscall\n\t"
+ ".ifne . - test_page - 4096\n\t"
+ ".error \"test page is not one page long\"\n\t"
+ ".endif\n\t"
+ ".popsection"
+ );
+
+extern const char test_page[];
+static void const *current_test_page_addr = test_page;
+
+static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
+ int flags)
+{
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = handler;
+ sa.sa_flags = SA_SIGINFO | flags;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(sig, &sa, 0))
+ err(1, "sigaction");
+}
+
+static void clearhandler(int sig)
+{
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(sig, &sa, 0))
+ err(1, "sigaction");
+}
+
+/* State used by our signal handlers. */
+static gregset_t initial_regs;
+
+static volatile unsigned long rip;
+
+static void sigsegv_for_sigreturn_test(int sig, siginfo_t *info, void *ctx_void)
+{
+ ucontext_t *ctx = (ucontext_t*)ctx_void;
+
+ if (rip != ctx->uc_mcontext.gregs[REG_RIP]) {
+ printf("[FAIL]\tRequested RIP=0x%lx but got RIP=0x%lx\n",
+ rip, (unsigned long)ctx->uc_mcontext.gregs[REG_RIP]);
+ fflush(stdout);
+ _exit(1);
+ }
+
+ memcpy(&ctx->uc_mcontext.gregs, &initial_regs, sizeof(gregset_t));
+
+ printf("[OK]\tGot SIGSEGV at RIP=0x%lx\n", rip);
+}
+
+static void sigusr1(int sig, siginfo_t *info, void *ctx_void)
+{
+ ucontext_t *ctx = (ucontext_t*)ctx_void;
+
+ memcpy(&initial_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t));
+
+ /* Set IP and CX to match so that SYSRET can happen. */
+ ctx->uc_mcontext.gregs[REG_RIP] = rip;
+ ctx->uc_mcontext.gregs[REG_RCX] = rip;
+
+ /* R11 and EFLAGS should already match. */
+ assert(ctx->uc_mcontext.gregs[REG_EFL] ==
+ ctx->uc_mcontext.gregs[REG_R11]);
+
+ sethandler(SIGSEGV, sigsegv_for_sigreturn_test, SA_RESETHAND);
+
+ return;
+}
+
+static void test_sigreturn_to(unsigned long ip)
+{
+ rip = ip;
+ printf("[RUN]\tsigreturn to 0x%lx\n", ip);
+ raise(SIGUSR1);
+}
+
+static jmp_buf jmpbuf;
+
+static void sigsegv_for_fallthrough(int sig, siginfo_t *info, void *ctx_void)
+{
+ ucontext_t *ctx = (ucontext_t*)ctx_void;
+
+ if (rip != ctx->uc_mcontext.gregs[REG_RIP]) {
+ printf("[FAIL]\tExpected SIGSEGV at 0x%lx but got RIP=0x%lx\n",
+ rip, (unsigned long)ctx->uc_mcontext.gregs[REG_RIP]);
+ fflush(stdout);
+ _exit(1);
+ }
+
+ siglongjmp(jmpbuf, 1);
+}
+
+static void test_syscall_fallthrough_to(unsigned long ip)
+{
+ void *new_address = (void *)(ip - 4096);
+ void *ret;
+
+ printf("[RUN]\tTrying a SYSCALL that falls through to 0x%lx\n", ip);
+
+ ret = mremap((void *)current_test_page_addr, 4096, 4096,
+ MREMAP_MAYMOVE | MREMAP_FIXED, new_address);
+ if (ret == MAP_FAILED) {
+ if (ip <= (1UL << 47) - PAGE_SIZE) {
+ err(1, "mremap to %p", new_address);
+ } else {
+ printf("[OK]\tmremap to %p failed\n", new_address);
+ return;
+ }
+ }
+
+ if (ret != new_address)
+ errx(1, "mremap malfunctioned: asked for %p but got %p\n",
+ new_address, ret);
+
+ current_test_page_addr = new_address;
+ rip = ip;
+
+ if (sigsetjmp(jmpbuf, 1) == 0) {
+ asm volatile ("call *%[syscall_insn]" :: "a" (SYS_getpid),
+ [syscall_insn] "rm" (ip - 2));
+ errx(1, "[FAIL]\tSyscall trampoline returned");
+ }
+
+ printf("[OK]\tWe survived\n");
+}
+
+int main()
+{
+ /*
+ * When the kernel returns from a slow-path syscall, it will
+ * detect whether SYSRET is appropriate. If it incorrectly
+ * thinks that SYSRET is appropriate when RIP is noncanonical,
+ * it'll crash on Intel CPUs.
+ */
+ sethandler(SIGUSR1, sigusr1, 0);
+ for (int i = 47; i < 64; i++)
+ test_sigreturn_to(1UL<<i);
+
+ clearhandler(SIGUSR1);
+
+ sethandler(SIGSEGV, sigsegv_for_fallthrough, 0);
+
+ /* One extra test to check that we didn't screw up the mremap logic. */
+ test_syscall_fallthrough_to((1UL << 47) - 2*PAGE_SIZE);
+
+ /* These are the interesting cases. */
+ for (int i = 47; i < 64; i++) {
+ test_syscall_fallthrough_to((1UL<<i) - PAGE_SIZE);
+ test_syscall_fallthrough_to(1UL<<i);
+ }
+
+ return 0;
+}