Merge branch 'pm-cpufreq'
* pm-cpufreq:
cpufreq: cpufreq-dt: fix potential double put of cpu OF node
cpufreq: cpu0: rename driver and internals to 'cpufreq_dt'
cpufreq: ppc-corenet: remove duplicate update of cpu_data
cpufreq: Replace strnicmp with strncasecmp
cpufreq: powernv: Set the cpus to nominal frequency during reboot/kexec
cpufreq: powernv: Set the pstate of the last hotplugged out cpu in policy->cpus to minimum
cpufreq: Allow stop CPU callback to be used by all cpufreq drivers
cpufreq: cpu0: Make allocate_resources() work for any CPU
cpufreq: cpu0: try regulators with name "cpu-supply"
cpufreq: cpu0: Move per-cluster initialization code to ->init()
cpufreq: cpu0: use dev_{err|warn|dbg} instead of pr_{err|warn|debug}
cpufreq: cpu0: print relevant error when we defer probe
cpufreq: cpu0: don't validate clock on clk_put()
cpufreq: cpu0: Update Module Author
cpufreq: Add support for per-policy driver data
diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt
index 298e2f6..6fd0f15 100644
--- a/Documentation/devicetree/bindings/arm/cpus.txt
+++ b/Documentation/devicetree/bindings/arm/cpus.txt
@@ -219,6 +219,12 @@
Value type: <phandle>
Definition: Specifies the ACC[2] node associated with this CPU.
+ - cpu-idle-states
+ Usage: Optional
+ Value type: <prop-encoded-array>
+ Definition:
+ # List of phandles to idle state nodes supported
+ by this cpu [3].
Example 1 (dual-cluster big.LITTLE system 32-bit):
@@ -415,3 +421,5 @@
--
[1] arm/msm/qcom,saw2.txt
[2] arm/msm/qcom,kpss-acc.txt
+[3] ARM Linux kernel documentation - idle states bindings
+ Documentation/devicetree/bindings/arm/idle-states.txt
diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
index 8b4f7b7f..abde1ea 100644
--- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
+++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
@@ -8,6 +8,8 @@
* samsung,exynos4210-pd - for exynos4210 type power domain.
- reg: physical base address of the controller and length of memory mapped
region.
+- #power-domain-cells: number of cells in power domain specifier;
+ must be 0.
Optional Properties:
- clocks: List of clock handles. The parent clocks of the input clocks to the
@@ -29,6 +31,7 @@
lcd0: power-domain-lcd0 {
compatible = "samsung,exynos4210-pd";
reg = <0x10023C00 0x10>;
+ #power-domain-cells = <0>;
};
mfc_pd: power-domain@10044060 {
@@ -37,12 +40,8 @@
clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MOUT_SW_ACLK333>,
<&clock CLK_MOUT_USER_ACLK333>;
clock-names = "oscclk", "pclk0", "clk0";
+ #power-domain-cells = <0>;
};
-Example of the node using power domain:
-
- node {
- /* ... */
- samsung,power-domain = <&lcd0>;
- /* ... */
- };
+See Documentation/devicetree/bindings/power/power_domain.txt for description
+of consumer-side bindings.
diff --git a/Documentation/devicetree/bindings/arm/idle-states.txt b/Documentation/devicetree/bindings/arm/idle-states.txt
new file mode 100644
index 0000000..37375c7
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/idle-states.txt
@@ -0,0 +1,679 @@
+==========================================
+ARM idle states binding description
+==========================================
+
+==========================================
+1 - Introduction
+==========================================
+
+ARM systems contain HW capable of managing power consumption dynamically,
+where cores can be put in different low-power states (ranging from simple
+wfi to power gating) according to OS PM policies. The CPU states representing
+the range of dynamic idle states that a processor can enter at run-time, can be
+specified through device tree bindings representing the parameters required
+to enter/exit specific idle states on a given processor.
+
+According to the Server Base System Architecture document (SBSA, [3]), the
+power states an ARM CPU can be put into are identified by the following list:
+
+- Running
+- Idle_standby
+- Idle_retention
+- Sleep
+- Off
+
+The power states described in the SBSA document define the basic CPU states on
+top of which ARM platforms implement power management schemes that allow an OS
+PM implementation to put the processor in different idle states (which include
+states listed above; "off" state is not an idle state since it does not have
+wake-up capabilities, hence it is not considered in this document).
+
+Idle state parameters (eg entry latency) are platform specific and need to be
+characterized with bindings that provide the required information to OS PM
+code so that it can build the required tables and use them at runtime.
+
+The device tree binding definition for ARM idle states is the subject of this
+document.
+
+===========================================
+2 - idle-states definitions
+===========================================
+
+Idle states are characterized for a specific system through a set of
+timing and energy related properties, that underline the HW behaviour
+triggered upon idle states entry and exit.
+
+The following diagram depicts the CPU execution phases and related timing
+properties required to enter and exit an idle state:
+
+..__[EXEC]__|__[PREP]__|__[ENTRY]__|__[IDLE]__|__[EXIT]__|__[EXEC]__..
+ | | | | |
+
+ |<------ entry ------->|
+ | latency |
+ |<- exit ->|
+ | latency |
+ |<-------- min-residency -------->|
+ |<------- wakeup-latency ------->|
+
+ Diagram 1: CPU idle state execution phases
+
+EXEC: Normal CPU execution.
+
+PREP: Preparation phase before committing the hardware to idle mode
+ like cache flushing. This is abortable on pending wake-up
+ event conditions. The abort latency is assumed to be negligible
+ (i.e. less than the ENTRY + EXIT duration). If aborted, CPU
+ goes back to EXEC. This phase is optional. If not abortable,
+ this should be included in the ENTRY phase instead.
+
+ENTRY: The hardware is committed to idle mode. This period must run
+ to completion up to IDLE before anything else can happen.
+
+IDLE: This is the actual energy-saving idle period. This may last
+ between 0 and infinite time, until a wake-up event occurs.
+
+EXIT: Period during which the CPU is brought back to operational
+ mode (EXEC).
+
+entry-latency: Worst case latency required to enter the idle state. The
+exit-latency may be guaranteed only after entry-latency has passed.
+
+min-residency: Minimum period, including preparation and entry, for a given
+idle state to be worthwhile energywise.
+
+wakeup-latency: Maximum delay between the signaling of a wake-up event and the
+CPU being able to execute normal code again. If not specified, this is assumed
+to be entry-latency + exit-latency.
+
+These timing parameters can be used by an OS in different circumstances.
+
+An idle CPU requires the expected min-residency time to select the most
+appropriate idle state based on the expected expiry time of the next IRQ
+(ie wake-up) that causes the CPU to return to the EXEC phase.
+
+An operating system scheduler may need to compute the shortest wake-up delay
+for CPUs in the system by detecting how long will it take to get a CPU out
+of an idle state, eg:
+
+wakeup-delay = exit-latency + max(entry-latency - (now - entry-timestamp), 0)
+
+In other words, the scheduler can make its scheduling decision by selecting
+(eg waking-up) the CPU with the shortest wake-up latency.
+The wake-up latency must take into account the entry latency if that period
+has not expired. The abortable nature of the PREP period can be ignored
+if it cannot be relied upon (e.g. the PREP deadline may occur much sooner than
+the worst case since it depends on the CPU operating conditions, ie caches
+state).
+
+An OS has to reliably probe the wakeup-latency since some devices can enforce
+latency constraints guarantees to work properly, so the OS has to detect the
+worst case wake-up latency it can incur if a CPU is allowed to enter an
+idle state, and possibly to prevent that to guarantee reliable device
+functioning.
+
+The min-residency time parameter deserves further explanation since it is
+expressed in time units but must factor in energy consumption coefficients.
+
+The energy consumption of a cpu when it enters a power state can be roughly
+characterised by the following graph:
+
+ |
+ |
+ |
+ e |
+ n | /---
+ e | /------
+ r | /------
+ g | /-----
+ y | /------
+ | ----
+ | /|
+ | / |
+ | / |
+ | / |
+ | / |
+ | / |
+ |/ |
+ -----|-------+----------------------------------
+ 0| 1 time(ms)
+
+ Graph 1: Energy vs time example
+
+The graph is split in two parts delimited by time 1ms on the X-axis.
+The graph curve with X-axis values = { x | 0 < x < 1ms } has a steep slope
+and denotes the energy costs incurred whilst entering and leaving the idle
+state.
+The graph curve in the area delimited by X-axis values = {x | x > 1ms } has
+shallower slope and essentially represents the energy consumption of the idle
+state.
+
+min-residency is defined for a given idle state as the minimum expected
+residency time for a state (inclusive of preparation and entry) after
+which choosing that state become the most energy efficient option. A good
+way to visualise this, is by taking the same graph above and comparing some
+states energy consumptions plots.
+
+For sake of simplicity, let's consider a system with two idle states IDLE1,
+and IDLE2:
+
+ |
+ |
+ |
+ | /-- IDLE1
+ e | /---
+ n | /----
+ e | /---
+ r | /-----/--------- IDLE2
+ g | /-------/---------
+ y | ------------ /---|
+ | / /---- |
+ | / /--- |
+ | / /---- |
+ | / /--- |
+ | --- |
+ | / |
+ | / |
+ |/ | time
+ ---/----------------------------+------------------------
+ |IDLE1-energy < IDLE2-energy | IDLE2-energy < IDLE1-energy
+ |
+ IDLE2-min-residency
+
+ Graph 2: idle states min-residency example
+
+In graph 2 above, that takes into account idle states entry/exit energy
+costs, it is clear that if the idle state residency time (ie time till next
+wake-up IRQ) is less than IDLE2-min-residency, IDLE1 is the better idle state
+choice energywise.
+
+This is mainly down to the fact that IDLE1 entry/exit energy costs are lower
+than IDLE2.
+
+However, the lower power consumption (ie shallower energy curve slope) of idle
+state IDLE2 implies that after a suitable time, IDLE2 becomes more energy
+efficient.
+
+The time at which IDLE2 becomes more energy efficient than IDLE1 (and other
+shallower states in a system with multiple idle states) is defined
+IDLE2-min-residency and corresponds to the time when energy consumption of
+IDLE1 and IDLE2 states breaks even.
+
+The definitions provided in this section underpin the idle states
+properties specification that is the subject of the following sections.
+
+===========================================
+3 - idle-states node
+===========================================
+
+ARM processor idle states are defined within the idle-states node, which is
+a direct child of the cpus node [1] and provides a container where the
+processor idle states, defined as device tree nodes, are listed.
+
+- idle-states node
+
+ Usage: Optional - On ARM systems, it is a container of processor idle
+ states nodes. If the system does not provide CPU
+ power management capabilities or the processor just
+ supports idle_standby an idle-states node is not
+ required.
+
+ Description: idle-states node is a container node, where its
+ subnodes describe the CPU idle states.
+
+ Node name must be "idle-states".
+
+ The idle-states node's parent node must be the cpus node.
+
+ The idle-states node's child nodes can be:
+
+ - one or more state nodes
+
+ Any other configuration is considered invalid.
+
+ An idle-states node defines the following properties:
+
+ - entry-method
+ Value type: <stringlist>
+ Usage and definition depend on ARM architecture version.
+ # On ARM v8 64-bit this property is required and must
+ be one of:
+ - "psci" (see bindings in [2])
+ # On ARM 32-bit systems this property is optional
+
+The nodes describing the idle states (state) can only be defined within the
+idle-states node, any other configuration is considered invalid and therefore
+must be ignored.
+
+===========================================
+4 - state node
+===========================================
+
+A state node represents an idle state description and must be defined as
+follows:
+
+- state node
+
+ Description: must be child of the idle-states node
+
+ The state node name shall follow standard device tree naming
+ rules ([5], 2.2.1 "Node names"), in particular state nodes which
+ are siblings within a single common parent must be given a unique name.
+
+ The idle state entered by executing the wfi instruction (idle_standby
+ SBSA,[3][4]) is considered standard on all ARM platforms and therefore
+ must not be listed.
+
+ With the definitions provided above, the following list represents
+ the valid properties for a state node:
+
+ - compatible
+ Usage: Required
+ Value type: <stringlist>
+ Definition: Must be "arm,idle-state".
+
+ - local-timer-stop
+ Usage: See definition
+ Value type: <none>
+ Definition: if present the CPU local timer control logic is
+ lost on state entry, otherwise it is retained.
+
+ - entry-latency-us
+ Usage: Required
+ Value type: <prop-encoded-array>
+ Definition: u32 value representing worst case latency in
+ microseconds required to enter the idle state.
+ The exit-latency-us duration may be guaranteed
+ only after entry-latency-us has passed.
+
+ - exit-latency-us
+ Usage: Required
+ Value type: <prop-encoded-array>
+ Definition: u32 value representing worst case latency
+ in microseconds required to exit the idle state.
+
+ - min-residency-us
+ Usage: Required
+ Value type: <prop-encoded-array>
+ Definition: u32 value representing minimum residency duration
+ in microseconds, inclusive of preparation and
+ entry, for this idle state to be considered
+ worthwhile energy wise (refer to section 2 of
+ this document for a complete description).
+
+ - wakeup-latency-us:
+ Usage: Optional
+ Value type: <prop-encoded-array>
+ Definition: u32 value representing maximum delay between the
+ signaling of a wake-up event and the CPU being
+ able to execute normal code again. If omitted,
+ this is assumed to be equal to:
+
+ entry-latency-us + exit-latency-us
+
+ It is important to supply this value on systems
+ where the duration of PREP phase (see diagram 1,
+ section 2) is non-neglibigle.
+ In such systems entry-latency-us + exit-latency-us
+ will exceed wakeup-latency-us by this duration.
+
+ In addition to the properties listed above, a state node may require
+ additional properties specifics to the entry-method defined in the
+ idle-states node, please refer to the entry-method bindings
+ documentation for properties definitions.
+
+===========================================
+4 - Examples
+===========================================
+
+Example 1 (ARM 64-bit, 16-cpu system, PSCI enable-method):
+
+cpus {
+ #size-cells = <0>;
+ #address-cells = <2>;
+
+ CPU0: cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57";
+ reg = <0x0 0x0>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+ &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU1: cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57";
+ reg = <0x0 0x1>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+ &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU2: cpu@100 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57";
+ reg = <0x0 0x100>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+ &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU3: cpu@101 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57";
+ reg = <0x0 0x101>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+ &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU4: cpu@10000 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57";
+ reg = <0x0 0x10000>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+ &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU5: cpu@10001 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57";
+ reg = <0x0 0x10001>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+ &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU6: cpu@10100 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57";
+ reg = <0x0 0x10100>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+ &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU7: cpu@10101 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57";
+ reg = <0x0 0x10101>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+ &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU8: cpu@100000000 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x1 0x0>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+ &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU9: cpu@100000001 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x1 0x1>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+ &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU10: cpu@100000100 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x1 0x100>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+ &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU11: cpu@100000101 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x1 0x101>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+ &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU12: cpu@100010000 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x1 0x10000>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+ &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU13: cpu@100010001 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x1 0x10001>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+ &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU14: cpu@100010100 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x1 0x10100>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+ &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU15: cpu@100010101 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x1 0x10101>;
+ enable-method = "psci";
+ cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+ &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+ };
+
+ idle-states {
+ entry-method = "arm,psci";
+
+ CPU_RETENTION_0_0: cpu-retention-0-0 {
+ compatible = "arm,idle-state";
+ arm,psci-suspend-param = <0x0010000>;
+ entry-latency-us = <20>;
+ exit-latency-us = <40>;
+ min-residency-us = <80>;
+ };
+
+ CLUSTER_RETENTION_0: cluster-retention-0 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ arm,psci-suspend-param = <0x1010000>;
+ entry-latency-us = <50>;
+ exit-latency-us = <100>;
+ min-residency-us = <250>;
+ wakeup-latency-us = <130>;
+ };
+
+ CPU_SLEEP_0_0: cpu-sleep-0-0 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ arm,psci-suspend-param = <0x0010000>;
+ entry-latency-us = <250>;
+ exit-latency-us = <500>;
+ min-residency-us = <950>;
+ };
+
+ CLUSTER_SLEEP_0: cluster-sleep-0 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ arm,psci-suspend-param = <0x1010000>;
+ entry-latency-us = <600>;
+ exit-latency-us = <1100>;
+ min-residency-us = <2700>;
+ wakeup-latency-us = <1500>;
+ };
+
+ CPU_RETENTION_1_0: cpu-retention-1-0 {
+ compatible = "arm,idle-state";
+ arm,psci-suspend-param = <0x0010000>;
+ entry-latency-us = <20>;
+ exit-latency-us = <40>;
+ min-residency-us = <90>;
+ };
+
+ CLUSTER_RETENTION_1: cluster-retention-1 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ arm,psci-suspend-param = <0x1010000>;
+ entry-latency-us = <50>;
+ exit-latency-us = <100>;
+ min-residency-us = <270>;
+ wakeup-latency-us = <100>;
+ };
+
+ CPU_SLEEP_1_0: cpu-sleep-1-0 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ arm,psci-suspend-param = <0x0010000>;
+ entry-latency-us = <70>;
+ exit-latency-us = <100>;
+ min-residency-us = <300>;
+ wakeup-latency-us = <150>;
+ };
+
+ CLUSTER_SLEEP_1: cluster-sleep-1 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ arm,psci-suspend-param = <0x1010000>;
+ entry-latency-us = <500>;
+ exit-latency-us = <1200>;
+ min-residency-us = <3500>;
+ wakeup-latency-us = <1300>;
+ };
+ };
+
+};
+
+Example 2 (ARM 32-bit, 8-cpu system, two clusters):
+
+cpus {
+ #size-cells = <0>;
+ #address-cells = <1>;
+
+ CPU0: cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x0>;
+ cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU1: cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x1>;
+ cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU2: cpu@2 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x2>;
+ cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU3: cpu@3 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x3>;
+ cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+ };
+
+ CPU4: cpu@100 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x100>;
+ cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU5: cpu@101 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x101>;
+ cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU6: cpu@102 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x102>;
+ cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+ };
+
+ CPU7: cpu@103 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x103>;
+ cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+ };
+
+ idle-states {
+ CPU_SLEEP_0_0: cpu-sleep-0-0 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ entry-latency-us = <200>;
+ exit-latency-us = <100>;
+ min-residency-us = <400>;
+ wakeup-latency-us = <250>;
+ };
+
+ CLUSTER_SLEEP_0: cluster-sleep-0 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ entry-latency-us = <500>;
+ exit-latency-us = <1500>;
+ min-residency-us = <2500>;
+ wakeup-latency-us = <1700>;
+ };
+
+ CPU_SLEEP_1_0: cpu-sleep-1-0 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ entry-latency-us = <300>;
+ exit-latency-us = <500>;
+ min-residency-us = <900>;
+ wakeup-latency-us = <600>;
+ };
+
+ CLUSTER_SLEEP_1: cluster-sleep-1 {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ entry-latency-us = <800>;
+ exit-latency-us = <2000>;
+ min-residency-us = <6500>;
+ wakeup-latency-us = <2300>;
+ };
+ };
+
+};
+
+===========================================
+5 - References
+===========================================
+
+[1] ARM Linux Kernel documentation - CPUs bindings
+ Documentation/devicetree/bindings/arm/cpus.txt
+
+[2] ARM Linux Kernel documentation - PSCI bindings
+ Documentation/devicetree/bindings/arm/psci.txt
+
+[3] ARM Server Base System Architecture (SBSA)
+ http://infocenter.arm.com/help/index.jsp
+
+[4] ARM Architecture Reference Manuals
+ http://infocenter.arm.com/help/index.jsp
+
+[5] ePAPR standard
+ https://www.power.org/documentation/epapr-version-1-1/
diff --git a/Documentation/devicetree/bindings/arm/psci.txt b/Documentation/devicetree/bindings/arm/psci.txt
index b4a58f3..5aa40ed 100644
--- a/Documentation/devicetree/bindings/arm/psci.txt
+++ b/Documentation/devicetree/bindings/arm/psci.txt
@@ -50,6 +50,16 @@
- migrate : Function ID for MIGRATE operation
+Device tree nodes that require usage of PSCI CPU_SUSPEND function (ie idle
+state nodes, as per bindings in [1]) must specify the following properties:
+
+- arm,psci-suspend-param
+ Usage: Required for state nodes[1] if the corresponding
+ idle-states node entry-method property is set
+ to "psci".
+ Value type: <u32>
+ Definition: power_state parameter to pass to the PSCI
+ suspend call.
Example:
@@ -64,7 +74,6 @@
migrate = <0x95c10003>;
};
-
Case 2: PSCI v0.2 only
psci {
@@ -88,3 +97,6 @@
...
};
+
+[1] Kernel documentation - ARM idle states bindings
+ Documentation/devicetree/bindings/arm/idle-states.txt
diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
new file mode 100644
index 0000000..98c1667
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/power_domain.txt
@@ -0,0 +1,49 @@
+* Generic PM domains
+
+System on chip designs are often divided into multiple PM domains that can be
+used for power gating of selected IP blocks for power saving by reduced leakage
+current.
+
+This device tree binding can be used to bind PM domain consumer devices with
+their PM domains provided by PM domain providers. A PM domain provider can be
+represented by any node in the device tree and can provide one or more PM
+domains. A consumer node can refer to the provider by a phandle and a set of
+phandle arguments (so called PM domain specifiers) of length specified by the
+#power-domain-cells property in the PM domain provider node.
+
+==PM domain providers==
+
+Required properties:
+ - #power-domain-cells : Number of cells in a PM domain specifier;
+ Typically 0 for nodes representing a single PM domain and 1 for nodes
+ providing multiple PM domains (e.g. power controllers), but can be any value
+ as specified by device tree binding documentation of particular provider.
+
+Example:
+
+ power: power-controller@12340000 {
+ compatible = "foo,power-controller";
+ reg = <0x12340000 0x1000>;
+ #power-domain-cells = <1>;
+ };
+
+The node above defines a power controller that is a PM domain provider and
+expects one cell as its phandle argument.
+
+==PM domain consumers==
+
+Required properties:
+ - power-domains : A phandle and PM domain specifier as defined by bindings of
+ the power controller specified by phandle.
+
+Example:
+
+ leaky-device@12350000 {
+ compatible = "foo,i-leak-current";
+ reg = <0x12350000 0x1000>;
+ power-domains = <&power 0>;
+ };
+
+The node above defines a typical PM domain consumer device, which is located
+inside a PM domain with index 0 of a power controller represented by a node
+with the label "power".
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 10d51c2..ccbab56 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -3303,11 +3303,13 @@
tdfx= [HW,DRM]
- test_suspend= [SUSPEND]
+ test_suspend= [SUSPEND][,N]
Specify "mem" (for Suspend-to-RAM) or "standby" (for
- standby suspend) as the system sleep state to briefly
- enter during system startup. The system is woken from
- this state using a wakeup-capable RTC alarm.
+ standby suspend) or "freeze" (for suspend type freeze)
+ as the system sleep state during system startup with
+ the optional capability to repeat N number of times.
+ The system is woken from this state using a
+ wakeup-capable RTC alarm.
thash_entries= [KNL,NET]
Set number of hash buckets for TCP connection
diff --git a/Documentation/power/suspend-and-interrupts.txt b/Documentation/power/suspend-and-interrupts.txt
new file mode 100644
index 0000000..6966364
--- /dev/null
+++ b/Documentation/power/suspend-and-interrupts.txt
@@ -0,0 +1,123 @@
+System Suspend and Device Interrupts
+
+Copyright (C) 2014 Intel Corp.
+Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+
+
+Suspending and Resuming Device IRQs
+-----------------------------------
+
+Device interrupt request lines (IRQs) are generally disabled during system
+suspend after the "late" phase of suspending devices (that is, after all of the
+->prepare, ->suspend and ->suspend_late callbacks have been executed for all
+devices). That is done by suspend_device_irqs().
+
+The rationale for doing so is that after the "late" phase of device suspend
+there is no legitimate reason why any interrupts from suspended devices should
+trigger and if any devices have not been suspended properly yet, it is better to
+block interrupts from them anyway. Also, in the past we had problems with
+interrupt handlers for shared IRQs that device drivers implementing them were
+not prepared for interrupts triggering after their devices had been suspended.
+In some cases they would attempt to access, for example, memory address spaces
+of suspended devices and cause unpredictable behavior to ensue as a result.
+Unfortunately, such problems are very difficult to debug and the introduction
+of suspend_device_irqs(), along with the "noirq" phase of device suspend and
+resume, was the only practical way to mitigate them.
+
+Device IRQs are re-enabled during system resume, right before the "early" phase
+of resuming devices (that is, before starting to execute ->resume_early
+callbacks for devices). The function doing that is resume_device_irqs().
+
+
+The IRQF_NO_SUSPEND Flag
+------------------------
+
+There are interrupts that can legitimately trigger during the entire system
+suspend-resume cycle, including the "noirq" phases of suspending and resuming
+devices as well as during the time when nonboot CPUs are taken offline and
+brought back online. That applies to timer interrupts in the first place,
+but also to IPIs and to some other special-purpose interrupts.
+
+The IRQF_NO_SUSPEND flag is used to indicate that to the IRQ subsystem when
+requesting a special-purpose interrupt. It causes suspend_device_irqs() to
+leave the corresponding IRQ enabled so as to allow the interrupt to work all
+the time as expected.
+
+Note that the IRQF_NO_SUSPEND flag affects the entire IRQ and not just one
+user of it. Thus, if the IRQ is shared, all of the interrupt handlers installed
+for it will be executed as usual after suspend_device_irqs(), even if the
+IRQF_NO_SUSPEND flag was not passed to request_irq() (or equivalent) by some of
+the IRQ's users. For this reason, using IRQF_NO_SUSPEND and IRQF_SHARED at the
+same time should be avoided.
+
+
+System Wakeup Interrupts, enable_irq_wake() and disable_irq_wake()
+------------------------------------------------------------------
+
+System wakeup interrupts generally need to be configured to wake up the system
+from sleep states, especially if they are used for different purposes (e.g. as
+I/O interrupts) in the working state.
+
+That may involve turning on a special signal handling logic within the platform
+(such as an SoC) so that signals from a given line are routed in a different way
+during system sleep so as to trigger a system wakeup when needed. For example,
+the platform may include a dedicated interrupt controller used specifically for
+handling system wakeup events. Then, if a given interrupt line is supposed to
+wake up the system from sleep sates, the corresponding input of that interrupt
+controller needs to be enabled to receive signals from the line in question.
+After wakeup, it generally is better to disable that input to prevent the
+dedicated controller from triggering interrupts unnecessarily.
+
+The IRQ subsystem provides two helper functions to be used by device drivers for
+those purposes. Namely, enable_irq_wake() turns on the platform's logic for
+handling the given IRQ as a system wakeup interrupt line and disable_irq_wake()
+turns that logic off.
+
+Calling enable_irq_wake() causes suspend_device_irqs() to treat the given IRQ
+in a special way. Namely, the IRQ remains enabled, by on the first interrupt
+it will be disabled, marked as pending and "suspended" so that it will be
+re-enabled by resume_device_irqs() during the subsequent system resume. Also
+the PM core is notified about the event which casues the system suspend in
+progress to be aborted (that doesn't have to happen immediately, but at one
+of the points where the suspend thread looks for pending wakeup events).
+
+This way every interrupt from a wakeup interrupt source will either cause the
+system suspend currently in progress to be aborted or wake up the system if
+already suspended. However, after suspend_device_irqs() interrupt handlers are
+not executed for system wakeup IRQs. They are only executed for IRQF_NO_SUSPEND
+IRQs at that time, but those IRQs should not be configured for system wakeup
+using enable_irq_wake().
+
+
+Interrupts and Suspend-to-Idle
+------------------------------
+
+Suspend-to-idle (also known as the "freeze" sleep state) is a relatively new
+system sleep state that works by idling all of the processors and waiting for
+interrupts right after the "noirq" phase of suspending devices.
+
+Of course, this means that all of the interrupts with the IRQF_NO_SUSPEND flag
+set will bring CPUs out of idle while in that state, but they will not cause the
+IRQ subsystem to trigger a system wakeup.
+
+System wakeup interrupts, in turn, will trigger wakeup from suspend-to-idle in
+analogy with what they do in the full system suspend case. The only difference
+is that the wakeup from suspend-to-idle is signaled using the usual working
+state interrupt delivery mechanisms and doesn't require the platform to use
+any special interrupt handling logic for it to work.
+
+
+IRQF_NO_SUSPEND and enable_irq_wake()
+-------------------------------------
+
+There are no valid reasons to use both enable_irq_wake() and the IRQF_NO_SUSPEND
+flag on the same IRQ.
+
+First of all, if the IRQ is not shared, the rules for handling IRQF_NO_SUSPEND
+interrupts (interrupt handlers are invoked after suspend_device_irqs()) are
+directly at odds with the rules for handling system wakeup interrupts (interrupt
+handlers are not invoked after suspend_device_irqs()).
+
+Second, both enable_irq_wake() and IRQF_NO_SUSPEND apply to entire IRQs and not
+to individual interrupt handlers, so sharing an IRQ between a system wakeup
+interrupt source and an IRQF_NO_SUSPEND interrupt source does not make sense.
diff --git a/MAINTAINERS b/MAINTAINERS
index 3705430..f10ed39 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1665,6 +1665,12 @@
S: Supported
F: drivers/tty/serial/atmel_serial.c
+ATMEL Audio ALSA driver
+M: Bo Shen <voice.shen@atmel.com>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Supported
+F: sound/soc/atmel
+
ATMEL DMA DRIVER
M: Nicolas Ferre <nicolas.ferre@atmel.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
@@ -2098,7 +2104,7 @@
F: drivers/scsi/bfa/
BROCADE BNA 10 GIGABIT ETHERNET DRIVER
-M: Rasesh Mody <rmody@brocade.com>
+M: Rasesh Mody <rasesh.mody@qlogic.com>
L: netdev@vger.kernel.org
S: Supported
F: drivers/net/ethernet/brocade/bna/
@@ -5478,7 +5484,7 @@
LINUX FOR POWERPC EMBEDDED MPC5XXX
M: Anatolij Gustschin <agust@denx.de>
L: linuxppc-dev@lists.ozlabs.org
-T: git git://git.denx.de/linux-2.6-agust.git
+T: git git://git.denx.de/linux-denx-agust.git
S: Maintained
F: arch/powerpc/platforms/512x/
F: arch/powerpc/platforms/52xx/
diff --git a/Makefile b/Makefile
index be79944..b77de27 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
VERSION = 3
PATCHLEVEL = 17
SUBLEVEL = 0
-EXTRAVERSION = -rc7
+EXTRAVERSION =
NAME = Shuffling Zombie Juror
# *DOCUMENTATION*
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
index a25c262..322fd15 100644
--- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
+++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
@@ -38,6 +38,7 @@
compatible = "arm,cortex-a15";
reg = <0>;
cci-control-port = <&cci_control1>;
+ cpu-idle-states = <&CLUSTER_SLEEP_BIG>;
};
cpu1: cpu@1 {
@@ -45,6 +46,7 @@
compatible = "arm,cortex-a15";
reg = <1>;
cci-control-port = <&cci_control1>;
+ cpu-idle-states = <&CLUSTER_SLEEP_BIG>;
};
cpu2: cpu@2 {
@@ -52,6 +54,7 @@
compatible = "arm,cortex-a7";
reg = <0x100>;
cci-control-port = <&cci_control2>;
+ cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>;
};
cpu3: cpu@3 {
@@ -59,6 +62,7 @@
compatible = "arm,cortex-a7";
reg = <0x101>;
cci-control-port = <&cci_control2>;
+ cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>;
};
cpu4: cpu@4 {
@@ -66,6 +70,25 @@
compatible = "arm,cortex-a7";
reg = <0x102>;
cci-control-port = <&cci_control2>;
+ cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>;
+ };
+
+ idle-states {
+ CLUSTER_SLEEP_BIG: cluster-sleep-big {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ entry-latency-us = <1000>;
+ exit-latency-us = <700>;
+ min-residency-us = <2000>;
+ };
+
+ CLUSTER_SLEEP_LITTLE: cluster-sleep-little {
+ compatible = "arm,idle-state";
+ local-timer-stop;
+ entry-latency-us = <1000>;
+ exit-latency-us = <500>;
+ min-residency-us = <2500>;
+ };
};
};
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h
index 79ecb4f..10e78d0 100644
--- a/arch/arm/include/asm/cacheflush.h
+++ b/arch/arm/include/asm/cacheflush.h
@@ -466,6 +466,7 @@
*/
#define v7_exit_coherency_flush(level) \
asm volatile( \
+ ".arch armv7-a \n\t" \
"stmfd sp!, {fp, ip} \n\t" \
"mrc p15, 0, r0, c1, c0, 0 @ get SCTLR \n\t" \
"bic r0, r0, #"__stringify(CR_C)" \n\t" \
diff --git a/arch/arm/include/asm/tls.h b/arch/arm/include/asm/tls.h
index 36172ad..5f833f7 100644
--- a/arch/arm/include/asm/tls.h
+++ b/arch/arm/include/asm/tls.h
@@ -81,6 +81,7 @@
asm("mcr p15, 0, %0, c13, c0, 3"
: : "r" (val));
} else {
+#ifdef CONFIG_KUSER_HELPERS
/*
* User space must never try to access this
* directly. Expect your app to break
@@ -89,6 +90,7 @@
* entry-armv.S for details)
*/
*((unsigned int *)0xffff0ff0) = val;
+#endif
}
}
diff --git a/arch/arm/kernel/kprobes-test.c b/arch/arm/kernel/kprobes-test.c
index 08d7312..b206d77 100644
--- a/arch/arm/kernel/kprobes-test.c
+++ b/arch/arm/kernel/kprobes-test.c
@@ -110,10 +110,13 @@
*
* @ TESTCASE_START
* bl __kprobes_test_case_start
- * @ start of inline data...
+ * .pushsection .rodata
+ * "10:
* .ascii "mov r0, r7" @ text title for test case
* .byte 0
- * .align 2, 0
+ * .popsection
+ * @ start of inline data...
+ * .word 10b @ pointer to title in .rodata section
*
* @ TEST_ARG_REG
* .byte ARG_TYPE_REG
@@ -971,7 +974,7 @@
__asm__ __volatile__ (
"stmdb sp!, {r4-r11} \n\t"
"sub sp, sp, #"__stringify(TEST_MEMORY_SIZE)"\n\t"
- "bic r0, lr, #1 @ r0 = inline title string \n\t"
+ "bic r0, lr, #1 @ r0 = inline data \n\t"
"mov r1, sp \n\t"
"bl kprobes_test_case_start \n\t"
"bx r0 \n\t"
@@ -1349,15 +1352,14 @@
return pc + 4;
}
-static uintptr_t __used kprobes_test_case_start(const char *title, void *stack)
+static uintptr_t __used kprobes_test_case_start(const char **title, void *stack)
{
struct test_arg *args;
struct test_arg_end *end_arg;
unsigned long test_code;
- args = (struct test_arg *)PTR_ALIGN(title + strlen(title) + 1, 4);
-
- current_title = title;
+ current_title = *title++;
+ args = (struct test_arg *)title;
current_args = args;
current_stack = stack;
diff --git a/arch/arm/kernel/kprobes-test.h b/arch/arm/kernel/kprobes-test.h
index eecc90a..4430990 100644
--- a/arch/arm/kernel/kprobes-test.h
+++ b/arch/arm/kernel/kprobes-test.h
@@ -111,11 +111,14 @@
#define TESTCASE_START(title) \
__asm__ __volatile__ ( \
"bl __kprobes_test_case_start \n\t" \
+ ".pushsection .rodata \n\t" \
+ "10: \n\t" \
/* don't use .asciz here as 'title' may be */ \
/* multiple strings to be concatenated. */ \
".ascii "#title" \n\t" \
".byte 0 \n\t" \
- ".align 2, 0 \n\t"
+ ".popsection \n\t" \
+ ".word 10b \n\t"
#define TEST_ARG_REG(reg, val) \
".byte "__stringify(ARG_TYPE_REG)" \n\t" \
diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c
index 6a24e11..b89e5f3 100644
--- a/arch/arm/mach-exynos/exynos.c
+++ b/arch/arm/mach-exynos/exynos.c
@@ -193,7 +193,6 @@
/* to be supported later */
return;
- pm_genpd_poweroff_unused();
exynos_pm_init();
}
diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c
index fd76e1b..20f2671 100644
--- a/arch/arm/mach-exynos/pm_domains.c
+++ b/arch/arm/mach-exynos/pm_domains.c
@@ -105,78 +105,6 @@
return exynos_pd_power(domain, false);
}
-static void exynos_add_device_to_domain(struct exynos_pm_domain *pd,
- struct device *dev)
-{
- int ret;
-
- dev_dbg(dev, "adding to power domain %s\n", pd->pd.name);
-
- while (1) {
- ret = pm_genpd_add_device(&pd->pd, dev);
- if (ret != -EAGAIN)
- break;
- cond_resched();
- }
-
- pm_genpd_dev_need_restore(dev, true);
-}
-
-static void exynos_remove_device_from_domain(struct device *dev)
-{
- struct generic_pm_domain *genpd = dev_to_genpd(dev);
- int ret;
-
- dev_dbg(dev, "removing from power domain %s\n", genpd->name);
-
- while (1) {
- ret = pm_genpd_remove_device(genpd, dev);
- if (ret != -EAGAIN)
- break;
- cond_resched();
- }
-}
-
-static void exynos_read_domain_from_dt(struct device *dev)
-{
- struct platform_device *pd_pdev;
- struct exynos_pm_domain *pd;
- struct device_node *node;
-
- node = of_parse_phandle(dev->of_node, "samsung,power-domain", 0);
- if (!node)
- return;
- pd_pdev = of_find_device_by_node(node);
- if (!pd_pdev)
- return;
- pd = platform_get_drvdata(pd_pdev);
- exynos_add_device_to_domain(pd, dev);
-}
-
-static int exynos_pm_notifier_call(struct notifier_block *nb,
- unsigned long event, void *data)
-{
- struct device *dev = data;
-
- switch (event) {
- case BUS_NOTIFY_BIND_DRIVER:
- if (dev->of_node)
- exynos_read_domain_from_dt(dev);
-
- break;
-
- case BUS_NOTIFY_UNBOUND_DRIVER:
- exynos_remove_device_from_domain(dev);
-
- break;
- }
- return NOTIFY_DONE;
-}
-
-static struct notifier_block platform_nb = {
- .notifier_call = exynos_pm_notifier_call,
-};
-
static __init int exynos4_pm_init_power_domain(void)
{
struct platform_device *pdev;
@@ -202,7 +130,6 @@
pd->base = of_iomap(np, 0);
pd->pd.power_off = exynos_pd_power_off;
pd->pd.power_on = exynos_pd_power_on;
- pd->pd.of_node = np;
pd->oscclk = clk_get(dev, "oscclk");
if (IS_ERR(pd->oscclk))
@@ -228,15 +155,12 @@
clk_put(pd->oscclk);
no_clk:
- platform_set_drvdata(pdev, pd);
-
on = __raw_readl(pd->base + 0x4) & INT_LOCAL_PWR_EN;
pm_genpd_init(&pd->pd, NULL, !on);
+ of_genpd_add_provider_simple(np, &pd->pd);
}
- bus_register_notifier(&platform_bus_type, &platform_nb);
-
return 0;
}
arch_initcall(exynos4_pm_init_power_domain);
diff --git a/arch/arm/mach-s3c64xx/common.c b/arch/arm/mach-s3c64xx/common.c
index 5c45aae..16547f2 100644
--- a/arch/arm/mach-s3c64xx/common.c
+++ b/arch/arm/mach-s3c64xx/common.c
@@ -440,8 +440,3 @@
/* if all else fails, or mode was for soft, jump to 0 */
soft_restart(0);
}
-
-void __init s3c64xx_init_late(void)
-{
- s3c64xx_pm_late_initcall();
-}
diff --git a/arch/arm/mach-s3c64xx/common.h b/arch/arm/mach-s3c64xx/common.h
index 7043e7a..9eb8644 100644
--- a/arch/arm/mach-s3c64xx/common.h
+++ b/arch/arm/mach-s3c64xx/common.h
@@ -23,7 +23,6 @@
void s3c64xx_init_io(struct map_desc *mach_desc, int size);
void s3c64xx_restart(enum reboot_mode mode, const char *cmd);
-void s3c64xx_init_late(void);
void s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f,
unsigned long xusbxti_f, bool is_s3c6400, void __iomem *reg_base);
@@ -52,12 +51,6 @@
#define s3c6410_init NULL
#endif
-#ifdef CONFIG_PM
-int __init s3c64xx_pm_late_initcall(void);
-#else
-static inline int s3c64xx_pm_late_initcall(void) { return 0; }
-#endif
-
#ifdef CONFIG_S3C64XX_PL080
extern struct pl08x_platform_data s3c64xx_dma0_plat_data;
extern struct pl08x_platform_data s3c64xx_dma1_plat_data;
diff --git a/arch/arm/mach-s3c64xx/mach-anw6410.c b/arch/arm/mach-s3c64xx/mach-anw6410.c
index 60576df..6224c67 100644
--- a/arch/arm/mach-s3c64xx/mach-anw6410.c
+++ b/arch/arm/mach-s3c64xx/mach-anw6410.c
@@ -233,7 +233,6 @@
.init_irq = s3c6410_init_irq,
.map_io = anw6410_map_io,
.init_machine = anw6410_machine_init,
- .init_late = s3c64xx_init_late,
.init_time = samsung_timer_init,
.restart = s3c64xx_restart,
MACHINE_END
diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c
index fe11633..10b913b 100644
--- a/arch/arm/mach-s3c64xx/mach-crag6410.c
+++ b/arch/arm/mach-s3c64xx/mach-crag6410.c
@@ -857,7 +857,6 @@
.init_irq = s3c6410_init_irq,
.map_io = crag6410_map_io,
.init_machine = crag6410_machine_init,
- .init_late = s3c64xx_init_late,
.init_time = samsung_timer_init,
.restart = s3c64xx_restart,
MACHINE_END
diff --git a/arch/arm/mach-s3c64xx/mach-hmt.c b/arch/arm/mach-s3c64xx/mach-hmt.c
index 19e8feb..e4b087c 100644
--- a/arch/arm/mach-s3c64xx/mach-hmt.c
+++ b/arch/arm/mach-s3c64xx/mach-hmt.c
@@ -277,7 +277,6 @@
.init_irq = s3c6410_init_irq,
.map_io = hmt_map_io,
.init_machine = hmt_machine_init,
- .init_late = s3c64xx_init_late,
.init_time = samsung_timer_init,
.restart = s3c64xx_restart,
MACHINE_END
diff --git a/arch/arm/mach-s3c64xx/mach-mini6410.c b/arch/arm/mach-s3c64xx/mach-mini6410.c
index 9cbc076..ab61af5 100644
--- a/arch/arm/mach-s3c64xx/mach-mini6410.c
+++ b/arch/arm/mach-s3c64xx/mach-mini6410.c
@@ -366,7 +366,6 @@
.init_irq = s3c6410_init_irq,
.map_io = mini6410_map_io,
.init_machine = mini6410_machine_init,
- .init_late = s3c64xx_init_late,
.init_time = samsung_timer_init,
.restart = s3c64xx_restart,
MACHINE_END
diff --git a/arch/arm/mach-s3c64xx/mach-ncp.c b/arch/arm/mach-s3c64xx/mach-ncp.c
index 4bae7dc..80cb144 100644
--- a/arch/arm/mach-s3c64xx/mach-ncp.c
+++ b/arch/arm/mach-s3c64xx/mach-ncp.c
@@ -103,7 +103,6 @@
.init_irq = s3c6410_init_irq,
.map_io = ncp_map_io,
.init_machine = ncp_machine_init,
- .init_late = s3c64xx_init_late,
.init_time = samsung_timer_init,
.restart = s3c64xx_restart,
MACHINE_END
diff --git a/arch/arm/mach-s3c64xx/mach-real6410.c b/arch/arm/mach-s3c64xx/mach-real6410.c
index fbad2af..85fa959 100644
--- a/arch/arm/mach-s3c64xx/mach-real6410.c
+++ b/arch/arm/mach-s3c64xx/mach-real6410.c
@@ -335,7 +335,6 @@
.init_irq = s3c6410_init_irq,
.map_io = real6410_map_io,
.init_machine = real6410_machine_init,
- .init_late = s3c64xx_init_late,
.init_time = samsung_timer_init,
.restart = s3c64xx_restart,
MACHINE_END
diff --git a/arch/arm/mach-s3c64xx/mach-smartq5.c b/arch/arm/mach-s3c64xx/mach-smartq5.c
index dec4c08..33224ab 100644
--- a/arch/arm/mach-s3c64xx/mach-smartq5.c
+++ b/arch/arm/mach-s3c64xx/mach-smartq5.c
@@ -156,7 +156,6 @@
.init_irq = s3c6410_init_irq,
.map_io = smartq_map_io,
.init_machine = smartq5_machine_init,
- .init_late = s3c64xx_init_late,
.init_time = samsung_timer_init,
.restart = s3c64xx_restart,
MACHINE_END
diff --git a/arch/arm/mach-s3c64xx/mach-smartq7.c b/arch/arm/mach-s3c64xx/mach-smartq7.c
index 27b3220..fc7fece 100644
--- a/arch/arm/mach-s3c64xx/mach-smartq7.c
+++ b/arch/arm/mach-s3c64xx/mach-smartq7.c
@@ -172,7 +172,6 @@
.init_irq = s3c6410_init_irq,
.map_io = smartq_map_io,
.init_machine = smartq7_machine_init,
- .init_late = s3c64xx_init_late,
.init_time = samsung_timer_init,
.restart = s3c64xx_restart,
MACHINE_END
diff --git a/arch/arm/mach-s3c64xx/mach-smdk6400.c b/arch/arm/mach-s3c64xx/mach-smdk6400.c
index 9107497..6f42512 100644
--- a/arch/arm/mach-s3c64xx/mach-smdk6400.c
+++ b/arch/arm/mach-s3c64xx/mach-smdk6400.c
@@ -92,7 +92,6 @@
.init_irq = s3c6400_init_irq,
.map_io = smdk6400_map_io,
.init_machine = smdk6400_machine_init,
- .init_late = s3c64xx_init_late,
.init_time = samsung_timer_init,
.restart = s3c64xx_restart,
MACHINE_END
diff --git a/arch/arm/mach-s3c64xx/mach-smdk6410.c b/arch/arm/mach-s3c64xx/mach-smdk6410.c
index 1dc86d7..661eb66 100644
--- a/arch/arm/mach-s3c64xx/mach-smdk6410.c
+++ b/arch/arm/mach-s3c64xx/mach-smdk6410.c
@@ -705,7 +705,6 @@
.init_irq = s3c6410_init_irq,
.map_io = smdk6410_map_io,
.init_machine = smdk6410_machine_init,
- .init_late = s3c64xx_init_late,
.init_time = samsung_timer_init,
.restart = s3c64xx_restart,
MACHINE_END
diff --git a/arch/arm/mach-s3c64xx/pm.c b/arch/arm/mach-s3c64xx/pm.c
index 6b37694..aaf7bea 100644
--- a/arch/arm/mach-s3c64xx/pm.c
+++ b/arch/arm/mach-s3c64xx/pm.c
@@ -347,10 +347,3 @@
return 0;
}
arch_initcall(s3c64xx_pm_initcall);
-
-int __init s3c64xx_pm_late_initcall(void)
-{
- pm_genpd_poweroff_unused();
-
- return 0;
-}
diff --git a/arch/arm/mach-shmobile/pm-r8a7779.c b/arch/arm/mach-shmobile/pm-r8a7779.c
index 69f70b7..82fe3d7 100644
--- a/arch/arm/mach-shmobile/pm-r8a7779.c
+++ b/arch/arm/mach-shmobile/pm-r8a7779.c
@@ -87,7 +87,6 @@
genpd->dev_ops.stop = pm_clk_suspend;
genpd->dev_ops.start = pm_clk_resume;
genpd->dev_ops.active_wakeup = pd_active_wakeup;
- genpd->dev_irq_safe = true;
genpd->power_off = pd_power_down;
genpd->power_on = pd_power_up;
diff --git a/arch/arm/mach-shmobile/pm-rmobile.c b/arch/arm/mach-shmobile/pm-rmobile.c
index ebdd16e..818de2f 100644
--- a/arch/arm/mach-shmobile/pm-rmobile.c
+++ b/arch/arm/mach-shmobile/pm-rmobile.c
@@ -111,7 +111,6 @@
genpd->dev_ops.stop = pm_clk_suspend;
genpd->dev_ops.start = pm_clk_resume;
genpd->dev_ops.active_wakeup = rmobile_pd_active_wakeup;
- genpd->dev_irq_safe = true;
genpd->power_off = rmobile_pd_power_down;
genpd->power_on = rmobile_pd_power_up;
__rmobile_pd_power_up(rmobile_pd, false);
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
index 0c1ab49..83792f4 100644
--- a/arch/arm/mm/alignment.c
+++ b/arch/arm/mm/alignment.c
@@ -41,6 +41,7 @@
* This code is not portable to processors with late data abort handling.
*/
#define CODING_BITS(i) (i & 0x0e000000)
+#define COND_BITS(i) (i & 0xf0000000)
#define LDST_I_BIT(i) (i & (1 << 26)) /* Immediate constant */
#define LDST_P_BIT(i) (i & (1 << 24)) /* Preindex */
@@ -821,6 +822,8 @@
break;
case 0x04000000: /* ldr or str immediate */
+ if (COND_BITS(instr) == 0xf0000000) /* NEON VLDn, VSTn */
+ goto bad;
offset.un = OFFSET_BITS(instr);
handler = do_alignment_ldrstr;
break;
diff --git a/arch/arm/mm/proc-v7-3level.S b/arch/arm/mm/proc-v7-3level.S
index b64e67c..d3daed0 100644
--- a/arch/arm/mm/proc-v7-3level.S
+++ b/arch/arm/mm/proc-v7-3level.S
@@ -157,9 +157,9 @@
* TFR EV X F IHD LR S
* .EEE ..EE PUI. .TAT 4RVI ZWRS BLDP WCAM
* rxxx rrxx xxx0 0101 xxxx xxxx x111 xxxx < forced
- * 11 0 110 1 0011 1100 .111 1101 < we want
+ * 11 0 110 0 0011 1100 .111 1101 < we want
*/
.align 2
.type v7_crval, #object
v7_crval:
- crval clear=0x0120c302, mmuset=0x30c23c7d, ucset=0x00c01c7c
+ crval clear=0x0122c302, mmuset=0x30c03c7d, ucset=0x00c01c7c
diff --git a/arch/arm64/include/asm/cpu_ops.h b/arch/arm64/include/asm/cpu_ops.h
index d7b4b38..47dfa31 100644
--- a/arch/arm64/include/asm/cpu_ops.h
+++ b/arch/arm64/include/asm/cpu_ops.h
@@ -28,6 +28,8 @@
* enable-method property.
* @cpu_init: Reads any data necessary for a specific enable-method from the
* devicetree, for a given cpu node and proposed logical id.
+ * @cpu_init_idle: Reads any data necessary to initialize CPU idle states from
+ * devicetree, for a given cpu node and proposed logical id.
* @cpu_prepare: Early one-time preparation step for a cpu. If there is a
* mechanism for doing so, tests whether it is possible to boot
* the given CPU.
@@ -47,6 +49,7 @@
struct cpu_operations {
const char *name;
int (*cpu_init)(struct device_node *, unsigned int);
+ int (*cpu_init_idle)(struct device_node *, unsigned int);
int (*cpu_prepare)(unsigned int);
int (*cpu_boot)(unsigned int);
void (*cpu_postboot)(void);
diff --git a/arch/arm64/include/asm/cpuidle.h b/arch/arm64/include/asm/cpuidle.h
new file mode 100644
index 0000000..b52a993
--- /dev/null
+++ b/arch/arm64/include/asm/cpuidle.h
@@ -0,0 +1,13 @@
+#ifndef __ASM_CPUIDLE_H
+#define __ASM_CPUIDLE_H
+
+#ifdef CONFIG_CPU_IDLE
+extern int cpu_init_idle(unsigned int cpu);
+#else
+static inline int cpu_init_idle(unsigned int cpu)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
+#endif
diff --git a/arch/arm64/include/asm/suspend.h b/arch/arm64/include/asm/suspend.h
index e9c149c..456d67c 100644
--- a/arch/arm64/include/asm/suspend.h
+++ b/arch/arm64/include/asm/suspend.h
@@ -21,6 +21,7 @@
phys_addr_t save_ptr_stash_phys;
};
+extern int __cpu_suspend(unsigned long arg, int (*fn)(unsigned long));
extern void cpu_resume(void);
extern int cpu_suspend(unsigned long);
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index df7ef87..6e9538c 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -26,6 +26,7 @@
arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o
+arm64-obj-$(CONFIG_CPU_IDLE) += cpuidle.o
arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o
arm64-obj-$(CONFIG_KGDB) += kgdb.o
arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o
diff --git a/arch/arm64/kernel/cpuidle.c b/arch/arm64/kernel/cpuidle.c
new file mode 100644
index 0000000..19d17f5
--- /dev/null
+++ b/arch/arm64/kernel/cpuidle.c
@@ -0,0 +1,31 @@
+/*
+ * ARM64 CPU idle arch support
+ *
+ * Copyright (C) 2014 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/of.h>
+#include <linux/of_device.h>
+
+#include <asm/cpuidle.h>
+#include <asm/cpu_ops.h>
+
+int cpu_init_idle(unsigned int cpu)
+{
+ int ret = -EOPNOTSUPP;
+ struct device_node *cpu_node = of_cpu_device_node_get(cpu);
+
+ if (!cpu_node)
+ return -ENODEV;
+
+ if (cpu_ops[cpu] && cpu_ops[cpu]->cpu_init_idle)
+ ret = cpu_ops[cpu]->cpu_init_idle(cpu_node, cpu);
+
+ of_node_put(cpu_node);
+ return ret;
+}
diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c
index 5539547..866c1c8 100644
--- a/arch/arm64/kernel/psci.c
+++ b/arch/arm64/kernel/psci.c
@@ -21,6 +21,7 @@
#include <linux/reboot.h>
#include <linux/pm.h>
#include <linux/delay.h>
+#include <linux/slab.h>
#include <uapi/linux/psci.h>
#include <asm/compiler.h>
@@ -28,6 +29,7 @@
#include <asm/errno.h>
#include <asm/psci.h>
#include <asm/smp_plat.h>
+#include <asm/suspend.h>
#include <asm/system_misc.h>
#define PSCI_POWER_STATE_TYPE_STANDBY 0
@@ -65,6 +67,8 @@
PSCI_FN_MAX,
};
+static DEFINE_PER_CPU_READ_MOSTLY(struct psci_power_state *, psci_power_state);
+
static u32 psci_function_id[PSCI_FN_MAX];
static int psci_to_linux_errno(int errno)
@@ -93,6 +97,18 @@
& PSCI_0_2_POWER_STATE_AFFL_MASK);
}
+static void psci_power_state_unpack(u32 power_state,
+ struct psci_power_state *state)
+{
+ state->id = (power_state & PSCI_0_2_POWER_STATE_ID_MASK) >>
+ PSCI_0_2_POWER_STATE_ID_SHIFT;
+ state->type = (power_state & PSCI_0_2_POWER_STATE_TYPE_MASK) >>
+ PSCI_0_2_POWER_STATE_TYPE_SHIFT;
+ state->affinity_level =
+ (power_state & PSCI_0_2_POWER_STATE_AFFL_MASK) >>
+ PSCI_0_2_POWER_STATE_AFFL_SHIFT;
+}
+
/*
* The following two functions are invoked via the invoke_psci_fn pointer
* and will not be inlined, allowing us to piggyback on the AAPCS.
@@ -199,6 +215,63 @@
return err;
}
+static int __maybe_unused cpu_psci_cpu_init_idle(struct device_node *cpu_node,
+ unsigned int cpu)
+{
+ int i, ret, count = 0;
+ struct psci_power_state *psci_states;
+ struct device_node *state_node;
+
+ /*
+ * If the PSCI cpu_suspend function hook has not been initialized
+ * idle states must not be enabled, so bail out
+ */
+ if (!psci_ops.cpu_suspend)
+ return -EOPNOTSUPP;
+
+ /* Count idle states */
+ while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
+ count))) {
+ count++;
+ of_node_put(state_node);
+ }
+
+ if (!count)
+ return -ENODEV;
+
+ psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL);
+ if (!psci_states)
+ return -ENOMEM;
+
+ for (i = 0; i < count; i++) {
+ u32 psci_power_state;
+
+ state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
+
+ ret = of_property_read_u32(state_node,
+ "arm,psci-suspend-param",
+ &psci_power_state);
+ if (ret) {
+ pr_warn(" * %s missing arm,psci-suspend-param property\n",
+ state_node->full_name);
+ of_node_put(state_node);
+ goto free_mem;
+ }
+
+ of_node_put(state_node);
+ pr_debug("psci-power-state %#x index %d\n", psci_power_state,
+ i);
+ psci_power_state_unpack(psci_power_state, &psci_states[i]);
+ }
+ /* Idle states parsed correctly, initialize per-cpu pointer */
+ per_cpu(psci_power_state, cpu) = psci_states;
+ return 0;
+
+free_mem:
+ kfree(psci_states);
+ return ret;
+}
+
static int get_set_conduit_method(struct device_node *np)
{
const char *method;
@@ -436,8 +509,39 @@
#endif
#endif
+static int psci_suspend_finisher(unsigned long index)
+{
+ struct psci_power_state *state = __get_cpu_var(psci_power_state);
+
+ return psci_ops.cpu_suspend(state[index - 1],
+ virt_to_phys(cpu_resume));
+}
+
+static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index)
+{
+ int ret;
+ struct psci_power_state *state = __get_cpu_var(psci_power_state);
+ /*
+ * idle state index 0 corresponds to wfi, should never be called
+ * from the cpu_suspend operations
+ */
+ if (WARN_ON_ONCE(!index))
+ return -EINVAL;
+
+ if (state->type == PSCI_POWER_STATE_TYPE_STANDBY)
+ ret = psci_ops.cpu_suspend(state[index - 1], 0);
+ else
+ ret = __cpu_suspend(index, psci_suspend_finisher);
+
+ return ret;
+}
+
const struct cpu_operations cpu_psci_ops = {
.name = "psci",
+#ifdef CONFIG_CPU_IDLE
+ .cpu_init_idle = cpu_psci_cpu_init_idle,
+ .cpu_suspend = cpu_psci_cpu_suspend,
+#endif
#ifdef CONFIG_SMP
.cpu_init = cpu_psci_cpu_init,
.cpu_prepare = cpu_psci_cpu_prepare,
diff --git a/arch/arm64/kernel/sleep.S b/arch/arm64/kernel/sleep.S
index b192572..a564b44 100644
--- a/arch/arm64/kernel/sleep.S
+++ b/arch/arm64/kernel/sleep.S
@@ -49,28 +49,39 @@
orr \dst, \dst, \mask // dst|=(aff3>>rs3)
.endm
/*
- * Save CPU state for a suspend. This saves callee registers, and allocates
- * space on the kernel stack to save the CPU specific registers + some
- * other data for resume.
+ * Save CPU state for a suspend and execute the suspend finisher.
+ * On success it will return 0 through cpu_resume - ie through a CPU
+ * soft/hard reboot from the reset vector.
+ * On failure it returns the suspend finisher return value or force
+ * -EOPNOTSUPP if the finisher erroneously returns 0 (the suspend finisher
+ * is not allowed to return, if it does this must be considered failure).
+ * It saves callee registers, and allocates space on the kernel stack
+ * to save the CPU specific registers + some other data for resume.
*
* x0 = suspend finisher argument
+ * x1 = suspend finisher function pointer
*/
-ENTRY(__cpu_suspend)
+ENTRY(__cpu_suspend_enter)
stp x29, lr, [sp, #-96]!
stp x19, x20, [sp,#16]
stp x21, x22, [sp,#32]
stp x23, x24, [sp,#48]
stp x25, x26, [sp,#64]
stp x27, x28, [sp,#80]
+ /*
+ * Stash suspend finisher and its argument in x20 and x19
+ */
+ mov x19, x0
+ mov x20, x1
mov x2, sp
sub sp, sp, #CPU_SUSPEND_SZ // allocate cpu_suspend_ctx
- mov x1, sp
+ mov x0, sp
/*
- * x1 now points to struct cpu_suspend_ctx allocated on the stack
+ * x0 now points to struct cpu_suspend_ctx allocated on the stack
*/
- str x2, [x1, #CPU_CTX_SP]
- ldr x2, =sleep_save_sp
- ldr x2, [x2, #SLEEP_SAVE_SP_VIRT]
+ str x2, [x0, #CPU_CTX_SP]
+ ldr x1, =sleep_save_sp
+ ldr x1, [x1, #SLEEP_SAVE_SP_VIRT]
#ifdef CONFIG_SMP
mrs x7, mpidr_el1
ldr x9, =mpidr_hash
@@ -82,11 +93,21 @@
ldp w3, w4, [x9, #MPIDR_HASH_SHIFTS]
ldp w5, w6, [x9, #(MPIDR_HASH_SHIFTS + 8)]
compute_mpidr_hash x8, x3, x4, x5, x6, x7, x10
- add x2, x2, x8, lsl #3
+ add x1, x1, x8, lsl #3
#endif
- bl __cpu_suspend_finisher
+ bl __cpu_suspend_save
+ /*
+ * Grab suspend finisher in x20 and its argument in x19
+ */
+ mov x0, x19
+ mov x1, x20
+ /*
+ * We are ready for power down, fire off the suspend finisher
+ * in x1, with argument in x0
+ */
+ blr x1
/*
- * Never gets here, unless suspend fails.
+ * Never gets here, unless suspend finisher fails.
* Successful cpu_suspend should return from cpu_resume, returning
* through this code path is considered an error
* If the return value is set to 0 force x0 = -EOPNOTSUPP
@@ -103,7 +124,7 @@
ldp x27, x28, [sp, #80]
ldp x29, lr, [sp], #96
ret
-ENDPROC(__cpu_suspend)
+ENDPROC(__cpu_suspend_enter)
.ltorg
/*
diff --git a/arch/arm64/kernel/suspend.c b/arch/arm64/kernel/suspend.c
index 55a99b9..13ad4db 100644
--- a/arch/arm64/kernel/suspend.c
+++ b/arch/arm64/kernel/suspend.c
@@ -9,22 +9,19 @@
#include <asm/suspend.h>
#include <asm/tlbflush.h>
-extern int __cpu_suspend(unsigned long);
+extern int __cpu_suspend_enter(unsigned long arg, int (*fn)(unsigned long));
/*
- * This is called by __cpu_suspend() to save the state, and do whatever
+ * This is called by __cpu_suspend_enter() to save the state, and do whatever
* flushing is required to ensure that when the CPU goes to sleep we have
* the necessary data available when the caches are not searched.
*
- * @arg: Argument to pass to suspend operations
- * @ptr: CPU context virtual address
- * @save_ptr: address of the location where the context physical address
- * must be saved
+ * ptr: CPU context virtual address
+ * save_ptr: address of the location where the context physical address
+ * must be saved
*/
-int __cpu_suspend_finisher(unsigned long arg, struct cpu_suspend_ctx *ptr,
- phys_addr_t *save_ptr)
+void notrace __cpu_suspend_save(struct cpu_suspend_ctx *ptr,
+ phys_addr_t *save_ptr)
{
- int cpu = smp_processor_id();
-
*save_ptr = virt_to_phys(ptr);
cpu_do_suspend(ptr);
@@ -35,8 +32,6 @@
*/
__flush_dcache_area(ptr, sizeof(*ptr));
__flush_dcache_area(save_ptr, sizeof(*save_ptr));
-
- return cpu_ops[cpu]->cpu_suspend(arg);
}
/*
@@ -56,15 +51,15 @@
}
/**
- * cpu_suspend
+ * cpu_suspend() - function to enter a low-power state
+ * @arg: argument to pass to CPU suspend operations
*
- * @arg: argument to pass to the finisher function
+ * Return: 0 on success, -EOPNOTSUPP if CPU suspend hook not initialized, CPU
+ * operations back-end error code otherwise.
*/
int cpu_suspend(unsigned long arg)
{
- struct mm_struct *mm = current->active_mm;
- int ret, cpu = smp_processor_id();
- unsigned long flags;
+ int cpu = smp_processor_id();
/*
* If cpu_ops have not been registered or suspend
@@ -72,6 +67,21 @@
*/
if (!cpu_ops[cpu] || !cpu_ops[cpu]->cpu_suspend)
return -EOPNOTSUPP;
+ return cpu_ops[cpu]->cpu_suspend(arg);
+}
+
+/*
+ * __cpu_suspend
+ *
+ * arg: argument to pass to the finisher function
+ * fn: finisher function pointer
+ *
+ */
+int __cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
+{
+ struct mm_struct *mm = current->active_mm;
+ int ret;
+ unsigned long flags;
/*
* From this point debug exceptions are disabled to prevent
@@ -86,7 +96,7 @@
* page tables, so that the thread address space is properly
* set-up on function return.
*/
- ret = __cpu_suspend(arg);
+ ret = __cpu_suspend_enter(arg, fn);
if (ret == 0) {
cpu_switch_mm(mm->pgd, mm);
flush_tlb_all();
@@ -95,7 +105,7 @@
* Restore per-cpu offset before any kernel
* subsystem relying on it has a chance to run.
*/
- set_my_cpu_offset(per_cpu_offset(cpu));
+ set_my_cpu_offset(per_cpu_offset(smp_processor_id()));
/*
* Restore HW breakpoint registers to sane values
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index 337ce5a..1183d54 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -2623,6 +2623,7 @@
.irq_eoi = ack_apic_level,
.irq_set_affinity = native_ioapic_set_affinity,
.irq_retrigger = ioapic_retrigger_irq,
+ .flags = IRQCHIP_SKIP_SET_WAKE,
};
static inline void init_IO_APIC_traps(void)
@@ -3173,6 +3174,7 @@
.irq_ack = ack_apic_edge,
.irq_set_affinity = msi_set_affinity,
.irq_retrigger = ioapic_retrigger_irq,
+ .flags = IRQCHIP_SKIP_SET_WAKE,
};
int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc,
@@ -3271,6 +3273,7 @@
.irq_ack = ack_apic_edge,
.irq_set_affinity = dmar_msi_set_affinity,
.irq_retrigger = ioapic_retrigger_irq,
+ .flags = IRQCHIP_SKIP_SET_WAKE,
};
int arch_setup_dmar_msi(unsigned int irq)
@@ -3321,6 +3324,7 @@
.irq_ack = ack_apic_edge,
.irq_set_affinity = hpet_msi_set_affinity,
.irq_retrigger = ioapic_retrigger_irq,
+ .flags = IRQCHIP_SKIP_SET_WAKE,
};
int default_setup_hpet_msi(unsigned int irq, unsigned int id)
@@ -3384,6 +3388,7 @@
.irq_ack = ack_apic_edge,
.irq_set_affinity = ht_set_affinity,
.irq_retrigger = ioapic_retrigger_irq,
+ .flags = IRQCHIP_SKIP_SET_WAKE,
};
int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev)
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
index b0ea767..93d1606 100644
--- a/drivers/acpi/acpi_lpss.c
+++ b/drivers/acpi/acpi_lpss.c
@@ -54,55 +54,58 @@
#define LPSS_PRV_REG_COUNT 9
-struct lpss_shared_clock {
- const char *name;
- unsigned long rate;
- struct clk *clk;
-};
+/* LPSS Flags */
+#define LPSS_CLK BIT(0)
+#define LPSS_CLK_GATE BIT(1)
+#define LPSS_CLK_DIVIDER BIT(2)
+#define LPSS_LTR BIT(3)
+#define LPSS_SAVE_CTX BIT(4)
struct lpss_private_data;
struct lpss_device_desc {
- bool clk_required;
- const char *clkdev_name;
- bool ltr_required;
+ unsigned int flags;
unsigned int prv_offset;
size_t prv_size_override;
- bool clk_divider;
- bool clk_gate;
- bool save_ctx;
- struct lpss_shared_clock *shared_clock;
void (*setup)(struct lpss_private_data *pdata);
};
static struct lpss_device_desc lpss_dma_desc = {
- .clk_required = true,
- .clkdev_name = "hclk",
+ .flags = LPSS_CLK,
};
struct lpss_private_data {
void __iomem *mmio_base;
resource_size_t mmio_size;
+ unsigned int fixed_clk_rate;
struct clk *clk;
const struct lpss_device_desc *dev_desc;
u32 prv_reg_ctx[LPSS_PRV_REG_COUNT];
};
+/* UART Component Parameter Register */
+#define LPSS_UART_CPR 0xF4
+#define LPSS_UART_CPR_AFCE BIT(4)
+
static void lpss_uart_setup(struct lpss_private_data *pdata)
{
unsigned int offset;
- u32 reg;
+ u32 val;
offset = pdata->dev_desc->prv_offset + LPSS_TX_INT;
- reg = readl(pdata->mmio_base + offset);
- writel(reg | LPSS_TX_INT_MASK, pdata->mmio_base + offset);
+ val = readl(pdata->mmio_base + offset);
+ writel(val | LPSS_TX_INT_MASK, pdata->mmio_base + offset);
- offset = pdata->dev_desc->prv_offset + LPSS_GENERAL;
- reg = readl(pdata->mmio_base + offset);
- writel(reg | LPSS_GENERAL_UART_RTS_OVRD, pdata->mmio_base + offset);
+ val = readl(pdata->mmio_base + LPSS_UART_CPR);
+ if (!(val & LPSS_UART_CPR_AFCE)) {
+ offset = pdata->dev_desc->prv_offset + LPSS_GENERAL;
+ val = readl(pdata->mmio_base + offset);
+ val |= LPSS_GENERAL_UART_RTS_OVRD;
+ writel(val, pdata->mmio_base + offset);
+ }
}
-static void lpss_i2c_setup(struct lpss_private_data *pdata)
+static void byt_i2c_setup(struct lpss_private_data *pdata)
{
unsigned int offset;
u32 val;
@@ -111,100 +114,56 @@
val = readl(pdata->mmio_base + offset);
val |= LPSS_RESETS_RESET_APB | LPSS_RESETS_RESET_FUNC;
writel(val, pdata->mmio_base + offset);
+
+ if (readl(pdata->mmio_base + pdata->dev_desc->prv_offset))
+ pdata->fixed_clk_rate = 133000000;
}
-static struct lpss_device_desc wpt_dev_desc = {
- .clk_required = true,
- .prv_offset = 0x800,
- .ltr_required = true,
- .clk_divider = true,
- .clk_gate = true,
-};
-
static struct lpss_device_desc lpt_dev_desc = {
- .clk_required = true,
+ .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR,
.prv_offset = 0x800,
- .ltr_required = true,
- .clk_divider = true,
- .clk_gate = true,
};
static struct lpss_device_desc lpt_i2c_dev_desc = {
- .clk_required = true,
+ .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_LTR,
.prv_offset = 0x800,
- .ltr_required = true,
- .clk_gate = true,
};
static struct lpss_device_desc lpt_uart_dev_desc = {
- .clk_required = true,
+ .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR,
.prv_offset = 0x800,
- .ltr_required = true,
- .clk_divider = true,
- .clk_gate = true,
.setup = lpss_uart_setup,
};
static struct lpss_device_desc lpt_sdio_dev_desc = {
+ .flags = LPSS_LTR,
.prv_offset = 0x1000,
.prv_size_override = 0x1018,
- .ltr_required = true,
-};
-
-static struct lpss_shared_clock pwm_clock = {
- .name = "pwm_clk",
- .rate = 25000000,
};
static struct lpss_device_desc byt_pwm_dev_desc = {
- .clk_required = true,
- .save_ctx = true,
- .shared_clock = &pwm_clock,
+ .flags = LPSS_SAVE_CTX,
};
static struct lpss_device_desc byt_uart_dev_desc = {
- .clk_required = true,
+ .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
.prv_offset = 0x800,
- .clk_divider = true,
- .clk_gate = true,
- .save_ctx = true,
.setup = lpss_uart_setup,
};
static struct lpss_device_desc byt_spi_dev_desc = {
- .clk_required = true,
+ .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
.prv_offset = 0x400,
- .clk_divider = true,
- .clk_gate = true,
- .save_ctx = true,
};
static struct lpss_device_desc byt_sdio_dev_desc = {
- .clk_required = true,
-};
-
-static struct lpss_shared_clock i2c_clock = {
- .name = "i2c_clk",
- .rate = 100000000,
+ .flags = LPSS_CLK,
};
static struct lpss_device_desc byt_i2c_dev_desc = {
- .clk_required = true,
+ .flags = LPSS_CLK | LPSS_SAVE_CTX,
.prv_offset = 0x800,
- .save_ctx = true,
- .shared_clock = &i2c_clock,
- .setup = lpss_i2c_setup,
-};
-
-static struct lpss_shared_clock bsw_pwm_clock = {
- .name = "pwm_clk",
- .rate = 19200000,
-};
-
-static struct lpss_device_desc bsw_pwm_dev_desc = {
- .clk_required = true,
- .save_ctx = true,
- .shared_clock = &bsw_pwm_clock,
+ .setup = byt_i2c_setup,
};
#else
@@ -237,7 +196,7 @@
{ "INT33FC", },
/* Braswell LPSS devices */
- { "80862288", LPSS_ADDR(bsw_pwm_dev_desc) },
+ { "80862288", LPSS_ADDR(byt_pwm_dev_desc) },
{ "8086228A", LPSS_ADDR(byt_uart_dev_desc) },
{ "8086228E", LPSS_ADDR(byt_spi_dev_desc) },
{ "808622C1", LPSS_ADDR(byt_i2c_dev_desc) },
@@ -251,7 +210,8 @@
{ "INT3436", LPSS_ADDR(lpt_sdio_dev_desc) },
{ "INT3437", },
- { "INT3438", LPSS_ADDR(wpt_dev_desc) },
+ /* Wildcat Point LPSS devices */
+ { "INT3438", LPSS_ADDR(lpt_dev_desc) },
{ }
};
@@ -276,7 +236,6 @@
struct lpss_private_data *pdata)
{
const struct lpss_device_desc *dev_desc = pdata->dev_desc;
- struct lpss_shared_clock *shared_clock = dev_desc->shared_clock;
const char *devname = dev_name(&adev->dev);
struct clk *clk = ERR_PTR(-ENODEV);
struct lpss_clk_data *clk_data;
@@ -289,12 +248,7 @@
clk_data = platform_get_drvdata(lpss_clk_dev);
if (!clk_data)
return -ENODEV;
-
- if (dev_desc->clkdev_name) {
- clk_register_clkdev(clk_data->clk, dev_desc->clkdev_name,
- devname);
- return 0;
- }
+ clk = clk_data->clk;
if (!pdata->mmio_base
|| pdata->mmio_size < dev_desc->prv_offset + LPSS_CLK_SIZE)
@@ -303,24 +257,19 @@
parent = clk_data->name;
prv_base = pdata->mmio_base + dev_desc->prv_offset;
- if (shared_clock) {
- clk = shared_clock->clk;
- if (!clk) {
- clk = clk_register_fixed_rate(NULL, shared_clock->name,
- "lpss_clk", 0,
- shared_clock->rate);
- shared_clock->clk = clk;
- }
- parent = shared_clock->name;
+ if (pdata->fixed_clk_rate) {
+ clk = clk_register_fixed_rate(NULL, devname, parent, 0,
+ pdata->fixed_clk_rate);
+ goto out;
}
- if (dev_desc->clk_gate) {
+ if (dev_desc->flags & LPSS_CLK_GATE) {
clk = clk_register_gate(NULL, devname, parent, 0,
prv_base, 0, 0, NULL);
parent = devname;
}
- if (dev_desc->clk_divider) {
+ if (dev_desc->flags & LPSS_CLK_DIVIDER) {
/* Prevent division by zero */
if (!readl(prv_base))
writel(LPSS_CLK_DIVIDER_DEF_MASK, prv_base);
@@ -344,7 +293,7 @@
kfree(parent);
kfree(clk_name);
}
-
+out:
if (IS_ERR(clk))
return PTR_ERR(clk);
@@ -392,7 +341,10 @@
pdata->dev_desc = dev_desc;
- if (dev_desc->clk_required) {
+ if (dev_desc->setup)
+ dev_desc->setup(pdata);
+
+ if (dev_desc->flags & LPSS_CLK) {
ret = register_device_clock(adev, pdata);
if (ret) {
/* Skip the device, but continue the namespace scan. */
@@ -413,9 +365,6 @@
goto err_out;
}
- if (dev_desc->setup)
- dev_desc->setup(pdata);
-
adev->driver_data = pdata;
pdev = acpi_create_platform_device(adev);
if (!IS_ERR_OR_NULL(pdev)) {
@@ -692,19 +641,19 @@
switch (action) {
case BUS_NOTIFY_BOUND_DRIVER:
- if (pdata->dev_desc->save_ctx)
+ if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
pdev->dev.pm_domain = &acpi_lpss_pm_domain;
break;
case BUS_NOTIFY_UNBOUND_DRIVER:
- if (pdata->dev_desc->save_ctx)
+ if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
pdev->dev.pm_domain = NULL;
break;
case BUS_NOTIFY_ADD_DEVICE:
- if (pdata->dev_desc->ltr_required)
+ if (pdata->dev_desc->flags & LPSS_LTR)
return sysfs_create_group(&pdev->dev.kobj,
&lpss_attr_group);
case BUS_NOTIFY_DEL_DEVICE:
- if (pdata->dev_desc->ltr_required)
+ if (pdata->dev_desc->flags & LPSS_LTR)
sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group);
default:
break;
@@ -721,7 +670,7 @@
{
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
- if (!pdata || !pdata->mmio_base || !pdata->dev_desc->ltr_required)
+ if (!pdata || !pdata->mmio_base || !(pdata->dev_desc->flags & LPSS_LTR))
return;
if (pdata->mmio_size >= pdata->dev_desc->prv_offset + LPSS_LTR_SIZE)
diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c
index 996fa19..f30c407 100644
--- a/drivers/acpi/acpi_pnp.c
+++ b/drivers/acpi/acpi_pnp.c
@@ -132,10 +132,6 @@
{"PNP0401"}, /* ECP Printer Port */
/* apple-gmux */
{"APP000B"},
- /* fujitsu-laptop.c */
- {"FUJ02bf"},
- {"FUJ02B1"},
- {"FUJ02E3"},
/* system */
{"PNP0c02"}, /* General ID for reserving resources */
{"PNP0c01"}, /* memory controller */
diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c
index 0cf159c..56710a0 100644
--- a/drivers/acpi/acpica/evxfgpe.c
+++ b/drivers/acpi/acpica/evxfgpe.c
@@ -596,6 +596,38 @@
ACPI_EXPORT_SYMBOL(acpi_enable_all_runtime_gpes)
+/******************************************************************************
+ *
+ * FUNCTION: acpi_enable_all_wakeup_gpes
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enable all "wakeup" GPEs and disable all of the other GPEs, in
+ * all GPE blocks.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_enable_all_wakeup_gpes(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_enable_all_wakeup_gpes);
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_hw_enable_all_wakeup_gpes();
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_enable_all_wakeup_gpes)
+
/*******************************************************************************
*
* FUNCTION: acpi_install_gpe_block
diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c
index 2e6caab..ea62d40 100644
--- a/drivers/acpi/acpica/hwgpe.c
+++ b/drivers/acpi/acpica/hwgpe.c
@@ -396,11 +396,11 @@
/* Examine each GPE Register within the block */
for (i = 0; i < gpe_block->register_count; i++) {
- if (!gpe_block->register_info[i].enable_for_wake) {
- continue;
- }
- /* Enable all "wake" GPEs in this register */
+ /*
+ * Enable all "wake" GPEs in this register and disable the
+ * remaining ones.
+ */
status =
acpi_hw_write(gpe_block->register_info[i].enable_for_wake,
diff --git a/drivers/acpi/acpica/utresrc.c b/drivers/acpi/acpica/utresrc.c
index 14cb6c0..5cd017c 100644
--- a/drivers/acpi/acpica/utresrc.c
+++ b/drivers/acpi/acpica/utresrc.c
@@ -87,7 +87,9 @@
const char *acpi_gbl_ll_decode[] = {
"ActiveHigh",
- "ActiveLow"
+ "ActiveLow",
+ "ActiveBoth",
+ "Reserved"
};
const char *acpi_gbl_max_decode[] = {
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index 5fdfe65..8ec8a89 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -695,7 +695,7 @@
if (battery->power_unit && dmi_name_in_vendors("LENOVO")) {
const char *s;
s = dmi_get_system_info(DMI_PRODUCT_VERSION);
- if (s && !strnicmp(s, "ThinkPad", 8)) {
+ if (s && !strncasecmp(s, "ThinkPad", 8)) {
dmi_walk(find_battery, battery);
if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
&battery->flags) &&
diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c
index 36eb42e..ed122e1 100644
--- a/drivers/acpi/blacklist.c
+++ b/drivers/acpi/blacklist.c
@@ -247,8 +247,8 @@
},
/*
- * These machines will power on immediately after shutdown when
- * reporting the Windows 2012 OSI.
+ * The wireless hotkey does not work on those machines when
+ * returning true for _OSI("Windows 2012")
*/
{
.callback = dmi_disable_osi_win8,
@@ -258,6 +258,38 @@
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7737"),
},
},
+ {
+ .callback = dmi_disable_osi_win8,
+ .ident = "Dell Inspiron 7537",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7537"),
+ },
+ },
+ {
+ .callback = dmi_disable_osi_win8,
+ .ident = "Dell Inspiron 5437",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5437"),
+ },
+ },
+ {
+ .callback = dmi_disable_osi_win8,
+ .ident = "Dell Inspiron 3437",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 3437"),
+ },
+ },
+ {
+ .callback = dmi_disable_osi_win8,
+ .ident = "Dell Vostro 3446",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3446"),
+ },
+ },
/*
* BIOS invocation of _OSI(Linux) is almost always a BIOS bug.
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c
index 67075f8..bea6896 100644
--- a/drivers/acpi/device_pm.c
+++ b/drivers/acpi/device_pm.c
@@ -1041,6 +1041,40 @@
};
/**
+ * acpi_dev_pm_detach - Remove ACPI power management from the device.
+ * @dev: Device to take care of.
+ * @power_off: Whether or not to try to remove power from the device.
+ *
+ * Remove the device from the general ACPI PM domain and remove its wakeup
+ * notifier. If @power_off is set, additionally remove power from the device if
+ * possible.
+ *
+ * Callers must ensure proper synchronization of this function with power
+ * management callbacks.
+ */
+static void acpi_dev_pm_detach(struct device *dev, bool power_off)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+
+ if (adev && dev->pm_domain == &acpi_general_pm_domain) {
+ dev->pm_domain = NULL;
+ acpi_remove_pm_notifier(adev);
+ if (power_off) {
+ /*
+ * If the device's PM QoS resume latency limit or flags
+ * have been exposed to user space, they have to be
+ * hidden at this point, so that they don't affect the
+ * choice of the low-power state to put the device into.
+ */
+ dev_pm_qos_hide_latency_limit(dev);
+ dev_pm_qos_hide_flags(dev);
+ acpi_device_wakeup(adev, ACPI_STATE_S0, false);
+ acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
+ }
+ }
+}
+
+/**
* acpi_dev_pm_attach - Prepare device for ACPI power management.
* @dev: Device to prepare.
* @power_on: Whether or not to power on the device.
@@ -1072,42 +1106,9 @@
acpi_dev_pm_full_power(adev);
acpi_device_wakeup(adev, ACPI_STATE_S0, false);
}
+
+ dev->pm_domain->detach = acpi_dev_pm_detach;
return 0;
}
EXPORT_SYMBOL_GPL(acpi_dev_pm_attach);
-
-/**
- * acpi_dev_pm_detach - Remove ACPI power management from the device.
- * @dev: Device to take care of.
- * @power_off: Whether or not to try to remove power from the device.
- *
- * Remove the device from the general ACPI PM domain and remove its wakeup
- * notifier. If @power_off is set, additionally remove power from the device if
- * possible.
- *
- * Callers must ensure proper synchronization of this function with power
- * management callbacks.
- */
-void acpi_dev_pm_detach(struct device *dev, bool power_off)
-{
- struct acpi_device *adev = ACPI_COMPANION(dev);
-
- if (adev && dev->pm_domain == &acpi_general_pm_domain) {
- dev->pm_domain = NULL;
- acpi_remove_pm_notifier(adev);
- if (power_off) {
- /*
- * If the device's PM QoS resume latency limit or flags
- * have been exposed to user space, they have to be
- * hidden at this point, so that they don't affect the
- * choice of the low-power state to put the device into.
- */
- dev_pm_qos_hide_latency_limit(dev);
- dev_pm_qos_hide_flags(dev);
- acpi_device_wakeup(adev, ACPI_STATE_S0, false);
- acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
- }
- }
-}
-EXPORT_SYMBOL_GPL(acpi_dev_pm_detach);
#endif /* CONFIG_PM */
diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index 8acf53e..5328b10 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -27,12 +27,10 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/thermal.h>
#include <linux/acpi.h>
-#define PREFIX "ACPI: "
-
#define ACPI_FAN_CLASS "fan"
#define ACPI_FAN_FILE_STATE "state"
@@ -127,8 +125,9 @@
};
/* --------------------------------------------------------------------------
- Driver Interface
- -------------------------------------------------------------------------- */
+ * Driver Interface
+ * --------------------------------------------------------------------------
+*/
static int acpi_fan_add(struct acpi_device *device)
{
@@ -143,7 +142,7 @@
result = acpi_bus_update_power(device->handle, NULL);
if (result) {
- printk(KERN_ERR PREFIX "Setting initial power state\n");
+ dev_err(&device->dev, "Setting initial power state\n");
goto end;
}
@@ -168,10 +167,9 @@
&device->dev.kobj,
"device");
if (result)
- dev_err(&device->dev, "Failed to create sysfs link "
- "'device'\n");
+ dev_err(&device->dev, "Failed to create sysfs link 'device'\n");
- printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
+ dev_info(&device->dev, "ACPI: %s [%s] (%s)\n",
acpi_device_name(device), acpi_device_bid(device),
!device->power.state ? "on" : "off");
@@ -217,7 +215,7 @@
result = acpi_bus_update_power(to_acpi_device(dev)->handle, NULL);
if (result)
- printk(KERN_ERR PREFIX "Error updating fan power state\n");
+ dev_err(dev, "Error updating fan power state\n");
return result;
}
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 3abe9b2..9964f70 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -152,6 +152,16 @@
osi_linux.dmi ? " via DMI" : "");
}
+ if (!strcmp("Darwin", interface)) {
+ /*
+ * Apple firmware will behave poorly if it receives positive
+ * answers to "Darwin" and any other OS. Respond positively
+ * to Darwin and then disable all other vendor strings.
+ */
+ acpi_update_interfaces(ACPI_DISABLE_ALL_VENDOR_STRINGS);
+ supported = ACPI_UINT32_MAX;
+ }
+
return supported;
}
@@ -825,7 +835,7 @@
acpi_irq_handler = handler;
acpi_irq_context = context;
- if (request_irq(irq, acpi_irq, IRQF_SHARED | IRQF_NO_SUSPEND, "acpi", acpi_irq)) {
+ if (request_irq(irq, acpi_irq, IRQF_SHARED, "acpi", acpi_irq)) {
printk(KERN_ERR PREFIX "SCI (IRQ%d) allocation failed\n", irq);
acpi_irq_handler = NULL;
return AE_NOT_ACQUIRED;
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index e6ae603..cd4de7e 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -35,6 +35,7 @@
#include <linux/pci-aspm.h>
#include <linux/acpi.h>
#include <linux/slab.h>
+#include <linux/dmi.h>
#include <acpi/apei.h> /* for acpi_hest_init() */
#include "internal.h"
@@ -430,6 +431,19 @@
acpi_handle handle = device->handle;
/*
+ * Apple always return failure on _OSC calls when _OSI("Darwin") has
+ * been called successfully. We know the feature set supported by the
+ * platform, so avoid calling _OSC at all
+ */
+
+ if (dmi_match(DMI_SYS_VENDOR, "Apple Inc.")) {
+ root->osc_control_set = ~OSC_PCI_EXPRESS_PME_CONTROL;
+ decode_osc_control(root, "OS assumes control of",
+ root->osc_control_set);
+ return;
+ }
+
+ /*
* All supported architectures that use ACPI have support for
* PCI domains, so we indicate this in _OSC support capabilities.
*/
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index e32321c..ef58f46 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -16,7 +16,7 @@
u32 acpi_id, int *apic_id)
{
struct acpi_madt_local_apic *lapic =
- (struct acpi_madt_local_apic *)entry;
+ container_of(entry, struct acpi_madt_local_apic, header);
if (!(lapic->lapic_flags & ACPI_MADT_ENABLED))
return -ENODEV;
@@ -32,7 +32,7 @@
int device_declaration, u32 acpi_id, int *apic_id)
{
struct acpi_madt_local_x2apic *apic =
- (struct acpi_madt_local_x2apic *)entry;
+ container_of(entry, struct acpi_madt_local_x2apic, header);
if (!(apic->lapic_flags & ACPI_MADT_ENABLED))
return -ENODEV;
@@ -49,7 +49,7 @@
int device_declaration, u32 acpi_id, int *apic_id)
{
struct acpi_madt_local_sapic *lsapic =
- (struct acpi_madt_local_sapic *)entry;
+ container_of(entry, struct acpi_madt_local_sapic, header);
if (!(lsapic->lapic_flags & ACPI_MADT_ENABLED))
return -ENODEV;
diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c
index 366ca40..a7a3edd 100644
--- a/drivers/acpi/sbs.c
+++ b/drivers/acpi/sbs.c
@@ -35,6 +35,7 @@
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/power_supply.h>
+#include <linux/dmi.h>
#include "sbshc.h"
#include "battery.h"
@@ -61,6 +62,8 @@
module_param(cache_time, uint, 0644);
MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
+static bool sbs_manager_broken;
+
#define MAX_SBS_BAT 4
#define ACPI_SBS_BLOCK_MAX 32
@@ -109,6 +112,7 @@
u8 batteries_supported:4;
u8 manager_present:1;
u8 charger_present:1;
+ u8 charger_exists:1;
};
#define to_acpi_sbs(x) container_of(x, struct acpi_sbs, charger)
@@ -429,9 +433,19 @@
result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBS_CHARGER,
0x13, (u8 *) & status);
- if (!result)
- sbs->charger_present = (status >> 15) & 0x1;
- return result;
+
+ if (result)
+ return result;
+
+ /*
+ * The spec requires that bit 4 always be 1. If it's not set, assume
+ * that the implementation doesn't support an SBS charger
+ */
+ if (!((status >> 4) & 0x1))
+ return -ENODEV;
+
+ sbs->charger_present = (status >> 15) & 0x1;
+ return 0;
}
static ssize_t acpi_battery_alarm_show(struct device *dev,
@@ -483,16 +497,21 @@
ACPI_SBS_MANAGER, 0x01, (u8 *)&state, 2);
} else if (battery->id == 0)
battery->present = 1;
+
if (result || !battery->present)
return result;
if (saved_present != battery->present) {
battery->update_time = 0;
result = acpi_battery_get_info(battery);
- if (result)
+ if (result) {
+ battery->present = 0;
return result;
+ }
}
result = acpi_battery_get_state(battery);
+ if (result)
+ battery->present = 0;
return result;
}
@@ -524,6 +543,7 @@
result = power_supply_register(&sbs->device->dev, &battery->bat);
if (result)
goto end;
+
result = device_create_file(battery->bat.dev, &alarm_attr);
if (result)
goto end;
@@ -554,6 +574,7 @@
if (result)
goto end;
+ sbs->charger_exists = 1;
sbs->charger.name = "sbs-charger";
sbs->charger.type = POWER_SUPPLY_TYPE_MAINS;
sbs->charger.properties = sbs_ac_props;
@@ -580,9 +601,12 @@
struct acpi_battery *bat;
u8 saved_charger_state = sbs->charger_present;
u8 saved_battery_state;
- acpi_ac_get_present(sbs);
- if (sbs->charger_present != saved_charger_state)
- kobject_uevent(&sbs->charger.dev->kobj, KOBJ_CHANGE);
+
+ if (sbs->charger_exists) {
+ acpi_ac_get_present(sbs);
+ if (sbs->charger_present != saved_charger_state)
+ kobject_uevent(&sbs->charger.dev->kobj, KOBJ_CHANGE);
+ }
if (sbs->manager_present) {
for (id = 0; id < MAX_SBS_BAT; ++id) {
@@ -598,12 +622,31 @@
}
}
+static int disable_sbs_manager(const struct dmi_system_id *d)
+{
+ sbs_manager_broken = true;
+ return 0;
+}
+
+static struct dmi_system_id acpi_sbs_dmi_table[] = {
+ {
+ .callback = disable_sbs_manager,
+ .ident = "Apple",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc.")
+ },
+ },
+ { },
+};
+
static int acpi_sbs_add(struct acpi_device *device)
{
struct acpi_sbs *sbs;
int result = 0;
int id;
+ dmi_check_system(acpi_sbs_dmi_table);
+
sbs = kzalloc(sizeof(struct acpi_sbs), GFP_KERNEL);
if (!sbs) {
result = -ENOMEM;
@@ -619,17 +662,24 @@
device->driver_data = sbs;
result = acpi_charger_add(sbs);
- if (result)
+ if (result && result != -ENODEV)
goto end;
- result = acpi_manager_get_info(sbs);
- if (!result) {
- sbs->manager_present = 1;
- for (id = 0; id < MAX_SBS_BAT; ++id)
- if ((sbs->batteries_supported & (1 << id)))
- acpi_battery_add(sbs, id);
- } else
+ result = 0;
+
+ if (!sbs_manager_broken) {
+ result = acpi_manager_get_info(sbs);
+ if (!result) {
+ sbs->manager_present = 0;
+ for (id = 0; id < MAX_SBS_BAT; ++id)
+ if ((sbs->batteries_supported & (1 << id)))
+ acpi_battery_add(sbs, id);
+ }
+ }
+
+ if (!sbs->manager_present)
acpi_battery_add(sbs, 0);
+
acpi_smbus_register_callback(sbs->hc, acpi_sbs_callback, sbs);
end:
if (result)
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 54da4a3..05a31b5 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -14,6 +14,7 @@
#include <linux/irq.h>
#include <linux/dmi.h>
#include <linux/device.h>
+#include <linux/interrupt.h>
#include <linux/suspend.h>
#include <linux/reboot.h>
#include <linux/acpi.h>
@@ -626,6 +627,19 @@
return 0;
}
+static int acpi_freeze_prepare(void)
+{
+ acpi_enable_all_wakeup_gpes();
+ enable_irq_wake(acpi_gbl_FADT.sci_interrupt);
+ return 0;
+}
+
+static void acpi_freeze_restore(void)
+{
+ disable_irq_wake(acpi_gbl_FADT.sci_interrupt);
+ acpi_enable_all_runtime_gpes();
+}
+
static void acpi_freeze_end(void)
{
acpi_scan_lock_release();
@@ -633,6 +647,8 @@
static const struct platform_freeze_ops acpi_freeze_ops = {
.begin = acpi_freeze_begin,
+ .prepare = acpi_freeze_prepare,
+ .restore = acpi_freeze_restore,
.end = acpi_freeze_end,
};
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
index 07c8c5a..834f35c 100644
--- a/drivers/acpi/utils.c
+++ b/drivers/acpi/utils.c
@@ -661,7 +661,6 @@
* @uuid: UUID of requested functions, should be 16 bytes at least
* @rev: revision number of requested functions
* @funcs: bitmap of requested functions
- * @exclude: excluding special value, used to support i915 and nouveau
*
* Evaluate device's _DSM method to check whether it supports requested
* functions. Currently only support 64 functions at maximum, should be
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index 8e7e185..807a88a 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -411,12 +411,6 @@
return 0;
}
-static int __init video_set_use_native_backlight(const struct dmi_system_id *d)
-{
- use_native_backlight_dmi = true;
- return 0;
-}
-
static int __init video_disable_native_backlight(const struct dmi_system_id *d)
{
use_native_backlight_dmi = false;
@@ -467,265 +461,6 @@
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"),
},
},
- {
- .callback = video_set_use_native_backlight,
- .ident = "ThinkPad X230",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X230"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "ThinkPad T430 and T430s",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T430"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "ThinkPad T430",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "2349D15"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "ThinkPad T431s",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "20AACTO1WW"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "ThinkPad Edge E530",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "3259A2G"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "ThinkPad Edge E530",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "3259CTO"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "ThinkPad Edge E530",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "3259HJG"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "ThinkPad W530",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad W530"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "ThinkPad X1 Carbon",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X1 Carbon"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "Lenovo Yoga 13",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo IdeaPad Yoga 13"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "Lenovo Yoga 2 11",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2 11"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "Thinkpad Helix",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Helix"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "Dell Inspiron 7520",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7520"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "Acer Aspire 5733Z",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5733Z"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "Acer Aspire 5742G",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5742G"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "Acer Aspire V5-171",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
- DMI_MATCH(DMI_PRODUCT_NAME, "V5-171"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "Acer Aspire V5-431",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire V5-431"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "Acer Aspire V5-471G",
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire V5-471G"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "Acer TravelMate B113",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
- DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate B113"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "Acer Aspire V5-572G",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Acer Aspire"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "V5-572G/Dazzle_CX"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "Acer Aspire V5-573G",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Acer Aspire"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "V5-573G/Dazzle_HW"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "ASUS Zenbook Prime UX31A",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
- DMI_MATCH(DMI_PRODUCT_NAME, "UX31A"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "HP ProBook 4340s",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "HP ProBook 4340s"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "HP ProBook 4540s",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "HP ProBook 4540s"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "HP ProBook 2013 models",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
- DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook "),
- DMI_MATCH(DMI_PRODUCT_NAME, " G1"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "HP EliteBook 2013 models",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
- DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook "),
- DMI_MATCH(DMI_PRODUCT_NAME, " G1"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "HP EliteBook 2014 models",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
- DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook "),
- DMI_MATCH(DMI_PRODUCT_NAME, " G2"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "HP ZBook 14",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
- DMI_MATCH(DMI_PRODUCT_NAME, "HP ZBook 14"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "HP ZBook 15",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
- DMI_MATCH(DMI_PRODUCT_NAME, "HP ZBook 15"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "HP ZBook 17",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
- DMI_MATCH(DMI_PRODUCT_NAME, "HP ZBook 17"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "HP EliteBook 8470p",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
- DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook 8470p"),
- },
- },
- {
- .callback = video_set_use_native_backlight,
- .ident = "HP EliteBook 8780w",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
- DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook 8780w"),
- },
- },
/*
* These models have a working acpi_video backlight control, and using
@@ -1419,6 +1154,23 @@
}
}
+static bool acpi_video_device_in_dod(struct acpi_video_device *device)
+{
+ struct acpi_video_bus *video = device->video;
+ int i;
+
+ /* If we have a broken _DOD, no need to test */
+ if (!video->attached_count)
+ return true;
+
+ for (i = 0; i < video->attached_count; i++) {
+ if (video->attached_array[i].bind_info == device)
+ return true;
+ }
+
+ return false;
+}
+
/*
* Arg:
* video : video bus device
@@ -1858,6 +1610,15 @@
static int count;
char *name;
+ /*
+ * Do not create backlight device for video output
+ * device that is not in the enumerated list.
+ */
+ if (!acpi_video_device_in_dod(device)) {
+ dev_dbg(&device->dev->dev, "not in _DOD list, ignore\n");
+ return;
+ }
+
result = acpi_video_init_brightness(device);
if (result)
return;
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index c42feb2..27c4349 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -174,6 +174,14 @@
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5737"),
},
},
+ {
+ .callback = video_detect_force_vendor,
+ .ident = "Lenovo IdeaPad Z570",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Ideapad Z570"),
+ },
+ },
{ },
};
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index 3cf61a1..47bbdc1 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -15,6 +15,7 @@
#include <linux/io.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
#include <linux/amba/bus.h>
#include <linux/sizes.h>
@@ -182,10 +183,16 @@
int ret;
do {
- ret = amba_get_enable_pclk(pcdev);
- if (ret)
+ ret = dev_pm_domain_attach(dev, true);
+ if (ret == -EPROBE_DEFER)
break;
+ ret = amba_get_enable_pclk(pcdev);
+ if (ret) {
+ dev_pm_domain_detach(dev, true);
+ break;
+ }
+
pm_runtime_get_noresume(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
@@ -199,6 +206,7 @@
pm_runtime_put_noidle(dev);
amba_put_disable_pclk(pcdev);
+ dev_pm_domain_detach(dev, true);
} while (0);
return ret;
@@ -220,6 +228,7 @@
pm_runtime_put_noidle(dev);
amba_put_disable_pclk(pcdev);
+ dev_pm_domain_detach(dev, true);
return ret;
}
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index ab4f4ce..b2afc29 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -21,6 +21,7 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
#include <linux/idr.h>
#include <linux/acpi.h>
#include <linux/clk/clk-conf.h>
@@ -506,11 +507,12 @@
if (ret < 0)
return ret;
- acpi_dev_pm_attach(_dev, true);
-
- ret = drv->probe(dev);
- if (ret)
- acpi_dev_pm_detach(_dev, true);
+ ret = dev_pm_domain_attach(_dev, true);
+ if (ret != -EPROBE_DEFER) {
+ ret = drv->probe(dev);
+ if (ret)
+ dev_pm_domain_detach(_dev, true);
+ }
if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
dev_warn(_dev, "probe deferral not supported\n");
@@ -532,7 +534,7 @@
int ret;
ret = drv->remove(dev);
- acpi_dev_pm_detach(_dev, true);
+ dev_pm_domain_detach(_dev, true);
return ret;
}
@@ -543,7 +545,7 @@
struct platform_device *dev = to_platform_device(_dev);
drv->shutdown(dev);
- acpi_dev_pm_detach(_dev, true);
+ dev_pm_domain_detach(_dev, true);
}
/**
diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c
index df2e5ee..b0f1388 100644
--- a/drivers/base/power/common.c
+++ b/drivers/base/power/common.c
@@ -11,6 +11,8 @@
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/pm_clock.h>
+#include <linux/acpi.h>
+#include <linux/pm_domain.h>
/**
* dev_pm_get_subsys_data - Create or refcount power.subsys_data for device.
@@ -82,3 +84,53 @@
return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_put_subsys_data);
+
+/**
+ * dev_pm_domain_attach - Attach a device to its PM domain.
+ * @dev: Device to attach.
+ * @power_on: Used to indicate whether we should power on the device.
+ *
+ * The @dev may only be attached to a single PM domain. By iterating through
+ * the available alternatives we try to find a valid PM domain for the device.
+ * As attachment succeeds, the ->detach() callback in the struct dev_pm_domain
+ * should be assigned by the corresponding attach function.
+ *
+ * This function should typically be invoked from subsystem level code during
+ * the probe phase. Especially for those that holds devices which requires
+ * power management through PM domains.
+ *
+ * Callers must ensure proper synchronization of this function with power
+ * management callbacks.
+ *
+ * Returns 0 on successfully attached PM domain or negative error code.
+ */
+int dev_pm_domain_attach(struct device *dev, bool power_on)
+{
+ int ret;
+
+ ret = acpi_dev_pm_attach(dev, power_on);
+ if (ret)
+ ret = genpd_dev_pm_attach(dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_domain_attach);
+
+/**
+ * dev_pm_domain_detach - Detach a device from its PM domain.
+ * @dev: Device to attach.
+ * @power_off: Used to indicate whether we should power off the device.
+ *
+ * This functions will reverse the actions from dev_pm_domain_attach() and thus
+ * try to detach the @dev from its PM domain. Typically it should be invoked
+ * from subsystem level code during the remove phase.
+ *
+ * Callers must ensure proper synchronization of this function with power
+ * management callbacks.
+ */
+void dev_pm_domain_detach(struct device *dev, bool power_off)
+{
+ if (dev->pm_domain && dev->pm_domain->detach)
+ dev->pm_domain->detach(dev, power_off);
+}
+EXPORT_SYMBOL_GPL(dev_pm_domain_detach);
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index eee55c1..40bc2f4 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -8,6 +8,7 @@
#include <linux/kernel.h>
#include <linux/io.h>
+#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
#include <linux/pm_qos.h>
@@ -25,10 +26,6 @@
__routine = genpd->dev_ops.callback; \
if (__routine) { \
__ret = __routine(dev); \
- } else { \
- __routine = dev_gpd_data(dev)->ops.callback; \
- if (__routine) \
- __ret = __routine(dev); \
} \
__ret; \
})
@@ -70,8 +67,6 @@
return genpd;
}
-#ifdef CONFIG_PM
-
struct generic_pm_domain *dev_to_genpd(struct device *dev)
{
if (IS_ERR_OR_NULL(dev->pm_domain))
@@ -147,13 +142,13 @@
{
s64 usecs64;
- if (!genpd->cpu_data)
+ if (!genpd->cpuidle_data)
return;
usecs64 = genpd->power_on_latency_ns;
do_div(usecs64, NSEC_PER_USEC);
- usecs64 += genpd->cpu_data->saved_exit_latency;
- genpd->cpu_data->idle_state->exit_latency = usecs64;
+ usecs64 += genpd->cpuidle_data->saved_exit_latency;
+ genpd->cpuidle_data->idle_state->exit_latency = usecs64;
}
/**
@@ -193,9 +188,9 @@
return 0;
}
- if (genpd->cpu_data) {
+ if (genpd->cpuidle_data) {
cpuidle_pause_and_lock();
- genpd->cpu_data->idle_state->disabled = true;
+ genpd->cpuidle_data->idle_state->disabled = true;
cpuidle_resume_and_unlock();
goto out;
}
@@ -285,8 +280,6 @@
return genpd ? pm_genpd_poweron(genpd) : -EINVAL;
}
-#endif /* CONFIG_PM */
-
#ifdef CONFIG_PM_RUNTIME
static int genpd_start_dev_no_timing(struct generic_pm_domain *genpd,
@@ -430,7 +423,7 @@
* Queue up the execution of pm_genpd_poweroff() unless it's already been done
* before.
*/
-void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
+static void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
{
queue_work(pm_wq, &genpd->power_off_work);
}
@@ -520,17 +513,17 @@
}
}
- if (genpd->cpu_data) {
+ if (genpd->cpuidle_data) {
/*
- * If cpu_data is set, cpuidle should turn the domain off when
- * the CPU in it is idle. In that case we don't decrement the
- * subdomain counts of the master domains, so that power is not
- * removed from the current domain prematurely as a result of
- * cutting off the masters' power.
+ * If cpuidle_data is set, cpuidle should turn the domain off
+ * when the CPU in it is idle. In that case we don't decrement
+ * the subdomain counts of the master domains, so that power is
+ * not removed from the current domain prematurely as a result
+ * of cutting off the masters' power.
*/
genpd->status = GPD_STATE_POWER_OFF;
cpuidle_pause_and_lock();
- genpd->cpu_data->idle_state->disabled = false;
+ genpd->cpuidle_data->idle_state->disabled = false;
cpuidle_resume_and_unlock();
goto out;
}
@@ -619,8 +612,6 @@
if (IS_ERR(genpd))
return -EINVAL;
- might_sleep_if(!genpd->dev_irq_safe);
-
stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
if (stop_ok && !stop_ok(dev))
return -EBUSY;
@@ -665,8 +656,6 @@
if (IS_ERR(genpd))
return -EINVAL;
- might_sleep_if(!genpd->dev_irq_safe);
-
/* If power.irq_safe, the PM domain is never powered off. */
if (dev->power.irq_safe)
return genpd_start_dev_no_timing(genpd, dev);
@@ -733,6 +722,13 @@
mutex_unlock(&gpd_list_lock);
}
+static int __init genpd_poweroff_unused(void)
+{
+ pm_genpd_poweroff_unused();
+ return 0;
+}
+late_initcall(genpd_poweroff_unused);
+
#else
static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
@@ -741,6 +737,9 @@
return NOTIFY_DONE;
}
+static inline void
+genpd_queue_power_off_work(struct generic_pm_domain *genpd) {}
+
static inline void genpd_power_off_work_fn(struct work_struct *work) {}
#define pm_genpd_runtime_suspend NULL
@@ -774,46 +773,6 @@
return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev);
}
-static int genpd_suspend_dev(struct generic_pm_domain *genpd, struct device *dev)
-{
- return GENPD_DEV_CALLBACK(genpd, int, suspend, dev);
-}
-
-static int genpd_suspend_late(struct generic_pm_domain *genpd, struct device *dev)
-{
- return GENPD_DEV_CALLBACK(genpd, int, suspend_late, dev);
-}
-
-static int genpd_resume_early(struct generic_pm_domain *genpd, struct device *dev)
-{
- return GENPD_DEV_CALLBACK(genpd, int, resume_early, dev);
-}
-
-static int genpd_resume_dev(struct generic_pm_domain *genpd, struct device *dev)
-{
- return GENPD_DEV_CALLBACK(genpd, int, resume, dev);
-}
-
-static int genpd_freeze_dev(struct generic_pm_domain *genpd, struct device *dev)
-{
- return GENPD_DEV_CALLBACK(genpd, int, freeze, dev);
-}
-
-static int genpd_freeze_late(struct generic_pm_domain *genpd, struct device *dev)
-{
- return GENPD_DEV_CALLBACK(genpd, int, freeze_late, dev);
-}
-
-static int genpd_thaw_early(struct generic_pm_domain *genpd, struct device *dev)
-{
- return GENPD_DEV_CALLBACK(genpd, int, thaw_early, dev);
-}
-
-static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev)
-{
- return GENPD_DEV_CALLBACK(genpd, int, thaw, dev);
-}
-
/**
* pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
* @genpd: PM domain to power off, if possible.
@@ -995,7 +954,7 @@
if (IS_ERR(genpd))
return -EINVAL;
- return genpd->suspend_power_off ? 0 : genpd_suspend_dev(genpd, dev);
+ return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
}
/**
@@ -1016,7 +975,7 @@
if (IS_ERR(genpd))
return -EINVAL;
- return genpd->suspend_power_off ? 0 : genpd_suspend_late(genpd, dev);
+ return genpd->suspend_power_off ? 0 : pm_generic_suspend_late(dev);
}
/**
@@ -1103,7 +1062,7 @@
if (IS_ERR(genpd))
return -EINVAL;
- return genpd->suspend_power_off ? 0 : genpd_resume_early(genpd, dev);
+ return genpd->suspend_power_off ? 0 : pm_generic_resume_early(dev);
}
/**
@@ -1124,7 +1083,7 @@
if (IS_ERR(genpd))
return -EINVAL;
- return genpd->suspend_power_off ? 0 : genpd_resume_dev(genpd, dev);
+ return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
}
/**
@@ -1145,7 +1104,7 @@
if (IS_ERR(genpd))
return -EINVAL;
- return genpd->suspend_power_off ? 0 : genpd_freeze_dev(genpd, dev);
+ return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
}
/**
@@ -1167,7 +1126,7 @@
if (IS_ERR(genpd))
return -EINVAL;
- return genpd->suspend_power_off ? 0 : genpd_freeze_late(genpd, dev);
+ return genpd->suspend_power_off ? 0 : pm_generic_freeze_late(dev);
}
/**
@@ -1231,7 +1190,7 @@
if (IS_ERR(genpd))
return -EINVAL;
- return genpd->suspend_power_off ? 0 : genpd_thaw_early(genpd, dev);
+ return genpd->suspend_power_off ? 0 : pm_generic_thaw_early(dev);
}
/**
@@ -1252,7 +1211,7 @@
if (IS_ERR(genpd))
return -EINVAL;
- return genpd->suspend_power_off ? 0 : genpd_thaw_dev(genpd, dev);
+ return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
}
/**
@@ -1344,13 +1303,13 @@
}
/**
- * pm_genpd_syscore_switch - Switch power during system core suspend or resume.
+ * genpd_syscore_switch - Switch power during system core suspend or resume.
* @dev: Device that normally is marked as "always on" to switch power for.
*
* This routine may only be called during the system core (syscore) suspend or
* resume phase for devices whose "always on" flags are set.
*/
-void pm_genpd_syscore_switch(struct device *dev, bool suspend)
+static void genpd_syscore_switch(struct device *dev, bool suspend)
{
struct generic_pm_domain *genpd;
@@ -1366,7 +1325,18 @@
genpd->suspended_count--;
}
}
-EXPORT_SYMBOL_GPL(pm_genpd_syscore_switch);
+
+void pm_genpd_syscore_poweroff(struct device *dev)
+{
+ genpd_syscore_switch(dev, true);
+}
+EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweroff);
+
+void pm_genpd_syscore_poweron(struct device *dev)
+{
+ genpd_syscore_switch(dev, false);
+}
+EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron);
#else
@@ -1466,6 +1436,9 @@
spin_unlock_irq(&dev->power.lock);
+ if (genpd->attach_dev)
+ genpd->attach_dev(dev);
+
mutex_lock(&gpd_data->lock);
gpd_data->base.dev = dev;
list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
@@ -1484,39 +1457,6 @@
}
/**
- * __pm_genpd_of_add_device - Add a device to an I/O PM domain.
- * @genpd_node: Device tree node pointer representing a PM domain to which the
- * the device is added to.
- * @dev: Device to be added.
- * @td: Set of PM QoS timing parameters to attach to the device.
- */
-int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev,
- struct gpd_timing_data *td)
-{
- struct generic_pm_domain *genpd = NULL, *gpd;
-
- dev_dbg(dev, "%s()\n", __func__);
-
- if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev))
- return -EINVAL;
-
- mutex_lock(&gpd_list_lock);
- list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
- if (gpd->of_node == genpd_node) {
- genpd = gpd;
- break;
- }
- }
- mutex_unlock(&gpd_list_lock);
-
- if (!genpd)
- return -EINVAL;
-
- return __pm_genpd_add_device(genpd, dev, td);
-}
-
-
-/**
* __pm_genpd_name_add_device - Find I/O PM domain and add a device to it.
* @domain_name: Name of the PM domain to add the device to.
* @dev: Device to be added.
@@ -1558,6 +1498,9 @@
genpd->device_count--;
genpd->max_off_time_changed = true;
+ if (genpd->detach_dev)
+ genpd->detach_dev(dev);
+
spin_lock_irq(&dev->power.lock);
dev->pm_domain = NULL;
@@ -1744,112 +1687,6 @@
}
/**
- * pm_genpd_add_callbacks - Add PM domain callbacks to a given device.
- * @dev: Device to add the callbacks to.
- * @ops: Set of callbacks to add.
- * @td: Timing data to add to the device along with the callbacks (optional).
- *
- * Every call to this routine should be balanced with a call to
- * __pm_genpd_remove_callbacks() and they must not be nested.
- */
-int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops,
- struct gpd_timing_data *td)
-{
- struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL;
- int ret = 0;
-
- if (!(dev && ops))
- return -EINVAL;
-
- gpd_data_new = __pm_genpd_alloc_dev_data(dev);
- if (!gpd_data_new)
- return -ENOMEM;
-
- pm_runtime_disable(dev);
- device_pm_lock();
-
- ret = dev_pm_get_subsys_data(dev);
- if (ret)
- goto out;
-
- spin_lock_irq(&dev->power.lock);
-
- if (dev->power.subsys_data->domain_data) {
- gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
- } else {
- gpd_data = gpd_data_new;
- dev->power.subsys_data->domain_data = &gpd_data->base;
- }
- gpd_data->refcount++;
- gpd_data->ops = *ops;
- if (td)
- gpd_data->td = *td;
-
- spin_unlock_irq(&dev->power.lock);
-
- out:
- device_pm_unlock();
- pm_runtime_enable(dev);
-
- if (gpd_data != gpd_data_new)
- __pm_genpd_free_dev_data(dev, gpd_data_new);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks);
-
-/**
- * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device.
- * @dev: Device to remove the callbacks from.
- * @clear_td: If set, clear the device's timing data too.
- *
- * This routine can only be called after pm_genpd_add_callbacks().
- */
-int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td)
-{
- struct generic_pm_domain_data *gpd_data = NULL;
- bool remove = false;
- int ret = 0;
-
- if (!(dev && dev->power.subsys_data))
- return -EINVAL;
-
- pm_runtime_disable(dev);
- device_pm_lock();
-
- spin_lock_irq(&dev->power.lock);
-
- if (dev->power.subsys_data->domain_data) {
- gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
- gpd_data->ops = (struct gpd_dev_ops){ NULL };
- if (clear_td)
- gpd_data->td = (struct gpd_timing_data){ 0 };
-
- if (--gpd_data->refcount == 0) {
- dev->power.subsys_data->domain_data = NULL;
- remove = true;
- }
- } else {
- ret = -EINVAL;
- }
-
- spin_unlock_irq(&dev->power.lock);
-
- device_pm_unlock();
- pm_runtime_enable(dev);
-
- if (ret)
- return ret;
-
- dev_pm_put_subsys_data(dev);
- if (remove)
- __pm_genpd_free_dev_data(dev, gpd_data);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks);
-
-/**
* pm_genpd_attach_cpuidle - Connect the given PM domain with cpuidle.
* @genpd: PM domain to be connected with cpuidle.
* @state: cpuidle state this domain can disable/enable.
@@ -1861,7 +1698,7 @@
int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state)
{
struct cpuidle_driver *cpuidle_drv;
- struct gpd_cpu_data *cpu_data;
+ struct gpd_cpuidle_data *cpuidle_data;
struct cpuidle_state *idle_state;
int ret = 0;
@@ -1870,12 +1707,12 @@
genpd_acquire_lock(genpd);
- if (genpd->cpu_data) {
+ if (genpd->cpuidle_data) {
ret = -EEXIST;
goto out;
}
- cpu_data = kzalloc(sizeof(*cpu_data), GFP_KERNEL);
- if (!cpu_data) {
+ cpuidle_data = kzalloc(sizeof(*cpuidle_data), GFP_KERNEL);
+ if (!cpuidle_data) {
ret = -ENOMEM;
goto out;
}
@@ -1893,9 +1730,9 @@
ret = -EAGAIN;
goto err;
}
- cpu_data->idle_state = idle_state;
- cpu_data->saved_exit_latency = idle_state->exit_latency;
- genpd->cpu_data = cpu_data;
+ cpuidle_data->idle_state = idle_state;
+ cpuidle_data->saved_exit_latency = idle_state->exit_latency;
+ genpd->cpuidle_data = cpuidle_data;
genpd_recalc_cpu_exit_latency(genpd);
out:
@@ -1906,7 +1743,7 @@
cpuidle_driver_unref();
err_drv:
- kfree(cpu_data);
+ kfree(cpuidle_data);
goto out;
}
@@ -1929,7 +1766,7 @@
*/
int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd)
{
- struct gpd_cpu_data *cpu_data;
+ struct gpd_cpuidle_data *cpuidle_data;
struct cpuidle_state *idle_state;
int ret = 0;
@@ -1938,20 +1775,20 @@
genpd_acquire_lock(genpd);
- cpu_data = genpd->cpu_data;
- if (!cpu_data) {
+ cpuidle_data = genpd->cpuidle_data;
+ if (!cpuidle_data) {
ret = -ENODEV;
goto out;
}
- idle_state = cpu_data->idle_state;
+ idle_state = cpuidle_data->idle_state;
if (!idle_state->disabled) {
ret = -EAGAIN;
goto out;
}
- idle_state->exit_latency = cpu_data->saved_exit_latency;
+ idle_state->exit_latency = cpuidle_data->saved_exit_latency;
cpuidle_driver_unref();
- genpd->cpu_data = NULL;
- kfree(cpu_data);
+ genpd->cpuidle_data = NULL;
+ kfree(cpuidle_data);
out:
genpd_release_lock(genpd);
@@ -1970,17 +1807,13 @@
/* Default device callbacks for generic PM domains. */
/**
- * pm_genpd_default_save_state - Default "save device state" for PM domians.
+ * pm_genpd_default_save_state - Default "save device state" for PM domains.
* @dev: Device to handle.
*/
static int pm_genpd_default_save_state(struct device *dev)
{
int (*cb)(struct device *__dev);
- cb = dev_gpd_data(dev)->ops.save_state;
- if (cb)
- return cb(dev);
-
if (dev->type && dev->type->pm)
cb = dev->type->pm->runtime_suspend;
else if (dev->class && dev->class->pm)
@@ -1997,17 +1830,13 @@
}
/**
- * pm_genpd_default_restore_state - Default PM domians "restore device state".
+ * pm_genpd_default_restore_state - Default PM domains "restore device state".
* @dev: Device to handle.
*/
static int pm_genpd_default_restore_state(struct device *dev)
{
int (*cb)(struct device *__dev);
- cb = dev_gpd_data(dev)->ops.restore_state;
- if (cb)
- return cb(dev);
-
if (dev->type && dev->type->pm)
cb = dev->type->pm->runtime_resume;
else if (dev->class && dev->class->pm)
@@ -2023,109 +1852,6 @@
return cb ? cb(dev) : 0;
}
-#ifdef CONFIG_PM_SLEEP
-
-/**
- * pm_genpd_default_suspend - Default "device suspend" for PM domians.
- * @dev: Device to handle.
- */
-static int pm_genpd_default_suspend(struct device *dev)
-{
- int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend;
-
- return cb ? cb(dev) : pm_generic_suspend(dev);
-}
-
-/**
- * pm_genpd_default_suspend_late - Default "late device suspend" for PM domians.
- * @dev: Device to handle.
- */
-static int pm_genpd_default_suspend_late(struct device *dev)
-{
- int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late;
-
- return cb ? cb(dev) : pm_generic_suspend_late(dev);
-}
-
-/**
- * pm_genpd_default_resume_early - Default "early device resume" for PM domians.
- * @dev: Device to handle.
- */
-static int pm_genpd_default_resume_early(struct device *dev)
-{
- int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early;
-
- return cb ? cb(dev) : pm_generic_resume_early(dev);
-}
-
-/**
- * pm_genpd_default_resume - Default "device resume" for PM domians.
- * @dev: Device to handle.
- */
-static int pm_genpd_default_resume(struct device *dev)
-{
- int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume;
-
- return cb ? cb(dev) : pm_generic_resume(dev);
-}
-
-/**
- * pm_genpd_default_freeze - Default "device freeze" for PM domians.
- * @dev: Device to handle.
- */
-static int pm_genpd_default_freeze(struct device *dev)
-{
- int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze;
-
- return cb ? cb(dev) : pm_generic_freeze(dev);
-}
-
-/**
- * pm_genpd_default_freeze_late - Default "late device freeze" for PM domians.
- * @dev: Device to handle.
- */
-static int pm_genpd_default_freeze_late(struct device *dev)
-{
- int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late;
-
- return cb ? cb(dev) : pm_generic_freeze_late(dev);
-}
-
-/**
- * pm_genpd_default_thaw_early - Default "early device thaw" for PM domians.
- * @dev: Device to handle.
- */
-static int pm_genpd_default_thaw_early(struct device *dev)
-{
- int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early;
-
- return cb ? cb(dev) : pm_generic_thaw_early(dev);
-}
-
-/**
- * pm_genpd_default_thaw - Default "device thaw" for PM domians.
- * @dev: Device to handle.
- */
-static int pm_genpd_default_thaw(struct device *dev)
-{
- int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw;
-
- return cb ? cb(dev) : pm_generic_thaw(dev);
-}
-
-#else /* !CONFIG_PM_SLEEP */
-
-#define pm_genpd_default_suspend NULL
-#define pm_genpd_default_suspend_late NULL
-#define pm_genpd_default_resume_early NULL
-#define pm_genpd_default_resume NULL
-#define pm_genpd_default_freeze NULL
-#define pm_genpd_default_freeze_late NULL
-#define pm_genpd_default_thaw_early NULL
-#define pm_genpd_default_thaw NULL
-
-#endif /* !CONFIG_PM_SLEEP */
-
/**
* pm_genpd_init - Initialize a generic I/O PM domain object.
* @genpd: PM domain object to initialize.
@@ -2177,15 +1903,452 @@
genpd->domain.ops.complete = pm_genpd_complete;
genpd->dev_ops.save_state = pm_genpd_default_save_state;
genpd->dev_ops.restore_state = pm_genpd_default_restore_state;
- genpd->dev_ops.suspend = pm_genpd_default_suspend;
- genpd->dev_ops.suspend_late = pm_genpd_default_suspend_late;
- genpd->dev_ops.resume_early = pm_genpd_default_resume_early;
- genpd->dev_ops.resume = pm_genpd_default_resume;
- genpd->dev_ops.freeze = pm_genpd_default_freeze;
- genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late;
- genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early;
- genpd->dev_ops.thaw = pm_genpd_default_thaw;
mutex_lock(&gpd_list_lock);
list_add(&genpd->gpd_list_node, &gpd_list);
mutex_unlock(&gpd_list_lock);
}
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
+/*
+ * Device Tree based PM domain providers.
+ *
+ * The code below implements generic device tree based PM domain providers that
+ * bind device tree nodes with generic PM domains registered in the system.
+ *
+ * Any driver that registers generic PM domains and needs to support binding of
+ * devices to these domains is supposed to register a PM domain provider, which
+ * maps a PM domain specifier retrieved from the device tree to a PM domain.
+ *
+ * Two simple mapping functions have been provided for convenience:
+ * - __of_genpd_xlate_simple() for 1:1 device tree node to PM domain mapping.
+ * - __of_genpd_xlate_onecell() for mapping of multiple PM domains per node by
+ * index.
+ */
+
+/**
+ * struct of_genpd_provider - PM domain provider registration structure
+ * @link: Entry in global list of PM domain providers
+ * @node: Pointer to device tree node of PM domain provider
+ * @xlate: Provider-specific xlate callback mapping a set of specifier cells
+ * into a PM domain.
+ * @data: context pointer to be passed into @xlate callback
+ */
+struct of_genpd_provider {
+ struct list_head link;
+ struct device_node *node;
+ genpd_xlate_t xlate;
+ void *data;
+};
+
+/* List of registered PM domain providers. */
+static LIST_HEAD(of_genpd_providers);
+/* Mutex to protect the list above. */
+static DEFINE_MUTEX(of_genpd_mutex);
+
+/**
+ * __of_genpd_xlate_simple() - Xlate function for direct node-domain mapping
+ * @genpdspec: OF phandle args to map into a PM domain
+ * @data: xlate function private data - pointer to struct generic_pm_domain
+ *
+ * This is a generic xlate function that can be used to model PM domains that
+ * have their own device tree nodes. The private data of xlate function needs
+ * to be a valid pointer to struct generic_pm_domain.
+ */
+struct generic_pm_domain *__of_genpd_xlate_simple(
+ struct of_phandle_args *genpdspec,
+ void *data)
+{
+ if (genpdspec->args_count != 0)
+ return ERR_PTR(-EINVAL);
+ return data;
+}
+EXPORT_SYMBOL_GPL(__of_genpd_xlate_simple);
+
+/**
+ * __of_genpd_xlate_onecell() - Xlate function using a single index.
+ * @genpdspec: OF phandle args to map into a PM domain
+ * @data: xlate function private data - pointer to struct genpd_onecell_data
+ *
+ * This is a generic xlate function that can be used to model simple PM domain
+ * controllers that have one device tree node and provide multiple PM domains.
+ * A single cell is used as an index into an array of PM domains specified in
+ * the genpd_onecell_data struct when registering the provider.
+ */
+struct generic_pm_domain *__of_genpd_xlate_onecell(
+ struct of_phandle_args *genpdspec,
+ void *data)
+{
+ struct genpd_onecell_data *genpd_data = data;
+ unsigned int idx = genpdspec->args[0];
+
+ if (genpdspec->args_count != 1)
+ return ERR_PTR(-EINVAL);
+
+ if (idx >= genpd_data->num_domains) {
+ pr_err("%s: invalid domain index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!genpd_data->domains[idx])
+ return ERR_PTR(-ENOENT);
+
+ return genpd_data->domains[idx];
+}
+EXPORT_SYMBOL_GPL(__of_genpd_xlate_onecell);
+
+/**
+ * __of_genpd_add_provider() - Register a PM domain provider for a node
+ * @np: Device node pointer associated with the PM domain provider.
+ * @xlate: Callback for decoding PM domain from phandle arguments.
+ * @data: Context pointer for @xlate callback.
+ */
+int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
+ void *data)
+{
+ struct of_genpd_provider *cp;
+
+ cp = kzalloc(sizeof(*cp), GFP_KERNEL);
+ if (!cp)
+ return -ENOMEM;
+
+ cp->node = of_node_get(np);
+ cp->data = data;
+ cp->xlate = xlate;
+
+ mutex_lock(&of_genpd_mutex);
+ list_add(&cp->link, &of_genpd_providers);
+ mutex_unlock(&of_genpd_mutex);
+ pr_debug("Added domain provider from %s\n", np->full_name);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__of_genpd_add_provider);
+
+/**
+ * of_genpd_del_provider() - Remove a previously registered PM domain provider
+ * @np: Device node pointer associated with the PM domain provider
+ */
+void of_genpd_del_provider(struct device_node *np)
+{
+ struct of_genpd_provider *cp;
+
+ mutex_lock(&of_genpd_mutex);
+ list_for_each_entry(cp, &of_genpd_providers, link) {
+ if (cp->node == np) {
+ list_del(&cp->link);
+ of_node_put(cp->node);
+ kfree(cp);
+ break;
+ }
+ }
+ mutex_unlock(&of_genpd_mutex);
+}
+EXPORT_SYMBOL_GPL(of_genpd_del_provider);
+
+/**
+ * of_genpd_get_from_provider() - Look-up PM domain
+ * @genpdspec: OF phandle args to use for look-up
+ *
+ * Looks for a PM domain provider under the node specified by @genpdspec and if
+ * found, uses xlate function of the provider to map phandle args to a PM
+ * domain.
+ *
+ * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR()
+ * on failure.
+ */
+static struct generic_pm_domain *of_genpd_get_from_provider(
+ struct of_phandle_args *genpdspec)
+{
+ struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
+ struct of_genpd_provider *provider;
+
+ mutex_lock(&of_genpd_mutex);
+
+ /* Check if we have such a provider in our array */
+ list_for_each_entry(provider, &of_genpd_providers, link) {
+ if (provider->node == genpdspec->np)
+ genpd = provider->xlate(genpdspec, provider->data);
+ if (!IS_ERR(genpd))
+ break;
+ }
+
+ mutex_unlock(&of_genpd_mutex);
+
+ return genpd;
+}
+
+/**
+ * genpd_dev_pm_detach - Detach a device from its PM domain.
+ * @dev: Device to attach.
+ * @power_off: Currently not used
+ *
+ * Try to locate a corresponding generic PM domain, which the device was
+ * attached to previously. If such is found, the device is detached from it.
+ */
+static void genpd_dev_pm_detach(struct device *dev, bool power_off)
+{
+ struct generic_pm_domain *pd = NULL, *gpd;
+ int ret = 0;
+
+ if (!dev->pm_domain)
+ return;
+
+ mutex_lock(&gpd_list_lock);
+ list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
+ if (&gpd->domain == dev->pm_domain) {
+ pd = gpd;
+ break;
+ }
+ }
+ mutex_unlock(&gpd_list_lock);
+
+ if (!pd)
+ return;
+
+ dev_dbg(dev, "removing from PM domain %s\n", pd->name);
+
+ while (1) {
+ ret = pm_genpd_remove_device(pd, dev);
+ if (ret != -EAGAIN)
+ break;
+ cond_resched();
+ }
+
+ if (ret < 0) {
+ dev_err(dev, "failed to remove from PM domain %s: %d",
+ pd->name, ret);
+ return;
+ }
+
+ /* Check if PM domain can be powered off after removing this device. */
+ genpd_queue_power_off_work(pd);
+}
+
+/**
+ * genpd_dev_pm_attach - Attach a device to its PM domain using DT.
+ * @dev: Device to attach.
+ *
+ * Parse device's OF node to find a PM domain specifier. If such is found,
+ * attaches the device to retrieved pm_domain ops.
+ *
+ * Both generic and legacy Samsung-specific DT bindings are supported to keep
+ * backwards compatibility with existing DTBs.
+ *
+ * Returns 0 on successfully attached PM domain or negative error code.
+ */
+int genpd_dev_pm_attach(struct device *dev)
+{
+ struct of_phandle_args pd_args;
+ struct generic_pm_domain *pd;
+ int ret;
+
+ if (!dev->of_node)
+ return -ENODEV;
+
+ if (dev->pm_domain)
+ return -EEXIST;
+
+ ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
+ "#power-domain-cells", 0, &pd_args);
+ if (ret < 0) {
+ if (ret != -ENOENT)
+ return ret;
+
+ /*
+ * Try legacy Samsung-specific bindings
+ * (for backwards compatibility of DT ABI)
+ */
+ pd_args.args_count = 0;
+ pd_args.np = of_parse_phandle(dev->of_node,
+ "samsung,power-domain", 0);
+ if (!pd_args.np)
+ return -ENOENT;
+ }
+
+ pd = of_genpd_get_from_provider(&pd_args);
+ if (IS_ERR(pd)) {
+ dev_dbg(dev, "%s() failed to find PM domain: %ld\n",
+ __func__, PTR_ERR(pd));
+ of_node_put(dev->of_node);
+ return PTR_ERR(pd);
+ }
+
+ dev_dbg(dev, "adding to PM domain %s\n", pd->name);
+
+ while (1) {
+ ret = pm_genpd_add_device(pd, dev);
+ if (ret != -EAGAIN)
+ break;
+ cond_resched();
+ }
+
+ if (ret < 0) {
+ dev_err(dev, "failed to add to PM domain %s: %d",
+ pd->name, ret);
+ of_node_put(dev->of_node);
+ return ret;
+ }
+
+ dev->pm_domain->detach = genpd_dev_pm_detach;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
+#endif
+
+
+/*** debugfs support ***/
+
+#ifdef CONFIG_PM_ADVANCED_DEBUG
+#include <linux/pm.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/init.h>
+#include <linux/kobject.h>
+static struct dentry *pm_genpd_debugfs_dir;
+
+/*
+ * TODO: This function is a slightly modified version of rtpm_status_show
+ * from sysfs.c, but dependencies between PM_GENERIC_DOMAINS and PM_RUNTIME
+ * are too loose to generalize it.
+ */
+#ifdef CONFIG_PM_RUNTIME
+static void rtpm_status_str(struct seq_file *s, struct device *dev)
+{
+ static const char * const status_lookup[] = {
+ [RPM_ACTIVE] = "active",
+ [RPM_RESUMING] = "resuming",
+ [RPM_SUSPENDED] = "suspended",
+ [RPM_SUSPENDING] = "suspending"
+ };
+ const char *p = "";
+
+ if (dev->power.runtime_error)
+ p = "error";
+ else if (dev->power.disable_depth)
+ p = "unsupported";
+ else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup))
+ p = status_lookup[dev->power.runtime_status];
+ else
+ WARN_ON(1);
+
+ seq_puts(s, p);
+}
+#else
+static void rtpm_status_str(struct seq_file *s, struct device *dev)
+{
+ seq_puts(s, "active");
+}
+#endif
+
+static int pm_genpd_summary_one(struct seq_file *s,
+ struct generic_pm_domain *gpd)
+{
+ static const char * const status_lookup[] = {
+ [GPD_STATE_ACTIVE] = "on",
+ [GPD_STATE_WAIT_MASTER] = "wait-master",
+ [GPD_STATE_BUSY] = "busy",
+ [GPD_STATE_REPEAT] = "off-in-progress",
+ [GPD_STATE_POWER_OFF] = "off"
+ };
+ struct pm_domain_data *pm_data;
+ const char *kobj_path;
+ struct gpd_link *link;
+ int ret;
+
+ ret = mutex_lock_interruptible(&gpd->lock);
+ if (ret)
+ return -ERESTARTSYS;
+
+ if (WARN_ON(gpd->status >= ARRAY_SIZE(status_lookup)))
+ goto exit;
+ seq_printf(s, "%-30s %-15s ", gpd->name, status_lookup[gpd->status]);
+
+ /*
+ * Modifications on the list require holding locks on both
+ * master and slave, so we are safe.
+ * Also gpd->name is immutable.
+ */
+ list_for_each_entry(link, &gpd->master_links, master_node) {
+ seq_printf(s, "%s", link->slave->name);
+ if (!list_is_last(&link->master_node, &gpd->master_links))
+ seq_puts(s, ", ");
+ }
+
+ list_for_each_entry(pm_data, &gpd->dev_list, list_node) {
+ kobj_path = kobject_get_path(&pm_data->dev->kobj, GFP_KERNEL);
+ if (kobj_path == NULL)
+ continue;
+
+ seq_printf(s, "\n %-50s ", kobj_path);
+ rtpm_status_str(s, pm_data->dev);
+ kfree(kobj_path);
+ }
+
+ seq_puts(s, "\n");
+exit:
+ mutex_unlock(&gpd->lock);
+
+ return 0;
+}
+
+static int pm_genpd_summary_show(struct seq_file *s, void *data)
+{
+ struct generic_pm_domain *gpd;
+ int ret = 0;
+
+ seq_puts(s, " domain status slaves\n");
+ seq_puts(s, " /device runtime status\n");
+ seq_puts(s, "----------------------------------------------------------------------\n");
+
+ ret = mutex_lock_interruptible(&gpd_list_lock);
+ if (ret)
+ return -ERESTARTSYS;
+
+ list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
+ ret = pm_genpd_summary_one(s, gpd);
+ if (ret)
+ break;
+ }
+ mutex_unlock(&gpd_list_lock);
+
+ return ret;
+}
+
+static int pm_genpd_summary_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pm_genpd_summary_show, NULL);
+}
+
+static const struct file_operations pm_genpd_summary_fops = {
+ .open = pm_genpd_summary_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init pm_genpd_debug_init(void)
+{
+ struct dentry *d;
+
+ pm_genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL);
+
+ if (!pm_genpd_debugfs_dir)
+ return -ENOMEM;
+
+ d = debugfs_create_file("pm_genpd_summary", S_IRUGO,
+ pm_genpd_debugfs_dir, NULL, &pm_genpd_summary_fops);
+ if (!d)
+ return -ENOMEM;
+
+ return 0;
+}
+late_initcall(pm_genpd_debug_init);
+
+static void __exit pm_genpd_debug_exit(void)
+{
+ debugfs_remove_recursive(pm_genpd_debugfs_dir);
+}
+__exitcall(pm_genpd_debug_exit);
+#endif /* CONFIG_PM_ADVANCED_DEBUG */
diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c
index a089e3b..d88a62e 100644
--- a/drivers/base/power/domain_governor.c
+++ b/drivers/base/power/domain_governor.c
@@ -42,7 +42,7 @@
* default_stop_ok - Default PM domain governor routine for stopping devices.
* @dev: Device to check.
*/
-bool default_stop_ok(struct device *dev)
+static bool default_stop_ok(struct device *dev)
{
struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
unsigned long flags;
@@ -229,10 +229,7 @@
#else /* !CONFIG_PM_RUNTIME */
-bool default_stop_ok(struct device *dev)
-{
- return false;
-}
+static inline bool default_stop_ok(struct device *dev) { return false; }
#define default_power_down_ok NULL
#define always_on_power_down_ok NULL
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index b67d9ae..4497319 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -540,7 +540,7 @@
* Call the "noirq" resume handlers for all devices in dpm_noirq_list and
* enable device drivers to receive interrupts.
*/
-static void dpm_resume_noirq(pm_message_t state)
+void dpm_resume_noirq(pm_message_t state)
{
struct device *dev;
ktime_t starttime = ktime_get();
@@ -662,7 +662,7 @@
* dpm_resume_early - Execute "early resume" callbacks for all devices.
* @state: PM transition of the system being carried out.
*/
-static void dpm_resume_early(pm_message_t state)
+void dpm_resume_early(pm_message_t state)
{
struct device *dev;
ktime_t starttime = ktime_get();
@@ -1093,7 +1093,7 @@
* Prevent device drivers from receiving interrupts and call the "noirq" suspend
* handlers for all non-sysdev devices.
*/
-static int dpm_suspend_noirq(pm_message_t state)
+int dpm_suspend_noirq(pm_message_t state)
{
ktime_t starttime = ktime_get();
int error = 0;
@@ -1232,7 +1232,7 @@
* dpm_suspend_late - Execute "late suspend" callbacks for all devices.
* @state: PM transition of the system being carried out.
*/
-static int dpm_suspend_late(pm_message_t state)
+int dpm_suspend_late(pm_message_t state)
{
ktime_t starttime = ktime_get();
int error = 0;
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index 95b181d1..a9d26ed 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -92,9 +92,6 @@
* wakeup_count - Report the number of wakeup events related to the device
*/
-static const char enabled[] = "enabled";
-static const char disabled[] = "disabled";
-
const char power_group_name[] = "power";
EXPORT_SYMBOL_GPL(power_group_name);
@@ -336,11 +333,14 @@
#endif /* CONFIG_PM_RUNTIME */
#ifdef CONFIG_PM_SLEEP
+static const char _enabled[] = "enabled";
+static const char _disabled[] = "disabled";
+
static ssize_t
wake_show(struct device * dev, struct device_attribute *attr, char * buf)
{
return sprintf(buf, "%s\n", device_can_wakeup(dev)
- ? (device_may_wakeup(dev) ? enabled : disabled)
+ ? (device_may_wakeup(dev) ? _enabled : _disabled)
: "");
}
@@ -357,11 +357,11 @@
cp = memchr(buf, '\n', n);
if (cp)
len = cp - buf;
- if (len == sizeof enabled - 1
- && strncmp(buf, enabled, sizeof enabled - 1) == 0)
+ if (len == sizeof _enabled - 1
+ && strncmp(buf, _enabled, sizeof _enabled - 1) == 0)
device_set_wakeup_enable(dev, 1);
- else if (len == sizeof disabled - 1
- && strncmp(buf, disabled, sizeof disabled - 1) == 0)
+ else if (len == sizeof _disabled - 1
+ && strncmp(buf, _disabled, sizeof _disabled - 1) == 0)
device_set_wakeup_enable(dev, 0);
else
return -EINVAL;
@@ -570,7 +570,8 @@
char *buf)
{
return sprintf(buf, "%s\n",
- device_async_suspend_enabled(dev) ? enabled : disabled);
+ device_async_suspend_enabled(dev) ?
+ _enabled : _disabled);
}
static ssize_t async_store(struct device *dev, struct device_attribute *attr,
@@ -582,9 +583,10 @@
cp = memchr(buf, '\n', n);
if (cp)
len = cp - buf;
- if (len == sizeof enabled - 1 && strncmp(buf, enabled, len) == 0)
+ if (len == sizeof _enabled - 1 && strncmp(buf, _enabled, len) == 0)
device_enable_async_suspend(dev);
- else if (len == sizeof disabled - 1 && strncmp(buf, disabled, len) == 0)
+ else if (len == sizeof _disabled - 1 &&
+ strncmp(buf, _disabled, len) == 0)
device_disable_async_suspend(dev);
else
return -EINVAL;
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index eb1bd2e..c2744b3 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -24,6 +24,9 @@
*/
bool events_check_enabled __read_mostly;
+/* If set and the system is suspending, terminate the suspend. */
+static bool pm_abort_suspend __read_mostly;
+
/*
* Combined counters of registered wakeup events and wakeup events in progress.
* They need to be modified together atomically, so it's better to use one
@@ -719,7 +722,18 @@
pm_print_active_wakeup_sources();
}
- return ret;
+ return ret || pm_abort_suspend;
+}
+
+void pm_system_wakeup(void)
+{
+ pm_abort_suspend = true;
+ freeze_wake();
+}
+
+void pm_wakeup_clear(void)
+{
+ pm_abort_suspend = false;
}
/**
diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c
index dbb8350..8d98a32 100644
--- a/drivers/base/syscore.c
+++ b/drivers/base/syscore.c
@@ -9,7 +9,7 @@
#include <linux/syscore_ops.h>
#include <linux/mutex.h>
#include <linux/module.h>
-#include <linux/interrupt.h>
+#include <linux/suspend.h>
#include <trace/events/power.h>
static LIST_HEAD(syscore_ops_list);
@@ -54,9 +54,8 @@
pr_debug("Checking wakeup interrupts\n");
/* Return error code if there are any wakeup interrupts pending. */
- ret = check_wakeup_irqs();
- if (ret)
- return ret;
+ if (pm_wakeup_pending())
+ return -EBUSY;
WARN_ONCE(!irqs_disabled(),
"Interrupts enabled before system core suspend.\n");
diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig
index 32748c3..c5029c1 100644
--- a/drivers/cpuidle/Kconfig
+++ b/drivers/cpuidle/Kconfig
@@ -25,11 +25,19 @@
bool "Menu governor (for tickless system)"
default y
+config DT_IDLE_STATES
+ bool
+
menu "ARM CPU Idle Drivers"
depends on ARM
source "drivers/cpuidle/Kconfig.arm"
endmenu
+menu "ARM64 CPU Idle Drivers"
+depends on ARM64
+source "drivers/cpuidle/Kconfig.arm64"
+endmenu
+
menu "MIPS CPU Idle Drivers"
depends on MIPS
source "drivers/cpuidle/Kconfig.mips"
diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm
index 38cff69..e339c7f 100644
--- a/drivers/cpuidle/Kconfig.arm
+++ b/drivers/cpuidle/Kconfig.arm
@@ -7,6 +7,7 @@
depends on MCPM
select ARM_CPU_SUSPEND
select CPU_IDLE_MULTIPLE_DRIVERS
+ select DT_IDLE_STATES
help
Select this option to enable CPU idle driver for big.LITTLE based
ARM systems. Driver manages CPUs coordination through MCPM and
diff --git a/drivers/cpuidle/Kconfig.arm64 b/drivers/cpuidle/Kconfig.arm64
new file mode 100644
index 0000000..d0a08ed
--- /dev/null
+++ b/drivers/cpuidle/Kconfig.arm64
@@ -0,0 +1,14 @@
+#
+# ARM64 CPU Idle drivers
+#
+
+config ARM64_CPUIDLE
+ bool "Generic ARM64 CPU idle Driver"
+ select ARM64_CPU_SUSPEND
+ select DT_IDLE_STATES
+ help
+ Select this to enable generic cpuidle driver for ARM64.
+ It provides a generic idle driver whose idle states are configured
+ at run-time through DT nodes. The CPUidle suspend backend is
+ initialized by calling the CPU operations init idle hook
+ provided by architecture code.
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
index 11edb31..4d177b9 100644
--- a/drivers/cpuidle/Makefile
+++ b/drivers/cpuidle/Makefile
@@ -4,6 +4,7 @@
obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
+obj-$(CONFIG_DT_IDLE_STATES) += dt_idle_states.o
##################################################################################
# ARM SoC drivers
@@ -22,6 +23,10 @@
obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o
###############################################################################
+# ARM64 drivers
+obj-$(CONFIG_ARM64_CPUIDLE) += cpuidle-arm64.o
+
+###############################################################################
# POWERPC drivers
obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o
obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o
diff --git a/drivers/cpuidle/cpuidle-arm64.c b/drivers/cpuidle/cpuidle-arm64.c
new file mode 100644
index 0000000..50997ea
--- /dev/null
+++ b/drivers/cpuidle/cpuidle-arm64.c
@@ -0,0 +1,133 @@
+/*
+ * ARM64 generic CPU idle driver.
+ *
+ * Copyright (C) 2014 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.
+ */
+
+#define pr_fmt(fmt) "CPUidle arm64: " fmt
+
+#include <linux/cpuidle.h>
+#include <linux/cpumask.h>
+#include <linux/cpu_pm.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include <asm/cpuidle.h>
+#include <asm/suspend.h>
+
+#include "dt_idle_states.h"
+
+/*
+ * arm64_enter_idle_state - Programs CPU to enter the specified state
+ *
+ * dev: cpuidle device
+ * drv: cpuidle driver
+ * idx: state index
+ *
+ * Called from the CPUidle framework to program the device to the
+ * specified target state selected by the governor.
+ */
+static int arm64_enter_idle_state(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int idx)
+{
+ int ret;
+
+ if (!idx) {
+ cpu_do_idle();
+ return idx;
+ }
+
+ ret = cpu_pm_enter();
+ if (!ret) {
+ /*
+ * Pass idle state index to cpu_suspend which in turn will
+ * call the CPU ops suspend protocol with idle index as a
+ * parameter.
+ */
+ ret = cpu_suspend(idx);
+
+ cpu_pm_exit();
+ }
+
+ return ret ? -1 : idx;
+}
+
+static struct cpuidle_driver arm64_idle_driver = {
+ .name = "arm64_idle",
+ .owner = THIS_MODULE,
+ /*
+ * State at index 0 is standby wfi and considered standard
+ * on all ARM platforms. If in some platforms simple wfi
+ * can't be used as "state 0", DT bindings must be implemented
+ * to work around this issue and allow installing a special
+ * handler for idle state index 0.
+ */
+ .states[0] = {
+ .enter = arm64_enter_idle_state,
+ .exit_latency = 1,
+ .target_residency = 1,
+ .power_usage = UINT_MAX,
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .name = "WFI",
+ .desc = "ARM64 WFI",
+ }
+};
+
+static const struct of_device_id arm64_idle_state_match[] __initconst = {
+ { .compatible = "arm,idle-state",
+ .data = arm64_enter_idle_state },
+ { },
+};
+
+/*
+ * arm64_idle_init
+ *
+ * Registers the arm64 specific cpuidle driver with the cpuidle
+ * framework. It relies on core code to parse the idle states
+ * and initialize them using driver data structures accordingly.
+ */
+static int __init arm64_idle_init(void)
+{
+ int cpu, ret;
+ struct cpuidle_driver *drv = &arm64_idle_driver;
+
+ /*
+ * Initialize idle states data, starting at index 1.
+ * This driver is DT only, if no DT idle states are detected (ret == 0)
+ * let the driver initialization fail accordingly since there is no
+ * reason to initialize the idle driver if only wfi is supported.
+ */
+ ret = dt_init_idle_driver(drv, arm64_idle_state_match, 1);
+ if (ret <= 0) {
+ if (ret)
+ pr_err("failed to initialize idle states\n");
+ return ret ? : -ENODEV;
+ }
+
+ /*
+ * Call arch CPU operations in order to initialize
+ * idle states suspend back-end specific data
+ */
+ for_each_possible_cpu(cpu) {
+ ret = cpu_init_idle(cpu);
+ if (ret) {
+ pr_err("CPU %d failed to init idle CPU ops\n", cpu);
+ return ret;
+ }
+ }
+
+ ret = cpuidle_register(drv, NULL);
+ if (ret) {
+ pr_err("failed to register cpuidle driver\n");
+ return ret;
+ }
+
+ return 0;
+}
+device_initcall(arm64_idle_init);
diff --git a/drivers/cpuidle/cpuidle-big_little.c b/drivers/cpuidle/cpuidle-big_little.c
index ef94c3b..fbc00a1 100644
--- a/drivers/cpuidle/cpuidle-big_little.c
+++ b/drivers/cpuidle/cpuidle-big_little.c
@@ -24,6 +24,8 @@
#include <asm/smp_plat.h>
#include <asm/suspend.h>
+#include "dt_idle_states.h"
+
static int bl_enter_powerdown(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx);
@@ -73,6 +75,12 @@
.state_count = 2,
};
+static const struct of_device_id bl_idle_state_match[] __initconst = {
+ { .compatible = "arm,idle-state",
+ .data = bl_enter_powerdown },
+ { },
+};
+
static struct cpuidle_driver bl_idle_big_driver = {
.name = "big_idle",
.owner = THIS_MODULE,
@@ -159,6 +167,7 @@
static const struct of_device_id compatible_machine_match[] = {
{ .compatible = "arm,vexpress,v2p-ca15_a7" },
{ .compatible = "samsung,exynos5420" },
+ { .compatible = "samsung,exynos5800" },
{},
};
@@ -190,6 +199,17 @@
if (ret)
goto out_uninit_little;
+ /* Start at index 1, index 0 standard WFI */
+ ret = dt_init_idle_driver(&bl_idle_big_driver, bl_idle_state_match, 1);
+ if (ret < 0)
+ goto out_uninit_big;
+
+ /* Start at index 1, index 0 standard WFI */
+ ret = dt_init_idle_driver(&bl_idle_little_driver,
+ bl_idle_state_match, 1);
+ if (ret < 0)
+ goto out_uninit_big;
+
ret = cpuidle_register(&bl_idle_little_driver, NULL);
if (ret)
goto out_uninit_big;
diff --git a/drivers/cpuidle/dt_idle_states.c b/drivers/cpuidle/dt_idle_states.c
new file mode 100644
index 0000000..52f4d11
--- /dev/null
+++ b/drivers/cpuidle/dt_idle_states.c
@@ -0,0 +1,213 @@
+/*
+ * DT idle states parsing code.
+ *
+ * Copyright (C) 2014 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.
+ */
+
+#define pr_fmt(fmt) "DT idle-states: " fmt
+
+#include <linux/cpuidle.h>
+#include <linux/cpumask.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include "dt_idle_states.h"
+
+static int init_state_node(struct cpuidle_state *idle_state,
+ const struct of_device_id *matches,
+ struct device_node *state_node)
+{
+ int err;
+ const struct of_device_id *match_id;
+
+ match_id = of_match_node(matches, state_node);
+ if (!match_id)
+ return -ENODEV;
+ /*
+ * CPUidle drivers are expected to initialize the const void *data
+ * pointer of the passed in struct of_device_id array to the idle
+ * state enter function.
+ */
+ idle_state->enter = match_id->data;
+
+ err = of_property_read_u32(state_node, "wakeup-latency-us",
+ &idle_state->exit_latency);
+ if (err) {
+ u32 entry_latency, exit_latency;
+
+ err = of_property_read_u32(state_node, "entry-latency-us",
+ &entry_latency);
+ if (err) {
+ pr_debug(" * %s missing entry-latency-us property\n",
+ state_node->full_name);
+ return -EINVAL;
+ }
+
+ err = of_property_read_u32(state_node, "exit-latency-us",
+ &exit_latency);
+ if (err) {
+ pr_debug(" * %s missing exit-latency-us property\n",
+ state_node->full_name);
+ return -EINVAL;
+ }
+ /*
+ * If wakeup-latency-us is missing, default to entry+exit
+ * latencies as defined in idle states bindings
+ */
+ idle_state->exit_latency = entry_latency + exit_latency;
+ }
+
+ err = of_property_read_u32(state_node, "min-residency-us",
+ &idle_state->target_residency);
+ if (err) {
+ pr_debug(" * %s missing min-residency-us property\n",
+ state_node->full_name);
+ return -EINVAL;
+ }
+
+ idle_state->flags = CPUIDLE_FLAG_TIME_VALID;
+ if (of_property_read_bool(state_node, "local-timer-stop"))
+ idle_state->flags |= CPUIDLE_FLAG_TIMER_STOP;
+ /*
+ * TODO:
+ * replace with kstrdup and pointer assignment when name
+ * and desc become string pointers
+ */
+ strncpy(idle_state->name, state_node->name, CPUIDLE_NAME_LEN - 1);
+ strncpy(idle_state->desc, state_node->name, CPUIDLE_DESC_LEN - 1);
+ return 0;
+}
+
+/*
+ * Check that the idle state is uniform across all CPUs in the CPUidle driver
+ * cpumask
+ */
+static bool idle_state_valid(struct device_node *state_node, unsigned int idx,
+ const cpumask_t *cpumask)
+{
+ int cpu;
+ struct device_node *cpu_node, *curr_state_node;
+ bool valid = true;
+
+ /*
+ * Compare idle state phandles for index idx on all CPUs in the
+ * CPUidle driver cpumask. Start from next logical cpu following
+ * cpumask_first(cpumask) since that's the CPU state_node was
+ * retrieved from. If a mismatch is found bail out straight
+ * away since we certainly hit a firmware misconfiguration.
+ */
+ for (cpu = cpumask_next(cpumask_first(cpumask), cpumask);
+ cpu < nr_cpu_ids; cpu = cpumask_next(cpu, cpumask)) {
+ cpu_node = of_cpu_device_node_get(cpu);
+ curr_state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
+ idx);
+ if (state_node != curr_state_node)
+ valid = false;
+
+ of_node_put(curr_state_node);
+ of_node_put(cpu_node);
+ if (!valid)
+ break;
+ }
+
+ return valid;
+}
+
+/**
+ * dt_init_idle_driver() - Parse the DT idle states and initialize the
+ * idle driver states array
+ * @drv: Pointer to CPU idle driver to be initialized
+ * @matches: Array of of_device_id match structures to search in for
+ * compatible idle state nodes. The data pointer for each valid
+ * struct of_device_id entry in the matches array must point to
+ * a function with the following signature, that corresponds to
+ * the CPUidle state enter function signature:
+ *
+ * int (*)(struct cpuidle_device *dev,
+ * struct cpuidle_driver *drv,
+ * int index);
+ *
+ * @start_idx: First idle state index to be initialized
+ *
+ * If DT idle states are detected and are valid the state count and states
+ * array entries in the cpuidle driver are initialized accordingly starting
+ * from index start_idx.
+ *
+ * Return: number of valid DT idle states parsed, <0 on failure
+ */
+int dt_init_idle_driver(struct cpuidle_driver *drv,
+ const struct of_device_id *matches,
+ unsigned int start_idx)
+{
+ struct cpuidle_state *idle_state;
+ struct device_node *state_node, *cpu_node;
+ int i, err = 0;
+ const cpumask_t *cpumask;
+ unsigned int state_idx = start_idx;
+
+ if (state_idx >= CPUIDLE_STATE_MAX)
+ return -EINVAL;
+ /*
+ * We get the idle states for the first logical cpu in the
+ * driver mask (or cpu_possible_mask if the driver cpumask is not set)
+ * and we check through idle_state_valid() if they are uniform
+ * across CPUs, otherwise we hit a firmware misconfiguration.
+ */
+ cpumask = drv->cpumask ? : cpu_possible_mask;
+ cpu_node = of_cpu_device_node_get(cpumask_first(cpumask));
+
+ for (i = 0; ; i++) {
+ state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
+ if (!state_node)
+ break;
+
+ if (!idle_state_valid(state_node, i, cpumask)) {
+ pr_warn("%s idle state not valid, bailing out\n",
+ state_node->full_name);
+ err = -EINVAL;
+ break;
+ }
+
+ if (state_idx == CPUIDLE_STATE_MAX) {
+ pr_warn("State index reached static CPU idle driver states array size\n");
+ break;
+ }
+
+ idle_state = &drv->states[state_idx++];
+ err = init_state_node(idle_state, matches, state_node);
+ if (err) {
+ pr_err("Parsing idle state node %s failed with err %d\n",
+ state_node->full_name, err);
+ err = -EINVAL;
+ break;
+ }
+ of_node_put(state_node);
+ }
+
+ of_node_put(state_node);
+ of_node_put(cpu_node);
+ if (err)
+ return err;
+ /*
+ * Update the driver state count only if some valid DT idle states
+ * were detected
+ */
+ if (i)
+ drv->state_count = state_idx;
+
+ /*
+ * Return the number of present and valid DT idle states, which can
+ * also be 0 on platforms with missing DT idle states or legacy DT
+ * configuration predating the DT idle states bindings.
+ */
+ return i;
+}
+EXPORT_SYMBOL_GPL(dt_init_idle_driver);
diff --git a/drivers/cpuidle/dt_idle_states.h b/drivers/cpuidle/dt_idle_states.h
new file mode 100644
index 0000000..4818134
--- /dev/null
+++ b/drivers/cpuidle/dt_idle_states.h
@@ -0,0 +1,7 @@
+#ifndef __DT_IDLE_STATES
+#define __DT_IDLE_STATES
+
+int dt_init_idle_driver(struct cpuidle_driver *drv,
+ const struct of_device_id *matches,
+ unsigned int start_idx);
+#endif
diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c
index ca89412..fb9f511 100644
--- a/drivers/cpuidle/governor.c
+++ b/drivers/cpuidle/governor.c
@@ -28,7 +28,7 @@
struct cpuidle_governor *gov;
list_for_each_entry(gov, &cpuidle_governors, governor_list)
- if (!strnicmp(str, gov->name, CPUIDLE_NAME_LEN))
+ if (!strncasecmp(str, gov->name, CPUIDLE_NAME_LEN))
return gov;
return NULL;
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 1411613..e42925f 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -1310,6 +1310,16 @@
POSTING_READ(RING_FAULT_REG(&dev_priv->ring[RCS]));
}
+static void i915_ggtt_flush(struct drm_i915_private *dev_priv)
+{
+ if (INTEL_INFO(dev_priv->dev)->gen < 6) {
+ intel_gtt_chipset_flush();
+ } else {
+ I915_WRITE(GFX_FLSH_CNTL_GEN6, GFX_FLSH_CNTL_EN);
+ POSTING_READ(GFX_FLSH_CNTL_GEN6);
+ }
+}
+
void i915_gem_suspend_gtt_mappings(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1326,6 +1336,8 @@
dev_priv->gtt.base.start,
dev_priv->gtt.base.total,
true);
+
+ i915_ggtt_flush(dev_priv);
}
void i915_gem_restore_gtt_mappings(struct drm_device *dev)
@@ -1378,7 +1390,7 @@
gen6_write_pdes(container_of(vm, struct i915_hw_ppgtt, base));
}
- i915_gem_chipset_flush(dev);
+ i915_ggtt_flush(dev_priv);
}
int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj)
diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
index ca52ad2..d8de1d5 100644
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -396,6 +396,16 @@
return -EINVAL;
}
+/*
+ * If the vendor backlight interface is not in use and ACPI backlight interface
+ * is broken, do not bother processing backlight change requests from firmware.
+ */
+static bool should_ignore_backlight_request(void)
+{
+ return acpi_video_backlight_support() &&
+ !acpi_video_verify_backlight_support();
+}
+
static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -404,11 +414,7 @@
DRM_DEBUG_DRIVER("bclp = 0x%08x\n", bclp);
- /*
- * If the acpi_video interface is not supposed to be used, don't
- * bother processing backlight level change requests from firmware.
- */
- if (!acpi_video_verify_backlight_support()) {
+ if (should_ignore_backlight_request()) {
DRM_DEBUG_KMS("opregion backlight request ignored\n");
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
index 4b5bb5d..f8cbb51 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
@@ -1763,9 +1763,10 @@
const int or = ffs(outp->or) - 1;
const u32 loff = (or * 0x800) + (link * 0x80);
const u16 mask = (outp->sorconf.link << 6) | outp->or;
+ struct dcb_output match;
u8 ver, hdr;
- if (dcb_outp_match(bios, DCB_OUTPUT_DP, mask, &ver, &hdr, outp))
+ if (dcb_outp_match(bios, DCB_OUTPUT_DP, mask, &ver, &hdr, &match))
nv_mask(priv, 0x61c10c + loff, 0x00000001, 0x00000000);
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c
index 99cd9e4..3440fc9 100644
--- a/drivers/gpu/drm/nouveau/nouveau_chan.c
+++ b/drivers/gpu/drm/nouveau/nouveau_chan.c
@@ -285,6 +285,7 @@
struct nouveau_software_chan *swch;
struct nv_dma_v0 args = {};
int ret, i;
+ bool save;
nvif_object_map(chan->object);
@@ -386,7 +387,11 @@
}
/* initialise synchronisation */
- return nouveau_fence(chan->drm)->context_new(chan);
+ save = cli->base.super;
+ cli->base.super = true; /* hack until fencenv50 fixed */
+ ret = nouveau_fence(chan->drm)->context_new(chan);
+ cli->base.super = save;
+ return ret;
}
int
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 65b4fd5..4a21b2b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -550,14 +550,12 @@
}
int
-nouveau_display_suspend(struct drm_device *dev)
+nouveau_display_suspend(struct drm_device *dev, bool runtime)
{
- struct nouveau_drm *drm = nouveau_drm(dev);
struct drm_crtc *crtc;
nouveau_display_fini(dev);
- NV_INFO(drm, "unpinning framebuffer(s)...\n");
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_framebuffer *nouveau_fb;
@@ -579,12 +577,13 @@
}
void
-nouveau_display_repin(struct drm_device *dev)
+nouveau_display_resume(struct drm_device *dev, bool runtime)
{
struct nouveau_drm *drm = nouveau_drm(dev);
struct drm_crtc *crtc;
- int ret;
+ int ret, head;
+ /* re-pin fb/cursors */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_framebuffer *nouveau_fb;
@@ -606,13 +605,6 @@
if (ret)
NV_ERROR(drm, "Could not pin/map cursor.\n");
}
-}
-
-void
-nouveau_display_resume(struct drm_device *dev)
-{
- struct drm_crtc *crtc;
- int head;
nouveau_display_init(dev);
@@ -627,6 +619,13 @@
for (head = 0; head < dev->mode_config.num_crtc; head++)
drm_vblank_on(dev, head);
+ /* This should ensure we don't hit a locking problem when someone
+ * wakes us up via a connector. We should never go into suspend
+ * while the display is on anyways.
+ */
+ if (runtime)
+ return;
+
drm_helper_resume_force_mode(dev);
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h
index 88ca177..be3d594 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.h
+++ b/drivers/gpu/drm/nouveau/nouveau_display.h
@@ -63,9 +63,8 @@
void nouveau_display_destroy(struct drm_device *dev);
int nouveau_display_init(struct drm_device *dev);
void nouveau_display_fini(struct drm_device *dev);
-int nouveau_display_suspend(struct drm_device *dev);
-void nouveau_display_repin(struct drm_device *dev);
-void nouveau_display_resume(struct drm_device *dev);
+int nouveau_display_suspend(struct drm_device *dev, bool runtime);
+void nouveau_display_resume(struct drm_device *dev, bool runtime);
int nouveau_display_vblank_enable(struct drm_device *, int);
void nouveau_display_vblank_disable(struct drm_device *, int);
int nouveau_display_scanoutpos(struct drm_device *, int, unsigned int,
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index 9c3af96..3ed32dd 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -547,9 +547,11 @@
struct nouveau_cli *cli;
int ret;
- if (dev->mode_config.num_crtc && !runtime) {
+ if (dev->mode_config.num_crtc) {
+ NV_INFO(drm, "suspending console...\n");
+ nouveau_fbcon_set_suspend(dev, 1);
NV_INFO(drm, "suspending display...\n");
- ret = nouveau_display_suspend(dev);
+ ret = nouveau_display_suspend(dev, runtime);
if (ret)
return ret;
}
@@ -603,7 +605,7 @@
fail_display:
if (dev->mode_config.num_crtc) {
NV_INFO(drm, "resuming display...\n");
- nouveau_display_resume(dev);
+ nouveau_display_resume(dev, runtime);
}
return ret;
}
@@ -618,9 +620,6 @@
drm_dev->switch_power_state == DRM_SWITCH_POWER_DYNAMIC_OFF)
return 0;
- if (drm_dev->mode_config.num_crtc)
- nouveau_fbcon_set_suspend(drm_dev, 1);
-
ret = nouveau_do_suspend(drm_dev, false);
if (ret)
return ret;
@@ -633,7 +632,7 @@
}
static int
-nouveau_do_resume(struct drm_device *dev)
+nouveau_do_resume(struct drm_device *dev, bool runtime)
{
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_cli *cli;
@@ -658,7 +657,9 @@
if (dev->mode_config.num_crtc) {
NV_INFO(drm, "resuming display...\n");
- nouveau_display_repin(dev);
+ nouveau_display_resume(dev, runtime);
+ NV_INFO(drm, "resuming console...\n");
+ nouveau_fbcon_set_suspend(dev, 0);
}
return 0;
@@ -681,47 +682,21 @@
return ret;
pci_set_master(pdev);
- ret = nouveau_do_resume(drm_dev);
- if (ret)
- return ret;
-
- if (drm_dev->mode_config.num_crtc) {
- nouveau_display_resume(drm_dev);
- nouveau_fbcon_set_suspend(drm_dev, 0);
- }
-
- return 0;
+ return nouveau_do_resume(drm_dev, false);
}
static int nouveau_pmops_freeze(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
- int ret;
-
- if (drm_dev->mode_config.num_crtc)
- nouveau_fbcon_set_suspend(drm_dev, 1);
-
- ret = nouveau_do_suspend(drm_dev, false);
- return ret;
+ return nouveau_do_suspend(drm_dev, false);
}
static int nouveau_pmops_thaw(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
- int ret;
-
- ret = nouveau_do_resume(drm_dev);
- if (ret)
- return ret;
-
- if (drm_dev->mode_config.num_crtc) {
- nouveau_display_resume(drm_dev);
- nouveau_fbcon_set_suspend(drm_dev, 0);
- }
-
- return 0;
+ return nouveau_do_resume(drm_dev, false);
}
@@ -977,7 +952,7 @@
return ret;
pci_set_master(pdev);
- ret = nouveau_do_resume(drm_dev);
+ ret = nouveau_do_resume(drm_dev, true);
drm_kms_helper_poll_enable(drm_dev);
/* do magic */
nvif_mask(device, 0x88488, (1 << 25), (1 << 25));
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 8bdd270..49fe607 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -486,6 +486,16 @@
.fb_probe = nouveau_fbcon_create,
};
+static void
+nouveau_fbcon_set_suspend_work(struct work_struct *work)
+{
+ struct nouveau_fbdev *fbcon = container_of(work, typeof(*fbcon), work);
+ console_lock();
+ nouveau_fbcon_accel_restore(fbcon->dev);
+ nouveau_fbcon_zfill(fbcon->dev, fbcon);
+ fb_set_suspend(fbcon->helper.fbdev, FBINFO_STATE_RUNNING);
+ console_unlock();
+}
int
nouveau_fbcon_init(struct drm_device *dev)
@@ -503,6 +513,7 @@
if (!fbcon)
return -ENOMEM;
+ INIT_WORK(&fbcon->work, nouveau_fbcon_set_suspend_work);
fbcon->dev = dev;
drm->fbcon = fbcon;
@@ -551,14 +562,14 @@
{
struct nouveau_drm *drm = nouveau_drm(dev);
if (drm->fbcon) {
- console_lock();
- if (state == 0) {
- nouveau_fbcon_accel_restore(dev);
- nouveau_fbcon_zfill(dev, drm->fbcon);
+ if (state == FBINFO_STATE_RUNNING) {
+ schedule_work(&drm->fbcon->work);
+ return;
}
+ flush_work(&drm->fbcon->work);
+ console_lock();
fb_set_suspend(drm->fbcon->helper.fbdev, state);
- if (state == 1)
- nouveau_fbcon_accel_save_disable(dev);
+ nouveau_fbcon_accel_save_disable(dev);
console_unlock();
}
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
index 34658cf..0b465c7 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.h
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
@@ -36,6 +36,7 @@
struct nouveau_framebuffer nouveau_fb;
struct list_head fbdev_list;
struct drm_device *dev;
+ struct work_struct work;
unsigned int saved_flags;
struct nvif_object surf2d;
struct nvif_object clip;
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index 3a4d64e..092d89b 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -674,16 +674,20 @@
qup->adap.dev.of_node = pdev->dev.of_node;
strlcpy(qup->adap.name, "QUP I2C adapter", sizeof(qup->adap.name));
- ret = i2c_add_adapter(&qup->adap);
- if (ret)
- goto fail;
-
pm_runtime_set_autosuspend_delay(qup->dev, MSEC_PER_SEC);
pm_runtime_use_autosuspend(qup->dev);
pm_runtime_set_active(qup->dev);
pm_runtime_enable(qup->dev);
+
+ ret = i2c_add_adapter(&qup->adap);
+ if (ret)
+ goto fail_runtime;
+
return 0;
+fail_runtime:
+ pm_runtime_disable(qup->dev);
+ pm_runtime_set_suspended(qup->dev);
fail:
qup_i2c_disable_clocks(qup);
return ret;
diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
index 93cfc83..b38b052 100644
--- a/drivers/i2c/busses/i2c-rk3x.c
+++ b/drivers/i2c/busses/i2c-rk3x.c
@@ -238,7 +238,7 @@
for (i = 0; i < 8; ++i) {
val = 0;
for (j = 0; j < 4; ++j) {
- if (i2c->processed == i2c->msg->len)
+ if ((i2c->processed == i2c->msg->len) && (cnt != 0))
break;
if (i2c->processed == 0 && cnt == 0)
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index ccfbbab..2f90ac6 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -50,6 +50,7 @@
#include <linux/irqflags.h>
#include <linux/rwsem.h>
#include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
#include <linux/acpi.h>
#include <linux/jump_label.h>
#include <asm/uaccess.h>
@@ -643,10 +644,13 @@
if (status < 0)
return status;
- acpi_dev_pm_attach(&client->dev, true);
- status = driver->probe(client, i2c_match_id(driver->id_table, client));
- if (status)
- acpi_dev_pm_detach(&client->dev, true);
+ status = dev_pm_domain_attach(&client->dev, true);
+ if (status != -EPROBE_DEFER) {
+ status = driver->probe(client, i2c_match_id(driver->id_table,
+ client));
+ if (status)
+ dev_pm_domain_detach(&client->dev, true);
+ }
return status;
}
@@ -666,7 +670,7 @@
status = driver->remove(client);
}
- acpi_dev_pm_detach(&client->dev, true);
+ dev_pm_domain_detach(&client->dev, true);
return status;
}
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 183588b..9f0fbec 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -64,6 +64,10 @@
#define cpu_to_group(cpu) cpu_to_node(cpu)
#define ANY_GROUP NUMA_NO_NODE
+static bool devices_handle_discard_safely = false;
+module_param(devices_handle_discard_safely, bool, 0644);
+MODULE_PARM_DESC(devices_handle_discard_safely,
+ "Set to Y if all devices in each array reliably return zeroes on reads from discarded regions");
static struct workqueue_struct *raid5_wq;
/*
* Stripe cache
@@ -6208,7 +6212,7 @@
mddev->queue->limits.discard_granularity = stripe;
/*
* unaligned part of discard request will be ignored, so can't
- * guarantee discard_zerors_data
+ * guarantee discard_zeroes_data
*/
mddev->queue->limits.discard_zeroes_data = 0;
@@ -6233,6 +6237,18 @@
!bdev_get_queue(rdev->bdev)->
limits.discard_zeroes_data)
discard_supported = false;
+ /* Unfortunately, discard_zeroes_data is not currently
+ * a guarantee - just a hint. So we only allow DISCARD
+ * if the sysadmin has confirmed that only safe devices
+ * are in use by setting a module parameter.
+ */
+ if (!devices_handle_discard_safely) {
+ if (discard_supported) {
+ pr_info("md/raid456: discard support disabled due to uncertainty.\n");
+ pr_info("Set raid456.devices_handle_discard_safely=Y to override.\n");
+ }
+ discard_supported = false;
+ }
}
if (discard_supported &&
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index a7e24848..9da812b 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -3524,6 +3524,7 @@
.disconnect = em28xx_usb_disconnect,
.suspend = em28xx_usb_suspend,
.resume = em28xx_usb_resume,
+ .reset_resume = em28xx_usb_resume,
.id_table = em28xx_id_table,
};
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index 4fa8fef9..65cf7a7 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -16,6 +16,7 @@
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
#include <linux/acpi.h>
#include <linux/mmc/card.h>
@@ -315,7 +316,7 @@
ret = device_add(&func->dev);
if (ret == 0) {
sdio_func_set_present(func);
- acpi_dev_pm_attach(&func->dev, false);
+ dev_pm_domain_attach(&func->dev, false);
}
return ret;
@@ -332,7 +333,7 @@
if (!sdio_func_present(func))
return;
- acpi_dev_pm_detach(&func->dev, false);
+ dev_pm_domain_detach(&func->dev, false);
device_del(&func->dev);
put_device(&func->dev);
}
diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c
index 2fee73b..823d01c 100644
--- a/drivers/net/ethernet/broadcom/bnx2.c
+++ b/drivers/net/ethernet/broadcom/bnx2.c
@@ -3236,8 +3236,9 @@
skb->protocol = eth_type_trans(skb, bp->dev);
- if ((len > (bp->dev->mtu + ETH_HLEN)) &&
- (ntohs(skb->protocol) != 0x8100)) {
+ if (len > (bp->dev->mtu + ETH_HLEN) &&
+ skb->protocol != htons(0x8100) &&
+ skb->protocol != htons(ETH_P_8021AD)) {
dev_kfree_skb(skb);
goto next_rx;
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index e7d3a62..ba49948 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -6918,7 +6918,8 @@
skb->protocol = eth_type_trans(skb, tp->dev);
if (len > (tp->dev->mtu + ETH_HLEN) &&
- skb->protocol != htons(ETH_P_8021Q)) {
+ skb->protocol != htons(ETH_P_8021Q) &&
+ skb->protocol != htons(ETH_P_8021AD)) {
dev_kfree_skb_any(skb);
goto drop_it_no_recycle;
}
diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index ca5d779..e1e02fb 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -30,7 +30,6 @@
#include <linux/of_device.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h>
-#include <linux/pinctrl/consumer.h>
#include "macb.h"
@@ -2071,7 +2070,6 @@
struct phy_device *phydev;
u32 config;
int err = -ENXIO;
- struct pinctrl *pinctrl;
const char *mac;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -2080,15 +2078,6 @@
goto err_out;
}
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl)) {
- err = PTR_ERR(pinctrl);
- if (err == -EPROBE_DEFER)
- goto err_out;
-
- dev_warn(&pdev->dev, "No pinctrl provided\n");
- }
-
err = -ENOMEM;
dev = alloc_etherdev(sizeof(*bp));
if (!dev)
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index 7e2d5d5..871e3a5 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -78,13 +78,13 @@
#endif /* CONFIG_PCI_MSI */
static uint8_t num_vfs[3] = {0, 0, 0};
-static int num_vfs_argc = 3;
+static int num_vfs_argc;
module_param_array(num_vfs, byte , &num_vfs_argc, 0444);
MODULE_PARM_DESC(num_vfs, "enable #num_vfs functions if num_vfs > 0\n"
"num_vfs=port1,port2,port1+2");
static uint8_t probe_vf[3] = {0, 0, 0};
-static int probe_vfs_argc = 3;
+static int probe_vfs_argc;
module_param_array(probe_vf, byte, &probe_vfs_argc, 0444);
MODULE_PARM_DESC(probe_vf, "number of vfs to probe by pf driver (num_vfs > 0)\n"
"probe_vf=port1,port2,port1+2");
diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c
index 3205861..5c40683 100644
--- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c
@@ -135,6 +135,7 @@
int i, j;
struct nx_host_tx_ring *tx_ring = adapter->tx_ring;
+ spin_lock(&adapter->tx_clean_lock);
cmd_buf = tx_ring->cmd_buf_arr;
for (i = 0; i < tx_ring->num_desc; i++) {
buffrag = cmd_buf->frag_array;
@@ -158,6 +159,7 @@
}
cmd_buf++;
}
+ spin_unlock(&adapter->tx_clean_lock);
}
void netxen_free_sw_resources(struct netxen_adapter *adapter)
@@ -1792,9 +1794,9 @@
break;
}
- if (count && netif_running(netdev)) {
- tx_ring->sw_consumer = sw_consumer;
+ tx_ring->sw_consumer = sw_consumer;
+ if (count && netif_running(netdev)) {
smp_mb();
if (netif_queue_stopped(netdev) && netif_carrier_ok(netdev))
diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
index 1159031..5ec5a2b 100644
--- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
@@ -1186,7 +1186,6 @@
return;
smp_mb();
- spin_lock(&adapter->tx_clean_lock);
netif_carrier_off(netdev);
netif_tx_disable(netdev);
@@ -1204,7 +1203,6 @@
netxen_napi_disable(adapter);
netxen_release_tx_buffers(adapter);
- spin_unlock(&adapter->tx_clean_lock);
}
/* Usage: During suspend and firmware recovery module */
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
index 86783e1..3172cdf 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
@@ -1177,9 +1177,8 @@
{
u32 idc_params, val;
- if (qlcnic_83xx_lockless_flash_read32(adapter,
- QLC_83XX_IDC_FLASH_PARAM_ADDR,
- (u8 *)&idc_params, 1)) {
+ if (qlcnic_83xx_flash_read32(adapter, QLC_83XX_IDC_FLASH_PARAM_ADDR,
+ (u8 *)&idc_params, 1)) {
dev_info(&adapter->pdev->dev,
"%s:failed to get IDC params from flash\n", __func__);
adapter->dev_init_timeo = QLC_83XX_IDC_INIT_TIMEOUT_SECS;
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
index 141f116..494e810 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
@@ -1333,21 +1333,21 @@
struct qlcnic_host_tx_ring *tx_ring;
struct qlcnic_esw_statistics port_stats;
struct qlcnic_mac_statistics mac_stats;
- int index, ret, length, size, tx_size, ring;
+ int index, ret, length, size, ring;
char *p;
- tx_size = adapter->drv_tx_rings * QLCNIC_TX_STATS_LEN;
+ memset(data, 0, stats->n_stats * sizeof(u64));
- memset(data, 0, tx_size * sizeof(u64));
for (ring = 0, index = 0; ring < adapter->drv_tx_rings; ring++) {
- if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
+ if (adapter->is_up == QLCNIC_ADAPTER_UP_MAGIC) {
tx_ring = &adapter->tx_ring[ring];
data = qlcnic_fill_tx_queue_stats(data, tx_ring);
qlcnic_update_stats(adapter);
+ } else {
+ data += QLCNIC_TX_STATS_LEN;
}
}
- memset(data, 0, stats->n_stats * sizeof(u64));
length = QLCNIC_STATS_LEN;
for (index = 0; index < length; index++) {
p = (char *)adapter + qlcnic_gstrings_stats[index].stat_offset;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 6e6ee22..b0c1521 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -2786,8 +2786,15 @@
if (IS_ERR(priv->stmmac_clk)) {
dev_warn(priv->device, "%s: warning: cannot get CSR clock\n",
__func__);
- ret = PTR_ERR(priv->stmmac_clk);
- goto error_clk_get;
+ /* If failed to obtain stmmac_clk and specific clk_csr value
+ * is NOT passed from the platform, probe fail.
+ */
+ if (!priv->plat->clk_csr) {
+ ret = PTR_ERR(priv->stmmac_clk);
+ goto error_clk_get;
+ } else {
+ priv->stmmac_clk = NULL;
+ }
}
clk_prepare_enable(priv->stmmac_clk);
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index a9c5eaa..0fcb5e7e 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -387,6 +387,7 @@
int hdr_offset;
u32 net_trans_info;
u32 hash;
+ u32 skb_length = skb->len;
/* We will atmost need two pages to describe the rndis
@@ -562,7 +563,7 @@
drop:
if (ret == 0) {
- net->stats.tx_bytes += skb->len;
+ net->stats.tx_bytes += skb_length;
net->stats.tx_packets++;
} else {
kfree(packet);
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index 3381c4f..0c6adaa 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -112,17 +112,15 @@
return err;
}
+/* Requires RTNL */
static int macvtap_set_queue(struct net_device *dev, struct file *file,
struct macvtap_queue *q)
{
struct macvlan_dev *vlan = netdev_priv(dev);
- int err = -EBUSY;
- rtnl_lock();
if (vlan->numqueues == MAX_MACVTAP_QUEUES)
- goto out;
+ return -EBUSY;
- err = 0;
rcu_assign_pointer(q->vlan, vlan);
rcu_assign_pointer(vlan->taps[vlan->numvtaps], q);
sock_hold(&q->sk);
@@ -136,9 +134,7 @@
vlan->numvtaps++;
vlan->numqueues++;
-out:
- rtnl_unlock();
- return err;
+ return 0;
}
static int macvtap_disable_queue(struct macvtap_queue *q)
@@ -454,11 +450,12 @@
static int macvtap_open(struct inode *inode, struct file *file)
{
struct net *net = current->nsproxy->net_ns;
- struct net_device *dev = dev_get_by_macvtap_minor(iminor(inode));
+ struct net_device *dev;
struct macvtap_queue *q;
- int err;
+ int err = -ENODEV;
- err = -ENODEV;
+ rtnl_lock();
+ dev = dev_get_by_macvtap_minor(iminor(inode));
if (!dev)
goto out;
@@ -498,6 +495,7 @@
if (dev)
dev_put(dev);
+ rtnl_unlock();
return err;
}
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 74760e8..604ef21 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -24,7 +24,7 @@
#include <net/ip6_checksum.h>
/* Version Information */
-#define DRIVER_VERSION "v1.06.0 (2014/03/03)"
+#define DRIVER_VERSION "v1.06.1 (2014/10/01)"
#define DRIVER_AUTHOR "Realtek linux nic maintainers <nic_swsd@realtek.com>"
#define DRIVER_DESC "Realtek RTL8152/RTL8153 Based USB Ethernet Adapters"
#define MODULENAME "r8152"
@@ -1949,10 +1949,34 @@
ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data);
}
+static int rtl_start_rx(struct r8152 *tp)
+{
+ int i, ret = 0;
+
+ INIT_LIST_HEAD(&tp->rx_done);
+ for (i = 0; i < RTL8152_MAX_RX; i++) {
+ INIT_LIST_HEAD(&tp->rx_info[i].list);
+ ret = r8152_submit_rx(tp, &tp->rx_info[i], GFP_KERNEL);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int rtl_stop_rx(struct r8152 *tp)
+{
+ int i;
+
+ for (i = 0; i < RTL8152_MAX_RX; i++)
+ usb_kill_urb(tp->rx_info[i].urb);
+
+ return 0;
+}
+
static int rtl_enable(struct r8152 *tp)
{
u32 ocp_data;
- int i, ret;
r8152b_reset_packet_filter(tp);
@@ -1962,14 +1986,7 @@
rxdy_gated_en(tp, false);
- INIT_LIST_HEAD(&tp->rx_done);
- ret = 0;
- for (i = 0; i < RTL8152_MAX_RX; i++) {
- INIT_LIST_HEAD(&tp->rx_info[i].list);
- ret |= r8152_submit_rx(tp, &tp->rx_info[i], GFP_KERNEL);
- }
-
- return ret;
+ return rtl_start_rx(tp);
}
static int rtl8152_enable(struct r8152 *tp)
@@ -2053,8 +2070,7 @@
mdelay(1);
}
- for (i = 0; i < RTL8152_MAX_RX; i++)
- usb_kill_urb(tp->rx_info[i].urb);
+ rtl_stop_rx(tp);
rtl8152_nic_reset(tp);
}
@@ -2185,28 +2201,6 @@
}
}
-static void rtl_clear_bp(struct r8152 *tp)
-{
- ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_0, 0);
- ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_2, 0);
- ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_4, 0);
- ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_6, 0);
- ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_0, 0);
- ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_2, 0);
- ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_4, 0);
- ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_6, 0);
- mdelay(3);
- ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_BA, 0);
- ocp_write_word(tp, MCU_TYPE_USB, USB_BP_BA, 0);
-}
-
-static void r8153_clear_bp(struct r8152 *tp)
-{
- ocp_write_byte(tp, MCU_TYPE_PLA, PLA_BP_EN, 0);
- ocp_write_byte(tp, MCU_TYPE_USB, USB_BP_EN, 0);
- rtl_clear_bp(tp);
-}
-
static void r8153_teredo_off(struct r8152 *tp)
{
u32 ocp_data;
@@ -2249,8 +2243,6 @@
r8152_mdio_write(tp, MII_BMCR, data);
}
- rtl_clear_bp(tp);
-
set_bit(PHY_RESET, &tp->flags);
}
@@ -2401,8 +2393,6 @@
r8152_mdio_write(tp, MII_BMCR, data);
}
- r8153_clear_bp(tp);
-
if (tp->version == RTL_VER_03) {
data = ocp_reg_read(tp, OCP_EEE_CFG);
data &= ~CTAP_SHORT_EN;
@@ -3083,13 +3073,14 @@
clear_bit(WORK_ENABLE, &tp->flags);
usb_kill_urb(tp->intr_urb);
cancel_delayed_work_sync(&tp->schedule);
+ tasklet_disable(&tp->tl);
if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
+ rtl_stop_rx(tp);
rtl_runtime_suspend_enable(tp, true);
} else {
- tasklet_disable(&tp->tl);
tp->rtl_ops.down(tp);
- tasklet_enable(&tp->tl);
}
+ tasklet_enable(&tp->tl);
}
return 0;
@@ -3108,17 +3099,18 @@
if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
rtl_runtime_suspend_enable(tp, false);
clear_bit(SELECTIVE_SUSPEND, &tp->flags);
+ set_bit(WORK_ENABLE, &tp->flags);
if (tp->speed & LINK_STATUS)
- tp->rtl_ops.disable(tp);
+ rtl_start_rx(tp);
} else {
tp->rtl_ops.up(tp);
rtl8152_set_speed(tp, AUTONEG_ENABLE,
tp->mii.supports_gmii ? SPEED_1000 : SPEED_100,
DUPLEX_FULL);
+ tp->speed = 0;
+ netif_carrier_off(tp->netdev);
+ set_bit(WORK_ENABLE, &tp->flags);
}
- tp->speed = 0;
- netif_carrier_off(tp->netdev);
- set_bit(WORK_ENABLE, &tp->flags);
usb_submit_urb(tp->intr_urb, GFP_KERNEL);
}
@@ -3405,7 +3397,7 @@
if (test_bit(RTL8152_UNPLUG, &tp->flags))
return;
- r8153_power_cut_en(tp, true);
+ r8153_power_cut_en(tp, false);
}
static int rtl_ops_init(struct r8152 *tp, const struct usb_device_id *id)
@@ -3558,7 +3550,11 @@
usb_set_intfdata(intf, NULL);
if (tp) {
- set_bit(RTL8152_UNPLUG, &tp->flags);
+ struct usb_device *udev = tp->udev;
+
+ if (udev->state == USB_STATE_NOTATTACHED)
+ set_bit(RTL8152_UNPLUG, &tp->flags);
+
tasklet_kill(&tp->tl);
unregister_netdev(tp->netdev);
tp->rtl_ops.unload(tp);
diff --git a/drivers/parisc/superio.c b/drivers/parisc/superio.c
index a042d06..8be2096 100644
--- a/drivers/parisc/superio.c
+++ b/drivers/parisc/superio.c
@@ -395,7 +395,8 @@
serial_port.iotype = UPIO_PORT;
serial_port.type = PORT_16550A;
serial_port.uartclk = 115200*16;
- serial_port.fifosize = 16;
+ serial_port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE |
+ UPF_BOOT_AUTOCONF;
/* serial port #1 */
serial_port.iobase = sio_dev.sp1_base;
diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c
index 82e06a8..a9f9c46 100644
--- a/drivers/pci/pcie/pme.c
+++ b/drivers/pci/pcie/pme.c
@@ -41,11 +41,17 @@
}
__setup("pcie_pme=", pcie_pme_setup);
+enum pme_suspend_level {
+ PME_SUSPEND_NONE = 0,
+ PME_SUSPEND_WAKEUP,
+ PME_SUSPEND_NOIRQ,
+};
+
struct pcie_pme_service_data {
spinlock_t lock;
struct pcie_device *srv;
struct work_struct work;
- bool noirq; /* Don't enable the PME interrupt used by this service. */
+ enum pme_suspend_level suspend_level;
};
/**
@@ -223,7 +229,7 @@
spin_lock_irq(&data->lock);
for (;;) {
- if (data->noirq)
+ if (data->suspend_level != PME_SUSPEND_NONE)
break;
pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta);
@@ -250,7 +256,7 @@
spin_lock_irq(&data->lock);
}
- if (!data->noirq)
+ if (data->suspend_level == PME_SUSPEND_NONE)
pcie_pme_interrupt_enable(port, true);
spin_unlock_irq(&data->lock);
@@ -367,6 +373,21 @@
return ret;
}
+static bool pcie_pme_check_wakeup(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+
+ if (!bus)
+ return false;
+
+ list_for_each_entry(dev, &bus->devices, bus_list)
+ if (device_may_wakeup(&dev->dev)
+ || pcie_pme_check_wakeup(dev->subordinate))
+ return true;
+
+ return false;
+}
+
/**
* pcie_pme_suspend - Suspend PCIe PME service device.
* @srv: PCIe service device to suspend.
@@ -375,11 +396,26 @@
{
struct pcie_pme_service_data *data = get_service_data(srv);
struct pci_dev *port = srv->port;
+ bool wakeup;
+ if (device_may_wakeup(&port->dev)) {
+ wakeup = true;
+ } else {
+ down_read(&pci_bus_sem);
+ wakeup = pcie_pme_check_wakeup(port->subordinate);
+ up_read(&pci_bus_sem);
+ }
spin_lock_irq(&data->lock);
- pcie_pme_interrupt_enable(port, false);
- pcie_clear_root_pme_status(port);
- data->noirq = true;
+ if (wakeup) {
+ enable_irq_wake(srv->irq);
+ data->suspend_level = PME_SUSPEND_WAKEUP;
+ } else {
+ struct pci_dev *port = srv->port;
+
+ pcie_pme_interrupt_enable(port, false);
+ pcie_clear_root_pme_status(port);
+ data->suspend_level = PME_SUSPEND_NOIRQ;
+ }
spin_unlock_irq(&data->lock);
synchronize_irq(srv->irq);
@@ -394,12 +430,17 @@
static int pcie_pme_resume(struct pcie_device *srv)
{
struct pcie_pme_service_data *data = get_service_data(srv);
- struct pci_dev *port = srv->port;
spin_lock_irq(&data->lock);
- data->noirq = false;
- pcie_clear_root_pme_status(port);
- pcie_pme_interrupt_enable(port, true);
+ if (data->suspend_level == PME_SUSPEND_NOIRQ) {
+ struct pci_dev *port = srv->port;
+
+ pcie_clear_root_pme_status(port);
+ pcie_pme_interrupt_enable(port, true);
+ } else {
+ disable_irq_wake(srv->irq);
+ }
+ data->suspend_level = PME_SUSPEND_NONE;
spin_unlock_irq(&data->lock);
return 0;
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c
index 87aa28c..2655d4a 100644
--- a/drivers/platform/x86/fujitsu-laptop.c
+++ b/drivers/platform/x86/fujitsu-laptop.c
@@ -1050,6 +1050,13 @@
},
};
+static const struct acpi_device_id fujitsu_ids[] __used = {
+ {ACPI_FUJITSU_HID, 0},
+ {ACPI_FUJITSU_HOTKEY_HID, 0},
+ {"", 0}
+};
+MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
+
static int __init fujitsu_init(void)
{
int ret, result, max_brightness;
@@ -1208,12 +1215,3 @@
MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*");
MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*");
MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*");
-
-static struct pnp_device_id pnp_ids[] __used = {
- {.id = "FUJ02bf"},
- {.id = "FUJ02B1"},
- {.id = "FUJ02E3"},
- {.id = ""}
-};
-
-MODULE_DEVICE_TABLE(pnp, pnp_ids);
diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
index 79788a1..02e69e7 100644
--- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
+++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
@@ -1647,7 +1647,7 @@
if (event_dev->priv_flags & IFF_802_1Q_VLAN)
event_dev = vlan_dev_real_dev(event_dev);
- cdev = cxgbi_device_find_by_netdev(event_dev, NULL);
+ cdev = cxgbi_device_find_by_netdev_rcu(event_dev, NULL);
if (!cdev)
return ret;
diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c
index d65df6d..addd1dd 100644
--- a/drivers/scsi/cxgbi/libcxgbi.c
+++ b/drivers/scsi/cxgbi/libcxgbi.c
@@ -57,6 +57,9 @@
static LIST_HEAD(cdev_list);
static DEFINE_MUTEX(cdev_mutex);
+static LIST_HEAD(cdev_rcu_list);
+static DEFINE_SPINLOCK(cdev_rcu_lock);
+
int cxgbi_device_portmap_create(struct cxgbi_device *cdev, unsigned int base,
unsigned int max_conn)
{
@@ -142,6 +145,10 @@
list_add_tail(&cdev->list_head, &cdev_list);
mutex_unlock(&cdev_mutex);
+ spin_lock(&cdev_rcu_lock);
+ list_add_tail_rcu(&cdev->rcu_node, &cdev_rcu_list);
+ spin_unlock(&cdev_rcu_lock);
+
log_debug(1 << CXGBI_DBG_DEV,
"cdev 0x%p, p# %u.\n", cdev, nports);
return cdev;
@@ -153,9 +160,16 @@
log_debug(1 << CXGBI_DBG_DEV,
"cdev 0x%p, p# %u,%s.\n",
cdev, cdev->nports, cdev->nports ? cdev->ports[0]->name : "");
+
mutex_lock(&cdev_mutex);
list_del(&cdev->list_head);
mutex_unlock(&cdev_mutex);
+
+ spin_lock(&cdev_rcu_lock);
+ list_del_rcu(&cdev->rcu_node);
+ spin_unlock(&cdev_rcu_lock);
+ synchronize_rcu();
+
cxgbi_device_destroy(cdev);
}
EXPORT_SYMBOL_GPL(cxgbi_device_unregister);
@@ -167,12 +181,9 @@
mutex_lock(&cdev_mutex);
list_for_each_entry_safe(cdev, tmp, &cdev_list, list_head) {
if ((cdev->flags & flag) == flag) {
- log_debug(1 << CXGBI_DBG_DEV,
- "cdev 0x%p, p# %u,%s.\n",
- cdev, cdev->nports, cdev->nports ?
- cdev->ports[0]->name : "");
- list_del(&cdev->list_head);
- cxgbi_device_destroy(cdev);
+ mutex_unlock(&cdev_mutex);
+ cxgbi_device_unregister(cdev);
+ mutex_lock(&cdev_mutex);
}
}
mutex_unlock(&cdev_mutex);
@@ -191,6 +202,7 @@
}
}
mutex_unlock(&cdev_mutex);
+
log_debug(1 << CXGBI_DBG_DEV,
"lldev 0x%p, NO match found.\n", lldev);
return NULL;
@@ -230,6 +242,39 @@
}
EXPORT_SYMBOL_GPL(cxgbi_device_find_by_netdev);
+struct cxgbi_device *cxgbi_device_find_by_netdev_rcu(struct net_device *ndev,
+ int *port)
+{
+ struct net_device *vdev = NULL;
+ struct cxgbi_device *cdev;
+ int i;
+
+ if (ndev->priv_flags & IFF_802_1Q_VLAN) {
+ vdev = ndev;
+ ndev = vlan_dev_real_dev(ndev);
+ pr_info("vlan dev %s -> %s.\n", vdev->name, ndev->name);
+ }
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(cdev, &cdev_rcu_list, rcu_node) {
+ for (i = 0; i < cdev->nports; i++) {
+ if (ndev == cdev->ports[i]) {
+ cdev->hbas[i]->vdev = vdev;
+ rcu_read_unlock();
+ if (port)
+ *port = i;
+ return cdev;
+ }
+ }
+ }
+ rcu_read_unlock();
+
+ log_debug(1 << CXGBI_DBG_DEV,
+ "ndev 0x%p, %s, NO match found.\n", ndev, ndev->name);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(cxgbi_device_find_by_netdev_rcu);
+
static struct cxgbi_device *cxgbi_device_find_by_mac(struct net_device *ndev,
int *port)
{
diff --git a/drivers/scsi/cxgbi/libcxgbi.h b/drivers/scsi/cxgbi/libcxgbi.h
index b3e6e75..1d98fad 100644
--- a/drivers/scsi/cxgbi/libcxgbi.h
+++ b/drivers/scsi/cxgbi/libcxgbi.h
@@ -527,6 +527,7 @@
#define CXGBI_FLAG_IPV4_SET 0x10
struct cxgbi_device {
struct list_head list_head;
+ struct list_head rcu_node;
unsigned int flags;
struct net_device **ports;
void *lldev;
@@ -709,6 +710,8 @@
void cxgbi_device_unregister_all(unsigned int flag);
struct cxgbi_device *cxgbi_device_find_by_lldev(void *);
struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *, int *);
+struct cxgbi_device *cxgbi_device_find_by_netdev_rcu(struct net_device *,
+ int *);
int cxgbi_hbas_add(struct cxgbi_device *, u64, unsigned int,
struct scsi_host_template *,
struct scsi_transport_template *);
diff --git a/drivers/sh/pm_runtime.c b/drivers/sh/pm_runtime.c
index 72f6381..fe2c2d5 100644
--- a/drivers/sh/pm_runtime.c
+++ b/drivers/sh/pm_runtime.c
@@ -75,8 +75,6 @@
.con_ids = { NULL, },
};
-static bool default_pm_on;
-
static int __init sh_pm_runtime_init(void)
{
if (IS_ENABLED(CONFIG_ARCH_SHMOBILE_MULTI)) {
@@ -96,16 +94,7 @@
return 0;
}
- default_pm_on = true;
pm_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
return 0;
}
core_initcall(sh_pm_runtime_init);
-
-static int __init sh_pm_runtime_late_init(void)
-{
- if (default_pm_on)
- pm_genpd_poweroff_unused();
- return 0;
-}
-late_initcall(sh_pm_runtime_late_init);
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index ca935df..3907f14 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -35,6 +35,7 @@
#include <linux/spi/spi.h>
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
#include <linux/export.h>
#include <linux/sched/rt.h>
#include <linux/delay.h>
@@ -264,10 +265,12 @@
if (ret)
return ret;
- acpi_dev_pm_attach(dev, true);
- ret = sdrv->probe(to_spi_device(dev));
- if (ret)
- acpi_dev_pm_detach(dev, true);
+ ret = dev_pm_domain_attach(dev, true);
+ if (ret != -EPROBE_DEFER) {
+ ret = sdrv->probe(to_spi_device(dev));
+ if (ret)
+ dev_pm_domain_detach(dev, true);
+ }
return ret;
}
@@ -278,7 +281,7 @@
int ret;
ret = sdrv->remove(to_spi_device(dev));
- acpi_dev_pm_detach(dev, true);
+ dev_pm_domain_detach(dev, true);
return ret;
}
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 3f42785..9bfa725 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -970,6 +970,13 @@
.cmd_per_lun = 1, /* until we override it */
.skip_settle_delay = 1,
.ordered_tag = 1,
+
+ /*
+ * The uas drivers expects tags not to be bigger than the maximum
+ * per-device queue depth, which is not true with the blk-mq tag
+ * allocator.
+ */
+ .disable_blk_mq = true,
};
#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 7c018a1..5f29354 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -3568,15 +3568,9 @@
lru_cache_add_file(page);
unlock_page(page);
page_cache_release(page);
- if (rc == -EAGAIN)
- list_add_tail(&page->lru, &tmplist);
}
+ /* Fallback to the readpage in error/reconnect cases */
kref_put(&rdata->refcount, cifs_readdata_release);
- if (rc == -EAGAIN) {
- /* Re-add pages to the page_list and retry */
- list_splice(&tmplist, page_list);
- continue;
- }
break;
}
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c
index 1a6df4b..52131d8 100644
--- a/fs/cifs/smb1ops.c
+++ b/fs/cifs/smb1ops.c
@@ -586,7 +586,7 @@
tmprc = CIFS_open(xid, &oparms, &oplock, NULL);
if (tmprc == -EOPNOTSUPP)
*symlink = true;
- else
+ else if (tmprc == 0)
CIFSSMBClose(xid, tcon, fid.netfid);
}
diff --git a/fs/cifs/smb2maperror.c b/fs/cifs/smb2maperror.c
index af59d03..8257a5a 100644
--- a/fs/cifs/smb2maperror.c
+++ b/fs/cifs/smb2maperror.c
@@ -256,6 +256,8 @@
{STATUS_DLL_MIGHT_BE_INCOMPATIBLE, -EIO,
"STATUS_DLL_MIGHT_BE_INCOMPATIBLE"},
{STATUS_STOPPED_ON_SYMLINK, -EOPNOTSUPP, "STATUS_STOPPED_ON_SYMLINK"},
+ {STATUS_IO_REPARSE_TAG_NOT_HANDLED, -EOPNOTSUPP,
+ "STATUS_REPARSE_NOT_HANDLED"},
{STATUS_DEVICE_REQUIRES_CLEANING, -EIO,
"STATUS_DEVICE_REQUIRES_CLEANING"},
{STATUS_DEVICE_DOOR_OPEN, -EIO, "STATUS_DEVICE_DOOR_OPEN"},
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index e94457c..b01f6e1 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3104,7 +3104,8 @@
buf->page_len = maxcount;
buf->len += maxcount;
- xdr->page_ptr += (maxcount + PAGE_SIZE - 1) / PAGE_SIZE;
+ xdr->page_ptr += (buf->page_base + maxcount + PAGE_SIZE - 1)
+ / PAGE_SIZE;
/* Use rest of head for padding and remaining ops: */
buf->tail[0].iov_base = xdr->p;
diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c
index e3cfa02..12ba682 100644
--- a/fs/ocfs2/dlm/dlmmaster.c
+++ b/fs/ocfs2/dlm/dlmmaster.c
@@ -2039,6 +2039,10 @@
"and killing the other node now! This node is OK and can continue.\n");
__dlm_print_one_lock_resource(res);
spin_unlock(&res->spinlock);
+ spin_lock(&dlm->master_lock);
+ if (mle)
+ __dlm_put_mle(mle);
+ spin_unlock(&dlm->master_lock);
spin_unlock(&dlm->spinlock);
*ret_data = (void *)res;
dlm_put(dlm);
diff --git a/include/acpi/acnames.h b/include/acpi/acnames.h
index c728113..f97804b 100644
--- a/include/acpi/acnames.h
+++ b/include/acpi/acnames.h
@@ -59,6 +59,10 @@
#define METHOD_NAME__PRS "_PRS"
#define METHOD_NAME__PRT "_PRT"
#define METHOD_NAME__PRW "_PRW"
+#define METHOD_NAME__PS0 "_PS0"
+#define METHOD_NAME__PS1 "_PS1"
+#define METHOD_NAME__PS2 "_PS2"
+#define METHOD_NAME__PS3 "_PS3"
#define METHOD_NAME__REG "_REG"
#define METHOD_NAME__SB_ "_SB_"
#define METHOD_NAME__SEG "_SEG"
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
index b7c89d4..9fc1d71 100644
--- a/include/acpi/acpixf.h
+++ b/include/acpi/acpixf.h
@@ -46,7 +46,7 @@
/* Current ACPICA subsystem version in YYYYMMDD format */
-#define ACPI_CA_VERSION 0x20140724
+#define ACPI_CA_VERSION 0x20140828
#include <acpi/acconfig.h>
#include <acpi/actypes.h>
@@ -692,6 +692,7 @@
*event_status))
ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable_all_gpes(void))
ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_runtime_gpes(void))
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_wakeup_gpes(void))
ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
acpi_get_gpe_device(u32 gpe_index,
diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h
index 7626bfe..29e7937 100644
--- a/include/acpi/actbl1.h
+++ b/include/acpi/actbl1.h
@@ -952,7 +952,8 @@
ACPI_SRAT_TYPE_CPU_AFFINITY = 0,
ACPI_SRAT_TYPE_MEMORY_AFFINITY = 1,
ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY = 2,
- ACPI_SRAT_TYPE_RESERVED = 3 /* 3 and greater are reserved */
+ ACPI_SRAT_TYPE_GICC_AFFINITY = 3,
+ ACPI_SRAT_TYPE_RESERVED = 4 /* 4 and greater are reserved */
};
/*
@@ -968,7 +969,7 @@
u32 flags;
u8 local_sapic_eid;
u8 proximity_domain_hi[3];
- u32 reserved; /* Reserved, must be zero */
+ u32 clock_domain;
};
/* Flags */
@@ -1010,6 +1011,20 @@
#define ACPI_SRAT_CPU_ENABLED (1) /* 00: Use affinity structure */
+/* 3: GICC Affinity (ACPI 5.1) */
+
+struct acpi_srat_gicc_affinity {
+ struct acpi_subtable_header header;
+ u32 proximity_domain;
+ u32 acpi_processor_uid;
+ u32 flags;
+ u32 clock_domain;
+};
+
+/* Flags for struct acpi_srat_gicc_affinity */
+
+#define ACPI_SRAT_GICC_ENABLED (1) /* 00: Use affinity structure */
+
/* Reset to default packing */
#pragma pack()
diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h
index 787bcc8..5480cb2 100644
--- a/include/acpi/actbl3.h
+++ b/include/acpi/actbl3.h
@@ -310,10 +310,15 @@
u32 common_flags;
};
+/* Flag Definitions: timer_flags and virtual_timer_flags above */
+
+#define ACPI_GTDT_GT_IRQ_MODE (1)
+#define ACPI_GTDT_GT_IRQ_POLARITY (1<<1)
+
/* Flag Definitions: common_flags above */
-#define ACPI_GTDT_GT_IS_SECURE_TIMER (1)
-#define ACPI_GTDT_GT_ALWAYS_ON (1<<1)
+#define ACPI_GTDT_GT_IS_SECURE_TIMER (1)
+#define ACPI_GTDT_GT_ALWAYS_ON (1<<1)
/* 1: SBSA Generic Watchdog Structure */
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 807cbc4..b7926bb 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -587,7 +587,6 @@
#if defined(CONFIG_ACPI) && defined(CONFIG_PM)
struct acpi_device *acpi_dev_pm_get_node(struct device *dev);
int acpi_dev_pm_attach(struct device *dev, bool power_on);
-void acpi_dev_pm_detach(struct device *dev, bool power_off);
#else
static inline struct acpi_device *acpi_dev_pm_get_node(struct device *dev)
{
@@ -597,7 +596,6 @@
{
return -ENODEV;
}
-static inline void acpi_dev_pm_detach(struct device *dev, bool power_off) {}
#endif
#ifdef CONFIG_ACPI
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index 698ad05..69517a2 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -193,11 +193,6 @@
/* The following three functions are for the core kernel use only. */
extern void suspend_device_irqs(void);
extern void resume_device_irqs(void);
-#ifdef CONFIG_PM_SLEEP
-extern int check_wakeup_irqs(void);
-#else
-static inline int check_wakeup_irqs(void) { return 0; }
-#endif
/**
* struct irq_affinity_notify - context for notification of IRQ affinity changes
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 62af592..03f48d9 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -173,6 +173,7 @@
* IRQD_IRQ_DISABLED - Disabled state of the interrupt
* IRQD_IRQ_MASKED - Masked state of the interrupt
* IRQD_IRQ_INPROGRESS - In progress state of the interrupt
+ * IRQD_WAKEUP_ARMED - Wakeup mode armed
*/
enum {
IRQD_TRIGGER_MASK = 0xf,
@@ -186,6 +187,7 @@
IRQD_IRQ_DISABLED = (1 << 16),
IRQD_IRQ_MASKED = (1 << 17),
IRQD_IRQ_INPROGRESS = (1 << 18),
+ IRQD_WAKEUP_ARMED = (1 << 19),
};
static inline bool irqd_is_setaffinity_pending(struct irq_data *d)
@@ -257,6 +259,12 @@
return d->state_use_accessors & IRQD_IRQ_INPROGRESS;
}
+static inline bool irqd_is_wakeup_armed(struct irq_data *d)
+{
+ return d->state_use_accessors & IRQD_WAKEUP_ARMED;
+}
+
+
/*
* Functions for chained handlers which can be enabled/disabled by the
* standard disable_irq/enable_irq calls. Must be called with
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index 472c021..cb1a31e 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -36,6 +36,11 @@
* @threads_oneshot: bitfield to handle shared oneshot threads
* @threads_active: number of irqaction threads currently running
* @wait_for_threads: wait queue for sync_irq to wait for threaded handlers
+ * @nr_actions: number of installed actions on this descriptor
+ * @no_suspend_depth: number of irqactions on a irq descriptor with
+ * IRQF_NO_SUSPEND set
+ * @force_resume_depth: number of irqactions on a irq descriptor with
+ * IRQF_FORCE_RESUME set
* @dir: /proc/irq/ procfs entry
* @name: flow handler name for /proc/interrupts output
*/
@@ -68,6 +73,11 @@
unsigned long threads_oneshot;
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
+#ifdef CONFIG_PM_SLEEP
+ unsigned int nr_actions;
+ unsigned int no_suspend_depth;
+ unsigned int force_resume_depth;
+#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 72c0fe0..383fd68 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -619,6 +619,7 @@
*/
struct dev_pm_domain {
struct dev_pm_ops ops;
+ void (*detach)(struct device *dev, bool power_off);
};
/*
@@ -679,12 +680,16 @@
extern void device_pm_lock(void);
extern void dpm_resume_start(pm_message_t state);
extern void dpm_resume_end(pm_message_t state);
+extern void dpm_resume_noirq(pm_message_t state);
+extern void dpm_resume_early(pm_message_t state);
extern void dpm_resume(pm_message_t state);
extern void dpm_complete(pm_message_t state);
extern void device_pm_unlock(void);
extern int dpm_suspend_end(pm_message_t state);
extern int dpm_suspend_start(pm_message_t state);
+extern int dpm_suspend_noirq(pm_message_t state);
+extern int dpm_suspend_late(pm_message_t state);
extern int dpm_suspend(pm_message_t state);
extern int dpm_prepare(pm_message_t state);
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index ebc4c76..73e938b 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -35,18 +35,10 @@
int (*stop)(struct device *dev);
int (*save_state)(struct device *dev);
int (*restore_state)(struct device *dev);
- int (*suspend)(struct device *dev);
- int (*suspend_late)(struct device *dev);
- int (*resume_early)(struct device *dev);
- int (*resume)(struct device *dev);
- int (*freeze)(struct device *dev);
- int (*freeze_late)(struct device *dev);
- int (*thaw_early)(struct device *dev);
- int (*thaw)(struct device *dev);
bool (*active_wakeup)(struct device *dev);
};
-struct gpd_cpu_data {
+struct gpd_cpuidle_data {
unsigned int saved_exit_latency;
struct cpuidle_state *idle_state;
};
@@ -71,7 +63,6 @@
unsigned int suspended_count; /* System suspend device counter */
unsigned int prepared_count; /* Suspend counter of prepared devices */
bool suspend_power_off; /* Power status before system suspend */
- bool dev_irq_safe; /* Device callbacks are IRQ-safe */
int (*power_off)(struct generic_pm_domain *domain);
s64 power_off_latency_ns;
int (*power_on)(struct generic_pm_domain *domain);
@@ -80,8 +71,9 @@
s64 max_off_time_ns; /* Maximum allowed "suspended" time. */
bool max_off_time_changed;
bool cached_power_down_ok;
- struct device_node *of_node; /* Node in device tree */
- struct gpd_cpu_data *cpu_data;
+ struct gpd_cpuidle_data *cpuidle_data;
+ void (*attach_dev)(struct device *dev);
+ void (*detach_dev)(struct device *dev);
};
static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
@@ -108,7 +100,6 @@
struct generic_pm_domain_data {
struct pm_domain_data base;
- struct gpd_dev_ops ops;
struct gpd_timing_data td;
struct notifier_block nb;
struct mutex lock;
@@ -127,17 +118,11 @@
return to_gpd_data(dev->power.subsys_data->domain_data);
}
-extern struct dev_power_governor simple_qos_governor;
-
extern struct generic_pm_domain *dev_to_genpd(struct device *dev);
extern int __pm_genpd_add_device(struct generic_pm_domain *genpd,
struct device *dev,
struct gpd_timing_data *td);
-extern int __pm_genpd_of_add_device(struct device_node *genpd_node,
- struct device *dev,
- struct gpd_timing_data *td);
-
extern int __pm_genpd_name_add_device(const char *domain_name,
struct device *dev,
struct gpd_timing_data *td);
@@ -151,10 +136,6 @@
const char *subdomain_name);
extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
struct generic_pm_domain *target);
-extern int pm_genpd_add_callbacks(struct device *dev,
- struct gpd_dev_ops *ops,
- struct gpd_timing_data *td);
-extern int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td);
extern int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state);
extern int pm_genpd_name_attach_cpuidle(const char *name, int state);
extern int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd);
@@ -165,8 +146,7 @@
extern int pm_genpd_poweron(struct generic_pm_domain *genpd);
extern int pm_genpd_name_poweron(const char *domain_name);
-extern bool default_stop_ok(struct device *dev);
-
+extern struct dev_power_governor simple_qos_governor;
extern struct dev_power_governor pm_domain_always_on_gov;
#else
@@ -184,12 +164,6 @@
{
return -ENOSYS;
}
-static inline int __pm_genpd_of_add_device(struct device_node *genpd_node,
- struct device *dev,
- struct gpd_timing_data *td)
-{
- return -ENOSYS;
-}
static inline int __pm_genpd_name_add_device(const char *domain_name,
struct device *dev,
struct gpd_timing_data *td)
@@ -217,16 +191,6 @@
{
return -ENOSYS;
}
-static inline int pm_genpd_add_callbacks(struct device *dev,
- struct gpd_dev_ops *ops,
- struct gpd_timing_data *td)
-{
- return -ENOSYS;
-}
-static inline int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td)
-{
- return -ENOSYS;
-}
static inline int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int st)
{
return -ENOSYS;
@@ -255,10 +219,6 @@
{
return -ENOSYS;
}
-static inline bool default_stop_ok(struct device *dev)
-{
- return false;
-}
#define simple_qos_governor NULL
#define pm_domain_always_on_gov NULL
#endif
@@ -269,45 +229,87 @@
return __pm_genpd_add_device(genpd, dev, NULL);
}
-static inline int pm_genpd_of_add_device(struct device_node *genpd_node,
- struct device *dev)
-{
- return __pm_genpd_of_add_device(genpd_node, dev, NULL);
-}
-
static inline int pm_genpd_name_add_device(const char *domain_name,
struct device *dev)
{
return __pm_genpd_name_add_device(domain_name, dev, NULL);
}
-static inline int pm_genpd_remove_callbacks(struct device *dev)
-{
- return __pm_genpd_remove_callbacks(dev, true);
-}
-
#ifdef CONFIG_PM_GENERIC_DOMAINS_RUNTIME
-extern void genpd_queue_power_off_work(struct generic_pm_domain *genpd);
extern void pm_genpd_poweroff_unused(void);
#else
-static inline void genpd_queue_power_off_work(struct generic_pm_domain *gpd) {}
static inline void pm_genpd_poweroff_unused(void) {}
#endif
#ifdef CONFIG_PM_GENERIC_DOMAINS_SLEEP
-extern void pm_genpd_syscore_switch(struct device *dev, bool suspend);
+extern void pm_genpd_syscore_poweroff(struct device *dev);
+extern void pm_genpd_syscore_poweron(struct device *dev);
#else
-static inline void pm_genpd_syscore_switch(struct device *dev, bool suspend) {}
+static inline void pm_genpd_syscore_poweroff(struct device *dev) {}
+static inline void pm_genpd_syscore_poweron(struct device *dev) {}
#endif
-static inline void pm_genpd_syscore_poweroff(struct device *dev)
+/* OF PM domain providers */
+struct of_device_id;
+
+struct genpd_onecell_data {
+ struct generic_pm_domain **domains;
+ unsigned int num_domains;
+};
+
+typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args,
+ void *data);
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
+int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
+ void *data);
+void of_genpd_del_provider(struct device_node *np);
+
+struct generic_pm_domain *__of_genpd_xlate_simple(
+ struct of_phandle_args *genpdspec,
+ void *data);
+struct generic_pm_domain *__of_genpd_xlate_onecell(
+ struct of_phandle_args *genpdspec,
+ void *data);
+
+int genpd_dev_pm_attach(struct device *dev);
+#else /* !CONFIG_PM_GENERIC_DOMAINS_OF */
+static inline int __of_genpd_add_provider(struct device_node *np,
+ genpd_xlate_t xlate, void *data)
{
- pm_genpd_syscore_switch(dev, true);
+ return 0;
+}
+static inline void of_genpd_del_provider(struct device_node *np) {}
+
+#define __of_genpd_xlate_simple NULL
+#define __of_genpd_xlate_onecell NULL
+
+static inline int genpd_dev_pm_attach(struct device *dev)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
+
+static inline int of_genpd_add_provider_simple(struct device_node *np,
+ struct generic_pm_domain *genpd)
+{
+ return __of_genpd_add_provider(np, __of_genpd_xlate_simple, genpd);
+}
+static inline int of_genpd_add_provider_onecell(struct device_node *np,
+ struct genpd_onecell_data *data)
+{
+ return __of_genpd_add_provider(np, __of_genpd_xlate_onecell, data);
}
-static inline void pm_genpd_syscore_poweron(struct device *dev)
+#ifdef CONFIG_PM
+extern int dev_pm_domain_attach(struct device *dev, bool power_on);
+extern void dev_pm_domain_detach(struct device *dev, bool power_off);
+#else
+static inline int dev_pm_domain_attach(struct device *dev, bool power_on)
{
- pm_genpd_syscore_switch(dev, false);
+ return -ENODEV;
}
+static inline void dev_pm_domain_detach(struct device *dev, bool power_off) {}
+#endif
#endif /* _LINUX_PM_DOMAIN_H */
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index 519064e..3388c1b 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -189,6 +189,8 @@
struct platform_freeze_ops {
int (*begin)(void);
+ int (*prepare)(void);
+ void (*restore)(void);
void (*end)(void);
};
@@ -371,6 +373,8 @@
extern bool events_check_enabled;
extern bool pm_wakeup_pending(void);
+extern void pm_system_wakeup(void);
+extern void pm_wakeup_clear(void);
extern bool pm_get_wakeup_count(unsigned int *count, bool block);
extern bool pm_save_wakeup_count(unsigned int count);
extern void pm_wakep_autosleep_enabled(bool set);
@@ -418,6 +422,8 @@
#define pm_notifier(fn, pri) do { (void)(fn); } while (0)
static inline bool pm_wakeup_pending(void) { return false; }
+static inline void pm_system_wakeup(void) {}
+static inline void pm_wakeup_clear(void) {}
static inline void lock_system_sleep(void) {}
static inline void unlock_system_sleep(void) {}
diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
index 9bcb220..cf485f9 100644
--- a/include/net/ip6_fib.h
+++ b/include/net/ip6_fib.h
@@ -114,16 +114,13 @@
u32 rt6i_flags;
struct rt6key rt6i_src;
struct rt6key rt6i_prefsrc;
- u32 rt6i_metric;
struct inet6_dev *rt6i_idev;
unsigned long _rt6i_peer;
- u32 rt6i_genid;
-
+ u32 rt6i_metric;
/* more non-fragment space at head required */
unsigned short rt6i_nfheader_len;
-
u8 rt6i_protocol;
};
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index 361d260..e0d6466 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -352,26 +352,12 @@
atomic_inc(&net->ipv4.rt_genid);
}
-#if IS_ENABLED(CONFIG_IPV6)
-static inline int rt_genid_ipv6(struct net *net)
-{
- return atomic_read(&net->ipv6.rt_genid);
-}
-
+extern void (*__fib6_flush_trees)(struct net *net);
static inline void rt_genid_bump_ipv6(struct net *net)
{
- atomic_inc(&net->ipv6.rt_genid);
+ if (__fib6_flush_trees)
+ __fib6_flush_trees(net);
}
-#else
-static inline int rt_genid_ipv6(struct net *net)
-{
- return 0;
-}
-
-static inline void rt_genid_bump_ipv6(struct net *net)
-{
-}
-#endif
#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
static inline struct netns_ieee802154_lowpan *
diff --git a/init/Kconfig b/init/Kconfig
index e84c642..80a6907 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -811,6 +811,7 @@
int "Kernel log buffer size (16 => 64KB, 17 => 128KB)"
range 12 21
default 17
+ depends on PRINTK
help
Select the minimal kernel log buffer size as a power of 2.
The final size is affected by LOG_CPU_MAX_BUF_SHIFT config
@@ -830,6 +831,7 @@
range 0 21
default 12 if !BASE_SMALL
default 0 if BASE_SMALL
+ depends on PRINTK
help
This option allows to increase the default ring buffer size
according to the number of CPUs. The value defines the contribution
@@ -1475,6 +1477,7 @@
config HAVE_FUTEX_CMPXCHG
bool
+ depends on FUTEX
help
Architectures should select this if futex_atomic_cmpxchg_inatomic()
is implemented and always working. This removes a couple of runtime
diff --git a/kernel/events/core.c b/kernel/events/core.c
index d640a8b..963bf13 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7948,8 +7948,10 @@
for_each_task_context_nr(ctxn) {
ret = perf_event_init_context(child, ctxn);
- if (ret)
+ if (ret) {
+ perf_event_free_task(child);
return ret;
+ }
}
return 0;
diff --git a/kernel/fork.c b/kernel/fork.c
index 0cf9cdb..a91e47d 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1360,7 +1360,7 @@
goto bad_fork_cleanup_policy;
retval = audit_alloc(p);
if (retval)
- goto bad_fork_cleanup_policy;
+ goto bad_fork_cleanup_perf;
/* copy all the process information */
shm_init_task(p);
retval = copy_semundo(clone_flags, p);
@@ -1566,8 +1566,9 @@
exit_sem(p);
bad_fork_cleanup_audit:
audit_free(p);
-bad_fork_cleanup_policy:
+bad_fork_cleanup_perf:
perf_event_free_task(p);
+bad_fork_cleanup_policy:
#ifdef CONFIG_NUMA
mpol_put(p->mempolicy);
bad_fork_cleanup_threadgroup_lock:
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 6223fab..8fb52e9 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -342,6 +342,31 @@
return irq_wait_for_poll(desc);
}
+static bool irq_may_run(struct irq_desc *desc)
+{
+ unsigned int mask = IRQD_IRQ_INPROGRESS | IRQD_WAKEUP_ARMED;
+
+ /*
+ * If the interrupt is not in progress and is not an armed
+ * wakeup interrupt, proceed.
+ */
+ if (!irqd_has_set(&desc->irq_data, mask))
+ return true;
+
+ /*
+ * If the interrupt is an armed wakeup source, mark it pending
+ * and suspended, disable it and notify the pm core about the
+ * event.
+ */
+ if (irq_pm_check_wakeup(desc))
+ return false;
+
+ /*
+ * Handle a potential concurrent poll on a different core.
+ */
+ return irq_check_poll(desc);
+}
+
/**
* handle_simple_irq - Simple and software-decoded IRQs.
* @irq: the interrupt number
@@ -359,9 +384,8 @@
{
raw_spin_lock(&desc->lock);
- if (unlikely(irqd_irq_inprogress(&desc->irq_data)))
- if (!irq_check_poll(desc))
- goto out_unlock;
+ if (!irq_may_run(desc))
+ goto out_unlock;
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
kstat_incr_irqs_this_cpu(irq, desc);
@@ -412,9 +436,8 @@
raw_spin_lock(&desc->lock);
mask_ack_irq(desc);
- if (unlikely(irqd_irq_inprogress(&desc->irq_data)))
- if (!irq_check_poll(desc))
- goto out_unlock;
+ if (!irq_may_run(desc))
+ goto out_unlock;
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
kstat_incr_irqs_this_cpu(irq, desc);
@@ -485,9 +508,8 @@
raw_spin_lock(&desc->lock);
- if (unlikely(irqd_irq_inprogress(&desc->irq_data)))
- if (!irq_check_poll(desc))
- goto out;
+ if (!irq_may_run(desc))
+ goto out;
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
kstat_incr_irqs_this_cpu(irq, desc);
@@ -541,19 +563,23 @@
raw_spin_lock(&desc->lock);
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
- /*
- * If we're currently running this IRQ, or its disabled,
- * we shouldn't process the IRQ. Mark it pending, handle
- * the necessary masking and go out
- */
- if (unlikely(irqd_irq_disabled(&desc->irq_data) ||
- irqd_irq_inprogress(&desc->irq_data) || !desc->action)) {
- if (!irq_check_poll(desc)) {
- desc->istate |= IRQS_PENDING;
- mask_ack_irq(desc);
- goto out_unlock;
- }
+
+ if (!irq_may_run(desc)) {
+ desc->istate |= IRQS_PENDING;
+ mask_ack_irq(desc);
+ goto out_unlock;
}
+
+ /*
+ * If its disabled or no action available then mask it and get
+ * out of here.
+ */
+ if (irqd_irq_disabled(&desc->irq_data) || !desc->action) {
+ desc->istate |= IRQS_PENDING;
+ mask_ack_irq(desc);
+ goto out_unlock;
+ }
+
kstat_incr_irqs_this_cpu(irq, desc);
/* Start handling the irq */
@@ -602,18 +628,21 @@
raw_spin_lock(&desc->lock);
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
- /*
- * If we're currently running this IRQ, or its disabled,
- * we shouldn't process the IRQ. Mark it pending, handle
- * the necessary masking and go out
- */
- if (unlikely(irqd_irq_disabled(&desc->irq_data) ||
- irqd_irq_inprogress(&desc->irq_data) || !desc->action)) {
- if (!irq_check_poll(desc)) {
- desc->istate |= IRQS_PENDING;
- goto out_eoi;
- }
+
+ if (!irq_may_run(desc)) {
+ desc->istate |= IRQS_PENDING;
+ goto out_eoi;
}
+
+ /*
+ * If its disabled or no action available then mask it and get
+ * out of here.
+ */
+ if (irqd_irq_disabled(&desc->irq_data) || !desc->action) {
+ desc->istate |= IRQS_PENDING;
+ goto out_eoi;
+ }
+
kstat_incr_irqs_this_cpu(irq, desc);
do {
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
index 099ea2e..4332d76 100644
--- a/kernel/irq/internals.h
+++ b/kernel/irq/internals.h
@@ -63,8 +63,8 @@
extern int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,
unsigned long flags);
-extern void __disable_irq(struct irq_desc *desc, unsigned int irq, bool susp);
-extern void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume);
+extern void __disable_irq(struct irq_desc *desc, unsigned int irq);
+extern void __enable_irq(struct irq_desc *desc, unsigned int irq);
extern int irq_startup(struct irq_desc *desc, bool resend);
extern void irq_shutdown(struct irq_desc *desc);
@@ -194,3 +194,15 @@
__this_cpu_inc(*desc->kstat_irqs);
__this_cpu_inc(kstat.irqs_sum);
}
+
+#ifdef CONFIG_PM_SLEEP
+bool irq_pm_check_wakeup(struct irq_desc *desc);
+void irq_pm_install_action(struct irq_desc *desc, struct irqaction *action);
+void irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action);
+#else
+static inline bool irq_pm_check_wakeup(struct irq_desc *desc) { return false; }
+static inline void
+irq_pm_install_action(struct irq_desc *desc, struct irqaction *action) { }
+static inline void
+irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action) { }
+#endif
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 3dc6a61..0a9104b 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -382,14 +382,8 @@
}
#endif
-void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend)
+void __disable_irq(struct irq_desc *desc, unsigned int irq)
{
- if (suspend) {
- if (!desc->action || (desc->action->flags & IRQF_NO_SUSPEND))
- return;
- desc->istate |= IRQS_SUSPENDED;
- }
-
if (!desc->depth++)
irq_disable(desc);
}
@@ -401,7 +395,7 @@
if (!desc)
return -EINVAL;
- __disable_irq(desc, irq, false);
+ __disable_irq(desc, irq);
irq_put_desc_busunlock(desc, flags);
return 0;
}
@@ -442,20 +436,8 @@
}
EXPORT_SYMBOL(disable_irq);
-void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume)
+void __enable_irq(struct irq_desc *desc, unsigned int irq)
{
- if (resume) {
- if (!(desc->istate & IRQS_SUSPENDED)) {
- if (!desc->action)
- return;
- if (!(desc->action->flags & IRQF_FORCE_RESUME))
- return;
- /* Pretend that it got disabled ! */
- desc->depth++;
- }
- desc->istate &= ~IRQS_SUSPENDED;
- }
-
switch (desc->depth) {
case 0:
err_out:
@@ -497,7 +479,7 @@
KERN_ERR "enable_irq before setup/request_irq: irq %u\n", irq))
goto out;
- __enable_irq(desc, irq, false);
+ __enable_irq(desc, irq);
out:
irq_put_desc_busunlock(desc, flags);
}
@@ -1218,6 +1200,8 @@
new->irq = irq;
*old_ptr = new;
+ irq_pm_install_action(desc, new);
+
/* Reset broken irq detection when installing new handler */
desc->irq_count = 0;
desc->irqs_unhandled = 0;
@@ -1228,7 +1212,7 @@
*/
if (shared && (desc->istate & IRQS_SPURIOUS_DISABLED)) {
desc->istate &= ~IRQS_SPURIOUS_DISABLED;
- __enable_irq(desc, irq, false);
+ __enable_irq(desc, irq);
}
raw_spin_unlock_irqrestore(&desc->lock, flags);
@@ -1336,6 +1320,8 @@
/* Found it - now remove it from the list of entries: */
*action_ptr = action->next;
+ irq_pm_remove_action(desc, action);
+
/* If this was the last handler, shut down the IRQ line: */
if (!desc->action) {
irq_shutdown(desc);
diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c
index abcd6ca..3ca5325 100644
--- a/kernel/irq/pm.c
+++ b/kernel/irq/pm.c
@@ -9,17 +9,105 @@
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/interrupt.h>
+#include <linux/suspend.h>
#include <linux/syscore_ops.h>
#include "internals.h"
+bool irq_pm_check_wakeup(struct irq_desc *desc)
+{
+ if (irqd_is_wakeup_armed(&desc->irq_data)) {
+ irqd_clear(&desc->irq_data, IRQD_WAKEUP_ARMED);
+ desc->istate |= IRQS_SUSPENDED | IRQS_PENDING;
+ desc->depth++;
+ irq_disable(desc);
+ pm_system_wakeup();
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Called from __setup_irq() with desc->lock held after @action has
+ * been installed in the action chain.
+ */
+void irq_pm_install_action(struct irq_desc *desc, struct irqaction *action)
+{
+ desc->nr_actions++;
+
+ if (action->flags & IRQF_FORCE_RESUME)
+ desc->force_resume_depth++;
+
+ WARN_ON_ONCE(desc->force_resume_depth &&
+ desc->force_resume_depth != desc->nr_actions);
+
+ if (action->flags & IRQF_NO_SUSPEND)
+ desc->no_suspend_depth++;
+
+ WARN_ON_ONCE(desc->no_suspend_depth &&
+ desc->no_suspend_depth != desc->nr_actions);
+}
+
+/*
+ * Called from __free_irq() with desc->lock held after @action has
+ * been removed from the action chain.
+ */
+void irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action)
+{
+ desc->nr_actions--;
+
+ if (action->flags & IRQF_FORCE_RESUME)
+ desc->force_resume_depth--;
+
+ if (action->flags & IRQF_NO_SUSPEND)
+ desc->no_suspend_depth--;
+}
+
+static bool suspend_device_irq(struct irq_desc *desc, int irq)
+{
+ if (!desc->action || desc->no_suspend_depth)
+ return false;
+
+ if (irqd_is_wakeup_set(&desc->irq_data)) {
+ irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);
+ /*
+ * We return true here to force the caller to issue
+ * synchronize_irq(). We need to make sure that the
+ * IRQD_WAKEUP_ARMED is visible before we return from
+ * suspend_device_irqs().
+ */
+ return true;
+ }
+
+ desc->istate |= IRQS_SUSPENDED;
+ __disable_irq(desc, irq);
+
+ /*
+ * Hardware which has no wakeup source configuration facility
+ * requires that the non wakeup interrupts are masked at the
+ * chip level. The chip implementation indicates that with
+ * IRQCHIP_MASK_ON_SUSPEND.
+ */
+ if (irq_desc_get_chip(desc)->flags & IRQCHIP_MASK_ON_SUSPEND)
+ mask_irq(desc);
+ return true;
+}
+
/**
* suspend_device_irqs - disable all currently enabled interrupt lines
*
- * During system-wide suspend or hibernation device drivers need to be prevented
- * from receiving interrupts and this function is provided for this purpose.
- * It marks all interrupt lines in use, except for the timer ones, as disabled
- * and sets the IRQS_SUSPENDED flag for each of them.
+ * During system-wide suspend or hibernation device drivers need to be
+ * prevented from receiving interrupts and this function is provided
+ * for this purpose.
+ *
+ * So we disable all interrupts and mark them IRQS_SUSPENDED except
+ * for those which are unused, those which are marked as not
+ * suspendable via an interrupt request with the flag IRQF_NO_SUSPEND
+ * set and those which are marked as active wakeup sources.
+ *
+ * The active wakeup sources are handled by the flow handler entry
+ * code which checks for the IRQD_WAKEUP_ARMED flag, suspends the
+ * interrupt and notifies the pm core about the wakeup.
*/
void suspend_device_irqs(void)
{
@@ -28,18 +116,36 @@
for_each_irq_desc(irq, desc) {
unsigned long flags;
+ bool sync;
raw_spin_lock_irqsave(&desc->lock, flags);
- __disable_irq(desc, irq, true);
+ sync = suspend_device_irq(desc, irq);
raw_spin_unlock_irqrestore(&desc->lock, flags);
- }
- for_each_irq_desc(irq, desc)
- if (desc->istate & IRQS_SUSPENDED)
+ if (sync)
synchronize_irq(irq);
+ }
}
EXPORT_SYMBOL_GPL(suspend_device_irqs);
+static void resume_irq(struct irq_desc *desc, int irq)
+{
+ irqd_clear(&desc->irq_data, IRQD_WAKEUP_ARMED);
+
+ if (desc->istate & IRQS_SUSPENDED)
+ goto resume;
+
+ /* Force resume the interrupt? */
+ if (!desc->force_resume_depth)
+ return;
+
+ /* Pretend that it got disabled ! */
+ desc->depth++;
+resume:
+ desc->istate &= ~IRQS_SUSPENDED;
+ __enable_irq(desc, irq);
+}
+
static void resume_irqs(bool want_early)
{
struct irq_desc *desc;
@@ -54,7 +160,7 @@
continue;
raw_spin_lock_irqsave(&desc->lock, flags);
- __enable_irq(desc, irq, true);
+ resume_irq(desc, irq);
raw_spin_unlock_irqrestore(&desc->lock, flags);
}
}
@@ -93,38 +199,3 @@
resume_irqs(false);
}
EXPORT_SYMBOL_GPL(resume_device_irqs);
-
-/**
- * check_wakeup_irqs - check if any wake-up interrupts are pending
- */
-int check_wakeup_irqs(void)
-{
- struct irq_desc *desc;
- int irq;
-
- for_each_irq_desc(irq, desc) {
- /*
- * Only interrupts which are marked as wakeup source
- * and have not been disabled before the suspend check
- * can abort suspend.
- */
- if (irqd_is_wakeup_set(&desc->irq_data)) {
- if (desc->depth == 1 && desc->istate & IRQS_PENDING)
- return -EBUSY;
- continue;
- }
- /*
- * Check the non wakeup interrupts whether they need
- * to be masked before finally going into suspend
- * state. That's for hardware which has no wakeup
- * source configuration facility. The chip
- * implementation indicates that with
- * IRQCHIP_MASK_ON_SUSPEND.
- */
- if (desc->istate & IRQS_SUSPENDED &&
- irq_desc_get_chip(desc)->flags & IRQCHIP_MASK_ON_SUSPEND)
- mask_irq(desc);
- }
-
- return 0;
-}
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index e4e4121..bbef57f 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -302,6 +302,10 @@
def_bool y
depends on PM_RUNTIME && PM_GENERIC_DOMAINS
+config PM_GENERIC_DOMAINS_OF
+ def_bool y
+ depends on PM_GENERIC_DOMAINS && OF
+
config CPU_PM
bool
depends on SUSPEND || CPU_IDLE
diff --git a/kernel/power/process.c b/kernel/power/process.c
index 4ee194e..7b32322 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -129,6 +129,7 @@
if (!pm_freezing)
atomic_inc(&system_freezing_cnt);
+ pm_wakeup_clear();
printk("Freezing user space processes ... ");
pm_freezing = true;
error = try_to_freeze_tasks(true);
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index f1604d8..791a618 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -725,6 +725,14 @@
clear_bit(bit, addr);
}
+static void memory_bm_clear_current(struct memory_bitmap *bm)
+{
+ int bit;
+
+ bit = max(bm->cur.node_bit - 1, 0);
+ clear_bit(bit, bm->cur.node->data);
+}
+
static int memory_bm_test_bit(struct memory_bitmap *bm, unsigned long pfn)
{
void *addr;
@@ -1333,23 +1341,39 @@
void swsusp_free(void)
{
- struct zone *zone;
- unsigned long pfn, max_zone_pfn;
+ unsigned long fb_pfn, fr_pfn;
- for_each_populated_zone(zone) {
- max_zone_pfn = zone_end_pfn(zone);
- for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
- if (pfn_valid(pfn)) {
- struct page *page = pfn_to_page(pfn);
+ if (!forbidden_pages_map || !free_pages_map)
+ goto out;
- if (swsusp_page_is_forbidden(page) &&
- swsusp_page_is_free(page)) {
- swsusp_unset_page_forbidden(page);
- swsusp_unset_page_free(page);
- __free_page(page);
- }
- }
+ memory_bm_position_reset(forbidden_pages_map);
+ memory_bm_position_reset(free_pages_map);
+
+loop:
+ fr_pfn = memory_bm_next_pfn(free_pages_map);
+ fb_pfn = memory_bm_next_pfn(forbidden_pages_map);
+
+ /*
+ * Find the next bit set in both bitmaps. This is guaranteed to
+ * terminate when fb_pfn == fr_pfn == BM_END_OF_MAP.
+ */
+ do {
+ if (fb_pfn < fr_pfn)
+ fb_pfn = memory_bm_next_pfn(forbidden_pages_map);
+ if (fr_pfn < fb_pfn)
+ fr_pfn = memory_bm_next_pfn(free_pages_map);
+ } while (fb_pfn != fr_pfn);
+
+ if (fr_pfn != BM_END_OF_MAP && pfn_valid(fr_pfn)) {
+ struct page *page = pfn_to_page(fr_pfn);
+
+ memory_bm_clear_current(forbidden_pages_map);
+ memory_bm_clear_current(free_pages_map);
+ __free_page(page);
+ goto loop;
}
+
+out:
nr_copy_pages = 0;
nr_meta_pages = 0;
restore_pblist = NULL;
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 18c6219..4ca9a33 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -146,17 +146,29 @@
static int platform_suspend_prepare_late(suspend_state_t state)
{
+ return state == PM_SUSPEND_FREEZE && freeze_ops->prepare ?
+ freeze_ops->prepare() : 0;
+}
+
+static int platform_suspend_prepare_noirq(suspend_state_t state)
+{
return state != PM_SUSPEND_FREEZE && suspend_ops->prepare_late ?
suspend_ops->prepare_late() : 0;
}
-static void platform_suspend_wake(suspend_state_t state)
+static void platform_resume_noirq(suspend_state_t state)
{
if (state != PM_SUSPEND_FREEZE && suspend_ops->wake)
suspend_ops->wake();
}
-static void platform_suspend_finish(suspend_state_t state)
+static void platform_resume_early(suspend_state_t state)
+{
+ if (state == PM_SUSPEND_FREEZE && freeze_ops->restore)
+ freeze_ops->restore();
+}
+
+static void platform_resume_finish(suspend_state_t state)
{
if (state != PM_SUSPEND_FREEZE && suspend_ops->finish)
suspend_ops->finish();
@@ -172,7 +184,7 @@
return 0;
}
-static void platform_suspend_end(suspend_state_t state)
+static void platform_resume_end(suspend_state_t state)
{
if (state == PM_SUSPEND_FREEZE && freeze_ops && freeze_ops->end)
freeze_ops->end();
@@ -180,7 +192,7 @@
suspend_ops->end();
}
-static void platform_suspend_recover(suspend_state_t state)
+static void platform_recover(suspend_state_t state)
{
if (state != PM_SUSPEND_FREEZE && suspend_ops->recover)
suspend_ops->recover();
@@ -265,13 +277,22 @@
if (error)
goto Platform_finish;
- error = dpm_suspend_end(PMSG_SUSPEND);
+ error = dpm_suspend_late(PMSG_SUSPEND);
if (error) {
- printk(KERN_ERR "PM: Some devices failed to power down\n");
+ printk(KERN_ERR "PM: late suspend of devices failed\n");
goto Platform_finish;
}
error = platform_suspend_prepare_late(state);
if (error)
+ goto Devices_early_resume;
+
+ error = dpm_suspend_noirq(PMSG_SUSPEND);
+ if (error) {
+ printk(KERN_ERR "PM: noirq suspend of devices failed\n");
+ goto Platform_early_resume;
+ }
+ error = platform_suspend_prepare_noirq(state);
+ if (error)
goto Platform_wake;
if (suspend_test(TEST_PLATFORM))
@@ -318,11 +339,17 @@
enable_nonboot_cpus();
Platform_wake:
- platform_suspend_wake(state);
- dpm_resume_start(PMSG_RESUME);
+ platform_resume_noirq(state);
+ dpm_resume_noirq(PMSG_RESUME);
+
+ Platform_early_resume:
+ platform_resume_early(state);
+
+ Devices_early_resume:
+ dpm_resume_early(PMSG_RESUME);
Platform_finish:
- platform_suspend_finish(state);
+ platform_resume_finish(state);
return error;
}
@@ -361,14 +388,16 @@
suspend_test_start();
dpm_resume_end(PMSG_RESUME);
suspend_test_finish("resume devices");
+ trace_suspend_resume(TPS("resume_console"), state, true);
resume_console();
+ trace_suspend_resume(TPS("resume_console"), state, false);
Close:
- platform_suspend_end(state);
+ platform_resume_end(state);
return error;
Recover_platform:
- platform_suspend_recover(state);
+ platform_recover(state);
goto Resume_devices;
}
diff --git a/kernel/power/suspend_test.c b/kernel/power/suspend_test.c
index bd91bc1..084452e 100644
--- a/kernel/power/suspend_test.c
+++ b/kernel/power/suspend_test.c
@@ -22,6 +22,8 @@
#define TEST_SUSPEND_SECONDS 10
static unsigned long suspend_test_start_time;
+static u32 test_repeat_count_max = 1;
+static u32 test_repeat_count_current;
void suspend_test_start(void)
{
@@ -74,6 +76,7 @@
int status;
/* this may fail if the RTC hasn't been initialized */
+repeat:
status = rtc_read_time(rtc, &alm.time);
if (status < 0) {
printk(err_readtime, dev_name(&rtc->dev), status);
@@ -100,10 +103,21 @@
if (state == PM_SUSPEND_STANDBY) {
printk(info_test, pm_states[state]);
status = pm_suspend(state);
+ if (status < 0)
+ state = PM_SUSPEND_FREEZE;
}
+ if (state == PM_SUSPEND_FREEZE) {
+ printk(info_test, pm_states[state]);
+ status = pm_suspend(state);
+ }
+
if (status < 0)
printk(err_suspend, status);
+ test_repeat_count_current++;
+ if (test_repeat_count_current < test_repeat_count_max)
+ goto repeat;
+
/* Some platforms can't detect that the alarm triggered the
* wakeup, or (accordingly) disable it after it afterwards.
* It's supposed to give oneshot behavior; cope.
@@ -137,16 +151,28 @@
static int __init setup_test_suspend(char *value)
{
int i;
+ char *repeat;
+ char *suspend_type;
- /* "=mem" ==> "mem" */
+ /* example : "=mem[,N]" ==> "mem[,N]" */
value++;
+ suspend_type = strsep(&value, ",");
+ if (!suspend_type)
+ return 0;
+
+ repeat = strsep(&value, ",");
+ if (repeat) {
+ if (kstrtou32(repeat, 0, &test_repeat_count_max))
+ return 0;
+ }
+
for (i = 0; pm_labels[i]; i++)
- if (!strcmp(pm_labels[i], value)) {
+ if (!strcmp(pm_labels[i], suspend_type)) {
test_state_label = pm_labels[i];
return 0;
}
- printk(warn_bad_state, value);
+ printk(warn_bad_state, suspend_type);
return 0;
}
__setup("test_suspend", setup_test_suspend);
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index b38fb2b..2d75c94 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -3359,7 +3359,7 @@
iter->head = cpu_buffer->reader_page->read;
iter->cache_reader_page = iter->head_page;
- iter->cache_read = iter->head;
+ iter->cache_read = cpu_buffer->read;
if (iter->head)
iter->read_stamp = cpu_buffer->read_stamp;
diff --git a/lib/rhashtable.c b/lib/rhashtable.c
index 7b36e4d..16d0263 100644
--- a/lib/rhashtable.c
+++ b/lib/rhashtable.c
@@ -588,13 +588,13 @@
* rhashtable_destroy - destroy hash table
* @ht: the hash table to destroy
*
- * Frees the bucket array.
+ * Frees the bucket array. This function is not rcu safe, therefore the caller
+ * has to make sure that no resizing may happen by unpublishing the hashtable
+ * and waiting for the quiescent cycle before releasing the bucket array.
*/
void rhashtable_destroy(const struct rhashtable *ht)
{
- const struct bucket_table *tbl = rht_dereference(ht->tbl, ht);
-
- bucket_table_free(tbl);
+ bucket_table_free(ht->tbl);
}
EXPORT_SYMBOL_GPL(rhashtable_destroy);
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index d9a21d06..f8ffd94 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1795,14 +1795,17 @@
for (i = 0; i < HPAGE_PMD_NR; i++, haddr += PAGE_SIZE) {
pte_t *pte, entry;
BUG_ON(PageCompound(page+i));
+ /*
+ * Note that pmd_numa is not transferred deliberately
+ * to avoid any possibility that pte_numa leaks to
+ * a PROT_NONE VMA by accident.
+ */
entry = mk_pte(page + i, vma->vm_page_prot);
entry = maybe_mkwrite(pte_mkdirty(entry), vma);
if (!pmd_write(*pmd))
entry = pte_wrprotect(entry);
if (!pmd_young(*pmd))
entry = pte_mkold(entry);
- if (pmd_numa(*pmd))
- entry = pte_mknuma(entry);
pte = pte_offset_map(&_pmd, haddr);
BUG_ON(!pte_none(*pte));
set_pte_at(mm, haddr, pte, entry);
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 085dc6d..28928ce 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -292,6 +292,9 @@
/* vmpressure notifications */
struct vmpressure vmpressure;
+ /* css_online() has been completed */
+ int initialized;
+
/*
* the counter to account for mem+swap usage.
*/
@@ -1099,10 +1102,21 @@
* skipping css reference should be safe.
*/
if (next_css) {
- if ((next_css == &root->css) ||
- ((next_css->flags & CSS_ONLINE) &&
- css_tryget_online(next_css)))
- return mem_cgroup_from_css(next_css);
+ struct mem_cgroup *memcg = mem_cgroup_from_css(next_css);
+
+ if (next_css == &root->css)
+ return memcg;
+
+ if (css_tryget_online(next_css)) {
+ /*
+ * Make sure the memcg is initialized:
+ * mem_cgroup_css_online() orders the the
+ * initialization against setting the flag.
+ */
+ if (smp_load_acquire(&memcg->initialized))
+ return memcg;
+ css_put(next_css);
+ }
prev_css = next_css;
goto skip_node;
@@ -5549,6 +5563,7 @@
{
struct mem_cgroup *memcg = mem_cgroup_from_css(css);
struct mem_cgroup *parent = mem_cgroup_from_css(css->parent);
+ int ret;
if (css->id > MEM_CGROUP_ID_MAX)
return -ENOSPC;
@@ -5585,7 +5600,18 @@
}
mutex_unlock(&memcg_create_mutex);
- return memcg_init_kmem(memcg, &memory_cgrp_subsys);
+ ret = memcg_init_kmem(memcg, &memory_cgrp_subsys);
+ if (ret)
+ return ret;
+
+ /*
+ * Make sure the memcg is initialized: mem_cgroup_iter()
+ * orders reading memcg->initialized against its callers
+ * reading the memcg members.
+ */
+ smp_store_release(&memcg->initialized, 1);
+
+ return 0;
}
/*
diff --git a/mm/migrate.c b/mm/migrate.c
index f78ec9b..2740360 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -146,8 +146,11 @@
pte = pte_mkold(mk_pte(new, vma->vm_page_prot));
if (pte_swp_soft_dirty(*ptep))
pte = pte_mksoft_dirty(pte);
+
+ /* Recheck VMA as permissions can change since migration started */
if (is_write_migration_entry(entry))
- pte = pte_mkwrite(pte);
+ pte = maybe_mkwrite(pte, vma);
+
#ifdef CONFIG_HUGETLB_PAGE
if (PageHuge(new)) {
pte = pte_mkhuge(pte);
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 18cee0d..eee9619 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1612,7 +1612,7 @@
}
__mod_zone_page_state(zone, NR_ALLOC_BATCH, -(1 << order));
- if (zone_page_state(zone, NR_ALLOC_BATCH) == 0 &&
+ if (atomic_long_read(&zone->vm_stat[NR_ALLOC_BATCH]) <= 0 &&
!zone_is_fair_depleted(zone))
zone_set_flag(zone, ZONE_FAIR_DEPLETED);
@@ -5701,9 +5701,8 @@
zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + (tmp >> 1);
__mod_zone_page_state(zone, NR_ALLOC_BATCH,
- high_wmark_pages(zone) -
- low_wmark_pages(zone) -
- zone_page_state(zone, NR_ALLOC_BATCH));
+ high_wmark_pages(zone) - low_wmark_pages(zone) -
+ atomic_long_read(&zone->vm_stat[NR_ALLOC_BATCH]));
setup_zone_migrate_reserve(zone);
spin_unlock_irqrestore(&zone->lock, flags);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index da1378a..8d28969 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3152,6 +3152,9 @@
NAPI_GRO_CB(skb)->free = NAPI_GRO_FREE_STOLEN_HEAD;
goto done;
}
+ /* switch back to head shinfo */
+ pinfo = skb_shinfo(p);
+
if (pinfo->frag_list)
goto merge;
if (skb_gro_len(p) != pinfo->gso_size)
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index bd41dd1..bda4bb8 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -764,9 +764,14 @@
t = ip_tunnel_find(itn, p, itn->fb_tunnel_dev->type);
- if (!t && (cmd == SIOCADDTUNNEL)) {
- t = ip_tunnel_create(net, itn, p);
- err = PTR_ERR_OR_ZERO(t);
+ if (cmd == SIOCADDTUNNEL) {
+ if (!t) {
+ t = ip_tunnel_create(net, itn, p);
+ err = PTR_ERR_OR_ZERO(t);
+ break;
+ }
+
+ err = -EEXIST;
break;
}
if (dev != itn->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 173e7ea..cbadb94 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -746,7 +746,7 @@
}
n = ipv4_neigh_lookup(&rt->dst, NULL, &new_gw);
- if (n) {
+ if (!IS_ERR(n)) {
if (!(n->nud_state & NUD_VALID)) {
neigh_event_send(n, NULL);
} else {
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 3342ee6..3e118df 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -4780,10 +4780,11 @@
if (ip6_del_rt(ifp->rt))
dst_free(&ifp->rt->dst);
+
+ rt_genid_bump_ipv6(net);
break;
}
atomic_inc(&net->ipv6.dev_addr_genid);
- rt_genid_bump_ipv6(net);
}
static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
diff --git a/net/ipv6/addrconf_core.c b/net/ipv6/addrconf_core.c
index e696045..98cc4cd 100644
--- a/net/ipv6/addrconf_core.c
+++ b/net/ipv6/addrconf_core.c
@@ -8,6 +8,13 @@
#include <net/addrconf.h>
#include <net/ip.h>
+/* if ipv6 module registers this function is used by xfrm to force all
+ * sockets to relookup their nodes - this is fairly expensive, be
+ * careful
+ */
+void (*__fib6_flush_trees)(struct net *);
+EXPORT_SYMBOL(__fib6_flush_trees);
+
#define IPV6_ADDR_SCOPE_TYPE(scope) ((scope) << 16)
static inline unsigned int ipv6_addr_scope2type(unsigned int scope)
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 76b7f5e..97b9fa8 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -1605,6 +1605,24 @@
fib6_clean_tree(net, fn, fib6_prune_clone, 1, NULL);
}
+static int fib6_update_sernum(struct rt6_info *rt, void *arg)
+{
+ __u32 sernum = *(__u32 *)arg;
+
+ if (rt->rt6i_node &&
+ rt->rt6i_node->fn_sernum != sernum)
+ rt->rt6i_node->fn_sernum = sernum;
+
+ return 0;
+}
+
+static void fib6_flush_trees(struct net *net)
+{
+ __u32 new_sernum = fib6_new_sernum();
+
+ fib6_clean_all(net, fib6_update_sernum, &new_sernum);
+}
+
/*
* Garbage collection
*/
@@ -1788,6 +1806,8 @@
NULL);
if (ret)
goto out_unregister_subsys;
+
+ __fib6_flush_trees = fib6_flush_trees;
out:
return ret;
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 5f19dfb..f304471 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -314,6 +314,8 @@
struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
t = ip6gre_tunnel_find(net, parms, ARPHRD_IP6GRE);
+ if (t && create)
+ return NULL;
if (t || !create)
return t;
@@ -1724,4 +1726,5 @@
MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
MODULE_DESCRIPTION("GRE over IPv6 tunneling device");
MODULE_ALIAS_RTNL_LINK("ip6gre");
+MODULE_ALIAS_RTNL_LINK("ip6gretap");
MODULE_ALIAS_NETDEV("ip6gre0");
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index f9de5a6..69a84b4 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -364,8 +364,12 @@
(t = rtnl_dereference(*tp)) != NULL;
tp = &t->next) {
if (ipv6_addr_equal(local, &t->parms.laddr) &&
- ipv6_addr_equal(remote, &t->parms.raddr))
+ ipv6_addr_equal(remote, &t->parms.raddr)) {
+ if (create)
+ return NULL;
+
return t;
+ }
}
if (!create)
return NULL;
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index 7f52fd9..5833a22 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -253,8 +253,12 @@
(t = rtnl_dereference(*tp)) != NULL;
tp = &t->next) {
if (ipv6_addr_equal(local, &t->parms.laddr) &&
- ipv6_addr_equal(remote, &t->parms.raddr))
+ ipv6_addr_equal(remote, &t->parms.raddr)) {
+ if (create)
+ return NULL;
+
return t;
+ }
}
if (!create)
return NULL;
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index f23fbd2..bafde82 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -314,7 +314,6 @@
memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers);
- rt->rt6i_genid = rt_genid_ipv6(net);
INIT_LIST_HEAD(&rt->rt6i_siblings);
}
return rt;
@@ -1098,9 +1097,6 @@
* DST_OBSOLETE_FORCE_CHK which forces validation calls down
* into this function always.
*/
- if (rt->rt6i_genid != rt_genid_ipv6(dev_net(rt->dst.dev)))
- return NULL;
-
if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie))
return NULL;
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index b5c1d3a..6d77cce 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -847,6 +847,7 @@
tristate '"TPROXY" target transparent proxying support'
depends on NETFILTER_XTABLES
depends on NETFILTER_ADVANCED
+ depends on (IPV6 || IPV6=n)
depends on IP_NF_MANGLE
select NF_DEFRAG_IPV4
select NF_DEFRAG_IPV6 if IP6_NF_IPTABLES
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
index c138b8f..f37f071 100644
--- a/net/netfilter/nfnetlink.c
+++ b/net/netfilter/nfnetlink.c
@@ -222,6 +222,51 @@
}
}
+struct nfnl_err {
+ struct list_head head;
+ struct nlmsghdr *nlh;
+ int err;
+};
+
+static int nfnl_err_add(struct list_head *list, struct nlmsghdr *nlh, int err)
+{
+ struct nfnl_err *nfnl_err;
+
+ nfnl_err = kmalloc(sizeof(struct nfnl_err), GFP_KERNEL);
+ if (nfnl_err == NULL)
+ return -ENOMEM;
+
+ nfnl_err->nlh = nlh;
+ nfnl_err->err = err;
+ list_add_tail(&nfnl_err->head, list);
+
+ return 0;
+}
+
+static void nfnl_err_del(struct nfnl_err *nfnl_err)
+{
+ list_del(&nfnl_err->head);
+ kfree(nfnl_err);
+}
+
+static void nfnl_err_reset(struct list_head *err_list)
+{
+ struct nfnl_err *nfnl_err, *next;
+
+ list_for_each_entry_safe(nfnl_err, next, err_list, head)
+ nfnl_err_del(nfnl_err);
+}
+
+static void nfnl_err_deliver(struct list_head *err_list, struct sk_buff *skb)
+{
+ struct nfnl_err *nfnl_err, *next;
+
+ list_for_each_entry_safe(nfnl_err, next, err_list, head) {
+ netlink_ack(skb, nfnl_err->nlh, nfnl_err->err);
+ nfnl_err_del(nfnl_err);
+ }
+}
+
static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
u_int16_t subsys_id)
{
@@ -230,6 +275,7 @@
const struct nfnetlink_subsystem *ss;
const struct nfnl_callback *nc;
bool success = true, done = false;
+ static LIST_HEAD(err_list);
int err;
if (subsys_id >= NFNL_SUBSYS_COUNT)
@@ -287,6 +333,7 @@
type = nlh->nlmsg_type;
if (type == NFNL_MSG_BATCH_BEGIN) {
/* Malformed: Batch begin twice */
+ nfnl_err_reset(&err_list);
success = false;
goto done;
} else if (type == NFNL_MSG_BATCH_END) {
@@ -333,6 +380,7 @@
* original skb.
*/
if (err == -EAGAIN) {
+ nfnl_err_reset(&err_list);
ss->abort(skb);
nfnl_unlock(subsys_id);
kfree_skb(nskb);
@@ -341,11 +389,24 @@
}
ack:
if (nlh->nlmsg_flags & NLM_F_ACK || err) {
+ /* Errors are delivered once the full batch has been
+ * processed, this avoids that the same error is
+ * reported several times when replaying the batch.
+ */
+ if (nfnl_err_add(&err_list, nlh, err) < 0) {
+ /* We failed to enqueue an error, reset the
+ * list of errors and send OOM to userspace
+ * pointing to the batch header.
+ */
+ nfnl_err_reset(&err_list);
+ netlink_ack(skb, nlmsg_hdr(oskb), -ENOMEM);
+ success = false;
+ goto done;
+ }
/* We don't stop processing the batch on errors, thus,
* userspace gets all the errors that the batch
* triggers.
*/
- netlink_ack(skb, nlh, err);
if (err)
success = false;
}
@@ -361,6 +422,7 @@
else
ss->abort(skb);
+ nfnl_err_deliver(&err_list, oskb);
nfnl_unlock(subsys_id);
kfree_skb(nskb);
}
diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c
index 28fb8f3..8892b7b 100644
--- a/net/netfilter/nft_hash.c
+++ b/net/netfilter/nft_hash.c
@@ -180,15 +180,17 @@
static void nft_hash_destroy(const struct nft_set *set)
{
const struct rhashtable *priv = nft_set_priv(set);
- const struct bucket_table *tbl;
+ const struct bucket_table *tbl = priv->tbl;
struct nft_hash_elem *he, *next;
unsigned int i;
- tbl = rht_dereference(priv->tbl, priv);
- for (i = 0; i < tbl->size; i++)
- rht_for_each_entry_safe(he, next, tbl->buckets[i], priv, node)
+ for (i = 0; i < tbl->size; i++) {
+ for (he = rht_entry(tbl->buckets[i], struct nft_hash_elem, node);
+ he != NULL; he = next) {
+ next = rht_entry(he->node.next, struct nft_hash_elem, node);
nft_hash_elem_destroy(set, he);
-
+ }
+ }
rhashtable_destroy(priv);
}
diff --git a/net/netfilter/nft_rbtree.c b/net/netfilter/nft_rbtree.c
index e1836ff..46214f2 100644
--- a/net/netfilter/nft_rbtree.c
+++ b/net/netfilter/nft_rbtree.c
@@ -234,13 +234,11 @@
struct nft_rbtree_elem *rbe;
struct rb_node *node;
- spin_lock_bh(&nft_rbtree_lock);
while ((node = priv->root.rb_node) != NULL) {
rb_erase(node, &priv->root);
rbe = rb_entry(node, struct nft_rbtree_elem, node);
nft_rbtree_elem_destroy(set, rbe);
}
- spin_unlock_bh(&nft_rbtree_lock);
}
static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features,
diff --git a/net/sched/ematch.c b/net/sched/ematch.c
index 3a633de..ad57f44 100644
--- a/net/sched/ematch.c
+++ b/net/sched/ematch.c
@@ -526,9 +526,11 @@
match_idx = stack[--stackp];
cur_match = tcf_em_get_match(tree, match_idx);
- if (tcf_em_early_end(cur_match, res))
+ if (tcf_em_early_end(cur_match, res)) {
+ if (tcf_em_is_inverted(cur_match))
+ res = !res;
goto pop_stack;
- else {
+ } else {
match_idx++;
goto proceed;
}
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index e4f6102..b86b426 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -51,7 +51,7 @@
{ 0x04, 0xaf01 },
{ 0x08, 0x000d },
{ 0x09, 0xd810 },
- { 0x0a, 0x0060 },
+ { 0x0a, 0x0120 },
{ 0x0b, 0x0000 },
{ 0x0d, 0x2800 },
{ 0x0f, 0x0000 },
@@ -60,7 +60,7 @@
{ 0x33, 0x0208 },
{ 0x49, 0x0004 },
{ 0x4f, 0x50e9 },
- { 0x50, 0x2c00 },
+ { 0x50, 0x2000 },
{ 0x63, 0x2902 },
{ 0x67, 0x1111 },
{ 0x68, 0x1016 },
@@ -104,7 +104,6 @@
{ 0x02170700, 0x00000000 },
{ 0x02270100, 0x00000000 },
{ 0x02370100, 0x00000000 },
- { 0x02040000, 0x00004002 },
{ 0x01870700, 0x00000020 },
{ 0x00830000, 0x000000c3 },
{ 0x00930000, 0x000000c3 },
@@ -192,7 +191,6 @@
/*handle index registers*/
if (reg <= 0xff) {
rt286_hw_write(client, RT286_COEF_INDEX, reg);
- reg = RT286_PROC_COEF;
for (i = 0; i < INDEX_CACHE_SIZE; i++) {
if (reg == rt286->index_cache[i].reg) {
rt286->index_cache[i].def = value;
@@ -200,6 +198,7 @@
}
}
+ reg = RT286_PROC_COEF;
}
data[0] = (reg >> 24) & 0xff;
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index 484b3bb..4021cd4 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -647,7 +647,7 @@
return -ENOMEM;
dev_set_drvdata(dev, ssm2602);
- ssm2602->type = SSM2602;
+ ssm2602->type = type;
ssm2602->regmap = regmap;
return snd_soc_register_codec(dev, &soc_codec_dev_ssm2602,
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 87eb577..de6ab06 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -748,8 +748,9 @@
return 0;
}
-static int _fsl_ssi_set_dai_fmt(struct fsl_ssi_private *ssi_private,
- unsigned int fmt)
+static int _fsl_ssi_set_dai_fmt(struct device *dev,
+ struct fsl_ssi_private *ssi_private,
+ unsigned int fmt)
{
struct regmap *regs = ssi_private->regs;
u32 strcr = 0, stcr, srcr, scr, mask;
@@ -758,7 +759,7 @@
ssi_private->dai_fmt = fmt;
if (fsl_ssi_is_i2s_master(ssi_private) && IS_ERR(ssi_private->baudclk)) {
- dev_err(&ssi_private->pdev->dev, "baudclk is missing which is necessary for master mode\n");
+ dev_err(dev, "baudclk is missing which is necessary for master mode\n");
return -EINVAL;
}
@@ -913,7 +914,7 @@
{
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
- return _fsl_ssi_set_dai_fmt(ssi_private, fmt);
+ return _fsl_ssi_set_dai_fmt(cpu_dai->dev, ssi_private, fmt);
}
/**
@@ -1387,7 +1388,8 @@
done:
if (ssi_private->dai_fmt)
- _fsl_ssi_set_dai_fmt(ssi_private, ssi_private->dai_fmt);
+ _fsl_ssi_set_dai_fmt(&pdev->dev, ssi_private,
+ ssi_private->dai_fmt);
return 0;
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index 3092b58f..cecfab3c 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -102,13 +102,11 @@
fe->dpcm[stream].runtime = fe_substream->runtime;
ret = dpcm_path_get(fe, stream, &list);
- if (ret < 0) {
- mutex_unlock(&fe->card->mutex);
+ if (ret < 0)
goto fe_err;
- } else if (ret == 0) {
+ else if (ret == 0)
dev_dbg(fe->dev, "ASoC: %s no valid %s route\n",
fe->dai_link->name, stream ? "capture" : "playback");
- }
/* calculate valid and active FE <-> BE dpcms */
dpcm_process_paths(fe, stream, &list, 1);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 889f4e3..d074aa9 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -3203,7 +3203,7 @@
unsigned int val, mask;
void *data;
- if (!component->regmap)
+ if (!component->regmap || !params->num_regs)
return -EINVAL;
len = params->num_regs * component->val_bytes;