Merge "iommu/arm-smmu: Wire up generic configuration support" into msm-4.8
diff --git a/Documentation/android.txt b/Documentation/android.txt
index d53aba4..0f40a78 100644
--- a/Documentation/android.txt
+++ b/Documentation/android.txt
@@ -67,6 +67,8 @@
RAMFS
RTC_CLASS
RTC_LIB
+SWITCH
+SWITCH_GPIO
TMPFS
UID_STAT
UID16
diff --git a/Documentation/conf.py b/Documentation/conf.py
index bf6f310..d769cd8 100644
--- a/Documentation/conf.py
+++ b/Documentation/conf.py
@@ -37,7 +37,7 @@
extensions = ['kernel-doc', 'rstFlatTable', 'kernel_include', 'cdomain']
# The name of the math extension changed on Sphinx 1.4
-if minor > 3:
+if major == 1 and minor > 3:
extensions.append("sphinx.ext.imgmath")
else:
extensions.append("sphinx.ext.pngmath")
@@ -332,6 +332,10 @@
'''
}
+# Fix reference escape troubles with Sphinx 1.4.x
+if major == 1 and minor > 3:
+ latex_elements['preamble'] += '\\renewcommand*{\\DUrole}[2]{ #2 }\n'
+
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt
index ac8a37e..0cf9a6b 100644
--- a/Documentation/cpu-freq/governors.txt
+++ b/Documentation/cpu-freq/governors.txt
@@ -227,7 +227,49 @@
usage, similar to "ondemand" and "conservative" governors, but with a
different set of configurable behaviors.
-The tuneable values for this governor are:
+The tunable values for this governor are:
+
+above_hispeed_delay: When speed is at or above hispeed_freq, wait for
+this long before raising speed in response to continued high load.
+The format is a single delay value, optionally followed by pairs of
+CPU speeds and the delay to use at or above those speeds. Colons can
+be used between the speeds and associated delays for readability. For
+example:
+
+ 80000 1300000:200000 1500000:40000
+
+uses delay 80000 uS until CPU speed 1.3 GHz, at which speed delay
+200000 uS is used until speed 1.5 GHz, at which speed (and above)
+delay 40000 uS is used. If speeds are specified these must appear in
+ascending order. Default is 20000 uS.
+
+boost: If non-zero, immediately boost speed of all CPUs to at least
+hispeed_freq until zero is written to this attribute. If zero, allow
+CPU speeds to drop below hispeed_freq according to load as usual.
+Default is zero.
+
+boostpulse: On each write, immediately boost speed of all CPUs to
+hispeed_freq for at least the period of time specified by
+boostpulse_duration, after which speeds are allowed to drop below
+hispeed_freq according to load as usual. Its a write-only file.
+
+boostpulse_duration: Length of time to hold CPU speed at hispeed_freq
+on a write to boostpulse, before allowing speed to drop according to
+load as usual. Default is 80000 uS.
+
+go_hispeed_load: The CPU load at which to ramp to hispeed_freq.
+Default is 99%.
+
+hispeed_freq: An intermediate "high speed" at which to initially ramp
+when CPU load hits the value specified in go_hispeed_load. If load
+stays high for the amount of time specified in above_hispeed_delay,
+then speed may be bumped higher. Default is the maximum speed allowed
+by the policy at governor initialization time.
+
+io_is_busy: If set, the governor accounts IO time as CPU busy time.
+
+min_sample_time: The minimum amount of time to spend at the current
+frequency before ramping down. Default is 80000 uS.
target_loads: CPU load values used to adjust speed to influence the
current CPU load toward that value. In general, the lower the target
@@ -246,32 +288,6 @@
values also usually appear in an ascending order. The default is
target load 90% for all speeds.
-min_sample_time: The minimum amount of time to spend at the current
-frequency before ramping down. Default is 80000 uS.
-
-hispeed_freq: An intermediate "hi speed" at which to initially ramp
-when CPU load hits the value specified in go_hispeed_load. If load
-stays high for the amount of time specified in above_hispeed_delay,
-then speed may be bumped higher. Default is the maximum speed
-allowed by the policy at governor initialization time.
-
-go_hispeed_load: The CPU load at which to ramp to hispeed_freq.
-Default is 99%.
-
-above_hispeed_delay: When speed is at or above hispeed_freq, wait for
-this long before raising speed in response to continued high load.
-The format is a single delay value, optionally followed by pairs of
-CPU speeds and the delay to use at or above those speeds. Colons can
-be used between the speeds and associated delays for readability. For
-example:
-
- 80000 1300000:200000 1500000:40000
-
-uses delay 80000 uS until CPU speed 1.3 GHz, at which speed delay
-200000 uS is used until speed 1.5 GHz, at which speed (and above)
-delay 40000 uS is used. If speeds are specified these must appear in
-ascending order. Default is 20000 uS.
-
timer_rate: Sample rate for reevaluating CPU load when the CPU is not
idle. A deferrable timer is used, such that the CPU will not be woken
from idle to service this timer until something else needs to run.
@@ -288,21 +304,6 @@
when not at lowest speed. A value of -1 means defer timers
indefinitely at all speeds. Default is 80000 uS.
-boost: If non-zero, immediately boost speed of all CPUs to at least
-hispeed_freq until zero is written to this attribute. If zero, allow
-CPU speeds to drop below hispeed_freq according to load as usual.
-Default is zero.
-
-boostpulse: On each write, immediately boost speed of all CPUs to
-hispeed_freq for at least the period of time specified by
-boostpulse_duration, after which speeds are allowed to drop below
-hispeed_freq according to load as usual.
-
-boostpulse_duration: Length of time to hold CPU speed at hispeed_freq
-on a write to boostpulse, before allowing speed to drop according to
-load as usual. Default is 80000 uS.
-
-
3. The Governor Interface in the CPUfreq Core
=============================================
diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
new file mode 100644
index 0000000..ae476d0
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
@@ -0,0 +1,324 @@
+* Low Power Management Levels
+
+The application processor in MSM can do a variety of C-States for low power
+management. The LPM module performs the System low power modes based on
+the latency/residency information of the individual CPUs and clusters.
+
+LPM-levels defines a hierarchy of low power modes that a cluster and
+clusters/cpus within that cluster can enter. The bottom hierarchy level
+represents the low power modes that a CPU can enter. The CPU low power nodes
+are associated with a cluster that defines the low power modes that a cluster
+can enter. For system involving a hierarchy of clusters, the cluster low power
+modes can be contained within another cluster.
+
+[Top Level Node]
+Required properties:
+
+- compatible: "qcom,lpm-levels"
+
+[Node bindings for qcom,pm-cluster]
+ Required properties:
+ - reg - The numeric cluster id
+ - label: Identifies the cluster name. The name will be
+ used when reporting the stats for each low power mode.
+ - qcom,spm-device-names: List of SPM device names which control the
+ low power modes for this driver. The lpm driver uses the device name
+ to obtain a handle to the SPM driver that controls the cluster's low
+ power mode. This is only required if "qcom,use-psci" is not defined.
+ - qcom,default-level: The default low power level that a cluster is
+ programmed. The SPM of the corresponding device is configured at this
+ low power mode by default.
+ - qcom,cpu: List of CPU phandles to identify the CPUs associated with
+ this cluster. This property is required if and only if the cluster
+ node contains a qcom,pm-cpu node.
+
+ qcom,pm-cluster contains qcom,pm-cluster-level nodes which identify
+ the various low power modes that the cluster can enter. The
+ qcom,pm-cluster node should also include another cluster node or a cpu
+ node that defines their respective low power modes.
+
+[Node bindings for qcom,pm-cluster-level]
+ Required properties:
+ - reg: The numeric cluster level id
+ - label: Name to identify the low power mode in stats
+ module.
+ - qcom,spm-<device-name>-mode: For each SPM device defined in
+ qcom,spm-devices-names, a corresponding entry identifying the low
+ power mode is expected. For example, the qcom,pm-cluster node contains
+ a SPM device by name "l2" then the cluster level should contain a
+ qcom,spm-l2-mode. When a cluster level is chosen ,the SPM device is
+ programmed with its
+ corresponding low power mode. The accepted values for this property
+ are:
+ - "active"
+ - "wfi"
+ - "retention"
+ - "gdhs"
+ - "pc"
+ - "fpc"
+ - qcom,min-child-idx: The minimum level that a child CPU should be in
+ before this level can be chosen. This property is required for all
+ non-default level.
+ - qcom,latency-us: The latency in handling the interrupt if this level
+ was chosen, in uSec
+ - qcom,ss-power: The steady state power expelled when the processor is
+ in this level in mWatts
+ - qcom,energy-overhead: The energy used up in entering and exiting
+ this level in mWatts.uSec
+ - qcom,time-overhead: The time spent in entering and exiting this
+ level in uS
+ Optional properties:
+ - qcom,notify-rpm: When set, the driver flushes the RPM sleep set and
+ configures the virtual MPM driver in prepration for a RPM assisted
+ sleep.
+ - qcom,last-level - When set, the cluster level is applied only when
+ there is 1 online core.
+ - qcom,disable-dynamic-int-routing: When set disables the dynamic
+ routing of rpm-smd and mpm interrupts to next wake up core.
+ - qcom,use-psci: This boolean property allows the LPM modules to
+ terminate in PSCI to configure SPM for low power modes.
+ - qcom,psci-mode-shift: The property is used to determine with bit
+ location of the cluster mode in the composite state ID used to define
+ cluster low power modes in PSCI v1.0. Required only if qcom,use-psci
+ is defined at the lpm-levels root node.
+ - qcom,psci-mode-mask: The property is used to determine with bit
+ mask of the cluster mode in the composite state ID used to define
+ cluster low power modes in PSCI v1.0. Required only if qcom,use-psci
+ is defined at the lpm-levels root node.
+ - qcom,psci-mode: ID to be passed into the PSCI firmware. Required
+ only if qcom,use-psci is defined at the lpm-levels root node.
+ - qcom,is-reset: This boolean property will tell whether
+ cluster level need power management notifications to be sent out
+ or not for the drivers to prepare for cluster collapse.
+ - qcom,hyp-psci: This property is used to determine if the cpu
+ enters the low power mode within hypervisor.
+ - qcom,reset-level: This property is used to determine in this
+ low power mode only control logic power collapse happens or memory
+ logic power collapse aswell happens or retention state.
+ The accepted values for this property are:
+ "LPM_RESET_LVL_NONE" - No power collapse
+ "LPM_RESET_LVL_RET" - Retention state
+ "LPM_RESET_LVL_GDHS" - Only control logic power collapse (GDHS)
+ "LPM_RESET_LVL_PC" - Control logic and memory logic
+ power collapse (PC)
+
+[Node bindings for qcom,pm-cpu]
+qcom,pm-cpu contains the low power modes that a cpu could enter. Currently it
+doesn't have any required properties and is a container for
+qcom,pm-cpu-levels.
+
+[Node bindings for qcom,pm-cpu-levels]
+ Required properties:
+ - reg: The numeric cpu level id
+ - qcom,spm-cpu-mode: The sleep mode of the processor, values for the
+ property are:
+ "wfi" - Wait for Interrupt
+ "retention" - Retention
+ "standalone_pc" - Standalone power collapse
+ "pc" - Power Collapse
+ - qcom,latency-us: The latency in handling the interrupt if this level
+ was chosen, in uSec
+ - qcom,ss-power: The steady state power expelled when the processor is
+ in this level in mWatts
+ - qcom,energy-overhead: The energy used up in entering and exiting
+ this level in mWatts.uSec
+ - qcom,time-overhead: The time spent in entering and exiting this
+ level in uS
+ - qcom,use-broadcast-timer: Indicates that the timer gets reset during
+ power collapse and the cpu relies on Broadcast timer for scheduled
+ wakeups. Required only for states where the CPUs internal timer state
+ is lost.
+
+ Optional properties:
+ - qcom,psci-mode-shift: Same as cluster level fields.
+ - qcom,psci-mode-mask: Same as cluster level fields.
+ - qcom,psci-cpu-mode: ID to be passed into PSCI firmware.
+ - qcom,jtag-save-restore: A boolean specifying jtag registers save and restore
+ required are not.
+ - qcom,is-reset: This boolean property maps to "power state" bit in PSCI
+ state_id configuration. This property will tell whether CPU get reset for
+ a particular LPM or not. This property will also be used to notify the
+ drivers in case of cpu reset.
+
+[Example dts]
+
+qcom,lpm-levels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "qcom,lpm-levels";
+
+ qcom,pm-cluster@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ label = "system";
+ qcom,spm-device-names = "cci";
+ qcom,default-level = <0>;
+
+ qcom,pm-cluster-level@0{
+ reg = <0>;
+ label = "system-cci-retention";
+ qcom,spm-cci-mode = "retention";
+ qcom,latency-us = <100>;
+ qcom,ss-power = <1000>;
+ qcom,energy-overhead = <300000>;
+ qcom,time-overhead = <100>;
+ };
+
+ qcom,pm-cluster-level@2{
+ reg = <1>;
+ label = "system-cci-pc";
+ qcom,spm-cci-mode = "pc";
+ qcom,latency-us = <30000>;
+ qcom,ss-power = <83>;
+ qcom,energy-overhead = <2274420>;
+ qcom,time-overhead = <6605>;
+ qcom,min-child-idx = <1>;
+ qcom,notify-rpm;
+ };
+
+ qcom,pm-cluster@0{
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ label = "a53";
+ qcom,spm-device-names = "l2";
+ qcom,default-level=<0>;
+ qcom,cpu = <&CPU0 &CPU1 &CPU2 &CPU3>;
+
+ qcom,pm-cluster-level@0{
+ reg = <0>;
+ label = "a53-l2-retention";
+ qcom,spm-l2-mode = "retention";
+ qcom,latency-us = <100>;
+ qcom,ss-power = <1000>;
+ qcom,energy-overhead = <300000>;
+ qcom,time-overhead = <100>;
+ };
+
+ qcom,pm-cluster-level@1{
+ reg = <1>;
+ label = "a53-l2-pc";
+ qcom,spm-l2-mode = "pc";
+ qcom,latency-us = <30000>;
+ qcom,ss-power = <83>;
+ qcom,energy-overhead = <2274420>;
+ qcom,time-overhead = <6605>;
+ qcom,min-child-idx = <3>;
+ };
+
+ qcom,pm-cpu {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ qcom,pm-cpu-level@0 {
+ reg = <0>;
+ qcom,spm-cpu-mode = "wfi";
+ qcom,latency-us = <1>;
+ qcom,ss-power = <715>;
+ qcom,energy-overhead = <17700>;
+ qcom,time-overhead = <2>;
+ };
+
+ qcom,pm-cpu-level@1 {
+ reg = <1>;
+ qcom,spm-cpu-mode = "retention";
+ qcom,latency-us = <35>;
+ qcom,ss-power = <542>;
+ qcom,energy-overhead = <34920>;
+ qcom,time-overhead = <40>;
+ };
+
+ qcom,pm-cpu-level@2 {
+ reg = <2>;
+ qcom,spm-cpu-mode = "standalone_pc";
+ qcom,latency-us = <300>;
+ qcom,ss-power = <476>;
+ qcom,energy-overhead = <225300>;
+ qcom,time-overhead = <350>;
+ };
+
+ qcom,pm-cpu-level@3 {
+ reg = <3>;
+ qcom,spm-cpu-mode = "pc";
+ qcom,latency-us = <500>;
+ qcom,ss-power = <163>;
+ qcom,energy-overhead = <577736>;
+ qcom,time-overhead = <1000>;
+ };
+ };
+ };
+
+ qcom,pm-cluster@1{
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ label = "a57";
+ qcom,spm-device-names = "l2";
+ qcom,default-level=<0>;
+ qcom,cpu = <&CPU4 &CPU5 &CPU6 &CPU7>;
+
+ qcom,pm-cluster-level@0{
+ reg = <0>;
+ label = "a57-l2-retention";
+ qcom,spm-l2-mode = "retention";
+ qcom,latency-us = <100>;
+ qcom,ss-power = <1000>;
+ qcom,energy-overhead = <300000>;
+ qcom,time-overhead = <100>;
+ };
+
+ qcom,pm-cluster-level@2{
+ reg = <1>;
+ label = "a57-l2-pc";
+ qcom,spm-l2-mode = "pc";
+ qcom,latency-us = <30000>;
+ qcom,ss-power = <83>;
+ qcom,energy-overhead = <2274420>;
+ qcom,time-overhead = <6605>;
+ qcom,min-child-idx = <3>;
+ };
+
+ qcom,pm-cpu {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ qcom,pm-cpu-level@0 {
+ reg = <0>;
+ qcom,spm-cpu-mode = "wfi";
+ qcom,latency-us = <1>;
+ qcom,ss-power = <715>;
+ qcom,energy-overhead = <17700>;
+ qcom,time-overhead = <2>;
+ };
+
+ qcom,pm-cpu-level@1 {
+ reg = <1>;
+ qcom,spm-cpu-mode = "retention";
+ qcom,latency-us = <35>;
+ qcom,ss-power = <542>;
+ qcom,energy-overhead = <34920>;
+ qcom,time-overhead = <40>;
+ };
+
+ qcom,pm-cpu-level@2 {
+ reg = <2>;
+ qcom,spm-cpu-mode = "standalone_pc";
+ qcom,latency-us = <300>;
+ qcom,ss-power = <476>;
+ qcom,energy-overhead = <225300>;
+ qcom,time-overhead = <350>;
+ };
+
+ qcom,pm-cpu-level@3 {
+ reg = <3>;
+ qcom,spm-cpu-mode = "pc";
+ qcom,latency-us = <500>;
+ qcom,ss-power = <163>;
+ qcom,energy-overhead = <577736>;
+ qcom,time-overhead = <1000>;
+ };
+ };
+ };
+ };
+
+
+};
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_core.txt b/Documentation/devicetree/bindings/arm/msm/msm_core.txt
new file mode 100644
index 0000000..f385915
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/msm_core.txt
@@ -0,0 +1,71 @@
+MSM Core Energy Aware driver
+
+The Energy Aware driver provides per core power and temperature
+information to the scheduler for it to make more power efficient
+scheduling decision.
+
+The required properties for the Energy-aware driver are:
+
+- compatible: "qcom,apss-core-ea"
+- reg: Physical address mapped to this device
+
+Required nodes:
+- ea@X: Parent node that has the sensor mapping for each cpu.
+ This node's phandle is provided within cpu node
+ to invoke/probe energy-aware only for available cpus.
+ There should be one such node present for each cpu.
+
+Optional properties:
+- qcom,low-hyst-temp: Degrees C below which the power numbers
+ need to be recomputed for the cores and reset
+ the threshold. If this is not present, the default
+ value is 10C.
+- qcom,high-hyst-temp: Degrees C above which the power numbers
+ need to be recomputed for the cores and reset
+ the threshold. If this property is not present,
+ the default value is 5C.
+- qcom,polling-interval: Interval for which the power numbers
+ need to be recomputed for the cores if there
+ is no change in threshold. If this property is not
+ present, the power is recalculated only on
+ temperature threshold notifications.
+-qcom,throttling-temp: Temperature threshold for cpu frequency mitigation.
+ The value should be set same as the threshold temperature
+ in thermal module - 5 C, such that there is a bandwidth to
+ control the cores before frequency mitigation happens.
+
+[Second level nodes]
+Require properties to define per core characteristics:
+- sensor: Sensor phandle to map a particular sensor to the core.
+ If this property is not present, then the core is assumed
+ to be at 40C for all the power estimations. No sensor
+ threshold is set. This phandle's compatible property is
+ "qcom,sensor-information". This driver relies on the
+ sensor-type and scaling-factor information provided in this
+ phandle.
+
+Example
+
+qcom,msm-core@0xfc4b0000 {
+ compatible = "qcom,apss-core-ea";
+ reg = <0xfc4b0000 0x1000>;
+ qcom,low-hyst-temp = <10>;
+ qcom,high-hyst-temp = <5>;
+ qcom,polling-interval = <50>;
+
+ ea0: ea0 {
+ sensor = <&sensor_information0>;
+ };
+
+ ea1: ea1 {
+ sensor = <&sensor_information1>;
+ };
+
+};
+
+CPU0: cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0x0>;
+ qcom,ea = <&ea0>;
+};
diff --git a/Documentation/devicetree/bindings/arm/msm/spm-v2.txt b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
new file mode 100644
index 0000000..194059c
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
@@ -0,0 +1,169 @@
+* MSM Subsystem Power Manager (spm-v2)
+
+S4 generation of MSMs have SPM hardware blocks to control the Application
+Processor Sub-System power. These SPM blocks run individual state machine
+to determine what the core (L2 or Krait/Scorpion) would do when the WFI
+instruction is executed by the core. The SAW hardware block handles SPM and
+AVS functionality for the cores.
+
+The devicetree representation of the SPM block should be:
+
+Required properties
+
+- compatible: "qcom,spm-v2"
+- reg: The physical address and the size of the SPM's memory mapped registers
+- qcom,cpu: phandle for the CPU that the SPM block is attached to. This field
+is required on only for SPMs that control the CPU. This field is not required
+for SPMs that control L2/CCI/L3
+- qcom,saw2-ver-reg: The location of the version register
+- qcom,name: The name with which a SPM device is identified by the power
+management code.
+
+----------------------------------------------------
+Non-PSCI targets should follow the rules shown below
+----------------------------------------------------
+Required properties for only Non-PSCI targets:
+
+- qcom,saw2-cfg: SAW2 configuration register
+- qcom,saw2-spm-ctl: The SPM control register
+- qcom,saw2-spm-dly: Provides the values for the SPM delay command in the SPM
+ sequence
+
+Optional properties for only Non-PSCI targets
+- reg-names: Register names for the physical address required if spm device
+ has more than one physical addressed to be mapped. Allowed register
+ names are: "saw-base", "q2s", "hw-flush", "slpreq"
+- qcom,saw2-avs-ctl: The AVS control register
+- qcom,saw2-avs-hysterisis: The AVS hysterisis register to delay the AVS
+ controller requests
+- qcom,vctl-timeout-us: The timeout value in us to wait for voltage to change
+ after sending the voltage command to the PMIC
+- qcom,saw2-avs-limit: The AVS limit register
+- qcom,saw2-avs-dly: The AVS delay register is used to specify the delay values
+ between AVS controller requests
+- qcom,saw2-pmic-data0..7: Specify the pmic data value and the associated FTS
+ index to send the PMIC data to
+- qcom,vctl-port: The PVC (PMIC Virtual Channel) port used for changing
+ voltage
+- qcom,phase-port: The PVC port used for changing the number of phases
+- qcom,pfm-port: The PVC port used for enabling PWM/PFM modes
+- qcom,cpu-vctl-mask: Mask of cpus, whose voltage the spm device can control.
+ Depricated: Replaced with cpu-vctl-list when cpu phandles are available.
+- qcom,cpu-vctl-list: List of cpu node phandles, whose voltage the spm device
+ can control.
+- qcom,use-qchannel-for-pc: Boolean property to specify if qchannel should be
+ ignored when entering power collapse. If this property is set qchannel
+ will not be ignored in power collapse.
+- qcom,supports-rpm-hs: Indicates that this SPM instance allow handshake with
+RPM processor when executing the sleep command in the SPM sequence. Supported
+only on SAW2 v3.0 and above.
+- qcom,use-spm-clock-gating: This boolean property is used to indicate that
+ the SPM needs to be used for clock gating. Using the SPM for clock
+ gating would result in auto clock gating being disabled. Use this on
+ targets that do not support or do not use auto clock gating.
+- qcom,use-qchannel-for-wfi: This boolean property is used to indicate
+ that the SPM gets triggerd by the qchannel and not by means of
+ wfi. So a wfe could trigger a spm for clock gating as well.
+- modes: Lists all the available low power modes for the device
+
+Second level properties for modes
+
+Required properties (if modes node is available)
+- qcom,label: Specifies the mode name such as:
+ qcom,saw2-spm-cmd-wfi: WFI mode
+ qcom,saw2-spm-cmd-ret: Retention mode
+ qcom,saw2-spm-cmd-spc: Standalone PC mode
+ qcom,saw2-spm-cmd-pc: Power Collapse mode
+ qcom,saw2-spm-cmd-gdhs: GDHS mode
+- qcom,sequence: Specifies sequence for the low power mode
+Optional properties
+- qcom,pc_mode: Specifies pc_mode bit should be set in the SPM control register
+- qcom,ret_mode: Specifies ret_mode bit should be set in the SPM control register
+- qcom,spm_en: Specifies spm_en bit should be set in the SPM control register
+- qcom,isar: Specifies isar bit should be set in the SPM control register
+ Specify this property only if SPM should retain its start address at
+ the end of the program.
+- qcom,slp_cmd_mode: Specifies slp_cmd_mode bit should be set in SPM control register.
+ Adding this property results in SPM handshaking with RPM. Please remove
+ the RPM handshake command from the sleep sequence, replace that with
+ Sleep without RPM handshake command.
+- qcom,event_sync: Specifies event_sync byte should be set in SPM control
+ register.
+
+----------------------------------------------------
+PSCI targets should follow the rules shown below
+----------------------------------------------------
+Optional properties for only PSCI targets:
+
+- qcom,saw2-avs-ctl: The AVS control register
+- qcom,saw2-avs-hysterisis: The AVS hysterisis register to delay the AVS
+ controller requests
+- qcom,vctl-timeout-us: The timeout value in us to wait for voltage to change
+ after sending the voltage command to the PMIC
+- qcom,saw2-avs-limit: The AVS limit register
+- qcom,saw2-avs-dly: The AVS delay register is used to specify the delay values
+ between AVS controller requests
+- qcom,vctl-port: The PVC (PMIC Virtual Channel) port used for changing
+ voltage
+- qcom,phase-port: The PVC port used for changing the number of phases
+- qcom,pfm-port: The PVC port used for enabling PWM/PFM modes
+- qcom,cpu-vctl-list: List of cpu node phandles, whose voltage the spm device
+ can control.
+
+
+Example 1:
+ qcom,spm@f9089000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf9089000 0x1000>;
+ qcom,cpu = <&CPU0>;
+ qcom,saw2-ver-reg = <0xfd0>;
+ qcom,saw2-cfg = <0x1b>;
+ qcom,saw2-avs-ctl = <0>;
+ qcom,saw2-avs-hysteresis = <0>;
+ qcom,saw2-avs-limit = <0>;
+ qcom,saw2-avs-dly= <0>;
+ qcom,saw2-spm-dly= <0x20000400>;
+ qcom,saw2-spm-ctl = <0x1>;
+ qcom,cpu-vctl-list = <&CPU0 &CPU1 &CPU2 &CPU3>;
+ qcom,mode0 {
+ qcom,label = "qcom,saw2-spm-cmd-wfi";
+ qcom,sequence = [03 0b 0f];
+ qcom,spm_en;
+ };
+
+ qcom,mode1 {
+ qcom,label = "qcom,saw2-spm-cmd-spc";
+ qcom,sequence = [00 20 50 80 60 70 10 92
+ a0 b0 03 68 70 3b 92 a0 b0
+ 82 2b 50 10 30 02 22 30 0f];
+ qcom,spm_en;
+ qcom,pc_mode;
+ };
+
+ qcom,mode2 {
+ qcom,label = "qcom,saw2-spm-cmd-pc";
+ qcom,sequence = [00 20 10 92 a0 b0 07 3b 92
+ a0 b0 82 10 30 02 22 30 0f];
+ qcom,spm_en;
+ qcom,pc_mode;
+ };
+ };
+
+Example 2:
+ qcom,spm@9A10000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0x9A10000 0x1000>;
+ qcom,name = "system-cbf"; /* CBF SAW */
+ qcom,saw2-ver-reg = <0xFD0>;
+ qcom,cpu-vctl-list = <&CPU0 &CPU1 &CPU2 &CPU3>;
+ qcom,vctl-timeout-us = <50>;
+ qcom,vctl-port = <0x0>;
+ qcom,phase-port = <0x1>;
+ qcom,saw2-avs-ctl = <0x1100>;
+ qcom,pfm-port = <0x2>;
+};
+
diff --git a/Documentation/devicetree/bindings/cache/msm_gladiator_erp_v2.txt b/Documentation/devicetree/bindings/cache/msm_gladiator_erp.txt
similarity index 78%
rename from Documentation/devicetree/bindings/cache/msm_gladiator_erp_v2.txt
rename to Documentation/devicetree/bindings/cache/msm_gladiator_erp.txt
index 3c1c5c0..73a48aa 100644
--- a/Documentation/devicetree/bindings/cache/msm_gladiator_erp_v2.txt
+++ b/Documentation/devicetree/bindings/cache/msm_gladiator_erp.txt
@@ -1,7 +1,8 @@
* MSM Gladiator error reporting driver
Required properties:
-- compatible: Should be "qcom,msm-gladiator-v2"
+- compatible: Should be "qcom,msm-gladiator" or "qcom,msm-gladiator-v2" or
+"qcom,msm-gladiator-v3"
- reg: I/O address Gladiator H/W block
- reg-names: Should be "gladiator_base"
- interrupts: Should contain the gladiator error interrupt number
@@ -11,7 +12,7 @@
Example:
qcom,msm-gladiator-v2@b1c0000 {
- compatible = "qcom,msm-gladiator-v2";
+ compatible = "qcom,msm-gladiator";
reg = <0xb1c0000 0xe000>;
reg-names = "gladiator_base";
interrupts = <0 34 0>;
diff --git a/Documentation/devicetree/bindings/clock/imx31-clock.txt b/Documentation/devicetree/bindings/clock/imx31-clock.txt
index 19df842c..8163d56 100644
--- a/Documentation/devicetree/bindings/clock/imx31-clock.txt
+++ b/Documentation/devicetree/bindings/clock/imx31-clock.txt
@@ -77,7 +77,7 @@
clks: ccm@53f80000{
compatible = "fsl,imx31-ccm";
reg = <0x53f80000 0x4000>;
- interrupts = <0 31 0x04 0 53 0x04>;
+ interrupts = <31>, <53>;
#clock-cells = <1>;
};
diff --git a/Documentation/devicetree/bindings/display/msm/dsi.txt b/Documentation/devicetree/bindings/display/msm/dsi.txt
index 6b1cab1..3534f04 100644
--- a/Documentation/devicetree/bindings/display/msm/dsi.txt
+++ b/Documentation/devicetree/bindings/display/msm/dsi.txt
@@ -107,6 +107,20 @@
Optional properties:
- qcom,dsi-phy-regulator-ldo-mode: Boolean value indicating if the LDO mode PHY
regulator is wanted.
+- qcom,mdss-mdp-transfer-time-us: Specifies the dsi transfer time for command mode
+ panels in microseconds. Driver uses this number to adjust
+ the clock rate according to the expected transfer time.
+ Increasing this value would slow down the mdp processing
+ and can result in slower performance.
+ Decreasing this value can speed up the mdp processing,
+ but this can also impact power consumption.
+ As a rule this time should not be higher than the time
+ that would be expected with the processing at the
+ dsi link rate since anyways this would be the maximum
+ transfer time that could be achieved.
+ If ping pong split is enabled, this time should not be higher
+ than two times the dsi link rate time.
+ If the property is not specified, then the default value is 14000 us.
[1] Documentation/devicetree/bindings/clocks/clock-bindings.txt
[2] Documentation/devicetree/bindings/graph.txt
@@ -157,6 +171,8 @@
qcom,master-dsi;
qcom,sync-dual-dsi;
+ qcom,mdss-mdp-transfer-time-us = <12000>;
+
pinctrl-names = "default", "sleep";
pinctrl-0 = <&dsi_active>;
pinctrl-1 = <&dsi_suspend>;
diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt
index e4ba5be..5eee0c9 100644
--- a/Documentation/devicetree/bindings/display/msm/sde.txt
+++ b/Documentation/devicetree/bindings/display/msm/sde.txt
@@ -90,8 +90,6 @@
-- qcom,supply-post-on-sleep: time to sleep (ms) after turning on
-- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off
-- qcom,supply-post-off-sleep: time to sleep (ms) after turning off
-- qcom,sde-reg-bus: Property to provide Bus scaling for register access for
- mdss blocks.
- qcom,sde-sspp-src-size: A u32 value indicates the address range for each sspp.
- qcom,sde-mixer-size: A u32 value indicates the address range for each mixer.
- qcom,sde-ctl-size: A u32 value indicates the address range for each ctl.
@@ -186,6 +184,7 @@
e.g. qcom,sde-sspp-vig-blocks
-- qcom,sde-vig-csc-off: offset of CSC hardware
-- qcom,sde-vig-qseed-off: offset of QSEED hardware
+ -- qcom,sde-vig-qseed-size: A u32 address range for qseed scaler.
-- qcom,sde-vig-pcc: offset and version of PCC hardware
-- qcom,sde-vig-hsic: offset and version of global PA adjustment
-- qcom,sde-vig-memcolor: offset and version of PA memcolor hardware
@@ -195,6 +194,7 @@
indicates that the SSPP RGB contains that feature hardware.
e.g. qcom,sde-sspp-vig-blocks
-- qcom,sde-rgb-scaler-off: offset of RGB scaler hardware
+ -- qcom,sde-rgb-scaler-size: A u32 address range for scaler.
-- qcom,sde-rgb-pcc: offset and version of PCC hardware
- qcom,sde-dspp-blocks: A node that lists the blocks inside the DSPP hardware. The
block entries will contain the offset and version of each
@@ -247,6 +247,33 @@
- qcom,sde-reg-dma-version: Version of the reg dma hardware block.
- qcom,sde-reg-dma-trigger-off: Offset of the lut dma trigger reg from "mdp_phys"
defined in reg property.
+- qcom,sde-dram-channels: This represents the number of channels in the
+ Bus memory controller.
+- qcom,sde-num-nrt-paths: Integer property represents the number of non-realtime
+ paths in each Bus Scaling Usecase. This value depends on
+ number of AXI ports that are dedicated to non-realtime VBIF
+ for particular chipset.
+ These paths must be defined after rt-paths in
+ "qcom,msm-bus,vectors-KBps" vector request.
+- qcom,sde-max-bw-low-kbps: This value indicates the max bandwidth in Kbps
+ that can be supported without underflow.
+ This is a low bandwidth threshold which should
+ be applied in most scenarios to be safe from
+ underflows when unable to satisfy bandwidth
+ requirements.
+- qcom,sde-max-bw-high-kbps: This value indicates the max bandwidth in Kbps
+ that can be supported without underflow.
+ This is a high bandwidth threshold which can be
+ applied in scenarios where panel interface can
+ be more tolerant to memory latency such as
+ command mode panels.
+
+Bus Scaling Subnodes:
+- qcom,sde-reg-bus: Property to provide Bus scaling for register access for
+ mdss blocks.
+- qcom,sde-data-bus: Property to provide Bus scaling for data bus access for
+ mdss blocks.
+
Bus Scaling Data:
- qcom,msm-bus,name: String property describing client name.
- qcom,msm-bus,num-cases: This is the number of Bus Scaling use cases
@@ -415,9 +442,16 @@
qcom,sde-vbif-dynamic-ot-wr-limit = <62208000 2>,
<124416000 4>, <248832000 16>;
+ qcom,sde-dram-channels = <2>;
+ qcom,sde-num-nrt-paths = <1>;
+
+ qcom,sde-max-bw-high-kbps = <9000000>;
+ qcom,sde-max-bw-low-kbps = <9000000>;
+
qcom,sde-sspp-vig-blocks {
qcom,sde-vig-csc-off = <0x320>;
qcom,sde-vig-qseed-off = <0x200>;
+ qcom,sde-vig-qseed-size = <0x74>;
/* Offset from vig top, version of HSIC */
qcom,sde-vig-hsic = <0x200 0x00010000>;
qcom,sde-vig-memcolor = <0x200 0x00010000>;
@@ -426,6 +460,7 @@
qcom,sde-sspp-rgb-blocks {
qcom,sde-rgb-scaler-off = <0x200>;
+ qcom,sde-rgb-scaler-size = <0x74>;
qcom,sde-rgb-pcc = <0x380 0x00010000>;
};
@@ -466,6 +501,18 @@
};
};
+ qcom,sde-data-bus {
+ qcom,msm-bus,name = "mdss_sde";
+ qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,num-paths = <3>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>, <23 512 0 0>, <25 512 0 0>,
+ <22 512 0 6400000>, <23 512 0 6400000>,
+ <25 512 0 6400000>,
+ <22 512 0 6400000>, <23 512 0 6400000>,
+ <25 512 0 6400000>;
+ };
+
qcom,sde-reg-bus {
/* Reg Bus Scale Settings */
qcom,msm-bus,name = "mdss_reg";
diff --git a/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
index c1cba82..c7f43bc 100644
--- a/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
+++ b/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
@@ -79,6 +79,10 @@
Optional properties:
- qcom,mdss-dsi-panel-name: A string used as a descriptive name of the panel
+- qcom,mdss-dsi-panel-phy-timings: An array of length 'n' char that specifies the DSI PHY lane
+ timing settings for the panel. This is specific to SDE DRM driver.
+ The value of 'n' depends on the DSI PHY h/w revision and parsing this
+ property properly will be taken care in the DSI PHY DRM driver.
- qcom,cmd-sync-wait-broadcast: Boolean used to broadcast dcs command to panels.
- qcom,mdss-dsi-fbc-enable: Boolean used to enable frame buffer compression mode.
- qcom,mdss-dsi-fbc-slice-height: Slice height(in lines) of compressed block.
diff --git a/Documentation/devicetree/bindings/drm/msm/sde-dsi.txt b/Documentation/devicetree/bindings/drm/msm/sde-dsi.txt
index ea35d14..c6626d1 100644
--- a/Documentation/devicetree/bindings/drm/msm/sde-dsi.txt
+++ b/Documentation/devicetree/bindings/drm/msm/sde-dsi.txt
@@ -9,8 +9,9 @@
versions include 1.4 and 2.0.
eg: qcom,dsi-ctrl-hw-v1.4, qcom,dsi-ctrl-hw-v2.0
And for dsi phy driver:
- qcom,dsi-phy-v1.0, qcom,dsi-phy-v2.0, qcom,dsi-phy-v3.0,
- qcom,dsi-phy-v4.0
+ qcom,dsi-phy-v0.0-hpm, qcom,dsi-phy-v0.0-lpm,
+ qcom,dsi-phy-v1.0, qcom,dsi-phy-v2.0,
+ qcom,dsi-phy-v3.0
- reg: Base address and length of DSI controller's memory
mapped regions.
- reg-names: A list of strings that name the list of regs.
@@ -80,3 +81,17 @@
-- qcom,supply-post-on-sleep: time to sleep (ms) after turning on
-- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off
-- qcom,supply-post-off-sleep: time to sleep (ms) after turning off
+- qcom,mdss-mdp-transfer-time-us: Specifies the dsi transfer time for command mode
+ panels in microseconds. Driver uses this number to adjust
+ the clock rate according to the expected transfer time.
+ Increasing this value would slow down the mdp processing
+ and can result in slower performance.
+ Decreasing this value can speed up the mdp processing,
+ but this can also impact power consumption.
+ As a rule this time should not be higher than the time
+ that would be expected with the processing at the
+ dsi link rate since anyways this would be the maximum
+ transfer time that could be achieved.
+ If ping pong split enabled, this time should not be higher
+ than two times the dsi link rate time.
+ If the property is not specified, then the default value is 14000 us.
diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt
index d1eaa17..2aab68d 100644
--- a/Documentation/devicetree/bindings/gpu/adreno.txt
+++ b/Documentation/devicetree/bindings/gpu/adreno.txt
@@ -124,7 +124,20 @@
Specify the bit of the highest DDR bank. This
is programmed into protected registers and also
passed to the user as a property.
-
+- qcom,min-access-length:
+ Specify the minimum access length for the chip.
+ Either 32 or 64 bytes.
+ Based on the above options, program the appropriate bit into
+ certain protected registers and also pass to the user as
+ a property.
+- qcom,ubwc-mode:
+ Specify the ubwc mode for this chip.
+ 1: UBWC 1.0
+ 2: UBWC 2.0
+ 3: UBWC 3.0
+ Based on the ubwc mode, program the appropriate bit into
+ certain protected registers and also pass to the user as
+ a property.
- qcom,l2pc-cpu-mask:
Disables L2PC on masked CPUs when any of Graphics
rendering thread is running on masked CPUs.
@@ -143,6 +156,12 @@
Specify the name of GPU temperature sensor. This name will be used
to get the temperature from the thermal driver API.
+- qcom,enable-midframe-timer:
+ Boolean. Enables the use of midframe sampling timer. This timer
+ samples the GPU powerstats if the cmdbatch expiry takes longer than
+ the threshold set by KGSL_GOVERNOR_CALL_INTERVAL. Enable only if
+ target has NAP state enabled.
+
GPU Quirks:
- qcom,gpu-quirk-two-pass-use-wfi:
Signal the GPU to set Set TWOPASSUSEWFI bit in
diff --git a/Documentation/devicetree/bindings/media/video/msm-vidc.txt b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
index ce5334d..b894c31 100644
--- a/Documentation/devicetree/bindings/media/video/msm-vidc.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
@@ -10,7 +10,6 @@
of macroblocks per second. The load is a reflection of hardware capability
rather than a performance guarantee. Performance is guaranteed only up to
advertised capability of the chipset.
-- qcom,firmware-name : firmware file name to be downloaded.
Optional properties:
- reg : offset and length of the register set for the device.
@@ -24,22 +23,10 @@
supports mvc decoder = 0x00000003
supports h264 encoder = 0x00000004
supports h264 decoder = 0x0000000c
- supports h263 encoder = 0x00000010
- supports h263 decoder = 0x00000030
supports mpeg1 encoder = 0x00000040
supports mpeg1 decoder = 0x000000c0
supports mpeg2 encoder = 0x00000100
supports mpeg2 decoder = 0x00000300
- supports mpeg4 encoder = 0x00000400
- supports mpeg4 decoder = 0x00000c00
- supports divx_311 encoder = 0x00001000
- supports divx_311 decoder = 0x00003000
- supports divx encoder = 0x00004000
- supports divx decoder = 0x0000c000
- supports vc1 encoder = 0x00010000
- supports vc1 decoder = 0x00030000
- supports spark encoder = 0x00040000
- supports spark decoder = 0x000c0000
supports vp6 encoder = 0x00100000
supports vp6 decoder = 0x00300000
supports vp7 encoder = 0x00400000
@@ -48,25 +35,12 @@
supports vp8 decoder = 0x03000000
supports hevc encoder = 0x04000000
supports hevc decoder = 0x0c000000
- supports hevc_hybrid encoder = 0x10000000
- supports hevc_hybrid decoder = 0x30000000
-- qcom,dcvs-tbl : specifies the parameter to configure DCVS algorithm. Video
- instance load (in mbs/sec) and corresponding low and high threshold DCVS
- load for supported codec formats.
-- qcom,dcvs-limit : specifies the minimum specifications required to kick in
- DCVS algorithm. Min MBs per frame and the fps for encoder and decoder to
- kick in DCVS.
-- qcom,imem-ab-tbl : video core frequency in Hz and corresponding imem ab value
- in kbps. The imem ab value corresponds to the clock frequency at which imem
- should operate for a particular video core frequency.
- qcom,reg-presets : list of offset-value pairs for registers to be written.
The offsets are from the base offset specified in 'reg'. This is mainly
used for QoS, VBIF, etc. presets for video.
- qcom,qdss-presets : list of physical address and memory allocation size pairs.
when fw_debug_mode is set as HFI_DEBUG_MODE_QDSS, all firmware messages will be
written to QDSS memory.
-- qcom,imem-size: imem size required by hardware for optimum performance.
- If imem is not present then there is no need to specify this property.
- *-supply: A phandle pointing to the appropriate regulator. Number of
regulators vary across targets.
- clock-names: an array of clocks that the driver is supposed to be
@@ -90,8 +64,6 @@
represented in Q16 format.
- qcom,sw-power-collapse = A bool indicating if video hardware core can be
power collapsed in idle state.
-- qcom,slave-side-cp = A bool indicating the content protection mode for an
- ongoing video session. It is true for targets where the mode is slave side.
- qcom,never-unload-fw = A bool indicating if video firmware should be not be
unloaded after all active sessions have closed. Once a new session starts up
after this, the firmware will be ready to go. This should be set on platforms
@@ -99,8 +71,6 @@
- qcom,use-non-secure-pil = A bool indicating which type of pil to use to load
the fw.
- qcom,fw-bias = The address at which venus fw is loaded (manually).
-- qcom,enable-idle-indicator = A bool to enable video hardware to generate
- idle message when it is in idle state.
- qcom,enable-thermal-mitigation = A bool to enable thermal mitigation when
thermal run away occurs.
- qcom,hfi-version = The hfi packetization version supported by venus firmware.
@@ -204,22 +174,6 @@
<72000 133330000 0x0c000000>, /* HEVC decoder VGA 60fps */
<36000 133330000 0x0c000000>, /* HEVC VGA 30 fps */
<36000 133330000 0x01000414>; /* Legacy encoder VGA 30 fps */
- qcom,dcvs-tbl =
- <972000 972000 19944000 0xffffffff>, /* Legacy decoder UHD 30fps */
- <489600 489600 972000 0xffffffff>, /* Legacy decoder 1080p 60fps */
- <244800 244800 489600 0xffffffff>, /* Legacy decoder 1080p 30fps */
- <829440 489600 972000 0x55555555>; /* Legacy encoder DCI 24fps
- qcom,dcvs-limit =
- <32400 30>, /* Encoder UHD */
- <14400 30>; /* Decoder WQHD */
- qcom,imem-ab-tbl =
- <75000000 1500000>, /* imem clk @ 75 Mhz */
- <150000000 1500000>, /* imem clk @ 75 Mhz */
- <355333333 2500000>, /* imem clk @ 170 Mhz */
- <533000000 6000000>; /* imem clk @ 320 Mhz */
-
- qcom,imem-size = <4096>;
- qcom,firmware-name = "venus";
qcom,hfi-version = "3xx";
qcom,reg-presets = <0x80004 0x1>,
<0x80178 0x00001FFF>;
@@ -230,12 +184,10 @@
clock-names = "foo_clk", "bar_clk", "baz_clk";
qcom,clock-configs = <0x3 0x1 0x0>;
qcom,sw-power-collapse;
- qcom,enable-idle-indicator;
qcom,buffer-type-tz-usage-table = <0x1 0x1>,
<0x1fe 0x2>;
qcom,enable-thermal-mitigation;
qcom,use-non-secure-pil;
- qcom,slave-side-cp;
qcom,use_dynamic_bw_update;
qcom,fw-bias = <0xe000000>;
msm_vidc_cb1: msm_vidc_cb1 {
diff --git a/Documentation/devicetree/bindings/mfd/tps65086.txt b/Documentation/devicetree/bindings/mfd/tps65086.txt
index d370561..9cfa886 100644
--- a/Documentation/devicetree/bindings/mfd/tps65086.txt
+++ b/Documentation/devicetree/bindings/mfd/tps65086.txt
@@ -23,7 +23,7 @@
defined below.
Optional regulator properties:
- - ti,regulator-step-size-25mv : This is applicable for buck[1,2,6], set this
+ - ti,regulator-step-size-25mv : This is applicable for buck[1-6], set this
if the regulator is factory set with a 25mv
step voltage mapping.
- ti,regulator-decay : This is applicable for buck[1-6], set this if
diff --git a/Documentation/devicetree/bindings/regulator/fan53555.txt b/Documentation/devicetree/bindings/regulator/fan53555.txt
index 54a3f2c..90ca0b7 100644
--- a/Documentation/devicetree/bindings/regulator/fan53555.txt
+++ b/Documentation/devicetree/bindings/regulator/fan53555.txt
@@ -1,7 +1,8 @@
Binding for Fairchild FAN53555 regulators
Required properties:
- - compatible: one of "fcs,fan53555", "silergy,syr827", "silergy,syr828"
+ - compatible: one of "fcs,fan53555", "silergy,syr827", "silergy,syr828" or
+ "halo,hl7509"
- reg: I2C address
Optional properties:
@@ -10,6 +11,10 @@
voltage. The other one is used for the runtime voltage setting
Possible values are either <0> or <1>
- vin-supply: regulator supplying the vin pin
+ - fcs,disable-suspend: Boolean flag which specifies if suspend voltage
+ configuration should be avoided. If this property is present,
+ then the voltage selector register defined by
+ fcs,suspend-voltage-selector should not be modified at runtime.
Example:
@@ -21,3 +26,12 @@
vin-supply = <&parent_reg>;
fcs,suspend-voltage-selector = <1>;
};
+
+ regulator@60 {
+ compatible = "halo,hl7509";
+ regulator-name = "hl7509";
+ regulator-min-microvolt = <600000>;
+ regulator-max-microvolt = <1230000>;
+ fcs,suspend-voltage-selector = <1>;
+ fcs,disable-suspend;
+ };
diff --git a/Documentation/devicetree/bindings/regulator/rpmh-regulator.txt b/Documentation/devicetree/bindings/regulator/rpmh-regulator.txt
index d97b50e..9deb7d4 100644
--- a/Documentation/devicetree/bindings/regulator/rpmh-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/rpmh-regulator.txt
@@ -163,6 +163,24 @@
regulator. Supported values are RPMH_REGULATOR_LEVEL_*
(i.e. 1 to ~513).
+- qcom,min-dropout-voltage
+ Usage: optional; VRM regulators only
+ Value type: <u32>
+ Definition: Specifies the minimum voltage in microvolts that the parent
+ supply regulator must output above the output of this
+ regulator. It is only meaningful if the property
+ <regulator-name>-parent-supply has been specified in the
+ first level node.
+
+- qcom,min-dropout-voltage-level
+ Usage: optional; ARC regulators only
+ Value type: <u32>
+ Definition: Specifies the minimum voltage level difference that the
+ parent supply regulator must output above the output of this
+ regulator. It is only meaningful if the property
+ <regulator-name>-parent-supply has been specified in the
+ first level node.
+
========
Examples
========
diff --git a/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt b/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt
index 90d8a35..95cc85a 100644
--- a/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt
+++ b/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt
@@ -46,8 +46,8 @@
BAM pipe is not available to receive data from slimbus
- qcom,apps-ch-pipes: This value represents BAM pipe-mask used by application
processor for data channels. If this property is not defined,
- default mask of 0x3F000000 is used indicating apps can use 6
- pipes from 24-29.
+ default mask of 0 is used indicating that application
+ processor does not use BAM pipes for data channels.
- qcom,ea-pc: This value represents product code (PC) field of enumeration
address (EA) for the QTI slimbus controller hardware.
This value is needed if data-channels originating from apps
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index f0a48ea..3bdc8967 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -116,6 +116,7 @@
grinn Grinn
gumstix Gumstix, Inc.
gw Gateworks Corporation
+halo Halo Microelectronics Co., Ltd.
hannstar HannStar Display Corporation
haoyu Haoyu Microelectronic Co. Ltd.
hardkernel Hardkernel Co., Ltd
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index be49573..9c5e23f 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -4008,10 +4008,11 @@
it if 0 is given (See Documentation/cgroup-v1/memory.txt)
swiotlb= [ARM,IA-64,PPC,MIPS,X86]
- Format: { <int> | force }
+ Format: { <int> | force | noforce }
<int> -- Number of I/O TLB slabs
force -- force using of bounce buffers even if they
wouldn't be automatically used by the kernel
+ noforce -- Never use bounce buffers (for debugging)
switches= [HW,M68k]
diff --git a/Documentation/media/index.rst b/Documentation/media/index.rst
index e347a3e..7f8f0af 100644
--- a/Documentation/media/index.rst
+++ b/Documentation/media/index.rst
@@ -1,11 +1,6 @@
Linux Media Subsystem Documentation
===================================
-.. Sphinx 1.4.x has a definition for DUrole that doesn't work on alltt blocks
-.. raw:: latex
-
- \renewcommand*{\DUrole}[2]{ #2 }
-
Contents:
.. toctree::
diff --git a/Documentation/scheduler/sched-energy.txt b/Documentation/scheduler/sched-energy.txt
new file mode 100644
index 0000000..dab2f90
--- /dev/null
+++ b/Documentation/scheduler/sched-energy.txt
@@ -0,0 +1,362 @@
+Energy cost model for energy-aware scheduling (EXPERIMENTAL)
+
+Introduction
+=============
+
+The basic energy model uses platform energy data stored in sched_group_energy
+data structures attached to the sched_groups in the sched_domain hierarchy. The
+energy cost model offers two functions that can be used to guide scheduling
+decisions:
+
+1. static unsigned int sched_group_energy(struct energy_env *eenv)
+2. static int energy_diff(struct energy_env *eenv)
+
+sched_group_energy() estimates the energy consumed by all cpus in a specific
+sched_group including any shared resources owned exclusively by this group of
+cpus. Resources shared with other cpus are excluded (e.g. later level caches).
+
+energy_diff() estimates the total energy impact of a utilization change. That
+is, adding, removing, or migrating utilization (tasks).
+
+Both functions use a struct energy_env to specify the scenario to be evaluated:
+
+ struct energy_env {
+ struct sched_group *sg_top;
+ struct sched_group *sg_cap;
+ int cap_idx;
+ int util_delta;
+ int src_cpu;
+ int dst_cpu;
+ int energy;
+ };
+
+sg_top: sched_group to be evaluated. Not used by energy_diff().
+
+sg_cap: sched_group covering the cpus in the same frequency domain. Set by
+sched_group_energy().
+
+cap_idx: Capacity state to be used for energy calculations. Set by
+find_new_capacity().
+
+util_delta: Amount of utilization to be added, removed, or migrated.
+
+src_cpu: Source cpu from where 'util_delta' utilization is removed. Should be
+-1 if no source (e.g. task wake-up).
+
+dst_cpu: Destination cpu where 'util_delta' utilization is added. Should be -1
+if utilization is removed (e.g. terminating tasks).
+
+energy: Result of sched_group_energy().
+
+The metric used to represent utilization is the actual per-entity running time
+averaged over time using a geometric series. Very similar to the existing
+per-entity load-tracking, but _not_ scaled by task priority and capped by the
+capacity of the cpu. The latter property does mean that utilization may
+underestimate the compute requirements for task on fully/over utilized cpus.
+The greatest potential for energy savings without affecting performance too much
+is scenarios where the system isn't fully utilized. If the system is deemed
+fully utilized load-balancing should be done with task load (includes task
+priority) instead in the interest of fairness and performance.
+
+
+Background and Terminology
+===========================
+
+To make it clear from the start:
+
+energy = [joule] (resource like a battery on powered devices)
+power = energy/time = [joule/second] = [watt]
+
+The goal of energy-aware scheduling is to minimize energy, while still getting
+the job done. That is, we want to maximize:
+
+ performance [inst/s]
+ --------------------
+ power [W]
+
+which is equivalent to minimizing:
+
+ energy [J]
+ -----------
+ instruction
+
+while still getting 'good' performance. It is essentially an alternative
+optimization objective to the current performance-only objective for the
+scheduler. This alternative considers two objectives: energy-efficiency and
+performance. Hence, there needs to be a user controllable knob to switch the
+objective. Since it is early days, this is currently a sched_feature
+(ENERGY_AWARE).
+
+The idea behind introducing an energy cost model is to allow the scheduler to
+evaluate the implications of its decisions rather than applying energy-saving
+techniques blindly that may only have positive effects on some platforms. At
+the same time, the energy cost model must be as simple as possible to minimize
+the scheduler latency impact.
+
+Platform topology
+------------------
+
+The system topology (cpus, caches, and NUMA information, not peripherals) is
+represented in the scheduler by the sched_domain hierarchy which has
+sched_groups attached at each level that covers one or more cpus (see
+sched-domains.txt for more details). To add energy awareness to the scheduler
+we need to consider power and frequency domains.
+
+Power domain:
+
+A power domain is a part of the system that can be powered on/off
+independently. Power domains are typically organized in a hierarchy where you
+may be able to power down just a cpu or a group of cpus along with any
+associated resources (e.g. shared caches). Powering up a cpu means that all
+power domains it is a part of in the hierarchy must be powered up. Hence, it is
+more expensive to power up the first cpu that belongs to a higher level power
+domain than powering up additional cpus in the same high level domain. Two
+level power domain hierarchy example:
+
+ Power source
+ +-------------------------------+----...
+per group PD G G
+ | +----------+ |
+ +--------+-------| Shared | (other groups)
+per-cpu PD G G | resource |
+ | | +----------+
+ +-------+ +-------+
+ | CPU 0 | | CPU 1 |
+ +-------+ +-------+
+
+Frequency domain:
+
+Frequency domains (P-states) typically cover the same group of cpus as one of
+the power domain levels. That is, there might be several smaller power domains
+sharing the same frequency (P-state) or there might be a power domain spanning
+multiple frequency domains.
+
+From a scheduling point of view there is no need to know the actual frequencies
+[Hz]. All the scheduler cares about is the compute capacity available at the
+current state (P-state) the cpu is in and any other available states. For that
+reason, and to also factor in any cpu micro-architecture differences, compute
+capacity scaling states are called 'capacity states' in this document. For SMP
+systems this is equivalent to P-states. For mixed micro-architecture systems
+(like ARM big.LITTLE) it is P-states scaled according to the micro-architecture
+performance relative to the other cpus in the system.
+
+Energy modelling:
+------------------
+
+Due to the hierarchical nature of the power domains, the most obvious way to
+model energy costs is therefore to associate power and energy costs with
+domains (groups of cpus). Energy costs of shared resources are associated with
+the group of cpus that share the resources, only the cost of powering the
+cpu itself and any private resources (e.g. private L1 caches) is associated
+with the per-cpu groups (lowest level).
+
+For example, for an SMP system with per-cpu power domains and a cluster level
+(group of cpus) power domain we get the overall energy costs to be:
+
+ energy = energy_cluster + n * energy_cpu
+
+where 'n' is the number of cpus powered up and energy_cluster is the cost paid
+as soon as any cpu in the cluster is powered up.
+
+The power and frequency domains can naturally be mapped onto the existing
+sched_domain hierarchy and sched_groups by adding the necessary data to the
+existing data structures.
+
+The energy model considers energy consumption from two contributors (shown in
+the illustration below):
+
+1. Busy energy: Energy consumed while a cpu and the higher level groups that it
+belongs to are busy running tasks. Busy energy is associated with the state of
+the cpu, not an event. The time the cpu spends in this state varies. Thus, the
+most obvious platform parameter for this contribution is busy power
+(energy/time).
+
+2. Idle energy: Energy consumed while a cpu and higher level groups that it
+belongs to are idle (in a C-state). Like busy energy, idle energy is associated
+with the state of the cpu. Thus, the platform parameter for this contribution
+is idle power (energy/time).
+
+Energy consumed during transitions from an idle-state (C-state) to a busy state
+(P-state) or going the other way is ignored by the model to simplify the energy
+model calculations.
+
+
+ Power
+ ^
+ | busy->idle idle->busy
+ | transition transition
+ |
+ | _ __
+ | / \ / \__________________
+ |______________/ \ /
+ | \ /
+ | Busy \ Idle / Busy
+ | low P-state \____________/ high P-state
+ |
+ +------------------------------------------------------------> time
+
+Busy |--------------| |-----------------|
+
+Wakeup |------| |------|
+
+Idle |------------|
+
+
+The basic algorithm
+====================
+
+The basic idea is to determine the total energy impact when utilization is
+added or removed by estimating the impact at each level in the sched_domain
+hierarchy starting from the bottom (sched_group contains just a single cpu).
+The energy cost comes from busy time (sched_group is awake because one or more
+cpus are busy) and idle time (in an idle-state). Energy model numbers account
+for energy costs associated with all cpus in the sched_group as a group.
+
+ for_each_domain(cpu, sd) {
+ sg = sched_group_of(cpu)
+ energy_before = curr_util(sg) * busy_power(sg)
+ + (1-curr_util(sg)) * idle_power(sg)
+ energy_after = new_util(sg) * busy_power(sg)
+ + (1-new_util(sg)) * idle_power(sg)
+ energy_diff += energy_before - energy_after
+
+ }
+
+ return energy_diff
+
+{curr, new}_util: The cpu utilization at the lowest level and the overall
+non-idle time for the entire group for higher levels. Utilization is in the
+range 0.0 to 1.0 in the pseudo-code.
+
+busy_power: The power consumption of the sched_group.
+
+idle_power: The power consumption of the sched_group when idle.
+
+Note: It is a fundamental assumption that the utilization is (roughly) scale
+invariant. Task utilization tracking factors in any frequency scaling and
+performance scaling differences due to difference cpu microarchitectures such
+that task utilization can be used across the entire system.
+
+
+Platform energy data
+=====================
+
+struct sched_group_energy can be attached to sched_groups in the sched_domain
+hierarchy and has the following members:
+
+cap_states:
+ List of struct capacity_state representing the supported capacity states
+ (P-states). struct capacity_state has two members: cap and power, which
+ represents the compute capacity and the busy_power of the state. The
+ list must be ordered by capacity low->high.
+
+nr_cap_states:
+ Number of capacity states in cap_states list.
+
+idle_states:
+ List of struct idle_state containing idle_state power cost for each
+ idle-state supported by the system orderd by shallowest state first.
+ All states must be included at all level in the hierarchy, i.e. a
+ sched_group spanning just a single cpu must also include coupled
+ idle-states (cluster states). In addition to the cpuidle idle-states,
+ the list must also contain an entry for the idling using the arch
+ default idle (arch_idle_cpu()). Despite this state may not be a true
+ hardware idle-state it is considered the shallowest idle-state in the
+ energy model and must be the first entry. cpus may enter this state
+ (possibly 'active idling') if cpuidle decides not enter a cpuidle
+ idle-state. Default idle may not be used when cpuidle is enabled.
+ In this case, it should just be a copy of the first cpuidle idle-state.
+
+nr_idle_states:
+ Number of idle states in idle_states list.
+
+There are no unit requirements for the energy cost data. Data can be normalized
+with any reference, however, the normalization must be consistent across all
+energy cost data. That is, one bogo-joule/watt must be the same quantity for
+data, but we don't care what it is.
+
+A recipe for platform characterization
+=======================================
+
+Obtaining the actual model data for a particular platform requires some way of
+measuring power/energy. There isn't a tool to help with this (yet). This
+section provides a recipe for use as reference. It covers the steps used to
+characterize the ARM TC2 development platform. This sort of measurements is
+expected to be done anyway when tuning cpuidle and cpufreq for a given
+platform.
+
+The energy model needs two types of data (struct sched_group_energy holds
+these) for each sched_group where energy costs should be taken into account:
+
+1. Capacity state information
+
+A list containing the compute capacity and power consumption when fully
+utilized attributed to the group as a whole for each available capacity state.
+At the lowest level (group contains just a single cpu) this is the power of the
+cpu alone without including power consumed by resources shared with other cpus.
+It basically needs to fit the basic modelling approach described in "Background
+and Terminology" section:
+
+ energy_system = energy_shared + n * energy_cpu
+
+for a system containing 'n' busy cpus. Only 'energy_cpu' should be included at
+the lowest level. 'energy_shared' is included at the next level which
+represents the group of cpus among which the resources are shared.
+
+This model is, of course, a simplification of reality. Thus, power/energy
+attributions might not always exactly represent how the hardware is designed.
+Also, busy power is likely to depend on the workload. It is therefore
+recommended to use a representative mix of workloads when characterizing the
+capacity states.
+
+If the group has no capacity scaling support, the list will contain a single
+state where power is the busy power attributed to the group. The capacity
+should be set to a default value (1024).
+
+When frequency domains include multiple power domains, the group representing
+the frequency domain and all child groups share capacity states. This must be
+indicated by setting the SD_SHARE_CAP_STATES sched_domain flag. All groups at
+all levels that share the capacity state must have the list of capacity states
+with the power set to the contribution of the individual group.
+
+2. Idle power information
+
+Stored in the idle_states list. The power number is the group idle power
+consumption in each idle state as well when the group is idle but has not
+entered an idle-state ('active idle' as mentioned earlier). Due to the way the
+energy model is defined, the idle power of the deepest group idle state can
+alternatively be accounted for in the parent group busy power. In that case the
+group idle state power values are offset such that the idle power of the
+deepest state is zero. It is less intuitive, but it is easier to measure as
+idle power consumed by the group and the busy/idle power of the parent group
+cannot be distinguished without per group measurement points.
+
+Measuring capacity states and idle power:
+
+The capacity states' capacity and power can be estimated by running a benchmark
+workload at each available capacity state. By restricting the benchmark to run
+on subsets of cpus it is possible to extrapolate the power consumption of
+shared resources.
+
+ARM TC2 has two clusters of two and three cpus respectively. Each cluster has a
+shared L2 cache. TC2 has on-chip energy counters per cluster. Running a
+benchmark workload on just one cpu in a cluster means that power is consumed in
+the cluster (higher level group) and a single cpu (lowest level group). Adding
+another benchmark task to another cpu increases the power consumption by the
+amount consumed by the additional cpu. Hence, it is possible to extrapolate the
+cluster busy power.
+
+For platforms that don't have energy counters or equivalent instrumentation
+built-in, it may be possible to use an external DAQ to acquire similar data.
+
+If the benchmark includes some performance score (for example sysbench cpu
+benchmark), this can be used to record the compute capacity.
+
+Measuring idle power requires insight into the idle state implementation on the
+particular platform. Specifically, if the platform has coupled idle-states (or
+package states). To measure non-coupled per-cpu idle-states it is necessary to
+keep one cpu busy to keep any shared resources alive to isolate the idle power
+of the cpu from idle/busy power of the shared resources. The cpu can be tricked
+into different per-cpu idle states by disabling the other states. Based on
+various combinations of measurements with specific cpus busy and disabling
+idle-states it is possible to extrapolate the idle-state power.
diff --git a/Documentation/scheduler/sched-tune.txt b/Documentation/scheduler/sched-tune.txt
new file mode 100644
index 0000000..9bd2231
--- /dev/null
+++ b/Documentation/scheduler/sched-tune.txt
@@ -0,0 +1,366 @@
+ Central, scheduler-driven, power-performance control
+ (EXPERIMENTAL)
+
+Abstract
+========
+
+The topic of a single simple power-performance tunable, that is wholly
+scheduler centric, and has well defined and predictable properties has come up
+on several occasions in the past [1,2]. With techniques such as a scheduler
+driven DVFS [3], we now have a good framework for implementing such a tunable.
+This document describes the overall ideas behind its design and implementation.
+
+
+Table of Contents
+=================
+
+1. Motivation
+2. Introduction
+3. Signal Boosting Strategy
+4. OPP selection using boosted CPU utilization
+5. Per task group boosting
+6. Question and Answers
+ - What about "auto" mode?
+ - What about boosting on a congested system?
+ - How CPUs are boosted when we have tasks with multiple boost values?
+7. References
+
+
+1. Motivation
+=============
+
+Sched-DVFS [3] is a new event-driven cpufreq governor which allows the
+scheduler to select the optimal DVFS operating point (OPP) for running a task
+allocated to a CPU. The introduction of sched-DVFS enables running workloads at
+the most energy efficient OPPs.
+
+However, sometimes it may be desired to intentionally boost the performance of
+a workload even if that could imply a reasonable increase in energy
+consumption. For example, in order to reduce the response time of a task, we
+may want to run the task at a higher OPP than the one that is actually required
+by it's CPU bandwidth demand.
+
+This last requirement is especially important if we consider that one of the
+main goals of the sched-DVFS component is to replace all currently available
+CPUFreq policies. Since sched-DVFS is event based, as opposed to the sampling
+driven governors we currently have, it is already more responsive at selecting
+the optimal OPP to run tasks allocated to a CPU. However, just tracking the
+actual task load demand may not be enough from a performance standpoint. For
+example, it is not possible to get behaviors similar to those provided by the
+"performance" and "interactive" CPUFreq governors.
+
+This document describes an implementation of a tunable, stacked on top of the
+sched-DVFS which extends its functionality to support task performance
+boosting.
+
+By "performance boosting" we mean the reduction of the time required to
+complete a task activation, i.e. the time elapsed from a task wakeup to its
+next deactivation (e.g. because it goes back to sleep or it terminates). For
+example, if we consider a simple periodic task which executes the same workload
+for 5[s] every 20[s] while running at a certain OPP, a boosted execution of
+that task must complete each of its activations in less than 5[s].
+
+A previous attempt [5] to introduce such a boosting feature has not been
+successful mainly because of the complexity of the proposed solution. The
+approach described in this document exposes a single simple interface to
+user-space. This single tunable knob allows the tuning of system wide
+scheduler behaviours ranging from energy efficiency at one end through to
+incremental performance boosting at the other end. This first tunable affects
+all tasks. However, a more advanced extension of the concept is also provided
+which uses CGroups to boost the performance of only selected tasks while using
+the energy efficient default for all others.
+
+The rest of this document introduces in more details the proposed solution
+which has been named SchedTune.
+
+
+2. Introduction
+===============
+
+SchedTune exposes a simple user-space interface with a single power-performance
+tunable:
+
+ /proc/sys/kernel/sched_cfs_boost
+
+This permits expressing a boost value as an integer in the range [0..100].
+
+A value of 0 (default) configures the CFS scheduler for maximum energy
+efficiency. This means that sched-DVFS runs the tasks at the minimum OPP
+required to satisfy their workload demand.
+A value of 100 configures scheduler for maximum performance, which translates
+to the selection of the maximum OPP on that CPU.
+
+The range between 0 and 100 can be set to satisfy other scenarios suitably. For
+example to satisfy interactive response or depending on other system events
+(battery level etc).
+
+A CGroup based extension is also provided, which permits further user-space
+defined task classification to tune the scheduler for different goals depending
+on the specific nature of the task, e.g. background vs interactive vs
+low-priority.
+
+The overall design of the SchedTune module is built on top of "Per-Entity Load
+Tracking" (PELT) signals and sched-DVFS by introducing a bias on the Operating
+Performance Point (OPP) selection.
+Each time a task is allocated on a CPU, sched-DVFS has the opportunity to tune
+the operating frequency of that CPU to better match the workload demand. The
+selection of the actual OPP being activated is influenced by the global boost
+value, or the boost value for the task CGroup when in use.
+
+This simple biasing approach leverages existing frameworks, which means minimal
+modifications to the scheduler, and yet it allows to achieve a range of
+different behaviours all from a single simple tunable knob.
+The only new concept introduced is that of signal boosting.
+
+
+3. Signal Boosting Strategy
+===========================
+
+The whole PELT machinery works based on the value of a few load tracking signals
+which basically track the CPU bandwidth requirements for tasks and the capacity
+of CPUs. The basic idea behind the SchedTune knob is to artificially inflate
+some of these load tracking signals to make a task or RQ appears more demanding
+that it actually is.
+
+Which signals have to be inflated depends on the specific "consumer". However,
+independently from the specific (signal, consumer) pair, it is important to
+define a simple and possibly consistent strategy for the concept of boosting a
+signal.
+
+A boosting strategy defines how the "abstract" user-space defined
+sched_cfs_boost value is translated into an internal "margin" value to be added
+to a signal to get its inflated value:
+
+ margin := boosting_strategy(sched_cfs_boost, signal)
+ boosted_signal := signal + margin
+
+Different boosting strategies were identified and analyzed before selecting the
+one found to be most effective.
+
+Signal Proportional Compensation (SPC)
+--------------------------------------
+
+In this boosting strategy the sched_cfs_boost value is used to compute a
+margin which is proportional to the complement of the original signal.
+When a signal has a maximum possible value, its complement is defined as
+the delta from the actual value and its possible maximum.
+
+Since the tunable implementation uses signals which have SCHED_LOAD_SCALE as
+the maximum possible value, the margin becomes:
+
+ margin := sched_cfs_boost * (SCHED_LOAD_SCALE - signal)
+
+Using this boosting strategy:
+- a 100% sched_cfs_boost means that the signal is scaled to the maximum value
+- each value in the range of sched_cfs_boost effectively inflates the signal in
+ question by a quantity which is proportional to the maximum value.
+
+For example, by applying the SPC boosting strategy to the selection of the OPP
+to run a task it is possible to achieve these behaviors:
+
+- 0% boosting: run the task at the minimum OPP required by its workload
+- 100% boosting: run the task at the maximum OPP available for the CPU
+- 50% boosting: run at the half-way OPP between minimum and maximum
+
+Which means that, at 50% boosting, a task will be scheduled to run at half of
+the maximum theoretically achievable performance on the specific target
+platform.
+
+A graphical representation of an SPC boosted signal is represented in the
+following figure where:
+ a) "-" represents the original signal
+ b) "b" represents a 50% boosted signal
+ c) "p" represents a 100% boosted signal
+
+
+ ^
+ | SCHED_LOAD_SCALE
+ +-----------------------------------------------------------------+
+ |pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
+ |
+ | boosted_signal
+ | bbbbbbbbbbbbbbbbbbbbbbbb
+ |
+ | original signal
+ | bbbbbbbbbbbbbbbbbbbbbbbb+----------------------+
+ | |
+ |bbbbbbbbbbbbbbbbbb |
+ | |
+ | |
+ | |
+ | +-----------------------+
+ | |
+ | |
+ | |
+ |------------------+
+ |
+ |
+ +----------------------------------------------------------------------->
+
+The plot above shows a ramped load signal (titled 'original_signal') and it's
+boosted equivalent. For each step of the original signal the boosted signal
+corresponding to a 50% boost is midway from the original signal and the upper
+bound. Boosting by 100% generates a boosted signal which is always saturated to
+the upper bound.
+
+
+4. OPP selection using boosted CPU utilization
+==============================================
+
+It is worth calling out that the implementation does not introduce any new load
+signals. Instead, it provides an API to tune existing signals. This tuning is
+done on demand and only in scheduler code paths where it is sensible to do so.
+The new API calls are defined to return either the default signal or a boosted
+one, depending on the value of sched_cfs_boost. This is a clean an non invasive
+modification of the existing existing code paths.
+
+The signal representing a CPU's utilization is boosted according to the
+previously described SPC boosting strategy. To sched-DVFS, this allows a CPU
+(ie CFS run-queue) to appear more used then it actually is.
+
+Thus, with the sched_cfs_boost enabled we have the following main functions to
+get the current utilization of a CPU:
+
+ cpu_util()
+ boosted_cpu_util()
+
+The new boosted_cpu_util() is similar to the first but returns a boosted
+utilization signal which is a function of the sched_cfs_boost value.
+
+This function is used in the CFS scheduler code paths where sched-DVFS needs to
+decide the OPP to run a CPU at.
+For example, this allows selecting the highest OPP for a CPU which has
+the boost value set to 100%.
+
+
+5. Per task group boosting
+==========================
+
+The availability of a single knob which is used to boost all tasks in the
+system is certainly a simple solution but it quite likely doesn't fit many
+utilization scenarios, especially in the mobile device space.
+
+For example, on battery powered devices there usually are many background
+services which are long running and need energy efficient scheduling. On the
+other hand, some applications are more performance sensitive and require an
+interactive response and/or maximum performance, regardless of the energy cost.
+To better service such scenarios, the SchedTune implementation has an extension
+that provides a more fine grained boosting interface.
+
+A new CGroup controller, namely "schedtune", could be enabled which allows to
+defined and configure task groups with different boosting values.
+Tasks that require special performance can be put into separate CGroups.
+The value of the boost associated with the tasks in this group can be specified
+using a single knob exposed by the CGroup controller:
+
+ schedtune.boost
+
+This knob allows the definition of a boost value that is to be used for
+SPC boosting of all tasks attached to this group.
+
+The current schedtune controller implementation is really simple and has these
+main characteristics:
+
+ 1) It is only possible to create 1 level depth hierarchies
+
+ The root control groups define the system-wide boost value to be applied
+ by default to all tasks. Its direct subgroups are named "boost groups" and
+ they define the boost value for specific set of tasks.
+ Further nested subgroups are not allowed since they do not have a sensible
+ meaning from a user-space standpoint.
+
+ 2) It is possible to define only a limited number of "boost groups"
+
+ This number is defined at compile time and by default configured to 16.
+ This is a design decision motivated by two main reasons:
+ a) In a real system we do not expect utilization scenarios with more then few
+ boost groups. For example, a reasonable collection of groups could be
+ just "background", "interactive" and "performance".
+ b) It simplifies the implementation considerably, especially for the code
+ which has to compute the per CPU boosting once there are multiple
+ RUNNABLE tasks with different boost values.
+
+Such a simple design should allow servicing the main utilization scenarios identified
+so far. It provides a simple interface which can be used to manage the
+power-performance of all tasks or only selected tasks.
+Moreover, this interface can be easily integrated by user-space run-times (e.g.
+Android, ChromeOS) to implement a QoS solution for task boosting based on tasks
+classification, which has been a long standing requirement.
+
+Setup and usage
+---------------
+
+0. Use a kernel with CGROUP_SCHEDTUNE support enabled
+
+1. Check that the "schedtune" CGroup controller is available:
+
+ root@linaro-nano:~# cat /proc/cgroups
+ #subsys_name hierarchy num_cgroups enabled
+ cpuset 0 1 1
+ cpu 0 1 1
+ schedtune 0 1 1
+
+2. Mount a tmpfs to create the CGroups mount point (Optional)
+
+ root@linaro-nano:~# sudo mount -t tmpfs cgroups /sys/fs/cgroup
+
+3. Mount the "schedtune" controller
+
+ root@linaro-nano:~# mkdir /sys/fs/cgroup/stune
+ root@linaro-nano:~# sudo mount -t cgroup -o schedtune stune /sys/fs/cgroup/stune
+
+4. Setup the system-wide boost value (Optional)
+
+ If not configured the root control group has a 0% boost value, which
+ basically disables boosting for all tasks in the system thus running in
+ an energy-efficient mode.
+
+ root@linaro-nano:~# echo $SYSBOOST > /sys/fs/cgroup/stune/schedtune.boost
+
+5. Create task groups and configure their specific boost value (Optional)
+
+ For example here we create a "performance" boost group configure to boost
+ all its tasks to 100%
+
+ root@linaro-nano:~# mkdir /sys/fs/cgroup/stune/performance
+ root@linaro-nano:~# echo 100 > /sys/fs/cgroup/stune/performance/schedtune.boost
+
+6. Move tasks into the boost group
+
+ For example, the following moves the tasks with PID $TASKPID (and all its
+ threads) into the "performance" boost group.
+
+ root@linaro-nano:~# echo "TASKPID > /sys/fs/cgroup/stune/performance/cgroup.procs
+
+This simple configuration allows only the threads of the $TASKPID task to run,
+when needed, at the highest OPP in the most capable CPU of the system.
+
+
+6. Question and Answers
+=======================
+
+What about "auto" mode?
+-----------------------
+
+The 'auto' mode as described in [5] can be implemented by interfacing SchedTune
+with some suitable user-space element. This element could use the exposed
+system-wide or cgroup based interface.
+
+How are multiple groups of tasks with different boost values managed?
+---------------------------------------------------------------------
+
+The current SchedTune implementation keeps track of the boosted RUNNABLE tasks
+on a CPU. Once sched-DVFS selects the OPP to run a CPU at, the CPU utilization
+is boosted with a value which is the maximum of the boost values of the
+currently RUNNABLE tasks in its RQ.
+
+This allows sched-DVFS to boost a CPU only while there are boosted tasks ready
+to run and switch back to the energy efficient mode as soon as the last boosted
+task is dequeued.
+
+
+7. References
+=============
+[1] http://lwn.net/Articles/552889
+[2] http://lkml.org/lkml/2012/5/18/91
+[3] http://lkml.org/lkml/2015/6/26/620
diff --git a/Documentation/sphinx/rstFlatTable.py b/Documentation/sphinx/rstFlatTable.py
index 55f2757..25feb0d 100755
--- a/Documentation/sphinx/rstFlatTable.py
+++ b/Documentation/sphinx/rstFlatTable.py
@@ -157,6 +157,11 @@
def buildTableNode(self):
colwidths = self.directive.get_column_widths(self.max_cols)
+ if isinstance(colwidths, tuple):
+ # Since docutils 0.13, get_column_widths returns a (widths,
+ # colwidths) tuple, where widths is a string (i.e. 'auto').
+ # See https://sourceforge.net/p/docutils/patches/120/.
+ colwidths = colwidths[1]
stub_columns = self.directive.options.get('stub-columns', 0)
header_rows = self.directive.options.get('header-rows', 0)
diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt
index e20aacb..91723ed 100644
--- a/Documentation/trace/ftrace.txt
+++ b/Documentation/trace/ftrace.txt
@@ -362,6 +362,26 @@
to correlate events across hypervisor/guest if
tb_offset is known.
+ mono: This uses the fast monotonic clock (CLOCK_MONOTONIC)
+ which is monotonic and is subject to NTP rate adjustments.
+
+ mono_raw:
+ This is the raw monotonic clock (CLOCK_MONOTONIC_RAW)
+ which is montonic but is not subject to any rate adjustments
+ and ticks at the same rate as the hardware clocksource.
+
+ boot: This is the boot clock (CLOCK_BOOTTIME) and is based on the
+ fast monotonic clock, but also accounts for time spent in
+ suspend. Since the clock access is designed for use in
+ tracing in the suspend path, some side effects are possible
+ if clock is accessed after the suspend time is accounted before
+ the fast mono clock is updated. In this case, the clock update
+ appears to happen slightly sooner than it normally would have.
+ Also on 32-bit systems, it's possible that the 64-bit boot offset
+ sees a partial update. These effects are rare and post
+ processing should be able to handle them. See comments in the
+ ktime_get_boot_fast_ns() function for more information.
+
To set a clock, simply echo the clock name into this file.
echo global > trace_clock
diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index 6bbceb9..1f5eab4 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -2050,6 +2050,7 @@
PPC | KVM_REG_PPC_TM_VSCR | 32
PPC | KVM_REG_PPC_TM_DSCR | 64
PPC | KVM_REG_PPC_TM_TAR | 64
+ PPC | KVM_REG_PPC_TM_XER | 64
| |
MIPS | KVM_REG_MIPS_R0 | 64
...
diff --git a/Makefile b/Makefile
index 2a73c9b..18d0eaa 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 4
PATCHLEVEL = 9
-SUBLEVEL = 0
+SUBLEVEL = 9
EXTRAVERSION =
NAME = Roaring Lionus
diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c
index 940dfb4..04abdec 100644
--- a/arch/alpha/kernel/ptrace.c
+++ b/arch/alpha/kernel/ptrace.c
@@ -283,7 +283,7 @@
/* When I and D space are separate, these will need to be fixed. */
case PTRACE_PEEKTEXT: /* read word at location addr. */
case PTRACE_PEEKDATA:
- copied = access_process_vm(child, addr, &tmp, sizeof(tmp),
+ copied = ptrace_access_vm(child, addr, &tmp, sizeof(tmp),
FOLL_FORCE);
ret = -EIO;
if (copied != sizeof(tmp))
diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index bd204bf..249e101 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -28,7 +28,7 @@
select HAVE_KPROBES
select HAVE_KRETPROBES
select HAVE_MEMBLOCK
- select HAVE_MOD_ARCH_SPECIFIC if ARC_DW2_UNWIND
+ select HAVE_MOD_ARCH_SPECIFIC
select HAVE_OPROFILE
select HAVE_PERF_EVENTS
select HANDLE_DOMAIN_IRQ
diff --git a/arch/arc/include/asm/cacheflush.h b/arch/arc/include/asm/cacheflush.h
index a093adb..fc662f4 100644
--- a/arch/arc/include/asm/cacheflush.h
+++ b/arch/arc/include/asm/cacheflush.h
@@ -85,6 +85,10 @@
*/
#define PG_dc_clean PG_arch_1
+#define CACHE_COLORS_NUM 4
+#define CACHE_COLORS_MSK (CACHE_COLORS_NUM - 1)
+#define CACHE_COLOR(addr) (((unsigned long)(addr) >> (PAGE_SHIFT)) & CACHE_COLORS_MSK)
+
/*
* Simple wrapper over config option
* Bootup code ensures that hardware matches kernel configuration
@@ -94,8 +98,6 @@
return IS_ENABLED(CONFIG_ARC_CACHE_VIPT_ALIASING);
}
-#define CACHE_COLOR(addr) (((unsigned long)(addr) >> (PAGE_SHIFT)) & 1)
-
/*
* checks if two addresses (after page aligning) index into same cache set
*/
diff --git a/arch/arc/include/asm/delay.h b/arch/arc/include/asm/delay.h
index a36e860..d5da211 100644
--- a/arch/arc/include/asm/delay.h
+++ b/arch/arc/include/asm/delay.h
@@ -26,7 +26,9 @@
" lp 1f \n"
" nop \n"
"1: \n"
- : : "r"(loops));
+ :
+ : "r"(loops)
+ : "lp_count");
}
extern void __bad_udelay(void);
diff --git a/arch/arc/include/asm/module.h b/arch/arc/include/asm/module.h
index 6e91d8b..567590e 100644
--- a/arch/arc/include/asm/module.h
+++ b/arch/arc/include/asm/module.h
@@ -14,13 +14,13 @@
#include <asm-generic/module.h>
-#ifdef CONFIG_ARC_DW2_UNWIND
struct mod_arch_specific {
+#ifdef CONFIG_ARC_DW2_UNWIND
void *unw_info;
int unw_sec_idx;
+#endif
const char *secstr;
};
-#endif
#define MODULE_PROC_FAMILY "ARC700"
diff --git a/arch/arc/kernel/module.c b/arch/arc/kernel/module.c
index 42e964d..3d99a60 100644
--- a/arch/arc/kernel/module.c
+++ b/arch/arc/kernel/module.c
@@ -32,8 +32,8 @@
#ifdef CONFIG_ARC_DW2_UNWIND
mod->arch.unw_sec_idx = 0;
mod->arch.unw_info = NULL;
- mod->arch.secstr = secstr;
#endif
+ mod->arch.secstr = secstr;
return 0;
}
@@ -113,8 +113,10 @@
}
+#ifdef CONFIG_ARC_DW2_UNWIND
if (strcmp(module->arch.secstr+sechdrs[tgtsec].sh_name, ".eh_frame") == 0)
module->arch.unw_sec_idx = tgtsec;
+#endif
return 0;
diff --git a/arch/arc/kernel/unaligned.c b/arch/arc/kernel/unaligned.c
index abd961f..91ebe38 100644
--- a/arch/arc/kernel/unaligned.c
+++ b/arch/arc/kernel/unaligned.c
@@ -241,8 +241,9 @@
if (state.fault)
goto fault;
+ /* clear any remanants of delay slot */
if (delay_mode(regs)) {
- regs->ret = regs->bta;
+ regs->ret = regs->bta ~1U;
regs->status32 &= ~STATUS_DE_MASK;
} else {
regs->ret += state.instr_len;
diff --git a/arch/arc/mm/cache.c b/arch/arc/mm/cache.c
index 50d7169..8147583 100644
--- a/arch/arc/mm/cache.c
+++ b/arch/arc/mm/cache.c
@@ -979,11 +979,16 @@
/* check for D-Cache aliasing on ARCompact: ARCv2 has PIPT */
if (is_isa_arcompact()) {
int handled = IS_ENABLED(CONFIG_ARC_CACHE_VIPT_ALIASING);
+ int num_colors = dc->sz_k/dc->assoc/TO_KB(PAGE_SIZE);
- if (dc->alias && !handled)
- panic("Enable CONFIG_ARC_CACHE_VIPT_ALIASING\n");
- else if (!dc->alias && handled)
+ if (dc->alias) {
+ if (!handled)
+ panic("Enable CONFIG_ARC_CACHE_VIPT_ALIASING\n");
+ if (CACHE_COLORS_NUM != num_colors)
+ panic("CACHE_COLORS_NUM not optimized for config\n");
+ } else if (!dc->alias && handled) {
panic("Disable CONFIG_ARC_CACHE_VIPT_ALIASING\n");
+ }
}
}
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 5af3ec1..54f95d3 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -485,6 +485,7 @@
am3517-evm.dtb \
am3517_mt_ventoux.dtb \
logicpd-torpedo-37xx-devkit.dtb \
+ logicpd-som-lv-37xx-devkit.dtb \
omap3430-sdp.dtb \
omap3-beagle.dtb \
omap3-beagle-xm.dtb \
diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 194d884..795c146 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -16,6 +16,7 @@
interrupt-parent = <&intc>;
#address-cells = <1>;
#size-cells = <1>;
+ chosen { };
aliases {
i2c0 = &i2c0;
diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi
index a275fa9..a20a71d 100644
--- a/arch/arm/boot/dts/am4372.dtsi
+++ b/arch/arm/boot/dts/am4372.dtsi
@@ -16,6 +16,7 @@
interrupt-parent = <&wakeupgen>;
#address-cells = <1>;
#size-cells = <1>;
+ chosen { };
memory@0 {
device_type = "memory";
diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi
index 46d46d8..74dd21b 100644
--- a/arch/arm/boot/dts/bcm283x.dtsi
+++ b/arch/arm/boot/dts/bcm283x.dtsi
@@ -104,7 +104,7 @@
reg = <0x7e104000 0x10>;
};
- mailbox: mailbox@7e00b800 {
+ mailbox: mailbox@7e00b880 {
compatible = "brcm,bcm2835-mbox";
reg = <0x7e00b880 0x40>;
interrupts = <0 1>;
diff --git a/arch/arm/boot/dts/da850-evm.dts b/arch/arm/boot/dts/da850-evm.dts
index 41de15f..78492a0 100644
--- a/arch/arm/boot/dts/da850-evm.dts
+++ b/arch/arm/boot/dts/da850-evm.dts
@@ -99,6 +99,7 @@
#size-cells = <1>;
compatible = "m25p64";
spi-max-frequency = <30000000>;
+ m25p,fast-read;
reg = <0>;
partition@0 {
label = "U-Boot-SPL";
diff --git a/arch/arm/boot/dts/dm814x.dtsi b/arch/arm/boot/dts/dm814x.dtsi
index ff90a6c..d87efab 100644
--- a/arch/arm/boot/dts/dm814x.dtsi
+++ b/arch/arm/boot/dts/dm814x.dtsi
@@ -12,6 +12,7 @@
interrupt-parent = <&intc>;
#address-cells = <1>;
#size-cells = <1>;
+ chosen { };
aliases {
i2c0 = &i2c1;
diff --git a/arch/arm/boot/dts/dm816x.dtsi b/arch/arm/boot/dts/dm816x.dtsi
index f1e0f77..cbdfbc4 100644
--- a/arch/arm/boot/dts/dm816x.dtsi
+++ b/arch/arm/boot/dts/dm816x.dtsi
@@ -12,6 +12,7 @@
interrupt-parent = <&intc>;
#address-cells = <1>;
#size-cells = <1>;
+ chosen { };
aliases {
i2c0 = &i2c1;
diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi
index d4fcd68..064d84f 100644
--- a/arch/arm/boot/dts/dra7.dtsi
+++ b/arch/arm/boot/dts/dra7.dtsi
@@ -18,6 +18,7 @@
compatible = "ti,dra7xx";
interrupt-parent = <&crossbar_mpu>;
+ chosen { };
aliases {
i2c0 = &i2c1;
@@ -1376,6 +1377,7 @@
phy-names = "sata-phy";
clocks = <&sata_ref_clk>;
ti,hwmods = "sata";
+ ports-implemented = <0x1>;
};
rtc: rtc@48838000 {
diff --git a/arch/arm/boot/dts/imx31.dtsi b/arch/arm/boot/dts/imx31.dtsi
index 1ce7ae9..11e9e6b 100644
--- a/arch/arm/boot/dts/imx31.dtsi
+++ b/arch/arm/boot/dts/imx31.dtsi
@@ -30,11 +30,11 @@
};
};
- avic: avic-interrupt-controller@60000000 {
+ avic: interrupt-controller@68000000 {
compatible = "fsl,imx31-avic", "fsl,avic";
interrupt-controller;
#interrupt-cells = <1>;
- reg = <0x60000000 0x100000>;
+ reg = <0x68000000 0x100000>;
};
soc {
@@ -118,13 +118,6 @@
interrupts = <19>;
clocks = <&clks 25>;
};
-
- clks: ccm@53f80000{
- compatible = "fsl,imx31-ccm";
- reg = <0x53f80000 0x4000>;
- interrupts = <0 31 0x04 0 53 0x04>;
- #clock-cells = <1>;
- };
};
aips@53f00000 { /* AIPS2 */
@@ -134,6 +127,13 @@
reg = <0x53f00000 0x100000>;
ranges;
+ clks: ccm@53f80000{
+ compatible = "fsl,imx31-ccm";
+ reg = <0x53f80000 0x4000>;
+ interrupts = <31>, <53>;
+ #clock-cells = <1>;
+ };
+
gpt: timer@53f90000 {
compatible = "fsl,imx31-gpt";
reg = <0x53f90000 0x4000>;
diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts
index 59bc5a4..a150bca 100644
--- a/arch/arm/boot/dts/imx6q-cm-fx6.dts
+++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts
@@ -183,7 +183,6 @@
MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
- MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8
>;
};
diff --git a/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi b/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi
index b0b3220..01166ba 100644
--- a/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi
@@ -319,8 +319,6 @@
compatible = "fsl,imx6q-nitrogen6_max-sgtl5000",
"fsl,imx-audio-sgtl5000";
model = "imx6q-nitrogen6_max-sgtl5000";
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_sgtl5000>;
ssi-controller = <&ssi1>;
audio-codec = <&codec>;
audio-routing =
@@ -402,6 +400,8 @@
codec: sgtl5000@0a {
compatible = "fsl,sgtl5000";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sgtl5000>;
reg = <0x0a>;
clocks = <&clks IMX6QDL_CLK_CKO>;
VDDA-supply = <®_2p5v>;
diff --git a/arch/arm/boot/dts/logicpd-som-lv-37xx-devkit.dts b/arch/arm/boot/dts/logicpd-som-lv-37xx-devkit.dts
index da85984..38faa90 100644
--- a/arch/arm/boot/dts/logicpd-som-lv-37xx-devkit.dts
+++ b/arch/arm/boot/dts/logicpd-som-lv-37xx-devkit.dts
@@ -158,7 +158,7 @@
&mmc1 {
interrupts-extended = <&intc 83 &omap3_pmx_core 0x11a>;
pinctrl-names = "default";
- pinctrl-0 = <&mmc1_pins &mmc1_cd>;
+ pinctrl-0 = <&mmc1_pins>;
wp-gpios = <&gpio4 30 GPIO_ACTIVE_HIGH>; /* gpio_126 */
cd-gpios = <&gpio4 14 IRQ_TYPE_LEVEL_LOW>; /* gpio_110 */
vmmc-supply = <&vmmc1>;
@@ -193,7 +193,8 @@
OMAP3_CORE1_IOPAD(0x214a, PIN_INPUT | MUX_MODE0) /* sdmmc1_dat1.sdmmc1_dat1 */
OMAP3_CORE1_IOPAD(0x214c, PIN_INPUT | MUX_MODE0) /* sdmmc1_dat2.sdmmc1_dat2 */
OMAP3_CORE1_IOPAD(0x214e, PIN_INPUT | MUX_MODE0) /* sdmmc1_dat3.sdmmc1_dat3 */
- OMAP3_CORE1_IOPAD(0x2132, PIN_INPUT_PULLUP | MUX_MODE4) /* cam_strobe.gpio_126 sdmmc1_wp*/
+ OMAP3_CORE1_IOPAD(0x2132, PIN_INPUT_PULLUP | MUX_MODE4) /* cam_strobe.gpio_126 */
+ OMAP3_CORE1_IOPAD(0x212c, PIN_INPUT_PULLUP | MUX_MODE4) /* cam_d11.gpio_110 */
>;
};
@@ -242,12 +243,6 @@
OMAP3_WKUP_IOPAD(0x2a16, PIN_OUTPUT | PIN_OFF_OUTPUT_LOW | MUX_MODE4) /* sys_boot6.gpio_8 */
>;
};
-
- mmc1_cd: pinmux_mmc1_cd {
- pinctrl-single,pins = <
- OMAP3_WKUP_IOPAD(0x212c, PIN_INPUT_PULLUP | MUX_MODE4) /* cam_d11.gpio_110 */
- >;
- };
};
diff --git a/arch/arm/boot/dts/omap2.dtsi b/arch/arm/boot/dts/omap2.dtsi
index 4f793a0..f1d6de8 100644
--- a/arch/arm/boot/dts/omap2.dtsi
+++ b/arch/arm/boot/dts/omap2.dtsi
@@ -17,6 +17,7 @@
interrupt-parent = <&intc>;
#address-cells = <1>;
#size-cells = <1>;
+ chosen { };
aliases {
serial0 = &uart1;
diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi
index 353d818..2008648 100644
--- a/arch/arm/boot/dts/omap3.dtsi
+++ b/arch/arm/boot/dts/omap3.dtsi
@@ -17,6 +17,7 @@
interrupt-parent = <&intc>;
#address-cells = <1>;
#size-cells = <1>;
+ chosen { };
aliases {
i2c0 = &i2c1;
diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index 0ced079..9c289dd 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -15,6 +15,7 @@
interrupt-parent = <&wakeupgen>;
#address-cells = <1>;
#size-cells = <1>;
+ chosen { };
aliases {
i2c0 = &i2c1;
diff --git a/arch/arm/boot/dts/omap5.dtsi b/arch/arm/boot/dts/omap5.dtsi
index 2526211..1d1d8e9 100644
--- a/arch/arm/boot/dts/omap5.dtsi
+++ b/arch/arm/boot/dts/omap5.dtsi
@@ -17,6 +17,7 @@
compatible = "ti,omap5";
interrupt-parent = <&wakeupgen>;
+ chosen { };
aliases {
i2c0 = &i2c1;
@@ -985,6 +986,7 @@
phy-names = "sata-phy";
clocks = <&sata_ref_clk>;
ti,hwmods = "sata";
+ ports-implemented = <0x1>;
};
dss: dss@58000000 {
diff --git a/arch/arm/boot/dts/r8a7794.dtsi b/arch/arm/boot/dts/r8a7794.dtsi
index 9365580..7e860d3 100644
--- a/arch/arm/boot/dts/r8a7794.dtsi
+++ b/arch/arm/boot/dts/r8a7794.dtsi
@@ -319,7 +319,7 @@
"ch12";
clocks = <&mstp5_clks R8A7794_CLK_AUDIO_DMAC0>;
clock-names = "fck";
- power-domains = <&cpg_clocks>;
+ power-domains = <&sysc R8A7794_PD_ALWAYS_ON>;
#dma-cells = <1>;
dma-channels = <13>;
};
@@ -1025,8 +1025,7 @@
clocks = <&extal_clk &usb_extal_clk>;
#clock-cells = <1>;
clock-output-names = "main", "pll0", "pll1", "pll3",
- "lb", "qspi", "sdh", "sd0", "z",
- "rcan";
+ "lb", "qspi", "sdh", "sd0", "rcan";
#power-domain-cells = <0>;
};
/* Variable factor clocks */
@@ -1260,7 +1259,7 @@
mstp7_clks: mstp7_clks@e615014c {
compatible = "renesas,r8a7794-mstp-clocks", "renesas,cpg-mstp-clocks";
reg = <0 0xe615014c 0 4>, <0 0xe61501c4 0 4>;
- clocks = <&mp_clk>, <&mp_clk>,
+ clocks = <&mp_clk>, <&hp_clk>,
<&zs_clk>, <&p_clk>, <&p_clk>, <&zs_clk>,
<&zs_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>,
<&zx_clk>;
@@ -1483,7 +1482,7 @@
"mix.0", "mix.1",
"dvc.0", "dvc.1",
"clk_a", "clk_b", "clk_c", "clk_i";
- power-domains = <&cpg_clocks>;
+ power-domains = <&sysc R8A7794_PD_ALWAYS_ON>;
status = "disabled";
diff --git a/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts b/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
index ba5bca0..44377a9 100644
--- a/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
+++ b/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
@@ -227,3 +227,8 @@
pinctrl-0 = <&uart0_pins_a>;
status = "okay";
};
+
+&usbphy {
+ /* VBUS on usb host ports are tied to DC5V and therefore always on */
+ status = "okay";
+};
diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig
index c2dff4f..9f6d2a6 100644
--- a/arch/arm/configs/qcom_defconfig
+++ b/arch/arm/configs/qcom_defconfig
@@ -162,8 +162,8 @@
CONFIG_IPQ_LCC_806X=y
CONFIG_MSM_GCC_8660=y
CONFIG_MSM_LCC_8960=y
-CONFIG_MSM_GCC_9615=y
-CONFIG_MSM_LCC_9615=y
+CONFIG_MDM_GCC_9615=y
+CONFIG_MDM_LCC_9615=y
CONFIG_MSM_MMCC_8960=y
CONFIG_MSM_MMCC_8974=y
CONFIG_HWSPINLOCK_QCOM=y
diff --git a/arch/arm/configs/ranchu_defconfig b/arch/arm/configs/ranchu_defconfig
new file mode 100644
index 0000000..49e7bbd
--- /dev/null
+++ b/arch/arm/configs/ranchu_defconfig
@@ -0,0 +1,316 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_AUDIT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_ARCH_MMAP_RND_BITS=16
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_VIRT=y
+CONFIG_ARM_KERNMEM_PERMS=y
+CONFIG_SMP=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_KSM=y
+CONFIG_SECCOMP=y
+CONFIG_CMDLINE="console=ttyAMA0"
+CONFIG_VFP=y
+CONFIG_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
+CONFIG_PM_DEBUG=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_INET_DIAG_DESTROY=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_LRO is not set
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_SECURITY=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_BRIDGE=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_CLS_U32=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_CLS_ACT=y
+# CONFIG_WIRELESS is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_VIRTIO_BLK=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
+CONFIG_NETDEVICES=y
+CONFIG_TUN=y
+CONFIG_VIRTIO_NET=y
+CONFIG_SMSC911X=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_USB_USBNET=y
+# CONFIG_WLAN is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+CONFIG_KEYBOARD_GOLDFISH_EVENTS=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_XPAD=y
+CONFIG_JOYSTICK_XPAD_FF=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
+CONFIG_INPUT_TABLET=y
+CONFIG_TABLET_USB_ACECAD=y
+CONFIG_TABLET_USB_AIPTEK=y
+CONFIG_TABLET_USB_GTCO=y
+CONFIG_TABLET_USB_HANWANG=y
+CONFIG_TABLET_USB_KBTAB=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+# CONFIG_SERIO_SERPORT is not set
+CONFIG_SERIO_AMBAKMI=y
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_VIRTIO_CONSOLE=y
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HWMON is not set
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_FB=y
+CONFIG_FB_GOLDFISH=y
+CONFIG_FB_SIMPLE=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_HIDRAW=y
+CONFIG_UHID=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_ACRUX=y
+CONFIG_HID_ACRUX_FF=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_PRODIKEYS=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_DRAGONRISE=y
+CONFIG_DRAGONRISE_FF=y
+CONFIG_HID_EMS_FF=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_HOLTEK=y
+CONFIG_HID_KEYTOUCH=y
+CONFIG_HID_KYE=y
+CONFIG_HID_UCLOGIC=y
+CONFIG_HID_WALTOP=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_TWINHAN=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LCPOWER=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_LOGITECH_DJ=y
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGIG940_FF=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_HID_NTRIG=y
+CONFIG_HID_ORTEK=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_PANTHERLORD_FF=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_PICOLCD=y
+CONFIG_HID_PRIMAX=y
+CONFIG_HID_ROCCAT=y
+CONFIG_HID_SAITEK=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SPEEDLINK=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_HID_GREENASIA=y
+CONFIG_GREENASIA_FF=y
+CONFIG_HID_SMARTJOYPLUS=y
+CONFIG_SMARTJOYPLUS_FF=y
+CONFIG_HID_TIVO=y
+CONFIG_HID_TOPSEED=y
+CONFIG_HID_THRUSTMASTER=y
+CONFIG_HID_WACOM=y
+CONFIG_HID_WIIMOTE=y
+CONFIG_HID_ZEROPLUS=y
+CONFIG_HID_ZYDACRON=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_OTG_WAKELOCK=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_PL031=y
+CONFIG_VIRTIO_MMIO=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_SYNC=y
+CONFIG_SW_SYNC=y
+CONFIG_SW_SYNC_USER=y
+CONFIG_ION=y
+CONFIG_GOLDFISH_AUDIO=y
+CONFIG_GOLDFISH=y
+CONFIG_GOLDFISH_PIPE=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_QUOTA=y
+CONFIG_FUSE_FS=y
+CONFIG_CUSE=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_PSTORE=y
+CONFIG_PSTORE_CONSOLE=y
+CONFIG_PSTORE_RAM=y
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_DEBUG_INFO=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_PANIC_TIMEOUT=5
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_ENABLE_DEFAULT_TRACERS=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_SELINUX=y
+CONFIG_VIRTUALIZATION=y
diff --git a/arch/arm/crypto/aes-ce-glue.c b/arch/arm/crypto/aes-ce-glue.c
index aef022a..04410d9 100644
--- a/arch/arm/crypto/aes-ce-glue.c
+++ b/arch/arm/crypto/aes-ce-glue.c
@@ -88,8 +88,13 @@
u32 *rki = ctx->key_enc + (i * kwords);
u32 *rko = rki + kwords;
+#ifndef CONFIG_CPU_BIG_ENDIAN
rko[0] = ror32(ce_aes_sub(rki[kwords - 1]), 8);
rko[0] = rko[0] ^ rki[0] ^ rcon[i];
+#else
+ rko[0] = rol32(ce_aes_sub(rki[kwords - 1]), 8);
+ rko[0] = rko[0] ^ rki[0] ^ (rcon[i] << 24);
+#endif
rko[1] = rko[0] ^ rki[1];
rko[2] = rko[1] ^ rki[2];
rko[3] = rko[2] ^ rki[3];
diff --git a/arch/arm/include/asm/cputype.h b/arch/arm/include/asm/cputype.h
index 522b5fe..b62eaeb 100644
--- a/arch/arm/include/asm/cputype.h
+++ b/arch/arm/include/asm/cputype.h
@@ -94,6 +94,9 @@
#define ARM_CPU_XSCALE_ARCH_V2 0x4000
#define ARM_CPU_XSCALE_ARCH_V3 0x6000
+/* Qualcomm implemented cores */
+#define ARM_CPU_PART_SCORPION 0x510002d0
+
extern unsigned int processor_id;
#ifdef CONFIG_CPU_CP15
diff --git a/arch/arm/include/asm/topology.h b/arch/arm/include/asm/topology.h
index 370f7a7..d060641 100644
--- a/arch/arm/include/asm/topology.h
+++ b/arch/arm/include/asm/topology.h
@@ -3,6 +3,7 @@
#ifdef CONFIG_ARM_CPU_TOPOLOGY
+#include <linux/cpufreq.h>
#include <linux/cpumask.h>
struct cputopo_arm {
@@ -24,6 +25,12 @@
void store_cpu_topology(unsigned int cpuid);
const struct cpumask *cpu_coregroup_mask(int cpu);
+#ifdef CONFIG_CPU_FREQ
+#define arch_scale_freq_capacity cpufreq_scale_freq_capacity
+#endif
+#define arch_scale_cpu_capacity scale_cpu_capacity
+extern unsigned long scale_cpu_capacity(struct sched_domain *sd, int cpu);
+
#else
static inline void init_cpu_topology(void) { }
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
index b8df458..25538a9 100644
--- a/arch/arm/kernel/hw_breakpoint.c
+++ b/arch/arm/kernel/hw_breakpoint.c
@@ -1066,6 +1066,22 @@
return 0;
}
+ /*
+ * Scorpion CPUs (at least those in APQ8060) seem to set DBGPRSR.SPD
+ * whenever a WFI is issued, even if the core is not powered down, in
+ * violation of the architecture. When DBGPRSR.SPD is set, accesses to
+ * breakpoint and watchpoint registers are treated as undefined, so
+ * this results in boot time and runtime failures when these are
+ * accessed and we unexpectedly take a trap.
+ *
+ * It's not clear if/how this can be worked around, so we blacklist
+ * Scorpion CPUs to avoid these issues.
+ */
+ if (read_cpuid_part() == ARM_CPU_PART_SCORPION) {
+ pr_info("Scorpion CPU detected. Hardware breakpoints and watchpoints disabled\n");
+ return 0;
+ }
+
has_ossr = core_has_os_save_restore();
/* Determine how many BRPs/WRPs are available. */
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index b1a3599..bbf60e3 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -466,6 +466,17 @@
}
static void (*__smp_cross_call)(const struct cpumask *, unsigned int);
+DEFINE_PER_CPU(bool, pending_ipi);
+static void smp_cross_call_common(const struct cpumask *cpumask,
+ unsigned int func)
+{
+ unsigned int cpu;
+
+ for_each_cpu(cpu, cpumask)
+ per_cpu(pending_ipi, cpu) = true;
+
+ __smp_cross_call(cpumask, func);
+}
void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int))
{
@@ -518,31 +529,32 @@
void arch_send_call_function_ipi_mask(const struct cpumask *mask)
{
- smp_cross_call(mask, IPI_CALL_FUNC);
+ smp_cross_call_common(mask, IPI_CALL_FUNC);
}
void arch_send_wakeup_ipi_mask(const struct cpumask *mask)
{
- smp_cross_call(mask, IPI_WAKEUP);
+ smp_cross_call_common(mask, IPI_WAKEUP);
}
void arch_send_call_function_single_ipi(int cpu)
{
- smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC);
+ smp_cross_call_common(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE);
}
#ifdef CONFIG_IRQ_WORK
void arch_irq_work_raise(void)
{
if (arch_irq_work_has_interrupt())
- smp_cross_call(cpumask_of(smp_processor_id()), IPI_IRQ_WORK);
+ smp_cross_call_common(cpumask_of(smp_processor_id()),
+ IPI_IRQ_WORK);
}
#endif
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
void tick_broadcast(const struct cpumask *mask)
{
- smp_cross_call(mask, IPI_TIMER);
+ smp_cross_call_common(mask, IPI_TIMER);
}
#endif
@@ -664,7 +676,7 @@
void smp_send_reschedule(int cpu)
{
- smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE);
+ smp_cross_call_common(cpumask_of(cpu), IPI_RESCHEDULE);
}
void smp_send_stop(void)
@@ -675,7 +687,7 @@
cpumask_copy(&mask, cpu_online_mask);
cpumask_clear_cpu(smp_processor_id(), &mask);
if (!cpumask_empty(&mask))
- smp_cross_call(&mask, IPI_CPU_STOP);
+ smp_cross_call_common(&mask, IPI_CPU_STOP);
/* Wait up to one second for other CPUs to stop */
timeout = USEC_PER_SEC;
@@ -748,7 +760,16 @@
static void raise_nmi(cpumask_t *mask)
{
- smp_cross_call(mask, IPI_CPU_BACKTRACE);
+ /*
+ * Generate the backtrace directly if we are running in a calling
+ * context that is not preemptible by the backtrace IPI. Note
+ * that nmi_cpu_backtrace() automatically removes the current cpu
+ * from mask.
+ */
+ if (cpumask_test_cpu(smp_processor_id(), mask) && irqs_disabled())
+ nmi_cpu_backtrace(NULL);
+
+ smp_cross_call_common(mask, IPI_CPU_BACKTRACE);
}
void arch_trigger_cpumask_backtrace(const cpumask_t *mask, bool exclude_self)
diff --git a/arch/arm/kernel/smp_tlb.c b/arch/arm/kernel/smp_tlb.c
index 22313cb..9af0701 100644
--- a/arch/arm/kernel/smp_tlb.c
+++ b/arch/arm/kernel/smp_tlb.c
@@ -9,6 +9,7 @@
*/
#include <linux/preempt.h>
#include <linux/smp.h>
+#include <linux/uaccess.h>
#include <asm/smp_plat.h>
#include <asm/tlbflush.h>
@@ -40,8 +41,11 @@
static inline void ipi_flush_tlb_page(void *arg)
{
struct tlb_args *ta = (struct tlb_args *)arg;
+ unsigned int __ua_flags = uaccess_save_and_enable();
local_flush_tlb_page(ta->ta_vma, ta->ta_start);
+
+ uaccess_restore(__ua_flags);
}
static inline void ipi_flush_tlb_kernel_page(void *arg)
@@ -54,8 +58,11 @@
static inline void ipi_flush_tlb_range(void *arg)
{
struct tlb_args *ta = (struct tlb_args *)arg;
+ unsigned int __ua_flags = uaccess_save_and_enable();
local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end);
+
+ uaccess_restore(__ua_flags);
}
static inline void ipi_flush_tlb_kernel_range(void *arg)
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index c16e7d6..792340f 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -42,9 +42,15 @@
*/
static DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;
-unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu)
+unsigned long scale_cpu_capacity(struct sched_domain *sd, int cpu)
{
+#ifdef CONFIG_CPU_FREQ
+ unsigned long max_freq_scale = cpufreq_scale_max_freq_capacity(cpu);
+
+ return per_cpu(cpu_scale, cpu) * max_freq_scale >> SCHED_CAPACITY_SHIFT;
+#else
return per_cpu(cpu_scale, cpu);
+#endif
}
static void set_capacity_scale(unsigned int cpu, unsigned long capacity)
@@ -153,6 +159,8 @@
}
+static const struct sched_group_energy * const cpu_core_energy(int cpu);
+
/*
* Look for a customed capacity of a CPU in the cpu_capacity table during the
* boot. The update of all CPUs is in O(n^2) for heteregeneous system but the
@@ -160,10 +168,14 @@
*/
static void update_cpu_capacity(unsigned int cpu)
{
- if (!cpu_capacity(cpu))
- return;
+ unsigned long capacity = SCHED_CAPACITY_SCALE;
- set_capacity_scale(cpu, cpu_capacity(cpu) / middle_capacity);
+ if (cpu_core_energy(cpu)) {
+ int max_cap_idx = cpu_core_energy(cpu)->nr_cap_states - 1;
+ capacity = cpu_core_energy(cpu)->cap_states[max_cap_idx].cap;
+ }
+
+ set_capacity_scale(cpu, capacity);
pr_info("CPU%u: update cpu_capacity %lu\n",
cpu, arch_scale_cpu_capacity(NULL, cpu));
@@ -275,17 +287,138 @@
cpu_topology[cpuid].socket_id, mpidr);
}
+/*
+ * ARM TC2 specific energy cost model data. There are no unit requirements for
+ * the data. Data can be normalized to any reference point, but the
+ * normalization must be consistent. That is, one bogo-joule/watt must be the
+ * same quantity for all data, but we don't care what it is.
+ */
+static struct idle_state idle_states_cluster_a7[] = {
+ { .power = 25 }, /* arch_cpu_idle() (active idle) = WFI */
+ { .power = 25 }, /* WFI */
+ { .power = 10 }, /* cluster-sleep-l */
+ };
+
+static struct idle_state idle_states_cluster_a15[] = {
+ { .power = 70 }, /* arch_cpu_idle() (active idle) = WFI */
+ { .power = 70 }, /* WFI */
+ { .power = 25 }, /* cluster-sleep-b */
+ };
+
+static struct capacity_state cap_states_cluster_a7[] = {
+ /* Cluster only power */
+ { .cap = 150, .power = 2967, }, /* 350 MHz */
+ { .cap = 172, .power = 2792, }, /* 400 MHz */
+ { .cap = 215, .power = 2810, }, /* 500 MHz */
+ { .cap = 258, .power = 2815, }, /* 600 MHz */
+ { .cap = 301, .power = 2919, }, /* 700 MHz */
+ { .cap = 344, .power = 2847, }, /* 800 MHz */
+ { .cap = 387, .power = 3917, }, /* 900 MHz */
+ { .cap = 430, .power = 4905, }, /* 1000 MHz */
+ };
+
+static struct capacity_state cap_states_cluster_a15[] = {
+ /* Cluster only power */
+ { .cap = 426, .power = 7920, }, /* 500 MHz */
+ { .cap = 512, .power = 8165, }, /* 600 MHz */
+ { .cap = 597, .power = 8172, }, /* 700 MHz */
+ { .cap = 682, .power = 8195, }, /* 800 MHz */
+ { .cap = 768, .power = 8265, }, /* 900 MHz */
+ { .cap = 853, .power = 8446, }, /* 1000 MHz */
+ { .cap = 938, .power = 11426, }, /* 1100 MHz */
+ { .cap = 1024, .power = 15200, }, /* 1200 MHz */
+ };
+
+static struct sched_group_energy energy_cluster_a7 = {
+ .nr_idle_states = ARRAY_SIZE(idle_states_cluster_a7),
+ .idle_states = idle_states_cluster_a7,
+ .nr_cap_states = ARRAY_SIZE(cap_states_cluster_a7),
+ .cap_states = cap_states_cluster_a7,
+};
+
+static struct sched_group_energy energy_cluster_a15 = {
+ .nr_idle_states = ARRAY_SIZE(idle_states_cluster_a15),
+ .idle_states = idle_states_cluster_a15,
+ .nr_cap_states = ARRAY_SIZE(cap_states_cluster_a15),
+ .cap_states = cap_states_cluster_a15,
+};
+
+static struct idle_state idle_states_core_a7[] = {
+ { .power = 0 }, /* arch_cpu_idle (active idle) = WFI */
+ { .power = 0 }, /* WFI */
+ { .power = 0 }, /* cluster-sleep-l */
+ };
+
+static struct idle_state idle_states_core_a15[] = {
+ { .power = 0 }, /* arch_cpu_idle (active idle) = WFI */
+ { .power = 0 }, /* WFI */
+ { .power = 0 }, /* cluster-sleep-b */
+ };
+
+static struct capacity_state cap_states_core_a7[] = {
+ /* Power per cpu */
+ { .cap = 150, .power = 187, }, /* 350 MHz */
+ { .cap = 172, .power = 275, }, /* 400 MHz */
+ { .cap = 215, .power = 334, }, /* 500 MHz */
+ { .cap = 258, .power = 407, }, /* 600 MHz */
+ { .cap = 301, .power = 447, }, /* 700 MHz */
+ { .cap = 344, .power = 549, }, /* 800 MHz */
+ { .cap = 387, .power = 761, }, /* 900 MHz */
+ { .cap = 430, .power = 1024, }, /* 1000 MHz */
+ };
+
+static struct capacity_state cap_states_core_a15[] = {
+ /* Power per cpu */
+ { .cap = 426, .power = 2021, }, /* 500 MHz */
+ { .cap = 512, .power = 2312, }, /* 600 MHz */
+ { .cap = 597, .power = 2756, }, /* 700 MHz */
+ { .cap = 682, .power = 3125, }, /* 800 MHz */
+ { .cap = 768, .power = 3524, }, /* 900 MHz */
+ { .cap = 853, .power = 3846, }, /* 1000 MHz */
+ { .cap = 938, .power = 5177, }, /* 1100 MHz */
+ { .cap = 1024, .power = 6997, }, /* 1200 MHz */
+ };
+
+static struct sched_group_energy energy_core_a7 = {
+ .nr_idle_states = ARRAY_SIZE(idle_states_core_a7),
+ .idle_states = idle_states_core_a7,
+ .nr_cap_states = ARRAY_SIZE(cap_states_core_a7),
+ .cap_states = cap_states_core_a7,
+};
+
+static struct sched_group_energy energy_core_a15 = {
+ .nr_idle_states = ARRAY_SIZE(idle_states_core_a15),
+ .idle_states = idle_states_core_a15,
+ .nr_cap_states = ARRAY_SIZE(cap_states_core_a15),
+ .cap_states = cap_states_core_a15,
+};
+
+/* sd energy functions */
+static inline
+const struct sched_group_energy * const cpu_cluster_energy(int cpu)
+{
+ return cpu_topology[cpu].socket_id ? &energy_cluster_a7 :
+ &energy_cluster_a15;
+}
+
+static inline
+const struct sched_group_energy * const cpu_core_energy(int cpu)
+{
+ return cpu_topology[cpu].socket_id ? &energy_core_a7 :
+ &energy_core_a15;
+}
+
static inline int cpu_corepower_flags(void)
{
- return SD_SHARE_PKG_RESOURCES | SD_SHARE_POWERDOMAIN;
+ return SD_SHARE_PKG_RESOURCES | SD_SHARE_POWERDOMAIN | \
+ SD_SHARE_CAP_STATES;
}
static struct sched_domain_topology_level arm_topology[] = {
#ifdef CONFIG_SCHED_MC
- { cpu_corepower_mask, cpu_corepower_flags, SD_INIT_NAME(GMC) },
- { cpu_coregroup_mask, cpu_core_flags, SD_INIT_NAME(MC) },
+ { cpu_coregroup_mask, cpu_corepower_flags, cpu_core_energy, SD_INIT_NAME(MC) },
#endif
- { cpu_cpu_mask, SD_INIT_NAME(DIE) },
+ { cpu_cpu_mask, NULL, cpu_cluster_energy, SD_INIT_NAME(DIE) },
{ NULL, },
};
diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c
index ed3d0e9..d7a43af 100644
--- a/arch/arm/mach-davinci/da850.c
+++ b/arch/arm/mach-davinci/da850.c
@@ -319,6 +319,16 @@
.gpsc = 1,
};
+/*
+ * In order to avoid adding the emac_clk to the clock lookup table twice (and
+ * screwing up the linked list in the process) create a separate clock for
+ * mdio inheriting the rate from emac_clk.
+ */
+static struct clk mdio_clk = {
+ .name = "mdio",
+ .parent = &emac_clk,
+};
+
static struct clk mcasp_clk = {
.name = "mcasp",
.parent = &async3_clk,
@@ -494,7 +504,7 @@
CLK(NULL, "arm", &arm_clk),
CLK(NULL, "rmii", &rmii_clk),
CLK("davinci_emac.1", NULL, &emac_clk),
- CLK("davinci_mdio.0", "fck", &emac_clk),
+ CLK("davinci_mdio.0", "fck", &mdio_clk),
CLK("davinci-mcasp.0", NULL, &mcasp_clk),
CLK("davinci-mcbsp.0", NULL, &mcbsp0_clk),
CLK("davinci-mcbsp.1", NULL, &mcbsp1_clk),
diff --git a/arch/arm/mach-highbank/pm.c b/arch/arm/mach-highbank/pm.c
index 4003116..165e44e 100644
--- a/arch/arm/mach-highbank/pm.c
+++ b/arch/arm/mach-highbank/pm.c
@@ -36,11 +36,11 @@
static int highbank_pm_enter(suspend_state_t state)
{
cpu_pm_enter();
- cpu_cluster_pm_enter();
+ cpu_cluster_pm_enter(0);
cpu_suspend(0, highbank_suspend_finish);
- cpu_cluster_pm_exit();
+ cpu_cluster_pm_exit(0);
cpu_pm_exit();
return 0;
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 5b37ec2..e37ceb8 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -80,7 +80,7 @@
# Power Management
omap-4-5-pm-common = omap-mpuss-lowpower.o
obj-$(CONFIG_ARCH_OMAP4) += $(omap-4-5-pm-common)
-obj-$(CONFIG_ARCH_OMAP5) += $(omap-4-5-pm-common)
+obj-$(CONFIG_SOC_OMAP5) += $(omap-4-5-pm-common)
obj-$(CONFIG_OMAP_PM_NOOP) += omap-pm-noop.o
ifeq ($(CONFIG_PM),y)
diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c
index bab814d..60f5e83 100644
--- a/arch/arm/mach-omap2/board-generic.c
+++ b/arch/arm/mach-omap2/board-generic.c
@@ -306,7 +306,7 @@
.init_late = am43xx_init_late,
.init_irq = omap_gic_of_init,
.init_machine = omap_generic_init,
- .init_time = omap4_local_timer_init,
+ .init_time = omap3_gptimer_timer_init,
.dt_compat = am43_boards_compat,
.restart = omap44xx_restart,
MACHINE_END
diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h
index deed42e..6dcca29 100644
--- a/arch/arm/mach-omap2/common.h
+++ b/arch/arm/mach-omap2/common.h
@@ -262,8 +262,6 @@
extern void omap4_mpuss_early_init(void);
extern void omap_do_wfi(void);
-extern void omap4_secondary_startup(void);
-extern void omap4460_secondary_startup(void);
#ifdef CONFIG_SMP
/* Needed for secondary core boot */
@@ -275,16 +273,11 @@
extern int omap4_cpu_kill(unsigned int cpu);
extern const struct smp_operations omap4_smp_ops;
-
-extern void omap5_secondary_startup(void);
-extern void omap5_secondary_hyp_startup(void);
#endif
#if defined(CONFIG_SMP) && defined(CONFIG_PM)
extern int omap4_mpuss_init(void);
extern int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state);
-extern int omap4_finish_suspend(unsigned long cpu_state);
-extern void omap4_cpu_resume(void);
extern int omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state);
#else
static inline int omap4_enter_lowpower(unsigned int cpu,
@@ -305,14 +298,41 @@
return 0;
}
+#endif
+
+#ifdef CONFIG_ARCH_OMAP4
+void omap4_secondary_startup(void);
+void omap4460_secondary_startup(void);
+int omap4_finish_suspend(unsigned long cpu_state);
+void omap4_cpu_resume(void);
+#else
+static inline void omap4_secondary_startup(void)
+{
+}
+
+static inline void omap4460_secondary_startup(void)
+{
+}
static inline int omap4_finish_suspend(unsigned long cpu_state)
{
return 0;
}
-
static inline void omap4_cpu_resume(void)
-{}
+{
+}
+#endif
+#if defined(CONFIG_SOC_OMAP5) || defined(CONFIG_SOC_DRA7XX)
+void omap5_secondary_startup(void);
+void omap5_secondary_hyp_startup(void);
+#else
+static inline void omap5_secondary_startup(void)
+{
+}
+
+static inline void omap5_secondary_hyp_startup(void)
+{
+}
#endif
void pdata_quirks_init(const struct of_device_id *);
diff --git a/arch/arm/mach-omap2/cpuidle44xx.c b/arch/arm/mach-omap2/cpuidle44xx.c
index fa138d4..c11ecd9 100644
--- a/arch/arm/mach-omap2/cpuidle44xx.c
+++ b/arch/arm/mach-omap2/cpuidle44xx.c
@@ -127,7 +127,7 @@
* to save GIC and wakeupgen context.
*/
if (mpuss_can_lose_context)
- cpu_cluster_pm_enter();
+ cpu_cluster_pm_enter(0);
}
omap4_enter_lowpower(dev->cpu, cx->cpu_state);
@@ -165,7 +165,7 @@
* to restore GIC and wakeupgen context.
*/
if (dev->cpu == 0 && mpuss_can_lose_context)
- cpu_cluster_pm_exit();
+ cpu_cluster_pm_exit(0);
tick_broadcast_exit();
diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c
index 0e9acdd..f0da525 100644
--- a/arch/arm/mach-omap2/io.c
+++ b/arch/arm/mach-omap2/io.c
@@ -717,10 +717,11 @@
OMAP2_L4_IO_ADDRESS(OMAP54XX_SCM_BASE));
omap2_set_globals_prcm_mpu(OMAP2_L4_IO_ADDRESS(OMAP54XX_PRCM_MPU_BASE));
omap2_control_base_init();
- omap4_pm_init_early();
omap2_prcm_base_init();
omap5xxx_check_revision();
omap4_sar_ram_init();
+ omap4_mpuss_early_init();
+ omap4_pm_init_early();
omap54xx_voltagedomains_init();
omap54xx_powerdomains_init();
omap54xx_clockdomains_init();
diff --git a/arch/arm/mach-omap2/omap-mpuss-lowpower.c b/arch/arm/mach-omap2/omap-mpuss-lowpower.c
index ad98246..7d62ad4 100644
--- a/arch/arm/mach-omap2/omap-mpuss-lowpower.c
+++ b/arch/arm/mach-omap2/omap-mpuss-lowpower.c
@@ -48,6 +48,7 @@
#include <asm/smp_scu.h>
#include <asm/pgalloc.h>
#include <asm/suspend.h>
+#include <asm/virt.h>
#include <asm/hardware/cache-l2x0.h>
#include "soc.h"
@@ -244,10 +245,9 @@
save_state = 1;
break;
case PWRDM_POWER_RET:
- if (IS_PM44XX_ERRATUM(PM_OMAP4_CPU_OSWR_DISABLE)) {
+ if (IS_PM44XX_ERRATUM(PM_OMAP4_CPU_OSWR_DISABLE))
save_state = 0;
- break;
- }
+ break;
default:
/*
* CPUx CSWR is invalid hardware state. Also CPUx OSWR
@@ -371,8 +371,12 @@
pm_info = &per_cpu(omap4_pm_info, 0x0);
if (sar_base) {
pm_info->scu_sar_addr = sar_base + SCU_OFFSET0;
- pm_info->wkup_sar_addr = sar_base +
- CPU0_WAKEUP_NS_PA_ADDR_OFFSET;
+ if (cpu_is_omap44xx())
+ pm_info->wkup_sar_addr = sar_base +
+ CPU0_WAKEUP_NS_PA_ADDR_OFFSET;
+ else
+ pm_info->wkup_sar_addr = sar_base +
+ OMAP5_CPU0_WAKEUP_NS_PA_ADDR_OFFSET;
pm_info->l2x0_sar_addr = sar_base + L2X0_SAVE_OFFSET0;
}
pm_info->pwrdm = pwrdm_lookup("cpu0_pwrdm");
@@ -391,8 +395,12 @@
pm_info = &per_cpu(omap4_pm_info, 0x1);
if (sar_base) {
pm_info->scu_sar_addr = sar_base + SCU_OFFSET1;
- pm_info->wkup_sar_addr = sar_base +
- CPU1_WAKEUP_NS_PA_ADDR_OFFSET;
+ if (cpu_is_omap44xx())
+ pm_info->wkup_sar_addr = sar_base +
+ CPU1_WAKEUP_NS_PA_ADDR_OFFSET;
+ else
+ pm_info->wkup_sar_addr = sar_base +
+ OMAP5_CPU1_WAKEUP_NS_PA_ADDR_OFFSET;
pm_info->l2x0_sar_addr = sar_base + L2X0_SAVE_OFFSET1;
}
@@ -453,15 +461,24 @@
{
unsigned long startup_pa;
- if (!cpu_is_omap44xx())
+ if (!(cpu_is_omap44xx() || soc_is_omap54xx()))
return;
sar_base = omap4_get_sar_ram_base();
if (cpu_is_omap443x())
startup_pa = virt_to_phys(omap4_secondary_startup);
- else
+ else if (cpu_is_omap446x())
startup_pa = virt_to_phys(omap4460_secondary_startup);
+ else if ((__boot_cpu_mode & MODE_MASK) == HYP_MODE)
+ startup_pa = virt_to_phys(omap5_secondary_hyp_startup);
+ else
+ startup_pa = virt_to_phys(omap5_secondary_startup);
- writel_relaxed(startup_pa, sar_base + CPU1_WAKEUP_NS_PA_ADDR_OFFSET);
+ if (cpu_is_omap44xx())
+ writel_relaxed(startup_pa, sar_base +
+ CPU1_WAKEUP_NS_PA_ADDR_OFFSET);
+ else
+ writel_relaxed(startup_pa, sar_base +
+ OMAP5_CPU1_WAKEUP_NS_PA_ADDR_OFFSET);
}
diff --git a/arch/arm/mach-omap2/omap4-sar-layout.h b/arch/arm/mach-omap2/omap4-sar-layout.h
index 792b106..5b2966a 100644
--- a/arch/arm/mach-omap2/omap4-sar-layout.h
+++ b/arch/arm/mach-omap2/omap4-sar-layout.h
@@ -31,6 +31,8 @@
/* CPUx Wakeup Non-Secure Physical Address offsets in SAR_BANK3 */
#define CPU0_WAKEUP_NS_PA_ADDR_OFFSET 0xa04
#define CPU1_WAKEUP_NS_PA_ADDR_OFFSET 0xa08
+#define OMAP5_CPU0_WAKEUP_NS_PA_ADDR_OFFSET 0xe00
+#define OMAP5_CPU1_WAKEUP_NS_PA_ADDR_OFFSET 0xe04
#define SAR_BACKUP_STATUS_OFFSET (SAR_BANK3_OFFSET + 0x500)
#define SAR_SECURE_RAM_SIZE_OFFSET (SAR_BANK3_OFFSET + 0x504)
diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
index 5e2e221..b2f2448 100644
--- a/arch/arm/mach-omap2/timer.c
+++ b/arch/arm/mach-omap2/timer.c
@@ -510,18 +510,19 @@
}
#endif /* CONFIG_ARCH_OMAP3 */
-#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_SOC_AM33XX)
+#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_SOC_AM33XX) || \
+ defined(CONFIG_SOC_AM43XX)
void __init omap3_gptimer_timer_init(void)
{
__omap_sync32k_timer_init(2, "timer_sys_ck", NULL,
1, "timer_sys_ck", "ti,timer-alwon", true);
-
- clocksource_probe();
+ if (of_have_populated_dt())
+ clocksource_probe();
}
#endif
#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5) || \
- defined(CONFIG_SOC_DRA7XX) || defined(CONFIG_SOC_AM43XX)
+ defined(CONFIG_SOC_DRA7XX)
static void __init omap4_sync32k_timer_init(void)
{
__omap_sync32k_timer_init(1, "timer_32k_ck", "ti,timer-alwon",
diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c
index 12b9435..c725baf 100644
--- a/arch/arm/mach-pxa/pxa25x.c
+++ b/arch/arm/mach-pxa/pxa25x.c
@@ -156,7 +156,7 @@
pxa25x_dt_init_irq(struct device_node *node, struct device_node *parent)
{
pxa_dt_irq_init(pxa25x_set_wake);
- set_handle_irq(ichp_handle_irq);
+ set_handle_irq(icip_handle_irq);
return 0;
}
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index b0f48a3..9eabddc 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -192,13 +192,13 @@
{
tegra_pm_set(TEGRA_SUSPEND_LP2);
- cpu_cluster_pm_enter();
+ cpu_cluster_pm_enter(0);
suspend_cpu_complex();
cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
restore_cpu_complex();
- cpu_cluster_pm_exit();
+ cpu_cluster_pm_exit(0);
}
enum tegra_suspend_mode tegra_pm_validate_suspend_mode(
diff --git a/arch/arm/mach-ux500/pm.c b/arch/arm/mach-ux500/pm.c
index 8538910..a970e7f 100644
--- a/arch/arm/mach-ux500/pm.c
+++ b/arch/arm/mach-ux500/pm.c
@@ -134,8 +134,8 @@
*/
bool prcmu_is_cpu_in_wfi(int cpu)
{
- return readl(PRCM_ARM_WFI_STANDBY) & cpu ? PRCM_ARM_WFI_STANDBY_WFI1 :
- PRCM_ARM_WFI_STANDBY_WFI0;
+ return readl(PRCM_ARM_WFI_STANDBY) &
+ (cpu ? PRCM_ARM_WFI_STANDBY_WFI1 : PRCM_ARM_WFI_STANDBY_WFI0);
}
/*
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index d12002c..ed11864 100644
--- a/arch/arm/mach-zynq/common.c
+++ b/arch/arm/mach-zynq/common.c
@@ -59,7 +59,7 @@
static void __init zynq_memory_init(void)
{
if (!__pa(PAGE_OFFSET))
- memblock_reserve(__pa(PAGE_OFFSET), __pa(swapper_pg_dir));
+ memblock_reserve(__pa(PAGE_OFFSET), 0x80000);
}
static struct platform_device zynq_cpuidle_device = {
diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c
index f193414..4986dc0 100644
--- a/arch/arm/xen/enlighten.c
+++ b/arch/arm/xen/enlighten.c
@@ -372,8 +372,7 @@
* for secondary CPUs as they are brought up.
* For uniformity we use VCPUOP_register_vcpu_info even on cpu0.
*/
- xen_vcpu_info = __alloc_percpu(sizeof(struct vcpu_info),
- sizeof(struct vcpu_info));
+ xen_vcpu_info = alloc_percpu(struct vcpu_info);
if (xen_vcpu_info == NULL)
return -ENOMEM;
diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile
index 718bd0b..92dc1e6 100644
--- a/arch/arm64/boot/Makefile
+++ b/arch/arm64/boot/Makefile
@@ -14,6 +14,8 @@
# Based on the ia64 boot/Makefile.
#
+include $(srctree)/arch/arm64/boot/dts/Makefile
+
OBJCOPYFLAGS_Image :=-O binary -R .note -R .note.gnu.build-id -R .comment -S
targets := Image Image.gz
diff --git a/arch/arm64/boot/dts/broadcom/bcm2837-rpi-3-b.dts b/arch/arm64/boot/dts/broadcom/bcm2837-rpi-3-b.dts
index 7841b72..4617759 100644
--- a/arch/arm64/boot/dts/broadcom/bcm2837-rpi-3-b.dts
+++ b/arch/arm64/boot/dts/broadcom/bcm2837-rpi-3-b.dts
@@ -15,13 +15,6 @@
act {
gpios = <&gpio 47 0>;
};
-
- pwr {
- label = "PWR";
- gpios = <&gpio 35 0>;
- default-state = "keep";
- linux,default-trigger = "default-on";
- };
};
};
diff --git a/arch/arm64/boot/dts/broadcom/bcm2837.dtsi b/arch/arm64/boot/dts/broadcom/bcm2837.dtsi
index 8216bbb..c1f719b 100644
--- a/arch/arm64/boot/dts/broadcom/bcm2837.dtsi
+++ b/arch/arm64/boot/dts/broadcom/bcm2837.dtsi
@@ -1,7 +1,7 @@
#include "bcm283x.dtsi"
/ {
- compatible = "brcm,bcm2836";
+ compatible = "brcm,bcm2837";
soc {
ranges = <0x7e000000 0x3f000000 0x1000000>,
diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
index b548763..af45041 100644
--- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
@@ -322,7 +322,7 @@
compatible = "generic-ohci";
reg = <0x0 0xa7030000 0x0 0x10000>;
interrupt-parent = <&mbigen_usb>;
- interrupts = <64 4>;
+ interrupts = <640 4>;
dma-coherent;
status = "disabled";
};
@@ -331,7 +331,7 @@
compatible = "generic-ehci";
reg = <0x0 0xa7020000 0x0 0x10000>;
interrupt-parent = <&mbigen_usb>;
- interrupts = <65 4>;
+ interrupts = <641 4>;
dma-coherent;
status = "disabled";
};
diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
index 1c71e25..6c03c17 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
@@ -450,6 +450,9 @@
auxadc: auxadc@11001000 {
compatible = "mediatek,mt8173-auxadc";
reg = <0 0x11001000 0 0x1000>;
+ clocks = <&pericfg CLK_PERI_AUXADC>;
+ clock-names = "main";
+ #io-channel-cells = <1>;
};
uart0: serial@11002000 {
diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
index 5fda583..906fb83 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
@@ -21,6 +21,10 @@
reg = <0x0 0x80000000 0x1 0x0>;
};
+ gpu@57000000 {
+ vdd-supply = <&vdd_gpu>;
+ };
+
/* debug port */
serial@70006000 {
status = "okay";
@@ -291,4 +295,18 @@
clock-frequency = <32768>;
};
};
+
+ regulators {
+ vdd_gpu: regulator@100 {
+ compatible = "pwm-regulator";
+ reg = <100>;
+ pwms = <&pwm 1 4880>;
+ regulator-name = "VDD_GPU";
+ regulator-min-microvolt = <710000>;
+ regulator-max-microvolt = <1320000>;
+ enable-gpios = <&pmic 6 GPIO_ACTIVE_HIGH>;
+ regulator-ramp-delay = <80>;
+ regulator-enable-ramp-delay = <1000>;
+ };
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
index a69525c..acb1865 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,5 +19,1042 @@
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
+
+ wcd9xxx_intr {
+ wcd_intr_default: wcd_intr_default{
+ mux {
+ pins = "gpio54";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio54";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* pull down */
+ input-enable;
+ };
+ };
+ };
+
+ cdc_reset_ctrl {
+ cdc_reset_sleep: cdc_reset_sleep {
+ mux {
+ pins = "gpio64";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio64";
+ drive-strength = <2>;
+ bias-disable;
+ output-low;
+ };
+ };
+
+ cdc_reset_active:cdc_reset_active {
+ mux {
+ pins = "gpio64";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio64";
+ drive-strength = <8>;
+ bias-pull-down;
+ output-high;
+ };
+ };
+ };
+
+ spkr_i2s_clk_pin {
+ spkr_i2s_clk_sleep: spkr_i2s_clk_sleep {
+ mux {
+ pins = "gpio69";
+ function = "spkr_i2s";
+ };
+
+ config {
+ pins = "gpio69";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ };
+ };
+
+ spkr_i2s_clk_active: spkr_i2s_clk_active {
+ mux {
+ pins = "gpio69";
+ function = "spkr_i2s";
+ };
+
+ config {
+ pins = "gpio69";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ wcd_gnd_mic_swap {
+ wcd_gnd_mic_swap_idle: wcd_gnd_mic_swap_idle {
+ mux {
+ pins = "gpio51";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio51";
+ drive-strength = <2>;
+ bias-pull-down;
+ output-low;
+ };
+ };
+
+ wcd_gnd_mic_swap_active: wcd_gnd_mic_swap_active {
+ mux {
+ pins = "gpio51";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio51";
+ drive-strength = <2>;
+ bias-disable;
+ output-high;
+ };
+ };
+ };
+
+ pri_aux_pcm_clk {
+ pri_aux_pcm_clk_sleep: pri_aux_pcm_clk_sleep {
+ mux {
+ pins = "gpio65";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio65";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_aux_pcm_clk_active: pri_aux_pcm_clk_active {
+ mux {
+ pins = "gpio65";
+ function = "pri_mi2s";
+ };
+
+ config {
+ pins = "gpio65";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ pri_aux_pcm_sync {
+ pri_aux_pcm_sync_sleep: pri_aux_pcm_sync_sleep {
+ mux {
+ pins = "gpio66";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio66";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_aux_pcm_sync_active: pri_aux_pcm_sync_active {
+ mux {
+ pins = "gpio66";
+ function = "pri_mi2s_ws";
+ };
+
+ config {
+ pins = "gpio66";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ pri_aux_pcm_din {
+ pri_aux_pcm_din_sleep: pri_aux_pcm_din_sleep {
+ mux {
+ pins = "gpio67";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio67";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_aux_pcm_din_active: pri_aux_pcm_din_active {
+ mux {
+ pins = "gpio67";
+ function = "pri_mi2s";
+ };
+
+ config {
+ pins = "gpio67";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ pri_aux_pcm_dout {
+ pri_aux_pcm_dout_sleep: pri_aux_pcm_dout_sleep {
+ mux {
+ pins = "gpio68";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio68";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_aux_pcm_dout_active: pri_aux_pcm_dout_active {
+ mux {
+ pins = "gpio68";
+ function = "pri_mi2s";
+ };
+
+ config {
+ pins = "gpio68";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ sec_aux_pcm {
+ sec_aux_pcm_sleep: sec_aux_pcm_sleep {
+ mux {
+ pins = "gpio80", "gpio81";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio80", "gpio81";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ sec_aux_pcm_active: sec_aux_pcm_active {
+ mux {
+ pins = "gpio80", "gpio81";
+ function = "sec_mi2s";
+ };
+
+ config {
+ pins = "gpio80", "gpio81";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ sec_aux_pcm_din {
+ sec_aux_pcm_din_sleep: sec_aux_pcm_din_sleep {
+ mux {
+ pins = "gpio82";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio82";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ sec_aux_pcm_din_active: sec_aux_pcm_din_active {
+ mux {
+ pins = "gpio82";
+ function = "sec_mi2s";
+ };
+
+ config {
+ pins = "gpio82";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ sec_aux_pcm_dout {
+ sec_aux_pcm_dout_sleep: sec_aux_pcm_dout_sleep {
+ mux {
+ pins = "gpio83";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio83";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ sec_aux_pcm_dout_active: sec_aux_pcm_dout_active {
+ mux {
+ pins = "gpio83";
+ function = "sec_mi2s";
+ };
+
+ config {
+ pins = "gpio83";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ tert_aux_pcm {
+ tert_aux_pcm_sleep: tert_aux_pcm_sleep {
+ mux {
+ pins = "gpio75", "gpio76";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio75", "gpio76";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ tert_aux_pcm_active: tert_aux_pcm_active {
+ mux {
+ pins = "gpio75", "gpio76";
+ function = "ter_mi2s";
+ };
+
+ config {
+ pins = "gpio75", "gpio76";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ tert_aux_pcm_din {
+ tert_aux_pcm_din_sleep: tert_aux_pcm_din_sleep {
+ mux {
+ pins = "gpio77";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio77";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ tert_aux_pcm_din_active: tert_aux_pcm_din_active {
+ mux {
+ pins = "gpio77";
+ function = "ter_mi2s";
+ };
+
+ config {
+ pins = "gpio77";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ tert_aux_pcm_dout {
+ tert_aux_pcm_dout_sleep: tert_aux_pcm_dout_sleep {
+ mux {
+ pins = "gpio78";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio78";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ tert_aux_pcm_dout_active: tert_aux_pcm_dout_active {
+ mux {
+ pins = "gpio78";
+ function = "ter_mi2s";
+ };
+
+ config {
+ pins = "gpio78";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ quat_aux_pcm {
+ quat_aux_pcm_sleep: quat_aux_pcm_sleep {
+ mux {
+ pins = "gpio58", "gpio59";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio58", "gpio59";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_aux_pcm_active: quat_aux_pcm_active {
+ mux {
+ pins = "gpio58", "gpio59";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio58", "gpio59";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ quat_aux_pcm_din {
+ quat_aux_pcm_din_sleep: quat_aux_pcm_din_sleep {
+ mux {
+ pins = "gpio60";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio60";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_aux_pcm_din_active: quat_aux_pcm_din_active {
+ mux {
+ pins = "gpio60";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio60";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ quat_aux_pcm_dout {
+ quat_aux_pcm_dout_sleep: quat_aux_pcm_dout_sleep {
+ mux {
+ pins = "gpio61";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio61";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_aux_pcm_dout_active: quat_aux_pcm_dout_active {
+ mux {
+ pins = "gpio61";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio61";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ pri_mi2s_mclk {
+ pri_mi2s_mclk_sleep: pri_mi2s_mclk_sleep {
+ mux {
+ pins = "gpio64";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio64";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_mi2s_mclk_active: pri_mi2s_mclk_active {
+ mux {
+ pins = "gpio64";
+ function = "pri_mi2s";
+ };
+
+ config {
+ pins = "gpio64";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ pri_mi2s_sck {
+ pri_mi2s_sck_sleep: pri_mi2s_sck_sleep {
+ mux {
+ pins = "gpio65";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio65";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_mi2s_sck_active: pri_mi2s_sck_active {
+ mux {
+ pins = "gpio65";
+ function = "pri_mi2s";
+ };
+
+ config {
+ pins = "gpio65";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ pri_mi2s_ws {
+ pri_mi2s_ws_sleep: pri_mi2s_ws_sleep {
+ mux {
+ pins = "gpio66";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio66";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_mi2s_ws_active: pri_mi2s_ws_active {
+ mux {
+ pins = "gpio66";
+ function = "pri_mi2s_ws";
+ };
+
+ config {
+ pins = "gpio66";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ pri_mi2s_sd0 {
+ pri_mi2s_sd0_sleep: pri_mi2s_sd0_sleep {
+ mux {
+ pins = "gpio67";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio67";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_mi2s_sd0_active: pri_mi2s_sd0_active {
+ mux {
+ pins = "gpio67";
+ function = "pri_mi2s";
+ };
+
+ config {
+ pins = "gpio67";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ pri_mi2s_sd1 {
+ pri_mi2s_sd1_sleep: pri_mi2s_sd1_sleep {
+ mux {
+ pins = "gpio68";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio68";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ pri_mi2s_sd1_active: pri_mi2s_sd1_active {
+ mux {
+ pins = "gpio68";
+ function = "pri_mi2s";
+ };
+
+ config {
+ pins = "gpio68";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ sec_mi2s_mclk {
+ sec_mi2s_mclk_sleep: sec_mi2s_mclk_sleep {
+ mux {
+ pins = "gpio79";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio79";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ sec_mi2s_mclk_active: sec_mi2s_mclk_active {
+ mux {
+ pins = "gpio79";
+ function = "sec_mi2s";
+ };
+
+ config {
+ pins = "gpio79";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ sec_mi2s {
+ sec_mi2s_sleep: sec_mi2s_sleep {
+ mux {
+ pins = "gpio80", "gpio81";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio80", "gpio81";
+ drive-strength = <2>; /* 2 mA */
+ bias-disable; /* NO PULL */
+ input-enable;
+ };
+ };
+
+ sec_mi2s_active: sec_mi2s_active {
+ mux {
+ pins = "gpio80", "gpio81";
+ function = "sec_mi2s";
+ };
+
+ config {
+ pins = "gpio80", "gpio81";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ sec_mi2s_sd0 {
+ sec_mi2s_sd0_sleep: sec_mi2s_sd0_sleep {
+ mux {
+ pins = "gpio82";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio82";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ sec_mi2s_sd0_active: sec_mi2s_sd0_active {
+ mux {
+ pins = "gpio82";
+ function = "sec_mi2s";
+ };
+
+ config {
+ pins = "gpio82";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ sec_mi2s_sd1 {
+ sec_mi2s_sd1_sleep: sec_mi2s_sd1_sleep {
+ mux {
+ pins = "gpio83";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio83";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ sec_mi2s_sd1_active: sec_mi2s_sd1_active {
+ mux {
+ pins = "gpio83";
+ function = "sec_mi2s";
+ };
+
+ config {
+ pins = "gpio83";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ tert_mi2s_mclk {
+ tert_mi2s_mclk_sleep: tert_mi2s_mclk_sleep {
+ mux {
+ pins = "gpio74";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio74";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ tert_mi2s_mclk_active: tert_mi2s_mclk_active {
+ mux {
+ pins = "gpio74";
+ function = "ter_mi2s";
+ };
+
+ config {
+ pins = "gpio74";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ tert_mi2s {
+ tert_mi2s_sleep: tert_mi2s_sleep {
+ mux {
+ pins = "gpio75", "gpio76";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio75", "gpio76";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ tert_mi2s_active: tert_mi2s_active {
+ mux {
+ pins = "gpio75", "gpio76";
+ function = "ter_mi2s";
+ };
+
+ config {
+ pins = "gpio75", "gpio76";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ tert_mi2s_sd0 {
+ tert_mi2s_sd0_sleep: tert_mi2s_sd0_sleep {
+ mux {
+ pins = "gpio77";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio77";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ tert_mi2s_sd0_active: tert_mi2s_sd0_active {
+ mux {
+ pins = "gpio77";
+ function = "ter_mi2s";
+ };
+
+ config {
+ pins = "gpio77";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ tert_mi2s_sd1 {
+ tert_mi2s_sd1_sleep: tert_mi2s_sd1_sleep {
+ mux {
+ pins = "gpio78";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio78";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ tert_mi2s_sd1_active: tert_mi2s_sd1_active {
+ mux {
+ pins = "gpio78";
+ function = "ter_mi2s";
+ };
+
+ config {
+ pins = "gpio78";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ quat_mi2s_mclk {
+ quat_mi2s_mclk_sleep: quat_mi2s_mclk_sleep {
+ mux {
+ pins = "gpio57";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio57";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_mi2s_mclk_active: quat_mi2s_mclk_active {
+ mux {
+ pins = "gpio57";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio57";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ quat_mi2s {
+ quat_mi2s_sleep: quat_mi2s_sleep {
+ mux {
+ pins = "gpio58", "gpio59";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio58", "gpio59";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_mi2s_active: quat_mi2s_active {
+ mux {
+ pins = "gpio58", "gpio59";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio58", "gpio59";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ output-high;
+ };
+ };
+ };
+
+ quat_mi2s_sd0 {
+ quat_mi2s_sd0_sleep: quat_mi2s_sd0_sleep {
+ mux {
+ pins = "gpio60";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio60";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_mi2s_sd0_active: quat_mi2s_sd0_active {
+ mux {
+ pins = "gpio60";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio60";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ quat_mi2s_sd1 {
+ quat_mi2s_sd1_sleep: quat_mi2s_sd1_sleep {
+ mux {
+ pins = "gpio61";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio61";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_mi2s_sd1_active: quat_mi2s_sd1_active {
+ mux {
+ pins = "gpio61";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio61";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ quat_mi2s_sd2 {
+ quat_mi2s_sd2_sleep: quat_mi2s_sd2_sleep {
+ mux {
+ pins = "gpio62";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio62";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_mi2s_sd2_active: quat_mi2s_sd2_active {
+ mux {
+ pins = "gpio62";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio62";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
+
+ quat_mi2s_sd3 {
+ quat_mi2s_sd3_sleep: quat_mi2s_sd3_sleep {
+ mux {
+ pins = "gpio63";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio63";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ input-enable;
+ };
+ };
+
+ quat_mi2s_sd3_active: quat_mi2s_sd3_active {
+ mux {
+ pins = "gpio63";
+ function = "qua_mi2s";
+ };
+
+ config {
+ pins = "gpio63";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
index 3bcf6c0..228bbb3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -14,30 +14,11 @@
/* Stub regulators */
/ {
- pm8998_s1: regulator-pm8998-s1 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_s1";
- qcom,hpm-min-load = <100000>;
- regulator-min-microvolt = <800000>;
- regulator-max-microvolt = <800000>;
- };
- pm8998_s2: regulator-pm8998-s2 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_s2";
- qcom,hpm-min-load = <100000>;
- regulator-min-microvolt = <1100000>;
- regulator-max-microvolt = <1100000>;
- };
-
- pm8998_s3: regulator-pm8998-s3 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_s3";
- qcom,hpm-min-load = <100000>;
- regulator-min-microvolt = <1352000>;
- regulator-max-microvolt = <1352000>;
- };
-
+ /*
+ * RPMh does not provide support for PM8998 S4 because it is always-on
+ * at 1.8 V in auto mode. Therefore, use a stub regulator for S4.
+ */
pm8998_s4: regulator-pm8998-s4 {
compatible = "qcom,stub-regulator";
regulator-name = "pm8998_s4";
@@ -46,325 +27,6 @@
regulator-max-microvolt = <1800000>;
};
- pm8998_s5: regulator-pm8998-s5 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_s5";
- qcom,hpm-min-load = <100000>;
- regulator-min-microvolt = <1904000>;
- regulator-max-microvolt = <2040000>;
- };
-
- /* PM8998 S6 = VDD_MX supply */
- pm8998_s6_level: regulator-pm8998-s6-level {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_s6_level";
- qcom,hpm-min-load = <100000>;
- regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
- regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
- };
-
- pm8998_s6_level_ao: regulator-pm8998-s6-level-ao {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_s6_level_ao";
- qcom,hpm-min-load = <100000>;
- regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
- regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
- };
-
- pm8998_s7: regulator-pm8998-s7 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_s7";
- qcom,hpm-min-load = <100000>;
- regulator-min-microvolt = <900000>;
- regulator-max-microvolt = <1028000>;
- };
-
- /* PM8998 S9 + S8 = VDD_CX supply */
- pm8998_s9_level: regulator-pm8998-s9-level {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_s9_level";
- qcom,hpm-min-load = <100000>;
- regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
- regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
- };
-
- pm8998_s9_level_ao: regulator-pm8998-s9-level-ao {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_s9_level_ao";
- qcom,hpm-min-load = <100000>;
- regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
- regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
- };
-
- pm8998_l1: regulator-pm8998-l1 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l1";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <880000>;
- regulator-max-microvolt = <880000>;
- };
-
- pm8998_l2: regulator-pm8998-l2 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l2";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <1200000>;
- regulator-max-microvolt = <1200000>;
- };
-
- pm8998_l3: regulator-pm8998-l3 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l3";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <1000000>;
- regulator-max-microvolt = <1000000>;
- };
-
- /* PM8998 L4 = VDD_SSC_MX supply */
- pm8998_l4_level: regulator-pm8998-l4-level {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l4_level";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
- regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
- };
-
- pm8998_l5: regulator-pm8998-l5 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l5";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <800000>;
- regulator-max-microvolt = <800000>;
- };
-
- pm8998_l6: regulator-pm8998-l6 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l6";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <1856000>;
- regulator-max-microvolt = <1856000>;
- };
-
- pm8998_l7: regulator-pm8998-l7 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l7";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <1800000>;
- };
-
- pm8998_l8: regulator-pm8998-l8 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l8";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <1200000>;
- regulator-max-microvolt = <1200000>;
- };
-
- pm8998_l9: regulator-pm8998-l9 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l9";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <1808000>;
- regulator-max-microvolt = <2960000>;
- };
-
- pm8998_l10: regulator-pm8998-l10 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l10";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <1808000>;
- regulator-max-microvolt = <2960000>;
- };
-
- pm8998_l11: regulator-pm8998-l11 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l11";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <1000000>;
- regulator-max-microvolt = <1000000>;
- };
-
- pm8998_l12: regulator-pm8998-l12 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l12";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <1800000>;
- };
-
- pm8998_l13: regulator-pm8998-l13 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l13";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <1808000>;
- regulator-max-microvolt = <2960000>;
- };
-
- pm8998_l14: regulator-pm8998-l14 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l14";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <1800000>;
- };
-
- pm8998_l15: regulator-pm8998-l15 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l15";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <1800000>;
- };
-
- pm8998_l16: regulator-pm8998-l16 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l16";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <2704000>;
- regulator-max-microvolt = <2704000>;
- };
-
- pm8998_l17: regulator-pm8998-l17 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l17";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <1304000>;
- regulator-max-microvolt = <1304000>;
- };
-
- pm8998_l18: regulator-pm8998-l18 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l18";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <2704000>;
- regulator-max-microvolt = <2704000>;
- };
-
- pm8998_l19: regulator-pm8998-l19 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l19";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <3008000>;
- regulator-max-microvolt = <3008000>;
- };
-
- pm8998_l20: regulator-pm8998-l20 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l20";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <2960000>;
- regulator-max-microvolt = <2960000>;
- };
-
- pm8998_l21: regulator-pm8998-l21 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l21";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <2960000>;
- regulator-max-microvolt = <2960000>;
- };
-
- pm8998_l22: regulator-pm8998-l22 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l22";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <2864000>;
- regulator-max-microvolt = <2864000>;
- };
-
- pm8998_l23: regulator-pm8998-l23 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l23";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <3312000>;
- regulator-max-microvolt = <3312000>;
- };
-
- pm8998_l24: regulator-pm8998-l24 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l24";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <3088000>;
- regulator-max-microvolt = <3088000>;
- };
-
- pm8998_l25: regulator-pm8998-l25 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l25";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <3104000>;
- regulator-max-microvolt = <3104000>;
- };
-
- pm8998_l26: regulator-pm8998-l26 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l26";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <1200000>;
- regulator-max-microvolt = <1200000>;
- };
-
- /* PM8998 L27 = VDD_SSC_CX supply */
- pm8998_l27_level: regulator-pm8998-l27-level {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l27_level";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
- regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
- };
-
- pm8998_l28: regulator-pm8998-l28 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_l28";
- qcom,hpm-min-load = <10000>;
- regulator-min-microvolt = <3008000>;
- regulator-max-microvolt = <3008000>;
- };
-
- pm8998_lvs1: regulator-pm8998-lvs1 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_lvs1";
- };
-
- pm8998_lvs2: regulator-pm8998-lvs2 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8998_lvs2";
- };
-
- pmi8998_bob: regulator-pmi8998-bob {
- compatible = "qcom,stub-regulator";
- regulator-name = "pmi8998_bob";
- regulator-min-microvolt = <3312000>;
- regulator-max-microvolt = <3600000>;
- };
-
- /* PM8005 S1 + S4 = 2 phase VDD_GFX supply */
- pm8005_s1_level: regulator-pm8005-s1-level {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8005_s1_level";
- qcom,hpm-min-load = <100000>;
- regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
- regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
- };
-
- /* PM8005 S2 = VDD_MODEM supply */
- pm8005_s2_level: regulator-pm8005-s2-level {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8005_s2_level";
- qcom,hpm-min-load = <100000>;
- regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
- regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
- };
-
- pm8005_s3: regulator-pm8005-s3 {
- compatible = "qcom,stub-regulator";
- regulator-name = "pm8005_s3";
- qcom,hpm-min-load = <100000>;
- regulator-min-microvolt = <600000>;
- regulator-max-microvolt = <600000>;
- };
-
apc0_pwrcl_vreg: regulator-pwrcl {
compatible = "qcom,stub-regulator";
regulator-name = "apc0_pwrcl_corner";
@@ -386,3 +48,558 @@
regulator-max-microvolt = <26>;
};
};
+
+&soc {
+ /* RPMh regulators: */
+
+ /* PM8998 S1 = VDD_EBI supply */
+ rpmh-regulator-ebilvl {
+ compatible = "qcom,rpmh-arc-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ebi.lvl";
+ pm8998_s1_level: regulator-s1 {
+ regulator-name = "pm8998_s1_level";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+ };
+
+ rpmh-regulator-smpa2 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "smpa2";
+ pm8998_s2: regulator-s2 {
+ regulator-name = "pm8998_s2";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ qcom,init-voltage = <1100000>;
+ };
+ };
+
+ rpmh-regulator-smpa3 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "smpa3";
+ pm8998_s3: regulator-s3 {
+ regulator-name = "pm8998_s3";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1352000>;
+ regulator-max-microvolt = <1352000>;
+ qcom,init-voltage = <1352000>;
+ };
+ };
+
+ rpmh-regulator-smpa5 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "smpa5";
+ pm8998_s5: regulator-s5 {
+ regulator-name = "pm8998_s5";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1904000>;
+ regulator-max-microvolt = <2040000>;
+ qcom,init-voltage = <1904000>;
+ };
+ };
+
+ /* PM8998 S6 = VDD_MX supply */
+ rpmh-regulator-mxlvl {
+ compatible = "qcom,rpmh-arc-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "mx.lvl";
+ pm8998_s6_level: regulator-s6-level {
+ regulator-name = "pm8998_s6_level";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+
+ pm8998_s6_level_ao: regulator-s6-level-ao {
+ regulator-name = "pm8998_s6_level_ao";
+ qcom,set = <RPMH_REGULATOR_SET_ACTIVE>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+ };
+
+ rpmh-regulator-smpa7 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "smpa7";
+ pm8998_s7: regulator-s7 {
+ regulator-name = "pm8998_s7";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <1028000>;
+ qcom,init-voltage = <900000>;
+ };
+ };
+
+ /* PM8998 S9 + S8 = VDD_CX supply */
+ rpmh-regulator-cxlvl {
+ compatible = "qcom,rpmh-arc-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "cx.lvl";
+ pm8998_s9_level-parent-supply = <&pm8998_s6_level>;
+ pm8998_s9_level_ao-parent-supply = <&pm8998_s6_level_ao>;
+ pm8998_s9_level: regulator-s9-level {
+ regulator-name = "pm8998_s9_level";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ qcom,min-dropout-voltage-level = <(-1)>;
+ };
+
+ pm8998_s9_level_ao: regulator-s9-level-ao {
+ regulator-name = "pm8998_s9_level_ao";
+ qcom,set = <RPMH_REGULATOR_SET_ACTIVE>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ qcom,min-dropout-voltage-level = <(-1)>;
+ };
+ };
+
+ rpmh-regulator-ldoa1 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa1";
+ pm8998_l1: regulator-l1 {
+ regulator-name = "pm8998_l1";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <880000>;
+ regulator-max-microvolt = <880000>;
+ qcom,init-voltage = <880000>;
+ };
+ };
+
+ rpmh-regulator-ldoa2 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa2";
+ pm8998_l2: regulator-l2 {
+ regulator-name = "pm8998_l2";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ qcom,init-voltage = <1200000>;
+ };
+ };
+
+ rpmh-regulator-ldoa3 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa3";
+ pm8998_l3: regulator-l3 {
+ regulator-name = "pm8998_l3";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ qcom,init-voltage = <1000000>;
+ };
+ };
+
+ /* PM8998 L4 = VDD_SSC_MX supply */
+ rpmh-regulator-lmxlvl {
+ compatible = "qcom,rpmh-arc-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "lmx.lvl";
+ pm8998_l4_level: regulator-l4-level {
+ regulator-name = "pm8998_l4_level";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+ };
+
+ rpmh-regulator-ldoa5 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa5";
+ pm8998_l5: regulator-l5 {
+ regulator-name = "pm8998_l5";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <800000>;
+ qcom,init-voltage = <800000>;
+ };
+ };
+
+ rpmh-regulator-ldoa6 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa6";
+ pm8998_l6: regulator-l6 {
+ regulator-name = "pm8998_l6";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1856000>;
+ regulator-max-microvolt = <1856000>;
+ qcom,init-voltage = <1856000>;
+ };
+ };
+
+ rpmh-regulator-ldoa7 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa7";
+ pm8998_l7: regulator-l7 {
+ regulator-name = "pm8998_l7";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+ };
+ };
+
+ rpmh-regulator-ldoa8 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa8";
+ pm8998_l8: regulator-l8 {
+ regulator-name = "pm8998_l8";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ qcom,init-voltage = <1200000>;
+ };
+ };
+
+ rpmh-regulator-ldoa9 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa9";
+ pm8998_l9: regulator-l9 {
+ regulator-name = "pm8998_l9";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1808000>;
+ regulator-max-microvolt = <2960000>;
+ qcom,init-voltage = <1808000>;
+ };
+ };
+
+ rpmh-regulator-ldoa10 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa10";
+ pm8998_l10: regulator-l10 {
+ regulator-name = "pm8998_l10";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1808000>;
+ regulator-max-microvolt = <2960000>;
+ qcom,init-voltage = <1808000>;
+ };
+ };
+
+ rpmh-regulator-ldoa11 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa11";
+ pm8998_l11: regulator-l11 {
+ regulator-name = "pm8998_l11";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ qcom,init-voltage = <1000000>;
+ };
+ };
+
+ rpmh-regulator-ldoa12 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa12";
+ pm8998_l12: regulator-l12 {
+ regulator-name = "pm8998_l12";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+ };
+ };
+
+ rpmh-regulator-ldoa13 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa13";
+ pm8998_l13: regulator-l13 {
+ regulator-name = "pm8998_l13";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1808000>;
+ regulator-max-microvolt = <2960000>;
+ qcom,init-voltage = <1808000>;
+ };
+ };
+
+ rpmh-regulator-ldoa14 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa14";
+ pm8998_l14: regulator-l14 {
+ regulator-name = "pm8998_l14";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+ };
+ };
+
+ rpmh-regulator-ldoa15 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa15";
+ pm8998_l15: regulator-l15 {
+ regulator-name = "pm8998_l15";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+ };
+ };
+
+ rpmh-regulator-ldoa16 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa16";
+ pm8998_l16: regulator-l16 {
+ regulator-name = "pm8998_l16";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <2704000>;
+ regulator-max-microvolt = <2704000>;
+ qcom,init-voltage = <2704000>;
+ };
+ };
+
+ rpmh-regulator-ldoa17 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa17";
+ pm8998_l17: regulator-l17 {
+ regulator-name = "pm8998_l17";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1304000>;
+ regulator-max-microvolt = <1304000>;
+ qcom,init-voltage = <1304000>;
+ };
+ };
+
+ rpmh-regulator-ldoa18 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa18";
+ pm8998_l18: regulator-l18 {
+ regulator-name = "pm8998_l18";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <2704000>;
+ regulator-max-microvolt = <2704000>;
+ qcom,init-voltage = <2704000>;
+ };
+ };
+
+ rpmh-regulator-ldoa19 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa19";
+ pm8998_l19: regulator-l19 {
+ regulator-name = "pm8998_l19";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <3008000>;
+ regulator-max-microvolt = <3008000>;
+ qcom,init-voltage = <3008000>;
+ };
+ };
+
+ rpmh-regulator-ldoa20 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa20";
+ pm8998_l20: regulator-l20 {
+ regulator-name = "pm8998_l20";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <2960000>;
+ regulator-max-microvolt = <2960000>;
+ qcom,init-voltage = <2960000>;
+ };
+ };
+
+ rpmh-regulator-ldoa21 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa21";
+ pm8998_l21: regulator-l21 {
+ regulator-name = "pm8998_l21";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <2960000>;
+ regulator-max-microvolt = <2960000>;
+ qcom,init-voltage = <2960000>;
+ };
+ };
+
+ rpmh-regulator-ldoa22 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa22";
+ pm8998_l22: regulator-l22 {
+ regulator-name = "pm8998_l22";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <2864000>;
+ regulator-max-microvolt = <2864000>;
+ qcom,init-voltage = <2864000>;
+ };
+ };
+
+ rpmh-regulator-ldoa23 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa23";
+ pm8998_l23: regulator-l23 {
+ regulator-name = "pm8998_l23";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <3312000>;
+ regulator-max-microvolt = <3312000>;
+ qcom,init-voltage = <3312000>;
+ };
+ };
+
+ rpmh-regulator-ldoa24 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa24";
+ pm8998_l24: regulator-l24 {
+ regulator-name = "pm8998_l24";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <3088000>;
+ regulator-max-microvolt = <3088000>;
+ qcom,init-voltage = <3088000>;
+ };
+ };
+
+ rpmh-regulator-ldoa25 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa25";
+ pm8998_l25: regulator-l25 {
+ regulator-name = "pm8998_l25";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <3104000>;
+ regulator-max-microvolt = <3104000>;
+ qcom,init-voltage = <3104000>;
+ };
+ };
+
+ rpmh-regulator-ldoa26 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa26";
+ pm8998_l26: regulator-l26 {
+ regulator-name = "pm8998_l26";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ qcom,init-voltage = <1200000>;
+ };
+ };
+
+ /* PM8998 L27 = VDD_SSC_CX supply */
+ rpmh-regulator-lcxlvl {
+ compatible = "qcom,rpmh-arc-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "lcx.lvl";
+ pm8998_l27_level: regulator-l27-level {
+ regulator-name = "pm8998_l27_level";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+ };
+
+ rpmh-regulator-ldoa28 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "ldoa28";
+ pm8998_l28: regulator-l28 {
+ regulator-name = "pm8998_l28";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <3008000>;
+ regulator-max-microvolt = <3008000>;
+ qcom,init-voltage = <3008000>;
+ };
+ };
+
+ rpmh-regulator-vsa1 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "vsa1";
+ pm8998_lvs1: regulator-lvs1 {
+ regulator-name = "pm8998_lvs1";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+ };
+
+ rpmh-regulator-vsa2 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "vsa2";
+ pm8998_lvs2: regulator-lvs2 {
+ regulator-name = "pm8998_lvs2";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+ };
+
+ rpmh-regulator-bobb1 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "bobb1";
+ pmi8998_bob: regulator-bob {
+ regulator-name = "pmi8998_bob";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <3312000>;
+ regulator-max-microvolt = <3600000>;
+ qcom,init-voltage = <3312000>;
+ };
+ };
+
+ /* PM8005 S1 + S4 = 2 phase VDD_GFX supply */
+ rpmh-regulator-gfxlvl {
+ compatible = "qcom,rpmh-arc-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "gfx.lvl";
+ pm8005_s1_level: regulator-s1-level {
+ regulator-name = "pm8005_s1_level";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+ };
+
+ /* PM8005 S2 = VDD_MODEM supply */
+ rpmh-regulator-msslvl {
+ compatible = "qcom,rpmh-arc-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "mss.lvl";
+ pm8005_s2_level: regulator-s2-level {
+ regulator-name = "pm8005_s2_level";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+ };
+ };
+
+ rpmh-regulator-smpc3 {
+ compatible = "qcom,rpmh-vrm-regulator";
+ mboxes = <&apps_rsc 0>;
+ qcom,resource-name = "smpc3";
+ pm8005_s3: regulator-s3 {
+ regulator-name = "pm8005_s3";
+ qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ regulator-min-microvolt = <600000>;
+ regulator-max-microvolt = <600000>;
+ qcom,init-voltage = <600000>;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi b/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
index 3d70a17..663ff7e 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -10,6 +10,8 @@
* GNU General Public License for more details.
*/
+#include <dt-bindings/spmi/spmi.h>
+
&ufsphy_mem {
compatible = "qcom,ufs-phy-qrbtc-sdm845";
@@ -63,6 +65,50 @@
};
};
+&spmi_bus {
+ qcom,pm8998@0 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x0 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ };
+
+ qcom,pm8998@1 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x1 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ };
+
+ qcom,pmi8998@2 {
+ compatible = "qcom,spmi-pmic";
+ reg = <0x2 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ };
+
+ qcom,pmi8998@3 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x3 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ };
+
+ qcom,pm8005@4 {
+ compatible = "qcom,spmi-pmic";
+ reg = <0x4 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ };
+
+ qcom,pm8005@5 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x5 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ };
+};
+
&ufsphy_card {
compatible = "qcom,ufs-phy-qrbtc-sdm845";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
index 980ef8c..342eec7 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
@@ -18,38 +18,69 @@
reg-names = "mdp_phys",
"vbif_phys";
+ clocks = <&clock_dispcc DISP_CC_MDSS_AHB_CLK>,
+ <&clock_dispcc DISP_CC_MDSS_AXI_CLK>,
+ <&clock_dispcc DISP_CC_MDSS_MDP_CLK_SRC>,
+ <&clock_dispcc DISP_CC_MDSS_MDP_CLK>;
+ clock-names = "iface_clk", "bus_clk",
+ "core_clk_src", "core_clk";
+ clock-rate = <0 0 300000000 300000000>;
+ clock-max-rate = <0 0 430000000 430000000>;
+
+ mdp-vdd-supply = <&mdss_core_gdsc>;
+
/* interrupt config */
interrupt-parent = <&intc>;
interrupts = <0 83 0>;
interrupt-controller;
#interrupt-cells = <1>;
+ iommus = <&apps_smmu 0x880>;
/* hw blocks */
qcom,sde-off = <0x1000>;
+ qcom,sde-len = <0x45C>;
+
qcom,sde-ctl-off = <0x2000 0x2200 0x2400
0x2600 0x2800>;
+ qcom,sde-ctl-size = <0xE4>;
+
qcom,sde-mixer-off = <0x45000 0x46000 0x47000
0x48000 0x49000 0x4a000>;
+ qcom,sde-mixer-size = <0x320>;
+
qcom,sde-dspp-off = <0x55000 0x57000 0x59000 0x5b000>;
+ qcom,sde-dspp-size = <0x4>;
+
qcom,sde-wb-off = <0x66000>;
+ qcom,sde-wb-size = <0x2c8>;
+
qcom,sde-wb-xin-id = <6>;
qcom,sde-wb-id = <2>;
qcom,sde-intf-off = <0x6b000 0x6b800
0x6c000 0x6c800>;
+ qcom,sde-intf-size = <0x280>;
+
qcom,sde-intf-type = "dp", "dsi", "dsi", "dp";
qcom,sde-pp-off = <0x71000 0x71800
0x72000 0x72800 0x73000>;
qcom,sde-pp-slave = <0x0 0x0 0x0 0x0 0x1>;
+ qcom,sde-pp-size = <0xd4>;
+
qcom,sde-te2-off = <0x2000 0x2000 0x0 0x0 0x0>;
qcom,sde-cdm-off = <0x7a200>;
+ qcom,sde-cdm-size = <0x224>;
+
+ qcom,sde-dsc-off = <0x81000 0x81400 0x81800 0x81c00>;
+ qcom,sde-dsc-size = <0x140>;
+
qcom,sde-intf-max-prefetch-lines = <0x15 0x15 0x15 0x15>;
- qcom,sde-vbif-off = <0>;
qcom,sde-sspp-type = "vig", "vig", "vig", "vig",
"dma", "dma", "dma", "dma";
qcom,sde-sspp-off = <0x5000 0x7000 0x9000 0xb000
0x25000 0x27000 0x29000 0x2b000>;
+ qcom,sde-sspp-src-size = <0x1c8>;
qcom,sde-sspp-xin-id = <0 4 8 12
1 5 9 13>;
@@ -79,10 +110,56 @@
qcom,sde-has-cdp;
qcom,sde-has-src-split;
qcom,sde-has-dim-layer;
+ qcom,sde-max-bw-low-kbps = <9600000>;
+ qcom,sde-max-bw-high-kbps = <9600000>;
+ qcom,sde-dram-channels = <2>;
+ qcom,sde-num-nrt-paths = <0>;
+
+ qcom,sde-vbif-off = <0>;
+ qcom,sde-vbif-size = <0x1040>;
+ qcom,sde-vbif-id = <0>;
qcom,sde-sspp-vig-blocks {
qcom,sde-vig-csc-off = <0x1a00>;
qcom,sde-vig-qseed-off = <0xa00>;
+ qcom,sde-vig-qseed-size = <0xa0>;
+ };
+
+ qcom,platform-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,platform-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "mdp-vdd";
+ qcom,supply-min-voltage = <0>;
+ qcom,supply-max-voltage = <0>;
+ qcom,supply-enable-load = <0>;
+ qcom,supply-disable-load = <0>;
+ };
+ };
+
+ /* data and reg bus scale settings */
+ qcom,sde-data-bus {
+ qcom,msm-bus,name = "mdss_sde";
+ qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,num-paths = <2>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>, <23 512 0 0>,
+ <22 512 0 6400000>, <23 512 0 6400000>,
+ <22 512 0 6400000>, <23 512 0 6400000>;
+ };
+
+ qcom,sde-reg-bus {
+ qcom,msm-bus,name = "mdss_reg";
+ qcom,msm-bus,num-cases = <4>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,vectors-KBps =
+ <1 590 0 0>,
+ <1 590 0 76800>,
+ <1 590 0 150000>,
+ <1 590 0 300000>;
};
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
index 442fcff..7e39b3b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
@@ -16,7 +16,7 @@
usb3: ssusb@a600000 {
compatible = "qcom,dwc-usb3-msm";
reg = <0x0a600000 0xf8c00>,
- <0x0141e000 0x400>;
+ <0x088ee000 0x400>;
reg-names = "core_base", "ahb2phy_base";
#address-cells = <1>;
#size-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-vidc.dtsi b/arch/arm64/boot/dts/qcom/sdm845-vidc.dtsi
new file mode 100644
index 0000000..9545581
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-vidc.dtsi
@@ -0,0 +1,64 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/msm/msm-bus-ids.h>
+#include <dt-bindings/clock/qcom,videocc-sdm845.h>
+
+&soc {
+ msm_vidc: qcom,vidc@cc00000 {
+ compatible = "qcom,msm-vidc";
+ status = "disabled";
+ reg = <0xcc00000 0x100000>;
+ interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+ qcom,debug-timeout;
+ qcom,reg-presets =
+ <0x80124 0x00000003>,
+ <0x80550 0x01111111>,
+ <0x80560 0x01111111>,
+ <0x80568 0x01111111>,
+ <0x80570 0x01111111>,
+ <0x80580 0x01111111>,
+ <0x80588 0x01111111>,
+ <0xe2010 0x00000000>;
+ vdd-supply = <&venus_gdsc>;
+ venus-core0-supply = <&vcodec0_gdsc>;
+ venus-core1-supply = <&vcodec1_gdsc>;
+ clocks = <&clock_videocc VIDEO_CC_VENUS_CTL_CORE_CLK>,
+ <&clock_videocc VIDEO_CC_VENUS_AHB_CLK>,
+ <&clock_videocc VIDEO_CC_VENUS_CTL_AXI_CLK>,
+ <&clock_videocc VIDEO_CC_VCODEC0_CORE_CLK>,
+ <&clock_videocc VIDEO_CC_VCODEC1_CORE_CLK>;
+ clock-names = "core_clk", "iface_clk", "bus_clk",
+ "core0_clk", "core1_clk";
+ qcom,proxy-clock-names = "core_clk", "iface_clk",
+ "bus_clk", "core0_clk", "core1_clk";
+ qcom,clock-configs = <0x1 0x1 0x1 0x1 0x1>;
+ qcom,proxy-reg-names = "vdd";
+ bus_cnoc {
+ compatible = "qcom,msm-vidc,bus";
+ label = "cnoc";
+ qcom,bus-master = <MSM_BUS_MASTER_AMPSS_M0>;
+ qcom,bus-slave = <MSM_BUS_SLAVE_VENUS_CFG>;
+ qcom,bus-governor = "performance";
+ qcom,bus-range-kbps = <1 1>;
+ };
+ venus_bus_ddr {
+ compatible = "qcom,msm-vidc,bus";
+ label = "venus-ddr";
+ qcom,bus-master = <MSM_BUS_MASTER_VIDEO_P0>;
+ qcom,bus-slave = <MSM_BUS_SLAVE_EBI_CH0>;
+ qcom,bus-governor = "msm-vidc-ddr";
+ qcom,bus-range-kbps = <1000 3388000>;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 27d703f..8542ff3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -465,6 +465,32 @@
};
};
+ restart@10ac000 {
+ compatible = "qcom,pshold";
+ reg = <0xC264000 0x4>,
+ <0x1fd3000 0x4>;
+ reg-names = "pshold-base", "tcsr-boot-misc-detect";
+ };
+
+ spmi_bus: qcom,spmi@c440000 {
+ compatible = "qcom,spmi-pmic-arb";
+ reg = <0xc440000 0x1100>,
+ <0xc600000 0x2000000>,
+ <0xe600000 0x100000>,
+ <0xe700000 0xa0000>,
+ <0xc40a000 0x26000>;
+ reg-names = "core", "chnls", "obsrvr", "intr", "cnfg";
+ interrupt-names = "periph_irq";
+ interrupts = <GIC_SPI 481 IRQ_TYPE_NONE>;
+ qcom,ee = <0>;
+ qcom,channel = <0>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ interrupt-controller;
+ #interrupt-cells = <4>;
+ cell-index = <0>;
+ };
+
clock_gcc: qcom,gcc@100000 {
compatible = "qcom,gcc-sdm845";
reg = <0x100000 0x1f0000>;
@@ -803,6 +829,7 @@
reg-names = "slimbus_physical", "slimbus_bam_physical";
interrupts = <0 163 0>, <0 164 0>;
interrupt-names = "slimbus_irq", "slimbus_bam_irq";
+ qcom,apps-ch-pipes = <0x780000>;
qcom,ea-pc = <0x270>;
};
@@ -888,6 +915,11 @@
status = "ok";
};
+ qcom,msm-rtb {
+ compatible = "qcom,msm-rtb";
+ qcom,rtb-size = <0x100000>;
+ };
+
qcom,msm_fastrpc {
compatible = "qcom,msm-fastrpc-compute";
@@ -953,6 +985,11 @@
reg = <0x10 8>;
};
+ restart_reason@65c {
+ compatible = "qcom,msm-imem-restart_reason";
+ reg = <0x65c 4>;
+ };
+
pil@94c {
compatible = "qcom,msm-imem-pil";
reg = <0x94c 200>;
@@ -1283,6 +1320,14 @@
qcom,fragmented-data;
};
+ qcom,spcom {
+ compatible = "qcom,spcom";
+
+ /* predefined channels, remote side is server */
+ qcom,spcom-ch-names = "sp_kernel", "sp_ssr";
+ status = "ok";
+ };
+
qcom,glink_pkt {
compatible = "qcom,glinkpkt";
@@ -1505,6 +1550,13 @@
qcom,config-reg = <0x17990434>;
};
+ qcom,msm-gladiator-v3@17900000 {
+ compatible = "qcom,msm-gladiator-v3";
+ reg = <0x17900000 0xd080>;
+ reg-names = "gladiator_base";
+ interrupts = <0 17 0>;
+ };
+
cmd_db: qcom,cmd-db@861e0000 {
compatible = "qcom,cmd-db";
reg = <0x861e0000 0x4000>;
@@ -1619,3 +1671,4 @@
#include "sdm845-smp2p.dtsi"
#include "sdm845-camera.dtsi"
#include "sdm845-bus.dtsi"
+#include "sdm845-vidc.dtsi"
diff --git a/arch/arm64/configs/ranchu64_defconfig b/arch/arm64/configs/ranchu64_defconfig
new file mode 100644
index 0000000..fc55008
--- /dev/null
+++ b/arch/arm64/configs/ranchu64_defconfig
@@ -0,0 +1,312 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_POSIX_MQUEUE=y
+CONFIG_AUDIT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_ARCH_MMAP_RND_BITS=24
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+CONFIG_ARCH_VEXPRESS=y
+CONFIG_NR_CPUS=4
+CONFIG_PREEMPT=y
+CONFIG_KSM=y
+CONFIG_SECCOMP=y
+CONFIG_ARMV8_DEPRECATED=y
+CONFIG_SWP_EMULATION=y
+CONFIG_CP15_BARRIER_EMULATION=y
+CONFIG_SETEND_EMULATION=y
+CONFIG_CMDLINE="console=ttyAMA0"
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_COMPAT=y
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
+CONFIG_PM_DEBUG=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_INET_DIAG_DESTROY=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_LRO is not set
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_RPFILTER=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_TARGET_ECN=y
+CONFIG_IP_NF_TARGET_TTL=y
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_SECURITY=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_AH=y
+CONFIG_IP6_NF_MATCH_EUI64=y
+CONFIG_IP6_NF_MATCH_FRAG=y
+CONFIG_IP6_NF_MATCH_OPTS=y
+CONFIG_IP6_NF_MATCH_HL=y
+CONFIG_IP6_NF_MATCH_IPV6HEADER=y
+CONFIG_IP6_NF_MATCH_MH=y
+CONFIG_IP6_NF_MATCH_RT=y
+CONFIG_IP6_NF_TARGET_HL=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_BRIDGE=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_CLS_U32=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_CLS_ACT=y
+# CONFIG_WIRELESS is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_VIRTIO_BLK=y
+CONFIG_SCSI=y
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=y
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
+CONFIG_NETDEVICES=y
+CONFIG_TUN=y
+CONFIG_VIRTIO_NET=y
+CONFIG_SMC91X=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+# CONFIG_WLAN is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+CONFIG_KEYBOARD_GOLDFISH_EVENTS=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_INPUT_TABLET=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+# CONFIG_SERIO_SERPORT is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_VIRTIO_CONSOLE=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_BATTERY_GOLDFISH=y
+# CONFIG_HWMON is not set
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_FB=y
+CONFIG_FB_GOLDFISH=y
+CONFIG_FB_SIMPLE=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_HIDRAW=y
+CONFIG_UHID=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_ACRUX=y
+CONFIG_HID_ACRUX_FF=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_PRODIKEYS=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_DRAGONRISE=y
+CONFIG_DRAGONRISE_FF=y
+CONFIG_HID_EMS_FF=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_KEYTOUCH=y
+CONFIG_HID_KYE=y
+CONFIG_HID_WALTOP=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_TWINHAN=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LCPOWER=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_LOGITECH_DJ=y
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGIG940_FF=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_HID_ORTEK=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_PANTHERLORD_FF=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_PICOLCD=y
+CONFIG_HID_PRIMAX=y
+CONFIG_HID_SAITEK=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SPEEDLINK=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_HID_GREENASIA=y
+CONFIG_GREENASIA_FF=y
+CONFIG_HID_SMARTJOYPLUS=y
+CONFIG_SMARTJOYPLUS_FF=y
+CONFIG_HID_TIVO=y
+CONFIG_HID_TOPSEED=y
+CONFIG_HID_THRUSTMASTER=y
+CONFIG_HID_WACOM=y
+CONFIG_HID_WIIMOTE=y
+CONFIG_HID_ZEROPLUS=y
+CONFIG_HID_ZYDACRON=y
+# CONFIG_USB_SUPPORT is not set
+CONFIG_RTC_CLASS=y
+CONFIG_VIRTIO_MMIO=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_SYNC=y
+CONFIG_SW_SYNC=y
+CONFIG_SW_SYNC_USER=y
+CONFIG_ION=y
+CONFIG_GOLDFISH_AUDIO=y
+CONFIG_GOLDFISH=y
+CONFIG_GOLDFISH_PIPE=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_QUOTA=y
+CONFIG_FUSE_FS=y
+CONFIG_CUSE=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_FS=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_PANIC_TIMEOUT=5
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+# CONFIG_FTRACE is not set
+CONFIG_ATOMIC64_SELFTEST=y
+CONFIG_DEBUG_RODATA=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_SELINUX=y
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index e70996b..36fe5d29 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -9,10 +9,10 @@
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
-CONFIG_CGROUP_SCHEDTUNE=y
-CONFIG_RT_GROUP_SCHED=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHEDTUNE=y
+CONFIG_RT_GROUP_SCHED=y
CONFIG_SCHED_HMP=y
CONFIG_SCHED_HMP_CSTATE_AWARE=y
CONFIG_NAMESPACES=y
@@ -235,7 +235,6 @@
CONFIG_PPP_DEFLATE=y
CONFIG_PPP_MPPE=y
CONFIG_USB_USBNET=y
-CONFIG_WIL6210=m
CONFIG_INPUT_EVDEV=y
CONFIG_KEYBOARD_GPIO=y
# CONFIG_INPUT_MOUSE is not set
@@ -277,7 +276,6 @@
CONFIG_VIDEO_ADV_DEBUG=y
CONFIG_VIDEO_FIXED_MINOR_RANGES=y
CONFIG_V4L_PLATFORM_DRIVERS=y
-CONFIG_SPECTRA_CAMERA=y
CONFIG_MSM_VIDC_V4L2=y
CONFIG_MSM_VIDC_VMEM=y
CONFIG_MSM_VIDC_GOVERNORS=y
@@ -365,6 +363,7 @@
CONFIG_MSM_GLINK=y
CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
+CONFIG_MSM_SPCOM=y
CONFIG_TRACER_PKT=y
CONFIG_QTI_RPMH_API=y
CONFIG_MSM_SMP2P=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 80a75a9..df1025b 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -8,12 +8,12 @@
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_SCHEDTUNE=y
CONFIG_CGROUP_SCHED=y
CONFIG_RT_GROUP_SCHED=y
-CONFIG_CGROUP_FREEZER=y
-CONFIG_CGROUP_CPUACCT=y
-CONFIG_CGROUP_DEBUG=y
CONFIG_SCHED_HMP=y
CONFIG_SCHED_HMP_CSTATE_AWARE=y
CONFIG_NAMESPACES=y
@@ -240,7 +240,6 @@
CONFIG_PPP_BSDCOMP=y
CONFIG_PPP_DEFLATE=y
CONFIG_PPP_MPPE=y
-CONFIG_WIL6210=m
CONFIG_INPUT_EVDEV=y
CONFIG_KEYBOARD_GPIO=y
# CONFIG_INPUT_MOUSE is not set
@@ -284,7 +283,6 @@
CONFIG_VIDEO_ADV_DEBUG=y
CONFIG_VIDEO_FIXED_MINOR_RANGES=y
CONFIG_V4L_PLATFORM_DRIVERS=y
-CONFIG_SPECTRA_CAMERA=y
CONFIG_MSM_VIDC_V4L2=y
CONFIG_MSM_VIDC_VMEM=y
CONFIG_MSM_VIDC_GOVERNORS=y
@@ -376,6 +374,7 @@
CONFIG_MSM_GLINK=y
CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
+CONFIG_MSM_SPCOM=y
CONFIG_TRACER_PKT=y
CONFIG_QTI_RPMH_API=y
CONFIG_MSM_SMP2P=y
diff --git a/arch/arm64/crypto/aes-ce-ccm-core.S b/arch/arm64/crypto/aes-ce-ccm-core.S
index a2a7fbc..3363560 100644
--- a/arch/arm64/crypto/aes-ce-ccm-core.S
+++ b/arch/arm64/crypto/aes-ce-ccm-core.S
@@ -9,6 +9,7 @@
*/
#include <linux/linkage.h>
+#include <asm/assembler.h>
.text
.arch armv8-a+crypto
@@ -19,7 +20,7 @@
*/
ENTRY(ce_aes_ccm_auth_data)
ldr w8, [x3] /* leftover from prev round? */
- ld1 {v0.2d}, [x0] /* load mac */
+ ld1 {v0.16b}, [x0] /* load mac */
cbz w8, 1f
sub w8, w8, #16
eor v1.16b, v1.16b, v1.16b
@@ -31,7 +32,7 @@
beq 8f /* out of input? */
cbnz w8, 0b
eor v0.16b, v0.16b, v1.16b
-1: ld1 {v3.2d}, [x4] /* load first round key */
+1: ld1 {v3.16b}, [x4] /* load first round key */
prfm pldl1strm, [x1]
cmp w5, #12 /* which key size? */
add x6, x4, #16
@@ -41,17 +42,17 @@
mov v5.16b, v3.16b
b 4f
2: mov v4.16b, v3.16b
- ld1 {v5.2d}, [x6], #16 /* load 2nd round key */
+ ld1 {v5.16b}, [x6], #16 /* load 2nd round key */
3: aese v0.16b, v4.16b
aesmc v0.16b, v0.16b
-4: ld1 {v3.2d}, [x6], #16 /* load next round key */
+4: ld1 {v3.16b}, [x6], #16 /* load next round key */
aese v0.16b, v5.16b
aesmc v0.16b, v0.16b
-5: ld1 {v4.2d}, [x6], #16 /* load next round key */
+5: ld1 {v4.16b}, [x6], #16 /* load next round key */
subs w7, w7, #3
aese v0.16b, v3.16b
aesmc v0.16b, v0.16b
- ld1 {v5.2d}, [x6], #16 /* load next round key */
+ ld1 {v5.16b}, [x6], #16 /* load next round key */
bpl 3b
aese v0.16b, v4.16b
subs w2, w2, #16 /* last data? */
@@ -60,7 +61,7 @@
ld1 {v1.16b}, [x1], #16 /* load next input block */
eor v0.16b, v0.16b, v1.16b /* xor with mac */
bne 1b
-6: st1 {v0.2d}, [x0] /* store mac */
+6: st1 {v0.16b}, [x0] /* store mac */
beq 10f
adds w2, w2, #16
beq 10f
@@ -79,7 +80,7 @@
adds w7, w7, #1
bne 9b
eor v0.16b, v0.16b, v1.16b
- st1 {v0.2d}, [x0]
+ st1 {v0.16b}, [x0]
10: str w8, [x3]
ret
ENDPROC(ce_aes_ccm_auth_data)
@@ -89,27 +90,27 @@
* u32 rounds);
*/
ENTRY(ce_aes_ccm_final)
- ld1 {v3.2d}, [x2], #16 /* load first round key */
- ld1 {v0.2d}, [x0] /* load mac */
+ ld1 {v3.16b}, [x2], #16 /* load first round key */
+ ld1 {v0.16b}, [x0] /* load mac */
cmp w3, #12 /* which key size? */
sub w3, w3, #2 /* modified # of rounds */
- ld1 {v1.2d}, [x1] /* load 1st ctriv */
+ ld1 {v1.16b}, [x1] /* load 1st ctriv */
bmi 0f
bne 3f
mov v5.16b, v3.16b
b 2f
0: mov v4.16b, v3.16b
-1: ld1 {v5.2d}, [x2], #16 /* load next round key */
+1: ld1 {v5.16b}, [x2], #16 /* load next round key */
aese v0.16b, v4.16b
aesmc v0.16b, v0.16b
aese v1.16b, v4.16b
aesmc v1.16b, v1.16b
-2: ld1 {v3.2d}, [x2], #16 /* load next round key */
+2: ld1 {v3.16b}, [x2], #16 /* load next round key */
aese v0.16b, v5.16b
aesmc v0.16b, v0.16b
aese v1.16b, v5.16b
aesmc v1.16b, v1.16b
-3: ld1 {v4.2d}, [x2], #16 /* load next round key */
+3: ld1 {v4.16b}, [x2], #16 /* load next round key */
subs w3, w3, #3
aese v0.16b, v3.16b
aesmc v0.16b, v0.16b
@@ -120,47 +121,47 @@
aese v1.16b, v4.16b
/* final round key cancels out */
eor v0.16b, v0.16b, v1.16b /* en-/decrypt the mac */
- st1 {v0.2d}, [x0] /* store result */
+ st1 {v0.16b}, [x0] /* store result */
ret
ENDPROC(ce_aes_ccm_final)
.macro aes_ccm_do_crypt,enc
ldr x8, [x6, #8] /* load lower ctr */
- ld1 {v0.2d}, [x5] /* load mac */
- rev x8, x8 /* keep swabbed ctr in reg */
+ ld1 {v0.16b}, [x5] /* load mac */
+CPU_LE( rev x8, x8 ) /* keep swabbed ctr in reg */
0: /* outer loop */
- ld1 {v1.1d}, [x6] /* load upper ctr */
+ ld1 {v1.8b}, [x6] /* load upper ctr */
prfm pldl1strm, [x1]
add x8, x8, #1
rev x9, x8
cmp w4, #12 /* which key size? */
sub w7, w4, #2 /* get modified # of rounds */
ins v1.d[1], x9 /* no carry in lower ctr */
- ld1 {v3.2d}, [x3] /* load first round key */
+ ld1 {v3.16b}, [x3] /* load first round key */
add x10, x3, #16
bmi 1f
bne 4f
mov v5.16b, v3.16b
b 3f
1: mov v4.16b, v3.16b
- ld1 {v5.2d}, [x10], #16 /* load 2nd round key */
+ ld1 {v5.16b}, [x10], #16 /* load 2nd round key */
2: /* inner loop: 3 rounds, 2x interleaved */
aese v0.16b, v4.16b
aesmc v0.16b, v0.16b
aese v1.16b, v4.16b
aesmc v1.16b, v1.16b
-3: ld1 {v3.2d}, [x10], #16 /* load next round key */
+3: ld1 {v3.16b}, [x10], #16 /* load next round key */
aese v0.16b, v5.16b
aesmc v0.16b, v0.16b
aese v1.16b, v5.16b
aesmc v1.16b, v1.16b
-4: ld1 {v4.2d}, [x10], #16 /* load next round key */
+4: ld1 {v4.16b}, [x10], #16 /* load next round key */
subs w7, w7, #3
aese v0.16b, v3.16b
aesmc v0.16b, v0.16b
aese v1.16b, v3.16b
aesmc v1.16b, v1.16b
- ld1 {v5.2d}, [x10], #16 /* load next round key */
+ ld1 {v5.16b}, [x10], #16 /* load next round key */
bpl 2b
aese v0.16b, v4.16b
aese v1.16b, v4.16b
@@ -177,14 +178,14 @@
eor v0.16b, v0.16b, v2.16b /* xor mac with pt ^ rk[last] */
st1 {v1.16b}, [x0], #16 /* write output block */
bne 0b
- rev x8, x8
- st1 {v0.2d}, [x5] /* store mac */
+CPU_LE( rev x8, x8 )
+ st1 {v0.16b}, [x5] /* store mac */
str x8, [x6, #8] /* store lsb end of ctr (BE) */
5: ret
6: eor v0.16b, v0.16b, v5.16b /* final round mac */
eor v1.16b, v1.16b, v5.16b /* final round enc */
- st1 {v0.2d}, [x5] /* store mac */
+ st1 {v0.16b}, [x5] /* store mac */
add w2, w2, #16 /* process partial tail block */
7: ldrb w9, [x1], #1 /* get 1 byte of input */
umov w6, v1.b[0] /* get top crypted ctr byte */
diff --git a/arch/arm64/crypto/aes-ce-cipher.c b/arch/arm64/crypto/aes-ce-cipher.c
index f7bd9bf..50d9fe1 100644
--- a/arch/arm64/crypto/aes-ce-cipher.c
+++ b/arch/arm64/crypto/aes-ce-cipher.c
@@ -47,24 +47,24 @@
kernel_neon_begin_partial(4);
__asm__(" ld1 {v0.16b}, %[in] ;"
- " ld1 {v1.2d}, [%[key]], #16 ;"
+ " ld1 {v1.16b}, [%[key]], #16 ;"
" cmp %w[rounds], #10 ;"
" bmi 0f ;"
" bne 3f ;"
" mov v3.16b, v1.16b ;"
" b 2f ;"
"0: mov v2.16b, v1.16b ;"
- " ld1 {v3.2d}, [%[key]], #16 ;"
+ " ld1 {v3.16b}, [%[key]], #16 ;"
"1: aese v0.16b, v2.16b ;"
" aesmc v0.16b, v0.16b ;"
- "2: ld1 {v1.2d}, [%[key]], #16 ;"
+ "2: ld1 {v1.16b}, [%[key]], #16 ;"
" aese v0.16b, v3.16b ;"
" aesmc v0.16b, v0.16b ;"
- "3: ld1 {v2.2d}, [%[key]], #16 ;"
+ "3: ld1 {v2.16b}, [%[key]], #16 ;"
" subs %w[rounds], %w[rounds], #3 ;"
" aese v0.16b, v1.16b ;"
" aesmc v0.16b, v0.16b ;"
- " ld1 {v3.2d}, [%[key]], #16 ;"
+ " ld1 {v3.16b}, [%[key]], #16 ;"
" bpl 1b ;"
" aese v0.16b, v2.16b ;"
" eor v0.16b, v0.16b, v3.16b ;"
@@ -92,24 +92,24 @@
kernel_neon_begin_partial(4);
__asm__(" ld1 {v0.16b}, %[in] ;"
- " ld1 {v1.2d}, [%[key]], #16 ;"
+ " ld1 {v1.16b}, [%[key]], #16 ;"
" cmp %w[rounds], #10 ;"
" bmi 0f ;"
" bne 3f ;"
" mov v3.16b, v1.16b ;"
" b 2f ;"
"0: mov v2.16b, v1.16b ;"
- " ld1 {v3.2d}, [%[key]], #16 ;"
+ " ld1 {v3.16b}, [%[key]], #16 ;"
"1: aesd v0.16b, v2.16b ;"
" aesimc v0.16b, v0.16b ;"
- "2: ld1 {v1.2d}, [%[key]], #16 ;"
+ "2: ld1 {v1.16b}, [%[key]], #16 ;"
" aesd v0.16b, v3.16b ;"
" aesimc v0.16b, v0.16b ;"
- "3: ld1 {v2.2d}, [%[key]], #16 ;"
+ "3: ld1 {v2.16b}, [%[key]], #16 ;"
" subs %w[rounds], %w[rounds], #3 ;"
" aesd v0.16b, v1.16b ;"
" aesimc v0.16b, v0.16b ;"
- " ld1 {v3.2d}, [%[key]], #16 ;"
+ " ld1 {v3.16b}, [%[key]], #16 ;"
" bpl 1b ;"
" aesd v0.16b, v2.16b ;"
" eor v0.16b, v0.16b, v3.16b ;"
@@ -173,7 +173,12 @@
u32 *rki = ctx->key_enc + (i * kwords);
u32 *rko = rki + kwords;
+#ifndef CONFIG_CPU_BIG_ENDIAN
rko[0] = ror32(aes_sub(rki[kwords - 1]), 8) ^ rcon[i] ^ rki[0];
+#else
+ rko[0] = rol32(aes_sub(rki[kwords - 1]), 8) ^ (rcon[i] << 24) ^
+ rki[0];
+#endif
rko[1] = rko[0] ^ rki[1];
rko[2] = rko[1] ^ rki[2];
rko[3] = rko[2] ^ rki[3];
diff --git a/arch/arm64/crypto/aes-ce.S b/arch/arm64/crypto/aes-ce.S
index 78f3cfe..b46093d 100644
--- a/arch/arm64/crypto/aes-ce.S
+++ b/arch/arm64/crypto/aes-ce.S
@@ -10,6 +10,7 @@
*/
#include <linux/linkage.h>
+#include <asm/assembler.h>
#define AES_ENTRY(func) ENTRY(ce_ ## func)
#define AES_ENDPROC(func) ENDPROC(ce_ ## func)
diff --git a/arch/arm64/crypto/aes-modes.S b/arch/arm64/crypto/aes-modes.S
index f6e372c..838dad5 100644
--- a/arch/arm64/crypto/aes-modes.S
+++ b/arch/arm64/crypto/aes-modes.S
@@ -193,15 +193,16 @@
cbz w6, .Lcbcencloop
ld1 {v0.16b}, [x5] /* get iv */
- enc_prepare w3, x2, x5
+ enc_prepare w3, x2, x6
.Lcbcencloop:
ld1 {v1.16b}, [x1], #16 /* get next pt block */
eor v0.16b, v0.16b, v1.16b /* ..and xor with iv */
- encrypt_block v0, w3, x2, x5, w6
+ encrypt_block v0, w3, x2, x6, w7
st1 {v0.16b}, [x0], #16
subs w4, w4, #1
bne .Lcbcencloop
+ st1 {v0.16b}, [x5] /* return iv */
ret
AES_ENDPROC(aes_cbc_encrypt)
@@ -211,7 +212,7 @@
cbz w6, .LcbcdecloopNx
ld1 {v7.16b}, [x5] /* get iv */
- dec_prepare w3, x2, x5
+ dec_prepare w3, x2, x6
.LcbcdecloopNx:
#if INTERLEAVE >= 2
@@ -248,7 +249,7 @@
.Lcbcdecloop:
ld1 {v1.16b}, [x1], #16 /* get next ct block */
mov v0.16b, v1.16b /* ...and copy to v0 */
- decrypt_block v0, w3, x2, x5, w6
+ decrypt_block v0, w3, x2, x6, w7
eor v0.16b, v0.16b, v7.16b /* xor with iv => pt */
mov v7.16b, v1.16b /* ct is next iv */
st1 {v0.16b}, [x0], #16
@@ -256,6 +257,7 @@
bne .Lcbcdecloop
.Lcbcdecout:
FRAME_POP
+ st1 {v7.16b}, [x5] /* return iv */
ret
AES_ENDPROC(aes_cbc_decrypt)
@@ -267,24 +269,15 @@
AES_ENTRY(aes_ctr_encrypt)
FRAME_PUSH
- cbnz w6, .Lctrfirst /* 1st time around? */
- umov x5, v4.d[1] /* keep swabbed ctr in reg */
- rev x5, x5
-#if INTERLEAVE >= 2
- cmn w5, w4 /* 32 bit overflow? */
- bcs .Lctrinc
- add x5, x5, #1 /* increment BE ctr */
- b .LctrincNx
-#else
- b .Lctrinc
-#endif
-.Lctrfirst:
+ cbz w6, .Lctrnotfirst /* 1st time around? */
enc_prepare w3, x2, x6
ld1 {v4.16b}, [x5]
- umov x5, v4.d[1] /* keep swabbed ctr in reg */
- rev x5, x5
+
+.Lctrnotfirst:
+ umov x8, v4.d[1] /* keep swabbed ctr in reg */
+ rev x8, x8
#if INTERLEAVE >= 2
- cmn w5, w4 /* 32 bit overflow? */
+ cmn w8, w4 /* 32 bit overflow? */
bcs .Lctrloop
.LctrloopNx:
subs w4, w4, #INTERLEAVE
@@ -292,11 +285,11 @@
#if INTERLEAVE == 2
mov v0.8b, v4.8b
mov v1.8b, v4.8b
- rev x7, x5
- add x5, x5, #1
+ rev x7, x8
+ add x8, x8, #1
ins v0.d[1], x7
- rev x7, x5
- add x5, x5, #1
+ rev x7, x8
+ add x8, x8, #1
ins v1.d[1], x7
ld1 {v2.16b-v3.16b}, [x1], #32 /* get 2 input blocks */
do_encrypt_block2x
@@ -305,7 +298,7 @@
st1 {v0.16b-v1.16b}, [x0], #32
#else
ldr q8, =0x30000000200000001 /* addends 1,2,3[,0] */
- dup v7.4s, w5
+ dup v7.4s, w8
mov v0.16b, v4.16b
add v7.4s, v7.4s, v8.4s
mov v1.16b, v4.16b
@@ -323,18 +316,12 @@
eor v2.16b, v7.16b, v2.16b
eor v3.16b, v5.16b, v3.16b
st1 {v0.16b-v3.16b}, [x0], #64
- add x5, x5, #INTERLEAVE
+ add x8, x8, #INTERLEAVE
#endif
- cbz w4, .LctroutNx
-.LctrincNx:
- rev x7, x5
+ rev x7, x8
ins v4.d[1], x7
+ cbz w4, .Lctrout
b .LctrloopNx
-.LctroutNx:
- sub x5, x5, #1
- rev x7, x5
- ins v4.d[1], x7
- b .Lctrout
.Lctr1x:
adds w4, w4, #INTERLEAVE
beq .Lctrout
@@ -342,30 +329,39 @@
.Lctrloop:
mov v0.16b, v4.16b
encrypt_block v0, w3, x2, x6, w7
+
+ adds x8, x8, #1 /* increment BE ctr */
+ rev x7, x8
+ ins v4.d[1], x7
+ bcs .Lctrcarry /* overflow? */
+
+.Lctrcarrydone:
subs w4, w4, #1
bmi .Lctrhalfblock /* blocks < 0 means 1/2 block */
ld1 {v3.16b}, [x1], #16
eor v3.16b, v0.16b, v3.16b
st1 {v3.16b}, [x0], #16
- beq .Lctrout
-.Lctrinc:
- adds x5, x5, #1 /* increment BE ctr */
- rev x7, x5
- ins v4.d[1], x7
- bcc .Lctrloop /* no overflow? */
+ bne .Lctrloop
+
+.Lctrout:
+ st1 {v4.16b}, [x5] /* return next CTR value */
+ FRAME_POP
+ ret
+
+.Lctrhalfblock:
+ ld1 {v3.8b}, [x1]
+ eor v3.8b, v0.8b, v3.8b
+ st1 {v3.8b}, [x0]
+ FRAME_POP
+ ret
+
+.Lctrcarry:
umov x7, v4.d[0] /* load upper word of ctr */
rev x7, x7 /* ... to handle the carry */
add x7, x7, #1
rev x7, x7
ins v4.d[0], x7
- b .Lctrloop
-.Lctrhalfblock:
- ld1 {v3.8b}, [x1]
- eor v3.8b, v0.8b, v3.8b
- st1 {v3.8b}, [x0]
-.Lctrout:
- FRAME_POP
- ret
+ b .Lctrcarrydone
AES_ENDPROC(aes_ctr_encrypt)
.ltorg
@@ -386,7 +382,8 @@
.endm
.Lxts_mul_x:
- .word 1, 0, 0x87, 0
+CPU_LE( .quad 1, 0x87 )
+CPU_BE( .quad 0x87, 1 )
AES_ENTRY(aes_xts_encrypt)
FRAME_PUSH
diff --git a/arch/arm64/crypto/aes-neon.S b/arch/arm64/crypto/aes-neon.S
index b93170e..85f07ea 100644
--- a/arch/arm64/crypto/aes-neon.S
+++ b/arch/arm64/crypto/aes-neon.S
@@ -9,6 +9,7 @@
*/
#include <linux/linkage.h>
+#include <asm/assembler.h>
#define AES_ENTRY(func) ENTRY(neon_ ## func)
#define AES_ENDPROC(func) ENDPROC(neon_ ## func)
@@ -83,13 +84,13 @@
.endm
.macro do_block, enc, in, rounds, rk, rkp, i
- ld1 {v15.16b}, [\rk]
+ ld1 {v15.4s}, [\rk]
add \rkp, \rk, #16
mov \i, \rounds
1111: eor \in\().16b, \in\().16b, v15.16b /* ^round key */
tbl \in\().16b, {\in\().16b}, v13.16b /* ShiftRows */
sub_bytes \in
- ld1 {v15.16b}, [\rkp], #16
+ ld1 {v15.4s}, [\rkp], #16
subs \i, \i, #1
beq 2222f
.if \enc == 1
@@ -229,7 +230,7 @@
.endm
.macro do_block_2x, enc, in0, in1 rounds, rk, rkp, i
- ld1 {v15.16b}, [\rk]
+ ld1 {v15.4s}, [\rk]
add \rkp, \rk, #16
mov \i, \rounds
1111: eor \in0\().16b, \in0\().16b, v15.16b /* ^round key */
@@ -237,7 +238,7 @@
sub_bytes_2x \in0, \in1
tbl \in0\().16b, {\in0\().16b}, v13.16b /* ShiftRows */
tbl \in1\().16b, {\in1\().16b}, v13.16b /* ShiftRows */
- ld1 {v15.16b}, [\rkp], #16
+ ld1 {v15.4s}, [\rkp], #16
subs \i, \i, #1
beq 2222f
.if \enc == 1
@@ -254,7 +255,7 @@
.endm
.macro do_block_4x, enc, in0, in1, in2, in3, rounds, rk, rkp, i
- ld1 {v15.16b}, [\rk]
+ ld1 {v15.4s}, [\rk]
add \rkp, \rk, #16
mov \i, \rounds
1111: eor \in0\().16b, \in0\().16b, v15.16b /* ^round key */
@@ -266,7 +267,7 @@
tbl \in1\().16b, {\in1\().16b}, v13.16b /* ShiftRows */
tbl \in2\().16b, {\in2\().16b}, v13.16b /* ShiftRows */
tbl \in3\().16b, {\in3\().16b}, v13.16b /* ShiftRows */
- ld1 {v15.16b}, [\rkp], #16
+ ld1 {v15.4s}, [\rkp], #16
subs \i, \i, #1
beq 2222f
.if \enc == 1
@@ -306,12 +307,16 @@
.text
.align 4
.LForward_ShiftRows:
- .byte 0x0, 0x5, 0xa, 0xf, 0x4, 0x9, 0xe, 0x3
- .byte 0x8, 0xd, 0x2, 0x7, 0xc, 0x1, 0x6, 0xb
+CPU_LE( .byte 0x0, 0x5, 0xa, 0xf, 0x4, 0x9, 0xe, 0x3 )
+CPU_LE( .byte 0x8, 0xd, 0x2, 0x7, 0xc, 0x1, 0x6, 0xb )
+CPU_BE( .byte 0xb, 0x6, 0x1, 0xc, 0x7, 0x2, 0xd, 0x8 )
+CPU_BE( .byte 0x3, 0xe, 0x9, 0x4, 0xf, 0xa, 0x5, 0x0 )
.LReverse_ShiftRows:
- .byte 0x0, 0xd, 0xa, 0x7, 0x4, 0x1, 0xe, 0xb
- .byte 0x8, 0x5, 0x2, 0xf, 0xc, 0x9, 0x6, 0x3
+CPU_LE( .byte 0x0, 0xd, 0xa, 0x7, 0x4, 0x1, 0xe, 0xb )
+CPU_LE( .byte 0x8, 0x5, 0x2, 0xf, 0xc, 0x9, 0x6, 0x3 )
+CPU_BE( .byte 0x3, 0x6, 0x9, 0xc, 0xf, 0x2, 0x5, 0x8 )
+CPU_BE( .byte 0xb, 0xe, 0x1, 0x4, 0x7, 0xa, 0xd, 0x0 )
.LForward_Sbox:
.byte 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5
diff --git a/arch/arm64/crypto/ghash-ce-core.S b/arch/arm64/crypto/ghash-ce-core.S
index dc45701..f0bb9f0b 100644
--- a/arch/arm64/crypto/ghash-ce-core.S
+++ b/arch/arm64/crypto/ghash-ce-core.S
@@ -29,8 +29,8 @@
* struct ghash_key const *k, const char *head)
*/
ENTRY(pmull_ghash_update)
- ld1 {SHASH.16b}, [x3]
- ld1 {XL.16b}, [x1]
+ ld1 {SHASH.2d}, [x3]
+ ld1 {XL.2d}, [x1]
movi MASK.16b, #0xe1
ext SHASH2.16b, SHASH.16b, SHASH.16b, #8
shl MASK.2d, MASK.2d, #57
@@ -74,6 +74,6 @@
cbnz w0, 0b
- st1 {XL.16b}, [x1]
+ st1 {XL.2d}, [x1]
ret
ENDPROC(pmull_ghash_update)
diff --git a/arch/arm64/crypto/sha1-ce-core.S b/arch/arm64/crypto/sha1-ce-core.S
index 033aae6..c98e7e8 100644
--- a/arch/arm64/crypto/sha1-ce-core.S
+++ b/arch/arm64/crypto/sha1-ce-core.S
@@ -78,7 +78,7 @@
ld1r {k3.4s}, [x6]
/* load state */
- ldr dga, [x0]
+ ld1 {dgav.4s}, [x0]
ldr dgb, [x0, #16]
/* load sha1_ce_state::finalize */
@@ -144,7 +144,7 @@
b 1b
/* store new state */
-3: str dga, [x0]
+3: st1 {dgav.4s}, [x0]
str dgb, [x0, #16]
ret
ENDPROC(sha1_ce_transform)
diff --git a/arch/arm64/crypto/sha2-ce-core.S b/arch/arm64/crypto/sha2-ce-core.S
index 5df9d9d..01cfee0 100644
--- a/arch/arm64/crypto/sha2-ce-core.S
+++ b/arch/arm64/crypto/sha2-ce-core.S
@@ -85,7 +85,7 @@
ld1 {v12.4s-v15.4s}, [x8]
/* load state */
- ldp dga, dgb, [x0]
+ ld1 {dgav.4s, dgbv.4s}, [x0]
/* load sha256_ce_state::finalize */
ldr w4, [x0, #:lo12:sha256_ce_offsetof_finalize]
@@ -148,6 +148,6 @@
b 1b
/* store new state */
-3: stp dga, dgb, [x0]
+3: st1 {dgav.4s, dgbv.4s}, [x0]
ret
ENDPROC(sha2_ce_transform)
diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index b71086d..53211a0 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -217,7 +217,7 @@
#define _virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
#else
#define __virt_to_pgoff(kaddr) (((u64)(kaddr) & ~PAGE_OFFSET) / PAGE_SIZE * sizeof(struct page))
-#define __page_to_voff(page) (((u64)(page) & ~VMEMMAP_START) * PAGE_SIZE / sizeof(struct page))
+#define __page_to_voff(kaddr) (((u64)(kaddr) & ~VMEMMAP_START) * PAGE_SIZE / sizeof(struct page))
#define page_to_virt(page) ((void *)((__page_to_voff(page)) | PAGE_OFFSET))
#define virt_to_page(vaddr) ((struct page *)((__virt_to_pgoff(vaddr)) | VMEMMAP_START))
diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h
index e708b3d..61e032f2 100644
--- a/arch/arm64/include/asm/topology.h
+++ b/arch/arm64/include/asm/topology.h
@@ -32,6 +32,14 @@
cpumask_of_node(pcibus_to_node(bus)))
#endif /* CONFIG_NUMA */
+struct sched_domain;
+#ifdef CONFIG_CPU_FREQ
+#define arch_scale_freq_capacity cpufreq_scale_freq_capacity
+extern unsigned long cpufreq_scale_freq_capacity(struct sched_domain *sd, int cpu);
+extern unsigned long cpufreq_scale_max_freq_capacity(int cpu);
+#endif
+#define arch_scale_cpu_capacity scale_cpu_capacity
+extern unsigned long scale_cpu_capacity(struct sched_domain *sd, int cpu);
#include <asm-generic/topology.h>
diff --git a/arch/arm64/include/uapi/asm/ptrace.h b/arch/arm64/include/uapi/asm/ptrace.h
index b5c3933..d1ff83d 100644
--- a/arch/arm64/include/uapi/asm/ptrace.h
+++ b/arch/arm64/include/uapi/asm/ptrace.h
@@ -77,6 +77,7 @@
__uint128_t vregs[32];
__u32 fpsr;
__u32 fpcr;
+ __u32 __reserved[2];
};
struct user_hwdebug_state {
diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c
index 78f3680..da095e8 100644
--- a/arch/arm64/kernel/arm64ksyms.c
+++ b/arch/arm64/kernel/arm64ksyms.c
@@ -29,6 +29,7 @@
#include <linux/arm-smccc.h>
#include <linux/kprobes.h>
+#include <asm/cacheflush.h>
#include <asm/checksum.h>
EXPORT_SYMBOL(copy_page);
@@ -75,3 +76,8 @@
/* arm-smccc */
EXPORT_SYMBOL(arm_smccc_smc);
EXPORT_SYMBOL(arm_smccc_hvc);
+
+ /* caching functions */
+EXPORT_SYMBOL(__dma_inv_area);
+EXPORT_SYMBOL(__dma_clean_area);
+EXPORT_SYMBOL(__dma_flush_area);
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index 16d1b34..324b288 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -687,7 +687,7 @@
mov x0, sp
mov x1, #BAD_SYNC
mov x2, x25
- bl bad_mode
+ bl bad_el0_sync
b ret_to_user
ENDPROC(el0_sync)
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index e0c81da..8eedeef 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -550,6 +550,8 @@
/* (address, ctrl) registers */
limit = regset->n * regset->size;
while (count && offset < limit) {
+ if (count < PTRACE_HBP_ADDR_SZ)
+ return -EINVAL;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &addr,
offset, offset + PTRACE_HBP_ADDR_SZ);
if (ret)
@@ -559,6 +561,8 @@
return ret;
offset += PTRACE_HBP_ADDR_SZ;
+ if (!count)
+ break;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ctrl,
offset, offset + PTRACE_HBP_CTRL_SZ);
if (ret)
@@ -595,7 +599,7 @@
const void *kbuf, const void __user *ubuf)
{
int ret;
- struct user_pt_regs newregs;
+ struct user_pt_regs newregs = task_pt_regs(target)->user_regs;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newregs, 0, -1);
if (ret)
@@ -625,7 +629,8 @@
const void *kbuf, const void __user *ubuf)
{
int ret;
- struct user_fpsimd_state newstate;
+ struct user_fpsimd_state newstate =
+ target->thread.fpsimd_state.user_fpsimd;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newstate, 0, -1);
if (ret)
@@ -649,7 +654,7 @@
const void *kbuf, const void __user *ubuf)
{
int ret;
- unsigned long tls;
+ unsigned long tls = target->thread.tp_value;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1);
if (ret)
@@ -675,7 +680,8 @@
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
- int syscallno, ret;
+ int syscallno = task_pt_regs(target)->syscallno;
+ int ret;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &syscallno, 0, -1);
if (ret)
@@ -947,7 +953,7 @@
const void __user *ubuf)
{
int ret;
- compat_ulong_t tls;
+ compat_ulong_t tls = target->thread.tp_value;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1);
if (ret)
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index bdbaadf..6a4348b1 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -591,6 +591,18 @@
#else
#define acpi_table_parse_madt(...) do { } while (0)
#endif
+void (*__smp_cross_call)(const struct cpumask *, unsigned int);
+DEFINE_PER_CPU(bool, pending_ipi);
+
+void smp_cross_call_common(const struct cpumask *cpumask, unsigned int func)
+{
+ unsigned int cpu;
+
+ for_each_cpu(cpu, cpumask)
+ per_cpu(pending_ipi, cpu) = true;
+
+ __smp_cross_call(cpumask, func);
+}
/*
* Enumerate the possible CPU set from the device tree and build the
@@ -735,8 +747,6 @@
}
}
-void (*__smp_cross_call)(const struct cpumask *, unsigned int);
-
void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int))
{
__smp_cross_call = fn;
@@ -785,18 +795,18 @@
void arch_send_call_function_ipi_mask(const struct cpumask *mask)
{
- smp_cross_call(mask, IPI_CALL_FUNC);
+ smp_cross_call_common(mask, IPI_CALL_FUNC);
}
void arch_send_call_function_single_ipi(int cpu)
{
- smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC);
+ smp_cross_call_common(cpumask_of(cpu), IPI_CALL_FUNC);
}
#ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL
void arch_send_wakeup_ipi_mask(const struct cpumask *mask)
{
- smp_cross_call(mask, IPI_WAKEUP);
+ smp_cross_call_common(mask, IPI_WAKEUP);
}
#endif
@@ -809,15 +819,20 @@
#endif
static DEFINE_RAW_SPINLOCK(stop_lock);
+
+DEFINE_PER_CPU(struct pt_regs, regs_before_stop);
+
/*
* ipi_cpu_stop - handle IPI from smp_send_stop()
*/
-static void ipi_cpu_stop(unsigned int cpu)
+static void ipi_cpu_stop(unsigned int cpu, struct pt_regs *regs)
{
if (system_state == SYSTEM_BOOTING ||
system_state == SYSTEM_RUNNING) {
+ per_cpu(regs_before_stop, cpu) = *regs;
raw_spin_lock(&stop_lock);
pr_crit("CPU%u: stopping\n", cpu);
+ show_regs(regs);
dump_stack();
raw_spin_unlock(&stop_lock);
}
@@ -857,7 +872,7 @@
case IPI_CPU_STOP:
irq_enter();
- ipi_cpu_stop(cpu);
+ ipi_cpu_stop(cpu, regs);
irq_exit();
break;
@@ -892,19 +907,20 @@
if ((unsigned)ipinr < NR_IPI)
trace_ipi_exit_rcuidle(ipi_types[ipinr]);
+ per_cpu(pending_ipi, cpu) = false;
set_irq_regs(old_regs);
}
void smp_send_reschedule(int cpu)
{
BUG_ON(cpu_is_offline(cpu));
- smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE);
+ smp_cross_call_common(cpumask_of(cpu), IPI_RESCHEDULE);
}
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
void tick_broadcast(const struct cpumask *mask)
{
- smp_cross_call(mask, IPI_TIMER);
+ smp_cross_call_common(mask, IPI_TIMER);
}
#endif
@@ -921,7 +937,7 @@
if (system_state == SYSTEM_BOOTING ||
system_state == SYSTEM_RUNNING)
pr_crit("SMP: stopping secondary CPUs\n");
- smp_cross_call(&mask, IPI_CPU_STOP);
+ smp_cross_call_common(&mask, IPI_CPU_STOP);
}
/* Wait up to one second for other CPUs to stop */
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index 349b131..3b40f26 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -20,6 +20,7 @@
#include <linux/of.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/sched_energy.h>
#include <asm/cputype.h>
#include <asm/topology.h>
@@ -35,7 +36,7 @@
* rebalance_domains for all idle cores and the cpu_power can be updated
* during this sequence.
*/
-static DEFINE_PER_CPU(unsigned long, cpu_scale);
+static DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;
unsigned long arch_scale_freq_power(struct sched_domain *sd, int cpu)
{
@@ -47,6 +48,22 @@
per_cpu(cpu_scale, cpu) = power;
}
+unsigned long scale_cpu_capacity(struct sched_domain *sd, int cpu)
+{
+#ifdef CONFIG_CPU_FREQ
+ unsigned long max_freq_scale = cpufreq_scale_max_freq_capacity(cpu);
+
+ return per_cpu(cpu_scale, cpu) * max_freq_scale >> SCHED_CAPACITY_SHIFT;
+#else
+ return per_cpu(cpu_scale, cpu);
+#endif
+}
+
+static void set_capacity_scale(unsigned int cpu, unsigned long capacity)
+{
+ per_cpu(cpu_scale, cpu) = capacity;
+}
+
static int __init get_cpu_for_node(struct device_node *node)
{
struct device_node *cpu_node;
@@ -371,11 +388,67 @@
struct cpu_topology cpu_topology[NR_CPUS];
EXPORT_SYMBOL_GPL(cpu_topology);
+/* sd energy functions */
+static inline
+const struct sched_group_energy * const cpu_cluster_energy(int cpu)
+{
+ struct sched_group_energy *sge = sge_array[cpu][SD_LEVEL1];
+
+ if (!sge) {
+ pr_warn("Invalid sched_group_energy for Cluster%d\n", cpu);
+ return NULL;
+ }
+
+ return sge;
+}
+
+static inline
+const struct sched_group_energy * const cpu_core_energy(int cpu)
+{
+ struct sched_group_energy *sge = sge_array[cpu][SD_LEVEL0];
+
+ if (!sge) {
+ pr_warn("Invalid sched_group_energy for CPU%d\n", cpu);
+ return NULL;
+ }
+
+ return sge;
+}
+
const struct cpumask *cpu_coregroup_mask(int cpu)
{
return &cpu_topology[cpu].core_sibling;
}
+static inline int cpu_corepower_flags(void)
+{
+ return SD_SHARE_PKG_RESOURCES | SD_SHARE_POWERDOMAIN | \
+ SD_SHARE_CAP_STATES;
+}
+
+static struct sched_domain_topology_level arm64_topology[] = {
+#ifdef CONFIG_SCHED_MC
+ { cpu_coregroup_mask, cpu_corepower_flags, cpu_core_energy, SD_INIT_NAME(MC) },
+#endif
+ { cpu_cpu_mask, NULL, cpu_cluster_energy, SD_INIT_NAME(DIE) },
+ { NULL, },
+};
+
+static void update_cpu_capacity(unsigned int cpu)
+{
+ unsigned long capacity = SCHED_CAPACITY_SCALE;
+
+ if (cpu_core_energy(cpu)) {
+ int max_cap_idx = cpu_core_energy(cpu)->nr_cap_states - 1;
+ capacity = cpu_core_energy(cpu)->cap_states[max_cap_idx].cap;
+ }
+
+ set_capacity_scale(cpu, capacity);
+
+ pr_info("CPU%d: update cpu_capacity %lu\n",
+ cpu, arch_scale_cpu_capacity(NULL, cpu));
+}
+
static void update_siblings_masks(unsigned int cpuid)
{
struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
@@ -438,6 +511,7 @@
topology_populated:
update_siblings_masks(cpuid);
update_cpu_power(cpuid);
+ update_cpu_capacity(cpuid);
}
static void __init reset_cpu_topology(void)
@@ -479,10 +553,12 @@
if (of_have_populated_dt() && parse_dt_topology()) {
reset_cpu_topology();
} else {
+ set_sched_topology(arm64_topology);
for_each_possible_cpu(cpu)
update_siblings_masks(cpu);
}
reset_cpu_power();
parse_dt_cpu_power();
+ init_sched_energy_costs();
}
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index a2e2118..d5c4242 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -631,17 +631,34 @@
}
/*
- * bad_mode handles the impossible case in the exception vector.
+ * bad_mode handles the impossible case in the exception vector. This is always
+ * fatal.
*/
asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
{
- siginfo_t info;
- void __user *pc = (void __user *)instruction_pointer(regs);
console_verbose();
pr_crit("Bad mode in %s handler detected on CPU%d, code 0x%08x -- %s\n",
handler[reason], smp_processor_id(), esr,
esr_get_class_string(esr));
+
+ die("Oops - bad mode", regs, 0);
+ local_irq_disable();
+ panic("bad mode");
+}
+
+/*
+ * bad_el0_sync handles unexpected, but potentially recoverable synchronous
+ * exceptions taken from EL0. Unlike bad_mode, this returns.
+ */
+asmlinkage void bad_el0_sync(struct pt_regs *regs, int reason, unsigned int esr)
+{
+ siginfo_t info;
+ void __user *pc = (void __user *)instruction_pointer(regs);
+ console_verbose();
+
+ pr_crit("Bad EL0 synchronous exception detected on CPU%d, code 0x%08x -- %s\n",
+ smp_processor_id(), esr, esr_get_class_string(esr));
__show_regs(regs);
info.si_signo = SIGILL;
@@ -649,7 +666,10 @@
info.si_code = ILL_ILLOPC;
info.si_addr = pc;
- arm64_notify_die("Oops - bad mode", regs, &info, 0);
+ current->thread.fault_address = 0;
+ current->thread.fault_code = 0;
+
+ force_sig_info(info.si_signo, &info, current);
}
void __pte_error(const char *file, int line, unsigned long val)
diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c
index 83037cd..0c848c1 100644
--- a/arch/arm64/kvm/hyp/switch.c
+++ b/arch/arm64/kvm/hyp/switch.c
@@ -85,7 +85,13 @@
write_sysreg(val, hcr_el2);
/* Trap on AArch32 cp15 c15 accesses (EL1 or EL0) */
write_sysreg(1 << 15, hstr_el2);
- /* Make sure we trap PMU access from EL0 to EL2 */
+ /*
+ * Make sure we trap PMU access from EL0 to EL2. Also sanitize
+ * PMSELR_EL0 to make sure it never contains the cycle
+ * counter, which could make a PMXEVCNTR_EL0 access UNDEF at
+ * EL1 instead of being trapped to EL2.
+ */
+ write_sysreg(0, pmselr_el0);
write_sysreg(ARMV8_PMU_USERENR_MASK, pmuserenr_el0);
write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2);
__activate_traps_arch()();
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index e9c55ce..a4312d0 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -658,7 +658,8 @@
static int __init arm64_dma_init(void)
{
- if (swiotlb_force || max_pfn > (arm64_dma_phys_limit >> PAGE_SHIFT))
+ if (swiotlb_force == SWIOTLB_FORCE ||
+ max_pfn > (arm64_dma_phys_limit >> PAGE_SHIFT))
swiotlb = 1;
return atomic_pool_init();
diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c
index 2e49bd2..45bec62 100644
--- a/arch/arm64/mm/hugetlbpage.c
+++ b/arch/arm64/mm/hugetlbpage.c
@@ -51,20 +51,8 @@
*pgsize = PAGE_SIZE;
if (!pte_cont(pte))
return 1;
- if (!pgd_present(*pgd)) {
- VM_BUG_ON(!pgd_present(*pgd));
- return 1;
- }
pud = pud_offset(pgd, addr);
- if (!pud_present(*pud)) {
- VM_BUG_ON(!pud_present(*pud));
- return 1;
- }
pmd = pmd_offset(pud, addr);
- if (!pmd_present(*pmd)) {
- VM_BUG_ON(!pmd_present(*pmd));
- return 1;
- }
if ((pte_t *)pmd == ptep) {
*pgsize = PMD_SIZE;
return CONT_PMDS;
@@ -212,7 +200,7 @@
ncontig = find_num_contig(mm, addr, cpte, *cpte, &pgsize);
/* save the 1st pte to return */
pte = ptep_get_and_clear(mm, addr, cpte);
- for (i = 1; i < ncontig; ++i) {
+ for (i = 1, addr += pgsize; i < ncontig; ++i, addr += pgsize) {
/*
* If HW_AFDBM is enabled, then the HW could
* turn on the dirty bit for any of the page
@@ -250,8 +238,8 @@
pfn = pte_pfn(*cpte);
ncontig = find_num_contig(vma->vm_mm, addr, cpte,
*cpte, &pgsize);
- for (i = 0; i < ncontig; ++i, ++cpte) {
- changed = ptep_set_access_flags(vma, addr, cpte,
+ for (i = 0; i < ncontig; ++i, ++cpte, addr += pgsize) {
+ changed |= ptep_set_access_flags(vma, addr, cpte,
pfn_pte(pfn,
hugeprot),
dirty);
@@ -273,7 +261,7 @@
cpte = huge_pte_offset(mm, addr);
ncontig = find_num_contig(mm, addr, cpte, *cpte, &pgsize);
- for (i = 0; i < ncontig; ++i, ++cpte)
+ for (i = 0; i < ncontig; ++i, ++cpte, addr += pgsize)
ptep_set_wrprotect(mm, addr, cpte);
} else {
ptep_set_wrprotect(mm, addr, ptep);
@@ -291,7 +279,7 @@
cpte = huge_pte_offset(vma->vm_mm, addr);
ncontig = find_num_contig(vma->vm_mm, addr, cpte,
*cpte, &pgsize);
- for (i = 0; i < ncontig; ++i, ++cpte)
+ for (i = 0; i < ncontig; ++i, ++cpte, addr += pgsize)
ptep_clear_flush(vma, addr, cpte);
} else {
ptep_clear_flush(vma, addr, ptep);
diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index af38d02..0b9492e 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -403,8 +403,11 @@
*/
void __init mem_init(void)
{
- if (swiotlb_force || max_pfn > (arm64_dma_phys_limit >> PAGE_SHIFT))
+ if (swiotlb_force == SWIOTLB_FORCE ||
+ max_pfn > (arm64_dma_phys_limit >> PAGE_SHIFT))
swiotlb_init(1);
+ else
+ swiotlb_force = SWIOTLB_NO_FORCE;
set_max_mapnr(pfn_to_page(max_pfn) - mem_map);
diff --git a/arch/blackfin/kernel/ptrace.c b/arch/blackfin/kernel/ptrace.c
index 8d79286..360d996 100644
--- a/arch/blackfin/kernel/ptrace.c
+++ b/arch/blackfin/kernel/ptrace.c
@@ -270,7 +270,7 @@
switch (bfin_mem_access_type(addr, to_copy)) {
case BFIN_MEM_ACCESS_CORE:
case BFIN_MEM_ACCESS_CORE_ONLY:
- copied = access_process_vm(child, addr, &tmp,
+ copied = ptrace_access_vm(child, addr, &tmp,
to_copy, FOLL_FORCE);
if (copied)
break;
@@ -323,7 +323,7 @@
switch (bfin_mem_access_type(addr, to_copy)) {
case BFIN_MEM_ACCESS_CORE:
case BFIN_MEM_ACCESS_CORE_ONLY:
- copied = access_process_vm(child, addr, &data,
+ copied = ptrace_access_vm(child, addr, &data,
to_copy,
FOLL_FORCE | FOLL_WRITE);
break;
diff --git a/arch/cris/arch-v32/kernel/ptrace.c b/arch/cris/arch-v32/kernel/ptrace.c
index f0df654..fe1f9cf7b 100644
--- a/arch/cris/arch-v32/kernel/ptrace.c
+++ b/arch/cris/arch-v32/kernel/ptrace.c
@@ -147,7 +147,7 @@
/* The trampoline page is globally mapped, no page table to traverse.*/
tmp = *(unsigned long*)addr;
} else {
- copied = access_process_vm(child, addr, &tmp, sizeof(tmp), FOLL_FORCE);
+ copied = ptrace_access_vm(child, addr, &tmp, sizeof(tmp), FOLL_FORCE);
if (copied != sizeof(tmp))
break;
diff --git a/arch/cris/boot/rescue/Makefile b/arch/cris/boot/rescue/Makefile
index 52bd0bd..d98edbb 100644
--- a/arch/cris/boot/rescue/Makefile
+++ b/arch/cris/boot/rescue/Makefile
@@ -10,6 +10,9 @@
asflags-y += $(LINUXINCLUDE)
ccflags-y += -O2 $(LINUXINCLUDE)
+
+ifdef CONFIG_ETRAX_AXISFLASHMAP
+
arch-$(CONFIG_ETRAX_ARCH_V10) = v10
arch-$(CONFIG_ETRAX_ARCH_V32) = v32
@@ -28,6 +31,11 @@
$(call if_changed,objcopy)
cp -p $(obj)/rescue.bin $(objtree)
+else
+$(obj)/rescue.bin:
+
+endif
+
$(obj)/testrescue.bin: $(obj)/testrescue.o
$(OBJCOPY) $(OBJCOPYFLAGS) $(obj)/testrescue.o tr.bin
# Pad it to 784 bytes
diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c
index 31aa8c0..36f660d 100644
--- a/arch/ia64/kernel/ptrace.c
+++ b/arch/ia64/kernel/ptrace.c
@@ -1159,7 +1159,7 @@
case PTRACE_PEEKTEXT:
case PTRACE_PEEKDATA:
/* read word at location addr */
- if (access_process_vm(child, addr, &data, sizeof(data),
+ if (ptrace_access_vm(child, addr, &data, sizeof(data),
FOLL_FORCE)
!= sizeof(data))
return -EIO;
diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c
index 7e71a4e..5fcbdcd 100644
--- a/arch/mips/kernel/ptrace32.c
+++ b/arch/mips/kernel/ptrace32.c
@@ -69,7 +69,7 @@
if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0)
break;
- copied = access_process_vm(child, (u64)addrOthers, &tmp,
+ copied = ptrace_access_vm(child, (u64)addrOthers, &tmp,
sizeof(tmp), FOLL_FORCE);
if (copied != sizeof(tmp))
break;
@@ -178,7 +178,7 @@
if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0)
break;
ret = 0;
- if (access_process_vm(child, (u64)addrOthers, &data,
+ if (ptrace_access_vm(child, (u64)addrOthers, &data,
sizeof(data),
FOLL_FORCE | FOLL_WRITE) == sizeof(data))
break;
diff --git a/arch/mips/kvm/entry.c b/arch/mips/kvm/entry.c
index 6a02b3a..e92fb19 100644
--- a/arch/mips/kvm/entry.c
+++ b/arch/mips/kvm/entry.c
@@ -521,6 +521,9 @@
uasm_i_and(&p, V0, V0, AT);
uasm_i_lui(&p, AT, ST0_CU0 >> 16);
uasm_i_or(&p, V0, V0, AT);
+#ifdef CONFIG_64BIT
+ uasm_i_ori(&p, V0, V0, ST0_SX | ST0_UX);
+#endif
uasm_i_mtc0(&p, V0, C0_STATUS);
uasm_i_ehb(&p);
@@ -643,7 +646,7 @@
/* Setup status register for running guest in UM */
uasm_i_ori(&p, V1, V1, ST0_EXL | KSU_USER | ST0_IE);
- UASM_i_LA(&p, AT, ~(ST0_CU0 | ST0_MX));
+ UASM_i_LA(&p, AT, ~(ST0_CU0 | ST0_MX | ST0_SX | ST0_UX));
uasm_i_and(&p, V1, V1, AT);
uasm_i_mtc0(&p, V1, C0_STATUS);
uasm_i_ehb(&p);
diff --git a/arch/mips/kvm/mips.c b/arch/mips/kvm/mips.c
index 06a60b1..29ec9ab 100644
--- a/arch/mips/kvm/mips.c
+++ b/arch/mips/kvm/mips.c
@@ -360,8 +360,8 @@
dump_handler("kvm_exit", gebase + 0x2000, vcpu->arch.vcpu_run);
/* Invalidate the icache for these ranges */
- local_flush_icache_range((unsigned long)gebase,
- (unsigned long)gebase + ALIGN(size, PAGE_SIZE));
+ flush_icache_range((unsigned long)gebase,
+ (unsigned long)gebase + ALIGN(size, PAGE_SIZE));
/*
* Allocate comm page for guest kernel, a TLB will be reserved for
diff --git a/arch/parisc/include/asm/bitops.h b/arch/parisc/include/asm/bitops.h
index 3f9406d..da87943 100644
--- a/arch/parisc/include/asm/bitops.h
+++ b/arch/parisc/include/asm/bitops.h
@@ -6,7 +6,7 @@
#endif
#include <linux/compiler.h>
-#include <asm/types.h> /* for BITS_PER_LONG/SHIFT_PER_LONG */
+#include <asm/types.h>
#include <asm/byteorder.h>
#include <asm/barrier.h>
#include <linux/atomic.h>
@@ -17,6 +17,12 @@
* to include/asm-i386/bitops.h or kerneldoc
*/
+#if __BITS_PER_LONG == 64
+#define SHIFT_PER_LONG 6
+#else
+#define SHIFT_PER_LONG 5
+#endif
+
#define CHOP_SHIFTCOUNT(x) (((unsigned long) (x)) & (BITS_PER_LONG - 1))
diff --git a/arch/parisc/include/uapi/asm/bitsperlong.h b/arch/parisc/include/uapi/asm/bitsperlong.h
index e0a23c7..07fa7e5 100644
--- a/arch/parisc/include/uapi/asm/bitsperlong.h
+++ b/arch/parisc/include/uapi/asm/bitsperlong.h
@@ -3,10 +3,8 @@
#if defined(__LP64__)
#define __BITS_PER_LONG 64
-#define SHIFT_PER_LONG 6
#else
#define __BITS_PER_LONG 32
-#define SHIFT_PER_LONG 5
#endif
#include <asm-generic/bitsperlong.h>
diff --git a/arch/parisc/include/uapi/asm/swab.h b/arch/parisc/include/uapi/asm/swab.h
index e78403b..928e1bb 100644
--- a/arch/parisc/include/uapi/asm/swab.h
+++ b/arch/parisc/include/uapi/asm/swab.h
@@ -1,6 +1,7 @@
#ifndef _PARISC_SWAB_H
#define _PARISC_SWAB_H
+#include <asm/bitsperlong.h>
#include <linux/types.h>
#include <linux/compiler.h>
@@ -38,7 +39,7 @@
}
#define __arch_swab32 __arch_swab32
-#if BITS_PER_LONG > 32
+#if __BITS_PER_LONG > 32
/*
** From "PA-RISC 2.0 Architecture", HP Professional Books.
** See Appendix I page 8 , "Endian Byte Swapping".
@@ -61,6 +62,6 @@
return x;
}
#define __arch_swab64 __arch_swab64
-#endif /* BITS_PER_LONG > 32 */
+#endif /* __BITS_PER_LONG > 32 */
#endif /* _PARISC_SWAB_H */
diff --git a/arch/parisc/kernel/time.c b/arch/parisc/kernel/time.c
index 325f30d..47ef8fd 100644
--- a/arch/parisc/kernel/time.c
+++ b/arch/parisc/kernel/time.c
@@ -289,9 +289,26 @@
cr16_hz = 100 * PAGE0->mem_10msec; /* Hz */
- /* register at clocksource framework */
- clocksource_register_hz(&clocksource_cr16, cr16_hz);
-
/* register as sched_clock source */
sched_clock_register(read_cr16_sched_clock, BITS_PER_LONG, cr16_hz);
}
+
+static int __init init_cr16_clocksource(void)
+{
+ /*
+ * The cr16 interval timers are not syncronized across CPUs, so mark
+ * them unstable and lower rating on SMP systems.
+ */
+ if (num_online_cpus() > 1) {
+ clocksource_cr16.flags = CLOCK_SOURCE_UNSTABLE;
+ clocksource_cr16.rating = 0;
+ }
+
+ /* register at clocksource framework */
+ clocksource_register_hz(&clocksource_cr16,
+ 100 * PAGE0->mem_10msec);
+
+ return 0;
+}
+
+device_initcall(init_cr16_clocksource);
diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c
index 8ff9253..1a0b4f6 100644
--- a/arch/parisc/mm/fault.c
+++ b/arch/parisc/mm/fault.c
@@ -234,7 +234,7 @@
tsk->comm, code, address);
print_vma_addr(KERN_CONT " in ", regs->iaoq[0]);
- pr_cont(" trap #%lu: %s%c", code, trap_name(code),
+ pr_cont("\ntrap #%lu: %s%c", code, trap_name(code),
vma ? ',':'\n');
if (vma)
diff --git a/arch/powerpc/boot/ps3-head.S b/arch/powerpc/boot/ps3-head.S
index b6fcbaf..3dc44b0 100644
--- a/arch/powerpc/boot/ps3-head.S
+++ b/arch/powerpc/boot/ps3-head.S
@@ -57,11 +57,6 @@
bctr
1:
- /* Save the value at addr zero for a null pointer write check later. */
-
- li r4, 0
- lwz r3, 0(r4)
-
/* Primary delays then goes to _zimage_start in wrapper. */
or 31, 31, 31 /* db16cyc */
diff --git a/arch/powerpc/boot/ps3.c b/arch/powerpc/boot/ps3.c
index 4ec2d86..a05558a 100644
--- a/arch/powerpc/boot/ps3.c
+++ b/arch/powerpc/boot/ps3.c
@@ -119,13 +119,12 @@
flush_cache((void *)0x100, 512);
}
-void platform_init(unsigned long null_check)
+void platform_init(void)
{
const u32 heapsize = 0x1000000 - (u32)_end; /* 16MiB */
void *chosen;
unsigned long ft_addr;
u64 rm_size;
- unsigned long val;
console_ops.write = ps3_console_write;
platform_ops.exit = ps3_exit;
@@ -153,11 +152,6 @@
printf(" flat tree at 0x%lx\n\r", ft_addr);
- val = *(unsigned long *)0;
-
- if (val != null_check)
- printf("null check failed: %lx != %lx\n\r", val, null_check);
-
((kernel_entry_t)0)(ft_addr, 0, NULL);
ps3_exit();
diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper
index 404b3aa..76fe3cc 100755
--- a/arch/powerpc/boot/wrapper
+++ b/arch/powerpc/boot/wrapper
@@ -181,6 +181,28 @@
elf32-powerpc) format=elf32ppc ;;
esac
+ld_version()
+{
+ # Poached from scripts/ld-version.sh, but we don't want to call that because
+ # this script (wrapper) is distributed separately from the kernel source.
+ # Extract linker version number from stdin and turn into single number.
+ awk '{
+ gsub(".*\\)", "");
+ gsub(".*version ", "");
+ gsub("-.*", "");
+ split($1,a, ".");
+ print a[1]*100000000 + a[2]*1000000 + a[3]*10000;
+ exit
+ }'
+}
+
+# Do not include PT_INTERP segment when linking pie. Non-pie linking
+# just ignores this option.
+LD_VERSION=$(${CROSS}ld --version | ld_version)
+LD_NO_DL_MIN_VERSION=$(echo 2.26 | ld_version)
+if [ "$LD_VERSION" -ge "$LD_NO_DL_MIN_VERSION" ] ; then
+ nodl="--no-dynamic-linker"
+fi
platformo=$object/"$platform".o
lds=$object/zImage.lds
@@ -446,7 +468,7 @@
text_start="-Ttext $link_address"
fi
#link everything
- ${CROSS}ld -m $format -T $lds $text_start $pie -o "$ofile" \
+ ${CROSS}ld -m $format -T $lds $text_start $pie $nodl -o "$ofile" \
$platformo $tmp $object/wrapper.a
rm $tmp
fi
diff --git a/arch/powerpc/include/asm/book3s/64/mmu-hash.h b/arch/powerpc/include/asm/book3s/64/mmu-hash.h
index e407af2..2e6a823 100644
--- a/arch/powerpc/include/asm/book3s/64/mmu-hash.h
+++ b/arch/powerpc/include/asm/book3s/64/mmu-hash.h
@@ -70,7 +70,9 @@
#define HPTE_V_SSIZE_SHIFT 62
#define HPTE_V_AVPN_SHIFT 7
+#define HPTE_V_COMMON_BITS ASM_CONST(0x000fffffffffffff)
#define HPTE_V_AVPN ASM_CONST(0x3fffffffffffff80)
+#define HPTE_V_AVPN_3_0 ASM_CONST(0x000fffffffffff80)
#define HPTE_V_AVPN_VAL(x) (((x) & HPTE_V_AVPN) >> HPTE_V_AVPN_SHIFT)
#define HPTE_V_COMPARE(x,y) (!(((x) ^ (y)) & 0xffffffffffffff80UL))
#define HPTE_V_BOLTED ASM_CONST(0x0000000000000010)
@@ -80,14 +82,16 @@
#define HPTE_V_VALID ASM_CONST(0x0000000000000001)
/*
- * ISA 3.0 have a different HPTE format.
+ * ISA 3.0 has a different HPTE format.
*/
#define HPTE_R_3_0_SSIZE_SHIFT 58
+#define HPTE_R_3_0_SSIZE_MASK (3ull << HPTE_R_3_0_SSIZE_SHIFT)
#define HPTE_R_PP0 ASM_CONST(0x8000000000000000)
#define HPTE_R_TS ASM_CONST(0x4000000000000000)
#define HPTE_R_KEY_HI ASM_CONST(0x3000000000000000)
#define HPTE_R_RPN_SHIFT 12
#define HPTE_R_RPN ASM_CONST(0x0ffffffffffff000)
+#define HPTE_R_RPN_3_0 ASM_CONST(0x01fffffffffff000)
#define HPTE_R_PP ASM_CONST(0x0000000000000003)
#define HPTE_R_PPP ASM_CONST(0x8000000000000003)
#define HPTE_R_N ASM_CONST(0x0000000000000004)
@@ -316,12 +320,43 @@
*/
v = (vpn >> (23 - VPN_SHIFT)) & ~(mmu_psize_defs[psize].avpnm);
v <<= HPTE_V_AVPN_SHIFT;
- if (!cpu_has_feature(CPU_FTR_ARCH_300))
- v |= ((unsigned long) ssize) << HPTE_V_SSIZE_SHIFT;
+ v |= ((unsigned long) ssize) << HPTE_V_SSIZE_SHIFT;
return v;
}
/*
+ * ISA v3.0 defines a new HPTE format, which differs from the old
+ * format in having smaller AVPN and ARPN fields, and the B field
+ * in the second dword instead of the first.
+ */
+static inline unsigned long hpte_old_to_new_v(unsigned long v)
+{
+ /* trim AVPN, drop B */
+ return v & HPTE_V_COMMON_BITS;
+}
+
+static inline unsigned long hpte_old_to_new_r(unsigned long v, unsigned long r)
+{
+ /* move B field from 1st to 2nd dword, trim ARPN */
+ return (r & ~HPTE_R_3_0_SSIZE_MASK) |
+ (((v) >> HPTE_V_SSIZE_SHIFT) << HPTE_R_3_0_SSIZE_SHIFT);
+}
+
+static inline unsigned long hpte_new_to_old_v(unsigned long v, unsigned long r)
+{
+ /* insert B field */
+ return (v & HPTE_V_COMMON_BITS) |
+ ((r & HPTE_R_3_0_SSIZE_MASK) <<
+ (HPTE_V_SSIZE_SHIFT - HPTE_R_3_0_SSIZE_SHIFT));
+}
+
+static inline unsigned long hpte_new_to_old_r(unsigned long r)
+{
+ /* clear out B field */
+ return r & ~HPTE_R_3_0_SSIZE_MASK;
+}
+
+/*
* This function sets the AVPN and L fields of the HPTE appropriately
* using the base page size and actual page size.
*/
@@ -341,12 +376,8 @@
* aligned for the requested page size
*/
static inline unsigned long hpte_encode_r(unsigned long pa, int base_psize,
- int actual_psize, int ssize)
+ int actual_psize)
{
-
- if (cpu_has_feature(CPU_FTR_ARCH_300))
- pa |= ((unsigned long) ssize) << HPTE_R_3_0_SSIZE_SHIFT;
-
/* A 4K page needs no special encoding */
if (actual_psize == MMU_PAGE_4K)
return pa & HPTE_R_RPN;
diff --git a/arch/powerpc/include/asm/cpu_has_feature.h b/arch/powerpc/include/asm/cpu_has_feature.h
index b312b15..6e834ca 100644
--- a/arch/powerpc/include/asm/cpu_has_feature.h
+++ b/arch/powerpc/include/asm/cpu_has_feature.h
@@ -23,7 +23,9 @@
{
int i;
+#ifndef __clang__ /* clang can't cope with this */
BUILD_BUG_ON(!__builtin_constant_p(feature));
+#endif
#ifdef CONFIG_JUMP_LABEL_FEATURE_CHECK_DEBUG
if (!static_key_initialized) {
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
index 28350a2..5e12e19 100644
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -546,6 +546,7 @@
u64 tfiar;
u32 cr_tm;
+ u64 xer_tm;
u64 lr_tm;
u64 ctr_tm;
u64 amr_tm;
diff --git a/arch/powerpc/include/asm/mmu.h b/arch/powerpc/include/asm/mmu.h
index e311c25..a244e09 100644
--- a/arch/powerpc/include/asm/mmu.h
+++ b/arch/powerpc/include/asm/mmu.h
@@ -160,7 +160,9 @@
{
int i;
+#ifndef __clang__ /* clang can't cope with this */
BUILD_BUG_ON(!__builtin_constant_p(feature));
+#endif
#ifdef CONFIG_JUMP_LABEL_FEATURE_CHECK_DEBUG
if (!static_key_initialized) {
diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h
index c56ea8c..c4ced1d 100644
--- a/arch/powerpc/include/asm/ppc-opcode.h
+++ b/arch/powerpc/include/asm/ppc-opcode.h
@@ -157,7 +157,7 @@
#define PPC_INST_MCRXR 0x7c000400
#define PPC_INST_MCRXR_MASK 0xfc0007fe
#define PPC_INST_MFSPR_PVR 0x7c1f42a6
-#define PPC_INST_MFSPR_PVR_MASK 0xfc1fffff
+#define PPC_INST_MFSPR_PVR_MASK 0xfc1ffffe
#define PPC_INST_MFTMR 0x7c0002dc
#define PPC_INST_MSGSND 0x7c00019c
#define PPC_INST_MSGCLR 0x7c0001dc
@@ -174,13 +174,13 @@
#define PPC_INST_RFDI 0x4c00004e
#define PPC_INST_RFMCI 0x4c00004c
#define PPC_INST_MFSPR_DSCR 0x7c1102a6
-#define PPC_INST_MFSPR_DSCR_MASK 0xfc1fffff
+#define PPC_INST_MFSPR_DSCR_MASK 0xfc1ffffe
#define PPC_INST_MTSPR_DSCR 0x7c1103a6
-#define PPC_INST_MTSPR_DSCR_MASK 0xfc1fffff
+#define PPC_INST_MTSPR_DSCR_MASK 0xfc1ffffe
#define PPC_INST_MFSPR_DSCR_USER 0x7c0302a6
-#define PPC_INST_MFSPR_DSCR_USER_MASK 0xfc1fffff
+#define PPC_INST_MFSPR_DSCR_USER_MASK 0xfc1ffffe
#define PPC_INST_MTSPR_DSCR_USER 0x7c0303a6
-#define PPC_INST_MTSPR_DSCR_USER_MASK 0xfc1fffff
+#define PPC_INST_MTSPR_DSCR_USER_MASK 0xfc1ffffe
#define PPC_INST_MFVSRD 0x7c000066
#define PPC_INST_MTVSRD 0x7c000166
#define PPC_INST_SLBFEE 0x7c0007a7
diff --git a/arch/powerpc/include/uapi/asm/kvm.h b/arch/powerpc/include/uapi/asm/kvm.h
index c93cf35..0fb1326 100644
--- a/arch/powerpc/include/uapi/asm/kvm.h
+++ b/arch/powerpc/include/uapi/asm/kvm.h
@@ -596,6 +596,7 @@
#define KVM_REG_PPC_TM_VSCR (KVM_REG_PPC_TM | KVM_REG_SIZE_U32 | 0x67)
#define KVM_REG_PPC_TM_DSCR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x68)
#define KVM_REG_PPC_TM_TAR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x69)
+#define KVM_REG_PPC_TM_XER (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x6a)
/* PPC64 eXternal Interrupt Controller Specification */
#define KVM_DEV_XICS_GRP_SOURCES 1 /* 64-bit source attributes */
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index caec7bf..c833d88 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -569,6 +569,7 @@
DEFINE(VCPU_VRS_TM, offsetof(struct kvm_vcpu, arch.vr_tm.vr));
DEFINE(VCPU_VRSAVE_TM, offsetof(struct kvm_vcpu, arch.vrsave_tm));
DEFINE(VCPU_CR_TM, offsetof(struct kvm_vcpu, arch.cr_tm));
+ DEFINE(VCPU_XER_TM, offsetof(struct kvm_vcpu, arch.xer_tm));
DEFINE(VCPU_LR_TM, offsetof(struct kvm_vcpu, arch.lr_tm));
DEFINE(VCPU_CTR_TM, offsetof(struct kvm_vcpu, arch.ctr_tm));
DEFINE(VCPU_AMR_TM, offsetof(struct kvm_vcpu, arch.amr_tm));
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
index 5c31369..a5dd493 100644
--- a/arch/powerpc/kernel/eeh_driver.c
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -545,7 +545,7 @@
static void *__eeh_clear_pe_frozen_state(void *data, void *flag)
{
struct eeh_pe *pe = (struct eeh_pe *)data;
- bool *clear_sw_state = flag;
+ bool clear_sw_state = *(bool *)flag;
int i, rc = 1;
for (i = 0; rc && i < 3; i++)
diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S
index 04c546e..1f7f908 100644
--- a/arch/powerpc/kernel/head_64.S
+++ b/arch/powerpc/kernel/head_64.S
@@ -214,9 +214,9 @@
*/
_GLOBAL(book3e_start_thread)
LOAD_REG_IMMEDIATE(r5, MSR_KERNEL)
- cmpi 0, r3, 0
+ cmpwi r3, 0
beq 10f
- cmpi 0, r3, 1
+ cmpwi r3, 1
beq 11f
/* If the thread id is invalid, just exit. */
b 13f
@@ -241,9 +241,9 @@
* r3 = the thread physical id
*/
_GLOBAL(book3e_stop_thread)
- cmpi 0, r3, 0
+ cmpwi r3, 0
beq 10f
- cmpi 0, r3, 1
+ cmpwi r3, 1
beq 10f
/* If the thread id is invalid, just exit. */
b 13f
diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c
index 6ca9a2f..35f5244 100644
--- a/arch/powerpc/kernel/ibmebus.c
+++ b/arch/powerpc/kernel/ibmebus.c
@@ -180,6 +180,7 @@
static int ibmebus_create_devices(const struct of_device_id *matches)
{
struct device_node *root, *child;
+ struct device *dev;
int ret = 0;
root = of_find_node_by_path("/");
@@ -188,9 +189,12 @@
if (!of_match_node(matches, child))
continue;
- if (bus_find_device(&ibmebus_bus_type, NULL, child,
- ibmebus_match_node))
+ dev = bus_find_device(&ibmebus_bus_type, NULL, child,
+ ibmebus_match_node);
+ if (dev) {
+ put_device(dev);
continue;
+ }
ret = ibmebus_create_device(child);
if (ret) {
@@ -262,6 +266,7 @@
const char *buf, size_t count)
{
struct device_node *dn = NULL;
+ struct device *dev;
char *path;
ssize_t rc = 0;
@@ -269,8 +274,10 @@
if (!path)
return -ENOMEM;
- if (bus_find_device(&ibmebus_bus_type, NULL, path,
- ibmebus_match_path)) {
+ dev = bus_find_device(&ibmebus_bus_type, NULL, path,
+ ibmebus_match_path);
+ if (dev) {
+ put_device(dev);
printk(KERN_WARNING "%s: %s has already been probed\n",
__func__, path);
rc = -EEXIST;
@@ -307,6 +314,7 @@
if ((dev = bus_find_device(&ibmebus_bus_type, NULL, path,
ibmebus_match_path))) {
of_device_unregister(to_platform_device(dev));
+ put_device(dev);
kfree(path);
return count;
diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S
index 93cf7a5..030d72d 100644
--- a/arch/powerpc/kernel/misc_32.S
+++ b/arch/powerpc/kernel/misc_32.S
@@ -296,7 +296,7 @@
lis r3, KERNELBASE@h
iccci 0,r3
#endif
-#elif CONFIG_FSL_BOOKE
+#elif defined(CONFIG_FSL_BOOKE)
BEGIN_FTR_SECTION
mfspr r3,SPRN_L1CSR0
ori r3,r3,L1CSR0_CFI|L1CSR0_CLFC
diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
index 88ac964..1e8c572 100644
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -2747,6 +2747,9 @@
cpu_pkg = call_prom("instance-to-package", 1, 1, prom_cpu);
+ if (!PHANDLE_VALID(cpu_pkg))
+ return;
+
prom_getprop(cpu_pkg, "reg", &rval, sizeof(rval));
prom.cpu = be32_to_cpu(rval);
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index b1ec62f..5c8f12f 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -463,6 +463,10 @@
flush_fp_to_thread(target);
+ for (i = 0; i < 32 ; i++)
+ buf[i] = target->thread.TS_FPR(i);
+ buf[32] = target->thread.fp_state.fpscr;
+
/* copy to local buffer then write that out */
i = user_regset_copyin(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
if (i)
@@ -672,6 +676,9 @@
flush_altivec_to_thread(target);
flush_vsx_to_thread(target);
+ for (i = 0; i < 32 ; i++)
+ buf[i] = target->thread.fp_state.fpr[i][TS_VSRLOWOFFSET];
+
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
buf, 0, 32 * sizeof(double));
if (!ret)
@@ -1019,6 +1026,10 @@
flush_fp_to_thread(target);
flush_altivec_to_thread(target);
+ for (i = 0; i < 32; i++)
+ buf[i] = target->thread.TS_CKFPR(i);
+ buf[32] = target->thread.ckfp_state.fpscr;
+
/* copy to local buffer then write that out */
i = user_regset_copyin(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
if (i)
@@ -1283,6 +1294,9 @@
flush_altivec_to_thread(target);
flush_vsx_to_thread(target);
+ for (i = 0; i < 32 ; i++)
+ buf[i] = target->thread.ckfp_state.fpr[i][TS_VSRLOWOFFSET];
+
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
buf, 0, 32 * sizeof(double));
if (!ret)
diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c
index 010b7b3..1e887f3 100644
--- a/arch/powerpc/kernel/ptrace32.c
+++ b/arch/powerpc/kernel/ptrace32.c
@@ -73,7 +73,7 @@
if (get_user(addrOthers, (u32 __user * __user *)addr) != 0)
break;
- copied = access_process_vm(child, (u64)addrOthers, &tmp,
+ copied = ptrace_access_vm(child, (u64)addrOthers, &tmp,
sizeof(tmp), FOLL_FORCE);
if (copied != sizeof(tmp))
break;
@@ -178,7 +178,7 @@
if (get_user(addrOthers, (u32 __user * __user *)addr) != 0)
break;
ret = 0;
- if (access_process_vm(child, (u64)addrOthers, &tmp,
+ if (ptrace_access_vm(child, (u64)addrOthers, &tmp,
sizeof(tmp),
FOLL_FORCE | FOLL_WRITE) == sizeof(tmp))
break;
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index 3686471..094deb6 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -1288,6 +1288,9 @@
case KVM_REG_PPC_TM_CR:
*val = get_reg_val(id, vcpu->arch.cr_tm);
break;
+ case KVM_REG_PPC_TM_XER:
+ *val = get_reg_val(id, vcpu->arch.xer_tm);
+ break;
case KVM_REG_PPC_TM_LR:
*val = get_reg_val(id, vcpu->arch.lr_tm);
break;
@@ -1498,6 +1501,9 @@
case KVM_REG_PPC_TM_CR:
vcpu->arch.cr_tm = set_reg_val(id, *val);
break;
+ case KVM_REG_PPC_TM_XER:
+ vcpu->arch.xer_tm = set_reg_val(id, *val);
+ break;
case KVM_REG_PPC_TM_LR:
vcpu->arch.lr_tm = set_reg_val(id, *val);
break;
diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
index 99b4e9d..5420d06 100644
--- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c
+++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
@@ -653,6 +653,8 @@
HPTE_V_ABSENT);
do_tlbies(kvm, &rb, 1, global_invalidates(kvm, flags),
true);
+ /* Don't lose R/C bit updates done by hardware */
+ r |= be64_to_cpu(hpte[1]) & (HPTE_R_R | HPTE_R_C);
hpte[1] = cpu_to_be64(r);
}
}
diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
index c3c1d1b..6f81adb 100644
--- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
+++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
@@ -2600,11 +2600,13 @@
mfctr r7
mfspr r8, SPRN_AMR
mfspr r10, SPRN_TAR
+ mfxer r11
std r5, VCPU_LR_TM(r9)
stw r6, VCPU_CR_TM(r9)
std r7, VCPU_CTR_TM(r9)
std r8, VCPU_AMR_TM(r9)
std r10, VCPU_TAR_TM(r9)
+ std r11, VCPU_XER_TM(r9)
/* Restore r12 as trap number. */
lwz r12, VCPU_TRAP(r9)
@@ -2697,11 +2699,13 @@
ld r7, VCPU_CTR_TM(r4)
ld r8, VCPU_AMR_TM(r4)
ld r9, VCPU_TAR_TM(r4)
+ ld r10, VCPU_XER_TM(r4)
mtlr r5
mtcr r6
mtctr r7
mtspr SPRN_AMR, r8
mtspr SPRN_TAR, r9
+ mtxer r10
/*
* Load up PPR and DSCR values but don't put them in the actual SPRs
diff --git a/arch/powerpc/mm/hash_native_64.c b/arch/powerpc/mm/hash_native_64.c
index 83ddc0e..ad9fd52 100644
--- a/arch/powerpc/mm/hash_native_64.c
+++ b/arch/powerpc/mm/hash_native_64.c
@@ -221,13 +221,18 @@
return -1;
hpte_v = hpte_encode_v(vpn, psize, apsize, ssize) | vflags | HPTE_V_VALID;
- hpte_r = hpte_encode_r(pa, psize, apsize, ssize) | rflags;
+ hpte_r = hpte_encode_r(pa, psize, apsize) | rflags;
if (!(vflags & HPTE_V_BOLTED)) {
DBG_LOW(" i=%x hpte_v=%016lx, hpte_r=%016lx\n",
i, hpte_v, hpte_r);
}
+ if (cpu_has_feature(CPU_FTR_ARCH_300)) {
+ hpte_r = hpte_old_to_new_r(hpte_v, hpte_r);
+ hpte_v = hpte_old_to_new_v(hpte_v);
+ }
+
hptep->r = cpu_to_be64(hpte_r);
/* Guarantee the second dword is visible before the valid bit */
eieio();
@@ -295,6 +300,8 @@
vpn, want_v & HPTE_V_AVPN, slot, newpp);
hpte_v = be64_to_cpu(hptep->v);
+ if (cpu_has_feature(CPU_FTR_ARCH_300))
+ hpte_v = hpte_new_to_old_v(hpte_v, be64_to_cpu(hptep->r));
/*
* We need to invalidate the TLB always because hpte_remove doesn't do
* a tlb invalidate. If a hash bucket gets full, we "evict" a more/less
@@ -309,6 +316,8 @@
native_lock_hpte(hptep);
/* recheck with locks held */
hpte_v = be64_to_cpu(hptep->v);
+ if (cpu_has_feature(CPU_FTR_ARCH_300))
+ hpte_v = hpte_new_to_old_v(hpte_v, be64_to_cpu(hptep->r));
if (unlikely(!HPTE_V_COMPARE(hpte_v, want_v) ||
!(hpte_v & HPTE_V_VALID))) {
ret = -1;
@@ -350,6 +359,8 @@
for (i = 0; i < HPTES_PER_GROUP; i++) {
hptep = htab_address + slot;
hpte_v = be64_to_cpu(hptep->v);
+ if (cpu_has_feature(CPU_FTR_ARCH_300))
+ hpte_v = hpte_new_to_old_v(hpte_v, be64_to_cpu(hptep->r));
if (HPTE_V_COMPARE(hpte_v, want_v) && (hpte_v & HPTE_V_VALID))
/* HPTE matches */
@@ -409,6 +420,8 @@
want_v = hpte_encode_avpn(vpn, bpsize, ssize);
native_lock_hpte(hptep);
hpte_v = be64_to_cpu(hptep->v);
+ if (cpu_has_feature(CPU_FTR_ARCH_300))
+ hpte_v = hpte_new_to_old_v(hpte_v, be64_to_cpu(hptep->r));
/*
* We need to invalidate the TLB always because hpte_remove doesn't do
@@ -467,6 +480,8 @@
want_v = hpte_encode_avpn(vpn, psize, ssize);
native_lock_hpte(hptep);
hpte_v = be64_to_cpu(hptep->v);
+ if (cpu_has_feature(CPU_FTR_ARCH_300))
+ hpte_v = hpte_new_to_old_v(hpte_v, be64_to_cpu(hptep->r));
/* Even if we miss, we need to invalidate the TLB */
if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID))
@@ -504,6 +519,10 @@
/* Look at the 8 bit LP value */
unsigned int lp = (hpte_r >> LP_SHIFT) & ((1 << LP_BITS) - 1);
+ if (cpu_has_feature(CPU_FTR_ARCH_300)) {
+ hpte_v = hpte_new_to_old_v(hpte_v, hpte_r);
+ hpte_r = hpte_new_to_old_r(hpte_r);
+ }
if (!(hpte_v & HPTE_V_LARGE)) {
size = MMU_PAGE_4K;
a_size = MMU_PAGE_4K;
@@ -512,11 +531,7 @@
a_size = hpte_page_sizes[lp] >> 4;
}
/* This works for all page sizes, and for 256M and 1T segments */
- if (cpu_has_feature(CPU_FTR_ARCH_300))
- *ssize = hpte_r >> HPTE_R_3_0_SSIZE_SHIFT;
- else
- *ssize = hpte_v >> HPTE_V_SSIZE_SHIFT;
-
+ *ssize = hpte_v >> HPTE_V_SSIZE_SHIFT;
shift = mmu_psize_defs[size].shift;
avpn = (HPTE_V_AVPN_VAL(hpte_v) & ~mmu_psize_defs[size].avpnm);
@@ -639,6 +654,9 @@
want_v = hpte_encode_avpn(vpn, psize, ssize);
native_lock_hpte(hptep);
hpte_v = be64_to_cpu(hptep->v);
+ if (cpu_has_feature(CPU_FTR_ARCH_300))
+ hpte_v = hpte_new_to_old_v(hpte_v,
+ be64_to_cpu(hptep->r));
if (!HPTE_V_COMPARE(hpte_v, want_v) ||
!(hpte_v & HPTE_V_VALID))
native_unlock_hpte(hptep);
diff --git a/arch/powerpc/mm/pgtable-radix.c b/arch/powerpc/mm/pgtable-radix.c
index 688b545..9a25dce 100644
--- a/arch/powerpc/mm/pgtable-radix.c
+++ b/arch/powerpc/mm/pgtable-radix.c
@@ -65,7 +65,7 @@
if (!pmdp)
return -ENOMEM;
if (map_page_size == PMD_SIZE) {
- ptep = (pte_t *)pudp;
+ ptep = pmdp_ptep(pmdp);
goto set_the_pte;
}
ptep = pte_alloc_kernel(pmdp, ea);
@@ -90,7 +90,7 @@
}
pmdp = pmd_offset(pudp, ea);
if (map_page_size == PMD_SIZE) {
- ptep = (pte_t *)pudp;
+ ptep = pmdp_ptep(pmdp);
goto set_the_pte;
}
if (!pmd_present(*pmdp)) {
@@ -159,7 +159,7 @@
* Allocate Partition table and process table for the
* host.
*/
- BUILD_BUG_ON_MSG((PRTB_SIZE_SHIFT > 23), "Process table size too large.");
+ BUILD_BUG_ON_MSG((PRTB_SIZE_SHIFT > 36), "Process table size too large.");
process_tb = early_alloc_pgtable(1UL << PRTB_SIZE_SHIFT);
/*
* Fill in the process table.
@@ -181,7 +181,7 @@
rts_field = radix__get_tree_size();
- BUILD_BUG_ON_MSG((PATB_SIZE_SHIFT > 24), "Partition table size too large.");
+ BUILD_BUG_ON_MSG((PATB_SIZE_SHIFT > 36), "Partition table size too large.");
partition_tb = early_alloc_pgtable(1UL << PATB_SIZE_SHIFT);
partition_tb->patb0 = cpu_to_be64(rts_field | __pa(init_mm.pgd) |
RADIX_PGD_INDEX_SIZE | PATB_HR);
diff --git a/arch/powerpc/perf/power9-events-list.h b/arch/powerpc/perf/power9-events-list.h
index 6447dc1..929b56d 100644
--- a/arch/powerpc/perf/power9-events-list.h
+++ b/arch/powerpc/perf/power9-events-list.h
@@ -16,7 +16,7 @@
EVENT(PM_ICT_NOSLOT_CYC, 0x100f8)
EVENT(PM_CMPLU_STALL, 0x1e054)
EVENT(PM_INST_CMPL, 0x00002)
-EVENT(PM_BRU_CMPL, 0x40060)
+EVENT(PM_BRU_CMPL, 0x10012)
EVENT(PM_BR_MPRED_CMPL, 0x400f6)
/* All L1 D cache load references counted at finish, gated by reject */
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index d4b33dd..dcdfee0 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -145,7 +145,7 @@
*/
rc = opal_pci_eeh_freeze_clear(phb->opal_id, pe_no,
OPAL_EEH_ACTION_CLEAR_FREEZE_ALL);
- if (rc != OPAL_SUCCESS)
+ if (rc != OPAL_SUCCESS && rc != OPAL_UNSUPPORTED)
pr_warn("%s: Error %lld unfreezing PHB#%d-PE#%d\n",
__func__, rc, phb->hose->global_number, pe_no);
diff --git a/arch/powerpc/platforms/ps3/htab.c b/arch/powerpc/platforms/ps3/htab.c
index cb3c503..cc2b281 100644
--- a/arch/powerpc/platforms/ps3/htab.c
+++ b/arch/powerpc/platforms/ps3/htab.c
@@ -63,7 +63,7 @@
vflags &= ~HPTE_V_SECONDARY;
hpte_v = hpte_encode_v(vpn, psize, apsize, ssize) | vflags | HPTE_V_VALID;
- hpte_r = hpte_encode_r(ps3_mm_phys_to_lpar(pa), psize, apsize, ssize) | rflags;
+ hpte_r = hpte_encode_r(ps3_mm_phys_to_lpar(pa), psize, apsize) | rflags;
spin_lock_irqsave(&ps3_htab_lock, flags);
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index aa35245..f2c98f6 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -145,7 +145,7 @@
hpte_group, vpn, pa, rflags, vflags, psize);
hpte_v = hpte_encode_v(vpn, psize, apsize, ssize) | vflags | HPTE_V_VALID;
- hpte_r = hpte_encode_r(pa, psize, apsize, ssize) | rflags;
+ hpte_r = hpte_encode_r(pa, psize, apsize) | rflags;
if (!(vflags & HPTE_V_BOLTED))
pr_devel(" hpte_v=%016lx, hpte_r=%016lx\n", hpte_v, hpte_r);
diff --git a/arch/powerpc/sysdev/xics/icp-opal.c b/arch/powerpc/sysdev/xics/icp-opal.c
index d38e86f..60c5765 100644
--- a/arch/powerpc/sysdev/xics/icp-opal.c
+++ b/arch/powerpc/sysdev/xics/icp-opal.c
@@ -20,6 +20,7 @@
#include <asm/xics.h>
#include <asm/io.h>
#include <asm/opal.h>
+#include <asm/kvm_ppc.h>
static void icp_opal_teardown_cpu(void)
{
@@ -39,7 +40,26 @@
* Should we be flagging idle loop instead?
* Or creating some task to be scheduled?
*/
- opal_int_eoi((0x00 << 24) | XICS_IPI);
+ if (opal_int_eoi((0x00 << 24) | XICS_IPI) > 0)
+ force_external_irq_replay();
+}
+
+static unsigned int icp_opal_get_xirr(void)
+{
+ unsigned int kvm_xirr;
+ __be32 hw_xirr;
+ int64_t rc;
+
+ /* Handle an interrupt latched by KVM first */
+ kvm_xirr = kvmppc_get_xics_latch();
+ if (kvm_xirr)
+ return kvm_xirr;
+
+ /* Then ask OPAL */
+ rc = opal_int_get_xirr(&hw_xirr, false);
+ if (rc < 0)
+ return 0;
+ return be32_to_cpu(hw_xirr);
}
static unsigned int icp_opal_get_irq(void)
@@ -47,12 +67,8 @@
unsigned int xirr;
unsigned int vec;
unsigned int irq;
- int64_t rc;
- rc = opal_int_get_xirr(&xirr, false);
- if (rc < 0)
- return 0;
- xirr = be32_to_cpu(xirr);
+ xirr = icp_opal_get_xirr();
vec = xirr & 0x00ffffff;
if (vec == XICS_IRQ_SPURIOUS)
return 0;
@@ -67,7 +83,8 @@
xics_mask_unknown_vec(vec);
/* We might learn about it later, so EOI it */
- opal_int_eoi(xirr);
+ if (opal_int_eoi(xirr) > 0)
+ force_external_irq_replay();
return 0;
}
diff --git a/arch/s390/crypto/prng.c b/arch/s390/crypto/prng.c
index 9cc050f..1113389 100644
--- a/arch/s390/crypto/prng.c
+++ b/arch/s390/crypto/prng.c
@@ -507,8 +507,10 @@
prng_data->prngws.byte_counter += n;
prng_data->prngws.reseed_counter += n;
- if (copy_to_user(ubuf, prng_data->buf, chunk))
- return -EFAULT;
+ if (copy_to_user(ubuf, prng_data->buf, chunk)) {
+ ret = -EFAULT;
+ break;
+ }
nbytes -= chunk;
ret += chunk;
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index 9336e824..fc2974b 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -963,6 +963,11 @@
if (target == current)
save_fpu_regs();
+ if (MACHINE_HAS_VX)
+ convert_vx_to_fp(fprs, target->thread.fpu.vxrs);
+ else
+ memcpy(&fprs, target->thread.fpu.fprs, sizeof(fprs));
+
/* If setting FPC, must validate it first. */
if (count > 0 && pos < offsetof(s390_fp_regs, fprs)) {
u32 ufpc[2] = { target->thread.fpu.fpc, 0 };
@@ -1067,6 +1072,9 @@
if (target == current)
save_fpu_regs();
+ for (i = 0; i < __NUM_VXRS_LOW; i++)
+ vxrs[i] = *((__u64 *)(target->thread.fpu.vxrs + i) + 1);
+
rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1);
if (rc == 0)
for (i = 0; i < __NUM_VXRS_LOW; i++)
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 7f7ba5f2..d027f2e 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -445,7 +445,7 @@
* part of the System RAM resource.
*/
if (crashk_res.end) {
- memblock_add(crashk_res.start, resource_size(&crashk_res));
+ memblock_add_node(crashk_res.start, resource_size(&crashk_res), 0);
memblock_reserve(crashk_res.start, resource_size(&crashk_res));
insert_resource(&iomem_resource, &crashk_res);
}
diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c
index e959c02..8705ee6 100644
--- a/arch/s390/kernel/topology.c
+++ b/arch/s390/kernel/topology.c
@@ -448,6 +448,7 @@
struct sysinfo_15_1_x *info;
int i;
+ set_sched_topology(s390_topology);
if (!MACHINE_HAS_TOPOLOGY)
return 0;
tl_info = (struct sysinfo_15_1_x *)__get_free_page(GFP_KERNEL);
@@ -460,7 +461,6 @@
alloc_masks(info, &socket_info, 1);
alloc_masks(info, &book_info, 2);
alloc_masks(info, &drawer_info, 3);
- set_sched_topology(s390_topology);
return 0;
}
early_initcall(s390_topology_init);
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 9c7a1ec..47a1de7 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -916,7 +916,7 @@
memcpy(&mach->fac_mask, kvm->arch.model.fac_mask,
S390_ARCH_FAC_LIST_SIZE_BYTE);
memcpy((unsigned long *)&mach->fac_list, S390_lowcore.stfle_fac_list,
- S390_ARCH_FAC_LIST_SIZE_BYTE);
+ sizeof(S390_lowcore.stfle_fac_list));
if (copy_to_user((void __user *)attr->addr, mach, sizeof(*mach)))
ret = -EFAULT;
kfree(mach);
@@ -1437,7 +1437,7 @@
/* Populate the facility mask initially. */
memcpy(kvm->arch.model.fac_mask, S390_lowcore.stfle_fac_list,
- S390_ARCH_FAC_LIST_SIZE_BYTE);
+ sizeof(S390_lowcore.stfle_fac_list));
for (i = 0; i < S390_ARCH_FAC_LIST_SIZE_U64; i++) {
if (i < kvm_s390_fac_list_mask_size())
kvm->arch.model.fac_mask[i] &= kvm_s390_fac_list_mask[i];
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index 7a1897c..d56ef26 100644
--- a/arch/s390/mm/pgtable.c
+++ b/arch/s390/mm/pgtable.c
@@ -202,7 +202,7 @@
return pgste;
}
-static inline void ptep_xchg_commit(struct mm_struct *mm,
+static inline pte_t ptep_xchg_commit(struct mm_struct *mm,
unsigned long addr, pte_t *ptep,
pgste_t pgste, pte_t old, pte_t new)
{
@@ -220,6 +220,7 @@
} else {
*ptep = new;
}
+ return old;
}
pte_t ptep_xchg_direct(struct mm_struct *mm, unsigned long addr,
@@ -231,7 +232,7 @@
preempt_disable();
pgste = ptep_xchg_start(mm, addr, ptep);
old = ptep_flush_direct(mm, addr, ptep);
- ptep_xchg_commit(mm, addr, ptep, pgste, old, new);
+ old = ptep_xchg_commit(mm, addr, ptep, pgste, old, new);
preempt_enable();
return old;
}
@@ -246,7 +247,7 @@
preempt_disable();
pgste = ptep_xchg_start(mm, addr, ptep);
old = ptep_flush_lazy(mm, addr, ptep);
- ptep_xchg_commit(mm, addr, ptep, pgste, old, new);
+ old = ptep_xchg_commit(mm, addr, ptep, pgste, old, new);
preempt_enable();
return old;
}
diff --git a/arch/s390/pci/pci_dma.c b/arch/s390/pci/pci_dma.c
index 6b2f72f..049e386 100644
--- a/arch/s390/pci/pci_dma.c
+++ b/arch/s390/pci/pci_dma.c
@@ -419,6 +419,7 @@
size_t size, dma_addr_t *handle,
enum dma_data_direction dir)
{
+ unsigned long nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
struct zpci_dev *zdev = to_zpci(to_pci_dev(dev));
dma_addr_t dma_addr_base, dma_addr;
int flags = ZPCI_PTE_VALID;
@@ -426,8 +427,7 @@
unsigned long pa = 0;
int ret;
- size = PAGE_ALIGN(size);
- dma_addr_base = dma_alloc_address(dev, size >> PAGE_SHIFT);
+ dma_addr_base = dma_alloc_address(dev, nr_pages);
if (dma_addr_base == DMA_ERROR_CODE)
return -ENOMEM;
@@ -436,26 +436,27 @@
flags |= ZPCI_TABLE_PROTECTED;
for (s = sg; dma_addr < dma_addr_base + size; s = sg_next(s)) {
- pa = page_to_phys(sg_page(s)) + s->offset;
- ret = __dma_update_trans(zdev, pa, dma_addr, s->length, flags);
+ pa = page_to_phys(sg_page(s));
+ ret = __dma_update_trans(zdev, pa, dma_addr,
+ s->offset + s->length, flags);
if (ret)
goto unmap;
- dma_addr += s->length;
+ dma_addr += s->offset + s->length;
}
ret = __dma_purge_tlb(zdev, dma_addr_base, size, flags);
if (ret)
goto unmap;
*handle = dma_addr_base;
- atomic64_add(size >> PAGE_SHIFT, &zdev->mapped_pages);
+ atomic64_add(nr_pages, &zdev->mapped_pages);
return ret;
unmap:
dma_update_trans(zdev, 0, dma_addr_base, dma_addr - dma_addr_base,
ZPCI_PTE_INVALID);
- dma_free_address(dev, dma_addr_base, size >> PAGE_SHIFT);
+ dma_free_address(dev, dma_addr_base, nr_pages);
zpci_err("map error:\n");
zpci_err_dma(ret, pa);
return ret;
diff --git a/arch/tile/kernel/ptrace.c b/arch/tile/kernel/ptrace.c
index d89b701..e279572 100644
--- a/arch/tile/kernel/ptrace.c
+++ b/arch/tile/kernel/ptrace.c
@@ -111,7 +111,7 @@
const void *kbuf, const void __user *ubuf)
{
int ret;
- struct pt_regs regs;
+ struct pt_regs regs = *task_pt_regs(target);
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®s, 0,
sizeof(regs));
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index 2d44933..75725dc 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -97,6 +97,8 @@
KBUILD_CFLAGS += $(call cc-option,-mno-80387)
KBUILD_CFLAGS += $(call cc-option,-mno-fp-ret-in-387)
+ KBUILD_CFLAGS += -fno-pic
+
# Use -mpreferred-stack-boundary=3 if supported.
KBUILD_CFLAGS += $(call cc-option,-mpreferred-stack-boundary=3)
diff --git a/arch/x86/configs/i386_ranchu_defconfig b/arch/x86/configs/i386_ranchu_defconfig
new file mode 100644
index 0000000..a1c83c4
--- /dev/null
+++ b/arch/x86/configs/i386_ranchu_defconfig
@@ -0,0 +1,424 @@
+# CONFIG_64BIT is not set
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_POSIX_MQUEUE=y
+CONFIG_AUDIT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_ARCH_MMAP_RND_BITS=16
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_OSF_PARTITION=y
+CONFIG_AMIGA_PARTITION=y
+CONFIG_MAC_PARTITION=y
+CONFIG_BSD_DISKLABEL=y
+CONFIG_MINIX_SUBPARTITION=y
+CONFIG_SOLARIS_X86_PARTITION=y
+CONFIG_UNIXWARE_DISKLABEL=y
+CONFIG_SGI_PARTITION=y
+CONFIG_SUN_PARTITION=y
+CONFIG_KARMA_PARTITION=y
+CONFIG_SMP=y
+CONFIG_X86_BIGSMP=y
+CONFIG_MCORE2=y
+CONFIG_X86_GENERIC=y
+CONFIG_HPET_TIMER=y
+CONFIG_NR_CPUS=512
+CONFIG_PREEMPT=y
+# CONFIG_X86_MCE is not set
+CONFIG_X86_REBOOTFIXUPS=y
+CONFIG_X86_MSR=y
+CONFIG_X86_CPUID=y
+CONFIG_KSM=y
+CONFIG_CMA=y
+# CONFIG_MTRR_SANITIZER is not set
+CONFIG_EFI=y
+CONFIG_EFI_STUB=y
+CONFIG_HZ_100=y
+CONFIG_PHYSICAL_START=0x100000
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
+CONFIG_PM_DEBUG=y
+CONFIG_CPU_FREQ=y
+# CONFIG_CPU_FREQ_STAT is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_PCIEPORTBUS=y
+# CONFIG_PCIEASPM is not set
+CONFIG_PCCARD=y
+CONFIG_YENTA=y
+CONFIG_HOTPLUG_PCI=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_BINFMT_MISC=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG_DESTROY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_NETLABEL=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_SECURITY=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_CLS_U32=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_CFG80211=y
+CONFIG_MAC80211=y
+CONFIG_MAC80211_LEDS=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=16
+CONFIG_CONNECTOR=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_VIRTIO_BLK=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_SPI_ATTRS=y
+CONFIG_SCSI_ISCSI_ATTRS=y
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_ATA=y
+CONFIG_SATA_AHCI=y
+CONFIG_ATA_PIIX=y
+CONFIG_PATA_AMD=y
+CONFIG_PATA_OLDPIIX=y
+CONFIG_PATA_SCH=y
+CONFIG_PATA_MPIIX=y
+CONFIG_ATA_GENERIC=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_DEBUG=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_MIRROR=y
+CONFIG_DM_ZERO=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
+CONFIG_NETDEVICES=y
+CONFIG_NETCONSOLE=y
+CONFIG_TUN=y
+CONFIG_VIRTIO_NET=y
+CONFIG_BNX2=y
+CONFIG_TIGON3=y
+CONFIG_NET_TULIP=y
+CONFIG_E100=y
+CONFIG_E1000=y
+CONFIG_E1000E=y
+CONFIG_SKY2=y
+CONFIG_NE2K_PCI=y
+CONFIG_FORCEDETH=y
+CONFIG_8139TOO=y
+# CONFIG_8139TOO_PIO is not set
+CONFIG_R8169=y
+CONFIG_FDDI=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_USB_USBNET=y
+CONFIG_INPUT_POLLDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GOLDFISH_EVENTS=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_XPAD=y
+CONFIG_JOYSTICK_XPAD_FF=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
+CONFIG_INPUT_TABLET=y
+CONFIG_TABLET_USB_ACECAD=y
+CONFIG_TABLET_USB_AIPTEK=y
+CONFIG_TABLET_USB_GTCO=y
+CONFIG_TABLET_USB_HANWANG=y
+CONFIG_TABLET_USB_KBTAB=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_NONSTANDARD=y
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_VIRTIO_CONSOLE=y
+CONFIG_NVRAM=y
+CONFIG_I2C_I801=y
+CONFIG_BATTERY_GOLDFISH=y
+CONFIG_WATCHDOG=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_AGP=y
+CONFIG_AGP_AMD64=y
+CONFIG_AGP_INTEL=y
+CONFIG_DRM=y
+CONFIG_FB_MODE_HELPERS=y
+CONFIG_FB_TILEBLITTING=y
+CONFIG_FB_EFI=y
+CONFIG_FB_GOLDFISH=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_HIDRAW=y
+CONFIG_UHID=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_ACRUX=y
+CONFIG_HID_ACRUX_FF=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_PRODIKEYS=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_DRAGONRISE=y
+CONFIG_DRAGONRISE_FF=y
+CONFIG_HID_EMS_FF=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_HOLTEK=y
+CONFIG_HID_KEYTOUCH=y
+CONFIG_HID_KYE=y
+CONFIG_HID_UCLOGIC=y
+CONFIG_HID_WALTOP=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_TWINHAN=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LCPOWER=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_LOGITECH_DJ=y
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGIG940_FF=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_HID_NTRIG=y
+CONFIG_HID_ORTEK=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_PANTHERLORD_FF=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_PICOLCD=y
+CONFIG_HID_PRIMAX=y
+CONFIG_HID_ROCCAT=y
+CONFIG_HID_SAITEK=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SPEEDLINK=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_HID_GREENASIA=y
+CONFIG_GREENASIA_FF=y
+CONFIG_HID_SMARTJOYPLUS=y
+CONFIG_SMARTJOYPLUS_FF=y
+CONFIG_HID_TIVO=y
+CONFIG_HID_TOPSEED=y
+CONFIG_HID_THRUSTMASTER=y
+CONFIG_HID_WACOM=y
+CONFIG_HID_WIIMOTE=y
+CONFIG_HID_ZEROPLUS=y
+CONFIG_HID_ZYDACRON=y
+CONFIG_HID_PID=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_UHCI_HCD=y
+CONFIG_USB_PRINTER=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_OTG_WAKELOCK=y
+CONFIG_EDAC=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_HCTOSYS is not set
+CONFIG_DMADEVICES=y
+CONFIG_VIRTIO_PCI=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_SYNC=y
+CONFIG_SW_SYNC=y
+CONFIG_SYNC_FILE=y
+CONFIG_ION=y
+CONFIG_GOLDFISH_AUDIO=y
+CONFIG_SND_HDA_INTEL=y
+CONFIG_GOLDFISH=y
+CONFIG_GOLDFISH_PIPE=y
+CONFIG_GOLDFISH_SYNC=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ISCSI_IBFT_FIND=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+# CONFIG_PRINT_QUOTA_WARNING is not set
+CONFIG_FUSE_FS=y
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_HUGETLBFS=y
+CONFIG_PSTORE=y
+CONFIG_PSTORE_CONSOLE=y
+CONFIG_PSTORE_RAM=y
+# CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_UTF8=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=2048
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_SCHED_TRACER=y
+CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_PROVIDE_OHCI1394_DMA_INIT=y
+CONFIG_KEYS=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_SELINUX=y
+CONFIG_CRYPTO_AES_586=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_ASYMMETRIC_KEY_TYPE=y
+CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
+CONFIG_X509_CERTIFICATE_PARSER=y
+CONFIG_PKCS7_MESSAGE_PARSER=y
+CONFIG_PKCS7_TEST_KEY=y
+# CONFIG_VIRTUALIZATION is not set
+CONFIG_CRC_T10DIF=y
diff --git a/arch/x86/configs/x86_64_ranchu_defconfig b/arch/x86/configs/x86_64_ranchu_defconfig
new file mode 100644
index 0000000..d50434f
--- /dev/null
+++ b/arch/x86/configs/x86_64_ranchu_defconfig
@@ -0,0 +1,419 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_POSIX_MQUEUE=y
+CONFIG_AUDIT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_ARCH_MMAP_RND_BITS=32
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_OSF_PARTITION=y
+CONFIG_AMIGA_PARTITION=y
+CONFIG_MAC_PARTITION=y
+CONFIG_BSD_DISKLABEL=y
+CONFIG_MINIX_SUBPARTITION=y
+CONFIG_SOLARIS_X86_PARTITION=y
+CONFIG_UNIXWARE_DISKLABEL=y
+CONFIG_SGI_PARTITION=y
+CONFIG_SUN_PARTITION=y
+CONFIG_KARMA_PARTITION=y
+CONFIG_SMP=y
+CONFIG_MCORE2=y
+CONFIG_MAXSMP=y
+CONFIG_PREEMPT=y
+# CONFIG_X86_MCE is not set
+CONFIG_X86_MSR=y
+CONFIG_X86_CPUID=y
+CONFIG_KSM=y
+CONFIG_CMA=y
+# CONFIG_MTRR_SANITIZER is not set
+CONFIG_EFI=y
+CONFIG_EFI_STUB=y
+CONFIG_HZ_100=y
+CONFIG_PHYSICAL_START=0x100000
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
+CONFIG_PM_DEBUG=y
+CONFIG_CPU_FREQ=y
+# CONFIG_CPU_FREQ_STAT is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_PCI_MMCONFIG=y
+CONFIG_PCIEPORTBUS=y
+# CONFIG_PCIEASPM is not set
+CONFIG_PCCARD=y
+CONFIG_YENTA=y
+CONFIG_HOTPLUG_PCI=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_BINFMT_MISC=y
+CONFIG_IA32_EMULATION=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG_DESTROY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_NETLABEL=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_SECURITY=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_CLS_U32=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_CFG80211=y
+CONFIG_MAC80211=y
+CONFIG_MAC80211_LEDS=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DMA_CMA=y
+CONFIG_CONNECTOR=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_VIRTIO_BLK=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_SPI_ATTRS=y
+CONFIG_SCSI_ISCSI_ATTRS=y
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_ATA=y
+CONFIG_SATA_AHCI=y
+CONFIG_ATA_PIIX=y
+CONFIG_PATA_AMD=y
+CONFIG_PATA_OLDPIIX=y
+CONFIG_PATA_SCH=y
+CONFIG_PATA_MPIIX=y
+CONFIG_ATA_GENERIC=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_DEBUG=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_MIRROR=y
+CONFIG_DM_ZERO=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
+CONFIG_NETDEVICES=y
+CONFIG_NETCONSOLE=y
+CONFIG_TUN=y
+CONFIG_VIRTIO_NET=y
+CONFIG_BNX2=y
+CONFIG_TIGON3=y
+CONFIG_NET_TULIP=y
+CONFIG_E100=y
+CONFIG_E1000=y
+CONFIG_E1000E=y
+CONFIG_SKY2=y
+CONFIG_NE2K_PCI=y
+CONFIG_FORCEDETH=y
+CONFIG_8139TOO=y
+# CONFIG_8139TOO_PIO is not set
+CONFIG_R8169=y
+CONFIG_FDDI=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_USB_USBNET=y
+CONFIG_INPUT_POLLDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GOLDFISH_EVENTS=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_XPAD=y
+CONFIG_JOYSTICK_XPAD_FF=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
+CONFIG_INPUT_TABLET=y
+CONFIG_TABLET_USB_ACECAD=y
+CONFIG_TABLET_USB_AIPTEK=y
+CONFIG_TABLET_USB_GTCO=y
+CONFIG_TABLET_USB_HANWANG=y
+CONFIG_TABLET_USB_KBTAB=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_NONSTANDARD=y
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_VIRTIO_CONSOLE=y
+CONFIG_NVRAM=y
+CONFIG_I2C_I801=y
+CONFIG_BATTERY_GOLDFISH=y
+CONFIG_WATCHDOG=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_AGP=y
+CONFIG_AGP_AMD64=y
+CONFIG_AGP_INTEL=y
+CONFIG_DRM=y
+CONFIG_FB_MODE_HELPERS=y
+CONFIG_FB_TILEBLITTING=y
+CONFIG_FB_EFI=y
+CONFIG_FB_GOLDFISH=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_HIDRAW=y
+CONFIG_UHID=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_ACRUX=y
+CONFIG_HID_ACRUX_FF=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_PRODIKEYS=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_DRAGONRISE=y
+CONFIG_DRAGONRISE_FF=y
+CONFIG_HID_EMS_FF=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_HOLTEK=y
+CONFIG_HID_KEYTOUCH=y
+CONFIG_HID_KYE=y
+CONFIG_HID_UCLOGIC=y
+CONFIG_HID_WALTOP=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_TWINHAN=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LCPOWER=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_LOGITECH_DJ=y
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGIG940_FF=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_HID_NTRIG=y
+CONFIG_HID_ORTEK=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_PANTHERLORD_FF=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_PICOLCD=y
+CONFIG_HID_PRIMAX=y
+CONFIG_HID_ROCCAT=y
+CONFIG_HID_SAITEK=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SPEEDLINK=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_HID_GREENASIA=y
+CONFIG_GREENASIA_FF=y
+CONFIG_HID_SMARTJOYPLUS=y
+CONFIG_SMARTJOYPLUS_FF=y
+CONFIG_HID_TIVO=y
+CONFIG_HID_TOPSEED=y
+CONFIG_HID_THRUSTMASTER=y
+CONFIG_HID_WACOM=y
+CONFIG_HID_WIIMOTE=y
+CONFIG_HID_ZEROPLUS=y
+CONFIG_HID_ZYDACRON=y
+CONFIG_HID_PID=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_UHCI_HCD=y
+CONFIG_USB_PRINTER=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_OTG_WAKELOCK=y
+CONFIG_EDAC=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_HCTOSYS is not set
+CONFIG_DMADEVICES=y
+CONFIG_VIRTIO_PCI=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_SYNC=y
+CONFIG_SW_SYNC=y
+CONFIG_SYNC_FILE=y
+CONFIG_ION=y
+CONFIG_GOLDFISH_AUDIO=y
+CONFIG_SND_HDA_INTEL=y
+CONFIG_GOLDFISH=y
+CONFIG_GOLDFISH_PIPE=y
+CONFIG_GOLDFISH_SYNC=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ISCSI_IBFT_FIND=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+# CONFIG_PRINT_QUOTA_WARNING is not set
+CONFIG_FUSE_FS=y
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_HUGETLBFS=y
+CONFIG_PSTORE=y
+CONFIG_PSTORE_CONSOLE=y
+CONFIG_PSTORE_RAM=y
+# CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_UTF8=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_SCHED_TRACER=y
+CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_PROVIDE_OHCI1394_DMA_INIT=y
+CONFIG_KEYS=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_SELINUX=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_ASYMMETRIC_KEY_TYPE=y
+CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
+CONFIG_X509_CERTIFICATE_PARSER=y
+CONFIG_PKCS7_MESSAGE_PARSER=y
+CONFIG_PKCS7_TEST_KEY=y
+# CONFIG_VIRTUALIZATION is not set
+CONFIG_CRC_T10DIF=y
diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S
index 21b352a..edba860 100644
--- a/arch/x86/entry/entry_32.S
+++ b/arch/x86/entry/entry_32.S
@@ -889,8 +889,8 @@
jmp ftrace_stub
#endif
-.globl ftrace_stub
-ftrace_stub:
+/* This is weak to keep gas from relaxing the jumps */
+WEAK(ftrace_stub)
ret
END(ftrace_caller)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 6e395c9..7fe88bb 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -365,7 +365,11 @@
{
int i;
- if (x86_pmu.lbr_pt_coexist)
+ /*
+ * When lbr_pt_coexist we allow PT to coexist with either LBR or BTS.
+ * LBR and BTS are still mutually exclusive.
+ */
+ if (x86_pmu.lbr_pt_coexist && what == x86_lbr_exclusive_pt)
return 0;
if (!atomic_inc_not_zero(&x86_pmu.lbr_exclusive[what])) {
@@ -388,7 +392,7 @@
void x86_del_exclusive(unsigned int what)
{
- if (x86_pmu.lbr_pt_coexist)
+ if (x86_pmu.lbr_pt_coexist && what == x86_lbr_exclusive_pt)
return;
atomic_dec(&x86_pmu.lbr_exclusive[what]);
diff --git a/arch/x86/events/intel/cstate.c b/arch/x86/events/intel/cstate.c
index da51e5a..fec8a46 100644
--- a/arch/x86/events/intel/cstate.c
+++ b/arch/x86/events/intel/cstate.c
@@ -594,6 +594,9 @@
static inline void cstate_cleanup(void)
{
+ cpuhp_remove_state_nocalls(CPUHP_AP_PERF_X86_CSTATE_ONLINE);
+ cpuhp_remove_state_nocalls(CPUHP_AP_PERF_X86_CSTATE_STARTING);
+
if (has_cstate_core)
perf_pmu_unregister(&cstate_core_pmu);
@@ -606,16 +609,16 @@
int err;
cpuhp_setup_state(CPUHP_AP_PERF_X86_CSTATE_STARTING,
- "AP_PERF_X86_CSTATE_STARTING", cstate_cpu_init,
- NULL);
+ "perf/x86/cstate:starting", cstate_cpu_init, NULL);
cpuhp_setup_state(CPUHP_AP_PERF_X86_CSTATE_ONLINE,
- "AP_PERF_X86_CSTATE_ONLINE", NULL, cstate_cpu_exit);
+ "perf/x86/cstate:online", NULL, cstate_cpu_exit);
if (has_cstate_core) {
err = perf_pmu_register(&cstate_core_pmu, cstate_core_pmu.name, -1);
if (err) {
has_cstate_core = false;
pr_info("Failed to register cstate core pmu\n");
+ cstate_cleanup();
return err;
}
}
@@ -629,8 +632,7 @@
return err;
}
}
-
- return err;
+ return 0;
}
static int __init cstate_pmu_init(void)
@@ -655,8 +657,6 @@
static void __exit cstate_pmu_exit(void)
{
- cpuhp_remove_state_nocalls(CPUHP_AP_PERF_X86_CSTATE_ONLINE);
- cpuhp_remove_state_nocalls(CPUHP_AP_PERF_X86_CSTATE_STARTING);
cstate_cleanup();
}
module_exit(cstate_pmu_exit);
diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c
index dbaaf7dc..19d646a 100644
--- a/arch/x86/events/intel/uncore.c
+++ b/arch/x86/events/intel/uncore.c
@@ -763,30 +763,6 @@
pmu->registered = false;
}
-static void __uncore_exit_boxes(struct intel_uncore_type *type, int cpu)
-{
- struct intel_uncore_pmu *pmu = type->pmus;
- struct intel_uncore_box *box;
- int i, pkg;
-
- if (pmu) {
- pkg = topology_physical_package_id(cpu);
- for (i = 0; i < type->num_boxes; i++, pmu++) {
- box = pmu->boxes[pkg];
- if (box)
- uncore_box_exit(box);
- }
- }
-}
-
-static void uncore_exit_boxes(void *dummy)
-{
- struct intel_uncore_type **types;
-
- for (types = uncore_msr_uncores; *types; types++)
- __uncore_exit_boxes(*types++, smp_processor_id());
-}
-
static void uncore_free_boxes(struct intel_uncore_pmu *pmu)
{
int pkg;
@@ -1077,22 +1053,12 @@
return 0;
}
-static int first_init;
-
static int uncore_cpu_starting(unsigned int cpu)
{
struct intel_uncore_type *type, **types = uncore_msr_uncores;
struct intel_uncore_pmu *pmu;
struct intel_uncore_box *box;
- int i, pkg, ncpus = 1;
-
- if (first_init) {
- /*
- * On init we get the number of online cpus in the package
- * and set refcount for all of them.
- */
- ncpus = cpumask_weight(topology_core_cpumask(cpu));
- }
+ int i, pkg;
pkg = topology_logical_package_id(cpu);
for (; *types; types++) {
@@ -1103,7 +1069,7 @@
if (!box)
continue;
/* The first cpu on a package activates the box */
- if (atomic_add_return(ncpus, &box->refcnt) == ncpus)
+ if (atomic_inc_return(&box->refcnt) == 1)
uncore_box_init(box);
}
}
@@ -1407,19 +1373,17 @@
"PERF_X86_UNCORE_PREP",
uncore_cpu_prepare, NULL);
}
- first_init = 1;
+
cpuhp_setup_state(CPUHP_AP_PERF_X86_UNCORE_STARTING,
"AP_PERF_X86_UNCORE_STARTING",
uncore_cpu_starting, uncore_cpu_dying);
- first_init = 0;
+
cpuhp_setup_state(CPUHP_AP_PERF_X86_UNCORE_ONLINE,
"AP_PERF_X86_UNCORE_ONLINE",
uncore_event_cpu_online, uncore_event_cpu_offline);
return 0;
err:
- /* Undo box->init_box() */
- on_each_cpu_mask(&uncore_cpu_mask, uncore_exit_boxes, NULL, 1);
uncore_types_exit(uncore_msr_uncores);
uncore_pci_exit();
return ret;
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index a77ee02..bcbb1d2 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -604,7 +604,7 @@
u64 lbr_sel_mask; /* LBR_SELECT valid bits */
const int *lbr_sel_map; /* lbr_select mappings */
bool lbr_double_abort; /* duplicated lbr aborts */
- bool lbr_pt_coexist; /* LBR may coexist with PT */
+ bool lbr_pt_coexist; /* (LBR|BTS) may coexist with PT */
/*
* Intel PT/LBR/BTS are exclusive
diff --git a/arch/x86/include/asm/asm-prototypes.h b/arch/x86/include/asm/asm-prototypes.h
new file mode 100644
index 0000000..44b8762
--- /dev/null
+++ b/arch/x86/include/asm/asm-prototypes.h
@@ -0,0 +1,16 @@
+#include <asm/ftrace.h>
+#include <asm/uaccess.h>
+#include <asm/string.h>
+#include <asm/page.h>
+#include <asm/checksum.h>
+
+#include <asm-generic/asm-prototypes.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/special_insns.h>
+#include <asm/preempt.h>
+
+#ifndef CONFIG_X86_CMPXCHG64
+extern void cmpxchg8b_emu(void);
+#endif
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index a396292..ed10b5b 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -311,4 +311,6 @@
#define X86_BUG_NULL_SEG X86_BUG(10) /* Nulling a selector preserves the base */
#define X86_BUG_SWAPGS_FENCE X86_BUG(11) /* SWAPGS without input dep on GS */
#define X86_BUG_MONITOR X86_BUG(12) /* IPI required to wake up remote CPU */
+#define X86_BUG_AMD_E400 X86_BUG(13) /* CPU is among the affected by Erratum 400 */
+
#endif /* _ASM_X86_CPUFEATURES_H */
diff --git a/arch/x86/include/uapi/asm/prctl.h b/arch/x86/include/uapi/asm/prctl.h
index ae135de..835aa51 100644
--- a/arch/x86/include/uapi/asm/prctl.h
+++ b/arch/x86/include/uapi/asm/prctl.h
@@ -6,10 +6,8 @@
#define ARCH_GET_FS 0x1003
#define ARCH_GET_GS 0x1004
-#ifdef CONFIG_CHECKPOINT_RESTORE
-# define ARCH_MAP_VDSO_X32 0x2001
-# define ARCH_MAP_VDSO_32 0x2002
-# define ARCH_MAP_VDSO_64 0x2003
-#endif
+#define ARCH_MAP_VDSO_X32 0x2001
+#define ARCH_MAP_VDSO_32 0x2002
+#define ARCH_MAP_VDSO_64 0x2003
#endif /* _ASM_X86_PRCTL_H */
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 88c657b..f223491 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -2159,21 +2159,6 @@
}
/*
- * This can happen on physical hotplug. The sanity check at boot time
- * is done from native_smp_prepare_cpus() after num_possible_cpus() is
- * established.
- */
- if (topology_update_package_map(apicid, cpu) < 0) {
- int thiscpu = max + disabled_cpus;
-
- pr_warning("APIC: Package limit reached. Processor %d/0x%x ignored.\n",
- thiscpu, apicid);
-
- disabled_cpus++;
- return -ENOSPC;
- }
-
- /*
* Validate version
*/
if (version == 0x0) {
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index 48e6d84..7249f15 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -1876,6 +1876,7 @@
.irq_ack = irq_chip_ack_parent,
.irq_eoi = ioapic_ack_level,
.irq_set_affinity = ioapic_set_affinity,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
.flags = IRQCHIP_SKIP_SET_WAKE,
};
@@ -1887,6 +1888,7 @@
.irq_ack = irq_chip_ack_parent,
.irq_eoi = ioapic_ir_ack_level,
.irq_set_affinity = ioapic_set_affinity,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
.flags = IRQCHIP_SKIP_SET_WAKE,
};
@@ -2116,6 +2118,7 @@
if (idx != -1 && irq_trigger(idx))
unmask_ioapic_irq(irq_get_chip_data(0));
}
+ irq_domain_deactivate_irq(irq_data);
irq_domain_activate_irq(irq_data);
if (timer_irq_works()) {
if (disable_timer_pin_1 > 0)
@@ -2137,6 +2140,7 @@
* legacy devices should be connected to IO APIC #0
*/
replace_pin_at_irq_node(data, node, apic1, pin1, apic2, pin2);
+ irq_domain_deactivate_irq(irq_data);
irq_domain_activate_irq(irq_data);
legacy_pic->unmask(0);
if (timer_irq_works()) {
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index 1e81a37..1d31672 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -20,6 +20,10 @@
#include "cpu.h"
+static const int amd_erratum_383[];
+static const int amd_erratum_400[];
+static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum);
+
/*
* nodes_per_socket: Stores the number of nodes per socket.
* Refer to Fam15h Models 00-0fh BKDG - CPUID Fn8000_001E_ECX
@@ -305,20 +309,32 @@
/* get information required for multi-node processors */
if (boot_cpu_has(X86_FEATURE_TOPOEXT)) {
- u32 eax, ebx, ecx, edx;
- cpuid(0x8000001e, &eax, &ebx, &ecx, &edx);
- node_id = ecx & 7;
+ node_id = cpuid_ecx(0x8000001e) & 7;
- /* get compute unit information */
- smp_num_siblings = ((ebx >> 8) & 3) + 1;
- c->x86_max_cores /= smp_num_siblings;
- c->cpu_core_id = ebx & 0xff;
+ /*
+ * We may have multiple LLCs if L3 caches exist, so check if we
+ * have an L3 cache by looking at the L3 cache CPUID leaf.
+ */
+ if (cpuid_edx(0x80000006)) {
+ if (c->x86 == 0x17) {
+ /*
+ * LLC is at the core complex level.
+ * Core complex id is ApicId[3].
+ */
+ per_cpu(cpu_llc_id, cpu) = c->apicid >> 3;
+ } else {
+ /* LLC is at the node level. */
+ per_cpu(cpu_llc_id, cpu) = node_id;
+ }
+ }
} else if (cpu_has(c, X86_FEATURE_NODEID_MSR)) {
u64 value;
rdmsrl(MSR_FAM10H_NODE_ID, value);
node_id = value & 7;
+
+ per_cpu(cpu_llc_id, cpu) = node_id;
} else
return;
@@ -329,9 +345,6 @@
set_cpu_cap(c, X86_FEATURE_AMD_DCM);
cus_per_node = c->x86_max_cores / nodes_per_socket;
- /* store NodeID, use llc_shared_map to store sibling info */
- per_cpu(cpu_llc_id, cpu) = node_id;
-
/* core id has to be in the [0 .. cores_per_node - 1] range */
c->cpu_core_id %= cus_per_node;
}
@@ -356,15 +369,6 @@
/* use socket ID also for last level cache */
per_cpu(cpu_llc_id, cpu) = c->phys_proc_id;
amd_get_topology(c);
-
- /*
- * Fix percpu cpu_llc_id here as LLC topology is different
- * for Fam17h systems.
- */
- if (c->x86 != 0x17 || !cpuid_edx(0x80000006))
- return;
-
- per_cpu(cpu_llc_id, cpu) = c->apicid >> 3;
#endif
}
@@ -585,11 +589,16 @@
/* F16h erratum 793, CVE-2013-6885 */
if (c->x86 == 0x16 && c->x86_model <= 0xf)
msr_set_bit(MSR_AMD64_LS_CFG, 15);
-}
-static const int amd_erratum_383[];
-static const int amd_erratum_400[];
-static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum);
+ /*
+ * Check whether the machine is affected by erratum 400. This is
+ * used to select the proper idle routine and to enable the check
+ * whether the machine is affected in arch_post_acpi_init(), which
+ * sets the X86_BUG_AMD_APIC_C1E bug depending on the MSR check.
+ */
+ if (cpu_has_amd_erratum(c, amd_erratum_400))
+ set_cpu_bug(c, X86_BUG_AMD_E400);
+}
static void init_amd_k8(struct cpuinfo_x86 *c)
{
@@ -770,9 +779,6 @@
if (c->x86 > 0x11)
set_cpu_cap(c, X86_FEATURE_ARAT);
- if (cpu_has_amd_erratum(c, amd_erratum_400))
- set_cpu_bug(c, X86_BUG_AMD_APIC_C1E);
-
rdmsr_safe(MSR_AMD64_PATCH_LEVEL, &c->microcode, &dummy);
/* 3DNow or LM implies PREFETCHW */
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index cc9e980..023c7bf 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -667,13 +667,14 @@
c->x86_capability[CPUID_1_EDX] = edx;
}
+ /* Thermal and Power Management Leaf: level 0x00000006 (eax) */
+ if (c->cpuid_level >= 0x00000006)
+ c->x86_capability[CPUID_6_EAX] = cpuid_eax(0x00000006);
+
/* Additional Intel-defined flags: level 0x00000007 */
if (c->cpuid_level >= 0x00000007) {
cpuid_count(0x00000007, 0, &eax, &ebx, &ecx, &edx);
-
c->x86_capability[CPUID_7_0_EBX] = ebx;
-
- c->x86_capability[CPUID_6_EAX] = cpuid_eax(0x00000006);
c->x86_capability[CPUID_7_ECX] = ecx;
}
@@ -979,29 +980,21 @@
}
/*
- * The physical to logical package id mapping is initialized from the
- * acpi/mptables information. Make sure that CPUID actually agrees with
- * that.
+ * Validate that ACPI/mptables have the same information about the
+ * effective APIC id and update the package map.
*/
-static void sanitize_package_id(struct cpuinfo_x86 *c)
+static void validate_apic_and_package_id(struct cpuinfo_x86 *c)
{
#ifdef CONFIG_SMP
- unsigned int pkg, apicid, cpu = smp_processor_id();
+ unsigned int apicid, cpu = smp_processor_id();
apicid = apic->cpu_present_to_apicid(cpu);
- pkg = apicid >> boot_cpu_data.x86_coreid_bits;
- if (apicid != c->initial_apicid) {
- pr_err(FW_BUG "CPU%u: APIC id mismatch. Firmware: %x CPUID: %x\n",
+ if (apicid != c->apicid) {
+ pr_err(FW_BUG "CPU%u: APIC id mismatch. Firmware: %x APIC: %x\n",
cpu, apicid, c->initial_apicid);
- c->initial_apicid = apicid;
}
- if (pkg != c->phys_proc_id) {
- pr_err(FW_BUG "CPU%u: Using firmware package id %u instead of %u\n",
- cpu, pkg, c->phys_proc_id);
- c->phys_proc_id = pkg;
- }
- c->logical_proc_id = topology_phys_to_logical_pkg(pkg);
+ BUG_ON(topology_update_package_map(c->phys_proc_id, cpu));
#else
c->logical_proc_id = 0;
#endif
@@ -1132,7 +1125,6 @@
#ifdef CONFIG_NUMA
numa_add_cpu(smp_processor_id());
#endif
- sanitize_package_id(c);
}
/*
@@ -1188,6 +1180,7 @@
enable_sep_cpu();
#endif
mtrr_ap_init();
+ validate_apic_and_package_id(c);
}
struct msr_range {
@@ -1282,7 +1275,7 @@
{
int bit;
- if (get_option(&arg, &bit) && bit < NCAPINTS*32)
+ if (get_option(&arg, &bit) && bit >= 0 && bit < NCAPINTS * 32)
setup_clear_cpu_cap(bit);
else
return 0;
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
index 274fab9..932348fb 100644
--- a/arch/x86/kernel/hpet.c
+++ b/arch/x86/kernel/hpet.c
@@ -352,6 +352,7 @@
} else {
struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt);
+ irq_domain_deactivate_irq(irq_get_irq_data(hdev->irq));
irq_domain_activate_irq(irq_get_irq_data(hdev->irq));
disable_irq(hdev->irq);
irq_set_affinity(hdev->irq, cpumask_of(hdev->cpu));
diff --git a/arch/x86/kernel/pci-swiotlb.c b/arch/x86/kernel/pci-swiotlb.c
index b47edb8..8da13d4 100644
--- a/arch/x86/kernel/pci-swiotlb.c
+++ b/arch/x86/kernel/pci-swiotlb.c
@@ -70,7 +70,7 @@
{
int use_swiotlb = swiotlb | swiotlb_force;
- if (swiotlb_force)
+ if (swiotlb_force == SWIOTLB_FORCE)
swiotlb = 1;
return use_swiotlb;
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index 76629f4..fc7cf64 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -435,8 +435,7 @@
if (x86_idle || boot_option_idle_override == IDLE_POLL)
return;
- if (cpu_has_bug(c, X86_BUG_AMD_APIC_C1E)) {
- /* E400: APIC timer interrupt does not wake up CPU from C1e */
+ if (boot_cpu_has_bug(X86_BUG_AMD_E400)) {
pr_info("using AMD E400 aware idle routine\n");
x86_idle = amd_e400_idle;
} else if (prefer_mwait_c1_over_halt(c)) {
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 42f5eb7..e9bbe02 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -104,7 +104,6 @@
unsigned int __max_logical_packages __read_mostly;
EXPORT_SYMBOL(__max_logical_packages);
static unsigned int logical_packages __read_mostly;
-static bool logical_packages_frozen __read_mostly;
/* Maximum number of SMT threads on any online core */
int __max_smt_threads __read_mostly;
@@ -263,9 +262,14 @@
cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
}
-int topology_update_package_map(unsigned int apicid, unsigned int cpu)
+/**
+ * topology_update_package_map - Update the physical to logical package map
+ * @pkg: The physical package id as retrieved via CPUID
+ * @cpu: The cpu for which this is updated
+ */
+int topology_update_package_map(unsigned int pkg, unsigned int cpu)
{
- unsigned int new, pkg = apicid >> boot_cpu_data.x86_coreid_bits;
+ unsigned int new;
/* Called from early boot ? */
if (!physical_package_map)
@@ -278,16 +282,17 @@
if (test_and_set_bit(pkg, physical_package_map))
goto found;
- if (logical_packages_frozen) {
- physical_to_logical_pkg[pkg] = -1;
- pr_warn("APIC(%x) Package %u exceeds logical package max\n",
- apicid, pkg);
+ if (logical_packages >= __max_logical_packages) {
+ pr_warn("Package %u of CPU %u exceeds BIOS package data %u.\n",
+ logical_packages, cpu, __max_logical_packages);
return -ENOSPC;
}
new = logical_packages++;
- pr_info("APIC(%x) Converting physical %u to logical package %u\n",
- apicid, pkg, new);
+ if (new != pkg) {
+ pr_info("CPU %u Converting physical %u to logical package %u\n",
+ cpu, pkg, new);
+ }
physical_to_logical_pkg[pkg] = new;
found:
@@ -308,9 +313,9 @@
}
EXPORT_SYMBOL(topology_phys_to_logical_pkg);
-static void __init smp_init_package_map(void)
+static void __init smp_init_package_map(struct cpuinfo_x86 *c, unsigned int cpu)
{
- unsigned int ncpus, cpu;
+ unsigned int ncpus;
size_t size;
/*
@@ -355,27 +360,9 @@
size = BITS_TO_LONGS(max_physical_pkg_id) * sizeof(unsigned long);
physical_package_map = kzalloc(size, GFP_KERNEL);
- for_each_present_cpu(cpu) {
- unsigned int apicid = apic->cpu_present_to_apicid(cpu);
-
- if (apicid == BAD_APICID || !apic->apic_id_valid(apicid))
- continue;
- if (!topology_update_package_map(apicid, cpu))
- continue;
- pr_warn("CPU %u APICId %x disabled\n", cpu, apicid);
- per_cpu(x86_bios_cpu_apicid, cpu) = BAD_APICID;
- set_cpu_possible(cpu, false);
- set_cpu_present(cpu, false);
- }
-
- if (logical_packages > __max_logical_packages) {
- pr_warn("Detected more packages (%u), then computed by BIOS data (%u).\n",
- logical_packages, __max_logical_packages);
- logical_packages_frozen = true;
- __max_logical_packages = logical_packages;
- }
-
pr_info("Max logical packages: %u\n", __max_logical_packages);
+
+ topology_update_package_map(c->phys_proc_id, cpu);
}
void __init smp_store_boot_cpu_info(void)
@@ -385,7 +372,7 @@
*c = boot_cpu_data;
c->cpu_index = id;
- smp_init_package_map();
+ smp_init_package_map(c, id);
}
/*
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index a3ce9d2..9f676ad 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -171,6 +171,7 @@
#define NearBranch ((u64)1 << 52) /* Near branches */
#define No16 ((u64)1 << 53) /* No 16 bit operand */
#define IncSP ((u64)1 << 54) /* SP is incremented before ModRM calc */
+#define Aligned16 ((u64)1 << 55) /* Aligned to 16 byte boundary (e.g. FXSAVE) */
#define DstXacc (DstAccLo | SrcAccHi | SrcWrite)
@@ -446,6 +447,26 @@
FOP_START(salc) "pushf; sbb %al, %al; popf \n\t" FOP_RET
FOP_END;
+/*
+ * XXX: inoutclob user must know where the argument is being expanded.
+ * Relying on CC_HAVE_ASM_GOTO would allow us to remove _fault.
+ */
+#define asm_safe(insn, inoutclob...) \
+({ \
+ int _fault = 0; \
+ \
+ asm volatile("1:" insn "\n" \
+ "2:\n" \
+ ".pushsection .fixup, \"ax\"\n" \
+ "3: movl $1, %[_fault]\n" \
+ " jmp 2b\n" \
+ ".popsection\n" \
+ _ASM_EXTABLE(1b, 3b) \
+ : [_fault] "+qm"(_fault) inoutclob ); \
+ \
+ _fault ? X86EMUL_UNHANDLEABLE : X86EMUL_CONTINUE; \
+})
+
static int emulator_check_intercept(struct x86_emulate_ctxt *ctxt,
enum x86_intercept intercept,
enum x86_intercept_stage stage)
@@ -632,21 +653,24 @@
* depending on whether they're AVX encoded or not.
*
* Also included is CMPXCHG16B which is not a vector instruction, yet it is
- * subject to the same check.
+ * subject to the same check. FXSAVE and FXRSTOR are checked here too as their
+ * 512 bytes of data must be aligned to a 16 byte boundary.
*/
-static bool insn_aligned(struct x86_emulate_ctxt *ctxt, unsigned size)
+static unsigned insn_alignment(struct x86_emulate_ctxt *ctxt, unsigned size)
{
if (likely(size < 16))
- return false;
+ return 1;
if (ctxt->d & Aligned)
- return true;
+ return size;
else if (ctxt->d & Unaligned)
- return false;
+ return 1;
else if (ctxt->d & Avx)
- return false;
+ return 1;
+ else if (ctxt->d & Aligned16)
+ return 16;
else
- return true;
+ return size;
}
static __always_inline int __linearize(struct x86_emulate_ctxt *ctxt,
@@ -704,7 +728,7 @@
}
break;
}
- if (insn_aligned(ctxt, size) && ((la & (size - 1)) != 0))
+ if (la & (insn_alignment(ctxt, size) - 1))
return emulate_gp(ctxt, 0);
return X86EMUL_CONTINUE;
bad:
@@ -791,6 +815,20 @@
return ctxt->ops->read_std(ctxt, linear, data, size, &ctxt->exception);
}
+static int segmented_write_std(struct x86_emulate_ctxt *ctxt,
+ struct segmented_address addr,
+ void *data,
+ unsigned int size)
+{
+ int rc;
+ ulong linear;
+
+ rc = linearize(ctxt, addr, size, true, &linear);
+ if (rc != X86EMUL_CONTINUE)
+ return rc;
+ return ctxt->ops->write_std(ctxt, linear, data, size, &ctxt->exception);
+}
+
/*
* Prefetch the remaining bytes of the instruction without crossing page
* boundary if they are not in fetch_cache yet.
@@ -1544,7 +1582,6 @@
&ctxt->exception);
}
-/* Does not support long mode */
static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
u16 selector, int seg, u8 cpl,
enum x86_transfer_type transfer,
@@ -1581,20 +1618,34 @@
rpl = selector & 3;
- /* NULL selector is not valid for TR, CS and SS (except for long mode) */
- if ((seg == VCPU_SREG_CS
- || (seg == VCPU_SREG_SS
- && (ctxt->mode != X86EMUL_MODE_PROT64 || rpl != cpl))
- || seg == VCPU_SREG_TR)
- && null_selector)
- goto exception;
-
/* TR should be in GDT only */
if (seg == VCPU_SREG_TR && (selector & (1 << 2)))
goto exception;
- if (null_selector) /* for NULL selector skip all following checks */
+ /* NULL selector is not valid for TR, CS and (except for long mode) SS */
+ if (null_selector) {
+ if (seg == VCPU_SREG_CS || seg == VCPU_SREG_TR)
+ goto exception;
+
+ if (seg == VCPU_SREG_SS) {
+ if (ctxt->mode != X86EMUL_MODE_PROT64 || rpl != cpl)
+ goto exception;
+
+ /*
+ * ctxt->ops->set_segment expects the CPL to be in
+ * SS.DPL, so fake an expand-up 32-bit data segment.
+ */
+ seg_desc.type = 3;
+ seg_desc.p = 1;
+ seg_desc.s = 1;
+ seg_desc.dpl = cpl;
+ seg_desc.d = 1;
+ seg_desc.g = 1;
+ }
+
+ /* Skip all following checks */
goto load;
+ }
ret = read_segment_descriptor(ctxt, selector, &seg_desc, &desc_addr);
if (ret != X86EMUL_CONTINUE)
@@ -1710,6 +1761,21 @@
u16 selector, int seg)
{
u8 cpl = ctxt->ops->cpl(ctxt);
+
+ /*
+ * None of MOV, POP and LSS can load a NULL selector in CPL=3, but
+ * they can load it at CPL<3 (Intel's manual says only LSS can,
+ * but it's wrong).
+ *
+ * However, the Intel manual says that putting IST=1/DPL=3 in
+ * an interrupt gate will result in SS=3 (the AMD manual instead
+ * says it doesn't), so allow SS=3 in __load_segment_descriptor
+ * and only forbid it here.
+ */
+ if (seg == VCPU_SREG_SS && selector == 3 &&
+ ctxt->mode == X86EMUL_MODE_PROT64)
+ return emulate_exception(ctxt, GP_VECTOR, 0, true);
+
return __load_segment_descriptor(ctxt, selector, seg, cpl,
X86_TRANSFER_NONE, NULL);
}
@@ -3658,8 +3724,8 @@
}
/* Disable writeback. */
ctxt->dst.type = OP_NONE;
- return segmented_write(ctxt, ctxt->dst.addr.mem,
- &desc_ptr, 2 + ctxt->op_bytes);
+ return segmented_write_std(ctxt, ctxt->dst.addr.mem,
+ &desc_ptr, 2 + ctxt->op_bytes);
}
static int em_sgdt(struct x86_emulate_ctxt *ctxt)
@@ -3842,6 +3908,131 @@
return X86EMUL_CONTINUE;
}
+static int check_fxsr(struct x86_emulate_ctxt *ctxt)
+{
+ u32 eax = 1, ebx, ecx = 0, edx;
+
+ ctxt->ops->get_cpuid(ctxt, &eax, &ebx, &ecx, &edx);
+ if (!(edx & FFL(FXSR)))
+ return emulate_ud(ctxt);
+
+ if (ctxt->ops->get_cr(ctxt, 0) & (X86_CR0_TS | X86_CR0_EM))
+ return emulate_nm(ctxt);
+
+ /*
+ * Don't emulate a case that should never be hit, instead of working
+ * around a lack of fxsave64/fxrstor64 on old compilers.
+ */
+ if (ctxt->mode >= X86EMUL_MODE_PROT64)
+ return X86EMUL_UNHANDLEABLE;
+
+ return X86EMUL_CONTINUE;
+}
+
+/*
+ * FXSAVE and FXRSTOR have 4 different formats depending on execution mode,
+ * 1) 16 bit mode
+ * 2) 32 bit mode
+ * - like (1), but FIP and FDP (foo) are only 16 bit. At least Intel CPUs
+ * preserve whole 32 bit values, though, so (1) and (2) are the same wrt.
+ * save and restore
+ * 3) 64-bit mode with REX.W prefix
+ * - like (2), but XMM 8-15 are being saved and restored
+ * 4) 64-bit mode without REX.W prefix
+ * - like (3), but FIP and FDP are 64 bit
+ *
+ * Emulation uses (3) for (1) and (2) and preserves XMM 8-15 to reach the
+ * desired result. (4) is not emulated.
+ *
+ * Note: Guest and host CPUID.(EAX=07H,ECX=0H):EBX[bit 13] (deprecate FPU CS
+ * and FPU DS) should match.
+ */
+static int em_fxsave(struct x86_emulate_ctxt *ctxt)
+{
+ struct fxregs_state fx_state;
+ size_t size;
+ int rc;
+
+ rc = check_fxsr(ctxt);
+ if (rc != X86EMUL_CONTINUE)
+ return rc;
+
+ ctxt->ops->get_fpu(ctxt);
+
+ rc = asm_safe("fxsave %[fx]", , [fx] "+m"(fx_state));
+
+ ctxt->ops->put_fpu(ctxt);
+
+ if (rc != X86EMUL_CONTINUE)
+ return rc;
+
+ if (ctxt->ops->get_cr(ctxt, 4) & X86_CR4_OSFXSR)
+ size = offsetof(struct fxregs_state, xmm_space[8 * 16/4]);
+ else
+ size = offsetof(struct fxregs_state, xmm_space[0]);
+
+ return segmented_write_std(ctxt, ctxt->memop.addr.mem, &fx_state, size);
+}
+
+static int fxrstor_fixup(struct x86_emulate_ctxt *ctxt,
+ struct fxregs_state *new)
+{
+ int rc = X86EMUL_CONTINUE;
+ struct fxregs_state old;
+
+ rc = asm_safe("fxsave %[fx]", , [fx] "+m"(old));
+ if (rc != X86EMUL_CONTINUE)
+ return rc;
+
+ /*
+ * 64 bit host will restore XMM 8-15, which is not correct on non-64
+ * bit guests. Load the current values in order to preserve 64 bit
+ * XMMs after fxrstor.
+ */
+#ifdef CONFIG_X86_64
+ /* XXX: accessing XMM 8-15 very awkwardly */
+ memcpy(&new->xmm_space[8 * 16/4], &old.xmm_space[8 * 16/4], 8 * 16);
+#endif
+
+ /*
+ * Hardware doesn't save and restore XMM 0-7 without CR4.OSFXSR, but
+ * does save and restore MXCSR.
+ */
+ if (!(ctxt->ops->get_cr(ctxt, 4) & X86_CR4_OSFXSR))
+ memcpy(new->xmm_space, old.xmm_space, 8 * 16);
+
+ return rc;
+}
+
+static int em_fxrstor(struct x86_emulate_ctxt *ctxt)
+{
+ struct fxregs_state fx_state;
+ int rc;
+
+ rc = check_fxsr(ctxt);
+ if (rc != X86EMUL_CONTINUE)
+ return rc;
+
+ rc = segmented_read_std(ctxt, ctxt->memop.addr.mem, &fx_state, 512);
+ if (rc != X86EMUL_CONTINUE)
+ return rc;
+
+ if (fx_state.mxcsr >> 16)
+ return emulate_gp(ctxt, 0);
+
+ ctxt->ops->get_fpu(ctxt);
+
+ if (ctxt->mode < X86EMUL_MODE_PROT64)
+ rc = fxrstor_fixup(ctxt, &fx_state);
+
+ if (rc == X86EMUL_CONTINUE)
+ rc = asm_safe("fxrstor %[fx]", : [fx] "m"(fx_state));
+
+ ctxt->ops->put_fpu(ctxt);
+
+ return rc;
+}
+
static bool valid_cr(int nr)
{
switch (nr) {
@@ -4194,7 +4385,9 @@
};
static const struct group_dual group15 = { {
- N, N, N, N, N, N, N, GP(0, &pfx_0f_ae_7),
+ I(ModRM | Aligned16, em_fxsave),
+ I(ModRM | Aligned16, em_fxrstor),
+ N, N, N, N, N, GP(0, &pfx_0f_ae_7),
}, {
N, N, N, N, N, N, N, N,
} };
@@ -5066,21 +5259,13 @@
static int flush_pending_x87_faults(struct x86_emulate_ctxt *ctxt)
{
- bool fault = false;
+ int rc;
ctxt->ops->get_fpu(ctxt);
- asm volatile("1: fwait \n\t"
- "2: \n\t"
- ".pushsection .fixup,\"ax\" \n\t"
- "3: \n\t"
- "movb $1, %[fault] \n\t"
- "jmp 2b \n\t"
- ".popsection \n\t"
- _ASM_EXTABLE(1b, 3b)
- : [fault]"+qm"(fault));
+ rc = asm_safe("fwait");
ctxt->ops->put_fpu(ctxt);
- if (unlikely(fault))
+ if (unlikely(rc != X86EMUL_CONTINUE))
return emulate_exception(ctxt, MF_VECTOR, 0, false);
return X86EMUL_CONTINUE;
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 6f69340..3f05c04 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -2360,3 +2360,9 @@
jump_label_rate_limit(&apic_hw_disabled, HZ);
jump_label_rate_limit(&apic_sw_disabled, HZ);
}
+
+void kvm_lapic_exit(void)
+{
+ static_key_deferred_flush(&apic_hw_disabled);
+ static_key_deferred_flush(&apic_sw_disabled);
+}
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index f60d01c..4dfe4d6 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -108,6 +108,7 @@
int kvm_lapic_enable_pv_eoi(struct kvm_vcpu *vcpu, u64 data);
void kvm_lapic_init(void);
+void kvm_lapic_exit(void);
#define VEC_POS(v) ((v) & (32 - 1))
#define REG_POS(v) (((v) >> 5) << 4)
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 5382b82..64774f4 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -1343,10 +1343,10 @@
return vmcs12->pin_based_vm_exec_control & PIN_BASED_POSTED_INTR;
}
-static inline bool is_exception(u32 intr_info)
+static inline bool is_nmi(u32 intr_info)
{
return (intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VALID_MASK))
- == (INTR_TYPE_HARD_EXCEPTION | INTR_INFO_VALID_MASK);
+ == (INTR_TYPE_NMI_INTR | INTR_INFO_VALID_MASK);
}
static void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 exit_reason,
@@ -5476,7 +5476,7 @@
if (is_machine_check(intr_info))
return handle_machine_check(vcpu);
- if ((intr_info & INTR_INFO_INTR_TYPE_MASK) == INTR_TYPE_NMI_INTR)
+ if (is_nmi(intr_info))
return 1; /* already handled by vmx_vcpu_run() */
if (is_no_device(intr_info)) {
@@ -8018,7 +8018,7 @@
switch (exit_reason) {
case EXIT_REASON_EXCEPTION_NMI:
- if (!is_exception(intr_info))
+ if (is_nmi(intr_info))
return false;
else if (is_page_fault(intr_info))
return enable_ept;
@@ -8611,8 +8611,7 @@
kvm_machine_check();
/* We need to handle NMIs before interrupts are enabled */
- if ((exit_intr_info & INTR_INFO_INTR_TYPE_MASK) == INTR_TYPE_NMI_INTR &&
- (exit_intr_info & INTR_INFO_VALID_MASK)) {
+ if (is_nmi(exit_intr_info)) {
kvm_before_handle_nmi(&vmx->vcpu);
asm("int $2");
kvm_after_handle_nmi(&vmx->vcpu);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 04c5d96..731044e 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3036,6 +3036,8 @@
memset(&events->reserved, 0, sizeof(events->reserved));
}
+static void kvm_set_hflags(struct kvm_vcpu *vcpu, unsigned emul_flags);
+
static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
struct kvm_vcpu_events *events)
{
@@ -3072,10 +3074,13 @@
vcpu->arch.apic->sipi_vector = events->sipi_vector;
if (events->flags & KVM_VCPUEVENT_VALID_SMM) {
+ u32 hflags = vcpu->arch.hflags;
if (events->smi.smm)
- vcpu->arch.hflags |= HF_SMM_MASK;
+ hflags |= HF_SMM_MASK;
else
- vcpu->arch.hflags &= ~HF_SMM_MASK;
+ hflags &= ~HF_SMM_MASK;
+ kvm_set_hflags(vcpu, hflags);
+
vcpu->arch.smi_pending = events->smi.pending;
if (events->smi.smm_inside_nmi)
vcpu->arch.hflags |= HF_SMM_INSIDE_NMI_MASK;
@@ -3143,6 +3148,7 @@
memcpy(dest, xsave, XSAVE_HDR_OFFSET);
/* Set XSTATE_BV */
+ xstate_bv &= vcpu->arch.guest_supported_xcr0 | XFEATURE_MASK_FPSSE;
*(u64 *)(dest + XSAVE_HDR_OFFSET) = xstate_bv;
/*
@@ -3303,6 +3309,8 @@
switch (cap->cap) {
case KVM_CAP_HYPERV_SYNIC:
+ if (!irqchip_in_kernel(vcpu->kvm))
+ return -EINVAL;
return kvm_hv_activate_synic(vcpu);
default:
return -EINVAL;
@@ -5958,6 +5966,7 @@
void kvm_arch_exit(void)
{
+ kvm_lapic_exit();
perf_unregister_guest_info_callbacks(&kvm_guest_cbs);
if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC))
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index fe04a04..15f7436 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -1172,6 +1172,8 @@
set_memory_ro((unsigned long)header, header->pages);
prog->bpf_func = (void *)image;
prog->jited = 1;
+ } else {
+ prog = orig_prog;
}
out_addrs:
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c
index 3cd6983..3961103 100644
--- a/arch/x86/pci/acpi.c
+++ b/arch/x86/pci/acpi.c
@@ -114,6 +114,16 @@
DMI_MATCH(DMI_BIOS_VERSION, "6JET85WW (1.43 )"),
},
},
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=42606 */
+ {
+ .callback = set_nouse_crs,
+ .ident = "Supermicro X8DTH",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X8DTH-i/6/iF/6F"),
+ DMI_MATCH(DMI_BIOS_VERSION, "2.0a"),
+ },
+ },
/* https://bugzilla.kernel.org/show_bug.cgi?id=15362 */
{
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 936a488..274dfc4 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -210,6 +210,70 @@
return 0;
}
+#define OVERFLOW_ADDR_SHIFT (64 - EFI_PAGE_SHIFT)
+#define OVERFLOW_ADDR_MASK (U64_MAX << OVERFLOW_ADDR_SHIFT)
+#define U64_HIGH_BIT (~(U64_MAX >> 1))
+
+static bool __init efi_memmap_entry_valid(const efi_memory_desc_t *md, int i)
+{
+ u64 end = (md->num_pages << EFI_PAGE_SHIFT) + md->phys_addr - 1;
+ u64 end_hi = 0;
+ char buf[64];
+
+ if (md->num_pages == 0) {
+ end = 0;
+ } else if (md->num_pages > EFI_PAGES_MAX ||
+ EFI_PAGES_MAX - md->num_pages <
+ (md->phys_addr >> EFI_PAGE_SHIFT)) {
+ end_hi = (md->num_pages & OVERFLOW_ADDR_MASK)
+ >> OVERFLOW_ADDR_SHIFT;
+
+ if ((md->phys_addr & U64_HIGH_BIT) && !(end & U64_HIGH_BIT))
+ end_hi += 1;
+ } else {
+ return true;
+ }
+
+ pr_warn_once(FW_BUG "Invalid EFI memory map entries:\n");
+
+ if (end_hi) {
+ pr_warn("mem%02u: %s range=[0x%016llx-0x%llx%016llx] (invalid)\n",
+ i, efi_md_typeattr_format(buf, sizeof(buf), md),
+ md->phys_addr, end_hi, end);
+ } else {
+ pr_warn("mem%02u: %s range=[0x%016llx-0x%016llx] (invalid)\n",
+ i, efi_md_typeattr_format(buf, sizeof(buf), md),
+ md->phys_addr, end);
+ }
+ return false;
+}
+
+static void __init efi_clean_memmap(void)
+{
+ efi_memory_desc_t *out = efi.memmap.map;
+ const efi_memory_desc_t *in = out;
+ const efi_memory_desc_t *end = efi.memmap.map_end;
+ int i, n_removal;
+
+ for (i = n_removal = 0; in < end; i++) {
+ if (efi_memmap_entry_valid(in, i)) {
+ if (out != in)
+ memcpy(out, in, efi.memmap.desc_size);
+ out = (void *)out + efi.memmap.desc_size;
+ } else {
+ n_removal++;
+ }
+ in = (void *)in + efi.memmap.desc_size;
+ }
+
+ if (n_removal > 0) {
+ u64 size = efi.memmap.nr_map - n_removal;
+
+ pr_warn("Removing %d invalid memory map entries.\n", n_removal);
+ efi_memmap_install(efi.memmap.phys_map, size);
+ }
+}
+
void __init efi_print_memmap(void)
{
efi_memory_desc_t *md;
@@ -472,6 +536,8 @@
}
}
+ efi_clean_memmap();
+
if (efi_enabled(EFI_DBG))
efi_print_memmap();
}
diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c
index 319148b..2f25a36 100644
--- a/arch/x86/platform/efi/efi_64.c
+++ b/arch/x86/platform/efi/efi_64.c
@@ -269,6 +269,22 @@
efi_scratch.use_pgd = true;
/*
+ * Certain firmware versions are way too sentimential and still believe
+ * they are exclusive and unquestionable owners of the first physical page,
+ * even though they explicitly mark it as EFI_CONVENTIONAL_MEMORY
+ * (but then write-access it later during SetVirtualAddressMap()).
+ *
+ * Create a 1:1 mapping for this page, to avoid triple faults during early
+ * boot with such firmware. We are free to hand this page to the BIOS,
+ * as trim_bios_range() will reserve the first page and isolate it away
+ * from memory allocators anyway.
+ */
+ if (kernel_map_pages_in_pgd(pgd, 0x0, 0x0, 1, _PAGE_RW)) {
+ pr_err("Failed to create 1:1 mapping for the first page!\n");
+ return 1;
+ }
+
+ /*
* When making calls to the firmware everything needs to be 1:1
* mapped and addressable with 32-bit pointers. Map the kernel
* text and allocate a new stack because we can't rely on the
diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c
index 10aca63..30031d5 100644
--- a/arch/x86/platform/efi/quirks.c
+++ b/arch/x86/platform/efi/quirks.c
@@ -214,7 +214,7 @@
new_size = efi.memmap.desc_size * num_entries;
- new_phys = memblock_alloc(new_size, 0);
+ new_phys = efi_memmap_alloc(num_entries);
if (!new_phys) {
pr_err("Could not allocate boot services memmap\n");
return;
@@ -355,7 +355,7 @@
}
new_size = efi.memmap.desc_size * num_entries;
- new_phys = memblock_alloc(new_size, 0);
+ new_phys = efi_memmap_alloc(num_entries);
if (!new_phys) {
pr_err("Failed to allocate new EFI memmap\n");
return;
diff --git a/arch/x86/platform/mellanox/mlx-platform.c b/arch/x86/platform/mellanox/mlx-platform.c
index 7dcfcca..c0355d7 100644
--- a/arch/x86/platform/mellanox/mlx-platform.c
+++ b/arch/x86/platform/mellanox/mlx-platform.c
@@ -233,7 +233,7 @@
return 0;
fail_platform_mux_register:
- for (i--; i > 0 ; i--)
+ while (--i >= 0)
platform_device_unregister(priv->pdev_mux[i]);
platform_device_unregister(priv->pdev_i2c);
fail_alloc:
diff --git a/arch/x86/xen/pci-swiotlb-xen.c b/arch/x86/xen/pci-swiotlb-xen.c
index 0e98e5d..5f8b4b0 100644
--- a/arch/x86/xen/pci-swiotlb-xen.c
+++ b/arch/x86/xen/pci-swiotlb-xen.c
@@ -49,7 +49,7 @@
* activate this IOMMU. If running as PV privileged, activate it
* irregardless.
*/
- if ((xen_initial_domain() || swiotlb || swiotlb_force))
+ if (xen_initial_domain() || swiotlb || swiotlb_force == SWIOTLB_FORCE)
xen_swiotlb = 1;
/* If we are running under Xen, we MUST disable the native SWIOTLB.
diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c
index 9fa27ce..311acad 100644
--- a/arch/x86/xen/smp.c
+++ b/arch/x86/xen/smp.c
@@ -87,12 +87,6 @@
cpu_data(cpu).x86_max_cores = 1;
set_cpu_sibling_map(cpu);
- /*
- * identify_cpu() may have set logical_pkg_id to -1 due
- * to incorrect phys_proc_id. Let's re-comupte it.
- */
- topology_update_package_map(apic->cpu_present_to_apicid(cpu), cpu);
-
xen_setup_cpu_clockevents();
notify_cpu_starting(cpu);
diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c
index 88a044a..32cdc2c 100644
--- a/arch/xtensa/kernel/setup.c
+++ b/arch/xtensa/kernel/setup.c
@@ -540,7 +540,7 @@
void cpu_reset(void)
{
-#if XCHAL_HAVE_PTP_MMU
+#if XCHAL_HAVE_PTP_MMU && IS_ENABLED(CONFIG_MMU)
local_irq_disable();
/*
* We have full MMU: all autoload ways, ways 7, 8 and 9 of DTLB must
diff --git a/block/blk-mq.c b/block/blk-mq.c
index f3d27a6..81caceb 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -895,7 +895,7 @@
return WORK_CPU_UNBOUND;
if (--hctx->next_cpu_batch <= 0) {
- int cpu = hctx->next_cpu, next_cpu;
+ int next_cpu;
next_cpu = cpumask_next(hctx->next_cpu, hctx->cpumask);
if (next_cpu >= nr_cpu_ids)
@@ -903,8 +903,6 @@
hctx->next_cpu = next_cpu;
hctx->next_cpu_batch = BLK_MQ_CPU_WORK_BATCH;
-
- return cpu;
}
return hctx->next_cpu;
@@ -1332,9 +1330,9 @@
blk_mq_put_ctx(data.ctx);
if (!old_rq)
goto done;
- if (!blk_mq_direct_issue_request(old_rq, &cookie))
- goto done;
- blk_mq_insert_request(old_rq, false, true, true);
+ if (test_bit(BLK_MQ_S_STOPPED, &data.hctx->state) ||
+ blk_mq_direct_issue_request(old_rq, &cookie) != 0)
+ blk_mq_insert_request(old_rq, false, true, true);
goto done;
}
diff --git a/block/bsg.c b/block/bsg.c
index d214e92..b9a5361 100644
--- a/block/bsg.c
+++ b/block/bsg.c
@@ -655,6 +655,9 @@
dprintk("%s: write %Zd bytes\n", bd->name, count);
+ if (unlikely(segment_eq(get_fs(), KERNEL_DS)))
+ return -EINVAL;
+
bsg_set_block(bd, file);
bytes_written = 0;
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 5e24d88..3ab6807 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -1596,7 +1596,7 @@
{
struct cfq_group_data *cgd;
- cgd = kzalloc(sizeof(*cgd), GFP_KERNEL);
+ cgd = kzalloc(sizeof(*cgd), gfp);
if (!cgd)
return NULL;
return &cgd->cpd;
diff --git a/build.config.goldfish.arm b/build.config.goldfish.arm
new file mode 100644
index 0000000..866da93
--- /dev/null
+++ b/build.config.goldfish.arm
@@ -0,0 +1,12 @@
+ARCH=arm
+BRANCH=android-4.4
+CROSS_COMPILE=arm-linux-androidkernel-
+DEFCONFIG=ranchu_defconfig
+EXTRA_CMDS=''
+KERNEL_DIR=common
+LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin
+FILES="
+arch/arm/boot/zImage
+vmlinux
+System.map
+"
diff --git a/build.config.goldfish.arm64 b/build.config.goldfish.arm64
new file mode 100644
index 0000000..9c963cf
--- /dev/null
+++ b/build.config.goldfish.arm64
@@ -0,0 +1,12 @@
+ARCH=arm64
+BRANCH=android-4.4
+CROSS_COMPILE=aarch64-linux-android-
+DEFCONFIG=ranchu64_defconfig
+EXTRA_CMDS=''
+KERNEL_DIR=common
+LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin
+FILES="
+arch/arm64/boot/Image
+vmlinux
+System.map
+"
diff --git a/build.config.goldfish.mips b/build.config.goldfish.mips
new file mode 100644
index 0000000..8af53d2
--- /dev/null
+++ b/build.config.goldfish.mips
@@ -0,0 +1,11 @@
+ARCH=mips
+BRANCH=android-4.4
+CROSS_COMPILE=mips64el-linux-android-
+DEFCONFIG=ranchu_defconfig
+EXTRA_CMDS=''
+KERNEL_DIR=common
+LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9/bin
+FILES="
+vmlinux
+System.map
+"
diff --git a/build.config.goldfish.mips64 b/build.config.goldfish.mips64
new file mode 100644
index 0000000..2a33d36
--- /dev/null
+++ b/build.config.goldfish.mips64
@@ -0,0 +1,11 @@
+ARCH=mips
+BRANCH=android-4.4
+CROSS_COMPILE=mips64el-linux-android-
+DEFCONFIG=ranchu64_defconfig
+EXTRA_CMDS=''
+KERNEL_DIR=common
+LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9/bin
+FILES="
+vmlinux
+System.map
+"
diff --git a/build.config.goldfish.x86 b/build.config.goldfish.x86
new file mode 100644
index 0000000..f86253f
--- /dev/null
+++ b/build.config.goldfish.x86
@@ -0,0 +1,12 @@
+ARCH=x86
+BRANCH=android-4.4
+CROSS_COMPILE=x86_64-linux-android-
+DEFCONFIG=i386_ranchu_defconfig
+EXTRA_CMDS=''
+KERNEL_DIR=common
+LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin
+FILES="
+arch/x86/boot/bzImage
+vmlinux
+System.map
+"
diff --git a/build.config.goldfish.x86_64 b/build.config.goldfish.x86_64
new file mode 100644
index 0000000..e173886
--- /dev/null
+++ b/build.config.goldfish.x86_64
@@ -0,0 +1,12 @@
+ARCH=x86_64
+BRANCH=android-4.4
+CROSS_COMPILE=x86_64-linux-android-
+DEFCONFIG=x86_64_ranchu_defconfig
+EXTRA_CMDS=''
+KERNEL_DIR=common
+LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin
+FILES="
+arch/x86/boot/bzImage
+vmlinux
+System.map
+"
diff --git a/crypto/algapi.c b/crypto/algapi.c
index df939b5..1fad2a6 100644
--- a/crypto/algapi.c
+++ b/crypto/algapi.c
@@ -356,6 +356,7 @@
struct crypto_larval *larval;
int err;
+ alg->cra_flags &= ~CRYPTO_ALG_DEAD;
err = crypto_check_alg(alg);
if (err)
return err;
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index 0d099a2..e53bef6 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -852,6 +852,8 @@
if (ghes_read_estatus(ghes, 1)) {
ghes_clear_estatus(ghes);
continue;
+ } else {
+ ret = NMI_HANDLED;
}
sev = ghes_severity(ghes->estatus->error_severity);
@@ -863,12 +865,11 @@
__process_error(ghes);
ghes_clear_estatus(ghes);
-
- ret = NMI_HANDLED;
}
#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
- irq_work_queue(&ghes_proc_irq_work);
+ if (ret == NMI_HANDLED)
+ irq_work_queue(&ghes_proc_irq_work);
#endif
atomic_dec(&ghes_in_nmi);
return ret;
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index d0d0504..e0ea8f5 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -784,8 +784,10 @@
/* Add per logical CPU nodes for reading its feedback counters. */
cpu_dev = get_cpu_device(pr->id);
- if (!cpu_dev)
+ if (!cpu_dev) {
+ ret = -EINVAL;
goto out_free;
+ }
ret = kobject_init_and_add(&cpc_ptr->kobj, &cppc_ktype, &cpu_dev->kobj,
"acpi_cppc");
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index a6b36fc..02ded25 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -296,6 +296,26 @@
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
},
},
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1123661 */
+ .callback = video_detect_force_native,
+ .ident = "Dell XPS 17 L702X",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L702X"),
+ },
+ },
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1204476 */
+ /* https://bugs.launchpad.net/ubuntu/+source/linux-lts-trusty/+bug/1416940 */
+ .callback = video_detect_force_native,
+ .ident = "HP Pavilion dv6",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv6 Notebook PC"),
+ },
+ },
+
{ },
};
diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig
index bdfc6c6..a82fc02 100644
--- a/drivers/android/Kconfig
+++ b/drivers/android/Kconfig
@@ -19,6 +19,18 @@
Android process, using Binder to identify, invoke and pass arguments
between said processes.
+config ANDROID_BINDER_DEVICES
+ string "Android Binder devices"
+ depends on ANDROID_BINDER_IPC
+ default "binder"
+ ---help---
+ Default value for the binder.devices parameter.
+
+ The binder.devices parameter is a comma-separated list of strings
+ that specifies the names of the binder device nodes that will be
+ created. Each binder device has its own context manager, and is
+ therefore logically separated from the other devices.
+
config ANDROID_BINDER_IPC_32BIT
bool
depends on !64BIT && ANDROID_BINDER_IPC
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 3c71b98..1bd8401 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -50,14 +50,13 @@
static DEFINE_MUTEX(binder_deferred_lock);
static DEFINE_MUTEX(binder_mmap_lock);
+static HLIST_HEAD(binder_devices);
static HLIST_HEAD(binder_procs);
static HLIST_HEAD(binder_deferred_list);
static HLIST_HEAD(binder_dead_nodes);
static struct dentry *binder_debugfs_dir_entry_root;
static struct dentry *binder_debugfs_dir_entry_proc;
-static struct binder_node *binder_context_mgr_node;
-static kuid_t binder_context_mgr_uid = INVALID_UID;
static int binder_last_id;
#define BINDER_DEBUG_ENTRY(name) \
@@ -115,6 +114,9 @@
static bool binder_debug_no_lock;
module_param_named(proc_no_lock, binder_debug_no_lock, bool, S_IWUSR | S_IRUGO);
+static char *binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES;
+module_param_named(devices, binder_devices_param, charp, S_IRUGO);
+
static DECLARE_WAIT_QUEUE_HEAD(binder_user_error_wait);
static int binder_stop_on_user_error;
@@ -145,6 +147,17 @@
binder_stop_on_user_error = 2; \
} while (0)
+#define to_flat_binder_object(hdr) \
+ container_of(hdr, struct flat_binder_object, hdr)
+
+#define to_binder_fd_object(hdr) container_of(hdr, struct binder_fd_object, hdr)
+
+#define to_binder_buffer_object(hdr) \
+ container_of(hdr, struct binder_buffer_object, hdr)
+
+#define to_binder_fd_array_object(hdr) \
+ container_of(hdr, struct binder_fd_array_object, hdr)
+
enum binder_stat_types {
BINDER_STAT_PROC,
BINDER_STAT_THREAD,
@@ -158,7 +171,7 @@
struct binder_stats {
int br[_IOC_NR(BR_FAILED_REPLY) + 1];
- int bc[_IOC_NR(BC_DEAD_BINDER_DONE) + 1];
+ int bc[_IOC_NR(BC_REPLY_SG) + 1];
int obj_created[BINDER_STAT_COUNT];
int obj_deleted[BINDER_STAT_COUNT];
};
@@ -186,6 +199,7 @@
int to_node;
int data_size;
int offsets_size;
+ const char *context_name;
};
struct binder_transaction_log {
int next;
@@ -210,6 +224,18 @@
return e;
}
+struct binder_context {
+ struct binder_node *binder_context_mgr_node;
+ kuid_t binder_context_mgr_uid;
+ const char *name;
+};
+
+struct binder_device {
+ struct hlist_node hlist;
+ struct miscdevice miscdev;
+ struct binder_context context;
+};
+
struct binder_work {
struct list_head entry;
enum {
@@ -282,6 +308,7 @@
struct binder_node *target_node;
size_t data_size;
size_t offsets_size;
+ size_t extra_buffers_size;
uint8_t data[0];
};
@@ -325,6 +352,7 @@
int ready_threads;
long default_priority;
struct dentry *debugfs_entry;
+ struct binder_context *context;
};
enum {
@@ -648,7 +676,9 @@
static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc,
size_t data_size,
- size_t offsets_size, int is_async)
+ size_t offsets_size,
+ size_t extra_buffers_size,
+ int is_async)
{
struct rb_node *n = proc->free_buffers.rb_node;
struct binder_buffer *buffer;
@@ -656,7 +686,7 @@
struct rb_node *best_fit = NULL;
void *has_page_addr;
void *end_page_addr;
- size_t size;
+ size_t size, data_offsets_size;
if (proc->vma == NULL) {
pr_err("%d: binder_alloc_buf, no vma\n",
@@ -664,15 +694,20 @@
return NULL;
}
- size = ALIGN(data_size, sizeof(void *)) +
+ data_offsets_size = ALIGN(data_size, sizeof(void *)) +
ALIGN(offsets_size, sizeof(void *));
- if (size < data_size || size < offsets_size) {
+ if (data_offsets_size < data_size || data_offsets_size < offsets_size) {
binder_user_error("%d: got transaction with invalid size %zd-%zd\n",
proc->pid, data_size, offsets_size);
return NULL;
}
-
+ size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *));
+ if (size < data_offsets_size || size < extra_buffers_size) {
+ binder_user_error("%d: got transaction with invalid extra_buffers_size %zd\n",
+ proc->pid, extra_buffers_size);
+ return NULL;
+ }
if (is_async &&
proc->free_async_space < size + sizeof(struct binder_buffer)) {
binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
@@ -741,6 +776,7 @@
proc->pid, size, buffer);
buffer->data_size = data_size;
buffer->offsets_size = offsets_size;
+ buffer->extra_buffers_size = extra_buffers_size;
buffer->async_transaction = is_async;
if (is_async) {
proc->free_async_space -= size + sizeof(struct binder_buffer);
@@ -815,7 +851,8 @@
buffer_size = binder_buffer_size(proc, buffer);
size = ALIGN(buffer->data_size, sizeof(void *)) +
- ALIGN(buffer->offsets_size, sizeof(void *));
+ ALIGN(buffer->offsets_size, sizeof(void *)) +
+ ALIGN(buffer->extra_buffers_size, sizeof(void *));
binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
"%d: binder_free_buf %p size %zd buffer_size %zd\n",
@@ -929,8 +966,10 @@
if (internal) {
if (target_list == NULL &&
node->internal_strong_refs == 0 &&
- !(node == binder_context_mgr_node &&
- node->has_strong_ref)) {
+ !(node->proc &&
+ node == node->proc->context->
+ binder_context_mgr_node &&
+ node->has_strong_ref)) {
pr_err("invalid inc strong node for %d\n",
node->debug_id);
return -EINVAL;
@@ -1031,6 +1070,7 @@
struct rb_node **p = &proc->refs_by_node.rb_node;
struct rb_node *parent = NULL;
struct binder_ref *ref, *new_ref;
+ struct binder_context *context = proc->context;
while (*p) {
parent = *p;
@@ -1053,7 +1093,7 @@
rb_link_node(&new_ref->rb_node_node, parent, p);
rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node);
- new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1;
+ new_ref->desc = (node == context->binder_context_mgr_node) ? 0 : 1;
for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
ref = rb_entry(n, struct binder_ref, rb_node_desc);
if (ref->desc > new_ref->desc)
@@ -1240,11 +1280,158 @@
}
}
+/**
+ * binder_validate_object() - checks for a valid metadata object in a buffer.
+ * @buffer: binder_buffer that we're parsing.
+ * @offset: offset in the buffer at which to validate an object.
+ *
+ * Return: If there's a valid metadata object at @offset in @buffer, the
+ * size of that object. Otherwise, it returns zero.
+ */
+static size_t binder_validate_object(struct binder_buffer *buffer, u64 offset)
+{
+ /* Check if we can read a header first */
+ struct binder_object_header *hdr;
+ size_t object_size = 0;
+
+ if (offset > buffer->data_size - sizeof(*hdr) ||
+ buffer->data_size < sizeof(*hdr) ||
+ !IS_ALIGNED(offset, sizeof(u32)))
+ return 0;
+
+ /* Ok, now see if we can read a complete object. */
+ hdr = (struct binder_object_header *)(buffer->data + offset);
+ switch (hdr->type) {
+ case BINDER_TYPE_BINDER:
+ case BINDER_TYPE_WEAK_BINDER:
+ case BINDER_TYPE_HANDLE:
+ case BINDER_TYPE_WEAK_HANDLE:
+ object_size = sizeof(struct flat_binder_object);
+ break;
+ case BINDER_TYPE_FD:
+ object_size = sizeof(struct binder_fd_object);
+ break;
+ case BINDER_TYPE_PTR:
+ object_size = sizeof(struct binder_buffer_object);
+ break;
+ case BINDER_TYPE_FDA:
+ object_size = sizeof(struct binder_fd_array_object);
+ break;
+ default:
+ return 0;
+ }
+ if (offset <= buffer->data_size - object_size &&
+ buffer->data_size >= object_size)
+ return object_size;
+ else
+ return 0;
+}
+
+/**
+ * binder_validate_ptr() - validates binder_buffer_object in a binder_buffer.
+ * @b: binder_buffer containing the object
+ * @index: index in offset array at which the binder_buffer_object is
+ * located
+ * @start: points to the start of the offset array
+ * @num_valid: the number of valid offsets in the offset array
+ *
+ * Return: If @index is within the valid range of the offset array
+ * described by @start and @num_valid, and if there's a valid
+ * binder_buffer_object at the offset found in index @index
+ * of the offset array, that object is returned. Otherwise,
+ * %NULL is returned.
+ * Note that the offset found in index @index itself is not
+ * verified; this function assumes that @num_valid elements
+ * from @start were previously verified to have valid offsets.
+ */
+static struct binder_buffer_object *binder_validate_ptr(struct binder_buffer *b,
+ binder_size_t index,
+ binder_size_t *start,
+ binder_size_t num_valid)
+{
+ struct binder_buffer_object *buffer_obj;
+ binder_size_t *offp;
+
+ if (index >= num_valid)
+ return NULL;
+
+ offp = start + index;
+ buffer_obj = (struct binder_buffer_object *)(b->data + *offp);
+ if (buffer_obj->hdr.type != BINDER_TYPE_PTR)
+ return NULL;
+
+ return buffer_obj;
+}
+
+/**
+ * binder_validate_fixup() - validates pointer/fd fixups happen in order.
+ * @b: transaction buffer
+ * @objects_start start of objects buffer
+ * @buffer: binder_buffer_object in which to fix up
+ * @offset: start offset in @buffer to fix up
+ * @last_obj: last binder_buffer_object that we fixed up in
+ * @last_min_offset: minimum fixup offset in @last_obj
+ *
+ * Return: %true if a fixup in buffer @buffer at offset @offset is
+ * allowed.
+ *
+ * For safety reasons, we only allow fixups inside a buffer to happen
+ * at increasing offsets; additionally, we only allow fixup on the last
+ * buffer object that was verified, or one of its parents.
+ *
+ * Example of what is allowed:
+ *
+ * A
+ * B (parent = A, offset = 0)
+ * C (parent = A, offset = 16)
+ * D (parent = C, offset = 0)
+ * E (parent = A, offset = 32) // min_offset is 16 (C.parent_offset)
+ *
+ * Examples of what is not allowed:
+ *
+ * Decreasing offsets within the same parent:
+ * A
+ * C (parent = A, offset = 16)
+ * B (parent = A, offset = 0) // decreasing offset within A
+ *
+ * Referring to a parent that wasn't the last object or any of its parents:
+ * A
+ * B (parent = A, offset = 0)
+ * C (parent = A, offset = 0)
+ * C (parent = A, offset = 16)
+ * D (parent = B, offset = 0) // B is not A or any of A's parents
+ */
+static bool binder_validate_fixup(struct binder_buffer *b,
+ binder_size_t *objects_start,
+ struct binder_buffer_object *buffer,
+ binder_size_t fixup_offset,
+ struct binder_buffer_object *last_obj,
+ binder_size_t last_min_offset)
+{
+ if (!last_obj) {
+ /* Nothing to fix up in */
+ return false;
+ }
+
+ while (last_obj != buffer) {
+ /*
+ * Safe to retrieve the parent of last_obj, since it
+ * was already previously verified by the driver.
+ */
+ if ((last_obj->flags & BINDER_BUFFER_FLAG_HAS_PARENT) == 0)
+ return false;
+ last_min_offset = last_obj->parent_offset + sizeof(uintptr_t);
+ last_obj = (struct binder_buffer_object *)
+ (b->data + *(objects_start + last_obj->parent));
+ }
+ return (fixup_offset >= last_min_offset);
+}
+
static void binder_transaction_buffer_release(struct binder_proc *proc,
struct binder_buffer *buffer,
binder_size_t *failed_at)
{
- binder_size_t *offp, *off_end;
+ binder_size_t *offp, *off_start, *off_end;
int debug_id = buffer->debug_id;
binder_debug(BINDER_DEBUG_TRANSACTION,
@@ -1255,28 +1442,30 @@
if (buffer->target_node)
binder_dec_node(buffer->target_node, 1, 0);
- offp = (binder_size_t *)(buffer->data +
- ALIGN(buffer->data_size, sizeof(void *)));
+ off_start = (binder_size_t *)(buffer->data +
+ ALIGN(buffer->data_size, sizeof(void *)));
if (failed_at)
off_end = failed_at;
else
- off_end = (void *)offp + buffer->offsets_size;
- for (; offp < off_end; offp++) {
- struct flat_binder_object *fp;
+ off_end = (void *)off_start + buffer->offsets_size;
+ for (offp = off_start; offp < off_end; offp++) {
+ struct binder_object_header *hdr;
+ size_t object_size = binder_validate_object(buffer, *offp);
- if (*offp > buffer->data_size - sizeof(*fp) ||
- buffer->data_size < sizeof(*fp) ||
- !IS_ALIGNED(*offp, sizeof(u32))) {
- pr_err("transaction release %d bad offset %lld, size %zd\n",
+ if (object_size == 0) {
+ pr_err("transaction release %d bad object at offset %lld, size %zd\n",
debug_id, (u64)*offp, buffer->data_size);
continue;
}
- fp = (struct flat_binder_object *)(buffer->data + *offp);
- switch (fp->type) {
+ hdr = (struct binder_object_header *)(buffer->data + *offp);
+ switch (hdr->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
- struct binder_node *node = binder_get_node(proc, fp->binder);
+ struct flat_binder_object *fp;
+ struct binder_node *node;
+ fp = to_flat_binder_object(hdr);
+ node = binder_get_node(proc, fp->binder);
if (node == NULL) {
pr_err("transaction release %d bad node %016llx\n",
debug_id, (u64)fp->binder);
@@ -1285,15 +1474,17 @@
binder_debug(BINDER_DEBUG_TRANSACTION,
" node %d u%016llx\n",
node->debug_id, (u64)node->ptr);
- binder_dec_node(node, fp->type == BINDER_TYPE_BINDER, 0);
+ binder_dec_node(node, hdr->type == BINDER_TYPE_BINDER,
+ 0);
} break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
+ struct flat_binder_object *fp;
struct binder_ref *ref;
+ fp = to_flat_binder_object(hdr);
ref = binder_get_ref(proc, fp->handle,
- fp->type == BINDER_TYPE_HANDLE);
-
+ hdr->type == BINDER_TYPE_HANDLE);
if (ref == NULL) {
pr_err("transaction release %d bad handle %d\n",
debug_id, fp->handle);
@@ -1302,32 +1493,348 @@
binder_debug(BINDER_DEBUG_TRANSACTION,
" ref %d desc %d (node %d)\n",
ref->debug_id, ref->desc, ref->node->debug_id);
- binder_dec_ref(ref, fp->type == BINDER_TYPE_HANDLE);
+ binder_dec_ref(ref, hdr->type == BINDER_TYPE_HANDLE);
} break;
- case BINDER_TYPE_FD:
- binder_debug(BINDER_DEBUG_TRANSACTION,
- " fd %d\n", fp->handle);
- if (failed_at)
- task_close_fd(proc, fp->handle);
- break;
+ case BINDER_TYPE_FD: {
+ struct binder_fd_object *fp = to_binder_fd_object(hdr);
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " fd %d\n", fp->fd);
+ if (failed_at)
+ task_close_fd(proc, fp->fd);
+ } break;
+ case BINDER_TYPE_PTR:
+ /*
+ * Nothing to do here, this will get cleaned up when the
+ * transaction buffer gets freed
+ */
+ break;
+ case BINDER_TYPE_FDA: {
+ struct binder_fd_array_object *fda;
+ struct binder_buffer_object *parent;
+ uintptr_t parent_buffer;
+ u32 *fd_array;
+ size_t fd_index;
+ binder_size_t fd_buf_size;
+
+ fda = to_binder_fd_array_object(hdr);
+ parent = binder_validate_ptr(buffer, fda->parent,
+ off_start,
+ offp - off_start);
+ if (!parent) {
+ pr_err("transaction release %d bad parent offset",
+ debug_id);
+ continue;
+ }
+ /*
+ * Since the parent was already fixed up, convert it
+ * back to kernel address space to access it
+ */
+ parent_buffer = parent->buffer -
+ proc->user_buffer_offset;
+
+ fd_buf_size = sizeof(u32) * fda->num_fds;
+ if (fda->num_fds >= SIZE_MAX / sizeof(u32)) {
+ pr_err("transaction release %d invalid number of fds (%lld)\n",
+ debug_id, (u64)fda->num_fds);
+ continue;
+ }
+ if (fd_buf_size > parent->length ||
+ fda->parent_offset > parent->length - fd_buf_size) {
+ /* No space for all file descriptors here. */
+ pr_err("transaction release %d not enough space for %lld fds in buffer\n",
+ debug_id, (u64)fda->num_fds);
+ continue;
+ }
+ fd_array = (u32 *)(parent_buffer + fda->parent_offset);
+ for (fd_index = 0; fd_index < fda->num_fds; fd_index++)
+ task_close_fd(proc, fd_array[fd_index]);
+ } break;
default:
pr_err("transaction release %d bad object type %x\n",
- debug_id, fp->type);
+ debug_id, hdr->type);
break;
}
}
}
+static int binder_translate_binder(struct flat_binder_object *fp,
+ struct binder_transaction *t,
+ struct binder_thread *thread)
+{
+ struct binder_node *node;
+ struct binder_ref *ref;
+ struct binder_proc *proc = thread->proc;
+ struct binder_proc *target_proc = t->to_proc;
+
+ node = binder_get_node(proc, fp->binder);
+ if (!node) {
+ node = binder_new_node(proc, fp->binder, fp->cookie);
+ if (!node)
+ return -ENOMEM;
+
+ node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
+ node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
+ }
+ if (fp->cookie != node->cookie) {
+ binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
+ proc->pid, thread->pid, (u64)fp->binder,
+ node->debug_id, (u64)fp->cookie,
+ (u64)node->cookie);
+ return -EINVAL;
+ }
+ if (security_binder_transfer_binder(proc->tsk, target_proc->tsk))
+ return -EPERM;
+
+ ref = binder_get_ref_for_node(target_proc, node);
+ if (!ref)
+ return -EINVAL;
+
+ if (fp->hdr.type == BINDER_TYPE_BINDER)
+ fp->hdr.type = BINDER_TYPE_HANDLE;
+ else
+ fp->hdr.type = BINDER_TYPE_WEAK_HANDLE;
+ fp->binder = 0;
+ fp->handle = ref->desc;
+ fp->cookie = 0;
+ binder_inc_ref(ref, fp->hdr.type == BINDER_TYPE_HANDLE, &thread->todo);
+
+ trace_binder_transaction_node_to_ref(t, node, ref);
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " node %d u%016llx -> ref %d desc %d\n",
+ node->debug_id, (u64)node->ptr,
+ ref->debug_id, ref->desc);
+
+ return 0;
+}
+
+static int binder_translate_handle(struct flat_binder_object *fp,
+ struct binder_transaction *t,
+ struct binder_thread *thread)
+{
+ struct binder_ref *ref;
+ struct binder_proc *proc = thread->proc;
+ struct binder_proc *target_proc = t->to_proc;
+
+ ref = binder_get_ref(proc, fp->handle,
+ fp->hdr.type == BINDER_TYPE_HANDLE);
+ if (!ref) {
+ binder_user_error("%d:%d got transaction with invalid handle, %d\n",
+ proc->pid, thread->pid, fp->handle);
+ return -EINVAL;
+ }
+ if (security_binder_transfer_binder(proc->tsk, target_proc->tsk))
+ return -EPERM;
+
+ if (ref->node->proc == target_proc) {
+ if (fp->hdr.type == BINDER_TYPE_HANDLE)
+ fp->hdr.type = BINDER_TYPE_BINDER;
+ else
+ fp->hdr.type = BINDER_TYPE_WEAK_BINDER;
+ fp->binder = ref->node->ptr;
+ fp->cookie = ref->node->cookie;
+ binder_inc_node(ref->node, fp->hdr.type == BINDER_TYPE_BINDER,
+ 0, NULL);
+ trace_binder_transaction_ref_to_node(t, ref);
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " ref %d desc %d -> node %d u%016llx\n",
+ ref->debug_id, ref->desc, ref->node->debug_id,
+ (u64)ref->node->ptr);
+ } else {
+ struct binder_ref *new_ref;
+
+ new_ref = binder_get_ref_for_node(target_proc, ref->node);
+ if (!new_ref)
+ return -EINVAL;
+
+ fp->binder = 0;
+ fp->handle = new_ref->desc;
+ fp->cookie = 0;
+ binder_inc_ref(new_ref, fp->hdr.type == BINDER_TYPE_HANDLE,
+ NULL);
+ trace_binder_transaction_ref_to_ref(t, ref, new_ref);
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " ref %d desc %d -> ref %d desc %d (node %d)\n",
+ ref->debug_id, ref->desc, new_ref->debug_id,
+ new_ref->desc, ref->node->debug_id);
+ }
+ return 0;
+}
+
+static int binder_translate_fd(int fd,
+ struct binder_transaction *t,
+ struct binder_thread *thread,
+ struct binder_transaction *in_reply_to)
+{
+ struct binder_proc *proc = thread->proc;
+ struct binder_proc *target_proc = t->to_proc;
+ int target_fd;
+ struct file *file;
+ int ret;
+ bool target_allows_fd;
+
+ if (in_reply_to)
+ target_allows_fd = !!(in_reply_to->flags & TF_ACCEPT_FDS);
+ else
+ target_allows_fd = t->buffer->target_node->accept_fds;
+ if (!target_allows_fd) {
+ binder_user_error("%d:%d got %s with fd, %d, but target does not allow fds\n",
+ proc->pid, thread->pid,
+ in_reply_to ? "reply" : "transaction",
+ fd);
+ ret = -EPERM;
+ goto err_fd_not_accepted;
+ }
+
+ file = fget(fd);
+ if (!file) {
+ binder_user_error("%d:%d got transaction with invalid fd, %d\n",
+ proc->pid, thread->pid, fd);
+ ret = -EBADF;
+ goto err_fget;
+ }
+ ret = security_binder_transfer_file(proc->tsk, target_proc->tsk, file);
+ if (ret < 0) {
+ ret = -EPERM;
+ goto err_security;
+ }
+
+ target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);
+ if (target_fd < 0) {
+ ret = -ENOMEM;
+ goto err_get_unused_fd;
+ }
+ task_fd_install(target_proc, target_fd, file);
+ trace_binder_transaction_fd(t, fd, target_fd);
+ binder_debug(BINDER_DEBUG_TRANSACTION, " fd %d -> %d\n",
+ fd, target_fd);
+
+ return target_fd;
+
+err_get_unused_fd:
+err_security:
+ fput(file);
+err_fget:
+err_fd_not_accepted:
+ return ret;
+}
+
+static int binder_translate_fd_array(struct binder_fd_array_object *fda,
+ struct binder_buffer_object *parent,
+ struct binder_transaction *t,
+ struct binder_thread *thread,
+ struct binder_transaction *in_reply_to)
+{
+ binder_size_t fdi, fd_buf_size, num_installed_fds;
+ int target_fd;
+ uintptr_t parent_buffer;
+ u32 *fd_array;
+ struct binder_proc *proc = thread->proc;
+ struct binder_proc *target_proc = t->to_proc;
+
+ fd_buf_size = sizeof(u32) * fda->num_fds;
+ if (fda->num_fds >= SIZE_MAX / sizeof(u32)) {
+ binder_user_error("%d:%d got transaction with invalid number of fds (%lld)\n",
+ proc->pid, thread->pid, (u64)fda->num_fds);
+ return -EINVAL;
+ }
+ if (fd_buf_size > parent->length ||
+ fda->parent_offset > parent->length - fd_buf_size) {
+ /* No space for all file descriptors here. */
+ binder_user_error("%d:%d not enough space to store %lld fds in buffer\n",
+ proc->pid, thread->pid, (u64)fda->num_fds);
+ return -EINVAL;
+ }
+ /*
+ * Since the parent was already fixed up, convert it
+ * back to the kernel address space to access it
+ */
+ parent_buffer = parent->buffer - target_proc->user_buffer_offset;
+ fd_array = (u32 *)(parent_buffer + fda->parent_offset);
+ if (!IS_ALIGNED((unsigned long)fd_array, sizeof(u32))) {
+ binder_user_error("%d:%d parent offset not aligned correctly.\n",
+ proc->pid, thread->pid);
+ return -EINVAL;
+ }
+ for (fdi = 0; fdi < fda->num_fds; fdi++) {
+ target_fd = binder_translate_fd(fd_array[fdi], t, thread,
+ in_reply_to);
+ if (target_fd < 0)
+ goto err_translate_fd_failed;
+ fd_array[fdi] = target_fd;
+ }
+ return 0;
+
+err_translate_fd_failed:
+ /*
+ * Failed to allocate fd or security error, free fds
+ * installed so far.
+ */
+ num_installed_fds = fdi;
+ for (fdi = 0; fdi < num_installed_fds; fdi++)
+ task_close_fd(target_proc, fd_array[fdi]);
+ return target_fd;
+}
+
+static int binder_fixup_parent(struct binder_transaction *t,
+ struct binder_thread *thread,
+ struct binder_buffer_object *bp,
+ binder_size_t *off_start,
+ binder_size_t num_valid,
+ struct binder_buffer_object *last_fixup_obj,
+ binder_size_t last_fixup_min_off)
+{
+ struct binder_buffer_object *parent;
+ u8 *parent_buffer;
+ struct binder_buffer *b = t->buffer;
+ struct binder_proc *proc = thread->proc;
+ struct binder_proc *target_proc = t->to_proc;
+
+ if (!(bp->flags & BINDER_BUFFER_FLAG_HAS_PARENT))
+ return 0;
+
+ parent = binder_validate_ptr(b, bp->parent, off_start, num_valid);
+ if (!parent) {
+ binder_user_error("%d:%d got transaction with invalid parent offset or type\n",
+ proc->pid, thread->pid);
+ return -EINVAL;
+ }
+
+ if (!binder_validate_fixup(b, off_start,
+ parent, bp->parent_offset,
+ last_fixup_obj,
+ last_fixup_min_off)) {
+ binder_user_error("%d:%d got transaction with out-of-order buffer fixup\n",
+ proc->pid, thread->pid);
+ return -EINVAL;
+ }
+
+ if (parent->length < sizeof(binder_uintptr_t) ||
+ bp->parent_offset > parent->length - sizeof(binder_uintptr_t)) {
+ /* No space for a pointer here! */
+ binder_user_error("%d:%d got transaction with invalid parent offset\n",
+ proc->pid, thread->pid);
+ return -EINVAL;
+ }
+ parent_buffer = (u8 *)(parent->buffer -
+ target_proc->user_buffer_offset);
+ *(binder_uintptr_t *)(parent_buffer + bp->parent_offset) = bp->buffer;
+
+ return 0;
+}
+
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
- struct binder_transaction_data *tr, int reply)
+ struct binder_transaction_data *tr, int reply,
+ binder_size_t extra_buffers_size)
{
+ int ret;
struct binder_transaction *t;
struct binder_work *tcomplete;
- binder_size_t *offp, *off_end;
+ binder_size_t *offp, *off_end, *off_start;
binder_size_t off_min;
+ u8 *sg_bufp, *sg_buf_end;
struct binder_proc *target_proc;
struct binder_thread *target_thread = NULL;
struct binder_node *target_node = NULL;
@@ -1336,6 +1843,9 @@
struct binder_transaction *in_reply_to = NULL;
struct binder_transaction_log_entry *e;
uint32_t return_error;
+ struct binder_buffer_object *last_fixup_obj = NULL;
+ binder_size_t last_fixup_min_off = 0;
+ struct binder_context *context = proc->context;
e = binder_transaction_log_add(&binder_transaction_log);
e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY);
@@ -1344,6 +1854,7 @@
e->target_handle = tr->target.handle;
e->data_size = tr->data_size;
e->offsets_size = tr->offsets_size;
+ e->context_name = proc->context->name;
if (reply) {
in_reply_to = thread->transaction_stack;
@@ -1396,7 +1907,7 @@
}
target_node = ref->node;
} else {
- target_node = binder_context_mgr_node;
+ target_node = context->binder_context_mgr_node;
if (target_node == NULL) {
return_error = BR_DEAD_REPLY;
goto err_no_context_mgr_node;
@@ -1463,20 +1974,22 @@
if (reply)
binder_debug(BINDER_DEBUG_TRANSACTION,
- "%d:%d BC_REPLY %d -> %d:%d, data %016llx-%016llx size %lld-%lld\n",
+ "%d:%d BC_REPLY %d -> %d:%d, data %016llx-%016llx size %lld-%lld-%lld\n",
proc->pid, thread->pid, t->debug_id,
target_proc->pid, target_thread->pid,
(u64)tr->data.ptr.buffer,
(u64)tr->data.ptr.offsets,
- (u64)tr->data_size, (u64)tr->offsets_size);
+ (u64)tr->data_size, (u64)tr->offsets_size,
+ (u64)extra_buffers_size);
else
binder_debug(BINDER_DEBUG_TRANSACTION,
- "%d:%d BC_TRANSACTION %d -> %d - node %d, data %016llx-%016llx size %lld-%lld\n",
+ "%d:%d BC_TRANSACTION %d -> %d - node %d, data %016llx-%016llx size %lld-%lld-%lld\n",
proc->pid, thread->pid, t->debug_id,
target_proc->pid, target_node->debug_id,
(u64)tr->data.ptr.buffer,
(u64)tr->data.ptr.offsets,
- (u64)tr->data_size, (u64)tr->offsets_size);
+ (u64)tr->data_size, (u64)tr->offsets_size,
+ (u64)extra_buffers_size);
if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread;
@@ -1492,7 +2005,8 @@
trace_binder_transaction(reply, t, target_node);
t->buffer = binder_alloc_buf(target_proc, tr->data_size,
- tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
+ tr->offsets_size, extra_buffers_size,
+ !reply && (t->flags & TF_ONE_WAY));
if (t->buffer == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_alloc_buf_failed;
@@ -1505,8 +2019,9 @@
if (target_node)
binder_inc_node(target_node, 1, 0, NULL);
- offp = (binder_size_t *)(t->buffer->data +
- ALIGN(tr->data_size, sizeof(void *)));
+ off_start = (binder_size_t *)(t->buffer->data +
+ ALIGN(tr->data_size, sizeof(void *)));
+ offp = off_start;
if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
tr->data.ptr.buffer, tr->data_size)) {
@@ -1528,177 +2043,138 @@
return_error = BR_FAILED_REPLY;
goto err_bad_offset;
}
- off_end = (void *)offp + tr->offsets_size;
+ if (!IS_ALIGNED(extra_buffers_size, sizeof(u64))) {
+ binder_user_error("%d:%d got transaction with unaligned buffers size, %lld\n",
+ proc->pid, thread->pid,
+ (u64)extra_buffers_size);
+ return_error = BR_FAILED_REPLY;
+ goto err_bad_offset;
+ }
+ off_end = (void *)off_start + tr->offsets_size;
+ sg_bufp = (u8 *)(PTR_ALIGN(off_end, sizeof(void *)));
+ sg_buf_end = sg_bufp + extra_buffers_size;
off_min = 0;
for (; offp < off_end; offp++) {
- struct flat_binder_object *fp;
+ struct binder_object_header *hdr;
+ size_t object_size = binder_validate_object(t->buffer, *offp);
- if (*offp > t->buffer->data_size - sizeof(*fp) ||
- *offp < off_min ||
- t->buffer->data_size < sizeof(*fp) ||
- !IS_ALIGNED(*offp, sizeof(u32))) {
- binder_user_error("%d:%d got transaction with invalid offset, %lld (min %lld, max %lld)\n",
+ if (object_size == 0 || *offp < off_min) {
+ binder_user_error("%d:%d got transaction with invalid offset (%lld, min %lld max %lld) or object.\n",
proc->pid, thread->pid, (u64)*offp,
(u64)off_min,
- (u64)(t->buffer->data_size -
- sizeof(*fp)));
+ (u64)t->buffer->data_size);
return_error = BR_FAILED_REPLY;
goto err_bad_offset;
}
- fp = (struct flat_binder_object *)(t->buffer->data + *offp);
- off_min = *offp + sizeof(struct flat_binder_object);
- switch (fp->type) {
+
+ hdr = (struct binder_object_header *)(t->buffer->data + *offp);
+ off_min = *offp + object_size;
+ switch (hdr->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
- struct binder_ref *ref;
- struct binder_node *node = binder_get_node(proc, fp->binder);
+ struct flat_binder_object *fp;
- if (node == NULL) {
- node = binder_new_node(proc, fp->binder, fp->cookie);
- if (node == NULL) {
- return_error = BR_FAILED_REPLY;
- goto err_binder_new_node_failed;
- }
- node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
- node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
- }
- if (fp->cookie != node->cookie) {
- binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
- proc->pid, thread->pid,
- (u64)fp->binder, node->debug_id,
- (u64)fp->cookie, (u64)node->cookie);
+ fp = to_flat_binder_object(hdr);
+ ret = binder_translate_binder(fp, t, thread);
+ if (ret < 0) {
return_error = BR_FAILED_REPLY;
- goto err_binder_get_ref_for_node_failed;
+ goto err_translate_failed;
}
- if (security_binder_transfer_binder(proc->tsk,
- target_proc->tsk)) {
- return_error = BR_FAILED_REPLY;
- goto err_binder_get_ref_for_node_failed;
- }
- ref = binder_get_ref_for_node(target_proc, node);
- if (ref == NULL) {
- return_error = BR_FAILED_REPLY;
- goto err_binder_get_ref_for_node_failed;
- }
- if (fp->type == BINDER_TYPE_BINDER)
- fp->type = BINDER_TYPE_HANDLE;
- else
- fp->type = BINDER_TYPE_WEAK_HANDLE;
- fp->binder = 0;
- fp->handle = ref->desc;
- fp->cookie = 0;
- binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
- &thread->todo);
-
- trace_binder_transaction_node_to_ref(t, node, ref);
- binder_debug(BINDER_DEBUG_TRANSACTION,
- " node %d u%016llx -> ref %d desc %d\n",
- node->debug_id, (u64)node->ptr,
- ref->debug_id, ref->desc);
} break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
- struct binder_ref *ref;
+ struct flat_binder_object *fp;
- ref = binder_get_ref(proc, fp->handle,
- fp->type == BINDER_TYPE_HANDLE);
-
- if (ref == NULL) {
- binder_user_error("%d:%d got transaction with invalid handle, %d\n",
- proc->pid,
- thread->pid, fp->handle);
+ fp = to_flat_binder_object(hdr);
+ ret = binder_translate_handle(fp, t, thread);
+ if (ret < 0) {
return_error = BR_FAILED_REPLY;
- goto err_binder_get_ref_failed;
- }
- if (security_binder_transfer_binder(proc->tsk,
- target_proc->tsk)) {
- return_error = BR_FAILED_REPLY;
- goto err_binder_get_ref_failed;
- }
- if (ref->node->proc == target_proc) {
- if (fp->type == BINDER_TYPE_HANDLE)
- fp->type = BINDER_TYPE_BINDER;
- else
- fp->type = BINDER_TYPE_WEAK_BINDER;
- fp->binder = ref->node->ptr;
- fp->cookie = ref->node->cookie;
- binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);
- trace_binder_transaction_ref_to_node(t, ref);
- binder_debug(BINDER_DEBUG_TRANSACTION,
- " ref %d desc %d -> node %d u%016llx\n",
- ref->debug_id, ref->desc, ref->node->debug_id,
- (u64)ref->node->ptr);
- } else {
- struct binder_ref *new_ref;
-
- new_ref = binder_get_ref_for_node(target_proc, ref->node);
- if (new_ref == NULL) {
- return_error = BR_FAILED_REPLY;
- goto err_binder_get_ref_for_node_failed;
- }
- fp->binder = 0;
- fp->handle = new_ref->desc;
- fp->cookie = 0;
- binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);
- trace_binder_transaction_ref_to_ref(t, ref,
- new_ref);
- binder_debug(BINDER_DEBUG_TRANSACTION,
- " ref %d desc %d -> ref %d desc %d (node %d)\n",
- ref->debug_id, ref->desc, new_ref->debug_id,
- new_ref->desc, ref->node->debug_id);
+ goto err_translate_failed;
}
} break;
case BINDER_TYPE_FD: {
- int target_fd;
- struct file *file;
+ struct binder_fd_object *fp = to_binder_fd_object(hdr);
+ int target_fd = binder_translate_fd(fp->fd, t, thread,
+ in_reply_to);
- if (reply) {
- if (!(in_reply_to->flags & TF_ACCEPT_FDS)) {
- binder_user_error("%d:%d got reply with fd, %d, but target does not allow fds\n",
- proc->pid, thread->pid, fp->handle);
- return_error = BR_FAILED_REPLY;
- goto err_fd_not_allowed;
- }
- } else if (!target_node->accept_fds) {
- binder_user_error("%d:%d got transaction with fd, %d, but target does not allow fds\n",
- proc->pid, thread->pid, fp->handle);
- return_error = BR_FAILED_REPLY;
- goto err_fd_not_allowed;
- }
-
- file = fget(fp->handle);
- if (file == NULL) {
- binder_user_error("%d:%d got transaction with invalid fd, %d\n",
- proc->pid, thread->pid, fp->handle);
- return_error = BR_FAILED_REPLY;
- goto err_fget_failed;
- }
- if (security_binder_transfer_file(proc->tsk,
- target_proc->tsk,
- file) < 0) {
- fput(file);
- return_error = BR_FAILED_REPLY;
- goto err_get_unused_fd_failed;
- }
- target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);
if (target_fd < 0) {
- fput(file);
return_error = BR_FAILED_REPLY;
- goto err_get_unused_fd_failed;
+ goto err_translate_failed;
}
- task_fd_install(target_proc, target_fd, file);
- trace_binder_transaction_fd(t, fp->handle, target_fd);
- binder_debug(BINDER_DEBUG_TRANSACTION,
- " fd %d -> %d\n", fp->handle, target_fd);
- /* TODO: fput? */
- fp->binder = 0;
- fp->handle = target_fd;
+ fp->pad_binder = 0;
+ fp->fd = target_fd;
} break;
+ case BINDER_TYPE_FDA: {
+ struct binder_fd_array_object *fda =
+ to_binder_fd_array_object(hdr);
+ struct binder_buffer_object *parent =
+ binder_validate_ptr(t->buffer, fda->parent,
+ off_start,
+ offp - off_start);
+ if (!parent) {
+ binder_user_error("%d:%d got transaction with invalid parent offset or type\n",
+ proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ goto err_bad_parent;
+ }
+ if (!binder_validate_fixup(t->buffer, off_start,
+ parent, fda->parent_offset,
+ last_fixup_obj,
+ last_fixup_min_off)) {
+ binder_user_error("%d:%d got transaction with out-of-order buffer fixup\n",
+ proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ goto err_bad_parent;
+ }
+ ret = binder_translate_fd_array(fda, parent, t, thread,
+ in_reply_to);
+ if (ret < 0) {
+ return_error = BR_FAILED_REPLY;
+ goto err_translate_failed;
+ }
+ last_fixup_obj = parent;
+ last_fixup_min_off =
+ fda->parent_offset + sizeof(u32) * fda->num_fds;
+ } break;
+ case BINDER_TYPE_PTR: {
+ struct binder_buffer_object *bp =
+ to_binder_buffer_object(hdr);
+ size_t buf_left = sg_buf_end - sg_bufp;
+ if (bp->length > buf_left) {
+ binder_user_error("%d:%d got transaction with too large buffer\n",
+ proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ goto err_bad_offset;
+ }
+ if (copy_from_user(sg_bufp,
+ (const void __user *)(uintptr_t)
+ bp->buffer, bp->length)) {
+ binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
+ proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ goto err_copy_data_failed;
+ }
+ /* Fixup buffer pointer to target proc address space */
+ bp->buffer = (uintptr_t)sg_bufp +
+ target_proc->user_buffer_offset;
+ sg_bufp += ALIGN(bp->length, sizeof(u64));
+
+ ret = binder_fixup_parent(t, thread, bp, off_start,
+ offp - off_start,
+ last_fixup_obj,
+ last_fixup_min_off);
+ if (ret < 0) {
+ return_error = BR_FAILED_REPLY;
+ goto err_translate_failed;
+ }
+ last_fixup_obj = bp;
+ last_fixup_min_off = 0;
+ } break;
default:
binder_user_error("%d:%d got transaction with invalid object type, %x\n",
- proc->pid, thread->pid, fp->type);
+ proc->pid, thread->pid, hdr->type);
return_error = BR_FAILED_REPLY;
goto err_bad_object_type;
}
@@ -1728,14 +2204,10 @@
wake_up_interruptible(target_wait);
return;
-err_get_unused_fd_failed:
-err_fget_failed:
-err_fd_not_allowed:
-err_binder_get_ref_for_node_failed:
-err_binder_get_ref_failed:
-err_binder_new_node_failed:
+err_translate_failed:
err_bad_object_type:
err_bad_offset:
+err_bad_parent:
err_copy_data_failed:
trace_binder_transaction_failed_buffer_release(t->buffer);
binder_transaction_buffer_release(target_proc, t->buffer, offp);
@@ -1779,6 +2251,7 @@
binder_size_t *consumed)
{
uint32_t cmd;
+ struct binder_context *context = proc->context;
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
@@ -1805,10 +2278,10 @@
if (get_user(target, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
- if (target == 0 && binder_context_mgr_node &&
+ if (target == 0 && context->binder_context_mgr_node &&
(cmd == BC_INCREFS || cmd == BC_ACQUIRE)) {
ref = binder_get_ref_for_node(proc,
- binder_context_mgr_node);
+ context->binder_context_mgr_node);
if (ref->desc != target) {
binder_user_error("%d:%d tried to acquire reference to desc 0, got %d instead\n",
proc->pid, thread->pid,
@@ -1953,6 +2426,17 @@
break;
}
+ case BC_TRANSACTION_SG:
+ case BC_REPLY_SG: {
+ struct binder_transaction_data_sg tr;
+
+ if (copy_from_user(&tr, ptr, sizeof(tr)))
+ return -EFAULT;
+ ptr += sizeof(tr);
+ binder_transaction(proc, thread, &tr.transaction_data,
+ cmd == BC_REPLY_SG, tr.buffers_size);
+ break;
+ }
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr;
@@ -1960,7 +2444,8 @@
if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
- binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
+ binder_transaction(proc, thread, &tr,
+ cmd == BC_REPLY, 0);
break;
}
@@ -2714,9 +3199,11 @@
{
int ret = 0;
struct binder_proc *proc = filp->private_data;
+ struct binder_context *context = proc->context;
+
kuid_t curr_euid = current_euid();
- if (binder_context_mgr_node != NULL) {
+ if (context->binder_context_mgr_node) {
pr_err("BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
goto out;
@@ -2724,27 +3211,27 @@
ret = security_binder_set_context_mgr(proc->tsk);
if (ret < 0)
goto out;
- if (uid_valid(binder_context_mgr_uid)) {
- if (!uid_eq(binder_context_mgr_uid, curr_euid)) {
+ if (uid_valid(context->binder_context_mgr_uid)) {
+ if (!uid_eq(context->binder_context_mgr_uid, curr_euid)) {
pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
from_kuid(&init_user_ns, curr_euid),
from_kuid(&init_user_ns,
- binder_context_mgr_uid));
+ context->binder_context_mgr_uid));
ret = -EPERM;
goto out;
}
} else {
- binder_context_mgr_uid = curr_euid;
+ context->binder_context_mgr_uid = curr_euid;
}
- binder_context_mgr_node = binder_new_node(proc, 0, 0);
- if (binder_context_mgr_node == NULL) {
+ context->binder_context_mgr_node = binder_new_node(proc, 0, 0);
+ if (!context->binder_context_mgr_node) {
ret = -ENOMEM;
goto out;
}
- binder_context_mgr_node->local_weak_refs++;
- binder_context_mgr_node->local_strong_refs++;
- binder_context_mgr_node->has_strong_ref = 1;
- binder_context_mgr_node->has_weak_ref = 1;
+ context->binder_context_mgr_node->local_weak_refs++;
+ context->binder_context_mgr_node->local_strong_refs++;
+ context->binder_context_mgr_node->has_strong_ref = 1;
+ context->binder_context_mgr_node->has_weak_ref = 1;
out:
return ret;
}
@@ -2969,6 +3456,7 @@
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc;
+ struct binder_device *binder_dev;
binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
current->group_leader->pid, current->pid);
@@ -2982,6 +3470,9 @@
INIT_LIST_HEAD(&proc->todo);
init_waitqueue_head(&proc->wait);
proc->default_priority = task_nice(current);
+ binder_dev = container_of(filp->private_data, struct binder_device,
+ miscdev);
+ proc->context = &binder_dev->context;
binder_lock(__func__);
@@ -2997,8 +3488,17 @@
char strbuf[11];
snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
+ /*
+ * proc debug entries are shared between contexts, so
+ * this will fail if the process tries to open the driver
+ * again with a different context. The priting code will
+ * anyway print all contexts that a given PID has, so this
+ * is not a problem.
+ */
proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,
- binder_debugfs_dir_entry_proc, proc, &binder_proc_fops);
+ binder_debugfs_dir_entry_proc,
+ (void *)(unsigned long)proc->pid,
+ &binder_proc_fops);
}
return 0;
@@ -3091,6 +3591,7 @@
static void binder_deferred_release(struct binder_proc *proc)
{
struct binder_transaction *t;
+ struct binder_context *context = proc->context;
struct rb_node *n;
int threads, nodes, incoming_refs, outgoing_refs, buffers,
active_transactions, page_count;
@@ -3100,11 +3601,12 @@
hlist_del(&proc->proc_node);
- if (binder_context_mgr_node && binder_context_mgr_node->proc == proc) {
+ if (context->binder_context_mgr_node &&
+ context->binder_context_mgr_node->proc == proc) {
binder_debug(BINDER_DEBUG_DEAD_BINDER,
"%s: %d context_mgr_node gone\n",
__func__, proc->pid);
- binder_context_mgr_node = NULL;
+ context->binder_context_mgr_node = NULL;
}
threads = 0;
@@ -3391,6 +3893,7 @@
size_t header_pos;
seq_printf(m, "proc %d\n", proc->pid);
+ seq_printf(m, "context %s\n", proc->context->name);
header_pos = m->count;
for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n))
@@ -3460,7 +3963,9 @@
"BC_EXIT_LOOPER",
"BC_REQUEST_DEATH_NOTIFICATION",
"BC_CLEAR_DEATH_NOTIFICATION",
- "BC_DEAD_BINDER_DONE"
+ "BC_DEAD_BINDER_DONE",
+ "BC_TRANSACTION_SG",
+ "BC_REPLY_SG",
};
static const char * const binder_objstat_strings[] = {
@@ -3515,6 +4020,7 @@
int count, strong, weak;
seq_printf(m, "proc %d\n", proc->pid);
+ seq_printf(m, "context %s\n", proc->context->name);
count = 0;
for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n))
count++;
@@ -3622,23 +4128,18 @@
static int binder_proc_show(struct seq_file *m, void *unused)
{
struct binder_proc *itr;
- struct binder_proc *proc = m->private;
+ int pid = (unsigned long)m->private;
int do_lock = !binder_debug_no_lock;
- bool valid_proc = false;
if (do_lock)
binder_lock(__func__);
hlist_for_each_entry(itr, &binder_procs, proc_node) {
- if (itr == proc) {
- valid_proc = true;
- break;
+ if (itr->pid == pid) {
+ seq_puts(m, "binder proc state:\n");
+ print_binder_proc(m, itr, 1);
}
}
- if (valid_proc) {
- seq_puts(m, "binder proc state:\n");
- print_binder_proc(m, proc, 1);
- }
if (do_lock)
binder_unlock(__func__);
return 0;
@@ -3648,11 +4149,11 @@
struct binder_transaction_log_entry *e)
{
seq_printf(m,
- "%d: %s from %d:%d to %d:%d node %d handle %d size %d:%d\n",
+ "%d: %s from %d:%d to %d:%d context %s node %d handle %d size %d:%d\n",
e->debug_id, (e->call_type == 2) ? "reply" :
((e->call_type == 1) ? "async" : "call "), e->from_proc,
- e->from_thread, e->to_proc, e->to_thread, e->to_node,
- e->target_handle, e->data_size, e->offsets_size);
+ e->from_thread, e->to_proc, e->to_thread, e->context_name,
+ e->to_node, e->target_handle, e->data_size, e->offsets_size);
}
static int binder_transaction_log_show(struct seq_file *m, void *unused)
@@ -3680,26 +4181,50 @@
.release = binder_release,
};
-static struct miscdevice binder_miscdev = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "binder",
- .fops = &binder_fops
-};
-
BINDER_DEBUG_ENTRY(state);
BINDER_DEBUG_ENTRY(stats);
BINDER_DEBUG_ENTRY(transactions);
BINDER_DEBUG_ENTRY(transaction_log);
+static int __init init_binder_device(const char *name)
+{
+ int ret;
+ struct binder_device *binder_device;
+
+ binder_device = kzalloc(sizeof(*binder_device), GFP_KERNEL);
+ if (!binder_device)
+ return -ENOMEM;
+
+ binder_device->miscdev.fops = &binder_fops;
+ binder_device->miscdev.minor = MISC_DYNAMIC_MINOR;
+ binder_device->miscdev.name = name;
+
+ binder_device->context.binder_context_mgr_uid = INVALID_UID;
+ binder_device->context.name = name;
+
+ ret = misc_register(&binder_device->miscdev);
+ if (ret < 0) {
+ kfree(binder_device);
+ return ret;
+ }
+
+ hlist_add_head(&binder_device->hlist, &binder_devices);
+
+ return ret;
+}
+
static int __init binder_init(void)
{
int ret;
+ char *device_name, *device_names;
+ struct binder_device *device;
+ struct hlist_node *tmp;
binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
if (binder_debugfs_dir_entry_root)
binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
binder_debugfs_dir_entry_root);
- ret = misc_register(&binder_miscdev);
+
if (binder_debugfs_dir_entry_root) {
debugfs_create_file("state",
S_IRUGO,
@@ -3727,6 +4252,35 @@
&binder_transaction_log_failed,
&binder_transaction_log_fops);
}
+
+ /*
+ * Copy the module_parameter string, because we don't want to
+ * tokenize it in-place.
+ */
+ device_names = kzalloc(strlen(binder_devices_param) + 1, GFP_KERNEL);
+ if (!device_names) {
+ ret = -ENOMEM;
+ goto err_alloc_device_names_failed;
+ }
+ strcpy(device_names, binder_devices_param);
+
+ while ((device_name = strsep(&device_names, ","))) {
+ ret = init_binder_device(device_name);
+ if (ret)
+ goto err_init_binder_device_failed;
+ }
+
+ return ret;
+
+err_init_binder_device_failed:
+ hlist_for_each_entry_safe(device, tmp, &binder_devices, hlist) {
+ misc_deregister(&device->miscdev);
+ hlist_del(&device->hlist);
+ kfree(device);
+ }
+err_alloc_device_names_failed:
+ debugfs_remove_recursive(binder_debugfs_dir_entry_root);
+
return ret;
}
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 223a770..33e363d 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -1695,6 +1695,8 @@
if (qc->err_mask & ~AC_ERR_OTHER)
qc->err_mask &= ~AC_ERR_OTHER;
+ } else if (qc->tf.command == ATA_CMD_REQ_SENSE_DATA) {
+ qc->result_tf.command |= ATA_SENSE;
}
/* finish up */
@@ -4316,10 +4318,10 @@
{ "ST380013AS", "3.20", ATA_HORKAGE_MAX_SEC_1024 },
/*
- * Device times out with higher max sects.
+ * These devices time out with higher max sects.
* https://bugzilla.kernel.org/show_bug.cgi?id=121671
*/
- { "LITEON CX1-JB256-HP", NULL, ATA_HORKAGE_MAX_SEC_1024 },
+ { "LITEON CX1-JB*-HP", NULL, ATA_HORKAGE_MAX_SEC_1024 },
/* Devices we expect to fail diagnostics */
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index 823e938..2f32782 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -4132,6 +4132,9 @@
host->iomap = NULL;
hpriv->base = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
+ if (!hpriv->base)
+ return -ENOMEM;
+
hpriv->base -= SATAHC0_REG_BASE;
hpriv->clk = clk_get(&pdev->dev, NULL);
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 958e255..26cf6b9 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -956,13 +956,14 @@
timeout = MAX_JIFFY_OFFSET;
}
- retval = wait_for_completion_interruptible_timeout(&buf->completion,
+ timeout = wait_for_completion_interruptible_timeout(&buf->completion,
timeout);
- if (retval == -ERESTARTSYS || !retval) {
+ if (timeout == -ERESTARTSYS || !timeout) {
+ retval = timeout;
mutex_lock(&fw_lock);
fw_load_abort(fw_priv);
mutex_unlock(&fw_lock);
- } else if (retval > 0) {
+ } else if (timeout > 0) {
retval = 0;
}
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 62c63c0..c5cdd19 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -391,33 +391,33 @@
{
struct memory_block *mem = to_memory_block(dev);
unsigned long start_pfn, end_pfn;
+ unsigned long valid_start, valid_end, valid_pages;
unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;
- struct page *first_page;
struct zone *zone;
int zone_shift = 0;
start_pfn = section_nr_to_pfn(mem->start_section_nr);
end_pfn = start_pfn + nr_pages;
- first_page = pfn_to_page(start_pfn);
/* The block contains more than one zone can not be offlined. */
- if (!test_pages_in_a_zone(start_pfn, end_pfn))
+ if (!test_pages_in_a_zone(start_pfn, end_pfn, &valid_start, &valid_end))
return sprintf(buf, "none\n");
- zone = page_zone(first_page);
+ zone = page_zone(pfn_to_page(valid_start));
+ valid_pages = valid_end - valid_start;
/* MMOP_ONLINE_KEEP */
sprintf(buf, "%s", zone->name);
/* MMOP_ONLINE_KERNEL */
- zone_shift = zone_can_shift(start_pfn, nr_pages, ZONE_NORMAL);
+ zone_can_shift(valid_start, valid_pages, ZONE_NORMAL, &zone_shift);
if (zone_shift) {
strcat(buf, " ");
strcat(buf, (zone + zone_shift)->name);
}
/* MMOP_ONLINE_MOVABLE */
- zone_shift = zone_can_shift(start_pfn, nr_pages, ZONE_MOVABLE);
+ zone_can_shift(valid_start, valid_pages, ZONE_MOVABLE, &zone_shift);
if (zone_shift) {
strcat(buf, " ");
strcat(buf, (zone + zone_shift)->name);
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c
index 4c7c6da..6441dfd 100644
--- a/drivers/base/power/opp/core.c
+++ b/drivers/base/power/opp/core.c
@@ -584,6 +584,7 @@
struct clk *clk;
unsigned long freq, old_freq;
unsigned long u_volt, u_volt_min, u_volt_max;
+ unsigned long old_u_volt, old_u_volt_min, old_u_volt_max;
int ret;
if (unlikely(!target_freq)) {
@@ -633,6 +634,14 @@
return ret;
}
+ if (IS_ERR(old_opp)) {
+ old_u_volt = 0;
+ } else {
+ old_u_volt = old_opp->u_volt;
+ old_u_volt_min = old_opp->u_volt_min;
+ old_u_volt_max = old_opp->u_volt_max;
+ }
+
u_volt = opp->u_volt;
u_volt_min = opp->u_volt_min;
u_volt_max = opp->u_volt_max;
@@ -677,9 +686,10 @@
__func__, old_freq);
restore_voltage:
/* This shouldn't harm even if the voltages weren't updated earlier */
- if (!IS_ERR(old_opp))
- _set_opp_voltage(dev, reg, old_opp->u_volt,
- old_opp->u_volt_min, old_opp->u_volt_max);
+ if (old_u_volt) {
+ _set_opp_voltage(dev, reg, old_u_volt, old_u_volt_min,
+ old_u_volt_max);
+ }
return ret;
}
@@ -1316,7 +1326,7 @@
* that this function is *NOT* called under RCU protection or in contexts where
* mutex cannot be locked.
*/
-int dev_pm_opp_set_regulator(struct device *dev, const char *name)
+struct opp_table *dev_pm_opp_set_regulator(struct device *dev, const char *name)
{
struct opp_table *opp_table;
struct regulator *reg;
@@ -1354,20 +1364,20 @@
opp_table->regulator = reg;
mutex_unlock(&opp_table_lock);
- return 0;
+ return opp_table;
err:
_remove_opp_table(opp_table);
unlock:
mutex_unlock(&opp_table_lock);
- return ret;
+ return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_set_regulator);
/**
* dev_pm_opp_put_regulator() - Releases resources blocked for regulator
- * @dev: Device for which regulator was set.
+ * @opp_table: OPP table returned from dev_pm_opp_set_regulator().
*
* Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks
@@ -1375,22 +1385,12 @@
* that this function is *NOT* called under RCU protection or in contexts where
* mutex cannot be locked.
*/
-void dev_pm_opp_put_regulator(struct device *dev)
+void dev_pm_opp_put_regulator(struct opp_table *opp_table)
{
- struct opp_table *opp_table;
-
mutex_lock(&opp_table_lock);
- /* Check for existing table for 'dev' first */
- opp_table = _find_opp_table(dev);
- if (IS_ERR(opp_table)) {
- dev_err(dev, "Failed to find opp_table: %ld\n",
- PTR_ERR(opp_table));
- goto unlock;
- }
-
if (IS_ERR(opp_table->regulator)) {
- dev_err(dev, "%s: Doesn't have regulator set\n", __func__);
+ pr_err("%s: Doesn't have regulator set\n", __func__);
goto unlock;
}
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 50e30e7..a84332a 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -21,14 +21,22 @@
extern void pm_runtime_reinit(struct device *dev);
extern void pm_runtime_remove(struct device *dev);
+#define WAKE_IRQ_DEDICATED_ALLOCATED BIT(0)
+#define WAKE_IRQ_DEDICATED_MANAGED BIT(1)
+#define WAKE_IRQ_DEDICATED_MASK (WAKE_IRQ_DEDICATED_ALLOCATED | \
+ WAKE_IRQ_DEDICATED_MANAGED)
+
struct wake_irq {
struct device *dev;
+ unsigned int status;
int irq;
- bool dedicated_irq:1;
};
extern void dev_pm_arm_wake_irq(struct wake_irq *wirq);
extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq);
+extern void dev_pm_enable_wake_irq_check(struct device *dev,
+ bool can_change_status);
+extern void dev_pm_disable_wake_irq_check(struct device *dev);
#ifdef CONFIG_PM_SLEEP
@@ -104,6 +112,15 @@
{
}
+static inline void dev_pm_enable_wake_irq_check(struct device *dev,
+ bool can_change_status)
+{
+}
+
+static inline void dev_pm_disable_wake_irq_check(struct device *dev)
+{
+}
+
#endif
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 82a081e..23f3b95 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -515,7 +515,7 @@
callback = RPM_GET_CALLBACK(dev, runtime_suspend);
- dev_pm_enable_wake_irq(dev);
+ dev_pm_enable_wake_irq_check(dev, true);
retval = rpm_callback(callback, dev);
if (retval)
goto fail;
@@ -554,7 +554,7 @@
return retval;
fail:
- dev_pm_disable_wake_irq(dev);
+ dev_pm_disable_wake_irq_check(dev);
__update_runtime_status(dev, RPM_ACTIVE);
dev->power.deferred_resume = false;
wake_up_all(&dev->power.wait_queue);
@@ -737,12 +737,12 @@
callback = RPM_GET_CALLBACK(dev, runtime_resume);
- dev_pm_disable_wake_irq(dev);
+ dev_pm_disable_wake_irq_check(dev);
retval = rpm_callback(callback, dev);
if (retval) {
__update_runtime_status(dev, RPM_SUSPENDED);
pm_runtime_cancel_pending(dev);
- dev_pm_enable_wake_irq(dev);
+ dev_pm_enable_wake_irq_check(dev, false);
} else {
no_callback:
__update_runtime_status(dev, RPM_ACTIVE);
diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
index 0d77cd6..404d94c 100644
--- a/drivers/base/power/wakeirq.c
+++ b/drivers/base/power/wakeirq.c
@@ -110,8 +110,10 @@
dev->power.wakeirq = NULL;
spin_unlock_irqrestore(&dev->power.lock, flags);
- if (wirq->dedicated_irq)
+ if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED) {
free_irq(wirq->irq, wirq);
+ wirq->status &= ~WAKE_IRQ_DEDICATED_MASK;
+ }
kfree(wirq);
}
EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq);
@@ -179,7 +181,6 @@
wirq->dev = dev;
wirq->irq = irq;
- wirq->dedicated_irq = true;
irq_set_status_flags(irq, IRQ_NOAUTOEN);
/*
@@ -195,6 +196,8 @@
if (err)
goto err_free_irq;
+ wirq->status = WAKE_IRQ_DEDICATED_ALLOCATED;
+
return err;
err_free_irq:
@@ -210,9 +213,9 @@
* dev_pm_enable_wake_irq - Enable device wake-up interrupt
* @dev: Device
*
- * Called from the bus code or the device driver for
- * runtime_suspend() to enable the wake-up interrupt while
- * the device is running.
+ * Optionally called from the bus code or the device driver for
+ * runtime_resume() to override the PM runtime core managed wake-up
+ * interrupt handling to enable the wake-up interrupt.
*
* Note that for runtime_suspend()) the wake-up interrupts
* should be unconditionally enabled unlike for suspend()
@@ -222,7 +225,7 @@
{
struct wake_irq *wirq = dev->power.wakeirq;
- if (wirq && wirq->dedicated_irq)
+ if (wirq && (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED))
enable_irq(wirq->irq);
}
EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
@@ -231,20 +234,73 @@
* dev_pm_disable_wake_irq - Disable device wake-up interrupt
* @dev: Device
*
- * Called from the bus code or the device driver for
- * runtime_resume() to disable the wake-up interrupt while
- * the device is running.
+ * Optionally called from the bus code or the device driver for
+ * runtime_suspend() to override the PM runtime core managed wake-up
+ * interrupt handling to disable the wake-up interrupt.
*/
void dev_pm_disable_wake_irq(struct device *dev)
{
struct wake_irq *wirq = dev->power.wakeirq;
- if (wirq && wirq->dedicated_irq)
+ if (wirq && (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED))
disable_irq_nosync(wirq->irq);
}
EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq);
/**
+ * dev_pm_enable_wake_irq_check - Checks and enables wake-up interrupt
+ * @dev: Device
+ * @can_change_status: Can change wake-up interrupt status
+ *
+ * Enables wakeirq conditionally. We need to enable wake-up interrupt
+ * lazily on the first rpm_suspend(). This is needed as the consumer device
+ * starts in RPM_SUSPENDED state, and the the first pm_runtime_get() would
+ * otherwise try to disable already disabled wakeirq. The wake-up interrupt
+ * starts disabled with IRQ_NOAUTOEN set.
+ *
+ * Should be only called from rpm_suspend() and rpm_resume() path.
+ * Caller must hold &dev->power.lock to change wirq->status
+ */
+void dev_pm_enable_wake_irq_check(struct device *dev,
+ bool can_change_status)
+{
+ struct wake_irq *wirq = dev->power.wakeirq;
+
+ if (!wirq || !((wirq->status & WAKE_IRQ_DEDICATED_MASK)))
+ return;
+
+ if (likely(wirq->status & WAKE_IRQ_DEDICATED_MANAGED)) {
+ goto enable;
+ } else if (can_change_status) {
+ wirq->status |= WAKE_IRQ_DEDICATED_MANAGED;
+ goto enable;
+ }
+
+ return;
+
+enable:
+ enable_irq(wirq->irq);
+}
+
+/**
+ * dev_pm_disable_wake_irq_check - Checks and disables wake-up interrupt
+ * @dev: Device
+ *
+ * Disables wake-up interrupt conditionally based on status.
+ * Should be only called from rpm_suspend() and rpm_resume() path.
+ */
+void dev_pm_disable_wake_irq_check(struct device *dev)
+{
+ struct wake_irq *wirq = dev->power.wakeirq;
+
+ if (!wirq || !((wirq->status & WAKE_IRQ_DEDICATED_MASK)))
+ return;
+
+ if (wirq->status & WAKE_IRQ_DEDICATED_MANAGED)
+ disable_irq_nosync(wirq->irq);
+}
+
+/**
* dev_pm_arm_wake_irq - Arm device wake-up
* @wirq: Device wake-up interrupt
*
diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
index f642c42..168fa17 100644
--- a/drivers/bcma/bcma_private.h
+++ b/drivers/bcma/bcma_private.h
@@ -45,6 +45,9 @@
void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc);
void bcma_core_chipcommon_init(struct bcma_drv_cc *cc);
void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable);
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+void bcma_chipco_serial_init(struct bcma_drv_cc *cc);
+#endif /* CONFIG_BCMA_DRIVER_MIPS */
/* driver_chipcommon_b.c */
int bcma_core_chipcommon_b_init(struct bcma_drv_cc_b *ccb);
diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c
index b4f6520..62f5bfa 100644
--- a/drivers/bcma/driver_chipcommon.c
+++ b/drivers/bcma/driver_chipcommon.c
@@ -15,8 +15,6 @@
#include <linux/platform_device.h>
#include <linux/bcma/bcma.h>
-static void bcma_chipco_serial_init(struct bcma_drv_cc *cc);
-
static inline u32 bcma_cc_write32_masked(struct bcma_drv_cc *cc, u16 offset,
u32 mask, u32 value)
{
@@ -186,9 +184,6 @@
if (cc->capabilities & BCMA_CC_CAP_PMU)
bcma_pmu_early_init(cc);
- if (IS_BUILTIN(CONFIG_BCM47XX) && bus->hosttype == BCMA_HOSTTYPE_SOC)
- bcma_chipco_serial_init(cc);
-
if (bus->hosttype == BCMA_HOSTTYPE_SOC)
bcma_core_chipcommon_flash_detect(cc);
@@ -378,9 +373,9 @@
return res;
}
-static void bcma_chipco_serial_init(struct bcma_drv_cc *cc)
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+void bcma_chipco_serial_init(struct bcma_drv_cc *cc)
{
-#if IS_BUILTIN(CONFIG_BCM47XX)
unsigned int irq;
u32 baud_base;
u32 i;
@@ -422,5 +417,5 @@
ports[i].baud_base = baud_base;
ports[i].reg_shift = 0;
}
-#endif /* CONFIG_BCM47XX */
}
+#endif /* CONFIG_BCMA_DRIVER_MIPS */
diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c
index 96f1713..89af807 100644
--- a/drivers/bcma/driver_mips.c
+++ b/drivers/bcma/driver_mips.c
@@ -278,9 +278,12 @@
void bcma_core_mips_early_init(struct bcma_drv_mips *mcore)
{
+ struct bcma_bus *bus = mcore->core->bus;
+
if (mcore->early_setup_done)
return;
+ bcma_chipco_serial_init(&bus->drv_cc);
bcma_core_mips_nvram_init(mcore);
mcore->early_setup_done = true;
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index fa1b7a9..4af8187 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1646,7 +1646,7 @@
blk_mq_start_request(bd->rq);
if (lo->lo_state != Lo_bound)
- return -EIO;
+ return BLK_MQ_RQ_QUEUE_ERROR;
switch (req_op(cmd->rq)) {
case REQ_OP_FLUSH:
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 5545a67..3c3b8f6 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -56,6 +56,7 @@
struct virtio_blk_outhdr out_hdr;
struct virtio_scsi_inhdr in_hdr;
u8 status;
+ u8 sense[SCSI_SENSE_BUFFERSIZE];
struct scatterlist sg[];
};
@@ -102,7 +103,8 @@
}
if (type == cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_SCSI_CMD)) {
- sg_init_one(&sense, vbr->req->sense, SCSI_SENSE_BUFFERSIZE);
+ memcpy(vbr->sense, vbr->req->sense, SCSI_SENSE_BUFFERSIZE);
+ sg_init_one(&sense, vbr->sense, SCSI_SENSE_BUFFERSIZE);
sgs[num_out + num_in++] = &sense;
sg_init_one(&inhdr, &vbr->in_hdr, sizeof(vbr->in_hdr));
sgs[num_out + num_in++] = &inhdr;
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 5497f7f..d2ef51c 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -25,6 +25,7 @@
#include <linux/genhd.h>
#include <linux/highmem.h>
#include <linux/slab.h>
+#include <linux/backing-dev.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <linux/err.h>
@@ -111,6 +112,14 @@
return bvec->bv_len != PAGE_SIZE;
}
+static void zram_revalidate_disk(struct zram *zram)
+{
+ revalidate_disk(zram->disk);
+ /* revalidate_disk reset the BDI_CAP_STABLE_WRITES so set again */
+ zram->disk->queue->backing_dev_info.capabilities |=
+ BDI_CAP_STABLE_WRITES;
+}
+
/*
* Check if request is within bounds and aligned on zram logical blocks.
*/
@@ -1094,15 +1103,9 @@
zram->comp = comp;
zram->disksize = disksize;
set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT);
+ zram_revalidate_disk(zram);
up_write(&zram->init_lock);
- /*
- * Revalidate disk out of the init_lock to avoid lockdep splat.
- * It's okay because disk's capacity is protected by init_lock
- * so that revalidate_disk always sees up-to-date capacity.
- */
- revalidate_disk(zram->disk);
-
return len;
out_destroy_comp:
@@ -1148,7 +1151,7 @@
/* Make sure all the pending I/O are finished */
fsync_bdev(bdev);
zram_reset_device(zram);
- revalidate_disk(zram->disk);
+ zram_revalidate_disk(zram);
bdput(bdev);
mutex_lock(&bdev->bd_mutex);
diff --git a/drivers/bus/arm-ccn.c b/drivers/bus/arm-ccn.c
index d1074d9..aee8346 100644
--- a/drivers/bus/arm-ccn.c
+++ b/drivers/bus/arm-ccn.c
@@ -1570,7 +1570,10 @@
for (i = 0; i < ARRAY_SIZE(arm_ccn_pmu_events); i++)
arm_ccn_pmu_events_attrs[i] = &arm_ccn_pmu_events[i].attr.attr;
- return platform_driver_register(&arm_ccn_driver);
+ ret = platform_driver_register(&arm_ccn_driver);
+ if (ret)
+ cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_CCN_ONLINE);
+ return ret;
}
static void __exit arm_ccn_exit(void)
diff --git a/drivers/bus/vexpress-config.c b/drivers/bus/vexpress-config.c
index 9efdf1d..493e7b9 100644
--- a/drivers/bus/vexpress-config.c
+++ b/drivers/bus/vexpress-config.c
@@ -171,6 +171,7 @@
{
struct device_node *bridge;
struct device *parent;
+ int ret;
bridge = of_parse_phandle(node, "arm,vexpress,config-bridge", 0);
if (!bridge)
@@ -182,7 +183,11 @@
if (WARN_ON(!parent))
return -ENODEV;
- return of_platform_populate(node, NULL, NULL, parent);
+ ret = of_platform_populate(node, NULL, NULL, parent);
+
+ put_device(parent);
+
+ return ret;
}
static int __init vexpress_config_init(void)
diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c
index b831d9e..c4d378e 100644
--- a/drivers/char/diag/diag_masks.c
+++ b/drivers/char/diag/diag_masks.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -448,6 +448,7 @@
DIAG_SET_FEATURE_MASK(F_DIAG_LOG_ON_DEMAND_APPS);
DIAG_SET_FEATURE_MASK(F_DIAG_STM);
DIAG_SET_FEATURE_MASK(F_DIAG_DCI_EXTENDED_HEADER_SUPPORT);
+ DIAG_SET_FEATURE_MASK(F_DIAG_DIAGID_SUPPORT);
if (driver->supports_separate_cmdrsp)
DIAG_SET_FEATURE_MASK(F_DIAG_REQ_RSP_SUPPORT);
if (driver->supports_apps_hdlc_encoding)
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 7973cc5..ea380fb 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -125,6 +125,7 @@
#define DIAG_EXT_MOBILE_ID 0x06
#define DIAG_GET_TIME_API 0x21B
#define DIAG_SET_TIME_API 0x21C
+#define DIAG_GET_DIAG_ID 0x222
#define DIAG_SWITCH_COMMAND 0x081B
#define DIAG_BUFFERING_MODE 0x080C
@@ -257,6 +258,14 @@
#define DIAG_CNTL_TYPE 2
#define DIAG_DCI_TYPE 3
+/*
+ * List of diag ids
+ * 0 is reserved for unknown diag id, 1 for apps, diag ids
+ * for remaining pds are assigned dynamically.
+ */
+#define DIAG_ID_UNKNOWN 0
+#define DIAG_ID_APPS 1
+
/* List of remote processor supported */
enum remote_procs {
MDM = 1,
@@ -278,6 +287,29 @@
uint32_t chip_id;
} __packed;
+struct diag_cmd_diag_id_query_req_t {
+ struct diag_pkt_header_t header;
+ uint8_t version;
+} __packed;
+
+struct diag_id_tbl_t {
+ struct list_head link;
+ uint8_t diag_id;
+ char *process_name;
+} __packed;
+struct diag_id_t {
+ uint8_t diag_id;
+ uint8_t len;
+ char *process_name;
+} __packed;
+
+struct diag_cmd_diag_id_query_rsp_t {
+ struct diag_pkt_header_t header;
+ uint8_t version;
+ uint8_t num_entries;
+ struct diag_id_t entry;
+} __packed;
+
struct diag_cmd_time_sync_query_req_t {
struct diag_pkt_header_t header;
uint8_t version;
@@ -511,6 +543,8 @@
int dci_state;
struct workqueue_struct *diag_dci_wq;
struct list_head cmd_reg_list;
+ struct list_head diag_id_list;
+ struct mutex diag_id_mutex;
struct mutex cmd_reg_mutex;
uint32_t cmd_reg_count;
struct mutex diagfwd_channel_mutex;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 0e924a8..ac777b0 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -3513,7 +3513,9 @@
ret = diagchar_setup_cdev(dev);
if (ret)
goto fail;
-
+ mutex_init(&driver->diag_id_mutex);
+ INIT_LIST_HEAD(&driver->diag_id_list);
+ diag_add_diag_id_to_list(DIAG_ID_APPS, "APPS");
pr_debug("diagchar initialized now");
ret = diagfwd_bridge_init();
if (ret)
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index dbc36c9..3fce72f7 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -650,6 +650,55 @@
return write_len;
}
+int diag_process_diag_id_query_cmd(unsigned char *src_buf, int src_len,
+ unsigned char *dest_buf, int dest_len)
+{
+ int write_len = 0;
+ struct diag_cmd_diag_id_query_req_t *req = NULL;
+ struct diag_cmd_diag_id_query_rsp_t rsp;
+ struct list_head *start;
+ struct list_head *temp;
+ struct diag_id_tbl_t *item = NULL;
+ int rsp_len = 0;
+ int num_entries = 0;
+ uint8_t process_name_len = 0;
+
+ if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0) {
+ pr_err("diag: Invalid input in %s, src_buf:%pK, src_len:%d, dest_buf:%pK, dest_len:%d\n",
+ __func__, src_buf, src_len, dest_buf, dest_len);
+ return -EINVAL;
+ }
+ req = (struct diag_cmd_diag_id_query_req_t *) src_buf;
+ rsp.header.cmd_code = req->header.cmd_code;
+ rsp.header.subsys_id = req->header.subsys_id;
+ rsp.header.subsys_cmd_code = req->header.subsys_cmd_code;
+ rsp.version = req->version;
+ rsp.entry.process_name = NULL;
+ rsp.entry.len = 0;
+ rsp.entry.diag_id = 0;
+ write_len = sizeof(rsp.header) + sizeof(rsp.version) +
+ sizeof(rsp.num_entries);
+ rsp_len = write_len;
+ mutex_lock(&driver->diag_id_mutex);
+ list_for_each_safe(start, temp, &driver->diag_id_list) {
+ item = list_entry(start, struct diag_id_tbl_t, link);
+ memcpy(dest_buf + write_len, &item->diag_id,
+ sizeof(item->diag_id));
+ write_len = write_len + sizeof(item->diag_id);
+ process_name_len = strlen(item->process_name) + 1;
+ memcpy(dest_buf + write_len, &process_name_len,
+ sizeof(process_name_len));
+ write_len = write_len + sizeof(process_name_len);
+ memcpy(dest_buf + write_len, item->process_name,
+ strlen(item->process_name) + 1);
+ write_len = write_len + strlen(item->process_name) + 1;
+ num_entries++;
+ }
+ mutex_unlock(&driver->diag_id_mutex);
+ rsp.num_entries = num_entries;
+ memcpy(dest_buf, &rsp, rsp_len);
+ return write_len;
+}
int diag_process_time_sync_switch_cmd(unsigned char *src_buf, int src_len,
unsigned char *dest_buf, int dest_len)
{
@@ -993,6 +1042,17 @@
diag_send_rsp(driver->apps_rsp_buf, write_len);
return 0;
}
+ /* Check for diag id command */
+ else if ((*buf == DIAG_CMD_DIAG_SUBSYS) &&
+ (*(buf+1) == DIAG_SS_DIAG) &&
+ (*(uint16_t *)(buf+2) == DIAG_GET_DIAG_ID)) {
+ write_len = diag_process_diag_id_query_cmd(buf, len,
+ driver->apps_rsp_buf,
+ DIAG_MAX_RSP_SIZE);
+ if (write_len > 0)
+ diag_send_rsp(driver->apps_rsp_buf, write_len);
+ return 0;
+ }
/* Check for download command */
else if ((chk_apps_master()) && (*buf == 0x3A)) {
/* send response back */
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index 4cbd9da..b262897 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,6 +29,7 @@
/* tracks which peripheral is undergoing SSR */
static uint16_t reg_dirty;
+static uint8_t diag_id = DIAG_ID_APPS;
static void diag_notify_md_client(uint8_t peripheral, int data);
static void diag_mask_update_work_fn(struct work_struct *work)
@@ -648,6 +649,88 @@
}
}
+int diag_add_diag_id_to_list(uint8_t diag_id, char *process_name)
+{
+ struct diag_id_tbl_t *new_item = NULL;
+
+ if (!process_name || diag_id == 0)
+ return -EINVAL;
+
+ new_item = kzalloc(sizeof(struct diag_id_tbl_t), GFP_KERNEL);
+ if (!new_item)
+ return -ENOMEM;
+ kmemleak_not_leak(new_item);
+ new_item->process_name = kzalloc(strlen(process_name), GFP_KERNEL);
+ if (!new_item->process_name) {
+ kfree(new_item);
+ new_item = NULL;
+ return -ENOMEM;
+ }
+ kmemleak_not_leak(new_item->process_name);
+ new_item->diag_id = diag_id;
+ strlcpy(new_item->process_name, process_name, strlen(process_name) + 1);
+ INIT_LIST_HEAD(&new_item->link);
+ mutex_lock(&driver->diag_id_mutex);
+ list_add_tail(&new_item->link, &driver->diag_id_list);
+ mutex_unlock(&driver->diag_id_mutex);
+ return 0;
+}
+
+int diag_query_diag_id(char *process_name, uint8_t *diag_id)
+{
+ struct list_head *start;
+ struct list_head *temp;
+ struct diag_id_tbl_t *item = NULL;
+
+ if (!process_name || !diag_id)
+ return -EINVAL;
+
+ mutex_lock(&driver->diag_id_mutex);
+ list_for_each_safe(start, temp, &driver->diag_id_list) {
+ item = list_entry(start, struct diag_id_tbl_t, link);
+ if (strcmp(item->process_name, process_name) == 0) {
+ *diag_id = item->diag_id;
+ mutex_unlock(&driver->diag_id_mutex);
+ return 1;
+ }
+ }
+ mutex_unlock(&driver->diag_id_mutex);
+ return 0;
+}
+static void process_diagid(uint8_t *buf, uint32_t len,
+ uint8_t peripheral)
+{
+ struct diag_ctrl_diagid *header = NULL;
+ struct diag_ctrl_diagid ctrl_pkt;
+ char *process_name = NULL;
+ int err = 0;
+ uint8_t local_diag_id = 0;
+
+ if (!buf || len == 0 || peripheral >= NUM_PERIPHERALS)
+ return;
+ header = (struct diag_ctrl_diagid *)buf;
+ process_name = (char *)&header->process_name;
+ if (diag_query_diag_id(process_name, &local_diag_id))
+ ctrl_pkt.diag_id = local_diag_id;
+ else {
+ diag_id++;
+ diag_add_diag_id_to_list(diag_id, process_name);
+ ctrl_pkt.diag_id = diag_id;
+ }
+ ctrl_pkt.pkt_id = DIAG_CTRL_MSG_DIAGID;
+ ctrl_pkt.version = 1;
+ strlcpy((char *)&ctrl_pkt.process_name, process_name,
+ strlen(process_name) + 1);
+ ctrl_pkt.len = sizeof(ctrl_pkt.diag_id) + sizeof(ctrl_pkt.version) +
+ strlen(process_name) + 1;
+ err = diagfwd_write(peripheral, TYPE_CNTL, &ctrl_pkt, ctrl_pkt.len +
+ sizeof(ctrl_pkt.pkt_id) + sizeof(ctrl_pkt.len));
+ if (err && err != -ENODEV) {
+ pr_err("diag: Unable to send diag id ctrl packet to peripheral %d, err: %d\n",
+ peripheral, err);
+ }
+}
+
void diag_cntl_process_read_data(struct diagfwd_info *p_info, void *buf,
int len)
{
@@ -700,6 +783,10 @@
process_pd_status(ptr, ctrl_pkt->len,
p_info->peripheral);
break;
+ case DIAG_CTRL_MSG_DIAGID:
+ process_diagid(ptr, ctrl_pkt->len,
+ p_info->peripheral);
+ break;
default:
pr_debug("diag: Control packet %d not supported\n",
ctrl_pkt->pkt_id);
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
index 129cb1f..7823040 100644
--- a/drivers/char/diag/diagfwd_cntl.h
+++ b/drivers/char/diag/diagfwd_cntl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -46,7 +46,7 @@
#define DIAG_CTRL_MSG_DCI_HANDSHAKE_PKT 29
#define DIAG_CTRL_MSG_PD_STATUS 30
#define DIAG_CTRL_MSG_TIME_SYNC_PKT 31
-
+#define DIAG_CTRL_MSG_DIAGID 33
/*
* Feature Mask Definitions: Feature mask is used to specify Diag features
* supported by the Apps processor
@@ -67,6 +67,7 @@
#define F_DIAG_MASK_CENTRALIZATION 11
#define F_DIAG_SOCKETS_ENABLED 13
#define F_DIAG_DCI_EXTENDED_HEADER_SUPPORT 14
+#define F_DIAG_DIAGID_SUPPORT 15
#define ENABLE_SEPARATE_CMDRSP 1
#define DISABLE_SEPARATE_CMDRSP 0
@@ -261,7 +262,16 @@
uint8_t low_wm_val;
} __packed;
+struct diag_ctrl_diagid {
+ uint32_t pkt_id;
+ uint32_t len;
+ uint32_t version;
+ uint32_t diag_id;
+ char process_name[30];
+} __packed;
+
int diagfwd_cntl_init(void);
+int diag_add_diag_id_to_list(uint8_t diag_id, char *process_name);
void diagfwd_cntl_channel_init(void);
void diagfwd_cntl_exit(void);
void diag_cntl_channel_open(struct diagfwd_info *p_info);
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 5bb1985..6d9cc2d 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -381,9 +381,6 @@
char *kbuf; /* k-addr because vread() takes vmlist_lock rwlock */
int err = 0;
- if (!pfn_valid(PFN_DOWN(p)))
- return -EIO;
-
read = 0;
if (p < (unsigned long) high_memory) {
low_count = count;
@@ -412,6 +409,8 @@
* by the kernel or data corruption may occur
*/
kbuf = xlate_dev_kmem_ptr((void *)p);
+ if (!virt_addr_valid(kbuf))
+ return -ENXIO;
if (copy_to_user(buf, kbuf, sz))
return -EFAULT;
@@ -482,6 +481,8 @@
* corruption may occur.
*/
ptr = xlate_dev_kmem_ptr((void *)p);
+ if (!virt_addr_valid(ptr))
+ return -ENXIO;
copied = copy_from_user(ptr, buf, sz);
if (copied) {
@@ -512,9 +513,6 @@
char *kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */
int err = 0;
- if (!pfn_valid(PFN_DOWN(p)))
- return -EIO;
-
if (p < (unsigned long) high_memory) {
unsigned long to_write = min_t(unsigned long, count,
(unsigned long)high_memory - p);
diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index e3bf31b..a1ce060 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -185,7 +185,12 @@
TPM_STS_DATA_AVAIL | TPM_STS_VALID,
chip->timeout_c,
&priv->read_queue, true) == 0) {
- burstcnt = min_t(int, get_burstcount(chip), count - size);
+ burstcnt = get_burstcount(chip);
+ if (burstcnt < 0) {
+ dev_err(&chip->dev, "Unable to read burstcount\n");
+ return burstcnt;
+ }
+ burstcnt = min_t(int, burstcnt, count - size);
rc = tpm_tis_read_bytes(priv, TPM_DATA_FIFO(priv->locality),
burstcnt, buf + size);
@@ -271,7 +276,13 @@
}
while (count < len - 1) {
- burstcnt = min_t(int, get_burstcount(chip), len - count - 1);
+ burstcnt = get_burstcount(chip);
+ if (burstcnt < 0) {
+ dev_err(&chip->dev, "Unable to read burstcount\n");
+ rc = burstcnt;
+ goto out_err;
+ }
+ burstcnt = min_t(int, burstcnt, len - count - 1);
rc = tpm_tis_write_bytes(priv, TPM_DATA_FIFO(priv->locality),
burstcnt, buf + count);
if (rc < 0)
diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c
index 62028f4..a2ab008 100644
--- a/drivers/char/tpm/xen-tpmfront.c
+++ b/drivers/char/tpm/xen-tpmfront.c
@@ -307,7 +307,6 @@
rv = setup_ring(dev, priv);
if (rv) {
chip = dev_get_drvdata(&dev->dev);
- tpm_chip_unregister(chip);
ring_free(priv);
return rv;
}
diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
index 8c7763f..3bbd2a5 100644
--- a/drivers/clk/bcm/clk-bcm2835.c
+++ b/drivers/clk/bcm/clk-bcm2835.c
@@ -751,7 +751,9 @@
cprman_write(cprman, data->cm_reg,
(cprman_read(cprman, data->cm_reg) &
~data->load_mask) | data->hold_mask);
- cprman_write(cprman, data->a2w_reg, A2W_PLL_CHANNEL_DISABLE);
+ cprman_write(cprman, data->a2w_reg,
+ cprman_read(cprman, data->a2w_reg) |
+ A2W_PLL_CHANNEL_DISABLE);
spin_unlock(&cprman->regs_lock);
}
diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c
index f4fdac5..0621fbf 100644
--- a/drivers/clk/clk-wm831x.c
+++ b/drivers/clk/clk-wm831x.c
@@ -243,7 +243,7 @@
if (ret < 0) {
dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n",
ret);
- return true;
+ return false;
}
return (ret & WM831X_CLKOUT_ENA) != 0;
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index 97ae60f..bb8a77a 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -448,12 +448,20 @@
*
* con_id or dev_id may be NULL as a wildcard, just as in the rest of
* clkdev.
+ *
+ * To make things easier for mass registration, we detect error clk_hws
+ * from a previous clk_hw_register_*() call, and return the error code for
+ * those. This is to permit this function to be called immediately
+ * after clk_hw_register_*().
*/
int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id,
const char *dev_id)
{
struct clk_lookup *cl;
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
/*
* Since dev_id can be NULL, and NULL is handled specially, we must
* pass it as either a NULL format string, or with "%s".
diff --git a/drivers/clk/imx/clk-imx31.c b/drivers/clk/imx/clk-imx31.c
index 6a96414..6a49ba2 100644
--- a/drivers/clk/imx/clk-imx31.c
+++ b/drivers/clk/imx/clk-imx31.c
@@ -157,10 +157,8 @@
}
}
-int __init mx31_clocks_init(void)
+int __init mx31_clocks_init(unsigned long fref)
{
- u32 fref = 26000000; /* default */
-
_mx31_clocks_init(fref);
clk_register_clkdev(clk[gpt_gate], "per", "imx-gpt.0");
diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index 43c69ac..6ff621d 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -485,7 +485,6 @@
pll->inited = true;
}
-
static int clk_fabia_pll_enable(struct clk_hw *hw)
{
int ret;
@@ -822,7 +821,18 @@
unsigned long rate, unsigned long parent_rate)
{
struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
- int i, val = 0, div;
+ int i, val = 0, div, ret;
+
+ /*
+ * If the PLL is in FSM mode, then treat the set_rate callback
+ * as a no-operation.
+ */
+ ret = regmap_read(pll->clkr.regmap, pll->offset + PLL_MODE, &val);
+ if (ret)
+ return ret;
+
+ if (val & PLL_VOTE_FSM_ENA)
+ return 0;
if (!pll->post_div_table) {
pr_err("Missing the post_div_table for the PLL\n");
diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index acbe793..5ad98ef 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013, 2016-2017, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -23,6 +23,7 @@
u8 pre_div;
u16 m;
u16 n;
+ unsigned long src_freq;
};
/**
@@ -181,5 +182,6 @@
extern const struct clk_ops clk_byte2_ops;
extern const struct clk_ops clk_pixel_ops;
extern const struct clk_ops clk_gfx3d_ops;
+extern const struct clk_ops clk_dp_ops;
#endif
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 385cdd7..6741021 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -19,6 +19,7 @@
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/regmap.h>
+#include <linux/rational.h>
#include <linux/math64.h>
#include <linux/clk.h>
@@ -897,6 +898,92 @@
};
EXPORT_SYMBOL_GPL(clk_pixel_ops);
+static int clk_dp_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ struct freq_tbl f = { 0 };
+ unsigned long src_rate;
+ unsigned long num, den;
+ u32 mask = BIT(rcg->hid_width) - 1;
+ u32 hid_div, cfg;
+ int i, num_parents = clk_hw_get_num_parents(hw);
+
+ src_rate = clk_get_rate(clk_hw_get_parent(hw)->clk);
+ if (src_rate <= 0) {
+ pr_err("Invalid RCG parent rate\n");
+ return -EINVAL;
+ }
+
+ rational_best_approximation(src_rate, rate,
+ (unsigned long)(1 << 16) - 1,
+ (unsigned long)(1 << 16) - 1, &den, &num);
+
+ if (!num || !den) {
+ pr_err("Invalid MN values derived for requested rate %lu\n",
+ rate);
+ return -EINVAL;
+ }
+
+ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
+ hid_div = cfg;
+ cfg &= CFG_SRC_SEL_MASK;
+ cfg >>= CFG_SRC_SEL_SHIFT;
+
+ for (i = 0; i < num_parents; i++)
+ if (cfg == rcg->parent_map[i].cfg) {
+ f.src = rcg->parent_map[i].src;
+ break;
+ }
+
+ f.pre_div = hid_div;
+ f.pre_div >>= CFG_SRC_DIV_SHIFT;
+ f.pre_div &= mask;
+
+ if (num == den) {
+ f.m = 0;
+ f.n = 0;
+ } else {
+ f.m = num;
+ f.n = den;
+ }
+
+ return clk_rcg2_configure(rcg, &f);
+}
+
+static int clk_dp_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate, u8 index)
+{
+ return clk_dp_set_rate(hw, rate, parent_rate);
+}
+
+static int clk_dp_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ if (!hw)
+ return -EINVAL;
+
+ if (!clk_hw_get_parent(hw)) {
+ pr_err("Missing the parent for the DP RCG\n");
+ return -EINVAL;
+ }
+
+ req->best_parent_rate = clk_get_rate(clk_hw_get_parent(hw)->clk);
+ return 0;
+}
+
+const struct clk_ops clk_dp_ops = {
+ .is_enabled = clk_rcg2_is_enabled,
+ .get_parent = clk_rcg2_get_parent,
+ .set_parent = clk_rcg2_set_parent,
+ .recalc_rate = clk_rcg2_recalc_rate,
+ .set_rate = clk_dp_set_rate,
+ .set_rate_and_parent = clk_dp_set_rate_and_parent,
+ .determine_rate = clk_dp_determine_rate,
+ .list_registers = clk_rcg2_list_registers,
+};
+EXPORT_SYMBOL_GPL(clk_dp_ops);
+
static int clk_gfx3d_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c
index 52a7d39..28eb200 100644
--- a/drivers/clk/qcom/gcc-ipq806x.c
+++ b/drivers/clk/qcom/gcc-ipq806x.c
@@ -2990,11 +2990,11 @@
struct regmap *regmap;
int ret;
- ret = qcom_cc_register_board_clk(dev, "cxo_board", "cxo", 19200000);
+ ret = qcom_cc_register_board_clk(dev, "cxo_board", "cxo", 25000000);
if (ret)
return ret;
- ret = qcom_cc_register_board_clk(dev, "pxo_board", "pxo", 27000000);
+ ret = qcom_cc_register_board_clk(dev, "pxo_board", "pxo", 25000000);
if (ret)
return ret;
diff --git a/drivers/clk/renesas/clk-mstp.c b/drivers/clk/renesas/clk-mstp.c
index 9375777..b533f99 100644
--- a/drivers/clk/renesas/clk-mstp.c
+++ b/drivers/clk/renesas/clk-mstp.c
@@ -37,12 +37,14 @@
* @smstpcr: module stop control register
* @mstpsr: module stop status register (optional)
* @lock: protects writes to SMSTPCR
+ * @width_8bit: registers are 8-bit, not 32-bit
*/
struct mstp_clock_group {
struct clk_onecell_data data;
void __iomem *smstpcr;
void __iomem *mstpsr;
spinlock_t lock;
+ bool width_8bit;
};
/**
@@ -59,6 +61,18 @@
#define to_mstp_clock(_hw) container_of(_hw, struct mstp_clock, hw)
+static inline u32 cpg_mstp_read(struct mstp_clock_group *group,
+ u32 __iomem *reg)
+{
+ return group->width_8bit ? readb(reg) : clk_readl(reg);
+}
+
+static inline void cpg_mstp_write(struct mstp_clock_group *group, u32 val,
+ u32 __iomem *reg)
+{
+ group->width_8bit ? writeb(val, reg) : clk_writel(val, reg);
+}
+
static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable)
{
struct mstp_clock *clock = to_mstp_clock(hw);
@@ -70,12 +84,12 @@
spin_lock_irqsave(&group->lock, flags);
- value = clk_readl(group->smstpcr);
+ value = cpg_mstp_read(group, group->smstpcr);
if (enable)
value &= ~bitmask;
else
value |= bitmask;
- clk_writel(value, group->smstpcr);
+ cpg_mstp_write(group, value, group->smstpcr);
spin_unlock_irqrestore(&group->lock, flags);
@@ -83,7 +97,7 @@
return 0;
for (i = 1000; i > 0; --i) {
- if (!(clk_readl(group->mstpsr) & bitmask))
+ if (!(cpg_mstp_read(group, group->mstpsr) & bitmask))
break;
cpu_relax();
}
@@ -114,9 +128,9 @@
u32 value;
if (group->mstpsr)
- value = clk_readl(group->mstpsr);
+ value = cpg_mstp_read(group, group->mstpsr);
else
- value = clk_readl(group->smstpcr);
+ value = cpg_mstp_read(group, group->smstpcr);
return !(value & BIT(clock->bit_index));
}
@@ -188,6 +202,9 @@
return;
}
+ if (of_device_is_compatible(np, "renesas,r7s72100-mstp-clocks"))
+ group->width_8bit = true;
+
for (i = 0; i < MSTP_MAX_CLOCKS; ++i)
clks[i] = ERR_PTR(-ENOENT);
diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c
index e1365e7..25c41cd 100644
--- a/drivers/clk/renesas/renesas-cpg-mssr.c
+++ b/drivers/clk/renesas/renesas-cpg-mssr.c
@@ -33,9 +33,9 @@
#include "clk-div6.h"
#ifdef DEBUG
-#define WARN_DEBUG(x) do { } while (0)
-#else
#define WARN_DEBUG(x) WARN_ON(x)
+#else
+#define WARN_DEBUG(x) do { } while (0)
#endif
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c
index 2646d98..5c6d37b 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c
@@ -344,10 +344,10 @@
static const char * const i2s_parents[] = { "pll-audio-8x", "pll-audio-4x",
"pll-audio-2x", "pll-audio" };
static SUNXI_CCU_MUX_WITH_GATE(i2s0_clk, "i2s0", i2s_parents,
- 0x0b0, 16, 2, BIT(31), 0);
+ 0x0b0, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
static SUNXI_CCU_MUX_WITH_GATE(i2s1_clk, "i2s1", i2s_parents,
- 0x0b4, 16, 2, BIT(31), 0);
+ 0x0b4, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
/* TODO: the parent for most of the USB clocks is not known */
static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M",
@@ -415,7 +415,7 @@
0x13c, 16, 3, BIT(31), CLK_SET_RATE_PARENT);
static SUNXI_CCU_GATE(ac_dig_clk, "ac-dig", "pll-audio",
- 0x140, BIT(31), 0);
+ 0x140, BIT(31), CLK_SET_RATE_PARENT);
static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M",
0x144, BIT(31), 0);
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
index 4d70590..21c427d 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
@@ -394,16 +394,16 @@
static const char * const i2s_parents[] = { "pll-audio-8x", "pll-audio-4x",
"pll-audio-2x", "pll-audio" };
static SUNXI_CCU_MUX_WITH_GATE(i2s0_clk, "i2s0", i2s_parents,
- 0x0b0, 16, 2, BIT(31), 0);
+ 0x0b0, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
static SUNXI_CCU_MUX_WITH_GATE(i2s1_clk, "i2s1", i2s_parents,
- 0x0b4, 16, 2, BIT(31), 0);
+ 0x0b4, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
static SUNXI_CCU_MUX_WITH_GATE(i2s2_clk, "i2s2", i2s_parents,
- 0x0b8, 16, 2, BIT(31), 0);
+ 0x0b8, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
static SUNXI_CCU_M_WITH_GATE(spdif_clk, "spdif", "pll-audio",
- 0x0c0, 0, 4, BIT(31), 0);
+ 0x0c0, 0, 4, BIT(31), CLK_SET_RATE_PARENT);
static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M",
0x0cc, BIT(8), 0);
@@ -466,7 +466,7 @@
0x13c, 16, 3, BIT(31), 0);
static SUNXI_CCU_GATE(ac_dig_clk, "ac-dig", "pll-audio",
- 0x140, BIT(31), 0);
+ 0x140, BIT(31), CLK_SET_RATE_PARENT);
static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M",
0x144, BIT(31), 0);
diff --git a/drivers/clk/ti/clk-3xxx.c b/drivers/clk/ti/clk-3xxx.c
index 8831e1a..11d8aa3 100644
--- a/drivers/clk/ti/clk-3xxx.c
+++ b/drivers/clk/ti/clk-3xxx.c
@@ -22,13 +22,6 @@
#include "clock.h"
-/*
- * DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks
- * that are sourced by DPLL5, and both of these require this clock
- * to be at 120 MHz for proper operation.
- */
-#define DPLL5_FREQ_FOR_USBHOST 120000000
-
#define OMAP3430ES2_ST_DSS_IDLE_SHIFT 1
#define OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT 5
#define OMAP3430ES2_ST_SSI_IDLE_SHIFT 8
@@ -546,14 +539,21 @@
struct clk *dpll5_clk;
struct clk *dpll5_m2_clk;
+ /*
+ * Errata sprz319f advisory 2.1 documents a USB host clock drift issue
+ * that can be worked around using specially crafted dpll5 settings
+ * with a dpll5_m2 divider set to 8. Set the dpll5 rate to 8x the USB
+ * host clock rate, its .set_rate handler() will detect that frequency
+ * and use the errata settings.
+ */
dpll5_clk = clk_get(NULL, "dpll5_ck");
- clk_set_rate(dpll5_clk, DPLL5_FREQ_FOR_USBHOST);
+ clk_set_rate(dpll5_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST * 8);
clk_prepare_enable(dpll5_clk);
- /* Program dpll5_m2_clk divider for no division */
+ /* Program dpll5_m2_clk divider */
dpll5_m2_clk = clk_get(NULL, "dpll5_m2_ck");
clk_prepare_enable(dpll5_m2_clk);
- clk_set_rate(dpll5_m2_clk, DPLL5_FREQ_FOR_USBHOST);
+ clk_set_rate(dpll5_m2_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST);
clk_disable_unprepare(dpll5_m2_clk);
clk_disable_unprepare(dpll5_clk);
diff --git a/drivers/clk/ti/clk-7xx.c b/drivers/clk/ti/clk-7xx.c
index bfa17d3..9fd6043 100644
--- a/drivers/clk/ti/clk-7xx.c
+++ b/drivers/clk/ti/clk-7xx.c
@@ -201,7 +201,6 @@
DT_CLK(NULL, "atl_dpll_clk_mux", "atl_dpll_clk_mux"),
DT_CLK(NULL, "atl_gfclk_mux", "atl_gfclk_mux"),
DT_CLK(NULL, "dcan1_sys_clk_mux", "dcan1_sys_clk_mux"),
- DT_CLK(NULL, "gmac_gmii_ref_clk_div", "gmac_gmii_ref_clk_div"),
DT_CLK(NULL, "gmac_rft_clk_mux", "gmac_rft_clk_mux"),
DT_CLK(NULL, "gpu_core_gclk_mux", "gpu_core_gclk_mux"),
DT_CLK(NULL, "gpu_hyd_gclk_mux", "gpu_hyd_gclk_mux"),
diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h
index 90f3f47..13c37f4 100644
--- a/drivers/clk/ti/clock.h
+++ b/drivers/clk/ti/clock.h
@@ -257,11 +257,20 @@
unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw,
unsigned long parent_rate);
+/*
+ * OMAP3_DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks
+ * that are sourced by DPLL5, and both of these require this clock
+ * to be at 120 MHz for proper operation.
+ */
+#define OMAP3_DPLL5_FREQ_FOR_USBHOST 120000000
+
unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate);
int omap3_dpll4_set_rate(struct clk_hw *clk, unsigned long rate,
unsigned long parent_rate);
int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate, u8 index);
+int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate);
void omap3_clk_lock_dpll5(void);
unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,
diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c
index 9fc8754..4b9a419 100644
--- a/drivers/clk/ti/dpll.c
+++ b/drivers/clk/ti/dpll.c
@@ -114,6 +114,18 @@
.round_rate = &omap2_dpll_round_rate,
};
+static const struct clk_ops omap3_dpll5_ck_ops = {
+ .enable = &omap3_noncore_dpll_enable,
+ .disable = &omap3_noncore_dpll_disable,
+ .get_parent = &omap2_init_dpll_parent,
+ .recalc_rate = &omap3_dpll_recalc,
+ .set_rate = &omap3_dpll5_set_rate,
+ .set_parent = &omap3_noncore_dpll_set_parent,
+ .set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent,
+ .determine_rate = &omap3_noncore_dpll_determine_rate,
+ .round_rate = &omap2_dpll_round_rate,
+};
+
static const struct clk_ops omap3_dpll_per_ck_ops = {
.enable = &omap3_noncore_dpll_enable,
.disable = &omap3_noncore_dpll_disable,
@@ -474,7 +486,12 @@
.modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
};
- of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd);
+ if ((of_machine_is_compatible("ti,omap3630") ||
+ of_machine_is_compatible("ti,omap36xx")) &&
+ !strcmp(node->name, "dpll5_ck"))
+ of_ti_dpll_setup(node, &omap3_dpll5_ck_ops, &dd);
+ else
+ of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd);
}
CLK_OF_DECLARE(ti_omap3_dpll_clock, "ti,omap3-dpll-clock",
of_ti_omap3_dpll_setup);
diff --git a/drivers/clk/ti/dpll3xxx.c b/drivers/clk/ti/dpll3xxx.c
index 88f2ce8..4cdd28a 100644
--- a/drivers/clk/ti/dpll3xxx.c
+++ b/drivers/clk/ti/dpll3xxx.c
@@ -838,3 +838,70 @@
return omap3_noncore_dpll_set_rate_and_parent(hw, rate, parent_rate,
index);
}
+
+/* Apply DM3730 errata sprz319 advisory 2.1. */
+static bool omap3_dpll5_apply_errata(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct omap3_dpll5_settings {
+ unsigned int rate, m, n;
+ };
+
+ static const struct omap3_dpll5_settings precomputed[] = {
+ /*
+ * From DM3730 errata advisory 2.1, table 35 and 36.
+ * The N value is increased by 1 compared to the tables as the
+ * errata lists register values while last_rounded_field is the
+ * real divider value.
+ */
+ { 12000000, 80, 0 + 1 },
+ { 13000000, 443, 5 + 1 },
+ { 19200000, 50, 0 + 1 },
+ { 26000000, 443, 11 + 1 },
+ { 38400000, 25, 0 + 1 }
+ };
+
+ const struct omap3_dpll5_settings *d;
+ struct clk_hw_omap *clk = to_clk_hw_omap(hw);
+ struct dpll_data *dd;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(precomputed); ++i) {
+ if (parent_rate == precomputed[i].rate)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(precomputed))
+ return false;
+
+ d = &precomputed[i];
+
+ /* Update the M, N and rounded rate values and program the DPLL. */
+ dd = clk->dpll_data;
+ dd->last_rounded_m = d->m;
+ dd->last_rounded_n = d->n;
+ dd->last_rounded_rate = div_u64((u64)parent_rate * d->m, d->n);
+ omap3_noncore_dpll_program(clk, 0);
+
+ return true;
+}
+
+/**
+ * omap3_dpll5_set_rate - set rate for omap3 dpll5
+ * @hw: clock to change
+ * @rate: target rate for clock
+ * @parent_rate: rate of the parent clock
+ *
+ * Set rate for the DPLL5 clock. Apply the sprz319 advisory 2.1 on OMAP36xx if
+ * the DPLL is used for USB host (detected through the requested rate).
+ */
+int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ if (rate == OMAP3_DPLL5_FREQ_FOR_USBHOST * 8) {
+ if (omap3_dpll5_apply_errata(hw, parent_rate))
+ return 0;
+ }
+
+ return omap3_noncore_dpll_set_rate(hw, rate, parent_rate);
+}
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
index 8f3488b..7f6fed9 100644
--- a/drivers/clocksource/exynos_mct.c
+++ b/drivers/clocksource/exynos_mct.c
@@ -495,6 +495,7 @@
if (mct_int_type == MCT_INT_SPI) {
if (evt->irq != -1)
disable_irq_nosync(evt->irq);
+ exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
} else {
disable_percpu_irq(mct_irqs[MCT_L0_IRQ]);
}
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index 61be70e..0173b8b 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -102,15 +102,23 @@
governor. If unsure have a look at the help section of the
driver. Fallback governor will be the performance governor.
-config CPU_FREQ_DEFAULT_GOV_SCHEDUTIL
- bool "schedutil"
- depends on SMP
- select CPU_FREQ_GOV_SCHEDUTIL
+config CPU_FREQ_DEFAULT_GOV_SCHED
+ bool "sched"
+ select CPU_FREQ_GOV_SCHED
+ help
+ Use the CPUfreq governor 'sched' as default. This scales
+ cpu frequency using CPU utilization estimates from the
+ scheduler.
+
+config CPU_FREQ_DEFAULT_GOV_INTERACTIVE
+ bool "interactive"
+ select CPU_FREQ_GOV_INTERACTIVE
select CPU_FREQ_GOV_PERFORMANCE
help
- Use the 'schedutil' CPUFreq governor by default. If unsure,
- have a look at the help section of that governor. The fallback
- governor will be 'performance'.
+ Use the CPUFreq governor 'interactive' as default. This allows
+ you to get a full dynamic cpu frequency capable system by simply
+ loading your cpufreq low-level hardware driver, using the
+ 'interactive' governor for latency-sensitive workloads.
config CPU_FREQ_DEFAULT_GOV_INTERACTIVE
bool "interactive"
@@ -178,20 +186,6 @@
If in doubt, say N.
-config CPU_FREQ_GOV_INTERACTIVE
- bool "'interactive' cpufreq policy governor"
- help
- 'interactive' - This driver adds a dynamic cpufreq policy governor
- designed for latency-sensitive workloads.
-
- This governor attempts to reduce the latency of clock
- increases so that the system is more responsive to
- interactive workloads.
-
- For details, take a look at linux/Documentation/cpu-freq.
-
- If in doubt, say N.
-
config CPU_FREQ_GOV_CONSERVATIVE
tristate "'conservative' cpufreq governor"
depends on CPU_FREQ
@@ -216,20 +210,36 @@
If in doubt, say N.
-config CPU_FREQ_GOV_SCHEDUTIL
- bool "'schedutil' cpufreq policy governor"
- depends on CPU_FREQ && SMP
+config CPU_FREQ_GOV_SCHED
+ bool "'sched' cpufreq governor"
+ depends on CPU_FREQ
+ depends on SMP
+ select CPU_FREQ_GOV_COMMON
+ help
+ 'sched' - this governor scales cpu frequency from the
+ scheduler as a function of cpu capacity utilization. It does
+ not evaluate utilization on a periodic basis (as ondemand
+ does) but instead is event-driven by the scheduler.
+
+ If in doubt, say N.
+
+config CPU_FREQ_GOV_INTERACTIVE
+ tristate "'interactive' cpufreq policy governor"
+ depends on CPU_FREQ
select CPU_FREQ_GOV_ATTR_SET
select IRQ_WORK
help
- This governor makes decisions based on the utilization data provided
- by the scheduler. It sets the CPU frequency to be proportional to
- the utilization/capacity ratio coming from the scheduler. If the
- utilization is frequency-invariant, the new frequency is also
- proportional to the maximum available frequency. If that is not the
- case, it is proportional to the current frequency of the CPU. The
- frequency tipping point is at utilization/capacity equal to 80% in
- both cases.
+ 'interactive' - This driver adds a dynamic cpufreq policy governor
+ designed for latency-sensitive workloads.
+
+ This governor attempts to reduce the latency of clock
+ increases so that the system is more responsive to
+ interactive workloads.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cpufreq_interactive.
+
+ For details, take a look at linux/Documentation/cpu-freq.
If in doubt, say N.
diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c
index 5c07ae0..4d3ec92 100644
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -28,6 +28,7 @@
#include "cpufreq-dt.h"
struct private_data {
+ struct opp_table *opp_table;
struct device *cpu_dev;
struct thermal_cooling_device *cdev;
const char *reg_name;
@@ -143,6 +144,7 @@
static int cpufreq_init(struct cpufreq_policy *policy)
{
struct cpufreq_frequency_table *freq_table;
+ struct opp_table *opp_table = NULL;
struct private_data *priv;
struct device *cpu_dev;
struct clk *cpu_clk;
@@ -186,8 +188,9 @@
*/
name = find_supply_name(cpu_dev);
if (name) {
- ret = dev_pm_opp_set_regulator(cpu_dev, name);
- if (ret) {
+ opp_table = dev_pm_opp_set_regulator(cpu_dev, name);
+ if (IS_ERR(opp_table)) {
+ ret = PTR_ERR(opp_table);
dev_err(cpu_dev, "Failed to set regulator for cpu%d: %d\n",
policy->cpu, ret);
goto out_put_clk;
@@ -237,6 +240,7 @@
}
priv->reg_name = name;
+ priv->opp_table = opp_table;
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
if (ret) {
@@ -285,7 +289,7 @@
out_free_opp:
dev_pm_opp_of_cpumask_remove_table(policy->cpus);
if (name)
- dev_pm_opp_put_regulator(cpu_dev);
+ dev_pm_opp_put_regulator(opp_table);
out_put_clk:
clk_put(cpu_clk);
@@ -300,7 +304,7 @@
dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
if (priv->reg_name)
- dev_pm_opp_put_regulator(priv->cpu_dev);
+ dev_pm_opp_put_regulator(priv->opp_table);
clk_put(policy->clk);
kfree(priv);
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index c910111..019e817 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -29,6 +29,9 @@
#include <linux/suspend.h>
#include <linux/syscore_ops.h>
#include <linux/tick.h>
+#ifdef CONFIG_SMP
+#include <linux/sched.h>
+#endif
#include <trace/events/power.h>
static LIST_HEAD(cpufreq_policy_list);
@@ -117,6 +120,12 @@
}
EXPORT_SYMBOL_GPL(have_governor_per_policy);
+bool cpufreq_driver_is_slow(void)
+{
+ return !(cpufreq_driver->flags & CPUFREQ_DRIVER_FAST);
+}
+EXPORT_SYMBOL_GPL(cpufreq_driver_is_slow);
+
struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy)
{
if (have_governor_per_policy())
@@ -301,6 +310,50 @@
#endif
}
+/*********************************************************************
+ * FREQUENCY INVARIANT CPU CAPACITY *
+ *********************************************************************/
+
+static DEFINE_PER_CPU(unsigned long, freq_scale) = SCHED_CAPACITY_SCALE;
+static DEFINE_PER_CPU(unsigned long, max_freq_scale) = SCHED_CAPACITY_SCALE;
+
+static void
+scale_freq_capacity(struct cpufreq_policy *policy, struct cpufreq_freqs *freqs)
+{
+ unsigned long cur = freqs ? freqs->new : policy->cur;
+ unsigned long scale = (cur << SCHED_CAPACITY_SHIFT) / policy->max;
+ struct cpufreq_cpuinfo *cpuinfo = &policy->cpuinfo;
+ int cpu;
+
+ pr_debug("cpus %*pbl cur/cur max freq %lu/%u kHz freq scale %lu\n",
+ cpumask_pr_args(policy->cpus), cur, policy->max, scale);
+
+ for_each_cpu(cpu, policy->cpus)
+ per_cpu(freq_scale, cpu) = scale;
+
+ if (freqs)
+ return;
+
+ scale = (policy->max << SCHED_CAPACITY_SHIFT) / cpuinfo->max_freq;
+
+ pr_debug("cpus %*pbl cur max/max freq %u/%u kHz max freq scale %lu\n",
+ cpumask_pr_args(policy->cpus), policy->max, cpuinfo->max_freq,
+ scale);
+
+ for_each_cpu(cpu, policy->cpus)
+ per_cpu(max_freq_scale, cpu) = scale;
+}
+
+unsigned long cpufreq_scale_freq_capacity(struct sched_domain *sd, int cpu)
+{
+ return per_cpu(freq_scale, cpu);
+}
+
+unsigned long cpufreq_scale_max_freq_capacity(int cpu)
+{
+ return per_cpu(max_freq_scale, cpu);
+}
+
static void __cpufreq_notify_transition(struct cpufreq_policy *policy,
struct cpufreq_freqs *freqs, unsigned int state)
{
@@ -378,6 +431,9 @@
void cpufreq_freq_transition_begin(struct cpufreq_policy *policy,
struct cpufreq_freqs *freqs)
{
+#ifdef CONFIG_SMP
+ int cpu;
+#endif
/*
* Catch double invocations of _begin() which lead to self-deadlock.
@@ -405,6 +461,12 @@
spin_unlock(&policy->transition_lock);
+ scale_freq_capacity(policy, freqs);
+#ifdef CONFIG_SMP
+ for_each_cpu(cpu, policy->cpus)
+ trace_cpu_capacity(capacity_curr_of(cpu), cpu);
+#endif
+
cpufreq_notify_transition(policy, freqs, CPUFREQ_PRECHANGE);
}
EXPORT_SYMBOL_GPL(cpufreq_freq_transition_begin);
@@ -2192,6 +2254,8 @@
blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
CPUFREQ_NOTIFY, new_policy);
+ scale_freq_capacity(new_policy, NULL);
+
policy->min = new_policy->min;
policy->max = new_policy->max;
trace_cpu_frequency_limits(policy->max, policy->min, policy->cpu);
diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c
index 1347589..e8e16a5 100644
--- a/drivers/cpufreq/cpufreq_conservative.c
+++ b/drivers/cpufreq/cpufreq_conservative.c
@@ -302,7 +302,10 @@
dbs_info->requested_freq = policy->cur;
}
-static struct dbs_governor cs_governor = {
+#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE
+static
+#endif
+struct dbs_governor cs_governor = {
.gov = CPUFREQ_DBS_GOVERNOR_INITIALIZER("conservative"),
.kobj_type = { .default_attrs = cs_attributes },
.gov_dbs_timer = cs_dbs_timer,
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index f3266a3..d6cac0e 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -1,7 +1,7 @@
/*
* drivers/cpufreq/cpufreq_interactive.c
*
- * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010-2016 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -13,12 +13,14 @@
* GNU General Public License for more details.
*
* Author: Mike Chan (mike@android.com)
- *
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/cpu.h>
#include <linux/cpumask.h>
#include <linux/cpufreq.h>
+#include <linux/irq_work.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/rwsem.h>
@@ -27,72 +29,50 @@
#include <linux/tick.h>
#include <linux/time.h>
#include <linux/timer.h>
-#include <linux/workqueue.h>
#include <linux/kthread.h>
#include <linux/slab.h>
#define CREATE_TRACE_POINTS
#include <trace/events/cpufreq_interactive.h>
-struct cpufreq_interactive_cpuinfo {
- struct timer_list cpu_timer;
- struct timer_list cpu_slack_timer;
- spinlock_t load_lock; /* protects the next 4 fields */
- u64 time_in_idle;
- u64 time_in_idle_timestamp;
- u64 cputime_speedadj;
- u64 cputime_speedadj_timestamp;
- struct cpufreq_policy *policy;
- struct cpufreq_frequency_table *freq_table;
- spinlock_t target_freq_lock; /*protects target freq */
- unsigned int target_freq;
- unsigned int floor_freq;
- u64 pol_floor_val_time; /* policy floor_validate_time */
- u64 loc_floor_val_time; /* per-cpu floor_validate_time */
- u64 pol_hispeed_val_time; /* policy hispeed_validate_time */
- u64 loc_hispeed_val_time; /* per-cpu hispeed_validate_time */
- struct rw_semaphore enable_sem;
- int governor_enabled;
-};
+#define gov_attr_ro(_name) \
+static struct governor_attr _name = \
+__ATTR(_name, 0444, show_##_name, NULL)
-static DEFINE_PER_CPU(struct cpufreq_interactive_cpuinfo, cpuinfo);
+#define gov_attr_wo(_name) \
+static struct governor_attr _name = \
+__ATTR(_name, 0200, NULL, store_##_name)
-/* realtime thread handles frequency scaling */
-static struct task_struct *speedchange_task;
-static cpumask_t speedchange_cpumask;
-static spinlock_t speedchange_cpumask_lock;
-static struct mutex gov_lock;
+#define gov_attr_rw(_name) \
+static struct governor_attr _name = \
+__ATTR(_name, 0644, show_##_name, store_##_name)
-/* Target load. Lower values result in higher CPU speeds. */
-#define DEFAULT_TARGET_LOAD 90
-static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD};
+/* Separate instance required for each 'interactive' directory in sysfs */
+struct interactive_tunables {
+ struct gov_attr_set attr_set;
-#define DEFAULT_TIMER_RATE (20 * USEC_PER_MSEC)
-#define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE
-static unsigned int default_above_hispeed_delay[] = {
- DEFAULT_ABOVE_HISPEED_DELAY };
-
-struct cpufreq_interactive_tunables {
- int usage_count;
/* Hi speed to bump to from lo speed when load burst (default max) */
unsigned int hispeed_freq;
+
/* Go to hi speed when CPU load at or above this value. */
#define DEFAULT_GO_HISPEED_LOAD 99
unsigned long go_hispeed_load;
+
/* Target load. Lower values result in higher CPU speeds. */
spinlock_t target_loads_lock;
unsigned int *target_loads;
int ntarget_loads;
+
/*
* The minimum amount of time to spend at a frequency before we can ramp
* down.
*/
#define DEFAULT_MIN_SAMPLE_TIME (80 * USEC_PER_MSEC)
unsigned long min_sample_time;
- /*
- * The sample rate of the timer used to increase frequency
- */
- unsigned long timer_rate;
+
+ /* The sample rate of the timer used to increase frequency */
+ unsigned long sampling_rate;
+
/*
* Wait this long before raising speed above hispeed, by default a
* single timer interval.
@@ -100,114 +80,175 @@
spinlock_t above_hispeed_delay_lock;
unsigned int *above_hispeed_delay;
int nabove_hispeed_delay;
+
/* Non-zero means indefinite speed boost active */
- int boost_val;
+ int boost;
/* Duration of a boot pulse in usecs */
- int boostpulse_duration_val;
+ int boostpulse_duration;
/* End time of boost pulse in ktime converted to usecs */
u64 boostpulse_endtime;
bool boosted;
+
/*
- * Max additional time to wait in idle, beyond timer_rate, at speeds
+ * Max additional time to wait in idle, beyond sampling_rate, at speeds
* above minimum before wakeup to reduce speed, or -1 if unnecessary.
*/
-#define DEFAULT_TIMER_SLACK (4 * DEFAULT_TIMER_RATE)
- int timer_slack_val;
+#define DEFAULT_TIMER_SLACK (4 * DEFAULT_SAMPLING_RATE)
+ unsigned long timer_slack_delay;
+ unsigned long timer_slack;
bool io_is_busy;
};
-/* For cases where we have single governor instance for system */
-static struct cpufreq_interactive_tunables *common_tunables;
+/* Separate instance required for each 'struct cpufreq_policy' */
+struct interactive_policy {
+ struct cpufreq_policy *policy;
+ struct interactive_tunables *tunables;
+ struct list_head tunables_hook;
+};
-static struct attribute_group *get_sysfs_attr(void);
+/* Separate instance required for each CPU */
+struct interactive_cpu {
+ struct update_util_data update_util;
+ struct interactive_policy *ipolicy;
-static void cpufreq_interactive_timer_resched(
- struct cpufreq_interactive_cpuinfo *pcpu)
+ struct irq_work irq_work;
+ u64 last_sample_time;
+ unsigned long next_sample_jiffies;
+ bool work_in_progress;
+
+ struct rw_semaphore enable_sem;
+ struct timer_list slack_timer;
+
+ spinlock_t load_lock; /* protects the next 4 fields */
+ u64 time_in_idle;
+ u64 time_in_idle_timestamp;
+ u64 cputime_speedadj;
+ u64 cputime_speedadj_timestamp;
+
+ spinlock_t target_freq_lock; /*protects target freq */
+ unsigned int target_freq;
+
+ unsigned int floor_freq;
+ u64 pol_floor_val_time; /* policy floor_validate_time */
+ u64 loc_floor_val_time; /* per-cpu floor_validate_time */
+ u64 pol_hispeed_val_time; /* policy hispeed_validate_time */
+ u64 loc_hispeed_val_time; /* per-cpu hispeed_validate_time */
+};
+
+static DEFINE_PER_CPU(struct interactive_cpu, interactive_cpu);
+
+/* Realtime thread handles frequency scaling */
+static struct task_struct *speedchange_task;
+static cpumask_t speedchange_cpumask;
+static spinlock_t speedchange_cpumask_lock;
+
+/* Target load. Lower values result in higher CPU speeds. */
+#define DEFAULT_TARGET_LOAD 90
+static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD};
+
+#define DEFAULT_SAMPLING_RATE (20 * USEC_PER_MSEC)
+#define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_SAMPLING_RATE
+static unsigned int default_above_hispeed_delay[] = {
+ DEFAULT_ABOVE_HISPEED_DELAY
+};
+
+/* Iterate over interactive policies for tunables */
+#define for_each_ipolicy(__ip) \
+ list_for_each_entry(__ip, &tunables->attr_set.policy_list, tunables_hook)
+
+static struct interactive_tunables *global_tunables;
+static DEFINE_MUTEX(global_tunables_lock);
+
+static inline void update_slack_delay(struct interactive_tunables *tunables)
{
- struct cpufreq_interactive_tunables *tunables =
- pcpu->policy->governor_data;
- unsigned long expires;
- unsigned long flags;
-
- spin_lock_irqsave(&pcpu->load_lock, flags);
- pcpu->time_in_idle =
- get_cpu_idle_time(smp_processor_id(),
- &pcpu->time_in_idle_timestamp,
- tunables->io_is_busy);
- pcpu->cputime_speedadj = 0;
- pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp;
- expires = jiffies + usecs_to_jiffies(tunables->timer_rate);
- mod_timer(&pcpu->cpu_timer, expires);
-
- if (tunables->timer_slack_val >= 0 &&
- pcpu->target_freq > pcpu->policy->min) {
- expires += usecs_to_jiffies(tunables->timer_slack_val);
- mod_timer(&pcpu->cpu_slack_timer, expires);
- }
-
- spin_unlock_irqrestore(&pcpu->load_lock, flags);
+ tunables->timer_slack_delay = usecs_to_jiffies(tunables->timer_slack +
+ tunables->sampling_rate);
}
-/* The caller shall take enable_sem write semaphore to avoid any timer race.
- * The cpu_timer and cpu_slack_timer must be deactivated when calling this
- * function.
- */
-static void cpufreq_interactive_timer_start(
- struct cpufreq_interactive_tunables *tunables, int cpu)
+static bool timer_slack_required(struct interactive_cpu *icpu)
{
- struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu);
- unsigned long expires = jiffies +
- usecs_to_jiffies(tunables->timer_rate);
- unsigned long flags;
+ struct interactive_policy *ipolicy = icpu->ipolicy;
+ struct interactive_tunables *tunables = ipolicy->tunables;
- pcpu->cpu_timer.expires = expires;
- add_timer_on(&pcpu->cpu_timer, cpu);
- if (tunables->timer_slack_val >= 0 &&
- pcpu->target_freq > pcpu->policy->min) {
- expires += usecs_to_jiffies(tunables->timer_slack_val);
- pcpu->cpu_slack_timer.expires = expires;
- add_timer_on(&pcpu->cpu_slack_timer, cpu);
- }
+ if (tunables->timer_slack < 0)
+ return false;
- spin_lock_irqsave(&pcpu->load_lock, flags);
- pcpu->time_in_idle =
- get_cpu_idle_time(cpu, &pcpu->time_in_idle_timestamp,
- tunables->io_is_busy);
- pcpu->cputime_speedadj = 0;
- pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp;
- spin_unlock_irqrestore(&pcpu->load_lock, flags);
+ if (icpu->target_freq > ipolicy->policy->min)
+ return true;
+
+ return false;
}
-static unsigned int freq_to_above_hispeed_delay(
- struct cpufreq_interactive_tunables *tunables,
- unsigned int freq)
+static void gov_slack_timer_start(struct interactive_cpu *icpu, int cpu)
{
- int i;
+ struct interactive_tunables *tunables = icpu->ipolicy->tunables;
+
+ icpu->slack_timer.expires = jiffies + tunables->timer_slack_delay;
+ add_timer_on(&icpu->slack_timer, cpu);
+}
+
+static void gov_slack_timer_modify(struct interactive_cpu *icpu)
+{
+ struct interactive_tunables *tunables = icpu->ipolicy->tunables;
+
+ mod_timer(&icpu->slack_timer, jiffies + tunables->timer_slack_delay);
+}
+
+static void slack_timer_resched(struct interactive_cpu *icpu, int cpu,
+ bool modify)
+{
+ struct interactive_tunables *tunables = icpu->ipolicy->tunables;
+ unsigned long flags;
+
+ spin_lock_irqsave(&icpu->load_lock, flags);
+
+ icpu->time_in_idle = get_cpu_idle_time(cpu,
+ &icpu->time_in_idle_timestamp,
+ tunables->io_is_busy);
+ icpu->cputime_speedadj = 0;
+ icpu->cputime_speedadj_timestamp = icpu->time_in_idle_timestamp;
+
+ if (timer_slack_required(icpu)) {
+ if (modify)
+ gov_slack_timer_modify(icpu);
+ else
+ gov_slack_timer_start(icpu, cpu);
+ }
+
+ spin_unlock_irqrestore(&icpu->load_lock, flags);
+}
+
+static unsigned int
+freq_to_above_hispeed_delay(struct interactive_tunables *tunables,
+ unsigned int freq)
+{
+ unsigned long flags;
unsigned int ret;
- unsigned long flags;
+ int i;
spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags);
for (i = 0; i < tunables->nabove_hispeed_delay - 1 &&
- freq >= tunables->above_hispeed_delay[i+1]; i += 2)
+ freq >= tunables->above_hispeed_delay[i + 1]; i += 2)
;
ret = tunables->above_hispeed_delay[i];
spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags);
+
return ret;
}
-static unsigned int freq_to_targetload(
- struct cpufreq_interactive_tunables *tunables, unsigned int freq)
+static unsigned int freq_to_targetload(struct interactive_tunables *tunables,
+ unsigned int freq)
{
- int i;
- unsigned int ret;
unsigned long flags;
+ unsigned int ret;
+ int i;
spin_lock_irqsave(&tunables->target_loads_lock, flags);
for (i = 0; i < tunables->ntarget_loads - 1 &&
- freq >= tunables->target_loads[i+1]; i += 2)
+ freq >= tunables->target_loads[i + 1]; i += 2)
;
ret = tunables->target_loads[i];
@@ -220,78 +261,71 @@
* choose_freq() will find the minimum frequency that does not exceed its
* target load given the current load.
*/
-static unsigned int choose_freq(struct cpufreq_interactive_cpuinfo *pcpu,
- unsigned int loadadjfreq)
+static unsigned int choose_freq(struct interactive_cpu *icpu,
+ unsigned int loadadjfreq)
{
- unsigned int freq = pcpu->policy->cur;
- unsigned int prevfreq, freqmin, freqmax;
- unsigned int tl;
+ struct cpufreq_policy *policy = icpu->ipolicy->policy;
+ struct cpufreq_frequency_table *freq_table = policy->freq_table;
+ unsigned int prevfreq, freqmin = 0, freqmax = UINT_MAX, tl;
+ unsigned int freq = policy->cur;
int index;
- freqmin = 0;
- freqmax = UINT_MAX;
-
do {
prevfreq = freq;
- tl = freq_to_targetload(pcpu->policy->governor_data, freq);
+ tl = freq_to_targetload(icpu->ipolicy->tunables, freq);
/*
* Find the lowest frequency where the computed load is less
* than or equal to the target load.
*/
- index = cpufreq_frequency_table_target(
- pcpu->policy, loadadjfreq / tl,
- CPUFREQ_RELATION_L);
- freq = pcpu->freq_table[index].frequency;
+ index = cpufreq_frequency_table_target(policy, loadadjfreq / tl,
+ CPUFREQ_RELATION_L);
+
+ freq = freq_table[index].frequency;
if (freq > prevfreq) {
- /* The previous frequency is too low. */
+ /* The previous frequency is too low */
freqmin = prevfreq;
- if (freq >= freqmax) {
- /*
- * Find the highest frequency that is less
- * than freqmax.
- */
- index = cpufreq_frequency_table_target(
- pcpu->policy,
- freqmax - 1, CPUFREQ_RELATION_H);
- freq = pcpu->freq_table[index].frequency;
+ if (freq < freqmax)
+ continue;
- if (freq == freqmin) {
- /*
- * The first frequency below freqmax
- * has already been found to be too
- * low. freqmax is the lowest speed
- * we found that is fast enough.
- */
- freq = freqmax;
- break;
- }
+ /* Find highest frequency that is less than freqmax */
+ index = cpufreq_frequency_table_target(policy,
+ freqmax - 1, CPUFREQ_RELATION_H);
+
+ freq = freq_table[index].frequency;
+
+ if (freq == freqmin) {
+ /*
+ * The first frequency below freqmax has already
+ * been found to be too low. freqmax is the
+ * lowest speed we found that is fast enough.
+ */
+ freq = freqmax;
+ break;
}
} else if (freq < prevfreq) {
/* The previous frequency is high enough. */
freqmax = prevfreq;
- if (freq <= freqmin) {
- /*
- * Find the lowest frequency that is higher
- * than freqmin.
- */
- index = cpufreq_frequency_table_target(
- pcpu->policy,
- freqmin + 1, CPUFREQ_RELATION_L);
- freq = pcpu->freq_table[index].frequency;
+ if (freq > freqmin)
+ continue;
- /*
- * If freqmax is the first frequency above
- * freqmin then we have already found that
- * this speed is fast enough.
- */
- if (freq == freqmax)
- break;
- }
+ /* Find lowest frequency that is higher than freqmin */
+ index = cpufreq_frequency_table_target(policy,
+ freqmin + 1, CPUFREQ_RELATION_L);
+
+ freq = freq_table[index].frequency;
+
+ /*
+ * If freqmax is the first frequency above
+ * freqmin then we have already found that
+ * this speed is fast enough.
+ */
+ if (freq == freqmax)
+ break;
}
/* If same frequency chosen as previous then done. */
@@ -300,115 +334,97 @@
return freq;
}
-static u64 update_load(int cpu)
+static u64 update_load(struct interactive_cpu *icpu, int cpu)
{
- struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu);
- struct cpufreq_interactive_tunables *tunables =
- pcpu->policy->governor_data;
- u64 now;
- u64 now_idle;
- unsigned int delta_idle;
- unsigned int delta_time;
- u64 active_time;
+ struct interactive_tunables *tunables = icpu->ipolicy->tunables;
+ unsigned int delta_idle, delta_time;
+ u64 now_idle, now, active_time;
now_idle = get_cpu_idle_time(cpu, &now, tunables->io_is_busy);
- delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle);
- delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp);
+ delta_idle = (unsigned int)(now_idle - icpu->time_in_idle);
+ delta_time = (unsigned int)(now - icpu->time_in_idle_timestamp);
if (delta_time <= delta_idle)
active_time = 0;
else
active_time = delta_time - delta_idle;
- pcpu->cputime_speedadj += active_time * pcpu->policy->cur;
+ icpu->cputime_speedadj += active_time * icpu->ipolicy->policy->cur;
- pcpu->time_in_idle = now_idle;
- pcpu->time_in_idle_timestamp = now;
+ icpu->time_in_idle = now_idle;
+ icpu->time_in_idle_timestamp = now;
+
return now;
}
-static void cpufreq_interactive_timer(unsigned long data)
+/* Re-evaluate load to see if a frequency change is required or not */
+static void eval_target_freq(struct interactive_cpu *icpu)
{
- u64 now;
- unsigned int delta_time;
- u64 cputime_speedadj;
- int cpu_load;
- struct cpufreq_interactive_cpuinfo *pcpu =
- &per_cpu(cpuinfo, data);
- struct cpufreq_interactive_tunables *tunables =
- pcpu->policy->governor_data;
- unsigned int new_freq;
- unsigned int loadadjfreq;
- unsigned int index;
+ struct interactive_tunables *tunables = icpu->ipolicy->tunables;
+ struct cpufreq_policy *policy = icpu->ipolicy->policy;
+ struct cpufreq_frequency_table *freq_table = policy->freq_table;
+ u64 cputime_speedadj, now, max_fvtime;
+ unsigned int new_freq, loadadjfreq, index, delta_time;
unsigned long flags;
- u64 max_fvtime;
+ int cpu_load;
+ int cpu = smp_processor_id();
- if (!down_read_trylock(&pcpu->enable_sem))
- return;
- if (!pcpu->governor_enabled)
- goto exit;
-
- spin_lock_irqsave(&pcpu->load_lock, flags);
- now = update_load(data);
- delta_time = (unsigned int)(now - pcpu->cputime_speedadj_timestamp);
- cputime_speedadj = pcpu->cputime_speedadj;
- spin_unlock_irqrestore(&pcpu->load_lock, flags);
+ spin_lock_irqsave(&icpu->load_lock, flags);
+ now = update_load(icpu, smp_processor_id());
+ delta_time = (unsigned int)(now - icpu->cputime_speedadj_timestamp);
+ cputime_speedadj = icpu->cputime_speedadj;
+ spin_unlock_irqrestore(&icpu->load_lock, flags);
if (WARN_ON_ONCE(!delta_time))
- goto rearm;
+ return;
- spin_lock_irqsave(&pcpu->target_freq_lock, flags);
+ spin_lock_irqsave(&icpu->target_freq_lock, flags);
do_div(cputime_speedadj, delta_time);
loadadjfreq = (unsigned int)cputime_speedadj * 100;
- cpu_load = loadadjfreq / pcpu->policy->cur;
- tunables->boosted = tunables->boost_val || now < tunables->boostpulse_endtime;
+ cpu_load = loadadjfreq / policy->cur;
+ tunables->boosted = tunables->boost ||
+ now < tunables->boostpulse_endtime;
if (cpu_load >= tunables->go_hispeed_load || tunables->boosted) {
- if (pcpu->policy->cur < tunables->hispeed_freq) {
+ if (policy->cur < tunables->hispeed_freq) {
new_freq = tunables->hispeed_freq;
} else {
- new_freq = choose_freq(pcpu, loadadjfreq);
+ new_freq = choose_freq(icpu, loadadjfreq);
if (new_freq < tunables->hispeed_freq)
new_freq = tunables->hispeed_freq;
}
} else {
- new_freq = choose_freq(pcpu, loadadjfreq);
+ new_freq = choose_freq(icpu, loadadjfreq);
if (new_freq > tunables->hispeed_freq &&
- pcpu->policy->cur < tunables->hispeed_freq)
+ policy->cur < tunables->hispeed_freq)
new_freq = tunables->hispeed_freq;
}
- if (pcpu->policy->cur >= tunables->hispeed_freq &&
- new_freq > pcpu->policy->cur &&
- now - pcpu->pol_hispeed_val_time <
- freq_to_above_hispeed_delay(tunables, pcpu->policy->cur)) {
- trace_cpufreq_interactive_notyet(
- data, cpu_load, pcpu->target_freq,
- pcpu->policy->cur, new_freq);
- spin_unlock_irqrestore(&pcpu->target_freq_lock, flags);
- goto rearm;
+ if (policy->cur >= tunables->hispeed_freq &&
+ new_freq > policy->cur &&
+ now - icpu->pol_hispeed_val_time < freq_to_above_hispeed_delay(tunables, policy->cur)) {
+ trace_cpufreq_interactive_notyet(cpu, cpu_load,
+ icpu->target_freq, policy->cur, new_freq);
+ goto exit;
}
- pcpu->loc_hispeed_val_time = now;
+ icpu->loc_hispeed_val_time = now;
- index = cpufreq_frequency_table_target(pcpu->policy,
- new_freq, CPUFREQ_RELATION_L);
- new_freq = pcpu->freq_table[index].frequency;
+ index = cpufreq_frequency_table_target(policy, new_freq,
+ CPUFREQ_RELATION_L);
+ new_freq = freq_table[index].frequency;
/*
* Do not scale below floor_freq unless we have been at or above the
* floor frequency for the minimum sample time since last validated.
*/
- max_fvtime = max(pcpu->pol_floor_val_time, pcpu->loc_floor_val_time);
- if (new_freq < pcpu->floor_freq &&
- pcpu->target_freq >= pcpu->policy->cur) {
+ max_fvtime = max(icpu->pol_floor_val_time, icpu->loc_floor_val_time);
+ if (new_freq < icpu->floor_freq && icpu->target_freq >= policy->cur) {
if (now - max_fvtime < tunables->min_sample_time) {
- trace_cpufreq_interactive_notyet(
- data, cpu_load, pcpu->target_freq,
- pcpu->policy->cur, new_freq);
- spin_unlock_irqrestore(&pcpu->target_freq_lock, flags);
- goto rearm;
+ trace_cpufreq_interactive_notyet(cpu, cpu_load,
+ icpu->target_freq, policy->cur, new_freq);
+ goto exit;
}
}
@@ -421,82 +437,78 @@
*/
if (!tunables->boosted || new_freq > tunables->hispeed_freq) {
- pcpu->floor_freq = new_freq;
- if (pcpu->target_freq >= pcpu->policy->cur ||
- new_freq >= pcpu->policy->cur)
- pcpu->loc_floor_val_time = now;
+ icpu->floor_freq = new_freq;
+ if (icpu->target_freq >= policy->cur || new_freq >= policy->cur)
+ icpu->loc_floor_val_time = now;
}
- if (pcpu->target_freq == new_freq &&
- pcpu->target_freq <= pcpu->policy->cur) {
- trace_cpufreq_interactive_already(
- data, cpu_load, pcpu->target_freq,
- pcpu->policy->cur, new_freq);
- spin_unlock_irqrestore(&pcpu->target_freq_lock, flags);
- goto rearm;
+ if (icpu->target_freq == new_freq &&
+ icpu->target_freq <= policy->cur) {
+ trace_cpufreq_interactive_already(cpu, cpu_load,
+ icpu->target_freq, policy->cur, new_freq);
+ goto exit;
}
- trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq,
- pcpu->policy->cur, new_freq);
+ trace_cpufreq_interactive_target(cpu, cpu_load, icpu->target_freq,
+ policy->cur, new_freq);
- pcpu->target_freq = new_freq;
- spin_unlock_irqrestore(&pcpu->target_freq_lock, flags);
+ icpu->target_freq = new_freq;
+ spin_unlock_irqrestore(&icpu->target_freq_lock, flags);
+
spin_lock_irqsave(&speedchange_cpumask_lock, flags);
- cpumask_set_cpu(data, &speedchange_cpumask);
+ cpumask_set_cpu(cpu, &speedchange_cpumask);
spin_unlock_irqrestore(&speedchange_cpumask_lock, flags);
- wake_up_process(speedchange_task);
-rearm:
- if (!timer_pending(&pcpu->cpu_timer))
- cpufreq_interactive_timer_resched(pcpu);
+ wake_up_process(speedchange_task);
+ return;
exit:
- up_read(&pcpu->enable_sem);
- return;
+ spin_unlock_irqrestore(&icpu->target_freq_lock, flags);
+}
+
+static void cpufreq_interactive_update(struct interactive_cpu *icpu)
+{
+ eval_target_freq(icpu);
+ slack_timer_resched(icpu, smp_processor_id(), true);
}
static void cpufreq_interactive_idle_end(void)
{
- struct cpufreq_interactive_cpuinfo *pcpu =
- &per_cpu(cpuinfo, smp_processor_id());
+ struct interactive_cpu *icpu = &per_cpu(interactive_cpu,
+ smp_processor_id());
- if (!down_read_trylock(&pcpu->enable_sem))
+ if (!down_read_trylock(&icpu->enable_sem))
return;
- if (!pcpu->governor_enabled) {
- up_read(&pcpu->enable_sem);
- return;
+
+ if (icpu->ipolicy) {
+ /*
+ * We haven't sampled load for more than sampling_rate time, do
+ * it right now.
+ */
+ if (time_after_eq(jiffies, icpu->next_sample_jiffies))
+ cpufreq_interactive_update(icpu);
}
- /* Arm the timer for 1-2 ticks later if not already. */
- if (!timer_pending(&pcpu->cpu_timer)) {
- cpufreq_interactive_timer_resched(pcpu);
- } else if (time_after_eq(jiffies, pcpu->cpu_timer.expires)) {
- del_timer(&pcpu->cpu_timer);
- del_timer(&pcpu->cpu_slack_timer);
- cpufreq_interactive_timer(smp_processor_id());
- }
-
- up_read(&pcpu->enable_sem);
+ up_read(&icpu->enable_sem);
}
static void cpufreq_interactive_get_policy_info(struct cpufreq_policy *policy,
unsigned int *pmax_freq,
u64 *phvt, u64 *pfvt)
{
- struct cpufreq_interactive_cpuinfo *pcpu;
- unsigned int max_freq = 0;
+ struct interactive_cpu *icpu;
u64 hvt = ~0ULL, fvt = 0;
- unsigned int i;
+ unsigned int max_freq = 0, i;
for_each_cpu(i, policy->cpus) {
- pcpu = &per_cpu(cpuinfo, i);
+ icpu = &per_cpu(interactive_cpu, i);
- fvt = max(fvt, pcpu->loc_floor_val_time);
- if (pcpu->target_freq > max_freq) {
- max_freq = pcpu->target_freq;
- hvt = pcpu->loc_hispeed_val_time;
- } else if (pcpu->target_freq == max_freq) {
- hvt = min(hvt, pcpu->loc_hispeed_val_time);
+ fvt = max(fvt, icpu->loc_floor_val_time);
+ if (icpu->target_freq > max_freq) {
+ max_freq = icpu->target_freq;
+ hvt = icpu->loc_hispeed_val_time;
+ } else if (icpu->target_freq == max_freq) {
+ hvt = min(hvt, icpu->loc_hispeed_val_time);
}
}
@@ -508,7 +520,7 @@
static void cpufreq_interactive_adjust_cpu(unsigned int cpu,
struct cpufreq_policy *policy)
{
- struct cpufreq_interactive_cpuinfo *pcpu;
+ struct interactive_cpu *icpu;
u64 hvt, fvt;
unsigned int max_freq;
int i;
@@ -516,15 +528,15 @@
cpufreq_interactive_get_policy_info(policy, &max_freq, &hvt, &fvt);
for_each_cpu(i, policy->cpus) {
- pcpu = &per_cpu(cpuinfo, i);
- pcpu->pol_floor_val_time = fvt;
+ icpu = &per_cpu(interactive_cpu, i);
+ icpu->pol_floor_val_time = fvt;
}
if (max_freq != policy->cur) {
__cpufreq_driver_target(policy, max_freq, CPUFREQ_RELATION_H);
for_each_cpu(i, policy->cpus) {
- pcpu = &per_cpu(cpuinfo, i);
- pcpu->pol_hispeed_val_time = hvt;
+ icpu = &per_cpu(interactive_cpu, i);
+ icpu->pol_hispeed_val_time = hvt;
}
}
@@ -536,130 +548,112 @@
unsigned int cpu;
cpumask_t tmp_mask;
unsigned long flags;
- struct cpufreq_interactive_cpuinfo *pcpu;
- while (1) {
- set_current_state(TASK_INTERRUPTIBLE);
- spin_lock_irqsave(&speedchange_cpumask_lock, flags);
+again:
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_lock_irqsave(&speedchange_cpumask_lock, flags);
- if (cpumask_empty(&speedchange_cpumask)) {
- spin_unlock_irqrestore(&speedchange_cpumask_lock,
- flags);
- schedule();
-
- if (kthread_should_stop())
- break;
-
- spin_lock_irqsave(&speedchange_cpumask_lock, flags);
- }
-
- set_current_state(TASK_RUNNING);
- tmp_mask = speedchange_cpumask;
- cpumask_clear(&speedchange_cpumask);
+ if (cpumask_empty(&speedchange_cpumask)) {
spin_unlock_irqrestore(&speedchange_cpumask_lock, flags);
+ schedule();
- for_each_cpu(cpu, &tmp_mask) {
- pcpu = &per_cpu(cpuinfo, cpu);
+ if (kthread_should_stop())
+ return 0;
- down_write(&pcpu->policy->rwsem);
-
- if (likely(down_read_trylock(&pcpu->enable_sem))) {
- if (likely(pcpu->governor_enabled))
- cpufreq_interactive_adjust_cpu(cpu,
- pcpu->policy);
- up_read(&pcpu->enable_sem);
- }
-
- up_write(&pcpu->policy->rwsem);
- }
+ spin_lock_irqsave(&speedchange_cpumask_lock, flags);
}
- return 0;
+ set_current_state(TASK_RUNNING);
+ tmp_mask = speedchange_cpumask;
+ cpumask_clear(&speedchange_cpumask);
+ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags);
+
+ for_each_cpu(cpu, &tmp_mask) {
+ struct interactive_cpu *icpu = &per_cpu(interactive_cpu, cpu);
+ struct cpufreq_policy *policy = icpu->ipolicy->policy;
+
+ if (unlikely(!down_read_trylock(&icpu->enable_sem)))
+ continue;
+
+ if (likely(icpu->ipolicy))
+ cpufreq_interactive_adjust_cpu(cpu, policy);
+
+ up_read(&icpu->enable_sem);
+ }
+
+ goto again;
}
-static void cpufreq_interactive_boost(struct cpufreq_interactive_tunables *tunables)
+static void cpufreq_interactive_boost(struct interactive_tunables *tunables)
{
- int i;
- int anyboost = 0;
+ struct interactive_policy *ipolicy;
+ struct cpufreq_policy *policy;
+ struct interactive_cpu *icpu;
unsigned long flags[2];
- struct cpufreq_interactive_cpuinfo *pcpu;
+ bool wakeup = false;
+ int i;
tunables->boosted = true;
spin_lock_irqsave(&speedchange_cpumask_lock, flags[0]);
- for_each_online_cpu(i) {
- pcpu = &per_cpu(cpuinfo, i);
+ for_each_ipolicy(ipolicy) {
+ policy = ipolicy->policy;
- if (!down_read_trylock(&pcpu->enable_sem))
- continue;
+ for_each_cpu(i, policy->cpus) {
+ icpu = &per_cpu(interactive_cpu, i);
- if (!pcpu->governor_enabled) {
- up_read(&pcpu->enable_sem);
- continue;
+ if (!down_read_trylock(&icpu->enable_sem))
+ continue;
+
+ if (!icpu->ipolicy) {
+ up_read(&icpu->enable_sem);
+ continue;
+ }
+
+ spin_lock_irqsave(&icpu->target_freq_lock, flags[1]);
+ if (icpu->target_freq < tunables->hispeed_freq) {
+ icpu->target_freq = tunables->hispeed_freq;
+ cpumask_set_cpu(i, &speedchange_cpumask);
+ icpu->pol_hispeed_val_time = ktime_to_us(ktime_get());
+ wakeup = true;
+ }
+ spin_unlock_irqrestore(&icpu->target_freq_lock, flags[1]);
+
+ up_read(&icpu->enable_sem);
}
-
- if (tunables != pcpu->policy->governor_data) {
- up_read(&pcpu->enable_sem);
- continue;
- }
-
- spin_lock_irqsave(&pcpu->target_freq_lock, flags[1]);
- if (pcpu->target_freq < tunables->hispeed_freq) {
- pcpu->target_freq = tunables->hispeed_freq;
- cpumask_set_cpu(i, &speedchange_cpumask);
- pcpu->pol_hispeed_val_time =
- ktime_to_us(ktime_get());
- anyboost = 1;
- }
- spin_unlock_irqrestore(&pcpu->target_freq_lock, flags[1]);
-
- up_read(&pcpu->enable_sem);
}
spin_unlock_irqrestore(&speedchange_cpumask_lock, flags[0]);
- if (anyboost)
+ if (wakeup)
wake_up_process(speedchange_task);
}
-static int cpufreq_interactive_notifier(
- struct notifier_block *nb, unsigned long val, void *data)
+static int cpufreq_interactive_notifier(struct notifier_block *nb,
+ unsigned long val, void *data)
{
struct cpufreq_freqs *freq = data;
- struct cpufreq_interactive_cpuinfo *pcpu;
- int cpu;
+ struct interactive_cpu *icpu = &per_cpu(interactive_cpu, freq->cpu);
unsigned long flags;
- if (val == CPUFREQ_POSTCHANGE) {
- pcpu = &per_cpu(cpuinfo, freq->cpu);
- if (!down_read_trylock(&pcpu->enable_sem))
- return 0;
- if (!pcpu->governor_enabled) {
- up_read(&pcpu->enable_sem);
- return 0;
- }
+ if (val != CPUFREQ_POSTCHANGE)
+ return 0;
- for_each_cpu(cpu, pcpu->policy->cpus) {
- struct cpufreq_interactive_cpuinfo *pjcpu =
- &per_cpu(cpuinfo, cpu);
- if (cpu != freq->cpu) {
- if (!down_read_trylock(&pjcpu->enable_sem))
- continue;
- if (!pjcpu->governor_enabled) {
- up_read(&pjcpu->enable_sem);
- continue;
- }
- }
- spin_lock_irqsave(&pjcpu->load_lock, flags);
- update_load(cpu);
- spin_unlock_irqrestore(&pjcpu->load_lock, flags);
- if (cpu != freq->cpu)
- up_read(&pjcpu->enable_sem);
- }
+ if (!down_read_trylock(&icpu->enable_sem))
+ return 0;
- up_read(&pcpu->enable_sem);
+ if (!icpu->ipolicy) {
+ up_read(&icpu->enable_sem);
+ return 0;
}
+
+ spin_lock_irqsave(&icpu->load_lock, flags);
+ update_load(icpu, freq->cpu);
+ spin_unlock_irqrestore(&icpu->load_lock, flags);
+
+ up_read(&icpu->enable_sem);
+
return 0;
}
@@ -669,29 +663,26 @@
static unsigned int *get_tokenized_data(const char *buf, int *num_tokens)
{
- const char *cp;
- int i;
- int ntokens = 1;
+ const char *cp = buf;
+ int ntokens = 1, i = 0;
unsigned int *tokenized_data;
int err = -EINVAL;
- cp = buf;
while ((cp = strpbrk(cp + 1, " :")))
ntokens++;
if (!(ntokens & 0x1))
goto err;
- tokenized_data = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL);
+ tokenized_data = kcalloc(ntokens, sizeof(*tokenized_data), GFP_KERNEL);
if (!tokenized_data) {
err = -ENOMEM;
goto err;
}
cp = buf;
- i = 0;
while (i < ntokens) {
- if (sscanf(cp, "%u", &tokenized_data[i++]) != 1)
+ if (kstrtouint(cp, 0, &tokenized_data[i++]) < 0)
goto err_kfree;
cp = strpbrk(cp, " :");
@@ -712,13 +703,25 @@
return ERR_PTR(err);
}
-static ssize_t show_target_loads(
- struct cpufreq_interactive_tunables *tunables,
- char *buf)
+/* Interactive governor sysfs interface */
+static struct interactive_tunables *to_tunables(struct gov_attr_set *attr_set)
{
- int i;
- ssize_t ret = 0;
+ return container_of(attr_set, struct interactive_tunables, attr_set);
+}
+
+#define show_one(file_name, type) \
+static ssize_t show_##file_name(struct gov_attr_set *attr_set, char *buf) \
+{ \
+ struct interactive_tunables *tunables = to_tunables(attr_set); \
+ return sprintf(buf, type "\n", tunables->file_name); \
+}
+
+static ssize_t show_target_loads(struct gov_attr_set *attr_set, char *buf)
+{
+ struct interactive_tunables *tunables = to_tunables(attr_set);
unsigned long flags;
+ ssize_t ret = 0;
+ int i;
spin_lock_irqsave(&tunables->target_loads_lock, flags);
@@ -728,20 +731,21 @@
sprintf(buf + ret - 1, "\n");
spin_unlock_irqrestore(&tunables->target_loads_lock, flags);
+
return ret;
}
-static ssize_t store_target_loads(
- struct cpufreq_interactive_tunables *tunables,
- const char *buf, size_t count)
+static ssize_t store_target_loads(struct gov_attr_set *attr_set,
+ const char *buf, size_t count)
{
- int ntokens;
- unsigned int *new_target_loads = NULL;
+ struct interactive_tunables *tunables = to_tunables(attr_set);
+ unsigned int *new_target_loads;
unsigned long flags;
+ int ntokens;
new_target_loads = get_tokenized_data(buf, &ntokens);
if (IS_ERR(new_target_loads))
- return PTR_RET(new_target_loads);
+ return PTR_ERR(new_target_loads);
spin_lock_irqsave(&tunables->target_loads_lock, flags);
if (tunables->target_loads != default_target_loads)
@@ -749,15 +753,17 @@
tunables->target_loads = new_target_loads;
tunables->ntarget_loads = ntokens;
spin_unlock_irqrestore(&tunables->target_loads_lock, flags);
+
return count;
}
-static ssize_t show_above_hispeed_delay(
- struct cpufreq_interactive_tunables *tunables, char *buf)
+static ssize_t show_above_hispeed_delay(struct gov_attr_set *attr_set,
+ char *buf)
{
- int i;
- ssize_t ret = 0;
+ struct interactive_tunables *tunables = to_tunables(attr_set);
unsigned long flags;
+ ssize_t ret = 0;
+ int i;
spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags);
@@ -768,20 +774,21 @@
sprintf(buf + ret - 1, "\n");
spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags);
+
return ret;
}
-static ssize_t store_above_hispeed_delay(
- struct cpufreq_interactive_tunables *tunables,
- const char *buf, size_t count)
+static ssize_t store_above_hispeed_delay(struct gov_attr_set *attr_set,
+ const char *buf, size_t count)
{
- int ntokens;
+ struct interactive_tunables *tunables = to_tunables(attr_set);
unsigned int *new_above_hispeed_delay = NULL;
unsigned long flags;
+ int ntokens;
new_above_hispeed_delay = get_tokenized_data(buf, &ntokens);
if (IS_ERR(new_above_hispeed_delay))
- return PTR_RET(new_above_hispeed_delay);
+ return PTR_ERR(new_above_hispeed_delay);
spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags);
if (tunables->above_hispeed_delay != default_above_hispeed_delay)
@@ -789,78 +796,71 @@
tunables->above_hispeed_delay = new_above_hispeed_delay;
tunables->nabove_hispeed_delay = ntokens;
spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags);
+
return count;
-
}
-static ssize_t show_hispeed_freq(struct cpufreq_interactive_tunables *tunables,
- char *buf)
+static ssize_t store_hispeed_freq(struct gov_attr_set *attr_set,
+ const char *buf, size_t count)
{
- return sprintf(buf, "%u\n", tunables->hispeed_freq);
-}
-
-static ssize_t store_hispeed_freq(struct cpufreq_interactive_tunables *tunables,
- const char *buf, size_t count)
-{
+ struct interactive_tunables *tunables = to_tunables(attr_set);
+ unsigned long int val;
int ret;
- long unsigned int val;
ret = kstrtoul(buf, 0, &val);
if (ret < 0)
return ret;
+
tunables->hispeed_freq = val;
+
return count;
}
-static ssize_t show_go_hispeed_load(struct cpufreq_interactive_tunables
- *tunables, char *buf)
+static ssize_t store_go_hispeed_load(struct gov_attr_set *attr_set,
+ const char *buf, size_t count)
{
- return sprintf(buf, "%lu\n", tunables->go_hispeed_load);
-}
-
-static ssize_t store_go_hispeed_load(struct cpufreq_interactive_tunables
- *tunables, const char *buf, size_t count)
-{
- int ret;
+ struct interactive_tunables *tunables = to_tunables(attr_set);
unsigned long val;
+ int ret;
ret = kstrtoul(buf, 0, &val);
if (ret < 0)
return ret;
+
tunables->go_hispeed_load = val;
+
return count;
}
-static ssize_t show_min_sample_time(struct cpufreq_interactive_tunables
- *tunables, char *buf)
+static ssize_t store_min_sample_time(struct gov_attr_set *attr_set,
+ const char *buf, size_t count)
{
- return sprintf(buf, "%lu\n", tunables->min_sample_time);
-}
-
-static ssize_t store_min_sample_time(struct cpufreq_interactive_tunables
- *tunables, const char *buf, size_t count)
-{
- int ret;
+ struct interactive_tunables *tunables = to_tunables(attr_set);
unsigned long val;
+ int ret;
ret = kstrtoul(buf, 0, &val);
if (ret < 0)
return ret;
+
tunables->min_sample_time = val;
+
return count;
}
-static ssize_t show_timer_rate(struct cpufreq_interactive_tunables *tunables,
- char *buf)
+static ssize_t show_timer_rate(struct gov_attr_set *attr_set, char *buf)
{
- return sprintf(buf, "%lu\n", tunables->timer_rate);
+ struct interactive_tunables *tunables = to_tunables(attr_set);
+
+ return sprintf(buf, "%lu\n", tunables->sampling_rate);
}
-static ssize_t store_timer_rate(struct cpufreq_interactive_tunables *tunables,
- const char *buf, size_t count)
+static ssize_t store_timer_rate(struct gov_attr_set *attr_set, const char *buf,
+ size_t count)
{
- int ret;
+ struct interactive_tunables *tunables = to_tunables(attr_set);
unsigned long val, val_round;
+ int ret;
ret = kstrtoul(buf, 0, &val);
if (ret < 0)
@@ -871,49 +871,42 @@
pr_warn("timer_rate not aligned to jiffy. Rounded up to %lu\n",
val_round);
- tunables->timer_rate = val_round;
+ tunables->sampling_rate = val_round;
+
return count;
}
-static ssize_t show_timer_slack(struct cpufreq_interactive_tunables *tunables,
- char *buf)
+static ssize_t store_timer_slack(struct gov_attr_set *attr_set, const char *buf,
+ size_t count)
{
- return sprintf(buf, "%d\n", tunables->timer_slack_val);
-}
-
-static ssize_t store_timer_slack(struct cpufreq_interactive_tunables *tunables,
- const char *buf, size_t count)
-{
- int ret;
+ struct interactive_tunables *tunables = to_tunables(attr_set);
unsigned long val;
+ int ret;
ret = kstrtol(buf, 10, &val);
if (ret < 0)
return ret;
- tunables->timer_slack_val = val;
+ tunables->timer_slack = val;
+ update_slack_delay(tunables);
+
return count;
}
-static ssize_t show_boost(struct cpufreq_interactive_tunables *tunables,
- char *buf)
+static ssize_t store_boost(struct gov_attr_set *attr_set, const char *buf,
+ size_t count)
{
- return sprintf(buf, "%d\n", tunables->boost_val);
-}
-
-static ssize_t store_boost(struct cpufreq_interactive_tunables *tunables,
- const char *buf, size_t count)
-{
- int ret;
+ struct interactive_tunables *tunables = to_tunables(attr_set);
unsigned long val;
+ int ret;
ret = kstrtoul(buf, 0, &val);
if (ret < 0)
return ret;
- tunables->boost_val = val;
+ tunables->boost = val;
- if (tunables->boost_val) {
+ if (tunables->boost) {
trace_cpufreq_interactive_boost("on");
if (!tunables->boosted)
cpufreq_interactive_boost(tunables);
@@ -925,193 +918,100 @@
return count;
}
-static ssize_t store_boostpulse(struct cpufreq_interactive_tunables *tunables,
- const char *buf, size_t count)
+static ssize_t store_boostpulse(struct gov_attr_set *attr_set, const char *buf,
+ size_t count)
{
- int ret;
+ struct interactive_tunables *tunables = to_tunables(attr_set);
unsigned long val;
+ int ret;
ret = kstrtoul(buf, 0, &val);
if (ret < 0)
return ret;
tunables->boostpulse_endtime = ktime_to_us(ktime_get()) +
- tunables->boostpulse_duration_val;
+ tunables->boostpulse_duration;
trace_cpufreq_interactive_boost("pulse");
if (!tunables->boosted)
cpufreq_interactive_boost(tunables);
+
return count;
}
-static ssize_t show_boostpulse_duration(struct cpufreq_interactive_tunables
- *tunables, char *buf)
+static ssize_t store_boostpulse_duration(struct gov_attr_set *attr_set,
+ const char *buf, size_t count)
{
- return sprintf(buf, "%d\n", tunables->boostpulse_duration_val);
-}
-
-static ssize_t store_boostpulse_duration(struct cpufreq_interactive_tunables
- *tunables, const char *buf, size_t count)
-{
- int ret;
+ struct interactive_tunables *tunables = to_tunables(attr_set);
unsigned long val;
+ int ret;
ret = kstrtoul(buf, 0, &val);
if (ret < 0)
return ret;
- tunables->boostpulse_duration_val = val;
+ tunables->boostpulse_duration = val;
+
return count;
}
-static ssize_t show_io_is_busy(struct cpufreq_interactive_tunables *tunables,
- char *buf)
+static ssize_t store_io_is_busy(struct gov_attr_set *attr_set, const char *buf,
+ size_t count)
{
- return sprintf(buf, "%u\n", tunables->io_is_busy);
-}
-
-static ssize_t store_io_is_busy(struct cpufreq_interactive_tunables *tunables,
- const char *buf, size_t count)
-{
- int ret;
+ struct interactive_tunables *tunables = to_tunables(attr_set);
unsigned long val;
+ int ret;
ret = kstrtoul(buf, 0, &val);
if (ret < 0)
return ret;
+
tunables->io_is_busy = val;
+
return count;
}
-/*
- * Create show/store routines
- * - sys: One governor instance for complete SYSTEM
- * - pol: One governor instance per struct cpufreq_policy
- */
-#define show_gov_pol_sys(file_name) \
-static ssize_t show_##file_name##_gov_sys \
-(struct kobject *kobj, struct attribute *attr, char *buf) \
-{ \
- return show_##file_name(common_tunables, buf); \
-} \
- \
-static ssize_t show_##file_name##_gov_pol \
-(struct cpufreq_policy *policy, char *buf) \
-{ \
- return show_##file_name(policy->governor_data, buf); \
-}
+show_one(hispeed_freq, "%u");
+show_one(go_hispeed_load, "%lu");
+show_one(min_sample_time, "%lu");
+show_one(timer_slack, "%lu");
+show_one(boost, "%u");
+show_one(boostpulse_duration, "%u");
+show_one(io_is_busy, "%u");
-#define store_gov_pol_sys(file_name) \
-static ssize_t store_##file_name##_gov_sys \
-(struct kobject *kobj, struct attribute *attr, const char *buf, \
- size_t count) \
-{ \
- return store_##file_name(common_tunables, buf, count); \
-} \
- \
-static ssize_t store_##file_name##_gov_pol \
-(struct cpufreq_policy *policy, const char *buf, size_t count) \
-{ \
- return store_##file_name(policy->governor_data, buf, count); \
-}
+gov_attr_rw(target_loads);
+gov_attr_rw(above_hispeed_delay);
+gov_attr_rw(hispeed_freq);
+gov_attr_rw(go_hispeed_load);
+gov_attr_rw(min_sample_time);
+gov_attr_rw(timer_rate);
+gov_attr_rw(timer_slack);
+gov_attr_rw(boost);
+gov_attr_wo(boostpulse);
+gov_attr_rw(boostpulse_duration);
+gov_attr_rw(io_is_busy);
-#define show_store_gov_pol_sys(file_name) \
-show_gov_pol_sys(file_name); \
-store_gov_pol_sys(file_name)
-
-show_store_gov_pol_sys(target_loads);
-show_store_gov_pol_sys(above_hispeed_delay);
-show_store_gov_pol_sys(hispeed_freq);
-show_store_gov_pol_sys(go_hispeed_load);
-show_store_gov_pol_sys(min_sample_time);
-show_store_gov_pol_sys(timer_rate);
-show_store_gov_pol_sys(timer_slack);
-show_store_gov_pol_sys(boost);
-store_gov_pol_sys(boostpulse);
-show_store_gov_pol_sys(boostpulse_duration);
-show_store_gov_pol_sys(io_is_busy);
-
-#define gov_sys_attr_rw(_name) \
-static struct global_attr _name##_gov_sys = \
-__ATTR(_name, 0644, show_##_name##_gov_sys, store_##_name##_gov_sys)
-
-#define gov_pol_attr_rw(_name) \
-static struct freq_attr _name##_gov_pol = \
-__ATTR(_name, 0644, show_##_name##_gov_pol, store_##_name##_gov_pol)
-
-#define gov_sys_pol_attr_rw(_name) \
- gov_sys_attr_rw(_name); \
- gov_pol_attr_rw(_name)
-
-gov_sys_pol_attr_rw(target_loads);
-gov_sys_pol_attr_rw(above_hispeed_delay);
-gov_sys_pol_attr_rw(hispeed_freq);
-gov_sys_pol_attr_rw(go_hispeed_load);
-gov_sys_pol_attr_rw(min_sample_time);
-gov_sys_pol_attr_rw(timer_rate);
-gov_sys_pol_attr_rw(timer_slack);
-gov_sys_pol_attr_rw(boost);
-gov_sys_pol_attr_rw(boostpulse_duration);
-gov_sys_pol_attr_rw(io_is_busy);
-
-static struct global_attr boostpulse_gov_sys =
- __ATTR(boostpulse, 0200, NULL, store_boostpulse_gov_sys);
-
-static struct freq_attr boostpulse_gov_pol =
- __ATTR(boostpulse, 0200, NULL, store_boostpulse_gov_pol);
-
-/* One Governor instance for entire system */
-static struct attribute *interactive_attributes_gov_sys[] = {
- &target_loads_gov_sys.attr,
- &above_hispeed_delay_gov_sys.attr,
- &hispeed_freq_gov_sys.attr,
- &go_hispeed_load_gov_sys.attr,
- &min_sample_time_gov_sys.attr,
- &timer_rate_gov_sys.attr,
- &timer_slack_gov_sys.attr,
- &boost_gov_sys.attr,
- &boostpulse_gov_sys.attr,
- &boostpulse_duration_gov_sys.attr,
- &io_is_busy_gov_sys.attr,
- NULL,
+static struct attribute *interactive_attributes[] = {
+ &target_loads.attr,
+ &above_hispeed_delay.attr,
+ &hispeed_freq.attr,
+ &go_hispeed_load.attr,
+ &min_sample_time.attr,
+ &timer_rate.attr,
+ &timer_slack.attr,
+ &boost.attr,
+ &boostpulse.attr,
+ &boostpulse_duration.attr,
+ &io_is_busy.attr,
+ NULL
};
-static struct attribute_group interactive_attr_group_gov_sys = {
- .attrs = interactive_attributes_gov_sys,
- .name = "interactive",
+static struct kobj_type interactive_tunables_ktype = {
+ .default_attrs = interactive_attributes,
+ .sysfs_ops = &governor_sysfs_ops,
};
-/* Per policy governor instance */
-static struct attribute *interactive_attributes_gov_pol[] = {
- &target_loads_gov_pol.attr,
- &above_hispeed_delay_gov_pol.attr,
- &hispeed_freq_gov_pol.attr,
- &go_hispeed_load_gov_pol.attr,
- &min_sample_time_gov_pol.attr,
- &timer_rate_gov_pol.attr,
- &timer_slack_gov_pol.attr,
- &boost_gov_pol.attr,
- &boostpulse_gov_pol.attr,
- &boostpulse_duration_gov_pol.attr,
- &io_is_busy_gov_pol.attr,
- NULL,
-};
-
-static struct attribute_group interactive_attr_group_gov_pol = {
- .attrs = interactive_attributes_gov_pol,
- .name = "interactive",
-};
-
-static struct attribute_group *get_sysfs_attr(void)
-{
- if (have_governor_per_policy())
- return &interactive_attr_group_gov_pol;
- else
- return &interactive_attr_group_gov_sys;
-}
-
static int cpufreq_interactive_idle_notifier(struct notifier_block *nb,
- unsigned long val,
- void *data)
+ unsigned long val, void *data)
{
if (val == IDLE_END)
cpufreq_interactive_idle_end();
@@ -1123,31 +1023,170 @@
.notifier_call = cpufreq_interactive_idle_notifier,
};
-static int cpufreq_governor_interactive_init(struct cpufreq_policy *policy)
+/* Interactive Governor callbacks */
+struct interactive_governor {
+ struct cpufreq_governor gov;
+ unsigned int usage_count;
+};
+
+static struct interactive_governor interactive_gov;
+
+#define CPU_FREQ_GOV_INTERACTIVE (&interactive_gov.gov)
+
+static void irq_work(struct irq_work *irq_work)
{
- int rc;
- struct cpufreq_interactive_tunables *tunables;
+ struct interactive_cpu *icpu = container_of(irq_work, struct
+ interactive_cpu, irq_work);
- if (have_governor_per_policy())
- tunables = policy->governor_data;
- else
- tunables = common_tunables;
+ cpufreq_interactive_update(icpu);
+ icpu->work_in_progress = false;
+}
- if (have_governor_per_policy()) {
- WARN_ON(tunables);
- } else if (tunables) {
- tunables->usage_count++;
- policy->governor_data = tunables;
- return 0;
+static void update_util_handler(struct update_util_data *data, u64 time,
+ unsigned int flags)
+{
+ struct interactive_cpu *icpu = container_of(data,
+ struct interactive_cpu, update_util);
+ struct interactive_policy *ipolicy = icpu->ipolicy;
+ struct interactive_tunables *tunables = ipolicy->tunables;
+ u64 delta_ns;
+
+ /*
+ * The irq-work may not be allowed to be queued up right now.
+ * Possible reasons:
+ * - Work has already been queued up or is in progress.
+ * - It is too early (too little time from the previous sample).
+ */
+ if (icpu->work_in_progress)
+ return;
+
+ delta_ns = time - icpu->last_sample_time;
+ if ((s64)delta_ns < tunables->sampling_rate * NSEC_PER_USEC)
+ return;
+
+ icpu->last_sample_time = time;
+ icpu->next_sample_jiffies = usecs_to_jiffies(tunables->sampling_rate) +
+ jiffies;
+
+ icpu->work_in_progress = true;
+ irq_work_queue(&icpu->irq_work);
+}
+
+static void gov_set_update_util(struct interactive_policy *ipolicy)
+{
+ struct cpufreq_policy *policy = ipolicy->policy;
+ struct interactive_cpu *icpu;
+ int cpu;
+
+ for_each_cpu(cpu, policy->cpus) {
+ icpu = &per_cpu(interactive_cpu, cpu);
+
+ icpu->last_sample_time = 0;
+ icpu->next_sample_jiffies = 0;
+ cpufreq_add_update_util_hook(cpu, &icpu->update_util,
+ update_util_handler);
}
+}
+
+static inline void gov_clear_update_util(struct cpufreq_policy *policy)
+{
+ int i;
+
+ for_each_cpu(i, policy->cpus)
+ cpufreq_remove_update_util_hook(i);
+
+ synchronize_sched();
+}
+
+static void icpu_cancel_work(struct interactive_cpu *icpu)
+{
+ irq_work_sync(&icpu->irq_work);
+ icpu->work_in_progress = false;
+ del_timer_sync(&icpu->slack_timer);
+}
+
+static struct interactive_policy *
+interactive_policy_alloc(struct cpufreq_policy *policy)
+{
+ struct interactive_policy *ipolicy;
+
+ ipolicy = kzalloc(sizeof(*ipolicy), GFP_KERNEL);
+ if (!ipolicy)
+ return NULL;
+
+ ipolicy->policy = policy;
+
+ return ipolicy;
+}
+
+static void interactive_policy_free(struct interactive_policy *ipolicy)
+{
+ kfree(ipolicy);
+}
+
+static struct interactive_tunables *
+interactive_tunables_alloc(struct interactive_policy *ipolicy)
+{
+ struct interactive_tunables *tunables;
tunables = kzalloc(sizeof(*tunables), GFP_KERNEL);
- if (!tunables) {
- pr_err("%s: POLICY_INIT: kzalloc failed\n", __func__);
+ if (!tunables)
+ return NULL;
+
+ gov_attr_set_init(&tunables->attr_set, &ipolicy->tunables_hook);
+ if (!have_governor_per_policy())
+ global_tunables = tunables;
+
+ ipolicy->tunables = tunables;
+
+ return tunables;
+}
+
+static void interactive_tunables_free(struct interactive_tunables *tunables)
+{
+ if (!have_governor_per_policy())
+ global_tunables = NULL;
+
+ kfree(tunables);
+}
+
+int cpufreq_interactive_init(struct cpufreq_policy *policy)
+{
+ struct interactive_policy *ipolicy;
+ struct interactive_tunables *tunables;
+ int ret;
+
+ /* State should be equivalent to EXIT */
+ if (policy->governor_data)
+ return -EBUSY;
+
+ ipolicy = interactive_policy_alloc(policy);
+ if (!ipolicy)
return -ENOMEM;
+
+ mutex_lock(&global_tunables_lock);
+
+ if (global_tunables) {
+ if (WARN_ON(have_governor_per_policy())) {
+ ret = -EINVAL;
+ goto free_int_policy;
+ }
+
+ policy->governor_data = ipolicy;
+ ipolicy->tunables = global_tunables;
+
+ gov_attr_set_get(&global_tunables->attr_set,
+ &ipolicy->tunables_hook);
+ goto out;
}
- tunables->usage_count = 1;
+ tunables = interactive_tunables_alloc(ipolicy);
+ if (!tunables) {
+ ret = -ENOMEM;
+ goto free_int_policy;
+ }
+
+ tunables->hispeed_freq = policy->max;
tunables->above_hispeed_delay = default_above_hispeed_delay;
tunables->nabove_hispeed_delay =
ARRAY_SIZE(default_above_hispeed_delay);
@@ -1155,226 +1194,217 @@
tunables->target_loads = default_target_loads;
tunables->ntarget_loads = ARRAY_SIZE(default_target_loads);
tunables->min_sample_time = DEFAULT_MIN_SAMPLE_TIME;
- tunables->timer_rate = DEFAULT_TIMER_RATE;
- tunables->boostpulse_duration_val = DEFAULT_MIN_SAMPLE_TIME;
- tunables->timer_slack_val = DEFAULT_TIMER_SLACK;
+ tunables->boostpulse_duration = DEFAULT_MIN_SAMPLE_TIME;
+ tunables->sampling_rate = DEFAULT_SAMPLING_RATE;
+ tunables->timer_slack = DEFAULT_TIMER_SLACK;
+ update_slack_delay(tunables);
spin_lock_init(&tunables->target_loads_lock);
spin_lock_init(&tunables->above_hispeed_delay_lock);
- policy->governor_data = tunables;
- if (!have_governor_per_policy()) {
- common_tunables = tunables;
+ policy->governor_data = ipolicy;
+
+ ret = kobject_init_and_add(&tunables->attr_set.kobj,
+ &interactive_tunables_ktype,
+ get_governor_parent_kobj(policy), "%s",
+ interactive_gov.gov.name);
+ if (ret)
+ goto fail;
+
+ /* One time initialization for governor */
+ if (!interactive_gov.usage_count++) {
+ idle_notifier_register(&cpufreq_interactive_idle_nb);
+ cpufreq_register_notifier(&cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
}
- rc = sysfs_create_group(get_governor_parent_kobj(policy),
- get_sysfs_attr());
- if (rc) {
- kfree(tunables);
- policy->governor_data = NULL;
- if (!have_governor_per_policy()) {
- common_tunables = NULL;
- }
- return rc;
- }
-
- idle_notifier_register(&cpufreq_interactive_idle_nb);
- cpufreq_register_notifier(&cpufreq_notifier_block,
- CPUFREQ_TRANSITION_NOTIFIER);
-
+ out:
+ mutex_unlock(&global_tunables_lock);
return 0;
-}
-static void cpufreq_governor_interactive_exit(struct cpufreq_policy *policy)
-{
- struct cpufreq_interactive_tunables *tunables;
-
- if (have_governor_per_policy())
- tunables = policy->governor_data;
- else
- tunables = common_tunables;
-
- WARN_ON(!tunables);
-
- if (!--tunables->usage_count) {
- cpufreq_unregister_notifier(&cpufreq_notifier_block,
- CPUFREQ_TRANSITION_NOTIFIER);
- idle_notifier_unregister(&cpufreq_interactive_idle_nb);
-
- sysfs_remove_group(get_governor_parent_kobj(policy),
- get_sysfs_attr());
-
- kfree(tunables);
- common_tunables = NULL;
- }
-
+ fail:
policy->governor_data = NULL;
+ interactive_tunables_free(tunables);
+
+ free_int_policy:
+ mutex_unlock(&global_tunables_lock);
+
+ interactive_policy_free(ipolicy);
+ pr_err("governor initialization failed (%d)\n", ret);
+
+ return ret;
}
-static int cpufreq_governor_interactive_start(struct cpufreq_policy *policy)
+void cpufreq_interactive_exit(struct cpufreq_policy *policy)
{
- unsigned int j;
- struct cpufreq_interactive_cpuinfo *pcpu;
- struct cpufreq_frequency_table *freq_table;
- struct cpufreq_interactive_tunables *tunables;
+ struct interactive_policy *ipolicy = policy->governor_data;
+ struct interactive_tunables *tunables = ipolicy->tunables;
+ unsigned int count;
- if (have_governor_per_policy())
- tunables = policy->governor_data;
- else
- tunables = common_tunables;
+ mutex_lock(&global_tunables_lock);
- WARN_ON(!tunables);
-
- mutex_lock(&gov_lock);
-
- freq_table = policy->freq_table;
- if (!tunables->hispeed_freq)
- tunables->hispeed_freq = policy->max;
-
- for_each_cpu(j, policy->cpus) {
- pcpu = &per_cpu(cpuinfo, j);
- pcpu->policy = policy;
- pcpu->target_freq = policy->cur;
- pcpu->freq_table = freq_table;
- pcpu->floor_freq = pcpu->target_freq;
- pcpu->pol_floor_val_time =
- ktime_to_us(ktime_get());
- pcpu->loc_floor_val_time = pcpu->pol_floor_val_time;
- pcpu->pol_hispeed_val_time = pcpu->pol_floor_val_time;
- pcpu->loc_hispeed_val_time = pcpu->pol_floor_val_time;
- down_write(&pcpu->enable_sem);
- del_timer_sync(&pcpu->cpu_timer);
- del_timer_sync(&pcpu->cpu_slack_timer);
- cpufreq_interactive_timer_start(tunables, j);
- pcpu->governor_enabled = 1;
- up_write(&pcpu->enable_sem);
+ /* Last policy using the governor ? */
+ if (!--interactive_gov.usage_count) {
+ cpufreq_unregister_notifier(&cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+ idle_notifier_unregister(&cpufreq_interactive_idle_nb);
}
- mutex_unlock(&gov_lock);
+ count = gov_attr_set_put(&tunables->attr_set, &ipolicy->tunables_hook);
+ policy->governor_data = NULL;
+ if (!count)
+ interactive_tunables_free(tunables);
+
+ mutex_unlock(&global_tunables_lock);
+
+ interactive_policy_free(ipolicy);
+}
+
+int cpufreq_interactive_start(struct cpufreq_policy *policy)
+{
+ struct interactive_policy *ipolicy = policy->governor_data;
+ struct interactive_cpu *icpu;
+ unsigned int cpu;
+
+ for_each_cpu(cpu, policy->cpus) {
+ icpu = &per_cpu(interactive_cpu, cpu);
+
+ icpu->target_freq = policy->cur;
+ icpu->floor_freq = icpu->target_freq;
+ icpu->pol_floor_val_time = ktime_to_us(ktime_get());
+ icpu->loc_floor_val_time = icpu->pol_floor_val_time;
+ icpu->pol_hispeed_val_time = icpu->pol_floor_val_time;
+ icpu->loc_hispeed_val_time = icpu->pol_floor_val_time;
+
+ down_write(&icpu->enable_sem);
+ icpu->ipolicy = ipolicy;
+ up_write(&icpu->enable_sem);
+
+ slack_timer_resched(icpu, cpu, false);
+ }
+
+ gov_set_update_util(ipolicy);
return 0;
}
-static void cpufreq_governor_interactive_stop(struct cpufreq_policy *policy)
+void cpufreq_interactive_stop(struct cpufreq_policy *policy)
{
- unsigned int j;
- struct cpufreq_interactive_cpuinfo *pcpu;
+ struct interactive_policy *ipolicy = policy->governor_data;
+ struct interactive_cpu *icpu;
+ unsigned int cpu;
- mutex_lock(&gov_lock);
- for_each_cpu(j, policy->cpus) {
- pcpu = &per_cpu(cpuinfo, j);
- down_write(&pcpu->enable_sem);
- pcpu->governor_enabled = 0;
- del_timer_sync(&pcpu->cpu_timer);
- del_timer_sync(&pcpu->cpu_slack_timer);
- up_write(&pcpu->enable_sem);
+ gov_clear_update_util(ipolicy->policy);
+
+ for_each_cpu(cpu, policy->cpus) {
+ icpu = &per_cpu(interactive_cpu, cpu);
+
+ icpu_cancel_work(icpu);
+
+ down_write(&icpu->enable_sem);
+ icpu->ipolicy = NULL;
+ up_write(&icpu->enable_sem);
}
-
- mutex_unlock(&gov_lock);
}
-static void cpufreq_governor_interactive_limits(struct cpufreq_policy *policy)
+void cpufreq_interactive_limits(struct cpufreq_policy *policy)
{
- unsigned int j;
- struct cpufreq_interactive_cpuinfo *pcpu;
+ struct interactive_cpu *icpu;
+ unsigned int cpu;
unsigned long flags;
- if (policy->max < policy->cur)
- __cpufreq_driver_target(policy,
- policy->max, CPUFREQ_RELATION_H);
- else if (policy->min > policy->cur)
- __cpufreq_driver_target(policy,
- policy->min, CPUFREQ_RELATION_L);
- for_each_cpu(j, policy->cpus) {
- pcpu = &per_cpu(cpuinfo, j);
+ cpufreq_policy_apply_limits(policy);
- down_read(&pcpu->enable_sem);
- if (pcpu->governor_enabled == 0) {
- up_read(&pcpu->enable_sem);
- continue;
- }
+ for_each_cpu(cpu, policy->cpus) {
+ icpu = &per_cpu(interactive_cpu, cpu);
- spin_lock_irqsave(&pcpu->target_freq_lock, flags);
- if (policy->max < pcpu->target_freq)
- pcpu->target_freq = policy->max;
- else if (policy->min > pcpu->target_freq)
- pcpu->target_freq = policy->min;
+ spin_lock_irqsave(&icpu->target_freq_lock, flags);
- spin_unlock_irqrestore(&pcpu->target_freq_lock, flags);
- up_read(&pcpu->enable_sem);
+ if (policy->max < icpu->target_freq)
+ icpu->target_freq = policy->max;
+ else if (policy->min > icpu->target_freq)
+ icpu->target_freq = policy->min;
+
+ spin_unlock_irqrestore(&icpu->target_freq_lock, flags);
}
}
-static struct cpufreq_governor cpufreq_gov_interactive = {
- .name = "interactive",
- .init = cpufreq_governor_interactive_init,
- .exit = cpufreq_governor_interactive_exit,
- .start = cpufreq_governor_interactive_start,
- .stop = cpufreq_governor_interactive_stop,
- .limits = cpufreq_governor_interactive_limits,
- .max_transition_latency = 10000000,
- .owner = THIS_MODULE,
+static struct interactive_governor interactive_gov = {
+ .gov = {
+ .name = "interactive",
+ .max_transition_latency = TRANSITION_LATENCY_LIMIT,
+ .owner = THIS_MODULE,
+ .init = cpufreq_interactive_init,
+ .exit = cpufreq_interactive_exit,
+ .start = cpufreq_interactive_start,
+ .stop = cpufreq_interactive_stop,
+ .limits = cpufreq_interactive_limits,
+ }
};
static void cpufreq_interactive_nop_timer(unsigned long data)
{
+ /*
+ * The purpose of slack-timer is to wake up the CPU from IDLE, in order
+ * to decrease its frequency if it is not set to minimum already.
+ *
+ * This is important for platforms where CPU with higher frequencies
+ * consume higher power even at IDLE.
+ */
}
-static int __init cpufreq_interactive_init(void)
+static int __init cpufreq_interactive_gov_init(void)
{
- unsigned int i;
- struct cpufreq_interactive_cpuinfo *pcpu;
- struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
+ struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
+ struct interactive_cpu *icpu;
+ unsigned int cpu;
- /* Initalize per-cpu timers */
- for_each_possible_cpu(i) {
- pcpu = &per_cpu(cpuinfo, i);
- init_timer_pinned_deferrable(&pcpu->cpu_timer);
- pcpu->cpu_timer.function = cpufreq_interactive_timer;
- pcpu->cpu_timer.data = i;
- init_timer(&pcpu->cpu_slack_timer);
- pcpu->cpu_slack_timer.function = cpufreq_interactive_nop_timer;
- spin_lock_init(&pcpu->load_lock);
- spin_lock_init(&pcpu->target_freq_lock);
- init_rwsem(&pcpu->enable_sem);
+ for_each_possible_cpu(cpu) {
+ icpu = &per_cpu(interactive_cpu, cpu);
+
+ init_irq_work(&icpu->irq_work, irq_work);
+ spin_lock_init(&icpu->load_lock);
+ spin_lock_init(&icpu->target_freq_lock);
+ init_rwsem(&icpu->enable_sem);
+
+ /* Initialize per-cpu slack-timer */
+ init_timer_pinned(&icpu->slack_timer);
+ icpu->slack_timer.function = cpufreq_interactive_nop_timer;
}
spin_lock_init(&speedchange_cpumask_lock);
- mutex_init(&gov_lock);
- speedchange_task =
- kthread_create(cpufreq_interactive_speedchange_task, NULL,
- "cfinteractive");
+ speedchange_task = kthread_create(cpufreq_interactive_speedchange_task,
+ NULL, "cfinteractive");
if (IS_ERR(speedchange_task))
return PTR_ERR(speedchange_task);
sched_setscheduler_nocheck(speedchange_task, SCHED_FIFO, ¶m);
get_task_struct(speedchange_task);
- /* NB: wake up so the thread does not look hung to the freezer */
+ /* wake up so the thread does not look hung to the freezer */
wake_up_process(speedchange_task);
- return cpufreq_register_governor(&cpufreq_gov_interactive);
+ return cpufreq_register_governor(CPU_FREQ_GOV_INTERACTIVE);
}
#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE
struct cpufreq_governor *cpufreq_default_governor(void)
{
- return &cpufreq_gov_interactive;
+ return CPU_FREQ_GOV_INTERACTIVE;
}
-fs_initcall(cpufreq_interactive_init);
+
+fs_initcall(cpufreq_interactive_gov_init);
#else
-module_init(cpufreq_interactive_init);
+module_init(cpufreq_interactive_gov_init);
#endif
-static void __exit cpufreq_interactive_exit(void)
+static void __exit cpufreq_interactive_gov_exit(void)
{
- cpufreq_unregister_governor(&cpufreq_gov_interactive);
+ cpufreq_unregister_governor(CPU_FREQ_GOV_INTERACTIVE);
kthread_stop(speedchange_task);
put_task_struct(speedchange_task);
}
-
-module_exit(cpufreq_interactive_exit);
+module_exit(cpufreq_interactive_gov_exit);
MODULE_AUTHOR("Mike Chan <mike@android.com>");
-MODULE_DESCRIPTION("'cpufreq_interactive' - A cpufreq governor for "
- "Latency sensitive workloads");
+MODULE_DESCRIPTION("'cpufreq_interactive' - A dynamic cpufreq governor for Latency sensitive workloads");
MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/cpufreq_performance.c b/drivers/cpufreq/cpufreq_performance.c
index dafb679..399428e 100644
--- a/drivers/cpufreq/cpufreq_performance.c
+++ b/drivers/cpufreq/cpufreq_performance.c
@@ -22,7 +22,10 @@
__cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H);
}
-static struct cpufreq_governor cpufreq_gov_performance = {
+#ifdef CONFIG_CPU_FREQ_GOV_PERFORMANCE_MODULE
+static
+#endif
+struct cpufreq_governor cpufreq_gov_performance = {
.name = "performance",
.owner = THIS_MODULE,
.limits = cpufreq_gov_performance_limits,
diff --git a/drivers/cpufreq/cpufreq_powersave.c b/drivers/cpufreq/cpufreq_powersave.c
index 78a6510..5daa500 100644
--- a/drivers/cpufreq/cpufreq_powersave.c
+++ b/drivers/cpufreq/cpufreq_powersave.c
@@ -22,7 +22,10 @@
__cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L);
}
-static struct cpufreq_governor cpufreq_gov_powersave = {
+#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE
+static
+#endif
+struct cpufreq_governor cpufreq_gov_powersave = {
.name = "powersave",
.limits = cpufreq_gov_powersave_limits,
.owner = THIS_MODULE,
diff --git a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c
index bd897e3..765166d 100644
--- a/drivers/cpufreq/cpufreq_userspace.c
+++ b/drivers/cpufreq/cpufreq_userspace.c
@@ -118,7 +118,10 @@
mutex_unlock(&userspace_mutex);
}
-static struct cpufreq_governor cpufreq_gov_userspace = {
+#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE
+static
+#endif
+struct cpufreq_governor cpufreq_gov_userspace = {
.name = "userspace",
.init = cpufreq_userspace_policy_init,
.exit = cpufreq_userspace_policy_exit,
diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c
index d3ffde8..a84724e 100644
--- a/drivers/cpufreq/powernv-cpufreq.c
+++ b/drivers/cpufreq/powernv-cpufreq.c
@@ -647,8 +647,14 @@
if (unlikely(rebooting) && new_index != get_nominal_index())
return 0;
- if (!throttled)
+ if (!throttled) {
+ /* we don't want to be preempted while
+ * checking if the CPU frequency has been throttled
+ */
+ preempt_disable();
powernv_cpufreq_throttle_check(NULL);
+ preempt_enable();
+ }
cur_msec = jiffies_to_msecs(get_jiffies_64());
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
index 3ba81b1..cb3c48a 100644
--- a/drivers/cpuidle/Makefile
+++ b/drivers/cpuidle/Makefile
@@ -27,3 +27,4 @@
# POWERPC drivers
obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o
obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o
+obj-$(CONFIG_MSM_PM) += lpm-levels.o lpm-levels-of.o
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index c73207a..78ab946 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -192,7 +192,7 @@
}
/* Take note of the planned idle state. */
- sched_idle_set_state(target_state);
+ sched_idle_set_state(target_state, index);
trace_cpu_idle_rcuidle(index, dev->cpu);
time_start = ns_to_ktime(local_clock());
@@ -205,7 +205,7 @@
trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu);
/* The cpu is no longer idle or about to enter idle. */
- sched_idle_set_state(NULL);
+ sched_idle_set_state(NULL, -1);
if (broadcast) {
if (WARN_ON_ONCE(!irqs_disabled()))
diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c
new file mode 100644
index 0000000..2404e17
--- /dev/null
+++ b/drivers/cpuidle/lpm-levels-of.c
@@ -0,0 +1,903 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/moduleparam.h>
+#include "lpm-levels.h"
+
+bool use_psci;
+enum lpm_type {
+ IDLE = 0,
+ SUSPEND,
+ LPM_TYPE_NR
+};
+
+struct lpm_type_str {
+ enum lpm_type type;
+ char *str;
+};
+
+static const struct lpm_type_str lpm_types[] = {
+ {IDLE, "idle_enabled"},
+ {SUSPEND, "suspend_enabled"},
+};
+
+static DEFINE_PER_CPU(uint32_t *, max_residency);
+static DEFINE_PER_CPU(uint32_t *, min_residency);
+static struct lpm_level_avail *cpu_level_available[NR_CPUS];
+static struct platform_device *lpm_pdev;
+
+static void *get_enabled_ptr(struct kobj_attribute *attr,
+ struct lpm_level_avail *avail)
+{
+ void *arg = NULL;
+
+ if (!strcmp(attr->attr.name, lpm_types[IDLE].str))
+ arg = (void *) &avail->idle_enabled;
+ else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str))
+ arg = (void *) &avail->suspend_enabled;
+
+ return arg;
+}
+
+static struct lpm_level_avail *get_avail_ptr(struct kobject *kobj,
+ struct kobj_attribute *attr)
+{
+ struct lpm_level_avail *avail = NULL;
+
+ if (!strcmp(attr->attr.name, lpm_types[IDLE].str))
+ avail = container_of(attr, struct lpm_level_avail,
+ idle_enabled_attr);
+ else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str))
+ avail = container_of(attr, struct lpm_level_avail,
+ suspend_enabled_attr);
+
+ return avail;
+}
+
+static void set_optimum_cpu_residency(struct lpm_cpu *cpu, int cpu_id,
+ bool probe_time)
+{
+ int i, j;
+ bool mode_avail;
+ uint32_t *maximum_residency = per_cpu(max_residency, cpu_id);
+ uint32_t *minimum_residency = per_cpu(min_residency, cpu_id);
+
+ for (i = 0; i < cpu->nlevels; i++) {
+ struct power_params *pwr = &cpu->levels[i].pwr;
+
+ mode_avail = probe_time ||
+ lpm_cpu_mode_allow(cpu_id, i, true);
+
+ if (!mode_avail) {
+ maximum_residency[i] = 0;
+ minimum_residency[i] = 0;
+ continue;
+ }
+
+ maximum_residency[i] = ~0;
+ for (j = i + 1; j < cpu->nlevels; j++) {
+ mode_avail = probe_time ||
+ lpm_cpu_mode_allow(cpu_id, j, true);
+
+ if (mode_avail &&
+ (maximum_residency[i] > pwr->residencies[j]) &&
+ (pwr->residencies[j] != 0))
+ maximum_residency[i] = pwr->residencies[j];
+ }
+
+ minimum_residency[i] = pwr->time_overhead_us;
+ for (j = i-1; j >= 0; j--) {
+ if (probe_time || lpm_cpu_mode_allow(cpu_id, j, true)) {
+ minimum_residency[i] = maximum_residency[j] + 1;
+ break;
+ }
+ }
+ }
+}
+
+static void set_optimum_cluster_residency(struct lpm_cluster *cluster,
+ bool probe_time)
+{
+ int i, j;
+ bool mode_avail;
+
+ for (i = 0; i < cluster->nlevels; i++) {
+ struct power_params *pwr = &cluster->levels[i].pwr;
+
+ mode_avail = probe_time ||
+ lpm_cluster_mode_allow(cluster, i,
+ true);
+
+ if (!mode_avail) {
+ pwr->max_residency = 0;
+ pwr->min_residency = 0;
+ continue;
+ }
+
+ pwr->max_residency = ~0;
+ for (j = i+1; j < cluster->nlevels; j++) {
+ mode_avail = probe_time ||
+ lpm_cluster_mode_allow(cluster, j,
+ true);
+ if (mode_avail &&
+ (pwr->max_residency > pwr->residencies[j]) &&
+ (pwr->residencies[j] != 0))
+ pwr->max_residency = pwr->residencies[j];
+ }
+
+ pwr->min_residency = pwr->time_overhead_us;
+ for (j = i-1; j >= 0; j--) {
+ if (probe_time ||
+ lpm_cluster_mode_allow(cluster, j, true)) {
+ pwr->min_residency =
+ cluster->levels[j].pwr.max_residency + 1;
+ break;
+ }
+ }
+ }
+}
+
+uint32_t *get_per_cpu_max_residency(int cpu)
+{
+ return per_cpu(max_residency, cpu);
+}
+
+uint32_t *get_per_cpu_min_residency(int cpu)
+{
+ return per_cpu(min_residency, cpu);
+}
+ssize_t lpm_enable_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ int ret = 0;
+ struct kernel_param kp;
+
+ kp.arg = get_enabled_ptr(attr, get_avail_ptr(kobj, attr));
+ ret = param_get_bool(buf, &kp);
+ if (ret > 0) {
+ strlcat(buf, "\n", PAGE_SIZE);
+ ret++;
+ }
+
+ return ret;
+}
+
+ssize_t lpm_enable_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ int ret = 0;
+ struct kernel_param kp;
+ struct lpm_level_avail *avail;
+
+ avail = get_avail_ptr(kobj, attr);
+ if (WARN_ON(!avail))
+ return -EINVAL;
+ kp.arg = get_enabled_ptr(attr, avail);
+ ret = param_set_bool(buf, &kp);
+
+ if (avail->cpu_node)
+ set_optimum_cpu_residency(avail->data, avail->idx, false);
+ else
+ set_optimum_cluster_residency(avail->data, false);
+
+ return ret ? ret : len;
+}
+
+static int create_lvl_avail_nodes(const char *name,
+ struct kobject *parent, struct lpm_level_avail *avail,
+ void *data, int index, bool cpu_node)
+{
+ struct attribute_group *attr_group = NULL;
+ struct attribute **attr = NULL;
+ struct kobject *kobj = NULL;
+ int ret = 0;
+
+ kobj = kobject_create_and_add(name, parent);
+ if (!kobj)
+ return -ENOMEM;
+
+ attr_group = devm_kzalloc(&lpm_pdev->dev, sizeof(*attr_group),
+ GFP_KERNEL);
+ if (!attr_group) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ attr = devm_kzalloc(&lpm_pdev->dev,
+ sizeof(*attr) * (LPM_TYPE_NR + 1), GFP_KERNEL);
+ if (!attr) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ sysfs_attr_init(&avail->idle_enabled_attr.attr);
+ avail->idle_enabled_attr.attr.name = lpm_types[IDLE].str;
+ avail->idle_enabled_attr.attr.mode = 0644;
+ avail->idle_enabled_attr.show = lpm_enable_show;
+ avail->idle_enabled_attr.store = lpm_enable_store;
+
+ sysfs_attr_init(&avail->suspend_enabled_attr.attr);
+ avail->suspend_enabled_attr.attr.name = lpm_types[SUSPEND].str;
+ avail->suspend_enabled_attr.attr.mode = 0644;
+ avail->suspend_enabled_attr.show = lpm_enable_show;
+ avail->suspend_enabled_attr.store = lpm_enable_store;
+
+ attr[0] = &avail->idle_enabled_attr.attr;
+ attr[1] = &avail->suspend_enabled_attr.attr;
+ attr[2] = NULL;
+ attr_group->attrs = attr;
+
+ ret = sysfs_create_group(kobj, attr_group);
+ if (ret) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ avail->idle_enabled = true;
+ avail->suspend_enabled = true;
+ avail->kobj = kobj;
+ avail->data = data;
+ avail->idx = index;
+ avail->cpu_node = cpu_node;
+
+ return ret;
+
+failed:
+ kobject_put(kobj);
+ return ret;
+}
+
+static int create_cpu_lvl_nodes(struct lpm_cluster *p, struct kobject *parent)
+{
+ int cpu;
+ int i, cpu_idx;
+ struct kobject **cpu_kobj = NULL;
+ struct lpm_level_avail *level_list = NULL;
+ char cpu_name[20] = {0};
+ int ret = 0;
+
+ cpu_kobj = devm_kzalloc(&lpm_pdev->dev, sizeof(*cpu_kobj) *
+ cpumask_weight(&p->child_cpus), GFP_KERNEL);
+ if (!cpu_kobj)
+ return -ENOMEM;
+
+ cpu_idx = 0;
+ for_each_cpu(cpu, &p->child_cpus) {
+ snprintf(cpu_name, sizeof(cpu_name), "cpu%d", cpu);
+ cpu_kobj[cpu_idx] = kobject_create_and_add(cpu_name, parent);
+ if (!cpu_kobj[cpu_idx]) {
+ ret = -ENOMEM;
+ goto release_kobj;
+ }
+
+ level_list = devm_kzalloc(&lpm_pdev->dev,
+ p->cpu->nlevels * sizeof(*level_list),
+ GFP_KERNEL);
+ if (!level_list) {
+ ret = -ENOMEM;
+ goto release_kobj;
+ }
+
+ /*
+ * Skip enable/disable for WFI. cpuidle expects WFI to be
+ * available at all times.
+ */
+ for (i = 1; i < p->cpu->nlevels; i++) {
+
+ ret = create_lvl_avail_nodes(p->cpu->levels[i].name,
+ cpu_kobj[cpu_idx], &level_list[i],
+ (void *)p->cpu, cpu, true);
+ if (ret)
+ goto release_kobj;
+ }
+
+ cpu_level_available[cpu] = level_list;
+ cpu_idx++;
+ }
+
+ return ret;
+
+release_kobj:
+ for (i = 0; i < cpumask_weight(&p->child_cpus); i++)
+ kobject_put(cpu_kobj[i]);
+
+ return ret;
+}
+
+int create_cluster_lvl_nodes(struct lpm_cluster *p, struct kobject *kobj)
+{
+ int ret = 0;
+ struct lpm_cluster *child = NULL;
+ int i;
+ struct kobject *cluster_kobj = NULL;
+
+ if (!p)
+ return -ENODEV;
+
+ cluster_kobj = kobject_create_and_add(p->cluster_name, kobj);
+ if (!cluster_kobj)
+ return -ENOMEM;
+
+ for (i = 0; i < p->nlevels; i++) {
+ ret = create_lvl_avail_nodes(p->levels[i].level_name,
+ cluster_kobj, &p->levels[i].available,
+ (void *)p, 0, false);
+ if (ret)
+ return ret;
+ }
+
+ list_for_each_entry(child, &p->child, list) {
+ ret = create_cluster_lvl_nodes(child, cluster_kobj);
+ if (ret)
+ return ret;
+ }
+
+ if (p->cpu) {
+ ret = create_cpu_lvl_nodes(p, cluster_kobj);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+bool lpm_cpu_mode_allow(unsigned int cpu,
+ unsigned int index, bool from_idle)
+{
+ struct lpm_level_avail *avail = cpu_level_available[cpu];
+
+ if (!lpm_pdev || !avail)
+ return !from_idle;
+
+ return !!(from_idle ? avail[index].idle_enabled :
+ avail[index].suspend_enabled);
+}
+
+bool lpm_cluster_mode_allow(struct lpm_cluster *cluster,
+ unsigned int mode, bool from_idle)
+{
+ struct lpm_level_avail *avail = &cluster->levels[mode].available;
+
+ if (!lpm_pdev || !avail)
+ return false;
+
+ return !!(from_idle ? avail->idle_enabled :
+ avail->suspend_enabled);
+}
+
+static int parse_cluster_params(struct device_node *node,
+ struct lpm_cluster *c)
+{
+ char *key;
+ int ret;
+
+ key = "label";
+ ret = of_property_read_string(node, key, &c->cluster_name);
+ if (ret) {
+ pr_err("%s(): Cannot read required param %s\n", __func__, key);
+ return ret;
+ }
+
+ if (use_psci) {
+ key = "qcom,psci-mode-shift";
+ ret = of_property_read_u32(node, key,
+ &c->psci_mode_shift);
+ if (ret) {
+ pr_err("%s(): Failed to read param: %s\n",
+ __func__, key);
+ return ret;
+ }
+
+ key = "qcom,psci-mode-mask";
+ ret = of_property_read_u32(node, key,
+ &c->psci_mode_mask);
+ if (ret) {
+ pr_err("%s(): Failed to read param: %s\n",
+ __func__, key);
+ return ret;
+ }
+
+ /* Set ndevice to 1 as default */
+ c->ndevices = 1;
+
+ } else
+ pr_warn("Target supports PSCI only\n");
+ return 0;
+}
+
+static int parse_power_params(struct device_node *node,
+ struct power_params *pwr)
+{
+ char *key;
+ int ret;
+
+ key = "qcom,latency-us";
+ ret = of_property_read_u32(node, key, &pwr->latency_us);
+ if (ret)
+ goto fail;
+
+ key = "qcom,ss-power";
+ ret = of_property_read_u32(node, key, &pwr->ss_power);
+ if (ret)
+ goto fail;
+
+ key = "qcom,energy-overhead";
+ ret = of_property_read_u32(node, key, &pwr->energy_overhead);
+ if (ret)
+ goto fail;
+
+ key = "qcom,time-overhead";
+ ret = of_property_read_u32(node, key, &pwr->time_overhead_us);
+ if (ret)
+ goto fail;
+
+fail:
+ if (ret)
+ pr_err("%s(): %s Error reading %s\n", __func__, node->name,
+ key);
+ return ret;
+}
+
+static int parse_cluster_level(struct device_node *node,
+ struct lpm_cluster *cluster)
+{
+ struct lpm_cluster_level *level = &cluster->levels[cluster->nlevels];
+ int ret = -ENOMEM;
+ char *key;
+
+ key = "label";
+ ret = of_property_read_string(node, key, &level->level_name);
+ if (ret)
+ goto failed;
+
+ if (use_psci) {
+ char *k = "qcom,psci-mode";
+
+ ret = of_property_read_u32(node, k, &level->psci_id);
+ if (ret)
+ goto failed;
+
+ level->is_reset = of_property_read_bool(node, "qcom,is-reset");
+ } else
+ pr_warn("Build supports PSCI targets only");
+
+ key = "label";
+ ret = of_property_read_string(node, key, &level->level_name);
+ if (ret)
+ goto failed;
+
+ if (cluster->nlevels != cluster->default_level) {
+ key = "min child idx";
+ ret = of_property_read_u32(node, "qcom,min-child-idx",
+ &level->min_child_level);
+ if (ret)
+ goto failed;
+
+ if (cluster->min_child_level > level->min_child_level)
+ cluster->min_child_level = level->min_child_level;
+ }
+
+ level->notify_rpm = of_property_read_bool(node, "qcom,notify-rpm");
+ level->disable_dynamic_routing = of_property_read_bool(node,
+ "qcom,disable-dynamic-int-routing");
+ level->last_core_only = of_property_read_bool(node,
+ "qcom,last-core-only");
+
+ key = "parse_power_params";
+ ret = parse_power_params(node, &level->pwr);
+ if (ret)
+ goto failed;
+
+ key = "qcom,reset-level";
+ ret = of_property_read_u32(node, key, &level->reset_level);
+ if (ret == -EINVAL)
+ level->reset_level = LPM_RESET_LVL_NONE;
+ else if (ret)
+ goto failed;
+
+ cluster->nlevels++;
+ return 0;
+failed:
+ pr_err("Failed %s() key = %s ret = %d\n", __func__, key, ret);
+ kfree(level->mode);
+ level->mode = NULL;
+ return ret;
+}
+
+static int parse_cpu_mode(struct device_node *n, struct lpm_cpu_level *l)
+{
+ char *key;
+ int ret;
+
+ key = "qcom,spm-cpu-mode";
+ ret = of_property_read_string(n, key, &l->name);
+ if (ret) {
+ pr_err("Failed %s %d\n", n->name, __LINE__);
+ return ret;
+ }
+
+ if (use_psci) {
+ key = "qcom,psci-cpu-mode";
+
+ ret = of_property_read_u32(n, key, &l->psci_id);
+ if (ret) {
+ pr_err("Failed reading %s on device %s\n", key,
+ n->name);
+ return ret;
+ }
+ key = "qcom,hyp-psci";
+
+ l->hyp_psci = of_property_read_bool(n, key);
+ } else
+ pr_warn("Build supports PSCI targets only");
+ return 0;
+
+}
+
+static int get_cpumask_for_node(struct device_node *node, struct cpumask *mask)
+{
+ struct device_node *cpu_node;
+ int cpu;
+ int idx = 0;
+
+ cpu_node = of_parse_phandle(node, "qcom,cpu", idx++);
+ if (!cpu_node) {
+ pr_info("%s: No CPU phandle, assuming single cluster\n",
+ node->full_name);
+ /*
+ * Not all targets have the cpu node populated in the device
+ * tree. If cpu node is not populated assume all possible
+ * nodes belong to this cluster
+ */
+ cpumask_copy(mask, cpu_possible_mask);
+ return 0;
+ }
+
+ while (cpu_node) {
+ for_each_possible_cpu(cpu) {
+ if (of_get_cpu_node(cpu, NULL) == cpu_node) {
+ cpumask_set_cpu(cpu, mask);
+ break;
+ }
+ }
+ cpu_node = of_parse_phandle(node, "qcom,cpu", idx++);
+ }
+
+ return 0;
+}
+
+static int calculate_residency(struct power_params *base_pwr,
+ struct power_params *next_pwr)
+{
+ int32_t residency = (int32_t)(next_pwr->energy_overhead -
+ base_pwr->energy_overhead) -
+ ((int32_t)(next_pwr->ss_power * next_pwr->time_overhead_us)
+ - (int32_t)(base_pwr->ss_power * base_pwr->time_overhead_us));
+
+ residency /= (int32_t)(base_pwr->ss_power - next_pwr->ss_power);
+
+ if (residency < 0) {
+ pr_err("%s: residency < 0 for LPM\n",
+ __func__);
+ return next_pwr->time_overhead_us;
+ }
+
+ return residency < next_pwr->time_overhead_us ?
+ next_pwr->time_overhead_us : residency;
+}
+
+static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c)
+{
+ struct device_node *n;
+ int ret = -ENOMEM;
+ int i, j;
+ char *key;
+
+ c->cpu = devm_kzalloc(&lpm_pdev->dev, sizeof(*c->cpu), GFP_KERNEL);
+ if (!c->cpu)
+ return ret;
+
+ c->cpu->parent = c;
+ if (use_psci) {
+
+ key = "qcom,psci-mode-shift";
+
+ ret = of_property_read_u32(node, key, &c->cpu->psci_mode_shift);
+ if (ret) {
+ pr_err("Failed reading %s on device %s\n", key,
+ node->name);
+ return ret;
+ }
+ key = "qcom,psci-mode-mask";
+
+ ret = of_property_read_u32(node, key, &c->cpu->psci_mode_mask);
+ if (ret) {
+ pr_err("Failed reading %s on device %s\n", key,
+ node->name);
+ return ret;
+ }
+ }
+ for_each_child_of_node(node, n) {
+ struct lpm_cpu_level *l = &c->cpu->levels[c->cpu->nlevels];
+
+ c->cpu->nlevels++;
+
+ ret = parse_cpu_mode(n, l);
+ if (ret < 0) {
+ pr_info("Failed %s\n", l->name);
+ goto failed;
+ }
+
+ ret = parse_power_params(n, &l->pwr);
+ if (ret)
+ goto failed;
+
+ key = "qcom,use-broadcast-timer";
+ l->use_bc_timer = of_property_read_bool(n, key);
+
+ l->is_reset = of_property_read_bool(n, "qcom,is-reset");
+
+ key = "qcom,jtag-save-restore";
+ l->jtag_save_restore = of_property_read_bool(n, key);
+
+ key = "qcom,reset-level";
+ ret = of_property_read_u32(n, key, &l->reset_level);
+ if (ret == -EINVAL)
+ l->reset_level = LPM_RESET_LVL_NONE;
+ else if (ret)
+ goto failed;
+ }
+ for (i = 0; i < c->cpu->nlevels; i++) {
+ for (j = 0; j < c->cpu->nlevels; j++) {
+ if (i >= j) {
+ c->cpu->levels[i].pwr.residencies[j] = 0;
+ continue;
+ }
+
+ c->cpu->levels[i].pwr.residencies[j] =
+ calculate_residency(&c->cpu->levels[i].pwr,
+ &c->cpu->levels[j].pwr);
+
+ pr_err("%s: idx %d %u\n", __func__, j,
+ c->cpu->levels[i].pwr.residencies[j]);
+ }
+ }
+
+ return 0;
+failed:
+ for (i = 0; i < c->cpu->nlevels; i++) {
+ kfree(c->cpu->levels[i].name);
+ c->cpu->levels[i].name = NULL;
+ }
+ kfree(c->cpu);
+ c->cpu = NULL;
+ pr_err("%s(): Failed with error code:%d\n", __func__, ret);
+ return ret;
+}
+
+void free_cluster_node(struct lpm_cluster *cluster)
+{
+ struct list_head *list;
+ int i;
+
+ list_for_each(list, &cluster->child) {
+ struct lpm_cluster *n;
+
+ n = list_entry(list, typeof(*n), list);
+ list_del(list);
+ free_cluster_node(n);
+ };
+
+ if (cluster->cpu) {
+ for (i = 0; i < cluster->cpu->nlevels; i++) {
+ kfree(cluster->cpu->levels[i].name);
+ cluster->cpu->levels[i].name = NULL;
+ }
+ }
+ for (i = 0; i < cluster->nlevels; i++) {
+ kfree(cluster->levels[i].mode);
+ cluster->levels[i].mode = NULL;
+ }
+ kfree(cluster->cpu);
+ kfree(cluster->name);
+ kfree(cluster->lpm_dev);
+ cluster->cpu = NULL;
+ cluster->name = NULL;
+ cluster->lpm_dev = NULL;
+ cluster->ndevices = 0;
+}
+
+/*
+ * TODO:
+ * Expects a CPU or a cluster only. This ensures that affinity
+ * level of a cluster is consistent with reference to its
+ * child nodes.
+ */
+struct lpm_cluster *parse_cluster(struct device_node *node,
+ struct lpm_cluster *parent)
+{
+ struct lpm_cluster *c;
+ struct device_node *n;
+ char *key;
+ int ret = 0;
+ int i, j;
+
+ c = devm_kzalloc(&lpm_pdev->dev, sizeof(*c), GFP_KERNEL);
+ if (!c)
+ return ERR_PTR(-ENOMEM);
+
+ ret = parse_cluster_params(node, c);
+
+ if (ret)
+ goto failed_parse_params;
+
+ INIT_LIST_HEAD(&c->child);
+ c->parent = parent;
+ spin_lock_init(&c->sync_lock);
+ c->min_child_level = NR_LPM_LEVELS;
+
+ for_each_child_of_node(node, n) {
+
+ if (!n->name)
+ continue;
+ key = "qcom,pm-cluster-level";
+ if (!of_node_cmp(n->name, key)) {
+ WARN_ON(!use_psci && c->no_saw_devices);
+ if (parse_cluster_level(n, c))
+ goto failed_parse_cluster;
+ continue;
+ }
+
+ key = "qcom,pm-cluster";
+ if (!of_node_cmp(n->name, key)) {
+ struct lpm_cluster *child;
+
+ WARN_ON(!use_psci && c->no_saw_devices);
+ child = parse_cluster(n, c);
+ if (!child)
+ goto failed_parse_cluster;
+
+ list_add(&child->list, &c->child);
+ cpumask_or(&c->child_cpus, &c->child_cpus,
+ &child->child_cpus);
+ c->aff_level = child->aff_level + 1;
+ continue;
+ }
+
+ key = "qcom,pm-cpu";
+ if (!of_node_cmp(n->name, key)) {
+ /*
+ * Parse the the cpu node only if a pm-cpu node
+ * is available, though the mask is defined @ the
+ * cluster level
+ */
+ if (get_cpumask_for_node(node, &c->child_cpus))
+ goto failed_parse_cluster;
+
+ if (parse_cpu_levels(n, c))
+ goto failed_parse_cluster;
+
+ c->aff_level = 1;
+
+ for_each_cpu(i, &c->child_cpus) {
+ per_cpu(max_residency, i) = devm_kzalloc(
+ &lpm_pdev->dev,
+ sizeof(uint32_t) * c->cpu->nlevels,
+ GFP_KERNEL);
+ if (!per_cpu(max_residency, i))
+ return ERR_PTR(-ENOMEM);
+ per_cpu(min_residency, i) = devm_kzalloc(
+ &lpm_pdev->dev,
+ sizeof(uint32_t) * c->cpu->nlevels,
+ GFP_KERNEL);
+ if (!per_cpu(min_residency, i))
+ return ERR_PTR(-ENOMEM);
+ set_optimum_cpu_residency(c->cpu, i, true);
+ }
+ }
+ }
+
+ if (cpumask_intersects(&c->child_cpus, cpu_online_mask))
+ c->last_level = c->default_level;
+ else
+ c->last_level = c->nlevels-1;
+
+ for (i = 0; i < c->nlevels; i++) {
+ for (j = 0; j < c->nlevels; j++) {
+ if (i >= j) {
+ c->levels[i].pwr.residencies[j] = 0;
+ continue;
+ }
+ c->levels[i].pwr.residencies[j] = calculate_residency(
+ &c->levels[i].pwr, &c->levels[j].pwr);
+ }
+ }
+ set_optimum_cluster_residency(c, true);
+ return c;
+
+failed_parse_cluster:
+ pr_err("Failed parse cluster:%s\n", key);
+ if (parent)
+ list_del(&c->list);
+ free_cluster_node(c);
+failed_parse_params:
+ c->parent = NULL;
+ pr_err("Failed parse params\n");
+ kfree(c);
+ return NULL;
+}
+struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev)
+{
+ struct device_node *top = NULL;
+
+ use_psci = of_property_read_bool(pdev->dev.of_node, "qcom,use-psci");
+
+ top = of_find_node_by_name(pdev->dev.of_node, "qcom,pm-cluster");
+ if (!top) {
+ pr_err("Failed to find root node\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ lpm_pdev = pdev;
+ return parse_cluster(top, NULL);
+}
+
+void cluster_dt_walkthrough(struct lpm_cluster *cluster)
+{
+ struct list_head *list;
+ int i, j;
+ static int id;
+ char str[10] = {0};
+
+ if (!cluster)
+ return;
+
+ for (i = 0; i < id; i++)
+ snprintf(str+i, 10 - i, "\t");
+ pr_info("%d\n", __LINE__);
+
+ for (i = 0; i < cluster->nlevels; i++) {
+ struct lpm_cluster_level *l = &cluster->levels[i];
+
+ pr_info("%d ndevices:%d\n", __LINE__, cluster->ndevices);
+ for (j = 0; j < cluster->ndevices; j++)
+ pr_info("%sDevice: %p id:%p\n", str,
+ &cluster->name[j], &l->mode[i]);
+ }
+
+ if (cluster->cpu) {
+ pr_info("%d\n", __LINE__);
+ for (j = 0; j < cluster->cpu->nlevels; j++)
+ pr_info("%s\tCPU mode: %s id:%d\n", str,
+ cluster->cpu->levels[j].name,
+ cluster->cpu->levels[j].mode);
+ }
+
+ id++;
+
+ list_for_each(list, &cluster->child) {
+ struct lpm_cluster *n;
+
+ pr_info("%d\n", __LINE__);
+ n = list_entry(list, typeof(*n), list);
+ cluster_dt_walkthrough(n);
+ }
+ id--;
+}
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
new file mode 100644
index 0000000..46f233d
--- /dev/null
+++ b/drivers/cpuidle/lpm-levels.c
@@ -0,0 +1,1863 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2006-2007 Adam Belay <abelay@novell.com>
+ * Copyright (C) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/cpu.h>
+#include <linux/of.h>
+#include <linux/hrtimer.h>
+#include <linux/ktime.h>
+#include <linux/tick.h>
+#include <linux/suspend.h>
+#include <linux/pm_qos.h>
+#include <linux/of_platform.h>
+#include <linux/smp.h>
+#include <linux/remote_spinlock.h>
+#include <linux/msm_remote_spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/coresight-cti.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <linux/cpu_pm.h>
+#include <soc/qcom/spm.h>
+#include <soc/qcom/pm.h>
+#include <soc/qcom/event_timer.h>
+#include <soc/qcom/lpm-stats.h>
+#include <soc/qcom/jtag.h>
+#include <soc/qcom/system_pm.h>
+#include <asm/cputype.h>
+#include <asm/arch_timer.h>
+#include <asm/cacheflush.h>
+#include <asm/suspend.h>
+#include <asm/cpuidle.h>
+#include "lpm-levels.h"
+#include <trace/events/power.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/trace_msm_low_power.h>
+
+#define SCLK_HZ (32768)
+#define SCM_HANDOFF_LOCK_ID "S:7"
+#define PSCI_POWER_STATE(reset) (reset << 30)
+#define PSCI_AFFINITY_LEVEL(lvl) ((lvl & 0x3) << 24)
+static remote_spinlock_t scm_handoff_lock;
+
+enum {
+ MSM_LPM_LVL_DBG_SUSPEND_LIMITS = BIT(0),
+ MSM_LPM_LVL_DBG_IDLE_LIMITS = BIT(1),
+};
+
+enum debug_event {
+ CPU_ENTER,
+ CPU_EXIT,
+ CLUSTER_ENTER,
+ CLUSTER_EXIT,
+ PRE_PC_CB,
+};
+
+struct lpm_debug {
+ cycle_t time;
+ enum debug_event evt;
+ int cpu;
+ uint32_t arg1;
+ uint32_t arg2;
+ uint32_t arg3;
+ uint32_t arg4;
+};
+
+struct lpm_cluster *lpm_root_node;
+
+#define MAXSAMPLES 5
+
+static bool lpm_prediction = true;
+module_param_named(lpm_prediction, lpm_prediction, bool, 0664);
+
+static uint32_t ref_stddev = 100;
+module_param_named(ref_stddev, ref_stddev, uint, 0664);
+
+static uint32_t tmr_add = 100;
+module_param_named(tmr_add, tmr_add, uint, 0664);
+
+struct lpm_history {
+ uint32_t resi[MAXSAMPLES];
+ int mode[MAXSAMPLES];
+ int nsamp;
+ uint32_t hptr;
+ uint32_t hinvalid;
+ uint32_t htmr_wkup;
+ int64_t stime;
+};
+
+static DEFINE_PER_CPU(struct lpm_history, hist);
+
+static DEFINE_PER_CPU(struct lpm_cluster*, cpu_cluster);
+static bool suspend_in_progress;
+static struct hrtimer lpm_hrtimer;
+static struct hrtimer histtimer;
+static struct lpm_debug *lpm_debug;
+static phys_addr_t lpm_debug_phys;
+static const int num_dbg_elements = 0x100;
+
+static void cluster_unprepare(struct lpm_cluster *cluster,
+ const struct cpumask *cpu, int child_idx, bool from_idle,
+ int64_t time);
+static void cluster_prepare(struct lpm_cluster *cluster,
+ const struct cpumask *cpu, int child_idx, bool from_idle,
+ int64_t time);
+
+static bool menu_select;
+module_param_named(menu_select, menu_select, bool, 0664);
+
+static int msm_pm_sleep_time_override;
+module_param_named(sleep_time_override,
+ msm_pm_sleep_time_override, int, 0664);
+static uint64_t suspend_wake_time;
+
+static bool print_parsed_dt;
+module_param_named(print_parsed_dt, print_parsed_dt, bool, 0664);
+
+static bool sleep_disabled;
+module_param_named(sleep_disabled, sleep_disabled, bool, 0664);
+
+s32 msm_cpuidle_get_deep_idle_latency(void)
+{
+ return 10;
+}
+
+void lpm_suspend_wake_time(uint64_t wakeup_time)
+{
+ if (wakeup_time <= 0) {
+ suspend_wake_time = msm_pm_sleep_time_override;
+ return;
+ }
+
+ if (msm_pm_sleep_time_override &&
+ (msm_pm_sleep_time_override < wakeup_time))
+ suspend_wake_time = msm_pm_sleep_time_override;
+ else
+ suspend_wake_time = wakeup_time;
+}
+EXPORT_SYMBOL(lpm_suspend_wake_time);
+
+static uint32_t least_cluster_latency(struct lpm_cluster *cluster,
+ struct latency_level *lat_level)
+{
+ struct list_head *list;
+ struct lpm_cluster_level *level;
+ struct lpm_cluster *n;
+ struct power_params *pwr_params;
+ uint32_t latency = 0;
+ int i;
+
+ if (!cluster->list.next) {
+ for (i = 0; i < cluster->nlevels; i++) {
+ level = &cluster->levels[i];
+ pwr_params = &level->pwr;
+ if (lat_level->reset_level == level->reset_level) {
+ if ((latency > pwr_params->latency_us)
+ || (!latency))
+ latency = pwr_params->latency_us;
+ break;
+ }
+ }
+ } else {
+ list_for_each(list, &cluster->parent->child) {
+ n = list_entry(list, typeof(*n), list);
+ if (lat_level->level_name) {
+ if (strcmp(lat_level->level_name,
+ n->cluster_name))
+ continue;
+ }
+ for (i = 0; i < n->nlevels; i++) {
+ level = &n->levels[i];
+ pwr_params = &level->pwr;
+ if (lat_level->reset_level ==
+ level->reset_level) {
+ if ((latency > pwr_params->latency_us)
+ || (!latency))
+ latency =
+ pwr_params->latency_us;
+ break;
+ }
+ }
+ }
+ }
+ return latency;
+}
+
+static uint32_t least_cpu_latency(struct list_head *child,
+ struct latency_level *lat_level)
+{
+ struct list_head *list;
+ struct lpm_cpu_level *level;
+ struct power_params *pwr_params;
+ struct lpm_cpu *cpu;
+ struct lpm_cluster *n;
+ uint32_t latency = 0;
+ int i;
+
+ list_for_each(list, child) {
+ n = list_entry(list, typeof(*n), list);
+ if (lat_level->level_name) {
+ if (strcmp(lat_level->level_name, n->cluster_name))
+ continue;
+ }
+ cpu = n->cpu;
+ for (i = 0; i < cpu->nlevels; i++) {
+ level = &cpu->levels[i];
+ pwr_params = &level->pwr;
+ if (lat_level->reset_level == level->reset_level) {
+ if ((latency > pwr_params->latency_us)
+ || (!latency))
+ latency = pwr_params->latency_us;
+ break;
+ }
+ }
+ }
+ return latency;
+}
+
+static struct lpm_cluster *cluster_aff_match(struct lpm_cluster *cluster,
+ int affinity_level)
+{
+ struct lpm_cluster *n;
+
+ if ((cluster->aff_level == affinity_level)
+ || ((cluster->cpu) && (affinity_level == 0)))
+ return cluster;
+ else if (!cluster->cpu) {
+ n = list_entry(cluster->child.next, typeof(*n), list);
+ return cluster_aff_match(n, affinity_level);
+ } else
+ return NULL;
+}
+
+int lpm_get_latency(struct latency_level *level, uint32_t *latency)
+{
+ struct lpm_cluster *cluster;
+ uint32_t val;
+
+ if (!lpm_root_node) {
+ pr_err("%s: lpm_probe not completed\n", __func__);
+ return -EAGAIN;
+ }
+
+ if ((level->affinity_level < 0)
+ || (level->affinity_level > lpm_root_node->aff_level)
+ || (level->reset_level < LPM_RESET_LVL_RET)
+ || (level->reset_level > LPM_RESET_LVL_PC)
+ || !latency)
+ return -EINVAL;
+
+ cluster = cluster_aff_match(lpm_root_node, level->affinity_level);
+ if (!cluster) {
+ pr_err("%s:No matching cluster found for affinity_level:%d\n",
+ __func__, level->affinity_level);
+ return -EINVAL;
+ }
+
+ if (level->affinity_level == 0)
+ val = least_cpu_latency(&cluster->parent->child, level);
+ else
+ val = least_cluster_latency(cluster, level);
+
+ if (!val) {
+ pr_err("%s:No mode with affinity_level:%d reset_level:%d\n",
+ __func__, level->affinity_level, level->reset_level);
+ return -EINVAL;
+ }
+
+ *latency = val;
+
+ return 0;
+}
+EXPORT_SYMBOL(lpm_get_latency);
+
+static void update_debug_pc_event(enum debug_event event, uint32_t arg1,
+ uint32_t arg2, uint32_t arg3, uint32_t arg4)
+{
+ struct lpm_debug *dbg;
+ int idx;
+ static DEFINE_SPINLOCK(debug_lock);
+ static int pc_event_index;
+
+ if (!lpm_debug)
+ return;
+
+ spin_lock(&debug_lock);
+ idx = pc_event_index++;
+ dbg = &lpm_debug[idx & (num_dbg_elements - 1)];
+
+ dbg->evt = event;
+ dbg->time = arch_counter_get_cntvct();
+ dbg->cpu = raw_smp_processor_id();
+ dbg->arg1 = arg1;
+ dbg->arg2 = arg2;
+ dbg->arg3 = arg3;
+ dbg->arg4 = arg4;
+ spin_unlock(&debug_lock);
+}
+
+static int lpm_cpu_callback(struct notifier_block *cpu_nb,
+ unsigned long action, void *hcpu)
+{
+ unsigned long cpu = (unsigned long) hcpu;
+ struct lpm_cluster *cluster = per_cpu(cpu_cluster, (unsigned int) cpu);
+
+ switch (action & ~CPU_TASKS_FROZEN) {
+ case CPU_DYING:
+ cluster_prepare(cluster, get_cpu_mask((unsigned int) cpu),
+ NR_LPM_LEVELS, false, 0);
+ break;
+ case CPU_STARTING:
+ cluster_unprepare(cluster, get_cpu_mask((unsigned int) cpu),
+ NR_LPM_LEVELS, false, 0);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static enum hrtimer_restart lpm_hrtimer_cb(struct hrtimer *h)
+{
+ return HRTIMER_NORESTART;
+}
+
+static void histtimer_cancel(void)
+{
+ hrtimer_try_to_cancel(&histtimer);
+}
+
+static enum hrtimer_restart histtimer_fn(struct hrtimer *h)
+{
+ int cpu = raw_smp_processor_id();
+ struct lpm_history *history = &per_cpu(hist, cpu);
+
+ history->hinvalid = 1;
+ return HRTIMER_NORESTART;
+}
+
+static void histtimer_start(uint32_t time_us)
+{
+ uint64_t time_ns = time_us * NSEC_PER_USEC;
+ ktime_t hist_ktime = ns_to_ktime(time_ns);
+
+ histtimer.function = histtimer_fn;
+ hrtimer_start(&histtimer, hist_ktime, HRTIMER_MODE_REL_PINNED);
+}
+
+static void cluster_timer_init(struct lpm_cluster *cluster)
+{
+ struct list_head *list;
+
+ if (!cluster)
+ return;
+
+ hrtimer_init(&cluster->histtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+
+ list_for_each(list, &cluster->child) {
+ struct lpm_cluster *n;
+
+ n = list_entry(list, typeof(*n), list);
+ cluster_timer_init(n);
+ }
+}
+
+static void clusttimer_cancel(void)
+{
+ int cpu = raw_smp_processor_id();
+ struct lpm_cluster *cluster = per_cpu(cpu_cluster, cpu);
+
+ hrtimer_try_to_cancel(&cluster->histtimer);
+ hrtimer_try_to_cancel(&cluster->parent->histtimer);
+}
+
+static enum hrtimer_restart clusttimer_fn(struct hrtimer *h)
+{
+ struct lpm_cluster *cluster = container_of(h,
+ struct lpm_cluster, histtimer);
+
+ cluster->history.hinvalid = 1;
+ return HRTIMER_NORESTART;
+}
+
+static void clusttimer_start(struct lpm_cluster *cluster, uint32_t time_us)
+{
+ uint64_t time_ns = time_us * NSEC_PER_USEC;
+ ktime_t clust_ktime = ns_to_ktime(time_ns);
+
+ cluster->histtimer.function = clusttimer_fn;
+ hrtimer_start(&cluster->histtimer, clust_ktime,
+ HRTIMER_MODE_REL_PINNED);
+}
+
+static void msm_pm_set_timer(uint32_t modified_time_us)
+{
+ u64 modified_time_ns = modified_time_us * NSEC_PER_USEC;
+ ktime_t modified_ktime = ns_to_ktime(modified_time_ns);
+
+ lpm_hrtimer.function = lpm_hrtimer_cb;
+ hrtimer_start(&lpm_hrtimer, modified_ktime, HRTIMER_MODE_REL_PINNED);
+}
+
+static int set_device_mode(struct lpm_cluster *cluster, int ndevice,
+ struct lpm_cluster_level *level)
+{
+ struct low_power_ops *ops;
+
+ if (use_psci)
+ return 0;
+
+ ops = &cluster->lpm_dev[ndevice];
+ if (ops && ops->set_mode)
+ return ops->set_mode(ops, level->mode[ndevice],
+ level->notify_rpm);
+ else
+ return -EINVAL;
+}
+
+static uint64_t lpm_cpuidle_predict(struct cpuidle_device *dev,
+ struct lpm_cpu *cpu, int *idx_restrict,
+ uint32_t *idx_restrict_time)
+{
+ int i, j, divisor;
+ uint64_t max, avg, stddev;
+ int64_t thresh = LLONG_MAX;
+ struct lpm_history *history = &per_cpu(hist, dev->cpu);
+ uint32_t *min_residency = get_per_cpu_min_residency(dev->cpu);
+
+ if (!lpm_prediction)
+ return 0;
+
+ /*
+ * Samples are marked invalid when woken-up due to timer,
+ * so donot predict.
+ */
+ if (history->hinvalid) {
+ history->hinvalid = 0;
+ history->htmr_wkup = 1;
+ history->stime = 0;
+ return 0;
+ }
+
+ /*
+ * Predict only when all the samples are collected.
+ */
+ if (history->nsamp < MAXSAMPLES) {
+ history->stime = 0;
+ return 0;
+ }
+
+ /*
+ * Check if the samples are not much deviated, if so use the
+ * average of those as predicted sleep time. Else if any
+ * specific mode has more premature exits return the index of
+ * that mode.
+ */
+
+again:
+ max = avg = divisor = stddev = 0;
+ for (i = 0; i < MAXSAMPLES; i++) {
+ int64_t value = history->resi[i];
+
+ if (value <= thresh) {
+ avg += value;
+ divisor++;
+ if (value > max)
+ max = value;
+ }
+ }
+ do_div(avg, divisor);
+
+ for (i = 0; i < MAXSAMPLES; i++) {
+ int64_t value = history->resi[i];
+
+ if (value <= thresh) {
+ int64_t diff = value - avg;
+
+ stddev += diff * diff;
+ }
+ }
+ do_div(stddev, divisor);
+ stddev = int_sqrt(stddev);
+
+ /*
+ * If the deviation is less, return the average, else
+ * ignore one maximum sample and retry
+ */
+ if (((avg > stddev * 6) && (divisor >= (MAXSAMPLES - 1)))
+ || stddev <= ref_stddev) {
+ history->stime = ktime_to_us(ktime_get()) + avg;
+ return avg;
+ } else if (divisor > (MAXSAMPLES - 1)) {
+ thresh = max - 1;
+ goto again;
+ }
+
+ /*
+ * Find the number of premature exits for each of the mode,
+ * excluding clockgating mode, and they are more than fifty
+ * percent restrict that and deeper modes.
+ */
+ if (history->htmr_wkup != 1) {
+ for (j = 1; j < cpu->nlevels; j++) {
+ uint32_t failed = 0;
+ uint64_t total = 0;
+
+ for (i = 0; i < MAXSAMPLES; i++) {
+ if ((history->mode[i] == j) &&
+ (history->resi[i] < min_residency[j])) {
+ failed++;
+ total += history->resi[i];
+ }
+ }
+ if (failed > (MAXSAMPLES/2)) {
+ *idx_restrict = j;
+ do_div(total, failed);
+ *idx_restrict_time = total;
+ history->stime = ktime_to_us(ktime_get())
+ + *idx_restrict_time;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static inline void invalidate_predict_history(struct cpuidle_device *dev)
+{
+ struct lpm_history *history = &per_cpu(hist, dev->cpu);
+
+ if (!lpm_prediction)
+ return;
+
+ if (history->hinvalid) {
+ history->hinvalid = 0;
+ history->htmr_wkup = 1;
+ history->stime = 0;
+ }
+}
+
+static void clear_predict_history(void)
+{
+ struct lpm_history *history;
+ int i;
+ unsigned int cpu;
+
+ if (!lpm_prediction)
+ return;
+
+ for_each_possible_cpu(cpu) {
+ history = &per_cpu(hist, cpu);
+ for (i = 0; i < MAXSAMPLES; i++) {
+ history->resi[i] = 0;
+ history->mode[i] = -1;
+ history->hptr = 0;
+ history->nsamp = 0;
+ history->stime = 0;
+ }
+ }
+}
+
+static void update_history(struct cpuidle_device *dev, int idx);
+
+static int cpu_power_select(struct cpuidle_device *dev,
+ struct lpm_cpu *cpu)
+{
+ int best_level = -1;
+ uint32_t latency_us = pm_qos_request_for_cpu(PM_QOS_CPU_DMA_LATENCY,
+ dev->cpu);
+ uint32_t sleep_us =
+ (uint32_t)(ktime_to_us(tick_nohz_get_sleep_length()));
+ uint32_t modified_time_us = 0;
+ uint32_t next_event_us = 0;
+ int i, idx_restrict;
+ uint32_t lvl_latency_us = 0;
+ uint64_t predicted = 0;
+ uint32_t htime = 0, idx_restrict_time = 0;
+ uint32_t next_wakeup_us = sleep_us;
+ uint32_t *min_residency = get_per_cpu_min_residency(dev->cpu);
+ uint32_t *max_residency = get_per_cpu_max_residency(dev->cpu);
+
+ if (!cpu)
+ return -EINVAL;
+
+ if (sleep_disabled)
+ return 0;
+
+ idx_restrict = cpu->nlevels + 1;
+
+ next_event_us = (uint32_t)(ktime_to_us(get_next_event_time(dev->cpu)));
+
+ for (i = 0; i < cpu->nlevels; i++) {
+ struct lpm_cpu_level *level = &cpu->levels[i];
+ struct power_params *pwr_params = &level->pwr;
+ enum msm_pm_sleep_mode mode = level->mode;
+ bool allow;
+
+ allow = lpm_cpu_mode_allow(dev->cpu, i, true);
+
+ if (!allow)
+ continue;
+
+ lvl_latency_us = pwr_params->latency_us;
+
+ if (latency_us < lvl_latency_us)
+ break;
+
+ if (next_event_us) {
+ if (next_event_us < lvl_latency_us)
+ break;
+
+ if (((next_event_us - lvl_latency_us) < sleep_us) ||
+ (next_event_us < sleep_us))
+ next_wakeup_us = next_event_us - lvl_latency_us;
+ }
+
+ if (!i) {
+ /*
+ * If the next_wake_us itself is not sufficient for
+ * deeper low power modes than clock gating do not
+ * call prediction.
+ */
+ if (next_wakeup_us > max_residency[i]) {
+ predicted = lpm_cpuidle_predict(dev, cpu,
+ &idx_restrict, &idx_restrict_time);
+ if (predicted < min_residency[i])
+ predicted = 0;
+ } else
+ invalidate_predict_history(dev);
+ }
+
+ if (i >= idx_restrict)
+ break;
+
+ best_level = i;
+
+ if (next_event_us && next_event_us < sleep_us &&
+ (mode != MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT))
+ modified_time_us
+ = next_event_us - lvl_latency_us;
+ else
+ modified_time_us = 0;
+
+ if (predicted ? (predicted <= max_residency[i])
+ : (next_wakeup_us <= max_residency[i]))
+ break;
+ }
+
+ if (modified_time_us)
+ msm_pm_set_timer(modified_time_us);
+
+ /*
+ * Start timer to avoid staying in shallower mode forever
+ * incase of misprediciton
+ */
+ if ((predicted || (idx_restrict != (cpu->nlevels + 1)))
+ && ((best_level >= 0)
+ && (best_level < (cpu->nlevels-1)))) {
+ htime = predicted + tmr_add;
+ if (htime == tmr_add)
+ htime = idx_restrict_time;
+ else if (htime > max_residency[best_level])
+ htime = max_residency[best_level];
+
+ if ((next_wakeup_us > htime) &&
+ ((next_wakeup_us - htime) > max_residency[best_level]))
+ histtimer_start(htime);
+ }
+
+ trace_cpu_power_select(best_level, sleep_us, latency_us, next_event_us);
+
+ trace_cpu_pred_select(idx_restrict_time ? 2 : (predicted ? 1 : 0),
+ predicted, htime);
+
+ return best_level;
+}
+
+static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster,
+ struct cpumask *mask, bool from_idle, uint32_t *pred_time)
+{
+ int cpu;
+ int next_cpu = raw_smp_processor_id();
+ ktime_t next_event;
+ struct cpumask online_cpus_in_cluster;
+ struct lpm_history *history;
+ int64_t prediction = LONG_MAX;
+
+ next_event.tv64 = KTIME_MAX;
+ if (!suspend_wake_time)
+ suspend_wake_time = msm_pm_sleep_time_override;
+ if (!from_idle) {
+ if (mask)
+ cpumask_copy(mask, cpumask_of(raw_smp_processor_id()));
+ if (!suspend_wake_time)
+ return ~0ULL;
+ else
+ return USEC_PER_SEC * suspend_wake_time;
+ }
+
+ cpumask_and(&online_cpus_in_cluster,
+ &cluster->num_children_in_sync, cpu_online_mask);
+
+ for_each_cpu(cpu, &online_cpus_in_cluster) {
+ ktime_t *next_event_c;
+
+ next_event_c = get_next_event_cpu(cpu);
+ if (next_event_c->tv64 < next_event.tv64) {
+ next_event.tv64 = next_event_c->tv64;
+ next_cpu = cpu;
+ }
+
+ if (from_idle && lpm_prediction) {
+ history = &per_cpu(hist, cpu);
+ if (history->stime && (history->stime < prediction))
+ prediction = history->stime;
+ }
+ }
+
+ if (mask)
+ cpumask_copy(mask, cpumask_of(next_cpu));
+
+ if (from_idle && lpm_prediction) {
+ if (prediction > ktime_to_us(ktime_get()))
+ *pred_time = prediction - ktime_to_us(ktime_get());
+ }
+
+ if (ktime_to_us(next_event) > ktime_to_us(ktime_get()))
+ return ktime_to_us(ktime_sub(next_event, ktime_get()));
+ else
+ return 0;
+}
+
+static int cluster_predict(struct lpm_cluster *cluster,
+ uint32_t *pred_us)
+{
+ int i, j;
+ int ret = 0;
+ struct cluster_history *history = &cluster->history;
+ int64_t cur_time = ktime_to_us(ktime_get());
+
+ if (!lpm_prediction)
+ return 0;
+
+ if (history->hinvalid) {
+ history->hinvalid = 0;
+ history->htmr_wkup = 1;
+ history->flag = 0;
+ return ret;
+ }
+
+ if (history->nsamp == MAXSAMPLES) {
+ for (i = 0; i < MAXSAMPLES; i++) {
+ if ((cur_time - history->stime[i])
+ > CLUST_SMPL_INVLD_TIME)
+ history->nsamp--;
+ }
+ }
+
+ if (history->nsamp < MAXSAMPLES) {
+ history->flag = 0;
+ return ret;
+ }
+
+ if (history->flag == 2)
+ history->flag = 0;
+
+ if (history->htmr_wkup != 1) {
+ uint64_t total = 0;
+
+ if (history->flag == 1) {
+ for (i = 0; i < MAXSAMPLES; i++)
+ total += history->resi[i];
+ do_div(total, MAXSAMPLES);
+ *pred_us = total;
+ return 2;
+ }
+
+ for (j = 1; j < cluster->nlevels; j++) {
+ uint32_t failed = 0;
+
+ total = 0;
+ for (i = 0; i < MAXSAMPLES; i++) {
+ if ((history->mode[i] == j) && (history->resi[i]
+ < cluster->levels[j].pwr.min_residency)) {
+ failed++;
+ total += history->resi[i];
+ }
+ }
+
+ if (failed > (MAXSAMPLES-2)) {
+ do_div(total, failed);
+ *pred_us = total;
+ history->flag = 1;
+ return 1;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void update_cluster_history_time(struct cluster_history *history,
+ int idx, uint64_t start)
+{
+ history->entry_idx = idx;
+ history->entry_time = start;
+}
+
+static void update_cluster_history(struct cluster_history *history, int idx)
+{
+ uint32_t tmr = 0;
+ uint32_t residency = 0;
+ struct lpm_cluster *cluster =
+ container_of(history, struct lpm_cluster, history);
+
+ if (!lpm_prediction)
+ return;
+
+ if ((history->entry_idx == -1) || (history->entry_idx == idx)) {
+ residency = ktime_to_us(ktime_get()) - history->entry_time;
+ history->stime[history->hptr] = history->entry_time;
+ } else
+ return;
+
+ if (history->htmr_wkup) {
+ if (!history->hptr)
+ history->hptr = MAXSAMPLES-1;
+ else
+ history->hptr--;
+
+ history->resi[history->hptr] += residency;
+
+ history->htmr_wkup = 0;
+ tmr = 1;
+ } else {
+ history->resi[history->hptr] = residency;
+ }
+
+ history->mode[history->hptr] = idx;
+
+ history->entry_idx = INT_MIN;
+ history->entry_time = 0;
+
+ if (history->nsamp < MAXSAMPLES)
+ history->nsamp++;
+
+ trace_cluster_pred_hist(cluster->cluster_name,
+ history->mode[history->hptr], history->resi[history->hptr],
+ history->hptr, tmr);
+
+ (history->hptr)++;
+
+ if (history->hptr >= MAXSAMPLES)
+ history->hptr = 0;
+}
+
+static void clear_cl_history_each(struct cluster_history *history)
+{
+ int i;
+
+ for (i = 0; i < MAXSAMPLES; i++) {
+ history->resi[i] = 0;
+ history->mode[i] = -1;
+ history->stime[i] = 0;
+ }
+ history->hptr = 0;
+ history->nsamp = 0;
+ history->flag = 0;
+ history->hinvalid = 0;
+ history->htmr_wkup = 0;
+}
+static void clear_cl_predict_history(void)
+{
+ struct lpm_cluster *cluster = lpm_root_node;
+ struct list_head *list;
+
+ if (!lpm_prediction)
+ return;
+
+ clear_cl_history_each(&cluster->history);
+
+ list_for_each(list, &cluster->child) {
+ struct lpm_cluster *n;
+
+ n = list_entry(list, typeof(*n), list);
+ clear_cl_history_each(&n->history);
+ }
+}
+
+static int cluster_select(struct lpm_cluster *cluster, bool from_idle,
+ int *ispred)
+{
+ int best_level = -1;
+ int i;
+ struct cpumask mask;
+ uint32_t latency_us = ~0U;
+ uint32_t sleep_us;
+ uint32_t cpupred_us = 0, pred_us = 0;
+ int pred_mode = 0, predicted = 0;
+
+ if (!cluster)
+ return -EINVAL;
+
+ sleep_us = (uint32_t)get_cluster_sleep_time(cluster, NULL,
+ from_idle, &cpupred_us);
+
+ if (from_idle) {
+ pred_mode = cluster_predict(cluster, &pred_us);
+
+ if (cpupred_us && pred_mode && (cpupred_us < pred_us))
+ pred_us = cpupred_us;
+
+ if (pred_us && pred_mode && (pred_us < sleep_us))
+ predicted = 1;
+
+ if (predicted && (pred_us == cpupred_us))
+ predicted = 2;
+ }
+
+ if (cpumask_and(&mask, cpu_online_mask, &cluster->child_cpus))
+ latency_us = pm_qos_request_for_cpumask(PM_QOS_CPU_DMA_LATENCY,
+ &mask);
+
+ /*
+ * If atleast one of the core in the cluster is online, the cluster
+ * low power modes should be determined by the idle characteristics
+ * even if the last core enters the low power mode as a part of
+ * hotplug.
+ */
+
+ if (!from_idle && num_online_cpus() > 1 &&
+ cpumask_intersects(&cluster->child_cpus, cpu_online_mask))
+ from_idle = true;
+
+ for (i = 0; i < cluster->nlevels; i++) {
+ struct lpm_cluster_level *level = &cluster->levels[i];
+ struct power_params *pwr_params = &level->pwr;
+
+ if (!lpm_cluster_mode_allow(cluster, i, from_idle))
+ continue;
+
+ if (level->last_core_only &&
+ cpumask_weight(cpu_online_mask) > 1)
+ continue;
+
+ if (!cpumask_equal(&cluster->num_children_in_sync,
+ &level->num_cpu_votes))
+ continue;
+
+ if (from_idle && latency_us < pwr_params->latency_us)
+ break;
+
+ if (sleep_us < pwr_params->time_overhead_us)
+ break;
+
+ if (suspend_in_progress && from_idle && level->notify_rpm)
+ continue;
+
+ best_level = i;
+
+ if (predicted ? (pred_us <= pwr_params->max_residency)
+ : (sleep_us <= pwr_params->max_residency))
+ break;
+ }
+
+ if ((best_level == (cluster->nlevels - 1)) && (pred_mode == 2))
+ cluster->history.flag = 2;
+
+ *ispred = predicted;
+
+ trace_cluster_pred_select(cluster->cluster_name, best_level, sleep_us,
+ latency_us, predicted, pred_us);
+
+ return best_level;
+}
+
+static void cluster_notify(struct lpm_cluster *cluster,
+ struct lpm_cluster_level *level, bool enter)
+{
+ if (level->is_reset && enter)
+ cpu_cluster_pm_enter(cluster->aff_level);
+ else if (level->is_reset && !enter)
+ cpu_cluster_pm_exit(cluster->aff_level);
+}
+
+static int cluster_configure(struct lpm_cluster *cluster, int idx,
+ bool from_idle, int predicted)
+{
+ struct lpm_cluster_level *level = &cluster->levels[idx];
+ int ret, i;
+
+ if (!cpumask_equal(&cluster->num_children_in_sync, &cluster->child_cpus)
+ || is_IPI_pending(&cluster->num_children_in_sync)) {
+ return -EPERM;
+ }
+
+ if (idx != cluster->default_level) {
+ update_debug_pc_event(CLUSTER_ENTER, idx,
+ cluster->num_children_in_sync.bits[0],
+ cluster->child_cpus.bits[0], from_idle);
+ trace_cluster_enter(cluster->cluster_name, idx,
+ cluster->num_children_in_sync.bits[0],
+ cluster->child_cpus.bits[0], from_idle);
+ lpm_stats_cluster_enter(cluster->stats, idx);
+
+ if (from_idle && lpm_prediction)
+ update_cluster_history_time(&cluster->history, idx,
+ ktime_to_us(ktime_get()));
+ }
+
+ for (i = 0; i < cluster->ndevices; i++) {
+ ret = set_device_mode(cluster, i, level);
+ if (ret)
+ goto failed_set_mode;
+ }
+ if (level->notify_rpm) {
+ struct cpumask nextcpu, *cpumask;
+ uint64_t us;
+ uint32_t pred_us;
+
+ us = get_cluster_sleep_time(cluster, &nextcpu,
+ from_idle, &pred_us);
+ cpumask = level->disable_dynamic_routing ? NULL : &nextcpu;
+
+ if (ret) {
+ pr_info("Failed msm_rpm_enter_sleep() rc = %d\n", ret);
+ goto failed_set_mode;
+ }
+
+ us = us + 1;
+ clear_predict_history();
+ clear_cl_predict_history();
+
+ do_div(us, USEC_PER_SEC/SCLK_HZ);
+ system_sleep_enter(us);
+ }
+ /* Notify cluster enter event after successfully config completion */
+ cluster_notify(cluster, level, true);
+
+ cluster->last_level = idx;
+
+ if (predicted && (idx < (cluster->nlevels - 1))) {
+ struct power_params *pwr_params = &cluster->levels[idx].pwr;
+
+ tick_broadcast_exit();
+ clusttimer_start(cluster, pwr_params->max_residency + tmr_add);
+ tick_broadcast_enter();
+ }
+
+ return 0;
+failed_set_mode:
+
+ for (i = 0; i < cluster->ndevices; i++) {
+ int rc = 0;
+
+ level = &cluster->levels[cluster->default_level];
+ // rc = set_device_mode(cluster, i, level);
+ WARN_ON(rc);
+ }
+
+ return ret;
+}
+
+static void cluster_prepare(struct lpm_cluster *cluster,
+ const struct cpumask *cpu, int child_idx, bool from_idle,
+ int64_t start_time)
+{
+ int i;
+ int predicted = 0;
+
+ if (!cluster)
+ return;
+
+ if (cluster->min_child_level > child_idx)
+ return;
+
+ spin_lock(&cluster->sync_lock);
+ cpumask_or(&cluster->num_children_in_sync, cpu,
+ &cluster->num_children_in_sync);
+
+ for (i = 0; i < cluster->nlevels; i++) {
+ struct lpm_cluster_level *lvl = &cluster->levels[i];
+
+ if (child_idx >= lvl->min_child_level)
+ cpumask_or(&lvl->num_cpu_votes, cpu,
+ &lvl->num_cpu_votes);
+ }
+
+ /*
+ * cluster_select() does not make any configuration changes. So its ok
+ * to release the lock here. If a core wakes up for a rude request,
+ * it need not wait for another to finish its cluster selection and
+ * configuration process
+ */
+
+ if (!cpumask_equal(&cluster->num_children_in_sync,
+ &cluster->child_cpus))
+ goto failed;
+
+ i = cluster_select(cluster, from_idle, &predicted);
+
+ if (((i < 0) || (i == cluster->default_level))
+ && predicted && from_idle) {
+ update_cluster_history_time(&cluster->history,
+ -1, ktime_to_us(ktime_get()));
+
+ if (i < 0) {
+ struct power_params *pwr_params =
+ &cluster->levels[0].pwr;
+
+ tick_broadcast_exit();
+ clusttimer_start(cluster,
+ pwr_params->max_residency + tmr_add);
+ tick_broadcast_enter();
+ }
+ }
+
+ if (i < 0)
+ goto failed;
+
+ if (cluster_configure(cluster, i, from_idle, predicted))
+ goto failed;
+
+ cluster->stats->sleep_time = start_time;
+ cluster_prepare(cluster->parent, &cluster->num_children_in_sync, i,
+ from_idle, start_time);
+
+ spin_unlock(&cluster->sync_lock);
+ return;
+failed:
+ spin_unlock(&cluster->sync_lock);
+ cluster->stats->sleep_time = 0;
+}
+
+static void cluster_unprepare(struct lpm_cluster *cluster,
+ const struct cpumask *cpu, int child_idx, bool from_idle,
+ int64_t end_time)
+{
+ struct lpm_cluster_level *level;
+ bool first_cpu;
+ int last_level, i, ret;
+
+ if (!cluster)
+ return;
+
+ if (cluster->min_child_level > child_idx)
+ return;
+
+ spin_lock(&cluster->sync_lock);
+ last_level = cluster->default_level;
+ first_cpu = cpumask_equal(&cluster->num_children_in_sync,
+ &cluster->child_cpus);
+ cpumask_andnot(&cluster->num_children_in_sync,
+ &cluster->num_children_in_sync, cpu);
+
+ for (i = 0; i < cluster->nlevels; i++) {
+ struct lpm_cluster_level *lvl = &cluster->levels[i];
+
+ if (child_idx >= lvl->min_child_level)
+ cpumask_andnot(&lvl->num_cpu_votes,
+ &lvl->num_cpu_votes, cpu);
+ }
+
+ if (from_idle && first_cpu &&
+ (cluster->last_level == cluster->default_level))
+ update_cluster_history(&cluster->history, cluster->last_level);
+
+ if (!first_cpu || cluster->last_level == cluster->default_level)
+ goto unlock_return;
+
+ if (cluster->stats->sleep_time)
+ cluster->stats->sleep_time = end_time -
+ cluster->stats->sleep_time;
+ lpm_stats_cluster_exit(cluster->stats, cluster->last_level, true);
+
+ level = &cluster->levels[cluster->last_level];
+
+ if (level->notify_rpm)
+ system_sleep_exit();
+
+ update_debug_pc_event(CLUSTER_EXIT, cluster->last_level,
+ cluster->num_children_in_sync.bits[0],
+ cluster->child_cpus.bits[0], from_idle);
+ trace_cluster_exit(cluster->cluster_name, cluster->last_level,
+ cluster->num_children_in_sync.bits[0],
+ cluster->child_cpus.bits[0], from_idle);
+
+ last_level = cluster->last_level;
+ cluster->last_level = cluster->default_level;
+
+ for (i = 0; i < cluster->ndevices; i++) {
+ level = &cluster->levels[cluster->default_level];
+ ret = set_device_mode(cluster, i, level);
+
+ WARN_ON(ret);
+
+ }
+
+ cluster_notify(cluster, &cluster->levels[last_level], false);
+
+ if (from_idle)
+ update_cluster_history(&cluster->history, last_level);
+
+ cluster_unprepare(cluster->parent, &cluster->child_cpus,
+ last_level, from_idle, end_time);
+unlock_return:
+ spin_unlock(&cluster->sync_lock);
+}
+
+static inline void cpu_prepare(struct lpm_cluster *cluster, int cpu_index,
+ bool from_idle)
+{
+ struct lpm_cpu_level *cpu_level = &cluster->cpu->levels[cpu_index];
+ bool jtag_save_restore =
+ cluster->cpu->levels[cpu_index].jtag_save_restore;
+
+ /* Use broadcast timer for aggregating sleep mode within a cluster.
+ * A broadcast timer could be used in the following scenarios
+ * 1) The architected timer HW gets reset during certain low power
+ * modes and the core relies on a external(broadcast) timer to wake up
+ * from sleep. This information is passed through device tree.
+ * 2) The CPU low power mode could trigger a system low power mode.
+ * The low power module relies on Broadcast timer to aggregate the
+ * next wakeup within a cluster, in which case, CPU switches over to
+ * use broadcast timer.
+ */
+ if (from_idle && cpu_level->use_bc_timer)
+ tick_broadcast_enter();
+
+ if (from_idle && ((cpu_level->mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE)
+ || (cpu_level->mode ==
+ MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)
+ || (cpu_level->is_reset)))
+ cpu_pm_enter();
+
+ /*
+ * Save JTAG registers for 8996v1.0 & 8996v2.x in C4 LPM
+ */
+ if (jtag_save_restore)
+ msm_jtag_save_state();
+}
+
+static inline void cpu_unprepare(struct lpm_cluster *cluster, int cpu_index,
+ bool from_idle)
+{
+ struct lpm_cpu_level *cpu_level = &cluster->cpu->levels[cpu_index];
+ bool jtag_save_restore =
+ cluster->cpu->levels[cpu_index].jtag_save_restore;
+
+ if (from_idle && cpu_level->use_bc_timer)
+ tick_broadcast_exit();
+
+ if (from_idle && ((cpu_level->mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE)
+ || (cpu_level->mode ==
+ MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)
+ || cpu_level->is_reset))
+ cpu_pm_exit();
+
+ /*
+ * Restore JTAG registers for 8996v1.0 & 8996v2.x in C4 LPM
+ */
+ if (jtag_save_restore)
+ msm_jtag_restore_state();
+}
+
+int get_cluster_id(struct lpm_cluster *cluster, int *aff_lvl)
+{
+ int state_id = 0;
+
+ if (!cluster)
+ return 0;
+
+ spin_lock(&cluster->sync_lock);
+
+ if (!cpumask_equal(&cluster->num_children_in_sync,
+ &cluster->child_cpus))
+ goto unlock_and_return;
+
+ state_id |= get_cluster_id(cluster->parent, aff_lvl);
+
+ if (cluster->last_level != cluster->default_level) {
+ struct lpm_cluster_level *level
+ = &cluster->levels[cluster->last_level];
+
+ state_id |= (level->psci_id & cluster->psci_mode_mask)
+ << cluster->psci_mode_shift;
+ (*aff_lvl)++;
+ }
+unlock_and_return:
+ spin_unlock(&cluster->sync_lock);
+ return state_id;
+}
+
+#if !defined(CONFIG_CPU_V7)
+bool psci_enter_sleep(struct lpm_cluster *cluster, int idx, bool from_idle)
+{
+ int affinity_level = 0;
+ int state_id = get_cluster_id(cluster, &affinity_level);
+ int power_state =
+ PSCI_POWER_STATE(cluster->cpu->levels[idx].is_reset);
+ bool success = false;
+ /*
+ * idx = 0 is the default LPM state
+ */
+ if (!idx) {
+ stop_critical_timings();
+ wfi();
+ start_critical_timings();
+ return 1;
+ }
+
+ affinity_level = PSCI_AFFINITY_LEVEL(affinity_level);
+ state_id |= (power_state | affinity_level
+ | cluster->cpu->levels[idx].psci_id);
+
+ update_debug_pc_event(CPU_ENTER, state_id,
+ 0xdeaffeed, 0xdeaffeed, true);
+ stop_critical_timings();
+ success = !arm_cpuidle_suspend(state_id);
+ start_critical_timings();
+ update_debug_pc_event(CPU_EXIT, state_id,
+ success, 0xdeaffeed, true);
+ return success;
+}
+#elif defined(CONFIG_ARM_PSCI)
+bool psci_enter_sleep(struct lpm_cluster *cluster, int idx, bool from_idle)
+{
+ int affinity_level = 0;
+ int state_id = get_cluster_id(cluster, &affinity_level);
+ int power_state =
+ PSCI_POWER_STATE(cluster->cpu->levels[idx].is_reset);
+ bool success = false;
+
+ if (!idx) {
+ stop_critical_timings();
+ wfi();
+ start_critical_timings();
+ return 1;
+ }
+
+ affinity_level = PSCI_AFFINITY_LEVEL(affinity_level);
+ state_id |= (power_state | affinity_level
+ | cluster->cpu->levels[idx].psci_id);
+
+ update_debug_pc_event(CPU_ENTER, state_id,
+ 0xdeaffeed, 0xdeaffeed, true);
+ stop_critical_timings();
+ success = !arm_cpuidle_suspend(state_id);
+ start_critical_timings();
+ update_debug_pc_event(CPU_EXIT, state_id,
+ success, 0xdeaffeed, true);
+}
+#else
+bool psci_enter_sleep(struct lpm_cluster *cluster, int idx, bool from_idle)
+{
+ WARN_ONCE(true, "PSCI cpu_suspend ops not supported\n");
+ return false;
+}
+#endif
+
+static int lpm_cpuidle_select(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev)
+{
+ struct lpm_cluster *cluster = per_cpu(cpu_cluster, dev->cpu);
+ int idx;
+
+ if (!cluster)
+ return 0;
+
+ idx = cpu_power_select(dev, cluster->cpu);
+
+ if (idx < 0)
+ return 0;
+
+ return idx;
+}
+
+static void update_history(struct cpuidle_device *dev, int idx)
+{
+ struct lpm_history *history = &per_cpu(hist, dev->cpu);
+ uint32_t tmr = 0;
+
+ if (!lpm_prediction)
+ return;
+
+ if (history->htmr_wkup) {
+ if (!history->hptr)
+ history->hptr = MAXSAMPLES-1;
+ else
+ history->hptr--;
+
+ history->resi[history->hptr] += dev->last_residency;
+ history->htmr_wkup = 0;
+ tmr = 1;
+ } else
+ history->resi[history->hptr] = dev->last_residency;
+
+ history->mode[history->hptr] = idx;
+
+ trace_cpu_pred_hist(history->mode[history->hptr],
+ history->resi[history->hptr], history->hptr, tmr);
+
+ if (history->nsamp < MAXSAMPLES)
+ history->nsamp++;
+
+ (history->hptr)++;
+ if (history->hptr >= MAXSAMPLES)
+ history->hptr = 0;
+}
+
+static int lpm_cpuidle_enter(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int idx)
+{
+ struct lpm_cluster *cluster = per_cpu(cpu_cluster, dev->cpu);
+ bool success = true;
+ const struct cpumask *cpumask = get_cpu_mask(dev->cpu);
+ int64_t start_time = ktime_to_ns(ktime_get()), end_time;
+ struct power_params *pwr_params;
+
+ pwr_params = &cluster->cpu->levels[idx].pwr;
+
+ pwr_params = &cluster->cpu->levels[idx].pwr;
+
+ cpu_prepare(cluster, idx, true);
+ cluster_prepare(cluster, cpumask, idx, true, ktime_to_ns(ktime_get()));
+
+ trace_cpu_idle_enter(idx);
+ lpm_stats_cpu_enter(idx, start_time);
+
+ if (need_resched() || (idx < 0))
+ goto exit;
+
+ WARN_ON(!use_psci);
+ success = psci_enter_sleep(cluster, idx, true);
+
+exit:
+ end_time = ktime_to_ns(ktime_get());
+ lpm_stats_cpu_exit(idx, end_time, success);
+
+ cluster_unprepare(cluster, cpumask, idx, true, end_time);
+ cpu_unprepare(cluster, idx, true);
+ sched_set_cpu_cstate(smp_processor_id(), 0, 0, 0);
+ end_time = ktime_to_ns(ktime_get()) - start_time;
+ do_div(end_time, 1000);
+ dev->last_residency = end_time;
+ update_history(dev, idx);
+ trace_cpu_idle_exit(idx, success);
+ local_irq_enable();
+ if (lpm_prediction) {
+ histtimer_cancel();
+ clusttimer_cancel();
+ }
+ return idx;
+}
+
+#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS
+static int cpuidle_register_cpu(struct cpuidle_driver *drv,
+ struct cpumask *mask)
+{
+ struct cpuidle_device *device;
+ int cpu, ret;
+
+
+ if (!mask || !drv)
+ return -EINVAL;
+
+ drv->cpumask = mask;
+ ret = cpuidle_register_driver(drv);
+ if (ret) {
+ pr_err("Failed to register cpuidle driver %d\n", ret);
+ goto failed_driver_register;
+ }
+
+ for_each_cpu(cpu, mask) {
+ device = &per_cpu(cpuidle_dev, cpu);
+ device->cpu = cpu;
+
+ ret = cpuidle_register_device(device);
+ if (ret) {
+ pr_err("Failed to register cpuidle driver for cpu:%u\n",
+ cpu);
+ goto failed_driver_register;
+ }
+ }
+ return ret;
+failed_driver_register:
+ for_each_cpu(cpu, mask)
+ cpuidle_unregister_driver(drv);
+ return ret;
+}
+#else
+static int cpuidle_register_cpu(struct cpuidle_driver *drv,
+ struct cpumask *mask)
+{
+ return cpuidle_register(drv, NULL);
+}
+#endif
+
+static struct cpuidle_governor lpm_governor = {
+ .name = "qcom",
+ .rating = 30,
+ .select = lpm_cpuidle_select,
+ .owner = THIS_MODULE,
+};
+
+static int cluster_cpuidle_register(struct lpm_cluster *cl)
+{
+ int i = 0, ret = 0;
+ unsigned int cpu;
+ struct lpm_cluster *p = NULL;
+
+ if (!cl->cpu) {
+ struct lpm_cluster *n;
+
+ list_for_each_entry(n, &cl->child, list) {
+ ret = cluster_cpuidle_register(n);
+ if (ret)
+ break;
+ }
+ return ret;
+ }
+
+ cl->drv = kcalloc(1, sizeof(*cl->drv), GFP_KERNEL);
+ if (!cl->drv)
+ return -ENOMEM;
+
+ cl->drv->name = "msm_idle";
+
+ for (i = 0; i < cl->cpu->nlevels; i++) {
+ struct cpuidle_state *st = &cl->drv->states[i];
+ struct lpm_cpu_level *cpu_level = &cl->cpu->levels[i];
+
+ snprintf(st->name, CPUIDLE_NAME_LEN, "C%u\n", i);
+ snprintf(st->desc, CPUIDLE_DESC_LEN, cpu_level->name);
+ st->flags = 0;
+ st->exit_latency = cpu_level->pwr.latency_us;
+ st->power_usage = cpu_level->pwr.ss_power;
+ st->target_residency = 0;
+ st->enter = lpm_cpuidle_enter;
+ }
+
+ cl->drv->state_count = cl->cpu->nlevels;
+ cl->drv->safe_state_index = 0;
+ for_each_cpu(cpu, &cl->child_cpus)
+ per_cpu(cpu_cluster, cpu) = cl;
+
+ for_each_possible_cpu(cpu) {
+ if (cpu_online(cpu))
+ continue;
+ p = per_cpu(cpu_cluster, cpu);
+ while (p) {
+ int j;
+
+ spin_lock(&p->sync_lock);
+ cpumask_set_cpu(cpu, &p->num_children_in_sync);
+ for (j = 0; j < p->nlevels; j++)
+ cpumask_copy(&p->levels[j].num_cpu_votes,
+ &p->num_children_in_sync);
+ spin_unlock(&p->sync_lock);
+ p = p->parent;
+ }
+ }
+ ret = cpuidle_register_cpu(cl->drv, &cl->child_cpus);
+
+ if (ret) {
+ kfree(cl->drv);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/**
+ * init_lpm - initializes the governor
+ */
+static int __init init_lpm(void)
+{
+ return cpuidle_register_governor(&lpm_governor);
+}
+
+postcore_initcall(init_lpm);
+
+static void register_cpu_lpm_stats(struct lpm_cpu *cpu,
+ struct lpm_cluster *parent)
+{
+ const char **level_name;
+ int i;
+
+ level_name = kcalloc(cpu->nlevels, sizeof(*level_name), GFP_KERNEL);
+
+ if (!level_name)
+ return;
+
+ for (i = 0; i < cpu->nlevels; i++)
+ level_name[i] = cpu->levels[i].name;
+
+ lpm_stats_config_level("cpu", level_name, cpu->nlevels,
+ parent->stats, &parent->child_cpus);
+
+ kfree(level_name);
+}
+
+static void register_cluster_lpm_stats(struct lpm_cluster *cl,
+ struct lpm_cluster *parent)
+{
+ const char **level_name;
+ int i;
+ struct lpm_cluster *child;
+
+ if (!cl)
+ return;
+
+ level_name = kcalloc(cl->nlevels, sizeof(*level_name), GFP_KERNEL);
+
+ if (!level_name)
+ return;
+
+ for (i = 0; i < cl->nlevels; i++)
+ level_name[i] = cl->levels[i].level_name;
+
+ cl->stats = lpm_stats_config_level(cl->cluster_name, level_name,
+ cl->nlevels, parent ? parent->stats : NULL, NULL);
+
+ kfree(level_name);
+
+ if (cl->cpu) {
+ register_cpu_lpm_stats(cl->cpu, cl);
+ return;
+ }
+
+ list_for_each_entry(child, &cl->child, list)
+ register_cluster_lpm_stats(child, cl);
+}
+
+static int lpm_suspend_prepare(void)
+{
+ suspend_in_progress = true;
+ lpm_stats_suspend_enter();
+
+ return 0;
+}
+
+static void lpm_suspend_wake(void)
+{
+ suspend_in_progress = false;
+ lpm_stats_suspend_exit();
+}
+
+static int lpm_suspend_enter(suspend_state_t state)
+{
+ int cpu = raw_smp_processor_id();
+ struct lpm_cluster *cluster = per_cpu(cpu_cluster, cpu);
+ struct lpm_cpu *lpm_cpu = cluster->cpu;
+ const struct cpumask *cpumask = get_cpu_mask(cpu);
+ int idx;
+
+ for (idx = lpm_cpu->nlevels - 1; idx >= 0; idx--) {
+
+ if (lpm_cpu_mode_allow(cpu, idx, false))
+ break;
+ }
+ if (idx < 0) {
+ pr_err("Failed suspend\n");
+ return 0;
+ }
+ cpu_prepare(cluster, idx, false);
+ cluster_prepare(cluster, cpumask, idx, false, 0);
+ if (idx > 0)
+ update_debug_pc_event(CPU_ENTER, idx, 0xdeaffeed,
+ 0xdeaffeed, false);
+
+ /*
+ * Print the clocks which are enabled during system suspend
+ * This debug information is useful to know which are the
+ * clocks that are enabled and preventing the system level
+ * LPMs(XO and Vmin).
+ */
+
+ WARN_ON(!use_psci);
+ psci_enter_sleep(cluster, idx, true);
+
+ if (idx > 0)
+ update_debug_pc_event(CPU_EXIT, idx, true, 0xdeaffeed,
+ false);
+
+ cluster_unprepare(cluster, cpumask, idx, false, 0);
+ cpu_unprepare(cluster, idx, false);
+ return 0;
+}
+
+static const struct platform_suspend_ops lpm_suspend_ops = {
+ .enter = lpm_suspend_enter,
+ .valid = suspend_valid_only_mem,
+ .prepare_late = lpm_suspend_prepare,
+ .wake = lpm_suspend_wake,
+};
+
+static int lpm_probe(struct platform_device *pdev)
+{
+ int ret;
+ int size;
+ struct kobject *module_kobj = NULL;
+
+ get_online_cpus();
+ lpm_root_node = lpm_of_parse_cluster(pdev);
+
+ if (IS_ERR_OR_NULL(lpm_root_node)) {
+ pr_err("%s(): Failed to probe low power modes\n", __func__);
+ put_online_cpus();
+ return PTR_ERR(lpm_root_node);
+ }
+
+ if (print_parsed_dt)
+ cluster_dt_walkthrough(lpm_root_node);
+
+ /*
+ * Register hotplug notifier before broadcast time to ensure there
+ * to prevent race where a broadcast timer might not be setup on for a
+ * core. BUG in existing code but no known issues possibly because of
+ * how late lpm_levels gets initialized.
+ */
+ suspend_set_ops(&lpm_suspend_ops);
+ hrtimer_init(&lpm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hrtimer_init(&histtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ cluster_timer_init(lpm_root_node);
+
+ ret = remote_spin_lock_init(&scm_handoff_lock, SCM_HANDOFF_LOCK_ID);
+ if (ret) {
+ pr_err("%s: Failed initializing scm_handoff_lock (%d)\n",
+ __func__, ret);
+ put_online_cpus();
+ return ret;
+ }
+
+ size = num_dbg_elements * sizeof(struct lpm_debug);
+ lpm_debug = dma_alloc_coherent(&pdev->dev, size,
+ &lpm_debug_phys, GFP_KERNEL);
+ register_cluster_lpm_stats(lpm_root_node, NULL);
+
+ ret = cluster_cpuidle_register(lpm_root_node);
+ put_online_cpus();
+ if (ret) {
+ pr_err("%s()Failed to register with cpuidle framework\n",
+ __func__);
+ goto failed;
+ }
+ register_hotcpu_notifier(&lpm_cpu_nblk);
+ module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
+ if (!module_kobj) {
+ pr_err("%s: cannot find kobject for module %s\n",
+ __func__, KBUILD_MODNAME);
+ ret = -ENOENT;
+ goto failed;
+ }
+
+ ret = create_cluster_lvl_nodes(lpm_root_node, module_kobj);
+ if (ret) {
+ pr_err("%s(): Failed to create cluster level nodes\n",
+ __func__);
+ goto failed;
+ }
+
+ return 0;
+failed:
+ free_cluster_node(lpm_root_node);
+ lpm_root_node = NULL;
+ return ret;
+}
+
+static const struct of_device_id lpm_mtch_tbl[] = {
+ {.compatible = "qcom,lpm-levels"},
+ {},
+};
+
+static struct platform_driver lpm_driver = {
+ .probe = lpm_probe,
+ .driver = {
+ .name = "lpm-levels",
+ .owner = THIS_MODULE,
+ .of_match_table = lpm_mtch_tbl,
+ },
+};
+
+static int __init lpm_levels_module_init(void)
+{
+ int rc;
+
+ rc = platform_driver_register(&lpm_driver);
+ if (rc) {
+ pr_info("Error registering %s\n", lpm_driver.driver.name);
+ goto fail;
+ }
+
+fail:
+ return rc;
+}
+late_initcall(lpm_levels_module_init);
+
+enum msm_pm_l2_scm_flag lpm_cpu_pre_pc_cb(unsigned int cpu)
+{
+ struct lpm_cluster *cluster = per_cpu(cpu_cluster, cpu);
+ enum msm_pm_l2_scm_flag retflag = MSM_SCM_L2_ON;
+
+ /*
+ * No need to acquire the lock if probe isn't completed yet
+ * In the event of the hotplug happening before lpm probe, we want to
+ * flush the cache to make sure that L2 is flushed. In particular, this
+ * could cause incoherencies for a cluster architecture. This wouldn't
+ * affect the idle case as the idle driver wouldn't be registered
+ * before the probe function
+ */
+ if (!cluster)
+ return MSM_SCM_L2_OFF;
+
+ /*
+ * Assumes L2 only. What/How parameters gets passed into TZ will
+ * determine how this function reports this info back in msm-pm.c
+ */
+ spin_lock(&cluster->sync_lock);
+
+ if (!cluster->lpm_dev) {
+ retflag = MSM_SCM_L2_OFF;
+ goto unlock_and_return;
+ }
+
+ if (!cpumask_equal(&cluster->num_children_in_sync,
+ &cluster->child_cpus))
+ goto unlock_and_return;
+
+ if (cluster->lpm_dev)
+ retflag = cluster->lpm_dev->tz_flag;
+ /*
+ * The scm_handoff_lock will be release by the secure monitor.
+ * It is used to serialize power-collapses from this point on,
+ * so that both Linux and the secure context have a consistent
+ * view regarding the number of running cpus (cpu_count).
+ *
+ * It must be acquired before releasing the cluster lock.
+ */
+unlock_and_return:
+ update_debug_pc_event(PRE_PC_CB, retflag, 0xdeadbeef, 0xdeadbeef,
+ 0xdeadbeef);
+ trace_pre_pc_cb(retflag);
+ remote_spin_lock_rlock_id(&scm_handoff_lock,
+ REMOTE_SPINLOCK_TID_START + cpu);
+ spin_unlock(&cluster->sync_lock);
+ return retflag;
+}
diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h
new file mode 100644
index 0000000..6c9a50b
--- /dev/null
+++ b/drivers/cpuidle/lpm-levels.h
@@ -0,0 +1,166 @@
+/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <soc/qcom/pm.h>
+#include <soc/qcom/spm.h>
+
+#define NR_LPM_LEVELS 8
+#define MAXSAMPLES 5
+#define CLUST_SMPL_INVLD_TIME 40000
+
+extern bool use_psci;
+
+struct lpm_lookup_table {
+ uint32_t modes;
+ const char *mode_name;
+};
+
+struct power_params {
+ uint32_t latency_us; /* Enter + Exit latency */
+ uint32_t ss_power; /* Steady state power */
+ uint32_t energy_overhead; /* Enter + exit over head */
+ uint32_t time_overhead_us; /* Enter + exit overhead */
+ uint32_t residencies[NR_LPM_LEVELS];
+ uint32_t min_residency;
+ uint32_t max_residency;
+};
+
+struct lpm_cpu_level {
+ const char *name;
+ enum msm_pm_sleep_mode mode;
+ bool use_bc_timer;
+ struct power_params pwr;
+ unsigned int psci_id;
+ bool is_reset;
+ bool jtag_save_restore;
+ bool hyp_psci;
+ int reset_level;
+};
+
+struct lpm_cpu {
+ struct lpm_cpu_level levels[NR_LPM_LEVELS];
+ int nlevels;
+ unsigned int psci_mode_shift;
+ unsigned int psci_mode_mask;
+ struct lpm_cluster *parent;
+};
+
+struct lpm_level_avail {
+ bool idle_enabled;
+ bool suspend_enabled;
+ struct kobject *kobj;
+ struct kobj_attribute idle_enabled_attr;
+ struct kobj_attribute suspend_enabled_attr;
+ void *data;
+ int idx;
+ bool cpu_node;
+};
+
+struct lpm_cluster_level {
+ const char *level_name;
+ int *mode; /* SPM mode to enter */
+ int min_child_level;
+ struct cpumask num_cpu_votes;
+ struct power_params pwr;
+ bool notify_rpm;
+ bool disable_dynamic_routing;
+ bool sync_level;
+ bool last_core_only;
+ struct lpm_level_avail available;
+ unsigned int psci_id;
+ bool is_reset;
+ int reset_level;
+};
+
+struct low_power_ops {
+ struct msm_spm_device *spm;
+ int (*set_mode)(struct low_power_ops *ops, int mode, bool notify_rpm);
+ enum msm_pm_l2_scm_flag tz_flag;
+};
+
+struct cluster_history {
+ uint32_t resi[MAXSAMPLES];
+ int mode[MAXSAMPLES];
+ int64_t stime[MAXSAMPLES];
+ uint32_t hptr;
+ uint32_t hinvalid;
+ uint32_t htmr_wkup;
+ uint64_t entry_time;
+ int entry_idx;
+ int nsamp;
+ int flag;
+};
+
+struct lpm_cluster {
+ struct list_head list;
+ struct list_head child;
+ const char *cluster_name;
+ const char **name;
+ unsigned long aff_level; /* Affinity level of the node */
+ struct low_power_ops *lpm_dev;
+ int ndevices;
+ struct lpm_cluster_level levels[NR_LPM_LEVELS];
+ int nlevels;
+ enum msm_pm_l2_scm_flag l2_flag;
+ int min_child_level;
+ int default_level;
+ int last_level;
+ struct lpm_cpu *cpu;
+ struct cpuidle_driver *drv;
+ spinlock_t sync_lock;
+ struct cpumask child_cpus;
+ struct cpumask num_children_in_sync;
+ struct lpm_cluster *parent;
+ struct lpm_stats *stats;
+ unsigned int psci_mode_shift;
+ unsigned int psci_mode_mask;
+ bool no_saw_devices;
+ struct cluster_history history;
+ struct hrtimer histtimer;
+};
+
+int set_l2_mode(struct low_power_ops *ops, int mode, bool notify_rpm);
+int set_system_mode(struct low_power_ops *ops, int mode, bool notify_rpm);
+int set_l3_mode(struct low_power_ops *ops, int mode, bool notify_rpm);
+void lpm_suspend_wake_time(uint64_t wakeup_time);
+
+struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev);
+void free_cluster_node(struct lpm_cluster *cluster);
+void cluster_dt_walkthrough(struct lpm_cluster *cluster);
+
+int create_cluster_lvl_nodes(struct lpm_cluster *p, struct kobject *kobj);
+bool lpm_cpu_mode_allow(unsigned int cpu,
+ unsigned int mode, bool from_idle);
+bool lpm_cluster_mode_allow(struct lpm_cluster *cluster,
+ unsigned int mode, bool from_idle);
+uint32_t *get_per_cpu_max_residency(int cpu);
+uint32_t *get_per_cpu_min_residency(int cpu);
+extern struct lpm_cluster *lpm_root_node;
+
+#if CONFIG_SMP
+extern DEFINE_PER_CPU(bool, pending_ipi);
+static inline bool is_IPI_pending(const struct cpumask *mask)
+{
+ unsigned int cpu;
+
+ for_each_cpu(cpu, mask) {
+ if per_cpu(pending_ipi, cpu)
+ return true;
+ }
+ return false;
+}
+#else
+static inline bool is_IPI_pending(const struct cpumask *mask)
+{
+ return false;
+}
+#endif
diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c
index 954a64c..c310318 100644
--- a/drivers/crypto/caam/caamalg.c
+++ b/drivers/crypto/caam/caamalg.c
@@ -736,7 +736,9 @@
/* Will read cryptlen */
append_math_add(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
- aead_append_src_dst(desc, FIFOLD_TYPE_MSG1OUT2);
+ append_seq_fifo_load(desc, 0, FIFOLD_CLASS_BOTH | KEY_VLF |
+ FIFOLD_TYPE_MSG1OUT2 | FIFOLD_TYPE_LASTBOTH);
+ append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | KEY_VLF);
/* Write ICV */
append_seq_store(desc, ctx->authsize, LDST_CLASS_2_CCB |
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index bf3ea76..712592c 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -593,11 +593,16 @@
list_add(&devfreq->node, &devfreq_list);
governor = find_devfreq_governor(devfreq->governor_name);
- if (!IS_ERR(governor))
- devfreq->governor = governor;
- if (devfreq->governor)
- err = devfreq->governor->event_handler(devfreq,
- DEVFREQ_GOV_START, NULL);
+ if (IS_ERR(governor)) {
+ dev_err(dev, "%s: Unable to find governor for the device\n",
+ __func__);
+ err = PTR_ERR(governor);
+ goto err_init;
+ }
+
+ devfreq->governor = governor;
+ err = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_START,
+ NULL);
if (err) {
dev_err(dev, "%s: Unable to start governor for the device\n",
__func__);
diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c
index 29866f7..1b21bb6 100644
--- a/drivers/devfreq/exynos-bus.c
+++ b/drivers/devfreq/exynos-bus.c
@@ -498,7 +498,7 @@
if (IS_ERR(bus->devfreq)) {
dev_err(dev,
"failed to add devfreq dev with passive governor\n");
- ret = -EPROBE_DEFER;
+ ret = PTR_ERR(bus->devfreq);
goto err;
}
diff --git a/drivers/dma/cppi41.c b/drivers/dma/cppi41.c
index d5ba43a..55c1782 100644
--- a/drivers/dma/cppi41.c
+++ b/drivers/dma/cppi41.c
@@ -153,6 +153,8 @@
/* context for suspend/resume */
unsigned int dma_tdfdq;
+
+ bool is_suspended;
};
#define FIST_COMPLETION_QUEUE 93
@@ -257,6 +259,10 @@
BUG_ON(desc_num >= ALLOC_DECS_NUM);
c = cdd->chan_busy[desc_num];
cdd->chan_busy[desc_num] = NULL;
+
+ /* Usecount for chan_busy[], paired with push_desc_queue() */
+ pm_runtime_put(cdd->ddev.dev);
+
return c;
}
@@ -447,6 +453,15 @@
*/
__iowmb();
+ /*
+ * DMA transfers can take at least 200ms to complete with USB mass
+ * storage connected. To prevent autosuspend timeouts, we must use
+ * pm_runtime_get/put() when chan_busy[] is modified. This will get
+ * cleared in desc_to_chan() or cppi41_stop_chan() depending on the
+ * outcome of the transfer.
+ */
+ pm_runtime_get(cdd->ddev.dev);
+
desc_phys = lower_32_bits(c->desc_phys);
desc_num = (desc_phys - cdd->descs_phys) / sizeof(struct cppi41_desc);
WARN_ON(cdd->chan_busy[desc_num]);
@@ -457,20 +472,26 @@
cppi_writel(reg, cdd->qmgr_mem + QMGR_QUEUE_D(c->q_num));
}
-static void pending_desc(struct cppi41_channel *c)
+/*
+ * Caller must hold cdd->lock to prevent push_desc_queue()
+ * getting called out of order. We have both cppi41_dma_issue_pending()
+ * and cppi41_runtime_resume() call this function.
+ */
+static void cppi41_run_queue(struct cppi41_dd *cdd)
{
- struct cppi41_dd *cdd = c->cdd;
- unsigned long flags;
+ struct cppi41_channel *c, *_c;
- spin_lock_irqsave(&cdd->lock, flags);
- list_add_tail(&c->node, &cdd->pending);
- spin_unlock_irqrestore(&cdd->lock, flags);
+ list_for_each_entry_safe(c, _c, &cdd->pending, node) {
+ push_desc_queue(c);
+ list_del(&c->node);
+ }
}
static void cppi41_dma_issue_pending(struct dma_chan *chan)
{
struct cppi41_channel *c = to_cpp41_chan(chan);
struct cppi41_dd *cdd = c->cdd;
+ unsigned long flags;
int error;
error = pm_runtime_get(cdd->ddev.dev);
@@ -482,10 +503,11 @@
return;
}
- if (likely(pm_runtime_active(cdd->ddev.dev)))
- push_desc_queue(c);
- else
- pending_desc(c);
+ spin_lock_irqsave(&cdd->lock, flags);
+ list_add_tail(&c->node, &cdd->pending);
+ if (!cdd->is_suspended)
+ cppi41_run_queue(cdd);
+ spin_unlock_irqrestore(&cdd->lock, flags);
pm_runtime_mark_last_busy(cdd->ddev.dev);
pm_runtime_put_autosuspend(cdd->ddev.dev);
@@ -705,6 +727,9 @@
WARN_ON(!cdd->chan_busy[desc_num]);
cdd->chan_busy[desc_num] = NULL;
+ /* Usecount for chan_busy[], paired with push_desc_queue() */
+ pm_runtime_put(cdd->ddev.dev);
+
return 0;
}
@@ -1150,8 +1175,12 @@
static int __maybe_unused cppi41_runtime_suspend(struct device *dev)
{
struct cppi41_dd *cdd = dev_get_drvdata(dev);
+ unsigned long flags;
+ spin_lock_irqsave(&cdd->lock, flags);
+ cdd->is_suspended = true;
WARN_ON(!list_empty(&cdd->pending));
+ spin_unlock_irqrestore(&cdd->lock, flags);
return 0;
}
@@ -1159,14 +1188,11 @@
static int __maybe_unused cppi41_runtime_resume(struct device *dev)
{
struct cppi41_dd *cdd = dev_get_drvdata(dev);
- struct cppi41_channel *c, *_c;
unsigned long flags;
spin_lock_irqsave(&cdd->lock, flags);
- list_for_each_entry_safe(c, _c, &cdd->pending, node) {
- push_desc_queue(c);
- list_del(&c->node);
- }
+ cdd->is_suspended = false;
+ cppi41_run_queue(cdd);
spin_unlock_irqrestore(&cdd->lock, flags);
return 0;
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c
index 7ca27d4..6b16ce3 100644
--- a/drivers/dma/omap-dma.c
+++ b/drivers/dma/omap-dma.c
@@ -1339,6 +1339,7 @@
struct omap_dmadev *od;
struct resource *res;
int rc, i, irq;
+ u32 lch_count;
od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL);
if (!od)
@@ -1381,20 +1382,31 @@
spin_lock_init(&od->lock);
spin_lock_init(&od->irq_lock);
- if (!pdev->dev.of_node) {
- od->dma_requests = od->plat->dma_attr->lch_count;
- if (unlikely(!od->dma_requests))
- od->dma_requests = OMAP_SDMA_REQUESTS;
- } else if (of_property_read_u32(pdev->dev.of_node, "dma-requests",
- &od->dma_requests)) {
+ /* Number of DMA requests */
+ od->dma_requests = OMAP_SDMA_REQUESTS;
+ if (pdev->dev.of_node && of_property_read_u32(pdev->dev.of_node,
+ "dma-requests",
+ &od->dma_requests)) {
dev_info(&pdev->dev,
"Missing dma-requests property, using %u.\n",
OMAP_SDMA_REQUESTS);
- od->dma_requests = OMAP_SDMA_REQUESTS;
}
- od->lch_map = devm_kcalloc(&pdev->dev, od->dma_requests,
- sizeof(*od->lch_map), GFP_KERNEL);
+ /* Number of available logical channels */
+ if (!pdev->dev.of_node) {
+ lch_count = od->plat->dma_attr->lch_count;
+ if (unlikely(!lch_count))
+ lch_count = OMAP_SDMA_CHANNELS;
+ } else if (of_property_read_u32(pdev->dev.of_node, "dma-channels",
+ &lch_count)) {
+ dev_info(&pdev->dev,
+ "Missing dma-channels property, using %u.\n",
+ OMAP_SDMA_CHANNELS);
+ lch_count = OMAP_SDMA_CHANNELS;
+ }
+
+ od->lch_map = devm_kcalloc(&pdev->dev, lch_count, sizeof(*od->lch_map),
+ GFP_KERNEL);
if (!od->lch_map)
return -ENOMEM;
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 030fe05..9f3dbc8 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -448,6 +448,9 @@
/* for cyclic capability */
bool cyclic;
+
+ /* for runtime pm tracking */
+ bool active;
};
struct pl330_dmac {
@@ -2031,6 +2034,7 @@
_stop(pch->thread);
spin_unlock(&pch->thread->dmac->lock);
power_down = true;
+ pch->active = false;
} else {
/* Make sure the PL330 Channel thread is active */
spin_lock(&pch->thread->dmac->lock);
@@ -2050,6 +2054,7 @@
desc->status = PREP;
list_move_tail(&desc->node, &pch->work_list);
if (power_down) {
+ pch->active = true;
spin_lock(&pch->thread->dmac->lock);
_start(pch->thread);
spin_unlock(&pch->thread->dmac->lock);
@@ -2164,6 +2169,7 @@
unsigned long flags;
struct pl330_dmac *pl330 = pch->dmac;
LIST_HEAD(list);
+ bool power_down = false;
pm_runtime_get_sync(pl330->ddma.dev);
spin_lock_irqsave(&pch->lock, flags);
@@ -2174,6 +2180,8 @@
pch->thread->req[0].desc = NULL;
pch->thread->req[1].desc = NULL;
pch->thread->req_running = -1;
+ power_down = pch->active;
+ pch->active = false;
/* Mark all desc done */
list_for_each_entry(desc, &pch->submitted_list, node) {
@@ -2191,6 +2199,8 @@
list_splice_tail_init(&pch->completed_list, &pl330->desc_pool);
spin_unlock_irqrestore(&pch->lock, flags);
pm_runtime_mark_last_busy(pl330->ddma.dev);
+ if (power_down)
+ pm_runtime_put_autosuspend(pl330->ddma.dev);
pm_runtime_put_autosuspend(pl330->ddma.dev);
return 0;
@@ -2350,6 +2360,7 @@
* updated on work_list emptiness status.
*/
WARN_ON(list_empty(&pch->submitted_list));
+ pch->active = true;
pm_runtime_get_sync(pch->dmac->ddma.dev);
}
list_splice_tail_init(&pch->submitted_list, &pch->work_list);
diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c
index 2e441d0..4c357d4 100644
--- a/drivers/dma/sh/rcar-dmac.c
+++ b/drivers/dma/sh/rcar-dmac.c
@@ -986,6 +986,7 @@
{
struct rcar_dmac_chan *rchan = to_rcar_dmac_chan(chan);
struct rcar_dmac *dmac = to_rcar_dmac(chan->device);
+ struct rcar_dmac_chan_map *map = &rchan->map;
struct rcar_dmac_desc_page *page, *_page;
struct rcar_dmac_desc *desc;
LIST_HEAD(list);
@@ -1019,6 +1020,13 @@
free_page((unsigned long)page);
}
+ /* Remove slave mapping if present. */
+ if (map->slave.xfer_size) {
+ dma_unmap_resource(chan->device->dev, map->addr,
+ map->slave.xfer_size, map->dir, 0);
+ map->slave.xfer_size = 0;
+ }
+
pm_runtime_put(chan->device->dev);
}
diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
index 7829846..7c1e3a7 100644
--- a/drivers/extcon/extcon.c
+++ b/drivers/extcon/extcon.c
@@ -453,7 +453,7 @@
dev_err(&edev->dev, "out of memory in extcon_set_state\n");
kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
- return 0;
+ return -ENOMEM;
}
length = name_show(&edev->dev, NULL, prop_buf);
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 1ac199c..a4944e2 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -259,8 +259,10 @@
}
data = kmalloc(size, GFP_KERNEL);
- if (!data)
+ if (!data) {
+ ret = -ENOMEM;
goto free_entry;
+ }
ret = efivar_entry_get(entry, NULL, &size, data);
if (ret) {
diff --git a/drivers/firmware/efi/fake_mem.c b/drivers/firmware/efi/fake_mem.c
index 520a40e..6c7d60c 100644
--- a/drivers/firmware/efi/fake_mem.c
+++ b/drivers/firmware/efi/fake_mem.c
@@ -71,8 +71,7 @@
}
/* allocate memory for new EFI memmap */
- new_memmap_phy = memblock_alloc(efi.memmap.desc_size * new_nr_map,
- PAGE_SIZE);
+ new_memmap_phy = efi_memmap_alloc(new_nr_map);
if (!new_memmap_phy)
return;
diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
index ee49cd2..fac6799 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -30,14 +30,6 @@
unsigned long get_dram_base(efi_system_table_t *sys_table_arg);
-efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
- unsigned long orig_fdt_size,
- void *fdt, int new_fdt_size, char *cmdline_ptr,
- u64 initrd_addr, u64 initrd_size,
- efi_memory_desc_t *memory_map,
- unsigned long map_size, unsigned long desc_size,
- u32 desc_ver);
-
efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
void *handle,
unsigned long *new_fdt_addr,
diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c
index a6a9311..260c4b4 100644
--- a/drivers/firmware/efi/libstub/fdt.c
+++ b/drivers/firmware/efi/libstub/fdt.c
@@ -16,13 +16,10 @@
#include "efistub.h"
-efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
- unsigned long orig_fdt_size,
- void *fdt, int new_fdt_size, char *cmdline_ptr,
- u64 initrd_addr, u64 initrd_size,
- efi_memory_desc_t *memory_map,
- unsigned long map_size, unsigned long desc_size,
- u32 desc_ver)
+static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
+ unsigned long orig_fdt_size,
+ void *fdt, int new_fdt_size, char *cmdline_ptr,
+ u64 initrd_addr, u64 initrd_size)
{
int node, num_rsv;
int status;
@@ -101,25 +98,23 @@
if (status)
goto fdt_set_fail;
- fdt_val64 = cpu_to_fdt64((u64)(unsigned long)memory_map);
+ fdt_val64 = U64_MAX; /* placeholder */
status = fdt_setprop(fdt, node, "linux,uefi-mmap-start",
&fdt_val64, sizeof(fdt_val64));
if (status)
goto fdt_set_fail;
- fdt_val32 = cpu_to_fdt32(map_size);
+ fdt_val32 = U32_MAX; /* placeholder */
status = fdt_setprop(fdt, node, "linux,uefi-mmap-size",
&fdt_val32, sizeof(fdt_val32));
if (status)
goto fdt_set_fail;
- fdt_val32 = cpu_to_fdt32(desc_size);
status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-size",
&fdt_val32, sizeof(fdt_val32));
if (status)
goto fdt_set_fail;
- fdt_val32 = cpu_to_fdt32(desc_ver);
status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-ver",
&fdt_val32, sizeof(fdt_val32));
if (status)
@@ -148,6 +143,43 @@
return EFI_LOAD_ERROR;
}
+static efi_status_t update_fdt_memmap(void *fdt, struct efi_boot_memmap *map)
+{
+ int node = fdt_path_offset(fdt, "/chosen");
+ u64 fdt_val64;
+ u32 fdt_val32;
+ int err;
+
+ if (node < 0)
+ return EFI_LOAD_ERROR;
+
+ fdt_val64 = cpu_to_fdt64((unsigned long)*map->map);
+ err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-start",
+ &fdt_val64, sizeof(fdt_val64));
+ if (err)
+ return EFI_LOAD_ERROR;
+
+ fdt_val32 = cpu_to_fdt32(*map->map_size);
+ err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-size",
+ &fdt_val32, sizeof(fdt_val32));
+ if (err)
+ return EFI_LOAD_ERROR;
+
+ fdt_val32 = cpu_to_fdt32(*map->desc_size);
+ err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-desc-size",
+ &fdt_val32, sizeof(fdt_val32));
+ if (err)
+ return EFI_LOAD_ERROR;
+
+ fdt_val32 = cpu_to_fdt32(*map->desc_ver);
+ err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-desc-ver",
+ &fdt_val32, sizeof(fdt_val32));
+ if (err)
+ return EFI_LOAD_ERROR;
+
+ return EFI_SUCCESS;
+}
+
#ifndef EFI_FDT_ALIGN
#define EFI_FDT_ALIGN EFI_PAGE_SIZE
#endif
@@ -155,6 +187,7 @@
struct exit_boot_struct {
efi_memory_desc_t *runtime_map;
int *runtime_entry_count;
+ void *new_fdt_addr;
};
static efi_status_t exit_boot_func(efi_system_table_t *sys_table_arg,
@@ -170,7 +203,7 @@
efi_get_virtmap(*map->map, *map->map_size, *map->desc_size,
p->runtime_map, p->runtime_entry_count);
- return EFI_SUCCESS;
+ return update_fdt_memmap(p->new_fdt_addr, map);
}
/*
@@ -243,20 +276,10 @@
goto fail;
}
- /*
- * Now that we have done our final memory allocation (and free)
- * we can get the memory map key needed for
- * exit_boot_services().
- */
- status = efi_get_memory_map(sys_table, &map);
- if (status != EFI_SUCCESS)
- goto fail_free_new_fdt;
-
status = update_fdt(sys_table,
(void *)fdt_addr, fdt_size,
(void *)*new_fdt_addr, new_fdt_size,
- cmdline_ptr, initrd_addr, initrd_size,
- memory_map, map_size, desc_size, desc_ver);
+ cmdline_ptr, initrd_addr, initrd_size);
/* Succeeding the first time is the expected case. */
if (status == EFI_SUCCESS)
@@ -266,22 +289,19 @@
/*
* We need to allocate more space for the new
* device tree, so free existing buffer that is
- * too small. Also free memory map, as we will need
- * to get new one that reflects the free/alloc we do
- * on the device tree buffer.
+ * too small.
*/
efi_free(sys_table, new_fdt_size, *new_fdt_addr);
- sys_table->boottime->free_pool(memory_map);
new_fdt_size += EFI_PAGE_SIZE;
} else {
pr_efi_err(sys_table, "Unable to construct new device tree.\n");
- goto fail_free_mmap;
+ goto fail_free_new_fdt;
}
}
- sys_table->boottime->free_pool(memory_map);
priv.runtime_map = runtime_map;
priv.runtime_entry_count = &runtime_entry_count;
+ priv.new_fdt_addr = (void *)*new_fdt_addr;
status = efi_exit_boot_services(sys_table, handle, &map, &priv,
exit_boot_func);
@@ -319,9 +339,6 @@
pr_efi_err(sys_table, "Exit boot services failed.\n");
-fail_free_mmap:
- sys_table->boottime->free_pool(memory_map);
-
fail_free_new_fdt:
efi_free(sys_table, new_fdt_size, *new_fdt_addr);
diff --git a/drivers/firmware/efi/memmap.c b/drivers/firmware/efi/memmap.c
index f03ddec..7868644 100644
--- a/drivers/firmware/efi/memmap.c
+++ b/drivers/firmware/efi/memmap.c
@@ -9,6 +9,44 @@
#include <linux/efi.h>
#include <linux/io.h>
#include <asm/early_ioremap.h>
+#include <linux/memblock.h>
+#include <linux/slab.h>
+
+static phys_addr_t __init __efi_memmap_alloc_early(unsigned long size)
+{
+ return memblock_alloc(size, 0);
+}
+
+static phys_addr_t __init __efi_memmap_alloc_late(unsigned long size)
+{
+ unsigned int order = get_order(size);
+ struct page *p = alloc_pages(GFP_KERNEL, order);
+
+ if (!p)
+ return 0;
+
+ return PFN_PHYS(page_to_pfn(p));
+}
+
+/**
+ * efi_memmap_alloc - Allocate memory for the EFI memory map
+ * @num_entries: Number of entries in the allocated map.
+ *
+ * Depending on whether mm_init() has already been invoked or not,
+ * either memblock or "normal" page allocation is used.
+ *
+ * Returns the physical address of the allocated memory map on
+ * success, zero on failure.
+ */
+phys_addr_t __init efi_memmap_alloc(unsigned int num_entries)
+{
+ unsigned long size = num_entries * efi.memmap.desc_size;
+
+ if (slab_is_available())
+ return __efi_memmap_alloc_late(size);
+
+ return __efi_memmap_alloc_early(size);
+}
/**
* __efi_memmap_init - Common code for mapping the EFI memory map
diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c
index 5b00427..adba614 100644
--- a/drivers/gpio/gpio-stmpe.c
+++ b/drivers/gpio/gpio-stmpe.c
@@ -413,7 +413,7 @@
stmpe->partnum != STMPE1801) {
stmpe_reg_write(stmpe, statmsbreg + i, status[i]);
stmpe_reg_write(stmpe,
- stmpe->regs[STMPE_IDX_GPEDR_LSB + i],
+ stmpe->regs[STMPE_IDX_GPEDR_MSB] + i,
status[i]);
}
}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 868128a..9215931 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -986,7 +986,8 @@
return -ENODEV;
get_device(&gdev->dev);
filp->private_data = gdev;
- return 0;
+
+ return nonseekable_open(inode, filp);
}
/**
@@ -1011,7 +1012,7 @@
.release = gpio_chrdev_release,
.open = gpio_chrdev_open,
.owner = THIS_MODULE,
- .llseek = noop_llseek,
+ .llseek = no_llseek,
.unlocked_ioctl = gpio_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = gpio_ioctl_compat,
@@ -1316,12 +1317,12 @@
/* FIXME: should the legacy sysfs handling be moved to gpio_device? */
gpiochip_sysfs_unregister(gdev);
+ gpiochip_free_hogs(chip);
/* Numb the device, cancelling all outstanding operations */
gdev->chip = NULL;
gpiochip_irqchip_remove(chip);
acpi_gpiochip_remove(chip);
gpiochip_remove_pin_ranges(chip);
- gpiochip_free_hogs(chip);
of_gpiochip_remove(chip);
/*
* We accept no more calls into the driver from this point, so
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
index 9260cae..882404c 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
@@ -2577,6 +2577,9 @@
struct amdgpu_device *adev = crtc->dev->dev_private;
int xorigin = 0, yorigin = 0;
+ amdgpu_crtc->cursor_x = x;
+ amdgpu_crtc->cursor_y = y;
+
/* avivo cursor are offset into the total surface */
x += crtc->x;
y += crtc->y;
@@ -2596,9 +2599,6 @@
WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset,
((amdgpu_crtc->cursor_width - 1) << 16) | (amdgpu_crtc->cursor_height - 1));
- amdgpu_crtc->cursor_x = x;
- amdgpu_crtc->cursor_y = y;
-
return 0;
}
@@ -2661,12 +2661,11 @@
return ret;
}
- amdgpu_crtc->cursor_width = width;
- amdgpu_crtc->cursor_height = height;
-
dce_v10_0_lock_cursor(crtc, true);
- if (hot_x != amdgpu_crtc->cursor_hot_x ||
+ if (width != amdgpu_crtc->cursor_width ||
+ height != amdgpu_crtc->cursor_height ||
+ hot_x != amdgpu_crtc->cursor_hot_x ||
hot_y != amdgpu_crtc->cursor_hot_y) {
int x, y;
@@ -2675,6 +2674,8 @@
dce_v10_0_cursor_move_locked(crtc, x, y);
+ amdgpu_crtc->cursor_width = width;
+ amdgpu_crtc->cursor_height = height;
amdgpu_crtc->cursor_hot_x = hot_x;
amdgpu_crtc->cursor_hot_y = hot_y;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
index 367739b..7ddc321 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
@@ -2593,6 +2593,9 @@
struct amdgpu_device *adev = crtc->dev->dev_private;
int xorigin = 0, yorigin = 0;
+ amdgpu_crtc->cursor_x = x;
+ amdgpu_crtc->cursor_y = y;
+
/* avivo cursor are offset into the total surface */
x += crtc->x;
y += crtc->y;
@@ -2612,9 +2615,6 @@
WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset,
((amdgpu_crtc->cursor_width - 1) << 16) | (amdgpu_crtc->cursor_height - 1));
- amdgpu_crtc->cursor_x = x;
- amdgpu_crtc->cursor_y = y;
-
return 0;
}
@@ -2677,12 +2677,11 @@
return ret;
}
- amdgpu_crtc->cursor_width = width;
- amdgpu_crtc->cursor_height = height;
-
dce_v11_0_lock_cursor(crtc, true);
- if (hot_x != amdgpu_crtc->cursor_hot_x ||
+ if (width != amdgpu_crtc->cursor_width ||
+ height != amdgpu_crtc->cursor_height ||
+ hot_x != amdgpu_crtc->cursor_hot_x ||
hot_y != amdgpu_crtc->cursor_hot_y) {
int x, y;
@@ -2691,6 +2690,8 @@
dce_v11_0_cursor_move_locked(crtc, x, y);
+ amdgpu_crtc->cursor_width = width;
+ amdgpu_crtc->cursor_height = height;
amdgpu_crtc->cursor_hot_x = hot_x;
amdgpu_crtc->cursor_hot_y = hot_y;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
index 15f9fc0..fde6ee1 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
@@ -1933,6 +1933,9 @@
int w = amdgpu_crtc->cursor_width;
+ amdgpu_crtc->cursor_x = x;
+ amdgpu_crtc->cursor_y = y;
+
/* avivo cursor are offset into the total surface */
x += crtc->x;
y += crtc->y;
@@ -1952,8 +1955,6 @@
WREG32(EVERGREEN_CUR_SIZE + amdgpu_crtc->crtc_offset,
((w - 1) << 16) | (amdgpu_crtc->cursor_height - 1));
- amdgpu_crtc->cursor_x = x;
- amdgpu_crtc->cursor_y = y;
return 0;
}
@@ -2016,12 +2017,11 @@
return ret;
}
- amdgpu_crtc->cursor_width = width;
- amdgpu_crtc->cursor_height = height;
-
dce_v6_0_lock_cursor(crtc, true);
- if (hot_x != amdgpu_crtc->cursor_hot_x ||
+ if (width != amdgpu_crtc->cursor_width ||
+ height != amdgpu_crtc->cursor_height ||
+ hot_x != amdgpu_crtc->cursor_hot_x ||
hot_y != amdgpu_crtc->cursor_hot_y) {
int x, y;
@@ -2030,6 +2030,8 @@
dce_v6_0_cursor_move_locked(crtc, x, y);
+ amdgpu_crtc->cursor_width = width;
+ amdgpu_crtc->cursor_height = height;
amdgpu_crtc->cursor_hot_x = hot_x;
amdgpu_crtc->cursor_hot_y = hot_y;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
index 8c4d808..7d9ffde 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
@@ -2465,6 +2465,9 @@
struct amdgpu_device *adev = crtc->dev->dev_private;
int xorigin = 0, yorigin = 0;
+ amdgpu_crtc->cursor_x = x;
+ amdgpu_crtc->cursor_y = y;
+
/* avivo cursor are offset into the total surface */
x += crtc->x;
y += crtc->y;
@@ -2484,9 +2487,6 @@
WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset,
((amdgpu_crtc->cursor_width - 1) << 16) | (amdgpu_crtc->cursor_height - 1));
- amdgpu_crtc->cursor_x = x;
- amdgpu_crtc->cursor_y = y;
-
return 0;
}
@@ -2549,12 +2549,11 @@
return ret;
}
- amdgpu_crtc->cursor_width = width;
- amdgpu_crtc->cursor_height = height;
-
dce_v8_0_lock_cursor(crtc, true);
- if (hot_x != amdgpu_crtc->cursor_hot_x ||
+ if (width != amdgpu_crtc->cursor_width ||
+ height != amdgpu_crtc->cursor_height ||
+ hot_x != amdgpu_crtc->cursor_hot_x ||
hot_y != amdgpu_crtc->cursor_hot_y) {
int x, y;
@@ -2563,6 +2562,8 @@
dce_v8_0_cursor_move_locked(crtc, x, y);
+ amdgpu_crtc->cursor_width = width;
+ amdgpu_crtc->cursor_height = height;
amdgpu_crtc->cursor_hot_x = hot_x;
amdgpu_crtc->cursor_hot_y = hot_y;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
index bb97182..a88d365 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
@@ -3947,8 +3947,12 @@
temp = mmRLC_SRM_INDEX_CNTL_ADDR_0;
data = mmRLC_SRM_INDEX_CNTL_DATA_0;
for (i = 0; i < sizeof(unique_indices) / sizeof(int); i++) {
- amdgpu_mm_wreg(adev, temp + i, unique_indices[i] & 0x3FFFF, false);
- amdgpu_mm_wreg(adev, data + i, unique_indices[i] >> 20, false);
+ if (unique_indices[i] != 0) {
+ amdgpu_mm_wreg(adev, temp + i,
+ unique_indices[i] & 0x3FFFF, false);
+ amdgpu_mm_wreg(adev, data + i,
+ unique_indices[i] >> 20, false);
+ }
}
kfree(register_list_format);
@@ -3994,7 +3998,7 @@
static void cz_enable_cp_power_gating(struct amdgpu_device *adev, bool enable)
{
- WREG32_FIELD(RLC_PG_CNTL, CP_PG_DISABLE, enable ? 1 : 0);
+ WREG32_FIELD(RLC_PG_CNTL, CP_PG_DISABLE, enable ? 0 : 1);
}
static void gfx_v8_0_init_pg(struct amdgpu_device *adev)
@@ -5891,29 +5895,24 @@
adev->gfx.rlc.funcs->enter_safe_mode(adev);
if (enable && (adev->cg_flags & AMD_CG_SUPPORT_GFX_CGCG)) {
- /* 1 enable cntx_empty_int_enable/cntx_busy_int_enable/
- * Cmp_busy/GFX_Idle interrupts
- */
- gfx_v8_0_enable_gui_idle_interrupt(adev, true);
-
temp1 = data1 = RREG32(mmRLC_CGTT_MGCG_OVERRIDE);
data1 &= ~RLC_CGTT_MGCG_OVERRIDE__CGCG_MASK;
if (temp1 != data1)
WREG32(mmRLC_CGTT_MGCG_OVERRIDE, data1);
- /* 2 wait for RLC_SERDES_CU_MASTER & RLC_SERDES_NONCU_MASTER idle */
+ /* : wait for RLC_SERDES_CU_MASTER & RLC_SERDES_NONCU_MASTER idle */
gfx_v8_0_wait_for_rlc_serdes(adev);
- /* 3 - clear cgcg override */
+ /* 2 - clear cgcg override */
gfx_v8_0_send_serdes_cmd(adev, BPM_REG_CGCG_OVERRIDE, CLE_BPM_SERDES_CMD);
/* wait for RLC_SERDES_CU_MASTER & RLC_SERDES_NONCU_MASTER idle */
gfx_v8_0_wait_for_rlc_serdes(adev);
- /* 4 - write cmd to set CGLS */
+ /* 3 - write cmd to set CGLS */
gfx_v8_0_send_serdes_cmd(adev, BPM_REG_CGLS_EN, SET_BPM_SERDES_CMD);
- /* 5 - enable cgcg */
+ /* 4 - enable cgcg */
data |= RLC_CGCG_CGLS_CTRL__CGCG_EN_MASK;
if (adev->cg_flags & AMD_CG_SUPPORT_GFX_CGLS) {
@@ -5931,6 +5930,11 @@
if (temp != data)
WREG32(mmRLC_CGCG_CGLS_CTRL, data);
+
+ /* 5 enable cntx_empty_int_enable/cntx_busy_int_enable/
+ * Cmp_busy/GFX_Idle interrupts
+ */
+ gfx_v8_0_enable_gui_idle_interrupt(adev, true);
} else {
/* disable cntx_empty_int_enable & GFX Idle interrupt */
gfx_v8_0_enable_gui_idle_interrupt(adev, false);
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c
index b13c8aa..6df924f 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c
@@ -227,6 +227,9 @@
}
WREG32(HDP_REG_COHERENCY_FLUSH_CNTL, 0);
+ if (adev->mode_info.num_crtc)
+ amdgpu_display_set_vga_render_state(adev, false);
+
gmc_v6_0_mc_stop(adev, &save);
if (gmc_v6_0_wait_for_idle((void *)adev)) {
@@ -256,7 +259,6 @@
dev_warn(adev->dev, "Wait for MC idle timedout !\n");
}
gmc_v6_0_mc_resume(adev, &save);
- amdgpu_display_set_vga_render_state(adev, false);
}
static int gmc_v6_0_mc_init(struct amdgpu_device *adev)
diff --git a/drivers/gpu/drm/amd/amdgpu/si_dpm.c b/drivers/gpu/drm/amd/amdgpu/si_dpm.c
index d6f85b1..b447a01 100644
--- a/drivers/gpu/drm/amd/amdgpu/si_dpm.c
+++ b/drivers/gpu/drm/amd/amdgpu/si_dpm.c
@@ -56,7 +56,6 @@
#define BIOS_SCRATCH_4 0x5cd
MODULE_FIRMWARE("radeon/tahiti_smc.bin");
-MODULE_FIRMWARE("radeon/tahiti_k_smc.bin");
MODULE_FIRMWARE("radeon/pitcairn_smc.bin");
MODULE_FIRMWARE("radeon/pitcairn_k_smc.bin");
MODULE_FIRMWARE("radeon/verde_smc.bin");
@@ -3486,24 +3485,12 @@
(adev->pdev->device == 0x6817) ||
(adev->pdev->device == 0x6806))
max_mclk = 120000;
- } else if (adev->asic_type == CHIP_VERDE) {
- if ((adev->pdev->revision == 0x81) ||
- (adev->pdev->revision == 0x83) ||
- (adev->pdev->revision == 0x87) ||
- (adev->pdev->device == 0x6820) ||
- (adev->pdev->device == 0x6821) ||
- (adev->pdev->device == 0x6822) ||
- (adev->pdev->device == 0x6823) ||
- (adev->pdev->device == 0x682A) ||
- (adev->pdev->device == 0x682B)) {
- max_sclk = 75000;
- max_mclk = 80000;
- }
} else if (adev->asic_type == CHIP_OLAND) {
if ((adev->pdev->revision == 0xC7) ||
(adev->pdev->revision == 0x80) ||
(adev->pdev->revision == 0x81) ||
(adev->pdev->revision == 0x83) ||
+ (adev->pdev->revision == 0x87) ||
(adev->pdev->device == 0x6604) ||
(adev->pdev->device == 0x6605)) {
max_sclk = 75000;
@@ -7684,48 +7671,49 @@
chip_name = "tahiti";
break;
case CHIP_PITCAIRN:
- if ((adev->pdev->revision == 0x81) ||
- (adev->pdev->device == 0x6810) ||
- (adev->pdev->device == 0x6811) ||
- (adev->pdev->device == 0x6816) ||
- (adev->pdev->device == 0x6817) ||
- (adev->pdev->device == 0x6806))
+ if ((adev->pdev->revision == 0x81) &&
+ ((adev->pdev->device == 0x6810) ||
+ (adev->pdev->device == 0x6811)))
chip_name = "pitcairn_k";
else
chip_name = "pitcairn";
break;
case CHIP_VERDE:
- if ((adev->pdev->revision == 0x81) ||
- (adev->pdev->revision == 0x83) ||
- (adev->pdev->revision == 0x87) ||
- (adev->pdev->device == 0x6820) ||
- (adev->pdev->device == 0x6821) ||
- (adev->pdev->device == 0x6822) ||
- (adev->pdev->device == 0x6823) ||
- (adev->pdev->device == 0x682A) ||
- (adev->pdev->device == 0x682B))
+ if (((adev->pdev->device == 0x6820) &&
+ ((adev->pdev->revision == 0x81) ||
+ (adev->pdev->revision == 0x83))) ||
+ ((adev->pdev->device == 0x6821) &&
+ ((adev->pdev->revision == 0x83) ||
+ (adev->pdev->revision == 0x87))) ||
+ ((adev->pdev->revision == 0x87) &&
+ ((adev->pdev->device == 0x6823) ||
+ (adev->pdev->device == 0x682b))))
chip_name = "verde_k";
else
chip_name = "verde";
break;
case CHIP_OLAND:
- if ((adev->pdev->revision == 0xC7) ||
- (adev->pdev->revision == 0x80) ||
- (adev->pdev->revision == 0x81) ||
- (adev->pdev->revision == 0x83) ||
- (adev->pdev->device == 0x6604) ||
- (adev->pdev->device == 0x6605))
+ if (((adev->pdev->revision == 0x81) &&
+ ((adev->pdev->device == 0x6600) ||
+ (adev->pdev->device == 0x6604) ||
+ (adev->pdev->device == 0x6605) ||
+ (adev->pdev->device == 0x6610))) ||
+ ((adev->pdev->revision == 0x83) &&
+ (adev->pdev->device == 0x6610)))
chip_name = "oland_k";
else
chip_name = "oland";
break;
case CHIP_HAINAN:
- if ((adev->pdev->revision == 0x81) ||
- (adev->pdev->revision == 0x83) ||
- (adev->pdev->revision == 0xC3) ||
- (adev->pdev->device == 0x6664) ||
- (adev->pdev->device == 0x6665) ||
- (adev->pdev->device == 0x6667))
+ if (((adev->pdev->revision == 0x81) &&
+ (adev->pdev->device == 0x6660)) ||
+ ((adev->pdev->revision == 0x83) &&
+ ((adev->pdev->device == 0x6660) ||
+ (adev->pdev->device == 0x6663) ||
+ (adev->pdev->device == 0x6665) ||
+ (adev->pdev->device == 0x6667))) ||
+ ((adev->pdev->revision == 0xc3) &&
+ (adev->pdev->device == 0x6665)))
chip_name = "hainan_k";
else
chip_name = "hainan";
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c
index 76310ac..dca1b13 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c
@@ -1958,6 +1958,12 @@
int res;
uint64_t tmp64;
+ if (hwmgr->thermal_controller.fanInfo.bNoFan) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
if (smu_data->smu7_data.fan_table_start == 0) {
phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
PHM_PlatformCaps_MicrocodeFanControl);
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c
index 8c889ca..6c26b83 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c
@@ -2006,6 +2006,12 @@
if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl))
return 0;
+ if (hwmgr->thermal_controller.fanInfo.bNoFan) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
if (0 == smu7_data->fan_table_start) {
phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl);
return 0;
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c
index 71bb2f8..8ca1a33 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c
@@ -1885,6 +1885,12 @@
int res;
uint64_t tmp64;
+ if (hwmgr->thermal_controller.fanInfo.bNoFan) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
if (smu_data->smu7_data.fan_table_start == 0) {
phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
PHM_PlatformCaps_MicrocodeFanControl);
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c
index de2a24d..a6619e5 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c
@@ -2496,6 +2496,12 @@
PHM_PlatformCaps_MicrocodeFanControl))
return 0;
+ if (hwmgr->thermal_controller.fanInfo.bNoFan) {
+ phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+ PHM_PlatformCaps_MicrocodeFanControl);
+ return 0;
+ }
+
if (0 == smu_data->smu7_data.fan_table_start) {
phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
PHM_PlatformCaps_MicrocodeFanControl);
diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c
index 904beaa..f75c642 100644
--- a/drivers/gpu/drm/ast/ast_main.c
+++ b/drivers/gpu/drm/ast/ast_main.c
@@ -223,7 +223,8 @@
ast_write32(ast, 0x10000, 0xfc600309);
do {
- ;
+ if (pci_channel_offline(dev->pdev))
+ return -EIO;
} while (ast_read32(ast, 0x10000) != 0x01);
data = ast_read32(ast, 0x10004);
@@ -428,7 +429,9 @@
ast_detect_chip(dev, &need_post);
if (ast->chip != AST1180) {
- ast_get_dram_info(dev);
+ ret = ast_get_dram_info(dev);
+ if (ret)
+ goto out_free;
ast->vram_size = ast_get_vram_info(dev);
DRM_INFO("dram %d %d %d %08x\n", ast->mclk, ast->dram_type, ast->dram_bus_width, ast->vram_size);
}
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 21f9926..a05bb38 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1253,8 +1253,10 @@
if (!nonblock) {
ret = drm_atomic_helper_wait_for_fences(dev, state, true);
- if (ret)
+ if (ret) {
+ drm_atomic_helper_cleanup_planes(dev, state);
return ret;
+ }
}
/*
@@ -3113,6 +3115,8 @@
if (state->fb)
drm_framebuffer_reference(state->fb);
+
+ state->fence = NULL;
}
EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state);
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index b969a64..48a6167 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -952,8 +952,10 @@
u32 vblank_count;
unsigned int seq;
- if (WARN_ON(pipe >= dev->num_crtcs))
+ if (WARN_ON(pipe >= dev->num_crtcs)) {
+ *vblanktime = (struct timeval) { 0 };
return 0;
+ }
do {
seq = read_seqbegin(&vblank->seqlock);
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
index 11d44a1..ee07bb4 100644
--- a/drivers/gpu/drm/drm_mm.c
+++ b/drivers/gpu/drm/drm_mm.c
@@ -839,6 +839,7 @@
/* Clever trick to avoid a special case in the free hole tracking. */
INIT_LIST_HEAD(&mm->head_node.node_list);
+ mm->head_node.allocated = 0;
mm->head_node.hole_follows = 1;
mm->head_node.scanned_block = 0;
mm->head_node.scanned_prev_free = 0;
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 53f07ac..e14366d 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -1462,6 +1462,13 @@
return NULL;
mode->type |= DRM_MODE_TYPE_USERDEF;
+ /* fix up 1368x768: GFT/CVT can't express 1366 width due to alignment */
+ if (cmd->xres == 1366 && mode->hdisplay == 1368) {
+ mode->hdisplay = 1366;
+ mode->hsync_start--;
+ mode->hsync_end--;
+ drm_mode_set_name(mode);
+ }
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
return mode;
}
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index f6b64d7..276474d 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -143,8 +143,18 @@
}
if (dev->mode_config.delayed_event) {
+ /*
+ * FIXME:
+ *
+ * Use short (1s) delay to handle the initial delayed event.
+ * This delay should not be needed, but Optimus/nouveau will
+ * fail in a mysterious way if the delayed event is handled as
+ * soon as possible like it is done in
+ * drm_helper_probe_single_connector_modes() in case the poll
+ * was enabled before.
+ */
poll = true;
- delay = 0;
+ delay = HZ;
}
if (poll)
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
index 50eb944f..8f3ca52 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -473,6 +473,9 @@
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = psb_unlocked_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
.mmap = drm_gem_mmap,
.poll = drm_poll,
.read = drm_read,
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 18dfdd5..670beeb 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -2372,7 +2372,7 @@
assert_forcewakes_inactive(dev_priv);
- if (!IS_VALLEYVIEW(dev_priv) || !IS_CHERRYVIEW(dev_priv))
+ if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv))
intel_hpd_poll_init(dev_priv);
DRM_DEBUG_KMS("Device suspended\n");
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 685e9e06..da832d3 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -3684,6 +3684,8 @@
int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u32 mbox, u32 *val);
int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u32 mbox, u32 val);
+int skl_pcode_request(struct drm_i915_private *dev_priv, u32 mbox, u32 request,
+ u32 reply_mask, u32 reply, int timeout_base_ms);
/* intel_sideband.c */
u32 vlv_punit_read(struct drm_i915_private *dev_priv, u32 addr);
diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c
index 5b6f81c..7467355 100644
--- a/drivers/gpu/drm/i915/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/i915_gem_evict.c
@@ -194,6 +194,7 @@
}
/* Unbinding will emit any required flushes */
+ ret = 0;
while (!list_empty(&eviction_list)) {
vma = list_first_entry(&eviction_list,
struct i915_vma,
diff --git a/drivers/gpu/drm/i915/i915_gem_request.h b/drivers/gpu/drm/i915/i915_gem_request.h
index 974bd7b..59ac900 100644
--- a/drivers/gpu/drm/i915/i915_gem_request.h
+++ b/drivers/gpu/drm/i915/i915_gem_request.h
@@ -344,6 +344,25 @@
rcu_assign_pointer(active->request, request);
}
+/**
+ * i915_gem_active_set_retire_fn - updates the retirement callback
+ * @active - the active tracker
+ * @fn - the routine called when the request is retired
+ * @mutex - struct_mutex used to guard retirements
+ *
+ * i915_gem_active_set_retire_fn() updates the function pointer that
+ * is called when the final request associated with the @active tracker
+ * is retired.
+ */
+static inline void
+i915_gem_active_set_retire_fn(struct i915_gem_active *active,
+ i915_gem_retire_fn fn,
+ struct mutex *mutex)
+{
+ lockdep_assert_held(mutex);
+ active->retire = fn ?: i915_gem_retire_noop;
+}
+
static inline struct drm_i915_gem_request *
__i915_gem_active_peek(const struct i915_gem_active *active)
{
diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c
index 59989e8..9a71ed5 100644
--- a/drivers/gpu/drm/i915/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/i915_gem_stolen.c
@@ -55,10 +55,9 @@
return -ENODEV;
/* See the comment at the drm_mm_init() call for more about this check.
- * WaSkipStolenMemoryFirstPage:bdw,chv,kbl (incomplete)
+ * WaSkipStolenMemoryFirstPage:bdw+ (incomplete)
*/
- if (start < 4096 && (IS_GEN8(dev_priv) ||
- IS_KBL_REVID(dev_priv, 0, KBL_REVID_A0)))
+ if (start < 4096 && INTEL_GEN(dev_priv) >= 8)
start = 4096;
mutex_lock(&dev_priv->mm.stolen_lock);
diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c
index 1012eee..306fc54 100644
--- a/drivers/gpu/drm/i915/i915_sysfs.c
+++ b/drivers/gpu/drm/i915/i915_sysfs.c
@@ -460,7 +460,7 @@
static DEVICE_ATTR(gt_act_freq_mhz, S_IRUGO, gt_act_freq_mhz_show, NULL);
static DEVICE_ATTR(gt_cur_freq_mhz, S_IRUGO, gt_cur_freq_mhz_show, NULL);
-static DEVICE_ATTR(gt_boost_freq_mhz, S_IRUGO, gt_boost_freq_mhz_show, gt_boost_freq_mhz_store);
+static DEVICE_ATTR(gt_boost_freq_mhz, S_IRUGO | S_IWUSR, gt_boost_freq_mhz_show, gt_boost_freq_mhz_store);
static DEVICE_ATTR(gt_max_freq_mhz, S_IRUGO | S_IWUSR, gt_max_freq_mhz_show, gt_max_freq_mhz_store);
static DEVICE_ATTR(gt_min_freq_mhz, S_IRUGO | S_IWUSR, gt_min_freq_mhz_show, gt_min_freq_mhz_store);
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index dfbcf16..4149a0f 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -499,6 +499,7 @@
struct drm_i915_private *dev_priv = to_i915(crt->base.base.dev);
struct edid *edid;
struct i2c_adapter *i2c;
+ bool ret = false;
BUG_ON(crt->base.type != INTEL_OUTPUT_ANALOG);
@@ -515,17 +516,17 @@
*/
if (!is_digital) {
DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n");
- return true;
+ ret = true;
+ } else {
+ DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [EDID reports a digital panel]\n");
}
-
- DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [EDID reports a digital panel]\n");
} else {
DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [no valid EDID found]\n");
}
kfree(edid);
- return false;
+ return ret;
}
static enum drm_connector_status
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 3cb70d7..8079e5b 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -2587,8 +2587,9 @@
* We only keep the x/y offsets, so push all of the
* gtt offset into the x/y offsets.
*/
- _intel_adjust_tile_offset(&x, &y, tile_size,
- tile_width, tile_height, pitch_tiles,
+ _intel_adjust_tile_offset(&x, &y,
+ tile_width, tile_height,
+ tile_size, pitch_tiles,
gtt_offset_rotated * tile_size, 0);
gtt_offset_rotated += rot_info->plane[i].width * rot_info->plane[i].height;
@@ -2975,6 +2976,9 @@
unsigned int rotation = plane_state->base.rotation;
int ret;
+ if (!plane_state->base.visible)
+ return 0;
+
/* Rotate src coordinates to match rotated GTT view */
if (intel_rotation_90_or_270(rotation))
drm_rect_rotate(&plane_state->base.src,
@@ -6262,36 +6266,25 @@
dev_priv->cdclk_pll.vco = 0;
}
-static bool skl_cdclk_pcu_ready(struct drm_i915_private *dev_priv)
-{
- int ret;
- u32 val;
-
- /* inform PCU we want to change CDCLK */
- val = SKL_CDCLK_PREPARE_FOR_CHANGE;
- mutex_lock(&dev_priv->rps.hw_lock);
- ret = sandybridge_pcode_read(dev_priv, SKL_PCODE_CDCLK_CONTROL, &val);
- mutex_unlock(&dev_priv->rps.hw_lock);
-
- return ret == 0 && (val & SKL_CDCLK_READY_FOR_CHANGE);
-}
-
-static bool skl_cdclk_wait_for_pcu_ready(struct drm_i915_private *dev_priv)
-{
- return _wait_for(skl_cdclk_pcu_ready(dev_priv), 3000, 10) == 0;
-}
-
static void skl_set_cdclk(struct drm_i915_private *dev_priv, int cdclk, int vco)
{
struct drm_device *dev = &dev_priv->drm;
u32 freq_select, pcu_ack;
+ int ret;
WARN_ON((cdclk == 24000) != (vco == 0));
DRM_DEBUG_DRIVER("Changing CDCLK to %d kHz (VCO %d kHz)\n", cdclk, vco);
- if (!skl_cdclk_wait_for_pcu_ready(dev_priv)) {
- DRM_ERROR("failed to inform PCU about cdclk change\n");
+ mutex_lock(&dev_priv->rps.hw_lock);
+ ret = skl_pcode_request(dev_priv, SKL_PCODE_CDCLK_CONTROL,
+ SKL_CDCLK_PREPARE_FOR_CHANGE,
+ SKL_CDCLK_READY_FOR_CHANGE,
+ SKL_CDCLK_READY_FOR_CHANGE, 3);
+ mutex_unlock(&dev_priv->rps.hw_lock);
+ if (ret) {
+ DRM_ERROR("Failed to inform PCU about cdclk change (%d)\n",
+ ret);
return;
}
@@ -6876,6 +6869,12 @@
}
state = drm_atomic_state_alloc(crtc->dev);
+ if (!state) {
+ DRM_DEBUG_KMS("failed to disable [CRTC:%d:%s], out of memory",
+ crtc->base.id, crtc->name);
+ return;
+ }
+
state->acquire_ctx = crtc->dev->mode_config.acquire_ctx;
/* Everything's already locked, -EDEADLK can't happen. */
@@ -13970,8 +13969,9 @@
DRM_DEBUG_KMS("New cdclk calculated to be atomic %u, actual %u\n",
intel_state->cdclk, intel_state->dev_cdclk);
- } else
+ } else {
to_intel_atomic_state(state)->cdclk = dev_priv->atomic_cdclk_freq;
+ }
intel_modeset_clear_plls(state);
@@ -14072,8 +14072,9 @@
if (ret)
return ret;
- } else
- intel_state->cdclk = dev_priv->cdclk_freq;
+ } else {
+ intel_state->cdclk = dev_priv->atomic_cdclk_freq;
+ }
ret = drm_atomic_helper_check_planes(dev, state);
if (ret)
@@ -16441,6 +16442,7 @@
intel_update_czclk(dev_priv);
intel_update_cdclk(dev);
+ dev_priv->atomic_cdclk_freq = dev_priv->cdclk_freq;
intel_shared_dpll_init(dev);
@@ -16757,7 +16759,6 @@
for_each_intel_crtc(dev, crtc) {
struct intel_crtc_state *crtc_state = crtc->config;
- int pixclk = 0;
__drm_atomic_helper_crtc_destroy_state(&crtc_state->base);
memset(crtc_state, 0, sizeof(*crtc_state));
@@ -16769,23 +16770,9 @@
crtc->base.enabled = crtc_state->base.enable;
crtc->active = crtc_state->base.active;
- if (crtc_state->base.active) {
+ if (crtc_state->base.active)
dev_priv->active_crtcs |= 1 << crtc->pipe;
- if (INTEL_GEN(dev_priv) >= 9 || IS_BROADWELL(dev_priv))
- pixclk = ilk_pipe_pixel_rate(crtc_state);
- else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
- pixclk = crtc_state->base.adjusted_mode.crtc_clock;
- else
- WARN_ON(dev_priv->display.modeset_calc_cdclk);
-
- /* pixel rate mustn't exceed 95% of cdclk with IPS on BDW */
- if (IS_BROADWELL(dev_priv) && crtc_state->ips_enabled)
- pixclk = DIV_ROUND_UP(pixclk * 100, 95);
- }
-
- dev_priv->min_pixclk[crtc->pipe] = pixclk;
-
readout_plane_state(crtc);
DRM_DEBUG_KMS("[CRTC:%d:%s] hw state readout: %s\n",
@@ -16859,6 +16846,8 @@
}
for_each_intel_crtc(dev, crtc) {
+ int pixclk = 0;
+
crtc->base.hwmode = crtc->config->base.adjusted_mode;
memset(&crtc->base.mode, 0, sizeof(crtc->base.mode));
@@ -16886,10 +16875,23 @@
*/
crtc->base.state->mode.private_flags = I915_MODE_FLAG_INHERITED;
+ if (INTEL_GEN(dev_priv) >= 9 || IS_BROADWELL(dev_priv))
+ pixclk = ilk_pipe_pixel_rate(crtc->config);
+ else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
+ pixclk = crtc->config->base.adjusted_mode.crtc_clock;
+ else
+ WARN_ON(dev_priv->display.modeset_calc_cdclk);
+
+ /* pixel rate mustn't exceed 95% of cdclk with IPS on BDW */
+ if (IS_BROADWELL(dev_priv) && crtc->config->ips_enabled)
+ pixclk = DIV_ROUND_UP(pixclk * 100, 95);
+
drm_calc_timestamping_constants(&crtc->base, &crtc->base.hwmode);
update_scanline_offset(crtc);
}
+ dev_priv->min_pixclk[crtc->pipe] = pixclk;
+
intel_pipe_config_sanity_check(dev_priv, crtc->config);
}
}
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index bf344d0..0555250 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -280,7 +280,8 @@
struct intel_dp *intel_dp);
static void
intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
- struct intel_dp *intel_dp);
+ struct intel_dp *intel_dp,
+ bool force_disable_vdd);
static void
intel_dp_pps_init(struct drm_device *dev, struct intel_dp *intel_dp);
@@ -442,7 +443,7 @@
/* init power sequencer on this pipe and port */
intel_dp_init_panel_power_sequencer(dev, intel_dp);
- intel_dp_init_panel_power_sequencer_registers(dev, intel_dp);
+ intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, true);
/*
* Even vdd force doesn't work until we've made
@@ -479,7 +480,7 @@
* Only the HW needs to be reprogrammed, the SW state is fixed and
* has been setup during connector init.
*/
- intel_dp_init_panel_power_sequencer_registers(dev, intel_dp);
+ intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, false);
return 0;
}
@@ -562,7 +563,7 @@
port_name(port), pipe_name(intel_dp->pps_pipe));
intel_dp_init_panel_power_sequencer(dev, intel_dp);
- intel_dp_init_panel_power_sequencer_registers(dev, intel_dp);
+ intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, false);
}
void intel_power_sequencer_reset(struct drm_i915_private *dev_priv)
@@ -2924,7 +2925,7 @@
/* init power sequencer on this pipe and port */
intel_dp_init_panel_power_sequencer(dev, intel_dp);
- intel_dp_init_panel_power_sequencer_registers(dev, intel_dp);
+ intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, true);
}
static void vlv_pre_enable_dp(struct intel_encoder *encoder,
@@ -4017,6 +4018,11 @@
if (!to_intel_crtc(intel_encoder->base.crtc)->active)
return;
+ /* FIXME: we need to synchronize this sort of stuff with hardware
+ * readout. Currently fast link training doesn't work on boot-up. */
+ if (!intel_dp->lane_count)
+ return;
+
/* if link training is requested we should perform it always */
if ((intel_dp->compliance_test_type == DP_TEST_LINK_TRAINING) ||
(!drm_dp_channel_eq_ok(link_status, intel_dp->lane_count))) {
@@ -5054,7 +5060,8 @@
static void
intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
- struct intel_dp *intel_dp)
+ struct intel_dp *intel_dp,
+ bool force_disable_vdd)
{
struct drm_i915_private *dev_priv = to_i915(dev);
u32 pp_on, pp_off, pp_div, port_sel = 0;
@@ -5067,6 +5074,31 @@
intel_pps_get_registers(dev_priv, intel_dp, ®s);
+ /*
+ * On some VLV machines the BIOS can leave the VDD
+ * enabled even on power seqeuencers which aren't
+ * hooked up to any port. This would mess up the
+ * power domain tracking the first time we pick
+ * one of these power sequencers for use since
+ * edp_panel_vdd_on() would notice that the VDD was
+ * already on and therefore wouldn't grab the power
+ * domain reference. Disable VDD first to avoid this.
+ * This also avoids spuriously turning the VDD on as
+ * soon as the new power seqeuencer gets initialized.
+ */
+ if (force_disable_vdd) {
+ u32 pp = ironlake_get_pp_control(intel_dp);
+
+ WARN(pp & PANEL_POWER_ON, "Panel power already on\n");
+
+ if (pp & EDP_FORCE_VDD)
+ DRM_DEBUG_KMS("VDD already on, disabling first\n");
+
+ pp &= ~EDP_FORCE_VDD;
+
+ I915_WRITE(regs.pp_ctrl, pp);
+ }
+
pp_on = (seq->t1_t3 << PANEL_POWER_UP_DELAY_SHIFT) |
(seq->t8 << PANEL_LIGHT_ON_DELAY_SHIFT);
pp_off = (seq->t9 << PANEL_LIGHT_OFF_DELAY_SHIFT) |
@@ -5119,7 +5151,7 @@
vlv_initial_power_sequencer_setup(intel_dp);
} else {
intel_dp_init_panel_power_sequencer(dev, intel_dp);
- intel_dp_init_panel_power_sequencer_registers(dev, intel_dp);
+ intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, false);
}
}
diff --git a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c
index cd154ce..3460157 100644
--- a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c
+++ b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c
@@ -296,7 +296,8 @@
mutex_lock(&dev_priv->sb_lock);
vlv_iosf_sb_write(dev_priv, port, cfg1, 0);
vlv_iosf_sb_write(dev_priv, port, cfg0,
- CHV_GPIO_GPIOCFG_GPO | CHV_GPIO_GPIOTXSTATE(value));
+ CHV_GPIO_GPIOEN | CHV_GPIO_GPIOCFG_GPO |
+ CHV_GPIO_GPIOTXSTATE(value));
mutex_unlock(&dev_priv->sb_lock);
}
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index b7098f9..9127e57 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -745,6 +745,9 @@
{
struct intel_fbdev *ifbdev = to_i915(dev)->fbdev;
+ if (!ifbdev)
+ return;
+
ifbdev->cookie = async_schedule(intel_fbdev_initial_config, ifbdev);
}
diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
index 0adb879..4147e51 100644
--- a/drivers/gpu/drm/i915/intel_lrc.c
+++ b/drivers/gpu/drm/i915/intel_lrc.c
@@ -858,8 +858,7 @@
* this batch updates GEN8_L3SQCREG4 with default value we need to
* set this bit here to retain the WA during flush.
*/
- if (IS_SKL_REVID(dev_priv, 0, SKL_REVID_E0) ||
- IS_KBL_REVID(dev_priv, 0, KBL_REVID_E0))
+ if (IS_SKL_REVID(dev_priv, 0, SKL_REVID_E0))
l3sqc4_flush |= GEN8_LQSC_RO_PERF_DIS;
wa_ctx_emit(batch, index, (MI_STORE_REGISTER_MEM_GEN8 |
@@ -2153,30 +2152,42 @@
void intel_lr_context_resume(struct drm_i915_private *dev_priv)
{
- struct i915_gem_context *ctx = dev_priv->kernel_context;
struct intel_engine_cs *engine;
+ struct i915_gem_context *ctx;
- for_each_engine(engine, dev_priv) {
- struct intel_context *ce = &ctx->engine[engine->id];
- void *vaddr;
- uint32_t *reg_state;
+ /* Because we emit WA_TAIL_DWORDS there may be a disparity
+ * between our bookkeeping in ce->ring->head and ce->ring->tail and
+ * that stored in context. As we only write new commands from
+ * ce->ring->tail onwards, everything before that is junk. If the GPU
+ * starts reading from its RING_HEAD from the context, it may try to
+ * execute that junk and die.
+ *
+ * So to avoid that we reset the context images upon resume. For
+ * simplicity, we just zero everything out.
+ */
+ list_for_each_entry(ctx, &dev_priv->context_list, link) {
+ for_each_engine(engine, dev_priv) {
+ struct intel_context *ce = &ctx->engine[engine->id];
+ u32 *reg;
- if (!ce->state)
- continue;
+ if (!ce->state)
+ continue;
- vaddr = i915_gem_object_pin_map(ce->state->obj, I915_MAP_WB);
- if (WARN_ON(IS_ERR(vaddr)))
- continue;
+ reg = i915_gem_object_pin_map(ce->state->obj,
+ I915_MAP_WB);
+ if (WARN_ON(IS_ERR(reg)))
+ continue;
- reg_state = vaddr + LRC_STATE_PN * PAGE_SIZE;
+ reg += LRC_STATE_PN * PAGE_SIZE / sizeof(*reg);
+ reg[CTX_RING_HEAD+1] = 0;
+ reg[CTX_RING_TAIL+1] = 0;
- reg_state[CTX_RING_HEAD+1] = 0;
- reg_state[CTX_RING_TAIL+1] = 0;
+ ce->state->obj->dirty = true;
+ i915_gem_object_unpin_map(ce->state->obj);
- ce->state->obj->dirty = true;
- i915_gem_object_unpin_map(ce->state->obj);
-
- ce->ring->head = 0;
- ce->ring->tail = 0;
+ ce->ring->head = ce->ring->tail = 0;
+ ce->ring->last_retired_head = -1;
+ intel_ring_update_space(ce->ring);
+ }
}
}
diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c
index a24bc8c..a2655cd 100644
--- a/drivers/gpu/drm/i915/intel_overlay.c
+++ b/drivers/gpu/drm/i915/intel_overlay.c
@@ -216,7 +216,8 @@
{
GEM_BUG_ON(i915_gem_active_peek(&overlay->last_flip,
&overlay->i915->drm.struct_mutex));
- overlay->last_flip.retire = retire;
+ i915_gem_active_set_retire_fn(&overlay->last_flip, retire,
+ &overlay->i915->drm.struct_mutex);
i915_gem_active_set(&overlay->last_flip, req);
i915_add_request(req);
}
@@ -839,8 +840,8 @@
if (ret)
goto out_unpin;
- i915_gem_track_fb(overlay->vma->obj, new_bo,
- INTEL_FRONTBUFFER_OVERLAY(pipe));
+ i915_gem_track_fb(overlay->vma ? overlay->vma->obj : NULL,
+ vma->obj, INTEL_FRONTBUFFER_OVERLAY(pipe));
overlay->old_vma = overlay->vma;
overlay->vma = vma;
@@ -1430,6 +1431,8 @@
overlay->contrast = 75;
overlay->saturation = 146;
+ init_request_active(&overlay->last_flip, NULL);
+
regs = intel_overlay_map_regs(overlay);
if (!regs)
goto out_unpin_bo;
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index db24f89..e559a45 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -2879,6 +2879,21 @@
}
}
+/*
+ * FIXME: We still don't have the proper code detect if we need to apply the WA,
+ * so assume we'll always need it in order to avoid underruns.
+ */
+static bool skl_needs_memory_bw_wa(struct intel_atomic_state *state)
+{
+ struct drm_i915_private *dev_priv = to_i915(state->base.dev);
+
+ if (IS_SKYLAKE(dev_priv) || IS_BROXTON(dev_priv) ||
+ IS_KABYLAKE(dev_priv))
+ return true;
+
+ return false;
+}
+
static bool
intel_has_sagv(struct drm_i915_private *dev_priv)
{
@@ -2940,24 +2955,10 @@
return 0;
}
-static int
-intel_do_sagv_disable(struct drm_i915_private *dev_priv)
-{
- int ret;
- uint32_t temp = GEN9_SAGV_DISABLE;
-
- ret = sandybridge_pcode_read(dev_priv, GEN9_PCODE_SAGV_CONTROL,
- &temp);
- if (ret)
- return ret;
- else
- return temp & GEN9_SAGV_IS_DISABLED;
-}
-
int
intel_disable_sagv(struct drm_i915_private *dev_priv)
{
- int ret, result;
+ int ret;
if (!intel_has_sagv(dev_priv))
return 0;
@@ -2969,25 +2970,23 @@
mutex_lock(&dev_priv->rps.hw_lock);
/* bspec says to keep retrying for at least 1 ms */
- ret = wait_for(result = intel_do_sagv_disable(dev_priv), 1);
+ ret = skl_pcode_request(dev_priv, GEN9_PCODE_SAGV_CONTROL,
+ GEN9_SAGV_DISABLE,
+ GEN9_SAGV_IS_DISABLED, GEN9_SAGV_IS_DISABLED,
+ 1);
mutex_unlock(&dev_priv->rps.hw_lock);
- if (ret == -ETIMEDOUT) {
- DRM_ERROR("Request to disable SAGV timed out\n");
- return -ETIMEDOUT;
- }
-
/*
* Some skl systems, pre-release machines in particular,
* don't actually have an SAGV.
*/
- if (IS_SKYLAKE(dev_priv) && result == -ENXIO) {
+ if (IS_SKYLAKE(dev_priv) && ret == -ENXIO) {
DRM_DEBUG_DRIVER("No SAGV found on system, ignoring\n");
dev_priv->sagv_status = I915_SAGV_NOT_CONTROLLED;
return 0;
- } else if (result < 0) {
- DRM_ERROR("Failed to disable the SAGV\n");
- return result;
+ } else if (ret < 0) {
+ DRM_ERROR("Failed to disable the SAGV (%d)\n", ret);
+ return ret;
}
dev_priv->sagv_status = I915_SAGV_DISABLED;
@@ -2999,9 +2998,10 @@
struct drm_device *dev = state->dev;
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
- struct drm_crtc *crtc;
+ struct intel_crtc *crtc;
+ struct intel_plane *plane;
enum pipe pipe;
- int level, plane;
+ int level, id, latency;
if (!intel_has_sagv(dev_priv))
return false;
@@ -3019,27 +3019,36 @@
/* Since we're now guaranteed to only have one active CRTC... */
pipe = ffs(intel_state->active_crtcs) - 1;
- crtc = dev_priv->pipe_to_crtc_mapping[pipe];
+ crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
- if (crtc->state->mode.flags & DRM_MODE_FLAG_INTERLACE)
+ if (crtc->base.state->mode.flags & DRM_MODE_FLAG_INTERLACE)
return false;
- for_each_plane(dev_priv, pipe, plane) {
+ for_each_intel_plane_on_crtc(dev, crtc, plane) {
+ id = skl_wm_plane_id(plane);
+
/* Skip this plane if it's not enabled */
- if (intel_state->wm_results.plane[pipe][plane][0] == 0)
+ if (intel_state->wm_results.plane[pipe][id][0] == 0)
continue;
/* Find the highest enabled wm level for this plane */
for (level = ilk_wm_max_level(dev);
- intel_state->wm_results.plane[pipe][plane][level] == 0; --level)
+ intel_state->wm_results.plane[pipe][id][level] == 0; --level)
{ }
+ latency = dev_priv->wm.skl_latency[level];
+
+ if (skl_needs_memory_bw_wa(intel_state) &&
+ plane->base.state->fb->modifier[0] ==
+ I915_FORMAT_MOD_X_TILED)
+ latency += 15;
+
/*
* If any of the planes on this pipe don't enable wm levels
* that incur memory latencies higher then 30µs we can't enable
* the SAGV
*/
- if (dev_priv->wm.skl_latency[level] < SKL_SAGV_BLOCK_TIME)
+ if (latency < SKL_SAGV_BLOCK_TIME)
return false;
}
@@ -3549,12 +3558,18 @@
uint32_t width = 0, height = 0;
uint32_t plane_pixel_rate;
uint32_t y_tile_minimum, y_min_scanlines;
+ struct intel_atomic_state *state =
+ to_intel_atomic_state(cstate->base.state);
+ bool apply_memory_bw_wa = skl_needs_memory_bw_wa(state);
if (latency == 0 || !cstate->base.active || !intel_pstate->base.visible) {
*enabled = false;
return 0;
}
+ if (apply_memory_bw_wa && fb->modifier[0] == I915_FORMAT_MOD_X_TILED)
+ latency += 15;
+
width = drm_rect_width(&intel_pstate->base.src) >> 16;
height = drm_rect_height(&intel_pstate->base.src) >> 16;
@@ -3586,6 +3601,9 @@
y_min_scanlines = 4;
}
+ if (apply_memory_bw_wa)
+ y_min_scanlines *= 2;
+
plane_bytes_per_line = width * cpp;
if (fb->modifier[0] == I915_FORMAT_MOD_Y_TILED ||
fb->modifier[0] == I915_FORMAT_MOD_Yf_TILED) {
@@ -7921,6 +7939,81 @@
return 0;
}
+static bool skl_pcode_try_request(struct drm_i915_private *dev_priv, u32 mbox,
+ u32 request, u32 reply_mask, u32 reply,
+ u32 *status)
+{
+ u32 val = request;
+
+ *status = sandybridge_pcode_read(dev_priv, mbox, &val);
+
+ return *status || ((val & reply_mask) == reply);
+}
+
+/**
+ * skl_pcode_request - send PCODE request until acknowledgment
+ * @dev_priv: device private
+ * @mbox: PCODE mailbox ID the request is targeted for
+ * @request: request ID
+ * @reply_mask: mask used to check for request acknowledgment
+ * @reply: value used to check for request acknowledgment
+ * @timeout_base_ms: timeout for polling with preemption enabled
+ *
+ * Keep resending the @request to @mbox until PCODE acknowledges it, PCODE
+ * reports an error or an overall timeout of @timeout_base_ms+10 ms expires.
+ * The request is acknowledged once the PCODE reply dword equals @reply after
+ * applying @reply_mask. Polling is first attempted with preemption enabled
+ * for @timeout_base_ms and if this times out for another 10 ms with
+ * preemption disabled.
+ *
+ * Returns 0 on success, %-ETIMEDOUT in case of a timeout, <0 in case of some
+ * other error as reported by PCODE.
+ */
+int skl_pcode_request(struct drm_i915_private *dev_priv, u32 mbox, u32 request,
+ u32 reply_mask, u32 reply, int timeout_base_ms)
+{
+ u32 status;
+ int ret;
+
+ WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock));
+
+#define COND skl_pcode_try_request(dev_priv, mbox, request, reply_mask, reply, \
+ &status)
+
+ /*
+ * Prime the PCODE by doing a request first. Normally it guarantees
+ * that a subsequent request, at most @timeout_base_ms later, succeeds.
+ * _wait_for() doesn't guarantee when its passed condition is evaluated
+ * first, so send the first request explicitly.
+ */
+ if (COND) {
+ ret = 0;
+ goto out;
+ }
+ ret = _wait_for(COND, timeout_base_ms * 1000, 10);
+ if (!ret)
+ goto out;
+
+ /*
+ * The above can time out if the number of requests was low (2 in the
+ * worst case) _and_ PCODE was busy for some reason even after a
+ * (queued) request and @timeout_base_ms delay. As a workaround retry
+ * the poll with preemption disabled to maximize the number of
+ * requests. Increase the timeout from @timeout_base_ms to 10ms to
+ * account for interrupts that could reduce the number of these
+ * requests.
+ */
+ DRM_DEBUG_KMS("PCODE timeout, retrying with preemption disabled\n");
+ WARN_ON_ONCE(timeout_base_ms > 3);
+ preempt_disable();
+ ret = wait_for_atomic(COND, 10);
+ preempt_enable();
+
+out:
+ return ret ? ret : status;
+#undef COND
+}
+
static int byt_gpu_freq(struct drm_i915_private *dev_priv, int val)
{
/*
diff --git a/drivers/gpu/drm/i915/intel_psr.c b/drivers/gpu/drm/i915/intel_psr.c
index 108ba1e..9b307ce 100644
--- a/drivers/gpu/drm/i915/intel_psr.c
+++ b/drivers/gpu/drm/i915/intel_psr.c
@@ -825,13 +825,9 @@
dev_priv->psr_mmio_base = IS_HASWELL(dev_priv) ?
HSW_EDP_PSR_BASE : BDW_EDP_PSR_BASE;
- /* Per platform default */
- if (i915.enable_psr == -1) {
- if (IS_HASWELL(dev) || IS_BROADWELL(dev))
- i915.enable_psr = 1;
- else
- i915.enable_psr = 0;
- }
+ /* Per platform default: all disabled. */
+ if (i915.enable_psr == -1)
+ i915.enable_psr = 0;
/* Set link_standby x link_off defaults */
if (IS_HASWELL(dev) || IS_BROADWELL(dev))
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index ed9955d..8babfe0 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -1153,14 +1153,6 @@
WA_SET_BIT_MASKED(HDC_CHICKEN0,
HDC_FENCE_DEST_SLM_DISABLE);
- /* GEN8_L3SQCREG4 has a dependency with WA batch so any new changes
- * involving this register should also be added to WA batch as required.
- */
- if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_E0))
- /* WaDisableLSQCROPERFforOCL:kbl */
- I915_WRITE(GEN8_L3SQCREG4, I915_READ(GEN8_L3SQCREG4) |
- GEN8_LQSC_RO_PERF_DIS);
-
/* WaToEnableHwFixForPushConstHWBug:kbl */
if (IS_KBL_REVID(dev_priv, KBL_REVID_C0, REVID_FOREVER))
WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
index a38c2fe..23ed3f5 100644
--- a/drivers/gpu/drm/i915/intel_runtime_pm.c
+++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
@@ -1065,7 +1065,18 @@
static void vlv_init_display_clock_gating(struct drm_i915_private *dev_priv)
{
- I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE);
+ u32 val;
+
+ /*
+ * On driver load, a pipe may be active and driving a DSI display.
+ * Preserve DPOUNIT_CLOCK_GATE_DISABLE to avoid the pipe getting stuck
+ * (and never recovering) in this case. intel_dsi_post_disable() will
+ * clear it when we turn off the display.
+ */
+ val = I915_READ(DSPCLK_GATE_D);
+ val &= DPOUNIT_CLOCK_GATE_DISABLE;
+ val |= VRHUNIT_CLOCK_GATE_DISABLE;
+ I915_WRITE(DSPCLK_GATE_D, val);
/*
* Disable trickle feed and enable pnd deadline calculation
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 95c113a..af327f1 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -37,6 +37,7 @@
sde/sde_encoder_phys_cmd.o \
sde/sde_irq.o \
sde/sde_core_irq.o \
+ sde/sde_core_perf.o \
sde/sde_rm.o \
sde/sde_kms_utils.o \
sde/sde_kms.o \
@@ -45,6 +46,7 @@
sde/sde_backlight.o \
sde/sde_color_processing.o \
sde/sde_vbif.o \
+ sde_dbg.o \
sde_dbg_evtlog.o \
sde_io_util.o \
sde/sde_hw_reg_dma_v1_color_proc.o \
@@ -94,15 +96,22 @@
msm_drm-$(CONFIG_DRM_MSM_DSI_28NM_8960_PHY) += dsi/pll/dsi_pll_28nm_8960.o
endif
msm_drm-$(CONFIG_DRM_MSM_DSI_STAGING) += dsi-staging/dsi_phy.o \
- dsi-staging/dsi_clk_pwr.o \
+ dsi-staging/dsi_pwr.o \
dsi-staging/dsi_phy.o \
- dsi-staging/dsi_phy_hw_v4_0.o \
+ dsi-staging/dsi_phy_hw_v2_0.o \
+ dsi-staging/dsi_phy_hw_v3_0.o \
+ dsi-staging/dsi_phy_timing_calc.o \
+ dsi-staging/dsi_phy_timing_v2_0.o \
+ dsi-staging/dsi_phy_timing_v3_0.o \
+ dsi-staging/dsi_ctrl_hw_cmn.o \
dsi-staging/dsi_ctrl_hw_1_4.o \
+ dsi-staging/dsi_ctrl_hw_2_0.o \
dsi-staging/dsi_ctrl.o \
dsi-staging/dsi_catalog.o \
dsi-staging/dsi_drm.o \
dsi-staging/dsi_display.o \
dsi-staging/dsi_panel.o \
+ dsi-staging/dsi_clk_manager.o \
dsi-staging/dsi_display_test.o
msm_drm-$(CONFIG_DRM_MSM_DSI_PLL) += dsi/pll/dsi_pll.o \
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
index 06027a9..976be99 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,56 +18,74 @@
#include "dsi_catalog.h"
/**
- * dsi_catalog_14_init() - catalog init for dsi controller v1.4
+ * dsi_catalog_cmn_init() - catalog init for dsi controller v1.4
*/
-static void dsi_catalog_14_init(struct dsi_ctrl_hw *ctrl)
+static void dsi_catalog_cmn_init(struct dsi_ctrl_hw *ctrl,
+ enum dsi_ctrl_version version)
{
- ctrl->ops.host_setup = dsi_ctrl_hw_14_host_setup;
- ctrl->ops.setup_lane_map = dsi_ctrl_hw_14_setup_lane_map;
- ctrl->ops.video_engine_en = dsi_ctrl_hw_14_video_engine_en;
- ctrl->ops.video_engine_setup = dsi_ctrl_hw_14_video_engine_setup;
- ctrl->ops.set_video_timing = dsi_ctrl_hw_14_set_video_timing;
- ctrl->ops.cmd_engine_setup = dsi_ctrl_hw_14_cmd_engine_setup;
- ctrl->ops.setup_cmd_stream = dsi_ctrl_hw_14_setup_cmd_stream;
- ctrl->ops.ctrl_en = dsi_ctrl_hw_14_ctrl_en;
- ctrl->ops.cmd_engine_en = dsi_ctrl_hw_14_cmd_engine_en;
- ctrl->ops.phy_sw_reset = dsi_ctrl_hw_14_phy_sw_reset;
- ctrl->ops.soft_reset = dsi_ctrl_hw_14_soft_reset;
- ctrl->ops.kickoff_command = dsi_ctrl_hw_14_kickoff_command;
- ctrl->ops.kickoff_fifo_command = dsi_ctrl_hw_14_kickoff_fifo_command;
- ctrl->ops.reset_cmd_fifo = dsi_ctrl_hw_14_reset_cmd_fifo;
- ctrl->ops.trigger_command_dma = dsi_ctrl_hw_14_trigger_command_dma;
- ctrl->ops.ulps_request = dsi_ctrl_hw_14_ulps_request;
- ctrl->ops.ulps_exit = dsi_ctrl_hw_14_ulps_exit;
- ctrl->ops.clear_ulps_request = dsi_ctrl_hw_14_clear_ulps_request;
- ctrl->ops.get_lanes_in_ulps = dsi_ctrl_hw_14_get_lanes_in_ulps;
- ctrl->ops.clamp_enable = dsi_ctrl_hw_14_clamp_enable;
- ctrl->ops.clamp_disable = dsi_ctrl_hw_14_clamp_disable;
- ctrl->ops.get_interrupt_status = dsi_ctrl_hw_14_get_interrupt_status;
- ctrl->ops.get_error_status = dsi_ctrl_hw_14_get_error_status;
- ctrl->ops.clear_error_status = dsi_ctrl_hw_14_clear_error_status;
+ /* common functions */
+ ctrl->ops.host_setup = dsi_ctrl_hw_cmn_host_setup;
+ ctrl->ops.video_engine_en = dsi_ctrl_hw_cmn_video_engine_en;
+ ctrl->ops.video_engine_setup = dsi_ctrl_hw_cmn_video_engine_setup;
+ ctrl->ops.set_video_timing = dsi_ctrl_hw_cmn_set_video_timing;
+ ctrl->ops.cmd_engine_setup = dsi_ctrl_hw_cmn_cmd_engine_setup;
+ ctrl->ops.setup_cmd_stream = dsi_ctrl_hw_cmn_setup_cmd_stream;
+ ctrl->ops.ctrl_en = dsi_ctrl_hw_cmn_ctrl_en;
+ ctrl->ops.cmd_engine_en = dsi_ctrl_hw_cmn_cmd_engine_en;
+ ctrl->ops.phy_sw_reset = dsi_ctrl_hw_cmn_phy_sw_reset;
+ ctrl->ops.soft_reset = dsi_ctrl_hw_cmn_soft_reset;
+ ctrl->ops.kickoff_command = dsi_ctrl_hw_cmn_kickoff_command;
+ ctrl->ops.kickoff_fifo_command = dsi_ctrl_hw_cmn_kickoff_fifo_command;
+ ctrl->ops.reset_cmd_fifo = dsi_ctrl_hw_cmn_reset_cmd_fifo;
+ ctrl->ops.trigger_command_dma = dsi_ctrl_hw_cmn_trigger_command_dma;
+ ctrl->ops.get_interrupt_status = dsi_ctrl_hw_cmn_get_interrupt_status;
+ ctrl->ops.get_error_status = dsi_ctrl_hw_cmn_get_error_status;
+ ctrl->ops.clear_error_status = dsi_ctrl_hw_cmn_clear_error_status;
ctrl->ops.clear_interrupt_status =
- dsi_ctrl_hw_14_clear_interrupt_status;
+ dsi_ctrl_hw_cmn_clear_interrupt_status;
ctrl->ops.enable_status_interrupts =
- dsi_ctrl_hw_14_enable_status_interrupts;
+ dsi_ctrl_hw_cmn_enable_status_interrupts;
ctrl->ops.enable_error_interrupts =
- dsi_ctrl_hw_14_enable_error_interrupts;
+ dsi_ctrl_hw_cmn_enable_error_interrupts;
ctrl->ops.video_test_pattern_setup =
- dsi_ctrl_hw_14_video_test_pattern_setup;
+ dsi_ctrl_hw_cmn_video_test_pattern_setup;
ctrl->ops.cmd_test_pattern_setup =
- dsi_ctrl_hw_14_cmd_test_pattern_setup;
- ctrl->ops.test_pattern_enable = dsi_ctrl_hw_14_test_pattern_enable;
+ dsi_ctrl_hw_cmn_cmd_test_pattern_setup;
+ ctrl->ops.test_pattern_enable = dsi_ctrl_hw_cmn_test_pattern_enable;
ctrl->ops.trigger_cmd_test_pattern =
- dsi_ctrl_hw_14_trigger_cmd_test_pattern;
- ctrl->ops.reg_dump_to_buffer = dsi_ctrl_hw_14_reg_dump_to_buffer;
-}
+ dsi_ctrl_hw_cmn_trigger_cmd_test_pattern;
+ ctrl->ops.clear_phy0_ln_err = dsi_ctrl_hw_dln0_phy_err;
+ ctrl->ops.phy_reset_config = dsi_ctrl_hw_cmn_phy_reset_config;
-/**
- * dsi_catalog_20_init() - catalog init for dsi controller v2.0
- */
-static void dsi_catalog_20_init(struct dsi_ctrl_hw *ctrl)
-{
- set_bit(DSI_CTRL_CPHY, ctrl->feature_map);
+ switch (version) {
+ case DSI_CTRL_VERSION_1_4:
+ ctrl->ops.setup_lane_map = dsi_ctrl_hw_14_setup_lane_map;
+ ctrl->ops.ulps_ops.ulps_request = dsi_ctrl_hw_14_ulps_request;
+ ctrl->ops.ulps_ops.ulps_exit = dsi_ctrl_hw_14_ulps_exit;
+ ctrl->ops.wait_for_lane_idle =
+ dsi_ctrl_hw_14_wait_for_lane_idle;
+ ctrl->ops.ulps_ops.get_lanes_in_ulps =
+ dsi_ctrl_hw_14_get_lanes_in_ulps;
+ ctrl->ops.clamp_enable = dsi_ctrl_hw_14_clamp_enable;
+ ctrl->ops.clamp_disable = dsi_ctrl_hw_14_clamp_disable;
+ ctrl->ops.reg_dump_to_buffer =
+ dsi_ctrl_hw_14_reg_dump_to_buffer;
+ break;
+ case DSI_CTRL_VERSION_2_0:
+ ctrl->ops.setup_lane_map = dsi_ctrl_hw_20_setup_lane_map;
+ ctrl->ops.wait_for_lane_idle =
+ dsi_ctrl_hw_20_wait_for_lane_idle;
+ ctrl->ops.reg_dump_to_buffer =
+ dsi_ctrl_hw_20_reg_dump_to_buffer;
+ ctrl->ops.ulps_ops.ulps_request = NULL;
+ ctrl->ops.ulps_ops.ulps_exit = NULL;
+ ctrl->ops.ulps_ops.get_lanes_in_ulps = NULL;
+ ctrl->ops.clamp_enable = NULL;
+ ctrl->ops.clamp_disable = NULL;
+ break;
+ default:
+ break;
+ }
}
/**
@@ -102,10 +120,8 @@
switch (version) {
case DSI_CTRL_VERSION_1_4:
- dsi_catalog_14_init(ctrl);
- break;
case DSI_CTRL_VERSION_2_0:
- dsi_catalog_20_init(ctrl);
+ dsi_catalog_cmn_init(ctrl, version);
break;
default:
return -ENOTSUPP;
@@ -115,16 +131,43 @@
}
/**
- * dsi_catalog_phy_4_0_init() - catalog init for DSI PHY v4.0
+ * dsi_catalog_phy_2_0_init() - catalog init for DSI PHY 14nm
*/
-static void dsi_catalog_phy_4_0_init(struct dsi_phy_hw *phy)
+static void dsi_catalog_phy_2_0_init(struct dsi_phy_hw *phy)
{
- phy->ops.regulator_enable = dsi_phy_hw_v4_0_regulator_enable;
- phy->ops.regulator_disable = dsi_phy_hw_v4_0_regulator_disable;
- phy->ops.enable = dsi_phy_hw_v4_0_enable;
- phy->ops.disable = dsi_phy_hw_v4_0_disable;
+ phy->ops.regulator_enable = dsi_phy_hw_v2_0_regulator_enable;
+ phy->ops.regulator_disable = dsi_phy_hw_v2_0_regulator_disable;
+ phy->ops.enable = dsi_phy_hw_v2_0_enable;
+ phy->ops.disable = dsi_phy_hw_v2_0_disable;
phy->ops.calculate_timing_params =
- dsi_phy_hw_v4_0_calculate_timing_params;
+ dsi_phy_hw_calculate_timing_params;
+ phy->ops.phy_idle_on = dsi_phy_hw_v2_0_idle_on;
+ phy->ops.phy_idle_off = dsi_phy_hw_v2_0_idle_off;
+ phy->ops.calculate_timing_params =
+ dsi_phy_hw_calculate_timing_params;
+ phy->ops.phy_timing_val = dsi_phy_hw_timing_val_v2_0;
+}
+
+/**
+ * dsi_catalog_phy_3_0_init() - catalog init for DSI PHY 10nm
+ */
+static void dsi_catalog_phy_3_0_init(struct dsi_phy_hw *phy)
+{
+ phy->ops.regulator_enable = dsi_phy_hw_v3_0_regulator_enable;
+ phy->ops.regulator_disable = dsi_phy_hw_v3_0_regulator_disable;
+ phy->ops.enable = dsi_phy_hw_v3_0_enable;
+ phy->ops.disable = dsi_phy_hw_v3_0_disable;
+ phy->ops.calculate_timing_params =
+ dsi_phy_hw_calculate_timing_params;
+ phy->ops.ulps_ops.wait_for_lane_idle =
+ dsi_phy_hw_v3_0_wait_for_lane_idle;
+ phy->ops.ulps_ops.ulps_request =
+ dsi_phy_hw_v3_0_ulps_request;
+ phy->ops.ulps_ops.ulps_exit =
+ dsi_phy_hw_v3_0_ulps_exit;
+ phy->ops.ulps_ops.get_lanes_in_ulps =
+ dsi_phy_hw_v3_0_get_lanes_in_ulps;
+ phy->ops.phy_timing_val = dsi_phy_hw_timing_val_v3_0;
}
/**
@@ -152,13 +195,18 @@
phy->index = index;
set_bit(DSI_PHY_DPHY, phy->feature_map);
+ dsi_phy_timing_calc_init(phy, version);
+
switch (version) {
- case DSI_PHY_VERSION_4_0:
- dsi_catalog_phy_4_0_init(phy);
- break;
- case DSI_PHY_VERSION_1_0:
case DSI_PHY_VERSION_2_0:
+ dsi_catalog_phy_2_0_init(phy);
+ break;
case DSI_PHY_VERSION_3_0:
+ dsi_catalog_phy_3_0_init(phy);
+ break;
+ case DSI_PHY_VERSION_0_0_HPM:
+ case DSI_PHY_VERSION_0_0_LPM:
+ case DSI_PHY_VERSION_1_0:
default:
return -ENOTSUPP;
}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
index 98bd9b0..1f0df0a 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -34,7 +34,7 @@
/**
* dsi_catalog_phy_setup() - return catalog info for dsi phy hardware
- * @ctrl: Pointer to DSI PHY hw object.
+ * @phy: Pointer to DSI PHY hw object.
* @version: DSI PHY version.
* @index: DSI PHY instance ID.
*
@@ -46,58 +46,125 @@
enum dsi_phy_version version,
u32 index);
-/* Definitions for 4.0 PHY hardware driver */
-void dsi_phy_hw_v4_0_regulator_enable(struct dsi_phy_hw *phy,
- struct dsi_phy_per_lane_cfgs *cfg);
-void dsi_phy_hw_v4_0_regulator_disable(struct dsi_phy_hw *phy);
-void dsi_phy_hw_v4_0_enable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
-void dsi_phy_hw_v4_0_disable(struct dsi_phy_hw *phy);
-int dsi_phy_hw_v4_0_calculate_timing_params(struct dsi_phy_hw *phy,
- struct dsi_mode_info *mode,
- struct dsi_host_common_cfg *cfg,
- struct dsi_phy_per_lane_cfgs
- *timing);
+/**
+ * dsi_phy_timing_calc_init() - initialize info for DSI PHY timing calculations
+ * @phy: Pointer to DSI PHY hw object.
+ * @version: DSI PHY version.
+ *
+ * This function setups the catalog information in the dsi_phy_hw object.
+ *
+ * return: error code for failure and 0 for success.
+ */
+int dsi_phy_timing_calc_init(struct dsi_phy_hw *phy,
+ enum dsi_phy_version version);
-/* Definitions for 1.4 controller hardware driver */
-void dsi_ctrl_hw_14_host_setup(struct dsi_ctrl_hw *ctrl,
+/**
+ * dsi_phy_hw_calculate_timing_params() - DSI PHY timing parameter calculations
+ * @phy: Pointer to DSI PHY hw object.
+ * @mode: DSI mode information.
+ * @host: DSI host configuration.
+ * @timing: DSI phy lane configurations.
+ *
+ * This function setups the catalog information in the dsi_phy_hw object.
+ *
+ * return: error code for failure and 0 for success.
+ */
+int dsi_phy_hw_calculate_timing_params(struct dsi_phy_hw *phy,
+ struct dsi_mode_info *mode,
+ struct dsi_host_common_cfg *host,
+ struct dsi_phy_per_lane_cfgs *timing);
+
+/* Definitions for 14nm PHY hardware driver */
+void dsi_phy_hw_v2_0_regulator_enable(struct dsi_phy_hw *phy,
+ struct dsi_phy_per_lane_cfgs *cfg);
+void dsi_phy_hw_v2_0_regulator_disable(struct dsi_phy_hw *phy);
+void dsi_phy_hw_v2_0_enable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
+void dsi_phy_hw_v2_0_disable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
+void dsi_phy_hw_v2_0_idle_on(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
+void dsi_phy_hw_v2_0_idle_off(struct dsi_phy_hw *phy);
+int dsi_phy_hw_timing_val_v2_0(struct dsi_phy_per_lane_cfgs *timing_cfg,
+ u32 *timing_val, u32 size);
+
+/* Definitions for 10nm PHY hardware driver */
+void dsi_phy_hw_v3_0_regulator_enable(struct dsi_phy_hw *phy,
+ struct dsi_phy_per_lane_cfgs *cfg);
+void dsi_phy_hw_v3_0_regulator_disable(struct dsi_phy_hw *phy);
+void dsi_phy_hw_v3_0_enable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
+void dsi_phy_hw_v3_0_disable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
+int dsi_phy_hw_v3_0_wait_for_lane_idle(struct dsi_phy_hw *phy, u32 lanes);
+void dsi_phy_hw_v3_0_ulps_request(struct dsi_phy_hw *phy,
+ struct dsi_phy_cfg *cfg, u32 lanes);
+void dsi_phy_hw_v3_0_ulps_exit(struct dsi_phy_hw *phy,
+ struct dsi_phy_cfg *cfg, u32 lanes);
+u32 dsi_phy_hw_v3_0_get_lanes_in_ulps(struct dsi_phy_hw *phy);
+int dsi_phy_hw_timing_val_v3_0(struct dsi_phy_per_lane_cfgs *timing_cfg,
+ u32 *timing_val, u32 size);
+
+/* DSI controller common ops */
+u32 dsi_ctrl_hw_cmn_get_interrupt_status(struct dsi_ctrl_hw *ctrl);
+void dsi_ctrl_hw_cmn_clear_interrupt_status(struct dsi_ctrl_hw *ctrl, u32 ints);
+void dsi_ctrl_hw_cmn_enable_status_interrupts(struct dsi_ctrl_hw *ctrl,
+ u32 ints);
+
+u64 dsi_ctrl_hw_cmn_get_error_status(struct dsi_ctrl_hw *ctrl);
+void dsi_ctrl_hw_cmn_clear_error_status(struct dsi_ctrl_hw *ctrl, u64 errors);
+void dsi_ctrl_hw_cmn_enable_error_interrupts(struct dsi_ctrl_hw *ctrl,
+ u64 errors);
+
+void dsi_ctrl_hw_cmn_video_test_pattern_setup(struct dsi_ctrl_hw *ctrl,
+ enum dsi_test_pattern type,
+ u32 init_val);
+void dsi_ctrl_hw_cmn_cmd_test_pattern_setup(struct dsi_ctrl_hw *ctrl,
+ enum dsi_test_pattern type,
+ u32 init_val,
+ u32 stream_id);
+void dsi_ctrl_hw_cmn_test_pattern_enable(struct dsi_ctrl_hw *ctrl, bool enable);
+void dsi_ctrl_hw_cmn_trigger_cmd_test_pattern(struct dsi_ctrl_hw *ctrl,
+ u32 stream_id);
+
+void dsi_ctrl_hw_cmn_host_setup(struct dsi_ctrl_hw *ctrl,
struct dsi_host_common_cfg *config);
-void dsi_ctrl_hw_14_video_engine_en(struct dsi_ctrl_hw *ctrl, bool on);
-void dsi_ctrl_hw_14_video_engine_setup(struct dsi_ctrl_hw *ctrl,
+void dsi_ctrl_hw_cmn_video_engine_en(struct dsi_ctrl_hw *ctrl, bool on);
+void dsi_ctrl_hw_cmn_video_engine_setup(struct dsi_ctrl_hw *ctrl,
struct dsi_host_common_cfg *common_cfg,
struct dsi_video_engine_cfg *cfg);
-void dsi_ctrl_hw_14_set_video_timing(struct dsi_ctrl_hw *ctrl,
+void dsi_ctrl_hw_cmn_set_video_timing(struct dsi_ctrl_hw *ctrl,
struct dsi_mode_info *mode);
-void dsi_ctrl_hw_14_cmd_engine_setup(struct dsi_ctrl_hw *ctrl,
+void dsi_ctrl_hw_cmn_cmd_engine_setup(struct dsi_ctrl_hw *ctrl,
struct dsi_host_common_cfg *common_cfg,
struct dsi_cmd_engine_cfg *cfg);
-void dsi_ctrl_hw_14_ctrl_en(struct dsi_ctrl_hw *ctrl, bool on);
-void dsi_ctrl_hw_14_cmd_engine_en(struct dsi_ctrl_hw *ctrl, bool on);
+void dsi_ctrl_hw_cmn_ctrl_en(struct dsi_ctrl_hw *ctrl, bool on);
+void dsi_ctrl_hw_cmn_cmd_engine_en(struct dsi_ctrl_hw *ctrl, bool on);
-void dsi_ctrl_hw_14_setup_cmd_stream(struct dsi_ctrl_hw *ctrl,
+void dsi_ctrl_hw_cmn_setup_cmd_stream(struct dsi_ctrl_hw *ctrl,
u32 width_in_pixels,
u32 h_stride,
u32 height_in_lines,
u32 vc_id);
-void dsi_ctrl_hw_14_phy_sw_reset(struct dsi_ctrl_hw *ctrl);
-void dsi_ctrl_hw_14_soft_reset(struct dsi_ctrl_hw *ctrl);
+void dsi_ctrl_hw_cmn_phy_sw_reset(struct dsi_ctrl_hw *ctrl);
+void dsi_ctrl_hw_cmn_soft_reset(struct dsi_ctrl_hw *ctrl);
-void dsi_ctrl_hw_14_setup_lane_map(struct dsi_ctrl_hw *ctrl,
- struct dsi_lane_mapping *lane_map);
-void dsi_ctrl_hw_14_kickoff_command(struct dsi_ctrl_hw *ctrl,
+void dsi_ctrl_hw_cmn_kickoff_command(struct dsi_ctrl_hw *ctrl,
struct dsi_ctrl_cmd_dma_info *cmd,
u32 flags);
-void dsi_ctrl_hw_14_kickoff_fifo_command(struct dsi_ctrl_hw *ctrl,
+void dsi_ctrl_hw_cmn_kickoff_fifo_command(struct dsi_ctrl_hw *ctrl,
struct dsi_ctrl_cmd_dma_fifo_info *cmd,
u32 flags);
-void dsi_ctrl_hw_14_reset_cmd_fifo(struct dsi_ctrl_hw *ctrl);
-void dsi_ctrl_hw_14_trigger_command_dma(struct dsi_ctrl_hw *ctrl);
+void dsi_ctrl_hw_cmn_reset_cmd_fifo(struct dsi_ctrl_hw *ctrl);
+void dsi_ctrl_hw_cmn_trigger_command_dma(struct dsi_ctrl_hw *ctrl);
+void dsi_ctrl_hw_dln0_phy_err(struct dsi_ctrl_hw *ctrl);
+void dsi_ctrl_hw_cmn_phy_reset_config(struct dsi_ctrl_hw *ctrl,
+ bool enable);
+/* Definitions specific to 1.4 DSI controller hardware */
+int dsi_ctrl_hw_14_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl, u32 lanes);
+void dsi_ctrl_hw_14_setup_lane_map(struct dsi_ctrl_hw *ctrl,
+ struct dsi_lane_map *lane_map);
void dsi_ctrl_hw_14_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes);
void dsi_ctrl_hw_14_ulps_exit(struct dsi_ctrl_hw *ctrl, u32 lanes);
-void dsi_ctrl_hw_14_clear_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes);
u32 dsi_ctrl_hw_14_get_lanes_in_ulps(struct dsi_ctrl_hw *ctrl);
void dsi_ctrl_hw_14_clamp_enable(struct dsi_ctrl_hw *ctrl,
@@ -107,27 +174,16 @@
void dsi_ctrl_hw_14_clamp_disable(struct dsi_ctrl_hw *ctrl,
u32 lanes,
bool disable_ulps);
-u32 dsi_ctrl_hw_14_get_interrupt_status(struct dsi_ctrl_hw *ctrl);
-void dsi_ctrl_hw_14_clear_interrupt_status(struct dsi_ctrl_hw *ctrl, u32 ints);
-void dsi_ctrl_hw_14_enable_status_interrupts(struct dsi_ctrl_hw *ctrl,
- u32 ints);
-
-u64 dsi_ctrl_hw_14_get_error_status(struct dsi_ctrl_hw *ctrl);
-void dsi_ctrl_hw_14_clear_error_status(struct dsi_ctrl_hw *ctrl, u64 errors);
-void dsi_ctrl_hw_14_enable_error_interrupts(struct dsi_ctrl_hw *ctrl,
- u64 errors);
-
-void dsi_ctrl_hw_14_video_test_pattern_setup(struct dsi_ctrl_hw *ctrl,
- enum dsi_test_pattern type,
- u32 init_val);
-void dsi_ctrl_hw_14_cmd_test_pattern_setup(struct dsi_ctrl_hw *ctrl,
- enum dsi_test_pattern type,
- u32 init_val,
- u32 stream_id);
-void dsi_ctrl_hw_14_test_pattern_enable(struct dsi_ctrl_hw *ctrl, bool enable);
-void dsi_ctrl_hw_14_trigger_cmd_test_pattern(struct dsi_ctrl_hw *ctrl,
- u32 stream_id);
ssize_t dsi_ctrl_hw_14_reg_dump_to_buffer(struct dsi_ctrl_hw *ctrl,
char *buf,
u32 size);
+
+/* Definitions specific to 2.0 DSI controller hardware */
+void dsi_ctrl_hw_20_setup_lane_map(struct dsi_ctrl_hw *ctrl,
+ struct dsi_lane_map *lane_map);
+int dsi_ctrl_hw_20_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl, u32 lanes);
+ssize_t dsi_ctrl_hw_20_reg_dump_to_buffer(struct dsi_ctrl_hw *ctrl,
+ char *buf,
+ u32 size);
+
#endif /* _DSI_CATALOG_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h b/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h
new file mode 100644
index 0000000..6d73b21
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DSI_CLK_H_
+#define _DSI_CLK_H_
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+
+#define MAX_STRING_LEN 32
+#define MAX_DSI_CTRL 2
+
+enum dsi_clk_state {
+ DSI_CLK_OFF,
+ DSI_CLK_ON,
+ DSI_CLK_EARLY_GATE,
+};
+
+enum clk_req_client {
+ DSI_CLK_REQ_MDP_CLIENT = 0,
+ DSI_CLK_REQ_DSI_CLIENT,
+};
+
+enum dsi_link_clk_type {
+ DSI_LINK_ESC_CLK,
+ DSI_LINK_BYTE_CLK,
+ DSI_LINK_PIX_CLK,
+ DSI_LINK_BYTE_INTF_CLK,
+ DSI_LINK_CLK_MAX,
+};
+
+enum dsi_clk_type {
+ DSI_CORE_CLK = BIT(0),
+ DSI_LINK_CLK = BIT(1),
+ DSI_ALL_CLKS = (BIT(0) | BIT(1)),
+ DSI_CLKS_MAX = BIT(2),
+};
+
+struct dsi_clk_ctrl_info {
+ enum dsi_clk_type clk_type;
+ enum dsi_clk_state clk_state;
+ enum clk_req_client client;
+};
+
+struct clk_ctrl_cb {
+ void *priv;
+ int (*dsi_clk_cb)(void *priv, struct dsi_clk_ctrl_info clk_ctrl_info);
+};
+
+/**
+ * struct dsi_core_clk_info - Core clock information for DSI hardware
+ * @mdp_core_clk: Handle to MDP core clock.
+ * @iface_clk: Handle to MDP interface clock.
+ * @core_mmss_clk: Handle to MMSS core clock.
+ * @bus_clk: Handle to bus clock.
+ * @mnoc_clk: Handle to MMSS NOC clock.
+ */
+struct dsi_core_clk_info {
+ struct clk *mdp_core_clk;
+ struct clk *iface_clk;
+ struct clk *core_mmss_clk;
+ struct clk *bus_clk;
+ struct clk *mnoc_clk;
+};
+
+/**
+ * struct dsi_link_clk_info - Link clock information for DSI hardware.
+ * @byte_clk: Handle to DSI byte clock.
+ * @pixel_clk: Handle to DSI pixel clock.
+ * @esc_clk: Handle to DSI escape clock.
+ * @byte_intf_clk: Handle to DSI byte intf. clock.
+ */
+struct dsi_link_clk_info {
+ struct clk *byte_clk;
+ struct clk *pixel_clk;
+ struct clk *esc_clk;
+ struct clk *byte_intf_clk;
+};
+
+/**
+ * struct link_clk_freq - Clock frequency information for Link clocks
+ * @byte_clk_rate: Frequency of DSI byte clock in KHz.
+ * @pixel_clk_rate: Frequency of DSI pixel clock in KHz.
+ * @esc_clk_rate: Frequency of DSI escape clock in KHz.
+ */
+struct link_clk_freq {
+ u32 byte_clk_rate;
+ u32 pix_clk_rate;
+ u32 esc_clk_rate;
+};
+
+/**
+ * typedef *pre_clockoff_cb() - Callback before clock is turned off
+ * @priv: private data pointer.
+ * @clk_type: clock which is being turned off.
+ * @new_state: next state for the clock.
+ *
+ * @return: error code.
+ */
+typedef int (*pre_clockoff_cb)(void *priv,
+ enum dsi_clk_type clk_type,
+ enum dsi_clk_state new_state);
+
+/**
+ * typedef *post_clockoff_cb() - Callback after clock is turned off
+ * @priv: private data pointer.
+ * @clk_type: clock which was turned off.
+ * @curr_state: current state for the clock.
+ *
+ * @return: error code.
+ */
+typedef int (*post_clockoff_cb)(void *priv,
+ enum dsi_clk_type clk_type,
+ enum dsi_clk_state curr_state);
+
+/**
+ * typedef *post_clockon_cb() - Callback after clock is turned on
+ * @priv: private data pointer.
+ * @clk_type: clock which was turned on.
+ * @curr_state: current state for the clock.
+ *
+ * @return: error code.
+ */
+typedef int (*post_clockon_cb)(void *priv,
+ enum dsi_clk_type clk_type,
+ enum dsi_clk_state curr_state);
+
+/**
+ * typedef *pre_clockon_cb() - Callback before clock is turned on
+ * @priv: private data pointer.
+ * @clk_type: clock which is being turned on.
+ * @new_state: next state for the clock.
+ *
+ * @return: error code.
+ */
+typedef int (*pre_clockon_cb)(void *priv,
+ enum dsi_clk_type clk_type,
+ enum dsi_clk_state new_state);
+
+
+struct dsi_clk_info {
+ char name[MAX_STRING_LEN];
+ struct dsi_core_clk_info c_clks[MAX_DSI_CTRL];
+ struct dsi_link_clk_info l_clks[MAX_DSI_CTRL];
+ u32 bus_handle[MAX_DSI_CTRL];
+ pre_clockoff_cb pre_clkoff_cb;
+ post_clockoff_cb post_clkoff_cb;
+ post_clockon_cb post_clkon_cb;
+ pre_clockon_cb pre_clkon_cb;
+ void *priv_data;
+ u32 master_ndx;
+ u32 dsi_ctrl_count;
+};
+
+/**
+ * struct dsi_clk_link_set - Pair of clock handles to describe link clocks
+ * @byte_clk: Handle to DSi byte clock.
+ * @pixel_clk: Handle to DSI pixel clock.
+ */
+struct dsi_clk_link_set {
+ struct clk *byte_clk;
+ struct clk *pixel_clk;
+};
+
+/**
+ * dsi_display_clk_mgr_register() - Register DSI clock manager
+ * @info: Structure containing DSI clock information
+ */
+void *dsi_display_clk_mngr_register(struct dsi_clk_info *info);
+
+/**
+ * dsi_display_clk_mngr_deregister() - Deregister DSI clock manager
+ * @clk_mngr: DSI clock manager pointer
+ */
+int dsi_display_clk_mngr_deregister(void *clk_mngr);
+
+/**
+ * dsi_register_clk_handle() - Register clock handle with DSI clock manager
+ * @clk_mngr: DSI clock manager pointer
+ * @client: DSI clock client pointer.
+ */
+void *dsi_register_clk_handle(void *clk_mngr, char *client);
+
+/**
+ * dsi_deregister_clk_handle() - Deregister clock handle from DSI clock manager
+ * @client: DSI clock client pointer.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_deregister_clk_handle(void *client);
+
+/**
+ * dsi_display_clk_ctrl() - set frequencies for link clks
+ * @handle: Handle of desired DSI clock client.
+ * @clk_type: Clock which is being controlled.
+ * @clk_state: Desired state of clock
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_display_clk_ctrl(void *handle,
+ enum dsi_clk_type clk_type, enum dsi_clk_state clk_state);
+
+/**
+ * dsi_clk_set_link_frequencies() - set frequencies for link clks
+ * @client: DSI clock client pointer.
+ * @freq: Structure containing link clock frequencies.
+ * @index: Index of the DSI controller.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_link_frequencies(void *client, struct link_clk_freq freq,
+ u32 index);
+
+
+/**
+ * dsi_clk_set_pixel_clk_rate() - set frequency for pixel clock
+ * @client: DSI clock client pointer.
+ * @pixel_clk: Pixel clock rate in Hz.
+ * @index: Index of the DSI controller.
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_pixel_clk_rate(void *client, u64 pixel_clk, u32 index);
+
+
+/**
+ * dsi_clk_set_byte_clk_rate() - set frequency for byte clock
+ * @client: DSI clock client pointer.
+ * @byte_clk: Pixel clock rate in Hz.
+ * @index: Index of the DSI controller.
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index);
+
+/**
+ * dsi_clk_update_parent() - update parent clocks for specified clock
+ * @parent: link clock pair which are set as parent.
+ * @child: link clock pair whose parent has to be set.
+ */
+int dsi_clk_update_parent(struct dsi_clk_link_set *parent,
+ struct dsi_clk_link_set *child);
+#endif /* _DSI_CLK_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c
new file mode 100644
index 0000000..feeda8b
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c
@@ -0,0 +1,1135 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/msm-bus.h>
+#include "dsi_clk.h"
+
+struct dsi_core_clks {
+ struct dsi_core_clk_info clks;
+ u32 bus_handle;
+};
+
+struct dsi_link_clks {
+ struct dsi_link_clk_info clks;
+ struct link_clk_freq freq;
+};
+
+struct dsi_clk_mngr {
+ char name[MAX_STRING_LEN];
+ struct mutex clk_mutex;
+ struct list_head client_list;
+
+ u32 dsi_ctrl_count;
+ u32 master_ndx;
+ struct dsi_core_clks core_clks[MAX_DSI_CTRL];
+ struct dsi_link_clks link_clks[MAX_DSI_CTRL];
+ u32 core_clk_state;
+ u32 link_clk_state;
+
+ pre_clockoff_cb pre_clkoff_cb;
+ post_clockoff_cb post_clkoff_cb;
+ post_clockon_cb post_clkon_cb;
+ pre_clockon_cb pre_clkon_cb;
+
+ void *priv_data;
+};
+
+struct dsi_clk_client_info {
+ char name[MAX_STRING_LEN];
+ u32 core_refcount;
+ u32 link_refcount;
+ u32 core_clk_state;
+ u32 link_clk_state;
+ struct list_head list;
+ struct dsi_clk_mngr *mngr;
+};
+
+/**
+ * dsi_clk_set_link_frequencies() - set frequencies for link clks
+ * @clks: Link clock information
+ * @pixel_clk: pixel clock frequency in KHz.
+ * @byte_clk: Byte clock frequency in KHz.
+ * @esc_clk: Escape clock frequency in KHz.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_link_frequencies(void *client, struct link_clk_freq freq,
+ u32 index)
+{
+ int rc = 0;
+ struct dsi_clk_client_info *c = client;
+ struct dsi_clk_mngr *mngr;
+
+ if (!client) {
+ pr_err("invalid params\n");
+ return -EINVAL;
+ }
+
+ mngr = c->mngr;
+ memcpy(&mngr->link_clks[index].freq, &freq,
+ sizeof(struct link_clk_freq));
+ return rc;
+}
+
+/**
+ * dsi_clk_set_pixel_clk_rate() - set frequency for pixel clock
+ * @clks: DSI link clock information.
+ * @pixel_clk: Pixel clock rate in KHz.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_pixel_clk_rate(void *client, u64 pixel_clk, u32 index)
+{
+ int rc = 0;
+ struct dsi_clk_client_info *c = client;
+ struct dsi_clk_mngr *mngr;
+
+ mngr = c->mngr;
+ rc = clk_set_rate(mngr->link_clks[index].clks.pixel_clk, pixel_clk);
+ if (rc)
+ pr_err("failed to set clk rate for pixel clk, rc=%d\n", rc);
+ else
+ mngr->link_clks[index].freq.pix_clk_rate = pixel_clk;
+
+ return rc;
+}
+
+/**
+ * dsi_clk_set_byte_clk_rate() - set frequency for byte clock
+ * @client: DSI clock client pointer.
+ * @byte_clk: Pixel clock rate in Hz.
+ * @index: Index of the DSI controller.
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index)
+{
+ int rc = 0;
+ struct dsi_clk_client_info *c = client;
+ struct dsi_clk_mngr *mngr;
+
+ mngr = c->mngr;
+ rc = clk_set_rate(mngr->link_clks[index].clks.byte_clk, byte_clk);
+ if (rc)
+ pr_err("failed to set clk rate for byte clk, rc=%d\n", rc);
+ else
+ mngr->link_clks[index].freq.byte_clk_rate = byte_clk;
+
+ return rc;
+
+}
+
+/**
+ * dsi_clk_update_parent() - update parent clocks for specified clock
+ * @parent: link clock pair which are set as parent.
+ * @child: link clock pair whose parent has to be set.
+ */
+int dsi_clk_update_parent(struct dsi_clk_link_set *parent,
+ struct dsi_clk_link_set *child)
+{
+ int rc = 0;
+
+ rc = clk_set_parent(child->byte_clk, parent->byte_clk);
+ if (rc) {
+ pr_err("failed to set byte clk parent\n");
+ goto error;
+ }
+
+ rc = clk_set_parent(child->pixel_clk, parent->pixel_clk);
+ if (rc) {
+ pr_err("failed to set pixel clk parent\n");
+ goto error;
+ }
+error:
+ return rc;
+}
+
+int dsi_core_clk_start(struct dsi_core_clks *c_clks)
+{
+ int rc = 0;
+
+ rc = clk_prepare_enable(c_clks->clks.mdp_core_clk);
+ if (rc) {
+ pr_err("failed to enable mdp_core_clk, rc=%d\n", rc);
+ goto error;
+ }
+
+ if (c_clks->clks.mnoc_clk) {
+ rc = clk_prepare_enable(c_clks->clks.mnoc_clk);
+ if (rc) {
+ pr_err("failed to enable mnoc_clk, rc=%d\n", rc);
+ goto error_disable_core_clk;
+ }
+ }
+
+ rc = clk_prepare_enable(c_clks->clks.iface_clk);
+ if (rc) {
+ pr_err("failed to enable iface_clk, rc=%d\n", rc);
+ goto error_disable_mnoc_clk;
+ }
+
+ rc = clk_prepare_enable(c_clks->clks.bus_clk);
+ if (rc) {
+ pr_err("failed to enable bus_clk, rc=%d\n", rc);
+ goto error_disable_iface_clk;
+ }
+
+ rc = clk_prepare_enable(c_clks->clks.core_mmss_clk);
+ if (rc) {
+ pr_err("failed to enable core_mmss_clk, rc=%d\n", rc);
+ goto error_disable_bus_clk;
+ }
+
+ rc = msm_bus_scale_client_update_request(c_clks->bus_handle, 1);
+ if (rc) {
+ pr_err("bus scale client enable failed, rc=%d\n", rc);
+ goto error_disable_mmss_clk;
+ }
+
+ return rc;
+
+error_disable_mmss_clk:
+ clk_disable_unprepare(c_clks->clks.core_mmss_clk);
+error_disable_bus_clk:
+ clk_disable_unprepare(c_clks->clks.bus_clk);
+error_disable_iface_clk:
+ clk_disable_unprepare(c_clks->clks.iface_clk);
+error_disable_mnoc_clk:
+ if (c_clks->clks.mnoc_clk)
+ clk_disable_unprepare(c_clks->clks.mnoc_clk);
+error_disable_core_clk:
+ clk_disable_unprepare(c_clks->clks.mdp_core_clk);
+error:
+ return rc;
+}
+
+int dsi_core_clk_stop(struct dsi_core_clks *c_clks)
+{
+ if (msm_bus_scale_client_update_request(c_clks->bus_handle, 0))
+ pr_err("bus scale client disable failed\n");
+ clk_disable_unprepare(c_clks->clks.core_mmss_clk);
+ clk_disable_unprepare(c_clks->clks.bus_clk);
+ clk_disable_unprepare(c_clks->clks.iface_clk);
+ if (c_clks->clks.mnoc_clk)
+ clk_disable_unprepare(c_clks->clks.mnoc_clk);
+ clk_disable_unprepare(c_clks->clks.mdp_core_clk);
+
+ return 0;
+}
+
+static int dsi_link_clk_set_rate(struct dsi_link_clks *l_clks)
+{
+ int rc = 0;
+
+ rc = clk_set_rate(l_clks->clks.esc_clk, l_clks->freq.esc_clk_rate);
+ if (rc) {
+ pr_err("clk_set_rate failed for esc_clk rc = %d\n", rc);
+ goto error;
+ }
+
+ rc = clk_set_rate(l_clks->clks.byte_clk, l_clks->freq.byte_clk_rate);
+ if (rc) {
+ pr_err("clk_set_rate failed for byte_clk rc = %d\n", rc);
+ goto error;
+ }
+
+ rc = clk_set_rate(l_clks->clks.pixel_clk, l_clks->freq.pix_clk_rate);
+ if (rc) {
+ pr_err("clk_set_rate failed for pixel_clk rc = %d\n", rc);
+ goto error;
+ }
+
+ /*
+ * If byte_intf_clk is present, set rate for that too.
+ * For DPHY: byte_intf_clk_rate = byte_clk_rate / 2
+ * todo: this needs to be revisited when support for CPHY is added
+ */
+ if (l_clks->clks.byte_intf_clk) {
+ rc = clk_set_rate(l_clks->clks.byte_intf_clk,
+ (l_clks->freq.byte_clk_rate / 2));
+ if (rc) {
+ pr_err("set_rate failed for byte_intf_clk rc = %d\n",
+ rc);
+ goto error;
+ }
+ }
+error:
+ return rc;
+}
+
+static int dsi_link_clk_prepare(struct dsi_link_clks *l_clks)
+{
+ int rc = 0;
+
+ rc = clk_prepare(l_clks->clks.esc_clk);
+ if (rc) {
+ pr_err("Failed to prepare dsi esc clk, rc=%d\n", rc);
+ goto esc_clk_err;
+ }
+
+ rc = clk_prepare(l_clks->clks.byte_clk);
+ if (rc) {
+ pr_err("Failed to prepare dsi byte clk, rc=%d\n", rc);
+ goto byte_clk_err;
+ }
+
+ rc = clk_prepare(l_clks->clks.pixel_clk);
+ if (rc) {
+ pr_err("Failed to prepare dsi pixel clk, rc=%d\n", rc);
+ goto pixel_clk_err;
+ }
+
+ if (l_clks->clks.byte_intf_clk) {
+ rc = clk_prepare(l_clks->clks.byte_intf_clk);
+ if (rc) {
+ pr_err("Failed to prepare dsi byte intf clk, rc=%d\n",
+ rc);
+ goto byte_intf_clk_err;
+ }
+ }
+
+ return rc;
+
+byte_intf_clk_err:
+ clk_unprepare(l_clks->clks.pixel_clk);
+pixel_clk_err:
+ clk_unprepare(l_clks->clks.byte_clk);
+byte_clk_err:
+ clk_unprepare(l_clks->clks.esc_clk);
+esc_clk_err:
+ return rc;
+}
+
+static void dsi_link_clk_unprepare(struct dsi_link_clks *l_clks)
+{
+ if (l_clks->clks.byte_intf_clk)
+ clk_unprepare(l_clks->clks.byte_intf_clk);
+ clk_unprepare(l_clks->clks.pixel_clk);
+ clk_unprepare(l_clks->clks.byte_clk);
+ clk_unprepare(l_clks->clks.esc_clk);
+}
+
+static int dsi_link_clk_enable(struct dsi_link_clks *l_clks)
+{
+ int rc = 0;
+
+ rc = clk_enable(l_clks->clks.esc_clk);
+ if (rc) {
+ pr_err("Failed to enable dsi esc clk, rc=%d\n", rc);
+ goto esc_clk_err;
+ }
+
+ rc = clk_enable(l_clks->clks.byte_clk);
+ if (rc) {
+ pr_err("Failed to enable dsi byte clk, rc=%d\n", rc);
+ goto byte_clk_err;
+ }
+
+ rc = clk_enable(l_clks->clks.pixel_clk);
+ if (rc) {
+ pr_err("Failed to enable dsi pixel clk, rc=%d\n", rc);
+ goto pixel_clk_err;
+ }
+
+ if (l_clks->clks.byte_intf_clk) {
+ rc = clk_enable(l_clks->clks.byte_intf_clk);
+ if (rc) {
+ pr_err("Failed to enable dsi byte intf clk, rc=%d\n",
+ rc);
+ goto byte_intf_clk_err;
+ }
+ }
+
+ return rc;
+
+byte_intf_clk_err:
+ clk_disable(l_clks->clks.pixel_clk);
+pixel_clk_err:
+ clk_disable(l_clks->clks.byte_clk);
+byte_clk_err:
+ clk_disable(l_clks->clks.esc_clk);
+esc_clk_err:
+ return rc;
+}
+
+static void dsi_link_clk_disable(struct dsi_link_clks *l_clks)
+{
+ if (l_clks->clks.byte_intf_clk)
+ clk_disable(l_clks->clks.byte_intf_clk);
+ clk_disable(l_clks->clks.esc_clk);
+ clk_disable(l_clks->clks.pixel_clk);
+ clk_disable(l_clks->clks.byte_clk);
+}
+
+/**
+ * dsi_link_clk_start() - enable dsi link clocks
+ */
+int dsi_link_clk_start(struct dsi_link_clks *clks)
+{
+ int rc = 0;
+
+ rc = dsi_link_clk_set_rate(clks);
+ if (rc) {
+ pr_err("failed to set clk rates, rc = %d\n", rc);
+ goto error;
+ }
+
+ rc = dsi_link_clk_prepare(clks);
+ if (rc) {
+ pr_err("failed to prepare link clks, rc = %d\n", rc);
+ goto error;
+ }
+
+ rc = dsi_link_clk_enable(clks);
+ if (rc) {
+ pr_err("failed to enable link clks, rc = %d\n", rc);
+ goto error_unprepare;
+ }
+
+ pr_debug("Link clocks are enabled\n");
+ return rc;
+error_unprepare:
+ dsi_link_clk_unprepare(clks);
+error:
+ return rc;
+}
+
+/**
+ * dsi_link_clk_stop() - Stop DSI link clocks.
+ */
+int dsi_link_clk_stop(struct dsi_link_clks *clks)
+{
+ dsi_link_clk_disable(clks);
+ dsi_link_clk_unprepare(clks);
+
+ pr_debug("Link clocks disabled\n");
+
+ return 0;
+}
+
+static int dsi_display_core_clk_enable(struct dsi_core_clks *clks,
+ u32 ctrl_count, u32 master_ndx)
+{
+ int rc = 0;
+ int i;
+ struct dsi_core_clks *clk, *m_clks;
+
+ /*
+ * In case of split DSI usecases, the clock for master controller should
+ * be enabled before the other controller. Master controller in the
+ * clock context refers to the controller that sources the clock.
+ */
+
+ m_clks = &clks[master_ndx];
+
+ rc = dsi_core_clk_start(m_clks);
+ if (rc) {
+ pr_err("failed to turn on master clocks, rc=%d\n", rc);
+ goto error;
+ }
+
+ /* Turn on rest of the core clocks */
+ for (i = 0; i < ctrl_count; i++) {
+ clk = &clks[i];
+ if (!clk || (clk == m_clks))
+ continue;
+
+ rc = dsi_core_clk_start(clk);
+ if (rc) {
+ pr_err("failed to turn on clocks, rc=%d\n", rc);
+ goto error_disable_master;
+ }
+ }
+ return rc;
+error_disable_master:
+ (void)dsi_core_clk_stop(m_clks);
+error:
+ return rc;
+}
+
+static int dsi_display_link_clk_enable(struct dsi_link_clks *clks,
+ u32 ctrl_count, u32 master_ndx)
+{
+ int rc = 0;
+ int i;
+ struct dsi_link_clks *clk, *m_clks;
+
+ /*
+ * In case of split DSI usecases, the clock for master controller should
+ * be enabled before the other controller. Master controller in the
+ * clock context refers to the controller that sources the clock.
+ */
+
+ m_clks = &clks[master_ndx];
+
+ rc = dsi_link_clk_start(m_clks);
+ if (rc) {
+ pr_err("failed to turn on master clocks, rc=%d\n", rc);
+ goto error;
+ }
+
+ /* Turn on rest of the core clocks */
+ for (i = 0; i < ctrl_count; i++) {
+ clk = &clks[i];
+ if (!clk || (clk == m_clks))
+ continue;
+
+ rc = dsi_link_clk_start(clk);
+ if (rc) {
+ pr_err("failed to turn on clocks, rc=%d\n", rc);
+ goto error_disable_master;
+ }
+ }
+ return rc;
+error_disable_master:
+ (void)dsi_link_clk_stop(m_clks);
+error:
+ return rc;
+}
+
+static int dsi_display_core_clk_disable(struct dsi_core_clks *clks,
+ u32 ctrl_count, u32 master_ndx)
+{
+ int rc = 0;
+ int i;
+ struct dsi_core_clks *clk, *m_clks;
+
+ /*
+ * In case of split DSI usecases, clock for slave DSI controllers should
+ * be disabled first before disabling clock for master controller. Slave
+ * controllers in the clock context refer to controller which source
+ * clock from another controller.
+ */
+
+ m_clks = &clks[master_ndx];
+
+ /* Turn off non-master core clocks */
+ for (i = 0; i < ctrl_count; i++) {
+ clk = &clks[i];
+ if (!clk || (clk == m_clks))
+ continue;
+
+ rc = dsi_core_clk_stop(clk);
+ if (rc)
+ pr_err("failed to turn off clocks, rc=%d\n", rc);
+ }
+
+ rc = dsi_core_clk_stop(m_clks);
+ if (rc)
+ pr_err("failed to turn off master clocks, rc=%d\n", rc);
+
+ return rc;
+}
+
+static int dsi_display_link_clk_disable(struct dsi_link_clks *clks,
+ u32 ctrl_count, u32 master_ndx)
+{
+ int rc = 0;
+ int i;
+ struct dsi_link_clks *clk, *m_clks;
+
+ /*
+ * In case of split DSI usecases, clock for slave DSI controllers should
+ * be disabled first before disabling clock for master controller. Slave
+ * controllers in the clock context refer to controller which source
+ * clock from another controller.
+ */
+
+ m_clks = &clks[master_ndx];
+
+ /* Turn off non-master link clocks */
+ for (i = 0; i < ctrl_count; i++) {
+ clk = &clks[i];
+ if (!clk || (clk == m_clks))
+ continue;
+
+ rc = dsi_link_clk_stop(clk);
+ if (rc)
+ pr_err("failed to turn off clocks, rc=%d\n", rc);
+ }
+
+ rc = dsi_link_clk_stop(m_clks);
+ if (rc)
+ pr_err("failed to turn off master clocks, rc=%d\n", rc);
+
+ return rc;
+}
+
+static int dsi_update_clk_state(struct dsi_core_clks *c_clks, u32 c_state,
+ struct dsi_link_clks *l_clks, u32 l_state)
+{
+ int rc = 0;
+ struct dsi_clk_mngr *mngr;
+ bool l_c_on = false;
+
+ if (c_clks) {
+ mngr =
+ container_of(c_clks, struct dsi_clk_mngr, core_clks[0]);
+ } else if (l_clks) {
+ mngr =
+ container_of(l_clks, struct dsi_clk_mngr, link_clks[0]);
+ } else {
+ mngr = NULL;
+ }
+
+ if (!mngr)
+ return -EINVAL;
+
+ pr_debug("c_state = %d, l_state = %d\n",
+ c_clks ? c_state : -1, l_clks ? l_state : -1);
+ /*
+ * Below is the sequence to toggle DSI clocks:
+ * 1. For ON sequence, Core clocks before link clocks
+ * 2. For OFF sequence, Link clocks before core clocks.
+ */
+ if (c_clks && (c_state == DSI_CLK_ON)) {
+ if (mngr->core_clk_state == DSI_CLK_OFF) {
+ rc = mngr->pre_clkon_cb(mngr->priv_data,
+ DSI_CORE_CLK,
+ DSI_CLK_ON);
+ if (rc) {
+ pr_err("failed to turn on MDP FS rc= %d\n", rc);
+ goto error;
+ }
+ }
+ rc = dsi_display_core_clk_enable(c_clks, mngr->dsi_ctrl_count,
+ mngr->master_ndx);
+ if (rc) {
+ pr_err("failed to turn on core clks rc = %d\n", rc);
+ goto error;
+ }
+
+ if (mngr->post_clkon_cb) {
+ rc = mngr->post_clkon_cb(mngr->priv_data,
+ DSI_CORE_CLK,
+ DSI_CLK_ON);
+ if (rc)
+ pr_err("post clk on cb failed, rc = %d\n", rc);
+ }
+ mngr->core_clk_state = DSI_CLK_ON;
+ }
+
+ if (l_clks) {
+ if (l_state == DSI_CLK_ON) {
+ if (mngr->pre_clkon_cb) {
+ rc = mngr->pre_clkon_cb(mngr->priv_data,
+ DSI_LINK_CLK, l_state);
+ if (rc)
+ pr_err("pre link clk on cb failed\n");
+ }
+ rc = dsi_display_link_clk_enable(l_clks,
+ mngr->dsi_ctrl_count, mngr->master_ndx);
+ if (rc) {
+ pr_err("failed to start link clk rc= %d\n", rc);
+ goto error;
+ }
+ if (mngr->post_clkon_cb) {
+ rc = mngr->post_clkon_cb(mngr->priv_data,
+ DSI_LINK_CLK,
+ l_state);
+ if (rc)
+ pr_err("post link clk on cb failed\n");
+ }
+ } else {
+ /*
+ * Two conditions that need to be checked for Link
+ * clocks:
+ * 1. Link clocks need core clocks to be on when
+ * transitioning from EARLY_GATE to OFF state.
+ * 2. ULPS mode might have to be enabled in case of OFF
+ * state. For ULPS, Link clocks should be turned ON
+ * first before they are turned off again.
+ *
+ * If Link is going from EARLY_GATE to OFF state AND
+ * Core clock is already in EARLY_GATE or OFF state,
+ * turn on Core clocks and link clocks.
+ *
+ * ULPS state is managed as part of the pre_clkoff_cb.
+ */
+ if ((l_state == DSI_CLK_OFF) &&
+ (mngr->link_clk_state ==
+ DSI_CLK_EARLY_GATE) &&
+ (mngr->core_clk_state !=
+ DSI_CLK_ON)) {
+ rc = dsi_display_core_clk_enable(
+ mngr->core_clks, mngr->dsi_ctrl_count,
+ mngr->master_ndx);
+ if (rc) {
+ pr_err("core clks did not start\n");
+ goto error;
+ }
+
+ rc = dsi_display_link_clk_enable(l_clks,
+ mngr->dsi_ctrl_count, mngr->master_ndx);
+ if (rc) {
+ pr_err("Link clks did not start\n");
+ goto error;
+ }
+ l_c_on = true;
+ pr_debug("ECG: core and Link_on\n");
+ }
+
+ if (mngr->pre_clkoff_cb) {
+ rc = mngr->pre_clkoff_cb(mngr->priv_data,
+ DSI_LINK_CLK, l_state);
+ if (rc)
+ pr_err("pre link clk off cb failed\n");
+ }
+
+ rc = dsi_display_link_clk_disable(l_clks,
+ mngr->dsi_ctrl_count, mngr->master_ndx);
+ if (rc) {
+ pr_err("failed to stop link clk, rc = %d\n",
+ rc);
+ goto error;
+ }
+
+ if (mngr->post_clkoff_cb) {
+ rc = mngr->post_clkoff_cb(mngr->priv_data,
+ DSI_LINK_CLK, l_state);
+ if (rc)
+ pr_err("post link clk off cb failed\n");
+ }
+ /*
+ * This check is to save unnecessary clock state
+ * change when going from EARLY_GATE to OFF. In the
+ * case where the request happens for both Core and Link
+ * clocks in the same call, core clocks need to be
+ * turned on first before OFF state can be entered.
+ *
+ * Core clocks are turned on here for Link clocks to go
+ * to OFF state. If core clock request is also present,
+ * then core clocks can be turned off Core clocks are
+ * transitioned to OFF state.
+ */
+ if (l_c_on && (!(c_clks && (c_state == DSI_CLK_OFF)
+ && (mngr->core_clk_state ==
+ DSI_CLK_EARLY_GATE)))) {
+ rc = dsi_display_core_clk_disable(
+ mngr->core_clks, mngr->dsi_ctrl_count,
+ mngr->master_ndx);
+ if (rc) {
+ pr_err("core clks did not stop\n");
+ goto error;
+ }
+
+ l_c_on = false;
+ pr_debug("ECG: core off\n");
+ } else
+ pr_debug("ECG: core off skip\n");
+ }
+
+ mngr->link_clk_state = l_state;
+ }
+
+ if (c_clks && (c_state != DSI_CLK_ON)) {
+ /*
+ * When going to OFF state from EARLY GATE state, Core clocks
+ * should be turned on first so that the IOs can be clamped.
+ * l_c_on flag is set, then the core clocks were turned before
+ * to the Link clocks go to OFF state. So Core clocks are
+ * already ON and this step can be skipped.
+ *
+ * IOs are clamped in pre_clkoff_cb callback.
+ */
+ if ((c_state == DSI_CLK_OFF) &&
+ (mngr->core_clk_state ==
+ DSI_CLK_EARLY_GATE) && !l_c_on) {
+ rc = dsi_display_core_clk_enable(mngr->core_clks,
+ mngr->dsi_ctrl_count, mngr->master_ndx);
+ if (rc) {
+ pr_err("core clks did not start\n");
+ goto error;
+ }
+ pr_debug("ECG: core on\n");
+ } else
+ pr_debug("ECG: core on skip\n");
+
+ if (mngr->pre_clkoff_cb) {
+ rc = mngr->pre_clkoff_cb(mngr->priv_data,
+ DSI_CORE_CLK,
+ c_state);
+ if (rc)
+ pr_err("pre core clk off cb failed\n");
+ }
+
+ rc = dsi_display_core_clk_disable(c_clks, mngr->dsi_ctrl_count,
+ mngr->master_ndx);
+ if (rc) {
+ pr_err("failed to turn off core clks rc = %d\n", rc);
+ goto error;
+ }
+
+ if (c_state == DSI_CLK_OFF) {
+ if (mngr->post_clkoff_cb) {
+ rc = mngr->post_clkoff_cb(mngr->priv_data,
+ DSI_CORE_CLK,
+ DSI_CLK_OFF);
+ if (rc)
+ pr_err("post clkoff cb fail, rc = %d\n",
+ rc);
+ }
+ }
+ mngr->core_clk_state = c_state;
+ }
+
+error:
+ return rc;
+}
+
+static int dsi_recheck_clk_state(struct dsi_clk_mngr *mngr)
+{
+ int rc = 0;
+ struct list_head *pos = NULL;
+ struct dsi_clk_client_info *c;
+ u32 new_core_clk_state = DSI_CLK_OFF;
+ u32 new_link_clk_state = DSI_CLK_OFF;
+ u32 old_c_clk_state = DSI_CLK_OFF;
+ u32 old_l_clk_state = DSI_CLK_OFF;
+ struct dsi_core_clks *c_clks = NULL;
+ struct dsi_link_clks *l_clks = NULL;
+
+ /*
+ * Conditions to maintain DSI manager clock state based on
+ * clock states of various clients:
+ * 1. If any client has clock in ON state, DSI manager clock state
+ * should be ON.
+ * 2. If any client is in ECG state with rest of them turned OFF,
+ * go to Early gate state.
+ * 3. If all clients have clocks as OFF, then go to OFF state.
+ */
+ list_for_each(pos, &mngr->client_list) {
+ c = list_entry(pos, struct dsi_clk_client_info, list);
+ if (c->core_clk_state == DSI_CLK_ON) {
+ new_core_clk_state = DSI_CLK_ON;
+ break;
+ } else if (c->core_clk_state == DSI_CLK_EARLY_GATE) {
+ new_core_clk_state = DSI_CLK_EARLY_GATE;
+ }
+ }
+
+ list_for_each(pos, &mngr->client_list) {
+ c = list_entry(pos, struct dsi_clk_client_info, list);
+ if (c->link_clk_state == DSI_CLK_ON) {
+ new_link_clk_state = DSI_CLK_ON;
+ break;
+ } else if (c->link_clk_state == DSI_CLK_EARLY_GATE) {
+ new_link_clk_state = DSI_CLK_EARLY_GATE;
+ }
+ }
+
+ if (new_core_clk_state != mngr->core_clk_state)
+ c_clks = mngr->core_clks;
+
+ if (new_link_clk_state != mngr->link_clk_state)
+ l_clks = mngr->link_clks;
+
+ old_c_clk_state = mngr->core_clk_state;
+ old_l_clk_state = mngr->link_clk_state;
+
+ pr_debug("c_clk_state (%d -> %d)\n",
+ old_c_clk_state, new_core_clk_state);
+ pr_debug("l_clk_state (%d -> %d)\n",
+ old_l_clk_state, new_link_clk_state);
+
+ if (c_clks || l_clks) {
+ rc = dsi_update_clk_state(c_clks, new_core_clk_state,
+ l_clks, new_link_clk_state);
+ if (rc) {
+ pr_err("failed to update clock state, rc = %d\n", rc);
+ goto error;
+ }
+ }
+
+error:
+ return rc;
+}
+
+int dsi_clk_req_state(void *client, enum dsi_clk_type clk,
+ enum dsi_clk_state state)
+{
+ int rc = 0;
+ struct dsi_clk_client_info *c = client;
+ struct dsi_clk_mngr *mngr;
+ bool changed = false;
+
+ if (!client || !clk || clk > (DSI_CORE_CLK | DSI_LINK_CLK) ||
+ state > DSI_CLK_EARLY_GATE) {
+ pr_err("Invalid params, client = %pK, clk = 0x%x, state = %d\n",
+ client, clk, state);
+ return -EINVAL;
+ }
+
+ mngr = c->mngr;
+ mutex_lock(&mngr->clk_mutex);
+
+ pr_debug("[%s]%s: CLK=%d, new_state=%d, core=%d, linkl=%d\n",
+ mngr->name, c->name, clk, state, c->core_clk_state,
+ c->link_clk_state);
+
+ /*
+ * Clock refcount handling as below:
+ * i. Increment refcount whenever ON is called.
+ * ii. Decrement refcount when transitioning from ON state to
+ * either OFF or EARLY_GATE.
+ * iii. Do not decrement refcount when changing from
+ * EARLY_GATE to OFF.
+ */
+ if (state == DSI_CLK_ON) {
+ if (clk & DSI_CORE_CLK) {
+ c->core_refcount++;
+ if (c->core_clk_state != DSI_CLK_ON) {
+ c->core_clk_state = DSI_CLK_ON;
+ changed = true;
+ }
+ }
+ if (clk & DSI_LINK_CLK) {
+ c->link_refcount++;
+ if (c->link_clk_state != DSI_CLK_ON) {
+ c->link_clk_state = DSI_CLK_ON;
+ changed = true;
+ }
+ }
+ } else if ((state == DSI_CLK_EARLY_GATE) ||
+ (state == DSI_CLK_OFF)) {
+ if (clk & DSI_CORE_CLK) {
+ if (c->core_refcount == 0) {
+ if ((c->core_clk_state ==
+ DSI_CLK_EARLY_GATE) &&
+ (state == DSI_CLK_OFF)) {
+ changed = true;
+ c->core_clk_state = DSI_CLK_OFF;
+ } else {
+ pr_warn("Core refcount is zero for %s",
+ c->name);
+ }
+ } else {
+ c->core_refcount--;
+ if (c->core_refcount == 0) {
+ c->core_clk_state = state;
+ changed = true;
+ }
+ }
+ }
+ if (clk & DSI_LINK_CLK) {
+ if (c->link_refcount == 0) {
+ if ((c->link_clk_state ==
+ DSI_CLK_EARLY_GATE) &&
+ (state == DSI_CLK_OFF)) {
+ changed = true;
+ c->link_clk_state = DSI_CLK_OFF;
+ } else {
+ pr_warn("Link refcount is zero for %s",
+ c->name);
+ }
+ } else {
+ c->link_refcount--;
+ if (c->link_refcount == 0) {
+ c->link_clk_state = state;
+ changed = true;
+ }
+ }
+ }
+ }
+ pr_debug("[%s]%s: change=%d, Core (ref=%d, state=%d), Link (ref=%d, state=%d)\n",
+ mngr->name, c->name, changed, c->core_refcount,
+ c->core_clk_state, c->link_refcount, c->link_clk_state);
+
+ if (changed) {
+ rc = dsi_recheck_clk_state(mngr);
+ if (rc)
+ pr_err("Failed to adjust clock state rc = %d\n", rc);
+ }
+
+ mutex_unlock(&mngr->clk_mutex);
+ return rc;
+}
+
+DEFINE_MUTEX(dsi_mngr_clk_mutex);
+
+int dsi_display_clk_ctrl(void *handle,
+ enum dsi_clk_type clk_type, enum dsi_clk_state clk_state)
+{
+ int rc = 0;
+
+ if (!handle) {
+ pr_err("%s: Invalid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&dsi_mngr_clk_mutex);
+ rc = dsi_clk_req_state(handle, clk_type, clk_state);
+ if (rc)
+ pr_err("%s: failed set clk state, rc = %d\n", __func__, rc);
+ mutex_unlock(&dsi_mngr_clk_mutex);
+
+ return rc;
+}
+
+void *dsi_register_clk_handle(void *clk_mngr, char *client)
+{
+ void *handle = NULL;
+ struct dsi_clk_mngr *mngr = clk_mngr;
+ struct dsi_clk_client_info *c;
+
+ if (!mngr) {
+ pr_err("bad params\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ mutex_lock(&mngr->clk_mutex);
+
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
+ if (!c) {
+ handle = ERR_PTR(-ENOMEM);
+ goto error;
+ }
+
+ strlcpy(c->name, client, MAX_STRING_LEN);
+ c->mngr = mngr;
+
+ list_add(&c->list, &mngr->client_list);
+
+ pr_debug("[%s]: Added new client (%s)\n", mngr->name, c->name);
+ handle = c;
+error:
+ mutex_unlock(&mngr->clk_mutex);
+ return handle;
+}
+
+int dsi_deregister_clk_handle(void *client)
+{
+ int rc = 0;
+ struct dsi_clk_client_info *c = client;
+ struct dsi_clk_mngr *mngr;
+ struct list_head *pos = NULL;
+ struct list_head *tmp = NULL;
+ struct dsi_clk_client_info *node = NULL;
+
+ if (!client) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mngr = c->mngr;
+ pr_debug("%s: ENTER\n", mngr->name);
+ mutex_lock(&mngr->clk_mutex);
+ c->core_clk_state = DSI_CLK_OFF;
+ c->link_clk_state = DSI_CLK_OFF;
+
+ rc = dsi_recheck_clk_state(mngr);
+ if (rc) {
+ pr_err("clock state recheck failed rc = %d\n", rc);
+ goto error;
+ }
+
+ list_for_each_safe(pos, tmp, &mngr->client_list) {
+ node = list_entry(pos, struct dsi_clk_client_info,
+ list);
+ if (node == c) {
+ list_del(&node->list);
+ pr_debug("Removed device (%s)\n", node->name);
+ kfree(node);
+ break;
+ }
+ }
+
+error:
+ mutex_unlock(&mngr->clk_mutex);
+ pr_debug("%s: EXIT, rc = %d\n", mngr->name, rc);
+ return rc;
+}
+
+void *dsi_display_clk_mngr_register(struct dsi_clk_info *info)
+{
+ struct dsi_clk_mngr *mngr;
+ int i = 0;
+
+ if (!info) {
+ pr_err("Invalid params\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ mngr = kzalloc(sizeof(*mngr), GFP_KERNEL);
+ if (!mngr) {
+ mngr = ERR_PTR(-ENOMEM);
+ goto error;
+ }
+
+ mutex_init(&mngr->clk_mutex);
+ mngr->dsi_ctrl_count = info->dsi_ctrl_count;
+ mngr->master_ndx = info->master_ndx;
+
+ if (mngr->dsi_ctrl_count > MAX_DSI_CTRL) {
+ kfree(mngr);
+ return ERR_PTR(-EINVAL);
+ }
+
+ for (i = 0; i < mngr->dsi_ctrl_count; i++) {
+ memcpy(&mngr->core_clks[i].clks, &info->c_clks[i],
+ sizeof(struct dsi_core_clk_info));
+ memcpy(&mngr->link_clks[i].clks, &info->l_clks[i],
+ sizeof(struct dsi_link_clk_info));
+ mngr->core_clks[i].bus_handle = info->bus_handle[i];
+ }
+
+ INIT_LIST_HEAD(&mngr->client_list);
+ mngr->pre_clkon_cb = info->pre_clkon_cb;
+ mngr->post_clkon_cb = info->post_clkon_cb;
+ mngr->pre_clkoff_cb = info->pre_clkoff_cb;
+ mngr->post_clkoff_cb = info->post_clkoff_cb;
+ mngr->priv_data = info->priv_data;
+ memcpy(mngr->name, info->name, MAX_STRING_LEN);
+
+error:
+ pr_debug("EXIT, rc = %ld\n", PTR_ERR(mngr));
+ return mngr;
+}
+
+int dsi_display_clk_mngr_deregister(void *clk_mngr)
+{
+ int rc = 0;
+ struct dsi_clk_mngr *mngr = clk_mngr;
+ struct list_head *position = NULL;
+ struct list_head *tmp = NULL;
+ struct dsi_clk_client_info *node = NULL;
+
+ if (!mngr) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ pr_debug("%s: ENTER\n", mngr->name);
+ mutex_lock(&mngr->clk_mutex);
+
+ list_for_each_safe(position, tmp, &mngr->client_list) {
+ node = list_entry(position, struct dsi_clk_client_info,
+ list);
+ list_del(&node->list);
+ pr_debug("Removed device (%s)\n", node->name);
+ kfree(node);
+ }
+
+ rc = dsi_recheck_clk_state(mngr);
+ if (rc)
+ pr_err("failed to disable all clocks\n");
+
+ mutex_unlock(&mngr->clk_mutex);
+ pr_debug("%s: EXIT, rc = %d\n", mngr->name, rc);
+ kfree(mngr);
+ return rc;
+}
+
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.c b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.c
deleted file mode 100644
index 7def847..0000000
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.c
+++ /dev/null
@@ -1,727 +0,0 @@
-/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/of.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-
-#include "dsi_clk_pwr.h"
-
-#define INC_REFCOUNT(s, start_func) \
- ({ \
- int rc = 0; \
- if ((s)->refcount == 0) { \
- rc = start_func(s); \
- if (rc) \
- pr_err("failed to enable, rc = %d\n", rc); \
- } \
- (s)->refcount++; \
- rc; \
- })
-
-#define DEC_REFCOUNT(s, stop_func) \
- ({ \
- int rc = 0; \
- if ((s)->refcount == 0) { \
- pr_err("unbalanced refcount\n"); \
- } else { \
- (s)->refcount--; \
- if ((s)->refcount == 0) { \
- rc = stop_func(s); \
- if (rc) \
- pr_err("disable failed, rc=%d\n", rc); \
- } \
- } \
- rc; \
- })
-
-static int dsi_core_clk_start(struct dsi_core_clk_info *clks)
-{
- int rc = 0;
-
- rc = clk_prepare_enable(clks->mdp_core_clk);
- if (rc) {
- pr_err("failed to enable mdp_core_clk, rc=%d\n", rc);
- goto error;
- }
-
- rc = clk_prepare_enable(clks->iface_clk);
- if (rc) {
- pr_err("failed to enable iface_clk, rc=%d\n", rc);
- goto error_disable_core_clk;
- }
-
- rc = clk_prepare_enable(clks->bus_clk);
- if (rc) {
- pr_err("failed to enable bus_clk, rc=%d\n", rc);
- goto error_disable_iface_clk;
- }
-
- rc = clk_prepare_enable(clks->core_mmss_clk);
- if (rc) {
- pr_err("failed to enable core_mmss_clk, rc=%d\n", rc);
- goto error_disable_bus_clk;
- }
-
- return rc;
-
-error_disable_bus_clk:
- clk_disable_unprepare(clks->bus_clk);
-error_disable_iface_clk:
- clk_disable_unprepare(clks->iface_clk);
-error_disable_core_clk:
- clk_disable_unprepare(clks->mdp_core_clk);
-error:
- return rc;
-}
-
-static int dsi_core_clk_stop(struct dsi_core_clk_info *clks)
-{
- clk_disable_unprepare(clks->core_mmss_clk);
- clk_disable_unprepare(clks->bus_clk);
- clk_disable_unprepare(clks->iface_clk);
- clk_disable_unprepare(clks->mdp_core_clk);
-
- return 0;
-}
-
-static int dsi_link_clk_set_rate(struct dsi_link_clk_info *l_clks)
-{
- int rc = 0;
-
- rc = clk_set_rate(l_clks->esc_clk, l_clks->esc_clk_rate);
- if (rc) {
- pr_err("clk_set_rate failed for esc_clk rc = %d\n", rc);
- goto error;
- }
-
- rc = clk_set_rate(l_clks->byte_clk, l_clks->byte_clk_rate);
- if (rc) {
- pr_err("clk_set_rate failed for byte_clk rc = %d\n", rc);
- goto error;
- }
-
- rc = clk_set_rate(l_clks->pixel_clk, l_clks->pixel_clk_rate);
- if (rc) {
- pr_err("clk_set_rate failed for pixel_clk rc = %d\n", rc);
- goto error;
- }
-error:
- return rc;
-}
-
-static int dsi_link_clk_prepare(struct dsi_link_clk_info *l_clks)
-{
- int rc = 0;
-
- rc = clk_prepare(l_clks->esc_clk);
- if (rc) {
- pr_err("Failed to prepare dsi esc clk, rc=%d\n", rc);
- goto esc_clk_err;
- }
-
- rc = clk_prepare(l_clks->byte_clk);
- if (rc) {
- pr_err("Failed to prepare dsi byte clk, rc=%d\n", rc);
- goto byte_clk_err;
- }
-
- rc = clk_prepare(l_clks->pixel_clk);
- if (rc) {
- pr_err("Failed to prepare dsi pixel clk, rc=%d\n", rc);
- goto pixel_clk_err;
- }
-
- return rc;
-
-pixel_clk_err:
- clk_unprepare(l_clks->byte_clk);
-byte_clk_err:
- clk_unprepare(l_clks->esc_clk);
-esc_clk_err:
- return rc;
-}
-
-static void dsi_link_clk_unprepare(struct dsi_link_clk_info *l_clks)
-{
- clk_unprepare(l_clks->pixel_clk);
- clk_unprepare(l_clks->byte_clk);
- clk_unprepare(l_clks->esc_clk);
-}
-
-static int dsi_link_clk_enable(struct dsi_link_clk_info *l_clks)
-{
- int rc = 0;
-
- rc = clk_enable(l_clks->esc_clk);
- if (rc) {
- pr_err("Failed to enable dsi esc clk, rc=%d\n", rc);
- goto esc_clk_err;
- }
-
- rc = clk_enable(l_clks->byte_clk);
- if (rc) {
- pr_err("Failed to enable dsi byte clk, rc=%d\n", rc);
- goto byte_clk_err;
- }
-
- rc = clk_enable(l_clks->pixel_clk);
- if (rc) {
- pr_err("Failed to enable dsi pixel clk, rc=%d\n", rc);
- goto pixel_clk_err;
- }
-
- return rc;
-
-pixel_clk_err:
- clk_disable(l_clks->byte_clk);
-byte_clk_err:
- clk_disable(l_clks->esc_clk);
-esc_clk_err:
- return rc;
-}
-
-static void dsi_link_clk_disable(struct dsi_link_clk_info *l_clks)
-{
- clk_disable(l_clks->esc_clk);
- clk_disable(l_clks->pixel_clk);
- clk_disable(l_clks->byte_clk);
-}
-
-/**
- * dsi_link_clk_start() - enable dsi link clocks
- */
-static int dsi_link_clk_start(struct dsi_link_clk_info *clks)
-{
- int rc = 0;
-
- if (clks->set_new_rate) {
- rc = dsi_link_clk_set_rate(clks);
- if (rc) {
- pr_err("failed to set clk rates, rc = %d\n", rc);
- goto error;
- } else {
- clks->set_new_rate = false;
- }
- }
-
- rc = dsi_link_clk_prepare(clks);
- if (rc) {
- pr_err("failed to prepare link clks, rc = %d\n", rc);
- goto error;
- }
-
- rc = dsi_link_clk_enable(clks);
- if (rc) {
- pr_err("failed to enable link clks, rc = %d\n", rc);
- goto error_unprepare;
- }
-
- pr_debug("Link clocks are enabled\n");
- return rc;
-error_unprepare:
- dsi_link_clk_unprepare(clks);
-error:
- return rc;
-}
-
-/**
- * dsi_link_clk_stop() - Stop DSI link clocks.
- */
-static int dsi_link_clk_stop(struct dsi_link_clk_info *clks)
-{
- dsi_link_clk_disable(clks);
- dsi_link_clk_unprepare(clks);
-
- pr_debug("Link clocks disabled\n");
-
- return 0;
-}
-
-/*
- * dsi_pwr_parse_supply_node() - parse power supply node from root device node
- */
-static int dsi_pwr_parse_supply_node(struct device_node *root,
- struct dsi_regulator_info *regs)
-{
- int rc = 0;
- int i = 0;
- u32 tmp = 0;
- struct device_node *node = NULL;
-
- for_each_child_of_node(root, node) {
- const char *st = NULL;
-
- rc = of_property_read_string(node, "qcom,supply-name", &st);
- if (rc) {
- pr_err("failed to read name, rc = %d\n", rc);
- goto error;
- }
-
- snprintf(regs->vregs[i].vreg_name,
- ARRAY_SIZE(regs->vregs[i].vreg_name),
- "%s", st);
-
- rc = of_property_read_u32(node, "qcom,supply-min-voltage",
- &tmp);
- if (rc) {
- pr_err("failed to read min voltage, rc = %d\n", rc);
- goto error;
- }
- regs->vregs[i].min_voltage = tmp;
-
- rc = of_property_read_u32(node, "qcom,supply-max-voltage",
- &tmp);
- if (rc) {
- pr_err("failed to read max voltage, rc = %d\n", rc);
- goto error;
- }
- regs->vregs[i].max_voltage = tmp;
-
- rc = of_property_read_u32(node, "qcom,supply-enable-load",
- &tmp);
- if (rc) {
- pr_err("failed to read enable load, rc = %d\n", rc);
- goto error;
- }
- regs->vregs[i].enable_load = tmp;
-
- rc = of_property_read_u32(node, "qcom,supply-disable-load",
- &tmp);
- if (rc) {
- pr_err("failed to read disable load, rc = %d\n", rc);
- goto error;
- }
- regs->vregs[i].disable_load = tmp;
-
- /* Optional values */
- rc = of_property_read_u32(node, "qcom,supply-pre-on-sleep",
- &tmp);
- if (rc) {
- pr_debug("pre-on-sleep not specified\n");
- rc = 0;
- } else {
- regs->vregs[i].pre_on_sleep = tmp;
- }
-
- rc = of_property_read_u32(node, "qcom,supply-pre-off-sleep",
- &tmp);
- if (rc) {
- pr_debug("pre-off-sleep not specified\n");
- rc = 0;
- } else {
- regs->vregs[i].pre_off_sleep = tmp;
- }
-
- rc = of_property_read_u32(node, "qcom,supply-post-on-sleep",
- &tmp);
- if (rc) {
- pr_debug("post-on-sleep not specified\n");
- rc = 0;
- } else {
- regs->vregs[i].post_on_sleep = tmp;
- }
-
- rc = of_property_read_u32(node, "qcom,supply-post-off-sleep",
- &tmp);
- if (rc) {
- pr_debug("post-off-sleep not specified\n");
- rc = 0;
- } else {
- regs->vregs[i].post_off_sleep = tmp;
- }
-
- ++i;
- pr_debug("[%s] minv=%d maxv=%d, en_load=%d, dis_load=%d\n",
- regs->vregs[i].vreg_name,
- regs->vregs[i].min_voltage,
- regs->vregs[i].max_voltage,
- regs->vregs[i].enable_load,
- regs->vregs[i].disable_load);
- }
-
-error:
- return rc;
-}
-
-/**
- * dsi_pwr_enable_vregs() - enable/disable regulators
- */
-static int dsi_pwr_enable_vregs(struct dsi_regulator_info *regs, bool enable)
-{
- int rc = 0, i = 0;
- struct dsi_vreg *vreg;
- int num_of_v = 0;
-
- if (enable) {
- for (i = 0; i < regs->count; i++) {
- vreg = ®s->vregs[i];
- if (vreg->pre_on_sleep)
- msleep(vreg->pre_on_sleep);
-
- rc = regulator_set_load(vreg->vreg,
- vreg->enable_load);
- if (rc < 0) {
- pr_err("Setting optimum mode failed for %s\n",
- vreg->vreg_name);
- goto error;
- }
- num_of_v = regulator_count_voltages(vreg->vreg);
- if (num_of_v > 0) {
- rc = regulator_set_voltage(vreg->vreg,
- vreg->min_voltage,
- vreg->max_voltage);
- if (rc) {
- pr_err("Set voltage(%s) fail, rc=%d\n",
- vreg->vreg_name, rc);
- goto error_disable_opt_mode;
- }
- }
-
- rc = regulator_enable(vreg->vreg);
- if (rc) {
- pr_err("enable failed for %s, rc=%d\n",
- vreg->vreg_name, rc);
- goto error_disable_voltage;
- }
-
- if (vreg->post_on_sleep)
- msleep(vreg->post_on_sleep);
- }
- } else {
- for (i = (regs->count - 1); i >= 0; i--) {
- if (regs->vregs[i].pre_off_sleep)
- msleep(regs->vregs[i].pre_off_sleep);
-
- (void)regulator_set_load(regs->vregs[i].vreg,
- regs->vregs[i].disable_load);
- (void)regulator_disable(regs->vregs[i].vreg);
-
- if (regs->vregs[i].post_off_sleep)
- msleep(regs->vregs[i].post_off_sleep);
- }
- }
-
- return 0;
-error_disable_opt_mode:
- (void)regulator_set_load(regs->vregs[i].vreg,
- regs->vregs[i].disable_load);
-
-error_disable_voltage:
- if (num_of_v > 0)
- (void)regulator_set_voltage(regs->vregs[i].vreg,
- 0, regs->vregs[i].max_voltage);
-error:
- for (i--; i >= 0; i--) {
- if (regs->vregs[i].pre_off_sleep)
- msleep(regs->vregs[i].pre_off_sleep);
-
- (void)regulator_set_load(regs->vregs[i].vreg,
- regs->vregs[i].disable_load);
-
- num_of_v = regulator_count_voltages(regs->vregs[i].vreg);
- if (num_of_v > 0)
- (void)regulator_set_voltage(regs->vregs[i].vreg,
- 0, regs->vregs[i].max_voltage);
-
- (void)regulator_disable(regs->vregs[i].vreg);
-
- if (regs->vregs[i].post_off_sleep)
- msleep(regs->vregs[i].post_off_sleep);
- }
-
- return rc;
-}
-
-/**
-* dsi_clk_pwr_of_get_vreg_data - Parse regulator supply information
-* @of_node: Device of node to parse for supply information.
-* @regs: Pointer where regulator information will be copied to.
-* @supply_name: Name of the supply node.
-*
-* return: error code in case of failure or 0 for success.
-*/
-int dsi_clk_pwr_of_get_vreg_data(struct device_node *of_node,
- struct dsi_regulator_info *regs,
- char *supply_name)
-{
- int rc = 0;
- struct device_node *supply_root_node = NULL;
-
- if (!of_node || !regs) {
- pr_err("Bad params\n");
- return -EINVAL;
- }
-
- regs->count = 0;
- supply_root_node = of_get_child_by_name(of_node, supply_name);
- if (!supply_root_node) {
- supply_root_node = of_parse_phandle(of_node, supply_name, 0);
- if (!supply_root_node) {
- pr_err("No supply entry present for %s\n", supply_name);
- return -EINVAL;
- }
- }
-
- regs->count = of_get_available_child_count(supply_root_node);
- if (regs->count == 0) {
- pr_err("No vregs defined for %s\n", supply_name);
- return -EINVAL;
- }
-
- regs->vregs = kcalloc(regs->count, sizeof(*regs->vregs), GFP_KERNEL);
- if (!regs->vregs) {
- regs->count = 0;
- return -ENOMEM;
- }
-
- rc = dsi_pwr_parse_supply_node(supply_root_node, regs);
- if (rc) {
- pr_err("failed to parse supply node for %s, rc = %d\n",
- supply_name, rc);
-
- kfree(regs->vregs);
- regs->vregs = NULL;
- regs->count = 0;
- }
-
- return rc;
-}
-
-/**
- * dsi_clk_pwr_get_dt_vreg_data - parse regulator supply information
- * @dev: Device whose of_node needs to be parsed.
- * @regs: Pointer where regulator information will be copied to.
- * @supply_name: Name of the supply node.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_pwr_get_dt_vreg_data(struct device *dev,
- struct dsi_regulator_info *regs,
- char *supply_name)
-{
- int rc = 0;
- struct device_node *of_node = NULL;
- struct device_node *supply_node = NULL;
- struct device_node *supply_root_node = NULL;
-
- if (!dev || !regs) {
- pr_err("Bad params\n");
- return -EINVAL;
- }
-
- of_node = dev->of_node;
- regs->count = 0;
- supply_root_node = of_get_child_by_name(of_node, supply_name);
- if (!supply_root_node) {
- supply_root_node = of_parse_phandle(of_node, supply_name, 0);
- if (!supply_root_node) {
- pr_err("No supply entry present for %s\n", supply_name);
- return -EINVAL;
- }
- }
-
- for_each_child_of_node(supply_root_node, supply_node)
- regs->count++;
-
- if (regs->count == 0) {
- pr_err("No vregs defined for %s\n", supply_name);
- return -EINVAL;
- }
-
- regs->vregs = devm_kcalloc(dev, regs->count, sizeof(*regs->vregs),
- GFP_KERNEL);
- if (!regs->vregs) {
- regs->count = 0;
- return -ENOMEM;
- }
-
- rc = dsi_pwr_parse_supply_node(supply_root_node, regs);
- if (rc) {
- pr_err("failed to parse supply node for %s, rc = %d\n",
- supply_name, rc);
- devm_kfree(dev, regs->vregs);
- regs->vregs = NULL;
- regs->count = 0;
- }
-
- return rc;
-}
-
-/**
- * dsi_pwr_enable_regulator() - enable a set of regulators
- * @regs: Pointer to set of regulators to enable or disable.
- * @enable: Enable/Disable regulators.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_pwr_enable_regulator(struct dsi_regulator_info *regs, bool enable)
-{
- int rc = 0;
-
- if (enable) {
- if (regs->refcount == 0) {
- rc = dsi_pwr_enable_vregs(regs, true);
- if (rc)
- pr_err("failed to enable regulators\n");
- }
- regs->refcount++;
- } else {
- if (regs->refcount == 0) {
- pr_err("Unbalanced regulator off\n");
- } else {
- regs->refcount--;
- if (regs->refcount == 0) {
- rc = dsi_pwr_enable_vregs(regs, false);
- if (rc)
- pr_err("failed to disable vregs\n");
- }
- }
- }
-
- return rc;
-}
-
-/**
- * dsi_clk_enable_core_clks() - enable DSI core clocks
- * @clks: DSI core clock information.
- * @enable: enable/disable DSI core clocks.
- *
- * A ref count is maintained, so caller should make sure disable and enable
- * calls are balanced.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_enable_core_clks(struct dsi_core_clk_info *clks, bool enable)
-{
- int rc = 0;
-
- if (enable)
- rc = INC_REFCOUNT(clks, dsi_core_clk_start);
- else
- rc = DEC_REFCOUNT(clks, dsi_core_clk_stop);
-
- return rc;
-}
-
-/**
- * dsi_clk_enable_link_clks() - enable DSI link clocks
- * @clks: DSI link clock information.
- * @enable: enable/disable DSI link clocks.
- *
- * A ref count is maintained, so caller should make sure disable and enable
- * calls are balanced.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_enable_link_clks(struct dsi_link_clk_info *clks, bool enable)
-{
- int rc = 0;
-
- if (enable)
- rc = INC_REFCOUNT(clks, dsi_link_clk_start);
- else
- rc = DEC_REFCOUNT(clks, dsi_link_clk_stop);
-
- return rc;
-}
-
-/**
- * dsi_clk_set_link_frequencies() - set frequencies for link clks
- * @clks: Link clock information
- * @pixel_clk: pixel clock frequency in KHz.
- * @byte_clk: Byte clock frequency in KHz.
- * @esc_clk: Escape clock frequency in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_link_frequencies(struct dsi_link_clk_info *clks,
- u64 pixel_clk,
- u64 byte_clk,
- u64 esc_clk)
-{
- int rc = 0;
-
- clks->pixel_clk_rate = pixel_clk;
- clks->byte_clk_rate = byte_clk;
- clks->esc_clk_rate = esc_clk;
- clks->set_new_rate = true;
-
- return rc;
-}
-
-/**
- * dsi_clk_set_pixel_clk_rate() - set frequency for pixel clock
- * @clks: DSI link clock information.
- * @pixel_clk: Pixel clock rate in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_pixel_clk_rate(struct dsi_link_clk_info *clks, u64 pixel_clk)
-{
- int rc = 0;
-
- rc = clk_set_rate(clks->pixel_clk, pixel_clk);
- if (rc)
- pr_err("failed to set clk rate for pixel clk, rc=%d\n", rc);
- else
- clks->pixel_clk_rate = pixel_clk;
-
- return rc;
-}
-
-/**
- * dsi_clk_set_byte_clk_rate() - set frequency for byte clock
- * @clks: DSI link clock information.
- * @byte_clk: Byte clock rate in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_byte_clk_rate(struct dsi_link_clk_info *clks, u64 byte_clk)
-{
- int rc = 0;
-
- rc = clk_set_rate(clks->byte_clk, byte_clk);
- if (rc)
- pr_err("failed to set clk rate for byte clk, rc=%d\n", rc);
- else
- clks->byte_clk_rate = byte_clk;
-
- return rc;
-}
-
-/**
- * dsi_clk_update_parent() - update parent clocks for specified clock
- * @parent: link clock pair which are set as parent.
- * @child: link clock pair whose parent has to be set.
- */
-int dsi_clk_update_parent(struct dsi_clk_link_set *parent,
- struct dsi_clk_link_set *child)
-{
- int rc = 0;
-
- rc = clk_set_parent(child->byte_clk, parent->byte_clk);
- if (rc) {
- pr_err("failed to set byte clk parent\n");
- goto error;
- }
-
- rc = clk_set_parent(child->pixel_clk, parent->pixel_clk);
- if (rc) {
- pr_err("failed to set pixel clk parent\n");
- goto error;
- }
-error:
- return rc;
-}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.h b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.h
deleted file mode 100644
index 223ca4e..0000000
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.h
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef _DSI_CLK_PWR_H_
-#define _DSI_CLK_PWR_H_
-
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/types.h>
-#include <linux/regulator/consumer.h>
-#include <linux/clk.h>
-
-/**
- * struct dsi_vreg - regulator information for DSI regulators
- * @vreg: Handle to the regulator.
- * @vreg_name: Regulator name.
- * @min_voltage: Minimum voltage in uV.
- * @max_voltage: Maximum voltage in uV.
- * @enable_load: Load, in uA, when enabled.
- * @disable_load: Load, in uA, when disabled.
- * @pre_on_sleep: Sleep, in ms, before enabling the regulator.
- * @post_on_sleep: Sleep, in ms, after enabling the regulator.
- * @pre_off_sleep: Sleep, in ms, before disabling the regulator.
- * @post_off_sleep: Sleep, in ms, after disabling the regulator.
- */
-struct dsi_vreg {
- struct regulator *vreg;
- char vreg_name[32];
- u32 min_voltage;
- u32 max_voltage;
- u32 enable_load;
- u32 disable_load;
- u32 pre_on_sleep;
- u32 post_on_sleep;
- u32 pre_off_sleep;
- u32 post_off_sleep;
-};
-
-/**
- * struct dsi_regulator_info - set of vregs that are turned on/off together.
- * @vregs: Array of dsi_vreg structures.
- * @count: Number of vregs.
- * @refcount: Reference counting for enabling.
- */
-struct dsi_regulator_info {
- struct dsi_vreg *vregs;
- u32 count;
- u32 refcount;
-};
-
-/**
- * struct dsi_core_clk_info - Core clock information for DSI hardware
- * @mdp_core_clk: Handle to MDP core clock.
- * @iface_clk: Handle to MDP interface clock.
- * @core_mmss_clk: Handle to MMSS core clock.
- * @bus_clk: Handle to bus clock.
- * @refcount: Reference count for core clocks.
- * @clk_state: Current clock state.
- */
-struct dsi_core_clk_info {
- struct clk *mdp_core_clk;
- struct clk *iface_clk;
- struct clk *core_mmss_clk;
- struct clk *bus_clk;
-
- u32 refcount;
- u32 clk_state;
-};
-
-/**
- * struct dsi_link_clk_info - Link clock information for DSI hardware.
- * @byte_clk: Handle to DSI byte clock.
- * @byte_clk_rate: Frequency of DSI byte clock in KHz.
- * @pixel_clk: Handle to DSI pixel clock.
- * @pixel_clk_rate: Frequency of DSI pixel clock in KHz.
- * @esc_clk: Handle to DSI escape clock.
- * @esc_clk_rate: Frequency of DSI escape clock in KHz.
- * @refcount: Reference count for link clocks.
- * @clk_state: Current clock state.
- * @set_new_rate: private flag used by clock utility.
- */
-struct dsi_link_clk_info {
- struct clk *byte_clk;
- u64 byte_clk_rate;
-
- struct clk *pixel_clk;
- u64 pixel_clk_rate;
-
- struct clk *esc_clk;
- u64 esc_clk_rate;
-
- u32 refcount;
- u32 clk_state;
- bool set_new_rate;
-};
-
-/**
- * struct dsi_clk_link_set - Pair of clock handles to describe link clocks
- * @byte_clk: Handle to DSi byte clock.
- * @pixel_clk: Handle to DSI pixel clock.
- */
-struct dsi_clk_link_set {
- struct clk *byte_clk;
- struct clk *pixel_clk;
-};
-
-/**
- * dsi_clk_pwr_of_get_vreg_data - parse regulator supply information
- * @of_node: Device of node to parse for supply information.
- * @regs: Pointer where regulator information will be copied to.
- * @supply_name: Name of the supply node.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_pwr_of_get_vreg_data(struct device_node *of_node,
- struct dsi_regulator_info *regs,
- char *supply_name);
-
-/**
- * dsi_clk_pwr_get_dt_vreg_data - parse regulator supply information
- * @dev: Device whose of_node needs to be parsed.
- * @regs: Pointer where regulator information will be copied to.
- * @supply_name: Name of the supply node.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_pwr_get_dt_vreg_data(struct device *dev,
- struct dsi_regulator_info *regs,
- char *supply_name);
-
-/**
- * dsi_pwr_enable_regulator() - enable a set of regulators
- * @regs: Pointer to set of regulators to enable or disable.
- * @enable: Enable/Disable regulators.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_pwr_enable_regulator(struct dsi_regulator_info *regs, bool enable);
-
-/**
- * dsi_clk_enable_core_clks() - enable DSI core clocks
- * @clks: DSI core clock information.
- * @enable: enable/disable DSI core clocks.
- *
- * A ref count is maintained, so caller should make sure disable and enable
- * calls are balanced.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_enable_core_clks(struct dsi_core_clk_info *clks, bool enable);
-
-/**
- * dsi_clk_enable_link_clks() - enable DSI link clocks
- * @clks: DSI link clock information.
- * @enable: enable/disable DSI link clocks.
- *
- * A ref count is maintained, so caller should make sure disable and enable
- * calls are balanced.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_enable_link_clks(struct dsi_link_clk_info *clks, bool enable);
-
-/**
- * dsi_clk_set_link_frequencies() - set frequencies for link clks
- * @clks: Link clock information
- * @pixel_clk: pixel clock frequency in KHz.
- * @byte_clk: Byte clock frequency in KHz.
- * @esc_clk: Escape clock frequency in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_link_frequencies(struct dsi_link_clk_info *clks,
- u64 pixel_clk,
- u64 byte_clk,
- u64 esc_clk);
-
-/**
- * dsi_clk_set_pixel_clk_rate() - set frequency for pixel clock
- * @clks: DSI link clock information.
- * @pixel_clk: Pixel clock rate in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_pixel_clk_rate(struct dsi_link_clk_info *clks, u64 pixel_clk);
-
-/**
- * dsi_clk_set_byte_clk_rate() - set frequency for byte clock
- * @clks: DSI link clock information.
- * @byte_clk: Byte clock rate in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_byte_clk_rate(struct dsi_link_clk_info *clks, u64 byte_clk);
-
-/**
- * dsi_clk_update_parent() - update parent clocks for specified clock
- * @parent: link clock pair which are set as parent.
- * @child: link clock pair whose parent has to be set.
- */
-int dsi_clk_update_parent(struct dsi_clk_link_set *parent,
- struct dsi_clk_link_set *child);
-#endif /* _DSI_CLK_PWR_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
index b8520aa..e9fe5c0 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -27,7 +27,8 @@
#include "msm_gpu.h"
#include "dsi_ctrl.h"
#include "dsi_ctrl_hw.h"
-#include "dsi_clk_pwr.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
#include "dsi_catalog.h"
#define DSI_CTRL_DEFAULT_LABEL "MDSS DSI CTRL"
@@ -44,9 +45,6 @@
DSI_CTRL_OP_VID_ENGINE,
DSI_CTRL_OP_HOST_ENGINE,
DSI_CTRL_OP_CMD_TX,
- DSI_CTRL_OP_ULPS_TOGGLE,
- DSI_CTRL_OP_CLAMP_TOGGLE,
- DSI_CTRL_OP_SET_CLK_SOURCE,
DSI_CTRL_OP_HOST_INIT,
DSI_CTRL_OP_TPG,
DSI_CTRL_OP_PHY_SW_RESET,
@@ -99,16 +97,7 @@
/* Dump current state */
len += snprintf((buf + len), (SZ_4K - len), "Current State:\n");
len += snprintf((buf + len), (SZ_4K - len),
- "\tPOWER_STATUS = %s\n\tCORE_CLOCK = %s\n",
- TO_ON_OFF(dsi_ctrl->current_state.pwr_enabled),
- TO_ON_OFF(dsi_ctrl->current_state.core_clk_enabled));
- len += snprintf((buf + len), (SZ_4K - len),
- "\tLINK_CLOCK = %s\n\tULPS_STATUS = %s\n",
- TO_ON_OFF(dsi_ctrl->current_state.link_clk_enabled),
- TO_ON_OFF(dsi_ctrl->current_state.ulps_enabled));
- len += snprintf((buf + len), (SZ_4K - len),
- "\tCLAMP_STATUS = %s\n\tCTRL_ENGINE = %s\n",
- TO_ON_OFF(dsi_ctrl->current_state.clamp_enabled),
+ "\tCTRL_ENGINE = %s\n",
TO_ON_OFF(dsi_ctrl->current_state.controller_state));
len += snprintf((buf + len), (SZ_4K - len),
"\tVIDEO_ENGINE = %s\n\tCOMMAND_ENGINE = %s\n",
@@ -118,10 +107,10 @@
/* Dump clock information */
len += snprintf((buf + len), (SZ_4K - len), "\nClock Info:\n");
len += snprintf((buf + len), (SZ_4K - len),
- "\tBYTE_CLK = %llu, PIXEL_CLK = %llu, ESC_CLK = %llu\n",
- dsi_ctrl->clk_info.link_clks.byte_clk_rate,
- dsi_ctrl->clk_info.link_clks.pixel_clk_rate,
- dsi_ctrl->clk_info.link_clks.esc_clk_rate);
+ "\tBYTE_CLK = %u, PIXEL_CLK = %u, ESC_CLK = %u\n",
+ dsi_ctrl->clk_freq.byte_clk_rate,
+ dsi_ctrl->clk_freq.pix_clk_rate,
+ dsi_ctrl->clk_freq.esc_clk_rate);
/* TODO: make sure that this does not exceed 4K */
if (copy_to_user(buff, buf, len)) {
@@ -142,6 +131,8 @@
struct dsi_ctrl *dsi_ctrl = file->private_data;
char *buf;
u32 len = 0;
+ struct dsi_clk_ctrl_info clk_info;
+ int rc = 0;
if (!dsi_ctrl)
return -ENODEV;
@@ -153,15 +144,30 @@
if (!buf)
return -ENOMEM;
- if (dsi_ctrl->current_state.core_clk_enabled) {
- len = dsi_ctrl->hw.ops.reg_dump_to_buffer(&dsi_ctrl->hw,
- buf,
- SZ_4K);
- } else {
- len = snprintf((buf + len), (SZ_4K - len),
- "Core clocks are not turned on, cannot read\n");
+ clk_info.client = DSI_CLK_REQ_DSI_CLIENT;
+ clk_info.clk_type = DSI_CORE_CLK;
+ clk_info.clk_state = DSI_CLK_ON;
+
+ rc = dsi_ctrl->clk_cb.dsi_clk_cb(dsi_ctrl->clk_cb.priv, clk_info);
+ if (rc) {
+ pr_err("failed to enable DSI core clocks\n");
+ kfree(buf);
+ return rc;
}
+ if (dsi_ctrl->hw.ops.reg_dump_to_buffer)
+ len = dsi_ctrl->hw.ops.reg_dump_to_buffer(&dsi_ctrl->hw,
+ buf, SZ_4K);
+
+ clk_info.clk_state = DSI_CLK_OFF;
+ rc = dsi_ctrl->clk_cb.dsi_clk_cb(dsi_ctrl->clk_cb.priv, clk_info);
+ if (rc) {
+ pr_err("failed to disable DSI core clocks\n");
+ kfree(buf);
+ return rc;
+ }
+
+
/* TODO: make sure that this does not exceed 4K */
if (copy_to_user(buff, buf, len)) {
kfree(buf);
@@ -247,7 +253,7 @@
pr_debug("[%d] No change in state, pwr_state=%d\n",
dsi_ctrl->index, op_state);
rc = -EINVAL;
- } else if (state->power_state == DSI_CTRL_POWER_LINK_CLK_ON) {
+ } else if (state->power_state == DSI_CTRL_POWER_VREG_ON) {
if ((state->cmd_engine_state == DSI_CTRL_ENGINE_ON) ||
(state->vid_engine_state == DSI_CTRL_ENGINE_ON) ||
(state->controller_state == DSI_CTRL_ENGINE_ON)) {
@@ -266,7 +272,7 @@
pr_debug("[%d] No change in state, cmd_state=%d\n",
dsi_ctrl->index, op_state);
rc = -EINVAL;
- } else if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
+ } else if ((state->power_state != DSI_CTRL_POWER_VREG_ON) ||
(state->controller_state != DSI_CTRL_ENGINE_ON)) {
pr_debug("[%d]State error: op=%d: %d, %d\n",
dsi_ctrl->index,
@@ -281,7 +287,7 @@
pr_debug("[%d] No change in state, cmd_state=%d\n",
dsi_ctrl->index, op_state);
rc = -EINVAL;
- } else if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
+ } else if ((state->power_state != DSI_CTRL_POWER_VREG_ON) ||
(state->controller_state != DSI_CTRL_ENGINE_ON)) {
pr_debug("[%d]State error: op=%d: %d, %d\n",
dsi_ctrl->index,
@@ -296,7 +302,7 @@
pr_debug("[%d] No change in state, ctrl_state=%d\n",
dsi_ctrl->index, op_state);
rc = -EINVAL;
- } else if (state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) {
+ } else if (state->power_state != DSI_CTRL_POWER_VREG_ON) {
pr_debug("[%d]State error (link is off): op=%d:, %d\n",
dsi_ctrl->index,
op_state,
@@ -314,7 +320,7 @@
}
break;
case DSI_CTRL_OP_CMD_TX:
- if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
+ if ((state->power_state != DSI_CTRL_POWER_VREG_ON) ||
(state->host_initialized != true) ||
(state->cmd_engine_state != DSI_CTRL_ENGINE_ON)) {
pr_debug("[%d]State error: op=%d: %d, %d, %d\n",
@@ -331,57 +337,18 @@
pr_debug("[%d] No change in state, host_init=%d\n",
dsi_ctrl->index, op_state);
rc = -EINVAL;
- } else if (state->power_state != DSI_CTRL_POWER_CORE_CLK_ON) {
+ } else if (state->power_state != DSI_CTRL_POWER_VREG_ON) {
pr_debug("[%d]State error: op=%d: %d\n",
dsi_ctrl->index, op, state->power_state);
rc = -EINVAL;
}
break;
- case DSI_CTRL_OP_ULPS_TOGGLE:
- if (state->ulps_enabled == op_state) {
- pr_debug("[%d] No change in state, ulps_enabled=%d\n",
- dsi_ctrl->index, op_state);
- rc = -EINVAL;
- } else if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
- (state->controller_state != DSI_CTRL_ENGINE_ON)) {
- pr_debug("[%d]State error: op=%d: %d, %d\n",
- dsi_ctrl->index,
- op,
- state->power_state,
- state->controller_state);
- rc = -EINVAL;
- }
- break;
- case DSI_CTRL_OP_CLAMP_TOGGLE:
- if (state->clamp_enabled == op_state) {
- pr_debug("[%d] No change in state, clamp_enabled=%d\n",
- dsi_ctrl->index, op_state);
- rc = -EINVAL;
- } else if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
- (state->controller_state != DSI_CTRL_ENGINE_ON)) {
- pr_debug("[%d]State error: op=%d: %d, %d\n",
- dsi_ctrl->index,
- op,
- state->power_state,
- state->controller_state);
- rc = -EINVAL;
- }
- break;
- case DSI_CTRL_OP_SET_CLK_SOURCE:
- if (state->power_state == DSI_CTRL_POWER_LINK_CLK_ON) {
- pr_debug("[%d] State error: op=%d: %d\n",
- dsi_ctrl->index,
- op,
- state->power_state);
- rc = -EINVAL;
- }
- break;
case DSI_CTRL_OP_TPG:
if (state->tpg_enabled == op_state) {
pr_debug("[%d] No change in state, tpg_enabled=%d\n",
dsi_ctrl->index, op_state);
rc = -EINVAL;
- } else if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
+ } else if ((state->power_state != DSI_CTRL_POWER_VREG_ON) ||
(state->controller_state != DSI_CTRL_ENGINE_ON)) {
pr_debug("[%d]State error: op=%d: %d, %d\n",
dsi_ctrl->index,
@@ -392,7 +359,7 @@
}
break;
case DSI_CTRL_OP_PHY_SW_RESET:
- if (state->power_state != DSI_CTRL_POWER_CORE_CLK_ON) {
+ if (state->power_state != DSI_CTRL_POWER_VREG_ON) {
pr_debug("[%d]State error: op=%d: %d\n",
dsi_ctrl->index, op, state->power_state);
rc = -EINVAL;
@@ -422,23 +389,6 @@
switch (op) {
case DSI_CTRL_OP_POWER_STATE_CHANGE:
state->power_state = op_state;
- if (op_state == DSI_CTRL_POWER_OFF) {
- state->pwr_enabled = false;
- state->core_clk_enabled = false;
- state->link_clk_enabled = false;
- } else if (op_state == DSI_CTRL_POWER_VREG_ON) {
- state->pwr_enabled = true;
- state->core_clk_enabled = false;
- state->link_clk_enabled = false;
- } else if (op_state == DSI_CTRL_POWER_CORE_CLK_ON) {
- state->pwr_enabled = true;
- state->core_clk_enabled = true;
- state->link_clk_enabled = false;
- } else if (op_state == DSI_CTRL_POWER_LINK_CLK_ON) {
- state->pwr_enabled = true;
- state->core_clk_enabled = true;
- state->link_clk_enabled = true;
- }
break;
case DSI_CTRL_OP_CMD_ENGINE:
state->cmd_engine_state = op_state;
@@ -449,15 +399,6 @@
case DSI_CTRL_OP_HOST_ENGINE:
state->controller_state = op_state;
break;
- case DSI_CTRL_OP_ULPS_TOGGLE:
- state->ulps_enabled = (op_state == 1) ? true : false;
- break;
- case DSI_CTRL_OP_CLAMP_TOGGLE:
- state->clamp_enabled = (op_state == 1) ? true : false;
- break;
- case DSI_CTRL_OP_SET_CLK_SOURCE:
- state->clk_source_set = (op_state == 1) ? true : false;
- break;
case DSI_CTRL_OP_HOST_INIT:
state->host_initialized = (op_state == 1) ? true : false;
break;
@@ -513,6 +454,8 @@
devm_clk_put(&ctrl->pdev->dev, core->core_mmss_clk);
if (core->bus_clk)
devm_clk_put(&ctrl->pdev->dev, core->bus_clk);
+ if (core->mnoc_clk)
+ devm_clk_put(&ctrl->pdev->dev, core->mnoc_clk);
memset(core, 0x0, sizeof(*core));
@@ -522,6 +465,8 @@
devm_clk_put(&ctrl->pdev->dev, link->pixel_clk);
if (link->esc_clk)
devm_clk_put(&ctrl->pdev->dev, link->esc_clk);
+ if (link->byte_intf_clk)
+ devm_clk_put(&ctrl->pdev->dev, link->byte_intf_clk);
memset(link, 0x0, sizeof(*link));
@@ -571,6 +516,12 @@
goto fail;
}
+ core->mnoc_clk = devm_clk_get(&pdev->dev, "mnoc_clk");
+ if (IS_ERR(core->mnoc_clk)) {
+ core->mnoc_clk = NULL;
+ pr_debug("can't get mnoc clock, rc=%d\n", rc);
+ }
+
link->byte_clk = devm_clk_get(&pdev->dev, "byte_clk");
if (IS_ERR(link->byte_clk)) {
rc = PTR_ERR(link->byte_clk);
@@ -592,6 +543,12 @@
goto fail;
}
+ link->byte_intf_clk = devm_clk_get(&pdev->dev, "byte_intf_clk");
+ if (IS_ERR(link->byte_intf_clk)) {
+ link->byte_intf_clk = NULL;
+ pr_debug("can't find byte intf clk, rc=%d\n", rc);
+ }
+
rcg->byte_clk = devm_clk_get(&pdev->dev, "byte_clk_rcg");
if (IS_ERR(rcg->byte_clk)) {
rc = PTR_ERR(rcg->byte_clk);
@@ -657,7 +614,7 @@
struct dsi_regulator_info *regs;
struct regulator *vreg = NULL;
- rc = dsi_clk_pwr_get_dt_vreg_data(&pdev->dev,
+ rc = dsi_pwr_get_dt_vreg_data(&pdev->dev,
&ctrl->pwr_info.digital,
"qcom,core-supply-entries");
if (rc) {
@@ -665,7 +622,7 @@
goto error;
}
- rc = dsi_clk_pwr_get_dt_vreg_data(&pdev->dev,
+ rc = dsi_pwr_get_dt_vreg_data(&pdev->dev,
&ctrl->pwr_info.host_pwr,
"qcom,ctrl-supply-entries");
if (rc) {
@@ -775,7 +732,7 @@
}
static int dsi_ctrl_update_link_freqs(struct dsi_ctrl *dsi_ctrl,
- struct dsi_host_config *config)
+ struct dsi_host_config *config, void *clk_handle)
{
int rc = 0;
u32 num_of_lanes = 0;
@@ -809,10 +766,12 @@
pr_debug("byte_clk_rate = %llu, pclk_rate = %llu\n",
byte_clk_rate, pclk_rate);
- rc = dsi_clk_set_link_frequencies(&dsi_ctrl->clk_info.link_clks,
- pclk_rate,
- byte_clk_rate,
- config->esc_clk_rate_hz);
+ dsi_ctrl->clk_freq.byte_clk_rate = byte_clk_rate;
+ dsi_ctrl->clk_freq.pix_clk_rate = pclk_rate;
+ dsi_ctrl->clk_freq.esc_clk_rate = config->esc_clk_rate_hz;
+
+ rc = dsi_clk_set_link_frequencies(clk_handle, dsi_ctrl->clk_freq,
+ dsi_ctrl->index);
if (rc)
pr_err("Failed to update link frequencies\n");
@@ -824,11 +783,13 @@
int rc = 0;
if (enable) {
- rc = dsi_pwr_enable_regulator(&dsi_ctrl->pwr_info.host_pwr,
- true);
- if (rc) {
- pr_err("failed to enable host power regs, rc=%d\n", rc);
- goto error;
+ if (!dsi_ctrl->current_state.host_initialized) {
+ rc = dsi_pwr_enable_regulator(
+ &dsi_ctrl->pwr_info.host_pwr, true);
+ if (rc) {
+ pr_err("failed to enable host power regs\n");
+ goto error;
+ }
}
rc = dsi_pwr_enable_regulator(&dsi_ctrl->pwr_info.digital,
@@ -849,50 +810,19 @@
goto error;
}
- rc = dsi_pwr_enable_regulator(&dsi_ctrl->pwr_info.host_pwr,
- false);
- if (rc) {
- pr_err("failed to disable host power regs, rc=%d\n",
- rc);
- goto error;
+ if (!dsi_ctrl->current_state.host_initialized) {
+ rc = dsi_pwr_enable_regulator(
+ &dsi_ctrl->pwr_info.host_pwr, false);
+ if (rc) {
+ pr_err("failed to disable host power regs\n");
+ goto error;
+ }
}
}
error:
return rc;
}
-static int dsi_ctrl_vote_for_bandwidth(struct dsi_ctrl *dsi_ctrl, bool on)
-{
- int rc = 0;
- bool changed = false;
- struct dsi_ctrl_bus_scale_info *axi_bus = &dsi_ctrl->axi_bus_info;
-
- if (on) {
- if (axi_bus->refcount == 0)
- changed = true;
-
- axi_bus->refcount++;
- } else {
- if (axi_bus->refcount != 0) {
- axi_bus->refcount--;
-
- if (axi_bus->refcount == 0)
- changed = true;
- } else {
- pr_err("bus bw votes are not balanced\n");
- }
- }
-
- if (changed) {
- rc = msm_bus_scale_client_update_request(axi_bus->bus_handle,
- on ? 1 : 0);
- if (rc)
- pr_err("bus scale client update failed, rc=%d\n", rc);
- }
-
- return rc;
-}
-
static int dsi_ctrl_copy_and_pad_cmd(struct dsi_ctrl *dsi_ctrl,
const struct mipi_dsi_packet *packet,
u8 **buffer,
@@ -1089,16 +1019,28 @@
static int dsi_enable_ulps(struct dsi_ctrl *dsi_ctrl)
{
int rc = 0;
- u32 lanes;
+ u32 lanes = 0;
u32 ulps_lanes;
if (dsi_ctrl->host_config.panel_mode == DSI_OP_CMD_MODE)
lanes = dsi_ctrl->host_config.common_config.data_lanes;
- lanes |= DSI_CLOCK_LANE;
- dsi_ctrl->hw.ops.ulps_request(&dsi_ctrl->hw, lanes);
+ rc = dsi_ctrl->hw.ops.wait_for_lane_idle(&dsi_ctrl->hw, lanes);
+ if (rc) {
+ pr_err("lanes not entering idle, skip ULPS\n");
+ return rc;
+ }
- ulps_lanes = dsi_ctrl->hw.ops.get_lanes_in_ulps(&dsi_ctrl->hw);
+ if (!dsi_ctrl->hw.ops.ulps_ops.ulps_request ||
+ !dsi_ctrl->hw.ops.ulps_ops.ulps_exit) {
+ pr_debug("DSI controller ULPS ops not present\n");
+ return 0;
+ }
+
+ lanes |= DSI_CLOCK_LANE;
+ dsi_ctrl->hw.ops.ulps_ops.ulps_request(&dsi_ctrl->hw, lanes);
+
+ ulps_lanes = dsi_ctrl->hw.ops.ulps_ops.get_lanes_in_ulps(&dsi_ctrl->hw);
if ((lanes & ulps_lanes) != lanes) {
pr_err("Failed to enter ULPS, request=0x%x, actual=0x%x\n",
@@ -1114,25 +1056,29 @@
int rc = 0;
u32 ulps_lanes, lanes = 0;
+ dsi_ctrl->hw.ops.clear_phy0_ln_err(&dsi_ctrl->hw);
+
+ if (!dsi_ctrl->hw.ops.ulps_ops.ulps_request ||
+ !dsi_ctrl->hw.ops.ulps_ops.ulps_exit) {
+ pr_debug("DSI controller ULPS ops not present\n");
+ return 0;
+ }
+
if (dsi_ctrl->host_config.panel_mode == DSI_OP_CMD_MODE)
lanes = dsi_ctrl->host_config.common_config.data_lanes;
lanes |= DSI_CLOCK_LANE;
- ulps_lanes = dsi_ctrl->hw.ops.get_lanes_in_ulps(&dsi_ctrl->hw);
+
+ ulps_lanes = dsi_ctrl->hw.ops.ulps_ops.get_lanes_in_ulps(&dsi_ctrl->hw);
if ((lanes & ulps_lanes) != lanes)
pr_err("Mismatch between lanes in ULPS\n");
lanes &= ulps_lanes;
- dsi_ctrl->hw.ops.ulps_exit(&dsi_ctrl->hw, lanes);
+ dsi_ctrl->hw.ops.ulps_ops.ulps_exit(&dsi_ctrl->hw, lanes);
- /* 1 ms delay is recommended by specification */
- udelay(1000);
-
- dsi_ctrl->hw.ops.clear_ulps_request(&dsi_ctrl->hw, lanes);
-
- ulps_lanes = dsi_ctrl->hw.ops.get_lanes_in_ulps(&dsi_ctrl->hw);
+ ulps_lanes = dsi_ctrl->hw.ops.ulps_ops.get_lanes_in_ulps(&dsi_ctrl->hw);
if (ulps_lanes & lanes) {
pr_err("Lanes (0x%x) stuck in ULPS\n", ulps_lanes);
rc = -EIO;
@@ -1148,15 +1094,9 @@
struct dsi_ctrl_state_info *state = &dsi_ctrl->current_state;
if (!splash_enabled) {
- state->power_state = DSI_CTRL_POWER_OFF;
+ state->power_state = DSI_CTRL_POWER_VREG_OFF;
state->cmd_engine_state = DSI_CTRL_ENGINE_OFF;
state->vid_engine_state = DSI_CTRL_ENGINE_OFF;
- state->pwr_enabled = false;
- state->core_clk_enabled = false;
- state->link_clk_enabled = false;
- state->ulps_enabled = false;
- state->clamp_enabled = false;
- state->clk_source_set = false;
}
return rc;
@@ -1218,9 +1158,9 @@
return rc;
}
-static int dsi_enable_io_clamp(struct dsi_ctrl *dsi_ctrl, bool enable)
+static int dsi_enable_io_clamp(struct dsi_ctrl *dsi_ctrl,
+ bool enable, bool ulps_enabled)
{
- bool en_ulps = dsi_ctrl->current_state.ulps_enabled;
u32 lanes = 0;
if (dsi_ctrl->host_config.panel_mode == DSI_OP_CMD_MODE)
@@ -1229,9 +1169,11 @@
lanes |= DSI_CLOCK_LANE;
if (enable)
- dsi_ctrl->hw.ops.clamp_enable(&dsi_ctrl->hw, lanes, en_ulps);
+ dsi_ctrl->hw.ops.clamp_enable(&dsi_ctrl->hw,
+ lanes, ulps_enabled);
else
- dsi_ctrl->hw.ops.clamp_disable(&dsi_ctrl->hw, lanes, en_ulps);
+ dsi_ctrl->hw.ops.clamp_disable(&dsi_ctrl->hw,
+ lanes, ulps_enabled);
return 0;
}
@@ -1508,6 +1450,19 @@
return rc;
}
+int dsi_ctrl_clk_cb_register(struct dsi_ctrl *dsi_ctrl,
+ struct clk_ctrl_cb *clk_cb)
+{
+ if (!dsi_ctrl || !clk_cb) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ dsi_ctrl->clk_cb.priv = clk_cb->priv;
+ dsi_ctrl->clk_cb.dsi_clk_cb = clk_cb->dsi_clk_cb;
+ return 0;
+}
+
/**
* dsi_ctrl_phy_sw_reset() - perform a PHY software reset
* @dsi_ctrl: DSI controller handle.
@@ -1588,6 +1543,83 @@
}
/**
+ * dsi_ctrl_setup() - Setup DSI host hardware while coming out of idle screen.
+ * @dsi_ctrl: DSI controller handle.
+ *
+ * Initializes DSI controller hardware with host configuration provided by
+ * dsi_ctrl_update_host_config(). Initialization can be performed only during
+ * DSI_CTRL_POWER_CORE_CLK_ON state and after the PHY SW reset has been
+ * performed.
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl)
+{
+ int rc = 0;
+
+ if (!dsi_ctrl) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&dsi_ctrl->ctrl_lock);
+
+ dsi_ctrl->hw.ops.setup_lane_map(&dsi_ctrl->hw,
+ &dsi_ctrl->host_config.lane_map);
+
+ dsi_ctrl->hw.ops.host_setup(&dsi_ctrl->hw,
+ &dsi_ctrl->host_config.common_config);
+
+ if (dsi_ctrl->host_config.panel_mode == DSI_OP_CMD_MODE) {
+ dsi_ctrl->hw.ops.cmd_engine_setup(&dsi_ctrl->hw,
+ &dsi_ctrl->host_config.common_config,
+ &dsi_ctrl->host_config.u.cmd_engine);
+
+ dsi_ctrl->hw.ops.setup_cmd_stream(&dsi_ctrl->hw,
+ dsi_ctrl->host_config.video_timing.h_active,
+ dsi_ctrl->host_config.video_timing.h_active * 3,
+ dsi_ctrl->host_config.video_timing.v_active,
+ 0x0);
+ dsi_ctrl->hw.ops.cmd_engine_en(&dsi_ctrl->hw, true);
+ } else {
+ dsi_ctrl->hw.ops.video_engine_setup(&dsi_ctrl->hw,
+ &dsi_ctrl->host_config.common_config,
+ &dsi_ctrl->host_config.u.video_engine);
+ dsi_ctrl->hw.ops.set_video_timing(&dsi_ctrl->hw,
+ &dsi_ctrl->host_config.video_timing);
+ dsi_ctrl->hw.ops.video_engine_en(&dsi_ctrl->hw, true);
+ }
+
+ dsi_ctrl->hw.ops.enable_status_interrupts(&dsi_ctrl->hw, 0x0);
+ dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw, 0x0);
+ dsi_ctrl->hw.ops.ctrl_en(&dsi_ctrl->hw, true);
+
+ mutex_unlock(&dsi_ctrl->ctrl_lock);
+ return rc;
+}
+
+/**
+ * dsi_ctrl_phy_reset_config() - Mask/unmask propagation of ahb reset signal
+ * to DSI PHY hardware.
+ * @dsi_ctrl: DSI controller handle.
+ * @enable: Mask/unmask the PHY reset signal.
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_phy_reset_config(struct dsi_ctrl *dsi_ctrl, bool enable)
+{
+ if (!dsi_ctrl) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ if (dsi_ctrl->hw.ops.phy_reset_config)
+ dsi_ctrl->hw.ops.phy_reset_config(&dsi_ctrl->hw, enable);
+
+ return 0;
+}
+
+/**
* dsi_ctrl_host_init() - Initialize DSI host hardware.
* @dsi_ctrl: DSI controller handle.
*
@@ -1653,6 +1685,19 @@
return rc;
}
+int dsi_ctrl_soft_reset(struct dsi_ctrl *dsi_ctrl)
+{
+ if (!dsi_ctrl)
+ return -EINVAL;
+
+ mutex_lock(&dsi_ctrl->ctrl_lock);
+ dsi_ctrl->hw.ops.soft_reset(&dsi_ctrl->hw);
+ mutex_unlock(&dsi_ctrl->ctrl_lock);
+
+ pr_debug("[DSI_%d]Soft reset complete\n", dsi_ctrl->index);
+ return 0;
+}
+
/**
* dsi_ctrl_host_deinit() - De-Initialize DSI host hardware.
* @dsi_ctrl: DSI controller handle.
@@ -1702,7 +1747,7 @@
*/
int dsi_ctrl_update_host_config(struct dsi_ctrl *ctrl,
struct dsi_host_config *config,
- int flags)
+ int flags, void *clk_handle)
{
int rc = 0;
@@ -1720,7 +1765,7 @@
}
if (!(flags & DSI_MODE_FLAG_SEAMLESS)) {
- rc = dsi_ctrl_update_link_freqs(ctrl, config);
+ rc = dsi_ctrl_update_link_freqs(ctrl, config, clk_handle);
if (rc) {
pr_err("[%s] failed to update link frequencies, rc=%d\n",
ctrl->name, rc);
@@ -1795,12 +1840,6 @@
goto error;
}
- rc = dsi_ctrl_vote_for_bandwidth(dsi_ctrl, true);
- if (rc) {
- pr_err("bandwidth request failed, rc=%d\n", rc);
- goto error;
- }
-
if (flags & DSI_CTRL_CMD_READ) {
rc = dsi_message_rx(dsi_ctrl, msg, flags);
if (rc)
@@ -1813,7 +1852,6 @@
dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_CMD_TX, 0x0);
- (void)dsi_ctrl_vote_for_bandwidth(dsi_ctrl, false);
error:
mutex_unlock(&dsi_ctrl->ctrl_lock);
return rc;
@@ -1880,10 +1918,6 @@
enum dsi_power_state state)
{
int rc = 0;
- bool core_clk_enable = false;
- bool link_clk_enable = false;
- bool reg_enable = false;
- struct dsi_ctrl_state_info *drv_state;
if (!dsi_ctrl || (state >= DSI_CTRL_POWER_MAX)) {
pr_err("Invalid Params\n");
@@ -1900,57 +1934,14 @@
goto error;
}
- if (state == DSI_CTRL_POWER_LINK_CLK_ON)
- reg_enable = core_clk_enable = link_clk_enable = true;
- else if (state == DSI_CTRL_POWER_CORE_CLK_ON)
- reg_enable = core_clk_enable = true;
- else if (state == DSI_CTRL_POWER_VREG_ON)
- reg_enable = true;
-
- drv_state = &dsi_ctrl->current_state;
-
- if ((reg_enable) && (reg_enable != drv_state->pwr_enabled)) {
+ if (state == DSI_CTRL_POWER_VREG_ON) {
rc = dsi_ctrl_enable_supplies(dsi_ctrl, true);
if (rc) {
pr_err("[%d]failed to enable voltage supplies, rc=%d\n",
dsi_ctrl->index, rc);
goto error;
}
- }
-
- if ((core_clk_enable) &&
- (core_clk_enable != drv_state->core_clk_enabled)) {
- rc = dsi_clk_enable_core_clks(&dsi_ctrl->clk_info.core_clks,
- true);
- if (rc) {
- pr_err("[%d] failed to enable core clocks, rc=%d\n",
- dsi_ctrl->index, rc);
- goto error;
- }
- }
-
- if (link_clk_enable != drv_state->link_clk_enabled) {
- rc = dsi_clk_enable_link_clks(&dsi_ctrl->clk_info.link_clks,
- link_clk_enable);
- if (rc) {
- pr_err("[%d] failed to enable link clocks, rc=%d\n",
- dsi_ctrl->index, rc);
- goto error;
- }
- }
-
- if ((!core_clk_enable) &&
- (core_clk_enable != drv_state->core_clk_enabled)) {
- rc = dsi_clk_enable_core_clks(&dsi_ctrl->clk_info.core_clks,
- false);
- if (rc) {
- pr_err("[%d] failed to disable core clocks, rc=%d\n",
- dsi_ctrl->index, rc);
- goto error;
- }
- }
-
- if ((!reg_enable) && (reg_enable != drv_state->pwr_enabled)) {
+ } else if (state == DSI_CTRL_POWER_VREG_OFF) {
rc = dsi_ctrl_enable_supplies(dsi_ctrl, false);
if (rc) {
pr_err("[%d]failed to disable vreg supplies, rc=%d\n",
@@ -2147,14 +2138,14 @@
}
/**
- * dsi_ctrl_set_ulps() - set ULPS state for DSI lanes.
- * @dsi_ctrl: DSI controller handle.
- * @enable: enable/disable ULPS.
- *
- * ULPS can be enabled/disabled after DSI host engine is turned on.
- *
- * Return: error code.
- */
+ * dsi_ctrl_set_ulps() - set ULPS state for DSI lanes.
+ * @dsi_ctrl: DSI controller handle.
+ * @enable: enable/disable ULPS.
+ *
+ * ULPS can be enabled/disabled after DSI host engine is turned on.
+ *
+ * Return: error code.
+ */
int dsi_ctrl_set_ulps(struct dsi_ctrl *dsi_ctrl, bool enable)
{
int rc = 0;
@@ -2166,13 +2157,6 @@
mutex_lock(&dsi_ctrl->ctrl_lock);
- rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_ULPS_TOGGLE, enable);
- if (rc) {
- pr_err("[DSI_%d] Controller state check failed, rc=%d\n",
- dsi_ctrl->index, rc);
- goto error;
- }
-
if (enable)
rc = dsi_enable_ulps(dsi_ctrl);
else
@@ -2180,12 +2164,11 @@
if (rc) {
pr_err("[DSI_%d] Ulps state change(%d) failed, rc=%d\n",
- dsi_ctrl->index, enable, rc);
+ dsi_ctrl->index, enable, rc);
goto error;
}
-
pr_debug("[DSI_%d] ULPS state = %d\n", dsi_ctrl->index, enable);
- dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_ULPS_TOGGLE, enable);
+
error:
mutex_unlock(&dsi_ctrl->ctrl_lock);
return rc;
@@ -2200,7 +2183,8 @@
*
* Return: error code.
*/
-int dsi_ctrl_set_clamp_state(struct dsi_ctrl *dsi_ctrl, bool enable)
+int dsi_ctrl_set_clamp_state(struct dsi_ctrl *dsi_ctrl,
+ bool enable, bool ulps_enabled)
{
int rc = 0;
@@ -2209,23 +2193,21 @@
return -EINVAL;
}
- mutex_lock(&dsi_ctrl->ctrl_lock);
-
- rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_CLAMP_TOGGLE, enable);
- if (rc) {
- pr_err("[DSI_%d] Controller state check failed, rc=%d\n",
- dsi_ctrl->index, rc);
- goto error;
+ if (!dsi_ctrl->hw.ops.clamp_enable ||
+ !dsi_ctrl->hw.ops.clamp_disable) {
+ pr_debug("No clamp control for DSI controller\n");
+ return 0;
}
- rc = dsi_enable_io_clamp(dsi_ctrl, enable);
+ mutex_lock(&dsi_ctrl->ctrl_lock);
+
+ rc = dsi_enable_io_clamp(dsi_ctrl, enable, ulps_enabled);
if (rc) {
pr_err("[DSI_%d] Failed to enable IO clamp\n", dsi_ctrl->index);
goto error;
}
pr_debug("[DSI_%d] Clamp state = %d\n", dsi_ctrl->index, enable);
- dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_CLAMP_TOGGLE, enable);
error:
mutex_unlock(&dsi_ctrl->ctrl_lock);
return rc;
@@ -2244,7 +2226,6 @@
struct dsi_clk_link_set *source_clks)
{
int rc = 0;
- u32 op_state = 0;
if (!dsi_ctrl || !source_clks) {
pr_err("Invalid params\n");
@@ -2253,17 +2234,6 @@
mutex_lock(&dsi_ctrl->ctrl_lock);
- if (source_clks->pixel_clk && source_clks->byte_clk)
- op_state = 1;
-
- rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_SET_CLK_SOURCE,
- op_state);
- if (rc) {
- pr_err("[DSI_%d] Controller state check failed, rc=%d\n",
- dsi_ctrl->index, rc);
- goto error;
- }
-
rc = dsi_clk_update_parent(source_clks, &dsi_ctrl->clk_info.rcg_clks);
if (rc) {
pr_err("[DSI_%d]Failed to update link clk parent, rc=%d\n",
@@ -2278,8 +2248,6 @@
pr_debug("[DSI_%d] Source clocks are updated\n", dsi_ctrl->index);
- dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_SET_CLK_SOURCE, op_state);
-
error:
mutex_unlock(&dsi_ctrl->ctrl_lock);
return rc;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
index 993a35c..fc95a9a 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,7 +19,8 @@
#include "dsi_defs.h"
#include "dsi_ctrl_hw.h"
-#include "dsi_clk_pwr.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
#include "drm_mipi_dsi.h"
/*
@@ -41,18 +42,14 @@
/**
* enum dsi_power_state - defines power states for dsi controller.
- * @DSI_CTRL_POWER_OFF: DSI controller is powered down.
+ * @DSI_CTRL_POWER_VREG_OFF: Digital and analog supplies for DSI controller
+ turned off
* @DSI_CTRL_POWER_VREG_ON: Digital and analog supplies for DSI controller
- * are powered on.
- * @DSI_CTRL_POWER_CORE_CLK_ON: DSI core clocks for register access are enabled.
- * @DSI_CTRL_POWER_LINK_CLK_ON: DSI link clocks for link transfer are enabled.
* @DSI_CTRL_POWER_MAX: Maximum value.
*/
enum dsi_power_state {
- DSI_CTRL_POWER_OFF = 0,
+ DSI_CTRL_POWER_VREG_OFF = 0,
DSI_CTRL_POWER_VREG_ON,
- DSI_CTRL_POWER_CORE_CLK_ON,
- DSI_CTRL_POWER_LINK_CLK_ON,
DSI_CTRL_POWER_MAX,
};
@@ -112,38 +109,26 @@
* struct dsi_ctrl_bus_scale_info - Bus scale info for msm-bus bandwidth voting
* @bus_scale_table: Bus scale voting usecases.
* @bus_handle: Handle used for voting bandwidth.
- * @refcount: reference count.
*/
struct dsi_ctrl_bus_scale_info {
struct msm_bus_scale_pdata *bus_scale_table;
u32 bus_handle;
- u32 refcount;
};
/**
* struct dsi_ctrl_state_info - current driver state information
- * @power_state: Controller power state.
+ * @power_state: Status of power states on DSI controller.
* @cmd_engine_state: Status of DSI command engine.
* @vid_engine_state: Status of DSI video engine.
* @controller_state: Status of DSI Controller engine.
- * @pwr_enabled: Set to true, if voltage supplies are enabled.
- * @core_clk_enabled: Set to true, if core clocks are enabled.
- * @lin_clk_enabled: Set to true, if link clocks are enabled.
- * @ulps_enabled: Set to true, if lanes are in ULPS state.
- * @clamp_enabled: Set to true, if PHY output is clamped.
- * @clk_source_set: Set to true, if parent is set for DSI link clocks.
+ * @host_initialized: Boolean to indicate status of DSi host Initialization
+ * @tpg_enabled: Boolean to indicate whether tpg is enabled.
*/
struct dsi_ctrl_state_info {
enum dsi_power_state power_state;
enum dsi_engine_state cmd_engine_state;
enum dsi_engine_state vid_engine_state;
enum dsi_engine_state controller_state;
- bool pwr_enabled;
- bool core_clk_enabled;
- bool link_clk_enabled;
- bool ulps_enabled;
- bool clamp_enabled;
- bool clk_source_set;
bool host_initialized;
bool tpg_enabled;
};
@@ -189,9 +174,11 @@
* @drm_dev: Pointer to DRM device.
* @version: DSI controller version.
* @hw: DSI controller hardware object.
- * @current_state; Current driver and hardware state.
+ * @current_state: Current driver and hardware state.
+ * @clk_cb: Callback for DSI clock control.
* @int_info: Interrupt information.
* @clk_info: Clock information.
+ * @clk_freq: DSi Link clock frequency information.
* @pwr_info: Power information.
* @axi_bus_info: AXI bus information.
* @host_config: Current host configuration.
@@ -212,10 +199,12 @@
/* Current state */
struct dsi_ctrl_state_info current_state;
+ struct clk_ctrl_cb clk_cb;
struct dsi_ctrl_interrupts int_info;
/* Clock and power states */
struct dsi_ctrl_clk_info clk_info;
+ struct link_clk_freq clk_freq;
struct dsi_ctrl_power_info pwr_info;
struct dsi_ctrl_bus_scale_info axi_bus_info;
@@ -290,6 +279,7 @@
* @dsi_ctrl: DSI controller handle.
* @config: DSI host configuration.
* @flags: dsi_mode_flags modifying the behavior
+ * @clk_handle: Clock handle for DSI clocks
*
* Updates driver with new Host configuration to use for host initialization.
* This function call will only update the software context. The stored
@@ -299,7 +289,7 @@
*/
int dsi_ctrl_update_host_config(struct dsi_ctrl *dsi_ctrl,
struct dsi_host_config *config,
- int flags);
+ int flags, void *clk_handle);
/**
* dsi_ctrl_async_timing_update() - update only controller timing
@@ -329,6 +319,31 @@
int dsi_ctrl_phy_sw_reset(struct dsi_ctrl *dsi_ctrl);
/**
+ * dsi_ctrl_phy_reset_config() - Mask/unmask propagation of ahb reset signal
+ * to DSI PHY hardware.
+ * @dsi_ctrl: DSI controller handle.
+ * @enable: Mask/unmask the PHY reset signal.
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_phy_reset_config(struct dsi_ctrl *dsi_ctrl, bool enable);
+
+/**
+ * dsi_ctrl_soft_reset() - perform a soft reset on DSI controller
+ * @dsi_ctrl: DSI controller handle.
+ *
+ * The video, command and controller engines will be disabled before the
+ * reset is triggered. After, the engines will be re-enabled to the same state
+ * as before the reset.
+ *
+ * If the reset is done while MDP timing engine is turned on, the video
+ * engine should be re-enabled only during the vertical blanking time.
+ *
+ * Return: error code
+ */
+int dsi_ctrl_soft_reset(struct dsi_ctrl *dsi_ctrl);
+
+/**
* dsi_ctrl_host_init() - Initialize DSI host hardware.
* @dsi_ctrl: DSI controller handle.
*
@@ -353,6 +368,30 @@
int dsi_ctrl_host_deinit(struct dsi_ctrl *dsi_ctrl);
/**
+ * dsi_ctrl_set_ulps() - set ULPS state for DSI lanes.
+ * @dsi_ctrl: DSI controller handle.
+ * @enable: enable/disable ULPS.
+ *
+ * ULPS can be enabled/disabled after DSI host engine is turned on.
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_set_ulps(struct dsi_ctrl *dsi_ctrl, bool enable);
+
+/**
+ * dsi_ctrl_setup() - Setup DSI host hardware while coming out of idle screen.
+ * @dsi_ctrl: DSI controller handle.
+ *
+ * Initializes DSI controller hardware with host configuration provided by
+ * dsi_ctrl_update_host_config(). Initialization can be performed only during
+ * DSI_CTRL_POWER_CORE_CLK_ON state and after the PHY SW reset has been
+ * performed.
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl);
+
+/**
* dsi_ctrl_set_tpg_state() - enable/disable test pattern on the controller
* @dsi_ctrl: DSI controller handle.
* @on: enable/disable test pattern.
@@ -362,6 +401,7 @@
*
* Return: error code.
*/
+
int dsi_ctrl_set_tpg_state(struct dsi_ctrl *dsi_ctrl, bool on);
/**
@@ -454,15 +494,29 @@
int dsi_ctrl_set_ulps(struct dsi_ctrl *dsi_ctrl, bool enable);
/**
+ * dsi_ctrl_clk_cb_register() - Register DSI controller clk control callback
+ * @dsi_ctrl: DSI controller handle.
+ * @clk__cb: Structure containing callback for clock control.
+ *
+ * Register call for DSI clock control
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_clk_cb_register(struct dsi_ctrl *dsi_ctrl,
+ struct clk_ctrl_cb *clk_cb);
+
+/**
* dsi_ctrl_set_clamp_state() - set clamp state for DSI phy
* @dsi_ctrl: DSI controller handle.
* @enable: enable/disable clamping.
+ * @ulps_enabled: ulps state.
*
* Clamps can be enabled/disabled while DSI contoller is still turned on.
*
* Return: error code.
*/
-int dsi_ctrl_set_clamp_state(struct dsi_ctrl *dsi_Ctrl, bool enable);
+int dsi_ctrl_set_clamp_state(struct dsi_ctrl *dsi_Ctrl,
+ bool enable, bool ulps_enabled);
/**
* dsi_ctrl_set_clock_source() - set clock source fpr dsi link clocks
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
index b81cdaf..89c5cda 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -215,6 +215,41 @@
struct dsi_ctrl_hw;
+struct ctrl_ulps_config_ops {
+ /**
+ * ulps_request() - request ulps entry for specified lanes
+ * @ctrl: Pointer to the controller host hardware.
+ * @lanes: ORed list of lanes (enum dsi_data_lanes) which need
+ * to enter ULPS.
+ *
+ * Caller should check if lanes are in ULPS mode by calling
+ * get_lanes_in_ulps() operation.
+ */
+ void (*ulps_request)(struct dsi_ctrl_hw *ctrl, u32 lanes);
+
+ /**
+ * ulps_exit() - exit ULPS on specified lanes
+ * @ctrl: Pointer to the controller host hardware.
+ * @lanes: ORed list of lanes (enum dsi_data_lanes) which need
+ * to exit ULPS.
+ *
+ * Caller should check if lanes are in active mode by calling
+ * get_lanes_in_ulps() operation.
+ */
+ void (*ulps_exit)(struct dsi_ctrl_hw *ctrl, u32 lanes);
+
+ /**
+ * get_lanes_in_ulps() - returns the list of lanes in ULPS mode
+ * @ctrl: Pointer to the controller host hardware.
+ *
+ * Returns an ORed list of lanes (enum dsi_data_lanes) that are in ULPS
+ * state. If 0 is returned, all the lanes are active.
+ *
+ * Return: List of lanes in ULPS state.
+ */
+ u32 (*get_lanes_in_ulps)(struct dsi_ctrl_hw *ctrl);
+};
+
/**
* struct dsi_ctrl_hw_ops - operations supported by dsi host hardware
*/
@@ -311,12 +346,12 @@
* soft_reset() - perform a soft reset on DSI controller
* @ctrl: Pointer to the controller host hardware.
*
- * The video, command and controller engines will be disable before the
- * reset is triggered. These engines will not be enabled after the reset
- * is complete. Caller must re-enable the engines.
+ * The video, command and controller engines will be disabled before the
+ * reset is triggered. After, the engines will be re-enabled to the same
+ * state as before the reset.
*
* If the reset is done while MDP timing engine is turned on, the video
- * enigne should be re-enabled only during the vertical blanking time.
+ * engine should be re-enabled only during the vertical blanking time.
*/
void (*soft_reset)(struct dsi_ctrl_hw *ctrl);
@@ -327,7 +362,7 @@
* lanes and physical lanes.
*/
void (*setup_lane_map)(struct dsi_ctrl_hw *ctrl,
- struct dsi_lane_mapping *lane_map);
+ struct dsi_lane_map *lane_map);
/**
* kickoff_command() - transmits commands stored in memory
@@ -384,52 +419,27 @@
u32 total_read_len);
/**
- * ulps_request() - request ulps entry for specified lanes
+ * wait_for_lane_idle() - wait for DSI lanes to go to idle state
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes (enum dsi_data_lanes) which need
- * to enter ULPS.
- *
- * Caller should check if lanes are in ULPS mode by calling
- * get_lanes_in_ulps() operation.
+ * to be checked to be in idle state.
*/
- void (*ulps_request)(struct dsi_ctrl_hw *ctrl, u32 lanes);
+ int (*wait_for_lane_idle)(struct dsi_ctrl_hw *ctrl, u32 lanes);
+
+ struct ctrl_ulps_config_ops ulps_ops;
/**
- * ulps_exit() - exit ULPS on specified lanes
- * @ctrl: Pointer to the controller host hardware.
- * @lanes: ORed list of lanes (enum dsi_data_lanes) which need
- * to exit ULPS.
- *
- * Caller should check if lanes are in active mode by calling
- * get_lanes_in_ulps() operation.
+ * clamp_enable() - enable DSI clamps
+ * @ctrl: Pointer to the controller host hardware.
+ * @lanes: ORed list of lanes which need to have clamps released.
+ * @enable_ulps: ulps state.
*/
- void (*ulps_exit)(struct dsi_ctrl_hw *ctrl, u32 lanes);
-
- /**
- * clear_ulps_request() - clear ulps request once all lanes are active
- * @ctrl: Pointer to controller host hardware.
- * @lanes: ORed list of lanes (enum dsi_data_lanes).
- *
- * ULPS request should be cleared after the lanes have exited ULPS.
- */
- void (*clear_ulps_request)(struct dsi_ctrl_hw *ctrl, u32 lanes);
-
- /**
- * get_lanes_in_ulps() - returns the list of lanes in ULPS mode
- * @ctrl: Pointer to the controller host hardware.
- *
- * Returns an ORed list of lanes (enum dsi_data_lanes) that are in ULPS
- * state. If 0 is returned, all the lanes are active.
- *
- * Return: List of lanes in ULPS state.
- */
- u32 (*get_lanes_in_ulps)(struct dsi_ctrl_hw *ctrl);
/**
* clamp_enable() - enable DSI clamps to keep PHY driving a stable link
- * @ctrl: Pointer to the controller host hardware.
- * @lanes: ORed list of lanes which need to be clamped.
- * @enable_ulps: TODO:??
+ * @ctrl: Pointer to the controller host hardware.
+ * @lanes: ORed list of lanes which need to have clamps released.
+ * @enable_ulps: TODO:??
*/
void (*clamp_enable)(struct dsi_ctrl_hw *ctrl,
u32 lanes,
@@ -439,13 +449,22 @@
* clamp_disable() - disable DSI clamps
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes which need to have clamps released.
- * @disable_ulps: TODO:??
+ * @disable_ulps: ulps state.
*/
void (*clamp_disable)(struct dsi_ctrl_hw *ctrl,
u32 lanes,
bool disable_ulps);
/**
+ * phy_reset_config() - Disable/enable propagation of reset signal
+ * from ahb domain to DSI PHY
+ * @ctrl: Pointer to the controller host hardware.
+ * @enable: True to mask the reset signal, false to unmask
+ */
+ void (*phy_reset_config)(struct dsi_ctrl_hw *ctrl,
+ bool enable);
+
+ /**
* get_interrupt_status() - returns the interrupt status
* @ctrl: Pointer to the controller host hardware.
*
@@ -537,6 +556,12 @@
void (*test_pattern_enable)(struct dsi_ctrl_hw *ctrl, bool enable);
/**
+ * clear_phy0_ln_err() - clear DSI PHY lane-0 errors
+ * @ctrl: Pointer to the controller host hardware.
+ */
+ void (*clear_phy0_ln_err)(struct dsi_ctrl_hw *ctrl);
+
+ /**
* trigger_cmd_test_pattern() - trigger a command mode frame update with
* test pattern
* @ctrl: Pointer to the controller host hardware.
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c
index ca04eed..37473b8 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -14,583 +14,91 @@
#define pr_fmt(fmt) "dsi-hw:" fmt
#include <linux/delay.h>
+#include <linux/iopoll.h>
#include "dsi_ctrl_hw.h"
-#include "dsi_ctrl_reg_1_4.h"
+#include "dsi_ctrl_reg.h"
#include "dsi_hw.h"
#define MMSS_MISC_CLAMP_REG_OFF 0x0014
-/* Unsupported formats default to RGB888 */
-static const u8 cmd_mode_format_map[DSI_PIXEL_FORMAT_MAX] = {
- 0x6, 0x7, 0x8, 0x8, 0x0, 0x3, 0x4 };
-static const u8 video_mode_format_map[DSI_PIXEL_FORMAT_MAX] = {
- 0x0, 0x1, 0x2, 0x3, 0x3, 0x3, 0x3 };
-
-
/**
- * dsi_setup_trigger_controls() - setup dsi trigger configurations
- * @ctrl: Pointer to the controller host hardware.
- * @cfg: DSI host configuration that is common to both video and
- * command modes.
- */
-static void dsi_setup_trigger_controls(struct dsi_ctrl_hw *ctrl,
- struct dsi_host_common_cfg *cfg)
-{
- u32 reg = 0;
- const u8 trigger_map[DSI_TRIGGER_MAX] = {
- 0x0, 0x2, 0x1, 0x4, 0x5, 0x6 };
-
- reg |= (cfg->te_mode == DSI_TE_ON_EXT_PIN) ? BIT(31) : 0;
- reg |= (trigger_map[cfg->dma_cmd_trigger] & 0x7);
- reg |= (trigger_map[cfg->mdp_cmd_trigger] & 0x7) << 4;
- DSI_W32(ctrl, DSI_TRIG_CTRL, reg);
-}
-
-/**
- * dsi_ctrl_hw_14_host_setup() - setup dsi host configuration
- * @ctrl: Pointer to the controller host hardware.
- * @cfg: DSI host configuration that is common to both video and
- * command modes.
- */
-void dsi_ctrl_hw_14_host_setup(struct dsi_ctrl_hw *ctrl,
- struct dsi_host_common_cfg *cfg)
-{
- u32 reg_value = 0;
-
- dsi_setup_trigger_controls(ctrl, cfg);
-
- /* Setup clocking timing controls */
- reg_value = ((cfg->t_clk_post & 0x3F) << 8);
- reg_value |= (cfg->t_clk_pre & 0x3F);
- DSI_W32(ctrl, DSI_CLKOUT_TIMING_CTRL, reg_value);
-
- /* EOT packet control */
- reg_value = cfg->append_tx_eot ? 1 : 0;
- reg_value |= (cfg->ignore_rx_eot ? (1 << 4) : 0);
- DSI_W32(ctrl, DSI_EOT_PACKET_CTRL, reg_value);
-
- /* Turn on dsi clocks */
- DSI_W32(ctrl, DSI_CLK_CTRL, 0x23F);
-
- /* Setup DSI control register */
- reg_value = 0;
- reg_value |= (cfg->en_crc_check ? BIT(24) : 0);
- reg_value |= (cfg->en_ecc_check ? BIT(20) : 0);
- reg_value |= BIT(8); /* Clock lane */
- reg_value |= ((cfg->data_lanes & DSI_DATA_LANE_3) ? BIT(7) : 0);
- reg_value |= ((cfg->data_lanes & DSI_DATA_LANE_2) ? BIT(6) : 0);
- reg_value |= ((cfg->data_lanes & DSI_DATA_LANE_1) ? BIT(5) : 0);
- reg_value |= ((cfg->data_lanes & DSI_DATA_LANE_0) ? BIT(4) : 0);
-
- DSI_W32(ctrl, DSI_CTRL, reg_value);
-
- pr_debug("[DSI_%d]Host configuration complete\n", ctrl->index);
-}
-
-/**
- * phy_sw_reset() - perform a soft reset on the PHY.
- * @ctrl: Pointer to the controller host hardware.
- */
-void dsi_ctrl_hw_14_phy_sw_reset(struct dsi_ctrl_hw *ctrl)
-{
- DSI_W32(ctrl, DSI_PHY_SW_RESET, 0x1);
- udelay(1000);
- DSI_W32(ctrl, DSI_PHY_SW_RESET, 0x0);
- udelay(100);
-
- pr_debug("[DSI_%d] phy sw reset done\n", ctrl->index);
-}
-
-/**
- * soft_reset() - perform a soft reset on DSI controller
- * @ctrl: Pointer to the controller host hardware.
- *
- * The video, command and controller engines will be disable before the
- * reset is triggered. These engines will not be enabled after the reset
- * is complete. Caller must re-enable the engines.
- *
- * If the reset is done while MDP timing engine is turned on, the video
- * enigne should be re-enabled only during the vertical blanking time.
- */
-void dsi_ctrl_hw_14_soft_reset(struct dsi_ctrl_hw *ctrl)
-{
- u32 reg = 0;
- u32 reg_ctrl = 0;
-
- /* Clear DSI_EN, VIDEO_MODE_EN, CMD_MODE_EN */
- reg_ctrl = DSI_R32(ctrl, DSI_CTRL);
- DSI_W32(ctrl, DSI_CTRL, reg_ctrl & ~0x7);
-
- /* Force enable PCLK, BYTECLK, AHBM_HCLK */
- reg = DSI_R32(ctrl, DSI_CLK_CTRL);
- reg |= 0x23F;
- DSI_W32(ctrl, DSI_CLK_CTRL, reg);
-
- /* Trigger soft reset */
- DSI_W32(ctrl, DSI_SOFT_RESET, 0x1);
- udelay(1);
- DSI_W32(ctrl, DSI_SOFT_RESET, 0x0);
-
- /* Disable force clock on */
- reg &= ~(BIT(20) | BIT(11));
- DSI_W32(ctrl, DSI_CLK_CTRL, reg);
-
- /* Re-enable DSI controller */
- DSI_W32(ctrl, DSI_CTRL, reg_ctrl);
- pr_debug("[DSI_%d] ctrl soft reset done\n", ctrl->index);
-}
-
-/**
- * set_video_timing() - set up the timing for video frame
- * @ctrl: Pointer to controller host hardware.
- * @mode: Video mode information.
- *
- * Set up the video timing parameters for the DSI video mode operation.
- */
-void dsi_ctrl_hw_14_set_video_timing(struct dsi_ctrl_hw *ctrl,
- struct dsi_mode_info *mode)
-{
- u32 reg = 0;
- u32 hs_start = 0;
- u32 hs_end, active_h_start, active_h_end, h_total;
- u32 vs_start = 0, vs_end = 0;
- u32 vpos_start = 0, vpos_end, active_v_start, active_v_end, v_total;
-
- hs_end = mode->h_sync_width;
- active_h_start = mode->h_sync_width + mode->h_back_porch;
- active_h_end = active_h_start + mode->h_active;
- h_total = (mode->h_sync_width + mode->h_back_porch + mode->h_active +
- mode->h_front_porch) - 1;
-
- vpos_end = mode->v_sync_width;
- active_v_start = mode->v_sync_width + mode->v_back_porch;
- active_v_end = active_v_start + mode->v_active;
- v_total = (mode->v_sync_width + mode->v_back_porch + mode->v_active +
- mode->v_front_porch) - 1;
-
- reg = ((active_h_end & 0xFFFF) << 16) | (active_h_start & 0xFFFF);
- DSI_W32(ctrl, DSI_VIDEO_MODE_ACTIVE_H, reg);
-
- reg = ((active_v_end & 0xFFFF) << 16) | (active_v_start & 0xFFFF);
- DSI_W32(ctrl, DSI_VIDEO_MODE_ACTIVE_V, reg);
-
- reg = ((v_total & 0xFFFF) << 16) | (h_total & 0xFFFF);
- DSI_W32(ctrl, DSI_VIDEO_MODE_TOTAL, reg);
-
- reg = ((hs_end & 0xFFFF) << 16) | (hs_start & 0xFFFF);
- DSI_W32(ctrl, DSI_VIDEO_MODE_HSYNC, reg);
-
- reg = ((vs_end & 0xFFFF) << 16) | (vs_start & 0xFFFF);
- DSI_W32(ctrl, DSI_VIDEO_MODE_VSYNC, reg);
-
- reg = ((vpos_end & 0xFFFF) << 16) | (vpos_start & 0xFFFF);
- DSI_W32(ctrl, DSI_VIDEO_MODE_VSYNC_VPOS, reg);
-
- /* TODO: HS TIMER value? */
- DSI_W32(ctrl, DSI_HS_TIMER_CTRL, 0x3FD08);
- DSI_W32(ctrl, DSI_MISR_VIDEO_CTRL, 0x10100);
- DSI_W32(ctrl, DSI_DSI_TIMING_FLUSH, 0x1);
- pr_debug("[DSI_%d] ctrl video parameters updated\n", ctrl->index);
-}
-
-/**
- * setup_cmd_stream() - set up parameters for command pixel streams
- * @ctrl: Pointer to controller host hardware.
- * @width_in_pixels: Width of the stream in pixels.
- * @h_stride: Horizontal stride in bytes.
- * @height_inLines: Number of lines in the stream.
- * @vc_id: stream_id
- *
- * Setup parameters for command mode pixel stream size.
- */
-void dsi_ctrl_hw_14_setup_cmd_stream(struct dsi_ctrl_hw *ctrl,
- u32 width_in_pixels,
- u32 h_stride,
- u32 height_in_lines,
- u32 vc_id)
-{
- u32 reg = 0;
-
- reg = (h_stride + 1) << 16;
- reg |= (vc_id & 0x3) << 8;
- reg |= 0x39; /* packet data type */
- DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM0_CTRL, reg);
- DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM1_CTRL, reg);
-
- reg = (height_in_lines << 16) | width_in_pixels;
- DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM0_TOTAL, reg);
- DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM1_TOTAL, reg);
-}
-
-/**
- * video_engine_setup() - Setup dsi host controller for video mode
- * @ctrl: Pointer to controller host hardware.
- * @common_cfg: Common configuration parameters.
- * @cfg: Video mode configuration.
- *
- * Set up DSI video engine with a specific configuration. Controller and
- * video engine are not enabled as part of this function.
- */
-void dsi_ctrl_hw_14_video_engine_setup(struct dsi_ctrl_hw *ctrl,
- struct dsi_host_common_cfg *common_cfg,
- struct dsi_video_engine_cfg *cfg)
-{
- u32 reg = 0;
-
- reg |= (cfg->last_line_interleave_en ? BIT(31) : 0);
- reg |= (cfg->pulse_mode_hsa_he ? BIT(28) : 0);
- reg |= (cfg->hfp_lp11_en ? BIT(24) : 0);
- reg |= (cfg->hbp_lp11_en ? BIT(20) : 0);
- reg |= (cfg->hsa_lp11_en ? BIT(16) : 0);
- reg |= (cfg->eof_bllp_lp11_en ? BIT(15) : 0);
- reg |= (cfg->bllp_lp11_en ? BIT(12) : 0);
- reg |= (cfg->traffic_mode & 0x3) << 8;
- reg |= (cfg->vc_id & 0x3);
- reg |= (video_mode_format_map[common_cfg->dst_format] & 0x3) << 4;
- DSI_W32(ctrl, DSI_VIDEO_MODE_CTRL, reg);
-
- reg = (common_cfg->swap_mode & 0x7) << 12;
- reg |= (common_cfg->bit_swap_red ? BIT(0) : 0);
- reg |= (common_cfg->bit_swap_green ? BIT(4) : 0);
- reg |= (common_cfg->bit_swap_blue ? BIT(8) : 0);
- DSI_W32(ctrl, DSI_VIDEO_MODE_DATA_CTRL, reg);
- /* Enable Timing double buffering */
- DSI_W32(ctrl, DSI_DSI_TIMING_DB_MODE, 0x1);
-
-
- pr_debug("[DSI_%d] Video engine setup done\n", ctrl->index);
-}
-
-/**
- * cmd_engine_setup() - setup dsi host controller for command mode
- * @ctrl: Pointer to the controller host hardware.
- * @common_cfg: Common configuration parameters.
- * @cfg: Command mode configuration.
- *
- * Setup DSI CMD engine with a specific configuration. Controller and
- * command engine are not enabled as part of this function.
- */
-void dsi_ctrl_hw_14_cmd_engine_setup(struct dsi_ctrl_hw *ctrl,
- struct dsi_host_common_cfg *common_cfg,
- struct dsi_cmd_engine_cfg *cfg)
-{
- u32 reg = 0;
-
- reg = (cfg->max_cmd_packets_interleave & 0xF) << 20;
- reg |= (common_cfg->bit_swap_red ? BIT(4) : 0);
- reg |= (common_cfg->bit_swap_green ? BIT(8) : 0);
- reg |= (common_cfg->bit_swap_blue ? BIT(12) : 0);
- reg |= cmd_mode_format_map[common_cfg->dst_format];
- DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_CTRL, reg);
-
- reg = DSI_R32(ctrl, DSI_COMMAND_MODE_MDP_CTRL2);
- reg |= BIT(16);
- DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_CTRL2, reg);
-
- reg = cfg->wr_mem_start & 0xFF;
- reg |= (cfg->wr_mem_continue & 0xFF) << 8;
- reg |= (cfg->insert_dcs_command ? BIT(16) : 0);
- DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL, reg);
-
- pr_debug("[DSI_%d] Cmd engine setup done\n", ctrl->index);
-}
-
-/**
- * video_engine_en() - enable DSI video engine
- * @ctrl: Pointer to controller host hardware.
- * @on: Enable/disabel video engine.
- */
-void dsi_ctrl_hw_14_video_engine_en(struct dsi_ctrl_hw *ctrl, bool on)
-{
- u32 reg = 0;
-
- /* Set/Clear VIDEO_MODE_EN bit */
- reg = DSI_R32(ctrl, DSI_CTRL);
- if (on)
- reg |= BIT(1);
- else
- reg &= ~BIT(1);
-
- DSI_W32(ctrl, DSI_CTRL, reg);
-
- pr_debug("[DSI_%d] Video engine = %d\n", ctrl->index, on);
-}
-
-/**
- * ctrl_en() - enable DSI controller engine
- * @ctrl: Pointer to the controller host hardware.
- * @on: turn on/off the DSI controller engine.
- */
-void dsi_ctrl_hw_14_ctrl_en(struct dsi_ctrl_hw *ctrl, bool on)
-{
- u32 reg = 0;
-
- /* Set/Clear DSI_EN bit */
- reg = DSI_R32(ctrl, DSI_CTRL);
- if (on)
- reg |= BIT(0);
- else
- reg &= ~BIT(0);
-
- DSI_W32(ctrl, DSI_CTRL, reg);
-
- pr_debug("[DSI_%d] Controller engine = %d\n", ctrl->index, on);
-}
-
-/**
- * cmd_engine_en() - enable DSI controller command engine
- * @ctrl: Pointer to the controller host hardware.
- * @on: Turn on/off the DSI command engine.
- */
-void dsi_ctrl_hw_14_cmd_engine_en(struct dsi_ctrl_hw *ctrl, bool on)
-{
- u32 reg = 0;
-
- /* Set/Clear CMD_MODE_EN bit */
- reg = DSI_R32(ctrl, DSI_CTRL);
- if (on)
- reg |= BIT(2);
- else
- reg &= ~BIT(2);
-
- DSI_W32(ctrl, DSI_CTRL, reg);
-
- pr_debug("[DSI_%d] command engine = %d\n", ctrl->index, on);
-}
-
-/**
- * setup_lane_map() - setup mapping between logical and physical lanes
+ * dsi_ctrl_hw_14_setup_lane_map() - setup mapping between
+ * logical and physical lanes
* @ctrl: Pointer to the controller host hardware.
* @lane_map: Structure defining the mapping between DSI logical
* lanes and physical lanes.
*/
void dsi_ctrl_hw_14_setup_lane_map(struct dsi_ctrl_hw *ctrl,
- struct dsi_lane_mapping *lane_map)
+ struct dsi_lane_map *lane_map)
{
- u32 reg_value = 0;
- u32 lane_number = ((lane_map->physical_lane0 * 1000)+
- (lane_map->physical_lane1 * 100) +
- (lane_map->physical_lane2 * 10) +
- (lane_map->physical_lane3));
-
- if (lane_number == 123)
- reg_value = 0;
- else if (lane_number == 3012)
- reg_value = 1;
- else if (lane_number == 2301)
- reg_value = 2;
- else if (lane_number == 1230)
- reg_value = 3;
- else if (lane_number == 321)
- reg_value = 4;
- else if (lane_number == 1032)
- reg_value = 5;
- else if (lane_number == 2103)
- reg_value = 6;
- else if (lane_number == 3210)
- reg_value = 7;
-
- DSI_W32(ctrl, DSI_LANE_SWAP_CTRL, reg_value);
+ DSI_W32(ctrl, DSI_LANE_SWAP_CTRL, lane_map->lane_map_v1);
pr_debug("[DSI_%d] Lane swap setup complete\n", ctrl->index);
}
/**
- * kickoff_command() - transmits commands stored in memory
- * @ctrl: Pointer to the controller host hardware.
- * @cmd: Command information.
- * @flags: Modifiers for command transmission.
+ * dsi_ctrl_hw_14_wait_for_lane_idle()
+ * This function waits for all the active DSI lanes to be idle by polling all
+ * the FIFO_EMPTY bits and polling he lane status to ensure that all the lanes
+ * are in stop state. This function assumes that the bus clocks required to
+ * access the registers are already turned on.
*
- * The controller hardware is programmed with address and size of the
- * command buffer. The transmission is kicked off if
- * DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER flag is not set. If this flag is
- * set, caller should make a separate call to trigger_command_dma() to
- * transmit the command.
- */
-void dsi_ctrl_hw_14_kickoff_command(struct dsi_ctrl_hw *ctrl,
- struct dsi_ctrl_cmd_dma_info *cmd,
- u32 flags)
-{
- u32 reg = 0;
-
- /*Set BROADCAST_EN and EMBEDDED_MODE */
- reg = DSI_R32(ctrl, DSI_COMMAND_MODE_DMA_CTRL);
- if (cmd->en_broadcast)
- reg |= BIT(31);
- else
- reg &= ~BIT(31);
-
- if (cmd->is_master)
- reg |= BIT(30);
- else
- reg &= ~BIT(30);
-
- if (cmd->use_lpm)
- reg |= BIT(26);
- else
- reg &= ~BIT(26);
-
- reg |= BIT(28);
- DSI_W32(ctrl, DSI_COMMAND_MODE_DMA_CTRL, reg);
-
- DSI_W32(ctrl, DSI_DMA_CMD_OFFSET, cmd->offset);
- DSI_W32(ctrl, DSI_DMA_CMD_LENGTH, (cmd->length & 0xFFFFFF));
-
- /* wait for writes to complete before kick off */
- wmb();
-
- if (!(flags & DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER))
- DSI_W32(ctrl, DSI_CMD_MODE_DMA_SW_TRIGGER, 0x1);
-}
-
-/**
- * kickoff_fifo_command() - transmits a command using FIFO in dsi
- * hardware.
- * @ctrl: Pointer to the controller host hardware.
- * @cmd: Command information.
- * @flags: Modifiers for command transmission.
+ * @ctrl: Pointer to the controller host hardware.
+ * @lanes: ORed list of lanes (enum dsi_data_lanes) which need
+ * to be stopped.
*
- * The controller hardware FIFO is programmed with command header and
- * payload. The transmission is kicked off if
- * DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER flag is not set. If this flag is
- * set, caller should make a separate call to trigger_command_dma() to
- * transmit the command.
+ * return: Error code.
*/
-void dsi_ctrl_hw_14_kickoff_fifo_command(struct dsi_ctrl_hw *ctrl,
- struct dsi_ctrl_cmd_dma_fifo_info *cmd,
- u32 flags)
+int dsi_ctrl_hw_14_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl, u32 lanes)
{
- u32 reg = 0, i = 0;
- u32 *ptr = cmd->command;
- /*
- * Set CMD_DMA_TPG_EN, TPG_DMA_FIFO_MODE and
- * CMD_DMA_PATTERN_SEL = custom pattern stored in TPG DMA FIFO
- */
- reg = (BIT(1) | BIT(2) | (0x3 << 16));
- DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CTRL, reg);
+ int rc = 0, val = 0;
+ u32 stop_state_mask = 0, fifo_empty_mask = 0;
+ u32 const sleep_us = 10;
+ u32 const timeout_us = 100;
- /*
- * Program the FIFO with command buffer. Hardware requires an extra
- * DWORD (set to zero) if the length of command buffer is odd DWORDS.
- */
- for (i = 0; i < cmd->size; i += 4) {
- DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CMD_DMA_INIT_VAL, *ptr);
- ptr++;
+ if (lanes & DSI_DATA_LANE_0) {
+ stop_state_mask |= BIT(0);
+ fifo_empty_mask |= (BIT(12) | BIT(16));
+ }
+ if (lanes & DSI_DATA_LANE_1) {
+ stop_state_mask |= BIT(1);
+ fifo_empty_mask |= BIT(20);
+ }
+ if (lanes & DSI_DATA_LANE_2) {
+ stop_state_mask |= BIT(2);
+ fifo_empty_mask |= BIT(24);
+ }
+ if (lanes & DSI_DATA_LANE_3) {
+ stop_state_mask |= BIT(3);
+ fifo_empty_mask |= BIT(28);
}
- if ((cmd->size / 4) & 0x1)
- DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CMD_DMA_INIT_VAL, 0);
-
- /*Set BROADCAST_EN and EMBEDDED_MODE */
- reg = DSI_R32(ctrl, DSI_COMMAND_MODE_DMA_CTRL);
- if (cmd->en_broadcast)
- reg |= BIT(31);
- else
- reg &= ~BIT(31);
-
- if (cmd->is_master)
- reg |= BIT(30);
- else
- reg &= ~BIT(30);
-
- if (cmd->use_lpm)
- reg |= BIT(26);
- else
- reg &= ~BIT(26);
-
- reg |= BIT(28);
-
- DSI_W32(ctrl, DSI_COMMAND_MODE_DMA_CTRL, reg);
-
- DSI_W32(ctrl, DSI_DMA_CMD_LENGTH, (cmd->size & 0xFFFFFFFF));
- /* Finish writes before command trigger */
- wmb();
-
- if (!(flags & DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER))
- DSI_W32(ctrl, DSI_CMD_MODE_DMA_SW_TRIGGER, 0x1);
-
- pr_debug("[DSI_%d]size=%d, trigger = %d\n",
- ctrl->index, cmd->size,
- (flags & DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER) ? false : true);
-}
-
-void dsi_ctrl_hw_14_reset_cmd_fifo(struct dsi_ctrl_hw *ctrl)
-{
- /* disable cmd dma tpg */
- DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CTRL, 0x0);
-
- DSI_W32(ctrl, DSI_TPG_DMA_FIFO_RESET, 0x1);
- udelay(1);
- DSI_W32(ctrl, DSI_TPG_DMA_FIFO_RESET, 0x0);
-}
-
-/**
- * trigger_command_dma() - trigger transmission of command buffer.
- * @ctrl: Pointer to the controller host hardware.
- *
- * This trigger can be only used if there was a prior call to
- * kickoff_command() of kickoff_fifo_command() with
- * DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER flag.
- */
-void dsi_ctrl_hw_14_trigger_command_dma(struct dsi_ctrl_hw *ctrl)
-{
- DSI_W32(ctrl, DSI_CMD_MODE_DMA_SW_TRIGGER, 0x1);
- pr_debug("[DSI_%d] CMD DMA triggered\n", ctrl->index);
-}
-
-/**
- * get_cmd_read_data() - get data read from the peripheral
- * @ctrl: Pointer to the controller host hardware.
- * @rd_buf: Buffer where data will be read into.
- * @total_read_len: Number of bytes to read.
- *
- * return: number of bytes read.
- */
-u32 dsi_ctrl_hw_14_get_cmd_read_data(struct dsi_ctrl_hw *ctrl,
- u8 *rd_buf,
- u32 read_offset,
- u32 total_read_len)
-{
- u32 *lp, *temp, data;
- int i, j = 0, cnt;
- u32 read_cnt;
- u32 rx_byte = 0;
- u32 repeated_bytes = 0;
- u8 reg[16];
- u32 pkt_size = 0;
- int buf_offset = read_offset;
-
- lp = (u32 *)rd_buf;
- temp = (u32 *)reg;
- cnt = (rx_byte + 3) >> 2;
-
- if (cnt > 4)
- cnt = 4;
-
- if (rx_byte == 4)
- read_cnt = 4;
- else
- read_cnt = pkt_size + 6;
-
- if (read_cnt > 16) {
- int bytes_shifted;
-
- bytes_shifted = read_cnt - 16;
- repeated_bytes = buf_offset - bytes_shifted;
+ pr_debug("%s: polling for fifo empty, mask=0x%08x\n", __func__,
+ fifo_empty_mask);
+ rc = readl_poll_timeout(ctrl->base + DSI_FIFO_STATUS, val,
+ (val & fifo_empty_mask), sleep_us, timeout_us);
+ if (rc) {
+ pr_err("%s: fifo not empty, FIFO_STATUS=0x%08x\n",
+ __func__, val);
+ goto error;
}
- for (i = cnt - 1; i >= 0; i--) {
- data = DSI_R32(ctrl, DSI_RDBK_DATA0 + i*4);
- *temp++ = ntohl(data);
+ pr_debug("%s: polling for lanes to be in stop state, mask=0x%08x\n",
+ __func__, stop_state_mask);
+ rc = readl_poll_timeout(ctrl->base + DSI_LANE_STATUS, val,
+ (val & stop_state_mask), sleep_us, timeout_us);
+ if (rc) {
+ pr_err("%s: lanes not in stop state, LANE_STATUS=0x%08x\n",
+ __func__, val);
+ goto error;
}
- for (i = repeated_bytes; i < 16; i++)
- rd_buf[j++] = reg[i];
+error:
+ return rc;
- pr_debug("[DSI_%d] Read %d bytes\n", ctrl->index, j);
- return j;
}
+
/**
* ulps_request() - request ulps entry for specified lanes
* @ctrl: Pointer to the controller host hardware.
@@ -615,7 +123,12 @@
if (lanes & DSI_DATA_LANE_3)
reg |= BIT(3);
+ /*
+ * ULPS entry request. Wait for short time to make sure
+ * that the lanes enter ULPS. Recommended as per HPG.
+ */
DSI_W32(ctrl, DSI_LANE_CTRL, reg);
+ usleep_range(100, 110);
pr_debug("[DSI_%d] ULPS requested for lanes 0x%x\n", ctrl->index,
lanes);
@@ -634,9 +147,8 @@
{
u32 reg = 0;
- reg = DSI_R32(ctrl, DSI_LANE_CTRL);
if (lanes & DSI_CLOCK_LANE)
- reg |= BIT(12);
+ reg = BIT(12);
if (lanes & DSI_DATA_LANE_0)
reg |= BIT(8);
if (lanes & DSI_DATA_LANE_1)
@@ -646,45 +158,26 @@
if (lanes & DSI_DATA_LANE_3)
reg |= BIT(11);
+ /*
+ * ULPS Exit Request
+ * Hardware requirement is to wait for at least 1ms
+ */
DSI_W32(ctrl, DSI_LANE_CTRL, reg);
+ usleep_range(1000, 1010);
+ /*
+ * Sometimes when exiting ULPS, it is possible that some DSI
+ * lanes are not in the stop state which could lead to DSI
+ * commands not going through. To avoid this, force the lanes
+ * to be in stop state.
+ */
+ DSI_W32(ctrl, DSI_LANE_CTRL, reg << 8);
+ DSI_W32(ctrl, DSI_LANE_CTRL, 0x0);
pr_debug("[DSI_%d] ULPS exit request for lanes=0x%x\n",
ctrl->index, lanes);
}
/**
- * clear_ulps_request() - clear ulps request once all lanes are active
- * @ctrl: Pointer to controller host hardware.
- * @lanes: ORed list of lanes (enum dsi_data_lanes).
- *
- * ULPS request should be cleared after the lanes have exited ULPS.
- */
-void dsi_ctrl_hw_14_clear_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes)
-{
- u32 reg = 0;
-
- reg = DSI_R32(ctrl, DSI_LANE_CTRL);
- reg &= ~BIT(4); /* clock lane */
- if (lanes & DSI_DATA_LANE_0)
- reg &= ~BIT(0);
- if (lanes & DSI_DATA_LANE_1)
- reg &= ~BIT(1);
- if (lanes & DSI_DATA_LANE_2)
- reg &= ~BIT(2);
- if (lanes & DSI_DATA_LANE_3)
- reg &= ~BIT(3);
-
- DSI_W32(ctrl, DSI_LANE_CTRL, reg);
- /*
- * HPG recommends separate writes for clearing ULPS_REQUEST and
- * ULPS_EXIT.
- */
- DSI_W32(ctrl, DSI_LANE_CTRL, 0x0);
-
- pr_debug("[DSI_%d] ULPS request cleared\n", ctrl->index);
-}
-
-/**
* get_lanes_in_ulps() - returns the list of lanes in ULPS mode
* @ctrl: Pointer to the controller host hardware.
*
@@ -718,7 +211,7 @@
* clamp_enable() - enable DSI clamps to keep PHY driving a stable link
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes which need to be clamped.
- * @enable_ulps: TODO:??
+ * @enable_ulps: Boolean to specify if ULPS is enabled in DSI controller
*/
void dsi_ctrl_hw_14_clamp_enable(struct dsi_ctrl_hw *ctrl,
u32 lanes,
@@ -761,15 +254,12 @@
clamp_reg |= BIT(0);
}
- clamp_reg |= BIT(15); /* Enable clamp */
-
reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
reg |= (clamp_reg << bit_shift);
DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
-
reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
- reg |= BIT(30);
+ reg |= (BIT(15) << bit_shift); /* Enable clamp */
DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
pr_debug("[DSI_%d] Clamps enabled for lanes=0x%x\n", ctrl->index,
@@ -780,7 +270,7 @@
* clamp_disable() - disable DSI clamps
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes which need to have clamps released.
- * @disable_ulps: TODO:??
+ * @disable_ulps: Boolean to specify if ULPS is enabled in DSI controller
*/
void dsi_ctrl_hw_14_clamp_disable(struct dsi_ctrl_hw *ctrl,
u32 lanes,
@@ -826,11 +316,6 @@
clamp_reg |= BIT(15); /* Enable clamp */
clamp_reg <<= bit_shift;
- /* Disable PHY reset skip */
- reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
- reg &= ~BIT(30);
- DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
-
reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
reg &= ~(clamp_reg);
DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
@@ -838,521 +323,6 @@
pr_debug("[DSI_%d] Disable clamps for lanes=%d\n", ctrl->index, lanes);
}
-/**
- * get_interrupt_status() - returns the interrupt status
- * @ctrl: Pointer to the controller host hardware.
- *
- * Returns the ORed list of interrupts(enum dsi_status_int_type) that
- * are active. This list does not include any error interrupts. Caller
- * should call get_error_status for error interrupts.
- *
- * Return: List of active interrupts.
- */
-u32 dsi_ctrl_hw_14_get_interrupt_status(struct dsi_ctrl_hw *ctrl)
-{
- u32 reg = 0;
- u32 ints = 0;
-
- reg = DSI_R32(ctrl, DSI_INT_CTRL);
-
- if (reg & BIT(0))
- ints |= DSI_CMD_MODE_DMA_DONE;
- if (reg & BIT(8))
- ints |= DSI_CMD_FRAME_DONE;
- if (reg & BIT(10))
- ints |= DSI_CMD_STREAM0_FRAME_DONE;
- if (reg & BIT(12))
- ints |= DSI_CMD_STREAM1_FRAME_DONE;
- if (reg & BIT(14))
- ints |= DSI_CMD_STREAM2_FRAME_DONE;
- if (reg & BIT(16))
- ints |= DSI_VIDEO_MODE_FRAME_DONE;
- if (reg & BIT(20))
- ints |= DSI_BTA_DONE;
- if (reg & BIT(28))
- ints |= DSI_DYN_REFRESH_DONE;
- if (reg & BIT(30))
- ints |= DSI_DESKEW_DONE;
-
- pr_debug("[DSI_%d] Interrupt status = 0x%x, INT_CTRL=0x%x\n",
- ctrl->index, ints, reg);
- return ints;
-}
-
-/**
- * clear_interrupt_status() - clears the specified interrupts
- * @ctrl: Pointer to the controller host hardware.
- * @ints: List of interrupts to be cleared.
- */
-void dsi_ctrl_hw_14_clear_interrupt_status(struct dsi_ctrl_hw *ctrl, u32 ints)
-{
- u32 reg = 0;
-
- if (ints & DSI_CMD_MODE_DMA_DONE)
- reg |= BIT(0);
- if (ints & DSI_CMD_FRAME_DONE)
- reg |= BIT(8);
- if (ints & DSI_CMD_STREAM0_FRAME_DONE)
- reg |= BIT(10);
- if (ints & DSI_CMD_STREAM1_FRAME_DONE)
- reg |= BIT(12);
- if (ints & DSI_CMD_STREAM2_FRAME_DONE)
- reg |= BIT(14);
- if (ints & DSI_VIDEO_MODE_FRAME_DONE)
- reg |= BIT(16);
- if (ints & DSI_BTA_DONE)
- reg |= BIT(20);
- if (ints & DSI_DYN_REFRESH_DONE)
- reg |= BIT(28);
- if (ints & DSI_DESKEW_DONE)
- reg |= BIT(30);
-
- DSI_W32(ctrl, DSI_INT_CTRL, reg);
-
- pr_debug("[DSI_%d] Clear interrupts, ints = 0x%x, INT_CTRL=0x%x\n",
- ctrl->index, ints, reg);
-}
-
-/**
- * enable_status_interrupts() - enable the specified interrupts
- * @ctrl: Pointer to the controller host hardware.
- * @ints: List of interrupts to be enabled.
- *
- * Enables the specified interrupts. This list will override the
- * previous interrupts enabled through this function. Caller has to
- * maintain the state of the interrupts enabled. To disable all
- * interrupts, set ints to 0.
- */
-void dsi_ctrl_hw_14_enable_status_interrupts(struct dsi_ctrl_hw *ctrl, u32 ints)
-{
- u32 reg = 0;
-
- /* Do not change value of DSI_ERROR_MASK bit */
- reg |= (DSI_R32(ctrl, DSI_INT_CTRL) & BIT(25));
- if (ints & DSI_CMD_MODE_DMA_DONE)
- reg |= BIT(1);
- if (ints & DSI_CMD_FRAME_DONE)
- reg |= BIT(9);
- if (ints & DSI_CMD_STREAM0_FRAME_DONE)
- reg |= BIT(11);
- if (ints & DSI_CMD_STREAM1_FRAME_DONE)
- reg |= BIT(13);
- if (ints & DSI_CMD_STREAM2_FRAME_DONE)
- reg |= BIT(15);
- if (ints & DSI_VIDEO_MODE_FRAME_DONE)
- reg |= BIT(17);
- if (ints & DSI_BTA_DONE)
- reg |= BIT(21);
- if (ints & DSI_DYN_REFRESH_DONE)
- reg |= BIT(29);
- if (ints & DSI_DESKEW_DONE)
- reg |= BIT(31);
-
- DSI_W32(ctrl, DSI_INT_CTRL, reg);
-
- pr_debug("[DSI_%d] Enable interrupts 0x%x, INT_CTRL=0x%x\n",
- ctrl->index, ints, reg);
-}
-
-/**
- * get_error_status() - returns the error status
- * @ctrl: Pointer to the controller host hardware.
- *
- * Returns the ORed list of errors(enum dsi_error_int_type) that are
- * active. This list does not include any status interrupts. Caller
- * should call get_interrupt_status for status interrupts.
- *
- * Return: List of active error interrupts.
- */
-u64 dsi_ctrl_hw_14_get_error_status(struct dsi_ctrl_hw *ctrl)
-{
- u32 dln0_phy_err;
- u32 fifo_status;
- u32 ack_error;
- u32 timeout_errors;
- u32 clk_error;
- u32 dsi_status;
- u64 errors = 0;
-
- dln0_phy_err = DSI_R32(ctrl, DSI_DLN0_PHY_ERR);
- if (dln0_phy_err & BIT(0))
- errors |= DSI_DLN0_ESC_ENTRY_ERR;
- if (dln0_phy_err & BIT(4))
- errors |= DSI_DLN0_ESC_SYNC_ERR;
- if (dln0_phy_err & BIT(8))
- errors |= DSI_DLN0_LP_CONTROL_ERR;
- if (dln0_phy_err & BIT(12))
- errors |= DSI_DLN0_LP0_CONTENTION;
- if (dln0_phy_err & BIT(16))
- errors |= DSI_DLN0_LP1_CONTENTION;
-
- fifo_status = DSI_R32(ctrl, DSI_FIFO_STATUS);
- if (fifo_status & BIT(7))
- errors |= DSI_CMD_MDP_FIFO_UNDERFLOW;
- if (fifo_status & BIT(10))
- errors |= DSI_CMD_DMA_FIFO_UNDERFLOW;
- if (fifo_status & BIT(18))
- errors |= DSI_DLN0_HS_FIFO_OVERFLOW;
- if (fifo_status & BIT(19))
- errors |= DSI_DLN0_HS_FIFO_UNDERFLOW;
- if (fifo_status & BIT(22))
- errors |= DSI_DLN1_HS_FIFO_OVERFLOW;
- if (fifo_status & BIT(23))
- errors |= DSI_DLN1_HS_FIFO_UNDERFLOW;
- if (fifo_status & BIT(26))
- errors |= DSI_DLN2_HS_FIFO_OVERFLOW;
- if (fifo_status & BIT(27))
- errors |= DSI_DLN2_HS_FIFO_UNDERFLOW;
- if (fifo_status & BIT(30))
- errors |= DSI_DLN3_HS_FIFO_OVERFLOW;
- if (fifo_status & BIT(31))
- errors |= DSI_DLN3_HS_FIFO_UNDERFLOW;
-
- ack_error = DSI_R32(ctrl, DSI_ACK_ERR_STATUS);
- if (ack_error & BIT(16))
- errors |= DSI_RDBK_SINGLE_ECC_ERR;
- if (ack_error & BIT(17))
- errors |= DSI_RDBK_MULTI_ECC_ERR;
- if (ack_error & BIT(20))
- errors |= DSI_RDBK_CRC_ERR;
- if (ack_error & BIT(23))
- errors |= DSI_RDBK_INCOMPLETE_PKT;
- if (ack_error & BIT(24))
- errors |= DSI_PERIPH_ERROR_PKT;
-
- timeout_errors = DSI_R32(ctrl, DSI_TIMEOUT_STATUS);
- if (timeout_errors & BIT(0))
- errors |= DSI_HS_TX_TIMEOUT;
- if (timeout_errors & BIT(4))
- errors |= DSI_LP_RX_TIMEOUT;
- if (timeout_errors & BIT(8))
- errors |= DSI_BTA_TIMEOUT;
-
- clk_error = DSI_R32(ctrl, DSI_CLK_STATUS);
- if (clk_error & BIT(16))
- errors |= DSI_PLL_UNLOCK;
-
- dsi_status = DSI_R32(ctrl, DSI_STATUS);
- if (dsi_status & BIT(31))
- errors |= DSI_INTERLEAVE_OP_CONTENTION;
-
- pr_debug("[DSI_%d] Error status = 0x%llx, phy=0x%x, fifo=0x%x",
- ctrl->index, errors, dln0_phy_err, fifo_status);
- pr_debug("[DSI_%d] ack=0x%x, timeout=0x%x, clk=0x%x, dsi=0x%x\n",
- ctrl->index, ack_error, timeout_errors, clk_error, dsi_status);
- return errors;
-}
-
-/**
- * clear_error_status() - clears the specified errors
- * @ctrl: Pointer to the controller host hardware.
- * @errors: List of errors to be cleared.
- */
-void dsi_ctrl_hw_14_clear_error_status(struct dsi_ctrl_hw *ctrl, u64 errors)
-{
- u32 dln0_phy_err = 0;
- u32 fifo_status = 0;
- u32 ack_error = 0;
- u32 timeout_error = 0;
- u32 clk_error = 0;
- u32 dsi_status = 0;
- u32 int_ctrl = 0;
-
- if (errors & DSI_RDBK_SINGLE_ECC_ERR)
- ack_error |= BIT(16);
- if (errors & DSI_RDBK_MULTI_ECC_ERR)
- ack_error |= BIT(17);
- if (errors & DSI_RDBK_CRC_ERR)
- ack_error |= BIT(20);
- if (errors & DSI_RDBK_INCOMPLETE_PKT)
- ack_error |= BIT(23);
- if (errors & DSI_PERIPH_ERROR_PKT)
- ack_error |= BIT(24);
-
- if (errors & DSI_LP_RX_TIMEOUT)
- timeout_error |= BIT(4);
- if (errors & DSI_HS_TX_TIMEOUT)
- timeout_error |= BIT(0);
- if (errors & DSI_BTA_TIMEOUT)
- timeout_error |= BIT(8);
-
- if (errors & DSI_PLL_UNLOCK)
- clk_error |= BIT(16);
-
- if (errors & DSI_DLN0_LP0_CONTENTION)
- dln0_phy_err |= BIT(12);
- if (errors & DSI_DLN0_LP1_CONTENTION)
- dln0_phy_err |= BIT(16);
- if (errors & DSI_DLN0_ESC_ENTRY_ERR)
- dln0_phy_err |= BIT(0);
- if (errors & DSI_DLN0_ESC_SYNC_ERR)
- dln0_phy_err |= BIT(4);
- if (errors & DSI_DLN0_LP_CONTROL_ERR)
- dln0_phy_err |= BIT(8);
-
- if (errors & DSI_CMD_DMA_FIFO_UNDERFLOW)
- fifo_status |= BIT(10);
- if (errors & DSI_CMD_MDP_FIFO_UNDERFLOW)
- fifo_status |= BIT(7);
- if (errors & DSI_DLN0_HS_FIFO_OVERFLOW)
- fifo_status |= BIT(18);
- if (errors & DSI_DLN1_HS_FIFO_OVERFLOW)
- fifo_status |= BIT(22);
- if (errors & DSI_DLN2_HS_FIFO_OVERFLOW)
- fifo_status |= BIT(26);
- if (errors & DSI_DLN3_HS_FIFO_OVERFLOW)
- fifo_status |= BIT(30);
- if (errors & DSI_DLN0_HS_FIFO_UNDERFLOW)
- fifo_status |= BIT(19);
- if (errors & DSI_DLN1_HS_FIFO_UNDERFLOW)
- fifo_status |= BIT(23);
- if (errors & DSI_DLN2_HS_FIFO_UNDERFLOW)
- fifo_status |= BIT(27);
- if (errors & DSI_DLN3_HS_FIFO_UNDERFLOW)
- fifo_status |= BIT(31);
-
- if (errors & DSI_INTERLEAVE_OP_CONTENTION)
- dsi_status |= BIT(31);
-
- DSI_W32(ctrl, DSI_DLN0_PHY_ERR, dln0_phy_err);
- DSI_W32(ctrl, DSI_FIFO_STATUS, fifo_status);
- DSI_W32(ctrl, DSI_ACK_ERR_STATUS, ack_error);
- DSI_W32(ctrl, DSI_TIMEOUT_STATUS, timeout_error);
- DSI_W32(ctrl, DSI_CLK_STATUS, clk_error);
- DSI_W32(ctrl, DSI_STATUS, dsi_status);
-
- int_ctrl = DSI_R32(ctrl, DSI_INT_CTRL);
- int_ctrl |= BIT(24);
- DSI_W32(ctrl, DSI_INT_CTRL, int_ctrl);
- pr_debug("[DSI_%d] clear errors = 0x%llx, phy=0x%x, fifo=0x%x",
- ctrl->index, errors, dln0_phy_err, fifo_status);
- pr_debug("[DSI_%d] ack=0x%x, timeout=0x%x, clk=0x%x, dsi=0x%x\n",
- ctrl->index, ack_error, timeout_error, clk_error, dsi_status);
-}
-
-/**
- * enable_error_interrupts() - enable the specified interrupts
- * @ctrl: Pointer to the controller host hardware.
- * @errors: List of errors to be enabled.
- *
- * Enables the specified interrupts. This list will override the
- * previous interrupts enabled through this function. Caller has to
- * maintain the state of the interrupts enabled. To disable all
- * interrupts, set errors to 0.
- */
-void dsi_ctrl_hw_14_enable_error_interrupts(struct dsi_ctrl_hw *ctrl,
- u64 errors)
-{
- u32 int_ctrl = 0;
- u32 int_mask0 = 0x7FFF3BFF;
-
- int_ctrl = DSI_R32(ctrl, DSI_INT_CTRL);
- if (errors)
- int_ctrl |= BIT(25);
- else
- int_ctrl &= ~BIT(25);
-
- if (errors & DSI_RDBK_SINGLE_ECC_ERR)
- int_mask0 &= ~BIT(0);
- if (errors & DSI_RDBK_MULTI_ECC_ERR)
- int_mask0 &= ~BIT(1);
- if (errors & DSI_RDBK_CRC_ERR)
- int_mask0 &= ~BIT(2);
- if (errors & DSI_RDBK_INCOMPLETE_PKT)
- int_mask0 &= ~BIT(3);
- if (errors & DSI_PERIPH_ERROR_PKT)
- int_mask0 &= ~BIT(4);
-
- if (errors & DSI_LP_RX_TIMEOUT)
- int_mask0 &= ~BIT(5);
- if (errors & DSI_HS_TX_TIMEOUT)
- int_mask0 &= ~BIT(6);
- if (errors & DSI_BTA_TIMEOUT)
- int_mask0 &= ~BIT(7);
-
- if (errors & DSI_PLL_UNLOCK)
- int_mask0 &= ~BIT(28);
-
- if (errors & DSI_DLN0_LP0_CONTENTION)
- int_mask0 &= ~BIT(24);
- if (errors & DSI_DLN0_LP1_CONTENTION)
- int_mask0 &= ~BIT(25);
- if (errors & DSI_DLN0_ESC_ENTRY_ERR)
- int_mask0 &= ~BIT(21);
- if (errors & DSI_DLN0_ESC_SYNC_ERR)
- int_mask0 &= ~BIT(22);
- if (errors & DSI_DLN0_LP_CONTROL_ERR)
- int_mask0 &= ~BIT(23);
-
- if (errors & DSI_CMD_DMA_FIFO_UNDERFLOW)
- int_mask0 &= ~BIT(9);
- if (errors & DSI_CMD_MDP_FIFO_UNDERFLOW)
- int_mask0 &= ~BIT(11);
- if (errors & DSI_DLN0_HS_FIFO_OVERFLOW)
- int_mask0 &= ~BIT(16);
- if (errors & DSI_DLN1_HS_FIFO_OVERFLOW)
- int_mask0 &= ~BIT(17);
- if (errors & DSI_DLN2_HS_FIFO_OVERFLOW)
- int_mask0 &= ~BIT(18);
- if (errors & DSI_DLN3_HS_FIFO_OVERFLOW)
- int_mask0 &= ~BIT(19);
- if (errors & DSI_DLN0_HS_FIFO_UNDERFLOW)
- int_mask0 &= ~BIT(26);
- if (errors & DSI_DLN1_HS_FIFO_UNDERFLOW)
- int_mask0 &= ~BIT(27);
- if (errors & DSI_DLN2_HS_FIFO_UNDERFLOW)
- int_mask0 &= ~BIT(29);
- if (errors & DSI_DLN3_HS_FIFO_UNDERFLOW)
- int_mask0 &= ~BIT(30);
-
- if (errors & DSI_INTERLEAVE_OP_CONTENTION)
- int_mask0 &= ~BIT(8);
-
- DSI_W32(ctrl, DSI_INT_CTRL, int_ctrl);
- DSI_W32(ctrl, DSI_ERR_INT_MASK0, int_mask0);
-
- pr_debug("[DSI_%d] enable errors = 0x%llx, int_mask0=0x%x\n",
- ctrl->index, errors, int_mask0);
-}
-
-/**
- * video_test_pattern_setup() - setup test pattern engine for video mode
- * @ctrl: Pointer to the controller host hardware.
- * @type: Type of test pattern.
- * @init_val: Initial value to use for generating test pattern.
- */
-void dsi_ctrl_hw_14_video_test_pattern_setup(struct dsi_ctrl_hw *ctrl,
- enum dsi_test_pattern type,
- u32 init_val)
-{
- u32 reg = 0;
-
- DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_VIDEO_INIT_VAL, init_val);
-
- switch (type) {
- case DSI_TEST_PATTERN_FIXED:
- reg |= (0x2 << 4);
- break;
- case DSI_TEST_PATTERN_INC:
- reg |= (0x1 << 4);
- break;
- case DSI_TEST_PATTERN_POLY:
- DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_VIDEO_POLY, 0xF0F0F);
- break;
- default:
- break;
- }
-
- DSI_W32(ctrl, DSI_TPG_MAIN_CONTROL, 0x100);
- DSI_W32(ctrl, DSI_TPG_VIDEO_CONFIG, 0x5);
- DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CTRL, reg);
-
- pr_debug("[DSI_%d] Video test pattern setup done\n", ctrl->index);
-}
-
-/**
- * cmd_test_pattern_setup() - setup test patttern engine for cmd mode
- * @ctrl: Pointer to the controller host hardware.
- * @type: Type of test pattern.
- * @init_val: Initial value to use for generating test pattern.
- * @stream_id: Stream Id on which packets are generated.
- */
-void dsi_ctrl_hw_14_cmd_test_pattern_setup(struct dsi_ctrl_hw *ctrl,
- enum dsi_test_pattern type,
- u32 init_val,
- u32 stream_id)
-{
- u32 reg = 0;
- u32 init_offset;
- u32 poly_offset;
- u32 pattern_sel_shift;
-
- switch (stream_id) {
- case 0:
- init_offset = DSI_TEST_PATTERN_GEN_CMD_MDP_INIT_VAL0;
- poly_offset = DSI_TEST_PATTERN_GEN_CMD_MDP_STREAM0_POLY;
- pattern_sel_shift = 8;
- break;
- case 1:
- init_offset = DSI_TEST_PATTERN_GEN_CMD_MDP_INIT_VAL1;
- poly_offset = DSI_TEST_PATTERN_GEN_CMD_MDP_STREAM1_POLY;
- pattern_sel_shift = 12;
- break;
- case 2:
- init_offset = DSI_TEST_PATTERN_GEN_CMD_MDP_INIT_VAL2;
- poly_offset = DSI_TEST_PATTERN_GEN_CMD_MDP_STREAM2_POLY;
- pattern_sel_shift = 20;
- break;
- default:
- return;
- }
-
- DSI_W32(ctrl, init_offset, init_val);
-
- switch (type) {
- case DSI_TEST_PATTERN_FIXED:
- reg |= (0x2 << pattern_sel_shift);
- break;
- case DSI_TEST_PATTERN_INC:
- reg |= (0x1 << pattern_sel_shift);
- break;
- case DSI_TEST_PATTERN_POLY:
- DSI_W32(ctrl, poly_offset, 0xF0F0F);
- break;
- default:
- break;
- }
-
- DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CTRL, reg);
- pr_debug("[DSI_%d] Cmd test pattern setup done\n", ctrl->index);
-}
-
-/**
- * test_pattern_enable() - enable test pattern engine
- * @ctrl: Pointer to the controller host hardware.
- * @enable: Enable/Disable test pattern engine.
- */
-void dsi_ctrl_hw_14_test_pattern_enable(struct dsi_ctrl_hw *ctrl,
- bool enable)
-{
- u32 reg = DSI_R32(ctrl, DSI_TEST_PATTERN_GEN_CTRL);
-
- if (enable)
- reg |= BIT(0);
- else
- reg &= ~BIT(0);
-
- DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CTRL, reg);
-
- pr_debug("[DSI_%d] Test pattern enable=%d\n", ctrl->index, enable);
-}
-
-/**
- * trigger_cmd_test_pattern() - trigger a command mode frame update with
- * test pattern
- * @ctrl: Pointer to the controller host hardware.
- * @stream_id: Stream on which frame update is sent.
- */
-void dsi_ctrl_hw_14_trigger_cmd_test_pattern(struct dsi_ctrl_hw *ctrl,
- u32 stream_id)
-{
- switch (stream_id) {
- case 0:
- DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CMD_STREAM0_TRIGGER, 0x1);
- break;
- case 1:
- DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CMD_STREAM1_TRIGGER, 0x1);
- break;
- case 2:
- DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CMD_STREAM2_TRIGGER, 0x1);
- break;
- default:
- break;
- }
-
- pr_debug("[DSI_%d] Cmd Test pattern trigger\n", ctrl->index);
-}
-
#define DUMP_REG_VALUE(off) "\t%-30s: 0x%08x\n", #off, DSI_R32(ctrl, off)
ssize_t dsi_ctrl_hw_14_reg_dump_to_buffer(struct dsi_ctrl_hw *ctrl,
char *buf,
@@ -1508,5 +478,3 @@
pr_err("LLENGTH = %d\n", len);
return len;
}
-
-
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_2_0.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_2_0.c
new file mode 100644
index 0000000..c22849a
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_2_0.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "dsi-hw:" fmt
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+
+#include "dsi_ctrl_hw.h"
+#include "dsi_ctrl_reg.h"
+#include "dsi_hw.h"
+
+void dsi_ctrl_hw_20_setup_lane_map(struct dsi_ctrl_hw *ctrl,
+ struct dsi_lane_map *lane_map)
+{
+ u32 reg_value = lane_map->lane_map_v2[DSI_LOGICAL_LANE_0] |
+ (lane_map->lane_map_v2[DSI_LOGICAL_LANE_1] << 4) |
+ (lane_map->lane_map_v2[DSI_LOGICAL_LANE_2] << 8) |
+ (lane_map->lane_map_v2[DSI_LOGICAL_LANE_3] << 12);
+
+ DSI_W32(ctrl, DSI_LANE_SWAP_CTRL, reg_value);
+
+ pr_debug("[DSI_%d] Lane swap setup complete\n", ctrl->index);
+}
+
+int dsi_ctrl_hw_20_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl,
+ u32 lanes)
+{
+ int rc = 0, val = 0;
+ u32 fifo_empty_mask = 0;
+ u32 const sleep_us = 10;
+ u32 const timeout_us = 100;
+
+ if (lanes & DSI_DATA_LANE_0)
+ fifo_empty_mask |= (BIT(12) | BIT(16));
+
+ if (lanes & DSI_DATA_LANE_1)
+ fifo_empty_mask |= BIT(20);
+
+ if (lanes & DSI_DATA_LANE_2)
+ fifo_empty_mask |= BIT(24);
+
+ if (lanes & DSI_DATA_LANE_3)
+ fifo_empty_mask |= BIT(28);
+
+ pr_debug("%s: polling for fifo empty, mask=0x%08x\n", __func__,
+ fifo_empty_mask);
+ rc = readl_poll_timeout(ctrl->base + DSI_FIFO_STATUS, val,
+ (val & fifo_empty_mask), sleep_us, timeout_us);
+ if (rc) {
+ pr_err("%s: fifo not empty, FIFO_STATUS=0x%08x\n",
+ __func__, val);
+ goto error;
+ }
+
+error:
+ return rc;
+}
+
+#define DUMP_REG_VALUE(off) "\t%-30s: 0x%08x\n", #off, DSI_R32(ctrl, off)
+ssize_t dsi_ctrl_hw_20_reg_dump_to_buffer(struct dsi_ctrl_hw *ctrl,
+ char *buf,
+ u32 size)
+{
+ u32 len = 0;
+
+ len += snprintf((buf + len), (size - len), "CONFIGURATION REGS:\n");
+
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_HW_VERSION));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_STATUS));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_FIFO_STATUS));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_VIDEO_MODE_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_VIDEO_MODE_SYNC_DATATYPE));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_VIDEO_MODE_PIXEL_DATATYPE));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_VIDEO_MODE_BLANKING_DATATYPE));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_VIDEO_MODE_DATA_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_VIDEO_MODE_ACTIVE_H));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_VIDEO_MODE_ACTIVE_V));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_VIDEO_MODE_TOTAL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_VIDEO_MODE_HSYNC));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_VIDEO_MODE_VSYNC));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_VIDEO_MODE_VSYNC_VPOS));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_COMMAND_MODE_DMA_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_DMA_CMD_OFFSET));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_DMA_CMD_LENGTH));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_DMA_FIFO_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_DMA_NULL_PACKET_DATA));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM0_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM0_TOTAL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM1_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM1_TOTAL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_ACK_ERR_STATUS));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_RDBK_DATA0));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_RDBK_DATA1));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_RDBK_DATA2));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_RDBK_DATA3));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_RDBK_DATATYPE0));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_RDBK_DATATYPE1));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_TRIG_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_EXT_MUX));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_EXT_MUX_TE_PULSE_DETECT_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_CMD_MODE_DMA_SW_TRIGGER));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_CMD_MODE_MDP_SW_TRIGGER));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_CMD_MODE_BTA_SW_TRIGGER));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_RESET_SW_TRIGGER));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_LANE_STATUS));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_LANE_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_LANE_SWAP_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_DLN0_PHY_ERR));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_LP_TIMER_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_HS_TIMER_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_TIMEOUT_STATUS));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_CLKOUT_TIMING_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_EOT_PACKET));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_EOT_PACKET_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_GENERIC_ESC_TX_TRIGGER));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_ERR_INT_MASK0));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_INT_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_SOFT_RESET));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_CLK_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_CLK_STATUS));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_PHY_SW_RESET));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_AXI2AHB_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_CTRL2));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM2_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM2_TOTAL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_VBIF_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_AES_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_RDBK_DATA_CTRL));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_TEST_PATTERN_GEN_CMD_DMA_INIT_VAL2));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_TPG_DMA_FIFO_STATUS));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_TPG_DMA_FIFO_WRITE_TRIGGER));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_DSI_TIMING_FLUSH));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_DSI_TIMING_DB_MODE));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_TPG_DMA_FIFO_RESET));
+ len += snprintf((buf + len), (size - len),
+ DUMP_REG_VALUE(DSI_VERSION));
+
+ pr_err("LLENGTH = %d\n", len);
+ return len;
+}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
new file mode 100644
index 0000000..9a6fc77
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
@@ -0,0 +1,1104 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "dsi-hw:" fmt
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+
+#include "dsi_ctrl_hw.h"
+#include "dsi_ctrl_reg.h"
+#include "dsi_hw.h"
+
+#define MMSS_MISC_CLAMP_REG_OFF 0x0014
+
+/* Unsupported formats default to RGB888 */
+static const u8 cmd_mode_format_map[DSI_PIXEL_FORMAT_MAX] = {
+ 0x6, 0x7, 0x8, 0x8, 0x0, 0x3, 0x4 };
+static const u8 video_mode_format_map[DSI_PIXEL_FORMAT_MAX] = {
+ 0x0, 0x1, 0x2, 0x3, 0x3, 0x3, 0x3 };
+
+/**
+ * dsi_setup_trigger_controls() - setup dsi trigger configurations
+ * @ctrl: Pointer to the controller host hardware.
+ * @cfg: DSI host configuration that is common to both video and
+ * command modes.
+ */
+static void dsi_setup_trigger_controls(struct dsi_ctrl_hw *ctrl,
+ struct dsi_host_common_cfg *cfg)
+{
+ u32 reg = 0;
+ const u8 trigger_map[DSI_TRIGGER_MAX] = {
+ 0x0, 0x2, 0x1, 0x4, 0x5, 0x6 };
+
+ reg |= (cfg->te_mode == DSI_TE_ON_EXT_PIN) ? BIT(31) : 0;
+ reg |= (trigger_map[cfg->dma_cmd_trigger] & 0x7);
+ reg |= (trigger_map[cfg->mdp_cmd_trigger] & 0x7) << 4;
+ DSI_W32(ctrl, DSI_TRIG_CTRL, reg);
+}
+
+/**
+ * dsi_ctrl_hw_cmn_host_setup() - setup dsi host configuration
+ * @ctrl: Pointer to the controller host hardware.
+ * @cfg: DSI host configuration that is common to both video and
+ * command modes.
+ */
+void dsi_ctrl_hw_cmn_host_setup(struct dsi_ctrl_hw *ctrl,
+ struct dsi_host_common_cfg *cfg)
+{
+ u32 reg_value = 0;
+
+ dsi_setup_trigger_controls(ctrl, cfg);
+
+ /* Setup clocking timing controls */
+ reg_value = ((cfg->t_clk_post & 0x3F) << 8);
+ reg_value |= (cfg->t_clk_pre & 0x3F);
+ DSI_W32(ctrl, DSI_CLKOUT_TIMING_CTRL, reg_value);
+
+ /* EOT packet control */
+ reg_value = cfg->append_tx_eot ? 1 : 0;
+ reg_value |= (cfg->ignore_rx_eot ? (1 << 4) : 0);
+ DSI_W32(ctrl, DSI_EOT_PACKET_CTRL, reg_value);
+
+ /* Turn on dsi clocks */
+ DSI_W32(ctrl, DSI_CLK_CTRL, 0x23F);
+
+ /* Setup DSI control register */
+ reg_value = 0;
+ reg_value |= (cfg->en_crc_check ? BIT(24) : 0);
+ reg_value |= (cfg->en_ecc_check ? BIT(20) : 0);
+ reg_value |= BIT(8); /* Clock lane */
+ reg_value |= ((cfg->data_lanes & DSI_DATA_LANE_3) ? BIT(7) : 0);
+ reg_value |= ((cfg->data_lanes & DSI_DATA_LANE_2) ? BIT(6) : 0);
+ reg_value |= ((cfg->data_lanes & DSI_DATA_LANE_1) ? BIT(5) : 0);
+ reg_value |= ((cfg->data_lanes & DSI_DATA_LANE_0) ? BIT(4) : 0);
+
+ DSI_W32(ctrl, DSI_CTRL, reg_value);
+
+ pr_debug("[DSI_%d]Host configuration complete\n", ctrl->index);
+}
+
+/**
+ * phy_sw_reset() - perform a soft reset on the PHY.
+ * @ctrl: Pointer to the controller host hardware.
+ */
+void dsi_ctrl_hw_cmn_phy_sw_reset(struct dsi_ctrl_hw *ctrl)
+{
+ DSI_W32(ctrl, DSI_PHY_SW_RESET, 0x1);
+ udelay(1000);
+ DSI_W32(ctrl, DSI_PHY_SW_RESET, 0x0);
+ udelay(100);
+
+ pr_debug("[DSI_%d] phy sw reset done\n", ctrl->index);
+}
+
+/**
+ * soft_reset() - perform a soft reset on DSI controller
+ * @ctrl: Pointer to the controller host hardware.
+ *
+ * The video, command and controller engines will be disable before the
+ * reset is triggered. These engines will not be enabled after the reset
+ * is complete. Caller must re-enable the engines.
+ *
+ * If the reset is done while MDP timing engine is turned on, the video
+ * enigne should be re-enabled only during the vertical blanking time.
+ */
+void dsi_ctrl_hw_cmn_soft_reset(struct dsi_ctrl_hw *ctrl)
+{
+ u32 reg = 0;
+ u32 reg_ctrl = 0;
+
+ /* Clear DSI_EN, VIDEO_MODE_EN, CMD_MODE_EN */
+ reg_ctrl = DSI_R32(ctrl, DSI_CTRL);
+ DSI_W32(ctrl, DSI_CTRL, reg_ctrl & ~0x7);
+
+ /* Force enable PCLK, BYTECLK, AHBM_HCLK */
+ reg = DSI_R32(ctrl, DSI_CLK_CTRL);
+ reg |= 0x23F;
+ DSI_W32(ctrl, DSI_CLK_CTRL, reg);
+
+ /* Trigger soft reset */
+ DSI_W32(ctrl, DSI_SOFT_RESET, 0x1);
+ udelay(1);
+ DSI_W32(ctrl, DSI_SOFT_RESET, 0x0);
+
+ /* Disable force clock on */
+ reg &= ~(BIT(20) | BIT(11));
+ DSI_W32(ctrl, DSI_CLK_CTRL, reg);
+
+ /* Re-enable DSI controller */
+ DSI_W32(ctrl, DSI_CTRL, reg_ctrl);
+ pr_debug("[DSI_%d] ctrl soft reset done\n", ctrl->index);
+}
+
+/**
+ * set_video_timing() - set up the timing for video frame
+ * @ctrl: Pointer to controller host hardware.
+ * @mode: Video mode information.
+ *
+ * Set up the video timing parameters for the DSI video mode operation.
+ */
+void dsi_ctrl_hw_cmn_set_video_timing(struct dsi_ctrl_hw *ctrl,
+ struct dsi_mode_info *mode)
+{
+ u32 reg = 0;
+ u32 hs_start = 0;
+ u32 hs_end, active_h_start, active_h_end, h_total;
+ u32 vs_start = 0, vs_end = 0;
+ u32 vpos_start = 0, vpos_end, active_v_start, active_v_end, v_total;
+
+ hs_end = mode->h_sync_width;
+ active_h_start = mode->h_sync_width + mode->h_back_porch;
+ active_h_end = active_h_start + mode->h_active;
+ h_total = (mode->h_sync_width + mode->h_back_porch + mode->h_active +
+ mode->h_front_porch) - 1;
+
+ vpos_end = mode->v_sync_width;
+ active_v_start = mode->v_sync_width + mode->v_back_porch;
+ active_v_end = active_v_start + mode->v_active;
+ v_total = (mode->v_sync_width + mode->v_back_porch + mode->v_active +
+ mode->v_front_porch) - 1;
+
+ reg = ((active_h_end & 0xFFFF) << 16) | (active_h_start & 0xFFFF);
+ DSI_W32(ctrl, DSI_VIDEO_MODE_ACTIVE_H, reg);
+
+ reg = ((active_v_end & 0xFFFF) << 16) | (active_v_start & 0xFFFF);
+ DSI_W32(ctrl, DSI_VIDEO_MODE_ACTIVE_V, reg);
+
+ reg = ((v_total & 0xFFFF) << 16) | (h_total & 0xFFFF);
+ DSI_W32(ctrl, DSI_VIDEO_MODE_TOTAL, reg);
+
+ reg = ((hs_end & 0xFFFF) << 16) | (hs_start & 0xFFFF);
+ DSI_W32(ctrl, DSI_VIDEO_MODE_HSYNC, reg);
+
+ reg = ((vs_end & 0xFFFF) << 16) | (vs_start & 0xFFFF);
+ DSI_W32(ctrl, DSI_VIDEO_MODE_VSYNC, reg);
+
+ reg = ((vpos_end & 0xFFFF) << 16) | (vpos_start & 0xFFFF);
+ DSI_W32(ctrl, DSI_VIDEO_MODE_VSYNC_VPOS, reg);
+
+ /* TODO: HS TIMER value? */
+ DSI_W32(ctrl, DSI_HS_TIMER_CTRL, 0x3FD08);
+ DSI_W32(ctrl, DSI_MISR_VIDEO_CTRL, 0x10100);
+ DSI_W32(ctrl, DSI_DSI_TIMING_FLUSH, 0x1);
+ pr_debug("[DSI_%d] ctrl video parameters updated\n", ctrl->index);
+}
+
+/**
+ * setup_cmd_stream() - set up parameters for command pixel streams
+ * @ctrl: Pointer to controller host hardware.
+ * @width_in_pixels: Width of the stream in pixels.
+ * @h_stride: Horizontal stride in bytes.
+ * @height_inLines: Number of lines in the stream.
+ * @vc_id: stream_id
+ *
+ * Setup parameters for command mode pixel stream size.
+ */
+void dsi_ctrl_hw_cmn_setup_cmd_stream(struct dsi_ctrl_hw *ctrl,
+ u32 width_in_pixels,
+ u32 h_stride,
+ u32 height_in_lines,
+ u32 vc_id)
+{
+ u32 reg = 0;
+
+ reg = (h_stride + 1) << 16;
+ reg |= (vc_id & 0x3) << 8;
+ reg |= 0x39; /* packet data type */
+ DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM0_CTRL, reg);
+ DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM1_CTRL, reg);
+
+ reg = (height_in_lines << 16) | width_in_pixels;
+ DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM0_TOTAL, reg);
+ DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM1_TOTAL, reg);
+}
+
+/**
+ * video_engine_setup() - Setup dsi host controller for video mode
+ * @ctrl: Pointer to controller host hardware.
+ * @common_cfg: Common configuration parameters.
+ * @cfg: Video mode configuration.
+ *
+ * Set up DSI video engine with a specific configuration. Controller and
+ * video engine are not enabled as part of this function.
+ */
+void dsi_ctrl_hw_cmn_video_engine_setup(struct dsi_ctrl_hw *ctrl,
+ struct dsi_host_common_cfg *common_cfg,
+ struct dsi_video_engine_cfg *cfg)
+{
+ u32 reg = 0;
+
+ reg |= (cfg->last_line_interleave_en ? BIT(31) : 0);
+ reg |= (cfg->pulse_mode_hsa_he ? BIT(28) : 0);
+ reg |= (cfg->hfp_lp11_en ? BIT(24) : 0);
+ reg |= (cfg->hbp_lp11_en ? BIT(20) : 0);
+ reg |= (cfg->hsa_lp11_en ? BIT(16) : 0);
+ reg |= (cfg->eof_bllp_lp11_en ? BIT(15) : 0);
+ reg |= (cfg->bllp_lp11_en ? BIT(12) : 0);
+ reg |= (cfg->traffic_mode & 0x3) << 8;
+ reg |= (cfg->vc_id & 0x3);
+ reg |= (video_mode_format_map[common_cfg->dst_format] & 0x3) << 4;
+ DSI_W32(ctrl, DSI_VIDEO_MODE_CTRL, reg);
+
+ reg = (common_cfg->swap_mode & 0x7) << 12;
+ reg |= (common_cfg->bit_swap_red ? BIT(0) : 0);
+ reg |= (common_cfg->bit_swap_green ? BIT(4) : 0);
+ reg |= (common_cfg->bit_swap_blue ? BIT(8) : 0);
+ DSI_W32(ctrl, DSI_VIDEO_MODE_DATA_CTRL, reg);
+ /* Enable Timing double buffering */
+ DSI_W32(ctrl, DSI_DSI_TIMING_DB_MODE, 0x1);
+
+
+ pr_debug("[DSI_%d] Video engine setup done\n", ctrl->index);
+}
+
+/**
+ * cmd_engine_setup() - setup dsi host controller for command mode
+ * @ctrl: Pointer to the controller host hardware.
+ * @common_cfg: Common configuration parameters.
+ * @cfg: Command mode configuration.
+ *
+ * Setup DSI CMD engine with a specific configuration. Controller and
+ * command engine are not enabled as part of this function.
+ */
+void dsi_ctrl_hw_cmn_cmd_engine_setup(struct dsi_ctrl_hw *ctrl,
+ struct dsi_host_common_cfg *common_cfg,
+ struct dsi_cmd_engine_cfg *cfg)
+{
+ u32 reg = 0;
+
+ reg = (cfg->max_cmd_packets_interleave & 0xF) << 20;
+ reg |= (common_cfg->bit_swap_red ? BIT(4) : 0);
+ reg |= (common_cfg->bit_swap_green ? BIT(8) : 0);
+ reg |= (common_cfg->bit_swap_blue ? BIT(12) : 0);
+ reg |= cmd_mode_format_map[common_cfg->dst_format];
+ DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_CTRL, reg);
+
+ reg = DSI_R32(ctrl, DSI_COMMAND_MODE_MDP_CTRL2);
+ reg |= BIT(16);
+ DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_CTRL2, reg);
+
+ reg = cfg->wr_mem_start & 0xFF;
+ reg |= (cfg->wr_mem_continue & 0xFF) << 8;
+ reg |= (cfg->insert_dcs_command ? BIT(16) : 0);
+ DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL, reg);
+
+ pr_debug("[DSI_%d] Cmd engine setup done\n", ctrl->index);
+}
+
+/**
+ * video_engine_en() - enable DSI video engine
+ * @ctrl: Pointer to controller host hardware.
+ * @on: Enable/disabel video engine.
+ */
+void dsi_ctrl_hw_cmn_video_engine_en(struct dsi_ctrl_hw *ctrl, bool on)
+{
+ u32 reg = 0;
+
+ /* Set/Clear VIDEO_MODE_EN bit */
+ reg = DSI_R32(ctrl, DSI_CTRL);
+ if (on)
+ reg |= BIT(1);
+ else
+ reg &= ~BIT(1);
+
+ DSI_W32(ctrl, DSI_CTRL, reg);
+
+ pr_debug("[DSI_%d] Video engine = %d\n", ctrl->index, on);
+}
+
+/**
+ * ctrl_en() - enable DSI controller engine
+ * @ctrl: Pointer to the controller host hardware.
+ * @on: turn on/off the DSI controller engine.
+ */
+void dsi_ctrl_hw_cmn_ctrl_en(struct dsi_ctrl_hw *ctrl, bool on)
+{
+ u32 reg = 0;
+
+ /* Set/Clear DSI_EN bit */
+ reg = DSI_R32(ctrl, DSI_CTRL);
+ if (on)
+ reg |= BIT(0);
+ else
+ reg &= ~BIT(0);
+
+ DSI_W32(ctrl, DSI_CTRL, reg);
+
+ pr_debug("[DSI_%d] Controller engine = %d\n", ctrl->index, on);
+}
+
+/**
+ * cmd_engine_en() - enable DSI controller command engine
+ * @ctrl: Pointer to the controller host hardware.
+ * @on: Turn on/off the DSI command engine.
+ */
+void dsi_ctrl_hw_cmn_cmd_engine_en(struct dsi_ctrl_hw *ctrl, bool on)
+{
+ u32 reg = 0;
+
+ /* Set/Clear CMD_MODE_EN bit */
+ reg = DSI_R32(ctrl, DSI_CTRL);
+ if (on)
+ reg |= BIT(2);
+ else
+ reg &= ~BIT(2);
+
+ DSI_W32(ctrl, DSI_CTRL, reg);
+
+ pr_debug("[DSI_%d] command engine = %d\n", ctrl->index, on);
+}
+
+/**
+ * kickoff_command() - transmits commands stored in memory
+ * @ctrl: Pointer to the controller host hardware.
+ * @cmd: Command information.
+ * @flags: Modifiers for command transmission.
+ *
+ * The controller hardware is programmed with address and size of the
+ * command buffer. The transmission is kicked off if
+ * DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER flag is not set. If this flag is
+ * set, caller should make a separate call to trigger_command_dma() to
+ * transmit the command.
+ */
+void dsi_ctrl_hw_cmn_kickoff_command(struct dsi_ctrl_hw *ctrl,
+ struct dsi_ctrl_cmd_dma_info *cmd,
+ u32 flags)
+{
+ u32 reg = 0;
+
+ /*Set BROADCAST_EN and EMBEDDED_MODE */
+ reg = DSI_R32(ctrl, DSI_COMMAND_MODE_DMA_CTRL);
+ if (cmd->en_broadcast)
+ reg |= BIT(31);
+ else
+ reg &= ~BIT(31);
+
+ if (cmd->is_master)
+ reg |= BIT(30);
+ else
+ reg &= ~BIT(30);
+
+ if (cmd->use_lpm)
+ reg |= BIT(26);
+ else
+ reg &= ~BIT(26);
+
+ reg |= BIT(28);
+ DSI_W32(ctrl, DSI_COMMAND_MODE_DMA_CTRL, reg);
+
+ DSI_W32(ctrl, DSI_DMA_CMD_OFFSET, cmd->offset);
+ DSI_W32(ctrl, DSI_DMA_CMD_LENGTH, (cmd->length & 0xFFFFFF));
+
+ /* wait for writes to complete before kick off */
+ wmb();
+
+ if (!(flags & DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER))
+ DSI_W32(ctrl, DSI_CMD_MODE_DMA_SW_TRIGGER, 0x1);
+}
+
+/**
+ * kickoff_fifo_command() - transmits a command using FIFO in dsi
+ * hardware.
+ * @ctrl: Pointer to the controller host hardware.
+ * @cmd: Command information.
+ * @flags: Modifiers for command transmission.
+ *
+ * The controller hardware FIFO is programmed with command header and
+ * payload. The transmission is kicked off if
+ * DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER flag is not set. If this flag is
+ * set, caller should make a separate call to trigger_command_dma() to
+ * transmit the command.
+ */
+void dsi_ctrl_hw_cmn_kickoff_fifo_command(struct dsi_ctrl_hw *ctrl,
+ struct dsi_ctrl_cmd_dma_fifo_info *cmd,
+ u32 flags)
+{
+ u32 reg = 0, i = 0;
+ u32 *ptr = cmd->command;
+ /*
+ * Set CMD_DMA_TPG_EN, TPG_DMA_FIFO_MODE and
+ * CMD_DMA_PATTERN_SEL = custom pattern stored in TPG DMA FIFO
+ */
+ reg = (BIT(1) | BIT(2) | (0x3 << 16));
+ DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CTRL, reg);
+
+ /*
+ * Program the FIFO with command buffer. Hardware requires an extra
+ * DWORD (set to zero) if the length of command buffer is odd DWORDS.
+ */
+ for (i = 0; i < cmd->size; i += 4) {
+ DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CMD_DMA_INIT_VAL, *ptr);
+ ptr++;
+ }
+
+ if ((cmd->size / 4) & 0x1)
+ DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CMD_DMA_INIT_VAL, 0);
+
+ /*Set BROADCAST_EN and EMBEDDED_MODE */
+ reg = DSI_R32(ctrl, DSI_COMMAND_MODE_DMA_CTRL);
+ if (cmd->en_broadcast)
+ reg |= BIT(31);
+ else
+ reg &= ~BIT(31);
+
+ if (cmd->is_master)
+ reg |= BIT(30);
+ else
+ reg &= ~BIT(30);
+
+ if (cmd->use_lpm)
+ reg |= BIT(26);
+ else
+ reg &= ~BIT(26);
+
+ reg |= BIT(28);
+
+ DSI_W32(ctrl, DSI_COMMAND_MODE_DMA_CTRL, reg);
+
+ DSI_W32(ctrl, DSI_DMA_CMD_LENGTH, (cmd->size & 0xFFFFFFFF));
+ /* Finish writes before command trigger */
+ wmb();
+
+ if (!(flags & DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER))
+ DSI_W32(ctrl, DSI_CMD_MODE_DMA_SW_TRIGGER, 0x1);
+
+ pr_debug("[DSI_%d]size=%d, trigger = %d\n",
+ ctrl->index, cmd->size,
+ (flags & DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER) ? false : true);
+}
+
+void dsi_ctrl_hw_cmn_reset_cmd_fifo(struct dsi_ctrl_hw *ctrl)
+{
+ /* disable cmd dma tpg */
+ DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CTRL, 0x0);
+
+ DSI_W32(ctrl, DSI_TPG_DMA_FIFO_RESET, 0x1);
+ udelay(1);
+ DSI_W32(ctrl, DSI_TPG_DMA_FIFO_RESET, 0x0);
+}
+
+/**
+ * trigger_command_dma() - trigger transmission of command buffer.
+ * @ctrl: Pointer to the controller host hardware.
+ *
+ * This trigger can be only used if there was a prior call to
+ * kickoff_command() of kickoff_fifo_command() with
+ * DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER flag.
+ */
+void dsi_ctrl_hw_cmn_trigger_command_dma(struct dsi_ctrl_hw *ctrl)
+{
+ DSI_W32(ctrl, DSI_CMD_MODE_DMA_SW_TRIGGER, 0x1);
+ pr_debug("[DSI_%d] CMD DMA triggered\n", ctrl->index);
+}
+
+/**
+ * get_cmd_read_data() - get data read from the peripheral
+ * @ctrl: Pointer to the controller host hardware.
+ * @rd_buf: Buffer where data will be read into.
+ * @total_read_len: Number of bytes to read.
+ *
+ * return: number of bytes read.
+ */
+u32 dsi_ctrl_hw_cmn_get_cmd_read_data(struct dsi_ctrl_hw *ctrl,
+ u8 *rd_buf,
+ u32 read_offset,
+ u32 total_read_len)
+{
+ u32 *lp, *temp, data;
+ int i, j = 0, cnt;
+ u32 read_cnt;
+ u32 rx_byte = 0;
+ u32 repeated_bytes = 0;
+ u8 reg[16] = {0};
+ u32 pkt_size = 0;
+ int buf_offset = read_offset;
+
+ lp = (u32 *)rd_buf;
+ temp = (u32 *)reg;
+ cnt = (rx_byte + 3) >> 2;
+
+ if (cnt > 4)
+ cnt = 4;
+
+ if (rx_byte == 4)
+ read_cnt = 4;
+ else
+ read_cnt = pkt_size + 6;
+
+ if (read_cnt > 16) {
+ int bytes_shifted;
+
+ bytes_shifted = read_cnt - 16;
+ repeated_bytes = buf_offset - bytes_shifted;
+ }
+
+ for (i = cnt - 1; i >= 0; i--) {
+ data = DSI_R32(ctrl, DSI_RDBK_DATA0 + i*4);
+ *temp++ = ntohl(data);
+ }
+
+ for (i = repeated_bytes; i < 16; i++)
+ rd_buf[j++] = reg[i];
+
+ pr_debug("[DSI_%d] Read %d bytes\n", ctrl->index, j);
+ return j;
+}
+
+/**
+ * get_interrupt_status() - returns the interrupt status
+ * @ctrl: Pointer to the controller host hardware.
+ *
+ * Returns the ORed list of interrupts(enum dsi_status_int_type) that
+ * are active. This list does not include any error interrupts. Caller
+ * should call get_error_status for error interrupts.
+ *
+ * Return: List of active interrupts.
+ */
+u32 dsi_ctrl_hw_cmn_get_interrupt_status(struct dsi_ctrl_hw *ctrl)
+{
+ u32 reg = 0;
+ u32 ints = 0;
+
+ reg = DSI_R32(ctrl, DSI_INT_CTRL);
+
+ if (reg & BIT(0))
+ ints |= DSI_CMD_MODE_DMA_DONE;
+ if (reg & BIT(8))
+ ints |= DSI_CMD_FRAME_DONE;
+ if (reg & BIT(10))
+ ints |= DSI_CMD_STREAM0_FRAME_DONE;
+ if (reg & BIT(12))
+ ints |= DSI_CMD_STREAM1_FRAME_DONE;
+ if (reg & BIT(14))
+ ints |= DSI_CMD_STREAM2_FRAME_DONE;
+ if (reg & BIT(16))
+ ints |= DSI_VIDEO_MODE_FRAME_DONE;
+ if (reg & BIT(20))
+ ints |= DSI_BTA_DONE;
+ if (reg & BIT(28))
+ ints |= DSI_DYN_REFRESH_DONE;
+ if (reg & BIT(30))
+ ints |= DSI_DESKEW_DONE;
+
+ pr_debug("[DSI_%d] Interrupt status = 0x%x, INT_CTRL=0x%x\n",
+ ctrl->index, ints, reg);
+ return ints;
+}
+
+/**
+ * clear_interrupt_status() - clears the specified interrupts
+ * @ctrl: Pointer to the controller host hardware.
+ * @ints: List of interrupts to be cleared.
+ */
+void dsi_ctrl_hw_cmn_clear_interrupt_status(struct dsi_ctrl_hw *ctrl, u32 ints)
+{
+ u32 reg = 0;
+
+ if (ints & DSI_CMD_MODE_DMA_DONE)
+ reg |= BIT(0);
+ if (ints & DSI_CMD_FRAME_DONE)
+ reg |= BIT(8);
+ if (ints & DSI_CMD_STREAM0_FRAME_DONE)
+ reg |= BIT(10);
+ if (ints & DSI_CMD_STREAM1_FRAME_DONE)
+ reg |= BIT(12);
+ if (ints & DSI_CMD_STREAM2_FRAME_DONE)
+ reg |= BIT(14);
+ if (ints & DSI_VIDEO_MODE_FRAME_DONE)
+ reg |= BIT(16);
+ if (ints & DSI_BTA_DONE)
+ reg |= BIT(20);
+ if (ints & DSI_DYN_REFRESH_DONE)
+ reg |= BIT(28);
+ if (ints & DSI_DESKEW_DONE)
+ reg |= BIT(30);
+
+ DSI_W32(ctrl, DSI_INT_CTRL, reg);
+
+ pr_debug("[DSI_%d] Clear interrupts, ints = 0x%x, INT_CTRL=0x%x\n",
+ ctrl->index, ints, reg);
+}
+
+/**
+ * enable_status_interrupts() - enable the specified interrupts
+ * @ctrl: Pointer to the controller host hardware.
+ * @ints: List of interrupts to be enabled.
+ *
+ * Enables the specified interrupts. This list will override the
+ * previous interrupts enabled through this function. Caller has to
+ * maintain the state of the interrupts enabled. To disable all
+ * interrupts, set ints to 0.
+ */
+void dsi_ctrl_hw_cmn_enable_status_interrupts(
+ struct dsi_ctrl_hw *ctrl, u32 ints)
+{
+ u32 reg = 0;
+
+ /* Do not change value of DSI_ERROR_MASK bit */
+ reg |= (DSI_R32(ctrl, DSI_INT_CTRL) & BIT(25));
+ if (ints & DSI_CMD_MODE_DMA_DONE)
+ reg |= BIT(1);
+ if (ints & DSI_CMD_FRAME_DONE)
+ reg |= BIT(9);
+ if (ints & DSI_CMD_STREAM0_FRAME_DONE)
+ reg |= BIT(11);
+ if (ints & DSI_CMD_STREAM1_FRAME_DONE)
+ reg |= BIT(13);
+ if (ints & DSI_CMD_STREAM2_FRAME_DONE)
+ reg |= BIT(15);
+ if (ints & DSI_VIDEO_MODE_FRAME_DONE)
+ reg |= BIT(17);
+ if (ints & DSI_BTA_DONE)
+ reg |= BIT(21);
+ if (ints & DSI_DYN_REFRESH_DONE)
+ reg |= BIT(29);
+ if (ints & DSI_DESKEW_DONE)
+ reg |= BIT(31);
+
+ DSI_W32(ctrl, DSI_INT_CTRL, reg);
+
+ pr_debug("[DSI_%d] Enable interrupts 0x%x, INT_CTRL=0x%x\n",
+ ctrl->index, ints, reg);
+}
+
+/**
+ * get_error_status() - returns the error status
+ * @ctrl: Pointer to the controller host hardware.
+ *
+ * Returns the ORed list of errors(enum dsi_error_int_type) that are
+ * active. This list does not include any status interrupts. Caller
+ * should call get_interrupt_status for status interrupts.
+ *
+ * Return: List of active error interrupts.
+ */
+u64 dsi_ctrl_hw_cmn_get_error_status(struct dsi_ctrl_hw *ctrl)
+{
+ u32 dln0_phy_err;
+ u32 fifo_status;
+ u32 ack_error;
+ u32 timeout_errors;
+ u32 clk_error;
+ u32 dsi_status;
+ u64 errors = 0;
+
+ dln0_phy_err = DSI_R32(ctrl, DSI_DLN0_PHY_ERR);
+ if (dln0_phy_err & BIT(0))
+ errors |= DSI_DLN0_ESC_ENTRY_ERR;
+ if (dln0_phy_err & BIT(4))
+ errors |= DSI_DLN0_ESC_SYNC_ERR;
+ if (dln0_phy_err & BIT(8))
+ errors |= DSI_DLN0_LP_CONTROL_ERR;
+ if (dln0_phy_err & BIT(12))
+ errors |= DSI_DLN0_LP0_CONTENTION;
+ if (dln0_phy_err & BIT(16))
+ errors |= DSI_DLN0_LP1_CONTENTION;
+
+ fifo_status = DSI_R32(ctrl, DSI_FIFO_STATUS);
+ if (fifo_status & BIT(7))
+ errors |= DSI_CMD_MDP_FIFO_UNDERFLOW;
+ if (fifo_status & BIT(10))
+ errors |= DSI_CMD_DMA_FIFO_UNDERFLOW;
+ if (fifo_status & BIT(18))
+ errors |= DSI_DLN0_HS_FIFO_OVERFLOW;
+ if (fifo_status & BIT(19))
+ errors |= DSI_DLN0_HS_FIFO_UNDERFLOW;
+ if (fifo_status & BIT(22))
+ errors |= DSI_DLN1_HS_FIFO_OVERFLOW;
+ if (fifo_status & BIT(23))
+ errors |= DSI_DLN1_HS_FIFO_UNDERFLOW;
+ if (fifo_status & BIT(26))
+ errors |= DSI_DLN2_HS_FIFO_OVERFLOW;
+ if (fifo_status & BIT(27))
+ errors |= DSI_DLN2_HS_FIFO_UNDERFLOW;
+ if (fifo_status & BIT(30))
+ errors |= DSI_DLN3_HS_FIFO_OVERFLOW;
+ if (fifo_status & BIT(31))
+ errors |= DSI_DLN3_HS_FIFO_UNDERFLOW;
+
+ ack_error = DSI_R32(ctrl, DSI_ACK_ERR_STATUS);
+ if (ack_error & BIT(16))
+ errors |= DSI_RDBK_SINGLE_ECC_ERR;
+ if (ack_error & BIT(17))
+ errors |= DSI_RDBK_MULTI_ECC_ERR;
+ if (ack_error & BIT(20))
+ errors |= DSI_RDBK_CRC_ERR;
+ if (ack_error & BIT(23))
+ errors |= DSI_RDBK_INCOMPLETE_PKT;
+ if (ack_error & BIT(24))
+ errors |= DSI_PERIPH_ERROR_PKT;
+
+ timeout_errors = DSI_R32(ctrl, DSI_TIMEOUT_STATUS);
+ if (timeout_errors & BIT(0))
+ errors |= DSI_HS_TX_TIMEOUT;
+ if (timeout_errors & BIT(4))
+ errors |= DSI_LP_RX_TIMEOUT;
+ if (timeout_errors & BIT(8))
+ errors |= DSI_BTA_TIMEOUT;
+
+ clk_error = DSI_R32(ctrl, DSI_CLK_STATUS);
+ if (clk_error & BIT(16))
+ errors |= DSI_PLL_UNLOCK;
+
+ dsi_status = DSI_R32(ctrl, DSI_STATUS);
+ if (dsi_status & BIT(31))
+ errors |= DSI_INTERLEAVE_OP_CONTENTION;
+
+ pr_debug("[DSI_%d] Error status = 0x%llx, phy=0x%x, fifo=0x%x",
+ ctrl->index, errors, dln0_phy_err, fifo_status);
+ pr_debug("[DSI_%d] ack=0x%x, timeout=0x%x, clk=0x%x, dsi=0x%x\n",
+ ctrl->index, ack_error, timeout_errors, clk_error, dsi_status);
+ return errors;
+}
+
+/**
+ * clear_error_status() - clears the specified errors
+ * @ctrl: Pointer to the controller host hardware.
+ * @errors: List of errors to be cleared.
+ */
+void dsi_ctrl_hw_cmn_clear_error_status(struct dsi_ctrl_hw *ctrl, u64 errors)
+{
+ u32 dln0_phy_err = 0;
+ u32 fifo_status = 0;
+ u32 ack_error = 0;
+ u32 timeout_error = 0;
+ u32 clk_error = 0;
+ u32 dsi_status = 0;
+ u32 int_ctrl = 0;
+
+ if (errors & DSI_RDBK_SINGLE_ECC_ERR)
+ ack_error |= BIT(16);
+ if (errors & DSI_RDBK_MULTI_ECC_ERR)
+ ack_error |= BIT(17);
+ if (errors & DSI_RDBK_CRC_ERR)
+ ack_error |= BIT(20);
+ if (errors & DSI_RDBK_INCOMPLETE_PKT)
+ ack_error |= BIT(23);
+ if (errors & DSI_PERIPH_ERROR_PKT)
+ ack_error |= BIT(24);
+
+ if (errors & DSI_LP_RX_TIMEOUT)
+ timeout_error |= BIT(4);
+ if (errors & DSI_HS_TX_TIMEOUT)
+ timeout_error |= BIT(0);
+ if (errors & DSI_BTA_TIMEOUT)
+ timeout_error |= BIT(8);
+
+ if (errors & DSI_PLL_UNLOCK)
+ clk_error |= BIT(16);
+
+ if (errors & DSI_DLN0_LP0_CONTENTION)
+ dln0_phy_err |= BIT(12);
+ if (errors & DSI_DLN0_LP1_CONTENTION)
+ dln0_phy_err |= BIT(16);
+ if (errors & DSI_DLN0_ESC_ENTRY_ERR)
+ dln0_phy_err |= BIT(0);
+ if (errors & DSI_DLN0_ESC_SYNC_ERR)
+ dln0_phy_err |= BIT(4);
+ if (errors & DSI_DLN0_LP_CONTROL_ERR)
+ dln0_phy_err |= BIT(8);
+
+ if (errors & DSI_CMD_DMA_FIFO_UNDERFLOW)
+ fifo_status |= BIT(10);
+ if (errors & DSI_CMD_MDP_FIFO_UNDERFLOW)
+ fifo_status |= BIT(7);
+ if (errors & DSI_DLN0_HS_FIFO_OVERFLOW)
+ fifo_status |= BIT(18);
+ if (errors & DSI_DLN1_HS_FIFO_OVERFLOW)
+ fifo_status |= BIT(22);
+ if (errors & DSI_DLN2_HS_FIFO_OVERFLOW)
+ fifo_status |= BIT(26);
+ if (errors & DSI_DLN3_HS_FIFO_OVERFLOW)
+ fifo_status |= BIT(30);
+ if (errors & DSI_DLN0_HS_FIFO_UNDERFLOW)
+ fifo_status |= BIT(19);
+ if (errors & DSI_DLN1_HS_FIFO_UNDERFLOW)
+ fifo_status |= BIT(23);
+ if (errors & DSI_DLN2_HS_FIFO_UNDERFLOW)
+ fifo_status |= BIT(27);
+ if (errors & DSI_DLN3_HS_FIFO_UNDERFLOW)
+ fifo_status |= BIT(31);
+
+ if (errors & DSI_INTERLEAVE_OP_CONTENTION)
+ dsi_status |= BIT(31);
+
+ DSI_W32(ctrl, DSI_DLN0_PHY_ERR, dln0_phy_err);
+ DSI_W32(ctrl, DSI_FIFO_STATUS, fifo_status);
+ DSI_W32(ctrl, DSI_ACK_ERR_STATUS, ack_error);
+ DSI_W32(ctrl, DSI_TIMEOUT_STATUS, timeout_error);
+ DSI_W32(ctrl, DSI_CLK_STATUS, clk_error);
+ DSI_W32(ctrl, DSI_STATUS, dsi_status);
+
+ int_ctrl = DSI_R32(ctrl, DSI_INT_CTRL);
+ int_ctrl |= BIT(24);
+ DSI_W32(ctrl, DSI_INT_CTRL, int_ctrl);
+ pr_debug("[DSI_%d] clear errors = 0x%llx, phy=0x%x, fifo=0x%x",
+ ctrl->index, errors, dln0_phy_err, fifo_status);
+ pr_debug("[DSI_%d] ack=0x%x, timeout=0x%x, clk=0x%x, dsi=0x%x\n",
+ ctrl->index, ack_error, timeout_error, clk_error, dsi_status);
+}
+
+/**
+ * enable_error_interrupts() - enable the specified interrupts
+ * @ctrl: Pointer to the controller host hardware.
+ * @errors: List of errors to be enabled.
+ *
+ * Enables the specified interrupts. This list will override the
+ * previous interrupts enabled through this function. Caller has to
+ * maintain the state of the interrupts enabled. To disable all
+ * interrupts, set errors to 0.
+ */
+void dsi_ctrl_hw_cmn_enable_error_interrupts(struct dsi_ctrl_hw *ctrl,
+ u64 errors)
+{
+ u32 int_ctrl = 0;
+ u32 int_mask0 = 0x7FFF3BFF;
+
+ int_ctrl = DSI_R32(ctrl, DSI_INT_CTRL);
+ if (errors)
+ int_ctrl |= BIT(25);
+ else
+ int_ctrl &= ~BIT(25);
+
+ if (errors & DSI_RDBK_SINGLE_ECC_ERR)
+ int_mask0 &= ~BIT(0);
+ if (errors & DSI_RDBK_MULTI_ECC_ERR)
+ int_mask0 &= ~BIT(1);
+ if (errors & DSI_RDBK_CRC_ERR)
+ int_mask0 &= ~BIT(2);
+ if (errors & DSI_RDBK_INCOMPLETE_PKT)
+ int_mask0 &= ~BIT(3);
+ if (errors & DSI_PERIPH_ERROR_PKT)
+ int_mask0 &= ~BIT(4);
+
+ if (errors & DSI_LP_RX_TIMEOUT)
+ int_mask0 &= ~BIT(5);
+ if (errors & DSI_HS_TX_TIMEOUT)
+ int_mask0 &= ~BIT(6);
+ if (errors & DSI_BTA_TIMEOUT)
+ int_mask0 &= ~BIT(7);
+
+ if (errors & DSI_PLL_UNLOCK)
+ int_mask0 &= ~BIT(28);
+
+ if (errors & DSI_DLN0_LP0_CONTENTION)
+ int_mask0 &= ~BIT(24);
+ if (errors & DSI_DLN0_LP1_CONTENTION)
+ int_mask0 &= ~BIT(25);
+ if (errors & DSI_DLN0_ESC_ENTRY_ERR)
+ int_mask0 &= ~BIT(21);
+ if (errors & DSI_DLN0_ESC_SYNC_ERR)
+ int_mask0 &= ~BIT(22);
+ if (errors & DSI_DLN0_LP_CONTROL_ERR)
+ int_mask0 &= ~BIT(23);
+
+ if (errors & DSI_CMD_DMA_FIFO_UNDERFLOW)
+ int_mask0 &= ~BIT(9);
+ if (errors & DSI_CMD_MDP_FIFO_UNDERFLOW)
+ int_mask0 &= ~BIT(11);
+ if (errors & DSI_DLN0_HS_FIFO_OVERFLOW)
+ int_mask0 &= ~BIT(16);
+ if (errors & DSI_DLN1_HS_FIFO_OVERFLOW)
+ int_mask0 &= ~BIT(17);
+ if (errors & DSI_DLN2_HS_FIFO_OVERFLOW)
+ int_mask0 &= ~BIT(18);
+ if (errors & DSI_DLN3_HS_FIFO_OVERFLOW)
+ int_mask0 &= ~BIT(19);
+ if (errors & DSI_DLN0_HS_FIFO_UNDERFLOW)
+ int_mask0 &= ~BIT(26);
+ if (errors & DSI_DLN1_HS_FIFO_UNDERFLOW)
+ int_mask0 &= ~BIT(27);
+ if (errors & DSI_DLN2_HS_FIFO_UNDERFLOW)
+ int_mask0 &= ~BIT(29);
+ if (errors & DSI_DLN3_HS_FIFO_UNDERFLOW)
+ int_mask0 &= ~BIT(30);
+
+ if (errors & DSI_INTERLEAVE_OP_CONTENTION)
+ int_mask0 &= ~BIT(8);
+
+ DSI_W32(ctrl, DSI_INT_CTRL, int_ctrl);
+ DSI_W32(ctrl, DSI_ERR_INT_MASK0, int_mask0);
+
+ pr_debug("[DSI_%d] enable errors = 0x%llx, int_mask0=0x%x\n",
+ ctrl->index, errors, int_mask0);
+}
+
+/**
+ * video_test_pattern_setup() - setup test pattern engine for video mode
+ * @ctrl: Pointer to the controller host hardware.
+ * @type: Type of test pattern.
+ * @init_val: Initial value to use for generating test pattern.
+ */
+void dsi_ctrl_hw_cmn_video_test_pattern_setup(struct dsi_ctrl_hw *ctrl,
+ enum dsi_test_pattern type,
+ u32 init_val)
+{
+ u32 reg = 0;
+
+ DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_VIDEO_INIT_VAL, init_val);
+
+ switch (type) {
+ case DSI_TEST_PATTERN_FIXED:
+ reg |= (0x2 << 4);
+ break;
+ case DSI_TEST_PATTERN_INC:
+ reg |= (0x1 << 4);
+ break;
+ case DSI_TEST_PATTERN_POLY:
+ DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_VIDEO_POLY, 0xF0F0F);
+ break;
+ default:
+ break;
+ }
+
+ DSI_W32(ctrl, DSI_TPG_MAIN_CONTROL, 0x100);
+ DSI_W32(ctrl, DSI_TPG_VIDEO_CONFIG, 0x5);
+ DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CTRL, reg);
+
+ pr_debug("[DSI_%d] Video test pattern setup done\n", ctrl->index);
+}
+
+/**
+ * cmd_test_pattern_setup() - setup test patttern engine for cmd mode
+ * @ctrl: Pointer to the controller host hardware.
+ * @type: Type of test pattern.
+ * @init_val: Initial value to use for generating test pattern.
+ * @stream_id: Stream Id on which packets are generated.
+ */
+void dsi_ctrl_hw_cmn_cmd_test_pattern_setup(struct dsi_ctrl_hw *ctrl,
+ enum dsi_test_pattern type,
+ u32 init_val,
+ u32 stream_id)
+{
+ u32 reg = 0;
+ u32 init_offset;
+ u32 poly_offset;
+ u32 pattern_sel_shift;
+
+ switch (stream_id) {
+ case 0:
+ init_offset = DSI_TEST_PATTERN_GEN_CMD_MDP_INIT_VAL0;
+ poly_offset = DSI_TEST_PATTERN_GEN_CMD_MDP_STREAM0_POLY;
+ pattern_sel_shift = 8;
+ break;
+ case 1:
+ init_offset = DSI_TEST_PATTERN_GEN_CMD_MDP_INIT_VAL1;
+ poly_offset = DSI_TEST_PATTERN_GEN_CMD_MDP_STREAM1_POLY;
+ pattern_sel_shift = 12;
+ break;
+ case 2:
+ init_offset = DSI_TEST_PATTERN_GEN_CMD_MDP_INIT_VAL2;
+ poly_offset = DSI_TEST_PATTERN_GEN_CMD_MDP_STREAM2_POLY;
+ pattern_sel_shift = 20;
+ break;
+ default:
+ return;
+ }
+
+ DSI_W32(ctrl, init_offset, init_val);
+
+ switch (type) {
+ case DSI_TEST_PATTERN_FIXED:
+ reg |= (0x2 << pattern_sel_shift);
+ break;
+ case DSI_TEST_PATTERN_INC:
+ reg |= (0x1 << pattern_sel_shift);
+ break;
+ case DSI_TEST_PATTERN_POLY:
+ DSI_W32(ctrl, poly_offset, 0xF0F0F);
+ break;
+ default:
+ break;
+ }
+
+ DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CTRL, reg);
+ pr_debug("[DSI_%d] Cmd test pattern setup done\n", ctrl->index);
+}
+
+/**
+ * test_pattern_enable() - enable test pattern engine
+ * @ctrl: Pointer to the controller host hardware.
+ * @enable: Enable/Disable test pattern engine.
+ */
+void dsi_ctrl_hw_cmn_test_pattern_enable(struct dsi_ctrl_hw *ctrl,
+ bool enable)
+{
+ u32 reg = DSI_R32(ctrl, DSI_TEST_PATTERN_GEN_CTRL);
+
+ if (enable)
+ reg |= BIT(0);
+ else
+ reg &= ~BIT(0);
+
+ DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CTRL, reg);
+
+ pr_debug("[DSI_%d] Test pattern enable=%d\n", ctrl->index, enable);
+}
+
+/**
+ * trigger_cmd_test_pattern() - trigger a command mode frame update with
+ * test pattern
+ * @ctrl: Pointer to the controller host hardware.
+ * @stream_id: Stream on which frame update is sent.
+ */
+void dsi_ctrl_hw_cmn_trigger_cmd_test_pattern(struct dsi_ctrl_hw *ctrl,
+ u32 stream_id)
+{
+ switch (stream_id) {
+ case 0:
+ DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CMD_STREAM0_TRIGGER, 0x1);
+ break;
+ case 1:
+ DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CMD_STREAM1_TRIGGER, 0x1);
+ break;
+ case 2:
+ DSI_W32(ctrl, DSI_TEST_PATTERN_GEN_CMD_STREAM2_TRIGGER, 0x1);
+ break;
+ default:
+ break;
+ }
+
+ pr_debug("[DSI_%d] Cmd Test pattern trigger\n", ctrl->index);
+}
+
+void dsi_ctrl_hw_dln0_phy_err(struct dsi_ctrl_hw *ctrl)
+{
+ u32 status = 0;
+ /*
+ * Clear out any phy errors prior to exiting ULPS
+ * This fixes certain instances where phy does not exit
+ * ULPS cleanly. Also, do not print error during such cases.
+ */
+ status = DSI_R32(ctrl, DSI_DLN0_PHY_ERR);
+ if (status & 0x011111) {
+ DSI_W32(ctrl, DSI_DLN0_PHY_ERR, status);
+ pr_err("%s: phy_err_status = %x\n", __func__, status);
+ }
+}
+
+void dsi_ctrl_hw_cmn_phy_reset_config(struct dsi_ctrl_hw *ctrl,
+ bool enable)
+{
+ u32 reg = 0;
+
+ reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
+
+ /* Mask/unmask disable PHY reset bit */
+ if (enable)
+ reg |= BIT(30);
+ else
+ reg &= ~BIT(30);
+
+ DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
+}
+
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_reg_1_4.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_reg.h
similarity index 98%
rename from drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_reg_1_4.h
rename to drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_reg.h
index 028ad46..34e9e72 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_reg_1_4.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_reg.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -187,6 +187,7 @@
#define DSI_SECURE_DISPLAY_STATUS (0x02CC)
#define DSI_SECURE_DISPLAY_BLOCK_COMMAND_COLOR (0x02D0)
#define DSI_SECURE_DISPLAY_BLOCK_VIDEO_COLOR (0x02D4)
+#define DSI_LOGICAL_LANE_SWAP_CTRL (0x0310)
#endif /* _DSI_CTRL_REG_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
index 50a0733..fd64d6f 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -70,22 +70,6 @@
};
/**
- * enum dsi_data_lanes - dsi physical lanes
- * @DSI_DATA_LANE_0: Physical lane 0
- * @DSI_DATA_LANE_1: Physical lane 1
- * @DSI_DATA_LANE_2: Physical lane 2
- * @DSI_DATA_LANE_3: Physical lane 3
- * @DSI_CLOCK_LANE: Physical clock lane
- */
-enum dsi_data_lanes {
- DSI_DATA_LANE_0 = BIT(0),
- DSI_DATA_LANE_1 = BIT(1),
- DSI_DATA_LANE_2 = BIT(2),
- DSI_DATA_LANE_3 = BIT(3),
- DSI_CLOCK_LANE = BIT(4)
-};
-
-/**
* enum dsi_logical_lane - dsi logical lanes
* @DSI_LOGICAL_LANE_0: Logical lane 0
* @DSI_LOGICAL_LANE_1: Logical lane 1
@@ -104,6 +88,63 @@
};
/**
+ * enum dsi_data_lanes - BIT map for DSI data lanes
+ * This is used to identify the active DSI data lanes for
+ * various operations like DSI data lane enable/ULPS/clamp
+ * configurations.
+ * @DSI_DATA_LANE_0: BIT(DSI_LOGICAL_LANE_0)
+ * @DSI_DATA_LANE_1: BIT(DSI_LOGICAL_LANE_1)
+ * @DSI_DATA_LANE_2: BIT(DSI_LOGICAL_LANE_2)
+ * @DSI_DATA_LANE_3: BIT(DSI_LOGICAL_LANE_3)
+ * @DSI_CLOCK_LANE: BIT(DSI_LOGICAL_CLOCK_LANE)
+ */
+enum dsi_data_lanes {
+ DSI_DATA_LANE_0 = BIT(DSI_LOGICAL_LANE_0),
+ DSI_DATA_LANE_1 = BIT(DSI_LOGICAL_LANE_1),
+ DSI_DATA_LANE_2 = BIT(DSI_LOGICAL_LANE_2),
+ DSI_DATA_LANE_3 = BIT(DSI_LOGICAL_LANE_3),
+ DSI_CLOCK_LANE = BIT(DSI_LOGICAL_CLOCK_LANE)
+};
+
+/**
+ * enum dsi_phy_data_lanes - dsi physical lanes
+ * used for DSI logical to physical lane mapping
+ * @DSI_PHYSICAL_LANE_INVALID: Physical lane valid/invalid
+ * @DSI_PHYSICAL_LANE_0: Physical lane 0
+ * @DSI_PHYSICAL_LANE_1: Physical lane 1
+ * @DSI_PHYSICAL_LANE_2: Physical lane 2
+ * @DSI_PHYSICAL_LANE_3: Physical lane 3
+ */
+enum dsi_phy_data_lanes {
+ DSI_PHYSICAL_LANE_INVALID = 0,
+ DSI_PHYSICAL_LANE_0 = BIT(0),
+ DSI_PHYSICAL_LANE_1 = BIT(1),
+ DSI_PHYSICAL_LANE_2 = BIT(2),
+ DSI_PHYSICAL_LANE_3 = BIT(3)
+};
+
+enum dsi_lane_map_type_v1 {
+ DSI_LANE_MAP_0123,
+ DSI_LANE_MAP_3012,
+ DSI_LANE_MAP_2301,
+ DSI_LANE_MAP_1230,
+ DSI_LANE_MAP_0321,
+ DSI_LANE_MAP_1032,
+ DSI_LANE_MAP_2103,
+ DSI_LANE_MAP_3210,
+};
+
+/**
+ * lane_map: DSI logical <-> physical lane mapping
+ * lane_map_v1: Lane mapping for DSI controllers < v2.0
+ * lane_map_v2: Lane mapping for DSI controllers >= 2.0
+ */
+struct dsi_lane_map {
+ enum dsi_lane_map_type_v1 lane_map_v1;
+ u8 lane_map_v2[DSI_LANE_MAX - 1];
+};
+
+/**
* enum dsi_trigger_type - dsi trigger type
* @DSI_TRIGGER_NONE: No trigger.
* @DSI_TRIGGER_TE: TE trigger.
@@ -224,20 +265,6 @@
};
/**
- * struct dsi_lane_mapping - Mapping between DSI logical and physical lanes
- * @physical_lane0: Logical lane to which physical lane 0 is mapped.
- * @physical_lane1: Logical lane to which physical lane 1 is mapped.
- * @physical_lane2: Logical lane to which physical lane 2 is mapped.
- * @physical_lane3: Logical lane to which physical lane 3 is mapped.
- */
-struct dsi_lane_mapping {
- enum dsi_logical_lane physical_lane0;
- enum dsi_logical_lane physical_lane1;
- enum dsi_logical_lane physical_lane2;
- enum dsi_logical_lane physical_lane3;
-};
-
-/**
* struct dsi_host_common_cfg - Host configuration common to video and cmd mode
* @dst_format: Destination pixel format.
* @data_lanes: Physical data lanes to be enabled.
@@ -247,6 +274,7 @@
* @mdp_cmd_trigger: MDP frame update trigger for command mode.
* @dma_cmd_trigger: Command DMA trigger.
* @cmd_trigger_stream: Command mode stream to trigger.
+ * @swap_mode: DSI color swap mode.
* @bit_swap_read: Is red color bit swapped.
* @bit_swap_green: Is green color bit swapped.
* @bit_swap_blue: Is blue color bit swapped.
@@ -281,7 +309,6 @@
/**
* struct dsi_video_engine_cfg - DSI video engine configuration
- * @host_cfg: Pointer to host common configuration.
* @last_line_interleave_en: Allow command mode op interleaved on last line of
* video stream.
* @pulse_mode_hsa_he: Send HSA and HE following VS/VE packet if set to
@@ -309,8 +336,6 @@
/**
* struct dsi_cmd_engine_cfg - DSI command engine configuration
- * @host_cfg: Pointer to host common configuration.
- * @host_cfg: Common host configuration
* @max_cmd_packets_interleave Maximum number of command mode RGB packets to
* send with in one horizontal blanking period
* of the video mode frame.
@@ -318,12 +343,15 @@
* @wr_mem_continue: DCS command for write_memory_continue.
* @insert_dcs_command: Insert DCS command as first byte of payload
* of the pixel data.
+ * @mdp_transfer_time_us Specifies the mdp transfer time for command mode
+ * panels in microseconds
*/
struct dsi_cmd_engine_cfg {
u32 max_cmd_packets_interleave;
u32 wr_mem_start;
u32 wr_mem_continue;
bool insert_dcs_command;
+ u32 mdp_transfer_time_us;
};
/**
@@ -336,7 +364,6 @@
* @bit_clk_rate_hz: Bit clock frequency in Hz.
* @video_timing: Video timing information of a frame.
* @lane_map: Mapping between logical and physical lanes.
- * @phy_type: PHY type to be used.
*/
struct dsi_host_config {
enum dsi_op_mode panel_mode;
@@ -348,7 +375,7 @@
u64 esc_clk_rate_hz;
u64 bit_clk_rate_hz;
struct dsi_mode_info video_timing;
- struct dsi_lane_mapping lane_map;
+ struct dsi_lane_map lane_map;
};
/**
@@ -356,14 +383,13 @@
* @timing: Timing parameters for the panel.
* @pixel_clk_khz: Pixel clock in Khz.
* @panel_mode: Panel operation mode.
- * @flags: Additional flags.
+ * @dsi_mode_flags: Flags to signal other drm components via private flags
*/
struct dsi_display_mode {
struct dsi_mode_info timing;
u32 pixel_clk_khz;
enum dsi_op_mode panel_mode;
-
- u32 flags;
+ u32 dsi_mode_flags;
};
#endif /* _DSI_DEFS_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index 5a166a4..f54852d 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -23,6 +23,8 @@
#include "dsi_ctrl.h"
#include "dsi_ctrl_hw.h"
#include "dsi_drm.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
#define to_dsi_display(x) container_of(x, struct dsi_display, host)
@@ -54,6 +56,30 @@
return rc;
}
+int dsi_display_soft_reset(void *display)
+{
+ struct dsi_display *dsi_display;
+ struct dsi_display_ctrl *ctrl;
+ int rc = 0;
+ int i;
+
+ if (!display)
+ return -EINVAL;
+
+ dsi_display = display;
+
+ for (i = 0 ; i < dsi_display->ctrl_count; i++) {
+ ctrl = &dsi_display->ctrl[i];
+ rc = dsi_ctrl_soft_reset(ctrl->ctrl);
+ if (rc) {
+ pr_err("[%s] failed to soft reset host_%d, rc=%d\n",
+ dsi_display->name, i, rc);
+ break;
+ }
+ }
+
+ return rc;
+}
static ssize_t debugfs_dump_info_read(struct file *file,
char __user *buff,
size_t count,
@@ -164,6 +190,286 @@
}
}
+static int dsi_display_is_ulps_req_valid(struct dsi_display *display,
+ bool enable)
+{
+ /* TODO: make checks based on cont. splash */
+ int splash_enabled = false;
+
+ pr_debug("checking ulps req validity\n");
+
+ if (!dsi_panel_ulps_feature_enabled(display->panel))
+ return false;
+
+ /* TODO: ULPS during suspend */
+ if (!dsi_panel_initialized(display->panel))
+ return false;
+
+ if (enable && display->ulps_enabled) {
+ pr_debug("ULPS already enabled\n");
+ return false;
+ } else if (!enable && !display->ulps_enabled) {
+ pr_debug("ULPS already disabled\n");
+ return false;
+ }
+
+ /*
+ * No need to enter ULPS when transitioning from splash screen to
+ * boot animation since it is expected that the clocks would be turned
+ * right back on.
+ */
+ if (enable && splash_enabled)
+ return false;
+
+ return true;
+}
+
+
+/**
+ * dsi_display_set_ulps() - set ULPS state for DSI lanes.
+ * @dsi_display: DSI display handle.
+ * @enable: enable/disable ULPS.
+ *
+ * ULPS can be enabled/disabled after DSI host engine is turned on.
+ *
+ * Return: error code.
+ */
+static int dsi_display_set_ulps(struct dsi_display *display, bool enable)
+{
+ int rc = 0;
+ int i = 0;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ if (!dsi_display_is_ulps_req_valid(display, enable)) {
+ pr_debug("%s: skipping ULPS config, enable=%d\n",
+ __func__, enable);
+ return 0;
+ }
+
+ m_ctrl = &display->ctrl[display->cmd_master_idx];
+
+ rc = dsi_ctrl_set_ulps(m_ctrl->ctrl, enable);
+ if (rc) {
+ pr_err("Ulps controller state change(%d) failed\n", enable);
+ return rc;
+ }
+
+ rc = dsi_phy_set_ulps(m_ctrl->phy, &display->config, enable);
+ if (rc) {
+ pr_err("Ulps PHY state change(%d) failed\n", enable);
+ return rc;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_ctrl_set_ulps(ctrl->ctrl, enable);
+ if (rc) {
+ pr_err("Ulps controller state change(%d) failed\n",
+ enable);
+ return rc;
+ }
+
+ rc = dsi_phy_set_ulps(ctrl->phy, &display->config, enable);
+ if (rc) {
+ pr_err("Ulps PHY state change(%d) failed\n", enable);
+ return rc;
+ }
+
+ }
+ display->ulps_enabled = enable;
+ return 0;
+}
+
+/**
+ * dsi_display_set_clamp() - set clamp state for DSI IO.
+ * @dsi_display: DSI display handle.
+ * @enable: enable/disable clamping.
+ *
+ * Return: error code.
+ */
+static int dsi_display_set_clamp(struct dsi_display *display, bool enable)
+{
+ int rc = 0;
+ int i = 0;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+ bool ulps_enabled = false;
+
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ m_ctrl = &display->ctrl[display->cmd_master_idx];
+ ulps_enabled = display->ulps_enabled;
+
+ rc = dsi_ctrl_set_clamp_state(m_ctrl->ctrl, enable, ulps_enabled);
+ if (rc) {
+ pr_err("DSI Clamp state change(%d) failed\n", enable);
+ return rc;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_ctrl_set_clamp_state(ctrl->ctrl, enable, ulps_enabled);
+ if (rc) {
+ pr_err("DSI Clamp state change(%d) failed\n", enable);
+ return rc;
+ }
+ }
+ display->clamp_enabled = enable;
+ return 0;
+}
+
+/**
+ * dsi_display_setup_ctrl() - setup DSI controller.
+ * @dsi_display: DSI display handle.
+ *
+ * Return: error code.
+ */
+static int dsi_display_ctrl_setup(struct dsi_display *display)
+{
+ int rc = 0;
+ int i = 0;
+ struct dsi_display_ctrl *ctrl, *m_ctrl;
+
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ m_ctrl = &display->ctrl[display->cmd_master_idx];
+ rc = dsi_ctrl_setup(m_ctrl->ctrl);
+ if (rc) {
+ pr_err("DSI controller setup failed\n");
+ return rc;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_ctrl_setup(ctrl->ctrl);
+ if (rc) {
+ pr_err("DSI controller setup failed\n");
+ return rc;
+ }
+ }
+ return 0;
+}
+
+static int dsi_display_phy_enable(struct dsi_display *display);
+
+/**
+ * dsi_display_phy_idle_on() - enable DSI PHY while coming out of idle screen.
+ * @dsi_display: DSI display handle.
+ * @enable: enable/disable DSI PHY.
+ *
+ * Return: error code.
+ */
+static int dsi_display_phy_idle_on(struct dsi_display *display,
+ bool mmss_clamp)
+{
+ int rc = 0;
+ int i = 0;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ if (mmss_clamp && !display->phy_idle_power_off) {
+ dsi_display_phy_enable(display);
+ return 0;
+ }
+
+ m_ctrl = &display->ctrl[display->cmd_master_idx];
+ rc = dsi_phy_idle_ctrl(m_ctrl->phy, true);
+ if (rc) {
+ pr_err("DSI controller setup failed\n");
+ return rc;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_phy_idle_ctrl(ctrl->phy, true);
+ if (rc) {
+ pr_err("DSI controller setup failed\n");
+ return rc;
+ }
+ }
+ display->phy_idle_power_off = false;
+ return 0;
+}
+
+/**
+ * dsi_display_phy_idle_off() - disable DSI PHY while going to idle screen.
+ * @dsi_display: DSI display handle.
+ * @enable: enable/disable DSI PHY.
+ *
+ * Return: error code.
+ */
+static int dsi_display_phy_idle_off(struct dsi_display *display)
+{
+ int rc = 0;
+ int i = 0;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ if (!display->panel->allow_phy_power_off) {
+ pr_debug("panel doesn't support this feature\n");
+ return 0;
+ }
+
+ m_ctrl = &display->ctrl[display->cmd_master_idx];
+
+ rc = dsi_phy_idle_ctrl(m_ctrl->phy, false);
+ if (rc) {
+ pr_err("[%s] failed to enable cmd engine, rc=%d\n",
+ display->name, rc);
+ return rc;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_phy_idle_ctrl(ctrl->phy, false);
+ if (rc) {
+ pr_err("DSI controller setup failed\n");
+ return rc;
+ }
+ }
+ display->phy_idle_power_off = true;
+ return 0;
+}
+
+
+
static int dsi_display_ctrl_power_on(struct dsi_display *display)
{
int rc = 0;
@@ -171,7 +477,6 @@
struct dsi_display_ctrl *ctrl;
/* Sequence does not matter for split dsi usecases */
-
for (i = 0; i < display->ctrl_count; i++) {
ctrl = &display->ctrl[i];
if (!ctrl->ctrl)
@@ -192,7 +497,8 @@
ctrl = &display->ctrl[i];
if (!ctrl->ctrl)
continue;
- (void)dsi_ctrl_set_power_state(ctrl->ctrl, DSI_CTRL_POWER_OFF);
+ (void)dsi_ctrl_set_power_state(ctrl->ctrl,
+ DSI_CTRL_POWER_VREG_OFF);
}
return rc;
}
@@ -204,13 +510,13 @@
struct dsi_display_ctrl *ctrl;
/* Sequence does not matter for split dsi usecases */
-
for (i = 0; i < display->ctrl_count; i++) {
ctrl = &display->ctrl[i];
if (!ctrl->ctrl)
continue;
- rc = dsi_ctrl_set_power_state(ctrl->ctrl, DSI_CTRL_POWER_OFF);
+ rc = dsi_ctrl_set_power_state(ctrl->ctrl,
+ DSI_CTRL_POWER_VREG_OFF);
if (rc) {
pr_err("[%s] Failed to power off, rc=%d\n",
ctrl->ctrl->name, rc);
@@ -228,7 +534,6 @@
struct dsi_display_ctrl *ctrl;
/* Sequence does not matter for split dsi usecases */
-
for (i = 0; i < display->ctrl_count; i++) {
ctrl = &display->ctrl[i];
if (!ctrl->ctrl)
@@ -260,7 +565,6 @@
struct dsi_display_ctrl *ctrl;
/* Sequence does not matter for split dsi usecases */
-
for (i = 0; i < display->ctrl_count; i++) {
ctrl = &display->ctrl[i];
if (!ctrl->phy)
@@ -277,7 +581,7 @@
return rc;
}
-static int dsi_display_ctrl_core_clk_on(struct dsi_display *display)
+static int dsi_display_set_clk_src(struct dsi_display *display)
{
int rc = 0;
int i;
@@ -288,64 +592,14 @@
* be enabled before the other controller. Master controller in the
* clock context refers to the controller that sources the clock.
*/
-
- m_ctrl = &display->ctrl[display->clk_master_idx];
-
- rc = dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_CORE_CLK_ON);
- if (rc) {
- pr_err("[%s] failed to turn on clocks, rc=%d\n",
- display->name, rc);
- goto error;
- }
-
- /* Turn on rest of the controllers */
- for (i = 0; i < display->ctrl_count; i++) {
- ctrl = &display->ctrl[i];
- if (!ctrl->ctrl || (ctrl == m_ctrl))
- continue;
-
- rc = dsi_ctrl_set_power_state(ctrl->ctrl,
- DSI_CTRL_POWER_CORE_CLK_ON);
- if (rc) {
- pr_err("[%s] failed to turn on clock, rc=%d\n",
- display->name, rc);
- goto error_disable_master;
- }
- }
- return rc;
-error_disable_master:
- (void)dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_VREG_ON);
-error:
- return rc;
-}
-
-static int dsi_display_ctrl_link_clk_on(struct dsi_display *display)
-{
- int rc = 0;
- int i;
- struct dsi_display_ctrl *m_ctrl, *ctrl;
-
- /*
- * In case of split DSI usecases, the clock for master controller should
- * be enabled before the other controller. Master controller in the
- * clock context refers to the controller that sources the clock.
- */
-
m_ctrl = &display->ctrl[display->clk_master_idx];
rc = dsi_ctrl_set_clock_source(m_ctrl->ctrl,
- &display->clock_info.src_clks);
+ &display->clock_info.src_clks);
if (rc) {
pr_err("[%s] failed to set source clocks for master, rc=%d\n",
- display->name, rc);
- goto error;
- }
-
- rc = dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_LINK_CLK_ON);
- if (rc) {
- pr_err("[%s] failed to turn on clocks, rc=%d\n",
- display->name, rc);
- goto error;
+ display->name, rc);
+ return rc;
}
/* Turn on rest of the controllers */
@@ -355,97 +609,33 @@
continue;
rc = dsi_ctrl_set_clock_source(ctrl->ctrl,
- &display->clock_info.src_clks);
+ &display->clock_info.src_clks);
if (rc) {
pr_err("[%s] failed to set source clocks, rc=%d\n",
- display->name, rc);
- goto error_disable_master;
- }
-
- rc = dsi_ctrl_set_power_state(ctrl->ctrl,
- DSI_CTRL_POWER_LINK_CLK_ON);
- if (rc) {
- pr_err("[%s] failed to turn on clock, rc=%d\n",
- display->name, rc);
- goto error_disable_master;
+ display->name, rc);
+ return rc;
}
}
- return rc;
-error_disable_master:
- (void)dsi_ctrl_set_power_state(m_ctrl->ctrl,
- DSI_CTRL_POWER_CORE_CLK_ON);
-error:
- return rc;
+ return 0;
}
-static int dsi_display_ctrl_core_clk_off(struct dsi_display *display)
+static int dsi_display_phy_reset_config(struct dsi_display *display,
+ bool enable)
{
int rc = 0;
int i;
- struct dsi_display_ctrl *m_ctrl, *ctrl;
+ struct dsi_display_ctrl *ctrl;
- /*
- * In case of split DSI usecases, clock for slave DSI controllers should
- * be disabled first before disabling clock for master controller. Slave
- * controllers in the clock context refer to controller which source
- * clock from another controller.
- */
-
- m_ctrl = &display->ctrl[display->clk_master_idx];
-
- for (i = 0; i < display->ctrl_count; i++) {
+ for (i = 0 ; i < display->ctrl_count; i++) {
ctrl = &display->ctrl[i];
- if (!ctrl->ctrl || (ctrl == m_ctrl))
- continue;
-
- rc = dsi_ctrl_set_power_state(ctrl->ctrl,
- DSI_CTRL_POWER_VREG_ON);
+ rc = dsi_ctrl_phy_reset_config(ctrl->ctrl, enable);
if (rc) {
- pr_err("[%s] failed to turn off clock, rc=%d\n",
- display->name, rc);
+ pr_err("[%s] failed to %s phy reset, rc=%d\n",
+ display->name, enable ? "mask" : "unmask", rc);
+ return rc;
}
}
-
- rc = dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_VREG_ON);
- if (rc)
- pr_err("[%s] failed to turn off clocks, rc=%d\n",
- display->name, rc);
-
- return rc;
-}
-
-static int dsi_display_ctrl_link_clk_off(struct dsi_display *display)
-{
- int rc = 0;
- int i;
- struct dsi_display_ctrl *m_ctrl, *ctrl;
-
- /*
- * In case of split DSI usecases, clock for slave DSI controllers should
- * be disabled first before disabling clock for master controller. Slave
- * controllers in the clock context refer to controller which source
- * clock from another controller.
- */
-
- m_ctrl = &display->ctrl[display->clk_master_idx];
-
- for (i = 0; i < display->ctrl_count; i++) {
- ctrl = &display->ctrl[i];
- if (!ctrl->ctrl || (ctrl == m_ctrl))
- continue;
-
- rc = dsi_ctrl_set_power_state(ctrl->ctrl,
- DSI_CTRL_POWER_CORE_CLK_ON);
- if (rc) {
- pr_err("[%s] failed to turn off clock, rc=%d\n",
- display->name, rc);
- }
- }
- rc = dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_CORE_CLK_ON);
- if (rc)
- pr_err("[%s] failed to turn off clocks, rc=%d\n",
- display->name, rc);
- return rc;
+ return 0;
}
static int dsi_display_ctrl_init(struct dsi_display *display)
@@ -893,18 +1083,26 @@
return 0;
}
+ rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+ DSI_ALL_CLKS, DSI_CLK_ON);
+ if (rc) {
+ pr_err("[%s] failed to enable all DSI clocks, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
rc = dsi_display_wake_up(display);
if (rc) {
pr_err("[%s] failed to wake up display, rc=%d\n",
display->name, rc);
- goto error;
+ goto error_disable_clks;
}
rc = dsi_display_cmd_engine_enable(display);
if (rc) {
pr_err("[%s] failed to enable cmd engine, rc=%d\n",
display->name, rc);
- goto error;
+ goto error_disable_clks;
}
if (display->ctrl_count > 1) {
@@ -925,6 +1123,13 @@
}
error_disable_cmd_engine:
(void)dsi_display_cmd_engine_disable(display);
+error_disable_clks:
+ rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+ DSI_ALL_CLKS, DSI_CLK_OFF);
+ if (rc) {
+ pr_err("[%s] failed to enable all DSI clocks, rc=%d\n",
+ display->name, rc);
+ }
error:
return rc;
}
@@ -1094,17 +1299,335 @@
return rc;
}
-static int dsi_display_parse_lane_map(struct dsi_display *display)
+static int dsi_display_clk_ctrl_cb(void *priv,
+ struct dsi_clk_ctrl_info clk_state_info)
{
int rc = 0;
+ struct dsi_display *display = NULL;
+ void *clk_handle = NULL;
- display->lane_map.physical_lane0 = DSI_LOGICAL_LANE_0;
- display->lane_map.physical_lane1 = DSI_LOGICAL_LANE_1;
- display->lane_map.physical_lane2 = DSI_LOGICAL_LANE_2;
- display->lane_map.physical_lane3 = DSI_LOGICAL_LANE_3;
+ if (!priv) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ display = priv;
+
+ if (clk_state_info.client == DSI_CLK_REQ_MDP_CLIENT) {
+ clk_handle = display->mdp_clk_handle;
+ } else if (clk_state_info.client == DSI_CLK_REQ_DSI_CLIENT) {
+ clk_handle = display->dsi_clk_handle;
+ } else {
+ pr_err("invalid clk handle, return error\n");
+ return -EINVAL;
+ }
+
+ /*
+ * TODO: Wait for CMD_MDP_DONE interrupt if MDP client tries
+ * to turn off DSI clocks.
+ */
+ rc = dsi_display_clk_ctrl(clk_handle,
+ clk_state_info.clk_type, clk_state_info.clk_state);
+ if (rc) {
+ pr_err("[%s] failed to %d DSI %d clocks, rc=%d\n",
+ display->name, clk_state_info.clk_state,
+ clk_state_info.clk_type, rc);
+ return rc;
+ }
+ return 0;
+}
+
+int dsi_pre_clkoff_cb(void *priv,
+ enum dsi_clk_type clk,
+ enum dsi_clk_state new_state)
+{
+ int rc = 0;
+ struct dsi_display *display = priv;
+
+ if ((clk & DSI_LINK_CLK) && (new_state == DSI_CLK_OFF)) {
+ /*
+ * If ULPS feature is enabled, enter ULPS first.
+ */
+ if (dsi_panel_initialized(display->panel) &&
+ dsi_panel_ulps_feature_enabled(display->panel)) {
+ rc = dsi_display_set_ulps(display, true);
+ if (rc) {
+ pr_err("%s: failed enable ulps, rc = %d\n",
+ __func__, rc);
+ }
+ }
+ }
+
+ if ((clk & DSI_CORE_CLK) && (new_state == DSI_CLK_OFF)) {
+ /*
+ * Enable DSI clamps only if entering idle power collapse.
+ */
+ if (dsi_panel_initialized(display->panel) &&
+ dsi_panel_ulps_feature_enabled(display->panel)) {
+ dsi_display_phy_idle_off(display);
+ rc = dsi_display_set_clamp(display, true);
+ if (rc)
+ pr_err("%s: Failed to enable dsi clamps. rc=%d\n",
+ __func__, rc);
+ } else {
+ /* Make sure that controller is not in ULPS state when
+ * the DSI link is not active.
+ */
+ rc = dsi_display_set_ulps(display, false);
+ if (rc)
+ pr_err("%s: failed to disable ulps. rc=%d\n",
+ __func__, rc);
+ }
+ }
+
return rc;
}
+int dsi_post_clkon_cb(void *priv,
+ enum dsi_clk_type clk,
+ enum dsi_clk_state curr_state)
+{
+ int rc = 0;
+ struct dsi_display *display = priv;
+ bool mmss_clamp = false;
+
+ if (clk & DSI_CORE_CLK) {
+ mmss_clamp = display->clamp_enabled;
+ /*
+ * controller setup is needed if coming out of idle
+ * power collapse with clamps enabled.
+ */
+ if (mmss_clamp)
+ dsi_display_ctrl_setup(display);
+
+ if (display->ulps_enabled && mmss_clamp) {
+ /*
+ * ULPS Entry Request. This is needed if the lanes were
+ * in ULPS prior to power collapse, since after
+ * power collapse and reset, the DSI controller resets
+ * back to idle state and not ULPS. This ulps entry
+ * request will transition the state of the DSI
+ * controller to ULPS which will match the state of the
+ * DSI phy. This needs to be done prior to disabling
+ * the DSI clamps.
+ *
+ * Also, reset the ulps flag so that ulps_config
+ * function would reconfigure the controller state to
+ * ULPS.
+ */
+ display->ulps_enabled = false;
+ rc = dsi_display_set_ulps(display, true);
+ if (rc) {
+ pr_err("%s: Failed to enter ULPS. rc=%d\n",
+ __func__, rc);
+ goto error;
+ }
+ }
+
+ rc = dsi_display_set_clamp(display, false);
+ if (rc) {
+ pr_err("%s: Failed to disable dsi clamps. rc=%d\n",
+ __func__, rc);
+ goto error;
+ }
+
+ /*
+ * Phy setup is needed if coming out of idle
+ * power collapse with clamps enabled.
+ */
+ if (display->phy_idle_power_off || mmss_clamp)
+ dsi_display_phy_idle_on(display, mmss_clamp);
+ }
+ if (clk & DSI_LINK_CLK) {
+ if (display->ulps_enabled) {
+ rc = dsi_display_set_ulps(display, false);
+ if (rc) {
+ pr_err("%s: failed to disable ulps, rc= %d\n",
+ __func__, rc);
+ goto error;
+ }
+ }
+ }
+error:
+ return rc;
+}
+
+int dsi_post_clkoff_cb(void *priv,
+ enum dsi_clk_type clk_type,
+ enum dsi_clk_state curr_state)
+{
+ int rc = 0;
+ struct dsi_display *display = priv;
+
+ if (!display) {
+ pr_err("%s: Invalid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ if ((clk_type & DSI_CORE_CLK) &&
+ (curr_state == DSI_CLK_OFF)) {
+
+ rc = dsi_display_phy_power_off(display);
+ if (rc)
+ pr_err("[%s] failed to power off PHY, rc=%d\n",
+ display->name, rc);
+
+ rc = dsi_display_ctrl_power_off(display);
+ if (rc)
+ pr_err("[%s] failed to power DSI vregs, rc=%d\n",
+ display->name, rc);
+ }
+ return rc;
+}
+
+int dsi_pre_clkon_cb(void *priv,
+ enum dsi_clk_type clk_type,
+ enum dsi_clk_state new_state)
+{
+ int rc = 0;
+ struct dsi_display *display = priv;
+
+ if (!display) {
+ pr_err("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ if ((clk_type & DSI_CORE_CLK) && (new_state == DSI_CLK_ON)) {
+ /*
+ * Enable DSI core power
+ * 1.> PANEL_PM are controlled as part of
+ * panel_power_ctrl. Needed not be handled here.
+ * 2.> CORE_PM are controlled by dsi clk manager.
+ * 3.> CTRL_PM need to be enabled/disabled
+ * only during unblank/blank. Their state should
+ * not be changed during static screen.
+ */
+
+ rc = dsi_display_ctrl_power_on(display);
+ if (rc) {
+ pr_err("[%s] failed to power on dsi controllers, rc=%d\n",
+ display->name, rc);
+ return rc;
+ }
+
+ rc = dsi_display_phy_power_on(display);
+ if (rc) {
+ pr_err("[%s] failed to power on dsi phy, rc = %d\n",
+ display->name, rc);
+ return rc;
+ }
+
+ pr_debug("%s: Enable DSI core power\n", __func__);
+ }
+
+ return rc;
+}
+
+static void __set_lane_map_v2(u8 *lane_map_v2,
+ enum dsi_phy_data_lanes lane0,
+ enum dsi_phy_data_lanes lane1,
+ enum dsi_phy_data_lanes lane2,
+ enum dsi_phy_data_lanes lane3)
+{
+ lane_map_v2[DSI_LOGICAL_LANE_0] = lane0;
+ lane_map_v2[DSI_LOGICAL_LANE_1] = lane1;
+ lane_map_v2[DSI_LOGICAL_LANE_2] = lane2;
+ lane_map_v2[DSI_LOGICAL_LANE_3] = lane3;
+}
+
+static int dsi_display_parse_lane_map(struct dsi_display *display)
+{
+ int rc = 0, i = 0;
+ const char *data;
+ u8 temp[DSI_LANE_MAX - 1];
+
+ if (!display) {
+ pr_err("invalid params\n");
+ return -EINVAL;
+ }
+
+ /* lane-map-v2 supersedes lane-map-v1 setting */
+ rc = of_property_read_u8_array(display->pdev->dev.of_node,
+ "qcom,lane-map-v2", temp, (DSI_LANE_MAX - 1));
+ if (!rc) {
+ for (i = DSI_LOGICAL_LANE_0; i < (DSI_LANE_MAX - 1); i++)
+ display->lane_map.lane_map_v2[i] = BIT(temp[i]);
+ return 0;
+ } else if (rc != EINVAL) {
+ pr_warn("Incorrect mapping, configure default\n");
+ goto set_default;
+ }
+
+ /* lane-map older version, for DSI controller version < 2.0 */
+ data = of_get_property(display->pdev->dev.of_node,
+ "qcom,lane-map", NULL);
+ if (!data)
+ goto set_default;
+
+ if (!strcmp(data, "lane_map_3012")) {
+ display->lane_map.lane_map_v1 = DSI_LANE_MAP_3012;
+ __set_lane_map_v2(display->lane_map.lane_map_v2,
+ DSI_PHYSICAL_LANE_1,
+ DSI_PHYSICAL_LANE_2,
+ DSI_PHYSICAL_LANE_3,
+ DSI_PHYSICAL_LANE_0);
+ } else if (!strcmp(data, "lane_map_2301")) {
+ display->lane_map.lane_map_v1 = DSI_LANE_MAP_2301;
+ __set_lane_map_v2(display->lane_map.lane_map_v2,
+ DSI_PHYSICAL_LANE_2,
+ DSI_PHYSICAL_LANE_3,
+ DSI_PHYSICAL_LANE_0,
+ DSI_PHYSICAL_LANE_1);
+ } else if (!strcmp(data, "lane_map_1230")) {
+ display->lane_map.lane_map_v1 = DSI_LANE_MAP_1230;
+ __set_lane_map_v2(display->lane_map.lane_map_v2,
+ DSI_PHYSICAL_LANE_3,
+ DSI_PHYSICAL_LANE_0,
+ DSI_PHYSICAL_LANE_1,
+ DSI_PHYSICAL_LANE_2);
+ } else if (!strcmp(data, "lane_map_0321")) {
+ display->lane_map.lane_map_v1 = DSI_LANE_MAP_0321;
+ __set_lane_map_v2(display->lane_map.lane_map_v2,
+ DSI_PHYSICAL_LANE_0,
+ DSI_PHYSICAL_LANE_3,
+ DSI_PHYSICAL_LANE_2,
+ DSI_PHYSICAL_LANE_1);
+ } else if (!strcmp(data, "lane_map_1032")) {
+ display->lane_map.lane_map_v1 = DSI_LANE_MAP_1032;
+ __set_lane_map_v2(display->lane_map.lane_map_v2,
+ DSI_PHYSICAL_LANE_1,
+ DSI_PHYSICAL_LANE_0,
+ DSI_PHYSICAL_LANE_3,
+ DSI_PHYSICAL_LANE_2);
+ } else if (!strcmp(data, "lane_map_2103")) {
+ display->lane_map.lane_map_v1 = DSI_LANE_MAP_2103;
+ __set_lane_map_v2(display->lane_map.lane_map_v2,
+ DSI_PHYSICAL_LANE_2,
+ DSI_PHYSICAL_LANE_1,
+ DSI_PHYSICAL_LANE_0,
+ DSI_PHYSICAL_LANE_3);
+ } else if (!strcmp(data, "lane_map_3210")) {
+ display->lane_map.lane_map_v1 = DSI_LANE_MAP_3210;
+ __set_lane_map_v2(display->lane_map.lane_map_v2,
+ DSI_PHYSICAL_LANE_3,
+ DSI_PHYSICAL_LANE_2,
+ DSI_PHYSICAL_LANE_1,
+ DSI_PHYSICAL_LANE_0);
+ } else {
+ pr_warn("%s: invalid lane map %s specified. defaulting to lane_map0123\n",
+ __func__, data);
+ goto set_default;
+ }
+ return 0;
+
+set_default:
+ /* default lane mapping */
+ __set_lane_map_v2(display->lane_map.lane_map_v2, DSI_PHYSICAL_LANE_0,
+ DSI_PHYSICAL_LANE_1, DSI_PHYSICAL_LANE_2, DSI_PHYSICAL_LANE_3);
+ display->lane_map.lane_map_v1 = DSI_LANE_MAP_0123;
+ return 0;
+}
+
static int dsi_display_parse_dt(struct dsi_display *display)
{
int rc = 0;
@@ -1161,11 +1684,6 @@
display->panel_of = of_node;
}
- rc = dsi_display_parse_lane_map(display);
- if (rc) {
- pr_err("Lane map not found, rc=%d\n", rc);
- goto error;
- }
error:
return rc;
}
@@ -1204,6 +1722,23 @@
goto error_ctrl_put;
}
+ if (display->panel->phy_timing_len) {
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ rc = dsi_phy_set_timing_params(ctrl->phy,
+ display->panel->phy_timing_val,
+ display->panel->phy_timing_len);
+ if (rc)
+ pr_err("failed to add DSI PHY timing params");
+ }
+ }
+
+ rc = dsi_display_parse_lane_map(display);
+ if (rc) {
+ pr_err("Lane map not found, rc=%d\n", rc);
+ goto error_ctrl_put;
+ }
+
rc = dsi_display_clocks_init(display);
if (rc) {
pr_err("Failed to parse clock data, rc=%d\n", rc);
@@ -1349,12 +1884,10 @@
/* skip polarity comparison */
- if (cur->timing.refresh_rate == tgt->timing.refresh_rate) {
+ if (cur->timing.refresh_rate == tgt->timing.refresh_rate)
pr_debug("timing.refresh_rate identical %d %d\n",
cur->timing.refresh_rate,
tgt->timing.refresh_rate);
- return false;
- }
if (cur->pixel_clk_khz != tgt->pixel_clk_khz)
pr_debug("pixel_clk_khz differs %d %d\n",
@@ -1366,8 +1899,9 @@
return false;
}
- if (cur->flags != tgt->flags)
- pr_debug("flags differs %d %d\n", cur->flags, tgt->flags);
+ if (cur->dsi_mode_flags != tgt->dsi_mode_flags)
+ pr_debug("flags differs %d %d\n",
+ cur->dsi_mode_flags, tgt->dsi_mode_flags);
return true;
}
@@ -1427,6 +1961,13 @@
panel_mode = &display->panel->mode;
memcpy(panel_mode, dsi_mode, sizeof(*panel_mode));
+ /*
+ * dsi_mode_flags flags are used to communicate with other drm driver
+ * components, and are transient. They aren't inherently part of the
+ * display panel's mode and shouldn't be saved into the cached currently
+ * active mode.
+ */
+ panel_mode->dsi_mode_flags = 0;
error:
return rc;
@@ -1509,7 +2050,7 @@
}
/* TODO: Remove this direct reference to the dsi_ctrl */
- clk_hz = m_ctrl->clk_info.link_clks.pixel_clk_rate;
+ clk_hz = m_ctrl->clk_freq.pix_clk_rate;
timing = &per_ctrl_mode.timing;
switch (dfps_caps.type) {
@@ -1559,7 +2100,7 @@
pr_debug("Dynamic FPS not supported for seamless\n");
} else {
pr_debug("Mode switch is seamless Dynamic FPS\n");
- adj_mode->flags |= DSI_MODE_FLAG_DFPS |
+ adj_mode->dsi_mode_flags |= DSI_MODE_FLAG_DFPS |
DSI_MODE_FLAG_VBLANK_PRE_MODESET;
}
@@ -1586,7 +2127,7 @@
memcpy(&display->config.lane_map, &display->lane_map,
sizeof(display->lane_map));
- if (mode->flags & DSI_MODE_FLAG_DFPS) {
+ if (mode->dsi_mode_flags & DSI_MODE_FLAG_DFPS) {
rc = dsi_display_dfps_update(display, mode);
if (rc) {
pr_err("[%s]DSI dfps update failed, rc=%d\n",
@@ -1598,13 +2139,12 @@
for (i = 0; i < display->ctrl_count; i++) {
ctrl = &display->ctrl[i];
rc = dsi_ctrl_update_host_config(ctrl->ctrl, &display->config,
- mode->flags);
+ mode->dsi_mode_flags, display->dsi_clk_handle);
if (rc) {
pr_err("[%s] failed to update ctrl config, rc=%d\n",
display->name, rc);
goto error;
}
-
}
error:
return rc;
@@ -1686,7 +2226,12 @@
struct dsi_display_ctrl *display_ctrl;
struct drm_device *drm;
struct dsi_display *display;
+ struct dsi_clk_info info;
+ struct clk_ctrl_cb clk_cb;
+ void *handle = NULL;
struct platform_device *pdev = to_platform_device(dev);
+ char *client1 = "dsi_clk_client";
+ char *client2 = "mdp_event_client";
int i, rc = 0;
if (!dev || !pdev || !master) {
@@ -1711,6 +2256,8 @@
goto error;
}
+ memset(&info, 0x0, sizeof(info));
+
for (i = 0; i < display->ctrl_count; i++) {
display_ctrl = &display->ctrl[i];
@@ -1728,6 +2275,72 @@
(void)dsi_ctrl_drv_deinit(display_ctrl->ctrl);
goto error_ctrl_deinit;
}
+
+ memcpy(&info.c_clks[i], &display_ctrl->ctrl->clk_info.core_clks,
+ sizeof(struct dsi_core_clk_info));
+ memcpy(&info.l_clks[i], &display_ctrl->ctrl->clk_info.link_clks,
+ sizeof(struct dsi_link_clk_info));
+ info.bus_handle[i] =
+ display_ctrl->ctrl->axi_bus_info.bus_handle;
+ }
+
+ info.pre_clkoff_cb = dsi_pre_clkoff_cb;
+ info.pre_clkon_cb = dsi_pre_clkon_cb;
+ info.post_clkoff_cb = dsi_post_clkoff_cb;
+ info.post_clkon_cb = dsi_post_clkon_cb;
+ info.priv_data = display;
+ info.master_ndx = display->clk_master_idx;
+ info.dsi_ctrl_count = display->ctrl_count;
+ snprintf(info.name, MAX_STRING_LEN,
+ "DSI_MNGR-%s", display->name);
+
+ display->clk_mngr = dsi_display_clk_mngr_register(&info);
+ if (IS_ERR_OR_NULL(display->clk_mngr)) {
+ rc = PTR_ERR(display->clk_mngr);
+ display->clk_mngr = NULL;
+ pr_err("dsi clock registration failed, rc = %d\n", rc);
+ goto error_ctrl_deinit;
+ }
+
+ handle = dsi_register_clk_handle(display->clk_mngr, client1);
+ if (IS_ERR_OR_NULL(handle)) {
+ rc = PTR_ERR(handle);
+ pr_err("failed to register %s client, rc = %d\n",
+ client1, rc);
+ goto error_clk_deinit;
+ } else {
+ display->dsi_clk_handle = handle;
+ }
+
+ handle = dsi_register_clk_handle(display->clk_mngr, client2);
+ if (IS_ERR_OR_NULL(handle)) {
+ rc = PTR_ERR(handle);
+ pr_err("failed to register %s client, rc = %d\n",
+ client2, rc);
+ goto error_clk_client_deinit;
+ } else {
+ display->mdp_clk_handle = handle;
+ }
+
+ clk_cb.priv = display;
+ clk_cb.dsi_clk_cb = dsi_display_clk_ctrl_cb;
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ display_ctrl = &display->ctrl[i];
+
+ rc = dsi_ctrl_clk_cb_register(display_ctrl->ctrl, &clk_cb);
+ if (rc) {
+ pr_err("[%s] failed to register ctrl clk_cb[%d], rc=%d\n",
+ display->name, i, rc);
+ goto error_ctrl_deinit;
+ }
+
+ rc = dsi_phy_clk_cb_register(display_ctrl->phy, &clk_cb);
+ if (rc) {
+ pr_err("[%s] failed to register phy clk_cb[%d], rc=%d\n",
+ display->name, i, rc);
+ goto error_ctrl_deinit;
+ }
}
rc = dsi_display_mipi_host_init(display);
@@ -1759,6 +2372,10 @@
(void)dsi_panel_drv_deinit(display->panel);
error_host_deinit:
(void)dsi_display_mipi_host_deinit(display);
+error_clk_client_deinit:
+ (void)dsi_deregister_clk_handle(display->dsi_clk_handle);
+error_clk_deinit:
+ (void)dsi_display_clk_mngr_deregister(display->clk_mngr);
error_ctrl_deinit:
for (i = i - 1; i >= 0; i--) {
display_ctrl = &display->ctrl[i];
@@ -1866,7 +2483,6 @@
display->display_type = "unknown";
mutex_init(&display->display_lock);
-
display->pdev = pdev;
platform_set_drvdata(pdev, display);
mutex_lock(&dsi_display_list_lock);
@@ -2203,7 +2819,7 @@
}
if ((flags & DSI_VALIDATE_FLAG_ALLOW_ADJUST) &&
- (mode->flags & DSI_MODE_FLAG_SEAMLESS)) {
+ (mode->dsi_mode_flags & DSI_MODE_FLAG_SEAMLESS)) {
rc = dsi_display_validate_mode_seamless(display, mode);
if (rc) {
pr_err("[%s] seamless not possible rc=%d\n",
@@ -2294,25 +2910,12 @@
goto error;
}
- rc = dsi_display_ctrl_power_on(display);
- if (rc) {
- pr_err("[%s] failed to power on dsi controllers, rc=%d\n",
- display->name, rc);
- goto error_panel_post_unprep;
- }
-
- rc = dsi_display_phy_power_on(display);
- if (rc) {
- pr_err("[%s] failed to power on dsi phy, rc = %d\n",
- display->name, rc);
- goto error_ctrl_pwr_off;
- }
-
- rc = dsi_display_ctrl_core_clk_on(display);
+ rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+ DSI_CORE_CLK, DSI_CLK_ON);
if (rc) {
pr_err("[%s] failed to enable DSI core clocks, rc=%d\n",
display->name, rc);
- goto error_phy_pwr_off;
+ goto error_panel_post_unprep;
}
rc = dsi_display_phy_sw_reset(display);
@@ -2335,11 +2938,26 @@
goto error_phy_disable;
}
- rc = dsi_display_ctrl_link_clk_on(display);
+ rc = dsi_display_phy_reset_config(display, true);
+ if (rc) {
+ pr_err("[%s] failed to setup DSI controller, rc=%d\n",
+ display->name, rc);
+ goto error_ctrl_deinit;
+ }
+
+ rc = dsi_display_set_clk_src(display);
+ if (rc) {
+ pr_err("[%s] failed to set DSI link clock source, rc=%d\n",
+ display->name, rc);
+ goto error_phy_reset_off;
+ }
+
+ rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+ DSI_LINK_CLK, DSI_CLK_ON);
if (rc) {
pr_err("[%s] failed to enable DSI link clocks, rc=%d\n",
display->name, rc);
- goto error_ctrl_deinit;
+ goto error_phy_reset_off;
}
rc = dsi_display_ctrl_host_enable(display);
@@ -2360,17 +2978,17 @@
error_host_engine_off:
(void)dsi_display_ctrl_host_disable(display);
error_ctrl_link_off:
- (void)dsi_display_ctrl_link_clk_off(display);
+ (void)dsi_display_clk_ctrl(display->dsi_clk_handle,
+ DSI_LINK_CLK, DSI_CLK_OFF);
+error_phy_reset_off:
+ (void)dsi_display_phy_reset_config(display, false);
error_ctrl_deinit:
(void)dsi_display_ctrl_deinit(display);
error_phy_disable:
(void)dsi_display_phy_disable(display);
error_ctrl_clk_off:
- (void)dsi_display_ctrl_core_clk_off(display);
-error_phy_pwr_off:
- (void)dsi_display_phy_power_off(display);
-error_ctrl_pwr_off:
- (void)dsi_display_ctrl_power_off(display);
+ (void)dsi_display_clk_ctrl(display->dsi_clk_handle,
+ DSI_CORE_CLK, DSI_CLK_OFF);
error_panel_post_unprep:
(void)dsi_panel_post_unprepare(display->panel);
error:
@@ -2531,7 +3149,8 @@
pr_err("[%s] failed to disable DSI host, rc=%d\n",
display->name, rc);
- rc = dsi_display_ctrl_link_clk_off(display);
+ rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+ DSI_LINK_CLK, DSI_CLK_OFF);
if (rc)
pr_err("[%s] failed to disable Link clocks, rc=%d\n",
display->name, rc);
@@ -2546,21 +3165,17 @@
pr_err("[%s] failed to disable DSI PHY, rc=%d\n",
display->name, rc);
- rc = dsi_display_ctrl_core_clk_off(display);
+ rc = dsi_display_phy_reset_config(display, false);
+ if (rc)
+ pr_err("[%s] failed to disable DSI PHY reset config, rc=%d\n",
+ display->name, rc);
+
+ rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+ DSI_CORE_CLK, DSI_CLK_OFF);
if (rc)
pr_err("[%s] failed to disable DSI clocks, rc=%d\n",
display->name, rc);
- rc = dsi_display_phy_power_off(display);
- if (rc)
- pr_err("[%s] failed to power off PHY, rc=%d\n",
- display->name, rc);
-
- rc = dsi_display_ctrl_power_off(display);
- if (rc)
- pr_err("[%s] failed to power DSI vregs, rc=%d\n",
- display->name, rc);
-
rc = dsi_panel_post_unprepare(display->panel);
if (rc)
pr_err("[%s] panel post-unprepare failed, rc=%d\n",
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
index b77bf26..642ea40 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -110,14 +110,20 @@
* @cmd_master_idx: The master controller for sending DSI commands to panel.
* @video_master_idx: The master controller for enabling video engine.
* @clock_info: Clock sourcing for DSI display.
+ * @config: DSI host configuration information.
* @lane_map: Lane mapping between DSI host and Panel.
* @num_of_modes: Number of modes supported by display.
* @is_tpg_enabled: TPG state.
+ * @ulps_enabled: ulps state.
+ * @clamp_enabled: clamp state.
+ * @phy_idle_power_off: PHY power state.
* @host: DRM MIPI DSI Host.
- * @connector: Pointer to DRM connector object.
* @bridge: Pointer to DRM bridge object.
* @cmd_engine_refcount: Reference count enforcing single instance of cmd eng
- * @root: Debugfs root directory
+ * @clk_mngr: DSI clock manager.
+ * @dsi_clk_handle: DSI clock handle.
+ * @mdp_clk_handle: MDP clock handle.
+ * @root: Debugfs root directory
*/
struct dsi_display {
struct platform_device *pdev;
@@ -143,14 +149,21 @@
struct dsi_display_clk_info clock_info;
struct dsi_host_config config;
- struct dsi_lane_mapping lane_map;
+ struct dsi_lane_map lane_map;
u32 num_of_modes;
bool is_tpg_enabled;
+ bool ulps_enabled;
+ bool clamp_enabled;
+ bool phy_idle_power_off;
struct mipi_dsi_host host;
struct dsi_bridge *bridge;
u32 cmd_engine_refcount;
+ void *clk_mngr;
+ void *dsi_clk_handle;
+ void *mdp_clk_handle;
+
/* DEBUG FS */
struct dentry *root;
};
@@ -317,6 +330,51 @@
int dsi_display_disable(struct dsi_display *display);
/**
+ * dsi_pre_clkoff_cb() - Callback before clock is turned off
+ * @priv: private data pointer.
+ * @clk_type: clock which is being turned on.
+ * @new_state: next state for the clock.
+ *
+ * @return: error code.
+ */
+int dsi_pre_clkoff_cb(void *priv, enum dsi_clk_type clk_type,
+ enum dsi_clk_state new_state);
+
+/**
+ * dsi_post_clkoff_cb() - Callback after clock is turned off
+ * @priv: private data pointer.
+ * @clk_type: clock which is being turned on.
+ * @curr_state: current state for the clock.
+ *
+ * @return: error code.
+ */
+int dsi_post_clkoff_cb(void *priv, enum dsi_clk_type clk_type,
+ enum dsi_clk_state curr_state);
+
+/**
+ * dsi_post_clkon_cb() - Callback after clock is turned on
+ * @priv: private data pointer.
+ * @clk_type: clock which is being turned on.
+ * @curr_state: current state for the clock.
+ *
+ * @return: error code.
+ */
+int dsi_post_clkon_cb(void *priv, enum dsi_clk_type clk_type,
+ enum dsi_clk_state curr_state);
+
+
+/**
+ * dsi_pre_clkon_cb() - Callback before clock is turned on
+ * @priv: private data pointer.
+ * @clk_type: clock which is being turned on.
+ * @new_state: next state for the clock.
+ *
+ * @return: error code.
+ */
+int dsi_pre_clkon_cb(void *priv, enum dsi_clk_type clk_type,
+ enum dsi_clk_state new_state);
+
+/**
* dsi_display_unprepare() - power off display hardware.
* @display: Handle to display.
*
@@ -333,4 +391,19 @@
int dsi_dispaly_static_frame(struct dsi_display *display, bool enable);
int dsi_display_set_backlight(void *display, u32 bl_lvl);
+
+/**
+ * dsi_display_soft_reset() - perform a soft reset on DSI controller
+ * @display: Handle to display
+ *
+ * The video, command and controller engines will be disabled before the
+ * reset is triggered. After, the engines will be re-enabled to the same state
+ * as before the reset.
+ *
+ * If the reset is done while MDP timing engine is turned on, the video
+ * engine should be re-enabled only during the vertical blanking time.
+ *
+ * Return: error code
+ */
+int dsi_display_soft_reset(void *display);
#endif /* _DSI_DISPLAY_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
index bf50ebb..3f82819 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -51,11 +51,11 @@
dsi_mode->panel_mode = 0; /* TODO: Panel Mode */
if (msm_is_mode_seamless(drm_mode))
- dsi_mode->flags |= DSI_MODE_FLAG_SEAMLESS;
+ dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_SEAMLESS;
if (msm_is_mode_dynamic_fps(drm_mode))
- dsi_mode->flags |= DSI_MODE_FLAG_DFPS;
+ dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_DFPS;
if (msm_needs_vblank_pre_modeset(drm_mode))
- dsi_mode->flags |= DSI_MODE_FLAG_VBLANK_PRE_MODESET;
+ dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_VBLANK_PRE_MODESET;
}
static void convert_to_drm_mode(const struct dsi_display_mode *dsi_mode,
@@ -81,11 +81,11 @@
drm_mode->vrefresh = dsi_mode->timing.refresh_rate;
drm_mode->clock = dsi_mode->pixel_clk_khz;
- if (dsi_mode->flags & DSI_MODE_FLAG_SEAMLESS)
+ if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_SEAMLESS)
drm_mode->flags |= DRM_MODE_FLAG_SEAMLESS;
- if (dsi_mode->flags & DSI_MODE_FLAG_DFPS)
+ if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_DFPS)
drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_DYNAMIC_FPS;
- if (dsi_mode->flags & DSI_MODE_FLAG_VBLANK_PRE_MODESET)
+ if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_VBLANK_PRE_MODESET)
drm_mode->private_flags |= MSM_MODE_FLAG_VBLANK_PRE_MODESET;
drm_mode_set_name(drm_mode);
@@ -125,7 +125,7 @@
return;
}
- if (c_bridge->dsi_mode.flags & DSI_MODE_FLAG_SEAMLESS) {
+ if (c_bridge->dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_SEAMLESS) {
pr_debug("[%d] seamless pre-enable\n", c_bridge->id);
return;
}
@@ -155,7 +155,7 @@
return;
}
- if (c_bridge->dsi_mode.flags & DSI_MODE_FLAG_SEAMLESS) {
+ if (c_bridge->dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_SEAMLESS) {
pr_debug("[%d] seamless enable\n", c_bridge->id);
return;
}
@@ -313,6 +313,8 @@
break;
case DSI_OP_CMD_MODE:
sde_kms_info_add_keystr(info, "panel mode", "command");
+ sde_kms_info_add_keyint(info, "mdp_transfer_time_us",
+ panel->cmd_config.mdp_transfer_time_us);
break;
default:
pr_debug("invalid panel type:%d\n", panel->mode.panel_mode);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
index 28cfa1f..fa10b55 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -22,6 +22,8 @@
#define DSI_PANEL_DEFAULT_LABEL "Default dsi panel"
+#define DEFAULT_MDP_TRANSFER_TIME 14000
+
static int dsi_panel_vreg_get(struct dsi_panel *panel)
{
int rc = 0;
@@ -89,7 +91,18 @@
}
}
+ if (gpio_is_valid(r_config->lcd_mode_sel_gpio)) {
+ rc = gpio_request(r_config->lcd_mode_sel_gpio, "mode_gpio");
+ if (rc) {
+ pr_err("request for mode_gpio failed, rc=%d\n", rc);
+ goto error_release_mode_sel;
+ }
+ }
+
goto error;
+error_release_mode_sel:
+ if (gpio_is_valid(panel->bl_config.en_gpio))
+ gpio_free(panel->bl_config.en_gpio);
error_release_disp_en:
if (gpio_is_valid(r_config->disp_en_gpio))
gpio_free(r_config->disp_en_gpio);
@@ -114,6 +127,9 @@
if (gpio_is_valid(panel->bl_config.en_gpio))
gpio_free(panel->bl_config.en_gpio);
+ if (gpio_is_valid(panel->reset_config.lcd_mode_sel_gpio))
+ gpio_free(panel->reset_config.lcd_mode_sel_gpio);
+
return rc;
}
@@ -155,6 +171,25 @@
if (rc)
pr_err("unable to set dir for bklt gpio rc=%d\n", rc);
}
+
+ if (gpio_is_valid(panel->reset_config.lcd_mode_sel_gpio)) {
+ bool out = true;
+
+ if ((panel->reset_config.mode_sel_state == MODE_SEL_DUAL_PORT)
+ || (panel->reset_config.mode_sel_state
+ == MODE_GPIO_LOW))
+ out = false;
+ else if ((panel->reset_config.mode_sel_state
+ == MODE_SEL_SINGLE_PORT) ||
+ (panel->reset_config.mode_sel_state
+ == MODE_GPIO_HIGH))
+ out = true;
+
+ rc = gpio_direction_output(
+ panel->reset_config.lcd_mode_sel_gpio, out);
+ if (rc)
+ pr_err("unable to set dir for mode gpio rc=%d\n", rc);
+ }
exit:
return rc;
}
@@ -228,6 +263,9 @@
if (gpio_is_valid(panel->reset_config.reset_gpio))
gpio_set_value(panel->reset_config.reset_gpio, 0);
+ if (gpio_is_valid(panel->reset_config.lcd_mode_sel_gpio))
+ gpio_set_value(panel->reset_config.lcd_mode_sel_gpio, 0);
+
rc = dsi_panel_set_pinctrl_state(panel, false);
if (rc) {
pr_err("[%s] failed set pinctrl state, rc=%d\n", panel->name,
@@ -954,6 +992,14 @@
goto error;
}
+ if (of_property_read_u32(of_node, "qcom,mdss-mdp-transfer-time-us",
+ &val)) {
+ pr_debug("[%s] Fallback to default transfer-time-us\n", name);
+ cfg->mdp_transfer_time_us = DEFAULT_MDP_TRANSFER_TIME;
+ } else {
+ cfg->mdp_transfer_time_us = val;
+ }
+
error:
return rc;
}
@@ -1321,7 +1367,7 @@
{
int rc = 0;
- rc = dsi_clk_pwr_of_get_vreg_data(of_node,
+ rc = dsi_pwr_of_get_vreg_data(of_node,
&panel->power_info,
"qcom,panel-supply-entries");
if (rc) {
@@ -1337,6 +1383,7 @@
struct device_node *of_node)
{
int rc = 0;
+ const char *data;
panel->reset_config.reset_gpio = of_get_named_gpio(of_node,
"qcom,platform-reset-gpio",
@@ -1362,6 +1409,31 @@
}
}
+ panel->reset_config.lcd_mode_sel_gpio = of_get_named_gpio(of_node,
+ "qcom,panel-mode-gpio", 0);
+ if (!gpio_is_valid(panel->reset_config.lcd_mode_sel_gpio))
+ pr_debug("%s:%d mode gpio not specified\n", __func__, __LINE__);
+
+ data = of_get_property(of_node,
+ "qcom,mdss-dsi-mode-sel-gpio-state", NULL);
+ if (data) {
+ if (!strcmp(data, "single_port"))
+ panel->reset_config.mode_sel_state =
+ MODE_SEL_SINGLE_PORT;
+ else if (!strcmp(data, "dual_port"))
+ panel->reset_config.mode_sel_state =
+ MODE_SEL_DUAL_PORT;
+ else if (!strcmp(data, "high"))
+ panel->reset_config.mode_sel_state =
+ MODE_GPIO_HIGH;
+ else if (!strcmp(data, "low"))
+ panel->reset_config.mode_sel_state =
+ MODE_GPIO_LOW;
+ } else {
+ /* Set default mode as SPLIT mode */
+ panel->reset_config.mode_sel_state = MODE_SEL_DUAL_PORT;
+ }
+
/* TODO: release memory */
rc = dsi_panel_parse_reset_sequence(panel, of_node);
if (rc) {
@@ -1490,6 +1562,8 @@
struct device_node *of_node)
{
struct dsi_panel *panel;
+ const char *data;
+ u32 len = 0;
int rc = 0;
panel = kzalloc(sizeof(*panel), GFP_KERNEL);
@@ -1507,6 +1581,25 @@
goto error;
}
+ data = of_get_property(of_node,
+ "qcom,mdss-dsi-panel-phy-timings", &len);
+ if (!data) {
+ pr_debug("%s:%d, Unable to read Phy timing settings",
+ __func__, __LINE__);
+ } else {
+ int i = 0;
+
+ panel->phy_timing_val = kzalloc((sizeof(u32) * len),
+ GFP_KERNEL);
+ if (!panel->phy_timing_val) {
+ kfree(panel);
+ return ERR_PTR(-ENOMEM);
+ }
+ for (i = 0; i < len; i++)
+ panel->phy_timing_val[i] = data[i];
+ }
+ panel->phy_timing_len = len;
+
panel->mode.pixel_clk_khz = (DSI_H_TOTAL(&panel->mode.timing) *
DSI_V_TOTAL(&panel->mode.timing) *
panel->mode.timing.refresh_rate) / 1000;
@@ -1861,6 +1954,7 @@
pr_err("[%s] failed to send DSI_CMD_SET_ON cmds, rc=%d\n",
panel->name, rc);
}
+ panel->panel_initialized = true;
mutex_unlock(&panel->panel_lock);
return rc;
}
@@ -1882,6 +1976,7 @@
panel->name, rc);
goto error;
}
+ panel->panel_initialized = false;
error:
mutex_unlock(&panel->panel_lock);
return rc;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
index 4d21a4c..7b60193 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -25,7 +25,8 @@
#include "dsi_defs.h"
#include "dsi_ctrl_hw.h"
-#include "dsi_clk_pwr.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
#define MAX_BL_LEVEL 4096
@@ -68,6 +69,14 @@
DSI_BACKLIGHT_MAX,
};
+enum {
+ MODE_GPIO_NOT_VALID = 0,
+ MODE_SEL_DUAL_PORT,
+ MODE_SEL_SINGLE_PORT,
+ MODE_GPIO_HIGH,
+ MODE_GPIO_LOW,
+};
+
struct dsi_dfps_capabilities {
bool dfps_support;
enum dsi_dfps_type type;
@@ -130,6 +139,8 @@
int reset_gpio;
int disp_en_gpio;
+ int lcd_mode_sel_gpio;
+ u32 mode_sel_state;
};
struct dsi_panel {
@@ -151,6 +162,9 @@
struct dsi_panel_cmd_set cmd_sets[DSI_CMD_SET_MAX];
struct dsi_panel_phy_props phy_props;
+ u32 *phy_timing_val;
+ u32 phy_timing_len;
+
struct dsi_regulator_info power_info;
struct dsi_display_mode mode;
@@ -159,8 +173,22 @@
struct dsi_pinctrl_info pinctrl;
bool lp11_init;
+ bool ulps_enabled;
+ bool allow_phy_power_off;
+
+ bool panel_initialized;
};
+static inline bool dsi_panel_ulps_feature_enabled(struct dsi_panel *panel)
+{
+ return panel->ulps_enabled;
+}
+
+static inline bool dsi_panel_initialized(struct dsi_panel *panel)
+{
+ return panel->panel_initialized;
+}
+
struct dsi_panel *dsi_panel_get(struct device *parent,
struct device_node *of_node);
void dsi_panel_put(struct dsi_panel *panel);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
index 1ccbbe7..96a98bd 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -25,7 +25,8 @@
#include "msm_gpu.h"
#include "dsi_phy.h"
#include "dsi_phy_hw.h"
-#include "dsi_clk_pwr.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
#include "dsi_catalog.h"
#define DSI_PHY_DEFAULT_LABEL "MDSS PHY CTRL"
@@ -38,6 +39,20 @@
static LIST_HEAD(dsi_phy_list);
static DEFINE_MUTEX(dsi_phy_list_lock);
+static const struct dsi_ver_spec_info dsi_phy_v0_0_hpm = {
+ .version = DSI_PHY_VERSION_0_0_HPM,
+ .lane_cfg_count = 4,
+ .strength_cfg_count = 2,
+ .regulator_cfg_count = 1,
+ .timing_cfg_count = 8,
+};
+static const struct dsi_ver_spec_info dsi_phy_v0_0_lpm = {
+ .version = DSI_PHY_VERSION_0_0_LPM,
+ .lane_cfg_count = 4,
+ .strength_cfg_count = 2,
+ .regulator_cfg_count = 1,
+ .timing_cfg_count = 8,
+};
static const struct dsi_ver_spec_info dsi_phy_v1_0 = {
.version = DSI_PHY_VERSION_1_0,
.lane_cfg_count = 4,
@@ -56,26 +71,21 @@
.version = DSI_PHY_VERSION_3_0,
.lane_cfg_count = 4,
.strength_cfg_count = 2,
- .regulator_cfg_count = 1,
- .timing_cfg_count = 8,
-};
-static const struct dsi_ver_spec_info dsi_phy_v4_0 = {
- .version = DSI_PHY_VERSION_4_0,
- .lane_cfg_count = 4,
- .strength_cfg_count = 2,
- .regulator_cfg_count = 1,
- .timing_cfg_count = 8,
+ .regulator_cfg_count = 0,
+ .timing_cfg_count = 12,
};
static const struct of_device_id msm_dsi_phy_of_match[] = {
+ { .compatible = "qcom,dsi-phy-v0.0-hpm",
+ .data = &dsi_phy_v0_0_hpm,},
+ { .compatible = "qcom,dsi-phy-v0.0-lpm",
+ .data = &dsi_phy_v0_0_lpm,},
{ .compatible = "qcom,dsi-phy-v1.0",
.data = &dsi_phy_v1_0,},
{ .compatible = "qcom,dsi-phy-v2.0",
.data = &dsi_phy_v2_0,},
{ .compatible = "qcom,dsi-phy-v3.0",
.data = &dsi_phy_v3_0,},
- { .compatible = "qcom,dsi-phy-v4.0",
- .data = &dsi_phy_v4_0,},
{}
};
@@ -104,65 +114,6 @@
return 0;
}
-static int dsi_phy_clocks_deinit(struct msm_dsi_phy *phy)
-{
- int rc = 0;
- struct dsi_core_clk_info *core = &phy->clks.core_clks;
-
- if (core->mdp_core_clk)
- devm_clk_put(&phy->pdev->dev, core->mdp_core_clk);
- if (core->iface_clk)
- devm_clk_put(&phy->pdev->dev, core->iface_clk);
- if (core->core_mmss_clk)
- devm_clk_put(&phy->pdev->dev, core->core_mmss_clk);
- if (core->bus_clk)
- devm_clk_put(&phy->pdev->dev, core->bus_clk);
-
- memset(core, 0x0, sizeof(*core));
-
- return rc;
-}
-
-static int dsi_phy_clocks_init(struct platform_device *pdev,
- struct msm_dsi_phy *phy)
-{
- int rc = 0;
- struct dsi_core_clk_info *core = &phy->clks.core_clks;
-
- core->mdp_core_clk = devm_clk_get(&pdev->dev, "mdp_core_clk");
- if (IS_ERR(core->mdp_core_clk)) {
- rc = PTR_ERR(core->mdp_core_clk);
- pr_err("failed to get mdp_core_clk, rc=%d\n", rc);
- goto fail;
- }
-
- core->iface_clk = devm_clk_get(&pdev->dev, "iface_clk");
- if (IS_ERR(core->iface_clk)) {
- rc = PTR_ERR(core->iface_clk);
- pr_err("failed to get iface_clk, rc=%d\n", rc);
- goto fail;
- }
-
- core->core_mmss_clk = devm_clk_get(&pdev->dev, "core_mmss_clk");
- if (IS_ERR(core->core_mmss_clk)) {
- rc = PTR_ERR(core->core_mmss_clk);
- pr_err("failed to get core_mmss_clk, rc=%d\n", rc);
- goto fail;
- }
-
- core->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
- if (IS_ERR(core->bus_clk)) {
- rc = PTR_ERR(core->bus_clk);
- pr_err("failed to get bus_clk, rc=%d\n", rc);
- goto fail;
- }
-
- return rc;
-fail:
- dsi_phy_clocks_deinit(phy);
- return rc;
-}
-
static int dsi_phy_supplies_init(struct platform_device *pdev,
struct msm_dsi_phy *phy)
{
@@ -182,7 +133,7 @@
ARRAY_SIZE(regs->vregs[i].vreg_name),
"%s", "gdsc");
- rc = dsi_clk_pwr_get_dt_vreg_data(&pdev->dev,
+ rc = dsi_pwr_get_dt_vreg_data(&pdev->dev,
&phy->pwr_info.phy_pwr,
"qcom,phy-supply-entries");
if (rc) {
@@ -326,16 +277,18 @@
}
regs->count_per_lane = phy->ver_info->regulator_cfg_count;
+ if (regs->count_per_lane > 0) {
rc = dsi_phy_parse_dt_per_lane_cfgs(pdev, regs,
"qcom,platform-regulator-settings");
- if (rc) {
- pr_err("failed to parse lane cfgs, rc=%d\n", rc);
- goto err;
+ if (rc) {
+ pr_err("failed to parse lane cfgs, rc=%d\n", rc);
+ goto err;
+ }
}
/* Actual timing values are dependent on panel */
timing->count_per_lane = phy->ver_info->timing_cfg_count;
-
+ return 0;
err:
lane->count_per_lane = 0;
strength->count_per_lane = 0;
@@ -404,16 +357,10 @@
goto fail;
}
- rc = dsi_phy_clocks_init(pdev, dsi_phy);
- if (rc) {
- pr_err("failed to parse clock information, rc = %d\n", rc);
- goto fail_regmap;
- }
-
rc = dsi_phy_supplies_init(pdev, dsi_phy);
if (rc) {
pr_err("failed to parse voltage supplies, rc = %d\n", rc);
- goto fail_clks;
+ goto fail_regmap;
}
rc = dsi_catalog_phy_setup(&dsi_phy->hw, ver_info->version,
@@ -446,8 +393,6 @@
fail_supplies:
(void)dsi_phy_supplies_deinit(dsi_phy);
-fail_clks:
- (void)dsi_phy_clocks_deinit(dsi_phy);
fail_regmap:
(void)dsi_phy_regmap_deinit(dsi_phy);
fail:
@@ -489,10 +434,6 @@
if (rc)
pr_err("failed to deinitialize voltage supplies, rc=%d\n", rc);
- rc = dsi_phy_clocks_deinit(phy);
- if (rc)
- pr_err("failed to deinitialize clocks, rc=%d\n", rc);
-
rc = dsi_phy_regmap_deinit(phy);
if (rc)
pr_err("failed to deinitialize regmap, rc=%d\n", rc);
@@ -527,7 +468,7 @@
static void dsi_phy_disable_hw(struct msm_dsi_phy *phy)
{
if (phy->hw.ops.disable)
- phy->hw.ops.disable(&phy->hw);
+ phy->hw.ops.disable(&phy->hw, &phy->cfg);
if (phy->hw.ops.regulator_disable)
phy->hw.ops.regulator_disable(&phy->hw);
@@ -622,6 +563,19 @@
return 0;
}
+int dsi_phy_clk_cb_register(struct msm_dsi_phy *dsi_phy,
+ struct clk_ctrl_cb *clk_cb)
+{
+ if (!dsi_phy || !clk_cb) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ dsi_phy->clk_cb.priv = clk_cb->priv;
+ dsi_phy->clk_cb.dsi_clk_cb = clk_cb->dsi_clk_cb;
+ return 0;
+}
+
/**
* dsi_phy_validate_mode() - validate a display mode
* @dsi_phy: DSI PHY handle.
@@ -679,22 +633,27 @@
pr_err("failed to enable digital regulator\n");
goto error;
}
- rc = dsi_pwr_enable_regulator(&dsi_phy->pwr_info.phy_pwr, true);
- if (rc) {
- pr_err("failed to enable phy power\n");
- (void)dsi_pwr_enable_regulator(
- &dsi_phy->pwr_info.digital,
- false
- );
- goto error;
+
+ if (dsi_phy->dsi_phy_state == DSI_PHY_ENGINE_OFF) {
+ rc = dsi_pwr_enable_regulator(
+ &dsi_phy->pwr_info.phy_pwr, true);
+ if (rc) {
+ pr_err("failed to enable phy power\n");
+ (void)dsi_pwr_enable_regulator(
+ &dsi_phy->pwr_info.digital, false);
+ goto error;
+ }
}
} else {
- rc = dsi_pwr_enable_regulator(&dsi_phy->pwr_info.phy_pwr,
- false);
- if (rc) {
- pr_err("failed to enable digital regulator\n");
- goto error;
+ if (dsi_phy->dsi_phy_state == DSI_PHY_ENGINE_OFF) {
+ rc = dsi_pwr_enable_regulator(
+ &dsi_phy->pwr_info.phy_pwr, false);
+ if (rc) {
+ pr_err("failed to enable digital regulator\n");
+ goto error;
+ }
}
+
rc = dsi_pwr_enable_regulator(&dsi_phy->pwr_info.digital,
false);
if (rc) {
@@ -709,6 +668,100 @@
return rc;
}
+static int dsi_phy_enable_ulps(struct msm_dsi_phy *phy,
+ struct dsi_host_config *config)
+{
+ int rc = 0;
+ u32 lanes = 0;
+ u32 ulps_lanes;
+
+ if (config->panel_mode == DSI_OP_CMD_MODE)
+ lanes = config->common_config.data_lanes;
+ lanes |= DSI_CLOCK_LANE;
+
+ rc = phy->hw.ops.ulps_ops.wait_for_lane_idle(&phy->hw, lanes);
+ if (rc) {
+ pr_err("lanes not entering idle, skip ULPS\n");
+ return rc;
+ }
+
+ phy->hw.ops.ulps_ops.ulps_request(&phy->hw, &phy->cfg, lanes);
+
+ ulps_lanes = phy->hw.ops.ulps_ops.get_lanes_in_ulps(&phy->hw);
+
+ if ((lanes & ulps_lanes) != lanes) {
+ pr_err("Failed to enter ULPS, request=0x%x, actual=0x%x\n",
+ lanes, ulps_lanes);
+ rc = -EIO;
+ }
+
+ return rc;
+}
+
+static int dsi_phy_disable_ulps(struct msm_dsi_phy *phy,
+ struct dsi_host_config *config)
+{
+ int rc = 0;
+ u32 ulps_lanes, lanes = 0;
+
+ if (config->panel_mode == DSI_OP_CMD_MODE)
+ lanes = config->common_config.data_lanes;
+ lanes |= DSI_CLOCK_LANE;
+
+ ulps_lanes = phy->hw.ops.ulps_ops.get_lanes_in_ulps(&phy->hw);
+
+ if ((lanes & ulps_lanes) != lanes)
+ pr_err("Mismatch between lanes in ULPS\n");
+
+ lanes &= ulps_lanes;
+
+ phy->hw.ops.ulps_ops.ulps_exit(&phy->hw, &phy->cfg, lanes);
+
+ ulps_lanes = phy->hw.ops.ulps_ops.get_lanes_in_ulps(&phy->hw);
+ if (ulps_lanes & lanes) {
+ pr_err("Lanes (0x%x) stuck in ULPS\n", ulps_lanes);
+ rc = -EIO;
+ }
+
+ return rc;
+}
+
+
+int dsi_phy_set_ulps(struct msm_dsi_phy *phy, struct dsi_host_config *config,
+ bool enable)
+{
+ int rc = 0;
+
+ if (!phy) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ if (!phy->hw.ops.ulps_ops.ulps_request ||
+ !phy->hw.ops.ulps_ops.ulps_exit) {
+ pr_debug("DSI PHY ULPS ops not present\n");
+ return 0;
+ }
+
+ mutex_lock(&phy->phy_lock);
+
+ if (enable)
+ rc = dsi_phy_enable_ulps(phy, config);
+ else
+ rc = dsi_phy_disable_ulps(phy, config);
+
+ if (rc) {
+ pr_err("[DSI_PHY%d] Ulps state change(%d) failed, rc=%d\n",
+ phy->index, enable, rc);
+ goto error;
+ }
+ pr_debug("[DSI_PHY%d] ULPS state = %d\n", phy->index, enable);
+
+error:
+ mutex_unlock(&phy->phy_lock);
+ return rc;
+}
+
/**
* dsi_phy_enable() - enable DSI PHY hardware
* @dsi_phy: DSI PHY handle.
@@ -726,48 +779,58 @@
bool skip_validation)
{
int rc = 0;
+ struct dsi_clk_ctrl_info clk_info;
if (!phy || !config) {
pr_err("Invalid params\n");
return -EINVAL;
}
+ clk_info.client = DSI_CLK_REQ_DSI_CLIENT;
+ clk_info.clk_type = DSI_CORE_CLK;
+ clk_info.clk_state = DSI_CLK_ON;
+
+ rc = phy->clk_cb.dsi_clk_cb(phy->clk_cb.priv, clk_info);
+ if (rc) {
+ pr_err("failed to enable DSI core clocks\n");
+ return rc;
+ }
+
mutex_lock(&phy->phy_lock);
if (!skip_validation)
pr_debug("[PHY_%d] TODO: perform validation\n", phy->index);
- rc = dsi_clk_enable_core_clks(&phy->clks.core_clks, true);
- if (rc) {
- pr_err("failed to enable core clocks, rc=%d\n", rc);
- goto error;
- }
-
memcpy(&phy->mode, &config->video_timing, sizeof(phy->mode));
+ memcpy(&phy->cfg.lane_map, &config->lane_map, sizeof(config->lane_map));
phy->data_lanes = config->common_config.data_lanes;
phy->dst_format = config->common_config.dst_format;
- phy->lane_map = config->lane_map;
phy->cfg.pll_source = pll_source;
- rc = phy->hw.ops.calculate_timing_params(&phy->hw,
+ /**
+ * If PHY timing parameters are not present in panel dtsi file,
+ * then calculate them in the driver
+ */
+ if (!phy->cfg.is_phy_timing_present)
+ rc = phy->hw.ops.calculate_timing_params(&phy->hw,
&phy->mode,
&config->common_config,
&phy->cfg.timing);
if (rc) {
pr_err("[%s] failed to set timing, rc=%d\n", phy->name, rc);
- goto error_disable_clks;
+ goto error;
}
dsi_phy_enable_hw(phy);
+ phy->dsi_phy_state = DSI_PHY_ENGINE_ON;
-error_disable_clks:
- rc = dsi_clk_enable_core_clks(&phy->clks.core_clks, false);
- if (rc) {
- pr_err("failed to disable clocks, skip phy disable\n");
- goto error;
- }
error:
mutex_unlock(&phy->phy_lock);
+
+ clk_info.clk_state = DSI_CLK_OFF;
+ rc = phy->clk_cb.dsi_clk_cb(phy->clk_cb.priv, clk_info);
+ if (rc)
+ pr_err("failed to disable DSI core clocks\n");
return rc;
}
@@ -780,31 +843,67 @@
int dsi_phy_disable(struct msm_dsi_phy *phy)
{
int rc = 0;
+ struct dsi_clk_ctrl_info clk_info;
if (!phy) {
pr_err("Invalid params\n");
return -EINVAL;
}
+ clk_info.client = DSI_CLK_REQ_DSI_CLIENT;
+ clk_info.clk_type = DSI_CORE_CLK;
+ clk_info.clk_state = DSI_CLK_ON;
+
+ rc = phy->clk_cb.dsi_clk_cb(phy->clk_cb.priv, clk_info);
+ if (rc) {
+ pr_err("failed to enable DSI core clocks\n");
+ return rc;
+ }
+
mutex_lock(&phy->phy_lock);
-
- rc = dsi_clk_enable_core_clks(&phy->clks.core_clks, true);
- if (rc) {
- pr_err("failed to enable core clocks, rc=%d\n", rc);
- goto error;
- }
-
dsi_phy_disable_hw(phy);
+ phy->dsi_phy_state = DSI_PHY_ENGINE_OFF;
+ mutex_unlock(&phy->phy_lock);
- rc = dsi_clk_enable_core_clks(&phy->clks.core_clks, false);
- if (rc) {
- pr_err("failed to disable core clocks, rc=%d\n", rc);
- goto error;
+ clk_info.clk_state = DSI_CLK_OFF;
+
+ rc = phy->clk_cb.dsi_clk_cb(phy->clk_cb.priv, clk_info);
+ if (rc)
+ pr_err("failed to disable DSI core clocks\n");
+
+ return rc;
+}
+
+/**
+ * dsi_phy_idle_ctrl() - enable/disable DSI PHY during idle screen
+ * @phy: DSI PHY handle
+ * @enable: boolean to specify PHY enable/disable.
+ *
+ * Return: error code.
+ */
+
+int dsi_phy_idle_ctrl(struct msm_dsi_phy *phy, bool enable)
+{
+ if (!phy) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
}
-error:
+ mutex_lock(&phy->phy_lock);
+ if (enable) {
+ if (phy->hw.ops.phy_idle_on)
+ phy->hw.ops.phy_idle_on(&phy->hw, &phy->cfg);
+
+ if (phy->hw.ops.regulator_enable)
+ phy->hw.ops.regulator_enable(&phy->hw,
+ &phy->cfg.regulators);
+ } else {
+ if (phy->hw.ops.phy_idle_off)
+ phy->hw.ops.phy_idle_off(&phy->hw);
+ }
mutex_unlock(&phy->phy_lock);
- return rc;
+
+ return 0;
}
/**
@@ -819,11 +918,9 @@
* Return: error code.
*/
int dsi_phy_set_timing_params(struct msm_dsi_phy *phy,
- u8 *timing, u32 size)
+ u32 *timing, u32 size)
{
int rc = 0;
- int i, j;
- struct dsi_phy_per_lane_cfgs *timing_cfg;
if (!phy || !timing || !size) {
pr_err("Invalid params\n");
@@ -832,18 +929,10 @@
mutex_lock(&phy->phy_lock);
- if (size != (DSI_LANE_MAX * phy->cfg.timing.count_per_lane)) {
- pr_err("Unexpected timing array size %d\n", size);
- rc = -EINVAL;
- } else {
- timing_cfg = &phy->cfg.timing;
- for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) {
- for (j = 0; j < timing_cfg->count_per_lane; j++) {
- timing_cfg->lane[i][j] = *timing;
- timing++;
- }
- }
- }
+ if (phy->hw.ops.phy_timing_val)
+ rc = phy->hw.ops.phy_timing_val(&phy->cfg.timing, timing, size);
+ if (!rc)
+ phy->cfg.is_phy_timing_present = true;
mutex_unlock(&phy->phy_lock);
return rc;
}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
index 6c31bfa..4a64855 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -15,7 +15,8 @@
#define _DSI_PHY_H_
#include "dsi_defs.h"
-#include "dsi_clk_pwr.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
#include "dsi_phy_hw.h"
struct dsi_ver_spec_info {
@@ -27,14 +28,6 @@
};
/**
- * struct dsi_phy_clk_info - clock information for DSI controller
- * @core_clks: Core clocks needed to access PHY registers.
- */
-struct dsi_phy_clk_info {
- struct dsi_core_clk_info core_clks;
-};
-
-/**
* struct dsi_phy_power_info - digital and analog power supplies for DSI PHY
* @digital: Digital power supply for DSI PHY.
* @phy_pwr: Analog power supplies for DSI PHY to work.
@@ -45,6 +38,19 @@
};
/**
+ * enum phy_engine_state - define engine status for dsi phy.
+ * @DSI_PHY_ENGINE_OFF: Engine is turned off.
+ * @DSI_PHY_ENGINE_ON: Engine is turned on.
+ * @DSI_PHY_ENGINE_MAX: Maximum value.
+ */
+enum phy_engine_state {
+ DSI_PHY_ENGINE_OFF = 0,
+ DSI_PHY_ENGINE_ON,
+ DSI_PHY_ENGINE_MAX,
+};
+
+
+/**
* struct msm_dsi_phy - DSI PHY object
* @pdev: Pointer to platform device.
* @index: Instance id.
@@ -53,12 +59,14 @@
* @phy_lock: Mutex for hardware and object access.
* @ver_info: Version specific phy parameters.
* @hw: DSI PHY hardware object.
+ * @pwr_info: Power information.
* @cfg: DSI phy configuration.
+ * @clk_cb: structure containing call backs for clock control
* @power_state: True if PHY is powered on.
+ * @dsi_phy_state: PHY state information.
* @mode: Current mode.
* @data_lanes: Number of data lanes used.
* @dst_format: Destination format.
- * @lane_map: Map between logical and physical lanes.
*/
struct msm_dsi_phy {
struct platform_device *pdev;
@@ -70,16 +78,16 @@
const struct dsi_ver_spec_info *ver_info;
struct dsi_phy_hw hw;
- struct dsi_phy_clk_info clks;
struct dsi_phy_power_info pwr_info;
struct dsi_phy_cfg cfg;
+ struct clk_ctrl_cb clk_cb;
+ enum phy_engine_state dsi_phy_state;
bool power_state;
struct dsi_mode_info mode;
enum dsi_data_lanes data_lanes;
enum dsi_pixel_format dst_format;
- struct dsi_lane_mapping lane_map;
};
/**
@@ -170,6 +178,36 @@
int dsi_phy_disable(struct msm_dsi_phy *phy);
/**
+ * dsi_phy_set_ulps() - set ulps state for DSI pHY
+ * @phy: DSI PHY handle
+ * @config: DSi host configuration information.
+ * @enable: Enable/Disable
+ *
+ * Return: error code.
+ */
+int dsi_phy_set_ulps(struct msm_dsi_phy *phy, struct dsi_host_config *config,
+ bool enable);
+
+/**
+ * dsi_phy_clk_cb_register() - Register PHY clock control callback
+ * @phy: DSI PHY handle
+ * @clk_cb: Structure containing call back for clock control
+ *
+ * Return: error code.
+ */
+int dsi_phy_clk_cb_register(struct msm_dsi_phy *phy,
+ struct clk_ctrl_cb *clk_cb);
+
+/**
+ * dsi_phy_idle_ctrl() - enable/disable DSI PHY during idle screen
+ * @phy: DSI PHY handle
+ * @enable: boolean to specify PHY enable/disable.
+ *
+ * Return: error code.
+ */
+int dsi_phy_idle_ctrl(struct msm_dsi_phy *phy, bool enable);
+
+/**
* dsi_phy_set_timing_params() - timing parameters for the panel
* @phy: DSI PHY handle
* @timing: array holding timing params.
@@ -181,7 +219,7 @@
* Return: error code.
*/
int dsi_phy_set_timing_params(struct msm_dsi_phy *phy,
- u8 *timing, u32 size);
+ u32 *timing, u32 size);
/**
* dsi_phy_drv_register() - register platform driver for dsi phy
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
index 5edfd5e..daaa78a 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17,22 +17,25 @@
#include "dsi_defs.h"
#define DSI_MAX_SETTINGS 8
+#define DSI_PHY_TIMING_V3_SIZE 12
/**
* enum dsi_phy_version - DSI PHY version enumeration
* @DSI_PHY_VERSION_UNKNOWN: Unknown version.
- * @DSI_PHY_VERSION_1_0: 28nm-HPM.
- * @DSI_PHY_VERSION_2_0: 28nm-LPM.
- * @DSI_PHY_VERSION_3_0: 20nm.
- * @DSI_PHY_VERSION_4_0: 14nm.
+ * @DSI_PHY_VERSION_0_0_HPM: 28nm-HPM.
+ * @DSI_PHY_VERSION_0_0_LPM: 28nm-HPM.
+ * @DSI_PHY_VERSION_1_0: 20nm
+ * @DSI_PHY_VERSION_2_0: 14nm
+ * @DSI_PHY_VERSION_3_0: 10nm
* @DSI_PHY_VERSION_MAX:
*/
enum dsi_phy_version {
DSI_PHY_VERSION_UNKNOWN,
- DSI_PHY_VERSION_1_0, /* 28nm-HPM */
- DSI_PHY_VERSION_2_0, /* 28nm-LPM */
- DSI_PHY_VERSION_3_0, /* 20nm */
- DSI_PHY_VERSION_4_0, /* 14nm */
+ DSI_PHY_VERSION_0_0_HPM, /* 28nm-HPM */
+ DSI_PHY_VERSION_0_0_LPM, /* 28nm-LPM */
+ DSI_PHY_VERSION_1_0, /* 20nm */
+ DSI_PHY_VERSION_2_0, /* 14nm */
+ DSI_PHY_VERSION_3_0, /* 10nm */
DSI_PHY_VERSION_MAX
};
@@ -40,6 +43,7 @@
* enum dsi_phy_hw_features - features supported by DSI PHY hardware
* @DSI_PHY_DPHY: Supports DPHY
* @DSI_PHY_CPHY: Supports CPHY
+ * @DSI_PHY_MAX_FEATURES:
*/
enum dsi_phy_hw_features {
DSI_PHY_DPHY,
@@ -66,10 +70,12 @@
/**
* struct dsi_phy_per_lane_cfgs - Holds register values for PHY parameters
* @lane: A set of maximum 8 values for each lane.
+ * @lane_v3: A set of maximum 12 values for each lane.
* @count_per_lane: Number of values per each lane.
*/
struct dsi_phy_per_lane_cfgs {
u8 lane[DSI_LANE_MAX][DSI_MAX_SETTINGS];
+ u8 lane_v3[DSI_PHY_TIMING_V3_SIZE];
u32 count_per_lane;
};
@@ -78,19 +84,74 @@
* @lanecfg: Lane configuration settings.
* @strength: Strength settings for lanes.
* @timing: Timing parameters for lanes.
+ * @is_phy_timing_present: Boolean whether phy timings are defined.
* @regulators: Regulator settings for lanes.
* @pll_source: PLL source.
+ * @lane_map: DSI logical to PHY lane mapping.
*/
struct dsi_phy_cfg {
struct dsi_phy_per_lane_cfgs lanecfg;
struct dsi_phy_per_lane_cfgs strength;
struct dsi_phy_per_lane_cfgs timing;
+ bool is_phy_timing_present;
struct dsi_phy_per_lane_cfgs regulators;
enum dsi_phy_pll_source pll_source;
+ struct dsi_lane_map lane_map;
};
struct dsi_phy_hw;
+struct phy_ulps_config_ops {
+ /**
+ * wait_for_lane_idle() - wait for DSI lanes to go to idle state
+ * @phy: Pointer to DSI PHY hardware instance.
+ * @lanes: ORed list of lanes (enum dsi_data_lanes) which need
+ * to be checked to be in idle state.
+ */
+ int (*wait_for_lane_idle)(struct dsi_phy_hw *phy, u32 lanes);
+
+ /**
+ * ulps_request() - request ulps entry for specified lanes
+ * @phy: Pointer to DSI PHY hardware instance.
+ * @cfg: Per lane configurations for timing, strength and lane
+ * configurations.
+ * @lanes: ORed list of lanes (enum dsi_data_lanes) which need
+ * to enter ULPS.
+ *
+ * Caller should check if lanes are in ULPS mode by calling
+ * get_lanes_in_ulps() operation.
+ */
+ void (*ulps_request)(struct dsi_phy_hw *phy,
+ struct dsi_phy_cfg *cfg, u32 lanes);
+
+ /**
+ * ulps_exit() - exit ULPS on specified lanes
+ * @phy: Pointer to DSI PHY hardware instance.
+ * @cfg: Per lane configurations for timing, strength and lane
+ * configurations.
+ * @lanes: ORed list of lanes (enum dsi_data_lanes) which need
+ * to exit ULPS.
+ *
+ * Caller should check if lanes are in active mode by calling
+ * get_lanes_in_ulps() operation.
+ */
+ void (*ulps_exit)(struct dsi_phy_hw *phy,
+ struct dsi_phy_cfg *cfg, u32 lanes);
+
+ /**
+ * get_lanes_in_ulps() - returns the list of lanes in ULPS mode
+ * @phy: Pointer to DSI PHY hardware instance.
+ *
+ * Returns an ORed list of lanes (enum dsi_data_lanes) that are in ULPS
+ * state. If 0 is returned, all the lanes are active.
+ *
+ * Return: List of lanes in ULPS state.
+ */
+ u32 (*get_lanes_in_ulps)(struct dsi_phy_hw *phy);
+};
+
+
+
/**
* struct dsi_phy_hw_ops - Operations for DSI PHY hardware.
* @regulator_enable: Enable PHY regulators.
@@ -125,8 +186,24 @@
/**
* disable() - Disable PHY hardware
* @phy: Pointer to DSI PHY hardware object.
+ * @cfg: Per lane configurations for timing, strength and lane
+ * configurations.
*/
- void (*disable)(struct dsi_phy_hw *phy);
+ void (*disable)(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
+
+ /**
+ * phy_idle_on() - Enable PHY hardware when entering idle screen
+ * @phy: Pointer to DSI PHY hardware object.
+ * @cfg: Per lane configurations for timing, strength and lane
+ * configurations.
+ */
+ void (*phy_idle_on)(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
+
+ /**
+ * phy_idle_off() - Disable PHY hardware when exiting idle screen
+ * @phy: Pointer to DSI PHY hardware object.
+ */
+ void (*phy_idle_off)(struct dsi_phy_hw *phy);
/**
* calculate_timing_params() - calculates timing parameters.
@@ -139,6 +216,18 @@
struct dsi_mode_info *mode,
struct dsi_host_common_cfg *config,
struct dsi_phy_per_lane_cfgs *timing);
+
+ /**
+ * phy_timing_val() - Gets PHY timing values.
+ * @timing_val: Timing parameters for each lane which will be returned.
+ * @timing: Array containing PHY timing values
+ * @size: Size of the array
+ */
+ int (*phy_timing_val)(struct dsi_phy_per_lane_cfgs *timing_val,
+ u32 *timing, u32 size);
+
+ void *timing_ops;
+ struct phy_ulps_config_ops ulps_ops;
};
/**
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v2_0.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v2_0.c
new file mode 100644
index 0000000..9cf542d
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v2_0.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "dsi-phy-hw:" fmt
+#include <linux/math64.h>
+#include <linux/delay.h>
+#include "dsi_hw.h"
+#include "dsi_phy_hw.h"
+
+#define DSIPHY_CMN_REVISION_ID0 0x0000
+#define DSIPHY_CMN_REVISION_ID1 0x0004
+#define DSIPHY_CMN_REVISION_ID2 0x0008
+#define DSIPHY_CMN_REVISION_ID3 0x000C
+#define DSIPHY_CMN_CLK_CFG0 0x0010
+#define DSIPHY_CMN_CLK_CFG1 0x0014
+#define DSIPHY_CMN_GLBL_TEST_CTRL 0x0018
+#define DSIPHY_CMN_CTRL_0 0x001C
+#define DSIPHY_CMN_CTRL_1 0x0020
+#define DSIPHY_CMN_CAL_HW_TRIGGER 0x0024
+#define DSIPHY_CMN_CAL_SW_CFG0 0x0028
+#define DSIPHY_CMN_CAL_SW_CFG1 0x002C
+#define DSIPHY_CMN_CAL_SW_CFG2 0x0030
+#define DSIPHY_CMN_CAL_HW_CFG0 0x0034
+#define DSIPHY_CMN_CAL_HW_CFG1 0x0038
+#define DSIPHY_CMN_CAL_HW_CFG2 0x003C
+#define DSIPHY_CMN_CAL_HW_CFG3 0x0040
+#define DSIPHY_CMN_CAL_HW_CFG4 0x0044
+#define DSIPHY_CMN_PLL_CNTRL 0x0048
+#define DSIPHY_CMN_LDO_CNTRL 0x004C
+
+#define DSIPHY_CMN_REGULATOR_CAL_STATUS0 0x0064
+#define DSIPHY_CMN_REGULATOR_CAL_STATUS1 0x0068
+
+/* n = 0..3 for data lanes and n = 4 for clock lane */
+#define DSIPHY_DLNX_CFG0(n) (0x100 + ((n) * 0x80))
+#define DSIPHY_DLNX_CFG1(n) (0x104 + ((n) * 0x80))
+#define DSIPHY_DLNX_CFG2(n) (0x108 + ((n) * 0x80))
+#define DSIPHY_DLNX_CFG3(n) (0x10C + ((n) * 0x80))
+#define DSIPHY_DLNX_TEST_DATAPATH(n) (0x110 + ((n) * 0x80))
+#define DSIPHY_DLNX_TEST_STR(n) (0x114 + ((n) * 0x80))
+#define DSIPHY_DLNX_TIMING_CTRL_4(n) (0x118 + ((n) * 0x80))
+#define DSIPHY_DLNX_TIMING_CTRL_5(n) (0x11C + ((n) * 0x80))
+#define DSIPHY_DLNX_TIMING_CTRL_6(n) (0x120 + ((n) * 0x80))
+#define DSIPHY_DLNX_TIMING_CTRL_7(n) (0x124 + ((n) * 0x80))
+#define DSIPHY_DLNX_TIMING_CTRL_8(n) (0x128 + ((n) * 0x80))
+#define DSIPHY_DLNX_TIMING_CTRL_9(n) (0x12C + ((n) * 0x80))
+#define DSIPHY_DLNX_TIMING_CTRL_10(n) (0x130 + ((n) * 0x80))
+#define DSIPHY_DLNX_TIMING_CTRL_11(n) (0x134 + ((n) * 0x80))
+#define DSIPHY_DLNX_STRENGTH_CTRL_0(n) (0x138 + ((n) * 0x80))
+#define DSIPHY_DLNX_STRENGTH_CTRL_1(n) (0x13C + ((n) * 0x80))
+#define DSIPHY_DLNX_BIST_POLY(n) (0x140 + ((n) * 0x80))
+#define DSIPHY_DLNX_BIST_SEED0(n) (0x144 + ((n) * 0x80))
+#define DSIPHY_DLNX_BIST_SEED1(n) (0x148 + ((n) * 0x80))
+#define DSIPHY_DLNX_BIST_HEAD(n) (0x14C + ((n) * 0x80))
+#define DSIPHY_DLNX_BIST_SOT(n) (0x150 + ((n) * 0x80))
+#define DSIPHY_DLNX_BIST_CTRL0(n) (0x154 + ((n) * 0x80))
+#define DSIPHY_DLNX_BIST_CTRL1(n) (0x158 + ((n) * 0x80))
+#define DSIPHY_DLNX_BIST_CTRL2(n) (0x15C + ((n) * 0x80))
+#define DSIPHY_DLNX_BIST_CTRL3(n) (0x160 + ((n) * 0x80))
+#define DSIPHY_DLNX_VREG_CNTRL(n) (0x164 + ((n) * 0x80))
+#define DSIPHY_DLNX_HSTX_STR_STATUS(n) (0x168 + ((n) * 0x80))
+#define DSIPHY_DLNX_BIST_STATUS0(n) (0x16C + ((n) * 0x80))
+#define DSIPHY_DLNX_BIST_STATUS1(n) (0x170 + ((n) * 0x80))
+#define DSIPHY_DLNX_BIST_STATUS2(n) (0x174 + ((n) * 0x80))
+#define DSIPHY_DLNX_BIST_STATUS3(n) (0x178 + ((n) * 0x80))
+#define DSIPHY_DLNX_MISR_STATUS(n) (0x17C + ((n) * 0x80))
+
+#define DSIPHY_PLL_CLKBUFLR_EN 0x041C
+#define DSIPHY_PLL_PLL_BANDGAP 0x0508
+
+/**
+ * regulator_enable() - enable regulators for DSI PHY
+ * @phy: Pointer to DSI PHY hardware object.
+ * @reg_cfg: Regulator configuration for all DSI lanes.
+ */
+void dsi_phy_hw_v2_0_regulator_enable(struct dsi_phy_hw *phy,
+ struct dsi_phy_per_lane_cfgs *reg_cfg)
+{
+ int i;
+
+ for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++)
+ DSI_W32(phy, DSIPHY_DLNX_VREG_CNTRL(i), reg_cfg->lane[i][0]);
+
+ /* make sure all values are written to hardware */
+ wmb();
+
+ pr_debug("[DSI_%d] Phy regulators enabled\n", phy->index);
+}
+
+/**
+ * regulator_disable() - disable regulators
+ * @phy: Pointer to DSI PHY hardware object.
+ */
+void dsi_phy_hw_v2_0_regulator_disable(struct dsi_phy_hw *phy)
+{
+ pr_debug("[DSI_%d] Phy regulators disabled\n", phy->index);
+}
+
+/**
+ * enable() - Enable PHY hardware
+ * @phy: Pointer to DSI PHY hardware object.
+ * @cfg: Per lane configurations for timing, strength and lane
+ * configurations.
+ */
+void dsi_phy_hw_v2_0_enable(struct dsi_phy_hw *phy,
+ struct dsi_phy_cfg *cfg)
+{
+ int i;
+ struct dsi_phy_per_lane_cfgs *timing = &cfg->timing;
+ u32 data;
+
+ DSI_W32(phy, DSIPHY_CMN_LDO_CNTRL, 0x1C);
+
+ DSI_W32(phy, DSIPHY_CMN_GLBL_TEST_CTRL, 0x1);
+ for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) {
+
+ DSI_W32(phy, DSIPHY_DLNX_CFG0(i), cfg->lanecfg.lane[i][0]);
+ DSI_W32(phy, DSIPHY_DLNX_CFG1(i), cfg->lanecfg.lane[i][1]);
+ DSI_W32(phy, DSIPHY_DLNX_CFG2(i), cfg->lanecfg.lane[i][2]);
+ DSI_W32(phy, DSIPHY_DLNX_CFG3(i), cfg->lanecfg.lane[i][3]);
+
+ DSI_W32(phy, DSIPHY_DLNX_TEST_STR(i), 0x88);
+
+ DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_4(i), timing->lane[i][0]);
+ DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_5(i), timing->lane[i][1]);
+ DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_6(i), timing->lane[i][2]);
+ DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_7(i), timing->lane[i][3]);
+ DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_8(i), timing->lane[i][4]);
+ DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_9(i), timing->lane[i][5]);
+ DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_10(i), timing->lane[i][6]);
+ DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_11(i), timing->lane[i][7]);
+
+ DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_0(i),
+ cfg->strength.lane[i][0]);
+ DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_1(i),
+ cfg->strength.lane[i][1]);
+ }
+
+ /* make sure all values are written to hardware before enabling phy */
+ wmb();
+
+ DSI_W32(phy, DSIPHY_CMN_CTRL_1, 0x80);
+ udelay(100);
+ DSI_W32(phy, DSIPHY_CMN_CTRL_1, 0x00);
+
+ data = DSI_R32(phy, DSIPHY_CMN_GLBL_TEST_CTRL);
+
+ switch (cfg->pll_source) {
+ case DSI_PLL_SOURCE_STANDALONE:
+ DSI_W32(phy, DSIPHY_PLL_CLKBUFLR_EN, 0x01);
+ data &= ~BIT(2);
+ break;
+ case DSI_PLL_SOURCE_NATIVE:
+ DSI_W32(phy, DSIPHY_PLL_CLKBUFLR_EN, 0x03);
+ data &= ~BIT(2);
+ break;
+ case DSI_PLL_SOURCE_NON_NATIVE:
+ DSI_W32(phy, DSIPHY_PLL_CLKBUFLR_EN, 0x00);
+ data |= BIT(2);
+ break;
+ default:
+ break;
+ }
+
+ DSI_W32(phy, DSIPHY_CMN_GLBL_TEST_CTRL, data);
+
+ /* Enable bias current for pll1 during split display case */
+ if (cfg->pll_source == DSI_PLL_SOURCE_NON_NATIVE)
+ DSI_W32(phy, DSIPHY_PLL_PLL_BANDGAP, 0x3);
+
+ pr_debug("[DSI_%d]Phy enabled ", phy->index);
+}
+
+/**
+ * disable() - Disable PHY hardware
+ * @phy: Pointer to DSI PHY hardware object.
+ */
+void dsi_phy_hw_v2_0_disable(struct dsi_phy_hw *phy,
+ struct dsi_phy_cfg *cfg)
+{
+ DSI_W32(phy, DSIPHY_PLL_CLKBUFLR_EN, 0);
+ DSI_W32(phy, DSIPHY_CMN_GLBL_TEST_CTRL, 0);
+ DSI_W32(phy, DSIPHY_CMN_CTRL_0, 0);
+ pr_debug("[DSI_%d]Phy disabled ", phy->index);
+}
+
+/**
+ * dsi_phy_hw_v2_0_idle_on() - Enable DSI PHY hardware during idle screen
+ * @phy: Pointer to DSI PHY hardware object.
+ */
+void dsi_phy_hw_v2_0_idle_on(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg)
+{
+ int i = 0;
+
+ for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) {
+ DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_0(i),
+ cfg->strength.lane[i][0]);
+ DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_1(i),
+ cfg->strength.lane[i][1]);
+ }
+ wmb(); /* make sure write happens */
+ pr_debug("[DSI_%d]Phy enabled out of idle screen\n", phy->index);
+}
+
+
+/**
+ * dsi_phy_hw_v2_0_idle_off() - Disable DSI PHY hardware during idle screen
+ * @phy: Pointer to DSI PHY hardware object.
+ */
+void dsi_phy_hw_v2_0_idle_off(struct dsi_phy_hw *phy)
+{
+ int i = 0;
+
+ DSI_W32(phy, DSIPHY_CMN_CTRL_0, 0x7f);
+ for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++)
+ DSI_W32(phy, DSIPHY_DLNX_VREG_CNTRL(i), 0x1c);
+ DSI_W32(phy, DSIPHY_CMN_LDO_CNTRL, 0x1C);
+
+ for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++)
+ DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_1(i), 0x0);
+ wmb(); /* make sure write happens */
+ pr_debug("[DSI_%d]Phy disabled during idle screen\n", phy->index);
+}
+
+int dsi_phy_hw_timing_val_v2_0(struct dsi_phy_per_lane_cfgs *timing_cfg,
+ u32 *timing_val, u32 size)
+{
+ int i = 0, j = 0;
+
+ if (size != (DSI_LANE_MAX * DSI_MAX_SETTINGS)) {
+ pr_err("Unexpected timing array size %d\n", size);
+ return -EINVAL;
+ }
+
+ for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) {
+ for (j = 0; j < DSI_MAX_SETTINGS; j++) {
+ timing_cfg->lane[i][j] = *timing_val;
+ timing_val++;
+ }
+ }
+ return 0;
+}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
new file mode 100644
index 0000000..96f5c19
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "dsi-phy-hw:" fmt
+#include <linux/math64.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include "dsi_hw.h"
+#include "dsi_phy_hw.h"
+
+#define DSIPHY_CMN_CLK_CFG0 0x010
+#define DSIPHY_CMN_CLK_CFG1 0x014
+#define DSIPHY_CMN_GLBL_CTRL 0x018
+#define DSIPHY_CMN_RBUF_CTRL 0x01C
+#define DSIPHY_CMN_VREG_CTRL 0x020
+#define DSIPHY_CMN_CTRL_0 0x024
+#define DSIPHY_CMN_CTRL_1 0x028
+#define DSIPHY_CMN_CTRL_2 0x02C
+#define DSIPHY_CMN_LANE_CFG0 0x030
+#define DSIPHY_CMN_LANE_CFG1 0x034
+#define DSIPHY_CMN_PLL_CNTRL 0x038
+#define DSIPHY_CMN_LANE_CTRL0 0x098
+#define DSIPHY_CMN_LANE_CTRL1 0x09C
+#define DSIPHY_CMN_LANE_CTRL2 0x0A0
+#define DSIPHY_CMN_LANE_CTRL3 0x0A4
+#define DSIPHY_CMN_LANE_CTRL4 0x0A8
+#define DSIPHY_CMN_TIMING_CTRL_0 0x0AC
+#define DSIPHY_CMN_TIMING_CTRL_1 0x0B0
+#define DSIPHY_CMN_TIMING_CTRL_2 0x0B4
+#define DSIPHY_CMN_TIMING_CTRL_3 0x0B8
+#define DSIPHY_CMN_TIMING_CTRL_4 0x0BC
+#define DSIPHY_CMN_TIMING_CTRL_5 0x0C0
+#define DSIPHY_CMN_TIMING_CTRL_6 0x0C4
+#define DSIPHY_CMN_TIMING_CTRL_7 0x0C8
+#define DSIPHY_CMN_TIMING_CTRL_8 0x0CC
+#define DSIPHY_CMN_TIMING_CTRL_9 0x0D0
+#define DSIPHY_CMN_TIMING_CTRL_10 0x0D4
+#define DSIPHY_CMN_TIMING_CTRL_11 0x0D8
+#define DSIPHY_CMN_PHY_STATUS 0x0EC
+#define DSIPHY_CMN_LANE_STATUS0 0x0F4
+#define DSIPHY_CMN_LANE_STATUS1 0x0F8
+
+
+/* n = 0..3 for data lanes and n = 4 for clock lane */
+#define DSIPHY_LNX_CFG0(n) (0x200 + (0x80 * (n)))
+#define DSIPHY_LNX_CFG1(n) (0x204 + (0x80 * (n)))
+#define DSIPHY_LNX_CFG2(n) (0x208 + (0x80 * (n)))
+#define DSIPHY_LNX_CFG3(n) (0x20C + (0x80 * (n)))
+#define DSIPHY_LNX_TEST_DATAPATH(n) (0x210 + (0x80 * (n)))
+#define DSIPHY_LNX_PIN_SWAP(n) (0x214 + (0x80 * (n)))
+#define DSIPHY_LNX_HSTX_STR_CTRL(n) (0x218 + (0x80 * (n)))
+#define DSIPHY_LNX_OFFSET_TOP_CTRL(n) (0x21C + (0x80 * (n)))
+#define DSIPHY_LNX_OFFSET_BOT_CTRL(n) (0x220 + (0x80 * (n)))
+#define DSIPHY_LNX_LPTX_STR_CTRL(n) (0x224 + (0x80 * (n)))
+#define DSIPHY_LNX_LPRX_CTRL(n) (0x228 + (0x80 * (n)))
+#define DSIPHY_LNX_TX_DCTRL(n) (0x22C + (0x80 * (n)))
+
+static inline int dsi_conv_phy_to_logical_lane(
+ struct dsi_lane_map *lane_map, enum dsi_phy_data_lanes phy_lane)
+{
+ int i = 0;
+
+ if (phy_lane > DSI_PHYSICAL_LANE_3)
+ return -EINVAL;
+
+ for (i = DSI_LOGICAL_LANE_0; i < (DSI_LANE_MAX - 1); i++) {
+ if (lane_map->lane_map_v2[i] == phy_lane)
+ break;
+ }
+ return i;
+}
+
+static inline int dsi_conv_logical_to_phy_lane(
+ struct dsi_lane_map *lane_map, enum dsi_logical_lane lane)
+{
+ int i = 0;
+
+ if (lane > (DSI_LANE_MAX - 1))
+ return -EINVAL;
+
+ for (i = DSI_LOGICAL_LANE_0; i < (DSI_LANE_MAX - 1); i++) {
+ if (BIT(i) == lane_map->lane_map_v2[lane])
+ break;
+ }
+ return i;
+}
+
+/**
+ * regulator_enable() - enable regulators for DSI PHY
+ * @phy: Pointer to DSI PHY hardware object.
+ * @reg_cfg: Regulator configuration for all DSI lanes.
+ */
+void dsi_phy_hw_v3_0_regulator_enable(struct dsi_phy_hw *phy,
+ struct dsi_phy_per_lane_cfgs *reg_cfg)
+{
+ pr_debug("[DSI_%d] Phy regulators enabled\n", phy->index);
+ /* Nothing to be done for DSI PHY regulator enable */
+}
+
+/**
+ * regulator_disable() - disable regulators
+ * @phy: Pointer to DSI PHY hardware object.
+ */
+void dsi_phy_hw_v3_0_regulator_disable(struct dsi_phy_hw *phy)
+{
+ pr_debug("[DSI_%d] Phy regulators disabled\n", phy->index);
+ /* Nothing to be done for DSI PHY regulator disable */
+}
+
+
+static int dsi_phy_hw_v3_0_is_pll_on(struct dsi_phy_hw *phy)
+{
+ u32 data = 0;
+
+ data = DSI_R32(phy, DSIPHY_CMN_PLL_CNTRL);
+ mb(); /*make sure read happened */
+ return (data & BIT(0));
+}
+
+static void dsi_phy_hw_v3_0_config_lpcdrx(struct dsi_phy_hw *phy,
+ struct dsi_phy_cfg *cfg, bool enable)
+{
+ int phy_lane_0 = dsi_conv_logical_to_phy_lane(&cfg->lane_map,
+ DSI_LOGICAL_LANE_0);
+ /*
+ * LPRX and CDRX need to enabled only for physical data lane
+ * corresponding to the logical data lane 0
+ */
+
+ if (enable)
+ DSI_W32(phy, DSIPHY_LNX_LPRX_CTRL(phy_lane_0),
+ cfg->strength.lane[phy_lane_0][1]);
+ else
+ DSI_W32(phy, DSIPHY_LNX_LPRX_CTRL(phy_lane_0), 0);
+}
+
+static void dsi_phy_hw_v3_0_lane_swap_config(struct dsi_phy_hw *phy,
+ struct dsi_lane_map *lane_map)
+{
+ DSI_W32(phy, DSIPHY_CMN_LANE_CFG0,
+ (lane_map->lane_map_v2[DSI_LOGICAL_LANE_0] |
+ (lane_map->lane_map_v2[DSI_LOGICAL_LANE_1] << 4)));
+ DSI_W32(phy, DSIPHY_CMN_LANE_CFG1,
+ (lane_map->lane_map_v2[DSI_LOGICAL_LANE_2] |
+ (lane_map->lane_map_v2[DSI_LOGICAL_LANE_3] << 4)));
+}
+
+static void dsi_phy_hw_v3_0_lane_settings(struct dsi_phy_hw *phy,
+ struct dsi_phy_cfg *cfg)
+{
+ int i;
+ u8 tx_dctrl[] = {0x00, 0x00, 0x00, 0x02, 0x01};
+
+ /* Strength ctrl settings */
+ for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) {
+ DSI_W32(phy, DSIPHY_LNX_LPTX_STR_CTRL(i),
+ cfg->strength.lane[i][0]);
+ /*
+ * Disable LPRX and CDRX for all lanes. And later on, it will
+ * be only enabled for the physical data lane corresponding
+ * to the logical data lane 0
+ */
+ DSI_W32(phy, DSIPHY_LNX_LPRX_CTRL(i), 0);
+ DSI_W32(phy, DSIPHY_LNX_PIN_SWAP(i), 0x0);
+ DSI_W32(phy, DSIPHY_LNX_HSTX_STR_CTRL(i), 0x88);
+ }
+ dsi_phy_hw_v3_0_config_lpcdrx(phy, cfg, true);
+
+ /* other settings */
+ for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) {
+ DSI_W32(phy, DSIPHY_LNX_CFG0(i), cfg->lanecfg.lane[i][0]);
+ DSI_W32(phy, DSIPHY_LNX_CFG1(i), cfg->lanecfg.lane[i][1]);
+ DSI_W32(phy, DSIPHY_LNX_CFG2(i), cfg->lanecfg.lane[i][2]);
+ DSI_W32(phy, DSIPHY_LNX_CFG3(i), cfg->lanecfg.lane[i][3]);
+ DSI_W32(phy, DSIPHY_LNX_OFFSET_TOP_CTRL(i), 0x0);
+ DSI_W32(phy, DSIPHY_LNX_OFFSET_BOT_CTRL(i), 0x0);
+ DSI_W32(phy, DSIPHY_LNX_TX_DCTRL(i), tx_dctrl[i]);
+ }
+}
+
+/**
+ * enable() - Enable PHY hardware
+ * @phy: Pointer to DSI PHY hardware object.
+ * @cfg: Per lane configurations for timing, strength and lane
+ * configurations.
+ */
+void dsi_phy_hw_v3_0_enable(struct dsi_phy_hw *phy,
+ struct dsi_phy_cfg *cfg)
+{
+ int rc = 0;
+ u32 status;
+ u32 const delay_us = 5;
+ u32 const timeout_us = 1000;
+ struct dsi_phy_per_lane_cfgs *timing = &cfg->timing;
+ u32 data;
+
+ if (dsi_phy_hw_v3_0_is_pll_on(phy))
+ pr_warn("PLL turned on before configuring PHY\n");
+
+ /* wait for REFGEN READY */
+ rc = readl_poll_timeout_atomic(phy->base + DSIPHY_CMN_PHY_STATUS,
+ status, (status & BIT(0)), delay_us, timeout_us);
+ if (rc) {
+ pr_err("Ref gen not ready. Aborting\n");
+ return;
+ }
+
+ /* de-assert digital and pll power down */
+ data = BIT(6) | BIT(5);
+ DSI_W32(phy, DSIPHY_CMN_CTRL_0, data);
+
+ /* Assert PLL core reset */
+ DSI_W32(phy, DSIPHY_CMN_PLL_CNTRL, 0x00);
+
+ /* turn off resync FIFO */
+ DSI_W32(phy, DSIPHY_CMN_RBUF_CTRL, 0x00);
+
+ /* Select MS1 byte-clk */
+ DSI_W32(phy, DSIPHY_CMN_GLBL_CTRL, 0x10);
+
+ /* Enable LDO */
+ DSI_W32(phy, DSIPHY_CMN_VREG_CTRL, 0x59);
+
+ /* Configure PHY lane swap */
+ dsi_phy_hw_v3_0_lane_swap_config(phy, &cfg->lane_map);
+
+ /* DSI PHY timings */
+ DSI_W32(phy, DSIPHY_CMN_TIMING_CTRL_0, timing->lane_v3[0]);
+ DSI_W32(phy, DSIPHY_CMN_TIMING_CTRL_1, timing->lane_v3[1]);
+ DSI_W32(phy, DSIPHY_CMN_TIMING_CTRL_2, timing->lane_v3[2]);
+ DSI_W32(phy, DSIPHY_CMN_TIMING_CTRL_3, timing->lane_v3[3]);
+ DSI_W32(phy, DSIPHY_CMN_TIMING_CTRL_4, timing->lane_v3[4]);
+ DSI_W32(phy, DSIPHY_CMN_TIMING_CTRL_5, timing->lane_v3[5]);
+ DSI_W32(phy, DSIPHY_CMN_TIMING_CTRL_6, timing->lane_v3[6]);
+ DSI_W32(phy, DSIPHY_CMN_TIMING_CTRL_7, timing->lane_v3[7]);
+ DSI_W32(phy, DSIPHY_CMN_TIMING_CTRL_8, timing->lane_v3[8]);
+ DSI_W32(phy, DSIPHY_CMN_TIMING_CTRL_9, timing->lane_v3[9]);
+ DSI_W32(phy, DSIPHY_CMN_TIMING_CTRL_10, timing->lane_v3[10]);
+ DSI_W32(phy, DSIPHY_CMN_TIMING_CTRL_11, timing->lane_v3[11]);
+
+ /* Remove power down from all blocks */
+ DSI_W32(phy, DSIPHY_CMN_CTRL_0, 0x7f);
+
+ /*power up lanes */
+ data = DSI_R32(phy, DSIPHY_CMN_CTRL_0);
+ /* TODO: only power up lanes that are used */
+ data |= 0x1F;
+ DSI_W32(phy, DSIPHY_CMN_CTRL_0, data);
+ DSI_W32(phy, DSIPHY_CMN_LANE_CTRL0, 0x1F);
+
+ /* Select full-rate mode */
+ DSI_W32(phy, DSIPHY_CMN_CTRL_2, 0x40);
+
+ switch (cfg->pll_source) {
+ case DSI_PLL_SOURCE_STANDALONE:
+ case DSI_PLL_SOURCE_NATIVE:
+ data = 0x0; /* internal PLL */
+ break;
+ case DSI_PLL_SOURCE_NON_NATIVE:
+ data = 0x1; /* external PLL */
+ break;
+ default:
+ break;
+ }
+ DSI_W32(phy, DSIPHY_CMN_CLK_CFG1, (data << 2)); /* set PLL src */
+
+ /* DSI lane settings */
+ dsi_phy_hw_v3_0_lane_settings(phy, cfg);
+
+ pr_debug("[DSI_%d]Phy enabled ", phy->index);
+}
+
+/**
+ * disable() - Disable PHY hardware
+ * @phy: Pointer to DSI PHY hardware object.
+ */
+void dsi_phy_hw_v3_0_disable(struct dsi_phy_hw *phy,
+ struct dsi_phy_cfg *cfg)
+{
+ u32 data = 0;
+
+ if (dsi_phy_hw_v3_0_is_pll_on(phy))
+ pr_warn("Turning OFF PHY while PLL is on\n");
+
+ dsi_phy_hw_v3_0_config_lpcdrx(phy, cfg, false);
+
+ data = DSI_R32(phy, DSIPHY_CMN_CTRL_0);
+ /* disable all lanes */
+ data &= ~0x1F;
+ DSI_W32(phy, DSIPHY_CMN_CTRL_0, data);
+ DSI_W32(phy, DSIPHY_CMN_LANE_CTRL0, 0);
+
+ /* Turn off all PHY blocks */
+ DSI_W32(phy, DSIPHY_CMN_CTRL_0, 0x00);
+ /* make sure phy is turned off */
+ wmb();
+ pr_debug("[DSI_%d]Phy disabled ", phy->index);
+}
+
+int dsi_phy_hw_v3_0_wait_for_lane_idle(
+ struct dsi_phy_hw *phy, u32 lanes)
+{
+ int rc = 0, val = 0;
+ u32 stop_state_mask = 0;
+ u32 const sleep_us = 10;
+ u32 const timeout_us = 100;
+
+ stop_state_mask = BIT(4); /* clock lane */
+ if (lanes & DSI_DATA_LANE_0)
+ stop_state_mask |= BIT(0);
+ if (lanes & DSI_DATA_LANE_1)
+ stop_state_mask |= BIT(1);
+ if (lanes & DSI_DATA_LANE_2)
+ stop_state_mask |= BIT(2);
+ if (lanes & DSI_DATA_LANE_3)
+ stop_state_mask |= BIT(3);
+
+ pr_debug("%s: polling for lanes to be in stop state, mask=0x%08x\n",
+ __func__, stop_state_mask);
+ rc = readl_poll_timeout(phy->base + DSIPHY_CMN_LANE_STATUS1, val,
+ (val == stop_state_mask), sleep_us, timeout_us);
+ if (rc) {
+ pr_err("%s: lanes not in stop state, LANE_STATUS=0x%08x\n",
+ __func__, val);
+ return rc;
+ }
+
+ return 0;
+}
+
+void dsi_phy_hw_v3_0_ulps_request(struct dsi_phy_hw *phy,
+ struct dsi_phy_cfg *cfg, u32 lanes)
+{
+ u32 reg = 0;
+
+ if (lanes & DSI_CLOCK_LANE)
+ reg = BIT(4);
+ if (lanes & DSI_DATA_LANE_0)
+ reg |= BIT(0);
+ if (lanes & DSI_DATA_LANE_1)
+ reg |= BIT(1);
+ if (lanes & DSI_DATA_LANE_2)
+ reg |= BIT(2);
+ if (lanes & DSI_DATA_LANE_3)
+ reg |= BIT(3);
+
+ /*
+ * ULPS entry request. Wait for short time to make sure
+ * that the lanes enter ULPS. Recommended as per HPG.
+ */
+ DSI_W32(phy, DSIPHY_CMN_LANE_CTRL1, reg);
+ usleep_range(100, 110);
+
+ /* disable LPRX and CDRX */
+ dsi_phy_hw_v3_0_config_lpcdrx(phy, cfg, false);
+ /* disable lane LDOs */
+ DSI_W32(phy, DSIPHY_CMN_VREG_CTRL, 0x19);
+ pr_debug("[DSI_PHY%d] ULPS requested for lanes 0x%x\n", phy->index,
+ lanes);
+}
+
+void dsi_phy_hw_v3_0_ulps_exit(struct dsi_phy_hw *phy,
+ struct dsi_phy_cfg *cfg, u32 lanes)
+{
+ u32 reg = 0;
+
+ if (lanes & DSI_CLOCK_LANE)
+ reg = BIT(4);
+ if (lanes & DSI_DATA_LANE_0)
+ reg |= BIT(0);
+ if (lanes & DSI_DATA_LANE_1)
+ reg |= BIT(1);
+ if (lanes & DSI_DATA_LANE_2)
+ reg |= BIT(2);
+ if (lanes & DSI_DATA_LANE_3)
+ reg |= BIT(3);
+
+ /* enable lane LDOs */
+ DSI_W32(phy, DSIPHY_CMN_VREG_CTRL, 0x59);
+ /* enable LPRX and CDRX */
+ dsi_phy_hw_v3_0_config_lpcdrx(phy, cfg, true);
+
+ /* ULPS exit request */
+ DSI_W32(phy, DSIPHY_CMN_LANE_CTRL2, reg);
+ usleep_range(1000, 1010);
+
+ /* Clear ULPS request flags on all lanes */
+ DSI_W32(phy, DSIPHY_CMN_LANE_CTRL1, 0);
+ /* Clear ULPS exit flags on all lanes */
+ DSI_W32(phy, DSIPHY_CMN_LANE_CTRL2, 0);
+
+ /*
+ * Sometimes when exiting ULPS, it is possible that some DSI
+ * lanes are not in the stop state which could lead to DSI
+ * commands not going through. To avoid this, force the lanes
+ * to be in stop state.
+ */
+ DSI_W32(phy, DSIPHY_CMN_LANE_CTRL3, reg);
+ DSI_W32(phy, DSIPHY_CMN_LANE_CTRL3, 0);
+ usleep_range(100, 110);
+}
+
+u32 dsi_phy_hw_v3_0_get_lanes_in_ulps(struct dsi_phy_hw *phy)
+{
+ u32 lanes = 0;
+
+ lanes = DSI_R32(phy, DSIPHY_CMN_LANE_STATUS0);
+ pr_debug("[DSI_PHY%d] lanes in ulps = 0x%x\n", phy->index, lanes);
+ return lanes;
+}
+
+int dsi_phy_hw_timing_val_v3_0(struct dsi_phy_per_lane_cfgs *timing_cfg,
+ u32 *timing_val, u32 size)
+{
+ int i = 0;
+
+ if (size != DSI_PHY_TIMING_V3_SIZE) {
+ pr_err("Unexpected timing array size %d\n", size);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < size; i++)
+ timing_cfg->lane_v3[i] = timing_val[i];
+ return 0;
+}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v4_0.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v4_0.c
deleted file mode 100644
index 512352d..0000000
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v4_0.c
+++ /dev/null
@@ -1,858 +0,0 @@
-/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#define pr_fmt(fmt) "dsi-phy-hw:" fmt
-#include <linux/math64.h>
-#include <linux/delay.h>
-#include "dsi_hw.h"
-#include "dsi_phy_hw.h"
-
-#define DSIPHY_CMN_REVISION_ID0 0x0000
-#define DSIPHY_CMN_REVISION_ID1 0x0004
-#define DSIPHY_CMN_REVISION_ID2 0x0008
-#define DSIPHY_CMN_REVISION_ID3 0x000C
-#define DSIPHY_CMN_CLK_CFG0 0x0010
-#define DSIPHY_CMN_CLK_CFG1 0x0014
-#define DSIPHY_CMN_GLBL_TEST_CTRL 0x0018
-#define DSIPHY_CMN_CTRL_0 0x001C
-#define DSIPHY_CMN_CTRL_1 0x0020
-#define DSIPHY_CMN_CAL_HW_TRIGGER 0x0024
-#define DSIPHY_CMN_CAL_SW_CFG0 0x0028
-#define DSIPHY_CMN_CAL_SW_CFG1 0x002C
-#define DSIPHY_CMN_CAL_SW_CFG2 0x0030
-#define DSIPHY_CMN_CAL_HW_CFG0 0x0034
-#define DSIPHY_CMN_CAL_HW_CFG1 0x0038
-#define DSIPHY_CMN_CAL_HW_CFG2 0x003C
-#define DSIPHY_CMN_CAL_HW_CFG3 0x0040
-#define DSIPHY_CMN_CAL_HW_CFG4 0x0044
-#define DSIPHY_CMN_PLL_CNTRL 0x0048
-#define DSIPHY_CMN_LDO_CNTRL 0x004C
-
-#define DSIPHY_CMN_REGULATOR_CAL_STATUS0 0x0064
-#define DSIPHY_CMN_REGULATOR_CAL_STATUS1 0x0068
-
-/* n = 0..3 for data lanes and n = 4 for clock lane */
-#define DSIPHY_DLNX_CFG0(n) (0x100 + ((n) * 0x80))
-#define DSIPHY_DLNX_CFG1(n) (0x104 + ((n) * 0x80))
-#define DSIPHY_DLNX_CFG2(n) (0x108 + ((n) * 0x80))
-#define DSIPHY_DLNX_CFG3(n) (0x10C + ((n) * 0x80))
-#define DSIPHY_DLNX_TEST_DATAPATH(n) (0x110 + ((n) * 0x80))
-#define DSIPHY_DLNX_TEST_STR(n) (0x114 + ((n) * 0x80))
-#define DSIPHY_DLNX_TIMING_CTRL_4(n) (0x118 + ((n) * 0x80))
-#define DSIPHY_DLNX_TIMING_CTRL_5(n) (0x11C + ((n) * 0x80))
-#define DSIPHY_DLNX_TIMING_CTRL_6(n) (0x120 + ((n) * 0x80))
-#define DSIPHY_DLNX_TIMING_CTRL_7(n) (0x124 + ((n) * 0x80))
-#define DSIPHY_DLNX_TIMING_CTRL_8(n) (0x128 + ((n) * 0x80))
-#define DSIPHY_DLNX_TIMING_CTRL_9(n) (0x12C + ((n) * 0x80))
-#define DSIPHY_DLNX_TIMING_CTRL_10(n) (0x130 + ((n) * 0x80))
-#define DSIPHY_DLNX_TIMING_CTRL_11(n) (0x134 + ((n) * 0x80))
-#define DSIPHY_DLNX_STRENGTH_CTRL_0(n) (0x138 + ((n) * 0x80))
-#define DSIPHY_DLNX_STRENGTH_CTRL_1(n) (0x13C + ((n) * 0x80))
-#define DSIPHY_DLNX_BIST_POLY(n) (0x140 + ((n) * 0x80))
-#define DSIPHY_DLNX_BIST_SEED0(n) (0x144 + ((n) * 0x80))
-#define DSIPHY_DLNX_BIST_SEED1(n) (0x148 + ((n) * 0x80))
-#define DSIPHY_DLNX_BIST_HEAD(n) (0x14C + ((n) * 0x80))
-#define DSIPHY_DLNX_BIST_SOT(n) (0x150 + ((n) * 0x80))
-#define DSIPHY_DLNX_BIST_CTRL0(n) (0x154 + ((n) * 0x80))
-#define DSIPHY_DLNX_BIST_CTRL1(n) (0x158 + ((n) * 0x80))
-#define DSIPHY_DLNX_BIST_CTRL2(n) (0x15C + ((n) * 0x80))
-#define DSIPHY_DLNX_BIST_CTRL3(n) (0x160 + ((n) * 0x80))
-#define DSIPHY_DLNX_VREG_CNTRL(n) (0x164 + ((n) * 0x80))
-#define DSIPHY_DLNX_HSTX_STR_STATUS(n) (0x168 + ((n) * 0x80))
-#define DSIPHY_DLNX_BIST_STATUS0(n) (0x16C + ((n) * 0x80))
-#define DSIPHY_DLNX_BIST_STATUS1(n) (0x170 + ((n) * 0x80))
-#define DSIPHY_DLNX_BIST_STATUS2(n) (0x174 + ((n) * 0x80))
-#define DSIPHY_DLNX_BIST_STATUS3(n) (0x178 + ((n) * 0x80))
-#define DSIPHY_DLNX_MISR_STATUS(n) (0x17C + ((n) * 0x80))
-
-#define DSIPHY_PLL_CLKBUFLR_EN 0x041C
-#define DSIPHY_PLL_PLL_BANDGAP 0x0508
-
-/**
- * struct timing_entry - Calculated values for each timing parameter.
- * @mipi_min:
- * @mipi_max:
- * @rec_min:
- * @rec_max:
- * @rec:
- * @reg_value: Value to be programmed in register.
- */
-struct timing_entry {
- s32 mipi_min;
- s32 mipi_max;
- s32 rec_min;
- s32 rec_max;
- s32 rec;
- u8 reg_value;
-};
-
-/**
- * struct phy_timing_desc - Timing parameters for DSI PHY.
- */
-struct phy_timing_desc {
- struct timing_entry clk_prepare;
- struct timing_entry clk_zero;
- struct timing_entry clk_trail;
- struct timing_entry hs_prepare;
- struct timing_entry hs_zero;
- struct timing_entry hs_trail;
- struct timing_entry hs_rqst;
- struct timing_entry hs_rqst_clk;
- struct timing_entry hs_exit;
- struct timing_entry ta_go;
- struct timing_entry ta_sure;
- struct timing_entry ta_set;
- struct timing_entry clk_post;
- struct timing_entry clk_pre;
-};
-
-/**
- * struct phy_clk_params - Clock parameters for PHY timing calculations.
- */
-struct phy_clk_params {
- u32 bitclk_mbps;
- u32 escclk_numer;
- u32 escclk_denom;
- u32 tlpx_numer_ns;
- u32 treot_ns;
-};
-
-/**
- * regulator_enable() - enable regulators for DSI PHY
- * @phy: Pointer to DSI PHY hardware object.
- * @reg_cfg: Regulator configuration for all DSI lanes.
- */
-void dsi_phy_hw_v4_0_regulator_enable(struct dsi_phy_hw *phy,
- struct dsi_phy_per_lane_cfgs *reg_cfg)
-{
- int i;
-
- for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++)
- DSI_W32(phy, DSIPHY_DLNX_VREG_CNTRL(i), reg_cfg->lane[i][0]);
-
- /* make sure all values are written to hardware */
- wmb();
-
- pr_debug("[DSI_%d] Phy regulators enabled\n", phy->index);
-}
-
-/**
- * regulator_disable() - disable regulators
- * @phy: Pointer to DSI PHY hardware object.
- */
-void dsi_phy_hw_v4_0_regulator_disable(struct dsi_phy_hw *phy)
-{
- pr_debug("[DSI_%d] Phy regulators disabled\n", phy->index);
-}
-
-/**
- * enable() - Enable PHY hardware
- * @phy: Pointer to DSI PHY hardware object.
- * @cfg: Per lane configurations for timing, strength and lane
- * configurations.
- */
-void dsi_phy_hw_v4_0_enable(struct dsi_phy_hw *phy,
- struct dsi_phy_cfg *cfg)
-{
- int i;
- struct dsi_phy_per_lane_cfgs *timing = &cfg->timing;
- u32 data;
-
- DSI_W32(phy, DSIPHY_CMN_LDO_CNTRL, 0x1C);
-
- DSI_W32(phy, DSIPHY_CMN_GLBL_TEST_CTRL, 0x1);
- for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) {
-
- DSI_W32(phy, DSIPHY_DLNX_CFG0(i), cfg->lanecfg.lane[i][0]);
- DSI_W32(phy, DSIPHY_DLNX_CFG1(i), cfg->lanecfg.lane[i][1]);
- DSI_W32(phy, DSIPHY_DLNX_CFG2(i), cfg->lanecfg.lane[i][2]);
- DSI_W32(phy, DSIPHY_DLNX_CFG3(i), cfg->lanecfg.lane[i][3]);
-
- DSI_W32(phy, DSIPHY_DLNX_TEST_STR(i), 0x88);
-
- DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_4(i), timing->lane[i][0]);
- DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_5(i), timing->lane[i][1]);
- DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_6(i), timing->lane[i][2]);
- DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_7(i), timing->lane[i][3]);
- DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_8(i), timing->lane[i][4]);
- DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_9(i), timing->lane[i][5]);
- DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_10(i), timing->lane[i][6]);
- DSI_W32(phy, DSIPHY_DLNX_TIMING_CTRL_11(i), timing->lane[i][7]);
-
- DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_0(i),
- cfg->strength.lane[i][0]);
- DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_1(i),
- cfg->strength.lane[i][1]);
- }
-
- /* make sure all values are written to hardware before enabling phy */
- wmb();
-
- DSI_W32(phy, DSIPHY_CMN_CTRL_1, 0x80);
- udelay(100);
- DSI_W32(phy, DSIPHY_CMN_CTRL_1, 0x00);
-
- data = DSI_R32(phy, DSIPHY_CMN_GLBL_TEST_CTRL);
-
- switch (cfg->pll_source) {
- case DSI_PLL_SOURCE_STANDALONE:
- DSI_W32(phy, DSIPHY_PLL_CLKBUFLR_EN, 0x01);
- data &= ~BIT(2);
- break;
- case DSI_PLL_SOURCE_NATIVE:
- DSI_W32(phy, DSIPHY_PLL_CLKBUFLR_EN, 0x03);
- data &= ~BIT(2);
- break;
- case DSI_PLL_SOURCE_NON_NATIVE:
- DSI_W32(phy, DSIPHY_PLL_CLKBUFLR_EN, 0x00);
- data |= BIT(2);
- break;
- default:
- break;
- }
-
- DSI_W32(phy, DSIPHY_CMN_GLBL_TEST_CTRL, data);
-
- /* Enable bias current for pll1 during split display case */
- if (cfg->pll_source == DSI_PLL_SOURCE_NON_NATIVE)
- DSI_W32(phy, DSIPHY_PLL_PLL_BANDGAP, 0x3);
-
- pr_debug("[DSI_%d]Phy enabled ", phy->index);
-}
-
-/**
- * disable() - Disable PHY hardware
- * @phy: Pointer to DSI PHY hardware object.
- */
-void dsi_phy_hw_v4_0_disable(struct dsi_phy_hw *phy)
-{
- DSI_W32(phy, DSIPHY_PLL_CLKBUFLR_EN, 0);
- DSI_W32(phy, DSIPHY_CMN_GLBL_TEST_CTRL, 0);
- DSI_W32(phy, DSIPHY_CMN_CTRL_0, 0);
- pr_debug("[DSI_%d]Phy disabled ", phy->index);
-}
-
-static const u32 bits_per_pixel[DSI_PIXEL_FORMAT_MAX] = {
- 16, 18, 18, 24, 3, 8, 12 };
-
-/**
- * calc_clk_prepare - calculates prepare timing params for clk lane.
- */
-static int calc_clk_prepare(struct phy_clk_params *clk_params,
- struct phy_timing_desc *desc,
- s32 *actual_frac,
- s64 *actual_intermediate)
-{
- u32 const min_prepare_frac = 50;
- u64 const multiplier = BIT(20);
-
- struct timing_entry *t = &desc->clk_prepare;
- int rc = 0;
- u64 dividend, temp, temp_multiple;
- s32 frac = 0;
- s64 intermediate;
- s64 clk_prep_actual;
-
- dividend = ((t->rec_max - t->rec_min) * min_prepare_frac * multiplier);
- temp = roundup(div_s64(dividend, 100), multiplier);
- temp += (t->rec_min * multiplier);
- t->rec = div_s64(temp, multiplier);
-
- if (t->rec & 0xffffff00) {
- pr_err("Incorrect rec valuefor clk_prepare\n");
- rc = -EINVAL;
- } else {
- t->reg_value = t->rec;
- }
-
- /* calculate theoretical value */
- temp_multiple = 8 * t->reg_value * clk_params->tlpx_numer_ns
- * multiplier;
- intermediate = div_s64(temp_multiple, clk_params->bitclk_mbps);
- div_s64_rem(temp_multiple, clk_params->bitclk_mbps, &frac);
- clk_prep_actual = div_s64((intermediate + frac), multiplier);
-
- pr_debug("CLK_PREPARE:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d",
- t->mipi_min, t->mipi_max, t->rec_min, t->rec_max);
- pr_debug(" reg_value=%d, actual=%lld\n", t->reg_value, clk_prep_actual);
-
- *actual_frac = frac;
- *actual_intermediate = intermediate;
-
- return rc;
-}
-
-/**
- * calc_clk_zero - calculates zero timing params for clk lane.
- */
-static int calc_clk_zero(struct phy_clk_params *clk_params,
- struct phy_timing_desc *desc,
- s32 actual_frac,
- s64 actual_intermediate)
-{
- u32 const clk_zero_min_frac = 2;
- u64 const multiplier = BIT(20);
-
- int rc = 0;
- struct timing_entry *t = &desc->clk_zero;
- s64 mipi_min, rec_temp1, rec_temp2, rec_temp3, rec_min;
-
- mipi_min = ((300 * multiplier) - (actual_intermediate + actual_frac));
- t->mipi_min = div_s64(mipi_min, multiplier);
-
- rec_temp1 = div_s64((mipi_min * clk_params->bitclk_mbps),
- clk_params->tlpx_numer_ns);
- rec_temp2 = (rec_temp1 - (11 * multiplier));
- rec_temp3 = roundup(div_s64(rec_temp2, 8), multiplier);
- rec_min = (div_s64(rec_temp3, multiplier) - 3);
- t->rec_min = rec_min;
- t->rec_max = ((t->rec_min > 255) ? 511 : 255);
-
- t->rec = DIV_ROUND_UP(
- (((t->rec_max - t->rec_min) * clk_zero_min_frac) +
- (t->rec_min * 100)),
- 100);
-
- if (t->rec & 0xffffff00) {
- pr_err("Incorrect rec valuefor clk_zero\n");
- rc = -EINVAL;
- } else {
- t->reg_value = t->rec;
- }
-
- pr_debug("CLK_ZERO:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d, reg_val=%d\n",
- t->mipi_min, t->mipi_max, t->rec_min, t->rec_max,
- t->reg_value);
- return rc;
-}
-
-/**
- * calc_clk_trail - calculates prepare trail params for clk lane.
- */
-static int calc_clk_trail(struct phy_clk_params *clk_params,
- struct phy_timing_desc *desc,
- s64 *teot_clk_lane)
-{
- u64 const multiplier = BIT(20);
- u32 const phy_timing_frac = 30;
-
- int rc = 0;
- struct timing_entry *t = &desc->clk_trail;
- u64 temp_multiple;
- s32 frac;
- s64 mipi_max_tr, rec_temp1, rec_temp2, rec_temp3, mipi_max;
- s64 teot_clk_lane1;
-
- temp_multiple = div_s64(
- (12 * multiplier * clk_params->tlpx_numer_ns),
- clk_params->bitclk_mbps);
- div_s64_rem(temp_multiple, multiplier, &frac);
-
- mipi_max_tr = ((105 * multiplier) +
- (temp_multiple + frac));
- teot_clk_lane1 = div_s64(mipi_max_tr, multiplier);
-
- mipi_max = (mipi_max_tr - (clk_params->treot_ns * multiplier));
- t->mipi_max = div_s64(mipi_max, multiplier);
-
- temp_multiple = div_s64(
- (t->mipi_min * multiplier * clk_params->bitclk_mbps),
- clk_params->tlpx_numer_ns);
-
- div_s64_rem(temp_multiple, multiplier, &frac);
- rec_temp1 = temp_multiple + frac + (3 * multiplier);
- rec_temp2 = div_s64(rec_temp1, 8);
- rec_temp3 = roundup(rec_temp2, multiplier);
-
- t->rec_min = div_s64(rec_temp3, multiplier);
-
- /* recommended max */
- rec_temp1 = div_s64((mipi_max * clk_params->bitclk_mbps),
- clk_params->tlpx_numer_ns);
- rec_temp2 = rec_temp1 + (3 * multiplier);
- rec_temp3 = rec_temp2 / 8;
- t->rec_max = div_s64(rec_temp3, multiplier);
-
- t->rec = DIV_ROUND_UP(
- (((t->rec_max - t->rec_min) * phy_timing_frac) +
- (t->rec_min * 100)),
- 100);
-
- if (t->rec & 0xffffff00) {
- pr_err("Incorrect rec valuefor clk_zero\n");
- rc = -EINVAL;
- } else {
- t->reg_value = t->rec;
- }
-
- *teot_clk_lane = teot_clk_lane1;
- pr_debug("CLK_TRAIL:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d, reg_val=%d\n",
- t->mipi_min, t->mipi_max, t->rec_min, t->rec_max,
- t->reg_value);
- return rc;
-
-}
-
-/**
- * calc_hs_prepare - calculates prepare timing params for data lanes in HS.
- */
-static int calc_hs_prepare(struct phy_clk_params *clk_params,
- struct phy_timing_desc *desc,
- u64 *temp_mul)
-{
- u64 const multiplier = BIT(20);
- u32 const min_prepare_frac = 50;
- int rc = 0;
- struct timing_entry *t = &desc->hs_prepare;
- u64 temp_multiple, dividend, temp;
- s32 frac;
- s64 rec_temp1, rec_temp2, mipi_max, mipi_min;
- u32 low_clk_multiplier = 0;
-
- if (clk_params->bitclk_mbps <= 120)
- low_clk_multiplier = 2;
- /* mipi min */
- temp_multiple = div_s64((4 * multiplier * clk_params->tlpx_numer_ns),
- clk_params->bitclk_mbps);
- div_s64_rem(temp_multiple, multiplier, &frac);
- mipi_min = (40 * multiplier) + (temp_multiple + frac);
- t->mipi_min = div_s64(mipi_min, multiplier);
-
- /* mipi_max */
- temp_multiple = div_s64(
- (6 * multiplier * clk_params->tlpx_numer_ns),
- clk_params->bitclk_mbps);
- div_s64_rem(temp_multiple, multiplier, &frac);
- mipi_max = (85 * multiplier) + temp_multiple;
- t->mipi_max = div_s64(mipi_max, multiplier);
-
- /* recommended min */
- temp_multiple = div_s64((mipi_min * clk_params->bitclk_mbps),
- clk_params->tlpx_numer_ns);
- temp_multiple -= (low_clk_multiplier * multiplier);
- div_s64_rem(temp_multiple, multiplier, &frac);
- rec_temp1 = roundup(((temp_multiple + frac) / 8), multiplier);
- t->rec_min = div_s64(rec_temp1, multiplier);
-
- /* recommended max */
- temp_multiple = div_s64((mipi_max * clk_params->bitclk_mbps),
- clk_params->tlpx_numer_ns);
- temp_multiple -= (low_clk_multiplier * multiplier);
- div_s64_rem(temp_multiple, multiplier, &frac);
- rec_temp2 = rounddown((temp_multiple / 8), multiplier);
- t->rec_max = div_s64(rec_temp2, multiplier);
-
- /* register value */
- dividend = ((rec_temp2 - rec_temp1) * min_prepare_frac);
- temp = roundup(div_u64(dividend, 100), multiplier);
- t->rec = div_s64((temp + rec_temp1), multiplier);
-
- if (t->rec & 0xffffff00) {
- pr_err("Incorrect rec valuefor hs_prepare\n");
- rc = -EINVAL;
- } else {
- t->reg_value = t->rec;
- }
-
- temp_multiple = div_s64(
- (8 * (temp + rec_temp1) * clk_params->tlpx_numer_ns),
- clk_params->bitclk_mbps);
-
- *temp_mul = temp_multiple;
- pr_debug("HS_PREP:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d, reg_val=%d\n",
- t->mipi_min, t->mipi_max, t->rec_min, t->rec_max,
- t->reg_value);
- return rc;
-}
-
-/**
- * calc_hs_zero - calculates zero timing params for data lanes in HS.
- */
-static int calc_hs_zero(struct phy_clk_params *clk_params,
- struct phy_timing_desc *desc,
- u64 temp_multiple)
-{
- u32 const hs_zero_min_frac = 10;
- u64 const multiplier = BIT(20);
- int rc = 0;
- struct timing_entry *t = &desc->hs_zero;
- s64 rec_temp1, rec_temp2, rec_temp3, mipi_min;
- s64 rec_min;
-
- mipi_min = div_s64((10 * clk_params->tlpx_numer_ns * multiplier),
- clk_params->bitclk_mbps);
- rec_temp1 = (145 * multiplier) + mipi_min - temp_multiple;
- t->mipi_min = div_s64(rec_temp1, multiplier);
-
- /* recommended min */
- rec_temp1 = div_s64((rec_temp1 * clk_params->bitclk_mbps),
- clk_params->tlpx_numer_ns);
- rec_temp2 = rec_temp1 - (11 * multiplier);
- rec_temp3 = roundup((rec_temp2 / 8), multiplier);
- rec_min = rec_temp3 - (3 * multiplier);
- t->rec_min = div_s64(rec_min, multiplier);
- t->rec_max = ((t->rec_min > 255) ? 511 : 255);
-
- t->rec = DIV_ROUND_UP(
- (((t->rec_max - t->rec_min) * hs_zero_min_frac) +
- (t->rec_min * 100)),
- 100);
-
- if (t->rec & 0xffffff00) {
- pr_err("Incorrect rec valuefor hs_zero\n");
- rc = -EINVAL;
- } else {
- t->reg_value = t->rec;
- }
-
- pr_debug("HS_ZERO:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d, reg_val=%d\n",
- t->mipi_min, t->mipi_max, t->rec_min, t->rec_max,
- t->reg_value);
-
- return rc;
-}
-
-/**
- * calc_hs_trail - calculates trail timing params for data lanes in HS.
- */
-static int calc_hs_trail(struct phy_clk_params *clk_params,
- struct phy_timing_desc *desc,
- u64 teot_clk_lane)
-{
- u32 const phy_timing_frac = 30;
- int rc = 0;
- struct timing_entry *t = &desc->hs_trail;
- s64 rec_temp1;
-
- t->mipi_min = 60 +
- mult_frac(clk_params->tlpx_numer_ns, 4,
- clk_params->bitclk_mbps);
-
- t->mipi_max = teot_clk_lane - clk_params->treot_ns;
-
- t->rec_min = DIV_ROUND_UP(
- ((t->mipi_min * clk_params->bitclk_mbps) +
- (3 * clk_params->tlpx_numer_ns)),
- (8 * clk_params->tlpx_numer_ns));
-
- rec_temp1 = ((t->mipi_max * clk_params->bitclk_mbps) +
- (3 * clk_params->tlpx_numer_ns));
- t->rec_max = (rec_temp1 / (8 * clk_params->tlpx_numer_ns));
- rec_temp1 = DIV_ROUND_UP(
- ((t->rec_max - t->rec_min) * phy_timing_frac),
- 100);
- t->rec = rec_temp1 + t->rec_min;
-
- if (t->rec & 0xffffff00) {
- pr_err("Incorrect rec valuefor hs_trail\n");
- rc = -EINVAL;
- } else {
- t->reg_value = t->rec;
- }
-
- pr_debug("HS_TRAIL:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d, reg_val=%d\n",
- t->mipi_min, t->mipi_max, t->rec_min, t->rec_max,
- t->reg_value);
-
- return rc;
-}
-
-/**
- * calc_hs_rqst - calculates rqst timing params for data lanes in HS.
- */
-static int calc_hs_rqst(struct phy_clk_params *clk_params,
- struct phy_timing_desc *desc)
-{
- int rc = 0;
- struct timing_entry *t = &desc->hs_rqst;
-
- t->rec = DIV_ROUND_UP(
- ((t->mipi_min * clk_params->bitclk_mbps) -
- (8 * clk_params->tlpx_numer_ns)),
- (8 * clk_params->tlpx_numer_ns));
-
- if (t->rec & 0xffffff00) {
- pr_err("Incorrect rec valuefor hs_rqst, %d\n", t->rec);
- rc = -EINVAL;
- } else {
- t->reg_value = t->rec;
- }
-
- pr_debug("HS_RQST:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d, reg_val=%d\n",
- t->mipi_min, t->mipi_max, t->rec_min, t->rec_max,
- t->reg_value);
-
- return rc;
-}
-
-/**
- * calc_hs_exit - calculates exit timing params for data lanes in HS.
- */
-static int calc_hs_exit(struct phy_clk_params *clk_params,
- struct phy_timing_desc *desc)
-{
- u32 const hs_exit_min_frac = 10;
- int rc = 0;
- struct timing_entry *t = &desc->hs_exit;
-
- t->rec_min = (DIV_ROUND_UP(
- (t->mipi_min * clk_params->bitclk_mbps),
- (8 * clk_params->tlpx_numer_ns)) - 1);
-
- t->rec = DIV_ROUND_UP(
- (((t->rec_max - t->rec_min) * hs_exit_min_frac) +
- (t->rec_min * 100)),
- 100);
-
- if (t->rec & 0xffffff00) {
- pr_err("Incorrect rec valuefor hs_exit\n");
- rc = -EINVAL;
- } else {
- t->reg_value = t->rec;
- }
-
- pr_debug("HS_EXIT:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d, reg_val=%d\n",
- t->mipi_min, t->mipi_max, t->rec_min, t->rec_max,
- t->reg_value);
-
- return rc;
-}
-
-/**
- * calc_hs_rqst_clk - calculates rqst timing params for clock lane..
- */
-static int calc_hs_rqst_clk(struct phy_clk_params *clk_params,
- struct phy_timing_desc *desc)
-{
- int rc = 0;
- struct timing_entry *t = &desc->hs_rqst_clk;
-
- t->rec = DIV_ROUND_UP(
- ((t->mipi_min * clk_params->bitclk_mbps) -
- (8 * clk_params->tlpx_numer_ns)),
- (8 * clk_params->tlpx_numer_ns));
-
- if (t->rec & 0xffffff00) {
- pr_err("Incorrect rec valuefor hs_rqst_clk\n");
- rc = -EINVAL;
- } else {
- t->reg_value = t->rec;
- }
-
- pr_debug("HS_RQST_CLK:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d, reg_val=%d\n",
- t->mipi_min, t->mipi_max, t->rec_min, t->rec_max,
- t->reg_value);
-
- return rc;
-}
-
-/**
- * dsi_phy_calc_timing_params - calculates timing paramets for a given bit clock
- */
-static int dsi_phy_calc_timing_params(struct phy_clk_params *clk_params,
- struct phy_timing_desc *desc)
-{
- int rc = 0;
- s32 actual_frac = 0;
- s64 actual_intermediate = 0;
- u64 temp_multiple;
- s64 teot_clk_lane;
-
- rc = calc_clk_prepare(clk_params, desc, &actual_frac,
- &actual_intermediate);
- if (rc) {
- pr_err("clk_prepare calculations failed, rc=%d\n", rc);
- goto error;
- }
-
- rc = calc_clk_zero(clk_params, desc, actual_frac, actual_intermediate);
- if (rc) {
- pr_err("clk_zero calculations failed, rc=%d\n", rc);
- goto error;
- }
-
- rc = calc_clk_trail(clk_params, desc, &teot_clk_lane);
- if (rc) {
- pr_err("clk_trail calculations failed, rc=%d\n", rc);
- goto error;
- }
-
- rc = calc_hs_prepare(clk_params, desc, &temp_multiple);
- if (rc) {
- pr_err("hs_prepare calculations failed, rc=%d\n", rc);
- goto error;
- }
-
- rc = calc_hs_zero(clk_params, desc, temp_multiple);
- if (rc) {
- pr_err("hs_zero calculations failed, rc=%d\n", rc);
- goto error;
- }
-
- rc = calc_hs_trail(clk_params, desc, teot_clk_lane);
- if (rc) {
- pr_err("hs_trail calculations failed, rc=%d\n", rc);
- goto error;
- }
-
- rc = calc_hs_rqst(clk_params, desc);
- if (rc) {
- pr_err("hs_rqst calculations failed, rc=%d\n", rc);
- goto error;
- }
-
- rc = calc_hs_exit(clk_params, desc);
- if (rc) {
- pr_err("hs_exit calculations failed, rc=%d\n", rc);
- goto error;
- }
-
- rc = calc_hs_rqst_clk(clk_params, desc);
- if (rc) {
- pr_err("hs_rqst_clk calculations failed, rc=%d\n", rc);
- goto error;
- }
-error:
- return rc;
-}
-
-/**
- * calculate_timing_params() - calculates timing parameters.
- * @phy: Pointer to DSI PHY hardware object.
- * @mode: Mode information for which timing has to be calculated.
- * @config: DSI host configuration for this mode.
- * @timing: Timing parameters for each lane which will be returned.
- */
-int dsi_phy_hw_v4_0_calculate_timing_params(struct dsi_phy_hw *phy,
- struct dsi_mode_info *mode,
- struct dsi_host_common_cfg *host,
- struct dsi_phy_per_lane_cfgs *timing)
-{
- /* constants */
- u32 const esc_clk_mhz = 192; /* TODO: esc clock is hardcoded */
- u32 const esc_clk_mmss_cc_prediv = 10;
- u32 const tlpx_numer = 1000;
- u32 const tr_eot = 20;
- u32 const clk_prepare_spec_min = 38;
- u32 const clk_prepare_spec_max = 95;
- u32 const clk_trail_spec_min = 60;
- u32 const hs_exit_spec_min = 100;
- u32 const hs_exit_reco_max = 255;
- u32 const hs_rqst_spec_min = 50;
-
- /* local vars */
- int rc = 0;
- int i;
- u32 h_total, v_total;
- u64 inter_num;
- u32 num_of_lanes = 0;
- u32 bpp;
- u64 x, y;
- struct phy_timing_desc desc;
- struct phy_clk_params clk_params = {0};
-
- memset(&desc, 0x0, sizeof(desc));
- h_total = DSI_H_TOTAL(mode);
- v_total = DSI_V_TOTAL(mode);
-
- bpp = bits_per_pixel[host->dst_format];
-
- inter_num = bpp * mode->refresh_rate;
-
- if (host->data_lanes & DSI_DATA_LANE_0)
- num_of_lanes++;
- if (host->data_lanes & DSI_DATA_LANE_1)
- num_of_lanes++;
- if (host->data_lanes & DSI_DATA_LANE_2)
- num_of_lanes++;
- if (host->data_lanes & DSI_DATA_LANE_3)
- num_of_lanes++;
-
-
- x = mult_frac(v_total * h_total, inter_num, num_of_lanes);
- y = rounddown(x, 1);
-
- clk_params.bitclk_mbps = rounddown(mult_frac(y, 1, 1000000), 1);
- clk_params.escclk_numer = esc_clk_mhz;
- clk_params.escclk_denom = esc_clk_mmss_cc_prediv;
- clk_params.tlpx_numer_ns = tlpx_numer;
- clk_params.treot_ns = tr_eot;
-
-
- /* Setup default parameters */
- desc.clk_prepare.mipi_min = clk_prepare_spec_min;
- desc.clk_prepare.mipi_max = clk_prepare_spec_max;
- desc.clk_trail.mipi_min = clk_trail_spec_min;
- desc.hs_exit.mipi_min = hs_exit_spec_min;
- desc.hs_exit.rec_max = hs_exit_reco_max;
-
- desc.clk_prepare.rec_min = DIV_ROUND_UP(
- (desc.clk_prepare.mipi_min * clk_params.bitclk_mbps),
- (8 * clk_params.tlpx_numer_ns)
- );
-
- desc.clk_prepare.rec_max = rounddown(
- mult_frac((desc.clk_prepare.mipi_max * clk_params.bitclk_mbps),
- 1, (8 * clk_params.tlpx_numer_ns)),
- 1);
-
- desc.hs_rqst.mipi_min = hs_rqst_spec_min;
- desc.hs_rqst_clk.mipi_min = hs_rqst_spec_min;
-
- pr_debug("BIT CLOCK = %d, tlpx_numer_ns=%d, treot_ns=%d\n",
- clk_params.bitclk_mbps, clk_params.tlpx_numer_ns,
- clk_params.treot_ns);
- rc = dsi_phy_calc_timing_params(&clk_params, &desc);
- if (rc) {
- pr_err("Timing calc failed, rc=%d\n", rc);
- goto error;
- }
-
-
- for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) {
- timing->lane[i][0] = desc.hs_exit.reg_value;
-
- if (i == DSI_LOGICAL_CLOCK_LANE)
- timing->lane[i][1] = desc.clk_zero.reg_value;
- else
- timing->lane[i][1] = desc.hs_zero.reg_value;
-
- if (i == DSI_LOGICAL_CLOCK_LANE)
- timing->lane[i][2] = desc.clk_prepare.reg_value;
- else
- timing->lane[i][2] = desc.hs_prepare.reg_value;
-
- if (i == DSI_LOGICAL_CLOCK_LANE)
- timing->lane[i][3] = desc.clk_trail.reg_value;
- else
- timing->lane[i][3] = desc.hs_trail.reg_value;
-
- if (i == DSI_LOGICAL_CLOCK_LANE)
- timing->lane[i][4] = desc.hs_rqst_clk.reg_value;
- else
- timing->lane[i][4] = desc.hs_rqst.reg_value;
-
- timing->lane[i][5] = 0x3;
- timing->lane[i][6] = 0x4;
- timing->lane[i][7] = 0xA0;
- pr_debug("[%d][%d %d %d %d %d]\n", i, timing->lane[i][0],
- timing->lane[i][1],
- timing->lane[i][2],
- timing->lane[i][3],
- timing->lane[i][4]);
- }
- timing->count_per_lane = 8;
-
-error:
- return rc;
-}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_timing_calc.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_timing_calc.c
new file mode 100644
index 0000000..e52a0f2
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_timing_calc.c
@@ -0,0 +1,676 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "dsi-phy-timing:" fmt
+
+#include "dsi_phy_timing_calc.h"
+
+static const u32 bits_per_pixel[DSI_PIXEL_FORMAT_MAX] = {
+ 16, 18, 18, 24, 3, 8, 12 };
+
+static int dsi_phy_cmn_validate_and_set(struct timing_entry *t,
+ char const *t_name)
+{
+ if (t->rec & 0xffffff00) {
+ /* Output value can only be 8 bits */
+ pr_err("Incorrect %s rec value - %d\n", t_name, t->rec);
+ return -EINVAL;
+ }
+ t->reg_value = t->rec;
+ return 0;
+}
+
+/**
+ * calc_clk_prepare - calculates prepare timing params for clk lane.
+ */
+static int calc_clk_prepare(struct dsi_phy_hw *phy,
+ struct phy_clk_params *clk_params,
+ struct phy_timing_desc *desc,
+ s32 *actual_frac,
+ s64 *actual_intermediate)
+{
+ u64 const multiplier = BIT(20);
+ struct timing_entry *t = &desc->clk_prepare;
+ int rc = 0;
+ u64 dividend, temp, temp_multiple;
+ s32 frac = 0;
+ s64 intermediate;
+ s64 clk_prep_actual;
+
+ dividend = ((t->rec_max - t->rec_min) *
+ clk_params->clk_prep_buf * multiplier);
+ temp = roundup(div_s64(dividend, 100), multiplier);
+ temp += (t->rec_min * multiplier);
+ t->rec = div_s64(temp, multiplier);
+
+ rc = dsi_phy_cmn_validate_and_set(t, "clk_prepare");
+ if (rc)
+ goto error;
+
+ /* calculate theoretical value */
+ temp_multiple = 8 * t->reg_value * clk_params->tlpx_numer_ns
+ * multiplier;
+ intermediate = div_s64(temp_multiple, clk_params->bitclk_mbps);
+ div_s64_rem(temp_multiple, clk_params->bitclk_mbps, &frac);
+ clk_prep_actual = div_s64((intermediate + frac), multiplier);
+
+ pr_debug("CLK_PREPARE:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d",
+ t->mipi_min, t->mipi_max, t->rec_min, t->rec_max);
+ pr_debug(" reg_value=%d, actual=%lld\n", t->reg_value, clk_prep_actual);
+
+ *actual_frac = frac;
+ *actual_intermediate = intermediate;
+
+error:
+ return rc;
+}
+
+/**
+ * calc_clk_zero - calculates zero timing params for clk lane.
+ */
+static int calc_clk_zero(struct dsi_phy_hw *phy,
+ struct phy_clk_params *clk_params,
+ struct phy_timing_desc *desc,
+ s32 actual_frac, s64 actual_intermediate)
+{
+ u64 const multiplier = BIT(20);
+ int rc = 0;
+ struct timing_entry *t = &desc->clk_zero;
+ s64 mipi_min, rec_temp1;
+ struct phy_timing_ops *ops = phy->ops.timing_ops;
+
+ mipi_min = ((300 * multiplier) - (actual_intermediate + actual_frac));
+ t->mipi_min = div_s64(mipi_min, multiplier);
+
+ rec_temp1 = div_s64((mipi_min * clk_params->bitclk_mbps),
+ clk_params->tlpx_numer_ns);
+
+ if (ops->calc_clk_zero) {
+ t->rec_min = ops->calc_clk_zero(rec_temp1, multiplier);
+ } else {
+ rc = -EINVAL;
+ goto error;
+ }
+ t->rec_max = ((t->rec_min > 255) ? 511 : 255);
+
+ t->rec = DIV_ROUND_UP((((t->rec_max - t->rec_min) *
+ clk_params->clk_zero_buf) + (t->rec_min * 100)), 100);
+
+ rc = dsi_phy_cmn_validate_and_set(t, "clk_zero");
+ if (rc)
+ goto error;
+
+
+ pr_debug("CLK_ZERO:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d, reg_val=%d\n",
+ t->mipi_min, t->mipi_max, t->rec_min, t->rec_max,
+ t->reg_value);
+error:
+ return rc;
+}
+
+/**
+ * calc_clk_trail - calculates prepare trail params for clk lane.
+ */
+static int calc_clk_trail(struct dsi_phy_hw *phy,
+ struct phy_clk_params *clk_params,
+ struct phy_timing_desc *desc,
+ s64 *teot_clk_lane)
+{
+ u64 const multiplier = BIT(20);
+ int rc = 0;
+ struct timing_entry *t = &desc->clk_trail;
+ u64 temp_multiple;
+ s32 frac;
+ s64 mipi_max_tr, rec_temp1, mipi_max;
+ s64 teot_clk_lane1;
+ struct phy_timing_ops *ops = phy->ops.timing_ops;
+
+ temp_multiple = div_s64(
+ (12 * multiplier * clk_params->tlpx_numer_ns),
+ clk_params->bitclk_mbps);
+ div_s64_rem(temp_multiple, multiplier, &frac);
+
+ mipi_max_tr = ((105 * multiplier) +
+ (temp_multiple + frac));
+ teot_clk_lane1 = div_s64(mipi_max_tr, multiplier);
+
+ mipi_max = (mipi_max_tr - (clk_params->treot_ns * multiplier));
+ t->mipi_max = div_s64(mipi_max, multiplier);
+
+ temp_multiple = div_s64(
+ (t->mipi_min * multiplier * clk_params->bitclk_mbps),
+ clk_params->tlpx_numer_ns);
+
+ div_s64_rem(temp_multiple, multiplier, &frac);
+ if (ops->calc_clk_trail_rec_min) {
+ t->rec_min = ops->calc_clk_trail_rec_min(temp_multiple,
+ frac, multiplier);
+ } else {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ /* recommended max */
+ rec_temp1 = div_s64((mipi_max * clk_params->bitclk_mbps),
+ clk_params->tlpx_numer_ns);
+ if (ops->calc_clk_trail_rec_max) {
+ t->rec_max = ops->calc_clk_trail_rec_max(rec_temp1, multiplier);
+ } else {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ t->rec = DIV_ROUND_UP(
+ (((t->rec_max - t->rec_min) * clk_params->clk_trail_buf) +
+ (t->rec_min * 100)), 100);
+
+ rc = dsi_phy_cmn_validate_and_set(t, "clk_trail");
+ if (rc)
+ goto error;
+
+ *teot_clk_lane = teot_clk_lane1;
+ pr_debug("CLK_TRAIL:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d, reg_val=%d\n",
+ t->mipi_min, t->mipi_max, t->rec_min, t->rec_max,
+ t->reg_value);
+
+error:
+ return rc;
+
+}
+
+/**
+ * calc_hs_prepare - calculates prepare timing params for data lanes in HS.
+ */
+static int calc_hs_prepare(struct dsi_phy_hw *phy,
+ struct phy_clk_params *clk_params,
+ struct phy_timing_desc *desc,
+ u64 *temp_mul)
+{
+ u64 const multiplier = BIT(20);
+ int rc = 0;
+ struct timing_entry *t = &desc->hs_prepare;
+ u64 temp_multiple, dividend, temp;
+ s32 frac;
+ s64 rec_temp1, rec_temp2, mipi_max, mipi_min;
+ u32 low_clk_multiplier = 0;
+
+ if (clk_params->bitclk_mbps <= 120)
+ low_clk_multiplier = 2;
+ /* mipi min */
+ temp_multiple = div_s64((4 * multiplier * clk_params->tlpx_numer_ns),
+ clk_params->bitclk_mbps);
+ div_s64_rem(temp_multiple, multiplier, &frac);
+ mipi_min = (40 * multiplier) + (temp_multiple + frac);
+ t->mipi_min = div_s64(mipi_min, multiplier);
+
+ /* mipi_max */
+ temp_multiple = div_s64(
+ (6 * multiplier * clk_params->tlpx_numer_ns),
+ clk_params->bitclk_mbps);
+ div_s64_rem(temp_multiple, multiplier, &frac);
+ mipi_max = (85 * multiplier) + temp_multiple;
+ t->mipi_max = div_s64(mipi_max, multiplier);
+
+ /* recommended min */
+ temp_multiple = div_s64((mipi_min * clk_params->bitclk_mbps),
+ clk_params->tlpx_numer_ns);
+ temp_multiple -= (low_clk_multiplier * multiplier);
+ div_s64_rem(temp_multiple, multiplier, &frac);
+ rec_temp1 = roundup(((temp_multiple + frac) / 8), multiplier);
+ t->rec_min = div_s64(rec_temp1, multiplier);
+
+ /* recommended max */
+ temp_multiple = div_s64((mipi_max * clk_params->bitclk_mbps),
+ clk_params->tlpx_numer_ns);
+ temp_multiple -= (low_clk_multiplier * multiplier);
+ div_s64_rem(temp_multiple, multiplier, &frac);
+ rec_temp2 = rounddown((temp_multiple / 8), multiplier);
+ t->rec_max = div_s64(rec_temp2, multiplier);
+
+ /* register value */
+ dividend = ((rec_temp2 - rec_temp1) * clk_params->hs_prep_buf);
+ temp = roundup(div_u64(dividend, 100), multiplier);
+ t->rec = div_s64((temp + rec_temp1), multiplier);
+
+ rc = dsi_phy_cmn_validate_and_set(t, "hs_prepare");
+ if (rc)
+ goto error;
+
+ temp_multiple = div_s64(
+ (8 * (temp + rec_temp1) * clk_params->tlpx_numer_ns),
+ clk_params->bitclk_mbps);
+
+ *temp_mul = temp_multiple;
+ pr_debug("HS_PREP:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d, reg_val=%d\n",
+ t->mipi_min, t->mipi_max, t->rec_min, t->rec_max,
+ t->reg_value);
+error:
+ return rc;
+}
+
+/**
+ * calc_hs_zero - calculates zero timing params for data lanes in HS.
+ */
+static int calc_hs_zero(struct dsi_phy_hw *phy,
+ struct phy_clk_params *clk_params,
+ struct phy_timing_desc *desc,
+ u64 temp_multiple)
+{
+ u64 const multiplier = BIT(20);
+ int rc = 0;
+ struct timing_entry *t = &desc->hs_zero;
+ s64 rec_temp1, mipi_min;
+ struct phy_timing_ops *ops = phy->ops.timing_ops;
+
+ mipi_min = div_s64((10 * clk_params->tlpx_numer_ns * multiplier),
+ clk_params->bitclk_mbps);
+ rec_temp1 = (145 * multiplier) + mipi_min - temp_multiple;
+ t->mipi_min = div_s64(rec_temp1, multiplier);
+
+ /* recommended min */
+ rec_temp1 = div_s64((rec_temp1 * clk_params->bitclk_mbps),
+ clk_params->tlpx_numer_ns);
+
+ if (ops->calc_hs_zero) {
+ t->rec_min = ops->calc_hs_zero(rec_temp1, multiplier);
+ } else {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ t->rec_max = ((t->rec_min > 255) ? 511 : 255);
+ t->rec = DIV_ROUND_UP(
+ (((t->rec_max - t->rec_min) * clk_params->hs_zero_buf) +
+ (t->rec_min * 100)),
+ 100);
+
+ rc = dsi_phy_cmn_validate_and_set(t, "hs_zero");
+ if (rc)
+ goto error;
+
+ pr_debug("HS_ZERO:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d, reg_val=%d\n",
+ t->mipi_min, t->mipi_max, t->rec_min, t->rec_max,
+ t->reg_value);
+
+error:
+ return rc;
+}
+
+/**
+ * calc_hs_trail - calculates trail timing params for data lanes in HS.
+ */
+static int calc_hs_trail(struct dsi_phy_hw *phy,
+ struct phy_clk_params *clk_params,
+ struct phy_timing_desc *desc,
+ u64 teot_clk_lane)
+{
+ int rc = 0;
+ struct timing_entry *t = &desc->hs_trail;
+ s64 rec_temp1;
+ struct phy_timing_ops *ops = phy->ops.timing_ops;
+
+ t->mipi_min = 60 +
+ mult_frac(clk_params->tlpx_numer_ns, 4,
+ clk_params->bitclk_mbps);
+
+ t->mipi_max = teot_clk_lane - clk_params->treot_ns;
+
+ if (ops->calc_hs_trail) {
+ ops->calc_hs_trail(clk_params, desc);
+ } else {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ rec_temp1 = DIV_ROUND_UP(
+ ((t->rec_max - t->rec_min) * clk_params->hs_trail_buf),
+ 100);
+ t->rec = rec_temp1 + t->rec_min;
+
+ rc = dsi_phy_cmn_validate_and_set(t, "hs_trail");
+ if (rc)
+ goto error;
+
+ pr_debug("HS_TRAIL:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d, reg_val=%d\n",
+ t->mipi_min, t->mipi_max, t->rec_min, t->rec_max,
+ t->reg_value);
+
+error:
+ return rc;
+}
+
+/**
+ * calc_hs_rqst - calculates rqst timing params for data lanes in HS.
+ */
+static int calc_hs_rqst(struct dsi_phy_hw *phy,
+ struct phy_clk_params *clk_params,
+ struct phy_timing_desc *desc)
+{
+ int rc = 0;
+ struct timing_entry *t = &desc->hs_rqst;
+
+ t->rec = DIV_ROUND_UP(
+ ((t->mipi_min * clk_params->bitclk_mbps) -
+ (8 * clk_params->tlpx_numer_ns)),
+ (8 * clk_params->tlpx_numer_ns));
+
+ rc = dsi_phy_cmn_validate_and_set(t, "hs_rqst");
+ if (rc)
+ goto error;
+
+ pr_debug("HS_RQST:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d, reg_val=%d\n",
+ t->mipi_min, t->mipi_max, t->rec_min, t->rec_max,
+ t->reg_value);
+
+error:
+ return rc;
+}
+
+/**
+ * calc_hs_exit - calculates exit timing params for data lanes in HS.
+ */
+static int calc_hs_exit(struct dsi_phy_hw *phy,
+ struct phy_clk_params *clk_params,
+ struct phy_timing_desc *desc)
+{
+ int rc = 0;
+ struct timing_entry *t = &desc->hs_exit;
+
+ t->rec_min = (DIV_ROUND_UP(
+ (t->mipi_min * clk_params->bitclk_mbps),
+ (8 * clk_params->tlpx_numer_ns)) - 1);
+
+ t->rec = DIV_ROUND_UP(
+ (((t->rec_max - t->rec_min) * clk_params->hs_exit_buf) +
+ (t->rec_min * 100)), 100);
+
+ rc = dsi_phy_cmn_validate_and_set(t, "hs_exit");
+ if (rc)
+ goto error;
+
+
+ pr_debug("HS_EXIT:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d, reg_val=%d\n",
+ t->mipi_min, t->mipi_max, t->rec_min, t->rec_max,
+ t->reg_value);
+
+error:
+ return rc;
+}
+
+/**
+ * calc_hs_rqst_clk - calculates rqst timing params for clock lane..
+ */
+static int calc_hs_rqst_clk(struct dsi_phy_hw *phy,
+ struct phy_clk_params *clk_params,
+ struct phy_timing_desc *desc)
+{
+ int rc = 0;
+ struct timing_entry *t = &desc->hs_rqst_clk;
+
+ t->rec = DIV_ROUND_UP(
+ ((t->mipi_min * clk_params->bitclk_mbps) -
+ (8 * clk_params->tlpx_numer_ns)),
+ (8 * clk_params->tlpx_numer_ns));
+
+ rc = dsi_phy_cmn_validate_and_set(t, "hs_rqst_clk");
+ if (rc)
+ goto error;
+
+ pr_debug("HS_RQST_CLK:mipi_min=%d, mipi_max=%d, rec_min=%d, rec_max=%d, reg_val=%d\n",
+ t->mipi_min, t->mipi_max, t->rec_min, t->rec_max,
+ t->reg_value);
+
+error:
+ return rc;
+}
+
+/**
+ * dsi_phy_calc_timing_params - calculates timing paramets for a given bit clock
+ */
+static int dsi_phy_cmn_calc_timing_params(struct dsi_phy_hw *phy,
+ struct phy_clk_params *clk_params, struct phy_timing_desc *desc)
+{
+ int rc = 0;
+ s32 actual_frac = 0;
+ s64 actual_intermediate = 0;
+ u64 temp_multiple;
+ s64 teot_clk_lane;
+
+ rc = calc_clk_prepare(phy, clk_params, desc, &actual_frac,
+ &actual_intermediate);
+ if (rc) {
+ pr_err("clk_prepare calculations failed, rc=%d\n", rc);
+ goto error;
+ }
+
+ rc = calc_clk_zero(phy, clk_params, desc,
+ actual_frac, actual_intermediate);
+ if (rc) {
+ pr_err("clk_zero calculations failed, rc=%d\n", rc);
+ goto error;
+ }
+
+ rc = calc_clk_trail(phy, clk_params, desc, &teot_clk_lane);
+ if (rc) {
+ pr_err("clk_trail calculations failed, rc=%d\n", rc);
+ goto error;
+ }
+
+ rc = calc_hs_prepare(phy, clk_params, desc, &temp_multiple);
+ if (rc) {
+ pr_err("hs_prepare calculations failed, rc=%d\n", rc);
+ goto error;
+ }
+
+ rc = calc_hs_zero(phy, clk_params, desc, temp_multiple);
+ if (rc) {
+ pr_err("hs_zero calculations failed, rc=%d\n", rc);
+ goto error;
+ }
+
+ rc = calc_hs_trail(phy, clk_params, desc, teot_clk_lane);
+ if (rc) {
+ pr_err("hs_trail calculations failed, rc=%d\n", rc);
+ goto error;
+ }
+
+ rc = calc_hs_rqst(phy, clk_params, desc);
+ if (rc) {
+ pr_err("hs_rqst calculations failed, rc=%d\n", rc);
+ goto error;
+ }
+
+ rc = calc_hs_exit(phy, clk_params, desc);
+ if (rc) {
+ pr_err("hs_exit calculations failed, rc=%d\n", rc);
+ goto error;
+ }
+
+ rc = calc_hs_rqst_clk(phy, clk_params, desc);
+ if (rc) {
+ pr_err("hs_rqst_clk calculations failed, rc=%d\n", rc);
+ goto error;
+ }
+error:
+ return rc;
+}
+
+/**
+ * calculate_timing_params() - calculates timing parameters.
+ * @phy: Pointer to DSI PHY hardware object.
+ * @mode: Mode information for which timing has to be calculated.
+ * @config: DSI host configuration for this mode.
+ * @timing: Timing parameters for each lane which will be returned.
+ */
+int dsi_phy_hw_calculate_timing_params(struct dsi_phy_hw *phy,
+ struct dsi_mode_info *mode,
+ struct dsi_host_common_cfg *host,
+ struct dsi_phy_per_lane_cfgs *timing)
+{
+ /* constants */
+ u32 const esc_clk_mhz = 192; /* TODO: esc clock is hardcoded */
+ u32 const esc_clk_mmss_cc_prediv = 10;
+ u32 const tlpx_numer = 1000;
+ u32 const tr_eot = 20;
+ u32 const clk_prepare_spec_min = 38;
+ u32 const clk_prepare_spec_max = 95;
+ u32 const clk_trail_spec_min = 60;
+ u32 const hs_exit_spec_min = 100;
+ u32 const hs_exit_reco_max = 255;
+ u32 const hs_rqst_spec_min = 50;
+
+ /* local vars */
+ int rc = 0;
+ u32 h_total, v_total;
+ u64 inter_num;
+ u32 num_of_lanes = 0;
+ u32 bpp;
+ u64 x, y;
+ struct phy_timing_desc desc;
+ struct phy_clk_params clk_params = {0};
+ struct phy_timing_ops *ops = phy->ops.timing_ops;
+
+ memset(&desc, 0x0, sizeof(desc));
+ h_total = DSI_H_TOTAL(mode);
+ v_total = DSI_V_TOTAL(mode);
+
+ bpp = bits_per_pixel[host->dst_format];
+
+ inter_num = bpp * mode->refresh_rate;
+
+ if (host->data_lanes & DSI_DATA_LANE_0)
+ num_of_lanes++;
+ if (host->data_lanes & DSI_DATA_LANE_1)
+ num_of_lanes++;
+ if (host->data_lanes & DSI_DATA_LANE_2)
+ num_of_lanes++;
+ if (host->data_lanes & DSI_DATA_LANE_3)
+ num_of_lanes++;
+
+
+ x = mult_frac(v_total * h_total, inter_num, num_of_lanes);
+ y = rounddown(x, 1);
+
+ clk_params.bitclk_mbps = rounddown(mult_frac(y, 1, 1000000), 1);
+ clk_params.escclk_numer = esc_clk_mhz;
+ clk_params.escclk_denom = esc_clk_mmss_cc_prediv;
+ clk_params.tlpx_numer_ns = tlpx_numer;
+ clk_params.treot_ns = tr_eot;
+
+
+ /* Setup default parameters */
+ desc.clk_prepare.mipi_min = clk_prepare_spec_min;
+ desc.clk_prepare.mipi_max = clk_prepare_spec_max;
+ desc.clk_trail.mipi_min = clk_trail_spec_min;
+ desc.hs_exit.mipi_min = hs_exit_spec_min;
+ desc.hs_exit.rec_max = hs_exit_reco_max;
+ desc.hs_rqst.mipi_min = hs_rqst_spec_min;
+ desc.hs_rqst_clk.mipi_min = hs_rqst_spec_min;
+
+ if (ops->get_default_phy_params) {
+ ops->get_default_phy_params(&clk_params);
+ } else {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ desc.clk_prepare.rec_min = DIV_ROUND_UP(
+ (desc.clk_prepare.mipi_min * clk_params.bitclk_mbps),
+ (8 * clk_params.tlpx_numer_ns)
+ );
+
+ desc.clk_prepare.rec_max = rounddown(
+ mult_frac((desc.clk_prepare.mipi_max * clk_params.bitclk_mbps),
+ 1, (8 * clk_params.tlpx_numer_ns)),
+ 1);
+
+ pr_debug("BIT CLOCK = %d, tlpx_numer_ns=%d, treot_ns=%d\n",
+ clk_params.bitclk_mbps, clk_params.tlpx_numer_ns,
+ clk_params.treot_ns);
+ rc = dsi_phy_cmn_calc_timing_params(phy, &clk_params, &desc);
+ if (rc) {
+ pr_err("Timing calc failed, rc=%d\n", rc);
+ goto error;
+ }
+
+ if (ops->update_timing_params) {
+ ops->update_timing_params(timing, &desc);
+ } else {
+ rc = -EINVAL;
+ goto error;
+ }
+
+error:
+ return rc;
+}
+
+int dsi_phy_timing_calc_init(struct dsi_phy_hw *phy,
+ enum dsi_phy_version version)
+{
+ struct phy_timing_ops *ops = NULL;
+
+ if (version == DSI_PHY_VERSION_UNKNOWN ||
+ version >= DSI_PHY_VERSION_MAX || !phy) {
+ pr_err("Unsupported version: %d\n", version);
+ return -ENOTSUPP;
+ }
+
+ ops = kzalloc(sizeof(struct phy_timing_ops), GFP_KERNEL);
+ if (!ops)
+ return -EINVAL;
+ phy->ops.timing_ops = ops;
+
+ switch (version) {
+ case DSI_PHY_VERSION_2_0:
+ ops->get_default_phy_params =
+ dsi_phy_hw_v2_0_get_default_phy_params;
+ ops->calc_clk_zero =
+ dsi_phy_hw_v2_0_calc_clk_zero;
+ ops->calc_clk_trail_rec_min =
+ dsi_phy_hw_v2_0_calc_clk_trail_rec_min;
+ ops->calc_clk_trail_rec_max =
+ dsi_phy_hw_v2_0_calc_clk_trail_rec_max;
+ ops->calc_hs_zero =
+ dsi_phy_hw_v2_0_calc_hs_zero;
+ ops->calc_hs_trail =
+ dsi_phy_hw_v2_0_calc_hs_trail;
+ ops->update_timing_params =
+ dsi_phy_hw_v2_0_update_timing_params;
+ break;
+ case DSI_PHY_VERSION_3_0:
+ ops->get_default_phy_params =
+ dsi_phy_hw_v3_0_get_default_phy_params;
+ ops->calc_clk_zero =
+ dsi_phy_hw_v3_0_calc_clk_zero;
+ ops->calc_clk_trail_rec_min =
+ dsi_phy_hw_v3_0_calc_clk_trail_rec_min;
+ ops->calc_clk_trail_rec_max =
+ dsi_phy_hw_v3_0_calc_clk_trail_rec_max;
+ ops->calc_hs_zero =
+ dsi_phy_hw_v3_0_calc_hs_zero;
+ ops->calc_hs_trail =
+ dsi_phy_hw_v3_0_calc_hs_trail;
+ ops->update_timing_params =
+ dsi_phy_hw_v3_0_update_timing_params;
+ break;
+ case DSI_PHY_VERSION_0_0_HPM:
+ case DSI_PHY_VERSION_0_0_LPM:
+ case DSI_PHY_VERSION_1_0:
+ default:
+ kfree(ops);
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_timing_calc.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_timing_calc.h
new file mode 100644
index 0000000..bae6d05
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_timing_calc.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DSI_PHY_TIMING_CALC_H_
+#define _DSI_PHY_TIMING_CALC_H_
+
+#include <linux/math64.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/bitmap.h>
+#include <linux/errno.h>
+
+#include "dsi_defs.h"
+#include "dsi_phy_hw.h"
+#include "dsi_catalog.h"
+
+/**
+ * struct timing_entry - Calculated values for each timing parameter.
+ * @mipi_min:
+ * @mipi_max:
+ * @rec_min:
+ * @rec_max:
+ * @rec:
+ * @reg_value: Value to be programmed in register.
+ */
+struct timing_entry {
+ s32 mipi_min;
+ s32 mipi_max;
+ s32 rec_min;
+ s32 rec_max;
+ s32 rec;
+ u8 reg_value;
+};
+
+/**
+ * struct phy_timing_desc - Timing parameters for DSI PHY.
+ */
+struct phy_timing_desc {
+ struct timing_entry clk_prepare;
+ struct timing_entry clk_zero;
+ struct timing_entry clk_trail;
+ struct timing_entry hs_prepare;
+ struct timing_entry hs_zero;
+ struct timing_entry hs_trail;
+ struct timing_entry hs_rqst;
+ struct timing_entry hs_rqst_clk;
+ struct timing_entry hs_exit;
+ struct timing_entry ta_go;
+ struct timing_entry ta_sure;
+ struct timing_entry ta_set;
+ struct timing_entry clk_post;
+ struct timing_entry clk_pre;
+};
+
+/**
+ * struct phy_clk_params - Clock parameters for PHY timing calculations.
+ */
+struct phy_clk_params {
+ u32 bitclk_mbps;
+ u32 escclk_numer;
+ u32 escclk_denom;
+ u32 tlpx_numer_ns;
+ u32 treot_ns;
+ u32 clk_prep_buf;
+ u32 clk_zero_buf;
+ u32 clk_trail_buf;
+ u32 hs_prep_buf;
+ u32 hs_zero_buf;
+ u32 hs_trail_buf;
+ u32 hs_rqst_buf;
+ u32 hs_exit_buf;
+};
+
+/**
+ * Various Ops needed for auto-calculation of DSI PHY timing parameters.
+ */
+struct phy_timing_ops {
+ void (*get_default_phy_params)(struct phy_clk_params *params);
+
+ int32_t (*calc_clk_zero)(s64 rec_temp1, s64 mult);
+
+ int32_t (*calc_clk_trail_rec_min)(s64 temp_mul,
+ s64 frac, s64 mult);
+
+ int32_t (*calc_clk_trail_rec_max)(s64 temp1, s64 mult);
+
+ int32_t (*calc_hs_zero)(s64 temp1, s64 mult);
+
+ void (*calc_hs_trail)(struct phy_clk_params *clk_params,
+ struct phy_timing_desc *desc);
+
+ void (*update_timing_params)(struct dsi_phy_per_lane_cfgs *timing,
+ struct phy_timing_desc *desc);
+};
+
+/* DSI PHY timing functions for 14nm */
+void dsi_phy_hw_v2_0_get_default_phy_params(struct phy_clk_params *params);
+
+int32_t dsi_phy_hw_v2_0_calc_clk_zero(s64 rec_temp1, s64 mult);
+
+int32_t dsi_phy_hw_v2_0_calc_clk_trail_rec_min(s64 temp_mul,
+ s64 frac, s64 mult);
+
+int32_t dsi_phy_hw_v2_0_calc_clk_trail_rec_max(s64 temp1, s64 mult);
+
+int32_t dsi_phy_hw_v2_0_calc_hs_zero(s64 temp1, s64 mult);
+
+void dsi_phy_hw_v2_0_calc_hs_trail(struct phy_clk_params *clk_params,
+ struct phy_timing_desc *desc);
+
+void dsi_phy_hw_v2_0_update_timing_params(struct dsi_phy_per_lane_cfgs *timing,
+ struct phy_timing_desc *desc);
+
+/* DSI PHY timing functions for 10nm */
+void dsi_phy_hw_v3_0_get_default_phy_params(struct phy_clk_params *params);
+
+int32_t dsi_phy_hw_v3_0_calc_clk_zero(s64 rec_temp1, s64 mult);
+
+int32_t dsi_phy_hw_v3_0_calc_clk_trail_rec_min(s64 temp_mul,
+ s64 frac, s64 mult);
+
+int32_t dsi_phy_hw_v3_0_calc_clk_trail_rec_max(s64 temp1, s64 mult);
+
+int32_t dsi_phy_hw_v3_0_calc_hs_zero(s64 temp1, s64 mult);
+
+void dsi_phy_hw_v3_0_calc_hs_trail(struct phy_clk_params *clk_params,
+ struct phy_timing_desc *desc);
+
+void dsi_phy_hw_v3_0_update_timing_params(struct dsi_phy_per_lane_cfgs *timing,
+ struct phy_timing_desc *desc);
+
+#endif /* _DSI_PHY_TIMING_CALC_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_timing_v2_0.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_timing_v2_0.c
new file mode 100644
index 0000000..d3fb091
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_timing_v2_0.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "dsi-phy-timing:" fmt
+#include "dsi_phy_timing_calc.h"
+
+void dsi_phy_hw_v2_0_get_default_phy_params(struct phy_clk_params *params)
+{
+ params->clk_prep_buf = 50;
+ params->clk_zero_buf = 2;
+ params->clk_trail_buf = 30;
+ params->hs_prep_buf = 50;
+ params->hs_zero_buf = 10;
+ params->hs_trail_buf = 30;
+ params->hs_rqst_buf = 0;
+ params->hs_exit_buf = 10;
+}
+
+int32_t dsi_phy_hw_v2_0_calc_clk_zero(s64 rec_temp1, s64 mult)
+{
+ s64 rec_temp2, rec_temp3;
+
+ rec_temp2 = (rec_temp1 - (11 * mult));
+ rec_temp3 = roundup(div_s64(rec_temp2, 8), mult);
+ return (div_s64(rec_temp3, mult) - 3);
+}
+
+int32_t dsi_phy_hw_v2_0_calc_clk_trail_rec_min(s64 temp_mul,
+ s64 frac, s64 mult)
+{
+ s64 rec_temp1, rec_temp2, rec_temp3;
+
+ rec_temp1 = temp_mul + frac + (3 * mult);
+ rec_temp2 = div_s64(rec_temp1, 8);
+ rec_temp3 = roundup(rec_temp2, mult);
+
+ return div_s64(rec_temp3, mult);
+}
+
+int32_t dsi_phy_hw_v2_0_calc_clk_trail_rec_max(s64 temp1, s64 mult)
+{
+ s64 rec_temp2, rec_temp3;
+
+ rec_temp2 = temp1 + (3 * mult);
+ rec_temp3 = rec_temp2 / 8;
+ return div_s64(rec_temp3, mult);
+
+}
+
+int32_t dsi_phy_hw_v2_0_calc_hs_zero(s64 temp1, s64 mult)
+{
+ s64 rec_temp2, rec_temp3, rec_min;
+
+ rec_temp2 = temp1 - (11 * mult);
+ rec_temp3 = roundup((rec_temp2 / 8), mult);
+ rec_min = rec_temp3 - (3 * mult);
+ return div_s64(rec_min, mult);
+}
+
+void dsi_phy_hw_v2_0_calc_hs_trail(struct phy_clk_params *clk_params,
+ struct phy_timing_desc *desc)
+{
+ s64 rec_temp1;
+ struct timing_entry *t = &desc->hs_trail;
+
+ t->rec_min = DIV_ROUND_UP(
+ ((t->mipi_min * clk_params->bitclk_mbps) +
+ (3 * clk_params->tlpx_numer_ns)),
+ (8 * clk_params->tlpx_numer_ns));
+
+ rec_temp1 = ((t->mipi_max * clk_params->bitclk_mbps) +
+ (3 * clk_params->tlpx_numer_ns));
+ t->rec_max = (rec_temp1 / (8 * clk_params->tlpx_numer_ns));
+}
+
+void dsi_phy_hw_v2_0_update_timing_params(
+ struct dsi_phy_per_lane_cfgs *timing,
+ struct phy_timing_desc *desc)
+{
+ int i = 0;
+
+ for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) {
+ timing->lane[i][0] = desc->hs_exit.reg_value;
+
+ if (i == DSI_LOGICAL_CLOCK_LANE)
+ timing->lane[i][1] = desc->clk_zero.reg_value;
+ else
+ timing->lane[i][1] = desc->hs_zero.reg_value;
+
+ if (i == DSI_LOGICAL_CLOCK_LANE)
+ timing->lane[i][2] = desc->clk_prepare.reg_value;
+ else
+ timing->lane[i][2] = desc->hs_prepare.reg_value;
+
+ if (i == DSI_LOGICAL_CLOCK_LANE)
+ timing->lane[i][3] = desc->clk_trail.reg_value;
+ else
+ timing->lane[i][3] = desc->hs_trail.reg_value;
+
+ if (i == DSI_LOGICAL_CLOCK_LANE)
+ timing->lane[i][4] = desc->hs_rqst_clk.reg_value;
+ else
+ timing->lane[i][4] = desc->hs_rqst.reg_value;
+
+ timing->lane[i][5] = 0x3;
+ timing->lane[i][6] = 0x4;
+ timing->lane[i][7] = 0xA0;
+ pr_debug("[%d][%d %d %d %d %d]\n", i, timing->lane[i][0],
+ timing->lane[i][1],
+ timing->lane[i][2],
+ timing->lane[i][3],
+ timing->lane[i][4]);
+ }
+ timing->count_per_lane = 8;
+}
+
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_timing_v3_0.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_timing_v3_0.c
new file mode 100644
index 0000000..c97c87d
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_timing_v3_0.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "dsi-phy-timing:" fmt
+#include "dsi_phy_timing_calc.h"
+
+void dsi_phy_hw_v3_0_get_default_phy_params(
+ struct phy_clk_params *params)
+{
+ params->clk_prep_buf = 0;
+ params->clk_zero_buf = 0;
+ params->clk_trail_buf = 0;
+ params->hs_prep_buf = 0;
+ params->hs_zero_buf = 0;
+ params->hs_trail_buf = 0;
+ params->hs_rqst_buf = 0;
+ params->hs_exit_buf = 0;
+}
+
+int32_t dsi_phy_hw_v3_0_calc_clk_zero(s64 rec_temp1, s64 mult)
+{
+ s64 rec_temp2, rec_temp3;
+
+ rec_temp2 = (rec_temp1 - mult);
+ rec_temp3 = roundup(div_s64(rec_temp2, 8), mult);
+ return (div_s64(rec_temp3, mult) - 1);
+}
+
+int32_t dsi_phy_hw_v3_0_calc_clk_trail_rec_min(s64 temp_mul,
+ s64 frac, s64 mult)
+{
+ s64 rec_temp1, rec_temp2, rec_temp3;
+
+ rec_temp1 = temp_mul + frac;
+ rec_temp2 = div_s64(rec_temp1, 8);
+ rec_temp3 = roundup(rec_temp2, mult);
+ return (div_s64(rec_temp3, mult) - 1);
+}
+
+int32_t dsi_phy_hw_v3_0_calc_clk_trail_rec_max(s64 temp1, s64 mult)
+{
+ s64 rec_temp2;
+
+ rec_temp2 = temp1 / 8;
+ return (div_s64(rec_temp2, mult) - 1);
+}
+
+int32_t dsi_phy_hw_v3_0_calc_hs_zero(s64 temp1, s64 mult)
+{
+ s64 rec_temp2, rec_min;
+
+ rec_temp2 = roundup((temp1 / 8), mult);
+ rec_min = rec_temp2 - (1 * mult);
+ return div_s64(rec_min, mult);
+}
+
+void dsi_phy_hw_v3_0_calc_hs_trail(struct phy_clk_params *clk_params,
+ struct phy_timing_desc *desc)
+{
+ s64 rec_temp1;
+ struct timing_entry *t = &desc->hs_trail;
+
+ t->rec_min = DIV_ROUND_UP(
+ (t->mipi_min * clk_params->bitclk_mbps),
+ (8 * clk_params->tlpx_numer_ns)) - 1;
+
+ rec_temp1 = (t->mipi_max * clk_params->bitclk_mbps);
+ t->rec_max =
+ (div_s64(rec_temp1, (8 * clk_params->tlpx_numer_ns))) - 1;
+}
+
+void dsi_phy_hw_v3_0_update_timing_params(
+ struct dsi_phy_per_lane_cfgs *timing,
+ struct phy_timing_desc *desc)
+{
+ timing->lane_v3[0] = 0x00;
+ timing->lane_v3[1] = desc->clk_zero.reg_value;
+ timing->lane_v3[2] = desc->clk_prepare.reg_value;
+ timing->lane_v3[3] = desc->clk_trail.reg_value;
+ timing->lane_v3[4] = desc->hs_exit.reg_value;
+ timing->lane_v3[5] = desc->hs_zero.reg_value;
+ timing->lane_v3[6] = desc->hs_prepare.reg_value;
+ timing->lane_v3[7] = desc->hs_trail.reg_value;
+ timing->lane_v3[8] = desc->hs_rqst.reg_value;
+ timing->lane_v3[9] = 0x03;
+ timing->lane_v3[10] = 0x04;
+ timing->lane_v3[11] = 0x00;
+
+ pr_debug("[%d %d %d %d]\n", timing->lane_v3[0],
+ timing->lane_v3[1], timing->lane_v3[2], timing->lane_v3[3]);
+ pr_debug("[%d %d %d %d]\n", timing->lane_v3[4],
+ timing->lane_v3[5], timing->lane_v3[6], timing->lane_v3[7]);
+ pr_debug("[%d %d %d %d]\n", timing->lane_v3[8],
+ timing->lane_v3[9], timing->lane_v3[10], timing->lane_v3[11]);
+ timing->count_per_lane = 12;
+}
+
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.c b/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.c
new file mode 100644
index 0000000..609c5ff
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "dsi_pwr.h"
+
+/*
+ * dsi_pwr_parse_supply_node() - parse power supply node from root device node
+ */
+static int dsi_pwr_parse_supply_node(struct device_node *root,
+ struct dsi_regulator_info *regs)
+{
+ int rc = 0;
+ int i = 0;
+ u32 tmp = 0;
+ struct device_node *node = NULL;
+
+ for_each_child_of_node(root, node) {
+ const char *st = NULL;
+
+ rc = of_property_read_string(node, "qcom,supply-name", &st);
+ if (rc) {
+ pr_err("failed to read name, rc = %d\n", rc);
+ goto error;
+ }
+
+ snprintf(regs->vregs[i].vreg_name,
+ ARRAY_SIZE(regs->vregs[i].vreg_name),
+ "%s", st);
+
+ rc = of_property_read_u32(node, "qcom,supply-min-voltage",
+ &tmp);
+ if (rc) {
+ pr_err("failed to read min voltage, rc = %d\n", rc);
+ goto error;
+ }
+ regs->vregs[i].min_voltage = tmp;
+
+ rc = of_property_read_u32(node, "qcom,supply-max-voltage",
+ &tmp);
+ if (rc) {
+ pr_err("failed to read max voltage, rc = %d\n", rc);
+ goto error;
+ }
+ regs->vregs[i].max_voltage = tmp;
+
+ rc = of_property_read_u32(node, "qcom,supply-enable-load",
+ &tmp);
+ if (rc) {
+ pr_err("failed to read enable load, rc = %d\n", rc);
+ goto error;
+ }
+ regs->vregs[i].enable_load = tmp;
+
+ rc = of_property_read_u32(node, "qcom,supply-disable-load",
+ &tmp);
+ if (rc) {
+ pr_err("failed to read disable load, rc = %d\n", rc);
+ goto error;
+ }
+ regs->vregs[i].disable_load = tmp;
+
+ /* Optional values */
+ rc = of_property_read_u32(node, "qcom,supply-pre-on-sleep",
+ &tmp);
+ if (rc) {
+ pr_debug("pre-on-sleep not specified\n");
+ rc = 0;
+ } else {
+ regs->vregs[i].pre_on_sleep = tmp;
+ }
+
+ rc = of_property_read_u32(node, "qcom,supply-pre-off-sleep",
+ &tmp);
+ if (rc) {
+ pr_debug("pre-off-sleep not specified\n");
+ rc = 0;
+ } else {
+ regs->vregs[i].pre_off_sleep = tmp;
+ }
+
+ rc = of_property_read_u32(node, "qcom,supply-post-on-sleep",
+ &tmp);
+ if (rc) {
+ pr_debug("post-on-sleep not specified\n");
+ rc = 0;
+ } else {
+ regs->vregs[i].post_on_sleep = tmp;
+ }
+
+ rc = of_property_read_u32(node, "qcom,supply-post-off-sleep",
+ &tmp);
+ if (rc) {
+ pr_debug("post-off-sleep not specified\n");
+ rc = 0;
+ } else {
+ regs->vregs[i].post_off_sleep = tmp;
+ }
+
+ ++i;
+ pr_debug("[%s] minv=%d maxv=%d, en_load=%d, dis_load=%d\n",
+ regs->vregs[i].vreg_name,
+ regs->vregs[i].min_voltage,
+ regs->vregs[i].max_voltage,
+ regs->vregs[i].enable_load,
+ regs->vregs[i].disable_load);
+ }
+
+error:
+ return rc;
+}
+
+/**
+ * dsi_pwr_enable_vregs() - enable/disable regulators
+ */
+static int dsi_pwr_enable_vregs(struct dsi_regulator_info *regs, bool enable)
+{
+ int rc = 0, i = 0;
+ struct dsi_vreg *vreg;
+ int num_of_v = 0;
+
+ if (enable) {
+ for (i = 0; i < regs->count; i++) {
+ vreg = ®s->vregs[i];
+ if (vreg->pre_on_sleep)
+ msleep(vreg->pre_on_sleep);
+
+ rc = regulator_set_load(vreg->vreg,
+ vreg->enable_load);
+ if (rc < 0) {
+ pr_err("Setting optimum mode failed for %s\n",
+ vreg->vreg_name);
+ goto error;
+ }
+ num_of_v = regulator_count_voltages(vreg->vreg);
+ if (num_of_v > 0) {
+ rc = regulator_set_voltage(vreg->vreg,
+ vreg->min_voltage,
+ vreg->max_voltage);
+ if (rc) {
+ pr_err("Set voltage(%s) fail, rc=%d\n",
+ vreg->vreg_name, rc);
+ goto error_disable_opt_mode;
+ }
+ }
+
+ rc = regulator_enable(vreg->vreg);
+ if (rc) {
+ pr_err("enable failed for %s, rc=%d\n",
+ vreg->vreg_name, rc);
+ goto error_disable_voltage;
+ }
+
+ if (vreg->post_on_sleep)
+ msleep(vreg->post_on_sleep);
+ }
+ } else {
+ for (i = (regs->count - 1); i >= 0; i--) {
+ if (regs->vregs[i].pre_off_sleep)
+ msleep(regs->vregs[i].pre_off_sleep);
+
+ (void)regulator_set_load(regs->vregs[i].vreg,
+ regs->vregs[i].disable_load);
+ (void)regulator_disable(regs->vregs[i].vreg);
+
+ if (regs->vregs[i].post_off_sleep)
+ msleep(regs->vregs[i].post_off_sleep);
+ }
+ }
+
+ return 0;
+error_disable_opt_mode:
+ (void)regulator_set_load(regs->vregs[i].vreg,
+ regs->vregs[i].disable_load);
+
+error_disable_voltage:
+ if (num_of_v > 0)
+ (void)regulator_set_voltage(regs->vregs[i].vreg,
+ 0, regs->vregs[i].max_voltage);
+error:
+ for (i--; i >= 0; i--) {
+ if (regs->vregs[i].pre_off_sleep)
+ msleep(regs->vregs[i].pre_off_sleep);
+
+ (void)regulator_set_load(regs->vregs[i].vreg,
+ regs->vregs[i].disable_load);
+
+ num_of_v = regulator_count_voltages(regs->vregs[i].vreg);
+ if (num_of_v > 0)
+ (void)regulator_set_voltage(regs->vregs[i].vreg,
+ 0, regs->vregs[i].max_voltage);
+
+ (void)regulator_disable(regs->vregs[i].vreg);
+
+ if (regs->vregs[i].post_off_sleep)
+ msleep(regs->vregs[i].post_off_sleep);
+ }
+
+ return rc;
+}
+
+/**
+* dsi_pwr_of_get_vreg_data - Parse regulator supply information
+* @of_node: Device of node to parse for supply information.
+* @regs: Pointer where regulator information will be copied to.
+* @supply_name: Name of the supply node.
+*
+* return: error code in case of failure or 0 for success.
+*/
+int dsi_pwr_of_get_vreg_data(struct device_node *of_node,
+ struct dsi_regulator_info *regs,
+ char *supply_name)
+{
+ int rc = 0;
+ struct device_node *supply_root_node = NULL;
+
+ if (!of_node || !regs) {
+ pr_err("Bad params\n");
+ return -EINVAL;
+ }
+
+ regs->count = 0;
+ supply_root_node = of_get_child_by_name(of_node, supply_name);
+ if (!supply_root_node) {
+ supply_root_node = of_parse_phandle(of_node, supply_name, 0);
+ if (!supply_root_node) {
+ pr_err("No supply entry present for %s\n", supply_name);
+ return -EINVAL;
+ }
+ }
+
+ regs->count = of_get_available_child_count(supply_root_node);
+ if (regs->count == 0) {
+ pr_err("No vregs defined for %s\n", supply_name);
+ return -EINVAL;
+ }
+
+ regs->vregs = kcalloc(regs->count, sizeof(*regs->vregs), GFP_KERNEL);
+ if (!regs->vregs) {
+ regs->count = 0;
+ return -ENOMEM;
+ }
+
+ rc = dsi_pwr_parse_supply_node(supply_root_node, regs);
+ if (rc) {
+ pr_err("failed to parse supply node for %s, rc = %d\n",
+ supply_name, rc);
+
+ kfree(regs->vregs);
+ regs->vregs = NULL;
+ regs->count = 0;
+ }
+
+ return rc;
+}
+
+/**
+ * dsi_pwr_get_dt_vreg_data - parse regulator supply information
+ * @dev: Device whose of_node needs to be parsed.
+ * @regs: Pointer where regulator information will be copied to.
+ * @supply_name: Name of the supply node.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_pwr_get_dt_vreg_data(struct device *dev,
+ struct dsi_regulator_info *regs,
+ char *supply_name)
+{
+ int rc = 0;
+ struct device_node *of_node = NULL;
+ struct device_node *supply_node = NULL;
+ struct device_node *supply_root_node = NULL;
+
+ if (!dev || !regs) {
+ pr_err("Bad params\n");
+ return -EINVAL;
+ }
+
+ of_node = dev->of_node;
+ regs->count = 0;
+ supply_root_node = of_get_child_by_name(of_node, supply_name);
+ if (!supply_root_node) {
+ supply_root_node = of_parse_phandle(of_node, supply_name, 0);
+ if (!supply_root_node) {
+ pr_err("No supply entry present for %s\n", supply_name);
+ return -EINVAL;
+ }
+ }
+
+ for_each_child_of_node(supply_root_node, supply_node)
+ regs->count++;
+
+ if (regs->count == 0) {
+ pr_err("No vregs defined for %s\n", supply_name);
+ return -EINVAL;
+ }
+
+ regs->vregs = devm_kcalloc(dev, regs->count, sizeof(*regs->vregs),
+ GFP_KERNEL);
+ if (!regs->vregs) {
+ regs->count = 0;
+ return -ENOMEM;
+ }
+
+ rc = dsi_pwr_parse_supply_node(supply_root_node, regs);
+ if (rc) {
+ pr_err("failed to parse supply node for %s, rc = %d\n",
+ supply_name, rc);
+ devm_kfree(dev, regs->vregs);
+ regs->vregs = NULL;
+ regs->count = 0;
+ }
+
+ return rc;
+}
+
+/**
+ * dsi_pwr_enable_regulator() - enable a set of regulators
+ * @regs: Pointer to set of regulators to enable or disable.
+ * @enable: Enable/Disable regulators.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_pwr_enable_regulator(struct dsi_regulator_info *regs, bool enable)
+{
+ int rc = 0;
+
+ if (enable) {
+ if (regs->refcount == 0) {
+ rc = dsi_pwr_enable_vregs(regs, true);
+ if (rc)
+ pr_err("failed to enable regulators\n");
+ }
+ regs->refcount++;
+ } else {
+ if (regs->refcount == 0) {
+ pr_err("Unbalanced regulator off\n");
+ } else {
+ regs->refcount--;
+ if (regs->refcount == 0) {
+ rc = dsi_pwr_enable_vregs(regs, false);
+ if (rc)
+ pr_err("failed to disable vregs\n");
+ }
+ }
+ }
+
+ return rc;
+}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.h b/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.h
new file mode 100644
index 0000000..4d85244
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DSI_PWR_H_
+#define _DSI_PWR_H_
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/regulator/consumer.h>
+
+/**
+ * struct dsi_vreg - regulator information for DSI regulators
+ * @vreg: Handle to the regulator.
+ * @vreg_name: Regulator name.
+ * @min_voltage: Minimum voltage in uV.
+ * @max_voltage: Maximum voltage in uV.
+ * @enable_load: Load, in uA, when enabled.
+ * @disable_load: Load, in uA, when disabled.
+ * @pre_on_sleep: Sleep, in ms, before enabling the regulator.
+ * @post_on_sleep: Sleep, in ms, after enabling the regulator.
+ * @pre_off_sleep: Sleep, in ms, before disabling the regulator.
+ * @post_off_sleep: Sleep, in ms, after disabling the regulator.
+ */
+struct dsi_vreg {
+ struct regulator *vreg;
+ char vreg_name[32];
+ u32 min_voltage;
+ u32 max_voltage;
+ u32 enable_load;
+ u32 disable_load;
+ u32 pre_on_sleep;
+ u32 post_on_sleep;
+ u32 pre_off_sleep;
+ u32 post_off_sleep;
+};
+
+/**
+ * struct dsi_regulator_info - set of vregs that are turned on/off together.
+ * @vregs: Array of dsi_vreg structures.
+ * @count: Number of vregs.
+ * @refcount: Reference counting for enabling.
+ */
+struct dsi_regulator_info {
+ struct dsi_vreg *vregs;
+ u32 count;
+ u32 refcount;
+};
+
+/**
+ * dsi_pwr_of_get_vreg_data - parse regulator supply information
+ * @of_node: Device of node to parse for supply information.
+ * @regs: Pointer where regulator information will be copied to.
+ * @supply_name: Name of the supply node.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_pwr_of_get_vreg_data(struct device_node *of_node,
+ struct dsi_regulator_info *regs,
+ char *supply_name);
+
+/**
+ * dsi_pwr_get_dt_vreg_data - parse regulator supply information
+ * @dev: Device whose of_node needs to be parsed.
+ * @regs: Pointer where regulator information will be copied to.
+ * @supply_name: Name of the supply node.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_pwr_get_dt_vreg_data(struct device *dev,
+ struct dsi_regulator_info *regs,
+ char *supply_name);
+
+/**
+ * dsi_pwr_enable_regulator() - enable a set of regulators
+ * @regs: Pointer to set of regulators to enable or disable.
+ * @enable: Enable/Disable regulators.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_pwr_enable_regulator(struct dsi_regulator_info *regs, bool enable);
+#endif /* _DSI_PWR_H_ */
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
index a498a29..ff6802e 100644
--- a/drivers/gpu/drm/msm/msm_atomic.c
+++ b/drivers/gpu/drm/msm/msm_atomic.c
@@ -66,8 +66,7 @@
static void msm_atomic_wait_for_commit_done(
struct drm_device *dev,
- struct drm_atomic_state *old_state,
- int modeset_flags)
+ struct drm_atomic_state *old_state)
{
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
@@ -76,16 +75,9 @@
int i;
for_each_crtc_in_state(old_state, crtc, crtc_state, i) {
- int private_flags;
-
if (!crtc->state->enable)
continue;
- /* If specified, only wait if requested flag is true */
- private_flags = crtc->state->adjusted_mode.private_flags;
- if (modeset_flags && !(modeset_flags & private_flags))
- continue;
-
/* Legacy cursor ioctls are completely unsynced, and userspace
* relies on that (by doing tons of cursor updates). */
if (old_state->legacy_cursor_update)
@@ -318,11 +310,10 @@
else
funcs->commit(crtc);
}
- }
- /* ensure bridge/encoder updates happen on same vblank */
- msm_atomic_wait_for_commit_done(dev, old_state,
- MSM_MODE_FLAG_VBLANK_PRE_MODESET);
+ if (msm_needs_vblank_pre_modeset(&crtc->state->adjusted_mode))
+ drm_crtc_wait_one_vblank(crtc);
+ }
for_each_connector_in_state(old_state, connector, old_conn_state, i) {
const struct drm_encoder_helper_funcs *funcs;
@@ -417,7 +408,7 @@
* not be critical path)
*/
- msm_atomic_wait_for_commit_done(dev, state, 0);
+ msm_atomic_wait_for_commit_done(dev, state);
drm_atomic_helper_cleanup_planes(dev, state);
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 329381e..951594c 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -258,7 +258,10 @@
drm_mode_config_cleanup(ddev);
drm_vblank_cleanup(ddev);
- drm_dev_unregister(ddev);
+ if (priv->registered) {
+ drm_dev_unregister(ddev);
+ priv->registered = false;
+ }
#ifdef CONFIG_DRM_FBDEV_EMULATION
if (fbdev && priv->fbdev)
@@ -291,15 +294,13 @@
priv->vram.paddr, attrs);
}
- sde_evtlog_destroy();
+ sde_dbg_destroy();
sde_power_client_destroy(&priv->phandle, priv->pclient);
sde_power_resource_deinit(pdev, &priv->phandle);
component_unbind_all(dev, ddev);
- sde_power_client_destroy(&priv->phandle, priv->pclient);
-
sde_power_resource_deinit(pdev, &priv->phandle);
msm_mdss_destroy(ddev);
@@ -433,12 +434,18 @@
}
#endif
+static int msm_power_enable_wrapper(void *handle, void *client, bool enable)
+{
+ return sde_power_resource_enable(handle, client, enable);
+}
+
static int msm_drm_init(struct device *dev, struct drm_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct drm_device *ddev;
struct msm_drm_private *priv;
struct msm_kms *kms;
+ struct sde_dbg_power_ctrl dbg_power_ctrl = { 0 };
int ret, i;
ddev = drm_dev_alloc(drv, dev);
@@ -451,10 +458,6 @@
platform_set_drvdata(pdev, ddev);
ddev->platformdev = pdev;
- ret = drm_dev_register(ddev, 0);
- if (ret)
- goto fail;
-
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
@@ -499,9 +502,13 @@
if (ret)
goto fail;
- ret = sde_evtlog_init(ddev->primary->debugfs_root);
+ dbg_power_ctrl.handle = &priv->phandle;
+ dbg_power_ctrl.client = priv->pclient;
+ dbg_power_ctrl.enable_fn = msm_power_enable_wrapper;
+ ret = sde_dbg_init(ddev->primary->debugfs_root, &pdev->dev,
+ &dbg_power_ctrl);
if (ret) {
- dev_err(dev, "failed to init evtlog: %d\n", ret);
+ dev_err(dev, "failed to init sde dbg: %d\n", ret);
goto fail;
}
@@ -582,6 +589,10 @@
}
}
+ ret = drm_dev_register(ddev, 0);
+ if (ret)
+ goto fail;
+ priv->registered = true;
drm_mode_config_reset(ddev);
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 499dfcc..585e206 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -126,6 +126,9 @@
CRTC_PROP_OUTPUT_FENCE,
CRTC_PROP_OUTPUT_FENCE_OFFSET,
CRTC_PROP_DIM_LAYER_V1,
+ CRTC_PROP_CORE_CLK,
+ CRTC_PROP_CORE_AB,
+ CRTC_PROP_CORE_IB,
/* total # of properties */
CRTC_PROP_COUNT
@@ -343,6 +346,9 @@
/* list of clients waiting for events */
struct list_head client_event_list;
+
+ /* whether registered and drm_dev_unregister should be called */
+ bool registered;
};
struct msm_format {
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c
index 008a527..6892646 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.c
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c
@@ -453,7 +453,7 @@
}
static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node,
- struct sde_crtc *sde_crtc)
+ struct sde_crtc *sde_crtc, u32 last_feature)
{
struct sde_hw_cp_cfg hw_cfg;
struct sde_hw_mixer *hw_lm;
@@ -469,6 +469,10 @@
hw_lm = sde_crtc->mixers[i].hw_lm;
hw_dspp = sde_crtc->mixers[i].hw_dspp;
hw_cfg.ctl = sde_crtc->mixers[i].hw_ctl;
+ if (i == num_mixers - 1)
+ hw_cfg.last_feature = last_feature;
+ else
+ hw_cfg.last_feature = 0;
switch (prop_node->feature) {
case SDE_CP_CRTC_DSPP_VLUT:
if (!hw_dspp || !hw_dspp->ops.setup_vlut) {
@@ -587,6 +591,7 @@
struct sde_hw_ctl *ctl;
uint32_t flush_mask = 0;
u32 num_mixers = 0, i = 0;
+ u32 num_of_features;
if (!crtc || !crtc->dev) {
DRM_ERROR("invalid crtc %pK dev %pK\n", crtc,
@@ -612,9 +617,15 @@
return;
}
+ num_of_features = 0;
+ list_for_each_entry(prop_node, &sde_crtc->dirty_list, dirty_list)
+ num_of_features++;
+
list_for_each_entry_safe(prop_node, n, &sde_crtc->dirty_list,
dirty_list) {
- sde_cp_crtc_setfeature(prop_node, sde_crtc);
+ num_of_features--;
+ sde_cp_crtc_setfeature(prop_node, sde_crtc,
+ (num_of_features == 0));
/* Set the flush flag to true */
if (prop_node->is_dspp_feature)
set_dspp_flush = true;
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index ac9997c..59db57a 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -526,23 +526,17 @@
goto error_cleanup_conn;
}
- rc = drm_connector_register(&c_conn->base);
- if (rc) {
- SDE_ERROR("failed to register drm connector, %d\n", rc);
- goto error_cleanup_fence;
- }
-
rc = drm_mode_connector_attach_encoder(&c_conn->base, encoder);
if (rc) {
SDE_ERROR("failed to attach encoder to connector, %d\n", rc);
- goto error_unregister_conn;
+ goto error_cleanup_fence;
}
if (c_conn->ops.set_backlight) {
rc = sde_backlight_setup(&c_conn->base);
if (rc) {
pr_err("failed to setup backlight, rc=%d\n", rc);
- goto error_unregister_conn;
+ goto error_cleanup_fence;
}
}
@@ -557,7 +551,7 @@
if (!info) {
SDE_ERROR("failed to allocate info buffer\n");
rc = -ENOMEM;
- goto error_unregister_conn;
+ goto error_cleanup_fence;
}
sde_kms_info_reset(info);
@@ -565,7 +559,7 @@
if (rc) {
SDE_ERROR("post-init failed, %d\n", rc);
kfree(info);
- goto error_unregister_conn;
+ goto error_cleanup_fence;
}
msm_property_install_blob(&c_conn->property_info,
@@ -611,8 +605,6 @@
if (c_conn->blob_caps)
drm_property_unreference_blob(c_conn->blob_caps);
msm_property_destroy(&c_conn->property_info);
-error_unregister_conn:
- drm_connector_unregister(&c_conn->base);
error_cleanup_fence:
sde_fence_deinit(&c_conn->retire_fence);
error_cleanup_conn:
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index 9580282..665d337 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -113,6 +113,13 @@
int (*get_info)(struct msm_display_info *info, void *display);
int (*set_backlight)(void *display, u32 bl_lvl);
+
+ /**
+ * soft_reset - perform a soft reset on the connector
+ * @display: Pointer to private display structure
+ * Return: Zero on success, -ERROR otherwise
+ */
+ int (*soft_reset)(void *display);
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.c b/drivers/gpu/drm/msm/sde/sde_core_irq.c
index 502a7fa..67b577b 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.c
@@ -32,7 +32,7 @@
struct sde_irq_callback *cb;
unsigned long irq_flags;
- SDE_DEBUG("irq_idx=%d\n", irq_idx);
+ pr_debug("irq_idx=%d\n", irq_idx);
if (list_empty(&irq_obj->irq_cb_tbl[irq_idx]))
SDE_ERROR("irq_idx=%d has no registered callback\n", irq_idx);
diff --git a/drivers/gpu/drm/msm/sde/sde_core_perf.c b/drivers/gpu/drm/msm/sde/sde_core_perf.c
new file mode 100644
index 0000000..0ba644d
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_core_perf.c
@@ -0,0 +1,610 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
+
+#include <linux/debugfs.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/sort.h>
+#include <linux/clk.h>
+#include <linux/bitmap.h>
+
+#include "msm_prop.h"
+
+#include "sde_kms.h"
+#include "sde_fence.h"
+#include "sde_formats.h"
+#include "sde_hw_sspp.h"
+#include "sde_trace.h"
+#include "sde_crtc.h"
+#include "sde_plane.h"
+#include "sde_encoder.h"
+#include "sde_wb.h"
+#include "sde_core_perf.h"
+#include "sde_trace.h"
+
+static struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc)
+{
+ struct msm_drm_private *priv;
+
+ if (!crtc->dev || !crtc->dev->dev_private) {
+ SDE_ERROR("invalid device\n");
+ return NULL;
+ }
+
+ priv = crtc->dev->dev_private;
+ if (!priv || !priv->kms) {
+ SDE_ERROR("invalid kms\n");
+ return NULL;
+ }
+
+ return to_sde_kms(priv->kms);
+}
+
+static bool _sde_core_perf_crtc_is_power_on(struct drm_crtc *crtc)
+{
+ return sde_crtc_is_enabled(crtc);
+}
+
+static bool _sde_core_video_mode_intf_connected(struct drm_crtc *crtc)
+{
+ struct drm_crtc *tmp_crtc;
+
+ if (!crtc)
+ return 0;
+
+ drm_for_each_crtc(tmp_crtc, crtc->dev) {
+ if ((sde_crtc_get_intf_mode(tmp_crtc) == INTF_MODE_VIDEO) &&
+ _sde_core_perf_crtc_is_power_on(tmp_crtc)) {
+ SDE_DEBUG("video interface connected crtc:%d\n",
+ tmp_crtc->base.id);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int sde_core_perf_crtc_check(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ u32 bw, threshold;
+ u64 bw_sum_of_intfs = 0;
+ bool is_video_mode;
+ struct sde_crtc_state *sde_cstate;
+ struct drm_crtc *tmp_crtc;
+ struct sde_kms *kms;
+
+ if (!crtc || !state) {
+ SDE_ERROR("invalid crtc\n");
+ return -EINVAL;
+ }
+
+ kms = _sde_crtc_get_kms(crtc);
+ if (!kms || !kms->catalog) {
+ SDE_ERROR("invalid parameters\n");
+ return 0;
+ }
+
+ /* we only need bandwidth check on real-time clients (interfaces) */
+ if (sde_crtc_is_wb(crtc))
+ return 0;
+
+ sde_cstate = to_sde_crtc_state(state);
+
+ bw_sum_of_intfs = sde_crtc_get_property(sde_cstate, CRTC_PROP_CORE_AB);
+
+ drm_for_each_crtc(tmp_crtc, crtc->dev) {
+ if (_sde_core_perf_crtc_is_power_on(tmp_crtc) &&
+ sde_crtc_is_rt(tmp_crtc) && tmp_crtc != crtc) {
+ struct sde_crtc_state *tmp_cstate =
+ to_sde_crtc_state(tmp_crtc->state);
+
+ bw_sum_of_intfs += tmp_cstate->cur_perf.bw_ctl;
+ }
+ }
+
+ /* convert bandwidth to kb */
+ bw = DIV_ROUND_UP_ULL(bw_sum_of_intfs, 1000);
+ SDE_DEBUG("calculated bandwidth=%uk\n", bw);
+
+ is_video_mode = sde_crtc_get_intf_mode(crtc) == INTF_MODE_VIDEO;
+ threshold = (is_video_mode ||
+ _sde_core_video_mode_intf_connected(crtc)) ?
+ kms->catalog->perf.max_bw_low : kms->catalog->perf.max_bw_high;
+
+ SDE_DEBUG("final threshold bw limit = %d\n", threshold);
+
+ if (!threshold) {
+ sde_cstate->cur_perf.bw_ctl = 0;
+ SDE_ERROR("no bandwidth limits specified\n");
+ return -E2BIG;
+ } else if (bw > threshold) {
+ sde_cstate->cur_perf.bw_ctl = 0;
+ SDE_DEBUG("exceeds bandwidth: %ukb > %ukb\n", bw, threshold);
+ return -E2BIG;
+ }
+
+ return 0;
+}
+
+static void _sde_core_perf_calc_crtc(struct sde_kms *kms,
+ struct drm_crtc *crtc,
+ struct sde_core_perf_params *perf)
+{
+ struct sde_crtc_state *sde_cstate;
+
+ sde_cstate = to_sde_crtc_state(crtc->state);
+ memset(perf, 0, sizeof(struct sde_core_perf_params));
+
+ perf->bw_ctl = sde_crtc_get_property(sde_cstate, CRTC_PROP_CORE_AB);
+ perf->max_per_pipe_ib =
+ sde_crtc_get_property(sde_cstate, CRTC_PROP_CORE_IB);
+ perf->core_clk_rate =
+ sde_crtc_get_property(sde_cstate, CRTC_PROP_CORE_CLK);
+
+ SDE_DEBUG("crtc=%d clk_rate=%u ib=%llu ab=%llu\n",
+ crtc->base.id, perf->core_clk_rate,
+ perf->max_per_pipe_ib, perf->bw_ctl);
+}
+
+static u64 _sde_core_perf_crtc_calc_client_vote(struct sde_kms *kms,
+ struct drm_crtc *crtc, struct sde_core_perf_params *perf,
+ bool nrt_client, u32 core_clk)
+{
+ u64 bw_sum_of_intfs = 0;
+ struct drm_crtc *tmp_crtc;
+
+ drm_for_each_crtc(tmp_crtc, crtc->dev) {
+ if (_sde_core_perf_crtc_is_power_on(crtc) &&
+ /* RealTime clients */
+ ((!nrt_client) ||
+ /* Non-RealTime clients */
+ (nrt_client && sde_crtc_is_nrt(tmp_crtc)))) {
+ struct sde_crtc_state *sde_cstate =
+ to_sde_crtc_state(tmp_crtc->state);
+
+ perf->max_per_pipe_ib = max(perf->max_per_pipe_ib,
+ sde_cstate->cur_perf.max_per_pipe_ib);
+
+ bw_sum_of_intfs += sde_cstate->cur_perf.bw_ctl;
+
+ SDE_DEBUG("crtc=%d bw=%llu\n",
+ tmp_crtc->base.id,
+ sde_cstate->cur_perf.bw_ctl);
+ }
+ }
+
+ return bw_sum_of_intfs;
+}
+
+static void _sde_core_perf_crtc_update_client_vote(struct sde_kms *kms,
+ struct sde_core_perf_params *params, bool nrt_client, u64 bw_vote)
+{
+ struct msm_drm_private *priv = kms->dev->dev_private;
+ u64 bus_ab_quota, bus_ib_quota;
+
+ bus_ab_quota = max(bw_vote, kms->perf.perf_tune.min_bus_vote);
+ bus_ib_quota = params->max_per_pipe_ib;
+
+ SDE_ATRACE_INT("bus_quota", bus_ib_quota);
+ sde_power_data_bus_set_quota(&priv->phandle, kms->core_client,
+ nrt_client ? SDE_POWER_HANDLE_DATA_BUS_CLIENT_NRT :
+ SDE_POWER_HANDLE_DATA_BUS_CLIENT_RT,
+ bus_ab_quota, bus_ib_quota);
+ SDE_DEBUG("client:%s ab=%llu ib=%llu\n", nrt_client ? "nrt" : "rt",
+ bus_ab_quota, bus_ib_quota);
+}
+
+static void _sde_core_perf_crtc_update_bus(struct sde_kms *kms,
+ struct drm_crtc *crtc, u32 core_clk)
+{
+ u64 bw_sum_of_rt_intfs = 0, bw_sum_of_nrt_intfs = 0;
+ struct sde_core_perf_params params = {0};
+
+ SDE_ATRACE_BEGIN(__func__);
+
+ /*
+ * non-real time client
+ */
+ if (sde_crtc_is_nrt(crtc)) {
+ bw_sum_of_nrt_intfs = _sde_core_perf_crtc_calc_client_vote(
+ kms, crtc, ¶ms, true, core_clk);
+ _sde_core_perf_crtc_update_client_vote(kms, ¶ms, true,
+ bw_sum_of_nrt_intfs);
+ }
+
+ /*
+ * real time client
+ */
+ if (!sde_crtc_is_nrt(crtc) ||
+ sde_crtc_is_wb(crtc)) {
+ bw_sum_of_rt_intfs = _sde_core_perf_crtc_calc_client_vote(kms,
+ crtc, ¶ms, false, core_clk);
+ _sde_core_perf_crtc_update_client_vote(kms, ¶ms, false,
+ bw_sum_of_rt_intfs);
+ }
+
+ SDE_ATRACE_END(__func__);
+}
+
+/**
+ * @sde_core_perf_crtc_release_bw() - request zero bandwidth
+ * @crtc - pointer to a crtc
+ *
+ * Function checks a state variable for the crtc, if all pending commit
+ * requests are done, meaning no more bandwidth is needed, release
+ * bandwidth request.
+ */
+void sde_core_perf_crtc_release_bw(struct drm_crtc *crtc)
+{
+ struct drm_crtc *tmp_crtc;
+ struct sde_crtc_state *sde_cstate;
+ struct sde_kms *kms;
+
+ if (!crtc) {
+ SDE_ERROR("invalid crtc\n");
+ return;
+ }
+
+ kms = _sde_crtc_get_kms(crtc);
+ if (!kms || !kms->catalog) {
+ SDE_ERROR("invalid kms\n");
+ return;
+ }
+
+ sde_cstate = to_sde_crtc_state(crtc->state);
+
+ /* only do this for command panel or writeback */
+ if ((sde_crtc_get_intf_mode(crtc) != INTF_MODE_CMD) &&
+ (sde_crtc_get_intf_mode(crtc) != INTF_MODE_WB_LINE))
+ return;
+
+ /*
+ * If video interface present, cmd panel bandwidth cannot be
+ * released.
+ */
+ if (sde_crtc_get_intf_mode(crtc) == INTF_MODE_CMD)
+ drm_for_each_crtc(tmp_crtc, crtc->dev) {
+ if (_sde_core_perf_crtc_is_power_on(tmp_crtc) &&
+ sde_crtc_get_intf_mode(tmp_crtc) ==
+ INTF_MODE_VIDEO)
+ return;
+ }
+
+ /* Release the bandwidth */
+ if (kms->perf.enable_bw_release) {
+ trace_sde_cmd_release_bw(crtc->base.id);
+ sde_cstate->cur_perf.bw_ctl = 0;
+ sde_cstate->new_perf.bw_ctl = 0;
+ SDE_DEBUG("Release BW crtc=%d\n", crtc->base.id);
+ _sde_core_perf_crtc_update_bus(kms, crtc, 0);
+ }
+}
+
+static int _sde_core_select_clk_lvl(struct sde_kms *kms,
+ u32 clk_rate)
+{
+ return clk_round_rate(kms->perf.core_clk, clk_rate);
+}
+
+static u32 _sde_core_perf_get_core_clk_rate(struct sde_kms *kms)
+{
+ u32 clk_rate = 0;
+ struct drm_crtc *crtc;
+ struct sde_crtc_state *sde_cstate;
+ int ncrtc = 0;
+
+ drm_for_each_crtc(crtc, kms->dev) {
+ if (_sde_core_perf_crtc_is_power_on(crtc)) {
+ sde_cstate = to_sde_crtc_state(crtc->state);
+ clk_rate = max(sde_cstate->cur_perf.core_clk_rate,
+ clk_rate);
+ clk_rate = clk_round_rate(kms->perf.core_clk, clk_rate);
+ }
+ ncrtc++;
+ }
+ clk_rate = _sde_core_select_clk_lvl(kms, clk_rate);
+
+ SDE_DEBUG("clk:%u ncrtc:%d\n", clk_rate, ncrtc);
+
+ return clk_rate;
+}
+
+void sde_core_perf_crtc_update(struct drm_crtc *crtc,
+ int params_changed, bool stop_req)
+{
+ struct sde_core_perf_params *new, *old;
+ int update_bus = 0, update_clk = 0;
+ u32 clk_rate = 0;
+ struct sde_crtc *sde_crtc;
+ struct sde_crtc_state *sde_cstate;
+ int ret;
+ struct msm_drm_private *priv;
+ struct sde_kms *kms;
+
+ if (!crtc) {
+ SDE_ERROR("invalid crtc\n");
+ return;
+ }
+
+ kms = _sde_crtc_get_kms(crtc);
+ if (!kms || !kms->catalog) {
+ SDE_ERROR("invalid kms\n");
+ return;
+ }
+ priv = kms->dev->dev_private;
+
+ sde_crtc = to_sde_crtc(crtc);
+ sde_cstate = to_sde_crtc_state(crtc->state);
+
+ SDE_DEBUG("crtc:%d stop_req:%d core_clk:%u\n",
+ crtc->base.id, stop_req, kms->perf.core_clk_rate);
+
+ SDE_ATRACE_BEGIN(__func__);
+
+ old = &sde_cstate->cur_perf;
+ new = &sde_cstate->new_perf;
+
+ if (_sde_core_perf_crtc_is_power_on(crtc) && !stop_req) {
+ if (params_changed)
+ _sde_core_perf_calc_crtc(kms, crtc, new);
+
+ /*
+ * cases for bus bandwidth update.
+ * 1. new bandwidth vote or writeback output vote
+ * are higher than current vote for update request.
+ * 2. new bandwidth vote or writeback output vote are
+ * lower than current vote at end of commit or stop.
+ */
+ if ((params_changed && ((new->bw_ctl > old->bw_ctl))) ||
+ (!params_changed && ((new->bw_ctl < old->bw_ctl)))) {
+ SDE_DEBUG("crtc=%d p=%d new_bw=%llu,old_bw=%llu\n",
+ crtc->base.id, params_changed, new->bw_ctl,
+ old->bw_ctl);
+ old->bw_ctl = new->bw_ctl;
+ old->max_per_pipe_ib = new->max_per_pipe_ib;
+ update_bus = 1;
+ }
+
+ if ((params_changed &&
+ (new->core_clk_rate > old->core_clk_rate)) ||
+ (!params_changed &&
+ (new->core_clk_rate < old->core_clk_rate))) {
+ old->core_clk_rate = new->core_clk_rate;
+ update_clk = 1;
+ }
+ } else {
+ SDE_DEBUG("crtc=%d disable\n", crtc->base.id);
+ memset(old, 0, sizeof(*old));
+ memset(new, 0, sizeof(*new));
+ update_bus = 1;
+ update_clk = 1;
+ }
+
+ /*
+ * Calculate mdp clock before bandwidth calculation. If traffic shaper
+ * is enabled and clock increased, the bandwidth calculation can
+ * use the new clock for the rotator bw calculation.
+ */
+ if (update_clk)
+ clk_rate = _sde_core_perf_get_core_clk_rate(kms);
+
+ if (update_bus)
+ _sde_core_perf_crtc_update_bus(kms, crtc, clk_rate);
+
+ /*
+ * Update the clock after bandwidth vote to ensure
+ * bandwidth is available before clock rate is increased.
+ */
+ if (update_clk) {
+ SDE_ATRACE_INT(kms->perf.clk_name, clk_rate);
+ SDE_EVT32(kms->dev, stop_req, clk_rate);
+ ret = sde_power_clk_set_rate(&priv->phandle,
+ kms->perf.clk_name, clk_rate);
+ if (ret) {
+ SDE_ERROR("failed to set %s clock rate %u\n",
+ kms->perf.clk_name, clk_rate);
+ goto end;
+ }
+
+ kms->perf.core_clk_rate = clk_rate;
+ SDE_DEBUG("update clk rate = %d HZ\n", clk_rate);
+ }
+
+end:
+ SDE_ATRACE_END(__func__);
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+static ssize_t _sde_core_perf_mode_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct sde_core_perf *perf = file->private_data;
+ struct sde_perf_cfg *cfg = &perf->catalog->perf;
+ int perf_mode = 0;
+ char buf[10];
+
+ if (!perf)
+ return -ENODEV;
+
+ if (count >= sizeof(buf))
+ return -EFAULT;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ buf[count] = 0; /* end of string */
+
+ if (kstrtoint(buf, 0, &perf_mode))
+ return -EFAULT;
+
+ if (perf_mode) {
+ /* run the driver with max clk and BW vote */
+ perf->perf_tune.min_core_clk = perf->max_core_clk_rate;
+ perf->perf_tune.min_bus_vote =
+ (u64) cfg->max_bw_high * 1000;
+ } else {
+ /* reset the perf tune params to 0 */
+ perf->perf_tune.min_core_clk = 0;
+ perf->perf_tune.min_bus_vote = 0;
+ }
+ return count;
+}
+
+static ssize_t _sde_core_perf_mode_read(struct file *file,
+ char __user *buff, size_t count, loff_t *ppos)
+{
+ struct sde_core_perf *perf = file->private_data;
+ int len = 0;
+ char buf[40] = {'\0'};
+
+ if (!perf)
+ return -ENODEV;
+
+ if (*ppos)
+ return 0; /* the end */
+
+ len = snprintf(buf, sizeof(buf), "min_mdp_clk %lu min_bus_vote %llu\n",
+ perf->perf_tune.min_core_clk,
+ perf->perf_tune.min_bus_vote);
+ if (len < 0 || len >= sizeof(buf))
+ return 0;
+
+ if ((count < sizeof(buf)) || copy_to_user(buff, buf, len))
+ return -EFAULT;
+
+ *ppos += len; /* increase offset */
+
+ return len;
+}
+
+static const struct file_operations sde_core_perf_mode_fops = {
+ .open = simple_open,
+ .read = _sde_core_perf_mode_read,
+ .write = _sde_core_perf_mode_write,
+};
+
+static void sde_debugfs_core_perf_destroy(struct sde_core_perf *perf)
+{
+ debugfs_remove_recursive(perf->debugfs_root);
+ perf->debugfs_root = NULL;
+}
+
+static int sde_debugfs_core_perf_init(struct sde_core_perf *perf,
+ struct dentry *parent)
+{
+ struct sde_mdss_cfg *catalog = perf->catalog;
+ struct msm_drm_private *priv;
+ struct sde_kms *sde_kms;
+
+ priv = perf->dev->dev_private;
+ if (!priv || !priv->kms) {
+ SDE_ERROR("invalid KMS reference\n");
+ return -EINVAL;
+ }
+
+ sde_kms = to_sde_kms(priv->kms);
+
+ perf->debugfs_root = debugfs_create_dir("core_perf", parent);
+ if (!perf->debugfs_root) {
+ SDE_ERROR("failed to create core perf debugfs\n");
+ return -EINVAL;
+ }
+
+ debugfs_create_u64("max_core_clk_rate", 0644, perf->debugfs_root,
+ &perf->max_core_clk_rate);
+ debugfs_create_u32("core_clk_rate", 0644, perf->debugfs_root,
+ &perf->core_clk_rate);
+ debugfs_create_u32("enable_bw_release", 0644, perf->debugfs_root,
+ (u32 *)&perf->enable_bw_release);
+ debugfs_create_u32("threshold_low", 0644, perf->debugfs_root,
+ (u32 *)&catalog->perf.max_bw_low);
+ debugfs_create_u32("threshold_high", 0644, perf->debugfs_root,
+ (u32 *)&catalog->perf.max_bw_high);
+ debugfs_create_file("perf_mode", 0644, perf->debugfs_root,
+ (u32 *)perf, &sde_core_perf_mode_fops);
+
+ return 0;
+}
+#else
+static void sde_debugfs_core_perf_destroy(struct sde_core_perf *perf)
+{
+}
+
+static int sde_debugfs_core_perf_init(struct sde_core_perf *perf,
+ struct dentry *parent)
+{
+ return 0;
+}
+#endif
+
+void sde_core_perf_destroy(struct sde_core_perf *perf)
+{
+ if (!perf) {
+ SDE_ERROR("invalid parameters\n");
+ return;
+ }
+
+ sde_debugfs_core_perf_destroy(perf);
+ perf->max_core_clk_rate = 0;
+ perf->core_clk = NULL;
+ mutex_destroy(&perf->perf_lock);
+ perf->clk_name = NULL;
+ perf->phandle = NULL;
+ perf->catalog = NULL;
+ perf->dev = NULL;
+}
+
+int sde_core_perf_init(struct sde_core_perf *perf,
+ struct drm_device *dev,
+ struct sde_mdss_cfg *catalog,
+ struct sde_power_handle *phandle,
+ struct sde_power_client *pclient,
+ char *clk_name,
+ struct dentry *debugfs_parent)
+{
+ if (!perf || !catalog || !phandle || !pclient ||
+ !clk_name || !debugfs_parent) {
+ SDE_ERROR("invalid parameters\n");
+ return -EINVAL;
+ }
+
+ perf->dev = dev;
+ perf->catalog = catalog;
+ perf->phandle = phandle;
+ perf->pclient = pclient;
+ perf->clk_name = clk_name;
+ mutex_init(&perf->perf_lock);
+
+ perf->core_clk = sde_power_clk_get_clk(phandle, clk_name);
+ if (!perf->core_clk) {
+ SDE_ERROR("invalid core clk\n");
+ goto err;
+ }
+
+ perf->max_core_clk_rate = sde_power_clk_get_max_rate(phandle, clk_name);
+ if (!perf->max_core_clk_rate) {
+ SDE_ERROR("invalid max core clk rate\n");
+ goto err;
+ }
+
+ sde_debugfs_core_perf_init(perf, debugfs_parent);
+
+ return 0;
+
+err:
+ sde_core_perf_destroy(perf);
+ return -ENODEV;
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_core_perf.h b/drivers/gpu/drm/msm/sde/sde_core_perf.h
new file mode 100644
index 0000000..e5dd9b6
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_core_perf.h
@@ -0,0 +1,124 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SDE_CORE_PERF_H__
+#define __SDE_CORE_PERF_H__
+
+#include <linux/types.h>
+#include <linux/dcache.h>
+#include <linux/mutex.h>
+#include <drm/drm_crtc.h>
+
+#include "sde_hw_catalog.h"
+#include "sde_power_handle.h"
+
+/**
+ * struct sde_core_perf_params - definition of performance parameters
+ * @max_per_pipe_ib: maximum instantaneous bandwidth request
+ * @bw_ctl: arbitrated bandwidth request
+ * @core_clk_rate: core clock rate request
+ */
+struct sde_core_perf_params {
+ u64 max_per_pipe_ib;
+ u64 bw_ctl;
+ u32 core_clk_rate;
+};
+
+/**
+ * struct sde_core_perf_tune - definition of performance tuning control
+ * @min_core_clk: minimum core clock
+ * @min_bus_vote: minimum bus vote
+ */
+struct sde_core_perf_tune {
+ unsigned long min_core_clk;
+ u64 min_bus_vote;
+};
+
+/**
+ * struct sde_core_perf - definition of core performance context
+ * @dev: Pointer to drm device
+ * @debugfs_root: top level debug folder
+ * @perf_lock: serialization lock for this context
+ * @catalog: Pointer to catalog configuration
+ * @phandle: Pointer to power handler
+ * @pclient: Pointer to power client
+ * @clk_name: core clock name
+ * @core_clk: Pointer to core clock structure
+ * @core_clk_rate: current core clock rate
+ * @max_core_clk_rate: maximum allowable core clock rate
+ * @perf_tune: debug control for performance tuning
+ * @enable_bw_release: debug control for bandwidth release
+ */
+struct sde_core_perf {
+ struct drm_device *dev;
+ struct dentry *debugfs_root;
+ struct mutex perf_lock;
+ struct sde_mdss_cfg *catalog;
+ struct sde_power_handle *phandle;
+ struct sde_power_client *pclient;
+ char *clk_name;
+ struct clk *core_clk;
+ u32 core_clk_rate;
+ u64 max_core_clk_rate;
+ struct sde_core_perf_tune perf_tune;
+ u32 enable_bw_release;
+};
+
+/**
+ * sde_core_perf_crtc_check - validate performance of the given crtc state
+ * @crtc: Pointer to crtc
+ * @state: Pointer to new crtc state
+ * return: zero if success, or error code otherwise
+ */
+int sde_core_perf_crtc_check(struct drm_crtc *crtc,
+ struct drm_crtc_state *state);
+
+/**
+ * sde_core_perf_crtc_update - update performance of the given crtc
+ * @crtc: Pointer to crtc
+ * @params_changed: true if crtc parameters are modified
+ * @stop_req: true if this is a stop request
+ */
+void sde_core_perf_crtc_update(struct drm_crtc *crtc,
+ int params_changed, bool stop_req);
+
+/**
+ * sde_core_perf_crtc_release_bw - release bandwidth of the given crtc
+ * @crtc: Pointer to crtc
+ */
+void sde_core_perf_crtc_release_bw(struct drm_crtc *crtc);
+
+/**
+ * sde_core_perf_destroy - destroy the given core performance context
+ * @perf: Pointer to core performance context
+ */
+void sde_core_perf_destroy(struct sde_core_perf *perf);
+
+/**
+ * sde_core_perf_init - initialize the given core performance context
+ * @perf: Pointer to core performance context
+ * @dev: Pointer to drm device
+ * @catalog: Pointer to catalog
+ * @phandle: Pointer to power handle
+ * @pclient: Pointer to power client
+ * @clk_name: core clock name
+ * @debugfs_parent: Pointer to parent debugfs
+ */
+int sde_core_perf_init(struct sde_core_perf *perf,
+ struct drm_device *dev,
+ struct sde_mdss_cfg *catalog,
+ struct sde_power_handle *phandle,
+ struct sde_power_client *pclient,
+ char *clk_name,
+ struct dentry *debugfs_parent);
+
+#endif /* __SDE_CORE_PERF_H__ */
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 9832ca5..ba68652 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -34,6 +34,8 @@
#include "sde_color_processing.h"
#include "sde_encoder.h"
#include "sde_connector.h"
+#include "sde_power_handle.h"
+#include "sde_core_perf.h"
/* default input fence timeout, in ms */
#define SDE_CRTC_INPUT_FENCE_TIMEOUT 2000
@@ -428,6 +430,12 @@
cstate->is_rt = true;
}
+ if (cstate->num_connectors > 0 && cstate->connectors[0]->encoder)
+ cstate->intf_mode = sde_encoder_get_intf_mode(
+ cstate->connectors[0]->encoder);
+ else
+ cstate->intf_mode = INTF_MODE_NONE;
+
/* prepare main output fence */
sde_fence_prepare(&sde_crtc->output_fence);
}
@@ -486,6 +494,7 @@
static void sde_crtc_frame_event_work(struct kthread_work *work)
{
+ struct msm_drm_private *priv;
struct sde_crtc_frame_event *fevent;
struct drm_crtc *crtc;
struct sde_crtc *sde_crtc;
@@ -511,12 +520,14 @@
SDE_ERROR("invalid kms handle\n");
return;
}
+ priv = sde_kms->dev->dev_private;
SDE_DEBUG("crtc%d event:%u ts:%lld\n", crtc->base.id, fevent->event,
ktime_to_ns(fevent->ts));
if (fevent->event == SDE_ENCODER_FRAME_EVENT_DONE ||
- fevent->event == SDE_ENCODER_FRAME_EVENT_ERROR) {
+ (fevent->event & SDE_ENCODER_FRAME_EVENT_ERROR) ||
+ (fevent->event & SDE_ENCODER_FRAME_EVENT_PANEL_DEAD)) {
if (atomic_read(&sde_crtc->frame_pending) < 1) {
/* this should not happen */
@@ -531,6 +542,9 @@
crtc->base.id,
ktime_to_ns(fevent->ts));
SDE_EVT32(DRMID(crtc), fevent->event, 1);
+ sde_power_data_bus_bandwidth_ctrl(&priv->phandle,
+ sde_kms->core_client, false);
+ sde_core_perf_crtc_release_bw(crtc);
} else {
SDE_EVT32(DRMID(crtc), fevent->event, 2);
}
@@ -541,6 +555,10 @@
SDE_EVT32(DRMID(crtc), fevent->event, 3);
}
+ if (fevent->event & SDE_ENCODER_FRAME_EVENT_PANEL_DEAD)
+ SDE_ERROR("crtc%d ts:%lld received panel dead event\n",
+ crtc->base.id, ktime_to_ns(fevent->ts));
+
spin_lock_irqsave(&sde_crtc->spin_lock, flags);
list_add_tail(&fevent->list, &sde_crtc->frame_event_list);
spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
@@ -551,7 +569,6 @@
struct drm_crtc *crtc = (struct drm_crtc *)data;
struct sde_crtc *sde_crtc;
struct msm_drm_private *priv;
- struct list_head *list, *next;
struct sde_crtc_frame_event *fevent;
unsigned long flags;
int pipe_id;
@@ -569,20 +586,19 @@
SDE_EVT32(DRMID(crtc), event);
spin_lock_irqsave(&sde_crtc->spin_lock, flags);
- list_for_each_safe(list, next, &sde_crtc->frame_event_list) {
- list_del_init(list);
- break;
- }
+ fevent = list_first_entry_or_null(&sde_crtc->frame_event_list,
+ struct sde_crtc_frame_event, list);
+ if (fevent)
+ list_del_init(&fevent->list);
spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
- if (!list) {
+ if (!fevent) {
SDE_ERROR("crtc%d event %d overflow\n",
crtc->base.id, event);
SDE_EVT32(DRMID(crtc), event);
return;
}
- fevent = container_of(list, struct sde_crtc_frame_event, list);
fevent->event = event;
fevent->crtc = crtc;
fevent->ts = ktime_get();
@@ -907,6 +923,9 @@
/* wait for acquire fences before anything else is done */
_sde_crtc_wait_for_fences(crtc);
+ /* update performance setting before crtc kickoff */
+ sde_core_perf_crtc_update(crtc, 1, false);
+
/*
* Final plane updates: Give each plane a chance to complete all
* required writes/flushing before crtc's "flush
@@ -951,6 +970,8 @@
struct drm_encoder *encoder;
struct drm_device *dev;
struct sde_crtc *sde_crtc;
+ struct msm_drm_private *priv;
+ struct sde_kms *sde_kms;
if (!crtc) {
SDE_ERROR("invalid argument\n");
@@ -958,6 +979,8 @@
}
dev = crtc->dev;
sde_crtc = to_sde_crtc(crtc);
+ sde_kms = _sde_crtc_get_kms(crtc);
+ priv = sde_kms->dev->dev_private;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
if (encoder->crtc != crtc)
@@ -980,6 +1003,8 @@
/* acquire bandwidth and other resources */
SDE_DEBUG("crtc%d first commit\n", crtc->base.id);
SDE_EVT32(DRMID(crtc), 1);
+ sde_power_data_bus_bandwidth_ctrl(&priv->phandle,
+ sde_kms->core_client, true);
} else {
SDE_DEBUG("crtc%d commit\n", crtc->base.id);
SDE_EVT32(DRMID(crtc), 2);
@@ -1068,14 +1093,18 @@
static void sde_crtc_disable(struct drm_crtc *crtc)
{
+ struct msm_drm_private *priv;
struct sde_crtc *sde_crtc;
struct drm_encoder *encoder;
+ struct sde_kms *sde_kms;
if (!crtc) {
SDE_ERROR("invalid crtc\n");
return;
}
sde_crtc = to_sde_crtc(crtc);
+ sde_kms = _sde_crtc_get_kms(crtc);
+ priv = sde_kms->dev->dev_private;
SDE_DEBUG("crtc%d\n", crtc->base.id);
@@ -1100,9 +1129,14 @@
SDE_ERROR("crtc%d invalid frame pending\n",
crtc->base.id);
SDE_EVT32(DRMID(crtc));
+ sde_power_data_bus_bandwidth_ctrl(&priv->phandle,
+ sde_kms->core_client, false);
+ sde_core_perf_crtc_release_bw(crtc);
atomic_set(&sde_crtc->frame_pending, 0);
}
+ sde_core_perf_crtc_update(crtc, 0, true);
+
drm_for_each_encoder(encoder, crtc->dev) {
if (encoder->crtc != crtc)
continue;
@@ -1197,11 +1231,10 @@
int cnt = 0, rc = 0, mixer_width, i, z_pos;
- int left_crtc_zpos_cnt[SDE_STAGE_MAX] = {0};
- int right_crtc_zpos_cnt[SDE_STAGE_MAX] = {0};
struct sde_multirect_plane_states multirect_plane[SDE_STAGE_MAX * 2];
int multirect_count = 0;
const struct drm_plane_state *pipe_staged[SSPP_MAX];
+ int left_zpos_cnt = 0, right_zpos_cnt = 0;
if (!crtc) {
SDE_ERROR("invalid crtc\n");
@@ -1308,11 +1341,12 @@
}
}
+ /* assign mixer stages based on sorted zpos property */
+ sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL);
+
if (!sde_is_custom_client()) {
int stage_old = pstates[0].stage;
- /* assign mixer stages based on sorted zpos property */
- sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL);
z_pos = 0;
for (i = 0; i < cnt; i++) {
if (stage_old != pstates[i].stage)
@@ -1322,8 +1356,14 @@
}
}
+ z_pos = -1;
for (i = 0; i < cnt; i++) {
- z_pos = pstates[i].stage;
+ /* reset counts at every new blend stage */
+ if (pstates[i].stage != z_pos) {
+ left_zpos_cnt = 0;
+ right_zpos_cnt = 0;
+ z_pos = pstates[i].stage;
+ }
/* verify z_pos setting before using it */
if (z_pos >= SDE_STAGE_MAX - SDE_STAGE_0) {
@@ -1332,22 +1372,24 @@
rc = -EINVAL;
goto end;
} else if (pstates[i].drm_pstate->crtc_x < mixer_width) {
- if (left_crtc_zpos_cnt[z_pos] == 2) {
+ if (left_zpos_cnt == 2) {
SDE_ERROR("> 2 planes @ stage %d on left\n",
z_pos);
rc = -EINVAL;
goto end;
}
- left_crtc_zpos_cnt[z_pos]++;
+ left_zpos_cnt++;
+
} else {
- if (right_crtc_zpos_cnt[z_pos] == 2) {
+ if (right_zpos_cnt == 2) {
SDE_ERROR("> 2 planes @ stage %d on right\n",
z_pos);
rc = -EINVAL;
goto end;
}
- right_crtc_zpos_cnt[z_pos]++;
+ right_zpos_cnt++;
}
+
pstates[i].sde_pstate->stage = z_pos + SDE_STAGE_0;
SDE_DEBUG("%s: zpos %d", sde_crtc->name, z_pos);
}
@@ -1359,10 +1401,60 @@
multirect_plane[i].r0->plane->base.id,
multirect_plane[i].r1->plane->base.id);
rc = -EINVAL;
- break;
+ goto end;
}
}
+ rc = sde_core_perf_crtc_check(crtc, state);
+ if (rc) {
+ SDE_ERROR("crtc%d failed performance check %d\n",
+ crtc->base.id, rc);
+ goto end;
+ }
+
+ /*
+ * enforce pipe priority restrictions
+ * use pstates sorted by stage to check planes on same stage
+ * we assume that all pipes are in source split so its valid to compare
+ * without taking into account left/right mixer placement
+ */
+ for (i = 1; i < cnt; i++) {
+ struct plane_state *prv_pstate, *cur_pstate;
+ int32_t prv_x, cur_x, prv_id, cur_id;
+
+ prv_pstate = &pstates[i - 1];
+ cur_pstate = &pstates[i];
+ if (prv_pstate->stage != cur_pstate->stage)
+ continue;
+
+ prv_x = prv_pstate->drm_pstate->crtc_x;
+ cur_x = cur_pstate->drm_pstate->crtc_x;
+ prv_id = prv_pstate->sde_pstate->base.plane->base.id;
+ cur_id = cur_pstate->sde_pstate->base.plane->base.id;
+
+ /*
+ * Planes are enumerated in pipe-priority order such that planes
+ * with lower drm_id must be left-most in a shared blend-stage
+ * when using source split.
+ */
+ if (cur_x > prv_x && cur_id < prv_id) {
+ SDE_ERROR(
+ "shared z_pos %d lower id plane%d @ x%d should be left of plane%d @ x %d\n",
+ cur_pstate->stage, cur_id, cur_x,
+ prv_id, prv_x);
+ rc = -EINVAL;
+ goto end;
+ } else if (cur_x < prv_x && cur_id > prv_id) {
+ SDE_ERROR(
+ "shared z_pos %d lower id plane%d @ x%d should be left of plane%d @ x %d\n",
+ cur_pstate->stage, prv_id, prv_x,
+ cur_id, cur_x);
+ rc = -EINVAL;
+ goto end;
+ }
+ }
+
+
end:
return rc;
}
@@ -1423,6 +1515,7 @@
struct sde_crtc *sde_crtc;
struct drm_device *dev;
struct sde_kms_info *info;
+ struct sde_kms *sde_kms;
SDE_DEBUG("\n");
@@ -1433,6 +1526,7 @@
sde_crtc = to_sde_crtc(crtc);
dev = crtc->dev;
+ sde_kms = _sde_crtc_get_kms(crtc);
info = kzalloc(sizeof(struct sde_kms_info), GFP_KERNEL);
if (!info) {
@@ -1452,6 +1546,19 @@
"output_fence_offset", 0x0, 0, 1, 0,
CRTC_PROP_OUTPUT_FENCE_OFFSET);
+ msm_property_install_range(&sde_crtc->property_info,
+ "core_clk", 0x0, 0, U64_MAX,
+ sde_kms->perf.max_core_clk_rate,
+ CRTC_PROP_CORE_CLK);
+ msm_property_install_range(&sde_crtc->property_info,
+ "core_ab", 0x0, 0, U64_MAX,
+ SDE_POWER_HANDLE_DATA_BUS_AB_QUOTA,
+ CRTC_PROP_CORE_AB);
+ msm_property_install_range(&sde_crtc->property_info,
+ "core_ib", 0x0, 0, U64_MAX,
+ SDE_POWER_HANDLE_DATA_BUS_IB_QUOTA,
+ CRTC_PROP_CORE_IB);
+
msm_property_install_blob(&sde_crtc->property_info, "capabilities",
DRM_MODE_PROP_IMMUTABLE, CRTC_PROP_INFO);
@@ -1482,6 +1589,15 @@
}
sde_kms_info_add_keyint(info, "has_src_split", catalog->has_src_split);
+ if (catalog->perf.max_bw_low)
+ sde_kms_info_add_keyint(info, "max_bandwidth_low",
+ catalog->perf.max_bw_low);
+ if (catalog->perf.max_bw_high)
+ sde_kms_info_add_keyint(info, "max_bandwidth_high",
+ catalog->perf.max_bw_high);
+ if (sde_kms->perf.max_core_clk_rate)
+ sde_kms_info_add_keyint(info, "max_mdp_clk",
+ sde_kms->perf.max_core_clk_rate);
msm_property_set_blob(&sde_crtc->property_info, &sde_crtc->blob_info,
info->data, info->len, CRTC_PROP_INFO);
@@ -1598,6 +1714,7 @@
return ret;
}
+#ifdef CONFIG_DEBUG_FS
static int _sde_debugfs_status_show(struct seq_file *s, void *data)
{
struct sde_crtc *sde_crtc;
@@ -1715,6 +1832,7 @@
{
return single_open(file, _sde_debugfs_status_show, inode->i_private);
}
+#endif
static const struct drm_crtc_funcs sde_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
@@ -1737,6 +1855,37 @@
.atomic_flush = sde_crtc_atomic_flush,
};
+#ifdef CONFIG_DEBUG_FS
+#define DEFINE_SDE_DEBUGFS_SEQ_FOPS(__prefix) \
+static int __prefix ## _open(struct inode *inode, struct file *file) \
+{ \
+ return single_open(file, __prefix ## _show, inode->i_private); \
+} \
+static const struct file_operations __prefix ## _fops = { \
+ .owner = THIS_MODULE, \
+ .open = __prefix ## _open, \
+ .release = single_release, \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+}
+
+static int sde_crtc_debugfs_state_show(struct seq_file *s, void *v)
+{
+ struct drm_crtc *crtc = (struct drm_crtc *) s->private;
+ struct sde_crtc_state *cstate = to_sde_crtc_state(crtc->state);
+
+ seq_printf(s, "num_connectors: %d\n", cstate->num_connectors);
+ seq_printf(s, "is_rt: %d\n", cstate->is_rt);
+ seq_printf(s, "intf_mode: %d\n", cstate->intf_mode);
+ seq_printf(s, "bw_ctl: %llu\n", cstate->cur_perf.bw_ctl);
+ seq_printf(s, "core_clk_rate: %u\n", cstate->cur_perf.core_clk_rate);
+ seq_printf(s, "max_per_pipe_ib: %llu\n",
+ cstate->cur_perf.max_per_pipe_ib);
+
+ return 0;
+}
+DEFINE_SDE_DEBUGFS_SEQ_FOPS(sde_crtc_debugfs_state);
+
static void _sde_crtc_init_debugfs(struct sde_crtc *sde_crtc,
struct sde_kms *sde_kms)
{
@@ -1746,6 +1895,7 @@
.llseek = seq_lseek,
.release = single_release,
};
+
if (sde_crtc && sde_kms) {
sde_crtc->debugfs_root = debugfs_create_dir(sde_crtc->name,
sde_debugfs_get_root(sde_kms));
@@ -1754,9 +1904,19 @@
debugfs_create_file("status", 0444,
sde_crtc->debugfs_root,
sde_crtc, &debugfs_status_fops);
+ debugfs_create_file("state", 0644,
+ sde_crtc->debugfs_root,
+ &sde_crtc->base,
+ &sde_crtc_debugfs_state_fops);
}
}
}
+#else
+static void _sde_crtc_init_debugfs(struct sde_crtc *sde_crtc,
+ struct sde_kms *sde_kms)
+{
+}
+#endif
/* initialize crtc */
struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane)
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index d06955d..91fdaed 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -23,6 +23,7 @@
#include "msm_prop.h"
#include "sde_fence.h"
#include "sde_kms.h"
+#include "sde_core_perf.h"
#define SDE_CRTC_NAME_SIZE 12
@@ -136,11 +137,14 @@
* @connectors : Currently associated drm connectors
* @num_connectors: Number of associated drm connectors
* @is_rt : Whether or not the current commit contains RT connectors
+ * @intf_mode : Interface mode of the primary connector
* @property_values: Current crtc property values
* @input_fence_timeout_ns : Cached input fence timeout, in ns
* @property_blobs: Reference pointers for blob properties
* @num_dim_layers: Number of dim layers
* @dim_layer: Dim layer configs
+ * @cur_perf: current performance state
+ * @new_perf: new performance state
*/
struct sde_crtc_state {
struct drm_crtc_state base;
@@ -148,12 +152,16 @@
struct drm_connector *connectors[MAX_CONNECTORS];
int num_connectors;
bool is_rt;
+ enum sde_intf_mode intf_mode;
uint64_t property_values[CRTC_PROP_COUNT];
uint64_t input_fence_timeout_ns;
struct drm_property_blob *property_blobs[CRTC_PROP_COUNT];
uint32_t num_dim_layers;
struct sde_hw_dim_layer dim_layer[SDE_MAX_DIM_LAYERS];
+
+ struct sde_core_perf_params cur_perf;
+ struct sde_core_perf_params new_perf;
};
#define to_sde_crtc_state(x) \
@@ -253,4 +261,46 @@
*/
bool sde_crtc_is_rt(struct drm_crtc *crtc);
+/**
+ * sde_crtc_get_intf_mode - get interface mode of the given crtc
+ * @crtc: Pointert to crtc
+ */
+static inline enum sde_intf_mode sde_crtc_get_intf_mode(struct drm_crtc *crtc)
+{
+ struct sde_crtc_state *cstate =
+ crtc ? to_sde_crtc_state(crtc->state) : NULL;
+
+ return cstate ? cstate->intf_mode : INTF_MODE_NONE;
+}
+
+/**
+ * sde_core_perf_crtc_is_wb - check if writeback is primary output of this crtc
+ * @crtc: Pointer to crtc
+ */
+static inline bool sde_crtc_is_wb(struct drm_crtc *crtc)
+{
+ struct sde_crtc_state *cstate =
+ crtc ? to_sde_crtc_state(crtc->state) : NULL;
+
+ return cstate ? (cstate->intf_mode == INTF_MODE_WB_LINE) : false;
+}
+
+/**
+ * sde_crtc_is_nrt - check if primary output of this crtc is non-realtime client
+ * @crtc: Pointer to crtc
+ */
+static inline bool sde_crtc_is_nrt(struct drm_crtc *crtc)
+{
+ return sde_crtc_is_wb(crtc);
+}
+
+/**
+ * sde_crtc_is_enabled - check if sde crtc is enabled or not
+ * @crtc: Pointer to crtc
+ */
+static inline bool sde_crtc_is_enabled(struct drm_crtc *crtc)
+{
+ return crtc ? crtc->enabled : false;
+}
+
#endif /* _SDE_CRTC_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index ebae360..282fd88 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -115,93 +115,6 @@
#define to_sde_encoder_virt(x) container_of(x, struct sde_encoder_virt, base)
-#ifdef CONFIG_QCOM_BUS_SCALING
-#include <linux/msm-bus.h>
-#include <linux/msm-bus-board.h>
-#define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val) \
- { \
- .src = MSM_BUS_MASTER_MDP_PORT0, \
- .dst = MSM_BUS_SLAVE_EBI_CH0, \
- .ab = (ab_val), \
- .ib = (ib_val), \
- }
-
-static struct msm_bus_vectors mdp_bus_vectors[] = {
- MDP_BUS_VECTOR_ENTRY(0, 0),
- MDP_BUS_VECTOR_ENTRY(8000000000, 8000000000),
-};
-
-static struct msm_bus_paths mdp_bus_usecases[] = { {
- .num_paths = 1,
- .vectors =
- &mdp_bus_vectors[0],
- }, {
- .num_paths = 1,
- .vectors =
- &mdp_bus_vectors[1],
- }
-};
-
-static struct msm_bus_scale_pdata mdp_bus_scale_table = {
- .usecase = mdp_bus_usecases,
- .num_usecases = ARRAY_SIZE(mdp_bus_usecases),
- .name = "mdss_mdp",
-};
-
-static void bs_init(struct sde_encoder_virt *sde_enc)
-{
- if (!sde_enc) {
- SDE_ERROR("invalid encoder\n");
- return;
- }
-
- sde_enc->bus_scaling_client =
- msm_bus_scale_register_client(&mdp_bus_scale_table);
- SDE_DEBUG_ENC(sde_enc, "bus scale client %08x\n",
- sde_enc->bus_scaling_client);
-}
-
-static void bs_fini(struct sde_encoder_virt *sde_enc)
-{
- if (!sde_enc) {
- SDE_ERROR("invalid encoder\n");
- return;
- }
-
- if (sde_enc->bus_scaling_client) {
- msm_bus_scale_unregister_client(sde_enc->bus_scaling_client);
- sde_enc->bus_scaling_client = 0;
- }
-}
-
-static void bs_set(struct sde_encoder_virt *sde_enc, int idx)
-{
- if (!sde_enc) {
- SDE_ERROR("invalid encoder\n");
- return;
- }
-
- if (sde_enc->bus_scaling_client) {
- SDE_DEBUG_ENC(sde_enc, "set bus scaling to %d\n", idx);
- idx = 1;
- msm_bus_scale_client_update_request(sde_enc->bus_scaling_client,
- idx);
- }
-}
-#else
-static void bs_init(struct sde_encoder_virt *sde_enc)
-{
-}
-
-static void bs_fini(struct sde_encoder_virt *sde_enc)
-{
-}
-
-static void bs_set(struct sde_encoder_virt *sde_enc, int idx)
-{
-}
-#endif
-
void sde_encoder_get_hw_resources(struct drm_encoder *drm_enc,
struct sde_encoder_hw_resources *hw_res,
struct drm_connector_state *conn_state)
@@ -261,7 +174,6 @@
mutex_unlock(&sde_enc->enc_lock);
drm_encoder_cleanup(drm_enc);
- bs_fini(sde_enc);
debugfs_remove_recursive(sde_enc->debugfs_root);
mutex_destroy(&sde_enc->enc_lock);
@@ -477,8 +389,6 @@
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true);
- bs_set(sde_enc, 1);
-
sde_enc->cur_master = NULL;
for (i = 0; i < sde_enc->num_phys_encs; i++) {
struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
@@ -550,7 +460,6 @@
sde_enc->cur_master = NULL;
SDE_DEBUG_ENC(sde_enc, "cleared master\n");
- bs_set(sde_enc, 0);
sde_rm_release(&sde_kms->rm, drm_enc);
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
@@ -794,6 +703,51 @@
return rc;
}
+void sde_encoder_helper_hw_reset(struct sde_encoder_phys *phys_enc)
+{
+ struct sde_encoder_virt *sde_enc;
+ struct sde_connector *sde_con;
+ void *sde_con_disp;
+ struct sde_hw_ctl *ctl;
+ int rc;
+
+ if (!phys_enc) {
+ SDE_ERROR("invalid encoder\n");
+ return;
+ }
+ sde_enc = to_sde_encoder_virt(phys_enc->parent);
+ ctl = phys_enc->hw_ctl;
+
+ if (!ctl || !ctl->ops.reset)
+ return;
+
+ SDE_DEBUG_ENC(sde_enc, "ctl %d reset\n", ctl->idx);
+ SDE_EVT32(DRMID(phys_enc->parent), ctl->idx);
+
+ if (phys_enc->ops.is_master && phys_enc->ops.is_master(phys_enc) &&
+ phys_enc->connector) {
+ sde_con = to_sde_connector(phys_enc->connector);
+ sde_con_disp = sde_connector_get_display(phys_enc->connector);
+
+ if (sde_con->ops.soft_reset) {
+ rc = sde_con->ops.soft_reset(sde_con_disp);
+ if (rc) {
+ SDE_ERROR_ENC(sde_enc,
+ "connector soft reset failure\n");
+ SDE_DBG_DUMP("panic");
+ }
+ }
+ }
+
+ rc = ctl->ops.reset(ctl);
+ if (rc) {
+ SDE_ERROR_ENC(sde_enc, "ctl %d reset failure\n", ctl->idx);
+ SDE_DBG_DUMP("panic");
+ }
+
+ phys_enc->enable_state = SDE_ENC_ENABLED;
+}
+
/**
* _sde_encoder_kickoff_phys - handle physical encoder kickoff
* Iterate through the physical encoders and perform consolidated flush
@@ -823,6 +777,7 @@
/* don't perform flush/start operations for slave encoders */
for (i = 0; i < sde_enc->num_phys_encs; i++) {
struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
+ enum sde_rm_topology_name topology = SDE_RM_TOPOLOGY_UNKNOWN;
if (!phys || phys->enable_state == SDE_ENC_DISABLED)
continue;
@@ -831,7 +786,14 @@
if (!ctl)
continue;
- set_bit(i, sde_enc->frame_busy_mask);
+ if (phys->connector)
+ topology = sde_connector_get_topology_name(
+ phys->connector);
+
+ /* don't wait on ppsplit slaves, they dont register irqs */
+ if (!(topology == SDE_RM_TOPOLOGY_PPSPLIT &&
+ phys->split_role == ENC_ROLE_SLAVE))
+ set_bit(i, sde_enc->frame_busy_mask);
if (!phys->ops.needs_single_flush ||
!phys->ops.needs_single_flush(phys))
@@ -857,6 +819,7 @@
{
struct sde_encoder_virt *sde_enc;
struct sde_encoder_phys *phys;
+ bool needs_hw_reset = false;
unsigned int i;
if (!drm_enc) {
@@ -871,8 +834,21 @@
/* prepare for next kickoff, may include waiting on previous kickoff */
for (i = 0; i < sde_enc->num_phys_encs; i++) {
phys = sde_enc->phys_encs[i];
- if (phys && phys->ops.prepare_for_kickoff)
- phys->ops.prepare_for_kickoff(phys);
+ if (phys) {
+ if (phys->ops.prepare_for_kickoff)
+ phys->ops.prepare_for_kickoff(phys);
+ if (phys->enable_state == SDE_ENC_ERR_NEEDS_HW_RESET)
+ needs_hw_reset = true;
+ }
+ }
+
+ /* if any phys needs reset, reset all phys, in-order */
+ if (needs_hw_reset) {
+ for (i = 0; i < sde_enc->num_phys_encs; i++) {
+ phys = sde_enc->phys_encs[i];
+ if (phys && phys->ops.hw_reset)
+ phys->ops.hw_reset(phys);
+ }
}
}
@@ -1307,18 +1283,21 @@
priv = drm_enc->dev->dev_private;
if (!sde_enc->frame_busy_mask[0] || !sde_enc->crtc_frame_event_cb) {
- SDE_DEBUG("enc%d invalid timeout\n", drm_enc->base.id);
- SDE_EVT32(DRMID(drm_enc),
- sde_enc->frame_busy_mask[0], 0);
+ SDE_DEBUG_ENC(sde_enc, "invalid timeout\n");
+ SDE_EVT32(DRMID(drm_enc), sde_enc->frame_busy_mask[0], 0);
return;
} else if (!atomic_xchg(&sde_enc->frame_done_timeout, 0)) {
- SDE_ERROR("enc%d invalid timeout\n", drm_enc->base.id);
+ SDE_ERROR_ENC(sde_enc, "invalid timeout\n");
SDE_EVT32(DRMID(drm_enc), 0, 1);
return;
}
- SDE_EVT32(DRMID(drm_enc), 0, 2);
+ SDE_EVT32(DRMID(drm_enc), 2, sde_enc->crtc_frame_event);
+ SDE_ERROR_ENC(sde_enc, "frame done timeout, frame_event %d\n",
+ sde_enc->crtc_frame_event);
+
sde_enc->crtc_frame_event_cb(sde_enc->crtc_frame_event_cb_data,
+ sde_enc->crtc_frame_event |
SDE_ENCODER_FRAME_EVENT_ERROR);
}
@@ -1350,7 +1329,6 @@
drm_enc = &sde_enc->base;
drm_encoder_init(dev, drm_enc, &sde_encoder_funcs, drm_enc_mode, NULL);
drm_encoder_helper_add(drm_enc, &sde_encoder_helper_funcs);
- bs_init(sde_enc);
atomic_set(&sde_enc->frame_done_timeout, 0);
setup_timer(&sde_enc->frame_done_timer, sde_encoder_frame_done_timeout,
@@ -1399,3 +1377,26 @@
return ret;
}
+enum sde_intf_mode sde_encoder_get_intf_mode(struct drm_encoder *encoder)
+{
+ struct sde_encoder_virt *sde_enc = NULL;
+ int i;
+
+ if (!encoder) {
+ SDE_ERROR("invalid encoder\n");
+ return INTF_MODE_NONE;
+ }
+ sde_enc = to_sde_encoder_virt(encoder);
+
+ if (sde_enc->cur_master)
+ return sde_enc->cur_master->intf_mode;
+
+ for (i = 0; i < sde_enc->num_phys_encs; i++) {
+ struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
+
+ if (phys)
+ return phys->intf_mode;
+ }
+
+ return INTF_MODE_NONE;
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index ab8ff5a..61435c9 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -24,8 +24,9 @@
#include "msm_prop.h"
#include "sde_hw_mdss.h"
-#define SDE_ENCODER_FRAME_EVENT_DONE BIT(0)
-#define SDE_ENCODER_FRAME_EVENT_ERROR BIT(1)
+#define SDE_ENCODER_FRAME_EVENT_DONE BIT(0)
+#define SDE_ENCODER_FRAME_EVENT_ERROR BIT(1)
+#define SDE_ENCODER_FRAME_EVENT_PANEL_DEAD BIT(2)
/**
* Encoder functions and data types
@@ -85,7 +86,7 @@
* (i.e. ctl flush and start) immediately.
* @encoder: encoder pointer
*/
-void sde_encoder_kickoff(struct drm_encoder *drm_enc);
+void sde_encoder_kickoff(struct drm_encoder *encoder);
/**
* sde_encoder_wait_nxt_committed - Wait for hardware to have flushed the
@@ -97,6 +98,12 @@
*/
int sde_encoder_wait_for_commit_done(struct drm_encoder *drm_encoder);
+/*
+ * sde_encoder_get_intf_mode - get interface mode of the given encoder
+ * @encoder: Pointer to drm encoder object
+ */
+enum sde_intf_mode sde_encoder_get_intf_mode(struct drm_encoder *encoder);
+
/**
* sde_encoder_init - initialize virtual encoder object
* @dev: Pointer to drm device structure
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index 2ac0a64..0ee0f13 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -47,6 +47,22 @@
ENC_ROLE_SLAVE
};
+/**
+ * enum sde_enc_enable_state - current enabled state of the physical encoder
+ * @SDE_ENC_DISABLED: Encoder is disabled
+ * @SDE_ENC_ENABLING: Encoder transitioning to enabled
+ * Events bounding transition are encoder type specific
+ * @SDE_ENC_ENABLED: Encoder is enabled
+ * @SDE_ENC_ERR_NEEDS_HW_RESET: Encoder is enabled, but requires a hw_reset
+ * to recover from a previous error
+ */
+enum sde_enc_enable_state {
+ SDE_ENC_DISABLED,
+ SDE_ENC_ENABLING,
+ SDE_ENC_ENABLED,
+ SDE_ENC_ERR_NEEDS_HW_RESET
+};
+
struct sde_encoder_phys;
/**
@@ -94,6 +110,8 @@
* @needs_single_flush: Whether encoder slaves need to be flushed
* @setup_misr: Sets up MISR, enable and disables based on sysfs
* @collect_misr: Collects MISR data on frame update
+ * @hw_reset: Issue HW recovery such as CTL reset and clear
+ * SDE_ENC_ERR_NEEDS_HW_RESET state
*/
struct sde_encoder_phys_ops {
@@ -124,19 +142,7 @@
struct sde_misr_params *misr_map);
void (*collect_misr)(struct sde_encoder_phys *phys_enc,
struct sde_misr_params *misr_map);
-};
-
-/**
- * enum sde_enc_enable_state - current enabled state of the physical encoder
- * @SDE_ENC_DISABLED: Encoder is disabled
- * @SDE_ENC_ENABLING: Encoder transitioning to enabled
- * Events bounding transition are encoder type specific
- * @SDE_ENC_ENABLED: Encoder is enabled
- */
-enum sde_enc_enable_state {
- SDE_ENC_DISABLED,
- SDE_ENC_ENABLING,
- SDE_ENC_ENABLED
+ void (*hw_reset)(struct sde_encoder_phys *phys_enc);
};
/**
@@ -239,9 +245,10 @@
* @pp_rd_ptr_irq_idx: IRQ signifying panel's frame read pointer
* For CMD encoders, VBLANK is driven by the PP RD Done IRQ
* @pp_tx_done_irq_idx: IRQ signifying frame transmission to panel complete
- * @irq_cb: interrupt callback
- * @serialize_wait4pp: serialize wait4pp feature waits for pp_done interrupt
- * after ctl_start instead of before next frame kickoff
+ * @irq_cb: interrupt callback
+ * @serialize_wait4pp: serialize wait4pp feature waits for pp_done interrupt
+ * after ctl_start instead of before next frame kickoff
+ * @pp_timeout_report_cnt: number of pingpong done irq timeout errors
*/
struct sde_encoder_phys_cmd {
struct sde_encoder_phys base;
@@ -250,6 +257,7 @@
int irq_idx[INTR_IDX_MAX];
struct sde_irq_callback irq_cb[INTR_IDX_MAX];
bool serialize_wait4pp;
+ int pp_timeout_report_cnt;
};
/**
@@ -381,6 +389,14 @@
atomic_t *cnt,
s64 timeout_ms);
+/**
+ * sde_encoder_helper_hw_reset - issue ctl hw reset
+ * This helper function may be optionally specified by physical
+ * encoders if they require ctl hw reset. If state is currently
+ * SDE_ENC_ERR_NEEDS_HW_RESET, it is set back to SDE_ENC_ENABLED.
+ * @phys_enc: Pointer to physical encoder structure
+ */
+void sde_encoder_helper_hw_reset(struct sde_encoder_phys *phys_enc);
static inline enum sde_3d_blend_mode sde_encoder_helper_get_3d_blend_mode(
struct sde_encoder_phys *phys_enc)
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
index 64c70a2..c3a653b 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -31,6 +31,8 @@
#define to_sde_encoder_phys_cmd(x) \
container_of(x, struct sde_encoder_phys_cmd, base)
+#define PP_TIMEOUT_MAX_TRIALS 10
+
/*
* Tearcheck sync start and continue thresholds are empirically found
* based on common panels In the future, may want to allow panels to override
@@ -149,6 +151,53 @@
return false;
}
+static int _sde_encoder_phys_cmd_handle_ppdone_timeout(
+ struct sde_encoder_phys *phys_enc)
+{
+ struct sde_encoder_phys_cmd *cmd_enc =
+ to_sde_encoder_phys_cmd(phys_enc);
+ u32 frame_event = SDE_ENCODER_FRAME_EVENT_ERROR;
+ bool do_log = false;
+
+ cmd_enc->pp_timeout_report_cnt++;
+ if (cmd_enc->pp_timeout_report_cnt == PP_TIMEOUT_MAX_TRIALS) {
+ frame_event |= SDE_ENCODER_FRAME_EVENT_PANEL_DEAD;
+ do_log = true;
+ } else if (cmd_enc->pp_timeout_report_cnt == 1) {
+ do_log = true;
+ }
+
+ /* to avoid flooding, only log first time, and "dead" time */
+ if (do_log) {
+ SDE_ERROR_CMDENC(cmd_enc,
+ "pp:%d kickoff timed out ctl %d cnt %d koff_cnt %d\n",
+ phys_enc->hw_pp->idx - PINGPONG_0,
+ phys_enc->hw_ctl->idx - CTL_0,
+ cmd_enc->pp_timeout_report_cnt,
+ atomic_read(&phys_enc->pending_kickoff_cnt));
+
+ SDE_EVT32(DRMID(phys_enc->parent),
+ phys_enc->hw_pp->idx - PINGPONG_0,
+ 0xbad, cmd_enc->pp_timeout_report_cnt,
+ atomic_read(&phys_enc->pending_kickoff_cnt));
+
+ SDE_DBG_DUMP("sde", "dsi0_ctrl", "dsi0_phy", "dsi1_ctrl",
+ "dsi1_phy", "vbif", "vbif_nrt", "dbg_bus",
+ "vbif_dbg_bus", "panic");
+ }
+
+ atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
+
+ /* request a ctl reset before the next kickoff */
+ phys_enc->enable_state = SDE_ENC_ERR_NEEDS_HW_RESET;
+
+ if (phys_enc->parent_ops.handle_frame_done)
+ phys_enc->parent_ops.handle_frame_done(
+ phys_enc->parent, phys_enc, frame_event);
+
+ return -ETIMEDOUT;
+}
+
static int _sde_encoder_phys_cmd_wait_for_idle(
struct sde_encoder_phys *phys_enc)
{
@@ -180,32 +229,32 @@
&phys_enc->pending_kickoff_cnt,
KICKOFF_TIMEOUT_MS);
if (ret <= 0) {
+ /* read and clear interrupt */
irq_status = sde_core_irq_read(phys_enc->sde_kms,
INTR_IDX_PINGPONG, true);
if (irq_status) {
+ unsigned long flags;
SDE_EVT32(DRMID(phys_enc->parent),
phys_enc->hw_pp->idx - PINGPONG_0);
SDE_DEBUG_CMDENC(cmd_enc,
"pp:%d done but irq not triggered\n",
phys_enc->hw_pp->idx - PINGPONG_0);
+ local_irq_save(flags);
sde_encoder_phys_cmd_pp_tx_done_irq(cmd_enc,
INTR_IDX_PINGPONG);
+ local_irq_restore(flags);
ret = 0;
} else {
- SDE_EVT32(DRMID(phys_enc->parent),
- phys_enc->hw_pp->idx - PINGPONG_0);
- SDE_ERROR_CMDENC(cmd_enc, "pp:%d kickoff timed out\n",
- phys_enc->hw_pp->idx - PINGPONG_0);
- if (phys_enc->parent_ops.handle_frame_done)
- phys_enc->parent_ops.handle_frame_done(
- phys_enc->parent, phys_enc,
- SDE_ENCODER_FRAME_EVENT_ERROR);
- ret = -ETIMEDOUT;
+ ret = _sde_encoder_phys_cmd_handle_ppdone_timeout(
+ phys_enc);
}
} else {
ret = 0;
}
+ if (!ret)
+ cmd_enc->pp_timeout_report_cnt = 0;
+
return ret;
}
@@ -666,6 +715,7 @@
ops->prepare_for_kickoff = sde_encoder_phys_cmd_prepare_for_kickoff;
ops->trigger_start = sde_encoder_helper_trigger_start;
ops->needs_single_flush = sde_encoder_phys_cmd_needs_single_flush;
+ ops->hw_reset = sde_encoder_helper_hw_reset;
}
struct sde_encoder_phys *sde_encoder_phys_cmd_init(
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index b5f170c..e00b4d2 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -478,16 +478,19 @@
static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc)
{
+ struct msm_drm_private *priv;
struct sde_encoder_phys_vid *vid_enc;
struct sde_hw_intf *intf;
struct sde_hw_ctl *ctl;
u32 flush_mask = 0;
int ret;
- if (!phys_enc) {
- SDE_ERROR("invalid encoder\n");
+ if (!phys_enc || !phys_enc->parent || !phys_enc->parent->dev ||
+ !phys_enc->parent->dev->dev_private) {
+ SDE_ERROR("invalid encoder/device\n");
return;
}
+ priv = phys_enc->parent->dev->dev_private;
vid_enc = to_sde_encoder_phys_vid(phys_enc);
intf = vid_enc->hw_intf;
@@ -503,6 +506,9 @@
if (WARN_ON(!vid_enc->hw_intf->ops.enable_timing))
return;
+ sde_power_data_bus_bandwidth_ctrl(&priv->phandle,
+ phys_enc->sde_kms->core_client, true);
+
sde_encoder_helper_split_config(phys_enc, vid_enc->hw_intf->idx);
sde_encoder_phys_vid_setup_timing_engine(phys_enc);
@@ -647,16 +653,48 @@
return ret;
}
-static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc)
+static void sde_encoder_phys_vid_prepare_for_kickoff(
+ struct sde_encoder_phys *phys_enc)
{
struct sde_encoder_phys_vid *vid_enc;
- unsigned long lock_flags;
- int ret;
+ struct sde_hw_ctl *ctl;
+ int rc;
if (!phys_enc) {
SDE_ERROR("invalid encoder\n");
return;
}
+ vid_enc = to_sde_encoder_phys_vid(phys_enc);
+
+ ctl = phys_enc->hw_ctl;
+ if (!ctl || !ctl->ops.wait_reset_status)
+ return;
+
+ /*
+ * hw supports hardware initiated ctl reset, so before we kickoff a new
+ * frame, need to check and wait for hw initiated ctl reset completion
+ */
+ rc = ctl->ops.wait_reset_status(ctl);
+ if (rc) {
+ SDE_ERROR_VIDENC(vid_enc, "ctl %d reset failure: %d\n",
+ ctl->idx, rc);
+ SDE_DBG_DUMP("panic");
+ }
+}
+
+static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc)
+{
+ struct msm_drm_private *priv;
+ struct sde_encoder_phys_vid *vid_enc;
+ unsigned long lock_flags;
+ int ret;
+
+ if (!phys_enc || !phys_enc->parent || !phys_enc->parent->dev ||
+ !phys_enc->parent->dev->dev_private) {
+ SDE_ERROR("invalid encoder/device\n");
+ return;
+ }
+ priv = phys_enc->parent->dev->dev_private;
vid_enc = to_sde_encoder_phys_vid(phys_enc);
if (!vid_enc->hw_intf || !phys_enc->hw_ctl) {
@@ -702,6 +740,9 @@
sde_encoder_phys_vid_control_vblank_irq(phys_enc, false);
}
+ sde_power_data_bus_bandwidth_ctrl(&priv->phandle,
+ phys_enc->sde_kms->core_client, false);
+
if (atomic_read(&phys_enc->vblank_refcount))
SDE_ERROR_VIDENC(vid_enc, "invalid vblank refcount %d\n",
atomic_read(&phys_enc->vblank_refcount));
@@ -768,10 +809,12 @@
ops->get_hw_resources = sde_encoder_phys_vid_get_hw_resources;
ops->control_vblank_irq = sde_encoder_phys_vid_control_vblank_irq;
ops->wait_for_commit_done = sde_encoder_phys_vid_wait_for_commit_done;
+ ops->prepare_for_kickoff = sde_encoder_phys_vid_prepare_for_kickoff;
ops->handle_post_kickoff = sde_encoder_phys_vid_handle_post_kickoff;
ops->needs_single_flush = sde_encoder_phys_vid_needs_single_flush;
ops->setup_misr = sde_encoder_phys_vid_setup_misr;
ops->collect_misr = sde_encoder_phys_vid_collect_misr;
+ ops->hw_reset = sde_encoder_helper_hw_reset;
}
struct sde_encoder_phys *sde_encoder_phys_vid_init(
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
index 3aa4e16..97ec9a9 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
@@ -989,6 +989,7 @@
ops->prepare_for_kickoff = sde_encoder_phys_wb_prepare_for_kickoff;
ops->handle_post_kickoff = sde_encoder_phys_wb_handle_post_kickoff;
ops->trigger_start = sde_encoder_helper_trigger_start;
+ ops->hw_reset = sde_encoder_helper_hw_reset;
}
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c
index 41180f5..33be5a0 100644
--- a/drivers/gpu/drm/msm/sde/sde_formats.c
+++ b/drivers/gpu/drm/msm/sde/sde_formats.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -11,12 +11,15 @@
*/
#include <uapi/drm/drm_fourcc.h>
+#include <uapi/media/msm_media_info.h>
#include "sde_kms.h"
#include "sde_formats.h"
#define SDE_UBWC_META_MACRO_W_H 16
#define SDE_UBWC_META_BLOCK_SIZE 256
+#define SDE_UBWC_PLANE_SIZE_ALIGNMENT 4096
+
#define SDE_MAX_IMG_WIDTH 0x3FFF
#define SDE_MAX_IMG_HEIGHT 0x3FFF
@@ -42,7 +45,7 @@
.unpack_count = uc, \
.bpp = bp, \
.fetch_mode = fm, \
- .flag = flg, \
+ .flag = {(flg)}, \
.num_planes = np \
}
@@ -60,7 +63,7 @@
.unpack_count = count, \
.bpp = bp, \
.fetch_mode = fm, \
- .flag = flg, \
+ .flag = {(flg)}, \
.num_planes = np \
}
@@ -77,7 +80,24 @@
.unpack_count = 2, \
.bpp = 2, \
.fetch_mode = fm, \
- .flag = flg, \
+ .flag = {(flg)}, \
+ .num_planes = np \
+}
+
+#define PSEUDO_YUV_FMT_LOOSE(fmt, a, r, g, b, e0, e1, chroma, flg, fm, np)\
+{ \
+ .base.pixel_format = DRM_FORMAT_ ## fmt, \
+ .fetch_planes = SDE_PLANE_PSEUDO_PLANAR, \
+ .alpha_enable = false, \
+ .element = { (e0), (e1), 0, 0 }, \
+ .bits = { g, b, r, a }, \
+ .chroma_sample = chroma, \
+ .unpack_align_msb = 1, \
+ .unpack_tight = 0, \
+ .unpack_count = 2, \
+ .bpp = 2, \
+ .fetch_mode = fm, \
+ .flag = {(flg)}, \
.num_planes = np \
}
@@ -95,10 +115,20 @@
.unpack_count = 1, \
.bpp = bp, \
.fetch_mode = fm, \
- .flag = flg, \
+ .flag = {(flg)}, \
.num_planes = np \
}
+/*
+ * struct sde_media_color_map - maps drm format to media format
+ * @format: DRM base pixel format
+ * @color: Media API color related to DRM format
+ */
+struct sde_media_color_map {
+ uint32_t format;
+ uint32_t color;
+};
+
static const struct sde_format sde_format_map[] = {
INTERLEAVED_RGB_FMT(ARGB8888,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
@@ -366,13 +396,13 @@
PLANAR_YUV_FMT(YUV420,
0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C0_G_Y, C1_B_Cb, C2_R_Cr,
+ C2_R_Cr, C1_B_Cb, C0_G_Y,
false, SDE_CHROMA_420, 1, SDE_FORMAT_FLAG_YUV,
SDE_FETCH_LINEAR, 3),
PLANAR_YUV_FMT(YVU420,
0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C0_G_Y, C2_R_Cr, C1_B_Cb,
+ C1_B_Cb, C2_R_Cr, C0_G_Y,
false, SDE_CHROMA_420, 1, SDE_FORMAT_FLAG_YUV,
SDE_FETCH_LINEAR, 3),
};
@@ -421,6 +451,30 @@
SDE_FETCH_UBWC, 4),
};
+static const struct sde_format sde_format_map_p010[] = {
+ PSEUDO_YUV_FMT_LOOSE(NV12,
+ 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C1_B_Cb, C2_R_Cr,
+ SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX),
+ SDE_FETCH_LINEAR, 2),
+};
+
+static const struct sde_format sde_format_map_p010_ubwc[] = {
+ PSEUDO_YUV_FMT_LOOSE(NV12,
+ 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C1_B_Cb, C2_R_Cr,
+ SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX),
+ SDE_FETCH_UBWC, 4),
+};
+
+static const struct sde_format sde_format_map_tp10_ubwc[] = {
+ PSEUDO_YUV_FMT(NV12,
+ 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C1_B_Cb, C2_R_Cr,
+ SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX),
+ SDE_FETCH_UBWC, 4),
+};
+
/* _sde_get_v_h_subsample_rate - Get subsample rates for all formats we support
* Note: Not using the drm_format_*_subsampling since we have formats
*/
@@ -452,6 +506,37 @@
}
}
+static int _sde_format_get_media_color_ubwc(const struct sde_format *fmt)
+{
+ static const struct sde_media_color_map sde_media_ubwc_map[] = {
+ {DRM_FORMAT_RGBA8888, COLOR_FMT_RGBA8888_UBWC},
+ {DRM_FORMAT_RGBX8888, COLOR_FMT_RGBA8888_UBWC},
+ {DRM_FORMAT_RGBA1010102, COLOR_FMT_RGBA1010102_UBWC},
+ {DRM_FORMAT_RGBX1010102, COLOR_FMT_RGBA1010102_UBWC},
+ {DRM_FORMAT_RGB565, COLOR_FMT_RGB565_UBWC},
+ };
+ int color_fmt = -1;
+ int i;
+
+ if (fmt->base.pixel_format == DRM_FORMAT_NV12) {
+ if (SDE_FORMAT_IS_DX(fmt)) {
+ if (fmt->unpack_tight)
+ color_fmt = COLOR_FMT_NV12_BPP10_UBWC;
+ else
+ color_fmt = COLOR_FMT_P010_UBWC;
+ } else
+ color_fmt = COLOR_FMT_NV12_UBWC;
+ return color_fmt;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sde_media_ubwc_map); ++i)
+ if (fmt->base.pixel_format == sde_media_ubwc_map[i].format) {
+ color_fmt = sde_media_ubwc_map[i].color;
+ break;
+ }
+ return color_fmt;
+}
+
static int _sde_format_get_plane_sizes_ubwc(
const struct sde_format *fmt,
const uint32_t width,
@@ -459,6 +544,7 @@
struct sde_hw_fmt_layout *layout)
{
int i;
+ int color;
memset(layout, 0, sizeof(struct sde_hw_fmt_layout));
layout->format = fmt;
@@ -466,85 +552,55 @@
layout->height = height;
layout->num_planes = fmt->num_planes;
- if (fmt->base.pixel_format == DRM_FORMAT_NV12) {
- uint32_t y_stride_alignment, uv_stride_alignment;
- uint32_t y_height_alignment, uv_height_alignment;
- uint32_t y_tile_width = 32;
- uint32_t y_tile_height = 8;
- uint32_t uv_tile_width = y_tile_width / 2;
- uint32_t uv_tile_height = y_tile_height;
- uint32_t y_bpp_numer = 1, y_bpp_denom = 1;
- uint32_t uv_bpp_numer = 1, uv_bpp_denom = 1;
-
- y_stride_alignment = 128;
- uv_stride_alignment = 64;
- y_height_alignment = 32;
- uv_height_alignment = 32;
- y_bpp_numer = 1;
- uv_bpp_numer = 2;
- y_bpp_denom = 1;
- uv_bpp_denom = 1;
-
- layout->num_planes = 4;
- /* Y bitstream stride and plane size */
- layout->plane_pitch[0] = ALIGN(width, y_stride_alignment);
- layout->plane_pitch[0] = (layout->plane_pitch[0] * y_bpp_numer)
- / y_bpp_denom;
- layout->plane_size[0] = ALIGN(layout->plane_pitch[0] *
- ALIGN(height, y_height_alignment), 4096);
-
- /* CbCr bitstream stride and plane size */
- layout->plane_pitch[1] = ALIGN(width / 2, uv_stride_alignment);
- layout->plane_pitch[1] = (layout->plane_pitch[1] * uv_bpp_numer)
- / uv_bpp_denom;
- layout->plane_size[1] = ALIGN(layout->plane_pitch[1] *
- ALIGN(height / 2, uv_height_alignment), 4096);
-
- /* Y meta data stride and plane size */
- layout->plane_pitch[2] = ALIGN(
- DIV_ROUND_UP(width, y_tile_width), 64);
- layout->plane_size[2] = ALIGN(layout->plane_pitch[2] *
- ALIGN(DIV_ROUND_UP(height, y_tile_height), 16), 4096);
-
- /* CbCr meta data stride and plane size */
- layout->plane_pitch[3] = ALIGN(
- DIV_ROUND_UP(width / 2, uv_tile_width), 64);
- layout->plane_size[3] = ALIGN(layout->plane_pitch[3] *
- ALIGN(DIV_ROUND_UP(height / 2, uv_tile_height), 16),
- 4096);
-
- } else if (fmt->base.pixel_format == DRM_FORMAT_RGBA8888 ||
- fmt->base.pixel_format == DRM_FORMAT_RGBX8888 ||
- fmt->base.pixel_format == DRM_FORMAT_RGBA1010102 ||
- fmt->base.pixel_format == DRM_FORMAT_RGBX1010102 ||
- fmt->base.pixel_format == DRM_FORMAT_RGB565) {
- uint32_t stride_alignment, aligned_bitstream_width;
-
- if (fmt->base.pixel_format == DRM_FORMAT_RGB565)
- stride_alignment = 128;
- else
- stride_alignment = 64;
- layout->num_planes = 3;
-
- /* Nothing in plane[1] */
-
- /* RGB bitstream stride and plane size */
- aligned_bitstream_width = ALIGN(width, stride_alignment);
- layout->plane_pitch[0] = aligned_bitstream_width * fmt->bpp;
- layout->plane_size[0] = ALIGN(fmt->bpp * aligned_bitstream_width
- * ALIGN(height, 16), 4096);
-
- /* RGB meta data stride and plane size */
- layout->plane_pitch[2] = ALIGN(DIV_ROUND_UP(
- aligned_bitstream_width, 16), 64);
- layout->plane_size[2] = ALIGN(layout->plane_pitch[2] *
- ALIGN(DIV_ROUND_UP(height, 4), 16), 4096);
- } else {
+ color = _sde_format_get_media_color_ubwc(fmt);
+ if (color < 0) {
DRM_ERROR("UBWC format not supported for fmt:0x%X\n",
fmt->base.pixel_format);
return -EINVAL;
}
+ if (SDE_FORMAT_IS_YUV(layout->format)) {
+ uint32_t y_sclines, uv_sclines;
+ uint32_t y_meta_scanlines = 0;
+ uint32_t uv_meta_scanlines = 0;
+
+ layout->num_planes = 4;
+ layout->plane_pitch[0] = VENUS_Y_STRIDE(color, width);
+ y_sclines = VENUS_Y_SCANLINES(color, height);
+ layout->plane_size[0] = MSM_MEDIA_ALIGN(layout->plane_pitch[0] *
+ y_sclines, SDE_UBWC_PLANE_SIZE_ALIGNMENT);
+
+ layout->plane_pitch[1] = VENUS_UV_STRIDE(color, width);
+ uv_sclines = VENUS_UV_SCANLINES(color, height);
+ layout->plane_size[1] = MSM_MEDIA_ALIGN(layout->plane_pitch[1] *
+ uv_sclines, SDE_UBWC_PLANE_SIZE_ALIGNMENT);
+
+ layout->plane_pitch[2] = VENUS_Y_META_STRIDE(color, width);
+ y_meta_scanlines = VENUS_Y_META_SCANLINES(color, height);
+ layout->plane_size[2] = MSM_MEDIA_ALIGN(layout->plane_pitch[2] *
+ y_meta_scanlines, SDE_UBWC_PLANE_SIZE_ALIGNMENT);
+
+ layout->plane_pitch[3] = VENUS_UV_META_STRIDE(color, width);
+ uv_meta_scanlines = VENUS_UV_META_SCANLINES(color, height);
+ layout->plane_size[3] = MSM_MEDIA_ALIGN(layout->plane_pitch[3] *
+ uv_meta_scanlines, SDE_UBWC_PLANE_SIZE_ALIGNMENT);
+
+ } else {
+ uint32_t rgb_scanlines, rgb_meta_scanlines;
+
+ layout->num_planes = 3;
+
+ layout->plane_pitch[0] = VENUS_RGB_STRIDE(color, width);
+ rgb_scanlines = VENUS_RGB_SCANLINES(color, height);
+ layout->plane_size[0] = MSM_MEDIA_ALIGN(layout->plane_pitch[0] *
+ rgb_scanlines, SDE_UBWC_PLANE_SIZE_ALIGNMENT);
+
+ layout->plane_pitch[2] = VENUS_RGB_META_STRIDE(color, width);
+ rgb_meta_scanlines = VENUS_RGB_META_SCANLINES(color, height);
+ layout->plane_size[2] = MSM_MEDIA_ALIGN(layout->plane_pitch[2] *
+ rgb_meta_scanlines, SDE_UBWC_PLANE_SIZE_ALIGNMENT);
+ }
+
for (i = 0; i < SDE_MAX_PLANES; i++)
layout->total_size += layout->plane_size[i];
@@ -573,6 +629,7 @@
} else {
uint32_t v_subsample, h_subsample;
uint32_t chroma_samp;
+ uint32_t bpp = 1;
chroma_samp = fmt->chroma_sample;
_sde_get_v_h_subsample_rate(chroma_samp, &v_subsample,
@@ -583,8 +640,11 @@
return -EINVAL;
}
- layout->plane_pitch[0] = width;
- layout->plane_pitch[1] = width / h_subsample;
+ if ((fmt->base.pixel_format == DRM_FORMAT_NV12) &&
+ (SDE_FORMAT_IS_DX(fmt)))
+ bpp = 2;
+ layout->plane_pitch[0] = width * bpp;
+ layout->plane_pitch[1] = layout->plane_pitch[0] / h_subsample;
layout->plane_size[0] = layout->plane_pitch[0] * height;
layout->plane_size[1] = layout->plane_pitch[1] *
(height / v_subsample);
@@ -925,6 +985,23 @@
map_size = ARRAY_SIZE(sde_format_map_ubwc);
DBG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_COMPRESSED", format);
break;
+ case DRM_FORMAT_MOD_QCOM_DX:
+ map = sde_format_map_p010;
+ map_size = ARRAY_SIZE(sde_format_map_p010);
+ DBG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_DX", format);
+ break;
+ case (DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_COMPRESSED):
+ map = sde_format_map_p010_ubwc;
+ map_size = ARRAY_SIZE(sde_format_map_p010_ubwc);
+ DBG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_COMPRESSED/DX", format);
+ break;
+ case (DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_COMPRESSED |
+ DRM_FORMAT_MOD_QCOM_TIGHT):
+ map = sde_format_map_tp10_ubwc;
+ map_size = ARRAY_SIZE(sde_format_map_tp10_ubwc);
+ DBG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_COMPRESSED/DX/TIGHT",
+ format);
+ break;
default:
DRM_ERROR("unsupported format modifier %llX\n", mod0);
return NULL;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index c53a373..a74a392 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -108,6 +108,12 @@
};
enum {
+ PERF_MAX_BW_LOW,
+ PERF_MAX_BW_HIGH,
+ PERF_PROP_MAX,
+};
+
+enum {
SSPP_OFF,
SSPP_SIZE,
SSPP_TYPE,
@@ -126,6 +132,7 @@
enum {
VIG_QSEED_OFF,
+ VIG_QSEED_LEN,
VIG_CSC_OFF,
VIG_HSIC_PROP,
VIG_MEMCOLOR_PROP,
@@ -135,6 +142,7 @@
enum {
RGB_SCALER_OFF,
+ RGB_SCALER_LEN,
RGB_PCC_PROP,
RGB_PROP_MAX,
};
@@ -284,6 +292,11 @@
{SMART_DMA_REV, "qcom,sde-smart-dma-rev", false, PROP_TYPE_STRING},
};
+static struct sde_prop_type sde_perf_prop[] = {
+ {PERF_MAX_BW_LOW, "qcom,sde-max-bw-low-kbps", false, PROP_TYPE_U32},
+ {PERF_MAX_BW_HIGH, "qcom,sde-max-bw-high-kbps", false, PROP_TYPE_U32},
+};
+
static struct sde_prop_type sspp_prop[] = {
{SSPP_OFF, "qcom,sde-sspp-off", true, PROP_TYPE_U32_ARRAY},
{SSPP_SIZE, "qcom,sde-sspp-src-size", false, PROP_TYPE_U32},
@@ -305,6 +318,7 @@
static struct sde_prop_type vig_prop[] = {
{VIG_QSEED_OFF, "qcom,sde-vig-qseed-off", false, PROP_TYPE_U32},
+ {VIG_QSEED_LEN, "qcom,sde-vig-qseed-size", false, PROP_TYPE_U32},
{VIG_CSC_OFF, "qcom,sde-vig-csc-off", false, PROP_TYPE_U32},
{VIG_HSIC_PROP, "qcom,sde-vig-hsic", false, PROP_TYPE_U32_ARRAY},
{VIG_MEMCOLOR_PROP, "qcom,sde-vig-memcolor", false,
@@ -314,6 +328,7 @@
static struct sde_prop_type rgb_prop[] = {
{RGB_SCALER_OFF, "qcom,sde-rgb-scaler-off", false, PROP_TYPE_U32},
+ {RGB_SCALER_LEN, "qcom,sde-rgb-scaler-size", false, PROP_TYPE_U32},
{RGB_PCC_PROP, "qcom,sde-rgb-pcc", false, PROP_TYPE_U32_ARRAY},
};
@@ -678,6 +693,7 @@
sblk->maxupscale = MAX_SSPP_UPSCALE;
sblk->maxdwnscale = MAX_SSPP_DOWNSCALE;
sspp->id = SSPP_VIG0 + *vig_count;
+ snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u", sspp->id);
sspp->clk_ctrl = SDE_CLK_CTRL_VIG0 + *vig_count;
sblk->format_list = plane_formats_yuv;
set_bit(SDE_SSPP_QOS, &sspp->features);
@@ -691,14 +707,24 @@
sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED2;
sblk->scaler_blk.base = PROP_VALUE_ACCESS(prop_value,
VIG_QSEED_OFF, 0);
+ sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value,
+ VIG_QSEED_LEN, 0);
+ snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_scaler%u", sspp->id);
} else if (sde_cfg->qseed_type == SDE_SSPP_SCALER_QSEED3) {
set_bit(SDE_SSPP_SCALER_QSEED3, &sspp->features);
sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED3;
sblk->scaler_blk.base = PROP_VALUE_ACCESS(prop_value,
VIG_QSEED_OFF, 0);
+ sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value,
+ VIG_QSEED_LEN, 0);
+ snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_scaler%u", sspp->id);
}
sblk->csc_blk.id = SDE_SSPP_CSC;
+ snprintf(sblk->csc_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_csc%u", sspp->id);
if (sde_cfg->csc_type == SDE_SSPP_CSC) {
set_bit(SDE_SSPP_CSC, &sspp->features);
sblk->csc_blk.base = PROP_VALUE_ACCESS(prop_value,
@@ -710,6 +736,8 @@
}
sblk->hsic_blk.id = SDE_SSPP_HSIC;
+ snprintf(sblk->hsic_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_hsic%u", sspp->id);
if (prop_exists[VIG_HSIC_PROP]) {
sblk->hsic_blk.base = PROP_VALUE_ACCESS(prop_value,
VIG_HSIC_PROP, 0);
@@ -720,6 +748,8 @@
}
sblk->memcolor_blk.id = SDE_SSPP_MEMCOLOR;
+ snprintf(sblk->memcolor_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_memcolor%u", sspp->id);
if (prop_exists[VIG_MEMCOLOR_PROP]) {
sblk->memcolor_blk.base = PROP_VALUE_ACCESS(prop_value,
VIG_MEMCOLOR_PROP, 0);
@@ -730,6 +760,8 @@
}
sblk->pcc_blk.id = SDE_SSPP_PCC;
+ snprintf(sblk->pcc_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_pcc%u", sspp->id);
if (prop_exists[VIG_PCC_PROP]) {
sblk->pcc_blk.base = PROP_VALUE_ACCESS(prop_value,
VIG_PCC_PROP, 0);
@@ -747,6 +779,7 @@
sblk->maxupscale = MAX_SSPP_UPSCALE;
sblk->maxdwnscale = MAX_SSPP_DOWNSCALE;
sspp->id = SSPP_RGB0 + *rgb_count;
+ snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u", sspp->id);
sspp->clk_ctrl = SDE_CLK_CTRL_RGB0 + *rgb_count;
sblk->format_list = plane_formats;
set_bit(SDE_SSPP_QOS, &sspp->features);
@@ -760,11 +793,19 @@
sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED2;
sblk->scaler_blk.base = PROP_VALUE_ACCESS(prop_value,
RGB_SCALER_OFF, 0);
+ sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value,
+ RGB_SCALER_LEN, 0);
+ snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_scaler%u", sspp->id);
} else if (sde_cfg->qseed_type == SDE_SSPP_SCALER_QSEED3) {
set_bit(SDE_SSPP_SCALER_RGB, &sspp->features);
sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED3;
sblk->scaler_blk.base = PROP_VALUE_ACCESS(prop_value,
- RGB_SCALER_OFF, 0);
+ RGB_SCALER_LEN, 0);
+ sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value,
+ SSPP_SCALE_SIZE, 0);
+ snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_scaler%u", sspp->id);
}
sblk->pcc_blk.id = SDE_SSPP_PCC;
@@ -786,6 +827,7 @@
sblk->maxupscale = SSPP_UNITY_SCALE;
sblk->maxdwnscale = SSPP_UNITY_SCALE;
sspp->id = SSPP_CURSOR0 + *cursor_count;
+ snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u", sspp->id);
sspp->clk_ctrl = SDE_CLK_CTRL_CURSOR0 + *cursor_count;
sblk->format_list = plane_formats;
(*cursor_count)++;
@@ -799,6 +841,7 @@
sblk->maxdwnscale = SSPP_UNITY_SCALE;
sspp->id = SSPP_DMA0 + *dma_count;
sspp->clk_ctrl = SDE_CLK_CTRL_DMA0 + *dma_count;
+ snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u", sspp->id);
sblk->format_list = plane_formats;
set_bit(SDE_SSPP_QOS, &sspp->features);
(*dma_count)++;
@@ -897,6 +940,7 @@
sspp->sblk = sblk;
sspp->base = PROP_VALUE_ACCESS(prop_value, SSPP_OFF, i);
+ sspp->len = PROP_VALUE_ACCESS(prop_value, SSPP_SIZE, 0);
sblk->maxlinewidth = sde_cfg->max_sspp_linewidth;
set_bit(SDE_SSPP_SRC, &sspp->features);
@@ -908,6 +952,8 @@
set_bit(sde_cfg->smart_dma_rev, &sspp->features);
sblk->src_blk.id = SDE_SSPP_SRC;
+ snprintf(sblk->src_blk.name, SDE_HW_BLK_NAME_LEN, "sspp_src_%u",
+ sblk->src_blk.id);
of_property_read_string_index(np,
sspp_prop[SSPP_TYPE].prop_name, i, &type);
@@ -1023,7 +1069,9 @@
for (i = 0; i < off_count; i++) {
ctl = sde_cfg->ctl + i;
ctl->base = PROP_VALUE_ACCESS(prop_value, HW_OFF, i);
+ ctl->len = PROP_VALUE_ACCESS(prop_value, HW_LEN, 0);
ctl->id = CTL_0 + i;
+ snprintf(ctl->name, SDE_HW_BLK_NAME_LEN, "ctl_%u", ctl->id);
if (i < MAX_SPLIT_DISPLAY_CTL)
set_bit(SDE_CTL_SPLIT_DISPLAY, &ctl->features);
@@ -1134,6 +1182,8 @@
mixer->base = PROP_VALUE_ACCESS(prop_value, MIXER_OFF, i);
mixer->len = PROP_VALUE_ACCESS(prop_value, MIXER_LEN, 0);
mixer->id = LM_0 + i;
+ snprintf(mixer->name, SDE_HW_BLK_NAME_LEN, "lm_%u", mixer->id);
+
if (!prop_exists[MIXER_LEN])
mixer->len = DEFAULT_SDE_HW_BLOCK_LEN;
@@ -1228,6 +1278,8 @@
intf->base = PROP_VALUE_ACCESS(prop_value, INTF_OFF, i);
intf->len = PROP_VALUE_ACCESS(prop_value, INTF_LEN, 0);
intf->id = INTF_0 + i;
+ snprintf(intf->name, SDE_HW_BLK_NAME_LEN, "intf_%u", intf->id);
+
if (!prop_exists[INTF_LEN])
intf->len = DEFAULT_SDE_HW_BLOCK_LEN;
@@ -1306,6 +1358,7 @@
wb->base = PROP_VALUE_ACCESS(prop_value, WB_OFF, i);
wb->id = WB_0 + PROP_VALUE_ACCESS(prop_value, WB_ID, i);
+ snprintf(wb->name, SDE_HW_BLK_NAME_LEN, "wb_%u", wb->id);
wb->clk_ctrl = SDE_CLK_CTRL_WB0 +
PROP_VALUE_ACCESS(prop_value, WB_ID, i);
wb->xin_id = PROP_VALUE_ACCESS(prop_value, WB_XIN_ID, i);
@@ -1531,7 +1584,9 @@
for (i = 0; i < off_count; i++) {
dspp = sde_cfg->dspp + i;
dspp->base = PROP_VALUE_ACCESS(prop_value, DSPP_OFF, i);
+ dspp->len = PROP_VALUE_ACCESS(prop_value, DSPP_SIZE, 0);
dspp->id = DSPP_0 + i;
+ snprintf(dspp->name, SDE_HW_BLK_NAME_LEN, "dspp_%u", dspp->id);
sblk = kzalloc(sizeof(*sblk), GFP_KERNEL);
if (!sblk) {
@@ -1601,6 +1656,7 @@
cdm = sde_cfg->cdm + i;
cdm->base = PROP_VALUE_ACCESS(prop_value, HW_OFF, i);
cdm->id = CDM_0 + i;
+ snprintf(cdm->name, SDE_HW_BLK_NAME_LEN, "cdm_%u", cdm->id);
cdm->len = PROP_VALUE_ACCESS(prop_value, HW_LEN, 0);
/* intf3 and wb2 for cdm block */
@@ -1792,15 +1848,19 @@
pp->base = PROP_VALUE_ACCESS(prop_value, PP_OFF, i);
pp->id = PINGPONG_0 + i;
+ snprintf(pp->name, SDE_HW_BLK_NAME_LEN, "pingpong_%u", pp->id);
pp->len = PROP_VALUE_ACCESS(prop_value, PP_LEN, 0);
sblk->te.base = PROP_VALUE_ACCESS(prop_value, TE_OFF, i);
sblk->te.id = SDE_PINGPONG_TE;
+ snprintf(sblk->te.name, SDE_HW_BLK_NAME_LEN, "te_%u", pp->id);
set_bit(SDE_PINGPONG_TE, &pp->features);
sblk->te2.base = PROP_VALUE_ACCESS(prop_value, TE2_OFF, i);
if (sblk->te2.base) {
sblk->te2.id = SDE_PINGPONG_TE2;
+ snprintf(sblk->te2.name, SDE_HW_BLK_NAME_LEN, "te2_%u",
+ pp->id);
set_bit(SDE_PINGPONG_TE2, &pp->features);
set_bit(SDE_PINGPONG_SPLIT, &pp->features);
}
@@ -1811,6 +1871,8 @@
sblk->dsc.base = PROP_VALUE_ACCESS(prop_value, DSC_OFF, i);
if (sblk->dsc.base) {
sblk->dsc.id = SDE_PINGPONG_DSC;
+ snprintf(sblk->dsc.name, SDE_HW_BLK_NAME_LEN, "dsc_%u",
+ pp->id);
set_bit(SDE_PINGPONG_DSC, &pp->features);
}
}
@@ -1853,9 +1915,13 @@
cfg->mdss_count = 1;
cfg->mdss[0].base = MDSS_BASE_OFFSET;
cfg->mdss[0].id = MDP_TOP;
+ snprintf(cfg->mdss[0].name, SDE_HW_BLK_NAME_LEN, "mdss_%u",
+ cfg->mdss[0].id);
cfg->mdp_count = 1;
cfg->mdp[0].id = MDP_TOP;
+ snprintf(cfg->mdp[0].name, SDE_HW_BLK_NAME_LEN, "top_%u",
+ cfg->mdp[0].id);
cfg->mdp[0].base = PROP_VALUE_ACCESS(prop_value, SDE_OFF, 0);
cfg->mdp[0].len = PROP_VALUE_ACCESS(prop_value, SDE_LEN, 0);
if (!prop_exists[SDE_LEN])
@@ -1959,6 +2025,46 @@
return 0;
}
+static int sde_perf_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg)
+{
+ int rc, len, prop_count[PERF_PROP_MAX];
+ struct sde_prop_value *prop_value = NULL;
+ bool prop_exists[PERF_PROP_MAX];
+
+ if (!cfg) {
+ SDE_ERROR("invalid argument\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ prop_value = kzalloc(SDE_PROP_MAX *
+ sizeof(struct sde_prop_value), GFP_KERNEL);
+ if (!prop_value) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ rc = _validate_dt_entry(np, sde_perf_prop, ARRAY_SIZE(sde_perf_prop),
+ prop_count, &len);
+ if (rc)
+ goto freeprop;
+
+ rc = _read_dt_entry(np, sde_perf_prop, ARRAY_SIZE(sde_perf_prop),
+ prop_count, prop_exists, prop_value);
+ if (rc)
+ goto freeprop;
+
+ cfg->perf.max_bw_low =
+ PROP_VALUE_ACCESS(prop_value, PERF_MAX_BW_LOW, 0);
+ cfg->perf.max_bw_high =
+ PROP_VALUE_ACCESS(prop_value, PERF_MAX_BW_HIGH, 0);
+
+freeprop:
+ kfree(prop_value);
+end:
+ return rc;
+}
+
static void sde_hardware_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev)
{
switch (hw_rev) {
@@ -2064,6 +2170,10 @@
if (rc)
goto end;
+ rc = sde_perf_parse_dt(np, sde_cfg);
+ if (rc)
+ goto end;
+
sde_hardware_caps(sde_cfg, hw_rev);
return sde_cfg;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index d28be49a..7a5aae3 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -17,6 +17,7 @@
#include <linux/bug.h>
#include <linux/bitmap.h>
#include <linux/err.h>
+#include <linux/msm-bus.h>
#include <drm/drmP.h>
/**
@@ -42,10 +43,13 @@
#define SDE_HW_VER_171 SDE_HW_VER(1, 7, 1) /* 8996 v2.0 */
#define SDE_HW_VER_172 SDE_HW_VER(1, 7, 2) /* 8996 v3.0 */
#define SDE_HW_VER_300 SDE_HW_VER(3, 0, 0) /* 8998 v1.0 */
+#define SDE_HW_VER_301 SDE_HW_VER(3, 0, 1) /* 8998 v1.1 */
#define SDE_HW_VER_400 SDE_HW_VER(4, 0, 0) /* sdm845 v1.0 */
#define IS_SDM845_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_400)
+#define SDE_HW_BLK_NAME_LEN 16
+
#define MAX_IMG_WIDTH 0x3fff
#define MAX_IMG_HEIGHT 0x3fff
@@ -240,12 +244,14 @@
/**
* MACRO SDE_HW_BLK_INFO - information of HW blocks inside SDE
+ * @name: string name for debug purposes
* @id: enum identifying this block
* @base: register base offset to mdss
* @len: length of hardware block
* @features bit mask identifying sub-blocks/features
*/
#define SDE_HW_BLK_INFO \
+ char name[SDE_HW_BLK_NAME_LEN]; \
u32 id; \
u32 base; \
u32 len; \
@@ -253,12 +259,14 @@
/**
* MACRO SDE_HW_SUBBLK_INFO - information of HW sub-block inside SDE
+ * @name: string name for debug purposes
* @id: enum identifying this sub-block
* @base: offset of this sub-block relative to the block
* offset
* @len register block length of this sub-block
*/
#define SDE_HW_SUBBLK_INFO \
+ char name[SDE_HW_BLK_NAME_LEN]; \
u32 id; \
u32 base; \
u32 len
@@ -620,6 +628,16 @@
};
/**
+ * struct sde_perf_cfg - performance control settings
+ * @max_bw_low low threshold of maximum bandwidth (kbps)
+ * @max_bw_high high threshold of maximum bandwidth (kbps)
+ */
+struct sde_perf_cfg {
+ u32 max_bw_low;
+ u32 max_bw_high;
+};
+
+/**
* struct sde_mdss_cfg - information of MDSS HW
* This is the main catalog data structure representing
* this HW version. Contains number of instances,
@@ -688,6 +706,8 @@
u32 reg_dma_count;
struct sde_reg_dma_cfg dma_cfg;
/* Add additional block data structures here */
+
+ struct sde_perf_cfg perf;
};
struct sde_mdss_hw_cfg_handler {
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
index dad039e..c0d5044 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
@@ -14,6 +14,7 @@
#include "sde_hwio.h"
#include "sde_hw_catalog.h"
#include "sde_hw_cdm.h"
+#include "sde_dbg.h"
#define CDM_CSC_10_OPMODE 0x000
#define CDM_CSC_10_BASE 0x004
@@ -314,6 +315,9 @@
_setup_cdm_ops(&c->ops, c->cdm_hw_cap->features);
c->hw_mdp = hw_mdp;
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
index 19e3a7a..0e756b4 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
@@ -13,6 +13,7 @@
#include <linux/delay.h>
#include "sde_hwio.h"
#include "sde_hw_ctl.h"
+#include "sde_dbg.h"
#define CTL_LAYER(lm) \
(((lm) == LM_5) ? (0x024) : (((lm) - LM_0) * 0x004))
@@ -41,6 +42,7 @@
if (ctl == m->ctl[i].id) {
b->base_off = addr;
b->blk_off = m->ctl[i].base;
+ b->length = m->ctl[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_CTL;
return &m->ctl[i];
@@ -249,23 +251,58 @@
return 0;
}
+static u32 sde_hw_ctl_poll_reset_status(struct sde_hw_ctl *ctx, u32 count)
+{
+ struct sde_hw_blk_reg_map *c = &ctx->hw;
+ u32 status;
+
+ /* protect to do at least one iteration */
+ if (!count)
+ count = 1;
+
+ /*
+ * it takes around 30us to have mdp finish resetting its ctl path
+ * poll every 50us so that reset should be completed at 1st poll
+ */
+ do {
+ status = SDE_REG_READ(c, CTL_SW_RESET);
+ status &= 0x01;
+ if (status)
+ usleep_range(20, 50);
+ } while (status && --count > 0);
+
+ return status;
+}
+
static int sde_hw_ctl_reset_control(struct sde_hw_ctl *ctx)
{
struct sde_hw_blk_reg_map *c = &ctx->hw;
- int count = SDE_REG_RESET_TIMEOUT_COUNT;
- int reset;
+ pr_debug("issuing hw ctl reset for ctl:%d\n", ctx->idx);
SDE_REG_WRITE(c, CTL_SW_RESET, 0x1);
+ if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_COUNT))
+ return -EINVAL;
- for (; count > 0; count--) {
- /* insert small delay to avoid spinning the cpu while waiting */
- usleep_range(20, 50);
- reset = SDE_REG_READ(c, CTL_SW_RESET);
- if (reset == 0)
- return 0;
+ return 0;
+}
+
+static int sde_hw_ctl_wait_reset_status(struct sde_hw_ctl *ctx)
+{
+ struct sde_hw_blk_reg_map *c = &ctx->hw;
+ u32 status;
+
+ status = SDE_REG_READ(c, CTL_SW_RESET);
+ status &= 0x01;
+ if (!status)
+ return 0;
+
+ pr_debug("hw ctl reset is set for ctl:%d\n", ctx->idx);
+ if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_COUNT)) {
+ pr_err("hw recovery is not complete for ctl:%d\n", ctx->idx);
+ return -EINVAL;
}
- return -EINVAL;
+ return 0;
}
static void sde_hw_ctl_clear_all_blendstages(struct sde_hw_ctl *ctx)
@@ -455,6 +492,7 @@
ops->trigger_start = sde_hw_ctl_trigger_start;
ops->setup_intf_cfg = sde_hw_ctl_intf_cfg;
ops->reset = sde_hw_ctl_reset_control;
+ ops->wait_reset_status = sde_hw_ctl_wait_reset_status;
ops->clear_all_blendstages = sde_hw_ctl_clear_all_blendstages;
ops->setup_blendstage = sde_hw_ctl_setup_blendstage;
ops->get_bitmask_sspp = sde_hw_ctl_get_bitmask_sspp;
@@ -489,6 +527,9 @@
c->mixer_count = m->mixer_count;
c->mixer_hw_caps = m->mixer;
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
index 670a03d..4d1170e 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
@@ -107,6 +107,17 @@
int (*reset)(struct sde_hw_ctl *c);
+ /*
+ * wait_reset_status - checks ctl reset status
+ * @ctx : ctl path ctx pointer
+ *
+ * This function checks the ctl reset status bit.
+ * If the reset bit is set, it keeps polling the status till the hw
+ * reset is complete.
+ * Returns: 0 on success or -error if reset incomplete within interval
+ */
+ int (*wait_reset_status)(struct sde_hw_ctl *ctx);
+
uint32_t (*get_bitmask_sspp)(struct sde_hw_ctl *ctx,
enum sde_sspp blk);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
index 51ab26e..6110a07 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
@@ -15,6 +15,7 @@
#include "sde_hw_catalog.h"
#include "sde_hw_dspp.h"
#include "sde_hw_color_processing.h"
+#include "sde_dbg.h"
static struct sde_dspp_cfg *_dspp_offset(enum sde_dspp dspp,
struct sde_mdss_cfg *m,
@@ -27,6 +28,7 @@
if (dspp == m->dspp[i].id) {
b->base_off = addr;
b->blk_off = m->dspp[i].base;
+ b->length = m->dspp[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_DSPP;
return &m->dspp[i];
@@ -122,6 +124,9 @@
c->cap = cfg;
_setup_dspp_ops(c, c->cap->features);
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_intf.c b/drivers/gpu/drm/msm/sde/sde_hw_intf.c
index f0fc8f6..c17844d 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_intf.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_intf.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,6 +13,7 @@
#include "sde_hwio.h"
#include "sde_hw_catalog.h"
#include "sde_hw_intf.h"
+#include "sde_dbg.h"
#define INTF_TIMING_ENGINE_EN 0x000
#define INTF_CONFIG 0x004
@@ -83,6 +84,7 @@
(m->intf[i].type != INTF_NONE)) {
b->base_off = addr;
b->blk_off = m->intf[i].base;
+ b->length = m->intf[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_INTF;
return &m->intf[i];
@@ -329,9 +331,9 @@
c->mdss = m;
_setup_intf_ops(&c->ops, c->cap->features);
- /*
- * Perform any default initialization for the intf
- */
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_lm.c b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
index a471dad..520c7b1 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_lm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
@@ -15,6 +15,7 @@
#include "sde_hwio.h"
#include "sde_hw_lm.h"
#include "sde_hw_mdss.h"
+#include "sde_dbg.h"
#define LM_OP_MODE 0x00
#define LM_OUT_SIZE 0x04
@@ -43,6 +44,7 @@
if (mixer == m->mixer[i].id) {
b->base_off = addr;
b->blk_off = m->mixer[i].base;
+ b->length = m->mixer[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_LM;
return &m->mixer[i];
@@ -263,9 +265,9 @@
c->cap = cfg;
_setup_mixer_ops(m, &c->ops, c->cap->features);
- /*
- * Perform any default initialization for the sspp blocks
- */
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
index f799ad1..c500966 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
@@ -18,6 +18,8 @@
#include "msm_drv.h"
+#define SDE_DBG_NAME "sde"
+
#define SDE_NONE 0
#ifndef SDE_CSC_MATRIX_COEFF_SIZE
@@ -41,11 +43,18 @@
#define SDE_MAX_DE_CURVES 3
#endif
-#define SDE_FORMAT_FLAG_YUV (1 << 0)
-#define SDE_FORMAT_FLAG_DX (1 << 1)
+enum sde_format_flags {
+ SDE_FORMAT_FLAG_YUV_BIT,
+ SDE_FORMAT_FLAG_DX_BIT,
+ SDE_FORMAT_FLAG_BIT_MAX,
+};
-#define SDE_FORMAT_IS_YUV(X) ((X)->flag & SDE_FORMAT_FLAG_YUV)
-#define SDE_FORMAT_IS_DX(X) ((X)->flag & SDE_FORMAT_FLAG_DX)
+#define SDE_FORMAT_FLAG_YUV BIT(SDE_FORMAT_FLAG_YUV_BIT)
+#define SDE_FORMAT_FLAG_DX BIT(SDE_FORMAT_FLAG_DX_BIT)
+#define SDE_FORMAT_IS_YUV(X) \
+ (test_bit(SDE_FORMAT_FLAG_YUV_BIT, (X)->flag))
+#define SDE_FORMAT_IS_DX(X) \
+ (test_bit(SDE_FORMAT_FLAG_DX_BIT, (X)->flag))
#define SDE_FORMAT_IS_LINEAR(X) ((X)->fetch_mode == SDE_FETCH_LINEAR)
#define SDE_FORMAT_IS_UBWC(X) ((X)->fetch_mode == SDE_FETCH_UBWC)
@@ -361,7 +370,7 @@
u8 alpha_enable;
u8 num_planes;
enum sde_fetch_type fetch_mode;
- u32 flag;
+ DECLARE_BITMAP(flag, SDE_FORMAT_FLAG_BIT_MAX);
u16 tile_width;
u16 tile_height;
};
@@ -439,11 +448,13 @@
* @payload: Feature specific payload.
* @len: Length of the payload.
* @ctl: control pointer associated with dspp/lm.
+ * @last_feature: last feature that will be set.
*/
struct sde_hw_cp_cfg {
void *payload;
u32 len;
void *ctl;
+ u32 last_feature;
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
index 837edee..8488d03 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -14,6 +14,7 @@
#include "sde_hwio.h"
#include "sde_hw_catalog.h"
#include "sde_hw_pingpong.h"
+#include "sde_dbg.h"
#define PP_TEAR_CHECK_EN 0x000
#define PP_SYNC_CONFIG_VSYNC 0x004
@@ -47,6 +48,7 @@
if (pp == m->pingpong[i].id) {
b->base_off = addr;
b->blk_off = m->pingpong[i].base;
+ b->length = m->pingpong[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_PINGPONG;
return &m->pingpong[i];
@@ -159,6 +161,9 @@
c->pingpong_hw_cap = cfg;
_setup_pingpong_ops(&c->ops, c->pingpong_hw_cap->features);
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c
index 3bc5a77..49af946 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c
@@ -468,6 +468,7 @@
REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, dspp_buf[GAMUT][ctx->idx],
REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE);
+ kick_off.last_command = hw_cfg->last_feature;
rc = dma_ops->kick_off(&kick_off);
if (rc)
DRM_ERROR("failed to kick off ret %d\n", rc);
@@ -540,6 +541,7 @@
REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, dspp_buf[GC][ctx->idx],
REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE);
+ kick_off.last_command = hw_cfg->last_feature;
rc = dma_ops->kick_off(&kick_off);
if (rc) {
DRM_ERROR("failed to kick off ret %d\n", rc);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
index 1b98683..c7f6809 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
@@ -15,6 +15,7 @@
#include "sde_hw_lm.h"
#include "sde_hw_sspp.h"
#include "sde_hw_color_processing.h"
+#include "sde_dbg.h"
#define SDE_FETCH_CONFIG_RESET_VALUE 0x00000087
@@ -1057,6 +1058,7 @@
if (sspp == catalog->sspp[i].id) {
b->base_off = addr;
b->blk_off = catalog->sspp[i].base;
+ b->length = catalog->sspp[i].len;
b->hwversion = catalog->hwversion;
b->log_mask = SDE_DBG_MASK_SSPP;
return &catalog->sspp[i];
@@ -1071,26 +1073,38 @@
void __iomem *addr,
struct sde_mdss_cfg *catalog)
{
- struct sde_hw_pipe *ctx;
+ struct sde_hw_pipe *hw_pipe;
struct sde_sspp_cfg *cfg;
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
+ hw_pipe = kzalloc(sizeof(*hw_pipe), GFP_KERNEL);
+ if (!hw_pipe)
return ERR_PTR(-ENOMEM);
- cfg = _sspp_offset(idx, addr, catalog, &ctx->hw);
+ cfg = _sspp_offset(idx, addr, catalog, &hw_pipe->hw);
if (IS_ERR_OR_NULL(cfg)) {
- kfree(ctx);
+ kfree(hw_pipe);
return ERR_PTR(-EINVAL);
}
/* Assign ops */
- ctx->idx = idx;
- ctx->cap = cfg;
- _setup_layer_ops(ctx, ctx->cap->features);
- ctx->highest_bank_bit = catalog->mdp[0].highest_bank_bit;
+ hw_pipe->idx = idx;
+ hw_pipe->cap = cfg;
+ _setup_layer_ops(hw_pipe, hw_pipe->cap->features);
+ hw_pipe->highest_bank_bit = catalog->mdp[0].highest_bank_bit;
- return ctx;
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name,
+ hw_pipe->hw.blk_off,
+ hw_pipe->hw.blk_off + hw_pipe->hw.length,
+ hw_pipe->hw.xin_id);
+
+ if (cfg->sblk->scaler_blk.len)
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME,
+ cfg->sblk->scaler_blk.name,
+ cfg->sblk->scaler_blk.base,
+ cfg->sblk->scaler_blk.base + cfg->sblk->scaler_blk.len,
+ hw_pipe->hw.xin_id);
+
+ return hw_pipe;
}
void sde_hw_sspp_destroy(struct sde_hw_pipe *ctx)
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.c b/drivers/gpu/drm/msm/sde/sde_hw_top.c
index 1a5d469..c4b4592 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_top.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_top.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,6 +13,7 @@
#include "sde_hwio.h"
#include "sde_hw_catalog.h"
#include "sde_hw_top.h"
+#include "sde_dbg.h"
#define SSPP_SPARE 0x28
@@ -221,6 +222,7 @@
if (mdp == m->mdp[i].id) {
b->base_off = addr;
b->blk_off = m->mdp[i].base;
+ b->length = m->mdp[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_TOP;
return &m->mdp[i];
@@ -254,9 +256,9 @@
mdp->cap = cfg;
_setup_mdp_ops(&mdp->ops, mdp->cap->features);
- /*
- * Perform any default initialization for the intf
- */
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name,
+ mdp->hw.blk_off, mdp->hw.blk_off + mdp->hw.length,
+ mdp->hw.xin_id);
return mdp;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_util.h b/drivers/gpu/drm/msm/sde/sde_hw_util.h
index a4d8be9..15c0b36 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_util.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_util.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -24,12 +24,14 @@
* @base_off: mdp register mapped offset
* @blk_off: pipe offset relative to mdss offset
* @length length of register block offset
+ * @xin_id xin id
* @hwversion mdss hw version number
*/
struct sde_hw_blk_reg_map {
void __iomem *base_off;
u32 blk_off;
u32 length;
+ u32 xin_id;
u32 hwversion;
u32 log_mask;
};
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
index 76473fa..e9f54d0 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,6 +13,7 @@
#include "sde_hwio.h"
#include "sde_hw_catalog.h"
#include "sde_hw_vbif.h"
+#include "sde_dbg.h"
#define VBIF_VERSION 0x0000
#define VBIF_CLK_FORCE_CTRL0 0x0008
@@ -123,6 +124,7 @@
if (vbif == m->vbif[i].id) {
b->base_off = addr;
b->blk_off = m->vbif[i].base;
+ b->length = m->vbif[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_VBIF;
return &m->vbif[i];
@@ -156,6 +158,9 @@
c->cap = cfg;
_setup_vbif_ops(&c->ops, c->cap->features);
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_wb.c b/drivers/gpu/drm/msm/sde/sde_hw_wb.c
index 426e999..320b05f 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_wb.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -15,6 +15,7 @@
#include "sde_hw_catalog.h"
#include "sde_hw_wb.h"
#include "sde_formats.h"
+#include "sde_dbg.h"
#define WB_DST_FORMAT 0x000
#define WB_DST_OP_MODE 0x004
@@ -57,6 +58,7 @@
if (wb == m->wb[i].id) {
b->base_off = addr;
b->blk_off = m->wb[i].base;
+ b->length = m->wb[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_WB;
return &m->wb[i];
@@ -215,6 +217,9 @@
c->highest_bank_bit = m->mdp[0].highest_bank_bit;
c->hw_mdp = hw_mdp;
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 5aadae0..39b8863 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -558,7 +558,8 @@
.get_modes = dsi_connector_get_modes,
.mode_valid = dsi_conn_mode_valid,
.get_info = dsi_display_get_info,
- .set_backlight = dsi_display_set_backlight
+ .set_backlight = dsi_display_set_backlight,
+ .soft_reset = dsi_display_soft_reset
};
static const struct sde_connector_ops wb_ops = {
.post_init = sde_wb_connector_post_init,
@@ -566,6 +567,7 @@
.get_modes = sde_wb_connector_get_modes,
.set_property = sde_wb_connector_set_property,
.get_info = sde_wb_get_info,
+ .soft_reset = NULL
};
struct msm_display_info info;
struct drm_encoder *encoder;
@@ -860,6 +862,7 @@
/* safe to call these more than once during shutdown */
_sde_debugfs_destroy(sde_kms);
_sde_kms_mmu_destroy(sde_kms);
+ sde_core_perf_destroy(&sde_kms->perf);
if (sde_kms->catalog) {
for (i = 0; i < sde_kms->catalog->vbif_count; i++) {
@@ -1017,6 +1020,44 @@
return ret;
}
+static void __iomem *_sde_kms_ioremap(struct platform_device *pdev,
+ const char *name, unsigned long *out_size)
+{
+ struct resource *res;
+ unsigned long size;
+ void __iomem *ptr;
+
+ if (out_size)
+ *out_size = 0;
+
+ if (name)
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+ else
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res) {
+ /* availability depends on platform */
+ SDE_DEBUG("failed to get memory resource: %s\n", name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ size = resource_size(res);
+
+ ptr = devm_ioremap_nocache(&pdev->dev, res->start, size);
+ if (!ptr) {
+ SDE_ERROR("failed to ioremap: %s\n", name);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ SDE_DEBUG("IO:region %s %p %08lx\n", name, ptr, size);
+
+ if (out_size)
+ *out_size = size;
+
+ return ptr;
+}
+
+
static int sde_kms_hw_init(struct msm_kms *kms)
{
struct sde_kms *sde_kms;
@@ -1042,7 +1083,8 @@
goto end;
}
- sde_kms->mmio = msm_ioremap(dev->platformdev, "mdp_phys", "SDE");
+ sde_kms->mmio = _sde_kms_ioremap(dev->platformdev, "mdp_phys",
+ &sde_kms->mmio_len);
if (IS_ERR(sde_kms->mmio)) {
rc = PTR_ERR(sde_kms->mmio);
SDE_ERROR("mdp register memory map failed: %d\n", rc);
@@ -1051,8 +1093,13 @@
}
DRM_INFO("mapped mdp address space @%p\n", sde_kms->mmio);
- sde_kms->vbif[VBIF_RT] = msm_ioremap(dev->platformdev,
- "vbif_phys", "VBIF");
+ rc = sde_dbg_reg_register_base(SDE_DBG_NAME, sde_kms->mmio,
+ sde_kms->mmio_len);
+ if (rc)
+ SDE_ERROR("dbg base register kms failed: %d\n", rc);
+
+ sde_kms->vbif[VBIF_RT] = _sde_kms_ioremap(dev->platformdev, "vbif_phys",
+ &sde_kms->vbif_len[VBIF_RT]);
if (IS_ERR(sde_kms->vbif[VBIF_RT])) {
rc = PTR_ERR(sde_kms->vbif[VBIF_RT]);
SDE_ERROR("vbif register memory map failed: %d\n", rc);
@@ -1060,18 +1107,38 @@
goto error;
}
- sde_kms->vbif[VBIF_NRT] = msm_ioremap(dev->platformdev,
- "vbif_nrt_phys", "VBIF_NRT");
+ rc = sde_dbg_reg_register_base("vbif_rt", sde_kms->vbif[VBIF_RT],
+ sde_kms->vbif_len[VBIF_RT]);
+ if (rc)
+ SDE_ERROR("dbg base register vbif_rt failed: %d\n", rc);
+
+ sde_kms->vbif[VBIF_NRT] = _sde_kms_ioremap(dev->platformdev,
+ "vbif_nrt_phys", &sde_kms->vbif_len[VBIF_NRT]);
if (IS_ERR(sde_kms->vbif[VBIF_NRT])) {
sde_kms->vbif[VBIF_NRT] = NULL;
SDE_DEBUG("VBIF NRT is not defined");
+ } else {
+ rc = sde_dbg_reg_register_base("vbif_nrt",
+ sde_kms->vbif[VBIF_NRT],
+ sde_kms->vbif_len[VBIF_NRT]);
+ if (rc)
+ SDE_ERROR("dbg base register vbif_nrt failed: %d\n",
+ rc);
}
- sde_kms->reg_dma = msm_ioremap(dev->platformdev, "regdma_phys",
- "REG_DMA");
+ sde_kms->reg_dma = _sde_kms_ioremap(dev->platformdev, "regdma_phys",
+ &sde_kms->reg_dma_len);
if (IS_ERR(sde_kms->reg_dma)) {
sde_kms->reg_dma = NULL;
SDE_DEBUG("REG_DMA is not defined");
+ } else {
+ rc = sde_dbg_reg_register_base("vbif_nrt",
+ sde_kms->reg_dma,
+ sde_kms->reg_dma_len);
+ if (rc)
+ SDE_ERROR("dbg base register reg_dma failed: %d\n",
+ rc);
+
}
sde_kms->core_client = sde_power_client_create(&priv->phandle, "core");
@@ -1101,6 +1168,8 @@
goto power_error;
}
+ sde_dbg_init_dbg_buses(sde_kms->core_rev);
+
/* Initialize reg dma block which is a singleton */
rc = sde_reg_dma_init(sde_kms->reg_dma, sde_kms->catalog,
sde_kms->dev);
@@ -1159,6 +1228,14 @@
goto power_error;
}
+ rc = sde_core_perf_init(&sde_kms->perf, dev, sde_kms->catalog,
+ &priv->phandle, priv->pclient, "core_clk_src",
+ sde_kms->debugfs_debug);
+ if (rc) {
+ SDE_ERROR("failed to init perf %d\n", rc);
+ goto perf_err;
+ }
+
/*
* _sde_kms_drm_obj_init should create the DRM related objects
* i.e. CRTCs, planes, encoders, connectors and so forth
@@ -1166,7 +1243,7 @@
rc = _sde_kms_drm_obj_init(sde_kms);
if (rc) {
SDE_ERROR("modeset init failed: %d\n", rc);
- goto power_error;
+ goto drm_obj_init_err;
}
dev->mode_config.min_width = 0;
@@ -1197,6 +1274,9 @@
hw_intr_init_err:
_sde_kms_drm_obj_destroy(sde_kms);
+drm_obj_init_err:
+ sde_core_perf_destroy(&sde_kms->perf);
+perf_err:
power_error:
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
error:
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index 0550b19..bf1c12f 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -32,6 +32,7 @@
#include "sde_rm.h"
#include "sde_power_handle.h"
#include "sde_irq.h"
+#include "sde_core_perf.h"
#define DRMID(x) ((x) ? (x)->base.id : -1)
@@ -132,6 +133,7 @@
/* io/register spaces: */
void __iomem *mmio, *vbif[VBIF_MAX], *reg_dma;
+ unsigned long mmio_len, vbif_len[VBIF_MAX], reg_dma_len;
struct regulator *vdd;
struct regulator *mmagic;
@@ -142,6 +144,8 @@
struct sde_hw_intr *hw_intr;
struct sde_irq irq_obj;
+ struct sde_core_perf perf;
+
struct sde_rm rm;
bool rm_init;
diff --git a/drivers/gpu/drm/msm/sde/sde_rm.c b/drivers/gpu/drm/msm/sde/sde_rm.c
index 1d27b27..204a9e5 100644
--- a/drivers/gpu/drm/msm/sde/sde_rm.c
+++ b/drivers/gpu/drm/msm/sde/sde_rm.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1075,11 +1075,13 @@
kfree(rsvp);
- (void) msm_property_set_property(
- sde_connector_get_propinfo(conn),
- sde_connector_get_property_values(conn->state),
- CONNECTOR_PROP_TOPOLOGY_NAME,
- SDE_RM_TOPOLOGY_UNKNOWN);
+ /* if no remaining reservation, then clear the topology name */
+ if (!_sde_rm_get_rsvp(rm, conn->encoder))
+ (void) msm_property_set_property(
+ sde_connector_get_propinfo(conn),
+ sde_connector_get_property_values(conn->state),
+ CONNECTOR_PROP_TOPOLOGY_NAME,
+ SDE_RM_TOPOLOGY_UNKNOWN);
}
void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc)
diff --git a/drivers/gpu/drm/msm/sde/sde_rm.h b/drivers/gpu/drm/msm/sde/sde_rm.h
index 855b12c..4127bc2 100644
--- a/drivers/gpu/drm/msm/sde/sde_rm.h
+++ b/drivers/gpu/drm/msm/sde/sde_rm.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -191,11 +191,4 @@
*/
int sde_rm_check_property_topctl(uint64_t val);
-/**
- * sde_rm_check_property_topctl - validate property bitmask before it is set
- * @val: user's proposed topology control bitmask
- * @Return: 0 on success or error
- */
-int sde_rm_check_property_topctl(uint64_t val);
-
#endif /* __SDE_RM_H__ */
diff --git a/drivers/gpu/drm/msm/sde/sde_trace.h b/drivers/gpu/drm/msm/sde/sde_trace.h
index 862b5c9..2a4e6b5 100644
--- a/drivers/gpu/drm/msm/sde/sde_trace.h
+++ b/drivers/gpu/drm/msm/sde/sde_trace.h
@@ -92,6 +92,38 @@
__entry->vbif_idx)
)
+TRACE_EVENT(sde_perf_update_bus,
+ TP_PROTO(int client, unsigned long long ab_quota,
+ unsigned long long ib_quota),
+ TP_ARGS(client, ab_quota, ib_quota),
+ TP_STRUCT__entry(
+ __field(int, client)
+ __field(u64, ab_quota)
+ __field(u64, ib_quota)
+ ),
+ TP_fast_assign(
+ __entry->client = client;
+ __entry->ab_quota = ab_quota;
+ __entry->ib_quota = ib_quota;
+ ),
+ TP_printk("Request client:%d ab=%llu ib=%llu",
+ __entry->client,
+ __entry->ab_quota,
+ __entry->ib_quota)
+)
+
+
+TRACE_EVENT(sde_cmd_release_bw,
+ TP_PROTO(u32 crtc_id),
+ TP_ARGS(crtc_id),
+ TP_STRUCT__entry(
+ __field(u32, crtc_id)
+ ),
+ TP_fast_assign(
+ __entry->crtc_id = crtc_id;
+ ),
+ TP_printk("crtc:%d", __entry->crtc_id)
+);
TRACE_EVENT(sde_mark_write,
TP_PROTO(int pid, const char *name, bool trace_begin),
diff --git a/drivers/gpu/drm/msm/sde_dbg.c b/drivers/gpu/drm/msm/sde_dbg.c
new file mode 100644
index 0000000..4c7f3d2
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde_dbg.c
@@ -0,0 +1,2978 @@
+/* Copyright (c) 2009-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
+
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/ktime.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/dma-buf.h>
+#include <linux/slab.h>
+
+#include "sde_dbg.h"
+#include "sde/sde_hw_catalog.h"
+
+#define SDE_DBG_BASE_MAX 10
+
+#define DEFAULT_PANIC 1
+#define DEFAULT_REGDUMP SDE_DBG_DUMP_IN_MEM
+#define DEFAULT_DBGBUS_SDE SDE_DBG_DUMP_IN_MEM
+#define DEFAULT_DBGBUS_VBIFRT SDE_DBG_DUMP_IN_MEM
+#define DEFAULT_BASE_REG_CNT 0x100
+#define GROUP_BYTES 4
+#define ROW_BYTES 16
+#define RANGE_NAME_LEN 40
+#define REG_BASE_NAME_LEN 80
+
+#define DBGBUS_FLAGS_DSPP BIT(0)
+#define DBGBUS_DSPP_STATUS 0x34C
+
+#define DBGBUS_NAME_SDE "sde"
+#define DBGBUS_NAME_VBIF_RT "vbif_rt"
+
+/* offsets from sde_base address for the debug buses */
+#define DBGBUS_SSPP0 0x188
+#define DBGBUS_SSPP1 0x298
+#define DBGBUS_DSPP 0x348
+#define DBGBUS_PERIPH 0x418
+
+#define TEST_MASK(id, tp) ((id << 4) | (tp << 1) | BIT(0))
+
+/* following offsets are with respect to MDP VBIF base for DBG BUS access */
+#define MMSS_VBIF_CLKON 0x4
+#define MMSS_VBIF_TEST_BUS_OUT_CTRL 0x210
+#define MMSS_VBIF_TEST_BUS_OUT 0x230
+
+/**
+ * struct sde_dbg_reg_offset - tracking for start and end of region
+ * @start: start offset
+ * @start: end offset
+ */
+struct sde_dbg_reg_offset {
+ u32 start;
+ u32 end;
+};
+
+/**
+ * struct sde_dbg_reg_range - register dumping named sub-range
+ * @head: head of this node
+ * @reg_dump: address for the mem dump
+ * @range_name: name of this range
+ * @offset: offsets for range to dump
+ * @xin_id: client xin id
+ */
+struct sde_dbg_reg_range {
+ struct list_head head;
+ u32 *reg_dump;
+ char range_name[RANGE_NAME_LEN];
+ struct sde_dbg_reg_offset offset;
+ uint32_t xin_id;
+};
+
+/**
+ * struct sde_dbg_reg_base - register region base.
+ * may sub-ranges: sub-ranges are used for dumping
+ * or may not have sub-ranges: dumping is base -> max_offset
+ * @reg_base_head: head of this node
+ * @sub_range_list: head to the list with dump ranges
+ * @name: register base name
+ * @base: base pointer
+ * @off: cached offset of region for manual register dumping
+ * @cnt: cached range of region for manual register dumping
+ * @max_offset: length of region
+ * @buf: buffer used for manual register dumping
+ * @buf_len: buffer length used for manual register dumping
+ * @reg_dump: address for the mem dump if no ranges used
+ */
+struct sde_dbg_reg_base {
+ struct list_head reg_base_head;
+ struct list_head sub_range_list;
+ char name[REG_BASE_NAME_LEN];
+ void __iomem *base;
+ size_t off;
+ size_t cnt;
+ size_t max_offset;
+ char *buf;
+ size_t buf_len;
+ u32 *reg_dump;
+};
+
+struct sde_debug_bus_entry {
+ u32 wr_addr;
+ u32 block_id;
+ u32 test_id;
+};
+
+struct vbif_debug_bus_entry {
+ u32 disable_bus_addr;
+ u32 block_bus_addr;
+ u32 bit_offset;
+ u32 block_cnt;
+ u32 test_pnt_start;
+ u32 test_pnt_cnt;
+};
+
+struct sde_dbg_debug_bus_common {
+ char *name;
+ u32 enable_mask;
+ bool include_in_deferred_work;
+ u32 flags;
+ u32 entries_size;
+ u32 *dumped_content;
+};
+
+struct sde_dbg_sde_debug_bus {
+ struct sde_dbg_debug_bus_common cmn;
+ struct sde_debug_bus_entry *entries;
+};
+
+struct sde_dbg_vbif_debug_bus {
+ struct sde_dbg_debug_bus_common cmn;
+ struct vbif_debug_bus_entry *entries;
+};
+
+/**
+ * struct sde_dbg_base - global sde debug base structure
+ * @evtlog: event log instance
+ * @reg_base_list: list of register dumping regions
+ * @root: base debugfs root
+ * @dev: device pointer
+ * @power_ctrl: callback structure for enabling power for reading hw registers
+ * @req_dump_blks: list of blocks requested for dumping
+ * @panic_on_err: whether to kernel panic after triggering dump via debugfs
+ * @dump_work: work struct for deferring register dump work to separate thread
+ * @work_panic: panic after dump if internal user passed "panic" special region
+ * @enable_reg_dump: whether to dump registers into memory, kernel log, or both
+ * @dbgbus_sde: debug bus structure for the sde
+ * @dbgbus_vbif_rt: debug bus structure for the realtime vbif
+ */
+static struct sde_dbg_base {
+ struct sde_dbg_evtlog *evtlog;
+ struct list_head reg_base_list;
+ struct dentry *root;
+ struct device *dev;
+ struct sde_dbg_power_ctrl power_ctrl;
+
+ struct sde_dbg_reg_base *req_dump_blks[SDE_DBG_BASE_MAX];
+
+ u32 panic_on_err;
+ struct work_struct dump_work;
+ bool work_panic;
+ u32 enable_reg_dump;
+
+ struct sde_dbg_sde_debug_bus dbgbus_sde;
+ struct sde_dbg_vbif_debug_bus dbgbus_vbif_rt;
+} sde_dbg_base;
+
+/* sde_dbg_base_evtlog - global pointer to main sde event log for macro use */
+struct sde_dbg_evtlog *sde_dbg_base_evtlog;
+
+static struct sde_debug_bus_entry dbg_bus_sde_8998[] = {
+
+ /* Unpack 0 sspp 0*/
+ { DBGBUS_SSPP0, 50, 2 },
+ { DBGBUS_SSPP0, 60, 2 },
+ { DBGBUS_SSPP0, 70, 2 },
+ { DBGBUS_SSPP0, 85, 2 },
+
+ /* Upack 0 sspp 1*/
+ { DBGBUS_SSPP1, 50, 2 },
+ { DBGBUS_SSPP1, 60, 2 },
+ { DBGBUS_SSPP1, 70, 2 },
+ { DBGBUS_SSPP1, 85, 2 },
+
+ /* scheduler */
+ { DBGBUS_DSPP, 130, 0 },
+ { DBGBUS_DSPP, 130, 1 },
+ { DBGBUS_DSPP, 130, 2 },
+ { DBGBUS_DSPP, 130, 3 },
+ { DBGBUS_DSPP, 130, 4 },
+ { DBGBUS_DSPP, 130, 5 },
+
+ /* qseed */
+ { DBGBUS_SSPP0, 6, 0},
+ { DBGBUS_SSPP0, 6, 1},
+ { DBGBUS_SSPP0, 26, 0},
+ { DBGBUS_SSPP0, 26, 1},
+ { DBGBUS_SSPP1, 6, 0},
+ { DBGBUS_SSPP1, 6, 1},
+ { DBGBUS_SSPP1, 26, 0},
+ { DBGBUS_SSPP1, 26, 1},
+
+ /* scale */
+ { DBGBUS_SSPP0, 16, 0},
+ { DBGBUS_SSPP0, 16, 1},
+ { DBGBUS_SSPP0, 36, 0},
+ { DBGBUS_SSPP0, 36, 1},
+ { DBGBUS_SSPP1, 16, 0},
+ { DBGBUS_SSPP1, 16, 1},
+ { DBGBUS_SSPP1, 36, 0},
+ { DBGBUS_SSPP1, 36, 1},
+
+ /* fetch sspp0 */
+
+ /* vig 0 */
+ { DBGBUS_SSPP0, 0, 0 },
+ { DBGBUS_SSPP0, 0, 1 },
+ { DBGBUS_SSPP0, 0, 2 },
+ { DBGBUS_SSPP0, 0, 3 },
+ { DBGBUS_SSPP0, 0, 4 },
+ { DBGBUS_SSPP0, 0, 5 },
+ { DBGBUS_SSPP0, 0, 6 },
+ { DBGBUS_SSPP0, 0, 7 },
+
+ { DBGBUS_SSPP0, 1, 0 },
+ { DBGBUS_SSPP0, 1, 1 },
+ { DBGBUS_SSPP0, 1, 2 },
+ { DBGBUS_SSPP0, 1, 3 },
+ { DBGBUS_SSPP0, 1, 4 },
+ { DBGBUS_SSPP0, 1, 5 },
+ { DBGBUS_SSPP0, 1, 6 },
+ { DBGBUS_SSPP0, 1, 7 },
+
+ { DBGBUS_SSPP0, 2, 0 },
+ { DBGBUS_SSPP0, 2, 1 },
+ { DBGBUS_SSPP0, 2, 2 },
+ { DBGBUS_SSPP0, 2, 3 },
+ { DBGBUS_SSPP0, 2, 4 },
+ { DBGBUS_SSPP0, 2, 5 },
+ { DBGBUS_SSPP0, 2, 6 },
+ { DBGBUS_SSPP0, 2, 7 },
+
+ { DBGBUS_SSPP0, 4, 0 },
+ { DBGBUS_SSPP0, 4, 1 },
+ { DBGBUS_SSPP0, 4, 2 },
+ { DBGBUS_SSPP0, 4, 3 },
+ { DBGBUS_SSPP0, 4, 4 },
+ { DBGBUS_SSPP0, 4, 5 },
+ { DBGBUS_SSPP0, 4, 6 },
+ { DBGBUS_SSPP0, 4, 7 },
+
+ { DBGBUS_SSPP0, 5, 0 },
+ { DBGBUS_SSPP0, 5, 1 },
+ { DBGBUS_SSPP0, 5, 2 },
+ { DBGBUS_SSPP0, 5, 3 },
+ { DBGBUS_SSPP0, 5, 4 },
+ { DBGBUS_SSPP0, 5, 5 },
+ { DBGBUS_SSPP0, 5, 6 },
+ { DBGBUS_SSPP0, 5, 7 },
+
+ /* vig 2 */
+ { DBGBUS_SSPP0, 20, 0 },
+ { DBGBUS_SSPP0, 20, 1 },
+ { DBGBUS_SSPP0, 20, 2 },
+ { DBGBUS_SSPP0, 20, 3 },
+ { DBGBUS_SSPP0, 20, 4 },
+ { DBGBUS_SSPP0, 20, 5 },
+ { DBGBUS_SSPP0, 20, 6 },
+ { DBGBUS_SSPP0, 20, 7 },
+
+ { DBGBUS_SSPP0, 21, 0 },
+ { DBGBUS_SSPP0, 21, 1 },
+ { DBGBUS_SSPP0, 21, 2 },
+ { DBGBUS_SSPP0, 21, 3 },
+ { DBGBUS_SSPP0, 21, 4 },
+ { DBGBUS_SSPP0, 21, 5 },
+ { DBGBUS_SSPP0, 21, 6 },
+ { DBGBUS_SSPP0, 21, 7 },
+
+ { DBGBUS_SSPP0, 22, 0 },
+ { DBGBUS_SSPP0, 22, 1 },
+ { DBGBUS_SSPP0, 22, 2 },
+ { DBGBUS_SSPP0, 22, 3 },
+ { DBGBUS_SSPP0, 22, 4 },
+ { DBGBUS_SSPP0, 22, 5 },
+ { DBGBUS_SSPP0, 22, 6 },
+ { DBGBUS_SSPP0, 22, 7 },
+
+ { DBGBUS_SSPP0, 24, 0 },
+ { DBGBUS_SSPP0, 24, 1 },
+ { DBGBUS_SSPP0, 24, 2 },
+ { DBGBUS_SSPP0, 24, 3 },
+ { DBGBUS_SSPP0, 24, 4 },
+ { DBGBUS_SSPP0, 24, 5 },
+ { DBGBUS_SSPP0, 24, 6 },
+ { DBGBUS_SSPP0, 24, 7 },
+
+ { DBGBUS_SSPP0, 25, 0 },
+ { DBGBUS_SSPP0, 25, 1 },
+ { DBGBUS_SSPP0, 25, 2 },
+ { DBGBUS_SSPP0, 25, 3 },
+ { DBGBUS_SSPP0, 25, 4 },
+ { DBGBUS_SSPP0, 25, 5 },
+ { DBGBUS_SSPP0, 25, 6 },
+ { DBGBUS_SSPP0, 25, 7 },
+
+ /* dma 2 */
+ { DBGBUS_SSPP0, 30, 0 },
+ { DBGBUS_SSPP0, 30, 1 },
+ { DBGBUS_SSPP0, 30, 2 },
+ { DBGBUS_SSPP0, 30, 3 },
+ { DBGBUS_SSPP0, 30, 4 },
+ { DBGBUS_SSPP0, 30, 5 },
+ { DBGBUS_SSPP0, 30, 6 },
+ { DBGBUS_SSPP0, 30, 7 },
+
+ { DBGBUS_SSPP0, 31, 0 },
+ { DBGBUS_SSPP0, 31, 1 },
+ { DBGBUS_SSPP0, 31, 2 },
+ { DBGBUS_SSPP0, 31, 3 },
+ { DBGBUS_SSPP0, 31, 4 },
+ { DBGBUS_SSPP0, 31, 5 },
+ { DBGBUS_SSPP0, 31, 6 },
+ { DBGBUS_SSPP0, 31, 7 },
+
+ { DBGBUS_SSPP0, 32, 0 },
+ { DBGBUS_SSPP0, 32, 1 },
+ { DBGBUS_SSPP0, 32, 2 },
+ { DBGBUS_SSPP0, 32, 3 },
+ { DBGBUS_SSPP0, 32, 4 },
+ { DBGBUS_SSPP0, 32, 5 },
+ { DBGBUS_SSPP0, 32, 6 },
+ { DBGBUS_SSPP0, 32, 7 },
+
+ { DBGBUS_SSPP0, 33, 0 },
+ { DBGBUS_SSPP0, 33, 1 },
+ { DBGBUS_SSPP0, 33, 2 },
+ { DBGBUS_SSPP0, 33, 3 },
+ { DBGBUS_SSPP0, 33, 4 },
+ { DBGBUS_SSPP0, 33, 5 },
+ { DBGBUS_SSPP0, 33, 6 },
+ { DBGBUS_SSPP0, 33, 7 },
+
+ { DBGBUS_SSPP0, 34, 0 },
+ { DBGBUS_SSPP0, 34, 1 },
+ { DBGBUS_SSPP0, 34, 2 },
+ { DBGBUS_SSPP0, 34, 3 },
+ { DBGBUS_SSPP0, 34, 4 },
+ { DBGBUS_SSPP0, 34, 5 },
+ { DBGBUS_SSPP0, 34, 6 },
+ { DBGBUS_SSPP0, 34, 7 },
+
+ { DBGBUS_SSPP0, 35, 0 },
+ { DBGBUS_SSPP0, 35, 1 },
+ { DBGBUS_SSPP0, 35, 2 },
+ { DBGBUS_SSPP0, 35, 3 },
+
+ /* dma 0 */
+ { DBGBUS_SSPP0, 40, 0 },
+ { DBGBUS_SSPP0, 40, 1 },
+ { DBGBUS_SSPP0, 40, 2 },
+ { DBGBUS_SSPP0, 40, 3 },
+ { DBGBUS_SSPP0, 40, 4 },
+ { DBGBUS_SSPP0, 40, 5 },
+ { DBGBUS_SSPP0, 40, 6 },
+ { DBGBUS_SSPP0, 40, 7 },
+
+ { DBGBUS_SSPP0, 41, 0 },
+ { DBGBUS_SSPP0, 41, 1 },
+ { DBGBUS_SSPP0, 41, 2 },
+ { DBGBUS_SSPP0, 41, 3 },
+ { DBGBUS_SSPP0, 41, 4 },
+ { DBGBUS_SSPP0, 41, 5 },
+ { DBGBUS_SSPP0, 41, 6 },
+ { DBGBUS_SSPP0, 41, 7 },
+
+ { DBGBUS_SSPP0, 42, 0 },
+ { DBGBUS_SSPP0, 42, 1 },
+ { DBGBUS_SSPP0, 42, 2 },
+ { DBGBUS_SSPP0, 42, 3 },
+ { DBGBUS_SSPP0, 42, 4 },
+ { DBGBUS_SSPP0, 42, 5 },
+ { DBGBUS_SSPP0, 42, 6 },
+ { DBGBUS_SSPP0, 42, 7 },
+
+ { DBGBUS_SSPP0, 44, 0 },
+ { DBGBUS_SSPP0, 44, 1 },
+ { DBGBUS_SSPP0, 44, 2 },
+ { DBGBUS_SSPP0, 44, 3 },
+ { DBGBUS_SSPP0, 44, 4 },
+ { DBGBUS_SSPP0, 44, 5 },
+ { DBGBUS_SSPP0, 44, 6 },
+ { DBGBUS_SSPP0, 44, 7 },
+
+ { DBGBUS_SSPP0, 45, 0 },
+ { DBGBUS_SSPP0, 45, 1 },
+ { DBGBUS_SSPP0, 45, 2 },
+ { DBGBUS_SSPP0, 45, 3 },
+ { DBGBUS_SSPP0, 45, 4 },
+ { DBGBUS_SSPP0, 45, 5 },
+ { DBGBUS_SSPP0, 45, 6 },
+ { DBGBUS_SSPP0, 45, 7 },
+
+ /* fetch sspp1 */
+ /* vig 1 */
+ { DBGBUS_SSPP1, 0, 0 },
+ { DBGBUS_SSPP1, 0, 1 },
+ { DBGBUS_SSPP1, 0, 2 },
+ { DBGBUS_SSPP1, 0, 3 },
+ { DBGBUS_SSPP1, 0, 4 },
+ { DBGBUS_SSPP1, 0, 5 },
+ { DBGBUS_SSPP1, 0, 6 },
+ { DBGBUS_SSPP1, 0, 7 },
+
+ { DBGBUS_SSPP1, 1, 0 },
+ { DBGBUS_SSPP1, 1, 1 },
+ { DBGBUS_SSPP1, 1, 2 },
+ { DBGBUS_SSPP1, 1, 3 },
+ { DBGBUS_SSPP1, 1, 4 },
+ { DBGBUS_SSPP1, 1, 5 },
+ { DBGBUS_SSPP1, 1, 6 },
+ { DBGBUS_SSPP1, 1, 7 },
+
+ { DBGBUS_SSPP1, 2, 0 },
+ { DBGBUS_SSPP1, 2, 1 },
+ { DBGBUS_SSPP1, 2, 2 },
+ { DBGBUS_SSPP1, 2, 3 },
+ { DBGBUS_SSPP1, 2, 4 },
+ { DBGBUS_SSPP1, 2, 5 },
+ { DBGBUS_SSPP1, 2, 6 },
+ { DBGBUS_SSPP1, 2, 7 },
+
+ { DBGBUS_SSPP1, 4, 0 },
+ { DBGBUS_SSPP1, 4, 1 },
+ { DBGBUS_SSPP1, 4, 2 },
+ { DBGBUS_SSPP1, 4, 3 },
+ { DBGBUS_SSPP1, 4, 4 },
+ { DBGBUS_SSPP1, 4, 5 },
+ { DBGBUS_SSPP1, 4, 6 },
+ { DBGBUS_SSPP1, 4, 7 },
+
+ { DBGBUS_SSPP1, 5, 0 },
+ { DBGBUS_SSPP1, 5, 1 },
+ { DBGBUS_SSPP1, 5, 2 },
+ { DBGBUS_SSPP1, 5, 3 },
+ { DBGBUS_SSPP1, 5, 4 },
+ { DBGBUS_SSPP1, 5, 5 },
+ { DBGBUS_SSPP1, 5, 6 },
+ { DBGBUS_SSPP1, 5, 7 },
+
+ /* vig 3 */
+ { DBGBUS_SSPP1, 20, 0 },
+ { DBGBUS_SSPP1, 20, 1 },
+ { DBGBUS_SSPP1, 20, 2 },
+ { DBGBUS_SSPP1, 20, 3 },
+ { DBGBUS_SSPP1, 20, 4 },
+ { DBGBUS_SSPP1, 20, 5 },
+ { DBGBUS_SSPP1, 20, 6 },
+ { DBGBUS_SSPP1, 20, 7 },
+
+ { DBGBUS_SSPP1, 21, 0 },
+ { DBGBUS_SSPP1, 21, 1 },
+ { DBGBUS_SSPP1, 21, 2 },
+ { DBGBUS_SSPP1, 21, 3 },
+ { DBGBUS_SSPP1, 21, 4 },
+ { DBGBUS_SSPP1, 21, 5 },
+ { DBGBUS_SSPP1, 21, 6 },
+ { DBGBUS_SSPP1, 21, 7 },
+
+ { DBGBUS_SSPP1, 22, 0 },
+ { DBGBUS_SSPP1, 22, 1 },
+ { DBGBUS_SSPP1, 22, 2 },
+ { DBGBUS_SSPP1, 22, 3 },
+ { DBGBUS_SSPP1, 22, 4 },
+ { DBGBUS_SSPP1, 22, 5 },
+ { DBGBUS_SSPP1, 22, 6 },
+ { DBGBUS_SSPP1, 22, 7 },
+
+ { DBGBUS_SSPP1, 24, 0 },
+ { DBGBUS_SSPP1, 24, 1 },
+ { DBGBUS_SSPP1, 24, 2 },
+ { DBGBUS_SSPP1, 24, 3 },
+ { DBGBUS_SSPP1, 24, 4 },
+ { DBGBUS_SSPP1, 24, 5 },
+ { DBGBUS_SSPP1, 24, 6 },
+ { DBGBUS_SSPP1, 24, 7 },
+
+ { DBGBUS_SSPP1, 25, 0 },
+ { DBGBUS_SSPP1, 25, 1 },
+ { DBGBUS_SSPP1, 25, 2 },
+ { DBGBUS_SSPP1, 25, 3 },
+ { DBGBUS_SSPP1, 25, 4 },
+ { DBGBUS_SSPP1, 25, 5 },
+ { DBGBUS_SSPP1, 25, 6 },
+ { DBGBUS_SSPP1, 25, 7 },
+
+ /* dma 3 */
+ { DBGBUS_SSPP1, 30, 0 },
+ { DBGBUS_SSPP1, 30, 1 },
+ { DBGBUS_SSPP1, 30, 2 },
+ { DBGBUS_SSPP1, 30, 3 },
+ { DBGBUS_SSPP1, 30, 4 },
+ { DBGBUS_SSPP1, 30, 5 },
+ { DBGBUS_SSPP1, 30, 6 },
+ { DBGBUS_SSPP1, 30, 7 },
+
+ { DBGBUS_SSPP1, 31, 0 },
+ { DBGBUS_SSPP1, 31, 1 },
+ { DBGBUS_SSPP1, 31, 2 },
+ { DBGBUS_SSPP1, 31, 3 },
+ { DBGBUS_SSPP1, 31, 4 },
+ { DBGBUS_SSPP1, 31, 5 },
+ { DBGBUS_SSPP1, 31, 6 },
+ { DBGBUS_SSPP1, 31, 7 },
+
+ { DBGBUS_SSPP1, 32, 0 },
+ { DBGBUS_SSPP1, 32, 1 },
+ { DBGBUS_SSPP1, 32, 2 },
+ { DBGBUS_SSPP1, 32, 3 },
+ { DBGBUS_SSPP1, 32, 4 },
+ { DBGBUS_SSPP1, 32, 5 },
+ { DBGBUS_SSPP1, 32, 6 },
+ { DBGBUS_SSPP1, 32, 7 },
+
+ { DBGBUS_SSPP1, 33, 0 },
+ { DBGBUS_SSPP1, 33, 1 },
+ { DBGBUS_SSPP1, 33, 2 },
+ { DBGBUS_SSPP1, 33, 3 },
+ { DBGBUS_SSPP1, 33, 4 },
+ { DBGBUS_SSPP1, 33, 5 },
+ { DBGBUS_SSPP1, 33, 6 },
+ { DBGBUS_SSPP1, 33, 7 },
+
+ { DBGBUS_SSPP1, 34, 0 },
+ { DBGBUS_SSPP1, 34, 1 },
+ { DBGBUS_SSPP1, 34, 2 },
+ { DBGBUS_SSPP1, 34, 3 },
+ { DBGBUS_SSPP1, 34, 4 },
+ { DBGBUS_SSPP1, 34, 5 },
+ { DBGBUS_SSPP1, 34, 6 },
+ { DBGBUS_SSPP1, 34, 7 },
+
+ { DBGBUS_SSPP1, 35, 0 },
+ { DBGBUS_SSPP1, 35, 1 },
+ { DBGBUS_SSPP1, 35, 2 },
+
+ /* dma 1 */
+ { DBGBUS_SSPP1, 40, 0 },
+ { DBGBUS_SSPP1, 40, 1 },
+ { DBGBUS_SSPP1, 40, 2 },
+ { DBGBUS_SSPP1, 40, 3 },
+ { DBGBUS_SSPP1, 40, 4 },
+ { DBGBUS_SSPP1, 40, 5 },
+ { DBGBUS_SSPP1, 40, 6 },
+ { DBGBUS_SSPP1, 40, 7 },
+
+ { DBGBUS_SSPP1, 41, 0 },
+ { DBGBUS_SSPP1, 41, 1 },
+ { DBGBUS_SSPP1, 41, 2 },
+ { DBGBUS_SSPP1, 41, 3 },
+ { DBGBUS_SSPP1, 41, 4 },
+ { DBGBUS_SSPP1, 41, 5 },
+ { DBGBUS_SSPP1, 41, 6 },
+ { DBGBUS_SSPP1, 41, 7 },
+
+ { DBGBUS_SSPP1, 42, 0 },
+ { DBGBUS_SSPP1, 42, 1 },
+ { DBGBUS_SSPP1, 42, 2 },
+ { DBGBUS_SSPP1, 42, 3 },
+ { DBGBUS_SSPP1, 42, 4 },
+ { DBGBUS_SSPP1, 42, 5 },
+ { DBGBUS_SSPP1, 42, 6 },
+ { DBGBUS_SSPP1, 42, 7 },
+
+ { DBGBUS_SSPP1, 44, 0 },
+ { DBGBUS_SSPP1, 44, 1 },
+ { DBGBUS_SSPP1, 44, 2 },
+ { DBGBUS_SSPP1, 44, 3 },
+ { DBGBUS_SSPP1, 44, 4 },
+ { DBGBUS_SSPP1, 44, 5 },
+ { DBGBUS_SSPP1, 44, 6 },
+ { DBGBUS_SSPP1, 44, 7 },
+
+ { DBGBUS_SSPP1, 45, 0 },
+ { DBGBUS_SSPP1, 45, 1 },
+ { DBGBUS_SSPP1, 45, 2 },
+ { DBGBUS_SSPP1, 45, 3 },
+ { DBGBUS_SSPP1, 45, 4 },
+ { DBGBUS_SSPP1, 45, 5 },
+ { DBGBUS_SSPP1, 45, 6 },
+ { DBGBUS_SSPP1, 45, 7 },
+
+ /* cursor 1 */
+ { DBGBUS_SSPP1, 80, 0 },
+ { DBGBUS_SSPP1, 80, 1 },
+ { DBGBUS_SSPP1, 80, 2 },
+ { DBGBUS_SSPP1, 80, 3 },
+ { DBGBUS_SSPP1, 80, 4 },
+ { DBGBUS_SSPP1, 80, 5 },
+ { DBGBUS_SSPP1, 80, 6 },
+ { DBGBUS_SSPP1, 80, 7 },
+
+ { DBGBUS_SSPP1, 81, 0 },
+ { DBGBUS_SSPP1, 81, 1 },
+ { DBGBUS_SSPP1, 81, 2 },
+ { DBGBUS_SSPP1, 81, 3 },
+ { DBGBUS_SSPP1, 81, 4 },
+ { DBGBUS_SSPP1, 81, 5 },
+ { DBGBUS_SSPP1, 81, 6 },
+ { DBGBUS_SSPP1, 81, 7 },
+
+ { DBGBUS_SSPP1, 82, 0 },
+ { DBGBUS_SSPP1, 82, 1 },
+ { DBGBUS_SSPP1, 82, 2 },
+ { DBGBUS_SSPP1, 82, 3 },
+ { DBGBUS_SSPP1, 82, 4 },
+ { DBGBUS_SSPP1, 82, 5 },
+ { DBGBUS_SSPP1, 82, 6 },
+ { DBGBUS_SSPP1, 82, 7 },
+
+ { DBGBUS_SSPP1, 83, 0 },
+ { DBGBUS_SSPP1, 83, 1 },
+ { DBGBUS_SSPP1, 83, 2 },
+ { DBGBUS_SSPP1, 83, 3 },
+ { DBGBUS_SSPP1, 83, 4 },
+ { DBGBUS_SSPP1, 83, 5 },
+ { DBGBUS_SSPP1, 83, 6 },
+ { DBGBUS_SSPP1, 83, 7 },
+
+ { DBGBUS_SSPP1, 84, 0 },
+ { DBGBUS_SSPP1, 84, 1 },
+ { DBGBUS_SSPP1, 84, 2 },
+ { DBGBUS_SSPP1, 84, 3 },
+ { DBGBUS_SSPP1, 84, 4 },
+ { DBGBUS_SSPP1, 84, 5 },
+ { DBGBUS_SSPP1, 84, 6 },
+ { DBGBUS_SSPP1, 84, 7 },
+
+ /* dspp */
+ { DBGBUS_DSPP, 13, 0 },
+ { DBGBUS_DSPP, 19, 0 },
+ { DBGBUS_DSPP, 14, 0 },
+ { DBGBUS_DSPP, 14, 1 },
+ { DBGBUS_DSPP, 14, 3 },
+ { DBGBUS_DSPP, 20, 0 },
+ { DBGBUS_DSPP, 20, 1 },
+ { DBGBUS_DSPP, 20, 3 },
+
+ /* ppb_0 */
+ { DBGBUS_DSPP, 31, 0 },
+ { DBGBUS_DSPP, 33, 0 },
+ { DBGBUS_DSPP, 35, 0 },
+ { DBGBUS_DSPP, 42, 0 },
+
+ /* ppb_1 */
+ { DBGBUS_DSPP, 32, 0 },
+ { DBGBUS_DSPP, 34, 0 },
+ { DBGBUS_DSPP, 36, 0 },
+ { DBGBUS_DSPP, 43, 0 },
+
+ /* lm_lut */
+ { DBGBUS_DSPP, 109, 0 },
+ { DBGBUS_DSPP, 105, 0 },
+ { DBGBUS_DSPP, 103, 0 },
+
+ /* tear-check */
+ { DBGBUS_PERIPH, 63, 0 },
+ { DBGBUS_PERIPH, 64, 0 },
+ { DBGBUS_PERIPH, 65, 0 },
+ { DBGBUS_PERIPH, 73, 0 },
+ { DBGBUS_PERIPH, 74, 0 },
+
+ /* crossbar */
+ { DBGBUS_DSPP, 0, 0},
+
+ /* rotator */
+ { DBGBUS_DSPP, 9, 0},
+
+ /* blend */
+ /* LM0 */
+ { DBGBUS_DSPP, 63, 0},
+ { DBGBUS_DSPP, 63, 1},
+ { DBGBUS_DSPP, 63, 2},
+ { DBGBUS_DSPP, 63, 3},
+ { DBGBUS_DSPP, 63, 4},
+ { DBGBUS_DSPP, 63, 5},
+ { DBGBUS_DSPP, 63, 6},
+ { DBGBUS_DSPP, 63, 7},
+
+ { DBGBUS_DSPP, 64, 0},
+ { DBGBUS_DSPP, 64, 1},
+ { DBGBUS_DSPP, 64, 2},
+ { DBGBUS_DSPP, 64, 3},
+ { DBGBUS_DSPP, 64, 4},
+ { DBGBUS_DSPP, 64, 5},
+ { DBGBUS_DSPP, 64, 6},
+ { DBGBUS_DSPP, 64, 7},
+
+ { DBGBUS_DSPP, 65, 0},
+ { DBGBUS_DSPP, 65, 1},
+ { DBGBUS_DSPP, 65, 2},
+ { DBGBUS_DSPP, 65, 3},
+ { DBGBUS_DSPP, 65, 4},
+ { DBGBUS_DSPP, 65, 5},
+ { DBGBUS_DSPP, 65, 6},
+ { DBGBUS_DSPP, 65, 7},
+
+ { DBGBUS_DSPP, 66, 0},
+ { DBGBUS_DSPP, 66, 1},
+ { DBGBUS_DSPP, 66, 2},
+ { DBGBUS_DSPP, 66, 3},
+ { DBGBUS_DSPP, 66, 4},
+ { DBGBUS_DSPP, 66, 5},
+ { DBGBUS_DSPP, 66, 6},
+ { DBGBUS_DSPP, 66, 7},
+
+ { DBGBUS_DSPP, 67, 0},
+ { DBGBUS_DSPP, 67, 1},
+ { DBGBUS_DSPP, 67, 2},
+ { DBGBUS_DSPP, 67, 3},
+ { DBGBUS_DSPP, 67, 4},
+ { DBGBUS_DSPP, 67, 5},
+ { DBGBUS_DSPP, 67, 6},
+ { DBGBUS_DSPP, 67, 7},
+
+ { DBGBUS_DSPP, 68, 0},
+ { DBGBUS_DSPP, 68, 1},
+ { DBGBUS_DSPP, 68, 2},
+ { DBGBUS_DSPP, 68, 3},
+ { DBGBUS_DSPP, 68, 4},
+ { DBGBUS_DSPP, 68, 5},
+ { DBGBUS_DSPP, 68, 6},
+ { DBGBUS_DSPP, 68, 7},
+
+ { DBGBUS_DSPP, 69, 0},
+ { DBGBUS_DSPP, 69, 1},
+ { DBGBUS_DSPP, 69, 2},
+ { DBGBUS_DSPP, 69, 3},
+ { DBGBUS_DSPP, 69, 4},
+ { DBGBUS_DSPP, 69, 5},
+ { DBGBUS_DSPP, 69, 6},
+ { DBGBUS_DSPP, 69, 7},
+
+ /* LM1 */
+ { DBGBUS_DSPP, 70, 0},
+ { DBGBUS_DSPP, 70, 1},
+ { DBGBUS_DSPP, 70, 2},
+ { DBGBUS_DSPP, 70, 3},
+ { DBGBUS_DSPP, 70, 4},
+ { DBGBUS_DSPP, 70, 5},
+ { DBGBUS_DSPP, 70, 6},
+ { DBGBUS_DSPP, 70, 7},
+
+ { DBGBUS_DSPP, 71, 0},
+ { DBGBUS_DSPP, 71, 1},
+ { DBGBUS_DSPP, 71, 2},
+ { DBGBUS_DSPP, 71, 3},
+ { DBGBUS_DSPP, 71, 4},
+ { DBGBUS_DSPP, 71, 5},
+ { DBGBUS_DSPP, 71, 6},
+ { DBGBUS_DSPP, 71, 7},
+
+ { DBGBUS_DSPP, 72, 0},
+ { DBGBUS_DSPP, 72, 1},
+ { DBGBUS_DSPP, 72, 2},
+ { DBGBUS_DSPP, 72, 3},
+ { DBGBUS_DSPP, 72, 4},
+ { DBGBUS_DSPP, 72, 5},
+ { DBGBUS_DSPP, 72, 6},
+ { DBGBUS_DSPP, 72, 7},
+
+ { DBGBUS_DSPP, 73, 0},
+ { DBGBUS_DSPP, 73, 1},
+ { DBGBUS_DSPP, 73, 2},
+ { DBGBUS_DSPP, 73, 3},
+ { DBGBUS_DSPP, 73, 4},
+ { DBGBUS_DSPP, 73, 5},
+ { DBGBUS_DSPP, 73, 6},
+ { DBGBUS_DSPP, 73, 7},
+
+ { DBGBUS_DSPP, 74, 0},
+ { DBGBUS_DSPP, 74, 1},
+ { DBGBUS_DSPP, 74, 2},
+ { DBGBUS_DSPP, 74, 3},
+ { DBGBUS_DSPP, 74, 4},
+ { DBGBUS_DSPP, 74, 5},
+ { DBGBUS_DSPP, 74, 6},
+ { DBGBUS_DSPP, 74, 7},
+
+ { DBGBUS_DSPP, 75, 0},
+ { DBGBUS_DSPP, 75, 1},
+ { DBGBUS_DSPP, 75, 2},
+ { DBGBUS_DSPP, 75, 3},
+ { DBGBUS_DSPP, 75, 4},
+ { DBGBUS_DSPP, 75, 5},
+ { DBGBUS_DSPP, 75, 6},
+ { DBGBUS_DSPP, 75, 7},
+
+ { DBGBUS_DSPP, 76, 0},
+ { DBGBUS_DSPP, 76, 1},
+ { DBGBUS_DSPP, 76, 2},
+ { DBGBUS_DSPP, 76, 3},
+ { DBGBUS_DSPP, 76, 4},
+ { DBGBUS_DSPP, 76, 5},
+ { DBGBUS_DSPP, 76, 6},
+ { DBGBUS_DSPP, 76, 7},
+
+ /* LM2 */
+ { DBGBUS_DSPP, 77, 0},
+ { DBGBUS_DSPP, 77, 1},
+ { DBGBUS_DSPP, 77, 2},
+ { DBGBUS_DSPP, 77, 3},
+ { DBGBUS_DSPP, 77, 4},
+ { DBGBUS_DSPP, 77, 5},
+ { DBGBUS_DSPP, 77, 6},
+ { DBGBUS_DSPP, 77, 7},
+
+ { DBGBUS_DSPP, 78, 0},
+ { DBGBUS_DSPP, 78, 1},
+ { DBGBUS_DSPP, 78, 2},
+ { DBGBUS_DSPP, 78, 3},
+ { DBGBUS_DSPP, 78, 4},
+ { DBGBUS_DSPP, 78, 5},
+ { DBGBUS_DSPP, 78, 6},
+ { DBGBUS_DSPP, 78, 7},
+
+ { DBGBUS_DSPP, 79, 0},
+ { DBGBUS_DSPP, 79, 1},
+ { DBGBUS_DSPP, 79, 2},
+ { DBGBUS_DSPP, 79, 3},
+ { DBGBUS_DSPP, 79, 4},
+ { DBGBUS_DSPP, 79, 5},
+ { DBGBUS_DSPP, 79, 6},
+ { DBGBUS_DSPP, 79, 7},
+
+ { DBGBUS_DSPP, 80, 0},
+ { DBGBUS_DSPP, 80, 1},
+ { DBGBUS_DSPP, 80, 2},
+ { DBGBUS_DSPP, 80, 3},
+ { DBGBUS_DSPP, 80, 4},
+ { DBGBUS_DSPP, 80, 5},
+ { DBGBUS_DSPP, 80, 6},
+ { DBGBUS_DSPP, 80, 7},
+
+ { DBGBUS_DSPP, 81, 0},
+ { DBGBUS_DSPP, 81, 1},
+ { DBGBUS_DSPP, 81, 2},
+ { DBGBUS_DSPP, 81, 3},
+ { DBGBUS_DSPP, 81, 4},
+ { DBGBUS_DSPP, 81, 5},
+ { DBGBUS_DSPP, 81, 6},
+ { DBGBUS_DSPP, 81, 7},
+
+ { DBGBUS_DSPP, 82, 0},
+ { DBGBUS_DSPP, 82, 1},
+ { DBGBUS_DSPP, 82, 2},
+ { DBGBUS_DSPP, 82, 3},
+ { DBGBUS_DSPP, 82, 4},
+ { DBGBUS_DSPP, 82, 5},
+ { DBGBUS_DSPP, 82, 6},
+ { DBGBUS_DSPP, 82, 7},
+
+ { DBGBUS_DSPP, 83, 0},
+ { DBGBUS_DSPP, 83, 1},
+ { DBGBUS_DSPP, 83, 2},
+ { DBGBUS_DSPP, 83, 3},
+ { DBGBUS_DSPP, 83, 4},
+ { DBGBUS_DSPP, 83, 5},
+ { DBGBUS_DSPP, 83, 6},
+ { DBGBUS_DSPP, 83, 7},
+
+ /* csc */
+ { DBGBUS_SSPP0, 7, 0},
+ { DBGBUS_SSPP0, 7, 1},
+ { DBGBUS_SSPP0, 27, 0},
+ { DBGBUS_SSPP0, 27, 1},
+ { DBGBUS_SSPP1, 7, 0},
+ { DBGBUS_SSPP1, 7, 1},
+ { DBGBUS_SSPP1, 27, 0},
+ { DBGBUS_SSPP1, 27, 1},
+
+ /* pcc */
+ { DBGBUS_SSPP0, 3, 3},
+ { DBGBUS_SSPP0, 23, 3},
+ { DBGBUS_SSPP0, 33, 3},
+ { DBGBUS_SSPP0, 43, 3},
+ { DBGBUS_SSPP1, 3, 3},
+ { DBGBUS_SSPP1, 23, 3},
+ { DBGBUS_SSPP1, 33, 3},
+ { DBGBUS_SSPP1, 43, 3},
+
+ /* spa */
+ { DBGBUS_SSPP0, 8, 0},
+ { DBGBUS_SSPP0, 28, 0},
+ { DBGBUS_SSPP1, 8, 0},
+ { DBGBUS_SSPP1, 28, 0},
+ { DBGBUS_DSPP, 13, 0},
+ { DBGBUS_DSPP, 19, 0},
+
+ /* igc */
+ { DBGBUS_SSPP0, 9, 0},
+ { DBGBUS_SSPP0, 9, 1},
+ { DBGBUS_SSPP0, 9, 3},
+ { DBGBUS_SSPP0, 29, 0},
+ { DBGBUS_SSPP0, 29, 1},
+ { DBGBUS_SSPP0, 29, 3},
+ { DBGBUS_SSPP0, 17, 0},
+ { DBGBUS_SSPP0, 17, 1},
+ { DBGBUS_SSPP0, 17, 3},
+ { DBGBUS_SSPP0, 37, 0},
+ { DBGBUS_SSPP0, 37, 1},
+ { DBGBUS_SSPP0, 37, 3},
+ { DBGBUS_SSPP0, 46, 0},
+ { DBGBUS_SSPP0, 46, 1},
+ { DBGBUS_SSPP0, 46, 3},
+
+ { DBGBUS_SSPP1, 9, 0},
+ { DBGBUS_SSPP1, 9, 1},
+ { DBGBUS_SSPP1, 9, 3},
+ { DBGBUS_SSPP1, 29, 0},
+ { DBGBUS_SSPP1, 29, 1},
+ { DBGBUS_SSPP1, 29, 3},
+ { DBGBUS_SSPP1, 17, 0},
+ { DBGBUS_SSPP1, 17, 1},
+ { DBGBUS_SSPP1, 17, 3},
+ { DBGBUS_SSPP1, 37, 0},
+ { DBGBUS_SSPP1, 37, 1},
+ { DBGBUS_SSPP1, 37, 3},
+ { DBGBUS_SSPP1, 46, 0},
+ { DBGBUS_SSPP1, 46, 1},
+ { DBGBUS_SSPP1, 46, 3},
+
+ { DBGBUS_DSPP, 14, 0},
+ { DBGBUS_DSPP, 14, 1},
+ { DBGBUS_DSPP, 14, 3},
+ { DBGBUS_DSPP, 20, 0},
+ { DBGBUS_DSPP, 20, 1},
+ { DBGBUS_DSPP, 20, 3},
+
+ { DBGBUS_PERIPH, 60, 0},
+};
+
+static struct sde_debug_bus_entry dbg_bus_sde_sdm845[] = {
+
+ /* Unpack 0 sspp 0*/
+ { DBGBUS_SSPP0, 50, 2 },
+ { DBGBUS_SSPP0, 60, 2 },
+ { DBGBUS_SSPP0, 70, 2 },
+
+ /* Upack 0 sspp 1*/
+ { DBGBUS_SSPP1, 50, 2 },
+ { DBGBUS_SSPP1, 60, 2 },
+ { DBGBUS_SSPP1, 70, 2 },
+
+ /* scheduler */
+ { DBGBUS_DSPP, 130, 0 },
+ { DBGBUS_DSPP, 130, 1 },
+ { DBGBUS_DSPP, 130, 2 },
+ { DBGBUS_DSPP, 130, 3 },
+ { DBGBUS_DSPP, 130, 4 },
+ { DBGBUS_DSPP, 130, 5 },
+
+ /* qseed */
+ { DBGBUS_SSPP0, 6, 0},
+ { DBGBUS_SSPP0, 6, 1},
+ { DBGBUS_SSPP0, 26, 0},
+ { DBGBUS_SSPP0, 26, 1},
+ { DBGBUS_SSPP1, 6, 0},
+ { DBGBUS_SSPP1, 6, 1},
+ { DBGBUS_SSPP1, 26, 0},
+ { DBGBUS_SSPP1, 26, 1},
+
+ /* scale */
+ { DBGBUS_SSPP0, 16, 0},
+ { DBGBUS_SSPP0, 16, 1},
+ { DBGBUS_SSPP0, 36, 0},
+ { DBGBUS_SSPP0, 36, 1},
+ { DBGBUS_SSPP1, 16, 0},
+ { DBGBUS_SSPP1, 16, 1},
+ { DBGBUS_SSPP1, 36, 0},
+ { DBGBUS_SSPP1, 36, 1},
+
+ /* fetch sspp0 */
+
+ /* vig 0 */
+ { DBGBUS_SSPP0, 0, 0 },
+ { DBGBUS_SSPP0, 0, 1 },
+ { DBGBUS_SSPP0, 0, 2 },
+ { DBGBUS_SSPP0, 0, 3 },
+ { DBGBUS_SSPP0, 0, 4 },
+ { DBGBUS_SSPP0, 0, 5 },
+ { DBGBUS_SSPP0, 0, 6 },
+ { DBGBUS_SSPP0, 0, 7 },
+
+ { DBGBUS_SSPP0, 1, 0 },
+ { DBGBUS_SSPP0, 1, 1 },
+ { DBGBUS_SSPP0, 1, 2 },
+ { DBGBUS_SSPP0, 1, 3 },
+ { DBGBUS_SSPP0, 1, 4 },
+ { DBGBUS_SSPP0, 1, 5 },
+ { DBGBUS_SSPP0, 1, 6 },
+ { DBGBUS_SSPP0, 1, 7 },
+
+ { DBGBUS_SSPP0, 2, 0 },
+ { DBGBUS_SSPP0, 2, 1 },
+ { DBGBUS_SSPP0, 2, 2 },
+ { DBGBUS_SSPP0, 2, 3 },
+ { DBGBUS_SSPP0, 2, 4 },
+ { DBGBUS_SSPP0, 2, 5 },
+ { DBGBUS_SSPP0, 2, 6 },
+ { DBGBUS_SSPP0, 2, 7 },
+
+ { DBGBUS_SSPP0, 4, 0 },
+ { DBGBUS_SSPP0, 4, 1 },
+ { DBGBUS_SSPP0, 4, 2 },
+ { DBGBUS_SSPP0, 4, 3 },
+ { DBGBUS_SSPP0, 4, 4 },
+ { DBGBUS_SSPP0, 4, 5 },
+ { DBGBUS_SSPP0, 4, 6 },
+ { DBGBUS_SSPP0, 4, 7 },
+
+ { DBGBUS_SSPP0, 5, 0 },
+ { DBGBUS_SSPP0, 5, 1 },
+ { DBGBUS_SSPP0, 5, 2 },
+ { DBGBUS_SSPP0, 5, 3 },
+ { DBGBUS_SSPP0, 5, 4 },
+ { DBGBUS_SSPP0, 5, 5 },
+ { DBGBUS_SSPP0, 5, 6 },
+ { DBGBUS_SSPP0, 5, 7 },
+
+ /* vig 2 */
+ { DBGBUS_SSPP0, 20, 0 },
+ { DBGBUS_SSPP0, 20, 1 },
+ { DBGBUS_SSPP0, 20, 2 },
+ { DBGBUS_SSPP0, 20, 3 },
+ { DBGBUS_SSPP0, 20, 4 },
+ { DBGBUS_SSPP0, 20, 5 },
+ { DBGBUS_SSPP0, 20, 6 },
+ { DBGBUS_SSPP0, 20, 7 },
+
+ { DBGBUS_SSPP0, 21, 0 },
+ { DBGBUS_SSPP0, 21, 1 },
+ { DBGBUS_SSPP0, 21, 2 },
+ { DBGBUS_SSPP0, 21, 3 },
+ { DBGBUS_SSPP0, 21, 4 },
+ { DBGBUS_SSPP0, 21, 5 },
+ { DBGBUS_SSPP0, 21, 6 },
+ { DBGBUS_SSPP0, 21, 7 },
+
+ { DBGBUS_SSPP0, 22, 0 },
+ { DBGBUS_SSPP0, 22, 1 },
+ { DBGBUS_SSPP0, 22, 2 },
+ { DBGBUS_SSPP0, 22, 3 },
+ { DBGBUS_SSPP0, 22, 4 },
+ { DBGBUS_SSPP0, 22, 5 },
+ { DBGBUS_SSPP0, 22, 6 },
+ { DBGBUS_SSPP0, 22, 7 },
+
+ { DBGBUS_SSPP0, 24, 0 },
+ { DBGBUS_SSPP0, 24, 1 },
+ { DBGBUS_SSPP0, 24, 2 },
+ { DBGBUS_SSPP0, 24, 3 },
+ { DBGBUS_SSPP0, 24, 4 },
+ { DBGBUS_SSPP0, 24, 5 },
+ { DBGBUS_SSPP0, 24, 6 },
+ { DBGBUS_SSPP0, 24, 7 },
+
+ { DBGBUS_SSPP0, 25, 0 },
+ { DBGBUS_SSPP0, 25, 1 },
+ { DBGBUS_SSPP0, 25, 2 },
+ { DBGBUS_SSPP0, 25, 3 },
+ { DBGBUS_SSPP0, 25, 4 },
+ { DBGBUS_SSPP0, 25, 5 },
+ { DBGBUS_SSPP0, 25, 6 },
+ { DBGBUS_SSPP0, 25, 7 },
+
+ /* dma 2 */
+ { DBGBUS_SSPP0, 30, 0 },
+ { DBGBUS_SSPP0, 30, 1 },
+ { DBGBUS_SSPP0, 30, 2 },
+ { DBGBUS_SSPP0, 30, 3 },
+ { DBGBUS_SSPP0, 30, 4 },
+ { DBGBUS_SSPP0, 30, 5 },
+ { DBGBUS_SSPP0, 30, 6 },
+ { DBGBUS_SSPP0, 30, 7 },
+
+ { DBGBUS_SSPP0, 31, 0 },
+ { DBGBUS_SSPP0, 31, 1 },
+ { DBGBUS_SSPP0, 31, 2 },
+ { DBGBUS_SSPP0, 31, 3 },
+ { DBGBUS_SSPP0, 31, 4 },
+ { DBGBUS_SSPP0, 31, 5 },
+ { DBGBUS_SSPP0, 31, 6 },
+ { DBGBUS_SSPP0, 31, 7 },
+
+ { DBGBUS_SSPP0, 32, 0 },
+ { DBGBUS_SSPP0, 32, 1 },
+ { DBGBUS_SSPP0, 32, 2 },
+ { DBGBUS_SSPP0, 32, 3 },
+ { DBGBUS_SSPP0, 32, 4 },
+ { DBGBUS_SSPP0, 32, 5 },
+ { DBGBUS_SSPP0, 32, 6 },
+ { DBGBUS_SSPP0, 32, 7 },
+
+ { DBGBUS_SSPP0, 33, 0 },
+ { DBGBUS_SSPP0, 33, 1 },
+ { DBGBUS_SSPP0, 33, 2 },
+ { DBGBUS_SSPP0, 33, 3 },
+ { DBGBUS_SSPP0, 33, 4 },
+ { DBGBUS_SSPP0, 33, 5 },
+ { DBGBUS_SSPP0, 33, 6 },
+ { DBGBUS_SSPP0, 33, 7 },
+
+ { DBGBUS_SSPP0, 34, 0 },
+ { DBGBUS_SSPP0, 34, 1 },
+ { DBGBUS_SSPP0, 34, 2 },
+ { DBGBUS_SSPP0, 34, 3 },
+ { DBGBUS_SSPP0, 34, 4 },
+ { DBGBUS_SSPP0, 34, 5 },
+ { DBGBUS_SSPP0, 34, 6 },
+ { DBGBUS_SSPP0, 34, 7 },
+
+ { DBGBUS_SSPP0, 35, 0 },
+ { DBGBUS_SSPP0, 35, 1 },
+ { DBGBUS_SSPP0, 35, 2 },
+ { DBGBUS_SSPP0, 35, 3 },
+
+ /* dma 0 */
+ { DBGBUS_SSPP0, 40, 0 },
+ { DBGBUS_SSPP0, 40, 1 },
+ { DBGBUS_SSPP0, 40, 2 },
+ { DBGBUS_SSPP0, 40, 3 },
+ { DBGBUS_SSPP0, 40, 4 },
+ { DBGBUS_SSPP0, 40, 5 },
+ { DBGBUS_SSPP0, 40, 6 },
+ { DBGBUS_SSPP0, 40, 7 },
+
+ { DBGBUS_SSPP0, 41, 0 },
+ { DBGBUS_SSPP0, 41, 1 },
+ { DBGBUS_SSPP0, 41, 2 },
+ { DBGBUS_SSPP0, 41, 3 },
+ { DBGBUS_SSPP0, 41, 4 },
+ { DBGBUS_SSPP0, 41, 5 },
+ { DBGBUS_SSPP0, 41, 6 },
+ { DBGBUS_SSPP0, 41, 7 },
+
+ { DBGBUS_SSPP0, 42, 0 },
+ { DBGBUS_SSPP0, 42, 1 },
+ { DBGBUS_SSPP0, 42, 2 },
+ { DBGBUS_SSPP0, 42, 3 },
+ { DBGBUS_SSPP0, 42, 4 },
+ { DBGBUS_SSPP0, 42, 5 },
+ { DBGBUS_SSPP0, 42, 6 },
+ { DBGBUS_SSPP0, 42, 7 },
+
+ { DBGBUS_SSPP0, 44, 0 },
+ { DBGBUS_SSPP0, 44, 1 },
+ { DBGBUS_SSPP0, 44, 2 },
+ { DBGBUS_SSPP0, 44, 3 },
+ { DBGBUS_SSPP0, 44, 4 },
+ { DBGBUS_SSPP0, 44, 5 },
+ { DBGBUS_SSPP0, 44, 6 },
+ { DBGBUS_SSPP0, 44, 7 },
+
+ { DBGBUS_SSPP0, 45, 0 },
+ { DBGBUS_SSPP0, 45, 1 },
+ { DBGBUS_SSPP0, 45, 2 },
+ { DBGBUS_SSPP0, 45, 3 },
+ { DBGBUS_SSPP0, 45, 4 },
+ { DBGBUS_SSPP0, 45, 5 },
+ { DBGBUS_SSPP0, 45, 6 },
+ { DBGBUS_SSPP0, 45, 7 },
+
+ /* fetch sspp1 */
+ /* vig 1 */
+ { DBGBUS_SSPP1, 0, 0 },
+ { DBGBUS_SSPP1, 0, 1 },
+ { DBGBUS_SSPP1, 0, 2 },
+ { DBGBUS_SSPP1, 0, 3 },
+ { DBGBUS_SSPP1, 0, 4 },
+ { DBGBUS_SSPP1, 0, 5 },
+ { DBGBUS_SSPP1, 0, 6 },
+ { DBGBUS_SSPP1, 0, 7 },
+
+ { DBGBUS_SSPP1, 1, 0 },
+ { DBGBUS_SSPP1, 1, 1 },
+ { DBGBUS_SSPP1, 1, 2 },
+ { DBGBUS_SSPP1, 1, 3 },
+ { DBGBUS_SSPP1, 1, 4 },
+ { DBGBUS_SSPP1, 1, 5 },
+ { DBGBUS_SSPP1, 1, 6 },
+ { DBGBUS_SSPP1, 1, 7 },
+
+ { DBGBUS_SSPP1, 2, 0 },
+ { DBGBUS_SSPP1, 2, 1 },
+ { DBGBUS_SSPP1, 2, 2 },
+ { DBGBUS_SSPP1, 2, 3 },
+ { DBGBUS_SSPP1, 2, 4 },
+ { DBGBUS_SSPP1, 2, 5 },
+ { DBGBUS_SSPP1, 2, 6 },
+ { DBGBUS_SSPP1, 2, 7 },
+
+ { DBGBUS_SSPP1, 4, 0 },
+ { DBGBUS_SSPP1, 4, 1 },
+ { DBGBUS_SSPP1, 4, 2 },
+ { DBGBUS_SSPP1, 4, 3 },
+ { DBGBUS_SSPP1, 4, 4 },
+ { DBGBUS_SSPP1, 4, 5 },
+ { DBGBUS_SSPP1, 4, 6 },
+ { DBGBUS_SSPP1, 4, 7 },
+
+ { DBGBUS_SSPP1, 5, 0 },
+ { DBGBUS_SSPP1, 5, 1 },
+ { DBGBUS_SSPP1, 5, 2 },
+ { DBGBUS_SSPP1, 5, 3 },
+ { DBGBUS_SSPP1, 5, 4 },
+ { DBGBUS_SSPP1, 5, 5 },
+ { DBGBUS_SSPP1, 5, 6 },
+ { DBGBUS_SSPP1, 5, 7 },
+
+ /* vig 3 */
+ { DBGBUS_SSPP1, 20, 0 },
+ { DBGBUS_SSPP1, 20, 1 },
+ { DBGBUS_SSPP1, 20, 2 },
+ { DBGBUS_SSPP1, 20, 3 },
+ { DBGBUS_SSPP1, 20, 4 },
+ { DBGBUS_SSPP1, 20, 5 },
+ { DBGBUS_SSPP1, 20, 6 },
+ { DBGBUS_SSPP1, 20, 7 },
+
+ { DBGBUS_SSPP1, 21, 0 },
+ { DBGBUS_SSPP1, 21, 1 },
+ { DBGBUS_SSPP1, 21, 2 },
+ { DBGBUS_SSPP1, 21, 3 },
+ { DBGBUS_SSPP1, 21, 4 },
+ { DBGBUS_SSPP1, 21, 5 },
+ { DBGBUS_SSPP1, 21, 6 },
+ { DBGBUS_SSPP1, 21, 7 },
+
+ { DBGBUS_SSPP1, 22, 0 },
+ { DBGBUS_SSPP1, 22, 1 },
+ { DBGBUS_SSPP1, 22, 2 },
+ { DBGBUS_SSPP1, 22, 3 },
+ { DBGBUS_SSPP1, 22, 4 },
+ { DBGBUS_SSPP1, 22, 5 },
+ { DBGBUS_SSPP1, 22, 6 },
+ { DBGBUS_SSPP1, 22, 7 },
+
+ { DBGBUS_SSPP1, 24, 0 },
+ { DBGBUS_SSPP1, 24, 1 },
+ { DBGBUS_SSPP1, 24, 2 },
+ { DBGBUS_SSPP1, 24, 3 },
+ { DBGBUS_SSPP1, 24, 4 },
+ { DBGBUS_SSPP1, 24, 5 },
+ { DBGBUS_SSPP1, 24, 6 },
+ { DBGBUS_SSPP1, 24, 7 },
+
+ { DBGBUS_SSPP1, 25, 0 },
+ { DBGBUS_SSPP1, 25, 1 },
+ { DBGBUS_SSPP1, 25, 2 },
+ { DBGBUS_SSPP1, 25, 3 },
+ { DBGBUS_SSPP1, 25, 4 },
+ { DBGBUS_SSPP1, 25, 5 },
+ { DBGBUS_SSPP1, 25, 6 },
+ { DBGBUS_SSPP1, 25, 7 },
+
+ /* dma 3 */
+ { DBGBUS_SSPP1, 30, 0 },
+ { DBGBUS_SSPP1, 30, 1 },
+ { DBGBUS_SSPP1, 30, 2 },
+ { DBGBUS_SSPP1, 30, 3 },
+ { DBGBUS_SSPP1, 30, 4 },
+ { DBGBUS_SSPP1, 30, 5 },
+ { DBGBUS_SSPP1, 30, 6 },
+ { DBGBUS_SSPP1, 30, 7 },
+
+ { DBGBUS_SSPP1, 31, 0 },
+ { DBGBUS_SSPP1, 31, 1 },
+ { DBGBUS_SSPP1, 31, 2 },
+ { DBGBUS_SSPP1, 31, 3 },
+ { DBGBUS_SSPP1, 31, 4 },
+ { DBGBUS_SSPP1, 31, 5 },
+ { DBGBUS_SSPP1, 31, 6 },
+ { DBGBUS_SSPP1, 31, 7 },
+
+ { DBGBUS_SSPP1, 32, 0 },
+ { DBGBUS_SSPP1, 32, 1 },
+ { DBGBUS_SSPP1, 32, 2 },
+ { DBGBUS_SSPP1, 32, 3 },
+ { DBGBUS_SSPP1, 32, 4 },
+ { DBGBUS_SSPP1, 32, 5 },
+ { DBGBUS_SSPP1, 32, 6 },
+ { DBGBUS_SSPP1, 32, 7 },
+
+ { DBGBUS_SSPP1, 33, 0 },
+ { DBGBUS_SSPP1, 33, 1 },
+ { DBGBUS_SSPP1, 33, 2 },
+ { DBGBUS_SSPP1, 33, 3 },
+ { DBGBUS_SSPP1, 33, 4 },
+ { DBGBUS_SSPP1, 33, 5 },
+ { DBGBUS_SSPP1, 33, 6 },
+ { DBGBUS_SSPP1, 33, 7 },
+
+ { DBGBUS_SSPP1, 34, 0 },
+ { DBGBUS_SSPP1, 34, 1 },
+ { DBGBUS_SSPP1, 34, 2 },
+ { DBGBUS_SSPP1, 34, 3 },
+ { DBGBUS_SSPP1, 34, 4 },
+ { DBGBUS_SSPP1, 34, 5 },
+ { DBGBUS_SSPP1, 34, 6 },
+ { DBGBUS_SSPP1, 34, 7 },
+
+ { DBGBUS_SSPP1, 35, 0 },
+ { DBGBUS_SSPP1, 35, 1 },
+ { DBGBUS_SSPP1, 35, 2 },
+
+ /* dma 1 */
+ { DBGBUS_SSPP1, 40, 0 },
+ { DBGBUS_SSPP1, 40, 1 },
+ { DBGBUS_SSPP1, 40, 2 },
+ { DBGBUS_SSPP1, 40, 3 },
+ { DBGBUS_SSPP1, 40, 4 },
+ { DBGBUS_SSPP1, 40, 5 },
+ { DBGBUS_SSPP1, 40, 6 },
+ { DBGBUS_SSPP1, 40, 7 },
+
+ { DBGBUS_SSPP1, 41, 0 },
+ { DBGBUS_SSPP1, 41, 1 },
+ { DBGBUS_SSPP1, 41, 2 },
+ { DBGBUS_SSPP1, 41, 3 },
+ { DBGBUS_SSPP1, 41, 4 },
+ { DBGBUS_SSPP1, 41, 5 },
+ { DBGBUS_SSPP1, 41, 6 },
+ { DBGBUS_SSPP1, 41, 7 },
+
+ { DBGBUS_SSPP1, 42, 0 },
+ { DBGBUS_SSPP1, 42, 1 },
+ { DBGBUS_SSPP1, 42, 2 },
+ { DBGBUS_SSPP1, 42, 3 },
+ { DBGBUS_SSPP1, 42, 4 },
+ { DBGBUS_SSPP1, 42, 5 },
+ { DBGBUS_SSPP1, 42, 6 },
+ { DBGBUS_SSPP1, 42, 7 },
+
+ { DBGBUS_SSPP1, 44, 0 },
+ { DBGBUS_SSPP1, 44, 1 },
+ { DBGBUS_SSPP1, 44, 2 },
+ { DBGBUS_SSPP1, 44, 3 },
+ { DBGBUS_SSPP1, 44, 4 },
+ { DBGBUS_SSPP1, 44, 5 },
+ { DBGBUS_SSPP1, 44, 6 },
+ { DBGBUS_SSPP1, 44, 7 },
+
+ { DBGBUS_SSPP1, 45, 0 },
+ { DBGBUS_SSPP1, 45, 1 },
+ { DBGBUS_SSPP1, 45, 2 },
+ { DBGBUS_SSPP1, 45, 3 },
+ { DBGBUS_SSPP1, 45, 4 },
+ { DBGBUS_SSPP1, 45, 5 },
+ { DBGBUS_SSPP1, 45, 6 },
+ { DBGBUS_SSPP1, 45, 7 },
+
+ /* dspp */
+ { DBGBUS_DSPP, 13, 0 },
+ { DBGBUS_DSPP, 19, 0 },
+ { DBGBUS_DSPP, 14, 0 },
+ { DBGBUS_DSPP, 14, 1 },
+ { DBGBUS_DSPP, 14, 3 },
+ { DBGBUS_DSPP, 20, 0 },
+ { DBGBUS_DSPP, 20, 1 },
+ { DBGBUS_DSPP, 20, 3 },
+
+ /* ppb_0 */
+ { DBGBUS_DSPP, 31, 0 },
+ { DBGBUS_DSPP, 33, 0 },
+ { DBGBUS_DSPP, 35, 0 },
+ { DBGBUS_DSPP, 42, 0 },
+
+ /* ppb_1 */
+ { DBGBUS_DSPP, 32, 0 },
+ { DBGBUS_DSPP, 34, 0 },
+ { DBGBUS_DSPP, 36, 0 },
+ { DBGBUS_DSPP, 43, 0 },
+
+ /* lm_lut */
+ { DBGBUS_DSPP, 109, 0 },
+ { DBGBUS_DSPP, 105, 0 },
+ { DBGBUS_DSPP, 103, 0 },
+
+ /* crossbar */
+ { DBGBUS_DSPP, 0, 0},
+
+ /* rotator */
+ { DBGBUS_DSPP, 9, 0},
+
+ /* blend */
+ /* LM0 */
+ { DBGBUS_DSPP, 63, 1},
+ { DBGBUS_DSPP, 63, 2},
+ { DBGBUS_DSPP, 63, 3},
+ { DBGBUS_DSPP, 63, 4},
+ { DBGBUS_DSPP, 63, 5},
+ { DBGBUS_DSPP, 63, 6},
+ { DBGBUS_DSPP, 63, 7},
+
+ { DBGBUS_DSPP, 64, 1},
+ { DBGBUS_DSPP, 64, 2},
+ { DBGBUS_DSPP, 64, 3},
+ { DBGBUS_DSPP, 64, 4},
+ { DBGBUS_DSPP, 64, 5},
+ { DBGBUS_DSPP, 64, 6},
+ { DBGBUS_DSPP, 64, 7},
+
+ { DBGBUS_DSPP, 65, 1},
+ { DBGBUS_DSPP, 65, 2},
+ { DBGBUS_DSPP, 65, 3},
+ { DBGBUS_DSPP, 65, 4},
+ { DBGBUS_DSPP, 65, 5},
+ { DBGBUS_DSPP, 65, 6},
+ { DBGBUS_DSPP, 65, 7},
+
+ { DBGBUS_DSPP, 66, 1},
+ { DBGBUS_DSPP, 66, 2},
+ { DBGBUS_DSPP, 66, 3},
+ { DBGBUS_DSPP, 66, 4},
+ { DBGBUS_DSPP, 66, 5},
+ { DBGBUS_DSPP, 66, 6},
+ { DBGBUS_DSPP, 66, 7},
+
+ { DBGBUS_DSPP, 67, 1},
+ { DBGBUS_DSPP, 67, 2},
+ { DBGBUS_DSPP, 67, 3},
+ { DBGBUS_DSPP, 67, 4},
+ { DBGBUS_DSPP, 67, 5},
+ { DBGBUS_DSPP, 67, 6},
+ { DBGBUS_DSPP, 67, 7},
+
+ { DBGBUS_DSPP, 68, 1},
+ { DBGBUS_DSPP, 68, 2},
+ { DBGBUS_DSPP, 68, 3},
+ { DBGBUS_DSPP, 68, 4},
+ { DBGBUS_DSPP, 68, 5},
+ { DBGBUS_DSPP, 68, 6},
+ { DBGBUS_DSPP, 68, 7},
+
+ { DBGBUS_DSPP, 69, 1},
+ { DBGBUS_DSPP, 69, 2},
+ { DBGBUS_DSPP, 69, 3},
+ { DBGBUS_DSPP, 69, 4},
+ { DBGBUS_DSPP, 69, 5},
+ { DBGBUS_DSPP, 69, 6},
+ { DBGBUS_DSPP, 69, 7},
+
+ { DBGBUS_DSPP, 84, 1},
+ { DBGBUS_DSPP, 84, 2},
+ { DBGBUS_DSPP, 84, 3},
+ { DBGBUS_DSPP, 84, 4},
+ { DBGBUS_DSPP, 84, 5},
+ { DBGBUS_DSPP, 84, 6},
+ { DBGBUS_DSPP, 84, 7},
+
+
+ { DBGBUS_DSPP, 85, 1},
+ { DBGBUS_DSPP, 85, 2},
+ { DBGBUS_DSPP, 85, 3},
+ { DBGBUS_DSPP, 85, 4},
+ { DBGBUS_DSPP, 85, 5},
+ { DBGBUS_DSPP, 85, 6},
+ { DBGBUS_DSPP, 85, 7},
+
+
+ { DBGBUS_DSPP, 86, 1},
+ { DBGBUS_DSPP, 86, 2},
+ { DBGBUS_DSPP, 86, 3},
+ { DBGBUS_DSPP, 86, 4},
+ { DBGBUS_DSPP, 86, 5},
+ { DBGBUS_DSPP, 86, 6},
+ { DBGBUS_DSPP, 86, 7},
+
+
+ { DBGBUS_DSPP, 87, 1},
+ { DBGBUS_DSPP, 87, 2},
+ { DBGBUS_DSPP, 87, 3},
+ { DBGBUS_DSPP, 87, 4},
+ { DBGBUS_DSPP, 87, 5},
+ { DBGBUS_DSPP, 87, 6},
+ { DBGBUS_DSPP, 87, 7},
+
+ /* LM1 */
+ { DBGBUS_DSPP, 70, 1},
+ { DBGBUS_DSPP, 70, 2},
+ { DBGBUS_DSPP, 70, 3},
+ { DBGBUS_DSPP, 70, 4},
+ { DBGBUS_DSPP, 70, 5},
+ { DBGBUS_DSPP, 70, 6},
+ { DBGBUS_DSPP, 70, 7},
+
+ { DBGBUS_DSPP, 71, 1},
+ { DBGBUS_DSPP, 71, 2},
+ { DBGBUS_DSPP, 71, 3},
+ { DBGBUS_DSPP, 71, 4},
+ { DBGBUS_DSPP, 71, 5},
+ { DBGBUS_DSPP, 71, 6},
+ { DBGBUS_DSPP, 71, 7},
+
+ { DBGBUS_DSPP, 72, 1},
+ { DBGBUS_DSPP, 72, 2},
+ { DBGBUS_DSPP, 72, 3},
+ { DBGBUS_DSPP, 72, 4},
+ { DBGBUS_DSPP, 72, 5},
+ { DBGBUS_DSPP, 72, 6},
+ { DBGBUS_DSPP, 72, 7},
+
+ { DBGBUS_DSPP, 73, 1},
+ { DBGBUS_DSPP, 73, 2},
+ { DBGBUS_DSPP, 73, 3},
+ { DBGBUS_DSPP, 73, 4},
+ { DBGBUS_DSPP, 73, 5},
+ { DBGBUS_DSPP, 73, 6},
+ { DBGBUS_DSPP, 73, 7},
+
+ { DBGBUS_DSPP, 74, 1},
+ { DBGBUS_DSPP, 74, 2},
+ { DBGBUS_DSPP, 74, 3},
+ { DBGBUS_DSPP, 74, 4},
+ { DBGBUS_DSPP, 74, 5},
+ { DBGBUS_DSPP, 74, 6},
+ { DBGBUS_DSPP, 74, 7},
+
+ { DBGBUS_DSPP, 75, 1},
+ { DBGBUS_DSPP, 75, 2},
+ { DBGBUS_DSPP, 75, 3},
+ { DBGBUS_DSPP, 75, 4},
+ { DBGBUS_DSPP, 75, 5},
+ { DBGBUS_DSPP, 75, 6},
+ { DBGBUS_DSPP, 75, 7},
+
+ { DBGBUS_DSPP, 76, 1},
+ { DBGBUS_DSPP, 76, 2},
+ { DBGBUS_DSPP, 76, 3},
+ { DBGBUS_DSPP, 76, 4},
+ { DBGBUS_DSPP, 76, 5},
+ { DBGBUS_DSPP, 76, 6},
+ { DBGBUS_DSPP, 76, 7},
+
+ { DBGBUS_DSPP, 88, 1},
+ { DBGBUS_DSPP, 88, 2},
+ { DBGBUS_DSPP, 88, 3},
+ { DBGBUS_DSPP, 88, 4},
+ { DBGBUS_DSPP, 88, 5},
+ { DBGBUS_DSPP, 88, 6},
+ { DBGBUS_DSPP, 88, 7},
+
+ { DBGBUS_DSPP, 89, 1},
+ { DBGBUS_DSPP, 89, 2},
+ { DBGBUS_DSPP, 89, 3},
+ { DBGBUS_DSPP, 89, 4},
+ { DBGBUS_DSPP, 89, 5},
+ { DBGBUS_DSPP, 89, 6},
+ { DBGBUS_DSPP, 89, 7},
+
+ { DBGBUS_DSPP, 90, 1},
+ { DBGBUS_DSPP, 90, 2},
+ { DBGBUS_DSPP, 90, 3},
+ { DBGBUS_DSPP, 90, 4},
+ { DBGBUS_DSPP, 90, 5},
+ { DBGBUS_DSPP, 90, 6},
+ { DBGBUS_DSPP, 90, 7},
+
+ { DBGBUS_DSPP, 91, 1},
+ { DBGBUS_DSPP, 91, 2},
+ { DBGBUS_DSPP, 91, 3},
+ { DBGBUS_DSPP, 91, 4},
+ { DBGBUS_DSPP, 91, 5},
+ { DBGBUS_DSPP, 91, 6},
+ { DBGBUS_DSPP, 91, 7},
+
+ /* LM2 */
+ { DBGBUS_DSPP, 77, 0},
+ { DBGBUS_DSPP, 77, 1},
+ { DBGBUS_DSPP, 77, 2},
+ { DBGBUS_DSPP, 77, 3},
+ { DBGBUS_DSPP, 77, 4},
+ { DBGBUS_DSPP, 77, 5},
+ { DBGBUS_DSPP, 77, 6},
+ { DBGBUS_DSPP, 77, 7},
+
+ { DBGBUS_DSPP, 78, 0},
+ { DBGBUS_DSPP, 78, 1},
+ { DBGBUS_DSPP, 78, 2},
+ { DBGBUS_DSPP, 78, 3},
+ { DBGBUS_DSPP, 78, 4},
+ { DBGBUS_DSPP, 78, 5},
+ { DBGBUS_DSPP, 78, 6},
+ { DBGBUS_DSPP, 78, 7},
+
+ { DBGBUS_DSPP, 79, 0},
+ { DBGBUS_DSPP, 79, 1},
+ { DBGBUS_DSPP, 79, 2},
+ { DBGBUS_DSPP, 79, 3},
+ { DBGBUS_DSPP, 79, 4},
+ { DBGBUS_DSPP, 79, 5},
+ { DBGBUS_DSPP, 79, 6},
+ { DBGBUS_DSPP, 79, 7},
+
+ { DBGBUS_DSPP, 80, 0},
+ { DBGBUS_DSPP, 80, 1},
+ { DBGBUS_DSPP, 80, 2},
+ { DBGBUS_DSPP, 80, 3},
+ { DBGBUS_DSPP, 80, 4},
+ { DBGBUS_DSPP, 80, 5},
+ { DBGBUS_DSPP, 80, 6},
+ { DBGBUS_DSPP, 80, 7},
+
+ { DBGBUS_DSPP, 81, 0},
+ { DBGBUS_DSPP, 81, 1},
+ { DBGBUS_DSPP, 81, 2},
+ { DBGBUS_DSPP, 81, 3},
+ { DBGBUS_DSPP, 81, 4},
+ { DBGBUS_DSPP, 81, 5},
+ { DBGBUS_DSPP, 81, 6},
+ { DBGBUS_DSPP, 81, 7},
+
+ { DBGBUS_DSPP, 82, 0},
+ { DBGBUS_DSPP, 82, 1},
+ { DBGBUS_DSPP, 82, 2},
+ { DBGBUS_DSPP, 82, 3},
+ { DBGBUS_DSPP, 82, 4},
+ { DBGBUS_DSPP, 82, 5},
+ { DBGBUS_DSPP, 82, 6},
+ { DBGBUS_DSPP, 82, 7},
+
+ { DBGBUS_DSPP, 83, 0},
+ { DBGBUS_DSPP, 83, 1},
+ { DBGBUS_DSPP, 83, 2},
+ { DBGBUS_DSPP, 83, 3},
+ { DBGBUS_DSPP, 83, 4},
+ { DBGBUS_DSPP, 83, 5},
+ { DBGBUS_DSPP, 83, 6},
+ { DBGBUS_DSPP, 83, 7},
+
+ { DBGBUS_DSPP, 92, 1},
+ { DBGBUS_DSPP, 92, 2},
+ { DBGBUS_DSPP, 92, 3},
+ { DBGBUS_DSPP, 92, 4},
+ { DBGBUS_DSPP, 92, 5},
+ { DBGBUS_DSPP, 92, 6},
+ { DBGBUS_DSPP, 92, 7},
+
+ { DBGBUS_DSPP, 93, 1},
+ { DBGBUS_DSPP, 93, 2},
+ { DBGBUS_DSPP, 93, 3},
+ { DBGBUS_DSPP, 93, 4},
+ { DBGBUS_DSPP, 93, 5},
+ { DBGBUS_DSPP, 93, 6},
+ { DBGBUS_DSPP, 93, 7},
+
+ { DBGBUS_DSPP, 94, 1},
+ { DBGBUS_DSPP, 94, 2},
+ { DBGBUS_DSPP, 94, 3},
+ { DBGBUS_DSPP, 94, 4},
+ { DBGBUS_DSPP, 94, 5},
+ { DBGBUS_DSPP, 94, 6},
+ { DBGBUS_DSPP, 94, 7},
+
+ { DBGBUS_DSPP, 95, 1},
+ { DBGBUS_DSPP, 95, 2},
+ { DBGBUS_DSPP, 95, 3},
+ { DBGBUS_DSPP, 95, 4},
+ { DBGBUS_DSPP, 95, 5},
+ { DBGBUS_DSPP, 95, 6},
+ { DBGBUS_DSPP, 95, 7},
+
+ /* LM5 */
+ { DBGBUS_DSPP, 110, 1},
+ { DBGBUS_DSPP, 110, 2},
+ { DBGBUS_DSPP, 110, 3},
+ { DBGBUS_DSPP, 110, 4},
+ { DBGBUS_DSPP, 110, 5},
+ { DBGBUS_DSPP, 110, 6},
+ { DBGBUS_DSPP, 110, 7},
+
+ { DBGBUS_DSPP, 111, 1},
+ { DBGBUS_DSPP, 111, 2},
+ { DBGBUS_DSPP, 111, 3},
+ { DBGBUS_DSPP, 111, 4},
+ { DBGBUS_DSPP, 111, 5},
+ { DBGBUS_DSPP, 111, 6},
+ { DBGBUS_DSPP, 111, 7},
+
+ { DBGBUS_DSPP, 112, 1},
+ { DBGBUS_DSPP, 112, 2},
+ { DBGBUS_DSPP, 112, 3},
+ { DBGBUS_DSPP, 112, 4},
+ { DBGBUS_DSPP, 112, 5},
+ { DBGBUS_DSPP, 112, 6},
+ { DBGBUS_DSPP, 112, 7},
+
+ { DBGBUS_DSPP, 113, 1},
+ { DBGBUS_DSPP, 113, 2},
+ { DBGBUS_DSPP, 113, 3},
+ { DBGBUS_DSPP, 113, 4},
+ { DBGBUS_DSPP, 113, 5},
+ { DBGBUS_DSPP, 113, 6},
+ { DBGBUS_DSPP, 113, 7},
+
+ { DBGBUS_DSPP, 114, 1},
+ { DBGBUS_DSPP, 114, 2},
+ { DBGBUS_DSPP, 114, 3},
+ { DBGBUS_DSPP, 114, 4},
+ { DBGBUS_DSPP, 114, 5},
+ { DBGBUS_DSPP, 114, 6},
+ { DBGBUS_DSPP, 114, 7},
+
+ { DBGBUS_DSPP, 115, 1},
+ { DBGBUS_DSPP, 115, 2},
+ { DBGBUS_DSPP, 115, 3},
+ { DBGBUS_DSPP, 115, 4},
+ { DBGBUS_DSPP, 115, 5},
+ { DBGBUS_DSPP, 115, 6},
+ { DBGBUS_DSPP, 115, 7},
+
+ { DBGBUS_DSPP, 116, 1},
+ { DBGBUS_DSPP, 116, 2},
+ { DBGBUS_DSPP, 116, 3},
+ { DBGBUS_DSPP, 116, 4},
+ { DBGBUS_DSPP, 116, 5},
+ { DBGBUS_DSPP, 116, 6},
+ { DBGBUS_DSPP, 116, 7},
+
+ { DBGBUS_DSPP, 117, 1},
+ { DBGBUS_DSPP, 117, 2},
+ { DBGBUS_DSPP, 117, 3},
+ { DBGBUS_DSPP, 117, 4},
+ { DBGBUS_DSPP, 117, 5},
+ { DBGBUS_DSPP, 117, 6},
+ { DBGBUS_DSPP, 117, 7},
+
+ { DBGBUS_DSPP, 118, 1},
+ { DBGBUS_DSPP, 118, 2},
+ { DBGBUS_DSPP, 118, 3},
+ { DBGBUS_DSPP, 118, 4},
+ { DBGBUS_DSPP, 118, 5},
+ { DBGBUS_DSPP, 118, 6},
+ { DBGBUS_DSPP, 118, 7},
+
+ { DBGBUS_DSPP, 119, 1},
+ { DBGBUS_DSPP, 119, 2},
+ { DBGBUS_DSPP, 119, 3},
+ { DBGBUS_DSPP, 119, 4},
+ { DBGBUS_DSPP, 119, 5},
+ { DBGBUS_DSPP, 119, 6},
+ { DBGBUS_DSPP, 119, 7},
+
+ { DBGBUS_DSPP, 120, 1},
+ { DBGBUS_DSPP, 120, 2},
+ { DBGBUS_DSPP, 120, 3},
+ { DBGBUS_DSPP, 120, 4},
+ { DBGBUS_DSPP, 120, 5},
+ { DBGBUS_DSPP, 120, 6},
+ { DBGBUS_DSPP, 120, 7},
+
+ /* csc */
+ { DBGBUS_SSPP0, 7, 0},
+ { DBGBUS_SSPP0, 7, 1},
+ { DBGBUS_SSPP0, 27, 0},
+ { DBGBUS_SSPP0, 27, 1},
+ { DBGBUS_SSPP1, 7, 0},
+ { DBGBUS_SSPP1, 7, 1},
+ { DBGBUS_SSPP1, 27, 0},
+ { DBGBUS_SSPP1, 27, 1},
+
+ /* pcc */
+ { DBGBUS_SSPP0, 3, 3},
+ { DBGBUS_SSPP0, 23, 3},
+ { DBGBUS_SSPP0, 33, 3},
+ { DBGBUS_SSPP0, 43, 3},
+ { DBGBUS_SSPP1, 3, 3},
+ { DBGBUS_SSPP1, 23, 3},
+ { DBGBUS_SSPP1, 33, 3},
+ { DBGBUS_SSPP1, 43, 3},
+
+ /* spa */
+ { DBGBUS_SSPP0, 8, 0},
+ { DBGBUS_SSPP0, 28, 0},
+ { DBGBUS_SSPP1, 8, 0},
+ { DBGBUS_SSPP1, 28, 0},
+ { DBGBUS_DSPP, 13, 0},
+ { DBGBUS_DSPP, 19, 0},
+
+ /* igc */
+ { DBGBUS_SSPP0, 17, 0},
+ { DBGBUS_SSPP0, 17, 1},
+ { DBGBUS_SSPP0, 17, 3},
+ { DBGBUS_SSPP0, 37, 0},
+ { DBGBUS_SSPP0, 37, 1},
+ { DBGBUS_SSPP0, 37, 3},
+ { DBGBUS_SSPP0, 46, 0},
+ { DBGBUS_SSPP0, 46, 1},
+ { DBGBUS_SSPP0, 46, 3},
+
+ { DBGBUS_SSPP1, 17, 0},
+ { DBGBUS_SSPP1, 17, 1},
+ { DBGBUS_SSPP1, 17, 3},
+ { DBGBUS_SSPP1, 37, 0},
+ { DBGBUS_SSPP1, 37, 1},
+ { DBGBUS_SSPP1, 37, 3},
+ { DBGBUS_SSPP1, 46, 0},
+ { DBGBUS_SSPP1, 46, 1},
+ { DBGBUS_SSPP1, 46, 3},
+
+ { DBGBUS_DSPP, 14, 0},
+ { DBGBUS_DSPP, 14, 1},
+ { DBGBUS_DSPP, 14, 3},
+ { DBGBUS_DSPP, 20, 0},
+ { DBGBUS_DSPP, 20, 1},
+ { DBGBUS_DSPP, 20, 3},
+
+ /* intf0-3 */
+ { DBGBUS_PERIPH, 0, 0},
+ { DBGBUS_PERIPH, 1, 0},
+ { DBGBUS_PERIPH, 2, 0},
+ { DBGBUS_PERIPH, 3, 0},
+
+ /* te counter wrapper */
+ { DBGBUS_PERIPH, 60, 0},
+
+ /* dsc0 */
+ { DBGBUS_PERIPH, 47, 0},
+ { DBGBUS_PERIPH, 47, 1},
+ { DBGBUS_PERIPH, 47, 2},
+ { DBGBUS_PERIPH, 47, 3},
+ { DBGBUS_PERIPH, 47, 4},
+ { DBGBUS_PERIPH, 47, 5},
+ { DBGBUS_PERIPH, 47, 6},
+ { DBGBUS_PERIPH, 47, 7},
+
+ /* dsc1 */
+ { DBGBUS_PERIPH, 48, 0},
+ { DBGBUS_PERIPH, 48, 1},
+ { DBGBUS_PERIPH, 48, 2},
+ { DBGBUS_PERIPH, 48, 3},
+ { DBGBUS_PERIPH, 48, 4},
+ { DBGBUS_PERIPH, 48, 5},
+ { DBGBUS_PERIPH, 48, 6},
+ { DBGBUS_PERIPH, 48, 7},
+
+ /* dsc2 */
+ { DBGBUS_PERIPH, 51, 0},
+ { DBGBUS_PERIPH, 51, 1},
+ { DBGBUS_PERIPH, 51, 2},
+ { DBGBUS_PERIPH, 51, 3},
+ { DBGBUS_PERIPH, 51, 4},
+ { DBGBUS_PERIPH, 51, 5},
+ { DBGBUS_PERIPH, 51, 6},
+ { DBGBUS_PERIPH, 51, 7},
+
+ /* dsc3 */
+ { DBGBUS_PERIPH, 52, 0},
+ { DBGBUS_PERIPH, 52, 1},
+ { DBGBUS_PERIPH, 52, 2},
+ { DBGBUS_PERIPH, 52, 3},
+ { DBGBUS_PERIPH, 52, 4},
+ { DBGBUS_PERIPH, 52, 5},
+ { DBGBUS_PERIPH, 52, 6},
+ { DBGBUS_PERIPH, 52, 7},
+
+ /* tear-check */
+ { DBGBUS_PERIPH, 63, 0 },
+ { DBGBUS_PERIPH, 64, 0 },
+ { DBGBUS_PERIPH, 65, 0 },
+ { DBGBUS_PERIPH, 73, 0 },
+ { DBGBUS_PERIPH, 74, 0 },
+
+ /* cdwn */
+ { DBGBUS_PERIPH, 80, 0},
+ { DBGBUS_PERIPH, 80, 1},
+ { DBGBUS_PERIPH, 80, 2},
+
+ { DBGBUS_PERIPH, 81, 0},
+ { DBGBUS_PERIPH, 81, 1},
+ { DBGBUS_PERIPH, 81, 2},
+
+ { DBGBUS_PERIPH, 82, 0},
+ { DBGBUS_PERIPH, 82, 1},
+ { DBGBUS_PERIPH, 82, 2},
+ { DBGBUS_PERIPH, 82, 3},
+ { DBGBUS_PERIPH, 82, 4},
+ { DBGBUS_PERIPH, 82, 5},
+ { DBGBUS_PERIPH, 82, 6},
+ { DBGBUS_PERIPH, 82, 7},
+
+ /* hdmi */
+ { DBGBUS_PERIPH, 68, 0},
+ { DBGBUS_PERIPH, 68, 1},
+ { DBGBUS_PERIPH, 68, 2},
+ { DBGBUS_PERIPH, 68, 3},
+ { DBGBUS_PERIPH, 68, 4},
+ { DBGBUS_PERIPH, 68, 5},
+
+ /* edp */
+ { DBGBUS_PERIPH, 69, 0},
+ { DBGBUS_PERIPH, 69, 1},
+ { DBGBUS_PERIPH, 69, 2},
+ { DBGBUS_PERIPH, 69, 3},
+ { DBGBUS_PERIPH, 69, 4},
+ { DBGBUS_PERIPH, 69, 5},
+
+ /* dsi0 */
+ { DBGBUS_PERIPH, 70, 0},
+ { DBGBUS_PERIPH, 70, 1},
+ { DBGBUS_PERIPH, 70, 2},
+ { DBGBUS_PERIPH, 70, 3},
+ { DBGBUS_PERIPH, 70, 4},
+ { DBGBUS_PERIPH, 70, 5},
+
+ /* dsi1 */
+ { DBGBUS_PERIPH, 71, 0},
+ { DBGBUS_PERIPH, 71, 1},
+ { DBGBUS_PERIPH, 71, 2},
+ { DBGBUS_PERIPH, 71, 3},
+ { DBGBUS_PERIPH, 71, 4},
+ { DBGBUS_PERIPH, 71, 5},
+};
+
+static struct vbif_debug_bus_entry vbif_dbg_bus_msm8998[] = {
+ {0x214, 0x21c, 16, 2, 0x0, 0xd}, /* arb clients */
+ {0x214, 0x21c, 16, 2, 0x80, 0xc0}, /* arb clients */
+ {0x214, 0x21c, 16, 2, 0x100, 0x140}, /* arb clients */
+ {0x214, 0x21c, 0, 16, 0x0, 0xf}, /* xin blocks - axi side */
+ {0x214, 0x21c, 0, 16, 0x80, 0xa4}, /* xin blocks - axi side */
+ {0x214, 0x21c, 0, 15, 0x100, 0x124}, /* xin blocks - axi side */
+ {0x21c, 0x214, 0, 14, 0, 0xc}, /* xin blocks - clock side */
+};
+
+/**
+ * _sde_dbg_enable_power - use callback to turn power on for hw register access
+ * @enable: whether to turn power on or off
+ */
+static inline void _sde_dbg_enable_power(int enable)
+{
+ if (!sde_dbg_base.power_ctrl.enable_fn)
+ return;
+ sde_dbg_base.power_ctrl.enable_fn(
+ sde_dbg_base.power_ctrl.handle,
+ sde_dbg_base.power_ctrl.client,
+ enable);
+}
+
+/**
+ * _sde_dump_reg - helper function for dumping rotator register set content
+ * @dump_name: register set name
+ * @reg_dump_flag: dumping flag controlling in-log/memory dump location
+ * @addr: starting address offset for dumping
+ * @len_bytes: range of the register set
+ * @dump_mem: output buffer for memory dump location option
+ * @from_isr: whether being called from isr context
+ */
+static void _sde_dump_reg(const char *dump_name, u32 reg_dump_flag, char *addr,
+ size_t len_bytes, u32 **dump_mem, bool from_isr)
+{
+ u32 in_log, in_mem, len_align_16, len_bytes_aligned;
+ u32 *dump_addr = NULL;
+ char *end_addr;
+ int i;
+
+ in_log = (reg_dump_flag & SDE_DBG_DUMP_IN_LOG);
+ in_mem = (reg_dump_flag & SDE_DBG_DUMP_IN_MEM);
+
+ pr_debug("reg_dump_flag=%d in_log=%d in_mem=%d\n",
+ reg_dump_flag, in_log, in_mem);
+
+ if (!in_log && !in_mem)
+ return;
+
+ len_align_16 = (len_bytes + 15) / 16;
+ len_bytes_aligned = len_align_16 * 16;
+ end_addr = addr + len_bytes;
+
+ if (in_mem) {
+ if (dump_mem && !(*dump_mem)) {
+ phys_addr_t phys = 0;
+ *dump_mem = dma_alloc_coherent(sde_dbg_base.dev,
+ len_bytes_aligned, &phys, GFP_KERNEL);
+ }
+
+ if (dump_mem && *dump_mem) {
+ dump_addr = *dump_mem;
+ pr_info("%s: start_addr:0x%pK end_addr:0x%pK reg_addr=0x%pK\n",
+ dump_name, dump_addr,
+ dump_addr + len_bytes_aligned, addr);
+ } else {
+ in_mem = 0;
+ pr_err("dump_mem: kzalloc fails!\n");
+ }
+ }
+
+ if (!from_isr)
+ _sde_dbg_enable_power(true);
+
+ for (i = 0; i < len_align_16; i++) {
+ u32 x0, x4, x8, xc;
+
+ x0 = (addr < end_addr) ? readl_relaxed(addr + 0x0) : 0;
+ x4 = (addr + 0x4 < end_addr) ? readl_relaxed(addr + 0x4) : 0;
+ x8 = (addr + 0x8 < end_addr) ? readl_relaxed(addr + 0x8) : 0;
+ xc = (addr + 0xc < end_addr) ? readl_relaxed(addr + 0xc) : 0;
+
+ if (in_log)
+ pr_info("%pK : %08x %08x %08x %08x\n", addr, x0, x4, x8,
+ xc);
+
+ if (dump_addr) {
+ dump_addr[i * 4] = x0;
+ dump_addr[i * 4 + 1] = x4;
+ dump_addr[i * 4 + 2] = x8;
+ dump_addr[i * 4 + 3] = xc;
+ }
+
+ addr += 16;
+ }
+
+ if (!from_isr)
+ _sde_dbg_enable_power(false);
+}
+
+/**
+ * _sde_dbg_get_dump_range - helper to retrieve dump length for a range node
+ * @range_node: range node to dump
+ * @max_offset: max offset of the register base
+ * @Return: length
+ */
+static u32 _sde_dbg_get_dump_range(struct sde_dbg_reg_offset *range_node,
+ size_t max_offset)
+{
+ u32 length = 0;
+
+ if ((range_node->start > range_node->end) ||
+ (range_node->end > max_offset) || (range_node->start == 0
+ && range_node->end == 0)) {
+ length = max_offset;
+ } else {
+ length = range_node->end - range_node->start;
+ }
+
+ return length;
+}
+
+/**
+ * _sde_dump_reg_by_ranges - dump ranges or full range of the register blk base
+ * @dbg: register blk base structure
+ * @reg_dump_flag: dump target, memory, kernel log, or both
+ */
+static void _sde_dump_reg_by_ranges(struct sde_dbg_reg_base *dbg,
+ u32 reg_dump_flag)
+{
+ char *addr;
+ size_t len;
+ struct sde_dbg_reg_range *range_node;
+
+ if (!dbg || !dbg->base) {
+ pr_err("dbg base is null!\n");
+ return;
+ }
+
+ pr_info("%s:=========%s DUMP=========\n", __func__, dbg->name);
+
+ /* If there is a list to dump the registers by ranges, use the ranges */
+ if (!list_empty(&dbg->sub_range_list)) {
+ list_for_each_entry(range_node, &dbg->sub_range_list, head) {
+ len = _sde_dbg_get_dump_range(&range_node->offset,
+ dbg->max_offset);
+ addr = dbg->base + range_node->offset.start;
+ pr_debug("%s: range_base=0x%pK start=0x%x end=0x%x\n",
+ range_node->range_name,
+ addr, range_node->offset.start,
+ range_node->offset.end);
+
+ _sde_dump_reg((const char *)range_node->range_name,
+ reg_dump_flag, addr, len, &range_node->reg_dump,
+ false);
+ }
+ } else {
+ /* If there is no list to dump ranges, dump all registers */
+ pr_info("Ranges not found, will dump full registers\n");
+ pr_info("base:0x%pK len:0x%zx\n", dbg->base, dbg->max_offset);
+ addr = dbg->base;
+ len = dbg->max_offset;
+ _sde_dump_reg((const char *)dbg->name, reg_dump_flag, addr,
+ len, &dbg->reg_dump, false);
+ }
+}
+
+/**
+ * _sde_dump_reg_by_blk - dump a named register base region
+ * @blk_name: register blk name
+ */
+static void _sde_dump_reg_by_blk(const char *blk_name)
+{
+ struct sde_dbg_base *dbg_base = &sde_dbg_base;
+ struct sde_dbg_reg_base *blk_base;
+
+ if (!dbg_base)
+ return;
+
+ list_for_each_entry(blk_base, &dbg_base->reg_base_list, reg_base_head) {
+ if (strlen(blk_base->name) &&
+ !strcmp(blk_base->name, blk_name)) {
+ _sde_dump_reg_by_ranges(blk_base,
+ dbg_base->enable_reg_dump);
+ break;
+ }
+ }
+}
+
+/**
+ * _sde_dump_reg_all - dump all register regions
+ */
+static void _sde_dump_reg_all(void)
+{
+ struct sde_dbg_base *dbg_base = &sde_dbg_base;
+ struct sde_dbg_reg_base *blk_base;
+
+ if (!dbg_base)
+ return;
+
+ list_for_each_entry(blk_base, &dbg_base->reg_base_list, reg_base_head)
+ if (strlen(blk_base->name))
+ _sde_dump_reg_by_blk(blk_base->name);
+}
+
+/**
+ * _sde_dump_get_blk_addr - retrieve register block address by name
+ * @blk_name: register blk name
+ * @Return: register blk base, or NULL
+ */
+static struct sde_dbg_reg_base *_sde_dump_get_blk_addr(const char *blk_name)
+{
+ struct sde_dbg_base *dbg_base = &sde_dbg_base;
+ struct sde_dbg_reg_base *blk_base;
+
+ list_for_each_entry(blk_base, &dbg_base->reg_base_list, reg_base_head)
+ if (strlen(blk_base->name) && !strcmp(blk_base->name, blk_name))
+ return blk_base;
+
+ return NULL;
+}
+
+static void _sde_dbg_dump_sde_dbg_bus(struct sde_dbg_sde_debug_bus *bus)
+{
+ bool in_log, in_mem;
+ u32 **dump_mem = NULL;
+ u32 *dump_addr = NULL;
+ u32 status = 0;
+ struct sde_debug_bus_entry *head;
+ phys_addr_t phys = 0;
+ int list_size;
+ int i;
+ u32 offset;
+ void __iomem *mem_base = NULL;
+ struct sde_dbg_reg_base *reg_base;
+
+ if (!bus || !bus->cmn.entries_size)
+ return;
+
+ list_for_each_entry(reg_base, &sde_dbg_base.reg_base_list,
+ reg_base_head)
+ if (strlen(reg_base->name) &&
+ !strcmp(reg_base->name, bus->cmn.name))
+ mem_base = reg_base->base;
+
+ if (!mem_base) {
+ pr_err("unable to find mem_base for %s\n", bus->cmn.name);
+ return;
+ }
+
+ dump_mem = &bus->cmn.dumped_content;
+
+ /* will keep in memory 4 entries of 4 bytes each */
+ list_size = (bus->cmn.entries_size * 4 * 4);
+
+ in_log = (bus->cmn.enable_mask & SDE_DBG_DUMP_IN_LOG);
+ in_mem = (bus->cmn.enable_mask & SDE_DBG_DUMP_IN_MEM);
+
+ if (!in_log && !in_mem)
+ return;
+
+ pr_info("======== start %s dump =========\n", bus->cmn.name);
+
+ if (in_mem) {
+ if (!(*dump_mem))
+ *dump_mem = dma_alloc_coherent(sde_dbg_base.dev,
+ list_size, &phys, GFP_KERNEL);
+
+ if (*dump_mem) {
+ dump_addr = *dump_mem;
+ pr_info("%s: start_addr:0x%pK end_addr:0x%pK\n",
+ __func__, dump_addr, dump_addr + list_size);
+ } else {
+ in_mem = false;
+ pr_err("dump_mem: allocation fails\n");
+ }
+ }
+
+ _sde_dbg_enable_power(true);
+ for (i = 0; i < bus->cmn.entries_size; i++) {
+ head = bus->entries + i;
+ writel_relaxed(TEST_MASK(head->block_id, head->test_id),
+ mem_base + head->wr_addr);
+ wmb(); /* make sure test bits were written */
+
+ if (bus->cmn.flags & DBGBUS_FLAGS_DSPP)
+ offset = DBGBUS_DSPP_STATUS;
+ else
+ offset = head->wr_addr + 0x4;
+
+ status = readl_relaxed(mem_base + offset);
+
+ if (in_log)
+ pr_err("waddr=0x%x blk=%d tst=%d val=0x%x\n",
+ head->wr_addr, head->block_id, head->test_id,
+ status);
+
+ if (dump_addr && in_mem) {
+ dump_addr[i*4] = head->wr_addr;
+ dump_addr[i*4 + 1] = head->block_id;
+ dump_addr[i*4 + 2] = head->test_id;
+ dump_addr[i*4 + 3] = status;
+ }
+
+ /* Disable debug bus once we are done */
+ writel_relaxed(0, mem_base + head->wr_addr);
+
+ }
+ _sde_dbg_enable_power(false);
+
+ pr_info("======== end %s dump =========\n", bus->cmn.name);
+}
+
+static void _sde_dbg_dump_vbif_debug_bus_entry(
+ struct vbif_debug_bus_entry *head, void __iomem *mem_base,
+ u32 *dump_addr, bool in_log)
+{
+ int i, j;
+ u32 val;
+
+ if (!dump_addr && !in_log)
+ return;
+
+ for (i = 0; i < head->block_cnt; i++) {
+ writel_relaxed(1 << (i + head->bit_offset),
+ mem_base + head->block_bus_addr);
+ /* make sure that current bus blcok enable */
+ wmb();
+ for (j = head->test_pnt_start; j < head->test_pnt_cnt; j++) {
+ writel_relaxed(j, mem_base + head->block_bus_addr + 4);
+ /* make sure that test point is enabled */
+ wmb();
+ val = readl_relaxed(mem_base + MMSS_VBIF_TEST_BUS_OUT);
+ if (dump_addr) {
+ *dump_addr++ = head->block_bus_addr;
+ *dump_addr++ = i;
+ *dump_addr++ = j;
+ *dump_addr++ = val;
+ }
+ if (in_log)
+ pr_err("testpoint:%x arb/xin id=%d index=%d val=0x%x\n",
+ head->block_bus_addr, i, j, val);
+ }
+ }
+}
+
+static void _sde_dbg_dump_vbif_dbg_bus(struct sde_dbg_vbif_debug_bus *bus)
+{
+ bool in_log, in_mem;
+ u32 **dump_mem = NULL;
+ u32 *dump_addr = NULL;
+ u32 value;
+ struct vbif_debug_bus_entry *head;
+ phys_addr_t phys = 0;
+ int i, list_size = 0;
+ void __iomem *mem_base = NULL;
+ struct vbif_debug_bus_entry *dbg_bus;
+ u32 bus_size;
+ struct sde_dbg_reg_base *reg_base;
+
+ if (!bus || !bus->cmn.entries_size)
+ return;
+
+ list_for_each_entry(reg_base, &sde_dbg_base.reg_base_list,
+ reg_base_head)
+ if (strlen(reg_base->name) &&
+ !strcmp(reg_base->name, bus->cmn.name))
+ mem_base = reg_base->base;
+
+ if (!mem_base) {
+ pr_err("unable to find mem_base for %s\n", bus->cmn.name);
+ return;
+ }
+
+ dbg_bus = bus->entries;
+ bus_size = bus->cmn.entries_size;
+ list_size = bus->cmn.entries_size;
+ dump_mem = &bus->cmn.dumped_content;
+
+ pr_info("======== start %s dump =========\n", bus->cmn.name);
+
+ if (!dump_mem || !dbg_bus || !bus_size || !list_size)
+ return;
+
+ /* allocate memory for each test point */
+ for (i = 0; i < bus_size; i++) {
+ head = dbg_bus + i;
+ list_size += (head->block_cnt * head->test_pnt_cnt);
+ }
+
+ /* 4 bytes * 4 entries for each test point*/
+ list_size *= 16;
+
+ in_log = (bus->cmn.enable_mask & SDE_DBG_DUMP_IN_LOG);
+ in_mem = (bus->cmn.enable_mask & SDE_DBG_DUMP_IN_MEM);
+
+ if (!in_log && !in_mem)
+ return;
+
+ if (in_mem) {
+ if (!(*dump_mem))
+ *dump_mem = dma_alloc_coherent(sde_dbg_base.dev,
+ list_size, &phys, GFP_KERNEL);
+
+ if (*dump_mem) {
+ dump_addr = *dump_mem;
+ pr_info("%s: start_addr:0x%pK end_addr:0x%pK\n",
+ __func__, dump_addr, dump_addr + list_size);
+ } else {
+ in_mem = false;
+ pr_err("dump_mem: allocation fails\n");
+ }
+ }
+
+ _sde_dbg_enable_power(true);
+
+ value = readl_relaxed(mem_base + MMSS_VBIF_CLKON);
+ writel_relaxed(value | BIT(1), mem_base + MMSS_VBIF_CLKON);
+
+ /* make sure that vbif core is on */
+ wmb();
+
+ for (i = 0; i < bus_size; i++) {
+ head = dbg_bus + i;
+
+ writel_relaxed(0, mem_base + head->disable_bus_addr);
+ writel_relaxed(BIT(0), mem_base + MMSS_VBIF_TEST_BUS_OUT_CTRL);
+ /* make sure that other bus is off */
+ wmb();
+
+ _sde_dbg_dump_vbif_debug_bus_entry(head, mem_base, dump_addr,
+ in_log);
+ if (dump_addr)
+ dump_addr += (head->block_cnt * head->test_pnt_cnt * 4);
+ }
+
+ _sde_dbg_enable_power(false);
+
+ pr_info("======== end %s dump =========\n", bus->cmn.name);
+}
+
+/**
+ * _sde_dump_array - dump array of register bases
+ * @blk_arr: array of register base pointers
+ * @len: length of blk_arr
+ * @do_panic: whether to trigger a panic after dumping
+ * @name: string indicating origin of dump
+ * @dump_dbgbus_sde: whether to dump the sde debug bus
+ * @dump_dbgbus_vbif_rt: whether to dump the vbif rt debug bus
+ */
+static void _sde_dump_array(struct sde_dbg_reg_base *blk_arr[],
+ u32 len, bool do_panic, const char *name, bool dump_dbgbus_sde,
+ bool dump_dbgbus_vbif_rt)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (blk_arr[i] != NULL)
+ _sde_dump_reg_by_ranges(blk_arr[i],
+ sde_dbg_base.enable_reg_dump);
+ }
+
+ sde_evtlog_dump_all(sde_dbg_base.evtlog);
+
+ if (dump_dbgbus_sde)
+ _sde_dbg_dump_sde_dbg_bus(&sde_dbg_base.dbgbus_sde);
+
+ if (dump_dbgbus_vbif_rt)
+ _sde_dbg_dump_vbif_dbg_bus(&sde_dbg_base.dbgbus_vbif_rt);
+
+ if (do_panic && sde_dbg_base.panic_on_err)
+ panic(name);
+}
+
+/**
+ * _sde_dump_work - deferred dump work function
+ * @work: work structure
+ */
+static void _sde_dump_work(struct work_struct *work)
+{
+ _sde_dump_array(sde_dbg_base.req_dump_blks,
+ ARRAY_SIZE(sde_dbg_base.req_dump_blks),
+ sde_dbg_base.work_panic, "evtlog_workitem",
+ sde_dbg_base.dbgbus_sde.cmn.include_in_deferred_work,
+ sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work);
+}
+
+void sde_dbg_dump(bool queue_work, const char *name, ...)
+{
+ int i, index = 0;
+ bool do_panic = false;
+ bool dump_dbgbus_sde = false;
+ bool dump_dbgbus_vbif_rt = false;
+ va_list args;
+ char *blk_name = NULL;
+ struct sde_dbg_reg_base *blk_base = NULL;
+ struct sde_dbg_reg_base **blk_arr;
+ u32 blk_len;
+
+ if (!sde_evtlog_is_enabled(sde_dbg_base.evtlog, SDE_EVTLOG_DEFAULT))
+ return;
+
+ if (queue_work && work_pending(&sde_dbg_base.dump_work))
+ return;
+
+ blk_arr = &sde_dbg_base.req_dump_blks[0];
+ blk_len = ARRAY_SIZE(sde_dbg_base.req_dump_blks);
+
+ memset(sde_dbg_base.req_dump_blks, 0,
+ sizeof(sde_dbg_base.req_dump_blks));
+
+ va_start(args, name);
+ for (i = 0; i < SDE_EVTLOG_MAX_DATA; i++) {
+ blk_name = va_arg(args, char*);
+ if (IS_ERR_OR_NULL(blk_name))
+ break;
+
+ blk_base = _sde_dump_get_blk_addr(blk_name);
+ if (blk_base) {
+ if (index < blk_len) {
+ blk_arr[index] = blk_base;
+ index++;
+ } else {
+ pr_err("insufficient space to to dump %s\n",
+ blk_name);
+ }
+ }
+
+ if (!strcmp(blk_name, "dbg_bus"))
+ dump_dbgbus_sde = true;
+
+ if (!strcmp(blk_name, "vbif_dbg_bus"))
+ dump_dbgbus_vbif_rt = true;
+
+ if (!strcmp(blk_name, "panic"))
+ do_panic = true;
+ }
+ blk_name = va_arg(args, char*);
+ if (!IS_ERR_OR_NULL(blk_name))
+ pr_err("could not parse all dump arguments\n");
+ va_end(args);
+
+ if (queue_work) {
+ /* schedule work to dump later */
+ sde_dbg_base.work_panic = do_panic;
+ sde_dbg_base.dbgbus_sde.cmn.include_in_deferred_work =
+ dump_dbgbus_sde;
+ sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work =
+ dump_dbgbus_vbif_rt;
+ schedule_work(&sde_dbg_base.dump_work);
+ } else {
+ _sde_dump_array(blk_arr, blk_len, do_panic, name,
+ dump_dbgbus_sde, dump_dbgbus_vbif_rt);
+ }
+}
+
+/*
+ * sde_dbg_debugfs_open - debugfs open handler for evtlog dump
+ * @inode: debugfs inode
+ * @file: file handle
+ */
+static int sde_dbg_debugfs_open(struct inode *inode, struct file *file)
+{
+ /* non-seekable */
+ file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+/**
+ * sde_evtlog_dump_read - debugfs read handler for evtlog dump
+ * @file: file handler
+ * @buff: user buffer content for debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_evtlog_dump_read(struct file *file, char __user *buff,
+ size_t count, loff_t *ppos)
+{
+ ssize_t len = 0;
+ char evtlog_buf[SDE_EVTLOG_BUF_MAX];
+
+ len = sde_evtlog_dump_to_buffer(sde_dbg_base.evtlog, evtlog_buf,
+ SDE_EVTLOG_BUF_MAX);
+ if (copy_to_user(buff, evtlog_buf, len))
+ return -EFAULT;
+ *ppos += len;
+
+ return len;
+}
+
+/**
+ * sde_evtlog_dump_write - debugfs write handler for evtlog dump
+ * @file: file handler
+ * @user_buf: user buffer content from debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_evtlog_dump_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ _sde_dump_reg_all();
+
+ sde_evtlog_dump_all(sde_dbg_base.evtlog);
+
+ _sde_dbg_dump_sde_dbg_bus(&sde_dbg_base.dbgbus_sde);
+ _sde_dbg_dump_vbif_dbg_bus(&sde_dbg_base.dbgbus_vbif_rt);
+
+ if (sde_dbg_base.panic_on_err)
+ panic("sde");
+
+ return count;
+}
+
+static const struct file_operations sde_evtlog_fops = {
+ .open = sde_dbg_debugfs_open,
+ .read = sde_evtlog_dump_read,
+ .write = sde_evtlog_dump_write,
+};
+
+void sde_dbg_init_dbg_buses(u32 hwversion)
+{
+ static struct sde_dbg_base *dbg = &sde_dbg_base;
+ char debug_name[80] = "";
+
+ memset(&dbg->dbgbus_sde, 0, sizeof(dbg->dbgbus_sde));
+ memset(&dbg->dbgbus_vbif_rt, 0, sizeof(dbg->dbgbus_vbif_rt));
+
+ switch (hwversion) {
+ case SDE_HW_VER_300:
+ case SDE_HW_VER_301:
+ dbg->dbgbus_sde.entries = dbg_bus_sde_8998;
+ dbg->dbgbus_sde.cmn.entries_size = ARRAY_SIZE(dbg_bus_sde_8998);
+ dbg->dbgbus_sde.cmn.flags = DBGBUS_FLAGS_DSPP;
+
+ dbg->dbgbus_vbif_rt.entries = vbif_dbg_bus_msm8998;
+ dbg->dbgbus_vbif_rt.cmn.entries_size =
+ ARRAY_SIZE(vbif_dbg_bus_msm8998);
+ break;
+
+ case SDE_HW_VER_400:
+ dbg->dbgbus_sde.entries = dbg_bus_sde_sdm845;
+ dbg->dbgbus_sde.cmn.entries_size =
+ ARRAY_SIZE(dbg_bus_sde_sdm845);
+ dbg->dbgbus_sde.cmn.flags = DBGBUS_FLAGS_DSPP;
+
+ /* vbif is unchanged vs 8998 */
+ dbg->dbgbus_vbif_rt.entries = vbif_dbg_bus_msm8998;
+ dbg->dbgbus_vbif_rt.cmn.entries_size =
+ ARRAY_SIZE(vbif_dbg_bus_msm8998);
+ break;
+ default:
+ pr_err("unsupported chipset id %u\n", hwversion);
+ break;
+ }
+
+ if (dbg->dbgbus_sde.entries) {
+ dbg->dbgbus_sde.cmn.name = DBGBUS_NAME_SDE;
+ snprintf(debug_name, sizeof(debug_name), "%s_dbgbus",
+ dbg->dbgbus_sde.cmn.name);
+ dbg->dbgbus_sde.cmn.enable_mask = DEFAULT_DBGBUS_SDE;
+ debugfs_create_u32(debug_name, 0644, dbg->root,
+ &dbg->dbgbus_sde.cmn.enable_mask);
+ }
+
+ if (dbg->dbgbus_vbif_rt.entries) {
+ dbg->dbgbus_vbif_rt.cmn.name = DBGBUS_NAME_VBIF_RT;
+ snprintf(debug_name, sizeof(debug_name), "%s_dbgbus",
+ dbg->dbgbus_vbif_rt.cmn.name);
+ dbg->dbgbus_vbif_rt.cmn.enable_mask = DEFAULT_DBGBUS_VBIFRT;
+ debugfs_create_u32(debug_name, 0644, dbg->root,
+ &dbg->dbgbus_vbif_rt.cmn.enable_mask);
+ }
+}
+
+int sde_dbg_init(struct dentry *debugfs_root, struct device *dev,
+ struct sde_dbg_power_ctrl *power_ctrl)
+{
+ int i;
+
+ INIT_LIST_HEAD(&sde_dbg_base.reg_base_list);
+ sde_dbg_base.dev = dev;
+ sde_dbg_base.power_ctrl = *power_ctrl;
+
+
+ sde_dbg_base.evtlog = sde_evtlog_init();
+ if (IS_ERR_OR_NULL(sde_dbg_base.evtlog))
+ return PTR_ERR(sde_dbg_base.evtlog);
+
+ sde_dbg_base_evtlog = sde_dbg_base.evtlog;
+
+ sde_dbg_base.root = debugfs_create_dir("evt_dbg", debugfs_root);
+ if (IS_ERR_OR_NULL(sde_dbg_base.root)) {
+ pr_err("debugfs_create_dir fail, error %ld\n",
+ PTR_ERR(sde_dbg_base.root));
+ sde_dbg_base.root = NULL;
+ return -ENODEV;
+ }
+
+ INIT_WORK(&sde_dbg_base.dump_work, _sde_dump_work);
+ sde_dbg_base.work_panic = false;
+
+ for (i = 0; i < SDE_EVTLOG_ENTRY; i++)
+ sde_dbg_base.evtlog->logs[i].counter = i;
+
+ debugfs_create_file("dump", 0644, sde_dbg_base.root, NULL,
+ &sde_evtlog_fops);
+ debugfs_create_u32("enable", 0644, sde_dbg_base.root,
+ &(sde_dbg_base.evtlog->enable));
+ debugfs_create_u32("panic", 0644, sde_dbg_base.root,
+ &sde_dbg_base.panic_on_err);
+ debugfs_create_u32("reg_dump", 0644, sde_dbg_base.root,
+ &sde_dbg_base.enable_reg_dump);
+
+ sde_dbg_base.panic_on_err = DEFAULT_PANIC;
+ sde_dbg_base.enable_reg_dump = DEFAULT_REGDUMP;
+
+ pr_info("evtlog_status: enable:%d, panic:%d, dump:%d\n",
+ sde_dbg_base.evtlog->enable, sde_dbg_base.panic_on_err,
+ sde_dbg_base.enable_reg_dump);
+
+ return 0;
+}
+
+/**
+ * sde_dbg_destroy - destroy sde debug facilities
+ */
+void sde_dbg_destroy(void)
+{
+ debugfs_remove_recursive(sde_dbg_base.root);
+ sde_dbg_base.root = 0;
+
+ sde_dbg_base_evtlog = NULL;
+ sde_evtlog_destroy(sde_dbg_base.evtlog);
+ sde_dbg_base.evtlog = NULL;
+}
+
+/**
+ * sde_dbg_reg_base_release - release allocated reg dump file private data
+ * @inode: debugfs inode
+ * @file: file handle
+ * @Return: 0 on success
+ */
+static int sde_dbg_reg_base_release(struct inode *inode, struct file *file)
+{
+ struct sde_dbg_reg_base *dbg = file->private_data;
+
+ if (dbg && dbg->buf) {
+ kfree(dbg->buf);
+ dbg->buf_len = 0;
+ dbg->buf = NULL;
+ }
+ return 0;
+}
+
+
+/**
+ * sde_dbg_reg_base_offset_write - set new offset and len to debugfs reg base
+ * @file: file handler
+ * @user_buf: user buffer content from debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_dbg_reg_base_offset_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct sde_dbg_reg_base *dbg = file->private_data;
+ u32 off = 0;
+ u32 cnt = DEFAULT_BASE_REG_CNT;
+ char buf[24];
+
+ if (!dbg)
+ return -ENODEV;
+
+ if (count >= sizeof(buf))
+ return -EFAULT;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ buf[count] = 0; /* end of string */
+
+ if (sscanf(buf, "%5x %x", &off, &cnt) != 2)
+ return -EFAULT;
+
+ if (off > dbg->max_offset)
+ return -EINVAL;
+
+ if (cnt > (dbg->max_offset - off))
+ cnt = dbg->max_offset - off;
+
+ dbg->off = off;
+ dbg->cnt = cnt;
+
+ pr_debug("offset=%x cnt=%x\n", off, cnt);
+
+ return count;
+}
+
+/**
+ * sde_dbg_reg_base_offset_read - read current offset and len of register base
+ * @file: file handler
+ * @user_buf: user buffer content from debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_dbg_reg_base_offset_read(struct file *file,
+ char __user *buff, size_t count, loff_t *ppos)
+{
+ struct sde_dbg_reg_base *dbg = file->private_data;
+ int len = 0;
+ char buf[24] = {'\0'};
+
+ if (!dbg)
+ return -ENODEV;
+
+ if (*ppos)
+ return 0; /* the end */
+
+ len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt);
+ if (len < 0 || len >= sizeof(buf))
+ return 0;
+
+ if ((count < sizeof(buf)) || copy_to_user(buff, buf, len))
+ return -EFAULT;
+
+ *ppos += len; /* increase offset */
+
+ return len;
+}
+
+/**
+ * sde_dbg_reg_base_reg_write - write to reg base hw at offset a given value
+ * @file: file handler
+ * @user_buf: user buffer content from debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_dbg_reg_base_reg_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct sde_dbg_reg_base *dbg = file->private_data;
+ size_t off;
+ u32 data, cnt;
+ char buf[24];
+
+ if (!dbg)
+ return -ENODEV;
+
+ if (count >= sizeof(buf))
+ return -EFAULT;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ buf[count] = 0; /* end of string */
+
+ cnt = sscanf(buf, "%zx %x", &off, &data);
+
+ if (cnt < 2)
+ return -EFAULT;
+
+ if (off >= dbg->max_offset)
+ return -EFAULT;
+
+ _sde_dbg_enable_power(true);
+
+ writel_relaxed(data, dbg->base + off);
+
+ _sde_dbg_enable_power(false);
+
+ pr_debug("addr=%zx data=%x\n", off, data);
+
+ return count;
+}
+
+/**
+ * sde_dbg_reg_base_reg_read - read len from reg base hw at current offset
+ * @file: file handler
+ * @user_buf: user buffer content from debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_dbg_reg_base_reg_read(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct sde_dbg_reg_base *dbg = file->private_data;
+ size_t len;
+
+ if (!dbg) {
+ pr_err("invalid handle\n");
+ return -ENODEV;
+ }
+
+ if (!dbg->buf) {
+ char dump_buf[64];
+ char *ptr;
+ int cnt, tot;
+
+ dbg->buf_len = sizeof(dump_buf) *
+ DIV_ROUND_UP(dbg->cnt, ROW_BYTES);
+ dbg->buf = kzalloc(dbg->buf_len, GFP_KERNEL);
+
+ if (!dbg->buf)
+ return -ENOMEM;
+
+ ptr = dbg->base + dbg->off;
+ tot = 0;
+
+ _sde_dbg_enable_power(true);
+
+ for (cnt = dbg->cnt; cnt > 0; cnt -= ROW_BYTES) {
+ hex_dump_to_buffer(ptr, min(cnt, ROW_BYTES),
+ ROW_BYTES, GROUP_BYTES, dump_buf,
+ sizeof(dump_buf), false);
+ len = scnprintf(dbg->buf + tot, dbg->buf_len - tot,
+ "0x%08x: %s\n",
+ ((int) (unsigned long) ptr) -
+ ((int) (unsigned long) dbg->base),
+ dump_buf);
+
+ ptr += ROW_BYTES;
+ tot += len;
+ if (tot >= dbg->buf_len)
+ break;
+ }
+
+ _sde_dbg_enable_power(false);
+
+ dbg->buf_len = tot;
+ }
+
+ if (*ppos >= dbg->buf_len)
+ return 0; /* done reading */
+
+ len = min(count, dbg->buf_len - (size_t) *ppos);
+ if (copy_to_user(user_buf, dbg->buf + *ppos, len)) {
+ pr_err("failed to copy to user\n");
+ return -EFAULT;
+ }
+
+ *ppos += len; /* increase offset */
+
+ return len;
+}
+
+static const struct file_operations sde_off_fops = {
+ .open = sde_dbg_debugfs_open,
+ .release = sde_dbg_reg_base_release,
+ .read = sde_dbg_reg_base_offset_read,
+ .write = sde_dbg_reg_base_offset_write,
+};
+
+static const struct file_operations sde_reg_fops = {
+ .open = sde_dbg_debugfs_open,
+ .release = sde_dbg_reg_base_release,
+ .read = sde_dbg_reg_base_reg_read,
+ .write = sde_dbg_reg_base_reg_write,
+};
+
+int sde_dbg_reg_register_base(const char *name, void __iomem *base,
+ size_t max_offset)
+{
+ struct sde_dbg_base *dbg_base = &sde_dbg_base;
+ struct sde_dbg_reg_base *reg_base;
+ struct dentry *ent_off, *ent_reg;
+ char dn[80] = "";
+ int prefix_len = 0;
+
+ reg_base = kzalloc(sizeof(*reg_base), GFP_KERNEL);
+ if (!reg_base)
+ return -ENOMEM;
+
+ if (name)
+ strlcpy(reg_base->name, name, sizeof(reg_base->name));
+ reg_base->base = base;
+ reg_base->max_offset = max_offset;
+ reg_base->off = 0;
+ reg_base->cnt = DEFAULT_BASE_REG_CNT;
+ reg_base->reg_dump = NULL;
+
+ if (name)
+ prefix_len = snprintf(dn, sizeof(dn), "%s_", name);
+ strlcpy(dn + prefix_len, "off", sizeof(dn) - prefix_len);
+ ent_off = debugfs_create_file(dn, 0644, dbg_base->root, reg_base,
+ &sde_off_fops);
+ if (IS_ERR_OR_NULL(ent_off)) {
+ pr_err("debugfs_create_file: offset fail\n");
+ goto off_fail;
+ }
+
+ strlcpy(dn + prefix_len, "reg", sizeof(dn) - prefix_len);
+ ent_reg = debugfs_create_file(dn, 0644, dbg_base->root, reg_base,
+ &sde_reg_fops);
+ if (IS_ERR_OR_NULL(ent_reg)) {
+ pr_err("debugfs_create_file: reg fail\n");
+ goto reg_fail;
+ }
+
+ /* Initialize list to make sure check for null list will be valid */
+ INIT_LIST_HEAD(®_base->sub_range_list);
+
+ pr_debug("%s base: %pK max_offset 0x%zX\n", reg_base->name,
+ reg_base->base, reg_base->max_offset);
+
+ list_add(®_base->reg_base_head, &dbg_base->reg_base_list);
+
+ return 0;
+reg_fail:
+ debugfs_remove(ent_off);
+off_fail:
+ kfree(reg_base);
+ return -ENODEV;
+}
+
+void sde_dbg_reg_register_dump_range(const char *base_name,
+ const char *range_name, u32 offset_start, u32 offset_end,
+ uint32_t xin_id)
+{
+ struct sde_dbg_reg_base *reg_base;
+ struct sde_dbg_reg_range *range;
+
+ reg_base = _sde_dump_get_blk_addr(base_name);
+ if (!reg_base) {
+ pr_err("error: for range %s unable to locate base %s\n",
+ range_name, base_name);
+ return;
+ }
+
+ range = kzalloc(sizeof(*range), GFP_KERNEL);
+ if (!range)
+ return;
+
+ strlcpy(range->range_name, range_name, sizeof(range->range_name));
+ range->offset.start = offset_start;
+ range->offset.end = offset_end;
+ range->xin_id = xin_id;
+ list_add_tail(&range->head, ®_base->sub_range_list);
+
+ pr_debug("%s start: 0x%X end: 0x%X\n", range->range_name,
+ range->offset.start, range->offset.end);
+}
diff --git a/drivers/gpu/drm/msm/sde_dbg.h b/drivers/gpu/drm/msm/sde_dbg.h
index 271c41f..59da9fd 100644
--- a/drivers/gpu/drm/msm/sde_dbg.h
+++ b/drivers/gpu/drm/msm/sde_dbg.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,34 +29,279 @@
SDE_EVTLOG_ALL = BIT(7)
};
+enum sde_dbg_dump_flag {
+ SDE_DBG_DUMP_IN_LOG = BIT(0),
+ SDE_DBG_DUMP_IN_MEM = BIT(1),
+};
+
+#ifdef CONFIG_DRM_SDE_EVTLOG_DEBUG
+#define SDE_EVTLOG_DEFAULT_ENABLE 1
+#else
+#define SDE_EVTLOG_DEFAULT_ENABLE 0
+#endif
+
+/*
+ * evtlog will print this number of entries when it is called through
+ * sysfs node or panic. This prevents kernel log from evtlog message
+ * flood.
+ */
+#define SDE_EVTLOG_PRINT_ENTRY 256
+
+/*
+ * evtlog keeps this number of entries in memory for debug purpose. This
+ * number must be greater than print entry to prevent out of bound evtlog
+ * entry array access.
+ */
+#define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 4)
+#define SDE_EVTLOG_MAX_DATA 15
+#define SDE_EVTLOG_BUF_MAX 512
+#define SDE_EVTLOG_BUF_ALIGN 32
+
+struct sde_dbg_power_ctrl {
+ void *handle;
+ void *client;
+ int (*enable_fn)(void *handle, void *client, bool enable);
+};
+
+struct sde_dbg_evtlog_log {
+ u32 counter;
+ s64 time;
+ const char *name;
+ int line;
+ u32 data[SDE_EVTLOG_MAX_DATA];
+ u32 data_cnt;
+ int pid;
+};
+
+struct sde_dbg_evtlog {
+ struct sde_dbg_evtlog_log logs[SDE_EVTLOG_ENTRY];
+ u32 first;
+ u32 last;
+ u32 curr;
+ u32 next;
+ u32 enable;
+ spinlock_t spin_lock;
+};
+
+extern struct sde_dbg_evtlog *sde_dbg_base_evtlog;
+
/**
- * SDE_EVT32 - Write an list of 32bit values as an event into the event log
+ * SDE_EVT32 - Write a list of 32bit values to the event log, default area
* ... - variable arguments
*/
-#define SDE_EVT32(...) sde_evtlog(__func__, __LINE__, SDE_EVTLOG_DEFAULT, \
- ##__VA_ARGS__, SDE_EVTLOG_DATA_LIMITER)
-#define SDE_EVT32_IRQ(...) sde_evtlog(__func__, __LINE__, SDE_EVTLOG_IRQ, \
- ##__VA_ARGS__, SDE_EVTLOG_DATA_LIMITER)
+#define SDE_EVT32(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \
+ __LINE__, SDE_EVTLOG_DEFAULT, ##__VA_ARGS__, \
+ SDE_EVTLOG_DATA_LIMITER)
-#define SDE_DBG_DUMP(...) \
- sde_dbg_dump(false, __func__, ##__VA_ARGS__, \
+/**
+ * SDE_EVT32_IRQ - Write a list of 32bit values to the event log, IRQ area
+ * ... - variable arguments
+ */
+#define SDE_EVT32_IRQ(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \
+ __LINE__, SDE_EVTLOG_IRQ, ##__VA_ARGS__, \
+ SDE_EVTLOG_DATA_LIMITER)
+
+/**
+ * SDE_DBG_DUMP - trigger dumping of all sde_dbg facilities
+ * @va_args: list of named register dump ranges and regions to dump, as
+ * registered previously through sde_dbg_reg_register_base and
+ * sde_dbg_reg_register_dump_range.
+ * Including the special name "panic" will trigger a panic after
+ * the dumping work has completed.
+ */
+#define SDE_DBG_DUMP(...) sde_dbg_dump(false, __func__, ##__VA_ARGS__, \
SDE_DBG_DUMP_DATA_LIMITER)
-#define SDE_DBG_DUMP_WQ(...) \
- sde_dbg_dump(true, __func__, ##__VA_ARGS__, \
+/**
+ * SDE_DBG_DUMP_WQ - trigger dumping of all sde_dbg facilities, queuing the work
+ * @va_args: list of named register dump ranges and regions to dump, as
+ * registered previously through sde_dbg_reg_register_base and
+ * sde_dbg_reg_register_dump_range.
+ * Including the special name "panic" will trigger a panic after
+ * the dumping work has completed.
+ */
+#define SDE_DBG_DUMP_WQ(...) sde_dbg_dump(true, __func__, ##__VA_ARGS__, \
SDE_DBG_DUMP_DATA_LIMITER)
#if defined(CONFIG_DEBUG_FS)
-int sde_evtlog_init(struct dentry *debugfs_root);
-void sde_evtlog_destroy(void);
-void sde_evtlog(const char *name, int line, int flag, ...);
-void sde_dbg_dump(bool queue, const char *name, ...);
+/**
+ * sde_evtlog_init - allocate a new event log object
+ * Returns: evtlog or -ERROR
+ */
+struct sde_dbg_evtlog *sde_evtlog_init(void);
+
+/**
+ * sde_evtlog_destroy - destroy previously allocated event log
+ * @evtlog: pointer to evtlog
+ * Returns: none
+ */
+void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog);
+
+/**
+ * sde_evtlog_log - log an entry into the event log.
+ * log collection may be enabled/disabled entirely via debugfs
+ * log area collection may be filtered by user provided flags via debugfs.
+ * @evtlog: pointer to evtlog
+ * @name: function name of call site
+ * @line: line number of call site
+ * @flag: log area filter flag checked against user's debugfs request
+ * Returns: none
+ */
+void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, const char *name, int line,
+ int flag, ...);
+
+/**
+ * sde_evtlog_dump_all - print all entries in event log to kernel log
+ * @evtlog: pointer to evtlog
+ * Returns: none
+ */
+void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog);
+
+/**
+ * sde_evtlog_is_enabled - check whether log collection is enabled for given
+ * event log and log area flag
+ * @evtlog: pointer to evtlog
+ * @flag: log area filter flag
+ * Returns: none
+ */
+bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag);
+
+/**
+ * sde_evtlog_dump_to_buffer - print content of event log to the given buffer
+ * @evtlog: pointer to evtlog
+ * @evtlog_buf: target buffer to print into
+ * @evtlog_buf_size: size of target buffer
+ * Returns: number of bytes written to buffer
+ */
+ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
+ char *evtlog_buf, ssize_t evtlog_buf_size);
+
+/**
+ * sde_dbg_init_dbg_buses - initialize debug bus dumping support for the chipset
+ * @hwversion: Chipset revision
+ */
+void sde_dbg_init_dbg_buses(u32 hwversion);
+
+/**
+ * sde_dbg_init - initialize global sde debug facilities: evtlog, regdump
+ * @debugfs_root: debugfs root in which to create sde debug entries
+ * @dev: device handle
+ * @power_ctrl: power control callback structure for enabling clocks
+ * during register dumping
+ * Returns: 0 or -ERROR
+ */
+int sde_dbg_init(struct dentry *debugfs_root, struct device *dev,
+ struct sde_dbg_power_ctrl *power_ctrl);
+
+/**
+ * sde_dbg_destroy - destroy the global sde debug facilities
+ * Returns: none
+ */
+void sde_dbg_destroy(void);
+
+/**
+ * sde_dbg_dump - trigger dumping of all sde_dbg facilities
+ * @queue_work: whether to queue the dumping work to the work_struct
+ * @name: string indicating origin of dump
+ * @va_args: list of named register dump ranges and regions to dump, as
+ * registered previously through sde_dbg_reg_register_base and
+ * sde_dbg_reg_register_dump_range.
+ * Including the special name "panic" will trigger a panic after
+ * the dumping work has completed.
+ * Returns: none
+ */
+void sde_dbg_dump(bool queue_work, const char *name, ...);
+
+/**
+ * sde_dbg_reg_register_base - register a hw register address section for later
+ * dumping. call this before calling sde_dbg_reg_register_dump_range
+ * to be able to specify sub-ranges within the base hw range.
+ * @name: name of base region
+ * @base: base pointer of region
+ * @max_offset: length of region
+ * Returns: 0 or -ERROR
+ */
+int sde_dbg_reg_register_base(const char *name, void __iomem *base,
+ size_t max_offset);
+
+/**
+ * sde_dbg_reg_register_dump_range - register a hw register sub-region for
+ * later register dumping associated with base specified by
+ * sde_dbg_reg_register_base
+ * @base_name: name of base region
+ * @range_name: name of sub-range within base region
+ * @offset_start: sub-range's start offset from base's base pointer
+ * @offset_end: sub-range's end offset from base's base pointer
+ * @xin_id: xin id
+ * Returns: none
+ */
+void sde_dbg_reg_register_dump_range(const char *base_name,
+ const char *range_name, u32 offset_start, u32 offset_end,
+ uint32_t xin_id);
+
#else
-static inline int sde_evtlog_init(struct dentry *debugfs_root) { return 0; }
-static inline void sde_evtlog(const char *name, int line, flag, ...) {}
-static inline void sde_evtlog_destroy(void) { }
-static inline void sde_dbg_dump(bool queue, const char *name, ...) {}
-#endif
+static inline struct sde_dbg_evtlog *sde_evtlog_init(void)
+{
+ return NULL;
+}
+
+static inline void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog)
+{
+}
+
+static inline void sde_evtlog_log(struct sde_dbg_evtlog *evtlog,
+ const char *name, int line, int flag, ...)
+{
+}
+
+static inline void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog)
+{
+}
+
+static inline bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog,
+ u32 flag)
+{
+ return false;
+}
+
+static inline ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
+ char *evtlog_buf, ssize_t evtlog_buf_size)
+{
+ return 0;
+}
+
+void sde_dbg_init_dbg_buses(u32 hwversion)
+{
+}
+
+static inline int sde_dbg_init(struct dentry *debugfs_root, struct device *dev,
+ struct sde_dbg_power_ctrl *power_ctrl)
+{
+ return 0;
+}
+
+static inline void sde_dbg_destroy(void)
+{
+}
+
+static inline void sde_dbg_dump(bool queue_work, const char *name, ...)
+{
+}
+
+static inline int sde_dbg_reg_register_base(const char *name,
+ void __iomem *base, size_t max_offset)
+{
+ return 0;
+}
+
+static inline void sde_dbg_reg_register_dump_range(const char *base_name,
+ const char *range_name, u32 offset_start, u32 offset_end,
+ uint32_t xin_id)
+{
+}
+
+#endif /* defined(CONFIG_DEBUG_FS) */
+
#endif /* SDE_DBG_H_ */
diff --git a/drivers/gpu/drm/msm/sde_dbg_evtlog.c b/drivers/gpu/drm/msm/sde_dbg_evtlog.c
index 7283277..759bdab 100644
--- a/drivers/gpu/drm/msm/sde_dbg_evtlog.c
+++ b/drivers/gpu/drm/msm/sde_dbg_evtlog.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-#define pr_fmt(fmt) "sde_evtlog:[%s] " fmt, __func__
+#define pr_fmt(fmt) "sde_dbg:[%s] " fmt, __func__
#include <linux/delay.h>
#include <linux/spinlock.h>
@@ -18,77 +18,36 @@
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/dma-buf.h>
+#include <linux/slab.h>
#include "sde_dbg.h"
#include "sde_trace.h"
-#ifdef CONFIG_DRM_SDE_EVTLOG_DEBUG
-#define SDE_EVTLOG_DEFAULT_ENABLE 1
-#else
-#define SDE_EVTLOG_DEFAULT_ENABLE 0
-#endif
-
-#define SDE_DBG_DEFAULT_PANIC 1
-
-/*
- * evtlog will print this number of entries when it is called through
- * sysfs node or panic. This prevents kernel log from evtlog message
- * flood.
- */
-#define SDE_EVTLOG_PRINT_ENTRY 256
-
-/*
- * evtlog keeps this number of entries in memory for debug purpose. This
- * number must be greater than print entry to prevent out of bound evtlog
- * entry array access.
- */
-#define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 4)
-#define SDE_EVTLOG_MAX_DATA 15
-#define SDE_EVTLOG_BUF_MAX 512
-#define SDE_EVTLOG_BUF_ALIGN 32
-
-DEFINE_SPINLOCK(sde_evtloglock);
-
-struct tlog {
- u32 counter;
- s64 time;
- const char *name;
- int line;
- u32 data[SDE_EVTLOG_MAX_DATA];
- u32 data_cnt;
- int pid;
-};
-
-static struct sde_dbg_evtlog {
- struct tlog logs[SDE_EVTLOG_ENTRY];
- u32 first;
- u32 last;
- u32 curr;
- struct dentry *evtlog;
- u32 evtlog_enable;
- u32 panic_on_err;
- struct work_struct evtlog_dump_work;
- bool work_panic;
-} sde_dbg_evtlog;
-
-static inline bool sde_evtlog_is_enabled(u32 flag)
+bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag)
{
- return (flag & sde_dbg_evtlog.evtlog_enable) ||
- (flag == SDE_EVTLOG_ALL && sde_dbg_evtlog.evtlog_enable);
+ if (!evtlog)
+ return false;
+
+ return (flag & evtlog->enable) ||
+ (flag == SDE_EVTLOG_ALL && evtlog->enable);
}
-void sde_evtlog(const char *name, int line, int flag, ...)
+void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, const char *name, int line,
+ int flag, ...)
{
unsigned long flags;
int i, val = 0;
va_list args;
- struct tlog *log;
+ struct sde_dbg_evtlog_log *log;
- if (!sde_evtlog_is_enabled(flag))
+ if (!evtlog)
return;
- spin_lock_irqsave(&sde_evtloglock, flags);
- log = &sde_dbg_evtlog.logs[sde_dbg_evtlog.curr];
+ if (!sde_evtlog_is_enabled(evtlog, flag))
+ return;
+
+ spin_lock_irqsave(&evtlog->spin_lock, flags);
+ log = &evtlog->logs[evtlog->curr];
log->time = ktime_to_us(ktime_get());
log->name = name;
log->line = line;
@@ -106,26 +65,27 @@
}
va_end(args);
log->data_cnt = i;
- sde_dbg_evtlog.curr = (sde_dbg_evtlog.curr + 1) % SDE_EVTLOG_ENTRY;
- sde_dbg_evtlog.last++;
+ evtlog->curr = (evtlog->curr + 1) % SDE_EVTLOG_ENTRY;
+ evtlog->last++;
trace_sde_evtlog(name, line, i > 0 ? log->data[0] : 0,
i > 1 ? log->data[1] : 0);
- spin_unlock_irqrestore(&sde_evtloglock, flags);
+ spin_unlock_irqrestore(&evtlog->spin_lock, flags);
}
/* always dump the last entries which are not dumped yet */
-static bool _sde_evtlog_dump_calc_range(void)
+static bool _sde_evtlog_dump_calc_range(struct sde_dbg_evtlog *evtlog)
{
- static u32 next;
bool need_dump = true;
unsigned long flags;
- struct sde_dbg_evtlog *evtlog = &sde_dbg_evtlog;
- spin_lock_irqsave(&sde_evtloglock, flags);
+ if (!evtlog)
+ return false;
- evtlog->first = next;
+ spin_lock_irqsave(&evtlog->spin_lock, flags);
+
+ evtlog->first = evtlog->next;
if (evtlog->last == evtlog->first) {
need_dump = false;
@@ -143,27 +103,34 @@
evtlog->last - evtlog->first);
evtlog->first = evtlog->last - SDE_EVTLOG_PRINT_ENTRY;
}
- next = evtlog->first + 1;
+ evtlog->next = evtlog->first + 1;
dump_exit:
- spin_unlock_irqrestore(&sde_evtloglock, flags);
+ spin_unlock_irqrestore(&evtlog->spin_lock, flags);
return need_dump;
}
-static ssize_t sde_evtlog_dump_entry(char *evtlog_buf, ssize_t evtlog_buf_size)
+ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
+ char *evtlog_buf, ssize_t evtlog_buf_size)
{
int i;
ssize_t off = 0;
- struct tlog *log, *prev_log;
+ struct sde_dbg_evtlog_log *log, *prev_log;
unsigned long flags;
- spin_lock_irqsave(&sde_evtloglock, flags);
+ if (!evtlog || !evtlog_buf)
+ return 0;
- log = &sde_dbg_evtlog.logs[sde_dbg_evtlog.first %
- SDE_EVTLOG_ENTRY];
+ /* update markers, exit if nothing to print */
+ if (!_sde_evtlog_dump_calc_range(evtlog))
+ return 0;
- prev_log = &sde_dbg_evtlog.logs[(sde_dbg_evtlog.first - 1) %
+ spin_lock_irqsave(&evtlog->spin_lock, flags);
+
+ log = &evtlog->logs[evtlog->first % SDE_EVTLOG_ENTRY];
+
+ prev_log = &evtlog->logs[(evtlog->first - 1) %
SDE_EVTLOG_ENTRY];
off = snprintf((evtlog_buf + off), (evtlog_buf_size - off), "%s:%-4d",
@@ -175,7 +142,7 @@
}
off += snprintf((evtlog_buf + off), (evtlog_buf_size - off),
- "=>[%-8d:%-11llu:%9llu][%-4d]:", sde_dbg_evtlog.first,
+ "=>[%-8d:%-11llu:%9llu][%-4d]:", evtlog->first,
log->time, (log->time - prev_log->time), log->pid);
for (i = 0; i < log->data_cnt; i++)
@@ -184,143 +151,37 @@
off += snprintf((evtlog_buf + off), (evtlog_buf_size - off), "\n");
- spin_unlock_irqrestore(&sde_evtloglock, flags);
+ spin_unlock_irqrestore(&evtlog->spin_lock, flags);
return off;
}
-static void _sde_evtlog_dump_all(void)
+void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog)
{
- char evtlog_buf[SDE_EVTLOG_BUF_MAX];
+ char buf[SDE_EVTLOG_BUF_MAX];
- while (_sde_evtlog_dump_calc_range()) {
- sde_evtlog_dump_entry(evtlog_buf, SDE_EVTLOG_BUF_MAX);
- pr_info("%s", evtlog_buf);
- }
-}
-
-static void _sde_dump_array(bool dead, const char *name)
-{
- _sde_evtlog_dump_all();
-
- if (dead && sde_dbg_evtlog.panic_on_err)
- panic(name);
-}
-
-static void _sde_dump_work(struct work_struct *work)
-{
- _sde_dump_array(sde_dbg_evtlog.work_panic, "evtlog_workitem");
-}
-
-void sde_dbg_dump(bool queue, const char *name, ...)
-{
- int i;
- bool dead = false;
- va_list args;
- char *blk_name = NULL;
-
- if (!sde_evtlog_is_enabled(SDE_EVTLOG_DEFAULT))
+ if (!evtlog)
return;
- if (queue && work_pending(&sde_dbg_evtlog.evtlog_dump_work))
- return;
-
- va_start(args, name);
- for (i = 0; i < SDE_EVTLOG_MAX_DATA; i++) {
- blk_name = va_arg(args, char*);
- if (IS_ERR_OR_NULL(blk_name))
- break;
-
- if (!strcmp(blk_name, "panic"))
- dead = true;
- }
- va_end(args);
-
- if (queue) {
- /* schedule work to dump later */
- sde_dbg_evtlog.work_panic = dead;
- schedule_work(&sde_dbg_evtlog.evtlog_dump_work);
- } else {
- _sde_dump_array(dead, name);
- }
+ while (sde_evtlog_dump_to_buffer(evtlog, buf, sizeof(buf)))
+ pr_info("%s", buf);
}
-static int sde_evtlog_dump_open(struct inode *inode, struct file *file)
+struct sde_dbg_evtlog *sde_evtlog_init(void)
{
- /* non-seekable */
- file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
- file->private_data = inode->i_private;
- return 0;
+ struct sde_dbg_evtlog *evtlog;
+
+ evtlog = kzalloc(sizeof(*evtlog), GFP_KERNEL);
+ if (!evtlog)
+ return ERR_PTR(-ENOMEM);
+
+ spin_lock_init(&evtlog->spin_lock);
+ evtlog->enable = SDE_EVTLOG_DEFAULT_ENABLE;
+
+ return evtlog;
}
-static ssize_t sde_evtlog_dump_read(struct file *file, char __user *buff,
- size_t count, loff_t *ppos)
+void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog)
{
- ssize_t len = 0;
- char evtlog_buf[SDE_EVTLOG_BUF_MAX];
-
- if (_sde_evtlog_dump_calc_range()) {
- len = sde_evtlog_dump_entry(evtlog_buf, SDE_EVTLOG_BUF_MAX);
- if (copy_to_user(buff, evtlog_buf, len))
- return -EFAULT;
- *ppos += len;
- }
-
- return len;
-}
-
-static ssize_t sde_evtlog_dump_write(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
-{
- _sde_evtlog_dump_all();
-
- if (sde_dbg_evtlog.panic_on_err)
- panic("sde");
-
- return count;
-}
-
-static const struct file_operations sde_evtlog_fops = {
- .open = sde_evtlog_dump_open,
- .read = sde_evtlog_dump_read,
- .write = sde_evtlog_dump_write,
-};
-
-int sde_evtlog_init(struct dentry *debugfs_root)
-{
- int i;
-
- sde_dbg_evtlog.evtlog = debugfs_create_dir("evt_dbg", debugfs_root);
- if (IS_ERR_OR_NULL(sde_dbg_evtlog.evtlog)) {
- pr_err("debugfs_create_dir fail, error %ld\n",
- PTR_ERR(sde_dbg_evtlog.evtlog));
- sde_dbg_evtlog.evtlog = NULL;
- return -ENODEV;
- }
-
- INIT_WORK(&sde_dbg_evtlog.evtlog_dump_work, _sde_dump_work);
- sde_dbg_evtlog.work_panic = false;
-
- for (i = 0; i < SDE_EVTLOG_ENTRY; i++)
- sde_dbg_evtlog.logs[i].counter = i;
-
- debugfs_create_file("dump", 0644, sde_dbg_evtlog.evtlog, NULL,
- &sde_evtlog_fops);
- debugfs_create_u32("enable", 0644, sde_dbg_evtlog.evtlog,
- &sde_dbg_evtlog.evtlog_enable);
- debugfs_create_u32("panic", 0644, sde_dbg_evtlog.evtlog,
- &sde_dbg_evtlog.panic_on_err);
-
- sde_dbg_evtlog.evtlog_enable = SDE_EVTLOG_DEFAULT_ENABLE;
- sde_dbg_evtlog.panic_on_err = SDE_DBG_DEFAULT_PANIC;
-
- pr_info("evtlog_status: enable:%d, panic:%d\n",
- sde_dbg_evtlog.evtlog_enable, sde_dbg_evtlog.panic_on_err);
-
- return 0;
-}
-
-void sde_evtlog_destroy(void)
-{
- debugfs_remove(sde_dbg_evtlog.evtlog);
+ kfree(evtlog);
}
diff --git a/drivers/gpu/drm/msm/sde_power_handle.c b/drivers/gpu/drm/msm/sde_power_handle.c
index f2b64ca..da56891 100644
--- a/drivers/gpu/drm/msm/sde_power_handle.c
+++ b/drivers/gpu/drm/msm/sde_power_handle.c
@@ -27,6 +27,7 @@
#include <linux/sde_io_util.h>
#include "sde_power_handle.h"
+#include "sde_trace.h"
struct sde_power_client *sde_power_client_create(
struct sde_power_handle *phandle, char *client_name)
@@ -272,6 +273,234 @@
}
#ifdef CONFIG_QCOM_BUS_SCALING
+
+#define MAX_AXI_PORT_COUNT 3
+
+static int _sde_power_data_bus_set_quota(
+ struct sde_power_data_bus_handle *pdbus,
+ u64 ab_quota_rt, u64 ab_quota_nrt,
+ u64 ib_quota_rt, u64 ib_quota_nrt)
+{
+ int new_uc_idx;
+ u64 ab_quota[MAX_AXI_PORT_COUNT] = {0, 0};
+ u64 ib_quota[MAX_AXI_PORT_COUNT] = {0, 0};
+ int rc;
+
+ if (pdbus->data_bus_hdl < 1) {
+ pr_err("invalid bus handle %d\n", pdbus->data_bus_hdl);
+ return -EINVAL;
+ }
+
+ if (!ab_quota_rt && !ab_quota_nrt && !ib_quota_rt && !ib_quota_nrt) {
+ new_uc_idx = 0;
+ } else {
+ int i;
+ struct msm_bus_vectors *vect = NULL;
+ struct msm_bus_scale_pdata *bw_table =
+ pdbus->data_bus_scale_table;
+ u32 nrt_axi_port_cnt = pdbus->nrt_axi_port_cnt;
+ u32 total_axi_port_cnt = pdbus->axi_port_cnt;
+ u32 rt_axi_port_cnt = total_axi_port_cnt - nrt_axi_port_cnt;
+ int match_cnt = 0;
+
+ if (!bw_table || !total_axi_port_cnt ||
+ total_axi_port_cnt > MAX_AXI_PORT_COUNT) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ if (pdbus->bus_channels) {
+ ib_quota_rt = div_u64(ib_quota_rt,
+ pdbus->bus_channels);
+ ib_quota_nrt = div_u64(ib_quota_nrt,
+ pdbus->bus_channels);
+ }
+
+ if (nrt_axi_port_cnt) {
+
+ ab_quota_rt = div_u64(ab_quota_rt, rt_axi_port_cnt);
+ ab_quota_nrt = div_u64(ab_quota_nrt, nrt_axi_port_cnt);
+
+ for (i = 0; i < total_axi_port_cnt; i++) {
+ if (i < rt_axi_port_cnt) {
+ ab_quota[i] = ab_quota_rt;
+ ib_quota[i] = ib_quota_rt;
+ } else {
+ ab_quota[i] = ab_quota_nrt;
+ ib_quota[i] = ib_quota_nrt;
+ }
+ }
+ } else {
+ ab_quota[0] = div_u64(ab_quota_rt + ab_quota_nrt,
+ total_axi_port_cnt);
+ ib_quota[0] = ib_quota_rt + ib_quota_nrt;
+
+ for (i = 1; i < total_axi_port_cnt; i++) {
+ ab_quota[i] = ab_quota[0];
+ ib_quota[i] = ib_quota[0];
+ }
+ }
+
+ for (i = 0; i < total_axi_port_cnt; i++) {
+ vect = &bw_table->usecase
+ [pdbus->curr_bw_uc_idx].vectors[i];
+ /* avoid performing updates for small changes */
+ if ((ab_quota[i] == vect->ab) &&
+ (ib_quota[i] == vect->ib))
+ match_cnt++;
+ }
+
+ if (match_cnt == total_axi_port_cnt) {
+ pr_debug("skip BW vote\n");
+ return 0;
+ }
+
+ new_uc_idx = (pdbus->curr_bw_uc_idx %
+ (bw_table->num_usecases - 1)) + 1;
+
+ for (i = 0; i < total_axi_port_cnt; i++) {
+ vect = &bw_table->usecase[new_uc_idx].vectors[i];
+ vect->ab = ab_quota[i];
+ vect->ib = ib_quota[i];
+
+ pr_debug("uc_idx=%d %s path idx=%d ab=%llu ib=%llu\n",
+ new_uc_idx, (i < rt_axi_port_cnt) ? "rt" : "nrt"
+ , i, vect->ab, vect->ib);
+ }
+ }
+ pdbus->curr_bw_uc_idx = new_uc_idx;
+ pdbus->ao_bw_uc_idx = new_uc_idx;
+
+ if ((pdbus->bus_ref_cnt == 0) && pdbus->curr_bw_uc_idx) {
+ rc = 0;
+ } else { /* vote BW if bus_bw_cnt > 0 or uc_idx is zero */
+ SDE_ATRACE_BEGIN("msm_bus_scale_req");
+ rc = msm_bus_scale_client_update_request(pdbus->data_bus_hdl,
+ new_uc_idx);
+ SDE_ATRACE_END("msm_bus_scale_req");
+ }
+ return rc;
+}
+
+int sde_power_data_bus_set_quota(struct sde_power_handle *phandle,
+ struct sde_power_client *pclient,
+ int bus_client, u64 ab_quota, u64 ib_quota)
+{
+ int rc = 0;
+ int i;
+ u64 total_ab_rt = 0, total_ib_rt = 0;
+ u64 total_ab_nrt = 0, total_ib_nrt = 0;
+ struct sde_power_client *client;
+
+ if (!phandle || !pclient ||
+ bus_client >= SDE_POWER_HANDLE_DATA_BUS_CLIENT_MAX) {
+ pr_err("invalid parameters\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&phandle->phandle_lock);
+
+ pclient->ab[bus_client] = ab_quota;
+ pclient->ib[bus_client] = ib_quota;
+ trace_sde_perf_update_bus(bus_client, ab_quota, ib_quota);
+
+ list_for_each_entry(client, &phandle->power_client_clist, list) {
+ for (i = 0; i < SDE_POWER_HANDLE_DATA_BUS_CLIENT_MAX; i++) {
+ if (i == SDE_POWER_HANDLE_DATA_BUS_CLIENT_NRT) {
+ total_ab_nrt += client->ab[i];
+ total_ib_nrt += client->ib[i];
+ } else {
+ total_ab_rt += client->ab[i];
+ total_ib_rt = max(total_ib_rt, client->ib[i]);
+ }
+ }
+ }
+
+ rc = _sde_power_data_bus_set_quota(&phandle->data_bus_handle,
+ total_ab_rt, total_ab_nrt,
+ total_ib_rt, total_ib_nrt);
+
+ mutex_unlock(&phandle->phandle_lock);
+
+ return rc;
+}
+
+static void sde_power_data_bus_unregister(
+ struct sde_power_data_bus_handle *pdbus)
+{
+ if (pdbus->data_bus_hdl) {
+ msm_bus_scale_unregister_client(pdbus->data_bus_hdl);
+ pdbus->data_bus_hdl = 0;
+ }
+}
+
+static int sde_power_data_bus_parse(struct platform_device *pdev,
+ struct sde_power_data_bus_handle *pdbus)
+{
+ struct device_node *node;
+ int rc = 0;
+ int paths;
+
+ pdbus->bus_channels = 1;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,sde-dram-channels", &pdbus->bus_channels);
+ if (rc) {
+ pr_debug("number of channels property not specified\n");
+ rc = 0;
+ }
+
+ pdbus->nrt_axi_port_cnt = 0;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,sde-num-nrt-paths",
+ &pdbus->nrt_axi_port_cnt);
+ if (rc) {
+ pr_debug("number of axi port property not specified\n");
+ rc = 0;
+ }
+
+ node = of_get_child_by_name(pdev->dev.of_node, "qcom,sde-data-bus");
+ if (node) {
+ rc = of_property_read_u32(node,
+ "qcom,msm-bus,num-paths", &paths);
+ if (rc) {
+ pr_err("Error. qcom,msm-bus,num-paths not found\n");
+ return rc;
+ }
+ pdbus->axi_port_cnt = paths;
+
+ pdbus->data_bus_scale_table =
+ msm_bus_pdata_from_node(pdev, node);
+ if (IS_ERR_OR_NULL(pdbus->data_bus_scale_table)) {
+ pr_err("reg bus handle parsing failed\n");
+ rc = PTR_ERR(pdbus->data_bus_scale_table);
+ goto end;
+ }
+ pdbus->data_bus_hdl = msm_bus_scale_register_client(
+ pdbus->data_bus_scale_table);
+ if (!pdbus->data_bus_hdl) {
+ pr_err("data_bus_client register failed\n");
+ rc = -EINVAL;
+ goto end;
+ }
+ pr_debug("register data_bus_hdl=%x\n", pdbus->data_bus_hdl);
+
+ /*
+ * Following call will not result in actual vote rather update
+ * the current index and ab/ib value. When continuous splash
+ * is enabled, actual vote will happen when splash handoff is
+ * done.
+ */
+ return _sde_power_data_bus_set_quota(pdbus,
+ SDE_POWER_HANDLE_DATA_BUS_AB_QUOTA,
+ SDE_POWER_HANDLE_DATA_BUS_AB_QUOTA,
+ SDE_POWER_HANDLE_DATA_BUS_IB_QUOTA,
+ SDE_POWER_HANDLE_DATA_BUS_IB_QUOTA);
+ }
+
+end:
+ return rc;
+}
+
static int sde_power_reg_bus_parse(struct platform_device *pdev,
struct sde_power_handle *phandle)
{
@@ -320,6 +549,24 @@
return rc;
}
#else
+static int sde_power_data_bus_parse(struct platform_device *pdev,
+ struct sde_power_data_bus_handle *pdbus)
+{
+ return 0;
+}
+
+static void sde_power_data_bus_unregister(
+ struct sde_power_data_bus_handle *pdbus)
+{
+}
+
+int sde_power_data_bus_set_quota(struct sde_power_handle *phandle,
+ struct sde_power_client *pclient,
+ int bus_client, u64 ab_quota, u64 ib_quota)
+{
+ return 0;
+}
+
static int sde_power_reg_bus_parse(struct platform_device *pdev,
struct sde_power_handle *phandle)
{
@@ -336,6 +583,57 @@
}
#endif
+void sde_power_data_bus_bandwidth_ctrl(struct sde_power_handle *phandle,
+ struct sde_power_client *pclient, int enable)
+{
+ struct sde_power_data_bus_handle *pdbus;
+ int changed = 0;
+
+ if (!phandle || !pclient) {
+ pr_err("invalid power/client handle\n");
+ return;
+ }
+
+ pdbus = &phandle->data_bus_handle;
+
+ mutex_lock(&phandle->phandle_lock);
+ if (enable) {
+ if (pdbus->bus_ref_cnt == 0)
+ changed++;
+ pdbus->bus_ref_cnt++;
+ } else {
+ if (pdbus->bus_ref_cnt) {
+ pdbus->bus_ref_cnt--;
+ if (pdbus->bus_ref_cnt == 0)
+ changed++;
+ } else {
+ pr_debug("Can not be turned off\n");
+ }
+ }
+
+ pr_debug("%pS: task:%s bw_cnt=%d changed=%d enable=%d\n",
+ __builtin_return_address(0), current->group_leader->comm,
+ pdbus->bus_ref_cnt, changed, enable);
+
+ if (changed) {
+ SDE_ATRACE_INT("data_bus_ctrl", enable);
+
+ if (!enable) {
+ if (!pdbus->handoff_pending) {
+ msm_bus_scale_client_update_request(
+ pdbus->data_bus_hdl, 0);
+ pdbus->ao_bw_uc_idx = 0;
+ }
+ } else {
+ msm_bus_scale_client_update_request(
+ pdbus->data_bus_hdl,
+ pdbus->curr_bw_uc_idx);
+ }
+ }
+
+ mutex_unlock(&phandle->phandle_lock);
+}
+
int sde_power_resource_init(struct platform_device *pdev,
struct sde_power_handle *phandle)
{
@@ -348,6 +646,7 @@
goto end;
}
mp = &phandle->mp;
+ phandle->dev = &pdev->dev;
rc = sde_power_parse_dt_clock(pdev, mp);
if (rc) {
@@ -386,11 +685,19 @@
goto bus_err;
}
+ rc = sde_power_data_bus_parse(pdev, &phandle->data_bus_handle);
+ if (rc) {
+ pr_err("register data bus parse failed rc=%d\n", rc);
+ goto data_bus_err;
+ }
+
INIT_LIST_HEAD(&phandle->power_client_clist);
mutex_init(&phandle->phandle_lock);
return rc;
+data_bus_err:
+ sde_power_reg_bus_unregister(phandle->reg_bus_hdl);
bus_err:
msm_dss_put_clk(mp->clk_config, mp->num_clk);
clk_err:
@@ -416,6 +723,8 @@
}
mp = &phandle->mp;
+ sde_power_data_bus_unregister(&phandle->data_bus_handle);
+
sde_power_reg_bus_unregister(phandle->reg_bus_hdl);
msm_dss_put_clk(mp->clk_config, mp->num_clk);
@@ -568,3 +877,49 @@
return rate;
}
+
+u64 sde_power_clk_get_max_rate(struct sde_power_handle *phandle,
+ char *clock_name)
+{
+ int i;
+ struct dss_module_power *mp;
+ u64 rate = 0;
+
+ if (!phandle) {
+ pr_err("invalid input power handle\n");
+ return 0;
+ }
+ mp = &phandle->mp;
+
+ for (i = 0; i < mp->num_clk; i++) {
+ if (!strcmp(mp->clk_config[i].clk_name, clock_name)) {
+ rate = mp->clk_config[i].max_rate;
+ break;
+ }
+ }
+
+ return rate;
+}
+
+struct clk *sde_power_clk_get_clk(struct sde_power_handle *phandle,
+ char *clock_name)
+{
+ int i;
+ struct dss_module_power *mp;
+ struct clk *clk = NULL;
+
+ if (!phandle) {
+ pr_err("invalid input power handle\n");
+ return 0;
+ }
+ mp = &phandle->mp;
+
+ for (i = 0; i < mp->num_clk; i++) {
+ if (!strcmp(mp->clk_config[i].clk_name, clock_name)) {
+ clk = mp->clk_config[i].clk;
+ break;
+ }
+ }
+
+ return clk;
+}
diff --git a/drivers/gpu/drm/msm/sde_power_handle.h b/drivers/gpu/drm/msm/sde_power_handle.h
index 3e43d80..b982d17 100644
--- a/drivers/gpu/drm/msm/sde_power_handle.h
+++ b/drivers/gpu/drm/msm/sde_power_handle.h
@@ -16,6 +16,9 @@
#define MAX_CLIENT_NAME_LEN 128
+#define SDE_POWER_HANDLE_DATA_BUS_IB_QUOTA 2000000000
+#define SDE_POWER_HANDLE_DATA_BUS_AB_QUOTA 2000000000
+
/**
* mdss_bus_vote_type: register bus vote type
* VOTE_INDEX_DISABLE: removes the client vote
@@ -29,6 +32,18 @@
};
/**
+ * enum sde_power_handle_data_bus_client - type of axi bus clients
+ * @SDE_POWER_HANDLE_DATA_BUS_CLIENT_RT: core real-time bus client
+ * @SDE_POWER_HANDLE_DATA_BUS_CLIENT_NRT: core non-real-time bus client
+ * @SDE_POWER_HANDLE_DATA_BUS_CLIENT_MAX: maximum number of bus client type
+ */
+enum sde_power_handle_data_bus_client {
+ SDE_POWER_HANDLE_DATA_BUS_CLIENT_RT,
+ SDE_POWER_HANDLE_DATA_BUS_CLIENT_NRT,
+ SDE_POWER_HANDLE_DATA_BUS_CLIENT_MAX
+};
+
+/**
* struct sde_power_client: stores the power client for sde driver
* @name: name of the client
* @usecase_ndx: current regs bus vote type
@@ -38,6 +53,8 @@
* client.
* @id: assigned during create. helps for debugging.
* @list: list to attach power handle master list
+ * @ab: arbitrated bandwidth for each bus client
+ * @ib: instantaneous bandwidth for each bus client
*/
struct sde_power_client {
char name[MAX_CLIENT_NAME_LEN];
@@ -45,6 +62,32 @@
short refcount;
u32 id;
struct list_head list;
+ u64 ab[SDE_POWER_HANDLE_DATA_BUS_CLIENT_MAX];
+ u64 ib[SDE_POWER_HANDLE_DATA_BUS_CLIENT_MAX];
+};
+
+/**
+ * struct sde_power_data_handle: power handle struct for data bus
+ * @data_bus_scale_table: pointer to bus scaling table
+ * @data_bus_hdl: current data bus handle
+ * @axi_port_cnt: number of rt axi ports
+ * @nrt_axi_port_cnt: number of nrt axi ports
+ * @bus_channels: number of memory bus channels
+ * @curr_bw_uc_idx: current use case index of data bus
+ * @ao_bw_uc_idx: active only use case index of data bus
+ * @bus_ref_cnt: reference count of data bus enable request
+ * @handoff_pending: True to indicate if bootloader hand-over is pending
+ */
+struct sde_power_data_bus_handle {
+ struct msm_bus_scale_pdata *data_bus_scale_table;
+ u32 data_bus_hdl;
+ u32 axi_port_cnt;
+ u32 nrt_axi_port_cnt;
+ u32 bus_channels;
+ u32 curr_bw_uc_idx;
+ u32 ao_bw_uc_idx;
+ u32 bus_ref_cnt;
+ int handoff_pending;
};
/**
@@ -52,15 +95,19 @@
* @mp: module power for clock and regulator
* @client_clist: master list to store all clients
* @phandle_lock: lock to synchronize the enable/disable
+ * @dev: pointer to device structure
* @usecase_ndx: current usecase index
* @reg_bus_hdl: current register bus handle
+ * @data_bus_handle: context structure for data bus control
*/
struct sde_power_handle {
struct dss_module_power mp;
struct list_head power_client_clist;
struct mutex phandle_lock;
+ struct device *dev;
u32 current_usecase_ndx;
u32 reg_bus_hdl;
+ struct sde_power_data_bus_handle data_bus_handle;
};
/**
@@ -134,4 +181,49 @@
*/
u64 sde_power_clk_get_rate(struct sde_power_handle *pdata, char *clock_name);
+/**
+ * sde_power_clk_get_max_rate() - get the maximum clock rate
+ * @pdata: power handle containing the resources
+ * @clock_name: clock name to get the max rate.
+ *
+ * Return: maximum clock rate or 0 if not found.
+ */
+u64 sde_power_clk_get_max_rate(struct sde_power_handle *pdata,
+ char *clock_name);
+
+/**
+ * sde_power_clk_get_clk() - get the clock
+ * @pdata: power handle containing the resources
+ * @clock_name: clock name to get the clk pointer.
+ *
+ * Return: Pointer to clock
+ */
+struct clk *sde_power_clk_get_clk(struct sde_power_handle *phandle,
+ char *clock_name);
+
+/**
+ * sde_power_data_bus_set_quota() - set data bus quota for power client
+ * @phandle: power handle containing the resources
+ * @client: client information to set quota
+ * @bus_client: real-time or non-real-time bus client
+ * @ab_quota: arbitrated bus bandwidth
+ * @ib_quota: instantaneous bus bandwidth
+ *
+ * Return: zero if success, or error code otherwise
+ */
+int sde_power_data_bus_set_quota(struct sde_power_handle *phandle,
+ struct sde_power_client *pclient,
+ int bus_client, u64 ab_quota, u64 ib_quota);
+
+/**
+ * sde_power_data_bus_bandwidth_ctrl() - control data bus bandwidth enable
+ * @phandle: power handle containing the resources
+ * @client: client information to bandwidth control
+ * @enable: true to enable bandwidth for data base
+ *
+ * Return: none
+ */
+void sde_power_data_bus_bandwidth_ctrl(struct sde_power_handle *phandle,
+ struct sde_power_client *pclient, int enable);
+
#endif /* _SDE_POWER_HANDLE_H_ */
diff --git a/drivers/gpu/drm/nouveau/dispnv04/hw.c b/drivers/gpu/drm/nouveau/dispnv04/hw.c
index 74856a8..e64f524 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/hw.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/hw.c
@@ -222,6 +222,7 @@
uint32_t mpllP;
pci_read_config_dword(pci_get_bus_and_slot(0, 3), 0x6c, &mpllP);
+ mpllP = (mpllP >> 8) & 0xf;
if (!mpllP)
mpllP = 4;
@@ -232,7 +233,7 @@
uint32_t clock;
pci_read_config_dword(pci_get_bus_and_slot(0, 5), 0x4c, &clock);
- return clock;
+ return clock / 1000;
}
ret = nouveau_hw_get_pllvals(dev, plltype, &pllvals);
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index a1570b1..23ffe85 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -333,6 +333,9 @@
if (bios->major_version < 5 && bios->data[0x48] & 0x4)
return NVReadVgaCrtc5758(dev, 0, 0xf) & 0xf;
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_MAXWELL)
+ return nvif_rd32(device, 0x001800) & 0x0000000f;
+ else
if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA)
return (nvif_rd32(device, NV_PEXTDEV_BOOT_0) >> 24) & 0xf;
else
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
index 343b865..a2e6a81 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
@@ -1209,6 +1209,7 @@
nvbo->page_shift != vma->vm->mmu->lpg_shift)) {
nvkm_vm_map(vma, new_mem->mm_node);
} else {
+ WARN_ON(ttm_bo_wait(bo, false, false));
nvkm_vm_unmap(vma);
}
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
index 7218a06..e0d7f84 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
@@ -1851,7 +1851,7 @@
.fb = gk104_fb_new,
.fuse = gf100_fuse_new,
.gpio = gk104_gpio_new,
- .i2c = gf119_i2c_new,
+ .i2c = gk104_i2c_new,
.ibus = gk104_ibus_new,
.iccsense = gf100_iccsense_new,
.imem = nv50_instmem_new,
@@ -1965,7 +1965,7 @@
.fb = gm107_fb_new,
.fuse = gm107_fuse_new,
.gpio = gk104_gpio_new,
- .i2c = gf119_i2c_new,
+ .i2c = gk104_i2c_new,
.ibus = gk104_ibus_new,
.iccsense = gf100_iccsense_new,
.imem = nv50_instmem_new,
@@ -1999,7 +1999,7 @@
.fb = gm107_fb_new,
.fuse = gm107_fuse_new,
.gpio = gk104_gpio_new,
- .i2c = gf119_i2c_new,
+ .i2c = gk104_i2c_new,
.ibus = gk104_ibus_new,
.iccsense = gf100_iccsense_new,
.imem = nv50_instmem_new,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c
index 6f0436d..f8f2f16 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c
@@ -59,7 +59,7 @@
);
}
for (i = 0; i < size; i++)
- nvkm_wr32(device, 0x61c440 + soff, (i << 8) | args->v0.data[0]);
+ nvkm_wr32(device, 0x61c440 + soff, (i << 8) | args->v0.data[i]);
for (; i < 0x60; i++)
nvkm_wr32(device, 0x61c440 + soff, (i << 8));
nvkm_mask(device, 0x61c448 + soff, 0x80000003, 0x80000003);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c
index cbc67f2..12d96426 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c
@@ -60,6 +60,7 @@
struct nvkm_gpuobj *inst = chan->base.inst;
int ret = 0;
+ mutex_lock(&subdev->mutex);
nvkm_wr32(device, 0x002634, chan->base.chid);
if (nvkm_msec(device, 2000,
if (nvkm_rd32(device, 0x002634) == chan->base.chid)
@@ -67,10 +68,12 @@
) < 0) {
nvkm_error(subdev, "channel %d [%s] kick timeout\n",
chan->base.chid, chan->base.object.client->name);
- ret = -EBUSY;
- if (suspend)
- return ret;
+ ret = -ETIMEDOUT;
}
+ mutex_unlock(&subdev->mutex);
+
+ if (ret && suspend)
+ return ret;
if (offset) {
nvkm_kmap(inst);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c
index ed43510..a2df4f3 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c
@@ -40,7 +40,9 @@
struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
struct nvkm_device *device = subdev->device;
struct nvkm_client *client = chan->base.object.client;
+ int ret = 0;
+ mutex_lock(&subdev->mutex);
nvkm_wr32(device, 0x002634, chan->base.chid);
if (nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x002634) & 0x00100000))
@@ -48,10 +50,10 @@
) < 0) {
nvkm_error(subdev, "channel %d [%s] kick timeout\n",
chan->base.chid, client->name);
- return -EBUSY;
+ ret = -ETIMEDOUT;
}
-
- return 0;
+ mutex_unlock(&subdev->mutex);
+ return ret;
}
static u32
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
index 157919c..6584d50 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
@@ -1756,6 +1756,50 @@
};
int
+gf100_gr_ctor_fw_legacy(struct gf100_gr *gr, const char *fwname,
+ struct gf100_gr_fuc *fuc, int ret)
+{
+ struct nvkm_subdev *subdev = &gr->base.engine.subdev;
+ struct nvkm_device *device = subdev->device;
+ const struct firmware *fw;
+ char f[32];
+
+ /* see if this firmware has a legacy path */
+ if (!strcmp(fwname, "fecs_inst"))
+ fwname = "fuc409c";
+ else if (!strcmp(fwname, "fecs_data"))
+ fwname = "fuc409d";
+ else if (!strcmp(fwname, "gpccs_inst"))
+ fwname = "fuc41ac";
+ else if (!strcmp(fwname, "gpccs_data"))
+ fwname = "fuc41ad";
+ else {
+ /* nope, let's just return the error we got */
+ nvkm_error(subdev, "failed to load %s\n", fwname);
+ return ret;
+ }
+
+ /* yes, try to load from the legacy path */
+ nvkm_debug(subdev, "%s: falling back to legacy path\n", fwname);
+
+ snprintf(f, sizeof(f), "nouveau/nv%02x_%s", device->chipset, fwname);
+ ret = request_firmware(&fw, f, device->dev);
+ if (ret) {
+ snprintf(f, sizeof(f), "nouveau/%s", fwname);
+ ret = request_firmware(&fw, f, device->dev);
+ if (ret) {
+ nvkm_error(subdev, "failed to load %s\n", fwname);
+ return ret;
+ }
+ }
+
+ fuc->size = fw->size;
+ fuc->data = kmemdup(fw->data, fuc->size, GFP_KERNEL);
+ release_firmware(fw);
+ return (fuc->data != NULL) ? 0 : -ENOMEM;
+}
+
+int
gf100_gr_ctor_fw(struct gf100_gr *gr, const char *fwname,
struct gf100_gr_fuc *fuc)
{
@@ -1765,10 +1809,8 @@
int ret;
ret = nvkm_firmware_get(device, fwname, &fw);
- if (ret) {
- nvkm_error(subdev, "failed to load %s\n", fwname);
- return ret;
- }
+ if (ret)
+ return gf100_gr_ctor_fw_legacy(gr, fwname, fuc, ret);
fuc->size = fw->size;
fuc->data = kmemdup(fw->data, fuc->size, GFP_KERNEL);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h
index 212800e..7d1d3c6 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h
@@ -12,6 +12,7 @@
bool rw;
bool ignore_checksum;
bool no_pcir;
+ bool require_checksum;
};
int nvbios_extend(struct nvkm_bios *, u32 length);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c
index b2557e8..7deb81b 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c
@@ -86,9 +86,12 @@
nvbios_checksum(&bios->data[image.base], image.size)) {
nvkm_debug(subdev, "%08x: checksum failed\n",
image.base);
- if (mthd->func->rw)
+ if (!mthd->func->require_checksum) {
+ if (mthd->func->rw)
+ score += 1;
score += 1;
- score += 1;
+ } else
+ return 0;
} else {
score += 3;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c
index 8fecb5f..06572f8 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c
@@ -99,6 +99,7 @@
.init = acpi_init,
.read = acpi_read_fast,
.rw = false,
+ .require_checksum = true,
};
const struct nvbios_source
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c
index 39c2a38..0c7ef25 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c
@@ -47,8 +47,10 @@
BUG_ON((first > limit) || (limit >= ltc->num_tags));
+ mutex_lock(<c->subdev.mutex);
ltc->func->cbc_clear(ltc, first, limit);
ltc->func->cbc_wait(ltc);
+ mutex_unlock(<c->subdev.mutex);
}
int
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 113db3c..27cb424 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -120,7 +120,7 @@
mode->type |= DRM_MODE_TYPE_DRIVER;
- if (panel->desc->num_modes == 1)
+ if (panel->desc->num_timings == 1)
mode->type |= DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c
index 2a10e24..fb16070 100644
--- a/drivers/gpu/drm/radeon/radeon_cursor.c
+++ b/drivers/gpu/drm/radeon/radeon_cursor.c
@@ -90,6 +90,9 @@
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
struct radeon_device *rdev = crtc->dev->dev_private;
+ if (radeon_crtc->cursor_out_of_bounds)
+ return;
+
if (ASIC_IS_DCE4(rdev)) {
WREG32(EVERGREEN_CUR_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset,
upper_32_bits(radeon_crtc->cursor_addr));
@@ -143,21 +146,25 @@
int xorigin = 0, yorigin = 0;
int w = radeon_crtc->cursor_width;
+ radeon_crtc->cursor_x = x;
+ radeon_crtc->cursor_y = y;
+
if (ASIC_IS_AVIVO(rdev)) {
/* avivo cursor are offset into the total surface */
x += crtc->x;
y += crtc->y;
}
- DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y);
- if (x < 0) {
+ if (x < 0)
xorigin = min(-x, radeon_crtc->max_cursor_width - 1);
- x = 0;
- }
- if (y < 0) {
+ if (y < 0)
yorigin = min(-y, radeon_crtc->max_cursor_height - 1);
- y = 0;
+
+ if (!ASIC_IS_AVIVO(rdev)) {
+ x += crtc->x;
+ y += crtc->y;
}
+ DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y);
/* fixed on DCE6 and newer */
if (ASIC_IS_AVIVO(rdev) && !ASIC_IS_DCE6(rdev)) {
@@ -180,27 +187,31 @@
if (i > 1) {
int cursor_end, frame_end;
- cursor_end = x - xorigin + w;
+ cursor_end = x + w;
frame_end = crtc->x + crtc->mode.crtc_hdisplay;
if (cursor_end >= frame_end) {
w = w - (cursor_end - frame_end);
if (!(frame_end & 0x7f))
w--;
- } else {
- if (!(cursor_end & 0x7f))
- w--;
+ } else if (cursor_end <= 0) {
+ goto out_of_bounds;
+ } else if (!(cursor_end & 0x7f)) {
+ w--;
}
if (w <= 0) {
- w = 1;
- cursor_end = x - xorigin + w;
- if (!(cursor_end & 0x7f)) {
- x--;
- WARN_ON_ONCE(x < 0);
- }
+ goto out_of_bounds;
}
}
}
+ if (x <= (crtc->x - w) || y <= (crtc->y - radeon_crtc->cursor_height) ||
+ x >= (crtc->x + crtc->mode.crtc_hdisplay) ||
+ y >= (crtc->y + crtc->mode.crtc_vdisplay))
+ goto out_of_bounds;
+
+ x += xorigin;
+ y += yorigin;
+
if (ASIC_IS_DCE4(rdev)) {
WREG32(EVERGREEN_CUR_POSITION + radeon_crtc->crtc_offset, (x << 16) | y);
WREG32(EVERGREEN_CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin);
@@ -212,6 +223,9 @@
WREG32(AVIVO_D1CUR_SIZE + radeon_crtc->crtc_offset,
((w - 1) << 16) | (radeon_crtc->cursor_height - 1));
} else {
+ x -= crtc->x;
+ y -= crtc->y;
+
if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)
y *= 2;
@@ -229,10 +243,20 @@
yorigin * 256);
}
- radeon_crtc->cursor_x = x;
- radeon_crtc->cursor_y = y;
+ if (radeon_crtc->cursor_out_of_bounds) {
+ radeon_crtc->cursor_out_of_bounds = false;
+ if (radeon_crtc->cursor_bo)
+ radeon_show_cursor(crtc);
+ }
return 0;
+
+ out_of_bounds:
+ if (!radeon_crtc->cursor_out_of_bounds) {
+ radeon_hide_cursor(crtc);
+ radeon_crtc->cursor_out_of_bounds = true;
+ }
+ return 0;
}
int radeon_crtc_cursor_move(struct drm_crtc *crtc,
@@ -297,22 +321,23 @@
return ret;
}
- radeon_crtc->cursor_width = width;
- radeon_crtc->cursor_height = height;
-
radeon_lock_cursor(crtc, true);
- if (hot_x != radeon_crtc->cursor_hot_x ||
+ if (width != radeon_crtc->cursor_width ||
+ height != radeon_crtc->cursor_height ||
+ hot_x != radeon_crtc->cursor_hot_x ||
hot_y != radeon_crtc->cursor_hot_y) {
int x, y;
x = radeon_crtc->cursor_x + radeon_crtc->cursor_hot_x - hot_x;
y = radeon_crtc->cursor_y + radeon_crtc->cursor_hot_y - hot_y;
- radeon_cursor_move_locked(crtc, x, y);
-
+ radeon_crtc->cursor_width = width;
+ radeon_crtc->cursor_height = height;
radeon_crtc->cursor_hot_x = hot_x;
radeon_crtc->cursor_hot_y = hot_y;
+
+ radeon_cursor_move_locked(crtc, x, y);
}
radeon_show_cursor(crtc);
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 00ea000..e0c143b 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -366,11 +366,10 @@
radeon_pci_shutdown(struct pci_dev *pdev)
{
/* if we are running in a VM, make sure the device
- * torn down properly on reboot/shutdown.
- * unfortunately we can't detect certain
- * hypervisors so just do this all the time.
+ * torn down properly on reboot/shutdown
*/
- radeon_pci_remove(pdev);
+ if (radeon_device_is_virtual())
+ radeon_pci_remove(pdev);
}
static int radeon_pmops_suspend(struct device *dev)
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index bb75201a..f1da484 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -330,6 +330,7 @@
u16 lut_r[256], lut_g[256], lut_b[256];
bool enabled;
bool can_tile;
+ bool cursor_out_of_bounds;
uint32_t crtc_offset;
struct drm_gem_object *cursor_bo;
uint64_t cursor_addr;
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c
index e402be8..877af4a 100644
--- a/drivers/gpu/drm/radeon/si.c
+++ b/drivers/gpu/drm/radeon/si.c
@@ -50,7 +50,6 @@
MODULE_FIRMWARE("radeon/tahiti_mc.bin");
MODULE_FIRMWARE("radeon/tahiti_rlc.bin");
MODULE_FIRMWARE("radeon/tahiti_smc.bin");
-MODULE_FIRMWARE("radeon/tahiti_k_smc.bin");
MODULE_FIRMWARE("radeon/PITCAIRN_pfp.bin");
MODULE_FIRMWARE("radeon/PITCAIRN_me.bin");
@@ -1657,9 +1656,6 @@
switch (rdev->family) {
case CHIP_TAHITI:
chip_name = "TAHITI";
- /* XXX: figure out which Tahitis need the new ucode */
- if (0)
- new_smc = true;
new_chip_name = "tahiti";
pfp_req_size = SI_PFP_UCODE_SIZE * 4;
me_req_size = SI_PM4_UCODE_SIZE * 4;
@@ -1671,12 +1667,9 @@
break;
case CHIP_PITCAIRN:
chip_name = "PITCAIRN";
- if ((rdev->pdev->revision == 0x81) ||
- (rdev->pdev->device == 0x6810) ||
- (rdev->pdev->device == 0x6811) ||
- (rdev->pdev->device == 0x6816) ||
- (rdev->pdev->device == 0x6817) ||
- (rdev->pdev->device == 0x6806))
+ if ((rdev->pdev->revision == 0x81) &&
+ ((rdev->pdev->device == 0x6810) ||
+ (rdev->pdev->device == 0x6811)))
new_smc = true;
new_chip_name = "pitcairn";
pfp_req_size = SI_PFP_UCODE_SIZE * 4;
@@ -1689,15 +1682,15 @@
break;
case CHIP_VERDE:
chip_name = "VERDE";
- if ((rdev->pdev->revision == 0x81) ||
- (rdev->pdev->revision == 0x83) ||
- (rdev->pdev->revision == 0x87) ||
- (rdev->pdev->device == 0x6820) ||
- (rdev->pdev->device == 0x6821) ||
- (rdev->pdev->device == 0x6822) ||
- (rdev->pdev->device == 0x6823) ||
- (rdev->pdev->device == 0x682A) ||
- (rdev->pdev->device == 0x682B))
+ if (((rdev->pdev->device == 0x6820) &&
+ ((rdev->pdev->revision == 0x81) ||
+ (rdev->pdev->revision == 0x83))) ||
+ ((rdev->pdev->device == 0x6821) &&
+ ((rdev->pdev->revision == 0x83) ||
+ (rdev->pdev->revision == 0x87))) ||
+ ((rdev->pdev->revision == 0x87) &&
+ ((rdev->pdev->device == 0x6823) ||
+ (rdev->pdev->device == 0x682b))))
new_smc = true;
new_chip_name = "verde";
pfp_req_size = SI_PFP_UCODE_SIZE * 4;
@@ -1710,12 +1703,13 @@
break;
case CHIP_OLAND:
chip_name = "OLAND";
- if ((rdev->pdev->revision == 0xC7) ||
- (rdev->pdev->revision == 0x80) ||
- (rdev->pdev->revision == 0x81) ||
- (rdev->pdev->revision == 0x83) ||
- (rdev->pdev->device == 0x6604) ||
- (rdev->pdev->device == 0x6605))
+ if (((rdev->pdev->revision == 0x81) &&
+ ((rdev->pdev->device == 0x6600) ||
+ (rdev->pdev->device == 0x6604) ||
+ (rdev->pdev->device == 0x6605) ||
+ (rdev->pdev->device == 0x6610))) ||
+ ((rdev->pdev->revision == 0x83) &&
+ (rdev->pdev->device == 0x6610)))
new_smc = true;
new_chip_name = "oland";
pfp_req_size = SI_PFP_UCODE_SIZE * 4;
@@ -1727,12 +1721,15 @@
break;
case CHIP_HAINAN:
chip_name = "HAINAN";
- if ((rdev->pdev->revision == 0x81) ||
- (rdev->pdev->revision == 0x83) ||
- (rdev->pdev->revision == 0xC3) ||
- (rdev->pdev->device == 0x6664) ||
- (rdev->pdev->device == 0x6665) ||
- (rdev->pdev->device == 0x6667))
+ if (((rdev->pdev->revision == 0x81) &&
+ (rdev->pdev->device == 0x6660)) ||
+ ((rdev->pdev->revision == 0x83) &&
+ ((rdev->pdev->device == 0x6660) ||
+ (rdev->pdev->device == 0x6663) ||
+ (rdev->pdev->device == 0x6665) ||
+ (rdev->pdev->device == 0x6667))) ||
+ ((rdev->pdev->revision == 0xc3) &&
+ (rdev->pdev->device == 0x6665)))
new_smc = true;
new_chip_name = "hainan";
pfp_req_size = SI_PFP_UCODE_SIZE * 4;
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
index c4993452..13ba73f 100644
--- a/drivers/gpu/drm/radeon/si_dpm.c
+++ b/drivers/gpu/drm/radeon/si_dpm.c
@@ -3008,24 +3008,12 @@
(rdev->pdev->device == 0x6817) ||
(rdev->pdev->device == 0x6806))
max_mclk = 120000;
- } else if (rdev->family == CHIP_VERDE) {
- if ((rdev->pdev->revision == 0x81) ||
- (rdev->pdev->revision == 0x83) ||
- (rdev->pdev->revision == 0x87) ||
- (rdev->pdev->device == 0x6820) ||
- (rdev->pdev->device == 0x6821) ||
- (rdev->pdev->device == 0x6822) ||
- (rdev->pdev->device == 0x6823) ||
- (rdev->pdev->device == 0x682A) ||
- (rdev->pdev->device == 0x682B)) {
- max_sclk = 75000;
- max_mclk = 80000;
- }
} else if (rdev->family == CHIP_OLAND) {
if ((rdev->pdev->revision == 0xC7) ||
(rdev->pdev->revision == 0x80) ||
(rdev->pdev->revision == 0x81) ||
(rdev->pdev->revision == 0x83) ||
+ (rdev->pdev->revision == 0x87) ||
(rdev->pdev->device == 0x6604) ||
(rdev->pdev->device == 0x6605)) {
max_sclk = 75000;
diff --git a/drivers/gpu/drm/savage/savage_state.c b/drivers/gpu/drm/savage/savage_state.c
index 3dc0d8f..2db89be 100644
--- a/drivers/gpu/drm/savage/savage_state.c
+++ b/drivers/gpu/drm/savage/savage_state.c
@@ -1004,6 +1004,7 @@
kvb_addr = memdup_user(cmdbuf->vb_addr, cmdbuf->vb_size);
if (IS_ERR(kvb_addr)) {
ret = PTR_ERR(kvb_addr);
+ kvb_addr = NULL;
goto done;
}
cmdbuf->vb_addr = kvb_addr;
diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c
index 059f409..2fde44c 100644
--- a/drivers/gpu/drm/tegra/dpaux.c
+++ b/drivers/gpu/drm/tegra/dpaux.c
@@ -539,9 +539,9 @@
dpaux->desc.owner = THIS_MODULE;
dpaux->pinctrl = devm_pinctrl_register(&pdev->dev, &dpaux->desc, dpaux);
- if (!dpaux->pinctrl) {
+ if (IS_ERR(dpaux->pinctrl)) {
dev_err(&pdev->dev, "failed to register pincontrol\n");
- return -ENODEV;
+ return PTR_ERR(dpaux->pinctrl);
}
#endif
/* enable and clear all interrupts */
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 7f08d68..d544ff9 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -832,7 +832,7 @@
}
- __drm_atomic_helper_crtc_destroy_state(state);
+ drm_atomic_helper_crtc_destroy_state(crtc, state);
}
static const struct drm_crtc_funcs vc4_crtc_funcs = {
diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c
index 47a095f..18e3717 100644
--- a/drivers/gpu/drm/vc4/vc4_gem.c
+++ b/drivers/gpu/drm/vc4/vc4_gem.c
@@ -544,14 +544,15 @@
handles = drm_malloc_ab(exec->bo_count, sizeof(uint32_t));
if (!handles) {
+ ret = -ENOMEM;
DRM_ERROR("Failed to allocate incoming GEM handles\n");
goto fail;
}
- ret = copy_from_user(handles,
- (void __user *)(uintptr_t)args->bo_handles,
- exec->bo_count * sizeof(uint32_t));
- if (ret) {
+ if (copy_from_user(handles,
+ (void __user *)(uintptr_t)args->bo_handles,
+ exec->bo_count * sizeof(uint32_t))) {
+ ret = -EFAULT;
DRM_ERROR("Failed to copy in GEM handles\n");
goto fail;
}
@@ -593,12 +594,14 @@
args->shader_rec_count);
struct vc4_bo *bo;
- if (uniforms_offset < shader_rec_offset ||
+ if (shader_rec_offset < args->bin_cl_size ||
+ uniforms_offset < shader_rec_offset ||
exec_size < uniforms_offset ||
args->shader_rec_count >= (UINT_MAX /
sizeof(struct vc4_shader_state)) ||
temp_size < exec_size) {
DRM_ERROR("overflow in exec arguments\n");
+ ret = -EINVAL;
goto fail;
}
diff --git a/drivers/gpu/drm/vc4/vc4_render_cl.c b/drivers/gpu/drm/vc4/vc4_render_cl.c
index 08886a3..5cdd003 100644
--- a/drivers/gpu/drm/vc4/vc4_render_cl.c
+++ b/drivers/gpu/drm/vc4/vc4_render_cl.c
@@ -461,7 +461,7 @@
}
ret = vc4_full_res_bounds_check(exec, *obj, surf);
- if (!ret)
+ if (ret)
return ret;
return 0;
diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile
index 0634776..2ca55d8c 100644
--- a/drivers/gpu/msm/Makefile
+++ b/drivers/gpu/msm/Makefile
@@ -29,6 +29,7 @@
adreno_a3xx.o \
adreno_a4xx.o \
adreno_a5xx.o \
+ adreno_a6xx.o \
adreno_a3xx_snapshot.o \
adreno_a4xx_snapshot.o \
adreno_a5xx_snapshot.o \
diff --git a/drivers/gpu/msm/a6xx_reg.h b/drivers/gpu/msm/a6xx_reg.h
new file mode 100644
index 0000000..a2385fb
--- /dev/null
+++ b/drivers/gpu/msm/a6xx_reg.h
@@ -0,0 +1,148 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _A6XX_REG_H
+#define _A6XX_REG_H
+
+/* A6XX interrupt bits */
+#define A6XX_INT_RBBM_GPU_IDLE 0
+#define A6XX_INT_CP_AHB_ERROR 1
+#define A6XX_INT_ATB_ASYNCFIFO_OVERFLOW 6
+#define A6XX_INT_RBBM_GPC_ERROR 7
+#define A6XX_INT_CP_SW 8
+#define A6XX_INT_CP_HW_ERROR 9
+#define A6XX_INT_CP_CCU_FLUSH_DEPTH_TS 10
+#define A6XX_INT_CP_CCU_FLUSH_COLOR_TS 11
+#define A6XX_INT_CP_CCU_RESOLVE_TS 12
+#define A6XX_INT_CP_IB2 13
+#define A6XX_INT_CP_IB1 14
+#define A6XX_INT_CP_RB 15
+#define A6XX_INT_CP_RB_DONE_TS 17
+#define A6XX_INT_CP_WT_DONE_TS 18
+#define A6XX_INT_CP_CACHE_FLUSH_TS 20
+#define A6XX_INT_RBBM_ATB_BUS_OVERFLOW 22
+#define A6XX_INT_RBBM_HANG_DETECT 23
+#define A6XX_INT_UCHE_OOB_ACCESS 24
+#define A6XX_INT_UCHE_TRAP_INTR 25
+#define A6XX_INT_DEBBUS_INTR_0 26
+#define A6XX_INT_DEBBUS_INTR_1 27
+#define A6XX_INT_ISDB_CPU_IRQ 30
+#define A6XX_INT_ISDB_UNDER_DEBUG 31
+
+/* CP Interrupt bits */
+#define A6XX_CP_OPCODE_ERROR 0
+#define A6XX_CP_UCODE_ERROR 1
+#define A6XX_CP_HW_FAULT_ERROR 2
+#define A6XX_CP_REGISTER_PROTECTION_ERROR 4
+#define A6XX_CP_AHB_ERROR 5
+#define A6XX_CP_VSD_PARITY_ERROR 6
+#define A6XX_CP_ILLEGAL_INSTR_ERROR 7
+
+/* CP registers */
+#define A6XX_CP_RB_BASE 0x800
+#define A6XX_CP_RB_BASE_HI 0x801
+#define A6XX_CP_RB_CNTL 0x802
+#define A6XX_CP_RB_RPTR_ADDR_LO 0x804
+#define A6XX_CP_RB_RPTR_ADDR_HI 0x805
+#define A6XX_CP_RB_RPTR 0x806
+#define A6XX_CP_RB_WPTR 0x807
+#define A6XX_CP_SQE_CNTL 0x808
+#define A6XX_CP_HW_FAULT 0x821
+#define A6XX_CP_INTERRUPT_STATUS 0x823
+#define A6XX_CP_PROTECT_STATUS 0X824
+#define A6XX_CP_SQE_INSTR_BASE_LO 0x830
+#define A6XX_CP_SQE_INSTR_BASE_HI 0x831
+#define A6XX_CP_MISC_CNTL 0x840
+#define A6XX_CP_ROQ_THRESHOLDS_1 0x8C1
+#define A6XX_CP_ROQ_THRESHOLDS_2 0x8C2
+#define A6XX_CP_MEM_POOL_SIZE 0x8C3
+#define A6XX_CP_CHICKEN_DBG 0x841
+#define A6XX_CP_ADDR_MODE_CNTL 0x842
+#define A6XX_CP_PROTECT_CNTL 0x84F
+#define A6XX_CP_PROTECT_REG 0x850
+#define A6XX_CP_ALWAYS_ON_COUNTER_LO 0x980
+#define A6XX_CP_ALWAYS_ON_COUNTER_HI 0x981
+#define A6XX_CP_AHB_CNTL 0x98D
+#define A6XX_VSC_ADDR_MODE_CNTL 0xC01
+
+/* RBBM registers */
+#define A6XX_RBBM_VBIF_CLIENT_QOS_CNTL 0x10
+#define A6XX_RBBM_INTERFACE_HANG_INT_CNTL 0x1f
+#define A6XX_RBBM_INT_CLEAR_CMD 0x37
+#define A6XX_RBBM_INT_0_MASK 0x38
+#define A6XX_RBBM_SW_RESET_CMD 0x43
+#define A6XX_RBBM_BLOCK_SW_RESET_CMD 0x45
+#define A6XX_RBBM_BLOCK_SW_RESET_CMD2 0x46
+#define A6XX_RBBM_CLOCK_CNTL 0xAE
+#define A6XX_RBBM_INT_0_STATUS 0x201
+#define A6XX_RBBM_STATUS 0x210
+#define A6XX_RBBM_STATUS3 0x213
+#define A6XX_RBBM_SECVID_TRUST_CNTL 0xF400
+#define A6XX_RBBM_SECVID_TSB_ADDR_MODE_CNTL 0xF810
+
+/* VSC registers */
+#define A6XX_GRAS_ADDR_MODE_CNTL 0x8601
+
+/* RB registers */
+#define A6XX_RB_ADDR_MODE_CNTL 0x8E05
+#define A6XX_RB_NC_MODE_CNTL 0x8E08
+
+/* PC registers */
+#define A6XX_PC_DBG_ECO_CNTL 0x9E00
+#define A6XX_PC_ADDR_MODE_CNTL 0x9E01
+
+/* HLSQ registers */
+#define A6XX_HLSQ_ADDR_MODE_CNTL 0xBE05
+
+/* VFD registers */
+#define A6XX_VFD_ADDR_MODE_CNTL 0xA601
+
+/* VPC registers */
+#define A6XX_VPC_ADDR_MODE_CNTL 0x9601
+
+/* UCHE registers */
+#define A6XX_UCHE_ADDR_MODE_CNTL 0xE00
+#define A6XX_UCHE_MODE_CNTL 0xE01
+#define A6XX_UCHE_WRITE_RANGE_MAX_LO 0xE05
+#define A6XX_UCHE_WRITE_RANGE_MAX_HI 0xE06
+#define A6XX_UCHE_WRITE_THRU_BASE_LO 0xE07
+#define A6XX_UCHE_WRITE_THRU_BASE_HI 0xE08
+#define A6XX_UCHE_TRAP_BASE_LO 0xE09
+#define A6XX_UCHE_TRAP_BASE_HI 0xE0A
+#define A6XX_UCHE_GMEM_RANGE_MIN_LO 0xE0B
+#define A6XX_UCHE_GMEM_RANGE_MIN_HI 0xE0C
+#define A6XX_UCHE_GMEM_RANGE_MAX_LO 0xE0D
+#define A6XX_UCHE_GMEM_RANGE_MAX_HI 0xE0E
+#define A6XX_UCHE_CACHE_WAYS 0xE17
+#define A6XX_UCHE_FILTER_CNTL 0xE18
+
+/* SP registers */
+#define A6XX_SP_ADDR_MODE_CNTL 0xAE01
+#define A6XX_SP_NC_MODE_CNTL 0xAE02
+
+/* TP registers */
+#define A6XX_TPL1_ADDR_MODE_CNTL 0xB601
+#define A6XX_TPL1_NC_MODE_CNTL 0xB604
+
+/* VBIF registers */
+#define A6XX_VBIF_VERSION 0x3000
+#define A6XX_VBIF_GATE_OFF_WRREQ_EN 0x302A
+#define A6XX_VBIF_XIN_HALT_CTRL0 0x3080
+#define A6XX_VBIF_XIN_HALT_CTRL1 0x3081
+
+/* GMU registers */
+#define A6XX_GMU_CX_ALWAYS_ON_COUNTER_L 0x1f888
+#define A6XX_GMU_CX_ALWAYS_ON_COUNTER_H 0x1f889
+
+#endif /* _A6XX_REG_H */
+
diff --git a/drivers/gpu/msm/adreno-gpulist.h b/drivers/gpu/msm/adreno-gpulist.h
index f143938..e12c3f7 100644
--- a/drivers/gpu/msm/adreno-gpulist.h
+++ b/drivers/gpu/msm/adreno-gpulist.h
@@ -20,6 +20,7 @@
.major = 0,
.minor = 6,
.patchid = 0x00,
+ .features = ADRENO_SOFT_FAULT_DETECT,
.pm4fw_name = "a300_pm4.fw",
.pfpfw_name = "a300_pfp.fw",
.gpudev = &adreno_a3xx_gpudev,
@@ -32,6 +33,7 @@
.major = 0,
.minor = 6,
.patchid = 0x20,
+ .features = ADRENO_SOFT_FAULT_DETECT,
.pm4fw_name = "a300_pm4.fw",
.pfpfw_name = "a300_pfp.fw",
.gpudev = &adreno_a3xx_gpudev,
@@ -44,6 +46,7 @@
.major = 0,
.minor = 4,
.patchid = 0x00,
+ .features = ADRENO_SOFT_FAULT_DETECT,
.pm4fw_name = "a300_pm4.fw",
.pfpfw_name = "a300_pfp.fw",
.gpudev = &adreno_a3xx_gpudev,
@@ -56,7 +59,7 @@
.major = 0,
.minor = 5,
.patchid = ANY_ID,
- .features = 0,
+ .features = ADRENO_SOFT_FAULT_DETECT,
.pm4fw_name = "a420_pm4.fw",
.pfpfw_name = "a420_pfp.fw",
.gpudev = &adreno_a4xx_gpudev,
@@ -70,7 +73,7 @@
.minor = 0,
.patchid = ANY_ID,
.features = ADRENO_USES_OCMEM | ADRENO_WARM_START |
- ADRENO_USE_BOOTSTRAP,
+ ADRENO_USE_BOOTSTRAP | ADRENO_SOFT_FAULT_DETECT,
.pm4fw_name = "a420_pm4.fw",
.pfpfw_name = "a420_pfp.fw",
.gpudev = &adreno_a4xx_gpudev,
@@ -92,7 +95,8 @@
.patchid = ANY_ID,
.features = ADRENO_USES_OCMEM | ADRENO_WARM_START |
ADRENO_USE_BOOTSTRAP | ADRENO_SPTP_PC | ADRENO_PPD |
- ADRENO_CONTENT_PROTECTION | ADRENO_PREEMPTION,
+ ADRENO_CONTENT_PROTECTION | ADRENO_PREEMPTION |
+ ADRENO_SOFT_FAULT_DETECT,
.pm4fw_name = "a420_pm4.fw",
.pfpfw_name = "a420_pfp.fw",
.gpudev = &adreno_a4xx_gpudev,
@@ -116,7 +120,8 @@
.minor = 8,
.patchid = ANY_ID,
.features = ADRENO_USES_OCMEM | ADRENO_WARM_START |
- ADRENO_USE_BOOTSTRAP | ADRENO_SPTP_PC,
+ ADRENO_USE_BOOTSTRAP | ADRENO_SPTP_PC |
+ ADRENO_SOFT_FAULT_DETECT,
.pm4fw_name = "a420_pm4.fw",
.pfpfw_name = "a420_pfp.fw",
.gpudev = &adreno_a4xx_gpudev,
@@ -299,4 +304,16 @@
.num_protected_regs = 0x20,
.busy_mask = 0xFFFFFFFE,
},
+ {
+ .gpurev = ADRENO_REV_A630,
+ .core = 6,
+ .major = 3,
+ .minor = 0,
+ .patchid = ANY_ID,
+ .features = ADRENO_64BIT,
+ .sqefw_name = "a630_sqe.fw",
+ .gpudev = &adreno_a6xx_gpudev,
+ .gmem_size = SZ_1M,
+ .num_protected_regs = 0x20,
+ },
};
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index d076d06..2480187 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -48,6 +48,10 @@
module_param(nopreempt, bool, 0444);
MODULE_PARM_DESC(nopreempt, "Disable GPU preemption");
+static bool swfdetect;
+module_param(swfdetect, bool, 0444);
+MODULE_PARM_DESC(swfdetect, "Enable soft fault detection");
+
#define DRIVER_VERSION_MAJOR 3
#define DRIVER_VERSION_MINOR 1
@@ -87,12 +91,15 @@
.mem_log = KGSL_LOG_LEVEL_DEFAULT,
.pwr_log = KGSL_LOG_LEVEL_DEFAULT,
},
+ .fw[0] = {
+ .fwvirt = NULL
+ },
+ .fw[1] = {
+ .fwvirt = NULL
+ },
.gmem_size = SZ_256K,
- .pfp_fw = NULL,
- .pm4_fw = NULL,
.ft_policy = KGSL_FT_DEFAULT_POLICY,
.ft_pf_policy = KGSL_FT_PAGEFAULT_DEFAULT_POLICY,
- .fast_hang_detect = 1,
.long_ib_detect = 1,
.input_work = __WORK_INITIALIZER(device_3d0.input_work,
adreno_input_work),
@@ -1032,22 +1039,24 @@
static void _adreno_free_memories(struct adreno_device *adreno_dev)
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+ struct adreno_firmware *pfp_fw = ADRENO_FW(adreno_dev, ADRENO_FW_PFP);
+ struct adreno_firmware *pm4_fw = ADRENO_FW(adreno_dev, ADRENO_FW_PM4);
if (test_bit(ADRENO_DEVICE_DRAWOBJ_PROFILE, &adreno_dev->priv))
kgsl_free_global(device, &adreno_dev->profile_buffer);
/* Free local copies of firmware and other command streams */
- kfree(adreno_dev->pfp_fw);
- adreno_dev->pfp_fw = NULL;
+ kfree(pfp_fw->fwvirt);
+ pfp_fw->fwvirt = NULL;
- kfree(adreno_dev->pm4_fw);
- adreno_dev->pm4_fw = NULL;
+ kfree(pm4_fw->fwvirt);
+ pm4_fw->fwvirt = NULL;
kfree(adreno_dev->gpmu_cmds);
adreno_dev->gpmu_cmds = NULL;
- kgsl_free_global(device, &adreno_dev->pm4);
- kgsl_free_global(device, &adreno_dev->pfp);
+ kgsl_free_global(device, &pfp_fw->memdesc);
+ kgsl_free_global(device, &pm4_fw->memdesc);
}
static int adreno_remove(struct platform_device *pdev)
@@ -1107,7 +1116,11 @@
static void adreno_fault_detect_init(struct adreno_device *adreno_dev)
{
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
- int i, val = adreno_dev->fast_hang_detect;
+ int i;
+
+ if (!(swfdetect ||
+ ADRENO_FEATURE(adreno_dev, ADRENO_SOFT_FAULT_DETECT)))
+ return;
/* Disable the fast hang detect bit until we know its a go */
adreno_dev->fast_hang_detect = 0;
@@ -1136,8 +1149,7 @@
set_bit(ADRENO_DEVICE_SOFT_FAULT_DETECT, &adreno_dev->priv);
- if (val)
- adreno_fault_detect_start(adreno_dev);
+ adreno_fault_detect_start(adreno_dev);
}
static int adreno_init(struct kgsl_device *device)
@@ -1747,7 +1759,8 @@
{
uint64_t gmem_vaddr = 0;
- if (adreno_is_a5xx(adreno_dev))
+ if (adreno_is_a5xx(adreno_dev) ||
+ adreno_is_a6xx(adreno_dev))
gmem_vaddr = ADRENO_UCHE_GMEM_BASE;
if (sizebytes != sizeof(uint64_t)) {
status = -EINVAL;
@@ -1791,8 +1804,8 @@
}
memset(&ucode, 0, sizeof(ucode));
- ucode.pfp = adreno_dev->pfp_fw_version;
- ucode.pm4 = adreno_dev->pm4_fw_version;
+ ucode.pfp = adreno_dev->fw[ADRENO_FW_PFP].version;
+ ucode.pm4 = adreno_dev->fw[ADRENO_FW_PM4].version;
if (copy_to_user(value, &ucode, sizeof(ucode))) {
status = -EFAULT;
@@ -1854,6 +1867,48 @@
}
status = 0;
break;
+ case KGSL_PROP_MIN_ACCESS_LENGTH:
+ {
+ unsigned int mal;
+
+ if (sizebytes < sizeof(unsigned int)) {
+ status = -EINVAL;
+ break;
+ }
+
+ if (of_property_read_u32(device->pdev->dev.of_node,
+ "qcom,min-access-length", &mal)) {
+ mal = 0;
+ }
+
+ if (copy_to_user(value, &mal, sizeof(mal))) {
+ status = -EFAULT;
+ break;
+ }
+ }
+ status = 0;
+ break;
+ case KGSL_PROP_UBWC_MODE:
+ {
+ unsigned int mode;
+
+ if (sizebytes < sizeof(unsigned int)) {
+ status = -EINVAL;
+ break;
+ }
+
+ if (of_property_read_u32(device->pdev->dev.of_node,
+ "qcom,ubwc-mode", &mode))
+ mode = 0;
+
+ if (copy_to_user(value, &mode, sizeof(mode))) {
+ status = -EFAULT;
+ break;
+ }
+ }
+ status = 0;
+ break;
+
case KGSL_PROP_DEVICE_BITNESS:
{
unsigned int bitness = 32;
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index a14cf58..48ddb96 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -85,6 +85,8 @@
#define ADRENO_DRAWOBJ_RB(c) \
((ADRENO_CONTEXT(c->context))->rb)
+#define ADRENO_FW(a, f) (&(a->fw[f]))
+
/* Adreno core features */
/* The core uses OCMEM for GMEM/binning memory */
#define ADRENO_USES_OCMEM BIT(0)
@@ -108,6 +110,8 @@
#define ADRENO_64BIT BIT(9)
/* The GPU supports retention for cpz registers */
#define ADRENO_CPZ_RETENTION BIT(10)
+/* The core has soft fault detection available */
+#define ADRENO_SOFT_FAULT_DETECT BIT(11)
/*
* Adreno GPU quirks - control bits for various workarounds
@@ -152,6 +156,10 @@
#define ADRENO_UCHE_GMEM_BASE 0x100000
+#define ADRENO_FW_PFP 0
+#define ADRENO_FW_SQE 0
+#define ADRENO_FW_PM4 1
+
enum adreno_gpurev {
ADRENO_REV_UNKNOWN = 0,
ADRENO_REV_A304 = 304,
@@ -173,6 +181,7 @@
ADRENO_REV_A512 = 512,
ADRENO_REV_A530 = 530,
ADRENO_REV_A540 = 540,
+ ADRENO_REV_A630 = 630,
};
#define ADRENO_START_WARM 0
@@ -251,6 +260,20 @@
};
/**
+ * struct adreno_firmware - Struct holding fw details
+ * @fwvirt: Buffer which holds the ucode
+ * @size: Size of ucode buffer
+ * @version: Version of ucode
+ * @memdesc: Memory descriptor which holds ucode buffer info
+ */
+struct adreno_firmware {
+ unsigned int *fwvirt;
+ size_t size;
+ unsigned int version;
+ struct kgsl_memdesc memdesc;
+};
+
+/**
* struct adreno_gpu_core - A specific GPU core definition
* @gpurev: Unique GPU revision identifier
* @core: Match for the core version of the GPU
@@ -290,6 +313,7 @@
unsigned long features;
const char *pm4fw_name;
const char *pfpfw_name;
+ const char *sqefw_name;
const char *zap_name;
struct adreno_gpudev *gpudev;
size_t gmem_size;
@@ -378,14 +402,7 @@
unsigned long gmem_base;
unsigned long gmem_size;
const struct adreno_gpu_core *gpucore;
- unsigned int *pfp_fw;
- size_t pfp_fw_size;
- unsigned int pfp_fw_version;
- struct kgsl_memdesc pfp;
- unsigned int *pm4_fw;
- size_t pm4_fw_size;
- unsigned int pm4_fw_version;
- struct kgsl_memdesc pm4;
+ struct adreno_firmware fw[2];
size_t gpmu_cmds_size;
unsigned int *gpmu_cmds;
struct adreno_ringbuffer ringbuffers[KGSL_PRIORITY_MAX_RB_LEVELS];
@@ -872,6 +889,7 @@
extern struct adreno_gpudev adreno_a3xx_gpudev;
extern struct adreno_gpudev adreno_a4xx_gpudev;
extern struct adreno_gpudev adreno_a5xx_gpudev;
+extern struct adreno_gpudev adreno_a6xx_gpudev;
extern int adreno_wake_nice;
extern unsigned int adreno_wake_timeout;
@@ -1043,6 +1061,14 @@
(ADRENO_CHIPID_PATCH(adreno_dev->chipid) == 1);
}
+static inline int adreno_is_a6xx(struct adreno_device *adreno_dev)
+{
+ return ADRENO_GPUREV(adreno_dev) >= 600 &&
+ ADRENO_GPUREV(adreno_dev) < 700;
+}
+
+ADRENO_TARGET(a630, ADRENO_REV_A630)
+
/*
* adreno_checkreg_off() - Checks the validity of a register enum
* @adreno_dev: Pointer to adreno device
@@ -1329,10 +1355,10 @@
static inline int adreno_compare_pm4_version(struct adreno_device *adreno_dev,
unsigned int version)
{
- if (adreno_dev->pm4_fw_version == version)
+ if (adreno_dev->fw[ADRENO_FW_PM4].version == version)
return 0;
- return (adreno_dev->pm4_fw_version > version) ? 1 : -1;
+ return (adreno_dev->fw[ADRENO_FW_PM4].version > version) ? 1 : -1;
}
/**
@@ -1346,10 +1372,10 @@
static inline int adreno_compare_pfp_version(struct adreno_device *adreno_dev,
unsigned int version)
{
- if (adreno_dev->pfp_fw_version == version)
+ if (adreno_dev->fw[ADRENO_FW_PFP].version == version)
return 0;
- return (adreno_dev->pfp_fw_version > version) ? 1 : -1;
+ return (adreno_dev->fw[ADRENO_FW_PFP].version > version) ? 1 : -1;
}
/*
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index 1a345e5..5d7fb21 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -1596,8 +1596,10 @@
int a3xx_microcode_read(struct adreno_device *adreno_dev)
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+ struct adreno_firmware *pm4_fw = ADRENO_FW(adreno_dev, ADRENO_FW_PM4);
+ struct adreno_firmware *pfp_fw = ADRENO_FW(adreno_dev, ADRENO_FW_PFP);
- if (adreno_dev->pm4_fw == NULL) {
+ if (pm4_fw->fwvirt == NULL) {
int len;
void *ptr;
@@ -1618,12 +1620,12 @@
return -ENOMEM;
}
- adreno_dev->pm4_fw_size = len / sizeof(uint32_t);
- adreno_dev->pm4_fw = ptr;
- adreno_dev->pm4_fw_version = adreno_dev->pm4_fw[1];
+ pm4_fw->size = len / sizeof(uint32_t);
+ pm4_fw->fwvirt = ptr;
+ pm4_fw->version = pm4_fw->fwvirt[1];
}
- if (adreno_dev->pfp_fw == NULL) {
+ if (pfp_fw->fwvirt == NULL) {
int len;
void *ptr;
@@ -1643,9 +1645,9 @@
return -ENOMEM;
}
- adreno_dev->pfp_fw_size = len / sizeof(uint32_t);
- adreno_dev->pfp_fw = ptr;
- adreno_dev->pfp_fw_version = adreno_dev->pfp_fw[5];
+ pfp_fw->size = len / sizeof(uint32_t);
+ pfp_fw->fwvirt = ptr;
+ pfp_fw->version = pfp_fw->fwvirt[1];
}
return 0;
@@ -1667,7 +1669,7 @@
adreno_writereg(adreno_dev, ADRENO_REG_CP_ME_RAM_WADDR, addr);
for (i = start; i < end; i++)
adreno_writereg(adreno_dev, ADRENO_REG_CP_ME_RAM_DATA,
- adreno_dev->pm4_fw[i]);
+ adreno_dev->fw[ADRENO_FW_PM4].fwvirt[i]);
}
/**
* load_pfp_ucode() - Load pfp ucode
@@ -1686,7 +1688,7 @@
adreno_writereg(adreno_dev, ADRENO_REG_CP_PFP_UCODE_ADDR, addr);
for (i = start; i < end; i++)
adreno_writereg(adreno_dev, ADRENO_REG_CP_PFP_UCODE_DATA,
- adreno_dev->pfp_fw[i]);
+ adreno_dev->fw[ADRENO_FW_PFP].fwvirt[i]);
}
/**
@@ -1716,6 +1718,8 @@
int i = 0;
int ret;
unsigned int pm4_size, pm4_idx, pm4_addr, pfp_size, pfp_idx, pfp_addr;
+ struct adreno_firmware *pfp_fw = ADRENO_FW(adreno_dev, ADRENO_FW_PFP);
+ struct adreno_firmware *pm4_fw = ADRENO_FW(adreno_dev, ADRENO_FW_PM4);
/* Only bootstrap jump tables of ucode */
if (load_jt) {
@@ -1731,8 +1735,8 @@
pfp_addr = 0;
}
- pm4_size = (adreno_dev->pm4_fw_size - pm4_idx);
- pfp_size = (adreno_dev->pfp_fw_size - pfp_idx);
+ pm4_size = (pm4_fw->size - pm4_idx);
+ pfp_size = (pfp_fw->size - pfp_idx);
bootstrap_size = (pm4_size + pfp_size + 5);
@@ -1791,10 +1795,10 @@
* from the ring buffer to the ME.
*/
if (adreno_is_a4xx(adreno_dev)) {
- for (i = pm4_idx; i < adreno_dev->pm4_fw_size; i++)
- *cmds++ = adreno_dev->pm4_fw[i];
- for (i = pfp_idx; i < adreno_dev->pfp_fw_size; i++)
- *cmds++ = adreno_dev->pfp_fw[i];
+ for (i = pm4_idx; i < pm4_fw->size; i++)
+ *cmds++ = pm4_fw->fwvirt[i];
+ for (i = pfp_idx; i < pfp_fw->size; i++)
+ *cmds++ = pfp_fw->fwvirt[i];
*cmds++ = cp_type3_packet(CP_REG_RMW, 3);
*cmds++ = 0x20000000 + A4XX_CP_RB_WPTR;
@@ -1807,10 +1811,10 @@
adreno_ringbuffer_submit(rb, NULL);
rb->_wptr = rb->_wptr + 2;
} else {
- for (i = pfp_idx; i < adreno_dev->pfp_fw_size; i++)
- *cmds++ = adreno_dev->pfp_fw[i];
- for (i = pm4_idx; i < adreno_dev->pm4_fw_size; i++)
- *cmds++ = adreno_dev->pm4_fw[i];
+ for (i = pfp_idx; i < pfp_fw->size; i++)
+ *cmds++ = pfp_fw->fwvirt[i];
+ for (i = pm4_idx; i < pm4_fw->size; i++)
+ *cmds++ = pm4_fw->fwvirt[i];
adreno_ringbuffer_submit(rb, NULL);
}
@@ -1835,6 +1839,8 @@
{
int status;
struct adreno_ringbuffer *rb = ADRENO_CURRENT_RINGBUFFER(adreno_dev);
+ size_t pm4_size = adreno_dev->fw[ADRENO_FW_PM4].size;
+ size_t pfp_size = adreno_dev->fw[ADRENO_FW_PFP].size;
if (start_type == ADRENO_START_COLD) {
/* If bootstrapping if supported to load ucode */
@@ -1863,12 +1869,10 @@
} else {
/* load the CP ucode using AHB writes */
- load_pm4_ucode(adreno_dev, 1, adreno_dev->pm4_fw_size,
- 0);
+ load_pm4_ucode(adreno_dev, 1, pm4_size, 0);
/* load the prefetch parser ucode using AHB writes */
- load_pfp_ucode(adreno_dev, 1, adreno_dev->pfp_fw_size,
- 0);
+ load_pfp_ucode(adreno_dev, 1, pfp_size, 0);
}
} else if (start_type == ADRENO_START_WARM) {
/* If bootstrapping if supported to load jump tables */
@@ -1881,16 +1885,14 @@
/* load the CP jump tables using AHB writes */
load_pm4_ucode(adreno_dev,
adreno_dev->gpucore->pm4_jt_idx,
- adreno_dev->pm4_fw_size,
- adreno_dev->gpucore->pm4_jt_addr);
+ pm4_size, adreno_dev->gpucore->pm4_jt_addr);
/*
* load the prefetch parser jump tables using AHB writes
*/
load_pfp_ucode(adreno_dev,
adreno_dev->gpucore->pfp_jt_idx,
- adreno_dev->pfp_fw_size,
- adreno_dev->gpucore->pfp_jt_addr);
+ pfp_size, adreno_dev->gpucore->pfp_jt_addr);
}
} else
return -EINVAL;
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index b02b9a5..f04baac 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -2202,15 +2202,17 @@
{
void *ptr;
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+ struct adreno_firmware *pm4_fw = ADRENO_FW(adreno_dev, ADRENO_FW_PM4);
+ struct adreno_firmware *pfp_fw = ADRENO_FW(adreno_dev, ADRENO_FW_PFP);
uint64_t gpuaddr;
- gpuaddr = adreno_dev->pm4.gpuaddr;
+ gpuaddr = pm4_fw->memdesc.gpuaddr;
kgsl_regwrite(device, A5XX_CP_PM4_INSTR_BASE_LO,
lower_32_bits(gpuaddr));
kgsl_regwrite(device, A5XX_CP_PM4_INSTR_BASE_HI,
upper_32_bits(gpuaddr));
- gpuaddr = adreno_dev->pfp.gpuaddr;
+ gpuaddr = pfp_fw->memdesc.gpuaddr;
kgsl_regwrite(device, A5XX_CP_PFP_INSTR_BASE_LO,
lower_32_bits(gpuaddr));
kgsl_regwrite(device, A5XX_CP_PFP_INSTR_BASE_HI,
@@ -2486,8 +2488,7 @@
}
static int _load_firmware(struct kgsl_device *device, const char *fwfile,
- struct kgsl_memdesc *ucode, size_t *ucode_size,
- unsigned int *ucode_version)
+ struct adreno_firmware *firmware)
{
const struct firmware *fw = NULL;
int ret;
@@ -2500,15 +2501,15 @@
return ret;
}
- ret = kgsl_allocate_global(device, ucode, fw->size - 4,
+ ret = kgsl_allocate_global(device, &firmware->memdesc, fw->size - 4,
KGSL_MEMFLAGS_GPUREADONLY, 0, "ucode");
if (ret)
goto done;
- memcpy(ucode->hostptr, &fw->data[4], fw->size - 4);
- *ucode_size = (fw->size - 4) / sizeof(uint32_t);
- *ucode_version = *(unsigned int *)&fw->data[4];
+ memcpy(firmware->memdesc.hostptr, &fw->data[4], fw->size - 4);
+ firmware->size = (fw->size - 4) / sizeof(uint32_t);
+ firmware->version = *(unsigned int *)&fw->data[4];
done:
release_firmware(fw);
@@ -2523,23 +2524,19 @@
static int a5xx_microcode_read(struct adreno_device *adreno_dev)
{
int ret;
+ struct adreno_firmware *pm4_fw = ADRENO_FW(adreno_dev, ADRENO_FW_PM4);
+ struct adreno_firmware *pfp_fw = ADRENO_FW(adreno_dev, ADRENO_FW_PFP);
- if (adreno_dev->pm4.hostptr == NULL) {
+ if (pm4_fw->memdesc.hostptr == NULL) {
ret = _load_firmware(KGSL_DEVICE(adreno_dev),
- adreno_dev->gpucore->pm4fw_name,
- &adreno_dev->pm4,
- &adreno_dev->pm4_fw_size,
- &adreno_dev->pm4_fw_version);
+ adreno_dev->gpucore->pm4fw_name, pm4_fw);
if (ret)
return ret;
}
- if (adreno_dev->pfp.hostptr == NULL) {
+ if (pfp_fw->memdesc.hostptr == NULL) {
ret = _load_firmware(KGSL_DEVICE(adreno_dev),
- adreno_dev->gpucore->pfpfw_name,
- &adreno_dev->pfp,
- &adreno_dev->pfp_fw_size,
- &adreno_dev->pfp_fw_version);
+ adreno_dev->gpucore->pfpfw_name, pfp_fw);
if (ret)
return ret;
}
diff --git a/drivers/gpu/msm/adreno_a5xx_snapshot.c b/drivers/gpu/msm/adreno_a5xx_snapshot.c
index c1e0a70..2e5913d 100644
--- a/drivers/gpu/msm/adreno_a5xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a5xx_snapshot.c
@@ -138,7 +138,8 @@
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct kgsl_snapshot_debug *header = (struct kgsl_snapshot_debug *)buf;
unsigned int *data = (unsigned int *)(buf + sizeof(*header));
- size_t size = adreno_dev->pm4_fw_size;
+ struct adreno_firmware *fw = ADRENO_FW(adreno_dev, ADRENO_FW_PM4);
+ size_t size = fw->size;
if (remain < DEBUG_SECTION_SZ(size)) {
SNAPSHOT_ERR_NOMEM(device, "CP PM4 RAM DEBUG");
@@ -148,7 +149,7 @@
header->type = SNAPSHOT_DEBUG_CP_PM4_RAM;
header->size = size;
- memcpy(data, adreno_dev->pm4.hostptr, size * sizeof(uint32_t));
+ memcpy(data, fw->memdesc.hostptr, size * sizeof(uint32_t));
return DEBUG_SECTION_SZ(size);
}
@@ -160,7 +161,8 @@
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct kgsl_snapshot_debug *header = (struct kgsl_snapshot_debug *)buf;
unsigned int *data = (unsigned int *)(buf + sizeof(*header));
- int size = adreno_dev->pfp_fw_size;
+ struct adreno_firmware *fw = ADRENO_FW(adreno_dev, ADRENO_FW_PFP);
+ int size = fw->size;
if (remain < DEBUG_SECTION_SZ(size)) {
SNAPSHOT_ERR_NOMEM(device, "CP PFP RAM DEBUG");
@@ -170,7 +172,7 @@
header->type = SNAPSHOT_DEBUG_CP_PFP_RAM;
header->size = size;
- memcpy(data, adreno_dev->pfp.hostptr, size * sizeof(uint32_t));
+ memcpy(data, fw->memdesc.hostptr, size * sizeof(uint32_t));
return DEBUG_SECTION_SZ(size);
}
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
new file mode 100644
index 0000000..1aea21c
--- /dev/null
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -0,0 +1,682 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/firmware.h>
+#include <linux/pm_opp.h>
+
+#include "adreno.h"
+#include "a6xx_reg.h"
+#include "adreno_cp_parser.h"
+#include "adreno_trace.h"
+#include "adreno_pm4types.h"
+#include "adreno_perfcounter.h"
+#include "adreno_ringbuffer.h"
+#include "kgsl_sharedmem.h"
+#include "kgsl_log.h"
+#include "kgsl.h"
+#include <linux/msm_kgsl.h>
+
+#define A6XX_CP_RB_CNTL_DEFAULT (((ilog2(4) << 8) & 0x1F00) | \
+ (ilog2(KGSL_RB_DWORDS >> 1) & 0x3F))
+
+#define MIN_HBB 13
+
+static const struct adreno_vbif_data a630_vbif[] = {
+ {A6XX_VBIF_GATE_OFF_WRREQ_EN, 0x00000009},
+ {A6XX_RBBM_VBIF_CLIENT_QOS_CNTL, 0x3},
+ {0, 0},
+};
+
+static const struct adreno_vbif_platform a6xx_vbif_platforms[] = {
+ { adreno_is_a630, a630_vbif },
+};
+
+static struct a6xx_protected_regs {
+ unsigned int base;
+ unsigned int count;
+ int read_protect;
+} a6xx_protected_regs_group[] = {
+ { 0x600, 0x51, 0 },
+ { 0xAE50, 0x2, 1 },
+ { 0x9624, 0x13, 1 },
+ { 0x8630, 0x8, 1 },
+ { 0x9E70, 0x1, 1 },
+ { 0x9E78, 0x187, 1 },
+ { 0xF000, 0x810, 1 },
+ { 0xFC00, 0x3, 0 },
+ { 0x50E, 0x0, 1 },
+ { 0x50F, 0x0, 0 },
+ { 0x510, 0x0, 1 },
+ { 0x0, 0x4F9, 0 },
+ { 0x501, 0xA, 0 },
+ { 0x511, 0x44, 0 },
+ { 0xE00, 0xE, 1 },
+ { 0x8E00, 0x0, 1 },
+ { 0x8E50, 0xF, 1 },
+ { 0xBE02, 0x0, 1 },
+ { 0xBE20, 0x11F3, 1 },
+ { 0x800, 0x82, 1 },
+ { 0x8A0, 0x8, 1 },
+ { 0x8AB, 0x19, 1 },
+ { 0x900, 0x4D, 1 },
+ { 0x98D, 0x76, 1 },
+ { 0x8D0, 0x23, 0 },
+ { 0x980, 0x4, 0 },
+ { 0xA630, 0x0, 1 },
+};
+
+/* Print some key registers if a spin-for-idle times out */
+static void spin_idle_debug(struct kgsl_device *device,
+ const char *str)
+{
+ unsigned int rptr, wptr;
+ unsigned int status, status3, intstatus;
+ unsigned int hwfault;
+
+ dev_err(device->dev, str);
+
+ kgsl_regread(device, A6XX_CP_RB_RPTR, &rptr);
+ kgsl_regread(device, A6XX_CP_RB_WPTR, &wptr);
+
+ kgsl_regread(device, A6XX_RBBM_STATUS, &status);
+ kgsl_regread(device, A6XX_RBBM_STATUS3, &status3);
+ kgsl_regread(device, A6XX_RBBM_INT_0_STATUS, &intstatus);
+ kgsl_regread(device, A6XX_CP_HW_FAULT, &hwfault);
+
+ dev_err(device->dev,
+ " rb=%X/%X rbbm_status=%8.8X/%8.8X int_0_status=%8.8X\n",
+ rptr, wptr, status, status3, intstatus);
+ dev_err(device->dev, " hwfault=%8.8X\n", hwfault);
+}
+
+static void a6xx_platform_setup(struct adreno_device *adreno_dev)
+{
+ uint64_t addr;
+
+ /* Calculate SP local and private mem addresses */
+ addr = ALIGN(ADRENO_UCHE_GMEM_BASE + adreno_dev->gmem_size, SZ_64K);
+ adreno_dev->sp_local_gpuaddr = addr;
+ adreno_dev->sp_pvt_gpuaddr = addr + SZ_64K;
+}
+
+/**
+ * a6xx_protect_init() - Initializes register protection on a6xx
+ * @device: Pointer to the device structure
+ * Performs register writes to enable protected access to sensitive
+ * registers
+ */
+static void a6xx_protect_init(struct adreno_device *adreno_dev)
+{
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+ int i;
+
+ /* enable access protection to privileged registers */
+ kgsl_regwrite(device, A6XX_CP_PROTECT_CNTL, 0x00000007);
+
+ if (ARRAY_SIZE(a6xx_protected_regs_group) >
+ adreno_dev->gpucore->num_protected_regs)
+ WARN(1, "Size exceeds the num of protection regs available\n");
+
+ for (i = 0; i < ARRAY_SIZE(a6xx_protected_regs_group); i++) {
+ struct a6xx_protected_regs *regs =
+ &a6xx_protected_regs_group[i];
+
+ kgsl_regwrite(device, A6XX_CP_PROTECT_REG + i,
+ regs->base | (regs->count << 18) |
+ (regs->read_protect << 31));
+ }
+
+}
+
+static void a6xx_enable_64bit(struct adreno_device *adreno_dev)
+{
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+
+ kgsl_regwrite(device, A6XX_CP_ADDR_MODE_CNTL, 0x1);
+ kgsl_regwrite(device, A6XX_VSC_ADDR_MODE_CNTL, 0x1);
+ kgsl_regwrite(device, A6XX_GRAS_ADDR_MODE_CNTL, 0x1);
+ kgsl_regwrite(device, A6XX_RB_ADDR_MODE_CNTL, 0x1);
+ kgsl_regwrite(device, A6XX_PC_ADDR_MODE_CNTL, 0x1);
+ kgsl_regwrite(device, A6XX_HLSQ_ADDR_MODE_CNTL, 0x1);
+ kgsl_regwrite(device, A6XX_VFD_ADDR_MODE_CNTL, 0x1);
+ kgsl_regwrite(device, A6XX_VPC_ADDR_MODE_CNTL, 0x1);
+ kgsl_regwrite(device, A6XX_UCHE_ADDR_MODE_CNTL, 0x1);
+ kgsl_regwrite(device, A6XX_SP_ADDR_MODE_CNTL, 0x1);
+ kgsl_regwrite(device, A6XX_TPL1_ADDR_MODE_CNTL, 0x1);
+ kgsl_regwrite(device, A6XX_RBBM_SECVID_TSB_ADDR_MODE_CNTL, 0x1);
+}
+
+/*
+ * a6xx_start() - Device start
+ * @adreno_dev: Pointer to adreno device
+ *
+ * a6xx device start
+ */
+static void a6xx_start(struct adreno_device *adreno_dev)
+{
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+ unsigned int bit, mal, mode;
+ unsigned int amsbc = 0;
+
+ adreno_vbif_start(adreno_dev, a6xx_vbif_platforms,
+ ARRAY_SIZE(a6xx_vbif_platforms));
+ /*
+ * Set UCHE_WRITE_THRU_BASE to the UCHE_TRAP_BASE effectively
+ * disabling L2 bypass
+ */
+ kgsl_regwrite(device, A6XX_UCHE_WRITE_RANGE_MAX_LO, 0xffffffc0);
+ kgsl_regwrite(device, A6XX_UCHE_WRITE_RANGE_MAX_HI, 0x0001ffff);
+ kgsl_regwrite(device, A6XX_UCHE_TRAP_BASE_LO, 0xfffff000);
+ kgsl_regwrite(device, A6XX_UCHE_TRAP_BASE_HI, 0x0001ffff);
+ kgsl_regwrite(device, A6XX_UCHE_WRITE_THRU_BASE_LO, 0xfffff000);
+ kgsl_regwrite(device, A6XX_UCHE_WRITE_THRU_BASE_HI, 0x0001ffff);
+
+ /* Program the GMEM VA range for the UCHE path */
+ kgsl_regwrite(device, A6XX_UCHE_GMEM_RANGE_MIN_LO,
+ ADRENO_UCHE_GMEM_BASE);
+ kgsl_regwrite(device, A6XX_UCHE_GMEM_RANGE_MIN_HI, 0x0);
+ kgsl_regwrite(device, A6XX_UCHE_GMEM_RANGE_MAX_LO,
+ ADRENO_UCHE_GMEM_BASE +
+ adreno_dev->gmem_size - 1);
+ kgsl_regwrite(device, A6XX_UCHE_GMEM_RANGE_MAX_HI, 0x0);
+
+ kgsl_regwrite(device, A6XX_UCHE_FILTER_CNTL, 0x804);
+ kgsl_regwrite(device, A6XX_UCHE_CACHE_WAYS, 0x4);
+
+ kgsl_regwrite(device, A6XX_CP_ROQ_THRESHOLDS_2, 0x010000C0);
+ kgsl_regwrite(device, A6XX_CP_ROQ_THRESHOLDS_1, 0x8040362C);
+
+ /* Setting the mem pool size */
+ kgsl_regwrite(device, A6XX_CP_MEM_POOL_SIZE, 128);
+
+ /* Setting the primFifo thresholds default values */
+ kgsl_regwrite(device, A6XX_PC_DBG_ECO_CNTL, (0x300 << 11));
+
+ /* Disable secured mode */
+ kgsl_regwrite(device, A6XX_RBBM_SECVID_TRUST_CNTL, 0x0);
+
+ /* Set the AHB default slave response to "ERROR" */
+ kgsl_regwrite(device, A6XX_CP_AHB_CNTL, 0x1);
+
+ if (of_property_read_u32(device->pdev->dev.of_node,
+ "qcom,highest-bank-bit", &bit))
+ bit = MIN_HBB;
+
+ if (of_property_read_u32(device->pdev->dev.of_node,
+ "qcom,min-access-length", &mal))
+ mal = 32;
+
+ if (of_property_read_u32(device->pdev->dev.of_node,
+ "qcom,ubwc-mode", &mode))
+ mode = 0;
+
+ switch (mode) {
+ case KGSL_UBWC_1_0:
+ mode = 1;
+ break;
+ case KGSL_UBWC_2_0:
+ mode = 0;
+ break;
+ case KGSL_UBWC_3_0:
+ mode = 0;
+ amsbc = 1; /* Only valid for A640 and A680 */
+ break;
+ default:
+ break;
+ }
+
+ if (bit >= 13 && bit <= 16)
+ bit = (bit - 13) & 0x03;
+ else
+ bit = 0;
+
+ mal = (mal == 64) ? 1 : 0;
+
+ kgsl_regwrite(device, A6XX_RB_NC_MODE_CNTL, (amsbc << 4) | (mal << 3) |
+ (bit << 1) | mode);
+ kgsl_regwrite(device, A6XX_TPL1_NC_MODE_CNTL, (mal << 3) |
+ (bit << 1) | mode);
+ kgsl_regwrite(device, A6XX_SP_NC_MODE_CNTL, (mal << 3) | (bit << 1) |
+ mode);
+
+ /* (1 << 29)globalInvFlushFilterDis bit needs to be set for A630 V1 */
+ kgsl_regwrite(device, A6XX_UCHE_MODE_CNTL, (1 << 29) | (mal << 23) |
+ (bit << 21));
+
+ kgsl_regwrite(device, A6XX_RBBM_INTERFACE_HANG_INT_CNTL,
+ (1 << 30) | 0x4000);
+
+ a6xx_protect_init(adreno_dev);
+}
+
+/*
+ * a6xx_microcode_load() - Load microcode
+ * @adreno_dev: Pointer to adreno device
+ */
+static int a6xx_microcode_load(struct adreno_device *adreno_dev)
+{
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+ struct adreno_firmware *fw = ADRENO_FW(adreno_dev, ADRENO_FW_SQE);
+ uint64_t gpuaddr;
+
+ gpuaddr = fw->memdesc.gpuaddr;
+ kgsl_regwrite(device, A6XX_CP_SQE_INSTR_BASE_LO,
+ lower_32_bits(gpuaddr));
+ kgsl_regwrite(device, A6XX_CP_SQE_INSTR_BASE_HI,
+ upper_32_bits(gpuaddr));
+
+ return 0;
+}
+
+
+/*
+ * CP_INIT_MAX_CONTEXT bit tells if the multiple hardware contexts can
+ * be used at once of if they should be serialized
+ */
+#define CP_INIT_MAX_CONTEXT BIT(0)
+
+/* Enables register protection mode */
+#define CP_INIT_ERROR_DETECTION_CONTROL BIT(1)
+
+/* Header dump information */
+#define CP_INIT_HEADER_DUMP BIT(2) /* Reserved */
+
+/* Default Reset states enabled for PFP and ME */
+#define CP_INIT_DEFAULT_RESET_STATE BIT(3)
+
+/* Drawcall filter range */
+#define CP_INIT_DRAWCALL_FILTER_RANGE BIT(4)
+
+/* Ucode workaround masks */
+#define CP_INIT_UCODE_WORKAROUND_MASK BIT(5)
+
+#define CP_INIT_MASK (CP_INIT_MAX_CONTEXT | \
+ CP_INIT_ERROR_DETECTION_CONTROL | \
+ CP_INIT_HEADER_DUMP | \
+ CP_INIT_DEFAULT_RESET_STATE | \
+ CP_INIT_UCODE_WORKAROUND_MASK)
+
+static void _set_ordinals(struct adreno_device *adreno_dev,
+ unsigned int *cmds, unsigned int count)
+{
+ unsigned int *start = cmds;
+
+ /* Enabled ordinal mask */
+ *cmds++ = CP_INIT_MASK;
+
+ if (CP_INIT_MASK & CP_INIT_MAX_CONTEXT)
+ *cmds++ = 0x00000003;
+
+ if (CP_INIT_MASK & CP_INIT_ERROR_DETECTION_CONTROL)
+ *cmds++ = 0x20000000;
+
+ if (CP_INIT_MASK & CP_INIT_HEADER_DUMP) {
+ /* Header dump address */
+ *cmds++ = 0x00000000;
+ /* Header dump enable and dump size */
+ *cmds++ = 0x00000000;
+ }
+
+ if (CP_INIT_MASK & CP_INIT_DRAWCALL_FILTER_RANGE) {
+ /* Start range */
+ *cmds++ = 0x00000000;
+ /* End range (inclusive) */
+ *cmds++ = 0x00000000;
+ }
+
+ if (CP_INIT_MASK & CP_INIT_UCODE_WORKAROUND_MASK)
+ *cmds++ = 0x00000000;
+
+ /* Pad rest of the cmds with 0's */
+ while ((unsigned int)(cmds - start) < count)
+ *cmds++ = 0x0;
+}
+
+/*
+ * a6xx_send_cp_init() - Initialize ringbuffer
+ * @adreno_dev: Pointer to adreno device
+ * @rb: Pointer to the ringbuffer of device
+ *
+ * Submit commands for ME initialization,
+ */
+static int a6xx_send_cp_init(struct adreno_device *adreno_dev,
+ struct adreno_ringbuffer *rb)
+{
+ unsigned int *cmds;
+ int ret;
+
+ cmds = adreno_ringbuffer_allocspace(rb, 9);
+ if (IS_ERR(cmds))
+ return PTR_ERR(cmds);
+
+ *cmds++ = cp_type7_packet(CP_ME_INIT, 8);
+
+ _set_ordinals(adreno_dev, cmds, 8);
+
+ ret = adreno_ringbuffer_submit_spin(rb, NULL, 2000);
+ if (ret)
+ spin_idle_debug(KGSL_DEVICE(adreno_dev),
+ "CP initialization failed to idle\n");
+
+ return ret;
+}
+
+/*
+ * a6xx_rb_start() - Start the ringbuffer
+ * @adreno_dev: Pointer to adreno device
+ * @start_type: Warm or cold start
+ */
+static int a6xx_rb_start(struct adreno_device *adreno_dev,
+ unsigned int start_type)
+{
+ struct adreno_ringbuffer *rb = ADRENO_CURRENT_RINGBUFFER(adreno_dev);
+ struct kgsl_device *device = &adreno_dev->dev;
+ uint64_t addr;
+ int ret;
+
+ addr = SCRATCH_RPTR_GPU_ADDR(device, rb->id);
+
+ adreno_writereg64(adreno_dev, ADRENO_REG_CP_RB_RPTR_ADDR_LO,
+ ADRENO_REG_CP_RB_RPTR_ADDR_HI, addr);
+
+ /*
+ * The size of the ringbuffer in the hardware is the log2
+ * representation of the size in quadwords (sizedwords / 2).
+ */
+ adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_CNTL,
+ A6XX_CP_RB_CNTL_DEFAULT);
+
+ adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_BASE,
+ rb->buffer_desc.gpuaddr);
+
+ ret = a6xx_microcode_load(adreno_dev);
+ if (ret)
+ return ret;
+
+ /* Clear the SQE_HALT to start the CP engine */
+ kgsl_regwrite(device, A6XX_CP_SQE_CNTL, 1);
+
+ return a6xx_send_cp_init(adreno_dev, rb);
+}
+
+static int _load_firmware(struct kgsl_device *device, const char *fwfile,
+ struct adreno_firmware *firmware)
+{
+ const struct firmware *fw = NULL;
+ int ret;
+
+ ret = request_firmware(&fw, fwfile, device->dev);
+
+ if (ret) {
+ KGSL_DRV_ERR(device, "request_firmware(%s) failed: %d\n",
+ fwfile, ret);
+ return ret;
+ }
+
+ ret = kgsl_allocate_global(device, &firmware->memdesc, fw->size - 4,
+ KGSL_MEMFLAGS_GPUREADONLY, 0, "ucode");
+
+ if (!ret) {
+ memcpy(firmware->memdesc.hostptr, &fw->data[4], fw->size - 4);
+ firmware->size = (fw->size - 4) / sizeof(uint32_t);
+ firmware->version = *(unsigned int *)&fw->data[4];
+ }
+
+ release_firmware(fw);
+
+ return ret;
+}
+
+#define SPTPRAC_POWER_CONTROL_OFFSET 0x204
+#define SPTPRAC_PWR_CLK_STATUS_OFFSET 0x14340
+#define SPTPRAC_POWERON_CTRL_MASK 0x00778000
+#define SPTPRAC_POWEROFF_CTRL_MASK 0x00778001
+#define SPTPRAC_POWERON_STATUS_MASK BIT(3)
+#define SPTPRAC_CTRL_TIMEOUT 10 /* ms */
+
+static int a6xx_sptprac_enable(struct adreno_device *adreno_dev)
+{
+ void __iomem *gmu_reg;
+ unsigned long t;
+ unsigned int val;
+ int ret;
+
+ gmu_reg = ioremap(0x506a000, 0x26000);
+
+ __raw_writel(SPTPRAC_POWERON_CTRL_MASK,
+ gmu_reg + SPTPRAC_POWER_CONTROL_OFFSET);
+
+ /* Make sure the above write is observed before the reads below */
+ wmb();
+
+ t = jiffies + msecs_to_jiffies(SPTPRAC_CTRL_TIMEOUT);
+
+ ret = -EINVAL;
+ while (!time_after(jiffies, t)) {
+ val = __raw_readl(gmu_reg + SPTPRAC_PWR_CLK_STATUS_OFFSET);
+ /*
+ * Make sure the above read completes before polling the
+ * register again
+ */
+ rmb();
+
+ if ((val & SPTPRAC_POWERON_STATUS_MASK) ==
+ SPTPRAC_POWERON_STATUS_MASK) {
+ ret = 0;
+ break;
+ }
+ cpu_relax();
+ }
+
+ iounmap(gmu_reg);
+
+ return ret;
+}
+
+static void a6xx_sptprac_disable(struct adreno_device *adreno_dev)
+{
+ void __iomem *gmu_reg;
+
+ gmu_reg = ioremap(0x506a000, 0x26000);
+
+ __raw_writel(SPTPRAC_POWEROFF_CTRL_MASK,
+ gmu_reg + SPTPRAC_POWER_CONTROL_OFFSET);
+ /* Make sure the above write posts before moving on */
+ wmb();
+
+ iounmap(gmu_reg);
+}
+
+/*
+ * a6xx_microcode_read() - Read microcode
+ * @adreno_dev: Pointer to adreno device
+ */
+static int a6xx_microcode_read(struct adreno_device *adreno_dev)
+{
+ return _load_firmware(KGSL_DEVICE(adreno_dev),
+ adreno_dev->gpucore->sqefw_name,
+ ADRENO_FW(adreno_dev, ADRENO_FW_SQE));
+}
+
+static void a6xx_cp_hw_err_callback(struct adreno_device *adreno_dev, int bit)
+{
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+ unsigned int status1, status2;
+
+ kgsl_regread(device, A6XX_CP_INTERRUPT_STATUS, &status1);
+
+ if (status1 & BIT(A6XX_CP_OPCODE_ERROR))
+ KGSL_DRV_CRIT_RATELIMIT(device, "CP opcode error interrupt\n");
+ if (status1 & BIT(A6XX_CP_UCODE_ERROR))
+ KGSL_DRV_CRIT_RATELIMIT(device, "CP ucode error interrupt\n");
+ if (status1 & BIT(A6XX_CP_HW_FAULT_ERROR)) {
+ kgsl_regread(device, A6XX_CP_HW_FAULT, &status2);
+ KGSL_DRV_CRIT_RATELIMIT(device,
+ "CP | Ringbuffer HW fault | status=%x\n",
+ status2);
+ }
+ if (status1 & BIT(A6XX_CP_REGISTER_PROTECTION_ERROR)) {
+ kgsl_regread(device, A6XX_CP_PROTECT_STATUS, &status2);
+ KGSL_DRV_CRIT_RATELIMIT(device,
+ "CP | Protected mode error | %s | addr=%x | status=%x\n",
+ status2 & (1 << 20) ? "READ" : "WRITE",
+ (status2 & 0x3FFFF) >> 2, status2);
+ }
+ if (status1 & BIT(A6XX_CP_AHB_ERROR))
+ KGSL_DRV_CRIT_RATELIMIT(device,
+ "CP AHB error interrupt\n");
+ if (status1 & BIT(A6XX_CP_VSD_PARITY_ERROR))
+ KGSL_DRV_CRIT_RATELIMIT(device,
+ "CP VSD decoder parity error\n");
+ if (status1 & BIT(A6XX_CP_ILLEGAL_INSTR_ERROR))
+ KGSL_DRV_CRIT_RATELIMIT(device,
+ "CP Illegal instruction error\n");
+
+}
+
+static void a6xx_err_callback(struct adreno_device *adreno_dev, int bit)
+{
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+
+ switch (bit) {
+ case A6XX_INT_CP_AHB_ERROR:
+ KGSL_DRV_CRIT_RATELIMIT(device, "CP: AHB bus error\n");
+ break;
+ case A6XX_INT_ATB_ASYNCFIFO_OVERFLOW:
+ KGSL_DRV_CRIT_RATELIMIT(device, "RBBM: ATB ASYNC overflow\n");
+ break;
+ case A6XX_INT_RBBM_ATB_BUS_OVERFLOW:
+ KGSL_DRV_CRIT_RATELIMIT(device, "RBBM: ATB bus overflow\n");
+ break;
+ case A6XX_INT_UCHE_OOB_ACCESS:
+ KGSL_DRV_CRIT_RATELIMIT(device, "UCHE: Out of bounds access\n");
+ break;
+ case A6XX_INT_UCHE_TRAP_INTR:
+ KGSL_DRV_CRIT_RATELIMIT(device, "UCHE: Trap interrupt\n");
+ break;
+ default:
+ KGSL_DRV_CRIT_RATELIMIT(device, "Unknown interrupt %d\n", bit);
+ }
+}
+
+#define A6XX_INT_MASK \
+ ((1 << A6XX_INT_CP_AHB_ERROR) | \
+ (1 << A6XX_INT_ATB_ASYNCFIFO_OVERFLOW) | \
+ (1 << A6XX_INT_RBBM_GPC_ERROR) | \
+ (1 << A6XX_INT_CP_SW) | \
+ (1 << A6XX_INT_CP_HW_ERROR) | \
+ (1 << A6XX_INT_CP_IB2) | \
+ (1 << A6XX_INT_CP_IB1) | \
+ (1 << A6XX_INT_CP_RB) | \
+ (1 << A6XX_INT_CP_CACHE_FLUSH_TS) | \
+ (1 << A6XX_INT_RBBM_ATB_BUS_OVERFLOW) | \
+ (1 << A6XX_INT_RBBM_HANG_DETECT) | \
+ (1 << A6XX_INT_UCHE_OOB_ACCESS) | \
+ (1 << A6XX_INT_UCHE_TRAP_INTR))
+
+static struct adreno_irq_funcs a6xx_irq_funcs[32] = {
+ ADRENO_IRQ_CALLBACK(NULL), /* 0 - RBBM_GPU_IDLE */
+ ADRENO_IRQ_CALLBACK(a6xx_err_callback), /* 1 - RBBM_AHB_ERROR */
+ ADRENO_IRQ_CALLBACK(NULL), /* 2 - UNUSED */
+ ADRENO_IRQ_CALLBACK(NULL), /* 3 - UNUSED */
+ ADRENO_IRQ_CALLBACK(NULL), /* 4 - UNUSED */
+ ADRENO_IRQ_CALLBACK(NULL), /* 5 - UNUSED */
+ /* 6 - RBBM_ATB_ASYNC_OVERFLOW */
+ ADRENO_IRQ_CALLBACK(a6xx_err_callback),
+ ADRENO_IRQ_CALLBACK(NULL), /* 7 - GPC_ERR */
+ ADRENO_IRQ_CALLBACK(NULL),/* 8 - CP_SW */
+ ADRENO_IRQ_CALLBACK(a6xx_cp_hw_err_callback), /* 9 - CP_HW_ERROR */
+ ADRENO_IRQ_CALLBACK(NULL), /* 10 - CP_CCU_FLUSH_DEPTH_TS */
+ ADRENO_IRQ_CALLBACK(NULL), /* 11 - CP_CCU_FLUSH_COLOR_TS */
+ ADRENO_IRQ_CALLBACK(NULL), /* 12 - CP_CCU_RESOLVE_TS */
+ ADRENO_IRQ_CALLBACK(adreno_cp_callback), /* 13 - CP_IB2_INT */
+ ADRENO_IRQ_CALLBACK(adreno_cp_callback), /* 14 - CP_IB1_INT */
+ ADRENO_IRQ_CALLBACK(adreno_cp_callback), /* 15 - CP_RB_INT */
+ ADRENO_IRQ_CALLBACK(NULL), /* 16 - UNUSED */
+ ADRENO_IRQ_CALLBACK(NULL), /* 17 - CP_RB_DONE_TS */
+ ADRENO_IRQ_CALLBACK(NULL), /* 18 - CP_WT_DONE_TS */
+ ADRENO_IRQ_CALLBACK(NULL), /* 19 - UNUSED */
+ ADRENO_IRQ_CALLBACK(adreno_cp_callback), /* 20 - CP_CACHE_FLUSH_TS */
+ ADRENO_IRQ_CALLBACK(NULL), /* 21 - UNUSED */
+ ADRENO_IRQ_CALLBACK(a6xx_err_callback), /* 22 - RBBM_ATB_BUS_OVERFLOW */
+ /* 23 - MISC_HANG_DETECT */
+ ADRENO_IRQ_CALLBACK(adreno_hang_int_callback),
+ ADRENO_IRQ_CALLBACK(a6xx_err_callback), /* 24 - UCHE_OOB_ACCESS */
+ ADRENO_IRQ_CALLBACK(a6xx_err_callback), /* 25 - UCHE_TRAP_INTR */
+ ADRENO_IRQ_CALLBACK(NULL), /* 26 - DEBBUS_INTR_0 */
+ ADRENO_IRQ_CALLBACK(NULL), /* 27 - DEBBUS_INTR_1 */
+ ADRENO_IRQ_CALLBACK(NULL), /* 28 - UNUSED */
+ ADRENO_IRQ_CALLBACK(NULL), /* 29 - UNUSED */
+ ADRENO_IRQ_CALLBACK(NULL), /* 30 - ISDB_CPU_IRQ */
+ ADRENO_IRQ_CALLBACK(NULL), /* 31 - ISDB_UNDER_DEBUG */
+};
+
+static struct adreno_irq a6xx_irq = {
+ .funcs = a6xx_irq_funcs,
+ .mask = A6XX_INT_MASK,
+};
+
+/* Register offset defines for A6XX, in order of enum adreno_regs */
+static unsigned int a6xx_register_offsets[ADRENO_REG_REGISTER_MAX] = {
+
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_BASE, A6XX_CP_RB_BASE),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_RPTR_ADDR_LO,
+ A6XX_CP_RB_RPTR_ADDR_LO),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_RPTR_ADDR_HI,
+ A6XX_CP_RB_RPTR_ADDR_HI),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_RPTR, A6XX_CP_RB_RPTR),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_WPTR, A6XX_CP_RB_WPTR),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_CNTL, A6XX_CP_RB_CNTL),
+ ADRENO_REG_DEFINE(ADRENO_REG_CP_CNTL, A6XX_CP_MISC_CNTL),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_STATUS, A6XX_RBBM_STATUS),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_STATUS3, A6XX_RBBM_STATUS3),
+
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_INT_0_MASK, A6XX_RBBM_INT_0_MASK),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_INT_0_STATUS, A6XX_RBBM_INT_0_STATUS),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_CLOCK_CTL, A6XX_RBBM_CLOCK_CNTL),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_INT_CLEAR_CMD,
+ A6XX_RBBM_INT_CLEAR_CMD),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_SW_RESET_CMD, A6XX_RBBM_SW_RESET_CMD),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_BLOCK_SW_RESET_CMD,
+ A6XX_RBBM_BLOCK_SW_RESET_CMD),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_BLOCK_SW_RESET_CMD2,
+ A6XX_RBBM_BLOCK_SW_RESET_CMD2),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_ALWAYSON_COUNTER_LO,
+ A6XX_CP_ALWAYS_ON_COUNTER_LO),
+ ADRENO_REG_DEFINE(ADRENO_REG_RBBM_ALWAYSON_COUNTER_HI,
+ A6XX_CP_ALWAYS_ON_COUNTER_HI),
+ ADRENO_REG_DEFINE(ADRENO_REG_VBIF_VERSION, A6XX_VBIF_VERSION),
+ ADRENO_REG_DEFINE(ADRENO_REG_VBIF_XIN_HALT_CTRL0,
+ A6XX_VBIF_XIN_HALT_CTRL0),
+ ADRENO_REG_DEFINE(ADRENO_REG_VBIF_XIN_HALT_CTRL1,
+ A6XX_VBIF_XIN_HALT_CTRL1),
+
+};
+
+static const struct adreno_reg_offsets a6xx_reg_offsets = {
+ .offsets = a6xx_register_offsets,
+ .offset_0 = ADRENO_REG_REGISTER_MAX,
+};
+
+struct adreno_gpudev adreno_a6xx_gpudev = {
+ .reg_offsets = &a6xx_reg_offsets,
+ .start = a6xx_start,
+ .irq = &a6xx_irq,
+ .irq_trace = trace_kgsl_a5xx_irq_status,
+ .num_prio_levels = KGSL_PRIORITY_MAX_RB_LEVELS,
+ .platform_setup = a6xx_platform_setup,
+ .rb_start = a6xx_rb_start,
+ .regulator_enable = a6xx_sptprac_enable,
+ .regulator_disable = a6xx_sptprac_disable,
+ .microcode_read = a6xx_microcode_read,
+ .enable_64bit = a6xx_enable_64bit,
+};
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index 2cb39ee..f6b27f7 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -237,6 +237,9 @@
int i, ret = 0;
unsigned int ts;
+ if (!test_bit(ADRENO_DEVICE_SOFT_FAULT_DETECT, &adreno_dev->priv))
+ return 1;
+
/* Check to see if the device is idle - if so report no hang */
if (_isidle(adreno_dev) == true)
ret = 1;
@@ -605,6 +608,16 @@
if (!test_and_set_bit(ADRENO_DISPATCHER_ACTIVE,
&dispatcher->priv))
reinit_completion(&dispatcher->idle_gate);
+
+ /*
+ * We update power stats generally at the expire of
+ * cmdbatch. In cases where the cmdbatch takes a long
+ * time to finish, it will delay power stats update,
+ * in effect it will delay DCVS decision. Start a
+ * timer to update power state on expire of this timer.
+ */
+ kgsl_pwrscale_midframe_timer_restart(device);
+
} else {
kgsl_active_count_put(device);
clear_bit(ADRENO_DISPATCHER_POWER, &dispatcher->priv);
diff --git a/drivers/gpu/msm/adreno_iommu.c b/drivers/gpu/msm/adreno_iommu.c
index aa41f11..5d5bd19 100644
--- a/drivers/gpu/msm/adreno_iommu.c
+++ b/drivers/gpu/msm/adreno_iommu.c
@@ -658,7 +658,10 @@
*cmds++ = (drawctxt ? drawctxt->base.id : 0);
/* Invalidate UCHE for new context */
- if (adreno_is_a5xx(adreno_dev)) {
+ if (adreno_is_a6xx(adreno_dev)) {
+ *cmds++ = cp_packet(adreno_dev, CP_EVENT_WRITE, 1);
+ *cmds++ = 0x31; /* CACHE_INVALIDATE */
+ } else if (adreno_is_a5xx(adreno_dev)) {
*cmds++ = cp_register(adreno_dev,
adreno_getreg(adreno_dev,
ADRENO_REG_UCHE_INVALIDATE0), 1);
@@ -872,7 +875,7 @@
/* Just do the context switch incase of NOMMU */
if (kgsl_mmu_get_mmutype(device) == KGSL_MMU_TYPE_NONE) {
if ((!(flags & ADRENO_CONTEXT_SWITCH_FORCE_GPU)) &&
- adreno_isidle(device))
+ adreno_isidle(device) && !adreno_is_a6xx(adreno_dev))
_set_ctxt_cpu(rb, drawctxt);
else
result = _set_ctxt_gpu(rb, drawctxt);
@@ -898,7 +901,7 @@
return result;
/* Context switch */
- if (cpu_path)
+ if (cpu_path && !adreno_is_a6xx(adreno_dev))
_set_ctxt_cpu(rb, drawctxt);
else
result = _set_ctxt_gpu(rb, drawctxt);
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
index e79e5e3..f17d349 100644
--- a/drivers/gpu/msm/adreno_snapshot.c
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -1036,7 +1036,8 @@
struct kgsl_snapshot_debug *header = (struct kgsl_snapshot_debug *)buf;
unsigned int *data = (unsigned int *)(buf + sizeof(*header));
int i;
- size_t size = adreno_dev->pm4_fw_size - 1;
+ struct adreno_firmware *fw = ADRENO_FW(adreno_dev, ADRENO_FW_PM4);
+ size_t size = fw->size - 1;
if (remain < DEBUG_SECTION_SZ(size)) {
SNAPSHOT_ERR_NOMEM(device, "CP PM4 RAM DEBUG");
@@ -1073,7 +1074,9 @@
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct kgsl_snapshot_debug *header = (struct kgsl_snapshot_debug *)buf;
unsigned int *data = (unsigned int *)(buf + sizeof(*header));
- int i, size = adreno_dev->pfp_fw_size - 1;
+ int i;
+ struct adreno_firmware *fw = ADRENO_FW(adreno_dev, ADRENO_FW_PFP);
+ int size = fw->size - 1;
if (remain < DEBUG_SECTION_SZ(size)) {
SNAPSHOT_ERR_NOMEM(device, "CP PFP RAM DEBUG");
diff --git a/drivers/gpu/msm/adreno_sysfs.c b/drivers/gpu/msm/adreno_sysfs.c
index 2d8be7a..0f8a7cc 100644
--- a/drivers/gpu/msm/adreno_sysfs.c
+++ b/drivers/gpu/msm/adreno_sysfs.c
@@ -77,34 +77,6 @@
return adreno_dev->ft_pf_policy;
}
-static int _ft_fast_hang_detect_store(struct adreno_device *adreno_dev,
- unsigned int val)
-{
- struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
-
- if (!test_bit(ADRENO_DEVICE_SOFT_FAULT_DETECT, &adreno_dev->priv))
- return 0;
-
- mutex_lock(&device->mutex);
-
- if (val) {
- if (!kgsl_active_count_get(device)) {
- adreno_fault_detect_start(adreno_dev);
- kgsl_active_count_put(device);
- }
- } else
- adreno_fault_detect_stop(adreno_dev);
-
- mutex_unlock(&device->mutex);
-
- return 0;
-}
-
-static unsigned int _ft_fast_hang_detect_show(struct adreno_device *adreno_dev)
-{
- return adreno_dev->fast_hang_detect;
-}
-
static int _ft_long_ib_detect_store(struct adreno_device *adreno_dev,
unsigned int val)
{
@@ -316,7 +288,6 @@
static ADRENO_SYSFS_U32(ft_policy);
static ADRENO_SYSFS_U32(ft_pagefault_policy);
-static ADRENO_SYSFS_BOOL(ft_fast_hang_detect);
static ADRENO_SYSFS_BOOL(ft_long_ib_detect);
static ADRENO_SYSFS_BOOL(ft_hang_intr_status);
@@ -334,7 +305,6 @@
static const struct device_attribute *_attr_list[] = {
&adreno_attr_ft_policy.attr,
&adreno_attr_ft_pagefault_policy.attr,
- &adreno_attr_ft_fast_hang_detect.attr,
&adreno_attr_ft_long_ib_detect.attr,
&adreno_attr_ft_hang_intr_status.attr,
&dev_attr_wake_nice.attr,
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index ea13419..b0e9292 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -2416,6 +2416,7 @@
case KGSL_STATE_ACTIVE:
kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
del_timer_sync(&device->idle_timer);
+ kgsl_pwrscale_midframe_timer_cancel(device);
device->ftbl->stop(device);
/* fall through */
case KGSL_STATE_AWARE:
@@ -2524,6 +2525,7 @@
case KGSL_STATE_ACTIVE:
kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
del_timer_sync(&device->idle_timer);
+ kgsl_pwrscale_midframe_timer_cancel(device);
break;
case KGSL_STATE_SLUMBER:
status = kgsl_pwrctrl_enable(device);
@@ -2548,6 +2550,8 @@
return -EBUSY;
}
+ kgsl_pwrscale_midframe_timer_cancel(device);
+
/*
* Read HW busy counters before going to NAP state.
* The data might be used by power scale governors
@@ -2587,6 +2591,7 @@
/* fall through */
case KGSL_STATE_NAP:
del_timer_sync(&device->idle_timer);
+ kgsl_pwrscale_midframe_timer_cancel(device);
if (device->pwrctrl.thermal_cycle == CYCLE_ACTIVE) {
device->pwrctrl.thermal_cycle = CYCLE_ENABLE;
del_timer_sync(&device->pwrctrl.thermal_timer);
diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c
index 77ff91b..ee38f93b 100644
--- a/drivers/gpu/msm/kgsl_pwrscale.c
+++ b/drivers/gpu/msm/kgsl_pwrscale.c
@@ -13,6 +13,7 @@
#include <linux/export.h>
#include <linux/kernel.h>
+#include <linux/hrtimer.h>
#include "kgsl.h"
#include "kgsl_pwrscale.h"
@@ -37,6 +38,18 @@
{0, 0},
};
+/**
+ * struct kgsl_midframe_info - midframe power stats sampling info
+ * @timer - midframe sampling timer
+ * @timer_check_ws - Updates powerstats on midframe expiry
+ * @device - pointer to kgsl_device
+ */
+static struct kgsl_midframe_info {
+ struct hrtimer timer;
+ struct work_struct timer_check_ws;
+ struct kgsl_device *device;
+} *kgsl_midframe = NULL;
+
static void do_devfreq_suspend(struct work_struct *work);
static void do_devfreq_resume(struct work_struct *work);
static void do_devfreq_notify(struct work_struct *work);
@@ -187,9 +200,57 @@
if (device->state != KGSL_STATE_SLUMBER)
queue_work(device->pwrscale.devfreq_wq,
&device->pwrscale.devfreq_notify_ws);
+
+ kgsl_pwrscale_midframe_timer_restart(device);
}
EXPORT_SYMBOL(kgsl_pwrscale_update);
+void kgsl_pwrscale_midframe_timer_restart(struct kgsl_device *device)
+{
+ if (kgsl_midframe) {
+ WARN_ON(!mutex_is_locked(&device->mutex));
+
+ /* If the timer is already running, stop it */
+ if (hrtimer_active(&kgsl_midframe->timer))
+ hrtimer_cancel(
+ &kgsl_midframe->timer);
+
+ hrtimer_start(&kgsl_midframe->timer,
+ ns_to_ktime(KGSL_GOVERNOR_CALL_INTERVAL
+ * NSEC_PER_USEC), HRTIMER_MODE_REL);
+ }
+}
+EXPORT_SYMBOL(kgsl_pwrscale_midframe_timer_restart);
+
+void kgsl_pwrscale_midframe_timer_cancel(struct kgsl_device *device)
+{
+ if (kgsl_midframe) {
+ WARN_ON(!mutex_is_locked(&device->mutex));
+ hrtimer_cancel(&kgsl_midframe->timer);
+ }
+}
+EXPORT_SYMBOL(kgsl_pwrscale_midframe_timer_cancel);
+
+static void kgsl_pwrscale_midframe_timer_check(struct work_struct *work)
+{
+ struct kgsl_device *device = kgsl_midframe->device;
+
+ mutex_lock(&device->mutex);
+ if (device->state == KGSL_STATE_ACTIVE)
+ kgsl_pwrscale_update(device);
+ mutex_unlock(&device->mutex);
+}
+
+static enum hrtimer_restart kgsl_pwrscale_midframe_timer(struct hrtimer *timer)
+{
+ struct kgsl_device *device = kgsl_midframe->device;
+
+ queue_work(device->pwrscale.devfreq_wq,
+ &kgsl_midframe->timer_check_ws);
+
+ return HRTIMER_NORESTART;
+}
+
/*
* kgsl_pwrscale_disable - temporarily disable the governor
* @device: The device
@@ -860,6 +921,17 @@
data->bin.ctxt_aware_busy_penalty = 12000;
}
+ if (of_property_read_bool(device->pdev->dev.of_node,
+ "qcom,enable-midframe-timer")) {
+ kgsl_midframe = kzalloc(
+ sizeof(struct kgsl_midframe_info), GFP_KERNEL);
+ hrtimer_init(&kgsl_midframe->timer,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ kgsl_midframe->timer.function =
+ kgsl_pwrscale_midframe_timer;
+ kgsl_midframe->device = device;
+ }
+
/*
* If there is a separate GX power rail, allow
* independent modification to its voltage through
@@ -908,6 +980,9 @@
INIT_WORK(&pwrscale->devfreq_suspend_ws, do_devfreq_suspend);
INIT_WORK(&pwrscale->devfreq_resume_ws, do_devfreq_resume);
INIT_WORK(&pwrscale->devfreq_notify_ws, do_devfreq_notify);
+ if (kgsl_midframe)
+ INIT_WORK(&kgsl_midframe->timer_check_ws,
+ kgsl_pwrscale_midframe_timer_check);
pwrscale->next_governor_call = ktime_add_us(ktime_get(),
KGSL_GOVERNOR_CALL_INTERVAL);
@@ -946,9 +1021,13 @@
pwrscale = &device->pwrscale;
if (!pwrscale->devfreqptr)
return;
+
+ kgsl_pwrscale_midframe_timer_cancel(device);
flush_workqueue(pwrscale->devfreq_wq);
destroy_workqueue(pwrscale->devfreq_wq);
devfreq_remove_device(device->pwrscale.devfreqptr);
+ kfree(kgsl_midframe);
+ kgsl_midframe = NULL;
device->pwrscale.devfreqptr = NULL;
srcu_cleanup_notifier_head(&device->pwrscale.nh);
for (i = 0; i < KGSL_PWREVENT_MAX; i++)
diff --git a/drivers/gpu/msm/kgsl_pwrscale.h b/drivers/gpu/msm/kgsl_pwrscale.h
index 9a6ccac..e3d3dc7 100644
--- a/drivers/gpu/msm/kgsl_pwrscale.h
+++ b/drivers/gpu/msm/kgsl_pwrscale.h
@@ -122,6 +122,9 @@
void kgsl_pwrscale_sleep(struct kgsl_device *device);
void kgsl_pwrscale_wake(struct kgsl_device *device);
+void kgsl_pwrscale_midframe_timer_restart(struct kgsl_device *device);
+void kgsl_pwrscale_midframe_timer_cancel(struct kgsl_device *device);
+
void kgsl_pwrscale_enable(struct kgsl_device *device);
void kgsl_pwrscale_disable(struct kgsl_device *device, bool turbo);
diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c
index 717704e..c0303f6 100644
--- a/drivers/hid/hid-corsair.c
+++ b/drivers/hid/hid-corsair.c
@@ -148,26 +148,36 @@
struct usb_interface *usbif = to_usb_interface(dev->parent);
struct usb_device *usbdev = interface_to_usbdev(usbif);
int brightness;
- char data[8];
+ char *data;
+
+ data = kmalloc(8, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
K90_REQUEST_STATUS,
USB_DIR_IN | USB_TYPE_VENDOR |
USB_RECIP_DEVICE, 0, 0, data, 8,
USB_CTRL_SET_TIMEOUT);
- if (ret < 0) {
+ if (ret < 5) {
dev_warn(dev, "Failed to get K90 initial state (error %d).\n",
ret);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
brightness = data[4];
if (brightness < 0 || brightness > 3) {
dev_warn(dev,
"Read invalid backlight brightness: %02hhx.\n",
data[4]);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
- return brightness;
+ ret = brightness;
+out:
+ kfree(data);
+
+ return ret;
}
static enum led_brightness k90_record_led_get(struct led_classdev *led_cdev)
@@ -253,17 +263,22 @@
struct usb_interface *usbif = to_usb_interface(dev->parent);
struct usb_device *usbdev = interface_to_usbdev(usbif);
const char *macro_mode;
- char data[8];
+ char *data;
+
+ data = kmalloc(2, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
K90_REQUEST_GET_MODE,
USB_DIR_IN | USB_TYPE_VENDOR |
USB_RECIP_DEVICE, 0, 0, data, 2,
USB_CTRL_SET_TIMEOUT);
- if (ret < 0) {
+ if (ret < 1) {
dev_warn(dev, "Failed to get K90 initial mode (error %d).\n",
ret);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
switch (data[0]) {
@@ -277,10 +292,15 @@
default:
dev_warn(dev, "K90 in unknown mode: %02hhx.\n",
data[0]);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
- return snprintf(buf, PAGE_SIZE, "%s\n", macro_mode);
+ ret = snprintf(buf, PAGE_SIZE, "%s\n", macro_mode);
+out:
+ kfree(data);
+
+ return ret;
}
static ssize_t k90_store_macro_mode(struct device *dev,
@@ -320,26 +340,36 @@
struct usb_interface *usbif = to_usb_interface(dev->parent);
struct usb_device *usbdev = interface_to_usbdev(usbif);
int current_profile;
- char data[8];
+ char *data;
+
+ data = kmalloc(8, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
K90_REQUEST_STATUS,
USB_DIR_IN | USB_TYPE_VENDOR |
USB_RECIP_DEVICE, 0, 0, data, 8,
USB_CTRL_SET_TIMEOUT);
- if (ret < 0) {
+ if (ret < 8) {
dev_warn(dev, "Failed to get K90 initial state (error %d).\n",
ret);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
current_profile = data[7];
if (current_profile < 1 || current_profile > 3) {
dev_warn(dev, "Read invalid current profile: %02hhx.\n",
data[7]);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
- return snprintf(buf, PAGE_SIZE, "%d\n", current_profile);
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", current_profile);
+out:
+ kfree(data);
+
+ return ret;
}
static ssize_t k90_store_current_profile(struct device *dev,
diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c
index 60d3020..e06c134 100644
--- a/drivers/hid/hid-cp2112.c
+++ b/drivers/hid/hid-cp2112.c
@@ -167,7 +167,7 @@
atomic_t xfer_avail;
struct gpio_chip gc;
u8 *in_out_buffer;
- spinlock_t lock;
+ struct mutex lock;
};
static int gpio_push_pull = 0xFF;
@@ -179,10 +179,9 @@
struct cp2112_device *dev = gpiochip_get_data(chip);
struct hid_device *hdev = dev->hdev;
u8 *buf = dev->in_out_buffer;
- unsigned long flags;
int ret;
- spin_lock_irqsave(&dev->lock, flags);
+ mutex_lock(&dev->lock);
ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf,
CP2112_GPIO_CONFIG_LENGTH, HID_FEATURE_REPORT,
@@ -206,8 +205,8 @@
ret = 0;
exit:
- spin_unlock_irqrestore(&dev->lock, flags);
- return ret <= 0 ? ret : -EIO;
+ mutex_unlock(&dev->lock);
+ return ret < 0 ? ret : -EIO;
}
static void cp2112_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
@@ -215,10 +214,9 @@
struct cp2112_device *dev = gpiochip_get_data(chip);
struct hid_device *hdev = dev->hdev;
u8 *buf = dev->in_out_buffer;
- unsigned long flags;
int ret;
- spin_lock_irqsave(&dev->lock, flags);
+ mutex_lock(&dev->lock);
buf[0] = CP2112_GPIO_SET;
buf[1] = value ? 0xff : 0;
@@ -230,7 +228,7 @@
if (ret < 0)
hid_err(hdev, "error setting GPIO values: %d\n", ret);
- spin_unlock_irqrestore(&dev->lock, flags);
+ mutex_unlock(&dev->lock);
}
static int cp2112_gpio_get(struct gpio_chip *chip, unsigned offset)
@@ -238,10 +236,9 @@
struct cp2112_device *dev = gpiochip_get_data(chip);
struct hid_device *hdev = dev->hdev;
u8 *buf = dev->in_out_buffer;
- unsigned long flags;
int ret;
- spin_lock_irqsave(&dev->lock, flags);
+ mutex_lock(&dev->lock);
ret = hid_hw_raw_request(hdev, CP2112_GPIO_GET, buf,
CP2112_GPIO_GET_LENGTH, HID_FEATURE_REPORT,
@@ -255,7 +252,7 @@
ret = (buf[1] >> offset) & 1;
exit:
- spin_unlock_irqrestore(&dev->lock, flags);
+ mutex_unlock(&dev->lock);
return ret;
}
@@ -266,10 +263,9 @@
struct cp2112_device *dev = gpiochip_get_data(chip);
struct hid_device *hdev = dev->hdev;
u8 *buf = dev->in_out_buffer;
- unsigned long flags;
int ret;
- spin_lock_irqsave(&dev->lock, flags);
+ mutex_lock(&dev->lock);
ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf,
CP2112_GPIO_CONFIG_LENGTH, HID_FEATURE_REPORT,
@@ -290,7 +286,7 @@
goto fail;
}
- spin_unlock_irqrestore(&dev->lock, flags);
+ mutex_unlock(&dev->lock);
/*
* Set gpio value when output direction is already set,
@@ -301,7 +297,7 @@
return 0;
fail:
- spin_unlock_irqrestore(&dev->lock, flags);
+ mutex_unlock(&dev->lock);
return ret < 0 ? ret : -EIO;
}
@@ -1057,7 +1053,7 @@
if (!dev->in_out_buffer)
return -ENOMEM;
- spin_lock_init(&dev->lock);
+ mutex_init(&dev->lock);
ret = hid_parse(hdev);
if (ret) {
diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c
index 1b764d1..1689568 100644
--- a/drivers/hid/hid-cypress.c
+++ b/drivers/hid/hid-cypress.c
@@ -39,6 +39,9 @@
if (!(quirks & CP_RDESC_SWAPPED_MIN_MAX))
return rdesc;
+ if (*rsize < 4)
+ return rdesc;
+
for (i = 0; i < *rsize - 4; i++)
if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) {
rdesc[i] = 0x19;
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 575aa65..9845189 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -76,6 +76,9 @@
#define USB_VENDOR_ID_ALPS_JP 0x044E
#define HID_DEVICE_ID_ALPS_U1_DUAL 0x120B
+#define USB_VENDOR_ID_AMI 0x046b
+#define USB_DEVICE_ID_AMI_VIRT_KEYBOARD_AND_MOUSE 0xff10
+
#define USB_VENDOR_ID_ANTON 0x1130
#define USB_DEVICE_ID_ANTON_TOUCH_PAD 0x3101
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index c5c5fbe..52026dc 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -872,7 +872,7 @@
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
.driver_data = LG_NOGET | LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
- .driver_data = LG_FF2 },
+ .driver_data = LG_NOGET | LG_FF2 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
.driver_data = LG_FF3 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
index 6087562..8f6c353 100644
--- a/drivers/hid/hid-sensor-hub.c
+++ b/drivers/hid/hid-sensor-hub.c
@@ -212,7 +212,6 @@
__s32 value;
int ret = 0;
- memset(buffer, 0, buffer_size);
mutex_lock(&data->mutex);
report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT);
if (!report || (field_index >= report->maxfield)) {
@@ -256,6 +255,8 @@
int buffer_index = 0;
int i;
+ memset(buffer, 0, buffer_size);
+
mutex_lock(&data->mutex);
report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT);
if (!report || (field_index >= report->maxfield) ||
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index e6cfd32..cde060f 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -57,6 +57,7 @@
{ USB_VENDOR_ID_AIREN, USB_DEVICE_ID_AIREN_SLIMPLUS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_AKAI, USB_DEVICE_ID_AKAI_MPKMINI2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_AKAI_09E8, USB_DEVICE_ID_AKAI_09E8_MIDIMIX, HID_QUIRK_NO_INIT_REPORTS },
+ { USB_VENDOR_ID_AMI, USB_DEVICE_ID_AMI_VIRT_KEYBOARD_AND_MOUSE, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 1cb7992..623be90 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -164,19 +164,21 @@
wacom->id[0] = STYLUS_DEVICE_ID;
}
- pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
- if (features->pressure_max > 255)
- pressure = (pressure << 1) | ((data[4] >> 6) & 1);
- pressure += (features->pressure_max + 1) / 2;
+ if (prox) {
+ pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
+ if (features->pressure_max > 255)
+ pressure = (pressure << 1) | ((data[4] >> 6) & 1);
+ pressure += (features->pressure_max + 1) / 2;
- input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
- input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
- input_report_abs(input, ABS_PRESSURE, pressure);
+ input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
+ input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
+ input_report_abs(input, ABS_PRESSURE, pressure);
- input_report_key(input, BTN_TOUCH, data[4] & 0x08);
- input_report_key(input, BTN_STYLUS, data[4] & 0x10);
- /* Only allow the stylus2 button to be reported for the pen tool. */
- input_report_key(input, BTN_STYLUS2, (wacom->tool[0] == BTN_TOOL_PEN) && (data[4] & 0x20));
+ input_report_key(input, BTN_TOUCH, data[4] & 0x08);
+ input_report_key(input, BTN_STYLUS, data[4] & 0x10);
+ /* Only allow the stylus2 button to be reported for the pen tool. */
+ input_report_key(input, BTN_STYLUS2, (wacom->tool[0] == BTN_TOOL_PEN) && (data[4] & 0x20));
+ }
if (!prox)
wacom->id[0] = 0;
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index 96a85cd..1bc1d479 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -389,6 +389,7 @@
{
struct vmbus_channel *channel, *tmp;
+ mutex_lock(&vmbus_connection.channel_mutex);
list_for_each_entry_safe(channel, tmp, &vmbus_connection.chn_list,
listentry) {
/* hv_process_channel_removal() needs this */
@@ -396,6 +397,7 @@
vmbus_device_unregister(channel->device_obj);
}
+ mutex_unlock(&vmbus_connection.channel_mutex);
}
/*
diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c
index 12e851a..46b4e35 100644
--- a/drivers/hwmon/amc6821.c
+++ b/drivers/hwmon/amc6821.c
@@ -188,8 +188,8 @@
!data->valid) {
for (i = 0; i < TEMP_IDX_LEN; i++)
- data->temp[i] = i2c_smbus_read_byte_data(client,
- temp_reg[i]);
+ data->temp[i] = (int8_t)i2c_smbus_read_byte_data(
+ client, temp_reg[i]);
data->stat1 = i2c_smbus_read_byte_data(client,
AMC6821_REG_STAT1);
diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c
index edf550f..0043a4c 100644
--- a/drivers/hwmon/ds620.c
+++ b/drivers/hwmon/ds620.c
@@ -166,7 +166,7 @@
if (res)
return res;
- val = (val * 10 / 625) * 8;
+ val = (clamp_val(val, -128000, 128000) * 10 / 625) * 8;
mutex_lock(&data->update_lock);
data->temp[attr->index] = val;
diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c
index b96a2a9..628be9c 100644
--- a/drivers/hwmon/g762.c
+++ b/drivers/hwmon/g762.c
@@ -193,14 +193,17 @@
* Convert fan RPM value from sysfs into count value for fan controller
* register (FAN_SET_CNT).
*/
-static inline unsigned char cnt_from_rpm(u32 rpm, u32 clk_freq, u16 p,
+static inline unsigned char cnt_from_rpm(unsigned long rpm, u32 clk_freq, u16 p,
u8 clk_div, u8 gear_mult)
{
- if (!rpm) /* to stop the fan, set cnt to 255 */
+ unsigned long f1 = clk_freq * 30 * gear_mult;
+ unsigned long f2 = p * clk_div;
+
+ if (!rpm) /* to stop the fan, set cnt to 255 */
return 0xff;
- return clamp_val(((clk_freq * 30 * gear_mult) / (rpm * p * clk_div)),
- 0, 255);
+ rpm = clamp_val(rpm, f1 / (255 * f2), ULONG_MAX / f2);
+ return DIV_ROUND_CLOSEST(f1, rpm * f2);
}
/* helper to grab and cache data, at most one time per second */
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 322ed92..841f242 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -1036,7 +1036,7 @@
};
static const u8 lm90_min_alarm_bits[3] = { 5, 3, 11 };
-static const u8 lm90_max_alarm_bits[3] = { 0, 4, 12 };
+static const u8 lm90_max_alarm_bits[3] = { 6, 4, 12 };
static const u8 lm90_crit_alarm_bits[3] = { 0, 1, 9 };
static const u8 lm90_emergency_alarm_bits[3] = { 15, 13, 14 };
static const u8 lm90_fault_bits[3] = { 0, 2, 10 };
diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c
index 3ce33d2..12b94b0 100644
--- a/drivers/hwmon/nct7802.c
+++ b/drivers/hwmon/nct7802.c
@@ -259,13 +259,15 @@
ret = 0;
else if (ret)
ret = DIV_ROUND_CLOSEST(1350000U, ret);
+ else
+ ret = 1350000U;
abort:
mutex_unlock(&data->access_lock);
return ret;
}
static int nct7802_write_fan_min(struct nct7802_data *data, u8 reg_fan_low,
- u8 reg_fan_high, unsigned int limit)
+ u8 reg_fan_high, unsigned long limit)
{
int err;
@@ -326,8 +328,8 @@
int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr];
int err;
+ voltage = clamp_val(voltage, 0, 0x3ff * nct7802_vmul[nr]);
voltage = DIV_ROUND_CLOSEST(voltage, nct7802_vmul[nr]);
- voltage = clamp_val(voltage, 0, 0x3ff);
mutex_lock(&data->access_lock);
err = regmap_write(data->regmap,
@@ -402,7 +404,7 @@
if (err < 0)
return err;
- val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127);
+ val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000);
err = regmap_write(data->regmap, nr, val & 0xff);
return err ? : count;
diff --git a/drivers/hwmon/scpi-hwmon.c b/drivers/hwmon/scpi-hwmon.c
index 559a3dc..094f948 100644
--- a/drivers/hwmon/scpi-hwmon.c
+++ b/drivers/hwmon/scpi-hwmon.c
@@ -251,6 +251,7 @@
{.compatible = "arm,scpi-sensors"},
{},
};
+MODULE_DEVICE_TABLE(of, scpi_of_match);
static struct platform_driver scpi_hwmon_platdrv = {
.driver = {
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 30cb6f0..2d2abe3 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -465,24 +465,18 @@
rwp = readl_relaxed(drvdata->base + TMC_RWP);
val = readl_relaxed(drvdata->base + TMC_STS);
- /*
- * Adjust the buffer to point to the beginning of the trace data
- * and update the available trace data.
- */
- if (val & TMC_STS_FULL) {
- drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr;
- drvdata->len = drvdata->size;
- } else {
- drvdata->buf = drvdata->vaddr;
- drvdata->len = rwp - drvdata->paddr;
- }
-
if (drvdata->memtype == TMC_ETR_MEM_TYPE_CONTIG) {
- /* How much memory do we still have */
- if (val & BIT(0))
+ /*
+ * Adjust the buffer to point to the beginning of the trace data
+ * and update the available trace data.
+ */
+ if (val & TMC_STS_FULL) {
drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr;
- else
+ drvdata->len = drvdata->size;
+ } else {
drvdata->buf = drvdata->vaddr;
+ drvdata->len = rwp - drvdata->paddr;
+ }
} else {
/*
* Reset these variables before computing since we
@@ -490,8 +484,9 @@
*/
drvdata->sg_blk_num = 0;
drvdata->delta_bottom = 0;
+ drvdata->len = drvdata->size;
- if (val & BIT(0))
+ if (val & TMC_STS_FULL)
tmc_etr_sg_rwp_pos(drvdata, rwp);
else
drvdata->buf = drvdata->vaddr;
@@ -563,58 +558,39 @@
static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev, u32 mode)
{
int ret = 0;
- bool used = false;
long val;
unsigned long flags;
- void __iomem *vaddr = NULL;
- dma_addr_t paddr;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
/* This shouldn't be happening */
if (WARN_ON(mode != CS_MODE_SYSFS))
return -EINVAL;
+ mutex_lock(&drvdata->mem_lock);
+
/*
- * If we don't have a buffer release the lock and allocate memory.
- * Otherwise keep the lock and move along.
+ * ETR DDR memory is not allocated until user enables
+ * tmc at least once. If user specifies different ETR
+ * DDR size than the default size or switches between
+ * contiguous or scatter-gather memory type after
+ * enabling tmc; the new selection will be honored from
+ * next tmc enable session.
*/
- spin_lock_irqsave(&drvdata->spinlock, flags);
- if (!drvdata->vaddr) {
- spin_unlock_irqrestore(&drvdata->spinlock, flags);
-
- /*
- * Contiguous memory can't be allocated while a spinlock is
- * held. As such allocate memory here and free it if a buffer
- * has already been allocated (from a previous session).
- */
- mutex_lock(&drvdata->mem_lock);
-
- /*
- * ETR DDR memory is not allocated until user enables
- * tmc at least once. If user specifies different ETR
- * DDR size than the default size or switches between
- * contiguous or scatter-gather memory type after
- * enabling tmc; the new selection will be honored from
- * next tmc enable session.
- */
- if (drvdata->size != drvdata->mem_size ||
- drvdata->memtype != drvdata->mem_type) {
- tmc_etr_free_mem(drvdata);
- drvdata->size = drvdata->mem_size;
- drvdata->memtype = drvdata->mem_type;
- }
- ret = tmc_etr_alloc_mem(drvdata);
- if (ret) {
- pm_runtime_put(drvdata->dev);
- mutex_unlock(&drvdata->mem_lock);
- return ret;
- }
- mutex_unlock(&drvdata->mem_lock);
-
- /* Let's try again */
- spin_lock_irqsave(&drvdata->spinlock, flags);
+ if (drvdata->size != drvdata->mem_size ||
+ drvdata->memtype != drvdata->mem_type) {
+ tmc_etr_free_mem(drvdata);
+ drvdata->size = drvdata->mem_size;
+ drvdata->memtype = drvdata->mem_type;
}
+ ret = tmc_etr_alloc_mem(drvdata);
+ if (ret) {
+ pm_runtime_put(drvdata->dev);
+ mutex_unlock(&drvdata->mem_lock);
+ return ret;
+ }
+ mutex_unlock(&drvdata->mem_lock);
+ spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) {
ret = -EBUSY;
goto out;
@@ -629,26 +605,10 @@
if (val == CS_MODE_SYSFS)
goto out;
- /*
- * If drvdata::buf == NULL, use the memory allocated above.
- * Otherwise a buffer still exists from a previous session, so
- * simply use that.
- */
- if (drvdata->buf == NULL) {
- used = true;
- drvdata->vaddr = vaddr;
- drvdata->paddr = paddr;
- drvdata->buf = drvdata->vaddr;
- }
-
tmc_etr_enable_hw(drvdata);
out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
- /* Free memory outside the spinlock if need be */
- if (!used && vaddr)
- dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
-
if (!ret)
dev_info(drvdata->dev, "TMC-ETR enabled\n");
diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c
index c4a7f28..5269890 100644
--- a/drivers/hwtracing/stm/core.c
+++ b/drivers/hwtracing/stm/core.c
@@ -361,7 +361,7 @@
struct stm_file *stmf;
struct device *dev;
unsigned int major = imajor(inode);
- int err = -ENODEV;
+ int err = -ENOMEM;
dev = class_find_device(&stm_class, NULL, &major, major_match);
if (!dev)
@@ -369,8 +369,9 @@
stmf = kzalloc(sizeof(*stmf), GFP_KERNEL);
if (!stmf)
- return -ENOMEM;
+ goto err_put_device;
+ err = -ENODEV;
stm_output_init(&stmf->output);
stmf->stm = to_stm_device(dev);
@@ -382,9 +383,10 @@
return nonseekable_open(inode, file);
err_free:
+ kfree(stmf);
+err_put_device:
/* matches class_find_device() above */
put_device(dev);
- kfree(stmf);
return err;
}
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index c2268cd..e34d82e 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -585,10 +585,29 @@
u8 command, int size, union i2c_smbus_data *data)
{
struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap);
+ unsigned short piix4_smba = adapdata->smba;
+ int retries = MAX_TIMEOUT;
+ int smbslvcnt;
u8 smba_en_lo;
u8 port;
int retval;
+ /* Request the SMBUS semaphore, avoid conflicts with the IMC */
+ smbslvcnt = inb_p(SMBSLVCNT);
+ do {
+ outb_p(smbslvcnt | 0x10, SMBSLVCNT);
+
+ /* Check the semaphore status */
+ smbslvcnt = inb_p(SMBSLVCNT);
+ if (smbslvcnt & 0x10)
+ break;
+
+ usleep_range(1000, 2000);
+ } while (--retries);
+ /* SMBus is still owned by the IMC, we give up */
+ if (!retries)
+ return -EBUSY;
+
mutex_lock(&piix4_mutex_sb800);
outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX);
@@ -606,6 +625,9 @@
mutex_unlock(&piix4_mutex_sb800);
+ /* Release the semaphore */
+ outb_p(smbslvcnt | 0x20, SMBSLVCNT);
+
return retval;
}
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index b432b64..7484aac 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -1657,7 +1657,7 @@
if (i2c_check_addr_validity(addr, info.flags)) {
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
- info.addr, node->full_name);
+ addr, node->full_name);
return ERR_PTR(-EINVAL);
}
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index 66f323f..6f638bb 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -331,7 +331,7 @@
unsigned long arg)
{
struct i2c_smbus_ioctl_data data_arg;
- union i2c_smbus_data temp;
+ union i2c_smbus_data temp = {};
int datasize, res;
if (copy_from_user(&data_arg,
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index 8bc3d36d..9c4ac26 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -151,6 +151,9 @@
buf[0] = val;
msg.buf = buf;
ret = __i2c_transfer(adap, &msg, 1);
+
+ if (ret >= 0 && ret != 1)
+ ret = -EREMOTEIO;
} else {
union i2c_smbus_data data;
ret = adap->algo->smbus_xfer(adap, client->addr,
@@ -179,7 +182,7 @@
/* Only select the channel if its different from the last channel */
if (data->last_chan != regval) {
ret = pca954x_reg_write(muxc->parent, client, regval);
- data->last_chan = ret ? 0 : regval;
+ data->last_chan = ret < 0 ? 0 : regval;
}
return ret;
diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c
index ce69048..3a557e3 100644
--- a/drivers/iio/accel/st_accel_core.c
+++ b/drivers/iio/accel/st_accel_core.c
@@ -154,8 +154,8 @@
#define ST_ACCEL_4_FS_MASK 0x80
#define ST_ACCEL_4_FS_AVL_2_VAL 0X00
#define ST_ACCEL_4_FS_AVL_6_VAL 0X01
-#define ST_ACCEL_4_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1024)
-#define ST_ACCEL_4_FS_AVL_6_GAIN IIO_G_TO_M_S_2(340)
+#define ST_ACCEL_4_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1000)
+#define ST_ACCEL_4_FS_AVL_6_GAIN IIO_G_TO_M_S_2(3000)
#define ST_ACCEL_4_BDU_ADDR 0x21
#define ST_ACCEL_4_BDU_MASK 0x40
#define ST_ACCEL_4_DRDY_IRQ_ADDR 0x21
@@ -346,6 +346,14 @@
.addr = ST_ACCEL_1_BDU_ADDR,
.mask = ST_ACCEL_1_BDU_MASK,
},
+ /*
+ * Data Alignment Setting - needs to be set to get
+ * left-justified data like all other sensors.
+ */
+ .das = {
+ .addr = 0x21,
+ .mask = 0x01,
+ },
.drdy_irq = {
.addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_1_DRDY_IRQ_INT1_MASK,
diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c
index 2bbf0c5..7d61b56 100644
--- a/drivers/iio/adc/palmas_gpadc.c
+++ b/drivers/iio/adc/palmas_gpadc.c
@@ -775,7 +775,7 @@
static int palmas_gpadc_suspend(struct device *dev)
{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct palmas_gpadc *adc = iio_priv(indio_dev);
int wakeup = adc->wakeup1_enable || adc->wakeup2_enable;
int ret;
@@ -798,7 +798,7 @@
static int palmas_gpadc_resume(struct device *dev)
{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct palmas_gpadc *adc = iio_priv(indio_dev);
int wakeup = adc->wakeup1_enable || adc->wakeup2_enable;
int ret;
diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c
index fe7775b..df40452 100644
--- a/drivers/iio/common/st_sensors/st_sensors_buffer.c
+++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c
@@ -30,7 +30,9 @@
for_each_set_bit(i, indio_dev->active_scan_mask, num_data_channels) {
const struct iio_chan_spec *channel = &indio_dev->channels[i];
- unsigned int bytes_to_read = channel->scan_type.realbits >> 3;
+ unsigned int bytes_to_read =
+ DIV_ROUND_UP(channel->scan_type.realbits +
+ channel->scan_type.shift, 8);
unsigned int storage_bytes =
channel->scan_type.storagebits >> 3;
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
index 975a1f1..79c8c7c 100644
--- a/drivers/iio/common/st_sensors/st_sensors_core.c
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -401,6 +401,15 @@
return err;
}
+ /* set DAS */
+ if (sdata->sensor_settings->das.addr) {
+ err = st_sensors_write_data_with_mask(indio_dev,
+ sdata->sensor_settings->das.addr,
+ sdata->sensor_settings->das.mask, 1);
+ if (err < 0)
+ return err;
+ }
+
if (sdata->int_pin_open_drain) {
dev_info(&indio_dev->dev,
"set interrupt line to open drain mode\n");
@@ -483,8 +492,10 @@
int err;
u8 *outdata;
struct st_sensor_data *sdata = iio_priv(indio_dev);
- unsigned int byte_for_channel = ch->scan_type.realbits >> 3;
+ unsigned int byte_for_channel;
+ byte_for_channel = DIV_ROUND_UP(ch->scan_type.realbits +
+ ch->scan_type.shift, 8);
outdata = kmalloc(byte_for_channel, GFP_KERNEL);
if (!outdata)
return -ENOMEM;
diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c
index 9a08146..6bb23a4 100644
--- a/drivers/iio/health/afe4403.c
+++ b/drivers/iio/health/afe4403.c
@@ -422,7 +422,7 @@
static int __maybe_unused afe4403_suspend(struct device *dev)
{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev *indio_dev = spi_get_drvdata(to_spi_device(dev));
struct afe4403_data *afe = iio_priv(indio_dev);
int ret;
@@ -443,7 +443,7 @@
static int __maybe_unused afe4403_resume(struct device *dev)
{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev *indio_dev = spi_get_drvdata(to_spi_device(dev));
struct afe4403_data *afe = iio_priv(indio_dev);
int ret;
diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c
index 4526640..964f523 100644
--- a/drivers/iio/health/afe4404.c
+++ b/drivers/iio/health/afe4404.c
@@ -428,7 +428,7 @@
static int __maybe_unused afe4404_suspend(struct device *dev)
{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct afe4404_data *afe = iio_priv(indio_dev);
int ret;
@@ -449,7 +449,7 @@
static int __maybe_unused afe4404_resume(struct device *dev)
{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct afe4404_data *afe = iio_priv(indio_dev);
int ret;
diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c
index 90ab8a2d..183c143 100644
--- a/drivers/iio/health/max30100.c
+++ b/drivers/iio/health/max30100.c
@@ -238,7 +238,7 @@
mutex_lock(&data->lock);
- while (cnt || (cnt = max30100_fifo_count(data) > 0)) {
+ while (cnt || (cnt = max30100_fifo_count(data)) > 0) {
ret = max30100_read_measurement(data);
if (ret)
break;
diff --git a/drivers/iio/humidity/dht11.c b/drivers/iio/humidity/dht11.c
index 9c47bc9..2a22ad9 100644
--- a/drivers/iio/humidity/dht11.c
+++ b/drivers/iio/humidity/dht11.c
@@ -71,7 +71,8 @@
* a) select an implementation using busy loop polling on those systems
* b) use the checksum to do some probabilistic decoding
*/
-#define DHT11_START_TRANSMISSION 18 /* ms */
+#define DHT11_START_TRANSMISSION_MIN 18000 /* us */
+#define DHT11_START_TRANSMISSION_MAX 20000 /* us */
#define DHT11_MIN_TIMERES 34000 /* ns */
#define DHT11_THRESHOLD 49000 /* ns */
#define DHT11_AMBIG_LOW 23000 /* ns */
@@ -228,7 +229,8 @@
ret = gpio_direction_output(dht11->gpio, 0);
if (ret)
goto err;
- msleep(DHT11_START_TRANSMISSION);
+ usleep_range(DHT11_START_TRANSMISSION_MIN,
+ DHT11_START_TRANSMISSION_MAX);
ret = gpio_direction_input(dht11->gpio);
if (ret)
goto err;
diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
index e0251b8..5fb571d 100644
--- a/drivers/iio/imu/bmi160/bmi160_core.c
+++ b/drivers/iio/imu/bmi160/bmi160_core.c
@@ -66,10 +66,8 @@
#define BMI160_REG_DUMMY 0x7F
-#define BMI160_ACCEL_PMU_MIN_USLEEP 3200
-#define BMI160_ACCEL_PMU_MAX_USLEEP 3800
-#define BMI160_GYRO_PMU_MIN_USLEEP 55000
-#define BMI160_GYRO_PMU_MAX_USLEEP 80000
+#define BMI160_ACCEL_PMU_MIN_USLEEP 3800
+#define BMI160_GYRO_PMU_MIN_USLEEP 80000
#define BMI160_SOFTRESET_USLEEP 1000
#define BMI160_CHANNEL(_type, _axis, _index) { \
@@ -151,20 +149,9 @@
},
};
-struct bmi160_pmu_time {
- unsigned long min;
- unsigned long max;
-};
-
-static struct bmi160_pmu_time bmi160_pmu_time[] = {
- [BMI160_ACCEL] = {
- .min = BMI160_ACCEL_PMU_MIN_USLEEP,
- .max = BMI160_ACCEL_PMU_MAX_USLEEP
- },
- [BMI160_GYRO] = {
- .min = BMI160_GYRO_PMU_MIN_USLEEP,
- .max = BMI160_GYRO_PMU_MIN_USLEEP,
- },
+static unsigned long bmi160_pmu_time[] = {
+ [BMI160_ACCEL] = BMI160_ACCEL_PMU_MIN_USLEEP,
+ [BMI160_GYRO] = BMI160_GYRO_PMU_MIN_USLEEP,
};
struct bmi160_scale {
@@ -289,7 +276,7 @@
if (ret < 0)
return ret;
- usleep_range(bmi160_pmu_time[t].min, bmi160_pmu_time[t].max);
+ usleep_range(bmi160_pmu_time[t], bmi160_pmu_time[t] + 1000);
return 0;
}
diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c
index 6511b20..a8ffa43 100644
--- a/drivers/iio/light/max44000.c
+++ b/drivers/iio/light/max44000.c
@@ -113,7 +113,7 @@
"0.100 "
"0.025 "
"0.00625 "
- "0.001625";
+ "0.0015625";
/* Available scales (internal to ulux) with pretty manual alignment: */
static const int max44000_scale_avail_ulux_array[] = {
diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c
index 1a2984c..ae04826 100644
--- a/drivers/infiniband/core/cache.c
+++ b/drivers/infiniband/core/cache.c
@@ -770,12 +770,8 @@
int err = 0;
table = kcalloc(ib_dev->phys_port_cnt, sizeof(*table), GFP_KERNEL);
-
- if (!table) {
- pr_warn("failed to allocate ib gid cache for %s\n",
- ib_dev->name);
+ if (!table)
return -ENOMEM;
- }
for (port = 0; port < ib_dev->phys_port_cnt; port++) {
u8 rdma_port = port + rdma_start_port(ib_dev);
@@ -1170,14 +1166,13 @@
GFP_KERNEL);
if (!device->cache.pkey_cache ||
!device->cache.lmc_cache) {
- pr_warn("Couldn't allocate cache for %s\n", device->name);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto free;
}
err = gid_table_setup_one(device);
if (err)
- /* Allocated memory will be cleaned in the release function */
- return err;
+ goto free;
for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p)
ib_cache_update(device, p + rdma_start_port(device));
@@ -1192,6 +1187,9 @@
err:
gid_table_cleanup_one(device);
+free:
+ kfree(device->cache.pkey_cache);
+ kfree(device->cache.lmc_cache);
return err;
}
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 2a6fc47..c25768c 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -2768,7 +2768,8 @@
if (!src_addr || !src_addr->sa_family) {
src_addr = (struct sockaddr *) &id->route.addr.src_addr;
src_addr->sa_family = dst_addr->sa_family;
- if (dst_addr->sa_family == AF_INET6) {
+ if (IS_ENABLED(CONFIG_IPV6) &&
+ dst_addr->sa_family == AF_INET6) {
struct sockaddr_in6 *src_addr6 = (struct sockaddr_in6 *) src_addr;
struct sockaddr_in6 *dst_addr6 = (struct sockaddr_in6 *) dst_addr;
src_addr6->sin6_scope_id = dst_addr6->sin6_scope_id;
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index 40cbd6b..2395fe2 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -1746,7 +1746,7 @@
if (!class)
goto out;
if (convert_mgmt_class(mad_hdr->mgmt_class) >=
- IB_MGMT_MAX_METHODS)
+ ARRAY_SIZE(class->method_table))
goto out;
method = class->method_table[convert_mgmt_class(
mad_hdr->mgmt_class)];
diff --git a/drivers/infiniband/core/multicast.c b/drivers/infiniband/core/multicast.c
index e51b739..322cb67 100644
--- a/drivers/infiniband/core/multicast.c
+++ b/drivers/infiniband/core/multicast.c
@@ -518,8 +518,11 @@
process_join_error(group, status);
else {
int mgids_changed, is_mgid0;
- ib_find_pkey(group->port->dev->device, group->port->port_num,
- be16_to_cpu(rec->pkey), &pkey_index);
+
+ if (ib_find_pkey(group->port->dev->device,
+ group->port->port_num, be16_to_cpu(rec->pkey),
+ &pkey_index))
+ pkey_index = MCAST_INVALID_PKEY_INDEX;
spin_lock_irq(&group->port->lock);
if (group->state == MCAST_BUSY &&
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index 84b4eff..c22fde6 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -134,6 +134,7 @@
IB_ACCESS_REMOTE_ATOMIC | IB_ACCESS_MW_BIND));
if (access & IB_ACCESS_ON_DEMAND) {
+ put_pid(umem->pid);
ret = ib_umem_odp_get(context, umem);
if (ret) {
kfree(umem);
@@ -149,6 +150,7 @@
page_list = (struct page **) __get_free_page(GFP_KERNEL);
if (!page_list) {
+ put_pid(umem->pid);
kfree(umem);
return ERR_PTR(-ENOMEM);
}
diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c
index 93e3d27..b85a1a9 100644
--- a/drivers/infiniband/hw/cxgb4/device.c
+++ b/drivers/infiniband/hw/cxgb4/device.c
@@ -828,8 +828,10 @@
}
rdev->status_page = (struct t4_dev_status_page *)
__get_free_page(GFP_KERNEL);
- if (!rdev->status_page)
+ if (!rdev->status_page) {
+ err = -ENOMEM;
goto destroy_ocqp_pool;
+ }
rdev->status_page->qp_start = rdev->lldi.vr->qp.start;
rdev->status_page->qp_size = rdev->lldi.vr->qp.size;
rdev->status_page->cq_start = rdev->lldi.vr->cq.start;
@@ -846,9 +848,17 @@
}
}
+ rdev->free_workq = create_singlethread_workqueue("iw_cxgb4_free");
+ if (!rdev->free_workq) {
+ err = -ENOMEM;
+ goto err_free_status_page;
+ }
+
rdev->status_page->db_off = 0;
return 0;
+err_free_status_page:
+ free_page((unsigned long)rdev->status_page);
destroy_ocqp_pool:
c4iw_ocqp_pool_destroy(rdev);
destroy_rqtpool:
@@ -862,6 +872,7 @@
static void c4iw_rdev_close(struct c4iw_rdev *rdev)
{
+ destroy_workqueue(rdev->free_workq);
kfree(rdev->wr_log);
free_page((unsigned long)rdev->status_page);
c4iw_pblpool_destroy(rdev);
diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
index 4788e1a..7d54066 100644
--- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
+++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
@@ -45,6 +45,7 @@
#include <linux/kref.h>
#include <linux/timer.h>
#include <linux/io.h>
+#include <linux/workqueue.h>
#include <asm/byteorder.h>
@@ -107,6 +108,7 @@
struct list_head qpids;
struct list_head cqids;
struct mutex lock;
+ struct kref kref;
};
enum c4iw_rdev_flags {
@@ -183,6 +185,7 @@
atomic_t wr_log_idx;
struct wr_log_entry *wr_log;
int wr_log_size;
+ struct workqueue_struct *free_workq;
};
static inline int c4iw_fatal_error(struct c4iw_rdev *rdev)
@@ -482,6 +485,8 @@
int sq_sig_all;
struct completion rq_drained;
struct completion sq_drained;
+ struct work_struct free_work;
+ struct c4iw_ucontext *ucontext;
};
static inline struct c4iw_qp *to_c4iw_qp(struct ib_qp *ibqp)
@@ -495,6 +500,7 @@
u32 key;
spinlock_t mmap_lock;
struct list_head mmaps;
+ struct kref kref;
};
static inline struct c4iw_ucontext *to_c4iw_ucontext(struct ib_ucontext *c)
@@ -502,6 +508,18 @@
return container_of(c, struct c4iw_ucontext, ibucontext);
}
+void _c4iw_free_ucontext(struct kref *kref);
+
+static inline void c4iw_put_ucontext(struct c4iw_ucontext *ucontext)
+{
+ kref_put(&ucontext->kref, _c4iw_free_ucontext);
+}
+
+static inline void c4iw_get_ucontext(struct c4iw_ucontext *ucontext)
+{
+ kref_get(&ucontext->kref);
+}
+
struct c4iw_mm_entry {
struct list_head entry;
u64 addr;
diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c
index 645e606..8278ba0 100644
--- a/drivers/infiniband/hw/cxgb4/provider.c
+++ b/drivers/infiniband/hw/cxgb4/provider.c
@@ -91,17 +91,28 @@
return -ENOSYS;
}
-static int c4iw_dealloc_ucontext(struct ib_ucontext *context)
+void _c4iw_free_ucontext(struct kref *kref)
{
- struct c4iw_dev *rhp = to_c4iw_dev(context->device);
- struct c4iw_ucontext *ucontext = to_c4iw_ucontext(context);
+ struct c4iw_ucontext *ucontext;
+ struct c4iw_dev *rhp;
struct c4iw_mm_entry *mm, *tmp;
- PDBG("%s context %p\n", __func__, context);
+ ucontext = container_of(kref, struct c4iw_ucontext, kref);
+ rhp = to_c4iw_dev(ucontext->ibucontext.device);
+
+ PDBG("%s ucontext %p\n", __func__, ucontext);
list_for_each_entry_safe(mm, tmp, &ucontext->mmaps, entry)
kfree(mm);
c4iw_release_dev_ucontext(&rhp->rdev, &ucontext->uctx);
kfree(ucontext);
+}
+
+static int c4iw_dealloc_ucontext(struct ib_ucontext *context)
+{
+ struct c4iw_ucontext *ucontext = to_c4iw_ucontext(context);
+
+ PDBG("%s context %p\n", __func__, context);
+ c4iw_put_ucontext(ucontext);
return 0;
}
@@ -125,6 +136,7 @@
c4iw_init_dev_ucontext(&rhp->rdev, &context->uctx);
INIT_LIST_HEAD(&context->mmaps);
spin_lock_init(&context->mmap_lock);
+ kref_init(&context->kref);
if (udata->outlen < sizeof(uresp) - sizeof(uresp.reserved)) {
if (!warned++)
diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c
index b7ac97b..cc2243f 100644
--- a/drivers/infiniband/hw/cxgb4/qp.c
+++ b/drivers/infiniband/hw/cxgb4/qp.c
@@ -321,7 +321,8 @@
FW_RI_RES_WR_DCAEN_V(0) |
FW_RI_RES_WR_DCACPU_V(0) |
FW_RI_RES_WR_FBMIN_V(2) |
- FW_RI_RES_WR_FBMAX_V(2) |
+ (t4_sq_onchip(&wq->sq) ? FW_RI_RES_WR_FBMAX_V(2) :
+ FW_RI_RES_WR_FBMAX_V(3)) |
FW_RI_RES_WR_CIDXFTHRESHO_V(0) |
FW_RI_RES_WR_CIDXFTHRESH_V(0) |
FW_RI_RES_WR_EQSIZE_V(eqsize));
@@ -345,7 +346,7 @@
FW_RI_RES_WR_DCAEN_V(0) |
FW_RI_RES_WR_DCACPU_V(0) |
FW_RI_RES_WR_FBMIN_V(2) |
- FW_RI_RES_WR_FBMAX_V(2) |
+ FW_RI_RES_WR_FBMAX_V(3) |
FW_RI_RES_WR_CIDXFTHRESHO_V(0) |
FW_RI_RES_WR_CIDXFTHRESH_V(0) |
FW_RI_RES_WR_EQSIZE_V(eqsize));
@@ -714,13 +715,32 @@
return 0;
}
-static void _free_qp(struct kref *kref)
+static void free_qp_work(struct work_struct *work)
+{
+ struct c4iw_ucontext *ucontext;
+ struct c4iw_qp *qhp;
+ struct c4iw_dev *rhp;
+
+ qhp = container_of(work, struct c4iw_qp, free_work);
+ ucontext = qhp->ucontext;
+ rhp = qhp->rhp;
+
+ PDBG("%s qhp %p ucontext %p\n", __func__, qhp, ucontext);
+ destroy_qp(&rhp->rdev, &qhp->wq,
+ ucontext ? &ucontext->uctx : &rhp->rdev.uctx);
+
+ if (ucontext)
+ c4iw_put_ucontext(ucontext);
+ kfree(qhp);
+}
+
+static void queue_qp_free(struct kref *kref)
{
struct c4iw_qp *qhp;
qhp = container_of(kref, struct c4iw_qp, kref);
PDBG("%s qhp %p\n", __func__, qhp);
- kfree(qhp);
+ queue_work(qhp->rhp->rdev.free_workq, &qhp->free_work);
}
void c4iw_qp_add_ref(struct ib_qp *qp)
@@ -732,7 +752,7 @@
void c4iw_qp_rem_ref(struct ib_qp *qp)
{
PDBG("%s ib_qp %p\n", __func__, qp);
- kref_put(&to_c4iw_qp(qp)->kref, _free_qp);
+ kref_put(&to_c4iw_qp(qp)->kref, queue_qp_free);
}
static void add_to_fc_list(struct list_head *head, struct list_head *entry)
@@ -1642,7 +1662,6 @@
struct c4iw_dev *rhp;
struct c4iw_qp *qhp;
struct c4iw_qp_attributes attrs;
- struct c4iw_ucontext *ucontext;
qhp = to_c4iw_qp(ib_qp);
rhp = qhp->rhp;
@@ -1662,11 +1681,6 @@
spin_unlock_irq(&rhp->lock);
free_ird(rhp, qhp->attr.max_ird);
- ucontext = ib_qp->uobject ?
- to_c4iw_ucontext(ib_qp->uobject->context) : NULL;
- destroy_qp(&rhp->rdev, &qhp->wq,
- ucontext ? &ucontext->uctx : &rhp->rdev.uctx);
-
c4iw_qp_rem_ref(ib_qp);
PDBG("%s ib_qp %p qpid 0x%0x\n", __func__, ib_qp, qhp->wq.sq.qid);
@@ -1767,6 +1781,7 @@
mutex_init(&qhp->mutex);
init_waitqueue_head(&qhp->wait);
kref_init(&qhp->kref);
+ INIT_WORK(&qhp->free_work, free_qp_work);
ret = insert_handle(rhp, &rhp->qpidr, qhp, qhp->wq.sq.qid);
if (ret)
@@ -1853,6 +1868,9 @@
ma_sync_key_mm->len = PAGE_SIZE;
insert_mmap(ucontext, ma_sync_key_mm);
}
+
+ c4iw_get_ucontext(ucontext);
+ qhp->ucontext = ucontext;
}
qhp->ibqp.qp_num = qhp->wq.sq.qid;
init_timer(&(qhp->timer));
diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.c b/drivers/infiniband/hw/i40iw/i40iw_verbs.c
index 6329c97..4b892ca 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c
@@ -2501,7 +2501,7 @@
return -ENOSYS;
}
- memcpy(&stats->value[0], &hw_stats, sizeof(*hw_stats));
+ memcpy(&stats->value[0], hw_stats, sizeof(*hw_stats));
return stats->num_counters;
}
diff --git a/drivers/infiniband/hw/mlx4/ah.c b/drivers/infiniband/hw/mlx4/ah.c
index b9bf075..8dfc76f 100644
--- a/drivers/infiniband/hw/mlx4/ah.c
+++ b/drivers/infiniband/hw/mlx4/ah.c
@@ -114,7 +114,9 @@
!(1 << ah->av.eth.stat_rate & dev->caps.stat_rate_support))
--ah->av.eth.stat_rate;
}
-
+ ah->av.eth.sl_tclass_flowlabel |=
+ cpu_to_be32((ah_attr->grh.traffic_class << 20) |
+ ah_attr->grh.flow_label);
/*
* HW requires multicast LID so we just choose one.
*/
@@ -122,7 +124,7 @@
ah->av.ib.dlid = cpu_to_be16(0xc000);
memcpy(ah->av.eth.dgid, ah_attr->grh.dgid.raw, 16);
- ah->av.eth.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 29);
+ ah->av.eth.sl_tclass_flowlabel |= cpu_to_be32(ah_attr->sl << 29);
return &ah->ibah;
}
diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c
index 1672907..18d309e 100644
--- a/drivers/infiniband/hw/mlx4/mad.c
+++ b/drivers/infiniband/hw/mlx4/mad.c
@@ -702,10 +702,18 @@
/* If a grh is present, we demux according to it */
if (wc->wc_flags & IB_WC_GRH) {
- slave = mlx4_ib_find_real_gid(ibdev, port, grh->dgid.global.interface_id);
- if (slave < 0) {
- mlx4_ib_warn(ibdev, "failed matching grh\n");
- return -ENOENT;
+ if (grh->dgid.global.interface_id ==
+ cpu_to_be64(IB_SA_WELL_KNOWN_GUID) &&
+ grh->dgid.global.subnet_prefix == cpu_to_be64(
+ atomic64_read(&dev->sriov.demux[port - 1].subnet_prefix))) {
+ slave = 0;
+ } else {
+ slave = mlx4_ib_find_real_gid(ibdev, port,
+ grh->dgid.global.interface_id);
+ if (slave < 0) {
+ mlx4_ib_warn(ibdev, "failed matching grh\n");
+ return -ENOENT;
+ }
}
}
/* Class-specific handling */
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index b597e82..46ad995 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -697,9 +697,11 @@
if (err)
goto out;
- props->active_width = (((u8 *)mailbox->buf)[5] == 0x40) ?
- IB_WIDTH_4X : IB_WIDTH_1X;
- props->active_speed = IB_SPEED_QDR;
+ props->active_width = (((u8 *)mailbox->buf)[5] == 0x40) ||
+ (((u8 *)mailbox->buf)[5] == 0x20 /*56Gb*/) ?
+ IB_WIDTH_4X : IB_WIDTH_1X;
+ props->active_speed = (((u8 *)mailbox->buf)[5] == 0x20 /*56Gb*/) ?
+ IB_SPEED_FDR : IB_SPEED_QDR;
props->port_cap_flags = IB_PORT_CM_SUP | IB_PORT_IP_BASED_GIDS;
props->gid_tbl_len = mdev->dev->caps.gid_table_len[port];
props->max_msg_sz = mdev->dev->caps.max_msg_sz;
@@ -2820,14 +2822,19 @@
goto err_steer_qp_release;
}
- bitmap_zero(ibdev->ib_uc_qpns_bitmap, ibdev->steer_qpn_count);
-
- err = mlx4_FLOW_STEERING_IB_UC_QP_RANGE(
- dev, ibdev->steer_qpn_base,
- ibdev->steer_qpn_base +
- ibdev->steer_qpn_count - 1);
- if (err)
- goto err_steer_free_bitmap;
+ if (dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_DMFS_IPOIB) {
+ bitmap_zero(ibdev->ib_uc_qpns_bitmap,
+ ibdev->steer_qpn_count);
+ err = mlx4_FLOW_STEERING_IB_UC_QP_RANGE(
+ dev, ibdev->steer_qpn_base,
+ ibdev->steer_qpn_base +
+ ibdev->steer_qpn_count - 1);
+ if (err)
+ goto err_steer_free_bitmap;
+ } else {
+ bitmap_fill(ibdev->ib_uc_qpns_bitmap,
+ ibdev->steer_qpn_count);
+ }
}
for (j = 1; j <= ibdev->dev->caps.num_ports; j++)
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index 570bc86..c224543 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -1280,7 +1280,8 @@
if (is_qp0(dev, mqp))
mlx4_CLOSE_PORT(dev->dev, mqp->port);
- if (dev->qp1_proxy[mqp->port - 1] == mqp) {
+ if (mqp->mlx4_ib_qp_type == MLX4_IB_QPT_PROXY_GSI &&
+ dev->qp1_proxy[mqp->port - 1] == mqp) {
mutex_lock(&dev->qp1_proxy_lock[mqp->port - 1]);
dev->qp1_proxy[mqp->port - 1] = NULL;
mutex_unlock(&dev->qp1_proxy_lock[mqp->port - 1]);
@@ -1764,14 +1765,14 @@
u8 port_num = mlx4_is_bonded(to_mdev(ibqp->device)->dev) ? 1 :
attr_mask & IB_QP_PORT ? attr->port_num : qp->port;
union ib_gid gid;
- struct ib_gid_attr gid_attr;
+ struct ib_gid_attr gid_attr = {.gid_type = IB_GID_TYPE_IB};
u16 vlan = 0xffff;
u8 smac[ETH_ALEN];
int status = 0;
int is_eth = rdma_cap_eth_ah(&dev->ib_dev, port_num) &&
attr->ah_attr.ah_flags & IB_AH_GRH;
- if (is_eth) {
+ if (is_eth && attr->ah_attr.ah_flags & IB_AH_GRH) {
int index = attr->ah_attr.grh.sgid_index;
status = ib_get_cached_gid(ibqp->device, port_num,
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index 32b09f0..4cab29e 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -496,6 +496,7 @@
struct mlx5_ib_dev *dev = to_mdev(ibdev);
struct mlx5_core_dev *mdev = dev->mdev;
int err = -ENOMEM;
+ int max_sq_desc;
int max_rq_sg;
int max_sq_sg;
u64 min_page_size = 1ull << MLX5_CAP_GEN(mdev, log_pg_sz);
@@ -618,9 +619,10 @@
props->max_qp_wr = 1 << MLX5_CAP_GEN(mdev, log_max_qp_sz);
max_rq_sg = MLX5_CAP_GEN(mdev, max_wqe_sz_rq) /
sizeof(struct mlx5_wqe_data_seg);
- max_sq_sg = (MLX5_CAP_GEN(mdev, max_wqe_sz_sq) -
- sizeof(struct mlx5_wqe_ctrl_seg)) /
- sizeof(struct mlx5_wqe_data_seg);
+ max_sq_desc = min_t(int, MLX5_CAP_GEN(mdev, max_wqe_sz_sq), 512);
+ max_sq_sg = (max_sq_desc - sizeof(struct mlx5_wqe_ctrl_seg) -
+ sizeof(struct mlx5_wqe_raddr_seg)) /
+ sizeof(struct mlx5_wqe_data_seg);
props->max_sge = min(max_rq_sg, max_sq_sg);
props->max_sge_rd = MLX5_MAX_SGE_RD;
props->max_cq = 1 << MLX5_CAP_GEN(mdev, log_max_cq);
diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c
index 4e90124..be2d02b 100644
--- a/drivers/infiniband/hw/mlx5/mr.c
+++ b/drivers/infiniband/hw/mlx5/mr.c
@@ -628,7 +628,8 @@
ent->order = i + 2;
ent->dev = dev;
- if (dev->mdev->profile->mask & MLX5_PROF_MASK_MR_CACHE)
+ if ((dev->mdev->profile->mask & MLX5_PROF_MASK_MR_CACHE) &&
+ (mlx5_core_is_pf(dev->mdev)))
limit = dev->mdev->profile->mr_cache[i].limit;
else
limit = 0;
@@ -646,6 +647,33 @@
return 0;
}
+static void wait_for_async_commands(struct mlx5_ib_dev *dev)
+{
+ struct mlx5_mr_cache *cache = &dev->cache;
+ struct mlx5_cache_ent *ent;
+ int total = 0;
+ int i;
+ int j;
+
+ for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) {
+ ent = &cache->ent[i];
+ for (j = 0 ; j < 1000; j++) {
+ if (!ent->pending)
+ break;
+ msleep(50);
+ }
+ }
+ for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) {
+ ent = &cache->ent[i];
+ total += ent->pending;
+ }
+
+ if (total)
+ mlx5_ib_warn(dev, "aborted while there are %d pending mr requests\n", total);
+ else
+ mlx5_ib_warn(dev, "done with all pending requests\n");
+}
+
int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev)
{
int i;
@@ -659,6 +687,7 @@
clean_keys(dev, i);
destroy_workqueue(dev->cache.wq);
+ wait_for_async_commands(dev);
del_timer_sync(&dev->delay_timer);
return 0;
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
index d1e9218..aee3942 100644
--- a/drivers/infiniband/hw/mlx5/qp.c
+++ b/drivers/infiniband/hw/mlx5/qp.c
@@ -351,6 +351,29 @@
return ALIGN(max_t(int, inl_size, size), MLX5_SEND_WQE_BB);
}
+static int get_send_sge(struct ib_qp_init_attr *attr, int wqe_size)
+{
+ int max_sge;
+
+ if (attr->qp_type == IB_QPT_RC)
+ max_sge = (min_t(int, wqe_size, 512) -
+ sizeof(struct mlx5_wqe_ctrl_seg) -
+ sizeof(struct mlx5_wqe_raddr_seg)) /
+ sizeof(struct mlx5_wqe_data_seg);
+ else if (attr->qp_type == IB_QPT_XRC_INI)
+ max_sge = (min_t(int, wqe_size, 512) -
+ sizeof(struct mlx5_wqe_ctrl_seg) -
+ sizeof(struct mlx5_wqe_xrc_seg) -
+ sizeof(struct mlx5_wqe_raddr_seg)) /
+ sizeof(struct mlx5_wqe_data_seg);
+ else
+ max_sge = (wqe_size - sq_overhead(attr)) /
+ sizeof(struct mlx5_wqe_data_seg);
+
+ return min_t(int, max_sge, wqe_size - sq_overhead(attr) /
+ sizeof(struct mlx5_wqe_data_seg));
+}
+
static int calc_sq_size(struct mlx5_ib_dev *dev, struct ib_qp_init_attr *attr,
struct mlx5_ib_qp *qp)
{
@@ -387,7 +410,11 @@
return -ENOMEM;
}
qp->sq.wqe_shift = ilog2(MLX5_SEND_WQE_BB);
- qp->sq.max_gs = attr->cap.max_send_sge;
+ qp->sq.max_gs = get_send_sge(attr, wqe_size);
+ if (qp->sq.max_gs < attr->cap.max_send_sge)
+ return -ENOMEM;
+
+ attr->cap.max_send_sge = qp->sq.max_gs;
qp->sq.max_post = wq_size / wqe_size;
attr->cap.max_send_wr = qp->sq.max_post;
diff --git a/drivers/infiniband/hw/mlx5/srq.c b/drivers/infiniband/hw/mlx5/srq.c
index 3857dbd..729b069 100644
--- a/drivers/infiniband/hw/mlx5/srq.c
+++ b/drivers/infiniband/hw/mlx5/srq.c
@@ -282,6 +282,7 @@
mlx5_ib_dbg(dev, "desc_size 0x%x, req wr 0x%x, srq size 0x%x, max_gs 0x%x, max_avail_gather 0x%x\n",
desc_size, init_attr->attr.max_wr, srq->msrq.max, srq->msrq.max_gs,
srq->msrq.max_avail_gather);
+ in.type = init_attr->srq_type;
if (pd->uobject)
err = create_srq_user(pd, srq, &in, udata, buf_size);
@@ -294,7 +295,6 @@
goto err_srq;
}
- in.type = init_attr->srq_type;
in.log_size = ilog2(srq->msrq.max);
in.wqe_shift = srq->msrq.wqe_shift - 4;
if (srq->wq_sig)
diff --git a/drivers/infiniband/sw/rxe/rxe_net.c b/drivers/infiniband/sw/rxe/rxe_net.c
index ffff5a5..f4f3942 100644
--- a/drivers/infiniband/sw/rxe/rxe_net.c
+++ b/drivers/infiniband/sw/rxe/rxe_net.c
@@ -554,7 +554,7 @@
}
spin_lock_bh(&dev_list_lock);
- list_add_tail(&rxe_dev_list, &rxe->list);
+ list_add_tail(&rxe->list, &rxe_dev_list);
spin_unlock_bh(&dev_list_lock);
return rxe;
}
diff --git a/drivers/infiniband/sw/rxe/rxe_param.h b/drivers/infiniband/sw/rxe/rxe_param.h
index f459c43..13ed2cc 100644
--- a/drivers/infiniband/sw/rxe/rxe_param.h
+++ b/drivers/infiniband/sw/rxe/rxe_param.h
@@ -82,7 +82,7 @@
RXE_MAX_SGE = 32,
RXE_MAX_SGE_RD = 32,
RXE_MAX_CQ = 16384,
- RXE_MAX_LOG_CQE = 13,
+ RXE_MAX_LOG_CQE = 15,
RXE_MAX_MR = 2 * 1024,
RXE_MAX_PD = 0x7ffc,
RXE_MAX_QP_RD_ATOM = 128,
diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c
index c3e60e4..44b2108 100644
--- a/drivers/infiniband/sw/rxe/rxe_qp.c
+++ b/drivers/infiniband/sw/rxe/rxe_qp.c
@@ -813,8 +813,7 @@
del_timer_sync(&qp->rnr_nak_timer);
rxe_cleanup_task(&qp->req.task);
- if (qp_type(qp) == IB_QPT_RC)
- rxe_cleanup_task(&qp->comp.task);
+ rxe_cleanup_task(&qp->comp.task);
/* flush out any receive wr's or pending requests */
__rxe_do_task(&qp->req.task);
@@ -855,4 +854,5 @@
free_rd_atomic_resources(qp);
kernel_sock_shutdown(qp->sk, SHUT_RDWR);
+ sock_release(qp->sk);
}
diff --git a/drivers/infiniband/sw/rxe/rxe_req.c b/drivers/infiniband/sw/rxe/rxe_req.c
index 22bd963..9f46be5 100644
--- a/drivers/infiniband/sw/rxe/rxe_req.c
+++ b/drivers/infiniband/sw/rxe/rxe_req.c
@@ -548,23 +548,23 @@
static void save_state(struct rxe_send_wqe *wqe,
struct rxe_qp *qp,
struct rxe_send_wqe *rollback_wqe,
- struct rxe_qp *rollback_qp)
+ u32 *rollback_psn)
{
rollback_wqe->state = wqe->state;
rollback_wqe->first_psn = wqe->first_psn;
rollback_wqe->last_psn = wqe->last_psn;
- rollback_qp->req.psn = qp->req.psn;
+ *rollback_psn = qp->req.psn;
}
static void rollback_state(struct rxe_send_wqe *wqe,
struct rxe_qp *qp,
struct rxe_send_wqe *rollback_wqe,
- struct rxe_qp *rollback_qp)
+ u32 rollback_psn)
{
wqe->state = rollback_wqe->state;
wqe->first_psn = rollback_wqe->first_psn;
wqe->last_psn = rollback_wqe->last_psn;
- qp->req.psn = rollback_qp->req.psn;
+ qp->req.psn = rollback_psn;
}
static void update_state(struct rxe_qp *qp, struct rxe_send_wqe *wqe,
@@ -593,8 +593,8 @@
int mtu;
int opcode;
int ret;
- struct rxe_qp rollback_qp;
struct rxe_send_wqe rollback_wqe;
+ u32 rollback_psn;
next_wqe:
if (unlikely(!qp->valid || qp->req.state == QP_STATE_ERROR))
@@ -719,7 +719,7 @@
* rxe_xmit_packet().
* Otherwise, completer might initiate an unjustified retry flow.
*/
- save_state(wqe, qp, &rollback_wqe, &rollback_qp);
+ save_state(wqe, qp, &rollback_wqe, &rollback_psn);
update_wqe_state(qp, wqe, &pkt);
update_wqe_psn(qp, wqe, &pkt, payload);
ret = rxe_xmit_packet(to_rdev(qp->ibqp.device), qp, &pkt, skb);
@@ -727,7 +727,7 @@
qp->need_req_skb = 1;
kfree_skb(skb);
- rollback_state(wqe, qp, &rollback_wqe, &rollback_qp);
+ rollback_state(wqe, qp, &rollback_wqe, rollback_psn);
if (ret == -EAGAIN) {
rxe_run_task(&qp->req.task, 1);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 339a1ee..81a8080 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -1054,8 +1054,6 @@
tx_qp = ib_create_qp(priv->pd, &attr);
if (PTR_ERR(tx_qp) == -EINVAL) {
- ipoib_warn(priv, "can't use GFP_NOIO for QPs on device %s, using GFP_KERNEL\n",
- priv->ca->name);
attr.create_flags &= ~IB_QP_CREATE_USE_GFP_NOIO;
tx_qp = ib_create_qp(priv->pd, &attr);
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index 1909dd2..fddff40 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -575,8 +575,11 @@
if (!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags))
return;
- if (ib_query_port(priv->ca, priv->port, &port_attr) ||
- port_attr.state != IB_PORT_ACTIVE) {
+ if (ib_query_port(priv->ca, priv->port, &port_attr)) {
+ ipoib_dbg(priv, "ib_query_port() failed\n");
+ return;
+ }
+ if (port_attr.state != IB_PORT_ACTIVE) {
ipoib_dbg(priv, "port state is not ACTIVE (state = %d) suspending join task\n",
port_attr.state);
return;
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index 64b3d11..140f3f3 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -651,13 +651,6 @@
SHOST_DIX_GUARD_CRC);
}
- /*
- * Limit the sg_tablesize and max_sectors based on the device
- * max fastreg page list length.
- */
- shost->sg_tablesize = min_t(unsigned short, shost->sg_tablesize,
- ib_conn->device->ib_device->attrs.max_fast_reg_page_list_len);
-
if (iscsi_host_add(shost,
ib_conn->device->ib_device->dma_device)) {
mutex_unlock(&iser_conn->state_mutex);
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index d980fb4..e7dcf14 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -366,6 +366,7 @@
struct srp_fr_desc *d;
struct ib_mr *mr;
int i, ret = -EINVAL;
+ enum ib_mr_type mr_type;
if (pool_size <= 0)
goto err;
@@ -379,9 +380,13 @@
spin_lock_init(&pool->lock);
INIT_LIST_HEAD(&pool->free_list);
+ if (device->attrs.device_cap_flags & IB_DEVICE_SG_GAPS_REG)
+ mr_type = IB_MR_TYPE_SG_GAPS;
+ else
+ mr_type = IB_MR_TYPE_MEM_REG;
+
for (i = 0, d = &pool->desc[0]; i < pool->size; i++, d++) {
- mr = ib_alloc_mr(pd, IB_MR_TYPE_MEM_REG,
- max_page_list_len);
+ mr = ib_alloc_mr(pd, mr_type, max_page_list_len);
if (IS_ERR(mr)) {
ret = PTR_ERR(mr);
goto destroy_pool;
@@ -3678,6 +3683,12 @@
indirect_sg_entries = cmd_sg_entries;
}
+ if (indirect_sg_entries > SG_MAX_SEGMENTS) {
+ pr_warn("Clamping indirect_sg_entries to %u\n",
+ SG_MAX_SEGMENTS);
+ indirect_sg_entries = SG_MAX_SEGMENTS;
+ }
+
srp_remove_wq = create_workqueue("srp_remove");
if (!srp_remove_wq) {
ret = -ENOMEM;
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 1ee1786..d01a5b1 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -26,6 +26,8 @@
obj-$(CONFIG_INPUT_MISC) += misc/
obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
+obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o
+obj-$(CONFIG_INPUT_KEYCOMBO) += keycombo.o
obj-$(CONFIG_RMI4_CORE) += rmi4/
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 83af17a..bbe1524 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -1376,6 +1376,12 @@
input_dev->name = xpad->name;
input_dev->phys = xpad->phys;
usb_to_input_id(xpad->udev, &input_dev->id);
+
+ if (xpad->xtype == XTYPE_XBOX360W) {
+ /* x360w controllers and the receiver have different ids */
+ input_dev->id.product = 0x02a1;
+ }
+
input_dev->dev.parent = &xpad->intf->dev;
input_set_drvdata(input_dev, xpad);
diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c
index f6e643b..c877e56 100644
--- a/drivers/input/keyboard/goldfish_events.c
+++ b/drivers/input/keyboard/goldfish_events.c
@@ -17,6 +17,7 @@
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/input.h>
+#include <linux/input/mt.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -24,6 +25,8 @@
#include <linux/io.h>
#include <linux/acpi.h>
+#define GOLDFISH_MAX_FINGERS 5
+
enum {
REG_READ = 0x00,
REG_SET_PAGE = 0x00,
@@ -52,7 +55,21 @@
value = __raw_readl(edev->addr + REG_READ);
input_event(edev->input, type, code, value);
- input_sync(edev->input);
+ // Send an extra (EV_SYN, SYN_REPORT, 0x0) event
+ // if a key was pressed. Some keyboard device
+ // drivers may only send the EV_KEY event and
+ // not EV_SYN.
+ // Note that sending an extra SYN_REPORT is not
+ // necessary nor correct protocol with other
+ // devices such as touchscreens, which will send
+ // their own SYN_REPORT's when sufficient event
+ // information has been collected (e.g., for
+ // touchscreens, when pressure and X/Y coordinates
+ // have been received). Hence, we will only send
+ // this extra SYN_REPORT if type == EV_KEY.
+ if (type == EV_KEY) {
+ input_sync(edev->input);
+ }
return IRQ_HANDLED;
}
@@ -154,6 +171,15 @@
input_dev->name = edev->name;
input_dev->id.bustype = BUS_HOST;
+ // Set the Goldfish Device to be multi-touch.
+ // In the Ranchu kernel, there is multi-touch-specific
+ // code for handling ABS_MT_SLOT events.
+ // See drivers/input/input.c:input_handle_abs_event.
+ // If we do not issue input_mt_init_slots,
+ // the kernel will filter out needed ABS_MT_SLOT
+ // events when we touch the screen in more than one place,
+ // preventing multi-touch with more than one finger from working.
+ input_mt_init_slots(input_dev, GOLDFISH_MAX_FINGERS, 0);
events_import_bits(edev, input_dev->evbit, EV_SYN, EV_MAX);
events_import_bits(edev, input_dev->keybit, EV_KEY, KEY_MAX);
diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
index 2adfd86c..930424e 100644
--- a/drivers/input/misc/drv260x.c
+++ b/drivers/input/misc/drv260x.c
@@ -592,7 +592,6 @@
}
haptics->input_dev->name = "drv260x:haptics";
- haptics->input_dev->dev.parent = client->dev.parent;
haptics->input_dev->close = drv260x_close;
input_set_drvdata(haptics->input_dev, haptics);
input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c
index eaa9e89..08769dd 100644
--- a/drivers/input/misc/gpio_matrix.c
+++ b/drivers/input/misc/gpio_matrix.c
@@ -19,13 +19,12 @@
#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
-#include <linux/wakelock.h>
struct gpio_kp {
struct gpio_event_input_devs *input_devs;
struct gpio_event_matrix_info *keypad_info;
struct hrtimer timer;
- struct wake_lock wake_lock;
+ struct wakeup_source wake_src;
int current_output;
unsigned int use_irq:1;
unsigned int key_state_changed:1;
@@ -215,7 +214,7 @@
}
for (in = 0; in < mi->ninputs; in++)
enable_irq(gpio_to_irq(mi->input_gpios[in]));
- wake_unlock(&kp->wake_lock);
+ __pm_relax(&kp->wake_src);
return HRTIMER_NORESTART;
}
@@ -242,7 +241,7 @@
else
gpio_direction_input(mi->output_gpios[i]);
}
- wake_lock(&kp->wake_lock);
+ __pm_stay_awake(&kp->wake_src);
hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
return IRQ_HANDLED;
}
@@ -396,7 +395,7 @@
hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
kp->timer.function = gpio_keypad_timer_func;
- wake_lock_init(&kp->wake_lock, WAKE_LOCK_SUSPEND, "gpio_kp");
+ wakeup_source_init(&kp->wake_src, "gpio_kp");
err = gpio_keypad_request_irqs(kp);
kp->use_irq = err == 0;
@@ -406,7 +405,7 @@
kp->use_irq ? "interrupt" : "polling");
if (kp->use_irq)
- wake_lock(&kp->wake_lock);
+ __pm_stay_awake(&kp->wake_src);
hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
return 0;
@@ -420,7 +419,7 @@
free_irq(gpio_to_irq(mi->input_gpios[i]), kp);
hrtimer_cancel(&kp->timer);
- wake_lock_destroy(&kp->wake_lock);
+ wakeup_source_trash(&kp->wake_src);
for (i = mi->noutputs - 1; i >= 0; i--) {
err_gpio_direction_input_failed:
gpio_free(mi->input_gpios[i]);
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index 6d7de9b..b93fe83 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -1346,6 +1346,18 @@
priv->multi_packet = 0;
+ /* Report trackstick */
+ if (alps_get_pkt_id_ss4_v2(packet) == SS4_PACKET_ID_STICK) {
+ if (priv->flags & ALPS_DUALPOINT) {
+ input_report_key(dev2, BTN_LEFT, f->ts_left);
+ input_report_key(dev2, BTN_RIGHT, f->ts_right);
+ input_report_key(dev2, BTN_MIDDLE, f->ts_middle);
+ input_sync(dev2);
+ }
+ return;
+ }
+
+ /* Report touchpad */
alps_report_mt_data(psmouse, (f->fingers <= 4) ? f->fingers : 4);
input_mt_report_finger_count(dev, f->fingers);
@@ -1356,13 +1368,6 @@
input_report_abs(dev, ABS_PRESSURE, f->pressure);
input_sync(dev);
-
- if (priv->flags & ALPS_DUALPOINT) {
- input_report_key(dev2, BTN_LEFT, f->ts_left);
- input_report_key(dev2, BTN_RIGHT, f->ts_right);
- input_report_key(dev2, BTN_MIDDLE, f->ts_middle);
- input_sync(dev2);
- }
}
static bool alps_is_valid_package_ss4_v2(struct psmouse *psmouse)
diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c
index cf805b9..2e934ae 100644
--- a/drivers/input/rmi4/rmi_f54.c
+++ b/drivers/input/rmi4/rmi_f54.c
@@ -200,7 +200,7 @@
error = rmi_write(rmi_dev, fn->fd.command_base_addr, F54_GET_REPORT);
if (error < 0)
- return error;
+ goto unlock;
init_completion(&f54->cmd_done);
@@ -209,9 +209,10 @@
queue_delayed_work(f54->workqueue, &f54->work, 0);
+unlock:
mutex_unlock(&f54->data_mutex);
- return 0;
+ return error;
}
static size_t rmi_f54_get_report_size(struct f54_data *f54)
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index 073246c..0cdd958 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -211,6 +211,12 @@
DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"),
},
},
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "PEGATRON CORPORATION"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "C15B"),
+ },
+ },
{ }
};
diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
index 02aec28..3e6003d 100644
--- a/drivers/input/touchscreen/elants_i2c.c
+++ b/drivers/input/touchscreen/elants_i2c.c
@@ -914,9 +914,9 @@
case QUEUE_HEADER_NORMAL:
report_count = ts->buf[FW_HDR_COUNT];
- if (report_count > 3) {
+ if (report_count == 0 || report_count > 3) {
dev_err(&client->dev,
- "too large report count: %*ph\n",
+ "bad report count: %*ph\n",
HEADER_SIZE, ts->buf);
break;
}
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 754595e..11a13b5 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -1021,7 +1021,7 @@
next_tail = (tail + sizeof(*cmd)) % CMD_BUFFER_SIZE;
left = (head - next_tail) % CMD_BUFFER_SIZE;
- if (left <= 2) {
+ if (left <= 0x20) {
struct iommu_cmd sync_cmd;
int ret;
diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c
index 594849a..f8ed8c9 100644
--- a/drivers/iommu/amd_iommu_v2.c
+++ b/drivers/iommu/amd_iommu_v2.c
@@ -805,8 +805,10 @@
goto out_free_domain;
group = iommu_group_get(&pdev->dev);
- if (!group)
+ if (!group) {
+ ret = -EINVAL;
goto out_free_domain;
+ }
ret = iommu_attach_group(dev_state->domain, group);
if (ret != 0)
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index d8376c2..d82637a 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -2037,6 +2037,25 @@
if (context_present(context))
goto out_unlock;
+ /*
+ * For kdump cases, old valid entries may be cached due to the
+ * in-flight DMA and copied pgtable, but there is no unmapping
+ * behaviour for them, thus we need an explicit cache flush for
+ * the newly-mapped device. For kdump, at this point, the device
+ * is supposed to finish reset at its driver probe stage, so no
+ * in-flight DMA will exist, and we don't need to worry anymore
+ * hereafter.
+ */
+ if (context_copied(context)) {
+ u16 did_old = context_domain_id(context);
+
+ if (did_old >= 0 && did_old < cap_ndoms(iommu->cap))
+ iommu->flush.flush_context(iommu, did_old,
+ (((u16)bus) << 8) | devfn,
+ DMA_CCMD_MASK_NOBIT,
+ DMA_CCMD_DEVICE_INVL);
+ }
+
pgd = domain->pgd;
context_clear_entry(context);
@@ -5197,6 +5216,25 @@
}
#ifdef CONFIG_INTEL_IOMMU_SVM
+#define MAX_NR_PASID_BITS (20)
+static inline unsigned long intel_iommu_get_pts(struct intel_iommu *iommu)
+{
+ /*
+ * Convert ecap_pss to extend context entry pts encoding, also
+ * respect the soft pasid_max value set by the iommu.
+ * - number of PASID bits = ecap_pss + 1
+ * - number of PASID table entries = 2^(pts + 5)
+ * Therefore, pts = ecap_pss - 4
+ * e.g. KBL ecap_pss = 0x13, PASID has 20 bits, pts = 15
+ */
+ if (ecap_pss(iommu->ecap) < 5)
+ return 0;
+
+ /* pasid_max is encoded as actual number of entries not the bits */
+ return find_first_bit((unsigned long *)&iommu->pasid_max,
+ MAX_NR_PASID_BITS) - 5;
+}
+
int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sdev)
{
struct device_domain_info *info;
@@ -5229,7 +5267,9 @@
if (!(ctx_lo & CONTEXT_PASIDE)) {
context[1].hi = (u64)virt_to_phys(iommu->pasid_state_table);
- context[1].lo = (u64)virt_to_phys(iommu->pasid_table) | ecap_pss(iommu->ecap);
+ context[1].lo = (u64)virt_to_phys(iommu->pasid_table) |
+ intel_iommu_get_pts(iommu);
+
wmb();
/* CONTEXT_TT_MULTI_LEVEL and CONTEXT_TT_DEV_IOTLB are both
* extended to permit requests-with-PASID if the PASIDE bit
diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c
index 353c549..c2662a1 100644
--- a/drivers/irqchip/irq-bcm7038-l1.c
+++ b/drivers/irqchip/irq-bcm7038-l1.c
@@ -215,6 +215,31 @@
return 0;
}
+static void bcm7038_l1_cpu_offline(struct irq_data *d)
+{
+ struct cpumask *mask = irq_data_get_affinity_mask(d);
+ int cpu = smp_processor_id();
+ cpumask_t new_affinity;
+
+ /* This CPU was not on the affinity mask */
+ if (!cpumask_test_cpu(cpu, mask))
+ return;
+
+ if (cpumask_weight(mask) > 1) {
+ /*
+ * Multiple CPU affinity, remove this CPU from the affinity
+ * mask
+ */
+ cpumask_copy(&new_affinity, mask);
+ cpumask_clear_cpu(cpu, &new_affinity);
+ } else {
+ /* Only CPU, put on the lowest online CPU */
+ cpumask_clear(&new_affinity);
+ cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity);
+ }
+ irq_set_affinity_locked(d, &new_affinity, false);
+}
+
static int __init bcm7038_l1_init_one(struct device_node *dn,
unsigned int idx,
struct bcm7038_l1_chip *intc)
@@ -266,6 +291,7 @@
.irq_mask = bcm7038_l1_mask,
.irq_unmask = bcm7038_l1_unmask,
.irq_set_affinity = bcm7038_l1_set_affinity,
+ .irq_cpu_offline = bcm7038_l1_cpu_offline,
};
static int bcm7038_l1_map(struct irq_domain *d, unsigned int virq,
diff --git a/drivers/irqchip/irq-gic-common.h b/drivers/irqchip/irq-gic-common.h
index 205e5fd..83b72f4 100644
--- a/drivers/irqchip/irq-gic-common.h
+++ b/drivers/irqchip/irq-gic-common.h
@@ -27,6 +27,8 @@
u32 iidr;
u32 mask;
};
+extern bool from_suspend;
+extern struct irq_chip gic_arch_extn;
int gic_configure_irq(unsigned int irq, unsigned int type,
void __iomem *base, void (*sync_access)(void));
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 300347e..c9281fb 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -689,6 +689,9 @@
static int gic_cpu_pm_notifier(struct notifier_block *self,
unsigned long cmd, void *v)
{
+ if (from_suspend)
+ return NOTIFY_OK;
+
if (cmd == CPU_PM_EXIT) {
if (gic_dist_security_disabled())
gic_enable_redist(true);
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index d6c404b..bc6d121 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -710,7 +710,8 @@
gic_cpu_if_up(gic);
}
-static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v)
+static int gic_notifier(struct notifier_block *self, unsigned long cmd,
+ void *aff_level)
{
int i;
@@ -729,11 +730,20 @@
gic_cpu_restore(&gic_data[i]);
break;
case CPU_CLUSTER_PM_ENTER:
- gic_dist_save(&gic_data[i]);
+ /*
+ * Affinity level of the node
+ * eg:
+ * cpu level = 0
+ * l2 level = 1
+ * cci level = 2
+ */
+ if (!(unsigned long)aff_level)
+ gic_dist_save(&gic_data[i]);
break;
case CPU_CLUSTER_PM_ENTER_FAILED:
case CPU_CLUSTER_PM_EXIT:
- gic_dist_restore(&gic_data[i]);
+ if (!(unsigned long)aff_level)
+ gic_dist_restore(&gic_data[i]);
break;
}
}
diff --git a/drivers/isdn/hardware/eicon/message.c b/drivers/isdn/hardware/eicon/message.c
index 1a1d997..296f141 100644
--- a/drivers/isdn/hardware/eicon/message.c
+++ b/drivers/isdn/hardware/eicon/message.c
@@ -11297,7 +11297,8 @@
((CAPI_MSG *) msg)->header.ncci = 0;
((CAPI_MSG *) msg)->info.facility_req.Selector = SELECTOR_LINE_INTERCONNECT;
((CAPI_MSG *) msg)->info.facility_req.structs[0] = 3;
- PUT_WORD(&(((CAPI_MSG *) msg)->info.facility_req.structs[1]), LI_REQ_SILENT_UPDATE);
+ ((CAPI_MSG *) msg)->info.facility_req.structs[1] = LI_REQ_SILENT_UPDATE & 0xff;
+ ((CAPI_MSG *) msg)->info.facility_req.structs[2] = LI_REQ_SILENT_UPDATE >> 8;
((CAPI_MSG *) msg)->info.facility_req.structs[3] = 0;
w = api_put(notify_plci->appl, (CAPI_MSG *) msg);
if (w != _QUEUE_FULL)
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index f550da3..1e66909 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -458,6 +458,21 @@
If unsure, say N.
+config DM_VERITY_HASH_PREFETCH_MIN_SIZE_128
+ bool "Prefetch size 128"
+
+config DM_VERITY_HASH_PREFETCH_MIN_SIZE
+ int "Verity hash prefetch minimum size"
+ depends on DM_VERITY
+ range 1 4096
+ default 128 if DM_VERITY_HASH_PREFETCH_MIN_SIZE_128
+ default 1
+ ---help---
+ This sets minimum number of hash blocks to prefetch for dm-verity.
+ For devices like eMMC, having larger prefetch size like 128 can improve
+ performance with increased memory consumption for keeping more hashes
+ in RAM.
+
config DM_VERITY_FEC
bool "Verity forward error correction support"
depends on DM_VERITY
@@ -510,6 +525,7 @@
depends on ASYMMETRIC_KEY_TYPE
depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
depends on MD_LINEAR
+ select DM_VERITY_HASH_PREFETCH_MIN_SIZE_128
---help---
This device-mapper target is virtually a VERITY target. This
target is setup by reading the metadata contents piggybacked
diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c
index bb6c128..881e709 100644
--- a/drivers/md/dm-android-verity.c
+++ b/drivers/md/dm-android-verity.c
@@ -635,6 +635,7 @@
android_verity_target.status = dm_linear_status,
android_verity_target.prepare_ioctl = dm_linear_prepare_ioctl,
android_verity_target.iterate_devices = dm_linear_iterate_devices,
+ android_verity_target.direct_access = dm_linear_direct_access,
android_verity_target.io_hints = NULL;
err = dm_linear_ctr(ti, DM_LINEAR_ARGS, linear_table_args);
diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h
index 0c7ff6a..c8d7ab64 100644
--- a/drivers/md/dm-android-verity.h
+++ b/drivers/md/dm-android-verity.h
@@ -118,4 +118,6 @@
extern int dm_linear_iterate_devices(struct dm_target *ti,
iterate_devices_callout_fn fn, void *data);
extern int dm_linear_ctr(struct dm_target *ti, unsigned int argc, char **argv);
+extern long dm_linear_direct_access(struct dm_target *ti, sector_t sector,
+ void **kaddr, pfn_t *pfn, long size);
#endif /* DM_ANDROID_VERITY_H */
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 715cc52..7a5b75f 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1503,12 +1503,15 @@
if (!cc->key_size && strcmp(key, "-"))
goto out;
+ /* clear the flag since following operations may invalidate previously valid key */
+ clear_bit(DM_CRYPT_KEY_VALID, &cc->flags);
+
if (cc->key_size && crypt_decode_key(cc->key, key, cc->key_size) < 0)
goto out;
- set_bit(DM_CRYPT_KEY_VALID, &cc->flags);
-
r = crypt_setkey_allcpus(cc);
+ if (!r)
+ set_bit(DM_CRYPT_KEY_VALID, &cc->flags);
out:
/* Hex key string not needed after here, so wipe it. */
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c
index 6a2e8dd..3643cba 100644
--- a/drivers/md/dm-flakey.c
+++ b/drivers/md/dm-flakey.c
@@ -200,11 +200,13 @@
if (!(fc->up_interval + fc->down_interval)) {
ti->error = "Total (up + down) interval is zero";
+ r = -EINVAL;
goto bad;
}
if (fc->up_interval + fc->down_interval < fc->up_interval) {
ti->error = "Interval overflow";
+ r = -EINVAL;
goto bad;
}
diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c
index 760a464..4ad62d6 100644
--- a/drivers/md/dm-linear.c
+++ b/drivers/md/dm-linear.c
@@ -147,7 +147,7 @@
}
EXPORT_SYMBOL_GPL(dm_linear_iterate_devices);
-static long linear_direct_access(struct dm_target *ti, sector_t sector,
+long dm_linear_direct_access(struct dm_target *ti, sector_t sector,
void **kaddr, pfn_t *pfn, long size)
{
struct linear_c *lc = ti->private;
@@ -164,6 +164,7 @@
return ret;
}
+EXPORT_SYMBOL_GPL(dm_linear_direct_access);
static struct target_type linear_target = {
.name = "linear",
@@ -173,9 +174,9 @@
.dtr = dm_linear_dtr,
.map = dm_linear_map,
.status = dm_linear_status,
- .prepare_ioctl = dm_linear_prepare_ioctl,
+ .prepare_ioctl = dm_linear_prepare_ioctl,
.iterate_devices = dm_linear_iterate_devices,
- .direct_access = linear_direct_access,
+ .direct_access = dm_linear_direct_access,
};
int __init dm_linear_init(void)
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index 6d53810..af2d79b 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -2994,6 +2994,9 @@
}
}
+ /* Disable/enable discard support on raid set. */
+ configure_discard_support(rs);
+
mddev_unlock(&rs->md);
return 0;
@@ -3580,12 +3583,6 @@
if (test_bit(RT_FLAG_UPDATE_SBS, &rs->runtime_flags))
rs_update_sbs(rs);
- /*
- * Disable/enable discard support on raid set after any
- * conversion, because devices can have been added
- */
- configure_discard_support(rs);
-
/* Load the bitmap from disk unless raid0 */
r = __load_dirty_region_bitmap(rs);
if (r)
diff --git a/drivers/md/dm-rq.c b/drivers/md/dm-rq.c
index 1d0d2ad..31a89c8 100644
--- a/drivers/md/dm-rq.c
+++ b/drivers/md/dm-rq.c
@@ -226,6 +226,9 @@
*/
static void rq_completed(struct mapped_device *md, int rw, bool run_queue)
{
+ struct request_queue *q = md->queue;
+ unsigned long flags;
+
atomic_dec(&md->pending[rw]);
/* nudge anyone waiting on suspend queue */
@@ -238,8 +241,11 @@
* back into ->request_fn() could deadlock attempting to grab the
* queue lock again.
*/
- if (!md->queue->mq_ops && run_queue)
- blk_run_queue_async(md->queue);
+ if (!q->mq_ops && run_queue) {
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_run_queue_async(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+ }
/*
* dm_put() must be at the end of this function. See the comment above
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 399bcac..d837a28 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -925,12 +925,6 @@
BUG_ON(!request_based); /* No targets in this table */
- if (list_empty(devices) && __table_type_request_based(live_md_type)) {
- /* inherit live MD type */
- t->type = live_md_type;
- return 0;
- }
-
/*
* The only way to establish DM_TYPE_MQ_REQUEST_BASED is by
* having a compatible target use dm_table_set_type.
@@ -949,6 +943,19 @@
return -EINVAL;
}
+ if (list_empty(devices)) {
+ int srcu_idx;
+ struct dm_table *live_table = dm_get_live_table(t->md, &srcu_idx);
+
+ /* inherit live table's type and all_blk_mq */
+ if (live_table) {
+ t->type = live_table->type;
+ t->all_blk_mq = live_table->all_blk_mq;
+ }
+ dm_put_live_table(t->md, srcu_idx);
+ return 0;
+ }
+
/* Non-request-stackable devices can't be used for request-based dm */
list_for_each_entry(dd, devices, list) {
struct request_queue *q = bdev_get_queue(dd->dm_dev->bdev);
@@ -975,6 +982,11 @@
t->all_blk_mq = true;
}
+ if (t->type == DM_TYPE_MQ_REQUEST_BASED && !t->all_blk_mq) {
+ DMERR("table load rejected: all devices are not blk-mq request-stackable");
+ return -EINVAL;
+ }
+
return 0;
}
diff --git a/drivers/md/dm-verity-fec.h b/drivers/md/dm-verity-fec.h
index f36a6772..4db0cae 100644
--- a/drivers/md/dm-verity-fec.h
+++ b/drivers/md/dm-verity-fec.h
@@ -12,6 +12,7 @@
#ifndef DM_VERITY_FEC_H
#define DM_VERITY_FEC_H
+#include "dm.h"
#include "dm-core.h"
#include "dm-verity.h"
#include <linux/rslib.h>
diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index b53539e..5d0a996 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -501,6 +501,7 @@
container_of(work, struct dm_verity_prefetch_work, work);
struct dm_verity *v = pw->v;
int i;
+ sector_t prefetch_size;
for (i = v->levels - 2; i >= 0; i--) {
sector_t hash_block_start;
@@ -523,8 +524,14 @@
hash_block_end = v->hash_blocks - 1;
}
no_prefetch_cluster:
+ // for emmc, it is more efficient to send bigger read
+ prefetch_size = max((sector_t)CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE,
+ hash_block_end - hash_block_start + 1);
+ if ((hash_block_start + prefetch_size) >= (v->hash_start + v->hash_blocks)) {
+ prefetch_size = hash_block_end - hash_block_start + 1;
+ }
dm_bufio_prefetch(v->bufio, hash_block_start,
- hash_block_end - hash_block_start + 1);
+ prefetch_size);
}
kfree(pw);
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 2089d46..24925f2 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -6829,7 +6829,7 @@
/* need to ensure recovery thread has run */
wait_event_interruptible_timeout(mddev->sb_wait,
!test_bit(MD_RECOVERY_NEEDED,
- &mddev->flags),
+ &mddev->recovery),
msecs_to_jiffies(5000));
if (cmd == STOP_ARRAY || cmd == STOP_ARRAY_RO) {
/* Need to flush page cache, and ensure no-one else opens
@@ -7092,7 +7092,8 @@
if (test_bit(MD_CLOSING, &mddev->flags)) {
mutex_unlock(&mddev->open_mutex);
- return -ENODEV;
+ err = -ENODEV;
+ goto out;
}
err = 0;
@@ -7101,6 +7102,8 @@
check_disk_change(bdev);
out:
+ if (err)
+ mddev_put(mddev);
return err;
}
diff --git a/drivers/md/persistent-data/dm-space-map-metadata.c b/drivers/md/persistent-data/dm-space-map-metadata.c
index 7e44005..20557e2 100644
--- a/drivers/md/persistent-data/dm-space-map-metadata.c
+++ b/drivers/md/persistent-data/dm-space-map-metadata.c
@@ -775,16 +775,14 @@
memcpy(&smm->sm, &bootstrap_ops, sizeof(smm->sm));
r = sm_ll_new_metadata(&smm->ll, tm);
- if (r)
- return r;
-
- if (nr_blocks > DM_SM_METADATA_MAX_BLOCKS)
- nr_blocks = DM_SM_METADATA_MAX_BLOCKS;
- r = sm_ll_extend(&smm->ll, nr_blocks);
- if (r)
- return r;
-
+ if (!r) {
+ if (nr_blocks > DM_SM_METADATA_MAX_BLOCKS)
+ nr_blocks = DM_SM_METADATA_MAX_BLOCKS;
+ r = sm_ll_extend(&smm->ll, nr_blocks);
+ }
memcpy(&smm->sm, &ops, sizeof(smm->sm));
+ if (r)
+ return r;
/*
* Now we need to update the newly created data structures with the
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 92ac251..cce6057b 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -6984,6 +6984,15 @@
stripe = (stripe | (stripe-1)) + 1;
mddev->queue->limits.discard_alignment = stripe;
mddev->queue->limits.discard_granularity = stripe;
+
+ /*
+ * We use 16-bit counter of active stripes in bi_phys_segments
+ * (minus one for over-loaded initialization)
+ */
+ blk_queue_max_hw_sectors(mddev->queue, 0xfffe * STRIPE_SECTORS);
+ blk_queue_max_discard_sectors(mddev->queue,
+ 0xfffe * STRIPE_SECTORS);
+
/*
* unaligned part of discard request will be ignored, so can't
* guarantee discard_zeroes_data
diff --git a/drivers/media/dvb-frontends/mn88472.c b/drivers/media/dvb-frontends/mn88472.c
index 18fb2df..7265011 100644
--- a/drivers/media/dvb-frontends/mn88472.c
+++ b/drivers/media/dvb-frontends/mn88472.c
@@ -488,18 +488,6 @@
goto err_kfree;
}
- /* Check demod answers with correct chip id */
- ret = regmap_read(dev->regmap[0], 0xff, &utmp);
- if (ret)
- goto err_regmap_0_regmap_exit;
-
- dev_dbg(&client->dev, "chip id=%02x\n", utmp);
-
- if (utmp != 0x02) {
- ret = -ENODEV;
- goto err_regmap_0_regmap_exit;
- }
-
/*
* Chip has three I2C addresses for different register banks. Used
* addresses are 0x18, 0x1a and 0x1c. We register two dummy clients,
@@ -536,6 +524,18 @@
}
i2c_set_clientdata(dev->client[2], dev);
+ /* Check demod answers with correct chip id */
+ ret = regmap_read(dev->regmap[2], 0xff, &utmp);
+ if (ret)
+ goto err_regmap_2_regmap_exit;
+
+ dev_dbg(&client->dev, "chip id=%02x\n", utmp);
+
+ if (utmp != 0x02) {
+ ret = -ENODEV;
+ goto err_regmap_2_regmap_exit;
+ }
+
/* Sleep because chip is active by default */
ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
if (ret)
diff --git a/drivers/media/dvb-frontends/mn88473.c b/drivers/media/dvb-frontends/mn88473.c
index 451974a..2932bdc 100644
--- a/drivers/media/dvb-frontends/mn88473.c
+++ b/drivers/media/dvb-frontends/mn88473.c
@@ -485,18 +485,6 @@
goto err_kfree;
}
- /* Check demod answers with correct chip id */
- ret = regmap_read(dev->regmap[0], 0xff, &uitmp);
- if (ret)
- goto err_regmap_0_regmap_exit;
-
- dev_dbg(&client->dev, "chip id=%02x\n", uitmp);
-
- if (uitmp != 0x03) {
- ret = -ENODEV;
- goto err_regmap_0_regmap_exit;
- }
-
/*
* Chip has three I2C addresses for different register banks. Used
* addresses are 0x18, 0x1a and 0x1c. We register two dummy clients,
@@ -533,6 +521,18 @@
}
i2c_set_clientdata(dev->client[2], dev);
+ /* Check demod answers with correct chip id */
+ ret = regmap_read(dev->regmap[2], 0xff, &uitmp);
+ if (ret)
+ goto err_regmap_2_regmap_exit;
+
+ dev_dbg(&client->dev, "chip id=%02x\n", uitmp);
+
+ if (uitmp != 0x03) {
+ ret = -ENODEV;
+ goto err_regmap_2_regmap_exit;
+ }
+
/* Sleep because chip is active by default */
ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
if (ret)
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 2669b4b..5a27bff 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -655,6 +655,7 @@
config VIDEO_S5K4ECGX
tristate "Samsung S5K4ECGX sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ select CRC32
---help---
This is a V4L2 sensor-level driver for Samsung S5K4ECGX 5M
camera sensor with an embedded SoC image signal processor.
diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index 4740da3..59aa4da 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -288,8 +288,12 @@
tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode);
tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input);
- /* Svideo should enable YCrCb output and disable GPCL output
- * For Composite and TV, it should be the reverse
+ /*
+ * Setup the FID/GLCO/VLK/HVLK and INTREQ/GPCL/VBLK output signals. For
+ * S-Video we output the vertical lock (VLK) signal on FID/GLCO/VLK/HVLK
+ * and set INTREQ/GPCL/VBLK to logic 0. For composite we output the
+ * field indicator (FID) signal on FID/GLCO/VLK/HVLK and set
+ * INTREQ/GPCL/VBLK to logic 1.
*/
val = tvp5150_read(sd, TVP5150_MISC_CTL);
if (val < 0) {
@@ -298,9 +302,9 @@
}
if (decoder->input == TVP5150_SVIDEO)
- val = (val & ~0x40) | 0x10;
+ val = (val & ~TVP5150_MISC_CTL_GPCL) | TVP5150_MISC_CTL_HVLK;
else
- val = (val & ~0x10) | 0x40;
+ val = (val & ~TVP5150_MISC_CTL_HVLK) | TVP5150_MISC_CTL_GPCL;
tvp5150_write(sd, TVP5150_MISC_CTL, val);
};
@@ -452,7 +456,12 @@
},{ /* Automatic offset and AGC enabled */
TVP5150_ANAL_CHL_CTL, 0x15
},{ /* Activate YCrCb output 0x9 or 0xd ? */
- TVP5150_MISC_CTL, 0x6f
+ TVP5150_MISC_CTL, TVP5150_MISC_CTL_GPCL |
+ TVP5150_MISC_CTL_INTREQ_OE |
+ TVP5150_MISC_CTL_YCBCR_OE |
+ TVP5150_MISC_CTL_SYNC_OE |
+ TVP5150_MISC_CTL_VBLANK |
+ TVP5150_MISC_CTL_CLOCK_OE,
},{ /* Activates video std autodetection for all standards */
TVP5150_AUTOSW_MSK, 0x0
},{ /* Default format: 0x47. For 4:2:2: 0x40 */
@@ -815,6 +824,7 @@
return 0;
case V4L2_CID_HUE:
tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->val);
+ break;
case V4L2_CID_TEST_PATTERN:
decoder->enable = ctrl->val ? false : true;
tvp5150_selmux(sd);
@@ -857,8 +867,6 @@
f = &format->format;
- tvp5150_reset(sd, 0);
-
f->width = decoder->rect.width;
f->height = decoder->rect.height / 2;
@@ -1047,21 +1055,27 @@
static int tvp5150_s_stream(struct v4l2_subdev *sd, int enable)
{
struct tvp5150 *decoder = to_tvp5150(sd);
- /* Output format: 8-bit ITU-R BT.656 with embedded syncs */
- int val = 0x09;
+ int val;
- /* Output format: 8-bit 4:2:2 YUV with discrete sync */
- if (decoder->mbus_type == V4L2_MBUS_PARALLEL)
- val = 0x0d;
+ /* Enable or disable the video output signals. */
+ val = tvp5150_read(sd, TVP5150_MISC_CTL);
+ if (val < 0)
+ return val;
- /* Initializes TVP5150 to its default values */
- /* # set PCLK (27MHz) */
- tvp5150_write(sd, TVP5150_CONF_SHARED_PIN, 0x00);
+ val &= ~(TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_SYNC_OE |
+ TVP5150_MISC_CTL_CLOCK_OE);
- if (enable)
- tvp5150_write(sd, TVP5150_MISC_CTL, val);
- else
- tvp5150_write(sd, TVP5150_MISC_CTL, 0x00);
+ if (enable) {
+ /*
+ * Enable the YCbCr and clock outputs. In discrete sync mode
+ * (non-BT.656) additionally enable the the sync outputs.
+ */
+ val |= TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_CLOCK_OE;
+ if (decoder->mbus_type == V4L2_MBUS_PARALLEL)
+ val |= TVP5150_MISC_CTL_SYNC_OE;
+ }
+
+ tvp5150_write(sd, TVP5150_MISC_CTL, val);
return 0;
}
@@ -1520,7 +1534,6 @@
res = core->hdl.error;
goto err;
}
- v4l2_ctrl_handler_setup(&core->hdl);
/* Default is no cropping */
core->rect.top = 0;
@@ -1531,6 +1544,8 @@
core->rect.left = 0;
core->rect.width = TVP5150_H_MAX;
+ tvp5150_reset(sd, 0); /* Calls v4l2_ctrl_handler_setup() */
+
res = v4l2_async_register_subdev(sd);
if (res < 0)
goto err;
diff --git a/drivers/media/i2c/tvp5150_reg.h b/drivers/media/i2c/tvp5150_reg.h
index 25a9949..30a48c2 100644
--- a/drivers/media/i2c/tvp5150_reg.h
+++ b/drivers/media/i2c/tvp5150_reg.h
@@ -9,6 +9,15 @@
#define TVP5150_ANAL_CHL_CTL 0x01 /* Analog channel controls */
#define TVP5150_OP_MODE_CTL 0x02 /* Operation mode controls */
#define TVP5150_MISC_CTL 0x03 /* Miscellaneous controls */
+#define TVP5150_MISC_CTL_VBLK_GPCL BIT(7)
+#define TVP5150_MISC_CTL_GPCL BIT(6)
+#define TVP5150_MISC_CTL_INTREQ_OE BIT(5)
+#define TVP5150_MISC_CTL_HVLK BIT(4)
+#define TVP5150_MISC_CTL_YCBCR_OE BIT(3)
+#define TVP5150_MISC_CTL_SYNC_OE BIT(2)
+#define TVP5150_MISC_CTL_VBLANK BIT(1)
+#define TVP5150_MISC_CTL_CLOCK_OE BIT(0)
+
#define TVP5150_AUTOSW_MSK 0x04 /* Autoswitch mask: TVP5150A / TVP5150AM */
/* Reserved 05h */
diff --git a/drivers/media/pci/solo6x10/solo6x10.h b/drivers/media/pci/solo6x10/solo6x10.h
index 5bd4987..3f8da5e 100644
--- a/drivers/media/pci/solo6x10/solo6x10.h
+++ b/drivers/media/pci/solo6x10/solo6x10.h
@@ -284,7 +284,10 @@
static inline void solo_reg_write(struct solo_dev *solo_dev, int reg,
u32 data)
{
+ u16 val;
+
writel(data, solo_dev->reg_base + reg);
+ pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val);
}
static inline void solo_irq_on(struct solo_dev *dev, u32 mask)
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 02dd73a..493035b 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -93,7 +93,7 @@
config VIDEO_PXA27x
tristate "PXA27x Quick Capture Interface driver"
- depends on VIDEO_DEV && HAS_DMA
+ depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
depends on PXA27x || COMPILE_TEST
select VIDEOBUF2_DMA_SG
select SG_SPLIT
diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c
index cff63e5..b8f3d9f 100644
--- a/drivers/media/platform/blackfin/ppi.c
+++ b/drivers/media/platform/blackfin/ppi.c
@@ -214,6 +214,8 @@
if (params->dlen > 24 || params->dlen <= 0)
return -EINVAL;
pctrl = devm_pinctrl_get(ppi->dev);
+ if (IS_ERR(pctrl))
+ return PTR_ERR(pctrl);
pstate = pinctrl_lookup_state(pctrl,
pin_state[(params->dlen + 7) / 8 - 1]);
if (pinctrl_select_state(pctrl, pstate))
diff --git a/drivers/media/platform/msm/Kconfig b/drivers/media/platform/msm/Kconfig
index 19567ad..d5d873c 100644
--- a/drivers/media/platform/msm/Kconfig
+++ b/drivers/media/platform/msm/Kconfig
@@ -14,3 +14,5 @@
IFE and postprocessing drivers.
source "drivers/media/platform/msm/vidc/Kconfig"
+
+source "drivers/media/platform/msm/sde/Kconfig"
diff --git a/drivers/media/platform/msm/Makefile b/drivers/media/platform/msm/Makefile
index 7605fdb..523a8b4 100644
--- a/drivers/media/platform/msm/Makefile
+++ b/drivers/media/platform/msm/Makefile
@@ -1,5 +1,6 @@
#
-# Makefile for the QCOM specific video device drivers
+# Makefile for the qti specific video device drivers
# based on V4L2.
#
obj-$(CONFIG_MSM_VIDC_V4L2) += vidc/
+obj-y += sde/
diff --git a/drivers/media/platform/msm/sde/Kconfig b/drivers/media/platform/msm/sde/Kconfig
index 3795972..85f5f42 100644
--- a/drivers/media/platform/msm/sde/Kconfig
+++ b/drivers/media/platform/msm/sde/Kconfig
@@ -1,8 +1,17 @@
config MSM_SDE_ROTATOR
bool "QTI V4L2 based SDE Rotator"
- depends on ARCH_MSM && VIDEO_V4L2
+ depends on ARCH_QCOM && VIDEO_V4L2
select V4L2_MEM2MEM_DEV
select VIDEOBUF2_CORE
select SW_SYNC if SYNC
---help---
- Enable support of V4L2 rotator driver.
\ No newline at end of file
+ Enable support of V4L2 rotator driver.
+
+config MSM_SDE_ROTATOR_EVTLOG_DEBUG
+ depends on MSM_SDE_ROTATOR
+ bool "Enable sde rotator debugging"
+ ---help---
+ The SDE rotator debugging provides support to enable rotator debugging
+ features to: Dump rotator registers during driver errors, panic
+ driver during fatal errors and enable some rotator driver logging
+ into an internal buffer (this avoids logging overhead).
diff --git a/drivers/media/platform/msm/sde/rotator/Makefile b/drivers/media/platform/msm/sde/rotator/Makefile
index 8793b1e..dd496e0 100644
--- a/drivers/media/platform/msm/sde/rotator/Makefile
+++ b/drivers/media/platform/msm/sde/rotator/Makefile
@@ -1,4 +1,4 @@
-ccflags-y += -I$(src)
+ccflags-y += -I$(src) -Idrivers/staging/android
obj-y := \
sde_rotator_dev.o \
@@ -15,9 +15,13 @@
sde_rotator_r1_ctl.o \
sde_rotator_r1.o
+obj-y += \
+ sde_rotator_r3.o
+
obj-$(CONFIG_SYNC) += \
sde_rotator_sync.o
obj-$(CONFIG_DEBUG_FS) += \
sde_rotator_debug.o \
- sde_rotator_r1_debug.o
\ No newline at end of file
+ sde_rotator_r1_debug.o \
+ sde_rotator_r3_debug.o
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
index 0665f46..34243e6 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -30,6 +30,7 @@
#include "sde_rotator_base.h"
#include "sde_rotator_util.h"
#include "sde_rotator_trace.h"
+#include "sde_rotator_debug.h"
static inline u64 fudge_factor(u64 val, u32 numer, u32 denom)
{
@@ -138,47 +139,16 @@
return clk_forced_on;
}
-static void apply_dynamic_ot_limit(u32 *ot_lim,
- struct sde_mdp_set_ot_params *params)
+u32 sde_mdp_get_ot_limit(u32 width, u32 height, u32 pixfmt, u32 fps, u32 is_rd)
{
struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ struct sde_mdp_format_params *fmt;
+ u32 ot_lim;
+ u32 is_yuv;
u32 res;
- if (false == test_bit(SDE_QOS_OTLIM, mdata->sde_qos_map))
- return;
-
- res = params->width * params->height;
-
- SDEROT_DBG("w:%d h:%d rot:%d yuv:%d wb:%d res:%d\n",
- params->width, params->height, params->is_rot,
- params->is_yuv, params->is_wb, res);
-
- if ((params->is_rot && params->is_yuv) ||
- params->is_wb) {
- if (res <= RES_1080p) {
- *ot_lim = 2;
- } else if (res <= RES_UHD) {
- if (params->is_rot && params->is_yuv)
- *ot_lim = 8;
- else
- *ot_lim = 16;
- }
- }
-}
-
-static u32 get_ot_limit(u32 reg_off, u32 bit_off,
- struct sde_mdp_set_ot_params *params)
-{
- struct sde_rot_data_type *mdata = sde_rot_get_mdata();
- u32 ot_lim = 0;
- u32 val;
-
- if (mdata->default_ot_wr_limit &&
- (params->reg_off_vbif_lim_conf == MMSS_VBIF_WR_LIM_CONF))
- ot_lim = mdata->default_ot_wr_limit;
- else if (mdata->default_ot_rd_limit &&
- (params->reg_off_vbif_lim_conf == MMSS_VBIF_RD_LIM_CONF))
- ot_lim = mdata->default_ot_rd_limit;
+ ot_lim = (is_rd) ? mdata->default_ot_rd_limit :
+ mdata->default_ot_wr_limit;
/*
* If default ot is not set from dt,
@@ -188,7 +158,56 @@
goto exit;
/* Modify the limits if the target and the use case requires it */
- apply_dynamic_ot_limit(&ot_lim, params);
+ if (false == test_bit(SDE_QOS_OTLIM, mdata->sde_qos_map))
+ goto exit;
+
+ res = width * height;
+
+ fmt = sde_get_format_params(pixfmt);
+
+ if (!fmt) {
+ SDEROT_WARN("invalid format %8.8x\n", pixfmt);
+ goto exit;
+ }
+
+ is_yuv = sde_mdp_is_yuv_format(fmt);
+
+ SDEROT_DBG("w:%d h:%d fps:%d pixfmt:%8.8x yuv:%d res:%d rd:%d\n",
+ width, height, fps, pixfmt, is_yuv, res, is_rd);
+
+ if (!is_yuv)
+ goto exit;
+
+ if ((res <= RES_1080p) && (fps <= 30))
+ ot_lim = 2;
+ else if ((res <= RES_1080p) && (fps <= 60))
+ ot_lim = 4;
+ else if ((res <= RES_UHD) && (fps <= 30))
+ ot_lim = 8;
+
+exit:
+ SDEROT_DBG("ot_lim=%d\n", ot_lim);
+ return ot_lim;
+}
+
+static u32 get_ot_limit(u32 reg_off, u32 bit_off,
+ struct sde_mdp_set_ot_params *params)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ u32 ot_lim;
+ u32 val;
+
+ ot_lim = sde_mdp_get_ot_limit(
+ params->width, params->height,
+ params->fmt, params->fps,
+ params->reg_off_vbif_lim_conf == MMSS_VBIF_RD_LIM_CONF);
+
+ /*
+ * If default ot is not set from dt,
+ * then do not configure it.
+ */
+ if (ot_lim == 0)
+ goto exit;
val = SDE_VBIF_READ(mdata, reg_off);
val &= (0xFF << bit_off);
@@ -199,6 +218,8 @@
exit:
SDEROT_DBG("ot_lim=%d\n", ot_lim);
+ SDEROT_EVTLOG(params->width, params->height, params->fmt, params->fps,
+ ot_lim);
return ot_lim;
}
@@ -210,6 +231,7 @@
params->reg_off_vbif_lim_conf;
u32 bit_off_vbif_lim_conf = (params->xin_id % 4) * 8;
u32 reg_val;
+ u32 sts;
bool forced_on;
ot_lim = get_ot_limit(
@@ -220,6 +242,16 @@
if (ot_lim == 0)
goto exit;
+ if (params->rotsts_base && params->rotsts_busy_mask) {
+ sts = readl_relaxed(params->rotsts_base);
+ if (sts & params->rotsts_busy_mask) {
+ SDEROT_ERR(
+ "Rotator still busy, should not modify VBIF\n");
+ SDEROT_EVTLOG_TOUT_HANDLER(
+ "rot", "vbif_dbg_bus", "panic");
+ }
+ }
+
trace_rot_perf_set_ot(params->num, params->xin_id, ot_lim);
forced_on = force_on_xin_clk(params->bit_off_mdp_clk_ctrl,
@@ -245,6 +277,7 @@
force_on_xin_clk(params->bit_off_mdp_clk_ctrl,
params->reg_off_mdp_clk_ctrl, false);
+ SDEROT_EVTLOG(params->num, params->xin_id, ot_lim);
exit:
return;
}
@@ -542,6 +575,15 @@
goto probe_done;
}
+ mdata->iclient = msm_ion_client_create(mdata->pdev->name);
+ if (IS_ERR_OR_NULL(mdata->iclient)) {
+ SDEROT_ERR("msm_ion_client_create() return error (%pK)\n",
+ mdata->iclient);
+ mdata->iclient = NULL;
+ rc = -EFAULT;
+ goto probe_done;
+ }
+
*pmdata = mdata;
return 0;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
index af45f8e..a7c1e89 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -34,12 +34,13 @@
u32 num;
u32 width;
u32 height;
- bool is_rot;
- bool is_wb;
- bool is_yuv;
+ u32 fps;
+ u32 fmt;
u32 reg_off_vbif_lim_conf;
u32 reg_off_mdp_clk_ctrl;
u32 bit_off_mdp_clk_ctrl;
+ char __iomem *rotsts_base;
+ u32 rotsts_busy_mask;
};
enum sde_bus_vote_type {
@@ -63,9 +64,28 @@
SDE_QOS_MAX,
};
+/**
+ * enum sde_rot_type: SDE rotator HW version
+ * @SDE_ROT_TYPE_V1_0: V1.0 HW version
+ * @SDE_ROT_TYPE_V1_1: V1.1 HW version
+ */
+enum sde_rot_type {
+ SDE_ROT_TYPE_V1_0 = 0x10000000,
+ SDE_ROT_TYPE_V1_1 = 0x10010000,
+ SDE_ROT_TYPE_MAX,
+};
+
+/**
+ * enum sde_caps_settings: SDE rotator capability definition
+ * @SDE_CAPS_R1_WB: MDSS V1.x WB block
+ * @SDE_CAPS_R3_WB: MDSS V3.x WB block
+ * @SDE_CAPS_R3_1P5_DOWNSCALE: 1.5x downscale rotator support
+ */
enum sde_caps_settings {
SDE_CAPS_R1_WB,
SDE_CAPS_R3_WB,
+ SDE_CAPS_R3_1P5_DOWNSCALE,
+ SDE_CAPS_SEC_ATTACH_DETACH_SMMU,
SDE_CAPS_MAX,
};
@@ -75,6 +95,13 @@
SDE_MAX_BUS_CLIENTS
};
+enum sde_rot_regdump_access {
+ SDE_ROT_REGDUMP_READ,
+ SDE_ROT_REGDUMP_WRITE,
+ SDE_ROT_REGDUMP_VBIF,
+ SDE_ROT_REGDUMP_MAX
+};
+
struct reg_bus_client {
char name[MAX_CLIENT_NAME_LEN];
short usecase_ndx;
@@ -88,6 +115,22 @@
struct sde_module_power mp;
struct reg_bus_client *reg_bus_clt;
bool domain_attached;
+ int domain;
+};
+
+struct sde_rot_vbif_debug_bus {
+ u32 disable_bus_addr;
+ u32 block_bus_addr;
+ u32 bit_offset;
+ u32 block_cnt;
+ u32 test_pnt_cnt;
+};
+
+struct sde_rot_regdump {
+ char *name;
+ u32 offset;
+ u32 len;
+ enum sde_rot_regdump_access access;
};
struct sde_rot_data_type {
@@ -123,6 +166,17 @@
int iommu_attached;
int iommu_ref_cnt;
+
+ struct sde_rot_vbif_debug_bus *nrt_vbif_dbg_bus;
+ u32 nrt_vbif_dbg_bus_size;
+
+ struct sde_rot_regdump *regdump;
+ u32 regdump_size;
+
+ void *sde_rot_hw;
+ int sec_cam_en;
+
+ struct ion_client *iclient;
};
int sde_rotator_base_init(struct sde_rot_data_type **pmdata,
@@ -143,6 +197,8 @@
struct sde_mdp_format_params *fmt,
struct sde_mult_factor *factor);
+u32 sde_mdp_get_ot_limit(u32 width, u32 height, u32 pixfmt, u32 fps, u32 is_rd);
+
void sde_mdp_set_ot_limit(struct sde_mdp_set_ot_params *params);
#define SDE_VBIF_WRITE(mdata, offset, value) \
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
index c2d586d..e29c552 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -25,6 +25,10 @@
#include <linux/msm-bus-board.h>
#include <linux/regulator/consumer.h>
#include <linux/dma-direction.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/rpm-smd.h>
+#include <soc/qcom/secure_buffer.h>
+#include <asm/cacheflush.h>
#include "sde_rotator_base.h"
#include "sde_rotator_core.h"
@@ -33,14 +37,32 @@
#include "sde_rotator_io_util.h"
#include "sde_rotator_smmu.h"
#include "sde_rotator_r1.h"
+#include "sde_rotator_r3.h"
#include "sde_rotator_trace.h"
+#include "sde_rotator_debug.h"
+
+
+/* Rotator device id to be used in SCM call */
+#define SDE_ROTATOR_DEVICE 21
+
+/* SCM call function id to be used for switching between secure and non
+ * secure context
+ */
+#define MEM_PROTECT_SD_CTRL_SWITCH 0x18
+
+/* Rotator secure SID */
+#define SDE_ROTATOR_SECURE_SID 0xe01
/* waiting for hw time out, 3 vsync for 30fps*/
#define ROT_HW_ACQUIRE_TIMEOUT_IN_MS 100
/* default pixel per clock ratio */
-#define ROT_PIXEL_PER_CLK_NUMERATOR 4
-#define ROT_PIXEL_PER_CLK_DENOMINATOR 1
+#define ROT_PIXEL_PER_CLK_NUMERATOR 36
+#define ROT_PIXEL_PER_CLK_DENOMINATOR 10
+#define ROT_FUDGE_FACTOR_NUMERATOR 105
+#define ROT_FUDGE_FACTOR_DENOMINATOR 100
+#define ROT_OVERHEAD_NUMERATOR 27
+#define ROT_OVERHEAD_DENOMINATOR 10000
/*
* Max rotator hw blocks possible. Used for upper array limits instead of
@@ -126,6 +148,7 @@
bus->curr_bw_uc_idx = new_uc_idx;
bus->curr_quota_val = quota;
+ SDEROT_EVTLOG(new_uc_idx, quota);
SDEROT_DBG("uc_idx=%d quota=%llu\n", new_uc_idx, quota);
ATRACE_BEGIN("msm_bus_scale_req_rot");
ret = msm_bus_scale_client_update_request(bus->bus_hdl,
@@ -230,7 +253,7 @@
clk_rate = clk_round_rate(clk, rate);
if (IS_ERR_VALUE(clk_rate)) {
SDEROT_ERR("unable to round rate err=%ld\n", clk_rate);
- } else if (clk_rate != clk_get_rate(clk)) {
+ } else {
ret = clk_set_rate(clk, clk_rate);
if (IS_ERR_VALUE(ret))
SDEROT_ERR("clk_set_rate failed, err:%d\n",
@@ -259,7 +282,7 @@
SDEROT_DBG("core_clk %lu\n", total_clk_rate);
ATRACE_INT("core_clk", total_clk_rate);
- sde_rotator_set_clk_rate(mgr, total_clk_rate, mgr->core_clk_idx);
+ sde_rotator_set_clk_rate(mgr, total_clk_rate, SDE_ROTATOR_CLK_ROT_CORE);
return 0;
}
@@ -273,7 +296,12 @@
return;
}
+ SDEROT_EVTLOG(on);
SDEROT_DBG("%s: rotator regulators", on ? "Enable" : "Disable");
+
+ if (mgr->ops_hw_pre_pmevent)
+ mgr->ops_hw_pre_pmevent(mgr, on);
+
ret = sde_rot_enable_vreg(mgr->module_power.vreg_config,
mgr->module_power.num_vreg, on);
if (ret) {
@@ -282,14 +310,40 @@
return;
}
+ if (mgr->ops_hw_post_pmevent)
+ mgr->ops_hw_post_pmevent(mgr, on);
+
mgr->regulator_enable = on;
}
-static int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable)
+static int sde_rotator_enable_clk(struct sde_rot_mgr *mgr, int clk_idx)
{
struct clk *clk;
int ret = 0;
- int i, changed = 0;
+
+ clk = sde_rotator_get_clk(mgr, clk_idx);
+ if (clk) {
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ SDEROT_ERR("enable failed clk_idx %d\n", clk_idx);
+ }
+
+ return ret;
+}
+
+static void sde_rotator_disable_clk(struct sde_rot_mgr *mgr, int clk_idx)
+{
+ struct clk *clk;
+
+ clk = sde_rotator_get_clk(mgr, clk_idx);
+ if (clk)
+ clk_disable_unprepare(clk);
+}
+
+int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable)
+{
+ int ret = 0;
+ int changed = 0;
if (enable) {
if (mgr->rot_enable_clk_cnt == 0)
@@ -306,33 +360,43 @@
}
if (changed) {
+ SDEROT_EVTLOG(enable);
SDEROT_DBG("Rotator clk %s\n", enable ? "enable" : "disable");
- for (i = 0; i < mgr->num_rot_clk; i++) {
- clk = mgr->rot_clk[i].clk;
-
- if (!clk)
- continue;
-
- if (enable) {
- ret = clk_prepare_enable(clk);
- if (ret) {
- SDEROT_ERR(
- "enable failed clk_idx %d\n",
- i);
- goto error;
- }
- } else {
- clk_disable_unprepare(clk);
- }
- }
if (enable) {
+ ret = sde_rotator_enable_clk(mgr,
+ SDE_ROTATOR_CLK_MNOC_AHB);
+ if (ret)
+ goto error_mnoc_ahb;
+ ret = sde_rotator_enable_clk(mgr,
+ SDE_ROTATOR_CLK_MDSS_AHB);
+ if (ret)
+ goto error_mdss_ahb;
+ ret = sde_rotator_enable_clk(mgr,
+ SDE_ROTATOR_CLK_MDSS_AXI);
+ if (ret)
+ goto error_mdss_axi;
+ ret = sde_rotator_enable_clk(mgr,
+ SDE_ROTATOR_CLK_ROT_CORE);
+ if (ret)
+ goto error_rot_core;
+ ret = sde_rotator_enable_clk(mgr,
+ SDE_ROTATOR_CLK_MDSS_ROT);
+ if (ret)
+ goto error_mdss_rot;
+
/* Active+Sleep */
msm_bus_scale_client_update_context(
mgr->data_bus.bus_hdl, false,
mgr->data_bus.curr_bw_uc_idx);
trace_rot_bw_ao_as_context(0);
} else {
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MDSS_ROT);
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_ROT_CORE);
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MDSS_AXI);
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MDSS_AHB);
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MNOC_AHB);
+
/* Active Only */
msm_bus_scale_client_update_context(
mgr->data_bus.bus_hdl, true,
@@ -342,9 +406,15 @@
}
return ret;
-error:
- for (i--; i >= 0; i--)
- clk_disable_unprepare(mgr->rot_clk[i].clk);
+error_mdss_rot:
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_ROT_CORE);
+error_rot_core:
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MDSS_AXI);
+error_mdss_axi:
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MDSS_AHB);
+error_mdss_ahb:
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MNOC_AHB);
+error_mnoc_ahb:
return ret;
}
@@ -393,6 +463,7 @@
static void sde_rotator_clear_fence(struct sde_rot_entry *entry)
{
if (entry->input_fence) {
+ SDEROT_EVTLOG(entry->input_fence, 1111);
SDEROT_DBG("sys_fence_put i:%p\n", entry->input_fence);
sde_rotator_put_sync_fence(entry->input_fence);
entry->input_fence = NULL;
@@ -403,6 +474,7 @@
if (entry->fenceq && entry->fenceq->timeline)
sde_rotator_resync_timeline(entry->fenceq->timeline);
+ SDEROT_EVTLOG(entry->output_fence, 2222);
SDEROT_DBG("sys_fence_put o:%p\n", entry->output_fence);
sde_rotator_put_sync_fence(entry->output_fence);
entry->output_fence = NULL;
@@ -449,6 +521,7 @@
planes[i].memory_id = buffer->planes[i].fd;
planes[i].offset = buffer->planes[i].offset;
planes[i].buffer = buffer->planes[i].buffer;
+ planes[i].handle = buffer->planes[i].handle;
}
ret = sde_mdp_data_get_and_validate_size(data, planes,
@@ -457,6 +530,80 @@
return ret;
}
+static int sde_rotator_secure_session_ctrl(bool enable)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ uint32_t sid_info;
+ struct scm_desc desc;
+ unsigned int resp = 0;
+ int ret = 0;
+
+ if (test_bit(SDE_CAPS_SEC_ATTACH_DETACH_SMMU,
+ mdata->sde_caps_map)) {
+ sid_info = SDE_ROTATOR_SECURE_SID;
+ desc.arginfo = SCM_ARGS(4, SCM_VAL, SCM_RW, SCM_VAL, SCM_VAL);
+ desc.args[0] = SDE_ROTATOR_DEVICE;
+ desc.args[1] = SCM_BUFFER_PHYS(&sid_info);
+ desc.args[2] = sizeof(uint32_t);
+
+ if (!mdata->sec_cam_en && enable) {
+ /*
+ * Enable secure camera operation
+ * Send SCM call to hypervisor to switch the
+ * secure_vmid to secure context
+ */
+ desc.args[3] = VMID_CP_CAMERA_PREVIEW;
+
+ mdata->sec_cam_en = 1;
+ sde_smmu_secure_ctrl(0);
+
+ dmac_flush_range(&sid_info, &sid_info + 1);
+ ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
+ MEM_PROTECT_SD_CTRL_SWITCH), &desc);
+ resp = desc.ret[0];
+ if (ret) {
+ SDEROT_ERR("scm_call(1) ret=%d, resp=%x\n",
+ ret, resp);
+ /* failure, attach smmu */
+ mdata->sec_cam_en = 0;
+ sde_smmu_secure_ctrl(1);
+ return -EINVAL;
+ }
+
+ SDEROT_DBG("scm_call(1) ret=%d, resp=%x",
+ ret, resp);
+ SDEROT_EVTLOG(1);
+ } else if (mdata->sec_cam_en && !enable) {
+ /*
+ * Disable secure camera operation
+ * Send SCM call to hypervisor to switch the
+ * secure_vmid to non-secure context
+ */
+ desc.args[3] = VMID_CP_PIXEL;
+ mdata->sec_cam_en = 0;
+
+ dmac_flush_range(&sid_info, &sid_info + 1);
+ ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
+ MEM_PROTECT_SD_CTRL_SWITCH), &desc);
+ resp = desc.ret[0];
+
+ SDEROT_DBG("scm_call(0): ret=%d, resp=%x",
+ ret, resp);
+
+ /* force smmu to reattach */
+ sde_smmu_secure_ctrl(1);
+ SDEROT_EVTLOG(0);
+ }
+ } else {
+ return 0;
+ }
+ if (ret)
+ return ret;
+
+ return resp;
+}
+
+
static int sde_rotator_map_and_check_data(struct sde_rot_entry *entry)
{
int ret;
@@ -465,6 +612,7 @@
struct sde_mdp_format_params *fmt;
struct sde_mdp_plane_sizes ps;
bool rotation;
+ bool secure;
input = &entry->item.input;
output = &entry->item.output;
@@ -475,6 +623,15 @@
if (IS_ERR_VALUE(ret))
return ret;
+ secure = (entry->item.flags & SDE_ROTATION_SECURE_CAMERA) ?
+ true : false;
+ ret = sde_rotator_secure_session_ctrl(secure);
+ if (ret) {
+ SDEROT_ERR("failed secure session enabling/disabling %d\n",
+ ret);
+ goto end;
+ }
+
/* if error during map, the caller will release the data */
ret = sde_mdp_data_map(&entry->src_buf, true, DMA_TO_DEVICE);
if (ret) {
@@ -564,6 +721,7 @@
static void sde_rotator_release_data(struct sde_rot_entry *entry)
{
+ SDEROT_EVTLOG(entry->src_buf.p[0].addr, entry->dst_buf.p[0].addr);
sde_mdp_data_free(&entry->src_buf, true, DMA_TO_DEVICE);
sde_mdp_data_free(&entry->dst_buf, true, DMA_FROM_DEVICE);
}
@@ -585,6 +743,9 @@
if (entry->item.flags & SDE_ROTATION_EXT_DMA_BUF)
flag |= SDE_ROT_EXT_DMA_BUF;
+ if (entry->item.flags & SDE_ROTATION_SECURE_CAMERA)
+ flag |= SDE_SECURE_CAMERA_SESSION;
+
ret = sde_rotator_import_buffer(input, &entry->src_buf, flag,
&mgr->pdev->dev, true);
if (ret) {
@@ -606,59 +767,171 @@
return ret;
}
+/*
+ * sde_rotator_require_reconfiguration - check if reconfiguration is required
+ * @mgr: Pointer to rotator manager
+ * @hw: Pointer to rotator hw resource
+ * @entry: Pointer to next rotation entry
+ *
+ * Parameters are validated by caller.
+ */
+static int sde_rotator_require_reconfiguration(struct sde_rot_mgr *mgr,
+ struct sde_rot_hw_resource *hw, struct sde_rot_entry *entry)
+{
+ /* OT setting change may impact queued entries */
+ if (entry->perf && (entry->perf->rdot_limit != mgr->rdot_limit ||
+ entry->perf->wrot_limit != mgr->wrot_limit))
+ return true;
+
+ return false;
+}
+
+/*
+ * sde_rotator_is_hw_idle - check if hw block is not processing request
+ * @mgr: Pointer to rotator manager
+ * @hw: Pointer to rotator hw resource
+ *
+ * Parameters are validated by caller.
+ */
+static int sde_rotator_is_hw_idle(struct sde_rot_mgr *mgr,
+ struct sde_rot_hw_resource *hw)
+{
+ int i;
+
+ /*
+ * Wait until all queues are idle in order to update global
+ * setting such as VBIF QoS. This check can be relaxed if global
+ * settings can be updated individually by entries already
+ * queued in hw queue, i.e. REGDMA can update VBIF directly.
+ */
+ for (i = 0; i < mgr->queue_count; i++) {
+ struct sde_rot_hw_resource *hw_res = mgr->commitq[i].hw;
+
+ if (hw_res && atomic_read(&hw_res->num_active))
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * sde_rotator_is_hw_available - check if hw is available for the given entry
+ * @mgr: Pointer to rotator manager
+ * @hw: Pointer to rotator hw resource
+ * @entry: Pointer to rotation entry
+ *
+ * Parameters are validated by caller.
+ */
+static int sde_rotator_is_hw_available(struct sde_rot_mgr *mgr,
+ struct sde_rot_hw_resource *hw, struct sde_rot_entry *entry)
+{
+ /*
+ * Wait until hw is idle if reconfiguration is required; otherwise,
+ * wait until free queue entry is available
+ */
+ if (sde_rotator_require_reconfiguration(mgr, hw, entry)) {
+ SDEROT_DBG(
+ "wait4idle active=%d pending=%d rdot:%u/%u wrot:%u/%u s:%d.%d\n",
+ atomic_read(&hw->num_active), hw->pending_count,
+ mgr->rdot_limit, entry->perf->rdot_limit,
+ mgr->wrot_limit, entry->perf->wrot_limit,
+ entry->item.session_id,
+ entry->item.sequence_id);
+ return sde_rotator_is_hw_idle(mgr, hw);
+ } else {
+ return (atomic_read(&hw->num_active) < hw->max_active);
+ }
+}
+
+/*
+ * sde_rotator_get_hw_resource - block waiting for hw availability or timeout
+ * @queue: Pointer to rotator queue
+ * @entry: Pointer to rotation entry
+ */
static struct sde_rot_hw_resource *sde_rotator_get_hw_resource(
struct sde_rot_queue *queue, struct sde_rot_entry *entry)
{
- struct sde_rot_hw_resource *hw = queue->hw;
+ struct sde_rot_hw_resource *hw;
struct sde_rot_mgr *mgr;
int ret;
- if (!hw) {
- SDEROT_ERR("no hw in the queue\n");
+ if (!queue || !entry || !queue->hw) {
+ SDEROT_ERR("null parameters\n");
return NULL;
}
+ hw = queue->hw;
mgr = entry->private->mgr;
WARN_ON(atomic_read(&hw->num_active) > hw->max_active);
- while (atomic_read(&hw->num_active) >= hw->max_active) {
- sde_rot_mgr_unlock(entry->private->mgr);
+ while (!sde_rotator_is_hw_available(mgr, hw, entry)) {
+ sde_rot_mgr_unlock(mgr);
ret = wait_event_timeout(hw->wait_queue,
- (atomic_read(&hw->num_active) < hw->max_active),
+ sde_rotator_is_hw_available(mgr, hw, entry),
msecs_to_jiffies(mgr->hwacquire_timeout));
- sde_rot_mgr_lock(entry->private->mgr);
+ sde_rot_mgr_lock(mgr);
if (!ret) {
SDEROT_ERR(
"timeout waiting for hw resource, a:%d p:%d\n",
atomic_read(&hw->num_active),
hw->pending_count);
- if (hw->workload)
- SDEROT_ERR("possible faulty workload s:%d.%d\n",
- hw->workload->item.session_id,
- hw->workload->item.sequence_id);
return NULL;
}
}
atomic_inc(&hw->num_active);
- SDEROT_DBG("active=%d pending=%d s:%d.%d\n",
- atomic_read(&hw->num_active), hw->pending_count,
+ SDEROT_EVTLOG(atomic_read(&hw->num_active), hw->pending_count,
+ mgr->rdot_limit, entry->perf->rdot_limit,
+ mgr->wrot_limit, entry->perf->wrot_limit,
entry->item.session_id, entry->item.sequence_id);
- hw->workload = entry;
+ SDEROT_DBG("active=%d pending=%d rdot=%u/%u wrot=%u/%u s:%d.%d\n",
+ atomic_read(&hw->num_active), hw->pending_count,
+ mgr->rdot_limit, entry->perf->rdot_limit,
+ mgr->wrot_limit, entry->perf->wrot_limit,
+ entry->item.session_id, entry->item.sequence_id);
+ mgr->rdot_limit = entry->perf->rdot_limit;
+ mgr->wrot_limit = entry->perf->wrot_limit;
return hw;
}
+/*
+ * sde_rotator_put_hw_resource - return hw resource and wake up waiting clients
+ * @queue: Pointer to rotator queue
+ * @entry: Pointer to rotation entry
+ * @hw: Pointer to hw resource to be returned
+ */
static void sde_rotator_put_hw_resource(struct sde_rot_queue *queue,
- struct sde_rot_hw_resource *hw)
+ struct sde_rot_entry *entry, struct sde_rot_hw_resource *hw)
{
- struct sde_rot_entry *entry = hw->workload;
+ struct sde_rot_mgr *mgr;
+ int i;
+
+ if (!queue || !entry || !hw) {
+ SDEROT_ERR("null parameters\n");
+ return;
+ }
+
+ mgr = entry->private->mgr;
WARN_ON(atomic_read(&hw->num_active) < 1);
- hw->workload = NULL;
if (!atomic_add_unless(&hw->num_active, -1, 0))
SDEROT_ERR("underflow active=%d pending=%d s:%d.%d\n",
atomic_read(&hw->num_active), hw->pending_count,
entry->item.session_id, entry->item.sequence_id);
- wake_up(&hw->wait_queue);
+ /*
+ * Wake up all queues in case any entry is waiting for hw idle,
+ * in order to update global settings, such as VBIF QoS.
+ * This can be relaxed to the given hw resource if global
+ * settings can be updated individually by entries already
+ * queued in hw queue.
+ */
+ for (i = 0; i < mgr->queue_count; i++) {
+ struct sde_rot_hw_resource *hw_res = mgr->commitq[i].hw;
+
+ if (hw_res)
+ wake_up(&hw_res->wait_queue);
+ }
+ SDEROT_EVTLOG(atomic_read(&hw->num_active), hw->pending_count,
+ entry->item.session_id, entry->item.sequence_id);
SDEROT_DBG("active=%d pending=%d s:%d.%d\n",
atomic_read(&hw->num_active), hw->pending_count,
entry->item.session_id, entry->item.sequence_id);
@@ -864,19 +1137,46 @@
u32 bw;
bw = width * height * frame_rate;
- if (fmt->chroma_sample == SDE_MDP_CHROMA_420)
+
+ if (sde_mdp_is_tp10_format(fmt))
+ bw *= 2;
+ else if (sde_mdp_is_p010_format(fmt))
+ bw *= 3;
+ else if (fmt->chroma_sample == SDE_MDP_CHROMA_420)
bw = (bw * 3) / 2;
else
bw *= fmt->bpp;
+ SDEROT_EVTLOG(bw, width, height, frame_rate, fmt->format);
return bw;
}
+static int sde_rotator_find_max_fps(struct sde_rot_mgr *mgr)
+{
+ struct sde_rot_file_private *priv;
+ struct sde_rot_perf *perf;
+ int max_fps = 0;
+
+ list_for_each_entry(priv, &mgr->file_list, list) {
+ list_for_each_entry(perf, &priv->perf_list, list) {
+ if (perf->config.frame_rate > max_fps)
+ max_fps = perf->config.frame_rate;
+ }
+ }
+
+ SDEROT_DBG("Max fps:%d\n", max_fps);
+ return max_fps;
+}
+
static int sde_rotator_calc_perf(struct sde_rot_mgr *mgr,
struct sde_rot_perf *perf)
{
struct sde_rotation_config *config = &perf->config;
u32 read_bw, write_bw;
struct sde_mdp_format_params *in_fmt, *out_fmt;
+ struct sde_rotator_device *rot_dev;
+ int max_fps;
+
+ rot_dev = platform_get_drvdata(mgr->pdev);
in_fmt = sde_get_format_params(config->input.format);
if (!in_fmt) {
@@ -889,17 +1189,44 @@
return -EINVAL;
}
+ /*
+ * rotator processes 4 pixels per clock, but the actual throughtput
+ * is 3.6. We also need to take into account for overhead time. Final
+ * equation is:
+ * W x H / throughput / (1/fps - overhead) * fudge_factor
+ */
+ max_fps = sde_rotator_find_max_fps(mgr);
perf->clk_rate = config->input.width * config->input.height;
- perf->clk_rate *= config->frame_rate;
- /* rotator processes 4 pixels per clock */
perf->clk_rate = (perf->clk_rate * mgr->pixel_per_clk.denom) /
mgr->pixel_per_clk.numer;
+ perf->clk_rate *= max_fps;
+ perf->clk_rate = (perf->clk_rate * mgr->fudge_factor.numer) /
+ mgr->fudge_factor.denom;
+ perf->clk_rate *= mgr->overhead.denom;
+
+ /*
+ * check for override overhead default value
+ */
+ if (rot_dev->min_overhead_us > (mgr->overhead.numer * 100))
+ perf->clk_rate = DIV_ROUND_UP_ULL(perf->clk_rate,
+ (mgr->overhead.denom - max_fps *
+ (rot_dev->min_overhead_us / 100)));
+ else
+ perf->clk_rate = DIV_ROUND_UP_ULL(perf->clk_rate,
+ (mgr->overhead.denom - max_fps *
+ mgr->overhead.numer));
+
+ /*
+ * check for Override clock calcualtion
+ */
+ if (rot_dev->min_rot_clk > perf->clk_rate)
+ perf->clk_rate = rot_dev->min_rot_clk;
read_bw = sde_rotator_calc_buf_bw(in_fmt, config->input.width,
- config->input.height, config->frame_rate);
+ config->input.height, max_fps);
write_bw = sde_rotator_calc_buf_bw(out_fmt, config->output.width,
- config->output.height, config->frame_rate);
+ config->output.height, max_fps);
read_bw = sde_apply_comp_ratio_factor(read_bw, in_fmt,
&config->input.comp_ratio);
@@ -907,6 +1234,25 @@
&config->output.comp_ratio);
perf->bw = read_bw + write_bw;
+
+ /*
+ * check for override bw calculation
+ */
+ if (rot_dev->min_bw > perf->bw)
+ perf->bw = rot_dev->min_bw;
+
+ perf->rdot_limit = sde_mdp_get_ot_limit(
+ config->input.width, config->input.height,
+ config->input.format, config->frame_rate, true);
+ perf->wrot_limit = sde_mdp_get_ot_limit(
+ config->input.width, config->input.height,
+ config->input.format, config->frame_rate, false);
+
+ SDEROT_DBG("clk:%lu, rdBW:%d, wrBW:%d, rdOT:%d, wrOT:%d\n",
+ perf->clk_rate, read_bw, write_bw, perf->rdot_limit,
+ perf->wrot_limit);
+ SDEROT_EVTLOG(perf->clk_rate, read_bw, write_bw, perf->rdot_limit,
+ perf->wrot_limit);
return 0;
}
@@ -1010,6 +1356,15 @@
mgr = entry->private->mgr;
+ SDEROT_EVTLOG(
+ entry->item.session_id, entry->item.sequence_id,
+ entry->item.src_rect.x, entry->item.src_rect.y,
+ entry->item.src_rect.w, entry->item.src_rect.h,
+ entry->item.dst_rect.x, entry->item.dst_rect.y,
+ entry->item.dst_rect.w, entry->item.dst_rect.h,
+ entry->item.flags,
+ entry->dnsc_factor_w, entry->dnsc_factor_h);
+
SDEDEV_DBG(mgr->device,
"commit handler s:%d.%u src:(%d,%d,%d,%d) dst:(%d,%d,%d,%d) f:0x%x dnsc:%u/%u\n",
entry->item.session_id, entry->item.sequence_id,
@@ -1043,6 +1398,14 @@
entry->item.dst_rect.x, entry->item.dst_rect.y,
entry->item.dst_rect.w, entry->item.dst_rect.h);
+ ATRACE_INT("sde_smmu_ctrl", 0);
+ ret = sde_smmu_ctrl(1);
+ if (IS_ERR_VALUE(ret)) {
+ SDEROT_ERR("IOMMU attach failed\n");
+ goto smmu_error;
+ }
+ ATRACE_INT("sde_smmu_ctrl", 1);
+
ret = sde_rotator_map_and_check_data(entry);
if (ret) {
SDEROT_ERR("fail to prepare input/output data %d\n", ret);
@@ -1068,7 +1431,9 @@
sde_rot_mgr_unlock(mgr);
return;
error:
- sde_rotator_put_hw_resource(entry->commitq, hw);
+ sde_smmu_ctrl(0);
+smmu_error:
+ sde_rotator_put_hw_resource(entry->commitq, entry, hw);
get_hw_res_err:
sde_rotator_signal_output(entry);
sde_rotator_release_entry(mgr, entry);
@@ -1118,11 +1483,13 @@
entry->item.flags,
entry->dnsc_factor_w, entry->dnsc_factor_h);
+ SDEROT_EVTLOG(entry->item.session_id, 0);
ret = mgr->ops_wait_for_entry(hw, entry);
if (ret) {
SDEROT_ERR("fail to wait for completion %d\n", ret);
atomic_inc(&request->failed_count);
}
+ SDEROT_EVTLOG(entry->item.session_id, 1);
if (entry->item.ts)
entry->item.ts[SDE_ROTATOR_TS_DONE] = ktime_get();
@@ -1140,8 +1507,9 @@
entry->item.dst_rect.w, entry->item.dst_rect.h);
sde_rot_mgr_lock(mgr);
- sde_rotator_put_hw_resource(entry->commitq, entry->commitq->hw);
+ sde_rotator_put_hw_resource(entry->commitq, entry, entry->commitq->hw);
sde_rotator_signal_output(entry);
+ ATRACE_INT("sde_rot_done", 1);
sde_rotator_release_entry(mgr, entry);
atomic_dec(&request->pending_count);
if (request->retireq && request->retire_work)
@@ -1149,6 +1517,10 @@
if (entry->item.ts)
entry->item.ts[SDE_ROTATOR_TS_RETIRE] = ktime_get();
sde_rot_mgr_unlock(mgr);
+
+ ATRACE_INT("sde_smmu_ctrl", 3);
+ sde_smmu_ctrl(0);
+ ATRACE_INT("sde_smmu_ctrl", 4);
}
static bool sde_rotator_verify_format(struct sde_rot_mgr *mgr,
@@ -1158,15 +1530,21 @@
u8 in_v_subsample, in_h_subsample;
u8 out_v_subsample, out_h_subsample;
- if (!sde_mdp_is_wb_format(out_fmt)) {
- SDEROT_DBG("Invalid output format\n");
- return false;
+ if (!sde_rotator_is_valid_pixfmt(mgr, in_fmt->format, true)) {
+ SDEROT_ERR("Invalid input format %x\n", in_fmt->format);
+ goto verify_error;
+ }
+
+ if (!sde_rotator_is_valid_pixfmt(mgr, out_fmt->format, false)) {
+ SDEROT_ERR("Invalid output format %x\n", out_fmt->format);
+ goto verify_error;
}
if ((in_fmt->is_yuv != out_fmt->is_yuv) ||
- (in_fmt->pixel_mode != out_fmt->pixel_mode)) {
- SDEROT_DBG("Rotator does not support CSC\n");
- return false;
+ (in_fmt->pixel_mode != out_fmt->pixel_mode) ||
+ (in_fmt->unpack_tight != out_fmt->unpack_tight)) {
+ SDEROT_ERR("Rotator does not support CSC\n");
+ goto verify_error;
}
/* Forcing same pixel depth */
@@ -1176,8 +1554,8 @@
(in_fmt->bits[C2_R_Cr] != out_fmt->bits[C2_R_Cr]) ||
(in_fmt->bits[C0_G_Y] != out_fmt->bits[C0_G_Y]) ||
(in_fmt->bits[C1_B_Cb] != out_fmt->bits[C1_B_Cb])) {
- SDEROT_DBG("Bit format does not match\n");
- return false;
+ SDEROT_ERR("Bit format does not match\n");
+ goto verify_error;
}
}
@@ -1190,89 +1568,163 @@
if ((in_v_subsample != out_h_subsample) ||
(in_h_subsample != out_v_subsample)) {
- SDEROT_DBG("Rotation has invalid subsampling\n");
- return false;
+ SDEROT_ERR("Rotation has invalid subsampling\n");
+ goto verify_error;
}
} else {
if (in_fmt->chroma_sample != out_fmt->chroma_sample) {
- SDEROT_DBG("Format subsampling mismatch\n");
- return false;
+ SDEROT_ERR("Format subsampling mismatch\n");
+ goto verify_error;
}
}
- SDEROT_DBG("in_fmt=%0d, out_fmt=%d\n", in_fmt->format, out_fmt->format);
return true;
+
+verify_error:
+ SDEROT_ERR("in_fmt=0x%x, out_fmt=0x%x\n",
+ in_fmt->format, out_fmt->format);
+ return false;
}
-int sde_rotator_verify_config(struct sde_rot_mgr *mgr,
+static struct sde_mdp_format_params *__verify_input_config(
+ struct sde_rot_mgr *mgr,
+ struct sde_rotation_config *config)
+{
+ struct sde_mdp_format_params *in_fmt;
+ u8 in_v_subsample, in_h_subsample;
+ u32 input;
+ int verify_input_only;
+
+ if (!mgr || !config) {
+ SDEROT_ERR("null parameters\n");
+ return NULL;
+ }
+
+ input = config->input.format;
+ verify_input_only =
+ (config->flags & SDE_ROTATION_VERIFY_INPUT_ONLY) ? 1 : 0;
+
+ in_fmt = sde_get_format_params(input);
+ if (!in_fmt) {
+ if (!verify_input_only)
+ SDEROT_ERR("Unrecognized input format:0x%x\n", input);
+ return NULL;
+ }
+
+ sde_mdp_get_v_h_subsample_rate(in_fmt->chroma_sample,
+ &in_v_subsample, &in_h_subsample);
+
+ /* Dimension of image needs to be divisible by subsample rate */
+ if ((config->input.height % in_v_subsample) ||
+ (config->input.width % in_h_subsample)) {
+ if (!verify_input_only)
+ SDEROT_ERR(
+ "In ROI, subsample mismatch, w=%d, h=%d, vss%d, hss%d\n",
+ config->input.width,
+ config->input.height,
+ in_v_subsample, in_h_subsample);
+ return NULL;
+ }
+
+ return in_fmt;
+}
+
+static struct sde_mdp_format_params *__verify_output_config(
+ struct sde_rot_mgr *mgr,
+ struct sde_rotation_config *config)
+{
+ struct sde_mdp_format_params *out_fmt;
+ u8 out_v_subsample, out_h_subsample;
+ u32 output;
+ int verify_input_only;
+
+ if (!mgr || !config) {
+ SDEROT_ERR("null parameters\n");
+ return NULL;
+ }
+
+ output = config->output.format;
+ verify_input_only =
+ (config->flags & SDE_ROTATION_VERIFY_INPUT_ONLY) ? 1 : 0;
+
+ out_fmt = sde_get_format_params(output);
+ if (!out_fmt) {
+ if (!verify_input_only)
+ SDEROT_ERR("Unrecognized output format:0x%x\n", output);
+ return NULL;
+ }
+
+ sde_mdp_get_v_h_subsample_rate(out_fmt->chroma_sample,
+ &out_v_subsample, &out_h_subsample);
+
+ /* Dimension of image needs to be divisible by subsample rate */
+ if ((config->output.height % out_v_subsample) ||
+ (config->output.width % out_h_subsample)) {
+ if (!verify_input_only)
+ SDEROT_ERR(
+ "Out ROI, subsample mismatch, w=%d, h=%d, vss%d, hss%d\n",
+ config->output.width,
+ config->output.height,
+ out_v_subsample, out_h_subsample);
+ return NULL;
+ }
+
+ return out_fmt;
+}
+
+int sde_rotator_verify_config_input(struct sde_rot_mgr *mgr,
+ struct sde_rotation_config *config)
+{
+ struct sde_mdp_format_params *in_fmt;
+
+ in_fmt = __verify_input_config(mgr, config);
+ if (!in_fmt)
+ return -EINVAL;
+
+ return 0;
+}
+
+int sde_rotator_verify_config_output(struct sde_rot_mgr *mgr,
+ struct sde_rotation_config *config)
+{
+ struct sde_mdp_format_params *out_fmt;
+
+ out_fmt = __verify_output_config(mgr, config);
+ if (!out_fmt)
+ return -EINVAL;
+
+ return 0;
+}
+
+int sde_rotator_verify_config_all(struct sde_rot_mgr *mgr,
struct sde_rotation_config *config)
{
struct sde_mdp_format_params *in_fmt, *out_fmt;
- u8 in_v_subsample, in_h_subsample;
- u8 out_v_subsample, out_h_subsample;
- u32 input, output;
bool rotation;
- int verify_input_only;
if (!mgr || !config) {
SDEROT_ERR("null parameters\n");
return -EINVAL;
}
- input = config->input.format;
- output = config->output.format;
rotation = (config->flags & SDE_ROTATION_90) ? true : false;
- verify_input_only =
- (config->flags & SDE_ROTATION_VERIFY_INPUT_ONLY) ? 1 : 0;
- in_fmt = sde_get_format_params(input);
- if (!in_fmt) {
- SDEROT_DBG("Unrecognized input format:%u\n", input);
+ in_fmt = __verify_input_config(mgr, config);
+ if (!in_fmt)
return -EINVAL;
- }
- out_fmt = sde_get_format_params(output);
- if (!out_fmt) {
- SDEROT_DBG("Unrecognized output format:%u\n", output);
+ out_fmt = __verify_output_config(mgr, config);
+ if (!out_fmt)
return -EINVAL;
- }
- sde_mdp_get_v_h_subsample_rate(in_fmt->chroma_sample,
- &in_v_subsample, &in_h_subsample);
- sde_mdp_get_v_h_subsample_rate(out_fmt->chroma_sample,
- &out_v_subsample, &out_h_subsample);
-
- /* Dimension of image needs to be divisible by subsample rate */
- if ((config->input.height % in_v_subsample) ||
- (config->input.width % in_h_subsample)) {
- SDEROT_DBG(
- "In ROI, subsample mismatch, w=%d, h=%d, vss%d, hss%d\n",
- config->input.width,
- config->input.height,
- in_v_subsample, in_h_subsample);
+ if (!sde_rotator_verify_format(mgr, in_fmt, out_fmt, rotation)) {
+ SDEROT_ERR(
+ "Rot format pairing invalid, in_fmt:0x%x, out_fmt:0x%x\n",
+ config->input.format,
+ config->output.format);
return -EINVAL;
}
- if ((config->output.height % out_v_subsample) ||
- (config->output.width % out_h_subsample)) {
- SDEROT_DBG(
- "Out ROI, subsample mismatch, w=%d, h=%d, vss%d, hss%d\n",
- config->output.width,
- config->output.height,
- out_v_subsample, out_h_subsample);
- if (!verify_input_only)
- return -EINVAL;
- }
-
- if (!sde_rotator_verify_format(mgr, in_fmt,
- out_fmt, rotation)) {
- SDEROT_DBG(
- "Rot format pairing invalid, in_fmt:%d, out_fmt:%d\n",
- input, output);
- if (!verify_input_only)
- return -EINVAL;
- }
-
return 0;
}
@@ -1596,16 +2048,9 @@
config.session_id = session_id;
perf->config = config;
- perf->last_wb_idx = -1;
+ perf->last_wb_idx = 0;
INIT_LIST_HEAD(&perf->list);
-
- ret = sde_rotator_calc_perf(mgr, perf);
- if (ret) {
- SDEROT_ERR("error setting the session %d\n", ret);
- goto copy_user_err;
- }
-
list_add(&perf->list, &private->perf_list);
ret = sde_rotator_resource_ctrl(mgr, true);
@@ -1626,25 +2071,17 @@
goto enable_clk_err;
}
- ret = sde_rotator_update_perf(mgr);
- if (ret) {
- SDEROT_ERR("fail to open session, not enough clk/bw\n");
- goto perf_err;
- }
SDEROT_DBG("open session id=%u in{%u,%u}f:%u out{%u,%u}f:%u\n",
config.session_id, config.input.width, config.input.height,
config.input.format, config.output.width, config.output.height,
config.output.format);
goto done;
-perf_err:
- sde_rotator_clk_ctrl(mgr, false);
enable_clk_err:
update_clk_err:
sde_rotator_resource_ctrl(mgr, false);
resource_err:
list_del_init(&perf->list);
-copy_user_err:
devm_kfree(&mgr->pdev->dev, perf->work_distribution);
alloc_err:
devm_kfree(&mgr->pdev->dev, perf);
@@ -1694,7 +2131,7 @@
int ret = 0;
struct sde_rot_perf *perf;
- ret = sde_rotator_verify_config(mgr, config);
+ ret = sde_rotator_verify_config_all(mgr, config);
if (ret) {
SDEROT_ERR("Rotator verify format failed\n");
return ret;
@@ -1716,11 +2153,23 @@
}
ret = sde_rotator_update_perf(mgr);
+ if (ret) {
+ SDEROT_ERR("error in updating perf: %d\n", ret);
+ goto done;
+ }
- SDEROT_DBG("reconfig session id=%u in{%u,%u}f:%u out{%u,%u}f:%u\n",
+ ret = sde_rotator_update_clk(mgr);
+ if (ret) {
+ SDEROT_ERR("error in updating the rotator clk: %d\n", ret);
+ goto done;
+ }
+
+ SDEROT_DBG(
+ "reconfig session id=%u in{%u,%u}f:%u out{%u,%u}f:%u fps:%d clk:%lu, bw:%llu\n",
config->session_id, config->input.width, config->input.height,
config->input.format, config->output.width,
- config->output.height, config->output.format);
+ config->output.height, config->output.format,
+ config->frame_rate, perf->clk_rate, perf->bw);
done:
return ret;
}
@@ -1839,6 +2288,11 @@
return -EINVAL;
}
+ /*
+ * if secure camera session was enabled
+ * go back to non secure state
+ */
+ sde_rotator_secure_session_ctrl(false);
sde_rotator_release_rotator_perf_session(mgr, private);
list_del_init(&private->list);
@@ -1861,7 +2315,7 @@
#define SPRINT(fmt, ...) \
(cnt += scnprintf(buf + cnt, len - cnt, fmt, ##__VA_ARGS__))
- SPRINT("wb_count=%d\n", mgr->queue_count);
+ SPRINT("queue_count=%d\n", mgr->queue_count);
SPRINT("downscale=1\n");
SPRINT("ubwc=1\n");
@@ -1892,7 +2346,6 @@
SPRINT("footswitch_cnt=%d\n", mgr->res_ref_cnt);
SPRINT("regulator_enable=%d\n", mgr->regulator_enable);
SPRINT("enable_clk_cnt=%d\n", mgr->rot_enable_clk_cnt);
- SPRINT("core_clk_idx=%d\n", mgr->core_clk_idx);
for (i = 0; i < mgr->num_rot_clk; i++)
if (mgr->rot_clk[i].clk)
SPRINT("%s=%lu\n", mgr->rot_clk[i].clk_name,
@@ -2092,17 +2545,39 @@
return 0;
}
+static inline int sde_rotator_search_dt_clk(struct platform_device *pdev,
+ struct sde_rot_mgr *mgr, char *clk_name, int clk_idx)
+{
+ struct clk *tmp;
+
+ if (clk_idx >= SDE_ROTATOR_CLK_MAX) {
+ SDEROT_ERR("invalid clk index %d\n", clk_idx);
+ return -EINVAL;
+ }
+
+ tmp = devm_clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(tmp)) {
+ SDEROT_ERR("unable to get clk: %s\n", clk_name);
+ return PTR_ERR(tmp);
+ }
+
+ strlcpy(mgr->rot_clk[clk_idx].clk_name, clk_name,
+ sizeof(mgr->rot_clk[clk_idx].clk_name));
+
+ mgr->rot_clk[clk_idx].clk = tmp;
+ return 0;
+}
+
static int sde_rotator_parse_dt_clk(struct platform_device *pdev,
struct sde_rot_mgr *mgr)
{
- u32 i = 0, rc = 0;
- const char *clock_name;
+ u32 rc = 0;
int num_clk;
num_clk = of_property_count_strings(pdev->dev.of_node,
"clock-names");
- if (num_clk <= 0) {
- SDEROT_ERR("clocks are not defined\n");
+ if ((num_clk <= 0) || (num_clk > SDE_ROTATOR_CLK_MAX)) {
+ SDEROT_ERR("Number of clocks are out of range: %d\n", num_clk);
goto clk_err;
}
@@ -2116,19 +2591,17 @@
goto clk_err;
}
- for (i = 0; i < mgr->num_rot_clk; i++) {
- u32 clock_rate = 0;
-
- of_property_read_string_index(pdev->dev.of_node, "clock-names",
- i, &clock_name);
- strlcpy(mgr->rot_clk[i].clk_name, clock_name,
- sizeof(mgr->rot_clk[i].clk_name));
-
- of_property_read_u32_index(pdev->dev.of_node, "clock-rate",
- i, &clock_rate);
- mgr->rot_clk[i].rate = clock_rate;
- }
-
+ if (sde_rotator_search_dt_clk(pdev, mgr, "mnoc_clk",
+ SDE_ROTATOR_CLK_MNOC_AHB) ||
+ sde_rotator_search_dt_clk(pdev, mgr, "iface_clk",
+ SDE_ROTATOR_CLK_MDSS_AHB) ||
+ sde_rotator_search_dt_clk(pdev, mgr, "axi_clk",
+ SDE_ROTATOR_CLK_MDSS_AXI) ||
+ sde_rotator_search_dt_clk(pdev, mgr, "rot_core_clk",
+ SDE_ROTATOR_CLK_ROT_CORE) ||
+ sde_rotator_search_dt_clk(pdev, mgr, "rot_clk",
+ SDE_ROTATOR_CLK_MDSS_ROT))
+ rc = -EINVAL;
clk_err:
return rc;
}
@@ -2136,10 +2609,7 @@
static int sde_rotator_register_clk(struct platform_device *pdev,
struct sde_rot_mgr *mgr)
{
- int i, ret;
- struct clk *clk;
- struct sde_rot_clk *rot_clk;
- int core_clk_idx = -1;
+ int ret;
ret = sde_rotator_parse_dt_clk(pdev, mgr);
if (ret) {
@@ -2147,34 +2617,12 @@
return -EINVAL;
}
- for (i = 0; i < mgr->num_rot_clk; i++) {
- rot_clk = &mgr->rot_clk[i];
-
- clk = devm_clk_get(&pdev->dev, rot_clk->clk_name);
- if (IS_ERR(clk)) {
- SDEROT_ERR("unable to get clk: %s\n",
- rot_clk->clk_name);
- return PTR_ERR(clk);
- }
- rot_clk->clk = clk;
-
- if (strcmp(rot_clk->clk_name, "rot_core_clk") == 0)
- core_clk_idx = i;
- }
-
- if (core_clk_idx < 0) {
- SDEROT_ERR("undefined core clk\n");
- return -ENXIO;
- }
-
- mgr->core_clk_idx = core_clk_idx;
-
return 0;
}
static void sde_rotator_unregister_clk(struct sde_rot_mgr *mgr)
{
- kfree(mgr->rot_clk);
+ devm_kfree(mgr->device, mgr->rot_clk);
mgr->rot_clk = NULL;
mgr->num_rot_clk = 0;
}
@@ -2235,6 +2683,10 @@
mgr->queue_count = 1;
mgr->pixel_per_clk.numer = ROT_PIXEL_PER_CLK_NUMERATOR;
mgr->pixel_per_clk.denom = ROT_PIXEL_PER_CLK_DENOMINATOR;
+ mgr->fudge_factor.numer = ROT_FUDGE_FACTOR_NUMERATOR;
+ mgr->fudge_factor.denom = ROT_FUDGE_FACTOR_DENOMINATOR;
+ mgr->overhead.numer = ROT_OVERHEAD_NUMERATOR;
+ mgr->overhead.denom = ROT_OVERHEAD_DENOMINATOR;
mutex_init(&mgr->lock);
atomic_set(&mgr->device_suspended, 0);
@@ -2274,11 +2726,14 @@
sde_rotator_clk_ctrl(mgr, true);
mdata->mdss_version = SDE_REG_READ(mdata, SDE_REG_HW_VERSION);
- SDEROT_INFO("mdss revision %x\n", mdata->mdss_version);
+ SDEROT_DBG("mdss revision %x\n", mdata->mdss_version);
if ((mdata->mdss_version & 0xFFFF0000) == 0x10070000) {
mgr->ops_hw_init = sde_rotator_r1_init;
+ } else if ((mdata->mdss_version & 0xF0000000) == 0x30000000) {
+ mgr->ops_hw_init = sde_rotator_r3_init;
} else {
+ ret = -ENODEV;
SDEROT_ERR("unsupported sde version %x\n",
mdata->mdss_version);
goto error_map_hw_ops;
@@ -2305,13 +2760,15 @@
error_init_queue:
mgr->ops_hw_destroy(mgr);
error_hw_init:
+error_map_hw_ops:
+ sde_rotator_clk_ctrl(mgr, false);
+ sde_rotator_resource_ctrl(mgr, false);
pm_runtime_disable(mgr->device);
sde_rotator_res_destroy(mgr);
error_res_init:
error_parse_dt:
sysfs_remove_group(&mgr->device->kobj, &sde_rotator_fs_attr_group);
error_create_sysfs:
-error_map_hw_ops:
devm_kfree(&pdev->dev, mgr);
*pmgr = NULL;
return ret;
@@ -2345,7 +2802,7 @@
}
}
-#if defined(CONFIG_PM_RUNTIME)
+#if defined(CONFIG_PM)
/*
* sde_rotator_runtime_suspend - Turn off power upon runtime suspend event
* @dev: Pointer to device structure
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
index 5052ea1..f91df67 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -59,9 +59,12 @@
/* use client provided dma buf instead of ion fd */
#define SDE_ROTATION_EXT_DMA_BUF 0x20000
-/*********************************************************************
- *configuration structures
- *********************************************************************/
+/* secure camera operation*/
+#define SDE_ROTATION_SECURE_CAMERA 0x40000
+
+/**********************************************************************
+ * configuration structures
+ **********************************************************************/
struct sde_rotation_buf_info {
uint32_t width;
@@ -92,6 +95,15 @@
SDE_ROTATOR_TS_MAX
};
+enum sde_rotator_clk_type {
+ SDE_ROTATOR_CLK_MDSS_AHB,
+ SDE_ROTATOR_CLK_MDSS_AXI,
+ SDE_ROTATOR_CLK_ROT_CORE,
+ SDE_ROTATOR_CLK_MDSS_ROT,
+ SDE_ROTATOR_CLK_MNOC_AHB,
+ SDE_ROTATOR_CLK_MAX
+};
+
struct sde_rotation_item {
/* rotation request flag */
uint32_t flags;
@@ -108,37 +120,37 @@
/* The output buffer for the request */
struct sde_layer_buffer output;
- /*
- * DMA pipe selection for this request by client:
- * 0: DMA pipe 0
- * 1: DMA pipe 1
- * or SDE_ROTATION_HW_ANY if client wants
- * driver to allocate any that is available
- *
- * OR
- *
- * Reserved
- */
+ /*
+ * DMA pipe selection for this request by client:
+ * 0: DMA pipe 0
+ * 1: DMA pipe 1
+ * or SDE_ROTATION_HW_ANY if client wants
+ * driver to allocate any that is available
+ *
+ * OR
+ *
+ * Reserved
+ */
uint32_t pipe_idx;
- /*
- * Write-back block selection for this request by client:
- * 0: Write-back block 0
- * 1: Write-back block 1
- * or SDE_ROTATION_HW_ANY if client wants
- * driver to allocate any that is available
- *
- * OR
- *
- * Priority selection for this request by client:
- * 0: Highest
- * 1..n: Limited by the lowest available priority
- */
+ /*
+ * Write-back block selection for this request by client:
+ * 0: Write-back block 0
+ * 1: Write-back block 1
+ * or SDE_ROTATION_HW_ANY if client wants
+ * driver to allocate any that is available
+ *
+ * OR
+ *
+ * Priority selection for this request by client:
+ * 0: Highest
+ * 1..n: Limited by the lowest available priority
+ */
uint32_t wb_idx;
- /*
- * Sequence ID of this request within the session
- */
+ /*
+ * Sequence ID of this request within the session
+ */
uint32_t sequence_id;
/* Which session ID is this request scheduled on */
@@ -169,7 +181,6 @@
atomic_t num_active;
int max_active;
wait_queue_head_t wait_queue;
- struct sde_rot_entry *workload;
};
struct sde_rot_queue {
@@ -225,6 +236,8 @@
struct mutex work_dis_lock;
u32 *work_distribution;
int last_wb_idx; /* last known wb index, used when above count is 0 */
+ u32 rdot_limit;
+ u32 wrot_limit;
};
struct sde_rot_file_private {
@@ -274,10 +287,13 @@
int rot_enable_clk_cnt;
struct sde_rot_clk *rot_clk;
int num_rot_clk;
- int core_clk_idx;
+ u32 rdot_limit;
+ u32 wrot_limit;
u32 hwacquire_timeout;
struct sde_mult_factor pixel_per_clk;
+ struct sde_mult_factor fudge_factor;
+ struct sde_mult_factor overhead;
int (*ops_config_hw)(struct sde_rot_hw_resource *hw,
struct sde_rot_entry *entry);
@@ -290,6 +306,8 @@
void (*ops_hw_free)(struct sde_rot_mgr *mgr,
struct sde_rot_hw_resource *hw);
int (*ops_hw_init)(struct sde_rot_mgr *mgr);
+ void (*ops_hw_pre_pmevent)(struct sde_rot_mgr *mgr, bool pmon);
+ void (*ops_hw_post_pmevent)(struct sde_rot_mgr *mgr, bool pmon);
void (*ops_hw_destroy)(struct sde_rot_mgr *mgr);
ssize_t (*ops_hw_show_caps)(struct sde_rot_mgr *mgr,
struct device_attribute *attr, char *buf, ssize_t len);
@@ -299,10 +317,32 @@
struct dentry *debugfs_root);
int (*ops_hw_validate_entry)(struct sde_rot_mgr *mgr,
struct sde_rot_entry *entry);
+ u32 (*ops_hw_get_pixfmt)(struct sde_rot_mgr *mgr, int index,
+ bool input);
+ int (*ops_hw_is_valid_pixfmt)(struct sde_rot_mgr *mgr, u32 pixfmt,
+ bool input);
void *hw_data;
};
+static inline int sde_rotator_is_valid_pixfmt(struct sde_rot_mgr *mgr,
+ u32 pixfmt, bool input)
+{
+ if (mgr && mgr->ops_hw_is_valid_pixfmt)
+ return mgr->ops_hw_is_valid_pixfmt(mgr, pixfmt, input);
+
+ return false;
+}
+
+static inline u32 sde_rotator_get_pixfmt(struct sde_rot_mgr *mgr,
+ int index, bool input)
+{
+ if (mgr && mgr->ops_hw_get_pixfmt)
+ return mgr->ops_hw_get_pixfmt(mgr, index, input);
+
+ return 0;
+}
+
static inline int __compare_session_item_rect(
struct sde_rotation_buf_info *s_rect,
struct sde_rect *i_rect, uint32_t i_fmt, bool src)
@@ -373,13 +413,21 @@
struct sde_rot_file_private *private,
struct sde_rot_entry_container *req);
-int sde_rotator_verify_config(struct sde_rot_mgr *rot_dev,
+int sde_rotator_verify_config_all(struct sde_rot_mgr *rot_dev,
+ struct sde_rotation_config *config);
+
+int sde_rotator_verify_config_input(struct sde_rot_mgr *rot_dev,
+ struct sde_rotation_config *config);
+
+int sde_rotator_verify_config_output(struct sde_rot_mgr *rot_dev,
struct sde_rotation_config *config);
int sde_rotator_validate_request(struct sde_rot_mgr *rot_dev,
struct sde_rot_file_private *ctx,
struct sde_rot_entry_container *req);
+int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable);
+
static inline void sde_rot_mgr_lock(struct sde_rot_mgr *mgr)
{
mutex_lock(&mgr->lock);
@@ -390,7 +438,7 @@
mutex_unlock(&mgr->lock);
}
-#if defined(CONFIG_PM_RUNTIME)
+#if defined(CONFIG_PM)
int sde_rotator_runtime_resume(struct device *dev);
int sde_rotator_runtime_suspend(struct device *dev);
int sde_rotator_runtime_idle(struct device *dev);
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
index 01fabeb..a41c450 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -22,6 +22,633 @@
#include "sde_rotator_core.h"
#include "sde_rotator_dev.h"
+#ifdef CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG
+#define SDE_EVTLOG_DEFAULT_ENABLE 1
+#else
+#define SDE_EVTLOG_DEFAULT_ENABLE 0
+#endif
+#define SDE_EVTLOG_DEFAULT_PANIC 1
+#define SDE_EVTLOG_DEFAULT_REGDUMP SDE_ROT_DBG_DUMP_IN_MEM
+#define SDE_EVTLOG_DEFAULT_VBIF_DBGBUSDUMP SDE_ROT_DBG_DUMP_IN_MEM
+
+/*
+ * evtlog will print this number of entries when it is called through
+ * sysfs node or panic. This prevents kernel log from evtlog message
+ * flood.
+ */
+#define SDE_ROT_EVTLOG_PRINT_ENTRY 256
+
+/*
+ * evtlog keeps this number of entries in memory for debug purpose. This
+ * number must be greater than print entry to prevent out of bound evtlog
+ * entry array access.
+ */
+#define SDE_ROT_EVTLOG_ENTRY (SDE_ROT_EVTLOG_PRINT_ENTRY * 4)
+#define SDE_ROT_EVTLOG_MAX_DATA 15
+#define SDE_ROT_EVTLOG_BUF_MAX 512
+#define SDE_ROT_EVTLOG_BUF_ALIGN 32
+#define SDE_ROT_DEBUG_BASE_MAX 10
+
+#define SDE_ROT_DEFAULT_BASE_REG_CNT 0x100
+#define GROUP_BYTES 4
+#define ROW_BYTES 16
+
+static DEFINE_SPINLOCK(sde_rot_xlock);
+
+/*
+ * tlog - EVTLOG entry structure
+ * @counter - EVTLOG entriy counter
+ * @time - timestamp of EVTLOG entry
+ * @name - function name of EVTLOG entry
+ * @line - line number of EVTLOG entry
+ * @data - EVTLOG data contents
+ * @data_cnt - number of data contents
+ * @pid - pid of current calling thread
+ */
+struct tlog {
+ u32 counter;
+ s64 time;
+ const char *name;
+ int line;
+ u32 data[SDE_ROT_EVTLOG_MAX_DATA];
+ u32 data_cnt;
+ int pid;
+};
+
+/*
+ * sde_rot_dbg_evtlog - EVTLOG debug data structure
+ * @logs - EVTLOG entries
+ * @first - first entry index in the EVTLOG
+ * @last - last entry index in the EVTLOG
+ * @curr - curr entry index in the EVTLOG
+ * @evtlog - EVTLOG debugfs handle
+ * @evtlog_enable - boolean indicates EVTLOG enable/disable
+ * @panic_on_err - boolean indicates issue panic after EVTLOG dump
+ * @enable_reg_dump - control in-log/memory dump for rotator registers
+ * @enable_vbif_dbgbus_dump - control in-log/memory dump for VBIF debug bus
+ * @evtlog_dump_work - schedule work strucutre for timeout handler
+ * @work_dump_reg - storage for register dump control in schedule work
+ * @work_panic - storage for panic control in schedule work
+ * @work_vbif_dbgbus - storage for VBIF debug bus control in schedule work
+ * @nrt_vbif_dbgbus_dump - memory buffer for VBIF debug bus dumping
+ * @reg_dump_array - memory buffer for rotator registers dumping
+ */
+struct sde_rot_dbg_evtlog {
+ struct tlog logs[SDE_ROT_EVTLOG_ENTRY];
+ u32 first;
+ u32 last;
+ u32 curr;
+ struct dentry *evtlog;
+ u32 evtlog_enable;
+ u32 panic_on_err;
+ u32 enable_reg_dump;
+ u32 enable_vbif_dbgbus_dump;
+ struct work_struct evtlog_dump_work;
+ bool work_dump_reg;
+ bool work_panic;
+ bool work_vbif_dbgbus;
+ u32 *nrt_vbif_dbgbus_dump; /* address for the nrt vbif debug bus dump */
+ u32 *reg_dump_array[SDE_ROT_DEBUG_BASE_MAX];
+} sde_rot_dbg_evtlog;
+
+/*
+ * sde_rot_evtlog_is_enabled - helper function for checking EVTLOG
+ * enable/disable
+ * @flag - EVTLOG option flag
+ */
+static inline bool sde_rot_evtlog_is_enabled(u32 flag)
+{
+ return (flag & sde_rot_dbg_evtlog.evtlog_enable) ||
+ (flag == SDE_ROT_EVTLOG_ALL &&
+ sde_rot_dbg_evtlog.evtlog_enable);
+}
+
+/*
+ * __vbif_debug_bus - helper function for VBIF debug bus dump
+ * @head - VBIF debug bus data structure
+ * @vbif_base - VBIF IO mapped address
+ * @dump_addr - output buffer for memory dump option
+ * @in_log - boolean indicates in-log dump option
+ */
+static void __vbif_debug_bus(struct sde_rot_vbif_debug_bus *head,
+ void __iomem *vbif_base, u32 *dump_addr, bool in_log)
+{
+ int i, j;
+ u32 val;
+
+ if (!dump_addr && !in_log)
+ return;
+
+ for (i = 0; i < head->block_cnt; i++) {
+ writel_relaxed(1 << (i + head->bit_offset),
+ vbif_base + head->block_bus_addr);
+ /* make sure that current bus blcok enable */
+ wmb();
+ for (j = 0; j < head->test_pnt_cnt; j++) {
+ writel_relaxed(j, vbif_base + head->block_bus_addr + 4);
+ /* make sure that test point is enabled */
+ wmb();
+ val = readl_relaxed(vbif_base + MMSS_VBIF_TEST_BUS_OUT);
+ if (dump_addr) {
+ *dump_addr++ = head->block_bus_addr;
+ *dump_addr++ = i;
+ *dump_addr++ = j;
+ *dump_addr++ = val;
+ }
+ if (in_log)
+ pr_err("testpoint:%x arb/xin id=%d index=%d val=0x%x\n",
+ head->block_bus_addr, i, j, val);
+ }
+ }
+}
+
+/*
+ * sde_rot_dump_vbif_debug_bus - VBIF debug bus dump
+ * @bus_dump_flag - dump flag controlling in-log/memory dump option
+ * @dump_mem - output buffer for memory dump location
+ */
+static void sde_rot_dump_vbif_debug_bus(u32 bus_dump_flag,
+ u32 **dump_mem)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ bool in_log, in_mem;
+ u32 *dump_addr = NULL;
+ u32 value;
+ struct sde_rot_vbif_debug_bus *head;
+ phys_addr_t phys = 0;
+ int i, list_size = 0;
+ void __iomem *vbif_base;
+ struct sde_rot_vbif_debug_bus *dbg_bus;
+ u32 bus_size;
+
+ pr_info("======== NRT VBIF Debug bus DUMP =========\n");
+ vbif_base = mdata->vbif_nrt_io.base;
+ dbg_bus = mdata->nrt_vbif_dbg_bus;
+ bus_size = mdata->nrt_vbif_dbg_bus_size;
+
+ if (!vbif_base || !dbg_bus || !bus_size)
+ return;
+
+ /* allocate memory for each test point */
+ for (i = 0; i < bus_size; i++) {
+ head = dbg_bus + i;
+ list_size += (head->block_cnt * head->test_pnt_cnt);
+ }
+
+ /* 4 bytes * 4 entries for each test point*/
+ list_size *= 16;
+
+ in_log = (bus_dump_flag & SDE_ROT_DBG_DUMP_IN_LOG);
+ in_mem = (bus_dump_flag & SDE_ROT_DBG_DUMP_IN_MEM);
+
+ if (in_mem) {
+ if (!(*dump_mem))
+ *dump_mem = dma_alloc_coherent(&mdata->pdev->dev,
+ list_size, &phys, GFP_KERNEL);
+
+ if (*dump_mem) {
+ dump_addr = *dump_mem;
+ pr_info("%s: start_addr:0x%pK end_addr:0x%pK\n",
+ __func__, dump_addr, dump_addr + list_size);
+ } else {
+ in_mem = false;
+ pr_err("dump_mem: allocation fails\n");
+ }
+ }
+
+ sde_smmu_ctrl(1);
+
+ value = readl_relaxed(vbif_base + MMSS_VBIF_CLKON);
+ writel_relaxed(value | BIT(1), vbif_base + MMSS_VBIF_CLKON);
+
+ /* make sure that vbif core is on */
+ wmb();
+
+ for (i = 0; i < bus_size; i++) {
+ head = dbg_bus + i;
+
+ writel_relaxed(0, vbif_base + head->disable_bus_addr);
+ writel_relaxed(BIT(0), vbif_base + MMSS_VBIF_TEST_BUS_OUT_CTRL);
+ /* make sure that other bus is off */
+ wmb();
+
+ __vbif_debug_bus(head, vbif_base, dump_addr, in_log);
+ if (dump_addr)
+ dump_addr += (head->block_cnt * head->test_pnt_cnt * 4);
+ }
+
+ sde_smmu_ctrl(0);
+
+ pr_info("========End VBIF Debug bus=========\n");
+}
+
+/*
+ * sde_rot_dump_reg - helper function for dumping rotator register set content
+ * @dump_name - register set name
+ * @reg_dump_flag - dumping flag controlling in-log/memory dump location
+ * @access - access type, sde registers or vbif registers
+ * @addr - starting address offset for dumping
+ * @len - range of the register set
+ * @dump_mem - output buffer for memory dump location option
+ */
+void sde_rot_dump_reg(const char *dump_name, u32 reg_dump_flag,
+ enum sde_rot_regdump_access access, u32 addr,
+ int len, u32 **dump_mem)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ bool in_log, in_mem;
+ u32 *dump_addr = NULL;
+ phys_addr_t phys = 0;
+ int i;
+ void __iomem *base;
+
+ in_log = (reg_dump_flag & SDE_ROT_DBG_DUMP_IN_LOG);
+ in_mem = (reg_dump_flag & SDE_ROT_DBG_DUMP_IN_MEM);
+
+ pr_debug("reg_dump_flag=%d in_log=%d in_mem=%d\n",
+ reg_dump_flag, in_log, in_mem);
+
+ if (len % 16)
+ len += 16;
+ len /= 16;
+
+ if (in_mem) {
+ if (!(*dump_mem))
+ *dump_mem = dma_alloc_coherent(&mdata->pdev->dev,
+ len * 16, &phys, GFP_KERNEL);
+
+ if (*dump_mem) {
+ dump_addr = *dump_mem;
+ pr_info("%s: start_addr:0x%p end_addr:0x%p reg_addr=0x%X\n",
+ dump_name, dump_addr, dump_addr + (u32)len * 16,
+ addr);
+ } else {
+ in_mem = false;
+ pr_err("dump_mem: kzalloc fails!\n");
+ }
+ }
+
+ base = mdata->sde_io.base;
+ /*
+ * VBIF NRT base handling
+ */
+ if (access == SDE_ROT_REGDUMP_VBIF)
+ base = mdata->vbif_nrt_io.base;
+
+ for (i = 0; i < len; i++) {
+ u32 x0, x4, x8, xc;
+
+ x0 = readl_relaxed(base + addr+0x0);
+ x4 = readl_relaxed(base + addr+0x4);
+ x8 = readl_relaxed(base + addr+0x8);
+ xc = readl_relaxed(base + addr+0xc);
+
+ if (in_log)
+ pr_info("0x%08X : %08x %08x %08x %08x\n",
+ addr, x0, x4, x8, xc);
+
+ if (dump_addr && in_mem) {
+ dump_addr[i*4] = x0;
+ dump_addr[i*4 + 1] = x4;
+ dump_addr[i*4 + 2] = x8;
+ dump_addr[i*4 + 3] = xc;
+ }
+
+ addr += 16;
+ }
+}
+
+/*
+ * sde_rot_dump_reg_all - dumping all SDE rotator registers
+ */
+static void sde_rot_dump_reg_all(void)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ struct sde_rot_regdump *head, *regdump;
+ u32 regdump_size;
+ int i;
+
+ regdump = mdata->regdump;
+ regdump_size = mdata->regdump_size;
+
+ if (!regdump || !regdump_size)
+ return;
+
+ /* Enable clock to rotator if not yet enabled */
+ sde_smmu_ctrl(1);
+
+ for (i = 0; (i < regdump_size) && (i < SDE_ROT_DEBUG_BASE_MAX); i++) {
+ head = ®dump[i];
+
+ if (head->access == SDE_ROT_REGDUMP_WRITE) {
+ writel_relaxed(1, mdata->sde_io.base + head->offset);
+ /* Make sure write go through */
+ wmb();
+ } else {
+ sde_rot_dump_reg(head->name,
+ sde_rot_dbg_evtlog.enable_reg_dump,
+ head->access,
+ head->offset, head->len,
+ &sde_rot_dbg_evtlog.reg_dump_array[i]);
+ }
+ }
+
+ /* Disable rotator clock */
+ sde_smmu_ctrl(0);
+}
+
+/*
+ * __sde_rot_evtlog_dump_calc_range - calculate dump range for EVTLOG
+ */
+static bool __sde_rot_evtlog_dump_calc_range(void)
+{
+ static u32 next;
+ bool need_dump = true;
+ unsigned long flags;
+ struct sde_rot_dbg_evtlog *evtlog = &sde_rot_dbg_evtlog;
+
+ spin_lock_irqsave(&sde_rot_xlock, flags);
+
+ evtlog->first = next;
+
+ if (evtlog->last == evtlog->first) {
+ need_dump = false;
+ goto dump_exit;
+ }
+
+ if (evtlog->last < evtlog->first) {
+ evtlog->first %= SDE_ROT_EVTLOG_ENTRY;
+ if (evtlog->last < evtlog->first)
+ evtlog->last += SDE_ROT_EVTLOG_ENTRY;
+ }
+
+ if ((evtlog->last - evtlog->first) > SDE_ROT_EVTLOG_PRINT_ENTRY) {
+ pr_warn("evtlog buffer overflow before dump: %d\n",
+ evtlog->last - evtlog->first);
+ evtlog->first = evtlog->last - SDE_ROT_EVTLOG_PRINT_ENTRY;
+ }
+ next = evtlog->first + 1;
+
+dump_exit:
+ spin_unlock_irqrestore(&sde_rot_xlock, flags);
+
+ return need_dump;
+}
+
+/*
+ * sde_rot_evtlog_dump_entry - helper function for EVTLOG content dumping
+ * @evtlog_buf: EVTLOG dump output buffer
+ * @evtlog_buf_size: EVTLOG output buffer size
+ */
+static ssize_t sde_rot_evtlog_dump_entry(char *evtlog_buf,
+ ssize_t evtlog_buf_size)
+{
+ int i;
+ ssize_t off = 0;
+ struct tlog *log, *prev_log;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sde_rot_xlock, flags);
+
+ log = &sde_rot_dbg_evtlog.logs[sde_rot_dbg_evtlog.first %
+ SDE_ROT_EVTLOG_ENTRY];
+
+ prev_log = &sde_rot_dbg_evtlog.logs[(sde_rot_dbg_evtlog.first - 1) %
+ SDE_ROT_EVTLOG_ENTRY];
+
+ off = snprintf((evtlog_buf + off), (evtlog_buf_size - off), "%s:%-4d",
+ log->name, log->line);
+
+ if (off < SDE_ROT_EVTLOG_BUF_ALIGN) {
+ memset((evtlog_buf + off), 0x20,
+ (SDE_ROT_EVTLOG_BUF_ALIGN - off));
+ off = SDE_ROT_EVTLOG_BUF_ALIGN;
+ }
+
+ off += snprintf((evtlog_buf + off), (evtlog_buf_size - off),
+ "=>[%-8d:%-11llu:%9llu][%-4d]:", sde_rot_dbg_evtlog.first,
+ log->time, (log->time - prev_log->time), log->pid);
+
+ for (i = 0; i < log->data_cnt; i++)
+ off += snprintf((evtlog_buf + off), (evtlog_buf_size - off),
+ "%x ", log->data[i]);
+
+ off += snprintf((evtlog_buf + off), (evtlog_buf_size - off), "\n");
+
+ spin_unlock_irqrestore(&sde_rot_xlock, flags);
+
+ return off;
+}
+
+/*
+ * sde_rot_evtlog_dump_all - Dumping all content in EVTLOG buffer
+ */
+static void sde_rot_evtlog_dump_all(void)
+{
+ char evtlog_buf[SDE_ROT_EVTLOG_BUF_MAX];
+
+ while (__sde_rot_evtlog_dump_calc_range()) {
+ sde_rot_evtlog_dump_entry(evtlog_buf, SDE_ROT_EVTLOG_BUF_MAX);
+ pr_info("%s", evtlog_buf);
+ }
+}
+
+/*
+ * sde_rot_evtlog_dump_open - debugfs open handler for evtlog dump
+ * @inode: debugfs inode
+ * @file: file handler
+ */
+static int sde_rot_evtlog_dump_open(struct inode *inode, struct file *file)
+{
+ /* non-seekable */
+ file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+/*
+ * sde_rot_evtlog_dump_read - debugfs read handler for evtlog dump
+ * @file: file handler
+ * @buff: user buffer content for debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_rot_evtlog_dump_read(struct file *file, char __user *buff,
+ size_t count, loff_t *ppos)
+{
+ ssize_t len = 0;
+ char evtlog_buf[SDE_ROT_EVTLOG_BUF_MAX];
+
+ if (__sde_rot_evtlog_dump_calc_range()) {
+ len = sde_rot_evtlog_dump_entry(evtlog_buf,
+ SDE_ROT_EVTLOG_BUF_MAX);
+ if (copy_to_user(buff, evtlog_buf, len))
+ return -EFAULT;
+ *ppos += len;
+ }
+
+ return len;
+}
+
+/*
+ * sde_rot_evtlog_dump_write - debugfs write handler for evtlog dump
+ * @file: file handler
+ * @user_buf: user buffer content from debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_rot_evtlog_dump_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ sde_rot_evtlog_dump_all();
+
+ sde_rot_dump_reg_all();
+
+ if (sde_rot_dbg_evtlog.panic_on_err)
+ panic("evtlog_dump_write");
+
+ return count;
+}
+
+/*
+ * sde_rot_evtlog_dump_helper - helper function for evtlog dump
+ * @dead: boolean indicates panic after dump
+ * @panic_name: Panic signature name show up in log
+ * @dump_rot: boolean indicates rotator register dump
+ * @dump_vbif_debug_bus: boolean indicates VBIF debug bus dump
+ */
+static void sde_rot_evtlog_dump_helper(bool dead, const char *panic_name,
+ bool dump_rot, bool dump_vbif_debug_bus)
+{
+ sde_rot_evtlog_dump_all();
+
+ if (dump_rot)
+ sde_rot_dump_reg_all();
+
+ if (dump_vbif_debug_bus)
+ sde_rot_dump_vbif_debug_bus(
+ sde_rot_dbg_evtlog.enable_vbif_dbgbus_dump,
+ &sde_rot_dbg_evtlog.nrt_vbif_dbgbus_dump);
+
+ if (dead)
+ panic(panic_name);
+}
+
+/*
+ * sde_rot_evtlog_debug_work - schedule work function for evtlog dump
+ * @work: schedule work structure
+ */
+static void sde_rot_evtlog_debug_work(struct work_struct *work)
+{
+ sde_rot_evtlog_dump_helper(
+ sde_rot_dbg_evtlog.work_panic,
+ "evtlog_workitem",
+ sde_rot_dbg_evtlog.work_dump_reg,
+ sde_rot_dbg_evtlog.work_vbif_dbgbus);
+}
+
+/*
+ * sde_rot_dump_panic - Issue evtlog dump and generic panic
+ */
+void sde_rot_dump_panic(void)
+{
+ sde_rot_evtlog_dump_all();
+ sde_rot_dump_reg_all();
+
+ panic("sde_rotator");
+}
+
+/*
+ * sde_rot_evtlog_tout_handler - log dump timeout handler
+ * @queue: boolean indicate putting log dump into queue
+ * @name: function name having timeout
+ */
+void sde_rot_evtlog_tout_handler(bool queue, const char *name, ...)
+{
+ int i;
+ bool dead = false;
+ bool dump_rot = false;
+ bool dump_vbif_dbgbus = false;
+ char *blk_name = NULL;
+ va_list args;
+
+ if (!sde_rot_evtlog_is_enabled(SDE_ROT_EVTLOG_DEFAULT))
+ return;
+
+ if (queue && work_pending(&sde_rot_dbg_evtlog.evtlog_dump_work))
+ return;
+
+ va_start(args, name);
+ for (i = 0; i < SDE_ROT_EVTLOG_MAX_DATA; i++) {
+ blk_name = va_arg(args, char*);
+ if (IS_ERR_OR_NULL(blk_name))
+ break;
+
+ if (!strcmp(blk_name, "rot"))
+ dump_rot = true;
+
+ if (!strcmp(blk_name, "vbif_dbg_bus"))
+ dump_vbif_dbgbus = true;
+
+ if (!strcmp(blk_name, "panic"))
+ dead = true;
+ }
+ va_end(args);
+
+ if (queue) {
+ /* schedule work to dump later */
+ sde_rot_dbg_evtlog.work_panic = dead;
+ sde_rot_dbg_evtlog.work_dump_reg = dump_rot;
+ sde_rot_dbg_evtlog.work_vbif_dbgbus = dump_vbif_dbgbus;
+ schedule_work(&sde_rot_dbg_evtlog.evtlog_dump_work);
+ } else {
+ sde_rot_evtlog_dump_helper(dead, name, dump_rot,
+ dump_vbif_dbgbus);
+ }
+}
+
+/*
+ * sde_rot_evtlog - log contents into memory for dump analysis
+ * @name: Name of function calling evtlog
+ * @line: line number of calling function
+ * @flag: Log control flag
+ */
+void sde_rot_evtlog(const char *name, int line, int flag, ...)
+{
+ unsigned long flags;
+ int i, val = 0;
+ va_list args;
+ struct tlog *log;
+
+ if (!sde_rot_evtlog_is_enabled(flag))
+ return;
+
+ spin_lock_irqsave(&sde_rot_xlock, flags);
+ log = &sde_rot_dbg_evtlog.logs[sde_rot_dbg_evtlog.curr];
+ log->time = ktime_to_us(ktime_get());
+ log->name = name;
+ log->line = line;
+ log->data_cnt = 0;
+ log->pid = current->pid;
+
+ va_start(args, flag);
+ for (i = 0; i < SDE_ROT_EVTLOG_MAX_DATA; i++) {
+
+ val = va_arg(args, int);
+ if (val == SDE_ROT_DATA_LIMITER)
+ break;
+
+ log->data[i] = val;
+ }
+ va_end(args);
+ log->data_cnt = i;
+ sde_rot_dbg_evtlog.curr =
+ (sde_rot_dbg_evtlog.curr + 1) % SDE_ROT_EVTLOG_ENTRY;
+ sde_rot_dbg_evtlog.last++;
+
+ spin_unlock_irqrestore(&sde_rot_xlock, flags);
+}
+
/*
* sde_rotator_stat_show - Show statistics on read to this debugfs file
* @s: Pointer to sequence file structure
@@ -35,10 +662,14 @@
u64 count = stats->count;
int num_events;
s64 proc_max, proc_min, proc_avg;
+ s64 swoh_max, swoh_min, swoh_avg;
proc_max = 0;
proc_min = S64_MAX;
proc_avg = 0;
+ swoh_max = 0;
+ swoh_min = S64_MAX;
+ swoh_avg = 0;
if (count > SDE_ROTATOR_NUM_EVENTS) {
num_events = SDE_ROTATOR_NUM_EVENTS;
@@ -59,9 +690,12 @@
s64 proc_time =
ktime_to_us(ktime_sub(ts[SDE_ROTATOR_TS_RETIRE],
start_time));
+ s64 sw_overhead_time =
+ ktime_to_us(ktime_sub(ts[SDE_ROTATOR_TS_FLUSH],
+ start_time));
seq_printf(s,
- "s:%d sq:%lld dq:%lld fe:%lld q:%lld c:%lld fl:%lld d:%lld sdq:%lld ddq:%lld t:%lld\n",
+ "s:%d sq:%lld dq:%lld fe:%lld q:%lld c:%lld fl:%lld d:%lld sdq:%lld ddq:%lld t:%lld oht:%lld\n",
i,
ktime_to_us(ktime_sub(ts[SDE_ROTATOR_TS_FENCE],
ts[SDE_ROTATOR_TS_SRCQB])),
@@ -81,21 +715,30 @@
ts[SDE_ROTATOR_TS_RETIRE])),
ktime_to_us(ktime_sub(ts[SDE_ROTATOR_TS_DSTDQB],
ts[SDE_ROTATOR_TS_RETIRE])),
- proc_time);
+ proc_time, sw_overhead_time);
proc_max = max(proc_max, proc_time);
proc_min = min(proc_min, proc_time);
proc_avg += proc_time;
+
+ swoh_max = max(swoh_max, sw_overhead_time);
+ swoh_min = min(swoh_min, sw_overhead_time);
+ swoh_avg += sw_overhead_time;
}
proc_avg = (num_events) ?
DIV_ROUND_CLOSEST_ULL(proc_avg, num_events) : 0;
+ swoh_avg = (num_events) ?
+ DIV_ROUND_CLOSEST_ULL(swoh_avg, num_events) : 0;
seq_printf(s, "count:%llu\n", count);
seq_printf(s, "fai1:%llu\n", stats->fail_count);
seq_printf(s, "t_max:%lld\n", proc_max);
seq_printf(s, "t_min:%lld\n", proc_min);
seq_printf(s, "t_avg:%lld\n", proc_avg);
+ seq_printf(s, "swoh_max:%lld\n", swoh_max);
+ seq_printf(s, "swoh_min:%lld\n", swoh_min);
+ seq_printf(s, "swoh_avg:%lld\n", swoh_avg);
return 0;
}
@@ -233,6 +876,86 @@
return 0;
}
+static const struct file_operations sde_rot_evtlog_fops = {
+ .open = sde_rot_evtlog_dump_open,
+ .read = sde_rot_evtlog_dump_read,
+ .write = sde_rot_evtlog_dump_write,
+};
+
+static int sde_rotator_evtlog_create_debugfs(
+ struct sde_rot_mgr *mgr,
+ struct dentry *debugfs_root)
+{
+ int i;
+
+ sde_rot_dbg_evtlog.evtlog = debugfs_create_dir("evtlog", debugfs_root);
+ if (IS_ERR_OR_NULL(sde_rot_dbg_evtlog.evtlog)) {
+ pr_err("debugfs_create_dir fail, error %ld\n",
+ PTR_ERR(sde_rot_dbg_evtlog.evtlog));
+ sde_rot_dbg_evtlog.evtlog = NULL;
+ return -ENODEV;
+ }
+
+ INIT_WORK(&sde_rot_dbg_evtlog.evtlog_dump_work,
+ sde_rot_evtlog_debug_work);
+ sde_rot_dbg_evtlog.work_panic = false;
+
+ for (i = 0; i < SDE_ROT_EVTLOG_ENTRY; i++)
+ sde_rot_dbg_evtlog.logs[i].counter = i;
+
+ debugfs_create_file("dump", 0644, sde_rot_dbg_evtlog.evtlog, NULL,
+ &sde_rot_evtlog_fops);
+ debugfs_create_u32("enable", 0644, sde_rot_dbg_evtlog.evtlog,
+ &sde_rot_dbg_evtlog.evtlog_enable);
+ debugfs_create_u32("panic", 0644, sde_rot_dbg_evtlog.evtlog,
+ &sde_rot_dbg_evtlog.panic_on_err);
+ debugfs_create_u32("reg_dump", 0644, sde_rot_dbg_evtlog.evtlog,
+ &sde_rot_dbg_evtlog.enable_reg_dump);
+ debugfs_create_u32("vbif_dbgbus_dump", 0644, sde_rot_dbg_evtlog.evtlog,
+ &sde_rot_dbg_evtlog.enable_vbif_dbgbus_dump);
+
+ sde_rot_dbg_evtlog.evtlog_enable = SDE_EVTLOG_DEFAULT_ENABLE;
+ sde_rot_dbg_evtlog.panic_on_err = SDE_EVTLOG_DEFAULT_PANIC;
+ sde_rot_dbg_evtlog.enable_reg_dump = SDE_EVTLOG_DEFAULT_REGDUMP;
+ sde_rot_dbg_evtlog.enable_vbif_dbgbus_dump =
+ SDE_EVTLOG_DEFAULT_VBIF_DBGBUSDUMP;
+
+ pr_info("evtlog_status: enable:%d, panic:%d, dump:%d\n",
+ sde_rot_dbg_evtlog.evtlog_enable,
+ sde_rot_dbg_evtlog.panic_on_err,
+ sde_rot_dbg_evtlog.enable_reg_dump);
+
+ return 0;
+}
+
+
+static int sde_rotator_perf_create_debugfs(
+ struct sde_rotator_device *rot_dev,
+ struct dentry *debugfs_root)
+{
+ rot_dev->perf_root = debugfs_create_dir("perf", debugfs_root);
+ if (IS_ERR_OR_NULL(rot_dev->perf_root)) {
+ pr_err("debugfs_create_dir for perf failed, error %ld\n",
+ PTR_ERR(rot_dev->perf_root));
+ rot_dev->perf_root = NULL;
+ return -ENODEV;
+ }
+
+ rot_dev->min_rot_clk = 0;
+ debugfs_create_u32("min_rot_clk", 0644,
+ rot_dev->perf_root, &rot_dev->min_rot_clk);
+
+ rot_dev->min_bw = 0;
+ debugfs_create_u32("min_bw", 0644,
+ rot_dev->perf_root, &rot_dev->min_bw);
+
+ rot_dev->min_overhead_us = 0;
+ debugfs_create_u32("min_overhead_us", 0644,
+ rot_dev->perf_root, &rot_dev->min_overhead_us);
+
+ return 0;
+}
+
/*
* struct sde_rotator_stat_ops - processed statistics file operations
*/
@@ -254,6 +977,273 @@
.release = single_release
};
+static int sde_rotator_debug_base_open(struct inode *inode, struct file *file)
+{
+ /* non-seekable */
+ file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static int sde_rotator_debug_base_release(struct inode *inode,
+ struct file *file)
+{
+ struct sde_rotator_debug_base *dbg = file->private_data;
+
+ if (dbg && dbg->buf) {
+ kfree(dbg->buf);
+ dbg->buf_len = 0;
+ dbg->buf = NULL;
+ }
+ return 0;
+}
+
+static ssize_t sde_rotator_debug_base_offset_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct sde_rotator_debug_base *dbg = file->private_data;
+ u32 off = 0;
+ u32 cnt = SDE_ROT_DEFAULT_BASE_REG_CNT;
+ char buf[24];
+
+ if (!dbg)
+ return -ENODEV;
+
+ if (count >= sizeof(buf))
+ return -EFAULT;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ buf[count] = 0;
+
+ if (sscanf(buf, "%5x %x", &off, &cnt) < 2)
+ return -EINVAL;
+
+ if (off > dbg->max_offset)
+ return -EINVAL;
+
+ if (cnt > (dbg->max_offset - off))
+ cnt = dbg->max_offset - off;
+
+ dbg->off = off;
+ dbg->cnt = cnt;
+
+ SDEROT_DBG("offset=%x cnt=%x\n", off, cnt);
+
+ return count;
+}
+
+static ssize_t sde_rotator_debug_base_offset_read(struct file *file,
+ char __user *buff, size_t count, loff_t *ppos)
+{
+ struct sde_rotator_debug_base *dbg = file->private_data;
+ int len = 0;
+ char buf[24] = {'\0'};
+
+ if (!dbg)
+ return -ENODEV;
+
+ if (*ppos)
+ return 0; /* the end */
+
+ len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt);
+ if (len < 0 || len >= sizeof(buf))
+ return 0;
+
+ if ((count < sizeof(buf)) || copy_to_user(buff, buf, len))
+ return -EFAULT;
+
+ *ppos += len; /* increase offset */
+
+ return len;
+}
+
+static ssize_t sde_rotator_debug_base_reg_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct sde_rotator_debug_base *dbg = file->private_data;
+ size_t off;
+ u32 data, cnt;
+ char buf[24];
+
+ if (!dbg)
+ return -ENODEV;
+
+ if (count >= sizeof(buf))
+ return -EFAULT;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ buf[count] = 0;
+
+ cnt = sscanf(buf, "%zx %x", &off, &data);
+
+ if (cnt < 2)
+ return -EFAULT;
+
+ if (off >= dbg->max_offset)
+ return -EFAULT;
+
+ /* Enable Clock for register access */
+ sde_rotator_clk_ctrl(dbg->mgr, true);
+
+ writel_relaxed(data, dbg->base + off);
+
+ /* Disable Clock after register access */
+ sde_rotator_clk_ctrl(dbg->mgr, false);
+
+ SDEROT_DBG("addr=%zx data=%x\n", off, data);
+
+ return count;
+}
+
+static ssize_t sde_rotator_debug_base_reg_read(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct sde_rotator_debug_base *dbg = file->private_data;
+ size_t len;
+
+ if (!dbg) {
+ SDEROT_ERR("invalid handle\n");
+ return -ENODEV;
+ }
+
+ if (!dbg->buf) {
+ char dump_buf[64];
+ char *ptr;
+ int cnt, tot;
+
+ dbg->buf_len = sizeof(dump_buf) *
+ DIV_ROUND_UP(dbg->cnt, ROW_BYTES);
+ dbg->buf = kzalloc(dbg->buf_len, GFP_KERNEL);
+
+ if (!dbg->buf) {
+ SDEROT_ERR("not enough memory to hold reg dump\n");
+ return -ENOMEM;
+ }
+
+ ptr = dbg->base + dbg->off;
+ tot = 0;
+
+ /* Enable clock for register access */
+ sde_rotator_clk_ctrl(dbg->mgr, true);
+
+ for (cnt = dbg->cnt; cnt > 0; cnt -= ROW_BYTES) {
+ hex_dump_to_buffer(ptr, min(cnt, ROW_BYTES),
+ ROW_BYTES, GROUP_BYTES, dump_buf,
+ sizeof(dump_buf), false);
+ len = scnprintf(dbg->buf + tot, dbg->buf_len - tot,
+ "0x%08x: %s\n",
+ ((int) (unsigned long) ptr) -
+ ((int) (unsigned long) dbg->base),
+ dump_buf);
+
+ ptr += ROW_BYTES;
+ tot += len;
+ if (tot >= dbg->buf_len)
+ break;
+ }
+ /* Disable clock after register access */
+ sde_rotator_clk_ctrl(dbg->mgr, false);
+
+ dbg->buf_len = tot;
+ }
+
+ if (*ppos >= dbg->buf_len)
+ return 0; /* done reading */
+
+ len = min(count, dbg->buf_len - (size_t) *ppos);
+ if (copy_to_user(user_buf, dbg->buf + *ppos, len)) {
+ SDEROT_ERR("failed to copy to user\n");
+ return -EFAULT;
+ }
+
+ *ppos += len; /* increase offset */
+
+ return len;
+}
+
+static const struct file_operations sde_rotator_off_fops = {
+ .open = sde_rotator_debug_base_open,
+ .release = sde_rotator_debug_base_release,
+ .read = sde_rotator_debug_base_offset_read,
+ .write = sde_rotator_debug_base_offset_write,
+};
+
+static const struct file_operations sde_rotator_reg_fops = {
+ .open = sde_rotator_debug_base_open,
+ .release = sde_rotator_debug_base_release,
+ .read = sde_rotator_debug_base_reg_read,
+ .write = sde_rotator_debug_base_reg_write,
+};
+
+int sde_rotator_debug_register_base(struct sde_rotator_device *rot_dev,
+ struct dentry *debugfs_root,
+ const char *name,
+ struct sde_io_data *io_data)
+{
+ struct sde_rotator_debug_base *dbg;
+ struct dentry *ent_off, *ent_reg;
+ char dbgname[80] = "";
+ int prefix_len = 0;
+
+ if (!io_data)
+ return -EINVAL;
+
+ dbg = kzalloc(sizeof(*dbg), GFP_KERNEL);
+ if (!dbg)
+ return -ENOMEM;
+
+ if (name)
+ strlcpy(dbg->name, name, sizeof(dbg->name));
+ dbg->base = io_data->base;
+ dbg->max_offset = io_data->len;
+ dbg->off = 0;
+ dbg->cnt = SDE_ROT_DEFAULT_BASE_REG_CNT;
+
+ if (name) {
+ if (strcmp(name, "sde"))
+ prefix_len = snprintf(dbgname, sizeof(dbgname), "%s_",
+ name);
+ else
+ /*
+ * For SDE Rotator registers block, the IO base address
+ * is based on MDP IO address base. It is necessary to
+ * apply the initial offset to it from the first
+ * regdump setting.
+ */
+ dbg->base += rot_dev->mdata->regdump ?
+ rot_dev->mdata->regdump[0].offset : 0;
+ }
+
+ strlcpy(dbgname + prefix_len, "off", sizeof(dbgname) - prefix_len);
+ ent_off = debugfs_create_file(dbgname, 0644, debugfs_root, dbg,
+ &sde_rotator_off_fops);
+ if (IS_ERR_OR_NULL(ent_off)) {
+ SDEROT_ERR("debugfs_create_file: offset fail\n");
+ goto off_fail;
+ }
+
+ strlcpy(dbgname + prefix_len, "reg", sizeof(dbgname) - prefix_len);
+ ent_reg = debugfs_create_file(dbgname, 0644, debugfs_root, dbg,
+ &sde_rotator_reg_fops);
+ if (IS_ERR_OR_NULL(ent_reg)) {
+ SDEROT_ERR("debugfs_create_file: reg fail\n");
+ goto reg_fail;
+ }
+
+ dbg->mgr = rot_dev->mgr;
+
+ return 0;
+reg_fail:
+ debugfs_remove(ent_off);
+off_fail:
+ kfree(dbg);
+ return -ENODEV;
+}
+
/*
* sde_rotator_create_debugfs - Setup rotator debugfs directory structure.
* @rot_dev: Pointer to rotator device
@@ -319,6 +1309,32 @@
return NULL;
}
+ if (sde_rotator_evtlog_create_debugfs(rot_dev->mgr, debugfs_root)) {
+ SDEROT_ERR("fail create evtlog debugfs\n");
+ debugfs_remove_recursive(debugfs_root);
+ return NULL;
+ }
+
+ if (sde_rotator_perf_create_debugfs(rot_dev, debugfs_root)) {
+ SDEROT_ERR("fail create perf debugfs\n");
+ debugfs_remove_recursive(debugfs_root);
+ return NULL;
+ }
+
+ if (sde_rotator_debug_register_base(rot_dev, debugfs_root,
+ "sde", &rot_dev->mdata->sde_io)) {
+ SDEROT_ERR("fail create debug register for sde rotator\n");
+ debugfs_remove_recursive(debugfs_root);
+ return NULL;
+ }
+
+ if (sde_rotator_debug_register_base(rot_dev, debugfs_root,
+ "vbif_nrt", &rot_dev->mdata->vbif_nrt_io)) {
+ SDEROT_ERR("fail create debug register for sderot vbif_nrt\n");
+ debugfs_remove_recursive(debugfs_root);
+ return NULL;
+ }
+
return debugfs_root;
}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
index 2ed1b75..c2c6f97 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
@@ -16,8 +16,45 @@
#include <linux/types.h>
#include <linux/dcache.h>
+#define SDE_ROT_DATA_LIMITER (-1)
+#define SDE_ROT_EVTLOG_TOUT_DATA_LIMITER (NULL)
+
+enum sde_rot_dbg_reg_dump_flag {
+ SDE_ROT_DBG_DUMP_IN_LOG = BIT(0),
+ SDE_ROT_DBG_DUMP_IN_MEM = BIT(1),
+};
+
+enum sde_rot_dbg_evtlog_flag {
+ SDE_ROT_EVTLOG_DEFAULT = BIT(0),
+ SDE_ROT_EVTLOG_IOMMU = BIT(1),
+ SDE_ROT_EVTLOG_DBG = BIT(6),
+ SDE_ROT_EVTLOG_ALL = BIT(7)
+};
+
+#define SDEROT_EVTLOG(...) sde_rot_evtlog(__func__, __LINE__, \
+ SDE_ROT_EVTLOG_DEFAULT, ##__VA_ARGS__, SDE_ROT_DATA_LIMITER)
+
+#define SDEROT_EVTLOG_TOUT_HANDLER(...) \
+ sde_rot_evtlog_tout_handler(false, __func__, ##__VA_ARGS__, \
+ SDE_ROT_EVTLOG_TOUT_DATA_LIMITER)
+
+void sde_rot_evtlog(const char *name, int line, int flag, ...);
+void sde_rot_dump_panic(void);
+void sde_rot_evtlog_tout_handler(bool queue, const char *name, ...);
+
struct sde_rotator_device;
+struct sde_rotator_debug_base {
+ char name[80];
+ void __iomem *base;
+ size_t off;
+ size_t cnt;
+ size_t max_offset;
+ char *buf;
+ size_t buf_len;
+ struct sde_rot_mgr *mgr;
+};
+
#if defined(CONFIG_DEBUG_FS)
struct dentry *sde_rotator_create_debugfs(
struct sde_rotator_device *rot_dev);
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
index 5183a7b..4bd997d 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -24,7 +24,7 @@
#include <linux/of.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
-#include <media/videobuf2-core.h>
+#include <media/videobuf2-v4l2.h>
#include <media/v4l2-mem2mem.h>
#include "sde_rotator_base.h"
@@ -52,188 +52,6 @@
#define SDE_ROTATOR_DEGREE_270 270
#define SDE_ROTATOR_DEGREE_180 180
#define SDE_ROTATOR_DEGREE_90 90
-/*
- * Format description/mapping
- * @pixelformat: external format defined in msm_sde_rotator header.
- *
- * Note RGBA/8888 naming convention follows internal convention and
- * is reverse of standard V4L2 convention. Description containing
- * prefix 'SDE/' refers to SDE specific conventions and/or features.
- */
-static const struct v4l2_fmtdesc fmtdesc[] = {
- {
- .description = "SDE/XRGB_8888",
- .pixelformat = SDE_PIX_FMT_XRGB_8888,
- },
- {
- .description = "SDE/ARGB_8888",
- .pixelformat = SDE_PIX_FMT_ARGB_8888,
- },
- {
- .description = "SDE/ABGR_8888",
- .pixelformat = SDE_PIX_FMT_ABGR_8888,
- },
- {
- .description = "SDE/RGBA_8888",
- .pixelformat = SDE_PIX_FMT_RGBA_8888,
- },
- {
- .description = "SDE/BGRA_8888",
- .pixelformat = SDE_PIX_FMT_BGRA_8888,
- },
- {
- .description = "SDE/RGBX_8888",
- .pixelformat = SDE_PIX_FMT_RGBX_8888,
- },
- {
- .description = "SDE/BGRX_8888",
- .pixelformat = SDE_PIX_FMT_BGRX_8888,
- },
- {
- .description = "RGBA_5551",
- .pixelformat = SDE_PIX_FMT_RGBA_5551,
- },
- {
- .description = "ARGB_4444",
- .pixelformat = SDE_PIX_FMT_ARGB_4444,
- },
- {
- .description = "RGBA_4444",
- .pixelformat = SDE_PIX_FMT_RGBA_4444,
- },
- {
- .description = "RGB_888",
- .pixelformat = SDE_PIX_FMT_RGB_888,
- },
- {
- .description = "BGR_888",
- .pixelformat = SDE_PIX_FMT_BGR_888,
- },
- {
- .description = "RGB_565",
- .pixelformat = SDE_PIX_FMT_RGB_565,
- },
- {
- .description = "BGR_565",
- .pixelformat = SDE_PIX_FMT_BGR_565,
- },
- {
- .description = "Y_CB_CR_H2V2",
- .pixelformat = SDE_PIX_FMT_Y_CB_CR_H2V2,
- },
- {
- .description = "Y_CR_CB_H2V2",
- .pixelformat = SDE_PIX_FMT_Y_CR_CB_H2V2,
- },
- {
- .description = "SDE/Y_CR_CB_GH2V2",
- .pixelformat = SDE_PIX_FMT_Y_CR_CB_GH2V2,
- },
- {
- .description = "Y_CBCR_H2V2",
- .pixelformat = SDE_PIX_FMT_Y_CBCR_H2V2,
- },
- {
- .description = "Y_CRCB_H2V2",
- .pixelformat = SDE_PIX_FMT_Y_CRCB_H2V2,
- },
- {
- .description = "Y_CBCR_H1V2",
- .pixelformat = SDE_PIX_FMT_Y_CBCR_H1V2,
- },
- {
- .description = "Y_CRCB_H1V2",
- .pixelformat = SDE_PIX_FMT_Y_CRCB_H1V2,
- },
- {
- .description = "Y_CBCR_H2V1",
- .pixelformat = SDE_PIX_FMT_Y_CBCR_H2V1,
- },
- {
- .description = "Y_CRCB_H2V1",
- .pixelformat = SDE_PIX_FMT_Y_CRCB_H2V1,
- },
- {
- .description = "YCBYCR_H2V1",
- .pixelformat = SDE_PIX_FMT_YCBYCR_H2V1,
- },
- {
- .description = "SDE/Y_CBCR_H2V2_VENUS",
- .pixelformat = SDE_PIX_FMT_Y_CBCR_H2V2_VENUS,
- },
- {
- .description = "SDE/Y_CRCB_H2V2_VENUS",
- .pixelformat = SDE_PIX_FMT_Y_CRCB_H2V2_VENUS,
- },
- {
- .description = "SDE/RGBA_8888_UBWC",
- .pixelformat = SDE_PIX_FMT_RGBA_8888_UBWC,
- },
- {
- .description = "SDE/RGBX_8888_UBWC",
- .pixelformat = SDE_PIX_FMT_RGBX_8888_UBWC,
- },
- {
- .description = "SDE/RGB_565_UBWC",
- .pixelformat = SDE_PIX_FMT_RGB_565_UBWC,
- },
- {
- .description = "SDE/Y_CBCR_H2V2_UBWC",
- .pixelformat = SDE_PIX_FMT_Y_CBCR_H2V2_UBWC,
- },
- {
- .description = "SDE/RGBA_1010102",
- .pixelformat = SDE_PIX_FMT_RGBA_1010102,
- },
- {
- .description = "SDE/RGBX_1010102",
- .pixelformat = SDE_PIX_FMT_RGBX_1010102,
- },
- {
- .description = "SDE/ARGB_2101010",
- .pixelformat = SDE_PIX_FMT_ARGB_2101010,
- },
- {
- .description = "SDE/XRGB_2101010",
- .pixelformat = SDE_PIX_FMT_XRGB_2101010,
- },
- {
- .description = "SDE/BGRA_1010102",
- .pixelformat = SDE_PIX_FMT_BGRA_1010102,
- },
- {
- .description = "SDE/BGRX_1010102",
- .pixelformat = SDE_PIX_FMT_BGRX_1010102,
- },
- {
- .description = "SDE/ABGR_2101010",
- .pixelformat = SDE_PIX_FMT_ABGR_2101010,
- },
- {
- .description = "SDE/XBGR_2101010",
- .pixelformat = SDE_PIX_FMT_XBGR_2101010,
- },
- {
- .description = "SDE/RGBA_1010102_UBWC",
- .pixelformat = SDE_PIX_FMT_RGBA_1010102_UBWC,
- },
- {
- .description = "SDE/RGBX_1010102_UBWC",
- .pixelformat = SDE_PIX_FMT_RGBX_1010102_UBWC,
- },
- {
- .description = "SDE/Y_CBCR_H2V2_P010",
- .pixelformat = SDE_PIX_FMT_Y_CBCR_H2V2_P010,
- },
- {
- .description = "SDE/Y_CBCR_H2V2_TP10",
- .pixelformat = SDE_PIX_FMT_Y_CBCR_H2V2_TP10,
- },
- {
- .description = "SDE/Y_CBCR_H2V2_TP10_UBWC",
- .pixelformat = SDE_PIX_FMT_Y_CBCR_H2V2_TP10_UBWC,
- },
-};
static void sde_rotator_submit_handler(struct work_struct *work);
static void sde_rotator_retire_handler(struct work_struct *work);
@@ -253,26 +71,6 @@
}
/*
- * sde_rotator_get_format_idx - Get rotator format lookup index.
- * @ctx: Pointer to rotator ctx.
- * @f: v4l2 format.
- */
-static int sde_rotator_get_format_idx(struct sde_rotator_ctx *ctx,
- struct v4l2_format *f)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(fmtdesc); i++)
- if (fmtdesc[i].pixelformat == f->fmt.pix.pixelformat)
- break;
-
- if (i == ARRAY_SIZE(fmtdesc))
- return -EINVAL;
-
- return i;
-}
-
-/*
* sde_rotator_get_flags_from_ctx - Get low-level command flag
* @ctx: Pointer to rotator context.
*/
@@ -292,6 +90,8 @@
ret_flags ^= SDE_ROTATION_FLIP_UD;
if (ctx->secure)
ret_flags |= SDE_ROTATION_SECURE;
+ if (ctx->secure_camera)
+ ret_flags |= SDE_ROTATION_SECURE_CAMERA;
if (ctx->format_out.fmt.pix.field == V4L2_FIELD_INTERLACED &&
ctx->format_cap.fmt.pix.field == V4L2_FIELD_NONE)
ret_flags |= SDE_ROTATION_DEINTERLACE;
@@ -323,6 +123,25 @@
config->output.format = ctx->format_cap.fmt.pix.pixelformat;
config->output.comp_ratio.numer = 1;
config->output.comp_ratio.denom = 1;
+
+ /*
+ * Use compression ratio of the first buffer to estimate
+ * performance requirement of the session. If core layer does
+ * not support dynamic per buffer compression ratio recalculation,
+ * this configuration will determine the overall static compression
+ * ratio of the session.
+ */
+ if (ctx->vbinfo_out)
+ config->input.comp_ratio = ctx->vbinfo_out[0].comp_ratio;
+ if (ctx->vbinfo_cap)
+ config->output.comp_ratio = ctx->vbinfo_cap[0].comp_ratio;
+
+ SDEDEV_DBG(ctx->rot_dev->dev, "config s:%d out_cr:%u/%u cap_cr:%u/%u\n",
+ ctx->session_id,
+ config->input.comp_ratio.numer,
+ config->input.comp_ratio.denom,
+ config->output.comp_ratio.numer,
+ config->output.comp_ratio.denom);
}
/*
@@ -338,7 +157,7 @@
item->session_id = ctx->session_id;
item->sequence_id = 0;
/* assign high/low priority */
- item->wb_idx = (ctx->priority >= V4L2_PRIORITY_DEFAULT) ? 0 : 1;
+ item->wb_idx = (ctx->fh.prio >= V4L2_PRIORITY_DEFAULT) ? 0 : 1;
item->src_rect.x = ctx->crop_out.left;
item->src_rect.y = ctx->crop_out.top;
item->src_rect.w = ctx->crop_out.width;
@@ -430,18 +249,19 @@
/*
* sde_rotator_queue_setup - vb2_ops queue_setup callback.
* @q: Pointer to vb2 queue struct.
- * @fmt: Pointer to v4l2 format struct (NULL is valid argument).
+ * @parg: Pointer to v4l2 format struct (NULL is valid argument).
* @num_buffers: Pointer of number of buffers requested.
* @num_planes: Pointer to number of planes requested.
* @sizes: Array containing sizes of planes.
* @alloc_ctxs: Array of allocated contexts for each plane.
*/
static int sde_rotator_queue_setup(struct vb2_queue *q,
- const struct v4l2_format *fmt,
+ const void *parg,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
struct sde_rotator_ctx *ctx = vb2_get_drv_priv(q);
+ const struct v4l2_format *fmt = parg;
int i;
if (!num_buffers)
@@ -473,8 +293,11 @@
ctx->nbuf_out, GFP_KERNEL);
if (!ctx->vbinfo_out)
return -ENOMEM;
- for (i = 0; i < ctx->nbuf_out; i++)
+ for (i = 0; i < ctx->nbuf_out; i++) {
ctx->vbinfo_out[i].fd = -1;
+ ctx->vbinfo_out[i].comp_ratio.numer = 1;
+ ctx->vbinfo_out[i].comp_ratio.denom = 1;
+ }
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
ctx->nbuf_cap = *num_buffers;
@@ -483,8 +306,11 @@
ctx->nbuf_cap, GFP_KERNEL);
if (!ctx->vbinfo_cap)
return -ENOMEM;
- for (i = 0; i < ctx->nbuf_cap; i++)
+ for (i = 0; i < ctx->nbuf_cap; i++) {
ctx->vbinfo_cap[i].fd = -1;
+ ctx->vbinfo_cap[i].comp_ratio.numer = 1;
+ ctx->vbinfo_cap[i].comp_ratio.denom = 1;
+ }
break;
default:
return -EINVAL;
@@ -500,8 +326,42 @@
static void sde_rotator_buf_queue(struct vb2_buffer *vb)
{
struct sde_rotator_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+}
+
+/*
+ * sde_rotator_buf_finish - vb2_ops buf_finish to finalize buffer before going
+ * back to user space
+ * @vb: Pointer to vb2 buffer struct.
+ */
+static void sde_rotator_buf_finish(struct vb2_buffer *vb)
+{
+ struct sde_rotator_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ int i;
+
+ SDEDEV_DBG(ctx->rot_dev->dev,
+ "buf_finish t:%d i:%d s:%d m:%u np:%d up:%lu\n",
+ vb->type, vb->index, vb->state,
+ vb->vb2_queue->memory,
+ vb->num_planes,
+ vb->planes[0].m.userptr);
+
+ if (vb->vb2_queue->memory != VB2_MEMORY_USERPTR)
+ return;
+
+ /*
+ * We use userptr to tunnel fd, and fd can be the same across qbuf
+ * even though the underlying buffer is different. Since vb2 layer
+ * optimizes memory mapping for userptr by first checking if userptr
+ * has changed, it will not trigger put_userptr if fd value does
+ * not change. In order to force buffer release, we need to clear
+ * userptr when the current buffer is done and ready to go back to
+ * user mode. Since 0 is a valid fd, reset userptr to -1 instead.
+ */
+ for (i = 0; i < vb->num_planes; i++)
+ vb->planes[i].m.userptr = ~0;
}
/*
@@ -523,23 +383,23 @@
/* return buffers according videobuffer2-core.h */
if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- struct vb2_buffer *buf;
+ struct vb2_v4l2_buffer *buf;
- while ((buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) {
+ while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) {
SDEDEV_DBG(rot_dev->dev,
"return vb t:%d i:%d\n",
- buf->v4l2_buf.type,
- buf->v4l2_buf.index);
+ buf->vb2_buf.type,
+ buf->vb2_buf.index);
v4l2_m2m_buf_done(buf, state);
}
} else if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- struct vb2_buffer *buf;
+ struct vb2_v4l2_buffer *buf;
- while ((buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) {
+ while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) {
SDEDEV_DBG(rot_dev->dev,
"return vb t:%d i:%d\n",
- buf->v4l2_buf.type,
- buf->v4l2_buf.index);
+ buf->vb2_buf.type,
+ buf->vb2_buf.index);
v4l2_m2m_buf_done(buf, state);
}
} else {
@@ -556,23 +416,10 @@
{
struct sde_rotator_ctx *ctx = vb2_get_drv_priv(q);
struct sde_rotator_device *rot_dev = ctx->rot_dev;
- struct sde_rotation_config config;
- int ret;
SDEDEV_DBG(rot_dev->dev, "start streaming s:%d t:%d\n",
ctx->session_id, q->type);
- sde_rot_mgr_lock(rot_dev->mgr);
- sde_rotator_get_config_from_ctx(ctx, &config);
- ret = sde_rotator_session_config(rot_dev->mgr, ctx->private, &config);
- sde_rot_mgr_unlock(rot_dev->mgr);
- if (ret < 0) {
- SDEDEV_ERR(rot_dev->dev,
- "fail config in stream on s:%d t:%d r:%d\n",
- ctx->session_id, q->type, ret);
- return -EINVAL;
- }
-
if (!IS_ERR_OR_NULL(ctx->request) ||
atomic_read(&ctx->command_pending))
SDEDEV_ERR(rot_dev->dev,
@@ -660,6 +507,7 @@
.stop_streaming = sde_rotator_stop_streaming,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
+ .buf_finish = sde_rotator_buf_finish,
};
/*
@@ -667,36 +515,54 @@
* @alloc_ctx: Contexts allocated in buf_setup.
* @vaddr: Virtual addr passed from userpsace (in our case ion fd)
* @size: Size of the buffer
- * @write: True if buffer will be used for writing the data.
+ * @dma_dir: DMA data direction of the given buffer.
*/
static void *sde_rotator_get_userptr(void *alloc_ctx,
- unsigned long vaddr, unsigned long size, int write)
+ unsigned long vaddr, unsigned long size,
+ enum dma_data_direction dma_dir)
{
struct sde_rotator_ctx *ctx = alloc_ctx;
struct sde_rotator_device *rot_dev = ctx->rot_dev;
struct sde_rotator_buf_handle *buf;
+ struct ion_client *iclient = rot_dev->mdata->iclient;
buf = kzalloc(sizeof(*buf), GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
buf->fd = vaddr;
- buf->secure = ctx->secure;
+ buf->secure = ctx->secure || ctx->secure_camera;
buf->ctx = ctx;
buf->rot_dev = rot_dev;
- buf->buffer = dma_buf_get(buf->fd);
-
- if (IS_ERR_OR_NULL(buf->buffer)) {
- SDEDEV_ERR(rot_dev->dev, "fail get dmabuf fd:%d r:%ld\n",
+ if (ctx->secure_camera) {
+ buf->handle = ion_import_dma_buf(iclient,
+ buf->fd);
+ if (IS_ERR_OR_NULL(buf->handle)) {
+ SDEDEV_ERR(rot_dev->dev,
+ "fail get ion_handler fd:%d r:%ld\n",
buf->fd, PTR_ERR(buf->buffer));
- goto error_dma_buf_get;
+ goto error_buf_get;
+ }
+ SDEDEV_DBG(rot_dev->dev,
+ "get ion_handle s:%d fd:%d buf:%pad\n",
+ buf->ctx->session_id,
+ buf->fd, &buf->handle);
+ } else {
+ buf->buffer = dma_buf_get(buf->fd);
+ if (IS_ERR_OR_NULL(buf->buffer)) {
+ SDEDEV_ERR(rot_dev->dev,
+ "fail get dmabuf fd:%d r:%ld\n",
+ buf->fd, PTR_ERR(buf->buffer));
+ goto error_buf_get;
+ }
+ SDEDEV_DBG(rot_dev->dev,
+ "get dmabuf s:%d fd:%d buf:%pad\n",
+ buf->ctx->session_id,
+ buf->fd, &buf->buffer);
}
- SDEDEV_DBG(rot_dev->dev, "get dmabuf s:%d fd:%d buf:%pad\n",
- buf->ctx->session_id,
- buf->fd, &buf->buffer);
return buf;
-error_dma_buf_get:
+error_buf_get:
kfree(buf);
return ERR_PTR(-ENOMEM);
}
@@ -745,22 +611,8 @@
static int sde_rotator_s_ctx_ctrl(struct sde_rotator_ctx *ctx,
s32 *ctx_ctrl, struct v4l2_ctrl *ctrl)
{
- struct sde_rotator_device *rot_dev = ctx->rot_dev;
- struct sde_rotation_config config;
- s32 prev_val;
- int ret;
-
- prev_val = *ctx_ctrl;
*ctx_ctrl = ctrl->val;
- sde_rotator_get_config_from_ctx(ctx, &config);
- ret = sde_rotator_session_config(rot_dev->mgr, ctx->private, &config);
- if (ret) {
- SDEDEV_WARN(rot_dev->dev, "fail %s:%d s:%d\n",
- ctrl->name, ctrl->val, ctx->session_id);
- *ctx_ctrl = prev_val;
- }
-
- return ret;
+ return 0;
}
/*
@@ -797,6 +649,9 @@
ret = sde_rotator_s_ctx_ctrl(ctx, &ctx->secure, ctrl);
break;
+ case V4L2_CID_SDE_ROTATOR_SECURE_CAMERA:
+ ret = sde_rotator_s_ctx_ctrl(ctx, &ctx->secure_camera, ctrl);
+ break;
default:
v4l2_warn(&rot_dev->v4l2_dev, "invalid control %d\n", ctrl->id);
ret = -EINVAL;
@@ -827,6 +682,17 @@
.step = 1,
};
+static const struct v4l2_ctrl_config sde_rotator_ctrl_secure_camera = {
+ .ops = &sde_rotator_ctrl_ops,
+ .id = V4L2_CID_SDE_ROTATOR_SECURE_CAMERA,
+ .name = "Secure Camera content",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 0,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+};
+
/*
* sde_rotator_ctx_show - show context state.
*/
@@ -847,7 +713,7 @@
SPRINT("rotate=%d\n", ctx->rotate);
SPRINT("hflip=%d\n", ctx->hflip);
SPRINT("vflip=%d\n", ctx->vflip);
- SPRINT("priority=%d\n", ctx->priority);
+ SPRINT("priority=%d\n", ctx->fh.prio);
SPRINT("secure=%d\n", ctx->secure);
SPRINT("timeperframe=%u %u\n", ctx->timeperframe.numerator,
ctx->timeperframe.denominator);
@@ -1021,7 +887,6 @@
ctx->hflip = 0;
ctx->vflip = 0;
ctx->rotate = 0;
- ctx->priority = V4L2_PRIORITY_DEFAULT;
ctx->secure = 0;
atomic_set(&ctx->command_pending, 0);
ctx->abort_pending = 0;
@@ -1045,10 +910,10 @@
file->private_data = &ctx->fh;
v4l2_fh_add(&ctx->fh);
- ctx->m2m_ctx = v4l2_m2m_ctx_init(rot_dev->m2m_dev,
+ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(rot_dev->m2m_dev,
ctx, sde_rotator_queue_init);
- if (IS_ERR_OR_NULL(ctx->m2m_ctx)) {
- ret = PTR_ERR(ctx->m2m_ctx);
+ if (IS_ERR_OR_NULL(ctx->fh.m2m_ctx)) {
+ ret = PTR_ERR(ctx->fh.m2m_ctx);
goto error_m2m_init;
}
@@ -1104,6 +969,8 @@
&sde_rotator_ctrl_ops, V4L2_CID_ROTATE, 0, 270, 90, 0);
v4l2_ctrl_new_custom(ctrl_handler,
&sde_rotator_ctrl_secure, NULL);
+ v4l2_ctrl_new_custom(ctrl_handler,
+ &sde_rotator_ctrl_secure_camera, NULL);
if (ctrl_handler->error) {
ret = ctrl_handler->error;
v4l2_ctrl_handler_free(ctrl_handler);
@@ -1155,8 +1022,8 @@
mutex_lock(&rot_dev->lock);
v4l2_ctrl_handler_free(&ctx->ctrl_handler);
SDEDEV_DBG(rot_dev->dev, "release streams s:%d\n", session_id);
- v4l2_m2m_streamoff(file, ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- v4l2_m2m_streamoff(file, ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
mutex_unlock(&rot_dev->lock);
SDEDEV_DBG(rot_dev->dev, "release submit work s:%d w:%x\n",
session_id, work_busy(&ctx->submit_work));
@@ -1174,7 +1041,7 @@
destroy_workqueue(ctx->work_queue.rot_work_queue);
sysfs_remove_group(&ctx->kobj, &sde_rotator_fs_attr_group);
kobject_put(&ctx->kobj);
- v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
kfree(ctx->vbinfo_out);
@@ -1199,7 +1066,7 @@
int ret;
mutex_lock(&rot_dev->lock);
- ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+ ret = v4l2_m2m_poll(file, ctx->fh.m2m_ctx, wait);
mutex_unlock(&rot_dev->lock);
return ret;
}
@@ -1228,8 +1095,9 @@
cap->bus_info[0] = 0;
strlcpy(cap->driver, SDE_ROTATOR_DRV_NAME, sizeof(cap->driver));
strlcpy(cap->card, SDE_ROTATOR_DRV_NAME, sizeof(cap->card));
- cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M |
- V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_CAPTURE;
+ cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M |
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_CAPTURE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -1243,12 +1111,21 @@
static int sde_rotator_enum_fmt_vid_cap(struct file *file,
void *fh, struct v4l2_fmtdesc *f)
{
- if (f->index >= ARRAY_SIZE(fmtdesc))
+ struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
+ struct sde_rotator_device *rot_dev = ctx->rot_dev;
+ struct sde_mdp_format_params *fmt;
+ u32 pixfmt;
+
+ pixfmt = sde_rotator_get_pixfmt(rot_dev->mgr, f->index, false);
+ if (!pixfmt)
return -EINVAL;
- f->pixelformat = fmtdesc[f->index].pixelformat;
- strlcpy(f->description, fmtdesc[f->index].description,
- sizeof(f->description));
+ fmt = sde_get_format_params(pixfmt);
+ if (!fmt)
+ return -EINVAL;
+
+ f->pixelformat = pixfmt;
+ strlcpy(f->description, fmt->description, sizeof(f->description));
return 0;
}
@@ -1262,12 +1139,21 @@
static int sde_rotator_enum_fmt_vid_out(struct file *file,
void *fh, struct v4l2_fmtdesc *f)
{
- if (f->index >= ARRAY_SIZE(fmtdesc))
+ struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
+ struct sde_rotator_device *rot_dev = ctx->rot_dev;
+ struct sde_mdp_format_params *fmt;
+ u32 pixfmt;
+
+ pixfmt = sde_rotator_get_pixfmt(rot_dev->mgr, f->index, true);
+ if (!pixfmt)
return -EINVAL;
- f->pixelformat = fmtdesc[f->index].pixelformat;
- strlcpy(f->description, fmtdesc[f->index].description,
- sizeof(f->description));
+ fmt = sde_get_format_params(pixfmt);
+ if (!fmt)
+ return -EINVAL;
+
+ f->pixelformat = pixfmt;
+ strlcpy(f->description, fmt->description, sizeof(f->description));
return 0;
}
@@ -1316,19 +1202,22 @@
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
struct sde_rotator_device *rot_dev = ctx->rot_dev;
struct sde_rotation_config config;
- int fmt_idx;
int ret;
- fmt_idx = sde_rotator_get_format_idx(ctx, f);
- if (fmt_idx < 0)
+ if ((f->fmt.pix.width == 0) || (f->fmt.pix.height == 0)) {
+ SDEDEV_WARN(ctx->rot_dev->dev,
+ "Not supporting 0 width/height: %dx%d\n",
+ f->fmt.pix.width, f->fmt.pix.height);
return -EINVAL;
+ }
sde_rot_mgr_lock(rot_dev->mgr);
sde_rotator_get_config_from_ctx(ctx, &config);
config.output.format = f->fmt.pix.pixelformat;
config.output.width = f->fmt.pix.width;
config.output.height = f->fmt.pix.height;
- ret = sde_rotator_verify_config(rot_dev->mgr, &config);
+ config.flags |= SDE_ROTATION_VERIFY_INPUT_ONLY;
+ ret = sde_rotator_verify_config_output(rot_dev->mgr, &config);
sde_rot_mgr_unlock(rot_dev->mgr);
if (ret) {
if ((config.output.width == f->fmt.pix.width) &&
@@ -1345,7 +1234,7 @@
}
sde_rotator_format_recalc(f);
- return 0;
+ return ret;
}
/*
@@ -1360,12 +1249,14 @@
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
struct sde_rotator_device *rot_dev = ctx->rot_dev;
struct sde_rotation_config config;
- int fmt_idx;
int ret;
- fmt_idx = sde_rotator_get_format_idx(ctx, f);
- if (fmt_idx < 0)
+ if ((f->fmt.pix.width == 0) || (f->fmt.pix.height == 0)) {
+ SDEDEV_WARN(ctx->rot_dev->dev,
+ "Not supporting 0 width/height: %dx%d\n",
+ f->fmt.pix.width, f->fmt.pix.height);
return -EINVAL;
+ }
sde_rot_mgr_lock(rot_dev->mgr);
sde_rotator_get_config_from_ctx(ctx, &config);
@@ -1373,7 +1264,7 @@
config.input.width = f->fmt.pix.width;
config.input.height = f->fmt.pix.height;
config.flags |= SDE_ROTATION_VERIFY_INPUT_ONLY;
- ret = sde_rotator_verify_config(rot_dev->mgr, &config);
+ ret = sde_rotator_verify_config_input(rot_dev->mgr, &config);
sde_rot_mgr_unlock(rot_dev->mgr);
if (ret) {
if ((config.input.width == f->fmt.pix.width) &&
@@ -1390,7 +1281,7 @@
}
sde_rotator_format_recalc(f);
- return 0;
+ return ret;
}
/*
@@ -1404,7 +1295,6 @@
{
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
struct sde_rotator_device *rot_dev = ctx->rot_dev;
- struct sde_rotation_config config;
int ret;
ret = sde_rotator_try_fmt_vid_cap(file, fh, f);
@@ -1426,12 +1316,6 @@
f->fmt.pix.field,
f->fmt.pix.width, f->fmt.pix.height);
- /* configure hal to current input/output setting */
- sde_rot_mgr_lock(rot_dev->mgr);
- sde_rotator_get_config_from_ctx(ctx, &config);
- sde_rotator_session_config(rot_dev->mgr, ctx->private, &config);
- sde_rot_mgr_unlock(rot_dev->mgr);
-
return 0;
}
@@ -1481,7 +1365,7 @@
{
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
- return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, req);
+ return v4l2_m2m_reqbufs(file, ctx->fh.m2m_ctx, req);
}
/*
@@ -1521,7 +1405,7 @@
ctx->vbinfo_out[idx].dqbuf_ts = NULL;
}
- ret = v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+ ret = v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf);
if (ret < 0)
SDEDEV_ERR(ctx->rot_dev->dev, "fail qbuf s:%d t:%d r:%d\n",
ctx->session_id, buf->type, ret);
@@ -1541,7 +1425,7 @@
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
int ret;
- ret = v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+ ret = v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf);
if (ret) {
SDEDEV_ERR(ctx->rot_dev->dev,
@@ -1592,7 +1476,7 @@
{
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
- return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+ return v4l2_m2m_querybuf(file, ctx->fh.m2m_ctx, buf);
}
/*
@@ -1605,12 +1489,41 @@
void *fh, enum v4l2_buf_type buf_type)
{
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
+ struct sde_rotator_device *rot_dev = ctx->rot_dev;
+ struct sde_rotation_config config;
+ struct vb2_queue *vq;
int ret;
SDEDEV_DBG(ctx->rot_dev->dev, "stream on s:%d t:%d\n",
ctx->session_id, buf_type);
- ret = v4l2_m2m_streamon(file, ctx->m2m_ctx, buf_type);
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
+ buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT ?
+ V4L2_BUF_TYPE_VIDEO_CAPTURE :
+ V4L2_BUF_TYPE_VIDEO_OUTPUT);
+
+ if (!vq) {
+ SDEDEV_ERR(ctx->rot_dev->dev, "fail to get vq on s:%d t:%d\n",
+ ctx->session_id, buf_type);
+ return -EINVAL;
+ }
+
+ if (vb2_is_streaming(vq)) {
+ sde_rot_mgr_lock(rot_dev->mgr);
+ sde_rotator_get_config_from_ctx(ctx, &config);
+ config.flags &= ~SDE_ROTATION_VERIFY_INPUT_ONLY;
+ ret = sde_rotator_session_config(rot_dev->mgr, ctx->private,
+ &config);
+ sde_rot_mgr_unlock(rot_dev->mgr);
+ if (ret < 0) {
+ SDEDEV_ERR(rot_dev->dev,
+ "fail config in stream on s:%d t:%d r:%d\n",
+ ctx->session_id, buf_type, ret);
+ return ret;
+ }
+ }
+
+ ret = v4l2_m2m_streamon(file, ctx->fh.m2m_ctx, buf_type);
if (ret < 0)
SDEDEV_ERR(ctx->rot_dev->dev, "fail stream on s:%d t:%d\n",
ctx->session_id, buf_type);
@@ -1633,7 +1546,7 @@
SDEDEV_DBG(ctx->rot_dev->dev, "stream off s:%d t:%d\n",
ctx->session_id, buf_type);
- ret = v4l2_m2m_streamoff(file, ctx->m2m_ctx, buf_type);
+ ret = v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx, buf_type);
if (ret < 0)
SDEDEV_ERR(ctx->rot_dev->dev, "fail stream off s:%d t:%d\n",
ctx->session_id, buf_type);
@@ -1715,7 +1628,6 @@
{
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
struct sde_rotator_device *rot_dev = ctx->rot_dev;
- struct sde_rotation_config config;
struct sde_rotation_item item;
struct v4l2_rect rect;
@@ -1788,12 +1700,6 @@
return -EINVAL;
}
- /* configure hal to current input/output setting */
- sde_rot_mgr_lock(rot_dev->mgr);
- sde_rotator_get_config_from_ctx(ctx, &config);
- sde_rotator_session_config(rot_dev->mgr, ctx->private, &config);
- sde_rot_mgr_unlock(rot_dev->mgr);
-
return 0;
}
@@ -1845,38 +1751,6 @@
}
/*
- * sde_rotator_g_priority - Get the priority
- * @file: Pointer to file struct.
- * @fh: V4l2 file handle.
- * @p: Pointer to priority enumeration.
- */
-static int sde_rotator_g_priority(struct file *file, void *fh,
- enum v4l2_priority *p)
-{
- struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
-
- if (!p)
- return -EINVAL;
- *p = ctx->priority;
- return 0;
-}
-
-/*
- * sde_rotator_s_priority - Set the priority
- * @file: Pointer to file struct.
- * @fh: V4l2 file handle.
- * @p: Pointer to priority enumeration.
- */
-static int sde_rotator_s_priority(struct file *file, void *fh,
- enum v4l2_priority p)
-{
- struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
-
- ctx->priority = p;
- return 0;
-}
-
-/*
* sde_rotator_private_ioctl - V4l2 private ioctl handler.
* @file: Pointer to file struct.
* @fd: V4l2 device file handle.
@@ -1891,6 +1765,7 @@
sde_rotator_ctx_from_fh(file->private_data);
struct sde_rotator_device *rot_dev = ctx->rot_dev;
struct msm_sde_rotator_fence *fence = arg;
+ struct msm_sde_rotator_comp_ratio *comp_ratio = arg;
struct sde_rotator_vbinfo *vbinfo;
switch (cmd) {
@@ -1969,6 +1844,52 @@
ctx->session_id, fence->index,
fence->fd);
break;
+ case VIDIOC_S_SDE_ROTATOR_COMP_RATIO:
+ if (!comp_ratio)
+ return -EINVAL;
+ else if (!comp_ratio->numer || !comp_ratio->denom)
+ return -EINVAL;
+ else if (comp_ratio->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ comp_ratio->index < ctx->nbuf_out)
+ vbinfo = &ctx->vbinfo_out[comp_ratio->index];
+ else if (comp_ratio->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ comp_ratio->index < ctx->nbuf_cap)
+ vbinfo = &ctx->vbinfo_cap[comp_ratio->index];
+ else
+ return -EINVAL;
+
+ vbinfo->comp_ratio.numer = comp_ratio->numer;
+ vbinfo->comp_ratio.denom = comp_ratio->denom;
+
+ SDEDEV_DBG(rot_dev->dev,
+ "VIDIOC_S_SDE_ROTATOR_COMP_RATIO s:%d i:%d t:%d cr:%u/%u\n",
+ ctx->session_id, comp_ratio->index,
+ comp_ratio->type,
+ vbinfo->comp_ratio.numer,
+ vbinfo->comp_ratio.denom);
+ break;
+ case VIDIOC_G_SDE_ROTATOR_COMP_RATIO:
+ if (!comp_ratio)
+ return -EINVAL;
+ else if (comp_ratio->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ comp_ratio->index < ctx->nbuf_out)
+ vbinfo = &ctx->vbinfo_out[comp_ratio->index];
+ else if (comp_ratio->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ comp_ratio->index < ctx->nbuf_cap)
+ vbinfo = &ctx->vbinfo_cap[comp_ratio->index];
+ else
+ return -EINVAL;
+
+ comp_ratio->numer = vbinfo->comp_ratio.numer;
+ comp_ratio->denom = vbinfo->comp_ratio.denom;
+
+ SDEDEV_DBG(rot_dev->dev,
+ "VIDIOC_G_SDE_ROTATOR_COMP_RATIO s:%d i:%d t:%d cr:%u/%u\n",
+ ctx->session_id, comp_ratio->index,
+ comp_ratio->type,
+ comp_ratio->numer,
+ comp_ratio->denom);
+ break;
default:
SDEDEV_WARN(rot_dev->dev, "invalid ioctl type %x\n", cmd);
return -ENOTTY;
@@ -2008,6 +1929,24 @@
break;
}
+ case VIDIOC_S_SDE_ROTATOR_COMP_RATIO:
+ case VIDIOC_G_SDE_ROTATOR_COMP_RATIO:
+ {
+ struct msm_sde_rotator_comp_ratio comp_ratio;
+
+ if (copy_from_user(&comp_ratio, (void __user *)arg,
+ sizeof(struct msm_sde_rotator_comp_ratio)))
+ return -EFAULT;
+
+ ret = sde_rotator_private_ioctl(file, file->private_data,
+ 0, cmd, (void *)&comp_ratio);
+
+ if (copy_to_user((void __user *)arg, &comp_ratio,
+ sizeof(struct msm_sde_rotator_comp_ratio)))
+ return -EFAULT;
+
+ break;
+ }
default:
ret = -ENOIOCTLCMD;
break;
@@ -2040,8 +1979,6 @@
.vidioc_s_crop = sde_rotator_s_crop,
.vidioc_g_parm = sde_rotator_g_parm,
.vidioc_s_parm = sde_rotator_s_parm,
- .vidioc_g_priority = sde_rotator_g_priority,
- .vidioc_s_priority = sde_rotator_s_priority,
.vidioc_default = sde_rotator_private_ioctl,
.vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
@@ -2056,8 +1993,8 @@
*/
static void sde_rotator_retire_handler(struct work_struct *work)
{
- struct vb2_buffer *src_buf;
- struct vb2_buffer *dst_buf;
+ struct vb2_v4l2_buffer *src_buf;
+ struct vb2_v4l2_buffer *dst_buf;
struct sde_rotator_ctx *ctx;
struct sde_rotator_device *rot_dev;
@@ -2092,11 +2029,11 @@
}
/* pending request. reschedule this context. */
- v4l2_m2m_try_schedule(ctx->m2m_ctx);
+ v4l2_m2m_try_schedule(ctx->fh.m2m_ctx);
} else {
/* no pending request. acknowledge the usual way. */
- src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
- dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+ src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
if (!src_buf || !dst_buf) {
SDEDEV_ERR(rot_dev->dev,
@@ -2110,7 +2047,7 @@
wake_up(&ctx->wait_queue);
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
- v4l2_m2m_job_finish(rot_dev->m2m_dev, ctx->m2m_ctx);
+ v4l2_m2m_job_finish(rot_dev->m2m_dev, ctx->fh.m2m_ctx);
}
mutex_unlock(&rot_dev->lock);
}
@@ -2150,17 +2087,19 @@
goto error_null_buffer;
}
- vbinfo_out = &ctx->vbinfo_out[src_buf->v4l2_buf.index];
- vbinfo_cap = &ctx->vbinfo_cap[dst_buf->v4l2_buf.index];
+ vbinfo_out = &ctx->vbinfo_out[src_buf->index];
+ vbinfo_cap = &ctx->vbinfo_cap[dst_buf->index];
SDEDEV_DBG(rot_dev->dev,
- "process buffer s:%d.%u src:(%u,%u,%u,%u) dst:(%u,%u,%u,%u) rot:%d flip:%d/%d sec:%d\n",
+ "process buffer s:%d.%u src:(%u,%u,%u,%u) dst:(%u,%u,%u,%u) rot:%d flip:%d/%d sec:%d src_cr:%u/%u dst_cr:%u/%u\n",
ctx->session_id, vbinfo_cap->fence_ts,
ctx->crop_out.left, ctx->crop_out.top,
ctx->crop_out.width, ctx->crop_out.height,
ctx->crop_cap.left, ctx->crop_cap.top,
ctx->crop_cap.width, ctx->crop_cap.height,
- ctx->rotate, ctx->hflip, ctx->vflip, ctx->secure);
+ ctx->rotate, ctx->hflip, ctx->vflip, ctx->secure,
+ vbinfo_out->comp_ratio.numer, vbinfo_out->comp_ratio.denom,
+ vbinfo_cap->comp_ratio.numer, vbinfo_cap->comp_ratio.denom);
/* allocate slot for timestamp */
ts = stats->ts[stats->count++ % SDE_ROTATOR_NUM_EVENTS];
@@ -2173,7 +2112,7 @@
trace_rot_entry_fence(
ctx->session_id, vbinfo_cap->fence_ts,
- ctx->priority,
+ ctx->fh.prio,
(ctx->rotate << 0) | (ctx->hflip << 8) |
(ctx->hflip << 9) | (ctx->secure << 10),
ctx->format_out.fmt.pix.pixelformat,
@@ -2215,15 +2154,19 @@
sde_rotator_get_item_from_ctx(ctx, &item);
item.flags |= SDE_ROTATION_EXT_DMA_BUF;
item.input.planes[0].buffer = src_handle->buffer;
+ item.input.planes[0].handle = src_handle->handle;
item.input.planes[0].offset = src_handle->addr;
item.input.planes[0].stride = ctx->format_out.fmt.pix.bytesperline;
item.input.plane_count = 1;
item.input.fence = NULL;
+ item.input.comp_ratio = vbinfo_out->comp_ratio;
item.output.planes[0].buffer = dst_handle->buffer;
+ item.output.planes[0].handle = dst_handle->handle;
item.output.planes[0].offset = dst_handle->addr;
item.output.planes[0].stride = ctx->format_cap.fmt.pix.bytesperline;
item.output.plane_count = 1;
item.output.fence = NULL;
+ item.output.comp_ratio = vbinfo_cap->comp_ratio;
item.sequence_id = vbinfo_cap->fence_ts;
item.ts = ts;
@@ -2267,8 +2210,8 @@
{
struct sde_rotator_ctx *ctx;
struct sde_rotator_device *rot_dev;
- struct vb2_buffer *src_buf;
- struct vb2_buffer *dst_buf;
+ struct vb2_v4l2_buffer *src_buf;
+ struct vb2_v4l2_buffer *dst_buf;
int ret;
ctx = container_of(work, struct sde_rotator_ctx, submit_work);
@@ -2293,17 +2236,18 @@
}
/* submit new request */
- dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
- src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+ src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
sde_rot_mgr_lock(rot_dev->mgr);
- ret = sde_rotator_process_buffers(ctx, src_buf, dst_buf);
+ ret = sde_rotator_process_buffers(ctx, &src_buf->vb2_buf,
+ &dst_buf->vb2_buf);
sde_rot_mgr_unlock(rot_dev->mgr);
if (ret) {
SDEDEV_ERR(rot_dev->dev,
"fail process buffer in submit s:%d\n",
ctx->session_id);
/* advance to device run to clean up buffers */
- v4l2_m2m_try_schedule(ctx->m2m_ctx);
+ v4l2_m2m_try_schedule(ctx->fh.m2m_ctx);
}
mutex_unlock(&rot_dev->lock);
@@ -2317,8 +2261,8 @@
{
struct sde_rotator_ctx *ctx = priv;
struct sde_rotator_device *rot_dev;
- struct vb2_buffer *src_buf;
- struct vb2_buffer *dst_buf;
+ struct vb2_v4l2_buffer *src_buf;
+ struct vb2_v4l2_buffer *dst_buf;
int ret;
if (!ctx || !ctx->rot_dev) {
@@ -2365,8 +2309,8 @@
goto error_process_buffers;
}
- src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
- dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+ src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
if (!src_buf || !dst_buf) {
SDEDEV_ERR(rot_dev->dev,
"null buffer in device run s:%d sb:%p db:%p\n",
@@ -2379,7 +2323,7 @@
wake_up(&ctx->wait_queue);
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
- v4l2_m2m_job_finish(rot_dev->m2m_dev, ctx->m2m_ctx);
+ v4l2_m2m_job_finish(rot_dev->m2m_dev, ctx->fh.m2m_ctx);
} else {
/* pending request not complete. something wrong. */
SDEDEV_ERR(rot_dev->dev,
@@ -2399,8 +2343,8 @@
/* no pending request. submit buffer the usual way. */
atomic_inc(&ctx->command_pending);
- dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
- src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+ src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
if (!src_buf || !dst_buf) {
SDEDEV_ERR(rot_dev->dev,
"null buffer in device run s:%d sb:%p db:%p\n",
@@ -2410,7 +2354,8 @@
}
sde_rot_mgr_lock(rot_dev->mgr);
- ret = sde_rotator_process_buffers(ctx, src_buf, dst_buf);
+ ret = sde_rotator_process_buffers(ctx, &src_buf->vb2_buf,
+ &dst_buf->vb2_buf);
sde_rot_mgr_unlock(rot_dev->mgr);
if (ret) {
SDEDEV_ERR(rot_dev->dev,
@@ -2428,14 +2373,14 @@
error_empty_buffer:
atomic_dec(&ctx->command_pending);
wake_up(&ctx->wait_queue);
- src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
- dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+ src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
if (src_buf)
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
if (dst_buf)
v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
sde_rotator_resync_timeline(ctx->work_queue.timeline);
- v4l2_m2m_job_finish(rot_dev->m2m_dev, ctx->m2m_ctx);
+ v4l2_m2m_job_finish(rot_dev->m2m_dev, ctx->fh.m2m_ctx);
}
/*
@@ -2455,7 +2400,7 @@
rot_dev = ctx->rot_dev;
SDEDEV_DBG(rot_dev->dev, "job abort s:%d\n", ctx->session_id);
- v4l2_m2m_job_finish(rot_dev->m2m_dev, ctx->m2m_ctx);
+ v4l2_m2m_job_finish(rot_dev->m2m_dev, ctx->fh.m2m_ctx);
}
/*
@@ -2490,8 +2435,8 @@
SDEDEV_DBG(rot_dev->dev,
"submit job s:%d sc:%d dc:%d p:%d\n",
ctx->session_id,
- v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx),
- v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx),
+ v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx),
+ v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx),
atomic_read(&ctx->command_pending));
atomic_inc(&ctx->command_pending);
queue_work(ctx->work_queue.rot_work_queue, &ctx->submit_work);
@@ -2559,6 +2504,9 @@
rot_dev->early_submit = SDE_ROTATOR_EARLY_SUBMIT;
rot_dev->fence_timeout = SDE_ROTATOR_FENCE_TIMEOUT;
rot_dev->streamoff_timeout = SDE_ROTATOR_STREAM_OFF_TIMEOUT;
+ rot_dev->min_rot_clk = 0;
+ rot_dev->min_bw = 0;
+ rot_dev->min_overhead_us = 0;
rot_dev->drvdata = sde_rotator_get_drv_data(&pdev->dev);
rot_dev->pdev = pdev;
@@ -2679,7 +2627,6 @@
.resume = sde_rotator_resume,
.driver = {
.name = SDE_ROTATOR_DRV_NAME,
- .owner = THIS_MODULE,
.of_match_table = sde_rotator_dt_match,
.pm = &sde_rotator_pm_ops,
},
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
index b3d51a9..a46c0b5 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
@@ -33,7 +33,7 @@
#define SDE_ROTATOR_DRV_NAME "sde_rotator"
/* Event logging constants */
-#define SDE_ROTATOR_NUM_EVENTS 256
+#define SDE_ROTATOR_NUM_EVENTS 4096
#define SDE_ROTATOR_NUM_TIMESTAMPS SDE_ROTATOR_TS_MAX
struct sde_rotator_device;
@@ -48,6 +48,7 @@
* @addr: Address of rotator mmu mapped buffer.
* @secure: Non-secure/secure buffer.
* @buffer: Pointer to dma buf associated with this fd.
+ * @ihandle: ion handle associated with this fd
*/
struct sde_rotator_buf_handle {
int fd;
@@ -57,6 +58,7 @@
ion_phys_addr_t addr;
int secure;
struct dma_buf *buffer;
+ struct ion_handle *handle;
};
/*
@@ -66,6 +68,7 @@
* @fence_ts: completion timestamp associated with fd
* @qbuf_ts: timestamp associated with buffer queue event
* @dqbuf_ts: Pointer to timestamp associated with buffer dequeue event
+ * @comp_ratio: compression ratio of this buffer
*/
struct sde_rotator_vbinfo {
int fd;
@@ -73,6 +76,7 @@
u32 fence_ts;
ktime_t qbuf_ts;
ktime_t *dqbuf_ts;
+ struct sde_mult_factor comp_ratio;
};
/*
@@ -80,7 +84,6 @@
* @kobj: kernel object of this context
* @rot_dev: Pointer to rotator device.
* @fh: V4l2 file handle.
- * @m2m_ctx: Memory to memory context.
* @ctrl_handler: control handler
* @format_cap: Current capture format.
* @format_out: Current output format.
@@ -91,7 +94,6 @@
* @hflip: horizontal flip (1-flip)
* @vflip: vertical flip (1-flip)
* @rotate: rotation angle (0,90,180,270)
- * @priority: Priority of this context
* @secure: Non-secure (0) / Secure processing
* @command_pending: Number of pending transaction in h/w
* @abort_pending: True if abort is requested for async handling.
@@ -110,7 +112,6 @@
struct kobject kobj;
struct sde_rotator_device *rot_dev;
struct v4l2_fh fh;
- struct v4l2_m2m_ctx *m2m_ctx;
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_format format_cap;
struct v4l2_format format_out;
@@ -121,8 +122,8 @@
s32 hflip;
s32 vflip;
s32 rotate;
- enum v4l2_priority priority;
s32 secure;
+ s32 secure_camera;
atomic_t command_pending;
int abort_pending;
int nbuf_cap;
@@ -164,6 +165,9 @@
* @session_id: Next context session identifier
* @fence_timeout: Timeout value in msec for fence wait
* @streamoff_timeout: Timeout value in msec for stream off
+ * @min_rot_clk: Override the minimum rotator clock from perf calculation
+ * @min_bw: Override the minimum bandwidth from perf calculation
+ * @min_overhead_us: Override the minimum overhead in us from perf calculation
* @debugfs_root: Pointer to debugfs directory entry.
* @stats: placeholder for rotator statistics
*/
@@ -181,8 +185,12 @@
u32 session_id;
u32 fence_timeout;
u32 streamoff_timeout;
+ u32 min_rot_clk;
+ u32 min_bw;
+ u32 min_overhead_us;
struct sde_rotator_statistics stats;
struct dentry *debugfs_root;
+ struct dentry *perf_root;
};
static inline
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c
index 8e57933..3b36b6b 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c
@@ -16,9 +16,10 @@
#include "sde_rotator_formats.h"
#include "sde_rotator_util.h"
-#define FMT_RGB_565(fmt, frame_fmt, flag_arg, e0, e1, e2, isubwc) \
+#define FMT_RGB_565(fmt, desc, frame_fmt, flag_arg, e0, e1, e2, isubwc) \
{ \
.format = (fmt), \
+ .description = (desc), \
.flag = flag_arg, \
.fetch_planes = SDE_MDP_PLANE_INTERLEAVED, \
.unpack_tight = 1, \
@@ -37,9 +38,10 @@
.is_ubwc = isubwc, \
}
-#define FMT_RGB_888(fmt, frame_fmt, flag_arg, e0, e1, e2, isubwc) \
+#define FMT_RGB_888(fmt, desc, frame_fmt, flag_arg, e0, e1, e2, isubwc) \
{ \
.format = (fmt), \
+ .description = (desc), \
.flag = flag_arg, \
.fetch_planes = SDE_MDP_PLANE_INTERLEAVED, \
.unpack_tight = 1, \
@@ -58,10 +60,11 @@
.is_ubwc = isubwc, \
}
-#define FMT_RGB_8888(fmt, frame_fmt, flag_arg, \
+#define FMT_RGB_8888(fmt, desc, frame_fmt, flag_arg, \
alpha_en, e0, e1, e2, e3, isubwc) \
{ \
.format = (fmt), \
+ .description = (desc), \
.flag = flag_arg, \
.fetch_planes = SDE_MDP_PLANE_INTERLEAVED, \
.unpack_tight = 1, \
@@ -93,10 +96,11 @@
.unpack_tight = 1, \
.unpack_align_msb = 0
-#define FMT_YUV_PSEUDO(fmt, frame_fmt, samp, pixel_type, \
+#define FMT_YUV_PSEUDO(fmt, desc, frame_fmt, samp, pixel_type, \
flag_arg, e0, e1, isubwc) \
{ \
FMT_YUV_COMMON(fmt), \
+ .description = (desc), \
.flag = flag_arg, \
.fetch_planes = SDE_MDP_PLANE_PSEUDO_PLANAR, \
.chroma_sample = samp, \
@@ -108,10 +112,11 @@
.is_ubwc = isubwc, \
}
-#define FMT_YUV_PLANR(fmt, frame_fmt, samp, \
+#define FMT_YUV_PLANR(fmt, desc, frame_fmt, samp, \
flag_arg, e0, e1) \
{ \
FMT_YUV_COMMON(fmt), \
+ .description = (desc), \
.flag = flag_arg, \
.fetch_planes = SDE_MDP_PLANE_PLANAR, \
.chroma_sample = samp, \
@@ -123,9 +128,10 @@
.is_ubwc = SDE_MDP_COMPRESS_NONE, \
}
-#define FMT_RGB_1555(fmt, alpha_en, flag_arg, e0, e1, e2, e3) \
+#define FMT_RGB_1555(fmt, desc, alpha_en, flag_arg, e0, e1, e2, e3) \
{ \
.format = (fmt), \
+ .description = (desc), \
.flag = flag_arg, \
.fetch_planes = SDE_MDP_PLANE_INTERLEAVED, \
.unpack_tight = 1, \
@@ -145,9 +151,10 @@
.is_ubwc = SDE_MDP_COMPRESS_NONE, \
}
-#define FMT_RGB_4444(fmt, alpha_en, flag_arg, e0, e1, e2, e3) \
+#define FMT_RGB_4444(fmt, desc, alpha_en, flag_arg, e0, e1, e2, e3) \
{ \
.format = (fmt), \
+ .description = (desc), \
.flag = flag_arg, \
.fetch_planes = SDE_MDP_PLANE_INTERLEAVED, \
.unpack_tight = 1, \
@@ -167,10 +174,11 @@
.is_ubwc = SDE_MDP_COMPRESS_NONE, \
}
-#define FMT_RGB_1010102(fmt, frame_fmt, flag_arg, \
+#define FMT_RGB_1010102(fmt, desc, frame_fmt, flag_arg, \
alpha_en, e0, e1, e2, e3, isubwc) \
{ \
.format = (fmt), \
+ .description = (desc), \
.flag = flag_arg, \
.fetch_planes = SDE_MDP_PLANE_INTERLEAVED, \
.unpack_tight = 1, \
@@ -190,7 +198,6 @@
.is_ubwc = isubwc, \
}
-#define VALID_ROT_WB_ALL (VALID_ROT_WB_FORMAT | VALID_ROT_R3_WB_FORMAT)
/*
* UBWC formats table:
* This table holds the UBWC formats supported.
@@ -200,7 +207,8 @@
static struct sde_mdp_format_params_ubwc sde_mdp_format_ubwc_map[] = {
{
.mdp_format = FMT_RGB_565(SDE_PIX_FMT_RGB_565_UBWC,
- SDE_MDP_FMT_TILE_A5X, VALID_ROT_WB_ALL,
+ "SDE/RGB_565_UBWC",
+ SDE_MDP_FMT_TILE_A5X, 0,
C2_R_Cr, C0_G_Y, C1_B_Cb, SDE_MDP_COMPRESS_UBWC),
.micro = {
.tile_height = 4,
@@ -209,7 +217,8 @@
},
{
.mdp_format = FMT_RGB_8888(SDE_PIX_FMT_RGBA_8888_UBWC,
- SDE_MDP_FMT_TILE_A5X, VALID_ROT_WB_ALL, 1,
+ "SDE/RGBA_8888_UBWC",
+ SDE_MDP_FMT_TILE_A5X, 0, 1,
C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA,
SDE_MDP_COMPRESS_UBWC),
.micro = {
@@ -219,7 +228,8 @@
},
{
.mdp_format = FMT_RGB_8888(SDE_PIX_FMT_RGBX_8888_UBWC,
- SDE_MDP_FMT_TILE_A5X, VALID_ROT_WB_ALL, 0,
+ "SDE/RGBX_8888_UBWC",
+ SDE_MDP_FMT_TILE_A5X, 0, 0,
C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA,
SDE_MDP_COMPRESS_UBWC),
.micro = {
@@ -229,9 +239,10 @@
},
{
.mdp_format = FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CBCR_H2V2_UBWC,
+ "SDE/Y_CBCR_H2V2_UBWC",
SDE_MDP_FMT_TILE_A5X, SDE_MDP_CHROMA_420,
SDE_MDP_PIXEL_NORMAL,
- VALID_ROT_WB_ALL, C1_B_Cb, C2_R_Cr,
+ 0, C1_B_Cb, C2_R_Cr,
SDE_MDP_COMPRESS_UBWC),
.micro = {
.tile_height = 8,
@@ -240,7 +251,8 @@
},
{
.mdp_format = FMT_RGB_1010102(SDE_PIX_FMT_RGBA_1010102_UBWC,
- SDE_MDP_FMT_TILE_A5X, VALID_ROT_R3_WB_FORMAT, 1,
+ "SDE/RGBA_1010102_UBWC",
+ SDE_MDP_FMT_TILE_A5X, 0, 1,
C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA,
SDE_MDP_COMPRESS_UBWC),
.micro = {
@@ -250,7 +262,8 @@
},
{
.mdp_format = FMT_RGB_1010102(SDE_PIX_FMT_RGBX_1010102_UBWC,
- SDE_MDP_FMT_TILE_A5X, VALID_ROT_R3_WB_FORMAT, 0,
+ "SDE/RGBX_1010102_UBWC",
+ SDE_MDP_FMT_TILE_A5X, 0, 0,
C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA,
SDE_MDP_COMPRESS_UBWC),
.micro = {
@@ -260,9 +273,10 @@
},
{
.mdp_format = FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CBCR_H2V2_TP10_UBWC,
+ "SDE/Y_CBCR_H2V2_TP10_UBWC",
SDE_MDP_FMT_TILE_A5X, SDE_MDP_CHROMA_420,
SDE_MDP_PIXEL_10BIT,
- VALID_ROT_R3_WB_FORMAT | VALID_MDP_WB_INTF_FORMAT,
+ 0,
C1_B_Cb, C2_R_Cr, SDE_MDP_COMPRESS_UBWC),
.micro = {
.tile_height = 4,
@@ -273,84 +287,89 @@
static struct sde_mdp_format_params sde_mdp_format_map[] = {
FMT_RGB_565(
- SDE_PIX_FMT_RGB_565, SDE_MDP_FMT_LINEAR, VALID_ROT_WB_ALL |
- VALID_MDP_WB_INTF_FORMAT, C1_B_Cb, C0_G_Y, C2_R_Cr,
- SDE_MDP_COMPRESS_NONE),
+ SDE_PIX_FMT_RGB_565, "RGB_565", SDE_MDP_FMT_LINEAR,
+ 0, C1_B_Cb, C0_G_Y, C2_R_Cr, SDE_MDP_COMPRESS_NONE),
FMT_RGB_565(
- SDE_PIX_FMT_BGR_565, SDE_MDP_FMT_LINEAR, VALID_ROT_WB_ALL |
- VALID_MDP_WB_INTF_FORMAT, C2_R_Cr, C0_G_Y, C1_B_Cb,
- SDE_MDP_COMPRESS_NONE),
+ SDE_PIX_FMT_BGR_565, "BGR_565", SDE_MDP_FMT_LINEAR,
+ 0, C2_R_Cr, C0_G_Y, C1_B_Cb, SDE_MDP_COMPRESS_NONE),
FMT_RGB_888(
- SDE_PIX_FMT_RGB_888, SDE_MDP_FMT_LINEAR, VALID_ROT_WB_ALL |
- VALID_MDP_WB_INTF_FORMAT, C2_R_Cr, C0_G_Y, C1_B_Cb,
- SDE_MDP_COMPRESS_NONE),
+ SDE_PIX_FMT_RGB_888, "RGB_888", SDE_MDP_FMT_LINEAR,
+ 0, C2_R_Cr, C0_G_Y, C1_B_Cb, SDE_MDP_COMPRESS_NONE),
FMT_RGB_888(
- SDE_PIX_FMT_BGR_888, SDE_MDP_FMT_LINEAR, VALID_ROT_WB_ALL |
- VALID_MDP_WB_INTF_FORMAT, C1_B_Cb, C0_G_Y, C2_R_Cr,
+ SDE_PIX_FMT_BGR_888, "BGR_888", SDE_MDP_FMT_LINEAR,
+ 0, C1_B_Cb, C0_G_Y, C2_R_Cr, SDE_MDP_COMPRESS_NONE),
+
+ FMT_RGB_8888(
+ SDE_PIX_FMT_ABGR_8888, "SDE/ABGR_8888", SDE_MDP_FMT_LINEAR,
+ 0, 1, C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr,
SDE_MDP_COMPRESS_NONE),
FMT_RGB_8888(
- SDE_PIX_FMT_ABGR_8888, SDE_MDP_FMT_LINEAR,
- VALID_ROT_WB_ALL, 1, C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr,
+ SDE_PIX_FMT_XRGB_8888, "SDE/XRGB_8888", SDE_MDP_FMT_LINEAR,
+ 0, 0, C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb,
+ SDE_MDP_COMPRESS_NONE),
+ FMT_RGB_8888(
+ SDE_PIX_FMT_ARGB_8888, "SDE/ARGB_8888", SDE_MDP_FMT_LINEAR,
+ 0, 1, C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb,
+ SDE_MDP_COMPRESS_NONE),
+ FMT_RGB_8888(
+ SDE_PIX_FMT_RGBA_8888, "SDE/RGBA_8888", SDE_MDP_FMT_LINEAR,
+ 0, 1, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA,
+ SDE_MDP_COMPRESS_NONE),
+ FMT_RGB_8888(
+ SDE_PIX_FMT_RGBX_8888, "SDE/RGBX_8888", SDE_MDP_FMT_LINEAR,
+ 0, 0, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA,
+ SDE_MDP_COMPRESS_NONE),
+ FMT_RGB_8888(
+ SDE_PIX_FMT_BGRA_8888, "SDE/BGRA_8888", SDE_MDP_FMT_LINEAR,
+ 0, 1, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA,
+ SDE_MDP_COMPRESS_NONE),
+ FMT_RGB_8888(
+ SDE_PIX_FMT_BGRX_8888, "SDE/BGRX_8888", SDE_MDP_FMT_LINEAR,
+ 0, 0, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA,
+ SDE_MDP_COMPRESS_NONE),
+ FMT_RGB_8888(
+ SDE_PIX_FMT_XBGR_8888, "SDE/XBGR_8888", SDE_MDP_FMT_LINEAR,
+ 0, 0, C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr,
SDE_MDP_COMPRESS_NONE),
- FMT_RGB_8888(
- SDE_PIX_FMT_XRGB_8888, SDE_MDP_FMT_LINEAR,
- VALID_ROT_WB_ALL | VALID_MDP_WB_INTF_FORMAT,
- 0, C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, SDE_MDP_COMPRESS_NONE),
- FMT_RGB_8888(
- SDE_PIX_FMT_ARGB_8888, SDE_MDP_FMT_LINEAR,
- VALID_ROT_WB_ALL, 1, C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb,
- SDE_MDP_COMPRESS_NONE),
- FMT_RGB_8888(
- SDE_PIX_FMT_RGBA_8888, SDE_MDP_FMT_LINEAR,
- VALID_ROT_WB_ALL, 1, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA,
- SDE_MDP_COMPRESS_NONE),
- FMT_RGB_8888(
- SDE_PIX_FMT_RGBX_8888, SDE_MDP_FMT_LINEAR,
- VALID_ROT_WB_ALL | VALID_MDP_WB_INTF_FORMAT,
- 0, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, SDE_MDP_COMPRESS_NONE),
- FMT_RGB_8888(
- SDE_PIX_FMT_BGRA_8888, SDE_MDP_FMT_LINEAR,
- VALID_ROT_WB_ALL, 1, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA,
- SDE_MDP_COMPRESS_NONE),
- FMT_RGB_8888(
- SDE_PIX_FMT_BGRX_8888, SDE_MDP_FMT_LINEAR,
- VALID_ROT_WB_ALL | VALID_MDP_WB_INTF_FORMAT,
- 0, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, SDE_MDP_COMPRESS_NONE),
-
- FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CRCB_H2V1, SDE_MDP_FMT_LINEAR,
+ FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CRCB_H2V1, "Y_CRCB_H2V1",
+ SDE_MDP_FMT_LINEAR,
SDE_MDP_CHROMA_H2V1, SDE_MDP_PIXEL_NORMAL,
- VALID_ROT_WB_ALL, C2_R_Cr, C1_B_Cb, SDE_MDP_COMPRESS_NONE),
- FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CBCR_H2V1, SDE_MDP_FMT_LINEAR,
+ 0, C2_R_Cr, C1_B_Cb, SDE_MDP_COMPRESS_NONE),
+ FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CBCR_H2V1, "Y_CBCR_H2V1",
+ SDE_MDP_FMT_LINEAR,
SDE_MDP_CHROMA_H2V1, SDE_MDP_PIXEL_NORMAL,
- VALID_ROT_WB_ALL, C1_B_Cb, C2_R_Cr, SDE_MDP_COMPRESS_NONE),
- FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CRCB_H1V2, SDE_MDP_FMT_LINEAR,
+ 0, C1_B_Cb, C2_R_Cr, SDE_MDP_COMPRESS_NONE),
+ FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CRCB_H1V2, "Y_CRCB_H1V2",
+ SDE_MDP_FMT_LINEAR,
SDE_MDP_CHROMA_H1V2, SDE_MDP_PIXEL_NORMAL,
- VALID_ROT_WB_ALL, C2_R_Cr, C1_B_Cb, SDE_MDP_COMPRESS_NONE),
- FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CBCR_H1V2, SDE_MDP_FMT_LINEAR,
+ 0, C2_R_Cr, C1_B_Cb, SDE_MDP_COMPRESS_NONE),
+ FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CBCR_H1V2, "Y_CBCR_H1V2",
+ SDE_MDP_FMT_LINEAR,
SDE_MDP_CHROMA_H1V2, SDE_MDP_PIXEL_NORMAL,
- VALID_ROT_WB_ALL, C1_B_Cb, C2_R_Cr, SDE_MDP_COMPRESS_NONE),
- FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CRCB_H2V2, SDE_MDP_FMT_LINEAR,
+ 0, C1_B_Cb, C2_R_Cr, SDE_MDP_COMPRESS_NONE),
+ FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CRCB_H2V2, "Y_CRCB_H2V2",
+ SDE_MDP_FMT_LINEAR,
SDE_MDP_CHROMA_420, SDE_MDP_PIXEL_NORMAL,
- VALID_ROT_WB_ALL | VALID_MDP_WB_INTF_FORMAT,
- C2_R_Cr, C1_B_Cb, SDE_MDP_COMPRESS_NONE),
- FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CBCR_H2V2, SDE_MDP_FMT_LINEAR,
+ 0, C2_R_Cr, C1_B_Cb, SDE_MDP_COMPRESS_NONE),
+ FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CBCR_H2V2, "Y_CBCR_H2V2",
+ SDE_MDP_FMT_LINEAR,
SDE_MDP_CHROMA_420, SDE_MDP_PIXEL_NORMAL,
- VALID_ROT_WB_ALL | VALID_MDP_WB_INTF_FORMAT,
- C1_B_Cb, C2_R_Cr, SDE_MDP_COMPRESS_NONE),
- FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CBCR_H2V2_VENUS, SDE_MDP_FMT_LINEAR,
+ 0, C1_B_Cb, C2_R_Cr, SDE_MDP_COMPRESS_NONE),
+ FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CBCR_H2V2_VENUS, "SDE/Y_CBCR_H2V2_VENUS",
+ SDE_MDP_FMT_LINEAR,
SDE_MDP_CHROMA_420, SDE_MDP_PIXEL_NORMAL,
- VALID_ROT_WB_ALL | VALID_MDP_WB_INTF_FORMAT,
- C1_B_Cb, C2_R_Cr, SDE_MDP_COMPRESS_NONE),
- FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CRCB_H2V2_VENUS, SDE_MDP_FMT_LINEAR,
+ 0, C1_B_Cb, C2_R_Cr, SDE_MDP_COMPRESS_NONE),
+ FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CRCB_H2V2_VENUS, "SDE/Y_CRCB_H2V2_VENUS",
+ SDE_MDP_FMT_LINEAR,
SDE_MDP_CHROMA_420, SDE_MDP_PIXEL_NORMAL,
- VALID_ROT_WB_ALL | VALID_MDP_WB_INTF_FORMAT,
- C2_R_Cr, C1_B_Cb, SDE_MDP_COMPRESS_NONE),
+ 0, C2_R_Cr, C1_B_Cb, SDE_MDP_COMPRESS_NONE),
{
FMT_YUV_COMMON(SDE_PIX_FMT_Y_CBCR_H2V2_P010),
- .flag = VALID_ROT_R3_WB_FORMAT,
+ .description = "SDE/Y_CBCR_H2V2_P010",
+ .flag = 0,
.fetch_planes = SDE_MDP_PLANE_PSEUDO_PLANAR,
.chroma_sample = SDE_MDP_CHROMA_420,
.unpack_count = 2,
@@ -364,7 +383,8 @@
},
{
FMT_YUV_COMMON(SDE_PIX_FMT_Y_CBCR_H2V2_TP10),
- .flag = VALID_ROT_R3_WB_FORMAT,
+ .description = "SDE/Y_CBCR_H2V2_TP10",
+ .flag = 0,
.fetch_planes = SDE_MDP_PLANE_PSEUDO_PLANAR,
.chroma_sample = SDE_MDP_CHROMA_420,
.unpack_count = 2,
@@ -377,19 +397,20 @@
.is_ubwc = SDE_MDP_COMPRESS_NONE,
},
- FMT_YUV_PLANR(SDE_PIX_FMT_Y_CB_CR_H2V2, SDE_MDP_FMT_LINEAR,
- SDE_MDP_CHROMA_420, VALID_ROT_WB_FORMAT |
- VALID_MDP_WB_INTF_FORMAT, C2_R_Cr, C1_B_Cb),
- FMT_YUV_PLANR(SDE_PIX_FMT_Y_CR_CB_H2V2, SDE_MDP_FMT_LINEAR,
- SDE_MDP_CHROMA_420, VALID_ROT_WB_FORMAT |
- VALID_MDP_WB_INTF_FORMAT, C1_B_Cb, C2_R_Cr),
- FMT_YUV_PLANR(SDE_PIX_FMT_Y_CR_CB_GH2V2, SDE_MDP_FMT_LINEAR,
- SDE_MDP_CHROMA_420, VALID_ROT_WB_FORMAT |
- VALID_MDP_WB_INTF_FORMAT, C1_B_Cb, C2_R_Cr),
+ FMT_YUV_PLANR(SDE_PIX_FMT_Y_CB_CR_H2V2, "Y_CB_CR_H2V2",
+ SDE_MDP_FMT_LINEAR,
+ SDE_MDP_CHROMA_420, 0, C2_R_Cr, C1_B_Cb),
+ FMT_YUV_PLANR(SDE_PIX_FMT_Y_CR_CB_H2V2, "Y_CR_CB_H2V2",
+ SDE_MDP_FMT_LINEAR,
+ SDE_MDP_CHROMA_420, 0, C1_B_Cb, C2_R_Cr),
+ FMT_YUV_PLANR(SDE_PIX_FMT_Y_CR_CB_GH2V2, "SDE/Y_CR_CB_GH2V2",
+ SDE_MDP_FMT_LINEAR,
+ SDE_MDP_CHROMA_420, 0, C1_B_Cb, C2_R_Cr),
{
FMT_YUV_COMMON(SDE_PIX_FMT_YCBYCR_H2V1),
- .flag = VALID_ROT_WB_FORMAT,
+ .description = "YCBYCR_H2V1",
+ .flag = 0,
.fetch_planes = SDE_MDP_PLANE_INTERLEAVED,
.chroma_sample = SDE_MDP_CHROMA_H2V1,
.unpack_count = 4,
@@ -399,35 +420,69 @@
.element = { C2_R_Cr, C0_G_Y, C1_B_Cb, C0_G_Y },
.is_ubwc = SDE_MDP_COMPRESS_NONE,
},
- FMT_RGB_1555(SDE_PIX_FMT_RGBA_5551, 1, VALID_ROT_WB_ALL,
+ FMT_RGB_1555(SDE_PIX_FMT_RGBA_5551, "RGBA_5551", 1, 0,
C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr),
- FMT_RGB_4444(SDE_PIX_FMT_RGBA_4444, 1, VALID_ROT_WB_ALL,
- C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr),
- FMT_RGB_4444(SDE_PIX_FMT_ARGB_4444, 1, VALID_ROT_WB_ALL,
+ FMT_RGB_1555(SDE_PIX_FMT_ARGB_1555, "ARGB_1555", 1, 0,
C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA),
- FMT_RGB_1010102(SDE_PIX_FMT_RGBA_1010102, SDE_MDP_FMT_LINEAR,
- VALID_ROT_R3_WB_FORMAT | VALID_MDP_WB_INTF_FORMAT,
- 1, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, SDE_MDP_COMPRESS_NONE),
- FMT_RGB_1010102(SDE_PIX_FMT_RGBX_1010102, SDE_MDP_FMT_LINEAR,
- VALID_ROT_R3_WB_FORMAT | VALID_MDP_WB_INTF_FORMAT,
- 0, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, SDE_MDP_COMPRESS_NONE),
- FMT_RGB_1010102(SDE_PIX_FMT_BGRA_1010102, SDE_MDP_FMT_LINEAR,
- VALID_ROT_R3_WB_FORMAT | VALID_MDP_WB_INTF_FORMAT,
- 1, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, SDE_MDP_COMPRESS_NONE),
- FMT_RGB_1010102(SDE_PIX_FMT_BGRX_1010102, SDE_MDP_FMT_LINEAR,
- VALID_ROT_R3_WB_FORMAT | VALID_MDP_WB_INTF_FORMAT,
- 0, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, SDE_MDP_COMPRESS_NONE),
- FMT_RGB_1010102(SDE_PIX_FMT_ARGB_2101010, SDE_MDP_FMT_LINEAR,
- INVALID_WB_FORMAT, 1, C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb,
+ FMT_RGB_1555(SDE_PIX_FMT_ABGR_1555, "ABGR_1555", 1, 0,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA),
+ FMT_RGB_1555(SDE_PIX_FMT_BGRA_5551, "BGRA_5551", 1, 0,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb),
+ FMT_RGB_1555(SDE_PIX_FMT_BGRX_5551, "BGRX_5551", 0, 0,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb),
+ FMT_RGB_1555(SDE_PIX_FMT_RGBX_5551, "RGBX_5551", 0, 0,
+ C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr),
+ FMT_RGB_1555(SDE_PIX_FMT_XBGR_1555, "XBGR_1555", 0, 0,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA),
+ FMT_RGB_1555(SDE_PIX_FMT_XRGB_1555, "XRGB_1555", 0, 0,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA),
+ FMT_RGB_4444(SDE_PIX_FMT_RGBA_4444, "RGBA_4444", 1, 0,
+ C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr),
+ FMT_RGB_4444(SDE_PIX_FMT_ARGB_4444, "ARGB_4444", 1, 0,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA),
+ FMT_RGB_4444(SDE_PIX_FMT_BGRA_4444, "BGRA_4444", 1, 0,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb),
+ FMT_RGB_4444(SDE_PIX_FMT_ABGR_4444, "ABGR_4444", 1, 0,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA),
+ FMT_RGB_4444(SDE_PIX_FMT_RGBX_4444, "RGBX_4444", 0, 0,
+ C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr),
+ FMT_RGB_4444(SDE_PIX_FMT_XRGB_4444, "XRGB_4444", 0, 0,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA),
+ FMT_RGB_4444(SDE_PIX_FMT_BGRX_4444, "BGRX_4444", 0, 0,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb),
+ FMT_RGB_4444(SDE_PIX_FMT_XBGR_4444, "XBGR_4444", 0, 0,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA),
+ FMT_RGB_1010102(SDE_PIX_FMT_RGBA_1010102, "SDE/RGBA_1010102",
+ SDE_MDP_FMT_LINEAR,
+ 0, 1, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA,
SDE_MDP_COMPRESS_NONE),
- FMT_RGB_1010102(SDE_PIX_FMT_XRGB_2101010, SDE_MDP_FMT_LINEAR,
- INVALID_WB_FORMAT, 0, C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb,
+ FMT_RGB_1010102(SDE_PIX_FMT_RGBX_1010102, "SDE/RGBX_1010102",
+ SDE_MDP_FMT_LINEAR,
+ 0, 0, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA,
SDE_MDP_COMPRESS_NONE),
- FMT_RGB_1010102(SDE_PIX_FMT_ABGR_2101010, SDE_MDP_FMT_LINEAR,
- INVALID_WB_FORMAT, 1, C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr,
+ FMT_RGB_1010102(SDE_PIX_FMT_BGRA_1010102, "SDE/BGRA_1010102",
+ SDE_MDP_FMT_LINEAR,
+ 0, 1, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA,
SDE_MDP_COMPRESS_NONE),
- FMT_RGB_1010102(SDE_PIX_FMT_XBGR_2101010, SDE_MDP_FMT_LINEAR,
- INVALID_WB_FORMAT, 0, C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr,
+ FMT_RGB_1010102(SDE_PIX_FMT_BGRX_1010102, "SDE/BGRX_1010102",
+ SDE_MDP_FMT_LINEAR,
+ 0, 0, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA,
+ SDE_MDP_COMPRESS_NONE),
+ FMT_RGB_1010102(SDE_PIX_FMT_ARGB_2101010, "SDE/ARGB_2101010",
+ SDE_MDP_FMT_LINEAR,
+ 0, 1, C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb,
+ SDE_MDP_COMPRESS_NONE),
+ FMT_RGB_1010102(SDE_PIX_FMT_XRGB_2101010, "SDE/XRGB_2101010",
+ SDE_MDP_FMT_LINEAR,
+ 0, 0, C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb,
+ SDE_MDP_COMPRESS_NONE),
+ FMT_RGB_1010102(SDE_PIX_FMT_ABGR_2101010, "SDE/ABGR_2101010",
+ SDE_MDP_FMT_LINEAR,
+ 0, 1, C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr,
+ SDE_MDP_COMPRESS_NONE),
+ FMT_RGB_1010102(SDE_PIX_FMT_XBGR_2101010, "SDE/XBGR_2101010",
+ SDE_MDP_FMT_LINEAR,
+ 0, 0, C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr,
SDE_MDP_COMPRESS_NONE),
};
@@ -488,24 +543,3 @@
return 0;
}
-
-/*
- * sde_mdp_is_wb_format - determine if the given fmt is supported by writeback
- * @fmt: Pointer to format parameter
- */
-bool sde_mdp_is_wb_format(struct sde_mdp_format_params *fmt)
-{
- struct sde_rot_data_type *mdata = sde_rot_get_mdata();
-
- if (!mdata || !fmt)
- return false;
- else if (test_bit(SDE_CAPS_R1_WB, mdata->sde_caps_map) &&
- (fmt->flag & VALID_ROT_WB_FORMAT))
- return true;
- else if (test_bit(SDE_CAPS_R3_WB, mdata->sde_caps_map) &&
- (fmt->flag & VALID_ROT_R3_WB_FORMAT))
- return true;
- else
- return false;
-}
-
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.h
index 198bee3..bdd16a9 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17,19 +17,11 @@
#include <linux/types.h>
#include <media/msm_sde_rotator.h>
-/* internal formats */
-#define SDE_PIX_FMT_Y_CBCR_H2V2_TP10 v4l2_fourcc('T', 'P', '1', '0')
-
#define SDE_ROT_MAX_PLANES 4
#define UBWC_META_MACRO_W_H 16
#define UBWC_META_BLOCK_SIZE 256
-#define INVALID_WB_FORMAT 0
-#define VALID_ROT_WB_FORMAT BIT(0)
-#define VALID_MDP_WB_INTF_FORMAT BIT(1)
-#define VALID_ROT_R3_WB_FORMAT BIT(2)
-
/*
* Value of enum chosen to fit the number of bits
* expected by the HW programming.
@@ -79,6 +71,7 @@
struct sde_mdp_format_params {
u32 format;
+ const char *description;
u32 flag;
u8 is_yuv;
u8 is_ubwc;
@@ -111,8 +104,6 @@
int sde_rot_get_ubwc_micro_dim(u32 format, u16 *w, u16 *h);
-bool sde_mdp_is_wb_format(struct sde_mdp_format_params *fmt);
-
static inline bool sde_mdp_is_tilea4x_format(struct sde_mdp_format_params *fmt)
{
return fmt && (fmt->frame_format == SDE_MDP_FMT_TILE_A4X);
@@ -133,10 +124,34 @@
return fmt && (fmt->frame_format == SDE_MDP_FMT_LINEAR);
}
+static inline bool sde_mdp_is_nv12_format(struct sde_mdp_format_params *fmt)
+{
+ return fmt && (fmt->fetch_planes == SDE_MDP_PLANE_PSEUDO_PLANAR) &&
+ (fmt->chroma_sample == SDE_MDP_CHROMA_420);
+}
+
+static inline bool sde_mdp_is_nv12_8b_format(struct sde_mdp_format_params *fmt)
+{
+ return fmt && sde_mdp_is_nv12_format(fmt) &&
+ (fmt->pixel_mode == SDE_MDP_PIXEL_NORMAL);
+}
+
+static inline bool sde_mdp_is_nv12_10b_format(struct sde_mdp_format_params *fmt)
+{
+ return fmt && sde_mdp_is_nv12_format(fmt) &&
+ (fmt->pixel_mode == SDE_MDP_PIXEL_10BIT);
+}
+
static inline bool sde_mdp_is_tp10_format(struct sde_mdp_format_params *fmt)
{
- return fmt && ((fmt->format == SDE_PIX_FMT_Y_CBCR_H2V2_TP10_UBWC) ||
- (fmt->format == SDE_PIX_FMT_Y_CBCR_H2V2_TP10));
+ return fmt && sde_mdp_is_nv12_10b_format(fmt) &&
+ fmt->unpack_tight;
+}
+
+static inline bool sde_mdp_is_p010_format(struct sde_mdp_format_params *fmt)
+{
+ return fmt && sde_mdp_is_nv12_10b_format(fmt) &&
+ !fmt->unpack_tight;
}
static inline bool sde_mdp_is_yuv_format(struct sde_mdp_format_params *fmt)
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_io_util.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_io_util.c
index cd99a92..9f87fdd 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_io_util.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_io_util.c
@@ -14,6 +14,7 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
+#include <linux/regulator/consumer.h>
#include <linux/delay.h>
#include "sde_rotator_io_util.h"
@@ -197,7 +198,7 @@
vreg_unconfig:
if (type == SDE_REG_LDO)
- regulator_set_optimum_mode(curr_vreg->vreg, 0);
+ regulator_set_load(curr_vreg->vreg, 0);
vreg_set_voltage_fail:
regulator_put(curr_vreg->vreg);
@@ -237,7 +238,7 @@
if (in_vreg[i].pre_on_sleep && need_sleep)
usleep_range(in_vreg[i].pre_on_sleep * 1000,
in_vreg[i].pre_on_sleep * 1000);
- rc = regulator_set_optimum_mode(in_vreg[i].vreg,
+ rc = regulator_set_load(in_vreg[i].vreg,
in_vreg[i].enable_load);
if (rc < 0) {
DEV_ERR("%pS->%s: %s set opt m fail\n",
@@ -261,7 +262,7 @@
if (in_vreg[i].pre_off_sleep)
usleep_range(in_vreg[i].pre_off_sleep * 1000,
in_vreg[i].pre_off_sleep * 1000);
- regulator_set_optimum_mode(in_vreg[i].vreg,
+ regulator_set_load(in_vreg[i].vreg,
in_vreg[i].disable_load);
regulator_disable(in_vreg[i].vreg);
if (in_vreg[i].post_off_sleep)
@@ -272,14 +273,14 @@
return rc;
disable_vreg:
- regulator_set_optimum_mode(in_vreg[i].vreg, in_vreg[i].disable_load);
+ regulator_set_load(in_vreg[i].vreg, in_vreg[i].disable_load);
vreg_set_opt_mode_fail:
for (i--; i >= 0; i--) {
if (in_vreg[i].pre_off_sleep)
usleep_range(in_vreg[i].pre_off_sleep * 1000,
in_vreg[i].pre_off_sleep * 1000);
- regulator_set_optimum_mode(in_vreg[i].vreg,
+ regulator_set_load(in_vreg[i].vreg,
in_vreg[i].disable_load);
regulator_disable(in_vreg[i].vreg);
if (in_vreg[i].post_off_sleep)
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c
index 31cc4f3..5b574ed 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c
@@ -45,6 +45,100 @@
struct sde_mdp_hw_resource *mdp_hw;
};
+static u32 sde_hw_rotator_input_pixfmts[] = {
+ SDE_PIX_FMT_XRGB_8888,
+ SDE_PIX_FMT_ARGB_8888,
+ SDE_PIX_FMT_ABGR_8888,
+ SDE_PIX_FMT_RGBA_8888,
+ SDE_PIX_FMT_BGRA_8888,
+ SDE_PIX_FMT_RGBX_8888,
+ SDE_PIX_FMT_BGRX_8888,
+ SDE_PIX_FMT_XBGR_8888,
+ SDE_PIX_FMT_RGBA_5551,
+ SDE_PIX_FMT_ARGB_1555,
+ SDE_PIX_FMT_ABGR_1555,
+ SDE_PIX_FMT_BGRA_5551,
+ SDE_PIX_FMT_BGRX_5551,
+ SDE_PIX_FMT_RGBX_5551,
+ SDE_PIX_FMT_XBGR_1555,
+ SDE_PIX_FMT_XRGB_1555,
+ SDE_PIX_FMT_ARGB_4444,
+ SDE_PIX_FMT_RGBA_4444,
+ SDE_PIX_FMT_BGRA_4444,
+ SDE_PIX_FMT_ABGR_4444,
+ SDE_PIX_FMT_RGBX_4444,
+ SDE_PIX_FMT_XRGB_4444,
+ SDE_PIX_FMT_BGRX_4444,
+ SDE_PIX_FMT_XBGR_4444,
+ SDE_PIX_FMT_RGB_888,
+ SDE_PIX_FMT_BGR_888,
+ SDE_PIX_FMT_RGB_565,
+ SDE_PIX_FMT_BGR_565,
+ SDE_PIX_FMT_Y_CB_CR_H2V2,
+ SDE_PIX_FMT_Y_CR_CB_H2V2,
+ SDE_PIX_FMT_Y_CR_CB_GH2V2,
+ SDE_PIX_FMT_Y_CBCR_H2V2,
+ SDE_PIX_FMT_Y_CRCB_H2V2,
+ SDE_PIX_FMT_Y_CBCR_H1V2,
+ SDE_PIX_FMT_Y_CRCB_H1V2,
+ SDE_PIX_FMT_Y_CBCR_H2V1,
+ SDE_PIX_FMT_Y_CRCB_H2V1,
+ SDE_PIX_FMT_YCBYCR_H2V1,
+ SDE_PIX_FMT_Y_CBCR_H2V2_VENUS,
+ SDE_PIX_FMT_Y_CRCB_H2V2_VENUS,
+ SDE_PIX_FMT_RGBA_8888_UBWC,
+ SDE_PIX_FMT_RGBX_8888_UBWC,
+ SDE_PIX_FMT_RGB_565_UBWC,
+ SDE_PIX_FMT_Y_CBCR_H2V2_UBWC,
+};
+
+static u32 sde_hw_rotator_output_pixfmts[] = {
+ SDE_PIX_FMT_XRGB_8888,
+ SDE_PIX_FMT_ARGB_8888,
+ SDE_PIX_FMT_ABGR_8888,
+ SDE_PIX_FMT_RGBA_8888,
+ SDE_PIX_FMT_BGRA_8888,
+ SDE_PIX_FMT_RGBX_8888,
+ SDE_PIX_FMT_BGRX_8888,
+ SDE_PIX_FMT_XBGR_8888,
+ SDE_PIX_FMT_RGBA_5551,
+ SDE_PIX_FMT_ARGB_1555,
+ SDE_PIX_FMT_ABGR_1555,
+ SDE_PIX_FMT_BGRA_5551,
+ SDE_PIX_FMT_BGRX_5551,
+ SDE_PIX_FMT_RGBX_5551,
+ SDE_PIX_FMT_XBGR_1555,
+ SDE_PIX_FMT_XRGB_1555,
+ SDE_PIX_FMT_ARGB_4444,
+ SDE_PIX_FMT_RGBA_4444,
+ SDE_PIX_FMT_BGRA_4444,
+ SDE_PIX_FMT_ABGR_4444,
+ SDE_PIX_FMT_RGBX_4444,
+ SDE_PIX_FMT_XRGB_4444,
+ SDE_PIX_FMT_BGRX_4444,
+ SDE_PIX_FMT_XBGR_4444,
+ SDE_PIX_FMT_RGB_888,
+ SDE_PIX_FMT_BGR_888,
+ SDE_PIX_FMT_RGB_565,
+ SDE_PIX_FMT_BGR_565,
+ SDE_PIX_FMT_Y_CB_CR_H2V2,
+ SDE_PIX_FMT_Y_CR_CB_H2V2,
+ SDE_PIX_FMT_Y_CR_CB_GH2V2,
+ SDE_PIX_FMT_Y_CBCR_H2V2,
+ SDE_PIX_FMT_Y_CRCB_H2V2,
+ SDE_PIX_FMT_Y_CBCR_H1V2,
+ SDE_PIX_FMT_Y_CRCB_H1V2,
+ SDE_PIX_FMT_Y_CBCR_H2V1,
+ SDE_PIX_FMT_Y_CRCB_H2V1,
+ SDE_PIX_FMT_YCBYCR_H2V1,
+ SDE_PIX_FMT_Y_CBCR_H2V2_VENUS,
+ SDE_PIX_FMT_Y_CRCB_H2V2_VENUS,
+ SDE_PIX_FMT_RGBA_8888_UBWC,
+ SDE_PIX_FMT_RGBX_8888_UBWC,
+ SDE_PIX_FMT_RGB_565_UBWC,
+ SDE_PIX_FMT_Y_CBCR_H2V2_UBWC,
+};
+
static struct sde_mdp_hw_resource *sde_rotator_hw_alloc(
struct sde_rot_mgr *mgr, u32 ctl_id, u32 wb_id, int irq_num)
{
@@ -403,6 +497,52 @@
return cnt;
}
+/*
+ * sde_hw_rotator_get_pixfmt - get the indexed pixel format
+ * @mgr: Pointer to rotator manager
+ * @index: index of pixel format
+ * @input: true for input port; false for output port
+ */
+static u32 sde_hw_rotator_get_pixfmt(struct sde_rot_mgr *mgr,
+ int index, bool input)
+{
+ if (input) {
+ if (index < ARRAY_SIZE(sde_hw_rotator_input_pixfmts))
+ return sde_hw_rotator_input_pixfmts[index];
+ else
+ return 0;
+ } else {
+ if (index < ARRAY_SIZE(sde_hw_rotator_output_pixfmts))
+ return sde_hw_rotator_output_pixfmts[index];
+ else
+ return 0;
+ }
+}
+
+/*
+ * sde_hw_rotator_is_valid_pixfmt - verify if the given pixel format is valid
+ * @mgr: Pointer to rotator manager
+ * @pixfmt: pixel format to be verified
+ * @input: true for input port; false for output port
+ */
+static int sde_hw_rotator_is_valid_pixfmt(struct sde_rot_mgr *mgr, u32 pixfmt,
+ bool input)
+{
+ int i;
+
+ if (input) {
+ for (i = 0; i < ARRAY_SIZE(sde_hw_rotator_input_pixfmts); i++)
+ if (sde_hw_rotator_input_pixfmts[i] == pixfmt)
+ return true;
+ } else {
+ for (i = 0; i < ARRAY_SIZE(sde_hw_rotator_output_pixfmts); i++)
+ if (sde_hw_rotator_output_pixfmts[i] == pixfmt)
+ return true;
+ }
+
+ return false;
+}
+
static int sde_rotator_hw_parse_dt(struct sde_rotator_r1_data *hw_data,
struct platform_device *dev)
{
@@ -552,6 +692,8 @@
mgr->ops_hw_show_caps = sde_rotator_hw_show_caps;
mgr->ops_hw_show_state = sde_rotator_hw_show_state;
mgr->ops_hw_create_debugfs = sde_rotator_r1_create_debugfs;
+ mgr->ops_hw_get_pixfmt = sde_hw_rotator_get_pixfmt;
+ mgr->ops_hw_is_valid_pixfmt = sde_hw_rotator_is_valid_pixfmt;
ret = sde_rotator_hw_parse_dt(mgr->hw_data, mgr->pdev);
if (ret)
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_ctl.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_ctl.c
index b5c0790..fef4a85 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_ctl.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_ctl.c
@@ -167,7 +167,7 @@
int sde_mdp_display_wait4comp(struct sde_mdp_ctl *ctl)
{
- int ret;
+ int ret = 0;
if (!ctl) {
SDEROT_ERR("invalid ctl\n");
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_internal.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_internal.h
index b451775..d9b4f38 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_internal.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_internal.h
@@ -116,6 +116,7 @@
struct sde_mdp_plane_sizes src_planes;
struct sde_mdp_mixer *mixer_left;
struct sde_mdp_mixer *mixer_right;
+ struct sde_mdp_shared_reg_ctrl clk_ctrl;
u32 params_changed;
u32 offset;
};
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_pipe.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_pipe.c
index 1181998..5f886d7 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_pipe.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_pipe.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -131,6 +131,10 @@
static struct sde_mdp_pipe sde_pipe[16];
static const u32 offset[] = {0x00025000, 0x00027000};
static const u32 xin_id[] = {2, 10};
+ static const struct sde_mdp_shared_reg_ctrl clk_ctrl[] = {
+ {0x2AC, 8},
+ {0x2B4, 8}
+ };
if (ndx >= ARRAY_SIZE(offset)) {
SDEROT_ERR("invalid parameters\n");
@@ -144,6 +148,7 @@
pipe->base = mdata->sde_io.base + pipe->offset;
pipe->type = SDE_MDP_PIPE_TYPE_DMA;
pipe->mixer_left = mixer;
+ pipe->clk_ctrl = clk_ctrl[pipe->num - SDE_MDP_SSPP_DMA0];
return pipe;
}
@@ -343,6 +348,24 @@
return 0;
}
+static void sde_mdp_set_ot_limit_pipe(struct sde_mdp_pipe *pipe)
+{
+ struct sde_mdp_set_ot_params ot_params = {0,};
+
+ ot_params.xin_id = pipe->xin_id;
+ ot_params.num = pipe->num;
+ ot_params.width = pipe->src.w;
+ ot_params.height = pipe->src.h;
+ ot_params.fps = 60;
+ ot_params.reg_off_vbif_lim_conf = MMSS_VBIF_RD_LIM_CONF;
+ ot_params.reg_off_mdp_clk_ctrl = pipe->clk_ctrl.reg_off;
+ ot_params.bit_off_mdp_clk_ctrl = pipe->clk_ctrl.bit_off +
+ CLK_FORCE_ON_OFFSET;
+ ot_params.fmt = (pipe->src_fmt) ? pipe->src_fmt->format : 0;
+
+ sde_mdp_set_ot_limit(&ot_params);
+}
+
int sde_mdp_pipe_queue_data(struct sde_mdp_pipe *pipe,
struct sde_mdp_data *src_data)
{
@@ -390,6 +413,8 @@
if (test_bit(SDE_QOS_PER_PIPE_LUT, mdata->sde_qos_map))
sde_mdp_pipe_qos_lut(pipe);
+
+ sde_mdp_set_ot_limit_pipe(pipe);
}
ret = sde_mdp_src_addr_setup(pipe, src_data);
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_wb.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_wb.c
index 65cb396..863dfb0 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_wb.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_wb.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -316,8 +316,8 @@
{
struct sde_mdp_writeback_ctx *ctx;
int rc = 0;
- u64 rot_time;
- u32 status, mask, isr;
+ u64 rot_time = 0;
+ u32 status, mask, isr = 0;
ctx = (struct sde_mdp_writeback_ctx *) ctl->priv_data;
if (!ctx) {
@@ -402,18 +402,17 @@
static void sde_mdp_set_ot_limit_wb(struct sde_mdp_writeback_ctx *ctx)
{
- struct sde_mdp_set_ot_params ot_params;
+ struct sde_mdp_set_ot_params ot_params = {0,};
ot_params.xin_id = ctx->xin_id;
ot_params.num = ctx->wb_num;
ot_params.width = ctx->width;
ot_params.height = ctx->height;
+ ot_params.fps = 60;
ot_params.reg_off_vbif_lim_conf = MMSS_VBIF_WR_LIM_CONF;
ot_params.reg_off_mdp_clk_ctrl = ctx->clk_ctrl.reg_off;
ot_params.bit_off_mdp_clk_ctrl = ctx->clk_ctrl.bit_off;
- ot_params.is_rot = (ctx->type == SDE_MDP_WRITEBACK_TYPE_ROTATOR);
- ot_params.is_wb = true;
- ot_params.is_yuv = ctx->dst_fmt->is_yuv;
+ ot_params.fmt = (ctx->dst_fmt) ? ctx->dst_fmt->format : 0;
sde_mdp_set_ot_limit(&ot_params);
}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
new file mode 100644
index 0000000..1f42bc8
--- /dev/null
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -0,0 +1,2453 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/sync.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-buf.h>
+#include <linux/msm_ion.h>
+#include <linux/clk/msm-clk.h>
+
+#include "sde_rotator_core.h"
+#include "sde_rotator_util.h"
+#include "sde_rotator_smmu.h"
+#include "sde_rotator_r3.h"
+#include "sde_rotator_r3_internal.h"
+#include "sde_rotator_r3_hwio.h"
+#include "sde_rotator_r3_debug.h"
+#include "sde_rotator_trace.h"
+#include "sde_rotator_debug.h"
+
+#define RES_UHD (3840*2160)
+
+/* traffic shaping clock ticks = finish_time x 19.2MHz */
+#define TRAFFIC_SHAPE_CLKTICK_14MS 268800
+#define TRAFFIC_SHAPE_CLKTICK_12MS 230400
+
+/* XIN mapping */
+#define XIN_SSPP 0
+#define XIN_WRITEBACK 1
+
+/* wait for at most 2 vsync for lowest refresh rate (24hz) */
+#define KOFF_TIMEOUT msecs_to_jiffies(42 * 32)
+
+/* Macro for constructing the REGDMA command */
+#define SDE_REGDMA_WRITE(p, off, data) \
+ do { \
+ *p++ = REGDMA_OP_REGWRITE | \
+ ((off) & REGDMA_ADDR_OFFSET_MASK); \
+ *p++ = (data); \
+ } while (0)
+
+#define SDE_REGDMA_MODIFY(p, off, mask, data) \
+ do { \
+ *p++ = REGDMA_OP_REGMODIFY | \
+ ((off) & REGDMA_ADDR_OFFSET_MASK); \
+ *p++ = (mask); \
+ *p++ = (data); \
+ } while (0)
+
+#define SDE_REGDMA_BLKWRITE_INC(p, off, len) \
+ do { \
+ *p++ = REGDMA_OP_BLKWRITE_INC | \
+ ((off) & REGDMA_ADDR_OFFSET_MASK); \
+ *p++ = (len); \
+ } while (0)
+
+#define SDE_REGDMA_BLKWRITE_DATA(p, data) \
+ do { \
+ *(p) = (data); \
+ (p)++; \
+ } while (0)
+
+/* Macro for directly accessing mapped registers */
+#define SDE_ROTREG_WRITE(base, off, data) \
+ writel_relaxed(data, (base + (off)))
+
+#define SDE_ROTREG_READ(base, off) \
+ readl_relaxed(base + (off))
+
+static u32 sde_hw_rotator_input_pixfmts[] = {
+ SDE_PIX_FMT_XRGB_8888,
+ SDE_PIX_FMT_ARGB_8888,
+ SDE_PIX_FMT_ABGR_8888,
+ SDE_PIX_FMT_RGBA_8888,
+ SDE_PIX_FMT_BGRA_8888,
+ SDE_PIX_FMT_RGBX_8888,
+ SDE_PIX_FMT_BGRX_8888,
+ SDE_PIX_FMT_XBGR_8888,
+ SDE_PIX_FMT_RGBA_5551,
+ SDE_PIX_FMT_ARGB_1555,
+ SDE_PIX_FMT_ABGR_1555,
+ SDE_PIX_FMT_BGRA_5551,
+ SDE_PIX_FMT_BGRX_5551,
+ SDE_PIX_FMT_RGBX_5551,
+ SDE_PIX_FMT_XBGR_1555,
+ SDE_PIX_FMT_XRGB_1555,
+ SDE_PIX_FMT_ARGB_4444,
+ SDE_PIX_FMT_RGBA_4444,
+ SDE_PIX_FMT_BGRA_4444,
+ SDE_PIX_FMT_ABGR_4444,
+ SDE_PIX_FMT_RGBX_4444,
+ SDE_PIX_FMT_XRGB_4444,
+ SDE_PIX_FMT_BGRX_4444,
+ SDE_PIX_FMT_XBGR_4444,
+ SDE_PIX_FMT_RGB_888,
+ SDE_PIX_FMT_BGR_888,
+ SDE_PIX_FMT_RGB_565,
+ SDE_PIX_FMT_BGR_565,
+ SDE_PIX_FMT_Y_CB_CR_H2V2,
+ SDE_PIX_FMT_Y_CR_CB_H2V2,
+ SDE_PIX_FMT_Y_CR_CB_GH2V2,
+ SDE_PIX_FMT_Y_CBCR_H2V2,
+ SDE_PIX_FMT_Y_CRCB_H2V2,
+ SDE_PIX_FMT_Y_CBCR_H1V2,
+ SDE_PIX_FMT_Y_CRCB_H1V2,
+ SDE_PIX_FMT_Y_CBCR_H2V1,
+ SDE_PIX_FMT_Y_CRCB_H2V1,
+ SDE_PIX_FMT_YCBYCR_H2V1,
+ SDE_PIX_FMT_Y_CBCR_H2V2_VENUS,
+ SDE_PIX_FMT_Y_CRCB_H2V2_VENUS,
+ SDE_PIX_FMT_RGBA_8888_UBWC,
+ SDE_PIX_FMT_RGBX_8888_UBWC,
+ SDE_PIX_FMT_RGB_565_UBWC,
+ SDE_PIX_FMT_Y_CBCR_H2V2_UBWC,
+ SDE_PIX_FMT_RGBA_1010102,
+ SDE_PIX_FMT_RGBX_1010102,
+ SDE_PIX_FMT_ARGB_2101010,
+ SDE_PIX_FMT_XRGB_2101010,
+ SDE_PIX_FMT_BGRA_1010102,
+ SDE_PIX_FMT_BGRX_1010102,
+ SDE_PIX_FMT_ABGR_2101010,
+ SDE_PIX_FMT_XBGR_2101010,
+ SDE_PIX_FMT_RGBA_1010102_UBWC,
+ SDE_PIX_FMT_RGBX_1010102_UBWC,
+ SDE_PIX_FMT_Y_CBCR_H2V2_P010,
+ SDE_PIX_FMT_Y_CBCR_H2V2_TP10,
+ SDE_PIX_FMT_Y_CBCR_H2V2_TP10_UBWC,
+};
+
+static u32 sde_hw_rotator_output_pixfmts[] = {
+ SDE_PIX_FMT_XRGB_8888,
+ SDE_PIX_FMT_ARGB_8888,
+ SDE_PIX_FMT_ABGR_8888,
+ SDE_PIX_FMT_RGBA_8888,
+ SDE_PIX_FMT_BGRA_8888,
+ SDE_PIX_FMT_RGBX_8888,
+ SDE_PIX_FMT_BGRX_8888,
+ SDE_PIX_FMT_XBGR_8888,
+ SDE_PIX_FMT_RGBA_5551,
+ SDE_PIX_FMT_ARGB_1555,
+ SDE_PIX_FMT_ABGR_1555,
+ SDE_PIX_FMT_BGRA_5551,
+ SDE_PIX_FMT_BGRX_5551,
+ SDE_PIX_FMT_RGBX_5551,
+ SDE_PIX_FMT_XBGR_1555,
+ SDE_PIX_FMT_XRGB_1555,
+ SDE_PIX_FMT_ARGB_4444,
+ SDE_PIX_FMT_RGBA_4444,
+ SDE_PIX_FMT_BGRA_4444,
+ SDE_PIX_FMT_ABGR_4444,
+ SDE_PIX_FMT_RGBX_4444,
+ SDE_PIX_FMT_XRGB_4444,
+ SDE_PIX_FMT_BGRX_4444,
+ SDE_PIX_FMT_XBGR_4444,
+ SDE_PIX_FMT_RGB_888,
+ SDE_PIX_FMT_BGR_888,
+ SDE_PIX_FMT_RGB_565,
+ SDE_PIX_FMT_BGR_565,
+ /* SDE_PIX_FMT_Y_CB_CR_H2V2 */
+ /* SDE_PIX_FMT_Y_CR_CB_H2V2 */
+ /* SDE_PIX_FMT_Y_CR_CB_GH2V2 */
+ SDE_PIX_FMT_Y_CBCR_H2V2,
+ SDE_PIX_FMT_Y_CRCB_H2V2,
+ SDE_PIX_FMT_Y_CBCR_H1V2,
+ SDE_PIX_FMT_Y_CRCB_H1V2,
+ SDE_PIX_FMT_Y_CBCR_H2V1,
+ SDE_PIX_FMT_Y_CRCB_H2V1,
+ /* SDE_PIX_FMT_YCBYCR_H2V1 */
+ SDE_PIX_FMT_Y_CBCR_H2V2_VENUS,
+ SDE_PIX_FMT_Y_CRCB_H2V2_VENUS,
+ SDE_PIX_FMT_RGBA_8888_UBWC,
+ SDE_PIX_FMT_RGBX_8888_UBWC,
+ SDE_PIX_FMT_RGB_565_UBWC,
+ SDE_PIX_FMT_Y_CBCR_H2V2_UBWC,
+ SDE_PIX_FMT_RGBA_1010102,
+ SDE_PIX_FMT_RGBX_1010102,
+ /* SDE_PIX_FMT_ARGB_2101010 */
+ /* SDE_PIX_FMT_XRGB_2101010 */
+ SDE_PIX_FMT_BGRA_1010102,
+ SDE_PIX_FMT_BGRX_1010102,
+ /* SDE_PIX_FMT_ABGR_2101010 */
+ /* SDE_PIX_FMT_XBGR_2101010 */
+ SDE_PIX_FMT_RGBA_1010102_UBWC,
+ SDE_PIX_FMT_RGBX_1010102_UBWC,
+ SDE_PIX_FMT_Y_CBCR_H2V2_P010,
+ SDE_PIX_FMT_Y_CBCR_H2V2_TP10,
+ SDE_PIX_FMT_Y_CBCR_H2V2_TP10_UBWC,
+};
+
+static struct sde_rot_vbif_debug_bus nrt_vbif_dbg_bus_r3[] = {
+ {0x214, 0x21c, 16, 1, 0x10}, /* arb clients */
+ {0x214, 0x21c, 0, 12, 0x13}, /* xin blocks - axi side */
+ {0x21c, 0x214, 0, 12, 0xc}, /* xin blocks - clock side */
+};
+
+static struct sde_rot_regdump sde_rot_r3_regdump[] = {
+ { "SDEROT_ROTTOP", SDE_ROT_ROTTOP_OFFSET, 0x100, SDE_ROT_REGDUMP_READ },
+ { "SDEROT_SSPP", SDE_ROT_SSPP_OFFSET, 0x200, SDE_ROT_REGDUMP_READ },
+ { "SDEROT_WB", SDE_ROT_WB_OFFSET, 0x300, SDE_ROT_REGDUMP_READ },
+ { "SDEROT_REGDMA_CSR", SDE_ROT_REGDMA_OFFSET, 0x100,
+ SDE_ROT_REGDUMP_READ },
+ /*
+ * Need to perform a SW reset to REGDMA in order to access the
+ * REGDMA RAM especially if REGDMA is waiting for Rotator IDLE.
+ * REGDMA RAM should be dump at last.
+ */
+ { "SDEROT_REGDMA_RESET", ROTTOP_SW_RESET_OVERRIDE, 1,
+ SDE_ROT_REGDUMP_WRITE },
+ { "SDEROT_REGDMA_RAM", SDE_ROT_REGDMA_RAM_OFFSET, 0x2000,
+ SDE_ROT_REGDUMP_READ },
+ { "SDEROT_VBIF_NRT", SDE_ROT_VBIF_NRT_OFFSET, 0x590,
+ SDE_ROT_REGDUMP_VBIF },
+};
+
+/* Invalid software timestamp value for initialization */
+#define SDE_REGDMA_SWTS_INVALID (~0)
+
+/**
+ * sde_hw_rotator_elapsed_swts - Find difference of 2 software timestamps
+ * @ts_curr: current software timestamp
+ * @ts_prev: previous software timestamp
+ * @return: the amount ts_curr is ahead of ts_prev
+ */
+static int sde_hw_rotator_elapsed_swts(u32 ts_curr, u32 ts_prev)
+{
+ u32 diff = (ts_curr - ts_prev) & SDE_REGDMA_SWTS_MASK;
+
+ return sign_extend32(diff, (SDE_REGDMA_SWTS_SHIFT - 1));
+}
+
+/**
+ * sde_hw_rotator_pending_swts - Check if the given context is still pending
+ * @rot: Pointer to hw rotator
+ * @ctx: Pointer to rotator context
+ * @pswts: Pointer to returned reference software timestamp, optional
+ * @return: true if context has pending requests
+ */
+static int sde_hw_rotator_pending_swts(struct sde_hw_rotator *rot,
+ struct sde_hw_rotator_context *ctx, u32 *pswts)
+{
+ u32 swts;
+ int ts_diff;
+ bool pending;
+
+ if (ctx->last_regdma_timestamp == SDE_REGDMA_SWTS_INVALID)
+ swts = SDE_ROTREG_READ(rot->mdss_base, REGDMA_TIMESTAMP_REG);
+ else
+ swts = ctx->last_regdma_timestamp;
+
+ if (ctx->q_id == ROT_QUEUE_LOW_PRIORITY)
+ swts >>= SDE_REGDMA_SWTS_SHIFT;
+
+ swts &= SDE_REGDMA_SWTS_MASK;
+
+ ts_diff = sde_hw_rotator_elapsed_swts(ctx->timestamp, swts);
+
+ if (pswts)
+ *pswts = swts;
+
+ pending = (ts_diff > 0) ? true : false;
+
+ SDEROT_DBG("ts:0x%x, queue_id:%d, swts:0x%x, pending:%d\n",
+ ctx->timestamp, ctx->q_id, swts, pending);
+ SDEROT_EVTLOG(ctx->timestamp, swts, ctx->q_id, ts_diff);
+ return pending;
+}
+
+/**
+ * sde_hw_rotator_enable_irq - Enable hw rotator interrupt with ref. count
+ * Also, clear rotator/regdma irq status.
+ * @rot: Pointer to hw rotator
+ */
+static void sde_hw_rotator_enable_irq(struct sde_hw_rotator *rot)
+{
+ SDEROT_DBG("irq_num:%d enabled:%d\n", rot->irq_num,
+ atomic_read(&rot->irq_enabled));
+
+ if (!atomic_read(&rot->irq_enabled)) {
+ if (rot->mode == ROT_REGDMA_OFF)
+ SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR,
+ ROT_DONE_MASK);
+ else
+ SDE_ROTREG_WRITE(rot->mdss_base,
+ REGDMA_CSR_REGDMA_INT_CLEAR, REGDMA_INT_MASK);
+
+ enable_irq(rot->irq_num);
+ }
+ atomic_inc(&rot->irq_enabled);
+}
+
+/**
+ * sde_hw_rotator_disable_irq - Disable hw rotator interrupt with ref. count
+ * Also, clear rotator/regdma irq enable masks.
+ * @rot: Pointer to hw rotator
+ */
+static void sde_hw_rotator_disable_irq(struct sde_hw_rotator *rot)
+{
+ SDEROT_DBG("irq_num:%d enabled:%d\n", rot->irq_num,
+ atomic_read(&rot->irq_enabled));
+
+ if (!atomic_read(&rot->irq_enabled)) {
+ SDEROT_ERR("irq %d is already disabled\n", rot->irq_num);
+ return;
+ }
+
+ if (!atomic_dec_return(&rot->irq_enabled)) {
+ if (rot->mode == ROT_REGDMA_OFF)
+ SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_EN, 0);
+ else
+ SDE_ROTREG_WRITE(rot->mdss_base,
+ REGDMA_CSR_REGDMA_INT_EN, 0);
+ /* disable irq after last pending irq is handled, if any */
+ synchronize_irq(rot->irq_num);
+ disable_irq_nosync(rot->irq_num);
+ }
+}
+
+/**
+ * sde_hw_rotator_dump_status - Dump hw rotator status on error
+ * @rot: Pointer to hw rotator
+ */
+static void sde_hw_rotator_dump_status(struct sde_hw_rotator *rot)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+
+ SDEROT_ERR(
+ "op_mode = %x, int_en = %x, int_status = %x\n",
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_OP_MODE),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_INT_EN),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_INT_STATUS));
+
+ SDEROT_ERR(
+ "ts = %x, q0_status = %x, q1_status = %x, block_status = %x\n",
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_TIMESTAMP_REG),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_QUEUE_0_STATUS),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_QUEUE_1_STATUS),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_BLOCK_STATUS));
+
+ SDEROT_ERR(
+ "invalid_cmd_offset = %x, fsm_state = %x\n",
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_INVALID_CMD_RAM_OFFSET),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_FSM_STATE));
+
+ SDEROT_ERR(
+ "UBWC decode status = %x, UBWC encode status = %x\n",
+ SDE_ROTREG_READ(rot->mdss_base, ROT_SSPP_UBWC_ERROR_STATUS),
+ SDE_ROTREG_READ(rot->mdss_base, ROT_WB_UBWC_ERROR_STATUS));
+
+ SDEROT_ERR("VBIF XIN HALT status = %x VBIF AXI HALT status = %x\n",
+ SDE_VBIF_READ(mdata, MMSS_VBIF_XIN_HALT_CTRL1),
+ SDE_VBIF_READ(mdata, MMSS_VBIF_AXI_HALT_CTRL1));
+}
+
+/**
+ * sde_hw_rotator_get_ctx(): Retrieve rotator context from rotator HW based
+ * on provided session_id. Each rotator has a different session_id.
+ */
+static struct sde_hw_rotator_context *sde_hw_rotator_get_ctx(
+ struct sde_hw_rotator *rot, u32 session_id,
+ enum sde_rot_queue_prio q_id)
+{
+ int i;
+ struct sde_hw_rotator_context *ctx = NULL;
+
+ for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++) {
+ ctx = rot->rotCtx[q_id][i];
+
+ if (ctx && (ctx->session_id == session_id)) {
+ SDEROT_DBG(
+ "rotCtx sloti[%d][%d] ==> ctx:%p | session-id:%d\n",
+ q_id, i, ctx, ctx->session_id);
+ return ctx;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * sde_hw_rotator_map_vaddr - map the debug buffer to kernel space
+ * @dbgbuf: Pointer to debug buffer
+ * @buf: Pointer to layer buffer structure
+ * @data: Pointer to h/w mapped buffer structure
+ */
+static void sde_hw_rotator_map_vaddr(struct sde_dbg_buf *dbgbuf,
+ struct sde_layer_buffer *buf, struct sde_mdp_data *data)
+{
+ dbgbuf->dmabuf = data->p[0].srcp_dma_buf;
+ dbgbuf->buflen = data->p[0].srcp_dma_buf->size;
+
+ dbgbuf->vaddr = NULL;
+ dbgbuf->width = buf->width;
+ dbgbuf->height = buf->height;
+
+ if (dbgbuf->dmabuf && (dbgbuf->buflen > 0)) {
+ dma_buf_begin_cpu_access(dbgbuf->dmabuf, 0, dbgbuf->buflen,
+ DMA_FROM_DEVICE);
+ dbgbuf->vaddr = dma_buf_kmap(dbgbuf->dmabuf, 0);
+ SDEROT_DBG("vaddr mapping: 0x%p/%ld w:%d/h:%d\n",
+ dbgbuf->vaddr, dbgbuf->buflen,
+ dbgbuf->width, dbgbuf->height);
+ }
+}
+
+/*
+ * sde_hw_rotator_unmap_vaddr - unmap the debug buffer from kernel space
+ * @dbgbuf: Pointer to debug buffer
+ */
+static void sde_hw_rotator_unmap_vaddr(struct sde_dbg_buf *dbgbuf)
+{
+ if (dbgbuf->vaddr) {
+ dma_buf_kunmap(dbgbuf->dmabuf, 0, dbgbuf->vaddr);
+ dma_buf_end_cpu_access(dbgbuf->dmabuf, 0, dbgbuf->buflen,
+ DMA_FROM_DEVICE);
+ }
+
+ dbgbuf->vaddr = NULL;
+ dbgbuf->dmabuf = NULL;
+ dbgbuf->buflen = 0;
+ dbgbuf->width = 0;
+ dbgbuf->height = 0;
+}
+
+/*
+ * sde_hw_rotator_setup_timestamp_packet - setup timestamp writeback command
+ * @ctx: Pointer to rotator context
+ * @mask: Bit mask location of the timestamp
+ * @swts: Software timestamp
+ */
+static void sde_hw_rotator_setup_timestamp_packet(
+ struct sde_hw_rotator_context *ctx, u32 mask, u32 swts)
+{
+ u32 *wrptr;
+
+ wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+
+ /*
+ * Create a dummy packet write out to 1 location for timestamp
+ * generation.
+ */
+ SDE_REGDMA_BLKWRITE_INC(wrptr, ROT_SSPP_SRC_SIZE, 6);
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x00010001);
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x00010001);
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, ctx->ts_addr);
+ SDE_REGDMA_WRITE(wrptr, ROT_SSPP_SRC_YSTRIDE0, 4);
+ SDE_REGDMA_BLKWRITE_INC(wrptr, ROT_SSPP_SRC_FORMAT, 4);
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x004037FF);
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x03020100);
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x80000000);
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, ctx->timestamp);
+ /*
+ * Must clear secure buffer setting for SW timestamp because
+ * SW timstamp buffer allocation is always non-secure region.
+ */
+ if (ctx->is_secure) {
+ SDE_REGDMA_WRITE(wrptr, ROT_SSPP_SRC_ADDR_SW_STATUS, 0);
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_ADDR_SW_STATUS, 0);
+ }
+ SDE_REGDMA_BLKWRITE_INC(wrptr, ROT_WB_DST_FORMAT, 4);
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x000037FF);
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x03020100);
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, ctx->ts_addr);
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_YSTRIDE0, 4);
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_SIZE, 0x00010001);
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_IMG_SIZE, 0x00010001);
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_XY, 0);
+ SDE_REGDMA_WRITE(wrptr, ROTTOP_DNSC, 0);
+ SDE_REGDMA_WRITE(wrptr, ROTTOP_OP_MODE, 1);
+ SDE_REGDMA_MODIFY(wrptr, REGDMA_TIMESTAMP_REG, mask, swts);
+ SDE_REGDMA_WRITE(wrptr, ROTTOP_START_CTRL, 1);
+
+ sde_hw_rotator_put_regdma_segment(ctx, wrptr);
+}
+
+/*
+ * sde_hw_rotator_setup_fetchengine - setup fetch engine
+ * @ctx: Pointer to rotator context
+ * @queue_id: Priority queue identifier
+ * @cfg: Fetch configuration
+ * @danger_lut: real-time QoS LUT for danger setting (not used)
+ * @safe_lut: real-time QoS LUT for safe setting (not used)
+ * @dnsc_factor_w: downscale factor for width
+ * @dnsc_factor_h: downscale factor for height
+ * @flags: Control flag
+ */
+static void sde_hw_rotator_setup_fetchengine(struct sde_hw_rotator_context *ctx,
+ enum sde_rot_queue_prio queue_id,
+ struct sde_hw_rot_sspp_cfg *cfg, u32 danger_lut, u32 safe_lut,
+ u32 dnsc_factor_w, u32 dnsc_factor_h, u32 flags)
+{
+ struct sde_hw_rotator *rot = ctx->rot;
+ struct sde_mdp_format_params *fmt;
+ struct sde_mdp_data *data;
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ u32 *wrptr;
+ u32 opmode = 0;
+ u32 chroma_samp = 0;
+ u32 src_format = 0;
+ u32 unpack = 0;
+ u32 width = cfg->img_width;
+ u32 height = cfg->img_height;
+ u32 fetch_blocksize = 0;
+ int i;
+
+ if (ctx->rot->mode == ROT_REGDMA_ON) {
+ SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_EN,
+ REGDMA_INT_MASK);
+ SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_OP_MODE,
+ REGDMA_EN);
+ }
+
+ wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+
+ /* source image setup */
+ if ((flags & SDE_ROT_FLAG_DEINTERLACE)
+ && !(flags & SDE_ROT_FLAG_SOURCE_ROTATED_90)) {
+ for (i = 0; i < cfg->src_plane.num_planes; i++)
+ cfg->src_plane.ystride[i] *= 2;
+ width *= 2;
+ height /= 2;
+ }
+
+ /*
+ * REGDMA BLK write from SRC_SIZE to OP_MODE, total 15 registers
+ */
+ SDE_REGDMA_BLKWRITE_INC(wrptr, ROT_SSPP_SRC_SIZE, 15);
+
+ /* SRC_SIZE, SRC_IMG_SIZE, SRC_XY, OUT_SIZE, OUT_XY */
+ SDE_REGDMA_BLKWRITE_DATA(wrptr,
+ cfg->src_rect->w | (cfg->src_rect->h << 16));
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, 0); /* SRC_IMG_SIZE unused */
+ SDE_REGDMA_BLKWRITE_DATA(wrptr,
+ cfg->src_rect->x | (cfg->src_rect->y << 16));
+ SDE_REGDMA_BLKWRITE_DATA(wrptr,
+ cfg->src_rect->w | (cfg->src_rect->h << 16));
+ SDE_REGDMA_BLKWRITE_DATA(wrptr,
+ cfg->src_rect->x | (cfg->src_rect->y << 16));
+
+ /* SRC_ADDR [0-3], SRC_YSTRIDE [0-1] */
+ data = cfg->data;
+ for (i = 0; i < SDE_ROT_MAX_PLANES; i++)
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, data->p[i].addr);
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, cfg->src_plane.ystride[0] |
+ (cfg->src_plane.ystride[1] << 16));
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, cfg->src_plane.ystride[2] |
+ (cfg->src_plane.ystride[3] << 16));
+
+ /* UNUSED, write 0 */
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
+
+ /* setup source format */
+ fmt = cfg->fmt;
+
+ chroma_samp = fmt->chroma_sample;
+ if (flags & SDE_ROT_FLAG_SOURCE_ROTATED_90) {
+ if (chroma_samp == SDE_MDP_CHROMA_H2V1)
+ chroma_samp = SDE_MDP_CHROMA_H1V2;
+ else if (chroma_samp == SDE_MDP_CHROMA_H1V2)
+ chroma_samp = SDE_MDP_CHROMA_H2V1;
+ }
+
+ src_format = (chroma_samp << 23) |
+ (fmt->fetch_planes << 19) |
+ (fmt->bits[C3_ALPHA] << 6) |
+ (fmt->bits[C2_R_Cr] << 4) |
+ (fmt->bits[C1_B_Cb] << 2) |
+ (fmt->bits[C0_G_Y] << 0);
+
+ if (fmt->alpha_enable &&
+ (fmt->fetch_planes == SDE_MDP_PLANE_INTERLEAVED))
+ src_format |= BIT(8); /* SRCC3_EN */
+
+ src_format |= ((fmt->unpack_count - 1) << 12) |
+ (fmt->unpack_tight << 17) |
+ (fmt->unpack_align_msb << 18) |
+ ((fmt->bpp - 1) << 9) |
+ ((fmt->frame_format & 3) << 30);
+
+ if (flags & SDE_ROT_FLAG_ROT_90)
+ src_format |= BIT(11); /* ROT90 */
+
+ if (sde_mdp_is_ubwc_format(fmt))
+ opmode |= BIT(0); /* BWC_DEC_EN */
+
+ /* if this is YUV pixel format, enable CSC */
+ if (sde_mdp_is_yuv_format(fmt))
+ src_format |= BIT(15); /* SRC_COLOR_SPACE */
+
+ if (fmt->pixel_mode == SDE_MDP_PIXEL_10BIT)
+ src_format |= BIT(14); /* UNPACK_DX_FORMAT */
+
+ /* SRC_FORMAT */
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, src_format);
+
+ /* setup source unpack pattern */
+ unpack = (fmt->element[3] << 24) | (fmt->element[2] << 16) |
+ (fmt->element[1] << 8) | (fmt->element[0] << 0);
+
+ /* SRC_UNPACK_PATTERN */
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, unpack);
+
+ /* setup source op mode */
+ if (flags & SDE_ROT_FLAG_FLIP_LR)
+ opmode |= BIT(13); /* FLIP_MODE L/R horizontal flip */
+ if (flags & SDE_ROT_FLAG_FLIP_UD)
+ opmode |= BIT(14); /* FLIP_MODE U/D vertical flip */
+ opmode |= BIT(31); /* MDSS_MDP_OP_PE_OVERRIDE */
+
+ /* SRC_OP_MODE */
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, opmode);
+
+ /* setup source fetch config, TP10 uses different block size */
+ if (test_bit(SDE_CAPS_R3_1P5_DOWNSCALE, mdata->sde_caps_map) &&
+ (dnsc_factor_w == 1) && (dnsc_factor_h == 1)) {
+ if (sde_mdp_is_tp10_format(fmt))
+ fetch_blocksize = SDE_ROT_SSPP_FETCH_BLOCKSIZE_144_EXT;
+ else
+ fetch_blocksize = SDE_ROT_SSPP_FETCH_BLOCKSIZE_192_EXT;
+ } else {
+ if (sde_mdp_is_tp10_format(fmt))
+ fetch_blocksize = SDE_ROT_SSPP_FETCH_BLOCKSIZE_96;
+ else
+ fetch_blocksize = SDE_ROT_SSPP_FETCH_BLOCKSIZE_128;
+ }
+
+ SDE_REGDMA_WRITE(wrptr, ROT_SSPP_FETCH_CONFIG,
+ fetch_blocksize |
+ SDE_ROT_SSPP_FETCH_CONFIG_RESET_VALUE |
+ ((rot->highest_bank & 0x3) << 18));
+
+ /* setup source buffer plane security status */
+ if (flags & (SDE_ROT_FLAG_SECURE_OVERLAY_SESSION |
+ SDE_ROT_FLAG_SECURE_CAMERA_SESSION)) {
+ SDE_REGDMA_WRITE(wrptr, ROT_SSPP_SRC_ADDR_SW_STATUS, 0xF);
+ ctx->is_secure = true;
+ } else {
+ SDE_REGDMA_WRITE(wrptr, ROT_SSPP_SRC_ADDR_SW_STATUS, 0);
+ ctx->is_secure = false;
+ }
+
+ /*
+ * Determine if traffic shaping is required. Only enable traffic
+ * shaping when content is 4k@30fps. The actual traffic shaping
+ * bandwidth calculation is done in output setup.
+ */
+ if (((cfg->src_rect->w * cfg->src_rect->h) >= RES_UHD) &&
+ (cfg->fps <= 30)) {
+ SDEROT_DBG("Enable Traffic Shaper\n");
+ ctx->is_traffic_shaping = true;
+ } else {
+ SDEROT_DBG("Disable Traffic Shaper\n");
+ ctx->is_traffic_shaping = false;
+ }
+
+ /* Update command queue write ptr */
+ sde_hw_rotator_put_regdma_segment(ctx, wrptr);
+}
+
+/*
+ * sde_hw_rotator_setup_wbengine - setup writeback engine
+ * @ctx: Pointer to rotator context
+ * @queue_id: Priority queue identifier
+ * @cfg: Writeback configuration
+ * @flags: Control flag
+ */
+static void sde_hw_rotator_setup_wbengine(struct sde_hw_rotator_context *ctx,
+ enum sde_rot_queue_prio queue_id,
+ struct sde_hw_rot_wb_cfg *cfg,
+ u32 flags)
+{
+ struct sde_mdp_format_params *fmt;
+ u32 *wrptr;
+ u32 pack = 0;
+ u32 dst_format = 0;
+ int i;
+
+ wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+
+ fmt = cfg->fmt;
+
+ /* setup WB DST format */
+ dst_format |= (fmt->chroma_sample << 23) |
+ (fmt->fetch_planes << 19) |
+ (fmt->bits[C3_ALPHA] << 6) |
+ (fmt->bits[C2_R_Cr] << 4) |
+ (fmt->bits[C1_B_Cb] << 2) |
+ (fmt->bits[C0_G_Y] << 0);
+
+ /* alpha control */
+ if (fmt->bits[C3_ALPHA] || fmt->alpha_enable) {
+ dst_format |= BIT(8);
+ if (!fmt->alpha_enable) {
+ dst_format |= BIT(14);
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_ALPHA_X_VALUE, 0);
+ }
+ }
+
+ dst_format |= ((fmt->unpack_count - 1) << 12) |
+ (fmt->unpack_tight << 17) |
+ (fmt->unpack_align_msb << 18) |
+ ((fmt->bpp - 1) << 9) |
+ ((fmt->frame_format & 3) << 30);
+
+ if (sde_mdp_is_yuv_format(fmt))
+ dst_format |= BIT(15);
+
+ if (fmt->pixel_mode == SDE_MDP_PIXEL_10BIT)
+ dst_format |= BIT(21); /* PACK_DX_FORMAT */
+
+ /*
+ * REGDMA BLK write, from DST_FORMAT to DST_YSTRIDE 1, total 9 regs
+ */
+ SDE_REGDMA_BLKWRITE_INC(wrptr, ROT_WB_DST_FORMAT, 9);
+
+ /* DST_FORMAT */
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, dst_format);
+
+ /* DST_OP_MODE */
+ if (sde_mdp_is_ubwc_format(fmt))
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, BIT(0));
+ else
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
+
+ /* DST_PACK_PATTERN */
+ pack = (fmt->element[3] << 24) | (fmt->element[2] << 16) |
+ (fmt->element[1] << 8) | (fmt->element[0] << 0);
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, pack);
+
+ /* DST_ADDR [0-3], DST_YSTRIDE [0-1] */
+ for (i = 0; i < SDE_ROT_MAX_PLANES; i++)
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, cfg->data->p[i].addr);
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, cfg->dst_plane.ystride[0] |
+ (cfg->dst_plane.ystride[1] << 16));
+ SDE_REGDMA_BLKWRITE_DATA(wrptr, cfg->dst_plane.ystride[2] |
+ (cfg->dst_plane.ystride[3] << 16));
+
+ /* setup WB out image size and ROI */
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_IMG_SIZE,
+ cfg->img_width | (cfg->img_height << 16));
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_SIZE,
+ cfg->dst_rect->w | (cfg->dst_rect->h << 16));
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_XY,
+ cfg->dst_rect->x | (cfg->dst_rect->y << 16));
+
+ if (flags & (SDE_ROT_FLAG_SECURE_OVERLAY_SESSION |
+ SDE_ROT_FLAG_SECURE_CAMERA_SESSION))
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_ADDR_SW_STATUS, 0x1);
+ else
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_ADDR_SW_STATUS, 0);
+
+ /*
+ * setup Downscale factor
+ */
+ SDE_REGDMA_WRITE(wrptr, ROTTOP_DNSC,
+ cfg->v_downscale_factor |
+ (cfg->h_downscale_factor << 16));
+
+ /* write config setup for bank configration */
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_WRITE_CONFIG,
+ (ctx->rot->highest_bank & 0x3) << 8);
+
+ if (flags & SDE_ROT_FLAG_ROT_90)
+ SDE_REGDMA_WRITE(wrptr, ROTTOP_OP_MODE, 0x3);
+ else
+ SDE_REGDMA_WRITE(wrptr, ROTTOP_OP_MODE, 0x1);
+
+ /* setup traffic shaper for 4k 30fps content */
+ if (ctx->is_traffic_shaping) {
+ u32 bw;
+
+ /*
+ * Target to finish in 12ms, and we need to set number of bytes
+ * per clock tick for traffic shaping.
+ * Each clock tick run @ 19.2MHz, so we need we know total of
+ * clock ticks in 14ms, i.e. 12ms/(1/19.2MHz) ==> 23040
+ * Finally, calcualte the byte count per clock tick based on
+ * resolution, bpp and compression ratio.
+ */
+ bw = cfg->dst_rect->w * cfg->dst_rect->h;
+
+ if (fmt->chroma_sample == SDE_MDP_CHROMA_420)
+ bw = (bw * 3) / 2;
+ else
+ bw *= fmt->bpp;
+
+ bw /= TRAFFIC_SHAPE_CLKTICK_12MS;
+ if (bw > 0xFF)
+ bw = 0xFF;
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_TRAFFIC_SHAPER_WR_CLIENT,
+ BIT(31) | bw);
+ SDEROT_DBG("Enable ROT_WB Traffic Shaper:%d\n", bw);
+ } else {
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_TRAFFIC_SHAPER_WR_CLIENT, 0);
+ SDEROT_DBG("Disable ROT_WB Traffic Shaper\n");
+ }
+
+ /* Update command queue write ptr */
+ sde_hw_rotator_put_regdma_segment(ctx, wrptr);
+}
+
+/*
+ * sde_hw_rotator_start_no_regdma - start non-regdma operation
+ * @ctx: Pointer to rotator context
+ * @queue_id: Priority queue identifier
+ */
+static u32 sde_hw_rotator_start_no_regdma(struct sde_hw_rotator_context *ctx,
+ enum sde_rot_queue_prio queue_id)
+{
+ struct sde_hw_rotator *rot = ctx->rot;
+ u32 *wrptr;
+ u32 *rdptr;
+ u8 *addr;
+ u32 mask;
+ u32 blksize;
+
+ rdptr = sde_hw_rotator_get_regdma_segment_base(ctx);
+ wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+
+ if (rot->irq_num >= 0) {
+ SDE_REGDMA_WRITE(wrptr, ROTTOP_INTR_EN, 1);
+ SDE_REGDMA_WRITE(wrptr, ROTTOP_INTR_CLEAR, 1);
+ reinit_completion(&ctx->rot_comp);
+ sde_hw_rotator_enable_irq(rot);
+ }
+
+ SDE_REGDMA_WRITE(wrptr, ROTTOP_START_CTRL, 1);
+
+ /* Update command queue write ptr */
+ sde_hw_rotator_put_regdma_segment(ctx, wrptr);
+
+ SDEROT_DBG("BEGIN %d\n", ctx->timestamp);
+ /* Write all command stream to Rotator blocks */
+ /* Rotator will start right away after command stream finish writing */
+ while (rdptr < wrptr) {
+ u32 op = REGDMA_OP_MASK & *rdptr;
+
+ switch (op) {
+ case REGDMA_OP_NOP:
+ SDEROT_DBG("NOP\n");
+ rdptr++;
+ break;
+ case REGDMA_OP_REGWRITE:
+ SDEROT_DBG("REGW %6.6x %8.8x\n",
+ rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
+ rdptr[1]);
+ addr = rot->mdss_base +
+ (*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
+ writel_relaxed(*rdptr++, addr);
+ break;
+ case REGDMA_OP_REGMODIFY:
+ SDEROT_DBG("REGM %6.6x %8.8x %8.8x\n",
+ rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
+ rdptr[1], rdptr[2]);
+ addr = rot->mdss_base +
+ (*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
+ mask = *rdptr++;
+ writel_relaxed((readl_relaxed(addr) & mask) | *rdptr++,
+ addr);
+ break;
+ case REGDMA_OP_BLKWRITE_SINGLE:
+ SDEROT_DBG("BLKWS %6.6x %6.6x\n",
+ rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
+ rdptr[1]);
+ addr = rot->mdss_base +
+ (*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
+ blksize = *rdptr++;
+ while (blksize--) {
+ SDEROT_DBG("DATA %8.8x\n", rdptr[0]);
+ writel_relaxed(*rdptr++, addr);
+ }
+ break;
+ case REGDMA_OP_BLKWRITE_INC:
+ SDEROT_DBG("BLKWI %6.6x %6.6x\n",
+ rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
+ rdptr[1]);
+ addr = rot->mdss_base +
+ (*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
+ blksize = *rdptr++;
+ while (blksize--) {
+ SDEROT_DBG("DATA %8.8x\n", rdptr[0]);
+ writel_relaxed(*rdptr++, addr);
+ addr += 4;
+ }
+ break;
+ default:
+ /* Other not supported OP mode
+ * Skip data for now for unregonized OP mode
+ */
+ SDEROT_DBG("UNDEFINED\n");
+ rdptr++;
+ break;
+ }
+ }
+ SDEROT_DBG("END %d\n", ctx->timestamp);
+
+ return ctx->timestamp;
+}
+
+/*
+ * sde_hw_rotator_start_regdma - start regdma operation
+ * @ctx: Pointer to rotator context
+ * @queue_id: Priority queue identifier
+ */
+static u32 sde_hw_rotator_start_regdma(struct sde_hw_rotator_context *ctx,
+ enum sde_rot_queue_prio queue_id)
+{
+ struct sde_hw_rotator *rot = ctx->rot;
+ u32 *wrptr;
+ u32 regdmaSlot;
+ u32 offset;
+ long length;
+ long ts_length;
+ u32 enableInt;
+ u32 swts = 0;
+ u32 mask = 0;
+
+ wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+
+ /*
+ * Last ROT command must be ROT_START before REGDMA start
+ */
+ SDE_REGDMA_WRITE(wrptr, ROTTOP_START_CTRL, 1);
+ sde_hw_rotator_put_regdma_segment(ctx, wrptr);
+
+ /*
+ * Start REGDMA with command offset and size
+ */
+ regdmaSlot = sde_hw_rotator_get_regdma_ctxidx(ctx);
+ length = ((long)wrptr - (long)ctx->regdma_base) / 4;
+ offset = (u32)(ctx->regdma_base - (u32 *)(rot->mdss_base +
+ REGDMA_RAM_REGDMA_CMD_RAM));
+ enableInt = ((ctx->timestamp & 1) + 1) << 30;
+
+ SDEROT_DBG(
+ "regdma(%d)[%d] <== INT:0x%X|length:%ld|offset:0x%X, ts:%X\n",
+ queue_id, regdmaSlot, enableInt, length, offset,
+ ctx->timestamp);
+
+ /* ensure the command packet is issued before the submit command */
+ wmb();
+
+ /* REGDMA submission for current context */
+ if (queue_id == ROT_QUEUE_HIGH_PRIORITY) {
+ SDE_ROTREG_WRITE(rot->mdss_base,
+ REGDMA_CSR_REGDMA_QUEUE_0_SUBMIT,
+ (length << 14) | offset);
+ swts = ctx->timestamp;
+ mask = ~SDE_REGDMA_SWTS_MASK;
+ } else {
+ SDE_ROTREG_WRITE(rot->mdss_base,
+ REGDMA_CSR_REGDMA_QUEUE_1_SUBMIT,
+ (length << 14) | offset);
+ swts = ctx->timestamp << SDE_REGDMA_SWTS_SHIFT;
+ mask = ~(SDE_REGDMA_SWTS_MASK << SDE_REGDMA_SWTS_SHIFT);
+ }
+
+ /* Write timestamp after previous rotator job finished */
+ sde_hw_rotator_setup_timestamp_packet(ctx, mask, swts);
+ offset += length;
+ ts_length = sde_hw_rotator_get_regdma_segment(ctx) - wrptr;
+ WARN_ON((length + ts_length) > SDE_HW_ROT_REGDMA_SEG_SIZE);
+
+ /* ensure command packet is issue before the submit command */
+ wmb();
+
+ if (queue_id == ROT_QUEUE_HIGH_PRIORITY) {
+ SDE_ROTREG_WRITE(rot->mdss_base,
+ REGDMA_CSR_REGDMA_QUEUE_0_SUBMIT,
+ enableInt | (ts_length << 14) | offset);
+ } else {
+ SDE_ROTREG_WRITE(rot->mdss_base,
+ REGDMA_CSR_REGDMA_QUEUE_1_SUBMIT,
+ enableInt | (ts_length << 14) | offset);
+ }
+
+ /* Update command queue write ptr */
+ sde_hw_rotator_put_regdma_segment(ctx, wrptr);
+
+ return ctx->timestamp;
+}
+
+/*
+ * sde_hw_rotator_wait_done_no_regdma - wait for non-regdma completion
+ * @ctx: Pointer to rotator context
+ * @queue_id: Priority queue identifier
+ * @flags: Option flag
+ */
+static u32 sde_hw_rotator_wait_done_no_regdma(
+ struct sde_hw_rotator_context *ctx,
+ enum sde_rot_queue_prio queue_id, u32 flag)
+{
+ struct sde_hw_rotator *rot = ctx->rot;
+ int rc = 0;
+ u32 sts = 0;
+ u32 status;
+ unsigned long flags;
+
+ if (rot->irq_num >= 0) {
+ SDEROT_DBG("Wait for Rotator completion\n");
+ rc = wait_for_completion_timeout(&ctx->rot_comp,
+ KOFF_TIMEOUT);
+
+ spin_lock_irqsave(&rot->rotisr_lock, flags);
+ status = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_STATUS);
+ if (rc == 0) {
+ /*
+ * Timeout, there might be error,
+ * or rotator still busy
+ */
+ if (status & ROT_BUSY_BIT)
+ SDEROT_ERR(
+ "Timeout waiting for rotator done\n");
+ else if (status & ROT_ERROR_BIT)
+ SDEROT_ERR(
+ "Rotator report error status\n");
+ else
+ SDEROT_WARN(
+ "Timeout waiting, but rotator job is done!!\n");
+
+ sde_hw_rotator_disable_irq(rot);
+ }
+ spin_unlock_irqrestore(&rot->rotisr_lock, flags);
+ } else {
+ int cnt = 200;
+
+ do {
+ udelay(500);
+ status = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_STATUS);
+ cnt--;
+ } while ((cnt > 0) && (status & ROT_BUSY_BIT)
+ && ((status & ROT_ERROR_BIT) == 0));
+
+ if (status & ROT_ERROR_BIT)
+ SDEROT_ERR("Rotator error\n");
+ else if (status & ROT_BUSY_BIT)
+ SDEROT_ERR("Rotator busy\n");
+
+ SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR,
+ ROT_DONE_CLEAR);
+ }
+
+ sts = (status & ROT_ERROR_BIT) ? -ENODEV : 0;
+
+ return sts;
+}
+
+/*
+ * sde_hw_rotator_wait_done_regdma - wait for regdma completion
+ * @ctx: Pointer to rotator context
+ * @queue_id: Priority queue identifier
+ * @flags: Option flag
+ */
+static u32 sde_hw_rotator_wait_done_regdma(
+ struct sde_hw_rotator_context *ctx,
+ enum sde_rot_queue_prio queue_id, u32 flag)
+{
+ struct sde_hw_rotator *rot = ctx->rot;
+ int rc = 0;
+ u32 status;
+ u32 last_isr;
+ u32 last_ts;
+ u32 int_id;
+ u32 swts;
+ u32 sts = 0;
+ unsigned long flags;
+
+ if (rot->irq_num >= 0) {
+ SDEROT_DBG("Wait for REGDMA completion, ctx:%p, ts:%X\n",
+ ctx, ctx->timestamp);
+ rc = wait_event_timeout(ctx->regdma_waitq,
+ !sde_hw_rotator_pending_swts(rot, ctx, &swts),
+ KOFF_TIMEOUT);
+
+ ATRACE_INT("sde_rot_done", 0);
+ spin_lock_irqsave(&rot->rotisr_lock, flags);
+
+ last_isr = ctx->last_regdma_isr_status;
+ last_ts = ctx->last_regdma_timestamp;
+ status = last_isr & REGDMA_INT_MASK;
+ int_id = last_ts & 1;
+ SDEROT_DBG("INT status:0x%X, INT id:%d, timestamp:0x%X\n",
+ status, int_id, last_ts);
+
+ if (rc == 0 || (status & REGDMA_INT_ERR_MASK)) {
+ bool pending;
+
+ pending = sde_hw_rotator_pending_swts(rot, ctx, &swts);
+ SDEROT_ERR(
+ "Timeout wait for regdma interrupt status, ts:0x%X/0x%X pending:%d\n",
+ ctx->timestamp, swts, pending);
+
+ if (status & REGDMA_WATCHDOG_INT)
+ SDEROT_ERR("REGDMA watchdog interrupt\n");
+ else if (status & REGDMA_INVALID_DESCRIPTOR)
+ SDEROT_ERR("REGDMA invalid descriptor\n");
+ else if (status & REGDMA_INCOMPLETE_CMD)
+ SDEROT_ERR("REGDMA incomplete command\n");
+ else if (status & REGDMA_INVALID_CMD)
+ SDEROT_ERR("REGDMA invalid command\n");
+
+ sde_hw_rotator_dump_status(rot);
+ status = ROT_ERROR_BIT;
+ } else {
+ if (rc == 1)
+ SDEROT_WARN(
+ "REGDMA done but no irq, ts:0x%X/0x%X\n",
+ ctx->timestamp, swts);
+ status = 0;
+ }
+
+ spin_unlock_irqrestore(&rot->rotisr_lock, flags);
+ } else {
+ int cnt = 200;
+ bool pending;
+
+ do {
+ udelay(500);
+ last_isr = SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_INT_STATUS);
+ pending = sde_hw_rotator_pending_swts(rot, ctx, &swts);
+ cnt--;
+ } while ((cnt > 0) && pending &&
+ ((last_isr & REGDMA_INT_ERR_MASK) == 0));
+
+ if (last_isr & REGDMA_INT_ERR_MASK) {
+ SDEROT_ERR("Rotator error, ts:0x%X/0x%X status:%x\n",
+ ctx->timestamp, swts, last_isr);
+ sde_hw_rotator_dump_status(rot);
+ status = ROT_ERROR_BIT;
+ } else if (pending) {
+ SDEROT_ERR("Rotator timeout, ts:0x%X/0x%X status:%x\n",
+ ctx->timestamp, swts, last_isr);
+ sde_hw_rotator_dump_status(rot);
+ status = ROT_ERROR_BIT;
+ } else {
+ status = 0;
+ }
+
+ SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_CLEAR,
+ last_isr);
+ }
+
+ sts = (status & ROT_ERROR_BIT) ? -ENODEV : 0;
+
+ if (status & ROT_ERROR_BIT)
+ SDEROT_EVTLOG_TOUT_HANDLER("rot", "vbif_dbg_bus", "panic");
+
+ return sts;
+}
+
+/*
+ * setup_rotator_ops - setup callback functions for the low-level HAL
+ * @ops: Pointer to low-level ops callback
+ * @mode: Operation mode (non-regdma or regdma)
+ */
+static void setup_rotator_ops(struct sde_hw_rotator_ops *ops,
+ enum sde_rotator_regdma_mode mode)
+{
+ ops->setup_rotator_fetchengine = sde_hw_rotator_setup_fetchengine;
+ ops->setup_rotator_wbengine = sde_hw_rotator_setup_wbengine;
+ if (mode == ROT_REGDMA_ON) {
+ ops->start_rotator = sde_hw_rotator_start_regdma;
+ ops->wait_rotator_done = sde_hw_rotator_wait_done_regdma;
+ } else {
+ ops->start_rotator = sde_hw_rotator_start_no_regdma;
+ ops->wait_rotator_done = sde_hw_rotator_wait_done_no_regdma;
+ }
+}
+
+/*
+ * sde_hw_rotator_swts_create - create software timestamp buffer
+ * @rot: Pointer to rotator hw
+ *
+ * This buffer is used by regdma to keep track of last completed command.
+ */
+static int sde_hw_rotator_swts_create(struct sde_hw_rotator *rot)
+{
+ int rc = 0;
+ struct ion_handle *handle;
+ struct sde_mdp_img_data *data;
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ u32 bufsize = sizeof(int) * SDE_HW_ROT_REGDMA_TOTAL_CTX * 2;
+
+ rot->iclient = mdata->iclient;
+
+ handle = ion_alloc(rot->iclient, bufsize, SZ_4K,
+ ION_HEAP(ION_SYSTEM_HEAP_ID), 0);
+ if (IS_ERR_OR_NULL(handle)) {
+ SDEROT_ERR("ion memory allocation failed\n");
+ return -ENOMEM;
+ }
+
+ data = &rot->swts_buf;
+ data->len = bufsize;
+ data->srcp_dma_buf = ion_share_dma_buf(rot->iclient, handle);
+ if (IS_ERR(data->srcp_dma_buf)) {
+ SDEROT_ERR("ion_dma_buf setup failed\n");
+ rc = -ENOMEM;
+ goto imap_err;
+ }
+
+ sde_smmu_ctrl(1);
+
+ data->srcp_attachment = sde_smmu_dma_buf_attach(data->srcp_dma_buf,
+ &rot->pdev->dev, SDE_IOMMU_DOMAIN_ROT_UNSECURE);
+ if (IS_ERR_OR_NULL(data->srcp_attachment)) {
+ SDEROT_ERR("sde_smmu_dma_buf_attach error\n");
+ rc = -ENOMEM;
+ goto err_put;
+ }
+
+ data->srcp_table = dma_buf_map_attachment(data->srcp_attachment,
+ DMA_BIDIRECTIONAL);
+ if (IS_ERR_OR_NULL(data->srcp_table)) {
+ SDEROT_ERR("dma_buf_map_attachment error\n");
+ rc = -ENOMEM;
+ goto err_detach;
+ }
+
+ rc = sde_smmu_map_dma_buf(data->srcp_dma_buf, data->srcp_table,
+ SDE_IOMMU_DOMAIN_ROT_UNSECURE, &data->addr,
+ &data->len, DMA_BIDIRECTIONAL);
+ if (IS_ERR_VALUE(rc)) {
+ SDEROT_ERR("smmu_map_dma_buf failed: (%d)\n", rc);
+ goto err_unmap;
+ }
+
+ dma_buf_begin_cpu_access(data->srcp_dma_buf, 0, data->len,
+ DMA_FROM_DEVICE);
+ rot->swts_buffer = dma_buf_kmap(data->srcp_dma_buf, 0);
+ if (IS_ERR_OR_NULL(rot->swts_buffer)) {
+ SDEROT_ERR("ion kernel memory mapping failed\n");
+ rc = IS_ERR(rot->swts_buffer);
+ goto kmap_err;
+ }
+
+ data->mapped = true;
+ SDEROT_DBG("swts buffer mapped: %pad/%lx va:%p\n", &data->addr,
+ data->len, rot->swts_buffer);
+
+ ion_free(rot->iclient, handle);
+
+ sde_smmu_ctrl(0);
+
+ return rc;
+kmap_err:
+ sde_smmu_unmap_dma_buf(data->srcp_table, SDE_IOMMU_DOMAIN_ROT_UNSECURE,
+ DMA_FROM_DEVICE, data->srcp_dma_buf);
+err_unmap:
+ dma_buf_unmap_attachment(data->srcp_attachment, data->srcp_table,
+ DMA_FROM_DEVICE);
+err_detach:
+ dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment);
+err_put:
+ dma_buf_put(data->srcp_dma_buf);
+ data->srcp_dma_buf = NULL;
+imap_err:
+ ion_free(rot->iclient, handle);
+
+ return rc;
+}
+
+/*
+ * sde_hw_rotator_swtc_destroy - destroy software timestamp buffer
+ * @rot: Pointer to rotator hw
+ */
+static void sde_hw_rotator_swtc_destroy(struct sde_hw_rotator *rot)
+{
+ struct sde_mdp_img_data *data;
+
+ data = &rot->swts_buf;
+
+ dma_buf_end_cpu_access(data->srcp_dma_buf, 0, data->len,
+ DMA_FROM_DEVICE);
+ dma_buf_kunmap(data->srcp_dma_buf, 0, rot->swts_buffer);
+
+ sde_smmu_unmap_dma_buf(data->srcp_table, SDE_IOMMU_DOMAIN_ROT_UNSECURE,
+ DMA_FROM_DEVICE, data->srcp_dma_buf);
+ dma_buf_unmap_attachment(data->srcp_attachment, data->srcp_table,
+ DMA_FROM_DEVICE);
+ dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment);
+ dma_buf_put(data->srcp_dma_buf);
+ data->srcp_dma_buf = NULL;
+}
+
+/*
+ * sde_hw_rotator_pre_pmevent - SDE rotator core will call this before a
+ * PM event occurs
+ * @mgr: Pointer to rotator manager
+ * @pmon: Boolean indicate an on/off power event
+ */
+void sde_hw_rotator_pre_pmevent(struct sde_rot_mgr *mgr, bool pmon)
+{
+ struct sde_hw_rotator *rot;
+ u32 l_ts, h_ts, swts, hwts;
+ u32 rotsts, regdmasts;
+
+ /*
+ * Check last HW timestamp with SW timestamp before power off event.
+ * If there is a mismatch, that will be quite possible the rotator HW
+ * is either hang or not finishing last submitted job. In that case,
+ * it is best to do a timeout eventlog to capture some good events
+ * log data for analysis.
+ */
+ if (!pmon && mgr && mgr->hw_data) {
+ rot = mgr->hw_data;
+ h_ts = atomic_read(&rot->timestamp[ROT_QUEUE_HIGH_PRIORITY]);
+ l_ts = atomic_read(&rot->timestamp[ROT_QUEUE_LOW_PRIORITY]);
+
+ /* contruct the combined timstamp */
+ swts = (h_ts & SDE_REGDMA_SWTS_MASK) |
+ ((l_ts & SDE_REGDMA_SWTS_MASK) <<
+ SDE_REGDMA_SWTS_SHIFT);
+
+ /* Need to turn on clock to access rotator register */
+ sde_rotator_clk_ctrl(mgr, true);
+ hwts = SDE_ROTREG_READ(rot->mdss_base, REGDMA_TIMESTAMP_REG);
+ regdmasts = SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_BLOCK_STATUS);
+ rotsts = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_STATUS);
+
+ SDEROT_DBG(
+ "swts:0x%x, hwts:0x%x, regdma-sts:0x%x, rottop-sts:0x%x\n",
+ swts, hwts, regdmasts, rotsts);
+ SDEROT_EVTLOG(swts, hwts, regdmasts, rotsts);
+
+ if ((swts != hwts) && ((regdmasts & REGDMA_BUSY) ||
+ (rotsts & ROT_STATUS_MASK))) {
+ SDEROT_ERR(
+ "Mismatch SWTS with HWTS: swts:0x%x, hwts:0x%x, regdma-sts:0x%x, rottop-sts:0x%x\n",
+ swts, hwts, regdmasts, rotsts);
+ SDEROT_EVTLOG_TOUT_HANDLER("rot", "vbif_dbg_bus",
+ "panic");
+ }
+
+ /* Turn off rotator clock after checking rotator registers */
+ sde_rotator_clk_ctrl(mgr, false);
+ }
+}
+
+/*
+ * sde_hw_rotator_post_pmevent - SDE rotator core will call this after a
+ * PM event occurs
+ * @mgr: Pointer to rotator manager
+ * @pmon: Boolean indicate an on/off power event
+ */
+void sde_hw_rotator_post_pmevent(struct sde_rot_mgr *mgr, bool pmon)
+{
+ struct sde_hw_rotator *rot;
+ u32 l_ts, h_ts, swts;
+
+ /*
+ * After a power on event, the rotator HW is reset to default setting.
+ * It is necessary to synchronize the SW timestamp with the HW.
+ */
+ if (pmon && mgr && mgr->hw_data) {
+ rot = mgr->hw_data;
+ h_ts = atomic_read(&rot->timestamp[ROT_QUEUE_HIGH_PRIORITY]);
+ l_ts = atomic_read(&rot->timestamp[ROT_QUEUE_LOW_PRIORITY]);
+
+ /* contruct the combined timstamp */
+ swts = (h_ts & SDE_REGDMA_SWTS_MASK) |
+ ((l_ts & SDE_REGDMA_SWTS_MASK) <<
+ SDE_REGDMA_SWTS_SHIFT);
+
+ SDEROT_DBG("swts:0x%x, h_ts:0x%x, l_ts;0x%x\n",
+ swts, h_ts, l_ts);
+ SDEROT_EVTLOG(swts, h_ts, l_ts);
+ rot->reset_hw_ts = true;
+ rot->last_hw_ts = swts;
+ }
+}
+
+/*
+ * sde_hw_rotator_destroy - Destroy hw rotator and free allocated resources
+ * @mgr: Pointer to rotator manager
+ */
+static void sde_hw_rotator_destroy(struct sde_rot_mgr *mgr)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ struct sde_hw_rotator *rot;
+
+ if (!mgr || !mgr->pdev || !mgr->hw_data) {
+ SDEROT_ERR("null parameters\n");
+ return;
+ }
+
+ rot = mgr->hw_data;
+ if (rot->irq_num >= 0)
+ devm_free_irq(&mgr->pdev->dev, rot->irq_num, mdata);
+
+ if (rot->mode == ROT_REGDMA_ON)
+ sde_hw_rotator_swtc_destroy(rot);
+
+ devm_kfree(&mgr->pdev->dev, mgr->hw_data);
+ mgr->hw_data = NULL;
+}
+
+/*
+ * sde_hw_rotator_alloc_ext - allocate rotator resource from rotator hw
+ * @mgr: Pointer to rotator manager
+ * @pipe_id: pipe identifier (not used)
+ * @wb_id: writeback identifier/priority queue identifier
+ *
+ * This function allocates a new hw rotator resource for the given priority.
+ */
+static struct sde_rot_hw_resource *sde_hw_rotator_alloc_ext(
+ struct sde_rot_mgr *mgr, u32 pipe_id, u32 wb_id)
+{
+ struct sde_hw_rotator_resource_info *resinfo;
+
+ if (!mgr || !mgr->hw_data) {
+ SDEROT_ERR("null parameters\n");
+ return NULL;
+ }
+
+ /*
+ * Allocate rotator resource info. Each allocation is per
+ * HW priority queue
+ */
+ resinfo = devm_kzalloc(&mgr->pdev->dev, sizeof(*resinfo), GFP_KERNEL);
+ if (!resinfo) {
+ SDEROT_ERR("Failed allocation HW rotator resource info\n");
+ return NULL;
+ }
+
+ resinfo->rot = mgr->hw_data;
+ resinfo->hw.wb_id = wb_id;
+ atomic_set(&resinfo->hw.num_active, 0);
+ init_waitqueue_head(&resinfo->hw.wait_queue);
+
+ /* For non-regdma, only support one active session */
+ if (resinfo->rot->mode == ROT_REGDMA_OFF)
+ resinfo->hw.max_active = 1;
+ else {
+ resinfo->hw.max_active = SDE_HW_ROT_REGDMA_TOTAL_CTX - 1;
+
+ if (resinfo->rot->iclient == NULL)
+ sde_hw_rotator_swts_create(resinfo->rot);
+ }
+
+ if (resinfo->rot->irq_num >= 0)
+ sde_hw_rotator_enable_irq(resinfo->rot);
+
+ SDEROT_DBG("New rotator resource:%p, priority:%d\n",
+ resinfo, wb_id);
+
+ return &resinfo->hw;
+}
+
+/*
+ * sde_hw_rotator_free_ext - free the given rotator resource
+ * @mgr: Pointer to rotator manager
+ * @hw: Pointer to rotator resource
+ */
+static void sde_hw_rotator_free_ext(struct sde_rot_mgr *mgr,
+ struct sde_rot_hw_resource *hw)
+{
+ struct sde_hw_rotator_resource_info *resinfo;
+
+ if (!mgr || !mgr->hw_data)
+ return;
+
+ resinfo = container_of(hw, struct sde_hw_rotator_resource_info, hw);
+
+ SDEROT_DBG(
+ "Free rotator resource:%p, priority:%d, active:%d, pending:%d\n",
+ resinfo, hw->wb_id, atomic_read(&hw->num_active),
+ hw->pending_count);
+
+ if (resinfo->rot->irq_num >= 0)
+ sde_hw_rotator_disable_irq(resinfo->rot);
+
+ devm_kfree(&mgr->pdev->dev, resinfo);
+}
+
+/*
+ * sde_hw_rotator_alloc_rotctx - allocate rotator context
+ * @rot: Pointer to rotator hw
+ * @hw: Pointer to rotator resource
+ * @session_id: Session identifier of this context
+ *
+ * This function allocates a new rotator context for the given session id.
+ */
+static struct sde_hw_rotator_context *sde_hw_rotator_alloc_rotctx(
+ struct sde_hw_rotator *rot,
+ struct sde_rot_hw_resource *hw,
+ u32 session_id)
+{
+ struct sde_hw_rotator_context *ctx;
+
+ /* Allocate rotator context */
+ ctx = devm_kzalloc(&rot->pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ SDEROT_ERR("Failed allocation HW rotator context\n");
+ return NULL;
+ }
+
+ ctx->rot = rot;
+ ctx->q_id = hw->wb_id;
+ ctx->session_id = session_id;
+ ctx->hwres = hw;
+ ctx->timestamp = atomic_add_return(1, &rot->timestamp[ctx->q_id]);
+ ctx->timestamp &= SDE_REGDMA_SWTS_MASK;
+ ctx->is_secure = false;
+
+ ctx->regdma_base = rot->cmd_wr_ptr[ctx->q_id]
+ [sde_hw_rotator_get_regdma_ctxidx(ctx)];
+ ctx->regdma_wrptr = ctx->regdma_base;
+ ctx->ts_addr = (dma_addr_t)((u32 *)rot->swts_buf.addr +
+ ctx->q_id * SDE_HW_ROT_REGDMA_TOTAL_CTX +
+ sde_hw_rotator_get_regdma_ctxidx(ctx));
+
+ ctx->last_regdma_timestamp = SDE_REGDMA_SWTS_INVALID;
+
+ init_completion(&ctx->rot_comp);
+ init_waitqueue_head(&ctx->regdma_waitq);
+
+ /* Store rotator context for lookup purpose */
+ sde_hw_rotator_put_ctx(ctx);
+
+ SDEROT_DBG(
+ "New rot CTX:%p, ctxidx:%d, session-id:%d, prio:%d, timestamp:%X, active:%d\n",
+ ctx, sde_hw_rotator_get_regdma_ctxidx(ctx), ctx->session_id,
+ ctx->q_id, ctx->timestamp,
+ atomic_read(&ctx->hwres->num_active));
+
+ return ctx;
+}
+
+/*
+ * sde_hw_rotator_free_rotctx - free the given rotator context
+ * @rot: Pointer to rotator hw
+ * @ctx: Pointer to rotator context
+ */
+static void sde_hw_rotator_free_rotctx(struct sde_hw_rotator *rot,
+ struct sde_hw_rotator_context *ctx)
+{
+ if (!rot || !ctx)
+ return;
+
+ SDEROT_DBG(
+ "Free rot CTX:%p, ctxidx:%d, session-id:%d, prio:%d, timestamp:%X, active:%d\n",
+ ctx, sde_hw_rotator_get_regdma_ctxidx(ctx), ctx->session_id,
+ ctx->q_id, ctx->timestamp,
+ atomic_read(&ctx->hwres->num_active));
+
+ /* Clear rotator context from lookup purpose */
+ sde_hw_rotator_clr_ctx(ctx);
+
+ devm_kfree(&rot->pdev->dev, ctx);
+}
+
+/*
+ * sde_hw_rotator_config - configure hw for the given rotation entry
+ * @hw: Pointer to rotator resource
+ * @entry: Pointer to rotation entry
+ *
+ * This function setup the fetch/writeback/rotator blocks, as well as VBIF
+ * based on the given rotation entry.
+ */
+static int sde_hw_rotator_config(struct sde_rot_hw_resource *hw,
+ struct sde_rot_entry *entry)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ struct sde_hw_rotator *rot;
+ struct sde_hw_rotator_resource_info *resinfo;
+ struct sde_hw_rotator_context *ctx;
+ struct sde_hw_rot_sspp_cfg sspp_cfg;
+ struct sde_hw_rot_wb_cfg wb_cfg;
+ u32 danger_lut = 0; /* applicable for realtime client only */
+ u32 safe_lut = 0; /* applicable for realtime client only */
+ u32 flags = 0;
+ struct sde_rotation_item *item;
+
+ if (!hw || !entry) {
+ SDEROT_ERR("null hw resource/entry\n");
+ return -EINVAL;
+ }
+
+ resinfo = container_of(hw, struct sde_hw_rotator_resource_info, hw);
+ rot = resinfo->rot;
+ item = &entry->item;
+
+ ctx = sde_hw_rotator_alloc_rotctx(rot, hw, item->session_id);
+ if (!ctx) {
+ SDEROT_ERR("Failed allocating rotator context!!\n");
+ return -EINVAL;
+ }
+
+ if (rot->reset_hw_ts) {
+ SDEROT_EVTLOG(rot->last_hw_ts);
+ SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_TIMESTAMP_REG,
+ rot->last_hw_ts);
+ /* ensure write is issued to the rotator HW */
+ wmb();
+ rot->reset_hw_ts = false;
+ }
+
+ flags = (item->flags & SDE_ROTATION_FLIP_LR) ?
+ SDE_ROT_FLAG_FLIP_LR : 0;
+ flags |= (item->flags & SDE_ROTATION_FLIP_UD) ?
+ SDE_ROT_FLAG_FLIP_UD : 0;
+ flags |= (item->flags & SDE_ROTATION_90) ?
+ SDE_ROT_FLAG_ROT_90 : 0;
+ flags |= (item->flags & SDE_ROTATION_DEINTERLACE) ?
+ SDE_ROT_FLAG_DEINTERLACE : 0;
+ flags |= (item->flags & SDE_ROTATION_SECURE) ?
+ SDE_ROT_FLAG_SECURE_OVERLAY_SESSION : 0;
+ flags |= (item->flags & SDE_ROTATION_SECURE_CAMERA) ?
+ SDE_ROT_FLAG_SECURE_CAMERA_SESSION : 0;
+
+
+ sspp_cfg.img_width = item->input.width;
+ sspp_cfg.img_height = item->input.height;
+ sspp_cfg.fps = entry->perf->config.frame_rate;
+ sspp_cfg.bw = entry->perf->bw;
+ sspp_cfg.fmt = sde_get_format_params(item->input.format);
+ if (!sspp_cfg.fmt) {
+ SDEROT_ERR("null format\n");
+ return -EINVAL;
+ }
+ sspp_cfg.src_rect = &item->src_rect;
+ sspp_cfg.data = &entry->src_buf;
+ sde_mdp_get_plane_sizes(sspp_cfg.fmt, item->input.width,
+ item->input.height, &sspp_cfg.src_plane,
+ 0, /* No bwc_mode */
+ (flags & SDE_ROT_FLAG_SOURCE_ROTATED_90) ?
+ true : false);
+
+ rot->ops.setup_rotator_fetchengine(ctx, ctx->q_id,
+ &sspp_cfg, danger_lut, safe_lut,
+ entry->dnsc_factor_w, entry->dnsc_factor_h, flags);
+
+ wb_cfg.img_width = item->output.width;
+ wb_cfg.img_height = item->output.height;
+ wb_cfg.fps = entry->perf->config.frame_rate;
+ wb_cfg.bw = entry->perf->bw;
+ wb_cfg.fmt = sde_get_format_params(item->output.format);
+ wb_cfg.dst_rect = &item->dst_rect;
+ wb_cfg.data = &entry->dst_buf;
+ sde_mdp_get_plane_sizes(wb_cfg.fmt, item->output.width,
+ item->output.height, &wb_cfg.dst_plane,
+ 0, /* No bwc_mode */
+ (flags & SDE_ROT_FLAG_ROT_90) ? true : false);
+
+ wb_cfg.v_downscale_factor = entry->dnsc_factor_h;
+ wb_cfg.h_downscale_factor = entry->dnsc_factor_w;
+
+ rot->ops.setup_rotator_wbengine(ctx, ctx->q_id, &wb_cfg, flags);
+
+ /* setup VA mapping for debugfs */
+ if (rot->dbgmem) {
+ sde_hw_rotator_map_vaddr(&ctx->src_dbgbuf,
+ &item->input,
+ &entry->src_buf);
+
+ sde_hw_rotator_map_vaddr(&ctx->dst_dbgbuf,
+ &item->output,
+ &entry->dst_buf);
+ }
+
+ SDEROT_EVTLOG(ctx->timestamp, flags,
+ item->input.width, item->input.height,
+ item->output.width, item->output.height,
+ entry->src_buf.p[0].addr, entry->dst_buf.p[0].addr,
+ item->input.format, item->output.format,
+ entry->perf->config.frame_rate);
+
+ if (mdata->default_ot_rd_limit) {
+ struct sde_mdp_set_ot_params ot_params;
+
+ memset(&ot_params, 0, sizeof(struct sde_mdp_set_ot_params));
+ ot_params.xin_id = XIN_SSPP;
+ ot_params.num = 0; /* not used */
+ ot_params.width = entry->perf->config.input.width;
+ ot_params.height = entry->perf->config.input.height;
+ ot_params.fps = entry->perf->config.frame_rate;
+ ot_params.reg_off_vbif_lim_conf = MMSS_VBIF_RD_LIM_CONF;
+ ot_params.reg_off_mdp_clk_ctrl =
+ MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0;
+ ot_params.bit_off_mdp_clk_ctrl =
+ MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0_XIN0;
+ ot_params.fmt = ctx->is_traffic_shaping ?
+ SDE_PIX_FMT_ABGR_8888 :
+ entry->perf->config.input.format;
+ ot_params.rotsts_base = rot->mdss_base + ROTTOP_STATUS;
+ ot_params.rotsts_busy_mask = ROT_BUSY_BIT;
+ sde_mdp_set_ot_limit(&ot_params);
+ }
+
+ if (mdata->default_ot_wr_limit) {
+ struct sde_mdp_set_ot_params ot_params;
+
+ memset(&ot_params, 0, sizeof(struct sde_mdp_set_ot_params));
+ ot_params.xin_id = XIN_WRITEBACK;
+ ot_params.num = 0; /* not used */
+ ot_params.width = entry->perf->config.input.width;
+ ot_params.height = entry->perf->config.input.height;
+ ot_params.fps = entry->perf->config.frame_rate;
+ ot_params.reg_off_vbif_lim_conf = MMSS_VBIF_WR_LIM_CONF;
+ ot_params.reg_off_mdp_clk_ctrl =
+ MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0;
+ ot_params.bit_off_mdp_clk_ctrl =
+ MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0_XIN1;
+ ot_params.fmt = ctx->is_traffic_shaping ?
+ SDE_PIX_FMT_ABGR_8888 :
+ entry->perf->config.input.format;
+ ot_params.rotsts_base = rot->mdss_base + ROTTOP_STATUS;
+ ot_params.rotsts_busy_mask = ROT_BUSY_BIT;
+ sde_mdp_set_ot_limit(&ot_params);
+ }
+
+ if (test_bit(SDE_QOS_PER_PIPE_LUT, mdata->sde_qos_map)) {
+ u32 qos_lut = 0; /* low priority for nrt read client */
+
+ trace_rot_perf_set_qos_luts(XIN_SSPP, sspp_cfg.fmt->format,
+ qos_lut, sde_mdp_is_linear_format(sspp_cfg.fmt));
+
+ SDE_ROTREG_WRITE(rot->mdss_base, ROT_SSPP_CREQ_LUT, qos_lut);
+ }
+
+ /* Set CDP control registers to 0 if CDP is disabled */
+ if (!test_bit(SDE_QOS_CDP, mdata->sde_qos_map)) {
+ SDE_ROTREG_WRITE(rot->mdss_base, ROT_SSPP_CDP_CNTL, 0x0);
+ SDE_ROTREG_WRITE(rot->mdss_base, ROT_WB_CDP_CNTL, 0x0);
+ }
+
+ if (mdata->npriority_lvl > 0) {
+ u32 mask, reg_val, i, vbif_qos;
+
+ for (i = 0; i < mdata->npriority_lvl; i++) {
+ reg_val = SDE_VBIF_READ(mdata,
+ MMSS_VBIF_NRT_VBIF_QOS_REMAP_00 + i*4);
+ mask = 0x3 << (XIN_SSPP * 2);
+ reg_val &= ~(mask);
+ vbif_qos = mdata->vbif_nrt_qos[i];
+ reg_val |= vbif_qos << (XIN_SSPP * 2);
+ /* ensure write is issued after the read operation */
+ mb();
+ SDE_VBIF_WRITE(mdata,
+ MMSS_VBIF_NRT_VBIF_QOS_REMAP_00 + i*4,
+ reg_val);
+ }
+ }
+
+ /* Enable write gather for writeback to remove write gaps, which
+ * may hang AXI/BIMC/SDE.
+ */
+ SDE_VBIF_WRITE(mdata, MMSS_VBIF_NRT_VBIF_WRITE_GATHTER_EN,
+ BIT(XIN_WRITEBACK));
+
+ return 0;
+}
+
+/*
+ * sde_hw_rotator_kickoff - kickoff processing on the given entry
+ * @hw: Pointer to rotator resource
+ * @entry: Pointer to rotation entry
+ */
+static int sde_hw_rotator_kickoff(struct sde_rot_hw_resource *hw,
+ struct sde_rot_entry *entry)
+{
+ struct sde_hw_rotator *rot;
+ struct sde_hw_rotator_resource_info *resinfo;
+ struct sde_hw_rotator_context *ctx;
+
+ if (!hw || !entry) {
+ SDEROT_ERR("null hw resource/entry\n");
+ return -EINVAL;
+ }
+
+ resinfo = container_of(hw, struct sde_hw_rotator_resource_info, hw);
+ rot = resinfo->rot;
+
+ /* Lookup rotator context from session-id */
+ ctx = sde_hw_rotator_get_ctx(rot, entry->item.session_id, hw->wb_id);
+ if (!ctx) {
+ SDEROT_ERR("Cannot locate rotator ctx from sesison id:%d\n",
+ entry->item.session_id);
+ return -EINVAL;
+ }
+
+ rot->ops.start_rotator(ctx, ctx->q_id);
+
+ return 0;
+}
+
+/*
+ * sde_hw_rotator_wait4done - wait for completion notification
+ * @hw: Pointer to rotator resource
+ * @entry: Pointer to rotation entry
+ *
+ * This function blocks until the given entry is complete, error
+ * is detected, or timeout.
+ */
+static int sde_hw_rotator_wait4done(struct sde_rot_hw_resource *hw,
+ struct sde_rot_entry *entry)
+{
+ struct sde_hw_rotator *rot;
+ struct sde_hw_rotator_resource_info *resinfo;
+ struct sde_hw_rotator_context *ctx;
+ int ret;
+
+ if (!hw || !entry) {
+ SDEROT_ERR("null hw resource/entry\n");
+ return -EINVAL;
+ }
+
+ resinfo = container_of(hw, struct sde_hw_rotator_resource_info, hw);
+ rot = resinfo->rot;
+
+ /* Lookup rotator context from session-id */
+ ctx = sde_hw_rotator_get_ctx(rot, entry->item.session_id, hw->wb_id);
+ if (!ctx) {
+ SDEROT_ERR("Cannot locate rotator ctx from sesison id:%d\n",
+ entry->item.session_id);
+ return -EINVAL;
+ }
+
+ ret = rot->ops.wait_rotator_done(ctx, ctx->q_id, 0);
+
+ if (rot->dbgmem) {
+ sde_hw_rotator_unmap_vaddr(&ctx->src_dbgbuf);
+ sde_hw_rotator_unmap_vaddr(&ctx->dst_dbgbuf);
+ }
+
+ /* Current rotator context job is finished, time to free up*/
+ sde_hw_rotator_free_rotctx(rot, ctx);
+
+ return ret;
+}
+
+/*
+ * sde_rotator_hw_rev_init - setup feature and/or capability bitmask
+ * @rot: Pointer to hw rotator
+ *
+ * This function initializes feature and/or capability bitmask based on
+ * h/w version read from the device.
+ */
+static int sde_rotator_hw_rev_init(struct sde_hw_rotator *rot)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ u32 hw_version;
+
+ if (!mdata) {
+ SDEROT_ERR("null rotator data\n");
+ return -EINVAL;
+ }
+
+ hw_version = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_HW_VERSION);
+ SDEROT_DBG("hw version %8.8x\n", hw_version);
+
+ clear_bit(SDE_QOS_PER_PIPE_IB, mdata->sde_qos_map);
+ set_bit(SDE_QOS_OVERHEAD_FACTOR, mdata->sde_qos_map);
+ clear_bit(SDE_QOS_CDP, mdata->sde_qos_map);
+ set_bit(SDE_QOS_OTLIM, mdata->sde_qos_map);
+ set_bit(SDE_QOS_PER_PIPE_LUT, mdata->sde_qos_map);
+ clear_bit(SDE_QOS_SIMPLIFIED_PREFILL, mdata->sde_qos_map);
+
+ set_bit(SDE_CAPS_R3_WB, mdata->sde_caps_map);
+
+ if (hw_version != SDE_ROT_TYPE_V1_0) {
+ SDEROT_DBG("Supporting 1.5 downscale for SDE Rotator\n");
+ set_bit(SDE_CAPS_R3_1P5_DOWNSCALE, mdata->sde_caps_map);
+ }
+
+ set_bit(SDE_CAPS_SEC_ATTACH_DETACH_SMMU, mdata->sde_caps_map);
+
+ mdata->nrt_vbif_dbg_bus = nrt_vbif_dbg_bus_r3;
+ mdata->nrt_vbif_dbg_bus_size =
+ ARRAY_SIZE(nrt_vbif_dbg_bus_r3);
+
+ mdata->regdump = sde_rot_r3_regdump;
+ mdata->regdump_size = ARRAY_SIZE(sde_rot_r3_regdump);
+ SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_TIMESTAMP_REG, 0);
+ return 0;
+}
+
+/*
+ * sde_hw_rotator_rotirq_handler - non-regdma interrupt handler
+ * @irq: Interrupt number
+ * @ptr: Pointer to private handle provided during registration
+ *
+ * This function services rotator interrupt and wakes up waiting client
+ * with pending rotation requests already submitted to h/w.
+ */
+static irqreturn_t sde_hw_rotator_rotirq_handler(int irq, void *ptr)
+{
+ struct sde_hw_rotator *rot = ptr;
+ struct sde_hw_rotator_context *ctx;
+ irqreturn_t ret = IRQ_NONE;
+ u32 isr;
+
+ isr = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_INTR_STATUS);
+
+ SDEROT_DBG("intr_status = %8.8x\n", isr);
+
+ if (isr & ROT_DONE_MASK) {
+ if (rot->irq_num >= 0)
+ sde_hw_rotator_disable_irq(rot);
+ SDEROT_DBG("Notify rotator complete\n");
+
+ /* Normal rotator only 1 session, no need to lookup */
+ ctx = rot->rotCtx[0][0];
+ WARN_ON(ctx == NULL);
+ complete_all(&ctx->rot_comp);
+
+ spin_lock(&rot->rotisr_lock);
+ SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR,
+ ROT_DONE_CLEAR);
+ spin_unlock(&rot->rotisr_lock);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+/*
+ * sde_hw_rotator_regdmairq_handler - regdma interrupt handler
+ * @irq: Interrupt number
+ * @ptr: Pointer to private handle provided during registration
+ *
+ * This function services rotator interrupt, decoding the source of
+ * events (high/low priority queue), and wakes up all waiting clients
+ * with pending rotation requests already submitted to h/w.
+ */
+static irqreturn_t sde_hw_rotator_regdmairq_handler(int irq, void *ptr)
+{
+ struct sde_hw_rotator *rot = ptr;
+ struct sde_hw_rotator_context *ctx;
+ irqreturn_t ret = IRQ_NONE;
+ u32 isr;
+ u32 ts;
+ u32 q_id;
+
+ isr = SDE_ROTREG_READ(rot->mdss_base, REGDMA_CSR_REGDMA_INT_STATUS);
+ /* acknowledge interrupt before reading latest timestamp */
+ SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_CLEAR, isr);
+ ts = SDE_ROTREG_READ(rot->mdss_base, REGDMA_TIMESTAMP_REG);
+
+ SDEROT_DBG("intr_status = %8.8x, sw_TS:%X\n", isr, ts);
+
+ /* Any REGDMA status, including error and watchdog timer, should
+ * trigger and wake up waiting thread
+ */
+ if (isr & (REGDMA_INT_HIGH_MASK | REGDMA_INT_LOW_MASK)) {
+ spin_lock(&rot->rotisr_lock);
+
+ /*
+ * Obtain rotator context based on timestamp from regdma
+ * and low/high interrupt status
+ */
+ if (isr & REGDMA_INT_HIGH_MASK) {
+ q_id = ROT_QUEUE_HIGH_PRIORITY;
+ ts = ts & SDE_REGDMA_SWTS_MASK;
+ } else if (isr & REGDMA_INT_LOW_MASK) {
+ q_id = ROT_QUEUE_LOW_PRIORITY;
+ ts = (ts >> SDE_REGDMA_SWTS_SHIFT) &
+ SDE_REGDMA_SWTS_MASK;
+ } else {
+ SDEROT_ERR("unknown ISR status: isr=0x%X\n", isr);
+ goto done_isr_handle;
+ }
+ ctx = rot->rotCtx[q_id][ts & SDE_HW_ROT_REGDMA_SEG_MASK];
+
+ /*
+ * Wake up all waiting context from the current and previous
+ * SW Timestamp.
+ */
+ while (ctx &&
+ sde_hw_rotator_elapsed_swts(ctx->timestamp, ts) >= 0) {
+ ctx->last_regdma_isr_status = isr;
+ ctx->last_regdma_timestamp = ts;
+ SDEROT_DBG(
+ "regdma complete: ctx:%p, ts:%X\n", ctx, ts);
+ wake_up_all(&ctx->regdma_waitq);
+
+ ts = (ts - 1) & SDE_REGDMA_SWTS_MASK;
+ ctx = rot->rotCtx[q_id]
+ [ts & SDE_HW_ROT_REGDMA_SEG_MASK];
+ };
+
+done_isr_handle:
+ spin_unlock(&rot->rotisr_lock);
+ ret = IRQ_HANDLED;
+ } else if (isr & REGDMA_INT_ERR_MASK) {
+ /*
+ * For REGDMA Err, we save the isr info and wake up
+ * all waiting contexts
+ */
+ int i, j;
+
+ SDEROT_ERR(
+ "regdma err isr:%X, wake up all waiting contexts\n",
+ isr);
+
+ spin_lock(&rot->rotisr_lock);
+
+ for (i = 0; i < ROT_QUEUE_MAX; i++) {
+ for (j = 0; j < SDE_HW_ROT_REGDMA_TOTAL_CTX; j++) {
+ ctx = rot->rotCtx[i][j];
+ if (ctx && ctx->last_regdma_isr_status == 0) {
+ ctx->last_regdma_isr_status = isr;
+ ctx->last_regdma_timestamp = ts;
+ wake_up_all(&ctx->regdma_waitq);
+ SDEROT_DBG("Wakeup rotctx[%d][%d]:%p\n",
+ i, j, ctx);
+ }
+ }
+ }
+
+ spin_unlock(&rot->rotisr_lock);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+/*
+ * sde_hw_rotator_validate_entry - validate rotation entry
+ * @mgr: Pointer to rotator manager
+ * @entry: Pointer to rotation entry
+ *
+ * This function validates the given rotation entry and provides possible
+ * fixup (future improvement) if available. This function returns 0 if
+ * the entry is valid, and returns error code otherwise.
+ */
+static int sde_hw_rotator_validate_entry(struct sde_rot_mgr *mgr,
+ struct sde_rot_entry *entry)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ int ret = 0;
+ u16 src_w, src_h, dst_w, dst_h;
+ struct sde_rotation_item *item = &entry->item;
+ struct sde_mdp_format_params *fmt;
+
+ src_w = item->src_rect.w;
+ src_h = item->src_rect.h;
+
+ if (item->flags & SDE_ROTATION_90) {
+ dst_w = item->dst_rect.h;
+ dst_h = item->dst_rect.w;
+ } else {
+ dst_w = item->dst_rect.w;
+ dst_h = item->dst_rect.h;
+ }
+
+ entry->dnsc_factor_w = 0;
+ entry->dnsc_factor_h = 0;
+
+ if ((src_w != dst_w) || (src_h != dst_h)) {
+ if ((src_w % dst_w) || (src_h % dst_h)) {
+ SDEROT_DBG("non integral scale not support\n");
+ ret = -EINVAL;
+ goto dnsc_1p5_check;
+ }
+ entry->dnsc_factor_w = src_w / dst_w;
+ if ((entry->dnsc_factor_w & (entry->dnsc_factor_w - 1)) ||
+ (entry->dnsc_factor_w > 64)) {
+ SDEROT_DBG("non power-of-2 w_scale not support\n");
+ ret = -EINVAL;
+ goto dnsc_err;
+ }
+ entry->dnsc_factor_h = src_h / dst_h;
+ if ((entry->dnsc_factor_h & (entry->dnsc_factor_h - 1)) ||
+ (entry->dnsc_factor_h > 64)) {
+ SDEROT_DBG("non power-of-2 h_scale not support\n");
+ ret = -EINVAL;
+ goto dnsc_err;
+ }
+ }
+
+ fmt = sde_get_format_params(item->output.format);
+ /*
+ * Rotator downscale support max 4 times for UBWC format and
+ * max 2 times for TP10/TP10_UBWC format
+ */
+ if (sde_mdp_is_ubwc_format(fmt) && (entry->dnsc_factor_h > 4)) {
+ SDEROT_DBG("max downscale for UBWC format is 4\n");
+ ret = -EINVAL;
+ goto dnsc_err;
+ }
+ if (sde_mdp_is_tp10_format(fmt) && (entry->dnsc_factor_h > 2)) {
+ SDEROT_DBG("downscale with TP10 cannot be more than 2\n");
+ ret = -EINVAL;
+ }
+ goto dnsc_err;
+
+dnsc_1p5_check:
+ /* Check for 1.5 downscale that only applies to V2 HW */
+ if (test_bit(SDE_CAPS_R3_1P5_DOWNSCALE, mdata->sde_caps_map)) {
+ entry->dnsc_factor_w = src_w / dst_w;
+ if ((entry->dnsc_factor_w != 1) ||
+ ((dst_w * 3) != (src_w * 2))) {
+ SDEROT_DBG(
+ "No supporting non 1.5 downscale width ratio, src_w:%d, dst_w:%d\n",
+ src_w, dst_w);
+ ret = -EINVAL;
+ goto dnsc_err;
+ }
+
+ entry->dnsc_factor_h = src_h / dst_h;
+ if ((entry->dnsc_factor_h != 1) ||
+ ((dst_h * 3) != (src_h * 2))) {
+ SDEROT_DBG(
+ "Not supporting non 1.5 downscale height ratio, src_h:%d, dst_h:%d\n",
+ src_h, dst_h);
+ ret = -EINVAL;
+ goto dnsc_err;
+ }
+ ret = 0;
+ }
+
+dnsc_err:
+ /* Downscaler does not support asymmetrical dnsc */
+ if (entry->dnsc_factor_w != entry->dnsc_factor_h) {
+ SDEROT_DBG("asymmetric downscale not support\n");
+ ret = -EINVAL;
+ }
+
+ if (ret) {
+ entry->dnsc_factor_w = 0;
+ entry->dnsc_factor_h = 0;
+ }
+ return ret;
+}
+
+/*
+ * sde_hw_rotator_show_caps - output capability info to sysfs 'caps' file
+ * @mgr: Pointer to rotator manager
+ * @attr: Pointer to device attribute interface
+ * @buf: Pointer to output buffer
+ * @len: Length of output buffer
+ */
+static ssize_t sde_hw_rotator_show_caps(struct sde_rot_mgr *mgr,
+ struct device_attribute *attr, char *buf, ssize_t len)
+{
+ struct sde_hw_rotator *hw_data;
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ int cnt = 0;
+
+ if (!mgr || !buf)
+ return 0;
+
+ hw_data = mgr->hw_data;
+
+#define SPRINT(fmt, ...) \
+ (cnt += scnprintf(buf + cnt, len - cnt, fmt, ##__VA_ARGS__))
+
+ /* insert capabilities here */
+ if (test_bit(SDE_CAPS_R3_1P5_DOWNSCALE, mdata->sde_caps_map))
+ SPRINT("min_downscale=1.5\n");
+ else
+ SPRINT("min_downscale=2.0\n");
+
+ SPRINT("downscale_compression=1\n");
+
+#undef SPRINT
+ return cnt;
+}
+
+/*
+ * sde_hw_rotator_show_state - output state info to sysfs 'state' file
+ * @mgr: Pointer to rotator manager
+ * @attr: Pointer to device attribute interface
+ * @buf: Pointer to output buffer
+ * @len: Length of output buffer
+ */
+static ssize_t sde_hw_rotator_show_state(struct sde_rot_mgr *mgr,
+ struct device_attribute *attr, char *buf, ssize_t len)
+{
+ struct sde_hw_rotator *rot;
+ struct sde_hw_rotator_context *ctx;
+ int cnt = 0;
+ int num_active = 0;
+ int i, j;
+
+ if (!mgr || !buf) {
+ SDEROT_ERR("null parameters\n");
+ return 0;
+ }
+
+ rot = mgr->hw_data;
+
+#define SPRINT(fmt, ...) \
+ (cnt += scnprintf(buf + cnt, len - cnt, fmt, ##__VA_ARGS__))
+
+ if (rot) {
+ SPRINT("rot_mode=%d\n", rot->mode);
+ SPRINT("irq_num=%d\n", rot->irq_num);
+
+ if (rot->mode == ROT_REGDMA_OFF) {
+ SPRINT("max_active=1\n");
+ SPRINT("num_active=%d\n", rot->rotCtx[0][0] ? 1 : 0);
+ } else {
+ for (i = 0; i < ROT_QUEUE_MAX; i++) {
+ for (j = 0; j < SDE_HW_ROT_REGDMA_TOTAL_CTX;
+ j++) {
+ ctx = rot->rotCtx[i][j];
+
+ if (ctx) {
+ SPRINT(
+ "rotCtx[%d][%d]:%p\n",
+ i, j, ctx);
+ ++num_active;
+ }
+ }
+ }
+
+ SPRINT("max_active=%d\n", SDE_HW_ROT_REGDMA_TOTAL_CTX);
+ SPRINT("num_active=%d\n", num_active);
+ }
+ }
+
+#undef SPRINT
+ return cnt;
+}
+
+/*
+ * sde_hw_rotator_get_pixfmt - get the indexed pixel format
+ * @mgr: Pointer to rotator manager
+ * @index: index of pixel format
+ * @input: true for input port; false for output port
+ */
+static u32 sde_hw_rotator_get_pixfmt(struct sde_rot_mgr *mgr,
+ int index, bool input)
+{
+ if (input) {
+ if (index < ARRAY_SIZE(sde_hw_rotator_input_pixfmts))
+ return sde_hw_rotator_input_pixfmts[index];
+ else
+ return 0;
+ } else {
+ if (index < ARRAY_SIZE(sde_hw_rotator_output_pixfmts))
+ return sde_hw_rotator_output_pixfmts[index];
+ else
+ return 0;
+ }
+}
+
+/*
+ * sde_hw_rotator_is_valid_pixfmt - verify if the given pixel format is valid
+ * @mgr: Pointer to rotator manager
+ * @pixfmt: pixel format to be verified
+ * @input: true for input port; false for output port
+ */
+static int sde_hw_rotator_is_valid_pixfmt(struct sde_rot_mgr *mgr, u32 pixfmt,
+ bool input)
+{
+ int i;
+
+ if (input) {
+ for (i = 0; i < ARRAY_SIZE(sde_hw_rotator_input_pixfmts); i++)
+ if (sde_hw_rotator_input_pixfmts[i] == pixfmt)
+ return true;
+ } else {
+ for (i = 0; i < ARRAY_SIZE(sde_hw_rotator_output_pixfmts); i++)
+ if (sde_hw_rotator_output_pixfmts[i] == pixfmt)
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * sde_hw_rotator_parse_dt - parse r3 specific device tree settings
+ * @hw_data: Pointer to rotator hw
+ * @dev: Pointer to platform device
+ */
+static int sde_hw_rotator_parse_dt(struct sde_hw_rotator *hw_data,
+ struct platform_device *dev)
+{
+ int ret = 0;
+ u32 data;
+
+ if (!hw_data || !dev)
+ return -EINVAL;
+
+ ret = of_property_read_u32(dev->dev.of_node, "qcom,mdss-rot-mode",
+ &data);
+ if (ret) {
+ SDEROT_DBG("default to regdma off\n");
+ ret = 0;
+ hw_data->mode = ROT_REGDMA_OFF;
+ } else if (data < ROT_REGDMA_MAX) {
+ SDEROT_DBG("set to regdma mode %d\n", data);
+ hw_data->mode = data;
+ } else {
+ SDEROT_ERR("regdma mode out of range. default to regdma off\n");
+ hw_data->mode = ROT_REGDMA_OFF;
+ }
+
+ ret = of_property_read_u32(dev->dev.of_node,
+ "qcom,mdss-highest-bank-bit", &data);
+ if (ret) {
+ SDEROT_DBG("default to A5X bank\n");
+ ret = 0;
+ hw_data->highest_bank = 2;
+ } else {
+ SDEROT_DBG("set highest bank bit to %d\n", data);
+ hw_data->highest_bank = data;
+ }
+
+ return ret;
+}
+
+/*
+ * sde_rotator_r3_init - initialize the r3 module
+ * @mgr: Pointer to rotator manager
+ *
+ * This function setup r3 callback functions, parses r3 specific
+ * device tree settings, installs r3 specific interrupt handler,
+ * as well as initializes r3 internal data structure.
+ */
+int sde_rotator_r3_init(struct sde_rot_mgr *mgr)
+{
+ struct sde_hw_rotator *rot;
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ int i;
+ int ret;
+
+ rot = devm_kzalloc(&mgr->pdev->dev, sizeof(*rot), GFP_KERNEL);
+ if (!rot)
+ return -ENOMEM;
+
+ mgr->hw_data = rot;
+ mgr->queue_count = ROT_QUEUE_MAX;
+
+ rot->mdss_base = mdata->sde_io.base;
+ rot->pdev = mgr->pdev;
+
+ /* Assign ops */
+ mgr->ops_hw_destroy = sde_hw_rotator_destroy;
+ mgr->ops_hw_alloc = sde_hw_rotator_alloc_ext;
+ mgr->ops_hw_free = sde_hw_rotator_free_ext;
+ mgr->ops_config_hw = sde_hw_rotator_config;
+ mgr->ops_kickoff_entry = sde_hw_rotator_kickoff;
+ mgr->ops_wait_for_entry = sde_hw_rotator_wait4done;
+ mgr->ops_hw_validate_entry = sde_hw_rotator_validate_entry;
+ mgr->ops_hw_show_caps = sde_hw_rotator_show_caps;
+ mgr->ops_hw_show_state = sde_hw_rotator_show_state;
+ mgr->ops_hw_create_debugfs = sde_rotator_r3_create_debugfs;
+ mgr->ops_hw_get_pixfmt = sde_hw_rotator_get_pixfmt;
+ mgr->ops_hw_is_valid_pixfmt = sde_hw_rotator_is_valid_pixfmt;
+ mgr->ops_hw_pre_pmevent = sde_hw_rotator_pre_pmevent;
+ mgr->ops_hw_post_pmevent = sde_hw_rotator_post_pmevent;
+
+ ret = sde_hw_rotator_parse_dt(mgr->hw_data, mgr->pdev);
+ if (ret)
+ goto error_parse_dt;
+
+ rot->irq_num = platform_get_irq(mgr->pdev, 0);
+ if (rot->irq_num < 0) {
+ SDEROT_ERR("fail to get rotator irq\n");
+ } else {
+ if (rot->mode == ROT_REGDMA_OFF)
+ ret = devm_request_threaded_irq(&mgr->pdev->dev,
+ rot->irq_num,
+ sde_hw_rotator_rotirq_handler,
+ NULL, 0, "sde_rotator_r3", rot);
+ else
+ ret = devm_request_threaded_irq(&mgr->pdev->dev,
+ rot->irq_num,
+ sde_hw_rotator_regdmairq_handler,
+ NULL, 0, "sde_rotator_r3", rot);
+ if (ret) {
+ SDEROT_ERR("fail to request irq r:%d\n", ret);
+ rot->irq_num = -1;
+ } else {
+ disable_irq(rot->irq_num);
+ }
+ }
+ atomic_set(&rot->irq_enabled, 0);
+
+ setup_rotator_ops(&rot->ops, rot->mode);
+
+ spin_lock_init(&rot->rotctx_lock);
+ spin_lock_init(&rot->rotisr_lock);
+
+ /* REGDMA initialization */
+ if (rot->mode == ROT_REGDMA_OFF) {
+ for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++)
+ rot->cmd_wr_ptr[0][i] = &rot->cmd_queue[
+ SDE_HW_ROT_REGDMA_SEG_SIZE * i];
+ } else {
+ for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++)
+ rot->cmd_wr_ptr[ROT_QUEUE_HIGH_PRIORITY][i] =
+ (u32 *)(rot->mdss_base +
+ REGDMA_RAM_REGDMA_CMD_RAM +
+ SDE_HW_ROT_REGDMA_SEG_SIZE * 4 * i);
+
+ for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++)
+ rot->cmd_wr_ptr[ROT_QUEUE_LOW_PRIORITY][i] =
+ (u32 *)(rot->mdss_base +
+ REGDMA_RAM_REGDMA_CMD_RAM +
+ SDE_HW_ROT_REGDMA_SEG_SIZE * 4 *
+ (i + SDE_HW_ROT_REGDMA_TOTAL_CTX));
+ }
+
+ atomic_set(&rot->timestamp[0], 0);
+ atomic_set(&rot->timestamp[1], 0);
+
+ ret = sde_rotator_hw_rev_init(rot);
+ if (ret)
+ goto error_hw_rev_init;
+
+ /* set rotator CBCR to shutoff memory/periphery on clock off.*/
+ clk_set_flags(mgr->rot_clk[SDE_ROTATOR_CLK_ROT_CORE].clk,
+ CLKFLAG_NORETAIN_MEM);
+ clk_set_flags(mgr->rot_clk[SDE_ROTATOR_CLK_ROT_CORE].clk,
+ CLKFLAG_NORETAIN_PERIPH);
+
+ mdata->sde_rot_hw = rot;
+ return 0;
+error_hw_rev_init:
+ if (rot->irq_num >= 0)
+ devm_free_irq(&mgr->pdev->dev, rot->irq_num, mdata);
+ devm_kfree(&mgr->pdev->dev, mgr->hw_data);
+error_parse_dt:
+ return ret;
+}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.h
new file mode 100644
index 0000000..e6158a9
--- /dev/null
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SDE_ROTATOR_R3_H__
+#define __SDE_ROTATOR_R3_H__
+
+#include "sde_rotator_core.h"
+
+int sde_rotator_r3_init(struct sde_rot_mgr *mgr);
+
+#endif /* __SDE_ROTATOR_R3_H__ */
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_debug.c
new file mode 100644
index 0000000..987e61c
--- /dev/null
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_debug.c
@@ -0,0 +1,48 @@
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+
+#include "sde_rotator_r3_debug.h"
+#include "sde_rotator_core.h"
+#include "sde_rotator_r3.h"
+#include "sde_rotator_r3_internal.h"
+
+/*
+ * sde_rotator_r3_create_debugfs - Setup rotator r3 debugfs directory structure.
+ * @rot_dev: Pointer to rotator device
+ */
+int sde_rotator_r3_create_debugfs(struct sde_rot_mgr *mgr,
+ struct dentry *debugfs_root)
+{
+ struct sde_hw_rotator *hw_data;
+
+ if (!mgr || !debugfs_root || !mgr->hw_data)
+ return -EINVAL;
+
+ hw_data = mgr->hw_data;
+
+ if (!debugfs_create_bool("dbgmem", 0644,
+ debugfs_root, &hw_data->dbgmem)) {
+ SDEROT_ERR("fail create dbgmem\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_debug.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_debug.h
new file mode 100644
index 0000000..25cd637
--- /dev/null
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_debug.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SDE_ROTATOR_R3_DEBUG_H__
+#define __SDE_ROTATOR_R3_DEBUG_H__
+
+#include <linux/types.h>
+#include <linux/dcache.h>
+
+struct sde_rot_mgr;
+
+#if defined(CONFIG_DEBUG_FS)
+int sde_rotator_r3_create_debugfs(struct sde_rot_mgr *mgr,
+ struct dentry *debugfs_root);
+#else
+static inline
+int sde_rotator_r3_create_debugfs(struct sde_rot_mgr *mgr,
+ struct dentry *debugfs_root)
+{
+ return 0;
+}
+#endif
+#endif /* __SDE_ROTATOR_R3_DEBUG_H__ */
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h
new file mode 100644
index 0000000..fedade1
--- /dev/null
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h
@@ -0,0 +1,281 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _SDE_ROTATOR_R3_HWIO_H
+#define _SDE_ROTATOR_R3_HWIO_H
+
+#include <linux/bitops.h>
+
+/* MMSS_MDSS:
+ * OFFSET=0x000000
+ */
+#define MMSS_MDSS_HW_INTR_STATUS 0x10
+#define MMSS_MDSS_HW_INTR_STATUS_ROT BIT(2)
+
+/* SDE_ROT_ROTTOP:
+ * OFFSET=0x0A8800
+ */
+#define SDE_ROT_ROTTOP_OFFSET 0xA8800
+#define ROTTOP_HW_VERSION (SDE_ROT_ROTTOP_OFFSET+0x00)
+#define ROTTOP_CLK_CTRL (SDE_ROT_ROTTOP_OFFSET+0x10)
+#define ROTTOP_CLK_STATUS (SDE_ROT_ROTTOP_OFFSET+0x14)
+#define ROTTOP_ROT_NEWROI_PRIOR_TO_START (SDE_ROT_ROTTOP_OFFSET+0x18)
+#define ROTTOP_SW_RESET (SDE_ROT_ROTTOP_OFFSET+0x20)
+#define ROTTOP_SW_RESET_CTRL (SDE_ROT_ROTTOP_OFFSET+0x24)
+#define ROTTOP_SW_RESET_OVERRIDE (SDE_ROT_ROTTOP_OFFSET+0x28)
+#define ROTTOP_INTR_EN (SDE_ROT_ROTTOP_OFFSET+0x30)
+#define ROTTOP_INTR_STATUS (SDE_ROT_ROTTOP_OFFSET+0x34)
+#define ROTTOP_INTR_CLEAR (SDE_ROT_ROTTOP_OFFSET+0x38)
+#define ROTTOP_START_CTRL (SDE_ROT_ROTTOP_OFFSET+0x40)
+#define ROTTOP_STATUS (SDE_ROT_ROTTOP_OFFSET+0x44)
+#define ROTTOP_OP_MODE (SDE_ROT_ROTTOP_OFFSET+0x48)
+#define ROTTOP_DNSC (SDE_ROT_ROTTOP_OFFSET+0x4C)
+#define ROTTOP_DEBUGBUS_CTRL (SDE_ROT_ROTTOP_OFFSET+0x50)
+#define ROTTOP_DEBUGBUS_STATUS (SDE_ROT_ROTTOP_OFFSET+0x54)
+#define ROTTOP_ROT_UBWC_DEC_VERSION (SDE_ROT_ROTTOP_OFFSET+0x58)
+#define ROTTOP_ROT_UBWC_ENC_VERSION (SDE_ROT_ROTTOP_OFFSET+0x5C)
+
+/* SDE_ROT_SSPP:
+ * OFFSET=0x0A8900
+ */
+#define SDE_ROT_SSPP_OFFSET 0xA8900
+#define ROT_SSPP_SRC_SIZE (SDE_ROT_SSPP_OFFSET+0x00)
+#define ROT_SSPP_SRC_IMG_SIZE (SDE_ROT_SSPP_OFFSET+0x04)
+#define ROT_SSPP_SRC_XY (SDE_ROT_SSPP_OFFSET+0x08)
+#define ROT_SSPP_OUT_SIZE (SDE_ROT_SSPP_OFFSET+0x0C)
+#define ROT_SSPP_OUT_XY (SDE_ROT_SSPP_OFFSET+0x10)
+#define ROT_SSPP_SRC0_ADDR (SDE_ROT_SSPP_OFFSET+0x14)
+#define ROT_SSPP_SRC1_ADDR (SDE_ROT_SSPP_OFFSET+0x18)
+#define ROT_SSPP_SRC2_ADDR (SDE_ROT_SSPP_OFFSET+0x1C)
+#define ROT_SSPP_SRC3_ADDR (SDE_ROT_SSPP_OFFSET+0x20)
+#define ROT_SSPP_SRC_YSTRIDE0 (SDE_ROT_SSPP_OFFSET+0x24)
+#define ROT_SSPP_SRC_YSTRIDE1 (SDE_ROT_SSPP_OFFSET+0x28)
+#define ROT_SSPP_TILE_FRAME_SIZE (SDE_ROT_SSPP_OFFSET+0x2C)
+#define ROT_SSPP_SRC_FORMAT (SDE_ROT_SSPP_OFFSET+0x30)
+#define ROT_SSPP_SRC_UNPACK_PATTERN (SDE_ROT_SSPP_OFFSET+0x34)
+#define ROT_SSPP_SRC_OP_MODE (SDE_ROT_SSPP_OFFSET+0x38)
+#define ROT_SSPP_SRC_CONSTANT_COLOR (SDE_ROT_SSPP_OFFSET+0x3C)
+#define ROT_SSPP_FETCH_CONFIG (SDE_ROT_SSPP_OFFSET+0x48)
+#define ROT_SSPP_VC1_RANGE (SDE_ROT_SSPP_OFFSET+0x4C)
+#define ROT_SSPP_REQPRIORITY_FIFO_WATERMARK_0 (SDE_ROT_SSPP_OFFSET+0x50)
+#define ROT_SSPP_REQPRIORITY_FIFO_WATERMARK_1 (SDE_ROT_SSPP_OFFSET+0x54)
+#define ROT_SSPP_REQPRIORITY_FIFO_WATERMARK_2 (SDE_ROT_SSPP_OFFSET+0x58)
+#define ROT_SSPP_DANGER_LUT (SDE_ROT_SSPP_OFFSET+0x60)
+#define ROT_SSPP_SAFE_LUT (SDE_ROT_SSPP_OFFSET+0x64)
+#define ROT_SSPP_CREQ_LUT (SDE_ROT_SSPP_OFFSET+0x68)
+#define ROT_SSPP_QOS_CTRL (SDE_ROT_SSPP_OFFSET+0x6C)
+#define ROT_SSPP_SRC_ADDR_SW_STATUS (SDE_ROT_SSPP_OFFSET+0x70)
+#define ROT_SSPP_CURRENT_SRC0_ADDR (SDE_ROT_SSPP_OFFSET+0xA4)
+#define ROT_SSPP_CURRENT_SRC1_ADDR (SDE_ROT_SSPP_OFFSET+0xA8)
+#define ROT_SSPP_CURRENT_SRC2_ADDR (SDE_ROT_SSPP_OFFSET+0xAC)
+#define ROT_SSPP_CURRENT_SRC3_ADDR (SDE_ROT_SSPP_OFFSET+0xB0)
+#define ROT_SSPP_DECIMATION_CONFIG (SDE_ROT_SSPP_OFFSET+0xB4)
+#define ROT_SSPP_FETCH_SMP_WR_PLANE0 (SDE_ROT_SSPP_OFFSET+0xD0)
+#define ROT_SSPP_FETCH_SMP_WR_PLANE1 (SDE_ROT_SSPP_OFFSET+0xD4)
+#define ROT_SSPP_FETCH_SMP_WR_PLANE2 (SDE_ROT_SSPP_OFFSET+0xD8)
+#define ROT_SSPP_SMP_UNPACK_RD_PLANE0 (SDE_ROT_SSPP_OFFSET+0xE0)
+#define ROT_SSPP_SMP_UNPACK_RD_PLANE1 (SDE_ROT_SSPP_OFFSET+0xE4)
+#define ROT_SSPP_SMP_UNPACK_RD_PLANE2 (SDE_ROT_SSPP_OFFSET+0xE8)
+#define ROT_SSPP_FILL_LEVELS (SDE_ROT_SSPP_OFFSET+0xF0)
+#define ROT_SSPP_STATUS (SDE_ROT_SSPP_OFFSET+0xF4)
+#define ROT_SSPP_UNPACK_LINE_COUNT (SDE_ROT_SSPP_OFFSET+0xF8)
+#define ROT_SSPP_UNPACK_BLK_COUNT (SDE_ROT_SSPP_OFFSET+0xFC)
+#define ROT_SSPP_SW_PIX_EXT_C0_LR (SDE_ROT_SSPP_OFFSET+0x100)
+#define ROT_SSPP_SW_PIX_EXT_C0_TB (SDE_ROT_SSPP_OFFSET+0x104)
+#define ROT_SSPP_SW_PIX_EXT_C0_REQ_PIXELS (SDE_ROT_SSPP_OFFSET+0x108)
+#define ROT_SSPP_SW_PIX_EXT_C1C2_LR (SDE_ROT_SSPP_OFFSET+0x110)
+#define ROT_SSPP_SW_PIX_EXT_C1C2_TB (SDE_ROT_SSPP_OFFSET+0x114)
+#define ROT_SSPP_SW_PIX_EXT_C1C2_REQ_PIXELS (SDE_ROT_SSPP_OFFSET+0x118)
+#define ROT_SSPP_SW_PIX_EXT_C3_LR (SDE_ROT_SSPP_OFFSET+0x120)
+#define ROT_SSPP_SW_PIX_EXT_C3_TB (SDE_ROT_SSPP_OFFSET+0x124)
+#define ROT_SSPP_SW_PIX_EXT_C3_REQ_PIXELS (SDE_ROT_SSPP_OFFSET+0x128)
+#define ROT_SSPP_TRAFFIC_SHAPER (SDE_ROT_SSPP_OFFSET+0x130)
+#define ROT_SSPP_CDP_CNTL (SDE_ROT_SSPP_OFFSET+0x134)
+#define ROT_SSPP_UBWC_ERROR_STATUS (SDE_ROT_SSPP_OFFSET+0x138)
+#define ROT_SSPP_SW_CROP_W_C0C3 (SDE_ROT_SSPP_OFFSET+0x140)
+#define ROT_SSPP_SW_CROP_W_C1C2 (SDE_ROT_SSPP_OFFSET+0x144)
+#define ROT_SSPP_SW_CROP_H_C0C3 (SDE_ROT_SSPP_OFFSET+0x148)
+#define ROT_SSPP_SW_CROP_H_C1C2 (SDE_ROT_SSPP_OFFSET+0x14C)
+#define ROT_SSPP_TRAFFIC_SHAPER_PREFILL (SDE_ROT_SSPP_OFFSET+0x150)
+#define ROT_SSPP_TRAFFIC_SHAPER_REC1_PREFILL (SDE_ROT_SSPP_OFFSET+0x154)
+#define ROT_SSPP_OUT_SIZE_REC1 (SDE_ROT_SSPP_OFFSET+0x160)
+#define ROT_SSPP_OUT_XY_REC1 (SDE_ROT_SSPP_OFFSET+0x164)
+#define ROT_SSPP_SRC_XY_REC1 (SDE_ROT_SSPP_OFFSET+0x168)
+#define ROT_SSPP_SRC_SIZE_REC1 (SDE_ROT_SSPP_OFFSET+0x16C)
+#define ROT_SSPP_MULTI_REC_OP_MODE (SDE_ROT_SSPP_OFFSET+0x170)
+#define ROT_SSPP_SRC_FORMAT_REC1 (SDE_ROT_SSPP_OFFSET+0x174)
+#define ROT_SSPP_SRC_UNPACK_PATTERN_REC1 (SDE_ROT_SSPP_OFFSET+0x178)
+#define ROT_SSPP_SRC_OP_MODE_REC1 (SDE_ROT_SSPP_OFFSET+0x17C)
+#define ROT_SSPP_SRC_CONSTANT_COLOR_REC1 (SDE_ROT_SSPP_OFFSET+0x180)
+#define ROT_SSPP_TPG_CONTROL (SDE_ROT_SSPP_OFFSET+0x190)
+#define ROT_SSPP_TPG_CONFIG (SDE_ROT_SSPP_OFFSET+0x194)
+#define ROT_SSPP_TPG_COMPONENT_LIMITS (SDE_ROT_SSPP_OFFSET+0x198)
+#define ROT_SSPP_TPG_RECTANGLE (SDE_ROT_SSPP_OFFSET+0x19C)
+#define ROT_SSPP_TPG_BLACK_WHITE_PATTERN_FRAMES (SDE_ROT_SSPP_OFFSET+0x1A0)
+#define ROT_SSPP_TPG_RGB_MAPPING (SDE_ROT_SSPP_OFFSET+0x1A4)
+#define ROT_SSPP_TPG_PATTERN_GEN_INIT_VAL (SDE_ROT_SSPP_OFFSET+0x1A8)
+
+#define SDE_ROT_SSPP_FETCH_CONFIG_RESET_VALUE 0x00087
+#define SDE_ROT_SSPP_FETCH_BLOCKSIZE_128 (0 << 16)
+#define SDE_ROT_SSPP_FETCH_BLOCKSIZE_96 (2 << 16)
+#define SDE_ROT_SSPP_FETCH_BLOCKSIZE_192_EXT ((0 << 16) | (1 << 15))
+#define SDE_ROT_SSPP_FETCH_BLOCKSIZE_144_EXT ((2 << 16) | (1 << 15))
+
+
+/* SDE_ROT_WB:
+ * OFFSET=0x0A8B00
+ */
+#define SDE_ROT_WB_OFFSET 0xA8B00
+#define ROT_WB_DST_FORMAT (SDE_ROT_WB_OFFSET+0x000)
+#define ROT_WB_DST_OP_MODE (SDE_ROT_WB_OFFSET+0x004)
+#define ROT_WB_DST_PACK_PATTERN (SDE_ROT_WB_OFFSET+0x008)
+#define ROT_WB_DST0_ADDR (SDE_ROT_WB_OFFSET+0x00C)
+#define ROT_WB_DST1_ADDR (SDE_ROT_WB_OFFSET+0x010)
+#define ROT_WB_DST2_ADDR (SDE_ROT_WB_OFFSET+0x014)
+#define ROT_WB_DST3_ADDR (SDE_ROT_WB_OFFSET+0x018)
+#define ROT_WB_DST_YSTRIDE0 (SDE_ROT_WB_OFFSET+0x01C)
+#define ROT_WB_DST_YSTRIDE1 (SDE_ROT_WB_OFFSET+0x020)
+#define ROT_WB_DST_DITHER_BITDEPTH (SDE_ROT_WB_OFFSET+0x024)
+#define ROT_WB_DITHER_MATRIX_ROW0 (SDE_ROT_WB_OFFSET+0x030)
+#define ROT_WB_DITHER_MATRIX_ROW1 (SDE_ROT_WB_OFFSET+0x034)
+#define ROT_WB_DITHER_MATRIX_ROW2 (SDE_ROT_WB_OFFSET+0x038)
+#define ROT_WB_DITHER_MATRIX_ROW3 (SDE_ROT_WB_OFFSET+0x03C)
+#define ROT_WB_TRAFFIC_SHAPER_WR_CLIENT (SDE_ROT_WB_OFFSET+0x040)
+#define ROT_WB_DST_WRITE_CONFIG (SDE_ROT_WB_OFFSET+0x048)
+#define ROT_WB_ROTATOR_PIPE_DOWNSCALER (SDE_ROT_WB_OFFSET+0x054)
+#define ROT_WB_OUT_SIZE (SDE_ROT_WB_OFFSET+0x074)
+#define ROT_WB_DST_ALPHA_X_VALUE (SDE_ROT_WB_OFFSET+0x078)
+#define ROT_WB_HW_VERSION (SDE_ROT_WB_OFFSET+0x080)
+#define ROT_WB_DANGER_LUT (SDE_ROT_WB_OFFSET+0x084)
+#define ROT_WB_SAFE_LUT (SDE_ROT_WB_OFFSET+0x088)
+#define ROT_WB_CREQ_LUT (SDE_ROT_WB_OFFSET+0x08C)
+#define ROT_WB_QOS_CTRL (SDE_ROT_WB_OFFSET+0x090)
+#define ROT_WB_CSC_MATRIX_COEFF_0 (SDE_ROT_WB_OFFSET+0x260)
+#define ROT_WB_CSC_MATRIX_COEFF_1 (SDE_ROT_WB_OFFSET+0x264)
+#define ROT_WB_CSC_MATRIX_COEFF_2 (SDE_ROT_WB_OFFSET+0x268)
+#define ROT_WB_CSC_MATRIX_COEFF_3 (SDE_ROT_WB_OFFSET+0x26C)
+#define ROT_WB_CSC_MATRIX_COEFF_4 (SDE_ROT_WB_OFFSET+0x270)
+#define ROT_WB_CSC_COMP0_PRECLAMP (SDE_ROT_WB_OFFSET+0x274)
+#define ROT_WB_CSC_COMP1_PRECLAMP (SDE_ROT_WB_OFFSET+0x278)
+#define ROT_WB_CSC_COMP2_PRECLAMP (SDE_ROT_WB_OFFSET+0x27C)
+#define ROT_WB_CSC_COMP0_POSTCLAMP (SDE_ROT_WB_OFFSET+0x280)
+#define ROT_WB_CSC_COMP1_POSTCLAMP (SDE_ROT_WB_OFFSET+0x284)
+#define ROT_WB_CSC_COMP2_POSTCLAMP (SDE_ROT_WB_OFFSET+0x288)
+#define ROT_WB_CSC_COMP0_PREBIAS (SDE_ROT_WB_OFFSET+0x28C)
+#define ROT_WB_CSC_COMP1_PREBIAS (SDE_ROT_WB_OFFSET+0x290)
+#define ROT_WB_CSC_COMP2_PREBIAS (SDE_ROT_WB_OFFSET+0x294)
+#define ROT_WB_CSC_COMP0_POSTBIAS (SDE_ROT_WB_OFFSET+0x298)
+#define ROT_WB_CSC_COMP1_POSTBIAS (SDE_ROT_WB_OFFSET+0x29C)
+#define ROT_WB_CSC_COMP2_POSTBIAS (SDE_ROT_WB_OFFSET+0x2A0)
+#define ROT_WB_DST_ADDR_SW_STATUS (SDE_ROT_WB_OFFSET+0x2B0)
+#define ROT_WB_CDP_CNTL (SDE_ROT_WB_OFFSET+0x2B4)
+#define ROT_WB_STATUS (SDE_ROT_WB_OFFSET+0x2B8)
+#define ROT_WB_UBWC_ERROR_STATUS (SDE_ROT_WB_OFFSET+0x2BC)
+#define ROT_WB_OUT_IMG_SIZE (SDE_ROT_WB_OFFSET+0x2C0)
+#define ROT_WB_OUT_XY (SDE_ROT_WB_OFFSET+0x2C4)
+
+
+/* SDE_ROT_REGDMA_RAM:
+ * OFFSET=0x0A8E00
+ */
+#define SDE_ROT_REGDMA_RAM_OFFSET 0xA8E00
+#define REGDMA_RAM_REGDMA_CMD_RAM (SDE_ROT_REGDMA_RAM_OFFSET+0x00)
+
+
+/* SDE_ROT_REGDMA_CSR:
+ * OFFSET=0x0AAE00
+ */
+#define SDE_ROT_REGDMA_OFFSET 0xAAE00
+#define REGDMA_CSR_REGDMA_VERSION (SDE_ROT_REGDMA_OFFSET+0x00)
+#define REGDMA_CSR_REGDMA_OP_MODE (SDE_ROT_REGDMA_OFFSET+0x04)
+#define REGDMA_CSR_REGDMA_QUEUE_0_SUBMIT (SDE_ROT_REGDMA_OFFSET+0x10)
+#define REGDMA_CSR_REGDMA_QUEUE_0_STATUS (SDE_ROT_REGDMA_OFFSET+0x14)
+#define REGDMA_CSR_REGDMA_QUEUE_1_SUBMIT (SDE_ROT_REGDMA_OFFSET+0x18)
+#define REGDMA_CSR_REGDMA_QUEUE_1_STATUS (SDE_ROT_REGDMA_OFFSET+0x1C)
+#define REGDMA_CSR_REGDMA_BLOCK_LO_0 (SDE_ROT_REGDMA_OFFSET+0x20)
+#define REGDMA_CSR_REGDMA_BLOCK_HI_0 (SDE_ROT_REGDMA_OFFSET+0x24)
+#define REGDMA_CSR_REGDMA_BLOCK_LO_1 (SDE_ROT_REGDMA_OFFSET+0x28)
+#define REGDMA_CSR_REGDMA_BLOCK_HI_1 (SDE_ROT_REGDMA_OFFSET+0x2C)
+#define REGDMA_CSR_REGDMA_BLOCK_LO_2 (SDE_ROT_REGDMA_OFFSET+0x30)
+#define REGDMA_CSR_REGDMA_BLOCK_HI_2 (SDE_ROT_REGDMA_OFFSET+0x34)
+#define REGDMA_CSR_REGDMA_BLOCK_LO_3 (SDE_ROT_REGDMA_OFFSET+0x38)
+#define REGDMA_CSR_REGDMA_BLOCK_HI_3 (SDE_ROT_REGDMA_OFFSET+0x3C)
+#define REGDMA_CSR_REGDMA_WD_TIMER_CTL (SDE_ROT_REGDMA_OFFSET+0x40)
+#define REGDMA_CSR_REGDMA_WD_TIMER_CTL2 (SDE_ROT_REGDMA_OFFSET+0x44)
+#define REGDMA_CSR_REGDMA_WD_TIMER_LOAD_VALUE (SDE_ROT_REGDMA_OFFSET+0x48)
+#define REGDMA_CSR_REGDMA_WD_TIMER_STATUS_VALUE (SDE_ROT_REGDMA_OFFSET+0x4C)
+#define REGDMA_CSR_REGDMA_INT_STATUS (SDE_ROT_REGDMA_OFFSET+0x50)
+#define REGDMA_CSR_REGDMA_INT_EN (SDE_ROT_REGDMA_OFFSET+0x54)
+#define REGDMA_CSR_REGDMA_INT_CLEAR (SDE_ROT_REGDMA_OFFSET+0x58)
+#define REGDMA_CSR_REGDMA_BLOCK_STATUS (SDE_ROT_REGDMA_OFFSET+0x5C)
+#define REGDMA_CSR_REGDMA_INVALID_CMD_RAM_OFFSET (SDE_ROT_REGDMA_OFFSET+0x60)
+#define REGDMA_CSR_REGDMA_FSM_STATE (SDE_ROT_REGDMA_OFFSET+0x64)
+#define REGDMA_CSR_REGDMA_DEBUG_SEL (SDE_ROT_REGDMA_OFFSET+0x68)
+
+
+/* SDE_ROT_QDSS:
+ * OFFSET=0x0AAF00
+ */
+#define ROT_QDSS_CONFIG 0x00
+#define ROT_QDSS_ATB_DATA_ENABLE0 0x04
+#define ROT_QDSS_ATB_DATA_ENABLE1 0x08
+#define ROT_QDSS_ATB_DATA_ENABLE2 0x0C
+#define ROT_QDSS_ATB_DATA_ENABLE3 0x10
+#define ROT_QDSS_CLK_CTRL 0x14
+#define ROT_QDSS_CLK_STATUS 0x18
+#define ROT_QDSS_PULSE_TRIGGER 0x20
+
+/*
+ * SDE_ROT_VBIF_NRT:
+ */
+#define SDE_ROT_VBIF_NRT_OFFSET 0
+
+/* REGDMA OP Code */
+#define REGDMA_OP_NOP (0 << 28)
+#define REGDMA_OP_REGWRITE (1 << 28)
+#define REGDMA_OP_REGMODIFY (2 << 28)
+#define REGDMA_OP_BLKWRITE_SINGLE (3 << 28)
+#define REGDMA_OP_BLKWRITE_INC (4 << 28)
+#define REGDMA_OP_MASK 0xF0000000
+
+/* REGDMA ADDR offset Mask */
+#define REGDMA_ADDR_OFFSET_MASK 0xFFFFF
+
+/* General defines */
+#define ROT_DONE_MASK 0x1
+#define ROT_DONE_CLEAR 0x1
+#define ROT_BUSY_BIT BIT(0)
+#define ROT_ERROR_BIT BIT(8)
+#define ROT_STATUS_MASK (ROT_BUSY_BIT | ROT_ERROR_BIT)
+#define REGDMA_BUSY BIT(0)
+#define REGDMA_EN 0x1
+#define REGDMA_SECURE_EN BIT(8)
+#define REGDMA_HALT BIT(16)
+
+#define REGDMA_WATCHDOG_INT BIT(19)
+#define REGDMA_INVALID_DESCRIPTOR BIT(18)
+#define REGDMA_INCOMPLETE_CMD BIT(17)
+#define REGDMA_INVALID_CMD BIT(16)
+#define REGDMA_QUEUE1_INT2 BIT(10)
+#define REGDMA_QUEUE1_INT1 BIT(9)
+#define REGDMA_QUEUE1_INT0 BIT(8)
+#define REGDMA_QUEUE0_INT2 BIT(2)
+#define REGDMA_QUEUE0_INT1 BIT(1)
+#define REGDMA_QUEUE0_INT0 BIT(0)
+#define REGDMA_INT_MASK 0x000F0707
+#define REGDMA_INT_HIGH_MASK 0x00000007
+#define REGDMA_INT_LOW_MASK 0x00000700
+#define REGDMA_INT_ERR_MASK 0x000F0000
+#define REGDMA_TIMESTAMP_REG ROT_SSPP_TPG_PATTERN_GEN_INIT_VAL
+
+#endif /*_SDE_ROTATOR_R3_HWIO_H */
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
new file mode 100644
index 0000000..5502cc0
--- /dev/null
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
@@ -0,0 +1,382 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _SDE_ROTATOR_R3_INTERNAL_H
+#define _SDE_ROTATOR_R3_INTERNAL_H
+
+#include "sde_rotator_core.h"
+
+struct sde_hw_rotator;
+struct sde_hw_rotator_context;
+
+/**
+ * Flags
+ */
+#define SDE_ROT_FLAG_SECURE_OVERLAY_SESSION 0x1
+#define SDE_ROT_FLAG_FLIP_LR 0x2
+#define SDE_ROT_FLAG_FLIP_UD 0x4
+#define SDE_ROT_FLAG_SOURCE_ROTATED_90 0x8
+#define SDE_ROT_FLAG_ROT_90 0x10
+#define SDE_ROT_FLAG_DEINTERLACE 0x20
+#define SDE_ROT_FLAG_SECURE_CAMERA_SESSION 0x40
+
+/**
+ * General defines
+ */
+#define SDE_HW_ROT_REGDMA_RAM_SIZE 1024
+#define SDE_HW_ROT_REGDMA_TOTAL_CTX 8
+#define SDE_HW_ROT_REGDMA_SEG_MASK (SDE_HW_ROT_REGDMA_TOTAL_CTX - 1)
+#define SDE_HW_ROT_REGDMA_SEG_SIZE \
+ (SDE_HW_ROT_REGDMA_RAM_SIZE / SDE_HW_ROT_REGDMA_TOTAL_CTX)
+#define SDE_REGDMA_SWTS_MASK 0x00000FFF
+#define SDE_REGDMA_SWTS_SHIFT 12
+
+enum sde_rot_queue_prio {
+ ROT_QUEUE_HIGH_PRIORITY,
+ ROT_QUEUE_LOW_PRIORITY,
+ ROT_QUEUE_MAX
+};
+
+enum sde_rot_angle {
+ ROT_ANGLE_0,
+ ROT_ANGLE_90,
+ ROT_ANGEL_MAX
+};
+
+enum sde_rotator_regdma_mode {
+ ROT_REGDMA_OFF,
+ ROT_REGDMA_ON,
+ ROT_REGDMA_MAX
+};
+
+/**
+ * struct sde_hw_rot_sspp_cfg: Rotator SSPP Configration description
+ * @src: source surface information
+ * @src_rect: src ROI, caller takes into account the different operations
+ * such as decimation, flip etc to program this field
+ * @addr: source surface address
+ */
+struct sde_hw_rot_sspp_cfg {
+ struct sde_mdp_format_params *fmt;
+ struct sde_mdp_plane_sizes src_plane;
+ struct sde_rect *src_rect;
+ struct sde_mdp_data *data;
+ u32 img_width;
+ u32 img_height;
+ u32 fps;
+ u64 bw;
+};
+
+
+
+/**
+ * struct sde_hw_rot_wb_cfg: Rotator WB Configration description
+ * @dest: destination surface information
+ * @dest_rect: dest ROI, caller takes into account the different operations
+ * such as decimation, flip etc to program this field
+ * @addr: destination surface address
+ */
+struct sde_hw_rot_wb_cfg {
+ struct sde_mdp_format_params *fmt;
+ struct sde_mdp_plane_sizes dst_plane;
+ struct sde_rect *dst_rect;
+ struct sde_mdp_data *data;
+ u32 img_width;
+ u32 img_height;
+ u32 v_downscale_factor;
+ u32 h_downscale_factor;
+ u32 fps;
+ u64 bw;
+};
+
+
+
+/**
+ *
+ * struct sde_hw_rotator_ops: Interface to the Rotator Hw driver functions
+ *
+ * Pre-requsises:
+ * - Caller must call the init function to get the rotator context
+ * - These functions will be called after clocks are enabled
+ */
+struct sde_hw_rotator_ops {
+ /**
+ * setup_rotator_fetchengine():
+ * Setup Source format
+ * Setup Source dimension/cropping rectangle (ROI)
+ * Setup Source surface base address and stride
+ * Setup fetch engine op mode (linear/tiled/compression/...)
+ * @ctx: Rotator context created in sde_hw_rotator_config
+ * @queue_id: Select either low / high priority queue
+ * @cfg: Rotator Fetch engine configuration parameters
+ * @danger_lut: Danger LUT setting
+ * @safe_lut: Safe LUT setting
+ * @dnsc_factor_w: Downscale factor for width
+ * @dnsc_factor_h: Downscale factor for height
+ * @flags: Specific config flag, see SDE_ROT_FLAG_ for details
+ */
+ void (*setup_rotator_fetchengine)(
+ struct sde_hw_rotator_context *ctx,
+ enum sde_rot_queue_prio queue_id,
+ struct sde_hw_rot_sspp_cfg *cfg,
+ u32 danger_lut,
+ u32 safe_lut,
+ u32 dnsc_factor_w,
+ u32 dnsc_factor_h,
+ u32 flags);
+
+ /**
+ * setup_rotator_wbengine():
+ * Setup destination formats
+ * Setup destination dimension/cropping rectangle (ROI)
+ * Setup destination surface base address and strides
+ * Setup writeback engine op mode (linear/tiled/compression)
+ * @ctx: Rotator context created in sde_hw_rotator_config
+ * @queue_id: Select either low / high priority queue
+ * @cfg: Rotator WriteBack engine configuration parameters
+ * @flags: Specific config flag, see SDE_ROT_FLAG_ for details
+ */
+ void (*setup_rotator_wbengine)(
+ struct sde_hw_rotator_context *ctx,
+ enum sde_rot_queue_prio queue_id,
+ struct sde_hw_rot_wb_cfg *cfg,
+ u32 flags);
+
+ /**
+ * start_rotator():
+ * Kick start rotator operation based on cached setup parameters
+ * REGDMA commands will get generated at this points
+ * @ctx: Rotator context
+ * @queue_id: Select either low / high priority queue
+ * Returns: unique job timestamp per submit. Used for tracking
+ * rotator finished job.
+ */
+ u32 (*start_rotator)(
+ struct sde_hw_rotator_context *ctx,
+ enum sde_rot_queue_prio queue_id);
+
+ /**
+ * wait_rotator_done():
+ * Notify Rotator HAL layer previously submitted job finished.
+ * A job timestamp will return to caller.
+ * @ctx: Rotator context
+ * @flags: Reserved
+ * Returns: job timestamp for tracking purpose
+ *
+ */
+ u32 (*wait_rotator_done)(
+ struct sde_hw_rotator_context *ctx,
+ enum sde_rot_queue_prio queue_id,
+ u32 flags);
+};
+
+/**
+ * struct sde_dbg_buf : Debug buffer used by debugfs
+ * @vaddr: VA address mapped from dma buffer
+ * @dmabuf: DMA buffer
+ * @buflen: Length of DMA buffer
+ * @width: pixel width of buffer
+ * @height: pixel height of buffer
+ */
+struct sde_dbg_buf {
+ void *vaddr;
+ struct dma_buf *dmabuf;
+ unsigned long buflen;
+ u32 width;
+ u32 height;
+};
+
+/**
+ * struct sde_hw_rotator_context : Each rotator context ties to each priority
+ * queue. Max number of concurrent contexts in regdma is limited to regdma
+ * ram segment size allocation. Each rotator context can be any priority. A
+ * incrementatl timestamp is used to identify and assigne to each context.
+ */
+struct sde_hw_rotator_context {
+ struct sde_hw_rotator *rot;
+ struct sde_rot_hw_resource *hwres;
+ enum sde_rot_queue_prio q_id;
+ u32 session_id;
+ u32 *regdma_base;
+ u32 *regdma_wrptr;
+ u32 timestamp;
+ struct completion rot_comp;
+ wait_queue_head_t regdma_waitq;
+ struct sde_dbg_buf src_dbgbuf;
+ struct sde_dbg_buf dst_dbgbuf;
+ u32 last_regdma_isr_status;
+ u32 last_regdma_timestamp;
+ dma_addr_t ts_addr;
+ bool is_secure;
+ bool is_traffic_shaping;
+};
+
+/**
+ * struct sde_hw_rotator_resource_info : Each rotator resource ties to each
+ * priority queue
+ */
+struct sde_hw_rotator_resource_info {
+ struct sde_hw_rotator *rot;
+ struct sde_rot_hw_resource hw;
+};
+
+/**
+ * struct sde_hw_rotator : Rotator description
+ * @hw: mdp register mapped offset
+ * @ops: pointer to operations possible for the rotator HW
+ */
+struct sde_hw_rotator {
+ /* base */
+ char __iomem *mdss_base;
+
+ /* Platform device from upper manager */
+ struct platform_device *pdev;
+
+ /* Ops */
+ struct sde_hw_rotator_ops ops;
+
+ /* Cmd Queue */
+ u32 cmd_queue[SDE_HW_ROT_REGDMA_RAM_SIZE];
+
+ /* Cmd Queue Write Ptr */
+ u32 *cmd_wr_ptr[ROT_QUEUE_MAX][SDE_HW_ROT_REGDMA_TOTAL_CTX];
+
+ /* Rotator Context */
+ struct sde_hw_rotator_context
+ *rotCtx[ROT_QUEUE_MAX][SDE_HW_ROT_REGDMA_TOTAL_CTX];
+
+ /* Cmd timestamp sequence for different priority*/
+ atomic_t timestamp[ROT_QUEUE_MAX];
+
+ /* regdma mode */
+ enum sde_rotator_regdma_mode mode;
+
+ /* logical interrupt number */
+ int irq_num;
+ atomic_t irq_enabled;
+
+ /* internal ION memory for SW timestamp */
+ struct ion_client *iclient;
+ struct sde_mdp_img_data swts_buf;
+ void *swts_buffer;
+
+ u32 highest_bank;
+
+ spinlock_t rotctx_lock;
+ spinlock_t rotisr_lock;
+
+ bool dbgmem;
+ bool reset_hw_ts;
+ u32 last_hw_ts;
+};
+
+/**
+ * sde_hw_rotator_get_regdma_ctxidx(): regdma segment index is based on
+ * timestamp. For non-regdma, just return 0 (i.e. first index)
+ * @ctx: Rotator Context
+ * return: regdma segment index
+ */
+static inline u32 sde_hw_rotator_get_regdma_ctxidx(
+ struct sde_hw_rotator_context *ctx)
+{
+ if (ctx->rot->mode == ROT_REGDMA_OFF)
+ return 0;
+ else
+ return ctx->timestamp & SDE_HW_ROT_REGDMA_SEG_MASK;
+}
+
+/**
+ * sde_hw_rotator_get_regdma_segment_base: return the base pointe of current
+ * regdma command buffer
+ * @ctx: Rotator Context
+ * return: base segment address
+ */
+static inline u32 *sde_hw_rotator_get_regdma_segment_base(
+ struct sde_hw_rotator_context *ctx)
+{
+ SDEROT_DBG("regdma base @slot[%d]: %p\n",
+ sde_hw_rotator_get_regdma_ctxidx(ctx),
+ ctx->regdma_base);
+
+ return ctx->regdma_base;
+}
+
+/**
+ * sde_hw_rotator_get_regdma_segment(): return current regdma command buffer
+ * pointer for current regdma segment.
+ * @ctx: Rotator Context
+ * return: segment address
+ */
+static inline u32 *sde_hw_rotator_get_regdma_segment(
+ struct sde_hw_rotator_context *ctx)
+{
+ u32 idx = sde_hw_rotator_get_regdma_ctxidx(ctx);
+ u32 *addr = ctx->regdma_wrptr;
+
+ SDEROT_DBG("regdma slot[%d] ==> %p\n", idx, addr);
+ return addr;
+}
+
+/**
+ * sde_hw_rotator_put_regdma_segment(): update current regdma command buffer
+ * pointer for current regdma segment
+ * @ctx: Rotator Context
+ * @wrptr: current regdma segment location
+ */
+static inline void sde_hw_rotator_put_regdma_segment(
+ struct sde_hw_rotator_context *ctx,
+ u32 *wrptr)
+{
+ u32 idx = sde_hw_rotator_get_regdma_ctxidx(ctx);
+
+ ctx->regdma_wrptr = wrptr;
+ SDEROT_DBG("regdma slot[%d] <== %p\n", idx, wrptr);
+}
+
+/**
+ * sde_hw_rotator_put_ctx(): Storing rotator context according to its
+ * timestamp.
+ */
+static inline void sde_hw_rotator_put_ctx(struct sde_hw_rotator_context *ctx)
+{
+ struct sde_hw_rotator *rot = ctx->rot;
+ u32 idx = sde_hw_rotator_get_regdma_ctxidx(ctx);
+ unsigned long flags;
+
+ spin_lock_irqsave(&rot->rotisr_lock, flags);
+ rot->rotCtx[ctx->q_id][idx] = ctx;
+ spin_unlock_irqrestore(&rot->rotisr_lock, flags);
+
+ SDEROT_DBG("rotCtx[%d][%d] <== ctx:%p | session-id:%d\n",
+ ctx->q_id, idx, ctx, ctx->session_id);
+}
+
+/**
+ * sde_hw_rotator_clr_ctx(): Clearing rotator context according to its
+ * timestamp.
+ */
+static inline void sde_hw_rotator_clr_ctx(struct sde_hw_rotator_context *ctx)
+{
+ struct sde_hw_rotator *rot = ctx->rot;
+ u32 idx = sde_hw_rotator_get_regdma_ctxidx(ctx);
+ unsigned long flags;
+
+ spin_lock_irqsave(&rot->rotisr_lock, flags);
+ rot->rotCtx[ctx->q_id][idx] = NULL;
+ spin_unlock_irqrestore(&rot->rotisr_lock, flags);
+
+ SDEROT_DBG("rotCtx[%d][%d] <== null | session-id:%d\n",
+ ctx->q_id, idx, ctx->session_id);
+}
+
+#endif /*_SDE_ROTATOR_R3_INTERNAL_H */
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c
index 0fe7b37..918dfc6 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c
@@ -33,6 +33,7 @@
#include "sde_rotator_util.h"
#include "sde_rotator_io_util.h"
#include "sde_rotator_smmu.h"
+#include "sde_rotator_debug.h"
#define SMMU_SDE_ROT_SEC "qcom,smmu_sde_rot_sec"
#define SMMU_SDE_ROT_UNSEC "qcom,smmu_sde_rot_unsec"
@@ -50,6 +51,30 @@
return true;
}
+static inline bool sde_smmu_is_valid_domain_condition(
+ struct sde_rot_data_type *mdata,
+ int domain_type,
+ bool is_attach)
+{
+ if (is_attach) {
+ if (test_bit(SDE_CAPS_SEC_ATTACH_DETACH_SMMU,
+ mdata->sde_caps_map) &&
+ (mdata->sec_cam_en &&
+ domain_type == SDE_IOMMU_DOMAIN_ROT_SECURE))
+ return false;
+ else
+ return true;
+ } else {
+ if (test_bit(SDE_CAPS_SEC_ATTACH_DETACH_SMMU,
+ mdata->sde_caps_map) &&
+ (mdata->sec_cam_en &&
+ domain_type == SDE_IOMMU_DOMAIN_ROT_SECURE))
+ return true;
+ else
+ return false;
+ }
+}
+
struct sde_smmu_client *sde_smmu_get_cb(u32 domain)
{
struct sde_rot_data_type *mdata = sde_rot_get_mdata();
@@ -179,7 +204,7 @@
* And iommu attach is done only once during the initial attach and it is never
* detached as smmu v2 uses a feature called 'retention'.
*/
-static int sde_smmu_attach(struct sde_rot_data_type *mdata)
+int sde_smmu_attach(struct sde_rot_data_type *mdata)
{
struct sde_smmu_client *sde_smmu;
int i, rc = 0;
@@ -198,7 +223,10 @@
goto err;
}
- if (!sde_smmu->domain_attached) {
+ if (!sde_smmu->domain_attached &&
+ sde_smmu_is_valid_domain_condition(mdata,
+ i,
+ true)) {
rc = arm_iommu_attach_device(sde_smmu->dev,
sde_smmu->mmu_mapping);
if (rc) {
@@ -213,10 +241,9 @@
SDEROT_DBG("iommu v2 domain[%i] attached\n", i);
}
} else {
- SDEROT_ERR(
+ SDEROT_DBG(
"iommu device not attached for domain[%d]\n",
i);
- return -ENODEV;
}
}
return 0;
@@ -239,7 +266,7 @@
* Only disables the clks as it is not required to detach the iommu mapped
* VA range from the device in smmu as explained in the sde_smmu_attach
*/
-static int sde_smmu_detach(struct sde_rot_data_type *mdata)
+int sde_smmu_detach(struct sde_rot_data_type *mdata)
{
struct sde_smmu_client *sde_smmu;
int i;
@@ -249,8 +276,18 @@
continue;
sde_smmu = sde_smmu_get_cb(i);
- if (sde_smmu && sde_smmu->dev)
- sde_smmu_enable_power(sde_smmu, false);
+ if (sde_smmu && sde_smmu->dev) {
+ if (sde_smmu->domain_attached &&
+ sde_smmu_is_valid_domain_condition(mdata,
+ i, false)) {
+ arm_iommu_detach_device(sde_smmu->dev);
+ SDEROT_DBG("iommu domain[%i] detached\n", i);
+ sde_smmu->domain_attached = false;
+ }
+ else {
+ sde_smmu_enable_power(sde_smmu, false);
+ }
+ }
}
return 0;
}
@@ -332,6 +369,8 @@
int rc = 0;
mutex_lock(&sde_smmu_ref_cnt_lock);
+ SDEROT_EVTLOG(__builtin_return_address(0), enable, mdata->iommu_ref_cnt,
+ mdata->iommu_attached);
SDEROT_DBG("%pS: enable:%d ref_cnt:%d attach:%d\n",
__builtin_return_address(0), enable, mdata->iommu_ref_cnt,
mdata->iommu_attached);
@@ -364,6 +403,33 @@
return mdata->iommu_ref_cnt;
}
+int sde_smmu_secure_ctrl(int enable)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ int rc = 0;
+
+ mutex_lock(&sde_smmu_ref_cnt_lock);
+ /*
+ * Attach/detach secure context irrespective of ref count,
+ * We come here only when secure camera is disabled
+ */
+ if (enable) {
+ rc = sde_smmu_attach(mdata);
+ if (!rc)
+ mdata->iommu_attached = true;
+ } else {
+ rc = sde_smmu_detach(mdata);
+ /*
+ * keep iommu_attached equal to true,
+ * so that driver does not attemp to attach
+ * while in secure state
+ */
+ }
+
+ mutex_unlock(&sde_smmu_ref_cnt_lock);
+ return rc;
+}
+
/*
* sde_smmu_device_create()
* @dev: sde_mdp device
@@ -407,9 +473,10 @@
sde_smmu = (struct sde_smmu_client *)token;
- /* TODO: trigger rotator panic and dump */
- SDEROT_ERR("TODO: trigger rotator panic and dump, iova=0x%08lx\n",
- iova);
+ /* trigger rotator panic and dump */
+ SDEROT_ERR("trigger rotator panic and dump, iova=0x%08lx\n", iova);
+
+ sde_rot_dump_panic();
return rc;
}
@@ -444,7 +511,6 @@
struct sde_smmu_domain smmu_domain;
const struct of_device_id *match;
struct sde_module_power *mp;
- int disable_htw = 1;
char name[MAX_CLIENT_NAME_LEN];
if (!mdata) {
@@ -473,6 +539,7 @@
}
sde_smmu = &mdata->sde_smmu[smmu_domain.domain];
+ sde_smmu->domain = smmu_domain.domain;
mp = &sde_smmu->mp;
memset(mp, 0, sizeof(struct sde_module_power));
@@ -489,11 +556,13 @@
mp->num_vreg = 1;
}
- rc = sde_rot_config_vreg(&pdev->dev, mp->vreg_config,
- mp->num_vreg, true);
- if (rc) {
- SDEROT_ERR("vreg config failed rc=%d\n", rc);
- return rc;
+ if (mp->vreg_config) {
+ rc = sde_rot_config_vreg(&pdev->dev, mp->vreg_config,
+ mp->num_vreg, true);
+ if (rc) {
+ SDEROT_ERR("vreg config failed rc=%d\n", rc);
+ goto release_vreg;
+ }
}
rc = sde_smmu_clk_register(pdev, mp);
@@ -501,18 +570,16 @@
SDEROT_ERR(
"smmu clk register failed for domain[%d] with err:%d\n",
smmu_domain.domain, rc);
- sde_rot_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg,
- false);
- return rc;
+ goto disable_vreg;
}
snprintf(name, MAX_CLIENT_NAME_LEN, "smmu:%u", smmu_domain.domain);
sde_smmu->reg_bus_clt = sde_reg_bus_vote_client_create(name);
if (IS_ERR_OR_NULL(sde_smmu->reg_bus_clt)) {
SDEROT_ERR("mdss bus client register failed\n");
- sde_rot_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg,
- false);
- return PTR_ERR(sde_smmu->reg_bus_clt);
+ rc = PTR_ERR(sde_smmu->reg_bus_clt);
+ sde_smmu->reg_bus_clt = NULL;
+ goto unregister_clk;
}
rc = sde_smmu_enable_power(sde_smmu, true);
@@ -528,16 +595,10 @@
SDEROT_ERR("iommu create mapping failed for domain[%d]\n",
smmu_domain.domain);
rc = PTR_ERR(sde_smmu->mmu_mapping);
+ sde_smmu->mmu_mapping = NULL;
goto disable_power;
}
- rc = iommu_domain_set_attr(sde_smmu->mmu_mapping->domain,
- DOMAIN_ATTR_COHERENT_HTW_DISABLE, &disable_htw);
- if (rc) {
- SDEROT_ERR("couldn't disable coherent HTW\n");
- goto release_mapping;
- }
-
if (smmu_domain.domain == SDE_IOMMU_DOMAIN_ROT_SECURE) {
int secure_vmid = VMID_CP_PIXEL;
@@ -562,13 +623,20 @@
release_mapping:
arm_iommu_release_mapping(sde_smmu->mmu_mapping);
+ sde_smmu->mmu_mapping = NULL;
disable_power:
sde_smmu_enable_power(sde_smmu, false);
bus_client_destroy:
sde_reg_bus_vote_client_destroy(sde_smmu->reg_bus_clt);
sde_smmu->reg_bus_clt = NULL;
- sde_rot_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg,
- false);
+unregister_clk:
+disable_vreg:
+ sde_rot_config_vreg(&pdev->dev, sde_smmu->mp.vreg_config,
+ sde_smmu->mp.num_vreg, false);
+release_vreg:
+ devm_kfree(&pdev->dev, sde_smmu->mp.vreg_config);
+ sde_smmu->mp.vreg_config = NULL;
+ sde_smmu->mp.num_vreg = 0;
return rc;
}
@@ -579,9 +647,21 @@
for (i = 0; i < SDE_IOMMU_MAX_DOMAIN; i++) {
sde_smmu = sde_smmu_get_cb(i);
- if (sde_smmu && sde_smmu->dev &&
- (sde_smmu->dev == &pdev->dev))
- arm_iommu_release_mapping(sde_smmu->mmu_mapping);
+ if (!sde_smmu || !sde_smmu->dev ||
+ (sde_smmu->dev != &pdev->dev))
+ continue;
+
+ sde_smmu->dev = NULL;
+ arm_iommu_release_mapping(sde_smmu->mmu_mapping);
+ sde_smmu->mmu_mapping = NULL;
+ sde_smmu_enable_power(sde_smmu, false);
+ sde_reg_bus_vote_client_destroy(sde_smmu->reg_bus_clt);
+ sde_smmu->reg_bus_clt = NULL;
+ sde_rot_config_vreg(&pdev->dev, sde_smmu->mp.vreg_config,
+ sde_smmu->mp.num_vreg, false);
+ devm_kfree(&pdev->dev, sde_smmu->mp.vreg_config);
+ sde_smmu->mp.vreg_config = NULL;
+ sde_smmu->mp.num_vreg = 0;
}
return 0;
}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.h
index 1adcfb7..4a1c3d3 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.h
@@ -45,4 +45,6 @@
void sde_smmu_unmap_dma_buf(struct sg_table *table, int domain,
int dir, struct dma_buf *dma_buf);
+int sde_smmu_secure_ctrl(int enable);
+
#endif /* SDE_ROTATOR_SMMU_H */
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_sync.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_sync.c
index 2990522..dd8f69c 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_sync.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_sync.c
@@ -16,8 +16,8 @@
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/slab.h>
-#include <linux/sync.h>
-#include <linux/sw_sync.h>
+#include <sync.h>
+#include <sw_sync.h>
#include "sde_rotator_util.h"
#include "sde_rotator_sync.h"
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
index c881481..fb7bb37 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
@@ -192,7 +192,7 @@
4096);
/* CbCr bitstream stride and plane size */
- ps->ystride[1] = ALIGN(width, 64);
+ ps->ystride[1] = ALIGN(width, 128);
ps->plane_size[1] = ALIGN(ps->ystride[1] *
ALIGN(height / 2, 32), 4096);
@@ -355,13 +355,6 @@
chroma_samp = fmt->chroma_sample;
- if (rotation) {
- if (chroma_samp == SDE_MDP_CHROMA_H2V1)
- chroma_samp = SDE_MDP_CHROMA_H1V2;
- else if (chroma_samp == SDE_MDP_CHROMA_H1V2)
- chroma_samp = SDE_MDP_CHROMA_H2V1;
- }
-
sde_mdp_get_v_h_subsample_rate(chroma_samp,
&v_subsample, &h_subsample);
@@ -586,7 +579,7 @@
struct sde_mdp_plane_sizes *ps, struct sde_mdp_format_params *fmt)
{
u16 macro_w, micro_w, micro_h;
- u32 offset;
+ u32 offset = 0;
int ret;
ret = sde_rot_get_ubwc_micro_dim(fmt->format, µ_w, µ_h);
@@ -752,6 +745,13 @@
return 0;
}
+static int sde_mdp_is_map_needed(struct sde_mdp_img_data *data)
+{
+ if (data->flags & SDE_SECURE_CAMERA_SESSION)
+ return false;
+ return true;
+}
+
static int sde_mdp_get_img(struct sde_fb_data *img,
struct sde_mdp_img_data *data, struct device *dev,
bool rotator, int dir)
@@ -760,6 +760,8 @@
unsigned long *len;
u32 domain;
dma_addr_t *start;
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ struct ion_client *iclient = mdata->iclient;
start = &data->addr;
len = &data->len;
@@ -773,35 +775,79 @@
data->srcp_dma_buf = NULL;
return ret;
}
- domain = sde_smmu_get_domain_type(data->flags, rotator);
- SDEROT_DBG("%d domain=%d ihndl=%p\n",
- __LINE__, domain, data->srcp_dma_buf);
- data->srcp_attachment =
- sde_smmu_dma_buf_attach(data->srcp_dma_buf, dev,
- domain);
- if (IS_ERR(data->srcp_attachment)) {
- SDEROT_ERR("%d Failed to attach dma buf\n", __LINE__);
- ret = PTR_ERR(data->srcp_attachment);
- goto err_put;
+ if (sde_mdp_is_map_needed(data)) {
+ domain = sde_smmu_get_domain_type(data->flags, rotator);
+
+ SDEROT_DBG("%d domain=%d ihndl=%p\n",
+ __LINE__, domain, data->srcp_dma_buf);
+ data->srcp_attachment =
+ sde_smmu_dma_buf_attach(data->srcp_dma_buf, dev,
+ domain);
+ if (IS_ERR(data->srcp_attachment)) {
+ SDEROT_ERR("%d Failed to attach dma buf\n", __LINE__);
+ ret = PTR_ERR(data->srcp_attachment);
+ goto err_put;
+ }
+
+ SDEROT_DBG("%d attach=%p\n", __LINE__, data->srcp_attachment);
+ data->srcp_table =
+ dma_buf_map_attachment(data->srcp_attachment, dir);
+ if (IS_ERR(data->srcp_table)) {
+ SDEROT_ERR("%d Failed to map attachment\n", __LINE__);
+ ret = PTR_ERR(data->srcp_table);
+ goto err_detach;
+ }
+
+ SDEROT_DBG("%d table=%p\n", __LINE__, data->srcp_table);
+ data->addr = 0;
+ data->len = 0;
+ data->mapped = false;
+ data->skip_detach = false;
+ /* return early, mapping will be done later */
+ } else {
+ struct ion_handle *ihandle = NULL;
+ struct sg_table *sg_ptr = NULL;
+
+ do {
+ ihandle = img->handle;
+ if (IS_ERR_OR_NULL(ihandle)) {
+ ret = -EINVAL;
+ SDEROT_ERR("invalid ion handle\n");
+ break;
+ }
+
+ sg_ptr = ion_sg_table(iclient, ihandle);
+ if (sg_ptr == NULL) {
+ SDEROT_ERR("ion sg table get failed\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ if (sg_ptr->nents != 1) {
+ SDEROT_ERR("ion buffer mapping failed\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ if (((uint64_t)sg_dma_address(sg_ptr->sgl) >=
+ PHY_ADDR_4G - sg_ptr->sgl->length)) {
+ SDEROT_ERR("ion buffer mapped size invalid\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ data->addr = sg_dma_address(sg_ptr->sgl);
+ data->len = sg_ptr->sgl->length;
+ data->mapped = true;
+ ret = 0;
+ } while (0);
+
+ if (!IS_ERR_OR_NULL(ihandle))
+ ion_free(iclient, ihandle);
+ return ret;
}
- SDEROT_DBG("%d attach=%p\n", __LINE__, data->srcp_attachment);
- data->srcp_table =
- dma_buf_map_attachment(data->srcp_attachment, dir);
- if (IS_ERR(data->srcp_table)) {
- SDEROT_ERR("%d Failed to map attachment\n", __LINE__);
- ret = PTR_ERR(data->srcp_table);
- goto err_detach;
- }
-
- SDEROT_DBG("%d table=%p\n", __LINE__, data->srcp_table);
- data->addr = 0;
- data->len = 0;
- data->mapped = false;
- data->skip_detach = false;
- /* return early, mapping will be done later */
-
return 0;
err_detach:
dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment);
@@ -818,23 +864,39 @@
{
int ret = -EINVAL;
int domain;
+ struct scatterlist *sg;
+ unsigned int i;
+ struct sg_table *table;
if (data->addr && data->len)
return 0;
if (!IS_ERR_OR_NULL(data->srcp_dma_buf)) {
- domain = sde_smmu_get_domain_type(data->flags,
- rotator);
- ret = sde_smmu_map_dma_buf(data->srcp_dma_buf,
- data->srcp_table, domain,
- &data->addr, &data->len, dir);
- if (IS_ERR_VALUE(ret)) {
- SDEROT_ERR("smmu map dma buf failed: (%d)\n", ret);
- goto err_unmap;
+ if (sde_mdp_is_map_needed(data)) {
+ domain = sde_smmu_get_domain_type(data->flags,
+ rotator);
+ ret = sde_smmu_map_dma_buf(data->srcp_dma_buf,
+ data->srcp_table, domain,
+ &data->addr, &data->len, dir);
+ if (IS_ERR_VALUE(ret)) {
+ SDEROT_ERR("smmu map buf failed:(%d)\n", ret);
+ goto err_unmap;
+ }
+ SDEROT_DBG("map %pad/%lx d:%u f:%x\n",
+ &data->addr,
+ data->len,
+ domain,
+ data->flags);
+ data->mapped = true;
+ } else {
+ data->addr = sg_phys(data->srcp_table->sgl);
+ data->len = 0;
+ table = data->srcp_table;
+ for_each_sg(table->sgl, sg, table->nents, i) {
+ data->len += sg->length;
+ }
+ ret = 0;
}
- SDEROT_DBG("map %pad/%lx d:%u f:%x\n", &data->addr, data->len,
- domain, data->flags);
- data->mapped = true;
}
if (!data->addr) {
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.h
index 93074d6..3f94a15 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.h
@@ -19,6 +19,7 @@
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/dma-buf.h>
+#include <linux/msm_ion.h>
#include "sde_rotator_hwio.h"
#include "sde_rotator_base.h"
@@ -44,6 +45,8 @@
#define SDEDEV_ERR(dev, fmt, ...) \
dev_err(dev, "<SDEROT_ERR> " fmt, ##__VA_ARGS__)
+#define PHY_ADDR_4G (1ULL<<32)
+
struct sde_rect {
u16 x;
u16 y;
@@ -62,12 +65,14 @@
#define SDE_SOURCE_ROTATED_90 0x00100000
#define SDE_SECURE_OVERLAY_SESSION 0x00008000
#define SDE_ROT_EXT_DMA_BUF 0x00010000
+#define SDE_SECURE_CAMERA_SESSION 0x00020000
struct sde_rot_data_type;
struct sde_fb_data {
uint32_t offset;
struct dma_buf *buffer;
+ struct ion_handle *handle;
int memory_id;
int id;
uint32_t flags;
@@ -79,6 +84,7 @@
/* DMA buffer file descriptor information. */
int fd;
struct dma_buf *buffer;
+ struct ion_handle *handle;
/* Pixel offset in the dma buffer. */
uint32_t offset;
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index f75a33fa..fcf173a 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -177,30 +177,12 @@
case HFI_VIDEO_CODEC_H264:
hal_codec = HAL_VIDEO_CODEC_H264;
break;
- case HFI_VIDEO_CODEC_H263:
- hal_codec = HAL_VIDEO_CODEC_H263;
- break;
case HFI_VIDEO_CODEC_MPEG1:
hal_codec = HAL_VIDEO_CODEC_MPEG1;
break;
case HFI_VIDEO_CODEC_MPEG2:
hal_codec = HAL_VIDEO_CODEC_MPEG2;
break;
- case HFI_VIDEO_CODEC_MPEG4:
- hal_codec = HAL_VIDEO_CODEC_MPEG4;
- break;
- case HFI_VIDEO_CODEC_DIVX_311:
- hal_codec = HAL_VIDEO_CODEC_DIVX_311;
- break;
- case HFI_VIDEO_CODEC_DIVX:
- hal_codec = HAL_VIDEO_CODEC_DIVX;
- break;
- case HFI_VIDEO_CODEC_VC1:
- hal_codec = HAL_VIDEO_CODEC_VC1;
- break;
- case HFI_VIDEO_CODEC_SPARK:
- hal_codec = HAL_VIDEO_CODEC_SPARK;
- break;
case HFI_VIDEO_CODEC_VP8:
hal_codec = HAL_VIDEO_CODEC_VP8;
break;
@@ -255,30 +237,12 @@
case HAL_VIDEO_CODEC_H264:
hfi_codec = HFI_VIDEO_CODEC_H264;
break;
- case HAL_VIDEO_CODEC_H263:
- hfi_codec = HFI_VIDEO_CODEC_H263;
- break;
case HAL_VIDEO_CODEC_MPEG1:
hfi_codec = HFI_VIDEO_CODEC_MPEG1;
break;
case HAL_VIDEO_CODEC_MPEG2:
hfi_codec = HFI_VIDEO_CODEC_MPEG2;
break;
- case HAL_VIDEO_CODEC_MPEG4:
- hfi_codec = HFI_VIDEO_CODEC_MPEG4;
- break;
- case HAL_VIDEO_CODEC_DIVX_311:
- hfi_codec = HFI_VIDEO_CODEC_DIVX_311;
- break;
- case HAL_VIDEO_CODEC_DIVX:
- hfi_codec = HFI_VIDEO_CODEC_DIVX;
- break;
- case HAL_VIDEO_CODEC_VC1:
- hfi_codec = HFI_VIDEO_CODEC_VC1;
- break;
- case HAL_VIDEO_CODEC_SPARK:
- hfi_codec = HFI_VIDEO_CODEC_SPARK;
- break;
case HAL_VIDEO_CODEC_VP8:
hfi_codec = HFI_VIDEO_CODEC_VP8;
break;
@@ -506,35 +470,6 @@
if (!pkt)
return -EINVAL;
- /*
- * Legacy packetization should skip sending any 3xx specific session
- * cmds. Add 3xx specific packetization to the switch case below.
- */
- switch (pkt_type) {
- case HFI_CMD_SESSION_CONTINUE:
- dprintk(VIDC_INFO,
- "%s - skip sending %x for legacy hfi\n",
- __func__, pkt_type);
- return -EPERM;
- default:
- break;
- }
-
- pkt->size = sizeof(struct vidc_hal_session_cmd_pkt);
- pkt->packet_type = pkt_type;
- pkt->session_id = hash32_ptr(session);
-
- return rc;
-}
-
-int create_3x_pkt_cmd_session_cmd(struct vidc_hal_session_cmd_pkt *pkt,
- int pkt_type, struct hal_session *session)
-{
- int rc = 0;
-
- if (!pkt)
- return -EINVAL;
-
pkt->size = sizeof(struct vidc_hal_session_cmd_pkt);
pkt->packet_type = pkt_type;
pkt->session_id = hash32_ptr(session);
@@ -727,29 +662,6 @@
return ret;
}
-static u32 get_hfi_buf_mode(enum buffer_mode_type hal_buf_mode)
-{
- u32 buf_mode;
-
- switch (hal_buf_mode) {
- case HAL_BUFFER_MODE_STATIC:
- buf_mode = HFI_BUFFER_MODE_STATIC;
- break;
- case HAL_BUFFER_MODE_RING:
- buf_mode = HFI_BUFFER_MODE_RING;
- break;
- case HAL_BUFFER_MODE_DYNAMIC:
- buf_mode = HFI_BUFFER_MODE_DYNAMIC;
- break;
- default:
- dprintk(VIDC_ERR, "Invalid buffer mode: %#x\n",
- hal_buf_mode);
- buf_mode = 0;
- break;
- }
- return buf_mode;
-}
-
static u32 get_hfi_ltr_mode(enum ltr_mode ltr_mode_type)
{
u32 ltrmode;
@@ -977,44 +889,6 @@
return rc;
}
-int create_pkt_cmd_session_parse_seq_header(
- struct hfi_cmd_session_parse_sequence_header_packet *pkt,
- struct hal_session *session, struct vidc_seq_hdr *seq_hdr)
-{
- int rc = 0;
-
- if (!pkt || !session || !seq_hdr)
- return -EINVAL;
-
- pkt->size = sizeof(struct hfi_cmd_session_parse_sequence_header_packet);
- pkt->packet_type = HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER;
- pkt->session_id = hash32_ptr(session);
- pkt->header_len = seq_hdr->seq_hdr_len;
- if (!seq_hdr->seq_hdr)
- return -EINVAL;
- pkt->packet_buffer = (u32)seq_hdr->seq_hdr;
- return rc;
-}
-
-int create_pkt_cmd_session_get_seq_hdr(
- struct hfi_cmd_session_get_sequence_header_packet *pkt,
- struct hal_session *session, struct vidc_seq_hdr *seq_hdr)
-{
- int rc = 0;
-
- if (!pkt || !session || !seq_hdr)
- return -EINVAL;
-
- pkt->size = sizeof(struct hfi_cmd_session_get_sequence_header_packet);
- pkt->packet_type = HFI_CMD_SESSION_GET_SEQUENCE_HEADER;
- pkt->session_id = hash32_ptr(session);
- pkt->buffer_len = seq_hdr->seq_hdr_len;
- if (!seq_hdr->seq_hdr)
- return -EINVAL;
- pkt->packet_buffer = (u32)seq_hdr->seq_hdr;
- return rc;
-}
-
int create_pkt_cmd_session_get_buf_req(
struct hfi_cmd_session_get_property_packet *pkt,
struct hal_session *session)
@@ -1076,6 +950,9 @@
pkt->session_id = hash32_ptr(session);
pkt->num_properties = 1;
switch (ptype) {
+ case HAL_CONFIG_VDEC_ENTROPY:
+ pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_VDEC_ENTROPY;
+ break;
case HAL_PARAM_PROFILE_LEVEL_CURRENT:
pkt->rg_property_data[0] =
HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT;
@@ -1089,31 +966,6 @@
return rc;
}
-int create_3x_pkt_cmd_session_get_property(
- struct hfi_cmd_session_get_property_packet *pkt,
- struct hal_session *session, enum hal_property ptype)
-{
- int rc = 0;
-
- if (!pkt || !session) {
- dprintk(VIDC_ERR, "%s Invalid parameters\n", __func__);
- return -EINVAL;
- }
- pkt->size = sizeof(struct hfi_cmd_session_get_property_packet);
- pkt->packet_type = HFI_CMD_SESSION_GET_PROPERTY;
- pkt->session_id = hash32_ptr(session);
- pkt->num_properties = 1;
- switch (ptype) {
- case HAL_CONFIG_VDEC_ENTROPY:
- pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_VDEC_ENTROPY;
- break;
- default:
- rc = create_pkt_cmd_session_get_property(pkt,
- session, ptype);
- }
- return rc;
-}
-
int create_pkt_cmd_session_set_property(
struct hfi_cmd_session_set_property_packet *pkt,
struct hal_session *session,
@@ -1314,48 +1166,9 @@
else
return -EINVAL;
hfi->enable = prop->enable;
- hfi->width = prop->width;
- hfi->height = prop->height;
pkt->size += sizeof(u32) + sizeof(struct hfi_multi_stream);
break;
}
- case HAL_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT:
- {
- struct hfi_display_picture_buffer_count *hfi;
- struct hal_display_picture_buffer_count *prop =
- (struct hal_display_picture_buffer_count *) pdata;
- pkt->rg_property_data[0] =
- HFI_PROPERTY_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT;
- hfi = (struct hfi_display_picture_buffer_count *)
- &pkt->rg_property_data[1];
- hfi->count = prop->count;
- hfi->enable = prop->enable;
- pkt->size += sizeof(u32) +
- sizeof(struct hfi_display_picture_buffer_count);
- break;
- }
- case HAL_PARAM_DIVX_FORMAT:
- {
- int *data = pdata;
-
- pkt->rg_property_data[0] = HFI_PROPERTY_PARAM_DIVX_FORMAT;
- switch (*data) {
- case HAL_DIVX_FORMAT_4:
- pkt->rg_property_data[1] = HFI_DIVX_FORMAT_4;
- break;
- case HAL_DIVX_FORMAT_5:
- pkt->rg_property_data[1] = HFI_DIVX_FORMAT_5;
- break;
- case HAL_DIVX_FORMAT_6:
- pkt->rg_property_data[1] = HFI_DIVX_FORMAT_6;
- break;
- default:
- dprintk(VIDC_ERR, "Invalid divx format: %#x\n", *data);
- break;
- }
- pkt->size += sizeof(u32) * 2;
- break;
- }
case HAL_CONFIG_VDEC_MB_ERROR_MAP_REPORTING:
{
create_pkt_enable(pkt->rg_property_data,
@@ -1364,14 +1177,6 @@
pkt->size += sizeof(u32) * 2;
break;
}
- case HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER:
- {
- create_pkt_enable(pkt->rg_property_data,
- HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER,
- ((struct hal_enable *)pdata)->enable);
- pkt->size += sizeof(u32) * 2;
- break;
- }
case HAL_PARAM_VDEC_SYNC_FRAME_DECODE:
{
create_pkt_enable(pkt->rg_property_data,
@@ -1409,19 +1214,6 @@
pkt->size += sizeof(u32) + sizeof(struct hfi_bitrate);
break;
}
- case HAL_CONFIG_VENC_MAX_BITRATE:
- {
- struct hfi_bitrate *hfi;
-
- pkt->rg_property_data[0] =
- HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE;
- hfi = (struct hfi_bitrate *) &pkt->rg_property_data[1];
- hfi->bit_rate = ((struct hal_bitrate *)pdata)->bit_rate;
- hfi->layer_id = ((struct hal_bitrate *)pdata)->layer_id;
-
- pkt->size += sizeof(u32) + sizeof(struct hfi_bitrate);
- break;
- }
case HAL_PARAM_PROFILE_LEVEL_CURRENT:
{
struct hfi_profile_level *hfi;
@@ -1510,32 +1302,6 @@
pkt->size += sizeof(u32) * 2;
break;
}
- case HAL_PARAM_VENC_MPEG4_TIME_RESOLUTION:
- {
- struct hfi_mpeg4_time_resolution *hfi;
-
- pkt->rg_property_data[0] =
- HFI_PROPERTY_PARAM_VENC_MPEG4_TIME_RESOLUTION;
- hfi = (struct hfi_mpeg4_time_resolution *)
- &pkt->rg_property_data[1];
- hfi->time_increment_resolution =
- ((struct hal_mpeg4_time_resolution *)pdata)->
- time_increment_resolution;
- pkt->size += sizeof(u32) * 2;
- break;
- }
- case HAL_PARAM_VENC_MPEG4_HEADER_EXTENSION:
- {
- struct hfi_mpeg4_header_extension *hfi;
-
- pkt->rg_property_data[0] =
- HFI_PROPERTY_PARAM_VENC_MPEG4_HEADER_EXTENSION;
- hfi = (struct hfi_mpeg4_header_extension *)
- &pkt->rg_property_data[1];
- hfi->header_extension = (u32)(unsigned long) pdata;
- pkt->size += sizeof(u32) * 2;
- break;
- }
case HAL_PARAM_VENC_H264_DEBLOCK_CONTROL:
{
struct hfi_h264_db_control *hfi;
@@ -1565,26 +1331,11 @@
sizeof(struct hfi_h264_db_control);
break;
}
- case HAL_PARAM_VENC_SESSION_QP:
- {
- struct hfi_quantization *hfi;
- struct hal_quantization *hal_quant =
- (struct hal_quantization *) pdata;
- pkt->rg_property_data[0] =
- HFI_PROPERTY_PARAM_VENC_SESSION_QP;
- hfi = (struct hfi_quantization *) &pkt->rg_property_data[1];
- hfi->qp_i = hal_quant->qpi;
- hfi->qp_p = hal_quant->qpp;
- hfi->qp_b = hal_quant->qpb;
- hfi->layer_id = hal_quant->layer_id;
- pkt->size += sizeof(u32) + sizeof(struct hfi_quantization);
- break;
- }
case HAL_PARAM_VENC_SESSION_QP_RANGE:
{
struct hfi_quantization_range *hfi;
- struct hfi_quantization_range *hal_range =
- (struct hfi_quantization_range *) pdata;
+ struct hal_quantization_range *hal_range =
+ (struct hal_quantization_range *) pdata;
u32 min_qp, max_qp;
pkt->rg_property_data[0] =
@@ -1610,9 +1361,10 @@
* 0xiippbb, where ii = qp range for I-frames,
* pp = qp range for P-frames, etc.
*/
- hfi->min_qp = min_qp | min_qp << 8 | min_qp << 16;
- hfi->max_qp = max_qp | max_qp << 8 | max_qp << 16;
- hfi->layer_id = hal_range->layer_id;
+ hfi->min_qp.qp_packed = min_qp | min_qp << 8 | min_qp << 16;
+ hfi->min_qp.layer_id = hal_range->layer_id;
+ hfi->max_qp.qp_packed = max_qp | max_qp << 8 | max_qp << 16;
+ hfi->max_qp.layer_id = hal_range->layer_id;
pkt->size += sizeof(u32) +
sizeof(struct hfi_quantization_range);
@@ -1621,47 +1373,23 @@
case HAL_PARAM_VENC_SESSION_QP_RANGE_PACKED:
{
struct hfi_quantization_range *hfi;
- struct hfi_quantization_range *hal_range =
- (struct hfi_quantization_range *) pdata;
+ struct hal_quantization_range *hal_range =
+ (struct hal_quantization_range *) pdata;
pkt->rg_property_data[0] =
HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE;
hfi = (struct hfi_quantization_range *)
&pkt->rg_property_data[1];
- hfi->min_qp = hal_range->min_qp;
- hfi->max_qp = hal_range->max_qp;
- hfi->layer_id = hal_range->layer_id;
+ hfi->min_qp.qp_packed = hal_range->min_qp;
+ hfi->min_qp.layer_id = hal_range->layer_id;
+ hfi->max_qp.qp_packed = hal_range->max_qp;
+ hfi->max_qp.layer_id = hal_range->layer_id;
pkt->size += sizeof(u32) +
sizeof(struct hfi_quantization_range);
break;
}
- case HAL_PARAM_VENC_SEARCH_RANGE:
- {
- struct hfi_vc1e_perf_cfg_type *hfi;
- struct hal_vc1e_perf_cfg_type *hal_mv_searchrange =
- (struct hal_vc1e_perf_cfg_type *) pdata;
- pkt->rg_property_data[0] =
- HFI_PROPERTY_PARAM_VENC_VC1_PERF_CFG;
- hfi = (struct hfi_vc1e_perf_cfg_type *)
- &pkt->rg_property_data[1];
- hfi->search_range_x_subsampled[0] =
- hal_mv_searchrange->i_frame.x_subsampled;
- hfi->search_range_x_subsampled[1] =
- hal_mv_searchrange->p_frame.x_subsampled;
- hfi->search_range_x_subsampled[2] =
- hal_mv_searchrange->b_frame.x_subsampled;
- hfi->search_range_y_subsampled[0] =
- hal_mv_searchrange->i_frame.y_subsampled;
- hfi->search_range_y_subsampled[1] =
- hal_mv_searchrange->p_frame.y_subsampled;
- hfi->search_range_y_subsampled[2] =
- hal_mv_searchrange->b_frame.y_subsampled;
- pkt->size += sizeof(u32) +
- sizeof(struct hfi_vc1e_perf_cfg_type);
- break;
- }
case HAL_PARAM_VENC_MAX_NUM_B_FRAMES:
{
struct hfi_max_num_b_frames *hfi;
@@ -1763,21 +1491,26 @@
pkt->rg_property_data[0] =
HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH;
hfi = (struct hfi_intra_refresh *) &pkt->rg_property_data[1];
+ hfi->mbs = 0;
switch (prop->mode) {
case HAL_INTRA_REFRESH_NONE:
hfi->mode = HFI_INTRA_REFRESH_NONE;
break;
case HAL_INTRA_REFRESH_ADAPTIVE:
hfi->mode = HFI_INTRA_REFRESH_ADAPTIVE;
+ hfi->mbs = prop->air_mbs;
break;
case HAL_INTRA_REFRESH_CYCLIC:
hfi->mode = HFI_INTRA_REFRESH_CYCLIC;
+ hfi->mbs = prop->cir_mbs;
break;
case HAL_INTRA_REFRESH_CYCLIC_ADAPTIVE:
hfi->mode = HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE;
+ hfi->mbs = prop->air_mbs;
break;
case HAL_INTRA_REFRESH_RANDOM:
hfi->mode = HFI_INTRA_REFRESH_RANDOM;
+ hfi->mbs = prop->air_mbs;
break;
default:
dprintk(VIDC_ERR,
@@ -1785,9 +1518,6 @@
prop->mode);
break;
}
- hfi->air_mbs = prop->air_mbs;
- hfi->air_ref = prop->air_ref;
- hfi->cir_mbs = prop->cir_mbs;
pkt->size += sizeof(u32) + sizeof(struct hfi_intra_refresh);
break;
}
@@ -1804,9 +1534,6 @@
case HAL_MULTI_SLICE_OFF:
hfi->multi_slice = HFI_MULTI_SLICE_OFF;
break;
- case HAL_MULTI_SLICE_GOB:
- hfi->multi_slice = HFI_MULTI_SLICE_GOB;
- break;
case HAL_MULTI_SLICE_BY_MB_COUNT:
hfi->multi_slice = HFI_MULTI_SLICE_BY_MB_COUNT;
break;
@@ -1889,38 +1616,6 @@
pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
break;
}
- case HAL_PARAM_BUFFER_ALLOC_MODE:
- {
- u32 buffer_type;
- u32 buffer_mode;
- struct hfi_buffer_alloc_mode *hfi;
- struct hal_buffer_alloc_mode *alloc_info = pdata;
-
- pkt->rg_property_data[0] =
- HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE;
- hfi = (struct hfi_buffer_alloc_mode *)
- &pkt->rg_property_data[1];
- buffer_type = get_hfi_buffer(alloc_info->buffer_type);
- if (buffer_type)
- hfi->buffer_type = buffer_type;
- else
- return -EINVAL;
- buffer_mode = get_hfi_buf_mode(alloc_info->buffer_mode);
- if (buffer_mode)
- hfi->buffer_mode = buffer_mode;
- else
- return -EINVAL;
- pkt->size += sizeof(u32) + sizeof(struct hfi_buffer_alloc_mode);
- break;
- }
- case HAL_PARAM_VDEC_FRAME_ASSEMBLY:
- {
- create_pkt_enable(pkt->rg_property_data,
- HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY,
- ((struct hal_enable *)pdata)->enable);
- pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
- break;
- }
case HAL_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC:
{
create_pkt_enable(pkt->rg_property_data,
@@ -1937,18 +1632,6 @@
pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
break;
}
- case HAL_PARAM_VDEC_SCS_THRESHOLD:
- {
- struct hfi_scs_threshold *hfi;
-
- pkt->rg_property_data[0] =
- HFI_PROPERTY_PARAM_VDEC_SCS_THRESHOLD;
- hfi = (struct hfi_scs_threshold *) &pkt->rg_property_data[1];
- hfi->threshold_value =
- ((struct hal_scs_threshold *) pdata)->threshold_value;
- pkt->size += sizeof(u32) + sizeof(struct hfi_scs_threshold);
- break;
- }
case HAL_PARAM_MVC_BUFFER_LAYOUT:
{
struct hfi_mvc_buffer_layout_descp_type *hfi;
@@ -2028,23 +1711,6 @@
pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
break;
}
- case HAL_PARAM_VENC_ENABLE_INITIAL_QP:
- {
- struct hfi_initial_quantization *hfi;
- struct hal_initial_quantization *quant = pdata;
-
- pkt->rg_property_data[0] =
- HFI_PROPERTY_PARAM_VENC_INITIAL_QP;
- hfi = (struct hfi_initial_quantization *)
- &pkt->rg_property_data[1];
- hfi->init_qp_enable = quant->init_qp_enable;
- hfi->qp_i = quant->qpi;
- hfi->qp_p = quant->qpp;
- hfi->qp_b = quant->qpb;
- pkt->size += sizeof(u32) +
- sizeof(struct hfi_initial_quantization);
- break;
- }
case HAL_PARAM_VPE_COLOR_SPACE_CONVERSION:
{
struct hfi_vpe_color_space_conversion *hfi = NULL;
@@ -2107,14 +1773,6 @@
pkt->size += sizeof(u32) * 2;
break;
}
- case HAL_PARAM_VDEC_NON_SECURE_OUTPUT2:
- {
- create_pkt_enable(pkt->rg_property_data,
- HFI_PROPERTY_PARAM_VDEC_NONCP_OUTPUT2,
- ((struct hal_enable *)pdata)->enable);
- pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
- break;
- }
case HAL_PARAM_VENC_HIER_P_HYBRID_MODE:
{
pkt->rg_property_data[0] =
@@ -2135,14 +1793,6 @@
pkt->size += sizeof(u32) * 2;
break;
}
- case HAL_CONFIG_VENC_FRAME_QP:
- {
- pkt->rg_property_data[0] =
- HFI_PROPERTY_CONFIG_VENC_FRAME_QP;
- pkt->rg_property_data[1] = *(u32 *)pdata;
- pkt->size += sizeof(u32) * 2;
- break;
- }
case HAL_CONFIG_VENC_BASELAYER_PRIORITYID:
{
pkt->rg_property_data[0] =
@@ -2174,14 +1824,6 @@
pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
break;
}
- case HAL_PARAM_VENC_CONSTRAINED_INTRA_PRED:
- {
- create_pkt_enable(pkt->rg_property_data,
- HFI_PROPERTY_PARAM_VENC_CONSTRAINED_INTRA_PRED,
- ((struct hal_enable *)pdata)->enable);
- pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
- break;
- }
case HAL_PARAM_VENC_H264_TRANSFORM_8x8:
{
create_pkt_enable(pkt->rg_property_data,
@@ -2237,6 +1879,78 @@
pkt->size += sizeof(u32) + sizeof(struct hfi_iframe_size);
break;
}
+ case HAL_PARAM_BUFFER_SIZE_MINIMUM:
+ {
+ struct hfi_buffer_size_minimum *hfi;
+ struct hal_buffer_size_minimum *prop =
+ (struct hal_buffer_size_minimum *) pdata;
+ u32 buffer_type;
+
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_PARAM_BUFFER_SIZE_MINIMUM;
+
+ hfi = (struct hfi_buffer_size_minimum *)
+ &pkt->rg_property_data[1];
+ hfi->buffer_size = prop->buffer_size;
+
+ buffer_type = get_hfi_buffer(prop->buffer_type);
+ if (buffer_type)
+ hfi->buffer_type = buffer_type;
+ else
+ return -EINVAL;
+
+ pkt->size += sizeof(u32) + sizeof(struct
+ hfi_buffer_size_minimum);
+ break;
+ }
+ case HAL_PARAM_SYNC_BASED_INTERRUPT:
+ {
+ create_pkt_enable(pkt->rg_property_data,
+ HFI_PROPERTY_PARAM_SYNC_BASED_INTERRUPT,
+ ((struct hal_enable *)pdata)->enable);
+ pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+ break;
+ }
+ case HAL_PARAM_VENC_VQZIP_SEI:
+ {
+ create_pkt_enable(pkt->rg_property_data,
+ HFI_PROPERTY_PARAM_VENC_VQZIP_SEI_TYPE,
+ ((struct hal_enable *)pdata)->enable);
+ pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+ break;
+ }
+
+ case HAL_PARAM_VENC_LOW_LATENCY:
+ {
+ struct hfi_enable *hfi;
+
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_PARAM_VENC_LOW_LATENCY_MODE;
+ hfi = (struct hfi_enable *) &pkt->rg_property_data[1];
+ hfi->enable = ((struct hal_enable *) pdata)->enable;
+ pkt->size += sizeof(u32) * 2;
+ break;
+ }
+ case HAL_CONFIG_VENC_BLUR_RESOLUTION:
+ {
+ struct hfi_frame_size *hfi;
+ struct hal_frame_size *prop = (struct hal_frame_size *) pdata;
+ u32 buffer_type;
+
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_CONFIG_VENC_BLUR_FRAME_SIZE;
+ hfi = (struct hfi_frame_size *) &pkt->rg_property_data[1];
+ buffer_type = get_hfi_buffer(prop->buffer_type);
+ if (buffer_type)
+ hfi->buffer_type = buffer_type;
+ else
+ return -EINVAL;
+
+ hfi->height = prop->height;
+ hfi->width = prop->width;
+ pkt->size += sizeof(u32) + sizeof(struct hfi_frame_size);
+ break;
+ }
/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
case HAL_CONFIG_BUFFER_REQUIREMENTS:
case HAL_CONFIG_PRIORITY:
@@ -2262,7 +1976,6 @@
case HAL_CONFIG_VDEC_MULTI_STREAM:
case HAL_PARAM_VENC_MULTI_SLICE_INFO:
case HAL_CONFIG_VENC_TIMESTAMP_SCALE:
- case HAL_PARAM_BUFFER_SIZE_MINIMUM:
default:
dprintk(VIDC_ERR, "DEFAULT: Calling %#x\n", ptype);
rc = -ENOTSUPP;
@@ -2319,176 +2032,6 @@
return 0;
}
-static int create_3x_pkt_cmd_session_set_property(
- struct hfi_cmd_session_set_property_packet *pkt,
- struct hal_session *session,
- enum hal_property ptype, void *pdata)
-{
- int rc = 0;
-
- if (!pkt || !session || !pdata)
- return -EINVAL;
-
- pkt->size = sizeof(struct hfi_cmd_session_set_property_packet);
- pkt->packet_type = HFI_CMD_SESSION_SET_PROPERTY;
- pkt->session_id = hash32_ptr(session);
- pkt->num_properties = 1;
-
- /*
- * Any session set property which is different in 3XX packetization
- * should be added as a new case below. All unchanged session set
- * properties will be handled in the default case.
- */
- switch (ptype) {
- case HAL_PARAM_VDEC_MULTI_STREAM:
- {
- u32 buffer_type;
- struct hfi_3x_multi_stream *hfi;
- struct hal_multi_stream *prop =
- (struct hal_multi_stream *) pdata;
- pkt->rg_property_data[0] =
- HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM;
- hfi = (struct hfi_3x_multi_stream *) &pkt->rg_property_data[1];
-
- buffer_type = get_hfi_buffer(prop->buffer_type);
- if (buffer_type)
- hfi->buffer_type = buffer_type;
- else
- return -EINVAL;
- hfi->enable = prop->enable;
- pkt->size += sizeof(u32) + sizeof(struct hfi_3x_multi_stream);
- break;
- }
- case HAL_PARAM_VENC_INTRA_REFRESH:
- {
- struct hfi_3x_intra_refresh *hfi;
- struct hal_intra_refresh *prop =
- (struct hal_intra_refresh *) pdata;
- pkt->rg_property_data[0] =
- HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH;
- hfi = (struct hfi_3x_intra_refresh *) &pkt->rg_property_data[1];
- hfi->mbs = 0;
- switch (prop->mode) {
- case HAL_INTRA_REFRESH_NONE:
- hfi->mode = HFI_INTRA_REFRESH_NONE;
- break;
- case HAL_INTRA_REFRESH_ADAPTIVE:
- hfi->mode = HFI_INTRA_REFRESH_ADAPTIVE;
- hfi->mbs = prop->air_mbs;
- break;
- case HAL_INTRA_REFRESH_CYCLIC:
- hfi->mode = HFI_INTRA_REFRESH_CYCLIC;
- hfi->mbs = prop->cir_mbs;
- break;
- case HAL_INTRA_REFRESH_CYCLIC_ADAPTIVE:
- hfi->mode = HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE;
- hfi->mbs = prop->air_mbs;
- break;
- case HAL_INTRA_REFRESH_RANDOM:
- hfi->mode = HFI_INTRA_REFRESH_RANDOM;
- hfi->mbs = prop->air_mbs;
- break;
- default:
- dprintk(VIDC_ERR,
- "Invalid intra refresh setting: %d\n",
- prop->mode);
- break;
- }
- pkt->size += sizeof(u32) + sizeof(struct hfi_3x_intra_refresh);
- break;
- }
- case HAL_PARAM_SYNC_BASED_INTERRUPT:
- {
- create_pkt_enable(pkt->rg_property_data,
- HFI_PROPERTY_PARAM_SYNC_BASED_INTERRUPT,
- ((struct hal_enable *)pdata)->enable);
- pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
- break;
- }
- case HAL_PARAM_VENC_VQZIP_SEI:
- {
- create_pkt_enable(pkt->rg_property_data,
- HFI_PROPERTY_PARAM_VENC_VQZIP_SEI_TYPE,
- ((struct hal_enable *)pdata)->enable);
- pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
- break;
- }
- /* Deprecated param on Venus 3xx */
- case HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER:
- {
- rc = -ENOTSUPP;
- break;
- }
- case HAL_PARAM_BUFFER_SIZE_MINIMUM:
- {
- struct hfi_buffer_size_minimum *hfi;
- struct hal_buffer_size_minimum *prop =
- (struct hal_buffer_size_minimum *) pdata;
- u32 buffer_type;
-
- pkt->rg_property_data[0] =
- HFI_PROPERTY_PARAM_BUFFER_SIZE_MINIMUM;
-
- hfi = (struct hfi_buffer_size_minimum *)
- &pkt->rg_property_data[1];
- hfi->buffer_size = prop->buffer_size;
-
- buffer_type = get_hfi_buffer(prop->buffer_type);
- if (buffer_type)
- hfi->buffer_type = buffer_type;
- else
- return -EINVAL;
-
- pkt->size += sizeof(u32) + sizeof(struct
- hfi_buffer_count_actual);
- break;
- }
- case HAL_PARAM_VENC_H264_PIC_ORDER_CNT:
- {
- pkt->rg_property_data[0] =
- HFI_PROPERTY_PARAM_VENC_H264_PICORDER_CNT_TYPE;
- pkt->rg_property_data[1] = *(u32 *)pdata;
- pkt->size += sizeof(u32) * 2;
- break;
- }
- case HAL_PARAM_VENC_LOW_LATENCY:
- {
- struct hfi_enable *hfi;
-
- pkt->rg_property_data[0] =
- HFI_PROPERTY_PARAM_VENC_LOW_LATENCY_MODE;
- hfi = (struct hfi_enable *) &pkt->rg_property_data[1];
- hfi->enable = ((struct hal_enable *) pdata)->enable;
- pkt->size += sizeof(u32) * 2;
- break;
- }
- case HAL_CONFIG_VENC_BLUR_RESOLUTION:
- {
- struct hfi_frame_size *hfi;
- struct hal_frame_size *prop = (struct hal_frame_size *) pdata;
- u32 buffer_type;
-
- pkt->rg_property_data[0] =
- HFI_PROPERTY_CONFIG_VENC_BLUR_FRAME_SIZE;
- hfi = (struct hfi_frame_size *) &pkt->rg_property_data[1];
- buffer_type = get_hfi_buffer(prop->buffer_type);
- if (buffer_type)
- hfi->buffer_type = buffer_type;
- else
- return -EINVAL;
-
- hfi->height = prop->height;
- hfi->width = prop->width;
- pkt->size += sizeof(u32) + sizeof(struct hfi_frame_size);
- break;
- }
- default:
- rc = create_pkt_cmd_session_set_property(pkt,
- session, ptype, pdata);
- }
- return rc;
-}
-
int create_pkt_cmd_session_sync_process(
struct hfi_cmd_session_sync_process_packet *pkt,
struct hal_session *session)
@@ -2524,44 +2067,22 @@
.session_etb_decoder = create_pkt_cmd_session_etb_decoder,
.session_etb_encoder = create_pkt_cmd_session_etb_encoder,
.session_ftb = create_pkt_cmd_session_ftb,
- .session_parse_seq_header = create_pkt_cmd_session_parse_seq_header,
- .session_get_seq_hdr = create_pkt_cmd_session_get_seq_hdr,
.session_get_buf_req = create_pkt_cmd_session_get_buf_req,
.session_flush = create_pkt_cmd_session_flush,
.session_get_property = create_pkt_cmd_session_get_property,
.session_set_property = create_pkt_cmd_session_set_property,
};
-struct hfi_packetization_ops *get_venus_3x_ops(void)
-{
- static struct hfi_packetization_ops hfi_venus_3x;
-
- hfi_venus_3x = hfi_default;
-
- /* Override new HFI functions for HFI_PACKETIZATION_3XX here. */
- hfi_venus_3x.session_set_property =
- create_3x_pkt_cmd_session_set_property;
- hfi_venus_3x.session_get_property =
- create_3x_pkt_cmd_session_get_property;
- hfi_venus_3x.session_cmd = create_3x_pkt_cmd_session_cmd;
- hfi_venus_3x.session_sync_process = create_pkt_cmd_session_sync_process;
-
- return &hfi_venus_3x;
-}
-
struct hfi_packetization_ops *hfi_get_pkt_ops_handle(
enum hfi_packetization_type type)
{
dprintk(VIDC_DBG, "%s selected\n",
- type == HFI_PACKETIZATION_LEGACY ? "legacy packetization" :
- type == HFI_PACKETIZATION_3XX ? "3xx packetization" :
- "Unknown hfi");
+ type == HFI_PACKETIZATION_4XX ?
+ "4xx packetization" : "Unknown hfi");
switch (type) {
- case HFI_PACKETIZATION_LEGACY:
+ case HFI_PACKETIZATION_4XX:
return &hfi_default;
- case HFI_PACKETIZATION_3XX:
- return get_venus_3x_ops();
}
return NULL;
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.h b/drivers/media/platform/msm/vidc/hfi_packetization.h
index a6e7afac..e0def0f 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.h
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.h
@@ -23,8 +23,7 @@
((q)->pkt_ops->op(args)) : 0)
enum hfi_packetization_type {
- HFI_PACKETIZATION_LEGACY,
- HFI_PACKETIZATION_3XX,
+ HFI_PACKETIZATION_4XX,
};
struct hfi_packetization_ops {
@@ -74,12 +73,6 @@
int (*session_ftb)(struct hfi_cmd_session_fill_buffer_packet *pkt,
struct hal_session *session,
struct vidc_frame_data *output_frame);
- int (*session_parse_seq_header)(
- struct hfi_cmd_session_parse_sequence_header_packet *pkt,
- struct hal_session *session, struct vidc_seq_hdr *seq_hdr);
- int (*session_get_seq_hdr)(
- struct hfi_cmd_session_get_sequence_header_packet *pkt,
- struct hal_session *session, struct vidc_seq_hdr *seq_hdr);
int (*session_get_buf_req)(
struct hfi_cmd_session_get_property_packet *pkt,
struct hal_session *session);
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index c8eed4b..c8cff77 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -707,74 +707,6 @@
return 0;
}
-static int copy_alloc_mode_to_sessions(
- struct hfi_buffer_alloc_mode_supported *prop,
- struct msm_vidc_capability *capabilities,
- u32 num_sessions, u32 codecs, u32 domain)
-{
- u32 i = 0, j = 0;
- struct msm_vidc_capability *capability;
- u32 sess_codec;
- u32 sess_domain;
-
- /*
- * iterate over num_sessions and copy all the entries
- * to matching sessions.
- */
- for (i = 0; i < num_sessions; i++) {
- sess_codec = 0;
- sess_domain = 0;
- capability = &capabilities[i];
-
- if (capability->codec)
- sess_codec =
- vidc_get_hfi_codec(capability->codec);
- if (capability->domain)
- sess_domain =
- vidc_get_hfi_domain(capability->domain);
-
- if (!(sess_codec & codecs && sess_domain & domain))
- continue;
-
- for (j = 0; j < prop->num_entries; j++) {
- if (prop->buffer_type == HFI_BUFFER_OUTPUT ||
- prop->buffer_type == HFI_BUFFER_OUTPUT2) {
- switch (prop->rg_data[j]) {
- case HFI_BUFFER_MODE_STATIC:
- capability->alloc_mode_out |=
- HAL_BUFFER_MODE_STATIC;
- break;
- case HFI_BUFFER_MODE_RING:
- capability->alloc_mode_out |=
- HAL_BUFFER_MODE_RING;
- break;
- case HFI_BUFFER_MODE_DYNAMIC:
- capability->alloc_mode_out |=
- HAL_BUFFER_MODE_DYNAMIC;
- break;
- }
- } else if (prop->buffer_type == HFI_BUFFER_INPUT) {
- switch (prop->rg_data[j]) {
- case HFI_BUFFER_MODE_STATIC:
- capability->alloc_mode_in |=
- HAL_BUFFER_MODE_STATIC;
- break;
- case HFI_BUFFER_MODE_RING:
- capability->alloc_mode_in |=
- HAL_BUFFER_MODE_RING;
- break;
- case HFI_BUFFER_MODE_DYNAMIC:
- capability->alloc_mode_in |=
- HAL_BUFFER_MODE_DYNAMIC;
- break;
- }
- }
- }
- }
-
- return 0;
-}
-
static enum vidc_status hfi_parse_init_done_properties(
struct msm_vidc_capability *capabilities,
u32 num_sessions, u8 *data_ptr, u32 num_properties,
@@ -913,13 +845,6 @@
num_properties--;
break;
}
- case HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED:
- {
- next_offset +=
- sizeof(struct hfi_interlace_format_supported);
- num_properties--;
- break;
- }
case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED:
{
next_offset +=
@@ -933,12 +858,6 @@
num_properties--;
break;
}
- case HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE:
- {
- next_offset += sizeof(u32);
- num_properties--;
- break;
- }
case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH:
{
next_offset +=
@@ -946,29 +865,6 @@
num_properties--;
break;
}
- case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED:
- {
- struct hfi_buffer_alloc_mode_supported *prop =
- (struct hfi_buffer_alloc_mode_supported *)
- (data_ptr + next_offset);
-
- if (prop->num_entries >= 32) {
- dprintk(VIDC_ERR,
- "%s - num_entries: %d from f/w seems suspect\n",
- __func__, prop->num_entries);
- break;
- }
- next_offset +=
- sizeof(struct hfi_buffer_alloc_mode_supported) -
- sizeof(u32) + prop->num_entries * sizeof(u32);
-
- copy_alloc_mode_to_sessions(prop,
- capabilities, num_sessions,
- codecs, domain);
-
- num_properties--;
- break;
- }
default:
dprintk(VIDC_DBG,
"%s: default case - data_ptr %pK, prop_id 0x%x\n",
@@ -1752,42 +1648,6 @@
return 0;
}
-static int hfi_process_session_get_seq_hdr_done(
- u32 device_id,
- struct hfi_msg_session_get_sequence_header_done_packet *pkt,
- struct msm_vidc_cb_info *info)
-{
- struct msm_vidc_cb_data_done data_done = {0};
-
- if (!pkt || pkt->size !=
- sizeof(struct
- hfi_msg_session_get_sequence_header_done_packet)) {
- dprintk(VIDC_ERR, "%s: bad packet/packet size\n",
- __func__);
- return -E2BIG;
- }
-
- dprintk(VIDC_DBG, "RECEIVED:SESSION_GET_SEQ_HDR_DONE[%#x]\n",
- pkt->session_id);
-
- data_done.device_id = device_id;
- data_done.size = sizeof(struct msm_vidc_cb_data_done);
- data_done.session_id = (void *)(uintptr_t)pkt->session_id;
- data_done.status = hfi_map_err_status(pkt->error_type);
- data_done.output_done.packet_buffer1 =
- (ion_phys_addr_t)pkt->sequence_header;
- data_done.output_done.filled_len1 = pkt->header_len;
- dprintk(VIDC_INFO, "seq_hdr: %#x, Length: %d\n",
- pkt->sequence_header, pkt->header_len);
-
- *info = (struct msm_vidc_cb_info) {
- .response_type = HAL_SESSION_GET_SEQ_HDR_DONE,
- .response.data = data_done,
- };
-
- return 0;
-}
-
static void hfi_process_sys_get_prop_image_version(
struct hfi_msg_sys_property_info_packet *pkt)
{
@@ -1933,9 +1793,6 @@
case HFI_MSG_SYS_RELEASE_RESOURCE:
pkt_func = (pkt_func_def)hfi_process_sys_rel_resource_done;
break;
- case HFI_MSG_SESSION_GET_SEQUENCE_HEADER_DONE:
- pkt_func = (pkt_func_def) hfi_process_session_get_seq_hdr_done;
- break;
case HFI_MSG_SESSION_RELEASE_BUFFERS_DONE:
pkt_func = (pkt_func_def)hfi_process_session_rel_buf_done;
break;
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index dedd15f..a635a6a 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -51,7 +51,6 @@
};
static const char *const mpeg_vidc_video_alloc_mode_type[] = {
"Buffer Allocation Static",
- "Buffer Allocation Ring Buffer",
"Buffer Allocation Dynamic Buffer"
};
@@ -1298,7 +1297,6 @@
default:
dprintk(VIDC_ERR, "Invalid q type = %d\n", q->type);
rc = -EINVAL;
- break;
}
exit:
return rc;
@@ -1663,12 +1661,10 @@
inst->capability.height.max = DEFAULT_HEIGHT;
inst->capability.width.min = MIN_SUPPORTED_WIDTH;
inst->capability.width.max = DEFAULT_WIDTH;
- inst->capability.alloc_mode_in = HAL_BUFFER_MODE_STATIC;
- inst->capability.alloc_mode_out = HAL_BUFFER_MODE_STATIC;
inst->capability.secure_output2_threshold.min = 0;
inst->capability.secure_output2_threshold.max = 0;
inst->buffer_mode_set[OUTPUT_PORT] = HAL_BUFFER_MODE_STATIC;
- inst->buffer_mode_set[CAPTURE_PORT] = HAL_BUFFER_MODE_STATIC;
+ inst->buffer_mode_set[CAPTURE_PORT] = HAL_BUFFER_MODE_DYNAMIC;
inst->prop.fps = DEFAULT_FPS;
inst->operating_rate = 0;
memcpy(&inst->fmts[OUTPUT_PORT], &vdec_formats[2],
@@ -1929,7 +1925,6 @@
void *pdata = NULL;
struct hfi_device *hdev;
struct hal_extradata_enable extra;
- struct hal_buffer_alloc_mode alloc_mode;
struct hal_multi_stream multi_stream;
struct v4l2_ctrl *temp_ctrl = NULL;
struct hal_profile_level profile_level;
@@ -2084,16 +2079,6 @@
"Failed setting OUTPUT2 size : %d\n",
rc);
- alloc_mode.buffer_mode =
- inst->buffer_mode_set[CAPTURE_PORT];
- alloc_mode.buffer_type = HAL_BUFFER_OUTPUT2;
- rc = call_hfi_op(hdev, session_set_property,
- inst->session, HAL_PARAM_BUFFER_ALLOC_MODE,
- &alloc_mode);
- if (rc)
- dprintk(VIDC_ERR,
- "Failed to set alloc_mode on OUTPUT2: %d\n",
- rc);
break;
default:
dprintk(VIDC_ERR,
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 6c57b2e..89d44de 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -2275,23 +2275,6 @@
(inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_H264) ||
(inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_HEVC);
- if (is_cont_intra_supported) {
- if (ctrl->val != HAL_INTRA_REFRESH_NONE)
- enable.enable = true;
- else
- enable.enable = false;
-
- rc = call_hfi_op(hdev, session_set_property,
- (void *)inst->session,
- HAL_PARAM_VENC_CONSTRAINED_INTRA_PRED, &enable);
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed to set constrained intra\n");
- rc = -EINVAL;
- break;
- }
- }
-
property_id = HAL_PARAM_VENC_INTRA_REFRESH;
intra_refresh.mode = ctrl->val;
@@ -2870,11 +2853,9 @@
inst->capability.height.max = DEFAULT_HEIGHT;
inst->capability.width.min = MIN_SUPPORTED_WIDTH;
inst->capability.width.max = DEFAULT_WIDTH;
- inst->capability.alloc_mode_in = HAL_BUFFER_MODE_STATIC;
- inst->capability.alloc_mode_out = HAL_BUFFER_MODE_STATIC;
inst->capability.secure_output2_threshold.min = 0;
inst->capability.secure_output2_threshold.max = 0;
- inst->buffer_mode_set[OUTPUT_PORT] = HAL_BUFFER_MODE_STATIC;
+ inst->buffer_mode_set[OUTPUT_PORT] = HAL_BUFFER_MODE_DYNAMIC;
inst->buffer_mode_set[CAPTURE_PORT] = HAL_BUFFER_MODE_STATIC;
inst->prop.fps = DEFAULT_FPS;
inst->capability.pixelprocess_capabilities = 0;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 8106815..3dd5a15 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -3708,20 +3708,6 @@
"Failed to scale bus. Performance might be impacted\n");
}
-static int request_seq_header(struct msm_vidc_inst *inst,
- struct vidc_frame_data *data)
-{
- struct vidc_seq_hdr seq_hdr = {
- .seq_hdr = data->device_addr,
- .seq_hdr_len = data->alloc_len,
- };
-
- dprintk(VIDC_DBG, "Requesting sequence header in %pa\n",
- &seq_hdr.seq_hdr);
- return call_hfi_op(inst->core->device, session_get_seq_hdr,
- inst->session, &seq_hdr);
-}
-
/*
* Attempts to queue `vb` to hardware. If, for various reasons, the buffer
* cannot be queued to hardware, the buffer will be staged for commit in the
@@ -3861,18 +3847,6 @@
if (batch_mode) {
int ftb_index = 0, c = 0;
- for (c = 0; atomic_read(&inst->seq_hdr_reqs) > 0; ++c) {
- rc = request_seq_header(inst, &ftbs.data[c]);
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed requesting sequence header: %d\n",
- rc);
- goto err_bad_input;
- }
-
- atomic_dec(&inst->seq_hdr_reqs);
- }
-
ftb_index = c;
rc = call_hfi_op(hdev, session_process_batch, inst->session,
etbs.count, etbs.data,
@@ -3917,18 +3891,6 @@
if (!batch_mode && ftbs.count) {
int c = 0;
- for (c = 0; atomic_read(&inst->seq_hdr_reqs) > 0; ++c) {
- rc = request_seq_header(inst, &ftbs.data[c]);
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed requesting sequence header: %d\n",
- rc);
- goto err_bad_input;
- }
-
- atomic_dec(&inst->seq_hdr_reqs);
- }
-
for (; c < ftbs.count; ++c) {
struct vidc_frame_data *frame_data = &ftbs.data[c];
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
index a0e0a86..f418260 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
@@ -304,9 +304,6 @@
case HAL_BUFFER_MODE_STATIC:
write_str(&dbg_buf, "buffer mode : %s\n", "static");
break;
- case HAL_BUFFER_MODE_RING:
- write_str(&dbg_buf, "buffer mode : %s\n", "ring");
- break;
case HAL_BUFFER_MODE_DYNAMIC:
write_str(&dbg_buf, "buffer mode : %s\n", "dynamic");
break;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index c05a9cc..b4f3cd7 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -292,7 +292,6 @@
struct msm_vidc_capability capability;
u32 buffer_size_limit;
enum buffer_mode_type buffer_mode_set[MAX_PORT_NUM];
- atomic_t seq_hdr_reqs;
struct v4l2_ctrl **ctrls;
bool dcvs_mode;
enum msm_vidc_pixel_depth bit_depth;
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 83846d3..a59d73f 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -223,22 +223,6 @@
}
break;
}
- case HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER:
- {
- struct hfi_cmd_session_parse_sequence_header_packet *pkt =
- (struct hfi_cmd_session_parse_sequence_header_packet *)
- packet;
- pkt->packet_buffer -= fw_bias;
- break;
- }
- case HFI_CMD_SESSION_GET_SEQUENCE_HEADER:
- {
- struct hfi_cmd_session_get_sequence_header_packet *pkt =
- (struct hfi_cmd_session_get_sequence_header_packet *)
- packet;
- pkt->packet_buffer -= fw_bias;
- break;
- }
default:
break;
}
@@ -1358,8 +1342,7 @@
mutex_lock(&device->lock);
- if (device->packetization_type == HFI_PACKETIZATION_3XX)
- prop = HAL_VIDEO_DYNAMIC_BUF_MODE;
+ prop = HAL_VIDEO_DYNAMIC_BUF_MODE;
mutex_unlock(&device->lock);
return prop;
@@ -3025,72 +3008,6 @@
return rc;
}
-static int venus_hfi_session_parse_seq_hdr(void *sess,
- struct vidc_seq_hdr *seq_hdr)
-{
- struct hfi_cmd_session_parse_sequence_header_packet *pkt;
- int rc = 0;
- u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
- struct hal_session *session = sess;
- struct venus_hfi_device *device;
-
- if (!session || !session->device || !seq_hdr) {
- dprintk(VIDC_ERR, "Invalid Params\n");
- return -EINVAL;
- }
-
- device = session->device;
- mutex_lock(&device->lock);
-
- pkt = (struct hfi_cmd_session_parse_sequence_header_packet *)packet;
- rc = call_hfi_pkt_op(device, session_parse_seq_header,
- pkt, session, seq_hdr);
- if (rc) {
- dprintk(VIDC_ERR,
- "Session parse seq hdr: failed to create pkt\n");
- goto err_create_pkt;
- }
-
- if (__iface_cmdq_write(session->device, pkt))
- rc = -ENOTEMPTY;
-err_create_pkt:
- mutex_unlock(&device->lock);
- return rc;
-}
-
-static int venus_hfi_session_get_seq_hdr(void *sess,
- struct vidc_seq_hdr *seq_hdr)
-{
- struct hfi_cmd_session_get_sequence_header_packet *pkt;
- int rc = 0;
- u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
- struct hal_session *session = sess;
- struct venus_hfi_device *device;
-
- if (!session || !session->device || !seq_hdr) {
- dprintk(VIDC_ERR, "Invalid Params\n");
- return -EINVAL;
- }
-
- device = session->device;
- mutex_lock(&device->lock);
-
- pkt = (struct hfi_cmd_session_get_sequence_header_packet *)packet;
- rc = call_hfi_pkt_op(device, session_get_seq_hdr,
- pkt, session, seq_hdr);
- if (rc) {
- dprintk(VIDC_ERR,
- "Session get seq hdr: failed to create pkt\n");
- goto err_create_pkt;
- }
-
- if (__iface_cmdq_write(session->device, pkt))
- rc = -ENOTEMPTY;
-err_create_pkt:
- mutex_unlock(&device->lock);
- return rc;
-}
-
static int venus_hfi_session_get_buf_req(void *sess)
{
struct hfi_cmd_session_get_property_packet pkt;
@@ -4537,23 +4454,13 @@
static int __initialize_packetization(struct venus_hfi_device *device)
{
int rc = 0;
- const char *hfi_version;
if (!device || !device->res) {
dprintk(VIDC_ERR, "%s - invalid param\n", __func__);
return -EINVAL;
}
- hfi_version = device->res->hfi_version;
-
- if (!hfi_version) {
- device->packetization_type = HFI_PACKETIZATION_LEGACY;
- } else if (!strcmp(hfi_version, "3xx")) {
- device->packetization_type = HFI_PACKETIZATION_3XX;
- } else {
- dprintk(VIDC_ERR, "Unsupported hfi version\n");
- return -EINVAL;
- }
+ device->packetization_type = HFI_PACKETIZATION_4XX;
device->pkt_ops = hfi_get_pkt_ops_handle(device->packetization_type);
if (!device->pkt_ops) {
@@ -4705,8 +4612,6 @@
hdev->session_etb = venus_hfi_session_etb;
hdev->session_ftb = venus_hfi_session_ftb;
hdev->session_process_batch = venus_hfi_session_process_batch;
- hdev->session_parse_seq_hdr = venus_hfi_session_parse_seq_hdr;
- hdev->session_get_seq_hdr = venus_hfi_session_get_seq_hdr;
hdev->session_get_buf_req = venus_hfi_session_get_buf_req;
hdev->session_flush = venus_hfi_session_flush;
hdev->session_set_property = venus_hfi_session_set_property;
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h
index 6088873..949bc47 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi.h
@@ -63,8 +63,6 @@
#define HFI_BUFFER_INTERNAL_SCRATCH_1 (HFI_OX_BASE + 0x5)
#define HFI_BUFFER_INTERNAL_SCRATCH_2 (HFI_OX_BASE + 0x6)
-#define HFI_BUFFER_MODE_STATIC (HFI_OX_BASE + 0x1)
-#define HFI_BUFFER_MODE_RING (HFI_OX_BASE + 0x2)
#define HFI_BUFFER_MODE_DYNAMIC (HFI_OX_BASE + 0x3)
#define HFI_FLUSH_INPUT (HFI_OX_BASE + 0x1)
@@ -131,24 +129,14 @@
(HFI_PROPERTY_PARAM_OX_START + 0x001)
#define HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO \
(HFI_PROPERTY_PARAM_OX_START + 0x002)
-#define HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED \
- (HFI_PROPERTY_PARAM_OX_START + 0x003)
-#define HFI_PROPERTY_PARAM_CHROMA_SITE \
-(HFI_PROPERTY_PARAM_OX_START + 0x004)
#define HFI_PROPERTY_PARAM_EXTRA_DATA_HEADER_CONFIG \
(HFI_PROPERTY_PARAM_OX_START + 0x005)
#define HFI_PROPERTY_PARAM_INDEX_EXTRADATA \
(HFI_PROPERTY_PARAM_OX_START + 0x006)
-#define HFI_PROPERTY_PARAM_DIVX_FORMAT \
- (HFI_PROPERTY_PARAM_OX_START + 0x007)
-#define HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE \
- (HFI_PROPERTY_PARAM_OX_START + 0x008)
#define HFI_PROPERTY_PARAM_S3D_FRAME_PACKING_EXTRADATA \
(HFI_PROPERTY_PARAM_OX_START + 0x009)
#define HFI_PROPERTY_PARAM_ERR_DETECTION_CODE_EXTRADATA \
(HFI_PROPERTY_PARAM_OX_START + 0x00A)
-#define HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED \
- (HFI_PROPERTY_PARAM_OX_START + 0x00B)
#define HFI_PROPERTY_PARAM_BUFFER_SIZE_MINIMUM \
(HFI_PROPERTY_PARAM_OX_START + 0x00C)
#define HFI_PROPERTY_PARAM_SYNC_BASED_INTERRUPT \
@@ -162,15 +150,10 @@
(HFI_PROPERTY_CONFIG_OX_START + 0x002)
#define HFI_PROPERTY_CONFIG_PRIORITY \
(HFI_PROPERTY_CONFIG_OX_START + 0x003)
-#define HFI_PROPERTY_CONFIG_BATCH_INFO \
- (HFI_PROPERTY_CONFIG_OX_START + 0x004)
-
#define HFI_PROPERTY_PARAM_VDEC_OX_START \
(HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x3000)
#define HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER \
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001)
-#define HFI_PROPERTY_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT\
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x002)
#define HFI_PROPERTY_PARAM_VDEC_MULTI_VIEW_SELECT \
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x003)
#define HFI_PROPERTY_PARAM_VDEC_PICTURE_TYPE_DECODE \
@@ -181,8 +164,6 @@
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x006)
#define HFI_PROPERTY_PARAM_VDEC_NUM_CONCEALED_MB \
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x007)
-#define HFI_PROPERTY_PARAM_VDEC_H264_ENTROPY_SWITCHING \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x008)
#define HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO\
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x009)
#define HFI_PROPERTY_PARAM_VDEC_FRAME_RATE_EXTRADATA \
@@ -193,9 +174,6 @@
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00C)
#define HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE \
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00D)
-
-#define HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00E)
#define HFI_PROPERTY_PARAM_VDEC_VC1_FRAMEDISP_EXTRADATA \
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x011)
#define HFI_PROPERTY_PARAM_VDEC_VC1_SEQDISP_EXTRADATA \
@@ -214,8 +192,6 @@
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x018)
#define HFI_PROPERTY_PARAM_VDEC_FRAME_BITS_INFO_EXTRADATA \
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x019)
-#define HFI_PROPERTY_PARAM_VDEC_SCS_THRESHOLD \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x01A)
#define HFI_PROPERTY_PARAM_VUI_DISPLAY_INFO_EXTRADATA \
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x01B)
#define HFI_PROPERTY_PARAM_VDEC_VQZIP_SEI_EXTRADATA \
@@ -256,12 +232,8 @@
(HFI_PROPERTY_PARAM_VENC_OX_START + 0x008)
#define HFI_PROPERTY_PARAM_VENC_OVERRIDE_QP_EXTRADATA \
(HFI_PROPERTY_PARAM_VENC_OX_START + 0x009)
-
#define HFI_PROPERTY_CONFIG_VENC_OX_START \
(HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x6000)
-#define HFI_PROPERTY_CONFIG_VENC_FRAME_QP \
- (HFI_PROPERTY_CONFIG_VENC_OX_START + 0x001)
-
#define HFI_PROPERTY_PARAM_VPE_OX_START \
(HFI_DOMAIN_BASE_VPE + HFI_ARCH_OX_OFFSET + 0x7000)
#define HFI_PROPERTY_PARAM_VPE_COLOR_SPACE_CONVERSION \
@@ -296,13 +268,6 @@
u32 buffer_alignment;
};
-#define HFI_CHROMA_SITE_0 (HFI_OX_BASE + 0x1)
-#define HFI_CHROMA_SITE_1 (HFI_OX_BASE + 0x2)
-#define HFI_CHROMA_SITE_2 (HFI_OX_BASE + 0x3)
-#define HFI_CHROMA_SITE_3 (HFI_OX_BASE + 0x4)
-#define HFI_CHROMA_SITE_4 (HFI_OX_BASE + 0x5)
-#define HFI_CHROMA_SITE_5 (HFI_OX_BASE + 0x6)
-
struct hfi_data_payload {
u32 size;
u8 rg_data[1];
@@ -312,11 +277,6 @@
u32 picture_type;
};
-struct hfi_display_picture_buffer_count {
- int enable;
- u32 count;
-};
-
struct hfi_extra_data_header_config {
u32 type;
u32 buffer_type;
@@ -325,17 +285,6 @@
u32 client_extra_data_id;
};
-struct hfi_interlace_format_supported {
- u32 buffer_type;
- u32 format;
-};
-
-struct hfi_buffer_alloc_mode_supported {
- u32 buffer_type;
- u32 num_entries;
- u32 rg_data[1];
-};
-
struct hfi_mb_error_map {
u32 error_map_size;
u8 rg_error_map[1];
@@ -419,8 +368,6 @@
#define HFI_MSG_SESSION_PROPERTY_INFO (HFI_MSG_SESSION_OX_START + 0x9)
#define HFI_MSG_SESSION_RELEASE_RESOURCES_DONE \
(HFI_MSG_SESSION_OX_START + 0xA)
-#define HFI_MSG_SESSION_PARSE_SEQUENCE_HEADER_DONE \
- (HFI_MSG_SESSION_OX_START + 0xB)
#define HFI_MSG_SESSION_RELEASE_BUFFERS_DONE \
(HFI_MSG_SESSION_OX_START + 0xC)
@@ -576,14 +523,6 @@
u32 session_id;
};
-struct hfi_cmd_session_parse_sequence_header_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 header_len;
- u32 packet_buffer;
-};
-
struct hfi_msg_sys_session_abort_done_packet {
u32 size;
u32 packet_type;
@@ -734,15 +673,6 @@
u32 rgData[0];
};
-struct hfi_msg_session_parse_sequence_header_done_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 error_type;
- u32 num_properties;
- u32 rg_property_data[1];
-};
-
struct hfi_msg_session_property_info_packet {
u32 size;
u32 packet_type;
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 7edada9..1ed3d2d 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -946,10 +946,6 @@
u32 ngap;
};
-struct hal_seq_header_info {
- u32 nax_header_len;
-};
-
struct hal_aspect_ratio {
u32 aspect_width;
u32 aspect_height;
@@ -1068,11 +1064,6 @@
u32 extradata_size;
};
-struct vidc_seq_hdr {
- ion_phys_addr_t seq_hdr;
- u32 seq_hdr_len;
-};
-
struct hal_fw_info {
char version[VENUS_VERSION_LENGTH];
phys_addr_t base_addr;
@@ -1096,9 +1087,8 @@
};
enum buffer_mode_type {
- HAL_BUFFER_MODE_STATIC = 0x001,
- HAL_BUFFER_MODE_RING = 0x010,
HAL_BUFFER_MODE_DYNAMIC = 0x100,
+ HAL_BUFFER_MODE_STATIC = 0x001,
};
struct hal_buffer_alloc_mode {
@@ -1188,7 +1178,6 @@
struct hal_nal_stream_format_supported nal_stream_format_supported;
struct hal_nal_stream_format_select nal_stream_format_select;
struct hal_multi_view_format multi_view_format;
- struct hal_seq_header_info seq_header_info;
struct hal_codec_supported codec_supported;
struct hal_multi_view_select multi_view_select;
struct hal_timestamp_scale timestamp_scale;
@@ -1344,7 +1333,6 @@
struct vidc_frame_plane_config frame_plane_config;
struct vidc_uncompressed_frame_config uncompressed_frame_config;
struct vidc_frame_data frame_data;
- struct vidc_seq_hdr seq_hdr;
struct vidc_hal_ebd ebd;
struct vidc_hal_fbd fbd;
struct vidc_hal_sys_init_done sys_init_done;
@@ -1505,10 +1493,6 @@
int (*session_process_batch)(void *sess,
int num_etbs, struct vidc_frame_data etbs[],
int num_ftbs, struct vidc_frame_data ftbs[]);
- int (*session_parse_seq_hdr)(void *sess,
- struct vidc_seq_hdr *seq_hdr);
- int (*session_get_seq_hdr)(void *sess,
- struct vidc_seq_hdr *seq_hdr);
int (*session_get_buf_req)(void *sess);
int (*session_flush)(void *sess, enum hal_flush flush_mode);
int (*session_set_property)(void *sess, enum hal_property ptype,
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index 9ab1a81..a552992 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -76,14 +76,8 @@
#define HFI_EVENT_SESSION_ERROR (HFI_COMMON_BASE + 0x2)
#define HFI_VIDEO_CODEC_H264 0x00000002
-#define HFI_VIDEO_CODEC_H263 0x00000004
#define HFI_VIDEO_CODEC_MPEG1 0x00000008
#define HFI_VIDEO_CODEC_MPEG2 0x00000010
-#define HFI_VIDEO_CODEC_MPEG4 0x00000020
-#define HFI_VIDEO_CODEC_DIVX_311 0x00000040
-#define HFI_VIDEO_CODEC_DIVX 0x00000080
-#define HFI_VIDEO_CODEC_VC1 0x00000100
-#define HFI_VIDEO_CODEC_SPARK 0x00000200
#define HFI_VIDEO_CODEC_VP8 0x00001000
#define HFI_VIDEO_CODEC_HEVC 0x00002000
#define HFI_VIDEO_CODEC_VP9 0x00004000
@@ -113,18 +107,7 @@
#define HFI_H264_LEVEL_42 0x00002000
#define HFI_H264_LEVEL_5 0x00004000
#define HFI_H264_LEVEL_51 0x00008000
-#define HFI_H264_LEVEL_52 0x00010000
-
-#define HFI_H263_PROFILE_BASELINE 0x00000001
-
-#define HFI_H263_LEVEL_10 0x00000001
-#define HFI_H263_LEVEL_20 0x00000002
-#define HFI_H263_LEVEL_30 0x00000004
-#define HFI_H263_LEVEL_40 0x00000008
-#define HFI_H263_LEVEL_45 0x00000010
-#define HFI_H263_LEVEL_50 0x00000020
-#define HFI_H263_LEVEL_60 0x00000040
-#define HFI_H263_LEVEL_70 0x00000080
+#define HFI_H264_LEVEL_52 0x00010000
#define HFI_MPEG2_PROFILE_SIMPLE 0x00000001
#define HFI_MPEG2_PROFILE_MAIN 0x00000002
@@ -138,36 +121,6 @@
#define HFI_MPEG2_LEVEL_H14 0x00000004
#define HFI_MPEG2_LEVEL_HL 0x00000008
-#define HFI_MPEG4_PROFILE_SIMPLE 0x00000001
-#define HFI_MPEG4_PROFILE_ADVANCEDSIMPLE 0x00000002
-
-#define HFI_MPEG4_LEVEL_0 0x00000001
-#define HFI_MPEG4_LEVEL_0b 0x00000002
-#define HFI_MPEG4_LEVEL_1 0x00000004
-#define HFI_MPEG4_LEVEL_2 0x00000008
-#define HFI_MPEG4_LEVEL_3 0x00000010
-#define HFI_MPEG4_LEVEL_4 0x00000020
-#define HFI_MPEG4_LEVEL_4a 0x00000040
-#define HFI_MPEG4_LEVEL_5 0x00000080
-#define HFI_MPEG4_LEVEL_6 0x00000100
-#define HFI_MPEG4_LEVEL_7 0x00000200
-#define HFI_MPEG4_LEVEL_8 0x00000400
-#define HFI_MPEG4_LEVEL_9 0x00000800
-#define HFI_MPEG4_LEVEL_3b 0x00001000
-
-#define HFI_VC1_PROFILE_SIMPLE 0x00000001
-#define HFI_VC1_PROFILE_MAIN 0x00000002
-#define HFI_VC1_PROFILE_ADVANCED 0x00000004
-
-#define HFI_VC1_LEVEL_LOW 0x00000001
-#define HFI_VC1_LEVEL_MEDIUM 0x00000002
-#define HFI_VC1_LEVEL_HIGH 0x00000004
-#define HFI_VC1_LEVEL_0 0x00000008
-#define HFI_VC1_LEVEL_1 0x00000010
-#define HFI_VC1_LEVEL_2 0x00000020
-#define HFI_VC1_LEVEL_3 0x00000040
-#define HFI_VC1_LEVEL_4 0x00000080
-
#define HFI_VPX_PROFILE_SIMPLE 0x00000001
#define HFI_VPX_PROFILE_ADVANCED 0x00000002
#define HFI_VPX_PROFILE_VERSION_0 0x00000004
@@ -175,16 +128,6 @@
#define HFI_VPX_PROFILE_VERSION_2 0x00000010
#define HFI_VPX_PROFILE_VERSION_3 0x00000020
-#define HFI_DIVX_FORMAT_4 (HFI_COMMON_BASE + 0x1)
-#define HFI_DIVX_FORMAT_5 (HFI_COMMON_BASE + 0x2)
-#define HFI_DIVX_FORMAT_6 (HFI_COMMON_BASE + 0x3)
-
-#define HFI_DIVX_PROFILE_QMOBILE 0x00000001
-#define HFI_DIVX_PROFILE_MOBILE 0x00000002
-#define HFI_DIVX_PROFILE_MT 0x00000004
-#define HFI_DIVX_PROFILE_HT 0x00000008
-#define HFI_DIVX_PROFILE_HD 0x00000010
-
#define HFI_HEVC_PROFILE_MAIN 0x00000001
#define HFI_HEVC_PROFILE_MAIN10 0x00000002
#define HFI_HEVC_PROFILE_MAIN_STILL_PIC 0x00000004
@@ -267,8 +210,6 @@
(HFI_PROPERTY_PARAM_COMMON_START + 0x00B)
#define HFI_PROPERTY_PARAM_MULTI_VIEW_FORMAT \
(HFI_PROPERTY_PARAM_COMMON_START + 0x00C)
-#define HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE \
- (HFI_PROPERTY_PARAM_COMMON_START + 0x00D)
#define HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED \
(HFI_PROPERTY_PARAM_COMMON_START + 0x00E)
#define HFI_PROPERTY_PARAM_MVC_BUFFER_LAYOUT \
@@ -287,8 +228,6 @@
(HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x001)
#define HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR \
(HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x002)
-#define HFI_PROPERTY_PARAM_VDEC_NONCP_OUTPUT2 \
- (HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x003)
#define HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH \
(HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x007)
#define HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT \
@@ -310,20 +249,8 @@
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x003)
#define HFI_PROPERTY_PARAM_VENC_RATE_CONTROL \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x004)
-#define HFI_PROPERTY_PARAM_VENC_H264_PICORDER_CNT_TYPE \
- (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x005)
-#define HFI_PROPERTY_PARAM_VENC_SESSION_QP \
- (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x006)
-#define HFI_PROPERTY_PARAM_VENC_MPEG4_AC_PREDICTION \
- (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x007)
#define HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x008)
-#define HFI_PROPERTY_PARAM_VENC_MPEG4_TIME_RESOLUTION \
- (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x009)
-#define HFI_PROPERTY_PARAM_VENC_MPEG4_SHORT_HEADER \
- (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00A)
-#define HFI_PROPERTY_PARAM_VENC_MPEG4_HEADER_EXTENSION \
- (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00B)
#define HFI_PROPERTY_PARAM_VENC_OPEN_GOP \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00C)
#define HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH \
@@ -334,8 +261,6 @@
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00F)
#define HFI_PROPERTY_PARAM_VENC_QUALITY_VS_SPEED \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x010)
-#define HFI_PROPERTY_PARAM_VENC_ADVANCED \
- (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x012)
#define HFI_PROPERTY_PARAM_VENC_H264_SPS_ID \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x014)
#define HFI_PROPERTY_PARAM_VENC_H264_PPS_ID \
@@ -346,8 +271,6 @@
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x017)
#define HFI_PROPERTY_PARAM_VENC_NUMREF \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x018)
-#define HFI_PROPERTY_PARAM_VENC_MULTIREF_P \
- (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x019)
#define HFI_PROPERTY_PARAM_VENC_H264_NAL_SVC_EXT \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01B)
#define HFI_PROPERTY_PARAM_VENC_LTRMODE \
@@ -356,8 +279,6 @@
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01D)
#define HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01E)
-#define HFI_PROPERTY_PARAM_VENC_VC1_PERF_CFG \
- (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01F)
#define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x020)
#define HFI_PROPERTY_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC \
@@ -372,12 +293,8 @@
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x026)
#define HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x027)
-#define HFI_PROPERTY_PARAM_VENC_INITIAL_QP \
- (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x028)
#define HFI_PROPERTY_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x029)
-#define HFI_PROPERTY_PARAM_VENC_CONSTRAINED_INTRA_PRED \
- (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x02B)
#define HFI_PROPERTY_PARAM_VENC_HIER_B_MAX_NUM_ENH_LAYER \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x02C)
#define HFI_PROPERTY_PARAM_VENC_HIER_P_HYBRID_MODE \
@@ -401,9 +318,6 @@
(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x004)
#define HFI_PROPERTY_CONFIG_VENC_SLICE_SIZE \
(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x005)
-#define HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE \
- (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x007)
-
#define HFI_PROPERTY_PARAM_VPE_COMMON_START \
(HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x7000)
#define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER \
@@ -414,8 +328,6 @@
(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00A)
#define HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER \
(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00B)
-#define HFI_PROPERTY_CONFIG_VENC_LTRPERIOD \
- (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00C)
#define HFI_PROPERTY_CONFIG_VENC_PERF_MODE \
(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00E)
#define HFI_PROPERTY_CONFIG_VENC_BASELAYER_PRIORITYID \
@@ -528,13 +440,6 @@
struct hfi_intra_refresh {
u32 mode;
- u32 air_mbs;
- u32 air_ref;
- u32 cir_mbs;
-};
-
-struct hfi_3x_intra_refresh {
- u32 mode;
u32 mbs;
};
@@ -551,11 +456,6 @@
u32 max_num_b_frames;
};
-struct hfi_vc1e_perf_cfg_type {
- u32 search_range_x_subsampled[3];
- u32 search_range_y_subsampled[3];
-};
-
struct hfi_conceal_color {
u32 conceal_color;
};
@@ -565,24 +465,9 @@
u32 bframes;
};
-struct hfi_mpeg4_header_extension {
- u32 header_extension;
-};
-
-struct hfi_mpeg4_time_resolution {
- u32 time_increment_resolution;
-};
-
struct hfi_multi_stream {
u32 buffer_type;
u32 enable;
- u32 width;
- u32 height;
-};
-
-struct hfi_3x_multi_stream {
- u32 buffer_type;
- u32 enable;
};
struct hfi_multi_view_format {
@@ -593,7 +478,6 @@
#define HFI_MULTI_SLICE_OFF (HFI_COMMON_BASE + 0x1)
#define HFI_MULTI_SLICE_BY_MB_COUNT (HFI_COMMON_BASE + 0x2)
#define HFI_MULTI_SLICE_BY_BYTE_COUNT (HFI_COMMON_BASE + 0x3)
-#define HFI_MULTI_SLICE_GOB (HFI_COMMON_BASE + 0x4)
struct hfi_multi_slice_control {
u32 multi_slice;
@@ -634,23 +518,13 @@
};
struct hfi_quantization {
- u32 qp_i;
- u32 qp_p;
- u32 qp_b;
+ u32 qp_packed;
u32 layer_id;
};
-struct hfi_initial_quantization {
- u32 qp_i;
- u32 qp_p;
- u32 qp_b;
- u32 init_qp_enable;
-};
-
struct hfi_quantization_range {
- u32 min_qp;
- u32 max_qp;
- u32 layer_id;
+ struct hfi_quantization min_qp;
+ struct hfi_quantization max_qp;
};
#define HFI_LTR_MODE_DISABLE 0x0
@@ -726,8 +600,10 @@
#define HFI_COLOR_FORMAT_YUV444 (HFI_COMMON_BASE + 0xE)
#define HFI_COLOR_FORMAT_RGBA8888 (HFI_COMMON_BASE + 0x10)
-#define HFI_COLOR_FORMAT_YUV420_TP10 \
+#define HFI_COLOR_FORMAT_P010 \
(HFI_COLOR_FORMAT_10_BIT_BASE + HFI_COLOR_FORMAT_NV12)
+#define HFI_COLOR_FORMAT_YUV420_TP10 \
+ (HFI_COLOR_FORMAT_10_BIT_BASE + HFI_COLOR_FORMAT_NV12_4x4TILE)
#define HFI_COLOR_FORMAT_NV12_UBWC \
(HFI_COLOR_FORMAT_UBWC_BASE + HFI_COLOR_FORMAT_NV12)
@@ -802,10 +678,6 @@
u32 csc_limit[HFI_MAX_LIMIT_COEFFS];
};
-struct hfi_scs_threshold {
- u32 threshold_value;
-};
-
#define HFI_ROTATE_NONE (HFI_COMMON_BASE + 0x1)
#define HFI_ROTATE_90 (HFI_COMMON_BASE + 0x2)
#define HFI_ROTATE_180 (HFI_COMMON_BASE + 0x3)
@@ -881,10 +753,6 @@
u32 video_domains;
};
-struct hfi_seq_header_info {
- u32 max_hader_len;
-};
-
struct hfi_aspect_ratio {
u32 aspect_width;
u32 aspect_height;
@@ -1057,14 +925,6 @@
u32 rg_buffer_info[1];
};
-struct hfi_cmd_session_get_sequence_header_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 buffer_len;
- u32 packet_buffer;
-};
-
struct hfi_cmd_session_sync_process_packet {
u32 size;
u32 packet_type;
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index 0a5b8f5..27e7cf6 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -926,10 +926,11 @@
mfc_debug_enter();
if (dev)
mutex_lock(&dev->mfc_mutex);
- s5p_mfc_clock_on();
vb2_queue_release(&ctx->vq_src);
vb2_queue_release(&ctx->vq_dst);
if (dev) {
+ s5p_mfc_clock_on();
+
/* Mark context as idle */
clear_work_bit_irqsave(ctx);
/*
@@ -951,9 +952,9 @@
if (s5p_mfc_power_off() < 0)
mfc_err("Power off failed\n");
}
+ mfc_debug(2, "Shutting down clock\n");
+ s5p_mfc_clock_off();
}
- mfc_debug(2, "Shutting down clock\n");
- s5p_mfc_clock_off();
if (dev)
dev->ctx[ctx->num] = NULL;
s5p_mfc_dec_ctrls_delete(ctx);
@@ -1082,6 +1083,7 @@
idx);
if (ret == 0)
return child;
+ device_del(child);
}
put_device(child);
diff --git a/drivers/media/platform/sti/hva/hva-hw.c b/drivers/media/platform/sti/hva/hva-hw.c
index d341d49..cf2a8d8 100644
--- a/drivers/media/platform/sti/hva/hva-hw.c
+++ b/drivers/media/platform/sti/hva/hva-hw.c
@@ -305,16 +305,16 @@
/* get memory for registers */
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hva->regs = devm_ioremap_resource(dev, regs);
- if (IS_ERR_OR_NULL(hva->regs)) {
+ if (IS_ERR(hva->regs)) {
dev_err(dev, "%s failed to get regs\n", HVA_PREFIX);
return PTR_ERR(hva->regs);
}
/* get memory for esram */
esram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (IS_ERR_OR_NULL(esram)) {
+ if (!esram) {
dev_err(dev, "%s failed to get esram\n", HVA_PREFIX);
- return PTR_ERR(esram);
+ return -ENODEV;
}
hva->esram_addr = esram->start;
hva->esram_size = resource_size(esram);
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 0f30190..63165d3 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -263,6 +263,8 @@
if (allowance > ITE_RXDCR_MAX)
allowance = ITE_RXDCR_MAX;
+
+ use_demodulator = true;
}
}
diff --git a/drivers/media/spi/gs1662.c b/drivers/media/spi/gs1662.c
index d76f362..5143a90 100644
--- a/drivers/media/spi/gs1662.c
+++ b/drivers/media/spi/gs1662.c
@@ -453,10 +453,9 @@
static int gs_remove(struct spi_device *spi)
{
struct v4l2_subdev *sd = spi_get_drvdata(spi);
- struct gs *gs = to_gs(sd);
v4l2_device_unregister_subdev(sd);
- kfree(gs);
+
return 0;
}
diff --git a/drivers/media/usb/dvb-usb/dibusb-common.c b/drivers/media/usb/dvb-usb/dibusb-common.c
index de3ee25..8207e69 100644
--- a/drivers/media/usb/dvb-usb/dibusb-common.c
+++ b/drivers/media/usb/dvb-usb/dibusb-common.c
@@ -382,9 +382,9 @@
if (buf[0] != 0)
deb_info("key: %*ph\n", 5, buf);
+ret:
kfree(buf);
-ret:
return ret;
}
EXPORT_SYMBOL(dibusb_rc_query);
diff --git a/drivers/media/usb/dvb-usb/pctv452e.c b/drivers/media/usb/dvb-usb/pctv452e.c
index 07fa08b..d54ebe7 100644
--- a/drivers/media/usb/dvb-usb/pctv452e.c
+++ b/drivers/media/usb/dvb-usb/pctv452e.c
@@ -97,14 +97,13 @@
u8 c; /* transaction counter, wraps around... */
u8 initialized; /* set to 1 if 0x15 has been sent */
u16 last_rc_key;
-
- unsigned char data[80];
};
static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data,
unsigned int write_len, unsigned int read_len)
{
struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 *buf;
u8 id;
unsigned int rlen;
int ret;
@@ -114,36 +113,39 @@
return -EIO;
}
- mutex_lock(&state->ca_mutex);
+ buf = kmalloc(64, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
id = state->c++;
- state->data[0] = SYNC_BYTE_OUT;
- state->data[1] = id;
- state->data[2] = cmd;
- state->data[3] = write_len;
+ buf[0] = SYNC_BYTE_OUT;
+ buf[1] = id;
+ buf[2] = cmd;
+ buf[3] = write_len;
- memcpy(state->data + 4, data, write_len);
+ memcpy(buf + 4, data, write_len);
rlen = (read_len > 0) ? 64 : 0;
- ret = dvb_usb_generic_rw(d, state->data, 4 + write_len,
- state->data, rlen, /* delay_ms */ 0);
+ ret = dvb_usb_generic_rw(d, buf, 4 + write_len,
+ buf, rlen, /* delay_ms */ 0);
if (0 != ret)
goto failed;
ret = -EIO;
- if (SYNC_BYTE_IN != state->data[0] || id != state->data[1])
+ if (SYNC_BYTE_IN != buf[0] || id != buf[1])
goto failed;
- memcpy(data, state->data + 4, read_len);
+ memcpy(data, buf + 4, read_len);
- mutex_unlock(&state->ca_mutex);
+ kfree(buf);
return 0;
failed:
err("CI error %d; %02X %02X %02X -> %*ph.",
- ret, SYNC_BYTE_OUT, id, cmd, 3, state->data);
+ ret, SYNC_BYTE_OUT, id, cmd, 3, buf);
- mutex_unlock(&state->ca_mutex);
+ kfree(buf);
return ret;
}
@@ -410,53 +412,57 @@
u8 *rcv_buf, u8 rcv_len)
{
struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 *buf;
u8 id;
int ret;
- mutex_lock(&state->ca_mutex);
+ buf = kmalloc(64, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
id = state->c++;
ret = -EINVAL;
if (snd_len > 64 - 7 || rcv_len > 64 - 7)
goto failed;
- state->data[0] = SYNC_BYTE_OUT;
- state->data[1] = id;
- state->data[2] = PCTV_CMD_I2C;
- state->data[3] = snd_len + 3;
- state->data[4] = addr << 1;
- state->data[5] = snd_len;
- state->data[6] = rcv_len;
+ buf[0] = SYNC_BYTE_OUT;
+ buf[1] = id;
+ buf[2] = PCTV_CMD_I2C;
+ buf[3] = snd_len + 3;
+ buf[4] = addr << 1;
+ buf[5] = snd_len;
+ buf[6] = rcv_len;
- memcpy(state->data + 7, snd_buf, snd_len);
+ memcpy(buf + 7, snd_buf, snd_len);
- ret = dvb_usb_generic_rw(d, state->data, 7 + snd_len,
- state->data, /* rcv_len */ 64,
+ ret = dvb_usb_generic_rw(d, buf, 7 + snd_len,
+ buf, /* rcv_len */ 64,
/* delay_ms */ 0);
if (ret < 0)
goto failed;
/* TT USB protocol error. */
ret = -EIO;
- if (SYNC_BYTE_IN != state->data[0] || id != state->data[1])
+ if (SYNC_BYTE_IN != buf[0] || id != buf[1])
goto failed;
/* I2C device didn't respond as expected. */
ret = -EREMOTEIO;
- if (state->data[5] < snd_len || state->data[6] < rcv_len)
+ if (buf[5] < snd_len || buf[6] < rcv_len)
goto failed;
- memcpy(rcv_buf, state->data + 7, rcv_len);
- mutex_unlock(&state->ca_mutex);
+ memcpy(rcv_buf, buf + 7, rcv_len);
+ kfree(buf);
return rcv_len;
failed:
err("I2C error %d; %02X %02X %02X %02X %02X -> %*ph",
ret, SYNC_BYTE_OUT, id, addr << 1, snd_len, rcv_len,
- 7, state->data);
+ 7, buf);
- mutex_unlock(&state->ca_mutex);
+ kfree(buf);
return ret;
}
@@ -505,7 +511,7 @@
static int pctv452e_power_ctrl(struct dvb_usb_device *d, int i)
{
struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
- u8 *rx;
+ u8 *b0, *rx;
int ret;
info("%s: %d\n", __func__, i);
@@ -516,11 +522,12 @@
if (state->initialized)
return 0;
- rx = kmalloc(PCTV_ANSWER_LEN, GFP_KERNEL);
- if (!rx)
+ b0 = kmalloc(5 + PCTV_ANSWER_LEN, GFP_KERNEL);
+ if (!b0)
return -ENOMEM;
- mutex_lock(&state->ca_mutex);
+ rx = b0 + 5;
+
/* hmm where shoud this should go? */
ret = usb_set_interface(d->udev, 0, ISOC_INTERFACE_ALTERNATIVE);
if (ret != 0)
@@ -528,66 +535,70 @@
__func__, ret);
/* this is a one-time initialization, dont know where to put */
- state->data[0] = 0xaa;
- state->data[1] = state->c++;
- state->data[2] = PCTV_CMD_RESET;
- state->data[3] = 1;
- state->data[4] = 0;
+ b0[0] = 0xaa;
+ b0[1] = state->c++;
+ b0[2] = PCTV_CMD_RESET;
+ b0[3] = 1;
+ b0[4] = 0;
/* reset board */
- ret = dvb_usb_generic_rw(d, state->data, 5, rx, PCTV_ANSWER_LEN, 0);
+ ret = dvb_usb_generic_rw(d, b0, 5, rx, PCTV_ANSWER_LEN, 0);
if (ret)
goto ret;
- state->data[1] = state->c++;
- state->data[4] = 1;
+ b0[1] = state->c++;
+ b0[4] = 1;
/* reset board (again?) */
- ret = dvb_usb_generic_rw(d, state->data, 5, rx, PCTV_ANSWER_LEN, 0);
+ ret = dvb_usb_generic_rw(d, b0, 5, rx, PCTV_ANSWER_LEN, 0);
if (ret)
goto ret;
state->initialized = 1;
ret:
- mutex_unlock(&state->ca_mutex);
- kfree(rx);
+ kfree(b0);
return ret;
}
static int pctv452e_rc_query(struct dvb_usb_device *d)
{
struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 *b, *rx;
int ret, i;
u8 id;
- mutex_lock(&state->ca_mutex);
+ b = kmalloc(CMD_BUFFER_SIZE + PCTV_ANSWER_LEN, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+ rx = b + CMD_BUFFER_SIZE;
+
id = state->c++;
/* prepare command header */
- state->data[0] = SYNC_BYTE_OUT;
- state->data[1] = id;
- state->data[2] = PCTV_CMD_IR;
- state->data[3] = 0;
+ b[0] = SYNC_BYTE_OUT;
+ b[1] = id;
+ b[2] = PCTV_CMD_IR;
+ b[3] = 0;
/* send ir request */
- ret = dvb_usb_generic_rw(d, state->data, 4,
- state->data, PCTV_ANSWER_LEN, 0);
+ ret = dvb_usb_generic_rw(d, b, 4, rx, PCTV_ANSWER_LEN, 0);
if (ret != 0)
goto ret;
if (debug > 3) {
- info("%s: read: %2d: %*ph: ", __func__, ret, 3, state->data);
- for (i = 0; (i < state->data[3]) && ((i + 3) < PCTV_ANSWER_LEN); i++)
- info(" %02x", state->data[i + 3]);
+ info("%s: read: %2d: %*ph: ", __func__, ret, 3, rx);
+ for (i = 0; (i < rx[3]) && ((i+3) < PCTV_ANSWER_LEN); i++)
+ info(" %02x", rx[i+3]);
info("\n");
}
- if ((state->data[3] == 9) && (state->data[12] & 0x01)) {
+ if ((rx[3] == 9) && (rx[12] & 0x01)) {
/* got a "press" event */
- state->last_rc_key = RC_SCANCODE_RC5(state->data[7], state->data[6]);
+ state->last_rc_key = RC_SCANCODE_RC5(rx[7], rx[6]);
if (debug > 2)
info("%s: cmd=0x%02x sys=0x%02x\n",
- __func__, state->data[6], state->data[7]);
+ __func__, rx[6], rx[7]);
rc_keydown(d->rc_dev, RC_TYPE_RC5, state->last_rc_key, 0);
} else if (state->last_rc_key) {
@@ -595,7 +606,7 @@
state->last_rc_key = 0;
}
ret:
- mutex_unlock(&state->ca_mutex);
+ kfree(b);
return ret;
}
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index c52d94c..ce5a7dc 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1244,10 +1244,84 @@
case V4L2_SDR_FMT_CS8: descr = "Complex S8"; break;
case V4L2_SDR_FMT_CS14LE: descr = "Complex S14LE"; break;
case V4L2_SDR_FMT_RU12LE: descr = "Real U12LE"; break;
- case V4L2_TCH_FMT_DELTA_TD16: descr = "16-bit signed deltas"; break;
- case V4L2_TCH_FMT_DELTA_TD08: descr = "8-bit signed deltas"; break;
- case V4L2_TCH_FMT_TU16: descr = "16-bit unsigned touch data"; break;
- case V4L2_TCH_FMT_TU08: descr = "8-bit unsigned touch data"; break;
+ case V4L2_PIX_FMT_NV12_UBWC:
+ descr = "NV12 UBWC"; break;
+ case V4L2_PIX_FMT_RGBA8888_UBWC:
+ descr = "RGBA8888 UBWC"; break;
+ case V4L2_PIX_FMT_SDE_ABGR_8888:
+ descr = "32-bit ABGR 8-8-8-8"; break;
+ case V4L2_PIX_FMT_SDE_RGBA_8888:
+ descr = "32-bit RGBA 8-8-8-8"; break;
+ case V4L2_PIX_FMT_SDE_RGBX_8888:
+ descr = "32-bit RGBX 8-8-8-8"; break;
+ case V4L2_PIX_FMT_SDE_XBGR_8888:
+ descr = "32-bit XBGR 8-8-8-8"; break;
+ case V4L2_PIX_FMT_SDE_RGBA_5551:
+ descr = "16-bit RGBA 5-5-5-1"; break;
+ case V4L2_PIX_FMT_SDE_ABGR_1555:
+ descr = "16-bit ABGR 1-5-5-5"; break;
+ case V4L2_PIX_FMT_SDE_BGRA_5551:
+ descr = "16-bit BGRA 5-5-5-1"; break;
+ case V4L2_PIX_FMT_SDE_BGRX_5551:
+ descr = "16-bit BGRX 5-5-5-1"; break;
+ case V4L2_PIX_FMT_SDE_RGBX_5551:
+ descr = "16-bit RGBX 5-5-5-1"; break;
+ case V4L2_PIX_FMT_SDE_XBGR_1555:
+ descr = "16-bit XBGR 1-5-5-5"; break;
+ case V4L2_PIX_FMT_SDE_RGBA_4444:
+ descr = "16-bit RGBA 4-4-4-4"; break;
+ case V4L2_PIX_FMT_SDE_BGRA_4444:
+ descr = "16-bit BGRA 4-4-4-4"; break;
+ case V4L2_PIX_FMT_SDE_ABGR_4444:
+ descr = "16-bit ABGR 4-4-4-4"; break;
+ case V4L2_PIX_FMT_SDE_RGBX_4444:
+ descr = "16-bit RGBX 4-4-4-4"; break;
+ case V4L2_PIX_FMT_SDE_BGRX_4444:
+ descr = "16-bit BGRX 4-4-4-4"; break;
+ case V4L2_PIX_FMT_SDE_XBGR_4444:
+ descr = "16-bit XBGR 4-4-4-4"; break;
+ case V4L2_PIX_FMT_SDE_BGR_565:
+ descr = "16-bit BGR 5-6-5"; break;
+ case V4L2_PIX_FMT_SDE_Y_CR_CB_GH2V2:
+ descr = "Planar YVU 4:2:0 A16"; break;
+ case V4L2_PIX_FMT_SDE_Y_CBCR_H1V2:
+ descr = "Y/CbCr 4:2:2"; break;
+ case V4L2_PIX_FMT_SDE_Y_CRCB_H1V2:
+ descr = "Y/CrCb 4:2:2"; break;
+ case V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_VENUS:
+ descr = "Y/CbCr 4:2:0 Venus"; break;
+ case V4L2_PIX_FMT_SDE_Y_CRCB_H2V2_VENUS:
+ descr = "Y/CrCb 4:2:0 Venus"; break;
+ case V4L2_PIX_FMT_SDE_RGBX_8888_UBWC:
+ descr = "RGBX 8:8:8:8 UBWC"; break;
+ case V4L2_PIX_FMT_SDE_RGB_565_UBWC:
+ descr = "RGB 5:6:5 UBWC"; break;
+ case V4L2_PIX_FMT_SDE_RGBA_1010102:
+ descr = "RGBA 10:10:10:2"; break;
+ case V4L2_PIX_FMT_SDE_RGBX_1010102:
+ descr = "RGBX 10:10:10:2"; break;
+ case V4L2_PIX_FMT_SDE_ARGB_2101010:
+ descr = "ARGB 2:10:10:10"; break;
+ case V4L2_PIX_FMT_SDE_XRGB_2101010:
+ descr = "XRGB 2:10:10:10"; break;
+ case V4L2_PIX_FMT_SDE_BGRA_1010102:
+ descr = "BGRA 10:10:10:2"; break;
+ case V4L2_PIX_FMT_SDE_BGRX_1010102:
+ descr = "BGRX 10:10:10:2"; break;
+ case V4L2_PIX_FMT_SDE_ABGR_2101010:
+ descr = "ABGR 2:10:10:10"; break;
+ case V4L2_PIX_FMT_SDE_XBGR_2101010:
+ descr = "XBGR 2:10:10:10"; break;
+ case V4L2_PIX_FMT_SDE_RGBA_1010102_UBWC:
+ descr = "RGBA 10:10:10:2 UBWC"; break;
+ case V4L2_PIX_FMT_SDE_RGBX_1010102_UBWC:
+ descr = "RGBX 10:10:10:2 UBWC"; break;
+ case V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_TP10:
+ descr = "Y/CbCr 4:2:0 TP10"; break;
+ case V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010:
+ descr = "Y/CbCr 4:2:0 P10"; break;
+ case V4L2_PIX_FMT_NV12_TP10_UBWC:
+ descr = "Y/CbCr 4:2:0 TP10 UBWC"; break;
default:
/* Compressed formats */
diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c
index 9a4d868..df2e775 100644
--- a/drivers/mfd/tps65217.c
+++ b/drivers/mfd/tps65217.c
@@ -424,6 +424,24 @@
return 0;
}
+static int tps65217_remove(struct i2c_client *client)
+{
+ struct tps65217 *tps = i2c_get_clientdata(client);
+ unsigned int virq;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tps65217_irqs); i++) {
+ virq = irq_find_mapping(tps->irq_domain, i);
+ if (virq)
+ irq_dispose_mapping(virq);
+ }
+
+ irq_domain_remove(tps->irq_domain);
+ tps->irq_domain = NULL;
+
+ return 0;
+}
+
static const struct i2c_device_id tps65217_id_table[] = {
{"tps65217", TPS65217},
{ /* sentinel */ }
@@ -437,6 +455,7 @@
},
.id_table = tps65217_id_table,
.probe = tps65217_probe,
+ .remove = tps65217_remove,
};
static int __init tps65217_init(void)
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7ea17ec..e62b430 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -57,6 +57,9 @@
obj-y += qcom/
obj-$(CONFIG_MEMORY_STATE_TIME) += memory_state_time.o
+obj-$(CONFIG_UID_CPUTIME) += uid_cputime.o
+obj-$(CONFIG_MEMORY_STATE_TIME) += memory_state_time.o
+
lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_heap.o
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index 8cac7ef..dbe676d 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -408,7 +408,7 @@
EXPORT_SYMBOL_GPL(mei_cldev_enabled);
/**
- * mei_cldev_enable_device - enable me client device
+ * mei_cldev_enable - enable me client device
* create connection with me client
*
* @cldev: me client device
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 6fe0235..e2af61f 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -425,7 +425,7 @@
*
* @cl: host client
* @length: size of the buffer
- * @type: operation type
+ * @fop_type: operation type
* @fp: associated file pointer (might be NULL)
*
* Return: cb on success and NULL on failure
@@ -459,7 +459,7 @@
*
* @cl: host client
* @length: size of the buffer
- * @type: operation type
+ * @fop_type: operation type
* @fp: associated file pointer (might be NULL)
*
* Return: cb on success and NULL on failure
@@ -686,7 +686,7 @@
pm_runtime_mark_last_busy(dev->dev);
dev_dbg(dev->dev, "rpm: autosuspend\n");
- pm_runtime_autosuspend(dev->dev);
+ pm_request_autosuspend(dev->dev);
}
/**
@@ -1536,7 +1536,7 @@
rets = first_chunk ? mei_cl_tx_flow_ctrl_creds(cl) : 1;
if (rets < 0)
- return rets;
+ goto err;
if (rets == 0) {
cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
@@ -1570,11 +1570,8 @@
cb->buf.size, cb->buf_idx);
rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx);
- if (rets) {
- cl->status = rets;
- list_move_tail(&cb->list, &cmpl_list->list);
- return rets;
- }
+ if (rets)
+ goto err;
cl->status = 0;
cl->writing_state = MEI_WRITING;
@@ -1582,14 +1579,21 @@
cb->completed = mei_hdr.msg_complete == 1;
if (first_chunk) {
- if (mei_cl_tx_flow_ctrl_creds_reduce(cl))
- return -EIO;
+ if (mei_cl_tx_flow_ctrl_creds_reduce(cl)) {
+ rets = -EIO;
+ goto err;
+ }
}
if (mei_hdr.msg_complete)
list_move_tail(&cb->list, &dev->write_waiting_list.list);
return 0;
+
+err:
+ cl->status = rets;
+ list_move_tail(&cb->list, &cmpl_list->list);
+ return rets;
}
/**
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h
index 7ad15d6..c8307e8 100644
--- a/drivers/misc/mei/hw-me-regs.h
+++ b/drivers/misc/mei/hw-me-regs.h
@@ -122,6 +122,8 @@
#define MEI_DEV_ID_SPT_H 0xA13A /* Sunrise Point H */
#define MEI_DEV_ID_SPT_H_2 0xA13B /* Sunrise Point H 2 */
+#define MEI_DEV_ID_LBG 0xA1BA /* Lewisburg (SPT) */
+
#define MEI_DEV_ID_BXT_M 0x1A9A /* Broxton M */
#define MEI_DEV_ID_APL_I 0x5A9A /* Apollo Lake I */
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
index f3ffd88..f9c6ec4 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -87,6 +87,7 @@
{MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, mei_me_pch8_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, mei_me_pch8_sps_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, mei_me_pch8_sps_cfg)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_LBG, mei_me_pch8_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, mei_me_pch8_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_APL_I, mei_me_pch8_cfg)},
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index 3678220..df382be 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -818,7 +818,7 @@
struct mmc_async_req *cur_areq = &test_areq[0].areq;
struct mmc_async_req *other_areq = &test_areq[1].areq;
int i;
- int ret;
+ int ret = RESULT_OK;
test_areq[0].test = test;
test_areq[1].test = test;
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 60a642a..348b58b 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -453,7 +453,6 @@
{
if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
mmc_unregister_pm_notifier(host);
-
mmc_stop_host(host);
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index fa7ecd1..a0aa64e 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -223,6 +223,7 @@
static int mmc_read_ssr(struct mmc_card *card)
{
unsigned int au, es, et, eo;
+ u32 *raw_ssr;
int i;
if (!(card->csd.cmdclass & CCC_APP_SPEC)) {
@@ -231,14 +232,21 @@
return 0;
}
- if (mmc_app_sd_status(card, card->raw_ssr)) {
+ raw_ssr = kmalloc(sizeof(card->raw_ssr), GFP_KERNEL);
+ if (!raw_ssr)
+ return -ENOMEM;
+
+ if (mmc_app_sd_status(card, raw_ssr)) {
pr_warn("%s: problem reading SD Status register\n",
mmc_hostname(card->host));
+ kfree(raw_ssr);
return 0;
}
for (i = 0; i < 16; i++)
- card->raw_ssr[i] = be32_to_cpu(card->raw_ssr[i]);
+ card->raw_ssr[i] = be32_to_cpu(raw_ssr[i]);
+
+ kfree(raw_ssr);
/*
* UNSTUFF_BITS only works with four u32s so we have to offset the
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index 44ecebd..c8b8ac6 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -309,6 +309,9 @@
cmd0 = BF_SSP(cmd->opcode, CMD0_CMD);
cmd1 = cmd->arg;
+ if (cmd->opcode == MMC_STOP_TRANSMISSION)
+ cmd0 |= BM_SSP_CMD0_APPEND_8CYC;
+
if (host->sdio_irq_en) {
ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK;
cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN;
@@ -417,8 +420,7 @@
ssp->base + HW_SSP_BLOCK_SIZE);
}
- if ((cmd->opcode == MMC_STOP_TRANSMISSION) ||
- (cmd->opcode == SD_IO_RW_EXTENDED))
+ if (cmd->opcode == SD_IO_RW_EXTENDED)
cmd0 |= BM_SSP_CMD0_APPEND_8CYC;
cmd1 = cmd->arg;
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
index 81d4dc0..fddd0be 100644
--- a/drivers/mmc/host/sdhci-acpi.c
+++ b/drivers/mmc/host/sdhci-acpi.c
@@ -394,7 +394,8 @@
/* Power on the SDHCI controller and its children */
acpi_device_fix_up_power(device);
list_for_each_entry(child, &device->children, node)
- acpi_device_fix_up_power(child);
+ if (child->status.present && child->status.enabled)
+ acpi_device_fix_up_power(child);
if (acpi_bus_get_status(device) || !device->status.present)
return -ENODEV;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 42ef3eb..ba637ff 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2086,16 +2086,32 @@
if (!host->tuning_done) {
pr_info(DRIVER_NAME ": Timeout waiting for Buffer Read Ready interrupt during tuning procedure, falling back to fixed sampling clock\n");
-
- sdhci_do_reset(host, SDHCI_RESET_CMD);
- sdhci_do_reset(host, SDHCI_RESET_DATA);
-
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
ctrl &= ~SDHCI_CTRL_TUNED_CLK;
ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+ sdhci_do_reset(host, SDHCI_RESET_CMD);
+ sdhci_do_reset(host, SDHCI_RESET_DATA);
+
err = -EIO;
+
+ if (cmd.opcode != MMC_SEND_TUNING_BLOCK_HS200)
+ goto out;
+
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = MMC_STOP_TRANSMISSION;
+ cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+ cmd.busy_timeout = 50;
+ mmc_wait_for_cmd(mmc, &cmd, 0);
+
+ spin_lock_irqsave(&host->lock, flags);
+
goto out;
}
@@ -2703,7 +2719,8 @@
if (intmask & SDHCI_INT_RETUNE)
mmc_retune_needed(host->mmc);
- if (intmask & SDHCI_INT_CARD_INT) {
+ if ((intmask & SDHCI_INT_CARD_INT) &&
+ (host->ier & SDHCI_INT_CARD_INT)) {
sdhci_enable_sdio_irq_nolock(host, false);
host->thread_isr |= SDHCI_INT_CARD_INT;
result = IRQ_WAKE_THREAD;
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index bfa587d..50ee1ba 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -541,7 +541,7 @@
Flexible Static Memory Controller (FSMC)
config MTD_NAND_XWAY
- tristate "Support for NAND on Lantiq XWAY SoC"
+ bool "Support for NAND on Lantiq XWAY SoC"
depends on LANTIQ && SOC_TYPE_XWAY
help
Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c
index 8523881..bc6e49a 100644
--- a/drivers/mtd/nand/lpc32xx_mlc.c
+++ b/drivers/mtd/nand/lpc32xx_mlc.c
@@ -776,7 +776,7 @@
init_completion(&host->comp_controller);
host->irq = platform_get_irq(pdev, 0);
- if ((host->irq < 0) || (host->irq >= NR_IRQS)) {
+ if (host->irq < 0) {
dev_err(&pdev->dev, "failed to get platform irq\n");
res = -EINVAL;
goto err_exit3;
diff --git a/drivers/mtd/nand/xway_nand.c b/drivers/mtd/nand/xway_nand.c
index 1f2948c..895101a 100644
--- a/drivers/mtd/nand/xway_nand.c
+++ b/drivers/mtd/nand/xway_nand.c
@@ -232,7 +232,6 @@
{ .compatible = "lantiq,nand-xway" },
{},
};
-MODULE_DEVICE_TABLE(of, xway_nand_match);
static struct platform_driver xway_nand_driver = {
.probe = xway_nand_probe,
@@ -243,6 +242,4 @@
},
};
-module_platform_driver(xway_nand_driver);
-
-MODULE_LICENSE("GPL");
+builtin_platform_driver(xway_nand_driver);
diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
index d403ba7..d489fbd 100644
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
@@ -1077,12 +1077,14 @@
/* Get flash device data */
for_each_available_child_of_node(dev->of_node, np) {
- if (of_property_read_u32(np, "reg", &cs)) {
+ ret = of_property_read_u32(np, "reg", &cs);
+ if (ret) {
dev_err(dev, "Couldn't determine chip select.\n");
goto err;
}
- if (cs > CQSPI_MAX_CHIPSELECT) {
+ if (cs >= CQSPI_MAX_CHIPSELECT) {
+ ret = -EINVAL;
dev_err(dev, "Chip select %d out of range.\n", cs);
goto err;
}
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 95c32f2..a09d3d4 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -246,6 +246,16 @@
tristate "Virtual Ethernet over NTB Transport"
depends on NTB_TRANSPORT
+config RMNET_IPLO
+ tristate "RMNET IPLO driver"
+ ---help---
+ This allows the creation of the rmnet ip loopback driver which is
+ used for testing rmnet_data control and data path.
+
+ rmnet_iplo supports most of the IOCTL's which will be defined by
+ a physical device which uses rmnet_data. rmnet_iplo will
+ loopback any data which is sent to it.
+
config RIONET
tristate "RapidIO Ethernet over messaging driver support"
depends on RAPIDIO
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 7336cbd..4e9ad07 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -19,6 +19,7 @@
obj-$(CONFIG_NETCONSOLE) += netconsole.o
obj-$(CONFIG_PHYLIB) += phy/
obj-$(CONFIG_RIONET) += rionet.o
+obj-$(CONFIG_RMNET_IPLO) += rmnet_iplo.o
obj-$(CONFIG_NET_TEAM) += team/
obj-$(CONFIG_TUN) += tun.o
obj-$(CONFIG_VETH) += veth.o
diff --git a/drivers/net/can/c_can/c_can_pci.c b/drivers/net/can/c_can/c_can_pci.c
index 7be393c..cf7c189 100644
--- a/drivers/net/can/c_can/c_can_pci.c
+++ b/drivers/net/can/c_can/c_can_pci.c
@@ -161,6 +161,7 @@
dev->irq = pdev->irq;
priv->base = addr;
+ priv->device = &pdev->dev;
if (!c_can_pci_data->freq) {
dev_err(&pdev->dev, "no clock frequency defined\n");
diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
index 680d1ff..6749b18 100644
--- a/drivers/net/can/ti_hecc.c
+++ b/drivers/net/can/ti_hecc.c
@@ -948,7 +948,12 @@
netif_napi_add(ndev, &priv->napi, ti_hecc_rx_poll,
HECC_DEF_NAPI_WEIGHT);
- clk_enable(priv->clk);
+ err = clk_prepare_enable(priv->clk);
+ if (err) {
+ dev_err(&pdev->dev, "clk_prepare_enable() failed\n");
+ goto probe_exit_clk;
+ }
+
err = register_candev(ndev);
if (err) {
dev_err(&pdev->dev, "register_candev() failed\n");
@@ -981,7 +986,7 @@
struct ti_hecc_priv *priv = netdev_priv(ndev);
unregister_candev(ndev);
- clk_disable(priv->clk);
+ clk_disable_unprepare(priv->clk);
clk_put(priv->clk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
iounmap(priv->base);
@@ -1006,7 +1011,7 @@
hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_PDR);
priv->can.state = CAN_STATE_SLEEPING;
- clk_disable(priv->clk);
+ clk_disable_unprepare(priv->clk);
return 0;
}
@@ -1015,8 +1020,11 @@
{
struct net_device *dev = platform_get_drvdata(pdev);
struct ti_hecc_priv *priv = netdev_priv(dev);
+ int err;
- clk_enable(priv->clk);
+ err = clk_prepare_enable(priv->clk);
+ if (err)
+ return err;
hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_PDR);
priv->can.state = CAN_STATE_ERROR_ACTIVE;
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index 9ec33b5..2ce7ae9 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -393,7 +393,7 @@
if (addr == BRCM_PSEUDO_PHY_ADDR && priv->indir_phy_mask & BIT(addr))
return bcm_sf2_sw_indir_rw(priv, 1, addr, regnum, 0);
else
- return mdiobus_read(priv->master_mii_bus, addr, regnum);
+ return mdiobus_read_nested(priv->master_mii_bus, addr, regnum);
}
static int bcm_sf2_sw_mdio_write(struct mii_bus *bus, int addr, int regnum,
@@ -407,7 +407,7 @@
if (addr == BRCM_PSEUDO_PHY_ADDR && priv->indir_phy_mask & BIT(addr))
bcm_sf2_sw_indir_rw(priv, 0, addr, regnum, val);
else
- mdiobus_write(priv->master_mii_bus, addr, regnum, val);
+ mdiobus_write_nested(priv->master_mii_bus, addr, regnum, val);
return 0;
}
@@ -982,6 +982,7 @@
const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME;
struct device_node *dn = pdev->dev.of_node;
struct b53_platform_data *pdata;
+ struct dsa_switch_ops *ops;
struct bcm_sf2_priv *priv;
struct b53_device *dev;
struct dsa_switch *ds;
@@ -995,6 +996,10 @@
if (!priv)
return -ENOMEM;
+ ops = devm_kzalloc(&pdev->dev, sizeof(*ops), GFP_KERNEL);
+ if (!ops)
+ return -ENOMEM;
+
dev = b53_switch_alloc(&pdev->dev, &bcm_sf2_io_ops, priv);
if (!dev)
return -ENOMEM;
@@ -1014,6 +1019,8 @@
ds = dev->ds;
/* Override the parts that are non-standard wrt. normal b53 devices */
+ memcpy(ops, ds->ops, sizeof(*ops));
+ ds->ops = ops;
ds->ops->get_tag_protocol = bcm_sf2_sw_get_tag_protocol;
ds->ops->setup = bcm_sf2_sw_setup;
ds->ops->get_phy_flags = bcm_sf2_sw_get_phy_flags;
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c
index 25d1eb4..be7ec5a 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.c
+++ b/drivers/net/ethernet/broadcom/bcmsysport.c
@@ -710,11 +710,8 @@
unsigned int c_index, last_c_index, last_tx_cn, num_tx_cbs;
unsigned int pkts_compl = 0, bytes_compl = 0;
struct bcm_sysport_cb *cb;
- struct netdev_queue *txq;
u32 hw_ind;
- txq = netdev_get_tx_queue(ndev, ring->index);
-
/* Compute how many descriptors have been processed since last call */
hw_ind = tdma_readl(priv, TDMA_DESC_RING_PROD_CONS_INDEX(ring->index));
c_index = (hw_ind >> RING_CONS_INDEX_SHIFT) & RING_CONS_INDEX_MASK;
@@ -745,9 +742,6 @@
ring->c_index = c_index;
- if (netif_tx_queue_stopped(txq) && pkts_compl)
- netif_tx_wake_queue(txq);
-
netif_dbg(priv, tx_done, ndev,
"ring=%d c_index=%d pkts_compl=%d, bytes_compl=%d\n",
ring->index, ring->c_index, pkts_compl, bytes_compl);
@@ -759,16 +753,33 @@
static unsigned int bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv,
struct bcm_sysport_tx_ring *ring)
{
+ struct netdev_queue *txq;
unsigned int released;
unsigned long flags;
+ txq = netdev_get_tx_queue(priv->netdev, ring->index);
+
spin_lock_irqsave(&ring->lock, flags);
released = __bcm_sysport_tx_reclaim(priv, ring);
+ if (released)
+ netif_tx_wake_queue(txq);
+
spin_unlock_irqrestore(&ring->lock, flags);
return released;
}
+/* Locked version of the per-ring TX reclaim, but does not wake the queue */
+static void bcm_sysport_tx_clean(struct bcm_sysport_priv *priv,
+ struct bcm_sysport_tx_ring *ring)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ring->lock, flags);
+ __bcm_sysport_tx_reclaim(priv, ring);
+ spin_unlock_irqrestore(&ring->lock, flags);
+}
+
static int bcm_sysport_tx_poll(struct napi_struct *napi, int budget)
{
struct bcm_sysport_tx_ring *ring =
@@ -1253,7 +1264,7 @@
napi_disable(&ring->napi);
netif_napi_del(&ring->napi);
- bcm_sysport_tx_reclaim(priv, ring);
+ bcm_sysport_tx_clean(priv, ring);
kfree(ring->cbs);
ring->cbs = NULL;
diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index 1026c45..930c816 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -770,6 +770,17 @@
u32 reserved8;
};
+struct mvpp2_txq_pcpu_buf {
+ /* Transmitted SKB */
+ struct sk_buff *skb;
+
+ /* Physical address of transmitted buffer */
+ dma_addr_t phys;
+
+ /* Size transmitted */
+ size_t size;
+};
+
/* Per-CPU Tx queue control */
struct mvpp2_txq_pcpu {
int cpu;
@@ -785,11 +796,8 @@
/* Number of Tx DMA descriptors reserved for each CPU */
int reserved_num;
- /* Array of transmitted skb */
- struct sk_buff **tx_skb;
-
- /* Array of transmitted buffers' physical addresses */
- dma_addr_t *tx_buffs;
+ /* Infos about transmitted buffers */
+ struct mvpp2_txq_pcpu_buf *buffs;
/* Index of last TX DMA descriptor that was inserted */
int txq_put_index;
@@ -979,10 +987,11 @@
struct sk_buff *skb,
struct mvpp2_tx_desc *tx_desc)
{
- txq_pcpu->tx_skb[txq_pcpu->txq_put_index] = skb;
- if (skb)
- txq_pcpu->tx_buffs[txq_pcpu->txq_put_index] =
- tx_desc->buf_phys_addr;
+ struct mvpp2_txq_pcpu_buf *tx_buf =
+ txq_pcpu->buffs + txq_pcpu->txq_put_index;
+ tx_buf->skb = skb;
+ tx_buf->size = tx_desc->data_size;
+ tx_buf->phys = tx_desc->buf_phys_addr;
txq_pcpu->txq_put_index++;
if (txq_pcpu->txq_put_index == txq_pcpu->size)
txq_pcpu->txq_put_index = 0;
@@ -4401,17 +4410,16 @@
int i;
for (i = 0; i < num; i++) {
- dma_addr_t buf_phys_addr =
- txq_pcpu->tx_buffs[txq_pcpu->txq_get_index];
- struct sk_buff *skb = txq_pcpu->tx_skb[txq_pcpu->txq_get_index];
+ struct mvpp2_txq_pcpu_buf *tx_buf =
+ txq_pcpu->buffs + txq_pcpu->txq_get_index;
mvpp2_txq_inc_get(txq_pcpu);
- dma_unmap_single(port->dev->dev.parent, buf_phys_addr,
- skb_headlen(skb), DMA_TO_DEVICE);
- if (!skb)
+ dma_unmap_single(port->dev->dev.parent, tx_buf->phys,
+ tx_buf->size, DMA_TO_DEVICE);
+ if (!tx_buf->skb)
continue;
- dev_kfree_skb_any(skb);
+ dev_kfree_skb_any(tx_buf->skb);
}
}
@@ -4651,15 +4659,10 @@
for_each_present_cpu(cpu) {
txq_pcpu = per_cpu_ptr(txq->pcpu, cpu);
txq_pcpu->size = txq->size;
- txq_pcpu->tx_skb = kmalloc(txq_pcpu->size *
- sizeof(*txq_pcpu->tx_skb),
- GFP_KERNEL);
- if (!txq_pcpu->tx_skb)
- goto error;
-
- txq_pcpu->tx_buffs = kmalloc(txq_pcpu->size *
- sizeof(dma_addr_t), GFP_KERNEL);
- if (!txq_pcpu->tx_buffs)
+ txq_pcpu->buffs = kmalloc(txq_pcpu->size *
+ sizeof(struct mvpp2_txq_pcpu_buf),
+ GFP_KERNEL);
+ if (!txq_pcpu->buffs)
goto error;
txq_pcpu->count = 0;
@@ -4673,8 +4676,7 @@
error:
for_each_present_cpu(cpu) {
txq_pcpu = per_cpu_ptr(txq->pcpu, cpu);
- kfree(txq_pcpu->tx_skb);
- kfree(txq_pcpu->tx_buffs);
+ kfree(txq_pcpu->buffs);
}
dma_free_coherent(port->dev->dev.parent,
@@ -4693,8 +4695,7 @@
for_each_present_cpu(cpu) {
txq_pcpu = per_cpu_ptr(txq->pcpu, cpu);
- kfree(txq_pcpu->tx_skb);
- kfree(txq_pcpu->tx_buffs);
+ kfree(txq_pcpu->buffs);
}
if (txq->descs)
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index fb8bb02..d223e7c 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -1740,8 +1740,11 @@
/* Process all completions if exist to prevent
* the queues freezing if they are full
*/
- for (i = 0; i < priv->rx_ring_num; i++)
+ for (i = 0; i < priv->rx_ring_num; i++) {
+ local_bh_disable();
napi_schedule(&priv->rx_cq[i]->napi);
+ local_bh_enable();
+ }
netif_tx_start_all_queues(dev);
netif_device_attach(dev);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c
index d17c242..90e81ae 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c
@@ -247,6 +247,7 @@
}
if (fs->flow_type & FLOW_MAC_EXT &&
!is_zero_ether_addr(fs->m_ext.h_dest)) {
+ mask_spec(fs->m_ext.h_dest, fs->h_ext.h_dest, ETH_ALEN);
ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4,
outer_headers_c, dmac_47_16),
fs->m_ext.h_dest);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 246d98e..5dc3e24 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -3773,14 +3773,7 @@
mlx5_lag_add(mdev, netdev);
- if (mlx5e_vxlan_allowed(mdev)) {
- rtnl_lock();
- udp_tunnel_get_rx_info(netdev);
- rtnl_unlock();
- }
-
mlx5e_enable_async_events(priv);
- queue_work(priv->wq, &priv->set_rx_mode_work);
if (MLX5_CAP_GEN(mdev, vport_group_manager)) {
mlx5_query_nic_vport_mac_address(mdev, 0, rep.hw_id);
@@ -3790,6 +3783,18 @@
rep.priv_data = priv;
mlx5_eswitch_register_vport_rep(esw, 0, &rep);
}
+
+ if (netdev->reg_state != NETREG_REGISTERED)
+ return;
+
+ /* Device already registered: sync netdev system state */
+ if (mlx5e_vxlan_allowed(mdev)) {
+ rtnl_lock();
+ udp_tunnel_get_rx_info(netdev);
+ rtnl_unlock();
+ }
+
+ queue_work(priv->wq, &priv->set_rx_mode_work);
}
static void mlx5e_nic_disable(struct mlx5e_priv *priv)
@@ -3937,10 +3942,6 @@
const struct mlx5e_profile *profile = priv->profile;
set_bit(MLX5E_STATE_DESTROYING, &priv->state);
- if (profile->disable)
- profile->disable(priv);
-
- flush_workqueue(priv->wq);
rtnl_lock();
if (netif_running(netdev))
@@ -3948,6 +3949,10 @@
netif_device_detach(netdev);
rtnl_unlock();
+ if (profile->disable)
+ profile->disable(priv);
+ flush_workqueue(priv->wq);
+
mlx5e_destroy_q_counter(priv);
profile->cleanup_rx(priv);
mlx5e_close_drop_rq(priv);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index 33495d8..e7b2158 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -193,6 +193,9 @@
return false;
}
+ if (unlikely(page_is_pfmemalloc(dma_info->page)))
+ return false;
+
cache->page_cache[cache->tail] = *dma_info;
cache->tail = tail_next;
return true;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx_am.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx_am.c
index 1fffe48..cbfac06 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx_am.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx_am.c
@@ -109,7 +109,6 @@
switch (am->tune_state) {
case MLX5E_AM_PARKING_ON_TOP:
case MLX5E_AM_PARKING_TIRED:
- WARN_ONCE(true, "mlx5e_am_on_top: PARKING\n");
return true;
case MLX5E_AM_GOING_RIGHT:
return (am->steps_left > 1) && (am->steps_right == 1);
@@ -123,7 +122,6 @@
switch (am->tune_state) {
case MLX5E_AM_PARKING_ON_TOP:
case MLX5E_AM_PARKING_TIRED:
- WARN_ONCE(true, "mlx5e_am_turn: PARKING\n");
break;
case MLX5E_AM_GOING_RIGHT:
am->tune_state = MLX5E_AM_GOING_LEFT;
@@ -144,7 +142,6 @@
switch (am->tune_state) {
case MLX5E_AM_PARKING_ON_TOP:
case MLX5E_AM_PARKING_TIRED:
- WARN_ONCE(true, "mlx5e_am_step: PARKING\n");
break;
case MLX5E_AM_GOING_RIGHT:
if (am->profile_ix == (MLX5E_PARAMS_AM_NUM_PROFILES - 1))
@@ -282,10 +279,8 @@
u32 delta_us = ktime_us_delta(end->time, start->time);
unsigned int npkts = end->pkt_ctr - start->pkt_ctr;
- if (!delta_us) {
- WARN_ONCE(true, "mlx5e_am_calc_stats: delta_us=0\n");
+ if (!delta_us)
return;
- }
curr_stats->ppms = (npkts * USEC_PER_MSEC) / delta_us;
curr_stats->epms = (MLX5E_AM_NEVENTS * USEC_PER_MSEC) / delta_us;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
index be1f733..c7011ef 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
@@ -1703,7 +1703,7 @@
if (!ESW_ALLOWED(esw))
return -EPERM;
- if (!LEGAL_VPORT(esw, vport))
+ if (!LEGAL_VPORT(esw, vport) || is_multicast_ether_addr(mac))
return -EINVAL;
mutex_lock(&esw->state_lock);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index ada24e1..0c9ef87 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -468,6 +468,13 @@
MLX5_SET(cmd_hca_cap, set_hca_cap, pkey_table_size,
to_fw_pkey_sz(dev, 128));
+ /* Check log_max_qp from HCA caps to set in current profile */
+ if (MLX5_CAP_GEN_MAX(dev, log_max_qp) < profile[prof_sel].log_max_qp) {
+ mlx5_core_warn(dev, "log_max_qp value in current profile is %d, changing it to HCA capability limit (%d)\n",
+ profile[prof_sel].log_max_qp,
+ MLX5_CAP_GEN_MAX(dev, log_max_qp));
+ profile[prof_sel].log_max_qp = MLX5_CAP_GEN_MAX(dev, log_max_qp);
+ }
if (prof->mask & MLX5_PROF_MASK_QP_SIZE)
MLX5_SET(cmd_hca_cap, set_hca_cap, log_max_qp,
prof->log_max_qp);
@@ -540,7 +547,6 @@
struct mlx5_priv *priv = &mdev->priv;
struct msix_entry *msix = priv->msix_arr;
int irq = msix[i + MLX5_EQ_VEC_COMP_BASE].vector;
- int numa_node = priv->numa_node;
int err;
if (!zalloc_cpumask_var(&priv->irq_info[i].mask, GFP_KERNEL)) {
@@ -548,7 +554,7 @@
return -ENOMEM;
}
- cpumask_set_cpu(cpumask_local_spread(i, numa_node),
+ cpumask_set_cpu(cpumask_local_spread(i, priv->numa_node),
priv->irq_info[i].mask);
err = irq_set_affinity_hint(irq, priv->irq_info[i].mask);
@@ -1152,6 +1158,9 @@
{
int err = 0;
+ if (cleanup)
+ mlx5_drain_health_wq(dev);
+
mutex_lock(&dev->intf_state_mutex);
if (test_bit(MLX5_INTERFACE_STATE_DOWN, &dev->intf_state)) {
dev_warn(&dev->pdev->dev, "%s: interface is down, NOP\n",
@@ -1312,7 +1321,7 @@
mlx5_enter_error_state(dev);
mlx5_unload_one(dev, priv, false);
- /* In case of kernel call save the pci state and drain health wq */
+ /* In case of kernel call save the pci state and drain the health wq */
if (state) {
pci_save_state(pdev);
mlx5_drain_health_wq(dev);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.h b/drivers/net/ethernet/mellanox/mlxsw/pci.h
index d942a3e..846fd4d 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/pci.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/pci.h
@@ -211,21 +211,21 @@
/* pci_eqe_cmd_token
* Command completion event - token
*/
-MLXSW_ITEM32(pci, eqe, cmd_token, 0x08, 16, 16);
+MLXSW_ITEM32(pci, eqe, cmd_token, 0x00, 16, 16);
/* pci_eqe_cmd_status
* Command completion event - status
*/
-MLXSW_ITEM32(pci, eqe, cmd_status, 0x08, 0, 8);
+MLXSW_ITEM32(pci, eqe, cmd_status, 0x00, 0, 8);
/* pci_eqe_cmd_out_param_h
* Command completion event - output parameter - higher part
*/
-MLXSW_ITEM32(pci, eqe, cmd_out_param_h, 0x0C, 0, 32);
+MLXSW_ITEM32(pci, eqe, cmd_out_param_h, 0x04, 0, 32);
/* pci_eqe_cmd_out_param_l
* Command completion event - output parameter - lower part
*/
-MLXSW_ITEM32(pci, eqe, cmd_out_param_l, 0x10, 0, 32);
+MLXSW_ITEM32(pci, eqe, cmd_out_param_l, 0x08, 0, 32);
#endif
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index dda5761..f902c4d 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -684,6 +684,7 @@
dev_kfree_skb_any(skb_orig);
return NETDEV_TX_OK;
}
+ dev_consume_skb_any(skb_orig);
}
if (eth_skb_pad(skb)) {
diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
index 92bda87..d548f0a 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
@@ -314,6 +314,7 @@
dev_kfree_skb_any(skb_orig);
return NETDEV_TX_OK;
}
+ dev_consume_skb_any(skb_orig);
}
mlxsw_sx_txhdr_construct(skb, &tx_info);
/* TX header is consumed by HW on the way so we shouldn't count its
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
index d6a2178..862f18e 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -1508,6 +1508,19 @@
buffer = PTR_ALIGN(priv->tx_align[q], DPTR_ALIGN) +
entry / NUM_TX_DESC * DPTR_ALIGN;
len = PTR_ALIGN(skb->data, DPTR_ALIGN) - skb->data;
+ /* Zero length DMA descriptors are problematic as they seem to
+ * terminate DMA transfers. Avoid them by simply using a length of
+ * DPTR_ALIGN (4) when skb data is aligned to DPTR_ALIGN.
+ *
+ * As skb is guaranteed to have at least ETH_ZLEN (60) bytes of
+ * data by the call to skb_put_padto() above this is safe with
+ * respect to both the length of the first DMA descriptor (len)
+ * overflowing the available data and the length of the second DMA
+ * descriptor (skb->len - len) being negative.
+ */
+ if (len == 0)
+ len = DPTR_ALIGN;
+
memcpy(buffer, skb->data, len);
dma_addr = dma_map_single(ndev->dev.parent, buffer, len, DMA_TO_DEVICE);
if (dma_mapping_error(ndev->dev.parent, dma_addr))
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index caf069a..b2893fb 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -3349,12 +3349,6 @@
spin_lock_init(&priv->lock);
spin_lock_init(&priv->tx_lock);
- ret = register_netdev(ndev);
- if (ret) {
- pr_err("%s: ERROR %i registering the device\n", __func__, ret);
- goto error_netdev_register;
- }
-
/* If a specific clk_csr value is passed from the platform
* this means that the CSR Clock Range selection cannot be
* changed at run-time and it is fixed. Viceversa the driver'll try to
@@ -3376,15 +3370,24 @@
if (ret < 0) {
pr_debug("%s: MDIO bus (id: %d) registration failed",
__func__, priv->plat->bus_id);
- goto error_mdio_register;
+ goto error_napi_register;
}
}
- return 0;
+ ret = register_netdev(ndev);
+ if (ret) {
+ pr_err("%s: ERROR %i registering the device\n", __func__, ret);
+ goto error_netdev_register;
+ }
-error_mdio_register:
- unregister_netdev(ndev);
+ return ret;
+
error_netdev_register:
+ if (priv->hw->pcs != STMMAC_PCS_RGMII &&
+ priv->hw->pcs != STMMAC_PCS_TBI &&
+ priv->hw->pcs != STMMAC_PCS_RTBI)
+ stmmac_mdio_unregister(ndev);
+error_napi_register:
netif_napi_del(&priv->napi);
error_hw_init:
clk_disable_unprepare(priv->pclk);
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index c9140c3..ff038e5 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -659,6 +659,7 @@
* policy filters on the host). Deliver these via the VF
* interface in the guest.
*/
+ rcu_read_lock();
vf_netdev = rcu_dereference(net_device_ctx->vf_netdev);
if (vf_netdev && (vf_netdev->flags & IFF_UP))
net = vf_netdev;
@@ -667,6 +668,7 @@
skb = netvsc_alloc_recv_skb(net, packet, csum_info, *data, vlan_tci);
if (unlikely(!skb)) {
++net->stats.rx_dropped;
+ rcu_read_unlock();
return NVSP_STAT_FAIL;
}
@@ -696,6 +698,7 @@
* TODO - use NAPI?
*/
netif_rx(skb);
+ rcu_read_unlock();
return 0;
}
diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c
index 1056ed1..f186e04 100644
--- a/drivers/net/ieee802154/atusb.c
+++ b/drivers/net/ieee802154/atusb.c
@@ -112,13 +112,26 @@
{
struct usb_device *usb_dev = atusb->usb_dev;
int ret;
+ uint8_t *buffer;
uint8_t value;
+ buffer = kmalloc(1, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
dev_dbg(&usb_dev->dev, "atusb: reg = 0x%x\n", reg);
ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0),
ATUSB_REG_READ, ATUSB_REQ_FROM_DEV,
- 0, reg, &value, 1, 1000);
- return ret >= 0 ? value : ret;
+ 0, reg, buffer, 1, 1000);
+
+ if (ret >= 0) {
+ value = buffer[0];
+ kfree(buffer);
+ return value;
+ } else {
+ kfree(buffer);
+ return ret;
+ }
}
static int atusb_write_subreg(struct atusb *atusb, uint8_t reg, uint8_t mask,
@@ -587,9 +600,13 @@
static int atusb_get_and_show_revision(struct atusb *atusb)
{
struct usb_device *usb_dev = atusb->usb_dev;
- unsigned char buffer[3];
+ unsigned char *buffer;
int ret;
+ buffer = kmalloc(3, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
/* Get a couple of the ATMega Firmware values */
ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0),
ATUSB_ID, ATUSB_REQ_FROM_DEV, 0, 0,
@@ -605,15 +622,20 @@
dev_info(&usb_dev->dev, "Please update to version 0.2 or newer");
}
+ kfree(buffer);
return ret;
}
static int atusb_get_and_show_build(struct atusb *atusb)
{
struct usb_device *usb_dev = atusb->usb_dev;
- char build[ATUSB_BUILD_SIZE + 1];
+ char *build;
int ret;
+ build = kmalloc(ATUSB_BUILD_SIZE + 1, GFP_KERNEL);
+ if (!build)
+ return -ENOMEM;
+
ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0),
ATUSB_BUILD, ATUSB_REQ_FROM_DEV, 0, 0,
build, ATUSB_BUILD_SIZE, 1000);
@@ -622,6 +644,7 @@
dev_info(&usb_dev->dev, "Firmware: build %s\n", build);
}
+ kfree(build);
return ret;
}
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index 7869b06..6f38daf 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -827,7 +827,7 @@
return -EINVAL;
ret = virtio_net_hdr_from_skb(skb, &vnet_hdr,
- macvtap_is_little_endian(q));
+ macvtap_is_little_endian(q), true);
if (ret)
BUG();
diff --git a/drivers/net/phy/bcm63xx.c b/drivers/net/phy/bcm63xx.c
index e741bf6..b0492ef 100644
--- a/drivers/net/phy/bcm63xx.c
+++ b/drivers/net/phy/bcm63xx.c
@@ -21,6 +21,23 @@
MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>");
MODULE_LICENSE("GPL");
+static int bcm63xx_config_intr(struct phy_device *phydev)
+{
+ int reg, err;
+
+ reg = phy_read(phydev, MII_BCM63XX_IR);
+ if (reg < 0)
+ return reg;
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ reg &= ~MII_BCM63XX_IR_GMASK;
+ else
+ reg |= MII_BCM63XX_IR_GMASK;
+
+ err = phy_write(phydev, MII_BCM63XX_IR, reg);
+ return err;
+}
+
static int bcm63xx_config_init(struct phy_device *phydev)
{
int reg, err;
@@ -55,7 +72,7 @@
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
- .config_intr = bcm_phy_config_intr,
+ .config_intr = bcm63xx_config_intr,
}, {
/* same phy as above, with just a different OUI */
.phy_id = 0x002bdc00,
@@ -67,7 +84,7 @@
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
- .config_intr = bcm_phy_config_intr,
+ .config_intr = bcm63xx_config_intr,
} };
module_phy_driver(bcm63xx_driver);
diff --git a/drivers/net/rmnet_iplo.c b/drivers/net/rmnet_iplo.c
new file mode 100644
index 0000000..8eb2251
--- /dev/null
+++ b/drivers/net/rmnet_iplo.c
@@ -0,0 +1,232 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/rtnetlink.h>
+#include <linux/ip.h>
+#include <linux/icmp.h>
+#include <linux/msm_rmnet.h>
+
+unsigned int multiplication_factor = 1;
+module_param(multiplication_factor, uint, 0644);
+
+struct net_device *mydevice;
+
+static void iplo_do_ip_loopback(struct sk_buff *skb, int ip_offset)
+{
+ struct iphdr *hdr;
+ struct icmphdr *icmp;
+ __be32 ipaddr;
+ int i;
+
+ hdr = (struct iphdr *)(skb->data + ip_offset);
+ ipaddr = hdr->saddr;
+ hdr->saddr = hdr->daddr;
+ hdr->daddr = ipaddr;
+ switch (hdr->protocol) {
+ case 1: /* ICMP */
+ icmp = (struct icmphdr *)(skb->data + ip_offset + hdr->ihl * 4);
+ if (icmp->type == ICMP_ECHO)
+ icmp->type = ICMP_ECHOREPLY;
+ break;
+ case 11: /* UDP */
+ break;
+ case 6: /* TCP */
+ break;
+ default:
+ break;
+ }
+ if (multiplication_factor < 2) {
+ netif_rx(skb);
+ skb->dev->stats.tx_packets++;
+ } else {
+ for (i = 0; i < (multiplication_factor - 1); i++) {
+ netif_rx(skb_copy(skb, GFP_ATOMIC));
+ skb->dev->stats.tx_packets++;
+ }
+ netif_rx(skb);
+ skb->dev->stats.tx_packets++;
+ }
+}
+
+static netdev_tx_t iplo_vnd_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ int ip_offset = 0;
+
+ switch (ntohs(skb->protocol)) {
+ case ETH_P_MAP:
+ ip_offset = 4;
+ case ETH_P_IP:
+ iplo_do_ip_loopback(skb, ip_offset);
+ dev->stats.rx_packets++;
+ break;
+ default:
+ dev->stats.tx_dropped++;
+ kfree_skb(skb);
+ break;
+ }
+ return NETDEV_TX_OK;
+}
+
+static int iplo_vnd_ioctl_extended(struct net_device *dev, struct ifreq *ifr)
+{
+ struct rmnet_ioctl_extended_s ext_cmd;
+ int rc = 0;
+
+ rc = copy_from_user(&ext_cmd, ifr->ifr_ifru.ifru_data,
+ sizeof(struct rmnet_ioctl_extended_s));
+
+ if (rc) {
+ pr_err("%s() copy_from_user failed, error %d\n", __func__, rc);
+ return rc;
+ }
+
+ switch (ext_cmd.extended_ioctl) {
+ case RMNET_IOCTL_SET_MRU:
+ break;
+ case RMNET_IOCTL_GET_EPID:
+ ext_cmd.u.data = 100;
+ break;
+ case RMNET_IOCTL_GET_SUPPORTED_FEATURES:
+ ext_cmd.u.data = 0;
+ break;
+ case RMNET_IOCTL_GET_DRIVER_NAME:
+ strlcpy(ext_cmd.u.if_name, "rmnet_mhi",
+ sizeof(ext_cmd.u.if_name));
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ rc = copy_to_user(ifr->ifr_ifru.ifru_data, &ext_cmd,
+ sizeof(struct rmnet_ioctl_extended_s));
+
+ if (rc)
+ pr_err("%s() copy_to_user failed, error %d\n", __func__, rc);
+
+ return rc;
+}
+
+static int iplo_vnd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ int rc = 0;
+
+ struct rmnet_ioctl_data_s ioctl_data;
+
+ switch (cmd) {
+ case RMNET_IOCTL_SET_LLP_IP: /* Set RAWIP protocol */
+ break;
+ case RMNET_IOCTL_GET_LLP: /* Get link protocol state */
+ ioctl_data.u.operation_mode = RMNET_MODE_LLP_IP;
+ if (copy_to_user(ifr->ifr_ifru.ifru_data, &ioctl_data,
+ sizeof(struct rmnet_ioctl_data_s)))
+ rc = -EFAULT;
+ break;
+ case RMNET_IOCTL_GET_OPMODE: /* Get operation mode */
+ ioctl_data.u.operation_mode = RMNET_MODE_LLP_IP;
+ if (copy_to_user(ifr->ifr_ifru.ifru_data, &ioctl_data,
+ sizeof(struct rmnet_ioctl_data_s)))
+ rc = -EFAULT;
+ break;
+ case RMNET_IOCTL_SET_QOS_ENABLE:
+ rc = -EINVAL;
+ break;
+ case RMNET_IOCTL_SET_QOS_DISABLE:
+ rc = 0;
+ break;
+ case RMNET_IOCTL_OPEN:
+ case RMNET_IOCTL_CLOSE:
+ /* We just ignore them and return success */
+ rc = 0;
+ break;
+ case RMNET_IOCTL_EXTENDED:
+ rc = iplo_vnd_ioctl_extended(dev, ifr);
+ break;
+ default:
+ /* Don't fail any IOCTL right now */
+ rc = 0;
+ break;
+ }
+
+ return rc;
+}
+
+static int iplo_vnd_change_mtu(struct net_device *dev, int new_mtu)
+{
+ if (0 > new_mtu || 16384 < new_mtu)
+ return -EINVAL;
+
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+static const struct net_device_ops iplo_device_ops = {
+ .ndo_init = 0,
+ .ndo_do_ioctl = iplo_vnd_ioctl,
+ .ndo_start_xmit = iplo_vnd_start_xmit,
+ .ndo_change_mtu = iplo_vnd_change_mtu,
+};
+
+static void iplo_device_setup(struct net_device *dev)
+{
+ dev->flags |= IFF_NOARP;
+ dev->netdev_ops = &iplo_device_ops;
+ dev->mtu = 1500;
+ dev->needed_headroom = 0;
+ dev->watchdog_timeo = 100;
+ dev->header_ops = 0;
+ dev->type = ARPHRD_RAWIP;
+ dev->hard_header_len = 0;
+ dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
+ dev->tx_queue_len = 1000;
+}
+
+int __init rmnet_iplo_init(void)
+{
+ int rc;
+
+ pr_err("iplo: Module is coming up\n");
+ mydevice = alloc_netdev(100, "rmnet_mhi0", NET_NAME_ENUM,
+ iplo_device_setup);
+ if (!mydevice) {
+ pr_err("iplo: Failed to to allocate netdev for iplo\n");
+ return -EINVAL;
+ }
+ rtnl_lock();
+ rc = register_netdevice(mydevice);
+ rtnl_unlock();
+ if (rc != 0) {
+ pr_err("iplo: Failed to to register netdev [%s]\n",
+ mydevice->name);
+ free_netdev(mydevice);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+void __exit rmnet_iplo_exit(void)
+{
+ unregister_netdev(mydevice);
+ free_netdev(mydevice);
+ pr_err("iplo: Module is going away\n");
+}
+
+module_init(rmnet_iplo_init)
+module_exit(rmnet_iplo_exit)
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("RmNet IP Loop Back Driver");
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 929dafb8..e5d9041 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1374,7 +1374,7 @@
return -EINVAL;
ret = virtio_net_hdr_from_skb(skb, &gso,
- tun_is_little_endian(tun));
+ tun_is_little_endian(tun), true);
if (ret) {
struct skb_shared_info *sinfo = skb_shinfo(skb);
pr_err("unexpected GSO type: "
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index dd623f6..b82be81 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -531,6 +531,7 @@
#define SAMSUNG_VENDOR_ID 0x04e8
#define LENOVO_VENDOR_ID 0x17ef
#define NVIDIA_VENDOR_ID 0x0955
+#define HP_VENDOR_ID 0x03f0
static const struct usb_device_id products[] = {
/* BLACKLIST !!
@@ -677,6 +678,13 @@
.driver_info = 0,
},
+/* HP lt2523 (Novatel E371) - handled by qmi_wwan */
+{
+ USB_DEVICE_AND_INTERFACE_INFO(HP_VENDOR_ID, 0x421d, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+ .driver_info = 0,
+},
+
/* AnyDATA ADU960S - handled by qmi_wwan */
{
USB_DEVICE_AND_INTERFACE_INFO(0x16d5, 0x650a, USB_CLASS_COMM,
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 6fe1cdb..24d5272 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -654,6 +654,13 @@
USB_CDC_PROTO_NONE),
.driver_info = (unsigned long)&qmi_wwan_info,
},
+ { /* HP lt2523 (Novatel E371) */
+ USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x421d,
+ USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET,
+ USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long)&qmi_wwan_info,
+ },
{ /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */
USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7),
.driver_info = (unsigned long)&qmi_wwan_info,
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index efb84f0..90b426c 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -32,7 +32,7 @@
#define NETNEXT_VERSION "08"
/* Information for net */
-#define NET_VERSION "6"
+#define NET_VERSION "7"
#define DRIVER_VERSION "v1." NETNEXT_VERSION "." NET_VERSION
#define DRIVER_AUTHOR "Realtek linux nic maintainers <nic_swsd@realtek.com>"
@@ -1730,7 +1730,7 @@
u8 checksum = CHECKSUM_NONE;
u32 opts2, opts3;
- if (tp->version == RTL_VER_01 || tp->version == RTL_VER_02)
+ if (!(tp->netdev->features & NETIF_F_RXCSUM))
goto return_result;
opts2 = le32_to_cpu(rx_desc->opts2);
@@ -3572,43 +3572,93 @@
*/
if (!sw_linking && tp->rtl_ops.in_nway(tp))
return true;
+ else if (!skb_queue_empty(&tp->tx_queue))
+ return true;
else
return false;
}
-static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message)
+static int rtl8152_rumtime_suspend(struct r8152 *tp)
{
- struct r8152 *tp = usb_get_intfdata(intf);
struct net_device *netdev = tp->netdev;
int ret = 0;
- mutex_lock(&tp->control);
+ if (netif_running(netdev) && test_bit(WORK_ENABLE, &tp->flags)) {
+ u32 rcr = 0;
- if (PMSG_IS_AUTO(message)) {
- if (netif_running(netdev) && delay_autosuspend(tp)) {
+ if (delay_autosuspend(tp)) {
ret = -EBUSY;
goto out1;
}
- set_bit(SELECTIVE_SUSPEND, &tp->flags);
- } else {
- netif_device_detach(netdev);
+ if (netif_carrier_ok(netdev)) {
+ u32 ocp_data;
+
+ rcr = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
+ ocp_data = rcr & ~RCR_ACPT_ALL;
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
+ rxdy_gated_en(tp, true);
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA,
+ PLA_OOB_CTRL);
+ if (!(ocp_data & RXFIFO_EMPTY)) {
+ rxdy_gated_en(tp, false);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, rcr);
+ ret = -EBUSY;
+ goto out1;
+ }
+ }
+
+ clear_bit(WORK_ENABLE, &tp->flags);
+ usb_kill_urb(tp->intr_urb);
+
+ tp->rtl_ops.autosuspend_en(tp, true);
+
+ if (netif_carrier_ok(netdev)) {
+ napi_disable(&tp->napi);
+ rtl_stop_rx(tp);
+ rxdy_gated_en(tp, false);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, rcr);
+ napi_enable(&tp->napi);
+ }
}
+ set_bit(SELECTIVE_SUSPEND, &tp->flags);
+
+out1:
+ return ret;
+}
+
+static int rtl8152_system_suspend(struct r8152 *tp)
+{
+ struct net_device *netdev = tp->netdev;
+ int ret = 0;
+
+ netif_device_detach(netdev);
+
if (netif_running(netdev) && test_bit(WORK_ENABLE, &tp->flags)) {
clear_bit(WORK_ENABLE, &tp->flags);
usb_kill_urb(tp->intr_urb);
napi_disable(&tp->napi);
- if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
- rtl_stop_rx(tp);
- tp->rtl_ops.autosuspend_en(tp, true);
- } else {
- cancel_delayed_work_sync(&tp->schedule);
- tp->rtl_ops.down(tp);
- }
+ cancel_delayed_work_sync(&tp->schedule);
+ tp->rtl_ops.down(tp);
napi_enable(&tp->napi);
}
-out1:
+
+ return ret;
+}
+
+static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct r8152 *tp = usb_get_intfdata(intf);
+ int ret;
+
+ mutex_lock(&tp->control);
+
+ if (PMSG_IS_AUTO(message))
+ ret = rtl8152_rumtime_suspend(tp);
+ else
+ ret = rtl8152_system_suspend(tp);
+
mutex_unlock(&tp->control);
return ret;
@@ -4310,6 +4360,11 @@
NETIF_F_HIGHDMA | NETIF_F_FRAGLIST |
NETIF_F_IPV6_CSUM | NETIF_F_TSO6;
+ if (tp->version == RTL_VER_01) {
+ netdev->features &= ~NETIF_F_RXCSUM;
+ netdev->hw_features &= ~NETIF_F_RXCSUM;
+ }
+
netdev->ethtool_ops = &ops;
netif_set_gso_max_size(netdev, RTL_LIMITED_TSO_SIZE);
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index cbf1c61..51fc0c3 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -840,7 +840,7 @@
hdr = skb_vnet_hdr(skb);
if (virtio_net_hdr_from_skb(skb, &hdr->hdr,
- virtio_is_little_endian(vi->vdev)))
+ virtio_is_little_endian(vi->vdev), false))
BUG();
if (vi->mergeable_rx_bufs)
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 820de6a..95cf1d8 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -263,7 +263,9 @@
.flowi4_iif = LOOPBACK_IFINDEX,
.flowi4_tos = RT_TOS(ip4h->tos),
.flowi4_flags = FLOWI_FLAG_ANYSRC | FLOWI_FLAG_SKIP_NH_OIF,
+ .flowi4_proto = ip4h->protocol,
.daddr = ip4h->daddr,
+ .saddr = ip4h->saddr,
};
struct net *net = dev_net(vrf_dev);
struct rtable *rt;
@@ -371,6 +373,8 @@
struct in6_addr *nexthop;
int ret;
+ nf_reset(skb);
+
skb->protocol = htons(ETH_P_IPV6);
skb->dev = dev;
@@ -552,6 +556,8 @@
u32 nexthop;
int ret = -EINVAL;
+ nf_reset(skb);
+
/* Be paranoid, rather than too clever. */
if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
struct sk_buff *skb2;
@@ -850,8 +856,6 @@
{
struct net *net = dev_net(dev);
- nf_reset(skb);
-
if (NF_HOOK(pf, hook, net, NULL, skb, dev, NULL, vrf_rcv_finish) < 0)
skb = NULL; /* kfree_skb(skb) handled by nf code */
@@ -966,6 +970,7 @@
*/
need_strict = rt6_need_strict(&ipv6_hdr(skb)->daddr);
if (!ipv6_ndisc_frame(skb) && !need_strict) {
+ vrf_rx_stats(vrf_dev, skb->len);
skb->dev = vrf_dev;
skb->skb_iif = vrf_dev->ifindex;
@@ -1007,6 +1012,8 @@
goto out;
}
+ vrf_rx_stats(vrf_dev, skb->len);
+
skb_push(skb, skb->mac_len);
dev_queue_xmit_nit(skb, vrf_dev);
skb_pull(skb, skb->mac_len);
@@ -1232,6 +1239,8 @@
return -EINVAL;
vrf->tb_id = nla_get_u32(data[IFLA_VRF_TABLE]);
+ if (vrf->tb_id == RT_TABLE_UNSPEC)
+ return -EINVAL;
dev->priv_flags |= IFF_L3MDEV_MASTER;
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 2ba01ca..0fafaa9 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -2887,7 +2887,7 @@
memcpy(&vxlan->cfg, conf, sizeof(*conf));
if (!vxlan->cfg.dst_port) {
if (conf->flags & VXLAN_F_GPE)
- vxlan->cfg.dst_port = 4790; /* IANA assigned VXLAN-GPE port */
+ vxlan->cfg.dst_port = htons(4790); /* IANA VXLAN-GPE port */
else
vxlan->cfg.dst_port = default_port;
}
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 21ae8d6..0c45322 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -1534,7 +1534,7 @@
switch (ar->state) {
case ATH10K_STATE_ON:
ar->state = ATH10K_STATE_RESTARTING;
- ath10k_hif_stop(ar);
+ ath10k_halt(ar);
ath10k_scan_finish(ar);
ieee80211_restart_hw(ar->hw);
break;
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 521f1c5..be5b527 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -557,10 +557,8 @@
*/
ATH10K_FW_FEATURE_BTCOEX_PARAM = 14,
- /* Older firmware with HTT delivers incorrect tx status for null func
- * frames to driver, but this fixed in 10.2 and 10.4 firmware versions.
- * Also this workaround results in reporting of incorrect null func
- * status for 10.4. This flag is used to skip the workaround.
+ /* Unused flag and proven to be not working, enable this if you want
+ * to experiment sending NULL func data frames in HTT TX
*/
ATH10K_FW_FEATURE_SKIP_NULL_FUNC_WAR = 15,
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 76297d6..f2e85eb 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3255,8 +3255,6 @@
if (ar->htt.target_version_major < 3 &&
(ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)) &&
!test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
- ar->running_fw->fw_file.fw_features) &&
- !test_bit(ATH10K_FW_FEATURE_SKIP_NULL_FUNC_WAR,
ar->running_fw->fw_file.fw_features))
return ATH10K_HW_TXRX_MGMT;
@@ -4449,7 +4447,6 @@
ar->state = ATH10K_STATE_ON;
break;
case ATH10K_STATE_RESTARTING:
- ath10k_halt(ar);
ar->state = ATH10K_STATE_RESTARTED;
break;
case ATH10K_STATE_ON:
diff --git a/drivers/net/wireless/ath/ath10k/spectral.c b/drivers/net/wireless/ath/ath10k/spectral.c
index 7d9b0da..2ffc1fe 100644
--- a/drivers/net/wireless/ath/ath10k/spectral.c
+++ b/drivers/net/wireless/ath/ath10k/spectral.c
@@ -338,7 +338,7 @@
} else {
res = -EINVAL;
}
- } else if (strncmp("background", buf, 9) == 0) {
+ } else if (strncmp("background", buf, 10) == 0) {
res = ath10k_spectral_scan_config(ar, SPECTRAL_BACKGROUND);
} else if (strncmp("manual", buf, 6) == 0) {
res = ath10k_spectral_scan_config(ar, SPECTRAL_MANUAL);
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 14b13f0..a35f78b 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -2792,7 +2792,7 @@
WARN_ON(1);
}
- return val;
+ return !!val;
}
EXPORT_SYMBOL(ath9k_hw_gpio_get);
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 0dd454a..aff473d 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -26,7 +26,6 @@
{ PCI_VDEVICE(ATHEROS, 0x0023) }, /* PCI */
{ PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */
{ PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */
- { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */
#ifdef CONFIG_ATH9K_PCOEM
/* Mini PCI AR9220 MB92 cards: Compex WLM200NX, Wistron DNMA-92 */
@@ -37,7 +36,7 @@
.driver_data = ATH9K_PCI_LED_ACT_HI },
#endif
- { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
+ { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */
#ifdef CONFIG_ATH9K_PCOEM
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
@@ -85,7 +84,11 @@
0x10CF, /* Fujitsu */
0x1536),
.driver_data = ATH9K_PCI_D3_L1_WAR },
+#endif
+ { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
+
+#ifdef CONFIG_ATH9K_PCOEM
/* AR9285 card for Asus */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
0x002B,
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 52bfbb9..e47286b 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -2787,7 +2787,7 @@
fifo_list = &txq->txq_fifo[txq->txq_tailidx];
if (list_empty(fifo_list)) {
ath_txq_unlock(sc, txq);
- return;
+ break;
}
bf = list_first_entry(fifo_list, struct ath_buf, list);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c
index d02ca14..8d3e53f 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c
@@ -91,7 +91,7 @@
#define IWL8000_FW_PRE "iwlwifi-8000C-"
#define IWL8000_MODULE_FIRMWARE(api) \
- IWL8000_FW_PRE "-" __stringify(api) ".ucode"
+ IWL8000_FW_PRE __stringify(api) ".ucode"
#define IWL8265_FW_PRE "iwlwifi-8265-"
#define IWL8265_MODULE_FIRMWARE(api) \
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
index fc77188..52de3c6 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
@@ -1144,9 +1144,10 @@
.frame_limit = IWL_FRAME_LIMIT,
};
- /* Make sure reserved queue is still marked as such (or allocated) */
- mvm->queue_info[mvm_sta->reserved_queue].status =
- IWL_MVM_QUEUE_RESERVED;
+ /* Make sure reserved queue is still marked as such (if allocated) */
+ if (mvm_sta->reserved_queue != IEEE80211_INVAL_HW_QUEUE)
+ mvm->queue_info[mvm_sta->reserved_queue].status =
+ IWL_MVM_QUEUE_RESERVED;
for (i = 0; i <= IWL_MAX_TID_COUNT; i++) {
struct iwl_mvm_tid_data *tid_data = &mvm_sta->tid_data[i];
diff --git a/drivers/net/wireless/intersil/orinoco/mic.c b/drivers/net/wireless/intersil/orinoco/mic.c
index bc7397d..08bc782 100644
--- a/drivers/net/wireless/intersil/orinoco/mic.c
+++ b/drivers/net/wireless/intersil/orinoco/mic.c
@@ -16,7 +16,7 @@
/********************************************************************/
int orinoco_mic_init(struct orinoco_private *priv)
{
- priv->tx_tfm_mic = crypto_alloc_ahash("michael_mic", 0,
+ priv->tx_tfm_mic = crypto_alloc_shash("michael_mic", 0,
CRYPTO_ALG_ASYNC);
if (IS_ERR(priv->tx_tfm_mic)) {
printk(KERN_DEBUG "orinoco_mic_init: could not allocate "
@@ -25,7 +25,7 @@
return -ENOMEM;
}
- priv->rx_tfm_mic = crypto_alloc_ahash("michael_mic", 0,
+ priv->rx_tfm_mic = crypto_alloc_shash("michael_mic", 0,
CRYPTO_ALG_ASYNC);
if (IS_ERR(priv->rx_tfm_mic)) {
printk(KERN_DEBUG "orinoco_mic_init: could not allocate "
@@ -40,17 +40,16 @@
void orinoco_mic_free(struct orinoco_private *priv)
{
if (priv->tx_tfm_mic)
- crypto_free_ahash(priv->tx_tfm_mic);
+ crypto_free_shash(priv->tx_tfm_mic);
if (priv->rx_tfm_mic)
- crypto_free_ahash(priv->rx_tfm_mic);
+ crypto_free_shash(priv->rx_tfm_mic);
}
-int orinoco_mic(struct crypto_ahash *tfm_michael, u8 *key,
+int orinoco_mic(struct crypto_shash *tfm_michael, u8 *key,
u8 *da, u8 *sa, u8 priority,
u8 *data, size_t data_len, u8 *mic)
{
- AHASH_REQUEST_ON_STACK(req, tfm_michael);
- struct scatterlist sg[2];
+ SHASH_DESC_ON_STACK(desc, tfm_michael);
u8 hdr[ETH_HLEN + 2]; /* size of header + padding */
int err;
@@ -67,18 +66,27 @@
hdr[ETH_ALEN * 2 + 2] = 0;
hdr[ETH_ALEN * 2 + 3] = 0;
- /* Use scatter gather to MIC header and data in one go */
- sg_init_table(sg, 2);
- sg_set_buf(&sg[0], hdr, sizeof(hdr));
- sg_set_buf(&sg[1], data, data_len);
+ desc->tfm = tfm_michael;
+ desc->flags = 0;
- if (crypto_ahash_setkey(tfm_michael, key, MIC_KEYLEN))
- return -1;
+ err = crypto_shash_setkey(tfm_michael, key, MIC_KEYLEN);
+ if (err)
+ return err;
- ahash_request_set_tfm(req, tfm_michael);
- ahash_request_set_callback(req, 0, NULL, NULL);
- ahash_request_set_crypt(req, sg, mic, data_len + sizeof(hdr));
- err = crypto_ahash_digest(req);
- ahash_request_zero(req);
+ err = crypto_shash_init(desc);
+ if (err)
+ return err;
+
+ err = crypto_shash_update(desc, hdr, sizeof(hdr));
+ if (err)
+ return err;
+
+ err = crypto_shash_update(desc, data, data_len);
+ if (err)
+ return err;
+
+ err = crypto_shash_final(desc, mic);
+ shash_desc_zero(desc);
+
return err;
}
diff --git a/drivers/net/wireless/intersil/orinoco/mic.h b/drivers/net/wireless/intersil/orinoco/mic.h
index ce731d0..e8724e8 100644
--- a/drivers/net/wireless/intersil/orinoco/mic.h
+++ b/drivers/net/wireless/intersil/orinoco/mic.h
@@ -6,6 +6,7 @@
#define _ORINOCO_MIC_H_
#include <linux/types.h>
+#include <crypto/hash.h>
#define MICHAEL_MIC_LEN 8
@@ -15,7 +16,7 @@
int orinoco_mic_init(struct orinoco_private *priv);
void orinoco_mic_free(struct orinoco_private *priv);
-int orinoco_mic(struct crypto_ahash *tfm_michael, u8 *key,
+int orinoco_mic(struct crypto_shash *tfm_michael, u8 *key,
u8 *da, u8 *sa, u8 priority,
u8 *data, size_t data_len, u8 *mic);
diff --git a/drivers/net/wireless/intersil/orinoco/orinoco.h b/drivers/net/wireless/intersil/orinoco/orinoco.h
index 2f0c84b..5fa1c3e 100644
--- a/drivers/net/wireless/intersil/orinoco/orinoco.h
+++ b/drivers/net/wireless/intersil/orinoco/orinoco.h
@@ -152,8 +152,8 @@
u8 *wpa_ie;
int wpa_ie_len;
- struct crypto_ahash *rx_tfm_mic;
- struct crypto_ahash *tx_tfm_mic;
+ struct crypto_shash *rx_tfm_mic;
+ struct crypto_shash *tx_tfm_mic;
unsigned int wpa_enabled:1;
unsigned int tkip_cm_active:1;
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
index a5e6ec2..82d949e 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
@@ -4372,6 +4372,13 @@
void rtl8xxxu_gen2_report_connect(struct rtl8xxxu_priv *priv,
u8 macid, bool connect)
{
+#ifdef RTL8XXXU_GEN2_REPORT_CONNECT
+ /*
+ * Barry Day reports this causes issues with 8192eu and 8723bu
+ * devices reconnecting. The reason for this is unclear, but
+ * until it is better understood, leave the code in place but
+ * disabled, so it is not lost.
+ */
struct h2c_cmd h2c;
memset(&h2c, 0, sizeof(struct h2c_cmd));
@@ -4383,6 +4390,7 @@
h2c.media_status_rpt.parm &= ~BIT(0);
rtl8xxxu_gen2_h2c_cmd(priv, &h2c, sizeof(h2c.media_status_rpt));
+#endif
}
void rtl8xxxu_gen1_init_aggregation(struct rtl8xxxu_priv *priv)
diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c
index 264466f..4ac928b 100644
--- a/drivers/net/wireless/realtek/rtlwifi/base.c
+++ b/drivers/net/wireless/realtek/rtlwifi/base.c
@@ -1303,12 +1303,13 @@
static void setup_arp_tx(struct rtl_priv *rtlpriv, struct rtl_ps_ctl *ppsc)
{
+ struct ieee80211_hw *hw = rtlpriv->hw;
+
rtlpriv->ra.is_special_data = true;
if (rtlpriv->cfg->ops->get_btc_status())
rtlpriv->btcoexist.btc_ops->btc_special_packet_notify(
rtlpriv, 1);
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
+ rtl_lps_leave(hw);
ppsc->last_delaylps_stamp_jiffies = jiffies;
}
@@ -1381,8 +1382,7 @@
if (is_tx) {
rtlpriv->ra.is_special_data = true;
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
+ rtl_lps_leave(hw);
ppsc->last_delaylps_stamp_jiffies = jiffies;
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c
index 8e7f23c..4da4e45 100644
--- a/drivers/net/wireless/realtek/rtlwifi/core.c
+++ b/drivers/net/wireless/realtek/rtlwifi/core.c
@@ -1150,10 +1150,8 @@
} else {
mstatus = RT_MEDIA_DISCONNECT;
- if (mac->link_state == MAC80211_LINKED) {
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
- }
+ if (mac->link_state == MAC80211_LINKED)
+ rtl_lps_leave(hw);
if (ppsc->p2p_ps_info.p2p_ps_mode > P2P_PS_NONE)
rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
mac->link_state = MAC80211_NOLINK;
@@ -1431,8 +1429,7 @@
}
if (mac->link_state == MAC80211_LINKED) {
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
+ rtl_lps_leave(hw);
mac->link_state = MAC80211_LINKED_SCANNING;
} else {
rtl_ips_nic_on(hw);
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
index 0dfa9ea..5be4fc9 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.c
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
@@ -663,11 +663,9 @@
}
if (((rtlpriv->link_info.num_rx_inperiod +
- rtlpriv->link_info.num_tx_inperiod) > 8) ||
- (rtlpriv->link_info.num_rx_inperiod > 2)) {
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
- }
+ rtlpriv->link_info.num_tx_inperiod) > 8) ||
+ (rtlpriv->link_info.num_rx_inperiod > 2))
+ rtl_lps_leave(hw);
}
static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw,
@@ -918,10 +916,8 @@
}
if (((rtlpriv->link_info.num_rx_inperiod +
rtlpriv->link_info.num_tx_inperiod) > 8) ||
- (rtlpriv->link_info.num_rx_inperiod > 2)) {
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
- }
+ (rtlpriv->link_info.num_rx_inperiod > 2))
+ rtl_lps_leave(hw);
skb = new_skb;
no_new:
if (rtlpriv->use_new_trx_flow) {
diff --git a/drivers/net/wireless/realtek/rtlwifi/ps.c b/drivers/net/wireless/realtek/rtlwifi/ps.c
index 18d979a..d0ffc4d 100644
--- a/drivers/net/wireless/realtek/rtlwifi/ps.c
+++ b/drivers/net/wireless/realtek/rtlwifi/ps.c
@@ -407,8 +407,8 @@
}
}
-/*Enter the leisure power save mode.*/
-void rtl_lps_enter(struct ieee80211_hw *hw)
+/* Interrupt safe routine to enter the leisure power save mode.*/
+static void rtl_lps_enter_core(struct ieee80211_hw *hw)
{
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
@@ -444,10 +444,9 @@
spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
}
-EXPORT_SYMBOL(rtl_lps_enter);
-/*Leave the leisure power save mode.*/
-void rtl_lps_leave(struct ieee80211_hw *hw)
+/* Interrupt safe routine to leave the leisure power save mode.*/
+static void rtl_lps_leave_core(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
@@ -477,7 +476,6 @@
}
spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
}
-EXPORT_SYMBOL(rtl_lps_leave);
/* For sw LPS*/
void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len)
@@ -670,12 +668,34 @@
struct rtl_priv *rtlpriv = rtl_priv(hw);
if (rtlpriv->enter_ps)
- rtl_lps_enter(hw);
+ rtl_lps_enter_core(hw);
else
- rtl_lps_leave(hw);
+ rtl_lps_leave_core(hw);
}
EXPORT_SYMBOL_GPL(rtl_lps_change_work_callback);
+void rtl_lps_enter(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ if (!in_interrupt())
+ return rtl_lps_enter_core(hw);
+ rtlpriv->enter_ps = true;
+ schedule_work(&rtlpriv->works.lps_change_work);
+}
+EXPORT_SYMBOL_GPL(rtl_lps_enter);
+
+void rtl_lps_leave(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ if (!in_interrupt())
+ return rtl_lps_leave_core(hw);
+ rtlpriv->enter_ps = false;
+ schedule_work(&rtlpriv->works.lps_change_work);
+}
+EXPORT_SYMBOL_GPL(rtl_lps_leave);
+
void rtl_swlps_wq_callback(void *data)
{
struct rtl_works *rtlworks = container_of_dwork_rtl(data,
diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.c b/drivers/net/wireless/realtek/rtlwifi/usb.c
index 32aa5c1..3837bbd 100644
--- a/drivers/net/wireless/realtek/rtlwifi/usb.c
+++ b/drivers/net/wireless/realtek/rtlwifi/usb.c
@@ -1067,6 +1067,7 @@
return -ENOMEM;
}
rtlpriv = hw->priv;
+ rtlpriv->hw = hw;
rtlpriv->usb_data = kzalloc(RTL_USB_MAX_RX_COUNT * sizeof(u32),
GFP_KERNEL);
if (!rtlpriv->usb_data)
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
index abe5c6b..1480734 100644
--- a/drivers/nvdimm/namespace_devs.c
+++ b/drivers/nvdimm/namespace_devs.c
@@ -957,6 +957,7 @@
{
resource_size_t allocated = 0, available = 0;
struct nd_region *nd_region = to_nd_region(dev->parent);
+ struct nd_namespace_common *ndns = to_ndns(dev);
struct nd_mapping *nd_mapping;
struct nvdimm_drvdata *ndd;
struct nd_label_id label_id;
@@ -964,7 +965,7 @@
u8 *uuid = NULL;
int rc, i;
- if (dev->driver || to_ndns(dev)->claim)
+ if (dev->driver || ndns->claim)
return -EBUSY;
if (is_namespace_pmem(dev)) {
@@ -1034,20 +1035,16 @@
nd_namespace_pmem_set_resource(nd_region, nspm,
val * nd_region->ndr_mappings);
- } else if (is_namespace_blk(dev)) {
- struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
-
- /*
- * Try to delete the namespace if we deleted all of its
- * allocation, this is not the seed device for the
- * region, and it is not actively claimed by a btt
- * instance.
- */
- if (val == 0 && nd_region->ns_seed != dev
- && !nsblk->common.claim)
- nd_device_unregister(dev, ND_ASYNC);
}
+ /*
+ * Try to delete the namespace if we deleted all of its
+ * allocation, this is not the seed device for the region, and
+ * it is not actively claimed by a btt instance.
+ */
+ if (val == 0 && nd_region->ns_seed != dev && !ndns->claim)
+ nd_device_unregister(dev, ND_ASYNC);
+
return rc;
}
diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c
index cea8350..a2ac9e6 100644
--- a/drivers/nvdimm/pfn_devs.c
+++ b/drivers/nvdimm/pfn_devs.c
@@ -108,7 +108,7 @@
{
struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev);
- return sprintf(buf, "%lx\n", nd_pfn->align);
+ return sprintf(buf, "%ld\n", nd_pfn->align);
}
static ssize_t __align_store(struct nd_pfn *nd_pfn, const char *buf)
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 79e679d..da10b48 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -1122,12 +1122,7 @@
if (ret)
return ret;
- /* Checking for ctrl->tagset is a trick to avoid sleeping on module
- * load, since we only need the quirk on reset_controller. Notice
- * that the HGST device needs this delay only in firmware activation
- * procedure; unfortunately we have no (easy) way to verify this.
- */
- if ((ctrl->quirks & NVME_QUIRK_DELAY_BEFORE_CHK_RDY) && ctrl->tagset)
+ if (ctrl->quirks & NVME_QUIRK_DELAY_BEFORE_CHK_RDY)
msleep(NVME_QUIRK_DELAY_AMOUNT);
return nvme_wait_ready(ctrl, cap, false);
diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index af5e2dc..011f88e 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -271,7 +271,7 @@
mutex_lock(&subsys->lock);
ret = -EBUSY;
- if (nvmet_ns_enabled(ns))
+ if (ns->enabled)
goto out_unlock;
kfree(ns->device_path);
@@ -307,7 +307,7 @@
int ret = 0;
mutex_lock(&subsys->lock);
- if (nvmet_ns_enabled(ns)) {
+ if (ns->enabled) {
ret = -EBUSY;
goto out_unlock;
}
@@ -339,7 +339,7 @@
static ssize_t nvmet_ns_enable_show(struct config_item *item, char *page)
{
- return sprintf(page, "%d\n", nvmet_ns_enabled(to_nvmet_ns(item)));
+ return sprintf(page, "%d\n", to_nvmet_ns(item)->enabled);
}
static ssize_t nvmet_ns_enable_store(struct config_item *item,
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index a21437a..55ce769 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -264,7 +264,7 @@
int ret = 0;
mutex_lock(&subsys->lock);
- if (!list_empty(&ns->dev_link))
+ if (ns->enabled)
goto out_unlock;
ns->bdev = blkdev_get_by_path(ns->device_path, FMODE_READ | FMODE_WRITE,
@@ -309,6 +309,7 @@
list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry)
nvmet_add_async_event(ctrl, NVME_AER_TYPE_NOTICE, 0, 0);
+ ns->enabled = true;
ret = 0;
out_unlock:
mutex_unlock(&subsys->lock);
@@ -325,11 +326,11 @@
struct nvmet_ctrl *ctrl;
mutex_lock(&subsys->lock);
- if (list_empty(&ns->dev_link)) {
- mutex_unlock(&subsys->lock);
- return;
- }
- list_del_init(&ns->dev_link);
+ if (!ns->enabled)
+ goto out_unlock;
+
+ ns->enabled = false;
+ list_del_rcu(&ns->dev_link);
mutex_unlock(&subsys->lock);
/*
@@ -351,6 +352,7 @@
if (ns->bdev)
blkdev_put(ns->bdev, FMODE_WRITE|FMODE_READ);
+out_unlock:
mutex_unlock(&subsys->lock);
}
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 76b6eed..7655a35 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -47,6 +47,7 @@
loff_t size;
u8 nguid[16];
+ bool enabled;
struct nvmet_subsys *subsys;
const char *device_path;
@@ -61,11 +62,6 @@
return container_of(to_config_group(item), struct nvmet_ns, group);
}
-static inline bool nvmet_ns_enabled(struct nvmet_ns *ns)
-{
- return !list_empty_careful(&ns->dev_link);
-}
-
struct nvmet_cq {
u16 qid;
u16 size;
diff --git a/drivers/of/of_numa.c b/drivers/of/of_numa.c
index f63d4b0d..a53982a 100644
--- a/drivers/of/of_numa.c
+++ b/drivers/of/of_numa.c
@@ -176,7 +176,12 @@
np->name);
of_node_put(np);
- if (!r)
+ /*
+ * If numa=off passed on command line, or with a defective
+ * device tree, the nid may not be in the set of possible
+ * nodes. Check for this case and return NUMA_NO_NODE.
+ */
+ if (!r && nid < MAX_NUMNODES && node_possible(nid))
return nid;
return NUMA_NO_NODE;
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
index bed1999..af8f6e9 100644
--- a/drivers/pci/host/pcie-designware.c
+++ b/drivers/pci/host/pcie-designware.c
@@ -807,11 +807,6 @@
{
u32 val;
- /* get iATU unroll support */
- pp->iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pp);
- dev_dbg(pp->dev, "iATU unroll: %s\n",
- pp->iatu_unroll_enabled ? "enabled" : "disabled");
-
/* set the number of lanes */
val = dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL);
val &= ~PORT_LINK_MODE_MASK;
@@ -882,6 +877,11 @@
* we should not program the ATU here.
*/
if (!pp->ops->rd_other_conf) {
+ /* get iATU unroll support */
+ pp->iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pp);
+ dev_dbg(pp->dev, "iATU unroll: %s\n",
+ pp->iatu_unroll_enabled ? "enabled" : "disabled");
+
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
PCIE_ATU_TYPE_MEM, pp->mem_base,
pp->mem_bus_addr, pp->mem_size);
diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c
index e04f69b..3452983 100644
--- a/drivers/pci/host/pcie-rockchip.c
+++ b/drivers/pci/host/pcie-rockchip.c
@@ -533,7 +533,7 @@
/* Fix the transmitted FTS count desired to exit from L0s. */
status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL_PLC1);
- status = (status & PCIE_CORE_CTRL_PLC1_FTS_MASK) |
+ status = (status & ~PCIE_CORE_CTRL_PLC1_FTS_MASK) |
(PCIE_CORE_CTRL_PLC1_FTS_CNT << PCIE_CORE_CTRL_PLC1_FTS_SHIFT);
rockchip_pcie_write(rockchip, status, PCIE_CORE_CTRL_PLC1);
@@ -590,8 +590,8 @@
/* Check the final link width from negotiated lane counter from MGMT */
status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL);
- status = 0x1 << ((status & PCIE_CORE_PL_CONF_LANE_MASK) >>
- PCIE_CORE_PL_CONF_LANE_MASK);
+ status = 0x1 << ((status & PCIE_CORE_PL_CONF_LANE_MASK) >>
+ PCIE_CORE_PL_CONF_LANE_SHIFT);
dev_dbg(dev, "current link width is x%d\n", status);
rockchip_pcie_write(rockchip, ROCKCHIP_VENDOR_ID,
diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c
index dc67f39..c614ff7 100644
--- a/drivers/pci/hotplug/rpadlpar_core.c
+++ b/drivers/pci/hotplug/rpadlpar_core.c
@@ -257,8 +257,13 @@
static int dlpar_add_vio_slot(char *drc_name, struct device_node *dn)
{
- if (vio_find_node(dn))
+ struct vio_dev *vio_dev;
+
+ vio_dev = vio_find_node(dn);
+ if (vio_dev) {
+ put_device(&vio_dev->dev);
return -EINVAL;
+ }
if (!vio_register_device_node(dn)) {
printk(KERN_ERR
@@ -334,6 +339,9 @@
return -EINVAL;
vio_unregister_device(vio_dev);
+
+ put_device(&vio_dev->dev);
+
return 0;
}
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index ad70507..3455f75 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -1294,7 +1294,8 @@
} else if (dev->msi_enabled) {
struct msi_desc *entry = first_pci_msi_entry(dev);
- if (WARN_ON_ONCE(!entry || nr >= entry->nvec_used))
+ if (WARN_ON_ONCE(!entry || !entry->affinity ||
+ nr >= entry->nvec_used))
return NULL;
return &entry->affinity[nr];
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index ba34907..eda6a7c 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -2106,6 +2106,10 @@
if (!dev->pme_support)
return false;
+ /* PME-capable in principle, but not from the intended sleep state */
+ if (!pci_pme_capable(dev, pci_target_state(dev)))
+ return false;
+
while (bus->parent) {
struct pci_dev *bridge = bus->self;
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 0ec649d..b0916b1 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -518,25 +518,32 @@
link = kzalloc(sizeof(*link), GFP_KERNEL);
if (!link)
return NULL;
+
INIT_LIST_HEAD(&link->sibling);
INIT_LIST_HEAD(&link->children);
INIT_LIST_HEAD(&link->link);
link->pdev = pdev;
- if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) {
+
+ /*
+ * Root Ports and PCI/PCI-X to PCIe Bridges are roots of PCIe
+ * hierarchies.
+ */
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE) {
+ link->root = link;
+ } else {
struct pcie_link_state *parent;
+
parent = pdev->bus->parent->self->link_state;
if (!parent) {
kfree(link);
return NULL;
}
+
link->parent = parent;
+ link->root = link->parent->root;
list_add(&link->link, &parent->children);
}
- /* Setup a pointer to the root port link */
- if (!link->parent)
- link->root = link;
- else
- link->root = link->parent->root;
list_add(&link->sibling, &link_list);
pdev->link_state = link;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 104c46d..300770c 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1050,6 +1050,7 @@
pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
if (!pos)
return;
+
pdev->pcie_cap = pos;
pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16);
pdev->pcie_flags_reg = reg16;
@@ -1057,13 +1058,14 @@
pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;
/*
- * A Root Port is always the upstream end of a Link. No PCIe
- * component has two Links. Two Links are connected by a Switch
- * that has a Port on each Link and internal logic to connect the
- * two Ports.
+ * A Root Port or a PCI-to-PCIe bridge is always the upstream end
+ * of a Link. No PCIe component has two Links. Two Links are
+ * connected by a Switch that has a Port on each Link and internal
+ * logic to connect the two Ports.
*/
type = pci_pcie_type(pdev);
- if (type == PCI_EXP_TYPE_ROOT_PORT)
+ if (type == PCI_EXP_TYPE_ROOT_PORT ||
+ type == PCI_EXP_TYPE_PCIE_BRIDGE)
pdev->has_secondary_link = 1;
else if (type == PCI_EXP_TYPE_UPSTREAM ||
type == PCI_EXP_TYPE_DOWNSTREAM) {
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index c232729..3a035e07 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -3137,8 +3137,9 @@
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b7, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2298, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x229c, quirk_remove_d3_delay);
+
/*
- * Some devices may pass our check in pci_intx_mask_supported if
+ * Some devices may pass our check in pci_intx_mask_supported() if
* PCI_COMMAND_INTX_DISABLE works though they actually do not properly
* support this feature.
*/
@@ -3146,53 +3147,139 @@
{
dev->broken_intx_masking = 1;
}
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CHELSIO, 0x0030,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(0x1814, 0x0601, /* Ralink RT2800 802.11n PCI */
- quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x0030,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(0x1814, 0x0601, /* Ralink RT2800 802.11n PCI */
+ quirk_broken_intx_masking);
+
/*
* Realtek RTL8169 PCI Gigabit Ethernet Controller (rev 10)
* Subsystem: Realtek RTL8169/8110 Family PCI Gigabit Ethernet NIC
*
* RTL8110SC - Fails under PCI device assignment using DisINTx masking.
*/
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_REALTEK, 0x8169,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MELLANOX, PCI_ANY_ID,
- quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_REALTEK, 0x8169,
+ quirk_broken_intx_masking);
/*
* Intel i40e (XL710/X710) 10/20/40GbE NICs all have broken INTx masking,
* DisINTx can be set but the interrupt status bit is non-functional.
*/
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1572,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1574,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1580,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1581,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1583,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1584,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1585,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1586,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1587,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1588,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1589,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x37d0,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x37d1,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x37d2,
- quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1572,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1574,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1580,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1581,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1583,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1584,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1585,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1586,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1587,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1588,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1589,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x37d0,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x37d1,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x37d2,
+ quirk_broken_intx_masking);
+
+static u16 mellanox_broken_intx_devs[] = {
+ PCI_DEVICE_ID_MELLANOX_HERMON_SDR,
+ PCI_DEVICE_ID_MELLANOX_HERMON_DDR,
+ PCI_DEVICE_ID_MELLANOX_HERMON_QDR,
+ PCI_DEVICE_ID_MELLANOX_HERMON_DDR_GEN2,
+ PCI_DEVICE_ID_MELLANOX_HERMON_QDR_GEN2,
+ PCI_DEVICE_ID_MELLANOX_HERMON_EN,
+ PCI_DEVICE_ID_MELLANOX_HERMON_EN_GEN2,
+ PCI_DEVICE_ID_MELLANOX_CONNECTX_EN,
+ PCI_DEVICE_ID_MELLANOX_CONNECTX_EN_T_GEN2,
+ PCI_DEVICE_ID_MELLANOX_CONNECTX_EN_GEN2,
+ PCI_DEVICE_ID_MELLANOX_CONNECTX_EN_5_GEN2,
+ PCI_DEVICE_ID_MELLANOX_CONNECTX2,
+ PCI_DEVICE_ID_MELLANOX_CONNECTX3,
+ PCI_DEVICE_ID_MELLANOX_CONNECTX3_PRO,
+};
+
+#define CONNECTX_4_CURR_MAX_MINOR 99
+#define CONNECTX_4_INTX_SUPPORT_MINOR 14
+
+/*
+ * Check ConnectX-4/LX FW version to see if it supports legacy interrupts.
+ * If so, don't mark it as broken.
+ * FW minor > 99 means older FW version format and no INTx masking support.
+ * FW minor < 14 means new FW version format and no INTx masking support.
+ */
+static void mellanox_check_broken_intx_masking(struct pci_dev *pdev)
+{
+ __be32 __iomem *fw_ver;
+ u16 fw_major;
+ u16 fw_minor;
+ u16 fw_subminor;
+ u32 fw_maj_min;
+ u32 fw_sub_min;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mellanox_broken_intx_devs); i++) {
+ if (pdev->device == mellanox_broken_intx_devs[i]) {
+ pdev->broken_intx_masking = 1;
+ return;
+ }
+ }
+
+ /* Getting here means Connect-IB cards and up. Connect-IB has no INTx
+ * support so shouldn't be checked further
+ */
+ if (pdev->device == PCI_DEVICE_ID_MELLANOX_CONNECTIB)
+ return;
+
+ if (pdev->device != PCI_DEVICE_ID_MELLANOX_CONNECTX4 &&
+ pdev->device != PCI_DEVICE_ID_MELLANOX_CONNECTX4_LX)
+ return;
+
+ /* For ConnectX-4 and ConnectX-4LX, need to check FW support */
+ if (pci_enable_device_mem(pdev)) {
+ dev_warn(&pdev->dev, "Can't enable device memory\n");
+ return;
+ }
+
+ fw_ver = ioremap(pci_resource_start(pdev, 0), 4);
+ if (!fw_ver) {
+ dev_warn(&pdev->dev, "Can't map ConnectX-4 initialization segment\n");
+ goto out;
+ }
+
+ /* Reading from resource space should be 32b aligned */
+ fw_maj_min = ioread32be(fw_ver);
+ fw_sub_min = ioread32be(fw_ver + 1);
+ fw_major = fw_maj_min & 0xffff;
+ fw_minor = fw_maj_min >> 16;
+ fw_subminor = fw_sub_min & 0xffff;
+ if (fw_minor > CONNECTX_4_CURR_MAX_MINOR ||
+ fw_minor < CONNECTX_4_INTX_SUPPORT_MINOR) {
+ dev_warn(&pdev->dev, "ConnectX-4: FW %u.%u.%u doesn't support INTx masking, disabling. Please upgrade FW to %d.14.1100 and up for INTx support\n",
+ fw_major, fw_minor, fw_subminor, pdev->device ==
+ PCI_DEVICE_ID_MELLANOX_CONNECTX4 ? 12 : 14);
+ pdev->broken_intx_masking = 1;
+ }
+
+ iounmap(fw_ver);
+
+out:
+ pci_disable_device(pdev);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX, PCI_ANY_ID,
+ mellanox_check_broken_intx_masking);
static void quirk_no_bus_reset(struct pci_dev *dev)
{
@@ -3255,6 +3342,25 @@
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PORT_RIDGE,
quirk_thunderbolt_hotplug_msi);
+static void quirk_chelsio_extend_vpd(struct pci_dev *dev)
+{
+ pci_set_vpd_size(dev, 8192);
+}
+
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x20, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x21, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x22, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x23, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x24, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x25, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x26, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x30, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x31, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x32, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x35, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x36, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x37, quirk_chelsio_extend_vpd);
+
#ifdef CONFIG_ACPI
/*
* Apple: Shutdown Cactus Ridge Thunderbolt controller.
diff --git a/drivers/pinctrl/freescale/pinctrl-imx.c b/drivers/pinctrl/freescale/pinctrl-imx.c
index 79c4e14..5ef7e87 100644
--- a/drivers/pinctrl/freescale/pinctrl-imx.c
+++ b/drivers/pinctrl/freescale/pinctrl-imx.c
@@ -778,10 +778,10 @@
imx_pinctrl_desc->name = dev_name(&pdev->dev);
imx_pinctrl_desc->pins = info->pins;
imx_pinctrl_desc->npins = info->npins;
- imx_pinctrl_desc->pctlops = &imx_pctrl_ops,
- imx_pinctrl_desc->pmxops = &imx_pmx_ops,
- imx_pinctrl_desc->confops = &imx_pinconf_ops,
- imx_pinctrl_desc->owner = THIS_MODULE,
+ imx_pinctrl_desc->pctlops = &imx_pctrl_ops;
+ imx_pinctrl_desc->pmxops = &imx_pmx_ops;
+ imx_pinctrl_desc->confops = &imx_pinconf_ops;
+ imx_pinctrl_desc->owner = THIS_MODULE;
ret = imx_pinctrl_probe_dt(pdev, info);
if (ret) {
diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c
index 71bbeb9..583ae3f 100644
--- a/drivers/pinctrl/intel/pinctrl-baytrail.c
+++ b/drivers/pinctrl/intel/pinctrl-baytrail.c
@@ -731,16 +731,23 @@
int reg)
{
struct byt_community *comm = byt_get_community(vg, offset);
- u32 reg_offset = 0;
+ u32 reg_offset;
if (!comm)
return NULL;
offset -= comm->pin_base;
- if (reg == BYT_INT_STAT_REG)
+ switch (reg) {
+ case BYT_INT_STAT_REG:
reg_offset = (offset / 32) * 4;
- else
+ break;
+ case BYT_DEBOUNCE_REG:
+ reg_offset = 0;
+ break;
+ default:
reg_offset = comm->pad_map[offset] * 16;
+ break;
+ }
return comm->reg_base + reg_offset + reg;
}
@@ -1092,6 +1099,7 @@
enum pin_config_param param = pinconf_to_config_param(*config);
void __iomem *conf_reg = byt_gpio_reg(vg, offset, BYT_CONF0_REG);
void __iomem *val_reg = byt_gpio_reg(vg, offset, BYT_VAL_REG);
+ void __iomem *db_reg = byt_gpio_reg(vg, offset, BYT_DEBOUNCE_REG);
unsigned long flags;
u32 conf, pull, val, debounce;
u16 arg = 0;
@@ -1128,7 +1136,7 @@
return -EINVAL;
raw_spin_lock_irqsave(&vg->lock, flags);
- debounce = readl(byt_gpio_reg(vg, offset, BYT_DEBOUNCE_REG));
+ debounce = readl(db_reg);
raw_spin_unlock_irqrestore(&vg->lock, flags);
switch (debounce & BYT_DEBOUNCE_PULSE_MASK) {
@@ -1176,6 +1184,7 @@
unsigned int param, arg;
void __iomem *conf_reg = byt_gpio_reg(vg, offset, BYT_CONF0_REG);
void __iomem *val_reg = byt_gpio_reg(vg, offset, BYT_VAL_REG);
+ void __iomem *db_reg = byt_gpio_reg(vg, offset, BYT_DEBOUNCE_REG);
unsigned long flags;
u32 conf, val, debounce;
int i, ret = 0;
@@ -1238,36 +1247,40 @@
break;
case PIN_CONFIG_INPUT_DEBOUNCE:
- debounce = readl(byt_gpio_reg(vg, offset,
- BYT_DEBOUNCE_REG));
- conf &= ~BYT_DEBOUNCE_PULSE_MASK;
+ debounce = readl(db_reg);
+ debounce &= ~BYT_DEBOUNCE_PULSE_MASK;
switch (arg) {
+ case 0:
+ conf &= BYT_DEBOUNCE_EN;
+ break;
case 375:
- conf |= BYT_DEBOUNCE_PULSE_375US;
+ debounce |= BYT_DEBOUNCE_PULSE_375US;
break;
case 750:
- conf |= BYT_DEBOUNCE_PULSE_750US;
+ debounce |= BYT_DEBOUNCE_PULSE_750US;
break;
case 1500:
- conf |= BYT_DEBOUNCE_PULSE_1500US;
+ debounce |= BYT_DEBOUNCE_PULSE_1500US;
break;
case 3000:
- conf |= BYT_DEBOUNCE_PULSE_3MS;
+ debounce |= BYT_DEBOUNCE_PULSE_3MS;
break;
case 6000:
- conf |= BYT_DEBOUNCE_PULSE_6MS;
+ debounce |= BYT_DEBOUNCE_PULSE_6MS;
break;
case 12000:
- conf |= BYT_DEBOUNCE_PULSE_12MS;
+ debounce |= BYT_DEBOUNCE_PULSE_12MS;
break;
case 24000:
- conf |= BYT_DEBOUNCE_PULSE_24MS;
+ debounce |= BYT_DEBOUNCE_PULSE_24MS;
break;
default:
ret = -EINVAL;
}
+ if (!ret)
+ writel(debounce, db_reg);
break;
default:
ret = -ENOTSUPP;
@@ -1606,7 +1619,9 @@
continue;
}
+ raw_spin_lock(&vg->lock);
pending = readl(reg);
+ raw_spin_unlock(&vg->lock);
for_each_set_bit(pin, &pending, 32) {
virq = irq_find_mapping(vg->chip.irqdomain, base + pin);
generic_handle_irq(virq);
diff --git a/drivers/pinctrl/intel/pinctrl-broxton.c b/drivers/pinctrl/intel/pinctrl-broxton.c
index 59cb7a6..901b356 100644
--- a/drivers/pinctrl/intel/pinctrl-broxton.c
+++ b/drivers/pinctrl/intel/pinctrl-broxton.c
@@ -19,7 +19,7 @@
#define BXT_PAD_OWN 0x020
#define BXT_HOSTSW_OWN 0x080
-#define BXT_PADCFGLOCK 0x090
+#define BXT_PADCFGLOCK 0x060
#define BXT_GPI_IE 0x110
#define BXT_COMMUNITY(s, e) \
diff --git a/drivers/pinctrl/intel/pinctrl-merrifield.c b/drivers/pinctrl/intel/pinctrl-merrifield.c
index 7826c7f..9931be6 100644
--- a/drivers/pinctrl/intel/pinctrl-merrifield.c
+++ b/drivers/pinctrl/intel/pinctrl-merrifield.c
@@ -794,6 +794,9 @@
unsigned int i;
int ret;
+ if (!mrfld_buf_available(mp, pin))
+ return -ENOTSUPP;
+
for (i = 0; i < nconfigs; i++) {
switch (pinconf_to_config_param(configs[i])) {
case PIN_CONFIG_BIAS_DISABLE:
diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c
index 57122ed..9443c9d 100644
--- a/drivers/pinctrl/meson/pinctrl-meson.c
+++ b/drivers/pinctrl/meson/pinctrl-meson.c
@@ -212,7 +212,7 @@
{
struct meson_pinctrl *pc = pinctrl_dev_get_drvdata(pcdev);
- meson_pmx_disable_other_groups(pc, range->pin_base + offset, -1);
+ meson_pmx_disable_other_groups(pc, offset, -1);
return 0;
}
diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c
index aea310a..c9a1469 100644
--- a/drivers/pinctrl/pinctrl-amd.c
+++ b/drivers/pinctrl/pinctrl-amd.c
@@ -382,26 +382,21 @@
{
int ret = 0;
u32 pin_reg;
- unsigned long flags;
- bool level_trig;
- u32 active_level;
+ unsigned long flags, irq_flags;
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct amd_gpio *gpio_dev = gpiochip_get_data(gc);
spin_lock_irqsave(&gpio_dev->lock, flags);
pin_reg = readl(gpio_dev->base + (d->hwirq)*4);
- /*
- * When level_trig is set EDGE and active_level is set HIGH in BIOS
- * default settings, ignore incoming settings from client and use
- * BIOS settings to configure GPIO register.
+ /* Ignore the settings coming from the client and
+ * read the values from the ACPI tables
+ * while setting the trigger type
*/
- level_trig = !(pin_reg & (LEVEL_TRIGGER << LEVEL_TRIG_OFF));
- active_level = pin_reg & (ACTIVE_LEVEL_MASK << ACTIVE_LEVEL_OFF);
- if(level_trig &&
- ((active_level >> ACTIVE_LEVEL_OFF) == ACTIVE_HIGH))
- type = IRQ_TYPE_EDGE_FALLING;
+ irq_flags = irq_get_trigger_type(d->irq);
+ if (irq_flags != IRQ_TYPE_NONE)
+ type = irq_flags;
switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_EDGE_RISING:
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index 5222936..522c724 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -139,4 +139,11 @@
which are using SSBI for communication with SoC. Example PMIC's
devices are pm8058 and pm8921.
+config PINCTRL_WCD
+ tristate "Qualcomm Technologies, Inc WCD pin controller driver"
+ depends on WCD934X_CODEC
+ help
+ This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+ WCD gpio controller block.
+
endif
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index c66ee3c..7c74288 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -17,3 +17,4 @@
obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-mpp.o
obj-$(CONFIG_PINCTRL_SDM845) += pinctrl-sdm845.o
obj-$(CONFIG_PINCTRL_SDM830) += pinctrl-sdm830.o
+obj-$(CONFIG_PINCTRL_WCD) += pinctrl-wcd.o
diff --git a/drivers/pinctrl/qcom/pinctrl-wcd.c b/drivers/pinctrl/qcom/pinctrl-wcd.c
new file mode 100644
index 0000000..2cc68a0
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-wcd.c
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mfd/wcd934x/registers.h>
+
+#include "../core.h"
+#include "../pinctrl-utils.h"
+
+#define WCD_REG_DIR_CTL WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE
+#define WCD_REG_VAL_CTL WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA
+#define WCD_GPIO_PULL_UP 1
+#define WCD_GPIO_PULL_DOWN 2
+#define WCD_GPIO_BIAS_DISABLE 3
+#define WCD_GPIO_STRING_LEN 20
+
+/**
+ * struct wcd_gpio_pad - keep current GPIO settings
+ * @offset: offset of gpio.
+ * @is_valid: Set to false, when GPIO in high Z state.
+ * @value: value of a pin
+ * @output_enabled: Set to true if GPIO is output and false if it is input
+ * @pullup: Constant current which flow through GPIO output buffer.
+ * @strength: Drive strength of a pin
+ */
+struct wcd_gpio_pad {
+ u16 offset;
+ bool is_valid;
+ bool value;
+ bool output_enabled;
+ unsigned int pullup;
+ unsigned int strength;
+};
+
+struct wcd_gpio_priv {
+ struct device *dev;
+ struct regmap *map;
+ struct pinctrl_dev *ctrl;
+ struct gpio_chip chip;
+};
+
+static int wcd_gpio_read(struct wcd_gpio_priv *priv_data,
+ struct wcd_gpio_pad *pad, unsigned int addr)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(priv_data->map, addr, &val);
+ if (ret < 0)
+ dev_err(priv_data->dev, "%s: read 0x%x failed\n",
+ __func__, addr);
+ else
+ ret = (val >> pad->offset);
+
+ return ret;
+}
+
+static int wcd_gpio_write(struct wcd_gpio_priv *priv_data,
+ struct wcd_gpio_pad *pad, unsigned int addr,
+ unsigned int val)
+{
+ int ret;
+
+ ret = regmap_update_bits(priv_data->map, addr, (1 << pad->offset),
+ val << pad->offset);
+ if (ret < 0)
+ dev_err(priv_data->dev, "write 0x%x failed\n", addr);
+
+ return ret;
+}
+
+static int wcd_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ return pctldev->desc->npins;
+}
+
+static const char *wcd_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned int pin)
+{
+ return pctldev->desc->pins[pin].name;
+}
+
+static int wcd_get_group_pins(struct pinctrl_dev *pctldev, unsigned int pin,
+ const unsigned int **pins, unsigned int *num_pins)
+{
+ *pins = &pctldev->desc->pins[pin].number;
+ *num_pins = 1;
+ return 0;
+}
+
+static const struct pinctrl_ops wcd_pinctrl_ops = {
+ .get_groups_count = wcd_get_groups_count,
+ .get_group_name = wcd_get_group_name,
+ .get_group_pins = wcd_get_group_pins,
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_group,
+ .dt_free_map = pinctrl_utils_free_map,
+};
+
+static int wcd_config_get(struct pinctrl_dev *pctldev,
+ unsigned int pin, unsigned long *config)
+{
+ unsigned int param = pinconf_to_config_param(*config);
+ struct wcd_gpio_pad *pad;
+ unsigned int arg;
+
+ pad = pctldev->desc->pins[pin].drv_data;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ arg = pad->pullup == WCD_GPIO_PULL_DOWN;
+ break;
+ case PIN_CONFIG_BIAS_DISABLE:
+ arg = pad->pullup = WCD_GPIO_BIAS_DISABLE;
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ arg = pad->pullup == WCD_GPIO_PULL_UP;
+ break;
+ case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+ arg = !pad->is_valid;
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ arg = pad->output_enabled;
+ break;
+ case PIN_CONFIG_OUTPUT:
+ arg = pad->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *config = pinconf_to_config_packed(param, arg);
+ return 0;
+}
+
+static int wcd_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
+ unsigned long *configs, unsigned int nconfs)
+{
+ struct wcd_gpio_priv *priv_data = pinctrl_dev_get_drvdata(pctldev);
+ struct wcd_gpio_pad *pad;
+ unsigned int param, arg;
+ int i, ret;
+
+ pad = pctldev->desc->pins[pin].drv_data;
+
+ for (i = 0; i < nconfs; i++) {
+ param = pinconf_to_config_param(configs[i]);
+ arg = pinconf_to_config_argument(configs[i]);
+
+ dev_dbg(priv_data->dev, "%s: param: %d arg: %d",
+ __func__, param, arg);
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ pad->pullup = WCD_GPIO_BIAS_DISABLE;
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ pad->pullup = WCD_GPIO_PULL_UP;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ pad->pullup = WCD_GPIO_PULL_DOWN;
+ break;
+ case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+ pad->is_valid = false;
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ pad->output_enabled = false;
+ break;
+ case PIN_CONFIG_OUTPUT:
+ pad->output_enabled = true;
+ pad->value = arg;
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ pad->strength = arg;
+ break;
+ default:
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ if (pad->output_enabled) {
+ ret = wcd_gpio_write(priv_data, pad, WCD_REG_DIR_CTL,
+ pad->output_enabled);
+ if (ret < 0)
+ goto done;
+ ret = wcd_gpio_write(priv_data, pad, WCD_REG_VAL_CTL,
+ pad->value);
+ } else
+ ret = wcd_gpio_write(priv_data, pad, WCD_REG_DIR_CTL,
+ pad->output_enabled);
+done:
+ return ret;
+}
+
+static const struct pinconf_ops wcd_pinconf_ops = {
+ .is_generic = true,
+ .pin_config_group_get = wcd_config_get,
+ .pin_config_group_set = wcd_config_set,
+};
+
+static int wcd_gpio_direction_input(struct gpio_chip *chip, unsigned int pin)
+{
+ struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip);
+ unsigned long config;
+
+ config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
+
+ return wcd_config_set(priv_data->ctrl, pin, &config, 1);
+}
+
+static int wcd_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int pin, int val)
+{
+ struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip);
+ unsigned long config;
+
+ config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
+
+ return wcd_config_set(priv_data->ctrl, pin, &config, 1);
+}
+
+static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin)
+{
+ struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip);
+ struct wcd_gpio_pad *pad;
+ int value;
+
+ pad = priv_data->ctrl->desc->pins[pin].drv_data;
+
+ if (!pad->is_valid)
+ return -EINVAL;
+
+ value = wcd_gpio_read(priv_data, pad, WCD_REG_VAL_CTL);
+ return value;
+}
+
+static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int value)
+{
+ struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip);
+ unsigned long config;
+
+ config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
+
+ wcd_config_set(priv_data->ctrl, pin, &config, 1);
+}
+
+static const struct gpio_chip wcd_gpio_chip = {
+ .direction_input = wcd_gpio_direction_input,
+ .direction_output = wcd_gpio_direction_output,
+ .get = wcd_gpio_get,
+ .set = wcd_gpio_set,
+};
+
+static int wcd_pinctrl_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pinctrl_pin_desc *pindesc;
+ struct pinctrl_desc *pctrldesc;
+ struct wcd_gpio_pad *pad, *pads;
+ struct wcd_gpio_priv *priv_data;
+ int ret, i, j;
+ u32 npins;
+ char **name;
+
+ ret = of_property_read_u32(dev->of_node, "qcom,num-gpios", &npins);
+ if (ret) {
+ dev_err(dev, "%s: Looking up %s property in node %s failed\n",
+ __func__, "qcom,num-gpios", dev->of_node->full_name);
+ ret = -EINVAL;
+ goto err_priv_alloc;
+ }
+ if (!npins) {
+ dev_err(dev, "%s: no.of pins are 0\n", __func__);
+ ret = -EINVAL;
+ goto err_priv_alloc;
+ }
+
+ priv_data = devm_kzalloc(dev, sizeof(*priv_data), GFP_KERNEL);
+ if (!priv_data) {
+ ret = -ENOMEM;
+ goto err_priv_alloc;
+ }
+
+ priv_data->dev = dev;
+ priv_data->map = dev_get_regmap(dev->parent, NULL);
+ if (!priv_data->map) {
+ dev_err(dev, "%s: failed to get regmap\n", __func__);
+ ret = -EINVAL;
+ goto err_regmap;
+ }
+
+ pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
+ if (!pindesc) {
+ ret = -ENOMEM;
+ goto err_pinsec_alloc;
+ }
+
+ pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
+ if (!pads) {
+ ret = -ENOMEM;
+ goto err_pads_alloc;
+ }
+
+ pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
+ if (!pctrldesc) {
+ ret = -ENOMEM;
+ goto err_pinctrl_alloc;
+ }
+
+ pctrldesc->pctlops = &wcd_pinctrl_ops;
+ pctrldesc->confops = &wcd_pinconf_ops;
+ pctrldesc->owner = THIS_MODULE;
+ pctrldesc->name = dev_name(dev);
+ pctrldesc->pins = pindesc;
+ pctrldesc->npins = npins;
+
+ name = devm_kcalloc(dev, npins, sizeof(char *), GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto err_name_alloc;
+ }
+ for (i = 0; i < npins; i++, pindesc++) {
+ name[i] = devm_kzalloc(dev, sizeof(char) * WCD_GPIO_STRING_LEN,
+ GFP_KERNEL);
+ if (!name[i]) {
+ ret = -ENOMEM;
+ goto err_pin;
+ }
+ pad = &pads[i];
+ pindesc->drv_data = pad;
+ pindesc->number = i;
+ snprintf(name[i], (WCD_GPIO_STRING_LEN - 1), "gpio%d", (i+1));
+ pindesc->name = name[i];
+ pad->offset = i;
+ pad->is_valid = true;
+ }
+
+ priv_data->chip = wcd_gpio_chip;
+ priv_data->chip.parent = dev;
+ priv_data->chip.base = -1;
+ priv_data->chip.ngpio = npins;
+ priv_data->chip.label = dev_name(dev);
+ priv_data->chip.of_gpio_n_cells = 2;
+ priv_data->chip.can_sleep = false;
+
+ priv_data->ctrl = devm_pinctrl_register(dev, pctrldesc, priv_data);
+ if (IS_ERR(priv_data->ctrl)) {
+ dev_err(dev, "%s: failed to register to pinctrl\n", __func__);
+ ret = PTR_ERR(priv_data->ctrl);
+ goto err_pin;
+ }
+
+ ret = gpiochip_add_data(&priv_data->chip, priv_data);
+ if (ret) {
+ dev_err(dev, "%s: can't add gpio chip\n", __func__);
+ goto err_pin;
+ }
+
+ ret = gpiochip_add_pin_range(&priv_data->chip, dev_name(dev), 0, 0,
+ npins);
+ if (ret) {
+ dev_err(dev, "%s: failed to add pin range\n", __func__);
+ goto err_range;
+ }
+ platform_set_drvdata(pdev, priv_data);
+
+ return 0;
+
+err_range:
+ gpiochip_remove(&priv_data->chip);
+err_pin:
+ for (j = 0; j < i; j++)
+ devm_kfree(dev, name[j]);
+ devm_kfree(dev, name);
+err_name_alloc:
+ devm_kfree(dev, pctrldesc);
+err_pinctrl_alloc:
+ devm_kfree(dev, pads);
+err_pads_alloc:
+ devm_kfree(dev, pindesc);
+err_pinsec_alloc:
+err_regmap:
+ devm_kfree(dev, priv_data);
+err_priv_alloc:
+ return ret;
+}
+
+static int wcd_pinctrl_remove(struct platform_device *pdev)
+{
+ struct wcd_gpio_priv *priv_data = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&priv_data->chip);
+
+ return 0;
+}
+
+static const struct of_device_id wcd_pinctrl_of_match[] = {
+ { .compatible = "qcom,wcd-pinctrl" },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, wcd_pinctrl_of_match);
+
+static struct platform_driver wcd_pinctrl_driver = {
+ .driver = {
+ .name = "qcom-wcd-pinctrl",
+ .of_match_table = wcd_pinctrl_of_match,
+ },
+ .probe = wcd_pinctrl_probe,
+ .remove = wcd_pinctrl_remove,
+};
+
+module_platform_driver(wcd_pinctrl_driver);
+
+MODULE_DESCRIPTION("Qualcomm Technologies, Inc WCD GPIO pin control driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c
index f3a8897..cf80ce1 100644
--- a/drivers/pinctrl/sh-pfc/core.c
+++ b/drivers/pinctrl/sh-pfc/core.c
@@ -389,6 +389,21 @@
return 0;
}
+const struct sh_pfc_bias_info *
+sh_pfc_pin_to_bias_info(const struct sh_pfc_bias_info *info,
+ unsigned int num, unsigned int pin)
+{
+ unsigned int i;
+
+ for (i = 0; i < num; i++)
+ if (info[i].pin == pin)
+ return &info[i];
+
+ WARN_ONCE(1, "Pin %u is not in bias info list\n", pin);
+
+ return NULL;
+}
+
static int sh_pfc_init_ranges(struct sh_pfc *pfc)
{
struct sh_pfc_pin_range *range;
diff --git a/drivers/pinctrl/sh-pfc/core.h b/drivers/pinctrl/sh-pfc/core.h
index 0bbdea58..6d598dd 100644
--- a/drivers/pinctrl/sh-pfc/core.h
+++ b/drivers/pinctrl/sh-pfc/core.h
@@ -33,4 +33,8 @@
int sh_pfc_get_pin_index(struct sh_pfc *pfc, unsigned int pin);
int sh_pfc_config_mux(struct sh_pfc *pfc, unsigned mark, int pinmux_type);
+const struct sh_pfc_bias_info *
+sh_pfc_pin_to_bias_info(const struct sh_pfc_bias_info *info,
+ unsigned int num, unsigned int pin);
+
#endif /* __SH_PFC_CORE_H__ */
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7795.c b/drivers/pinctrl/sh-pfc/pfc-r8a7795.c
index 2e8cc2a..84cee66 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7795.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7795.c
@@ -5188,184 +5188,183 @@
#define PU5 0x14
#define PU6 0x18
-static const struct {
- u16 reg : 11;
- u16 bit : 5;
-} pullups[] = {
- [RCAR_GP_PIN(2, 11)] = { PU0, 31 }, /* AVB_PHY_INT */
- [RCAR_GP_PIN(2, 10)] = { PU0, 30 }, /* AVB_MAGIC */
- [RCAR_GP_PIN(2, 9)] = { PU0, 29 }, /* AVB_MDC */
+static const struct sh_pfc_bias_info bias_info[] = {
+ { RCAR_GP_PIN(2, 11), PU0, 31 }, /* AVB_PHY_INT */
+ { RCAR_GP_PIN(2, 10), PU0, 30 }, /* AVB_MAGIC */
+ { RCAR_GP_PIN(2, 9), PU0, 29 }, /* AVB_MDC */
- [RCAR_GP_PIN(1, 19)] = { PU1, 31 }, /* A19 */
- [RCAR_GP_PIN(1, 18)] = { PU1, 30 }, /* A18 */
- [RCAR_GP_PIN(1, 17)] = { PU1, 29 }, /* A17 */
- [RCAR_GP_PIN(1, 16)] = { PU1, 28 }, /* A16 */
- [RCAR_GP_PIN(1, 15)] = { PU1, 27 }, /* A15 */
- [RCAR_GP_PIN(1, 14)] = { PU1, 26 }, /* A14 */
- [RCAR_GP_PIN(1, 13)] = { PU1, 25 }, /* A13 */
- [RCAR_GP_PIN(1, 12)] = { PU1, 24 }, /* A12 */
- [RCAR_GP_PIN(1, 11)] = { PU1, 23 }, /* A11 */
- [RCAR_GP_PIN(1, 10)] = { PU1, 22 }, /* A10 */
- [RCAR_GP_PIN(1, 9)] = { PU1, 21 }, /* A9 */
- [RCAR_GP_PIN(1, 8)] = { PU1, 20 }, /* A8 */
- [RCAR_GP_PIN(1, 7)] = { PU1, 19 }, /* A7 */
- [RCAR_GP_PIN(1, 6)] = { PU1, 18 }, /* A6 */
- [RCAR_GP_PIN(1, 5)] = { PU1, 17 }, /* A5 */
- [RCAR_GP_PIN(1, 4)] = { PU1, 16 }, /* A4 */
- [RCAR_GP_PIN(1, 3)] = { PU1, 15 }, /* A3 */
- [RCAR_GP_PIN(1, 2)] = { PU1, 14 }, /* A2 */
- [RCAR_GP_PIN(1, 1)] = { PU1, 13 }, /* A1 */
- [RCAR_GP_PIN(1, 0)] = { PU1, 12 }, /* A0 */
- [RCAR_GP_PIN(2, 8)] = { PU1, 11 }, /* PWM2_A */
- [RCAR_GP_PIN(2, 7)] = { PU1, 10 }, /* PWM1_A */
- [RCAR_GP_PIN(2, 6)] = { PU1, 9 }, /* PWM0 */
- [RCAR_GP_PIN(2, 5)] = { PU1, 8 }, /* IRQ5 */
- [RCAR_GP_PIN(2, 4)] = { PU1, 7 }, /* IRQ4 */
- [RCAR_GP_PIN(2, 3)] = { PU1, 6 }, /* IRQ3 */
- [RCAR_GP_PIN(2, 2)] = { PU1, 5 }, /* IRQ2 */
- [RCAR_GP_PIN(2, 1)] = { PU1, 4 }, /* IRQ1 */
- [RCAR_GP_PIN(2, 0)] = { PU1, 3 }, /* IRQ0 */
- [RCAR_GP_PIN(2, 14)] = { PU1, 2 }, /* AVB_AVTP_CAPTURE_A */
- [RCAR_GP_PIN(2, 13)] = { PU1, 1 }, /* AVB_AVTP_MATCH_A */
- [RCAR_GP_PIN(2, 12)] = { PU1, 0 }, /* AVB_LINK */
+ { RCAR_GP_PIN(1, 19), PU1, 31 }, /* A19 */
+ { RCAR_GP_PIN(1, 18), PU1, 30 }, /* A18 */
+ { RCAR_GP_PIN(1, 17), PU1, 29 }, /* A17 */
+ { RCAR_GP_PIN(1, 16), PU1, 28 }, /* A16 */
+ { RCAR_GP_PIN(1, 15), PU1, 27 }, /* A15 */
+ { RCAR_GP_PIN(1, 14), PU1, 26 }, /* A14 */
+ { RCAR_GP_PIN(1, 13), PU1, 25 }, /* A13 */
+ { RCAR_GP_PIN(1, 12), PU1, 24 }, /* A12 */
+ { RCAR_GP_PIN(1, 11), PU1, 23 }, /* A11 */
+ { RCAR_GP_PIN(1, 10), PU1, 22 }, /* A10 */
+ { RCAR_GP_PIN(1, 9), PU1, 21 }, /* A9 */
+ { RCAR_GP_PIN(1, 8), PU1, 20 }, /* A8 */
+ { RCAR_GP_PIN(1, 7), PU1, 19 }, /* A7 */
+ { RCAR_GP_PIN(1, 6), PU1, 18 }, /* A6 */
+ { RCAR_GP_PIN(1, 5), PU1, 17 }, /* A5 */
+ { RCAR_GP_PIN(1, 4), PU1, 16 }, /* A4 */
+ { RCAR_GP_PIN(1, 3), PU1, 15 }, /* A3 */
+ { RCAR_GP_PIN(1, 2), PU1, 14 }, /* A2 */
+ { RCAR_GP_PIN(1, 1), PU1, 13 }, /* A1 */
+ { RCAR_GP_PIN(1, 0), PU1, 12 }, /* A0 */
+ { RCAR_GP_PIN(2, 8), PU1, 11 }, /* PWM2_A */
+ { RCAR_GP_PIN(2, 7), PU1, 10 }, /* PWM1_A */
+ { RCAR_GP_PIN(2, 6), PU1, 9 }, /* PWM0 */
+ { RCAR_GP_PIN(2, 5), PU1, 8 }, /* IRQ5 */
+ { RCAR_GP_PIN(2, 4), PU1, 7 }, /* IRQ4 */
+ { RCAR_GP_PIN(2, 3), PU1, 6 }, /* IRQ3 */
+ { RCAR_GP_PIN(2, 2), PU1, 5 }, /* IRQ2 */
+ { RCAR_GP_PIN(2, 1), PU1, 4 }, /* IRQ1 */
+ { RCAR_GP_PIN(2, 0), PU1, 3 }, /* IRQ0 */
+ { RCAR_GP_PIN(2, 14), PU1, 2 }, /* AVB_AVTP_CAPTURE_A */
+ { RCAR_GP_PIN(2, 13), PU1, 1 }, /* AVB_AVTP_MATCH_A */
+ { RCAR_GP_PIN(2, 12), PU1, 0 }, /* AVB_LINK */
- [RCAR_GP_PIN(7, 3)] = { PU2, 29 }, /* HDMI1_CEC */
- [RCAR_GP_PIN(7, 2)] = { PU2, 28 }, /* HDMI0_CEC */
- [RCAR_GP_PIN(7, 1)] = { PU2, 27 }, /* AVS2 */
- [RCAR_GP_PIN(7, 0)] = { PU2, 26 }, /* AVS1 */
- [RCAR_GP_PIN(0, 15)] = { PU2, 25 }, /* D15 */
- [RCAR_GP_PIN(0, 14)] = { PU2, 24 }, /* D14 */
- [RCAR_GP_PIN(0, 13)] = { PU2, 23 }, /* D13 */
- [RCAR_GP_PIN(0, 12)] = { PU2, 22 }, /* D12 */
- [RCAR_GP_PIN(0, 11)] = { PU2, 21 }, /* D11 */
- [RCAR_GP_PIN(0, 10)] = { PU2, 20 }, /* D10 */
- [RCAR_GP_PIN(0, 9)] = { PU2, 19 }, /* D9 */
- [RCAR_GP_PIN(0, 8)] = { PU2, 18 }, /* D8 */
- [RCAR_GP_PIN(0, 7)] = { PU2, 17 }, /* D7 */
- [RCAR_GP_PIN(0, 6)] = { PU2, 16 }, /* D6 */
- [RCAR_GP_PIN(0, 5)] = { PU2, 15 }, /* D5 */
- [RCAR_GP_PIN(0, 4)] = { PU2, 14 }, /* D4 */
- [RCAR_GP_PIN(0, 3)] = { PU2, 13 }, /* D3 */
- [RCAR_GP_PIN(0, 2)] = { PU2, 12 }, /* D2 */
- [RCAR_GP_PIN(0, 1)] = { PU2, 11 }, /* D1 */
- [RCAR_GP_PIN(0, 0)] = { PU2, 10 }, /* D0 */
- [RCAR_GP_PIN(1, 27)] = { PU2, 8 }, /* EX_WAIT0_A */
- [RCAR_GP_PIN(1, 26)] = { PU2, 7 }, /* WE1_N */
- [RCAR_GP_PIN(1, 25)] = { PU2, 6 }, /* WE0_N */
- [RCAR_GP_PIN(1, 24)] = { PU2, 5 }, /* RD_WR_N */
- [RCAR_GP_PIN(1, 23)] = { PU2, 4 }, /* RD_N */
- [RCAR_GP_PIN(1, 22)] = { PU2, 3 }, /* BS_N */
- [RCAR_GP_PIN(1, 21)] = { PU2, 2 }, /* CS1_N_A26 */
- [RCAR_GP_PIN(1, 20)] = { PU2, 1 }, /* CS0_N */
+ { RCAR_GP_PIN(7, 3), PU2, 29 }, /* HDMI1_CEC */
+ { RCAR_GP_PIN(7, 2), PU2, 28 }, /* HDMI0_CEC */
+ { RCAR_GP_PIN(7, 1), PU2, 27 }, /* AVS2 */
+ { RCAR_GP_PIN(7, 0), PU2, 26 }, /* AVS1 */
+ { RCAR_GP_PIN(0, 15), PU2, 25 }, /* D15 */
+ { RCAR_GP_PIN(0, 14), PU2, 24 }, /* D14 */
+ { RCAR_GP_PIN(0, 13), PU2, 23 }, /* D13 */
+ { RCAR_GP_PIN(0, 12), PU2, 22 }, /* D12 */
+ { RCAR_GP_PIN(0, 11), PU2, 21 }, /* D11 */
+ { RCAR_GP_PIN(0, 10), PU2, 20 }, /* D10 */
+ { RCAR_GP_PIN(0, 9), PU2, 19 }, /* D9 */
+ { RCAR_GP_PIN(0, 8), PU2, 18 }, /* D8 */
+ { RCAR_GP_PIN(0, 7), PU2, 17 }, /* D7 */
+ { RCAR_GP_PIN(0, 6), PU2, 16 }, /* D6 */
+ { RCAR_GP_PIN(0, 5), PU2, 15 }, /* D5 */
+ { RCAR_GP_PIN(0, 4), PU2, 14 }, /* D4 */
+ { RCAR_GP_PIN(0, 3), PU2, 13 }, /* D3 */
+ { RCAR_GP_PIN(0, 2), PU2, 12 }, /* D2 */
+ { RCAR_GP_PIN(0, 1), PU2, 11 }, /* D1 */
+ { RCAR_GP_PIN(0, 0), PU2, 10 }, /* D0 */
+ { RCAR_GP_PIN(1, 27), PU2, 8 }, /* EX_WAIT0_A */
+ { RCAR_GP_PIN(1, 26), PU2, 7 }, /* WE1_N */
+ { RCAR_GP_PIN(1, 25), PU2, 6 }, /* WE0_N */
+ { RCAR_GP_PIN(1, 24), PU2, 5 }, /* RD_WR_N */
+ { RCAR_GP_PIN(1, 23), PU2, 4 }, /* RD_N */
+ { RCAR_GP_PIN(1, 22), PU2, 3 }, /* BS_N */
+ { RCAR_GP_PIN(1, 21), PU2, 2 }, /* CS1_N_A26 */
+ { RCAR_GP_PIN(1, 20), PU2, 1 }, /* CS0_N */
- [RCAR_GP_PIN(4, 9)] = { PU3, 31 }, /* SD3_DAT0 */
- [RCAR_GP_PIN(4, 8)] = { PU3, 30 }, /* SD3_CMD */
- [RCAR_GP_PIN(4, 7)] = { PU3, 29 }, /* SD3_CLK */
- [RCAR_GP_PIN(4, 6)] = { PU3, 28 }, /* SD2_DS */
- [RCAR_GP_PIN(4, 5)] = { PU3, 27 }, /* SD2_DAT3 */
- [RCAR_GP_PIN(4, 4)] = { PU3, 26 }, /* SD2_DAT2 */
- [RCAR_GP_PIN(4, 3)] = { PU3, 25 }, /* SD2_DAT1 */
- [RCAR_GP_PIN(4, 2)] = { PU3, 24 }, /* SD2_DAT0 */
- [RCAR_GP_PIN(4, 1)] = { PU3, 23 }, /* SD2_CMD */
- [RCAR_GP_PIN(4, 0)] = { PU3, 22 }, /* SD2_CLK */
- [RCAR_GP_PIN(3, 11)] = { PU3, 21 }, /* SD1_DAT3 */
- [RCAR_GP_PIN(3, 10)] = { PU3, 20 }, /* SD1_DAT2 */
- [RCAR_GP_PIN(3, 9)] = { PU3, 19 }, /* SD1_DAT1 */
- [RCAR_GP_PIN(3, 8)] = { PU3, 18 }, /* SD1_DAT0 */
- [RCAR_GP_PIN(3, 7)] = { PU3, 17 }, /* SD1_CMD */
- [RCAR_GP_PIN(3, 6)] = { PU3, 16 }, /* SD1_CLK */
- [RCAR_GP_PIN(3, 5)] = { PU3, 15 }, /* SD0_DAT3 */
- [RCAR_GP_PIN(3, 4)] = { PU3, 14 }, /* SD0_DAT2 */
- [RCAR_GP_PIN(3, 3)] = { PU3, 13 }, /* SD0_DAT1 */
- [RCAR_GP_PIN(3, 2)] = { PU3, 12 }, /* SD0_DAT0 */
- [RCAR_GP_PIN(3, 1)] = { PU3, 11 }, /* SD0_CMD */
- [RCAR_GP_PIN(3, 0)] = { PU3, 10 }, /* SD0_CLK */
+ { RCAR_GP_PIN(4, 9), PU3, 31 }, /* SD3_DAT0 */
+ { RCAR_GP_PIN(4, 8), PU3, 30 }, /* SD3_CMD */
+ { RCAR_GP_PIN(4, 7), PU3, 29 }, /* SD3_CLK */
+ { RCAR_GP_PIN(4, 6), PU3, 28 }, /* SD2_DS */
+ { RCAR_GP_PIN(4, 5), PU3, 27 }, /* SD2_DAT3 */
+ { RCAR_GP_PIN(4, 4), PU3, 26 }, /* SD2_DAT2 */
+ { RCAR_GP_PIN(4, 3), PU3, 25 }, /* SD2_DAT1 */
+ { RCAR_GP_PIN(4, 2), PU3, 24 }, /* SD2_DAT0 */
+ { RCAR_GP_PIN(4, 1), PU3, 23 }, /* SD2_CMD */
+ { RCAR_GP_PIN(4, 0), PU3, 22 }, /* SD2_CLK */
+ { RCAR_GP_PIN(3, 11), PU3, 21 }, /* SD1_DAT3 */
+ { RCAR_GP_PIN(3, 10), PU3, 20 }, /* SD1_DAT2 */
+ { RCAR_GP_PIN(3, 9), PU3, 19 }, /* SD1_DAT1 */
+ { RCAR_GP_PIN(3, 8), PU3, 18 }, /* SD1_DAT0 */
+ { RCAR_GP_PIN(3, 7), PU3, 17 }, /* SD1_CMD */
+ { RCAR_GP_PIN(3, 6), PU3, 16 }, /* SD1_CLK */
+ { RCAR_GP_PIN(3, 5), PU3, 15 }, /* SD0_DAT3 */
+ { RCAR_GP_PIN(3, 4), PU3, 14 }, /* SD0_DAT2 */
+ { RCAR_GP_PIN(3, 3), PU3, 13 }, /* SD0_DAT1 */
+ { RCAR_GP_PIN(3, 2), PU3, 12 }, /* SD0_DAT0 */
+ { RCAR_GP_PIN(3, 1), PU3, 11 }, /* SD0_CMD */
+ { RCAR_GP_PIN(3, 0), PU3, 10 }, /* SD0_CLK */
- [RCAR_GP_PIN(5, 19)] = { PU4, 31 }, /* MSIOF0_SS1 */
- [RCAR_GP_PIN(5, 18)] = { PU4, 30 }, /* MSIOF0_SYNC */
- [RCAR_GP_PIN(5, 17)] = { PU4, 29 }, /* MSIOF0_SCK */
- [RCAR_GP_PIN(5, 16)] = { PU4, 28 }, /* HRTS0_N */
- [RCAR_GP_PIN(5, 15)] = { PU4, 27 }, /* HCTS0_N */
- [RCAR_GP_PIN(5, 14)] = { PU4, 26 }, /* HTX0 */
- [RCAR_GP_PIN(5, 13)] = { PU4, 25 }, /* HRX0 */
- [RCAR_GP_PIN(5, 12)] = { PU4, 24 }, /* HSCK0 */
- [RCAR_GP_PIN(5, 11)] = { PU4, 23 }, /* RX2_A */
- [RCAR_GP_PIN(5, 10)] = { PU4, 22 }, /* TX2_A */
- [RCAR_GP_PIN(5, 9)] = { PU4, 21 }, /* SCK2 */
- [RCAR_GP_PIN(5, 8)] = { PU4, 20 }, /* RTS1_N_TANS */
- [RCAR_GP_PIN(5, 7)] = { PU4, 19 }, /* CTS1_N */
- [RCAR_GP_PIN(5, 6)] = { PU4, 18 }, /* TX1_A */
- [RCAR_GP_PIN(5, 5)] = { PU4, 17 }, /* RX1_A */
- [RCAR_GP_PIN(5, 4)] = { PU4, 16 }, /* RTS0_N_TANS */
- [RCAR_GP_PIN(5, 3)] = { PU4, 15 }, /* CTS0_N */
- [RCAR_GP_PIN(5, 2)] = { PU4, 14 }, /* TX0 */
- [RCAR_GP_PIN(5, 1)] = { PU4, 13 }, /* RX0 */
- [RCAR_GP_PIN(5, 0)] = { PU4, 12 }, /* SCK0 */
- [RCAR_GP_PIN(3, 15)] = { PU4, 11 }, /* SD1_WP */
- [RCAR_GP_PIN(3, 14)] = { PU4, 10 }, /* SD1_CD */
- [RCAR_GP_PIN(3, 13)] = { PU4, 9 }, /* SD0_WP */
- [RCAR_GP_PIN(3, 12)] = { PU4, 8 }, /* SD0_CD */
- [RCAR_GP_PIN(4, 17)] = { PU4, 7 }, /* SD3_DS */
- [RCAR_GP_PIN(4, 16)] = { PU4, 6 }, /* SD3_DAT7 */
- [RCAR_GP_PIN(4, 15)] = { PU4, 5 }, /* SD3_DAT6 */
- [RCAR_GP_PIN(4, 14)] = { PU4, 4 }, /* SD3_DAT5 */
- [RCAR_GP_PIN(4, 13)] = { PU4, 3 }, /* SD3_DAT4 */
- [RCAR_GP_PIN(4, 12)] = { PU4, 2 }, /* SD3_DAT3 */
- [RCAR_GP_PIN(4, 11)] = { PU4, 1 }, /* SD3_DAT2 */
- [RCAR_GP_PIN(4, 10)] = { PU4, 0 }, /* SD3_DAT1 */
+ { RCAR_GP_PIN(5, 19), PU4, 31 }, /* MSIOF0_SS1 */
+ { RCAR_GP_PIN(5, 18), PU4, 30 }, /* MSIOF0_SYNC */
+ { RCAR_GP_PIN(5, 17), PU4, 29 }, /* MSIOF0_SCK */
+ { RCAR_GP_PIN(5, 16), PU4, 28 }, /* HRTS0_N */
+ { RCAR_GP_PIN(5, 15), PU4, 27 }, /* HCTS0_N */
+ { RCAR_GP_PIN(5, 14), PU4, 26 }, /* HTX0 */
+ { RCAR_GP_PIN(5, 13), PU4, 25 }, /* HRX0 */
+ { RCAR_GP_PIN(5, 12), PU4, 24 }, /* HSCK0 */
+ { RCAR_GP_PIN(5, 11), PU4, 23 }, /* RX2_A */
+ { RCAR_GP_PIN(5, 10), PU4, 22 }, /* TX2_A */
+ { RCAR_GP_PIN(5, 9), PU4, 21 }, /* SCK2 */
+ { RCAR_GP_PIN(5, 8), PU4, 20 }, /* RTS1_N_TANS */
+ { RCAR_GP_PIN(5, 7), PU4, 19 }, /* CTS1_N */
+ { RCAR_GP_PIN(5, 6), PU4, 18 }, /* TX1_A */
+ { RCAR_GP_PIN(5, 5), PU4, 17 }, /* RX1_A */
+ { RCAR_GP_PIN(5, 4), PU4, 16 }, /* RTS0_N_TANS */
+ { RCAR_GP_PIN(5, 3), PU4, 15 }, /* CTS0_N */
+ { RCAR_GP_PIN(5, 2), PU4, 14 }, /* TX0 */
+ { RCAR_GP_PIN(5, 1), PU4, 13 }, /* RX0 */
+ { RCAR_GP_PIN(5, 0), PU4, 12 }, /* SCK0 */
+ { RCAR_GP_PIN(3, 15), PU4, 11 }, /* SD1_WP */
+ { RCAR_GP_PIN(3, 14), PU4, 10 }, /* SD1_CD */
+ { RCAR_GP_PIN(3, 13), PU4, 9 }, /* SD0_WP */
+ { RCAR_GP_PIN(3, 12), PU4, 8 }, /* SD0_CD */
+ { RCAR_GP_PIN(4, 17), PU4, 7 }, /* SD3_DS */
+ { RCAR_GP_PIN(4, 16), PU4, 6 }, /* SD3_DAT7 */
+ { RCAR_GP_PIN(4, 15), PU4, 5 }, /* SD3_DAT6 */
+ { RCAR_GP_PIN(4, 14), PU4, 4 }, /* SD3_DAT5 */
+ { RCAR_GP_PIN(4, 13), PU4, 3 }, /* SD3_DAT4 */
+ { RCAR_GP_PIN(4, 12), PU4, 2 }, /* SD3_DAT3 */
+ { RCAR_GP_PIN(4, 11), PU4, 1 }, /* SD3_DAT2 */
+ { RCAR_GP_PIN(4, 10), PU4, 0 }, /* SD3_DAT1 */
- [RCAR_GP_PIN(6, 24)] = { PU5, 31 }, /* USB0_PWEN */
- [RCAR_GP_PIN(6, 23)] = { PU5, 30 }, /* AUDIO_CLKB_B */
- [RCAR_GP_PIN(6, 22)] = { PU5, 29 }, /* AUDIO_CLKA_A */
- [RCAR_GP_PIN(6, 21)] = { PU5, 28 }, /* SSI_SDATA9_A */
- [RCAR_GP_PIN(6, 20)] = { PU5, 27 }, /* SSI_SDATA8 */
- [RCAR_GP_PIN(6, 19)] = { PU5, 26 }, /* SSI_SDATA7 */
- [RCAR_GP_PIN(6, 18)] = { PU5, 25 }, /* SSI_WS78 */
- [RCAR_GP_PIN(6, 17)] = { PU5, 24 }, /* SSI_SCK78 */
- [RCAR_GP_PIN(6, 16)] = { PU5, 23 }, /* SSI_SDATA6 */
- [RCAR_GP_PIN(6, 15)] = { PU5, 22 }, /* SSI_WS6 */
- [RCAR_GP_PIN(6, 14)] = { PU5, 21 }, /* SSI_SCK6 */
- [RCAR_GP_PIN(6, 13)] = { PU5, 20 }, /* SSI_SDATA5 */
- [RCAR_GP_PIN(6, 12)] = { PU5, 19 }, /* SSI_WS5 */
- [RCAR_GP_PIN(6, 11)] = { PU5, 18 }, /* SSI_SCK5 */
- [RCAR_GP_PIN(6, 10)] = { PU5, 17 }, /* SSI_SDATA4 */
- [RCAR_GP_PIN(6, 9)] = { PU5, 16 }, /* SSI_WS4 */
- [RCAR_GP_PIN(6, 8)] = { PU5, 15 }, /* SSI_SCK4 */
- [RCAR_GP_PIN(6, 7)] = { PU5, 14 }, /* SSI_SDATA3 */
- [RCAR_GP_PIN(6, 6)] = { PU5, 13 }, /* SSI_WS34 */
- [RCAR_GP_PIN(6, 5)] = { PU5, 12 }, /* SSI_SCK34 */
- [RCAR_GP_PIN(6, 4)] = { PU5, 11 }, /* SSI_SDATA2_A */
- [RCAR_GP_PIN(6, 3)] = { PU5, 10 }, /* SSI_SDATA1_A */
- [RCAR_GP_PIN(6, 2)] = { PU5, 9 }, /* SSI_SDATA0 */
- [RCAR_GP_PIN(6, 1)] = { PU5, 8 }, /* SSI_WS01239 */
- [RCAR_GP_PIN(6, 0)] = { PU5, 7 }, /* SSI_SCK01239 */
- [RCAR_GP_PIN(5, 25)] = { PU5, 5 }, /* MLB_DAT */
- [RCAR_GP_PIN(5, 24)] = { PU5, 4 }, /* MLB_SIG */
- [RCAR_GP_PIN(5, 23)] = { PU5, 3 }, /* MLB_CLK */
- [RCAR_GP_PIN(5, 22)] = { PU5, 2 }, /* MSIOF0_RXD */
- [RCAR_GP_PIN(5, 21)] = { PU5, 1 }, /* MSIOF0_SS2 */
- [RCAR_GP_PIN(5, 20)] = { PU5, 0 }, /* MSIOF0_TXD */
+ { RCAR_GP_PIN(6, 24), PU5, 31 }, /* USB0_PWEN */
+ { RCAR_GP_PIN(6, 23), PU5, 30 }, /* AUDIO_CLKB_B */
+ { RCAR_GP_PIN(6, 22), PU5, 29 }, /* AUDIO_CLKA_A */
+ { RCAR_GP_PIN(6, 21), PU5, 28 }, /* SSI_SDATA9_A */
+ { RCAR_GP_PIN(6, 20), PU5, 27 }, /* SSI_SDATA8 */
+ { RCAR_GP_PIN(6, 19), PU5, 26 }, /* SSI_SDATA7 */
+ { RCAR_GP_PIN(6, 18), PU5, 25 }, /* SSI_WS78 */
+ { RCAR_GP_PIN(6, 17), PU5, 24 }, /* SSI_SCK78 */
+ { RCAR_GP_PIN(6, 16), PU5, 23 }, /* SSI_SDATA6 */
+ { RCAR_GP_PIN(6, 15), PU5, 22 }, /* SSI_WS6 */
+ { RCAR_GP_PIN(6, 14), PU5, 21 }, /* SSI_SCK6 */
+ { RCAR_GP_PIN(6, 13), PU5, 20 }, /* SSI_SDATA5 */
+ { RCAR_GP_PIN(6, 12), PU5, 19 }, /* SSI_WS5 */
+ { RCAR_GP_PIN(6, 11), PU5, 18 }, /* SSI_SCK5 */
+ { RCAR_GP_PIN(6, 10), PU5, 17 }, /* SSI_SDATA4 */
+ { RCAR_GP_PIN(6, 9), PU5, 16 }, /* SSI_WS4 */
+ { RCAR_GP_PIN(6, 8), PU5, 15 }, /* SSI_SCK4 */
+ { RCAR_GP_PIN(6, 7), PU5, 14 }, /* SSI_SDATA3 */
+ { RCAR_GP_PIN(6, 6), PU5, 13 }, /* SSI_WS34 */
+ { RCAR_GP_PIN(6, 5), PU5, 12 }, /* SSI_SCK34 */
+ { RCAR_GP_PIN(6, 4), PU5, 11 }, /* SSI_SDATA2_A */
+ { RCAR_GP_PIN(6, 3), PU5, 10 }, /* SSI_SDATA1_A */
+ { RCAR_GP_PIN(6, 2), PU5, 9 }, /* SSI_SDATA0 */
+ { RCAR_GP_PIN(6, 1), PU5, 8 }, /* SSI_WS01239 */
+ { RCAR_GP_PIN(6, 0), PU5, 7 }, /* SSI_SCK01239 */
+ { RCAR_GP_PIN(5, 25), PU5, 5 }, /* MLB_DAT */
+ { RCAR_GP_PIN(5, 24), PU5, 4 }, /* MLB_SIG */
+ { RCAR_GP_PIN(5, 23), PU5, 3 }, /* MLB_CLK */
+ { RCAR_GP_PIN(5, 22), PU5, 2 }, /* MSIOF0_RXD */
+ { RCAR_GP_PIN(5, 21), PU5, 1 }, /* MSIOF0_SS2 */
+ { RCAR_GP_PIN(5, 20), PU5, 0 }, /* MSIOF0_TXD */
- [RCAR_GP_PIN(6, 31)] = { PU6, 6 }, /* USB31_OVC */
- [RCAR_GP_PIN(6, 30)] = { PU6, 5 }, /* USB31_PWEN */
- [RCAR_GP_PIN(6, 29)] = { PU6, 4 }, /* USB30_OVC */
- [RCAR_GP_PIN(6, 28)] = { PU6, 3 }, /* USB30_PWEN */
- [RCAR_GP_PIN(6, 27)] = { PU6, 2 }, /* USB1_OVC */
- [RCAR_GP_PIN(6, 26)] = { PU6, 1 }, /* USB1_PWEN */
- [RCAR_GP_PIN(6, 25)] = { PU6, 0 }, /* USB0_OVC */
+ { RCAR_GP_PIN(6, 31), PU6, 6 }, /* USB31_OVC */
+ { RCAR_GP_PIN(6, 30), PU6, 5 }, /* USB31_PWEN */
+ { RCAR_GP_PIN(6, 29), PU6, 4 }, /* USB30_OVC */
+ { RCAR_GP_PIN(6, 28), PU6, 3 }, /* USB30_PWEN */
+ { RCAR_GP_PIN(6, 27), PU6, 2 }, /* USB1_OVC */
+ { RCAR_GP_PIN(6, 26), PU6, 1 }, /* USB1_PWEN */
+ { RCAR_GP_PIN(6, 25), PU6, 0 }, /* USB0_OVC */
};
static unsigned int r8a7795_pinmux_get_bias(struct sh_pfc *pfc,
unsigned int pin)
{
+ const struct sh_pfc_bias_info *info;
u32 reg;
u32 bit;
- if (WARN_ON_ONCE(!pullups[pin].reg))
+ info = sh_pfc_pin_to_bias_info(bias_info, ARRAY_SIZE(bias_info), pin);
+ if (!info)
return PIN_CONFIG_BIAS_DISABLE;
- reg = pullups[pin].reg;
- bit = BIT(pullups[pin].bit);
+ reg = info->reg;
+ bit = BIT(info->bit);
if (sh_pfc_read_reg(pfc, PUEN + reg, 32) & bit) {
if (sh_pfc_read_reg(pfc, PUD + reg, 32) & bit)
@@ -5379,15 +5378,17 @@
static void r8a7795_pinmux_set_bias(struct sh_pfc *pfc, unsigned int pin,
unsigned int bias)
{
+ const struct sh_pfc_bias_info *info;
u32 enable, updown;
u32 reg;
u32 bit;
- if (WARN_ON_ONCE(!pullups[pin].reg))
+ info = sh_pfc_pin_to_bias_info(bias_info, ARRAY_SIZE(bias_info), pin);
+ if (!info)
return;
- reg = pullups[pin].reg;
- bit = BIT(pullups[pin].bit);
+ reg = info->reg;
+ bit = BIT(info->bit);
enable = sh_pfc_read_reg(pfc, PUEN + reg, 32) & ~bit;
if (bias != PIN_CONFIG_BIAS_DISABLE)
diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c
index c577258..fcacfa7 100644
--- a/drivers/pinctrl/sh-pfc/pinctrl.c
+++ b/drivers/pinctrl/sh-pfc/pinctrl.c
@@ -570,7 +570,8 @@
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
- return true;
+ return pin->configs &
+ (SH_PFC_PIN_CFG_PULL_UP | SH_PFC_PIN_CFG_PULL_DOWN);
case PIN_CONFIG_BIAS_PULL_UP:
return pin->configs & SH_PFC_PIN_CFG_PULL_UP;
diff --git a/drivers/pinctrl/sh-pfc/sh_pfc.h b/drivers/pinctrl/sh-pfc/sh_pfc.h
index 2345421..9556c17 100644
--- a/drivers/pinctrl/sh-pfc/sh_pfc.h
+++ b/drivers/pinctrl/sh-pfc/sh_pfc.h
@@ -189,6 +189,12 @@
unsigned long size;
};
+struct sh_pfc_bias_info {
+ u16 pin;
+ u16 reg : 11;
+ u16 bit : 5;
+};
+
struct sh_pfc_pin_range;
struct sh_pfc {
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c
index aa8bd97..9668633 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c
@@ -561,7 +561,7 @@
0, 0, 0, 0};
static const unsigned ether_rmii_pins[] = {30, 31, 32, 33, 34, 35, 36, 37, 39,
41, 42, 45};
-static const int ether_rmii_muxvals[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+static const int ether_rmii_muxvals[] = {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1};
static const unsigned i2c0_pins[] = {63, 64};
static const int i2c0_muxvals[] = {0, 0};
static const unsigned i2c1_pins[] = {65, 66};
diff --git a/drivers/platform/goldfish/Makefile b/drivers/platform/goldfish/Makefile
index d348712..277a820 100644
--- a/drivers/platform/goldfish/Makefile
+++ b/drivers/platform/goldfish/Makefile
@@ -2,4 +2,5 @@
# Makefile for Goldfish platform specific drivers
#
obj-$(CONFIG_GOLDFISH_BUS) += pdev_bus.o
-obj-$(CONFIG_GOLDFISH_PIPE) += goldfish_pipe.o
+obj-$(CONFIG_GOLDFISH_PIPE) += goldfish_pipe_all.o
+goldfish_pipe_all-objs := goldfish_pipe.o goldfish_pipe_v2.o
diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c
index 198d16d..00d8406 100644
--- a/drivers/platform/goldfish/goldfish_pipe.c
+++ b/drivers/platform/goldfish/goldfish_pipe.c
@@ -15,52 +15,11 @@
*
*/
-/* This source file contains the implementation of a special device driver
- * that intends to provide a *very* fast communication channel between the
- * guest system and the QEMU emulator.
- *
- * Usage from the guest is simply the following (error handling simplified):
- *
- * int fd = open("/dev/qemu_pipe",O_RDWR);
- * .... write() or read() through the pipe.
- *
- * This driver doesn't deal with the exact protocol used during the session.
- * It is intended to be as simple as something like:
- *
- * // do this _just_ after opening the fd to connect to a specific
- * // emulator service.
- * const char* msg = "<pipename>";
- * if (write(fd, msg, strlen(msg)+1) < 0) {
- * ... could not connect to <pipename> service
- * close(fd);
- * }
- *
- * // after this, simply read() and write() to communicate with the
- * // service. Exact protocol details left as an exercise to the reader.
- *
- * This driver is very fast because it doesn't copy any data through
- * intermediate buffers, since the emulator is capable of translating
- * guest user addresses into host ones.
- *
- * Note that we must however ensure that each user page involved in the
- * exchange is properly mapped during a transfer.
+/* This source file contains the implementation of the legacy version of
+ * a goldfish pipe device driver. See goldfish_pipe_v2.c for the current
+ * version.
*/
-
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/spinlock.h>
-#include <linux/miscdevice.h>
-#include <linux/platform_device.h>
-#include <linux/poll.h>
-#include <linux/sched.h>
-#include <linux/bitops.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/goldfish.h>
-#include <linux/dma-mapping.h>
-#include <linux/mm.h>
-#include <linux/acpi.h>
+#include "goldfish_pipe.h"
/*
* IMPORTANT: The following constants must match the ones used and defined
@@ -110,29 +69,15 @@
#define PIPE_WAKE_READ (1 << 1) /* pipe can now be read from */
#define PIPE_WAKE_WRITE (1 << 2) /* pipe can now be written to */
-struct access_params {
- unsigned long channel;
- u32 size;
- unsigned long address;
- u32 cmd;
- u32 result;
- /* reserved for future extension */
- u32 flags;
-};
+#define MAX_PAGES_TO_GRAB 32
-/* The global driver data. Holds a reference to the i/o page used to
- * communicate with the emulator, and a wake queue for blocked tasks
- * waiting to be awoken.
- */
-struct goldfish_pipe_dev {
- spinlock_t lock;
- unsigned char __iomem *base;
- struct access_params *aps;
- int irq;
- u32 version;
-};
+#define DEBUG 0
-static struct goldfish_pipe_dev pipe_dev[1];
+#if DEBUG
+#define DPRINT(...) { printk(KERN_ERR __VA_ARGS__); }
+#else
+#define DPRINT(...)
+#endif
/* This data type models a given pipe instance */
struct goldfish_pipe {
@@ -142,6 +87,15 @@
wait_queue_head_t wake_queue;
};
+struct access_params {
+ unsigned long channel;
+ u32 size;
+ unsigned long address;
+ u32 cmd;
+ u32 result;
+ /* reserved for future extension */
+ u32 flags;
+};
/* Bit flags for the 'flags' field */
enum {
@@ -231,8 +185,10 @@
if (valid_batchbuffer_addr(dev, aps)) {
dev->aps = aps;
return 0;
- } else
+ } else {
+ devm_kfree(&pdev->dev, aps);
return -1;
+ }
}
/* A value that will not be set by qemu emulator */
@@ -269,6 +225,7 @@
struct goldfish_pipe *pipe = filp->private_data;
struct goldfish_pipe_dev *dev = pipe->dev;
unsigned long address, address_end;
+ struct page* pages[MAX_PAGES_TO_GRAB] = {};
int count = 0, ret = -EINVAL;
/* If the emulator already closed the pipe, no need to go further */
@@ -293,45 +250,61 @@
while (address < address_end) {
unsigned long page_end = (address & PAGE_MASK) + PAGE_SIZE;
- unsigned long next = page_end < address_end ? page_end
- : address_end;
- unsigned long avail = next - address;
- int status, wakeBit;
- struct page *page;
-
- /* Either vaddr or paddr depending on the device version */
- unsigned long xaddr;
+ unsigned long next, avail;
+ int status, wakeBit, page_i, num_contiguous_pages;
+ long first_page, last_page, requested_pages;
+ unsigned long xaddr, xaddr_prev, xaddr_i;
/*
- * We grab the pages on a page-by-page basis in case user
- * space gives us a potentially huge buffer but the read only
- * returns a small amount, then there's no need to pin that
- * much memory to the process.
+ * Attempt to grab multiple physically contiguous pages.
*/
- down_read(¤t->mm->mmap_sem);
- ret = get_user_pages(address, 1, is_write ? 0 : FOLL_WRITE,
- &page, NULL);
- up_read(¤t->mm->mmap_sem);
- if (ret < 0)
- break;
-
- if (dev->version) {
- /* Device version 1 or newer (qemu-android) expects the
- * physical address.
- */
- xaddr = page_to_phys(page) | (address & ~PAGE_MASK);
- } else {
- /* Device version 0 (classic emulator) expects the
- * virtual address.
- */
- xaddr = address;
+ first_page = address & PAGE_MASK;
+ last_page = (address_end - 1) & PAGE_MASK;
+ requested_pages = ((last_page - first_page) >> PAGE_SHIFT) + 1;
+ if (requested_pages > MAX_PAGES_TO_GRAB) {
+ requested_pages = MAX_PAGES_TO_GRAB;
}
+ ret = get_user_pages_fast(first_page, requested_pages,
+ !is_write, pages);
+
+ DPRINT("%s: requested pages: %d %d %p\n", __FUNCTION__,
+ ret, requested_pages, first_page);
+ if (ret == 0) {
+ DPRINT("%s: error: (requested pages == 0) (wanted %d)\n",
+ __FUNCTION__, requested_pages);
+ mutex_unlock(&pipe->lock);
+ return ret;
+ }
+ if (ret < 0) {
+ DPRINT("%s: (requested pages < 0) %d \n",
+ __FUNCTION__, requested_pages);
+ mutex_unlock(&pipe->lock);
+ return ret;
+ }
+
+ xaddr = page_to_phys(pages[0]) | (address & ~PAGE_MASK);
+ xaddr_prev = xaddr;
+ num_contiguous_pages = ret == 0 ? 0 : 1;
+ for (page_i = 1; page_i < ret; page_i++) {
+ xaddr_i = page_to_phys(pages[page_i]) | (address & ~PAGE_MASK);
+ if (xaddr_i == xaddr_prev + PAGE_SIZE) {
+ page_end += PAGE_SIZE;
+ xaddr_prev = xaddr_i;
+ num_contiguous_pages++;
+ } else {
+ DPRINT("%s: discontinuous page boundary: %d pages instead\n",
+ __FUNCTION__, page_i);
+ break;
+ }
+ }
+ next = page_end < address_end ? page_end : address_end;
+ avail = next - address;
/* Now, try to transfer the bytes in the current page */
spin_lock_irqsave(&dev->lock, irq_flags);
if (access_with_param(dev,
- is_write ? CMD_WRITE_BUFFER : CMD_READ_BUFFER,
- xaddr, avail, pipe, &status)) {
+ is_write ? CMD_WRITE_BUFFER : CMD_READ_BUFFER,
+ xaddr, avail, pipe, &status)) {
gf_write_ptr(pipe, dev->base + PIPE_REG_CHANNEL,
dev->base + PIPE_REG_CHANNEL_HIGH);
writel(avail, dev->base + PIPE_REG_SIZE);
@@ -344,9 +317,13 @@
}
spin_unlock_irqrestore(&dev->lock, irq_flags);
- if (status > 0 && !is_write)
- set_page_dirty(page);
- put_page(page);
+ for (page_i = 0; page_i < ret; page_i++) {
+ if (status > 0 && !is_write &&
+ page_i < num_contiguous_pages) {
+ set_page_dirty(pages[page_i]);
+ }
+ put_page(pages[page_i]);
+ }
if (status > 0) { /* Correct transfer */
count += status;
@@ -368,7 +345,7 @@
*/
if (status != PIPE_ERROR_AGAIN)
pr_info_ratelimited("goldfish_pipe: backend returned error %d on %s\n",
- status, is_write ? "write" : "read");
+ status, is_write ? "write" : "read");
ret = 0;
break;
}
@@ -378,7 +355,7 @@
* non-blocking mode, just return the error code.
*/
if (status != PIPE_ERROR_AGAIN ||
- (filp->f_flags & O_NONBLOCK) != 0) {
+ (filp->f_flags & O_NONBLOCK) != 0) {
ret = goldfish_pipe_error_convert(status);
break;
}
@@ -392,7 +369,7 @@
/* Tell the emulator we're going to wait for a wake event */
goldfish_cmd(pipe,
- is_write ? CMD_WAKE_ON_WRITE : CMD_WAKE_ON_READ);
+ is_write ? CMD_WAKE_ON_WRITE : CMD_WAKE_ON_READ);
/* Unlock the pipe, then wait for the wake signal */
mutex_unlock(&pipe->lock);
@@ -538,6 +515,8 @@
pipe->dev = dev;
mutex_init(&pipe->lock);
+ DPRINT("%s: call. pipe_dev pipe_dev=0x%lx new_pipe_addr=0x%lx file=0x%lx\n", __FUNCTION__, pipe_dev, pipe, file);
+ // spin lock init, write head of list, i guess
init_waitqueue_head(&pipe->wake_queue);
/*
@@ -560,6 +539,7 @@
{
struct goldfish_pipe *pipe = filp->private_data;
+ DPRINT("%s: call. pipe=0x%lx file=0x%lx\n", __FUNCTION__, pipe, filp);
/* The guest is closing the channel, so tell the emulator right now */
goldfish_cmd(pipe, CMD_CLOSE);
kfree(pipe);
@@ -576,73 +556,35 @@
.release = goldfish_pipe_release,
};
-static struct miscdevice goldfish_pipe_device = {
+static struct miscdevice goldfish_pipe_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "goldfish_pipe",
.fops = &goldfish_pipe_fops,
};
-static int goldfish_pipe_probe(struct platform_device *pdev)
+int goldfish_pipe_device_init_v1(struct platform_device *pdev)
{
- int err;
- struct resource *r;
struct goldfish_pipe_dev *dev = pipe_dev;
-
- /* not thread safe, but this should not happen */
- WARN_ON(dev->base != NULL);
-
- spin_lock_init(&dev->lock);
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (r == NULL || resource_size(r) < PAGE_SIZE) {
- dev_err(&pdev->dev, "can't allocate i/o page\n");
- return -EINVAL;
- }
- dev->base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
- if (dev->base == NULL) {
- dev_err(&pdev->dev, "ioremap failed\n");
- return -EINVAL;
- }
-
- r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (r == NULL) {
- err = -EINVAL;
- goto error;
- }
- dev->irq = r->start;
-
- err = devm_request_irq(&pdev->dev, dev->irq, goldfish_pipe_interrupt,
+ int err = devm_request_irq(&pdev->dev, dev->irq, goldfish_pipe_interrupt,
IRQF_SHARED, "goldfish_pipe", dev);
if (err) {
- dev_err(&pdev->dev, "unable to allocate IRQ\n");
- goto error;
+ dev_err(&pdev->dev, "unable to allocate IRQ for v1\n");
+ return err;
}
- err = misc_register(&goldfish_pipe_device);
+ err = misc_register(&goldfish_pipe_dev);
if (err) {
- dev_err(&pdev->dev, "unable to register device\n");
- goto error;
+ dev_err(&pdev->dev, "unable to register v1 device\n");
+ return err;
}
+
setup_access_params_addr(pdev, dev);
-
- /* Although the pipe device in the classic Android emulator does not
- * recognize the 'version' register, it won't treat this as an error
- * either and will simply return 0, which is fine.
- */
- dev->version = readl(dev->base + PIPE_REG_VERSION);
return 0;
-
-error:
- dev->base = NULL;
- return err;
}
-static int goldfish_pipe_remove(struct platform_device *pdev)
+void goldfish_pipe_device_deinit_v1(struct platform_device *pdev)
{
- struct goldfish_pipe_dev *dev = pipe_dev;
- misc_deregister(&goldfish_pipe_device);
- dev->base = NULL;
- return 0;
+ misc_deregister(&goldfish_pipe_dev);
}
static const struct acpi_device_id goldfish_pipe_acpi_match[] = {
diff --git a/drivers/platform/goldfish/goldfish_pipe.h b/drivers/platform/goldfish/goldfish_pipe.h
new file mode 100644
index 0000000..6cd1b63
--- /dev/null
+++ b/drivers/platform/goldfish/goldfish_pipe.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef GOLDFISH_PIPE_H
+#define GOLDFISH_PIPE_H
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/goldfish.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/acpi.h>
+
+
+/* Initialize the legacy version of the pipe device driver */
+int goldfish_pipe_device_init_v1(struct platform_device *pdev);
+
+/* Deinitialize the legacy version of the pipe device driver */
+void goldfish_pipe_device_deinit_v1(struct platform_device *pdev);
+
+/* Forward declarations for the device struct */
+struct goldfish_pipe;
+struct goldfish_pipe_device_buffers;
+
+/* The global driver data. Holds a reference to the i/o page used to
+ * communicate with the emulator, and a wake queue for blocked tasks
+ * waiting to be awoken.
+ */
+struct goldfish_pipe_dev {
+ /*
+ * Global device spinlock. Protects the following members:
+ * - pipes, pipes_capacity
+ * - [*pipes, *pipes + pipes_capacity) - array data
+ * - first_signalled_pipe,
+ * goldfish_pipe::prev_signalled,
+ * goldfish_pipe::next_signalled,
+ * goldfish_pipe::signalled_flags - all singnalled-related fields,
+ * in all allocated pipes
+ * - open_command_params - PIPE_CMD_OPEN-related buffers
+ *
+ * It looks like a lot of different fields, but the trick is that the only
+ * operation that happens often is the signalled pipes array manipulation.
+ * That's why it's OK for now to keep the rest of the fields under the same
+ * lock. If we notice too much contention because of PIPE_CMD_OPEN,
+ * then we should add a separate lock there.
+ */
+ spinlock_t lock;
+
+ /*
+ * Array of the pipes of |pipes_capacity| elements,
+ * indexed by goldfish_pipe::id
+ */
+ struct goldfish_pipe **pipes;
+ u32 pipes_capacity;
+
+ /* Pointers to the buffers host uses for interaction with this driver */
+ struct goldfish_pipe_dev_buffers *buffers;
+
+ /* Head of a doubly linked list of signalled pipes */
+ struct goldfish_pipe *first_signalled_pipe;
+
+ /* Some device-specific data */
+ int irq;
+ int version;
+ unsigned char __iomem *base;
+
+ /* v1-specific access parameters */
+ struct access_params *aps;
+};
+
+extern struct goldfish_pipe_dev pipe_dev[1];
+
+#endif /* GOLDFISH_PIPE_H */
diff --git a/drivers/platform/goldfish/goldfish_pipe_v2.c b/drivers/platform/goldfish/goldfish_pipe_v2.c
new file mode 100644
index 0000000..ad373ed
--- /dev/null
+++ b/drivers/platform/goldfish/goldfish_pipe_v2.c
@@ -0,0 +1,889 @@
+/*
+ * Copyright (C) 2012 Intel, Inc.
+ * Copyright (C) 2013 Intel, Inc.
+ * Copyright (C) 2014 Linaro Limited
+ * Copyright (C) 2011-2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/* This source file contains the implementation of a special device driver
+ * that intends to provide a *very* fast communication channel between the
+ * guest system and the QEMU emulator.
+ *
+ * Usage from the guest is simply the following (error handling simplified):
+ *
+ * int fd = open("/dev/qemu_pipe",O_RDWR);
+ * .... write() or read() through the pipe.
+ *
+ * This driver doesn't deal with the exact protocol used during the session.
+ * It is intended to be as simple as something like:
+ *
+ * // do this _just_ after opening the fd to connect to a specific
+ * // emulator service.
+ * const char* msg = "<pipename>";
+ * if (write(fd, msg, strlen(msg)+1) < 0) {
+ * ... could not connect to <pipename> service
+ * close(fd);
+ * }
+ *
+ * // after this, simply read() and write() to communicate with the
+ * // service. Exact protocol details left as an exercise to the reader.
+ *
+ * This driver is very fast because it doesn't copy any data through
+ * intermediate buffers, since the emulator is capable of translating
+ * guest user addresses into host ones.
+ *
+ * Note that we must however ensure that each user page involved in the
+ * exchange is properly mapped during a transfer.
+ */
+
+#include "goldfish_pipe.h"
+
+
+/*
+ * Update this when something changes in the driver's behavior so the host
+ * can benefit from knowing it
+ */
+enum {
+ PIPE_DRIVER_VERSION = 2,
+ PIPE_CURRENT_DEVICE_VERSION = 2
+};
+
+/*
+ * IMPORTANT: The following constants must match the ones used and defined
+ * in external/qemu/hw/goldfish_pipe.c in the Android source tree.
+ */
+
+/* List of bitflags returned in status of CMD_POLL command */
+enum PipePollFlags {
+ PIPE_POLL_IN = 1 << 0,
+ PIPE_POLL_OUT = 1 << 1,
+ PIPE_POLL_HUP = 1 << 2
+};
+
+/* Possible status values used to signal errors - see goldfish_pipe_error_convert */
+enum PipeErrors {
+ PIPE_ERROR_INVAL = -1,
+ PIPE_ERROR_AGAIN = -2,
+ PIPE_ERROR_NOMEM = -3,
+ PIPE_ERROR_IO = -4
+};
+
+/* Bit-flags used to signal events from the emulator */
+enum PipeWakeFlags {
+ PIPE_WAKE_CLOSED = 1 << 0, /* emulator closed pipe */
+ PIPE_WAKE_READ = 1 << 1, /* pipe can now be read from */
+ PIPE_WAKE_WRITE = 1 << 2 /* pipe can now be written to */
+};
+
+/* Bit flags for the 'flags' field */
+enum PipeFlagsBits {
+ BIT_CLOSED_ON_HOST = 0, /* pipe closed by host */
+ BIT_WAKE_ON_WRITE = 1, /* want to be woken on writes */
+ BIT_WAKE_ON_READ = 2, /* want to be woken on reads */
+};
+
+enum PipeRegs {
+ PIPE_REG_CMD = 0,
+
+ PIPE_REG_SIGNAL_BUFFER_HIGH = 4,
+ PIPE_REG_SIGNAL_BUFFER = 8,
+ PIPE_REG_SIGNAL_BUFFER_COUNT = 12,
+
+ PIPE_REG_OPEN_BUFFER_HIGH = 20,
+ PIPE_REG_OPEN_BUFFER = 24,
+
+ PIPE_REG_VERSION = 36,
+
+ PIPE_REG_GET_SIGNALLED = 48,
+};
+
+enum PipeCmdCode {
+ PIPE_CMD_OPEN = 1, /* to be used by the pipe device itself */
+ PIPE_CMD_CLOSE,
+ PIPE_CMD_POLL,
+ PIPE_CMD_WRITE,
+ PIPE_CMD_WAKE_ON_WRITE,
+ PIPE_CMD_READ,
+ PIPE_CMD_WAKE_ON_READ,
+
+ /*
+ * TODO(zyy): implement a deferred read/write execution to allow parallel
+ * processing of pipe operations on the host.
+ */
+ PIPE_CMD_WAKE_ON_DONE_IO,
+};
+
+enum {
+ MAX_BUFFERS_PER_COMMAND = 336,
+ MAX_SIGNALLED_PIPES = 64,
+ INITIAL_PIPES_CAPACITY = 64
+};
+
+struct goldfish_pipe_dev;
+struct goldfish_pipe;
+struct goldfish_pipe_command;
+
+/* A per-pipe command structure, shared with the host */
+struct goldfish_pipe_command {
+ s32 cmd; /* PipeCmdCode, guest -> host */
+ s32 id; /* pipe id, guest -> host */
+ s32 status; /* command execution status, host -> guest */
+ s32 reserved; /* to pad to 64-bit boundary */
+ union {
+ /* Parameters for PIPE_CMD_{READ,WRITE} */
+ struct {
+ u32 buffers_count; /* number of buffers, guest -> host */
+ s32 consumed_size; /* number of consumed bytes, host -> guest */
+ u64 ptrs[MAX_BUFFERS_PER_COMMAND]; /* buffer pointers, guest -> host */
+ u32 sizes[MAX_BUFFERS_PER_COMMAND]; /* buffer sizes, guest -> host */
+ } rw_params;
+ };
+};
+
+/* A single signalled pipe information */
+struct signalled_pipe_buffer {
+ u32 id;
+ u32 flags;
+};
+
+/* Parameters for the PIPE_CMD_OPEN command */
+struct open_command_param {
+ u64 command_buffer_ptr;
+ u32 rw_params_max_count;
+};
+
+/* Device-level set of buffers shared with the host */
+struct goldfish_pipe_dev_buffers {
+ struct open_command_param open_command_params;
+ struct signalled_pipe_buffer signalled_pipe_buffers[MAX_SIGNALLED_PIPES];
+};
+
+/* This data type models a given pipe instance */
+struct goldfish_pipe {
+ u32 id; /* pipe ID - index into goldfish_pipe_dev::pipes array */
+ unsigned long flags; /* The wake flags pipe is waiting for
+ * Note: not protected with any lock, uses atomic operations
+ * and barriers to make it thread-safe.
+ */
+ unsigned long signalled_flags; /* wake flags host have signalled,
+ * - protected by goldfish_pipe_dev::lock */
+
+ struct goldfish_pipe_command *command_buffer; /* A pointer to command buffer */
+
+ /* doubly linked list of signalled pipes, protected by goldfish_pipe_dev::lock */
+ struct goldfish_pipe *prev_signalled;
+ struct goldfish_pipe *next_signalled;
+
+ /*
+ * A pipe's own lock. Protects the following:
+ * - *command_buffer - makes sure a command can safely write its parameters
+ * to the host and read the results back.
+ */
+ struct mutex lock;
+
+ wait_queue_head_t wake_queue; /* A wake queue for sleeping until host signals an event */
+ struct goldfish_pipe_dev *dev; /* Pointer to the parent goldfish_pipe_dev instance */
+};
+
+struct goldfish_pipe_dev pipe_dev[1] = {};
+
+static int goldfish_cmd_locked(struct goldfish_pipe *pipe, enum PipeCmdCode cmd)
+{
+ pipe->command_buffer->cmd = cmd;
+ pipe->command_buffer->status = PIPE_ERROR_INVAL; /* failure by default */
+ writel(pipe->id, pipe->dev->base + PIPE_REG_CMD);
+ return pipe->command_buffer->status;
+}
+
+static int goldfish_cmd(struct goldfish_pipe *pipe, enum PipeCmdCode cmd)
+{
+ int status;
+ if (mutex_lock_interruptible(&pipe->lock))
+ return PIPE_ERROR_IO;
+ status = goldfish_cmd_locked(pipe, cmd);
+ mutex_unlock(&pipe->lock);
+ return status;
+}
+
+/*
+ * This function converts an error code returned by the emulator through
+ * the PIPE_REG_STATUS i/o register into a valid negative errno value.
+ */
+static int goldfish_pipe_error_convert(int status)
+{
+ switch (status) {
+ case PIPE_ERROR_AGAIN:
+ return -EAGAIN;
+ case PIPE_ERROR_NOMEM:
+ return -ENOMEM;
+ case PIPE_ERROR_IO:
+ return -EIO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pin_user_pages(unsigned long first_page, unsigned long last_page,
+ unsigned last_page_size, int is_write,
+ struct page *pages[MAX_BUFFERS_PER_COMMAND], unsigned *iter_last_page_size)
+{
+ int ret;
+ int requested_pages = ((last_page - first_page) >> PAGE_SHIFT) + 1;
+ if (requested_pages > MAX_BUFFERS_PER_COMMAND) {
+ requested_pages = MAX_BUFFERS_PER_COMMAND;
+ *iter_last_page_size = PAGE_SIZE;
+ } else {
+ *iter_last_page_size = last_page_size;
+ }
+
+ ret = get_user_pages_fast(
+ first_page, requested_pages, !is_write, pages);
+ if (ret <= 0)
+ return -EFAULT;
+ if (ret < requested_pages)
+ *iter_last_page_size = PAGE_SIZE;
+ return ret;
+
+}
+
+static void release_user_pages(struct page **pages, int pages_count,
+ int is_write, s32 consumed_size)
+{
+ int i;
+ for (i = 0; i < pages_count; i++) {
+ if (!is_write && consumed_size > 0) {
+ set_page_dirty(pages[i]);
+ }
+ put_page(pages[i]);
+ }
+}
+
+/* Populate the call parameters, merging adjacent pages together */
+static void populate_rw_params(
+ struct page **pages, int pages_count,
+ unsigned long address, unsigned long address_end,
+ unsigned long first_page, unsigned long last_page,
+ unsigned iter_last_page_size, int is_write,
+ struct goldfish_pipe_command *command)
+{
+ /*
+ * Process the first page separately - it's the only page that
+ * needs special handling for its start address.
+ */
+ unsigned long xaddr = page_to_phys(pages[0]);
+ unsigned long xaddr_prev = xaddr;
+ int buffer_idx = 0;
+ int i = 1;
+ int size_on_page = first_page == last_page
+ ? (int)(address_end - address)
+ : (PAGE_SIZE - (address & ~PAGE_MASK));
+ command->rw_params.ptrs[0] = (u64)(xaddr | (address & ~PAGE_MASK));
+ command->rw_params.sizes[0] = size_on_page;
+ for (; i < pages_count; ++i) {
+ xaddr = page_to_phys(pages[i]);
+ size_on_page = (i == pages_count - 1) ? iter_last_page_size : PAGE_SIZE;
+ if (xaddr == xaddr_prev + PAGE_SIZE) {
+ command->rw_params.sizes[buffer_idx] += size_on_page;
+ } else {
+ ++buffer_idx;
+ command->rw_params.ptrs[buffer_idx] = (u64)xaddr;
+ command->rw_params.sizes[buffer_idx] = size_on_page;
+ }
+ xaddr_prev = xaddr;
+ }
+ command->rw_params.buffers_count = buffer_idx + 1;
+}
+
+static int transfer_max_buffers(struct goldfish_pipe* pipe,
+ unsigned long address, unsigned long address_end, int is_write,
+ unsigned long last_page, unsigned int last_page_size,
+ s32* consumed_size, int* status)
+{
+ struct page *pages[MAX_BUFFERS_PER_COMMAND];
+ unsigned long first_page = address & PAGE_MASK;
+ unsigned int iter_last_page_size;
+ int pages_count = pin_user_pages(first_page, last_page,
+ last_page_size, is_write,
+ pages, &iter_last_page_size);
+ if (pages_count < 0)
+ return pages_count;
+
+ /* Serialize access to the pipe command buffers */
+ if (mutex_lock_interruptible(&pipe->lock))
+ return -ERESTARTSYS;
+
+ populate_rw_params(pages, pages_count, address, address_end,
+ first_page, last_page, iter_last_page_size, is_write,
+ pipe->command_buffer);
+
+ /* Transfer the data */
+ *status = goldfish_cmd_locked(pipe,
+ is_write ? PIPE_CMD_WRITE : PIPE_CMD_READ);
+
+ *consumed_size = pipe->command_buffer->rw_params.consumed_size;
+
+ mutex_unlock(&pipe->lock);
+
+ release_user_pages(pages, pages_count, is_write, *consumed_size);
+
+ return 0;
+}
+
+static int wait_for_host_signal(struct goldfish_pipe *pipe, int is_write)
+{
+ u32 wakeBit = is_write ? BIT_WAKE_ON_WRITE : BIT_WAKE_ON_READ;
+ set_bit(wakeBit, &pipe->flags);
+
+ /* Tell the emulator we're going to wait for a wake event */
+ (void)goldfish_cmd(pipe,
+ is_write ? PIPE_CMD_WAKE_ON_WRITE : PIPE_CMD_WAKE_ON_READ);
+
+ while (test_bit(wakeBit, &pipe->flags)) {
+ if (wait_event_interruptible(
+ pipe->wake_queue,
+ !test_bit(wakeBit, &pipe->flags)))
+ return -ERESTARTSYS;
+
+ if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags))
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static ssize_t goldfish_pipe_read_write(struct file *filp,
+ char __user *buffer, size_t bufflen, int is_write)
+{
+ struct goldfish_pipe *pipe = filp->private_data;
+ int count = 0, ret = -EINVAL;
+ unsigned long address, address_end, last_page;
+ unsigned int last_page_size;
+
+ /* If the emulator already closed the pipe, no need to go further */
+ if (unlikely(test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)))
+ return -EIO;
+ /* Null reads or writes succeeds */
+ if (unlikely(bufflen == 0))
+ return 0;
+ /* Check the buffer range for access */
+ if (unlikely(!access_ok(is_write ? VERIFY_WRITE : VERIFY_READ,
+ buffer, bufflen)))
+ return -EFAULT;
+
+ address = (unsigned long)buffer;
+ address_end = address + bufflen;
+ last_page = (address_end - 1) & PAGE_MASK;
+ last_page_size = ((address_end - 1) & ~PAGE_MASK) + 1;
+
+ while (address < address_end) {
+ s32 consumed_size;
+ int status;
+ ret = transfer_max_buffers(pipe, address, address_end, is_write,
+ last_page, last_page_size, &consumed_size, &status);
+ if (ret < 0)
+ break;
+
+ if (consumed_size > 0) {
+ /* No matter what's the status, we've transfered something */
+ count += consumed_size;
+ address += consumed_size;
+ }
+ if (status > 0)
+ continue;
+ if (status == 0) {
+ /* EOF */
+ ret = 0;
+ break;
+ }
+ if (count > 0) {
+ /*
+ * An error occured, but we already transfered
+ * something on one of the previous iterations.
+ * Just return what we already copied and log this
+ * err.
+ */
+ if (status != PIPE_ERROR_AGAIN)
+ pr_info_ratelimited("goldfish_pipe: backend error %d on %s\n",
+ status, is_write ? "write" : "read");
+ break;
+ }
+
+ /*
+ * If the error is not PIPE_ERROR_AGAIN, or if we are in
+ * non-blocking mode, just return the error code.
+ */
+ if (status != PIPE_ERROR_AGAIN || (filp->f_flags & O_NONBLOCK) != 0) {
+ ret = goldfish_pipe_error_convert(status);
+ break;
+ }
+
+ status = wait_for_host_signal(pipe, is_write);
+ if (status < 0)
+ return status;
+ }
+
+ if (count > 0)
+ return count;
+ return ret;
+}
+
+static ssize_t goldfish_pipe_read(struct file *filp, char __user *buffer,
+ size_t bufflen, loff_t *ppos)
+{
+ return goldfish_pipe_read_write(filp, buffer, bufflen, /* is_write */ 0);
+}
+
+static ssize_t goldfish_pipe_write(struct file *filp,
+ const char __user *buffer, size_t bufflen,
+ loff_t *ppos)
+{
+ return goldfish_pipe_read_write(filp,
+ /* cast away the const */(char __user *)buffer, bufflen,
+ /* is_write */ 1);
+}
+
+static unsigned int goldfish_pipe_poll(struct file *filp, poll_table *wait)
+{
+ struct goldfish_pipe *pipe = filp->private_data;
+ unsigned int mask = 0;
+ int status;
+
+ poll_wait(filp, &pipe->wake_queue, wait);
+
+ status = goldfish_cmd(pipe, PIPE_CMD_POLL);
+ if (status < 0) {
+ return -ERESTARTSYS;
+ }
+
+ if (status & PIPE_POLL_IN)
+ mask |= POLLIN | POLLRDNORM;
+ if (status & PIPE_POLL_OUT)
+ mask |= POLLOUT | POLLWRNORM;
+ if (status & PIPE_POLL_HUP)
+ mask |= POLLHUP;
+ if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags))
+ mask |= POLLERR;
+
+ return mask;
+}
+
+static void signalled_pipes_add_locked(struct goldfish_pipe_dev *dev,
+ u32 id, u32 flags)
+{
+ struct goldfish_pipe *pipe;
+
+ BUG_ON(id >= dev->pipes_capacity);
+
+ pipe = dev->pipes[id];
+ if (!pipe)
+ return;
+ pipe->signalled_flags |= flags;
+
+ if (pipe->prev_signalled || pipe->next_signalled
+ || dev->first_signalled_pipe == pipe)
+ return; /* already in the list */
+ pipe->next_signalled = dev->first_signalled_pipe;
+ if (dev->first_signalled_pipe) {
+ dev->first_signalled_pipe->prev_signalled = pipe;
+ }
+ dev->first_signalled_pipe = pipe;
+}
+
+static void signalled_pipes_remove_locked(struct goldfish_pipe_dev *dev,
+ struct goldfish_pipe *pipe) {
+ if (pipe->prev_signalled)
+ pipe->prev_signalled->next_signalled = pipe->next_signalled;
+ if (pipe->next_signalled)
+ pipe->next_signalled->prev_signalled = pipe->prev_signalled;
+ if (pipe == dev->first_signalled_pipe)
+ dev->first_signalled_pipe = pipe->next_signalled;
+ pipe->prev_signalled = NULL;
+ pipe->next_signalled = NULL;
+}
+
+static struct goldfish_pipe *signalled_pipes_pop_front(struct goldfish_pipe_dev *dev,
+ int *wakes)
+{
+ struct goldfish_pipe *pipe;
+ unsigned long flags;
+ spin_lock_irqsave(&dev->lock, flags);
+
+ pipe = dev->first_signalled_pipe;
+ if (pipe) {
+ *wakes = pipe->signalled_flags;
+ pipe->signalled_flags = 0;
+ /*
+ * This is an optimized version of signalled_pipes_remove_locked() -
+ * we want to make it as fast as possible to wake the sleeping pipe
+ * operations faster
+ */
+ dev->first_signalled_pipe = pipe->next_signalled;
+ if (dev->first_signalled_pipe)
+ dev->first_signalled_pipe->prev_signalled = NULL;
+ pipe->next_signalled = NULL;
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return pipe;
+}
+
+static void goldfish_interrupt_task(unsigned long unused)
+{
+ struct goldfish_pipe_dev *dev = pipe_dev;
+ /* Iterate over the signalled pipes and wake them one by one */
+ struct goldfish_pipe *pipe;
+ int wakes;
+ while ((pipe = signalled_pipes_pop_front(dev, &wakes)) != NULL) {
+ if (wakes & PIPE_WAKE_CLOSED) {
+ pipe->flags = 1 << BIT_CLOSED_ON_HOST;
+ } else {
+ if (wakes & PIPE_WAKE_READ)
+ clear_bit(BIT_WAKE_ON_READ, &pipe->flags);
+ if (wakes & PIPE_WAKE_WRITE)
+ clear_bit(BIT_WAKE_ON_WRITE, &pipe->flags);
+ }
+ /*
+ * wake_up_interruptible() implies a write barrier, so don't explicitly
+ * add another one here.
+ */
+ wake_up_interruptible(&pipe->wake_queue);
+ }
+}
+DECLARE_TASKLET(goldfish_interrupt_tasklet, goldfish_interrupt_task, 0);
+
+/*
+ * The general idea of the interrupt handling:
+ *
+ * 1. device raises an interrupt if there's at least one signalled pipe
+ * 2. IRQ handler reads the signalled pipes and their count from the device
+ * 3. device writes them into a shared buffer and returns the count
+ * it only resets the IRQ if it has returned all signalled pipes,
+ * otherwise it leaves it raised, so IRQ handler will be called
+ * again for the next chunk
+ * 4. IRQ handler adds all returned pipes to the device's signalled pipes list
+ * 5. IRQ handler launches a tasklet to process the signalled pipes from the
+ * list in a separate context
+ */
+static irqreturn_t goldfish_pipe_interrupt(int irq, void *dev_id)
+{
+ u32 count;
+ u32 i;
+ unsigned long flags;
+ struct goldfish_pipe_dev *dev = dev_id;
+ if (dev != pipe_dev)
+ return IRQ_NONE;
+
+ /* Request the signalled pipes from the device */
+ spin_lock_irqsave(&dev->lock, flags);
+
+ count = readl(dev->base + PIPE_REG_GET_SIGNALLED);
+ if (count == 0) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return IRQ_NONE;
+ }
+ if (count > MAX_SIGNALLED_PIPES)
+ count = MAX_SIGNALLED_PIPES;
+
+ for (i = 0; i < count; ++i)
+ signalled_pipes_add_locked(dev,
+ dev->buffers->signalled_pipe_buffers[i].id,
+ dev->buffers->signalled_pipe_buffers[i].flags);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ tasklet_schedule(&goldfish_interrupt_tasklet);
+ return IRQ_HANDLED;
+}
+
+static int get_free_pipe_id_locked(struct goldfish_pipe_dev *dev)
+{
+ int id;
+ for (id = 0; id < dev->pipes_capacity; ++id)
+ if (!dev->pipes[id])
+ return id;
+
+ {
+ /* Reallocate the array */
+ u32 new_capacity = 2 * dev->pipes_capacity;
+ struct goldfish_pipe **pipes =
+ kcalloc(new_capacity, sizeof(*pipes),
+ GFP_ATOMIC);
+ if (!pipes)
+ return -ENOMEM;
+ memcpy(pipes, dev->pipes, sizeof(*pipes) * dev->pipes_capacity);
+ kfree(dev->pipes);
+ dev->pipes = pipes;
+ id = dev->pipes_capacity;
+ dev->pipes_capacity = new_capacity;
+ }
+ return id;
+}
+
+/**
+ * goldfish_pipe_open - open a channel to the AVD
+ * @inode: inode of device
+ * @file: file struct of opener
+ *
+ * Create a new pipe link between the emulator and the use application.
+ * Each new request produces a new pipe.
+ *
+ * Note: we use the pipe ID as a mux. All goldfish emulations are 32bit
+ * right now so this is fine. A move to 64bit will need this addressing
+ */
+static int goldfish_pipe_open(struct inode *inode, struct file *file)
+{
+ struct goldfish_pipe_dev *dev = pipe_dev;
+ unsigned long flags;
+ int id;
+ int status;
+
+ /* Allocate new pipe kernel object */
+ struct goldfish_pipe *pipe = kzalloc(sizeof(*pipe), GFP_KERNEL);
+ if (pipe == NULL)
+ return -ENOMEM;
+
+ pipe->dev = dev;
+ mutex_init(&pipe->lock);
+ init_waitqueue_head(&pipe->wake_queue);
+
+ /*
+ * Command buffer needs to be allocated on its own page to make sure it is
+ * physically contiguous in host's address space.
+ */
+ pipe->command_buffer =
+ (struct goldfish_pipe_command*)__get_free_page(GFP_KERNEL);
+ if (!pipe->command_buffer) {
+ status = -ENOMEM;
+ goto err_pipe;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ id = get_free_pipe_id_locked(dev);
+ if (id < 0) {
+ status = id;
+ goto err_id_locked;
+ }
+
+ dev->pipes[id] = pipe;
+ pipe->id = id;
+ pipe->command_buffer->id = id;
+
+ /* Now tell the emulator we're opening a new pipe. */
+ dev->buffers->open_command_params.rw_params_max_count =
+ MAX_BUFFERS_PER_COMMAND;
+ dev->buffers->open_command_params.command_buffer_ptr =
+ (u64)(unsigned long)__pa(pipe->command_buffer);
+ status = goldfish_cmd_locked(pipe, PIPE_CMD_OPEN);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ if (status < 0)
+ goto err_cmd;
+ /* All is done, save the pipe into the file's private data field */
+ file->private_data = pipe;
+ return 0;
+
+err_cmd:
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->pipes[id] = NULL;
+err_id_locked:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ free_page((unsigned long)pipe->command_buffer);
+err_pipe:
+ kfree(pipe);
+ return status;
+}
+
+static int goldfish_pipe_release(struct inode *inode, struct file *filp)
+{
+ unsigned long flags;
+ struct goldfish_pipe *pipe = filp->private_data;
+ struct goldfish_pipe_dev *dev = pipe->dev;
+
+ /* The guest is closing the channel, so tell the emulator right now */
+ (void)goldfish_cmd(pipe, PIPE_CMD_CLOSE);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->pipes[pipe->id] = NULL;
+ signalled_pipes_remove_locked(dev, pipe);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ filp->private_data = NULL;
+ free_page((unsigned long)pipe->command_buffer);
+ kfree(pipe);
+ return 0;
+}
+
+static const struct file_operations goldfish_pipe_fops = {
+ .owner = THIS_MODULE,
+ .read = goldfish_pipe_read,
+ .write = goldfish_pipe_write,
+ .poll = goldfish_pipe_poll,
+ .open = goldfish_pipe_open,
+ .release = goldfish_pipe_release,
+};
+
+static struct miscdevice goldfish_pipe_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "goldfish_pipe",
+ .fops = &goldfish_pipe_fops,
+};
+
+static int goldfish_pipe_device_init_v2(struct platform_device *pdev)
+{
+ char *page;
+ struct goldfish_pipe_dev *dev = pipe_dev;
+ int err = devm_request_irq(&pdev->dev, dev->irq, goldfish_pipe_interrupt,
+ IRQF_SHARED, "goldfish_pipe", dev);
+ if (err) {
+ dev_err(&pdev->dev, "unable to allocate IRQ for v2\n");
+ return err;
+ }
+
+ err = misc_register(&goldfish_pipe_dev);
+ if (err) {
+ dev_err(&pdev->dev, "unable to register v2 device\n");
+ return err;
+ }
+
+ dev->first_signalled_pipe = NULL;
+ dev->pipes_capacity = INITIAL_PIPES_CAPACITY;
+ dev->pipes = kcalloc(dev->pipes_capacity, sizeof(*dev->pipes), GFP_KERNEL);
+ if (!dev->pipes)
+ return -ENOMEM;
+
+ /*
+ * We're going to pass two buffers, open_command_params and
+ * signalled_pipe_buffers, to the host. This means each of those buffers
+ * needs to be contained in a single physical page. The easiest choice is
+ * to just allocate a page and place the buffers in it.
+ */
+ BUG_ON(sizeof(*dev->buffers) > PAGE_SIZE);
+ page = (char*)__get_free_page(GFP_KERNEL);
+ if (!page) {
+ kfree(dev->pipes);
+ return -ENOMEM;
+ }
+ dev->buffers = (struct goldfish_pipe_dev_buffers*)page;
+
+ /* Send the buffer addresses to the host */
+ {
+ u64 paddr = __pa(&dev->buffers->signalled_pipe_buffers);
+ writel((u32)(unsigned long)(paddr >> 32), dev->base + PIPE_REG_SIGNAL_BUFFER_HIGH);
+ writel((u32)(unsigned long)paddr, dev->base + PIPE_REG_SIGNAL_BUFFER);
+ writel((u32)MAX_SIGNALLED_PIPES, dev->base + PIPE_REG_SIGNAL_BUFFER_COUNT);
+
+ paddr = __pa(&dev->buffers->open_command_params);
+ writel((u32)(unsigned long)(paddr >> 32), dev->base + PIPE_REG_OPEN_BUFFER_HIGH);
+ writel((u32)(unsigned long)paddr, dev->base + PIPE_REG_OPEN_BUFFER);
+ }
+ return 0;
+}
+
+static void goldfish_pipe_device_deinit_v2(struct platform_device *pdev) {
+ struct goldfish_pipe_dev *dev = pipe_dev;
+ misc_deregister(&goldfish_pipe_dev);
+ kfree(dev->pipes);
+ free_page((unsigned long)dev->buffers);
+}
+
+static int goldfish_pipe_probe(struct platform_device *pdev)
+{
+ int err;
+ struct resource *r;
+ struct goldfish_pipe_dev *dev = pipe_dev;
+
+ BUG_ON(sizeof(struct goldfish_pipe_command) > PAGE_SIZE);
+
+ /* not thread safe, but this should not happen */
+ WARN_ON(dev->base != NULL);
+
+ spin_lock_init(&dev->lock);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL || resource_size(r) < PAGE_SIZE) {
+ dev_err(&pdev->dev, "can't allocate i/o page\n");
+ return -EINVAL;
+ }
+ dev->base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
+ if (dev->base == NULL) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ return -EINVAL;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (r == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+ dev->irq = r->start;
+
+ /*
+ * Exchange the versions with the host device
+ *
+ * Note: v1 driver used to not report its version, so we write it before
+ * reading device version back: this allows the host implementation to
+ * detect the old driver (if there was no version write before read).
+ */
+ writel((u32)PIPE_DRIVER_VERSION, dev->base + PIPE_REG_VERSION);
+ dev->version = readl(dev->base + PIPE_REG_VERSION);
+ if (dev->version < PIPE_CURRENT_DEVICE_VERSION) {
+ /* initialize the old device version */
+ err = goldfish_pipe_device_init_v1(pdev);
+ } else {
+ /* Host device supports the new interface */
+ err = goldfish_pipe_device_init_v2(pdev);
+ }
+ if (!err)
+ return 0;
+
+error:
+ dev->base = NULL;
+ return err;
+}
+
+static int goldfish_pipe_remove(struct platform_device *pdev)
+{
+ struct goldfish_pipe_dev *dev = pipe_dev;
+ if (dev->version < PIPE_CURRENT_DEVICE_VERSION)
+ goldfish_pipe_device_deinit_v1(pdev);
+ else
+ goldfish_pipe_device_deinit_v2(pdev);
+ dev->base = NULL;
+ return 0;
+}
+
+static const struct acpi_device_id goldfish_pipe_acpi_match[] = {
+ { "GFSH0003", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, goldfish_pipe_acpi_match);
+
+static const struct of_device_id goldfish_pipe_of_match[] = {
+ { .compatible = "google,android-pipe", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_pipe_of_match);
+
+static struct platform_driver goldfish_pipe_driver = {
+ .probe = goldfish_pipe_probe,
+ .remove = goldfish_pipe_remove,
+ .driver = {
+ .name = "goldfish_pipe",
+ .of_match_table = goldfish_pipe_of_match,
+ .acpi_match_table = ACPI_PTR(goldfish_pipe_acpi_match),
+ }
+};
+
+module_platform_driver(goldfish_pipe_driver);
+MODULE_AUTHOR("David Turner <digit@google.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index 58c1704..188388b 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -1245,6 +1245,7 @@
}
mutex_lock(&gsi_ctx->mlock);
+ reinit_completion(&ctx->compl);
val = (((evt_ring_hdl << GSI_EE_n_EV_CH_CMD_CHID_SHFT) &
GSI_EE_n_EV_CH_CMD_CHID_BMSK) |
((op << GSI_EE_n_EV_CH_CMD_OPCODE_SHFT) &
@@ -1339,6 +1340,7 @@
}
mutex_lock(&gsi_ctx->mlock);
+ reinit_completion(&ctx->compl);
val = (((evt_ring_hdl << GSI_EE_n_EV_CH_CMD_CHID_SHFT) &
GSI_EE_n_EV_CH_CMD_CHID_BMSK) |
((op << GSI_EE_n_EV_CH_CMD_OPCODE_SHFT) &
@@ -1796,7 +1798,7 @@
}
mutex_lock(&gsi_ctx->mlock);
- init_completion(&ctx->compl);
+ reinit_completion(&ctx->compl);
gsi_ctx->ch_dbg[chan_hdl].ch_start++;
val = (((chan_hdl << GSI_EE_n_GSI_CH_CMD_CHID_SHFT) &
@@ -1854,7 +1856,7 @@
}
mutex_lock(&gsi_ctx->mlock);
- init_completion(&ctx->compl);
+ reinit_completion(&ctx->compl);
gsi_ctx->ch_dbg[chan_hdl].ch_stop++;
val = (((chan_hdl << GSI_EE_n_GSI_CH_CMD_CHID_SHFT) &
@@ -1923,7 +1925,7 @@
}
mutex_lock(&gsi_ctx->mlock);
- init_completion(&ctx->compl);
+ reinit_completion(&ctx->compl);
gsi_ctx->ch_dbg[chan_hdl].ch_db_stop++;
val = (((chan_hdl << GSI_EE_n_GSI_CH_CMD_CHID_SHFT) &
@@ -1989,7 +1991,7 @@
mutex_lock(&gsi_ctx->mlock);
reset:
- init_completion(&ctx->compl);
+ reinit_completion(&ctx->compl);
gsi_ctx->ch_dbg[chan_hdl].ch_reset++;
val = (((chan_hdl << GSI_EE_n_GSI_CH_CMD_CHID_SHFT) &
GSI_EE_n_GSI_CH_CMD_CHID_BMSK) |
@@ -2055,7 +2057,7 @@
}
mutex_lock(&gsi_ctx->mlock);
- init_completion(&ctx->compl);
+ reinit_completion(&ctx->compl);
gsi_ctx->ch_dbg[chan_hdl].ch_de_alloc++;
val = (((chan_hdl << GSI_EE_n_GSI_CH_CMD_CHID_SHFT) &
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
index c9e20d3..f935bab 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1272,8 +1272,9 @@
"RX num_db=%u\n"
"RX num_unexpected_db=%u\n"
"RX num_pkts_in_dis_uninit_state=%u\n"
- "num_ic_inj_vdev_change=%u\n"
- "num_ic_inj_fw_desc_change=%u\n"
+ "RX num_ic_inj_vdev_change=%u\n"
+ "RX num_ic_inj_fw_desc_change=%u\n"
+ "RX num_qmb_int_handled=%u\n"
"RX reserved1=%u\n"
"RX reserved2=%u\n",
stats.rx_ch_stats.max_outstanding_pkts,
@@ -1295,6 +1296,7 @@
stats.rx_ch_stats.num_pkts_in_dis_uninit_state,
stats.rx_ch_stats.num_ic_inj_vdev_change,
stats.rx_ch_stats.num_ic_inj_fw_desc_change,
+ stats.rx_ch_stats.num_qmb_int_handled,
stats.rx_ch_stats.reserved1,
stats.rx_ch_stats.reserved2);
cnt += nbytes;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
index f0eaad8..299a431 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
@@ -920,6 +920,10 @@
struct IpaHwStatsWDIInfoData_t *wdi_uc_stats_mmio;
void *priv;
ipa_uc_ready_cb uc_ready_cb;
+ /* for AP+STA stats update */
+#ifdef IPA_WAN_MSG_IPv6_ADDR_GW_LEN
+ ipa_wdi_meter_notifier_cb stats_notify;
+#endif
};
/**
@@ -1504,6 +1508,7 @@
int ipa2_suspend_wdi_pipe(u32 clnt_hdl);
int ipa2_get_wdi_stats(struct IpaHwStatsWDIInfoData_t *stats);
u16 ipa2_get_smem_restr_bytes(void);
+int ipa2_broadcast_wdi_quota_reach_ind(uint32_t fid, uint64_t num_bytes);
int ipa2_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *inp,
ipa_notify_cb notify, void *priv, u8 hdr_len,
struct ipa_ntn_conn_out_params *outp);
@@ -1544,6 +1549,10 @@
bool ipa2_get_client_uplink(int pipe_idx);
+int ipa2_get_wlan_stats(struct ipa_get_wdi_sap_stats *wdi_sap_stats);
+
+int ipa2_set_wlan_quota(struct ipa_set_wifi_quota *wdi_quota);
+
/*
* IPADMA
*/
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
index 7291a44..b9775e0 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -763,7 +763,8 @@
IPAWANDBG("Quota reached indication on qmux(%d) Mbytes(%lu)\n",
qmi_ind.apn.mux_id,
(unsigned long int) qmi_ind.apn.num_Mbytes);
- ipa_broadcast_quota_reach_ind(qmi_ind.apn.mux_id);
+ ipa_broadcast_quota_reach_ind(qmi_ind.apn.mux_id,
+ IPA_UPSTEAM_MODEM);
}
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h
index 7793fc0..67dd031 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -138,7 +138,8 @@
int rmnet_ipa_set_data_quota(struct wan_ioctl_set_data_quota *data);
-void ipa_broadcast_quota_reach_ind(uint32_t mux_id);
+void ipa_broadcast_quota_reach_ind(uint32_t mux_id,
+ enum ipa_upstream_type upstream_type);
int rmnet_ipa_set_tether_client_pipe(struct wan_ioctl_set_tether_client_pipe
*data);
@@ -146,6 +147,8 @@
int rmnet_ipa_query_tethering_stats(struct wan_ioctl_query_tether_stats *data,
bool reset);
+int rmnet_ipa_reset_tethering_stats(struct wan_ioctl_reset_tether_stats *data);
+
int ipa_qmi_get_data_stats(struct ipa_get_data_stats_req_msg_v01 *req,
struct ipa_get_data_stats_resp_msg_v01 *resp);
@@ -238,7 +241,21 @@
return -EPERM;
}
-static inline void ipa_broadcast_quota_reach_ind(uint32_t mux_id) { }
+static inline void ipa_broadcast_quota_reach_ind
+(
+ uint32_t mux_id,
+ enum ipa_upstream_type upstream_type)
+{
+}
+
+static int rmnet_ipa_reset_tethering_stats
+(
+ struct wan_ioctl_reset_tether_stats *data
+)
+{
+ return -EPERM;
+
+}
static inline int ipa_qmi_get_data_stats(
struct ipa_get_data_stats_req_msg_v01 *req,
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c
index ab4da62..1fd4aad 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -12,6 +12,7 @@
#include "ipa_i.h"
#include <linux/dmapool.h>
#include <linux/delay.h>
+#include "ipa_qmi_service.h"
#define IPA_HOLB_TMR_DIS 0x0
@@ -477,6 +478,9 @@
RX_STATS(num_db);
RX_STATS(num_unexpected_db);
RX_STATS(num_pkts_in_dis_uninit_state);
+ RX_STATS(num_ic_inj_vdev_change);
+ RX_STATS(num_ic_inj_fw_desc_change);
+ RX_STATS(num_qmb_int_handled);
RX_STATS(reserved1);
RX_STATS(reserved2);
@@ -1205,6 +1209,12 @@
ep->client_notify = in->sys.notify;
ep->priv = in->sys.priv;
+ /* for AP+STA stats update */
+ if (in->wdi_notify)
+ ipa_ctx->uc_wdi_ctx.stats_notify = in->wdi_notify;
+ else
+ IPADBG("in->wdi_notify is null\n");
+
if (!ep->skip_ep_cfg) {
if (ipa2_cfg_ep(ipa_ep_idx, &in->sys.ipa_ep_cfg)) {
IPAERR("fail to configure EP.\n");
@@ -1302,6 +1312,12 @@
IPADBG("client (ep: %d) disconnected\n", clnt_hdl);
+ /* for AP+STA stats update */
+ if (ipa_ctx->uc_wdi_ctx.stats_notify)
+ ipa_ctx->uc_wdi_ctx.stats_notify = NULL;
+ else
+ IPADBG("uc_wdi_ctx.stats_notify already null\n");
+
uc_timeout:
return result;
}
@@ -1660,6 +1676,23 @@
return result;
}
+/**
+ * ipa_broadcast_wdi_quota_reach_ind() - quota reach
+ * @uint32_t fid: [in] input netdev ID
+ * @uint64_t num_bytes: [in] used bytes
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa2_broadcast_wdi_quota_reach_ind(uint32_t fid,
+ uint64_t num_bytes)
+{
+ IPAERR("Quota reached indication on fis(%d) Mbytes(%lu)\n",
+ fid,
+ (unsigned long int) num_bytes);
+ ipa_broadcast_quota_reach_ind(0, IPA_UPSTEAM_WLAN);
+ return 0;
+}
+
int ipa_write_qmapid_wdi_pipe(u32 clnt_hdl, u8 qmap_id)
{
int result = 0;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
index 05d8da9..4fdd84b 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -953,11 +953,39 @@
}
}
+/* ipa2_get_wlan_stats() - get ipa wifi stats
+ *
+ * Return value: success or failure
+ */
+int ipa2_get_wlan_stats(struct ipa_get_wdi_sap_stats *wdi_sap_stats)
+{
+ if (ipa_ctx->uc_wdi_ctx.stats_notify) {
+ ipa_ctx->uc_wdi_ctx.stats_notify(IPA_GET_WDI_SAP_STATS,
+ wdi_sap_stats);
+ } else {
+ IPAERR("uc_wdi_ctx.stats_notify not registered\n");
+ return -EFAULT;
+ }
+ return 0;
+}
+
+int ipa2_set_wlan_quota(struct ipa_set_wifi_quota *wdi_quota)
+{
+ if (ipa_ctx->uc_wdi_ctx.stats_notify) {
+ ipa_ctx->uc_wdi_ctx.stats_notify(IPA_SET_WIFI_QUOTA,
+ wdi_quota);
+ } else {
+ IPAERR("uc_wdi_ctx.stats_notify not registered\n");
+ return -EFAULT;
+ }
+ return 0;
+}
+
/**
* ipa2_get_client() - provide client mapping
* @client: client type
*
- * Return value: none
+ * Return value: client mapping enum
*/
enum ipacm_client_enum ipa2_get_client(int pipe_idx)
{
@@ -5027,6 +5055,8 @@
api_ctrl->ipa_suspend_wdi_pipe = ipa2_suspend_wdi_pipe;
api_ctrl->ipa_get_wdi_stats = ipa2_get_wdi_stats;
api_ctrl->ipa_get_smem_restr_bytes = ipa2_get_smem_restr_bytes;
+ api_ctrl->ipa_broadcast_wdi_quota_reach_ind =
+ ipa2_broadcast_wdi_quota_reach_ind;
api_ctrl->ipa_uc_wdi_get_dbpa = ipa2_uc_wdi_get_dbpa;
api_ctrl->ipa_uc_reg_rdyCB = ipa2_uc_reg_rdyCB;
api_ctrl->ipa_uc_dereg_rdyCB = ipa2_uc_dereg_rdyCB;
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index 02e2c5f..513d7bb 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -52,6 +52,8 @@
#define DEFAULT_OUTSTANDING_LOW 32
#define IPA_WWAN_DEV_NAME "rmnet_ipa%d"
+#define IPA_UPSTEAM_WLAN_IFACE_NAME "wlan0"
+
#define IPA_WWAN_DEVICE_COUNT (1)
#define IPA_WWAN_RX_SOFTIRQ_THRESH 16
@@ -768,6 +770,22 @@
return MAX_NUM_OF_MUX_CHANNEL;
}
+static enum ipa_upstream_type find_upstream_type(const char *upstreamIface)
+{
+ int i;
+
+ for (i = 0; i < MAX_NUM_OF_MUX_CHANNEL; i++) {
+ if (strcmp(mux_channel[i].vchannel_name,
+ upstreamIface) == 0)
+ return IPA_UPSTEAM_MODEM;
+ }
+
+ if (strcmp(IPA_UPSTEAM_WLAN_IFACE_NAME, upstreamIface) == 0)
+ return IPA_UPSTEAM_WLAN;
+ else
+ return IPA_UPSTEAM_MAX;
+}
+
static int wwan_register_to_ipa(int index)
{
struct ipa_tx_intf tx_properties = {0};
@@ -2505,10 +2523,10 @@
}
/**
- * rmnet_ipa_set_data_quota() - Data quota setting IOCTL handler
+ * rmnet_ipa_set_data_quota_modem() - Data quota setting IOCTL handler
* @data - IOCTL data
*
- * This function handles WAN_IOC_SET_DATA_QUOTA.
+ * This function handles WAN_IOC_SET_DATA_QUOTA on modem interface.
* It translates the given interface name to the Modem MUX ID and
* sends the request of the quota to the IPA Modem driver via QMI.
*
@@ -2517,12 +2535,16 @@
* -EFAULT: Invalid interface name provided
* other: See ipa_qmi_set_data_quota
*/
-int rmnet_ipa_set_data_quota(struct wan_ioctl_set_data_quota *data)
+static int rmnet_ipa_set_data_quota_modem(struct wan_ioctl_set_data_quota *data)
{
u32 mux_id;
int index;
struct ipa_set_data_usage_quota_req_msg_v01 req;
+ /* stop quota */
+ if (!data->set_quota)
+ ipa_qmi_stop_data_qouta();
+
index = find_vchannel_name_index(data->interface_name);
IPAWANERR("iface name %s, quota %lu\n",
data->interface_name,
@@ -2547,6 +2569,65 @@
return ipa_qmi_set_data_quota(&req);
}
+static int rmnet_ipa_set_data_quota_wifi(struct wan_ioctl_set_data_quota *data)
+{
+ struct ipa_set_wifi_quota wifi_quota;
+ int rc = 0;
+
+ memset(&wifi_quota, 0, sizeof(struct ipa_set_wifi_quota));
+ wifi_quota.set_quota = data->set_quota;
+ wifi_quota.quota_bytes = data->quota_mbytes;
+ IPAWANDBG("iface name %s, quota %lu\n",
+ data->interface_name,
+ (unsigned long int) data->quota_mbytes);
+
+ rc = ipa2_set_wlan_quota(&wifi_quota);
+ /* check if wlan-fw takes this quota-set */
+ if (!wifi_quota.set_valid)
+ rc = -EFAULT;
+ return rc;
+}
+
+/**
+ * rmnet_ipa_set_data_quota() - Data quota setting IOCTL handler
+ * @data - IOCTL data
+ *
+ * This function handles WAN_IOC_SET_DATA_QUOTA.
+ * It translates the given interface name to the Modem MUX ID and
+ * sends the request of the quota to the IPA Modem driver via QMI.
+ *
+ * Return codes:
+ * 0: Success
+ * -EFAULT: Invalid interface name provided
+ * other: See ipa_qmi_set_data_quota
+ */
+int rmnet_ipa_set_data_quota(struct wan_ioctl_set_data_quota *data)
+{
+ enum ipa_upstream_type upstream_type;
+ int rc = 0;
+
+ /* get IPA backhaul type */
+ upstream_type = find_upstream_type(data->interface_name);
+
+ if (upstream_type == IPA_UPSTEAM_MAX) {
+ IPAWANERR("upstream iface %s not supported\n",
+ data->interface_name);
+ } else if (upstream_type == IPA_UPSTEAM_WLAN) {
+ rc = rmnet_ipa_set_data_quota_wifi(data);
+ if (rc) {
+ IPAWANERR("set quota on wifi failed\n");
+ return rc;
+ }
+ } else {
+ rc = rmnet_ipa_set_data_quota_modem(data);
+ if (rc) {
+ IPAWANERR("set quota on modem failed\n");
+ return rc;
+ }
+ }
+ return rc;
+}
+
/* rmnet_ipa_set_tether_client_pipe() -
* @data - IOCTL data
*
@@ -2594,8 +2675,59 @@
return 0;
}
-int rmnet_ipa_query_tethering_stats(struct wan_ioctl_query_tether_stats *data,
- bool reset)
+static int rmnet_ipa_query_tethering_stats_wifi(
+ struct wan_ioctl_query_tether_stats *data, bool reset)
+{
+ struct ipa_get_wdi_sap_stats *sap_stats;
+ int rc;
+
+ sap_stats = kzalloc(sizeof(struct ipa_get_wdi_sap_stats),
+ GFP_KERNEL);
+ if (!sap_stats)
+ return -ENOMEM;
+
+ sap_stats->reset_stats = reset;
+ IPAWANDBG("reset the pipe stats %d\n", sap_stats->reset_stats);
+
+ rc = ipa2_get_wlan_stats(sap_stats);
+ if (rc) {
+ kfree(sap_stats);
+ return rc;
+ } else if (reset) {
+ kfree(sap_stats);
+ return 0;
+ }
+
+ if (sap_stats->stats_valid) {
+ data->ipv4_tx_packets = sap_stats->ipv4_tx_packets;
+ data->ipv4_tx_bytes = sap_stats->ipv4_tx_bytes;
+ data->ipv4_rx_packets = sap_stats->ipv4_rx_packets;
+ data->ipv4_rx_bytes = sap_stats->ipv4_rx_bytes;
+ data->ipv6_tx_packets = sap_stats->ipv6_tx_packets;
+ data->ipv6_tx_bytes = sap_stats->ipv6_tx_bytes;
+ data->ipv6_rx_packets = sap_stats->ipv6_rx_packets;
+ data->ipv6_rx_bytes = sap_stats->ipv6_rx_bytes;
+ }
+
+ IPAWANDBG("v4_rx_p(%lu) v6_rx_p(%lu) v4_rx_b(%lu) v6_rx_b(%lu)\n",
+ (unsigned long int) data->ipv4_rx_packets,
+ (unsigned long int) data->ipv6_rx_packets,
+ (unsigned long int) data->ipv4_rx_bytes,
+ (unsigned long int) data->ipv6_rx_bytes);
+ IPAWANDBG("tx_p_v4(%lu)v6(%lu)tx_b_v4(%lu) v6(%lu)\n",
+ (unsigned long int) data->ipv4_tx_packets,
+ (unsigned long int) data->ipv6_tx_packets,
+ (unsigned long int) data->ipv4_tx_bytes,
+ (unsigned long int) data->ipv6_tx_bytes);
+
+ kfree(sap_stats);
+ return rc;
+}
+
+int rmnet_ipa_query_tethering_stats_modem(
+ struct wan_ioctl_query_tether_stats *data,
+ bool reset
+)
{
struct ipa_get_data_stats_req_msg_v01 *req;
struct ipa_get_data_stats_resp_msg_v01 *resp;
@@ -2740,6 +2872,70 @@
return 0;
}
+int rmnet_ipa_query_tethering_stats(struct wan_ioctl_query_tether_stats *data,
+ bool reset)
+{
+ enum ipa_upstream_type upstream_type;
+ int rc = 0;
+
+ /* get IPA backhaul type */
+ upstream_type = find_upstream_type(data->upstreamIface);
+
+ if (upstream_type == IPA_UPSTEAM_MAX) {
+ IPAWANERR("upstreamIface %s not supported\n",
+ data->upstreamIface);
+ } else if (upstream_type == IPA_UPSTEAM_WLAN) {
+ IPAWANDBG_LOW(" query wifi-backhaul stats\n");
+ rc = rmnet_ipa_query_tethering_stats_wifi(
+ data, false);
+ if (rc) {
+ IPAWANERR("wlan WAN_IOC_QUERY_TETHER_STATS failed\n");
+ return rc;
+ }
+ } else {
+ IPAWANDBG_LOW(" query modem-backhaul stats\n");
+ rc = rmnet_ipa_query_tethering_stats_modem(
+ data, false);
+ if (rc) {
+ IPAWANERR("modem WAN_IOC_QUERY_TETHER_STATS failed\n");
+ return rc;
+ }
+ }
+ return rc;
+}
+
+int rmnet_ipa_reset_tethering_stats(struct wan_ioctl_reset_tether_stats *data)
+{
+ enum ipa_upstream_type upstream_type;
+ int rc = 0;
+
+ /* get IPA backhaul type */
+ upstream_type = find_upstream_type(data->upstreamIface);
+
+ if (upstream_type == IPA_UPSTEAM_MAX) {
+ IPAWANERR("upstream iface %s not supported\n",
+ data->upstreamIface);
+ } else if (upstream_type == IPA_UPSTEAM_WLAN) {
+ IPAWANDBG(" reset wifi-backhaul stats\n");
+ rc = rmnet_ipa_query_tethering_stats_wifi(
+ NULL, true);
+ if (rc) {
+ IPAWANERR("reset WLAN stats failed\n");
+ return rc;
+ }
+ } else {
+ IPAWANDBG(" reset modem-backhaul stats\n");
+ rc = rmnet_ipa_query_tethering_stats_modem(
+ NULL, true);
+ if (rc) {
+ IPAWANERR("reset MODEM stats failed\n");
+ return rc;
+ }
+ }
+ return rc;
+}
+
+
/**
* ipa_broadcast_quota_reach_ind() - Send Netlink broadcast on Quota
* @mux_id - The MUX ID on which the quota has been reached
@@ -2749,7 +2945,8 @@
* on the specific interface which matches the mux_id has been reached.
*
*/
-void ipa_broadcast_quota_reach_ind(u32 mux_id)
+void ipa_broadcast_quota_reach_ind(u32 mux_id,
+ enum ipa_upstream_type upstream_type)
{
char alert_msg[IPA_QUOTA_REACH_ALERT_MAX_SIZE];
char iface_name_l[IPA_QUOTA_REACH_IF_NAME_MAX_SIZE];
@@ -2759,11 +2956,17 @@
int res;
int index;
- index = find_mux_channel_index(mux_id);
-
- if (index == MAX_NUM_OF_MUX_CHANNEL) {
- IPAWANERR("%u is an mux ID\n", mux_id);
+ /* check upstream_type*/
+ if (upstream_type == IPA_UPSTEAM_MAX) {
+ IPAWANERR("upstreamIface type %d not supported\n",
+ upstream_type);
return;
+ } else if (upstream_type == IPA_UPSTEAM_MODEM) {
+ index = find_mux_channel_index(mux_id);
+ if (index == MAX_NUM_OF_MUX_CHANNEL) {
+ IPAWANERR("%u is an mux ID\n", mux_id);
+ return;
+ }
}
res = snprintf(alert_msg, IPA_QUOTA_REACH_ALERT_MAX_SIZE,
@@ -2772,16 +2975,28 @@
IPAWANERR("message too long (%d)", res);
return;
}
+
/* posting msg for L-release for CNE */
+ if (upstream_type == IPA_UPSTEAM_MODEM) {
res = snprintf(iface_name_l, IPA_QUOTA_REACH_IF_NAME_MAX_SIZE,
- "UPSTREAM=%s", mux_channel[index].vchannel_name);
+ "UPSTREAM=%s", mux_channel[index].vchannel_name);
+ } else {
+ res = snprintf(iface_name_l, IPA_QUOTA_REACH_IF_NAME_MAX_SIZE,
+ "UPSTREAM=%s", IPA_UPSTEAM_WLAN_IFACE_NAME);
+ }
if (res >= IPA_QUOTA_REACH_IF_NAME_MAX_SIZE) {
IPAWANERR("message too long (%d)", res);
return;
}
+
/* posting msg for M-release for CNE */
+ if (upstream_type == IPA_UPSTEAM_MODEM) {
res = snprintf(iface_name_m, IPA_QUOTA_REACH_IF_NAME_MAX_SIZE,
- "INTERFACE=%s", mux_channel[index].vchannel_name);
+ "INTERFACE=%s", mux_channel[index].vchannel_name);
+ } else {
+ res = snprintf(iface_name_m, IPA_QUOTA_REACH_IF_NAME_MAX_SIZE,
+ "INTERFACE=%s", IPA_UPSTEAM_WLAN_IFACE_NAME);
+ }
if (res >= IPA_QUOTA_REACH_IF_NAME_MAX_SIZE) {
IPAWANERR("message too long (%d)", res);
return;
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c
index 811dba4..436cf21 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -256,8 +256,9 @@
break;
}
- if (rmnet_ipa_query_tethering_stats(NULL, true)) {
- IPAWANERR("WAN_IOC_QUERY_TETHER_STATS failed\n");
+ if (rmnet_ipa_reset_tethering_stats(
+ (struct wan_ioctl_reset_tether_stats *)param)) {
+ IPAWANERR("WAN_IOC_RESET_TETHER_STATS failed\n");
retval = -EFAULT;
break;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 298f8c1..e9e3c15 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -3711,30 +3711,49 @@
static void ipa3_freeze_clock_vote_and_notify_modem(void)
{
int res;
- u32 ipa_clk_state;
struct ipa_active_client_logging_info log_info;
if (ipa3_ctx->smp2p_info.res_sent)
return;
+ if (ipa3_ctx->smp2p_info.out_base_id == 0) {
+ IPAERR("smp2p out gpio not assigned\n");
+ return;
+ }
+
IPA_ACTIVE_CLIENTS_PREP_SPECIAL(log_info, "FREEZE_VOTE");
res = ipa3_inc_client_enable_clks_no_block(&log_info);
if (res)
- ipa_clk_state = 0;
+ ipa3_ctx->smp2p_info.ipa_clk_on = false;
else
- ipa_clk_state = 1;
+ ipa3_ctx->smp2p_info.ipa_clk_on = true;
- if (ipa3_ctx->smp2p_info.out_base_id) {
- gpio_set_value(ipa3_ctx->smp2p_info.out_base_id +
- IPA_GPIO_OUT_CLK_VOTE_IDX, ipa_clk_state);
- gpio_set_value(ipa3_ctx->smp2p_info.out_base_id +
- IPA_GPIO_OUT_CLK_RSP_CMPLT_IDX, 1);
- ipa3_ctx->smp2p_info.res_sent = true;
- } else {
- IPAERR("smp2p out gpio not assigned\n");
- }
+ gpio_set_value(ipa3_ctx->smp2p_info.out_base_id +
+ IPA_GPIO_OUT_CLK_VOTE_IDX,
+ ipa3_ctx->smp2p_info.ipa_clk_on);
+ gpio_set_value(ipa3_ctx->smp2p_info.out_base_id +
+ IPA_GPIO_OUT_CLK_RSP_CMPLT_IDX, 1);
- IPADBG("IPA clocks are %s\n", ipa_clk_state ? "ON" : "OFF");
+ ipa3_ctx->smp2p_info.res_sent = true;
+ IPADBG("IPA clocks are %s\n",
+ ipa3_ctx->smp2p_info.ipa_clk_on ? "ON" : "OFF");
+}
+
+void ipa3_reset_freeze_vote(void)
+{
+ if (ipa3_ctx->smp2p_info.res_sent == false)
+ return;
+
+ if (ipa3_ctx->smp2p_info.ipa_clk_on)
+ IPA_ACTIVE_CLIENTS_DEC_SPECIAL("FREEZE_VOTE");
+
+ gpio_set_value(ipa3_ctx->smp2p_info.out_base_id +
+ IPA_GPIO_OUT_CLK_VOTE_IDX, 0);
+ gpio_set_value(ipa3_ctx->smp2p_info.out_base_id +
+ IPA_GPIO_OUT_CLK_RSP_CMPLT_IDX, 0);
+
+ ipa3_ctx->smp2p_info.res_sent = false;
+ ipa3_ctx->smp2p_info.ipa_clk_on = false;
}
static int ipa3_panic_notifier(struct notifier_block *this,
@@ -4093,7 +4112,7 @@
if (result) {
IPAERR("FW loading process has failed\n");
- BUG();
+ return result;
} else
ipa3_post_init(&ipa3_res, ipa3_ctx->dev);
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index 9fe93b2..097d983 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -46,6 +46,20 @@
int res = 0;
struct ipahal_reg_endp_init_rsrc_grp rsrc_grp;
+ /* Assign the resource group for pipe */
+ memset(&rsrc_grp, 0, sizeof(rsrc_grp));
+ rsrc_grp.rsrc_grp = ipa_get_ep_group(ep->client);
+ if (rsrc_grp.rsrc_grp == -1) {
+ IPAERR("invalid group for client %d\n", ep->client);
+ WARN_ON(1);
+ return -EFAULT;
+ }
+
+ IPADBG("Setting group %d for pipe %d\n",
+ rsrc_grp.rsrc_grp, clnt_hdl);
+ ipahal_write_reg_n_fields(IPA_ENDP_INIT_RSRC_GRP_n, clnt_hdl,
+ &rsrc_grp);
+
IPADBG("Enabling data path\n");
if (IPA_CLIENT_IS_CONS(ep->client)) {
memset(&holb_cfg, 0, sizeof(holb_cfg));
@@ -64,20 +78,6 @@
res = ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
}
- /* Assign the resource group for pipe */
- memset(&rsrc_grp, 0, sizeof(rsrc_grp));
- rsrc_grp.rsrc_grp = ipa_get_ep_group(ep->client);
- if (rsrc_grp.rsrc_grp == -1) {
- IPAERR("invalid group for client %d\n", ep->client);
- WARN_ON(1);
- return -EFAULT;
- }
-
- IPADBG("Setting group %d for pipe %d\n",
- rsrc_grp.rsrc_grp, clnt_hdl);
- ipahal_write_reg_n_fields(IPA_ENDP_INIT_RSRC_GRP_n, clnt_hdl,
- &rsrc_grp);
-
return res;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
index 5912d3f..3fb767c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1356,8 +1356,9 @@
"RX num_db=%u\n"
"RX num_unexpected_db=%u\n"
"RX num_pkts_in_dis_uninit_state=%u\n"
- "num_ic_inj_vdev_change=%u\n"
- "num_ic_inj_fw_desc_change=%u\n"
+ "RX num_ic_inj_vdev_change=%u\n"
+ "RX num_ic_inj_fw_desc_change=%u\n"
+ "RX num_qmb_int_handled=%u\n"
"RX reserved1=%u\n"
"RX reserved2=%u\n",
stats.rx_ch_stats.max_outstanding_pkts,
@@ -1379,6 +1380,7 @@
stats.rx_ch_stats.num_pkts_in_dis_uninit_state,
stats.rx_ch_stats.num_ic_inj_vdev_change,
stats.rx_ch_stats.num_ic_inj_fw_desc_change,
+ stats.rx_ch_stats.num_qmb_int_handled,
stats.rx_ch_stats.reserved1,
stats.rx_ch_stats.reserved2);
cnt += nbytes;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index ab386a4..91577a3 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1360,13 +1360,6 @@
}
}
- result = ipa3_enable_data_path(ipa_ep_idx);
- if (result) {
- IPAERR("enable data path failed res=%d clnt=%d.\n", result,
- ipa_ep_idx);
- goto fail_gen2;
- }
-
if (!ep->skip_ep_cfg) {
if (ipa3_cfg_ep(ipa_ep_idx, &sys_in->ipa_ep_cfg)) {
IPAERR("fail to configure EP.\n");
@@ -1498,6 +1491,13 @@
ipa3_install_dflt_flt_rules(ipa_ep_idx);
}
+ result = ipa3_enable_data_path(ipa_ep_idx);
+ if (result) {
+ IPAERR("enable data path failed res=%d ep=%d.\n", result,
+ ipa_ep_idx);
+ goto fail_gen2;
+ }
+
if (!ep->keep_ipa_awake)
IPA_ACTIVE_CLIENTS_DEC_EP(sys_in->client);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index bec0b27..acad448 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -42,6 +42,7 @@
#define IPA_COOKIE 0x57831603
#define MTU_BYTE 1500
+#define IPA_EP_NOT_ALLOCATED (-1)
#define IPA3_MAX_NUM_PIPES 31
#define IPA_SYS_DESC_FIFO_SZ 0x800
#define IPA_SYS_TX_DATA_DESC_FIFO_SZ 0x1000
@@ -1003,6 +1004,7 @@
struct ipa3_smp2p_info {
u32 out_base_id;
u32 in_base_id;
+ bool ipa_clk_on;
bool res_sent;
};
@@ -2042,6 +2044,7 @@
int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map);
int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr,
u32 size, bool map);
+void ipa3_reset_freeze_vote(void);
int ipa3_ntn_init(void);
int ipa3_get_ntn_stats(struct Ipa3HwStatsNTNInfoData_t *stats);
struct dentry *ipa_debugfs_get_root(void);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
index 480bacb..d2c9ae1 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
@@ -473,6 +473,9 @@
RX_STATS(num_db);
RX_STATS(num_unexpected_db);
RX_STATS(num_pkts_in_dis_uninit_state);
+ RX_STATS(num_ic_inj_vdev_change);
+ RX_STATS(num_ic_inj_fw_desc_change);
+ RX_STATS(num_qmb_int_handled);
RX_STATS(reserved1);
RX_STATS(reserved2);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index d478a06..4f986f5 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -95,7 +95,8 @@
#define QMB_MASTER_SELECT_PCIE (1)
#define IPA_CLIENT_NOT_USED \
- {-1, -1, false, IPA_DPS_HPS_SEQ_TYPE_INVALID, QMB_MASTER_SELECT_DDR}
+ {IPA_EP_NOT_ALLOCATED, IPA_EP_NOT_ALLOCATED, false, \
+ IPA_DPS_HPS_SEQ_TYPE_INVALID, QMB_MASTER_SELECT_DDR}
/* Resource Group index*/
#define IPA_v3_0_GROUP_UL (0)
@@ -1236,12 +1237,18 @@
*/
int ipa3_get_ep_mapping(enum ipa_client_type client)
{
+ int ipa_ep_idx;
+
if (client >= IPA_CLIENT_MAX || client < 0) {
IPAERR("Bad client number! client =%d\n", client);
return -EINVAL;
}
- return ipa3_ep_mapping[ipa3_get_hw_type_index()][client].pipe_num;
+ ipa_ep_idx = ipa3_ep_mapping[ipa3_get_hw_type_index()][client].pipe_num;
+ if (ipa_ep_idx < 0 || ipa_ep_idx >= IPA3_MAX_NUM_PIPES)
+ return IPA_EP_NOT_ALLOCATED;
+
+ return ipa_ep_idx;
}
/**
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index 77c1d10..48c25f0 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -2403,6 +2403,7 @@
ipa3_qmi_service_exit();
/*hold a proxy vote for the modem*/
ipa3_proxy_clk_vote();
+ ipa3_reset_freeze_vote();
IPAWANINFO("IPA BEFORE_POWERUP handling is complete\n");
break;
case SUBSYS_AFTER_POWERUP:
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index 26e4cbc..6032b70 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -175,6 +175,15 @@
},
{
.callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X45U",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X45U"),
+ },
+ .driver_data = &quirk_asus_wapf4,
+ },
+ {
+ .callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. X456UA",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c
index 61f39ab..82d6771 100644
--- a/drivers/platform/x86/fujitsu-laptop.c
+++ b/drivers/platform/x86/fujitsu-laptop.c
@@ -177,43 +177,43 @@
#if IS_ENABLED(CONFIG_LEDS_CLASS)
static enum led_brightness logolamp_get(struct led_classdev *cdev);
-static void logolamp_set(struct led_classdev *cdev,
+static int logolamp_set(struct led_classdev *cdev,
enum led_brightness brightness);
static struct led_classdev logolamp_led = {
.name = "fujitsu::logolamp",
.brightness_get = logolamp_get,
- .brightness_set = logolamp_set
+ .brightness_set_blocking = logolamp_set
};
static enum led_brightness kblamps_get(struct led_classdev *cdev);
-static void kblamps_set(struct led_classdev *cdev,
+static int kblamps_set(struct led_classdev *cdev,
enum led_brightness brightness);
static struct led_classdev kblamps_led = {
.name = "fujitsu::kblamps",
.brightness_get = kblamps_get,
- .brightness_set = kblamps_set
+ .brightness_set_blocking = kblamps_set
};
static enum led_brightness radio_led_get(struct led_classdev *cdev);
-static void radio_led_set(struct led_classdev *cdev,
+static int radio_led_set(struct led_classdev *cdev,
enum led_brightness brightness);
static struct led_classdev radio_led = {
.name = "fujitsu::radio_led",
.brightness_get = radio_led_get,
- .brightness_set = radio_led_set
+ .brightness_set_blocking = radio_led_set
};
static enum led_brightness eco_led_get(struct led_classdev *cdev);
-static void eco_led_set(struct led_classdev *cdev,
+static int eco_led_set(struct led_classdev *cdev,
enum led_brightness brightness);
static struct led_classdev eco_led = {
.name = "fujitsu::eco_led",
.brightness_get = eco_led_get,
- .brightness_set = eco_led_set
+ .brightness_set_blocking = eco_led_set
};
#endif
@@ -267,48 +267,48 @@
#if IS_ENABLED(CONFIG_LEDS_CLASS)
/* LED class callbacks */
-static void logolamp_set(struct led_classdev *cdev,
+static int logolamp_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
if (brightness >= LED_FULL) {
call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON);
- call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_ON);
+ return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_ON);
} else if (brightness >= LED_HALF) {
call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON);
- call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_OFF);
+ return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_OFF);
} else {
- call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_OFF);
+ return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_OFF);
}
}
-static void kblamps_set(struct led_classdev *cdev,
+static int kblamps_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
if (brightness >= LED_FULL)
- call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_ON);
+ return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_ON);
else
- call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF);
+ return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF);
}
-static void radio_led_set(struct led_classdev *cdev,
+static int radio_led_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
if (brightness >= LED_FULL)
- call_fext_func(FUNC_RFKILL, 0x5, RADIO_LED_ON, RADIO_LED_ON);
+ return call_fext_func(FUNC_RFKILL, 0x5, RADIO_LED_ON, RADIO_LED_ON);
else
- call_fext_func(FUNC_RFKILL, 0x5, RADIO_LED_ON, 0x0);
+ return call_fext_func(FUNC_RFKILL, 0x5, RADIO_LED_ON, 0x0);
}
-static void eco_led_set(struct led_classdev *cdev,
+static int eco_led_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
int curr;
curr = call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0);
if (brightness >= LED_FULL)
- call_fext_func(FUNC_LEDS, 0x1, ECO_LED, curr | ECO_LED_ON);
+ return call_fext_func(FUNC_LEDS, 0x1, ECO_LED, curr | ECO_LED_ON);
else
- call_fext_func(FUNC_LEDS, 0x1, ECO_LED, curr & ~ECO_LED_ON);
+ return call_fext_func(FUNC_LEDS, 0x1, ECO_LED, curr & ~ECO_LED_ON);
}
static enum led_brightness logolamp_get(struct led_classdev *cdev)
diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c
index 1fc0de8..3617705 100644
--- a/drivers/platform/x86/intel_mid_powerbtn.c
+++ b/drivers/platform/x86/intel_mid_powerbtn.c
@@ -77,7 +77,7 @@
input_set_capability(input, EV_KEY, KEY_POWER);
- error = request_threaded_irq(irq, NULL, mfld_pb_isr, 0,
+ error = request_threaded_irq(irq, NULL, mfld_pb_isr, IRQF_ONESHOT,
DRIVER_NAME, input);
if (error) {
dev_err(&pdev->dev, "Unable to request irq %d for mfld power"
diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c
index f5746b9..e958433 100644
--- a/drivers/power/supply/bq24190_charger.c
+++ b/drivers/power/supply/bq24190_charger.c
@@ -1141,7 +1141,7 @@
dev_dbg(bdi->dev, "prop: %d\n", psp);
- pm_runtime_put_sync(bdi->dev);
+ pm_runtime_get_sync(bdi->dev);
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index 3b0dbc6..bccb3f5 100644
--- a/drivers/power/supply/bq27xxx_battery.c
+++ b/drivers/power/supply/bq27xxx_battery.c
@@ -164,6 +164,25 @@
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
},
+ [BQ27510] = {
+ [BQ27XXX_REG_CTRL] = 0x00,
+ [BQ27XXX_REG_TEMP] = 0x06,
+ [BQ27XXX_REG_INT_TEMP] = 0x28,
+ [BQ27XXX_REG_VOLT] = 0x08,
+ [BQ27XXX_REG_AI] = 0x14,
+ [BQ27XXX_REG_FLAGS] = 0x0a,
+ [BQ27XXX_REG_TTE] = 0x16,
+ [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_TTES] = 0x1a,
+ [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_NAC] = 0x0c,
+ [BQ27XXX_REG_FCC] = 0x12,
+ [BQ27XXX_REG_CYCT] = 0x1e,
+ [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_SOC] = 0x20,
+ [BQ27XXX_REG_DCAP] = 0x2e,
+ [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+ },
[BQ27530] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
@@ -302,6 +321,24 @@
POWER_SUPPLY_PROP_MANUFACTURER,
};
+static enum power_supply_property bq27510_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
static enum power_supply_property bq27530_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
@@ -385,6 +422,7 @@
BQ27XXX_PROP(BQ27000, bq27000_battery_props),
BQ27XXX_PROP(BQ27010, bq27010_battery_props),
BQ27XXX_PROP(BQ27500, bq27500_battery_props),
+ BQ27XXX_PROP(BQ27510, bq27510_battery_props),
BQ27XXX_PROP(BQ27530, bq27530_battery_props),
BQ27XXX_PROP(BQ27541, bq27541_battery_props),
BQ27XXX_PROP(BQ27545, bq27545_battery_props),
@@ -635,7 +673,8 @@
*/
static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags)
{
- if (di->chip == BQ27500 || di->chip == BQ27541 || di->chip == BQ27545)
+ if (di->chip == BQ27500 || di->chip == BQ27510 ||
+ di->chip == BQ27541 || di->chip == BQ27545)
return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD);
if (di->chip == BQ27530 || di->chip == BQ27421)
return flags & BQ27XXX_FLAG_OT;
diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c
index 85d4ea2..5c5c3a6 100644
--- a/drivers/power/supply/bq27xxx_battery_i2c.c
+++ b/drivers/power/supply/bq27xxx_battery_i2c.c
@@ -149,8 +149,8 @@
{ "bq27200", BQ27000 },
{ "bq27210", BQ27010 },
{ "bq27500", BQ27500 },
- { "bq27510", BQ27500 },
- { "bq27520", BQ27500 },
+ { "bq27510", BQ27510 },
+ { "bq27520", BQ27510 },
{ "bq27530", BQ27530 },
{ "bq27531", BQ27530 },
{ "bq27541", BQ27541 },
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index 243b233..3c71f60 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -442,6 +442,7 @@
return i;
}
}
+ pr_err("Cannot find matching power limit for constraint %d\n", cid);
return -EINVAL;
}
@@ -457,6 +458,10 @@
get_online_cpus();
rd = power_zone_to_rapl_domain(power_zone);
id = contraint_to_pl(rd, cid);
+ if (id < 0) {
+ ret = id;
+ goto set_exit;
+ }
rp = rd->rp;
@@ -496,6 +501,11 @@
get_online_cpus();
rd = power_zone_to_rapl_domain(power_zone);
id = contraint_to_pl(rd, cid);
+ if (id < 0) {
+ ret = id;
+ goto get_exit;
+ }
+
switch (rd->rpl[id].prim_id) {
case PL1_ENABLE:
prim = POWER_LIMIT1;
@@ -512,6 +522,7 @@
else
*data = val;
+get_exit:
put_online_cpus();
return ret;
@@ -527,6 +538,10 @@
get_online_cpus();
rd = power_zone_to_rapl_domain(power_zone);
id = contraint_to_pl(rd, cid);
+ if (id < 0) {
+ ret = id;
+ goto set_time_exit;
+ }
switch (rd->rpl[id].prim_id) {
case PL1_ENABLE:
@@ -538,6 +553,8 @@
default:
ret = -EINVAL;
}
+
+set_time_exit:
put_online_cpus();
return ret;
}
@@ -552,6 +569,10 @@
get_online_cpus();
rd = power_zone_to_rapl_domain(power_zone);
id = contraint_to_pl(rd, cid);
+ if (id < 0) {
+ ret = id;
+ goto get_time_exit;
+ }
switch (rd->rpl[id].prim_id) {
case PL1_ENABLE:
@@ -566,6 +587,8 @@
}
if (!ret)
*data = val;
+
+get_time_exit:
put_online_cpus();
return ret;
@@ -707,7 +730,7 @@
case ENERGY_UNIT:
scale = ENERGY_UNIT_SCALE;
/* per domain unit takes precedence */
- if (rd && rd->domain_energy_unit)
+ if (rd->domain_energy_unit)
units = rd->domain_energy_unit;
else
units = rp->energy_unit;
diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c
index 54382ef..a3ade9e 100644
--- a/drivers/regulator/axp20x-regulator.c
+++ b/drivers/regulator/axp20x-regulator.c
@@ -272,7 +272,7 @@
64, AXP806_DCDCD_V_CTRL, 0x3f, AXP806_PWR_OUT_CTRL1,
BIT(3)),
AXP_DESC(AXP806, DCDCE, "dcdce", "vine", 1100, 3400, 100,
- AXP806_DCDCB_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(4)),
+ AXP806_DCDCE_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(4)),
AXP_DESC(AXP806, ALDO1, "aldo1", "aldoin", 700, 3300, 100,
AXP806_ALDO1_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(5)),
AXP_DESC(AXP806, ALDO2, "aldo2", "aldoin", 700, 3400, 100,
@@ -337,10 +337,18 @@
AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)),
AXP_DESC(AXP809, ELDO3, "eldo3", "eldoin", 700, 3300, 100,
AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)),
- AXP_DESC_IO(AXP809, LDO_IO0, "ldo_io0", "ips", 700, 3300, 100,
+ /*
+ * Note the datasheet only guarantees reliable operation up to
+ * 3.3V, this needs to be enforced via dts provided constraints
+ */
+ AXP_DESC_IO(AXP809, LDO_IO0, "ldo_io0", "ips", 700, 3800, 100,
AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07,
AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
- AXP_DESC_IO(AXP809, LDO_IO1, "ldo_io1", "ips", 700, 3300, 100,
+ /*
+ * Note the datasheet only guarantees reliable operation up to
+ * 3.3V, this needs to be enforced via dts provided constraints
+ */
+ AXP_DESC_IO(AXP809, LDO_IO1, "ldo_io1", "ips", 700, 3800, 100,
AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07,
AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
AXP_DESC_FIXED(AXP809, RTC_LDO, "rtc_ldo", "ips", 1800),
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 183fa22..da0f284 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -198,11 +198,11 @@
static struct device_node *of_get_regulator(struct device *dev, const char *supply)
{
struct device_node *regnode = NULL;
- char prop_name[32]; /* 32 is max size of property name */
+ char prop_name[256];
dev_dbg(dev, "Looking up %s-supply from device tree\n", supply);
- snprintf(prop_name, 32, "%s-supply", supply);
+ snprintf(prop_name, sizeof(prop_name), "%s-supply", supply);
regnode = of_parse_phandle(dev->of_node, prop_name, 0);
if (!regnode) {
diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
index d7da81a..13d53a3 100644
--- a/drivers/regulator/fan53555.c
+++ b/drivers/regulator/fan53555.c
@@ -55,6 +55,7 @@
enum fan53555_vendor {
FAN53555_VENDOR_FAIRCHILD = 0,
FAN53555_VENDOR_SILERGY,
+ HALO_HL7509,
};
/* IC Type */
@@ -98,6 +99,8 @@
unsigned int slew_rate;
/* Sleep voltage cache */
unsigned int sleep_vol_cache;
+ /* Disable suspend */
+ bool disable_suspend;
};
static int fan53555_set_suspend_voltage(struct regulator_dev *rdev, int uV)
@@ -105,6 +108,8 @@
struct fan53555_device_info *di = rdev_get_drvdata(rdev);
int ret;
+ if (di->disable_suspend)
+ return 0;
if (di->sleep_vol_cache == uV)
return 0;
ret = regulator_map_voltage_linear(rdev, uV, uV);
@@ -309,6 +314,9 @@
case FAN53555_VENDOR_SILERGY:
ret = fan53555_voltages_setup_silergy(di);
break;
+ case HALO_HL7509:
+ ret = fan53555_voltages_setup_fairchild(di);
+ break;
default:
dev_err(di->dev, "vendor %d not supported!\n", di->vendor);
return -EINVAL;
@@ -344,26 +352,29 @@
.val_bits = 8,
};
-static struct fan53555_platform_data *fan53555_parse_dt(struct device *dev,
- struct device_node *np,
- const struct regulator_desc *desc)
+static int fan53555_parse_dt(struct fan53555_device_info *di,
+ struct fan53555_platform_data *pdata,
+ const struct regulator_desc *desc)
{
- struct fan53555_platform_data *pdata;
+ struct device *dev = di->dev;
+ struct device_node *np = dev->of_node;
int ret;
u32 tmp;
- pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return NULL;
-
pdata->regulator = of_get_regulator_init_data(dev, np, desc);
+ if (!pdata->regulator) {
+ dev_err(dev, "regulator init data is missing\n");
+ return -ENODEV;
+ }
ret = of_property_read_u32(np, "fcs,suspend-voltage-selector",
&tmp);
if (!ret)
pdata->sleep_vsel_id = tmp;
- return pdata;
+ di->disable_suspend = of_property_read_bool(np, "fcs,disable-suspend");
+
+ return ret;
}
static const struct of_device_id fan53555_dt_ids[] = {
@@ -376,6 +387,9 @@
}, {
.compatible = "silergy,syr828",
.data = (void *)FAN53555_VENDOR_SILERGY,
+ }, {
+ .compatible = "halo,hl7509",
+ .data = (void *)HALO_HL7509,
},
{ }
};
@@ -384,7 +398,6 @@
static int fan53555_regulator_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct device_node *np = client->dev.of_node;
struct fan53555_device_info *di;
struct fan53555_platform_data *pdata;
struct regulator_config config = { };
@@ -397,14 +410,17 @@
return -ENOMEM;
pdata = dev_get_platdata(&client->dev);
- if (!pdata)
- pdata = fan53555_parse_dt(&client->dev, np, &di->desc);
-
- if (!pdata || !pdata->regulator) {
- dev_err(&client->dev, "Platform data not found!\n");
- return -ENODEV;
+ if (!pdata) {
+ pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
}
+ di->dev = &client->dev;
+ ret = fan53555_parse_dt(di, pdata, &di->desc);
+ if (ret)
+ return ret;
+
di->regulator = pdata->regulator;
if (client->dev.of_node) {
const struct of_device_id *match;
@@ -433,7 +449,6 @@
dev_err(&client->dev, "Failed to allocate regmap!\n");
return PTR_ERR(di->regmap);
}
- di->dev = &client->dev;
i2c_set_clientdata(client, di);
/* Get chip ID */
ret = regmap_read(di->regmap, FAN53555_ID1, &val);
@@ -462,7 +477,7 @@
config.init_data = di->regulator;
config.regmap = di->regmap;
config.driver_data = di;
- config.of_node = np;
+ config.of_node = client->dev.of_node;
ret = fan53555_regulator_register(di, &config);
if (ret < 0)
@@ -478,6 +493,9 @@
}, {
.name = "syr82x",
.driver_data = FAN53555_VENDOR_SILERGY
+ }, {
+ .name = "hl7509",
+ .driver_data = HALO_HL7509
},
{ },
};
diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c
index bcf38fd..379cdac 100644
--- a/drivers/regulator/helpers.c
+++ b/drivers/regulator/helpers.c
@@ -454,13 +454,17 @@
int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable)
{
unsigned int val;
+ unsigned int val_on = rdev->desc->bypass_val_on;
int ret;
ret = regmap_read(rdev->regmap, rdev->desc->bypass_reg, &val);
if (ret != 0)
return ret;
- *enable = (val & rdev->desc->bypass_mask) == rdev->desc->bypass_val_on;
+ if (!val_on)
+ val_on = rdev->desc->bypass_mask;
+
+ *enable = (val & rdev->desc->bypass_mask) == val_on;
return 0;
}
diff --git a/drivers/regulator/rpmh-regulator.c b/drivers/regulator/rpmh-regulator.c
index db4b0fa..2987ed2 100644
--- a/drivers/regulator/rpmh-regulator.c
+++ b/drivers/regulator/rpmh-regulator.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1245,6 +1245,11 @@
return -EINVAL;
}
}
+
+ prop = "qcom,min-dropout-voltage-level";
+ rc = of_property_read_u32(vreg->of_node, prop, &temp);
+ if (!rc)
+ vreg->rdesc.min_dropout_uV = temp;
} else if (type == RPMH_REGULATOR_TYPE_VRM) {
prop = "qcom,init-enable";
rc = of_property_read_u32(vreg->of_node, prop, &temp);
@@ -1293,6 +1298,11 @@
RPMH_REGULATOR_REG_VRM_HEADROOM,
temp / 1000);
}
+
+ prop = "qcom,min-dropout-voltage";
+ rc = of_property_read_u32(vreg->of_node, prop, &temp);
+ if (!rc)
+ vreg->rdesc.min_dropout_uV = temp;
}
return 0;
diff --git a/drivers/regulator/stw481x-vmmc.c b/drivers/regulator/stw481x-vmmc.c
index 7d2ae3e..342f5da 100644
--- a/drivers/regulator/stw481x-vmmc.c
+++ b/drivers/regulator/stw481x-vmmc.c
@@ -47,7 +47,8 @@
.volt_table = stw481x_vmmc_voltages,
.enable_time = 200, /* FIXME: look this up */
.enable_reg = STW_CONF1,
- .enable_mask = STW_CONF1_PDN_VMMC,
+ .enable_mask = STW_CONF1_PDN_VMMC | STW_CONF1_MMC_LS_STATUS,
+ .enable_val = STW_CONF1_PDN_VMMC,
.vsel_reg = STW_CONF1,
.vsel_mask = STW_CONF1_VMMC_MASK,
};
diff --git a/drivers/regulator/tps65086-regulator.c b/drivers/regulator/tps65086-regulator.c
index 33f389d..caf174f 100644
--- a/drivers/regulator/tps65086-regulator.c
+++ b/drivers/regulator/tps65086-regulator.c
@@ -71,18 +71,17 @@
unsigned int decay_mask;
};
-static const struct regulator_linear_range tps65086_buck126_10mv_ranges[] = {
+static const struct regulator_linear_range tps65086_10mv_ranges[] = {
REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0),
REGULATOR_LINEAR_RANGE(410000, 0x1, 0x7F, 10000),
};
static const struct regulator_linear_range tps65086_buck126_25mv_ranges[] = {
- REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0),
- REGULATOR_LINEAR_RANGE(1000000, 0x1, 0x18, 0),
+ REGULATOR_LINEAR_RANGE(1000000, 0x0, 0x18, 0),
REGULATOR_LINEAR_RANGE(1025000, 0x19, 0x7F, 25000),
};
-static const struct regulator_linear_range tps65086_buck345_ranges[] = {
+static const struct regulator_linear_range tps65086_buck345_25mv_ranges[] = {
REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0),
REGULATOR_LINEAR_RANGE(425000, 0x1, 0x7F, 25000),
};
@@ -125,27 +124,27 @@
static struct tps65086_regulator regulators[] = {
TPS65086_REGULATOR("BUCK1", "buck1", BUCK1, 0x80, TPS65086_BUCK1CTRL,
BUCK_VID_MASK, TPS65086_BUCK123CTRL, BIT(0),
- tps65086_buck126_10mv_ranges, TPS65086_BUCK1CTRL,
+ tps65086_10mv_ranges, TPS65086_BUCK1CTRL,
BIT(0)),
TPS65086_REGULATOR("BUCK2", "buck2", BUCK2, 0x80, TPS65086_BUCK2CTRL,
BUCK_VID_MASK, TPS65086_BUCK123CTRL, BIT(1),
- tps65086_buck126_10mv_ranges, TPS65086_BUCK2CTRL,
+ tps65086_10mv_ranges, TPS65086_BUCK2CTRL,
BIT(0)),
TPS65086_REGULATOR("BUCK3", "buck3", BUCK3, 0x80, TPS65086_BUCK3VID,
BUCK_VID_MASK, TPS65086_BUCK123CTRL, BIT(2),
- tps65086_buck345_ranges, TPS65086_BUCK3DECAY,
+ tps65086_10mv_ranges, TPS65086_BUCK3DECAY,
BIT(0)),
TPS65086_REGULATOR("BUCK4", "buck4", BUCK4, 0x80, TPS65086_BUCK4VID,
BUCK_VID_MASK, TPS65086_BUCK4CTRL, BIT(0),
- tps65086_buck345_ranges, TPS65086_BUCK4VID,
+ tps65086_10mv_ranges, TPS65086_BUCK4VID,
BIT(0)),
TPS65086_REGULATOR("BUCK5", "buck5", BUCK5, 0x80, TPS65086_BUCK5VID,
BUCK_VID_MASK, TPS65086_BUCK5CTRL, BIT(0),
- tps65086_buck345_ranges, TPS65086_BUCK5CTRL,
+ tps65086_10mv_ranges, TPS65086_BUCK5CTRL,
BIT(0)),
TPS65086_REGULATOR("BUCK6", "buck6", BUCK6, 0x80, TPS65086_BUCK6VID,
BUCK_VID_MASK, TPS65086_BUCK6CTRL, BIT(0),
- tps65086_buck126_10mv_ranges, TPS65086_BUCK6CTRL,
+ tps65086_10mv_ranges, TPS65086_BUCK6CTRL,
BIT(0)),
TPS65086_REGULATOR("LDOA1", "ldoa1", LDOA1, 0xF, TPS65086_LDOA1CTRL,
VDOA1_VID_MASK, TPS65086_LDOA1CTRL, BIT(0),
@@ -162,18 +161,6 @@
TPS65086_SWITCH("VTT", "vtt", VTT, TPS65086_SWVTT_EN, BIT(4)),
};
-static inline bool has_25mv_mode(int id)
-{
- switch (id) {
- case BUCK1:
- case BUCK2:
- case BUCK6:
- return true;
- default:
- return false;
- }
-}
-
static int tps65086_of_parse_cb(struct device_node *dev,
const struct regulator_desc *desc,
struct regulator_config *config)
@@ -181,12 +168,27 @@
int ret;
/* Check for 25mV step mode */
- if (has_25mv_mode(desc->id) &&
- of_property_read_bool(config->of_node, "ti,regulator-step-size-25mv")) {
- regulators[desc->id].desc.linear_ranges =
+ if (of_property_read_bool(config->of_node, "ti,regulator-step-size-25mv")) {
+ switch (desc->id) {
+ case BUCK1:
+ case BUCK2:
+ case BUCK6:
+ regulators[desc->id].desc.linear_ranges =
tps65086_buck126_25mv_ranges;
- regulators[desc->id].desc.n_linear_ranges =
+ regulators[desc->id].desc.n_linear_ranges =
ARRAY_SIZE(tps65086_buck126_25mv_ranges);
+ break;
+ case BUCK3:
+ case BUCK4:
+ case BUCK5:
+ regulators[desc->id].desc.linear_ranges =
+ tps65086_buck345_25mv_ranges;
+ regulators[desc->id].desc.n_linear_ranges =
+ ARRAY_SIZE(tps65086_buck345_25mv_ranges);
+ break;
+ default:
+ dev_warn(config->dev, "25mV step mode only valid for BUCK regulators\n");
+ }
}
/* Check for decay mode */
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index f396bfe..5fcbefc 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -91,17 +91,12 @@
Say y here to support the Qualcomm Peripherial Image Loader for the
Hexagon V5 based remote processors.
-config QCOM_WCNSS_IRIS
- tristate
- depends on OF && ARCH_QCOM
-
config QCOM_WCNSS_PIL
tristate "Qualcomm WCNSS Peripheral Image Loader"
depends on OF && ARCH_QCOM
depends on QCOM_SMEM
select QCOM_MDT_LOADER
select QCOM_SCM
- select QCOM_WCNSS_IRIS
select REMOTEPROC
help
Say y here to support the Peripheral Image Loader for the Qualcomm
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 6dfb62e..034b6f3 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -13,6 +13,7 @@
obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o
obj-$(CONFIG_QCOM_MDT_LOADER) += qcom_mdt_loader.o
obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o
-obj-$(CONFIG_QCOM_WCNSS_IRIS) += qcom_wcnss_iris.o
-obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss.o
+obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o
+qcom_wcnss_pil-y += qcom_wcnss.o
+qcom_wcnss_pil-y += qcom_wcnss_iris.o
obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o
diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c
index f5cedea..323b629 100644
--- a/drivers/remoteproc/qcom_wcnss.c
+++ b/drivers/remoteproc/qcom_wcnss.c
@@ -143,7 +143,6 @@
mutex_unlock(&wcnss->iris_lock);
}
-EXPORT_SYMBOL_GPL(qcom_wcnss_assign_iris);
static int wcnss_load(struct rproc *rproc, const struct firmware *fw)
{
@@ -619,6 +618,28 @@
},
};
-module_platform_driver(wcnss_driver);
+static int __init wcnss_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&wcnss_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&qcom_iris_driver);
+ if (ret)
+ platform_driver_unregister(&wcnss_driver);
+
+ return ret;
+}
+module_init(wcnss_init);
+
+static void __exit wcnss_exit(void)
+{
+ platform_driver_unregister(&qcom_iris_driver);
+ platform_driver_unregister(&wcnss_driver);
+}
+module_exit(wcnss_exit);
+
MODULE_DESCRIPTION("Qualcomm Peripherial Image Loader for Wireless Subsystem");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/remoteproc/qcom_wcnss.h b/drivers/remoteproc/qcom_wcnss.h
index 9dc4a9f..25fb7f6 100644
--- a/drivers/remoteproc/qcom_wcnss.h
+++ b/drivers/remoteproc/qcom_wcnss.h
@@ -4,6 +4,8 @@
struct qcom_iris;
struct qcom_wcnss;
+extern struct platform_driver qcom_iris_driver;
+
struct wcnss_vreg_info {
const char * const name;
int min_voltage;
diff --git a/drivers/remoteproc/qcom_wcnss_iris.c b/drivers/remoteproc/qcom_wcnss_iris.c
index f0ca24a..05d6e17 100644
--- a/drivers/remoteproc/qcom_wcnss_iris.c
+++ b/drivers/remoteproc/qcom_wcnss_iris.c
@@ -94,14 +94,12 @@
return ret;
}
-EXPORT_SYMBOL_GPL(qcom_iris_enable);
void qcom_iris_disable(struct qcom_iris *iris)
{
clk_disable_unprepare(iris->xo_clk);
regulator_bulk_disable(iris->num_vregs, iris->vregs);
}
-EXPORT_SYMBOL_GPL(qcom_iris_disable);
static int qcom_iris_probe(struct platform_device *pdev)
{
@@ -174,7 +172,7 @@
{}
};
-static struct platform_driver wcnss_driver = {
+struct platform_driver qcom_iris_driver = {
.probe = qcom_iris_probe,
.remove = qcom_iris_remove,
.driver = {
@@ -182,7 +180,3 @@
.of_match_table = iris_of_match,
},
};
-
-module_platform_driver(wcnss_driver);
-MODULE_DESCRIPTION("Qualcomm Wireless Subsystem Iris driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c
index ae8963f..da4e152 100644
--- a/drivers/remoteproc/st_remoteproc.c
+++ b/drivers/remoteproc/st_remoteproc.c
@@ -245,8 +245,10 @@
goto free_rproc;
enabled = st_rproc_state(pdev);
- if (enabled < 0)
+ if (enabled < 0) {
+ ret = enabled;
goto free_rproc;
+ }
if (enabled) {
atomic_inc(&rproc->power);
diff --git a/drivers/rpmsg/qcom_smd.c b/drivers/rpmsg/qcom_smd.c
index 06fef2b..1d4770c 100644
--- a/drivers/rpmsg/qcom_smd.c
+++ b/drivers/rpmsg/qcom_smd.c
@@ -739,7 +739,7 @@
while (qcom_smd_get_tx_avail(channel) < tlen) {
if (!wait) {
- ret = -ENOMEM;
+ ret = -EAGAIN;
goto out;
}
diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c
index b6ea9ff..e0a629e 100644
--- a/drivers/rpmsg/rpmsg_core.c
+++ b/drivers/rpmsg/rpmsg_core.c
@@ -411,8 +411,8 @@
struct device *dev = &rpdev->dev;
int ret;
- dev_set_name(&rpdev->dev, "%s:%s",
- dev_name(dev->parent), rpdev->id.name);
+ dev_set_name(&rpdev->dev, "%s.%s.%d.%d", dev_name(dev->parent),
+ rpdev->id.name, rpdev->src, rpdev->dst);
rpdev->dev.bus = &rpmsg_bus;
rpdev->dev.release = rpmsg_release_device;
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index e883063..3167e85 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -870,7 +870,7 @@
goto cleanup;
for (i=0; i < MAXMINOR; ++i ) {
- sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL);
+ sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sys_ser[i].buffer) {
rc = -ENOMEM;
break;
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index 5810019..d5bf36e 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -289,11 +289,12 @@
/**
- * zfcp_dbf_rec_run - trace event related to running recovery
+ * zfcp_dbf_rec_run_lvl - trace event related to running recovery
+ * @level: trace level to be used for event
* @tag: identifier for event
* @erp: erp_action running
*/
-void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp)
+void zfcp_dbf_rec_run_lvl(int level, char *tag, struct zfcp_erp_action *erp)
{
struct zfcp_dbf *dbf = erp->adapter->dbf;
struct zfcp_dbf_rec *rec = &dbf->rec_buf;
@@ -319,11 +320,21 @@
else
rec->u.run.rec_count = atomic_read(&erp->adapter->erp_counter);
- debug_event(dbf->rec, 1, rec, sizeof(*rec));
+ debug_event(dbf->rec, level, rec, sizeof(*rec));
spin_unlock_irqrestore(&dbf->rec_lock, flags);
}
/**
+ * zfcp_dbf_rec_run - trace event related to running recovery
+ * @tag: identifier for event
+ * @erp: erp_action running
+ */
+void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp)
+{
+ zfcp_dbf_rec_run_lvl(1, tag, erp);
+}
+
+/**
* zfcp_dbf_rec_run_wka - trace wka port event with info like running recovery
* @tag: identifier for event
* @wka_port: well known address port
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index 36d0758..db186d4 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -2,7 +2,7 @@
* zfcp device driver
* debug feature declarations
*
- * Copyright IBM Corp. 2008, 2015
+ * Copyright IBM Corp. 2008, 2016
*/
#ifndef ZFCP_DBF_H
@@ -283,6 +283,30 @@
struct zfcp_dbf_scsi scsi_buf;
};
+/**
+ * zfcp_dbf_hba_fsf_resp_suppress - true if we should not trace by default
+ * @req: request that has been completed
+ *
+ * Returns true if FCP response with only benign residual under count.
+ */
+static inline
+bool zfcp_dbf_hba_fsf_resp_suppress(struct zfcp_fsf_req *req)
+{
+ struct fsf_qtcb *qtcb = req->qtcb;
+ u32 fsf_stat = qtcb->header.fsf_status;
+ struct fcp_resp *fcp_rsp;
+ u8 rsp_flags, fr_status;
+
+ if (qtcb->prefix.qtcb_type != FSF_IO_COMMAND)
+ return false; /* not an FCP response */
+ fcp_rsp = (struct fcp_resp *)&qtcb->bottom.io.fcp_rsp;
+ rsp_flags = fcp_rsp->fr_flags;
+ fr_status = fcp_rsp->fr_status;
+ return (fsf_stat == FSF_FCP_RSP_AVAILABLE) &&
+ (rsp_flags == FCP_RESID_UNDER) &&
+ (fr_status == SAM_STAT_GOOD);
+}
+
static inline
void zfcp_dbf_hba_fsf_resp(char *tag, int level, struct zfcp_fsf_req *req)
{
@@ -304,7 +328,9 @@
zfcp_dbf_hba_fsf_resp("fs_perr", 1, req);
} else if (qtcb->header.fsf_status != FSF_GOOD) {
- zfcp_dbf_hba_fsf_resp("fs_ferr", 1, req);
+ zfcp_dbf_hba_fsf_resp("fs_ferr",
+ zfcp_dbf_hba_fsf_resp_suppress(req)
+ ? 5 : 1, req);
} else if ((req->fsf_command == FSF_QTCB_OPEN_PORT_WITH_DID) ||
(req->fsf_command == FSF_QTCB_OPEN_LUN)) {
@@ -388,4 +414,15 @@
_zfcp_dbf_scsi(tmp_tag, 1, scmnd, NULL);
}
+/**
+ * zfcp_dbf_scsi_nullcmnd() - trace NULLify of SCSI command in dev/tgt-reset.
+ * @scmnd: SCSI command that was NULLified.
+ * @fsf_req: request that owned @scmnd.
+ */
+static inline void zfcp_dbf_scsi_nullcmnd(struct scsi_cmnd *scmnd,
+ struct zfcp_fsf_req *fsf_req)
+{
+ _zfcp_dbf_scsi("scfc__1", 3, scmnd, fsf_req);
+}
+
#endif /* ZFCP_DBF_H */
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index a59d678..7ccfce5 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -3,7 +3,7 @@
*
* Error Recovery Procedures (ERP).
*
- * Copyright IBM Corp. 2002, 2015
+ * Copyright IBM Corp. 2002, 2016
*/
#define KMSG_COMPONENT "zfcp"
@@ -1204,6 +1204,62 @@
}
}
+/**
+ * zfcp_erp_try_rport_unblock - unblock rport if no more/new recovery
+ * @port: zfcp_port whose fc_rport we should try to unblock
+ */
+static void zfcp_erp_try_rport_unblock(struct zfcp_port *port)
+{
+ unsigned long flags;
+ struct zfcp_adapter *adapter = port->adapter;
+ int port_status;
+ struct Scsi_Host *shost = adapter->scsi_host;
+ struct scsi_device *sdev;
+
+ write_lock_irqsave(&adapter->erp_lock, flags);
+ port_status = atomic_read(&port->status);
+ if ((port_status & ZFCP_STATUS_COMMON_UNBLOCKED) == 0 ||
+ (port_status & (ZFCP_STATUS_COMMON_ERP_INUSE |
+ ZFCP_STATUS_COMMON_ERP_FAILED)) != 0) {
+ /* new ERP of severity >= port triggered elsewhere meanwhile or
+ * local link down (adapter erp_failed but not clear unblock)
+ */
+ zfcp_dbf_rec_run_lvl(4, "ertru_p", &port->erp_action);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
+ return;
+ }
+ spin_lock(shost->host_lock);
+ __shost_for_each_device(sdev, shost) {
+ struct zfcp_scsi_dev *zsdev = sdev_to_zfcp(sdev);
+ int lun_status;
+
+ if (zsdev->port != port)
+ continue;
+ /* LUN under port of interest */
+ lun_status = atomic_read(&zsdev->status);
+ if ((lun_status & ZFCP_STATUS_COMMON_ERP_FAILED) != 0)
+ continue; /* unblock rport despite failed LUNs */
+ /* LUN recovery not given up yet [maybe follow-up pending] */
+ if ((lun_status & ZFCP_STATUS_COMMON_UNBLOCKED) == 0 ||
+ (lun_status & ZFCP_STATUS_COMMON_ERP_INUSE) != 0) {
+ /* LUN blocked:
+ * not yet unblocked [LUN recovery pending]
+ * or meanwhile blocked [new LUN recovery triggered]
+ */
+ zfcp_dbf_rec_run_lvl(4, "ertru_l", &zsdev->erp_action);
+ spin_unlock(shost->host_lock);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
+ return;
+ }
+ }
+ /* now port has no child or all children have completed recovery,
+ * and no ERP of severity >= port was meanwhile triggered elsewhere
+ */
+ zfcp_scsi_schedule_rport_register(port);
+ spin_unlock(shost->host_lock);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
+}
+
static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
{
struct zfcp_adapter *adapter = act->adapter;
@@ -1214,6 +1270,7 @@
case ZFCP_ERP_ACTION_REOPEN_LUN:
if (!(act->status & ZFCP_STATUS_ERP_NO_REF))
scsi_device_put(sdev);
+ zfcp_erp_try_rport_unblock(port);
break;
case ZFCP_ERP_ACTION_REOPEN_PORT:
@@ -1224,7 +1281,7 @@
*/
if (act->step != ZFCP_ERP_STEP_UNINITIALIZED)
if (result == ZFCP_ERP_SUCCEEDED)
- zfcp_scsi_schedule_rport_register(port);
+ zfcp_erp_try_rport_unblock(port);
/* fall through */
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
put_device(&port->dev);
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index c8fed9f..21c8c68 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -3,7 +3,7 @@
*
* External function declarations.
*
- * Copyright IBM Corp. 2002, 2015
+ * Copyright IBM Corp. 2002, 2016
*/
#ifndef ZFCP_EXT_H
@@ -35,6 +35,8 @@
extern void zfcp_dbf_rec_trig(char *, struct zfcp_adapter *,
struct zfcp_port *, struct scsi_device *, u8, u8);
extern void zfcp_dbf_rec_run(char *, struct zfcp_erp_action *);
+extern void zfcp_dbf_rec_run_lvl(int level, char *tag,
+ struct zfcp_erp_action *erp);
extern void zfcp_dbf_rec_run_wka(char *, struct zfcp_fc_wka_port *, u64);
extern void zfcp_dbf_hba_fsf_uss(char *, struct zfcp_fsf_req *);
extern void zfcp_dbf_hba_fsf_res(char *, int, struct zfcp_fsf_req *);
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h
index be1c04b..ea3c76a 100644
--- a/drivers/s390/scsi/zfcp_fsf.h
+++ b/drivers/s390/scsi/zfcp_fsf.h
@@ -3,7 +3,7 @@
*
* Interface to the FSF support functions.
*
- * Copyright IBM Corp. 2002, 2015
+ * Copyright IBM Corp. 2002, 2016
*/
#ifndef FSF_H
@@ -78,6 +78,7 @@
#define FSF_APP_TAG_CHECK_FAILURE 0x00000082
#define FSF_REF_TAG_CHECK_FAILURE 0x00000083
#define FSF_ADAPTER_STATUS_AVAILABLE 0x000000AD
+#define FSF_FCP_RSP_AVAILABLE 0x000000AF
#define FSF_UNKNOWN_COMMAND 0x000000E2
#define FSF_UNKNOWN_OP_SUBTYPE 0x000000E3
#define FSF_INVALID_COMMAND_OPTION 0x000000E5
diff --git a/drivers/s390/scsi/zfcp_reqlist.h b/drivers/s390/scsi/zfcp_reqlist.h
index 7c2c619..703fce5 100644
--- a/drivers/s390/scsi/zfcp_reqlist.h
+++ b/drivers/s390/scsi/zfcp_reqlist.h
@@ -4,7 +4,7 @@
* Data structure and helper functions for tracking pending FSF
* requests.
*
- * Copyright IBM Corp. 2009
+ * Copyright IBM Corp. 2009, 2016
*/
#ifndef ZFCP_REQLIST_H
@@ -180,4 +180,32 @@
spin_unlock_irqrestore(&rl->lock, flags);
}
+/**
+ * zfcp_reqlist_apply_for_all() - apply a function to every request.
+ * @rl: the requestlist that contains the target requests.
+ * @f: the function to apply to each request; the first parameter of the
+ * function will be the target-request; the second parameter is the same
+ * pointer as given with the argument @data.
+ * @data: freely chosen argument; passed through to @f as second parameter.
+ *
+ * Uses :c:macro:`list_for_each_entry` to iterate over the lists in the hash-
+ * table (not a 'safe' variant, so don't modify the list).
+ *
+ * Holds @rl->lock over the entire request-iteration.
+ */
+static inline void
+zfcp_reqlist_apply_for_all(struct zfcp_reqlist *rl,
+ void (*f)(struct zfcp_fsf_req *, void *), void *data)
+{
+ struct zfcp_fsf_req *req;
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&rl->lock, flags);
+ for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++)
+ list_for_each_entry(req, &rl->buckets[i], list)
+ f(req, data);
+ spin_unlock_irqrestore(&rl->lock, flags);
+}
+
#endif /* ZFCP_REQLIST_H */
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 9069f98..07ffdbb 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -3,7 +3,7 @@
*
* Interface to Linux SCSI midlayer.
*
- * Copyright IBM Corp. 2002, 2015
+ * Copyright IBM Corp. 2002, 2016
*/
#define KMSG_COMPONENT "zfcp"
@@ -88,9 +88,7 @@
}
if (unlikely(!(status & ZFCP_STATUS_COMMON_UNBLOCKED))) {
- /* This could be either
- * open LUN pending: this is temporary, will result in
- * open LUN or ERP_FAILED, so retry command
+ /* This could be
* call to rport_delete pending: mimic retry from
* fc_remote_port_chkready until rport is BLOCKED
*/
@@ -209,6 +207,57 @@
return retval;
}
+struct zfcp_scsi_req_filter {
+ u8 tmf_scope;
+ u32 lun_handle;
+ u32 port_handle;
+};
+
+static void zfcp_scsi_forget_cmnd(struct zfcp_fsf_req *old_req, void *data)
+{
+ struct zfcp_scsi_req_filter *filter =
+ (struct zfcp_scsi_req_filter *)data;
+
+ /* already aborted - prevent side-effects - or not a SCSI command */
+ if (old_req->data == NULL || old_req->fsf_command != FSF_QTCB_FCP_CMND)
+ return;
+
+ /* (tmf_scope == FCP_TMF_TGT_RESET || tmf_scope == FCP_TMF_LUN_RESET) */
+ if (old_req->qtcb->header.port_handle != filter->port_handle)
+ return;
+
+ if (filter->tmf_scope == FCP_TMF_LUN_RESET &&
+ old_req->qtcb->header.lun_handle != filter->lun_handle)
+ return;
+
+ zfcp_dbf_scsi_nullcmnd((struct scsi_cmnd *)old_req->data, old_req);
+ old_req->data = NULL;
+}
+
+static void zfcp_scsi_forget_cmnds(struct zfcp_scsi_dev *zsdev, u8 tm_flags)
+{
+ struct zfcp_adapter *adapter = zsdev->port->adapter;
+ struct zfcp_scsi_req_filter filter = {
+ .tmf_scope = FCP_TMF_TGT_RESET,
+ .port_handle = zsdev->port->handle,
+ };
+ unsigned long flags;
+
+ if (tm_flags == FCP_TMF_LUN_RESET) {
+ filter.tmf_scope = FCP_TMF_LUN_RESET;
+ filter.lun_handle = zsdev->lun_handle;
+ }
+
+ /*
+ * abort_lock secures against other processings - in the abort-function
+ * and normal cmnd-handler - of (struct zfcp_fsf_req *)->data
+ */
+ write_lock_irqsave(&adapter->abort_lock, flags);
+ zfcp_reqlist_apply_for_all(adapter->req_list, zfcp_scsi_forget_cmnd,
+ &filter);
+ write_unlock_irqrestore(&adapter->abort_lock, flags);
+}
+
static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags)
{
struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scpnt->device);
@@ -241,8 +290,10 @@
if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) {
zfcp_dbf_scsi_devreset("fail", scpnt, tm_flags);
retval = FAILED;
- } else
+ } else {
zfcp_dbf_scsi_devreset("okay", scpnt, tm_flags);
+ zfcp_scsi_forget_cmnds(zfcp_sdev, tm_flags);
+ }
zfcp_fsf_req_free(fsf_req);
return retval;
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
index 79871f3..d5b26fa 100644
--- a/drivers/scsi/aacraid/linit.c
+++ b/drivers/scsi/aacraid/linit.c
@@ -160,7 +160,6 @@
{ 0x9005, 0x028b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 62 }, /* Adaptec PMC Series 6 (Tupelo) */
{ 0x9005, 0x028c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 63 }, /* Adaptec PMC Series 7 (Denali) */
{ 0x9005, 0x028d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 64 }, /* Adaptec PMC Series 8 */
- { 0x9005, 0x028f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 65 }, /* Adaptec PMC Series 9 */
{ 0,}
};
MODULE_DEVICE_TABLE(pci, aac_pci_tbl);
@@ -239,7 +238,6 @@
{ aac_src_init, "aacraid", "ADAPTEC ", "RAID ", 2, AAC_QUIRK_SRC }, /* Adaptec PMC Series 6 (Tupelo) */
{ aac_srcv_init, "aacraid", "ADAPTEC ", "RAID ", 2, AAC_QUIRK_SRC }, /* Adaptec PMC Series 7 (Denali) */
{ aac_srcv_init, "aacraid", "ADAPTEC ", "RAID ", 2, AAC_QUIRK_SRC }, /* Adaptec PMC Series 8 */
- { aac_srcv_init, "aacraid", "ADAPTEC ", "RAID ", 2, AAC_QUIRK_SRC } /* Adaptec PMC Series 9 */
};
/**
diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c
index cbf0103..596a759 100644
--- a/drivers/scsi/g_NCR5380.c
+++ b/drivers/scsi/g_NCR5380.c
@@ -170,12 +170,12 @@
if (ports[i]) {
/* At this point we have our region reserved */
magic_configure(i, 0, magic); /* no IRQ yet */
- outb(0xc0, ports[i] + 9);
- if (inb(ports[i] + 9) != 0x80) {
+ base = ports[i];
+ outb(0xc0, base + 9);
+ if (inb(base + 9) != 0x80) {
ret = -ENODEV;
goto out_release;
}
- base = ports[i];
port_idx = i;
} else
return -EINVAL;
diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
index 642b739..e3b911c 100644
--- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
+++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
@@ -45,6 +45,7 @@
#define INITIAL_SRP_LIMIT 800
#define DEFAULT_MAX_SECTORS 256
+#define MAX_TXU 1024 * 1024
static uint max_vdma_size = MAX_H_COPY_RDMA;
@@ -1239,7 +1240,7 @@
}
info = dma_alloc_coherent(&vscsi->dma_dev->dev, sizeof(*info), &token,
- GFP_KERNEL);
+ GFP_ATOMIC);
if (!info) {
dev_err(&vscsi->dev, "bad dma_alloc_coherent %p\n",
iue->target);
@@ -1291,7 +1292,7 @@
info->mad_version = cpu_to_be32(MAD_VERSION_1);
info->os_type = cpu_to_be32(LINUX);
memset(&info->port_max_txu[0], 0, sizeof(info->port_max_txu));
- info->port_max_txu[0] = cpu_to_be32(128 * PAGE_SIZE);
+ info->port_max_txu[0] = cpu_to_be32(MAX_TXU);
dma_wmb();
rc = h_copy_rdma(sizeof(*info), vscsi->dds.window[LOCAL].liobn,
@@ -1357,7 +1358,7 @@
}
cap = dma_alloc_coherent(&vscsi->dma_dev->dev, olen, &token,
- GFP_KERNEL);
+ GFP_ATOMIC);
if (!cap) {
dev_err(&vscsi->dev, "bad dma_alloc_coherent %p\n",
iue->target);
@@ -3702,7 +3703,7 @@
1, 1);
if (rc) {
pr_err("srp_transfer_data() failed: %d\n", rc);
- return -EAGAIN;
+ return -EIO;
}
/*
* We now tell TCM to add this WRITE CDB directly into the TCM storage
diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c
index 52d8bbf..bd04bd0 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fusion.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c
@@ -2000,6 +2000,8 @@
io_request->DevHandle = pd_sync->seq[pd_index].devHandle;
pRAID_Context->regLockFlags |=
(MR_RL_FLAGS_SEQ_NUM_ENABLE|MR_RL_FLAGS_GRANT_DESTINATION_CUDA);
+ pRAID_Context->Type = MPI2_TYPE_CUDA;
+ pRAID_Context->nseg = 0x1;
} else if (fusion->fast_path_io) {
pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id);
pRAID_Context->configSeqNum = 0;
@@ -2035,12 +2037,10 @@
pRAID_Context->timeoutValue =
cpu_to_le16((os_timeout_value > timeout_limit) ?
timeout_limit : os_timeout_value);
- if (fusion->adapter_type == INVADER_SERIES) {
- pRAID_Context->Type = MPI2_TYPE_CUDA;
- pRAID_Context->nseg = 0x1;
+ if (fusion->adapter_type == INVADER_SERIES)
io_request->IoFlags |=
cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH);
- }
+
cmd->request_desc->SCSIIO.RequestFlags =
(MPI2_REQ_DESCRIPT_FLAGS_FP_IO <<
MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
@@ -2823,6 +2823,7 @@
dev_err(&instance->pdev->dev, "pending commands remain after waiting, "
"will reset adapter scsi%d.\n",
instance->host->host_no);
+ *convert = 1;
retval = 1;
}
out:
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h
index 3e71bc1..7008061 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.h
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.h
@@ -393,6 +393,7 @@
* @eedp_enable: eedp support enable bit
* @eedp_type: 0(type_1), 1(type_2), 2(type_3)
* @eedp_block_length: block size
+ * @ata_command_pending: SATL passthrough outstanding for device
*/
struct MPT3SAS_DEVICE {
struct MPT3SAS_TARGET *sas_target;
@@ -402,6 +403,17 @@
u8 block;
u8 tlr_snoop_check;
u8 ignore_delay_remove;
+ /*
+ * Bug workaround for SATL handling: the mpt2/3sas firmware
+ * doesn't return BUSY or TASK_SET_FULL for subsequent
+ * commands while a SATL pass through is in operation as the
+ * spec requires, it simply does nothing with them until the
+ * pass through completes, causing them possibly to timeout if
+ * the passthrough is a long executing command (like format or
+ * secure erase). This variable allows us to do the right
+ * thing while a SATL command is pending.
+ */
+ unsigned long ata_command_pending;
};
#define MPT3_CMD_NOT_USED 0x8000 /* free */
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index 1c4744e..f84a608 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -3885,9 +3885,18 @@
}
}
-static inline bool ata_12_16_cmd(struct scsi_cmnd *scmd)
+static int _scsih_set_satl_pending(struct scsi_cmnd *scmd, bool pending)
{
- return (scmd->cmnd[0] == ATA_12 || scmd->cmnd[0] == ATA_16);
+ struct MPT3SAS_DEVICE *priv = scmd->device->hostdata;
+
+ if (scmd->cmnd[0] != ATA_12 && scmd->cmnd[0] != ATA_16)
+ return 0;
+
+ if (pending)
+ return test_and_set_bit(0, &priv->ata_command_pending);
+
+ clear_bit(0, &priv->ata_command_pending);
+ return 0;
}
/**
@@ -3911,9 +3920,7 @@
if (!scmd)
continue;
count++;
- if (ata_12_16_cmd(scmd))
- scsi_internal_device_unblock(scmd->device,
- SDEV_RUNNING);
+ _scsih_set_satl_pending(scmd, false);
mpt3sas_base_free_smid(ioc, smid);
scsi_dma_unmap(scmd);
if (ioc->pci_error_recovery)
@@ -4044,13 +4051,6 @@
if (ioc->logging_level & MPT_DEBUG_SCSI)
scsi_print_command(scmd);
- /*
- * Lock the device for any subsequent command until command is
- * done.
- */
- if (ata_12_16_cmd(scmd))
- scsi_internal_device_block(scmd->device);
-
sas_device_priv_data = scmd->device->hostdata;
if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
scmd->result = DID_NO_CONNECT << 16;
@@ -4064,6 +4064,19 @@
return 0;
}
+ /*
+ * Bug work around for firmware SATL handling. The loop
+ * is based on atomic operations and ensures consistency
+ * since we're lockless at this point
+ */
+ do {
+ if (test_bit(0, &sas_device_priv_data->ata_command_pending)) {
+ scmd->result = SAM_STAT_BUSY;
+ scmd->scsi_done(scmd);
+ return 0;
+ }
+ } while (_scsih_set_satl_pending(scmd, true));
+
sas_target_priv_data = sas_device_priv_data->sas_target;
/* invalid device handle */
@@ -4626,8 +4639,7 @@
if (scmd == NULL)
return 1;
- if (ata_12_16_cmd(scmd))
- scsi_internal_device_unblock(scmd->device, SDEV_RUNNING);
+ _scsih_set_satl_pending(scmd, false);
mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c
index 4c57d9a..7de5d8d 100644
--- a/drivers/scsi/mvsas/mv_94xx.c
+++ b/drivers/scsi/mvsas/mv_94xx.c
@@ -668,7 +668,7 @@
{
u32 tmp;
tmp = mvs_cr32(mvi, MVS_COMMAND_ACTIVE+(slot_idx >> 3));
- if (tmp && 1 << (slot_idx % 32)) {
+ if (tmp & 1 << (slot_idx % 32)) {
mv_printk("command active %08X, slot [%x].\n", tmp, slot_idx);
mvs_cw32(mvi, MVS_COMMAND_ACTIVE + (slot_idx >> 3),
1 << (slot_idx % 32));
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 56d6142..078d797 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -3489,7 +3489,7 @@
sizeof(struct ct6_dsd), 0,
SLAB_HWCACHE_ALIGN, NULL);
if (!ctx_cachep)
- goto fail_free_gid_list;
+ goto fail_free_srb_mempool;
}
ha->ctx_mempool = mempool_create_slab_pool(SRB_MIN_REQ,
ctx_cachep);
@@ -3642,7 +3642,7 @@
ha->loop_id_map = kzalloc(BITS_TO_LONGS(LOOPID_MAP_SIZE) * sizeof(long),
GFP_KERNEL);
if (!ha->loop_id_map)
- goto fail_async_pd;
+ goto fail_loop_id_map;
else {
qla2x00_set_reserved_loop_ids(ha);
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0123,
@@ -3651,6 +3651,8 @@
return 0;
+fail_loop_id_map:
+ dma_pool_free(ha->s_dma_pool, ha->async_pd, ha->async_pd_dma);
fail_async_pd:
dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma);
fail_ex_init_cb:
@@ -3678,6 +3680,10 @@
dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
ha->ms_iocb = NULL;
ha->ms_iocb_dma = 0;
+
+ if (ha->sns_cmd)
+ dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt),
+ ha->sns_cmd, ha->sns_cmd_dma);
fail_dma_pool:
if (IS_QLA82XX(ha) || ql2xenabledif) {
dma_pool_destroy(ha->fcp_cmnd_dma_pool);
@@ -3695,10 +3701,12 @@
kfree(ha->nvram);
ha->nvram = NULL;
fail_free_ctx_mempool:
- mempool_destroy(ha->ctx_mempool);
+ if (ha->ctx_mempool)
+ mempool_destroy(ha->ctx_mempool);
ha->ctx_mempool = NULL;
fail_free_srb_mempool:
- mempool_destroy(ha->srb_mempool);
+ if (ha->srb_mempool)
+ mempool_destroy(ha->srb_mempool);
ha->srb_mempool = NULL;
fail_free_gid_list:
dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha),
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index a72bfde..e90a8e1 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -1204,10 +1204,6 @@
struct request_queue *rq = sdev->request_queue;
struct scsi_target *starget = sdev->sdev_target;
- error = scsi_device_set_state(sdev, SDEV_RUNNING);
- if (error)
- return error;
-
error = scsi_target_add(starget);
if (error)
return error;
diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c
index 8c9a35c..50adabb 100644
--- a/drivers/scsi/ses.c
+++ b/drivers/scsi/ses.c
@@ -587,7 +587,7 @@
ses_enclosure_data_process(edev, to_scsi_device(edev->edev.parent), 0);
- if (scsi_is_sas_rphy(&sdev->sdev_gendev))
+ if (scsi_is_sas_rphy(sdev->sdev_target->dev.parent))
efd.addr = sas_get_address(sdev);
if (efd.addr) {
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index f22fa96..8251f6e 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -581,6 +581,9 @@
sg_io_hdr_t *hp;
unsigned char cmnd[SG_MAX_CDB_SIZE];
+ if (unlikely(segment_eq(get_fs(), KERNEL_DS)))
+ return -EINVAL;
+
if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
return -ENXIO;
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 4ad72b6..12a4758 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -8764,6 +8764,9 @@
if (hba->devfreq)
devfreq_remove_device(hba->devfreq);
}
+
+ ufshcd_exit_latency_hist(hba);
+
ufshcd_hba_exit(hba);
ufsdbg_remove_debugfs(hba);
}
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index d17dd49..709801f 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -888,6 +888,7 @@
bool no_ref_clk_gating;
int scsi_block_reqs_cnt;
+
int latency_hist_enabled;
struct io_latency_state io_lat_s;
};
diff --git a/drivers/slimbus/slim-msm.c b/drivers/slimbus/slim-msm.c
index b426a2c..e7d3381 100644
--- a/drivers/slimbus/slim-msm.c
+++ b/drivers/slimbus/slim-msm.c
@@ -18,6 +18,9 @@
#include <linux/gcd.h>
#include "slim-msm.h"
+/* Pipe Number Offset Mask */
+#define P_OFF_MASK 0x3FC
+
int msm_slim_rx_enqueue(struct msm_slim_ctrl *dev, u32 *buf, u8 len)
{
spin_lock(&dev->rx_lock);
@@ -899,7 +902,7 @@
}
/* Get the pipe indices for the message queues */
- pipe_offset = (readl_relaxed(dev->base + pipe_reg) & 0xfc) >> 2;
+ pipe_offset = (readl_relaxed(dev->base + pipe_reg) & P_OFF_MASK) >> 2;
dev_dbg(dev->dev, "Message queue pipe offset %d\n", pipe_offset);
config->mode = SPS_MODE_SRC;
@@ -959,7 +962,7 @@
}
/* Get the pipe indices for the message queues */
- pipe_offset = (readl_relaxed(dev->base + pipe_reg) & 0xfc) >> 2;
+ pipe_offset = (readl_relaxed(dev->base + pipe_reg) & P_OFF_MASK) >> 2;
pipe_offset += 1;
dev_dbg(dev->dev, "TX Message queue pipe offset %d\n", pipe_offset);
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 474f914..aa51411 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -174,8 +174,8 @@
gladiator hang detection and collects the context for the
gladiator hang.
-config MSM_GLADIATOR_ERP_V2
- tristate "GLADIATOR coherency interconnect error reporting driver v2"
+config MSM_GLADIATOR_ERP
+ tristate "GLADIATOR coherency interconnect error reporting driver"
help
Support dumping debug information for the GLADIATOR
cache interconnect in the error interrupt handler.
@@ -183,9 +183,9 @@
If unsure, say N.
-config PANIC_ON_GLADIATOR_ERROR_V2
- depends on MSM_GLADIATOR_ERP_V2
- bool "Panic on GLADIATOR error report v2"
+config PANIC_ON_GLADIATOR_ERROR
+ depends on MSM_GLADIATOR_ERP
+ bool "Panic on GLADIATOR error report"
help
Panic upon detection of an Gladiator coherency interconnect error
in order to support dumping debug information.
@@ -540,3 +540,72 @@
This option enables driver which provides communication interface
between MSM and WCD DSP over glink transport protocol. This driver
provides read and write interface via char device.
+
+config MSM_EVENT_TIMER
+ bool "Event timer"
+ help
+ This option enables a modules that manages a list of event timers
+ that need to be monitored by the PM. The enables the PM code to
+ monitor events that require the core to be awake and ready to
+ handle the event.
+
+config MSM_PM
+ depends on PM
+ select MSM_IDLE_STATS if DEBUG_FS
+ select CPU_IDLE_MULTIPLE_DRIVERS
+ bool "Qualcomm platform specific PM driver"
+ help
+ Platform specific power driver to manage cores and l2 low power
+ modes. It interface with various system driver and put the cores
+ into low power modes. It implements OS initiated scheme and
+ determines last CPU to call into PSCI for cluster Low power
+ modes.
+
+config MSM_NOPM
+ default y if !PM
+ bool
+ help
+ This enables bare minimum support of power management at platform level.
+ i.e WFI
+
+config APSS_CORE_EA
+ depends on CPU_FREQ && PM_OPP
+ bool "Qualcomm Technology Inc specific power aware driver"
+ help
+ Platform specific power aware driver to provide power
+ and temperature information to the scheduler.
+
+if MSM_PM
+menuconfig MSM_IDLE_STATS
+ bool "Collect idle statistics"
+ help
+ Collect cores various low power mode idle statistics
+ and export them in proc/msm_pm_stats. User can read
+ this data and determine what low power modes and how
+ many times cores have entered into LPM modes.
+
+if MSM_IDLE_STATS
+
+config MSM_IDLE_STATS_FIRST_BUCKET
+ int "First bucket time"
+ default 62500
+ help
+ Upper time limit in nanoseconds of first bucket.
+
+config MSM_IDLE_STATS_BUCKET_SHIFT
+ int "Bucket shift"
+ default 2
+
+config MSM_IDLE_STATS_BUCKET_COUNT
+ int "Bucket count"
+ default 10
+
+config MSM_SUSPEND_STATS_FIRST_BUCKET
+ int "First bucket time for suspend"
+ default 1000000000
+ help
+ Upper time limit in nanoseconds of first bucket of the
+ histogram. This is for collecting statistics on suspend.
+
+endif # MSM_IDLE_STATS
+endif # MSM_PM
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 531685c..207b116 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -18,7 +18,7 @@
obj-$(CONFIG_MSM_BOOT_STATS) += boot_stats.o
obj-$(CONFIG_MSM_CORE_HANG_DETECT) += core_hang_detect.o
obj-$(CONFIG_MSM_GLADIATOR_HANG_DETECT) += gladiator_hang_detect.o
-obj-$(CONFIG_MSM_GLADIATOR_ERP_V2) += gladiator_erp_v2.o
+obj-$(CONFIG_MSM_GLADIATOR_ERP) += gladiator_erp.o
obj-$(CONFIG_QCOM_EUD) += eud.o
obj-$(CONFIG_QCOM_WATCHDOG_V2) += watchdog_v2.o
obj-$(CONFIG_QCOM_MEMORY_DUMP_V2) += memory_dump_v2.o
@@ -62,3 +62,6 @@
endif
obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
obj-$(CONFIG_WCD_DSP_GLINK) += wcd-dsp-glink.o
+obj-$(CONFIG_MSM_EVENT_TIMER) += event_timer.o
+obj-$(CONFIG_MSM_IDLE_STATS) += lpm-stats.o
+obj-$(CONFIG_APSS_CORE_EA) += msm-core.o debug_core.o
diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c
index 54d355f..0c2ba4d 100644
--- a/drivers/soc/qcom/cmd-db.c
+++ b/drivers/soc/qcom/cmd-db.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -27,6 +27,7 @@
#define CMD_DB_MAGIC 0x0C0330DBUL
#define SLAVE_ID_MASK 0x7
#define SLAVE_ID_SHIFT 16
+#define CMD_DB_STANDALONE_MASK BIT(0)
struct entry_header {
uint64_t res_id;
@@ -220,6 +221,14 @@
return cmd_db_status;
}
+int cmd_db_is_standalone(void)
+{
+ if (cmd_db_status < 0)
+ return cmd_db_status;
+
+ return !!(cmd_db_header->reserved & CMD_DB_STANDALONE_MASK);
+}
+
int cmd_db_get_slave_id(const char *resource_id)
{
int ret;
@@ -259,6 +268,10 @@
}
cmd_db_status = 0;
of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+
+ if (cmd_db_is_standalone() == 1)
+ pr_info("Command DB is initialized in standalone mode.\n");
+
failed:
return cmd_db_status;
}
diff --git a/drivers/soc/qcom/debug_core.c b/drivers/soc/qcom/debug_core.c
new file mode 100644
index 0000000..019360a
--- /dev/null
+++ b/drivers/soc/qcom/debug_core.c
@@ -0,0 +1,330 @@
+/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+#include <linux/debugfs.h>
+#include <linux/ctype.h>
+#include <linux/cpu.h>
+#include "soc/qcom/msm-core.h"
+
+#define MAX_PSTATES 50
+#define NUM_OF_PENTRY 3 /* number of variables for ptable node */
+#define NUM_OF_EENTRY 2 /* number of variables for enable node */
+
+enum arg_offset {
+ CPU_OFFSET,
+ FREQ_OFFSET,
+ POWER_OFFSET,
+};
+
+struct core_debug {
+ int cpu;
+ struct cpu_pstate_pwr *head;
+ int enabled;
+ int len;
+ struct cpu_pwr_stats *ptr;
+ struct cpu_pstate_pwr *driver_data;
+ int driver_len;
+};
+
+static DEFINE_PER_CPU(struct core_debug, c_dgfs);
+static struct cpu_pwr_stats *msm_core_data;
+static struct debugfs_blob_wrapper help_msg = {
+ .data =
+"MSM CORE Debug-FS Support\n"
+"\n"
+"Hierarchy schema\n"
+"/sys/kernel/debug/msm_core\n"
+" /help - Static help text\n"
+" /ptable - write to p-state table\n"
+" /enable - enable the written p-state table\n"
+" /ptable_dump - Dump the debug ptable\n"
+"\n"
+"Usage\n"
+" Input test frequency and power information in ptable:\n"
+" echo \"0 300000 120\" > ptable\n"
+" format: <cpu> <frequency in khz> <power>\n"
+"\n"
+" Enable the ptable for the cpu:\n"
+" echo \"0 1\" > enable\n"
+" format: <cpu> <1 to enable, 0 to disable>\n"
+" Note: Writing 0 to disable will reset/clear the ptable\n"
+"\n"
+" Dump the entire ptable:\n"
+" cat ptable\n"
+" ----- CPU0 - Enabled ---------\n"
+" Freq Power\n"
+" 700000 120\n"
+"----- CPU0 - Live numbers -----\n"
+" Freq Power\n"
+" 300000 218\n"
+" ----- CPU1 - Written ---------\n"
+" Freq Power\n"
+" 700000 120\n"
+" Ptable dump will dump the status of the table as well\n"
+" It shows:\n"
+" Enabled -> for a cpu that debug ptable enabled\n"
+" Written -> for a cpu that has debug ptable values written\n"
+" but not enabled\n"
+"\n",
+
+};
+
+static void add_to_ptable(unsigned int *arg)
+{
+ struct core_debug *node;
+ int i, cpu = arg[CPU_OFFSET];
+ uint32_t freq = arg[FREQ_OFFSET];
+ uint32_t power = arg[POWER_OFFSET];
+
+ if (!cpu_possible(cpu))
+ return;
+
+ if ((freq == 0) || (power == 0)) {
+ pr_warn("Incorrect power data\n");
+ return;
+ }
+
+ node = &per_cpu(c_dgfs, cpu);
+
+ if (node->len >= MAX_PSTATES) {
+ pr_warn("Dropped ptable update - no space left.\n");
+ return;
+ }
+
+ if (!node->head) {
+ node->head = kzalloc(sizeof(struct cpu_pstate_pwr) *
+ (MAX_PSTATES + 1),
+ GFP_KERNEL);
+ if (!node->head)
+ return;
+ }
+
+ for (i = 0; i < node->len; i++) {
+ if (node->head[i].freq == freq) {
+ node->head[i].power = power;
+ return;
+ }
+ }
+
+ /*
+ * Insert a new frequency (may need to move things around to
+ * keep in ascending order).
+ */
+ for (i = MAX_PSTATES - 1; i > 0; i--) {
+ if (node->head[i-1].freq > freq) {
+ node->head[i].freq = node->head[i-1].freq;
+ node->head[i].power = node->head[i-1].power;
+ } else if (node->head[i-1].freq != 0) {
+ break;
+ }
+ }
+
+ if (node->len < MAX_PSTATES) {
+ node->head[i].freq = freq;
+ node->head[i].power = power;
+ node->len++;
+ }
+
+ if (node->ptr)
+ node->ptr->len = node->len;
+}
+
+static int split_ptable_args(char *line, unsigned int *arg, uint32_t n)
+{
+ char *args;
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < n; i++) {
+ if (!line)
+ break;
+ args = strsep(&line, " ");
+ ret = kstrtouint(args, 10, &arg[i]);
+ if (ret)
+ return ret;
+ }
+ return ret;
+}
+
+static ssize_t msm_core_ptable_write(struct file *file,
+ const char __user *ubuf, size_t len, loff_t *offp)
+{
+ char *kbuf;
+ int ret;
+ unsigned int arg[3];
+
+ if (len == 0)
+ return 0;
+
+ kbuf = kzalloc(len + 1, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+
+ if (copy_from_user(kbuf, ubuf, len)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ kbuf[len] = '\0';
+ ret = split_ptable_args(kbuf, arg, NUM_OF_PENTRY);
+ if (!ret) {
+ add_to_ptable(arg);
+ ret = len;
+ }
+done:
+ kfree(kbuf);
+ return ret;
+}
+
+static void print_table(struct seq_file *m, struct cpu_pstate_pwr *c_n,
+ int len)
+{
+ int i;
+
+ seq_puts(m, " Freq Power\n");
+ for (i = 0; i < len; i++)
+ seq_printf(m, " %d %u\n", c_n[i].freq,
+ c_n[i].power);
+
+}
+
+static int msm_core_ptable_read(struct seq_file *m, void *data)
+{
+ int cpu;
+ struct core_debug *node;
+
+ for_each_possible_cpu(cpu) {
+ node = &per_cpu(c_dgfs, cpu);
+ if (node->head) {
+ seq_printf(m, "----- CPU%d - %s - Debug -------\n",
+ cpu, node->enabled == 1 ? "Enabled" : "Written");
+ print_table(m, node->head, node->len);
+ }
+ if (msm_core_data[cpu].ptable) {
+ seq_printf(m, "--- CPU%d - Live numbers at %ldC---\n",
+ cpu, node->ptr->temp);
+ print_table(m, msm_core_data[cpu].ptable,
+ node->driver_len);
+ }
+ }
+ return 0;
+}
+
+static ssize_t msm_core_enable_write(struct file *file,
+ const char __user *ubuf, size_t len, loff_t *offp)
+{
+ char *kbuf;
+ int ret;
+ unsigned int arg[3];
+ int cpu;
+
+ if (len == 0)
+ return 0;
+
+ kbuf = kzalloc(len + 1, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+
+ if (copy_from_user(kbuf, ubuf, len)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ kbuf[len] = '\0';
+ ret = split_ptable_args(kbuf, arg, NUM_OF_EENTRY);
+ if (ret)
+ goto done;
+ cpu = arg[CPU_OFFSET];
+
+ if (cpu_possible(cpu)) {
+ struct core_debug *node = &per_cpu(c_dgfs, cpu);
+
+ if (arg[FREQ_OFFSET]) {
+ msm_core_data[cpu].ptable = node->head;
+ msm_core_data[cpu].len = node->len;
+ } else {
+ msm_core_data[cpu].ptable = node->driver_data;
+ msm_core_data[cpu].len = node->driver_len;
+ node->len = 0;
+ }
+ node->enabled = arg[FREQ_OFFSET];
+ }
+ ret = len;
+ blocking_notifier_call_chain(
+ get_power_update_notifier(), cpu, NULL);
+
+done:
+ kfree(kbuf);
+ return ret;
+}
+
+static const struct file_operations msm_core_enable_ops = {
+ .write = msm_core_enable_write,
+};
+
+static int msm_core_dump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, msm_core_ptable_read, inode->i_private);
+}
+
+static const struct file_operations msm_core_ptable_ops = {
+ .open = msm_core_dump_open,
+ .read = seq_read,
+ .write = msm_core_ptable_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int msm_core_debug_init(void)
+{
+ struct dentry *dir;
+ struct dentry *file;
+ int i;
+
+ msm_core_data = get_cpu_pwr_stats();
+ if (!msm_core_data)
+ goto fail;
+
+ dir = debugfs_create_dir("msm_core", NULL);
+ if (IS_ERR_OR_NULL(dir))
+ return PTR_ERR(dir);
+
+ file = debugfs_create_file("enable", 0660, dir, NULL,
+ &msm_core_enable_ops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("ptable", 0660, dir, NULL,
+ &msm_core_ptable_ops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ help_msg.size = strlen(help_msg.data);
+ file = debugfs_create_blob("help", 0444, dir, &help_msg);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ for (i = 0; i < num_possible_cpus(); i++) {
+ per_cpu(c_dgfs, i).ptr = &msm_core_data[i];
+ per_cpu(c_dgfs, i).driver_data = msm_core_data[i].ptable;
+ per_cpu(c_dgfs, i).driver_len = msm_core_data[i].len;
+ }
+ return 0;
+fail:
+ debugfs_remove(dir);
+ return PTR_ERR(file);
+}
+late_initcall(msm_core_debug_init);
diff --git a/drivers/soc/qcom/event_timer.c b/drivers/soc/qcom/event_timer.c
new file mode 100644
index 0000000..24c90fb
--- /dev/null
+++ b/drivers/soc/qcom/event_timer.c
@@ -0,0 +1,503 @@
+/* Copyright (c) 2012, 2014-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/cpu.h>
+#include <soc/qcom/event_timer.h>
+
+/**
+ * struct event_timer_info - basic event timer structure
+ * @node: timerqueue node to track time ordered data structure
+ * of event timers
+ * @notify: irq affinity notifier.
+ * @timer: hrtimer created for this event.
+ * @function : callback function for event timer.
+ * @data : callback data for event timer.
+ * @irq: irq number for which event timer is created.
+ * @cpu: event timer associated cpu.
+ */
+struct event_timer_info {
+ struct timerqueue_node node;
+ struct irq_affinity_notify notify;
+ void (*function)(void *);
+ void *data;
+ int irq;
+ int cpu;
+};
+
+struct hrtimer_info {
+ struct hrtimer event_hrtimer;
+ bool timer_initialized;
+};
+
+static DEFINE_PER_CPU(struct hrtimer_info, per_cpu_hrtimer);
+
+static DEFINE_PER_CPU(struct timerqueue_head, timer_head) = {
+ .head = RB_ROOT,
+ .next = NULL,
+};
+
+static DEFINE_SPINLOCK(event_timer_lock);
+static DEFINE_SPINLOCK(event_setup_lock);
+
+static void create_timer_smp(void *data);
+static void setup_event_hrtimer(struct event_timer_info *event);
+static enum hrtimer_restart event_hrtimer_cb(struct hrtimer *hrtimer);
+static void irq_affinity_change_notifier(struct irq_affinity_notify *notify,
+ const cpumask_t *new_cpu_mask);
+static void irq_affinity_release(struct kref *ref);
+
+static int msm_event_debug_mask;
+module_param_named(debug_mask, msm_event_debug_mask, int, 0664);
+
+enum {
+ MSM_EVENT_TIMER_DEBUG = 1U << 0,
+};
+
+/**
+ * add_event_timer() : Add a wakeup event. Intended to be called
+ * by clients once. Returns a handle to be used
+ * for future transactions.
+ * @irq: event associated irq number.
+ * @function : The callback function will be called when event
+ * timer expires.
+ * @data: callback data provided by client.
+ */
+struct event_timer_info *add_event_timer(uint32_t irq,
+ void (*function)(void *), void *data)
+{
+ struct event_timer_info *event_info =
+ kzalloc(sizeof(struct event_timer_info), GFP_KERNEL);
+
+ if (!event_info)
+ return NULL;
+
+ event_info->function = function;
+ event_info->data = data;
+
+ if (irq) {
+ struct irq_desc *desc = irq_to_desc(irq);
+ struct cpumask *mask = desc->irq_common_data.affinity;
+
+ get_online_cpus();
+ event_info->cpu = cpumask_any_and(mask, cpu_online_mask);
+ if (event_info->cpu >= nr_cpu_ids)
+ event_info->cpu = cpumask_first(cpu_online_mask);
+
+ event_info->notify.notify = irq_affinity_change_notifier;
+ event_info->notify.release = irq_affinity_release;
+ irq_set_affinity_notifier(irq, &event_info->notify);
+ put_online_cpus();
+ }
+
+ /* Init rb node and hr timer */
+ timerqueue_init(&event_info->node);
+ pr_debug("New Event Added. Event %p(on cpu%d). irq %d.\n",
+ event_info, event_info->cpu, irq);
+
+ return event_info;
+}
+EXPORT_SYMBOL(add_event_timer);
+
+/**
+ * is_event_next(): Helper function to check if the event is the next
+ * expiring event
+ * @event : handle to the event to be checked.
+ */
+static bool is_event_next(struct event_timer_info *event)
+{
+ struct event_timer_info *next_event;
+ struct timerqueue_node *next;
+ bool ret = false;
+
+ next = timerqueue_getnext(&per_cpu(timer_head, event->cpu));
+ if (!next)
+ goto exit_is_next_event;
+
+ next_event = container_of(next, struct event_timer_info, node);
+ if (!next_event)
+ goto exit_is_next_event;
+
+ if (next_event == event)
+ ret = true;
+
+exit_is_next_event:
+ return ret;
+}
+
+/**
+ * is_event_active(): Helper function to check if the timer for a given event
+ * has been started.
+ * @event : handle to the event to be checked.
+ */
+static bool is_event_active(struct event_timer_info *event)
+{
+ struct timerqueue_node *next;
+ struct event_timer_info *next_event;
+ bool ret = false;
+
+ for (next = timerqueue_getnext(&per_cpu(timer_head, event->cpu)); next;
+ next = timerqueue_iterate_next(next)) {
+ next_event = container_of(next, struct event_timer_info, node);
+
+ if (event == next_event) {
+ ret = true;
+ break;
+ }
+ }
+ return ret;
+}
+
+/**
+ * create_hrtimer(): Helper function to setup hrtimer.
+ */
+static void create_hrtimer(struct event_timer_info *event)
+
+{
+ bool timer_initialized = per_cpu(per_cpu_hrtimer.timer_initialized,
+ event->cpu);
+ struct hrtimer *event_hrtimer = &per_cpu(per_cpu_hrtimer.event_hrtimer,
+ event->cpu);
+
+ if (!timer_initialized) {
+ hrtimer_init(event_hrtimer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_ABS_PINNED);
+ per_cpu(per_cpu_hrtimer.timer_initialized, event->cpu) = true;
+ }
+
+ event_hrtimer->function = event_hrtimer_cb;
+ hrtimer_start(event_hrtimer, event->node.expires,
+ HRTIMER_MODE_ABS_PINNED);
+}
+
+/**
+ * event_hrtimer_cb() : Callback function for hr timer.
+ * Make the client CB from here and remove the event
+ * from the time ordered queue.
+ */
+static enum hrtimer_restart event_hrtimer_cb(struct hrtimer *hrtimer)
+{
+ struct event_timer_info *event;
+ struct timerqueue_node *next;
+ unsigned long flags;
+ int cpu;
+
+ spin_lock_irqsave(&event_timer_lock, flags);
+ cpu = smp_processor_id();
+ next = timerqueue_getnext(&per_cpu(timer_head, cpu));
+
+ while (next && (ktime_to_ns(next->expires)
+ <= ktime_to_ns(hrtimer->node.expires))) {
+ event = container_of(next, struct event_timer_info, node);
+ if (!event)
+ goto hrtimer_cb_exit;
+
+ WARN_ON_ONCE(event->cpu != cpu);
+
+ if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
+ pr_debug("Deleting event %p @ %lu(on cpu%d)\n", event,
+ (unsigned long)ktime_to_ns(next->expires), cpu);
+
+ timerqueue_del(&per_cpu(timer_head, cpu), &event->node);
+
+ if (event->function)
+ event->function(event->data);
+
+ next = timerqueue_getnext(&per_cpu(timer_head, cpu));
+ }
+
+ if (next) {
+ event = container_of(next, struct event_timer_info, node);
+ create_hrtimer(event);
+ }
+hrtimer_cb_exit:
+ spin_unlock_irqrestore(&event_timer_lock, flags);
+ return HRTIMER_NORESTART;
+}
+
+/**
+ * create_timer_smp(): Helper function used setting up timer on CPUs.
+ */
+static void create_timer_smp(void *data)
+{
+ unsigned long flags;
+ struct event_timer_info *event =
+ (struct event_timer_info *)data;
+ struct timerqueue_node *next;
+
+ spin_lock_irqsave(&event_timer_lock, flags);
+
+ if (is_event_active(event))
+ timerqueue_del(&per_cpu(timer_head, event->cpu), &event->node);
+
+ next = timerqueue_getnext(&per_cpu(timer_head, event->cpu));
+ timerqueue_add(&per_cpu(timer_head, event->cpu), &event->node);
+
+ if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
+ pr_debug("Adding Event %p(on cpu%d) for %lu\n", event,
+ event->cpu,
+ (unsigned long)ktime_to_ns(event->node.expires));
+
+ if (!next || (next && (ktime_to_ns(event->node.expires) <
+ ktime_to_ns(next->expires)))) {
+ if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
+ pr_debug("Setting timer for %lu(on cpu%d)\n",
+ (unsigned long)ktime_to_ns(event->node.expires),
+ event->cpu);
+
+ create_hrtimer(event);
+ }
+ spin_unlock_irqrestore(&event_timer_lock, flags);
+}
+
+/**
+ * setup_timer() : Helper function to setup timer on primary
+ * core during hrtimer callback.
+ * @event: event handle causing the wakeup.
+ */
+static void setup_event_hrtimer(struct event_timer_info *event)
+{
+ smp_call_function_single(event->cpu, create_timer_smp, event, 1);
+}
+
+static void irq_affinity_release(struct kref *ref)
+{
+ struct event_timer_info *event;
+ struct irq_affinity_notify *notify =
+ container_of(ref, struct irq_affinity_notify, kref);
+
+ event = container_of(notify, struct event_timer_info, notify);
+ pr_debug("event = %p\n", event);
+}
+
+static void irq_affinity_change_notifier(struct irq_affinity_notify *notify,
+ const cpumask_t *mask_val)
+{
+ struct event_timer_info *event;
+ unsigned long flags;
+ unsigned int irq;
+ int old_cpu = -EINVAL, new_cpu = -EINVAL;
+ bool next_event = false;
+
+ event = container_of(notify, struct event_timer_info, notify);
+ irq = notify->irq;
+
+ if (!event)
+ return;
+
+ /*
+ * This logic is inline with irq-gic.c for finding
+ * the next affinity CPU.
+ */
+ new_cpu = cpumask_any_and(mask_val, cpu_online_mask);
+ if (new_cpu >= nr_cpu_ids)
+ return;
+
+ old_cpu = event->cpu;
+
+ if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
+ pr_debug("irq %d, event %p, old_cpu(%d)->new_cpu(%d).\n",
+ irq, event, old_cpu, new_cpu);
+
+ /* No change in IRQ affinity */
+ if (old_cpu == new_cpu)
+ return;
+
+ spin_lock_irqsave(&event_timer_lock, flags);
+
+ /* If the event is not active OR
+ * If it is the next event
+ * and the timer is already in callback
+ * Just reset cpu and return
+ */
+ if (!is_event_active(event) ||
+ (is_event_next(event) &&
+ (hrtimer_try_to_cancel(&per_cpu(per_cpu_hrtimer.
+ event_hrtimer, old_cpu)) < 0))) {
+ event->cpu = new_cpu;
+ spin_unlock_irqrestore(&event_timer_lock, flags);
+ if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
+ pr_debug("Event:%p is not active or in callback\n",
+ event);
+ return;
+ }
+
+ /* Update the flag based on EVENT is next are not */
+ if (is_event_next(event))
+ next_event = true;
+
+ event->cpu = new_cpu;
+
+ /*
+ * We are here either because hrtimer was active or event is not next
+ * Delete the event from the timer queue anyway
+ */
+ timerqueue_del(&per_cpu(timer_head, old_cpu), &event->node);
+
+ if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
+ pr_debug("Event:%p is in the list\n", event);
+
+ spin_unlock_irqrestore(&event_timer_lock, flags);
+
+ /*
+ * Migrating event timer to a new CPU is automatically
+ * taken care. Since we have already modify the event->cpu
+ * with new CPU.
+ *
+ * Typical cases are
+ *
+ * 1)
+ * C0 C1
+ * | ^
+ * ----------------- |
+ * | | | |
+ * E1 E2 E3 |
+ * |(migrating) |
+ * -------------------------
+ *
+ * 2)
+ * C0 C1
+ * | ^
+ * ---------------- |
+ * | | | |
+ * E1 E2 E3 |
+ * |(migrating) |
+ * ---------------------------------
+ *
+ * Here after moving the E1 to C1. Need to start
+ * E2 on C0.
+ */
+ spin_lock(&event_setup_lock);
+ /* Setup event timer on new cpu*/
+ setup_event_hrtimer(event);
+
+ /* Setup event on the old cpu*/
+ if (next_event) {
+ struct timerqueue_node *next;
+
+ next = timerqueue_getnext(&per_cpu(timer_head, old_cpu));
+ if (next) {
+ event = container_of(next,
+ struct event_timer_info, node);
+ setup_event_hrtimer(event);
+ }
+ }
+ spin_unlock(&event_setup_lock);
+}
+
+/**
+ * activate_event_timer() : Set the expiration time for an event in absolute
+ * ktime. This is a oneshot event timer, clients
+ * should call this again to set another expiration.
+ * @event : event handle.
+ * @event_time : event time in absolute ktime.
+ */
+void activate_event_timer(struct event_timer_info *event, ktime_t event_time)
+{
+ if (!event)
+ return;
+
+ if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
+ pr_debug("Adding event %p timer @ %lu(on cpu%d)\n", event,
+ (unsigned long)ktime_to_us(event_time),
+ event->cpu);
+
+ spin_lock(&event_setup_lock);
+ event->node.expires = event_time;
+ /* Start hrtimer and add event to rb tree */
+ setup_event_hrtimer(event);
+ spin_unlock(&event_setup_lock);
+}
+EXPORT_SYMBOL(activate_event_timer);
+
+/**
+ * deactivate_event_timer() : Deactivate an event timer, this removes the event
+ * from the time ordered queue of event timers.
+ * @event: event handle.
+ */
+void deactivate_event_timer(struct event_timer_info *event)
+{
+ unsigned long flags;
+
+ if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
+ pr_debug("Deactivate timer\n");
+
+ spin_lock_irqsave(&event_timer_lock, flags);
+ if (is_event_active(event)) {
+ if (is_event_next(event))
+ hrtimer_try_to_cancel(&per_cpu(
+ per_cpu_hrtimer.event_hrtimer, event->cpu));
+
+ timerqueue_del(&per_cpu(timer_head, event->cpu), &event->node);
+ }
+ spin_unlock_irqrestore(&event_timer_lock, flags);
+}
+
+/**
+ * destroy_event_timer() : Free the event info data structure allocated during
+ * add_event_timer().
+ * @event: event handle.
+ */
+void destroy_event_timer(struct event_timer_info *event)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&event_timer_lock, flags);
+ if (is_event_active(event)) {
+ if (is_event_next(event))
+ hrtimer_try_to_cancel(&per_cpu(
+ per_cpu_hrtimer.event_hrtimer, event->cpu));
+
+ timerqueue_del(&per_cpu(timer_head, event->cpu), &event->node);
+ }
+ spin_unlock_irqrestore(&event_timer_lock, flags);
+ kfree(event);
+}
+EXPORT_SYMBOL(destroy_event_timer);
+
+/**
+ * get_next_event_timer() - Get the next wakeup event. Returns
+ * a ktime value of the next expiring event.
+ */
+ktime_t get_next_event_time(int cpu)
+{
+ unsigned long flags;
+ struct timerqueue_node *next;
+ struct event_timer_info *event;
+ ktime_t next_event = ns_to_ktime(0);
+
+ spin_lock_irqsave(&event_timer_lock, flags);
+ next = timerqueue_getnext(&per_cpu(timer_head, cpu));
+ event = container_of(next, struct event_timer_info, node);
+ spin_unlock_irqrestore(&event_timer_lock, flags);
+
+ if (!next || event->cpu != cpu)
+ return next_event;
+
+ next_event = hrtimer_get_remaining(
+ &per_cpu(per_cpu_hrtimer.event_hrtimer, cpu));
+
+ if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
+ pr_debug("Next Event %lu(on cpu%d)\n",
+ (unsigned long)ktime_to_us(next_event), cpu);
+
+ return next_event;
+}
diff --git a/drivers/soc/qcom/gladiator_erp.c b/drivers/soc/qcom/gladiator_erp.c
new file mode 100644
index 0000000..835d4b0
--- /dev/null
+++ b/drivers/soc/qcom/gladiator_erp.c
@@ -0,0 +1,1130 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/cpu_pm.h>
+#include <linux/platform_device.h>
+#include <soc/qcom/scm.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+
+#define MODULE_NAME "gladiator_error_reporting"
+
+#define INVALID_NUM 0xDEADBEEF
+
+struct reg_off {
+ unsigned int gladiator_id_coreid;
+ unsigned int gladiator_id_revisionid;
+ unsigned int gladiator_faulten;
+ unsigned int gladiator_errvld;
+ unsigned int gladiator_errclr;
+ unsigned int gladiator_errlog0;
+ unsigned int gladiator_errlog1;
+ unsigned int gladiator_errlog2;
+ unsigned int gladiator_errlog3;
+ unsigned int gladiator_errlog4;
+ unsigned int gladiator_errlog5;
+ unsigned int gladiator_errlog6;
+ unsigned int gladiator_errlog7;
+ unsigned int gladiator_errlog8;
+ unsigned int observer_0_id_coreid;
+ unsigned int observer_0_id_revisionid;
+ unsigned int observer_0_faulten;
+ unsigned int observer_0_errvld;
+ unsigned int observer_0_errclr;
+ unsigned int observer_0_errlog0;
+ unsigned int observer_0_errlog1;
+ unsigned int observer_0_errlog2;
+ unsigned int observer_0_errlog3;
+ unsigned int observer_0_errlog4;
+ unsigned int observer_0_errlog5;
+ unsigned int observer_0_errlog6;
+ unsigned int observer_0_errlog7;
+ unsigned int observer_0_errlog8;
+ unsigned int observer_0_stallen;
+};
+
+struct reg_masks_shift {
+ unsigned int gld_trans_opcode_mask;
+ unsigned int gld_trans_opcode_shift;
+ unsigned int gld_error_type_mask;
+ unsigned int gld_error_type_shift;
+ unsigned int gld_len1_mask;
+ unsigned int gld_len1_shift;
+ unsigned int gld_trans_sourceid_mask;
+ unsigned int gld_trans_sourceid_shift;
+ unsigned int gld_trans_targetid_mask;
+ unsigned int gld_trans_targetid_shift;
+ unsigned int gld_errlog_error;
+ unsigned int gld_errlog5_error_type_mask;
+ unsigned int gld_errlog5_error_type_shift;
+ unsigned int gld_ace_port_parity_mask;
+ unsigned int gld_ace_port_parity_shift;
+ unsigned int gld_ace_port_disconnect_mask;
+ unsigned int gld_ace_port_disconnect_shift;
+ unsigned int gld_ace_port_directory_mask;
+ unsigned int gld_ace_port_directory_shift;
+ unsigned int gld_index_parity_mask;
+ unsigned int gld_index_parity_shift;
+ unsigned int obs_trans_opcode_mask;
+ unsigned int obs_trans_opcode_shift;
+ unsigned int obs_error_type_mask;
+ unsigned int obs_error_type_shift;
+ unsigned int obs_len1_mask;
+ unsigned int obs_len1_shift;
+};
+
+struct msm_gladiator_data {
+ void __iomem *gladiator_virt_base;
+ int erp_irq;
+ struct notifier_block pm_notifier_block;
+ struct clk *qdss_clk;
+ struct reg_off *reg_offs;
+ struct reg_masks_shift *reg_masks_shifts;
+ bool glad_v2;
+ bool glad_v3;
+};
+
+static int enable_panic_on_error;
+module_param(enable_panic_on_error, int, 0000);
+
+enum gld_trans_opcode {
+ GLD_RD,
+ GLD_RDX,
+ GLD_RDL,
+ GLD_RESERVED,
+ GLD_WR,
+ GLD_WRC,
+ GLD_PRE,
+};
+
+enum obs_trans_opcode {
+ OBS_RD,
+ OBS_RDW,
+ OBS_RDL,
+ OBS_RDX,
+ OBS_WR,
+ OBS_WRW,
+ OBS_WRC,
+ OBS_RESERVED,
+ OBS_PRE,
+ OBS_URG,
+};
+
+enum obs_err_code {
+ OBS_SLV,
+ OBS_DEC,
+ OBS_UNS,
+ OBS_DISC,
+ OBS_SEC,
+ OBS_HIDE,
+ OBS_TMO,
+ OBS_RSV,
+};
+
+enum err_log {
+ ID_COREID,
+ ID_REVISIONID,
+ FAULTEN,
+ ERRVLD,
+ ERRCLR,
+ ERR_LOG0,
+ ERR_LOG1,
+ ERR_LOG2,
+ ERR_LOG3,
+ ERR_LOG4,
+ ERR_LOG5,
+ ERR_LOG6,
+ ERR_LOG7,
+ ERR_LOG8,
+ STALLEN,
+ MAX_NUM,
+};
+
+enum type_logger_error {
+ DATA_TRANSFER_ERROR,
+ DVM_ERROR,
+ TX_ERROR,
+ TXR_ERROR,
+ DISCONNECT_ERROR,
+ DIRECTORY_ERROR,
+ PARITY_ERROR,
+};
+
+static void clear_gladiator_error(void __iomem *gladiator_virt_base,
+ struct reg_off *offs)
+{
+ writel_relaxed(1, gladiator_virt_base + offs->gladiator_errclr);
+ writel_relaxed(1, gladiator_virt_base + offs->observer_0_errclr);
+}
+
+static inline void print_gld_transaction(unsigned int opc)
+{
+ switch (opc) {
+ case GLD_RD:
+ pr_alert("Transaction type: READ\n");
+ break;
+ case GLD_RDX:
+ pr_alert("Transaction type: EXCLUSIVE READ\n");
+ break;
+ case GLD_RDL:
+ pr_alert("Transaction type: LINKED READ\n");
+ break;
+ case GLD_WR:
+ pr_alert("Transaction type: WRITE\n");
+ break;
+ case GLD_WRC:
+ pr_alert("Transaction type: CONDITIONAL WRITE\n");
+ break;
+ case GLD_PRE:
+ pr_alert("Transaction: Preamble packet of linked sequence\n");
+ break;
+ default:
+ pr_alert("Transaction type: Unknown; value:%u\n", opc);
+ }
+}
+
+static inline void print_gld_errtype(unsigned int errtype)
+{
+ if (errtype == 0)
+ pr_alert("Error type: Snoop data transfer\n");
+ else if (errtype == 1)
+ pr_alert("Error type: DVM error\n");
+ else if (errtype == 3)
+ pr_alert("Error type: Disconnect, directory, or parity error\n");
+ else
+ pr_alert("Error type: Unknown; value:%u\n", errtype);
+}
+
+static void decode_gld_errlog0(u32 err_reg,
+ struct reg_masks_shift *mask_shifts)
+{
+ unsigned int opc, errtype, len1;
+
+ opc = (err_reg & mask_shifts->gld_trans_opcode_mask) >>
+ mask_shifts->gld_trans_opcode_shift;
+ errtype = (err_reg & mask_shifts->gld_error_type_mask) >>
+ mask_shifts->gld_error_type_shift;
+ len1 = (err_reg & mask_shifts->gld_len1_mask) >>
+ mask_shifts->gld_len1_shift;
+
+ print_gld_transaction(opc);
+ print_gld_errtype(errtype);
+ pr_alert("number of payload bytes: %d\n", len1 + 1);
+}
+
+static void decode_gld_errlog1(u32 err_reg,
+ struct reg_masks_shift *mask_shifts)
+{
+ if ((err_reg & mask_shifts->gld_errlog_error) ==
+ mask_shifts->gld_errlog_error)
+ pr_alert("Transaction issued on IO target generic interface\n");
+ else
+ pr_alert("Transaction source ID: %d\n",
+ (err_reg & mask_shifts->gld_trans_sourceid_mask)
+ >> mask_shifts->gld_trans_sourceid_shift);
+}
+
+static void decode_gld_errlog2(u32 err_reg,
+ struct reg_masks_shift *mask_shifts)
+{
+ if ((err_reg & mask_shifts->gld_errlog_error) ==
+ mask_shifts->gld_errlog_error)
+ pr_alert("Error response coming from: external DVM network\n");
+ else
+ pr_alert("Error response coming from: Target ID: %d\n",
+ (err_reg & mask_shifts->gld_trans_targetid_mask)
+ >> mask_shifts->gld_trans_targetid_shift);
+}
+
+static void decode_ace_port_index(u32 type, u32 error,
+ struct reg_masks_shift *mask_shifts)
+{
+ unsigned int port;
+
+ switch (type) {
+ case DISCONNECT_ERROR:
+ port = (error & mask_shifts->gld_ace_port_disconnect_mask)
+ >> mask_shifts->gld_ace_port_disconnect_shift;
+ pr_alert("ACE port index: %d\n", port);
+ break;
+ case DIRECTORY_ERROR:
+ port = (error & mask_shifts->gld_ace_port_directory_mask)
+ >> mask_shifts->gld_ace_port_directory_shift;
+ pr_alert("ACE port index: %d\n", port);
+ break;
+ case PARITY_ERROR:
+ port = (error & mask_shifts->gld_ace_port_parity_mask)
+ >> mask_shifts->gld_ace_port_parity_shift;
+ pr_alert("ACE port index: %d\n", port);
+ }
+}
+
+static void decode_index_parity(u32 error, struct reg_masks_shift *mask_shifts)
+{
+ pr_alert("Index: %d\n",
+ (error & mask_shifts->gld_index_parity_mask)
+ >> mask_shifts->gld_index_parity_shift);
+}
+
+static void decode_gld_logged_error(u32 err_reg5,
+ struct reg_masks_shift *mask_shifts)
+{
+ unsigned int log_err_type, i, value;
+
+ log_err_type = (err_reg5 & mask_shifts->gld_errlog5_error_type_mask)
+ >> mask_shifts->gld_errlog5_error_type_shift;
+ for (i = 0 ; i <= 6 ; i++) {
+ value = log_err_type & 0x1;
+ switch (i) {
+ case DATA_TRANSFER_ERROR:
+ if (value == 0)
+ continue;
+ pr_alert("Error type: Data transfer error\n");
+ break;
+ case DVM_ERROR:
+ if (value == 0)
+ continue;
+ pr_alert("Error type: DVM error\n");
+ break;
+ case TX_ERROR:
+ if (value == 0)
+ continue;
+ pr_alert("Error type: Tx error\n");
+ break;
+ case TXR_ERROR:
+ if (value == 0)
+ continue;
+ pr_alert("Error type: TxR error\n");
+ break;
+ case DISCONNECT_ERROR:
+ if (value == 0)
+ continue;
+ pr_alert("Error type: Disconnect error\n");
+ decode_ace_port_index(
+ DISCONNECT_ERROR,
+ err_reg5,
+ mask_shifts);
+ break;
+ case DIRECTORY_ERROR:
+ if (value == 0)
+ continue;
+ pr_alert("Error type: Directory error\n");
+ decode_ace_port_index(
+ DIRECTORY_ERROR,
+ err_reg5,
+ mask_shifts);
+ break;
+ case PARITY_ERROR:
+ if (value == 0)
+ continue;
+ pr_alert("Error type: Parity error\n");
+ decode_ace_port_index(PARITY_ERROR, err_reg5,
+ mask_shifts);
+ decode_index_parity(err_reg5, mask_shifts);
+ break;
+ }
+ log_err_type = log_err_type >> 1;
+ }
+}
+
+static void decode_gld_errlog(u32 err_reg, unsigned int err_log,
+ struct msm_gladiator_data *msm_gld_data)
+{
+ switch (err_log) {
+ case ERR_LOG0:
+ decode_gld_errlog0(err_reg, msm_gld_data->reg_masks_shifts);
+ break;
+ case ERR_LOG1:
+ decode_gld_errlog1(err_reg, msm_gld_data->reg_masks_shifts);
+ break;
+ case ERR_LOG2:
+ decode_gld_errlog2(err_reg, msm_gld_data->reg_masks_shifts);
+ break;
+ case ERR_LOG3:
+ pr_alert("Lower 32-bits of error address: %08x\n", err_reg);
+ break;
+ case ERR_LOG4:
+ pr_alert("Upper 32-bits of error address: %08x\n", err_reg);
+ break;
+ case ERR_LOG5:
+ pr_alert("Lower 32-bits of user: %08x\n", err_reg);
+ break;
+ case ERR_LOG6:
+ pr_alert("Mid 32-bits(63-32) of user: %08x\n", err_reg);
+ break;
+ case ERR_LOG7:
+ break;
+ case ERR_LOG8:
+ pr_alert("Upper 32-bits(95-64) of user: %08x\n", err_reg);
+ break;
+ default:
+ pr_alert("Invalid error register; reg num:%u\n", err_log);
+ }
+}
+
+static inline void print_obs_transaction(unsigned int opc)
+{
+ switch (opc) {
+ case OBS_RD:
+ pr_alert("Transaction type: READ\n");
+ break;
+ case OBS_RDW:
+ pr_alert("Transaction type: WRAPPED READ\n");
+ break;
+ case OBS_RDL:
+ pr_alert("Transaction type: LINKED READ\n");
+ break;
+ case OBS_RDX:
+ pr_alert("Transaction type: EXCLUSIVE READ\n");
+ break;
+ case OBS_WR:
+ pr_alert("Transaction type: WRITE\n");
+ break;
+ case OBS_WRW:
+ pr_alert("Transaction type: WRAPPED WRITE\n");
+ break;
+ case OBS_WRC:
+ pr_alert("Transaction type: CONDITIONAL WRITE\n");
+ break;
+ case OBS_PRE:
+ pr_alert("Transaction: Preamble packet of linked sequence\n");
+ break;
+ case OBS_URG:
+ pr_alert("Transaction type: Urgency Packet\n");
+ break;
+ default:
+ pr_alert("Transaction type: Unknown; value:%u\n", opc);
+ }
+}
+
+static inline void print_obs_errcode(unsigned int errcode)
+{
+ switch (errcode) {
+ case OBS_SLV:
+ pr_alert("Error code: Target error detected by slave\n");
+ pr_alert("Source: Target\n");
+ break;
+ case OBS_DEC:
+ pr_alert("Error code: Address decode error\n");
+ pr_alert("Source: Initiator NIU\n");
+ break;
+ case OBS_UNS:
+ pr_alert("Error code: Unsupported request\n");
+ pr_alert("Source: Target NIU\n");
+ break;
+ case OBS_DISC:
+ pr_alert("Error code: Disconnected target or domain\n");
+ pr_alert("Source: Power Disconnect\n");
+ break;
+ case OBS_SEC:
+ pr_alert("Error code: Security violation\n");
+ pr_alert("Source: Initiator NIU or Firewall\n");
+ break;
+ case OBS_HIDE:
+ pr_alert("Error :Hidden security violation, reported as OK\n");
+ pr_alert("Source: Firewall\n");
+ break;
+ case OBS_TMO:
+ pr_alert("Error code: Time-out\n");
+ pr_alert("Source: Target NIU\n");
+ break;
+ default:
+ pr_alert("Error code: Unknown; code:%u\n", errcode);
+ }
+}
+
+static void decode_obs_errlog0(u32 err_reg,
+ struct reg_masks_shift *mask_shifts)
+{
+ unsigned int opc, errcode;
+
+ opc = (err_reg & mask_shifts->obs_trans_opcode_mask) >>
+ mask_shifts->obs_trans_opcode_shift;
+ errcode = (err_reg & mask_shifts->obs_error_type_mask) >>
+ mask_shifts->obs_error_type_shift;
+
+ print_obs_transaction(opc);
+ print_obs_errcode(errcode);
+}
+
+static void decode_obs_errlog0_len(u32 err_reg,
+ struct reg_masks_shift *mask_shifts)
+{
+ unsigned int len1;
+
+ len1 = (err_reg & mask_shifts->obs_len1_mask) >>
+ mask_shifts->obs_len1_shift;
+ pr_alert("number of payload bytes: %d\n", len1 + 1);
+}
+
+static void decode_obs_errlog(u32 err_reg, unsigned int err_log,
+ struct msm_gladiator_data *msm_gld_data)
+{
+ switch (err_log) {
+ case ERR_LOG0:
+ decode_obs_errlog0(err_reg, msm_gld_data->reg_masks_shifts);
+ decode_obs_errlog0_len(err_reg, msm_gld_data->reg_masks_shifts);
+ break;
+ case ERR_LOG1:
+ pr_alert("RouteId of the error: %08x\n", err_reg);
+ break;
+ case ERR_LOG2:
+ /* reserved error log register */
+ break;
+ case ERR_LOG3:
+ pr_alert("Lower 32-bits of error address: %08x\n", err_reg);
+ break;
+ case ERR_LOG4:
+ pr_alert("Upper 12-bits of error address: %08x\n", err_reg);
+ break;
+ case ERR_LOG5:
+ pr_alert("Lower 13-bits of user: %08x\n", err_reg);
+ break;
+ case ERR_LOG6:
+ /* reserved error log register */
+ break;
+ case ERR_LOG7:
+ pr_alert("Security filed of the logged error: %08x\n", err_reg);
+ break;
+ case ERR_LOG8:
+ /* reserved error log register */
+ break;
+ case STALLEN:
+ pr_alert("stall mode of the error logger: %08x\n",
+ err_reg & 0x1);
+ break;
+ default:
+ pr_alert("Invalid error register; reg num:%u\n", err_log);
+ }
+}
+
+static void decode_obs_errlog_v3(u32 err_reg, unsigned int err_log,
+ struct msm_gladiator_data *msm_gld_data)
+{
+ switch (err_log) {
+ case ERR_LOG0:
+ decode_obs_errlog0(err_reg, msm_gld_data->reg_masks_shifts);
+ break;
+ case ERR_LOG1:
+ decode_obs_errlog0_len(err_reg, msm_gld_data->reg_masks_shifts);
+ break;
+ case ERR_LOG2:
+ pr_alert("Path of the error: %08x\n", err_reg);
+ break;
+ case ERR_LOG3:
+ pr_alert("ExtID of the error: %08x\n", err_reg);
+ break;
+ case ERR_LOG4:
+ pr_alert("ERRLOG2_LSB: %08x\n", err_reg);
+ break;
+ case ERR_LOG5:
+ pr_alert("ERRLOG2_MSB: %08x\n", err_reg);
+ break;
+ case ERR_LOG6:
+ pr_alert("ERRLOG3_LSB: %08x\n", err_reg);
+ break;
+ case ERR_LOG7:
+ pr_alert("ERRLOG3_MSB: %08x\n", err_reg);
+ break;
+ case FAULTEN:
+ pr_alert("stall mode of the error logger: %08x\n",
+ err_reg & 0x3);
+ break;
+ default:
+ pr_alert("Invalid error register; reg num:%u\n", err_log);
+ }
+}
+
+static u32 get_gld_offset(unsigned int err_log, struct reg_off *offs)
+{
+ u32 offset = 0;
+
+ switch (err_log) {
+ case FAULTEN:
+ offset = offs->gladiator_faulten;
+ break;
+ case ERRVLD:
+ offset = offs->gladiator_errvld;
+ break;
+ case ERRCLR:
+ offset = offs->gladiator_errclr;
+ break;
+ case ERR_LOG0:
+ offset = offs->gladiator_errlog0;
+ break;
+ case ERR_LOG1:
+ offset = offs->gladiator_errlog1;
+ break;
+ case ERR_LOG2:
+ offset = offs->gladiator_errlog2;
+ break;
+ case ERR_LOG3:
+ offset = offs->gladiator_errlog3;
+ break;
+ case ERR_LOG4:
+ offset = offs->gladiator_errlog4;
+ break;
+ case ERR_LOG5:
+ offset = offs->gladiator_errlog5;
+ break;
+ case ERR_LOG6:
+ offset = offs->gladiator_errlog6;
+ break;
+ case ERR_LOG7:
+ offset = offs->gladiator_errlog7;
+ break;
+ case ERR_LOG8:
+ offset = offs->gladiator_errlog8;
+ break;
+ default:
+ pr_alert("Invalid gladiator error register; reg num:%u\n",
+ err_log);
+ }
+ return offset;
+}
+
+static u32 get_obs_offset(unsigned int err_log, struct reg_off *offs)
+{
+ u32 offset = 0;
+
+ switch (err_log) {
+ case FAULTEN:
+ offset = offs->observer_0_faulten;
+ break;
+ case ERRVLD:
+ offset = offs->observer_0_errvld;
+ break;
+ case ERRCLR:
+ offset = offs->observer_0_errclr;
+ break;
+ case ERR_LOG0:
+ offset = offs->observer_0_errlog0;
+ break;
+ case ERR_LOG1:
+ offset = offs->observer_0_errlog1;
+ break;
+ case ERR_LOG2:
+ offset = offs->observer_0_errlog2;
+ break;
+ case ERR_LOG3:
+ offset = offs->observer_0_errlog3;
+ break;
+ case ERR_LOG4:
+ offset = offs->observer_0_errlog4;
+ break;
+ case ERR_LOG5:
+ offset = offs->observer_0_errlog5;
+ break;
+ case ERR_LOG6:
+ offset = offs->observer_0_errlog6;
+ break;
+ case ERR_LOG7:
+ offset = offs->observer_0_errlog7;
+ break;
+ case ERR_LOG8:
+ offset = offs->observer_0_errlog8;
+ break;
+ case STALLEN:
+ offset = offs->observer_0_stallen;
+ break;
+ default:
+ pr_alert("Invalid observer error register; reg num:%u\n",
+ err_log);
+ }
+ return offset;
+}
+
+static void decode_gld_errlog5(struct msm_gladiator_data *msm_gld_data)
+{
+ unsigned int errtype;
+ u32 err_reg0, err_reg5;
+ struct reg_masks_shift *mask_shifts = msm_gld_data->reg_masks_shifts;
+
+ err_reg0 = readl_relaxed(msm_gld_data->gladiator_virt_base +
+ get_gld_offset(ERR_LOG0, msm_gld_data->reg_offs));
+ err_reg5 = readl_relaxed(msm_gld_data->gladiator_virt_base +
+ get_gld_offset(ERR_LOG5, msm_gld_data->reg_offs));
+
+ errtype = (err_reg0 & mask_shifts->gld_error_type_mask) >>
+ mask_shifts->gld_error_type_shift;
+ if (errtype == 3)
+ decode_gld_logged_error(err_reg5, mask_shifts);
+ else if (errtype == 0 || errtype == 1)
+ pr_alert("Lower 32-bits of user: %08x\n", err_reg5);
+ else
+ pr_alert("Error type: Unknown; value:%u\n", errtype);
+}
+
+static void dump_gld_err_regs(struct msm_gladiator_data *msm_gld_data,
+ unsigned int err_buf[MAX_NUM])
+{
+ unsigned int err_log;
+ unsigned int start = FAULTEN;
+ unsigned int end = ERR_LOG8;
+
+ if (msm_gld_data->glad_v2 || msm_gld_data->glad_v3) {
+ start = FAULTEN;
+ end = ERR_LOG8;
+ }
+
+ pr_alert("Main log register data:\n");
+ for (err_log = start; err_log <= end; err_log++) {
+ err_buf[err_log] = readl_relaxed(
+ msm_gld_data->gladiator_virt_base +
+ get_gld_offset(err_log,
+ msm_gld_data->reg_offs));
+ pr_alert("%08x ", err_buf[err_log]);
+ }
+}
+
+static void dump_obsrv_err_regs(struct msm_gladiator_data *msm_gld_data,
+ unsigned int err_buf[MAX_NUM])
+{
+ unsigned int err_log;
+ unsigned int start = ID_COREID;
+ unsigned int end = STALLEN;
+
+ if (msm_gld_data->glad_v2) {
+ start = ID_COREID;
+ end = STALLEN;
+ } else if (msm_gld_data->glad_v3) {
+ start = FAULTEN;
+ end = ERR_LOG7;
+ }
+
+ pr_alert("Observer log register data:\n");
+ for (err_log = start; err_log <= end; err_log++) {
+ err_buf[err_log] = readl_relaxed(
+ msm_gld_data->gladiator_virt_base +
+ get_obs_offset(
+ err_log,
+ msm_gld_data->reg_offs)
+ );
+ pr_alert("%08x ", err_buf[err_log]);
+ }
+}
+
+static void parse_gld_err_regs(struct msm_gladiator_data *msm_gld_data,
+ unsigned int err_buf[MAX_NUM])
+{
+ unsigned int err_log;
+
+ pr_alert("Main error log register data:\n");
+ for (err_log = ERR_LOG0; err_log <= ERR_LOG8; err_log++) {
+ /* skip log register 7 as its reserved */
+ if (err_log == ERR_LOG7)
+ continue;
+ if (err_log == ERR_LOG5) {
+ decode_gld_errlog5(msm_gld_data);
+ continue;
+ }
+ decode_gld_errlog(err_buf[err_log], err_log,
+ msm_gld_data);
+ }
+}
+
+static void parse_obsrv_err_regs(struct msm_gladiator_data *msm_gld_data,
+ unsigned int err_buf[MAX_NUM])
+{
+ unsigned int err_log;
+
+ pr_alert("Observor error log register data:\n");
+ if (msm_gld_data->glad_v2) {
+ for (err_log = ERR_LOG0; err_log <= STALLEN; err_log++) {
+ /* skip log register 2, 6 and 8 as they are reserved */
+ if ((err_log == ERR_LOG2) || (err_log == ERR_LOG6)
+ || (err_log == ERR_LOG8))
+ continue;
+ decode_obs_errlog(err_buf[err_log], err_log,
+ msm_gld_data);
+ }
+ } else if (msm_gld_data->glad_v3) {
+ decode_obs_errlog_v3(err_buf[STALLEN], STALLEN,
+ msm_gld_data);
+ for (err_log = ERR_LOG0; err_log <= ERR_LOG7; err_log++) {
+ decode_obs_errlog_v3(err_buf[err_log], err_log,
+ msm_gld_data);
+ }
+ }
+
+}
+
+static irqreturn_t msm_gladiator_isr(int irq, void *dev_id)
+{
+ unsigned int gld_err_buf[MAX_NUM], obs_err_buf[MAX_NUM];
+
+ struct msm_gladiator_data *msm_gld_data = dev_id;
+
+ /* Check validity */
+ bool gld_err_valid = readl_relaxed(msm_gld_data->gladiator_virt_base +
+ msm_gld_data->reg_offs->gladiator_errvld);
+
+ bool obsrv_err_valid = readl_relaxed(
+ msm_gld_data->gladiator_virt_base +
+ msm_gld_data->reg_offs->observer_0_errvld);
+
+ if (!gld_err_valid && !obsrv_err_valid) {
+ pr_err("%s Invalid Gladiator error reported, clear it\n",
+ __func__);
+ /* Clear IRQ */
+ clear_gladiator_error(msm_gld_data->gladiator_virt_base,
+ msm_gld_data->reg_offs);
+ return IRQ_HANDLED;
+ }
+ pr_alert("Gladiator Error Detected:\n");
+ if (gld_err_valid)
+ dump_gld_err_regs(msm_gld_data, gld_err_buf);
+
+ if (obsrv_err_valid)
+ dump_obsrv_err_regs(msm_gld_data, obs_err_buf);
+
+ if (gld_err_valid)
+ parse_gld_err_regs(msm_gld_data, gld_err_buf);
+
+ if (obsrv_err_valid)
+ parse_obsrv_err_regs(msm_gld_data, obs_err_buf);
+
+ /* Clear IRQ */
+ clear_gladiator_error(msm_gld_data->gladiator_virt_base,
+ msm_gld_data->reg_offs);
+ if (enable_panic_on_error)
+ panic("Gladiator Cache Interconnect Error Detected!\n");
+ else
+ WARN(1, "Gladiator Cache Interconnect Error Detected\n");
+
+ return IRQ_HANDLED;
+}
+
+static const struct of_device_id gladiator_erp_match_table[] = {
+ { .compatible = "qcom,msm-gladiator-v2" },
+ { .compatible = "qcom,msm-gladiator-v3" },
+ {},
+};
+
+static int parse_dt_node(struct platform_device *pdev,
+ struct msm_gladiator_data *msm_gld_data)
+{
+ int ret = 0;
+ struct resource *res;
+
+ res = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "gladiator_base");
+ if (!res)
+ return -ENODEV;
+ if (!devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res),
+ "msm-gladiator-erp")) {
+
+ dev_err(&pdev->dev, "%s cannot reserve gladiator erp region\n",
+ __func__);
+ return -ENXIO;
+ }
+ msm_gld_data->gladiator_virt_base = devm_ioremap(&pdev->dev,
+ res->start, resource_size(res));
+ if (!msm_gld_data->gladiator_virt_base) {
+ dev_err(&pdev->dev, "%s cannot map gladiator register space\n",
+ __func__);
+ return -ENXIO;
+ }
+ msm_gld_data->erp_irq = platform_get_irq(pdev, 0);
+ if (!msm_gld_data->erp_irq)
+ return -ENODEV;
+
+ /* clear existing errors before enabling the interrupt */
+ clear_gladiator_error(msm_gld_data->gladiator_virt_base,
+ msm_gld_data->reg_offs);
+ ret = devm_request_irq(&pdev->dev, msm_gld_data->erp_irq,
+ msm_gladiator_isr, IRQF_TRIGGER_HIGH,
+ "gladiator-error", msm_gld_data);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to register irq handler\n");
+
+ return ret;
+}
+
+static inline void gladiator_irq_init(void __iomem *gladiator_virt_base,
+ struct reg_off *offs)
+{
+ writel_relaxed(1, gladiator_virt_base + offs->gladiator_faulten);
+ writel_relaxed(1, gladiator_virt_base + offs->observer_0_faulten);
+}
+
+#define CCI_LEVEL 2
+static int gladiator_erp_pm_callback(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ unsigned int level = (unsigned long) data;
+ struct msm_gladiator_data *msm_gld_data = container_of(nb,
+ struct msm_gladiator_data, pm_notifier_block);
+
+ if (level != CCI_LEVEL)
+ return NOTIFY_DONE;
+
+ switch (val) {
+ case CPU_CLUSTER_PM_EXIT:
+ gladiator_irq_init(msm_gld_data->gladiator_virt_base,
+ msm_gld_data->reg_offs);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static void init_offsets_and_masks_v2(struct msm_gladiator_data *msm_gld_data)
+{
+ msm_gld_data->reg_offs->gladiator_id_coreid = 0x0;
+ msm_gld_data->reg_offs->gladiator_id_revisionid = 0x4;
+ msm_gld_data->reg_offs->gladiator_faulten = 0x1010;
+ msm_gld_data->reg_offs->gladiator_errvld = 0x1014;
+ msm_gld_data->reg_offs->gladiator_errclr = 0x1018;
+ msm_gld_data->reg_offs->gladiator_errlog0 = 0x101C;
+ msm_gld_data->reg_offs->gladiator_errlog1 = 0x1020;
+ msm_gld_data->reg_offs->gladiator_errlog2 = 0x1024;
+ msm_gld_data->reg_offs->gladiator_errlog3 = 0x1028;
+ msm_gld_data->reg_offs->gladiator_errlog4 = 0x102C;
+ msm_gld_data->reg_offs->gladiator_errlog5 = 0x1030;
+ msm_gld_data->reg_offs->gladiator_errlog6 = 0x1034;
+ msm_gld_data->reg_offs->gladiator_errlog7 = 0x1038;
+ msm_gld_data->reg_offs->gladiator_errlog8 = 0x103C;
+ msm_gld_data->reg_offs->observer_0_id_coreid = 0x8000;
+ msm_gld_data->reg_offs->observer_0_id_revisionid = 0x8004;
+ msm_gld_data->reg_offs->observer_0_faulten = 0x8008;
+ msm_gld_data->reg_offs->observer_0_errvld = 0x800C;
+ msm_gld_data->reg_offs->observer_0_errclr = 0x8010;
+ msm_gld_data->reg_offs->observer_0_errlog0 = 0x8014;
+ msm_gld_data->reg_offs->observer_0_errlog1 = 0x8018;
+ msm_gld_data->reg_offs->observer_0_errlog2 = 0x801C;
+ msm_gld_data->reg_offs->observer_0_errlog3 = 0x8020;
+ msm_gld_data->reg_offs->observer_0_errlog4 = 0x8024;
+ msm_gld_data->reg_offs->observer_0_errlog5 = 0x8028;
+ msm_gld_data->reg_offs->observer_0_errlog6 = 0x802C;
+ msm_gld_data->reg_offs->observer_0_errlog7 = 0x8030;
+ msm_gld_data->reg_offs->observer_0_errlog8 = 0x8034;
+ msm_gld_data->reg_offs->observer_0_stallen = 0x8038;
+
+ msm_gld_data->reg_masks_shifts->gld_trans_opcode_mask = 0xE;
+ msm_gld_data->reg_masks_shifts->gld_trans_opcode_shift = 1;
+ msm_gld_data->reg_masks_shifts->gld_error_type_mask = 0x700;
+ msm_gld_data->reg_masks_shifts->gld_error_type_shift = 8;
+ msm_gld_data->reg_masks_shifts->gld_len1_mask = 0xFFF;
+ msm_gld_data->reg_masks_shifts->gld_len1_shift = 16;
+ msm_gld_data->reg_masks_shifts->gld_trans_sourceid_mask = 0x7;
+ msm_gld_data->reg_masks_shifts->gld_trans_sourceid_shift = 0;
+ msm_gld_data->reg_masks_shifts->gld_trans_targetid_mask = 0x7;
+ msm_gld_data->reg_masks_shifts->gld_trans_targetid_shift = 0;
+ msm_gld_data->reg_masks_shifts->gld_errlog_error = 0x7;
+ msm_gld_data->reg_masks_shifts->gld_errlog5_error_type_mask =
+ 0xFF000000;
+ msm_gld_data->reg_masks_shifts->gld_errlog5_error_type_shift = 24;
+ msm_gld_data->reg_masks_shifts->gld_ace_port_parity_mask = 0xc000;
+ msm_gld_data->reg_masks_shifts->gld_ace_port_parity_shift = 14;
+ msm_gld_data->reg_masks_shifts->gld_ace_port_disconnect_mask = 0xf0000;
+ msm_gld_data->reg_masks_shifts->gld_ace_port_disconnect_shift = 16;
+ msm_gld_data->reg_masks_shifts->gld_ace_port_directory_mask = 0xf00000;
+ msm_gld_data->reg_masks_shifts->gld_ace_port_directory_shift = 20;
+ msm_gld_data->reg_masks_shifts->gld_index_parity_mask = 0x1FFF;
+ msm_gld_data->reg_masks_shifts->gld_index_parity_shift = 0;
+ msm_gld_data->reg_masks_shifts->obs_trans_opcode_mask = 0x1E;
+ msm_gld_data->reg_masks_shifts->obs_trans_opcode_shift = 1;
+ msm_gld_data->reg_masks_shifts->obs_error_type_mask = 0x700;
+ msm_gld_data->reg_masks_shifts->obs_error_type_shift = 8;
+ msm_gld_data->reg_masks_shifts->obs_len1_mask = 0x7F0;
+ msm_gld_data->reg_masks_shifts->obs_len1_shift = 16;
+}
+
+static void init_offsets_and_masks_v3(struct msm_gladiator_data *msm_gld_data)
+{
+ msm_gld_data->reg_offs->gladiator_id_coreid = 0x0;
+ msm_gld_data->reg_offs->gladiator_id_revisionid = 0x4;
+ msm_gld_data->reg_offs->gladiator_faulten = 0x1010;
+ msm_gld_data->reg_offs->gladiator_errvld = 0x1014;
+ msm_gld_data->reg_offs->gladiator_errclr = 0x1018;
+ msm_gld_data->reg_offs->gladiator_errlog0 = 0x101C;
+ msm_gld_data->reg_offs->gladiator_errlog1 = 0x1020;
+ msm_gld_data->reg_offs->gladiator_errlog2 = 0x1024;
+ msm_gld_data->reg_offs->gladiator_errlog3 = 0x1028;
+ msm_gld_data->reg_offs->gladiator_errlog4 = 0x102C;
+ msm_gld_data->reg_offs->gladiator_errlog5 = 0x1030;
+ msm_gld_data->reg_offs->gladiator_errlog6 = 0x1034;
+ msm_gld_data->reg_offs->gladiator_errlog7 = 0x1038;
+ msm_gld_data->reg_offs->gladiator_errlog8 = 0x103C;
+ msm_gld_data->reg_offs->observer_0_id_coreid = INVALID_NUM;
+ msm_gld_data->reg_offs->observer_0_id_revisionid = INVALID_NUM;
+ msm_gld_data->reg_offs->observer_0_faulten = 0x2008;
+ msm_gld_data->reg_offs->observer_0_errvld = 0x2010;
+ msm_gld_data->reg_offs->observer_0_errclr = 0x2018;
+ msm_gld_data->reg_offs->observer_0_errlog0 = 0x2020;
+ msm_gld_data->reg_offs->observer_0_errlog1 = 0x2024;
+ msm_gld_data->reg_offs->observer_0_errlog2 = 0x2028;
+ msm_gld_data->reg_offs->observer_0_errlog3 = 0x202C;
+ msm_gld_data->reg_offs->observer_0_errlog4 = 0x2030;
+ msm_gld_data->reg_offs->observer_0_errlog5 = 0x2034;
+ msm_gld_data->reg_offs->observer_0_errlog6 = 0x2038;
+ msm_gld_data->reg_offs->observer_0_errlog7 = 0x203C;
+ msm_gld_data->reg_offs->observer_0_errlog8 = INVALID_NUM;
+ msm_gld_data->reg_offs->observer_0_stallen = INVALID_NUM;
+
+ msm_gld_data->reg_masks_shifts->gld_trans_opcode_mask = 0xE;
+ msm_gld_data->reg_masks_shifts->gld_trans_opcode_shift = 1;
+ msm_gld_data->reg_masks_shifts->gld_error_type_mask = 0x700;
+ msm_gld_data->reg_masks_shifts->gld_error_type_shift = 8;
+ msm_gld_data->reg_masks_shifts->gld_len1_mask = 0xFFF0000;
+ msm_gld_data->reg_masks_shifts->gld_len1_shift = 16;
+ msm_gld_data->reg_masks_shifts->gld_trans_sourceid_mask = 0x7;
+ msm_gld_data->reg_masks_shifts->gld_trans_sourceid_shift = 0;
+ msm_gld_data->reg_masks_shifts->gld_trans_targetid_mask = 0x7;
+ msm_gld_data->reg_masks_shifts->gld_trans_targetid_shift = 0;
+ msm_gld_data->reg_masks_shifts->gld_errlog_error = 0x7;
+ msm_gld_data->reg_masks_shifts->gld_errlog5_error_type_mask =
+ 0xFF000000;
+ msm_gld_data->reg_masks_shifts->gld_errlog5_error_type_shift = 24;
+ msm_gld_data->reg_masks_shifts->gld_ace_port_parity_mask = 0xc000;
+ msm_gld_data->reg_masks_shifts->gld_ace_port_parity_shift = 14;
+ msm_gld_data->reg_masks_shifts->gld_ace_port_disconnect_mask = 0xf0000;
+ msm_gld_data->reg_masks_shifts->gld_ace_port_disconnect_shift = 16;
+ msm_gld_data->reg_masks_shifts->gld_ace_port_directory_mask = 0xf00000;
+ msm_gld_data->reg_masks_shifts->gld_ace_port_directory_shift = 20;
+ msm_gld_data->reg_masks_shifts->gld_index_parity_mask = 0x1FFF;
+ msm_gld_data->reg_masks_shifts->gld_index_parity_shift = 0;
+ msm_gld_data->reg_masks_shifts->obs_trans_opcode_mask = 0x70;
+ msm_gld_data->reg_masks_shifts->obs_trans_opcode_shift = 4;
+ msm_gld_data->reg_masks_shifts->obs_error_type_mask = 0x700;
+ msm_gld_data->reg_masks_shifts->obs_error_type_shift = 8;
+ msm_gld_data->reg_masks_shifts->obs_len1_mask = 0x1FF;
+ msm_gld_data->reg_masks_shifts->obs_len1_shift = 0;
+}
+
+static int gladiator_erp_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ struct msm_gladiator_data *msm_gld_data;
+
+ msm_gld_data = devm_kzalloc(&pdev->dev,
+ sizeof(struct msm_gladiator_data), GFP_KERNEL);
+ if (!msm_gld_data) {
+ ret = -ENOMEM;
+ goto bail;
+ }
+
+ msm_gld_data->reg_offs = devm_kzalloc(&pdev->dev,
+ sizeof(struct reg_off), GFP_KERNEL);
+ msm_gld_data->reg_masks_shifts = devm_kzalloc(&pdev->dev,
+ sizeof(struct reg_masks_shift), GFP_KERNEL);
+
+ if (!msm_gld_data->reg_offs || !msm_gld_data->reg_masks_shifts) {
+ ret = -ENOMEM;
+ goto bail;
+ }
+
+ msm_gld_data->glad_v2 = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,msm-gladiator-v2");
+ msm_gld_data->glad_v3 = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,msm-gladiator-v3");
+
+ if (msm_gld_data->glad_v2)
+ init_offsets_and_masks_v2(msm_gld_data);
+ else if (msm_gld_data->glad_v3)
+ init_offsets_and_masks_v3(msm_gld_data);
+
+ if (msm_gld_data->glad_v2) {
+ if (of_property_match_string(pdev->dev.of_node,
+ "clock-names", "atb_clk") >= 0) {
+ msm_gld_data->qdss_clk = devm_clk_get(&pdev->dev,
+ "atb_clk");
+ if (IS_ERR(msm_gld_data->qdss_clk)) {
+ dev_err(&pdev->dev, "Failed to get QDSS ATB clock\n");
+ goto bail;
+ }
+ } else {
+ dev_err(&pdev->dev, "No matching string of QDSS ATB clock\n");
+ goto bail;
+ }
+
+ ret = clk_prepare_enable(msm_gld_data->qdss_clk);
+ if (ret)
+ goto err_atb_clk;
+ }
+
+ ret = parse_dt_node(pdev, msm_gld_data);
+ if (ret)
+ goto bail;
+ msm_gld_data->pm_notifier_block.notifier_call =
+ gladiator_erp_pm_callback;
+
+ gladiator_irq_init(msm_gld_data->gladiator_virt_base,
+ msm_gld_data->reg_offs);
+ platform_set_drvdata(pdev, msm_gld_data);
+ cpu_pm_register_notifier(&msm_gld_data->pm_notifier_block);
+#ifdef CONFIG_PANIC_ON_GLADIATOR_ERROR
+ enable_panic_on_error = 1;
+#endif
+ dev_info(&pdev->dev, "MSM Gladiator Error Reporting Initialized\n");
+ return ret;
+
+err_atb_clk:
+ clk_disable_unprepare(msm_gld_data->qdss_clk);
+
+bail:
+ dev_err(&pdev->dev, "Probe failed bailing out\n");
+ return ret;
+}
+
+static int gladiator_erp_remove(struct platform_device *pdev)
+{
+ struct msm_gladiator_data *msm_gld_data = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+ cpu_pm_unregister_notifier(&msm_gld_data->pm_notifier_block);
+ clk_disable_unprepare(msm_gld_data->qdss_clk);
+ return 0;
+}
+
+static struct platform_driver gladiator_erp_driver = {
+ .probe = gladiator_erp_probe,
+ .remove = gladiator_erp_remove,
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = gladiator_erp_match_table,
+ },
+};
+
+static int __init init_gladiator_erp(void)
+{
+ int ret;
+
+ ret = scm_is_secure_device();
+ if (ret == 0) {
+ pr_info("Gladiator Error Reporting not available\n");
+ return -ENODEV;
+ }
+
+ return platform_driver_register(&gladiator_erp_driver);
+}
+module_init(init_gladiator_erp);
+
+static void __exit exit_gladiator_erp(void)
+{
+ return platform_driver_unregister(&gladiator_erp_driver);
+}
+module_exit(exit_gladiator_erp);
+
+MODULE_DESCRIPTION("Gladiator Error Reporting");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/gladiator_erp_v2.c b/drivers/soc/qcom/gladiator_erp_v2.c
deleted file mode 100644
index 25c7bd7..0000000
--- a/drivers/soc/qcom/gladiator_erp_v2.c
+++ /dev/null
@@ -1,851 +0,0 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/cpu_pm.h>
-#include <linux/platform_device.h>
-#include <soc/qcom/scm.h>
-#include <linux/of.h>
-#include <linux/clk.h>
-
-#define MODULE_NAME "gladiator-v2_error_reporting"
-
-/* Register Offsets */
-#define GLADIATOR_ID_COREID 0x0
-#define GLADIATOR_ID_REVISIONID 0x4
-#define GLADIATOR_FAULTEN 0x1010
-#define GLADIATOR_ERRVLD 0x1014
-#define GLADIATOR_ERRCLR 0x1018
-#define GLADIATOR_ERRLOG0 0x101C
-#define GLADIATOR_ERRLOG1 0x1020
-#define GLADIATOR_ERRLOG2 0x1024
-#define GLADIATOR_ERRLOG3 0x1028
-#define GLADIATOR_ERRLOG4 0x102C
-#define GLADIATOR_ERRLOG5 0x1030
-#define GLADIATOR_ERRLOG6 0x1034
-#define GLADIATOR_ERRLOG7 0x1038
-#define GLADIATOR_ERRLOG8 0x103C
-#define OBSERVER_0_ID_COREID 0x8000
-#define OBSERVER_0_ID_REVISIONID 0x8004
-#define OBSERVER_0_FAULTEN 0x8008
-#define OBSERVER_0_ERRVLD 0x800C
-#define OBSERVER_0_ERRCLR 0x8010
-#define OBSERVER_0_ERRLOG0 0x8014
-#define OBSERVER_0_ERRLOG1 0x8018
-#define OBSERVER_0_ERRLOG2 0x801C
-#define OBSERVER_0_ERRLOG3 0x8020
-#define OBSERVER_0_ERRLOG4 0x8024
-#define OBSERVER_0_ERRLOG5 0x8028
-#define OBSERVER_0_ERRLOG6 0x802C
-#define OBSERVER_0_ERRLOG7 0x8030
-#define OBSERVER_0_ERRLOG8 0x8034
-#define OBSERVER_0_STALLEN 0x8038
-
-#define GLD_TRANS_OPCODE_MASK 0xE
-#define GLD_TRANS_OPCODE_SHIFT 1
-#define GLD_ERROR_TYPE_MASK 0x700
-#define GLD_ERROR_TYPE_SHIFT 8
-#define GLD_LEN1_MASK 0xFFF0000
-#define GLD_LEN1_SHIFT 16
-#define GLD_TRANS_SOURCEID_MASK 0x7
-#define GLD_TRANS_SOURCEID_SHIFT 0
-#define GLD_TRANS_TARGETID_MASK 0x7
-#define GLD_TRANS_TARGETID_SHIFT 0
-#define GLD_ERRLOG_ERROR 0x7
-#define GLD_ERRLOG5_ERROR_TYPE_MASK 0xFF000000
-#define GLD_ERRLOG5_ERROR_TYPE_SHIFT 24
-#define GLD_ACE_PORT_PARITY_MASK 0xc000
-#define GLD_ACE_PORT_PARITY_SHIFT 14
-#define GLD_ACE_PORT_DISCONNECT_MASK 0xf0000
-#define GLD_ACE_PORT_DISCONNECT_SHIFT 16
-#define GLD_ACE_PORT_DIRECTORY_MASK 0xf00000
-#define GLD_ACE_PORT_DIRECTORY_SHIFT 20
-#define GLD_INDEX_PARITY_MASK 0x1FFF
-#define GLD_INDEX_PARITY_SHIFT 0
-#define OBS_TRANS_OPCODE_MASK 0x1E
-#define OBS_TRANS_OPCODE_SHIFT 1
-#define OBS_ERROR_TYPE_MASK 0x700
-#define OBS_ERROR_TYPE_SHIFT 8
-#define OBS_LEN1_MASK 0x7F0000
-#define OBS_LEN1_SHIFT 16
-
-struct msm_gladiator_data {
- void __iomem *gladiator_virt_base;
- int erp_irq;
- struct notifier_block pm_notifier_block;
- struct clk *qdss_clk;
-};
-
-static int enable_panic_on_error;
-module_param(enable_panic_on_error, int, 0);
-
-enum gld_trans_opcode {
- GLD_RD,
- GLD_RDX,
- GLD_RDL,
- GLD_RESERVED,
- GLD_WR,
- GLD_WRC,
- GLD_PRE,
-};
-
-enum obs_trans_opcode {
- OBS_RD,
- OBS_RDW,
- OBS_RDL,
- OBS_RDX,
- OBS_WR,
- OBS_WRW,
- OBS_WRC,
- OBS_RESERVED,
- OBS_PRE,
- OBS_URG,
-};
-
-enum obs_err_code {
- OBS_SLV,
- OBS_DEC,
- OBS_UNS,
- OBS_DISC,
- OBS_SEC,
- OBS_HIDE,
- OBS_TMO,
- OBS_RSV,
-};
-
-enum err_log {
- ID_COREID,
- ID_REVISIONID,
- FAULTEN,
- ERRVLD,
- ERRCLR,
- ERR_LOG0,
- ERR_LOG1,
- ERR_LOG2,
- ERR_LOG3,
- ERR_LOG4,
- ERR_LOG5,
- ERR_LOG6,
- ERR_LOG7,
- ERR_LOG8,
- STALLEN,
- MAX_NUM,
-};
-
-enum type_logger_error {
- DATA_TRANSFER_ERROR,
- DVM_ERROR,
- TX_ERROR,
- TXR_ERROR,
- DISCONNECT_ERROR,
- DIRECTORY_ERROR,
- PARITY_ERROR,
-};
-
-static void clear_gladiator_error(void __iomem *gladiator_virt_base)
-{
- writel_relaxed(1, gladiator_virt_base + GLADIATOR_ERRCLR);
- writel_relaxed(1, gladiator_virt_base + OBSERVER_0_ERRCLR);
-}
-
-static inline void print_gld_transaction(unsigned int opc)
-{
- switch (opc) {
- case GLD_RD:
- pr_alert("Transaction type: READ\n");
- break;
- case GLD_RDX:
- pr_alert("Transaction type: EXCLUSIVE READ\n");
- break;
- case GLD_RDL:
- pr_alert("Transaction type: LINKED READ\n");
- break;
- case GLD_WR:
- pr_alert("Transaction type: WRITE\n");
- break;
- case GLD_WRC:
- pr_alert("Transaction type: CONDITIONAL WRITE\n");
- break;
- case GLD_PRE:
- pr_alert("Transaction: Preamble packet of linked sequence\n");
- break;
- default:
- pr_alert("Transaction type: Unknown; value:%u\n", opc);
- }
-}
-
-static inline void print_gld_errtype(unsigned int errtype)
-{
- if (errtype == 0)
- pr_alert("Error type: Snoop data transfer\n");
- else if (errtype == 1)
- pr_alert("Error type: DVM error\n");
- else if (errtype == 3)
- pr_alert("Error type: Disconnect, directory, or parity error\n");
- else
- pr_alert("Error type: Unknown; value:%u\n", errtype);
-}
-
-static void decode_gld_errlog0(u32 err_reg)
-{
- unsigned int opc, errtype, len1;
-
- opc = (err_reg & GLD_TRANS_OPCODE_MASK) >> GLD_TRANS_OPCODE_SHIFT;
- errtype = (err_reg & GLD_ERROR_TYPE_MASK) >> GLD_ERROR_TYPE_SHIFT;
- len1 = (err_reg & GLD_LEN1_MASK) >> GLD_LEN1_SHIFT;
-
- print_gld_transaction(opc);
- print_gld_errtype(errtype);
- pr_alert("number of payload bytes: %d\n", len1 + 1);
-}
-
-static void decode_gld_errlog1(u32 err_reg)
-{
- if ((err_reg & GLD_ERRLOG_ERROR) == GLD_ERRLOG_ERROR)
- pr_alert("Transaction issued on IO target generic interface\n");
- else
- pr_alert("Transaction source ID: %d\n",
- (err_reg & GLD_TRANS_SOURCEID_MASK)
- >> GLD_TRANS_SOURCEID_SHIFT);
-}
-
-static void decode_gld_errlog2(u32 err_reg)
-{
- if ((err_reg & GLD_ERRLOG_ERROR) == GLD_ERRLOG_ERROR)
- pr_alert("Error response coming from: external DVM network\n");
- else
- pr_alert("Error response coming from: Target ID: %d\n",
- (err_reg & GLD_TRANS_TARGETID_MASK)
- >> GLD_TRANS_TARGETID_SHIFT);
-}
-
-static void decode_ace_port_index(u32 type, u32 error)
-{
- unsigned port;
-
- switch (type) {
- case DISCONNECT_ERROR:
- port = (error & GLD_ACE_PORT_DISCONNECT_MASK)
- >> GLD_ACE_PORT_DISCONNECT_SHIFT;
- pr_alert("ACE port index: %d\n", port);
- break;
- case DIRECTORY_ERROR:
- port = (error & GLD_ACE_PORT_DIRECTORY_MASK)
- >> GLD_ACE_PORT_DIRECTORY_SHIFT;
- pr_alert("ACE port index: %d\n", port);
- break;
- case PARITY_ERROR:
- port = (error & GLD_ACE_PORT_PARITY_MASK)
- >> GLD_ACE_PORT_PARITY_SHIFT;
- pr_alert("ACE port index: %d\n", port);
- }
-}
-
-static void decode_index_parity(u32 error)
-{
- pr_alert("Index: %d\n",
- (error & GLD_INDEX_PARITY_MASK)
- >> GLD_INDEX_PARITY_SHIFT);
-}
-
-static void decode_gld_logged_error(u32 err_reg5)
-{
- unsigned int log_err_type, i, value;
-
- log_err_type = (err_reg5 & GLD_ERRLOG5_ERROR_TYPE_MASK)
- >> GLD_ERRLOG5_ERROR_TYPE_SHIFT;
- for (i = 0 ; i <= 6 ; i++) {
- value = log_err_type & 0x1;
- switch (i) {
- case DATA_TRANSFER_ERROR:
- if (value == 0)
- continue;
- pr_alert("Error type: Data transfer error\n");
- break;
- case DVM_ERROR:
- if (value == 0)
- continue;
- pr_alert("Error type: DVM error\n");
- break;
- case TX_ERROR:
- if (value == 0)
- continue;
- pr_alert("Error type: Tx error\n");
- break;
- case TXR_ERROR:
- if (value == 0)
- continue;
- pr_alert("Error type: TxR error\n");
- break;
- case DISCONNECT_ERROR:
- if (value == 0)
- continue;
- pr_alert("Error type: Disconnect error\n");
- decode_ace_port_index(
- DISCONNECT_ERROR,
- err_reg5);
- break;
- case DIRECTORY_ERROR:
- if (value == 0)
- continue;
- pr_alert("Error type: Directory error\n");
- decode_ace_port_index(
- DIRECTORY_ERROR,
- err_reg5);
- break;
- case PARITY_ERROR:
- if (value == 0)
- continue;
- pr_alert("Error type: Parity error\n");
- decode_ace_port_index(PARITY_ERROR, err_reg5);
- decode_index_parity(err_reg5);
- break;
- }
- log_err_type = log_err_type >> 1;
- }
-}
-
-static void decode_gld_errlog(u32 err_reg, unsigned int err_log)
-{
- switch (err_log) {
- case ERR_LOG0:
- decode_gld_errlog0(err_reg);
- break;
- case ERR_LOG1:
- decode_gld_errlog1(err_reg);
- break;
- case ERR_LOG2:
- decode_gld_errlog2(err_reg);
- break;
- case ERR_LOG3:
- pr_alert("Lower 32-bits of error address: %08x\n", err_reg);
- break;
- case ERR_LOG4:
- pr_alert("Upper 32-bits of error address: %08x\n", err_reg);
- break;
- case ERR_LOG5:
- pr_alert("Lower 32-bits of user: %08x\n", err_reg);
- break;
- case ERR_LOG6:
- pr_alert("Mid 32-bits(63-32) of user: %08x\n", err_reg);
- break;
- case ERR_LOG7:
- break;
- case ERR_LOG8:
- pr_alert("Upper 32-bits(95-64) of user: %08x\n", err_reg);
- break;
- default:
- pr_alert("Invalid error register; reg num:%u\n", err_log);
- }
-}
-
-static inline void print_obs_transaction(unsigned int opc)
-{
- switch (opc) {
- case OBS_RD:
- pr_alert("Transaction type: READ\n");
- break;
- case OBS_RDW:
- pr_alert("Transaction type: WRAPPED READ\n");
- break;
- case OBS_RDL:
- pr_alert("Transaction type: LINKED READ\n");
- break;
- case OBS_RDX:
- pr_alert("Transaction type: EXCLUSIVE READ\n");
- break;
- case OBS_WR:
- pr_alert("Transaction type: WRITE\n");
- break;
- case OBS_WRW:
- pr_alert("Transaction type: WRAPPED WRITE\n");
- break;
- case OBS_WRC:
- pr_alert("Transaction type: CONDITIONAL WRITE\n");
- break;
- case OBS_PRE:
- pr_alert("Transaction: Preamble packet of linked sequence\n");
- break;
- case OBS_URG:
- pr_alert("Transaction type: Urgency Packet\n");
- break;
- default:
- pr_alert("Transaction type: Unknown; value:%u\n", opc);
- }
-}
-
-static inline void print_obs_errcode(unsigned int errcode)
-{
- switch (errcode) {
- case OBS_SLV:
- pr_alert("Error code: Target error detected by slave\n");
- pr_alert("Source: Target\n");
- break;
- case OBS_DEC:
- pr_alert("Error code: Address decode error\n");
- pr_alert("Source: Initiator NIU\n");
- break;
- case OBS_UNS:
- pr_alert("Error code: Unsupported request\n");
- pr_alert("Source: Target NIU\n");
- break;
- case OBS_DISC:
- pr_alert("Error code: Disconnected target or domain\n");
- pr_alert("Source: Power Disconnect\n");
- break;
- case OBS_SEC:
- pr_alert("Error code: Security violation\n");
- pr_alert("Source: Initiator NIU or Firewall\n");
- break;
- case OBS_HIDE:
- pr_alert("Error :Hidden security violation, reported as OK\n");
- pr_alert("Source: Firewall\n");
- break;
- case OBS_TMO:
- pr_alert("Error code: Time-out\n");
- pr_alert("Source: Target NIU\n");
- break;
- default:
- pr_alert("Error code: Unknown; code:%u\n", errcode);
- }
-}
-
-static void decode_obs_errlog0(u32 err_reg)
-{
- unsigned int opc, errcode, len1;
-
- opc = (err_reg & OBS_TRANS_OPCODE_MASK) >> OBS_TRANS_OPCODE_SHIFT;
- errcode = (err_reg & OBS_ERROR_TYPE_MASK) >> OBS_ERROR_TYPE_SHIFT;
- len1 = (err_reg & OBS_LEN1_MASK) >> OBS_LEN1_SHIFT;
-
- print_obs_transaction(opc);
- print_obs_errcode(errcode);
- pr_alert("number of payload bytes: %d\n", len1 + 1);
-}
-
-static void decode_obs_errlog(u32 err_reg, unsigned int err_log)
-{
- switch (err_log) {
- case ERR_LOG0:
- decode_obs_errlog0(err_reg);
- break;
- case ERR_LOG1:
- pr_alert("RouteId of the error: %08x\n", err_reg);
- break;
- case ERR_LOG2:
- /* reserved error log register */
- break;
- case ERR_LOG3:
- pr_alert("Lower 32-bits of error address: %08x\n", err_reg);
- break;
- case ERR_LOG4:
- pr_alert("Upper 12-bits of error address: %08x\n", err_reg);
- break;
- case ERR_LOG5:
- pr_alert("Lower 13-bits of user: %08x\n", err_reg);
- break;
- case ERR_LOG6:
- /* reserved error log register */
- break;
- case ERR_LOG7:
- pr_alert("Security filed of the logged error: %08x\n", err_reg);
- break;
- case ERR_LOG8:
- /* reserved error log register */
- break;
- case STALLEN:
- pr_alert("stall mode of the error logger: %08x\n",
- err_reg & 0x1);
- break;
- default:
- pr_alert("Invalid error register; reg num:%u\n", err_log);
- }
-}
-
-static u32 get_gld_offset(unsigned int err_log)
-{
- u32 offset = 0;
-
- switch (err_log) {
- case FAULTEN:
- offset = GLADIATOR_FAULTEN;
- break;
- case ERRVLD:
- offset = GLADIATOR_ERRVLD;
- break;
- case ERRCLR:
- offset = GLADIATOR_ERRCLR;
- break;
- case ERR_LOG0:
- offset = GLADIATOR_ERRLOG0;
- break;
- case ERR_LOG1:
- offset = GLADIATOR_ERRLOG1;
- break;
- case ERR_LOG2:
- offset = GLADIATOR_ERRLOG2;
- break;
- case ERR_LOG3:
- offset = GLADIATOR_ERRLOG3;
- break;
- case ERR_LOG4:
- offset = GLADIATOR_ERRLOG4;
- break;
- case ERR_LOG5:
- offset = GLADIATOR_ERRLOG5;
- break;
- case ERR_LOG6:
- offset = GLADIATOR_ERRLOG6;
- break;
- case ERR_LOG7:
- offset = GLADIATOR_ERRLOG7;
- break;
- case ERR_LOG8:
- offset = GLADIATOR_ERRLOG8;
- break;
- default:
- pr_alert("Invalid gladiator error register; reg num:%u\n",
- err_log);
- }
- return offset;
-}
-
-static u32 get_obs_offset(unsigned int err_log)
-{
- u32 offset = 0;
-
- switch (err_log) {
- case ID_COREID:
- offset = OBSERVER_0_ID_COREID;
- break;
- case ID_REVISIONID:
- offset = OBSERVER_0_ID_REVISIONID;
- break;
- case FAULTEN:
- offset = OBSERVER_0_FAULTEN;
- break;
- case ERRVLD:
- offset = OBSERVER_0_ERRVLD;
- break;
- case ERRCLR:
- offset = OBSERVER_0_ERRCLR;
- break;
- case ERR_LOG0:
- offset = OBSERVER_0_ERRLOG0;
- break;
- case ERR_LOG1:
- offset = OBSERVER_0_ERRLOG1;
- break;
- case ERR_LOG2:
- offset = OBSERVER_0_ERRLOG2;
- break;
- case ERR_LOG3:
- offset = OBSERVER_0_ERRLOG3;
- break;
- case ERR_LOG4:
- offset = OBSERVER_0_ERRLOG4;
- break;
- case ERR_LOG5:
- offset = OBSERVER_0_ERRLOG5;
- break;
- case ERR_LOG6:
- offset = OBSERVER_0_ERRLOG6;
- break;
- case ERR_LOG7:
- offset = OBSERVER_0_ERRLOG7;
- break;
- case ERR_LOG8:
- offset = OBSERVER_0_ERRLOG8;
- break;
- case STALLEN:
- offset = OBSERVER_0_STALLEN;
- break;
- default:
- pr_alert("Invalid observer error register; reg num:%u\n",
- err_log);
- }
- return offset;
-}
-
-static void decode_gld_errlog5(struct msm_gladiator_data *msm_gld_data)
-{
- unsigned int errtype;
- u32 err_reg0, err_reg5;
-
- err_reg0 = readl_relaxed(msm_gld_data->gladiator_virt_base +
- get_gld_offset(ERR_LOG0));
- err_reg5 = readl_relaxed(msm_gld_data->gladiator_virt_base +
- get_gld_offset(ERR_LOG5));
-
- errtype = (err_reg0 & GLD_ERROR_TYPE_MASK) >> GLD_ERROR_TYPE_SHIFT;
- if (errtype == 3)
- decode_gld_logged_error(err_reg5);
- else if (errtype == 0 || errtype == 1)
- pr_alert("Lower 32-bits of user: %08x\n", err_reg5);
- else
- pr_alert("Error type: Unknown; value:%u\n", errtype);
-}
-
-static irqreturn_t msm_gladiator_isr(int irq, void *dev_id)
-{
- u32 err_reg;
- unsigned int err_log, err_buf[MAX_NUM];
-
- struct msm_gladiator_data *msm_gld_data = dev_id;
-
- /* Check validity */
- bool gld_err_valid = readl_relaxed(msm_gld_data->gladiator_virt_base +
- GLADIATOR_ERRVLD);
-
- bool obsrv_err_valid = readl_relaxed(
- msm_gld_data->gladiator_virt_base + OBSERVER_0_ERRVLD);
-
- if (!gld_err_valid && !obsrv_err_valid) {
- pr_err("%s Invalid Gladiator error reported, clear it\n",
- __func__);
- /* Clear IRQ */
- clear_gladiator_error(msm_gld_data->gladiator_virt_base);
- return IRQ_HANDLED;
- }
- pr_alert("Gladiator Error Detected:\n");
- if (gld_err_valid) {
- for (err_log = FAULTEN; err_log <= ERR_LOG8; err_log++) {
- err_buf[err_log] = readl_relaxed(
- msm_gld_data->gladiator_virt_base +
- get_gld_offset(err_log));
- }
- pr_alert("Main log register data:\n%08x %08x %08x %08x\n%08x %08x %08x %08x\n%08x %08x %08x\n",
- err_buf[0], err_buf[1], err_buf[2], err_buf[3], err_buf[4], err_buf[5], err_buf[6],
- err_buf[7], err_buf[8], err_buf[9], err_buf[10]);
- }
-
- if (obsrv_err_valid) {
- for (err_log = ID_COREID; err_log <= STALLEN; err_log++) {
- err_buf[err_log] = readl_relaxed(
- msm_gld_data->gladiator_virt_base +
- get_obs_offset(err_log));
- }
- pr_alert("Observer log register data:\n%08x %08x %08x %08x\n%08x %08x %08x %08x\n%08x %08x %08x %08x\n%08x\n",
- err_buf[0], err_buf[1], err_buf[2], err_buf[3], err_buf[4], err_buf[5], err_buf[6], err_buf[7],
- err_buf[8], err_buf[9], err_buf[10], err_buf[11], err_buf[12]);
- }
-
- if (gld_err_valid) {
- pr_alert("Main error log register data:\n");
- for (err_log = ERR_LOG0; err_log <= ERR_LOG8; err_log++) {
- /* skip log register 7 as its reserved */
- if (err_log == ERR_LOG7)
- continue;
- if (err_log == ERR_LOG5) {
- decode_gld_errlog5(msm_gld_data);
- continue;
- }
- err_reg = readl_relaxed(
- msm_gld_data->gladiator_virt_base +
- get_gld_offset(err_log));
- decode_gld_errlog(err_reg, err_log);
- }
- }
- if (obsrv_err_valid) {
- pr_alert("Observor error log register data:\n");
- for (err_log = ERR_LOG0; err_log <= STALLEN; err_log++) {
- /* skip log register 2, 6 and 8 as they are reserved */
- if ((err_log == ERR_LOG2) || (err_log == ERR_LOG6)
- || (err_log == ERR_LOG8))
- continue;
- err_reg = readl_relaxed(
- msm_gld_data->gladiator_virt_base +
- get_obs_offset(err_log));
- decode_obs_errlog(err_reg, err_log);
- }
- }
- /* Clear IRQ */
- clear_gladiator_error(msm_gld_data->gladiator_virt_base);
- if (enable_panic_on_error)
- panic("Gladiator Cache Interconnect Error Detected!\n");
- else
- WARN(1, "Gladiator Cache Interconnect Error Detected\n");
-
- return IRQ_HANDLED;
-}
-
-static const struct of_device_id gladiator_erp_v2_match_table[] = {
- { .compatible = "qcom,msm-gladiator-v2" },
- {},
-};
-
-static int parse_dt_node(struct platform_device *pdev,
- struct msm_gladiator_data *msm_gld_data)
-{
- int ret = 0;
- struct resource *res;
-
- res = platform_get_resource_byname(pdev,
- IORESOURCE_MEM, "gladiator_base");
- if (!res)
- return -ENODEV;
- if (!devm_request_mem_region(&pdev->dev, res->start,
- resource_size(res),
- "msm-gladiator-erp")) {
-
- dev_err(&pdev->dev, "%s cannot reserve gladiator erp region\n",
- __func__);
- return -ENXIO;
- }
- msm_gld_data->gladiator_virt_base = devm_ioremap(&pdev->dev,
- res->start, resource_size(res));
- if (!msm_gld_data->gladiator_virt_base) {
- dev_err(&pdev->dev, "%s cannot map gladiator register space\n",
- __func__);
- return -ENXIO;
- }
- msm_gld_data->erp_irq = platform_get_irq(pdev, 0);
- if (!msm_gld_data->erp_irq)
- return -ENODEV;
-
- /* clear existing errors before enabling the interrupt */
- clear_gladiator_error(msm_gld_data->gladiator_virt_base);
- ret = devm_request_irq(&pdev->dev, msm_gld_data->erp_irq,
- msm_gladiator_isr, IRQF_TRIGGER_HIGH,
- "gladiator-error", msm_gld_data);
- if (ret)
- dev_err(&pdev->dev, "Failed to register irq handler\n");
-
- return ret;
-}
-
-static inline void gladiator_irq_init(void __iomem *gladiator_virt_base)
-{
- writel_relaxed(1, gladiator_virt_base + GLADIATOR_FAULTEN);
- writel_relaxed(1, gladiator_virt_base + OBSERVER_0_FAULTEN);
-}
-
-#define CCI_LEVEL 2
-static int gladiator_erp_pm_callback(struct notifier_block *nb,
- unsigned long val, void *data)
-{
- unsigned int level = (unsigned long) data;
- struct msm_gladiator_data *msm_gld_data = container_of(nb,
- struct msm_gladiator_data, pm_notifier_block);
-
- if (level != CCI_LEVEL)
- return NOTIFY_DONE;
-
- switch (val) {
- case CPU_CLUSTER_PM_EXIT:
- gladiator_irq_init(msm_gld_data->gladiator_virt_base);
- break;
- default:
- return NOTIFY_DONE;
- }
-
- return NOTIFY_OK;
-}
-
-static int gladiator_erp_v2_probe(struct platform_device *pdev)
-{
- int ret = -1;
- struct msm_gladiator_data *msm_gld_data;
-
- msm_gld_data = devm_kzalloc(&pdev->dev,
- sizeof(struct msm_gladiator_data), GFP_KERNEL);
- if (!msm_gld_data) {
- ret = -ENOMEM;
- goto bail;
- }
-
- if (of_property_match_string(pdev->dev.of_node,
- "clock-names", "atb_clk") >= 0) {
- msm_gld_data->qdss_clk = devm_clk_get(&pdev->dev, "atb_clk");
- if (IS_ERR(msm_gld_data->qdss_clk)) {
- dev_err(&pdev->dev, "Failed to get QDSS ATB clock\n");
- goto bail;
- }
- } else {
- dev_err(&pdev->dev, "No matching string of QDSS ATB clock\n");
- goto bail;
- }
-
- ret = clk_prepare_enable(msm_gld_data->qdss_clk);
- if (ret)
- goto err_atb_clk;
-
- ret = parse_dt_node(pdev, msm_gld_data);
- if (ret)
- goto bail;
- msm_gld_data->pm_notifier_block.notifier_call =
- gladiator_erp_pm_callback;
-
- gladiator_irq_init(msm_gld_data->gladiator_virt_base);
- platform_set_drvdata(pdev, msm_gld_data);
- cpu_pm_register_notifier(&msm_gld_data->pm_notifier_block);
-#ifdef CONFIG_PANIC_ON_GLADIATOR_ERROR_V2
- enable_panic_on_error = 1;
-#endif
- dev_info(&pdev->dev, "MSM Gladiator Error Reporting V2 Initialized\n");
- return ret;
-
-err_atb_clk:
- clk_disable_unprepare(msm_gld_data->qdss_clk);
-
-bail:
- dev_err(&pdev->dev, "Probe failed bailing out\n");
- return ret;
-}
-
-static int gladiator_erp_v2_remove(struct platform_device *pdev)
-{
- struct msm_gladiator_data *msm_gld_data = platform_get_drvdata(pdev);
-
- platform_set_drvdata(pdev, NULL);
- cpu_pm_unregister_notifier(&msm_gld_data->pm_notifier_block);
- clk_disable_unprepare(msm_gld_data->qdss_clk);
- return 0;
-}
-
-static struct platform_driver gladiator_erp_driver = {
- .probe = gladiator_erp_v2_probe,
- .remove = gladiator_erp_v2_remove,
- .driver = {
- .name = MODULE_NAME,
- .owner = THIS_MODULE,
- .of_match_table = gladiator_erp_v2_match_table,
- },
-};
-
-static int __init init_gladiator_erp_v2(void)
-{
- int ret;
-
- ret = scm_is_secure_device();
- if (ret == 0) {
- pr_info("Gladiator Error Reporting not available\n");
- return -ENODEV;
- }
-
- return platform_driver_register(&gladiator_erp_driver);
-}
-module_init(init_gladiator_erp_v2);
-
-static void __exit exit_gladiator_erp_v2(void)
-{
- return platform_driver_unregister(&gladiator_erp_driver);
-}
-module_exit(exit_gladiator_erp_v2);
-
-MODULE_DESCRIPTION("Gladiator Error Reporting V2");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/glink_spi_xprt.c b/drivers/soc/qcom/glink_spi_xprt.c
index 66caa6e..0ff92cd 100644
--- a/drivers/soc/qcom/glink_spi_xprt.c
+++ b/drivers/soc/qcom/glink_spi_xprt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1819,7 +1819,7 @@
einfo->spi_dev = spi_dev;
break;
case WDSP_EVENT_IPC1_INTR:
- queue_kthread_work(&einfo->kworker, &einfo->kwork);
+ kthread_queue_work(&einfo->kworker, &einfo->kwork);
break;
default:
pr_debug("%s: unhandled event %d", __func__, event);
@@ -2042,8 +2042,8 @@
einfo->in_ssr = true;
einfo->fifo_size = DEFAULT_FIFO_SIZE;
- init_kthread_work(&einfo->kwork, rx_worker);
- init_kthread_worker(&einfo->kworker);
+ kthread_init_work(&einfo->kwork, rx_worker);
+ kthread_init_worker(&einfo->kworker);
init_srcu_struct(&einfo->use_ref);
mutex_init(&einfo->write_lock);
init_waitqueue_head(&einfo->tx_blocked_queue);
@@ -2097,7 +2097,7 @@
dev_set_drvdata(&pdev->dev, NULL);
glink_core_unregister_transport(&einfo->xprt_if);
reg_xprt_fail:
- flush_kthread_worker(&einfo->kworker);
+ kthread_flush_worker(&einfo->kworker);
kthread_stop(einfo->task);
einfo->task = NULL;
kthread_fail:
@@ -2117,7 +2117,7 @@
einfo = (struct edge_info *)dev_get_drvdata(&pdev->dev);
glink_core_unregister_transport(&einfo->xprt_if);
- flush_kthread_worker(&einfo->kworker);
+ kthread_flush_worker(&einfo->kworker);
kthread_stop(einfo->task);
einfo->task = NULL;
spin_lock_irqsave(&edge_infos_lock, flags);
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 7c08c28..722127d 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -2145,7 +2145,7 @@
if (event_data == NULL)
return notifier_from_errno(-ENOMEM);
- if (state == NULL || *state != SHUTDOWN)
+ if (state == NULL || *state != ROOT_PD_SHUTDOWN)
event_data->crashed = true;
icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
diff --git a/drivers/soc/qcom/llcc-core.c b/drivers/soc/qcom/llcc-core.c
index e4d5acc..2c9d0a0 100644
--- a/drivers/soc/qcom/llcc-core.c
+++ b/drivers/soc/qcom/llcc-core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -22,7 +22,6 @@
/* Config registers offsets*/
#define COMMON_CFG0 0x00030004
#define DRP_ECC_ERROR_CFG 0x00040000
-#define TRP_MISC_CFG 0x00022300
/* TRP, DRP interrupt register offsets */
#define CMN_INTERRUPT_0_ENABLE 0x0003001C
@@ -33,8 +32,6 @@
#define DATA_RAM_ECC_ENABLE 0x1
#define SB_ERROR_THRESHOLD 0x1
#define SB_ERROR_THRESHOLD_SHIFT 24
-#define TAG_RAM_ECC_DISABLE 0x1
-#define TAG_RAM_ECC_DISABLE_SHIFT 0x1
#define SB_DB_TRP_INTERRUPT_ENABLE 0x3
#define TRP0_INTERRUPT_ENABLE 0x1
#define DRP0_INTERRUPT_ENABLE BIT(6)
@@ -43,14 +40,8 @@
static void qcom_llcc_core_setup(struct regmap *llcc_regmap)
{
- u32 trp_misc_val;
u32 sb_err_threshold;
- /* Enable Tag RAM ECC */
- trp_misc_val = (TAG_RAM_ECC_DISABLE << TAG_RAM_ECC_DISABLE_SHIFT);
- regmap_update_bits(llcc_regmap, TRP_MISC_CFG,
- ~trp_misc_val, trp_misc_val);
-
/* Enable TRP in instance 2 of common interrupt enable register */
regmap_update_bits(llcc_regmap, CMN_INTERRUPT_2_ENABLE,
TRP0_INTERRUPT_ENABLE, TRP0_INTERRUPT_ENABLE);
diff --git a/drivers/soc/qcom/lpm-stats.c b/drivers/soc/qcom/lpm-stats.c
new file mode 100644
index 0000000..74a86ec
--- /dev/null
+++ b/drivers/soc/qcom/lpm-stats.c
@@ -0,0 +1,864 @@
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include <soc/qcom/spm.h>
+#include <soc/qcom/pm.h>
+#include <soc/qcom/lpm-stats.h>
+
+#define MAX_STR_LEN 256
+#define MAX_TIME_LEN 20
+const char *lpm_stats_reset = "reset";
+const char *lpm_stats_suspend = "suspend";
+
+struct lpm_sleep_time {
+ struct kobj_attribute ts_attr;
+ unsigned int cpu;
+};
+
+struct level_stats {
+ const char *name;
+ struct lpm_stats *owner;
+ int64_t first_bucket_time;
+ int bucket[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
+ int64_t min_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
+ int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
+ int success_count;
+ int failed_count;
+ int64_t total_time;
+ uint64_t enter_time;
+};
+
+static struct level_stats suspend_time_stats;
+
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct lpm_stats, cpu_stats);
+
+static uint64_t get_total_sleep_time(unsigned int cpu_id)
+{
+ struct lpm_stats *stats = &per_cpu(cpu_stats, cpu_id);
+ int i;
+ uint64_t ret = 0;
+
+ for (i = 0; i < stats->num_levels; i++)
+ ret += stats->time_stats[i].total_time;
+
+ return ret;
+}
+
+static void update_level_stats(struct level_stats *stats, uint64_t t,
+ bool success)
+{
+ uint64_t bt;
+ int i;
+
+ if (!success) {
+ stats->failed_count++;
+ return;
+ }
+
+ stats->success_count++;
+ stats->total_time += t;
+ bt = t;
+ do_div(bt, stats->first_bucket_time);
+
+ if (bt < 1ULL << (CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT *
+ (CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1)))
+ i = DIV_ROUND_UP(fls((uint32_t)bt),
+ CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT);
+ else
+ i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
+
+ if (i >= CONFIG_MSM_IDLE_STATS_BUCKET_COUNT)
+ i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
+
+ stats->bucket[i]++;
+
+ if (t < stats->min_time[i] || !stats->max_time[i])
+ stats->min_time[i] = t;
+ if (t > stats->max_time[i])
+ stats->max_time[i] = t;
+}
+
+static void level_stats_print(struct seq_file *m, struct level_stats *stats)
+{
+ int i = 0;
+ int64_t bucket_time = 0;
+ char seqs[MAX_STR_LEN] = {0};
+ int64_t s = stats->total_time;
+ uint32_t ns = do_div(s, NSEC_PER_SEC);
+
+ snprintf(seqs, MAX_STR_LEN,
+ "[%s] %s:\n"
+ " success count: %7d\n"
+ " total success time: %lld.%09u\n",
+ stats->owner->name,
+ stats->name,
+ stats->success_count,
+ s, ns);
+ seq_puts(m, seqs);
+
+ if (stats->failed_count) {
+ snprintf(seqs, MAX_STR_LEN, " failed count: %7d\n",
+ stats->failed_count);
+ seq_puts(m, seqs);
+ }
+
+ bucket_time = stats->first_bucket_time;
+ for (i = 0;
+ i < CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
+ i++) {
+ s = bucket_time;
+ ns = do_div(s, NSEC_PER_SEC);
+ snprintf(seqs, MAX_STR_LEN,
+ "\t<%6lld.%09u: %7d (%lld-%lld)\n",
+ s, ns, stats->bucket[i],
+ stats->min_time[i],
+ stats->max_time[i]);
+ seq_puts(m, seqs);
+ bucket_time <<= CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT;
+ }
+ snprintf(seqs, MAX_STR_LEN,
+ "\t>=%5lld.%09u:%8d (%lld-%lld)\n",
+ s, ns, stats->bucket[i],
+ stats->min_time[i],
+ stats->max_time[i]);
+ seq_puts(m, seqs);
+}
+
+static int level_stats_file_show(struct seq_file *m, void *v)
+{
+ struct level_stats *stats = NULL;
+
+ if (!m->private)
+ return -EINVAL;
+
+ stats = (struct level_stats *) m->private;
+
+ level_stats_print(m, stats);
+
+ return 0;
+}
+
+static int level_stats_file_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, level_stats_file_show, inode->i_private);
+}
+
+static void level_stats_print_all(struct seq_file *m, struct lpm_stats *stats)
+{
+ struct list_head *centry = NULL;
+ struct lpm_stats *pos = NULL;
+ int i = 0;
+
+ for (i = 0; i < stats->num_levels; i++)
+ level_stats_print(m, &stats->time_stats[i]);
+
+ if (list_empty(&stats->child))
+ return;
+
+ centry = &stats->child;
+ list_for_each_entry(pos, centry, sibling) {
+ level_stats_print_all(m, pos);
+ }
+}
+
+static void level_stats_reset(struct level_stats *stats)
+{
+ memset(stats->bucket, 0, sizeof(stats->bucket));
+ memset(stats->min_time, 0, sizeof(stats->min_time));
+ memset(stats->max_time, 0, sizeof(stats->max_time));
+ stats->success_count = 0;
+ stats->failed_count = 0;
+ stats->total_time = 0;
+}
+
+static void level_stats_reset_all(struct lpm_stats *stats)
+{
+ struct list_head *centry = NULL;
+ struct lpm_stats *pos = NULL;
+ int i = 0;
+
+ for (i = 0; i < stats->num_levels; i++)
+ level_stats_reset(&stats->time_stats[i]);
+
+ if (list_empty(&stats->child))
+ return;
+
+ centry = &stats->child;
+ list_for_each_entry(pos, centry, sibling) {
+ level_stats_reset_all(pos);
+ }
+}
+
+static int lpm_stats_file_show(struct seq_file *m, void *v)
+{
+ struct lpm_stats *stats = (struct lpm_stats *)m->private;
+
+ if (!m->private) {
+ pr_err("%s: Invalid pdata, Cannot print stats\n", __func__);
+ return -EINVAL;
+ }
+
+ level_stats_print_all(m, stats);
+ level_stats_print(m, &suspend_time_stats);
+
+ return 0;
+}
+
+static int lpm_stats_file_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, lpm_stats_file_show, inode->i_private);
+}
+
+static ssize_t level_stats_file_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *off)
+{
+ char buf[MAX_STR_LEN] = {0};
+ struct inode *in = file->f_inode;
+ struct level_stats *stats = (struct level_stats *)in->i_private;
+ size_t len = strnlen(lpm_stats_reset, MAX_STR_LEN);
+
+ if (!stats)
+ return -EINVAL;
+
+ if (count != len+1)
+ return -EINVAL;
+
+ if (copy_from_user(buf, buffer, len))
+ return -EFAULT;
+
+ if (strcmp(buf, lpm_stats_reset))
+ return -EINVAL;
+
+ level_stats_reset(stats);
+
+ return count;
+}
+
+static ssize_t lpm_stats_file_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *off)
+{
+ char buf[MAX_STR_LEN] = {0};
+ struct inode *in = file->f_inode;
+ struct lpm_stats *stats = (struct lpm_stats *)in->i_private;
+ size_t len = strnlen(lpm_stats_reset, MAX_STR_LEN);
+
+ if (!stats)
+ return -EINVAL;
+
+ if (count != len+1)
+ return -EINVAL;
+
+ if (copy_from_user(buf, buffer, len))
+ return -EFAULT;
+
+ if (strcmp(buf, lpm_stats_reset))
+ return -EINVAL;
+
+ level_stats_reset_all(stats);
+
+ return count;
+}
+
+int lifo_stats_file_show(struct seq_file *m, void *v)
+{
+ struct lpm_stats *stats = NULL;
+ struct list_head *centry = NULL;
+ struct lpm_stats *pos = NULL;
+ char seqs[MAX_STR_LEN] = {0};
+
+ if (!m->private)
+ return -EINVAL;
+
+ stats = (struct lpm_stats *)m->private;
+
+ if (list_empty(&stats->child)) {
+ pr_err("%s: ERROR: Lifo level with no children.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ centry = &stats->child;
+ list_for_each_entry(pos, centry, sibling) {
+ snprintf(seqs, MAX_STR_LEN,
+ "%s:\n"
+ "\tLast-In:%u\n"
+ "\tFirst-Out:%u\n",
+ pos->name,
+ pos->lifo.last_in,
+ pos->lifo.first_out);
+ seq_puts(m, seqs);
+ }
+ return 0;
+}
+
+static int lifo_stats_file_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, lifo_stats_file_show, inode->i_private);
+}
+
+static void lifo_stats_reset_all(struct lpm_stats *stats)
+{
+ struct list_head *centry = NULL;
+ struct lpm_stats *pos = NULL;
+
+ centry = &stats->child;
+ list_for_each_entry(pos, centry, sibling) {
+ pos->lifo.last_in = 0;
+ pos->lifo.first_out = 0;
+ if (!list_empty(&pos->child))
+ lifo_stats_reset_all(pos);
+ }
+}
+
+static ssize_t lifo_stats_file_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *off)
+{
+ char buf[MAX_STR_LEN] = {0};
+ struct inode *in = file->f_inode;
+ struct lpm_stats *stats = (struct lpm_stats *)in->i_private;
+ size_t len = strnlen(lpm_stats_reset, MAX_STR_LEN);
+
+ if (!stats)
+ return -EINVAL;
+
+ if (count != len+1)
+ return -EINVAL;
+
+ if (copy_from_user(buf, buffer, len))
+ return -EFAULT;
+
+ if (strcmp(buf, lpm_stats_reset))
+ return -EINVAL;
+
+ lifo_stats_reset_all(stats);
+
+ return count;
+}
+
+static const struct file_operations level_stats_fops = {
+ .owner = THIS_MODULE,
+ .open = level_stats_file_open,
+ .read = seq_read,
+ .release = single_release,
+ .llseek = no_llseek,
+ .write = level_stats_file_write,
+};
+
+static const struct file_operations lpm_stats_fops = {
+ .owner = THIS_MODULE,
+ .open = lpm_stats_file_open,
+ .read = seq_read,
+ .release = single_release,
+ .llseek = no_llseek,
+ .write = lpm_stats_file_write,
+};
+
+static const struct file_operations lifo_stats_fops = {
+ .owner = THIS_MODULE,
+ .open = lifo_stats_file_open,
+ .read = seq_read,
+ .release = single_release,
+ .llseek = no_llseek,
+ .write = lifo_stats_file_write,
+};
+
+static void update_last_in_stats(struct lpm_stats *stats)
+{
+ struct list_head *centry = NULL;
+ struct lpm_stats *pos = NULL;
+
+ if (list_empty(&stats->child))
+ return;
+
+ centry = &stats->child;
+ list_for_each_entry(pos, centry, sibling) {
+ if (cpumask_test_cpu(smp_processor_id(), &pos->mask)) {
+ pos->lifo.last_in++;
+ return;
+ }
+ }
+ WARN(1, "Should not reach here\n");
+}
+
+static void update_first_out_stats(struct lpm_stats *stats)
+{
+ struct list_head *centry = NULL;
+ struct lpm_stats *pos = NULL;
+
+ if (list_empty(&stats->child))
+ return;
+
+ centry = &stats->child;
+ list_for_each_entry(pos, centry, sibling) {
+ if (cpumask_test_cpu(smp_processor_id(), &pos->mask)) {
+ pos->lifo.first_out++;
+ return;
+ }
+ }
+ WARN(1, "Should not reach here\n");
+}
+
+static inline void update_exit_stats(struct lpm_stats *stats, uint32_t index,
+ bool success)
+{
+ uint64_t exit_time = 0;
+
+ /* Update time stats only when exit is preceded by enter */
+ exit_time = stats->sleep_time;
+ update_level_stats(&stats->time_stats[index], exit_time,
+ success);
+}
+
+static int config_level(const char *name, const char **levels,
+ int num_levels, struct lpm_stats *parent, struct lpm_stats *stats)
+{
+ int i = 0;
+ struct dentry *directory = NULL;
+ const char *rootname = "lpm_stats";
+ const char *dirname = rootname;
+
+ strlcpy(stats->name, name, MAX_STR_LEN);
+ stats->num_levels = num_levels;
+ stats->parent = parent;
+ INIT_LIST_HEAD(&stats->sibling);
+ INIT_LIST_HEAD(&stats->child);
+
+ stats->time_stats = kcalloc(num_levels, sizeof(struct level_stats),
+ GFP_KERNEL);
+ if (!stats->time_stats)
+ return -ENOMEM;
+
+ if (parent) {
+ list_add_tail(&stats->sibling, &parent->child);
+ directory = parent->directory;
+ dirname = name;
+ }
+
+ stats->directory = debugfs_create_dir(dirname, directory);
+ if (!stats->directory) {
+ pr_err("%s: Unable to create %s debugfs directory\n",
+ __func__, dirname);
+ kfree(stats->time_stats);
+ return -EPERM;
+ }
+
+ for (i = 0; i < num_levels; i++) {
+ stats->time_stats[i].name = levels[i];
+ stats->time_stats[i].owner = stats;
+ stats->time_stats[i].first_bucket_time =
+ CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
+ stats->time_stats[i].enter_time = 0;
+
+ if (!debugfs_create_file(stats->time_stats[i].name, 0444,
+ stats->directory, (void *)&stats->time_stats[i],
+ &level_stats_fops)) {
+ pr_err("%s: Unable to create %s %s level-stats file\n",
+ __func__, stats->name,
+ stats->time_stats[i].name);
+ kfree(stats->time_stats);
+ return -EPERM;
+ }
+ }
+
+ if (!debugfs_create_file("stats", 0444, stats->directory,
+ (void *)stats, &lpm_stats_fops)) {
+ pr_err("%s: Unable to create %s's overall 'stats' file\n",
+ __func__, stats->name);
+ kfree(stats->time_stats);
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static ssize_t total_sleep_time_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct lpm_sleep_time *cpu_sleep_time = container_of(attr,
+ struct lpm_sleep_time, ts_attr);
+ unsigned int cpu = cpu_sleep_time->cpu;
+ uint64_t total_time = get_total_sleep_time(cpu);
+
+ return snprintf(buf, MAX_TIME_LEN, "%llu.%09u\n", total_time,
+ do_div(total_time, NSEC_PER_SEC));
+}
+
+static struct kobject *local_module_kobject(void)
+{
+ struct kobject *kobj;
+
+ kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
+
+ if (!kobj) {
+ int err;
+ struct module_kobject *mk;
+
+ mk = kzalloc(sizeof(*mk), GFP_KERNEL);
+ if (!mk)
+ return ERR_PTR(-ENOMEM);
+
+ mk->mod = THIS_MODULE;
+ mk->kobj.kset = module_kset;
+
+ err = kobject_init_and_add(&mk->kobj, &module_ktype, NULL,
+ "%s", KBUILD_MODNAME);
+
+ if (err) {
+ kobject_put(&mk->kobj);
+ kfree(mk);
+ pr_err("%s: cannot create kobject for %s\n",
+ __func__, KBUILD_MODNAME);
+ return ERR_PTR(err);
+ }
+
+ kobject_get(&mk->kobj);
+ kobj = &mk->kobj;
+ }
+
+ return kobj;
+}
+
+static int create_sysfs_node(unsigned int cpu, struct lpm_stats *stats)
+{
+ struct kobject *cpu_kobj = NULL;
+ struct lpm_sleep_time *ts = NULL;
+ struct kobject *stats_kobj;
+ char cpu_name[] = "cpuXX";
+ int ret = -ENOMEM;
+
+ stats_kobj = local_module_kobject();
+
+ if (IS_ERR_OR_NULL(stats_kobj))
+ return PTR_ERR(stats_kobj);
+
+ snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu);
+ cpu_kobj = kobject_create_and_add(cpu_name, stats_kobj);
+ if (!cpu_kobj)
+ return -ENOMEM;
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ goto failed;
+
+ sysfs_attr_init(&ts->ts_attr.attr);
+ ts->ts_attr.attr.name = "total_sleep_time_secs";
+ ts->ts_attr.attr.mode = 0444;
+ ts->ts_attr.show = total_sleep_time_show;
+ ts->ts_attr.store = NULL;
+ ts->cpu = cpu;
+
+ ret = sysfs_create_file(cpu_kobj, &ts->ts_attr.attr);
+ if (ret)
+ goto failed;
+
+ return 0;
+
+failed:
+ kfree(ts);
+ kobject_put(cpu_kobj);
+ return ret;
+}
+
+static struct lpm_stats *config_cpu_level(const char *name,
+ const char **levels, int num_levels, struct lpm_stats *parent,
+ struct cpumask *mask)
+{
+ int cpu = 0;
+ struct lpm_stats *pstats = NULL;
+ struct lpm_stats *stats = NULL;
+
+ for (pstats = parent; pstats; pstats = pstats->parent)
+ cpumask_or(&pstats->mask, &pstats->mask, mask);
+
+ for_each_cpu(cpu, mask) {
+ int ret = 0;
+ char cpu_name[MAX_STR_LEN] = {0};
+
+ stats = &per_cpu(cpu_stats, cpu);
+ snprintf(cpu_name, MAX_STR_LEN, "%s%d", name, cpu);
+ cpumask_set_cpu(cpu, &stats->mask);
+
+ stats->is_cpu = true;
+
+ ret = config_level(cpu_name, levels, num_levels, parent,
+ stats);
+ if (ret) {
+ pr_err("%s: Unable to create %s stats\n",
+ __func__, cpu_name);
+ return ERR_PTR(ret);
+ }
+
+ ret = create_sysfs_node(cpu, stats);
+
+ if (ret) {
+ pr_err("Could not create the sysfs node\n");
+ return ERR_PTR(ret);
+ }
+ }
+
+ return stats;
+}
+
+static void config_suspend_level(struct lpm_stats *stats)
+{
+ suspend_time_stats.name = lpm_stats_suspend;
+ suspend_time_stats.owner = stats;
+ suspend_time_stats.first_bucket_time =
+ CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET;
+ suspend_time_stats.enter_time = 0;
+ suspend_time_stats.success_count = 0;
+ suspend_time_stats.failed_count = 0;
+
+ if (!debugfs_create_file(suspend_time_stats.name, 0444,
+ stats->directory, (void *)&suspend_time_stats,
+ &level_stats_fops))
+ pr_err("%s: Unable to create %s Suspend stats file\n",
+ __func__, stats->name);
+}
+
+static struct lpm_stats *config_cluster_level(const char *name,
+ const char **levels, int num_levels, struct lpm_stats *parent)
+{
+ struct lpm_stats *stats = NULL;
+ int ret = 0;
+
+ stats = kzalloc(sizeof(struct lpm_stats), GFP_KERNEL);
+ if (!stats)
+ return ERR_PTR(-ENOMEM);
+
+ stats->is_cpu = false;
+
+ ret = config_level(name, levels, num_levels, parent, stats);
+ if (ret) {
+ pr_err("%s: Unable to create %s stats\n", __func__,
+ name);
+ kfree(stats);
+ return ERR_PTR(ret);
+ }
+
+ if (!debugfs_create_file("lifo", 0444, stats->directory,
+ (void *)stats, &lifo_stats_fops)) {
+ pr_err("%s: Unable to create %s lifo stats file\n",
+ __func__, stats->name);
+ kfree(stats);
+ return ERR_PTR(-EPERM);
+ }
+
+ if (!parent)
+ config_suspend_level(stats);
+
+ return stats;
+}
+
+static void cleanup_stats(struct lpm_stats *stats)
+{
+ struct list_head *centry = NULL;
+ struct lpm_stats *pos = NULL;
+
+ centry = &stats->child;
+ list_for_each_entry_reverse(pos, centry, sibling) {
+ if (!list_empty(&pos->child))
+ cleanup_stats(pos);
+
+ list_del_init(&pos->child);
+
+ kfree(pos->time_stats);
+ if (!pos->is_cpu)
+ kfree(pos);
+ }
+ kfree(stats->time_stats);
+ kfree(stats);
+}
+
+static void lpm_stats_cleanup(struct lpm_stats *stats)
+{
+ struct lpm_stats *pstats = stats;
+
+ if (!pstats)
+ return;
+
+ while (pstats->parent)
+ pstats = pstats->parent;
+
+ debugfs_remove_recursive(pstats->directory);
+
+ cleanup_stats(pstats);
+}
+
+/**
+ * lpm_stats_config_level() - API to configure levels stats.
+ *
+ * @name: Name of the cluster/cpu.
+ * @levels: Low power mode level names.
+ * @num_levels: Number of leves supported.
+ * @parent: Pointer to the parent's lpm_stats object.
+ * @mask: cpumask, if configuring cpu stats, else NULL.
+ *
+ * Function to communicate the low power mode levels supported by
+ * cpus or a cluster.
+ *
+ * Return: Pointer to the lpm_stats object or ERR_PTR(-ERRNO)
+ */
+struct lpm_stats *lpm_stats_config_level(const char *name,
+ const char **levels, int num_levels, struct lpm_stats *parent,
+ struct cpumask *mask)
+{
+ struct lpm_stats *stats = NULL;
+
+ if (!levels || num_levels <= 0 || IS_ERR(parent)) {
+ pr_err("%s: Invalid input\n\t\tlevels = %p\n\t\t"
+ "num_levels = %d\n\t\tparent = %ld\n",
+ __func__, levels, num_levels, PTR_ERR(parent));
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (mask)
+ stats = config_cpu_level(name, levels, num_levels, parent,
+ mask);
+ else
+ stats = config_cluster_level(name, levels, num_levels,
+ parent);
+
+ if (IS_ERR(stats)) {
+ lpm_stats_cleanup(parent);
+ return stats;
+ }
+
+ return stats;
+}
+EXPORT_SYMBOL(lpm_stats_config_level);
+
+/**
+ * lpm_stats_cluster_enter() - API to communicate the lpm level a cluster
+ * is prepared to enter.
+ *
+ * @stats: Pointer to the cluster's lpm_stats object.
+ * @index: Index of the lpm level that the cluster is going to enter.
+ *
+ * Function to communicate the low power mode level that the cluster is
+ * prepared to enter.
+ */
+void lpm_stats_cluster_enter(struct lpm_stats *stats, uint32_t index)
+{
+ if (IS_ERR_OR_NULL(stats))
+ return;
+
+ update_last_in_stats(stats);
+}
+EXPORT_SYMBOL(lpm_stats_cluster_enter);
+
+/**
+ * lpm_stats_cluster_exit() - API to communicate the lpm level a cluster
+ * exited.
+ *
+ * @stats: Pointer to the cluster's lpm_stats object.
+ * @index: Index of the cluster lpm level.
+ * @success: Success/Failure of the low power mode execution.
+ *
+ * Function to communicate the low power mode level that the cluster
+ * exited.
+ */
+void lpm_stats_cluster_exit(struct lpm_stats *stats, uint32_t index,
+ bool success)
+{
+ if (IS_ERR_OR_NULL(stats))
+ return;
+
+ update_exit_stats(stats, index, success);
+
+ update_first_out_stats(stats);
+}
+EXPORT_SYMBOL(lpm_stats_cluster_exit);
+
+/**
+ * lpm_stats_cpu_enter() - API to communicate the lpm level a cpu
+ * is prepared to enter.
+ *
+ * @index: cpu's lpm level index.
+ *
+ * Function to communicate the low power mode level that the cpu is
+ * prepared to enter.
+ */
+void lpm_stats_cpu_enter(uint32_t index, uint64_t time)
+{
+ struct lpm_stats *stats = &(*this_cpu_ptr(&(cpu_stats)));
+
+ stats->sleep_time = time;
+
+ if (!stats->time_stats)
+ return;
+
+}
+EXPORT_SYMBOL(lpm_stats_cpu_enter);
+
+/**
+ * lpm_stats_cpu_exit() - API to communicate the lpm level that the cpu exited.
+ *
+ * @index: cpu's lpm level index.
+ * @success: Success/Failure of the low power mode execution.
+ *
+ * Function to communicate the low power mode level that the cpu exited.
+ */
+void lpm_stats_cpu_exit(uint32_t index, uint64_t time, bool success)
+{
+ struct lpm_stats *stats = &(*this_cpu_ptr(&(cpu_stats)));
+
+ if (!stats->time_stats)
+ return;
+
+ stats->sleep_time = time - stats->sleep_time;
+
+ update_exit_stats(stats, index, success);
+}
+EXPORT_SYMBOL(lpm_stats_cpu_exit);
+
+/**
+ * lpm_stats_suspend_enter() - API to communicate system entering suspend.
+ *
+ * Function to communicate that the system is ready to enter suspend.
+ */
+void lpm_stats_suspend_enter(void)
+{
+ struct timespec ts;
+
+ getnstimeofday(&ts);
+ suspend_time_stats.enter_time = timespec_to_ns(&ts);
+}
+EXPORT_SYMBOL(lpm_stats_suspend_enter);
+
+/**
+ * lpm_stats_suspend_exit() - API to communicate system exiting suspend.
+ *
+ * Function to communicate that the system exited suspend.
+ */
+void lpm_stats_suspend_exit(void)
+{
+ struct timespec ts;
+ uint64_t exit_time = 0;
+
+ getnstimeofday(&ts);
+ exit_time = timespec_to_ns(&ts) - suspend_time_stats.enter_time;
+ update_level_stats(&suspend_time_stats, exit_time, true);
+}
+EXPORT_SYMBOL(lpm_stats_suspend_exit);
diff --git a/drivers/soc/qcom/msm-core.c b/drivers/soc/qcom/msm-core.c
new file mode 100644
index 0000000..fa3ba1d
--- /dev/null
+++ b/drivers/soc/qcom/msm-core.c
@@ -0,0 +1,1129 @@
+/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/cpu.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kthread.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/msm-core-interface.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pm_opp.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include <linux/thermal.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/uio_driver.h>
+#include <asm/smp_plat.h>
+#include <stdbool.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/trace_msm_core.h>
+
+#define TEMP_BASE_POINT 35
+#define TEMP_MAX_POINT 95
+#define CPU_HOTPLUG_LIMIT 80
+#define CPU_BIT_MASK(cpu) BIT(cpu)
+#define DEFAULT_TEMP 40
+#define DEFAULT_LOW_HYST_TEMP 10
+#define DEFAULT_HIGH_HYST_TEMP 5
+#define CLUSTER_OFFSET_FOR_MPIDR 8
+#define MAX_CORES_PER_CLUSTER 4
+#define MAX_NUM_OF_CLUSTERS 2
+#define NUM_OF_CORNERS 10
+#define DEFAULT_SCALING_FACTOR 1
+
+#define ALLOCATE_2D_ARRAY(type) (\
+static type **allocate_2d_array_##type(int idx)\
+{\
+ int i;\
+ type **ptr = NULL;\
+ if (!idx) \
+ return ERR_PTR(-EINVAL);\
+ ptr = kzalloc(sizeof(*ptr) * TEMP_DATA_POINTS, \
+ GFP_KERNEL);\
+ if (!ptr) { \
+ return ERR_PTR(-ENOMEM); \
+ } \
+ for (i = 0; i < TEMP_DATA_POINTS; i++) { \
+ ptr[i] = kzalloc(sizeof(*ptr[i]) * \
+ idx, GFP_KERNEL);\
+ if (!ptr[i]) {\
+ goto done;\
+ } \
+ } \
+ return ptr;\
+done:\
+ for (i = 0; i < TEMP_DATA_POINTS; i++) \
+ kfree(ptr[i]);\
+ kfree(ptr);\
+ return ERR_PTR(-ENOMEM);\
+})
+
+struct cpu_activity_info {
+ int cpu;
+ int mpidr;
+ long temp;
+ int sensor_id;
+ struct sensor_threshold hi_threshold;
+ struct sensor_threshold low_threshold;
+ struct cpu_static_info *sp;
+};
+
+struct cpu_static_info {
+ uint32_t **power;
+ cpumask_t mask;
+ struct cpufreq_frequency_table *table;
+ uint32_t *voltage;
+ uint32_t num_of_freqs;
+};
+
+static DEFINE_MUTEX(policy_update_mutex);
+static DEFINE_MUTEX(kthread_update_mutex);
+static DEFINE_SPINLOCK(update_lock);
+static struct delayed_work sampling_work;
+static struct completion sampling_completion;
+static struct task_struct *sampling_task;
+static int low_hyst_temp;
+static int high_hyst_temp;
+static struct platform_device *msm_core_pdev;
+static struct cpu_activity_info activity[NR_CPUS];
+DEFINE_PER_CPU(struct cpu_pstate_pwr *, ptable);
+static struct cpu_pwr_stats cpu_stats[NR_CPUS];
+static uint32_t scaling_factor;
+ALLOCATE_2D_ARRAY(uint32_t);
+
+static int poll_ms;
+module_param_named(polling_interval, poll_ms, int, 0664);
+
+static int disabled;
+module_param_named(disabled, disabled, int, 0664);
+
+static bool in_suspend;
+static bool activate_power_table;
+static int max_throttling_temp = 80; /* in C */
+module_param_named(throttling_temp, max_throttling_temp, int, 0664);
+
+/*
+ * Cannot be called from an interrupt context
+ */
+static void set_and_activate_threshold(uint32_t sensor_id,
+ struct sensor_threshold *threshold)
+{
+ if (sensor_set_trip(sensor_id, threshold)) {
+ pr_err("%s: Error in setting trip %d\n",
+ KBUILD_MODNAME, threshold->trip);
+ return;
+ }
+
+ if (sensor_activate_trip(sensor_id, threshold, true)) {
+ sensor_cancel_trip(sensor_id, threshold);
+ pr_err("%s: Error in enabling trip %d\n",
+ KBUILD_MODNAME, threshold->trip);
+ return;
+ }
+}
+
+static void set_threshold(struct cpu_activity_info *cpu_node)
+{
+ if (cpu_node->sensor_id < 0)
+ return;
+
+ /*
+ * Before operating on the threshold structure which is used by
+ * thermal core ensure that the sensor is disabled to prevent
+ * incorrect operations on the sensor list maintained by thermal code.
+ */
+ sensor_activate_trip(cpu_node->sensor_id,
+ &cpu_node->hi_threshold, false);
+ sensor_activate_trip(cpu_node->sensor_id,
+ &cpu_node->low_threshold, false);
+
+ cpu_node->hi_threshold.temp = (cpu_node->temp + high_hyst_temp) *
+ scaling_factor;
+ cpu_node->low_threshold.temp = (cpu_node->temp - low_hyst_temp) *
+ scaling_factor;
+
+ /*
+ * Set the threshold only if we are below the hotplug limit
+ * Adding more work at this high temperature range, seems to
+ * fail hotplug notifications.
+ */
+ if (cpu_node->hi_threshold.temp < (CPU_HOTPLUG_LIMIT * scaling_factor))
+ set_and_activate_threshold(cpu_node->sensor_id,
+ &cpu_node->hi_threshold);
+
+ set_and_activate_threshold(cpu_node->sensor_id,
+ &cpu_node->low_threshold);
+}
+
+static void samplequeue_handle(struct work_struct *work)
+{
+ complete(&sampling_completion);
+}
+
+/* May be called from an interrupt context */
+static void core_temp_notify(enum thermal_trip_type type,
+ int temp, void *data)
+{
+ struct cpu_activity_info *cpu_node =
+ (struct cpu_activity_info *) data;
+
+ trace_temp_notification(cpu_node->sensor_id,
+ type, temp, cpu_node->temp);
+
+ cpu_node->temp = temp / scaling_factor;
+
+ complete(&sampling_completion);
+}
+
+static void repopulate_stats(int cpu)
+{
+ int i;
+ struct cpu_activity_info *cpu_node = &activity[cpu];
+ int temp_point;
+ struct cpu_pstate_pwr *pt = per_cpu(ptable, cpu);
+
+ if (!pt)
+ return;
+
+ if (cpu_node->temp < TEMP_BASE_POINT)
+ temp_point = 0;
+ else if (cpu_node->temp > TEMP_MAX_POINT)
+ temp_point = TEMP_DATA_POINTS - 1;
+ else
+ temp_point = (cpu_node->temp - TEMP_BASE_POINT) / 5;
+
+ cpu_stats[cpu].temp = cpu_node->temp;
+ for (i = 0; i < cpu_node->sp->num_of_freqs; i++)
+ pt[i].power = cpu_node->sp->power[temp_point][i];
+
+ trace_cpu_stats(cpu, cpu_stats[cpu].temp, pt[0].power,
+ pt[cpu_node->sp->num_of_freqs-1].power);
+};
+
+void trigger_cpu_pwr_stats_calc(void)
+{
+ int cpu;
+ static long prev_temp[NR_CPUS];
+ struct cpu_activity_info *cpu_node;
+ int temp;
+
+ if (disabled)
+ return;
+
+ spin_lock(&update_lock);
+
+ for_each_online_cpu(cpu) {
+ cpu_node = &activity[cpu];
+ if (cpu_node->sensor_id < 0)
+ continue;
+
+ if (cpu_node->temp == prev_temp[cpu]) {
+ sensor_get_temp(cpu_node->sensor_id, &temp);
+ cpu_node->temp = temp / scaling_factor;
+ }
+
+ prev_temp[cpu] = cpu_node->temp;
+
+ /*
+ * Do not populate/update stats before policy and ptable have
+ * been updated.
+ */
+ if (activate_power_table && cpu_stats[cpu].ptable
+ && cpu_node->sp->table)
+ repopulate_stats(cpu);
+ }
+ spin_unlock(&update_lock);
+}
+EXPORT_SYMBOL(trigger_cpu_pwr_stats_calc);
+
+void set_cpu_throttled(cpumask_t *mask, bool throttling)
+{
+ int cpu;
+
+ if (!mask)
+ return;
+
+ spin_lock(&update_lock);
+ for_each_cpu(cpu, mask)
+ cpu_stats[cpu].throttling = throttling;
+ spin_unlock(&update_lock);
+}
+EXPORT_SYMBOL(set_cpu_throttled);
+
+static void update_related_freq_table(struct cpufreq_policy *policy)
+{
+ int cpu, num_of_freqs;
+ struct cpufreq_frequency_table *table;
+
+ table = cpufreq_frequency_get_table(policy->cpu);
+ if (!table) {
+ pr_err("Couldn't get freq table for cpu%d\n",
+ policy->cpu);
+ return;
+ }
+
+ for (num_of_freqs = 0; table[num_of_freqs].frequency !=
+ CPUFREQ_TABLE_END;)
+ num_of_freqs++;
+
+ /*
+ * Synchronous cores within cluster have the same
+ * policy. Since these cores do not have the cpufreq
+ * table initialized for all of them, copy the same
+ * table to all the related cpus.
+ */
+ for_each_cpu(cpu, policy->related_cpus) {
+ activity[cpu].sp->table = table;
+ activity[cpu].sp->num_of_freqs = num_of_freqs;
+ }
+}
+
+static __ref int do_sampling(void *data)
+{
+ int cpu;
+ struct cpu_activity_info *cpu_node;
+ static int prev_temp[NR_CPUS];
+
+ while (!kthread_should_stop()) {
+ wait_for_completion(&sampling_completion);
+ cancel_delayed_work(&sampling_work);
+
+ mutex_lock(&kthread_update_mutex);
+ if (in_suspend)
+ goto unlock;
+
+ trigger_cpu_pwr_stats_calc();
+
+ for_each_online_cpu(cpu) {
+ cpu_node = &activity[cpu];
+ if (prev_temp[cpu] != cpu_node->temp) {
+ prev_temp[cpu] = cpu_node->temp;
+ set_threshold(cpu_node);
+ trace_temp_threshold(cpu, cpu_node->temp,
+ cpu_node->hi_threshold.temp /
+ scaling_factor,
+ cpu_node->low_threshold.temp /
+ scaling_factor);
+ }
+ }
+ if (!poll_ms)
+ goto unlock;
+
+ schedule_delayed_work(&sampling_work,
+ msecs_to_jiffies(poll_ms));
+unlock:
+ mutex_unlock(&kthread_update_mutex);
+ }
+ return 0;
+}
+
+static void clear_static_power(struct cpu_static_info *sp)
+{
+ int i;
+
+ if (!sp)
+ return;
+
+ if (cpumask_first(&sp->mask) < num_possible_cpus())
+ return;
+
+ for (i = 0; i < TEMP_DATA_POINTS; i++)
+ kfree(sp->power[i]);
+ kfree(sp->power);
+ kfree(sp);
+}
+
+BLOCKING_NOTIFIER_HEAD(msm_core_stats_notifier_list);
+
+struct blocking_notifier_head *get_power_update_notifier(void)
+{
+ return &msm_core_stats_notifier_list;
+}
+
+int register_cpu_pwr_stats_ready_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&msm_core_stats_notifier_list,
+ nb);
+}
+
+static int update_userspace_power(struct sched_params __user *argp)
+{
+ int i;
+ int ret;
+ int cpu = -1;
+ struct cpu_activity_info *node;
+ struct cpu_static_info *sp, *clear_sp;
+ int cpumask, cluster, mpidr;
+ bool pdata_valid[NR_CPUS] = {0};
+
+ get_user(cpumask, &argp->cpumask);
+ get_user(cluster, &argp->cluster);
+ mpidr = cluster << 8;
+
+ pr_debug("%s: cpumask %d, cluster: %d\n", __func__, cpumask,
+ cluster);
+ for (i = 0; i < MAX_CORES_PER_CLUSTER; i++, cpumask >>= 1) {
+ if (!(cpumask & 0x01))
+ continue;
+
+ mpidr |= i;
+ for_each_possible_cpu(cpu) {
+ if (cpu_logical_map(cpu) == mpidr)
+ break;
+ }
+ }
+
+ if ((cpu < 0) || (cpu >= num_possible_cpus()))
+ return -EINVAL;
+
+ node = &activity[cpu];
+ /* Allocate new memory to copy cpumask specific power
+ * information.
+ */
+ sp = kzalloc(sizeof(*sp), GFP_KERNEL);
+ if (!sp)
+ return -ENOMEM;
+
+
+ sp->power = allocate_2d_array_uint32_t(node->sp->num_of_freqs);
+ if (IS_ERR_OR_NULL(sp->power)) {
+ ret = PTR_ERR(sp->power);
+ kfree(sp);
+ return ret;
+ }
+ sp->num_of_freqs = node->sp->num_of_freqs;
+ sp->voltage = node->sp->voltage;
+ sp->table = node->sp->table;
+
+ for (i = 0; i < TEMP_DATA_POINTS; i++) {
+ ret = copy_from_user(sp->power[i], &argp->power[i][0],
+ sizeof(sp->power[i][0]) * node->sp->num_of_freqs);
+ if (ret)
+ goto failed;
+ }
+
+ /* Copy the same power values for all the cpus in the cpumask
+ * argp->cpumask within the cluster (argp->cluster)
+ */
+ get_user(cpumask, &argp->cpumask);
+ spin_lock(&update_lock);
+ for (i = 0; i < MAX_CORES_PER_CLUSTER; i++, cpumask >>= 1) {
+ if (!(cpumask & 0x01))
+ continue;
+ mpidr = (cluster << CLUSTER_OFFSET_FOR_MPIDR);
+ mpidr |= i;
+ for_each_possible_cpu(cpu) {
+ if (!(cpu_logical_map(cpu) == mpidr))
+ continue;
+
+ node = &activity[cpu];
+ clear_sp = node->sp;
+ node->sp = sp;
+ cpumask_set_cpu(cpu, &sp->mask);
+ if (clear_sp) {
+ cpumask_clear_cpu(cpu, &clear_sp->mask);
+ clear_static_power(clear_sp);
+ }
+ cpu_stats[cpu].ptable = per_cpu(ptable, cpu);
+ repopulate_stats(cpu);
+ pdata_valid[cpu] = true;
+ }
+ }
+ spin_unlock(&update_lock);
+
+ for_each_possible_cpu(cpu) {
+ if (!pdata_valid[cpu])
+ continue;
+
+ blocking_notifier_call_chain(
+ &msm_core_stats_notifier_list, cpu, NULL);
+ }
+
+ activate_power_table = true;
+ return 0;
+
+failed:
+ for (i = 0; i < TEMP_DATA_POINTS; i++)
+ kfree(sp->power[i]);
+ kfree(sp->power);
+ kfree(sp);
+ return ret;
+}
+
+static long msm_core_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ long ret = 0;
+ struct cpu_activity_info *node = NULL;
+ struct sched_params __user *argp = (struct sched_params __user *)arg;
+ int i, cpu = num_possible_cpus();
+ int mpidr, cluster, cpumask;
+
+ if (!argp)
+ return -EINVAL;
+
+ get_user(cluster, &argp->cluster);
+ mpidr = (argp->cluster << (MAX_CORES_PER_CLUSTER *
+ MAX_NUM_OF_CLUSTERS));
+ cpumask = argp->cpumask;
+
+ switch (cmd) {
+ case EA_LEAKAGE:
+ ret = update_userspace_power(argp);
+ if (ret)
+ pr_err("Userspace power update failed with %ld\n", ret);
+ break;
+ case EA_VOLT:
+ for (i = 0; cpumask > 0; i++, cpumask >>= 1) {
+ for_each_possible_cpu(cpu) {
+ if (cpu_logical_map(cpu) == (mpidr | i))
+ break;
+ }
+ }
+ if (cpu >= num_possible_cpus())
+ break;
+
+ mutex_lock(&policy_update_mutex);
+ node = &activity[cpu];
+ if (!node->sp->table) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+ ret = copy_to_user((void __user *)&argp->voltage[0],
+ node->sp->voltage,
+ sizeof(uint32_t) * node->sp->num_of_freqs);
+ if (ret)
+ break;
+ for (i = 0; i < node->sp->num_of_freqs; i++) {
+ ret = copy_to_user((void __user *)&argp->freq[i],
+ &node->sp->table[i].frequency,
+ sizeof(uint32_t));
+ if (ret)
+ break;
+ }
+unlock:
+ mutex_unlock(&policy_update_mutex);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long msm_core_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ arg = (unsigned long)compat_ptr(arg);
+ return msm_core_ioctl(file, cmd, arg);
+}
+#endif
+
+static int msm_core_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int msm_core_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static inline void init_sens_threshold(struct sensor_threshold *threshold,
+ enum thermal_trip_type trip, long temp,
+ void *data)
+{
+ threshold->trip = trip;
+ threshold->temp = temp;
+ threshold->data = data;
+ threshold->notify = (void *)core_temp_notify;
+}
+
+static int msm_core_stats_init(struct device *dev, int cpu)
+{
+ int i;
+ struct cpu_activity_info *cpu_node;
+ struct cpu_pstate_pwr *pstate = NULL;
+
+ cpu_node = &activity[cpu];
+ cpu_stats[cpu].cpu = cpu;
+ cpu_stats[cpu].temp = cpu_node->temp;
+ cpu_stats[cpu].throttling = false;
+
+ cpu_stats[cpu].len = cpu_node->sp->num_of_freqs;
+ pstate = devm_kzalloc(dev,
+ sizeof(*pstate) * cpu_node->sp->num_of_freqs,
+ GFP_KERNEL);
+ if (!pstate)
+ return -ENOMEM;
+
+ for (i = 0; i < cpu_node->sp->num_of_freqs; i++)
+ pstate[i].freq = cpu_node->sp->table[i].frequency;
+
+ per_cpu(ptable, cpu) = pstate;
+
+ return 0;
+}
+
+static int msm_core_task_init(struct device *dev)
+{
+ init_completion(&sampling_completion);
+ sampling_task = kthread_run(do_sampling, NULL, "msm-core:sampling");
+ if (IS_ERR(sampling_task)) {
+ pr_err("Failed to create do_sampling err: %ld\n",
+ PTR_ERR(sampling_task));
+ return PTR_ERR(sampling_task);
+ }
+ return 0;
+}
+
+struct cpu_pwr_stats *get_cpu_pwr_stats(void)
+{
+ return cpu_stats;
+}
+EXPORT_SYMBOL(get_cpu_pwr_stats);
+
+static int msm_get_power_values(int cpu, struct cpu_static_info *sp)
+{
+ int i = 0, j;
+ int ret = 0;
+ uint64_t power;
+
+ /* Calculate dynamic power spent for every frequency using formula:
+ * Power = V * V * f
+ * where V = voltage for frequency
+ * f = frequency
+ */
+ sp->power = allocate_2d_array_uint32_t(sp->num_of_freqs);
+ if (IS_ERR_OR_NULL(sp->power))
+ return PTR_ERR(sp->power);
+
+ for (i = 0; i < TEMP_DATA_POINTS; i++) {
+ for (j = 0; j < sp->num_of_freqs; j++) {
+ power = sp->voltage[j] *
+ sp->table[j].frequency;
+ do_div(power, 1000);
+ do_div(power, 1000);
+ power *= sp->voltage[j];
+ do_div(power, 1000);
+ sp->power[i][j] = power;
+ }
+ }
+ return ret;
+}
+
+static int msm_get_voltage_levels(struct device *dev, int cpu,
+ struct cpu_static_info *sp)
+{
+ unsigned int *voltage;
+ int i;
+ int corner;
+ struct dev_pm_opp *opp;
+ struct device *cpu_dev = get_cpu_device(cpu);
+ /*
+ * Convert cpr corner voltage to average voltage of both
+ * a53 and a57 votlage value
+ */
+ int average_voltage[NUM_OF_CORNERS] = {0, 746, 841, 843, 940, 953, 976,
+ 1024, 1090, 1100};
+
+ if (!cpu_dev)
+ return -ENODEV;
+
+ voltage = devm_kzalloc(dev,
+ sizeof(*voltage) * sp->num_of_freqs, GFP_KERNEL);
+
+ if (!voltage)
+ return -ENOMEM;
+
+ rcu_read_lock();
+ for (i = 0; i < sp->num_of_freqs; i++) {
+ opp = dev_pm_opp_find_freq_exact(cpu_dev,
+ sp->table[i].frequency * 1000, true);
+ corner = dev_pm_opp_get_voltage(opp);
+
+ if (corner > 400000)
+ voltage[i] = corner / 1000;
+ else if (corner > 0 && corner < ARRAY_SIZE(average_voltage))
+ voltage[i] = average_voltage[corner];
+ else
+ voltage[i]
+ = average_voltage[ARRAY_SIZE(average_voltage) - 1];
+ }
+ rcu_read_unlock();
+
+ sp->voltage = voltage;
+ return 0;
+}
+
+static int msm_core_dyn_pwr_init(struct platform_device *pdev,
+ int cpu)
+{
+ int ret = 0;
+
+ if (!activity[cpu].sp->table)
+ return 0;
+
+ ret = msm_get_voltage_levels(&pdev->dev, cpu, activity[cpu].sp);
+ if (ret)
+ return ret;
+
+ ret = msm_get_power_values(cpu, activity[cpu].sp);
+
+ return ret;
+}
+
+static int msm_core_tsens_init(struct device_node *node, int cpu)
+{
+ int ret = 0;
+ char *key = NULL;
+ struct device_node *phandle;
+ const char *sensor_type = NULL;
+ struct cpu_activity_info *cpu_node = &activity[cpu];
+ int temp;
+
+ if (!node)
+ return -ENODEV;
+
+ key = "sensor";
+ phandle = of_parse_phandle(node, key, 0);
+ if (!phandle) {
+ pr_info("%s: No sensor mapping found for the core\n",
+ __func__);
+ /* Do not treat this as error as some targets might have
+ * temperature notification only in userspace.
+ * Use default temperature for the core. Userspace might
+ * update the temperature once it is up.
+ */
+ cpu_node->sensor_id = -ENODEV;
+ cpu_node->temp = DEFAULT_TEMP;
+ return 0;
+ }
+
+ key = "qcom,sensor-name";
+ ret = of_property_read_string(phandle, key,
+ &sensor_type);
+ if (ret) {
+ pr_err("%s: Cannot read tsens id\n", __func__);
+ return ret;
+ }
+
+ cpu_node->sensor_id = sensor_get_id((char *)sensor_type);
+ if (cpu_node->sensor_id < 0)
+ return cpu_node->sensor_id;
+
+ key = "qcom,scaling-factor";
+ ret = of_property_read_u32(phandle, key,
+ &scaling_factor);
+ if (ret) {
+ pr_info("%s: Cannot read tsens scaling factor\n", __func__);
+ scaling_factor = DEFAULT_SCALING_FACTOR;
+ }
+
+ ret = sensor_get_temp(cpu_node->sensor_id, &temp);
+ if (ret)
+ return ret;
+
+ cpu_node->temp = temp / scaling_factor;
+
+ init_sens_threshold(&cpu_node->hi_threshold,
+ THERMAL_TRIP_CONFIGURABLE_HI,
+ (cpu_node->temp + high_hyst_temp) * scaling_factor,
+ (void *)cpu_node);
+ init_sens_threshold(&cpu_node->low_threshold,
+ THERMAL_TRIP_CONFIGURABLE_LOW,
+ (cpu_node->temp - low_hyst_temp) * scaling_factor,
+ (void *)cpu_node);
+
+ return ret;
+}
+
+static int msm_core_mpidr_init(struct device_node *phandle)
+{
+ int ret = 0;
+ char *key = NULL;
+ int mpidr;
+
+ key = "reg";
+ ret = of_property_read_u32(phandle, key,
+ &mpidr);
+ if (ret) {
+ pr_err("%s: Cannot read mpidr\n", __func__);
+ return ret;
+ }
+ return mpidr;
+}
+
+static int msm_core_cpu_policy_handler(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ struct cpufreq_policy *policy = data;
+ struct cpu_activity_info *cpu_info = &activity[policy->cpu];
+ int cpu;
+ int ret;
+
+ if (cpu_info->sp->table)
+ return NOTIFY_OK;
+
+ switch (val) {
+ case CPUFREQ_CREATE_POLICY:
+ mutex_lock(&policy_update_mutex);
+ update_related_freq_table(policy);
+
+ for_each_cpu(cpu, policy->related_cpus) {
+ ret = msm_core_dyn_pwr_init(msm_core_pdev, cpu);
+ if (ret)
+ pr_debug("voltage-pwr table update failed\n");
+
+ ret = msm_core_stats_init(&msm_core_pdev->dev, cpu);
+ if (ret)
+ pr_debug("Stats table update failed\n");
+ }
+ mutex_unlock(&policy_update_mutex);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+struct notifier_block cpu_policy = {
+ .notifier_call = msm_core_cpu_policy_handler
+};
+
+static int system_suspend_handler(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ int cpu;
+
+ mutex_lock(&kthread_update_mutex);
+ switch (val) {
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ case PM_POST_RESTORE:
+ /*
+ * Set completion event to read temperature and repopulate
+ * stats
+ */
+ in_suspend = 0;
+ complete(&sampling_completion);
+ break;
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ /*
+ * cancel delayed work to be able to restart immediately
+ * after system resume
+ */
+ in_suspend = 1;
+ cancel_delayed_work(&sampling_work);
+ /*
+ * cancel TSENS interrupts as we do not want to wake up from
+ * suspend to take care of repopulate stats while the system is
+ * in suspend
+ */
+ for_each_possible_cpu(cpu) {
+ if (activity[cpu].sensor_id < 0)
+ continue;
+
+ sensor_activate_trip(activity[cpu].sensor_id,
+ &activity[cpu].hi_threshold, false);
+ sensor_activate_trip(activity[cpu].sensor_id,
+ &activity[cpu].low_threshold, false);
+ }
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&kthread_update_mutex);
+
+ return NOTIFY_OK;
+}
+
+static int msm_core_freq_init(void)
+{
+ int cpu;
+ struct cpufreq_policy *policy;
+
+ for_each_possible_cpu(cpu) {
+ activity[cpu].sp = kzalloc(sizeof(*(activity[cpu].sp)),
+ GFP_KERNEL);
+ if (!activity[cpu].sp)
+ return -ENOMEM;
+ }
+
+ for_each_online_cpu(cpu) {
+ if (activity[cpu].sp->table)
+ continue;
+
+ policy = cpufreq_cpu_get(cpu);
+ if (!policy)
+ continue;
+
+ update_related_freq_table(policy);
+ cpufreq_cpu_put(policy);
+ }
+
+ return 0;
+}
+
+static int msm_core_params_init(struct platform_device *pdev)
+{
+ int ret = 0;
+ unsigned long cpu = 0;
+ struct device_node *child_node = NULL;
+ struct device_node *ea_node = NULL;
+ char *key = NULL;
+ int mpidr;
+
+ for_each_possible_cpu(cpu) {
+ child_node = of_get_cpu_node(cpu, NULL);
+
+ if (!child_node)
+ continue;
+
+ mpidr = msm_core_mpidr_init(child_node);
+ if (mpidr < 0)
+ return mpidr;
+
+ if (cpu >= num_possible_cpus())
+ continue;
+
+ activity[cpu].mpidr = mpidr;
+
+ key = "qcom,ea";
+ ea_node = of_parse_phandle(child_node, key, 0);
+ if (!ea_node) {
+ pr_err("%s Couldn't find the ea_node for cpu%lu\n",
+ __func__, cpu);
+ return -ENODEV;
+ }
+
+ ret = msm_core_tsens_init(ea_node, cpu);
+ if (ret)
+ return ret;
+
+ if (!activity[cpu].sp->table)
+ continue;
+
+ ret = msm_core_dyn_pwr_init(msm_core_pdev, cpu);
+ if (ret)
+ pr_debug("voltage-pwr table update failed\n");
+
+ ret = msm_core_stats_init(&msm_core_pdev->dev, cpu);
+ if (ret)
+ pr_debug("Stats table update failed\n");
+ }
+
+ return 0;
+}
+
+static const struct file_operations msm_core_ops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = msm_core_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = msm_core_compat_ioctl,
+#endif
+ .open = msm_core_open,
+ .release = msm_core_release,
+};
+
+static struct miscdevice msm_core_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "pta",
+ .fops = &msm_core_ops
+};
+
+static void free_dyn_memory(void)
+{
+ int i, cpu;
+
+ for_each_possible_cpu(cpu) {
+ if (activity[cpu].sp) {
+ for (i = 0; i < TEMP_DATA_POINTS; i++) {
+ if (!activity[cpu].sp->power)
+ break;
+
+ kfree(activity[cpu].sp->power[i]);
+ }
+ }
+ kfree(activity[cpu].sp);
+ }
+}
+
+static int uio_init(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct uio_info *info = NULL;
+ struct resource *clnt_res = NULL;
+ u32 ea_mem_size = 0;
+ phys_addr_t ea_mem_pyhsical = 0;
+
+ clnt_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!clnt_res) {
+ pr_err("resource not found\n");
+ return -ENODEV;
+ }
+
+ info = devm_kzalloc(&pdev->dev, sizeof(struct uio_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ ea_mem_size = resource_size(clnt_res);
+ ea_mem_pyhsical = clnt_res->start;
+
+ if (ea_mem_size == 0) {
+ pr_err("msm-core: memory size is zero");
+ return -EINVAL;
+ }
+
+ /* Setup device */
+ info->name = clnt_res->name;
+ info->version = "1.0";
+ info->mem[0].addr = ea_mem_pyhsical;
+ info->mem[0].size = ea_mem_size;
+ info->mem[0].memtype = UIO_MEM_PHYS;
+
+ ret = uio_register_device(&pdev->dev, info);
+ if (ret) {
+ pr_err("uio register failed ret=%d", ret);
+ return ret;
+ }
+ dev_set_drvdata(&pdev->dev, info);
+
+ return 0;
+}
+
+static int msm_core_dev_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ char *key = NULL;
+ struct device_node *node;
+ int cpu;
+ struct uio_info *info;
+
+ if (!pdev)
+ return -ENODEV;
+
+ msm_core_pdev = pdev;
+ node = pdev->dev.of_node;
+ if (!node)
+ return -ENODEV;
+
+ key = "qcom,low-hyst-temp";
+ ret = of_property_read_u32(node, key, &low_hyst_temp);
+ if (ret)
+ low_hyst_temp = DEFAULT_LOW_HYST_TEMP;
+
+ key = "qcom,high-hyst-temp";
+ ret = of_property_read_u32(node, key, &high_hyst_temp);
+ if (ret)
+ high_hyst_temp = DEFAULT_HIGH_HYST_TEMP;
+
+ key = "qcom,polling-interval";
+ ret = of_property_read_u32(node, key, &poll_ms);
+ if (ret)
+ pr_info("msm-core initialized without polling period\n");
+
+ key = "qcom,throttling-temp";
+ ret = of_property_read_u32(node, key, &max_throttling_temp);
+
+ ret = uio_init(pdev);
+ if (ret)
+ return ret;
+
+ ret = msm_core_freq_init();
+ if (ret)
+ goto failed;
+
+ ret = misc_register(&msm_core_device);
+ if (ret) {
+ pr_err("%s: Error registering device %d\n", __func__, ret);
+ goto failed;
+ }
+
+ ret = msm_core_params_init(pdev);
+ if (ret)
+ goto failed;
+
+ ret = msm_core_task_init(&pdev->dev);
+ if (ret)
+ goto failed;
+
+ for_each_possible_cpu(cpu)
+ set_threshold(&activity[cpu]);
+
+ INIT_DEFERRABLE_WORK(&sampling_work, samplequeue_handle);
+ schedule_delayed_work(&sampling_work, msecs_to_jiffies(0));
+ cpufreq_register_notifier(&cpu_policy, CPUFREQ_POLICY_NOTIFIER);
+ pm_notifier(system_suspend_handler, 0);
+ return 0;
+failed:
+ info = dev_get_drvdata(&pdev->dev);
+ uio_unregister_device(info);
+ free_dyn_memory();
+ return ret;
+}
+
+static int msm_core_remove(struct platform_device *pdev)
+{
+ int cpu;
+ struct uio_info *info = dev_get_drvdata(&pdev->dev);
+
+ uio_unregister_device(info);
+
+ for_each_possible_cpu(cpu) {
+ if (activity[cpu].sensor_id < 0)
+ continue;
+
+ sensor_cancel_trip(activity[cpu].sensor_id,
+ &activity[cpu].hi_threshold);
+ sensor_cancel_trip(activity[cpu].sensor_id,
+ &activity[cpu].low_threshold);
+ }
+ free_dyn_memory();
+ misc_deregister(&msm_core_device);
+ return 0;
+}
+
+static const struct of_device_id msm_core_match_table[] = {
+ {.compatible = "qcom,apss-core-ea"},
+ {},
+};
+
+static struct platform_driver msm_core_driver = {
+ .probe = msm_core_dev_probe,
+ .driver = {
+ .name = "msm_core",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_core_match_table,
+ },
+ .remove = msm_core_remove,
+};
+
+static int __init msm_core_init(void)
+{
+ return platform_driver_register(&msm_core_driver);
+}
+late_initcall(msm_core_init);
diff --git a/drivers/soc/qcom/msm-spm.c b/drivers/soc/qcom/msm-spm.c
new file mode 100644
index 0000000..bffe3f3
--- /dev/null
+++ b/drivers/soc/qcom/msm-spm.c
@@ -0,0 +1,722 @@
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include "spm_driver.h"
+
+#define MSM_SPM_PMIC_STATE_IDLE 0
+
+enum {
+ MSM_SPM_DEBUG_SHADOW = 1U << 0,
+ MSM_SPM_DEBUG_VCTL = 1U << 1,
+};
+
+static int msm_spm_debug_mask;
+module_param_named(
+ debug_mask, msm_spm_debug_mask, int, 0664
+);
+
+struct saw2_data {
+ const char *ver_name;
+ uint32_t major;
+ uint32_t minor;
+ uint32_t *spm_reg_offset_ptr;
+};
+
+static uint32_t msm_spm_reg_offsets_saw2_v2_1[MSM_SPM_REG_NR] = {
+ [MSM_SPM_REG_SAW_SECURE] = 0x00,
+ [MSM_SPM_REG_SAW_ID] = 0x04,
+ [MSM_SPM_REG_SAW_CFG] = 0x08,
+ [MSM_SPM_REG_SAW_SPM_STS] = 0x0C,
+ [MSM_SPM_REG_SAW_AVS_STS] = 0x10,
+ [MSM_SPM_REG_SAW_PMIC_STS] = 0x14,
+ [MSM_SPM_REG_SAW_RST] = 0x18,
+ [MSM_SPM_REG_SAW_VCTL] = 0x1C,
+ [MSM_SPM_REG_SAW_AVS_CTL] = 0x20,
+ [MSM_SPM_REG_SAW_AVS_LIMIT] = 0x24,
+ [MSM_SPM_REG_SAW_AVS_DLY] = 0x28,
+ [MSM_SPM_REG_SAW_AVS_HYSTERESIS] = 0x2C,
+ [MSM_SPM_REG_SAW_SPM_CTL] = 0x30,
+ [MSM_SPM_REG_SAW_SPM_DLY] = 0x34,
+ [MSM_SPM_REG_SAW_PMIC_DATA_0] = 0x40,
+ [MSM_SPM_REG_SAW_PMIC_DATA_1] = 0x44,
+ [MSM_SPM_REG_SAW_PMIC_DATA_2] = 0x48,
+ [MSM_SPM_REG_SAW_PMIC_DATA_3] = 0x4C,
+ [MSM_SPM_REG_SAW_PMIC_DATA_4] = 0x50,
+ [MSM_SPM_REG_SAW_PMIC_DATA_5] = 0x54,
+ [MSM_SPM_REG_SAW_PMIC_DATA_6] = 0x58,
+ [MSM_SPM_REG_SAW_PMIC_DATA_7] = 0x5C,
+ [MSM_SPM_REG_SAW_SEQ_ENTRY] = 0x80,
+ [MSM_SPM_REG_SAW_VERSION] = 0xFD0,
+};
+
+static uint32_t msm_spm_reg_offsets_saw2_v3_0[MSM_SPM_REG_NR] = {
+ [MSM_SPM_REG_SAW_SECURE] = 0x00,
+ [MSM_SPM_REG_SAW_ID] = 0x04,
+ [MSM_SPM_REG_SAW_CFG] = 0x08,
+ [MSM_SPM_REG_SAW_SPM_STS] = 0x0C,
+ [MSM_SPM_REG_SAW_AVS_STS] = 0x10,
+ [MSM_SPM_REG_SAW_PMIC_STS] = 0x14,
+ [MSM_SPM_REG_SAW_RST] = 0x18,
+ [MSM_SPM_REG_SAW_VCTL] = 0x1C,
+ [MSM_SPM_REG_SAW_AVS_CTL] = 0x20,
+ [MSM_SPM_REG_SAW_AVS_LIMIT] = 0x24,
+ [MSM_SPM_REG_SAW_AVS_DLY] = 0x28,
+ [MSM_SPM_REG_SAW_AVS_HYSTERESIS] = 0x2C,
+ [MSM_SPM_REG_SAW_SPM_CTL] = 0x30,
+ [MSM_SPM_REG_SAW_SPM_DLY] = 0x34,
+ [MSM_SPM_REG_SAW_STS2] = 0x38,
+ [MSM_SPM_REG_SAW_PMIC_DATA_0] = 0x40,
+ [MSM_SPM_REG_SAW_PMIC_DATA_1] = 0x44,
+ [MSM_SPM_REG_SAW_PMIC_DATA_2] = 0x48,
+ [MSM_SPM_REG_SAW_PMIC_DATA_3] = 0x4C,
+ [MSM_SPM_REG_SAW_PMIC_DATA_4] = 0x50,
+ [MSM_SPM_REG_SAW_PMIC_DATA_5] = 0x54,
+ [MSM_SPM_REG_SAW_PMIC_DATA_6] = 0x58,
+ [MSM_SPM_REG_SAW_PMIC_DATA_7] = 0x5C,
+ [MSM_SPM_REG_SAW_SEQ_ENTRY] = 0x400,
+ [MSM_SPM_REG_SAW_VERSION] = 0xFD0,
+};
+
+static uint32_t msm_spm_reg_offsets_saw2_v4_1[MSM_SPM_REG_NR] = {
+ [MSM_SPM_REG_SAW_SECURE] = 0xC00,
+ [MSM_SPM_REG_SAW_ID] = 0xC04,
+ [MSM_SPM_REG_SAW_STS2] = 0xC10,
+ [MSM_SPM_REG_SAW_SPM_STS] = 0xC0C,
+ [MSM_SPM_REG_SAW_AVS_STS] = 0xC14,
+ [MSM_SPM_REG_SAW_PMIC_STS] = 0xC18,
+ [MSM_SPM_REG_SAW_RST] = 0xC1C,
+ [MSM_SPM_REG_SAW_VCTL] = 0x900,
+ [MSM_SPM_REG_SAW_AVS_CTL] = 0x904,
+ [MSM_SPM_REG_SAW_AVS_LIMIT] = 0x908,
+ [MSM_SPM_REG_SAW_AVS_DLY] = 0x90C,
+ [MSM_SPM_REG_SAW_SPM_CTL] = 0x0,
+ [MSM_SPM_REG_SAW_SPM_DLY] = 0x4,
+ [MSM_SPM_REG_SAW_CFG] = 0x0C,
+ [MSM_SPM_REG_SAW_PMIC_DATA_0] = 0x40,
+ [MSM_SPM_REG_SAW_PMIC_DATA_1] = 0x44,
+ [MSM_SPM_REG_SAW_PMIC_DATA_2] = 0x48,
+ [MSM_SPM_REG_SAW_PMIC_DATA_3] = 0x4C,
+ [MSM_SPM_REG_SAW_PMIC_DATA_4] = 0x50,
+ [MSM_SPM_REG_SAW_PMIC_DATA_5] = 0x54,
+ [MSM_SPM_REG_SAW_SEQ_ENTRY] = 0x400,
+ [MSM_SPM_REG_SAW_VERSION] = 0xFD0,
+};
+
+static struct saw2_data saw2_info[] = {
+ [0] = {
+ "SAW_v2.1",
+ 0x2,
+ 0x1,
+ msm_spm_reg_offsets_saw2_v2_1,
+ },
+ [1] = {
+ "SAW_v2.3",
+ 0x3,
+ 0x0,
+ msm_spm_reg_offsets_saw2_v3_0,
+ },
+ [2] = {
+ "SAW_v3.0",
+ 0x1,
+ 0x0,
+ msm_spm_reg_offsets_saw2_v3_0,
+ },
+ [3] = {
+ "SAW_v4.0",
+ 0x4,
+ 0x1,
+ msm_spm_reg_offsets_saw2_v4_1,
+ },
+};
+
+static uint32_t num_pmic_data;
+
+static void msm_spm_drv_flush_shadow(struct msm_spm_driver_data *dev,
+ unsigned int reg_index)
+{
+ if (!dev || !dev->reg_shadow)
+ return;
+
+ __raw_writel(dev->reg_shadow[reg_index],
+ dev->reg_base_addr + dev->reg_offsets[reg_index]);
+}
+
+static void msm_spm_drv_load_shadow(struct msm_spm_driver_data *dev,
+ unsigned int reg_index)
+{
+ dev->reg_shadow[reg_index] =
+ __raw_readl(dev->reg_base_addr +
+ dev->reg_offsets[reg_index]);
+}
+
+static inline uint32_t msm_spm_drv_get_num_spm_entry(
+ struct msm_spm_driver_data *dev)
+{
+ if (!dev)
+ return;
+
+ msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_ID);
+ return (dev->reg_shadow[MSM_SPM_REG_SAW_ID] >> 24) & 0xFF;
+}
+
+static inline void msm_spm_drv_set_start_addr(
+ struct msm_spm_driver_data *dev, uint32_t ctl)
+{
+ dev->reg_shadow[MSM_SPM_REG_SAW_SPM_CTL] = ctl;
+}
+
+static inline bool msm_spm_pmic_arb_present(struct msm_spm_driver_data *dev)
+{
+ msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_ID);
+ return (dev->reg_shadow[MSM_SPM_REG_SAW_ID] >> 2) & 0x1;
+}
+
+static inline void msm_spm_drv_set_vctl2(struct msm_spm_driver_data *dev,
+ uint32_t vlevel)
+{
+ unsigned int pmic_data = 0;
+
+ /**
+ * VCTL_PORT has to be 0, for PMIC_STS register to be updated.
+ * Ensure that vctl_port is always set to 0.
+ */
+ if (dev->vctl_port) {
+ WARN();
+ return;
+ }
+
+ pmic_data |= vlevel;
+ pmic_data |= (dev->vctl_port & 0x7) << 16;
+
+ dev->reg_shadow[MSM_SPM_REG_SAW_VCTL] &= ~0x700FF;
+ dev->reg_shadow[MSM_SPM_REG_SAW_VCTL] |= pmic_data;
+
+ dev->reg_shadow[MSM_SPM_REG_SAW_PMIC_DATA_3] &= ~0x700FF;
+ dev->reg_shadow[MSM_SPM_REG_SAW_PMIC_DATA_3] |= pmic_data;
+
+ msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_VCTL);
+ msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_PMIC_DATA_3);
+}
+
+static inline uint32_t msm_spm_drv_get_num_pmic_data(
+ struct msm_spm_driver_data *dev)
+{
+ msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_ID);
+ mb(); /* Ensure we flush */
+ return (dev->reg_shadow[MSM_SPM_REG_SAW_ID] >> 4) & 0x7;
+}
+
+static inline uint32_t msm_spm_drv_get_sts_pmic_state(
+ struct msm_spm_driver_data *dev)
+{
+ msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_PMIC_STS);
+ return (dev->reg_shadow[MSM_SPM_REG_SAW_PMIC_STS] >> 16) &
+ 0x03;
+}
+
+uint32_t msm_spm_drv_get_sts_curr_pmic_data(
+ struct msm_spm_driver_data *dev)
+{
+ msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_PMIC_STS);
+ return dev->reg_shadow[MSM_SPM_REG_SAW_PMIC_STS] & 0x300FF;
+}
+
+static inline void msm_spm_drv_get_saw2_ver(struct msm_spm_driver_data *dev,
+ uint32_t *major, uint32_t *minor)
+{
+ uint32_t val = 0;
+
+ dev->reg_shadow[MSM_SPM_REG_SAW_VERSION] =
+ __raw_readl(dev->reg_base_addr + dev->ver_reg);
+
+ val = dev->reg_shadow[MSM_SPM_REG_SAW_VERSION];
+
+ *major = (val >> 28) & 0xF;
+ *minor = (val >> 16) & 0xFFF;
+}
+
+inline int msm_spm_drv_set_spm_enable(
+ struct msm_spm_driver_data *dev, bool enable)
+{
+ uint32_t value = enable ? 0x01 : 0x00;
+
+ if (!dev)
+ return -EINVAL;
+
+ if ((dev->reg_shadow[MSM_SPM_REG_SAW_SPM_CTL] & 0x01) ^ value) {
+
+ dev->reg_shadow[MSM_SPM_REG_SAW_SPM_CTL] &= ~0x1;
+ dev->reg_shadow[MSM_SPM_REG_SAW_SPM_CTL] |= value;
+
+ msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_SPM_CTL);
+ wmb(); /* Ensure we flush */
+ }
+ return 0;
+}
+
+int msm_spm_drv_get_avs_enable(struct msm_spm_driver_data *dev)
+{
+ if (!dev)
+ return -EINVAL;
+
+ return dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] & 0x01;
+}
+
+int msm_spm_drv_set_avs_enable(struct msm_spm_driver_data *dev,
+ bool enable)
+{
+ uint32_t value = enable ? 0x1 : 0x0;
+
+ if (!dev)
+ return -EINVAL;
+
+ if ((dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] & 0x1) ^ value) {
+ dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] &= ~0x1;
+ dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] |= value;
+
+ msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
+ }
+
+ return 0;
+}
+
+int msm_spm_drv_set_avs_limit(struct msm_spm_driver_data *dev,
+ uint32_t min_lvl, uint32_t max_lvl)
+{
+ uint32_t value = (max_lvl & 0xff) << 16 | (min_lvl & 0xff);
+
+ if (!dev)
+ return -EINVAL;
+
+ dev->reg_shadow[MSM_SPM_REG_SAW_AVS_LIMIT] = value;
+
+ msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_AVS_LIMIT);
+
+ return 0;
+}
+
+static int msm_spm_drv_avs_irq_mask(enum msm_spm_avs_irq irq)
+{
+ switch (irq) {
+ case MSM_SPM_AVS_IRQ_MIN:
+ return BIT(1);
+ case MSM_SPM_AVS_IRQ_MAX:
+ return BIT(2);
+ default:
+ return -EINVAL;
+ }
+}
+
+int msm_spm_drv_set_avs_irq_enable(struct msm_spm_driver_data *dev,
+ enum msm_spm_avs_irq irq, bool enable)
+{
+ int mask = msm_spm_drv_avs_irq_mask(irq);
+ uint32_t value;
+
+ if (!dev)
+ return -EINVAL;
+ else if (mask < 0)
+ return mask;
+
+ value = enable ? mask : 0;
+
+ if ((dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] & mask) ^ value) {
+ dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] &= ~mask;
+ dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] |= value;
+ msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
+ }
+
+ return 0;
+}
+
+int msm_spm_drv_avs_clear_irq(struct msm_spm_driver_data *dev,
+ enum msm_spm_avs_irq irq)
+{
+ int mask = msm_spm_drv_avs_irq_mask(irq);
+
+ if (!dev)
+ return -EINVAL;
+ else if (mask < 0)
+ return mask;
+
+ if (dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] & mask) {
+ /*
+ * The interrupt status is cleared by disabling and then
+ * re-enabling the interrupt.
+ */
+ dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] &= ~mask;
+ msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
+ dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] |= mask;
+ msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
+ }
+
+ return 0;
+}
+
+void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev)
+{
+ int i;
+ int num_spm_entry = msm_spm_drv_get_num_spm_entry(dev);
+
+ if (!dev) {
+ __WARN();
+ return;
+ }
+
+ for (i = 0; i < num_spm_entry; i++) {
+ __raw_writel(dev->reg_seq_entry_shadow[i],
+ dev->reg_base_addr
+ + dev->reg_offsets[MSM_SPM_REG_SAW_SEQ_ENTRY]
+ + 4 * i);
+ }
+ mb(); /* Ensure we flush */
+}
+
+void dump_regs(struct msm_spm_driver_data *dev, int cpu)
+{
+ msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_SPM_STS);
+ mb(); /* Ensure we flush */
+ pr_err("CPU%d: spm register MSM_SPM_REG_SAW_SPM_STS: 0x%x\n", cpu,
+ dev->reg_shadow[MSM_SPM_REG_SAW_SPM_STS]);
+ msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_SPM_CTL);
+ mb(); /* Ensure we flush */
+ pr_err("CPU%d: spm register MSM_SPM_REG_SAW_SPM_CTL: 0x%x\n", cpu,
+ dev->reg_shadow[MSM_SPM_REG_SAW_SPM_CTL]);
+}
+
+int msm_spm_drv_write_seq_data(struct msm_spm_driver_data *dev,
+ uint8_t *cmd, uint32_t *offset)
+{
+ uint32_t cmd_w;
+ uint32_t offset_w = *offset / 4;
+ uint8_t last_cmd;
+
+ if (!cmd)
+ return -EINVAL;
+
+ while (1) {
+ int i;
+
+ cmd_w = 0;
+ last_cmd = 0;
+ cmd_w = dev->reg_seq_entry_shadow[offset_w];
+
+ for (i = (*offset % 4); i < 4; i++) {
+ last_cmd = *(cmd++);
+ cmd_w |= last_cmd << (i * 8);
+ (*offset)++;
+ if (last_cmd == 0x0f)
+ break;
+ }
+
+ dev->reg_seq_entry_shadow[offset_w++] = cmd_w;
+ if (last_cmd == 0x0f)
+ break;
+ }
+
+ return 0;
+}
+
+int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *dev,
+ uint32_t ctl)
+{
+
+ /* SPM is configured to reset start address to zero after end of Program
+ */
+ if (!dev)
+ return -EINVAL;
+
+ msm_spm_drv_set_start_addr(dev, ctl);
+
+ msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_SPM_CTL);
+ wmb(); /* Ensure we flush */
+
+ if (msm_spm_debug_mask & MSM_SPM_DEBUG_SHADOW) {
+ int i;
+
+ for (i = 0; i < MSM_SPM_REG_NR; i++)
+ pr_info("%s: reg %02x = 0x%08x\n", __func__,
+ dev->reg_offsets[i], dev->reg_shadow[i]);
+ }
+ msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_SPM_STS);
+
+ return 0;
+}
+
+uint32_t msm_spm_drv_get_vdd(struct msm_spm_driver_data *dev)
+{
+ msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_PMIC_STS);
+ return dev->reg_shadow[MSM_SPM_REG_SAW_PMIC_STS] & 0xFF;
+}
+
+#ifdef CONFIG_MSM_AVS_HW
+static bool msm_spm_drv_is_avs_enabled(struct msm_spm_driver_data *dev)
+{
+ msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
+ return dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] & BIT(0);
+}
+
+static void msm_spm_drv_disable_avs(struct msm_spm_driver_data *dev)
+{
+ msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
+ dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] &= ~BIT(27);
+ msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
+}
+
+static void msm_spm_drv_enable_avs(struct msm_spm_driver_data *dev)
+{
+ dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] |= BIT(27);
+ msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
+}
+
+static void msm_spm_drv_set_avs_vlevel(struct msm_spm_driver_data *dev,
+ unsigned int vlevel)
+{
+ vlevel &= 0x3f;
+ dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] &= ~0x7efc00;
+ dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] |= ((vlevel - 4) << 10);
+ dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] |= (vlevel << 17);
+ msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
+}
+
+#else
+static bool msm_spm_drv_is_avs_enabled(struct msm_spm_driver_data *dev)
+{
+ return false;
+}
+
+static void msm_spm_drv_disable_avs(struct msm_spm_driver_data *dev) { }
+
+static void msm_spm_drv_enable_avs(struct msm_spm_driver_data *dev) { }
+
+static void msm_spm_drv_set_avs_vlevel(struct msm_spm_driver_data *dev,
+ unsigned int vlevel) { }
+#endif
+
+int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev, unsigned int vlevel)
+{
+ uint32_t timeout_us, new_level;
+ bool avs_enabled;
+
+ if (!dev)
+ return -EINVAL;
+
+ avs_enabled = msm_spm_drv_is_avs_enabled(dev);
+
+ if (!msm_spm_pmic_arb_present(dev))
+ return -ENODEV;
+
+ if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
+ pr_info("%s: requesting vlevel %#x\n", __func__, vlevel);
+
+ if (avs_enabled)
+ msm_spm_drv_disable_avs(dev);
+
+ /* Kick the state machine back to idle */
+ dev->reg_shadow[MSM_SPM_REG_SAW_RST] = 1;
+ msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_RST);
+
+ msm_spm_drv_set_vctl2(dev, vlevel);
+
+ timeout_us = dev->vctl_timeout_us;
+ /* Confirm the voltage we set was what hardware sent */
+ do {
+ udelay(1);
+ new_level = msm_spm_drv_get_sts_curr_pmic_data(dev);
+ /* FSM is idle */
+ if (((new_level & 0x30000) == 0) &&
+ ((new_level & 0xFF) == vlevel))
+ break;
+ } while (--timeout_us);
+ if (!timeout_us) {
+ pr_info("Wrong level %#x\n", new_level);
+ goto set_vdd_bail;
+ }
+
+ if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
+ pr_info("%s: done, remaining timeout %u us\n",
+ __func__, timeout_us);
+
+ /* Set AVS min/max */
+ if (avs_enabled) {
+ msm_spm_drv_set_avs_vlevel(dev, vlevel);
+ msm_spm_drv_enable_avs(dev);
+ }
+
+ return 0;
+
+set_vdd_bail:
+ if (avs_enabled)
+ msm_spm_drv_enable_avs(dev);
+
+ pr_err("%s: failed %#x, remaining timeout %uus, vlevel %#x\n",
+ __func__, vlevel, timeout_us, new_level);
+ return -EIO;
+}
+
+static int msm_spm_drv_get_pmic_port(struct msm_spm_driver_data *dev,
+ enum msm_spm_pmic_port port)
+{
+ int index = -1;
+
+ switch (port) {
+ case MSM_SPM_PMIC_VCTL_PORT:
+ index = dev->vctl_port;
+ break;
+ case MSM_SPM_PMIC_PHASE_PORT:
+ index = dev->phase_port;
+ break;
+ case MSM_SPM_PMIC_PFM_PORT:
+ index = dev->pfm_port;
+ break;
+ default:
+ break;
+ }
+
+ return index;
+}
+
+int msm_spm_drv_set_pmic_data(struct msm_spm_driver_data *dev,
+ enum msm_spm_pmic_port port, unsigned int data)
+{
+ unsigned int pmic_data = 0;
+ unsigned int timeout_us = 0;
+ int index = 0;
+
+ if (!msm_spm_pmic_arb_present(dev))
+ return -ENODEV;
+
+ index = msm_spm_drv_get_pmic_port(dev, port);
+ if (index < 0)
+ return -ENODEV;
+
+ pmic_data |= data & 0xFF;
+ pmic_data |= (index & 0x7) << 16;
+
+ dev->reg_shadow[MSM_SPM_REG_SAW_VCTL] &= ~0x700FF;
+ dev->reg_shadow[MSM_SPM_REG_SAW_VCTL] |= pmic_data;
+ msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_VCTL);
+ mb(); /* Ensure we flush */
+
+ timeout_us = dev->vctl_timeout_us;
+ /**
+ * Confirm the pmic data set was what hardware sent by
+ * checking the PMIC FSM state.
+ * We cannot use the sts_pmic_data and check it against
+ * the value like we do fot set_vdd, since the PMIC_STS
+ * is only updated for SAW_VCTL sent with port index 0.
+ */
+ do {
+ if (msm_spm_drv_get_sts_pmic_state(dev) ==
+ MSM_SPM_PMIC_STATE_IDLE)
+ break;
+ udelay(1);
+ } while (--timeout_us);
+
+ if (!timeout_us) {
+ pr_err("%s: failed, remaining timeout %u us, data %d\n",
+ __func__, timeout_us, data);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+void msm_spm_drv_reinit(struct msm_spm_driver_data *dev, bool seq_write)
+{
+ int i;
+
+ if (seq_write)
+ msm_spm_drv_flush_seq_entry(dev);
+
+ for (i = 0; i < MSM_SPM_REG_SAW_PMIC_DATA_0 + num_pmic_data; i++)
+ msm_spm_drv_load_shadow(dev, i);
+
+ for (i = MSM_SPM_REG_NR_INITIALIZE + 1; i < MSM_SPM_REG_NR; i++)
+ msm_spm_drv_load_shadow(dev, i);
+}
+
+int msm_spm_drv_reg_init(struct msm_spm_driver_data *dev,
+ struct msm_spm_platform_data *data)
+{
+ int i;
+ bool found = false;
+
+ dev->ver_reg = data->ver_reg;
+ dev->reg_base_addr = data->reg_base_addr;
+ msm_spm_drv_get_saw2_ver(dev, &dev->major, &dev->minor);
+ for (i = 0; i < ARRAY_SIZE(saw2_info); i++)
+ if (dev->major == saw2_info[i].major &&
+ dev->minor == saw2_info[i].minor) {
+ pr_debug("%s: Version found\n",
+ saw2_info[i].ver_name);
+ dev->reg_offsets = saw2_info[i].spm_reg_offset_ptr;
+ found = true;
+ break;
+ }
+
+ if (!found) {
+ pr_err("%s: No SAW version found\n", __func__);
+ WARN_ON(!found);
+ }
+ return 0;
+}
+
+void msm_spm_drv_upd_reg_shadow(struct msm_spm_driver_data *dev, int id,
+ int val)
+{
+ dev->reg_shadow[id] = val;
+ msm_spm_drv_flush_shadow(dev, id);
+ /* Complete the above writes before other accesses */
+ mb();
+}
+
+int msm_spm_drv_init(struct msm_spm_driver_data *dev,
+ struct msm_spm_platform_data *data)
+{
+ int num_spm_entry;
+
+ if (!dev || !data)
+ return -ENODEV;
+
+ dev->vctl_port = data->vctl_port;
+ dev->phase_port = data->phase_port;
+ dev->pfm_port = data->pfm_port;
+ dev->reg_base_addr = data->reg_base_addr;
+ memcpy(dev->reg_shadow, data->reg_init_values,
+ sizeof(data->reg_init_values));
+
+ dev->vctl_timeout_us = data->vctl_timeout_us;
+
+
+ if (!num_pmic_data)
+ num_pmic_data = msm_spm_drv_get_num_pmic_data(dev);
+
+ num_spm_entry = msm_spm_drv_get_num_spm_entry(dev);
+
+ dev->reg_seq_entry_shadow =
+ kzalloc(sizeof(*dev->reg_seq_entry_shadow) * num_spm_entry,
+ GFP_KERNEL);
+
+ if (!dev->reg_seq_entry_shadow)
+ return -ENOMEM;
+
+ return 0;
+}
diff --git a/drivers/soc/qcom/msm_smem.c b/drivers/soc/qcom/msm_smem.c
index bf2929e..5b1e9de 100644
--- a/drivers/soc/qcom/msm_smem.c
+++ b/drivers/soc/qcom/msm_smem.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -83,6 +83,7 @@
static RAW_NOTIFIER_HEAD(smem_module_init_notifier_list);
static DEFINE_MUTEX(smem_module_init_notifier_lock);
static bool probe_done;
+uint32_t smem_max_items;
/* smem security feature components */
#define SMEM_TOC_IDENTIFIER 0x434f5424 /* "$TOC" */
@@ -138,6 +139,11 @@
};
static struct smem_partition_info partitions[NUM_SMEM_SUBSYSTEMS];
+
+#define SMEM_COMM_PART_VERSION 0x000C
+#define SMEM_COMM_HOST 0xFFFE
+static bool use_comm_partition;
+static struct smem_partition_info comm_partition;
/* end smem security feature components */
/* Identifier for the SMEM target info struct. */
@@ -148,6 +154,7 @@
uint32_t identifier;
uint32_t size;
phys_addr_t phys_base_addr;
+ uint32_t max_items;
};
struct restart_notifier_block {
@@ -311,7 +318,7 @@
if (!skip_init_check && !smem_initialized_check())
return ret;
- if (id >= SMEM_NUM_ITEMS)
+ if (id >= smem_max_items)
return ret;
if (use_spinlocks) {
@@ -373,7 +380,7 @@
if (!skip_init_check && !smem_initialized_check())
return NULL;
- if (id >= SMEM_NUM_ITEMS) {
+ if (id >= smem_max_items) {
SMEM_INFO("%s: invalid id %d\n", __func__, id);
return NULL;
}
@@ -384,12 +391,18 @@
return NULL;
}
- if (flags & SMEM_ANY_HOST_FLAG || !partitions[to_proc].offset)
- return __smem_get_entry_nonsecure(id, size, skip_init_check,
- use_rspinlock);
-
- partition_num = partitions[to_proc].partition_num;
- hdr = smem_areas[0].virt_addr + partitions[to_proc].offset;
+ if (flags & SMEM_ANY_HOST_FLAG || !partitions[to_proc].offset) {
+ if (use_comm_partition) {
+ partition_num = comm_partition.partition_num;
+ hdr = smem_areas[0].virt_addr + comm_partition.offset;
+ } else {
+ return __smem_get_entry_nonsecure(id, size,
+ skip_init_check, use_rspinlock);
+ }
+ } else {
+ partition_num = partitions[to_proc].partition_num;
+ hdr = smem_areas[0].virt_addr + partitions[to_proc].offset;
+ }
if (unlikely(!spinlocks_initialized)) {
rc = init_smem_remote_spinlock();
if (unlikely(rc)) {
@@ -614,8 +627,19 @@
uint32_t partition_num;
void *ret = NULL;
- hdr = smem_base + partitions[to_proc].offset;
- partition_num = partitions[to_proc].partition_num;
+ if (to_proc == SMEM_COMM_HOST) {
+ hdr = smem_base + comm_partition.offset;
+ partition_num = comm_partition.partition_num;
+ size_cacheline = comm_partition.size_cacheline;
+ } else if (to_proc < NUM_SMEM_SUBSYSTEMS) {
+ hdr = smem_base + partitions[to_proc].offset;
+ partition_num = partitions[to_proc].partition_num;
+ size_cacheline = partitions[to_proc].size_cacheline;
+ } else {
+ SMEM_INFO("%s: invalid to_proc %u for id %u\n", __func__,
+ to_proc, id);
+ return NULL;
+ }
if (hdr->identifier != SMEM_PART_HDR_IDENTIFIER) {
LOG_ERR(
@@ -627,7 +651,6 @@
BUG();
}
- size_cacheline = partitions[to_proc].size_cacheline;
free_space = hdr->offset_free_cached -
hdr->offset_free_uncached;
@@ -719,7 +742,7 @@
if (!smem_initialized_check())
return NULL;
- if (id >= SMEM_NUM_ITEMS) {
+ if (id >= smem_max_items) {
SMEM_INFO("%s: invalid id %u\n", __func__, id);
return NULL;
}
@@ -761,11 +784,16 @@
if (id > SMEM_FIXED_ITEM_LAST) {
SMEM_INFO("%s: allocating %u size %u to_proc %u flags %u\n",
__func__, id, size_in, to_proc, flags);
- if (flags & SMEM_ANY_HOST_FLAG || !partitions[to_proc].offset)
- ret = alloc_item_nonsecure(id, a_size_in);
- else
+ if (flags & SMEM_ANY_HOST_FLAG
+ || !partitions[to_proc].offset) {
+ if (use_comm_partition)
+ ret = alloc_item_secure(id, size_in,
+ SMEM_COMM_HOST, flags);
+ else
+ ret = alloc_item_nonsecure(id, a_size_in);
+ } else {
ret = alloc_item_secure(id, size_in, to_proc, flags);
-
+ }
} else {
SMEM_INFO("%s: attempted to allocate non-dynamic item %u\n",
__func__, id);
@@ -892,14 +920,18 @@
unsigned int smem_get_version(unsigned int idx)
{
int *version_array;
+ struct smem_shared *smem = smem_ram_base;
if (idx > 32) {
pr_err("%s: invalid idx:%d\n", __func__, idx);
return 0;
}
- version_array = __smem_find(SMEM_VERSION_INFO, SMEM_VERSION_INFO_SIZE,
- true);
+ if (use_comm_partition)
+ version_array = smem->version;
+ else
+ version_array = __smem_find(SMEM_VERSION_INFO,
+ SMEM_VERSION_INFO_SIZE, true);
if (version_array == NULL)
return 0;
@@ -947,6 +979,7 @@
static int is_inited;
unsigned long flags;
struct smem_shared *smem;
+ unsigned int ver;
if (likely(checked)) {
if (unlikely(!is_inited))
@@ -975,8 +1008,12 @@
* structures. Without the extra configuration data, the SMEM driver
* cannot be properly initialized.
*/
- if (smem_get_version(MODEM_SBL_VERSION_INDEX) != SMEM_VERSION << 16) {
- pr_err("%s: SBL version not correct\n", __func__);
+ ver = smem->version[MODEM_SBL_VERSION_INDEX];
+ if (ver == SMEM_COMM_PART_VERSION << 16) {
+ use_comm_partition = true;
+ } else if (ver != SMEM_VERSION << 16) {
+ pr_err("%s: SBL version not correct 0x%x\n",
+ __func__, smem->version[7]);
goto failed;
}
@@ -1122,6 +1159,7 @@
{
uint16_t remote_host;
struct smem_partition_header *hdr;
+ bool is_comm_partition = false;
if (!entry->offset) {
SMEM_INFO("Skipping smem partition %d - bad offset\n", num);
@@ -1136,31 +1174,38 @@
return;
}
- if (entry->host0 == SMEM_APPS)
- remote_host = entry->host1;
- else
- remote_host = entry->host0;
+ if (entry->host0 == SMEM_COMM_HOST && entry->host1 == SMEM_COMM_HOST)
+ is_comm_partition = true;
- if (remote_host >= NUM_SMEM_SUBSYSTEMS) {
- SMEM_INFO("Skipping smem partition %d - bad remote:%d\n", num,
- remote_host);
- return;
- }
- if (partitions[remote_host].offset) {
- SMEM_INFO("Skipping smem partition %d - duplicate of %d\n", num,
- partitions[remote_host].partition_num);
- return;
+ if (!is_comm_partition) {
+ if (entry->host0 == SMEM_APPS)
+ remote_host = entry->host1;
+ else
+ remote_host = entry->host0;
+
+ if (remote_host >= NUM_SMEM_SUBSYSTEMS) {
+ SMEM_INFO(
+ "Skipping smem partition %d - bad remote:%d\n",
+ num, remote_host);
+ return;
+ }
+ if (partitions[remote_host].offset) {
+ SMEM_INFO(
+ "Skipping smem partition %d - duplicate of %d\n",
+ num, partitions[remote_host].partition_num);
+ return;
+ }
+
+ if (entry->host0 != SMEM_APPS && entry->host1 != SMEM_APPS) {
+ SMEM_INFO(
+ "Non-APSS Partition %d offset:%x host0:%d host1:%d\n",
+ num, entry->offset, entry->host0, entry->host1);
+ return;
+ }
}
hdr = smem_areas[0].virt_addr + entry->offset;
- if (entry->host0 != SMEM_APPS && entry->host1 != SMEM_APPS) {
- SMEM_INFO(
- "Non-APSS Partition %d offset:%x host0:%d host1:%d\n",
- num, entry->offset, entry->host0, entry->host1);
- return;
- }
-
if (hdr->identifier != SMEM_PART_HDR_IDENTIFIER) {
LOG_ERR("Smem partition %d hdr magic is bad\n", num);
BUG();
@@ -1177,6 +1222,20 @@
LOG_ERR("Smem partition %d cached heap exceeds size\n", num);
BUG();
}
+ if (is_comm_partition) {
+ if (hdr->host0 == SMEM_COMM_HOST
+ && hdr->host1 == SMEM_COMM_HOST) {
+ comm_partition.partition_num = num;
+ comm_partition.offset = entry->offset;
+ comm_partition.size_cacheline = entry->size_cacheline;
+ SMEM_INFO("Common Partition %d offset:%x\n", num,
+ entry->offset);
+ } else {
+ LOG_ERR("Smem Comm partition hosts don't match TOC\n");
+ WARN_ON(1);
+ }
+ return;
+ }
if (hdr->host0 != SMEM_APPS && hdr->host1 != SMEM_APPS) {
LOG_ERR("Smem partition %d hosts don't match TOC\n", num);
BUG();
@@ -1254,6 +1313,8 @@
}
smem_ram_phys = smem_targ_info->phys_base_addr;
smem_ram_size = smem_targ_info->size;
+ if (smem_targ_info->max_items)
+ smem_max_items = smem_targ_info->max_items;
iounmap(smem_targ_info_addr);
return 0;
}
@@ -1490,7 +1551,7 @@
return 0;
registered = true;
-
+ smem_max_items = SMEM_NUM_ITEMS;
smem_ipc_log_ctx = ipc_log_context_create(NUM_LOG_PAGES, "smem", 0);
if (!smem_ipc_log_ctx) {
pr_err("%s: unable to create logging context\n", __func__);
diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c
index 0dd8b26..03a6204 100644
--- a/drivers/soc/qcom/peripheral-loader.c
+++ b/drivers/soc/qcom/peripheral-loader.c
@@ -170,7 +170,11 @@
ret = do_elf_ramdump(ramdump_dev, ramdump_segs, count);
kfree(ramdump_segs);
- if (!ret && desc->subsys_vmid > 0)
+ if (ret)
+ pil_err(desc, "%s: Ramdump collection failed for subsys %s rc:%d\n",
+ __func__, desc->name, ret);
+
+ if (desc->subsys_vmid > 0)
ret = pil_assign_mem_to_subsys(desc, priv->region_start,
(priv->region_end - priv->region_start));
@@ -578,6 +582,13 @@
return pil_init_entry_addr(priv, mdt);
}
+struct pil_map_fw_info {
+ void *region;
+ unsigned long attrs;
+ phys_addr_t base_addr;
+ struct device *dev;
+};
+
static void pil_release_mmap(struct pil_desc *desc)
{
struct pil_priv *priv = desc->priv;
@@ -596,14 +607,30 @@
}
}
-#define IOMAP_SIZE SZ_1M
+static void pil_clear_segment(struct pil_desc *desc)
+{
+ struct pil_priv *priv = desc->priv;
+ u8 __iomem *buf;
-struct pil_map_fw_info {
- void *region;
- unsigned long attrs;
- phys_addr_t base_addr;
- struct device *dev;
-};
+ struct pil_map_fw_info map_fw_info = {
+ .attrs = desc->attrs,
+ .region = priv->region,
+ .base_addr = priv->region_start,
+ .dev = desc->dev,
+ };
+
+ void *map_data = desc->map_data ? desc->map_data : &map_fw_info;
+
+ /* Clear memory so that unauthorized ELF code is not left behind */
+ buf = desc->map_fw_mem(priv->region_start, (priv->region_end -
+ priv->region_start), map_data);
+ pil_memset_io(buf, 0, (priv->region_end - priv->region_start));
+ desc->unmap_fw_mem(buf, (priv->region_end - priv->region_start),
+ map_data);
+
+}
+
+#define IOMAP_SIZE SZ_1M
static void *map_fw_mem(phys_addr_t paddr, size_t size, void *data)
{
@@ -901,6 +928,7 @@
desc->attrs);
priv->region = NULL;
}
+ pil_clear_segment(desc);
pil_release_mmap(desc);
}
return ret;
diff --git a/drivers/soc/qcom/ramdump.c b/drivers/soc/qcom/ramdump.c
index f917ea9..d5b051e 100644
--- a/drivers/soc/qcom/ramdump.c
+++ b/drivers/soc/qcom/ramdump.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -326,13 +326,7 @@
if (rd_dev->complete_ramdump) {
for (i = 0; i < nsegments-1; i++)
segments[i].size =
- PAGE_ALIGN(segments[i+1].address - segments[i].address);
-
- segments[nsegments-1].size =
- PAGE_ALIGN(segments[nsegments-1].size);
- } else {
- for (i = 0; i < nsegments; i++)
- segments[i].size = PAGE_ALIGN(segments[i].size);
+ segments[i + 1].address - segments[i].address;
}
rd_dev->segments = segments;
diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c
index d4b90f40..baf3e3f 100644
--- a/drivers/soc/qcom/rpmh.c
+++ b/drivers/soc/qcom/rpmh.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -26,6 +26,7 @@
#include <soc/qcom/rpmh.h>
#include <soc/qcom/tcs.h>
+#include <soc/qcom/cmd-db.h>
#define RPMH_MAX_MBOXES 2
#define RPMH_MAX_FAST_RES 32
@@ -83,6 +84,7 @@
static struct rpmh_mbox mbox_ctrlr[RPMH_MAX_MBOXES];
DEFINE_MUTEX(rpmh_mbox_mutex);
+bool rpmh_standalone;
static struct rpmh_msg *get_msg_from_pool(struct rpmh_client *rc)
{
@@ -291,6 +293,9 @@
if (IS_ERR_OR_NULL(rc))
return -EINVAL;
+ if (rpmh_standalone)
+ return 0;
+
rpm_msg = get_msg_from_pool(rc);
if (!rpm_msg)
return -ENOMEM;
@@ -332,6 +337,9 @@
might_sleep();
+ if (rpmh_standalone)
+ return 0;
+
rpm_msg.cmd.addr = addr;
rpm_msg.cmd.data = data;
@@ -391,8 +399,12 @@
int rpmh_write_async(struct rpmh_client *rc, enum rpmh_state state,
struct tcs_cmd *cmd, int n)
{
- struct rpmh_msg *rpm_msg = __get_rpmh_msg_async(rc, state, cmd, n,
- true);
+ struct rpmh_msg *rpm_msg;
+
+ if (rpmh_standalone)
+ return 0;
+
+ rpm_msg = __get_rpmh_msg_async(rc, state, cmd, n, true);
if (IS_ERR(rpm_msg))
return PTR_ERR(rpm_msg);
@@ -430,6 +442,9 @@
might_sleep();
+ if (rpmh_standalone)
+ return 0;
+
rpm_msg.msg.payload = cmd;
rpm_msg.msg.num_payload = n;
@@ -470,6 +485,9 @@
int count = 0;
int ret, i = 0;
+ if (rpmh_standalone)
+ return 0;
+
while (n[count++])
;
count--;
@@ -528,6 +546,9 @@
if (IS_ERR_OR_NULL(rc))
return -EINVAL;
+ if (rpmh_standalone)
+ return 0;
+
rpm_msg.msg.payload = cmd;
rpm_msg.msg.num_payload = n;
rpm_msg.msg.is_control = true;
@@ -553,6 +574,9 @@
if (IS_ERR_OR_NULL(rc))
return -EINVAL;
+ if (rpmh_standalone)
+ return 0;
+
rpm_msg.msg.invalidate = true;
spin_lock(&rpm->lock);
@@ -585,6 +609,9 @@
might_sleep();
+ if (rpmh_standalone)
+ return 0;
+
rpm_msg.cmd.addr = addr;
rpm_msg.cmd.data = 0;
@@ -638,6 +665,9 @@
if (IS_ERR_OR_NULL(rc))
return -EINVAL;
+ if (rpmh_standalone)
+ return 0;
+
if (!mbox_controller_is_idle(rc->chan))
return -EBUSY;
@@ -748,6 +778,10 @@
struct rpmh_client *rc;
int ret = 0;
+ ret = cmd_db_ready();
+ if (ret)
+ return ERR_PTR(ret);
+
rc = kzalloc(sizeof(*rc), GFP_KERNEL);
if (!rc)
return ERR_PTR(-ENOMEM);
@@ -775,6 +809,7 @@
mutex_lock(&rpmh_mbox_mutex);
rc->rpmh = get_mbox(pdev, name, index);
+ rpmh_standalone = (cmd_db_is_standalone() > 0);
mutex_unlock(&rpmh_mbox_mutex);
if (IS_ERR(rc->rpmh)) {
diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c
index 2136771..bcd00b4 100644
--- a/drivers/soc/qcom/service-notifier.c
+++ b/drivers/soc/qcom/service-notifier.c
@@ -229,6 +229,7 @@
struct msg_desc ind_desc;
struct qmi_servreg_notif_state_updated_ind_msg_v01 ind_msg = {
QMI_STATE_MIN_VAL, "", 0xFFFF };
+ enum pd_subsys_state state = USER_PD_STATE_CHANGE;
int rc;
ind_desc.msg_id = SERVREG_NOTIF_STATE_UPDATED_IND_MSG;
@@ -256,7 +257,7 @@
mutex_lock(¬if_add_lock);
mutex_lock(&service_list_lock);
rc = service_notif_queue_notification(service_notif,
- ind_msg.curr_state, NULL);
+ ind_msg.curr_state, &state);
if (rc & NOTIFY_STOP_MASK)
pr_err("Notifier callback aborted for %s with error %d\n",
ind_msg.service_name, rc);
@@ -373,6 +374,7 @@
mutex_lock(&service_list_lock);
list_for_each_entry(service_notif, &service_list, list) {
if (service_notif->instance_id == data->instance_id) {
+ enum pd_subsys_state state = ROOT_PD_UP;
rc = register_notif_listener(service_notif, data,
&curr_state);
if (rc) {
@@ -380,7 +382,7 @@
service_notif->service_path, rc);
} else {
rc = service_notif_queue_notification(
- service_notif, curr_state, NULL);
+ service_notif, curr_state, &state);
if (rc & NOTIFY_STOP_MASK)
pr_err("Notifier callback aborted for %s error:%d\n",
service_notif->service_path, rc);
@@ -434,7 +436,7 @@
{
struct qmi_client_info *data = container_of(work,
struct qmi_client_info, svc_exit);
- root_service_service_exit(data, UNKNOWN);
+ root_service_service_exit(data, ROOT_PD_DOWN);
}
static int service_event_notify(struct notifier_block *this,
@@ -466,15 +468,24 @@
struct qmi_client_info *info = container_of(this,
struct qmi_client_info, ssr_notifier);
struct notif_data *notif = data;
+ enum pd_subsys_state state;
switch (code) {
case SUBSYS_BEFORE_SHUTDOWN:
- pr_debug("Root PD DOWN(SSR notification), crashed?%d\n",
+ pr_debug("Root PD DOWN(SSR notification), state:%d\n",
notif->crashed);
- if (notif->crashed)
- root_service_service_exit(info, CRASHED);
- else
- root_service_service_exit(info, SHUTDOWN);
+ switch (notif->crashed) {
+ case CRASH_STATUS_ERR_FATAL:
+ state = ROOT_PD_ERR_FATAL;
+ break;
+ case CRASH_STATUS_WDOG_BITE:
+ state = ROOT_PD_WDOG_BITE;
+ break;
+ default:
+ state = ROOT_PD_SHUTDOWN;
+ break;
+ }
+ root_service_service_exit(info, state);
break;
default:
break;
diff --git a/drivers/soc/qcom/smem_debug.c b/drivers/soc/qcom/smem_debug.c
index 51a483b..8afd45b 100644
--- a/drivers/soc/qcom/smem_debug.c
+++ b/drivers/soc/qcom/smem_debug.c
@@ -1,7 +1,7 @@
/* arch/arm/mach-msm/smem_debug.c
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2013,2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2013,2016-2017 The Linux Foundation. All rights reserved.
* Author: Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -53,7 +53,7 @@
heap_info->free_offset,
heap_info->heap_remaining);
- for (n = 0; n < SMEM_NUM_ITEMS; n++) {
+ for (n = 0; n < smem_max_items; n++) {
if (toc[n].allocated == 0)
continue;
seq_printf(s, "%04d: offset %08x size %08x\n",
@@ -67,9 +67,8 @@
for (n = 0; n < 32; n++) {
version = smem_get_version(n);
- seq_printf(s, "entry %d: smem = %d proc_comm = %d\n", n,
- version >> 16,
- version & 0xffff);
+ seq_printf(s, "entry %d:%x smem = %d proc_comm = %d\n",
+ n, version, version >> 16, version & 0xffff);
}
}
diff --git a/drivers/soc/qcom/smem_private.h b/drivers/soc/qcom/smem_private.h
index 12decd4..29bd927 100644
--- a/drivers/soc/qcom/smem_private.h
+++ b/drivers/soc/qcom/smem_private.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013,2016 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013,2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,6 +18,7 @@
#define SMD_HEAP_SIZE 512
+extern uint32_t smem_max_items;
struct smem_heap_info {
unsigned int initialized;
diff --git a/drivers/soc/qcom/spm_devices.c b/drivers/soc/qcom/spm_devices.c
new file mode 100644
index 0000000..06c8be4
--- /dev/null
+++ b/drivers/soc/qcom/spm_devices.c
@@ -0,0 +1,991 @@
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/cpu.h>
+#include <soc/qcom/spm.h>
+#include "spm_driver.h"
+
+#define VDD_DEFAULT 0xDEADF00D
+#define SLP_CMD_BIT 17
+#define PC_MODE_BIT 16
+#define RET_MODE_BIT 15
+#define EVENT_SYNC_BIT 24
+#define ISAR_BIT 3
+#define SPM_EN_BIT 0
+
+struct msm_spm_power_modes {
+ uint32_t mode;
+ uint32_t ctl;
+};
+
+struct msm_spm_device {
+ struct list_head list;
+ bool initialized;
+ const char *name;
+ struct msm_spm_driver_data reg_data;
+ struct msm_spm_power_modes *modes;
+ uint32_t num_modes;
+ uint32_t cpu_vdd;
+ struct cpumask mask;
+ void __iomem *q2s_reg;
+ bool qchannel_ignore;
+ bool allow_rpm_hs;
+ bool use_spm_clk_gating;
+ bool use_qchannel_for_wfi;
+ void __iomem *flush_base_addr;
+ void __iomem *slpreq_base_addr;
+};
+
+struct msm_spm_vdd_info {
+ struct msm_spm_device *vctl_dev;
+ uint32_t vlevel;
+ int err;
+};
+
+static LIST_HEAD(spm_list);
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
+static DEFINE_PER_CPU(struct msm_spm_device *, cpu_vctl_device);
+
+static void msm_spm_smp_set_vdd(void *data)
+{
+ struct msm_spm_vdd_info *info = (struct msm_spm_vdd_info *)data;
+ struct msm_spm_device *dev = info->vctl_dev;
+
+ dev->cpu_vdd = info->vlevel;
+ info->err = msm_spm_drv_set_vdd(&dev->reg_data, info->vlevel);
+}
+
+/**
+ * msm_spm_probe_done(): Verify and return the status of the cpu(s) and l2
+ * probe.
+ * Return: 0 if all spm devices have been probed, else return -EPROBE_DEFER.
+ * if probe failed, then return the err number for that failure.
+ */
+int msm_spm_probe_done(void)
+{
+ struct msm_spm_device *dev;
+ int cpu;
+ int ret = 0;
+
+ for_each_possible_cpu(cpu) {
+ dev = per_cpu(cpu_vctl_device, cpu);
+ if (!dev)
+ return -EPROBE_DEFER;
+
+ ret = IS_ERR(dev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_spm_probe_done);
+
+void msm_spm_dump_regs(unsigned int cpu)
+{
+ dump_regs(&per_cpu(msm_cpu_spm_device, cpu).reg_data, cpu);
+}
+
+/**
+ * msm_spm_set_vdd(): Set core voltage
+ * @cpu: core id
+ * @vlevel: Encoded PMIC data.
+ *
+ * Return: 0 on success or -(ERRNO) on failure.
+ */
+int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel)
+{
+ struct msm_spm_vdd_info info;
+ struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+ int ret;
+
+ if (!dev)
+ return -EPROBE_DEFER;
+
+ ret = IS_ERR(dev);
+ if (ret)
+ return ret;
+
+ info.vctl_dev = dev;
+ info.vlevel = vlevel;
+
+ ret = smp_call_function_any(&dev->mask, msm_spm_smp_set_vdd, &info,
+ true);
+ if (ret)
+ return ret;
+
+ return info.err;
+}
+EXPORT_SYMBOL(msm_spm_set_vdd);
+
+/**
+ * msm_spm_get_vdd(): Get core voltage
+ * @cpu: core id
+ * @return: Returns encoded PMIC data.
+ */
+int msm_spm_get_vdd(unsigned int cpu)
+{
+ struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+ if (!dev)
+ return -EPROBE_DEFER;
+
+ return msm_spm_drv_get_vdd(&dev->reg_data) ? : -EINVAL;
+}
+EXPORT_SYMBOL(msm_spm_get_vdd);
+
+static void msm_spm_config_q2s(struct msm_spm_device *dev, unsigned int mode)
+{
+ uint32_t spm_legacy_mode = 0;
+ uint32_t qchannel_ignore = 0;
+ uint32_t val = 0;
+
+ if (!dev->q2s_reg)
+ return;
+
+ switch (mode) {
+ case MSM_SPM_MODE_DISABLED:
+ case MSM_SPM_MODE_CLOCK_GATING:
+ qchannel_ignore = !dev->use_qchannel_for_wfi;
+ spm_legacy_mode = 0;
+ break;
+ case MSM_SPM_MODE_RETENTION:
+ qchannel_ignore = 0;
+ spm_legacy_mode = 0;
+ break;
+ case MSM_SPM_MODE_GDHS:
+ case MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE:
+ case MSM_SPM_MODE_POWER_COLLAPSE:
+ qchannel_ignore = dev->qchannel_ignore;
+ spm_legacy_mode = 1;
+ break;
+ default:
+ break;
+ }
+
+ val = spm_legacy_mode << 2 | qchannel_ignore << 1;
+ __raw_writel(val, dev->q2s_reg);
+ mb(); /* Ensure flush */
+}
+
+static void msm_spm_config_hw_flush(struct msm_spm_device *dev,
+ unsigned int mode)
+{
+ uint32_t val = 0;
+
+ if (!dev->flush_base_addr)
+ return;
+
+ switch (mode) {
+ case MSM_SPM_MODE_FASTPC:
+ case MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE:
+ case MSM_SPM_MODE_POWER_COLLAPSE:
+ val = BIT(0);
+ break;
+ default:
+ break;
+ }
+
+ __raw_writel(val, dev->flush_base_addr);
+}
+
+static void msm_spm_config_slpreq(struct msm_spm_device *dev,
+ unsigned int mode)
+{
+ uint32_t val = 0;
+
+ if (!dev->slpreq_base_addr)
+ return;
+
+ switch (mode) {
+ case MSM_SPM_MODE_FASTPC:
+ case MSM_SPM_MODE_GDHS:
+ case MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE:
+ case MSM_SPM_MODE_POWER_COLLAPSE:
+ val = BIT(4);
+ break;
+ default:
+ break;
+ }
+
+ val = (__raw_readl(dev->slpreq_base_addr) & ~BIT(4)) | val;
+ __raw_writel(val, dev->slpreq_base_addr);
+}
+
+static int msm_spm_dev_set_low_power_mode(struct msm_spm_device *dev,
+ unsigned int mode, bool notify_rpm)
+{
+ uint32_t i;
+ int ret = -EINVAL;
+ uint32_t ctl = 0;
+
+ if (!dev) {
+ pr_err("dev is NULL\n");
+ return -ENODEV;
+ }
+
+ if (!dev->initialized)
+ return -ENXIO;
+
+ if (!dev->num_modes)
+ return 0;
+
+ if (mode == MSM_SPM_MODE_DISABLED) {
+ ret = msm_spm_drv_set_spm_enable(&dev->reg_data, false);
+ } else if (!msm_spm_drv_set_spm_enable(&dev->reg_data, true)) {
+ for (i = 0; i < dev->num_modes; i++) {
+ if (dev->modes[i].mode != mode)
+ continue;
+
+ ctl = dev->modes[i].ctl;
+ if (!dev->allow_rpm_hs && notify_rpm)
+ ctl &= ~BIT(SLP_CMD_BIT);
+
+ break;
+ }
+ ret = msm_spm_drv_set_low_power_mode(&dev->reg_data, ctl);
+ }
+
+ msm_spm_config_q2s(dev, mode);
+ msm_spm_config_hw_flush(dev, mode);
+ msm_spm_config_slpreq(dev, mode);
+
+ return ret;
+}
+
+static int msm_spm_dev_init(struct msm_spm_device *dev,
+ struct msm_spm_platform_data *data)
+{
+ int i, ret = -ENOMEM;
+ uint32_t offset = 0;
+
+ dev->cpu_vdd = VDD_DEFAULT;
+ dev->num_modes = data->num_modes;
+ dev->modes = kmalloc(
+ sizeof(struct msm_spm_power_modes) * dev->num_modes,
+ GFP_KERNEL);
+
+ if (!dev->modes)
+ goto spm_failed_malloc;
+
+ ret = msm_spm_drv_init(&dev->reg_data, data);
+
+ if (ret)
+ goto spm_failed_init;
+
+ for (i = 0; i < dev->num_modes; i++) {
+
+ /* Default offset is 0 and gets updated as we write more
+ * sequences into SPM
+ */
+ dev->modes[i].ctl = data->modes[i].ctl | ((offset & 0x1FF)
+ << 4);
+ ret = msm_spm_drv_write_seq_data(&dev->reg_data,
+ data->modes[i].cmd, &offset);
+ if (ret < 0)
+ goto spm_failed_init;
+
+ dev->modes[i].mode = data->modes[i].mode;
+ }
+
+ msm_spm_drv_reinit(&dev->reg_data, dev->num_modes ? true : false);
+
+ dev->initialized = true;
+
+ return 0;
+
+spm_failed_init:
+ kfree(dev->modes);
+spm_failed_malloc:
+ return ret;
+}
+
+/**
+ * msm_spm_turn_on_cpu_rail(): Power on cpu rail before turning on core
+ * @node: The SPM node that controls the voltage for the CPU
+ * @val: The value to be set on the rail
+ * @cpu: The cpu for this with rail is being powered on
+ */
+int msm_spm_turn_on_cpu_rail(struct device_node *vctl_node,
+ unsigned int val, int cpu, int vctl_offset)
+{
+ uint32_t timeout = 2000; /* delay for voltage to settle on the core */
+ struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+ void __iomem *base;
+
+ base = of_iomap(vctl_node, 1);
+ if (base) {
+ /*
+ * Program Q2S to disable SPM legacy mode and ignore Q2S
+ * channel requests.
+ * bit[1] = qchannel_ignore = 1
+ * bit[2] = spm_legacy_mode = 0
+ */
+ writel_relaxed(0x2, base);
+ mb(); /* Ensure flush */
+ iounmap(base);
+ }
+
+ base = of_iomap(vctl_node, 0);
+ if (!base)
+ return -ENOMEM;
+
+ if (dev && (dev->cpu_vdd != VDD_DEFAULT))
+ return 0;
+
+ /* Set the CPU supply regulator voltage */
+ val = (val & 0xFF);
+ writel_relaxed(val, base + vctl_offset);
+ mb(); /* Ensure flush */
+ udelay(timeout);
+
+ /* Enable the CPU supply regulator*/
+ val = 0x30080;
+ writel_relaxed(val, base + vctl_offset);
+ mb(); /* Ensure flush */
+ udelay(timeout);
+
+ iounmap(base);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_spm_turn_on_cpu_rail);
+
+void msm_spm_reinit(void)
+{
+ unsigned int cpu;
+
+ for_each_possible_cpu(cpu)
+ msm_spm_drv_reinit(
+ &per_cpu(msm_cpu_spm_device.reg_data, cpu), true);
+}
+EXPORT_SYMBOL(msm_spm_reinit);
+
+/*
+ * msm_spm_is_mode_avail() - Specifies if a mode is available for the cpu
+ * It should only be used to decide a mode before lpm driver is probed.
+ * @mode: SPM LPM mode to be selected
+ */
+bool msm_spm_is_mode_avail(unsigned int mode)
+{
+ struct msm_spm_device *dev = this_cpu_ptr(&msm_cpu_spm_device);
+ int i;
+
+ for (i = 0; i < dev->num_modes; i++) {
+ if (dev->modes[i].mode == mode)
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * msm_spm_is_avs_enabled() - Functions returns 1 if AVS is enabled and
+ * 0 if it is not.
+ * @cpu: specifies cpu's avs should be read
+ *
+ * Returns errno in case of failure or AVS enable state otherwise
+ */
+int msm_spm_is_avs_enabled(unsigned int cpu)
+{
+ struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+ if (!dev)
+ return -ENXIO;
+
+ return msm_spm_drv_get_avs_enable(&dev->reg_data);
+}
+EXPORT_SYMBOL(msm_spm_is_avs_enabled);
+
+/**
+ * msm_spm_avs_enable() - Enables AVS on the SAW that controls this cpu's
+ * voltage.
+ * @cpu: specifies which cpu's avs should be enabled
+ *
+ * Returns errno in case of failure or 0 if successful
+ */
+int msm_spm_avs_enable(unsigned int cpu)
+{
+ struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+ if (!dev)
+ return -ENXIO;
+
+ return msm_spm_drv_set_avs_enable(&dev->reg_data, true);
+}
+EXPORT_SYMBOL(msm_spm_avs_enable);
+
+/**
+ * msm_spm_avs_disable() - Disables AVS on the SAW that controls this cpu's
+ * voltage.
+ * @cpu: specifies which cpu's avs should be enabled
+ *
+ * Returns errno in case of failure or 0 if successful
+ */
+int msm_spm_avs_disable(unsigned int cpu)
+{
+ struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+ if (!dev)
+ return -ENXIO;
+
+ return msm_spm_drv_set_avs_enable(&dev->reg_data, false);
+}
+EXPORT_SYMBOL(msm_spm_avs_disable);
+
+/**
+ * msm_spm_avs_set_limit() - Set maximum and minimum AVS limits on the
+ * SAW that controls this cpu's voltage.
+ * @cpu: specify which cpu's avs should be configured
+ * @min_lvl: specifies the minimum PMIC output voltage control register
+ * value that may be sent to the PMIC
+ * @max_lvl: specifies the maximum PMIC output voltage control register
+ * value that may be sent to the PMIC
+ * Returns errno in case of failure or 0 if successful
+ */
+int msm_spm_avs_set_limit(unsigned int cpu,
+ uint32_t min_lvl, uint32_t max_lvl)
+{
+ struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+ if (!dev)
+ return -ENXIO;
+
+ return msm_spm_drv_set_avs_limit(&dev->reg_data, min_lvl, max_lvl);
+}
+EXPORT_SYMBOL(msm_spm_avs_set_limit);
+
+/**
+ * msm_spm_avs_enable_irq() - Enable an AVS interrupt
+ * @cpu: specifies which CPU's AVS should be configured
+ * @irq: specifies which interrupt to enable
+ *
+ * Returns errno in case of failure or 0 if successful.
+ */
+int msm_spm_avs_enable_irq(unsigned int cpu, enum msm_spm_avs_irq irq)
+{
+ struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+ if (!dev)
+ return -ENXIO;
+
+ return msm_spm_drv_set_avs_irq_enable(&dev->reg_data, irq, true);
+}
+EXPORT_SYMBOL(msm_spm_avs_enable_irq);
+
+/**
+ * msm_spm_avs_disable_irq() - Disable an AVS interrupt
+ * @cpu: specifies which CPU's AVS should be configured
+ * @irq: specifies which interrupt to disable
+ *
+ * Returns errno in case of failure or 0 if successful.
+ */
+int msm_spm_avs_disable_irq(unsigned int cpu, enum msm_spm_avs_irq irq)
+{
+ struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+ if (!dev)
+ return -ENXIO;
+
+ return msm_spm_drv_set_avs_irq_enable(&dev->reg_data, irq, false);
+}
+EXPORT_SYMBOL(msm_spm_avs_disable_irq);
+
+/**
+ * msm_spm_avs_clear_irq() - Clear a latched AVS interrupt
+ * @cpu: specifies which CPU's AVS should be configured
+ * @irq: specifies which interrupt to clear
+ *
+ * Returns errno in case of failure or 0 if successful.
+ */
+int msm_spm_avs_clear_irq(unsigned int cpu, enum msm_spm_avs_irq irq)
+{
+ struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+ if (!dev)
+ return -ENXIO;
+
+ return msm_spm_drv_avs_clear_irq(&dev->reg_data, irq);
+}
+EXPORT_SYMBOL(msm_spm_avs_clear_irq);
+
+/**
+ * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
+ * @mode: SPM LPM mode to enter
+ * @notify_rpm: Notify RPM in this mode
+ */
+int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm)
+{
+ struct msm_spm_device *dev = this_cpu_ptr(&msm_cpu_spm_device);
+
+ return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm);
+}
+EXPORT_SYMBOL(msm_spm_set_low_power_mode);
+
+/**
+ * msm_spm_init(): Board initalization function
+ * @data: platform specific SPM register configuration data
+ * @nr_devs: Number of SPM devices being initialized
+ */
+int __init msm_spm_init(struct msm_spm_platform_data *data, int nr_devs)
+{
+ unsigned int cpu;
+ int ret = 0;
+
+ if ((nr_devs < num_possible_cpus()) || !data)
+ return -EINVAL;
+
+ for_each_possible_cpu(cpu) {
+ struct msm_spm_device *dev = &per_cpu(msm_cpu_spm_device, cpu);
+
+ ret = msm_spm_dev_init(dev, &data[cpu]);
+ if (ret < 0) {
+ pr_warn("%s():failed CPU:%u ret:%d\n", __func__,
+ cpu, ret);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+struct msm_spm_device *msm_spm_get_device_by_name(const char *name)
+{
+ struct list_head *list;
+
+ list_for_each(list, &spm_list) {
+ struct msm_spm_device *dev
+ = list_entry(list, typeof(*dev), list);
+ if (dev->name && !strcmp(dev->name, name))
+ return dev;
+ }
+ return ERR_PTR(-ENODEV);
+}
+
+int msm_spm_config_low_power_mode(struct msm_spm_device *dev,
+ unsigned int mode, bool notify_rpm)
+{
+ return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm);
+}
+#ifdef CONFIG_MSM_L2_SPM
+
+/**
+ * msm_spm_apcs_set_phase(): Set number of SMPS phases.
+ * @cpu: cpu which is requesting the change in number of phases.
+ * @phase_cnt: Number of phases to be set active
+ */
+int msm_spm_apcs_set_phase(int cpu, unsigned int phase_cnt)
+{
+ struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+ if (!dev)
+ return -ENXIO;
+
+ return msm_spm_drv_set_pmic_data(&dev->reg_data,
+ MSM_SPM_PMIC_PHASE_PORT, phase_cnt);
+}
+EXPORT_SYMBOL(msm_spm_apcs_set_phase);
+
+/** msm_spm_enable_fts_lpm() : Enable FTS to switch to low power
+ * when the cores are in low power modes
+ * @cpu: cpu that is entering low power mode.
+ * @mode: The mode configuration for FTS
+ */
+int msm_spm_enable_fts_lpm(int cpu, uint32_t mode)
+{
+ struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+ if (!dev)
+ return -ENXIO;
+
+ return msm_spm_drv_set_pmic_data(&dev->reg_data,
+ MSM_SPM_PMIC_PFM_PORT, mode);
+}
+EXPORT_SYMBOL(msm_spm_enable_fts_lpm);
+
+#endif
+
+static int get_cpu_id(struct device_node *node)
+{
+ struct device_node *cpu_node;
+ u32 cpu;
+ char *key = "qcom,cpu";
+
+ cpu_node = of_parse_phandle(node, key, 0);
+ if (cpu_node) {
+ for_each_possible_cpu(cpu) {
+ if (of_get_cpu_node(cpu, NULL) == cpu_node)
+ return cpu;
+ }
+ } else
+ return num_possible_cpus();
+
+ return -EINVAL;
+}
+
+static struct msm_spm_device *msm_spm_get_device(struct platform_device *pdev)
+{
+ struct msm_spm_device *dev = NULL;
+ const char *val = NULL;
+ char *key = "qcom,name";
+ int cpu = get_cpu_id(pdev->dev.of_node);
+
+ if ((cpu >= 0) && cpu < num_possible_cpus())
+ dev = &per_cpu(msm_cpu_spm_device, cpu);
+ else if (cpu == num_possible_cpus())
+ dev = devm_kzalloc(&pdev->dev, sizeof(struct msm_spm_device),
+ GFP_KERNEL);
+
+ if (!dev)
+ return NULL;
+
+ if (of_property_read_string(pdev->dev.of_node, key, &val)) {
+ pr_err("%s(): Cannot find a required node key:%s\n",
+ __func__, key);
+ return NULL;
+ }
+ dev->name = val;
+ list_add(&dev->list, &spm_list);
+
+ return dev;
+}
+
+static void get_cpumask(struct device_node *node, struct cpumask *mask)
+{
+ unsigned int c;
+ int idx = 0;
+ struct device_node *cpu_node;
+ char *key = "qcom,cpu-vctl-list";
+
+ cpu_node = of_parse_phandle(node, key, idx++);
+ while (cpu_node) {
+ for_each_possible_cpu(c) {
+ if (of_get_cpu_node(c, NULL) == cpu_node)
+ cpumask_set_cpu(c, mask);
+ }
+ cpu_node = of_parse_phandle(node, key, idx++);
+ };
+}
+
+static int msm_spm_dev_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ int cpu = 0;
+ int i = 0;
+ struct device_node *node = pdev->dev.of_node;
+ struct device_node *n = NULL;
+ struct msm_spm_platform_data spm_data;
+ char *key = NULL;
+ uint32_t val = 0;
+ struct msm_spm_seq_entry modes[MSM_SPM_MODE_NR];
+ int len = 0;
+ struct msm_spm_device *dev = NULL;
+ struct resource *res = NULL;
+ uint32_t mode_count = 0;
+
+ struct spm_of {
+ char *key;
+ uint32_t id;
+ };
+
+ struct spm_of spm_of_data[] = {
+ {"qcom,saw2-cfg", MSM_SPM_REG_SAW_CFG},
+ {"qcom,saw2-avs-ctl", MSM_SPM_REG_SAW_AVS_CTL},
+ {"qcom,saw2-avs-hysteresis", MSM_SPM_REG_SAW_AVS_HYSTERESIS},
+ {"qcom,saw2-avs-limit", MSM_SPM_REG_SAW_AVS_LIMIT},
+ {"qcom,saw2-avs-dly", MSM_SPM_REG_SAW_AVS_DLY},
+ {"qcom,saw2-spm-dly", MSM_SPM_REG_SAW_SPM_DLY},
+ {"qcom,saw2-spm-ctl", MSM_SPM_REG_SAW_SPM_CTL},
+ {"qcom,saw2-pmic-data0", MSM_SPM_REG_SAW_PMIC_DATA_0},
+ {"qcom,saw2-pmic-data1", MSM_SPM_REG_SAW_PMIC_DATA_1},
+ {"qcom,saw2-pmic-data2", MSM_SPM_REG_SAW_PMIC_DATA_2},
+ {"qcom,saw2-pmic-data3", MSM_SPM_REG_SAW_PMIC_DATA_3},
+ {"qcom,saw2-pmic-data4", MSM_SPM_REG_SAW_PMIC_DATA_4},
+ {"qcom,saw2-pmic-data5", MSM_SPM_REG_SAW_PMIC_DATA_5},
+ {"qcom,saw2-pmic-data6", MSM_SPM_REG_SAW_PMIC_DATA_6},
+ {"qcom,saw2-pmic-data7", MSM_SPM_REG_SAW_PMIC_DATA_7},
+ };
+
+ struct mode_of {
+ char *key;
+ uint32_t id;
+ };
+
+ struct mode_of mode_of_data[] = {
+ {"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING},
+ {"qcom,saw2-spm-cmd-ret", MSM_SPM_MODE_RETENTION},
+ {"qcom,saw2-spm-cmd-gdhs", MSM_SPM_MODE_GDHS},
+ {"qcom,saw2-spm-cmd-spc",
+ MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE},
+ {"qcom,saw2-spm-cmd-pc", MSM_SPM_MODE_POWER_COLLAPSE},
+ {"qcom,saw2-spm-cmd-fpc", MSM_SPM_MODE_FASTPC},
+ };
+
+ dev = msm_spm_get_device(pdev);
+ if (!dev) {
+ /*
+ * For partial goods support some CPUs might not be available
+ * in which case, shouldn't throw an error
+ */
+ return 0;
+ }
+ get_cpumask(node, &dev->mask);
+
+ memset(&spm_data, 0, sizeof(struct msm_spm_platform_data));
+ memset(&modes, 0,
+ (MSM_SPM_MODE_NR - 2) * sizeof(struct msm_spm_seq_entry));
+
+ key = "qcom,saw2-ver-reg";
+ ret = of_property_read_u32(node, key, &val);
+ if (ret)
+ goto fail;
+ spm_data.ver_reg = val;
+
+ key = "qcom,vctl-timeout-us";
+ ret = of_property_read_u32(node, key, &val);
+ if (!ret)
+ spm_data.vctl_timeout_us = val;
+
+ /* SAW start address */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ spm_data.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!spm_data.reg_base_addr) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ spm_data.vctl_port = -1;
+ spm_data.phase_port = -1;
+ spm_data.pfm_port = -1;
+
+ key = "qcom,vctl-port";
+ of_property_read_u32(node, key, &spm_data.vctl_port);
+
+ key = "qcom,phase-port";
+ of_property_read_u32(node, key, &spm_data.phase_port);
+
+ key = "qcom,pfm-port";
+ of_property_read_u32(node, key, &spm_data.pfm_port);
+
+ /* Q2S (QChannel-2-SPM) register */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "q2s");
+ if (res) {
+ dev->q2s_reg = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!dev->q2s_reg) {
+ pr_err("%s(): Unable to iomap Q2S register\n",
+ __func__);
+ ret = -EADDRNOTAVAIL;
+ goto fail;
+ }
+ }
+
+ key = "qcom,use-qchannel-for-pc";
+ dev->qchannel_ignore = !of_property_read_bool(node, key);
+
+ key = "qcom,use-spm-clock-gating";
+ dev->use_spm_clk_gating = of_property_read_bool(node, key);
+
+ key = "qcom,use-qchannel-for-wfi";
+ dev->use_qchannel_for_wfi = of_property_read_bool(node, key);
+
+ /* HW flush address */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hw-flush");
+ if (res) {
+ dev->flush_base_addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dev->flush_base_addr)) {
+ ret = PTR_ERR(dev->flush_base_addr);
+ pr_err("%s(): Unable to iomap hw flush register %d\n",
+ __func__, ret);
+ goto fail;
+ }
+ }
+
+ /* Sleep req address */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "slpreq");
+ if (res) {
+ dev->slpreq_base_addr = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!dev->slpreq_base_addr) {
+ ret = -ENOMEM;
+ pr_err("%s(): Unable to iomap slpreq register\n",
+ __func__);
+ ret = -EADDRNOTAVAIL;
+ goto fail;
+ }
+ }
+
+ /*
+ * At system boot, cpus and or clusters can remain in reset. CCI SPM
+ * will not be triggered unless SPM_LEGACY_MODE bit is set for the
+ * cluster in reset. Initialize q2s registers and set the
+ * SPM_LEGACY_MODE bit.
+ */
+ msm_spm_config_q2s(dev, MSM_SPM_MODE_POWER_COLLAPSE);
+ msm_spm_drv_reg_init(&dev->reg_data, &spm_data);
+
+ for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
+ ret = of_property_read_u32(node, spm_of_data[i].key, &val);
+ if (ret)
+ continue;
+ msm_spm_drv_upd_reg_shadow(&dev->reg_data, spm_of_data[i].id,
+ val);
+ }
+
+ for_each_child_of_node(node, n) {
+ const char *name;
+ bool bit_set;
+ int sync;
+
+ if (!n->name)
+ continue;
+
+ ret = of_property_read_string(n, "qcom,label", &name);
+ if (ret)
+ continue;
+
+ for (i = 0; i < ARRAY_SIZE(mode_of_data); i++)
+ if (!strcmp(name, mode_of_data[i].key))
+ break;
+
+ if (i == ARRAY_SIZE(mode_of_data)) {
+ pr_err("Mode name invalid %s\n", name);
+ break;
+ }
+
+ modes[mode_count].mode = mode_of_data[i].id;
+ modes[mode_count].cmd =
+ (uint8_t *)of_get_property(n, "qcom,sequence", &len);
+ if (!modes[mode_count].cmd) {
+ pr_err("cmd is empty\n");
+ continue;
+ }
+
+ bit_set = of_property_read_bool(n, "qcom,pc_mode");
+ modes[mode_count].ctl |= bit_set ? BIT(PC_MODE_BIT) : 0;
+
+ bit_set = of_property_read_bool(n, "qcom,ret_mode");
+ modes[mode_count].ctl |= bit_set ? BIT(RET_MODE_BIT) : 0;
+
+ bit_set = of_property_read_bool(n, "qcom,slp_cmd_mode");
+ modes[mode_count].ctl |= bit_set ? BIT(SLP_CMD_BIT) : 0;
+
+ bit_set = of_property_read_bool(n, "qcom,isar");
+ modes[mode_count].ctl |= bit_set ? BIT(ISAR_BIT) : 0;
+
+ bit_set = of_property_read_bool(n, "qcom,spm_en");
+ modes[mode_count].ctl |= bit_set ? BIT(SPM_EN_BIT) : 0;
+
+ ret = of_property_read_u32(n, "qcom,event_sync", &sync);
+ if (!ret)
+ modes[mode_count].ctl |= sync << EVENT_SYNC_BIT;
+
+ mode_count++;
+ }
+
+ spm_data.modes = modes;
+ spm_data.num_modes = mode_count;
+
+ key = "qcom,supports-rpm-hs";
+ dev->allow_rpm_hs = of_property_read_bool(pdev->dev.of_node, key);
+
+ ret = msm_spm_dev_init(dev, &spm_data);
+ if (ret)
+ pr_err("SPM modes programming is not available from HLOS\n");
+
+ platform_set_drvdata(pdev, dev);
+
+ for_each_cpu(cpu, &dev->mask)
+ per_cpu(cpu_vctl_device, cpu) = dev;
+
+ if (!spm_data.num_modes)
+ return 0;
+
+ cpu = get_cpu_id(pdev->dev.of_node);
+
+ /* For CPUs that are online, the SPM has to be programmed for
+ * clockgating mode to ensure that it can use SPM for entering these
+ * low power modes.
+ */
+ get_online_cpus();
+ if ((cpu >= 0) && (cpu < num_possible_cpus()) && (cpu_online(cpu)))
+ msm_spm_config_low_power_mode(dev, MSM_SPM_MODE_CLOCK_GATING,
+ false);
+ put_online_cpus();
+ return ret;
+
+fail:
+ cpu = get_cpu_id(pdev->dev.of_node);
+ if (dev && (cpu >= num_possible_cpus() || (cpu < 0))) {
+ for_each_cpu(cpu, &dev->mask)
+ per_cpu(cpu_vctl_device, cpu) = ERR_PTR(ret);
+ }
+
+ pr_err("%s: CPU%d SPM device probe failed: %d\n", __func__, cpu, ret);
+
+ return ret;
+}
+
+static int msm_spm_dev_remove(struct platform_device *pdev)
+{
+ struct msm_spm_device *dev = platform_get_drvdata(pdev);
+
+ list_del(&dev->list);
+ return 0;
+}
+
+static const struct of_device_id msm_spm_match_table[] = {
+ {.compatible = "qcom,spm-v2"},
+ {},
+};
+
+static struct platform_driver msm_spm_device_driver = {
+ .probe = msm_spm_dev_probe,
+ .remove = msm_spm_dev_remove,
+ .driver = {
+ .name = "spm-v2",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_spm_match_table,
+ },
+};
+
+/**
+ * msm_spm_device_init(): Device tree initialization function
+ */
+int __init msm_spm_device_init(void)
+{
+ static bool registered;
+
+ if (registered)
+ return 0;
+ registered = true;
+ return platform_driver_register(&msm_spm_device_driver);
+}
+arch_initcall(msm_spm_device_init);
diff --git a/drivers/soc/qcom/spm_driver.h b/drivers/soc/qcom/spm_driver.h
new file mode 100644
index 0000000..a645813
--- /dev/null
+++ b/drivers/soc/qcom/spm_driver.h
@@ -0,0 +1,134 @@
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __ARCH_ARM_MACH_MSM_SPM_DEVICES_H
+#define __ARCH_ARM_MACH_MSM_SPM_DEVICES_H
+
+#include <soc/qcom/spm.h>
+
+enum {
+ MSM_SPM_REG_SAW_CFG,
+ MSM_SPM_REG_SAW_AVS_CTL,
+ MSM_SPM_REG_SAW_AVS_HYSTERESIS,
+ MSM_SPM_REG_SAW_SPM_CTL,
+ MSM_SPM_REG_SAW_PMIC_DLY,
+ MSM_SPM_REG_SAW_AVS_LIMIT,
+ MSM_SPM_REG_SAW_AVS_DLY,
+ MSM_SPM_REG_SAW_SPM_DLY,
+ MSM_SPM_REG_SAW_PMIC_DATA_0,
+ MSM_SPM_REG_SAW_PMIC_DATA_1,
+ MSM_SPM_REG_SAW_PMIC_DATA_2,
+ MSM_SPM_REG_SAW_PMIC_DATA_3,
+ MSM_SPM_REG_SAW_PMIC_DATA_4,
+ MSM_SPM_REG_SAW_PMIC_DATA_5,
+ MSM_SPM_REG_SAW_PMIC_DATA_6,
+ MSM_SPM_REG_SAW_PMIC_DATA_7,
+ MSM_SPM_REG_SAW_RST,
+
+ MSM_SPM_REG_NR_INITIALIZE = MSM_SPM_REG_SAW_RST,
+
+ MSM_SPM_REG_SAW_ID,
+ MSM_SPM_REG_SAW_SECURE,
+ MSM_SPM_REG_SAW_STS0,
+ MSM_SPM_REG_SAW_STS1,
+ MSM_SPM_REG_SAW_STS2,
+ MSM_SPM_REG_SAW_VCTL,
+ MSM_SPM_REG_SAW_SEQ_ENTRY,
+ MSM_SPM_REG_SAW_SPM_STS,
+ MSM_SPM_REG_SAW_AVS_STS,
+ MSM_SPM_REG_SAW_PMIC_STS,
+ MSM_SPM_REG_SAW_VERSION,
+
+ MSM_SPM_REG_NR,
+};
+
+struct msm_spm_seq_entry {
+ uint32_t mode;
+ uint8_t *cmd;
+ uint32_t ctl;
+};
+
+struct msm_spm_platform_data {
+ void __iomem *reg_base_addr;
+ uint32_t reg_init_values[MSM_SPM_REG_NR_INITIALIZE];
+
+ uint32_t ver_reg;
+ uint32_t vctl_port;
+ uint32_t phase_port;
+ uint32_t pfm_port;
+
+ uint8_t awake_vlevel;
+ uint32_t vctl_timeout_us;
+ uint32_t avs_timeout_us;
+
+ uint32_t num_modes;
+ struct msm_spm_seq_entry *modes;
+};
+
+enum msm_spm_pmic_port {
+ MSM_SPM_PMIC_VCTL_PORT,
+ MSM_SPM_PMIC_PHASE_PORT,
+ MSM_SPM_PMIC_PFM_PORT,
+};
+
+struct msm_spm_driver_data {
+ uint32_t major;
+ uint32_t minor;
+ uint32_t ver_reg;
+ uint32_t vctl_port;
+ uint32_t phase_port;
+ uint32_t pfm_port;
+ void __iomem *reg_base_addr;
+ uint32_t vctl_timeout_us;
+ uint32_t avs_timeout_us;
+ uint32_t reg_shadow[MSM_SPM_REG_NR];
+ uint32_t *reg_seq_entry_shadow;
+ uint32_t *reg_offsets;
+};
+
+int msm_spm_drv_init(struct msm_spm_driver_data *dev,
+ struct msm_spm_platform_data *data);
+int msm_spm_drv_reg_init(struct msm_spm_driver_data *dev,
+ struct msm_spm_platform_data *data);
+void msm_spm_drv_reinit(struct msm_spm_driver_data *dev, bool seq);
+int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *dev,
+ uint32_t ctl);
+int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev,
+ unsigned int vlevel);
+void dump_regs(struct msm_spm_driver_data *dev, int cpu);
+uint32_t msm_spm_drv_get_sts_curr_pmic_data(
+ struct msm_spm_driver_data *dev);
+int msm_spm_drv_write_seq_data(struct msm_spm_driver_data *dev,
+ uint8_t *cmd, uint32_t *offset);
+void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev);
+int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *dev,
+ bool enable);
+int msm_spm_drv_set_pmic_data(struct msm_spm_driver_data *dev,
+ enum msm_spm_pmic_port port, unsigned int data);
+
+int msm_spm_drv_set_avs_limit(struct msm_spm_driver_data *dev,
+ uint32_t min_lvl, uint32_t max_lvl);
+
+int msm_spm_drv_set_avs_enable(struct msm_spm_driver_data *dev,
+ bool enable);
+int msm_spm_drv_get_avs_enable(struct msm_spm_driver_data *dev);
+
+int msm_spm_drv_set_avs_irq_enable(struct msm_spm_driver_data *dev,
+ enum msm_spm_avs_irq irq, bool enable);
+int msm_spm_drv_avs_clear_irq(struct msm_spm_driver_data *dev,
+ enum msm_spm_avs_irq irq);
+
+void msm_spm_reinit(void);
+int msm_spm_init(struct msm_spm_platform_data *data, int nr_devs);
+void msm_spm_drv_upd_reg_shadow(struct msm_spm_driver_data *dev, int id,
+ int val);
+uint32_t msm_spm_drv_get_vdd(struct msm_spm_driver_data *dev);
+#endif
diff --git a/drivers/soc/ti/wkup_m3_ipc.c b/drivers/soc/ti/wkup_m3_ipc.c
index 8823cc8..5bb3760 100644
--- a/drivers/soc/ti/wkup_m3_ipc.c
+++ b/drivers/soc/ti/wkup_m3_ipc.c
@@ -459,6 +459,7 @@
if (IS_ERR(task)) {
dev_err(dev, "can't create rproc_boot thread\n");
+ ret = PTR_ERR(task);
goto err_put_rproc;
}
diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c
index ded3702..6b001c4 100644
--- a/drivers/spi/spi-orion.c
+++ b/drivers/spi/spi-orion.c
@@ -138,37 +138,62 @@
tclk_hz = clk_get_rate(orion_spi->clk);
if (devdata->typ == ARMADA_SPI) {
- unsigned int clk, spr, sppr, sppr2, err;
- unsigned int best_spr, best_sppr, best_err;
+ /*
+ * Given the core_clk (tclk_hz) and the target rate (speed) we
+ * determine the best values for SPR (in [0 .. 15]) and SPPR (in
+ * [0..7]) such that
+ *
+ * core_clk / (SPR * 2 ** SPPR)
+ *
+ * is as big as possible but not bigger than speed.
+ */
- best_err = speed;
- best_spr = 0;
- best_sppr = 0;
+ /* best integer divider: */
+ unsigned divider = DIV_ROUND_UP(tclk_hz, speed);
+ unsigned spr, sppr;
- /* Iterate over the valid range looking for best fit */
- for (sppr = 0; sppr < 8; sppr++) {
- sppr2 = 0x1 << sppr;
+ if (divider < 16) {
+ /* This is the easy case, divider is less than 16 */
+ spr = divider;
+ sppr = 0;
- spr = tclk_hz / sppr2;
- spr = DIV_ROUND_UP(spr, speed);
- if ((spr == 0) || (spr > 15))
- continue;
+ } else {
+ unsigned two_pow_sppr;
+ /*
+ * Find the highest bit set in divider. This and the
+ * three next bits define SPR (apart from rounding).
+ * SPPR is then the number of zero bits that must be
+ * appended:
+ */
+ sppr = fls(divider) - 4;
- clk = tclk_hz / (spr * sppr2);
- err = speed - clk;
+ /*
+ * As SPR only has 4 bits, we have to round divider up
+ * to the next multiple of 2 ** sppr.
+ */
+ two_pow_sppr = 1 << sppr;
+ divider = (divider + two_pow_sppr - 1) & -two_pow_sppr;
- if (err < best_err) {
- best_spr = spr;
- best_sppr = sppr;
- best_err = err;
- }
+ /*
+ * recalculate sppr as rounding up divider might have
+ * increased it enough to change the position of the
+ * highest set bit. In this case the bit that now
+ * doesn't make it into SPR is 0, so there is no need to
+ * round again.
+ */
+ sppr = fls(divider) - 4;
+ spr = divider >> sppr;
+
+ /*
+ * Now do range checking. SPR is constructed to have a
+ * width of 4 bits, so this is fine for sure. So we
+ * still need to check for sppr to fit into 3 bits:
+ */
+ if (sppr > 7)
+ return -EINVAL;
}
- if ((best_sppr == 0) && (best_spr == 0))
- return -EINVAL;
-
- prescale = ((best_sppr & 0x6) << 5) |
- ((best_sppr & 0x1) << 4) | best_spr;
+ prescale = ((sppr & 0x6) << 5) | ((sppr & 0x1) << 4) | spr;
} else {
/*
* the supported rates are: 4,6,8...30
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index dd7b5b4..d6239fa 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -1690,6 +1690,7 @@
pxa2xx_spi_write(drv_data, SSCR1, tmp);
tmp = SSCR0_SCR(2) | SSCR0_Motorola | SSCR0_DataSize(8);
pxa2xx_spi_write(drv_data, SSCR0, tmp);
+ break;
default:
tmp = SSCR1_RxTresh(RX_THRESH_DFLT) |
SSCR1_TxTresh(TX_THRESH_DFLT);
diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
index 0f28c08..77b551d 100644
--- a/drivers/ssb/pci.c
+++ b/drivers/ssb/pci.c
@@ -909,6 +909,7 @@
if (err) {
ssb_warn("WARNING: Using fallback SPROM failed (err %d)\n",
err);
+ goto out_free;
} else {
ssb_dbg("Using SPROM revision %d provided by platform\n",
sprom->revision);
diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger.c b/drivers/staging/android/fiq_debugger/fiq_debugger.c
index b132cff..675b974 100644
--- a/drivers/staging/android/fiq_debugger/fiq_debugger.c
+++ b/drivers/staging/android/fiq_debugger/fiq_debugger.c
@@ -33,7 +33,6 @@
#include <linux/timer.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
-#include <linux/wakelock.h>
#ifdef CONFIG_FIQ_GLUE
#include <asm/fiq_glue.h>
@@ -82,7 +81,7 @@
struct timer_list sleep_timer;
spinlock_t sleep_timer_lock;
bool uart_enabled;
- struct wake_lock debugger_wake_lock;
+ struct wakeup_source debugger_wake_src;
bool console_enable;
int current_cpu;
atomic_t unhandled_fiq_count;
@@ -563,7 +562,7 @@
state->uart_enabled = false;
fiq_debugger_enable_wakeup_irq(state);
}
- wake_unlock(&state->debugger_wake_lock);
+ __pm_relax(&state->debugger_wake_src);
spin_unlock_irqrestore(&state->sleep_timer_lock, flags);
}
@@ -575,7 +574,7 @@
if (state->wakeup_irq >= 0 && state->ignore_next_wakeup_irq) {
state->ignore_next_wakeup_irq = false;
} else if (!state->uart_enabled) {
- wake_lock(&state->debugger_wake_lock);
+ __pm_stay_awake(&state->debugger_wake_src);
fiq_debugger_uart_enable(state);
state->uart_enabled = true;
fiq_debugger_disable_wakeup_irq(state);
@@ -619,7 +618,7 @@
unsigned long flags;
spin_lock_irqsave(&state->sleep_timer_lock, flags);
- wake_lock(&state->debugger_wake_lock);
+ __pm_stay_awake(&state->debugger_wake_src);
mod_timer(&state->sleep_timer, jiffies + HZ * 5);
spin_unlock_irqrestore(&state->sleep_timer_lock, flags);
}
@@ -1086,8 +1085,7 @@
state->no_sleep = true;
state->ignore_next_wakeup_irq = !state->no_sleep;
- wake_lock_init(&state->debugger_wake_lock,
- WAKE_LOCK_SUSPEND, "serial-debug");
+ wakeup_source_init(&state->debugger_wake_src, "serial-debug");
state->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(state->clk))
@@ -1188,7 +1186,7 @@
clk_disable(state->clk);
if (state->clk)
clk_put(state->clk);
- wake_lock_destroy(&state->debugger_wake_lock);
+ wakeup_source_trash(&state->debugger_wake_src);
platform_set_drvdata(pdev, NULL);
kfree(state);
return ret;
diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c
index 0f97d7b..1c967c3 100644
--- a/drivers/staging/comedi/drivers/ni_mio_common.c
+++ b/drivers/staging/comedi/drivers/ni_mio_common.c
@@ -1832,7 +1832,7 @@
unsigned int *data)
{
struct ni_private *devpriv = dev->private;
- unsigned int mask = (s->maxdata + 1) >> 1;
+ unsigned int mask = s->maxdata;
int i, n;
unsigned int signbits;
unsigned int d;
@@ -1875,7 +1875,7 @@
return -ETIME;
}
d += signbits;
- data[n] = d;
+ data[n] = d & 0xffff;
}
} else if (devpriv->is_6143) {
for (n = 0; n < insn->n; n++) {
@@ -1924,9 +1924,8 @@
data[n] = dl;
} else {
d = ni_readw(dev, NI_E_AI_FIFO_DATA_REG);
- /* subtle: needs to be short addition */
d += signbits;
- data[n] = d;
+ data[n] = d & 0xffff;
}
}
}
diff --git a/drivers/staging/goldfish/Kconfig b/drivers/staging/goldfish/Kconfig
index 4e09460..d293bbc 100644
--- a/drivers/staging/goldfish/Kconfig
+++ b/drivers/staging/goldfish/Kconfig
@@ -4,6 +4,14 @@
---help---
Emulated audio channel for the Goldfish Android Virtual Device
+config GOLDFISH_SYNC
+ tristate "Goldfish AVD Sync Driver"
+ depends on GOLDFISH
+ depends on SW_SYNC
+ depends on SYNC_FILE
+ ---help---
+ Emulated sync fences for the Goldfish Android Virtual Device
+
config MTD_GOLDFISH_NAND
tristate "Goldfish NAND device"
depends on GOLDFISH
diff --git a/drivers/staging/goldfish/Makefile b/drivers/staging/goldfish/Makefile
index dec34ad..3313fce 100644
--- a/drivers/staging/goldfish/Makefile
+++ b/drivers/staging/goldfish/Makefile
@@ -4,3 +4,9 @@
obj-$(CONFIG_GOLDFISH_AUDIO) += goldfish_audio.o
obj-$(CONFIG_MTD_GOLDFISH_NAND) += goldfish_nand.o
+
+# and sync
+
+ccflags-y := -Idrivers/staging/android
+goldfish_sync-objs := goldfish_sync_timeline_fence.o goldfish_sync_timeline.o
+obj-$(CONFIG_GOLDFISH_SYNC) += goldfish_sync.o
diff --git a/drivers/staging/goldfish/goldfish_audio.c b/drivers/staging/goldfish/goldfish_audio.c
index bd55995..0bb0ee2 100644
--- a/drivers/staging/goldfish/goldfish_audio.c
+++ b/drivers/staging/goldfish/goldfish_audio.c
@@ -28,6 +28,7 @@
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/goldfish.h>
+#include <linux/acpi.h>
MODULE_AUTHOR("Google, Inc.");
MODULE_DESCRIPTION("Android QEMU Audio Driver");
@@ -116,6 +117,7 @@
size_t count, loff_t *pos)
{
struct goldfish_audio *data = fp->private_data;
+ unsigned long irq_flags;
int length;
int result = 0;
@@ -129,6 +131,10 @@
wait_event_interruptible(data->wait, data->buffer_status &
AUDIO_INT_READ_BUFFER_FULL);
+ spin_lock_irqsave(&data->lock, irq_flags);
+ data->buffer_status &= ~AUDIO_INT_READ_BUFFER_FULL;
+ spin_unlock_irqrestore(&data->lock, irq_flags);
+
length = AUDIO_READ(data, AUDIO_READ_BUFFER_AVAILABLE);
/* copy data to user space */
@@ -351,12 +357,19 @@
};
MODULE_DEVICE_TABLE(of, goldfish_audio_of_match);
+static const struct acpi_device_id goldfish_audio_acpi_match[] = {
+ { "GFSH0005", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, goldfish_audio_acpi_match);
+
static struct platform_driver goldfish_audio_driver = {
.probe = goldfish_audio_probe,
.remove = goldfish_audio_remove,
.driver = {
.name = "goldfish_audio",
.of_match_table = goldfish_audio_of_match,
+ .acpi_match_table = ACPI_PTR(goldfish_audio_acpi_match),
}
};
diff --git a/drivers/staging/goldfish/goldfish_sync_timeline.c b/drivers/staging/goldfish/goldfish_sync_timeline.c
new file mode 100644
index 0000000..5bef4c6
--- /dev/null
+++ b/drivers/staging/goldfish/goldfish_sync_timeline.c
@@ -0,0 +1,962 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/fdtable.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <linux/interrupt.h>
+#include <linux/kref.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/acpi.h>
+
+#include <linux/string.h>
+
+#include <linux/fs.h>
+#include <linux/syscalls.h>
+#include <linux/sync_file.h>
+#include <linux/fence.h>
+
+#include "goldfish_sync_timeline_fence.h"
+
+#define ERR(...) printk(KERN_ERR __VA_ARGS__);
+
+#define INFO(...) printk(KERN_INFO __VA_ARGS__);
+
+#define DPRINT(...) pr_debug(__VA_ARGS__);
+
+#define DTRACE() DPRINT("%s: enter", __func__)
+
+/* The Goldfish sync driver is designed to provide a interface
+ * between the underlying host's sync device and the kernel's
+ * fence sync framework..
+ * The purpose of the device/driver is to enable lightweight
+ * creation and signaling of timelines and fences
+ * in order to synchronize the guest with host-side graphics events.
+ *
+ * Each time the interrupt trips, the driver
+ * may perform a sync operation.
+ */
+
+/* The operations are: */
+
+/* Ready signal - used to mark when irq should lower */
+#define CMD_SYNC_READY 0
+
+/* Create a new timeline. writes timeline handle */
+#define CMD_CREATE_SYNC_TIMELINE 1
+
+/* Create a fence object. reads timeline handle and time argument.
+ * Writes fence fd to the SYNC_REG_HANDLE register. */
+#define CMD_CREATE_SYNC_FENCE 2
+
+/* Increments timeline. reads timeline handle and time argument */
+#define CMD_SYNC_TIMELINE_INC 3
+
+/* Destroys a timeline. reads timeline handle */
+#define CMD_DESTROY_SYNC_TIMELINE 4
+
+/* Starts a wait on the host with
+ * the given glsync object and sync thread handle. */
+#define CMD_TRIGGER_HOST_WAIT 5
+
+/* The register layout is: */
+
+#define SYNC_REG_BATCH_COMMAND 0x00 /* host->guest batch commands */
+#define SYNC_REG_BATCH_GUESTCOMMAND 0x04 /* guest->host batch commands */
+#define SYNC_REG_BATCH_COMMAND_ADDR 0x08 /* communicate physical address of host->guest batch commands */
+#define SYNC_REG_BATCH_COMMAND_ADDR_HIGH 0x0c /* 64-bit part */
+#define SYNC_REG_BATCH_GUESTCOMMAND_ADDR 0x10 /* communicate physical address of guest->host commands */
+#define SYNC_REG_BATCH_GUESTCOMMAND_ADDR_HIGH 0x14 /* 64-bit part */
+#define SYNC_REG_INIT 0x18 /* signals that the device has been probed */
+
+/* There is an ioctl associated with goldfish sync driver.
+ * Make it conflict with ioctls that are not likely to be used
+ * in the emulator.
+ *
+ * '@' 00-0F linux/radeonfb.h conflict!
+ * '@' 00-0F drivers/video/aty/aty128fb.c conflict!
+ */
+#define GOLDFISH_SYNC_IOC_MAGIC '@'
+
+#define GOLDFISH_SYNC_IOC_QUEUE_WORK _IOWR(GOLDFISH_SYNC_IOC_MAGIC, 0, struct goldfish_sync_ioctl_info)
+
+/* The above definitions (command codes, register layout, ioctl definitions)
+ * need to be in sync with the following files:
+ *
+ * Host-side (emulator):
+ * external/qemu/android/emulation/goldfish_sync.h
+ * external/qemu-android/hw/misc/goldfish_sync.c
+ *
+ * Guest-side (system image):
+ * device/generic/goldfish-opengl/system/egl/goldfish_sync.h
+ * device/generic/goldfish/ueventd.ranchu.rc
+ * platform/build/target/board/generic/sepolicy/file_contexts
+ */
+struct goldfish_sync_hostcmd {
+ /* sorted for alignment */
+ uint64_t handle;
+ uint64_t hostcmd_handle;
+ uint32_t cmd;
+ uint32_t time_arg;
+};
+
+struct goldfish_sync_guestcmd {
+ uint64_t host_command; /* uint64_t for alignment */
+ uint64_t glsync_handle;
+ uint64_t thread_handle;
+ uint64_t guest_timeline_handle;
+};
+
+#define GOLDFISH_SYNC_MAX_CMDS 32
+
+struct goldfish_sync_state {
+ char __iomem *reg_base;
+ int irq;
+
+ /* Spinlock protects |to_do| / |to_do_end|. */
+ spinlock_t lock;
+ /* |mutex_lock| protects all concurrent access
+ * to timelines for both kernel and user space. */
+ struct mutex mutex_lock;
+
+ /* Buffer holding commands issued from host. */
+ struct goldfish_sync_hostcmd to_do[GOLDFISH_SYNC_MAX_CMDS];
+ uint32_t to_do_end;
+
+ /* Addresses for the reading or writing
+ * of individual commands. The host can directly write
+ * to |batch_hostcmd| (and then this driver immediately
+ * copies contents to |to_do|). This driver either replies
+ * through |batch_hostcmd| or simply issues a
+ * guest->host command through |batch_guestcmd|.
+ */
+ struct goldfish_sync_hostcmd *batch_hostcmd;
+ struct goldfish_sync_guestcmd *batch_guestcmd;
+
+ /* Used to give this struct itself to a work queue
+ * function for executing actual sync commands. */
+ struct work_struct work_item;
+};
+
+static struct goldfish_sync_state global_sync_state[1];
+
+struct goldfish_sync_timeline_obj {
+ struct goldfish_sync_timeline *sync_tl;
+ uint32_t current_time;
+ /* We need to be careful about when we deallocate
+ * this |goldfish_sync_timeline_obj| struct.
+ * In order to ensure proper cleanup, we need to
+ * consider the triggered host-side wait that may
+ * still be in flight when the guest close()'s a
+ * goldfish_sync device's sync context fd (and
+ * destroys the |sync_tl| field above).
+ * The host-side wait may raise IRQ
+ * and tell the kernel to increment the timeline _after_
+ * the |sync_tl| has already been set to null.
+ *
+ * From observations on OpenGL apps and CTS tests, this
+ * happens at some very low probability upon context
+ * destruction or process close, but it does happen
+ * and it needs to be handled properly. Otherwise,
+ * if we clean up the surrounding |goldfish_sync_timeline_obj|
+ * too early, any |handle| field of any host->guest command
+ * might not even point to a null |sync_tl| field,
+ * but to garbage memory or even a reclaimed |sync_tl|.
+ * If we do not count such "pending waits" and kfree the object
+ * immediately upon |goldfish_sync_timeline_destroy|,
+ * we might get mysterous RCU stalls after running a long
+ * time because the garbage memory that is being read
+ * happens to be interpretable as a |spinlock_t| struct
+ * that is currently in the locked state.
+ *
+ * To track when to free the |goldfish_sync_timeline_obj|
+ * itself, we maintain a kref.
+ * The kref essentially counts the timeline itself plus
+ * the number of waits in flight. kref_init/kref_put
+ * are issued on
+ * |goldfish_sync_timeline_create|/|goldfish_sync_timeline_destroy|
+ * and kref_get/kref_put are issued on
+ * |goldfish_sync_fence_create|/|goldfish_sync_timeline_inc|.
+ *
+ * The timeline is destroyed after reference count
+ * reaches zero, which would happen after
+ * |goldfish_sync_timeline_destroy| and all pending
+ * |goldfish_sync_timeline_inc|'s are fulfilled.
+ *
+ * NOTE (1): We assume that |fence_create| and
+ * |timeline_inc| calls are 1:1, otherwise the kref scheme
+ * will not work. This is a valid assumption as long
+ * as the host-side virtual device implementation
+ * does not insert any timeline increments
+ * that we did not trigger from here.
+ *
+ * NOTE (2): The use of kref by itself requires no locks,
+ * but this does not mean everything works without locks.
+ * Related timeline operations do require a lock of some sort,
+ * or at least are not proven to work without it.
+ * In particualr, we assume that all the operations
+ * done on the |kref| field above are done in contexts where
+ * |global_sync_state->mutex_lock| is held. Do not
+ * remove that lock until everything is proven to work
+ * without it!!! */
+ struct kref kref;
+};
+
+/* We will call |delete_timeline_obj| when the last reference count
+ * of the kref is decremented. This deletes the sync
+ * timeline object along with the wrapper itself. */
+static void delete_timeline_obj(struct kref* kref) {
+ struct goldfish_sync_timeline_obj* obj =
+ container_of(kref, struct goldfish_sync_timeline_obj, kref);
+
+ goldfish_sync_timeline_put_internal(obj->sync_tl);
+ obj->sync_tl = NULL;
+ kfree(obj);
+}
+
+static uint64_t gensym_ctr;
+static void gensym(char *dst)
+{
+ sprintf(dst, "goldfish_sync:gensym:%llu", gensym_ctr);
+ gensym_ctr++;
+}
+
+/* |goldfish_sync_timeline_create| assumes that |global_sync_state->mutex_lock|
+ * is held. */
+static struct goldfish_sync_timeline_obj*
+goldfish_sync_timeline_create(void)
+{
+
+ char timeline_name[256];
+ struct goldfish_sync_timeline *res_sync_tl = NULL;
+ struct goldfish_sync_timeline_obj *res;
+
+ DTRACE();
+
+ gensym(timeline_name);
+
+ res_sync_tl = goldfish_sync_timeline_create_internal(timeline_name);
+ if (!res_sync_tl) {
+ ERR("Failed to create goldfish_sw_sync timeline.");
+ return NULL;
+ }
+
+ res = kzalloc(sizeof(struct goldfish_sync_timeline_obj), GFP_KERNEL);
+ res->sync_tl = res_sync_tl;
+ res->current_time = 0;
+ kref_init(&res->kref);
+
+ DPRINT("new timeline_obj=0x%p", res);
+ return res;
+}
+
+/* |goldfish_sync_fence_create| assumes that |global_sync_state->mutex_lock|
+ * is held. */
+static int
+goldfish_sync_fence_create(struct goldfish_sync_timeline_obj *obj,
+ uint32_t val)
+{
+
+ int fd;
+ char fence_name[256];
+ struct sync_pt *syncpt = NULL;
+ struct sync_file *sync_file_obj = NULL;
+ struct goldfish_sync_timeline *tl;
+
+ DTRACE();
+
+ if (!obj) return -1;
+
+ tl = obj->sync_tl;
+
+ syncpt = goldfish_sync_pt_create_internal(
+ tl, sizeof(struct sync_pt) + 4, val);
+ if (!syncpt) {
+ ERR("could not create sync point! "
+ "goldfish_sync_timeline=0x%p val=%d",
+ tl, val);
+ return -1;
+ }
+
+ fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fd < 0) {
+ ERR("could not get unused fd for sync fence. "
+ "errno=%d", fd);
+ goto err_cleanup_pt;
+ }
+
+ gensym(fence_name);
+
+ sync_file_obj = sync_file_create(&syncpt->base);
+ if (!sync_file_obj) {
+ ERR("could not create sync fence! "
+ "goldfish_sync_timeline=0x%p val=%d sync_pt=0x%p",
+ tl, val, syncpt);
+ goto err_cleanup_fd_pt;
+ }
+
+ DPRINT("installing sync fence into fd %d sync_file_obj=0x%p",
+ fd, sync_file_obj);
+ fd_install(fd, sync_file_obj->file);
+ kref_get(&obj->kref);
+
+ return fd;
+
+err_cleanup_fd_pt:
+ put_unused_fd(fd);
+err_cleanup_pt:
+ fence_put(&syncpt->base);
+ return -1;
+}
+
+/* |goldfish_sync_timeline_inc| assumes that |global_sync_state->mutex_lock|
+ * is held. */
+static void
+goldfish_sync_timeline_inc(struct goldfish_sync_timeline_obj *obj, uint32_t inc)
+{
+ DTRACE();
+ /* Just give up if someone else nuked the timeline.
+ * Whoever it was won't care that it doesn't get signaled. */
+ if (!obj) return;
+
+ DPRINT("timeline_obj=0x%p", obj);
+ goldfish_sync_timeline_signal_internal(obj->sync_tl, inc);
+ DPRINT("incremented timeline. increment max_time");
+ obj->current_time += inc;
+
+ /* Here, we will end up deleting the timeline object if it
+ * turns out that this call was a pending increment after
+ * |goldfish_sync_timeline_destroy| was called. */
+ kref_put(&obj->kref, delete_timeline_obj);
+ DPRINT("done");
+}
+
+/* |goldfish_sync_timeline_destroy| assumes
+ * that |global_sync_state->mutex_lock| is held. */
+static void
+goldfish_sync_timeline_destroy(struct goldfish_sync_timeline_obj *obj)
+{
+ DTRACE();
+ /* See description of |goldfish_sync_timeline_obj| for why we
+ * should not immediately destroy |obj| */
+ kref_put(&obj->kref, delete_timeline_obj);
+}
+
+static inline void
+goldfish_sync_cmd_queue(struct goldfish_sync_state *sync_state,
+ uint32_t cmd,
+ uint64_t handle,
+ uint32_t time_arg,
+ uint64_t hostcmd_handle)
+{
+ struct goldfish_sync_hostcmd *to_add;
+
+ DTRACE();
+
+ BUG_ON(sync_state->to_do_end == GOLDFISH_SYNC_MAX_CMDS);
+
+ to_add = &sync_state->to_do[sync_state->to_do_end];
+
+ to_add->cmd = cmd;
+ to_add->handle = handle;
+ to_add->time_arg = time_arg;
+ to_add->hostcmd_handle = hostcmd_handle;
+
+ sync_state->to_do_end += 1;
+}
+
+static inline void
+goldfish_sync_hostcmd_reply(struct goldfish_sync_state *sync_state,
+ uint32_t cmd,
+ uint64_t handle,
+ uint32_t time_arg,
+ uint64_t hostcmd_handle)
+{
+ unsigned long irq_flags;
+ struct goldfish_sync_hostcmd *batch_hostcmd =
+ sync_state->batch_hostcmd;
+
+ DTRACE();
+
+ spin_lock_irqsave(&sync_state->lock, irq_flags);
+
+ batch_hostcmd->cmd = cmd;
+ batch_hostcmd->handle = handle;
+ batch_hostcmd->time_arg = time_arg;
+ batch_hostcmd->hostcmd_handle = hostcmd_handle;
+ writel(0, sync_state->reg_base + SYNC_REG_BATCH_COMMAND);
+
+ spin_unlock_irqrestore(&sync_state->lock, irq_flags);
+}
+
+static inline void
+goldfish_sync_send_guestcmd(struct goldfish_sync_state *sync_state,
+ uint32_t cmd,
+ uint64_t glsync_handle,
+ uint64_t thread_handle,
+ uint64_t timeline_handle)
+{
+ unsigned long irq_flags;
+ struct goldfish_sync_guestcmd *batch_guestcmd =
+ sync_state->batch_guestcmd;
+
+ DTRACE();
+
+ spin_lock_irqsave(&sync_state->lock, irq_flags);
+
+ batch_guestcmd->host_command = (uint64_t)cmd;
+ batch_guestcmd->glsync_handle = (uint64_t)glsync_handle;
+ batch_guestcmd->thread_handle = (uint64_t)thread_handle;
+ batch_guestcmd->guest_timeline_handle = (uint64_t)timeline_handle;
+ writel(0, sync_state->reg_base + SYNC_REG_BATCH_GUESTCOMMAND);
+
+ spin_unlock_irqrestore(&sync_state->lock, irq_flags);
+}
+
+/* |goldfish_sync_interrupt| handles IRQ raises from the virtual device.
+ * In the context of OpenGL, this interrupt will fire whenever we need
+ * to signal a fence fd in the guest, with the command
+ * |CMD_SYNC_TIMELINE_INC|.
+ * However, because this function will be called in an interrupt context,
+ * it is necessary to do the actual work of signaling off of interrupt context.
+ * The shared work queue is used for this purpose. At the end when
+ * all pending commands are intercepted by the interrupt handler,
+ * we call |schedule_work|, which will later run the actual
+ * desired sync command in |goldfish_sync_work_item_fn|.
+ */
+static irqreturn_t goldfish_sync_interrupt(int irq, void *dev_id)
+{
+
+ struct goldfish_sync_state *sync_state = dev_id;
+
+ uint32_t nextcmd;
+ uint32_t command_r;
+ uint64_t handle_rw;
+ uint32_t time_r;
+ uint64_t hostcmd_handle_rw;
+
+ int count = 0;
+
+ DTRACE();
+
+ sync_state = dev_id;
+
+ spin_lock(&sync_state->lock);
+
+ for (;;) {
+
+ readl(sync_state->reg_base + SYNC_REG_BATCH_COMMAND);
+ nextcmd = sync_state->batch_hostcmd->cmd;
+
+ if (nextcmd == 0)
+ break;
+
+ command_r = nextcmd;
+ handle_rw = sync_state->batch_hostcmd->handle;
+ time_r = sync_state->batch_hostcmd->time_arg;
+ hostcmd_handle_rw = sync_state->batch_hostcmd->hostcmd_handle;
+
+ goldfish_sync_cmd_queue(
+ sync_state,
+ command_r,
+ handle_rw,
+ time_r,
+ hostcmd_handle_rw);
+
+ count++;
+ }
+
+ spin_unlock(&sync_state->lock);
+
+ schedule_work(&sync_state->work_item);
+
+ return (count == 0) ? IRQ_NONE : IRQ_HANDLED;
+}
+
+/* |goldfish_sync_work_item_fn| does the actual work of servicing
+ * host->guest sync commands. This function is triggered whenever
+ * the IRQ for the goldfish sync device is raised. Once it starts
+ * running, it grabs the contents of the buffer containing the
+ * commands it needs to execute (there may be multiple, because
+ * our IRQ is active high and not edge triggered), and then
+ * runs all of them one after the other.
+ */
+static void goldfish_sync_work_item_fn(struct work_struct *input)
+{
+
+ struct goldfish_sync_state *sync_state;
+ int sync_fence_fd;
+
+ struct goldfish_sync_timeline_obj *timeline;
+ uint64_t timeline_ptr;
+
+ uint64_t hostcmd_handle;
+
+ uint32_t cmd;
+ uint64_t handle;
+ uint32_t time_arg;
+
+ struct goldfish_sync_hostcmd *todo;
+ uint32_t todo_end;
+
+ unsigned long irq_flags;
+
+ struct goldfish_sync_hostcmd to_run[GOLDFISH_SYNC_MAX_CMDS];
+ uint32_t i = 0;
+
+ sync_state = container_of(input, struct goldfish_sync_state, work_item);
+
+ mutex_lock(&sync_state->mutex_lock);
+
+ spin_lock_irqsave(&sync_state->lock, irq_flags); {
+
+ todo_end = sync_state->to_do_end;
+
+ DPRINT("num sync todos: %u", sync_state->to_do_end);
+
+ for (i = 0; i < todo_end; i++)
+ to_run[i] = sync_state->to_do[i];
+
+ /* We expect that commands will come in at a slow enough rate
+ * so that incoming items will not be more than
+ * GOLDFISH_SYNC_MAX_CMDS.
+ *
+ * This is because the way the sync device is used,
+ * it's only for managing buffer data transfers per frame,
+ * with a sequential dependency between putting things in
+ * to_do and taking them out. Once a set of commands is
+ * queued up in to_do, the user of the device waits for
+ * them to be processed before queuing additional commands,
+ * which limits the rate at which commands come in
+ * to the rate at which we take them out here.
+ *
+ * We also don't expect more than MAX_CMDS to be issued
+ * at once; there is a correspondence between
+ * which buffers need swapping to the (display / buffer queue)
+ * to particular commands, and we don't expect there to be
+ * enough display or buffer queues in operation at once
+ * to overrun GOLDFISH_SYNC_MAX_CMDS.
+ */
+ sync_state->to_do_end = 0;
+
+ } spin_unlock_irqrestore(&sync_state->lock, irq_flags);
+
+ for (i = 0; i < todo_end; i++) {
+ DPRINT("todo index: %u", i);
+
+ todo = &to_run[i];
+
+ cmd = todo->cmd;
+
+ handle = (uint64_t)todo->handle;
+ time_arg = todo->time_arg;
+ hostcmd_handle = (uint64_t)todo->hostcmd_handle;
+
+ DTRACE();
+
+ timeline = (struct goldfish_sync_timeline_obj *)(uintptr_t)handle;
+
+ switch (cmd) {
+ case CMD_SYNC_READY:
+ break;
+ case CMD_CREATE_SYNC_TIMELINE:
+ DPRINT("exec CMD_CREATE_SYNC_TIMELINE: "
+ "handle=0x%llx time_arg=%d",
+ handle, time_arg);
+ timeline = goldfish_sync_timeline_create();
+ timeline_ptr = (uintptr_t)timeline;
+ goldfish_sync_hostcmd_reply(sync_state, CMD_CREATE_SYNC_TIMELINE,
+ timeline_ptr,
+ 0,
+ hostcmd_handle);
+ DPRINT("sync timeline created: %p", timeline);
+ break;
+ case CMD_CREATE_SYNC_FENCE:
+ DPRINT("exec CMD_CREATE_SYNC_FENCE: "
+ "handle=0x%llx time_arg=%d",
+ handle, time_arg);
+ sync_fence_fd = goldfish_sync_fence_create(timeline, time_arg);
+ goldfish_sync_hostcmd_reply(sync_state, CMD_CREATE_SYNC_FENCE,
+ sync_fence_fd,
+ 0,
+ hostcmd_handle);
+ break;
+ case CMD_SYNC_TIMELINE_INC:
+ DPRINT("exec CMD_SYNC_TIMELINE_INC: "
+ "handle=0x%llx time_arg=%d",
+ handle, time_arg);
+ goldfish_sync_timeline_inc(timeline, time_arg);
+ break;
+ case CMD_DESTROY_SYNC_TIMELINE:
+ DPRINT("exec CMD_DESTROY_SYNC_TIMELINE: "
+ "handle=0x%llx time_arg=%d",
+ handle, time_arg);
+ goldfish_sync_timeline_destroy(timeline);
+ break;
+ }
+ DPRINT("Done executing sync command");
+ }
+ mutex_unlock(&sync_state->mutex_lock);
+}
+
+/* Guest-side interface: file operations */
+
+/* Goldfish sync context and ioctl info.
+ *
+ * When a sync context is created by open()-ing the goldfish sync device, we
+ * create a sync context (|goldfish_sync_context|).
+ *
+ * Currently, the only data required to track is the sync timeline itself
+ * along with the current time, which are all packed up in the
+ * |goldfish_sync_timeline_obj| field. We use a |goldfish_sync_context|
+ * as the filp->private_data.
+ *
+ * Next, when a sync context user requests that work be queued and a fence
+ * fd provided, we use the |goldfish_sync_ioctl_info| struct, which holds
+ * information about which host handles to touch for this particular
+ * queue-work operation. We need to know about the host-side sync thread
+ * and the particular host-side GLsync object. We also possibly write out
+ * a file descriptor.
+ */
+struct goldfish_sync_context {
+ struct goldfish_sync_timeline_obj *timeline;
+};
+
+struct goldfish_sync_ioctl_info {
+ uint64_t host_glsync_handle_in;
+ uint64_t host_syncthread_handle_in;
+ int fence_fd_out;
+};
+
+static int goldfish_sync_open(struct inode *inode, struct file *file)
+{
+
+ struct goldfish_sync_context *sync_context;
+
+ DTRACE();
+
+ mutex_lock(&global_sync_state->mutex_lock);
+
+ sync_context = kzalloc(sizeof(struct goldfish_sync_context), GFP_KERNEL);
+
+ if (sync_context == NULL) {
+ ERR("Creation of goldfish sync context failed!");
+ mutex_unlock(&global_sync_state->mutex_lock);
+ return -ENOMEM;
+ }
+
+ sync_context->timeline = NULL;
+
+ file->private_data = sync_context;
+
+ DPRINT("successfully create a sync context @0x%p", sync_context);
+
+ mutex_unlock(&global_sync_state->mutex_lock);
+
+ return 0;
+}
+
+static int goldfish_sync_release(struct inode *inode, struct file *file)
+{
+
+ struct goldfish_sync_context *sync_context;
+
+ DTRACE();
+
+ mutex_lock(&global_sync_state->mutex_lock);
+
+ sync_context = file->private_data;
+
+ if (sync_context->timeline)
+ goldfish_sync_timeline_destroy(sync_context->timeline);
+
+ sync_context->timeline = NULL;
+
+ kfree(sync_context);
+
+ mutex_unlock(&global_sync_state->mutex_lock);
+
+ return 0;
+}
+
+/* |goldfish_sync_ioctl| is the guest-facing interface of goldfish sync
+ * and is used in conjunction with eglCreateSyncKHR to queue up the
+ * actual work of waiting for the EGL sync command to complete,
+ * possibly returning a fence fd to the guest.
+ */
+static long goldfish_sync_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct goldfish_sync_context *sync_context_data;
+ struct goldfish_sync_timeline_obj *timeline;
+ int fd_out;
+ struct goldfish_sync_ioctl_info ioctl_data;
+
+ DTRACE();
+
+ sync_context_data = file->private_data;
+ fd_out = -1;
+
+ switch (cmd) {
+ case GOLDFISH_SYNC_IOC_QUEUE_WORK:
+
+ DPRINT("exec GOLDFISH_SYNC_IOC_QUEUE_WORK");
+
+ mutex_lock(&global_sync_state->mutex_lock);
+
+ if (copy_from_user(&ioctl_data,
+ (void __user *)arg,
+ sizeof(ioctl_data))) {
+ ERR("Failed to copy memory for ioctl_data from user.");
+ mutex_unlock(&global_sync_state->mutex_lock);
+ return -EFAULT;
+ }
+
+ if (ioctl_data.host_syncthread_handle_in == 0) {
+ DPRINT("Error: zero host syncthread handle!!!");
+ mutex_unlock(&global_sync_state->mutex_lock);
+ return -EFAULT;
+ }
+
+ if (!sync_context_data->timeline) {
+ DPRINT("no timeline yet, create one.");
+ sync_context_data->timeline = goldfish_sync_timeline_create();
+ DPRINT("timeline: 0x%p", &sync_context_data->timeline);
+ }
+
+ timeline = sync_context_data->timeline;
+ fd_out = goldfish_sync_fence_create(timeline,
+ timeline->current_time + 1);
+ DPRINT("Created fence with fd %d and current time %u (timeline: 0x%p)",
+ fd_out,
+ sync_context_data->timeline->current_time + 1,
+ sync_context_data->timeline);
+
+ ioctl_data.fence_fd_out = fd_out;
+
+ if (copy_to_user((void __user *)arg,
+ &ioctl_data,
+ sizeof(ioctl_data))) {
+ DPRINT("Error, could not copy to user!!!");
+
+ sys_close(fd_out);
+ /* We won't be doing an increment, kref_put immediately. */
+ kref_put(&timeline->kref, delete_timeline_obj);
+ mutex_unlock(&global_sync_state->mutex_lock);
+ return -EFAULT;
+ }
+
+ /* We are now about to trigger a host-side wait;
+ * accumulate on |pending_waits|. */
+ goldfish_sync_send_guestcmd(global_sync_state,
+ CMD_TRIGGER_HOST_WAIT,
+ ioctl_data.host_glsync_handle_in,
+ ioctl_data.host_syncthread_handle_in,
+ (uint64_t)(uintptr_t)(sync_context_data->timeline));
+
+ mutex_unlock(&global_sync_state->mutex_lock);
+ return 0;
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations goldfish_sync_fops = {
+ .owner = THIS_MODULE,
+ .open = goldfish_sync_open,
+ .release = goldfish_sync_release,
+ .unlocked_ioctl = goldfish_sync_ioctl,
+ .compat_ioctl = goldfish_sync_ioctl,
+};
+
+static struct miscdevice goldfish_sync_device = {
+ .name = "goldfish_sync",
+ .fops = &goldfish_sync_fops,
+};
+
+
+static bool setup_verify_batch_cmd_addr(struct goldfish_sync_state *sync_state,
+ void *batch_addr,
+ uint32_t addr_offset,
+ uint32_t addr_offset_high)
+{
+ uint64_t batch_addr_phys;
+ uint32_t batch_addr_phys_test_lo;
+ uint32_t batch_addr_phys_test_hi;
+
+ if (!batch_addr) {
+ ERR("Could not use batch command address!");
+ return false;
+ }
+
+ batch_addr_phys = virt_to_phys(batch_addr);
+ writel((uint32_t)(batch_addr_phys),
+ sync_state->reg_base + addr_offset);
+ writel((uint32_t)(batch_addr_phys >> 32),
+ sync_state->reg_base + addr_offset_high);
+
+ batch_addr_phys_test_lo =
+ readl(sync_state->reg_base + addr_offset);
+ batch_addr_phys_test_hi =
+ readl(sync_state->reg_base + addr_offset_high);
+
+ if (virt_to_phys(batch_addr) !=
+ (((uint64_t)batch_addr_phys_test_hi << 32) |
+ batch_addr_phys_test_lo)) {
+ ERR("Invalid batch command address!");
+ return false;
+ }
+
+ return true;
+}
+
+int goldfish_sync_probe(struct platform_device *pdev)
+{
+ struct resource *ioresource;
+ struct goldfish_sync_state *sync_state = global_sync_state;
+ int status;
+
+ DTRACE();
+
+ sync_state->to_do_end = 0;
+
+ spin_lock_init(&sync_state->lock);
+ mutex_init(&sync_state->mutex_lock);
+
+ platform_set_drvdata(pdev, sync_state);
+
+ ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (ioresource == NULL) {
+ ERR("platform_get_resource failed");
+ return -ENODEV;
+ }
+
+ sync_state->reg_base =
+ devm_ioremap(&pdev->dev, ioresource->start, PAGE_SIZE);
+ if (sync_state->reg_base == NULL) {
+ ERR("Could not ioremap");
+ return -ENOMEM;
+ }
+
+ sync_state->irq = platform_get_irq(pdev, 0);
+ if (sync_state->irq < 0) {
+ ERR("Could not platform_get_irq");
+ return -ENODEV;
+ }
+
+ status = devm_request_irq(&pdev->dev,
+ sync_state->irq,
+ goldfish_sync_interrupt,
+ IRQF_SHARED,
+ pdev->name,
+ sync_state);
+ if (status) {
+ ERR("request_irq failed");
+ return -ENODEV;
+ }
+
+ INIT_WORK(&sync_state->work_item,
+ goldfish_sync_work_item_fn);
+
+ misc_register(&goldfish_sync_device);
+
+ /* Obtain addresses for batch send/recv of commands. */
+ {
+ struct goldfish_sync_hostcmd *batch_addr_hostcmd;
+ struct goldfish_sync_guestcmd *batch_addr_guestcmd;
+
+ batch_addr_hostcmd =
+ devm_kzalloc(&pdev->dev, sizeof(struct goldfish_sync_hostcmd),
+ GFP_KERNEL);
+ batch_addr_guestcmd =
+ devm_kzalloc(&pdev->dev, sizeof(struct goldfish_sync_guestcmd),
+ GFP_KERNEL);
+
+ if (!setup_verify_batch_cmd_addr(sync_state,
+ batch_addr_hostcmd,
+ SYNC_REG_BATCH_COMMAND_ADDR,
+ SYNC_REG_BATCH_COMMAND_ADDR_HIGH)) {
+ ERR("goldfish_sync: Could not setup batch command address");
+ return -ENODEV;
+ }
+
+ if (!setup_verify_batch_cmd_addr(sync_state,
+ batch_addr_guestcmd,
+ SYNC_REG_BATCH_GUESTCOMMAND_ADDR,
+ SYNC_REG_BATCH_GUESTCOMMAND_ADDR_HIGH)) {
+ ERR("goldfish_sync: Could not setup batch guest command address");
+ return -ENODEV;
+ }
+
+ sync_state->batch_hostcmd = batch_addr_hostcmd;
+ sync_state->batch_guestcmd = batch_addr_guestcmd;
+ }
+
+ INFO("goldfish_sync: Initialized goldfish sync device");
+
+ writel(0, sync_state->reg_base + SYNC_REG_INIT);
+
+ return 0;
+}
+
+static int goldfish_sync_remove(struct platform_device *pdev)
+{
+ struct goldfish_sync_state *sync_state = global_sync_state;
+
+ DTRACE();
+
+ misc_deregister(&goldfish_sync_device);
+ memset(sync_state, 0, sizeof(struct goldfish_sync_state));
+ return 0;
+}
+
+static const struct of_device_id goldfish_sync_of_match[] = {
+ { .compatible = "google,goldfish-sync", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_sync_of_match);
+
+static const struct acpi_device_id goldfish_sync_acpi_match[] = {
+ { "GFSH0006", 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(acpi, goldfish_sync_acpi_match);
+
+static struct platform_driver goldfish_sync = {
+ .probe = goldfish_sync_probe,
+ .remove = goldfish_sync_remove,
+ .driver = {
+ .name = "goldfish_sync",
+ .of_match_table = goldfish_sync_of_match,
+ .acpi_match_table = ACPI_PTR(goldfish_sync_acpi_match),
+ }
+};
+
+module_platform_driver(goldfish_sync);
+
+MODULE_AUTHOR("Google, Inc.");
+MODULE_DESCRIPTION("Android QEMU Sync Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
diff --git a/drivers/staging/goldfish/goldfish_sync_timeline_fence.c b/drivers/staging/goldfish/goldfish_sync_timeline_fence.c
new file mode 100644
index 0000000..e671618
--- /dev/null
+++ b/drivers/staging/goldfish/goldfish_sync_timeline_fence.c
@@ -0,0 +1,254 @@
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/syscalls.h>
+#include <linux/sync_file.h>
+#include <linux/fence.h>
+
+#include "goldfish_sync_timeline_fence.h"
+
+/*
+ * Timeline-based sync for Goldfish Sync
+ * Based on "Sync File validation framework"
+ * (drivers/dma-buf/sw_sync.c)
+ *
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/**
+ * struct goldfish_sync_timeline - sync object
+ * @kref: reference count on fence.
+ * @name: name of the goldfish_sync_timeline. Useful for debugging
+ * @child_list_head: list of children sync_pts for this goldfish_sync_timeline
+ * @child_list_lock: lock protecting @child_list_head and fence.status
+ * @active_list_head: list of active (unsignaled/errored) sync_pts
+ */
+struct goldfish_sync_timeline {
+ struct kref kref;
+ char name[32];
+
+ /* protected by child_list_lock */
+ u64 context;
+ int value;
+
+ struct list_head child_list_head;
+ spinlock_t child_list_lock;
+
+ struct list_head active_list_head;
+};
+
+static inline struct goldfish_sync_timeline *fence_parent(struct fence *fence)
+{
+ return container_of(fence->lock, struct goldfish_sync_timeline,
+ child_list_lock);
+}
+
+static const struct fence_ops goldfish_sync_timeline_fence_ops;
+
+static inline struct sync_pt *goldfish_sync_fence_to_sync_pt(struct fence *fence)
+{
+ if (fence->ops != &goldfish_sync_timeline_fence_ops)
+ return NULL;
+ return container_of(fence, struct sync_pt, base);
+}
+
+/**
+ * goldfish_sync_timeline_create_internal() - creates a sync object
+ * @name: sync_timeline name
+ *
+ * Creates a new sync_timeline. Returns the sync_timeline object or NULL in
+ * case of error.
+ */
+struct goldfish_sync_timeline
+*goldfish_sync_timeline_create_internal(const char *name)
+{
+ struct goldfish_sync_timeline *obj;
+
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (!obj)
+ return NULL;
+
+ kref_init(&obj->kref);
+ obj->context = fence_context_alloc(1);
+ strlcpy(obj->name, name, sizeof(obj->name));
+
+ INIT_LIST_HEAD(&obj->child_list_head);
+ INIT_LIST_HEAD(&obj->active_list_head);
+ spin_lock_init(&obj->child_list_lock);
+
+ return obj;
+}
+
+static void goldfish_sync_timeline_free_internal(struct kref *kref)
+{
+ struct goldfish_sync_timeline *obj =
+ container_of(kref, struct goldfish_sync_timeline, kref);
+
+ kfree(obj);
+}
+
+static void goldfish_sync_timeline_get_internal(
+ struct goldfish_sync_timeline *obj)
+{
+ kref_get(&obj->kref);
+}
+
+void goldfish_sync_timeline_put_internal(struct goldfish_sync_timeline *obj)
+{
+ kref_put(&obj->kref, goldfish_sync_timeline_free_internal);
+}
+
+/**
+ * goldfish_sync_timeline_signal() -
+ * signal a status change on a goldfish_sync_timeline
+ * @obj: sync_timeline to signal
+ * @inc: num to increment on timeline->value
+ *
+ * A sync implementation should call this any time one of it's fences
+ * has signaled or has an error condition.
+ */
+void goldfish_sync_timeline_signal_internal(struct goldfish_sync_timeline *obj,
+ unsigned int inc)
+{
+ unsigned long flags;
+ struct sync_pt *pt, *next;
+
+ spin_lock_irqsave(&obj->child_list_lock, flags);
+
+ obj->value += inc;
+
+ list_for_each_entry_safe(pt, next, &obj->active_list_head,
+ active_list) {
+ if (fence_is_signaled_locked(&pt->base))
+ list_del_init(&pt->active_list);
+ }
+
+ spin_unlock_irqrestore(&obj->child_list_lock, flags);
+}
+
+/**
+ * goldfish_sync_pt_create_internal() - creates a sync pt
+ * @parent: fence's parent sync_timeline
+ * @size: size to allocate for this pt
+ * @inc: value of the fence
+ *
+ * Creates a new sync_pt as a child of @parent. @size bytes will be
+ * allocated allowing for implementation specific data to be kept after
+ * the generic sync_timeline struct. Returns the sync_pt object or
+ * NULL in case of error.
+ */
+struct sync_pt *goldfish_sync_pt_create_internal(
+ struct goldfish_sync_timeline *obj, int size,
+ unsigned int value)
+{
+ unsigned long flags;
+ struct sync_pt *pt;
+
+ if (size < sizeof(*pt))
+ return NULL;
+
+ pt = kzalloc(size, GFP_KERNEL);
+ if (!pt)
+ return NULL;
+
+ spin_lock_irqsave(&obj->child_list_lock, flags);
+ goldfish_sync_timeline_get_internal(obj);
+ fence_init(&pt->base, &goldfish_sync_timeline_fence_ops, &obj->child_list_lock,
+ obj->context, value);
+ list_add_tail(&pt->child_list, &obj->child_list_head);
+ INIT_LIST_HEAD(&pt->active_list);
+ spin_unlock_irqrestore(&obj->child_list_lock, flags);
+ return pt;
+}
+
+static const char *goldfish_sync_timeline_fence_get_driver_name(
+ struct fence *fence)
+{
+ return "sw_sync";
+}
+
+static const char *goldfish_sync_timeline_fence_get_timeline_name(
+ struct fence *fence)
+{
+ struct goldfish_sync_timeline *parent = fence_parent(fence);
+
+ return parent->name;
+}
+
+static void goldfish_sync_timeline_fence_release(struct fence *fence)
+{
+ struct sync_pt *pt = goldfish_sync_fence_to_sync_pt(fence);
+ struct goldfish_sync_timeline *parent = fence_parent(fence);
+ unsigned long flags;
+
+ spin_lock_irqsave(fence->lock, flags);
+ list_del(&pt->child_list);
+ if (!list_empty(&pt->active_list))
+ list_del(&pt->active_list);
+ spin_unlock_irqrestore(fence->lock, flags);
+
+ goldfish_sync_timeline_put_internal(parent);
+ fence_free(fence);
+}
+
+static bool goldfish_sync_timeline_fence_signaled(struct fence *fence)
+{
+ struct goldfish_sync_timeline *parent = fence_parent(fence);
+
+ return (fence->seqno > parent->value) ? false : true;
+}
+
+static bool goldfish_sync_timeline_fence_enable_signaling(struct fence *fence)
+{
+ struct sync_pt *pt = goldfish_sync_fence_to_sync_pt(fence);
+ struct goldfish_sync_timeline *parent = fence_parent(fence);
+
+ if (goldfish_sync_timeline_fence_signaled(fence))
+ return false;
+
+ list_add_tail(&pt->active_list, &parent->active_list_head);
+ return true;
+}
+
+static void goldfish_sync_timeline_fence_disable_signaling(struct fence *fence)
+{
+ struct sync_pt *pt = container_of(fence, struct sync_pt, base);
+
+ list_del_init(&pt->active_list);
+}
+
+static void goldfish_sync_timeline_fence_value_str(struct fence *fence,
+ char *str, int size)
+{
+ snprintf(str, size, "%d", fence->seqno);
+}
+
+static void goldfish_sync_timeline_fence_timeline_value_str(
+ struct fence *fence,
+ char *str, int size)
+{
+ struct goldfish_sync_timeline *parent = fence_parent(fence);
+
+ snprintf(str, size, "%d", parent->value);
+}
+
+static const struct fence_ops goldfish_sync_timeline_fence_ops = {
+ .get_driver_name = goldfish_sync_timeline_fence_get_driver_name,
+ .get_timeline_name = goldfish_sync_timeline_fence_get_timeline_name,
+ .enable_signaling = goldfish_sync_timeline_fence_enable_signaling,
+ .disable_signaling = goldfish_sync_timeline_fence_disable_signaling,
+ .signaled = goldfish_sync_timeline_fence_signaled,
+ .wait = fence_default_wait,
+ .release = goldfish_sync_timeline_fence_release,
+ .fence_value_str = goldfish_sync_timeline_fence_value_str,
+ .timeline_value_str = goldfish_sync_timeline_fence_timeline_value_str,
+};
diff --git a/drivers/staging/goldfish/goldfish_sync_timeline_fence.h b/drivers/staging/goldfish/goldfish_sync_timeline_fence.h
new file mode 100644
index 0000000..fc25924
--- /dev/null
+++ b/drivers/staging/goldfish/goldfish_sync_timeline_fence.h
@@ -0,0 +1,58 @@
+#include <linux/sync_file.h>
+#include <linux/fence.h>
+
+/**
+ * struct sync_pt - sync_pt object
+ * @base: base fence object
+ * @child_list: sync timeline child's list
+ * @active_list: sync timeline active child's list
+ */
+struct sync_pt {
+ struct fence base;
+ struct list_head child_list;
+ struct list_head active_list;
+};
+
+/**
+ * goldfish_sync_timeline_create_internal() - creates a sync object
+ * @name: goldfish_sync_timeline name
+ *
+ * Creates a new goldfish_sync_timeline.
+ * Returns the goldfish_sync_timeline object or NULL in case of error.
+ */
+struct goldfish_sync_timeline
+*goldfish_sync_timeline_create_internal(const char *name);
+
+/**
+ * goldfish_sync_pt_create_internal() - creates a sync pt
+ * @parent: fence's parent goldfish_sync_timeline
+ * @size: size to allocate for this pt
+ * @inc: value of the fence
+ *
+ * Creates a new sync_pt as a child of @parent. @size bytes will be
+ * allocated allowing for implementation specific data to be kept after
+ * the generic sync_timeline struct. Returns the sync_pt object or
+ * NULL in case of error.
+ */
+struct sync_pt
+*goldfish_sync_pt_create_internal(struct goldfish_sync_timeline *obj,
+ int size, unsigned int value);
+
+/**
+ * goldfish_sync_timeline_signal_internal() -
+ * signal a status change on a sync_timeline
+ * @obj: goldfish_sync_timeline to signal
+ * @inc: num to increment on timeline->value
+ *
+ * A sync implementation should call this any time one of it's fences
+ * has signaled or has an error condition.
+ */
+void goldfish_sync_timeline_signal_internal(struct goldfish_sync_timeline *obj,
+ unsigned int inc);
+
+/**
+ * goldfish_sync_timeline_put_internal() - dec refcount of a sync_timeline
+ * and clean up memory if it was the last ref.
+ * @obj: goldfish_sync_timeline to decref
+ */
+void goldfish_sync_timeline_put_internal(struct goldfish_sync_timeline *obj);
diff --git a/drivers/staging/greybus/timesync_platform.c b/drivers/staging/greybus/timesync_platform.c
index 113f3d6..27f75b1 100644
--- a/drivers/staging/greybus/timesync_platform.c
+++ b/drivers/staging/greybus/timesync_platform.c
@@ -45,12 +45,18 @@
int gb_timesync_platform_lock_bus(struct gb_timesync_svc *pdata)
{
+ if (!arche_platform_change_state_cb)
+ return 0;
+
return arche_platform_change_state_cb(ARCHE_PLATFORM_STATE_TIME_SYNC,
pdata);
}
void gb_timesync_platform_unlock_bus(void)
{
+ if (!arche_platform_change_state_cb)
+ return;
+
arche_platform_change_state_cb(ARCHE_PLATFORM_STATE_ACTIVE, NULL);
}
diff --git a/drivers/staging/iio/adc/ad7606_core.c b/drivers/staging/iio/adc/ad7606_core.c
index f79ee61..cbd6bc5 100644
--- a/drivers/staging/iio/adc/ad7606_core.c
+++ b/drivers/staging/iio/adc/ad7606_core.c
@@ -189,7 +189,7 @@
mutex_lock(&indio_dev->mlock);
gpio_set_value(st->pdata->gpio_os0, (ret >> 0) & 1);
gpio_set_value(st->pdata->gpio_os1, (ret >> 1) & 1);
- gpio_set_value(st->pdata->gpio_os1, (ret >> 2) & 1);
+ gpio_set_value(st->pdata->gpio_os2, (ret >> 2) & 1);
st->oversampling = lval;
mutex_unlock(&indio_dev->mlock);
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_pool.c b/drivers/staging/lustre/lustre/ldlm/ldlm_pool.c
index 9a1136e3..34efb49 100644
--- a/drivers/staging/lustre/lustre/ldlm/ldlm_pool.c
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_pool.c
@@ -356,10 +356,10 @@
u32 recalc_interval_sec;
int count;
- recalc_interval_sec = ktime_get_seconds() - pl->pl_recalc_time;
+ recalc_interval_sec = ktime_get_real_seconds() - pl->pl_recalc_time;
if (recalc_interval_sec > 0) {
spin_lock(&pl->pl_lock);
- recalc_interval_sec = ktime_get_seconds() - pl->pl_recalc_time;
+ recalc_interval_sec = ktime_get_real_seconds() - pl->pl_recalc_time;
if (recalc_interval_sec > 0) {
/*
@@ -382,7 +382,7 @@
count);
}
- recalc_interval_sec = pl->pl_recalc_time - ktime_get_seconds() +
+ recalc_interval_sec = pl->pl_recalc_time - ktime_get_real_seconds() +
pl->pl_recalc_period;
if (recalc_interval_sec <= 0) {
/* DEBUG: should be re-removed after LU-4536 is fixed */
@@ -657,7 +657,7 @@
spin_lock_init(&pl->pl_lock);
atomic_set(&pl->pl_granted, 0);
- pl->pl_recalc_time = ktime_get_seconds();
+ pl->pl_recalc_time = ktime_get_real_seconds();
atomic_set(&pl->pl_lock_volume_factor, 1);
atomic_set(&pl->pl_grant_rate, 0);
diff --git a/drivers/staging/lustre/lustre/osc/osc_page.c b/drivers/staging/lustre/lustre/osc/osc_page.c
index 2a7a70a..9168451 100644
--- a/drivers/staging/lustre/lustre/osc/osc_page.c
+++ b/drivers/staging/lustre/lustre/osc/osc_page.c
@@ -542,7 +542,6 @@
struct cl_object *clobj = NULL;
struct cl_page **pvec;
struct osc_page *opg;
- struct osc_page *temp;
int maxscan = 0;
long count = 0;
int index = 0;
@@ -569,13 +568,15 @@
spin_lock(&cli->cl_lru_list_lock);
maxscan = min(target << 1, atomic_long_read(&cli->cl_lru_in_list));
- list_for_each_entry_safe(opg, temp, &cli->cl_lru_list, ops_lru) {
+ while (!list_empty(&cli->cl_lru_list)) {
struct cl_page *page;
bool will_free = false;
if (--maxscan < 0)
break;
+ opg = list_entry(cli->cl_lru_list.next, struct osc_page,
+ ops_lru);
page = opg->ops_cl.cpl_page;
if (lru_page_busy(cli, page)) {
list_move_tail(&opg->ops_lru, &cli->cl_lru_list);
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c
index 8be9f85..89dd6b9 100644
--- a/drivers/staging/media/davinci_vpfe/vpfe_video.c
+++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c
@@ -1362,7 +1362,7 @@
ret = vb2_queue_init(q);
if (ret) {
v4l2_err(&vpfe_dev->v4l2_dev, "vb2_queue_init() failed\n");
- return ret;
+ goto unlock_out;
}
fh->io_allowed = 1;
diff --git a/drivers/staging/media/s5p-cec/s5p_cec.c b/drivers/staging/media/s5p-cec/s5p_cec.c
index 1780a08..58d7562 100644
--- a/drivers/staging/media/s5p-cec/s5p_cec.c
+++ b/drivers/staging/media/s5p-cec/s5p_cec.c
@@ -231,7 +231,7 @@
return 0;
}
-static int s5p_cec_runtime_suspend(struct device *dev)
+static int __maybe_unused s5p_cec_runtime_suspend(struct device *dev)
{
struct s5p_cec_dev *cec = dev_get_drvdata(dev);
@@ -239,7 +239,7 @@
return 0;
}
-static int s5p_cec_runtime_resume(struct device *dev)
+static int __maybe_unused s5p_cec_runtime_resume(struct device *dev)
{
struct s5p_cec_dev *cec = dev_get_drvdata(dev);
int ret;
diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c
index d02e3e3..1235444 100644
--- a/drivers/staging/octeon/ethernet.c
+++ b/drivers/staging/octeon/ethernet.c
@@ -776,6 +776,7 @@
/* Initialize the device private structure. */
struct octeon_ethernet *priv = netdev_priv(dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
dev->netdev_ops = &cvm_oct_pow_netdev_ops;
priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED;
priv->port = CVMX_PIP_NUM_INPUT_PORTS;
@@ -820,6 +821,7 @@
}
/* Initialize the device private structure. */
+ SET_NETDEV_DEV(dev, &pdev->dev);
priv = netdev_priv(dev);
priv->netdev = dev;
priv->of_node = cvm_oct_node_for_port(pip, interface,
diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c
index 923c032..e980e2d 100644
--- a/drivers/target/iscsi/iscsi_target_configfs.c
+++ b/drivers/target/iscsi/iscsi_target_configfs.c
@@ -100,8 +100,10 @@
tpg_np_new = iscsit_tpg_add_network_portal(tpg,
&np->np_sockaddr, tpg_np, type);
- if (IS_ERR(tpg_np_new))
+ if (IS_ERR(tpg_np_new)) {
+ rc = PTR_ERR(tpg_np_new);
goto out;
+ }
} else {
tpg_np_new = iscsit_tpg_locate_child_np(tpg_np, type);
if (tpg_np_new) {
diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c
index 0814e58..205a509 100644
--- a/drivers/target/iscsi/iscsi_target_tpg.c
+++ b/drivers/target/iscsi/iscsi_target_tpg.c
@@ -260,7 +260,6 @@
iscsi_release_param_list(tpg->param_list);
tpg->param_list = NULL;
}
- kfree(tpg);
return -ENOMEM;
}
diff --git a/drivers/target/sbp/sbp_target.c b/drivers/target/sbp/sbp_target.c
index 58bb6ed..6ca388e 100644
--- a/drivers/target/sbp/sbp_target.c
+++ b/drivers/target/sbp/sbp_target.c
@@ -928,7 +928,7 @@
struct sbp_target_request *req;
int tag;
- tag = percpu_ida_alloc(&se_sess->sess_tag_pool, GFP_ATOMIC);
+ tag = percpu_ida_alloc(&se_sess->sess_tag_pool, TASK_RUNNING);
if (tag < 0)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c
index 4756250..70c143a 100644
--- a/drivers/target/target_core_user.c
+++ b/drivers/target/target_core_user.c
@@ -685,8 +685,6 @@
target_complete_cmd(cmd->se_cmd, SAM_STAT_CHECK_CONDITION);
cmd->se_cmd = NULL;
- kmem_cache_free(tcmu_cmd_cache, cmd);
-
return 0;
}
diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c
index c41c774..2dcd419 100644
--- a/drivers/thermal/thermal_hwmon.c
+++ b/drivers/thermal/thermal_hwmon.c
@@ -98,7 +98,7 @@
int temperature;
int ret;
- ret = tz->ops->get_trip_temp(tz, 0, &temperature);
+ ret = tz->ops->get_crit_temp(tz, &temperature);
if (ret)
return ret;
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 240a361..e8819aa 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -675,7 +675,7 @@
.device = uart_console_device,
.setup = univ8250_console_setup,
.match = univ8250_console_match,
- .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_CONSDEV,
+ .flags = CON_PRINTBUFFER | CON_ANYTIME,
.index = -1,
.data = &serial8250_reg,
};
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 1731b98..080d5a5 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -1411,7 +1411,7 @@
* Enable previously disabled RX interrupts.
*/
if (!(p->port.rs485.flags & SER_RS485_RX_DURING_TX)) {
- serial8250_clear_fifos(p);
+ serial8250_clear_and_reinit_fifos(p);
p->ier |= UART_IER_RLSI | UART_IER_RDI;
serial_port_out(&p->port, UART_IER, p->ier);
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 168b10c..fabbe76 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -481,6 +481,14 @@
/* disable PDC transmit */
atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS);
}
+
+ /*
+ * Disable the transmitter.
+ * This is mandatory when DMA is used, otherwise the DMA buffer
+ * is fully transmitted.
+ */
+ atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS);
+
/* Disable interrupts */
atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
@@ -513,6 +521,9 @@
/* Enable interrupts */
atmel_uart_writel(port, ATMEL_US_IER, atmel_port->tx_done_mask);
+
+ /* re-enable the transmitter */
+ atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
}
/*
@@ -798,6 +809,11 @@
*/
if (!uart_circ_empty(xmit))
atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx);
+ else if ((port->rs485.flags & SER_RS485_ENABLED) &&
+ !(port->rs485.flags & SER_RS485_RX_DURING_TX)) {
+ /* DMA done, stop TX, start RX for RS485 */
+ atmel_start_rx(port);
+ }
spin_unlock_irqrestore(&port->lock, flags);
}
@@ -900,12 +916,6 @@
desc->callback = atmel_complete_tx_dma;
desc->callback_param = atmel_port;
atmel_port->cookie_tx = dmaengine_submit(desc);
-
- } else {
- if (port->rs485.flags & SER_RS485_ENABLED) {
- /* DMA done, stop TX, start RX for RS485 */
- atmel_start_rx(port);
- }
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index fb06725..7933954 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -1264,7 +1264,7 @@
/* Setup interrupt */
ret = devm_request_irq(dev, irq, sc16is7xx_irq,
- IRQF_ONESHOT | flags, dev_name(dev), s);
+ flags, dev_name(dev), s);
if (!ret)
return 0;
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index 52bbd27..701c085 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -946,8 +946,8 @@
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT,
- .evbit = { BIT_MASK(EV_KEY) },
- .keybit = { BIT_MASK(KEY_LEFTALT) },
+ .evbit = { [BIT_WORD(EV_KEY)] = BIT_MASK(EV_KEY) },
+ .keybit = { [BIT_WORD(KEY_LEFTALT)] = BIT_MASK(KEY_LEFTALT) },
},
{ },
};
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 0f8caae..ece10e6 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -982,7 +982,7 @@
KBD_LED_TRIGGER((_led_bit) + 8, _name)
static struct kbd_led_trigger kbd_led_triggers[] = {
- KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrollock"),
+ KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrolllock"),
KBD_LED_TRIGGER(VC_NUMLOCK, "kbd-numlock"),
KBD_LED_TRIGGER(VC_CAPSLOCK, "kbd-capslock"),
KBD_LED_TRIGGER(VC_KANALOCK, "kbd-kanalock"),
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index fada988..c5ff13f 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1719,6 +1719,7 @@
{ USB_DEVICE(0x20df, 0x0001), /* Simtec Electronics Entropy Key */
.driver_info = QUIRK_CONTROL_LINE_STATE, },
{ USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
+ { USB_DEVICE(0x2184, 0x0036) }, /* GW Instek AFG-125 */
{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
},
/* Motorola H24 HSPA module: */
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index a2d90ac..1f7036c 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -234,6 +234,16 @@
if (ifp->desc.bNumEndpoints >= num_ep)
goto skip_to_next_endpoint_or_interface_descriptor;
+ /* Check for duplicate endpoint addresses */
+ for (i = 0; i < ifp->desc.bNumEndpoints; ++i) {
+ if (ifp->endpoint[i].desc.bEndpointAddress ==
+ d->bEndpointAddress) {
+ dev_warn(ddev, "config %d interface %d altsetting %d has a duplicate endpoint with address 0x%X, skipping\n",
+ cfgno, inum, asnum, d->bEndpointAddress);
+ goto skip_to_next_endpoint_or_interface_descriptor;
+ }
+ }
+
endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints];
++ifp->desc.bNumEndpoints;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index cbb1467..aef81a1 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -101,6 +101,7 @@
static void hub_release(struct kref *kref);
static int usb_reset_and_verify_device(struct usb_device *udev);
+static int hub_port_disable(struct usb_hub *hub, int port1, int set_state);
static inline char *portspeed(struct usb_hub *hub, int portstatus)
{
@@ -899,88 +900,6 @@
}
/*
- * If USB 3.0 ports are placed into the Disabled state, they will no longer
- * detect any device connects or disconnects. This is generally not what the
- * USB core wants, since it expects a disabled port to produce a port status
- * change event when a new device connects.
- *
- * Instead, set the link state to Disabled, wait for the link to settle into
- * that state, clear any change bits, and then put the port into the RxDetect
- * state.
- */
-static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
-{
- int ret;
- int total_time;
- u16 portchange, portstatus;
-
- if (!hub_is_superspeed(hub->hdev))
- return -EINVAL;
-
- ret = hub_port_status(hub, port1, &portstatus, &portchange);
- if (ret < 0)
- return ret;
-
- /*
- * USB controller Advanced Micro Devices, Inc. [AMD] FCH USB XHCI
- * Controller [1022:7814] will have spurious result making the following
- * usb 3.0 device hotplugging route to the 2.0 root hub and recognized
- * as high-speed device if we set the usb 3.0 port link state to
- * Disabled. Since it's already in USB_SS_PORT_LS_RX_DETECT state, we
- * check the state here to avoid the bug.
- */
- if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
- USB_SS_PORT_LS_RX_DETECT) {
- dev_dbg(&hub->ports[port1 - 1]->dev,
- "Not disabling port; link state is RxDetect\n");
- return ret;
- }
-
- ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED);
- if (ret)
- return ret;
-
- /* Wait for the link to enter the disabled state. */
- for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
- ret = hub_port_status(hub, port1, &portstatus, &portchange);
- if (ret < 0)
- return ret;
-
- if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
- USB_SS_PORT_LS_SS_DISABLED)
- break;
- if (total_time >= HUB_DEBOUNCE_TIMEOUT)
- break;
- msleep(HUB_DEBOUNCE_STEP);
- }
- if (total_time >= HUB_DEBOUNCE_TIMEOUT)
- dev_warn(&hub->ports[port1 - 1]->dev,
- "Could not disable after %d ms\n", total_time);
-
- return hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_RX_DETECT);
-}
-
-static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
-{
- struct usb_port *port_dev = hub->ports[port1 - 1];
- struct usb_device *hdev = hub->hdev;
- int ret = 0;
-
- if (port_dev->child && set_state)
- usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED);
- if (!hub->error) {
- if (hub_is_superspeed(hub->hdev))
- ret = hub_usb3_port_disable(hub, port1);
- else
- ret = usb_clear_port_feature(hdev, port1,
- USB_PORT_FEAT_ENABLE);
- }
- if (ret && ret != -ENODEV)
- dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret);
- return ret;
-}
-
-/*
* Disable a port and mark a logical connect-change event, so that some
* time later hub_wq will disconnect() any existing usb_device on the port
* and will re-enumerate if there actually is a device attached.
@@ -4140,6 +4059,26 @@
}
EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
+/* usb3 devices use U3 for disabled, make sure remote wakeup is disabled */
+static void hub_usb3_port_prepare_disable(struct usb_hub *hub,
+ struct usb_port *port_dev)
+{
+ struct usb_device *udev = port_dev->child;
+ int ret;
+
+ if (udev && udev->port_is_suspended && udev->do_remote_wakeup) {
+ ret = hub_set_port_link_state(hub, port_dev->portnum,
+ USB_SS_PORT_LS_U0);
+ if (!ret) {
+ msleep(USB_RESUME_TIMEOUT);
+ ret = usb_disable_remote_wakeup(udev);
+ }
+ if (ret)
+ dev_warn(&udev->dev,
+ "Port disable: can't disable remote wake\n");
+ udev->do_remote_wakeup = 0;
+ }
+}
#else /* CONFIG_PM */
@@ -4147,6 +4086,9 @@
#define hub_resume NULL
#define hub_reset_resume NULL
+static inline void hub_usb3_port_prepare_disable(struct usb_hub *hub,
+ struct usb_port *port_dev) { }
+
int usb_disable_lpm(struct usb_device *udev)
{
return 0;
@@ -4182,6 +4124,34 @@
#endif /* CONFIG_PM */
+/*
+ * USB-3 does not have a similar link state as USB-2 that will avoid negotiating
+ * a connection with a plugged-in cable but will signal the host when the cable
+ * is unplugged. Disable remote wake and set link state to U3 for USB-3 devices
+ */
+static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
+{
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+ struct usb_device *hdev = hub->hdev;
+ int ret = 0;
+
+ if (!hub->error) {
+ if (hub_is_superspeed(hub->hdev)) {
+ hub_usb3_port_prepare_disable(hub, port_dev);
+ ret = hub_set_port_link_state(hub, port_dev->portnum,
+ USB_SS_PORT_LS_U3);
+ } else {
+ ret = usb_clear_port_feature(hdev, port1,
+ USB_PORT_FEAT_ENABLE);
+ }
+ }
+ if (port_dev->child && set_state)
+ usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED);
+ if (ret && ret != -ENODEV)
+ dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret);
+ return ret;
+}
+
/* USB 2.0 spec, 7.1.7.3 / fig 7-29:
*
diff --git a/drivers/usb/core/ledtrig-usbport.c b/drivers/usb/core/ledtrig-usbport.c
index 3ed5162..1713248 100644
--- a/drivers/usb/core/ledtrig-usbport.c
+++ b/drivers/usb/core/ledtrig-usbport.c
@@ -74,8 +74,7 @@
usbport_data->count = 0;
usb_for_each_dev(usbport_data, usbport_trig_usb_dev_check);
- led_cdev->brightness_set(led_cdev,
- usbport_data->count ? LED_FULL : LED_OFF);
+ led_set_brightness(led_cdev, usbport_data->count ? LED_FULL : LED_OFF);
}
/***************************************
@@ -228,12 +227,12 @@
case USB_DEVICE_ADD:
usbport_trig_add_usb_dev_ports(usb_dev, usbport_data);
if (observed && usbport_data->count++ == 0)
- led_cdev->brightness_set(led_cdev, LED_FULL);
+ led_set_brightness(led_cdev, LED_FULL);
return NOTIFY_OK;
case USB_DEVICE_REMOVE:
usbport_trig_remove_usb_dev_ports(usbport_data, usb_dev);
if (observed && --usbport_data->count == 0)
- led_cdev->brightness_set(led_cdev, LED_OFF);
+ led_set_brightness(led_cdev, LED_OFF);
return NOTIFY_OK;
}
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index d2e50a2..24f9f98 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -37,6 +37,10 @@
/* CBM - Flash disk */
{ USB_DEVICE(0x0204, 0x6025), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* WORLDE easy key (easykey.25) MIDI controller */
+ { USB_DEVICE(0x0218, 0x0401), .driver_info =
+ USB_QUIRK_CONFIG_INTF_STRINGS },
+
/* HP 5300/5370C scanner */
{ USB_DEVICE(0x03f0, 0x0701), .driver_info =
USB_QUIRK_STRING_FETCH_255 },
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 1b152a3..7b054aa 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -50,6 +50,9 @@
#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
+static int count;
+static struct dwc3 *dwc3_instance[DWC_CTRL_COUNT];
+
void dwc3_usb3_phy_suspend(struct dwc3 *dwc, int suspend)
{
u32 reg;
@@ -726,8 +729,10 @@
/* Handle USB2.0-only core configuration */
if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
- if (dwc->maximum_speed == USB_SPEED_SUPER)
- dwc->maximum_speed = USB_SPEED_HIGH;
+ if (dwc->max_hw_supp_speed == USB_SPEED_SUPER) {
+ dwc->max_hw_supp_speed = USB_SPEED_HIGH;
+ dwc->maximum_speed = dwc->max_hw_supp_speed;
+ }
}
/* issue device SoftReset too */
@@ -1125,6 +1130,13 @@
void __iomem *regs;
void *mem;
+ if (count >= DWC_CTRL_COUNT) {
+ dev_err(dev, "Err dwc instance %d >= %d available\n",
+ count, DWC_CTRL_COUNT);
+ ret = -EINVAL;
+ return ret;
+ }
+
mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
if (!mem)
return -ENOMEM;
@@ -1198,6 +1210,7 @@
hird_threshold = 12;
dwc->maximum_speed = usb_get_maximum_speed(dev);
+ dwc->max_hw_supp_speed = dwc->maximum_speed;
dwc->dr_mode = usb_get_dr_mode(dev);
if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) {
@@ -1316,6 +1329,7 @@
DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
+ dwc->max_hw_supp_speed = dwc->maximum_speed;
break;
}
@@ -1336,6 +1350,15 @@
goto err_core_init;
}
+ dwc->dwc_ipc_log_ctxt = ipc_log_context_create(NUM_LOG_PAGES,
+ dev_name(dwc->dev), 0);
+ if (!dwc->dwc_ipc_log_ctxt)
+ dev_err(dwc->dev, "Error getting ipc_log_ctxt\n");
+
+ dwc3_instance[count] = dwc;
+ dwc->index = count;
+ count++;
+
pm_runtime_allow(dev);
return 0;
@@ -1378,6 +1401,11 @@
dwc3_free_event_buffers(dwc);
dwc3_free_scratch_buffers(dwc);
+ ipc_log_context_destroy(dwc->dwc_ipc_log_ctxt);
+ dwc->dwc_ipc_log_ctxt = NULL;
+ count--;
+ dwc3_instance[dwc->index] = NULL;
+
return 0;
}
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 4ddd8e1..ca58e97 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -46,9 +46,7 @@
#define DWC3_XHCI_RESOURCES_NUM 2
#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */
-#define DWC3_EVENT_SIZE 4 /* bytes */
-#define DWC3_EVENT_MAX_NUM 64 /* 2 events/endpoint */
-#define DWC3_EVENT_BUFFERS_SIZE (2 * PAGE_SIZE)
+#define DWC3_EVENT_BUFFERS_SIZE 4096
#define DWC3_EVENT_TYPE_MASK 0xfe
#define DWC3_EVENT_TYPE_DEV 0
@@ -330,9 +328,8 @@
#define DWC3_DCFG_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
#define DWC3_DCFG_SUPERSPEED (4 << 0)
#define DWC3_DCFG_HIGHSPEED (0 << 0)
-#define DWC3_DCFG_FULLSPEED2 (1 << 0)
+#define DWC3_DCFG_FULLSPEED (1 << 0)
#define DWC3_DCFG_LOWSPEED (2 << 0)
-#define DWC3_DCFG_FULLSPEED1 (3 << 0)
#define DWC3_DCFG_NUMP_SHIFT 17
#define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f)
@@ -426,9 +423,8 @@
#define DWC3_DSTS_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
#define DWC3_DSTS_SUPERSPEED (4 << 0)
#define DWC3_DSTS_HIGHSPEED (0 << 0)
-#define DWC3_DSTS_FULLSPEED2 (1 << 0)
+#define DWC3_DSTS_FULLSPEED (1 << 0)
#define DWC3_DSTS_LOWSPEED (2 << 0)
-#define DWC3_DSTS_FULLSPEED1 (3 << 0)
/* Device Generic Command Register */
#define DWC3_DGCMD_SET_LMP 0x01
@@ -488,6 +484,9 @@
#define DWC3_DEPCMD_TYPE_BULK 2
#define DWC3_DEPCMD_TYPE_INTR 3
+#define DWC_CTRL_COUNT 10
+#define NUM_LOG_PAGES 12
+
/* Structures */
struct dwc3_trb;
@@ -848,7 +847,8 @@
* @reg_phys: physical base address of dwc3 core register address space
* @nr_scratch: number of scratch buffers
* @u1u2: only used on revisions <1.83a for workaround
- * @maximum_speed: maximum speed requested (mainly for testing purposes)
+ * @maximum_speed: maximum speed to operate as requested by sw
+ * @max_hw_supp_speed: maximum speed supported by hw design
* @revision: revision register contents
* @dr_mode: requested mode of operation
* @hsphy_mode: UTMI phy mode, one of following:
@@ -936,6 +936,8 @@
* @bh_dbg_index: index for capturing bh_completion_time and bh_handled_evt_cnt
* @wait_linkstate: waitqueue for waiting LINK to move into required state
* @vbus_draw: current to be drawn from USB
+ * @index: dwc3 instance's number
+ * @dwc_ipc_log_ctxt: dwc3 ipc log context
*/
struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
@@ -984,6 +986,7 @@
u32 nr_scratch;
u32 u1u2;
u32 maximum_speed;
+ u32 max_hw_supp_speed;
/*
* All 3.1 IP version constants are greater than the 3.0 IP
@@ -1118,6 +1121,8 @@
unsigned int irq_dbg_index;
wait_queue_head_t wait_linkstate;
+ unsigned int index;
+ void *dwc_ipc_log_ctxt;
struct dwc3_gadget_events dbg_gadget_events;
};
diff --git a/drivers/usb/dwc3/debug.c b/drivers/usb/dwc3/debug.c
index 0be6885..fc26440 100644
--- a/drivers/usb/dwc3/debug.c
+++ b/drivers/usb/dwc3/debug.c
@@ -17,6 +17,13 @@
#include "debug.h"
+#include <linux/moduleparam.h>
+
+static unsigned int ep_addr_rxdbg_mask = 1;
+module_param(ep_addr_rxdbg_mask, uint, 0644);
+static unsigned int ep_addr_txdbg_mask = 1;
+module_param(ep_addr_txdbg_mask, uint, 0644);
+
void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...)
{
struct va_format vaf;
@@ -30,3 +37,128 @@
va_end(args);
}
+
+static int allow_dbg_print(u8 ep_num)
+{
+ int dir, num;
+
+ /* allow bus wide events */
+ if (ep_num == 0xff)
+ return 1;
+
+ dir = ep_num & 0x1;
+ num = ep_num >> 1;
+ num = 1 << num;
+
+ if (dir && (num & ep_addr_txdbg_mask))
+ return 1;
+ if (!dir && (num & ep_addr_rxdbg_mask))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * dwc3_dbg_print: prints the common part of the event
+ * @addr: endpoint address
+ * @name: event name
+ * @status: status
+ * @extra: extra information
+ * @dwc3: pointer to struct dwc3
+ */
+void dwc3_dbg_print(struct dwc3 *dwc, u8 ep_num, const char *name,
+ int status, const char *extra)
+{
+ if (!allow_dbg_print(ep_num))
+ return;
+
+ if (name == NULL)
+ return;
+
+ ipc_log_string(dwc->dwc_ipc_log_ctxt, "%02X %-25.25s %4i ?\t%s",
+ ep_num, name, status, extra);
+}
+
+/**
+ * dwc3_dbg_done: prints a DONE event
+ * @addr: endpoint address
+ * @td: transfer descriptor
+ * @status: status
+ * @dwc3: pointer to struct dwc3
+ */
+void dwc3_dbg_done(struct dwc3 *dwc, u8 ep_num,
+ const u32 count, int status)
+{
+ if (!allow_dbg_print(ep_num))
+ return;
+
+ ipc_log_string(dwc->dwc_ipc_log_ctxt, "%02X %-25.25s %4i ?\t%d",
+ ep_num, "DONE", status, count);
+}
+
+/**
+ * dwc3_dbg_event: prints a generic event
+ * @addr: endpoint address
+ * @name: event name
+ * @status: status
+ */
+void dwc3_dbg_event(struct dwc3 *dwc, u8 ep_num, const char *name, int status)
+{
+ if (!allow_dbg_print(ep_num))
+ return;
+
+ if (name != NULL)
+ dwc3_dbg_print(dwc, ep_num, name, status, "");
+}
+
+/*
+ * dwc3_dbg_queue: prints a QUEUE event
+ * @addr: endpoint address
+ * @req: USB request
+ * @status: status
+ */
+void dwc3_dbg_queue(struct dwc3 *dwc, u8 ep_num,
+ const struct usb_request *req, int status)
+{
+ if (!allow_dbg_print(ep_num))
+ return;
+
+ if (req != NULL) {
+ ipc_log_string(dwc->dwc_ipc_log_ctxt,
+ "%02X %-25.25s %4i ?\t%d %d", ep_num, "QUEUE", status,
+ !req->no_interrupt, req->length);
+ }
+}
+
+/**
+ * dwc3_dbg_setup: prints a SETUP event
+ * @addr: endpoint address
+ * @req: setup request
+ */
+void dwc3_dbg_setup(struct dwc3 *dwc, u8 ep_num,
+ const struct usb_ctrlrequest *req)
+{
+ if (!allow_dbg_print(ep_num))
+ return;
+
+ if (req != NULL) {
+ ipc_log_string(dwc->dwc_ipc_log_ctxt,
+ "%02X %-25.25s ?\t%02X %02X %04X %04X %d",
+ ep_num, "SETUP", req->bRequestType,
+ req->bRequest, le16_to_cpu(req->wValue),
+ le16_to_cpu(req->wIndex), le16_to_cpu(req->wLength));
+ }
+}
+
+/**
+ * dwc3_dbg_print_reg: prints a reg value
+ * @name: reg name
+ * @reg: reg value to be printed
+ */
+void dwc3_dbg_print_reg(struct dwc3 *dwc, const char *name, int reg)
+{
+ if (name == NULL)
+ return;
+
+ ipc_log_string(dwc->dwc_ipc_log_ctxt, "%s = 0x%08x", name, reg);
+}
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index c289d27..c438d1f 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -20,7 +20,29 @@
#define __DWC3_DEBUG_H
#include "core.h"
+#include <linux/ipc_logging.h>
+/*
+ * NOTE: Make sure to have dwc as local variable in function before using
+ * below macros.
+ */
+#define dbg_event(ep_num, name, status) \
+ dwc3_dbg_print(dwc, ep_num, name, status, "")
+
+#define dbg_print(ep_num, name, status, extra) \
+ dwc3_dbg_print(dwc, ep_num, name, status, extra)
+
+#define dbg_print_reg(name, reg) \
+ dwc3_dbg_print_reg(dwc, name, reg)
+
+#define dbg_done(ep_num, count, status) \
+ dwc3_dbg_done(dwc, ep_num, count, status)
+
+#define dbg_queue(ep_num, req, status) \
+ dwc3_dbg_queue(dwc, ep_num, req, status)
+
+#define dbg_setup(ep_num, req) \
+ dwc3_dbg_setup(dwc, ep_num, req)
/**
* dwc3_gadget_ep_cmd_string - returns endpoint command string
* @cmd: command code
@@ -311,6 +333,18 @@
}
void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...);
+void dwc3_dbg_print(struct dwc3 *dwc, u8 ep_num,
+ const char *name, int status, const char *extra);
+void dwc3_dbg_done(struct dwc3 *dwc, u8 ep_num,
+ const u32 count, int status);
+void dwc3_dbg_event(struct dwc3 *dwc, u8 ep_num,
+ const char *name, int status);
+void dwc3_dbg_queue(struct dwc3 *dwc, u8 ep_num,
+ const struct usb_request *req, int status);
+void dwc3_dbg_setup(struct dwc3 *dwc, u8 ep_num,
+ const struct usb_ctrlrequest *req);
+void dwc3_dbg_print_reg(struct dwc3 *dwc,
+ const char *name, int reg);
#ifdef CONFIG_DEBUG_FS
extern int dwc3_debugfs_init(struct dwc3 *);
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 2e2b647..079e6b1a 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -2582,6 +2582,8 @@
speed = extcon_get_cable_state_(edev, EXTCON_USB_SPEED);
dwc->maximum_speed = (speed == 0) ? USB_SPEED_HIGH : USB_SPEED_SUPER;
+ if (dwc->maximum_speed > dwc->max_hw_supp_speed)
+ dwc->maximum_speed = dwc->max_hw_supp_speed;
if (mdwc->id_state != id) {
mdwc->id_state = id;
@@ -2622,6 +2624,8 @@
speed = extcon_get_cable_state_(edev, EXTCON_USB_SPEED);
dwc->maximum_speed = (speed == 0) ? USB_SPEED_HIGH : USB_SPEED_SUPER;
+ if (dwc->maximum_speed > dwc->max_hw_supp_speed)
+ dwc->maximum_speed = dwc->max_hw_supp_speed;
mdwc->vbus_active = event;
if (dwc->is_drd && !mdwc->in_restart)
@@ -2718,6 +2722,38 @@
static DEVICE_ATTR_RW(mode);
+static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct dwc3_msm *mdwc = dev_get_drvdata(dev);
+ struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ usb_speed_string(dwc->max_hw_supp_speed));
+}
+
+static ssize_t speed_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct dwc3_msm *mdwc = dev_get_drvdata(dev);
+ struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
+ enum usb_device_speed req_speed = USB_SPEED_UNKNOWN;
+
+ if (sysfs_streq(buf, "high"))
+ req_speed = USB_SPEED_HIGH;
+ else if (sysfs_streq(buf, "super"))
+ req_speed = USB_SPEED_SUPER;
+
+ if (req_speed != USB_SPEED_UNKNOWN &&
+ req_speed != dwc->max_hw_supp_speed) {
+ dwc->maximum_speed = dwc->max_hw_supp_speed = req_speed;
+ schedule_work(&mdwc->restart_usb_work);
+ }
+
+ return count;
+}
+static DEVICE_ATTR_RW(speed);
+
static int dwc3_msm_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node, *dwc3_node;
@@ -3039,6 +3075,7 @@
dwc3_msm_id_notifier(&mdwc->id_nb, true, mdwc->extcon_id);
device_create_file(&pdev->dev, &dev_attr_mode);
+ device_create_file(&pdev->dev, &dev_attr_speed);
schedule_delayed_work(&mdwc->sm_work, 0);
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index 6df0f5d..427291a 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -38,6 +38,7 @@
#define PCI_DEVICE_ID_INTEL_BXT_M 0x1aaa
#define PCI_DEVICE_ID_INTEL_APL 0x5aaa
#define PCI_DEVICE_ID_INTEL_KBP 0xa2b0
+#define PCI_DEVICE_ID_INTEL_GLK 0x31aa
static const struct acpi_gpio_params reset_gpios = { 0, 0, false };
static const struct acpi_gpio_params cs_gpios = { 1, 0, false };
@@ -81,7 +82,7 @@
int ret;
struct property_entry properties[] = {
- PROPERTY_ENTRY_STRING("dr-mode", "peripheral"),
+ PROPERTY_ENTRY_STRING("dr_mode", "peripheral"),
{ }
};
@@ -229,6 +230,7 @@
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BXT_M), },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_APL), },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KBP), },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_GLK), },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB), },
{ } /* Terminating Entry */
};
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 566b645..ee60147 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -55,20 +55,13 @@
}
}
-static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
- u32 len, u32 type, bool chain)
+static void dwc3_ep0_prepare_one_trb(struct dwc3 *dwc, u8 epnum,
+ dma_addr_t buf_dma, u32 len, u32 type, bool chain)
{
- struct dwc3_gadget_ep_cmd_params params;
struct dwc3_trb *trb;
struct dwc3_ep *dep;
- int ret;
-
dep = dwc->eps[epnum];
- if (dep->flags & DWC3_EP_BUSY) {
- dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name);
- return 0;
- }
trb = &dwc->ep0_trb[dep->trb_enqueue];
@@ -89,15 +82,25 @@
trb->ctrl |= (DWC3_TRB_CTRL_IOC
| DWC3_TRB_CTRL_LST);
- if (chain)
+ trace_dwc3_prepare_trb(dep, trb);
+}
+
+static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_ep *dep;
+ int ret;
+
+ dep = dwc->eps[epnum];
+ if (dep->flags & DWC3_EP_BUSY) {
+ dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name);
return 0;
+ }
memset(¶ms, 0, sizeof(params));
params.param0 = upper_32_bits(dwc->ep0_trb_addr);
params.param1 = lower_32_bits(dwc->ep0_trb_addr);
- trace_dwc3_prepare_trb(dep, trb);
-
ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_STARTTRANSFER, ¶ms);
if (ret < 0) {
dwc3_trace(trace_dwc3_ep0, "%s STARTTRANSFER failed",
@@ -308,8 +311,9 @@
{
int ret;
- ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8,
+ dwc3_ep0_prepare_one_trb(dwc, 0, dwc->ctrl_req_addr, 8,
DWC3_TRBCTL_CONTROL_SETUP, false);
+ ret = dwc3_ep0_start_trans(dwc, 0);
WARN_ON(ret < 0);
}
@@ -883,9 +887,9 @@
dwc->ep0_next_event = DWC3_EP0_COMPLETE;
- ret = dwc3_ep0_start_trans(dwc, epnum,
- dwc->ctrl_req_addr, 0,
- DWC3_TRBCTL_CONTROL_DATA, false);
+ dwc3_ep0_prepare_one_trb(dwc, epnum, dwc->ctrl_req_addr,
+ 0, DWC3_TRBCTL_CONTROL_DATA, false);
+ ret = dwc3_ep0_start_trans(dwc, epnum);
WARN_ON(ret < 0);
}
}
@@ -969,9 +973,10 @@
req->direction = !!dep->number;
if (req->request.length == 0) {
- ret = dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ctrl_req_addr, 0,
DWC3_TRBCTL_CONTROL_DATA, false);
+ ret = dwc3_ep0_start_trans(dwc, dep->number);
} else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
&& (dep->number == 0)) {
u32 transfer_size = 0;
@@ -989,7 +994,7 @@
if (req->request.length > DWC3_EP0_BOUNCE_SIZE) {
transfer_size = ALIGN(req->request.length - maxpacket,
maxpacket);
- ret = dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
req->request.dma,
transfer_size,
DWC3_TRBCTL_CONTROL_DATA,
@@ -1001,9 +1006,10 @@
dwc->ep0_bounced = true;
- ret = dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ep0_bounce_addr, transfer_size,
DWC3_TRBCTL_CONTROL_DATA, false);
+ ret = dwc3_ep0_start_trans(dwc, dep->number);
} else {
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
dep->number);
@@ -1012,9 +1018,10 @@
return;
}
- ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number, req->request.dma,
req->request.length, DWC3_TRBCTL_CONTROL_DATA,
false);
+ ret = dwc3_ep0_start_trans(dwc, dep->number);
}
WARN_ON(ret < 0);
@@ -1028,8 +1035,9 @@
type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3
: DWC3_TRBCTL_CONTROL_STATUS2;
- return dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ctrl_req_addr, 0, type, false);
+ return dwc3_ep0_start_trans(dwc, dep->number);
}
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 7aa290f..8c41ebd 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -292,11 +292,11 @@
if (req->request.status == -EINPROGRESS)
req->request.status = status;
- if (dwc->ep0_bounced && dep->number == 0)
+ if (dwc->ep0_bounced && dep->number <= 1)
dwc->ep0_bounced = false;
- else
- usb_gadget_unmap_request(&dwc->gadget, &req->request,
- req->direction);
+
+ usb_gadget_unmap_request(&dwc->gadget, &req->request,
+ req->direction);
trace_dwc3_gadget_giveback(req);
@@ -505,6 +505,7 @@
if (dep->number > 1 && dep->trb_pool && dep->trb_pool_dma) {
memset(&dep->trb_pool[0], 0,
sizeof(struct dwc3_trb) * dep->num_trbs);
+ dbg_event(dep->number, "Clr_TRB", 0);
dev_dbg(dwc->dev, "Clr_TRB ring of %s\n", dep->name);
dma_free_coherent(dwc->dev,
@@ -837,6 +838,7 @@
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false, false);
+ dbg_event(dep->number, "ENABLE", ret);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -871,6 +873,7 @@
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_disable(dep);
+ dbg_event(dep->number, "DISABLE", ret);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -919,6 +922,9 @@
unsigned length, unsigned chain, unsigned node)
{
struct dwc3_trb *trb;
+ struct dwc3 *dwc = dep->dwc;
+ struct usb_gadget *gadget = &dwc->gadget;
+ enum usb_device_speed speed = gadget->speed;
dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s",
dep->name, req, (unsigned long long) dma,
@@ -946,10 +952,16 @@
break;
case USB_ENDPOINT_XFER_ISOC:
- if (!node)
+ if (!node) {
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
- else
+
+ if (speed == USB_SPEED_HIGH) {
+ struct usb_ep *ep = &dep->endpoint;
+ trb->size |= DWC3_TRB_SIZE_PCM1(ep->mult - 1);
+ }
+ } else {
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
+ }
/* always enable Interrupt on Missed ISOC */
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
@@ -1115,6 +1127,7 @@
req = next_request(&dep->started_list);
if (!req) {
dep->flags |= DWC3_EP_PENDING_REQUEST;
+ dbg_event(dep->number, "NO REQ", 0);
return 0;
}
@@ -1157,6 +1170,7 @@
struct dwc3_ep *dep, u32 cur_uf)
{
u32 uf;
+ int ret;
if (list_empty(&dep->pending_list)) {
dwc3_trace(trace_dwc3_gadget,
@@ -1169,7 +1183,9 @@
/* 4 micro frames in the future */
uf = cur_uf + dep->interval * 4;
- __dwc3_gadget_kick_transfer(dep, uf);
+ ret = __dwc3_gadget_kick_transfer(dep, uf);
+ if (ret < 0)
+ dbg_event(dep->number, "ISOC QUEUE", ret);
}
static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
@@ -1395,6 +1411,7 @@
}
out1:
+ dbg_event(dep->number, "DEQUEUE", 0);
/* giveback the request */
dwc3_gadget_giveback(dep, req, -ECONNRESET);
@@ -1416,7 +1433,7 @@
}
memset(¶ms, 0x00, sizeof(params));
-
+ dbg_event(dep->number, "HALT", value);
if (value) {
struct dwc3_trb *trb;
@@ -1488,6 +1505,7 @@
int ret;
spin_lock_irqsave(&dwc->lock, flags);
+ dbg_event(dep->number, "WEDGE", 0);
dep->flags |= DWC3_EP_WEDGE;
if (dep->number == 0 || dep->number == 1)
@@ -1643,6 +1661,8 @@
}
}
retry_count = 0;
+ dbg_event(0xFF, "Gdgwake gsyn",
+ atomic_read(&dwc->dev->power.usage_count));
ret = dwc3_gadget_wakeup_int(dwc);
@@ -1652,6 +1672,8 @@
pr_debug("Remote wakeup succeeded.\n");
pm_runtime_put_noidle(dwc->dev);
+ dbg_event(0xFF, "Gdgwake put",
+ atomic_read(&dwc->dev->power.usage_count));
}
static int dwc3_gadget_wakeup_int(struct dwc3 *dwc)
@@ -1834,6 +1856,7 @@
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (is_on) {
+ dbg_event(0xFF, "Pullup_enable", is_on);
if (dwc->revision <= DWC3_REVISION_187A) {
reg &= ~DWC3_DCTL_TRGTULST_MASK;
reg |= DWC3_DCTL_TRGTULST_RX_DET;
@@ -1852,6 +1875,7 @@
dwc->pullups_connected = true;
} else {
+ dbg_event(0xFF, "Pullup_disable", is_on);
dwc3_gadget_disable_irq(dwc);
__dwc3_gadget_ep_disable(dwc->eps[0]);
__dwc3_gadget_ep_disable(dwc->eps[1]);
@@ -1874,6 +1898,10 @@
if (!timeout) {
dev_err(dwc->dev, "failed to %s controller\n",
is_on ? "start" : "stop");
+ if (is_on)
+ dbg_event(0xFF, "STARTTOUT", reg);
+ else
+ dbg_event(0xFF, "STOPTOUT", reg);
return -ETIMEDOUT;
}
@@ -1891,6 +1919,7 @@
dwc->vbus_draw = mA;
dev_dbg(dwc->dev, "Notify controller from %s. mA = %u\n", __func__, mA);
+ dbg_event(0xFF, "currentDraw", mA);
dwc3_notify_event(dwc, DWC3_CONTROLLER_SET_CURRENT_DRAW_EVENT);
return 0;
}
@@ -1908,10 +1937,13 @@
* Need to wait for vbus_session(on) from otg driver or to
* the udc_start.
*/
+ dbg_event(0xFF, "WaitPullup", 0);
return 0;
}
pm_runtime_get_sync(dwc->dev);
+ dbg_event(0xFF, "Pullup gsync",
+ atomic_read(&dwc->dev->power.usage_count));
spin_lock_irqsave(&dwc->lock, flags);
/*
* If we are here after bus suspend notify otg state machine to
@@ -1925,7 +1957,8 @@
pm_runtime_mark_last_busy(dwc->dev);
pm_runtime_put_autosuspend(dwc->dev);
-
+ dbg_event(0xFF, "Pullup put",
+ atomic_read(&dwc->dev->power.usage_count));
return ret;
}
@@ -1933,6 +1966,7 @@
{
u32 reg;
+ dbg_event(0xFF, "UnmaskINT", 0);
/* Enable all but Start and End of Frame IRQs */
reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
DWC3_DEVTEN_EVNTOVERFLOWEN |
@@ -1957,6 +1991,7 @@
void dwc3_gadget_disable_irq(struct dwc3 *dwc)
{
+ dbg_event(0xFF, "MaskINT", 0);
/* mask all interrupts */
dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
}
@@ -2015,6 +2050,7 @@
is_active = !!is_active;
+ dbg_event(0xFF, "VbusSess", is_active);
spin_lock_irqsave(&dwc->lock, flags);
/* Mark that the vbus was powered */
@@ -2055,6 +2091,7 @@
int ret = 0;
u32 reg;
+ dbg_event(0xFF, "__Gadgetstart", 0);
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
reg &= ~(DWC3_DCFG_SPEED_MASK);
@@ -2079,7 +2116,7 @@
reg |= DWC3_DCFG_LOWSPEED;
break;
case USB_SPEED_FULL:
- reg |= DWC3_DCFG_FULLSPEED1;
+ reg |= DWC3_DCFG_FULLSPEED;
break;
case USB_SPEED_HIGH:
reg |= DWC3_DCFG_HIGHSPEED;
@@ -2186,6 +2223,7 @@
static void __dwc3_gadget_stop(struct dwc3 *dwc)
{
+ dbg_event(0xFF, "__Gadgetstop", 0);
dwc3_gadget_disable_irq(dwc);
__dwc3_gadget_ep_disable(dwc->eps[0]);
__dwc3_gadget_ep_disable(dwc->eps[1]);
@@ -2207,6 +2245,7 @@
{
struct dwc3 *dwc = gadget_to_dwc(g);
+ dbg_event(0xFF, "RestartUSBSession", 0);
return dwc3_notify_event(dwc, DWC3_CONTROLLER_RESTART_USB_SESSION);
}
@@ -2423,6 +2462,7 @@
* request in the pending_list.
*/
dep->flags |= DWC3_EP_MISSED_ISOC;
+ dbg_event(dep->number, "MISSED ISOC", status);
} else {
dev_err(dwc->dev, "incomplete IN transfer %s\n",
dep->name);
@@ -2694,6 +2734,7 @@
{
if (dwc->gadget_driver && dwc->gadget_driver->disconnect) {
spin_unlock(&dwc->lock);
+ dbg_event(0xFF, "DISCONNECT", 0);
dwc->gadget_driver->disconnect(&dwc->gadget);
spin_lock(&dwc->lock);
}
@@ -2703,6 +2744,7 @@
{
if (dwc->gadget_driver && dwc->gadget_driver->suspend) {
spin_unlock(&dwc->lock);
+ dbg_event(0xFF, "SUSPEND", 0);
dwc->gadget_driver->suspend(&dwc->gadget);
spin_lock(&dwc->lock);
}
@@ -2712,6 +2754,7 @@
{
if (dwc->gadget_driver && dwc->gadget_driver->resume) {
spin_unlock(&dwc->lock);
+ dbg_event(0xFF, "RESUME", 0);
dwc->gadget_driver->resume(&dwc->gadget);
spin_lock(&dwc->lock);
}
@@ -2724,6 +2767,7 @@
if (dwc->gadget.speed != USB_SPEED_UNKNOWN) {
spin_unlock(&dwc->lock);
+ dbg_event(0xFF, "UDC RESET", 0);
usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver);
spin_lock(&dwc->lock);
}
@@ -2822,6 +2866,7 @@
dep->flags &= ~DWC3_EP_STALL;
ret = dwc3_send_clear_stall_ep_cmd(dep);
+ dbg_event(dep->number, "ECLRSTALL", ret);
WARN_ON_ONCE(ret);
}
}
@@ -2830,6 +2875,7 @@
{
int reg;
+ dbg_event(0xFF, "DISCONNECT INT", 0);
dev_dbg(dwc->dev, "Notify OTG from %s\n", __func__);
dwc->b_suspend = false;
dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT);
@@ -2889,6 +2935,7 @@
dwc3_gadget_disconnect_interrupt(dwc);
}
+ dbg_event(0xFF, "BUS RESET", 0);
dev_dbg(dwc->dev, "Notify OTG from %s\n", __func__);
dwc->b_suspend = false;
dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT);
@@ -2952,6 +2999,7 @@
u32 reg;
u8 speed;
+ dbg_event(0xFF, "CONNECT DONE", 0);
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
speed = reg & DWC3_DSTS_CONNECTSPD;
dwc->speed = speed;
@@ -2990,8 +3038,7 @@
dwc->gadget.ep0->maxpacket = 64;
dwc->gadget.speed = USB_SPEED_HIGH;
break;
- case DWC3_DSTS_FULLSPEED2:
- case DWC3_DSTS_FULLSPEED1:
+ case DWC3_DSTS_FULLSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
dwc->gadget.ep0->maxpacket = 64;
dwc->gadget.speed = USB_SPEED_FULL;
@@ -3076,6 +3123,7 @@
dev_dbg(dwc->dev, "%s\n", __func__);
+ dbg_event(0xFF, "WAKEUP", remote_wakeup);
/*
* Identify if it is called from wakeup_interrupt() context for bus
* resume or as part of remote wakeup. And based on that check for
@@ -3216,6 +3264,7 @@
{
enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
+ dbg_event(0xFF, "SUSPEND INT", 0);
dev_dbg(dwc->dev, "%s Entry to %d\n", __func__, next);
if (dwc->link_state != next && next == DWC3_LINK_STATE_U3) {
@@ -3305,6 +3354,7 @@
dwc->dbg_gadget_events.eopf++;
} else {
dwc3_trace(trace_dwc3_gadget, "U3/L1-L2 Suspend Event");
+ dbg_event(0xFF, "GAD SUS", 0);
dwc->dbg_gadget_events.suspend++;
/*
* Ignore suspend event until the gadget enters into
@@ -3321,6 +3371,7 @@
break;
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
dwc3_trace(trace_dwc3_gadget, "Erratic Error");
+ dbg_event(0xFF, "ERROR", 0);
dwc->dbg_gadget_events.erratic_error++;
break;
case DWC3_DEVICE_EVENT_CMD_CMPL:
@@ -3471,6 +3522,7 @@
if (count > evt->length) {
dev_err(dwc->dev, "HUGE_EVCNT(%d)", count);
+ dbg_event(0xFF, "HUGE_EVCNT", count);
evt->lpos = (evt->lpos + count) % DWC3_EVENT_BUFFERS_SIZE;
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
return IRQ_HANDLED;
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index c2b0616..ec166f2 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -208,11 +208,16 @@
ep_found:
/* commit results */
- _ep->maxpacket = usb_endpoint_maxp(chosen_desc);
+ _ep->maxpacket = usb_endpoint_maxp(chosen_desc) & 0x7ff;
_ep->desc = chosen_desc;
_ep->comp_desc = NULL;
_ep->maxburst = 0;
- _ep->mult = 0;
+ _ep->mult = 1;
+
+ if (g->speed == USB_SPEED_HIGH && (usb_endpoint_xfer_isoc(_ep->desc) ||
+ usb_endpoint_xfer_int(_ep->desc)))
+ _ep->mult = ((usb_endpoint_maxp(_ep->desc) & 0x1800) >> 11) + 1;
+
if (!want_comp_desc)
return 0;
@@ -229,7 +234,7 @@
switch (usb_endpoint_type(_ep->desc)) {
case USB_ENDPOINT_XFER_ISOC:
/* mult: bits 1:0 of bmAttributes */
- _ep->mult = comp_desc->bmAttributes & 0x3;
+ _ep->mult = (comp_desc->bmAttributes & 0x3) + 1;
case USB_ENDPOINT_XFER_BULK:
case USB_ENDPOINT_XFER_INT:
_ep->maxburst = comp_desc->bMaxBurst + 1;
@@ -1810,9 +1815,7 @@
value = min(w_length, (u16) 1);
break;
- /* function drivers must handle get/set altsetting; if there's
- * no get() method, we know only altsetting zero works.
- */
+ /* function drivers must handle get/set altsetting */
case USB_REQ_SET_INTERFACE:
if (ctrl->bRequestType != USB_RECIP_INTERFACE)
goto unknown;
@@ -1821,7 +1824,13 @@
f = cdev->config->interface[intf];
if (!f)
break;
- if (w_value && !f->set_alt)
+
+ /*
+ * If there's no get_alt() method, we know only altsetting zero
+ * works. There is no need to check if set_alt() is not NULL
+ * as we check this in usb_add_function().
+ */
+ if (w_value && !f->get_alt)
break;
value = f->set_alt(f, w_index, w_value);
if (value == USB_GADGET_DELAYED_STATUS) {
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index a34651d..6b2c137 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -426,11 +426,6 @@
}
f = usb_get_function(fi);
- if (f == NULL) {
- /* Are we trying to symlink PTP without MTP function? */
- ret = -EINVAL; /* Invalid Configuration */
- goto out;
- }
if (IS_ERR(f)) {
ret = PTR_ERR(f);
goto out;
diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c
index cfb57ac..b1cca11 100644
--- a/drivers/usb/gadget/function/f_accessory.c
+++ b/drivers/usb/gadget/function/f_accessory.c
@@ -255,6 +255,7 @@
static struct usb_request *acc_request_new(struct usb_ep *ep, int buffer_size)
{
struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+
if (!req)
return NULL;
@@ -1076,6 +1077,7 @@
static void acc_start_work(struct work_struct *data)
{
char *envp[2] = { "ACCESSORY=START", NULL };
+
kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp);
}
diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c
index bcd8174..db7903d 100644
--- a/drivers/usb/gadget/function/f_audio_source.c
+++ b/drivers/usb/gadget/function/f_audio_source.c
@@ -310,6 +310,7 @@
static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size)
{
struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+
if (!req)
return NULL;
@@ -377,10 +378,9 @@
/* compute number of frames to send */
now = ktime_get();
- msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time);
- do_div(msecs, 1000000);
- frames = msecs * SAMPLE_RATE;
- do_div(frames, 1000);
+ msecs = div_s64((ktime_to_ns(now) - ktime_to_ns(audio->start_time)),
+ 1000000);
+ frames = div_s64((msecs * SAMPLE_RATE), 1000);
/* Readjust our frames_sent if we fall too far behind.
* If we get too far behind it is better to drop some frames than
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index cc3ac26..4a30afa 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -2605,6 +2605,8 @@
if (len < sizeof(*d) || h->interface >= ffs->interfaces_count)
return -EINVAL;
length = le32_to_cpu(d->dwSize);
+ if (len < length)
+ return -EINVAL;
type = le32_to_cpu(d->dwPropertyDataType);
if (type < USB_EXT_PROP_UNICODE ||
type > USB_EXT_PROP_UNICODE_MULTI) {
@@ -2613,6 +2615,11 @@
return -EINVAL;
}
pnl = le16_to_cpu(d->wPropertyNameLength);
+ if (length < 14 + pnl) {
+ pr_vdebug("invalid os descriptor length: %d pnl:%d (descriptor %d)\n",
+ length, pnl, type);
+ return -EINVAL;
+ }
pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl));
if (length != 14 + pnl + pdl) {
pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n",
@@ -2704,6 +2711,9 @@
}
}
if (flags & (1 << i)) {
+ if (len < 4) {
+ goto error;
+ }
os_descs_count = get_unaligned_le32(data);
data += 4;
len -= 4;
@@ -2781,7 +2791,8 @@
ffs_log("enter: len %zu", len);
- if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC ||
+ if (unlikely(len < 16 ||
+ get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC ||
get_unaligned_le32(data + 4) != len))
goto error;
str_count = get_unaligned_le32(data + 8);
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index e41fd34..c807b12 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -1,4 +1,5 @@
-/* Copyright (c) 2015-2016, Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2015-2017, Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,10 +14,6 @@
#include "f_gsi.h"
#include "rndis.h"
-#define log_event_err(x, ...) pr_err(x, ##__VA_ARGS__)
-#define log_event_dbg(x, ...) pr_debug(x, ##__VA_ARGS__)
-#define log_event_info(x, ...) pr_info(x, ##__VA_ARGS__)
-
static unsigned int gsi_in_aggr_size;
module_param(gsi_in_aggr_size, uint, 0644);
MODULE_PARM_DESC(gsi_in_aggr_size,
@@ -53,6 +50,7 @@
static inline bool usb_gsi_remote_wakeup_allowed(struct usb_function *f)
{
bool remote_wakeup_allowed;
+ struct f_gsi *gsi = func_to_gsi(f);
if (f->config->cdev->gadget->speed == USB_SPEED_SUPER)
remote_wakeup_allowed = f->func_wakeup_allowed;
@@ -60,13 +58,14 @@
remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup;
log_event_dbg("%s: remote_wakeup_allowed:%s", __func__,
- remote_wakeup_allowed ? "true" : "false");
+ (remote_wakeup_allowed ? "true" : "false"));
return remote_wakeup_allowed;
}
static void post_event(struct gsi_data_port *port, u8 event)
{
unsigned long flags;
+ struct f_gsi *gsi = d_port_to_gsi(port);
spin_lock_irqsave(&port->evt_q.q_lock, flags);
@@ -96,6 +95,7 @@
{
u8 event;
unsigned long flags;
+ struct f_gsi *gsi = d_port_to_gsi(port);
spin_lock_irqsave(&port->evt_q.q_lock, flags);
if (port->evt_q.head == port->evt_q.tail) {
@@ -119,6 +119,7 @@
u8 event;
unsigned long flags;
u8 peek_index = 0;
+ struct f_gsi *gsi = d_port_to_gsi(port);
spin_lock_irqsave(&port->evt_q.q_lock, flags);
if (port->evt_q.head == port->evt_q.tail) {
@@ -614,7 +615,7 @@
struct usb_gadget *gadget = d_port->gadget;
struct device *dev;
struct device *gad_dev;
- struct f_gsi *gsi;
+ struct f_gsi *gsi = d_port_to_gsi(d_port);
event = read_event(d_port);
@@ -634,8 +635,6 @@
return;
}
- gsi = d_port_to_gsi(d_port);
-
switch (d_port->sm_state) {
case STATE_UNINITIALIZED:
break;
@@ -955,6 +954,7 @@
struct gsi_ctrl_port *c_port = container_of(fp->private_data,
struct gsi_ctrl_port,
ctrl_device);
+ struct f_gsi *gsi = c_port_to_gsi(c_port);
if (!c_port) {
log_event_err("%s: gsi ctrl port %p", __func__, c_port);
@@ -978,6 +978,7 @@
struct gsi_ctrl_port *c_port = container_of(fp->private_data,
struct gsi_ctrl_port,
ctrl_device);
+ struct f_gsi *gsi = c_port_to_gsi(c_port);
if (!c_port) {
log_event_err("%s: gsi ctrl port %p", __func__, c_port);
@@ -997,7 +998,7 @@
struct gsi_ctrl_port *c_port = container_of(fp->private_data,
struct gsi_ctrl_port,
ctrl_device);
-
+ struct f_gsi *gsi = c_port_to_gsi(c_port);
struct gsi_ctrl_pkt *cpkt = NULL;
unsigned long flags;
int ret = 0;
@@ -1267,6 +1268,7 @@
struct gsi_ctrl_port *c_port = container_of(fp->private_data,
struct gsi_ctrl_port,
ctrl_device);
+ struct f_gsi *gsi = c_port_to_gsi(c_port);
unsigned long flags;
unsigned int mask = 0;
@@ -1372,29 +1374,28 @@
return net_dev;
}
-static void gsi_rndis_open(struct f_gsi *rndis)
+static void gsi_rndis_open(struct f_gsi *gsi)
{
- struct usb_composite_dev *cdev = rndis->function.config->cdev;
+ struct usb_composite_dev *cdev = gsi->function.config->cdev;
log_event_dbg("%s", __func__);
- rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3,
+ rndis_set_param_medium(gsi->params, RNDIS_MEDIUM_802_3,
gsi_xfer_bitrate(cdev->gadget) / 100);
- rndis_signal_connect(rndis->params);
+ rndis_signal_connect(gsi->params);
}
void gsi_rndis_flow_ctrl_enable(bool enable, struct rndis_params *param)
{
- struct f_gsi *rndis = param->v;
+ struct f_gsi *gsi = param->v;
struct gsi_data_port *d_port;
- if (!rndis) {
- log_event_err("%s: gsi prot ctx is %p", __func__, rndis);
+ if (!gsi) {
+ pr_err("%s: gsi prot ctx is %p", __func__, gsi);
return;
}
- d_port = &rndis->d_port;
-
+ d_port = &gsi->d_port;
if (enable) {
log_event_dbg("%s: posting HOST_NRDY\n", __func__);
post_event(d_port, EVT_HOST_NRDY);
@@ -1403,7 +1404,7 @@
post_event(d_port, EVT_HOST_READY);
}
- queue_work(rndis->d_port.ipa_usb_wq, &rndis->d_port.usb_ipa_w);
+ queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
}
static int queue_notification_request(struct f_gsi *gsi)
@@ -1594,10 +1595,10 @@
static void gsi_rndis_command_complete(struct usb_ep *ep,
struct usb_request *req)
{
- struct f_gsi *rndis = req->context;
+ struct f_gsi *gsi = req->context;
int status;
- status = rndis_msg_parser(rndis->params, (u8 *) req->buf);
+ status = rndis_msg_parser(gsi->params, (u8 *) req->buf);
if (status < 0)
log_event_err("RNDIS command error %d, %d/%d",
status, req->actual, req->length);
@@ -2188,6 +2189,7 @@
static int gsi_func_suspend(struct usb_function *f, u8 options)
{
bool func_wakeup_allowed;
+ struct f_gsi *gsi = func_to_gsi(f);
log_event_dbg("func susp %u cmd for %s",
options, f->name ? f->name : "");
@@ -2677,8 +2679,8 @@
&gsi->d_port.ipa_init_params, gsi->d_port.ipa_usb_notify_cb,
gsi);
if (status) {
- log_event_err("%s: failed to init teth prot %d",
- __func__, gsi->prot_id);
+ log_event_err("%s: failed to init teth prot(%d) with err:%d",
+ __func__, gsi->prot_id, status);
goto dereg_rndis;
}
@@ -2800,7 +2802,7 @@
int ret = 0;
if (prot_id >= IPA_USB_MAX_TETH_PROT_SIZE) {
- log_event_err("%s: invalid prot id %d", __func__, prot_id);
+ pr_err("%s: invalid prot id %d", __func__, prot_id);
ret = -EINVAL;
goto error;
}
@@ -3005,7 +3007,9 @@
{
int ret, name_len;
struct f_gsi *gsi;
+ char gsi_inst_name[MAX_INST_NAME_LEN + sizeof("gsi.") + 1];
struct gsi_opts *opts = container_of(fi, struct gsi_opts, func_inst);
+ void *ipc_log_ctxt;
name_len = strlen(name) + 1;
if (name_len > MAX_INST_NAME_LEN)
@@ -3018,12 +3022,24 @@
return -EINVAL;
}
+ /*
+ * create instance name with prefixing "gsi." to differentiate
+ * ipc log debugfs entry
+ */
+ snprintf(gsi_inst_name, sizeof(gsi_inst_name), "gsi.%s", name);
+ ipc_log_ctxt = ipc_log_context_create(NUM_LOG_PAGES, gsi_inst_name, 0);
+ if (!ipc_log_ctxt)
+ pr_err("%s: Err allocating ipc_log_ctxt for prot:%s\n",
+ __func__, gsi_inst_name);
+
gsi = gsi_function_init(ret);
- if (IS_ERR(gsi))
+ if (IS_ERR(gsi)) {
+ ipc_log_context_destroy(ipc_log_ctxt);
return PTR_ERR(gsi);
+ }
opts->gsi = gsi;
-
+ opts->gsi->ipc_log_ctxt = ipc_log_ctxt;
return 0;
}
@@ -3034,6 +3050,7 @@
if (opts->gsi->c_port.ctrl_device.fops)
misc_deregister(&opts->gsi->c_port.ctrl_device);
+ ipc_log_context_destroy(opts->gsi->ipc_log_ctxt);
kfree(opts->gsi);
kfree(opts);
}
@@ -3077,7 +3094,7 @@
ipa_usb_wq = alloc_workqueue("k_ipa_usb",
WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_FREEZABLE, 1);
if (!ipa_usb_wq) {
- log_event_err("Failed to create workqueue for IPA");
+ pr_err("%s(): Failed to create workqueue\n", __func__);
return -ENOMEM;
}
diff --git a/drivers/usb/gadget/function/f_gsi.h b/drivers/usb/gadget/function/f_gsi.h
index 9e2941c..43aae8f 100644
--- a/drivers/usb/gadget/function/f_gsi.h
+++ b/drivers/usb/gadget/function/f_gsi.h
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
-
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
@@ -25,6 +25,7 @@
#include <linux/etherdevice.h>
#include <linux/debugfs.h>
#include <linux/ipa_usb.h>
+#include <linux/ipc_logging.h>
#define GSI_RMNET_CTRL_NAME "rmnet_ctrl"
#define GSI_MBIM_CTRL_NAME "android_mbim"
@@ -82,6 +83,28 @@
#define EVT_IPA_SUSPEND 9
#define EVT_RESUMED 10
+#define NUM_LOG_PAGES 10
+#define log_event_err(x, ...) do { \
+ if (gsi) { \
+ ipc_log_string(gsi->ipc_log_ctxt, x, ##__VA_ARGS__); \
+ pr_err(x, ##__VA_ARGS__); \
+ } \
+} while (0)
+
+#define log_event_dbg(x, ...) do { \
+ if (gsi) { \
+ ipc_log_string(gsi->ipc_log_ctxt, x, ##__VA_ARGS__); \
+ pr_debug(x, ##__VA_ARGS__); \
+ } \
+} while (0)
+
+#define log_event_info(x, ...) do { \
+ if (gsi) { \
+ ipc_log_string(gsi->ipc_log_ctxt, x, ##__VA_ARGS__); \
+ pr_info(x, ##__VA_ARGS__); \
+ } \
+} while (0)
+
enum connection_state {
STATE_UNINITIALIZED,
STATE_INITIALIZED,
@@ -242,6 +265,7 @@
struct gsi_data_port d_port;
struct gsi_ctrl_port c_port;
+ void *ipc_log_ctxt;
};
static inline struct f_gsi *func_to_gsi(struct usb_function *f)
diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c
index 5840180..4d8694a 100644
--- a/drivers/usb/gadget/function/f_mtp.c
+++ b/drivers/usb/gadget/function/f_mtp.c
@@ -386,6 +386,7 @@
static struct usb_request *mtp_request_new(struct usb_ep *ep, int buffer_size)
{
struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+
if (!req)
return NULL;
@@ -1313,6 +1314,7 @@
} else if (ctrl->bRequest == MTP_REQ_GET_DEVICE_STATUS
&& w_index == 0 && w_value == 0) {
struct mtp_device_status *status = cdev->req->buf;
+
status->wLength =
__constant_cpu_to_le16(sizeof(*status));
@@ -1335,6 +1337,7 @@
/* respond with data transfer or status phase? */
if (value >= 0) {
int rc;
+
cdev->req->zero = value < w_length;
cdev->req->length = value;
rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC);
@@ -1685,6 +1688,7 @@
static void mtp_attr_release(struct config_item *item)
{
struct mtp_instance *fi_mtp = to_mtp_instance(item);
+
usb_put_function_instance(&fi_mtp->func_inst);
}
@@ -1804,7 +1808,7 @@
pr_err("\t2: Create MTP function\n");
pr_err("\t3: Create and symlink PTP function"
" with a gadget configuration\n");
- return NULL;
+ return ERR_PTR(-EINVAL); /* Invalid Configuration */
}
dev = fi_mtp->dev;
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index 197f733..d235113 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -1073,7 +1073,7 @@
struct usbg_cmd *cmd;
int tag;
- tag = percpu_ida_alloc(&se_sess->sess_tag_pool, GFP_ATOMIC);
+ tag = percpu_ida_alloc(&se_sess->sess_tag_pool, TASK_RUNNING);
if (tag < 0)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index cd214ec..969cfe7 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -1067,13 +1067,13 @@
agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
if (!agdev->out_ep) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- goto err;
+ return ret;
}
agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
if (!agdev->in_ep) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- goto err;
+ return ret;
}
uac2->p_prm.uac2 = uac2;
@@ -1091,7 +1091,7 @@
ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL,
NULL);
if (ret)
- goto err;
+ return ret;
prm = &agdev->uac2.c_prm;
prm->max_psize = hs_epout_desc.wMaxPacketSize;
@@ -1106,19 +1106,19 @@
prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
if (!prm->rbuf) {
prm->max_psize = 0;
- goto err_free_descs;
+ goto err;
}
ret = alsa_uac2_init(agdev);
if (ret)
- goto err_free_descs;
+ goto err;
return 0;
-err_free_descs:
- usb_free_all_descriptors(fn);
err:
kfree(agdev->uac2.p_prm.rbuf);
kfree(agdev->uac2.c_prm.rbuf);
+err_free_descs:
+ usb_free_all_descriptors(fn);
return -EINVAL;
}
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 3d0d5d9..0f01c04 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -243,7 +243,7 @@
req_size = video->ep->maxpacket
* max_t(unsigned int, video->ep->maxburst, 1)
- * (video->ep->mult + 1);
+ * (video->ep->mult);
for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL);
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index bd82dd1..1468d8f 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1126,7 +1126,7 @@
/* data and/or status stage for control request */
} else if (dev->state == STATE_DEV_SETUP) {
- /* IN DATA+STATUS caller makes len <= wLength */
+ len = min_t(size_t, len, dev->setup_wLength);
if (dev->setup_in) {
retval = setup_req (dev->gadget->ep0, dev->req, len);
if (retval == 0) {
@@ -1734,10 +1734,12 @@
* such as configuration notifications.
*/
-static int is_valid_config (struct usb_config_descriptor *config)
+static int is_valid_config(struct usb_config_descriptor *config,
+ unsigned int total)
{
return config->bDescriptorType == USB_DT_CONFIG
&& config->bLength == USB_DT_CONFIG_SIZE
+ && total >= USB_DT_CONFIG_SIZE
&& config->bConfigurationValue != 0
&& (config->bmAttributes & USB_CONFIG_ATT_ONE) != 0
&& (config->bmAttributes & USB_CONFIG_ATT_WAKEUP) == 0;
@@ -1762,7 +1764,8 @@
}
spin_unlock_irq(&dev->lock);
- if (len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4))
+ if ((len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4)) ||
+ (len > PAGE_SIZE * 4))
return -EINVAL;
/* we might need to change message format someday */
@@ -1786,7 +1789,8 @@
/* full or low speed config */
dev->config = (void *) kbuf;
total = le16_to_cpu(dev->config->wTotalLength);
- if (!is_valid_config (dev->config) || total >= length)
+ if (!is_valid_config(dev->config, total) ||
+ total > length - USB_DT_DEVICE_SIZE)
goto fail;
kbuf += total;
length -= total;
@@ -1795,10 +1799,13 @@
if (kbuf [1] == USB_DT_CONFIG) {
dev->hs_config = (void *) kbuf;
total = le16_to_cpu(dev->hs_config->wTotalLength);
- if (!is_valid_config (dev->hs_config) || total >= length)
+ if (!is_valid_config(dev->hs_config, total) ||
+ total > length - USB_DT_DEVICE_SIZE)
goto fail;
kbuf += total;
length -= total;
+ } else {
+ dev->hs_config = NULL;
}
/* could support multiple configs, using another encoding! */
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 03fe5c5..b2e3e72 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -1368,7 +1368,11 @@
if (!ret)
break;
}
- if (!ret && !udc->driver)
+ if (ret)
+ ret = -ENODEV;
+ else if (udc->driver)
+ ret = -EBUSY;
+ else
goto found;
} else {
list_for_each_entry(udc, &udc_list, list) {
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 77d0790..a81d9ab 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -330,7 +330,7 @@
/* caller must hold lock */
static void stop_activity(struct dummy *dum)
{
- struct dummy_ep *ep;
+ int i;
/* prevent any more requests */
dum->address = 0;
@@ -338,8 +338,8 @@
/* The timer is left running so that outstanding URBs can fail */
/* nuke any pending requests first, so driver i/o is quiesced */
- list_for_each_entry(ep, &dum->gadget.ep_list, ep.ep_list)
- nuke(dum, ep);
+ for (i = 0; i < DUMMY_ENDPOINTS; ++i)
+ nuke(dum, &dum->ep[i]);
/* driver now does any non-usb quiescing necessary */
}
diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c
index 940304c..02260cf 100644
--- a/drivers/usb/host/uhci-pci.c
+++ b/drivers/usb/host/uhci-pci.c
@@ -129,6 +129,10 @@
if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP)
uhci->wait_for_hp = 1;
+ /* Intel controllers use non-PME wakeup signalling */
+ if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_INTEL)
+ device_set_run_wake(uhci_dev(uhci), 1);
+
/* Set up pointers to PCI-specific functions */
uhci->reset_hc = uhci_pci_reset_hc;
uhci->check_and_reset_hc = uhci_pci_check_and_reset_hc;
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 6afe323..7064892 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -979,6 +979,40 @@
xhci->devs[slot_id] = NULL;
}
+/*
+ * Free a virt_device structure.
+ * If the virt_device added a tt_info (a hub) and has children pointing to
+ * that tt_info, then free the child first. Recursive.
+ * We can't rely on udev at this point to find child-parent relationships.
+ */
+void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id)
+{
+ struct xhci_virt_device *vdev;
+ struct list_head *tt_list_head;
+ struct xhci_tt_bw_info *tt_info, *next;
+ int i;
+
+ vdev = xhci->devs[slot_id];
+ if (!vdev)
+ return;
+
+ tt_list_head = &(xhci->rh_bw[vdev->real_port - 1].tts);
+ list_for_each_entry_safe(tt_info, next, tt_list_head, tt_list) {
+ /* is this a hub device that added a tt_info to the tts list */
+ if (tt_info->slot_id == slot_id) {
+ /* are any devices using this tt_info? */
+ for (i = 1; i < HCS_MAX_SLOTS(xhci->hcs_params1); i++) {
+ vdev = xhci->devs[i];
+ if (vdev && (vdev->tt_info == tt_info))
+ xhci_free_virt_devices_depth_first(
+ xhci, i);
+ }
+ }
+ }
+ /* we are now at a leaf device */
+ xhci_free_virt_device(xhci, slot_id);
+}
+
int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
struct usb_device *udev, gfp_t flags)
{
@@ -1796,7 +1830,7 @@
int size;
int i, j, num_ports;
- del_timer_sync(&xhci->cmd_timer);
+ cancel_delayed_work_sync(&xhci->cmd_timer);
/* Free the Event Ring Segment Table and the actual Event Ring */
size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries);
@@ -1829,8 +1863,8 @@
}
}
- for (i = 1; i < MAX_HC_SLOTS; ++i)
- xhci_free_virt_device(xhci, i);
+ for (i = HCS_MAX_SLOTS(xhci->hcs_params1); i > 0; i--)
+ xhci_free_virt_devices_depth_first(xhci, i);
dma_pool_destroy(xhci->segment_pool);
xhci->segment_pool = NULL;
@@ -2343,9 +2377,9 @@
INIT_LIST_HEAD(&xhci->cmd_list);
- /* init command timeout timer */
- setup_timer(&xhci->cmd_timer, xhci_handle_command_timeout,
- (unsigned long)xhci);
+ /* init command timeout work */
+ INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout);
+ init_completion(&xhci->cmd_ring_stop_completion);
page_size = readl(&xhci->op_regs->page_size);
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
@@ -2384,7 +2418,7 @@
* "physically contiguous and 64-byte (cache line) aligned".
*/
xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma,
- GFP_KERNEL);
+ flags);
if (!xhci->dcbaa)
goto fail;
memset(xhci->dcbaa, 0, sizeof *(xhci->dcbaa));
@@ -2480,7 +2514,7 @@
xhci->erst.entries = dma_alloc_coherent(dev,
sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS, &dma,
- GFP_KERNEL);
+ flags);
if (!xhci->erst.entries)
goto fail;
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
index 79959f1..f2365a4 100644
--- a/drivers/usb/host/xhci-mtk.c
+++ b/drivers/usb/host/xhci-mtk.c
@@ -560,8 +560,10 @@
goto disable_ldos;
irq = platform_get_irq(pdev, 0);
- if (irq < 0)
+ if (irq < 0) {
+ ret = irq;
goto disable_clk;
+ }
/* Initialize dma_mask and coherent_dma_mask to 32-bits */
ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index e96ae80..954abfd 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -165,7 +165,8 @@
pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI ||
- pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI)) {
+ pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI)) {
xhci->quirks |= XHCI_PME_STUCK_QUIRK;
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 797137e..521c181 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -260,23 +260,76 @@
readl(&xhci->dba->doorbell[0]);
}
-static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
+static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci, unsigned long delay)
+{
+ return mod_delayed_work(system_wq, &xhci->cmd_timer, delay);
+}
+
+static struct xhci_command *xhci_next_queued_cmd(struct xhci_hcd *xhci)
+{
+ return list_first_entry_or_null(&xhci->cmd_list, struct xhci_command,
+ cmd_list);
+}
+
+/*
+ * Turn all commands on command ring with status set to "aborted" to no-op trbs.
+ * If there are other commands waiting then restart the ring and kick the timer.
+ * This must be called with command ring stopped and xhci->lock held.
+ */
+static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci,
+ struct xhci_command *cur_cmd)
+{
+ struct xhci_command *i_cmd;
+ u32 cycle_state;
+
+ /* Turn all aborted commands in list to no-ops, then restart */
+ list_for_each_entry(i_cmd, &xhci->cmd_list, cmd_list) {
+
+ if (i_cmd->status != COMP_CMD_ABORT)
+ continue;
+
+ i_cmd->status = COMP_CMD_STOP;
+
+ xhci_dbg(xhci, "Turn aborted command %p to no-op\n",
+ i_cmd->command_trb);
+ /* get cycle state from the original cmd trb */
+ cycle_state = le32_to_cpu(
+ i_cmd->command_trb->generic.field[3]) & TRB_CYCLE;
+ /* modify the command trb to no-op command */
+ i_cmd->command_trb->generic.field[0] = 0;
+ i_cmd->command_trb->generic.field[1] = 0;
+ i_cmd->command_trb->generic.field[2] = 0;
+ i_cmd->command_trb->generic.field[3] = cpu_to_le32(
+ TRB_TYPE(TRB_CMD_NOOP) | cycle_state);
+
+ /*
+ * caller waiting for completion is called when command
+ * completion event is received for these no-op commands
+ */
+ }
+
+ xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
+
+ /* ring command ring doorbell to restart the command ring */
+ if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) &&
+ !(xhci->xhc_state & XHCI_STATE_DYING)) {
+ xhci->current_cmd = cur_cmd;
+ xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
+ xhci_ring_cmd_db(xhci);
+ }
+}
+
+/* Must be called with xhci->lock held, releases and aquires lock back */
+static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags)
{
u64 temp_64;
int ret;
xhci_dbg(xhci, "Abort command ring\n");
- temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
- xhci->cmd_ring_state = CMD_RING_STATE_ABORTED;
+ reinit_completion(&xhci->cmd_ring_stop_completion);
- /*
- * Writing the CMD_RING_ABORT bit should cause a cmd completion event,
- * however on some host hw the CMD_RING_RUNNING bit is correctly cleared
- * but the completion event in never sent. Use the cmd timeout timer to
- * handle those cases. Use twice the time to cover the bit polling retry
- */
- mod_timer(&xhci->cmd_timer, jiffies + (2 * XHCI_CMD_DEFAULT_TIMEOUT));
+ temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
xhci_write_64(xhci, temp_64 | CMD_RING_ABORT,
&xhci->op_regs->cmd_ring);
@@ -296,16 +349,30 @@
udelay(1000);
ret = xhci_handshake(&xhci->op_regs->cmd_ring,
CMD_RING_RUNNING, 0, 3 * 1000 * 1000);
- if (ret == 0)
- return 0;
-
- xhci_err(xhci, "Stopped the command ring failed, "
- "maybe the host is dead\n");
- del_timer(&xhci->cmd_timer);
- xhci->xhc_state |= XHCI_STATE_DYING;
- xhci_quiesce(xhci);
- xhci_halt(xhci);
- return -ESHUTDOWN;
+ if (ret < 0) {
+ xhci_err(xhci, "Stopped the command ring failed, "
+ "maybe the host is dead\n");
+ xhci->xhc_state |= XHCI_STATE_DYING;
+ xhci_quiesce(xhci);
+ xhci_halt(xhci);
+ return -ESHUTDOWN;
+ }
+ }
+ /*
+ * Writing the CMD_RING_ABORT bit should cause a cmd completion event,
+ * however on some host hw the CMD_RING_RUNNING bit is correctly cleared
+ * but the completion event in never sent. Wait 2 secs (arbitrary
+ * number) to handle those cases after negation of CMD_RING_RUNNING.
+ */
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ ret = wait_for_completion_timeout(&xhci->cmd_ring_stop_completion,
+ msecs_to_jiffies(2000));
+ spin_lock_irqsave(&xhci->lock, flags);
+ if (!ret) {
+ xhci_dbg(xhci, "No stop event for abort, ring start fail?\n");
+ xhci_cleanup_command_queue(xhci);
+ } else {
+ xhci_handle_stopped_cmd_ring(xhci, xhci_next_queued_cmd(xhci));
}
return 0;
@@ -850,17 +917,6 @@
spin_lock_irqsave(&xhci->lock, flags);
ep->stop_cmds_pending--;
- if (xhci->xhc_state & XHCI_STATE_REMOVING) {
- spin_unlock_irqrestore(&xhci->lock, flags);
- return;
- }
- if (xhci->xhc_state & XHCI_STATE_DYING) {
- xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "Stop EP timer ran, but another timer marked "
- "xHCI as DYING, exiting.");
- spin_unlock_irqrestore(&xhci->lock, flags);
- return;
- }
if (!(ep->stop_cmds_pending == 0 && (ep->ep_state & EP_HALT_PENDING))) {
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Stop EP timer ran, but no command pending, "
@@ -1211,101 +1267,62 @@
xhci_complete_del_and_free_cmd(cur_cmd, COMP_CMD_ABORT);
}
-/*
- * Turn all commands on command ring with status set to "aborted" to no-op trbs.
- * If there are other commands waiting then restart the ring and kick the timer.
- * This must be called with command ring stopped and xhci->lock held.
- */
-static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci,
- struct xhci_command *cur_cmd)
-{
- struct xhci_command *i_cmd, *tmp_cmd;
- u32 cycle_state;
-
- /* Turn all aborted commands in list to no-ops, then restart */
- list_for_each_entry_safe(i_cmd, tmp_cmd, &xhci->cmd_list,
- cmd_list) {
-
- if (i_cmd->status != COMP_CMD_ABORT)
- continue;
-
- i_cmd->status = COMP_CMD_STOP;
-
- xhci_dbg(xhci, "Turn aborted command %p to no-op\n",
- i_cmd->command_trb);
- /* get cycle state from the original cmd trb */
- cycle_state = le32_to_cpu(
- i_cmd->command_trb->generic.field[3]) & TRB_CYCLE;
- /* modify the command trb to no-op command */
- i_cmd->command_trb->generic.field[0] = 0;
- i_cmd->command_trb->generic.field[1] = 0;
- i_cmd->command_trb->generic.field[2] = 0;
- i_cmd->command_trb->generic.field[3] = cpu_to_le32(
- TRB_TYPE(TRB_CMD_NOOP) | cycle_state);
-
- /*
- * caller waiting for completion is called when command
- * completion event is received for these no-op commands
- */
- }
-
- xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
-
- /* ring command ring doorbell to restart the command ring */
- if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) &&
- !(xhci->xhc_state & XHCI_STATE_DYING)) {
- xhci->current_cmd = cur_cmd;
- mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
- xhci_ring_cmd_db(xhci);
- }
- return;
-}
-
-
-void xhci_handle_command_timeout(unsigned long data)
+void xhci_handle_command_timeout(struct work_struct *work)
{
struct xhci_hcd *xhci;
int ret;
unsigned long flags;
u64 hw_ring_state;
- bool second_timeout = false;
- xhci = (struct xhci_hcd *) data;
- /* mark this command to be cancelled */
+ xhci = container_of(to_delayed_work(work), struct xhci_hcd, cmd_timer);
+
spin_lock_irqsave(&xhci->lock, flags);
- if (xhci->current_cmd) {
- if (xhci->current_cmd->status == COMP_CMD_ABORT)
- second_timeout = true;
- xhci->current_cmd->status = COMP_CMD_ABORT;
+
+ /*
+ * If timeout work is pending, or current_cmd is NULL, it means we
+ * raced with command completion. Command is handled so just return.
+ */
+ if (!xhci->current_cmd || delayed_work_pending(&xhci->cmd_timer)) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return;
}
+ /* mark this command to be cancelled */
+ xhci->current_cmd->status = COMP_CMD_ABORT;
/* Make sure command ring is running before aborting it */
hw_ring_state = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
if ((xhci->cmd_ring_state & CMD_RING_STATE_RUNNING) &&
(hw_ring_state & CMD_RING_RUNNING)) {
- spin_unlock_irqrestore(&xhci->lock, flags);
+ /* Prevent new doorbell, and start command abort */
+ xhci->cmd_ring_state = CMD_RING_STATE_ABORTED;
xhci_dbg(xhci, "Command timeout\n");
- ret = xhci_abort_cmd_ring(xhci);
+ ret = xhci_abort_cmd_ring(xhci, flags);
if (unlikely(ret == -ESHUTDOWN)) {
xhci_err(xhci, "Abort command ring failed\n");
xhci_cleanup_command_queue(xhci);
+ spin_unlock_irqrestore(&xhci->lock, flags);
usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
xhci_dbg(xhci, "xHCI host controller is dead.\n");
+
+ return;
}
- return;
+
+ goto time_out_completed;
}
- /* command ring failed to restart, or host removed. Bail out */
- if (second_timeout || xhci->xhc_state & XHCI_STATE_REMOVING) {
- spin_unlock_irqrestore(&xhci->lock, flags);
- xhci_dbg(xhci, "command timed out twice, ring start fail?\n");
+ /* host removed. Bail out */
+ if (xhci->xhc_state & XHCI_STATE_REMOVING) {
+ xhci_dbg(xhci, "host removed, ring start fail?\n");
xhci_cleanup_command_queue(xhci);
- return;
+
+ goto time_out_completed;
}
/* command timeout on stopped ring, ring can't be aborted */
xhci_dbg(xhci, "Command timeout on stopped ring\n");
xhci_handle_stopped_cmd_ring(xhci, xhci->current_cmd);
+
+time_out_completed:
spin_unlock_irqrestore(&xhci->lock, flags);
return;
}
@@ -1338,7 +1355,7 @@
cmd = list_entry(xhci->cmd_list.next, struct xhci_command, cmd_list);
- del_timer(&xhci->cmd_timer);
+ cancel_delayed_work(&xhci->cmd_timer);
trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event);
@@ -1346,7 +1363,7 @@
/* If CMD ring stopped we own the trbs between enqueue and dequeue */
if (cmd_comp_code == COMP_CMD_STOP) {
- xhci_handle_stopped_cmd_ring(xhci, cmd);
+ complete_all(&xhci->cmd_ring_stop_completion);
return;
}
@@ -1364,8 +1381,11 @@
*/
if (cmd_comp_code == COMP_CMD_ABORT) {
xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
- if (cmd->status == COMP_CMD_ABORT)
+ if (cmd->status == COMP_CMD_ABORT) {
+ if (xhci->current_cmd == cmd)
+ xhci->current_cmd = NULL;
goto event_handled;
+ }
}
cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3]));
@@ -1426,7 +1446,9 @@
if (cmd->cmd_list.next != &xhci->cmd_list) {
xhci->current_cmd = list_entry(cmd->cmd_list.next,
struct xhci_command, cmd_list);
- mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
+ xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
+ } else if (xhci->current_cmd == cmd) {
+ xhci->current_cmd = NULL;
}
event_handled:
@@ -3920,9 +3942,9 @@
/* if there are no other commands queued we start the timeout timer */
if (xhci->cmd_list.next == &cmd->cmd_list &&
- !timer_pending(&xhci->cmd_timer)) {
+ !delayed_work_pending(&xhci->cmd_timer)) {
xhci->current_cmd = cmd;
- mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
+ xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
}
queue_trb(xhci, xhci->cmd_ring, false, field1, field2, field3,
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 1a4ca02..34e23c7 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1529,19 +1529,6 @@
xhci_urb_free_priv(urb_priv);
return ret;
}
- if ((xhci->xhc_state & XHCI_STATE_DYING) ||
- (xhci->xhc_state & XHCI_STATE_HALTED)) {
- xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "Ep 0x%x: URB %p to be canceled on "
- "non-responsive xHCI host.",
- urb->ep->desc.bEndpointAddress, urb);
- /* Let the stop endpoint command watchdog timer (which set this
- * state) finish cleaning up the endpoint TD lists. We must
- * have caught it in the middle of dropping a lock and giving
- * back an URB.
- */
- goto done;
- }
ep_index = xhci_get_endpoint_index(&urb->ep->desc);
ep = &xhci->devs[urb->dev->slot_id]->eps[ep_index];
@@ -3783,8 +3770,10 @@
mutex_lock(&xhci->mutex);
- if (xhci->xhc_state) /* dying, removing or halted */
+ if (xhci->xhc_state) { /* dying, removing or halted */
+ ret = -ESHUTDOWN;
goto out;
+ }
if (!udev->slot_id) {
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index f945380..c525722 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1571,7 +1571,8 @@
#define CMD_RING_STATE_STOPPED (1 << 2)
struct list_head cmd_list;
unsigned int cmd_ring_reserved_trbs;
- struct timer_list cmd_timer;
+ struct delayed_work cmd_timer;
+ struct completion cmd_ring_stop_completion;
struct xhci_command *current_cmd;
struct xhci_ring *event_ring;
struct xhci_erst erst;
@@ -1941,7 +1942,7 @@
unsigned int slot_id, unsigned int ep_index,
struct xhci_dequeue_state *deq_state);
void xhci_stop_endpoint_command_watchdog(unsigned long arg);
-void xhci_handle_command_timeout(unsigned long data);
+void xhci_handle_command_timeout(struct work_struct *work);
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
unsigned int ep_index, unsigned int stream_id);
diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c
index 310238c..8967980 100644
--- a/drivers/usb/musb/blackfin.c
+++ b/drivers/usb/musb/blackfin.c
@@ -469,6 +469,7 @@
.init = bfin_musb_init,
.exit = bfin_musb_exit,
+ .fifo_offset = bfin_fifo_offset,
.readb = bfin_readb,
.writeb = bfin_writeb,
.readw = bfin_readw,
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index c3e172e..338575f 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -578,11 +578,11 @@
| MUSB_PORT_STAT_RESUME;
musb->rh_timer = jiffies
+ msecs_to_jiffies(USB_RESUME_TIMEOUT);
- musb->need_finish_resume = 1;
-
musb->xceiv->otg->state = OTG_STATE_A_HOST;
musb->is_active = 1;
musb_host_resume_root_hub(musb);
+ schedule_delayed_work(&musb->finish_resume_work,
+ msecs_to_jiffies(USB_RESUME_TIMEOUT));
break;
case OTG_STATE_B_WAIT_ACON:
musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
@@ -2691,11 +2691,6 @@
mask = MUSB_DEVCTL_BDEVICE | MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV;
if ((devctl & mask) != (musb->context.devctl & mask))
musb->port1_status = 0;
- if (musb->need_finish_resume) {
- musb->need_finish_resume = 0;
- schedule_delayed_work(&musb->finish_resume_work,
- msecs_to_jiffies(USB_RESUME_TIMEOUT));
- }
/*
* The USB HUB code expects the device to be in RPM_ACTIVE once it came
@@ -2747,12 +2742,6 @@
musb_restore_context(musb);
- if (musb->need_finish_resume) {
- musb->need_finish_resume = 0;
- schedule_delayed_work(&musb->finish_resume_work,
- msecs_to_jiffies(USB_RESUME_TIMEOUT));
- }
-
spin_lock_irqsave(&musb->lock, flags);
error = musb_run_resume_work(musb);
if (error)
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 91817d7..854fbf7 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -216,6 +216,7 @@
void (*pre_root_reset_end)(struct musb *musb);
void (*post_root_reset_end)(struct musb *musb);
int (*phy_callback)(enum musb_vbus_id_status status);
+ void (*clear_ep_rxintr)(struct musb *musb, int epnum);
};
/*
@@ -409,7 +410,6 @@
/* is_suspended means USB B_PERIPHERAL suspend */
unsigned is_suspended:1;
- unsigned need_finish_resume :1;
/* may_wakeup means remote wakeup is enabled */
unsigned may_wakeup:1;
@@ -626,4 +626,10 @@
musb->ops->post_root_reset_end(musb);
}
+static inline void musb_platform_clear_ep_rxintr(struct musb *musb, int epnum)
+{
+ if (musb->ops->clear_ep_rxintr)
+ musb->ops->clear_ep_rxintr(musb, epnum);
+}
+
#endif /* __MUSB_CORE_H__ */
diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c
index 9b22d94..534a3f6 100644
--- a/drivers/usb/musb/musb_debugfs.c
+++ b/drivers/usb/musb/musb_debugfs.c
@@ -114,6 +114,7 @@
unsigned i;
seq_printf(s, "MUSB (M)HDRC Register Dump\n");
+ pm_runtime_get_sync(musb->controller);
for (i = 0; i < ARRAY_SIZE(musb_regmap); i++) {
switch (musb_regmap[i].size) {
@@ -132,6 +133,8 @@
}
}
+ pm_runtime_mark_last_busy(musb->controller);
+ pm_runtime_put_autosuspend(musb->controller);
return 0;
}
@@ -145,7 +148,10 @@
struct musb *musb = s->private;
unsigned test;
+ pm_runtime_get_sync(musb->controller);
test = musb_readb(musb->mregs, MUSB_TESTMODE);
+ pm_runtime_mark_last_busy(musb->controller);
+ pm_runtime_put_autosuspend(musb->controller);
if (test & MUSB_TEST_FORCE_HOST)
seq_printf(s, "force host\n");
@@ -194,11 +200,12 @@
u8 test;
char buf[18];
+ pm_runtime_get_sync(musb->controller);
test = musb_readb(musb->mregs, MUSB_TESTMODE);
if (test) {
dev_err(musb->controller, "Error: test mode is already set. "
"Please do USB Bus Reset to start a new test.\n");
- return count;
+ goto ret;
}
memset(buf, 0x00, sizeof(buf));
@@ -234,6 +241,9 @@
musb_writeb(musb->mregs, MUSB_TESTMODE, test);
+ret:
+ pm_runtime_mark_last_busy(musb->controller);
+ pm_runtime_put_autosuspend(musb->controller);
return count;
}
@@ -254,8 +264,13 @@
switch (musb->xceiv->otg->state) {
case OTG_STATE_A_HOST:
case OTG_STATE_A_WAIT_BCON:
+ pm_runtime_get_sync(musb->controller);
+
reg = musb_readb(musb->mregs, MUSB_DEVCTL);
connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0;
+
+ pm_runtime_mark_last_busy(musb->controller);
+ pm_runtime_put_autosuspend(musb->controller);
break;
default:
connect = -1;
@@ -284,6 +299,7 @@
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
+ pm_runtime_get_sync(musb->controller);
if (!strncmp(buf, "0", 1)) {
switch (musb->xceiv->otg->state) {
case OTG_STATE_A_HOST:
@@ -314,6 +330,8 @@
}
}
+ pm_runtime_mark_last_busy(musb->controller);
+ pm_runtime_put_autosuspend(musb->controller);
return count;
}
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index feae156..9f125e1 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -267,6 +267,17 @@
pm_runtime_put_autosuspend(dev);
}
+void dsps_musb_clear_ep_rxintr(struct musb *musb, int epnum)
+{
+ u32 epintr;
+ struct dsps_glue *glue = dev_get_drvdata(musb->controller->parent);
+ const struct dsps_musb_wrapper *wrp = glue->wrp;
+
+ /* musb->lock might already been held */
+ epintr = (1 << epnum) << wrp->rxep_shift;
+ musb_writel(musb->ctrl_base, wrp->epintr_status, epintr);
+}
+
static irqreturn_t dsps_interrupt(int irq, void *hci)
{
struct musb *musb = hci;
@@ -622,6 +633,7 @@
.set_mode = dsps_musb_set_mode,
.recover = dsps_musb_recover,
+ .clear_ep_rxintr = dsps_musb_clear_ep_rxintr,
};
static u64 musb_dmamask = DMA_BIT_MASK(32);
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 53bc4ce..8064514 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -2374,12 +2374,11 @@
int is_in = usb_pipein(urb->pipe);
int status = 0;
u16 csr;
+ struct dma_channel *dma = NULL;
musb_ep_select(regs, hw_end);
if (is_dma_capable()) {
- struct dma_channel *dma;
-
dma = is_in ? ep->rx_channel : ep->tx_channel;
if (dma) {
status = ep->musb->dma_controller->channel_abort(dma);
@@ -2395,10 +2394,9 @@
/* giveback saves bulk toggle */
csr = musb_h_flush_rxfifo(ep, 0);
- /* REVISIT we still get an irq; should likely clear the
- * endpoint's irq status here to avoid bogus irqs.
- * clearing that status is platform-specific...
- */
+ /* clear the endpoint's irq status here to avoid bogus irqs */
+ if (is_dma_capable() && dma)
+ musb_platform_clear_ep_rxintr(musb, ep->epnum);
} else if (ep->epnum) {
musb_h_tx_flush_fifo(ep);
csr = musb_readw(epio, MUSB_TXCSR);
diff --git a/drivers/usb/musb/musbhsdma.h b/drivers/usb/musb/musbhsdma.h
index f7b13fd2..a3dcbd5 100644
--- a/drivers/usb/musb/musbhsdma.h
+++ b/drivers/usb/musb/musbhsdma.h
@@ -157,5 +157,5 @@
void __iomem *base;
u8 channel_count;
u8 used_channels;
- u8 irq;
+ int irq;
};
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index c5527d4..d2c4876 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -8,7 +8,7 @@
config USB_OTG_WAKELOCK
bool "Hold a wakelock when USB connected"
- depends on WAKELOCK
+ depends on PM_WAKELOCKS
select USB_OTG_UTILS
help
Select this to automatically hold a wakelock when USB is
diff --git a/drivers/usb/phy/otg-wakelock.c b/drivers/usb/phy/otg-wakelock.c
index 479376b..ecd7410 100644
--- a/drivers/usb/phy/otg-wakelock.c
+++ b/drivers/usb/phy/otg-wakelock.c
@@ -19,7 +19,6 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/notifier.h>
-#include <linux/wakelock.h>
#include <linux/spinlock.h>
#include <linux/usb/otg.h>
@@ -42,7 +41,7 @@
struct otgwl_lock {
char name[40];
- struct wake_lock wakelock;
+ struct wakeup_source wakesrc;
bool held;
};
@@ -57,22 +56,21 @@
static void otgwl_hold(struct otgwl_lock *lock)
{
if (!lock->held) {
- wake_lock(&lock->wakelock);
+ __pm_stay_awake(&lock->wakesrc);
lock->held = true;
}
}
static void otgwl_temporary_hold(struct otgwl_lock *lock)
{
- wake_lock_timeout(&lock->wakelock,
- msecs_to_jiffies(TEMPORARY_HOLD_TIME));
+ __pm_wakeup_event(&lock->wakesrc, TEMPORARY_HOLD_TIME);
lock->held = false;
}
static void otgwl_drop(struct otgwl_lock *lock)
{
if (lock->held) {
- wake_unlock(&lock->wakelock);
+ __pm_relax(&lock->wakesrc);
lock->held = false;
}
}
@@ -151,8 +149,7 @@
snprintf(vbus_lock.name, sizeof(vbus_lock.name), "vbus-%s",
dev_name(otgwl_xceiv->dev));
- wake_lock_init(&vbus_lock.wakelock, WAKE_LOCK_SUSPEND,
- vbus_lock.name);
+ wakeup_source_init(&vbus_lock.wakesrc, vbus_lock.name);
otgwl_nb.notifier_call = otgwl_otg_notifications;
ret = usb_register_notifier(otgwl_xceiv, &otgwl_nb);
@@ -162,7 +159,7 @@
" failed\n", __func__,
dev_name(otgwl_xceiv->dev));
otgwl_xceiv = NULL;
- wake_lock_destroy(&vbus_lock.wakelock);
+ wakeup_source_trash(&vbus_lock.wakesrc);
return ret;
}
diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c
index 42a1afe..5f5f198 100644
--- a/drivers/usb/phy/phy-am335x-control.c
+++ b/drivers/usb/phy/phy-am335x-control.c
@@ -134,10 +134,12 @@
return NULL;
dev = bus_find_device(&platform_bus_type, NULL, node, match);
+ of_node_put(node);
if (!dev)
return NULL;
ctrl_usb = dev_get_drvdata(dev);
+ put_device(dev);
if (!ctrl_usb)
return NULL;
return &ctrl_usb->phy_ctrl;
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index f139488..e98590a 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -99,6 +99,8 @@
r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
value, index, NULL, 0, DEFAULT_TIMEOUT);
+ if (r < 0)
+ dev_err(&dev->dev, "failed to send control message: %d\n", r);
return r;
}
@@ -116,7 +118,20 @@
r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
value, index, buf, bufsize, DEFAULT_TIMEOUT);
- return r;
+ if (r < bufsize) {
+ if (r >= 0) {
+ dev_err(&dev->dev,
+ "short control message received (%d < %u)\n",
+ r, bufsize);
+ r = -EIO;
+ }
+
+ dev_err(&dev->dev, "failed to receive control message: %d\n",
+ r);
+ return r;
+ }
+
+ return 0;
}
static int ch341_set_baudrate(struct usb_device *dev,
@@ -158,9 +173,9 @@
static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
{
+ const unsigned int size = 2;
char *buffer;
int r;
- const unsigned size = 8;
unsigned long flags;
buffer = kmalloc(size, GFP_KERNEL);
@@ -171,14 +186,9 @@
if (r < 0)
goto out;
- /* setup the private status if available */
- if (r == 2) {
- r = 0;
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
- spin_unlock_irqrestore(&priv->lock, flags);
- } else
- r = -EPROTO;
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
+ spin_unlock_irqrestore(&priv->lock, flags);
out: kfree(buffer);
return r;
@@ -188,9 +198,9 @@
static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
{
+ const unsigned int size = 2;
char *buffer;
int r;
- const unsigned size = 8;
buffer = kmalloc(size, GFP_KERNEL);
if (!buffer)
@@ -253,7 +263,6 @@
spin_lock_init(&priv->lock);
priv->baud_rate = DEFAULT_BAUD_RATE;
- priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR;
r = ch341_configure(port->serial->dev, priv);
if (r < 0)
@@ -315,7 +324,7 @@
r = ch341_configure(serial->dev, priv);
if (r)
- goto out;
+ return r;
if (tty)
ch341_set_termios(tty, port, NULL);
@@ -325,12 +334,19 @@
if (r) {
dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n",
__func__, r);
- goto out;
+ return r;
}
r = usb_serial_generic_open(tty, port);
+ if (r)
+ goto err_kill_interrupt_urb;
-out: return r;
+ return 0;
+
+err_kill_interrupt_urb:
+ usb_kill_urb(port->interrupt_in_urb);
+
+ return r;
}
/* Old_termios contains the original termios settings and
@@ -345,26 +361,25 @@
baud_rate = tty_get_baud_rate(tty);
- priv->baud_rate = baud_rate;
-
if (baud_rate) {
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
- spin_unlock_irqrestore(&priv->lock, flags);
+ priv->baud_rate = baud_rate;
ch341_set_baudrate(port->serial->dev, priv);
- } else {
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
- spin_unlock_irqrestore(&priv->lock, flags);
}
- ch341_set_handshake(port->serial->dev, priv->line_control);
-
/* Unimplemented:
* (cflag & CSIZE) : data bits [5, 8]
* (cflag & PARENB) : parity {NONE, EVEN, ODD}
* (cflag & CSTOPB) : stop bits [1, 2]
*/
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (C_BAUD(tty) == B0)
+ priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
+ else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
+ priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ ch341_set_handshake(port->serial->dev, priv->line_control);
}
static void ch341_break_ctl(struct tty_struct *tty, int break_state)
@@ -539,14 +554,23 @@
static int ch341_reset_resume(struct usb_serial *serial)
{
- struct ch341_private *priv;
-
- priv = usb_get_serial_port_data(serial->port[0]);
+ struct usb_serial_port *port = serial->port[0];
+ struct ch341_private *priv = usb_get_serial_port_data(port);
+ int ret;
/* reconfigure ch341 serial port after bus-reset */
ch341_configure(serial->dev, priv);
- return 0;
+ if (tty_port_initialized(&port->port)) {
+ ret = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
+ if (ret) {
+ dev_err(&port->dev, "failed to submit interrupt urb: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return usb_serial_generic_resume(serial);
}
static struct usb_serial_driver ch341_device = {
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index 5f17a3b..80260b0 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -50,6 +50,7 @@
#define CYBERJACK_PRODUCT_ID 0x0100
/* Function prototypes */
+static int cyberjack_attach(struct usb_serial *serial);
static int cyberjack_port_probe(struct usb_serial_port *port);
static int cyberjack_port_remove(struct usb_serial_port *port);
static int cyberjack_open(struct tty_struct *tty,
@@ -77,6 +78,7 @@
.description = "Reiner SCT Cyberjack USB card reader",
.id_table = id_table,
.num_ports = 1,
+ .attach = cyberjack_attach,
.port_probe = cyberjack_port_probe,
.port_remove = cyberjack_port_remove,
.open = cyberjack_open,
@@ -100,6 +102,14 @@
short wrsent; /* Data already sent */
};
+static int cyberjack_attach(struct usb_serial *serial)
+{
+ if (serial->num_bulk_out < serial->num_ports)
+ return -ENODEV;
+
+ return 0;
+}
+
static int cyberjack_port_probe(struct usb_serial_port *port)
{
struct cyberjack_private *priv;
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index 97cabf8..b2f2e87 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -1043,6 +1043,7 @@
"%s - usb_submit_urb(write bulk) failed with status = %d\n",
__func__, status);
count = status;
+ kfree(buffer);
}
/* we are done with this urb, so let the host driver
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 11c05ce..36dfe99 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -2754,6 +2754,11 @@
EDGE_COMPATIBILITY_MASK1,
EDGE_COMPATIBILITY_MASK2 };
+ if (serial->num_bulk_in < 1 || serial->num_interrupt_in < 1) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
dev = serial->dev;
/* create our private serial structure */
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index fce82fd..c02808a 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -1499,8 +1499,7 @@
dev_dbg(dev, "%s - Download successful -- Device rebooting...\n", __func__);
- /* return an error on purpose */
- return -ENODEV;
+ return 1;
}
stayinbootmode:
@@ -1508,7 +1507,7 @@
dev_dbg(dev, "%s - STAYING IN BOOT MODE\n", __func__);
serial->product_info.TiMode = TI_MODE_BOOT;
- return 0;
+ return 1;
}
static int ti_do_config(struct edgeport_port *port, int feature, int on)
@@ -2549,6 +2548,13 @@
int status;
u16 product_id;
+ /* Make sure we have the required endpoints when in download mode. */
+ if (serial->interface->cur_altsetting->desc.bNumEndpoints > 1) {
+ if (serial->num_bulk_in < serial->num_ports ||
+ serial->num_bulk_out < serial->num_ports)
+ return -ENODEV;
+ }
+
/* create our private serial structure */
edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL);
if (!edge_serial)
@@ -2556,14 +2562,18 @@
mutex_init(&edge_serial->es_lock);
edge_serial->serial = serial;
+ INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work);
usb_set_serial_data(serial, edge_serial);
status = download_fw(edge_serial);
- if (status) {
+ if (status < 0) {
kfree(edge_serial);
return status;
}
+ if (status > 0)
+ return 1; /* bind but do not register any ports */
+
product_id = le16_to_cpu(
edge_serial->serial->dev->descriptor.idProduct);
@@ -2575,7 +2585,6 @@
}
}
- INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work);
edge_heartbeat_schedule(edge_serial);
return 0;
@@ -2583,6 +2592,9 @@
static void edge_disconnect(struct usb_serial *serial)
{
+ struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
+
+ cancel_delayed_work_sync(&edge_serial->heartbeat_work);
}
static void edge_release(struct usb_serial *serial)
diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c
index 344b4ee..d57fb51 100644
--- a/drivers/usb/serial/iuu_phoenix.c
+++ b/drivers/usb/serial/iuu_phoenix.c
@@ -68,6 +68,16 @@
u32 clk;
};
+static int iuu_attach(struct usb_serial *serial)
+{
+ unsigned char num_ports = serial->num_ports;
+
+ if (serial->num_bulk_in < num_ports || serial->num_bulk_out < num_ports)
+ return -ENODEV;
+
+ return 0;
+}
+
static int iuu_port_probe(struct usb_serial_port *port)
{
struct iuu_private *priv;
@@ -1196,6 +1206,7 @@
.tiocmset = iuu_tiocmset,
.set_termios = iuu_set_termios,
.init_termios = iuu_init_termios,
+ .attach = iuu_attach,
.port_probe = iuu_port_probe,
.port_remove = iuu_port_remove,
};
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index e49ad0c..83523fc 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -699,6 +699,19 @@
MODULE_FIRMWARE("keyspan_pda/xircom_pgs.fw");
#endif
+static int keyspan_pda_attach(struct usb_serial *serial)
+{
+ unsigned char num_ports = serial->num_ports;
+
+ if (serial->num_bulk_out < num_ports ||
+ serial->num_interrupt_in < num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int keyspan_pda_port_probe(struct usb_serial_port *port)
{
@@ -776,6 +789,7 @@
.break_ctl = keyspan_pda_break_ctl,
.tiocmget = keyspan_pda_tiocmget,
.tiocmset = keyspan_pda_tiocmset,
+ .attach = keyspan_pda_attach,
.port_probe = keyspan_pda_port_probe,
.port_remove = keyspan_pda_port_remove,
};
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index fc5d3a7..6cb4575 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -192,10 +192,11 @@
status_buf, KLSI_STATUSBUF_LEN,
10000
);
- if (rc < 0)
- dev_err(&port->dev, "Reading line status failed (error = %d)\n",
- rc);
- else {
+ if (rc != KLSI_STATUSBUF_LEN) {
+ dev_err(&port->dev, "reading line status failed: %d\n", rc);
+ if (rc >= 0)
+ rc = -EIO;
+ } else {
status = get_unaligned_le16(status_buf);
dev_info(&port->serial->dev->dev, "read status %x %x\n",
@@ -296,7 +297,7 @@
rc = usb_serial_generic_open(tty, port);
if (rc) {
retval = rc;
- goto exit;
+ goto err_free_cfg;
}
rc = usb_control_msg(port->serial->dev,
@@ -311,21 +312,38 @@
if (rc < 0) {
dev_err(&port->dev, "Enabling read failed (error = %d)\n", rc);
retval = rc;
+ goto err_generic_close;
} else
dev_dbg(&port->dev, "%s - enabled reading\n", __func__);
rc = klsi_105_get_line_state(port, &line_state);
- if (rc >= 0) {
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_state = line_state;
- spin_unlock_irqrestore(&priv->lock, flags);
- dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, line_state);
- retval = 0;
- } else
+ if (rc < 0) {
retval = rc;
+ goto err_disable_read;
+ }
-exit:
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->line_state = line_state;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__,
+ line_state);
+
+ return 0;
+
+err_disable_read:
+ usb_control_msg(port->serial->dev,
+ usb_sndctrlpipe(port->serial->dev, 0),
+ KL5KUSB105A_SIO_CONFIGURE,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
+ 0, /* index */
+ NULL, 0,
+ KLSI_TIMEOUT);
+err_generic_close:
+ usb_serial_generic_close(port);
+err_free_cfg:
kfree(cfg);
+
return retval;
}
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index 2363654..813035f 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -51,6 +51,7 @@
/* Function prototypes */
+static int kobil_attach(struct usb_serial *serial);
static int kobil_port_probe(struct usb_serial_port *probe);
static int kobil_port_remove(struct usb_serial_port *probe);
static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port);
@@ -86,6 +87,7 @@
.description = "KOBIL USB smart card terminal",
.id_table = id_table,
.num_ports = 1,
+ .attach = kobil_attach,
.port_probe = kobil_port_probe,
.port_remove = kobil_port_remove,
.ioctl = kobil_ioctl,
@@ -113,6 +115,16 @@
};
+static int kobil_attach(struct usb_serial *serial)
+{
+ if (serial->num_interrupt_out < serial->num_ports) {
+ dev_err(&serial->interface->dev, "missing interrupt-out endpoint\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int kobil_port_probe(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index de9992b..136ff5e 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -65,8 +65,6 @@
struct urb *write_urb_pool[NUM_URBS];
};
-static struct usb_serial_driver moschip7720_2port_driver;
-
#define USB_VENDOR_ID_MOSCHIP 0x9710
#define MOSCHIP_DEVICE_ID_7720 0x7720
#define MOSCHIP_DEVICE_ID_7715 0x7715
@@ -970,25 +968,6 @@
tty_port_tty_wakeup(&mos7720_port->port->port);
}
-/*
- * mos77xx_probe
- * this function installs the appropriate read interrupt endpoint callback
- * depending on whether the device is a 7720 or 7715, thus avoiding costly
- * run-time checks in the high-frequency callback routine itself.
- */
-static int mos77xx_probe(struct usb_serial *serial,
- const struct usb_device_id *id)
-{
- if (id->idProduct == MOSCHIP_DEVICE_ID_7715)
- moschip7720_2port_driver.read_int_callback =
- mos7715_interrupt_callback;
- else
- moschip7720_2port_driver.read_int_callback =
- mos7720_interrupt_callback;
-
- return 0;
-}
-
static int mos77xx_calc_num_ports(struct usb_serial *serial)
{
u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
@@ -1920,6 +1899,11 @@
u16 product;
int ret_val;
+ if (serial->num_bulk_in < 2 || serial->num_bulk_out < 2) {
+ dev_err(&serial->interface->dev, "missing bulk endpoints\n");
+ return -ENODEV;
+ }
+
product = le16_to_cpu(serial->dev->descriptor.idProduct);
dev = serial->dev;
@@ -1944,19 +1928,18 @@
tmp->interrupt_in_endpointAddress;
serial->port[1]->interrupt_in_urb = NULL;
serial->port[1]->interrupt_in_buffer = NULL;
+
+ if (serial->port[0]->interrupt_in_urb) {
+ struct urb *urb = serial->port[0]->interrupt_in_urb;
+
+ urb->complete = mos7715_interrupt_callback;
+ }
}
/* setting configuration feature to one */
usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
(__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5000);
- /* start the interrupt urb */
- ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL);
- if (ret_val)
- dev_err(&dev->dev,
- "%s - Error %d submitting control urb\n",
- __func__, ret_val);
-
#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
if (product == MOSCHIP_DEVICE_ID_7715) {
ret_val = mos7715_parport_init(serial);
@@ -1964,6 +1947,13 @@
return ret_val;
}
#endif
+ /* start the interrupt urb */
+ ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL);
+ if (ret_val) {
+ dev_err(&dev->dev, "failed to submit interrupt urb: %d\n",
+ ret_val);
+ }
+
/* LSR For Port 1 */
read_mos_reg(serial, 0, MOS7720_LSR, &data);
dev_dbg(&dev->dev, "LSR:%x\n", data);
@@ -1973,6 +1963,8 @@
static void mos7720_release(struct usb_serial *serial)
{
+ usb_kill_urb(serial->port[0]->interrupt_in_urb);
+
#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
/* close the parallel port */
@@ -2056,7 +2048,6 @@
.close = mos7720_close,
.throttle = mos7720_throttle,
.unthrottle = mos7720_unthrottle,
- .probe = mos77xx_probe,
.attach = mos7720_startup,
.release = mos7720_release,
.port_probe = mos7720_port_probe,
@@ -2070,7 +2061,7 @@
.chars_in_buffer = mos7720_chars_in_buffer,
.break_ctl = mos7720_break,
.read_bulk_callback = mos7720_bulk_in_callback,
- .read_int_callback = NULL /* dynamically assigned in probe() */
+ .read_int_callback = mos7720_interrupt_callback,
};
static struct usb_serial_driver * const serial_drivers[] = {
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 57426d7..4f9af47 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -2116,6 +2116,17 @@
return mos7840_num_ports;
}
+static int mos7840_attach(struct usb_serial *serial)
+{
+ if (serial->num_bulk_in < serial->num_ports ||
+ serial->num_bulk_out < serial->num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int mos7840_port_probe(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
@@ -2391,6 +2402,7 @@
.tiocmset = mos7840_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
+ .attach = mos7840_attach,
.port_probe = mos7840_port_probe,
.port_remove = mos7840_port_remove,
.read_bulk_callback = mos7840_bulk_in_callback,
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index f6c6900..a180b17 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -38,6 +38,7 @@
const unsigned char *buf, int count);
static int omninet_write_room(struct tty_struct *tty);
static void omninet_disconnect(struct usb_serial *serial);
+static int omninet_attach(struct usb_serial *serial);
static int omninet_port_probe(struct usb_serial_port *port);
static int omninet_port_remove(struct usb_serial_port *port);
@@ -56,6 +57,7 @@
.description = "ZyXEL - omni.net lcd plus usb",
.id_table = id_table,
.num_ports = 1,
+ .attach = omninet_attach,
.port_probe = omninet_port_probe,
.port_remove = omninet_port_remove,
.open = omninet_open,
@@ -104,6 +106,17 @@
__u8 od_outseq; /* Sequence number for bulk_out URBs */
};
+static int omninet_attach(struct usb_serial *serial)
+{
+ /* The second bulk-out endpoint is used for writing. */
+ if (serial->num_bulk_out < 2) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int omninet_port_probe(struct usb_serial_port *port)
{
struct omninet_data *od;
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 9894e34..42cc72e 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -268,6 +268,8 @@
#define TELIT_PRODUCT_CC864_SINGLE 0x1006
#define TELIT_PRODUCT_DE910_DUAL 0x1010
#define TELIT_PRODUCT_UE910_V2 0x1012
+#define TELIT_PRODUCT_LE922_USBCFG1 0x1040
+#define TELIT_PRODUCT_LE922_USBCFG2 0x1041
#define TELIT_PRODUCT_LE922_USBCFG0 0x1042
#define TELIT_PRODUCT_LE922_USBCFG3 0x1043
#define TELIT_PRODUCT_LE922_USBCFG5 0x1045
@@ -1210,6 +1212,10 @@
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG0),
.driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG1),
+ .driver_info = (kernel_ulong_t)&telit_le910_blacklist },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG2),
+ .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG3),
.driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG5, 0xff),
@@ -1989,6 +1995,7 @@
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d02, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x02, 0x01) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x00, 0x00) },
+ { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff) }, /* D-Link DWM-158 */
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */
@@ -2000,6 +2007,7 @@
{ USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD200, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_6802, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD300, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x421d, 0xff, 0xff, 0xff) }, /* HP lt2523 (Novatel E371) */
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, option_ids);
diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c
index a4b88bc..b8bf52b 100644
--- a/drivers/usb/serial/oti6858.c
+++ b/drivers/usb/serial/oti6858.c
@@ -134,6 +134,7 @@
static int oti6858_tiocmget(struct tty_struct *tty);
static int oti6858_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
+static int oti6858_attach(struct usb_serial *serial);
static int oti6858_port_probe(struct usb_serial_port *port);
static int oti6858_port_remove(struct usb_serial_port *port);
@@ -158,6 +159,7 @@
.write_bulk_callback = oti6858_write_bulk_callback,
.write_room = oti6858_write_room,
.chars_in_buffer = oti6858_chars_in_buffer,
+ .attach = oti6858_attach,
.port_probe = oti6858_port_probe,
.port_remove = oti6858_port_remove,
};
@@ -324,6 +326,20 @@
usb_serial_port_softint(port);
}
+static int oti6858_attach(struct usb_serial *serial)
+{
+ unsigned char num_ports = serial->num_ports;
+
+ if (serial->num_bulk_in < num_ports ||
+ serial->num_bulk_out < num_ports ||
+ serial->num_interrupt_in < num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int oti6858_port_probe(struct usb_serial_port *port)
{
struct oti6858_private *priv;
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index ae682e4..1db4b61 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -49,6 +49,7 @@
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) },
{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) },
+ { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID2) },
{ USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) },
{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) },
{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) },
@@ -220,9 +221,17 @@
static int pl2303_startup(struct usb_serial *serial)
{
struct pl2303_serial_private *spriv;
+ unsigned char num_ports = serial->num_ports;
enum pl2303_type type = TYPE_01;
unsigned char *buf;
+ if (serial->num_bulk_in < num_ports ||
+ serial->num_bulk_out < num_ports ||
+ serial->num_interrupt_in < num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
if (!spriv)
return -ENOMEM;
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index e3b7af8..09d9be8 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -27,6 +27,7 @@
#define ATEN_VENDOR_ID 0x0557
#define ATEN_VENDOR_ID2 0x0547
#define ATEN_PRODUCT_ID 0x2008
+#define ATEN_PRODUCT_ID2 0x2118
#define IODATA_VENDOR_ID 0x04bb
#define IODATA_PRODUCT_ID 0x0a03
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 1bc6089..696458d 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -124,6 +124,7 @@
{USB_DEVICE(0x1410, 0xa021)}, /* Novatel Gobi 3000 Composite */
{USB_DEVICE(0x413c, 0x8193)}, /* Dell Gobi 3000 QDL */
{USB_DEVICE(0x413c, 0x8194)}, /* Dell Gobi 3000 Composite */
+ {USB_DEVICE(0x413c, 0x81a6)}, /* Dell DW5570 QDL (MC8805) */
{USB_DEVICE(0x1199, 0x68a4)}, /* Sierra Wireless QDL */
{USB_DEVICE(0x1199, 0x68a5)}, /* Sierra Wireless Modem */
{USB_DEVICE(0x1199, 0x68a8)}, /* Sierra Wireless QDL */
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
index 85acb50..bd1a130 100644
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -408,16 +408,12 @@
{
struct usb_serial *serial;
struct qt2_port_private *port_priv;
- unsigned long flags;
int i;
serial = port->serial;
port_priv = usb_get_serial_port_data(port);
- spin_lock_irqsave(&port_priv->urb_lock, flags);
usb_kill_urb(port_priv->write_urb);
- port_priv->urb_in_use = false;
- spin_unlock_irqrestore(&port_priv->urb_lock, flags);
/* flush the port transmit buffer */
i = usb_control_msg(serial->dev,
diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c
index ef0dbf0..475e6c3 100644
--- a/drivers/usb/serial/spcp8x5.c
+++ b/drivers/usb/serial/spcp8x5.c
@@ -154,6 +154,19 @@
return 0;
}
+static int spcp8x5_attach(struct usb_serial *serial)
+{
+ unsigned char num_ports = serial->num_ports;
+
+ if (serial->num_bulk_in < num_ports ||
+ serial->num_bulk_out < num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int spcp8x5_port_probe(struct usb_serial_port *port)
{
const struct usb_device_id *id = usb_get_serial_data(port->serial);
@@ -477,6 +490,7 @@
.tiocmget = spcp8x5_tiocmget,
.tiocmset = spcp8x5_tiocmset,
.probe = spcp8x5_probe,
+ .attach = spcp8x5_attach,
.port_probe = spcp8x5_port_probe,
.port_remove = spcp8x5_port_remove,
};
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index a8b9bdb..bdbddbc 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -579,6 +579,13 @@
goto free_tdev;
}
+ if (serial->num_bulk_in < serial->num_ports ||
+ serial->num_bulk_out < serial->num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ status = -ENODEV;
+ goto free_tdev;
+ }
+
return 0;
free_tdev:
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index af3c7ee..16cc183 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -2109,6 +2109,13 @@
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_BROKEN_FUA ),
+/* Reported-by George Cherian <george.cherian@cavium.com> */
+UNUSUAL_DEV(0x152d, 0x9561, 0x0000, 0x9999,
+ "JMicron",
+ "JMS56x",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_REPORT_OPCODES),
+
/*
* Entrega Technologies U1-SC25 (later Xircom PortGear PGSCSI)
* and Mac USB Dock USB-SCSI */
diff --git a/drivers/usb/usbip/vudc_transfer.c b/drivers/usb/usbip/vudc_transfer.c
index aba6bd4..bc0296d 100644
--- a/drivers/usb/usbip/vudc_transfer.c
+++ b/drivers/usb/usbip/vudc_transfer.c
@@ -339,6 +339,8 @@
total = timer->frame_limit;
}
+ /* We have to clear ep0 flags separately as it's not on the list */
+ udc->ep[0].already_seen = 0;
list_for_each_entry(_ep, &udc->gadget.ep_list, ep_list) {
ep = to_vep(_ep);
ep->already_seen = 0;
diff --git a/drivers/usb/wusbcore/crypto.c b/drivers/usb/wusbcore/crypto.c
index 79451f7..062c205 100644
--- a/drivers/usb/wusbcore/crypto.c
+++ b/drivers/usb/wusbcore/crypto.c
@@ -216,7 +216,6 @@
struct scatterlist sg[4], sg_dst;
void *dst_buf;
size_t dst_size;
- const u8 bzero[16] = { 0 };
u8 iv[crypto_skcipher_ivsize(tfm_cbc)];
size_t zero_padding;
@@ -261,7 +260,7 @@
sg_set_buf(&sg[1], &scratch->b1, sizeof(scratch->b1));
sg_set_buf(&sg[2], b, blen);
/* 0 if well behaved :) */
- sg_set_buf(&sg[3], bzero, zero_padding);
+ sg_set_page(&sg[3], ZERO_PAGE(0), zero_padding, 0);
sg_init_one(&sg_dst, dst_buf, dst_size);
skcipher_request_set_tfm(req, tfm_cbc);
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index c6f2d89..64613fb 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -130,14 +130,14 @@
static void vhost_init_is_le(struct vhost_virtqueue *vq)
{
- if (vhost_has_feature(vq, VIRTIO_F_VERSION_1))
- vq->is_le = true;
+ vq->is_le = vhost_has_feature(vq, VIRTIO_F_VERSION_1)
+ || virtio_legacy_is_little_endian();
}
#endif /* CONFIG_VHOST_CROSS_ENDIAN_LEGACY */
static void vhost_reset_is_le(struct vhost_virtqueue *vq)
{
- vq->is_le = virtio_legacy_is_little_endian();
+ vhost_init_is_le(vq);
}
struct vhost_flush_struct {
@@ -1713,10 +1713,8 @@
int r;
bool is_le = vq->is_le;
- if (!vq->private_data) {
- vhost_reset_is_le(vq);
+ if (!vq->private_data)
return 0;
- }
vhost_init_is_le(vq);
diff --git a/drivers/video/fbdev/core/fbcmap.c b/drivers/video/fbdev/core/fbcmap.c
index f89245b..68a1135 100644
--- a/drivers/video/fbdev/core/fbcmap.c
+++ b/drivers/video/fbdev/core/fbcmap.c
@@ -163,17 +163,18 @@
int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to)
{
- int tooff = 0, fromoff = 0;
- int size;
+ unsigned int tooff = 0, fromoff = 0;
+ size_t size;
if (to->start > from->start)
fromoff = to->start - from->start;
else
tooff = from->start - to->start;
- size = to->len - tooff;
- if (size > (int) (from->len - fromoff))
- size = from->len - fromoff;
- if (size <= 0)
+ if (fromoff >= from->len || tooff >= to->len)
+ return -EINVAL;
+
+ size = min_t(size_t, to->len - tooff, from->len - fromoff);
+ if (size == 0)
return -EINVAL;
size *= sizeof(u16);
@@ -187,17 +188,18 @@
int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to)
{
- int tooff = 0, fromoff = 0;
- int size;
+ unsigned int tooff = 0, fromoff = 0;
+ size_t size;
if (to->start > from->start)
fromoff = to->start - from->start;
else
tooff = from->start - to->start;
- size = to->len - tooff;
- if (size > (int) (from->len - fromoff))
- size = from->len - fromoff;
- if (size <= 0)
+ if (fromoff >= from->len || tooff >= to->len)
+ return -EINVAL;
+
+ size = min_t(size_t, to->len - tooff, from->len - fromoff);
+ if (size == 0)
return -EINVAL;
size *= sizeof(u16);
diff --git a/drivers/video/fbdev/goldfishfb.c b/drivers/video/fbdev/goldfishfb.c
index 7f6c9e6..1e56b50 100644
--- a/drivers/video/fbdev/goldfishfb.c
+++ b/drivers/video/fbdev/goldfishfb.c
@@ -26,6 +26,7 @@
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
+#include <linux/acpi.h>
enum {
FB_GET_WIDTH = 0x00,
@@ -234,7 +235,7 @@
fb->fb.var.activate = FB_ACTIVATE_NOW;
fb->fb.var.height = readl(fb->reg_base + FB_GET_PHYS_HEIGHT);
fb->fb.var.width = readl(fb->reg_base + FB_GET_PHYS_WIDTH);
- fb->fb.var.pixclock = 10000;
+ fb->fb.var.pixclock = 0;
fb->fb.var.red.offset = 11;
fb->fb.var.red.length = 5;
@@ -304,12 +305,25 @@
return 0;
}
+static const struct of_device_id goldfish_fb_of_match[] = {
+ { .compatible = "google,goldfish-fb", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_fb_of_match);
+
+static const struct acpi_device_id goldfish_fb_acpi_match[] = {
+ { "GFSH0004", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, goldfish_fb_acpi_match);
static struct platform_driver goldfish_fb_driver = {
.probe = goldfish_fb_probe,
.remove = goldfish_fb_remove,
.driver = {
- .name = "goldfish_fb"
+ .name = "goldfish_fb",
+ .of_match_table = goldfish_fb_of_match,
+ .acpi_match_table = ACPI_PTR(goldfish_fb_acpi_match),
}
};
diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
index 48bfea9..5084098 100644
--- a/drivers/virtio/virtio_mmio.c
+++ b/drivers/virtio/virtio_mmio.c
@@ -59,6 +59,7 @@
#define pr_fmt(fmt) "virtio-mmio: " fmt
#include <linux/acpi.h>
+#include <linux/dma-mapping.h>
#include <linux/highmem.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -497,6 +498,7 @@
struct virtio_mmio_device *vm_dev;
struct resource *mem;
unsigned long magic;
+ int rc;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem)
@@ -545,9 +547,25 @@
}
vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID);
- if (vm_dev->version == 1)
+ if (vm_dev->version == 1) {
writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE);
+ rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
+ /*
+ * In the legacy case, ensure our coherently-allocated virtio
+ * ring will be at an address expressable as a 32-bit PFN.
+ */
+ if (!rc)
+ dma_set_coherent_mask(&pdev->dev,
+ DMA_BIT_MASK(32 + PAGE_SHIFT));
+ } else {
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ }
+ if (rc)
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (rc)
+ dev_warn(&pdev->dev, "Failed to enable 64-bit or 32-bit DMA. Trying to continue, but this might not work.\n");
+
platform_set_drvdata(pdev, vm_dev);
return register_virtio_device(&vm_dev->vdev);
diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c
index 6b5ee89..7cc5122 100644
--- a/drivers/vme/bridges/vme_ca91cx42.c
+++ b/drivers/vme/bridges/vme_ca91cx42.c
@@ -464,7 +464,7 @@
vme_bound = ioread32(bridge->base + CA91CX42_VSI_BD[i]);
pci_offset = ioread32(bridge->base + CA91CX42_VSI_TO[i]);
- *pci_base = (dma_addr_t)vme_base + pci_offset;
+ *pci_base = (dma_addr_t)*vme_base + pci_offset;
*size = (unsigned long long)((vme_bound - *vme_base) + granularity);
*enabled = 0;
diff --git a/drivers/watchdog/mei_wdt.c b/drivers/watchdog/mei_wdt.c
index 630bd18..2a9d5cd 100644
--- a/drivers/watchdog/mei_wdt.c
+++ b/drivers/watchdog/mei_wdt.c
@@ -389,6 +389,8 @@
wdt->wdd.max_timeout = MEI_WDT_MAX_TIMEOUT;
watchdog_set_drvdata(&wdt->wdd, wdt);
+ watchdog_stop_on_reboot(&wdt->wdd);
+
ret = watchdog_register_device(&wdt->wdd);
if (ret) {
dev_err(dev, "unable to register watchdog device = %d.\n", ret);
diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c
index 5796b5d..4f47b5e 100644
--- a/drivers/watchdog/qcom-wdt.c
+++ b/drivers/watchdog/qcom-wdt.c
@@ -209,7 +209,7 @@
wdt->wdd.parent = &pdev->dev;
wdt->layout = regs;
- if (readl(wdt->base + WDT_STS) & 1)
+ if (readl(wdt_addr(wdt, WDT_STS)) & 1)
wdt->wdd.bootstatus = WDIOF_CARDRESET;
/*
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index bb95212..2ef2b61 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -1007,7 +1007,7 @@
vma->vm_ops = &gntdev_vmops;
- vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
+ vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_MIXEDMAP;
if (use_ptemod)
vma->vm_flags |= VM_DONTCOPY;
diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c
index 87e6035..8e7a3d6 100644
--- a/drivers/xen/swiotlb-xen.c
+++ b/drivers/xen/swiotlb-xen.c
@@ -392,7 +392,7 @@
if (dma_capable(dev, dev_addr, size) &&
!range_straddles_page_boundary(phys, size) &&
!xen_arch_need_swiotlb(dev, phys, dev_addr) &&
- !swiotlb_force) {
+ (swiotlb_force != SWIOTLB_FORCE)) {
/* we are not interested in the dma_addr returned by
* xen_dma_map_page, only in the potential cache flushes executed
* by the function. */
@@ -549,7 +549,7 @@
phys_addr_t paddr = sg_phys(sg);
dma_addr_t dev_addr = xen_phys_to_bus(paddr);
- if (swiotlb_force ||
+ if (swiotlb_force == SWIOTLB_FORCE ||
xen_arch_need_swiotlb(hwdev, paddr, dev_addr) ||
!dma_capable(hwdev, dev_addr, sg->length) ||
range_straddles_page_boundary(paddr, sg->length)) {
diff --git a/fs/9p/acl.c b/fs/9p/acl.c
index b3c2cc7..082d227 100644
--- a/fs/9p/acl.c
+++ b/fs/9p/acl.c
@@ -277,6 +277,7 @@
case ACL_TYPE_ACCESS:
if (acl) {
struct iattr iattr;
+ struct posix_acl *old_acl = acl;
retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl);
if (retval)
@@ -287,6 +288,7 @@
* by the mode bits. So don't
* update ACL.
*/
+ posix_acl_release(old_acl);
value = NULL;
size = 0;
}
diff --git a/fs/attr.c b/fs/attr.c
index c902b3d..c4093c5 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -200,7 +200,7 @@
* the file open for write, as there can be no conflicting delegation in
* that case.
*/
-int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
+int notify_change2(struct vfsmount *mnt, struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
{
struct inode *inode = dentry->d_inode;
umode_t mode = inode->i_mode;
@@ -224,7 +224,7 @@
return -EPERM;
if (!inode_owner_or_capable(inode)) {
- error = inode_permission(inode, MAY_WRITE);
+ error = inode_permission2(mnt, inode, MAY_WRITE);
if (error)
return error;
}
@@ -307,7 +307,9 @@
if (error)
return error;
- if (inode->i_op->setattr)
+ if (mnt && inode->i_op->setattr2)
+ error = inode->i_op->setattr2(mnt, dentry, attr);
+ else if (inode->i_op->setattr)
error = inode->i_op->setattr(dentry, attr);
else
error = simple_setattr(dentry, attr);
@@ -320,4 +322,10 @@
return error;
}
+EXPORT_SYMBOL(notify_change2);
+
+int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
+{
+ return notify_change2(NULL, dentry, attr, delegated_inode);
+}
EXPORT_SYMBOL(notify_change);
diff --git a/fs/bad_inode.c b/fs/bad_inode.c
index 8712062..5f685c8 100644
--- a/fs/bad_inode.c
+++ b/fs/bad_inode.c
@@ -106,6 +106,50 @@
return -EIO;
}
+static const char *bad_inode_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
+{
+ return ERR_PTR(-EIO);
+}
+
+static struct posix_acl *bad_inode_get_acl(struct inode *inode, int type)
+{
+ return ERR_PTR(-EIO);
+}
+
+static int bad_inode_fiemap(struct inode *inode,
+ struct fiemap_extent_info *fieinfo, u64 start,
+ u64 len)
+{
+ return -EIO;
+}
+
+static int bad_inode_update_time(struct inode *inode, struct timespec *time,
+ int flags)
+{
+ return -EIO;
+}
+
+static int bad_inode_atomic_open(struct inode *inode, struct dentry *dentry,
+ struct file *file, unsigned int open_flag,
+ umode_t create_mode, int *opened)
+{
+ return -EIO;
+}
+
+static int bad_inode_tmpfile(struct inode *inode, struct dentry *dentry,
+ umode_t mode)
+{
+ return -EIO;
+}
+
+static int bad_inode_set_acl(struct inode *inode, struct posix_acl *acl,
+ int type)
+{
+ return -EIO;
+}
+
static const struct inode_operations bad_inode_ops =
{
.create = bad_inode_create,
@@ -118,14 +162,17 @@
.mknod = bad_inode_mknod,
.rename = bad_inode_rename2,
.readlink = bad_inode_readlink,
- /* follow_link must be no-op, otherwise unmounting this inode
- won't work */
- /* put_link returns void */
- /* truncate returns void */
.permission = bad_inode_permission,
.getattr = bad_inode_getattr,
.setattr = bad_inode_setattr,
.listxattr = bad_inode_listxattr,
+ .get_link = bad_inode_get_link,
+ .get_acl = bad_inode_get_acl,
+ .fiemap = bad_inode_fiemap,
+ .update_time = bad_inode_update_time,
+ .atomic_open = bad_inode_atomic_open,
+ .tmpfile = bad_inode_tmpfile,
+ .set_acl = bad_inode_set_acl,
};
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 05b5533..092a2eed 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -832,7 +832,7 @@
return true; /* already a holder */
else if (bdev->bd_holder != NULL)
return false; /* held by someone else */
- else if (bdev->bd_contains == bdev)
+ else if (whole == bdev)
return true; /* is a whole device which isn't held */
else if (whole->bd_holder == bd_may_claim)
@@ -1950,6 +1950,7 @@
spin_lock(&blockdev_superblock->s_inode_list_lock);
list_for_each_entry(inode, &blockdev_superblock->s_inodes, i_sb_list) {
struct address_space *mapping = inode->i_mapping;
+ struct block_device *bdev;
spin_lock(&inode->i_lock);
if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW) ||
@@ -1970,8 +1971,12 @@
*/
iput(old_inode);
old_inode = inode;
+ bdev = I_BDEV(inode);
- func(I_BDEV(inode), arg);
+ mutex_lock(&bdev->bd_mutex);
+ if (bdev->bd_openers)
+ func(bdev, arg);
+ mutex_unlock(&bdev->bd_mutex);
spin_lock(&blockdev_superblock->s_inode_list_lock);
}
diff --git a/fs/btrfs/async-thread.c b/fs/btrfs/async-thread.c
index e0f071f..ff0b0be 100644
--- a/fs/btrfs/async-thread.c
+++ b/fs/btrfs/async-thread.c
@@ -86,6 +86,20 @@
return work->wq->fs_info;
}
+bool btrfs_workqueue_normal_congested(struct btrfs_workqueue *wq)
+{
+ /*
+ * We could compare wq->normal->pending with num_online_cpus()
+ * to support "thresh == NO_THRESHOLD" case, but it requires
+ * moving up atomic_inc/dec in thresh_queue/exec_hook. Let's
+ * postpone it until someone needs the support of that case.
+ */
+ if (wq->normal->thresh == NO_THRESHOLD)
+ return false;
+
+ return atomic_read(&wq->normal->pending) > wq->normal->thresh * 2;
+}
+
BTRFS_WORK_HELPER(worker_helper);
BTRFS_WORK_HELPER(delalloc_helper);
BTRFS_WORK_HELPER(flush_delalloc_helper);
@@ -259,6 +273,8 @@
unsigned long flags;
while (1) {
+ void *wtag;
+
spin_lock_irqsave(lock, flags);
if (list_empty(list))
break;
@@ -285,11 +301,13 @@
spin_unlock_irqrestore(lock, flags);
/*
- * we don't want to call the ordered free functions
- * with the lock held though
+ * We don't want to call the ordered free functions with the
+ * lock held though. Save the work as tag for the trace event,
+ * because the callback could free the structure.
*/
+ wtag = work;
work->ordered_free(work);
- trace_btrfs_all_work_done(work);
+ trace_btrfs_all_work_done(wq->fs_info, wtag);
}
spin_unlock_irqrestore(lock, flags);
}
@@ -297,6 +315,7 @@
static void normal_work_helper(struct btrfs_work *work)
{
struct __btrfs_workqueue *wq;
+ void *wtag;
int need_order = 0;
/*
@@ -310,6 +329,8 @@
if (work->ordered_func)
need_order = 1;
wq = work->wq;
+ /* Safe for tracepoints in case work gets freed by the callback */
+ wtag = work;
trace_btrfs_work_sched(work);
thresh_exec_hook(wq);
@@ -319,7 +340,7 @@
run_ordered_work(wq);
}
if (!need_order)
- trace_btrfs_all_work_done(work);
+ trace_btrfs_all_work_done(wq->fs_info, wtag);
}
void btrfs_init_work(struct btrfs_work *work, btrfs_work_func_t uniq_func,
diff --git a/fs/btrfs/async-thread.h b/fs/btrfs/async-thread.h
index 8e52484..1f95973 100644
--- a/fs/btrfs/async-thread.h
+++ b/fs/btrfs/async-thread.h
@@ -84,4 +84,5 @@
void btrfs_set_work_high_priority(struct btrfs_work *work);
struct btrfs_fs_info *btrfs_work_owner(struct btrfs_work *work);
struct btrfs_fs_info *btrfs_workqueue_owner(struct __btrfs_workqueue *wq);
+bool btrfs_workqueue_normal_congested(struct btrfs_workqueue *wq);
#endif
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 0b8ce2b..86245b88 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2210,6 +2210,8 @@
cpu->target = le64_to_cpu(disk->target);
cpu->flags = le64_to_cpu(disk->flags);
cpu->limit = le64_to_cpu(disk->limit);
+ cpu->stripes_min = le32_to_cpu(disk->stripes_min);
+ cpu->stripes_max = le32_to_cpu(disk->stripes_max);
}
static inline void
@@ -2228,6 +2230,8 @@
disk->target = cpu_to_le64(cpu->target);
disk->flags = cpu_to_le64(cpu->flags);
disk->limit = cpu_to_le64(cpu->limit);
+ disk->stripes_min = cpu_to_le32(cpu->stripes_min);
+ disk->stripes_max = cpu_to_le32(cpu->stripes_max);
}
/* struct btrfs_super_block */
diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c
index 0fcf5f2..4d8f8a8 100644
--- a/fs/btrfs/delayed-inode.c
+++ b/fs/btrfs/delayed-inode.c
@@ -1353,7 +1353,8 @@
total_done++;
btrfs_release_prepared_delayed_node(delayed_node);
- if (async_work->nr == 0 || total_done < async_work->nr)
+ if ((async_work->nr == 0 && total_done < BTRFS_DELAYED_WRITEBACK) ||
+ total_done < async_work->nr)
goto again;
free_path:
@@ -1369,7 +1370,8 @@
{
struct btrfs_async_delayed_work *async_work;
- if (atomic_read(&delayed_root->items) < BTRFS_DELAYED_BACKGROUND)
+ if (atomic_read(&delayed_root->items) < BTRFS_DELAYED_BACKGROUND ||
+ btrfs_workqueue_normal_congested(fs_info->delayed_workers))
return 0;
async_work = kmalloc(sizeof(*async_work), GFP_NOFS);
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 3a57f99..1cd3257 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -559,7 +559,15 @@
u32 nritems = btrfs_header_nritems(leaf);
int slot;
- if (nritems == 0) {
+ /*
+ * Extent buffers from a relocation tree have a owner field that
+ * corresponds to the subvolume tree they are based on. So just from an
+ * extent buffer alone we can not find out what is the id of the
+ * corresponding subvolume tree, so we can not figure out if the extent
+ * buffer corresponds to the root of the relocation tree or not. So skip
+ * this check for relocation trees.
+ */
+ if (nritems == 0 && !btrfs_header_flag(leaf, BTRFS_HEADER_FLAG_RELOC)) {
struct btrfs_root *check_root;
key.objectid = btrfs_header_owner(leaf);
@@ -572,17 +580,24 @@
* open_ctree() some roots has not yet been set up.
*/
if (!IS_ERR_OR_NULL(check_root)) {
+ struct extent_buffer *eb;
+
+ eb = btrfs_root_node(check_root);
/* if leaf is the root, then it's fine */
- if (leaf->start !=
- btrfs_root_bytenr(&check_root->root_item)) {
+ if (leaf != eb) {
CORRUPT("non-root leaf's nritems is 0",
- leaf, root, 0);
+ leaf, check_root, 0);
+ free_extent_buffer(eb);
return -EIO;
}
+ free_extent_buffer(eb);
}
return 0;
}
+ if (nritems == 0)
+ return 0;
+
/* Check the 0 item */
if (btrfs_item_offset_nr(leaf, 0) + btrfs_item_size_nr(leaf, 0) !=
BTRFS_LEAF_DATA_SIZE(root)) {
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 4607af3..5909ae8 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -2537,11 +2537,11 @@
if (ref && ref->seq &&
btrfs_check_delayed_seq(fs_info, delayed_refs, ref->seq)) {
spin_unlock(&locked_ref->lock);
- btrfs_delayed_ref_unlock(locked_ref);
spin_lock(&delayed_refs->lock);
locked_ref->processing = 0;
delayed_refs->num_heads_ready++;
spin_unlock(&delayed_refs->lock);
+ btrfs_delayed_ref_unlock(locked_ref);
locked_ref = NULL;
cond_resched();
count++;
@@ -2587,7 +2587,10 @@
*/
if (must_insert_reserved)
locked_ref->must_insert_reserved = 1;
+ spin_lock(&delayed_refs->lock);
locked_ref->processing = 0;
+ delayed_refs->num_heads_ready++;
+ spin_unlock(&delayed_refs->lock);
btrfs_debug(fs_info,
"run_delayed_extent_op returned %d",
ret);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 8e3a5a2..be4da91 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3819,10 +3819,7 @@
break;
case S_IFDIR:
inode->i_fop = &btrfs_dir_file_operations;
- if (root == root->fs_info->tree_root)
- inode->i_op = &btrfs_dir_ro_inode_operations;
- else
- inode->i_op = &btrfs_dir_inode_operations;
+ inode->i_op = &btrfs_dir_inode_operations;
break;
case S_IFLNK:
inode->i_op = &btrfs_symlink_inode_operations;
@@ -5682,6 +5679,7 @@
inode->i_ino = BTRFS_EMPTY_SUBVOL_DIR_OBJECTID;
inode->i_op = &btrfs_dir_ro_inode_operations;
+ inode->i_opflags &= ~IOP_XATTR;
inode->i_fop = &simple_dir_operations;
inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO;
inode->i_mtime = current_time(inode);
@@ -10587,8 +10585,6 @@
static const struct inode_operations btrfs_dir_ro_inode_operations = {
.lookup = btrfs_lookup,
.permission = btrfs_permission,
- .get_acl = btrfs_get_acl,
- .set_acl = btrfs_set_acl,
.update_time = btrfs_update_time,
};
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 11f4fff..dfd9986 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -2335,10 +2335,6 @@
int err = -ENOMEM;
int ret = 0;
- mutex_lock(&fs_info->qgroup_rescan_lock);
- fs_info->qgroup_rescan_running = true;
- mutex_unlock(&fs_info->qgroup_rescan_lock);
-
path = btrfs_alloc_path();
if (!path)
goto out;
@@ -2449,6 +2445,7 @@
sizeof(fs_info->qgroup_rescan_progress));
fs_info->qgroup_rescan_progress.objectid = progress_objectid;
init_completion(&fs_info->qgroup_rescan_completion);
+ fs_info->qgroup_rescan_running = true;
spin_unlock(&fs_info->qgroup_lock);
mutex_unlock(&fs_info->qgroup_rescan_lock);
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index c4af0cd..2cf5e14 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -1395,14 +1395,23 @@
root_key.offset = objectid;
if (root->root_key.objectid == objectid) {
+ u64 commit_root_gen;
+
/* called by btrfs_init_reloc_root */
ret = btrfs_copy_root(trans, root, root->commit_root, &eb,
BTRFS_TREE_RELOC_OBJECTID);
BUG_ON(ret);
-
last_snap = btrfs_root_last_snapshot(&root->root_item);
- btrfs_set_root_last_snapshot(&root->root_item,
- trans->transid - 1);
+ /*
+ * Set the last_snapshot field to the generation of the commit
+ * root - like this ctree.c:btrfs_block_can_be_shared() behaves
+ * correctly (returns true) when the relocation root is created
+ * either inside the critical section of a transaction commit
+ * (through transaction.c:qgroup_account_snapshot()) and when
+ * it's created before the transaction commit is started.
+ */
+ commit_root_gen = btrfs_header_generation(root->commit_root);
+ btrfs_set_root_last_snapshot(&root->root_item, commit_root_gen);
} else {
/*
* called by btrfs_reloc_post_snapshot_hook.
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 3d33c4e..b890045 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1940,12 +1940,11 @@
next:
/* check the next slot in the tree to see if it is a valid item */
nritems = btrfs_header_nritems(path->nodes[0]);
+ path->slots[0]++;
if (path->slots[0] >= nritems) {
ret = btrfs_next_leaf(root, path);
if (ret)
goto out;
- } else {
- path->slots[0]++;
}
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
@@ -5205,6 +5204,7 @@
if (di_key.type == BTRFS_ROOT_ITEM_KEY)
continue;
+ btrfs_release_path(path);
di_inode = btrfs_iget(root->fs_info->sb, &di_key,
root, NULL);
if (IS_ERR(di_inode)) {
@@ -5214,13 +5214,12 @@
if (btrfs_inode_in_log(di_inode, trans->transid)) {
iput(di_inode);
- continue;
+ break;
}
ctx->log_new_dentries = false;
if (type == BTRFS_FT_DIR || type == BTRFS_FT_SYMLINK)
log_mode = LOG_INODE_ALL;
- btrfs_release_path(path);
ret = btrfs_log_inode(trans, root, di_inode,
log_mode, 0, LLONG_MAX, ctx);
if (!ret &&
diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
index 16e6ded..f3f2110 100644
--- a/fs/ceph/caps.c
+++ b/fs/ceph/caps.c
@@ -2507,9 +2507,20 @@
if (err < 0)
ret = err;
} else {
- ret = wait_event_interruptible(ci->i_cap_wq,
- try_get_cap_refs(ci, need, want, endoff,
- true, &_got, &err));
+ DEFINE_WAIT_FUNC(wait, woken_wake_function);
+ add_wait_queue(&ci->i_cap_wq, &wait);
+
+ while (!try_get_cap_refs(ci, need, want, endoff,
+ true, &_got, &err)) {
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
+ }
+
+ remove_wait_queue(&ci->i_cap_wq, &wait);
+
if (err == -EAGAIN)
continue;
if (err < 0)
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index a594c78..1afa111 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -1255,7 +1255,8 @@
struct ceph_mds_client *mdsc =
ceph_sb_to_client(dir->i_sb)->mdsc;
struct ceph_mds_request *req;
- int op, mask, err;
+ int op, err;
+ u32 mask;
if (flags & LOOKUP_RCU)
return -ECHILD;
@@ -1270,7 +1271,7 @@
mask = CEPH_STAT_CAP_INODE | CEPH_CAP_AUTH_SHARED;
if (ceph_security_xattr_wanted(dir))
mask |= CEPH_CAP_XATTR_SHARED;
- req->r_args.getattr.mask = mask;
+ req->r_args.getattr.mask = cpu_to_le32(mask);
err = ceph_mdsc_do_request(mdsc, NULL, req);
switch (err) {
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index ef4d046..12f2252 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -305,7 +305,8 @@
{
struct ceph_frag_tree_split *ls = (struct ceph_frag_tree_split*)l;
struct ceph_frag_tree_split *rs = (struct ceph_frag_tree_split*)r;
- return ceph_frag_compare(ls->frag, rs->frag);
+ return ceph_frag_compare(le32_to_cpu(ls->frag),
+ le32_to_cpu(rs->frag));
}
static bool is_frag_child(u32 f, struct ceph_inode_frag *frag)
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 815acd1..6a26c7b 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -288,12 +288,13 @@
struct ceph_mds_reply_info_parsed *info,
u64 features)
{
- if (info->head->op == CEPH_MDS_OP_GETFILELOCK)
+ u32 op = le32_to_cpu(info->head->op);
+
+ if (op == CEPH_MDS_OP_GETFILELOCK)
return parse_reply_info_filelock(p, end, info, features);
- else if (info->head->op == CEPH_MDS_OP_READDIR ||
- info->head->op == CEPH_MDS_OP_LSSNAP)
+ else if (op == CEPH_MDS_OP_READDIR || op == CEPH_MDS_OP_LSSNAP)
return parse_reply_info_dir(p, end, info, features);
- else if (info->head->op == CEPH_MDS_OP_CREATE)
+ else if (op == CEPH_MDS_OP_CREATE)
return parse_reply_info_create(p, end, info, features);
else
return -EIO;
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 1f17f6b..203287f 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -646,6 +646,8 @@
unsigned int max_read;
unsigned int max_write;
__u8 preauth_hash[512];
+ struct delayed_work reconnect; /* reconnect workqueue job */
+ struct mutex reconnect_mutex; /* prevent simultaneous reconnects */
#endif /* CONFIG_CIFS_SMB2 */
unsigned long echo_interval;
};
@@ -849,6 +851,7 @@
struct cifs_tcon {
struct list_head tcon_list;
int tc_count;
+ struct list_head rlist; /* reconnect list */
struct list_head openFileList;
spinlock_t open_file_lock; /* protects list above */
struct cifs_ses *ses; /* pointer to session associated with */
@@ -922,6 +925,7 @@
bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */
bool broken_sparse_sup; /* if server or share does not support sparse */
bool need_reconnect:1; /* connection reset, tid now invalid */
+ bool need_reopen_files:1; /* need to reopen tcon file handles */
bool use_resilient:1; /* use resilient instead of durable handles */
bool use_persistent:1; /* use persistent instead of durable handles */
#ifdef CONFIG_CIFS_SMB2
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index ced0e42..cd8025a 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -206,6 +206,9 @@
struct tcon_link *tlink,
struct cifs_pending_open *open);
extern void cifs_del_pending_open(struct cifs_pending_open *open);
+extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
+ int from_reconnect);
+extern void cifs_put_tcon(struct cifs_tcon *tcon);
#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL)
extern void cifs_dfs_release_automount_timer(void);
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 4547aed..893be07 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -52,6 +52,9 @@
#include "nterr.h"
#include "rfc1002pdu.h"
#include "fscache.h"
+#ifdef CONFIG_CIFS_SMB2
+#include "smb2proto.h"
+#endif
#define CIFS_PORT 445
#define RFC1001_PORT 139
@@ -2100,8 +2103,8 @@
return NULL;
}
-static void
-cifs_put_tcp_session(struct TCP_Server_Info *server)
+void
+cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
{
struct task_struct *task;
@@ -2118,6 +2121,19 @@
cancel_delayed_work_sync(&server->echo);
+#ifdef CONFIG_CIFS_SMB2
+ if (from_reconnect)
+ /*
+ * Avoid deadlock here: reconnect work calls
+ * cifs_put_tcp_session() at its end. Need to be sure
+ * that reconnect work does nothing with server pointer after
+ * that step.
+ */
+ cancel_delayed_work(&server->reconnect);
+ else
+ cancel_delayed_work_sync(&server->reconnect);
+#endif
+
spin_lock(&GlobalMid_Lock);
server->tcpStatus = CifsExiting;
spin_unlock(&GlobalMid_Lock);
@@ -2182,6 +2198,10 @@
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
+#ifdef CONFIG_CIFS_SMB2
+ INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
+ mutex_init(&tcp_ses->reconnect_mutex);
+#endif
memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr,
sizeof(tcp_ses->srcaddr));
memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr,
@@ -2340,7 +2360,7 @@
spin_unlock(&cifs_tcp_ses_lock);
sesInfoFree(ses);
- cifs_put_tcp_session(server);
+ cifs_put_tcp_session(server, 0);
}
#ifdef CONFIG_KEYS
@@ -2514,7 +2534,7 @@
mutex_unlock(&ses->session_mutex);
/* existing SMB ses has a server reference already */
- cifs_put_tcp_session(server);
+ cifs_put_tcp_session(server, 0);
free_xid(xid);
return ses;
}
@@ -2604,7 +2624,7 @@
return NULL;
}
-static void
+void
cifs_put_tcon(struct cifs_tcon *tcon)
{
unsigned int xid;
@@ -3792,7 +3812,7 @@
else if (ses)
cifs_put_smb_ses(ses);
else
- cifs_put_tcp_session(server);
+ cifs_put_tcp_session(server, 0);
bdi_destroy(&cifs_sb->bdi);
}
@@ -4103,7 +4123,7 @@
ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info);
if (IS_ERR(ses)) {
tcon = (struct cifs_tcon *)ses;
- cifs_put_tcp_session(master_tcon->ses->server);
+ cifs_put_tcp_session(master_tcon->ses->server, 0);
goto out;
}
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 7f5f617..18a1e1d 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -777,6 +777,11 @@
struct list_head *tmp1;
struct list_head tmp_list;
+ if (!tcon->use_persistent || !tcon->need_reopen_files)
+ return;
+
+ tcon->need_reopen_files = false;
+
cifs_dbg(FYI, "Reopen persistent handles");
INIT_LIST_HEAD(&tmp_list);
@@ -793,7 +798,8 @@
list_for_each_safe(tmp, tmp1, &tmp_list) {
open_file = list_entry(tmp, struct cifsFileInfo, rlist);
- cifs_reopen_file(open_file, false /* do not flush */);
+ if (cifs_reopen_file(open_file, false /* do not flush */))
+ tcon->need_reopen_files = true;
list_del_init(&open_file->rlist);
cifsFileInfo_put(open_file);
}
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index 9f51b81..0015287 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -189,7 +189,7 @@
xid = get_xid();
cifs_sb = CIFS_SB(inode->i_sb);
- cifs_dbg(VFS, "cifs ioctl 0x%x\n", command);
+ cifs_dbg(FYI, "cifs ioctl 0x%x\n", command);
switch (command) {
case FS_IOC_GETFLAGS:
if (pSMBFile == NULL)
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 8f6a2a5..a27fc87 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -285,6 +285,7 @@
rc = -ENOMEM;
goto error_exit;
}
+ spin_lock_init(&cifsFile->file_info_lock);
file->private_data = cifsFile;
cifsFile->tlink = cifs_get_tlink(tlink);
tcon = tlink_tcon(tlink);
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
index f9e766f..b2aff0c 100644
--- a/fs/cifs/smb2file.c
+++ b/fs/cifs/smb2file.c
@@ -260,7 +260,7 @@
* and check it for zero before using.
*/
max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf;
- if (!max_buf) {
+ if (max_buf < sizeof(struct smb2_lock_element)) {
free_xid(xid);
return -EINVAL;
}
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 5ca5ea46..8745722 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -250,16 +250,19 @@
}
cifs_mark_open_files_invalid(tcon);
+ if (tcon->use_persistent)
+ tcon->need_reopen_files = true;
rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage);
mutex_unlock(&tcon->ses->session_mutex);
- if (tcon->use_persistent)
- cifs_reopen_persistent_handles(tcon);
-
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
if (rc)
goto out;
+
+ if (smb2_command != SMB2_INTERNAL_CMD)
+ queue_delayed_work(cifsiod_wq, &server->reconnect, 0);
+
atomic_inc(&tconInfoReconnectCount);
out:
/*
@@ -280,7 +283,7 @@
case SMB2_CHANGE_NOTIFY:
case SMB2_QUERY_INFO:
case SMB2_SET_INFO:
- return -EAGAIN;
+ rc = -EAGAIN;
}
unload_nls(nls_codepage);
return rc;
@@ -1972,6 +1975,55 @@
add_credits(server, credits_received, CIFS_ECHO_OP);
}
+void smb2_reconnect_server(struct work_struct *work)
+{
+ struct TCP_Server_Info *server = container_of(work,
+ struct TCP_Server_Info, reconnect.work);
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon, *tcon2;
+ struct list_head tmp_list;
+ int tcon_exist = false;
+
+ /* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
+ mutex_lock(&server->reconnect_mutex);
+
+ INIT_LIST_HEAD(&tmp_list);
+ cifs_dbg(FYI, "Need negotiate, reconnecting tcons\n");
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+ list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
+ if (tcon->need_reconnect || tcon->need_reopen_files) {
+ tcon->tc_count++;
+ list_add_tail(&tcon->rlist, &tmp_list);
+ tcon_exist = true;
+ }
+ }
+ }
+ /*
+ * Get the reference to server struct to be sure that the last call of
+ * cifs_put_tcon() in the loop below won't release the server pointer.
+ */
+ if (tcon_exist)
+ server->srv_count++;
+
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) {
+ if (!smb2_reconnect(SMB2_INTERNAL_CMD, tcon))
+ cifs_reopen_persistent_handles(tcon);
+ list_del_init(&tcon->rlist);
+ cifs_put_tcon(tcon);
+ }
+
+ cifs_dbg(FYI, "Reconnecting tcons finished\n");
+ mutex_unlock(&server->reconnect_mutex);
+
+ /* now we can safely release srv struct */
+ if (tcon_exist)
+ cifs_put_tcp_session(server, 1);
+}
+
int
SMB2_echo(struct TCP_Server_Info *server)
{
@@ -1984,32 +2036,11 @@
cifs_dbg(FYI, "In echo request\n");
if (server->tcpStatus == CifsNeedNegotiate) {
- struct list_head *tmp, *tmp2;
- struct cifs_ses *ses;
- struct cifs_tcon *tcon;
-
- cifs_dbg(FYI, "Need negotiate, reconnecting tcons\n");
- spin_lock(&cifs_tcp_ses_lock);
- list_for_each(tmp, &server->smb_ses_list) {
- ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
- list_for_each(tmp2, &ses->tcon_list) {
- tcon = list_entry(tmp2, struct cifs_tcon,
- tcon_list);
- /* add check for persistent handle reconnect */
- if (tcon && tcon->need_reconnect) {
- spin_unlock(&cifs_tcp_ses_lock);
- rc = smb2_reconnect(SMB2_ECHO, tcon);
- spin_lock(&cifs_tcp_ses_lock);
- }
- }
- }
- spin_unlock(&cifs_tcp_ses_lock);
+ /* No need to send echo on newly established connections */
+ queue_delayed_work(cifsiod_wq, &server->reconnect, 0);
+ return rc;
}
- /* if no session, renegotiate failed above */
- if (server->tcpStatus == CifsNeedNegotiate)
- return -EIO;
-
rc = small_smb2_init(SMB2_ECHO, NULL, (void **)&req);
if (rc)
return rc;
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index fd3709e..dc0d141 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -80,6 +80,8 @@
#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE)
#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE)
+#define SMB2_INTERNAL_CMD cpu_to_le16(0xFFFF)
+
#define NUMBER_OF_SMB2_COMMANDS 0x0013
/* BB FIXME - analyze following length BB */
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index eb2cde2..f2d511a 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -96,6 +96,7 @@
extern int smb2_unlock_range(struct cifsFileInfo *cfile,
struct file_lock *flock, const unsigned int xid);
extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile);
+extern void smb2_reconnect_server(struct work_struct *work);
/*
* SMB2 Worker functions - most of protocol specific implementation details
diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c
index 699b786..c12bffe 100644
--- a/fs/cifs/smbencrypt.c
+++ b/fs/cifs/smbencrypt.c
@@ -23,7 +23,7 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <crypto/skcipher.h>
+#include <linux/crypto.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
@@ -69,46 +69,22 @@
static int
smbhash(unsigned char *out, const unsigned char *in, unsigned char *key)
{
- int rc;
unsigned char key2[8];
- struct crypto_skcipher *tfm_des;
- struct scatterlist sgin, sgout;
- struct skcipher_request *req;
+ struct crypto_cipher *tfm_des;
str_to_key(key, key2);
- tfm_des = crypto_alloc_skcipher("ecb(des)", 0, CRYPTO_ALG_ASYNC);
+ tfm_des = crypto_alloc_cipher("des", 0, 0);
if (IS_ERR(tfm_des)) {
- rc = PTR_ERR(tfm_des);
cifs_dbg(VFS, "could not allocate des crypto API\n");
- goto smbhash_err;
+ return PTR_ERR(tfm_des);
}
- req = skcipher_request_alloc(tfm_des, GFP_KERNEL);
- if (!req) {
- rc = -ENOMEM;
- cifs_dbg(VFS, "could not allocate des crypto API\n");
- goto smbhash_free_skcipher;
- }
+ crypto_cipher_setkey(tfm_des, key2, 8);
+ crypto_cipher_encrypt_one(tfm_des, out, in);
+ crypto_free_cipher(tfm_des);
- crypto_skcipher_setkey(tfm_des, key2, 8);
-
- sg_init_one(&sgin, in, 8);
- sg_init_one(&sgout, out, 8);
-
- skcipher_request_set_callback(req, 0, NULL, NULL);
- skcipher_request_set_crypt(req, &sgin, &sgout, 8, NULL);
-
- rc = crypto_skcipher_encrypt(req);
- if (rc)
- cifs_dbg(VFS, "could not encrypt crypt key rc: %d\n", rc);
-
- skcipher_request_free(req);
-
-smbhash_free_skcipher:
- crypto_free_skcipher(tfm_des);
-smbhash_err:
- return rc;
+ return 0;
}
static int
diff --git a/fs/coredump.c b/fs/coredump.c
index eb9c92c..8bdda8e 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -744,7 +744,7 @@
goto close_fail;
if (!(cprm.file->f_mode & FMODE_CAN_WRITE))
goto close_fail;
- if (do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file))
+ if (do_truncate2(cprm.file->f_path.mnt, cprm.file->f_path.dentry, 0, 0, cprm.file))
goto close_fail;
}
diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index 6865663..abc1884 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -171,6 +171,11 @@
BUG_ON(1);
}
+ /* No restrictions on file types which are never encrypted */
+ if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) &&
+ !S_ISLNK(child->i_mode))
+ return 1;
+
/* no restrictions if the parent directory is not encrypted */
if (!parent->i_sb->s_cop->is_encrypted(parent))
return 1;
diff --git a/fs/dax.c b/fs/dax.c
index 014defd..bf6218d 100644
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -1270,6 +1270,11 @@
struct blk_dax_ctl dax = { 0 };
ssize_t map_len;
+ if (fatal_signal_pending(current)) {
+ ret = -EINTR;
+ break;
+ }
+
dax.sector = iomap->blkno +
(((pos & PAGE_MASK) - iomap->offset) >> 9);
dax.size = (length + offset + PAGE_SIZE - 1) & PAGE_MASK;
diff --git a/fs/dcache.c b/fs/dcache.c
index 972741b..362396a 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1330,8 +1330,11 @@
}
spin_lock(&dentry->d_lock);
if (!d_unlinked(dentry)) {
- dentry->d_flags |= DCACHE_MOUNTED;
- ret = 0;
+ ret = -EBUSY;
+ if (!d_mountpoint(dentry)) {
+ dentry->d_flags |= DCACHE_MOUNTED;
+ ret = 0;
+ }
}
spin_unlock(&dentry->d_lock);
out:
diff --git a/fs/exec.c b/fs/exec.c
index 4e497b9..c8ca064 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -19,7 +19,7 @@
* current->executable is only used by the procfs. This allows a dispatch
* table to check for several different types of binary formats. We keep
* trying until we recognize the file or we run out of supported binary
- * formats.
+ * formats.
*/
#include <linux/slab.h>
@@ -1266,6 +1266,13 @@
flush_thread();
current->personality &= ~bprm->per_clear;
+ /*
+ * We have to apply CLOEXEC before we change whether the process is
+ * dumpable (in setup_new_exec) to avoid a race with a process in userspace
+ * trying to access the should-be-closed file descriptors of a process
+ * undergoing exec(2).
+ */
+ do_close_on_exec(current->files);
return 0;
out:
@@ -1275,8 +1282,22 @@
void would_dump(struct linux_binprm *bprm, struct file *file)
{
- if (inode_permission(file_inode(file), MAY_READ) < 0)
+ struct inode *inode = file_inode(file);
+ if (inode_permission2(file->f_path.mnt, inode, MAY_READ) < 0) {
+ struct user_namespace *old, *user_ns;
bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
+
+ /* Ensure mm->user_ns contains the executable */
+ user_ns = old = bprm->mm->user_ns;
+ while ((user_ns != &init_user_ns) &&
+ !privileged_wrt_inode_uidgid(user_ns, inode))
+ user_ns = user_ns->parent;
+
+ if (old != user_ns) {
+ bprm->mm->user_ns = get_user_ns(user_ns);
+ put_user_ns(old);
+ }
+ }
}
EXPORT_SYMBOL(would_dump);
@@ -1306,7 +1327,6 @@
!gid_eq(bprm->cred->gid, current_egid())) {
current->pdeath_signal = 0;
} else {
- would_dump(bprm, bprm->file);
if (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)
set_dumpable(current->mm, suid_dumpable);
}
@@ -1315,7 +1335,6 @@
group */
current->self_exec_id++;
flush_signal_handlers(current, 0);
- do_close_on_exec(current->files);
}
EXPORT_SYMBOL(setup_new_exec);
@@ -1406,7 +1425,7 @@
unsigned n_fs;
if (p->ptrace) {
- if (p->ptrace & PT_PTRACE_CAP)
+ if (ptracer_capable(p, current_user_ns()))
bprm->unsafe |= LSM_UNSAFE_PTRACE_CAP;
else
bprm->unsafe |= LSM_UNSAFE_PTRACE;
@@ -1741,6 +1760,8 @@
if (retval < 0)
goto out;
+ would_dump(bprm, bprm->file);
+
retval = exec_binprm(bprm);
if (retval < 0)
goto out;
diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h
index b1d52c1..f976111 100644
--- a/fs/ext4/ext4_jbd2.h
+++ b/fs/ext4/ext4_jbd2.h
@@ -414,17 +414,19 @@
return EXT4_INODE_WRITEBACK_DATA_MODE; /* writeback */
/* We do not support data journalling with delayed allocation */
if (!S_ISREG(inode->i_mode) ||
- test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)
+ test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA ||
+ (ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA) &&
+ !test_opt(inode->i_sb, DELALLOC))) {
+ /* We do not support data journalling for encrypted data */
+ if (S_ISREG(inode->i_mode) && ext4_encrypted_inode(inode))
+ return EXT4_INODE_ORDERED_DATA_MODE; /* ordered */
return EXT4_INODE_JOURNAL_DATA_MODE; /* journal data */
- if (ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA) &&
- !test_opt(inode->i_sb, DELALLOC))
- return EXT4_INODE_JOURNAL_DATA_MODE; /* journal data */
+ }
if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA)
return EXT4_INODE_ORDERED_DATA_MODE; /* ordered */
if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_WRITEBACK_DATA)
return EXT4_INODE_WRITEBACK_DATA_MODE; /* writeback */
- else
- BUG();
+ BUG();
}
static inline int ext4_should_journal_data(struct inode *inode)
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index bdef750..17a257c 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -337,8 +337,10 @@
len -= EXT4_MIN_INLINE_DATA_SIZE;
value = kzalloc(len, GFP_NOFS);
- if (!value)
+ if (!value) {
+ error = -ENOMEM;
goto out;
+ }
error = ext4_xattr_ibody_get(inode, i.name_index, i.name,
value, len);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 0241cab..1db9080 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3579,6 +3579,7 @@
size_t count = iov_iter_count(iter);
loff_t offset = iocb->ki_pos;
ssize_t ret;
+ int rw = iov_iter_rw(iter);
#ifdef CONFIG_EXT4_FS_ENCRYPTION
if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode))
@@ -3596,12 +3597,12 @@
return 0;
if (trace_android_fs_dataread_start_enabled() &&
- (iov_iter_rw(iter) == READ))
+ (rw == READ))
trace_android_fs_dataread_start(inode, offset, count,
current->pid,
current->comm);
if (trace_android_fs_datawrite_start_enabled() &&
- (iov_iter_rw(iter) == WRITE))
+ (rw == WRITE))
trace_android_fs_datawrite_start(inode, offset, count,
current->pid,
current->comm);
@@ -3614,10 +3615,10 @@
trace_ext4_direct_IO_exit(inode, offset, count, iov_iter_rw(iter), ret);
if (trace_android_fs_dataread_start_enabled() &&
- (iov_iter_rw(iter) == READ))
+ (rw == READ))
trace_android_fs_dataread_end(inode, offset, count);
if (trace_android_fs_datawrite_start_enabled() &&
- (iov_iter_rw(iter) == WRITE))
+ (rw == WRITE))
trace_android_fs_datawrite_end(inode, offset, count);
return ret;
@@ -4461,6 +4462,7 @@
struct inode *inode;
journal_t *journal = EXT4_SB(sb)->s_journal;
long ret;
+ loff_t size;
int block;
uid_t i_uid;
gid_t i_gid;
@@ -4561,6 +4563,11 @@
ei->i_file_acl |=
((__u64)le16_to_cpu(raw_inode->i_file_acl_high)) << 32;
inode->i_size = ext4_isize(raw_inode);
+ if ((size = i_size_read(inode)) < 0) {
+ EXT4_ERROR_INODE(inode, "bad i_size value: %lld", size);
+ ret = -EFSCORRUPTED;
+ goto bad_inode;
+ }
ei->i_disksize = inode->i_size;
#ifdef CONFIG_QUOTA
ei->i_reserved_quota = 0;
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index 6fbdf15..cec9280 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -751,6 +751,7 @@
if ((flags & BLKDEV_DISCARD_SECURE) && !blk_queue_secure_erase(q))
return -EOPNOTSUPP;
+
if (copy_from_user(&range, (struct fstrim_range __user *)arg,
sizeof(range)))
return -EFAULT;
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index 81e4b56..168ab9f 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -669,7 +669,7 @@
ext4_grpblk_t min;
ext4_grpblk_t max;
ext4_grpblk_t chunk;
- unsigned short border;
+ unsigned int border;
BUG_ON(len > EXT4_CLUSTERS_PER_GROUP(sb));
@@ -2287,7 +2287,7 @@
struct ext4_group_info *grinfo;
struct sg {
struct ext4_group_info info;
- ext4_grpblk_t counters[16];
+ ext4_grpblk_t counters[EXT4_MAX_BLOCK_LOG_SIZE + 2];
} sg;
group--;
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 52b0530..bbc316d 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -3193,10 +3193,15 @@
ext4_set_bit(s++, buf);
count++;
}
- for (j = ext4_bg_num_gdb(sb, grp); j > 0; j--) {
- ext4_set_bit(EXT4_B2C(sbi, s++), buf);
- count++;
+ j = ext4_bg_num_gdb(sb, grp);
+ if (s + j > EXT4_BLOCKS_PER_GROUP(sb)) {
+ ext4_error(sb, "Invalid number of block group "
+ "descriptor blocks: %d", j);
+ j = EXT4_BLOCKS_PER_GROUP(sb) - s;
}
+ count += j;
+ for (; j > 0; j--)
+ ext4_set_bit(EXT4_B2C(sbi, s++), buf);
}
if (!count)
return 0;
@@ -3301,7 +3306,7 @@
char *orig_data = kstrdup(data, GFP_KERNEL);
struct buffer_head *bh;
struct ext4_super_block *es = NULL;
- struct ext4_sb_info *sbi;
+ struct ext4_sb_info *sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
ext4_fsblk_t block;
ext4_fsblk_t sb_block = get_sb_block(&data);
ext4_fsblk_t logical_sb_block;
@@ -3320,16 +3325,14 @@
unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
ext4_group_t first_not_zeroed;
- sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
- if (!sbi)
- goto out_free_orig;
+ if ((data && !orig_data) || !sbi)
+ goto out_free_base;
sbi->s_blockgroup_lock =
kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);
- if (!sbi->s_blockgroup_lock) {
- kfree(sbi);
- goto out_free_orig;
- }
+ if (!sbi->s_blockgroup_lock)
+ goto out_free_base;
+
sb->s_fs_info = sbi;
sbi->s_sb = sb;
sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS;
@@ -3475,11 +3478,19 @@
*/
sbi->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;
- if (!parse_options((char *) sbi->s_es->s_mount_opts, sb,
- &journal_devnum, &journal_ioprio, 0)) {
- ext4_msg(sb, KERN_WARNING,
- "failed to parse options in superblock: %s",
- sbi->s_es->s_mount_opts);
+ if (sbi->s_es->s_mount_opts[0]) {
+ char *s_mount_opts = kstrndup(sbi->s_es->s_mount_opts,
+ sizeof(sbi->s_es->s_mount_opts),
+ GFP_KERNEL);
+ if (!s_mount_opts)
+ goto failed_mount;
+ if (!parse_options(s_mount_opts, sb, &journal_devnum,
+ &journal_ioprio, 0)) {
+ ext4_msg(sb, KERN_WARNING,
+ "failed to parse options in superblock: %s",
+ s_mount_opts);
+ }
+ kfree(s_mount_opts);
}
sbi->s_def_mount_opt = sbi->s_mount_opt;
if (!parse_options((char *) data, sb, &journal_devnum,
@@ -3505,6 +3516,11 @@
"both data=journal and dax");
goto failed_mount;
}
+ if (ext4_has_feature_encrypt(sb)) {
+ ext4_msg(sb, KERN_WARNING,
+ "encrypted files will use data=ordered "
+ "instead of data journaling mode");
+ }
if (test_opt(sb, DELALLOC))
clear_opt(sb, DELALLOC);
} else {
@@ -3660,12 +3676,16 @@
sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);
sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);
- if (EXT4_INODE_SIZE(sb) == 0 || EXT4_INODES_PER_GROUP(sb) == 0)
- goto cantfind_ext4;
sbi->s_inodes_per_block = blocksize / EXT4_INODE_SIZE(sb);
if (sbi->s_inodes_per_block == 0)
goto cantfind_ext4;
+ if (sbi->s_inodes_per_group < sbi->s_inodes_per_block ||
+ sbi->s_inodes_per_group > blocksize * 8) {
+ ext4_msg(sb, KERN_ERR, "invalid inodes per group: %lu\n",
+ sbi->s_blocks_per_group);
+ goto failed_mount;
+ }
sbi->s_itb_per_group = sbi->s_inodes_per_group /
sbi->s_inodes_per_block;
sbi->s_desc_per_block = blocksize / EXT4_DESC_SIZE(sb);
@@ -3748,13 +3768,6 @@
}
sbi->s_cluster_ratio = clustersize / blocksize;
- if (sbi->s_inodes_per_group > blocksize * 8) {
- ext4_msg(sb, KERN_ERR,
- "#inodes per group too big: %lu",
- sbi->s_inodes_per_group);
- goto failed_mount;
- }
-
/* Do we have standard group size of clustersize * 8 blocks ? */
if (sbi->s_blocks_per_group == clustersize << 3)
set_opt2(sb, STD_GROUP_SIZE);
@@ -3814,6 +3827,15 @@
(EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb)));
db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) /
EXT4_DESC_PER_BLOCK(sb);
+ if (ext4_has_feature_meta_bg(sb)) {
+ if (le32_to_cpu(es->s_first_meta_bg) >= db_count) {
+ ext4_msg(sb, KERN_WARNING,
+ "first meta block group too large: %u "
+ "(group descriptor block count %u)",
+ le32_to_cpu(es->s_first_meta_bg), db_count);
+ goto failed_mount;
+ }
+ }
sbi->s_group_desc = ext4_kvmalloc(db_count *
sizeof(struct buffer_head *),
GFP_KERNEL);
@@ -4160,7 +4182,9 @@
if (___ratelimit(&ext4_mount_msg_ratelimit, "EXT4-fs mount"))
ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
- "Opts: %s%s%s", descr, sbi->s_es->s_mount_opts,
+ "Opts: %.*s%s%s", descr,
+ (int) sizeof(sbi->s_es->s_mount_opts),
+ sbi->s_es->s_mount_opts,
*sbi->s_es->s_mount_opts ? "; " : "", orig_data);
if (es->s_error_count)
@@ -4239,8 +4263,8 @@
out_fail:
sb->s_fs_info = NULL;
kfree(sbi->s_blockgroup_lock);
+out_free_base:
kfree(sbi);
-out_free_orig:
kfree(orig_data);
return err ? err : ret;
}
@@ -4550,7 +4574,8 @@
&EXT4_SB(sb)->s_freeinodes_counter));
BUFFER_TRACE(sbh, "marking dirty");
ext4_superblock_csum_set(sb);
- lock_buffer(sbh);
+ if (sync)
+ lock_buffer(sbh);
if (buffer_write_io_error(sbh)) {
/*
* Oh, dear. A previous attempt to write the
@@ -4566,8 +4591,8 @@
set_buffer_uptodate(sbh);
}
mark_buffer_dirty(sbh);
- unlock_buffer(sbh);
if (sync) {
+ unlock_buffer(sbh);
error = __sync_dirty_buffer(sbh,
test_opt(sb, BARRIER) ? WRITE_FUA : WRITE_SYNC);
if (error)
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index 7e9b504..b4dbc2f 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -772,6 +772,11 @@
if (sanity_check_ckpt(sbi))
goto fail_no_cp;
+ if (cur_page == cp1)
+ sbi->cur_cp_pack = 1;
+ else
+ sbi->cur_cp_pack = 2;
+
if (cp_blks <= 1)
goto done;
@@ -1123,7 +1128,7 @@
le32_to_cpu(ckpt->checksum_offset)))
= cpu_to_le32(crc32);
- start_blk = __start_cp_addr(sbi);
+ start_blk = __start_cp_next_addr(sbi);
/* need to wait for end_io results */
wait_on_all_pages_writeback(sbi);
@@ -1187,6 +1192,7 @@
clear_prefree_segments(sbi, cpc);
clear_sbi_flag(sbi, SBI_IS_DIRTY);
clear_sbi_flag(sbi, SBI_NEED_CP);
+ __set_cp_next_pack(sbi);
/*
* redirty superblock if metadata like node page or inode cache is
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index b3cf04e..aee4a45 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -717,7 +717,7 @@
}
prealloc = 0;
- ofs_in_node = dn.ofs_in_node;
+ last_ofs_in_node = ofs_in_node = dn.ofs_in_node;
end_offset = ADDRS_PER_PAGE(dn.node_page, inode);
next_block:
@@ -1763,12 +1763,12 @@
trace_f2fs_direct_IO_enter(inode, offset, count, rw);
if (trace_android_fs_dataread_start_enabled() &&
- (iov_iter_rw(iter) == READ))
+ (rw == READ))
trace_android_fs_dataread_start(inode, offset,
count, current->pid,
current->comm);
if (trace_android_fs_datawrite_start_enabled() &&
- (iov_iter_rw(iter) == WRITE))
+ (rw == WRITE))
trace_android_fs_datawrite_start(inode, offset, count,
current->pid, current->comm);
@@ -1784,10 +1784,10 @@
}
if (trace_android_fs_dataread_start_enabled() &&
- (iov_iter_rw(iter) == READ))
+ (rw == READ))
trace_android_fs_dataread_end(inode, offset, count);
if (trace_android_fs_datawrite_start_enabled() &&
- (iov_iter_rw(iter) == WRITE))
+ (rw == WRITE))
trace_android_fs_datawrite_end(inode, offset, count);
trace_f2fs_direct_IO_exit(inode, offset, count, rw, err);
diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c
index fb245bd..687998e9 100644
--- a/fs/f2fs/debug.c
+++ b/fs/f2fs/debug.c
@@ -310,17 +310,17 @@
seq_printf(s, " - Inner Struct Count: tree: %d(%d), node: %d\n",
si->ext_tree, si->zombie_tree, si->ext_node);
seq_puts(s, "\nBalancing F2FS Async:\n");
- seq_printf(s, " - inmem: %4lld, wb_bios: %4d\n",
+ seq_printf(s, " - inmem: %4d, wb_bios: %4d\n",
si->inmem_pages, si->wb_bios);
- seq_printf(s, " - nodes: %4lld in %4d\n",
+ seq_printf(s, " - nodes: %4d in %4d\n",
si->ndirty_node, si->node_pages);
- seq_printf(s, " - dents: %4lld in dirs:%4d (%4d)\n",
+ seq_printf(s, " - dents: %4d in dirs:%4d (%4d)\n",
si->ndirty_dent, si->ndirty_dirs, si->ndirty_all);
- seq_printf(s, " - datas: %4lld in files:%4d\n",
+ seq_printf(s, " - datas: %4d in files:%4d\n",
si->ndirty_data, si->ndirty_files);
- seq_printf(s, " - meta: %4lld in %4d\n",
+ seq_printf(s, " - meta: %4d in %4d\n",
si->ndirty_meta, si->meta_pages);
- seq_printf(s, " - imeta: %4lld\n",
+ seq_printf(s, " - imeta: %4d\n",
si->ndirty_imeta);
seq_printf(s, " - NATs: %9d/%9d\n - SITs: %9d/%9d\n",
si->dirty_nats, si->nats, si->dirty_sits, si->sits);
@@ -373,6 +373,7 @@
}
static const struct file_operations stat_fops = {
+ .owner = THIS_MODULE,
.open = stat_open,
.read = seq_read,
.llseek = seq_lseek,
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 9e8de18..506af45 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -428,7 +428,7 @@
/* Use below internally in f2fs*/
unsigned long flags; /* use to pass per-file flags */
struct rw_semaphore i_sem; /* protect fi info */
- struct percpu_counter dirty_pages; /* # of dirty pages */
+ atomic_t dirty_pages; /* # of dirty pages */
f2fs_hash_t chash; /* hash value of given file name */
unsigned int clevel; /* maximum level of given file name */
nid_t i_xattr_nid; /* node id that contains xattrs */
@@ -764,6 +764,7 @@
/* for checkpoint */
struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */
+ int cur_cp_pack; /* remain current cp pack */
spinlock_t cp_lock; /* for flag in ckpt */
struct inode *meta_inode; /* cache meta blocks */
struct mutex cp_mutex; /* checkpoint procedure lock */
@@ -818,7 +819,7 @@
atomic_t nr_wb_bios; /* # of writeback bios */
/* # of pages, see count_type */
- struct percpu_counter nr_pages[NR_COUNT_TYPE];
+ atomic_t nr_pages[NR_COUNT_TYPE];
/* # of allocated blocks */
struct percpu_counter alloc_valid_block_count;
@@ -1232,7 +1233,7 @@
static inline void inc_page_count(struct f2fs_sb_info *sbi, int count_type)
{
- percpu_counter_inc(&sbi->nr_pages[count_type]);
+ atomic_inc(&sbi->nr_pages[count_type]);
if (count_type == F2FS_DIRTY_DATA || count_type == F2FS_INMEM_PAGES)
return;
@@ -1242,14 +1243,14 @@
static inline void inode_inc_dirty_pages(struct inode *inode)
{
- percpu_counter_inc(&F2FS_I(inode)->dirty_pages);
+ atomic_inc(&F2FS_I(inode)->dirty_pages);
inc_page_count(F2FS_I_SB(inode), S_ISDIR(inode->i_mode) ?
F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA);
}
static inline void dec_page_count(struct f2fs_sb_info *sbi, int count_type)
{
- percpu_counter_dec(&sbi->nr_pages[count_type]);
+ atomic_dec(&sbi->nr_pages[count_type]);
}
static inline void inode_dec_dirty_pages(struct inode *inode)
@@ -1258,19 +1259,19 @@
!S_ISLNK(inode->i_mode))
return;
- percpu_counter_dec(&F2FS_I(inode)->dirty_pages);
+ atomic_dec(&F2FS_I(inode)->dirty_pages);
dec_page_count(F2FS_I_SB(inode), S_ISDIR(inode->i_mode) ?
F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA);
}
static inline s64 get_pages(struct f2fs_sb_info *sbi, int count_type)
{
- return percpu_counter_sum_positive(&sbi->nr_pages[count_type]);
+ return atomic_read(&sbi->nr_pages[count_type]);
}
-static inline s64 get_dirty_pages(struct inode *inode)
+static inline int get_dirty_pages(struct inode *inode)
{
- return percpu_counter_sum_positive(&F2FS_I(inode)->dirty_pages);
+ return atomic_read(&F2FS_I(inode)->dirty_pages);
}
static inline int get_blocktype_secs(struct f2fs_sb_info *sbi, int block_type)
@@ -1329,22 +1330,27 @@
static inline block_t __start_cp_addr(struct f2fs_sb_info *sbi)
{
- block_t start_addr;
- struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
- unsigned long long ckpt_version = cur_cp_version(ckpt);
+ block_t start_addr = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_blkaddr);
- start_addr = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_blkaddr);
-
- /*
- * odd numbered checkpoint should at cp segment 0
- * and even segment must be at cp segment 1
- */
- if (!(ckpt_version & 1))
+ if (sbi->cur_cp_pack == 2)
start_addr += sbi->blocks_per_seg;
-
return start_addr;
}
+static inline block_t __start_cp_next_addr(struct f2fs_sb_info *sbi)
+{
+ block_t start_addr = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_blkaddr);
+
+ if (sbi->cur_cp_pack == 1)
+ start_addr += sbi->blocks_per_seg;
+ return start_addr;
+}
+
+static inline void __set_cp_next_pack(struct f2fs_sb_info *sbi)
+{
+ sbi->cur_cp_pack = (sbi->cur_cp_pack == 1) ? 2 : 1;
+}
+
static inline block_t __start_sum_addr(struct f2fs_sb_info *sbi)
{
return le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_start_sum);
@@ -2181,8 +2187,8 @@
unsigned long long hit_largest, hit_cached, hit_rbtree;
unsigned long long hit_total, total_ext;
int ext_tree, zombie_tree, ext_node;
- s64 ndirty_node, ndirty_dent, ndirty_meta, ndirty_data, ndirty_imeta;
- s64 inmem_pages;
+ int ndirty_node, ndirty_dent, ndirty_meta, ndirty_data, ndirty_imeta;
+ int inmem_pages;
unsigned int ndirty_dirs, ndirty_files, ndirty_all;
int nats, dirty_nats, sits, dirty_sits, fnids;
int total_count, utilization;
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index c786507..801111e 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -967,7 +967,7 @@
new_size = (dst + i) << PAGE_SHIFT;
if (dst_inode->i_size < new_size)
f2fs_i_size_write(dst_inode, new_size);
- } while ((do_replace[i] || blkaddr[i] == NULL_ADDR) && --ilen);
+ } while (--ilen && (do_replace[i] || blkaddr[i] == NULL_ADDR));
f2fs_put_dnode(&dn);
} else {
@@ -1526,7 +1526,7 @@
goto out;
f2fs_msg(F2FS_I_SB(inode)->sb, KERN_WARNING,
- "Unexpected flush for atomic writes: ino=%lu, npages=%lld",
+ "Unexpected flush for atomic writes: ino=%lu, npages=%u",
inode->i_ino, get_dirty_pages(inode));
ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
if (ret)
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 6132b4c..013c6a5 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -558,13 +558,9 @@
init_once((void *) fi);
- if (percpu_counter_init(&fi->dirty_pages, 0, GFP_NOFS)) {
- kmem_cache_free(f2fs_inode_cachep, fi);
- return NULL;
- }
-
/* Initialize f2fs-specific inode info */
fi->vfs_inode.i_version = 1;
+ atomic_set(&fi->dirty_pages, 0);
fi->i_current_depth = 1;
fi->i_advise = 0;
init_rwsem(&fi->i_sem);
@@ -687,16 +683,11 @@
static void f2fs_destroy_inode(struct inode *inode)
{
- percpu_counter_destroy(&F2FS_I(inode)->dirty_pages);
call_rcu(&inode->i_rcu, f2fs_i_callback);
}
static void destroy_percpu_info(struct f2fs_sb_info *sbi)
{
- int i;
-
- for (i = 0; i < NR_COUNT_TYPE; i++)
- percpu_counter_destroy(&sbi->nr_pages[i]);
percpu_counter_destroy(&sbi->alloc_valid_block_count);
percpu_counter_destroy(&sbi->total_valid_inode_count);
}
@@ -1447,6 +1438,7 @@
static void init_sb_info(struct f2fs_sb_info *sbi)
{
struct f2fs_super_block *raw_super = sbi->raw_super;
+ int i;
sbi->log_sectors_per_block =
le32_to_cpu(raw_super->log_sectors_per_block);
@@ -1471,6 +1463,9 @@
sbi->interval_time[REQ_TIME] = DEF_IDLE_INTERVAL;
clear_sbi_flag(sbi, SBI_NEED_FSCK);
+ for (i = 0; i < NR_COUNT_TYPE; i++)
+ atomic_set(&sbi->nr_pages[i], 0);
+
INIT_LIST_HEAD(&sbi->s_list);
mutex_init(&sbi->umount_mutex);
mutex_init(&sbi->wio_mutex[NODE]);
@@ -1486,13 +1481,7 @@
static int init_percpu_info(struct f2fs_sb_info *sbi)
{
- int i, err;
-
- for (i = 0; i < NR_COUNT_TYPE; i++) {
- err = percpu_counter_init(&sbi->nr_pages[i], 0, GFP_KERNEL);
- if (err)
- return err;
- }
+ int err;
err = percpu_counter_init(&sbi->alloc_valid_block_count, 0, GFP_KERNEL);
if (err)
diff --git a/fs/fs_struct.c b/fs/fs_struct.c
index 7dca743..940c683 100644
--- a/fs/fs_struct.c
+++ b/fs/fs_struct.c
@@ -44,6 +44,7 @@
if (old_pwd.dentry)
path_put(&old_pwd);
}
+EXPORT_SYMBOL(set_fs_pwd);
static inline int replace_path(struct path *p, const struct path *old, const struct path *new)
{
@@ -89,6 +90,7 @@
path_put(&fs->pwd);
kmem_cache_free(fs_cachep, fs);
}
+EXPORT_SYMBOL(free_fs_struct);
void exit_fs(struct task_struct *tsk)
{
@@ -127,6 +129,7 @@
}
return fs;
}
+EXPORT_SYMBOL_GPL(copy_fs_struct);
int unshare_fs_struct(void)
{
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index e920bf0..3fd1e21 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -2033,7 +2033,6 @@
struct fuse_req *req;
req = list_entry(head->next, struct fuse_req, list);
req->out.h.error = -ECONNABORTED;
- clear_bit(FR_PENDING, &req->flags);
clear_bit(FR_SENT, &req->flags);
list_del_init(&req->list);
request_end(fc, req);
@@ -2111,6 +2110,8 @@
spin_lock(&fiq->waitq.lock);
fiq->connected = 0;
list_splice_init(&fiq->pending, &to_end2);
+ list_for_each_entry(req, &to_end2, list)
+ clear_bit(FR_PENDING, &req->flags);
while (forget_pending(fiq))
kfree(dequeue_forget(fiq, 1, NULL));
wake_up_all_locked(&fiq->waitq);
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 3545ec6..fc8ba62 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -68,7 +68,7 @@
if (sec || nsec) {
struct timespec64 ts = {
sec,
- max_t(u32, nsec, NSEC_PER_SEC - 1)
+ min_t(u32, nsec, NSEC_PER_SEC - 1)
};
return get_jiffies_64() + timespec64_to_jiffies(&ts);
@@ -334,6 +334,7 @@
const struct dentry_operations fuse_root_dentry_operations = {
.d_init = fuse_dentry_init,
.d_release = fuse_dentry_release,
+ .d_canonical_path = fuse_dentry_canonical_path,
};
int fuse_valid_type(int m)
diff --git a/fs/inode.c b/fs/inode.c
index 88110fd..0aaebd1 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -1780,7 +1780,7 @@
return mask;
}
-static int __remove_privs(struct dentry *dentry, int kill)
+static int __remove_privs(struct vfsmount *mnt, struct dentry *dentry, int kill)
{
struct iattr newattrs;
@@ -1789,7 +1789,7 @@
* Note we call this on write, so notify_change will not
* encounter any conflicting delegations:
*/
- return notify_change(dentry, &newattrs, NULL);
+ return notify_change2(mnt, dentry, &newattrs, NULL);
}
/*
@@ -1811,7 +1811,7 @@
if (kill < 0)
return kill;
if (kill)
- error = __remove_privs(dentry, kill);
+ error = __remove_privs(file->f_path.mnt, dentry, kill);
if (!error)
inode_has_no_xattr(inode);
diff --git a/fs/internal.h b/fs/internal.h
index f4da334..15fe2aa 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -88,9 +88,11 @@
* super.c
*/
extern int do_remount_sb(struct super_block *, int, void *, int);
+extern int do_remount_sb2(struct vfsmount *, struct super_block *, int,
+ void *, int);
extern bool trylock_super(struct super_block *sb);
extern struct dentry *mount_fs(struct file_system_type *,
- int, const char *, void *);
+ int, const char *, struct vfsmount *, void *);
extern struct super_block *user_get_super(dev_t);
/*
diff --git a/fs/iomap.c b/fs/iomap.c
index a8ee8c3..814ae8f 100644
--- a/fs/iomap.c
+++ b/fs/iomap.c
@@ -113,6 +113,9 @@
BUG_ON(pos + len > iomap->offset + iomap->length);
+ if (fatal_signal_pending(current))
+ return -EINTR;
+
page = grab_cache_page_write_begin(inode->i_mapping, index, flags);
if (!page)
return -ENOMEM;
diff --git a/fs/namei.c b/fs/namei.c
index 5b4eed2..8fa2ddc 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -375,9 +375,11 @@
* flag in inode->i_opflags, that says "this has not special
* permission function, use the fast case".
*/
-static inline int do_inode_permission(struct inode *inode, int mask)
+static inline int do_inode_permission(struct vfsmount *mnt, struct inode *inode, int mask)
{
if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
+ if (likely(mnt && inode->i_op->permission2))
+ return inode->i_op->permission2(mnt, inode, mask);
if (likely(inode->i_op->permission))
return inode->i_op->permission(inode, mask);
@@ -401,7 +403,7 @@
* This does not check for a read-only file system. You probably want
* inode_permission().
*/
-int __inode_permission(struct inode *inode, int mask)
+int __inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask)
{
int retval;
@@ -421,7 +423,7 @@
return -EACCES;
}
- retval = do_inode_permission(inode, mask);
+ retval = do_inode_permission(mnt, inode, mask);
if (retval)
return retval;
@@ -429,7 +431,14 @@
if (retval)
return retval;
- return security_inode_permission(inode, mask);
+ retval = security_inode_permission(inode, mask);
+ return retval;
+}
+EXPORT_SYMBOL(__inode_permission2);
+
+int __inode_permission(struct inode *inode, int mask)
+{
+ return __inode_permission2(NULL, inode, mask);
}
EXPORT_SYMBOL(__inode_permission);
@@ -465,14 +474,20 @@
*
* When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
*/
-int inode_permission(struct inode *inode, int mask)
+int inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask)
{
int retval;
retval = sb_permission(inode->i_sb, inode, mask);
if (retval)
return retval;
- return __inode_permission(inode, mask);
+ return __inode_permission2(mnt, inode, mask);
+}
+EXPORT_SYMBOL(inode_permission2);
+
+int inode_permission(struct inode *inode, int mask)
+{
+ return inode_permission2(NULL, inode, mask);
}
EXPORT_SYMBOL(inode_permission);
@@ -1669,13 +1684,13 @@
static inline int may_lookup(struct nameidata *nd)
{
if (nd->flags & LOOKUP_RCU) {
- int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
+ int err = inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
if (err != -ECHILD)
return err;
if (unlazy_walk(nd, NULL, 0))
return -ECHILD;
}
- return inode_permission(nd->inode, MAY_EXEC);
+ return inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC);
}
static inline int handle_dots(struct nameidata *nd, int type)
@@ -2146,11 +2161,12 @@
nd->depth = 0;
if (flags & LOOKUP_ROOT) {
struct dentry *root = nd->root.dentry;
+ struct vfsmount *mnt = nd->root.mnt;
struct inode *inode = root->d_inode;
if (*s) {
if (!d_can_lookup(root))
return ERR_PTR(-ENOTDIR);
- retval = inode_permission(inode, MAY_EXEC);
+ retval = inode_permission2(mnt, inode, MAY_EXEC);
if (retval)
return ERR_PTR(retval);
}
@@ -2415,6 +2431,7 @@
/**
* lookup_one_len - filesystem helper to lookup single pathname component
* @name: pathname component to lookup
+ * @mnt: mount we are looking up on
* @base: base directory to lookup from
* @len: maximum length @len should be interpreted to
*
@@ -2423,7 +2440,7 @@
*
* The caller must hold base->i_mutex.
*/
-struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
+struct dentry *lookup_one_len2(const char *name, struct vfsmount *mnt, struct dentry *base, int len)
{
struct qstr this;
unsigned int c;
@@ -2457,12 +2474,18 @@
return ERR_PTR(err);
}
- err = inode_permission(base->d_inode, MAY_EXEC);
+ err = inode_permission2(mnt, base->d_inode, MAY_EXEC);
if (err)
return ERR_PTR(err);
return __lookup_hash(&this, base, 0);
}
+EXPORT_SYMBOL(lookup_one_len2);
+
+struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
+{
+ return lookup_one_len2(name, NULL, base, len);
+}
EXPORT_SYMBOL(lookup_one_len);
/**
@@ -2765,7 +2788,7 @@
* 11. We don't allow removal of NFS sillyrenamed files; it's handled by
* nfs_async_unlink().
*/
-static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
+static int may_delete(struct vfsmount *mnt, struct inode *dir, struct dentry *victim, bool isdir)
{
struct inode *inode = d_backing_inode(victim);
int error;
@@ -2777,7 +2800,7 @@
BUG_ON(victim->d_parent->d_inode != dir);
audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
- error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ error = inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC);
if (error)
return error;
if (IS_APPEND(dir))
@@ -2809,7 +2832,7 @@
* 4. We should have write and exec permissions on dir
* 5. We can't do it if dir is immutable (done in permission())
*/
-static inline int may_create(struct inode *dir, struct dentry *child)
+static inline int may_create(struct vfsmount *mnt, struct inode *dir, struct dentry *child)
{
struct user_namespace *s_user_ns;
audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
@@ -2821,7 +2844,7 @@
if (!kuid_has_mapping(s_user_ns, current_fsuid()) ||
!kgid_has_mapping(s_user_ns, current_fsgid()))
return -EOVERFLOW;
- return inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ return inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC);
}
/*
@@ -2868,10 +2891,10 @@
}
EXPORT_SYMBOL(unlock_rename);
-int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
- bool want_excl)
+int vfs_create2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry,
+ umode_t mode, bool want_excl)
{
- int error = may_create(dir, dentry);
+ int error = may_create(mnt, dir, dentry);
if (error)
return error;
@@ -2887,6 +2910,13 @@
fsnotify_create(dir, dentry);
return error;
}
+EXPORT_SYMBOL(vfs_create2);
+
+int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
+ bool want_excl)
+{
+ return vfs_create2(NULL, dir, dentry, mode, want_excl);
+}
EXPORT_SYMBOL(vfs_create);
bool may_open_dev(const struct path *path)
@@ -2898,6 +2928,7 @@
static int may_open(struct path *path, int acc_mode, int flag)
{
struct dentry *dentry = path->dentry;
+ struct vfsmount *mnt = path->mnt;
struct inode *inode = dentry->d_inode;
int error;
@@ -2922,7 +2953,7 @@
break;
}
- error = inode_permission(inode, MAY_OPEN | acc_mode);
+ error = inode_permission2(mnt, inode, MAY_OPEN | acc_mode);
if (error)
return error;
@@ -2957,7 +2988,7 @@
if (!error)
error = security_path_truncate(path);
if (!error) {
- error = do_truncate(path->dentry, 0,
+ error = do_truncate2(path->mnt, path->dentry, 0,
ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
filp);
}
@@ -2978,7 +3009,7 @@
if (error)
return error;
- error = inode_permission(dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
+ error = inode_permission2(dir->mnt, dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
if (error)
return error;
@@ -3409,7 +3440,7 @@
goto out;
dir = path.dentry->d_inode;
/* we want directory to be writable */
- error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ error = inode_permission2(nd->path.mnt, dir, MAY_WRITE | MAY_EXEC);
if (error)
goto out2;
if (!dir->i_op->tmpfile) {
@@ -3662,9 +3693,9 @@
}
EXPORT_SYMBOL(user_path_create);
-int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
+int vfs_mknod2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
{
- int error = may_create(dir, dentry);
+ int error = may_create(mnt, dir, dentry);
if (error)
return error;
@@ -3688,6 +3719,12 @@
fsnotify_create(dir, dentry);
return error;
}
+EXPORT_SYMBOL(vfs_mknod2);
+
+int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
+{
+ return vfs_mknod2(NULL, dir, dentry, mode, dev);
+}
EXPORT_SYMBOL(vfs_mknod);
static int may_mknod(umode_t mode)
@@ -3730,12 +3767,12 @@
goto out;
switch (mode & S_IFMT) {
case 0: case S_IFREG:
- error = vfs_create(path.dentry->d_inode,dentry,mode,true);
+ error = vfs_create2(path.mnt, path.dentry->d_inode,dentry,mode,true);
if (!error)
ima_post_path_mknod(dentry);
break;
case S_IFCHR: case S_IFBLK:
- error = vfs_mknod(path.dentry->d_inode,dentry,mode,
+ error = vfs_mknod2(path.mnt, path.dentry->d_inode,dentry,mode,
new_decode_dev(dev));
break;
case S_IFIFO: case S_IFSOCK:
@@ -3756,9 +3793,9 @@
return sys_mknodat(AT_FDCWD, filename, mode, dev);
}
-int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+int vfs_mkdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode)
{
- int error = may_create(dir, dentry);
+ int error = may_create(mnt, dir, dentry);
unsigned max_links = dir->i_sb->s_max_links;
if (error)
@@ -3780,6 +3817,12 @@
fsnotify_mkdir(dir, dentry);
return error;
}
+EXPORT_SYMBOL(vfs_mkdir2);
+
+int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+ return vfs_mkdir2(NULL, dir, dentry, mode);
+}
EXPORT_SYMBOL(vfs_mkdir);
SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
@@ -3798,7 +3841,7 @@
mode &= ~current_umask();
error = security_path_mkdir(&path, dentry, mode);
if (!error)
- error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
+ error = vfs_mkdir2(path.mnt, path.dentry->d_inode, dentry, mode);
done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
@@ -3812,9 +3855,9 @@
return sys_mkdirat(AT_FDCWD, pathname, mode);
}
-int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+int vfs_rmdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry)
{
- int error = may_delete(dir, dentry, 1);
+ int error = may_delete(mnt, dir, dentry, 1);
if (error)
return error;
@@ -3849,6 +3892,12 @@
d_delete(dentry);
return error;
}
+EXPORT_SYMBOL(vfs_rmdir2);
+
+int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ return vfs_rmdir2(NULL, dir, dentry);
+}
EXPORT_SYMBOL(vfs_rmdir);
static long do_rmdir(int dfd, const char __user *pathname)
@@ -3894,7 +3943,7 @@
error = security_path_rmdir(&path, dentry);
if (error)
goto exit3;
- error = vfs_rmdir(path.dentry->d_inode, dentry);
+ error = vfs_rmdir2(path.mnt, path.dentry->d_inode, dentry);
exit3:
dput(dentry);
exit2:
@@ -3933,10 +3982,10 @@
* be appropriate for callers that expect the underlying filesystem not
* to be NFS exported.
*/
-int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
+int vfs_unlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
{
struct inode *target = dentry->d_inode;
- int error = may_delete(dir, dentry, 0);
+ int error = may_delete(mnt, dir, dentry, 0);
if (error)
return error;
@@ -3971,6 +4020,12 @@
return error;
}
+EXPORT_SYMBOL(vfs_unlink2);
+
+int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
+{
+ return vfs_unlink2(NULL, dir, dentry, delegated_inode);
+}
EXPORT_SYMBOL(vfs_unlink);
/*
@@ -4018,7 +4073,7 @@
error = security_path_unlink(&path, dentry);
if (error)
goto exit2;
- error = vfs_unlink(path.dentry->d_inode, dentry, &delegated_inode);
+ error = vfs_unlink2(path.mnt, path.dentry->d_inode, dentry, &delegated_inode);
exit2:
dput(dentry);
}
@@ -4068,9 +4123,9 @@
return do_unlinkat(AT_FDCWD, pathname);
}
-int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
+int vfs_symlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, const char *oldname)
{
- int error = may_create(dir, dentry);
+ int error = may_create(mnt, dir, dentry);
if (error)
return error;
@@ -4087,6 +4142,12 @@
fsnotify_create(dir, dentry);
return error;
}
+EXPORT_SYMBOL(vfs_symlink2);
+
+int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
+{
+ return vfs_symlink2(NULL, dir, dentry, oldname);
+}
EXPORT_SYMBOL(vfs_symlink);
SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
@@ -4109,7 +4170,7 @@
error = security_path_symlink(&path, dentry, from->name);
if (!error)
- error = vfs_symlink(path.dentry->d_inode, dentry, from->name);
+ error = vfs_symlink2(path.mnt, path.dentry->d_inode, dentry, from->name);
done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
@@ -4144,7 +4205,7 @@
* be appropriate for callers that expect the underlying filesystem not
* to be NFS exported.
*/
-int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
+int vfs_link2(struct vfsmount *mnt, struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
{
struct inode *inode = old_dentry->d_inode;
unsigned max_links = dir->i_sb->s_max_links;
@@ -4153,7 +4214,7 @@
if (!inode)
return -ENOENT;
- error = may_create(dir, new_dentry);
+ error = may_create(mnt, dir, new_dentry);
if (error)
return error;
@@ -4203,6 +4264,12 @@
fsnotify_link(dir, inode, new_dentry);
return error;
}
+EXPORT_SYMBOL(vfs_link2);
+
+int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
+{
+ return vfs_link2(NULL, old_dentry, dir, new_dentry, delegated_inode);
+}
EXPORT_SYMBOL(vfs_link);
/*
@@ -4258,7 +4325,7 @@
error = security_path_link(old_path.dentry, &new_path, new_dentry);
if (error)
goto out_dput;
- error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode);
+ error = vfs_link2(old_path.mnt, old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode);
out_dput:
done_path_create(&new_path, new_dentry);
if (delegated_inode) {
@@ -4333,7 +4400,8 @@
* ->i_mutex on parents, which works but leads to some truly excessive
* locking].
*/
-int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+int vfs_rename2(struct vfsmount *mnt,
+ struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
struct inode **delegated_inode, unsigned int flags)
{
@@ -4352,19 +4420,19 @@
if (d_real_inode(old_dentry) == d_real_inode(new_dentry))
return 0;
- error = may_delete(old_dir, old_dentry, is_dir);
+ error = may_delete(mnt, old_dir, old_dentry, is_dir);
if (error)
return error;
if (!target) {
- error = may_create(new_dir, new_dentry);
+ error = may_create(mnt, new_dir, new_dentry);
} else {
new_is_dir = d_is_dir(new_dentry);
if (!(flags & RENAME_EXCHANGE))
- error = may_delete(new_dir, new_dentry, is_dir);
+ error = may_delete(mnt, new_dir, new_dentry, is_dir);
else
- error = may_delete(new_dir, new_dentry, new_is_dir);
+ error = may_delete(mnt, new_dir, new_dentry, new_is_dir);
}
if (error)
return error;
@@ -4378,12 +4446,12 @@
*/
if (new_dir != old_dir) {
if (is_dir) {
- error = inode_permission(source, MAY_WRITE);
+ error = inode_permission2(mnt, source, MAY_WRITE);
if (error)
return error;
}
if ((flags & RENAME_EXCHANGE) && new_is_dir) {
- error = inode_permission(target, MAY_WRITE);
+ error = inode_permission2(mnt, target, MAY_WRITE);
if (error)
return error;
}
@@ -4460,6 +4528,14 @@
return error;
}
+EXPORT_SYMBOL(vfs_rename2);
+
+int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry,
+ struct inode **delegated_inode, unsigned int flags)
+{
+ return vfs_rename2(NULL, old_dir, old_dentry, new_dir, new_dentry, delegated_inode, flags);
+}
EXPORT_SYMBOL(vfs_rename);
SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
@@ -4573,7 +4649,7 @@
&new_path, new_dentry, flags);
if (error)
goto exit5;
- error = vfs_rename(old_path.dentry->d_inode, old_dentry,
+ error = vfs_rename2(old_path.mnt, old_path.dentry->d_inode, old_dentry,
new_path.dentry->d_inode, new_dentry,
&delegated_inode, flags);
exit5:
@@ -4618,7 +4694,7 @@
int vfs_whiteout(struct inode *dir, struct dentry *dentry)
{
- int error = may_create(dir, dentry);
+ int error = may_create(NULL, dir, dentry);
if (error)
return error;
diff --git a/fs/namespace.c b/fs/namespace.c
index e6c234b..4dc014e 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -580,6 +580,7 @@
static void free_vfsmnt(struct mount *mnt)
{
+ kfree(mnt->mnt.data);
kfree_const(mnt->mnt_devname);
#ifdef CONFIG_SMP
free_percpu(mnt->mnt_pcp);
@@ -746,26 +747,50 @@
return NULL;
}
-static struct mountpoint *new_mountpoint(struct dentry *dentry)
+static struct mountpoint *get_mountpoint(struct dentry *dentry)
{
- struct hlist_head *chain = mp_hash(dentry);
- struct mountpoint *mp;
+ struct mountpoint *mp, *new = NULL;
int ret;
- mp = kmalloc(sizeof(struct mountpoint), GFP_KERNEL);
- if (!mp)
- return ERR_PTR(-ENOMEM);
-
- ret = d_set_mounted(dentry);
- if (ret) {
- kfree(mp);
- return ERR_PTR(ret);
+ if (d_mountpoint(dentry)) {
+mountpoint:
+ read_seqlock_excl(&mount_lock);
+ mp = lookup_mountpoint(dentry);
+ read_sequnlock_excl(&mount_lock);
+ if (mp)
+ goto done;
}
- mp->m_dentry = dentry;
- mp->m_count = 1;
- hlist_add_head(&mp->m_hash, chain);
- INIT_HLIST_HEAD(&mp->m_list);
+ if (!new)
+ new = kmalloc(sizeof(struct mountpoint), GFP_KERNEL);
+ if (!new)
+ return ERR_PTR(-ENOMEM);
+
+
+ /* Exactly one processes may set d_mounted */
+ ret = d_set_mounted(dentry);
+
+ /* Someone else set d_mounted? */
+ if (ret == -EBUSY)
+ goto mountpoint;
+
+ /* The dentry is not available as a mountpoint? */
+ mp = ERR_PTR(ret);
+ if (ret)
+ goto done;
+
+ /* Add the new mountpoint to the hash table */
+ read_seqlock_excl(&mount_lock);
+ new->m_dentry = dentry;
+ new->m_count = 1;
+ hlist_add_head(&new->m_hash, mp_hash(dentry));
+ INIT_HLIST_HEAD(&new->m_list);
+ read_sequnlock_excl(&mount_lock);
+
+ mp = new;
+ new = NULL;
+done:
+ kfree(new);
return mp;
}
@@ -948,11 +973,21 @@
if (!mnt)
return ERR_PTR(-ENOMEM);
+ mnt->mnt.data = NULL;
+ if (type->alloc_mnt_data) {
+ mnt->mnt.data = type->alloc_mnt_data();
+ if (!mnt->mnt.data) {
+ mnt_free_id(mnt);
+ free_vfsmnt(mnt);
+ return ERR_PTR(-ENOMEM);
+ }
+ }
if (flags & MS_KERNMOUNT)
mnt->mnt.mnt_flags = MNT_INTERNAL;
- root = mount_fs(type, flags, name, data);
+ root = mount_fs(type, flags, name, &mnt->mnt, data);
if (IS_ERR(root)) {
+ kfree(mnt->mnt.data);
mnt_free_id(mnt);
free_vfsmnt(mnt);
return ERR_CAST(root);
@@ -980,6 +1015,14 @@
if (!mnt)
return ERR_PTR(-ENOMEM);
+ if (sb->s_op->clone_mnt_data) {
+ mnt->mnt.data = sb->s_op->clone_mnt_data(old->mnt.data);
+ if (!mnt->mnt.data) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+ }
+
if (flag & (CL_SLAVE | CL_PRIVATE | CL_SHARED_TO_SLAVE))
mnt->mnt_group_id = 0; /* not a peer of original */
else
@@ -1048,6 +1091,7 @@
return mnt;
out_free:
+ kfree(mnt->mnt.data);
mnt_free_id(mnt);
free_vfsmnt(mnt);
return ERR_PTR(err);
@@ -1568,11 +1612,11 @@
struct mount *mnt;
namespace_lock();
+ lock_mount_hash();
mp = lookup_mountpoint(dentry);
if (IS_ERR_OR_NULL(mp))
goto out_unlock;
- lock_mount_hash();
event++;
while (!hlist_empty(&mp->m_list)) {
mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list);
@@ -1582,9 +1626,9 @@
}
else umount_tree(mnt, UMOUNT_CONNECTED);
}
- unlock_mount_hash();
put_mountpoint(mp);
out_unlock:
+ unlock_mount_hash();
namespace_unlock();
}
@@ -2013,9 +2057,7 @@
namespace_lock();
mnt = lookup_mnt(path);
if (likely(!mnt)) {
- struct mountpoint *mp = lookup_mountpoint(dentry);
- if (!mp)
- mp = new_mountpoint(dentry);
+ struct mountpoint *mp = get_mountpoint(dentry);
if (IS_ERR(mp)) {
namespace_unlock();
inode_unlock(dentry->d_inode);
@@ -2034,7 +2076,11 @@
static void unlock_mount(struct mountpoint *where)
{
struct dentry *dentry = where->m_dentry;
+
+ read_seqlock_excl(&mount_lock);
put_mountpoint(where);
+ read_sequnlock_excl(&mount_lock);
+
namespace_unlock();
inode_unlock(dentry->d_inode);
}
@@ -2253,8 +2299,14 @@
err = change_mount_flags(path->mnt, flags);
else if (!capable(CAP_SYS_ADMIN))
err = -EPERM;
- else
- err = do_remount_sb(sb, flags, data, 0);
+ else {
+ err = do_remount_sb2(path->mnt, sb, flags, data, 0);
+ namespace_lock();
+ lock_mount_hash();
+ propagate_remount(mnt);
+ unlock_mount_hash();
+ namespace_unlock();
+ }
if (!err) {
lock_mount_hash();
mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK;
@@ -3110,9 +3162,9 @@
touch_mnt_namespace(current->nsproxy->mnt_ns);
/* A moved mount should not expire automatically */
list_del_init(&new_mnt->mnt_expire);
+ put_mountpoint(root_mp);
unlock_mount_hash();
chroot_fs_refs(&root, &new);
- put_mountpoint(root_mp);
error = 0;
out4:
unlock_mount(old_mp);
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 5f1af4c..53e02b8 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -477,7 +477,7 @@
{
if (!list_empty(&NFS_I(dir)->open_files)) {
nfs_advise_use_readdirplus(dir);
- nfs_zap_mapping(dir, dir->i_mapping);
+ invalidate_mapping_pages(dir->i_mapping, 0, -1);
}
}
@@ -886,17 +886,6 @@
goto out;
}
-static bool nfs_dir_mapping_need_revalidate(struct inode *dir)
-{
- struct nfs_inode *nfsi = NFS_I(dir);
-
- if (nfs_attribute_cache_expired(dir))
- return true;
- if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
- return true;
- return false;
-}
-
/* The file offset position represents the dirent entry number. A
last cookie cache takes care of the common case of reading the
whole directory.
@@ -928,7 +917,7 @@
desc->decode = NFS_PROTO(inode)->decode_dirent;
desc->plus = nfs_use_readdirplus(inode, ctx) ? 1 : 0;
- if (ctx->pos == 0 || nfs_dir_mapping_need_revalidate(inode))
+ if (ctx->pos == 0 || nfs_attribute_cache_expired(inode))
res = nfs_revalidate_mapping(inode, file->f_mapping);
if (res < 0)
goto out;
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 9ea85ae..a1de8ef 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -374,7 +374,7 @@
*/
if (!PageUptodate(page)) {
unsigned pglen = nfs_page_length(page);
- unsigned end = offset + len;
+ unsigned end = offset + copied;
if (pglen == 0) {
zero_user_segments(page, 0, offset,
diff --git a/fs/nfs/filelayout/filelayoutdev.c b/fs/nfs/filelayout/filelayoutdev.c
index 4946ef4..85ef38f 100644
--- a/fs/nfs/filelayout/filelayoutdev.c
+++ b/fs/nfs/filelayout/filelayoutdev.c
@@ -283,7 +283,8 @@
s->nfs_client->cl_rpcclient->cl_auth->au_flavor);
out_test_devid:
- if (filelayout_test_devid_unavailable(devid))
+ if (ret->ds_clp == NULL ||
+ filelayout_test_devid_unavailable(devid))
ret = NULL;
out:
return ret;
diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c
index 98ace12..a5c3888 100644
--- a/fs/nfs/flexfilelayout/flexfilelayout.c
+++ b/fs/nfs/flexfilelayout/flexfilelayout.c
@@ -28,6 +28,9 @@
static struct group_info *ff_zero_group;
+static void ff_layout_read_record_layoutstats_done(struct rpc_task *task,
+ struct nfs_pgio_header *hdr);
+
static struct pnfs_layout_hdr *
ff_layout_alloc_layout_hdr(struct inode *inode, gfp_t gfp_flags)
{
@@ -1293,6 +1296,7 @@
hdr->pgio_mirror_idx + 1,
&hdr->pgio_mirror_idx))
goto out_eagain;
+ ff_layout_read_record_layoutstats_done(task, hdr);
pnfs_read_resend_pnfs(hdr);
return task->tk_status;
case -NFS4ERR_RESET_TO_MDS:
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 241da19..78ff8b6 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2678,7 +2678,8 @@
sattr->ia_valid |= ATTR_MTIME;
/* Except MODE, it seems harmless of setting twice. */
- if ((attrset[1] & FATTR4_WORD1_MODE))
+ if (opendata->o_arg.createmode != NFS4_CREATE_EXCLUSIVE &&
+ attrset[1] & FATTR4_WORD1_MODE)
sattr->ia_valid &= ~ATTR_MODE;
if (attrset[2] & FATTR4_WORD2_SECURITY_LABEL)
@@ -8371,6 +8372,7 @@
goto out;
}
+ nfs4_sequence_free_slot(&lgp->res.seq_res);
err = nfs4_handle_exception(server, nfs4err, exception);
if (!status) {
if (exception->retry)
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index 259ef85..415d7e6 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -299,6 +299,14 @@
}
}
+static void
+pnfs_clear_layoutreturn_info(struct pnfs_layout_hdr *lo)
+{
+ lo->plh_return_iomode = 0;
+ lo->plh_return_seq = 0;
+ clear_bit(NFS_LAYOUT_RETURN_REQUESTED, &lo->plh_flags);
+}
+
/*
* Mark a pnfs_layout_hdr and all associated layout segments as invalid
*
@@ -317,6 +325,7 @@
};
set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
+ pnfs_clear_layoutreturn_info(lo);
return pnfs_mark_matching_lsegs_invalid(lo, lseg_list, &range, 0);
}
@@ -411,7 +420,9 @@
list_del_init(&lseg->pls_list);
/* Matched by pnfs_get_layout_hdr in pnfs_layout_insert_lseg */
atomic_dec(&lo->plh_refcount);
- if (list_empty(&lo->plh_segs)) {
+ if (list_empty(&lo->plh_segs) &&
+ !test_bit(NFS_LAYOUT_RETURN_REQUESTED, &lo->plh_flags) &&
+ !test_bit(NFS_LAYOUT_RETURN, &lo->plh_flags)) {
if (atomic_read(&lo->plh_outstanding) == 0)
set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
clear_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags);
@@ -816,14 +827,6 @@
pnfs_destroy_layouts_byclid(clp, false);
}
-static void
-pnfs_clear_layoutreturn_info(struct pnfs_layout_hdr *lo)
-{
- lo->plh_return_iomode = 0;
- lo->plh_return_seq = 0;
- clear_bit(NFS_LAYOUT_RETURN_REQUESTED, &lo->plh_flags);
-}
-
/* update lo->plh_stateid with new if is more recent */
void
pnfs_set_layout_stateid(struct pnfs_layout_hdr *lo, const nfs4_stateid *new,
@@ -944,6 +947,7 @@
void pnfs_clear_layoutreturn_waitbit(struct pnfs_layout_hdr *lo)
{
clear_bit_unlock(NFS_LAYOUT_RETURN, &lo->plh_flags);
+ clear_bit(NFS_LAYOUT_RETURN_LOCK, &lo->plh_flags);
smp_mb__after_atomic();
wake_up_bit(&lo->plh_flags, NFS_LAYOUT_RETURN);
rpc_wake_up(&NFS_SERVER(lo->plh_inode)->roc_rpcwaitq);
@@ -957,8 +961,9 @@
/* Serialise LAYOUTGET/LAYOUTRETURN */
if (atomic_read(&lo->plh_outstanding) != 0)
return false;
- if (test_and_set_bit(NFS_LAYOUT_RETURN, &lo->plh_flags))
+ if (test_and_set_bit(NFS_LAYOUT_RETURN_LOCK, &lo->plh_flags))
return false;
+ set_bit(NFS_LAYOUT_RETURN, &lo->plh_flags);
pnfs_get_layout_hdr(lo);
if (test_bit(NFS_LAYOUT_RETURN_REQUESTED, &lo->plh_flags)) {
if (stateid != NULL) {
@@ -1252,13 +1257,11 @@
* i_lock */
spin_lock(&ino->i_lock);
lo = nfsi->layout;
- if (lo && test_bit(NFS_LAYOUT_RETURN, &lo->plh_flags))
- sleep = true;
- spin_unlock(&ino->i_lock);
-
- if (sleep)
+ if (lo && test_bit(NFS_LAYOUT_RETURN, &lo->plh_flags)) {
rpc_sleep_on(&NFS_SERVER(ino)->roc_rpcwaitq, task, NULL);
-
+ sleep = true;
+ }
+ spin_unlock(&ino->i_lock);
return sleep;
}
@@ -1950,6 +1953,8 @@
spin_lock(&inode->i_lock);
pnfs_set_plh_return_info(lo, range.iomode, 0);
+ /* Block LAYOUTGET */
+ set_bit(NFS_LAYOUT_RETURN, &lo->plh_flags);
/*
* mark all matching lsegs so that we are sure to have no live
* segments at hand when sending layoutreturn. See pnfs_put_lseg()
@@ -2286,6 +2291,10 @@
struct nfs_pageio_descriptor pgio;
if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) {
+ /* Prevent deadlocks with layoutreturn! */
+ pnfs_put_lseg(hdr->lseg);
+ hdr->lseg = NULL;
+
nfs_pageio_init_read(&pgio, hdr->inode, false,
hdr->completion_ops);
hdr->task.tk_status = nfs_pageio_resend(&pgio, hdr);
diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h
index 5c29551..44cad8a 100644
--- a/fs/nfs/pnfs.h
+++ b/fs/nfs/pnfs.h
@@ -96,6 +96,7 @@
NFS_LAYOUT_RW_FAILED, /* get rw layout failed stop trying */
NFS_LAYOUT_BULK_RECALL, /* bulk recall affecting layout */
NFS_LAYOUT_RETURN, /* layoutreturn in progress */
+ NFS_LAYOUT_RETURN_LOCK, /* Serialise layoutreturn */
NFS_LAYOUT_RETURN_REQUESTED, /* Return this layout ASAP */
NFS_LAYOUT_INVALID_STID, /* layout stateid id is invalid */
NFS_LAYOUT_FIRST_LAYOUTGET, /* Serialize first layoutget */
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 001796b..ddce94ce 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -2904,7 +2904,7 @@
MODULE_PARM_DESC(max_session_slots, "Maximum number of outstanding NFSv4.1 "
"requests the client will negotiate");
module_param(max_session_cb_slots, ushort, 0644);
-MODULE_PARM_DESC(max_session_slots, "Maximum number of parallel NFSv4.1 "
+MODULE_PARM_DESC(max_session_cb_slots, "Maximum number of parallel NFSv4.1 "
"callbacks the client will process for a given server");
module_param(send_implementation_id, ushort, 0644);
MODULE_PARM_DESC(send_implementation_id,
diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
index 42aace4..6481369 100644
--- a/fs/nfsd/nfs4layouts.c
+++ b/fs/nfsd/nfs4layouts.c
@@ -223,10 +223,11 @@
struct nfs4_layout_stateid *ls;
struct nfs4_stid *stp;
- stp = nfs4_alloc_stid(cstate->clp, nfs4_layout_stateid_cache);
+ stp = nfs4_alloc_stid(cstate->clp, nfs4_layout_stateid_cache,
+ nfsd4_free_layout_stateid);
if (!stp)
return NULL;
- stp->sc_free = nfsd4_free_layout_stateid;
+
get_nfs4_file(fp);
stp->sc_file = fp;
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 4b4beaa..a0dee8a 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -633,8 +633,8 @@
return co;
}
-struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl,
- struct kmem_cache *slab)
+struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *slab,
+ void (*sc_free)(struct nfs4_stid *))
{
struct nfs4_stid *stid;
int new_id;
@@ -650,6 +650,8 @@
idr_preload_end();
if (new_id < 0)
goto out_free;
+
+ stid->sc_free = sc_free;
stid->sc_client = cl;
stid->sc_stateid.si_opaque.so_id = new_id;
stid->sc_stateid.si_opaque.so_clid = cl->cl_clientid;
@@ -675,15 +677,12 @@
static struct nfs4_ol_stateid * nfs4_alloc_open_stateid(struct nfs4_client *clp)
{
struct nfs4_stid *stid;
- struct nfs4_ol_stateid *stp;
- stid = nfs4_alloc_stid(clp, stateid_slab);
+ stid = nfs4_alloc_stid(clp, stateid_slab, nfs4_free_ol_stateid);
if (!stid)
return NULL;
- stp = openlockstateid(stid);
- stp->st_stid.sc_free = nfs4_free_ol_stateid;
- return stp;
+ return openlockstateid(stid);
}
static void nfs4_free_deleg(struct nfs4_stid *stid)
@@ -781,11 +780,10 @@
goto out_dec;
if (delegation_blocked(¤t_fh->fh_handle))
goto out_dec;
- dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab));
+ dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab, nfs4_free_deleg));
if (dp == NULL)
goto out_dec;
- dp->dl_stid.sc_free = nfs4_free_deleg;
/*
* delegation seqid's are never incremented. The 4.1 special
* meaning of seqid 0 isn't meaningful, really, but let's avoid
@@ -5580,7 +5578,6 @@
stp->st_stateowner = nfs4_get_stateowner(&lo->lo_owner);
get_nfs4_file(fp);
stp->st_stid.sc_file = fp;
- stp->st_stid.sc_free = nfs4_free_lock_stateid;
stp->st_access_bmap = 0;
stp->st_deny_bmap = open_stp->st_deny_bmap;
stp->st_openstp = open_stp;
@@ -5623,7 +5620,7 @@
lst = find_lock_stateid(lo, fi);
if (lst == NULL) {
spin_unlock(&clp->cl_lock);
- ns = nfs4_alloc_stid(clp, stateid_slab);
+ ns = nfs4_alloc_stid(clp, stateid_slab, nfs4_free_lock_stateid);
if (ns == NULL)
return NULL;
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index c939936..4516e8b 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -603,8 +603,8 @@
__be32 nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
stateid_t *stateid, unsigned char typemask,
struct nfs4_stid **s, struct nfsd_net *nn);
-struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl,
- struct kmem_cache *slab);
+struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *slab,
+ void (*sc_free)(struct nfs4_stid *));
void nfs4_unhash_stid(struct nfs4_stid *s);
void nfs4_put_stid(struct nfs4_stid *s);
void nfs4_inc_and_copy_stateid(stateid_t *dst, struct nfs4_stid *stid);
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 7ebfca6..7f99c96 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -488,7 +488,7 @@
}
/* you can only watch an inode if you have read permissions on it */
- ret = inode_permission(path->dentry->d_inode, MAY_READ);
+ ret = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ);
if (ret)
path_put(path);
out:
diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c
index 741077d..a364524 100644
--- a/fs/notify/inode_mark.c
+++ b/fs/notify/inode_mark.c
@@ -150,12 +150,10 @@
*/
void fsnotify_unmount_inodes(struct super_block *sb)
{
- struct inode *inode, *next_i, *need_iput = NULL;
+ struct inode *inode, *iput_inode = NULL;
spin_lock(&sb->s_inode_list_lock);
- list_for_each_entry_safe(inode, next_i, &sb->s_inodes, i_sb_list) {
- struct inode *need_iput_tmp;
-
+ list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
/*
* We cannot __iget() an inode in state I_FREEING,
* I_WILL_FREE, or I_NEW which is fine because by that point
@@ -178,49 +176,24 @@
continue;
}
- need_iput_tmp = need_iput;
- need_iput = NULL;
-
- /* In case fsnotify_inode_delete() drops a reference. */
- if (inode != need_iput_tmp)
- __iget(inode);
- else
- need_iput_tmp = NULL;
+ __iget(inode);
spin_unlock(&inode->i_lock);
-
- /* In case the dropping of a reference would nuke next_i. */
- while (&next_i->i_sb_list != &sb->s_inodes) {
- spin_lock(&next_i->i_lock);
- if (!(next_i->i_state & (I_FREEING | I_WILL_FREE)) &&
- atomic_read(&next_i->i_count)) {
- __iget(next_i);
- need_iput = next_i;
- spin_unlock(&next_i->i_lock);
- break;
- }
- spin_unlock(&next_i->i_lock);
- next_i = list_next_entry(next_i, i_sb_list);
- }
-
- /*
- * We can safely drop s_inode_list_lock here because either
- * we actually hold references on both inode and next_i or
- * end of list. Also no new inodes will be added since the
- * umount has begun.
- */
spin_unlock(&sb->s_inode_list_lock);
- if (need_iput_tmp)
- iput(need_iput_tmp);
+ if (iput_inode)
+ iput(iput_inode);
/* for each watch, send FS_UNMOUNT and then remove it */
fsnotify(inode, FS_UNMOUNT, inode, FSNOTIFY_EVENT_INODE, NULL, 0);
fsnotify_inode_delete(inode);
- iput(inode);
+ iput_inode = inode;
spin_lock(&sb->s_inode_list_lock);
}
spin_unlock(&sb->s_inode_list_lock);
+
+ if (iput_inode)
+ iput(iput_inode);
}
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index 4dc09da..4da5c6a 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -337,7 +337,7 @@
if (error)
return error;
/* you can only watch an inode if you have read permissions on it */
- error = inode_permission(path->dentry->d_inode, MAY_READ);
+ error = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ);
if (error)
path_put(path);
return error;
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index 83d576f..77d1632 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -3303,6 +3303,16 @@
mlog(ML_BASTS, "lockres %s, level %d => %d\n", lockres->l_name,
lockres->l_level, new_level);
+ /*
+ * On DLM_LKF_VALBLK, fsdlm behaves differently with o2cb. It always
+ * expects DLM_LKF_VALBLK being set if the LKB has LVB, so that
+ * we can recover correctly from node failure. Otherwise, we may get
+ * invalid LVB in LKB, but without DLM_SBF_VALNOTVALID being set.
+ */
+ if (!ocfs2_is_o2cb_active() &&
+ lockres->l_ops->flags & LOCK_TYPE_USES_LVB)
+ lvb = 1;
+
if (lvb)
dlm_flags |= DLM_LKF_VALBLK;
diff --git a/fs/ocfs2/stackglue.c b/fs/ocfs2/stackglue.c
index 52c07346b..8203590 100644
--- a/fs/ocfs2/stackglue.c
+++ b/fs/ocfs2/stackglue.c
@@ -48,6 +48,12 @@
*/
static struct ocfs2_stack_plugin *active_stack;
+inline int ocfs2_is_o2cb_active(void)
+{
+ return !strcmp(active_stack->sp_name, OCFS2_STACK_PLUGIN_O2CB);
+}
+EXPORT_SYMBOL_GPL(ocfs2_is_o2cb_active);
+
static struct ocfs2_stack_plugin *ocfs2_stack_lookup(const char *name)
{
struct ocfs2_stack_plugin *p;
diff --git a/fs/ocfs2/stackglue.h b/fs/ocfs2/stackglue.h
index f2dce10..e3036e1 100644
--- a/fs/ocfs2/stackglue.h
+++ b/fs/ocfs2/stackglue.h
@@ -298,6 +298,9 @@
int ocfs2_stack_glue_register(struct ocfs2_stack_plugin *plugin);
void ocfs2_stack_glue_unregister(struct ocfs2_stack_plugin *plugin);
+/* In ocfs2_downconvert_lock(), we need to know which stack we are using */
+int ocfs2_is_o2cb_active(void);
+
extern struct kset *ocfs2_kset;
#endif /* STACKGLUE_H */
diff --git a/fs/open.c b/fs/open.c
index d3ed817..568749b 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -34,8 +34,8 @@
#include "internal.h"
-int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
- struct file *filp)
+int do_truncate2(struct vfsmount *mnt, struct dentry *dentry, loff_t length,
+ unsigned int time_attrs, struct file *filp)
{
int ret;
struct iattr newattrs;
@@ -60,18 +60,25 @@
inode_lock(dentry->d_inode);
/* Note any delegations or leases have already been broken: */
- ret = notify_change(dentry, &newattrs, NULL);
+ ret = notify_change2(mnt, dentry, &newattrs, NULL);
inode_unlock(dentry->d_inode);
return ret;
}
+int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
+ struct file *filp)
+{
+ return do_truncate2(NULL, dentry, length, time_attrs, filp);
+}
long vfs_truncate(const struct path *path, loff_t length)
{
struct inode *inode;
+ struct vfsmount *mnt;
struct dentry *upperdentry;
long error;
inode = path->dentry->d_inode;
+ mnt = path->mnt;
/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
if (S_ISDIR(inode->i_mode))
@@ -83,7 +90,7 @@
if (error)
goto out;
- error = inode_permission(inode, MAY_WRITE);
+ error = inode_permission2(mnt, inode, MAY_WRITE);
if (error)
goto mnt_drop_write_and_out;
@@ -117,7 +124,7 @@
if (!error)
error = security_path_truncate(path);
if (!error)
- error = do_truncate(path->dentry, length, 0, NULL);
+ error = do_truncate2(mnt, path->dentry, length, 0, NULL);
put_write_and_out:
put_write_access(upperdentry->d_inode);
@@ -166,6 +173,7 @@
{
struct inode *inode;
struct dentry *dentry;
+ struct vfsmount *mnt;
struct fd f;
int error;
@@ -182,6 +190,7 @@
small = 0;
dentry = f.file->f_path.dentry;
+ mnt = f.file->f_path.mnt;
inode = dentry->d_inode;
error = -EINVAL;
if (!S_ISREG(inode->i_mode) || !(f.file->f_mode & FMODE_WRITE))
@@ -201,7 +210,7 @@
if (!error)
error = security_path_truncate(&f.file->f_path);
if (!error)
- error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);
+ error = do_truncate2(mnt, dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);
sb_end_write(inode->i_sb);
out_putf:
fdput(f);
@@ -357,6 +366,7 @@
struct cred *override_cred;
struct path path;
struct inode *inode;
+ struct vfsmount *mnt;
int res;
unsigned int lookup_flags = LOOKUP_FOLLOW;
@@ -387,6 +397,7 @@
goto out;
inode = d_backing_inode(path.dentry);
+ mnt = path.mnt;
if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) {
/*
@@ -398,7 +409,7 @@
goto out_path_release;
}
- res = inode_permission(inode, mode | MAY_ACCESS);
+ res = inode_permission2(mnt, inode, mode | MAY_ACCESS);
/* SuS v2 requires we report a read only fs too */
if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
goto out_path_release;
@@ -442,7 +453,7 @@
if (error)
goto out;
- error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
+ error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
if (error)
goto dput_and_out;
@@ -462,6 +473,7 @@
{
struct fd f = fdget_raw(fd);
struct inode *inode;
+ struct vfsmount *mnt;
int error = -EBADF;
error = -EBADF;
@@ -469,12 +481,13 @@
goto out;
inode = file_inode(f.file);
+ mnt = f.file->f_path.mnt;
error = -ENOTDIR;
if (!S_ISDIR(inode->i_mode))
goto out_putf;
- error = inode_permission(inode, MAY_EXEC | MAY_CHDIR);
+ error = inode_permission2(mnt, inode, MAY_EXEC | MAY_CHDIR);
if (!error)
set_fs_pwd(current->fs, &f.file->f_path);
out_putf:
@@ -493,7 +506,7 @@
if (error)
goto out;
- error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
+ error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
if (error)
goto dput_and_out;
@@ -533,7 +546,7 @@
goto out_unlock;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
- error = notify_change(path->dentry, &newattrs, &delegated_inode);
+ error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
out_unlock:
inode_unlock(inode);
if (delegated_inode) {
@@ -613,7 +626,7 @@
inode_lock(inode);
error = security_path_chown(path, uid, gid);
if (!error)
- error = notify_change(path->dentry, &newattrs, &delegated_inode);
+ error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
inode_unlock(inode);
if (delegated_inode) {
error = break_deleg_wait(&delegated_inode);
diff --git a/fs/pnode.c b/fs/pnode.c
index 234a9ac..83b5bb1 100644
--- a/fs/pnode.c
+++ b/fs/pnode.c
@@ -458,3 +458,32 @@
__propagate_umount(mnt);
return 0;
}
+
+/*
+ * Iterates over all slaves, and slaves of slaves.
+ */
+static struct mount *next_descendent(struct mount *root, struct mount *cur)
+{
+ if (!IS_MNT_NEW(cur) && !list_empty(&cur->mnt_slave_list))
+ return first_slave(cur);
+ do {
+ if (cur->mnt_slave.next != &cur->mnt_master->mnt_slave_list)
+ return next_slave(cur);
+ cur = cur->mnt_master;
+ } while (cur != root);
+ return NULL;
+}
+
+void propagate_remount(struct mount *mnt)
+{
+ struct mount *m = mnt;
+ struct super_block *sb = mnt->mnt.mnt_sb;
+
+ if (sb->s_op->copy_mnt_data) {
+ m = next_descendent(mnt, m);
+ while (m) {
+ sb->s_op->copy_mnt_data(m->mnt.data, mnt->mnt.data);
+ m = next_descendent(mnt, m);
+ }
+ }
+}
diff --git a/fs/pnode.h b/fs/pnode.h
index 550f5a8..03a8001 100644
--- a/fs/pnode.h
+++ b/fs/pnode.h
@@ -44,6 +44,7 @@
int propagate_umount(struct list_head *);
int propagate_mount_busy(struct mount *, int);
void propagate_mount_unlock(struct mount *);
+void propagate_remount(struct mount *);
void mnt_release_group_id(struct mount *);
int get_dominating_id(struct mount *mnt, const struct path *root);
unsigned int mnt_get_count(struct mount *mnt);
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 5955220..c9d48dc 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -922,11 +922,10 @@
int error;
if (type == ACL_TYPE_ACCESS) {
- error = posix_acl_equiv_mode(acl, &inode->i_mode);
- if (error < 0)
- return 0;
- if (error == 0)
- acl = NULL;
+ error = posix_acl_update_mode(inode,
+ &inode->i_mode, &acl);
+ if (error)
+ return error;
}
inode->i_ctime = current_time(inode);
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 55313d9..d4e37ac 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -709,7 +709,7 @@
ctl_dir = container_of(head, struct ctl_dir, header);
if (!dir_emit_dots(file, ctx))
- return 0;
+ goto out;
pos = 2;
@@ -719,6 +719,7 @@
break;
}
}
+out:
sysctl_head_finish(head);
return 0;
}
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 87cf40b..65d28f9 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -150,10 +150,9 @@
const char *kaddr;
long pages_pinned;
struct page *page;
- unsigned int gup_flags = 0;
- pages_pinned = get_user_pages_remote(current, mm, page_start_vaddr,
- 1, gup_flags, &page, NULL);
+ pages_pinned = get_user_pages_remote(current, mm,
+ page_start_vaddr, 1, 0, &page, NULL);
if (pages_pinned < 1) {
seq_puts(m, "<fault>]");
return;
@@ -396,8 +395,10 @@
goto done;
}
- if (is_stack(priv, vma))
+ if (is_stack(priv, vma)) {
name = "[stack]";
+ goto done;
+ }
if (vma_get_anon_name(vma)) {
seq_pad(m, ' ');
diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c
index 3f1190d..6863773 100644
--- a/fs/proc_namespace.c
+++ b/fs/proc_namespace.c
@@ -118,7 +118,9 @@
if (err)
goto out;
show_mnt_opts(m, mnt);
- if (sb->s_op->show_options)
+ if (sb->s_op->show_options2)
+ err = sb->s_op->show_options2(mnt, m, mnt_path.dentry);
+ else if (sb->s_op->show_options)
err = sb->s_op->show_options(m, mnt_path.dentry);
seq_puts(m, " 0 0\n");
out:
@@ -180,7 +182,9 @@
err = show_sb_opts(m, sb);
if (err)
goto out;
- if (sb->s_op->show_options)
+ if (sb->s_op->show_options2) {
+ err = sb->s_op->show_options2(mnt, m, mnt->mnt_root);
+ } else if (sb->s_op->show_options)
err = sb->s_op->show_options(m, mnt->mnt_root);
seq_putc(m, '\n');
out:
diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c
index 97b79cc..9408a54 100644
--- a/fs/sdcardfs/derived_perm.c
+++ b/fs/sdcardfs/derived_perm.c
@@ -30,11 +30,12 @@
ci->userid = pi->userid;
ci->d_uid = pi->d_uid;
ci->under_android = pi->under_android;
+ set_top(ci, pi->top);
}
/* helper function for derived state */
-void setup_derived_state(struct inode *inode, perm_t perm,
- userid_t userid, uid_t uid, bool under_android)
+void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
+ uid_t uid, bool under_android, struct inode *top)
{
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
@@ -42,14 +43,14 @@
info->userid = userid;
info->d_uid = uid;
info->under_android = under_android;
+ set_top(info, top);
}
/* While renaming, there is a point where we want the path from dentry, but the name from newdentry */
void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry)
{
- struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
- struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode);
- struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
+ struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry));
+ struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent));
appid_t appid;
/* By default, each inode inherits from its parent.
@@ -60,7 +61,7 @@
* stage of each system call by fix_derived_permission(inode).
*/
- inherit_derived_state(parent->d_inode, dentry->d_inode);
+ inherit_derived_state(d_inode(parent), d_inode(dentry));
/* Derive custom permissions based on parent and current node */
switch (parent_info->perm) {
@@ -71,6 +72,7 @@
/* Legacy internal layout places users at top level */
info->perm = PERM_ROOT;
info->userid = simple_strtoul(newdentry->d_name.name, NULL, 10);
+ set_top(info, &info->vfs_inode);
break;
case PERM_ROOT:
/* Assume masked off by default. */
@@ -78,28 +80,33 @@
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID;
info->under_android = true;
+ set_top(info, &info->vfs_inode);
}
break;
case PERM_ANDROID:
if (!strcasecmp(newdentry->d_name.name, "data")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID_DATA;
+ set_top(info, &info->vfs_inode);
} else if (!strcasecmp(newdentry->d_name.name, "obb")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID_OBB;
+ set_top(info, &info->vfs_inode);
/* Single OBB directory is always shared */
} else if (!strcasecmp(newdentry->d_name.name, "media")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID_MEDIA;
+ set_top(info, &info->vfs_inode);
}
break;
case PERM_ANDROID_DATA:
case PERM_ANDROID_OBB:
case PERM_ANDROID_MEDIA:
- appid = get_appid(sbi->pkgl_id, newdentry->d_name.name);
+ appid = get_appid(newdentry->d_name.name);
if (appid != 0) {
info->d_uid = multiuser_get_uid(parent_info->userid, appid);
}
+ set_top(info, &info->vfs_inode);
break;
}
}
@@ -109,17 +116,72 @@
get_derived_permission_new(parent, dentry, dentry);
}
-void get_derive_permissions_recursive(struct dentry *parent) {
+static int descendant_may_need_fixup(perm_t perm) {
+ if (perm == PERM_PRE_ROOT || perm == PERM_ROOT || perm == PERM_ANDROID)
+ return 1;
+ return 0;
+}
+
+static int needs_fixup(perm_t perm) {
+ if (perm == PERM_ANDROID_DATA || perm == PERM_ANDROID_OBB
+ || perm == PERM_ANDROID_MEDIA)
+ return 1;
+ return 0;
+}
+
+void fixup_perms_recursive(struct dentry *dentry, const char* name, size_t len) {
+ struct dentry *child;
+ struct sdcardfs_inode_info *info;
+ if (!dget(dentry))
+ return;
+ if (!d_inode(dentry)) {
+ dput(dentry);
+ return;
+ }
+ info = SDCARDFS_I(d_inode(dentry));
+
+ if (needs_fixup(info->perm)) {
+ spin_lock(&dentry->d_lock);
+ list_for_each_entry(child, &dentry->d_subdirs, d_child) {
+ dget(child);
+ if (!strncasecmp(child->d_name.name, name, len)) {
+ if (child->d_inode) {
+ get_derived_permission(dentry, child);
+ fixup_tmp_permissions(child->d_inode);
+ dput(child);
+ break;
+ }
+ }
+ dput(child);
+ }
+ spin_unlock(&dentry->d_lock);
+ } else if (descendant_may_need_fixup(info->perm)) {
+ spin_lock(&dentry->d_lock);
+ list_for_each_entry(child, &dentry->d_subdirs, d_child) {
+ fixup_perms_recursive(child, name, len);
+ }
+ spin_unlock(&dentry->d_lock);
+ }
+ dput(dentry);
+}
+
+void fixup_top_recursive(struct dentry *parent) {
struct dentry *dentry;
+ struct sdcardfs_inode_info *info;
+ if (!d_inode(parent))
+ return;
+ info = SDCARDFS_I(d_inode(parent));
+ spin_lock(&parent->d_lock);
list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
- if (dentry->d_inode) {
- inode_lock(dentry->d_inode);
- get_derived_permission(parent, dentry);
- fix_derived_permission(dentry->d_inode);
- get_derive_permissions_recursive(dentry);
- inode_unlock(dentry->d_inode);
+ if (d_inode(dentry)) {
+ if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) {
+ get_derived_permission(parent, dentry);
+ fixup_tmp_permissions(d_inode(dentry));
+ fixup_top_recursive(dentry);
+ }
}
}
+ spin_unlock(&parent->d_lock);
}
/* main function for updating derived permission */
@@ -127,7 +189,7 @@
{
struct dentry *parent;
- if(!dentry || !dentry->d_inode) {
+ if(!dentry || !d_inode(dentry)) {
printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__);
return;
}
@@ -135,9 +197,8 @@
* 1. need to check whether the dentry is updated or not
* 2. remove the root dentry update
*/
- inode_lock(dentry->d_inode);
if(IS_ROOT(dentry)) {
- //setup_default_pre_root_state(dentry->d_inode);
+ //setup_default_pre_root_state(d_inode(dentry));
} else {
parent = dget_parent(dentry);
if(parent) {
@@ -145,15 +206,14 @@
dput(parent);
}
}
- fix_derived_permission(dentry->d_inode);
- inode_unlock(dentry->d_inode);
+ fixup_tmp_permissions(d_inode(dentry));
}
int need_graft_path(struct dentry *dentry)
{
int ret = 0;
struct dentry *parent = dget_parent(dentry);
- struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
+ struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent));
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
if(parent_info->perm == PERM_ANDROID &&
@@ -212,7 +272,7 @@
{
int ret = 0;
struct dentry *parent = dget_parent(dentry);
- struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
+ struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent));
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
spin_lock(&SDCARDFS_D(dentry)->lock);
diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c
index c249fa9..7750a04 100644
--- a/fs/sdcardfs/file.c
+++ b/fs/sdcardfs/file.c
@@ -216,7 +216,7 @@
goto out_err;
}
- if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
+ if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) {
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
" dentry: %s, task:%s\n",
__func__, dentry->d_name.name, current->comm);
diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c
index f95283e..5b31170 100644
--- a/fs/sdcardfs/inode.c
+++ b/fs/sdcardfs/inode.c
@@ -19,6 +19,7 @@
*/
#include "sdcardfs.h"
+#include <linux/fs_struct.h>
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
const struct cred * override_fsids(struct sdcardfs_sb_info* sbi)
@@ -53,9 +54,12 @@
{
int err;
struct dentry *lower_dentry;
+ struct vfsmount *lower_dentry_mnt;
struct dentry *lower_parent_dentry = NULL;
struct path lower_path;
const struct cred *saved_cred = NULL;
+ struct fs_struct *saved_fs;
+ struct fs_struct *copied_fs;
if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
@@ -70,11 +74,22 @@
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_dentry_mnt = lower_path.mnt;
lower_parent_dentry = lock_parent(lower_dentry);
/* set last 16bytes of mode field to 0664 */
mode = (mode & S_IFMT) | 00664;
- err = vfs_create(d_inode(lower_parent_dentry), lower_dentry, mode, want_excl);
+
+ /* temporarily change umask for lower fs write */
+ saved_fs = current->fs;
+ copied_fs = copy_fs_struct(current->fs);
+ if (!copied_fs) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+ current->fs = copied_fs;
+ current->fs->umask = 0;
+ err = vfs_create2(lower_dentry_mnt, d_inode(lower_parent_dentry), lower_dentry, mode, want_excl);
if (err)
goto out;
@@ -85,6 +100,9 @@
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
out:
+ current->fs = saved_fs;
+ free_fs_struct(copied_fs);
+out_unlock:
unlock_dir(lower_parent_dentry);
sdcardfs_put_lower_path(dentry, &lower_path);
REVERT_CRED(saved_cred);
@@ -138,6 +156,7 @@
{
int err;
struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
struct inode *lower_dir_inode = sdcardfs_lower_inode(dir);
struct dentry *lower_dir_dentry;
struct path lower_path;
@@ -156,10 +175,11 @@
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_mnt = lower_path.mnt;
dget(lower_dentry);
lower_dir_dentry = lock_parent(lower_dentry);
- err = vfs_unlink(lower_dir_inode, lower_dentry, NULL);
+ err = vfs_unlink2(lower_mnt, lower_dir_inode, lower_dentry, NULL);
/*
* Note: unlinking on top of NFS can cause silly-renamed files.
@@ -240,16 +260,15 @@
int err;
int make_nomedia_in_obb = 0;
struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
struct dentry *lower_parent_dentry = NULL;
struct path lower_path;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
const struct cred *saved_cred = NULL;
struct sdcardfs_inode_info *pi = SDCARDFS_I(dir);
- char *page_buf;
- char *nomedia_dir_name;
- char *nomedia_fullpath;
- int fullpath_namelen;
int touch_err = 0;
+ struct fs_struct *saved_fs;
+ struct fs_struct *copied_fs;
if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
@@ -272,14 +291,28 @@
/* the lower_dentry is negative here */
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_mnt = lower_path.mnt;
lower_parent_dentry = lock_parent(lower_dentry);
/* set last 16bytes of mode field to 0775 */
mode = (mode & S_IFMT) | 00775;
- err = vfs_mkdir(d_inode(lower_parent_dentry), lower_dentry, mode);
- if (err)
+ /* temporarily change umask for lower fs write */
+ saved_fs = current->fs;
+ copied_fs = copy_fs_struct(current->fs);
+ if (!copied_fs) {
+ err = -ENOMEM;
+ unlock_dir(lower_parent_dentry);
+ goto out_unlock;
+ }
+ current->fs = copied_fs;
+ current->fs->umask = 0;
+ err = vfs_mkdir2(lower_mnt, d_inode(lower_parent_dentry), lower_dentry, mode);
+
+ if (err) {
+ unlock_dir(lower_parent_dentry);
goto out;
+ }
/* if it is a local obb dentry, setup it with the base obbpath */
if(need_graft_path(dentry)) {
@@ -301,14 +334,18 @@
}
err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid);
- if (err)
+ if (err) {
+ unlock_dir(lower_parent_dentry);
goto out;
+ }
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
/* update number of links on parent directory */
set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink);
+ unlock_dir(lower_parent_dentry);
+
if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb"))
&& (pi->perm == PERM_ANDROID) && (pi->userid == 0))
make_nomedia_in_obb = 1;
@@ -316,43 +353,18 @@
/* When creating /Android/data and /Android/obb, mark them as .nomedia */
if (make_nomedia_in_obb ||
((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) {
-
- page_buf = (char *)__get_free_page(GFP_KERNEL);
- if (!page_buf) {
- printk(KERN_ERR "sdcardfs: failed to allocate page buf\n");
- goto out;
- }
-
- nomedia_dir_name = d_absolute_path(&lower_path, page_buf, PAGE_SIZE);
- if (IS_ERR(nomedia_dir_name)) {
- free_page((unsigned long)page_buf);
- printk(KERN_ERR "sdcardfs: failed to get .nomedia dir name\n");
- goto out;
- }
-
- fullpath_namelen = page_buf + PAGE_SIZE - nomedia_dir_name - 1;
- fullpath_namelen += strlen("/.nomedia");
- nomedia_fullpath = kzalloc(fullpath_namelen + 1, GFP_KERNEL);
- if (!nomedia_fullpath) {
- free_page((unsigned long)page_buf);
- printk(KERN_ERR "sdcardfs: failed to allocate .nomedia fullpath buf\n");
- goto out;
- }
-
- strcpy(nomedia_fullpath, nomedia_dir_name);
- free_page((unsigned long)page_buf);
- strcat(nomedia_fullpath, "/.nomedia");
- touch_err = touch(nomedia_fullpath, 0664);
+ set_fs_pwd(current->fs, &lower_path);
+ touch_err = touch(".nomedia", 0664);
if (touch_err) {
- printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n",
- nomedia_fullpath, touch_err);
- kfree(nomedia_fullpath);
+ printk(KERN_ERR "sdcardfs: failed to create .nomedia in %s: %d\n",
+ lower_path.dentry->d_name.name, touch_err);
goto out;
}
- kfree(nomedia_fullpath);
}
out:
- unlock_dir(lower_parent_dentry);
+ current->fs = saved_fs;
+ free_fs_struct(copied_fs);
+out_unlock:
sdcardfs_put_lower_path(dentry, &lower_path);
out_revert:
REVERT_CRED(saved_cred);
@@ -364,6 +376,7 @@
{
struct dentry *lower_dentry;
struct dentry *lower_dir_dentry;
+ struct vfsmount *lower_mnt;
int err;
struct path lower_path;
const struct cred *saved_cred = NULL;
@@ -384,9 +397,10 @@
sdcardfs_get_real_lower(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_mnt = lower_path.mnt;
lower_dir_dentry = lock_parent(lower_dentry);
- err = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry);
+ err = vfs_rmdir2(lower_mnt, d_inode(lower_dir_dentry), lower_dentry);
if (err)
goto out;
@@ -451,6 +465,7 @@
struct dentry *lower_new_dentry = NULL;
struct dentry *lower_old_dir_dentry = NULL;
struct dentry *lower_new_dir_dentry = NULL;
+ struct vfsmount *lower_mnt = NULL;
struct dentry *trap = NULL;
struct dentry *new_parent = NULL;
struct path lower_old_path, lower_new_path;
@@ -475,6 +490,7 @@
sdcardfs_get_lower_path(new_dentry, &lower_new_path);
lower_old_dentry = lower_old_path.dentry;
lower_new_dentry = lower_new_path.dentry;
+ lower_mnt = lower_old_path.mnt;
lower_old_dir_dentry = dget_parent(lower_old_dentry);
lower_new_dir_dentry = dget_parent(lower_new_dentry);
@@ -490,7 +506,8 @@
goto out;
}
- err = vfs_rename(d_inode(lower_old_dir_dentry), lower_old_dentry,
+ err = vfs_rename2(lower_mnt,
+ d_inode(lower_old_dir_dentry), lower_old_dentry,
d_inode(lower_new_dir_dentry), lower_new_dentry,
NULL, 0);
if (err)
@@ -517,11 +534,9 @@
}
/* At this point, not all dentry information has been moved, so
* we pass along new_dentry for the name.*/
- inode_lock(d_inode(old_dentry));
get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry);
- fix_derived_permission(d_inode(old_dentry));
- get_derive_permissions_recursive(old_dentry);
- inode_unlock(d_inode(old_dentry));
+ fixup_tmp_permissions(d_inode(old_dentry));
+ fixup_top_recursive(old_dentry);
out:
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
dput(lower_old_dir_dentry);
@@ -590,16 +605,63 @@
}
#endif
-static int sdcardfs_permission(struct inode *inode, int mask)
+static int sdcardfs_permission_wrn(struct inode *inode, int mask)
+{
+ WARN(1, "sdcardfs does not support permission. Use permission2.\n");
+ return -EINVAL;
+}
+
+void copy_attrs(struct inode *dest, const struct inode *src)
+{
+ dest->i_mode = src->i_mode;
+ dest->i_uid = src->i_uid;
+ dest->i_gid = src->i_gid;
+ dest->i_rdev = src->i_rdev;
+ dest->i_atime = src->i_atime;
+ dest->i_mtime = src->i_mtime;
+ dest->i_ctime = src->i_ctime;
+ dest->i_blkbits = src->i_blkbits;
+ dest->i_flags = src->i_flags;
+#ifdef CONFIG_FS_POSIX_ACL
+ dest->i_acl = src->i_acl;
+#endif
+#ifdef CONFIG_SECURITY
+ dest->i_security = src->i_security;
+#endif
+}
+
+static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int mask)
{
int err;
+ struct inode tmp;
+ struct inode *top = grab_top(SDCARDFS_I(inode));
+
+ if (!top) {
+ release_top(SDCARDFS_I(inode));
+ WARN(1, "Top value was null!\n");
+ return -EINVAL;
+ }
/*
* Permission check on sdcardfs inode.
* Calling process should have AID_SDCARD_RW permission
+ * Since generic_permission only needs i_mode, i_uid,
+ * i_gid, and i_sb, we can create a fake inode to pass
+ * this information down in.
+ *
+ * The underlying code may attempt to take locks in some
+ * cases for features we're not using, but if that changes,
+ * locks must be dealt with to avoid undefined behavior.
*/
- err = generic_permission(inode, mask);
-
+ copy_attrs(&tmp, inode);
+ tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
+ tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
+ tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top));
+ release_top(SDCARDFS_I(inode));
+ tmp.i_sb = inode->i_sb;
+ if (IS_POSIXACL(inode))
+ printk(KERN_WARNING "%s: This may be undefined behavior... \n", __func__);
+ err = generic_permission(&tmp, mask);
/* XXX
* Original sdcardfs code calls inode_permission(lower_inode,.. )
* for checking inode permission. But doing such things here seems
@@ -628,26 +690,63 @@
}
-static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
+static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia)
+{
+ WARN(1, "sdcardfs does not support setattr. User setattr2.\n");
+ return -EINVAL;
+}
+
+static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct iattr *ia)
{
int err;
struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
struct inode *inode;
struct inode *lower_inode;
struct path lower_path;
struct iattr lower_ia;
struct dentry *parent;
+ struct inode tmp;
+ struct dentry tmp_d;
+ struct inode *top;
+ const struct cred *saved_cred = NULL;
inode = d_inode(dentry);
+ top = grab_top(SDCARDFS_I(inode));
+
+ if (!top) {
+ release_top(SDCARDFS_I(inode));
+ return -EINVAL;
+ }
+
+ /*
+ * Permission check on sdcardfs inode.
+ * Calling process should have AID_SDCARD_RW permission
+ * Since generic_permission only needs i_mode, i_uid,
+ * i_gid, and i_sb, we can create a fake inode to pass
+ * this information down in.
+ *
+ * The underlying code may attempt to take locks in some
+ * cases for features we're not using, but if that changes,
+ * locks must be dealt with to avoid undefined behavior.
+ *
+ */
+ copy_attrs(&tmp, inode);
+ tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
+ tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
+ tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top));
+ tmp.i_size = i_size_read(inode);
+ release_top(SDCARDFS_I(inode));
+ tmp.i_sb = inode->i_sb;
+ tmp_d.d_inode = &tmp;
/*
* Check if user has permission to change dentry. We don't check if
* this user can change the lower inode: that should happen when
* calling notify_change on the lower inode.
*/
- err = setattr_prepare(dentry, ia);
+ err = setattr_prepare(&tmp_d, ia);
- /* no vfs_XXX operations required, cred overriding will be skipped. wj*/
if (!err) {
/* check the Android group ID */
parent = dget_parent(dentry);
@@ -663,8 +762,12 @@
if (err)
goto out_err;
+ /* save current_cred and override it */
+ OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred);
+
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_mnt = lower_path.mnt;
lower_inode = sdcardfs_lower_inode(inode);
/* prepare our own lower struct iattr (with the lower file) */
@@ -685,7 +788,7 @@
if (current->mm)
down_write(¤t->mm->mmap_sem);
if (ia->ia_valid & ATTR_SIZE) {
- err = inode_newsize_ok(inode, ia->ia_size);
+ err = inode_newsize_ok(&tmp, ia->ia_size);
if (err) {
if (current->mm)
up_write(¤t->mm->mmap_sem);
@@ -708,7 +811,7 @@
* tries to open(), unlink(), then ftruncate() a file.
*/
inode_lock(d_inode(lower_dentry));
- err = notify_change(lower_dentry, &lower_ia, /* note: lower_ia */
+ err = notify_change2(lower_mnt, lower_dentry, &lower_ia, /* note: lower_ia */
NULL);
inode_unlock(d_inode(lower_dentry));
if (current->mm)
@@ -727,10 +830,35 @@
out:
sdcardfs_put_lower_path(dentry, &lower_path);
+ REVERT_CRED(saved_cred);
out_err:
return err;
}
+static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct kstat *stat)
+{
+ struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
+ struct inode *top = grab_top(info);
+ if (!top)
+ return -EINVAL;
+
+ stat->dev = inode->i_sb->s_dev;
+ stat->ino = inode->i_ino;
+ stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top));
+ stat->nlink = inode->i_nlink;
+ stat->uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
+ stat->gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
+ stat->rdev = inode->i_rdev;
+ stat->size = i_size_read(inode);
+ stat->atime = inode->i_atime;
+ stat->mtime = inode->i_mtime;
+ stat->ctime = inode->i_ctime;
+ stat->blksize = (1 << inode->i_blkbits);
+ stat->blocks = inode->i_blocks;
+ release_top(info);
+ return 0;
+}
+
static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat)
{
@@ -739,6 +867,7 @@
struct inode *lower_inode;
struct path lower_path;
struct dentry *parent;
+ int err;
parent = dget_parent(dentry);
if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) {
@@ -756,19 +885,17 @@
lower_dentry = lower_path.dentry;
lower_inode = sdcardfs_lower_inode(inode);
-
sdcardfs_copy_and_fix_attrs(inode, lower_inode);
fsstack_copy_inode_size(inode, lower_inode);
-
- generic_fillattr(inode, stat);
+ err = sdcardfs_fillattr(mnt, inode, stat);
sdcardfs_put_lower_path(dentry, &lower_path);
- return 0;
+ return err;
}
const struct inode_operations sdcardfs_symlink_iops = {
- .permission = sdcardfs_permission,
- .setattr = sdcardfs_setattr,
+ .permission2 = sdcardfs_permission,
+ .setattr2 = sdcardfs_setattr,
/* XXX Following operations are implemented,
* but FUSE(sdcard) or FAT does not support them
* These methods are *NOT* perfectly tested.
@@ -781,14 +908,14 @@
const struct inode_operations sdcardfs_dir_iops = {
.create = sdcardfs_create,
.lookup = sdcardfs_lookup,
-#if 0
- .permission = sdcardfs_permission,
-#endif
+ .permission = sdcardfs_permission_wrn,
+ .permission2 = sdcardfs_permission,
.unlink = sdcardfs_unlink,
.mkdir = sdcardfs_mkdir,
.rmdir = sdcardfs_rmdir,
.rename = sdcardfs_rename,
- .setattr = sdcardfs_setattr,
+ .setattr = sdcardfs_setattr_wrn,
+ .setattr2 = sdcardfs_setattr,
.getattr = sdcardfs_getattr,
/* XXX Following operations are implemented,
* but FUSE(sdcard) or FAT does not support them
@@ -800,7 +927,9 @@
};
const struct inode_operations sdcardfs_main_iops = {
- .permission = sdcardfs_permission,
- .setattr = sdcardfs_setattr,
+ .permission = sdcardfs_permission_wrn,
+ .permission2 = sdcardfs_permission,
+ .setattr = sdcardfs_setattr_wrn,
+ .setattr2 = sdcardfs_setattr,
.getattr = sdcardfs_getattr,
};
diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c
index 2d94870..d271617 100644
--- a/fs/sdcardfs/lookup.c
+++ b/fs/sdcardfs/lookup.c
@@ -179,7 +179,7 @@
struct inode *lower_inode;
struct super_block *lower_sb;
- lower_inode = lower_path->dentry->d_inode;
+ lower_inode = d_inode(lower_path->dentry);
lower_sb = sdcardfs_lower_super(sb);
/* check that the lower file system didn't cross a mount point */
@@ -240,6 +240,30 @@
/* Use vfs_path_lookup to check if the dentry exists or not */
err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0,
&lower_path);
+ /* check for other cases */
+ if (err == -ENOENT) {
+ struct dentry *child;
+ struct dentry *match = NULL;
+ inode_lock(d_inode(lower_dir_dentry));
+ spin_lock(&lower_dir_dentry->d_lock);
+ list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) {
+ if (child && d_inode(child)) {
+ if (strcasecmp(child->d_name.name, name)==0) {
+ match = dget(child);
+ break;
+ }
+ }
+ }
+ spin_unlock(&lower_dir_dentry->d_lock);
+ inode_unlock(d_inode(lower_dir_dentry));
+ if (match) {
+ err = vfs_path_lookup(lower_dir_dentry,
+ lower_dir_mnt,
+ match->d_name.name, 0,
+ &lower_path);
+ dput(match);
+ }
+ }
/* no error: handle positive dentries */
if (!err) {
@@ -335,7 +359,7 @@
parent = dget_parent(dentry);
- if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
+ if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) {
ret = ERR_PTR(-EACCES);
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
" dentry: %s, task:%s\n",
@@ -362,18 +386,16 @@
}
if (ret)
dentry = ret;
- if (dentry->d_inode) {
- fsstack_copy_attr_times(dentry->d_inode,
- sdcardfs_lower_inode(dentry->d_inode));
- /* get drived permission */
- inode_lock(dentry->d_inode);
+ if (d_inode(dentry)) {
+ fsstack_copy_attr_times(d_inode(dentry),
+ sdcardfs_lower_inode(d_inode(dentry)));
+ /* get derived permission */
get_derived_permission(parent, dentry);
- fix_derived_permission(dentry->d_inode);
- inode_unlock(dentry->d_inode);
+ fixup_tmp_permissions(d_inode(dentry));
}
/* update parent directory's atime */
- fsstack_copy_attr_atime(parent->d_inode,
- sdcardfs_lower_inode(parent->d_inode));
+ fsstack_copy_attr_atime(d_inode(parent),
+ sdcardfs_lower_inode(d_inode(parent)));
out:
sdcardfs_put_lower_path(parent, &lower_parent_path);
diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c
index a652228..7a8eae2 100644
--- a/fs/sdcardfs/main.c
+++ b/fs/sdcardfs/main.c
@@ -28,7 +28,6 @@
Opt_fsgid,
Opt_gid,
Opt_debug,
- Opt_lower_fs,
Opt_mask,
Opt_multiuser, // May need?
Opt_userid,
@@ -49,7 +48,8 @@
};
static int parse_options(struct super_block *sb, char *options, int silent,
- int *debug, struct sdcardfs_mount_options *opts)
+ int *debug, struct sdcardfs_vfsmount_options *vfsopts,
+ struct sdcardfs_mount_options *opts)
{
char *p;
substring_t args[MAX_OPT_ARGS];
@@ -58,10 +58,10 @@
/* by default, we use AID_MEDIA_RW as uid, gid */
opts->fs_low_uid = AID_MEDIA_RW;
opts->fs_low_gid = AID_MEDIA_RW;
- opts->mask = 0;
+ vfsopts->mask = 0;
opts->multiuser = false;
opts->fs_user_id = 0;
- opts->gid = 0;
+ vfsopts->gid = 0;
/* by default, 0MB is reserved */
opts->reserved_mb = 0;
@@ -94,7 +94,7 @@
case Opt_gid:
if (match_int(&args[0], &option))
return 0;
- opts->gid = option;
+ vfsopts->gid = option;
break;
case Opt_userid:
if (match_int(&args[0], &option))
@@ -104,7 +104,7 @@
case Opt_mask:
if (match_int(&args[0], &option))
return 0;
- opts->mask = option;
+ vfsopts->mask = option;
break;
case Opt_multiuser:
opts->multiuser = true;
@@ -135,6 +135,65 @@
return 0;
}
+int parse_options_remount(struct super_block *sb, char *options, int silent,
+ struct sdcardfs_vfsmount_options *vfsopts)
+{
+ char *p;
+ substring_t args[MAX_OPT_ARGS];
+ int option;
+ int debug;
+
+ if (!options)
+ return 0;
+
+ while ((p = strsep(&options, ",")) != NULL) {
+ int token;
+ if (!*p)
+ continue;
+
+ token = match_token(p, sdcardfs_tokens, args);
+
+ switch (token) {
+ case Opt_debug:
+ debug = 1;
+ break;
+ case Opt_gid:
+ if (match_int(&args[0], &option))
+ return 0;
+ vfsopts->gid = option;
+
+ break;
+ case Opt_mask:
+ if (match_int(&args[0], &option))
+ return 0;
+ vfsopts->mask = option;
+ break;
+ case Opt_multiuser:
+ case Opt_userid:
+ case Opt_fsuid:
+ case Opt_fsgid:
+ case Opt_reserved_mb:
+ printk( KERN_WARNING "Option \"%s\" can't be changed during remount\n", p);
+ break;
+ /* unknown option */
+ default:
+ if (!silent) {
+ printk( KERN_ERR "Unrecognized mount option \"%s\" "
+ "or missing value", p);
+ }
+ return -EINVAL;
+ }
+ }
+
+ if (debug) {
+ printk( KERN_INFO "sdcardfs : options - debug:%d\n", debug);
+ printk( KERN_INFO "sdcardfs : options - gid:%d\n", vfsopts->gid);
+ printk( KERN_INFO "sdcardfs : options - mask:%d\n", vfsopts->mask);
+ }
+
+ return 0;
+}
+
#if 0
/*
* our custom d_alloc_root work-alike
@@ -172,14 +231,15 @@
* There is no need to lock the sdcardfs_super_info's rwsem as there is no
* way anyone can have a reference to the superblock at this point in time.
*/
-static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
- void *raw_data, int silent)
+static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb,
+ const char *dev_name, void *raw_data, int silent)
{
int err = 0;
int debug;
struct super_block *lower_sb;
struct path lower_path;
struct sdcardfs_sb_info *sb_info;
+ struct sdcardfs_vfsmount_options *mnt_opt = mnt->data;
struct inode *inode;
printk(KERN_INFO "sdcardfs version 2.0\n");
@@ -193,6 +253,7 @@
printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name);
printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data);
+ printk(KERN_INFO "sdcardfs: mnt -> %p\n", mnt);
/* parse lower path */
err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
@@ -212,7 +273,7 @@
sb_info = sb->s_fs_info;
/* parse options */
- err = parse_options(sb, raw_data, silent, &debug, &sb_info->options);
+ err = parse_options(sb, raw_data, silent, &debug, mnt_opt, &sb_info->options);
if (err) {
printk(KERN_ERR "sdcardfs: invalid options\n");
goto out_freesbi;
@@ -236,7 +297,7 @@
sb->s_op = &sdcardfs_sops;
/* get a new inode and allocate our root dentry */
- inode = sdcardfs_iget(sb, lower_path.dentry->d_inode, 0);
+ inode = sdcardfs_iget(sb, d_inode(lower_path.dentry), 0);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
goto out_sput;
@@ -268,16 +329,16 @@
sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL);
mutex_lock(&sdcardfs_super_list_lock);
if(sb_info->options.multiuser) {
- setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false);
+ setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root));
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name);
/*err = prepare_dir(sb_info->obbpath_s,
sb_info->options.fs_low_uid,
sb_info->options.fs_low_gid, 00755);*/
} else {
- setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false);
+ setup_derived_state(d_inode(sb->s_root), PERM_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root));
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
}
- fix_derived_permission(sb->s_root->d_inode);
+ fixup_tmp_permissions(d_inode(sb->s_root));
sb_info->sb = sb;
list_add(&sb_info->list, &sdcardfs_super_list);
mutex_unlock(&sdcardfs_super_list_lock);
@@ -306,9 +367,9 @@
}
/* A feature which supports mount_nodev() with options */
-static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data,
- int (*fill_super)(struct super_block *, const char *, void *, int))
+static struct dentry *mount_nodev_with_options(struct vfsmount *mnt,
+ struct file_system_type *fs_type, int flags, const char *dev_name, void *data,
+ int (*fill_super)(struct vfsmount *, struct super_block *, const char *, void *, int))
{
int error;
@@ -319,7 +380,7 @@
s->s_flags = flags;
- error = fill_super(s, dev_name, data, flags & MS_SILENT ? 1 : 0);
+ error = fill_super(mnt, s, dev_name, data, flags & MS_SILENT ? 1 : 0);
if (error) {
deactivate_locked_super(s);
return ERR_PTR(error);
@@ -328,15 +389,27 @@
return dget(s->s_root);
}
-struct dentry *sdcardfs_mount(struct file_system_type *fs_type, int flags,
+static struct dentry *sdcardfs_mount(struct vfsmount *mnt,
+ struct file_system_type *fs_type, int flags,
const char *dev_name, void *raw_data)
{
/*
* dev_name is a lower_path_name,
* raw_data is a option string.
*/
- return mount_nodev_with_options(fs_type, flags, dev_name,
- raw_data, sdcardfs_read_super);
+ return mount_nodev_with_options(mnt, fs_type, flags, dev_name,
+ raw_data, sdcardfs_read_super);
+}
+
+static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, int flags,
+ const char *dev_name, void *raw_data)
+{
+ WARN(1, "sdcardfs does not support mount. Use mount2.\n");
+ return ERR_PTR(-EINVAL);
+}
+
+void *sdcardfs_alloc_mnt_data(void) {
+ return kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
}
void sdcardfs_kill_sb(struct super_block *sb) {
@@ -353,7 +426,9 @@
static struct file_system_type sdcardfs_fs_type = {
.owner = THIS_MODULE,
.name = SDCARDFS_NAME,
- .mount = sdcardfs_mount,
+ .mount = sdcardfs_mount_wrn,
+ .mount2 = sdcardfs_mount,
+ .alloc_mnt_data = sdcardfs_alloc_mnt_data,
.kill_sb = sdcardfs_kill_sb,
.fs_flags = 0,
};
diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c
index 31e2145..03776fa 100644
--- a/fs/sdcardfs/packagelist.c
+++ b/fs/sdcardfs/packagelist.c
@@ -29,26 +29,13 @@
#include <linux/configfs.h>
-#define STRING_BUF_SIZE (512)
-
struct hashtable_entry {
struct hlist_node hlist;
- void *key;
- unsigned int value;
+ const char *key;
+ atomic_t value;
};
-struct sb_list {
- struct super_block *sb;
- struct list_head list;
-};
-
-struct packagelist_data {
- DECLARE_HASHTABLE(package_to_appid,8);
- struct mutex hashtable_lock;
-
-};
-
-static struct packagelist_data *pkgl_data_all;
+static DEFINE_HASHTABLE(package_to_appid, 8);
static struct kmem_cache *hashtable_entry_cachep;
@@ -64,22 +51,21 @@
return h;
}
-appid_t get_appid(void *pkgl_id, const char *app_name)
+appid_t get_appid(const char *app_name)
{
- struct packagelist_data *pkgl_dat = pkgl_data_all;
struct hashtable_entry *hash_cur;
unsigned int hash = str_hash(app_name);
appid_t ret_id;
- mutex_lock(&pkgl_dat->hashtable_lock);
- hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
+ rcu_read_lock();
+ hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) {
if (!strcasecmp(app_name, hash_cur->key)) {
- ret_id = (appid_t)hash_cur->value;
- mutex_unlock(&pkgl_dat->hashtable_lock);
+ ret_id = atomic_read(&hash_cur->value);
+ rcu_read_unlock();
return ret_id;
}
}
- mutex_unlock(&pkgl_dat->hashtable_lock);
+ rcu_read_unlock();
return 0;
}
@@ -120,116 +106,118 @@
}
}
-static int insert_str_to_int_lock(struct packagelist_data *pkgl_dat, char *key,
- unsigned int value)
+static struct hashtable_entry *alloc_packagelist_entry(const char *key,
+ appid_t value)
+{
+ struct hashtable_entry *ret = kmem_cache_alloc(hashtable_entry_cachep,
+ GFP_KERNEL);
+ if (!ret)
+ return NULL;
+
+ ret->key = kstrdup(key, GFP_KERNEL);
+ if (!ret->key) {
+ kmem_cache_free(hashtable_entry_cachep, ret);
+ return NULL;
+ }
+
+ atomic_set(&ret->value, value);
+ return ret;
+}
+
+static int insert_packagelist_entry_locked(const char *key, appid_t value)
{
struct hashtable_entry *hash_cur;
struct hashtable_entry *new_entry;
unsigned int hash = str_hash(key);
- hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
+ hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) {
if (!strcasecmp(key, hash_cur->key)) {
- hash_cur->value = value;
+ atomic_set(&hash_cur->value, value);
return 0;
}
}
- new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL);
+ new_entry = alloc_packagelist_entry(key, value);
if (!new_entry)
return -ENOMEM;
- new_entry->key = kstrdup(key, GFP_KERNEL);
- new_entry->value = value;
- hash_add(pkgl_dat->package_to_appid, &new_entry->hlist, hash);
+ hash_add_rcu(package_to_appid, &new_entry->hlist, hash);
return 0;
}
-static void fixup_perms(struct super_block *sb) {
+static void fixup_perms(struct super_block *sb, const char *key) {
if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) {
- inode_lock(sb->s_root->d_inode);
- get_derive_permissions_recursive(sb->s_root);
- inode_unlock(sb->s_root->d_inode);
+ fixup_perms_recursive(sb->s_root, key, strlen(key));
}
}
-static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key,
- unsigned int value) {
- int ret;
- struct sdcardfs_sb_info *sbinfo;
- mutex_lock(&sdcardfs_super_list_lock);
- mutex_lock(&pkgl_dat->hashtable_lock);
- ret = insert_str_to_int_lock(pkgl_dat, key, value);
- mutex_unlock(&pkgl_dat->hashtable_lock);
-
- list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
- if (sbinfo) {
- fixup_perms(sbinfo->sb);
- }
- }
- mutex_unlock(&sdcardfs_super_list_lock);
- return ret;
-}
-
-static void remove_str_to_int_lock(struct hashtable_entry *h_entry) {
- kfree(h_entry->key);
- hash_del(&h_entry->hlist);
- kmem_cache_free(hashtable_entry_cachep, h_entry);
-}
-
-static void remove_str_to_int(struct packagelist_data *pkgl_dat, const char *key)
+static void fixup_all_perms(const char *key)
{
struct sdcardfs_sb_info *sbinfo;
+ list_for_each_entry(sbinfo, &sdcardfs_super_list, list)
+ if (sbinfo)
+ fixup_perms(sbinfo->sb, key);
+}
+
+static int insert_packagelist_entry(const char *key, appid_t value)
+{
+ int err;
+
+ mutex_lock(&sdcardfs_super_list_lock);
+ err = insert_packagelist_entry_locked(key, value);
+ if (!err)
+ fixup_all_perms(key);
+ mutex_unlock(&sdcardfs_super_list_lock);
+
+ return err;
+}
+
+static void free_packagelist_entry(struct hashtable_entry *entry)
+{
+ kfree(entry->key);
+ hash_del_rcu(&entry->hlist);
+ kmem_cache_free(hashtable_entry_cachep, entry);
+}
+
+static void remove_packagelist_entry_locked(const char *key)
+{
struct hashtable_entry *hash_cur;
unsigned int hash = str_hash(key);
- mutex_lock(&sdcardfs_super_list_lock);
- mutex_lock(&pkgl_dat->hashtable_lock);
- hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
+
+ hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) {
if (!strcasecmp(key, hash_cur->key)) {
- remove_str_to_int_lock(hash_cur);
- break;
+ hash_del_rcu(&hash_cur->hlist);
+ synchronize_rcu();
+ free_packagelist_entry(hash_cur);
+ return;
}
}
- mutex_unlock(&pkgl_dat->hashtable_lock);
- list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
- if (sbinfo) {
- fixup_perms(sbinfo->sb);
- }
- }
+}
+
+static void remove_packagelist_entry(const char *key)
+{
+ mutex_lock(&sdcardfs_super_list_lock);
+ remove_packagelist_entry_locked(key);
+ fixup_all_perms(key);
mutex_unlock(&sdcardfs_super_list_lock);
return;
}
-static void remove_all_hashentrys(struct packagelist_data *pkgl_dat)
+static void packagelist_destroy(void)
{
struct hashtable_entry *hash_cur;
struct hlist_node *h_t;
+ HLIST_HEAD(free_list);
int i;
- mutex_lock(&pkgl_dat->hashtable_lock);
- hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist)
- remove_str_to_int_lock(hash_cur);
- mutex_unlock(&pkgl_dat->hashtable_lock);
- hash_init(pkgl_dat->package_to_appid);
-}
+ mutex_lock(&sdcardfs_super_list_lock);
+ hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) {
+ hash_del_rcu(&hash_cur->hlist);
+ hlist_add_head(&hash_cur->hlist, &free_list);
-static struct packagelist_data * packagelist_create(void)
-{
- struct packagelist_data *pkgl_dat;
-
- pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO);
- if (!pkgl_dat) {
- printk(KERN_ERR "sdcardfs: Failed to create hash\n");
- return ERR_PTR(-ENOMEM);
}
-
- mutex_init(&pkgl_dat->hashtable_lock);
- hash_init(pkgl_dat->package_to_appid);
-
- return pkgl_dat;
-}
-
-static void packagelist_destroy(struct packagelist_data *pkgl_dat)
-{
- remove_all_hashentrys(pkgl_dat);
+ synchronize_rcu();
+ hlist_for_each_entry_safe(hash_cur, h_t, &free_list, hlist)
+ free_packagelist_entry(hash_cur);
+ mutex_unlock(&sdcardfs_super_list_lock);
printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n");
- kfree(pkgl_dat);
}
struct package_appid {
@@ -245,26 +233,21 @@
static ssize_t package_appid_attr_show(struct config_item *item,
char *page)
{
- ssize_t count;
- count = sprintf(page, "%d\n", get_appid(pkgl_data_all, item->ci_name));
- return count;
+ return scnprintf(page, PAGE_SIZE, "%u\n", get_appid(item->ci_name));
}
static ssize_t package_appid_attr_store(struct config_item *item,
const char *page, size_t count)
{
struct package_appid *package_appid = to_package_appid(item);
- unsigned long tmp;
- char *p = (char *) page;
+ unsigned int tmp;
int ret;
- tmp = simple_strtoul(p, &p, 10);
- if (!p || (*p && (*p != '\n')))
- return -EINVAL;
+ ret = kstrtouint(page, 10, &tmp);
+ if (ret)
+ return ret;
- if (tmp > INT_MAX)
- return -ERANGE;
- ret = insert_str_to_int(pkgl_data_all, item->ci_name, (unsigned int)tmp);
+ ret = insert_packagelist_entry(item->ci_name, tmp);
package_appid->add_pid = tmp;
if (ret)
return ret;
@@ -289,7 +272,7 @@
{
printk(KERN_INFO "sdcardfs: removing %s\n", item->ci_dentry->d_name.name);
/* item->ci_name is freed already, so we rely on the dentry */
- remove_str_to_int(pkgl_data_all, item->ci_dentry->d_name.name);
+ remove_packagelist_entry(item->ci_dentry->d_name.name);
kfree(to_package_appid(item));
}
@@ -333,21 +316,21 @@
char *page)
{
struct hashtable_entry *hash_cur;
- struct hlist_node *h_t;
int i;
int count = 0, written = 0;
- char errormsg[] = "<truncated>\n";
+ const char errormsg[] = "<truncated>\n";
- mutex_lock(&pkgl_data_all->hashtable_lock);
- hash_for_each_safe(pkgl_data_all->package_to_appid, i, h_t, hash_cur, hlist) {
- written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n", (char *)hash_cur->key, hash_cur->value);
+ rcu_read_lock();
+ hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) {
+ written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n",
+ (const char *)hash_cur->key, atomic_read(&hash_cur->value));
if (count + written == PAGE_SIZE - sizeof(errormsg)) {
count += scnprintf(page + count, PAGE_SIZE - count, errormsg);
break;
}
count += written;
}
- mutex_unlock(&pkgl_data_all->hashtable_lock);
+ rcu_read_unlock();
return count;
}
@@ -430,7 +413,6 @@
return -ENOMEM;
}
- pkgl_data_all = packagelist_create();
configfs_sdcardfs_init();
return 0;
}
@@ -438,7 +420,7 @@
void packagelist_exit(void)
{
configfs_sdcardfs_exit();
- packagelist_destroy(pkgl_data_all);
+ packagelist_destroy();
if (hashtable_entry_cachep)
kmem_cache_destroy(hashtable_entry_cachep);
}
diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h
index 7cb14ac..66a97ef 100644
--- a/fs/sdcardfs/sdcardfs.h
+++ b/fs/sdcardfs/sdcardfs.h
@@ -68,14 +68,20 @@
#define AID_PACKAGE_INFO 1027
-#define fix_derived_permission(x) \
+
+/*
+ * Permissions are handled by our permission function.
+ * We don't want anyone who happens to look at our inode value to prematurely
+ * block access, so store more permissive values. These are probably never
+ * used.
+ */
+#define fixup_tmp_permissions(x) \
do { \
(x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \
- (x)->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(x))); \
- (x)->i_mode = ((x)->i_mode & S_IFMT) | get_mode(SDCARDFS_I(x));\
+ (x)->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); \
+ (x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\
} while (0)
-
/* OVERRIDE_CRED() and REVERT_CRED()
* OVERRID_CRED()
* backup original task->cred
@@ -169,6 +175,8 @@
userid_t userid;
uid_t d_uid;
bool under_android;
+ /* top folder for ownership */
+ struct inode *top;
struct inode vfs_inode;
};
@@ -185,12 +193,18 @@
uid_t fs_low_uid;
gid_t fs_low_gid;
userid_t fs_user_id;
- gid_t gid;
- mode_t mask;
bool multiuser;
unsigned int reserved_mb;
};
+struct sdcardfs_vfsmount_options {
+ gid_t gid;
+ mode_t mask;
+};
+
+extern int parse_options_remount(struct super_block *sb, char *options, int silent,
+ struct sdcardfs_vfsmount_options *vfsopts);
+
/* sdcardfs super-block data in memory */
struct sdcardfs_sb_info {
struct super_block *sb;
@@ -321,9 +335,39 @@
SDCARDFS_DENT_FUNC(lower_path)
SDCARDFS_DENT_FUNC(orig_path)
-static inline int get_gid(struct sdcardfs_inode_info *info) {
- struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb);
- if (sb_info->options.gid == AID_SDCARD_RW) {
+/* grab a refererence if we aren't linking to ourself */
+static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top)
+{
+ struct inode *old_top = NULL;
+ BUG_ON(IS_ERR_OR_NULL(top));
+ if (info->top && info->top != &info->vfs_inode) {
+ old_top = info->top;
+ }
+ if (top != &info->vfs_inode)
+ igrab(top);
+ info->top = top;
+ iput(old_top);
+}
+
+static inline struct inode *grab_top(struct sdcardfs_inode_info *info)
+{
+ struct inode *top = info->top;
+ if (top) {
+ return igrab(top);
+ } else {
+ return NULL;
+ }
+}
+
+static inline void release_top(struct sdcardfs_inode_info *info)
+{
+ iput(info->top);
+}
+
+static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) {
+ struct sdcardfs_vfsmount_options *opts = mnt->data;
+
+ if (opts->gid == AID_SDCARD_RW) {
/* As an optimization, certain trusted system components only run
* as owner but operate across all users. Since we're now handing
* out the sdcard_rw GID only to trusted apps, we're okay relaxing
@@ -331,14 +375,15 @@
* assigned to app directories are still multiuser aware. */
return AID_SDCARD_RW;
} else {
- return multiuser_get_uid(info->userid, sb_info->options.gid);
+ return multiuser_get_uid(info->userid, opts->gid);
}
}
-static inline int get_mode(struct sdcardfs_inode_info *info) {
+static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) {
int owner_mode;
int filtered_mode;
- struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb);
- int visible_mode = 0775 & ~sb_info->options.mask;
+ struct sdcardfs_vfsmount_options *opts = mnt->data;
+ int visible_mode = 0775 & ~opts->mask;
+
if (info->perm == PERM_PRE_ROOT) {
/* Top of multi-user view should always be visible to ensure
@@ -348,7 +393,7 @@
/* Block "other" access to Android directories, since only apps
* belonging to a specific user should be in there; we still
* leave +x open for the default view. */
- if (sb_info->options.gid == AID_SDCARD_RW) {
+ if (opts->gid == AID_SDCARD_RW) {
visible_mode = visible_mode & ~0006;
} else {
visible_mode = visible_mode & ~0007;
@@ -396,18 +441,19 @@
extern struct list_head sdcardfs_super_list;
/* for packagelist.c */
-extern appid_t get_appid(void *pkgl_id, const char *app_name);
+extern appid_t get_appid(const char *app_name);
extern int check_caller_access_to_name(struct inode *parent_node, const char* name);
extern int open_flags_to_access_mode(int open_flags);
extern int packagelist_init(void);
extern void packagelist_exit(void);
/* for derived_perm.c */
-extern void setup_derived_state(struct inode *inode, perm_t perm,
- userid_t userid, uid_t uid, bool under_android);
+extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
+ uid_t uid, bool under_android, struct inode *top);
extern void get_derived_permission(struct dentry *parent, struct dentry *dentry);
extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry);
-extern void get_derive_permissions_recursive(struct dentry *parent);
+extern void fixup_top_recursive(struct dentry *parent);
+extern void fixup_perms_recursive(struct dentry *dentry, const char *name, size_t len);
extern void update_derived_permission_lock(struct dentry *dentry);
extern int need_graft_path(struct dentry *dentry);
@@ -444,7 +490,7 @@
goto out_unlock;
}
- err = vfs_mkdir(d_inode(parent.dentry), dent, mode);
+ err = vfs_mkdir2(parent.mnt, d_inode(parent.dentry), dent, mode);
if (err) {
if (err == -EEXIST)
err = 0;
@@ -455,7 +501,7 @@
attrs.ia_gid = make_kgid(&init_user_ns, gid);
attrs.ia_valid = ATTR_UID | ATTR_GID;
inode_lock(d_inode(dent));
- notify_change(dent, &attrs, NULL);
+ notify_change2(parent.mnt, dent, &attrs, NULL);
inode_unlock(d_inode(dent));
out_dput:
@@ -513,12 +559,16 @@
return 1;
}
-/* Copies attrs and maintains sdcardfs managed attrs */
+/*
+ * Copies attrs and maintains sdcardfs managed attrs
+ * Since our permission check handles all special permissions, set those to be open
+ */
static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct inode *src)
{
- dest->i_mode = (src->i_mode & S_IFMT) | get_mode(SDCARDFS_I(dest));
+ dest->i_mode = (src->i_mode & S_IFMT) | S_IRWXU | S_IRWXG |
+ S_IROTH | S_IXOTH; /* 0775 */
dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid);
- dest->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(dest)));
+ dest->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW);
dest->i_rdev = src->i_rdev;
dest->i_atime = src->i_atime;
dest->i_mtime = src->i_mtime;
diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c
index 1d64901..edda32b 100644
--- a/fs/sdcardfs/super.c
+++ b/fs/sdcardfs/super.c
@@ -109,6 +109,50 @@
}
/*
+ * @mnt: mount point we are remounting
+ * @sb: superblock we are remounting
+ * @flags: numeric mount options
+ * @options: mount options string
+ */
+static int sdcardfs_remount_fs2(struct vfsmount *mnt, struct super_block *sb,
+ int *flags, char *options)
+{
+ int err = 0;
+
+ /*
+ * The VFS will take care of "ro" and "rw" flags among others. We
+ * can safely accept a few flags (RDONLY, MANDLOCK), and honor
+ * SILENT, but anything else left over is an error.
+ */
+ if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT | MS_REMOUNT)) != 0) {
+ printk(KERN_ERR
+ "sdcardfs: remount flags 0x%x unsupported\n", *flags);
+ err = -EINVAL;
+ }
+ printk(KERN_INFO "Remount options were %s for vfsmnt %p.\n", options, mnt);
+ err = parse_options_remount(sb, options, *flags & ~MS_SILENT, mnt->data);
+
+
+ return err;
+}
+
+static void* sdcardfs_clone_mnt_data(void *data) {
+ struct sdcardfs_vfsmount_options* opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
+ struct sdcardfs_vfsmount_options* old = data;
+ if(!opt) return NULL;
+ opt->gid = old->gid;
+ opt->mask = old->mask;
+ return opt;
+}
+
+static void sdcardfs_copy_mnt_data(void *data, void *newdata) {
+ struct sdcardfs_vfsmount_options* old = data;
+ struct sdcardfs_vfsmount_options* new = newdata;
+ old->gid = new->gid;
+ old->mask = new->mask;
+}
+
+/*
* Called by iput() when the inode reference count reached zero
* and the inode is not hashed anywhere. Used to clear anything
* that needs to be, before the inode is completely destroyed and put
@@ -126,6 +170,7 @@
*/
lower_inode = sdcardfs_lower_inode(inode);
sdcardfs_set_lower_inode(inode, NULL);
+ set_top(SDCARDFS_I(inode), inode);
iput(lower_inode);
}
@@ -190,19 +235,24 @@
lower_sb->s_op->umount_begin(lower_sb);
}
-static int sdcardfs_show_options(struct seq_file *m, struct dentry *root)
+static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, struct dentry *root)
{
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb);
struct sdcardfs_mount_options *opts = &sbi->options;
+ struct sdcardfs_vfsmount_options *vfsopts = mnt->data;
if (opts->fs_low_uid != 0)
- seq_printf(m, ",uid=%u", opts->fs_low_uid);
+ seq_printf(m, ",fsuid=%u", opts->fs_low_uid);
if (opts->fs_low_gid != 0)
- seq_printf(m, ",gid=%u", opts->fs_low_gid);
-
+ seq_printf(m, ",fsgid=%u", opts->fs_low_gid);
+ if (vfsopts->gid != 0)
+ seq_printf(m, ",gid=%u", vfsopts->gid);
if (opts->multiuser)
seq_printf(m, ",multiuser");
-
+ if (vfsopts->mask)
+ seq_printf(m, ",mask=%u", vfsopts->mask);
+ if (opts->fs_user_id)
+ seq_printf(m, ",userid=%u", opts->fs_user_id);
if (opts->reserved_mb != 0)
seq_printf(m, ",reserved=%uMB", opts->reserved_mb);
@@ -213,9 +263,12 @@
.put_super = sdcardfs_put_super,
.statfs = sdcardfs_statfs,
.remount_fs = sdcardfs_remount_fs,
+ .remount_fs2 = sdcardfs_remount_fs2,
+ .clone_mnt_data = sdcardfs_clone_mnt_data,
+ .copy_mnt_data = sdcardfs_copy_mnt_data,
.evict_inode = sdcardfs_evict_inode,
.umount_begin = sdcardfs_umount_begin,
- .show_options = sdcardfs_show_options,
+ .show_options2 = sdcardfs_show_options,
.alloc_inode = sdcardfs_alloc_inode,
.destroy_inode = sdcardfs_destroy_inode,
.drop_inode = generic_delete_inode,
diff --git a/fs/splice.c b/fs/splice.c
index 5a7750b..63b8f54 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -1086,7 +1086,13 @@
static int wait_for_space(struct pipe_inode_info *pipe, unsigned flags)
{
- while (pipe->nrbufs == pipe->buffers) {
+ for (;;) {
+ if (unlikely(!pipe->readers)) {
+ send_sig(SIGPIPE, current, 0);
+ return -EPIPE;
+ }
+ if (pipe->nrbufs != pipe->buffers)
+ return 0;
if (flags & SPLICE_F_NONBLOCK)
return -EAGAIN;
if (signal_pending(current))
@@ -1095,7 +1101,6 @@
pipe_wait(pipe);
pipe->waiting_writers--;
}
- return 0;
}
static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe,
diff --git a/fs/super.c b/fs/super.c
index 0bed501..719579f 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -750,7 +750,8 @@
}
/**
- * do_remount_sb - asks filesystem to change mount options.
+ * do_remount_sb2 - asks filesystem to change mount options.
+ * @mnt: mount we are looking at
* @sb: superblock in question
* @flags: numeric part of options
* @data: the rest of options
@@ -758,7 +759,7 @@
*
* Alters the mount options of a mounted file system.
*/
-int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
+int do_remount_sb2(struct vfsmount *mnt, struct super_block *sb, int flags, void *data, int force)
{
int retval;
int remount_ro;
@@ -800,7 +801,16 @@
}
}
- if (sb->s_op->remount_fs) {
+ if (mnt && sb->s_op->remount_fs2) {
+ retval = sb->s_op->remount_fs2(mnt, sb, &flags, data);
+ if (retval) {
+ if (!force)
+ goto cancel_readonly;
+ /* If forced remount, go ahead despite any errors */
+ WARN(1, "forced remount of a %s fs returned %i\n",
+ sb->s_type->name, retval);
+ }
+ } else if (sb->s_op->remount_fs) {
retval = sb->s_op->remount_fs(sb, &flags, data);
if (retval) {
if (!force)
@@ -832,6 +842,11 @@
return retval;
}
+int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
+{
+ return do_remount_sb2(NULL, sb, flags, data, force);
+}
+
static void do_emergency_remount(struct work_struct *work)
{
struct super_block *sb, *p = NULL;
@@ -1157,7 +1172,7 @@
EXPORT_SYMBOL(mount_single);
struct dentry *
-mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
+mount_fs(struct file_system_type *type, int flags, const char *name, struct vfsmount *mnt, void *data)
{
struct dentry *root;
struct super_block *sb;
@@ -1174,7 +1189,10 @@
goto out_free_secdata;
}
- root = type->mount(type, flags, name, data);
+ if (type->mount2)
+ root = type->mount2(mnt, type, flags, name, data);
+ else
+ root = type->mount(type, flags, name, data);
if (IS_ERR(root)) {
error = PTR_ERR(root);
goto out_free_secdata;
diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c
index fa9a20c..fe5e8d4 100644
--- a/fs/ubifs/tnc.c
+++ b/fs/ubifs/tnc.c
@@ -34,6 +34,11 @@
#include <linux/slab.h>
#include "ubifs.h"
+static int try_read_node(const struct ubifs_info *c, void *buf, int type,
+ int len, int lnum, int offs);
+static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key,
+ struct ubifs_zbranch *zbr, void *node);
+
/*
* Returned codes of 'matches_name()' and 'fallible_matches_name()' functions.
* @NAME_LESS: name corresponding to the first argument is less than second
@@ -402,7 +407,19 @@
return 0;
}
- err = ubifs_tnc_read_node(c, zbr, node);
+ if (c->replaying) {
+ err = fallible_read_node(c, &zbr->key, zbr, node);
+ /*
+ * When the node was not found, return -ENOENT, 0 otherwise.
+ * Negative return codes stay as-is.
+ */
+ if (err == 0)
+ err = -ENOENT;
+ else if (err == 1)
+ err = 0;
+ } else {
+ err = ubifs_tnc_read_node(c, zbr, node);
+ }
if (err)
return err;
@@ -2766,7 +2783,11 @@
if (nm->name) {
if (err) {
/* Handle collisions */
- err = resolve_collision(c, key, &znode, &n, nm);
+ if (c->replaying)
+ err = fallible_resolve_collision(c, key, &znode, &n,
+ nm, 0);
+ else
+ err = resolve_collision(c, key, &znode, &n, nm);
dbg_tnc("rc returned %d, znode %p, n %d",
err, znode, n);
if (unlikely(err < 0))
diff --git a/fs/utimes.c b/fs/utimes.c
index 22307cd..87ce37b 100644
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -91,7 +91,7 @@
}
retry_deleg:
inode_lock(inode);
- error = notify_change(path->dentry, &newattrs, &delegated_inode);
+ error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
inode_unlock(inode);
if (delegated_inode) {
error = break_deleg_wait(&delegated_inode);
diff --git a/fs/xfs/libxfs/xfs_ag_resv.c b/fs/xfs/libxfs/xfs_ag_resv.c
index e5ebc37..d346d42 100644
--- a/fs/xfs/libxfs/xfs_ag_resv.c
+++ b/fs/xfs/libxfs/xfs_ag_resv.c
@@ -256,6 +256,9 @@
goto out;
}
+ ASSERT(xfs_perag_resv(pag, XFS_AG_RESV_METADATA)->ar_reserved +
+ xfs_perag_resv(pag, XFS_AG_RESV_AGFL)->ar_reserved <=
+ pag->pagf_freeblks + pag->pagf_flcount);
out:
return error;
}
diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index effb64c..9f06a21 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -95,10 +95,7 @@
xfs_alloc_set_aside(
struct xfs_mount *mp)
{
- unsigned int blocks;
-
- blocks = 4 + (mp->m_sb.sb_agcount * XFS_ALLOC_AGFL_RESERVE);
- return blocks;
+ return mp->m_sb.sb_agcount * (XFS_ALLOC_AGFL_RESERVE + 4);
}
/*
@@ -365,36 +362,12 @@
return;
ASSERT(rlen >= args->minlen && rlen <= args->maxlen);
ASSERT(rlen % args->prod == args->mod);
+ ASSERT(args->pag->pagf_freeblks + args->pag->pagf_flcount >=
+ rlen + args->minleft);
args->len = rlen;
}
/*
- * Fix up length if there is too little space left in the a.g.
- * Return 1 if ok, 0 if too little, should give up.
- */
-STATIC int
-xfs_alloc_fix_minleft(
- xfs_alloc_arg_t *args) /* allocation argument structure */
-{
- xfs_agf_t *agf; /* a.g. freelist header */
- int diff; /* free space difference */
-
- if (args->minleft == 0)
- return 1;
- agf = XFS_BUF_TO_AGF(args->agbp);
- diff = be32_to_cpu(agf->agf_freeblks)
- - args->len - args->minleft;
- if (diff >= 0)
- return 1;
- args->len += diff; /* shrink the allocated space */
- /* casts to (int) catch length underflows */
- if ((int)args->len >= (int)args->minlen)
- return 1;
- args->agbno = NULLAGBLOCK;
- return 0;
-}
-
-/*
* Update the two btrees, logically removing from freespace the extent
* starting at rbno, rlen blocks. The extent is contained within the
* actual (current) free extent fbno for flen blocks.
@@ -689,8 +662,6 @@
xfs_alloc_arg_t *args) /* argument structure for allocation */
{
int error=0;
- xfs_extlen_t reservation;
- xfs_extlen_t oldmax;
ASSERT(args->minlen > 0);
ASSERT(args->maxlen > 0);
@@ -699,20 +670,6 @@
ASSERT(args->alignment > 0);
/*
- * Clamp maxlen to the amount of free space minus any reservations
- * that have been made.
- */
- oldmax = args->maxlen;
- reservation = xfs_ag_resv_needed(args->pag, args->resv);
- if (args->maxlen > args->pag->pagf_freeblks - reservation)
- args->maxlen = args->pag->pagf_freeblks - reservation;
- if (args->maxlen == 0) {
- args->agbno = NULLAGBLOCK;
- args->maxlen = oldmax;
- return 0;
- }
-
- /*
* Branch to correct routine based on the type.
*/
args->wasfromfl = 0;
@@ -731,8 +688,6 @@
/* NOTREACHED */
}
- args->maxlen = oldmax;
-
if (error || args->agbno == NULLAGBLOCK)
return error;
@@ -841,9 +796,6 @@
args->len = XFS_AGBLOCK_MIN(tend, args->agbno + args->maxlen)
- args->agbno;
xfs_alloc_fix_len(args);
- if (!xfs_alloc_fix_minleft(args))
- goto not_found;
-
ASSERT(args->agbno + args->len <= tend);
/*
@@ -1149,12 +1101,7 @@
XFS_WANT_CORRUPTED_GOTO(args->mp, i == 1, error0);
ASSERT(ltbno + ltlen <= be32_to_cpu(XFS_BUF_TO_AGF(args->agbp)->agf_length));
args->len = blen;
- if (!xfs_alloc_fix_minleft(args)) {
- xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR);
- trace_xfs_alloc_near_nominleft(args);
- return 0;
- }
- blen = args->len;
+
/*
* We are allocating starting at bnew for blen blocks.
*/
@@ -1346,12 +1293,6 @@
*/
args->len = XFS_EXTLEN_MIN(ltlena, args->maxlen);
xfs_alloc_fix_len(args);
- if (!xfs_alloc_fix_minleft(args)) {
- trace_xfs_alloc_near_nominleft(args);
- xfs_btree_del_cursor(bno_cur_lt, XFS_BTREE_NOERROR);
- xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR);
- return 0;
- }
rlen = args->len;
(void)xfs_alloc_compute_diff(args->agbno, rlen, args->alignment,
args->datatype, ltbnoa, ltlena, <new);
@@ -1553,8 +1494,6 @@
}
xfs_alloc_fix_len(args);
- if (!xfs_alloc_fix_minleft(args))
- goto out_nominleft;
rlen = args->len;
XFS_WANT_CORRUPTED_GOTO(args->mp, rlen <= flen, error0);
/*
@@ -2056,7 +1995,7 @@
int flags)
{
struct xfs_perag *pag = args->pag;
- xfs_extlen_t longest;
+ xfs_extlen_t alloc_len, longest;
xfs_extlen_t reservation; /* blocks that are still reserved */
int available;
@@ -2066,17 +2005,28 @@
reservation = xfs_ag_resv_needed(pag, args->resv);
/* do we have enough contiguous free space for the allocation? */
+ alloc_len = args->minlen + (args->alignment - 1) + args->minalignslop;
longest = xfs_alloc_longest_free_extent(args->mp, pag, min_free,
reservation);
- if ((args->minlen + args->alignment + args->minalignslop - 1) > longest)
+ if (longest < alloc_len)
return false;
/* do we have enough free space remaining for the allocation? */
available = (int)(pag->pagf_freeblks + pag->pagf_flcount -
- reservation - min_free - args->total);
- if (available < (int)args->minleft || available <= 0)
+ reservation - min_free - args->minleft);
+ if (available < (int)max(args->total, alloc_len))
return false;
+ /*
+ * Clamp maxlen to the amount of free space available for the actual
+ * extent allocation.
+ */
+ if (available < (int)args->maxlen && !(flags & XFS_ALLOC_FLAG_CHECK)) {
+ args->maxlen = available;
+ ASSERT(args->maxlen > 0);
+ ASSERT(args->maxlen >= args->minlen);
+ }
+
return true;
}
@@ -2122,7 +2072,8 @@
}
need = xfs_alloc_min_freelist(mp, pag);
- if (!xfs_alloc_space_available(args, need, flags))
+ if (!xfs_alloc_space_available(args, need, flags |
+ XFS_ALLOC_FLAG_CHECK))
goto out_agbp_relse;
/*
@@ -2455,12 +2406,15 @@
be32_to_cpu(agf->agf_flcount) <= XFS_AGFL_SIZE(mp)))
return false;
- if (be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) > XFS_BTREE_MAXLEVELS ||
+ if (be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) < 1 ||
+ be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]) < 1 ||
+ be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) > XFS_BTREE_MAXLEVELS ||
be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]) > XFS_BTREE_MAXLEVELS)
return false;
if (xfs_sb_version_hasrmapbt(&mp->m_sb) &&
- be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]) > XFS_BTREE_MAXLEVELS)
+ (be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]) < 1 ||
+ be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]) > XFS_BTREE_MAXLEVELS))
return false;
/*
@@ -2477,7 +2431,8 @@
return false;
if (xfs_sb_version_hasreflink(&mp->m_sb) &&
- be32_to_cpu(agf->agf_refcount_level) > XFS_BTREE_MAXLEVELS)
+ (be32_to_cpu(agf->agf_refcount_level) < 1 ||
+ be32_to_cpu(agf->agf_refcount_level) > XFS_BTREE_MAXLEVELS))
return false;
return true;;
@@ -2634,12 +2589,10 @@
xfs_agblock_t agsize; /* allocation group size */
int error;
int flags; /* XFS_ALLOC_FLAG_... locking flags */
- xfs_extlen_t minleft;/* minimum left value, temp copy */
xfs_mount_t *mp; /* mount structure pointer */
xfs_agnumber_t sagno; /* starting allocation group number */
xfs_alloctype_t type; /* input allocation type */
int bump_rotor = 0;
- int no_min = 0;
xfs_agnumber_t rotorstep = xfs_rotorstep; /* inode32 agf stepper */
mp = args->mp;
@@ -2668,7 +2621,6 @@
trace_xfs_alloc_vextent_badargs(args);
return 0;
}
- minleft = args->minleft;
switch (type) {
case XFS_ALLOCTYPE_THIS_AG:
@@ -2679,9 +2631,7 @@
*/
args->agno = XFS_FSB_TO_AGNO(mp, args->fsbno);
args->pag = xfs_perag_get(mp, args->agno);
- args->minleft = 0;
error = xfs_alloc_fix_freelist(args, 0);
- args->minleft = minleft;
if (error) {
trace_xfs_alloc_vextent_nofix(args);
goto error0;
@@ -2746,9 +2696,7 @@
*/
for (;;) {
args->pag = xfs_perag_get(mp, args->agno);
- if (no_min) args->minleft = 0;
error = xfs_alloc_fix_freelist(args, flags);
- args->minleft = minleft;
if (error) {
trace_xfs_alloc_vextent_nofix(args);
goto error0;
@@ -2788,20 +2736,17 @@
* or switch to non-trylock mode.
*/
if (args->agno == sagno) {
- if (no_min == 1) {
+ if (flags == 0) {
args->agbno = NULLAGBLOCK;
trace_xfs_alloc_vextent_allfailed(args);
break;
}
- if (flags == 0) {
- no_min = 1;
- } else {
- flags = 0;
- if (type == XFS_ALLOCTYPE_START_BNO) {
- args->agbno = XFS_FSB_TO_AGBNO(mp,
- args->fsbno);
- args->type = XFS_ALLOCTYPE_NEAR_BNO;
- }
+
+ flags = 0;
+ if (type == XFS_ALLOCTYPE_START_BNO) {
+ args->agbno = XFS_FSB_TO_AGBNO(mp,
+ args->fsbno);
+ args->type = XFS_ALLOCTYPE_NEAR_BNO;
}
}
xfs_perag_put(args->pag);
diff --git a/fs/xfs/libxfs/xfs_alloc.h b/fs/xfs/libxfs/xfs_alloc.h
index 7c404a6..1d0f48a 100644
--- a/fs/xfs/libxfs/xfs_alloc.h
+++ b/fs/xfs/libxfs/xfs_alloc.h
@@ -56,7 +56,7 @@
#define XFS_ALLOC_FLAG_FREEING 0x00000002 /* indicate caller is freeing extents*/
#define XFS_ALLOC_FLAG_NORMAP 0x00000004 /* don't modify the rmapbt */
#define XFS_ALLOC_FLAG_NOSHRINK 0x00000008 /* don't shrink the freelist */
-
+#define XFS_ALLOC_FLAG_CHECK 0x00000010 /* test only, don't modify args */
/*
* Argument structure for xfs_alloc routines.
diff --git a/fs/xfs/libxfs/xfs_alloc_btree.c b/fs/xfs/libxfs/xfs_alloc_btree.c
index 5ba2dac..c06ec77 100644
--- a/fs/xfs/libxfs/xfs_alloc_btree.c
+++ b/fs/xfs/libxfs/xfs_alloc_btree.c
@@ -421,7 +421,7 @@
ASSERT(btnum == XFS_BTNUM_BNO || btnum == XFS_BTNUM_CNT);
- cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_SLEEP);
+ cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_NOFS);
cur->bc_tp = tp;
cur->bc_mp = mp;
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index af1ecb1..6622d46 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -131,9 +131,6 @@
if (XFS_FORCED_SHUTDOWN(ip->i_mount))
return -EIO;
- if (!xfs_inode_hasattr(ip))
- return -ENOATTR;
-
error = xfs_attr_args_init(&args, ip, name, flags);
if (error)
return error;
@@ -392,9 +389,6 @@
if (XFS_FORCED_SHUTDOWN(dp->i_mount))
return -EIO;
- if (!xfs_inode_hasattr(dp))
- return -ENOATTR;
-
error = xfs_attr_args_init(&args, dp, name, flags);
if (error)
return error;
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
index 8ea91f3..2852521 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.c
+++ b/fs/xfs/libxfs/xfs_attr_leaf.c
@@ -253,6 +253,7 @@
{
struct xfs_mount *mp = bp->b_target->bt_mount;
struct xfs_attr_leafblock *leaf = bp->b_addr;
+ struct xfs_perag *pag = bp->b_pag;
struct xfs_attr3_icleaf_hdr ichdr;
xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr, leaf);
@@ -273,7 +274,12 @@
if (ichdr.magic != XFS_ATTR_LEAF_MAGIC)
return false;
}
- if (ichdr.count == 0)
+ /*
+ * In recovery there is a transient state where count == 0 is valid
+ * because we may have transitioned an empty shortform attr to a leaf
+ * if the attr didn't fit in shortform.
+ */
+ if (pag && pag->pagf_init && ichdr.count == 0)
return false;
/* XXX: need to range check rest of attr header values */
diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index c6eb219..f52fd63 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -49,6 +49,8 @@
#include "xfs_rmap.h"
#include "xfs_ag_resv.h"
#include "xfs_refcount.h"
+#include "xfs_rmap_btree.h"
+#include "xfs_icache.h"
kmem_zone_t *xfs_bmap_free_item_zone;
@@ -190,8 +192,12 @@
int maxrecs; /* maximum record count at this level */
xfs_mount_t *mp; /* mount structure */
xfs_filblks_t rval; /* return value */
+ xfs_filblks_t orig_len;
mp = ip->i_mount;
+
+ /* Calculate the worst-case size of the bmbt. */
+ orig_len = len;
maxrecs = mp->m_bmap_dmxr[0];
for (level = 0, rval = 0;
level < XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK);
@@ -199,12 +205,20 @@
len += maxrecs - 1;
do_div(len, maxrecs);
rval += len;
- if (len == 1)
- return rval + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) -
+ if (len == 1) {
+ rval += XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) -
level - 1;
+ break;
+ }
if (level == 0)
maxrecs = mp->m_bmap_dmxr[1];
}
+
+ /* Calculate the worst-case size of the rmapbt. */
+ if (xfs_sb_version_hasrmapbt(&mp->m_sb))
+ rval += 1 + xfs_rmapbt_calc_size(mp, orig_len) +
+ mp->m_rmap_maxlevels;
+
return rval;
}
@@ -504,7 +518,7 @@
xfs_bmap_trace_exlist(
xfs_inode_t *ip, /* incore inode pointer */
xfs_extnum_t cnt, /* count of entries in the list */
- int whichfork, /* data or attr fork */
+ int whichfork, /* data or attr or cow fork */
unsigned long caller_ip)
{
xfs_extnum_t idx; /* extent record index */
@@ -513,11 +527,13 @@
if (whichfork == XFS_ATTR_FORK)
state |= BMAP_ATTRFORK;
+ else if (whichfork == XFS_COW_FORK)
+ state |= BMAP_COWFORK;
ifp = XFS_IFORK_PTR(ip, whichfork);
- ASSERT(cnt == (ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t)));
+ ASSERT(cnt == xfs_iext_count(ifp));
for (idx = 0; idx < cnt; idx++)
- trace_xfs_extlist(ip, idx, whichfork, caller_ip);
+ trace_xfs_extlist(ip, idx, state, caller_ip);
}
/*
@@ -811,7 +827,7 @@
XFS_BTREE_LONG_PTRS);
arp = XFS_BMBT_REC_ADDR(mp, ablock, 1);
- nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ nextents = xfs_iext_count(ifp);
for (cnt = i = 0; i < nextents; i++) {
ep = xfs_iext_get_ext(ifp, i);
if (!isnullstartblock(xfs_bmbt_get_startblock(ep))) {
@@ -1137,6 +1153,10 @@
goto trans_cancel;
if (XFS_IFORK_Q(ip))
goto trans_cancel;
+ if (ip->i_d.di_anextents != 0) {
+ error = -EFSCORRUPTED;
+ goto trans_cancel;
+ }
if (ip->i_d.di_aformat != XFS_DINODE_FMT_EXTENTS) {
/*
* For inodes coming from pre-6.2 filesystems.
@@ -1144,7 +1164,6 @@
ASSERT(ip->i_d.di_aformat == 0);
ip->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
}
- ASSERT(ip->i_d.di_anextents == 0);
xfs_trans_ijoin(tp, ip, 0);
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
@@ -1296,7 +1315,7 @@
/*
* Here with bp and block set to the leftmost leaf node in the tree.
*/
- room = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ room = xfs_iext_count(ifp);
i = 0;
/*
* Loop over all leaf nodes. Copy information to the extent records.
@@ -1361,8 +1380,9 @@
return error;
block = XFS_BUF_TO_BLOCK(bp);
}
- ASSERT(i == (ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t)));
- ASSERT(i == XFS_IFORK_NEXTENTS(ip, whichfork));
+ if (i != XFS_IFORK_NEXTENTS(ip, whichfork))
+ return -EFSCORRUPTED;
+ ASSERT(i == xfs_iext_count(ifp));
XFS_BMAP_TRACE_EXLIST(ip, i, whichfork);
return 0;
error0:
@@ -1404,7 +1424,7 @@
if (lastx > 0) {
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, lastx - 1), prevp);
}
- if (lastx < (ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t))) {
+ if (lastx < xfs_iext_count(ifp)) {
xfs_bmbt_get_all(ep, gotp);
*eofp = 0;
} else {
@@ -1497,7 +1517,7 @@
(error = xfs_iread_extents(tp, ip, whichfork)))
return error;
lowest = *first_unused;
- nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ nextents = xfs_iext_count(ifp);
for (idx = 0, lastaddr = 0, max = lowest; idx < nextents; idx++) {
xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, idx);
off = xfs_bmbt_get_startoff(ep);
@@ -1582,7 +1602,7 @@
return error;
}
- nextents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t);
+ nextents = xfs_iext_count(ifp);
if (nextents == 0) {
*is_empty = 1;
return 0;
@@ -1735,7 +1755,7 @@
&bma->ip->i_d.di_nextents);
ASSERT(bma->idx >= 0);
- ASSERT(bma->idx <= ifp->if_bytes / sizeof(struct xfs_bmbt_rec));
+ ASSERT(bma->idx <= xfs_iext_count(ifp));
ASSERT(!isnullstartblock(new->br_startblock));
ASSERT(!bma->cur ||
(bma->cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL));
@@ -1794,7 +1814,7 @@
* Don't set contiguous if the combined extent would be too large.
* Also check for all-three-contiguous being too large.
*/
- if (bma->idx < ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t) - 1) {
+ if (bma->idx < xfs_iext_count(ifp) - 1) {
state |= BMAP_RIGHT_VALID;
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, bma->idx + 1), &RIGHT);
@@ -2300,7 +2320,7 @@
ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
ASSERT(*idx >= 0);
- ASSERT(*idx <= ifp->if_bytes / sizeof(struct xfs_bmbt_rec));
+ ASSERT(*idx <= xfs_iext_count(ifp));
ASSERT(!isnullstartblock(new->br_startblock));
XFS_STATS_INC(mp, xs_add_exlist);
@@ -2356,7 +2376,7 @@
* Don't set contiguous if the combined extent would be too large.
* Also check for all-three-contiguous being too large.
*/
- if (*idx < ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t) - 1) {
+ if (*idx < xfs_iext_count(&ip->i_df) - 1) {
state |= BMAP_RIGHT_VALID;
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx + 1), &RIGHT);
if (isnullstartblock(RIGHT.br_startblock))
@@ -2836,7 +2856,7 @@
* Check and set flags if the current (right) segment exists.
* If it doesn't exist, we're converting the hole at end-of-file.
*/
- if (*idx < ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t)) {
+ if (*idx < xfs_iext_count(ifp)) {
state |= BMAP_RIGHT_VALID;
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx), &right);
@@ -2966,7 +2986,7 @@
ifp = XFS_IFORK_PTR(bma->ip, whichfork);
ASSERT(bma->idx >= 0);
- ASSERT(bma->idx <= ifp->if_bytes / sizeof(struct xfs_bmbt_rec));
+ ASSERT(bma->idx <= xfs_iext_count(ifp));
ASSERT(!isnullstartblock(new->br_startblock));
ASSERT(!bma->cur ||
!(bma->cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL));
@@ -2992,7 +3012,7 @@
* Check and set flags if this segment has a current value.
* Not true if we're inserting into the "hole" at eof.
*/
- if (bma->idx < ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t)) {
+ if (bma->idx < xfs_iext_count(ifp)) {
state |= BMAP_RIGHT_VALID;
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, bma->idx), &right);
if (isnullstartblock(right.br_startblock))
@@ -3700,7 +3720,7 @@
align = xfs_get_cowextsz_hint(ap->ip);
else if (xfs_alloc_is_userdata(ap->datatype))
align = xfs_get_extsz_hint(ap->ip);
- if (unlikely(align)) {
+ if (align) {
error = xfs_bmap_extsize_align(mp, &ap->got, &ap->prev,
align, 0, ap->eof, 0, ap->conv,
&ap->offset, &ap->length);
@@ -3772,7 +3792,7 @@
args.minlen = ap->minlen;
}
/* apply extent size hints if obtained earlier */
- if (unlikely(align)) {
+ if (align) {
args.prod = align;
if ((args.mod = (xfs_extlen_t)do_mod(ap->offset, args.prod)))
args.mod = (xfs_extlen_t)(args.prod - args.mod);
@@ -3883,7 +3903,6 @@
args.fsbno = 0;
args.type = XFS_ALLOCTYPE_FIRST_AG;
args.total = ap->minlen;
- args.minleft = 0;
if ((error = xfs_alloc_vextent(&args)))
return error;
ap->dfops->dop_low = true;
@@ -4221,7 +4240,7 @@
break;
/* Else go on to the next record. */
- if (++lastx < ifp->if_bytes / sizeof(xfs_bmbt_rec_t))
+ if (++lastx < xfs_iext_count(ifp))
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, lastx), &got);
else
eof = 1;
@@ -4234,10 +4253,10 @@
xfs_bmapi_reserve_delalloc(
struct xfs_inode *ip,
int whichfork,
- xfs_fileoff_t aoff,
+ xfs_fileoff_t off,
xfs_filblks_t len,
+ xfs_filblks_t prealloc,
struct xfs_bmbt_irec *got,
- struct xfs_bmbt_irec *prev,
xfs_extnum_t *lastx,
int eof)
{
@@ -4248,10 +4267,17 @@
char rt = XFS_IS_REALTIME_INODE(ip);
xfs_extlen_t extsz;
int error;
+ xfs_fileoff_t aoff = off;
- alen = XFS_FILBLKS_MIN(len, MAXEXTLEN);
+ /*
+ * Cap the alloc length. Keep track of prealloc so we know whether to
+ * tag the inode before we return.
+ */
+ alen = XFS_FILBLKS_MIN(len + prealloc, MAXEXTLEN);
if (!eof)
alen = XFS_FILBLKS_MIN(alen, got->br_startoff - aoff);
+ if (prealloc && alen >= len)
+ prealloc = alen - len;
/* Figure out the extent size, adjust alen */
if (whichfork == XFS_COW_FORK)
@@ -4259,7 +4285,12 @@
else
extsz = xfs_get_extsz_hint(ip);
if (extsz) {
- error = xfs_bmap_extsize_align(mp, got, prev, extsz, rt, eof,
+ struct xfs_bmbt_irec prev;
+
+ if (!xfs_iext_get_extent(ifp, *lastx - 1, &prev))
+ prev.br_startoff = NULLFILEOFF;
+
+ error = xfs_bmap_extsize_align(mp, got, &prev, extsz, rt, eof,
1, 0, &aoff, &alen);
ASSERT(!error);
}
@@ -4312,6 +4343,16 @@
*/
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *lastx), got);
+ /*
+ * Tag the inode if blocks were preallocated. Note that COW fork
+ * preallocation can occur at the start or end of the extent, even when
+ * prealloc == 0, so we must also check the aligned offset and length.
+ */
+ if (whichfork == XFS_DATA_FORK && prealloc)
+ xfs_inode_set_eofblocks_tag(ip);
+ if (whichfork == XFS_COW_FORK && (prealloc || aoff < off || alen > len))
+ xfs_inode_set_cowblocks_tag(ip);
+
ASSERT(got->br_startoff <= aoff);
ASSERT(got->br_startoff + got->br_blockcount >= aoff + alen);
ASSERT(isnullstartblock(got->br_startblock));
@@ -4395,8 +4436,6 @@
if (error)
return error;
- if (bma->dfops->dop_low)
- bma->minleft = 0;
if (bma->cur)
bma->cur->bc_private.b.firstblock = *bma->firstblock;
if (bma->blkno == NULLFSBLOCK)
@@ -4568,8 +4607,6 @@
int n; /* current extent index */
xfs_fileoff_t obno; /* old block number (offset) */
int whichfork; /* data or attr fork */
- char inhole; /* current location is hole in file */
- char wasdelay; /* old extent was delayed */
#ifdef DEBUG
xfs_fileoff_t orig_bno; /* original block number value */
@@ -4655,22 +4692,44 @@
bma.firstblock = firstblock;
while (bno < end && n < *nmap) {
- inhole = eof || bma.got.br_startoff > bno;
- wasdelay = !inhole && isnullstartblock(bma.got.br_startblock);
+ bool need_alloc = false, wasdelay = false;
- /*
- * Make sure we only reflink into a hole.
- */
- if (flags & XFS_BMAPI_REMAP)
- ASSERT(inhole);
- if (flags & XFS_BMAPI_COWFORK)
- ASSERT(!inhole);
+ /* in hole or beyoned EOF? */
+ if (eof || bma.got.br_startoff > bno) {
+ if (flags & XFS_BMAPI_DELALLOC) {
+ /*
+ * For the COW fork we can reasonably get a
+ * request for converting an extent that races
+ * with other threads already having converted
+ * part of it, as there converting COW to
+ * regular blocks is not protected using the
+ * IOLOCK.
+ */
+ ASSERT(flags & XFS_BMAPI_COWFORK);
+ if (!(flags & XFS_BMAPI_COWFORK)) {
+ error = -EIO;
+ goto error0;
+ }
+
+ if (eof || bno >= end)
+ break;
+ } else {
+ need_alloc = true;
+ }
+ } else {
+ /*
+ * Make sure we only reflink into a hole.
+ */
+ ASSERT(!(flags & XFS_BMAPI_REMAP));
+ if (isnullstartblock(bma.got.br_startblock))
+ wasdelay = true;
+ }
/*
* First, deal with the hole before the allocated space
* that we found, if any.
*/
- if (inhole || wasdelay) {
+ if (need_alloc || wasdelay) {
bma.eof = eof;
bma.conv = !!(flags & XFS_BMAPI_CONVERT);
bma.wasdel = wasdelay;
@@ -4733,7 +4792,7 @@
/* Else go on to the next record. */
bma.prev = bma.got;
- if (++bma.idx < ifp->if_bytes / sizeof(xfs_bmbt_rec_t)) {
+ if (++bma.idx < xfs_iext_count(ifp)) {
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, bma.idx),
&bma.got);
} else
@@ -4885,7 +4944,7 @@
da_new = 0;
ASSERT(*idx >= 0);
- ASSERT(*idx < ifp->if_bytes / sizeof(struct xfs_bmbt_rec));
+ ASSERT(*idx <= xfs_iext_count(ifp));
ASSERT(del->br_blockcount > 0);
ASSERT(got->br_startoff <= del->br_startoff);
ASSERT(got_endoff >= del_endoff);
@@ -4902,8 +4961,11 @@
* sb counters as we might have to borrow some blocks for the
* indirect block accounting.
*/
- xfs_trans_reserve_quota_nblks(NULL, ip, -((long)del->br_blockcount), 0,
+ error = xfs_trans_reserve_quota_nblks(NULL, ip,
+ -((long)del->br_blockcount), 0,
isrt ? XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS);
+ if (error)
+ return error;
ip->i_delayed_blks -= del->br_blockcount;
if (whichfork == XFS_COW_FORK)
@@ -5013,7 +5075,7 @@
got_endoff = got->br_startoff + got->br_blockcount;
ASSERT(*idx >= 0);
- ASSERT(*idx < ifp->if_bytes / sizeof(struct xfs_bmbt_rec));
+ ASSERT(*idx <= xfs_iext_count(ifp));
ASSERT(del->br_blockcount > 0);
ASSERT(got->br_startoff <= del->br_startoff);
ASSERT(got_endoff >= del_endoff);
@@ -5119,8 +5181,7 @@
state |= BMAP_COWFORK;
ifp = XFS_IFORK_PTR(ip, whichfork);
- ASSERT((*idx >= 0) && (*idx < ifp->if_bytes /
- (uint)sizeof(xfs_bmbt_rec_t)));
+ ASSERT((*idx >= 0) && (*idx < xfs_iext_count(ifp)));
ASSERT(del->br_blockcount > 0);
ep = xfs_iext_get_ext(ifp, *idx);
xfs_bmbt_get_all(ep, &got);
@@ -5445,7 +5506,6 @@
int logflags; /* transaction logging flags */
xfs_extlen_t mod; /* rt extent offset */
xfs_mount_t *mp; /* mount structure */
- xfs_extnum_t nextents; /* number of file extents */
xfs_bmbt_irec_t prev; /* previous extent record */
xfs_fileoff_t start; /* first file offset deleted */
int tmp_logflags; /* partial logging flags */
@@ -5477,8 +5537,7 @@
if (!(ifp->if_flags & XFS_IFEXTENTS) &&
(error = xfs_iread_extents(tp, ip, whichfork)))
return error;
- nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
- if (nextents == 0) {
+ if (xfs_iext_count(ifp) == 0) {
*rlen = 0;
return 0;
}
@@ -5963,7 +6022,7 @@
mp = ip->i_mount;
ifp = XFS_IFORK_PTR(ip, whichfork);
- total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t);
+ total_extents = xfs_iext_count(ifp);
xfs_bmbt_get_all(gotp, &got);
@@ -6140,7 +6199,7 @@
* are collapsing out, so we cannot use the count of real extents here.
* Instead we have to calculate it from the incore fork.
*/
- total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t);
+ total_extents = xfs_iext_count(ifp);
if (total_extents == 0) {
*done = 1;
goto del_cursor;
@@ -6200,7 +6259,7 @@
* count can change. Update the total and grade the next record.
*/
if (direction == SHIFT_LEFT) {
- total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t);
+ total_extents = xfs_iext_count(ifp);
stop_extent = total_extents;
}
diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
index 7cae6ec..e7d40b3 100644
--- a/fs/xfs/libxfs/xfs_bmap.h
+++ b/fs/xfs/libxfs/xfs_bmap.h
@@ -110,6 +110,9 @@
/* Map something in the CoW fork. */
#define XFS_BMAPI_COWFORK 0x200
+/* Only convert delalloc space, don't allocate entirely new extents */
+#define XFS_BMAPI_DELALLOC 0x400
+
#define XFS_BMAPI_FLAGS \
{ XFS_BMAPI_ENTIRE, "ENTIRE" }, \
{ XFS_BMAPI_METADATA, "METADATA" }, \
@@ -120,7 +123,8 @@
{ XFS_BMAPI_CONVERT, "CONVERT" }, \
{ XFS_BMAPI_ZERO, "ZERO" }, \
{ XFS_BMAPI_REMAP, "REMAP" }, \
- { XFS_BMAPI_COWFORK, "COWFORK" }
+ { XFS_BMAPI_COWFORK, "COWFORK" }, \
+ { XFS_BMAPI_DELALLOC, "DELALLOC" }
static inline int xfs_bmapi_aflag(int w)
@@ -242,9 +246,8 @@
int fork, int *eofp, xfs_extnum_t *lastxp,
struct xfs_bmbt_irec *gotp, struct xfs_bmbt_irec *prevp);
int xfs_bmapi_reserve_delalloc(struct xfs_inode *ip, int whichfork,
- xfs_fileoff_t aoff, xfs_filblks_t len,
- struct xfs_bmbt_irec *got, struct xfs_bmbt_irec *prev,
- xfs_extnum_t *lastx, int eof);
+ xfs_fileoff_t off, xfs_filblks_t len, xfs_filblks_t prealloc,
+ struct xfs_bmbt_irec *got, xfs_extnum_t *lastx, int eof);
enum xfs_bmap_intent_type {
XFS_BMAP_MAP = 1,
diff --git a/fs/xfs/libxfs/xfs_bmap_btree.c b/fs/xfs/libxfs/xfs_bmap_btree.c
index 8007d2b..f76c169 100644
--- a/fs/xfs/libxfs/xfs_bmap_btree.c
+++ b/fs/xfs/libxfs/xfs_bmap_btree.c
@@ -502,12 +502,11 @@
if (args.fsbno == NULLFSBLOCK && args.minleft) {
/*
* Could not find an AG with enough free space to satisfy
- * a full btree split. Try again without minleft and if
+ * a full btree split. Try again and if
* successful activate the lowspace algorithm.
*/
args.fsbno = 0;
args.type = XFS_ALLOCTYPE_FIRST_AG;
- args.minleft = 0;
error = xfs_alloc_vextent(&args);
if (error)
goto error0;
@@ -796,7 +795,7 @@
struct xfs_btree_cur *cur;
ASSERT(whichfork != XFS_COW_FORK);
- cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_SLEEP);
+ cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_NOFS);
cur->bc_tp = tp;
cur->bc_mp = mp;
diff --git a/fs/xfs/libxfs/xfs_btree.c b/fs/xfs/libxfs/xfs_btree.c
index 0e80993..21e6a6a 100644
--- a/fs/xfs/libxfs/xfs_btree.c
+++ b/fs/xfs/libxfs/xfs_btree.c
@@ -1769,8 +1769,28 @@
if (error)
return error;
+ /* Check the inode owner since the verifiers don't. */
+ if (xfs_sb_version_hascrc(&cur->bc_mp->m_sb) &&
+ (cur->bc_flags & XFS_BTREE_LONG_PTRS) &&
+ be64_to_cpu((*blkp)->bb_u.l.bb_owner) !=
+ cur->bc_private.b.ip->i_ino)
+ goto out_bad;
+
+ /* Did we get the level we were looking for? */
+ if (be16_to_cpu((*blkp)->bb_level) != level)
+ goto out_bad;
+
+ /* Check that internal nodes have at least one record. */
+ if (level != 0 && be16_to_cpu((*blkp)->bb_numrecs) == 0)
+ goto out_bad;
+
xfs_btree_setbuf(cur, level, bp);
return 0;
+
+out_bad:
+ *blkp = NULL;
+ xfs_trans_brelse(cur->bc_tp, bp);
+ return -EFSCORRUPTED;
}
/*
diff --git a/fs/xfs/libxfs/xfs_dir2.c b/fs/xfs/libxfs/xfs_dir2.c
index 20a96dd..7825d78 100644
--- a/fs/xfs/libxfs/xfs_dir2.c
+++ b/fs/xfs/libxfs/xfs_dir2.c
@@ -36,21 +36,29 @@
struct xfs_name xfs_name_dotdot = { (unsigned char *)"..", 2, XFS_DIR3_FT_DIR };
/*
- * @mode, if set, indicates that the type field needs to be set up.
- * This uses the transformation from file mode to DT_* as defined in linux/fs.h
- * for file type specification. This will be propagated into the directory
- * structure if appropriate for the given operation and filesystem config.
+ * Convert inode mode to directory entry filetype
*/
-const unsigned char xfs_mode_to_ftype[S_IFMT >> S_SHIFT] = {
- [0] = XFS_DIR3_FT_UNKNOWN,
- [S_IFREG >> S_SHIFT] = XFS_DIR3_FT_REG_FILE,
- [S_IFDIR >> S_SHIFT] = XFS_DIR3_FT_DIR,
- [S_IFCHR >> S_SHIFT] = XFS_DIR3_FT_CHRDEV,
- [S_IFBLK >> S_SHIFT] = XFS_DIR3_FT_BLKDEV,
- [S_IFIFO >> S_SHIFT] = XFS_DIR3_FT_FIFO,
- [S_IFSOCK >> S_SHIFT] = XFS_DIR3_FT_SOCK,
- [S_IFLNK >> S_SHIFT] = XFS_DIR3_FT_SYMLINK,
-};
+unsigned char xfs_mode_to_ftype(int mode)
+{
+ switch (mode & S_IFMT) {
+ case S_IFREG:
+ return XFS_DIR3_FT_REG_FILE;
+ case S_IFDIR:
+ return XFS_DIR3_FT_DIR;
+ case S_IFCHR:
+ return XFS_DIR3_FT_CHRDEV;
+ case S_IFBLK:
+ return XFS_DIR3_FT_BLKDEV;
+ case S_IFIFO:
+ return XFS_DIR3_FT_FIFO;
+ case S_IFSOCK:
+ return XFS_DIR3_FT_SOCK;
+ case S_IFLNK:
+ return XFS_DIR3_FT_SYMLINK;
+ default:
+ return XFS_DIR3_FT_UNKNOWN;
+ }
+}
/*
* ASCII case-insensitive (ie. A-Z) support for directories that was
@@ -631,7 +639,8 @@
if ((rval = xfs_bmap_last_offset(args->dp, &last, XFS_DATA_FORK)))
return rval;
rval = XFS_FSB_TO_B(args->dp->i_mount, last) == args->geo->blksize;
- ASSERT(rval == 0 || args->dp->i_d.di_size == args->geo->blksize);
+ if (rval != 0 && args->dp->i_d.di_size != args->geo->blksize)
+ return -EFSCORRUPTED;
*vp = rval;
return 0;
}
diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
index becc926..ae0d55b 100644
--- a/fs/xfs/libxfs/xfs_dir2.h
+++ b/fs/xfs/libxfs/xfs_dir2.h
@@ -18,6 +18,9 @@
#ifndef __XFS_DIR2_H__
#define __XFS_DIR2_H__
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
+
struct xfs_defer_ops;
struct xfs_da_args;
struct xfs_inode;
@@ -32,10 +35,9 @@
extern struct xfs_name xfs_name_dotdot;
/*
- * directory filetype conversion tables.
+ * Convert inode mode to directory entry filetype
*/
-#define S_SHIFT 12
-extern const unsigned char xfs_mode_to_ftype[];
+extern unsigned char xfs_mode_to_ftype(int mode);
/*
* directory operations vector for encode/decode routines
diff --git a/fs/xfs/libxfs/xfs_dir2_data.c b/fs/xfs/libxfs/xfs_dir2_data.c
index 725fc78..e526f5a 100644
--- a/fs/xfs/libxfs/xfs_dir2_data.c
+++ b/fs/xfs/libxfs/xfs_dir2_data.c
@@ -329,7 +329,7 @@
err = xfs_da_read_buf(tp, dp, bno, mapped_bno, bpp,
XFS_DATA_FORK, &xfs_dir3_data_buf_ops);
- if (!err && tp)
+ if (!err && tp && *bpp)
xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_DATA_BUF);
return err;
}
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c
index 51b4e0d..d45c037 100644
--- a/fs/xfs/libxfs/xfs_ialloc.c
+++ b/fs/xfs/libxfs/xfs_ialloc.c
@@ -2450,8 +2450,6 @@
ASSERT(agi->agi_magicnum == cpu_to_be32(XFS_AGI_MAGIC));
#endif
- xfs_trans_buf_set_type(tp, bp, XFS_BLFT_AGI_BUF);
-
/*
* Compute byte offsets for the first and last fields in the first
* region and log the agi buffer. This only logs up through
@@ -2512,8 +2510,15 @@
if (!XFS_AGI_GOOD_VERSION(be32_to_cpu(agi->agi_versionnum)))
return false;
- if (be32_to_cpu(agi->agi_level) > XFS_BTREE_MAXLEVELS)
+ if (be32_to_cpu(agi->agi_level) < 1 ||
+ be32_to_cpu(agi->agi_level) > XFS_BTREE_MAXLEVELS)
return false;
+
+ if (xfs_sb_version_hasfinobt(&mp->m_sb) &&
+ (be32_to_cpu(agi->agi_free_level) < 1 ||
+ be32_to_cpu(agi->agi_free_level) > XFS_BTREE_MAXLEVELS))
+ return false;
+
/*
* during growfs operations, the perag is not fully initialised,
* so we can't use it for any useful checking. growfs ensures we can't
@@ -2592,6 +2597,8 @@
XFS_FSS_TO_BB(mp, 1), 0, bpp, &xfs_agi_buf_ops);
if (error)
return error;
+ if (tp)
+ xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_AGI_BUF);
xfs_buf_set_ref(*bpp, XFS_AGI_REF);
return 0;
diff --git a/fs/xfs/libxfs/xfs_ialloc_btree.c b/fs/xfs/libxfs/xfs_ialloc_btree.c
index eab68ae..6c6b959 100644
--- a/fs/xfs/libxfs/xfs_ialloc_btree.c
+++ b/fs/xfs/libxfs/xfs_ialloc_btree.c
@@ -357,7 +357,7 @@
struct xfs_agi *agi = XFS_BUF_TO_AGI(agbp);
struct xfs_btree_cur *cur;
- cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_SLEEP);
+ cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_NOFS);
cur->bc_tp = tp;
cur->bc_mp = mp;
diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index 134424f..37ee7f0 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -29,6 +29,7 @@
#include "xfs_icache.h"
#include "xfs_trans.h"
#include "xfs_ialloc.h"
+#include "xfs_dir2.h"
/*
* Check that none of the inode's in the buffer have a next
@@ -386,12 +387,25 @@
struct xfs_inode *ip,
struct xfs_dinode *dip)
{
+ uint16_t mode;
uint16_t flags;
uint64_t flags2;
if (dip->di_magic != cpu_to_be16(XFS_DINODE_MAGIC))
return false;
+ /* don't allow invalid i_size */
+ if (be64_to_cpu(dip->di_size) & (1ULL << 63))
+ return false;
+
+ mode = be16_to_cpu(dip->di_mode);
+ if (mode && xfs_mode_to_ftype(mode) == XFS_DIR3_FT_UNKNOWN)
+ return false;
+
+ /* No zero-length symlinks/dirs. */
+ if ((S_ISLNK(mode) || S_ISDIR(mode)) && dip->di_size == 0)
+ return false;
+
/* only version 3 or greater inodes are extensively verified here */
if (dip->di_version < 3)
return true;
diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
index 5dd56d3..222e103 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.c
+++ b/fs/xfs/libxfs/xfs_inode_fork.c
@@ -775,6 +775,13 @@
}
}
+/* Count number of incore extents based on if_bytes */
+xfs_extnum_t
+xfs_iext_count(struct xfs_ifork *ifp)
+{
+ return ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+}
+
/*
* Convert in-core extents to on-disk form
*
@@ -803,7 +810,7 @@
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED));
ASSERT(ifp->if_bytes > 0);
- nrecs = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ nrecs = xfs_iext_count(ifp);
XFS_BMAP_TRACE_EXLIST(ip, nrecs, whichfork);
ASSERT(nrecs > 0);
@@ -941,7 +948,7 @@
xfs_extnum_t idx) /* index of target extent */
{
ASSERT(idx >= 0);
- ASSERT(idx < ifp->if_bytes / sizeof(xfs_bmbt_rec_t));
+ ASSERT(idx < xfs_iext_count(ifp));
if ((ifp->if_flags & XFS_IFEXTIREC) && (idx == 0)) {
return ifp->if_u1.if_ext_irec->er_extbuf;
@@ -1017,7 +1024,7 @@
int new_size; /* size of extents after adding */
xfs_extnum_t nextents; /* number of extents in file */
- nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ nextents = xfs_iext_count(ifp);
ASSERT((idx >= 0) && (idx <= nextents));
byte_diff = ext_diff * sizeof(xfs_bmbt_rec_t);
new_size = ifp->if_bytes + byte_diff;
@@ -1241,7 +1248,7 @@
trace_xfs_iext_remove(ip, idx, state, _RET_IP_);
ASSERT(ext_diff > 0);
- nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ nextents = xfs_iext_count(ifp);
new_size = (nextents - ext_diff) * sizeof(xfs_bmbt_rec_t);
if (new_size == 0) {
@@ -1270,7 +1277,7 @@
ASSERT(!(ifp->if_flags & XFS_IFEXTIREC));
ASSERT(idx < XFS_INLINE_EXTS);
- nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ nextents = xfs_iext_count(ifp);
ASSERT(((nextents - ext_diff) > 0) &&
(nextents - ext_diff) < XFS_INLINE_EXTS);
@@ -1309,7 +1316,7 @@
ASSERT(!(ifp->if_flags & XFS_IFEXTIREC));
new_size = ifp->if_bytes -
(ext_diff * sizeof(xfs_bmbt_rec_t));
- nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ nextents = xfs_iext_count(ifp);
if (new_size == 0) {
xfs_iext_destroy(ifp);
@@ -1546,7 +1553,7 @@
int size; /* size of file extents */
ASSERT(ifp->if_flags & XFS_IFEXTIREC);
- nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ nextents = xfs_iext_count(ifp);
ASSERT(nextents <= XFS_LINEAR_EXTS);
size = nextents * sizeof(xfs_bmbt_rec_t);
@@ -1620,7 +1627,7 @@
xfs_extnum_t nextents; /* number of file extents */
xfs_fileoff_t startoff = 0; /* start offset of extent */
- nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ nextents = xfs_iext_count(ifp);
if (nextents == 0) {
*idxp = 0;
return NULL;
@@ -1733,8 +1740,8 @@
ASSERT(ifp->if_flags & XFS_IFEXTIREC);
ASSERT(page_idx >= 0);
- ASSERT(page_idx <= ifp->if_bytes / sizeof(xfs_bmbt_rec_t));
- ASSERT(page_idx < ifp->if_bytes / sizeof(xfs_bmbt_rec_t) || realloc);
+ ASSERT(page_idx <= xfs_iext_count(ifp));
+ ASSERT(page_idx < xfs_iext_count(ifp) || realloc);
nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
erp_idx = 0;
@@ -1782,7 +1789,7 @@
xfs_extnum_t nextents; /* number of extents in file */
ASSERT(!(ifp->if_flags & XFS_IFEXTIREC));
- nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ nextents = xfs_iext_count(ifp);
ASSERT(nextents <= XFS_LINEAR_EXTS);
erp = kmem_alloc(sizeof(xfs_ext_irec_t), KM_NOFS);
@@ -1906,7 +1913,7 @@
ASSERT(ifp->if_flags & XFS_IFEXTIREC);
nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
- nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ nextents = xfs_iext_count(ifp);
if (nextents == 0) {
xfs_iext_destroy(ifp);
@@ -1996,3 +2003,49 @@
ip->i_cformat = XFS_DINODE_FMT_EXTENTS;
ip->i_cnextents = 0;
}
+
+/*
+ * Lookup the extent covering bno.
+ *
+ * If there is an extent covering bno return the extent index, and store the
+ * expanded extent structure in *gotp, and the extent index in *idx.
+ * If there is no extent covering bno, but there is an extent after it (e.g.
+ * it lies in a hole) return that extent in *gotp and its index in *idx
+ * instead.
+ * If bno is beyond the last extent return false, and return the index after
+ * the last valid index in *idxp.
+ */
+bool
+xfs_iext_lookup_extent(
+ struct xfs_inode *ip,
+ struct xfs_ifork *ifp,
+ xfs_fileoff_t bno,
+ xfs_extnum_t *idxp,
+ struct xfs_bmbt_irec *gotp)
+{
+ struct xfs_bmbt_rec_host *ep;
+
+ XFS_STATS_INC(ip->i_mount, xs_look_exlist);
+
+ ep = xfs_iext_bno_to_ext(ifp, bno, idxp);
+ if (!ep)
+ return false;
+ xfs_bmbt_get_all(ep, gotp);
+ return true;
+}
+
+/*
+ * Return true if there is an extent at index idx, and return the expanded
+ * extent structure at idx in that case. Else return false.
+ */
+bool
+xfs_iext_get_extent(
+ struct xfs_ifork *ifp,
+ xfs_extnum_t idx,
+ struct xfs_bmbt_irec *gotp)
+{
+ if (idx < 0 || idx >= xfs_iext_count(ifp))
+ return false;
+ xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx), gotp);
+ return true;
+}
diff --git a/fs/xfs/libxfs/xfs_inode_fork.h b/fs/xfs/libxfs/xfs_inode_fork.h
index c9476f5..7fb8365 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.h
+++ b/fs/xfs/libxfs/xfs_inode_fork.h
@@ -152,6 +152,7 @@
struct xfs_bmbt_rec_host *
xfs_iext_get_ext(struct xfs_ifork *, xfs_extnum_t);
+xfs_extnum_t xfs_iext_count(struct xfs_ifork *);
void xfs_iext_insert(struct xfs_inode *, xfs_extnum_t, xfs_extnum_t,
struct xfs_bmbt_irec *, int);
void xfs_iext_add(struct xfs_ifork *, xfs_extnum_t, int);
@@ -181,6 +182,12 @@
void xfs_iext_irec_compact_full(struct xfs_ifork *);
void xfs_iext_irec_update_extoffs(struct xfs_ifork *, int, int);
+bool xfs_iext_lookup_extent(struct xfs_inode *ip,
+ struct xfs_ifork *ifp, xfs_fileoff_t bno,
+ xfs_extnum_t *idxp, struct xfs_bmbt_irec *gotp);
+bool xfs_iext_get_extent(struct xfs_ifork *ifp, xfs_extnum_t idx,
+ struct xfs_bmbt_irec *gotp);
+
extern struct kmem_zone *xfs_ifork_zone;
extern void xfs_ifork_init_cow(struct xfs_inode *ip);
diff --git a/fs/xfs/libxfs/xfs_refcount_btree.c b/fs/xfs/libxfs/xfs_refcount_btree.c
index 453bb27..2ba2169 100644
--- a/fs/xfs/libxfs/xfs_refcount_btree.c
+++ b/fs/xfs/libxfs/xfs_refcount_btree.c
@@ -408,13 +408,14 @@
*/
xfs_extlen_t
xfs_refcountbt_max_size(
- struct xfs_mount *mp)
+ struct xfs_mount *mp,
+ xfs_agblock_t agblocks)
{
/* Bail out if we're uninitialized, which can happen in mkfs. */
if (mp->m_refc_mxr[0] == 0)
return 0;
- return xfs_refcountbt_calc_size(mp, mp->m_sb.sb_agblocks);
+ return xfs_refcountbt_calc_size(mp, agblocks);
}
/*
@@ -429,22 +430,24 @@
{
struct xfs_buf *agbp;
struct xfs_agf *agf;
+ xfs_agblock_t agblocks;
xfs_extlen_t tree_len;
int error;
if (!xfs_sb_version_hasreflink(&mp->m_sb))
return 0;
- *ask += xfs_refcountbt_max_size(mp);
error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
if (error)
return error;
agf = XFS_BUF_TO_AGF(agbp);
+ agblocks = be32_to_cpu(agf->agf_length);
tree_len = be32_to_cpu(agf->agf_refcount_blocks);
xfs_buf_relse(agbp);
+ *ask += xfs_refcountbt_max_size(mp, agblocks);
*used += tree_len;
return error;
diff --git a/fs/xfs/libxfs/xfs_refcount_btree.h b/fs/xfs/libxfs/xfs_refcount_btree.h
index 3be7768..9db008b 100644
--- a/fs/xfs/libxfs/xfs_refcount_btree.h
+++ b/fs/xfs/libxfs/xfs_refcount_btree.h
@@ -66,7 +66,8 @@
extern xfs_extlen_t xfs_refcountbt_calc_size(struct xfs_mount *mp,
unsigned long long len);
-extern xfs_extlen_t xfs_refcountbt_max_size(struct xfs_mount *mp);
+extern xfs_extlen_t xfs_refcountbt_max_size(struct xfs_mount *mp,
+ xfs_agblock_t agblocks);
extern int xfs_refcountbt_calc_reserves(struct xfs_mount *mp,
xfs_agnumber_t agno, xfs_extlen_t *ask, xfs_extlen_t *used);
diff --git a/fs/xfs/libxfs/xfs_rmap_btree.c b/fs/xfs/libxfs/xfs_rmap_btree.c
index 83e672f..33a28ef 100644
--- a/fs/xfs/libxfs/xfs_rmap_btree.c
+++ b/fs/xfs/libxfs/xfs_rmap_btree.c
@@ -549,13 +549,14 @@
*/
xfs_extlen_t
xfs_rmapbt_max_size(
- struct xfs_mount *mp)
+ struct xfs_mount *mp,
+ xfs_agblock_t agblocks)
{
/* Bail out if we're uninitialized, which can happen in mkfs. */
if (mp->m_rmap_mxr[0] == 0)
return 0;
- return xfs_rmapbt_calc_size(mp, mp->m_sb.sb_agblocks);
+ return xfs_rmapbt_calc_size(mp, agblocks);
}
/*
@@ -570,25 +571,24 @@
{
struct xfs_buf *agbp;
struct xfs_agf *agf;
- xfs_extlen_t pool_len;
+ xfs_agblock_t agblocks;
xfs_extlen_t tree_len;
int error;
if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
return 0;
- /* Reserve 1% of the AG or enough for 1 block per record. */
- pool_len = max(mp->m_sb.sb_agblocks / 100, xfs_rmapbt_max_size(mp));
- *ask += pool_len;
-
error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
if (error)
return error;
agf = XFS_BUF_TO_AGF(agbp);
+ agblocks = be32_to_cpu(agf->agf_length);
tree_len = be32_to_cpu(agf->agf_rmap_blocks);
xfs_buf_relse(agbp);
+ /* Reserve 1% of the AG or enough for 1 block per record. */
+ *ask += max(agblocks / 100, xfs_rmapbt_max_size(mp, agblocks));
*used += tree_len;
return error;
diff --git a/fs/xfs/libxfs/xfs_rmap_btree.h b/fs/xfs/libxfs/xfs_rmap_btree.h
index 2a9ac47..19c08e9 100644
--- a/fs/xfs/libxfs/xfs_rmap_btree.h
+++ b/fs/xfs/libxfs/xfs_rmap_btree.h
@@ -60,7 +60,8 @@
extern xfs_extlen_t xfs_rmapbt_calc_size(struct xfs_mount *mp,
unsigned long long len);
-extern xfs_extlen_t xfs_rmapbt_max_size(struct xfs_mount *mp);
+extern xfs_extlen_t xfs_rmapbt_max_size(struct xfs_mount *mp,
+ xfs_agblock_t agblocks);
extern int xfs_rmapbt_calc_reserves(struct xfs_mount *mp,
xfs_agnumber_t agno, xfs_extlen_t *ask, xfs_extlen_t *used);
diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c
index a70aec9..584ec89 100644
--- a/fs/xfs/libxfs/xfs_sb.c
+++ b/fs/xfs/libxfs/xfs_sb.c
@@ -242,7 +242,7 @@
sbp->sb_blocklog < XFS_MIN_BLOCKSIZE_LOG ||
sbp->sb_blocklog > XFS_MAX_BLOCKSIZE_LOG ||
sbp->sb_blocksize != (1 << sbp->sb_blocklog) ||
- sbp->sb_dirblklog > XFS_MAX_BLOCKSIZE_LOG ||
+ sbp->sb_dirblklog + sbp->sb_blocklog > XFS_MAX_BLOCKSIZE_LOG ||
sbp->sb_inodesize < XFS_DINODE_MIN_SIZE ||
sbp->sb_inodesize > XFS_DINODE_MAX_SIZE ||
sbp->sb_inodelog < XFS_DINODE_MIN_LOG ||
@@ -262,6 +262,12 @@
return -EFSCORRUPTED;
}
+ if (xfs_sb_version_hascrc(&mp->m_sb) &&
+ sbp->sb_blocksize < XFS_MIN_CRC_BLOCKSIZE) {
+ xfs_notice(mp, "v5 SB sanity check failed");
+ return -EFSCORRUPTED;
+ }
+
/*
* Until this is fixed only page-sized or smaller data blocks work.
*/
@@ -338,13 +344,16 @@
XFS_PQUOTA_CHKD : XFS_GQUOTA_CHKD;
sbp->sb_qflags &= ~(XFS_OQUOTA_ENFD | XFS_OQUOTA_CHKD);
- if (sbp->sb_qflags & XFS_PQUOTA_ACCT) {
+ if (sbp->sb_qflags & XFS_PQUOTA_ACCT &&
+ sbp->sb_gquotino != NULLFSINO) {
/*
* In older version of superblock, on-disk superblock only
* has sb_gquotino, and in-core superblock has both sb_gquotino
* and sb_pquotino. But, only one of them is supported at any
* point of time. So, if PQUOTA is set in disk superblock,
- * copy over sb_gquotino to sb_pquotino.
+ * copy over sb_gquotino to sb_pquotino. The NULLFSINO test
+ * above is to make sure we don't do this twice and wipe them
+ * both out!
*/
sbp->sb_pquotino = sbp->sb_gquotino;
sbp->sb_gquotino = NULLFSINO;
diff --git a/fs/xfs/libxfs/xfs_types.h b/fs/xfs/libxfs/xfs_types.h
index 8d74870..cf044c0 100644
--- a/fs/xfs/libxfs/xfs_types.h
+++ b/fs/xfs/libxfs/xfs_types.h
@@ -75,11 +75,14 @@
* Minimum and maximum blocksize and sectorsize.
* The blocksize upper limit is pretty much arbitrary.
* The sectorsize upper limit is due to sizeof(sb_sectsize).
+ * CRC enable filesystems use 512 byte inodes, meaning 512 byte block sizes
+ * cannot be used.
*/
#define XFS_MIN_BLOCKSIZE_LOG 9 /* i.e. 512 bytes */
#define XFS_MAX_BLOCKSIZE_LOG 16 /* i.e. 65536 bytes */
#define XFS_MIN_BLOCKSIZE (1 << XFS_MIN_BLOCKSIZE_LOG)
#define XFS_MAX_BLOCKSIZE (1 << XFS_MAX_BLOCKSIZE_LOG)
+#define XFS_MIN_CRC_BLOCKSIZE (1 << (XFS_MIN_BLOCKSIZE_LOG + 1))
#define XFS_MIN_SECTORSIZE_LOG 9 /* i.e. 512 bytes */
#define XFS_MAX_SECTORSIZE_LOG 15 /* i.e. 32768 bytes */
#define XFS_MIN_SECTORSIZE (1 << XFS_MIN_SECTORSIZE_LOG)
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 3e57a56..06763f5 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -1158,19 +1158,22 @@
* block_invalidatepage() can send pages that are still marked dirty
* but otherwise have invalidated buffers.
*
- * We've historically freed buffers on the latter. Instead, quietly
- * filter out all dirty pages to avoid spurious buffer state warnings.
- * This can likely be removed once shrink_active_list() is fixed.
+ * We want to release the latter to avoid unnecessary buildup of the
+ * LRU, skip the former and warn if we've left any lingering
+ * delalloc/unwritten buffers on clean pages. Skip pages with delalloc
+ * or unwritten buffers and warn if the page is not dirty. Otherwise
+ * try to release the buffers.
*/
- if (PageDirty(page))
- return 0;
-
xfs_count_page_state(page, &delalloc, &unwritten);
- if (WARN_ON_ONCE(delalloc))
+ if (delalloc) {
+ WARN_ON_ONCE(!PageDirty(page));
return 0;
- if (WARN_ON_ONCE(unwritten))
+ }
+ if (unwritten) {
+ WARN_ON_ONCE(!PageDirty(page));
return 0;
+ }
return try_to_free_buffers(page);
}
@@ -1361,6 +1364,26 @@
if (error)
goto out_unlock;
+ /*
+ * The only time we can ever safely find delalloc blocks on direct I/O
+ * is a dio write to post-eof speculative preallocation. All other
+ * scenarios are indicative of a problem or misuse (such as mixing
+ * direct and mapped I/O).
+ *
+ * The file may be unmapped by the time we get here so we cannot
+ * reliably fail the I/O based on mapping. Instead, fail the I/O if this
+ * is a read or a write within eof. Otherwise, carry on but warn as a
+ * precuation if the file happens to be mapped.
+ */
+ if (direct && imap.br_startblock == DELAYSTARTBLOCK) {
+ if (!create || offset < i_size_read(VFS_I(ip))) {
+ WARN_ON_ONCE(1);
+ error = -EIO;
+ goto out_unlock;
+ }
+ WARN_ON_ONCE(mapping_mapped(VFS_I(ip)->i_mapping));
+ }
+
/* for DAX, we convert unwritten extents directly */
if (create &&
(!nimaps ||
@@ -1450,8 +1473,6 @@
(new || ISUNWRITTEN(&imap))))
set_buffer_new(bh_result);
- BUG_ON(direct && imap.br_startblock == DELAYSTARTBLOCK);
-
return 0;
out_unlock:
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index 552465e..efb8ccd 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -359,9 +359,7 @@
mp = ip->i_mount;
ifp = XFS_IFORK_PTR(ip, whichfork);
if ( XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS ) {
- xfs_bmap_count_leaves(ifp, 0,
- ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t),
- count);
+ xfs_bmap_count_leaves(ifp, 0, xfs_iext_count(ifp), count);
return 0;
}
@@ -426,7 +424,7 @@
ifp = XFS_IFORK_PTR(ip, whichfork);
if (!moretocome &&
xfs_iext_bno_to_ext(ifp, fileblock, &lastx) &&
- (lastx == (ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t))-1))
+ (lastx == xfs_iext_count(ifp) - 1))
out->bmv_oflags |= BMV_OF_LAST;
}
@@ -530,7 +528,6 @@
xfs_bmbt_irec_t *map; /* buffer for user's data */
xfs_mount_t *mp; /* file system mount point */
int nex; /* # of user extents can do */
- int nexleft; /* # of user extents left */
int subnex; /* # of bmapi's can do */
int nmap; /* number of map entries */
struct getbmapx *out; /* output structure */
@@ -688,10 +685,8 @@
goto out_free_map;
}
- nexleft = nex;
-
do {
- nmap = (nexleft > subnex) ? subnex : nexleft;
+ nmap = (nex> subnex) ? subnex : nex;
error = xfs_bmapi_read(ip, XFS_BB_TO_FSBT(mp, bmv->bmv_offset),
XFS_BB_TO_FSB(mp, bmv->bmv_length),
map, &nmap, bmapi_flags);
@@ -699,8 +694,8 @@
goto out_free_map;
ASSERT(nmap <= subnex);
- for (i = 0; i < nmap && nexleft && bmv->bmv_length &&
- cur_ext < bmv->bmv_count; i++) {
+ for (i = 0; i < nmap && bmv->bmv_length &&
+ cur_ext < bmv->bmv_count - 1; i++) {
out[cur_ext].bmv_oflags = 0;
if (map[i].br_state == XFS_EXT_UNWRITTEN)
out[cur_ext].bmv_oflags |= BMV_OF_PREALLOC;
@@ -762,16 +757,27 @@
continue;
}
+ /*
+ * In order to report shared extents accurately,
+ * we report each distinct shared/unshared part
+ * of a single bmbt record using multiple bmap
+ * extents. To make that happen, we iterate the
+ * same map array item multiple times, each
+ * time trimming out the subextent that we just
+ * reported.
+ *
+ * Because of this, we must check the out array
+ * index (cur_ext) directly against bmv_count-1
+ * to avoid overflows.
+ */
if (inject_map.br_startblock != NULLFSBLOCK) {
map[i] = inject_map;
i--;
- } else
- nexleft--;
+ }
bmv->bmv_entries++;
cur_ext++;
}
- } while (nmap && nexleft && bmv->bmv_length &&
- cur_ext < bmv->bmv_count);
+ } while (nmap && bmv->bmv_length && cur_ext < bmv->bmv_count - 1);
out_free_map:
kmem_free(map);
@@ -1792,6 +1798,7 @@
struct xfs_ifork tempifp, *ifp, *tifp;
int aforkblks = 0;
int taforkblks = 0;
+ xfs_extnum_t nextents;
__uint64_t tmp;
int error;
@@ -1877,14 +1884,13 @@
switch (ip->i_d.di_format) {
case XFS_DINODE_FMT_EXTENTS:
- /* If the extents fit in the inode, fix the
- * pointer. Otherwise it's already NULL or
- * pointing to the extent.
+ /*
+ * If the extents fit in the inode, fix the pointer. Otherwise
+ * it's already NULL or pointing to the extent.
*/
- if (ip->i_d.di_nextents <= XFS_INLINE_EXTS) {
- ifp->if_u1.if_extents =
- ifp->if_u2.if_inline_ext;
- }
+ nextents = xfs_iext_count(&ip->i_df);
+ if (nextents <= XFS_INLINE_EXTS)
+ ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
(*src_log_flags) |= XFS_ILOG_DEXT;
break;
case XFS_DINODE_FMT_BTREE:
@@ -1896,14 +1902,13 @@
switch (tip->i_d.di_format) {
case XFS_DINODE_FMT_EXTENTS:
- /* If the extents fit in the inode, fix the
- * pointer. Otherwise it's already NULL or
- * pointing to the extent.
+ /*
+ * If the extents fit in the inode, fix the pointer. Otherwise
+ * it's already NULL or pointing to the extent.
*/
- if (tip->i_d.di_nextents <= XFS_INLINE_EXTS) {
- tifp->if_u1.if_extents =
- tifp->if_u2.if_inline_ext;
- }
+ nextents = xfs_iext_count(&tip->i_df);
+ if (nextents <= XFS_INLINE_EXTS)
+ tifp->if_u1.if_extents = tifp->if_u2.if_inline_ext;
(*target_log_flags) |= XFS_ILOG_DEXT;
break;
case XFS_DINODE_FMT_BTREE:
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index b5b9bff..d7a67d7 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -423,6 +423,7 @@
out_free_pages:
for (i = 0; i < bp->b_page_count; i++)
__free_page(bp->b_pages[i]);
+ bp->b_flags &= ~_XBF_PAGES;
return error;
}
diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c
index 7a30b8f..9d06cc3 100644
--- a/fs/xfs/xfs_dquot.c
+++ b/fs/xfs/xfs_dquot.c
@@ -710,6 +710,10 @@
/* Simple advance */
next_id = *id + 1;
+ /* If we'd wrap past the max ID, stop */
+ if (next_id < *id)
+ return -ENOENT;
+
/* If new ID is within the current chunk, advancing it sufficed */
if (next_id % mp->m_quotainfo->qi_dqperchunk) {
*id = next_id;
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 6e4f7f9..9a5d64b 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -939,7 +939,6 @@
len, false);
}
-#define XFS_MAX_DEDUPE_LEN (16 * 1024 * 1024)
STATIC ssize_t
xfs_file_dedupe_range(
struct file *src_file,
@@ -950,14 +949,6 @@
{
int error;
- /*
- * Limit the total length we will dedupe for each operation.
- * This is intended to bound the total time spent in this
- * ioctl to something sane.
- */
- if (len > XFS_MAX_DEDUPE_LEN)
- len = XFS_MAX_DEDUPE_LEN;
-
error = xfs_reflink_remap_range(src_file, loff, dst_file, dst_loff,
len, true);
if (error)
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 93d12fa..242e809 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -631,6 +631,20 @@
xfs_set_low_space_thresholds(mp);
mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
+ /*
+ * If we expanded the last AG, free the per-AG reservation
+ * so we can reinitialize it with the new size.
+ */
+ if (new) {
+ struct xfs_perag *pag;
+
+ pag = xfs_perag_get(mp, agno);
+ error = xfs_ag_resv_free(pag);
+ xfs_perag_put(pag);
+ if (error)
+ goto out;
+ }
+
/* Reserve AG metadata blocks. */
error = xfs_fs_reserve_ag_blocks(mp);
if (error && error != -ENOSPC)
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index f295049..29cc988 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -123,7 +123,6 @@
{
/* asserts to verify all state is correct here */
ASSERT(atomic_read(&ip->i_pincount) == 0);
- ASSERT(!xfs_isiflocked(ip));
XFS_STATS_DEC(ip->i_mount, vn_active);
call_rcu(&VFS_I(ip)->i_rcu, xfs_inode_free_callback);
@@ -133,6 +132,8 @@
xfs_inode_free(
struct xfs_inode *ip)
{
+ ASSERT(!xfs_isiflocked(ip));
+
/*
* Because we use RCU freeing we need to ensure the inode always
* appears to be reclaimed with an invalid inode number when in the
@@ -981,6 +982,7 @@
if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
xfs_iunpin_wait(ip);
+ /* xfs_iflush_abort() drops the flush lock */
xfs_iflush_abort(ip, false);
goto reclaim;
}
@@ -989,10 +991,10 @@
goto out_ifunlock;
xfs_iunpin_wait(ip);
}
- if (xfs_iflags_test(ip, XFS_ISTALE))
+ if (xfs_iflags_test(ip, XFS_ISTALE) || xfs_inode_clean(ip)) {
+ xfs_ifunlock(ip);
goto reclaim;
- if (xfs_inode_clean(ip))
- goto reclaim;
+ }
/*
* Never flush out dirty data during non-blocking reclaim, as it would
@@ -1030,25 +1032,24 @@
xfs_buf_relse(bp);
}
- xfs_iflock(ip);
reclaim:
+ ASSERT(!xfs_isiflocked(ip));
+
/*
* Because we use RCU freeing we need to ensure the inode always appears
* to be reclaimed with an invalid inode number when in the free state.
- * We do this as early as possible under the ILOCK and flush lock so
- * that xfs_iflush_cluster() can be guaranteed to detect races with us
- * here. By doing this, we guarantee that once xfs_iflush_cluster has
- * locked both the XFS_ILOCK and the flush lock that it will see either
- * a valid, flushable inode that will serialise correctly against the
- * locks below, or it will see a clean (and invalid) inode that it can
- * skip.
+ * We do this as early as possible under the ILOCK so that
+ * xfs_iflush_cluster() can be guaranteed to detect races with us here.
+ * By doing this, we guarantee that once xfs_iflush_cluster has locked
+ * XFS_ILOCK that it will see either a valid, flushable inode that will
+ * serialise correctly, or it will see a clean (and invalid) inode that
+ * it can skip.
*/
spin_lock(&ip->i_flags_lock);
ip->i_flags = XFS_IRECLAIM;
ip->i_ino = 0;
spin_unlock(&ip->i_flags_lock);
- xfs_ifunlock(ip);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
XFS_STATS_INC(ip->i_mount, xs_ig_reclaims);
@@ -1580,10 +1581,15 @@
struct xfs_eofblocks *eofb = args;
bool need_iolock = true;
int match;
+ struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
ASSERT(!eofb || (eofb && eofb->eof_scan_owner != 0));
- if (!xfs_reflink_has_real_cow_blocks(ip)) {
+ /*
+ * Just clear the tag if we have an empty cow fork or none at all. It's
+ * possible the inode was fully unshared since it was originally tagged.
+ */
+ if (!xfs_is_reflink_inode(ip) || !ifp->if_bytes) {
trace_xfs_inode_free_cowblocks_invalid(ip);
xfs_inode_clear_cowblocks_tag(ip);
return 0;
@@ -1593,7 +1599,8 @@
* If the mapping is dirty or under writeback we cannot touch the
* CoW fork. Leave it alone if we're in the midst of a directio.
*/
- if (mapping_tagged(VFS_I(ip)->i_mapping, PAGECACHE_TAG_DIRTY) ||
+ if ((VFS_I(ip)->i_state & I_DIRTY_PAGES) ||
+ mapping_tagged(VFS_I(ip)->i_mapping, PAGECACHE_TAG_DIRTY) ||
mapping_tagged(VFS_I(ip)->i_mapping, PAGECACHE_TAG_WRITEBACK) ||
atomic_read(&VFS_I(ip)->i_dio_count))
return 0;
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 4e560e6..512ff13 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -2041,7 +2041,6 @@
agi->agi_unlinked[bucket_index] = cpu_to_be32(agino);
offset = offsetof(xfs_agi_t, agi_unlinked) +
(sizeof(xfs_agino_t) * bucket_index);
- xfs_trans_buf_set_type(tp, agibp, XFS_BLFT_AGI_BUF);
xfs_trans_log_buf(tp, agibp, offset,
(offset + sizeof(xfs_agino_t) - 1));
return 0;
@@ -2133,7 +2132,6 @@
agi->agi_unlinked[bucket_index] = cpu_to_be32(next_agino);
offset = offsetof(xfs_agi_t, agi_unlinked) +
(sizeof(xfs_agino_t) * bucket_index);
- xfs_trans_buf_set_type(tp, agibp, XFS_BLFT_AGI_BUF);
xfs_trans_log_buf(tp, agibp, offset,
(offset + sizeof(xfs_agino_t) - 1));
} else {
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
index f14c1de..71e8a81 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -246,6 +246,11 @@
* Synchronize processes attempting to flush the in-core inode back to disk.
*/
+static inline int xfs_isiflocked(struct xfs_inode *ip)
+{
+ return xfs_iflags_test(ip, XFS_IFLOCK);
+}
+
extern void __xfs_iflock(struct xfs_inode *ip);
static inline int xfs_iflock_nowait(struct xfs_inode *ip)
@@ -261,16 +266,12 @@
static inline void xfs_ifunlock(struct xfs_inode *ip)
{
+ ASSERT(xfs_isiflocked(ip));
xfs_iflags_clear(ip, XFS_IFLOCK);
smp_mb();
wake_up_bit(&ip->i_flags, __XFS_IFLOCK_BIT);
}
-static inline int xfs_isiflocked(struct xfs_inode *ip)
-{
- return xfs_iflags_test(ip, XFS_IFLOCK);
-}
-
/*
* Flags for inode locking.
* Bit ranges: 1<<1 - 1<<16-1 -- iolock/ilock modes (bitfield)
diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c
index 9610e9c..d90e781 100644
--- a/fs/xfs/xfs_inode_item.c
+++ b/fs/xfs/xfs_inode_item.c
@@ -164,7 +164,7 @@
struct xfs_bmbt_rec *p;
ASSERT(ip->i_df.if_u1.if_extents != NULL);
- ASSERT(ip->i_df.if_bytes / sizeof(xfs_bmbt_rec_t) > 0);
+ ASSERT(xfs_iext_count(&ip->i_df) > 0);
p = xlog_prepare_iovec(lv, vecp, XLOG_REG_TYPE_IEXT);
data_bytes = xfs_iextents_copy(ip, p, XFS_DATA_FORK);
@@ -261,7 +261,7 @@
ip->i_afp->if_bytes > 0) {
struct xfs_bmbt_rec *p;
- ASSERT(ip->i_afp->if_bytes / sizeof(xfs_bmbt_rec_t) ==
+ ASSERT(xfs_iext_count(ip->i_afp) ==
ip->i_d.di_anextents);
ASSERT(ip->i_afp->if_u1.if_extents != NULL);
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index c245bed..a391975 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -910,16 +910,14 @@
if (attr) {
if (ip->i_afp) {
if (ip->i_afp->if_flags & XFS_IFEXTENTS)
- fa.fsx_nextents = ip->i_afp->if_bytes /
- sizeof(xfs_bmbt_rec_t);
+ fa.fsx_nextents = xfs_iext_count(ip->i_afp);
else
fa.fsx_nextents = ip->i_d.di_anextents;
} else
fa.fsx_nextents = 0;
} else {
if (ip->i_df.if_flags & XFS_IFEXTENTS)
- fa.fsx_nextents = ip->i_df.if_bytes /
- sizeof(xfs_bmbt_rec_t);
+ fa.fsx_nextents = xfs_iext_count(&ip->i_df);
else
fa.fsx_nextents = ip->i_d.di_nextents;
}
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 436e109..cdc6bdd 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -395,11 +395,12 @@
struct xfs_inode *ip,
loff_t offset,
loff_t count,
- xfs_extnum_t idx,
- struct xfs_bmbt_irec *prev)
+ xfs_extnum_t idx)
{
struct xfs_mount *mp = ip->i_mount;
+ struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset);
+ struct xfs_bmbt_irec prev;
int shift = 0;
int64_t freesp;
xfs_fsblock_t qblocks;
@@ -419,8 +420,8 @@
*/
if ((mp->m_flags & XFS_MOUNT_DFLT_IOSIZE) ||
XFS_ISIZE(ip) < XFS_FSB_TO_B(mp, mp->m_dalign) ||
- idx == 0 ||
- prev->br_startoff + prev->br_blockcount < offset_fsb)
+ !xfs_iext_get_extent(ifp, idx - 1, &prev) ||
+ prev.br_startoff + prev.br_blockcount < offset_fsb)
return mp->m_writeio_blocks;
/*
@@ -439,8 +440,8 @@
* always extends to MAXEXTLEN rather than falling short due to things
* like stripe unit/width alignment of real extents.
*/
- if (prev->br_blockcount <= (MAXEXTLEN >> 1))
- alloc_blocks = prev->br_blockcount << 1;
+ if (prev.br_blockcount <= (MAXEXTLEN >> 1))
+ alloc_blocks = prev.br_blockcount << 1;
else
alloc_blocks = XFS_B_TO_FSB(mp, offset);
if (!alloc_blocks)
@@ -535,11 +536,11 @@
xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset);
xfs_fileoff_t maxbytes_fsb =
XFS_B_TO_FSB(mp, mp->m_super->s_maxbytes);
- xfs_fileoff_t end_fsb, orig_end_fsb;
+ xfs_fileoff_t end_fsb;
int error = 0, eof = 0;
struct xfs_bmbt_irec got;
- struct xfs_bmbt_irec prev;
xfs_extnum_t idx;
+ xfs_fsblock_t prealloc_blocks = 0;
ASSERT(!XFS_IS_REALTIME_INODE(ip));
ASSERT(!xfs_get_extsz_hint(ip));
@@ -563,8 +564,7 @@
goto out_unlock;
}
- xfs_bmap_search_extents(ip, offset_fsb, XFS_DATA_FORK, &eof, &idx,
- &got, &prev);
+ eof = !xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got);
if (!eof && got.br_startoff <= offset_fsb) {
if (xfs_is_reflink_inode(ip)) {
bool shared;
@@ -595,35 +595,32 @@
* the lower level functions are updated.
*/
count = min_t(loff_t, count, 1024 * PAGE_SIZE);
- end_fsb = orig_end_fsb =
- min(XFS_B_TO_FSB(mp, offset + count), maxbytes_fsb);
+ end_fsb = min(XFS_B_TO_FSB(mp, offset + count), maxbytes_fsb);
if (eof) {
- xfs_fsblock_t prealloc_blocks;
-
- prealloc_blocks =
- xfs_iomap_prealloc_size(ip, offset, count, idx, &prev);
+ prealloc_blocks = xfs_iomap_prealloc_size(ip, offset, count, idx);
if (prealloc_blocks) {
xfs_extlen_t align;
xfs_off_t end_offset;
+ xfs_fileoff_t p_end_fsb;
end_offset = XFS_WRITEIO_ALIGN(mp, offset + count - 1);
- end_fsb = XFS_B_TO_FSBT(mp, end_offset) +
- prealloc_blocks;
+ p_end_fsb = XFS_B_TO_FSBT(mp, end_offset) +
+ prealloc_blocks;
align = xfs_eof_alignment(ip, 0);
if (align)
- end_fsb = roundup_64(end_fsb, align);
+ p_end_fsb = roundup_64(p_end_fsb, align);
- end_fsb = min(end_fsb, maxbytes_fsb);
- ASSERT(end_fsb > offset_fsb);
+ p_end_fsb = min(p_end_fsb, maxbytes_fsb);
+ ASSERT(p_end_fsb > offset_fsb);
+ prealloc_blocks = p_end_fsb - end_fsb;
}
}
retry:
error = xfs_bmapi_reserve_delalloc(ip, XFS_DATA_FORK, offset_fsb,
- end_fsb - offset_fsb, &got,
- &prev, &idx, eof);
+ end_fsb - offset_fsb, prealloc_blocks, &got, &idx, eof);
switch (error) {
case 0:
break;
@@ -631,8 +628,8 @@
case -EDQUOT:
/* retry without any preallocation */
trace_xfs_delalloc_enospc(ip, offset, count);
- if (end_fsb != orig_end_fsb) {
- end_fsb = orig_end_fsb;
+ if (prealloc_blocks) {
+ prealloc_blocks = 0;
goto retry;
}
/*FALLTHRU*/
@@ -640,13 +637,6 @@
goto out_unlock;
}
- /*
- * Tag the inode as speculatively preallocated so we can reclaim this
- * space on demand, if necessary.
- */
- if (end_fsb != orig_end_fsb)
- xfs_inode_set_eofblocks_tag(ip);
-
trace_xfs_iomap_alloc(ip, offset, count, 0, &got);
done:
if (isnullstartblock(got.br_startblock))
@@ -691,7 +681,7 @@
xfs_trans_t *tp;
int nimaps;
int error = 0;
- int flags = 0;
+ int flags = XFS_BMAPI_DELALLOC;
int nres;
if (whichfork == XFS_COW_FORK)
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 405a65c..f5e0f60 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -98,12 +98,27 @@
static void
xfs_dentry_to_name(
struct xfs_name *namep,
+ struct dentry *dentry)
+{
+ namep->name = dentry->d_name.name;
+ namep->len = dentry->d_name.len;
+ namep->type = XFS_DIR3_FT_UNKNOWN;
+}
+
+static int
+xfs_dentry_mode_to_name(
+ struct xfs_name *namep,
struct dentry *dentry,
int mode)
{
namep->name = dentry->d_name.name;
namep->len = dentry->d_name.len;
- namep->type = xfs_mode_to_ftype[(mode & S_IFMT) >> S_SHIFT];
+ namep->type = xfs_mode_to_ftype(mode);
+
+ if (unlikely(namep->type == XFS_DIR3_FT_UNKNOWN))
+ return -EFSCORRUPTED;
+
+ return 0;
}
STATIC void
@@ -119,7 +134,7 @@
* xfs_init_security we must back out.
* ENOSPC can hit here, among other things.
*/
- xfs_dentry_to_name(&teardown, dentry, 0);
+ xfs_dentry_to_name(&teardown, dentry);
xfs_remove(XFS_I(dir), &teardown, XFS_I(inode));
}
@@ -154,8 +169,12 @@
if (error)
return error;
+ /* Verify mode is valid also for tmpfile case */
+ error = xfs_dentry_mode_to_name(&name, dentry, mode);
+ if (unlikely(error))
+ goto out_free_acl;
+
if (!tmpfile) {
- xfs_dentry_to_name(&name, dentry, mode);
error = xfs_create(XFS_I(dir), &name, mode, rdev, &ip);
} else {
error = xfs_create_tmpfile(XFS_I(dir), dentry, mode, &ip);
@@ -248,7 +267,7 @@
if (dentry->d_name.len >= MAXNAMELEN)
return ERR_PTR(-ENAMETOOLONG);
- xfs_dentry_to_name(&name, dentry, 0);
+ xfs_dentry_to_name(&name, dentry);
error = xfs_lookup(XFS_I(dir), &name, &cip, NULL);
if (unlikely(error)) {
if (unlikely(error != -ENOENT))
@@ -275,7 +294,7 @@
if (dentry->d_name.len >= MAXNAMELEN)
return ERR_PTR(-ENAMETOOLONG);
- xfs_dentry_to_name(&xname, dentry, 0);
+ xfs_dentry_to_name(&xname, dentry);
error = xfs_lookup(XFS_I(dir), &xname, &ip, &ci_name);
if (unlikely(error)) {
if (unlikely(error != -ENOENT))
@@ -310,7 +329,9 @@
struct xfs_name name;
int error;
- xfs_dentry_to_name(&name, dentry, inode->i_mode);
+ error = xfs_dentry_mode_to_name(&name, dentry, inode->i_mode);
+ if (unlikely(error))
+ return error;
error = xfs_link(XFS_I(dir), XFS_I(inode), &name);
if (unlikely(error))
@@ -329,7 +350,7 @@
struct xfs_name name;
int error;
- xfs_dentry_to_name(&name, dentry, 0);
+ xfs_dentry_to_name(&name, dentry);
error = xfs_remove(XFS_I(dir), &name, XFS_I(d_inode(dentry)));
if (error)
@@ -359,7 +380,9 @@
mode = S_IFLNK |
(irix_symlink_mode ? 0777 & ~current_umask() : S_IRWXUGO);
- xfs_dentry_to_name(&name, dentry, mode);
+ error = xfs_dentry_mode_to_name(&name, dentry, mode);
+ if (unlikely(error))
+ goto out;
error = xfs_symlink(XFS_I(dir), &name, symname, mode, &cip);
if (unlikely(error))
@@ -395,6 +418,7 @@
{
struct inode *new_inode = d_inode(ndentry);
int omode = 0;
+ int error;
struct xfs_name oname;
struct xfs_name nname;
@@ -405,8 +429,14 @@
if (flags & RENAME_EXCHANGE)
omode = d_inode(ndentry)->i_mode;
- xfs_dentry_to_name(&oname, odentry, omode);
- xfs_dentry_to_name(&nname, ndentry, d_inode(odentry)->i_mode);
+ error = xfs_dentry_mode_to_name(&oname, odentry, omode);
+ if (omode && unlikely(error))
+ return error;
+
+ error = xfs_dentry_mode_to_name(&nname, ndentry,
+ d_inode(odentry)->i_mode);
+ if (unlikely(error))
+ return error;
return xfs_rename(XFS_I(odir), &oname, XFS_I(d_inode(odentry)),
XFS_I(ndir), &nname,
diff --git a/fs/xfs/xfs_linux.h b/fs/xfs/xfs_linux.h
index 68640fb..1455b2520 100644
--- a/fs/xfs/xfs_linux.h
+++ b/fs/xfs/xfs_linux.h
@@ -330,11 +330,11 @@
}
#define ASSERT_ALWAYS(expr) \
- (unlikely(expr) ? (void)0 : assfail(#expr, __FILE__, __LINE__))
+ (likely(expr) ? (void)0 : assfail(#expr, __FILE__, __LINE__))
#ifdef DEBUG
#define ASSERT(expr) \
- (unlikely(expr) ? (void)0 : assfail(#expr, __FILE__, __LINE__))
+ (likely(expr) ? (void)0 : assfail(#expr, __FILE__, __LINE__))
#ifndef STATIC
# define STATIC noinline
@@ -345,7 +345,7 @@
#ifdef XFS_WARN
#define ASSERT(expr) \
- (unlikely(expr) ? (void)0 : asswarn(#expr, __FILE__, __LINE__))
+ (likely(expr) ? (void)0 : asswarn(#expr, __FILE__, __LINE__))
#ifndef STATIC
# define STATIC static noinline
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index 3b74fa0..4017aa9 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -3324,12 +3324,8 @@
xfs_mount_t *mp,
uint flags)
{
- int error;
-
trace_xfs_log_force(mp, 0, _RET_IP_);
- error = _xfs_log_force(mp, flags, NULL);
- if (error)
- xfs_warn(mp, "%s: error %d returned.", __func__, error);
+ _xfs_log_force(mp, flags, NULL);
}
/*
@@ -3473,12 +3469,8 @@
xfs_lsn_t lsn,
uint flags)
{
- int error;
-
trace_xfs_log_force(mp, lsn, _RET_IP_);
- error = _xfs_log_force_lsn(mp, lsn, flags, NULL);
- if (error)
- xfs_warn(mp, "%s: error %d returned.", __func__, error);
+ _xfs_log_force_lsn(mp, lsn, flags, NULL);
}
/*
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index a60d9e2..b669b12 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -1135,7 +1135,7 @@
return error;
}
rtblks = 0;
- nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ nextents = xfs_iext_count(ifp);
for (idx = 0; idx < nextents; idx++)
rtblks += xfs_bmbt_get_blockcount(xfs_iext_get_ext(ifp, idx));
*O_rtblks = (xfs_qcnt_t)rtblks;
@@ -1177,7 +1177,8 @@
* the case in all other instances. It's OK that we do this because
* quotacheck is done only at mount time.
*/
- error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_EXCL, &ip);
+ error = xfs_iget(mp, NULL, ino, XFS_IGET_DONTCACHE, XFS_ILOCK_EXCL,
+ &ip);
if (error) {
*res = BULKSTAT_RV_NOTHING;
return error;
diff --git a/fs/xfs/xfs_refcount_item.c b/fs/xfs/xfs_refcount_item.c
index fe86a66..6e4c744 100644
--- a/fs/xfs/xfs_refcount_item.c
+++ b/fs/xfs/xfs_refcount_item.c
@@ -526,13 +526,14 @@
xfs_refcount_finish_one_cleanup(tp, rcur, error);
error = xfs_defer_finish(&tp, &dfops, NULL);
if (error)
- goto abort_error;
+ goto abort_defer;
set_bit(XFS_CUI_RECOVERED, &cuip->cui_flags);
error = xfs_trans_commit(tp);
return error;
abort_error:
xfs_refcount_finish_one_cleanup(tp, rcur, error);
+abort_defer:
xfs_defer_cancel(&dfops);
xfs_trans_cancel(tp);
return error;
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index a279b4e..4d3f74e 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -243,12 +243,11 @@
struct xfs_bmbt_irec *imap,
bool *shared)
{
- struct xfs_bmbt_irec got, prev;
- xfs_fileoff_t end_fsb, orig_end_fsb;
- int eof = 0, error = 0;
- bool trimmed;
+ struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
+ struct xfs_bmbt_irec got;
+ int error = 0;
+ bool eof = false, trimmed;
xfs_extnum_t idx;
- xfs_extlen_t align;
/*
* Search the COW fork extent list first. This serves two purposes:
@@ -258,8 +257,9 @@
* extent list is generally faster than going out to the shared extent
* tree.
*/
- xfs_bmap_search_extents(ip, imap->br_startoff, XFS_COW_FORK, &eof, &idx,
- &got, &prev);
+
+ if (!xfs_iext_lookup_extent(ip, ifp, imap->br_startoff, &idx, &got))
+ eof = true;
if (!eof && got.br_startoff <= imap->br_startoff) {
trace_xfs_reflink_cow_found(ip, imap);
xfs_trim_extent(imap, got.br_startoff, got.br_blockcount);
@@ -285,33 +285,12 @@
if (error)
return error;
- end_fsb = orig_end_fsb = imap->br_startoff + imap->br_blockcount;
-
- align = xfs_eof_alignment(ip, xfs_get_cowextsz_hint(ip));
- if (align)
- end_fsb = roundup_64(end_fsb, align);
-
-retry:
error = xfs_bmapi_reserve_delalloc(ip, XFS_COW_FORK, imap->br_startoff,
- end_fsb - imap->br_startoff, &got, &prev, &idx, eof);
- switch (error) {
- case 0:
- break;
- case -ENOSPC:
- case -EDQUOT:
- /* retry without any preallocation */
+ imap->br_blockcount, 0, &got, &idx, eof);
+ if (error == -ENOSPC || error == -EDQUOT)
trace_xfs_reflink_cow_enospc(ip, imap);
- if (end_fsb != orig_end_fsb) {
- end_fsb = orig_end_fsb;
- goto retry;
- }
- /*FALLTHRU*/
- default:
+ if (error)
return error;
- }
-
- if (end_fsb != orig_end_fsb)
- xfs_inode_set_cowblocks_tag(ip);
trace_xfs_reflink_cow_alloc(ip, &got);
return 0;
@@ -486,7 +465,7 @@
/* This is the extent before; try sliding up one. */
if (irec.br_startoff < offset_fsb) {
idx++;
- if (idx >= ifp->if_bytes / sizeof(xfs_bmbt_rec_t))
+ if (idx >= xfs_iext_count(ifp))
return 0;
gotp = xfs_iext_get_ext(ifp, idx);
xfs_bmbt_get_all(gotp, &irec);
@@ -566,7 +545,7 @@
xfs_bmap_del_extent_cow(ip, &idx, &got, &del);
}
- if (++idx >= ifp->if_bytes / sizeof(struct xfs_bmbt_rec))
+ if (++idx >= xfs_iext_count(ifp))
break;
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx), &got);
}
@@ -1345,8 +1324,14 @@
goto out_unlock;
}
- if (len == 0)
+ /* Zero length dedupe exits immediately; reflink goes to EOF. */
+ if (len == 0) {
+ if (is_dedupe) {
+ ret = 0;
+ goto out_unlock;
+ }
len = isize - pos_in;
+ }
/* Ensure offsets don't wrap and the input is inside i_size */
if (pos_in + len < pos_in || pos_out + len < pos_out ||
@@ -1697,37 +1682,3 @@
trace_xfs_reflink_unshare_error(ip, error, _RET_IP_);
return error;
}
-
-/*
- * Does this inode have any real CoW reservations?
- */
-bool
-xfs_reflink_has_real_cow_blocks(
- struct xfs_inode *ip)
-{
- struct xfs_bmbt_irec irec;
- struct xfs_ifork *ifp;
- struct xfs_bmbt_rec_host *gotp;
- xfs_extnum_t idx;
-
- if (!xfs_is_reflink_inode(ip))
- return false;
-
- /* Go find the old extent in the CoW fork. */
- ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
- gotp = xfs_iext_bno_to_ext(ifp, 0, &idx);
- while (gotp) {
- xfs_bmbt_get_all(gotp, &irec);
-
- if (!isnullstartblock(irec.br_startblock))
- return true;
-
- /* Roll on... */
- idx++;
- if (idx >= ifp->if_bytes / sizeof(xfs_bmbt_rec_t))
- break;
- gotp = xfs_iext_get_ext(ifp, idx);
- }
-
- return false;
-}
diff --git a/fs/xfs/xfs_reflink.h b/fs/xfs/xfs_reflink.h
index fad1160..97ea9b4 100644
--- a/fs/xfs/xfs_reflink.h
+++ b/fs/xfs/xfs_reflink.h
@@ -50,6 +50,4 @@
extern int xfs_reflink_unshare(struct xfs_inode *ip, xfs_off_t offset,
xfs_off_t len);
-extern bool xfs_reflink_has_real_cow_blocks(struct xfs_inode *ip);
-
#endif /* __XFS_REFLINK_H */
diff --git a/fs/xfs/xfs_sysfs.c b/fs/xfs/xfs_sysfs.c
index 276d302..de6195e 100644
--- a/fs/xfs/xfs_sysfs.c
+++ b/fs/xfs/xfs_sysfs.c
@@ -396,7 +396,7 @@
int retries;
struct xfs_error_cfg *cfg = to_error_cfg(kobject);
- if (cfg->retry_timeout == XFS_ERR_RETRY_FOREVER)
+ if (cfg->max_retries == XFS_ERR_RETRY_FOREVER)
retries = -1;
else
retries = cfg->max_retries;
@@ -422,7 +422,7 @@
return -EINVAL;
if (val == -1)
- cfg->retry_timeout = XFS_ERR_RETRY_FOREVER;
+ cfg->max_retries = XFS_ERR_RETRY_FOREVER;
else
cfg->max_retries = val;
return count;
diff --git a/include/asm-generic/asm-prototypes.h b/include/asm-generic/asm-prototypes.h
new file mode 100644
index 0000000..939869c
--- /dev/null
+++ b/include/asm-generic/asm-prototypes.h
@@ -0,0 +1,13 @@
+#include <linux/bitops.h>
+#undef __memset
+extern void *__memset(void *, int, __kernel_size_t);
+#undef __memcpy
+extern void *__memcpy(void *, const void *, __kernel_size_t);
+#undef __memmove
+extern void *__memmove(void *, const void *, __kernel_size_t);
+#undef memset
+extern void *memset(void *, int, __kernel_size_t);
+#undef memcpy
+extern void *memcpy(void *, const void *, __kernel_size_t);
+#undef memmove
+extern void *memmove(void *, const void *, __kernel_size_t);
diff --git a/include/dt-bindings/clock/qcom,audio-ext-clk.h b/include/dt-bindings/clock/qcom,audio-ext-clk.h
index c9a8286..08f7dde 100644
--- a/include/dt-bindings/clock/qcom,audio-ext-clk.h
+++ b/include/dt-bindings/clock/qcom,audio-ext-clk.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,4 +29,9 @@
#define clk_audio_pmi_lnbb_clk 0x57312343
#endif
+#define clk_div_clk1 0xaa1157a6
+#define clk_div_clk1_ao 0x6b943d68
+#define clk_ln_bb_clk2 0xf83e6387
+#define clk_ln_bb_clk2_ao 0x96f09628
+
#endif
diff --git a/include/dt-bindings/clock/r8a7794-clock.h b/include/dt-bindings/clock/r8a7794-clock.h
index 9d02f53..88e6484 100644
--- a/include/dt-bindings/clock/r8a7794-clock.h
+++ b/include/dt-bindings/clock/r8a7794-clock.h
@@ -20,8 +20,7 @@
#define R8A7794_CLK_QSPI 5
#define R8A7794_CLK_SDH 6
#define R8A7794_CLK_SD0 7
-#define R8A7794_CLK_Z 8
-#define R8A7794_CLK_RCAN 9
+#define R8A7794_CLK_RCAN 8
/* MSTP0 */
#define R8A7794_CLK_MSIOF0 0
diff --git a/include/dt-bindings/msm/pm.h b/include/dt-bindings/msm/pm.h
new file mode 100644
index 0000000..4845022
--- /dev/null
+++ b/include/dt-bindings/msm/pm.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DT_MSM_PM_H__
+#define __DT_MSM_PM_H__
+
+#define LPM_RESET_LVL_NONE 0
+#define LPM_RESET_LVL_RET 1
+#define LPM_RESET_LVL_GDHS 2
+#define LPM_RESET_LVL_PC 3
+
+#define LPM_AFF_LVL_CPU 0
+#define LPM_AFF_LVL_L2 1
+#define LPM_AFF_LVL_CCI 2
+
+#endif
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 53d4ea5..e47a7f7 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1060,7 +1060,7 @@
static inline void blk_post_runtime_suspend(struct request_queue *q, int err) {}
static inline void blk_pre_runtime_resume(struct request_queue *q) {}
static inline void blk_post_runtime_resume(struct request_queue *q, int err) {}
-extern inline void blk_set_runtime_active(struct request_queue *q) {}
+static inline void blk_set_runtime_active(struct request_queue *q) {}
#endif
/*
diff --git a/include/linux/capability.h b/include/linux/capability.h
index dbc21c7..6ffb67e 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -240,8 +240,10 @@
return true;
}
#endif /* CONFIG_MULTIUSER */
+extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode);
extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap);
+extern bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns);
/* audit system wants to get cap info from files as well */
extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps);
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index aae03c4..40b66f9 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -108,22 +108,16 @@
{ .notifier_call = fn, .priority = pri }; \
__register_cpu_notifier(&fn##_nb); \
}
-#else /* #if defined(CONFIG_HOTPLUG_CPU) || !defined(MODULE) */
-#define cpu_notifier(fn, pri) do { (void)(fn); } while (0)
-#define __cpu_notifier(fn, pri) do { (void)(fn); } while (0)
-#endif /* #else #if defined(CONFIG_HOTPLUG_CPU) || !defined(MODULE) */
-#ifdef CONFIG_HOTPLUG_CPU
extern int register_cpu_notifier(struct notifier_block *nb);
extern int __register_cpu_notifier(struct notifier_block *nb);
extern void unregister_cpu_notifier(struct notifier_block *nb);
extern void __unregister_cpu_notifier(struct notifier_block *nb);
-#else
-#ifndef MODULE
-extern int register_cpu_notifier(struct notifier_block *nb);
-extern int __register_cpu_notifier(struct notifier_block *nb);
-#else
+#else /* #if defined(CONFIG_HOTPLUG_CPU) || !defined(MODULE) */
+#define cpu_notifier(fn, pri) do { (void)(fn); } while (0)
+#define __cpu_notifier(fn, pri) do { (void)(fn); } while (0)
+
static inline int register_cpu_notifier(struct notifier_block *nb)
{
return 0;
@@ -133,7 +127,6 @@
{
return 0;
}
-#endif
static inline void unregister_cpu_notifier(struct notifier_block *nb)
{
diff --git a/include/linux/cpu_pm.h b/include/linux/cpu_pm.h
index 455b233..91117bc 100644
--- a/include/linux/cpu_pm.h
+++ b/include/linux/cpu_pm.h
@@ -71,8 +71,8 @@
int cpu_pm_unregister_notifier(struct notifier_block *nb);
int cpu_pm_enter(void);
int cpu_pm_exit(void);
-int cpu_cluster_pm_enter(void);
-int cpu_cluster_pm_exit(void);
+int cpu_cluster_pm_enter(unsigned long aff_level);
+int cpu_cluster_pm_exit(unsigned long aff_level);
#else
@@ -96,12 +96,12 @@
return 0;
}
-static inline int cpu_cluster_pm_enter(void)
+static inline int cpu_cluster_pm_enter(unsigned long aff_level)
{
return 0;
}
-static inline int cpu_cluster_pm_exit(void)
+static inline int cpu_cluster_pm_exit(unsigned long aff_level)
{
return 0;
}
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 32dc0cbd..cc57986 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -177,6 +177,7 @@
int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu);
int cpufreq_update_policy(unsigned int cpu);
bool have_governor_per_policy(void);
+bool cpufreq_driver_is_slow(void);
struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy);
void cpufreq_enable_fast_switch(struct cpufreq_policy *policy);
void cpufreq_disable_fast_switch(struct cpufreq_policy *policy);
@@ -359,6 +360,14 @@
*/
#define CPUFREQ_NEED_INITIAL_FREQ_CHECK (1 << 5)
+/*
+ * Indicates that it is safe to call cpufreq_driver_target from
+ * non-interruptable context in scheduler hot paths. Drivers must
+ * opt-in to this flag, as the safe default is that they might sleep
+ * or be too slow for hot path use.
+ */
+#define CPUFREQ_DRIVER_FAST (1 << 6)
+
int cpufreq_register_driver(struct cpufreq_driver *driver_data);
int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);
@@ -553,6 +562,32 @@
ssize_t (*store)(struct gov_attr_set *attr_set, const char *buf,
size_t count);
};
+/* CPUFREQ DEFAULT GOVERNOR */
+/*
+ * Performance governor is fallback governor if any other gov failed to auto
+ * load due latency restrictions
+ */
+#ifdef CONFIG_CPU_FREQ_GOV_PERFORMANCE
+extern struct cpufreq_governor cpufreq_gov_performance;
+#endif
+#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE
+#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_performance)
+#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE)
+extern struct cpufreq_governor cpufreq_gov_powersave;
+#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_powersave)
+#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE)
+extern struct cpufreq_governor cpufreq_gov_userspace;
+#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_userspace)
+#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND)
+extern struct cpufreq_governor cpufreq_gov_ondemand;
+#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_ondemand)
+#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE)
+extern struct cpufreq_governor cpufreq_gov_conservative;
+#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_conservative)
+#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_SCHED)
+extern struct cpufreq_governor cpufreq_gov_sched;
+#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_sched)
+#endif
/*********************************************************************
* FREQUENCY TABLE HELPERS *
@@ -886,4 +921,8 @@
int cpufreq_generic_init(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table,
unsigned int transition_latency);
+
+struct sched_domain;
+unsigned long cpufreq_scale_freq_capacity(struct sched_domain *sd, int cpu);
+unsigned long cpufreq_scale_max_freq_capacity(int cpu);
#endif /* _LINUX_CPUFREQ_H */
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index afe641c..ba1cad7 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -80,7 +80,6 @@
CPUHP_AP_ARM_L2X0_STARTING,
CPUHP_AP_ARM_ARCH_TIMER_STARTING,
CPUHP_AP_ARM_GLOBAL_TIMER_STARTING,
- CPUHP_AP_DUMMY_TIMER_STARTING,
CPUHP_AP_JCORE_TIMER_STARTING,
CPUHP_AP_EXYNOS4_MCT_TIMER_STARTING,
CPUHP_AP_ARM_TWD_STARTING,
@@ -94,6 +93,8 @@
CPUHP_AP_KVM_ARM_VGIC_INIT_STARTING,
CPUHP_AP_KVM_ARM_VGIC_STARTING,
CPUHP_AP_KVM_ARM_TIMER_STARTING,
+ /* Must be the last timer callback */
+ CPUHP_AP_DUMMY_TIMER_STARTING,
CPUHP_AP_ARM_XEN_STARTING,
CPUHP_AP_ARM_CORESIGHT_STARTING,
CPUHP_AP_ARM_CORESIGHT4_STARTING,
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index bb31373..9a8eec9 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -207,7 +207,7 @@
#endif
/* kernel/sched/idle.c */
-extern void sched_idle_set_state(struct cpuidle_state *idle_state);
+extern void sched_idle_set_state(struct cpuidle_state *idle_state, int index);
extern void default_idle_call(void);
#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h
index 4d3f0d1..1b413a9 100644
--- a/include/linux/debugfs.h
+++ b/include/linux/debugfs.h
@@ -62,6 +62,21 @@
return filp->f_path.dentry->d_fsdata;
}
+#define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \
+static int __fops ## _open(struct inode *inode, struct file *file) \
+{ \
+ __simple_attr_check_format(__fmt, 0ull); \
+ return simple_attr_open(inode, file, __get, __set, __fmt); \
+} \
+static const struct file_operations __fops = { \
+ .owner = THIS_MODULE, \
+ .open = __fops ## _open, \
+ .release = simple_attr_release, \
+ .read = debugfs_attr_read, \
+ .write = debugfs_attr_write, \
+ .llseek = generic_file_llseek, \
+}
+
#if defined(CONFIG_DEBUG_FS)
struct dentry *debugfs_create_file(const char *name, umode_t mode,
@@ -99,21 +114,6 @@
ssize_t debugfs_attr_write(struct file *file, const char __user *buf,
size_t len, loff_t *ppos);
-#define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \
-static int __fops ## _open(struct inode *inode, struct file *file) \
-{ \
- __simple_attr_check_format(__fmt, 0ull); \
- return simple_attr_open(inode, file, __get, __set, __fmt); \
-} \
-static const struct file_operations __fops = { \
- .owner = THIS_MODULE, \
- .open = __fops ## _open, \
- .release = simple_attr_release, \
- .read = debugfs_attr_read, \
- .write = debugfs_attr_write, \
- .llseek = generic_file_llseek, \
-}
-
struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
struct dentry *new_dir, const char *new_name);
@@ -233,8 +233,18 @@
__releases(&debugfs_srcu)
{ }
-#define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \
- static const struct file_operations __fops = { 0 }
+static inline ssize_t debugfs_attr_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return -ENODEV;
+}
+
+static inline ssize_t debugfs_attr_write(struct file *file,
+ const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return -ENODEV;
+}
static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
struct dentry *new_dir, char *new_name)
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 2d08948..cba7177 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -103,6 +103,7 @@
#define EFI_PAGE_SHIFT 12
#define EFI_PAGE_SIZE (1UL << EFI_PAGE_SHIFT)
+#define EFI_PAGES_MAX (U64_MAX >> EFI_PAGE_SHIFT)
typedef struct {
u32 type;
@@ -930,6 +931,7 @@
#endif
extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr);
+extern phys_addr_t __init efi_memmap_alloc(unsigned int num_entries);
extern int __init efi_memmap_init_early(struct efi_memory_map_data *data);
extern int __init efi_memmap_init_late(phys_addr_t addr, unsigned long size);
extern void __init efi_memmap_unmap(void);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index dc0478c..bed7a84 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1603,13 +1603,21 @@
* VFS helper functions..
*/
extern int vfs_create(struct inode *, struct dentry *, umode_t, bool);
+extern int vfs_create2(struct vfsmount *, struct inode *, struct dentry *, umode_t, bool);
extern int vfs_mkdir(struct inode *, struct dentry *, umode_t);
+extern int vfs_mkdir2(struct vfsmount *, struct inode *, struct dentry *, umode_t);
extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t);
+extern int vfs_mknod2(struct vfsmount *, struct inode *, struct dentry *, umode_t, dev_t);
extern int vfs_symlink(struct inode *, struct dentry *, const char *);
+extern int vfs_symlink2(struct vfsmount *, struct inode *, struct dentry *, const char *);
extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **);
+extern int vfs_link2(struct vfsmount *, struct dentry *, struct inode *, struct dentry *, struct inode **);
extern int vfs_rmdir(struct inode *, struct dentry *);
+extern int vfs_rmdir2(struct vfsmount *, struct inode *, struct dentry *);
extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
+extern int vfs_unlink2(struct vfsmount *, struct inode *, struct dentry *, struct inode **);
extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
+extern int vfs_rename2(struct vfsmount *, struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
extern int vfs_whiteout(struct inode *, struct dentry *);
/*
@@ -1737,6 +1745,7 @@
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
int (*permission) (struct inode *, int);
+ int (*permission2) (struct vfsmount *, struct inode *, int);
struct posix_acl * (*get_acl)(struct inode *, int);
int (*readlink) (struct dentry *, char __user *,int);
@@ -1751,6 +1760,7 @@
int (*rename) (struct inode *, struct dentry *,
struct inode *, struct dentry *, unsigned int);
int (*setattr) (struct dentry *, struct iattr *);
+ int (*setattr2) (struct vfsmount *, struct dentry *, struct iattr *);
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
@@ -1799,9 +1809,13 @@
int (*unfreeze_fs) (struct super_block *);
int (*statfs) (struct dentry *, struct kstatfs *);
int (*remount_fs) (struct super_block *, int *, char *);
+ int (*remount_fs2) (struct vfsmount *, struct super_block *, int *, char *);
+ void *(*clone_mnt_data) (void *);
+ void (*copy_mnt_data) (void *, void *);
void (*umount_begin) (struct super_block *);
int (*show_options)(struct seq_file *, struct dentry *);
+ int (*show_options2)(struct vfsmount *,struct seq_file *, struct dentry *);
int (*show_devname)(struct seq_file *, struct dentry *);
int (*show_path)(struct seq_file *, struct dentry *);
int (*show_stats)(struct seq_file *, struct dentry *);
@@ -2035,6 +2049,9 @@
#define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */
struct dentry *(*mount) (struct file_system_type *, int,
const char *, void *);
+ struct dentry *(*mount2) (struct vfsmount *, struct file_system_type *, int,
+ const char *, void *);
+ void *(*alloc_mnt_data) (void);
void (*kill_sb) (struct super_block *);
struct module *owner;
struct file_system_type * next;
@@ -2333,6 +2350,8 @@
extern long vfs_truncate(const struct path *, loff_t);
extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
struct file *filp);
+extern int do_truncate2(struct vfsmount *, struct dentry *, loff_t start,
+ unsigned int time_attrs, struct file *filp);
extern int vfs_fallocate(struct file *file, int mode, loff_t offset,
loff_t len);
extern long do_sys_open(int dfd, const char __user *filename, int flags,
@@ -2575,8 +2594,11 @@
extern sector_t bmap(struct inode *, sector_t);
#endif
extern int notify_change(struct dentry *, struct iattr *, struct inode **);
+extern int notify_change2(struct vfsmount *, struct dentry *, struct iattr *, struct inode **);
extern int inode_permission(struct inode *, int);
+extern int inode_permission2(struct vfsmount *, struct inode *, int);
extern int __inode_permission(struct inode *, int);
+extern int __inode_permission2(struct vfsmount *, struct inode *, int);
extern int generic_permission(struct inode *, int);
extern int __check_sticky(struct inode *dir, struct inode *inode);
diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h
index 228bd44..497f2b3 100644
--- a/include/linux/iio/common/st_sensors.h
+++ b/include/linux/iio/common/st_sensors.h
@@ -116,6 +116,16 @@
};
/**
+ * struct st_sensor_das - ST sensor device data alignment selection
+ * @addr: address of the register.
+ * @mask: mask to write the das flag for left alignment.
+ */
+struct st_sensor_das {
+ u8 addr;
+ u8 mask;
+};
+
+/**
* struct st_sensor_data_ready_irq - ST sensor device data-ready interrupt
* @addr: address of the register.
* @mask_int1: mask to enable/disable IRQ on INT1 pin.
@@ -185,6 +195,7 @@
* @enable_axis: Enable one or more axis of the sensor.
* @fs: Full scale register and full scale list available.
* @bdu: Block data update register.
+ * @das: Data Alignment Selection register.
* @drdy_irq: Data ready register of the sensor.
* @multi_read_bit: Use or not particular bit for [I2C/SPI] multi-read.
* @bootime: samples to discard when sensor passing from power-down to power-up.
@@ -200,6 +211,7 @@
struct st_sensor_axis enable_axis;
struct st_sensor_fullscale fs;
struct st_sensor_bdu bdu;
+ struct st_sensor_das das;
struct st_sensor_data_ready_irq drdy_irq;
bool multi_read_bit;
unsigned int bootime;
diff --git a/include/linux/ipa.h b/include/linux/ipa.h
index d173b4c..5fc7dda 100644
--- a/include/linux/ipa.h
+++ b/include/linux/ipa.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -852,6 +852,7 @@
* injected due to vdev_id change
* @num_ic_inj_fw_desc_change : Number of times the Imm Cmd is
* injected due to fw_desc change
+ * @num_qmb_int_handled : Number of QMB interrupts handled
*/
struct IpaHwStatsWDIRxInfoData_t {
u32 max_outstanding_pkts;
@@ -865,6 +866,7 @@
u32 num_pkts_in_dis_uninit_state;
u32 num_ic_inj_vdev_change;
u32 num_ic_inj_fw_desc_change;
+ u32 num_qmb_int_handled;
u32 reserved1;
u32 reserved2;
} __packed;
diff --git a/include/linux/irq.h b/include/linux/irq.h
index e798755..39e3254 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -184,6 +184,7 @@
*
* IRQD_TRIGGER_MASK - Mask for the trigger type bits
* IRQD_SETAFFINITY_PENDING - Affinity setting is pending
+ * IRQD_ACTIVATED - Interrupt has already been activated
* IRQD_NO_BALANCING - Balancing disabled for this IRQ
* IRQD_PER_CPU - Interrupt is per cpu
* IRQD_AFFINITY_SET - Interrupt affinity was set
@@ -202,6 +203,7 @@
enum {
IRQD_TRIGGER_MASK = 0xf,
IRQD_SETAFFINITY_PENDING = (1 << 8),
+ IRQD_ACTIVATED = (1 << 9),
IRQD_NO_BALANCING = (1 << 10),
IRQD_PER_CPU = (1 << 11),
IRQD_AFFINITY_SET = (1 << 12),
@@ -312,6 +314,21 @@
return __irqd_to_state(d) & IRQD_AFFINITY_MANAGED;
}
+static inline bool irqd_is_activated(struct irq_data *d)
+{
+ return __irqd_to_state(d) & IRQD_ACTIVATED;
+}
+
+static inline void irqd_set_activated(struct irq_data *d)
+{
+ __irqd_to_state(d) |= IRQD_ACTIVATED;
+}
+
+static inline void irqd_clr_activated(struct irq_data *d)
+{
+ __irqd_to_state(d) &= ~IRQD_ACTIVATED;
+}
+
#undef __irqd_to_state
static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
diff --git a/include/linux/jump_label_ratelimit.h b/include/linux/jump_label_ratelimit.h
index 089f70f..23da3af 100644
--- a/include/linux/jump_label_ratelimit.h
+++ b/include/linux/jump_label_ratelimit.h
@@ -14,6 +14,7 @@
#ifdef HAVE_JUMP_LABEL
extern void static_key_slow_dec_deferred(struct static_key_deferred *key);
+extern void static_key_deferred_flush(struct static_key_deferred *key);
extern void
jump_label_rate_limit(struct static_key_deferred *key, unsigned long rl);
@@ -26,6 +27,10 @@
STATIC_KEY_CHECK_USE();
static_key_slow_dec(&key->key);
}
+static inline void static_key_deferred_flush(struct static_key_deferred *key)
+{
+ STATIC_KEY_CHECK_USE();
+}
static inline void
jump_label_rate_limit(struct static_key_deferred *key,
unsigned long rl)
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 61d20c1..2546988 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -120,7 +120,7 @@
*/
struct mem_cgroup_per_node {
struct lruvec lruvec;
- unsigned long lru_size[NR_LRU_LISTS];
+ unsigned long lru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS];
struct mem_cgroup_reclaim_iter iter[DEF_PRIORITY + 1];
@@ -432,7 +432,7 @@
int mem_cgroup_select_victim_node(struct mem_cgroup *memcg);
void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,
- int nr_pages);
+ int zid, int nr_pages);
unsigned long mem_cgroup_node_nr_lru_pages(struct mem_cgroup *memcg,
int nid, unsigned int lru_mask);
@@ -441,9 +441,23 @@
unsigned long mem_cgroup_get_lru_size(struct lruvec *lruvec, enum lru_list lru)
{
struct mem_cgroup_per_node *mz;
+ unsigned long nr_pages = 0;
+ int zid;
mz = container_of(lruvec, struct mem_cgroup_per_node, lruvec);
- return mz->lru_size[lru];
+ for (zid = 0; zid < MAX_NR_ZONES; zid++)
+ nr_pages += mz->lru_zone_size[zid][lru];
+ return nr_pages;
+}
+
+static inline
+unsigned long mem_cgroup_get_zone_lru_size(struct lruvec *lruvec,
+ enum lru_list lru, int zone_idx)
+{
+ struct mem_cgroup_per_node *mz;
+
+ mz = container_of(lruvec, struct mem_cgroup_per_node, lruvec);
+ return mz->lru_zone_size[zone_idx][lru];
}
void mem_cgroup_handle_over_high(void);
@@ -671,6 +685,12 @@
{
return 0;
}
+static inline
+unsigned long mem_cgroup_get_zone_lru_size(struct lruvec *lruvec,
+ enum lru_list lru, int zone_idx)
+{
+ return 0;
+}
static inline unsigned long
mem_cgroup_node_nr_lru_pages(struct mem_cgroup *memcg,
diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
index 01033fa..134a2f6 100644
--- a/include/linux/memory_hotplug.h
+++ b/include/linux/memory_hotplug.h
@@ -85,7 +85,8 @@
extern int add_one_highpage(struct page *page, int pfn, int bad_ppro);
/* VM interface that may be used by firmware interface */
extern int online_pages(unsigned long, unsigned long, int);
-extern int test_pages_in_a_zone(unsigned long, unsigned long);
+extern int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn,
+ unsigned long *valid_start, unsigned long *valid_end);
extern void __offline_isolated_pages(unsigned long, unsigned long);
typedef void (*online_page_callback_t)(struct page *page);
@@ -284,7 +285,7 @@
unsigned long map_offset);
extern struct page *sparse_decode_mem_map(unsigned long coded_mem_map,
unsigned long pnum);
-extern int zone_can_shift(unsigned long pfn, unsigned long nr_pages,
- enum zone_type target);
+extern bool zone_can_shift(unsigned long pfn, unsigned long nr_pages,
+ enum zone_type target, int *zone_shift);
#endif /* __LINUX_MEMORY_HOTPLUG_H */
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 46c927f..2d191bf 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1271,6 +1271,8 @@
unsigned int gup_flags);
extern int access_remote_vm(struct mm_struct *mm, unsigned long addr,
void *buf, int len, unsigned int gup_flags);
+extern int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
+ unsigned long addr, void *buf, int len, unsigned int gup_flags);
long get_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm,
unsigned long start, unsigned long nr_pages,
diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h
index 71613e8..41d376e 100644
--- a/include/linux/mm_inline.h
+++ b/include/linux/mm_inline.h
@@ -39,7 +39,7 @@
{
__update_lru_size(lruvec, lru, zid, nr_pages);
#ifdef CONFIG_MEMCG
- mem_cgroup_update_lru_size(lruvec, lru, nr_pages);
+ mem_cgroup_update_lru_size(lruvec, lru, zid, nr_pages);
#endif
}
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 4d740f2..3139ea4 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -480,6 +480,7 @@
*/
struct task_struct __rcu *owner;
#endif
+ struct user_namespace *user_ns;
/* store ref to file /proc/<pid>/exe symlink points to */
struct file __rcu *exe_file;
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index ea4019c..46a4b79 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -142,7 +142,6 @@
/* Allow other commands during this ongoing data transfer or busy wait */
bool cap_cmd_during_tfr;
-
ktime_t io_start;
#ifdef CONFIG_BLOCK
int lat_hist_enabled;
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 0f088f3..f99c993 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -972,12 +972,16 @@
* @zonelist - The zonelist to search for a suitable zone
* @highest_zoneidx - The zone index of the highest zone to return
* @nodes - An optional nodemask to filter the zonelist with
- * @zone - The first suitable zone found is returned via this parameter
+ * @return - Zoneref pointer for the first suitable zone found (see below)
*
* This function returns the first zone at or below a given zone index that is
* within the allowed nodemask. The zoneref returned is a cursor that can be
* used to iterate the zonelist with next_zones_zonelist by advancing it by
* one before calling.
+ *
+ * When no eligible zone is found, zoneref->zone is NULL (zoneref itself is
+ * never NULL). This may happen either genuinely, or due to concurrent nodemask
+ * update due to cpuset modification.
*/
static inline struct zoneref *first_zones_zonelist(struct zonelist *zonelist,
enum zone_type highest_zoneidx,
diff --git a/include/linux/mount.h b/include/linux/mount.h
index 1172cce..4f33341 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -67,6 +67,7 @@
struct dentry *mnt_root; /* root of the mounted tree */
struct super_block *mnt_sb; /* pointer to superblock */
int mnt_flags;
+ void *data;
};
struct file; /* forward dec */
diff --git a/include/linux/namei.h b/include/linux/namei.h
index a2866f6..cf437f5 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -82,6 +82,7 @@
const char *, unsigned int, struct path *);
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
+extern struct dentry *lookup_one_len2(const char *, struct vfsmount *mnt, struct dentry *, int);
extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int);
extern int follow_down_one(struct path *);
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 7c0f6ad..d213c76 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2502,14 +2502,19 @@
return NAPI_GRO_CB(skb)->frag0_len < hlen;
}
+static inline void skb_gro_frag0_invalidate(struct sk_buff *skb)
+{
+ NAPI_GRO_CB(skb)->frag0 = NULL;
+ NAPI_GRO_CB(skb)->frag0_len = 0;
+}
+
static inline void *skb_gro_header_slow(struct sk_buff *skb, unsigned int hlen,
unsigned int offset)
{
if (!pskb_may_pull(skb, hlen))
return NULL;
- NAPI_GRO_CB(skb)->frag0 = NULL;
- NAPI_GRO_CB(skb)->frag0_len = 0;
+ skb_gro_frag0_invalidate(skb);
return skb->data + offset;
}
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 9094faf..039e76e 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -282,7 +282,7 @@
static inline bool seqid_mutating_err(u32 err)
{
- /* rfc 3530 section 8.1.5: */
+ /* See RFC 7530, section 9.1.7 */
switch (err) {
case NFS4ERR_STALE_CLIENTID:
case NFS4ERR_STALE_STATEID:
@@ -291,6 +291,7 @@
case NFS4ERR_BADXDR:
case NFS4ERR_RESOURCE:
case NFS4ERR_NOFILEHANDLE:
+ case NFS4ERR_MOVED:
return false;
};
return true;
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index c58752f..f020ab4 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2256,12 +2256,29 @@
#define PCI_DEVICE_ID_ZOLTRIX_2BD0 0x2bd0
#define PCI_VENDOR_ID_MELLANOX 0x15b3
-#define PCI_DEVICE_ID_MELLANOX_TAVOR 0x5a44
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX3 0x1003
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX3_PRO 0x1007
+#define PCI_DEVICE_ID_MELLANOX_CONNECTIB 0x1011
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX4 0x1013
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX4_LX 0x1015
+#define PCI_DEVICE_ID_MELLANOX_TAVOR 0x5a44
#define PCI_DEVICE_ID_MELLANOX_TAVOR_BRIDGE 0x5a46
-#define PCI_DEVICE_ID_MELLANOX_ARBEL_COMPAT 0x6278
-#define PCI_DEVICE_ID_MELLANOX_ARBEL 0x6282
-#define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c
-#define PCI_DEVICE_ID_MELLANOX_SINAI 0x6274
+#define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c
+#define PCI_DEVICE_ID_MELLANOX_SINAI 0x6274
+#define PCI_DEVICE_ID_MELLANOX_ARBEL_COMPAT 0x6278
+#define PCI_DEVICE_ID_MELLANOX_ARBEL 0x6282
+#define PCI_DEVICE_ID_MELLANOX_HERMON_SDR 0x6340
+#define PCI_DEVICE_ID_MELLANOX_HERMON_DDR 0x634a
+#define PCI_DEVICE_ID_MELLANOX_HERMON_QDR 0x6354
+#define PCI_DEVICE_ID_MELLANOX_HERMON_EN 0x6368
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX_EN 0x6372
+#define PCI_DEVICE_ID_MELLANOX_HERMON_DDR_GEN2 0x6732
+#define PCI_DEVICE_ID_MELLANOX_HERMON_QDR_GEN2 0x673c
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX_EN_5_GEN2 0x6746
+#define PCI_DEVICE_ID_MELLANOX_HERMON_EN_GEN2 0x6750
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX_EN_T_GEN2 0x675a
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX_EN_GEN2 0x6764
+#define PCI_DEVICE_ID_MELLANOX_CONNECTX2 0x676e
#define PCI_VENDOR_ID_DFI 0x15bd
diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h
index 1c7eec0..3a481a4 100644
--- a/include/linux/percpu-refcount.h
+++ b/include/linux/percpu-refcount.h
@@ -204,7 +204,7 @@
static inline bool percpu_ref_tryget(struct percpu_ref *ref)
{
unsigned long __percpu *percpu_count;
- int ret;
+ bool ret;
rcu_read_lock_sched();
@@ -238,7 +238,7 @@
static inline bool percpu_ref_tryget_live(struct percpu_ref *ref)
{
unsigned long __percpu *percpu_count;
- int ret = false;
+ bool ret = false;
rcu_read_lock_sched();
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index bca2615..f6bc765 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -19,6 +19,7 @@
struct dev_pm_opp;
struct device;
+struct opp_table;
enum dev_pm_opp_event {
OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE,
@@ -62,8 +63,8 @@
void dev_pm_opp_put_supported_hw(struct device *dev);
int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
void dev_pm_opp_put_prop_name(struct device *dev);
-int dev_pm_opp_set_regulator(struct device *dev, const char *name);
-void dev_pm_opp_put_regulator(struct device *dev);
+struct opp_table *dev_pm_opp_set_regulator(struct device *dev, const char *name);
+void dev_pm_opp_put_regulator(struct opp_table *opp_table);
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
@@ -170,12 +171,12 @@
static inline void dev_pm_opp_put_prop_name(struct device *dev) {}
-static inline int dev_pm_opp_set_regulator(struct device *dev, const char *name)
+static inline struct opp_table *dev_pm_opp_set_regulator(struct device *dev, const char *name)
{
- return -ENOTSUPP;
+ return ERR_PTR(-ENOTSUPP);
}
-static inline void dev_pm_opp_put_regulator(struct device *dev) {}
+static inline void dev_pm_opp_put_regulator(struct opp_table *opp_table) {}
static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
{
diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h
index e30deb0..bed9557 100644
--- a/include/linux/power/bq27xxx_battery.h
+++ b/include/linux/power/bq27xxx_battery.h
@@ -4,7 +4,8 @@
enum bq27xxx_chip {
BQ27000 = 1, /* bq27000, bq27200 */
BQ27010, /* bq27010, bq27210 */
- BQ27500, /* bq27500, bq27510, bq27520 */
+ BQ27500, /* bq27500 */
+ BQ27510, /* bq27510, bq27520 */
BQ27530, /* bq27530, bq27531 */
BQ27541, /* bq27541, bq27542, bq27546, bq27742 */
BQ27545, /* bq27545 */
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index 504c98a..e0e5393 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -8,6 +8,9 @@
#include <linux/pid_namespace.h> /* For task_active_pid_ns. */
#include <uapi/linux/ptrace.h>
+extern int ptrace_access_vm(struct task_struct *tsk, unsigned long addr,
+ void *buf, int len, unsigned int gup_flags);
+
/*
* Ptrace flags
*
@@ -19,7 +22,6 @@
#define PT_SEIZED 0x00010000 /* SEIZE used, enable new behavior */
#define PT_PTRACED 0x00000001
#define PT_DTRACE 0x00000002 /* delayed trace (used on m68k, i386) */
-#define PT_PTRACE_CAP 0x00000004 /* ptracer can follow suid-exec */
#define PT_OPT_FLAG_SHIFT 3
/* PT_TRACE_* event enable flags */
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h
index 321f9ed..01f71e1 100644
--- a/include/linux/rcupdate.h
+++ b/include/linux/rcupdate.h
@@ -444,6 +444,10 @@
#error "Unknown RCU implementation specified to kernel configuration"
#endif
+#define RCU_SCHEDULER_INACTIVE 0
+#define RCU_SCHEDULER_INIT 1
+#define RCU_SCHEDULER_RUNNING 2
+
/*
* init_rcu_head_on_stack()/destroy_rcu_head_on_stack() are needed for dynamic
* initialization and destruction of rcu_head on the stack. rcu_head structures
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 05d342d..177f952 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -173,6 +173,9 @@
extern unsigned long nr_iowait(void);
extern unsigned long nr_iowait_cpu(int cpu);
extern void get_iowait_load(unsigned long *nr_waiters, unsigned long *load);
+#ifdef CONFIG_CPU_QUIET
+extern u64 nr_running_integral(unsigned int cpu);
+#endif
extern void sched_update_nr_prod(int cpu, long delta, bool inc);
extern void sched_get_nr_running_avg(int *avg, int *iowait_avg, int *big_avg);
@@ -1044,6 +1047,14 @@
#define SCHED_CAPACITY_SHIFT SCHED_FIXEDPOINT_SHIFT
#define SCHED_CAPACITY_SCALE (1L << SCHED_CAPACITY_SHIFT)
+struct sched_capacity_reqs {
+ unsigned long cfs;
+ unsigned long rt;
+ unsigned long dl;
+
+ unsigned long total;
+};
+
/*
* Wake-queues are lists of tasks with a pending wakeup, whose
* callers have already marked the task as woken internally,
@@ -1107,6 +1118,7 @@
#define SD_PREFER_SIBLING 0x1000 /* Prefer to place tasks in a sibling domain */
#define SD_OVERLAP 0x2000 /* sched_domains of this level overlap */
#define SD_NUMA 0x4000 /* cross-node balancing */
+#define SD_SHARE_CAP_STATES 0x8000 /* Domain members share capacity state */
#ifdef CONFIG_SCHED_SMT
static inline int cpu_smt_flags(void)
@@ -1139,6 +1151,24 @@
extern int sched_domain_level_max;
+struct capacity_state {
+ unsigned long cap; /* compute capacity */
+ unsigned long power; /* power consumption at this compute capacity */
+};
+
+struct idle_state {
+ unsigned long power; /* power consumption in this idle state */
+};
+
+struct sched_group_energy {
+ unsigned int nr_idle_states; /* number of idle states */
+ struct idle_state *idle_states; /* ptr to idle state array */
+ unsigned int nr_cap_states; /* number of capacity states */
+ struct capacity_state *cap_states; /* ptr to capacity state array */
+};
+
+unsigned long capacity_curr_of(int cpu);
+
struct sched_group;
struct sched_domain_shared {
@@ -1246,6 +1276,8 @@
typedef const struct cpumask *(*sched_domain_mask_f)(int cpu);
typedef int (*sched_domain_flags_f)(void);
+typedef
+const struct sched_group_energy * const(*sched_domain_energy_f)(int cpu);
#define SDTL_OVERLAP 0x01
@@ -1259,6 +1291,7 @@
struct sched_domain_topology_level {
sched_domain_mask_f mask;
sched_domain_flags_f sd_flags;
+ sched_domain_energy_f energy;
int flags;
int numa_level;
struct sd_data data;
@@ -1638,6 +1671,7 @@
struct list_head grp_list;
u64 cpu_cycles;
#endif
+
#ifdef CONFIG_CGROUP_SCHED
struct task_group *sched_task_group;
#endif
@@ -1789,6 +1823,7 @@
struct list_head cpu_timers[3];
/* process credentials */
+ const struct cred __rcu *ptracer_cred; /* Tracer's credentials at attach */
const struct cred __rcu *real_cred; /* objective and real subjective task
* credentials (COW) */
const struct cred __rcu *cred; /* effective (overridable) subjective task
diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h
index 00101b3..ae9032a 100644
--- a/include/linux/sched/sysctl.h
+++ b/include/linux/sched/sysctl.h
@@ -18,6 +18,16 @@
extern unsigned int sysctl_sched_min_granularity;
extern unsigned int sysctl_sched_wakeup_granularity;
extern unsigned int sysctl_sched_child_runs_first;
+extern unsigned int sysctl_sched_is_big_little;
+extern unsigned int sysctl_sched_sync_hint_enable;
+extern unsigned int sysctl_sched_initial_task_util;
+extern unsigned int sysctl_sched_cstate_aware;
+#ifdef CONFIG_SCHED_WALT
+extern unsigned int sysctl_sched_use_walt_cpu_util;
+extern unsigned int sysctl_sched_use_walt_task_util;
+extern unsigned int sysctl_sched_walt_init_task_load_pct;
+extern unsigned int sysctl_sched_walt_cpu_high_irqload;
+#endif
#ifdef CONFIG_SCHED_HMP
diff --git a/include/linux/sched_energy.h b/include/linux/sched_energy.h
new file mode 100644
index 0000000..1daf3e1
--- /dev/null
+++ b/include/linux/sched_energy.h
@@ -0,0 +1,44 @@
+#ifndef _LINUX_SCHED_ENERGY_H
+#define _LINUX_SCHED_ENERGY_H
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+/*
+ * There doesn't seem to be an NR_CPUS style max number of sched domain
+ * levels so here's an arbitrary constant one for the moment.
+ *
+ * The levels alluded to here correspond to entries in struct
+ * sched_domain_topology_level that are meant to be populated by arch
+ * specific code (topology.c).
+ */
+#define NR_SD_LEVELS 8
+
+#define SD_LEVEL0 0
+#define SD_LEVEL1 1
+#define SD_LEVEL2 2
+#define SD_LEVEL3 3
+#define SD_LEVEL4 4
+#define SD_LEVEL5 5
+#define SD_LEVEL6 6
+#define SD_LEVEL7 7
+
+/*
+ * Convenience macro for iterating through said sd levels.
+ */
+#define for_each_possible_sd_level(level) \
+ for (level = 0; level < NR_SD_LEVELS; level++)
+
+#ifdef CONFIG_SMP
+
+extern struct sched_group_energy *sge_array[NR_CPUS][NR_SD_LEVELS];
+
+void init_sched_energy_costs(void);
+
+#else
+
+#define init_sched_energy_costs() do { } while (0)
+
+#endif /* CONFIG_SMP */
+
+#endif
diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index 85cc819..333ad11 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -216,5 +216,6 @@
void rpc_clnt_xprt_switch_add_xprt(struct rpc_clnt *, struct rpc_xprt *);
bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt,
const struct sockaddr *sap);
+void rpc_cleanup_clids(void);
#endif /* __KERNEL__ */
#endif /* _LINUX_SUNRPC_CLNT_H */
diff --git a/include/linux/sunrpc/svc_xprt.h b/include/linux/sunrpc/svc_xprt.h
index e5d1934..7440290 100644
--- a/include/linux/sunrpc/svc_xprt.h
+++ b/include/linux/sunrpc/svc_xprt.h
@@ -66,6 +66,7 @@
#define XPT_LISTENER 10 /* listening endpoint */
#define XPT_CACHE_AUTH 11 /* cache auth info */
#define XPT_LOCAL 12 /* connection from loopback interface */
+#define XPT_KILL_TEMP 13 /* call xpo_kill_temp_xprt before closing */
struct svc_serv *xpt_server; /* service for transport */
atomic_t xpt_reserved; /* space on outq that is rsvd */
diff --git a/include/linux/swap.h b/include/linux/swap.h
index a56523c..55ff559 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -150,8 +150,9 @@
SWP_FILE = (1 << 7), /* set after swap_activate success */
SWP_AREA_DISCARD = (1 << 8), /* single-time swap area discards */
SWP_PAGE_DISCARD = (1 << 9), /* freed swap page-cluster discards */
+ SWP_STABLE_WRITES = (1 << 10), /* no overwrite PG_writeback pages */
/* add others here before... */
- SWP_SCANNING = (1 << 10), /* refcount in scan_swap_map */
+ SWP_SCANNING = (1 << 11), /* refcount in scan_swap_map */
};
#define SWAP_CLUSTER_MAX 32UL
diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h
index 5f81f8a..d261353 100644
--- a/include/linux/swiotlb.h
+++ b/include/linux/swiotlb.h
@@ -9,7 +9,13 @@
struct page;
struct scatterlist;
-extern int swiotlb_force;
+enum swiotlb_force {
+ SWIOTLB_NORMAL, /* Default - depending on HW DMA mask etc. */
+ SWIOTLB_FORCE, /* swiotlb=force */
+ SWIOTLB_NO_FORCE, /* swiotlb=noforce */
+};
+
+extern enum swiotlb_force swiotlb_force;
/*
* Maximum allowable number of contiguous slabs to map,
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index a17ae7b..647532b 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -62,8 +62,13 @@
/* TCP Fast Open Cookie as stored in memory */
struct tcp_fastopen_cookie {
+ union {
+ u8 val[TCP_FASTOPEN_COOKIE_MAX];
+#if IS_ENABLED(CONFIG_IPV6)
+ struct in6_addr addr;
+#endif
+ };
s8 len;
- u8 val[TCP_FASTOPEN_COOKIE_MAX];
bool exp; /* In RFC6994 experimental option format */
};
diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h
index 09168c5..361f8bf 100644
--- a/include/linux/timekeeping.h
+++ b/include/linux/timekeeping.h
@@ -249,6 +249,7 @@
extern u64 ktime_get_mono_fast_ns(void);
extern u64 ktime_get_raw_fast_ns(void);
+extern u64 ktime_get_boot_fast_ns(void);
/*
* Timespec interfaces utilizing the ktime based ones
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 509a8f4..fd09a1b 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -547,7 +547,9 @@
*/
static inline size_t usb_ep_align(struct usb_ep *ep, size_t len)
{
- return round_up(len, (size_t)le16_to_cpu(ep->desc->wMaxPacketSize));
+ int max_packet_size = (size_t)usb_endpoint_maxp(ep->desc) & 0x7ff;
+
+ return round_up(len, max_packet_size);
}
/**
diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
index 1c912f8..f211c34 100644
--- a/include/linux/virtio_net.h
+++ b/include/linux/virtio_net.h
@@ -56,7 +56,8 @@
static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb,
struct virtio_net_hdr *hdr,
- bool little_endian)
+ bool little_endian,
+ bool has_data_valid)
{
memset(hdr, 0, sizeof(*hdr));
@@ -91,7 +92,8 @@
skb_checksum_start_offset(skb));
hdr->csum_offset = __cpu_to_virtio16(little_endian,
skb->csum_offset);
- } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
+ } else if (has_data_valid &&
+ skb->ip_summed == CHECKSUM_UNNECESSARY) {
hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID;
} /* else everything is zero */
diff --git a/include/media/msm_sde_rotator.h b/include/media/msm_sde_rotator.h
new file mode 100644
index 0000000..a99fbc9
--- /dev/null
+++ b/include/media/msm_sde_rotator.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __MSM_SDE_ROTATOR__
+#define __MSM_SDE_ROTATOR__
+
+#include <uapi/media/msm_sde_rotator.h>
+
+#endif /* __MSM_SDE_ROTATOR__ */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 71d9266..677a047 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -4586,6 +4586,17 @@
void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss);
/**
+ * cfg80211_abandon_assoc - notify cfg80211 of abandoned association attempt
+ * @dev: network device
+ * @bss: The BSS entry with which association was abandoned.
+ *
+ * Call this whenever - for reasons reported through other API, like deauth RX,
+ * an association attempt was abandoned.
+ * This function may sleep. The caller must hold the corresponding wdev's mutex.
+ */
+void cfg80211_abandon_assoc(struct net_device *dev, struct cfg80211_bss *bss);
+
+/**
* cfg80211_tx_mlme_mgmt - notification of transmitted deauth/disassoc frame
* @dev: network device
* @buf: 802.11 frame (header + body)
diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h
index 521885c..261202f 100644
--- a/include/net/fib_rules.h
+++ b/include/net/fib_rules.h
@@ -8,6 +8,11 @@
#include <net/flow.h>
#include <net/rtnetlink.h>
+struct fib_kuid_range {
+ kuid_t start;
+ kuid_t end;
+};
+
struct fib_rule {
struct list_head list;
int iifindex;
@@ -30,8 +35,7 @@
int suppress_prefixlen;
char iifname[IFNAMSIZ];
char oifname[IFNAMSIZ];
- kuid_t uid_start;
- kuid_t uid_end;
+ struct fib_kuid_range uid_range;
struct rcu_head rcu;
};
@@ -96,7 +100,8 @@
[FRA_SUPPRESS_PREFIXLEN] = { .type = NLA_U32 }, \
[FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32 }, \
[FRA_GOTO] = { .type = NLA_U32 }, \
- [FRA_L3MDEV] = { .type = NLA_U8 }
+ [FRA_L3MDEV] = { .type = NLA_U8 }, \
+ [FRA_UID_RANGE] = { .len = sizeof(struct fib_rule_uid_range) }
static inline void fib_rule_get(struct fib_rule *rule)
{
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index fc89e35..9dc2c18 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -142,7 +142,8 @@
void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, int oif,
u32 mark, kuid_t uid);
void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu);
-void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark);
+void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
+ kuid_t uid);
void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
u32 mark);
void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk);
diff --git a/include/net/lwtunnel.h b/include/net/lwtunnel.h
index ea3f80f..fc7c0db 100644
--- a/include/net/lwtunnel.h
+++ b/include/net/lwtunnel.h
@@ -43,13 +43,12 @@
int (*get_encap_size)(struct lwtunnel_state *lwtstate);
int (*cmp_encap)(struct lwtunnel_state *a, struct lwtunnel_state *b);
int (*xmit)(struct sk_buff *skb);
+
+ struct module *owner;
};
#ifdef CONFIG_LWTUNNEL
-static inline void lwtstate_free(struct lwtunnel_state *lws)
-{
- kfree(lws);
-}
+void lwtstate_free(struct lwtunnel_state *lws);
static inline struct lwtunnel_state *
lwtstate_get(struct lwtunnel_state *lws)
@@ -106,6 +105,8 @@
unsigned int num);
int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *op,
unsigned int num);
+int lwtunnel_valid_encap_type(u16 encap_type);
+int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int len);
int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
struct nlattr *encap,
unsigned int family, const void *cfg,
@@ -169,6 +170,15 @@
return -EOPNOTSUPP;
}
+static inline int lwtunnel_valid_encap_type(u16 encap_type)
+{
+ return -EOPNOTSUPP;
+}
+static inline int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int len)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
struct nlattr *encap,
unsigned int family, const void *cfg,
diff --git a/include/net/route.h b/include/net/route.h
index e56c5a4..c0874c8 100644
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -153,8 +153,7 @@
flowi4_init_output(fl4, oif, sk ? sk->sk_mark : 0, tos,
RT_SCOPE_UNIVERSE, proto,
sk ? inet_sk_flowi_flags(sk) : 0,
- daddr, saddr, dport, sport,
- sk ? sock_i_uid(sk) : GLOBAL_ROOT_UID);
+ daddr, saddr, dport, sport, sock_net_uid(net, sk));
if (sk)
security_sk_classify_flow(sk, flowi4_to_flowi(fl4));
return ip_route_output_flow(net, fl4, sk);
@@ -271,7 +270,7 @@
flowi4_init_output(fl4, oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE,
protocol, flow_flags, dst, src, dport, sport,
- sock_i_uid(sk));
+ sk->sk_uid);
}
static inline struct rtable *ip_route_connect(struct flowi4 *fl4,
diff --git a/include/net/sock.h b/include/net/sock.h
index 92b2697..97f8ed2 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -419,6 +419,7 @@
u32 sk_max_ack_backlog;
__u32 sk_priority;
__u32 sk_mark;
+ kuid_t sk_uid;
struct pid *sk_peer_pid;
const struct cred *sk_peer_cred;
long sk_rcvtimeo;
@@ -1651,6 +1652,7 @@
sk->sk_wq = parent->wq;
parent->sk = sk;
sk_set_socket(sk, parent);
+ sk->sk_uid = SOCK_INODE(parent)->i_uid;
security_sock_graft(sk, parent);
write_unlock_bh(&sk->sk_callback_lock);
}
@@ -1658,6 +1660,11 @@
kuid_t sock_i_uid(struct sock *sk);
unsigned long sock_i_ino(struct sock *sk);
+static inline kuid_t sock_net_uid(const struct net *net, const struct sock *sk)
+{
+ return sk ? sk->sk_uid : make_kuid(net->user_ns, 0);
+}
+
static inline u32 net_tx_rndhash(void)
{
u32 v = prandom_u32();
diff --git a/include/rdma/ib_addr.h b/include/rdma/ib_addr.h
index 931a47b..1beab55 100644
--- a/include/rdma/ib_addr.h
+++ b/include/rdma/ib_addr.h
@@ -205,10 +205,12 @@
dev = dev_get_by_index(&init_net, dev_addr->bound_dev_if);
if (dev) {
- ip4 = (struct in_device *)dev->ip_ptr;
- if (ip4 && ip4->ifa_list && ip4->ifa_list->ifa_address)
+ ip4 = in_dev_get(dev);
+ if (ip4 && ip4->ifa_list && ip4->ifa_list->ifa_address) {
ipv6_addr_set_v4mapped(ip4->ifa_list->ifa_address,
(struct in6_addr *)gid);
+ in_dev_put(ip4);
+ }
dev_put(dev);
}
}
diff --git a/include/soc/qcom/cmd-db.h b/include/soc/qcom/cmd-db.h
index a07e0aa..e2c72d1 100644
--- a/include/soc/qcom/cmd-db.h
+++ b/include/soc/qcom/cmd-db.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -96,6 +96,13 @@
* return cmd_db_hw_type enum on success, errno on error
*/
int cmd_db_get_slave_id(const char *resource_id);
+
+/**
+ * cmd_db_is_standalone - Returns if the command DB is standalone
+ *
+ * return 1 if command DB is standalone, 0 if not, errno otherwise.
+ */
+int cmd_db_is_standalone(void);
#else
static inline u32 cmd_db_get_addr(const char *resource_id)
@@ -132,5 +139,10 @@
{
return -ENODEV;
}
+
+int cmd_db_is_standalone(void)
+{
+ return -ENODEV;
+}
#endif
#endif
diff --git a/include/soc/qcom/event_timer.h b/include/soc/qcom/event_timer.h
new file mode 100644
index 0000000..d54d8ab
--- /dev/null
+++ b/include/soc/qcom/event_timer.h
@@ -0,0 +1,80 @@
+/* Copyright (c) 2012, 2014,2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_EVENT_TIMER_H
+#define __ARCH_ARM_MACH_MSM_EVENT_TIMER_H
+
+#include <linux/hrtimer.h>
+
+struct event_timer_info;
+
+#ifdef CONFIG_MSM_EVENT_TIMER
+/**
+ * add_event_timer() : Add a wakeup event. Intended to be called
+ * by clients once. Returns a handle to be used
+ * for future transactions.
+ * @irq : Interrupt number to track affinity.
+ * @function : The callback function will be called when event
+ * timer expires.
+ * @data : Callback data provided by client.
+ */
+struct event_timer_info *add_event_timer(uint32_t irq,
+ void (*function)(void *), void *data);
+
+/** activate_event_timer() : Set the expiration time for an event in absolute
+ * ktime. This is a oneshot event timer, clients
+ * should call this again to set another expiration.
+ * @event : Event handle.
+ * @event_time : Event time in absolute ktime.
+ */
+void activate_event_timer(struct event_timer_info *event, ktime_t event_time);
+
+/**
+ * deactivate_event_timer() : Deactivate an event timer.
+ * @event: event handle.
+ */
+void deactivate_event_timer(struct event_timer_info *event);
+
+/**
+ * destroy_event_timer() : Free the event info data structure allocated during
+ * add_event_timer().
+ * @event: event handle.
+ */
+void destroy_event_timer(struct event_timer_info *event);
+
+/**
+ * get_next_event_timer() : Get the next wakeup event.
+ * returns a ktime value of the next
+ * expiring event.
+ */
+ktime_t get_next_event_time(int cpu);
+#else
+static inline void *add_event_timer(uint32_t irq, void (*function)(void *),
+ void *data)
+{
+ return NULL;
+}
+
+static inline void activate_event_timer(void *event, ktime_t event_time) {}
+
+static inline void deactivate_event_timer(void *event) {}
+
+static inline void destroy_event_timer(void *event) {}
+
+static inline ktime_t get_next_event_time(int cpu)
+{
+ return ns_to_ktime(0);
+}
+
+#endif /* CONFIG_MSM_EVENT_TIMER_MANAGER */
+#endif /* __ARCH_ARM_MACH_MSM_EVENT_TIMER_H */
diff --git a/include/soc/qcom/jtag.h b/include/soc/qcom/jtag.h
new file mode 100644
index 0000000..5719e05
--- /dev/null
+++ b/include/soc/qcom/jtag.h
@@ -0,0 +1,54 @@
+/* Copyright (c) 2012-2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MACH_JTAG_H
+#define __MACH_JTAG_H
+
+#if defined(CONFIG_MSM_JTAG) || defined(CONFIG_MSM_JTAG_MM) || \
+ defined(CONFIG_MSM_JTAGV8)
+extern void msm_jtag_save_state(void);
+extern void msm_jtag_restore_state(void);
+extern void msm_jtag_etm_save_state(void);
+extern void msm_jtag_etm_restore_state(void);
+extern bool msm_jtag_fuse_apps_access_disabled(void);
+#else
+static inline void msm_jtag_save_state(void) {}
+static inline void msm_jtag_restore_state(void) {}
+static inline void msm_jtag_etm_save_state(void) {}
+static inline void msm_jtag_etm_restore_state(void){}
+static inline bool msm_jtag_fuse_apps_access_disabled(void) { return false; }
+#endif
+#ifdef CONFIG_MSM_JTAGV8
+extern int msm_jtag_save_register(struct notifier_block *nb);
+extern int msm_jtag_save_unregister(struct notifier_block *nb);
+extern int msm_jtag_restore_register(struct notifier_block *nb);
+extern int msm_jtag_restore_unregister(struct notifier_block *nb);
+#else
+static inline int msm_jtag_save_register(struct notifier_block *nb)
+{
+ return 0;
+}
+static inline int msm_jtag_save_unregister(struct notifier_block *nb)
+{
+ return 0;
+}
+static inline int msm_jtag_restore_register(struct notifier_block *nb)
+{
+ return 0;
+}
+static inline int msm_jtag_restore_unregister(struct notifier_block *nb)
+{
+ return 0;
+}
+#endif
+
+#endif
diff --git a/include/soc/qcom/lpm-stats.h b/include/soc/qcom/lpm-stats.h
new file mode 100644
index 0000000..d8bd872
--- /dev/null
+++ b/include/soc/qcom/lpm-stats.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014-2015,2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_LPM_STATS_H
+#define __ARCH_ARM_MACH_MSM_LPM_STATS_H
+
+struct lpm_stats;
+
+#define MAX_STR_LEN 256
+
+struct lifo_stats {
+ uint32_t last_in;
+ uint32_t first_out;
+};
+
+struct lpm_stats {
+ char name[MAX_STR_LEN];
+ struct level_stats *time_stats;
+ uint32_t num_levels;
+ struct lifo_stats lifo;
+ struct lpm_stats *parent;
+ struct list_head sibling;
+ struct list_head child;
+ struct cpumask mask;
+ struct dentry *directory;
+ int64_t sleep_time;
+ bool is_cpu;
+};
+
+
+
+#ifdef CONFIG_MSM_IDLE_STATS
+struct lpm_stats *lpm_stats_config_level(const char *name,
+ const char **levels, int num_levels, struct lpm_stats *parent,
+ struct cpumask *mask);
+void lpm_stats_cluster_enter(struct lpm_stats *stats, uint32_t index);
+void lpm_stats_cluster_exit(struct lpm_stats *stats, uint32_t index,
+ bool success);
+void lpm_stats_cpu_enter(uint32_t index, uint64_t time);
+void lpm_stats_cpu_exit(uint32_t index, uint64_t time, bool success);
+void lpm_stats_suspend_enter(void);
+void lpm_stats_suspend_exit(void);
+#else
+static inline struct lpm_stats *lpm_stats_config_level(const char *name,
+ const char **levels, int num_levels, struct lpm_stats *parent,
+ struct cpumask *mask)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline void lpm_stats_cluster_enter(struct lpm_stats *stats,
+ uint32_t index)
+{ }
+
+static inline void lpm_stats_cluster_exit(struct lpm_stats *stats,
+ uint32_t index, bool success)
+{ }
+
+static inline void lpm_stats_cpu_enter(uint32_t index, uint64_t time)
+{ }
+
+static inline void lpm_stats_cpu_exit(uint32_t index, bool success,
+ uint64_t time)
+{ }
+
+static inline void lpm_stats_suspend_enter(void)
+{ }
+
+static inline void lpm_stats_suspend_exit(void)
+{ }
+#endif
+#endif /* __ARCH_ARM_MACH_MSM_LPM_STATS_H */
diff --git a/include/soc/qcom/msm-core.h b/include/soc/qcom/msm-core.h
new file mode 100644
index 0000000..cd44615
--- /dev/null
+++ b/include/soc/qcom/msm-core.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2014-2015,2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_CORE_H
+#define __ARCH_ARM_MACH_MSM_CORE_H
+#ifdef CONFIG_APSS_CORE_EA
+void set_cpu_throttled(struct cpumask *mask, bool throttling);
+struct blocking_notifier_head *get_power_update_notifier(void);
+#else
+static inline void set_cpu_throttled(struct cpumask *mask, bool throttling) {}
+struct blocking_notifier_head *get_power_update_notifier(void) {return NULL; }
+#endif
+#endif
+
diff --git a/include/soc/qcom/pm.h b/include/soc/qcom/pm.h
new file mode 100644
index 0000000..a82ada6
--- /dev/null
+++ b/include/soc/qcom/pm.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2009-2016, The Linux Foundation. All rights reserved.
+ * Author: San Mehat <san@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_PM_H
+#define __ARCH_ARM_MACH_MSM_PM_H
+
+#include <linux/types.h>
+#include <linux/cpuidle.h>
+#include <asm/smp_plat.h>
+#include <asm/barrier.h>
+#include <dt-bindings/msm/pm.h>
+
+#if !defined(CONFIG_SMP)
+#define msm_secondary_startup NULL
+#elif defined(CONFIG_CPU_V7)
+#define msm_secondary_startup secondary_startup
+#else
+#define msm_secondary_startup secondary_holding_pen
+#endif
+
+enum msm_pm_sleep_mode {
+ MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT,
+ MSM_PM_SLEEP_MODE_RETENTION,
+ MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE,
+ MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
+ MSM_PM_SLEEP_MODE_FASTPC,
+ MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND,
+ MSM_PM_SLEEP_MODE_NR,
+ MSM_PM_SLEEP_MODE_NOT_SELECTED,
+};
+
+enum msm_pm_l2_scm_flag {
+ MSM_SCM_L2_ON = 0,
+ MSM_SCM_L2_OFF = 1,
+ MSM_SCM_L2_GDHS = 3,
+ MSM_SCM_L3_PC_OFF = 4,
+};
+
+#define MSM_PM_MODE(cpu, mode_nr) ((cpu) *MSM_PM_SLEEP_MODE_NR + (mode_nr))
+
+struct msm_pm_time_params {
+ uint32_t latency_us;
+ uint32_t sleep_us;
+ uint32_t next_event_us;
+ uint32_t modified_time_us;
+};
+
+struct msm_pm_sleep_status_data {
+ void *base_addr;
+ uint32_t mask;
+};
+
+struct latency_level {
+ int affinity_level;
+ int reset_level;
+ const char *level_name;
+};
+
+/**
+ * lpm_cpu_pre_pc_cb(): API to get the L2 flag to pass to TZ
+ *
+ * @cpu: cpuid of the CPU going down.
+ *
+ * Returns the l2 flush flag enum that is passed down to TZ during power
+ * collaps
+ */
+enum msm_pm_l2_scm_flag lpm_cpu_pre_pc_cb(unsigned int cpu);
+
+/**
+ * msm_pm_sleep_mode_allow() - API to determine if sleep mode is allowed.
+ * @cpu: CPU on which to check for the sleep mode.
+ * @mode: Sleep Mode to check for.
+ * @idle: Idle or Suspend Sleep Mode.
+ *
+ * Helper function to determine if a Idle or Suspend
+ * Sleep mode is allowed for a specific CPU.
+ *
+ * Return: 1 for allowed; 0 if not allowed.
+ */
+int msm_pm_sleep_mode_allow(unsigned int cpu, unsigned int mode, bool idle);
+
+/**
+ * msm_pm_sleep_mode_supported() - API to determine if sleep mode is
+ * supported.
+ * @cpu: CPU on which to check for the sleep mode.
+ * @mode: Sleep Mode to check for.
+ * @idle: Idle or Suspend Sleep Mode.
+ *
+ * Helper function to determine if a Idle or Suspend
+ * Sleep mode is allowed and enabled for a specific CPU.
+ *
+ * Return: 1 for supported; 0 if not supported.
+ */
+int msm_pm_sleep_mode_supported(unsigned int cpu, unsigned int mode, bool idle);
+
+struct msm_pm_cpr_ops {
+ void (*cpr_suspend)(void);
+ void (*cpr_resume)(void);
+};
+
+void __init msm_pm_set_tz_retention_flag(unsigned int flag);
+void msm_pm_enable_retention(bool enable);
+bool msm_pm_retention_enabled(void);
+bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode, bool from_idle);
+static inline void msm_arch_idle(void)
+{
+ mb(); /* Flush */
+ wfi();
+}
+
+#ifdef CONFIG_MSM_PM
+
+void msm_pm_set_rpm_wakeup_irq(unsigned int irq);
+int msm_pm_wait_cpu_shutdown(unsigned int cpu);
+int __init msm_pm_sleep_status_init(void);
+void lpm_cpu_hotplug_enter(unsigned int cpu);
+s32 msm_cpuidle_get_deep_idle_latency(void);
+int msm_pm_collapse(unsigned long unused);
+
+/**
+ * lpm_get_latency() - API to get latency for a low power mode
+ * @latency_level: pointer to structure with below elements
+ * affinity_level: The level (CPU/L2/CCI etc.) for which the
+ * latency is required.
+ * LPM_AFF_LVL_CPU : CPU level
+ * LPM_AFF_LVL_L2 : L2 level
+ * LPM_AFF_LVL_CCI : CCI level
+ * reset_level: Can be passed "LPM_RESET_LVL_GDHS" for
+ * low power mode with control logic power collapse or
+ * "LPM_RESET_LVL_PC" for low power mode with control and
+ * memory logic power collapse or "LPM_RESET_LVL_RET" for
+ * retention mode.
+ * level_name: Pointer to the cluster name for which the latency
+ * is required or NULL if the minimum value out of all the
+ * clusters is to be returned. For CPU level, the name of the
+ * L2 cluster to be passed. For CCI it has no effect.
+ * @latency: address to get the latency value.
+ *
+ * latency value will be for the particular cluster or the minimum
+ * value out of all the clusters at the particular affinity_level
+ * and reset_level.
+ *
+ * Return: 0 for success; Error number for failure.
+ */
+int lpm_get_latency(struct latency_level *level, uint32_t *latency);
+
+#else
+static inline void msm_pm_set_rpm_wakeup_irq(unsigned int irq) {}
+static inline int msm_pm_wait_cpu_shutdown(unsigned int cpu) { return 0; }
+static inline int msm_pm_sleep_status_init(void) { return 0; };
+
+static inline void lpm_cpu_hotplug_enter(unsigned int cpu)
+{
+ msm_arch_idle();
+};
+
+static inline s32 msm_cpuidle_get_deep_idle_latency(void) { return 0; }
+#define msm_pm_collapse NULL
+
+static inline int lpm_get_latency(struct latency_level *level,
+ uint32_t *latency)
+{
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_HOTPLUG_CPU
+int msm_platform_secondary_init(unsigned int cpu);
+#else
+static inline int msm_platform_secondary_init(unsigned int cpu) { return 0; }
+#endif
+
+enum msm_pm_time_stats_id {
+ MSM_PM_STAT_REQUESTED_IDLE = 0,
+ MSM_PM_STAT_IDLE_SPIN,
+ MSM_PM_STAT_IDLE_WFI,
+ MSM_PM_STAT_RETENTION,
+ MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
+ MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE,
+ MSM_PM_STAT_IDLE_POWER_COLLAPSE,
+ MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE,
+ MSM_PM_STAT_SUSPEND,
+ MSM_PM_STAT_FAILED_SUSPEND,
+ MSM_PM_STAT_NOT_IDLE,
+ MSM_PM_STAT_COUNT
+};
+
+#ifdef CONFIG_MSM_IDLE_STATS
+void msm_pm_add_stats(enum msm_pm_time_stats_id *enable_stats, int size);
+void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t);
+void msm_pm_l2_add_stat(uint32_t id, int64_t t);
+#else
+static inline void msm_pm_add_stats(enum msm_pm_time_stats_id *enable_stats,
+ int size) {}
+static inline void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t) {}
+static inline void msm_pm_l2_add_stat(uint32_t id, int64_t t) {}
+#endif
+
+void msm_pm_set_cpr_ops(struct msm_pm_cpr_ops *ops);
+extern dma_addr_t msm_pc_debug_counters_phys;
+#endif /* __ARCH_ARM_MACH_MSM_PM_H */
diff --git a/include/soc/qcom/service-notifier.h b/include/soc/qcom/service-notifier.h
index 0106801..740f7f6 100644
--- a/include/soc/qcom/service-notifier.h
+++ b/include/soc/qcom/service-notifier.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -25,9 +25,12 @@
};
enum pd_subsys_state {
- CRASHED,
- SHUTDOWN,
- UNKNOWN,
+ ROOT_PD_DOWN,
+ ROOT_PD_UP,
+ ROOT_PD_ERR_FATAL,
+ ROOT_PD_WDOG_BITE,
+ ROOT_PD_SHUTDOWN,
+ USER_PD_STATE_CHANGE,
};
#if defined(CONFIG_MSM_SERVICE_NOTIFIER)
diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
new file mode 100644
index 0000000..1ed100b
--- /dev/null
+++ b/include/soc/qcom/spm.h
@@ -0,0 +1,147 @@
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_SPM_H
+#define __ARCH_ARM_MACH_MSM_SPM_H
+
+enum {
+ MSM_SPM_MODE_DISABLED,
+ MSM_SPM_MODE_CLOCK_GATING,
+ MSM_SPM_MODE_RETENTION,
+ MSM_SPM_MODE_GDHS,
+ MSM_SPM_MODE_POWER_COLLAPSE,
+ MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE,
+ MSM_SPM_MODE_FASTPC,
+ MSM_SPM_MODE_NR
+};
+
+enum msm_spm_avs_irq {
+ MSM_SPM_AVS_IRQ_MIN,
+ MSM_SPM_AVS_IRQ_MAX,
+};
+
+struct msm_spm_device;
+struct device_node;
+
+#if defined(CONFIG_MSM_SPM)
+
+int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm);
+int msm_spm_probe_done(void);
+int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel);
+int msm_spm_get_vdd(unsigned int cpu);
+int msm_spm_turn_on_cpu_rail(struct device_node *l2ccc_node,
+ unsigned int val, int cpu, int vctl_offset);
+struct msm_spm_device *msm_spm_get_device_by_name(const char *name);
+int msm_spm_config_low_power_mode(struct msm_spm_device *dev,
+ unsigned int mode, bool notify_rpm);
+int msm_spm_device_init(void);
+bool msm_spm_is_mode_avail(unsigned int mode);
+void msm_spm_dump_regs(unsigned int cpu);
+int msm_spm_is_avs_enabled(unsigned int cpu);
+int msm_spm_avs_enable(unsigned int cpu);
+int msm_spm_avs_disable(unsigned int cpu);
+int msm_spm_avs_set_limit(unsigned int cpu, uint32_t min_lvl,
+ uint32_t max_lvl);
+int msm_spm_avs_enable_irq(unsigned int cpu, enum msm_spm_avs_irq irq);
+int msm_spm_avs_disable_irq(unsigned int cpu, enum msm_spm_avs_irq irq);
+int msm_spm_avs_clear_irq(unsigned int cpu, enum msm_spm_avs_irq irq);
+
+#if defined(CONFIG_MSM_L2_SPM)
+
+/* Public functions */
+
+int msm_spm_apcs_set_phase(int cpu, unsigned int phase_cnt);
+int msm_spm_enable_fts_lpm(int cpu, uint32_t mode);
+
+#else
+
+static inline int msm_spm_apcs_set_phase(int cpu, unsigned int phase_cnt)
+{
+ return -ENODEV;
+}
+
+static inline int msm_spm_enable_fts_lpm(int cpu, uint32_t mode)
+{
+ return -ENODEV;
+}
+#endif /* defined(CONFIG_MSM_L2_SPM) */
+#else /* defined(CONFIG_MSM_SPM) */
+static inline int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm)
+{
+ return -ENODEV;
+}
+
+static inline int msm_spm_probe_done(void)
+{
+ return -ENODEV;
+}
+
+static inline int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel)
+{
+ return -ENODEV;
+}
+
+static inline int msm_spm_get_vdd(unsigned int cpu)
+{
+ return 0;
+}
+
+static inline int msm_spm_turn_on_cpu_rail(struct device_node *l2ccc_node,
+ unsigned int val, int cpu, int vctl_offset)
+{
+ return -ENODEV;
+}
+
+static inline int msm_spm_device_init(void)
+{
+ return -ENODEV;
+}
+
+static inline void msm_spm_dump_regs(unsigned int cpu)
+{ }
+
+static inline int msm_spm_config_low_power_mode(struct msm_spm_device *dev,
+ unsigned int mode, bool notify_rpm)
+{
+ return -ENODEV;
+}
+static inline struct msm_spm_device *msm_spm_get_device_by_name(
+ const char *name)
+{
+ return NULL;
+}
+
+static inline bool msm_spm_is_mode_avail(unsigned int mode)
+{
+ return false;
+}
+
+static inline int msm_spm_avs_enable_irq(unsigned int cpu,
+ enum msm_spm_avs_irq irq)
+{
+ return -ENODEV;
+}
+
+static inline int msm_spm_avs_disable_irq(unsigned int cpu,
+ enum msm_spm_avs_irq irq)
+{
+ return -ENODEV;
+}
+
+static inline int msm_spm_avs_clear_irq(unsigned int cpu,
+ enum msm_spm_avs_irq irq)
+{
+ return -ENODEV;
+}
+
+#endif /* defined (CONFIG_MSM_SPM) */
+#endif /* __ARCH_ARM_MACH_MSM_SPM_H */
diff --git a/include/sound/hdmi-codec.h b/include/sound/hdmi-codec.h
index 530c57b..915c435 100644
--- a/include/sound/hdmi-codec.h
+++ b/include/sound/hdmi-codec.h
@@ -36,10 +36,10 @@
HDMI_AC97,
HDMI_SPDIF,
} fmt;
- int bit_clk_inv:1;
- int frame_clk_inv:1;
- int bit_clk_master:1;
- int frame_clk_master:1;
+ unsigned int bit_clk_inv:1;
+ unsigned int frame_clk_inv:1;
+ unsigned int bit_clk_master:1;
+ unsigned int frame_clk_master:1;
};
/*
diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h
index e030d6f..6d7fe11 100644
--- a/include/trace/events/btrfs.h
+++ b/include/trace/events/btrfs.h
@@ -1162,22 +1162,26 @@
__entry->func, __entry->ordered_func, __entry->ordered_free)
);
-/* For situiations that the work is freed */
+/*
+ * For situiations when the work is freed, we pass fs_info and a tag that that
+ * matches address of the work structure so it can be paired with the
+ * scheduling event.
+ */
DECLARE_EVENT_CLASS(btrfs__work__done,
- TP_PROTO(struct btrfs_work *work),
+ TP_PROTO(struct btrfs_fs_info *fs_info, void *wtag),
- TP_ARGS(work),
+ TP_ARGS(fs_info, wtag),
TP_STRUCT__entry_btrfs(
- __field( void *, work )
+ __field( void *, wtag )
),
- TP_fast_assign_btrfs(btrfs_work_owner(work),
- __entry->work = work;
+ TP_fast_assign_btrfs(fs_info,
+ __entry->wtag = wtag;
),
- TP_printk_btrfs("work->%p", __entry->work)
+ TP_printk_btrfs("work->%p", __entry->wtag)
);
DEFINE_EVENT(btrfs__work, btrfs_work_queued,
@@ -1196,9 +1200,9 @@
DEFINE_EVENT(btrfs__work__done, btrfs_all_work_done,
- TP_PROTO(struct btrfs_work *work),
+ TP_PROTO(struct btrfs_fs_info *fs_info, void *wtag),
- TP_ARGS(work)
+ TP_ARGS(fs_info, wtag)
);
DEFINE_EVENT(btrfs__work, btrfs_ordered_sched,
diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h
index 951e6ca..faecc0b 100644
--- a/include/trace/events/cpufreq_interactive.h
+++ b/include/trace/events/cpufreq_interactive.h
@@ -8,102 +8,102 @@
DECLARE_EVENT_CLASS(set,
TP_PROTO(u32 cpu_id, unsigned long targfreq,
- unsigned long actualfreq),
+ unsigned long actualfreq),
TP_ARGS(cpu_id, targfreq, actualfreq),
TP_STRUCT__entry(
- __field( u32, cpu_id )
- __field(unsigned long, targfreq )
- __field(unsigned long, actualfreq )
- ),
+ __field(u32, cpu_id)
+ __field(unsigned long, targfreq)
+ __field(unsigned long, actualfreq)
+ ),
TP_fast_assign(
- __entry->cpu_id = (u32) cpu_id;
- __entry->targfreq = targfreq;
- __entry->actualfreq = actualfreq;
+ __entry->cpu_id = (u32)cpu_id;
+ __entry->targfreq = targfreq;
+ __entry->actualfreq = actualfreq;
),
TP_printk("cpu=%u targ=%lu actual=%lu",
- __entry->cpu_id, __entry->targfreq,
- __entry->actualfreq)
+ __entry->cpu_id, __entry->targfreq,
+ __entry->actualfreq)
);
DEFINE_EVENT(set, cpufreq_interactive_setspeed,
TP_PROTO(u32 cpu_id, unsigned long targfreq,
- unsigned long actualfreq),
+ unsigned long actualfreq),
TP_ARGS(cpu_id, targfreq, actualfreq)
);
DECLARE_EVENT_CLASS(loadeval,
- TP_PROTO(unsigned long cpu_id, unsigned long load,
- unsigned long curtarg, unsigned long curactual,
- unsigned long newtarg),
- TP_ARGS(cpu_id, load, curtarg, curactual, newtarg),
+ TP_PROTO(unsigned long cpu_id, unsigned long load,
+ unsigned long curtarg, unsigned long curactual,
+ unsigned long newtarg),
+ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg),
- TP_STRUCT__entry(
- __field(unsigned long, cpu_id )
- __field(unsigned long, load )
- __field(unsigned long, curtarg )
- __field(unsigned long, curactual )
- __field(unsigned long, newtarg )
- ),
+ TP_STRUCT__entry(
+ __field(unsigned long, cpu_id)
+ __field(unsigned long, load)
+ __field(unsigned long, curtarg)
+ __field(unsigned long, curactual)
+ __field(unsigned long, newtarg)
+ ),
- TP_fast_assign(
- __entry->cpu_id = cpu_id;
- __entry->load = load;
- __entry->curtarg = curtarg;
- __entry->curactual = curactual;
- __entry->newtarg = newtarg;
- ),
+ TP_fast_assign(
+ __entry->cpu_id = cpu_id;
+ __entry->load = load;
+ __entry->curtarg = curtarg;
+ __entry->curactual = curactual;
+ __entry->newtarg = newtarg;
+ ),
- TP_printk("cpu=%lu load=%lu cur=%lu actual=%lu targ=%lu",
- __entry->cpu_id, __entry->load, __entry->curtarg,
- __entry->curactual, __entry->newtarg)
+ TP_printk("cpu=%lu load=%lu cur=%lu actual=%lu targ=%lu",
+ __entry->cpu_id, __entry->load, __entry->curtarg,
+ __entry->curactual, __entry->newtarg)
);
DEFINE_EVENT(loadeval, cpufreq_interactive_target,
- TP_PROTO(unsigned long cpu_id, unsigned long load,
- unsigned long curtarg, unsigned long curactual,
- unsigned long newtarg),
- TP_ARGS(cpu_id, load, curtarg, curactual, newtarg)
+ TP_PROTO(unsigned long cpu_id, unsigned long load,
+ unsigned long curtarg, unsigned long curactual,
+ unsigned long newtarg),
+ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg)
);
DEFINE_EVENT(loadeval, cpufreq_interactive_already,
- TP_PROTO(unsigned long cpu_id, unsigned long load,
- unsigned long curtarg, unsigned long curactual,
- unsigned long newtarg),
- TP_ARGS(cpu_id, load, curtarg, curactual, newtarg)
+ TP_PROTO(unsigned long cpu_id, unsigned long load,
+ unsigned long curtarg, unsigned long curactual,
+ unsigned long newtarg),
+ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg)
);
DEFINE_EVENT(loadeval, cpufreq_interactive_notyet,
- TP_PROTO(unsigned long cpu_id, unsigned long load,
- unsigned long curtarg, unsigned long curactual,
- unsigned long newtarg),
- TP_ARGS(cpu_id, load, curtarg, curactual, newtarg)
+ TP_PROTO(unsigned long cpu_id, unsigned long load,
+ unsigned long curtarg, unsigned long curactual,
+ unsigned long newtarg),
+ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg)
);
TRACE_EVENT(cpufreq_interactive_boost,
- TP_PROTO(const char *s),
- TP_ARGS(s),
- TP_STRUCT__entry(
- __string(s, s)
- ),
- TP_fast_assign(
- __assign_str(s, s);
- ),
- TP_printk("%s", __get_str(s))
+ TP_PROTO(const char *s),
+ TP_ARGS(s),
+ TP_STRUCT__entry(
+ __string(s, s)
+ ),
+ TP_fast_assign(
+ __assign_str(s, s);
+ ),
+ TP_printk("%s", __get_str(s))
);
TRACE_EVENT(cpufreq_interactive_unboost,
- TP_PROTO(const char *s),
- TP_ARGS(s),
- TP_STRUCT__entry(
- __string(s, s)
- ),
- TP_fast_assign(
- __assign_str(s, s);
- ),
- TP_printk("%s", __get_str(s))
+ TP_PROTO(const char *s),
+ TP_ARGS(s),
+ TP_STRUCT__entry(
+ __string(s, s)
+ ),
+ TP_fast_assign(
+ __assign_str(s, s);
+ ),
+ TP_printk("%s", __get_str(s))
);
#endif /* _TRACE_CPUFREQ_INTERACTIVE_H */
diff --git a/include/trace/events/cpufreq_sched.h b/include/trace/events/cpufreq_sched.h
new file mode 100644
index 0000000..a46cd08
--- /dev/null
+++ b/include/trace/events/cpufreq_sched.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 Steve Muckle <smuckle@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM cpufreq_sched
+
+#if !defined(_TRACE_CPUFREQ_SCHED_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_CPUFREQ_SCHED_H
+
+#include <linux/sched.h>
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(cpufreq_sched_throttled,
+ TP_PROTO(unsigned int rem),
+ TP_ARGS(rem),
+ TP_STRUCT__entry(
+ __field( unsigned int, rem)
+ ),
+ TP_fast_assign(
+ __entry->rem = rem;
+ ),
+ TP_printk("throttled - %d usec remaining", __entry->rem)
+);
+
+TRACE_EVENT(cpufreq_sched_request_opp,
+ TP_PROTO(int cpu,
+ unsigned long capacity,
+ unsigned int freq_new,
+ unsigned int requested_freq),
+ TP_ARGS(cpu, capacity, freq_new, requested_freq),
+ TP_STRUCT__entry(
+ __field( int, cpu)
+ __field( unsigned long, capacity)
+ __field( unsigned int, freq_new)
+ __field( unsigned int, requested_freq)
+ ),
+ TP_fast_assign(
+ __entry->cpu = cpu;
+ __entry->capacity = capacity;
+ __entry->freq_new = freq_new;
+ __entry->requested_freq = requested_freq;
+ ),
+ TP_printk("cpu %d cap change, cluster cap request %ld => OPP %d "
+ "(cur %d)",
+ __entry->cpu, __entry->capacity, __entry->freq_new,
+ __entry->requested_freq)
+);
+
+TRACE_EVENT(cpufreq_sched_update_capacity,
+ TP_PROTO(int cpu,
+ bool request,
+ struct sched_capacity_reqs *scr,
+ unsigned long new_capacity),
+ TP_ARGS(cpu, request, scr, new_capacity),
+ TP_STRUCT__entry(
+ __field( int, cpu)
+ __field( bool, request)
+ __field( unsigned long, cfs)
+ __field( unsigned long, rt)
+ __field( unsigned long, dl)
+ __field( unsigned long, total)
+ __field( unsigned long, new_total)
+ ),
+ TP_fast_assign(
+ __entry->cpu = cpu;
+ __entry->request = request;
+ __entry->cfs = scr->cfs;
+ __entry->rt = scr->rt;
+ __entry->dl = scr->dl;
+ __entry->total = scr->total;
+ __entry->new_total = new_capacity;
+ ),
+ TP_printk("cpu=%d set_cap=%d cfs=%ld rt=%ld dl=%ld old_tot=%ld "
+ "new_tot=%ld",
+ __entry->cpu, __entry->request, __entry->cfs, __entry->rt,
+ __entry->dl, __entry->total, __entry->new_total)
+);
+
+#endif /* _TRACE_CPUFREQ_SCHED_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/trace/events/net.h b/include/trace/events/net.h
index 49cc7c3..89d009e 100644
--- a/include/trace/events/net.h
+++ b/include/trace/events/net.h
@@ -57,7 +57,7 @@
__entry->gso_type = skb_shinfo(skb)->gso_type;
),
- TP_printk("dev=%s queue_mapping=%u skbaddr=%p vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d len=%u data_len=%u network_offset=%d transport_offset_valid=%d transport_offset=%d tx_flags=%d gso_size=%d gso_segs=%d gso_type=%#x",
+ TP_printk("dev=%s queue_mapping=%u skbaddr=%pK vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d len=%u data_len=%u network_offset=%d transport_offset_valid=%d transport_offset=%d tx_flags=%d gso_size=%d gso_segs=%d gso_type=%#x",
__get_str(name), __entry->queue_mapping, __entry->skbaddr,
__entry->vlan_tagged, __entry->vlan_proto, __entry->vlan_tci,
__entry->protocol, __entry->ip_summed, __entry->len,
@@ -90,7 +90,7 @@
__assign_str(name, dev->name);
),
- TP_printk("dev=%s skbaddr=%p len=%u rc=%d",
+ TP_printk("dev=%s skbaddr=%pK len=%u rc=%d",
__get_str(name), __entry->skbaddr, __entry->len, __entry->rc)
);
@@ -112,7 +112,7 @@
__assign_str(name, skb->dev->name);
),
- TP_printk("dev=%s skbaddr=%p len=%u",
+ TP_printk("dev=%s skbaddr=%pK len=%u",
__get_str(name), __entry->skbaddr, __entry->len)
)
@@ -191,7 +191,7 @@
__entry->gso_type = skb_shinfo(skb)->gso_type;
),
- TP_printk("dev=%s napi_id=%#x queue_mapping=%u skbaddr=%p vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d hash=0x%08x l4_hash=%d len=%u data_len=%u truesize=%u mac_header_valid=%d mac_header=%d nr_frags=%d gso_size=%d gso_type=%#x",
+ TP_printk("dev=%s napi_id=%#x queue_mapping=%u skbaddr=%pK vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d hash=0x%08x l4_hash=%d len=%u data_len=%u truesize=%u mac_header_valid=%d mac_header=%d nr_frags=%d gso_size=%d gso_type=%#x",
__get_str(name), __entry->napi_id, __entry->queue_mapping,
__entry->skbaddr, __entry->vlan_tagged, __entry->vlan_proto,
__entry->vlan_tci, __entry->protocol, __entry->ip_summed,
diff --git a/include/trace/events/power.h b/include/trace/events/power.h
index 070be71..ec6f815 100644
--- a/include/trace/events/power.h
+++ b/include/trace/events/power.h
@@ -172,6 +172,13 @@
(unsigned long)__entry->cpu_id)
);
+DEFINE_EVENT(cpu, cpu_capacity,
+
+ TP_PROTO(unsigned int capacity, unsigned int cpu_id),
+
+ TP_ARGS(capacity, cpu_id)
+);
+
TRACE_EVENT(device_pm_callback_start,
TP_PROTO(struct device *dev, const char *pm_ops, int event),
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index 3f0b3df..904bedb 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -1304,6 +1304,285 @@
TP_printk("cpu=%d", __entry->cpu)
);
+TRACE_EVENT(sched_contrib_scale_f,
+ TP_PROTO(int cpu, unsigned long freq_scale_factor,
+ unsigned long cpu_scale_factor),
+ TP_ARGS(cpu, freq_scale_factor, cpu_scale_factor),
+ TP_STRUCT__entry(
+ __field(int, cpu)
+ __field(unsigned long, freq_scale_factor)
+ __field(unsigned long, cpu_scale_factor)
+ ),
+ TP_fast_assign(
+ __entry->cpu = cpu;
+ __entry->freq_scale_factor = freq_scale_factor;
+ __entry->cpu_scale_factor = cpu_scale_factor;
+ ),
+ TP_printk("cpu=%d freq_scale_factor=%lu cpu_scale_factor=%lu",
+ __entry->cpu, __entry->freq_scale_factor,
+ __entry->cpu_scale_factor)
+);
+
+/*
+ * Tracepoint for accounting sched averages for tasks.
+ */
+TRACE_EVENT(sched_load_avg_task,
+ TP_PROTO(struct task_struct *tsk, struct sched_avg *avg),
+ TP_ARGS(tsk, avg),
+ TP_STRUCT__entry(
+ __array( char, comm, TASK_COMM_LEN )
+ __field( pid_t, pid )
+ __field( int, cpu )
+ __field( unsigned long, load_avg )
+ __field( unsigned long, util_avg )
+ __field( u64, load_sum )
+ __field( u32, util_sum )
+ __field( u32, period_contrib )
+ ),
+ TP_fast_assign(
+ memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN);
+ __entry->pid = tsk->pid;
+ __entry->cpu = task_cpu(tsk);
+ __entry->load_avg = avg->load_avg;
+ __entry->util_avg = avg->util_avg;
+ __entry->load_sum = avg->load_sum;
+ __entry->util_sum = avg->util_sum;
+ __entry->period_contrib = avg->period_contrib;
+ ),
+ TP_printk("comm=%s pid=%d cpu=%d load_avg=%lu util_avg=%lu load_sum=%llu"
+ " util_sum=%u period_contrib=%u",
+ __entry->comm,
+ __entry->pid,
+ __entry->cpu,
+ __entry->load_avg,
+ __entry->util_avg,
+ (u64)__entry->load_sum,
+ (u32)__entry->util_sum,
+ (u32)__entry->period_contrib)
+);
+/*
+ * Tracepoint for accounting sched averages for cpus.
+ */
+TRACE_EVENT(sched_load_avg_cpu,
+ TP_PROTO(int cpu, struct cfs_rq *cfs_rq),
+ TP_ARGS(cpu, cfs_rq),
+ TP_STRUCT__entry(
+ __field( int, cpu )
+ __field( unsigned long, load_avg )
+ __field( unsigned long, util_avg )
+ ),
+ TP_fast_assign(
+ __entry->cpu = cpu;
+ __entry->load_avg = cfs_rq->avg.load_avg;
+ __entry->util_avg = cfs_rq->avg.util_avg;
+ ),
+ TP_printk("cpu=%d load_avg=%lu util_avg=%lu",
+ __entry->cpu, __entry->load_avg, __entry->util_avg)
+);
+/*
+ * Tracepoint for sched_tune_config settings
+ */
+TRACE_EVENT(sched_tune_config,
+ TP_PROTO(int boost),
+ TP_ARGS(boost),
+ TP_STRUCT__entry(
+ __field( int, boost )
+ ),
+ TP_fast_assign(
+ __entry->boost = boost;
+ ),
+ TP_printk("boost=%d ", __entry->boost)
+);
+/*
+ * Tracepoint for accounting CPU boosted utilization
+ */
+TRACE_EVENT(sched_boost_cpu,
+ TP_PROTO(int cpu, unsigned long util, long margin),
+ TP_ARGS(cpu, util, margin),
+ TP_STRUCT__entry(
+ __field( int, cpu )
+ __field( unsigned long, util )
+ __field(long, margin )
+ ),
+ TP_fast_assign(
+ __entry->cpu = cpu;
+ __entry->util = util;
+ __entry->margin = margin;
+ ),
+ TP_printk("cpu=%d util=%lu margin=%ld",
+ __entry->cpu,
+ __entry->util,
+ __entry->margin)
+);
+/*
+ * Tracepoint for schedtune_tasks_update
+ */
+TRACE_EVENT(sched_tune_tasks_update,
+ TP_PROTO(struct task_struct *tsk, int cpu, int tasks, int idx,
+ int boost, int max_boost),
+ TP_ARGS(tsk, cpu, tasks, idx, boost, max_boost),
+ TP_STRUCT__entry(
+ __array( char, comm, TASK_COMM_LEN )
+ __field( pid_t, pid )
+ __field( int, cpu )
+ __field( int, tasks )
+ __field( int, idx )
+ __field( int, boost )
+ __field( int, max_boost )
+ ),
+ TP_fast_assign(
+ memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN);
+ __entry->pid = tsk->pid;
+ __entry->cpu = cpu;
+ __entry->tasks = tasks;
+ __entry->idx = idx;
+ __entry->boost = boost;
+ __entry->max_boost = max_boost;
+ ),
+ TP_printk("pid=%d comm=%s "
+ "cpu=%d tasks=%d idx=%d boost=%d max_boost=%d",
+ __entry->pid, __entry->comm,
+ __entry->cpu, __entry->tasks, __entry->idx,
+ __entry->boost, __entry->max_boost)
+);
+/*
+ * Tracepoint for schedtune_boostgroup_update
+ */
+TRACE_EVENT(sched_tune_boostgroup_update,
+ TP_PROTO(int cpu, int variation, int max_boost),
+ TP_ARGS(cpu, variation, max_boost),
+ TP_STRUCT__entry(
+ __field( int, cpu )
+ __field( int, variation )
+ __field( int, max_boost )
+ ),
+ TP_fast_assign(
+ __entry->cpu = cpu;
+ __entry->variation = variation;
+ __entry->max_boost = max_boost;
+ ),
+ TP_printk("cpu=%d variation=%d max_boost=%d",
+ __entry->cpu, __entry->variation, __entry->max_boost)
+);
+/*
+ * Tracepoint for accounting task boosted utilization
+ */
+TRACE_EVENT(sched_boost_task,
+ TP_PROTO(struct task_struct *tsk, unsigned long util, long margin),
+ TP_ARGS(tsk, util, margin),
+ TP_STRUCT__entry(
+ __array( char, comm, TASK_COMM_LEN )
+ __field( pid_t, pid )
+ __field( unsigned long, util )
+ __field( long, margin )
+ ),
+ TP_fast_assign(
+ memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN);
+ __entry->pid = tsk->pid;
+ __entry->util = util;
+ __entry->margin = margin;
+ ),
+ TP_printk("comm=%s pid=%d util=%lu margin=%ld",
+ __entry->comm, __entry->pid,
+ __entry->util,
+ __entry->margin)
+);
+/*
+ * Tracepoint for accounting sched group energy
+ */
+TRACE_EVENT(sched_energy_diff,
+ TP_PROTO(struct task_struct *tsk, int scpu, int dcpu, int udelta,
+ int nrgb, int nrga, int nrgd, int capb, int capa, int capd,
+ int nrgn, int nrgp),
+ TP_ARGS(tsk, scpu, dcpu, udelta,
+ nrgb, nrga, nrgd, capb, capa, capd,
+ nrgn, nrgp),
+ TP_STRUCT__entry(
+ __array( char, comm, TASK_COMM_LEN )
+ __field( pid_t, pid )
+ __field( int, scpu )
+ __field( int, dcpu )
+ __field( int, udelta )
+ __field( int, nrgb )
+ __field( int, nrga )
+ __field( int, nrgd )
+ __field( int, capb )
+ __field( int, capa )
+ __field( int, capd )
+ __field( int, nrgn )
+ __field( int, nrgp )
+ ),
+ TP_fast_assign(
+ memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN);
+ __entry->pid = tsk->pid;
+ __entry->scpu = scpu;
+ __entry->dcpu = dcpu;
+ __entry->udelta = udelta;
+ __entry->nrgb = nrgb;
+ __entry->nrga = nrga;
+ __entry->nrgd = nrgd;
+ __entry->capb = capb;
+ __entry->capa = capa;
+ __entry->capd = capd;
+ __entry->nrgn = nrgn;
+ __entry->nrgp = nrgp;
+ ),
+ TP_printk("pid=%d comm=%s "
+ "src_cpu=%d dst_cpu=%d usage_delta=%d "
+ "nrg_before=%d nrg_after=%d nrg_diff=%d "
+ "cap_before=%d cap_after=%d cap_delta=%d "
+ "nrg_delta=%d nrg_payoff=%d",
+ __entry->pid, __entry->comm,
+ __entry->scpu, __entry->dcpu, __entry->udelta,
+ __entry->nrgb, __entry->nrga, __entry->nrgd,
+ __entry->capb, __entry->capa, __entry->capd,
+ __entry->nrgn, __entry->nrgp)
+);
+/*
+ * Tracepoint for schedtune_tasks_update
+ */
+TRACE_EVENT(sched_tune_filter,
+ TP_PROTO(int nrg_delta, int cap_delta,
+ int nrg_gain, int cap_gain,
+ int payoff, int region),
+ TP_ARGS(nrg_delta, cap_delta, nrg_gain, cap_gain, payoff, region),
+ TP_STRUCT__entry(
+ __field( int, nrg_delta )
+ __field( int, cap_delta )
+ __field( int, nrg_gain )
+ __field( int, cap_gain )
+ __field( int, payoff )
+ __field( int, region )
+ ),
+ TP_fast_assign(
+ __entry->nrg_delta = nrg_delta;
+ __entry->cap_delta = cap_delta;
+ __entry->nrg_gain = nrg_gain;
+ __entry->cap_gain = cap_gain;
+ __entry->payoff = payoff;
+ __entry->region = region;
+ ),
+ TP_printk("nrg_delta=%d cap_delta=%d nrg_gain=%d cap_gain=%d payoff=%d region=%d",
+ __entry->nrg_delta, __entry->cap_delta,
+ __entry->nrg_gain, __entry->cap_gain,
+ __entry->payoff, __entry->region)
+);
+/*
+ * Tracepoint for system overutilized flag
+ */
+TRACE_EVENT(sched_overutilized,
+ TP_PROTO(bool overutilized),
+ TP_ARGS(overutilized),
+ TP_STRUCT__entry(
+ __field( bool, overutilized )
+ ),
+ TP_fast_assign(
+ __entry->overutilized = overutilized;
+ ),
+ TP_printk("overutilized=%d",
+ __entry->overutilized ? 1 : 0)
+);
+
TRACE_EVENT(sched_get_nr_running_avg,
TP_PROTO(int avg, int big_avg, int iowait_avg),
diff --git a/include/trace/events/swiotlb.h b/include/trace/events/swiotlb.h
index 7ea4c5e..288c0c5 100644
--- a/include/trace/events/swiotlb.h
+++ b/include/trace/events/swiotlb.h
@@ -11,16 +11,16 @@
TP_PROTO(struct device *dev,
dma_addr_t dev_addr,
size_t size,
- int swiotlb_force),
+ enum swiotlb_force swiotlb_force),
TP_ARGS(dev, dev_addr, size, swiotlb_force),
TP_STRUCT__entry(
- __string( dev_name, dev_name(dev) )
- __field( u64, dma_mask )
- __field( dma_addr_t, dev_addr )
- __field( size_t, size )
- __field( int, swiotlb_force )
+ __string( dev_name, dev_name(dev) )
+ __field( u64, dma_mask )
+ __field( dma_addr_t, dev_addr )
+ __field( size_t, size )
+ __field( enum swiotlb_force, swiotlb_force )
),
TP_fast_assign(
@@ -37,7 +37,10 @@
__entry->dma_mask,
(unsigned long long)__entry->dev_addr,
__entry->size,
- __entry->swiotlb_force ? "swiotlb_force" : "" )
+ __print_symbolic(__entry->swiotlb_force,
+ { SWIOTLB_NORMAL, "NORMAL" },
+ { SWIOTLB_FORCE, "FORCE" },
+ { SWIOTLB_NO_FORCE, "NO_FORCE" }))
);
#endif /* _TRACE_SWIOTLB_H */
diff --git a/include/trace/events/trace_msm_core.h b/include/trace/events/trace_msm_core.h
new file mode 100644
index 0000000..45747f7
--- /dev/null
+++ b/include/trace/events/trace_msm_core.h
@@ -0,0 +1,103 @@
+/* Copyright (c) 2014,2016 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM msm_core
+
+#if !defined(_TRACE_MSM_CORE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_MSM_CORE_H
+
+#include <linux/tracepoint.h>
+#include <linux/thermal.h>
+
+TRACE_EVENT(cpu_stats,
+
+ TP_PROTO(unsigned int cpu, long temp,
+ uint64_t min_power, uint64_t max_power),
+
+ TP_ARGS(cpu, temp, min_power, max_power),
+
+ TP_STRUCT__entry(
+ __field(unsigned int, cpu)
+ __field(long, temp)
+ __field(uint64_t, min_power)
+ __field(uint64_t, max_power)
+ ),
+
+ TP_fast_assign(
+ __entry->cpu = cpu;
+ __entry->temp = temp;
+ __entry->min_power = min_power;
+ __entry->max_power = max_power;
+ ),
+
+ TP_printk("Cpu%d: temp:%ld power@minfreq:%llu power@maxfreq:%llu",
+ __entry->cpu, __entry->temp, __entry->min_power,
+ __entry->max_power)
+);
+
+TRACE_EVENT(temp_threshold,
+
+ TP_PROTO(unsigned int cpu, long temp,
+ long hi_thresh, long low_thresh),
+
+ TP_ARGS(cpu, temp, hi_thresh, low_thresh),
+
+ TP_STRUCT__entry(
+ __field(unsigned int, cpu)
+ __field(long, temp)
+ __field(long, hi_thresh)
+ __field(long, low_thresh)
+ ),
+
+ TP_fast_assign(
+ __entry->cpu = cpu;
+ __entry->temp = temp;
+ __entry->hi_thresh = hi_thresh;
+ __entry->low_thresh = low_thresh;
+ ),
+
+ TP_printk("Cpu%d: temp:%ld hi_thresh:%ld low_thresh:%ld",
+ __entry->cpu, __entry->temp, __entry->hi_thresh,
+ __entry->low_thresh)
+);
+
+TRACE_EVENT(temp_notification,
+
+ TP_PROTO(unsigned int sensor_id, enum thermal_trip_type type,
+ int temp, int prev_temp),
+
+ TP_ARGS(sensor_id, type, temp, prev_temp),
+
+ TP_STRUCT__entry(
+ __field(unsigned int, sensor_id)
+ __field(enum thermal_trip_type, type)
+ __field(int, temp)
+ __field(int, prev_temp)
+ ),
+
+ TP_fast_assign(
+ __entry->sensor_id = sensor_id;
+ __entry->type = type;
+ __entry->temp = temp;
+ __entry->prev_temp = prev_temp;
+ ),
+
+ TP_printk("Sensor_id%d: %s threshold triggered temp:%d(previous:%d)",
+ __entry->sensor_id,
+ __entry->type == THERMAL_TRIP_CONFIGURABLE_HI ? "High" : "Low",
+ __entry->temp, __entry->prev_temp)
+);
+
+#endif
+#define TRACE_INCLUDE_FILE trace_msm_core
+#include <trace/define_trace.h>
diff --git a/include/trace/events/trace_msm_low_power.h b/include/trace/events/trace_msm_low_power.h
new file mode 100644
index 0000000..97eefc6
--- /dev/null
+++ b/include/trace/events/trace_msm_low_power.h
@@ -0,0 +1,273 @@
+/* Copyright (c) 2012, 2014-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM msm_low_power
+
+#if !defined(_TRACE_MSM_LOW_POWER_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_MSM_LOW_POWER_H_
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(cpu_power_select,
+
+ TP_PROTO(int index, u32 sleep_us, u32 latency, u32 next_event_us),
+
+ TP_ARGS(index, sleep_us, latency, next_event_us),
+
+ TP_STRUCT__entry(
+ __field(int, index)
+ __field(u32, sleep_us)
+ __field(u32, latency)
+ __field(u32, next_event_us)
+ ),
+
+ TP_fast_assign(
+ __entry->index = index;
+ __entry->sleep_us = sleep_us;
+ __entry->latency = latency;
+ __entry->next_event_us = next_event_us;
+ ),
+
+ TP_printk("idx:%d sleep_time:%u latency:%u next_event:%u",
+ __entry->index, __entry->sleep_us, __entry->latency,
+ __entry->next_event_us)
+);
+
+TRACE_EVENT(cpu_pred_select,
+
+ TP_PROTO(u32 predtype, u64 predicted, u32 tmr_time),
+
+ TP_ARGS(predtype, predicted, tmr_time),
+
+ TP_STRUCT__entry(
+ __field(u32, predtype)
+ __field(u64, predicted)
+ __field(u32, tmr_time)
+ ),
+
+ TP_fast_assign(
+ __entry->predtype = predtype;
+ __entry->predicted = predicted;
+ __entry->tmr_time = tmr_time;
+ ),
+
+ TP_printk("pred:%u time:%lu tmr_time:%u",
+ __entry->predtype, (unsigned long)__entry->predicted,
+ __entry->tmr_time)
+);
+
+TRACE_EVENT(cpu_pred_hist,
+
+ TP_PROTO(int idx, u32 resi, u32 sample, u32 tmr),
+
+ TP_ARGS(idx, resi, sample, tmr),
+
+ TP_STRUCT__entry(
+ __field(int, idx)
+ __field(u32, resi)
+ __field(u32, sample)
+ __field(u32, tmr)
+ ),
+
+ TP_fast_assign(
+ __entry->idx = idx;
+ __entry->resi = resi;
+ __entry->sample = sample;
+ __entry->tmr = tmr;
+ ),
+
+ TP_printk("idx:%d resi:%u sample:%u tmr:%u",
+ __entry->idx, __entry->resi,
+ __entry->sample, __entry->tmr)
+);
+
+TRACE_EVENT(cpu_idle_enter,
+
+ TP_PROTO(int index),
+
+ TP_ARGS(index),
+
+ TP_STRUCT__entry(
+ __field(int, index)
+ ),
+
+ TP_fast_assign(
+ __entry->index = index;
+ ),
+
+ TP_printk("idx:%d",
+ __entry->index)
+);
+
+TRACE_EVENT(cpu_idle_exit,
+
+ TP_PROTO(int index, bool success),
+
+ TP_ARGS(index, success),
+
+ TP_STRUCT__entry(
+ __field(int, index)
+ __field(bool, success)
+ ),
+
+ TP_fast_assign(
+ __entry->index = index;
+ __entry->success = success;
+ ),
+
+ TP_printk("idx:%d success:%d",
+ __entry->index,
+ __entry->success)
+);
+
+TRACE_EVENT(cluster_enter,
+
+ TP_PROTO(const char *name, int index, unsigned long sync_cpus,
+ unsigned long child_cpus, bool from_idle),
+
+ TP_ARGS(name, index, sync_cpus, child_cpus, from_idle),
+
+ TP_STRUCT__entry(
+ __field(const char *, name)
+ __field(int, index)
+ __field(unsigned long, sync_cpus)
+ __field(unsigned long, child_cpus)
+ __field(bool, from_idle)
+ ),
+
+ TP_fast_assign(
+ __entry->name = name;
+ __entry->index = index;
+ __entry->sync_cpus = sync_cpus;
+ __entry->child_cpus = child_cpus;
+ __entry->from_idle = from_idle;
+ ),
+
+ TP_printk("cluster_name:%s idx:%d sync:0x%lx child:0x%lx idle:%d",
+ __entry->name,
+ __entry->index,
+ __entry->sync_cpus,
+ __entry->child_cpus,
+ __entry->from_idle)
+);
+
+TRACE_EVENT(cluster_exit,
+
+ TP_PROTO(const char *name, int index, unsigned long sync_cpus,
+ unsigned long child_cpus, bool from_idle),
+
+ TP_ARGS(name, index, sync_cpus, child_cpus, from_idle),
+
+ TP_STRUCT__entry(
+ __field(const char *, name)
+ __field(int, index)
+ __field(unsigned long, sync_cpus)
+ __field(unsigned long, child_cpus)
+ __field(bool, from_idle)
+ ),
+
+ TP_fast_assign(
+ __entry->name = name;
+ __entry->index = index;
+ __entry->sync_cpus = sync_cpus;
+ __entry->child_cpus = child_cpus;
+ __entry->from_idle = from_idle;
+ ),
+
+ TP_printk("cluster_name:%s idx:%d sync:0x%lx child:0x%lx idle:%d",
+ __entry->name,
+ __entry->index,
+ __entry->sync_cpus,
+ __entry->child_cpus,
+ __entry->from_idle)
+);
+
+TRACE_EVENT(cluster_pred_select,
+
+ TP_PROTO(const char *name, int index, u32 sleep_us,
+ u32 latency, int pred, u32 pred_us),
+
+ TP_ARGS(name, index, sleep_us, latency, pred, pred_us),
+
+ TP_STRUCT__entry(
+ __field(const char *, name)
+ __field(int, index)
+ __field(u32, sleep_us)
+ __field(u32, latency)
+ __field(int, pred)
+ __field(u32, pred_us)
+ ),
+
+ TP_fast_assign(
+ __entry->name = name;
+ __entry->index = index;
+ __entry->sleep_us = sleep_us;
+ __entry->latency = latency;
+ __entry->pred = pred;
+ __entry->pred_us = pred_us;
+ ),
+
+ TP_printk("name:%s idx:%d sleep_time:%u latency:%u pred:%d pred_us:%u",
+ __entry->name, __entry->index, __entry->sleep_us,
+ __entry->latency, __entry->pred, __entry->pred_us)
+);
+
+TRACE_EVENT(cluster_pred_hist,
+
+ TP_PROTO(const char *name, int idx, u32 resi,
+ u32 sample, u32 tmr),
+
+ TP_ARGS(name, idx, resi, sample, tmr),
+
+ TP_STRUCT__entry(
+ __field(const char *, name)
+ __field(int, idx)
+ __field(u32, resi)
+ __field(u32, sample)
+ __field(u32, tmr)
+ ),
+
+ TP_fast_assign(
+ __entry->name = name;
+ __entry->idx = idx;
+ __entry->resi = resi;
+ __entry->sample = sample;
+ __entry->tmr = tmr;
+ ),
+
+ TP_printk("name:%s idx:%d resi:%u sample:%u tmr:%u",
+ __entry->name, __entry->idx, __entry->resi,
+ __entry->sample, __entry->tmr)
+);
+
+TRACE_EVENT(pre_pc_cb,
+
+ TP_PROTO(int tzflag),
+
+ TP_ARGS(tzflag),
+
+ TP_STRUCT__entry(
+ __field(int, tzflag)
+ ),
+
+ TP_fast_assign(
+ __entry->tzflag = tzflag;
+ ),
+
+ TP_printk("tzflag:%d",
+ __entry->tzflag
+ )
+);
+#endif
+#define TRACE_INCLUDE_FILE trace_msm_low_power
+#include <trace/define_trace.h>
diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
index 98fefc6..1fa3215 100644
--- a/include/uapi/drm/drm_fourcc.h
+++ b/include/uapi/drm/drm_fourcc.h
@@ -241,8 +241,23 @@
*/
#define DRM_FORMAT_MOD_QCOM_COMPRESSED fourcc_mod_code(QCOM, 1)
+/*
+ * QTI DX Format
+ *
+ * Refers to a DX variant of the base format.
+ * Implementation may be platform and base-format specific.
+ */
+#define DRM_FORMAT_MOD_QCOM_DX fourcc_mod_code(QCOM, 0x2)
+
+/*
+ * QTI Tight Format
+ *
+ * Refers to a tightly packed variant of the base format.
+ * Implementation may be platform and base-format specific.
+ */
+#define DRM_FORMAT_MOD_QCOM_TIGHT fourcc_mod_code(QCOM, 0x4)
+
#if defined(__cplusplus)
}
#endif
-
#endif /* DRM_FOURCC_H */
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 856f627..46b5dbd 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -508,3 +508,4 @@
header-y += ipa_qmi_service_v01.h
header-y += msm_ipa.h
header-y += rmnet_ipa_fd_ioctl.h
+header-y += msm-core-interface.h
diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h
index 41420e3..51f891f 100644
--- a/include/uapi/linux/android/binder.h
+++ b/include/uapi/linux/android/binder.h
@@ -33,6 +33,8 @@
BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
+ BINDER_TYPE_FDA = B_PACK_CHARS('f', 'd', 'a', B_TYPE_LARGE),
+ BINDER_TYPE_PTR = B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE),
};
enum {
@@ -48,6 +50,14 @@
typedef __u64 binder_uintptr_t;
#endif
+/**
+ * struct binder_object_header - header shared by all binder metadata objects.
+ * @type: type of the object
+ */
+struct binder_object_header {
+ __u32 type;
+};
+
/*
* This is the flattened representation of a Binder object for transfer
* between processes. The 'offsets' supplied as part of a binder transaction
@@ -56,9 +66,8 @@
* between processes.
*/
struct flat_binder_object {
- /* 8 bytes for large_flat_header. */
- __u32 type;
- __u32 flags;
+ struct binder_object_header hdr;
+ __u32 flags;
/* 8 bytes of data. */
union {
@@ -70,6 +79,84 @@
binder_uintptr_t cookie;
};
+/**
+ * struct binder_fd_object - describes a filedescriptor to be fixed up.
+ * @hdr: common header structure
+ * @pad_flags: padding to remain compatible with old userspace code
+ * @pad_binder: padding to remain compatible with old userspace code
+ * @fd: file descriptor
+ * @cookie: opaque data, used by user-space
+ */
+struct binder_fd_object {
+ struct binder_object_header hdr;
+ __u32 pad_flags;
+ union {
+ binder_uintptr_t pad_binder;
+ __u32 fd;
+ };
+
+ binder_uintptr_t cookie;
+};
+
+/* struct binder_buffer_object - object describing a userspace buffer
+ * @hdr: common header structure
+ * @flags: one or more BINDER_BUFFER_* flags
+ * @buffer: address of the buffer
+ * @length: length of the buffer
+ * @parent: index in offset array pointing to parent buffer
+ * @parent_offset: offset in @parent pointing to this buffer
+ *
+ * A binder_buffer object represents an object that the
+ * binder kernel driver can copy verbatim to the target
+ * address space. A buffer itself may be pointed to from
+ * within another buffer, meaning that the pointer inside
+ * that other buffer needs to be fixed up as well. This
+ * can be done by setting the BINDER_BUFFER_FLAG_HAS_PARENT
+ * flag in @flags, by setting @parent buffer to the index
+ * in the offset array pointing to the parent binder_buffer_object,
+ * and by setting @parent_offset to the offset in the parent buffer
+ * at which the pointer to this buffer is located.
+ */
+struct binder_buffer_object {
+ struct binder_object_header hdr;
+ __u32 flags;
+ binder_uintptr_t buffer;
+ binder_size_t length;
+ binder_size_t parent;
+ binder_size_t parent_offset;
+};
+
+enum {
+ BINDER_BUFFER_FLAG_HAS_PARENT = 0x01,
+};
+
+/* struct binder_fd_array_object - object describing an array of fds in a buffer
+ * @hdr: common header structure
+ * @num_fds: number of file descriptors in the buffer
+ * @parent: index in offset array to buffer holding the fd array
+ * @parent_offset: start offset of fd array in the buffer
+ *
+ * A binder_fd_array object represents an array of file
+ * descriptors embedded in a binder_buffer_object. It is
+ * different from a regular binder_buffer_object because it
+ * describes a list of file descriptors to fix up, not an opaque
+ * blob of memory, and hence the kernel needs to treat it differently.
+ *
+ * An example of how this would be used is with Android's
+ * native_handle_t object, which is a struct with a list of integers
+ * and a list of file descriptors. The native_handle_t struct itself
+ * will be represented by a struct binder_buffer_objct, whereas the
+ * embedded list of file descriptors is represented by a
+ * struct binder_fd_array_object with that binder_buffer_object as
+ * a parent.
+ */
+struct binder_fd_array_object {
+ struct binder_object_header hdr;
+ binder_size_t num_fds;
+ binder_size_t parent;
+ binder_size_t parent_offset;
+};
+
/*
* On 64-bit platforms where user code may run in 32-bits the driver must
* translate the buffer (and local binder) addresses appropriately.
@@ -162,6 +249,11 @@
} data;
};
+struct binder_transaction_data_sg {
+ struct binder_transaction_data transaction_data;
+ binder_size_t buffers_size;
+};
+
struct binder_ptr_cookie {
binder_uintptr_t ptr;
binder_uintptr_t cookie;
@@ -346,6 +438,12 @@
/*
* void *: cookie
*/
+
+ BC_TRANSACTION_SG = _IOW('c', 17, struct binder_transaction_data_sg),
+ BC_REPLY_SG = _IOW('c', 18, struct binder_transaction_data_sg),
+ /*
+ * binder_transaction_data_sg: the sent command.
+ */
};
#endif /* _UAPI_LINUX_BINDER_H */
diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h
index 436f0f3..a66c4ba 100644
--- a/include/uapi/linux/fib_rules.h
+++ b/include/uapi/linux/fib_rules.h
@@ -29,6 +29,11 @@
__u32 flags;
};
+struct fib_rule_uid_range {
+ __u32 start;
+ __u32 end;
+};
+
enum {
FRA_UNSPEC,
FRA_DST, /* destination address */
@@ -53,6 +58,7 @@
FRA_UID_END,
FRA_PAD,
FRA_L3MDEV, /* iif or oif is l3mdev goto its table */
+ FRA_UID_RANGE, /* UID range */
__FRA_MAX
};
diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
index 270b764..bd01769 100644
--- a/include/uapi/linux/magic.h
+++ b/include/uapi/linux/magic.h
@@ -53,7 +53,7 @@
#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs"
-#define SDCARDFS_SUPER_MAGIC 0xb550ca10
+#define SDCARDFS_SUPER_MAGIC 0x5dca2df5
#define SMB_SUPER_MAGIC 0x517B
#define CGROUP_SUPER_MAGIC 0x27e0eb
diff --git a/include/uapi/linux/msm-core-interface.h b/include/uapi/linux/msm-core-interface.h
new file mode 100644
index 0000000..6c0dae4
--- /dev/null
+++ b/include/uapi/linux/msm-core-interface.h
@@ -0,0 +1,29 @@
+#ifndef __MSM_CORE_LIB_H__
+#define __MSM_CORE_LIB_H__
+
+#include <linux/ioctl.h>
+
+#define TEMP_DATA_POINTS 13
+#define MAX_NUM_FREQ 200
+
+enum msm_core_ioctl_params {
+ MSM_CORE_LEAKAGE,
+ MSM_CORE_VOLTAGE,
+};
+
+#define MSM_CORE_MAGIC 0x9D
+
+struct sched_params {
+ uint32_t cpumask;
+ uint32_t cluster;
+ uint32_t power[TEMP_DATA_POINTS][MAX_NUM_FREQ];
+ uint32_t voltage[MAX_NUM_FREQ];
+ uint32_t freq[MAX_NUM_FREQ];
+};
+
+
+#define EA_LEAKAGE _IOWR(MSM_CORE_MAGIC, MSM_CORE_LEAKAGE,\
+ struct sched_params)
+#define EA_VOLT _IOWR(MSM_CORE_MAGIC, MSM_CORE_VOLTAGE,\
+ struct sched_params)
+#endif
diff --git a/include/uapi/linux/msm_kgsl.h b/include/uapi/linux/msm_kgsl.h
index 7ed5a4c..941a816 100644
--- a/include/uapi/linux/msm_kgsl.h
+++ b/include/uapi/linux/msm_kgsl.h
@@ -222,6 +222,12 @@
/* Server Side Sync Timeout in milliseconds */
#define KGSL_SYNCOBJ_SERVER_TIMEOUT 2000
+/* UBWC Modes */
+#define KGSL_UBWC_NONE 0
+#define KGSL_UBWC_1_0 1
+#define KGSL_UBWC_2_0 2
+#define KGSL_UBWC_3_0 3
+
/*
* Reset status values for context
*/
@@ -319,6 +325,8 @@
#define KGSL_PROP_HIGHEST_BANK_BIT 0x17
#define KGSL_PROP_DEVICE_BITNESS 0x18
#define KGSL_PROP_DEVICE_QDSS_STM 0x19
+#define KGSL_PROP_MIN_ACCESS_LENGTH 0x1A
+#define KGSL_PROP_UBWC_MODE 0x1B
struct kgsl_shadowprop {
unsigned long gpuaddr;
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 56368e9..d3cbe48 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -323,7 +323,7 @@
* @NL80211_CMD_GET_SCAN: get scan results
* @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
* %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
- * probe requests at CCK rate or not. %NL80211_ATTR_MAC can be used to
+ * probe requests at CCK rate or not. %NL80211_ATTR_BSSID can be used to
* specify a BSSID to scan for; if not included, the wildcard BSSID will
* be used.
* @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to
@@ -1937,6 +1937,9 @@
* @NL80211_ATTR_NAN_MATCH: used to report a match. This is a nested attribute.
* See &enum nl80211_nan_match_attributes.
*
+ * @NL80211_ATTR_BSSID: The BSSID of the AP. Note that %NL80211_ATTR_MAC is also
+ * used in various commands/events for specifying the BSSID.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2336,6 +2339,8 @@
NL80211_ATTR_NAN_FUNC,
NL80211_ATTR_NAN_MATCH,
+ NL80211_ATTR_BSSID,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index 8f97ca5..e14377f 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -311,7 +311,6 @@
RTA_TABLE,
RTA_MARK,
RTA_MFC_STATS,
- RTA_UID,
RTA_VIA,
RTA_NEWDST,
RTA_PREF,
@@ -319,6 +318,7 @@
RTA_ENCAP,
RTA_EXPIRES,
RTA_PAD,
+ RTA_UID,
__RTA_MAX
};
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index cf4c7a1..6e7d325 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -645,6 +645,79 @@
#define V4L2_PIX_FMT_Y12I v4l2_fourcc('Y', '1', '2', 'I') /* Greyscale 12-bit L/R interleaved */
#define V4L2_PIX_FMT_Z16 v4l2_fourcc('Z', '1', '6', ' ') /* Depth data 16-bit */
+#define V4L2_PIX_FMT_SDE_ABGR_8888 \
+ v4l2_fourcc('R', 'A', '2', '4') /* 32-bit ABGR 8:8:8:8 */
+#define V4L2_PIX_FMT_SDE_RGBA_8888 \
+ v4l2_fourcc('A', 'B', '2', '4') /* 32-bit RGBA 8:8:8:8 */
+#define V4L2_PIX_FMT_SDE_RGBX_8888 \
+ v4l2_fourcc('X', 'B', '2', '4') /* 32-bit RGBX 8:8:8:8 */
+#define V4L2_PIX_FMT_SDE_XBGR_8888 \
+ v4l2_fourcc('R', 'X', '2', '4') /* 32-bit XBGR 8:8:8:8 */
+#define V4L2_PIX_FMT_SDE_RGBA_5551 \
+ v4l2_fourcc('R', 'A', '1', '5') /* 16-bit RGBA 5:5:5:1 */
+#define V4L2_PIX_FMT_SDE_ABGR_1555 \
+ v4l2_fourcc('A', 'B', '1', '5') /* 16-bit ABGR 1:5:5:5 */
+#define V4L2_PIX_FMT_SDE_BGRA_5551 \
+ v4l2_fourcc('B', 'A', '1', '5') /* 16-bit BGRA 5:5:5:1 */
+#define V4L2_PIX_FMT_SDE_BGRX_5551 \
+ v4l2_fourcc('B', 'X', '1', '5') /* 16-bit BGRX 5:5:5:1 */
+#define V4L2_PIX_FMT_SDE_RGBX_5551 \
+ v4l2_fourcc('R', 'X', '1', '5') /* 16-bit RGBX 5:5:5:1 */
+#define V4L2_PIX_FMT_SDE_XBGR_1555 \
+ v4l2_fourcc('X', 'B', '1', '5') /* 16-bit XBGR 1:5:5:5 */
+#define V4L2_PIX_FMT_SDE_RGBA_4444 \
+ v4l2_fourcc('R', 'A', '1', '2') /* 16-bit RGBA 4:4:4:4 */
+#define V4L2_PIX_FMT_SDE_BGRA_4444 \
+ v4l2_fourcc('b', 'A', '1', '2') /* 16-bit BGRA 4:4:4:4 */
+#define V4L2_PIX_FMT_SDE_ABGR_4444 \
+ v4l2_fourcc('A', 'B', '1', '2') /* 16-bit ABGR 4:4:4:4 */
+#define V4L2_PIX_FMT_SDE_RGBX_4444 \
+ v4l2_fourcc('R', 'X', '1', '2') /* 16-bit RGBX 4:4:4:4 */
+#define V4L2_PIX_FMT_SDE_BGRX_4444 \
+ v4l2_fourcc('B', 'X', '1', '2') /* 16-bit BGRX 4:4:4:4 */
+#define V4L2_PIX_FMT_SDE_XBGR_4444 \
+ v4l2_fourcc('X', 'B', '1', '2') /* 16-bit XBGR 4:4:4:4 */
+#define V4L2_PIX_FMT_SDE_BGR_565 \
+ v4l2_fourcc('B', 'G', '1', '6') /* 16-bit BGR 5:6:5 */
+#define V4L2_PIX_FMT_SDE_Y_CR_CB_GH2V2 \
+ v4l2_fourcc('Y', 'U', '4', '2') /* Planar YVU 4:2:0 A16 */
+#define V4L2_PIX_FMT_SDE_Y_CBCR_H1V2 \
+ v4l2_fourcc('N', 'H', '1', '6') /* Y/CbCr 4:2:2 */
+#define V4L2_PIX_FMT_SDE_Y_CRCB_H1V2 \
+ v4l2_fourcc('N', 'H', '6', '1') /* Y/CrCb 4:2:2 */
+#define V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_VENUS \
+ v4l2_fourcc('Q', 'N', 'V', '2') /* Y/CbCr 4:2:0 Venus */
+#define V4L2_PIX_FMT_SDE_Y_CRCB_H2V2_VENUS \
+ v4l2_fourcc('Q', 'N', 'V', '1') /* Y/CrCb 4:2:0 Venus */
+#define V4L2_PIX_FMT_SDE_RGBX_8888_UBWC \
+ v4l2_fourcc('Q', 'X', 'B', '4') /* RGBX 8:8:8:8 UBWC */
+#define V4L2_PIX_FMT_SDE_RGB_565_UBWC \
+ v4l2_fourcc('Q', 'R', 'G', '6') /* RGB 5:6:5 UBWC */
+#define V4L2_PIX_FMT_SDE_RGBA_1010102 \
+ v4l2_fourcc('A', 'B', '3', '0') /* RGBA 10:10:10:2 */
+#define V4L2_PIX_FMT_SDE_RGBX_1010102 \
+ v4l2_fourcc('X', 'B', '3', '0') /* RGBX 10:10:10:2 */
+#define V4L2_PIX_FMT_SDE_ARGB_2101010 \
+ v4l2_fourcc('A', 'R', '3', '0') /* ARGB 2:10:10:10 */
+#define V4L2_PIX_FMT_SDE_XRGB_2101010 \
+ v4l2_fourcc('X', 'R', '3', '0') /* XRGB 2:10:10:10 */
+#define V4L2_PIX_FMT_SDE_BGRA_1010102 \
+ v4l2_fourcc('B', 'A', '3', '0') /* BGRA 10:10:10:2 */
+#define V4L2_PIX_FMT_SDE_BGRX_1010102 \
+ v4l2_fourcc('B', 'X', '3', '0') /* BGRX 10:10:10:2 */
+#define V4L2_PIX_FMT_SDE_ABGR_2101010 \
+ v4l2_fourcc('R', 'A', '3', '0') /* ABGR 2:10:10:10 */
+#define V4L2_PIX_FMT_SDE_XBGR_2101010 \
+ v4l2_fourcc('R', 'X', '3', '0') /* XBGR 2:10:10:10 */
+#define V4L2_PIX_FMT_SDE_RGBA_1010102_UBWC \
+ v4l2_fourcc('Q', 'R', 'B', 'A') /* RGBA 10:10:10:2 UBWC */
+#define V4L2_PIX_FMT_SDE_RGBX_1010102_UBWC \
+ v4l2_fourcc('Q', 'X', 'B', 'A') /* RGBX 10:10:10:2 UBWC */
+#define V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_TP10 \
+ v4l2_fourcc('T', 'P', '1', '0') /* Y/CbCr 4:2:0 TP10 */
+#define V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010 \
+ v4l2_fourcc('P', '0', '1', '0') /* Y/CbCr 4:2:0 P10 */
+
/* SDR formats - used only for Software Defined Radio devices */
#define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */
#define V4L2_SDR_FMT_CU16LE v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */
diff --git a/include/uapi/media/Kbuild b/include/uapi/media/Kbuild
index 0210a2a..00e94033 100644
--- a/include/uapi/media/Kbuild
+++ b/include/uapi/media/Kbuild
@@ -5,3 +5,4 @@
header-y += cam_isp_ife.h
header-y += msm_media_info.h
header-y += msm_vidc.h
+header-y += msm_sde_rotator.h
diff --git a/include/uapi/media/msm_sde_rotator.h b/include/uapi/media/msm_sde_rotator.h
new file mode 100644
index 0000000..790135a
--- /dev/null
+++ b/include/uapi/media/msm_sde_rotator.h
@@ -0,0 +1,114 @@
+#ifndef __UAPI_MSM_SDE_ROTATOR_H__
+#define __UAPI_MSM_SDE_ROTATOR_H__
+
+#include <linux/videodev2.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* SDE Rotator pixel format definitions */
+#define SDE_PIX_FMT_XRGB_8888 V4L2_PIX_FMT_XBGR32
+#define SDE_PIX_FMT_ARGB_8888 V4L2_PIX_FMT_ABGR32
+#define SDE_PIX_FMT_ABGR_8888 V4L2_PIX_FMT_SDE_ABGR_8888
+#define SDE_PIX_FMT_RGBA_8888 V4L2_PIX_FMT_SDE_RGBA_8888
+#define SDE_PIX_FMT_BGRA_8888 V4L2_PIX_FMT_ARGB32
+#define SDE_PIX_FMT_RGBX_8888 V4L2_PIX_FMT_SDE_RGBX_8888
+#define SDE_PIX_FMT_BGRX_8888 V4L2_PIX_FMT_XRGB32
+#define SDE_PIX_FMT_XBGR_8888 V4L2_PIX_FMT_SDE_XBGR_8888
+#define SDE_PIX_FMT_RGBA_5551 V4L2_PIX_FMT_SDE_RGBA_5551
+#define SDE_PIX_FMT_ARGB_1555 V4L2_PIX_FMT_ARGB555
+#define SDE_PIX_FMT_ABGR_1555 V4L2_PIX_FMT_SDE_ABGR_1555
+#define SDE_PIX_FMT_BGRA_5551 V4L2_PIX_FMT_SDE_BGRA_5551
+#define SDE_PIX_FMT_BGRX_5551 V4L2_PIX_FMT_SDE_BGRX_5551
+#define SDE_PIX_FMT_RGBX_5551 V4L2_PIX_FMT_SDE_RGBX_5551
+#define SDE_PIX_FMT_XBGR_1555 V4L2_PIX_FMT_SDE_XBGR_1555
+#define SDE_PIX_FMT_XRGB_1555 V4L2_PIX_FMT_XRGB555
+#define SDE_PIX_FMT_ARGB_4444 V4L2_PIX_FMT_ARGB444
+#define SDE_PIX_FMT_RGBA_4444 V4L2_PIX_FMT_SDE_RGBA_4444
+#define SDE_PIX_FMT_BGRA_4444 V4L2_PIX_FMT_SDE_BGRA_4444
+#define SDE_PIX_FMT_ABGR_4444 V4L2_PIX_FMT_SDE_ABGR_4444
+#define SDE_PIX_FMT_RGBX_4444 V4L2_PIX_FMT_SDE_RGBX_4444
+#define SDE_PIX_FMT_XRGB_4444 V4L2_PIX_FMT_XRGB444
+#define SDE_PIX_FMT_BGRX_4444 V4L2_PIX_FMT_SDE_BGRX_4444
+#define SDE_PIX_FMT_XBGR_4444 V4L2_PIX_FMT_SDE_XBGR_4444
+#define SDE_PIX_FMT_RGB_888 V4L2_PIX_FMT_RGB24
+#define SDE_PIX_FMT_BGR_888 V4L2_PIX_FMT_BGR24
+#define SDE_PIX_FMT_RGB_565 V4L2_PIX_FMT_RGB565
+#define SDE_PIX_FMT_BGR_565 V4L2_PIX_FMT_SDE_BGR_565
+#define SDE_PIX_FMT_Y_CB_CR_H2V2 V4L2_PIX_FMT_YUV420
+#define SDE_PIX_FMT_Y_CR_CB_H2V2 V4L2_PIX_FMT_YVU420
+#define SDE_PIX_FMT_Y_CR_CB_GH2V2 V4L2_PIX_FMT_SDE_Y_CR_CB_GH2V2
+#define SDE_PIX_FMT_Y_CBCR_H2V2 V4L2_PIX_FMT_NV12
+#define SDE_PIX_FMT_Y_CRCB_H2V2 V4L2_PIX_FMT_NV21
+#define SDE_PIX_FMT_Y_CBCR_H1V2 V4L2_PIX_FMT_SDE_Y_CBCR_H1V2
+#define SDE_PIX_FMT_Y_CRCB_H1V2 V4L2_PIX_FMT_SDE_Y_CRCB_H1V2
+#define SDE_PIX_FMT_Y_CBCR_H2V1 V4L2_PIX_FMT_NV16
+#define SDE_PIX_FMT_Y_CRCB_H2V1 V4L2_PIX_FMT_NV61
+#define SDE_PIX_FMT_YCBYCR_H2V1 V4L2_PIX_FMT_YUYV
+#define SDE_PIX_FMT_Y_CBCR_H2V2_VENUS V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_VENUS
+#define SDE_PIX_FMT_Y_CRCB_H2V2_VENUS V4L2_PIX_FMT_SDE_Y_CRCB_H2V2_VENUS
+#define SDE_PIX_FMT_RGBA_8888_UBWC V4L2_PIX_FMT_RGBA8888_UBWC
+#define SDE_PIX_FMT_RGBX_8888_UBWC V4L2_PIX_FMT_SDE_RGBX_8888_UBWC
+#define SDE_PIX_FMT_RGB_565_UBWC V4L2_PIX_FMT_SDE_RGB_565_UBWC
+#define SDE_PIX_FMT_Y_CBCR_H2V2_UBWC V4L2_PIX_FMT_NV12_UBWC
+#define SDE_PIX_FMT_RGBA_1010102 V4L2_PIX_FMT_SDE_RGBA_1010102
+#define SDE_PIX_FMT_RGBX_1010102 V4L2_PIX_FMT_SDE_RGBX_1010102
+#define SDE_PIX_FMT_ARGB_2101010 V4L2_PIX_FMT_SDE_ARGB_2101010
+#define SDE_PIX_FMT_XRGB_2101010 V4L2_PIX_FMT_SDE_XRGB_2101010
+#define SDE_PIX_FMT_BGRA_1010102 V4L2_PIX_FMT_SDE_BGRA_1010102
+#define SDE_PIX_FMT_BGRX_1010102 V4L2_PIX_FMT_SDE_BGRX_1010102
+#define SDE_PIX_FMT_ABGR_2101010 V4L2_PIX_FMT_SDE_ABGR_2101010
+#define SDE_PIX_FMT_XBGR_2101010 V4L2_PIX_FMT_SDE_XBGR_2101010
+#define SDE_PIX_FMT_RGBA_1010102_UBWC V4L2_PIX_FMT_SDE_RGBA_1010102_UBWC
+#define SDE_PIX_FMT_RGBX_1010102_UBWC V4L2_PIX_FMT_SDE_RGBX_1010102_UBWC
+#define SDE_PIX_FMT_Y_CBCR_H2V2_P010 V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010
+#define SDE_PIX_FMT_Y_CBCR_H2V2_TP10 V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_TP10
+#define SDE_PIX_FMT_Y_CBCR_H2V2_TP10_UBWC V4L2_PIX_FMT_NV12_TP10_UBWC
+
+/*
+ * struct msm_sde_rotator_fence - v4l2 buffer fence info
+ * @index: id number of the buffer
+ * @type: enum v4l2_buf_type; buffer type
+ * @fd: file descriptor of the fence associated with this buffer
+ */
+struct msm_sde_rotator_fence {
+ __u32 index;
+ __u32 type;
+ __s32 fd;
+ __u32 reserved[5];
+};
+
+/*
+ * struct msm_sde_rotator_comp_ratio - v4l2 buffer compression ratio
+ * @index: id number of the buffer
+ * @type: enum v4l2_buf_type; buffer type
+ * @numer: numerator of the ratio
+ * @denom: denominator of the ratio
+ */
+struct msm_sde_rotator_comp_ratio {
+ __u32 index;
+ __u32 type;
+ __u32 numer;
+ __u32 denom;
+ __u32 reserved[4];
+};
+
+/* SDE Rotator private ioctl ID */
+#define VIDIOC_G_SDE_ROTATOR_FENCE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 10, struct msm_sde_rotator_fence)
+#define VIDIOC_S_SDE_ROTATOR_FENCE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 11, struct msm_sde_rotator_fence)
+#define VIDIOC_G_SDE_ROTATOR_COMP_RATIO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 12, struct msm_sde_rotator_comp_ratio)
+#define VIDIOC_S_SDE_ROTATOR_COMP_RATIO \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 13, struct msm_sde_rotator_comp_ratio)
+
+/* SDE Rotator private control ID's */
+#define V4L2_CID_SDE_ROTATOR_SECURE (V4L2_CID_USER_BASE + 0x1000)
+
+/*
+ * This control Id indicates this context is associated with the
+ * secure camera.
+ */
+#define V4L2_CID_SDE_ROTATOR_SECURE_CAMERA (V4L2_CID_USER_BASE + 0x2000)
+
+#endif /* __UAPI_MSM_SDE_ROTATOR_H__ */
diff --git a/include/uapi/media/msm_vidc.h b/include/uapi/media/msm_vidc.h
index d330e4d..e04c229 100644
--- a/include/uapi/media/msm_vidc.h
+++ b/include/uapi/media/msm_vidc.h
@@ -5,6 +5,7 @@
#define MSM_VIDC_HAL_INTERLACE_COLOR_FORMAT_NV12 0x2
#define MSM_VIDC_HAL_INTERLACE_COLOR_FORMAT_NV12_UBWC 0x8002
+#define MSM_VIDC_4x_1 0x1
struct msm_vidc_extradata_header {
unsigned int size;
@@ -77,6 +78,12 @@
unsigned int height;
};
+struct msm_vidc_misr_info {
+ unsigned int misr_dpb_luma;
+ unsigned int misr_dpb_chroma;
+ unsigned int misr_opb_luma;
+ unsigned int misr_opb_chroma;
+};
struct msm_vidc_output_crop_payload {
unsigned int size;
unsigned int version;
@@ -87,6 +94,10 @@
unsigned int display_height;
unsigned int width;
unsigned int height;
+ unsigned int frame_num;
+ unsigned int bit_depth_y;
+ unsigned int bit_depth_c;
+ struct msm_vidc_misr_info misr_info[2];
};
diff --git a/include/uapi/rdma/cxgb3-abi.h b/include/uapi/rdma/cxgb3-abi.h
index 48a19bd..d24eee1 100644
--- a/include/uapi/rdma/cxgb3-abi.h
+++ b/include/uapi/rdma/cxgb3-abi.h
@@ -30,7 +30,7 @@
* SOFTWARE.
*/
#ifndef CXGB3_ABI_USER_H
-#define CXBG3_ABI_USER_H
+#define CXGB3_ABI_USER_H
#include <linux/types.h>
diff --git a/init/Kconfig b/init/Kconfig
index 262fbd4..6a4e13a 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -400,6 +400,15 @@
If in doubt, say N here.
+config SCHED_WALT
+ bool "Support window based load tracking"
+ depends on SMP
+ help
+ This feature will allow the scheduler to maintain a tunable window
+ based set of metrics for tasks and runqueues. These metrics can be
+ used to guide task placement as well as task frequency requirements
+ for cpufreq governors.
+
config BSD_PROCESS_ACCT
bool "BSD Process Accounting"
depends on MULTIUSER
@@ -971,6 +980,82 @@
if CGROUPS
+config CGROUP_DEBUG
+ bool "Example debug cgroup subsystem"
+ default n
+ help
+ This option enables a simple cgroup subsystem that
+ exports useful debugging information about the cgroups
+ framework.
+
+ Say N if unsure.
+
+config CGROUP_FREEZER
+ bool "Freezer cgroup subsystem"
+ help
+ Provides a way to freeze and unfreeze all tasks in a
+ cgroup.
+
+config CGROUP_PIDS
+ bool "PIDs cgroup subsystem"
+ help
+ Provides enforcement of process number limits in the scope of a
+ cgroup. Any attempt to fork more processes than is allowed in the
+ cgroup will fail. PIDs are fundamentally a global resource because it
+ is fairly trivial to reach PID exhaustion before you reach even a
+ conservative kmemcg limit. As a result, it is possible to grind a
+ system to halt without being limited by other cgroup policies. The
+ PIDs cgroup subsystem is designed to stop this from happening.
+
+ It should be noted that organisational operations (such as attaching
+ to a cgroup hierarchy will *not* be blocked by the PIDs subsystem),
+ since the PIDs limit only affects a process's ability to fork, not to
+ attach to a cgroup.
+
+config CGROUP_DEVICE
+ bool "Device controller for cgroups"
+ help
+ Provides a cgroup implementing whitelists for devices which
+ a process in the cgroup can mknod or open.
+
+config CPUSETS
+ bool "Cpuset support"
+ help
+ This option will let you create and manage CPUSETs which
+ allow dynamically partitioning a system into sets of CPUs and
+ Memory Nodes and assigning tasks to run only within those sets.
+ This is primarily useful on large SMP or NUMA systems.
+
+ Say N if unsure.
+
+config PROC_PID_CPUSET
+ bool "Include legacy /proc/<pid>/cpuset file"
+ depends on CPUSETS
+ default y
+
+config CGROUP_CPUACCT
+ bool "Simple CPU accounting cgroup subsystem"
+ help
+ Provides a simple Resource Controller for monitoring the
+ total CPU consumed by the tasks in a cgroup.
+
+config CGROUP_SCHEDTUNE
+ bool "CFS tasks boosting cgroup subsystem (EXPERIMENTAL)"
+ depends on SCHED_TUNE
+ help
+ This option provides the "schedtune" controller which improves the
+ flexibility of the task boosting mechanism by introducing the support
+ to define "per task" boost values.
+
+ This new controller:
+ 1. allows only a two layers hierarchy, where the root defines the
+ system-wide boost value and its direct childrens define each one a
+ different "class of tasks" to be boosted with a different value
+ 2. supports up to 16 different task classes, each one which could be
+ configured with a different boost value
+
+ Say N if unsure.
+
config PAGE_COUNTER
bool
@@ -1294,6 +1379,7 @@
config SCHED_TUNE
bool "Boosting for CFS tasks (EXPERIMENTAL)"
+ depends on SMP
help
This option enables the system-wide support for task boosting.
When this support is enabled a new sysctl interface is exposed to
@@ -1318,6 +1404,16 @@
If unsure, say N.
+config DEFAULT_USE_ENERGY_AWARE
+ bool "Default to enabling the Energy Aware Scheduler feature"
+ default n
+ help
+ This option defaults the ENERGY_AWARE scheduling feature to true,
+ as without SCHED_DEBUG set this feature can't be enabled or disabled
+ via sysctl.
+
+ Say N if unsure.
+
config SYSFS_DEPRECATED
bool "Enable deprecated sysfs features to support old userspace tools"
depends on SYSFS
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 8cbd6e6..a37a10b 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -745,7 +745,7 @@
}
mode &= ~current_umask();
- ret = vfs_create(dir, path->dentry, mode, true);
+ ret = vfs_create2(path->mnt, dir, path->dentry, mode, true);
path->dentry->d_fsdata = NULL;
if (ret)
return ERR_PTR(ret);
@@ -761,7 +761,7 @@
if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY))
return ERR_PTR(-EINVAL);
acc = oflag2acc[oflag & O_ACCMODE];
- if (inode_permission(d_inode(path->dentry), acc))
+ if (inode_permission2(path->mnt, d_inode(path->dentry), acc))
return ERR_PTR(-EACCES);
return dentry_open(path, oflag, current_cred());
}
@@ -794,7 +794,7 @@
ro = mnt_want_write(mnt); /* we'll drop it in any case */
error = 0;
inode_lock(d_inode(root));
- path.dentry = lookup_one_len(name->name, root, strlen(name->name));
+ path.dentry = lookup_one_len2(name->name, mnt, root, strlen(name->name));
if (IS_ERR(path.dentry)) {
error = PTR_ERR(path.dentry);
goto out_putfd;
@@ -865,7 +865,7 @@
if (err)
goto out_name;
inode_lock_nested(d_inode(mnt->mnt_root), I_MUTEX_PARENT);
- dentry = lookup_one_len(name->name, mnt->mnt_root,
+ dentry = lookup_one_len2(name->name, mnt, mnt->mnt_root,
strlen(name->name));
if (IS_ERR(dentry)) {
err = PTR_ERR(dentry);
@@ -877,7 +877,7 @@
err = -ENOENT;
} else {
ihold(inode);
- err = vfs_unlink(d_inode(dentry->d_parent), dentry, NULL);
+ err = vfs_unlink2(mnt, d_inode(dentry->d_parent), dentry, NULL);
}
dput(dentry);
diff --git a/kernel/capability.c b/kernel/capability.c
index 00411c8..4984e1f 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -457,6 +457,19 @@
EXPORT_SYMBOL(file_ns_capable);
/**
+ * privileged_wrt_inode_uidgid - Do capabilities in the namespace work over the inode?
+ * @ns: The user namespace in question
+ * @inode: The inode in question
+ *
+ * Return true if the inode uid and gid are within the namespace.
+ */
+bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode)
+{
+ return kuid_has_mapping(ns, inode->i_uid) &&
+ kgid_has_mapping(ns, inode->i_gid);
+}
+
+/**
* capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped
* @inode: The inode in question
* @cap: The capability in question
@@ -469,7 +482,26 @@
{
struct user_namespace *ns = current_user_ns();
- return ns_capable(ns, cap) && kuid_has_mapping(ns, inode->i_uid) &&
- kgid_has_mapping(ns, inode->i_gid);
+ return ns_capable(ns, cap) && privileged_wrt_inode_uidgid(ns, inode);
}
EXPORT_SYMBOL(capable_wrt_inode_uidgid);
+
+/**
+ * ptracer_capable - Determine if the ptracer holds CAP_SYS_PTRACE in the namespace
+ * @tsk: The task that may be ptraced
+ * @ns: The user namespace to search for CAP_SYS_PTRACE in
+ *
+ * Return true if the task that is ptracing the current task had CAP_SYS_PTRACE
+ * in the specified user namespace.
+ */
+bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns)
+{
+ int ret = 0; /* An absent tracer adds no restrictions */
+ const struct cred *cred;
+ rcu_read_lock();
+ cred = rcu_dereference(tsk->ptracer_cred);
+ if (cred)
+ ret = security_capable_noaudit(cred, ns, CAP_SYS_PTRACE);
+ rcu_read_unlock();
+ return (ret == 0);
+}
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index b848fe8..eadd942 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -2862,25 +2862,6 @@
return 0;
}
-static int cgroup_allow_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
-{
- struct cgroup_subsys_state *css;
- int i;
- int ret;
-
- for_each_css(css, i, cgrp) {
- if (css->ss->allow_attach) {
- ret = css->ss->allow_attach(tset);
- if (ret)
- return ret;
- } else {
- return -EACCES;
- }
- }
-
- return 0;
-}
-
static int cgroup_procs_write_permission(struct task_struct *task,
struct cgroup *dst_cgrp,
struct kernfs_open_file *of)
@@ -2895,24 +2876,9 @@
*/
if (!uid_eq(cred->euid, GLOBAL_ROOT_UID) &&
!uid_eq(cred->euid, tcred->uid) &&
- !uid_eq(cred->euid, tcred->suid)) {
- /*
- * if the default permission check fails, give each
- * cgroup a chance to extend the permission check
- */
- struct cgroup_taskset tset = {
- .src_csets = LIST_HEAD_INIT(tset.src_csets),
- .dst_csets = LIST_HEAD_INIT(tset.dst_csets),
- .csets = &tset.src_csets,
- };
- struct css_set *cset;
- cset = task_css_set(task);
- list_add(&cset->mg_node, &tset.src_csets);
- ret = cgroup_allow_attach(dst_cgrp, &tset);
- list_del(&tset.src_csets);
- if (ret)
- ret = -EACCES;
- }
+ !uid_eq(cred->euid, tcred->suid) &&
+ !ns_capable(tcred->user_ns, CAP_SYS_RESOURCE))
+ ret = -EACCES;
if (!ret && cgroup_on_dfl(dst_cgrp)) {
struct super_block *sb = of->file->f_path.dentry->d_sb;
@@ -5274,6 +5240,11 @@
return ERR_PTR(err);
}
+/*
+ * The returned cgroup is fully initialized including its control mask, but
+ * it isn't associated with its kernfs_node and doesn't have the control
+ * mask applied.
+ */
static struct cgroup *cgroup_create(struct cgroup *parent)
{
struct cgroup_root *root = parent->root;
@@ -5338,11 +5309,6 @@
cgroup_propagate_control(cgrp);
- /* @cgrp doesn't have dir yet so the following will only create csses */
- ret = cgroup_apply_control_enable(cgrp);
- if (ret)
- goto out_destroy;
-
return cgrp;
out_cancel_ref:
@@ -5350,9 +5316,6 @@
out_free_cgrp:
kfree(cgrp);
return ERR_PTR(ret);
-out_destroy:
- cgroup_destroy_locked(cgrp);
- return ERR_PTR(ret);
}
static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
diff --git a/kernel/configs/android-base.config b/kernel/configs/android-base.config
index adc552e..4732628 100644
--- a/kernel/configs/android-base.config
+++ b/kernel/configs/android-base.config
@@ -135,7 +135,11 @@
CONFIG_PPP_MPPE=y
CONFIG_PREEMPT=y
CONFIG_PROFILING=y
+CONFIG_QFMT_V2=y
CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+CONFIG_QUOTA_TREE=y
+CONFIG_QUOTACTL=y
CONFIG_RANDOMIZE_BASE=y
CONFIG_RTC_CLASS=y
CONFIG_RT_GROUP_SCHED=y
diff --git a/kernel/configs/android-recommended.config b/kernel/configs/android-recommended.config
index 437f337..abec2ca 100644
--- a/kernel/configs/android-recommended.config
+++ b/kernel/configs/android-recommended.config
@@ -1,10 +1,12 @@
# KEEP ALPHABETICALLY SORTED
+# CONFIG_AIO is not set
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_LEGACY_PTYS is not set
# CONFIG_NF_CONNTRACK_SIP is not set
# CONFIG_PM_WAKELOCKS_GC is not set
# CONFIG_VT is not set
+CONFIG_ARM64_SW_TTBR0_PAN=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_BLK_DEV_DM=y
CONFIG_BLK_DEV_LOOP=y
@@ -92,6 +94,7 @@
CONFIG_LOGITECH_FF=y
CONFIG_MD=y
CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEMORY_STATE_TIME=y
CONFIG_MSDOS_FS=y
CONFIG_PANIC_TIMEOUT=5
CONFIG_PANTHERLORD_FF=y
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 2918a9a..7c23144 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -659,7 +659,6 @@
kthread_unpark(this_cpu_read(cpuhp_state.thread));
}
-#ifdef CONFIG_HOTPLUG_CPU
EXPORT_SYMBOL(register_cpu_notifier);
EXPORT_SYMBOL(__register_cpu_notifier);
void unregister_cpu_notifier(struct notifier_block *nb)
@@ -676,6 +675,7 @@
}
EXPORT_SYMBOL(__unregister_cpu_notifier);
+#ifdef CONFIG_HOTPLUG_CPU
/**
* clear_tasks_mm_cpumask - Safely clear tasks' mm_cpumask for a CPU
* @cpu: a CPU id
diff --git a/kernel/cpu_pm.c b/kernel/cpu_pm.c
index 009cc9a..f3c19d0b 100644
--- a/kernel/cpu_pm.c
+++ b/kernel/cpu_pm.c
@@ -22,14 +22,17 @@
#include <linux/spinlock.h>
#include <linux/syscore_ops.h>
+bool from_suspend;
+
static DEFINE_RWLOCK(cpu_pm_notifier_lock);
static RAW_NOTIFIER_HEAD(cpu_pm_notifier_chain);
-static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
+static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls,
+ void *data)
{
int ret;
- ret = __raw_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL,
+ ret = __raw_notifier_call_chain(&cpu_pm_notifier_chain, event, data,
nr_to_call, nr_calls);
return notifier_to_errno(ret);
@@ -101,13 +104,13 @@
int ret = 0;
read_lock(&cpu_pm_notifier_lock);
- ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls);
+ ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls, NULL);
if (ret)
/*
* Inform listeners (nr_calls - 1) about failure of CPU PM
* PM entry who are notified earlier to prepare for it.
*/
- cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL);
+ cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL, NULL);
read_unlock(&cpu_pm_notifier_lock);
return ret;
@@ -131,7 +134,7 @@
int ret;
read_lock(&cpu_pm_notifier_lock);
- ret = cpu_pm_notify(CPU_PM_EXIT, -1, NULL);
+ ret = cpu_pm_notify(CPU_PM_EXIT, -1, NULL, NULL);
read_unlock(&cpu_pm_notifier_lock);
return ret;
@@ -154,19 +157,21 @@
*
* Return conditions are same as __raw_notifier_call_chain.
*/
-int cpu_cluster_pm_enter(void)
+int cpu_cluster_pm_enter(unsigned long aff_level)
{
int nr_calls;
int ret = 0;
read_lock(&cpu_pm_notifier_lock);
- ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls);
+ ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls,
+ (void *) aff_level);
if (ret)
/*
* Inform listeners (nr_calls - 1) about failure of CPU cluster
* PM entry who are notified earlier to prepare for it.
*/
- cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL);
+ cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL,
+ (void *) aff_level);
read_unlock(&cpu_pm_notifier_lock);
return ret;
@@ -188,12 +193,12 @@
*
* Return conditions are same as __raw_notifier_call_chain.
*/
-int cpu_cluster_pm_exit(void)
+int cpu_cluster_pm_exit(unsigned long aff_level)
{
int ret;
read_lock(&cpu_pm_notifier_lock);
- ret = cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL);
+ ret = cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL, (void *) aff_level);
read_unlock(&cpu_pm_notifier_lock);
return ret;
@@ -205,17 +210,19 @@
{
int ret;
+ from_suspend = true;
ret = cpu_pm_enter();
if (ret)
return ret;
- ret = cpu_cluster_pm_enter();
+ ret = cpu_cluster_pm_enter(0);
return ret;
}
static void cpu_pm_resume(void)
{
- cpu_cluster_pm_exit();
+ from_suspend = false;
+ cpu_cluster_pm_exit(0);
cpu_pm_exit();
}
diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c
index 0874e2e..79517e5 100644
--- a/kernel/debug/debug_core.c
+++ b/kernel/debug/debug_core.c
@@ -598,11 +598,11 @@
/*
* Wait for the other CPUs to be notified and be waiting for us:
*/
- time_left = loops_per_jiffy * HZ;
+ time_left = MSEC_PER_SEC;
while (kgdb_do_roundup && --time_left &&
(atomic_read(&masters_in_kgdb) + atomic_read(&slaves_in_kgdb)) !=
online_cpus)
- cpu_relax();
+ udelay(1000);
if (!time_left)
pr_crit("Timed out waiting for secondary CPUs.\n");
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 9048830..8e901de 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -1474,7 +1474,6 @@
static void
list_add_event(struct perf_event *event, struct perf_event_context *ctx)
{
-
lockdep_assert_held(&ctx->lock);
WARN_ON_ONCE(event->attach_state & PERF_ATTACH_CONTEXT);
@@ -1629,6 +1628,8 @@
{
struct perf_event *group_leader = event->group_leader, *pos;
+ lockdep_assert_held(&event->ctx->lock);
+
/*
* We can have double attach due to group movement in perf_event_open.
*/
@@ -1702,6 +1703,8 @@
struct perf_event *sibling, *tmp;
struct list_head *list = NULL;
+ lockdep_assert_held(&event->ctx->lock);
+
/*
* We can have double detach due to exit/hot-unplug + close.
*/
@@ -1900,9 +1903,29 @@
*/
static void perf_remove_from_context(struct perf_event *event, unsigned long flags)
{
- lockdep_assert_held(&event->ctx->mutex);
+ struct perf_event_context *ctx = event->ctx;
+
+ lockdep_assert_held(&ctx->mutex);
event_function_call(event, __perf_remove_from_context, (void *)flags);
+
+ /*
+ * The above event_function_call() can NO-OP when it hits
+ * TASK_TOMBSTONE. In that case we must already have been detached
+ * from the context (by perf_event_exit_event()) but the grouping
+ * might still be in-tact.
+ */
+ WARN_ON_ONCE(event->attach_state & PERF_ATTACH_CONTEXT);
+ if ((flags & DETACH_GROUP) &&
+ (event->attach_state & PERF_ATTACH_GROUP)) {
+ /*
+ * Since in that case we cannot possibly be scheduled, simply
+ * detach now.
+ */
+ raw_spin_lock_irq(&ctx->lock);
+ perf_group_detach(event);
+ raw_spin_unlock_irq(&ctx->lock);
+ }
}
/*
@@ -6589,6 +6612,27 @@
char *buf = NULL;
char *name;
+ if (vma->vm_flags & VM_READ)
+ prot |= PROT_READ;
+ if (vma->vm_flags & VM_WRITE)
+ prot |= PROT_WRITE;
+ if (vma->vm_flags & VM_EXEC)
+ prot |= PROT_EXEC;
+
+ if (vma->vm_flags & VM_MAYSHARE)
+ flags = MAP_SHARED;
+ else
+ flags = MAP_PRIVATE;
+
+ if (vma->vm_flags & VM_DENYWRITE)
+ flags |= MAP_DENYWRITE;
+ if (vma->vm_flags & VM_MAYEXEC)
+ flags |= MAP_EXECUTABLE;
+ if (vma->vm_flags & VM_LOCKED)
+ flags |= MAP_LOCKED;
+ if (vma->vm_flags & VM_HUGETLB)
+ flags |= MAP_HUGETLB;
+
if (file) {
struct inode *inode;
dev_t dev;
@@ -6615,27 +6659,6 @@
maj = MAJOR(dev);
min = MINOR(dev);
- if (vma->vm_flags & VM_READ)
- prot |= PROT_READ;
- if (vma->vm_flags & VM_WRITE)
- prot |= PROT_WRITE;
- if (vma->vm_flags & VM_EXEC)
- prot |= PROT_EXEC;
-
- if (vma->vm_flags & VM_MAYSHARE)
- flags = MAP_SHARED;
- else
- flags = MAP_PRIVATE;
-
- if (vma->vm_flags & VM_DENYWRITE)
- flags |= MAP_DENYWRITE;
- if (vma->vm_flags & VM_MAYEXEC)
- flags |= MAP_EXECUTABLE;
- if (vma->vm_flags & VM_LOCKED)
- flags |= MAP_LOCKED;
- if (vma->vm_flags & VM_HUGETLB)
- flags |= MAP_HUGETLB;
-
goto got_name;
} else {
if (vma->vm_ops && vma->vm_ops->name) {
@@ -9509,6 +9532,37 @@
return 0;
}
+/*
+ * Variation on perf_event_ctx_lock_nested(), except we take two context
+ * mutexes.
+ */
+static struct perf_event_context *
+__perf_event_ctx_lock_double(struct perf_event *group_leader,
+ struct perf_event_context *ctx)
+{
+ struct perf_event_context *gctx;
+
+again:
+ rcu_read_lock();
+ gctx = READ_ONCE(group_leader->ctx);
+ if (!atomic_inc_not_zero(&gctx->refcount)) {
+ rcu_read_unlock();
+ goto again;
+ }
+ rcu_read_unlock();
+
+ mutex_lock_double(&gctx->mutex, &ctx->mutex);
+
+ if (group_leader->ctx != gctx) {
+ mutex_unlock(&ctx->mutex);
+ mutex_unlock(&gctx->mutex);
+ put_ctx(gctx);
+ goto again;
+ }
+
+ return gctx;
+}
+
/**
* sys_perf_event_open - open a performance event, associate it to a task/cpu
*
@@ -9755,12 +9809,31 @@
}
if (move_group) {
- gctx = group_leader->ctx;
- mutex_lock_double(&gctx->mutex, &ctx->mutex);
+ gctx = __perf_event_ctx_lock_double(group_leader, ctx);
+
if (gctx->task == TASK_TOMBSTONE) {
err = -ESRCH;
goto err_locked;
}
+
+ /*
+ * Check if we raced against another sys_perf_event_open() call
+ * moving the software group underneath us.
+ */
+ if (!(group_leader->group_caps & PERF_EV_CAP_SOFTWARE)) {
+ /*
+ * If someone moved the group out from under us, check
+ * if this new event wound up on the same ctx, if so
+ * its the regular !move_group case, otherwise fail.
+ */
+ if (gctx != ctx) {
+ err = -EINVAL;
+ goto err_locked;
+ } else {
+ perf_event_ctx_unlock(group_leader, gctx);
+ move_group = 0;
+ }
+ }
} else {
mutex_lock(&ctx->mutex);
}
@@ -9862,7 +9935,7 @@
perf_unpin_context(ctx);
if (move_group)
- mutex_unlock(&gctx->mutex);
+ perf_event_ctx_unlock(group_leader, gctx);
mutex_unlock(&ctx->mutex);
if (task) {
@@ -9888,7 +9961,7 @@
err_locked:
if (move_group)
- mutex_unlock(&gctx->mutex);
+ perf_event_ctx_unlock(group_leader, gctx);
mutex_unlock(&ctx->mutex);
/* err_file: */
fput(event_file);
diff --git a/kernel/exit.c b/kernel/exit.c
index 3076f30..46a7c2b 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -55,6 +55,8 @@
#include <linux/shm.h>
#include <linux/kcov.h>
+#include "sched/tune.h"
+
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <asm/pgtable.h>
@@ -775,6 +777,9 @@
}
exit_signals(tsk); /* sets PF_EXITING */
+
+ schedtune_exit_task(tsk);
+
/*
* Ensure that all new tsk->pi_lock acquisitions must observe
* PF_EXITING. Serializes against futex.c:attach_to_pi_owner().
diff --git a/kernel/fork.c b/kernel/fork.c
index 23f9d08..cb4faae 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -745,7 +745,8 @@
#endif
}
-static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
+static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p,
+ struct user_namespace *user_ns)
{
mm->mmap = NULL;
mm->mm_rb = RB_ROOT;
@@ -785,6 +786,7 @@
if (init_new_context(p, mm))
goto fail_nocontext;
+ mm->user_ns = get_user_ns(user_ns);
return mm;
fail_nocontext:
@@ -830,7 +832,7 @@
return NULL;
memset(mm, 0, sizeof(*mm));
- return mm_init(mm, current);
+ return mm_init(mm, current, current_user_ns());
}
/*
@@ -845,6 +847,7 @@
destroy_context(mm);
mmu_notifier_mm_destroy(mm);
check_mm(mm);
+ put_user_ns(mm->user_ns);
free_mm(mm);
}
EXPORT_SYMBOL_GPL(__mmdrop);
@@ -1127,7 +1130,7 @@
memcpy(mm, oldmm, sizeof(*mm));
- if (!mm_init(mm, tsk))
+ if (!mm_init(mm, tsk, mm->user_ns))
goto fail_nomem;
err = dup_mmap(mm, oldmm);
diff --git a/kernel/irq/affinity.c b/kernel/irq/affinity.c
index 17f51d63..668f51b 100644
--- a/kernel/irq/affinity.c
+++ b/kernel/irq/affinity.c
@@ -37,10 +37,10 @@
static int get_nodes_in_cpumask(const struct cpumask *mask, nodemask_t *nodemsk)
{
- int n, nodes;
+ int n, nodes = 0;
/* Calculate the number of nodes in the supplied affinity mask */
- for (n = 0, nodes = 0; n < num_online_nodes(); n++) {
+ for_each_online_node(n) {
if (cpumask_intersects(mask, cpumask_of_node(n))) {
node_set(n, *nodemsk);
nodes++;
@@ -81,7 +81,7 @@
nodes = get_nodes_in_cpumask(affinity, &nodemsk);
/*
- * If the number of nodes in the mask is less than or equal the
+ * If the number of nodes in the mask is greater than or equal the
* number of vectors we just spread the vectors across the nodes.
*/
if (nvec <= nodes) {
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 8c0a0ae..b59e676 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -1346,6 +1346,30 @@
}
EXPORT_SYMBOL_GPL(irq_domain_free_irqs_parent);
+static void __irq_domain_activate_irq(struct irq_data *irq_data)
+{
+ if (irq_data && irq_data->domain) {
+ struct irq_domain *domain = irq_data->domain;
+
+ if (irq_data->parent_data)
+ __irq_domain_activate_irq(irq_data->parent_data);
+ if (domain->ops->activate)
+ domain->ops->activate(domain, irq_data);
+ }
+}
+
+static void __irq_domain_deactivate_irq(struct irq_data *irq_data)
+{
+ if (irq_data && irq_data->domain) {
+ struct irq_domain *domain = irq_data->domain;
+
+ if (domain->ops->deactivate)
+ domain->ops->deactivate(domain, irq_data);
+ if (irq_data->parent_data)
+ __irq_domain_deactivate_irq(irq_data->parent_data);
+ }
+}
+
/**
* irq_domain_activate_irq - Call domain_ops->activate recursively to activate
* interrupt
@@ -1356,13 +1380,9 @@
*/
void irq_domain_activate_irq(struct irq_data *irq_data)
{
- if (irq_data && irq_data->domain) {
- struct irq_domain *domain = irq_data->domain;
-
- if (irq_data->parent_data)
- irq_domain_activate_irq(irq_data->parent_data);
- if (domain->ops->activate)
- domain->ops->activate(domain, irq_data);
+ if (!irqd_is_activated(irq_data)) {
+ __irq_domain_activate_irq(irq_data);
+ irqd_set_activated(irq_data);
}
}
@@ -1376,13 +1396,9 @@
*/
void irq_domain_deactivate_irq(struct irq_data *irq_data)
{
- if (irq_data && irq_data->domain) {
- struct irq_domain *domain = irq_data->domain;
-
- if (domain->ops->deactivate)
- domain->ops->deactivate(domain, irq_data);
- if (irq_data->parent_data)
- irq_domain_deactivate_irq(irq_data->parent_data);
+ if (irqd_is_activated(irq_data)) {
+ __irq_domain_deactivate_irq(irq_data);
+ irqd_clr_activated(irq_data);
}
}
diff --git a/kernel/jump_label.c b/kernel/jump_label.c
index 93ad6c1..a9b8cf5 100644
--- a/kernel/jump_label.c
+++ b/kernel/jump_label.c
@@ -182,6 +182,13 @@
}
EXPORT_SYMBOL_GPL(static_key_slow_dec_deferred);
+void static_key_deferred_flush(struct static_key_deferred *key)
+{
+ STATIC_KEY_CHECK_USE();
+ flush_delayed_work(&key->work);
+}
+EXPORT_SYMBOL_GPL(static_key_deferred_flush);
+
void jump_label_rate_limit(struct static_key_deferred *key,
unsigned long rl)
{
diff --git a/kernel/memremap.c b/kernel/memremap.c
index b501e39..9ecedc2 100644
--- a/kernel/memremap.c
+++ b/kernel/memremap.c
@@ -246,7 +246,9 @@
/* pages are dead and unused, undo the arch mapping */
align_start = res->start & ~(SECTION_SIZE - 1);
align_size = ALIGN(resource_size(res), SECTION_SIZE);
+ mem_hotplug_begin();
arch_remove_memory(align_start, align_size);
+ mem_hotplug_done();
untrack_pfn(NULL, PHYS_PFN(align_start), align_size);
pgmap_radix_release(res);
dev_WARN_ONCE(dev, pgmap->altmap && pgmap->altmap->alloc,
@@ -358,7 +360,9 @@
if (error)
goto err_pfn_remap;
+ mem_hotplug_begin();
error = arch_add_memory(nid, align_start, align_size, true);
+ mem_hotplug_done();
if (error)
goto err_add_memory;
diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c
index df9e8e9..eef2ce9 100644
--- a/kernel/pid_namespace.c
+++ b/kernel/pid_namespace.c
@@ -151,8 +151,12 @@
static void delayed_free_pidns(struct rcu_head *p)
{
- kmem_cache_free(pid_ns_cachep,
- container_of(p, struct pid_namespace, rcu));
+ struct pid_namespace *ns = container_of(p, struct pid_namespace, rcu);
+
+ dec_pid_namespaces(ns->ucounts);
+ put_user_ns(ns->user_ns);
+
+ kmem_cache_free(pid_ns_cachep, ns);
}
static void destroy_pid_namespace(struct pid_namespace *ns)
@@ -162,8 +166,6 @@
ns_free_inum(&ns->ns);
for (i = 0; i < PIDMAP_ENTRIES; i++)
kfree(ns->pidmap[i].page);
- dec_pid_namespaces(ns->ucounts);
- put_user_ns(ns->user_ns);
call_rcu(&ns->rcu, delayed_free_pidns);
}
diff --git a/kernel/power/process.c b/kernel/power/process.c
index b911dec..68d27ae 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -104,13 +104,13 @@
if (wq_busy)
show_workqueue_state();
- read_lock(&tasklist_lock);
- for_each_process_thread(g, p) {
- if (p != current && !freezer_should_skip(p)
- && freezing(p) && !frozen(p))
- sched_show_task(p);
- }
- read_unlock(&tasklist_lock);
+ read_lock(&tasklist_lock);
+ for_each_process_thread(g, p) {
+ if (p != current && !freezer_should_skip(p)
+ && freezing(p) && !frozen(p))
+ sched_show_task(p);
+ }
+ read_unlock(&tasklist_lock);
} else {
pr_cont("(elapsed %d.%03d seconds) ", elapsed_msecs / 1000,
elapsed_msecs % 1000);
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index e6474f7..49ba7c1 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -27,6 +27,35 @@
#include <linux/cn_proc.h>
#include <linux/compat.h>
+/*
+ * Access another process' address space via ptrace.
+ * Source/target buffer must be kernel space,
+ * Do not walk the page table directly, use get_user_pages
+ */
+int ptrace_access_vm(struct task_struct *tsk, unsigned long addr,
+ void *buf, int len, unsigned int gup_flags)
+{
+ struct mm_struct *mm;
+ int ret;
+
+ mm = get_task_mm(tsk);
+ if (!mm)
+ return 0;
+
+ if (!tsk->ptrace ||
+ (current != tsk->parent) ||
+ ((get_dumpable(mm) != SUID_DUMP_USER) &&
+ !ptracer_capable(tsk, mm->user_ns))) {
+ mmput(mm);
+ return 0;
+ }
+
+ ret = __access_remote_vm(tsk, mm, addr, buf, len, gup_flags);
+ mmput(mm);
+
+ return ret;
+}
+
/*
* ptrace a task: make the debugger its new parent and
@@ -39,6 +68,9 @@
BUG_ON(!list_empty(&child->ptrace_entry));
list_add(&child->ptrace_entry, &new_parent->ptraced);
child->parent = new_parent;
+ rcu_read_lock();
+ child->ptracer_cred = get_cred(__task_cred(new_parent));
+ rcu_read_unlock();
}
/**
@@ -71,12 +103,16 @@
*/
void __ptrace_unlink(struct task_struct *child)
{
+ const struct cred *old_cred;
BUG_ON(!child->ptrace);
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->parent = child->real_parent;
list_del_init(&child->ptrace_entry);
+ old_cred = child->ptracer_cred;
+ child->ptracer_cred = NULL;
+ put_cred(old_cred);
spin_lock(&child->sighand->siglock);
child->ptrace = 0;
@@ -220,7 +256,7 @@
static int __ptrace_may_access(struct task_struct *task, unsigned int mode)
{
const struct cred *cred = current_cred(), *tcred;
- int dumpable = 0;
+ struct mm_struct *mm;
kuid_t caller_uid;
kgid_t caller_gid;
@@ -271,16 +307,11 @@
return -EPERM;
ok:
rcu_read_unlock();
- smp_rmb();
- if (task->mm)
- dumpable = get_dumpable(task->mm);
- rcu_read_lock();
- if (dumpable != SUID_DUMP_USER &&
- !ptrace_has_cap(__task_cred(task)->user_ns, mode)) {
- rcu_read_unlock();
- return -EPERM;
- }
- rcu_read_unlock();
+ mm = task->mm;
+ if (mm &&
+ ((get_dumpable(mm) != SUID_DUMP_USER) &&
+ !ptrace_has_cap(mm->user_ns, mode)))
+ return -EPERM;
return security_ptrace_access_check(task, mode);
}
@@ -344,10 +375,6 @@
if (seize)
flags |= PT_SEIZED;
- rcu_read_lock();
- if (ns_capable(__task_cred(task)->user_ns, CAP_SYS_PTRACE))
- flags |= PT_PTRACE_CAP;
- rcu_read_unlock();
task->ptrace = flags;
__ptrace_link(task, current);
@@ -537,7 +564,8 @@
int this_len, retval;
this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
- retval = access_process_vm(tsk, src, buf, this_len, FOLL_FORCE);
+ retval = ptrace_access_vm(tsk, src, buf, this_len, FOLL_FORCE);
+
if (!retval) {
if (copied)
break;
@@ -564,7 +592,7 @@
this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
if (copy_from_user(buf, src, this_len))
return -EFAULT;
- retval = access_process_vm(tsk, dst, buf, this_len,
+ retval = ptrace_access_vm(tsk, dst, buf, this_len,
FOLL_FORCE | FOLL_WRITE);
if (!retval) {
if (copied)
@@ -1128,7 +1156,7 @@
unsigned long tmp;
int copied;
- copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), FOLL_FORCE);
+ copied = ptrace_access_vm(tsk, addr, &tmp, sizeof(tmp), FOLL_FORCE);
if (copied != sizeof(tmp))
return -EIO;
return put_user(tmp, (unsigned long __user *)data);
@@ -1139,7 +1167,7 @@
{
int copied;
- copied = access_process_vm(tsk, addr, &data, sizeof(data),
+ copied = ptrace_access_vm(tsk, addr, &data, sizeof(data),
FOLL_FORCE | FOLL_WRITE);
return (copied == sizeof(data)) ? 0 : -EIO;
}
@@ -1157,7 +1185,7 @@
switch (request) {
case PTRACE_PEEKTEXT:
case PTRACE_PEEKDATA:
- ret = access_process_vm(child, addr, &word, sizeof(word),
+ ret = ptrace_access_vm(child, addr, &word, sizeof(word),
FOLL_FORCE);
if (ret != sizeof(word))
ret = -EIO;
@@ -1167,7 +1195,7 @@
case PTRACE_POKETEXT:
case PTRACE_POKEDATA:
- ret = access_process_vm(child, addr, &data, sizeof(data),
+ ret = ptrace_access_vm(child, addr, &data, sizeof(data),
FOLL_FORCE | FOLL_WRITE);
ret = (ret != sizeof(data) ? -EIO : 0);
break;
diff --git a/kernel/rcu/rcu.h b/kernel/rcu/rcu.h
index 80adef7..0d6ff3e 100644
--- a/kernel/rcu/rcu.h
+++ b/kernel/rcu/rcu.h
@@ -136,6 +136,7 @@
#define TPS(x) tracepoint_string(x)
void rcu_early_boot_tests(void);
+void rcu_test_sync_prims(void);
/*
* This function really isn't for public consumption, but RCU is special in
diff --git a/kernel/rcu/tiny.c b/kernel/rcu/tiny.c
index 1898559..b23a4d0 100644
--- a/kernel/rcu/tiny.c
+++ b/kernel/rcu/tiny.c
@@ -185,9 +185,6 @@
* benefits of doing might_sleep() to reduce latency.)
*
* Cool, huh? (Due to Josh Triplett.)
- *
- * But we want to make this a static inline later. The cond_resched()
- * currently makes this problematic.
*/
void synchronize_sched(void)
{
@@ -195,7 +192,6 @@
lock_is_held(&rcu_lock_map) ||
lock_is_held(&rcu_sched_lock_map),
"Illegal synchronize_sched() in RCU read-side critical section");
- cond_resched();
}
EXPORT_SYMBOL_GPL(synchronize_sched);
diff --git a/kernel/rcu/tiny_plugin.h b/kernel/rcu/tiny_plugin.h
index 196f030..c64b827 100644
--- a/kernel/rcu/tiny_plugin.h
+++ b/kernel/rcu/tiny_plugin.h
@@ -60,12 +60,17 @@
/*
* During boot, we forgive RCU lockdep issues. After this function is
- * invoked, we start taking RCU lockdep issues seriously.
+ * invoked, we start taking RCU lockdep issues seriously. Note that unlike
+ * Tree RCU, Tiny RCU transitions directly from RCU_SCHEDULER_INACTIVE
+ * to RCU_SCHEDULER_RUNNING, skipping the RCU_SCHEDULER_INIT stage.
+ * The reason for this is that Tiny RCU does not need kthreads, so does
+ * not have to care about the fact that the scheduler is half-initialized
+ * at a certain phase of the boot process.
*/
void __init rcu_scheduler_starting(void)
{
WARN_ON(nr_context_switches() > 0);
- rcu_scheduler_active = 1;
+ rcu_scheduler_active = RCU_SCHEDULER_RUNNING;
}
#endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index 69a5611..10f62c6 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -127,13 +127,16 @@
int sysctl_panic_on_rcu_stall __read_mostly;
/*
- * The rcu_scheduler_active variable transitions from zero to one just
- * before the first task is spawned. So when this variable is zero, RCU
- * can assume that there is but one task, allowing RCU to (for example)
+ * The rcu_scheduler_active variable is initialized to the value
+ * RCU_SCHEDULER_INACTIVE and transitions RCU_SCHEDULER_INIT just before the
+ * first task is spawned. So when this variable is RCU_SCHEDULER_INACTIVE,
+ * RCU can assume that there is but one task, allowing RCU to (for example)
* optimize synchronize_rcu() to a simple barrier(). When this variable
- * is one, RCU must actually do all the hard work required to detect real
- * grace periods. This variable is also used to suppress boot-time false
- * positives from lockdep-RCU error checking.
+ * is RCU_SCHEDULER_INIT, RCU must actually do all the hard work required
+ * to detect real grace periods. This variable is also used to suppress
+ * boot-time false positives from lockdep-RCU error checking. Finally, it
+ * transitions from RCU_SCHEDULER_INIT to RCU_SCHEDULER_RUNNING after RCU
+ * is fully initialized, including all of its kthreads having been spawned.
*/
int rcu_scheduler_active __read_mostly;
EXPORT_SYMBOL_GPL(rcu_scheduler_active);
@@ -3985,18 +3988,22 @@
early_initcall(rcu_spawn_gp_kthread);
/*
- * This function is invoked towards the end of the scheduler's initialization
- * process. Before this is called, the idle task might contain
- * RCU read-side critical sections (during which time, this idle
- * task is booting the system). After this function is called, the
- * idle tasks are prohibited from containing RCU read-side critical
- * sections. This function also enables RCU lockdep checking.
+ * This function is invoked towards the end of the scheduler's
+ * initialization process. Before this is called, the idle task might
+ * contain synchronous grace-period primitives (during which time, this idle
+ * task is booting the system, and such primitives are no-ops). After this
+ * function is called, any synchronous grace-period primitives are run as
+ * expedited, with the requesting task driving the grace period forward.
+ * A later core_initcall() rcu_exp_runtime_mode() will switch to full
+ * runtime RCU functionality.
*/
void rcu_scheduler_starting(void)
{
WARN_ON(num_online_cpus() != 1);
WARN_ON(nr_context_switches() > 0);
- rcu_scheduler_active = 1;
+ rcu_test_sync_prims();
+ rcu_scheduler_active = RCU_SCHEDULER_INIT;
+ rcu_test_sync_prims();
}
/*
diff --git a/kernel/rcu/tree_exp.h b/kernel/rcu/tree_exp.h
index 24343eb..78eba41 100644
--- a/kernel/rcu/tree_exp.h
+++ b/kernel/rcu/tree_exp.h
@@ -522,18 +522,28 @@
};
/*
+ * Common code to drive an expedited grace period forward, used by
+ * workqueues and mid-boot-time tasks.
+ */
+static void rcu_exp_sel_wait_wake(struct rcu_state *rsp,
+ smp_call_func_t func, unsigned long s)
+{
+ /* Initialize the rcu_node tree in preparation for the wait. */
+ sync_rcu_exp_select_cpus(rsp, func);
+
+ /* Wait and clean up, including waking everyone. */
+ rcu_exp_wait_wake(rsp, s);
+}
+
+/*
* Work-queue handler to drive an expedited grace period forward.
*/
static void wait_rcu_exp_gp(struct work_struct *wp)
{
struct rcu_exp_work *rewp;
- /* Initialize the rcu_node tree in preparation for the wait. */
rewp = container_of(wp, struct rcu_exp_work, rew_work);
- sync_rcu_exp_select_cpus(rewp->rew_rsp, rewp->rew_func);
-
- /* Wait and clean up, including waking everyone. */
- rcu_exp_wait_wake(rewp->rew_rsp, rewp->rew_s);
+ rcu_exp_sel_wait_wake(rewp->rew_rsp, rewp->rew_func, rewp->rew_s);
}
/*
@@ -559,12 +569,18 @@
if (exp_funnel_lock(rsp, s))
return; /* Someone else did our work for us. */
- /* Marshall arguments and schedule the expedited grace period. */
- rew.rew_func = func;
- rew.rew_rsp = rsp;
- rew.rew_s = s;
- INIT_WORK_ONSTACK(&rew.rew_work, wait_rcu_exp_gp);
- schedule_work(&rew.rew_work);
+ /* Ensure that load happens before action based on it. */
+ if (unlikely(rcu_scheduler_active == RCU_SCHEDULER_INIT)) {
+ /* Direct call during scheduler init and early_initcalls(). */
+ rcu_exp_sel_wait_wake(rsp, func, s);
+ } else {
+ /* Marshall arguments & schedule the expedited grace period. */
+ rew.rew_func = func;
+ rew.rew_rsp = rsp;
+ rew.rew_s = s;
+ INIT_WORK_ONSTACK(&rew.rew_work, wait_rcu_exp_gp);
+ schedule_work(&rew.rew_work);
+ }
/* Wait for expedited grace period to complete. */
rdp = per_cpu_ptr(rsp->rda, raw_smp_processor_id());
@@ -666,6 +682,8 @@
{
struct rcu_state *rsp = rcu_state_p;
+ if (rcu_scheduler_active == RCU_SCHEDULER_INACTIVE)
+ return;
_synchronize_rcu_expedited(rsp, sync_rcu_exp_handler);
}
EXPORT_SYMBOL_GPL(synchronize_rcu_expedited);
@@ -683,3 +701,15 @@
EXPORT_SYMBOL_GPL(synchronize_rcu_expedited);
#endif /* #else #ifdef CONFIG_PREEMPT_RCU */
+
+/*
+ * Switch to run-time mode once Tree RCU has fully initialized.
+ */
+static int __init rcu_exp_runtime_mode(void)
+{
+ rcu_test_sync_prims();
+ rcu_scheduler_active = RCU_SCHEDULER_RUNNING;
+ rcu_test_sync_prims();
+ return 0;
+}
+core_initcall(rcu_exp_runtime_mode);
diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h
index 85c5a88..56583e7 100644
--- a/kernel/rcu/tree_plugin.h
+++ b/kernel/rcu/tree_plugin.h
@@ -670,7 +670,7 @@
lock_is_held(&rcu_lock_map) ||
lock_is_held(&rcu_sched_lock_map),
"Illegal synchronize_rcu() in RCU read-side critical section");
- if (!rcu_scheduler_active)
+ if (rcu_scheduler_active == RCU_SCHEDULER_INACTIVE)
return;
if (rcu_gp_is_expedited())
synchronize_rcu_expedited();
diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c
index f19271dc..4f6db7e 100644
--- a/kernel/rcu/update.c
+++ b/kernel/rcu/update.c
@@ -121,11 +121,14 @@
* Should expedited grace-period primitives always fall back to their
* non-expedited counterparts? Intended for use within RCU. Note
* that if the user specifies both rcu_expedited and rcu_normal, then
- * rcu_normal wins.
+ * rcu_normal wins. (Except during the time period during boot from
+ * when the first task is spawned until the rcu_exp_runtime_mode()
+ * core_initcall() is invoked, at which point everything is expedited.)
*/
bool rcu_gp_is_normal(void)
{
- return READ_ONCE(rcu_normal);
+ return READ_ONCE(rcu_normal) &&
+ rcu_scheduler_active != RCU_SCHEDULER_INIT;
}
EXPORT_SYMBOL_GPL(rcu_gp_is_normal);
@@ -135,13 +138,14 @@
/*
* Should normal grace-period primitives be expedited? Intended for
* use within RCU. Note that this function takes the rcu_expedited
- * sysfs/boot variable into account as well as the rcu_expedite_gp()
- * nesting. So looping on rcu_unexpedite_gp() until rcu_gp_is_expedited()
- * returns false is a -really- bad idea.
+ * sysfs/boot variable and rcu_scheduler_active into account as well
+ * as the rcu_expedite_gp() nesting. So looping on rcu_unexpedite_gp()
+ * until rcu_gp_is_expedited() returns false is a -really- bad idea.
*/
bool rcu_gp_is_expedited(void)
{
- return rcu_expedited || atomic_read(&rcu_expedited_nesting);
+ return rcu_expedited || atomic_read(&rcu_expedited_nesting) ||
+ rcu_scheduler_active == RCU_SCHEDULER_INIT;
}
EXPORT_SYMBOL_GPL(rcu_gp_is_expedited);
@@ -257,7 +261,7 @@
int notrace debug_lockdep_rcu_enabled(void)
{
- return rcu_scheduler_active && debug_locks &&
+ return rcu_scheduler_active != RCU_SCHEDULER_INACTIVE && debug_locks &&
current->lockdep_recursion == 0;
}
EXPORT_SYMBOL_GPL(debug_lockdep_rcu_enabled);
@@ -591,7 +595,7 @@
void synchronize_rcu_tasks(void)
{
/* Complain if the scheduler has not started. */
- RCU_LOCKDEP_WARN(!rcu_scheduler_active,
+ RCU_LOCKDEP_WARN(rcu_scheduler_active == RCU_SCHEDULER_INACTIVE,
"synchronize_rcu_tasks called too soon");
/* Wait for the grace period. */
@@ -813,6 +817,23 @@
#endif /* #ifdef CONFIG_TASKS_RCU */
+/*
+ * Test each non-SRCU synchronous grace-period wait API. This is
+ * useful just after a change in mode for these primitives, and
+ * during early boot.
+ */
+void rcu_test_sync_prims(void)
+{
+ if (!IS_ENABLED(CONFIG_PROVE_RCU))
+ return;
+ synchronize_rcu();
+ synchronize_rcu_bh();
+ synchronize_sched();
+ synchronize_rcu_expedited();
+ synchronize_rcu_bh_expedited();
+ synchronize_sched_expedited();
+}
+
#ifdef CONFIG_PROVE_RCU
/*
@@ -865,6 +886,7 @@
early_boot_test_call_rcu_bh();
if (rcu_self_test_sched)
early_boot_test_call_rcu_sched();
+ rcu_test_sync_prims();
}
static int rcu_verify_early_boot_tests(void)
diff --git a/kernel/relay.c b/kernel/relay.c
index da79a10..8f18d31 100644
--- a/kernel/relay.c
+++ b/kernel/relay.c
@@ -809,11 +809,11 @@
{
struct rchan_buf *buf;
- if (!chan)
+ if (!chan || cpu >= NR_CPUS)
return;
buf = *per_cpu_ptr(chan->buf, cpu);
- if (cpu >= NR_CPUS || !buf || subbufs_consumed > chan->n_subbufs)
+ if (!buf || subbufs_consumed > chan->n_subbufs)
return;
if (subbufs_consumed > buf->subbufs_produced - buf->subbufs_consumed)
diff --git a/kernel/sched/Makefile b/kernel/sched/Makefile
index 11cb1b2..eed4b72 100644
--- a/kernel/sched/Makefile
+++ b/kernel/sched/Makefile
@@ -18,8 +18,8 @@
obj-y += core.o loadavg.o clock.o cputime.o
obj-y += idle_task.o fair.o rt.o deadline.o stop_task.o
obj-y += wait.o swait.o completion.o idle.o sched_avg.o
-obj-$(CONFIG_SMP) += cpupri.o cpudeadline.o
obj-$(CONFIG_SCHED_HMP) += hmp.o boost.o
+obj-$(CONFIG_SMP) += cpupri.o cpudeadline.o energy.o
obj-$(CONFIG_SCHED_AUTOGROUP) += auto_group.o
obj-$(CONFIG_SCHEDSTATS) += stats.o
obj-$(CONFIG_SCHED_DEBUG) += debug.o
@@ -28,3 +28,4 @@
obj-$(CONFIG_CPU_FREQ) += cpufreq.o
obj-$(CONFIG_CPU_FREQ_GOV_SCHEDUTIL) += cpufreq_schedutil.o
obj-$(CONFIG_SCHED_CORE_CTL) += core_ctl.o
+obj-$(CONFIG_CPU_FREQ_GOV_SCHED) += cpufreq_sched.o
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 4f43951..5f82983 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -92,6 +92,7 @@
#define CREATE_TRACE_POINTS
#include <trace/events/sched.h>
+#include "walt.h"
ATOMIC_NOTIFIER_HEAD(load_alert_notifier_head);
@@ -160,6 +161,18 @@
/* cpus with isolated domains */
cpumask_var_t cpu_isolated_map;
+struct rq *
+lock_rq_of(struct task_struct *p, struct rq_flags *flags)
+{
+ return task_rq_lock(p, flags);
+}
+
+void
+unlock_rq_of(struct rq *rq, struct task_struct *p, struct rq_flags *flags)
+{
+ task_rq_unlock(rq, p, flags);
+}
+
/*
* this_rq_lock - lock this runqueue and disable interrupts.
*/
@@ -2337,6 +2350,7 @@
p->se.nr_migrations = 0;
p->se.vruntime = 0;
INIT_LIST_HEAD(&p->se.group_node);
+ walt_init_new_task_load(p);
#ifdef CONFIG_FAIR_GROUP_SCHED
p->se.cfs_rq = NULL;
@@ -2700,6 +2714,9 @@
add_new_task_to_grp(p);
raw_spin_lock_irqsave(&p->pi_lock, rf.flags);
+
+ walt_init_new_task_load(p);
+
p->state = TASK_RUNNING;
#ifdef CONFIG_SMP
/*
@@ -2716,7 +2733,7 @@
post_init_entity_util_avg(&p->se);
mark_task_starting(p);
- activate_task(rq, p, 0);
+ activate_task(rq, p, ENQUEUE_WAKEUP_NEW);
p->on_rq = TASK_ON_RQ_QUEUED;
trace_sched_wakeup_new(p);
check_preempt_curr(rq, p, WF_FORK);
@@ -3101,6 +3118,36 @@
return atomic_read(&this->nr_iowait);
}
+#ifdef CONFIG_CPU_QUIET
+u64 nr_running_integral(unsigned int cpu)
+{
+ unsigned int seqcnt;
+ u64 integral;
+ struct rq *q;
+
+ if (cpu >= nr_cpu_ids)
+ return 0;
+
+ q = cpu_rq(cpu);
+
+ /*
+ * Update average to avoid reading stalled value if there were
+ * no run-queue changes for a long time. On the other hand if
+ * the changes are happening right now, just read current value
+ * directly.
+ */
+
+ seqcnt = read_seqcount_begin(&q->ave_seqcnt);
+ integral = do_nr_running_integral(q);
+ if (read_seqcount_retry(&q->ave_seqcnt, seqcnt)) {
+ read_seqcount_begin(&q->ave_seqcnt);
+ integral = q->nr_running_integral;
+ }
+
+ return integral;
+}
+#endif
+
void get_iowait_load(unsigned long *nr_waiters, unsigned long *load)
{
struct rq *rq = this_rq();
@@ -3205,6 +3252,93 @@
return ns;
}
+#ifdef CONFIG_CPU_FREQ_GOV_SCHED
+
+static inline
+unsigned long add_capacity_margin(unsigned long cpu_capacity)
+{
+ cpu_capacity = cpu_capacity * capacity_margin;
+ cpu_capacity /= SCHED_CAPACITY_SCALE;
+ return cpu_capacity;
+}
+
+static inline
+unsigned long sum_capacity_reqs(unsigned long cfs_cap,
+ struct sched_capacity_reqs *scr)
+{
+ unsigned long total = add_capacity_margin(cfs_cap + scr->rt);
+ return total += scr->dl;
+}
+
+static void sched_freq_tick_pelt(int cpu)
+{
+ unsigned long cpu_utilization = capacity_max;
+ unsigned long capacity_curr = capacity_curr_of(cpu);
+ struct sched_capacity_reqs *scr;
+
+ scr = &per_cpu(cpu_sched_capacity_reqs, cpu);
+ if (sum_capacity_reqs(cpu_utilization, scr) < capacity_curr)
+ return;
+
+ /*
+ * To make free room for a task that is building up its "real"
+ * utilization and to harm its performance the least, request
+ * a jump to a higher OPP as soon as the margin of free capacity
+ * is impacted (specified by capacity_margin).
+ */
+ set_cfs_cpu_capacity(cpu, true, cpu_utilization);
+}
+
+#ifdef CONFIG_SCHED_WALT
+static void sched_freq_tick_walt(int cpu)
+{
+ unsigned long cpu_utilization = cpu_util(cpu);
+ unsigned long capacity_curr = capacity_curr_of(cpu);
+
+ if (walt_disabled || !sysctl_sched_use_walt_cpu_util)
+ return sched_freq_tick_pelt(cpu);
+
+ /*
+ * Add a margin to the WALT utilization.
+ * NOTE: WALT tracks a single CPU signal for all the scheduling
+ * classes, thus this margin is going to be added to the DL class as
+ * well, which is something we do not do in sched_freq_tick_pelt case.
+ */
+ cpu_utilization = add_capacity_margin(cpu_utilization);
+ if (cpu_utilization <= capacity_curr)
+ return;
+
+ /*
+ * It is likely that the load is growing so we
+ * keep the added margin in our request as an
+ * extra boost.
+ */
+ set_cfs_cpu_capacity(cpu, true, cpu_utilization);
+
+}
+#define _sched_freq_tick(cpu) sched_freq_tick_walt(cpu)
+#else
+#define _sched_freq_tick(cpu) sched_freq_tick_pelt(cpu)
+#endif /* CONFIG_SCHED_WALT */
+
+static void sched_freq_tick(int cpu)
+{
+ unsigned long capacity_orig, capacity_curr;
+
+ if (!sched_freq())
+ return;
+
+ capacity_orig = capacity_orig_of(cpu);
+ capacity_curr = capacity_curr_of(cpu);
+ if (capacity_curr == capacity_orig)
+ return;
+
+ _sched_freq_tick(cpu);
+}
+#else
+static inline void sched_freq_tick(int cpu) { }
+#endif /* CONFIG_CPU_FREQ_GOV_SCHED */
+
/*
* This function gets called by the timer code, with HZ frequency.
* We call it with interrupts disabled.
@@ -3229,6 +3363,8 @@
update_rq_clock(rq);
curr->sched_class->task_tick(rq, curr, 0);
cpu_load_update_active(rq);
+ walt_update_task_ravg(rq->curr, rq, TASK_UPDATE,
+ walt_ktime_clock(), 0);
calc_global_load_tick(rq);
wallclock = sched_ktime_clock();
@@ -3258,6 +3394,7 @@
check_for_migration(rq, curr);
core_ctl_check(wallclock);
+ sched_freq_tick(cpu);
}
#ifdef CONFIG_NO_HZ_FULL
@@ -3557,7 +3694,6 @@
update_rq_clock(rq);
next = pick_next_task(rq, prev, cookie);
-
clear_tsk_need_resched(prev);
clear_preempt_need_resched();
rq->clock_skip_update = 0;
@@ -6187,7 +6323,7 @@
printk(KERN_CONT " %*pbl",
cpumask_pr_args(sched_group_cpus(group)));
if (group->sgc->capacity != SCHED_CAPACITY_SCALE) {
- printk(KERN_CONT " (cpu_capacity = %d)",
+ printk(KERN_CONT " (cpu_capacity = %lu)",
group->sgc->capacity);
}
@@ -6251,7 +6387,8 @@
SD_SHARE_CPUCAPACITY |
SD_ASYM_CPUCAPACITY |
SD_SHARE_PKG_RESOURCES |
- SD_SHARE_POWERDOMAIN)) {
+ SD_SHARE_POWERDOMAIN |
+ SD_SHARE_CAP_STATES)) {
if (sd->groups != sd->groups->next)
return 0;
}
@@ -6284,7 +6421,8 @@
SD_SHARE_CPUCAPACITY |
SD_SHARE_PKG_RESOURCES |
SD_PREFER_SIBLING |
- SD_SHARE_POWERDOMAIN);
+ SD_SHARE_POWERDOMAIN |
+ SD_SHARE_CAP_STATES);
if (nr_node_ids == 1)
pflags &= ~SD_SERIALIZE;
}
@@ -6363,6 +6501,8 @@
if (cpupri_init(&rd->cpupri) != 0)
goto free_rto_mask;
+
+ init_max_cpu_capacity(&rd->max_cpu_capacity);
return 0;
free_rto_mask:
@@ -6474,11 +6614,14 @@
DEFINE_PER_CPU(struct sched_domain_shared *, sd_llc_shared);
DEFINE_PER_CPU(struct sched_domain *, sd_numa);
DEFINE_PER_CPU(struct sched_domain *, sd_asym);
+DEFINE_PER_CPU(struct sched_domain *, sd_ea);
+DEFINE_PER_CPU(struct sched_domain *, sd_scs);
static void update_top_cache_domain(int cpu)
{
struct sched_domain_shared *sds = NULL;
struct sched_domain *sd;
+ struct sched_domain *ea_sd = NULL;
int id = cpu;
int size = 1;
@@ -6499,6 +6642,17 @@
sd = highest_flag_domain(cpu, SD_ASYM_PACKING);
rcu_assign_pointer(per_cpu(sd_asym, cpu), sd);
+
+ for_each_domain(cpu, sd) {
+ if (sd->groups->sge)
+ ea_sd = sd;
+ else
+ break;
+ }
+ rcu_assign_pointer(per_cpu(sd_ea, cpu), ea_sd);
+
+ sd = highest_flag_domain(cpu, SD_SHARE_CAP_STATES);
+ rcu_assign_pointer(per_cpu(sd_scs, cpu), sd);
}
/*
@@ -6676,6 +6830,7 @@
* die on a /0 trap.
*/
sg->sgc->capacity = SCHED_CAPACITY_SCALE * cpumask_weight(sg_span);
+ sg->sgc->max_capacity = SCHED_CAPACITY_SCALE;
/*
* Make sure the first group of this domain contains the
@@ -6807,6 +6962,66 @@
}
/*
+ * Check that the per-cpu provided sd energy data is consistent for all cpus
+ * within the mask.
+ */
+static inline void check_sched_energy_data(int cpu, sched_domain_energy_f fn,
+ const struct cpumask *cpumask)
+{
+ const struct sched_group_energy * const sge = fn(cpu);
+ struct cpumask mask;
+ int i;
+
+ if (cpumask_weight(cpumask) <= 1)
+ return;
+
+ cpumask_xor(&mask, cpumask, get_cpu_mask(cpu));
+
+ for_each_cpu(i, &mask) {
+ const struct sched_group_energy * const e = fn(i);
+ int y;
+
+ BUG_ON(e->nr_idle_states != sge->nr_idle_states);
+
+ for (y = 0; y < (e->nr_idle_states); y++) {
+ BUG_ON(e->idle_states[y].power !=
+ sge->idle_states[y].power);
+ }
+
+ BUG_ON(e->nr_cap_states != sge->nr_cap_states);
+
+ for (y = 0; y < (e->nr_cap_states); y++) {
+ BUG_ON(e->cap_states[y].cap != sge->cap_states[y].cap);
+ BUG_ON(e->cap_states[y].power !=
+ sge->cap_states[y].power);
+ }
+ }
+}
+
+static void init_sched_energy(int cpu, struct sched_domain *sd,
+ sched_domain_energy_f fn)
+{
+ if (!(fn && fn(cpu)))
+ return;
+
+ if (cpu != group_balance_cpu(sd->groups))
+ return;
+
+ if (sd->child && !sd->child->groups->sge) {
+ pr_err("BUG: EAS setup broken for CPU%d\n", cpu);
+#ifdef CONFIG_SCHED_DEBUG
+ pr_err(" energy data on %s but not on %s domain\n",
+ sd->name, sd->child->name);
+#endif
+ return;
+ }
+
+ check_sched_energy_data(cpu, fn, sched_group_cpus(sd->groups));
+
+ sd->groups->sge = fn(cpu);
+}
+
+/*
* Initializers for schedule domains
* Non-inlined to reduce accumulated stack pressure in build_sched_domains()
*/
@@ -6922,6 +7137,7 @@
* SD_NUMA - describes NUMA topologies
* SD_SHARE_POWERDOMAIN - describes shared power domain
* SD_ASYM_CPUCAPACITY - describes mixed capacity topologies
+ * SD_SHARE_CAP_STATES - describes shared capacity states
*
* Odd one out, which beside describing the topology has a quirk also
* prescribes the desired behaviour that goes along with it:
@@ -6934,7 +7150,8 @@
SD_NUMA | \
SD_ASYM_PACKING | \
SD_ASYM_CPUCAPACITY | \
- SD_SHARE_POWERDOMAIN)
+ SD_SHARE_POWERDOMAIN | \
+ SD_SHARE_CAP_STATES)
static struct sched_domain *
sd_init(struct sched_domain_topology_level *tl,
@@ -7531,10 +7748,13 @@
/* Calculate CPU capacity for physical packages and nodes */
for (i = nr_cpumask_bits-1; i >= 0; i--) {
+ struct sched_domain_topology_level *tl = sched_domain_topology;
+
if (!cpumask_test_cpu(i, cpu_map))
continue;
- for (sd = *per_cpu_ptr(d.sd, i); sd; sd = sd->parent) {
+ for (sd = *per_cpu_ptr(d.sd, i); sd; sd = sd->parent, tl++) {
+ init_sched_energy(i, sd, tl->energy);
claim_allocations(i, sd);
init_sched_groups_capacity(i, sd);
}
@@ -7545,20 +7765,10 @@
for_each_cpu(i, cpu_map) {
rq = cpu_rq(i);
sd = *per_cpu_ptr(d.sd, i);
-
- /* Use READ_ONCE()/WRITE_ONCE() to avoid load/store tearing: */
- if (rq->cpu_capacity_orig > READ_ONCE(d.rd->max_cpu_capacity))
- WRITE_ONCE(d.rd->max_cpu_capacity, rq->cpu_capacity_orig);
-
cpu_attach_domain(sd, d.rd, i);
}
rcu_read_unlock();
- if (rq && sched_debug_enabled) {
- pr_info("span: %*pbl (max cpu_capacity = %lu)\n",
- cpumask_pr_args(cpu_map), rq->rd->max_cpu_capacity);
- }
-
ret = 0;
error:
__free_domain_allocs(&d, alloc_state, cpu_map);
@@ -7945,6 +8155,7 @@
{
cpumask_var_t non_isolated_cpus;
+ walt_init_cpu_efficiency();
alloc_cpumask_var(&non_isolated_cpus, GFP_KERNEL);
alloc_cpumask_var(&fallback_doms, GFP_KERNEL);
diff --git a/kernel/sched/cpufreq_sched.c b/kernel/sched/cpufreq_sched.c
new file mode 100644
index 0000000..1d471d5
--- /dev/null
+++ b/kernel/sched/cpufreq_sched.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2015 Michael Turquette <mturquette@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/cpufreq.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/percpu.h>
+#include <linux/irq_work.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/cpufreq_sched.h>
+
+#include "sched.h"
+
+#define THROTTLE_DOWN_NSEC 50000000 /* 50ms default */
+#define THROTTLE_UP_NSEC 500000 /* 500us default */
+
+struct static_key __read_mostly __sched_freq = STATIC_KEY_INIT_FALSE;
+static bool __read_mostly cpufreq_driver_slow;
+
+#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_SCHED
+static struct cpufreq_governor cpufreq_gov_sched;
+#endif
+
+static DEFINE_PER_CPU(unsigned long, enabled);
+DEFINE_PER_CPU(struct sched_capacity_reqs, cpu_sched_capacity_reqs);
+
+/**
+ * gov_data - per-policy data internal to the governor
+ * @up_throttle: next throttling period expiry if increasing OPP
+ * @down_throttle: next throttling period expiry if decreasing OPP
+ * @up_throttle_nsec: throttle period length in nanoseconds if increasing OPP
+ * @down_throttle_nsec: throttle period length in nanoseconds if decreasing OPP
+ * @task: worker thread for dvfs transition that may block/sleep
+ * @irq_work: callback used to wake up worker thread
+ * @requested_freq: last frequency requested by the sched governor
+ *
+ * struct gov_data is the per-policy cpufreq_sched-specific data structure. A
+ * per-policy instance of it is created when the cpufreq_sched governor receives
+ * the CPUFREQ_GOV_START condition and a pointer to it exists in the gov_data
+ * member of struct cpufreq_policy.
+ *
+ * Readers of this data must call down_read(policy->rwsem). Writers must
+ * call down_write(policy->rwsem).
+ */
+struct gov_data {
+ ktime_t up_throttle;
+ ktime_t down_throttle;
+ unsigned int up_throttle_nsec;
+ unsigned int down_throttle_nsec;
+ struct task_struct *task;
+ struct irq_work irq_work;
+ unsigned int requested_freq;
+};
+
+static void cpufreq_sched_try_driver_target(struct cpufreq_policy *policy,
+ unsigned int freq)
+{
+ struct gov_data *gd = policy->governor_data;
+
+ /* avoid race with cpufreq_sched_stop */
+ if (!down_write_trylock(&policy->rwsem))
+ return;
+
+ __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L);
+
+ gd->up_throttle = ktime_add_ns(ktime_get(), gd->up_throttle_nsec);
+ gd->down_throttle = ktime_add_ns(ktime_get(), gd->down_throttle_nsec);
+ up_write(&policy->rwsem);
+}
+
+static bool finish_last_request(struct gov_data *gd, unsigned int cur_freq)
+{
+ ktime_t now = ktime_get();
+
+ ktime_t throttle = gd->requested_freq < cur_freq ?
+ gd->down_throttle : gd->up_throttle;
+
+ if (ktime_after(now, throttle))
+ return false;
+
+ while (1) {
+ int usec_left = ktime_to_ns(ktime_sub(throttle, now));
+
+ usec_left /= NSEC_PER_USEC;
+ trace_cpufreq_sched_throttled(usec_left);
+ usleep_range(usec_left, usec_left + 100);
+ now = ktime_get();
+ if (ktime_after(now, throttle))
+ return true;
+ }
+}
+
+/*
+ * we pass in struct cpufreq_policy. This is safe because changing out the
+ * policy requires a call to __cpufreq_governor(policy, CPUFREQ_GOV_STOP),
+ * which tears down all of the data structures and __cpufreq_governor(policy,
+ * CPUFREQ_GOV_START) will do a full rebuild, including this kthread with the
+ * new policy pointer
+ */
+static int cpufreq_sched_thread(void *data)
+{
+ struct sched_param param;
+ struct cpufreq_policy *policy;
+ struct gov_data *gd;
+ unsigned int new_request = 0;
+ unsigned int last_request = 0;
+ int ret;
+
+ policy = (struct cpufreq_policy *) data;
+ gd = policy->governor_data;
+
+ param.sched_priority = 50;
+ ret = sched_setscheduler_nocheck(gd->task, SCHED_FIFO, ¶m);
+ if (ret) {
+ pr_warn("%s: failed to set SCHED_FIFO\n", __func__);
+ do_exit(-EINVAL);
+ } else {
+ pr_debug("%s: kthread (%d) set to SCHED_FIFO\n",
+ __func__, gd->task->pid);
+ }
+
+ do {
+ new_request = gd->requested_freq;
+ if (new_request == last_request) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (kthread_should_stop())
+ break;
+ schedule();
+ } else {
+ /*
+ * if the frequency thread sleeps while waiting to be
+ * unthrottled, start over to check for a newer request
+ */
+ if (finish_last_request(gd, policy->cur))
+ continue;
+ last_request = new_request;
+ cpufreq_sched_try_driver_target(policy, new_request);
+ }
+ } while (!kthread_should_stop());
+
+ return 0;
+}
+
+static void cpufreq_sched_irq_work(struct irq_work *irq_work)
+{
+ struct gov_data *gd;
+
+ gd = container_of(irq_work, struct gov_data, irq_work);
+ if (!gd)
+ return;
+
+ wake_up_process(gd->task);
+}
+
+static void update_fdomain_capacity_request(int cpu)
+{
+ unsigned int freq_new, index_new, cpu_tmp;
+ struct cpufreq_policy *policy;
+ struct gov_data *gd;
+ unsigned long capacity = 0;
+
+ /*
+ * Avoid grabbing the policy if possible. A test is still
+ * required after locking the CPU's policy to avoid racing
+ * with the governor changing.
+ */
+ if (!per_cpu(enabled, cpu))
+ return;
+
+ policy = cpufreq_cpu_get(cpu);
+ if (IS_ERR_OR_NULL(policy))
+ return;
+
+ if (policy->governor != &cpufreq_gov_sched ||
+ !policy->governor_data)
+ goto out;
+
+ gd = policy->governor_data;
+
+ /* find max capacity requested by cpus in this policy */
+ for_each_cpu(cpu_tmp, policy->cpus) {
+ struct sched_capacity_reqs *scr;
+
+ scr = &per_cpu(cpu_sched_capacity_reqs, cpu_tmp);
+ capacity = max(capacity, scr->total);
+ }
+
+ /* Convert the new maximum capacity request into a cpu frequency */
+ freq_new = capacity * policy->max >> SCHED_CAPACITY_SHIFT;
+ index_new = cpufreq_frequency_table_target(policy, freq_new, CPUFREQ_RELATION_L);
+ freq_new = policy->freq_table[index_new].frequency;
+
+ if (freq_new > policy->max)
+ freq_new = policy->max;
+
+ if (freq_new < policy->min)
+ freq_new = policy->min;
+
+ trace_cpufreq_sched_request_opp(cpu, capacity, freq_new,
+ gd->requested_freq);
+ if (freq_new == gd->requested_freq)
+ goto out;
+
+ gd->requested_freq = freq_new;
+
+ /*
+ * Throttling is not yet supported on platforms with fast cpufreq
+ * drivers.
+ */
+ if (cpufreq_driver_slow)
+ irq_work_queue_on(&gd->irq_work, cpu);
+ else
+ cpufreq_sched_try_driver_target(policy, freq_new);
+
+out:
+ cpufreq_cpu_put(policy);
+}
+
+void update_cpu_capacity_request(int cpu, bool request)
+{
+ unsigned long new_capacity;
+ struct sched_capacity_reqs *scr;
+
+ /* The rq lock serializes access to the CPU's sched_capacity_reqs. */
+ lockdep_assert_held(&cpu_rq(cpu)->lock);
+
+ scr = &per_cpu(cpu_sched_capacity_reqs, cpu);
+
+ new_capacity = scr->cfs + scr->rt;
+ new_capacity = new_capacity * capacity_margin
+ / SCHED_CAPACITY_SCALE;
+ new_capacity += scr->dl;
+
+ if (new_capacity == scr->total)
+ return;
+
+ trace_cpufreq_sched_update_capacity(cpu, request, scr, new_capacity);
+
+ scr->total = new_capacity;
+ if (request)
+ update_fdomain_capacity_request(cpu);
+}
+
+static inline void set_sched_freq(void)
+{
+ static_key_slow_inc(&__sched_freq);
+}
+
+static inline void clear_sched_freq(void)
+{
+ static_key_slow_dec(&__sched_freq);
+}
+
+static struct attribute_group sched_attr_group_gov_pol;
+static struct attribute_group *get_sysfs_attr(void)
+{
+ return &sched_attr_group_gov_pol;
+}
+
+static int cpufreq_sched_policy_init(struct cpufreq_policy *policy)
+{
+ struct gov_data *gd;
+ int cpu;
+ int rc;
+
+ for_each_cpu(cpu, policy->cpus)
+ memset(&per_cpu(cpu_sched_capacity_reqs, cpu), 0,
+ sizeof(struct sched_capacity_reqs));
+
+ gd = kzalloc(sizeof(*gd), GFP_KERNEL);
+ if (!gd)
+ return -ENOMEM;
+
+ gd->up_throttle_nsec = policy->cpuinfo.transition_latency ?
+ policy->cpuinfo.transition_latency :
+ THROTTLE_UP_NSEC;
+ gd->down_throttle_nsec = THROTTLE_DOWN_NSEC;
+ pr_debug("%s: throttle threshold = %u [ns]\n",
+ __func__, gd->up_throttle_nsec);
+
+ rc = sysfs_create_group(&policy->kobj, get_sysfs_attr());
+ if (rc) {
+ pr_err("%s: couldn't create sysfs attributes: %d\n", __func__, rc);
+ goto err;
+ }
+
+ policy->governor_data = gd;
+ if (cpufreq_driver_is_slow()) {
+ cpufreq_driver_slow = true;
+ gd->task = kthread_create(cpufreq_sched_thread, policy,
+ "kschedfreq:%d",
+ cpumask_first(policy->related_cpus));
+ if (IS_ERR_OR_NULL(gd->task)) {
+ pr_err("%s: failed to create kschedfreq thread\n",
+ __func__);
+ goto err;
+ }
+ get_task_struct(gd->task);
+ kthread_bind_mask(gd->task, policy->related_cpus);
+ wake_up_process(gd->task);
+ init_irq_work(&gd->irq_work, cpufreq_sched_irq_work);
+ }
+
+ set_sched_freq();
+
+ return 0;
+
+err:
+ policy->governor_data = NULL;
+ kfree(gd);
+ return -ENOMEM;
+}
+
+static void cpufreq_sched_policy_exit(struct cpufreq_policy *policy)
+{
+ struct gov_data *gd = policy->governor_data;
+
+ clear_sched_freq();
+ if (cpufreq_driver_slow) {
+ kthread_stop(gd->task);
+ put_task_struct(gd->task);
+ }
+
+ sysfs_remove_group(&policy->kobj, get_sysfs_attr());
+
+ policy->governor_data = NULL;
+
+ kfree(gd);
+}
+
+static int cpufreq_sched_start(struct cpufreq_policy *policy)
+{
+ int cpu;
+
+ for_each_cpu(cpu, policy->cpus)
+ per_cpu(enabled, cpu) = 1;
+
+ return 0;
+}
+
+static void cpufreq_sched_limits(struct cpufreq_policy *policy)
+{
+ unsigned int clamp_freq;
+ struct gov_data *gd = policy->governor_data;;
+
+ pr_debug("limit event for cpu %u: %u - %u kHz, currently %u kHz\n",
+ policy->cpu, policy->min, policy->max,
+ policy->cur);
+
+ clamp_freq = clamp(gd->requested_freq, policy->min, policy->max);
+
+ if (policy->cur != clamp_freq)
+ __cpufreq_driver_target(policy, clamp_freq, CPUFREQ_RELATION_L);
+}
+
+static void cpufreq_sched_stop(struct cpufreq_policy *policy)
+{
+ int cpu;
+
+ for_each_cpu(cpu, policy->cpus)
+ per_cpu(enabled, cpu) = 0;
+}
+
+/* Tunables */
+static ssize_t show_up_throttle_nsec(struct gov_data *gd, char *buf)
+{
+ return sprintf(buf, "%u\n", gd->up_throttle_nsec);
+}
+
+static ssize_t store_up_throttle_nsec(struct gov_data *gd,
+ const char *buf, size_t count)
+{
+ int ret;
+ long unsigned int val;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+ gd->up_throttle_nsec = val;
+ return count;
+}
+
+static ssize_t show_down_throttle_nsec(struct gov_data *gd, char *buf)
+{
+ return sprintf(buf, "%u\n", gd->down_throttle_nsec);
+}
+
+static ssize_t store_down_throttle_nsec(struct gov_data *gd,
+ const char *buf, size_t count)
+{
+ int ret;
+ long unsigned int val;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+ gd->down_throttle_nsec = val;
+ return count;
+}
+
+/*
+ * Create show/store routines
+ * - sys: One governor instance for complete SYSTEM
+ * - pol: One governor instance per struct cpufreq_policy
+ */
+#define show_gov_pol_sys(file_name) \
+static ssize_t show_##file_name##_gov_pol \
+(struct cpufreq_policy *policy, char *buf) \
+{ \
+ return show_##file_name(policy->governor_data, buf); \
+}
+
+#define store_gov_pol_sys(file_name) \
+static ssize_t store_##file_name##_gov_pol \
+(struct cpufreq_policy *policy, const char *buf, size_t count) \
+{ \
+ return store_##file_name(policy->governor_data, buf, count); \
+}
+
+#define gov_pol_attr_rw(_name) \
+ static struct freq_attr _name##_gov_pol = \
+ __ATTR(_name, 0644, show_##_name##_gov_pol, store_##_name##_gov_pol)
+
+#define show_store_gov_pol_sys(file_name) \
+ show_gov_pol_sys(file_name); \
+ store_gov_pol_sys(file_name)
+#define tunable_handlers(file_name) \
+ show_gov_pol_sys(file_name); \
+ store_gov_pol_sys(file_name); \
+ gov_pol_attr_rw(file_name)
+
+tunable_handlers(down_throttle_nsec);
+tunable_handlers(up_throttle_nsec);
+
+/* Per policy governor instance */
+static struct attribute *sched_attributes_gov_pol[] = {
+ &up_throttle_nsec_gov_pol.attr,
+ &down_throttle_nsec_gov_pol.attr,
+ NULL,
+};
+
+static struct attribute_group sched_attr_group_gov_pol = {
+ .attrs = sched_attributes_gov_pol,
+ .name = "sched",
+};
+
+#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_SCHED
+static
+#endif
+struct cpufreq_governor cpufreq_gov_sched = {
+ .name = "sched",
+ .init = cpufreq_sched_policy_init,
+ .exit = cpufreq_sched_policy_exit,
+ .start = cpufreq_sched_start,
+ .stop = cpufreq_sched_stop,
+ .limits = cpufreq_sched_limits,
+ .owner = THIS_MODULE,
+};
+
+static int __init cpufreq_sched_init(void)
+{
+ int cpu;
+
+ for_each_cpu(cpu, cpu_possible_mask)
+ per_cpu(enabled, cpu) = 0;
+ return cpufreq_register_governor(&cpufreq_gov_sched);
+}
+
+#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_SCHED
+struct cpufreq_governor *cpufreq_default_governor(void)
+{
+ return &cpufreq_gov_sched;
+}
+#endif
+
+/* Try to make this the default governor */
+fs_initcall(cpufreq_sched_init);
diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
deleted file mode 100644
index 69e0689..0000000
--- a/kernel/sched/cpufreq_schedutil.c
+++ /dev/null
@@ -1,576 +0,0 @@
-/*
- * CPUFreq governor based on scheduler-provided CPU utilization data.
- *
- * Copyright (C) 2016, Intel Corporation
- * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/cpufreq.h>
-#include <linux/slab.h>
-#include <trace/events/power.h>
-
-#include "sched.h"
-
-struct sugov_tunables {
- struct gov_attr_set attr_set;
- unsigned int rate_limit_us;
-};
-
-struct sugov_policy {
- struct cpufreq_policy *policy;
-
- struct sugov_tunables *tunables;
- struct list_head tunables_hook;
-
- raw_spinlock_t update_lock; /* For shared policies */
- u64 last_freq_update_time;
- s64 freq_update_delay_ns;
- unsigned int next_freq;
-
- /* The next fields are only needed if fast switch cannot be used. */
- struct irq_work irq_work;
- struct work_struct work;
- struct mutex work_lock;
- bool work_in_progress;
-
- bool need_freq_update;
-};
-
-struct sugov_cpu {
- struct update_util_data update_util;
- struct sugov_policy *sg_policy;
-
- unsigned int cached_raw_freq;
- unsigned long iowait_boost;
- unsigned long iowait_boost_max;
- u64 last_update;
-
- /* The fields below are only needed when sharing a policy. */
- unsigned long util;
- unsigned long max;
- unsigned int flags;
-};
-
-static DEFINE_PER_CPU(struct sugov_cpu, sugov_cpu);
-
-/************************ Governor internals ***********************/
-
-static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time)
-{
- s64 delta_ns;
-
- if (sg_policy->work_in_progress)
- return false;
-
- if (unlikely(sg_policy->need_freq_update)) {
- sg_policy->need_freq_update = false;
- /*
- * This happens when limits change, so forget the previous
- * next_freq value and force an update.
- */
- sg_policy->next_freq = UINT_MAX;
- return true;
- }
-
- delta_ns = time - sg_policy->last_freq_update_time;
- return delta_ns >= sg_policy->freq_update_delay_ns;
-}
-
-static void sugov_update_commit(struct sugov_policy *sg_policy, u64 time,
- unsigned int next_freq)
-{
- struct cpufreq_policy *policy = sg_policy->policy;
-
- sg_policy->last_freq_update_time = time;
-
- if (policy->fast_switch_enabled) {
- if (sg_policy->next_freq == next_freq) {
- trace_cpu_frequency(policy->cur, smp_processor_id());
- return;
- }
- sg_policy->next_freq = next_freq;
- next_freq = cpufreq_driver_fast_switch(policy, next_freq);
- if (next_freq == CPUFREQ_ENTRY_INVALID)
- return;
-
- policy->cur = next_freq;
- trace_cpu_frequency(next_freq, smp_processor_id());
- } else if (sg_policy->next_freq != next_freq) {
- sg_policy->next_freq = next_freq;
- sg_policy->work_in_progress = true;
- irq_work_queue(&sg_policy->irq_work);
- }
-}
-
-/**
- * get_next_freq - Compute a new frequency for a given cpufreq policy.
- * @sg_cpu: schedutil cpu object to compute the new frequency for.
- * @util: Current CPU utilization.
- * @max: CPU capacity.
- *
- * If the utilization is frequency-invariant, choose the new frequency to be
- * proportional to it, that is
- *
- * next_freq = C * max_freq * util / max
- *
- * Otherwise, approximate the would-be frequency-invariant utilization by
- * util_raw * (curr_freq / max_freq) which leads to
- *
- * next_freq = C * curr_freq * util_raw / max
- *
- * Take C = 1.25 for the frequency tipping point at (util / max) = 0.8.
- *
- * The lowest driver-supported frequency which is equal or greater than the raw
- * next_freq (as calculated above) is returned, subject to policy min/max and
- * cpufreq driver limitations.
- */
-static unsigned int get_next_freq(struct sugov_cpu *sg_cpu, unsigned long util,
- unsigned long max)
-{
- struct sugov_policy *sg_policy = sg_cpu->sg_policy;
- struct cpufreq_policy *policy = sg_policy->policy;
- unsigned int freq = arch_scale_freq_invariant() ?
- policy->cpuinfo.max_freq : policy->cur;
-
- freq = (freq + (freq >> 2)) * util / max;
-
- if (freq == sg_cpu->cached_raw_freq && sg_policy->next_freq != UINT_MAX)
- return sg_policy->next_freq;
- sg_cpu->cached_raw_freq = freq;
- return cpufreq_driver_resolve_freq(policy, freq);
-}
-
-static void sugov_get_util(unsigned long *util, unsigned long *max)
-{
- struct rq *rq = this_rq();
- unsigned long cfs_max;
-
- cfs_max = arch_scale_cpu_capacity(NULL, smp_processor_id());
-
- *util = min(rq->cfs.avg.util_avg, cfs_max);
- *max = cfs_max;
-}
-
-static void sugov_set_iowait_boost(struct sugov_cpu *sg_cpu, u64 time,
- unsigned int flags)
-{
- if (flags & SCHED_CPUFREQ_IOWAIT) {
- sg_cpu->iowait_boost = sg_cpu->iowait_boost_max;
- } else if (sg_cpu->iowait_boost) {
- s64 delta_ns = time - sg_cpu->last_update;
-
- /* Clear iowait_boost if the CPU apprears to have been idle. */
- if (delta_ns > TICK_NSEC)
- sg_cpu->iowait_boost = 0;
- }
-}
-
-static void sugov_iowait_boost(struct sugov_cpu *sg_cpu, unsigned long *util,
- unsigned long *max)
-{
- unsigned long boost_util = sg_cpu->iowait_boost;
- unsigned long boost_max = sg_cpu->iowait_boost_max;
-
- if (!boost_util)
- return;
-
- if (*util * boost_max < *max * boost_util) {
- *util = boost_util;
- *max = boost_max;
- }
- sg_cpu->iowait_boost >>= 1;
-}
-
-static void sugov_update_single(struct update_util_data *hook, u64 time,
- unsigned int flags)
-{
- struct sugov_cpu *sg_cpu = container_of(hook, struct sugov_cpu, update_util);
- struct sugov_policy *sg_policy = sg_cpu->sg_policy;
- struct cpufreq_policy *policy = sg_policy->policy;
- unsigned long util, max;
- unsigned int next_f;
-
- sugov_set_iowait_boost(sg_cpu, time, flags);
- sg_cpu->last_update = time;
-
- if (!sugov_should_update_freq(sg_policy, time))
- return;
-
- if (flags & SCHED_CPUFREQ_RT_DL) {
- next_f = policy->cpuinfo.max_freq;
- } else {
- sugov_get_util(&util, &max);
- sugov_iowait_boost(sg_cpu, &util, &max);
- next_f = get_next_freq(sg_cpu, util, max);
- }
- sugov_update_commit(sg_policy, time, next_f);
-}
-
-static unsigned int sugov_next_freq_shared(struct sugov_cpu *sg_cpu,
- unsigned long util, unsigned long max,
- unsigned int flags)
-{
- struct sugov_policy *sg_policy = sg_cpu->sg_policy;
- struct cpufreq_policy *policy = sg_policy->policy;
- unsigned int max_f = policy->cpuinfo.max_freq;
- u64 last_freq_update_time = sg_policy->last_freq_update_time;
- unsigned int j;
-
- if (flags & SCHED_CPUFREQ_RT_DL)
- return max_f;
-
- sugov_iowait_boost(sg_cpu, &util, &max);
-
- for_each_cpu(j, policy->cpus) {
- struct sugov_cpu *j_sg_cpu;
- unsigned long j_util, j_max;
- s64 delta_ns;
-
- if (j == smp_processor_id())
- continue;
-
- j_sg_cpu = &per_cpu(sugov_cpu, j);
- /*
- * If the CPU utilization was last updated before the previous
- * frequency update and the time elapsed between the last update
- * of the CPU utilization and the last frequency update is long
- * enough, don't take the CPU into account as it probably is
- * idle now (and clear iowait_boost for it).
- */
- delta_ns = last_freq_update_time - j_sg_cpu->last_update;
- if (delta_ns > TICK_NSEC) {
- j_sg_cpu->iowait_boost = 0;
- continue;
- }
- if (j_sg_cpu->flags & SCHED_CPUFREQ_RT_DL)
- return max_f;
-
- j_util = j_sg_cpu->util;
- j_max = j_sg_cpu->max;
- if (j_util * max > j_max * util) {
- util = j_util;
- max = j_max;
- }
-
- sugov_iowait_boost(j_sg_cpu, &util, &max);
- }
-
- return get_next_freq(sg_cpu, util, max);
-}
-
-static void sugov_update_shared(struct update_util_data *hook, u64 time,
- unsigned int flags)
-{
- struct sugov_cpu *sg_cpu = container_of(hook, struct sugov_cpu, update_util);
- struct sugov_policy *sg_policy = sg_cpu->sg_policy;
- unsigned long util, max;
- unsigned int next_f;
-
- sugov_get_util(&util, &max);
-
- raw_spin_lock(&sg_policy->update_lock);
-
- sg_cpu->util = util;
- sg_cpu->max = max;
- sg_cpu->flags = flags;
-
- sugov_set_iowait_boost(sg_cpu, time, flags);
- sg_cpu->last_update = time;
-
- if (sugov_should_update_freq(sg_policy, time)) {
- next_f = sugov_next_freq_shared(sg_cpu, util, max, flags);
- sugov_update_commit(sg_policy, time, next_f);
- }
-
- raw_spin_unlock(&sg_policy->update_lock);
-}
-
-static void sugov_work(struct work_struct *work)
-{
- struct sugov_policy *sg_policy = container_of(work, struct sugov_policy, work);
-
- mutex_lock(&sg_policy->work_lock);
- __cpufreq_driver_target(sg_policy->policy, sg_policy->next_freq,
- CPUFREQ_RELATION_L);
- mutex_unlock(&sg_policy->work_lock);
-
- sg_policy->work_in_progress = false;
-}
-
-static void sugov_irq_work(struct irq_work *irq_work)
-{
- struct sugov_policy *sg_policy;
-
- sg_policy = container_of(irq_work, struct sugov_policy, irq_work);
- schedule_work_on(smp_processor_id(), &sg_policy->work);
-}
-
-/************************** sysfs interface ************************/
-
-static struct sugov_tunables *global_tunables;
-static DEFINE_MUTEX(global_tunables_lock);
-
-static inline struct sugov_tunables *to_sugov_tunables(struct gov_attr_set *attr_set)
-{
- return container_of(attr_set, struct sugov_tunables, attr_set);
-}
-
-static ssize_t rate_limit_us_show(struct gov_attr_set *attr_set, char *buf)
-{
- struct sugov_tunables *tunables = to_sugov_tunables(attr_set);
-
- return sprintf(buf, "%u\n", tunables->rate_limit_us);
-}
-
-static ssize_t rate_limit_us_store(struct gov_attr_set *attr_set, const char *buf,
- size_t count)
-{
- struct sugov_tunables *tunables = to_sugov_tunables(attr_set);
- struct sugov_policy *sg_policy;
- unsigned int rate_limit_us;
-
- if (kstrtouint(buf, 10, &rate_limit_us))
- return -EINVAL;
-
- tunables->rate_limit_us = rate_limit_us;
-
- list_for_each_entry(sg_policy, &attr_set->policy_list, tunables_hook)
- sg_policy->freq_update_delay_ns = rate_limit_us * NSEC_PER_USEC;
-
- return count;
-}
-
-static struct governor_attr rate_limit_us = __ATTR_RW(rate_limit_us);
-
-static struct attribute *sugov_attributes[] = {
- &rate_limit_us.attr,
- NULL
-};
-
-static struct kobj_type sugov_tunables_ktype = {
- .default_attrs = sugov_attributes,
- .sysfs_ops = &governor_sysfs_ops,
-};
-
-/********************** cpufreq governor interface *********************/
-
-static struct cpufreq_governor schedutil_gov;
-
-static struct sugov_policy *sugov_policy_alloc(struct cpufreq_policy *policy)
-{
- struct sugov_policy *sg_policy;
-
- sg_policy = kzalloc(sizeof(*sg_policy), GFP_KERNEL);
- if (!sg_policy)
- return NULL;
-
- sg_policy->policy = policy;
- init_irq_work(&sg_policy->irq_work, sugov_irq_work);
- INIT_WORK(&sg_policy->work, sugov_work);
- mutex_init(&sg_policy->work_lock);
- raw_spin_lock_init(&sg_policy->update_lock);
- return sg_policy;
-}
-
-static void sugov_policy_free(struct sugov_policy *sg_policy)
-{
- mutex_destroy(&sg_policy->work_lock);
- kfree(sg_policy);
-}
-
-static struct sugov_tunables *sugov_tunables_alloc(struct sugov_policy *sg_policy)
-{
- struct sugov_tunables *tunables;
-
- tunables = kzalloc(sizeof(*tunables), GFP_KERNEL);
- if (tunables) {
- gov_attr_set_init(&tunables->attr_set, &sg_policy->tunables_hook);
- if (!have_governor_per_policy())
- global_tunables = tunables;
- }
- return tunables;
-}
-
-static void sugov_tunables_free(struct sugov_tunables *tunables)
-{
- if (!have_governor_per_policy())
- global_tunables = NULL;
-
- kfree(tunables);
-}
-
-static int sugov_init(struct cpufreq_policy *policy)
-{
- struct sugov_policy *sg_policy;
- struct sugov_tunables *tunables;
- unsigned int lat;
- int ret = 0;
-
- /* State should be equivalent to EXIT */
- if (policy->governor_data)
- return -EBUSY;
-
- sg_policy = sugov_policy_alloc(policy);
- if (!sg_policy)
- return -ENOMEM;
-
- mutex_lock(&global_tunables_lock);
-
- if (global_tunables) {
- if (WARN_ON(have_governor_per_policy())) {
- ret = -EINVAL;
- goto free_sg_policy;
- }
- policy->governor_data = sg_policy;
- sg_policy->tunables = global_tunables;
-
- gov_attr_set_get(&global_tunables->attr_set, &sg_policy->tunables_hook);
- goto out;
- }
-
- tunables = sugov_tunables_alloc(sg_policy);
- if (!tunables) {
- ret = -ENOMEM;
- goto free_sg_policy;
- }
-
- tunables->rate_limit_us = LATENCY_MULTIPLIER;
- lat = policy->cpuinfo.transition_latency / NSEC_PER_USEC;
- if (lat)
- tunables->rate_limit_us *= lat;
-
- policy->governor_data = sg_policy;
- sg_policy->tunables = tunables;
-
- ret = kobject_init_and_add(&tunables->attr_set.kobj, &sugov_tunables_ktype,
- get_governor_parent_kobj(policy), "%s",
- schedutil_gov.name);
- if (ret)
- goto fail;
-
- out:
- mutex_unlock(&global_tunables_lock);
-
- cpufreq_enable_fast_switch(policy);
- return 0;
-
- fail:
- policy->governor_data = NULL;
- sugov_tunables_free(tunables);
-
- free_sg_policy:
- mutex_unlock(&global_tunables_lock);
-
- sugov_policy_free(sg_policy);
- pr_err("initialization failed (error %d)\n", ret);
- return ret;
-}
-
-static void sugov_exit(struct cpufreq_policy *policy)
-{
- struct sugov_policy *sg_policy = policy->governor_data;
- struct sugov_tunables *tunables = sg_policy->tunables;
- unsigned int count;
-
- cpufreq_disable_fast_switch(policy);
-
- mutex_lock(&global_tunables_lock);
-
- count = gov_attr_set_put(&tunables->attr_set, &sg_policy->tunables_hook);
- policy->governor_data = NULL;
- if (!count)
- sugov_tunables_free(tunables);
-
- mutex_unlock(&global_tunables_lock);
-
- sugov_policy_free(sg_policy);
-}
-
-static int sugov_start(struct cpufreq_policy *policy)
-{
- struct sugov_policy *sg_policy = policy->governor_data;
- unsigned int cpu;
-
- sg_policy->freq_update_delay_ns = sg_policy->tunables->rate_limit_us * NSEC_PER_USEC;
- sg_policy->last_freq_update_time = 0;
- sg_policy->next_freq = UINT_MAX;
- sg_policy->work_in_progress = false;
- sg_policy->need_freq_update = false;
-
- for_each_cpu(cpu, policy->cpus) {
- struct sugov_cpu *sg_cpu = &per_cpu(sugov_cpu, cpu);
-
- sg_cpu->sg_policy = sg_policy;
- if (policy_is_shared(policy)) {
- sg_cpu->util = 0;
- sg_cpu->max = 0;
- sg_cpu->flags = SCHED_CPUFREQ_RT;
- sg_cpu->last_update = 0;
- sg_cpu->cached_raw_freq = 0;
- sg_cpu->iowait_boost = 0;
- sg_cpu->iowait_boost_max = policy->cpuinfo.max_freq;
- cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util,
- sugov_update_shared);
- } else {
- cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util,
- sugov_update_single);
- }
- }
- return 0;
-}
-
-static void sugov_stop(struct cpufreq_policy *policy)
-{
- struct sugov_policy *sg_policy = policy->governor_data;
- unsigned int cpu;
-
- for_each_cpu(cpu, policy->cpus)
- cpufreq_remove_update_util_hook(cpu);
-
- synchronize_sched();
-
- irq_work_sync(&sg_policy->irq_work);
- cancel_work_sync(&sg_policy->work);
-}
-
-static void sugov_limits(struct cpufreq_policy *policy)
-{
- struct sugov_policy *sg_policy = policy->governor_data;
-
- if (!policy->fast_switch_enabled) {
- mutex_lock(&sg_policy->work_lock);
- cpufreq_policy_apply_limits(policy);
- mutex_unlock(&sg_policy->work_lock);
- }
-
- sg_policy->need_freq_update = true;
-}
-
-static struct cpufreq_governor schedutil_gov = {
- .name = "schedutil",
- .owner = THIS_MODULE,
- .init = sugov_init,
- .exit = sugov_exit,
- .start = sugov_start,
- .stop = sugov_stop,
- .limits = sugov_limits,
-};
-
-#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL
-struct cpufreq_governor *cpufreq_default_governor(void)
-{
- return &schedutil_gov;
-}
-#endif
-
-static int __init sugov_register(void)
-{
- return cpufreq_register_governor(&schedutil_gov);
-}
-fs_initcall(sugov_register);
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index 6ce492f..0d8cc06 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -8,7 +8,7 @@
#ifdef CONFIG_PARAVIRT
#include <asm/paravirt.h>
#endif
-
+#include "walt.h"
#ifdef CONFIG_IRQ_TIME_ACCOUNTING
diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c
index 8cc5256..ae8bd29 100644
--- a/kernel/sched/debug.c
+++ b/kernel/sched/debug.c
@@ -261,9 +261,60 @@
}
static struct ctl_table *
+sd_alloc_ctl_energy_table(struct sched_group_energy *sge)
+{
+ struct ctl_table *table = sd_alloc_ctl_entry(5);
+
+ if (table == NULL)
+ return NULL;
+
+ set_table_entry(&table[0], "nr_idle_states", &sge->nr_idle_states,
+ sizeof(int), 0644, proc_dointvec_minmax, false);
+ set_table_entry(&table[1], "idle_states", &sge->idle_states[0].power,
+ sge->nr_idle_states*sizeof(struct idle_state), 0644,
+ proc_doulongvec_minmax, false);
+ set_table_entry(&table[2], "nr_cap_states", &sge->nr_cap_states,
+ sizeof(int), 0644, proc_dointvec_minmax, false);
+ set_table_entry(&table[3], "cap_states", &sge->cap_states[0].cap,
+ sge->nr_cap_states*sizeof(struct capacity_state), 0644,
+ proc_doulongvec_minmax, false);
+
+ return table;
+}
+
+static struct ctl_table *
+sd_alloc_ctl_group_table(struct sched_group *sg)
+{
+ struct ctl_table *table = sd_alloc_ctl_entry(2);
+
+ if (table == NULL)
+ return NULL;
+
+ table->procname = kstrdup("energy", GFP_KERNEL);
+ table->mode = 0555;
+ table->child = sd_alloc_ctl_energy_table((struct sched_group_energy *)sg->sge);
+
+ return table;
+}
+
+static struct ctl_table *
sd_alloc_ctl_domain_table(struct sched_domain *sd)
{
- struct ctl_table *table = sd_alloc_ctl_entry(14);
+ struct ctl_table *table;
+ unsigned int nr_entries = 14;
+
+ int i = 0;
+ struct sched_group *sg = sd->groups;
+
+ if (sg->sge) {
+ int nr_sgs = 0;
+
+ do {} while (nr_sgs++, sg = sg->next, sg != sd->groups);
+
+ nr_entries += nr_sgs;
+ }
+
+ table = sd_alloc_ctl_entry(nr_entries);
if (table == NULL)
return NULL;
@@ -296,7 +347,19 @@
sizeof(long), 0644, proc_doulongvec_minmax, false);
set_table_entry(&table[12], "name", sd->name,
CORENAME_MAX_SIZE, 0444, proc_dostring, false);
- /* &table[13] is terminator */
+ sg = sd->groups;
+ if (sg->sge) {
+ char buf[32];
+ struct ctl_table *entry = &table[13];
+
+ do {
+ snprintf(buf, 32, "group%d", i);
+ entry->procname = kstrdup(buf, GFP_KERNEL);
+ entry->mode = 0555;
+ entry->child = sd_alloc_ctl_group_table(sg);
+ } while (entry++, i++, sg = sg->next, sg != sd->groups);
+ }
+ /* &table[nr_entries-1] is terminator */
return table;
}
diff --git a/kernel/sched/energy.c b/kernel/sched/energy.c
new file mode 100644
index 0000000..b0656b7
--- /dev/null
+++ b/kernel/sched/energy.c
@@ -0,0 +1,124 @@
+/*
+ * Obtain energy cost data from DT and populate relevant scheduler data
+ * structures.
+ *
+ * Copyright (C) 2015 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#define pr_fmt(fmt) "sched-energy: " fmt
+
+#define DEBUG
+
+#include <linux/gfp.h>
+#include <linux/of.h>
+#include <linux/printk.h>
+#include <linux/sched.h>
+#include <linux/sched_energy.h>
+#include <linux/stddef.h>
+
+struct sched_group_energy *sge_array[NR_CPUS][NR_SD_LEVELS];
+
+static void free_resources(void)
+{
+ int cpu, sd_level;
+ struct sched_group_energy *sge;
+
+ for_each_possible_cpu(cpu) {
+ for_each_possible_sd_level(sd_level) {
+ sge = sge_array[cpu][sd_level];
+ if (sge) {
+ kfree(sge->cap_states);
+ kfree(sge->idle_states);
+ kfree(sge);
+ }
+ }
+ }
+}
+
+void init_sched_energy_costs(void)
+{
+ struct device_node *cn, *cp;
+ struct capacity_state *cap_states;
+ struct idle_state *idle_states;
+ struct sched_group_energy *sge;
+ const struct property *prop;
+ int sd_level, i, nstates, cpu;
+ const __be32 *val;
+
+ for_each_possible_cpu(cpu) {
+ cn = of_get_cpu_node(cpu, NULL);
+ if (!cn) {
+ pr_warn("CPU device node missing for CPU %d\n", cpu);
+ return;
+ }
+
+ if (!of_find_property(cn, "sched-energy-costs", NULL)) {
+ pr_warn("CPU device node has no sched-energy-costs\n");
+ return;
+ }
+
+ for_each_possible_sd_level(sd_level) {
+ cp = of_parse_phandle(cn, "sched-energy-costs", sd_level);
+ if (!cp)
+ break;
+
+ prop = of_find_property(cp, "busy-cost-data", NULL);
+ if (!prop || !prop->value) {
+ pr_warn("No busy-cost data, skipping sched_energy init\n");
+ goto out;
+ }
+
+ sge = kcalloc(1, sizeof(struct sched_group_energy),
+ GFP_NOWAIT);
+
+ nstates = (prop->length / sizeof(u32)) / 2;
+ cap_states = kcalloc(nstates,
+ sizeof(struct capacity_state),
+ GFP_NOWAIT);
+
+ for (i = 0, val = prop->value; i < nstates; i++) {
+ cap_states[i].cap = be32_to_cpup(val++);
+ cap_states[i].power = be32_to_cpup(val++);
+ }
+
+ sge->nr_cap_states = nstates;
+ sge->cap_states = cap_states;
+
+ prop = of_find_property(cp, "idle-cost-data", NULL);
+ if (!prop || !prop->value) {
+ pr_warn("No idle-cost data, skipping sched_energy init\n");
+ goto out;
+ }
+
+ nstates = (prop->length / sizeof(u32));
+ idle_states = kcalloc(nstates,
+ sizeof(struct idle_state),
+ GFP_NOWAIT);
+
+ for (i = 0, val = prop->value; i < nstates; i++)
+ idle_states[i].power = be32_to_cpup(val++);
+
+ sge->nr_idle_states = nstates;
+ sge->idle_states = idle_states;
+
+ sge_array[cpu][sd_level] = sge;
+ }
+ }
+
+ pr_info("Sched-energy-costs installed from DT\n");
+ return;
+
+out:
+ free_resources();
+}
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 7af3c6b..4eaf13e 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -30,11 +30,13 @@
#include <linux/mempolicy.h>
#include <linux/migrate.h>
#include <linux/task_work.h>
+#include <linux/module.h>
#include "sched.h"
+#include "tune.h"
#include <trace/events/sched.h>
-/* QHMP forward declarations */
+/* QHMP/Zone forward declarations */
struct lb_env;
struct sd_lb_stats;
@@ -102,6 +104,7 @@
static inline void init_cfs_rq_hmp_stats(struct cfs_rq *cfs_rq) { }
#ifdef CONFIG_SMP
+
static inline int
bail_inter_cluster_balance(struct lb_env *env, struct sd_lb_stats *sds)
{
@@ -116,6 +119,7 @@
return false;
}
#endif /* CONFIG_SMP */
+
#endif /* CONFIG_SCHED_HMP */
/*
@@ -133,6 +137,17 @@
unsigned int sysctl_sched_latency = 6000000ULL;
unsigned int normalized_sysctl_sched_latency = 6000000ULL;
+unsigned int sysctl_sched_is_big_little = 0;
+unsigned int sysctl_sched_sync_hint_enable = 1;
+unsigned int sysctl_sched_initial_task_util = 0;
+unsigned int sysctl_sched_cstate_aware = 1;
+
+#ifdef CONFIG_SCHED_WALT
+unsigned int sysctl_sched_use_walt_cpu_util = 1;
+unsigned int sysctl_sched_use_walt_task_util = 1;
+__read_mostly unsigned int sysctl_sched_walt_cpu_high_irqload =
+ (10 * NSEC_PER_MSEC);
+#endif
/*
* The initial- and re-scaling of tunables is configurable
* (default SCHED_TUNABLESCALING_LOG = *(1+ilog(ncpus))
@@ -785,10 +800,13 @@
if (entity_is_task(se))
sa->load_avg = scale_load_down(se->load.weight);
sa->load_sum = sa->load_avg * LOAD_AVG_MAX;
+
/*
* At this point, util_avg won't be used in select_task_rq_fair anyway
*/
- sa->util_avg = 0;
+ sa->util_avg = sched_freq() ?
+ sysctl_sched_initial_task_util :
+ 0;
sa->util_sum = 0;
/* when this task enqueue'ed, it will contribute to its cfs_rq's load_avg */
}
@@ -2921,6 +2939,7 @@
scale_freq = arch_scale_freq_capacity(NULL, cpu);
scale_cpu = arch_scale_cpu_capacity(NULL, cpu);
+ trace_sched_contrib_scale_f(cpu, scale_freq, scale_cpu);
/* delta_w is the amount already accumulated against our next period */
delta_w = sa->period_contrib;
@@ -3188,6 +3207,10 @@
if (update_cfs_rq_load_avg(now, cfs_rq, true) && update_tg)
update_tg_load_avg(cfs_rq, 0);
+
+ if (entity_is_task(se))
+ trace_sched_load_avg_task(task_of(se), &se->avg);
+ trace_sched_load_avg_cpu(cpu, cfs_rq);
}
/**
@@ -4653,6 +4676,30 @@
}
#endif
+#ifdef CONFIG_SMP
+static bool cpu_overutilized(int cpu);
+static unsigned long capacity_orig_of(int cpu);
+static unsigned long cpu_util(int cpu);
+static inline unsigned long boosted_cpu_util(int cpu);
+#else
+#define boosted_cpu_util(cpu) cpu_util(cpu)
+#endif
+
+#ifdef CONFIG_SMP
+static void update_capacity_of(int cpu)
+{
+ unsigned long req_cap;
+
+ if (!sched_freq())
+ return;
+
+ /* Convert scale-invariant capacity to cpu. */
+ req_cap = boosted_cpu_util(cpu);
+ req_cap = req_cap * SCHED_CAPACITY_SCALE / capacity_orig_of(cpu);
+ set_cfs_cpu_capacity(cpu, true, req_cap);
+}
+#endif
+
/*
* The enqueue_task method is called before nr_running is
* increased. Here we update the fair scheduling stats and
@@ -4663,6 +4710,10 @@
{
struct cfs_rq *cfs_rq;
struct sched_entity *se = &p->se;
+#ifdef CONFIG_SMP
+ int task_new = flags & ENQUEUE_WAKEUP_NEW;
+ int task_wakeup = flags & ENQUEUE_WAKEUP;
+#endif
/*
* If in_iowait is set, the code below may not trigger any cpufreq
@@ -4709,6 +4760,30 @@
inc_rq_hmp_stats(rq, p, 1);
}
+#ifdef CONFIG_SMP
+
+ if (!se) {
+ if (!task_new && !rq->rd->overutilized &&
+ cpu_overutilized(rq->cpu)) {
+ rq->rd->overutilized = true;
+ trace_sched_overutilized(true);
+ }
+
+ /*
+ * We want to potentially trigger a freq switch
+ * request only for tasks that are waking up; this is
+ * because we get here also during load balancing, but
+ * in these cases it seems wise to trigger as single
+ * request after load balancing is done.
+ */
+ if (task_new || task_wakeup)
+ update_capacity_of(cpu_of(rq));
+ }
+
+ /* Update SchedTune accouting */
+ schedtune_enqueue_task(p, cpu_of(rq));
+
+#endif /* CONFIG_SMP */
hrtick_update(rq);
}
@@ -4772,6 +4847,30 @@
dec_rq_hmp_stats(rq, p, 1);
}
+#ifdef CONFIG_SMP
+
+ if (!se) {
+ /*
+ * We want to potentially trigger a freq switch
+ * request only for tasks that are going to sleep;
+ * this is because we get here also during load
+ * balancing, but in these cases it seems wise to
+ * trigger as single request after load balancing is
+ * done.
+ */
+ if (task_sleep) {
+ if (rq->cfs.nr_running)
+ update_capacity_of(cpu_of(rq));
+ else if (sched_freq())
+ set_cfs_cpu_capacity(cpu_of(rq), false, 0);
+ }
+ }
+
+ /* Update SchedTune accouting */
+ schedtune_dequeue_task(p, cpu_of(rq));
+
+#endif /* CONFIG_SMP */
+
hrtick_update(rq);
}
@@ -5078,15 +5177,6 @@
return max(rq->cpu_load[type-1], total);
}
-static unsigned long capacity_of(int cpu)
-{
- return cpu_rq(cpu)->cpu_capacity;
-}
-
-static unsigned long capacity_orig_of(int cpu)
-{
- return cpu_rq(cpu)->cpu_capacity_orig;
-}
static unsigned long cpu_avg_load_per_task(int cpu)
{
@@ -5238,6 +5328,397 @@
}
/*
+ * Returns the current capacity of cpu after applying both
+ * cpu and freq scaling.
+ */
+unsigned long capacity_curr_of(int cpu)
+{
+ return cpu_rq(cpu)->cpu_capacity_orig *
+ arch_scale_freq_capacity(NULL, cpu)
+ >> SCHED_CAPACITY_SHIFT;
+}
+
+static inline bool energy_aware(void)
+{
+ return sched_feat(ENERGY_AWARE);
+}
+
+struct energy_env {
+ struct sched_group *sg_top;
+ struct sched_group *sg_cap;
+ int cap_idx;
+ int util_delta;
+ int src_cpu;
+ int dst_cpu;
+ int energy;
+ int payoff;
+ struct task_struct *task;
+ struct {
+ int before;
+ int after;
+ int delta;
+ int diff;
+ } nrg;
+ struct {
+ int before;
+ int after;
+ int delta;
+ } cap;
+};
+
+/*
+ * __cpu_norm_util() returns the cpu util relative to a specific capacity,
+ * i.e. it's busy ratio, in the range [0..SCHED_LOAD_SCALE] which is useful for
+ * energy calculations. Using the scale-invariant util returned by
+ * cpu_util() and approximating scale-invariant util by:
+ *
+ * util ~ (curr_freq/max_freq)*1024 * capacity_orig/1024 * running_time/time
+ *
+ * the normalized util can be found using the specific capacity.
+ *
+ * capacity = capacity_orig * curr_freq/max_freq
+ *
+ * norm_util = running_time/time ~ util/capacity
+ */
+static unsigned long __cpu_norm_util(int cpu, unsigned long capacity, int delta)
+{
+ int util = __cpu_util(cpu, delta);
+
+ if (util >= capacity)
+ return SCHED_CAPACITY_SCALE;
+
+ return (util << SCHED_CAPACITY_SHIFT)/capacity;
+}
+
+static int calc_util_delta(struct energy_env *eenv, int cpu)
+{
+ if (cpu == eenv->src_cpu)
+ return -eenv->util_delta;
+ if (cpu == eenv->dst_cpu)
+ return eenv->util_delta;
+ return 0;
+}
+
+static
+unsigned long group_max_util(struct energy_env *eenv)
+{
+ int i, delta;
+ unsigned long max_util = 0;
+
+ for_each_cpu(i, sched_group_cpus(eenv->sg_cap)) {
+ delta = calc_util_delta(eenv, i);
+ max_util = max(max_util, __cpu_util(i, delta));
+ }
+
+ return max_util;
+}
+
+/*
+ * group_norm_util() returns the approximated group util relative to it's
+ * current capacity (busy ratio) in the range [0..SCHED_LOAD_SCALE] for use in
+ * energy calculations. Since task executions may or may not overlap in time in
+ * the group the true normalized util is between max(cpu_norm_util(i)) and
+ * sum(cpu_norm_util(i)) when iterating over all cpus in the group, i. The
+ * latter is used as the estimate as it leads to a more pessimistic energy
+ * estimate (more busy).
+ */
+static unsigned
+long group_norm_util(struct energy_env *eenv, struct sched_group *sg)
+{
+ int i, delta;
+ unsigned long util_sum = 0;
+ unsigned long capacity = sg->sge->cap_states[eenv->cap_idx].cap;
+
+ for_each_cpu(i, sched_group_cpus(sg)) {
+ delta = calc_util_delta(eenv, i);
+ util_sum += __cpu_norm_util(i, capacity, delta);
+ }
+
+ if (util_sum > SCHED_CAPACITY_SCALE)
+ return SCHED_CAPACITY_SCALE;
+ return util_sum;
+}
+
+static int find_new_capacity(struct energy_env *eenv,
+ const struct sched_group_energy const *sge)
+{
+ int idx;
+ unsigned long util = group_max_util(eenv);
+
+ for (idx = 0; idx < sge->nr_cap_states; idx++) {
+ if (sge->cap_states[idx].cap >= util)
+ break;
+ }
+
+ eenv->cap_idx = idx;
+
+ return idx;
+}
+
+static int group_idle_state(struct sched_group *sg)
+{
+ int i, state = INT_MAX;
+
+ /* Find the shallowest idle state in the sched group. */
+ for_each_cpu(i, sched_group_cpus(sg))
+ state = min(state, idle_get_state_idx(cpu_rq(i)));
+
+ /* Take non-cpuidle idling into account (active idle/arch_cpu_idle()) */
+ state++;
+
+ return state;
+}
+
+/*
+ * sched_group_energy(): Computes the absolute energy consumption of cpus
+ * belonging to the sched_group including shared resources shared only by
+ * members of the group. Iterates over all cpus in the hierarchy below the
+ * sched_group starting from the bottom working it's way up before going to
+ * the next cpu until all cpus are covered at all levels. The current
+ * implementation is likely to gather the same util statistics multiple times.
+ * This can probably be done in a faster but more complex way.
+ * Note: sched_group_energy() may fail when racing with sched_domain updates.
+ */
+static int sched_group_energy(struct energy_env *eenv)
+{
+ struct sched_domain *sd;
+ int cpu, total_energy = 0;
+ struct cpumask visit_cpus;
+ struct sched_group *sg;
+
+ WARN_ON(!eenv->sg_top->sge);
+
+ cpumask_copy(&visit_cpus, sched_group_cpus(eenv->sg_top));
+
+ while (!cpumask_empty(&visit_cpus)) {
+ struct sched_group *sg_shared_cap = NULL;
+
+ cpu = cpumask_first(&visit_cpus);
+
+ /*
+ * Is the group utilization affected by cpus outside this
+ * sched_group?
+ */
+ sd = rcu_dereference(per_cpu(sd_scs, cpu));
+
+ if (!sd)
+ /*
+ * We most probably raced with hotplug; returning a
+ * wrong energy estimation is better than entering an
+ * infinite loop.
+ */
+ return -EINVAL;
+
+ if (sd->parent)
+ sg_shared_cap = sd->parent->groups;
+
+ for_each_domain(cpu, sd) {
+ sg = sd->groups;
+
+ /* Has this sched_domain already been visited? */
+ if (sd->child && group_first_cpu(sg) != cpu)
+ break;
+
+ do {
+ unsigned long group_util;
+ int sg_busy_energy, sg_idle_energy;
+ int cap_idx, idle_idx;
+
+ if (sg_shared_cap && sg_shared_cap->group_weight >= sg->group_weight)
+ eenv->sg_cap = sg_shared_cap;
+ else
+ eenv->sg_cap = sg;
+
+ cap_idx = find_new_capacity(eenv, sg->sge);
+
+ if (sg->group_weight == 1) {
+ /* Remove capacity of src CPU (before task move) */
+ if (eenv->util_delta == 0 &&
+ cpumask_test_cpu(eenv->src_cpu, sched_group_cpus(sg))) {
+ eenv->cap.before = sg->sge->cap_states[cap_idx].cap;
+ eenv->cap.delta -= eenv->cap.before;
+ }
+ /* Add capacity of dst CPU (after task move) */
+ if (eenv->util_delta != 0 &&
+ cpumask_test_cpu(eenv->dst_cpu, sched_group_cpus(sg))) {
+ eenv->cap.after = sg->sge->cap_states[cap_idx].cap;
+ eenv->cap.delta += eenv->cap.after;
+ }
+ }
+
+ idle_idx = group_idle_state(sg);
+ group_util = group_norm_util(eenv, sg);
+ sg_busy_energy = (group_util * sg->sge->cap_states[cap_idx].power)
+ >> SCHED_CAPACITY_SHIFT;
+ sg_idle_energy = ((SCHED_CAPACITY_SCALE-group_util)
+ * sg->sge->idle_states[idle_idx].power)
+ >> SCHED_CAPACITY_SHIFT;
+
+ total_energy += sg_busy_energy + sg_idle_energy;
+
+ if (!sd->child)
+ cpumask_xor(&visit_cpus, &visit_cpus, sched_group_cpus(sg));
+
+ if (cpumask_equal(sched_group_cpus(sg), sched_group_cpus(eenv->sg_top)))
+ goto next_cpu;
+
+ } while (sg = sg->next, sg != sd->groups);
+ }
+next_cpu:
+ cpumask_clear_cpu(cpu, &visit_cpus);
+ continue;
+ }
+
+ eenv->energy = total_energy;
+ return 0;
+}
+
+static inline bool cpu_in_sg(struct sched_group *sg, int cpu)
+{
+ return cpu != -1 && cpumask_test_cpu(cpu, sched_group_cpus(sg));
+}
+
+/*
+ * energy_diff(): Estimate the energy impact of changing the utilization
+ * distribution. eenv specifies the change: utilisation amount, source, and
+ * destination cpu. Source or destination cpu may be -1 in which case the
+ * utilization is removed from or added to the system (e.g. task wake-up). If
+ * both are specified, the utilization is migrated.
+ */
+static inline int __energy_diff(struct energy_env *eenv)
+{
+ struct sched_domain *sd;
+ struct sched_group *sg;
+ int sd_cpu = -1, energy_before = 0, energy_after = 0;
+
+ struct energy_env eenv_before = {
+ .util_delta = 0,
+ .src_cpu = eenv->src_cpu,
+ .dst_cpu = eenv->dst_cpu,
+ .nrg = { 0, 0, 0, 0},
+ .cap = { 0, 0, 0 },
+ };
+
+ if (eenv->src_cpu == eenv->dst_cpu)
+ return 0;
+
+ sd_cpu = (eenv->src_cpu != -1) ? eenv->src_cpu : eenv->dst_cpu;
+ sd = rcu_dereference(per_cpu(sd_ea, sd_cpu));
+
+ if (!sd)
+ return 0; /* Error */
+
+ sg = sd->groups;
+
+ do {
+ if (cpu_in_sg(sg, eenv->src_cpu) || cpu_in_sg(sg, eenv->dst_cpu)) {
+ eenv_before.sg_top = eenv->sg_top = sg;
+
+ if (sched_group_energy(&eenv_before))
+ return 0; /* Invalid result abort */
+ energy_before += eenv_before.energy;
+
+ /* Keep track of SRC cpu (before) capacity */
+ eenv->cap.before = eenv_before.cap.before;
+ eenv->cap.delta = eenv_before.cap.delta;
+
+ if (sched_group_energy(eenv))
+ return 0; /* Invalid result abort */
+ energy_after += eenv->energy;
+ }
+ } while (sg = sg->next, sg != sd->groups);
+
+ eenv->nrg.before = energy_before;
+ eenv->nrg.after = energy_after;
+ eenv->nrg.diff = eenv->nrg.after - eenv->nrg.before;
+ eenv->payoff = 0;
+
+ trace_sched_energy_diff(eenv->task,
+ eenv->src_cpu, eenv->dst_cpu, eenv->util_delta,
+ eenv->nrg.before, eenv->nrg.after, eenv->nrg.diff,
+ eenv->cap.before, eenv->cap.after, eenv->cap.delta,
+ eenv->nrg.delta, eenv->payoff);
+
+ return eenv->nrg.diff;
+}
+
+#ifdef CONFIG_SCHED_TUNE
+
+struct target_nrg schedtune_target_nrg;
+
+/*
+ * System energy normalization
+ * Returns the normalized value, in the range [0..SCHED_LOAD_SCALE],
+ * corresponding to the specified energy variation.
+ */
+static inline int
+normalize_energy(int energy_diff)
+{
+ u32 normalized_nrg;
+#ifdef CONFIG_SCHED_DEBUG
+ int max_delta;
+
+ /* Check for boundaries */
+ max_delta = schedtune_target_nrg.max_power;
+ max_delta -= schedtune_target_nrg.min_power;
+ WARN_ON(abs(energy_diff) >= max_delta);
+#endif
+
+ /* Do scaling using positive numbers to increase the range */
+ normalized_nrg = (energy_diff < 0) ? -energy_diff : energy_diff;
+
+ /* Scale by energy magnitude */
+ normalized_nrg <<= SCHED_CAPACITY_SHIFT;
+
+ /* Normalize on max energy for target platform */
+ normalized_nrg = reciprocal_divide(
+ normalized_nrg, schedtune_target_nrg.rdiv);
+
+ return (energy_diff < 0) ? -normalized_nrg : normalized_nrg;
+}
+
+static inline int
+energy_diff(struct energy_env *eenv)
+{
+ unsigned int boost;
+ int nrg_delta;
+
+ /* Conpute "absolute" energy diff */
+ __energy_diff(eenv);
+
+ /* Return energy diff when boost margin is 0 */
+#ifdef CONFIG_CGROUP_SCHEDTUNE
+ boost = schedtune_task_boost(eenv->task);
+#else
+ boost = get_sysctl_sched_cfs_boost();
+#endif
+ if (boost == 0)
+ return eenv->nrg.diff;
+
+ /* Compute normalized energy diff */
+ nrg_delta = normalize_energy(eenv->nrg.diff);
+ eenv->nrg.delta = nrg_delta;
+
+ eenv->payoff = schedtune_accept_deltas(
+ eenv->nrg.delta,
+ eenv->cap.delta,
+ eenv->task);
+
+ /*
+ * When SchedTune is enabled, the energy_diff() function will return
+ * the computed energy payoff value. Since the energy_diff() return
+ * value is expected to be negative by its callers, this evaluation
+ * function return a negative value each time the evaluation return a
+ * positive payoff, which is the condition for the acceptance of
+ * a scheduling decision
+ */
+ return -eenv->payoff;
+}
+#else /* CONFIG_SCHED_TUNE */
+#define energy_diff(eenv) __energy_diff(eenv)
+#endif
+
+/*
* Detect M:N waker/wakee relationships via a switching-frequency heuristic.
*
* A waker of many should wake a different task than the one last awakened
@@ -5333,6 +5814,169 @@
return 1;
}
+static inline int task_util(struct task_struct *p)
+{
+#ifdef CONFIG_SCHED_WALT
+ if (!walt_disabled && sysctl_sched_use_walt_task_util) {
+ unsigned long demand = p->ravg.demand;
+ return (demand << 10) / walt_ravg_window;
+ }
+#endif
+ return p->se.avg.util_avg;
+}
+
+static inline unsigned long boosted_task_util(struct task_struct *task);
+
+static inline bool __task_fits(struct task_struct *p, int cpu, int util)
+{
+ unsigned long capacity = capacity_of(cpu);
+
+ util += boosted_task_util(p);
+
+ return (capacity * 1024) > (util * capacity_margin);
+}
+
+static inline bool task_fits_max(struct task_struct *p, int cpu)
+{
+ unsigned long capacity = capacity_of(cpu);
+ unsigned long max_capacity = cpu_rq(cpu)->rd->max_cpu_capacity.val;
+
+ if (capacity == max_capacity)
+ return true;
+
+ if (capacity * capacity_margin > max_capacity * 1024)
+ return true;
+
+ return __task_fits(p, cpu, 0);
+}
+
+static inline bool task_fits_spare(struct task_struct *p, int cpu)
+{
+ return __task_fits(p, cpu, cpu_util(cpu));
+}
+
+static bool cpu_overutilized(int cpu)
+{
+ return (capacity_of(cpu) * 1024) < (cpu_util(cpu) * capacity_margin);
+}
+
+#ifdef CONFIG_SCHED_TUNE
+
+static long
+schedtune_margin(unsigned long signal, long boost)
+{
+ long long margin = 0;
+
+ /*
+ * Signal proportional compensation (SPC)
+ *
+ * The Boost (B) value is used to compute a Margin (M) which is
+ * proportional to the complement of the original Signal (S):
+ * M = B * (SCHED_LOAD_SCALE - S), if B is positive
+ * M = B * S, if B is negative
+ * The obtained M could be used by the caller to "boost" S.
+ */
+
+ if (boost >= 0) {
+ margin = SCHED_CAPACITY_SCALE - signal;
+ margin *= boost;
+ } else
+ margin = -signal * boost;
+ /*
+ * Fast integer division by constant:
+ * Constant : (C) = 100
+ * Precision : 0.1% (P) = 0.1
+ * Reference : C * 100 / P (R) = 100000
+ *
+ * Thus:
+ * Shift bits : ceil(log(R,2)) (S) = 17
+ * Mult const : round(2^S/C) (M) = 1311
+ *
+ *
+ */
+ margin *= 1311;
+ margin >>= 17;
+
+ if (boost < 0)
+ margin *= -1;
+ return margin;
+}
+
+static inline int
+schedtune_cpu_margin(unsigned long util, int cpu)
+{
+ int boost;
+
+#ifdef CONFIG_CGROUP_SCHEDTUNE
+ boost = schedtune_cpu_boost(cpu);
+#else
+ boost = get_sysctl_sched_cfs_boost();
+#endif
+ if (boost == 0)
+ return 0;
+
+ return schedtune_margin(util, boost);
+}
+
+static inline long
+schedtune_task_margin(struct task_struct *task)
+{
+ int boost;
+ unsigned long util;
+ long margin;
+
+#ifdef CONFIG_CGROUP_SCHEDTUNE
+ boost = schedtune_task_boost(task);
+#else
+ boost = get_sysctl_sched_cfs_boost();
+#endif
+ if (boost == 0)
+ return 0;
+
+ util = task_util(task);
+ margin = schedtune_margin(util, boost);
+
+ return margin;
+}
+
+#else /* CONFIG_SCHED_TUNE */
+
+static inline int
+schedtune_cpu_margin(unsigned long util, int cpu)
+{
+ return 0;
+}
+
+static inline int
+schedtune_task_margin(struct task_struct *task)
+{
+ return 0;
+}
+
+#endif /* CONFIG_SCHED_TUNE */
+
+static inline unsigned long
+boosted_cpu_util(int cpu)
+{
+ unsigned long util = cpu_util(cpu);
+ long margin = schedtune_cpu_margin(util, cpu);
+
+ trace_sched_boost_cpu(cpu, util, margin);
+
+ return util + margin;
+}
+
+static inline unsigned long
+boosted_task_util(struct task_struct *task)
+{
+ unsigned long util = task_util(task);
+ long margin = schedtune_task_margin(task);
+
+ trace_sched_boost_task(task, util, margin);
+
+ return util + margin;
+}
+
/*
* find_idlest_group finds and returns the least busy CPU group within the
* domain.
@@ -5342,7 +5986,10 @@
int this_cpu, int sd_flag)
{
struct sched_group *idlest = NULL, *group = sd->groups;
+ struct sched_group *fit_group = NULL, *spare_group = NULL;
unsigned long min_load = ULONG_MAX, this_load = 0;
+ unsigned long fit_capacity = ULONG_MAX;
+ unsigned long max_spare_capacity = capacity_margin - SCHED_CAPACITY_SCALE;
int load_idx = sd->forkexec_idx;
int imbalance = 100 + (sd->imbalance_pct-100)/2;
@@ -5350,7 +5997,7 @@
load_idx = sd->wake_idx;
do {
- unsigned long load, avg_load;
+ unsigned long load, avg_load, spare_capacity;
int local_group;
int i;
@@ -5373,6 +6020,25 @@
load = target_load(i, load_idx);
avg_load += load;
+
+ /*
+ * Look for most energy-efficient group that can fit
+ * that can fit the task.
+ */
+ if (capacity_of(i) < fit_capacity && task_fits_spare(p, i)) {
+ fit_capacity = capacity_of(i);
+ fit_group = group;
+ }
+
+ /*
+ * Look for group which has most spare capacity on a
+ * single cpu.
+ */
+ spare_capacity = capacity_of(i) - cpu_util(i);
+ if (spare_capacity > max_spare_capacity) {
+ max_spare_capacity = spare_capacity;
+ spare_group = group;
+ }
}
/* Adjust by relative CPU capacity of the group */
@@ -5386,6 +6052,12 @@
}
} while (group = group->next, group != sd->groups);
+ if (fit_group)
+ return fit_group;
+
+ if (spare_group)
+ return spare_group;
+
if (!idlest || 100*this_load < imbalance*min_load)
return NULL;
return idlest;
@@ -5410,7 +6082,7 @@
/* Traverse only the allowed CPUs */
for_each_cpu_and(i, sched_group_cpus(group), tsk_cpus_allowed(p)) {
- if (idle_cpu(i)) {
+ if (task_fits_spare(p, i)) {
struct rq *rq = cpu_rq(i);
struct cpuidle_state *idle = idle_get_state(rq);
if (idle && idle->exit_latency < min_exit_latency) {
@@ -5422,7 +6094,8 @@
min_exit_latency = idle->exit_latency;
latest_idle_timestamp = rq->idle_stamp;
shallowest_idle_cpu = i;
- } else if ((!idle || idle->exit_latency == min_exit_latency) &&
+ } else if (idle_cpu(i) &&
+ (!idle || idle->exit_latency == min_exit_latency) &&
rq->idle_stamp > latest_idle_timestamp) {
/*
* If equal or no active idle state, then
@@ -5431,6 +6104,13 @@
*/
latest_idle_timestamp = rq->idle_stamp;
shallowest_idle_cpu = i;
+ } else if (shallowest_idle_cpu == -1) {
+ /*
+ * If we haven't found an idle CPU yet
+ * pick a non-idle one that can fit the task as
+ * fallback.
+ */
+ shallowest_idle_cpu = i;
}
} else if (shallowest_idle_cpu == -1) {
load = weighted_cpuload(i);
@@ -5654,94 +6334,305 @@
static int select_idle_sibling(struct task_struct *p, int prev, int target)
{
struct sched_domain *sd;
- int i;
+ struct sched_group *sg;
+ int i = task_cpu(p);
+ int best_idle = -1;
+ int best_idle_cstate = -1;
+ int best_idle_capacity = INT_MAX;
- if (idle_cpu(target))
- return target;
+ if (!sysctl_sched_cstate_aware) {
+ if (idle_cpu(target))
+ return target;
+
+ /*
+ * If the prevous cpu is cache affine and idle, don't be stupid.
+ */
+ if (i != target && cpus_share_cache(i, target) && idle_cpu(i))
+ return i;
+
+ sd = rcu_dereference(per_cpu(sd_llc, target));
+ if (!sd)
+ return target;
+
+ i = select_idle_core(p, sd, target);
+ if ((unsigned)i < nr_cpumask_bits)
+ return i;
+
+ i = select_idle_cpu(p, sd, target);
+ if ((unsigned)i < nr_cpumask_bits)
+ return i;
+
+ i = select_idle_smt(p, sd, target);
+ if ((unsigned)i < nr_cpumask_bits)
+ return i;
+ }
/*
- * If the previous cpu is cache affine and idle, don't be stupid.
+ * Otherwise, iterate the domains and find an elegible idle cpu.
*/
- if (prev != target && cpus_share_cache(prev, target) && idle_cpu(prev))
- return prev;
-
sd = rcu_dereference(per_cpu(sd_llc, target));
- if (!sd)
- return target;
+ for_each_lower_domain(sd) {
+ sg = sd->groups;
+ do {
+ if (!cpumask_intersects(sched_group_cpus(sg),
+ tsk_cpus_allowed(p)))
+ goto next;
- i = select_idle_core(p, sd, target);
- if ((unsigned)i < nr_cpumask_bits)
- return i;
- i = select_idle_cpu(p, sd, target);
- if ((unsigned)i < nr_cpumask_bits)
- return i;
+ if (sysctl_sched_cstate_aware) {
+ for_each_cpu_and(i, tsk_cpus_allowed(p), sched_group_cpus(sg)) {
+ struct rq *rq = cpu_rq(i);
+ int idle_idx = idle_get_state_idx(rq);
+ unsigned long new_usage = boosted_task_util(p);
+ unsigned long capacity_orig = capacity_orig_of(i);
+ if (new_usage > capacity_orig || !idle_cpu(i))
+ goto next;
- i = select_idle_smt(p, sd, target);
- if ((unsigned)i < nr_cpumask_bits)
- return i;
+ if (i == target && new_usage <= capacity_curr_of(target))
+ return target;
+ if (best_idle < 0 || (idle_idx < best_idle_cstate && capacity_orig <= best_idle_capacity)) {
+ best_idle = i;
+ best_idle_cstate = idle_idx;
+ best_idle_capacity = capacity_orig;
+ }
+ }
+ } else {
+ for_each_cpu(i, sched_group_cpus(sg)) {
+ if (i == target || !idle_cpu(i))
+ goto next;
+ }
+
+ target = cpumask_first_and(sched_group_cpus(sg),
+ tsk_cpus_allowed(p));
+ goto done;
+ }
+next:
+ sg = sg->next;
+ } while (sg != sd->groups);
+ }
+ if (best_idle > 0)
+ target = best_idle;
+
+done:
return target;
}
-/*
- * cpu_util returns the amount of capacity of a CPU that is used by CFS
- * tasks. The unit of the return value must be the one of capacity so we can
- * compare the utilization with the capacity of the CPU that is available for
- * CFS task (ie cpu_capacity).
- *
- * cfs_rq.avg.util_avg is the sum of running time of runnable tasks plus the
- * recent utilization of currently non-runnable tasks on a CPU. It represents
- * the amount of utilization of a CPU in the range [0..capacity_orig] where
- * capacity_orig is the cpu_capacity available at the highest frequency
- * (arch_scale_freq_capacity()).
- * The utilization of a CPU converges towards a sum equal to or less than the
- * current capacity (capacity_curr <= capacity_orig) of the CPU because it is
- * the running time on this CPU scaled by capacity_curr.
- *
- * Nevertheless, cfs_rq.avg.util_avg can be higher than capacity_curr or even
- * higher than capacity_orig because of unfortunate rounding in
- * cfs.avg.util_avg or just after migrating tasks and new task wakeups until
- * the average stabilizes with the new running time. We need to check that the
- * utilization stays within the range of [0..capacity_orig] and cap it if
- * necessary. Without utilization capping, a group could be seen as overloaded
- * (CPU0 utilization at 121% + CPU1 utilization at 80%) whereas CPU1 has 20% of
- * available capacity. We allow utilization to overshoot capacity_curr (but not
- * capacity_orig) as it useful for predicting the capacity required after task
- * migrations (scheduler-driven DVFS).
- */
-static int cpu_util(int cpu)
+static inline int find_best_target(struct task_struct *p, bool boosted, bool prefer_idle)
{
- unsigned long util = cpu_rq(cpu)->cfs.avg.util_avg;
- unsigned long capacity = capacity_orig_of(cpu);
+ int iter_cpu;
+ int target_cpu = -1;
+ int target_util = 0;
+ int backup_capacity = 0;
+ int best_idle_cpu = -1;
+ int best_idle_cstate = INT_MAX;
+ int backup_cpu = -1;
+ unsigned long task_util_boosted, new_util;
- return (util >= capacity) ? capacity : util;
+ task_util_boosted = boosted_task_util(p);
+ for (iter_cpu = 0; iter_cpu < NR_CPUS; iter_cpu++) {
+ int cur_capacity;
+ struct rq *rq;
+ int idle_idx;
+
+ /*
+ * Iterate from higher cpus for boosted tasks.
+ */
+ int i = boosted ? NR_CPUS-iter_cpu-1 : iter_cpu;
+
+ if (!cpu_online(i) || !cpumask_test_cpu(i, tsk_cpus_allowed(p)))
+ continue;
+
+ /*
+ * p's blocked utilization is still accounted for on prev_cpu
+ * so prev_cpu will receive a negative bias due to the double
+ * accounting. However, the blocked utilization may be zero.
+ */
+ new_util = cpu_util(i) + task_util_boosted;
+
+ /*
+ * Ensure minimum capacity to grant the required boost.
+ * The target CPU can be already at a capacity level higher
+ * than the one required to boost the task.
+ */
+ if (new_util > capacity_orig_of(i))
+ continue;
+
+#ifdef CONFIG_SCHED_WALT
+ if (walt_cpu_high_irqload(i))
+ continue;
+#endif
+ /*
+ * Unconditionally favoring tasks that prefer idle cpus to
+ * improve latency.
+ */
+ if (idle_cpu(i) && prefer_idle) {
+ if (best_idle_cpu < 0)
+ best_idle_cpu = i;
+ continue;
+ }
+
+ cur_capacity = capacity_curr_of(i);
+ rq = cpu_rq(i);
+ idle_idx = idle_get_state_idx(rq);
+
+ if (new_util < cur_capacity) {
+ if (cpu_rq(i)->nr_running) {
+ if(prefer_idle) {
+ // Find a target cpu with lowest
+ // utilization.
+ if (target_util == 0 ||
+ target_util < new_util) {
+ target_cpu = i;
+ target_util = new_util;
+ }
+ } else {
+ // Find a target cpu with highest
+ // utilization.
+ if (target_util == 0 ||
+ target_util > new_util) {
+ target_cpu = i;
+ target_util = new_util;
+ }
+ }
+ } else if (!prefer_idle) {
+ if (best_idle_cpu < 0 ||
+ (sysctl_sched_cstate_aware &&
+ best_idle_cstate > idle_idx)) {
+ best_idle_cstate = idle_idx;
+ best_idle_cpu = i;
+ }
+ }
+ } else if (backup_capacity == 0 ||
+ backup_capacity > cur_capacity) {
+ // Find a backup cpu with least capacity.
+ backup_capacity = cur_capacity;
+ backup_cpu = i;
+ }
+ }
+
+ if (prefer_idle && best_idle_cpu >= 0)
+ target_cpu = best_idle_cpu;
+ else if (target_cpu < 0)
+ target_cpu = best_idle_cpu >= 0 ? best_idle_cpu : backup_cpu;
+
+ return target_cpu;
}
-static inline int task_util(struct task_struct *p)
+static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
{
- return p->se.avg.util_avg;
-}
+ struct sched_domain *sd;
+ struct sched_group *sg, *sg_target;
+ int target_max_cap = INT_MAX;
+ int target_cpu = task_cpu(p);
+ unsigned long task_util_boosted, new_util;
+ int i;
-/*
- * Disable WAKE_AFFINE in the case where task @p doesn't fit in the
- * capacity of either the waking CPU @cpu or the previous CPU @prev_cpu.
- *
- * In that case WAKE_AFFINE doesn't make sense and we'll let
- * BALANCE_WAKE sort things out.
- */
-static int wake_cap(struct task_struct *p, int cpu, int prev_cpu)
-{
- long min_cap, max_cap;
+ if (sysctl_sched_sync_hint_enable && sync) {
+ int cpu = smp_processor_id();
+ cpumask_t search_cpus;
+ cpumask_and(&search_cpus, tsk_cpus_allowed(p), cpu_online_mask);
+ if (cpumask_test_cpu(cpu, &search_cpus))
+ return cpu;
+ }
- min_cap = min(capacity_orig_of(prev_cpu), capacity_orig_of(cpu));
- max_cap = cpu_rq(cpu)->rd->max_cpu_capacity;
+ sd = rcu_dereference(per_cpu(sd_ea, task_cpu(p)));
- /* Minimum capacity is close to max, no need to abort wake_affine */
- if (max_cap - min_cap < max_cap >> 3)
- return 0;
+ if (!sd)
+ return target;
- return min_cap * 1024 < task_util(p) * capacity_margin;
+ sg = sd->groups;
+ sg_target = sg;
+
+ if (sysctl_sched_is_big_little) {
+
+ /*
+ * Find group with sufficient capacity. We only get here if no cpu is
+ * overutilized. We may end up overutilizing a cpu by adding the task,
+ * but that should not be any worse than select_idle_sibling().
+ * load_balance() should sort it out later as we get above the tipping
+ * point.
+ */
+ do {
+ /* Assuming all cpus are the same in group */
+ int max_cap_cpu = group_first_cpu(sg);
+
+ /*
+ * Assume smaller max capacity means more energy-efficient.
+ * Ideally we should query the energy model for the right
+ * answer but it easily ends up in an exhaustive search.
+ */
+ if (capacity_of(max_cap_cpu) < target_max_cap &&
+ task_fits_max(p, max_cap_cpu)) {
+ sg_target = sg;
+ target_max_cap = capacity_of(max_cap_cpu);
+ }
+ } while (sg = sg->next, sg != sd->groups);
+
+ task_util_boosted = boosted_task_util(p);
+ /* Find cpu with sufficient capacity */
+ for_each_cpu_and(i, tsk_cpus_allowed(p), sched_group_cpus(sg_target)) {
+ /*
+ * p's blocked utilization is still accounted for on prev_cpu
+ * so prev_cpu will receive a negative bias due to the double
+ * accounting. However, the blocked utilization may be zero.
+ */
+ new_util = cpu_util(i) + task_util_boosted;
+
+ /*
+ * Ensure minimum capacity to grant the required boost.
+ * The target CPU can be already at a capacity level higher
+ * than the one required to boost the task.
+ */
+ if (new_util > capacity_orig_of(i))
+ continue;
+
+ if (new_util < capacity_curr_of(i)) {
+ target_cpu = i;
+ if (cpu_rq(i)->nr_running)
+ break;
+ }
+
+ /* cpu has capacity at higher OPP, keep it as fallback */
+ if (target_cpu == task_cpu(p))
+ target_cpu = i;
+ }
+ } else {
+ /*
+ * Find a cpu with sufficient capacity
+ */
+#ifdef CONFIG_CGROUP_SCHEDTUNE
+ bool boosted = schedtune_task_boost(p) > 0;
+ bool prefer_idle = schedtune_prefer_idle(p) > 0;
+#else
+ bool boosted = 0;
+ bool prefer_idle = 0;
+#endif
+ int tmp_target = find_best_target(p, boosted, prefer_idle);
+ if (tmp_target >= 0) {
+ target_cpu = tmp_target;
+ if ((boosted || prefer_idle) && idle_cpu(target_cpu))
+ return target_cpu;
+ }
+ }
+
+ if (target_cpu != task_cpu(p)) {
+ struct energy_env eenv = {
+ .util_delta = task_util(p),
+ .src_cpu = task_cpu(p),
+ .dst_cpu = target_cpu,
+ .task = p,
+ };
+
+ /* Not enough spare capacity on previous cpu */
+ if (cpu_overutilized(task_cpu(p)))
+ return target_cpu;
+
+ if (energy_diff(&eenv) >= 0)
+ return task_cpu(p);
+ }
+
+ return target_cpu;
}
/*
@@ -5771,8 +6662,9 @@
if (sd_flag & SD_BALANCE_WAKE) {
record_wakee(p);
- want_affine = !wake_wide(p) && !wake_cap(p, cpu, prev_cpu)
- && cpumask_test_cpu(cpu, tsk_cpus_allowed(p));
+ want_affine = (!wake_wide(p) && task_fits_max(p, cpu) &&
+ cpumask_test_cpu(cpu, tsk_cpus_allowed(p))) ||
+ energy_aware();
}
rcu_read_lock();
@@ -5803,7 +6695,9 @@
}
if (!sd) {
- if (sd_flag & SD_BALANCE_WAKE) /* XXX always ? */
+ if (energy_aware() && !cpu_rq(cpu)->rd->overutilized)
+ new_cpu = energy_aware_wake_cpu(p, prev_cpu, sync);
+ else if (sd_flag & SD_BALANCE_WAKE) /* XXX always ? */
new_cpu = select_idle_sibling(p, prev_cpu, new_cpu);
} else while (sd) {
@@ -5898,6 +6792,8 @@
{
remove_entity_load_avg(&p->se);
}
+#else
+#define task_fits_max(p, cpu) true
#endif /* CONFIG_SMP */
static unsigned long
@@ -6144,6 +7040,8 @@
if (hrtick_enabled(rq))
hrtick_start_fair(rq, p);
+ rq->misfit_task = !task_fits_max(p, rq->cpu);
+
return p;
simple:
cfs_rq = &rq->cfs;
@@ -6165,9 +7063,12 @@
if (hrtick_enabled(rq))
hrtick_start_fair(rq, p);
+ rq->misfit_task = !task_fits_max(p, rq->cpu);
+
return p;
idle:
+ rq->misfit_task = 0;
/*
* This is OK, because current is on_cpu, which avoids it being picked
* for load-balance and preemption/IRQs are still disabled avoiding
@@ -6380,6 +7281,13 @@
enum fbq_type { regular, remote, all };
+enum group_type {
+ group_other = 0,
+ group_misfit_task,
+ group_imbalanced,
+ group_overloaded,
+};
+
#define LBF_ALL_PINNED 0x01
#define LBF_NEED_BREAK 0x02
#define LBF_DST_PINNED 0x04
@@ -6402,6 +7310,7 @@
int new_dst_cpu;
enum cpu_idle_type idle;
long imbalance;
+ unsigned int src_grp_nr_running;
/* The set of CPUs under consideration for load-balancing */
struct cpumask *cpus;
unsigned int busiest_grp_capacity;
@@ -6797,6 +7706,10 @@
{
raw_spin_lock(&rq->lock);
attach_task(rq, p);
+ /*
+ * We want to potentially raise target_cpu's OPP.
+ */
+ update_capacity_of(cpu_of(rq));
raw_spin_unlock(&rq->lock);
}
@@ -6818,6 +7731,11 @@
attach_task(env->dst_rq, p);
}
+ /*
+ * We want to potentially raise env.dst_cpu's OPP.
+ */
+ update_capacity_of(env->dst_cpu);
+
raw_spin_unlock(&env->dst_rq->lock);
}
@@ -6913,12 +7831,6 @@
/********** Helpers for find_busiest_group ************************/
-enum group_type {
- group_other = 0,
- group_imbalanced,
- group_overloaded,
-};
-
/*
* sg_lb_stats - stats of a sched_group required for load_balancing
*/
@@ -6938,6 +7850,7 @@
unsigned int group_weight;
enum group_type group_type;
int group_no_capacity;
+ int group_misfit_task; /* A cpu has a task too big for its capacity */
#ifdef CONFIG_NUMA_BALANCING
unsigned int nr_numa_running;
unsigned int nr_preferred_running;
@@ -7039,13 +7952,43 @@
return 1;
}
+void init_max_cpu_capacity(struct max_cpu_capacity *mcc)
+{
+ raw_spin_lock_init(&mcc->lock);
+ mcc->val = 0;
+ mcc->cpu = -1;
+}
+
static void update_cpu_capacity(struct sched_domain *sd, int cpu)
{
unsigned long capacity = arch_scale_cpu_capacity(sd, cpu);
struct sched_group *sdg = sd->groups;
+ struct max_cpu_capacity *mcc;
+ unsigned long max_capacity;
+ int max_cap_cpu;
+ unsigned long flags;
cpu_rq(cpu)->cpu_capacity_orig = capacity;
+ mcc = &cpu_rq(cpu)->rd->max_cpu_capacity;
+
+ raw_spin_lock_irqsave(&mcc->lock, flags);
+ max_capacity = mcc->val;
+ max_cap_cpu = mcc->cpu;
+
+ if ((max_capacity > capacity && max_cap_cpu == cpu) ||
+ (max_capacity < capacity)) {
+ mcc->val = capacity;
+ mcc->cpu = cpu;
+#ifdef CONFIG_SCHED_DEBUG
+ raw_spin_unlock_irqrestore(&mcc->lock, flags);
+ pr_info("CPU%d: update max cpu_capacity %lu\n", cpu, capacity);
+ goto skip_unlock;
+#endif
+ }
+ raw_spin_unlock_irqrestore(&mcc->lock, flags);
+
+skip_unlock: __attribute__ ((unused));
capacity *= scale_rt_capacity(cpu);
capacity >>= SCHED_CAPACITY_SHIFT;
@@ -7054,13 +7997,14 @@
cpu_rq(cpu)->cpu_capacity = capacity;
sdg->sgc->capacity = capacity;
+ sdg->sgc->max_capacity = capacity;
}
void update_group_capacity(struct sched_domain *sd, int cpu)
{
struct sched_domain *child = sd->child;
struct sched_group *group, *sdg = sd->groups;
- unsigned long capacity;
+ unsigned long capacity, max_capacity;
unsigned long interval;
interval = msecs_to_jiffies(sd->balance_interval);
@@ -7073,6 +8017,7 @@
}
capacity = 0;
+ max_capacity = 0;
if (child->flags & SD_OVERLAP) {
/*
@@ -7099,11 +8044,12 @@
*/
if (unlikely(!rq->sd)) {
capacity += capacity_of(cpu);
- continue;
+ } else {
+ sgc = rq->sd->groups->sgc;
+ capacity += sgc->capacity;
}
- sgc = rq->sd->groups->sgc;
- capacity += sgc->capacity;
+ max_capacity = max(capacity, max_capacity);
}
} else {
/*
@@ -7113,16 +8059,20 @@
group = child->groups;
do {
+ struct sched_group_capacity *sgc = group->sgc;
cpumask_t *cpus = sched_group_cpus(group);
/* Revisit this later. This won't work for MT domain */
- if (!cpu_isolated(cpumask_first(cpus)))
- capacity += group->sgc->capacity;
+ if (!cpu_isolated(cpumask_first(cpus))) {
+ capacity += sgc->capacity;
+ max_capacity = max(sgc->max_capacity, max_capacity);
+ }
group = group->next;
} while (group != child->groups);
}
sdg->sgc->capacity = capacity;
+ sdg->sgc->max_capacity = max_capacity;
}
/*
@@ -7227,6 +8177,9 @@
if (sg_imbalanced(group))
return group_imbalanced;
+ if (sgs->group_misfit_task)
+ return group_misfit_task;
+
return group_other;
}
@@ -7238,11 +8191,12 @@
* @local_group: Does group contain this_cpu.
* @sgs: variable to hold the statistics for this group.
* @overload: Indicate more than one runnable task for any CPU.
+ * @overutilized: Indicate overutilization for any CPU.
*/
static inline void update_sg_lb_stats(struct lb_env *env,
struct sched_group *group, int load_idx,
int local_group, struct sg_lb_stats *sgs,
- bool *overload)
+ bool *overload, bool *overutilized)
{
unsigned long load;
int i, nr_running;
@@ -7289,6 +8243,12 @@
*/
if (!nr_running && idle_cpu(i))
sgs->idle_cpus++;
+
+ if (cpu_overutilized(i)) {
+ *overutilized = true;
+ if (!sgs->group_misfit_task && rq->misfit_task)
+ sgs->group_misfit_task = capacity_of(i);
+ }
}
/* Isolated CPU has no weight */
@@ -7411,7 +8371,7 @@
struct sched_group *sg = env->sd->groups;
struct sg_lb_stats tmp_sgs;
int load_idx, prefer_sibling = 0;
- bool overload = false;
+ bool overload = false, overutilized = false;
if (child && child->flags & SD_PREFER_SIBLING)
prefer_sibling = 1;
@@ -7433,7 +8393,7 @@
}
update_sg_lb_stats(env, sg, load_idx, local_group, sgs,
- &overload);
+ &overload, &overutilized);
if (local_group)
goto next_group;
@@ -7473,10 +8433,23 @@
if (env->sd->flags & SD_NUMA)
env->fbq_type = fbq_classify_group(&sds->busiest_stat);
+ env->src_grp_nr_running = sds->busiest_stat.sum_nr_running;
+
if (!env->sd->parent) {
/* update overload indicator if we are at root domain */
if (env->dst_rq->rd->overload != overload)
env->dst_rq->rd->overload = overload;
+
+ /* Update over-utilization (tipping point, U >= 0) indicator */
+ if (env->dst_rq->rd->overutilized != overutilized) {
+ env->dst_rq->rd->overutilized = overutilized;
+ trace_sched_overutilized(overutilized);
+ }
+ } else {
+ if (!env->dst_rq->rd->overutilized && overutilized) {
+ env->dst_rq->rd->overutilized = true;
+ trace_sched_overutilized(true);
+ }
}
}
@@ -7697,6 +8670,10 @@
* this level.
*/
update_sd_lb_stats(env, &sds);
+
+ if (energy_aware() && !env->dst_rq->rd->overutilized)
+ goto out_balanced;
+
local = &sds.local_stat;
busiest = &sds.busiest_stat;
@@ -7890,6 +8867,13 @@
return 1;
}
+ if (energy_aware() && (capacity_of(env->src_cpu) < capacity_of(env->dst_cpu)) &&
+ env->src_rq->cfs.h_nr_running == 1 &&
+ cpu_overutilized(env->src_cpu) &&
+ !cpu_overutilized(env->dst_cpu)) {
+ return 1;
+ }
+
return unlikely(sd->nr_balance_failed >
sd->cache_nice_tries + NEED_ACTIVE_BALANCE_THRESHOLD);
}
@@ -8035,6 +9019,11 @@
* ld_moved - cumulative load moved across iterations
*/
cur_ld_moved = detach_tasks(&env);
+ /*
+ * We want to potentially lower env.src_cpu's OPP.
+ */
+ if (cur_ld_moved)
+ update_capacity_of(env.src_cpu);
/*
* We've detached some tasks from busiest_rq. Every
@@ -8128,8 +9117,10 @@
* excessive cache_hot migrations and active balances.
*/
if (idle != CPU_NEWLY_IDLE &&
- !(env.flags & LBF_BIG_TASK_ACTIVE_BALANCE))
- sd->nr_balance_failed++;
+ !(env.flags & LBF_BIG_TASK_ACTIVE_BALANCE)) {
+ if (env.src_grp_nr_running > 1)
+ sd->nr_balance_failed++;
+ }
if (need_active_balance(&env)) {
raw_spin_lock_irqsave(&busiest->lock, flags);
@@ -8279,6 +9270,7 @@
struct sched_domain *sd;
int pulled_task = 0;
u64 curr_cost = 0;
+ long removed_util=0;
if (cpu_isolated(this_cpu))
return 0;
@@ -8289,8 +9281,9 @@
*/
this_rq->idle_stamp = rq_clock(this_rq);
- if (this_rq->avg_idle < sysctl_sched_migration_cost ||
- !this_rq->rd->overload) {
+ if (!energy_aware() &&
+ (this_rq->avg_idle < sysctl_sched_migration_cost ||
+ !this_rq->rd->overload)) {
rcu_read_lock();
sd = rcu_dereference_check_sched_domain(this_rq->sd);
if (sd)
@@ -8302,6 +9295,17 @@
raw_spin_unlock(&this_rq->lock);
+ /*
+ * If removed_util_avg is !0 we most probably migrated some task away
+ * from this_cpu. In this case we might be willing to trigger an OPP
+ * update, but we want to do so if we don't find anybody else to pull
+ * here (we will trigger an OPP update with the pulled task's enqueue
+ * anyway).
+ *
+ * Record removed_util before calling update_blocked_averages, and use
+ * it below (before returning) to see if an OPP update is required.
+ */
+ removed_util = atomic_long_read(&(this_rq->cfs).removed_util_avg);
update_blocked_averages(this_cpu);
rcu_read_lock();
for_each_domain(this_cpu, sd) {
@@ -8368,6 +9372,13 @@
if (pulled_task)
this_rq->idle_stamp = 0;
+ else if (removed_util) {
+ /*
+ * No task pulled and someone has been migrated away.
+ * Good case to trigger an OPP update.
+ */
+ update_capacity_of(this_cpu);
+ }
return pulled_task;
}
@@ -8450,6 +9461,10 @@
p = detach_one_task(&env);
if (p) {
schedstat_inc(sd->alb_pushed);
+ /*
+ * We want to potentially lower env.src_cpu's OPP.
+ */
+ update_capacity_of(env.src_cpu);
/* Active balancing done, reset the failure counter. */
sd->nr_balance_failed = 0;
moved = true;
@@ -8822,27 +9837,6 @@
clear_bit(NOHZ_BALANCE_KICK, nohz_flags(this_cpu));
}
-static inline int _nohz_kick_needed(struct rq *rq, int cpu, int *type)
-{
- unsigned long now = jiffies;
-
- /*
- * None are in tickless mode and hence no need for NOHZ idle load
- * balancing.
- */
- if (likely(!atomic_read(&nohz.nr_cpus)))
- return 0;
-
-#ifdef CONFIG_SCHED_HMP
- return _nohz_kick_needed_hmp(rq, cpu, type);
-#endif
-
- if (time_before(now, nohz.next_balance))
- return 0;
-
- return (rq->nr_running >= 2);
-}
-
/*
* Current heuristic for kicking the idle load balancer in the presence
* of an idle cpu in the system.
@@ -8856,6 +9850,7 @@
*/
static inline bool nohz_kick_needed(struct rq *rq, int *type)
{
+ unsigned long now = jiffies;
#ifndef CONFIG_SCHED_HMP
struct sched_domain_shared *sds;
struct sched_domain *sd;
@@ -8874,13 +9869,28 @@
set_cpu_sd_state_busy();
nohz_balance_exit_idle(cpu);
- if (_nohz_kick_needed(rq, cpu, type))
+ /*
+ * None are in tickless mode and hence no need for NOHZ idle load
+ * balancing.
+ */
+ if (likely(!atomic_read(&nohz.nr_cpus)))
+ return false;
+
+#ifdef CONFIG_SCHED_HMP
+ return _nohz_kick_needed_hmp(rq, cpu, type);
+#endif
+
+ if (time_before(now, nohz.next_balance))
+ return false;
+
+ if (rq->nr_running >= 2 &&
+ (!energy_aware() || cpu_overutilized(cpu)))
return true;
#ifndef CONFIG_SCHED_HMP
rcu_read_lock();
sds = rcu_dereference(per_cpu(sd_llc_shared, cpu));
- if (sds) {
+ if (sds && !energy_aware()) {
/*
* XXX: write a coherent comment on why we do this.
* See also: http://lkml.kernel.org/r/20111202010832.602203411@sbsiddha-desk.sc.intel.com
@@ -8993,6 +10003,16 @@
if (static_branch_unlikely(&sched_numa_balancing))
task_tick_numa(rq, curr);
+
+#ifdef CONFIG_SMP
+ if (!rq->rd->overutilized && cpu_overutilized(task_cpu(curr))) {
+ rq->rd->overutilized = true;
+ trace_sched_overutilized(true);
+ }
+
+ rq->misfit_task = !task_fits_max(curr, rq->cpu);
+#endif
+
}
/*
@@ -9502,7 +10522,7 @@
}
-/* QHMP sched implementation begins here */
+/* QHMP/Zone sched implementation begins here */
#ifdef CONFIG_SCHED_HMP
#ifdef CONFIG_SMP
diff --git a/kernel/sched/features.h b/kernel/sched/features.h
index 6651a37..c30c48f 100644
--- a/kernel/sched/features.h
+++ b/kernel/sched/features.h
@@ -69,3 +69,12 @@
SCHED_FEAT(LB_MIN, false)
SCHED_FEAT(ATTACH_AGE_LOAD, true)
+/*
+ * Energy aware scheduling. Use platform energy model to guide scheduling
+ * decisions optimizing for energy efficiency.
+ */
+#ifdef CONFIG_DEFAULT_USE_ENERGY_AWARE
+SCHED_FEAT(ENERGY_AWARE, true)
+#else
+SCHED_FEAT(ENERGY_AWARE, false)
+#endif
diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
index 1d8718d..cf75f00 100644
--- a/kernel/sched/idle.c
+++ b/kernel/sched/idle.c
@@ -23,9 +23,10 @@
* sched_idle_set_state - Record idle state for the current CPU.
* @idle_state: State to record.
*/
-void sched_idle_set_state(struct cpuidle_state *idle_state)
+void sched_idle_set_state(struct cpuidle_state *idle_state, int index)
{
idle_set_state(this_rq(), idle_state);
+ idle_set_state_idx(this_rq(), index);
}
static int __read_mostly cpu_idle_force_poll;
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 67ab014..bcac711 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -61,6 +61,8 @@
#endif /* CONFIG_SCHED_HMP */
+#include "walt.h"
+
int sched_rr_timeslice = RR_TIMESLICE;
static int do_sched_rt_period_timer(struct rt_bandwidth *rt_b, int overrun);
@@ -1022,7 +1024,7 @@
* but accrue some time due to boosting.
*/
if (likely(rt_b->rt_runtime)) {
- static bool once;
+ static bool once = false;
rt_rq->rt_throttled = 1;
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index eed0639..41622ca 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -25,17 +25,16 @@
struct rq;
struct cpuidle_state;
+#ifdef CONFIG_SCHED_HMP
+#define NUM_TRACKED_WINDOWS 2
+#define NUM_LOAD_INDICES 1000
+
struct hmp_sched_stats {
int nr_big_tasks;
u64 cumulative_runnable_avg;
u64 pred_demands_sum;
};
-
-#ifdef CONFIG_SCHED_HMP
-#define NUM_TRACKED_WINDOWS 2
-#define NUM_LOAD_INDICES 1000
-
struct load_subtractions {
u64 window_start;
u64 subs;
@@ -80,7 +79,7 @@
u64 time;
};
-#endif
+#endif /* CONFIG_SCHED_HMP */
/* task_struct::on_rq states: */
@@ -502,8 +501,14 @@
struct list_head leaf_cfs_rq_list;
struct task_group *tg; /* group that "owns" this runqueue */
+#ifdef CONFIG_SCHED_WALT
+ u64 cumulative_runnable_avg;
+#endif
+
#ifdef CONFIG_CFS_BANDWIDTH
+#ifdef CONFIG_SCHED_HMP
struct hmp_sched_stats hmp_stats;
+#endif
int runtime_enabled;
u64 runtime_expires;
@@ -605,6 +610,12 @@
#ifdef CONFIG_SMP
+struct max_cpu_capacity {
+ raw_spinlock_t lock;
+ unsigned long val;
+ int cpu;
+};
+
/*
* We add the notion of a root-domain which will be used to define per-domain
* variables. Each exclusive cpuset essentially defines an island domain by
@@ -623,6 +634,9 @@
/* Indicate more than one runnable task for any CPU */
bool overload;
+ /* Indicate one or more cpus over-utilized (tipping point) */
+ bool overutilized;
+
/*
* The bit corresponding to a CPU gets set here if such CPU has more
* than one runnable -deadline task (as it is below for RT tasks).
@@ -639,7 +653,8 @@
cpumask_var_t rto_mask;
struct cpupri cpupri;
- unsigned long max_cpu_capacity;
+ /* Maximum cpu capacity in the system. */
+ struct max_cpu_capacity max_cpu_capacity;
};
extern struct root_domain def_root_domain;
@@ -668,6 +683,7 @@
#endif
#define CPU_LOAD_IDX_MAX 5
unsigned long cpu_load[CPU_LOAD_IDX_MAX];
+ unsigned int misfit_task;
#ifdef CONFIG_NO_HZ_COMMON
#ifdef CONFIG_SMP
unsigned long last_load_update_tick;
@@ -677,6 +693,14 @@
#ifdef CONFIG_NO_HZ_FULL
unsigned long last_sched_tick;
#endif
+
+#ifdef CONFIG_CPU_QUIET
+ /* time-based average load */
+ u64 nr_last_stamp;
+ u64 nr_running_integral;
+ seqcount_t ave_seqcnt;
+#endif
+
/* capture load from *all* tasks on this cpu: */
struct load_weight load;
unsigned long nr_load_updates;
@@ -739,11 +763,10 @@
u64 max_idle_balance_cost;
#endif
- struct hmp_sched_stats hmp_stats;
-
#ifdef CONFIG_SCHED_HMP
struct sched_cluster *cluster;
struct cpumask freq_domain_cpumask;
+ struct hmp_sched_stats hmp_stats;
int cstate, wakeup_latency, wakeup_energy;
u64 window_start;
@@ -818,6 +841,7 @@
#ifdef CONFIG_CPU_IDLE
/* Must be inspected within a rcu lock section */
struct cpuidle_state *idle_state;
+ int idle_state_idx;
#endif
};
@@ -984,6 +1008,8 @@
DECLARE_PER_CPU(struct sched_domain_shared *, sd_llc_shared);
DECLARE_PER_CPU(struct sched_domain *, sd_numa);
DECLARE_PER_CPU(struct sched_domain *, sd_asym);
+DECLARE_PER_CPU(struct sched_domain *, sd_ea);
+DECLARE_PER_CPU(struct sched_domain *, sd_scs);
struct sched_group_capacity {
atomic_t ref;
@@ -991,7 +1017,8 @@
* CPU capacity of this group, SCHED_CAPACITY_SCALE being max capacity
* for a single CPU.
*/
- unsigned int capacity;
+ unsigned long capacity;
+ unsigned long max_capacity; /* Max per-cpu capacity in group */
unsigned long next_update;
int imbalance; /* XXX unrelated to capacity but shared group state */
@@ -1004,6 +1031,7 @@
unsigned int group_weight;
struct sched_group_capacity *sgc;
+ const struct sched_group_energy const *sge;
/*
* The CPUs this group covers.
@@ -1325,6 +1353,7 @@
#else
#define ENQUEUE_MIGRATED 0x00
#endif
+#define ENQUEUE_WAKEUP_NEW 0x40
#define RETRY_TASK ((void *)-1UL)
@@ -1419,6 +1448,7 @@
#ifdef CONFIG_SMP
+extern void init_max_cpu_capacity(struct max_cpu_capacity *mcc);
extern void update_group_capacity(struct sched_domain *sd, int cpu);
extern void trigger_load_balance(struct rq *rq);
@@ -1440,6 +1470,17 @@
SCHED_WARN_ON(!rcu_read_lock_held());
return rq->idle_state;
}
+
+static inline void idle_set_state_idx(struct rq *rq, int idle_state_idx)
+{
+ rq->idle_state_idx = idle_state_idx;
+}
+
+static inline int idle_get_state_idx(struct rq *rq)
+{
+ WARN_ON(!rcu_read_lock_held());
+ return rq->idle_state_idx;
+}
#else
static inline void idle_set_state(struct rq *rq,
struct cpuidle_state *idle_state)
@@ -1450,6 +1491,15 @@
{
return NULL;
}
+
+static inline void idle_set_state_idx(struct rq *rq, int idle_state_idx)
+{
+}
+
+static inline int idle_get_state_idx(struct rq *rq)
+{
+ return -1;
+}
#endif
extern void sysrq_sched_debug_show(void);
@@ -1504,7 +1554,7 @@
static inline void sched_update_tick_dependency(struct rq *rq) { }
#endif
-static inline void add_nr_running(struct rq *rq, unsigned count)
+static inline void __add_nr_running(struct rq *rq, unsigned count)
{
unsigned prev_nr = rq->nr_running;
@@ -1521,7 +1571,7 @@
sched_update_tick_dependency(rq);
}
-static inline void sub_nr_running(struct rq *rq, unsigned count)
+static inline void __sub_nr_running(struct rq *rq, unsigned count)
{
sched_update_nr_prod(cpu_of(rq), count, false);
rq->nr_running -= count;
@@ -1529,6 +1579,43 @@
sched_update_tick_dependency(rq);
}
+#ifdef CONFIG_CPU_QUIET
+#define NR_AVE_SCALE(x) ((x) << FSHIFT)
+static inline u64 do_nr_running_integral(struct rq *rq)
+{
+ s64 nr, deltax;
+ u64 nr_running_integral = rq->nr_running_integral;
+
+ deltax = rq->clock_task - rq->nr_last_stamp;
+ nr = NR_AVE_SCALE(rq->nr_running);
+
+ nr_running_integral += nr * deltax;
+
+ return nr_running_integral;
+}
+
+static inline void add_nr_running(struct rq *rq, unsigned count)
+{
+ write_seqcount_begin(&rq->ave_seqcnt);
+ rq->nr_running_integral = do_nr_running_integral(rq);
+ rq->nr_last_stamp = rq->clock_task;
+ __add_nr_running(rq, count);
+ write_seqcount_end(&rq->ave_seqcnt);
+}
+
+static inline void sub_nr_running(struct rq *rq, unsigned count)
+{
+ write_seqcount_begin(&rq->ave_seqcnt);
+ rq->nr_running_integral = do_nr_running_integral(rq);
+ rq->nr_last_stamp = rq->clock_task;
+ __sub_nr_running(rq, count);
+ write_seqcount_end(&rq->ave_seqcnt);
+}
+#else
+#define add_nr_running __add_nr_running
+#define sub_nr_running __sub_nr_running
+#endif
+
static inline void rq_last_tick_reset(struct rq *rq)
{
#ifdef CONFIG_NO_HZ_FULL
@@ -1601,10 +1688,146 @@
}
#endif
+#ifdef CONFIG_SMP
+static inline unsigned long capacity_of(int cpu)
+{
+ return cpu_rq(cpu)->cpu_capacity;
+}
+
+static inline unsigned long capacity_orig_of(int cpu)
+{
+ return cpu_rq(cpu)->cpu_capacity_orig;
+}
+
+extern unsigned int sysctl_sched_use_walt_cpu_util;
+extern unsigned int walt_ravg_window;
+extern unsigned int walt_disabled;
+
+/*
+ * cpu_util returns the amount of capacity of a CPU that is used by CFS
+ * tasks. The unit of the return value must be the one of capacity so we can
+ * compare the utilization with the capacity of the CPU that is available for
+ * CFS task (ie cpu_capacity).
+ *
+ * cfs_rq.avg.util_avg is the sum of running time of runnable tasks plus the
+ * recent utilization of currently non-runnable tasks on a CPU. It represents
+ * the amount of utilization of a CPU in the range [0..capacity_orig] where
+ * capacity_orig is the cpu_capacity available at the highest frequency
+ * (arch_scale_freq_capacity()).
+ * The utilization of a CPU converges towards a sum equal to or less than the
+ * current capacity (capacity_curr <= capacity_orig) of the CPU because it is
+ * the running time on this CPU scaled by capacity_curr.
+ *
+ * Nevertheless, cfs_rq.avg.util_avg can be higher than capacity_curr or even
+ * higher than capacity_orig because of unfortunate rounding in
+ * cfs.avg.util_avg or just after migrating tasks and new task wakeups until
+ * the average stabilizes with the new running time. We need to check that the
+ * utilization stays within the range of [0..capacity_orig] and cap it if
+ * necessary. Without utilization capping, a group could be seen as overloaded
+ * (CPU0 utilization at 121% + CPU1 utilization at 80%) whereas CPU1 has 20% of
+ * available capacity. We allow utilization to overshoot capacity_curr (but not
+ * capacity_orig) as it useful for predicting the capacity required after task
+ * migrations (scheduler-driven DVFS).
+ */
+static inline unsigned long __cpu_util(int cpu, int delta)
+{
+ unsigned long util = cpu_rq(cpu)->cfs.avg.util_avg;
+ unsigned long capacity = capacity_orig_of(cpu);
+
+#ifdef CONFIG_SCHED_WALT
+ if (!walt_disabled && sysctl_sched_use_walt_cpu_util) {
+ util = cpu_rq(cpu)->prev_runnable_sum << SCHED_CAPACITY_SHIFT;
+ do_div(util, walt_ravg_window);
+ }
+#endif
+ delta += util;
+ if (delta < 0)
+ return 0;
+
+ return (delta >= capacity) ? capacity : delta;
+}
+
+static inline unsigned long cpu_util(int cpu)
+{
+ return __cpu_util(cpu, 0);
+}
+
+#endif
+
+#ifdef CONFIG_CPU_FREQ_GOV_SCHED
+#define capacity_max SCHED_CAPACITY_SCALE
+extern unsigned int capacity_margin;
+extern struct static_key __sched_freq;
+
+static inline bool sched_freq(void)
+{
+ return static_key_false(&__sched_freq);
+}
+
+DECLARE_PER_CPU(struct sched_capacity_reqs, cpu_sched_capacity_reqs);
+void update_cpu_capacity_request(int cpu, bool request);
+
+static inline void set_cfs_cpu_capacity(int cpu, bool request,
+ unsigned long capacity)
+{
+ struct sched_capacity_reqs *scr = &per_cpu(cpu_sched_capacity_reqs, cpu);
+
+#ifdef CONFIG_SCHED_WALT
+ if (!walt_disabled && sysctl_sched_use_walt_cpu_util) {
+ int rtdl = scr->rt + scr->dl;
+ /*
+ * WALT tracks the utilization of a CPU considering the load
+ * generated by all the scheduling classes.
+ * Since the following call to:
+ * update_cpu_capacity
+ * is already adding the RT and DL utilizations let's remove
+ * these contributions from the WALT signal.
+ */
+ if (capacity > rtdl)
+ capacity -= rtdl;
+ else
+ capacity = 0;
+ }
+#endif
+ if (scr->cfs != capacity) {
+ scr->cfs = capacity;
+ update_cpu_capacity_request(cpu, request);
+ }
+}
+
+static inline void set_rt_cpu_capacity(int cpu, bool request,
+ unsigned long capacity)
+{
+ if (per_cpu(cpu_sched_capacity_reqs, cpu).rt != capacity) {
+ per_cpu(cpu_sched_capacity_reqs, cpu).rt = capacity;
+ update_cpu_capacity_request(cpu, request);
+ }
+}
+
+static inline void set_dl_cpu_capacity(int cpu, bool request,
+ unsigned long capacity)
+{
+ if (per_cpu(cpu_sched_capacity_reqs, cpu).dl != capacity) {
+ per_cpu(cpu_sched_capacity_reqs, cpu).dl = capacity;
+ update_cpu_capacity_request(cpu, request);
+ }
+}
+#else
+static inline bool sched_freq(void) { return false; }
+static inline void set_cfs_cpu_capacity(int cpu, bool request,
+ unsigned long capacity)
+{ }
+static inline void set_rt_cpu_capacity(int cpu, bool request,
+ unsigned long capacity)
+{ }
+static inline void set_dl_cpu_capacity(int cpu, bool request,
+ unsigned long capacity)
+{ }
+#endif
+
static inline void sched_rt_avg_update(struct rq *rq, u64 rt_delta)
{
rq->rt_avg += rt_delta * arch_scale_freq_capacity(NULL, cpu_of(rq));
- sched_avg_update(rq);
}
#else
static inline void sched_rt_avg_update(struct rq *rq, u64 rt_delta) { }
@@ -1639,6 +1862,9 @@
raw_spin_unlock_irqrestore(&p->pi_lock, rf->flags);
}
+extern struct rq *lock_rq_of(struct task_struct *p, struct rq_flags *flags);
+extern void unlock_rq_of(struct rq *rq, struct task_struct *p, struct rq_flags *flags);
+
#ifdef CONFIG_SMP
#ifdef CONFIG_PREEMPT
@@ -1711,7 +1937,8 @@
static inline void double_unlock_balance(struct rq *this_rq, struct rq *busiest)
__releases(busiest->lock)
{
- raw_spin_unlock(&busiest->lock);
+ if (this_rq != busiest)
+ raw_spin_unlock(&busiest->lock);
lock_set_subclass(&this_rq->lock.dep_map, 0, _RET_IP_);
}
diff --git a/kernel/sched/stop_task.c b/kernel/sched/stop_task.c
index 427bc8f..a440769 100644
--- a/kernel/sched/stop_task.c
+++ b/kernel/sched/stop_task.c
@@ -1,4 +1,5 @@
#include "sched.h"
+#include "walt.h"
/*
* stop-task scheduling class.
diff --git a/kernel/sched/tune.c b/kernel/sched/tune.c
index 5a09d5a..5e5811c 100644
--- a/kernel/sched/tune.c
+++ b/kernel/sched/tune.c
@@ -3,30 +3,109 @@
#include <linux/percpu.h>
#include <linux/printk.h>
#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/rcupdate.h>
+#include <linux/slab.h>
+#include <trace/events/sched.h>
#include "sched.h"
+#include "tune.h"
unsigned int sysctl_sched_cfs_boost __read_mostly;
#ifdef CONFIG_CGROUP_SCHEDTUNE
+static bool schedtune_initialized = false;
+#endif /* CONFIG_CGROUP_SCHEDTUNE */
-#ifdef CONFIG_SCHED_HMP
-struct schedtune;
-static inline void init_sched_boost(struct schedtune *st);
-static void schedtune_attach(struct cgroup_taskset *tset);
-static u64 sched_boost_override_read(struct cgroup_subsys_state *css,
- struct cftype *cft);
-static int sched_boost_override_write(struct cgroup_subsys_state *css,
- struct cftype *cft, u64 override);
-static u64 sched_boost_enabled_read(struct cgroup_subsys_state *css,
- struct cftype *cft);
-static int sched_boost_enabled_write(struct cgroup_subsys_state *css,
- struct cftype *cft, u64 enable);
-static u64 sched_colocate_read(struct cgroup_subsys_state *css,
- struct cftype *cft);
-static int sched_colocate_write(struct cgroup_subsys_state *css,
- struct cftype *cft, u64 colocate);
-#endif /* CONFIG_SCHED_HMP */
+unsigned int sysctl_sched_cfs_boost __read_mostly;
+
+extern struct target_nrg schedtune_target_nrg;
+
+/* Performance Boost region (B) threshold params */
+static int perf_boost_idx;
+
+/* Performance Constraint region (C) threshold params */
+static int perf_constrain_idx;
+
+/**
+ * Performance-Energy (P-E) Space thresholds constants
+ */
+struct threshold_params {
+ int nrg_gain;
+ int cap_gain;
+};
+
+/*
+ * System specific P-E space thresholds constants
+ */
+static struct threshold_params
+threshold_gains[] = {
+ { 0, 5 }, /* < 10% */
+ { 1, 5 }, /* < 20% */
+ { 2, 5 }, /* < 30% */
+ { 3, 5 }, /* < 40% */
+ { 4, 5 }, /* < 50% */
+ { 5, 4 }, /* < 60% */
+ { 5, 3 }, /* < 70% */
+ { 5, 2 }, /* < 80% */
+ { 5, 1 }, /* < 90% */
+ { 5, 0 } /* <= 100% */
+};
+
+static int
+__schedtune_accept_deltas(int nrg_delta, int cap_delta,
+ int perf_boost_idx, int perf_constrain_idx)
+{
+ int payoff = -INT_MAX;
+ int gain_idx = -1;
+
+ /* Performance Boost (B) region */
+ if (nrg_delta >= 0 && cap_delta > 0)
+ gain_idx = perf_boost_idx;
+ /* Performance Constraint (C) region */
+ else if (nrg_delta < 0 && cap_delta <= 0)
+ gain_idx = perf_constrain_idx;
+
+ /* Default: reject schedule candidate */
+ if (gain_idx == -1)
+ return payoff;
+
+ /*
+ * Evaluate "Performance Boost" vs "Energy Increase"
+ *
+ * - Performance Boost (B) region
+ *
+ * Condition: nrg_delta > 0 && cap_delta > 0
+ * Payoff criteria:
+ * cap_gain / nrg_gain < cap_delta / nrg_delta =
+ * cap_gain * nrg_delta < cap_delta * nrg_gain
+ * Note that since both nrg_gain and nrg_delta are positive, the
+ * inequality does not change. Thus:
+ *
+ * payoff = (cap_delta * nrg_gain) - (cap_gain * nrg_delta)
+ *
+ * - Performance Constraint (C) region
+ *
+ * Condition: nrg_delta < 0 && cap_delta < 0
+ * payoff criteria:
+ * cap_gain / nrg_gain > cap_delta / nrg_delta =
+ * cap_gain * nrg_delta < cap_delta * nrg_gain
+ * Note that since nrg_gain > 0 while nrg_delta < 0, the
+ * inequality change. Thus:
+ *
+ * payoff = (cap_delta * nrg_gain) - (cap_gain * nrg_delta)
+ *
+ * This means that, in case of same positive defined {cap,nrg}_gain
+ * for both the B and C regions, we can use the same payoff formula
+ * where a positive value represents the accept condition.
+ */
+ payoff = cap_delta * threshold_gains[gain_idx].nrg_gain;
+ payoff -= nrg_delta * threshold_gains[gain_idx].cap_gain;
+
+ return payoff;
+}
+
+#ifdef CONFIG_CGROUP_SCHEDTUNE
/*
* EAS scheduler tunables for task groups.
@@ -68,8 +147,17 @@
/* Controls whether further updates are allowed to the colocate flag */
bool colocate_update_disabled;
-#endif
+#endif /* CONFIG_SCHED_HMP */
+ /* Performance Boost (B) region threshold params */
+ int perf_boost_idx;
+
+ /* Performance Constraint (C) region threshold params */
+ int perf_constrain_idx;
+
+ /* Hint to bias scheduling of tasks on that SchedTune CGroup
+ * towards idle CPUs */
+ int prefer_idle;
};
static inline struct schedtune *css_st(struct cgroup_subsys_state *css)
@@ -106,8 +194,42 @@
.colocate = false,
.colocate_update_disabled = false,
#endif
+ .perf_boost_idx = 0,
+ .perf_constrain_idx = 0,
+ .prefer_idle = 0,
};
+int
+schedtune_accept_deltas(int nrg_delta, int cap_delta,
+ struct task_struct *task)
+{
+ struct schedtune *ct;
+ int perf_boost_idx;
+ int perf_constrain_idx;
+
+ /* Optimal (O) region */
+ if (nrg_delta < 0 && cap_delta > 0) {
+ trace_sched_tune_filter(nrg_delta, cap_delta, 0, 0, 1, 0);
+ return INT_MAX;
+ }
+
+ /* Suboptimal (S) region */
+ if (nrg_delta > 0 && cap_delta < 0) {
+ trace_sched_tune_filter(nrg_delta, cap_delta, 0, 0, -1, 5);
+ return -INT_MAX;
+ }
+
+ /* Get task specific perf Boost/Constraints indexes */
+ rcu_read_lock();
+ ct = task_schedtune(task);
+ perf_boost_idx = ct->perf_boost_idx;
+ perf_constrain_idx = ct->perf_constrain_idx;
+ rcu_read_unlock();
+
+ return __schedtune_accept_deltas(nrg_delta, cap_delta,
+ perf_boost_idx, perf_constrain_idx);
+}
+
/*
* Maximum number of boost groups to support
* When per-task boosting is used we still allow only limited number of
@@ -136,198 +258,22 @@
* maximum per-CPU boosting value.
*/
struct boost_groups {
+ bool idle;
/* Maximum boost value for all RUNNABLE tasks on a CPU */
- unsigned boost_max;
+ int boost_max;
struct {
/* The boost for tasks on that boost group */
- unsigned boost;
+ int boost;
/* Count of RUNNABLE tasks on that boost group */
unsigned tasks;
} group[BOOSTGROUPS_COUNT];
+ /* CPU's boost group locking */
+ raw_spinlock_t lock;
};
/* Boost groups affecting each CPU in the system */
DEFINE_PER_CPU(struct boost_groups, cpu_boost_groups);
-static u64
-boost_read(struct cgroup_subsys_state *css, struct cftype *cft)
-{
- struct schedtune *st = css_st(css);
-
- return st->boost;
-}
-
-static int
-boost_write(struct cgroup_subsys_state *css, struct cftype *cft,
- u64 boost)
-{
- struct schedtune *st = css_st(css);
-
- if (boost < 0 || boost > 100)
- return -EINVAL;
-
- st->boost = boost;
- if (css == &root_schedtune.css)
- sysctl_sched_cfs_boost = boost;
-
- return 0;
-}
-
-static struct cftype files[] = {
- {
- .name = "boost",
- .read_u64 = boost_read,
- .write_u64 = boost_write,
- },
-#ifdef CONFIG_SCHED_HMP
- {
- .name = "sched_boost_no_override",
- .read_u64 = sched_boost_override_read,
- .write_u64 = sched_boost_override_write,
- },
- {
- .name = "sched_boost_enabled",
- .read_u64 = sched_boost_enabled_read,
- .write_u64 = sched_boost_enabled_write,
- },
- {
- .name = "colocate",
- .read_u64 = sched_colocate_read,
- .write_u64 = sched_colocate_write,
- },
-#endif
- { } /* terminate */
-};
-
-static int
-schedtune_boostgroup_init(struct schedtune *st)
-{
- /* Keep track of allocated boost groups */
- allocated_group[st->idx] = st;
-
- return 0;
-}
-
-static int
-schedtune_init(void)
-{
- struct boost_groups *bg;
- int cpu;
-
- /* Initialize the per CPU boost groups */
- for_each_possible_cpu(cpu) {
- bg = &per_cpu(cpu_boost_groups, cpu);
- memset(bg, 0, sizeof(struct boost_groups));
- }
-
- pr_info(" schedtune configured to support %d boost groups\n",
- BOOSTGROUPS_COUNT);
- return 0;
-}
-
-static struct cgroup_subsys_state *
-schedtune_css_alloc(struct cgroup_subsys_state *parent_css)
-{
- struct schedtune *st;
- int idx;
-
- if (!parent_css) {
- schedtune_init();
- return &root_schedtune.css;
- }
-
- /* Allow only single level hierachies */
- if (parent_css != &root_schedtune.css) {
- pr_err("Nested SchedTune boosting groups not allowed\n");
- return ERR_PTR(-ENOMEM);
- }
-
- /* Allow only a limited number of boosting groups */
- for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx)
- if (!allocated_group[idx])
- break;
- if (idx == BOOSTGROUPS_COUNT) {
- pr_err("Trying to create more than %d SchedTune boosting groups\n",
- BOOSTGROUPS_COUNT);
- return ERR_PTR(-ENOSPC);
- }
-
- st = kzalloc(sizeof(*st), GFP_KERNEL);
- if (!st)
- goto out;
-
- /* Initialize per CPUs boost group support */
- st->idx = idx;
- init_sched_boost(st);
- if (schedtune_boostgroup_init(st))
- goto release;
-
- return &st->css;
-
-release:
- kfree(st);
-out:
- return ERR_PTR(-ENOMEM);
-}
-
-static void
-schedtune_boostgroup_release(struct schedtune *st)
-{
- /* Keep track of allocated boost groups */
- allocated_group[st->idx] = NULL;
-}
-
-static void
-schedtune_css_free(struct cgroup_subsys_state *css)
-{
- struct schedtune *st = css_st(css);
-
- schedtune_boostgroup_release(st);
- kfree(st);
-}
-
-struct cgroup_subsys schedtune_cgrp_subsys = {
- .css_alloc = schedtune_css_alloc,
- .css_free = schedtune_css_free,
- .legacy_cftypes = files,
- .early_init = 1,
- .allow_attach = subsys_cgroup_allow_attach,
- .attach = schedtune_attach,
-};
-
-#endif /* CONFIG_CGROUP_SCHEDTUNE */
-
-int
-sysctl_sched_cfs_boost_handler(struct ctl_table *table, int write,
- void __user *buffer, size_t *lenp,
- loff_t *ppos)
-{
- int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
-
- if (ret || !write)
- return ret;
-
- return 0;
-}
-
-/* QHMP/Zone implementation s*/
-
-static void schedtune_attach(struct cgroup_taskset *tset)
-{
- struct task_struct *task;
- struct cgroup_subsys_state *css;
- struct schedtune *st;
- bool colocate;
-
- cgroup_taskset_first(tset, &css);
- st = css_st(css);
-
- colocate = st->colocate;
-
- cgroup_taskset_for_each(task, css, tset)
- sync_cgroup_colocation(task, colocate);
-}
-
#ifdef CONFIG_SCHED_HMP
static inline void init_sched_boost(struct schedtune *st)
{
@@ -397,6 +343,212 @@
return 0;
}
+#endif /* CONFIG_SCHED_HMP */
+
+static void
+schedtune_cpu_update(int cpu)
+{
+ struct boost_groups *bg;
+ int boost_max;
+ int idx;
+
+ bg = &per_cpu(cpu_boost_groups, cpu);
+
+ /* The root boost group is always active */
+ boost_max = bg->group[0].boost;
+ for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx) {
+ /*
+ * A boost group affects a CPU only if it has
+ * RUNNABLE tasks on that CPU
+ */
+ if (bg->group[idx].tasks == 0)
+ continue;
+
+ boost_max = max(boost_max, bg->group[idx].boost);
+ }
+ /* Ensures boost_max is non-negative when all cgroup boost values
+ * are neagtive. Avoids under-accounting of cpu capacity which may cause
+ * task stacking and frequency spikes.*/
+ boost_max = max(boost_max, 0);
+ bg->boost_max = boost_max;
+}
+
+static int
+schedtune_boostgroup_update(int idx, int boost)
+{
+ struct boost_groups *bg;
+ int cur_boost_max;
+ int old_boost;
+ int cpu;
+
+ /* Update per CPU boost groups */
+ for_each_possible_cpu(cpu) {
+ bg = &per_cpu(cpu_boost_groups, cpu);
+
+ /*
+ * Keep track of current boost values to compute the per CPU
+ * maximum only when it has been affected by the new value of
+ * the updated boost group
+ */
+ cur_boost_max = bg->boost_max;
+ old_boost = bg->group[idx].boost;
+
+ /* Update the boost value of this boost group */
+ bg->group[idx].boost = boost;
+
+ /* Check if this update increase current max */
+ if (boost > cur_boost_max && bg->group[idx].tasks) {
+ bg->boost_max = boost;
+ trace_sched_tune_boostgroup_update(cpu, 1, bg->boost_max);
+ continue;
+ }
+
+ /* Check if this update has decreased current max */
+ if (cur_boost_max == old_boost && old_boost > boost) {
+ schedtune_cpu_update(cpu);
+ trace_sched_tune_boostgroup_update(cpu, -1, bg->boost_max);
+ continue;
+ }
+
+ trace_sched_tune_boostgroup_update(cpu, 0, bg->boost_max);
+ }
+
+ return 0;
+}
+
+#define ENQUEUE_TASK 1
+#define DEQUEUE_TASK -1
+
+static inline void
+schedtune_tasks_update(struct task_struct *p, int cpu, int idx, int task_count)
+{
+ struct boost_groups *bg = &per_cpu(cpu_boost_groups, cpu);
+ int tasks = bg->group[idx].tasks + task_count;
+
+ /* Update boosted tasks count while avoiding to make it negative */
+ bg->group[idx].tasks = max(0, tasks);
+
+ trace_sched_tune_tasks_update(p, cpu, tasks, idx,
+ bg->group[idx].boost, bg->boost_max);
+
+ /* Boost group activation or deactivation on that RQ */
+ if (tasks == 1 || tasks == 0)
+ schedtune_cpu_update(cpu);
+}
+
+/*
+ * NOTE: This function must be called while holding the lock on the CPU RQ
+ */
+void schedtune_enqueue_task(struct task_struct *p, int cpu)
+{
+ struct boost_groups *bg = &per_cpu(cpu_boost_groups, cpu);
+ unsigned long irq_flags;
+ struct schedtune *st;
+ int idx;
+
+ if (!unlikely(schedtune_initialized))
+ return;
+
+ /*
+ * When a task is marked PF_EXITING by do_exit() it's going to be
+ * dequeued and enqueued multiple times in the exit path.
+ * Thus we avoid any further update, since we do not want to change
+ * CPU boosting while the task is exiting.
+ */
+ if (p->flags & PF_EXITING)
+ return;
+
+ /*
+ * Boost group accouting is protected by a per-cpu lock and requires
+ * interrupt to be disabled to avoid race conditions for example on
+ * do_exit()::cgroup_exit() and task migration.
+ */
+ raw_spin_lock_irqsave(&bg->lock, irq_flags);
+ rcu_read_lock();
+
+ st = task_schedtune(p);
+ idx = st->idx;
+
+ schedtune_tasks_update(p, cpu, idx, ENQUEUE_TASK);
+
+ rcu_read_unlock();
+ raw_spin_unlock_irqrestore(&bg->lock, irq_flags);
+}
+
+int schedtune_can_attach(struct cgroup_taskset *tset)
+{
+ struct task_struct *task;
+ struct cgroup_subsys_state *css;
+ struct boost_groups *bg;
+ struct rq_flags irq_flags;
+ unsigned int cpu;
+ struct rq *rq;
+ int src_bg; /* Source boost group index */
+ int dst_bg; /* Destination boost group index */
+ int tasks;
+
+ if (!unlikely(schedtune_initialized))
+ return 0;
+
+
+ cgroup_taskset_for_each(task, css, tset) {
+
+ /*
+ * Lock the CPU's RQ the task is enqueued to avoid race
+ * conditions with migration code while the task is being
+ * accounted
+ */
+ rq = lock_rq_of(task, &irq_flags);
+
+ if (!task->on_rq) {
+ unlock_rq_of(rq, task, &irq_flags);
+ continue;
+ }
+
+ /*
+ * Boost group accouting is protected by a per-cpu lock and requires
+ * interrupt to be disabled to avoid race conditions on...
+ */
+ cpu = cpu_of(rq);
+ bg = &per_cpu(cpu_boost_groups, cpu);
+ raw_spin_lock(&bg->lock);
+
+ dst_bg = css_st(css)->idx;
+ src_bg = task_schedtune(task)->idx;
+
+ /*
+ * Current task is not changing boostgroup, which can
+ * happen when the new hierarchy is in use.
+ */
+ if (unlikely(dst_bg == src_bg)) {
+ raw_spin_unlock(&bg->lock);
+ unlock_rq_of(rq, task, &irq_flags);
+ continue;
+ }
+
+ /*
+ * This is the case of a RUNNABLE task which is switching its
+ * current boost group.
+ */
+
+ /* Move task from src to dst boost group */
+ tasks = bg->group[src_bg].tasks - 1;
+ bg->group[src_bg].tasks = max(0, tasks);
+ bg->group[dst_bg].tasks += 1;
+
+ raw_spin_unlock(&bg->lock);
+ unlock_rq_of(rq, task, &irq_flags);
+
+ /* Update CPU boost group */
+ if (bg->group[src_bg].tasks == 0 || bg->group[dst_bg].tasks == 1)
+ schedtune_cpu_update(task_cpu(task));
+
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_SCHED_HMP
static u64 sched_boost_enabled_read(struct cgroup_subsys_state *css,
struct cftype *cft)
{
@@ -442,3 +594,546 @@
static inline void init_sched_boost(struct schedtune *st) { }
#endif /* CONFIG_SCHED_HMP */
+
+void schedtune_cancel_attach(struct cgroup_taskset *tset)
+{
+ /* This can happen only if SchedTune controller is mounted with
+ * other hierarchies ane one of them fails. Since usually SchedTune is
+ * mouted on its own hierarcy, for the time being we do not implement
+ * a proper rollback mechanism */
+ WARN(1, "SchedTune cancel attach not implemented");
+}
+
+/*
+ * NOTE: This function must be called while holding the lock on the CPU RQ
+ */
+void schedtune_dequeue_task(struct task_struct *p, int cpu)
+{
+ struct boost_groups *bg = &per_cpu(cpu_boost_groups, cpu);
+ unsigned long irq_flags;
+ struct schedtune *st;
+ int idx;
+
+ if (!unlikely(schedtune_initialized))
+ return;
+
+ /*
+ * When a task is marked PF_EXITING by do_exit() it's going to be
+ * dequeued and enqueued multiple times in the exit path.
+ * Thus we avoid any further update, since we do not want to change
+ * CPU boosting while the task is exiting.
+ * The last dequeue is already enforce by the do_exit() code path
+ * via schedtune_exit_task().
+ */
+ if (p->flags & PF_EXITING)
+ return;
+
+ /*
+ * Boost group accouting is protected by a per-cpu lock and requires
+ * interrupt to be disabled to avoid race conditions on...
+ */
+ raw_spin_lock_irqsave(&bg->lock, irq_flags);
+ rcu_read_lock();
+
+ st = task_schedtune(p);
+ idx = st->idx;
+
+ schedtune_tasks_update(p, cpu, idx, DEQUEUE_TASK);
+
+ rcu_read_unlock();
+ raw_spin_unlock_irqrestore(&bg->lock, irq_flags);
+}
+
+void schedtune_exit_task(struct task_struct *tsk)
+{
+ struct schedtune *st;
+ struct rq_flags irq_flags;
+ unsigned int cpu;
+ struct rq *rq;
+ int idx;
+
+ if (!unlikely(schedtune_initialized))
+ return;
+
+ rq = lock_rq_of(tsk, &irq_flags);
+ rcu_read_lock();
+
+ cpu = cpu_of(rq);
+ st = task_schedtune(tsk);
+ idx = st->idx;
+ schedtune_tasks_update(tsk, cpu, idx, DEQUEUE_TASK);
+
+ rcu_read_unlock();
+ unlock_rq_of(rq, tsk, &irq_flags);
+}
+
+int schedtune_cpu_boost(int cpu)
+{
+ struct boost_groups *bg;
+
+ bg = &per_cpu(cpu_boost_groups, cpu);
+ return bg->boost_max;
+}
+
+int schedtune_task_boost(struct task_struct *p)
+{
+ struct schedtune *st;
+ int task_boost;
+
+ /* Get task boost value */
+ rcu_read_lock();
+ st = task_schedtune(p);
+ task_boost = st->boost;
+ rcu_read_unlock();
+
+ return task_boost;
+}
+
+int schedtune_prefer_idle(struct task_struct *p)
+{
+ struct schedtune *st;
+ int prefer_idle;
+
+ /* Get prefer_idle value */
+ rcu_read_lock();
+ st = task_schedtune(p);
+ prefer_idle = st->prefer_idle;
+ rcu_read_unlock();
+
+ return prefer_idle;
+}
+
+static u64
+prefer_idle_read(struct cgroup_subsys_state *css, struct cftype *cft)
+{
+ struct schedtune *st = css_st(css);
+
+ return st->prefer_idle;
+}
+
+static int
+prefer_idle_write(struct cgroup_subsys_state *css, struct cftype *cft,
+ u64 prefer_idle)
+{
+ struct schedtune *st = css_st(css);
+ st->prefer_idle = prefer_idle;
+
+ return 0;
+}
+
+static s64
+boost_read(struct cgroup_subsys_state *css, struct cftype *cft)
+{
+ struct schedtune *st = css_st(css);
+
+ return st->boost;
+}
+
+#ifdef CONFIG_SCHED_HMP
+static void schedtune_attach(struct cgroup_taskset *tset)
+{
+ struct task_struct *task;
+ struct cgroup_subsys_state *css;
+ struct schedtune *st;
+ bool colocate;
+
+ cgroup_taskset_first(tset, &css);
+ st = css_st(css);
+
+ colocate = st->colocate;
+
+ cgroup_taskset_for_each(task, css, tset)
+ sync_cgroup_colocation(task, colocate);
+
+}
+#endif
+
+static int
+boost_write(struct cgroup_subsys_state *css, struct cftype *cft,
+ s64 boost)
+{
+ struct schedtune *st = css_st(css);
+ unsigned threshold_idx;
+ int boost_pct;
+
+ if (boost < -100 || boost > 100)
+ return -EINVAL;
+ boost_pct = boost;
+
+ /*
+ * Update threshold params for Performance Boost (B)
+ * and Performance Constraint (C) regions.
+ * The current implementatio uses the same cuts for both
+ * B and C regions.
+ */
+ threshold_idx = clamp(boost_pct, 0, 99) / 10;
+ st->perf_boost_idx = threshold_idx;
+ st->perf_constrain_idx = threshold_idx;
+
+ st->boost = boost;
+ if (css == &root_schedtune.css) {
+ sysctl_sched_cfs_boost = boost;
+ perf_boost_idx = threshold_idx;
+ perf_constrain_idx = threshold_idx;
+ }
+
+ /* Update CPU boost */
+ schedtune_boostgroup_update(st->idx, st->boost);
+
+ trace_sched_tune_config(st->boost);
+
+ return 0;
+}
+
+static struct cftype files[] = {
+#ifdef CONFIG_SCHED_HMP
+ {
+ .name = "sched_boost_no_override",
+ .read_u64 = sched_boost_override_read,
+ .write_u64 = sched_boost_override_write,
+ },
+ {
+ .name = "sched_boost_enabled",
+ .read_u64 = sched_boost_enabled_read,
+ .write_u64 = sched_boost_enabled_write,
+ },
+ {
+ .name = "colocate",
+ .read_u64 = sched_colocate_read,
+ .write_u64 = sched_colocate_write,
+ },
+#endif
+ {
+ .name = "boost",
+ .read_s64 = boost_read,
+ .write_s64 = boost_write,
+ },
+ {
+ .name = "prefer_idle",
+ .read_u64 = prefer_idle_read,
+ .write_u64 = prefer_idle_write,
+ },
+ { } /* terminate */
+};
+
+
+static int
+schedtune_boostgroup_init(struct schedtune *st)
+{
+ struct boost_groups *bg;
+ int cpu;
+
+ /* Keep track of allocated boost groups */
+ allocated_group[st->idx] = st;
+
+ /* Initialize the per CPU boost groups */
+ for_each_possible_cpu(cpu) {
+ bg = &per_cpu(cpu_boost_groups, cpu);
+ bg->group[st->idx].boost = 0;
+ bg->group[st->idx].tasks = 0;
+ }
+
+ return 0;
+}
+
+static struct cgroup_subsys_state *
+schedtune_css_alloc(struct cgroup_subsys_state *parent_css)
+{
+ struct schedtune *st;
+ int idx;
+
+ if (!parent_css)
+ return &root_schedtune.css;
+
+ /* Allow only single level hierachies */
+ if (parent_css != &root_schedtune.css) {
+ pr_err("Nested SchedTune boosting groups not allowed\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* Allow only a limited number of boosting groups */
+ for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx)
+ if (!allocated_group[idx])
+ break;
+ if (idx == BOOSTGROUPS_COUNT) {
+ pr_err("Trying to create more than %d SchedTune boosting groups\n",
+ BOOSTGROUPS_COUNT);
+ return ERR_PTR(-ENOSPC);
+ }
+
+ st = kzalloc(sizeof(*st), GFP_KERNEL);
+ if (!st)
+ goto out;
+
+ /* Initialize per CPUs boost group support */
+ st->idx = idx;
+ init_sched_boost(st);
+ if (schedtune_boostgroup_init(st))
+ goto release;
+
+ return &st->css;
+
+release:
+ kfree(st);
+out:
+ return ERR_PTR(-ENOMEM);
+}
+
+static void
+schedtune_boostgroup_release(struct schedtune *st)
+{
+ /* Reset this boost group */
+ schedtune_boostgroup_update(st->idx, 0);
+
+ /* Keep track of allocated boost groups */
+ allocated_group[st->idx] = NULL;
+}
+
+static void
+schedtune_css_free(struct cgroup_subsys_state *css)
+{
+ struct schedtune *st = css_st(css);
+
+ schedtune_boostgroup_release(st);
+ kfree(st);
+}
+
+struct cgroup_subsys schedtune_cgrp_subsys = {
+ .css_alloc = schedtune_css_alloc,
+ .css_free = schedtune_css_free,
+ .allow_attach = subsys_cgroup_allow_attach,
+ .attach = schedtune_attach,
+ .can_attach = schedtune_can_attach,
+ .cancel_attach = schedtune_cancel_attach,
+ .legacy_cftypes = files,
+ .early_init = 1,
+};
+
+static inline void
+schedtune_init_cgroups(void)
+{
+ struct boost_groups *bg;
+ int cpu;
+
+ /* Initialize the per CPU boost groups */
+ for_each_possible_cpu(cpu) {
+ bg = &per_cpu(cpu_boost_groups, cpu);
+ memset(bg, 0, sizeof(struct boost_groups));
+ raw_spin_lock_init(&bg->lock);
+ }
+
+ pr_info("schedtune: configured to support %d boost groups\n",
+ BOOSTGROUPS_COUNT);
+
+ schedtune_initialized = true;
+}
+
+#else /* CONFIG_CGROUP_SCHEDTUNE */
+
+int
+schedtune_accept_deltas(int nrg_delta, int cap_delta,
+ struct task_struct *task)
+{
+ /* Optimal (O) region */
+ if (nrg_delta < 0 && cap_delta > 0) {
+ trace_sched_tune_filter(nrg_delta, cap_delta, 0, 0, 1, 0);
+ return INT_MAX;
+ }
+
+ /* Suboptimal (S) region */
+ if (nrg_delta > 0 && cap_delta < 0) {
+ trace_sched_tune_filter(nrg_delta, cap_delta, 0, 0, -1, 5);
+ return -INT_MAX;
+ }
+
+ return __schedtune_accept_deltas(nrg_delta, cap_delta,
+ perf_boost_idx, perf_constrain_idx);
+}
+
+#endif /* CONFIG_CGROUP_SCHEDTUNE */
+
+int
+sysctl_sched_cfs_boost_handler(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+ unsigned threshold_idx;
+ int boost_pct;
+
+ if (ret || !write)
+ return ret;
+
+ if (sysctl_sched_cfs_boost < -100 || sysctl_sched_cfs_boost > 100)
+ return -EINVAL;
+ boost_pct = sysctl_sched_cfs_boost;
+
+ /*
+ * Update threshold params for Performance Boost (B)
+ * and Performance Constraint (C) regions.
+ * The current implementatio uses the same cuts for both
+ * B and C regions.
+ */
+ threshold_idx = clamp(boost_pct, 0, 99) / 10;
+ perf_boost_idx = threshold_idx;
+ perf_constrain_idx = threshold_idx;
+
+ return 0;
+}
+
+#ifdef CONFIG_SCHED_DEBUG
+static void
+schedtune_test_nrg(unsigned long delta_pwr)
+{
+ unsigned long test_delta_pwr;
+ unsigned long test_norm_pwr;
+ int idx;
+
+ /*
+ * Check normalization constants using some constant system
+ * energy values
+ */
+ pr_info("schedtune: verify normalization constants...\n");
+ for (idx = 0; idx < 6; ++idx) {
+ test_delta_pwr = delta_pwr >> idx;
+
+ /* Normalize on max energy for target platform */
+ test_norm_pwr = reciprocal_divide(
+ test_delta_pwr << SCHED_CAPACITY_SHIFT,
+ schedtune_target_nrg.rdiv);
+
+ pr_info("schedtune: max_pwr/2^%d: %4lu => norm_pwr: %5lu\n",
+ idx, test_delta_pwr, test_norm_pwr);
+ }
+}
+#else
+#define schedtune_test_nrg(delta_pwr)
+#endif
+
+/*
+ * Compute the min/max power consumption of a cluster and all its CPUs
+ */
+static void
+schedtune_add_cluster_nrg(
+ struct sched_domain *sd,
+ struct sched_group *sg,
+ struct target_nrg *ste)
+{
+ struct sched_domain *sd2;
+ struct sched_group *sg2;
+
+ struct cpumask *cluster_cpus;
+ char str[32];
+
+ unsigned long min_pwr;
+ unsigned long max_pwr;
+ int cpu;
+
+ /* Get Cluster energy using EM data for the first CPU */
+ cluster_cpus = sched_group_cpus(sg);
+ snprintf(str, 32, "CLUSTER[%*pbl]",
+ cpumask_pr_args(cluster_cpus));
+
+ min_pwr = sg->sge->idle_states[sg->sge->nr_idle_states - 1].power;
+ max_pwr = sg->sge->cap_states[sg->sge->nr_cap_states - 1].power;
+ pr_info("schedtune: %-17s min_pwr: %5lu max_pwr: %5lu\n",
+ str, min_pwr, max_pwr);
+
+ /*
+ * Keep track of this cluster's energy in the computation of the
+ * overall system energy
+ */
+ ste->min_power += min_pwr;
+ ste->max_power += max_pwr;
+
+ /* Get CPU energy using EM data for each CPU in the group */
+ for_each_cpu(cpu, cluster_cpus) {
+ /* Get a SD view for the specific CPU */
+ for_each_domain(cpu, sd2) {
+ /* Get the CPU group */
+ sg2 = sd2->groups;
+ min_pwr = sg2->sge->idle_states[sg2->sge->nr_idle_states - 1].power;
+ max_pwr = sg2->sge->cap_states[sg2->sge->nr_cap_states - 1].power;
+
+ ste->min_power += min_pwr;
+ ste->max_power += max_pwr;
+
+ snprintf(str, 32, "CPU[%d]", cpu);
+ pr_info("schedtune: %-17s min_pwr: %5lu max_pwr: %5lu\n",
+ str, min_pwr, max_pwr);
+
+ /*
+ * Assume we have EM data only at the CPU and
+ * the upper CLUSTER level
+ */
+ BUG_ON(!cpumask_equal(
+ sched_group_cpus(sg),
+ sched_group_cpus(sd2->parent->groups)
+ ));
+ break;
+ }
+ }
+}
+
+/*
+ * Initialize the constants required to compute normalized energy.
+ * The values of these constants depends on the EM data for the specific
+ * target system and topology.
+ * Thus, this function is expected to be called by the code
+ * that bind the EM to the topology information.
+ */
+static int
+schedtune_init(void)
+{
+ struct target_nrg *ste = &schedtune_target_nrg;
+ unsigned long delta_pwr = 0;
+ struct sched_domain *sd;
+ struct sched_group *sg;
+
+ pr_info("schedtune: init normalization constants...\n");
+ ste->max_power = 0;
+ ste->min_power = 0;
+
+ rcu_read_lock();
+
+ /*
+ * When EAS is in use, we always have a pointer to the highest SD
+ * which provides EM data.
+ */
+ sd = rcu_dereference(per_cpu(sd_ea, cpumask_first(cpu_online_mask)));
+ if (!sd) {
+ pr_info("schedtune: no energy model data\n");
+ goto nodata;
+ }
+
+ sg = sd->groups;
+ do {
+ schedtune_add_cluster_nrg(sd, sg, ste);
+ } while (sg = sg->next, sg != sd->groups);
+
+ rcu_read_unlock();
+
+ pr_info("schedtune: %-17s min_pwr: %5lu max_pwr: %5lu\n",
+ "SYSTEM", ste->min_power, ste->max_power);
+
+ /* Compute normalization constants */
+ delta_pwr = ste->max_power - ste->min_power;
+ ste->rdiv = reciprocal_value(delta_pwr);
+ pr_info("schedtune: using normalization constants mul: %u sh1: %u sh2: %u\n",
+ ste->rdiv.m, ste->rdiv.sh1, ste->rdiv.sh2);
+
+ schedtune_test_nrg(delta_pwr);
+
+#ifdef CONFIG_CGROUP_SCHEDTUNE
+ schedtune_init_cgroups();
+#else
+ pr_info("schedtune: configured to support global boosting only\n");
+#endif /* CONFIG_CGROUP_SCHEDTUNE */
+
+ return 0;
+
+nodata:
+ rcu_read_unlock();
+ return -EINVAL;
+}
+postcore_initcall(schedtune_init);
diff --git a/kernel/sched/tune.h b/kernel/sched/tune.h
new file mode 100644
index 0000000..4f64417
--- /dev/null
+++ b/kernel/sched/tune.h
@@ -0,0 +1,55 @@
+
+#ifdef CONFIG_SCHED_TUNE
+
+#include <linux/reciprocal_div.h>
+
+/*
+ * System energy normalization constants
+ */
+struct target_nrg {
+ unsigned long min_power;
+ unsigned long max_power;
+ struct reciprocal_value rdiv;
+};
+
+#ifdef CONFIG_CGROUP_SCHEDTUNE
+
+int schedtune_cpu_boost(int cpu);
+int schedtune_task_boost(struct task_struct *tsk);
+
+int schedtune_prefer_idle(struct task_struct *tsk);
+
+void schedtune_exit_task(struct task_struct *tsk);
+
+void schedtune_enqueue_task(struct task_struct *p, int cpu);
+void schedtune_dequeue_task(struct task_struct *p, int cpu);
+
+#else /* CONFIG_CGROUP_SCHEDTUNE */
+
+#define schedtune_cpu_boost(cpu) get_sysctl_sched_cfs_boost()
+#define schedtune_task_boost(tsk) get_sysctl_sched_cfs_boost()
+
+#define schedtune_exit_task(task) do { } while (0)
+
+#define schedtune_enqueue_task(task, cpu) do { } while (0)
+#define schedtune_dequeue_task(task, cpu) do { } while (0)
+
+#endif /* CONFIG_CGROUP_SCHEDTUNE */
+
+int schedtune_normalize_energy(int energy);
+int schedtune_accept_deltas(int nrg_delta, int cap_delta,
+ struct task_struct *task);
+
+#else /* CONFIG_SCHED_TUNE */
+
+#define schedtune_cpu_boost(cpu) 0
+#define schedtune_task_boost(tsk) 0
+
+#define schedtune_exit_task(task) do { } while (0)
+
+#define schedtune_enqueue_task(task, cpu) do { } while (0)
+#define schedtune_dequeue_task(task, cpu) do { } while (0)
+
+#define schedtune_accept_deltas(nrg_delta, cap_delta, task) nrg_delta
+
+#endif /* CONFIG_SCHED_TUNE */
diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c
new file mode 100644
index 0000000..7f686ff
--- /dev/null
+++ b/kernel/sched/walt.c
@@ -0,0 +1,1168 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * Window Assisted Load Tracking (WALT) implementation credits:
+ * Srivatsa Vaddagiri, Steve Muckle, Syed Rameez Mustafa, Joonwoo Park,
+ * Pavan Kumar Kondeti, Olav Haugan
+ *
+ * 2016-03-06: Integration with EAS/refactoring by Vikram Mulukutla
+ * and Todd Kjos
+ */
+
+#include <linux/syscore_ops.h>
+#include <linux/cpufreq.h>
+#include <trace/events/sched.h>
+#include "sched.h"
+#include "walt.h"
+
+#define WINDOW_STATS_RECENT 0
+#define WINDOW_STATS_MAX 1
+#define WINDOW_STATS_MAX_RECENT_AVG 2
+#define WINDOW_STATS_AVG 3
+#define WINDOW_STATS_INVALID_POLICY 4
+
+#define EXITING_TASK_MARKER 0xdeaddead
+
+static __read_mostly unsigned int walt_ravg_hist_size = 5;
+static __read_mostly unsigned int walt_window_stats_policy =
+ WINDOW_STATS_MAX_RECENT_AVG;
+static __read_mostly unsigned int walt_account_wait_time = 1;
+static __read_mostly unsigned int walt_freq_account_wait_time = 0;
+static __read_mostly unsigned int walt_io_is_busy = 0;
+
+unsigned int sysctl_sched_walt_init_task_load_pct = 15;
+
+/* 1 -> use PELT based load stats, 0 -> use window-based load stats */
+unsigned int __read_mostly walt_disabled = 0;
+
+static unsigned int max_possible_efficiency = 1024;
+static unsigned int min_possible_efficiency = 1024;
+
+/*
+ * Maximum possible frequency across all cpus. Task demand and cpu
+ * capacity (cpu_power) metrics are scaled in reference to it.
+ */
+static unsigned int max_possible_freq = 1;
+
+/*
+ * Minimum possible max_freq across all cpus. This will be same as
+ * max_possible_freq on homogeneous systems and could be different from
+ * max_possible_freq on heterogenous systems. min_max_freq is used to derive
+ * capacity (cpu_power) of cpus.
+ */
+static unsigned int min_max_freq = 1;
+
+static unsigned int max_capacity = 1024;
+static unsigned int min_capacity = 1024;
+static unsigned int max_load_scale_factor = 1024;
+static unsigned int max_possible_capacity = 1024;
+
+/* Mask of all CPUs that have max_possible_capacity */
+static cpumask_t mpc_mask = CPU_MASK_ALL;
+
+/* Window size (in ns) */
+__read_mostly unsigned int walt_ravg_window = 20000000;
+
+/* Min window size (in ns) = 10ms */
+#define MIN_SCHED_RAVG_WINDOW 10000000
+
+/* Max window size (in ns) = 1s */
+#define MAX_SCHED_RAVG_WINDOW 1000000000
+
+static unsigned int sync_cpu;
+static ktime_t ktime_last;
+static bool walt_ktime_suspended;
+
+static unsigned int task_load(struct task_struct *p)
+{
+ return p->ravg.demand;
+}
+
+void
+walt_inc_cumulative_runnable_avg(struct rq *rq,
+ struct task_struct *p)
+{
+ rq->cumulative_runnable_avg += p->ravg.demand;
+}
+
+void
+walt_dec_cumulative_runnable_avg(struct rq *rq,
+ struct task_struct *p)
+{
+ rq->cumulative_runnable_avg -= p->ravg.demand;
+ BUG_ON((s64)rq->cumulative_runnable_avg < 0);
+}
+
+static void
+fixup_cumulative_runnable_avg(struct rq *rq,
+ struct task_struct *p, s64 task_load_delta)
+{
+ rq->cumulative_runnable_avg += task_load_delta;
+ if ((s64)rq->cumulative_runnable_avg < 0)
+ panic("cra less than zero: tld: %lld, task_load(p) = %u\n",
+ task_load_delta, task_load(p));
+}
+
+u64 walt_ktime_clock(void)
+{
+ if (unlikely(walt_ktime_suspended))
+ return ktime_to_ns(ktime_last);
+ return ktime_get_ns();
+}
+
+static void walt_resume(void)
+{
+ walt_ktime_suspended = false;
+}
+
+static int walt_suspend(void)
+{
+ ktime_last = ktime_get();
+ walt_ktime_suspended = true;
+ return 0;
+}
+
+static struct syscore_ops walt_syscore_ops = {
+ .resume = walt_resume,
+ .suspend = walt_suspend
+};
+
+static int __init walt_init_ops(void)
+{
+ register_syscore_ops(&walt_syscore_ops);
+ return 0;
+}
+late_initcall(walt_init_ops);
+
+void walt_inc_cfs_cumulative_runnable_avg(struct cfs_rq *cfs_rq,
+ struct task_struct *p)
+{
+ cfs_rq->cumulative_runnable_avg += p->ravg.demand;
+}
+
+void walt_dec_cfs_cumulative_runnable_avg(struct cfs_rq *cfs_rq,
+ struct task_struct *p)
+{
+ cfs_rq->cumulative_runnable_avg -= p->ravg.demand;
+}
+
+static int exiting_task(struct task_struct *p)
+{
+ if (p->flags & PF_EXITING) {
+ if (p->ravg.sum_history[0] != EXITING_TASK_MARKER) {
+ p->ravg.sum_history[0] = EXITING_TASK_MARKER;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static int __init set_walt_ravg_window(char *str)
+{
+ get_option(&str, &walt_ravg_window);
+
+ walt_disabled = (walt_ravg_window < MIN_SCHED_RAVG_WINDOW ||
+ walt_ravg_window > MAX_SCHED_RAVG_WINDOW);
+ return 0;
+}
+
+early_param("walt_ravg_window", set_walt_ravg_window);
+
+static void
+update_window_start(struct rq *rq, u64 wallclock)
+{
+ s64 delta;
+ int nr_windows;
+
+ delta = wallclock - rq->window_start;
+ /* If the MPM global timer is cleared, set delta as 0 to avoid kernel BUG happening */
+ if (delta < 0) {
+ delta = 0;
+ WARN_ONCE(1, "WALT wallclock appears to have gone backwards or reset\n");
+ }
+
+ if (delta < walt_ravg_window)
+ return;
+
+ nr_windows = div64_u64(delta, walt_ravg_window);
+ rq->window_start += (u64)nr_windows * (u64)walt_ravg_window;
+}
+
+static u64 scale_exec_time(u64 delta, struct rq *rq)
+{
+ unsigned int cur_freq = rq->cur_freq;
+ int sf;
+
+ if (unlikely(cur_freq > max_possible_freq))
+ cur_freq = rq->max_possible_freq;
+
+ /* round up div64 */
+ delta = div64_u64(delta * cur_freq + max_possible_freq - 1,
+ max_possible_freq);
+
+ sf = DIV_ROUND_UP(rq->efficiency * 1024, max_possible_efficiency);
+
+ delta *= sf;
+ delta >>= 10;
+
+ return delta;
+}
+
+static int cpu_is_waiting_on_io(struct rq *rq)
+{
+ if (!walt_io_is_busy)
+ return 0;
+
+ return atomic_read(&rq->nr_iowait);
+}
+
+void walt_account_irqtime(int cpu, struct task_struct *curr,
+ u64 delta, u64 wallclock)
+{
+ struct rq *rq = cpu_rq(cpu);
+ unsigned long flags, nr_windows;
+ u64 cur_jiffies_ts;
+
+ raw_spin_lock_irqsave(&rq->lock, flags);
+
+ /*
+ * cputime (wallclock) uses sched_clock so use the same here for
+ * consistency.
+ */
+ delta += sched_clock() - wallclock;
+ cur_jiffies_ts = get_jiffies_64();
+
+ if (is_idle_task(curr))
+ walt_update_task_ravg(curr, rq, IRQ_UPDATE, walt_ktime_clock(),
+ delta);
+
+ nr_windows = cur_jiffies_ts - rq->irqload_ts;
+
+ if (nr_windows) {
+ if (nr_windows < 10) {
+ /* Decay CPU's irqload by 3/4 for each window. */
+ rq->avg_irqload *= (3 * nr_windows);
+ rq->avg_irqload = div64_u64(rq->avg_irqload,
+ 4 * nr_windows);
+ } else {
+ rq->avg_irqload = 0;
+ }
+ rq->avg_irqload += rq->cur_irqload;
+ rq->cur_irqload = 0;
+ }
+
+ rq->cur_irqload += delta;
+ rq->irqload_ts = cur_jiffies_ts;
+ raw_spin_unlock_irqrestore(&rq->lock, flags);
+}
+
+
+#define WALT_HIGH_IRQ_TIMEOUT 3
+
+u64 walt_irqload(int cpu) {
+ struct rq *rq = cpu_rq(cpu);
+ s64 delta;
+ delta = get_jiffies_64() - rq->irqload_ts;
+
+ /*
+ * Current context can be preempted by irq and rq->irqload_ts can be
+ * updated by irq context so that delta can be negative.
+ * But this is okay and we can safely return as this means there
+ * was recent irq occurrence.
+ */
+
+ if (delta < WALT_HIGH_IRQ_TIMEOUT)
+ return rq->avg_irqload;
+ else
+ return 0;
+}
+
+int walt_cpu_high_irqload(int cpu) {
+ return walt_irqload(cpu) >= sysctl_sched_walt_cpu_high_irqload;
+}
+
+static int account_busy_for_cpu_time(struct rq *rq, struct task_struct *p,
+ u64 irqtime, int event)
+{
+ if (is_idle_task(p)) {
+ /* TASK_WAKE && TASK_MIGRATE is not possible on idle task! */
+ if (event == PICK_NEXT_TASK)
+ return 0;
+
+ /* PUT_PREV_TASK, TASK_UPDATE && IRQ_UPDATE are left */
+ return irqtime || cpu_is_waiting_on_io(rq);
+ }
+
+ if (event == TASK_WAKE)
+ return 0;
+
+ if (event == PUT_PREV_TASK || event == IRQ_UPDATE ||
+ event == TASK_UPDATE)
+ return 1;
+
+ /* Only TASK_MIGRATE && PICK_NEXT_TASK left */
+ return walt_freq_account_wait_time;
+}
+
+/*
+ * Account cpu activity in its busy time counters (rq->curr/prev_runnable_sum)
+ */
+static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
+ int event, u64 wallclock, u64 irqtime)
+{
+ int new_window, nr_full_windows = 0;
+ int p_is_curr_task = (p == rq->curr);
+ u64 mark_start = p->ravg.mark_start;
+ u64 window_start = rq->window_start;
+ u32 window_size = walt_ravg_window;
+ u64 delta;
+
+ new_window = mark_start < window_start;
+ if (new_window) {
+ nr_full_windows = div64_u64((window_start - mark_start),
+ window_size);
+ if (p->ravg.active_windows < USHRT_MAX)
+ p->ravg.active_windows++;
+ }
+
+ /* Handle per-task window rollover. We don't care about the idle
+ * task or exiting tasks. */
+ if (new_window && !is_idle_task(p) && !exiting_task(p)) {
+ u32 curr_window = 0;
+
+ if (!nr_full_windows)
+ curr_window = p->ravg.curr_window;
+
+ p->ravg.prev_window = curr_window;
+ p->ravg.curr_window = 0;
+ }
+
+ if (!account_busy_for_cpu_time(rq, p, irqtime, event)) {
+ /* account_busy_for_cpu_time() = 0, so no update to the
+ * task's current window needs to be made. This could be
+ * for example
+ *
+ * - a wakeup event on a task within the current
+ * window (!new_window below, no action required),
+ * - switching to a new task from idle (PICK_NEXT_TASK)
+ * in a new window where irqtime is 0 and we aren't
+ * waiting on IO */
+
+ if (!new_window)
+ return;
+
+ /* A new window has started. The RQ demand must be rolled
+ * over if p is the current task. */
+ if (p_is_curr_task) {
+ u64 prev_sum = 0;
+
+ /* p is either idle task or an exiting task */
+ if (!nr_full_windows) {
+ prev_sum = rq->curr_runnable_sum;
+ }
+
+ rq->prev_runnable_sum = prev_sum;
+ rq->curr_runnable_sum = 0;
+ }
+
+ return;
+ }
+
+ if (!new_window) {
+ /* account_busy_for_cpu_time() = 1 so busy time needs
+ * to be accounted to the current window. No rollover
+ * since we didn't start a new window. An example of this is
+ * when a task starts execution and then sleeps within the
+ * same window. */
+
+ if (!irqtime || !is_idle_task(p) || cpu_is_waiting_on_io(rq))
+ delta = wallclock - mark_start;
+ else
+ delta = irqtime;
+ delta = scale_exec_time(delta, rq);
+ rq->curr_runnable_sum += delta;
+ if (!is_idle_task(p) && !exiting_task(p))
+ p->ravg.curr_window += delta;
+
+ return;
+ }
+
+ if (!p_is_curr_task) {
+ /* account_busy_for_cpu_time() = 1 so busy time needs
+ * to be accounted to the current window. A new window
+ * has also started, but p is not the current task, so the
+ * window is not rolled over - just split up and account
+ * as necessary into curr and prev. The window is only
+ * rolled over when a new window is processed for the current
+ * task.
+ *
+ * Irqtime can't be accounted by a task that isn't the
+ * currently running task. */
+
+ if (!nr_full_windows) {
+ /* A full window hasn't elapsed, account partial
+ * contribution to previous completed window. */
+ delta = scale_exec_time(window_start - mark_start, rq);
+ if (!exiting_task(p))
+ p->ravg.prev_window += delta;
+ } else {
+ /* Since at least one full window has elapsed,
+ * the contribution to the previous window is the
+ * full window (window_size). */
+ delta = scale_exec_time(window_size, rq);
+ if (!exiting_task(p))
+ p->ravg.prev_window = delta;
+ }
+ rq->prev_runnable_sum += delta;
+
+ /* Account piece of busy time in the current window. */
+ delta = scale_exec_time(wallclock - window_start, rq);
+ rq->curr_runnable_sum += delta;
+ if (!exiting_task(p))
+ p->ravg.curr_window = delta;
+
+ return;
+ }
+
+ if (!irqtime || !is_idle_task(p) || cpu_is_waiting_on_io(rq)) {
+ /* account_busy_for_cpu_time() = 1 so busy time needs
+ * to be accounted to the current window. A new window
+ * has started and p is the current task so rollover is
+ * needed. If any of these three above conditions are true
+ * then this busy time can't be accounted as irqtime.
+ *
+ * Busy time for the idle task or exiting tasks need not
+ * be accounted.
+ *
+ * An example of this would be a task that starts execution
+ * and then sleeps once a new window has begun. */
+
+ if (!nr_full_windows) {
+ /* A full window hasn't elapsed, account partial
+ * contribution to previous completed window. */
+ delta = scale_exec_time(window_start - mark_start, rq);
+ if (!is_idle_task(p) && !exiting_task(p))
+ p->ravg.prev_window += delta;
+
+ delta += rq->curr_runnable_sum;
+ } else {
+ /* Since at least one full window has elapsed,
+ * the contribution to the previous window is the
+ * full window (window_size). */
+ delta = scale_exec_time(window_size, rq);
+ if (!is_idle_task(p) && !exiting_task(p))
+ p->ravg.prev_window = delta;
+
+ }
+ /*
+ * Rollover for normal runnable sum is done here by overwriting
+ * the values in prev_runnable_sum and curr_runnable_sum.
+ * Rollover for new task runnable sum has completed by previous
+ * if-else statement.
+ */
+ rq->prev_runnable_sum = delta;
+
+ /* Account piece of busy time in the current window. */
+ delta = scale_exec_time(wallclock - window_start, rq);
+ rq->curr_runnable_sum = delta;
+ if (!is_idle_task(p) && !exiting_task(p))
+ p->ravg.curr_window = delta;
+
+ return;
+ }
+
+ if (irqtime) {
+ /* account_busy_for_cpu_time() = 1 so busy time needs
+ * to be accounted to the current window. A new window
+ * has started and p is the current task so rollover is
+ * needed. The current task must be the idle task because
+ * irqtime is not accounted for any other task.
+ *
+ * Irqtime will be accounted each time we process IRQ activity
+ * after a period of idleness, so we know the IRQ busy time
+ * started at wallclock - irqtime. */
+
+ BUG_ON(!is_idle_task(p));
+ mark_start = wallclock - irqtime;
+
+ /* Roll window over. If IRQ busy time was just in the current
+ * window then that is all that need be accounted. */
+ rq->prev_runnable_sum = rq->curr_runnable_sum;
+ if (mark_start > window_start) {
+ rq->curr_runnable_sum = scale_exec_time(irqtime, rq);
+ return;
+ }
+
+ /* The IRQ busy time spanned multiple windows. Process the
+ * busy time preceding the current window start first. */
+ delta = window_start - mark_start;
+ if (delta > window_size)
+ delta = window_size;
+ delta = scale_exec_time(delta, rq);
+ rq->prev_runnable_sum += delta;
+
+ /* Process the remaining IRQ busy time in the current window. */
+ delta = wallclock - window_start;
+ rq->curr_runnable_sum = scale_exec_time(delta, rq);
+
+ return;
+ }
+
+ BUG();
+}
+
+static int account_busy_for_task_demand(struct task_struct *p, int event)
+{
+ /* No need to bother updating task demand for exiting tasks
+ * or the idle task. */
+ if (exiting_task(p) || is_idle_task(p))
+ return 0;
+
+ /* When a task is waking up it is completing a segment of non-busy
+ * time. Likewise, if wait time is not treated as busy time, then
+ * when a task begins to run or is migrated, it is not running and
+ * is completing a segment of non-busy time. */
+ if (event == TASK_WAKE || (!walt_account_wait_time &&
+ (event == PICK_NEXT_TASK || event == TASK_MIGRATE)))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Called when new window is starting for a task, to record cpu usage over
+ * recently concluded window(s). Normally 'samples' should be 1. It can be > 1
+ * when, say, a real-time task runs without preemption for several windows at a
+ * stretch.
+ */
+static void update_history(struct rq *rq, struct task_struct *p,
+ u32 runtime, int samples, int event)
+{
+ u32 *hist = &p->ravg.sum_history[0];
+ int ridx, widx;
+ u32 max = 0, avg, demand;
+ u64 sum = 0;
+
+ /* Ignore windows where task had no activity */
+ if (!runtime || is_idle_task(p) || exiting_task(p) || !samples)
+ goto done;
+
+ /* Push new 'runtime' value onto stack */
+ widx = walt_ravg_hist_size - 1;
+ ridx = widx - samples;
+ for (; ridx >= 0; --widx, --ridx) {
+ hist[widx] = hist[ridx];
+ sum += hist[widx];
+ if (hist[widx] > max)
+ max = hist[widx];
+ }
+
+ for (widx = 0; widx < samples && widx < walt_ravg_hist_size; widx++) {
+ hist[widx] = runtime;
+ sum += hist[widx];
+ if (hist[widx] > max)
+ max = hist[widx];
+ }
+
+ p->ravg.sum = 0;
+
+ if (walt_window_stats_policy == WINDOW_STATS_RECENT) {
+ demand = runtime;
+ } else if (walt_window_stats_policy == WINDOW_STATS_MAX) {
+ demand = max;
+ } else {
+ avg = div64_u64(sum, walt_ravg_hist_size);
+ if (walt_window_stats_policy == WINDOW_STATS_AVG)
+ demand = avg;
+ else
+ demand = max(avg, runtime);
+ }
+
+ /*
+ * A throttled deadline sched class task gets dequeued without
+ * changing p->on_rq. Since the dequeue decrements hmp stats
+ * avoid decrementing it here again.
+ */
+ if (task_on_rq_queued(p) && (!task_has_dl_policy(p) ||
+ !p->dl.dl_throttled))
+ fixup_cumulative_runnable_avg(rq, p, demand);
+
+ p->ravg.demand = demand;
+
+done:
+ trace_walt_update_history(rq, p, runtime, samples, event);
+ return;
+}
+
+static void add_to_task_demand(struct rq *rq, struct task_struct *p,
+ u64 delta)
+{
+ delta = scale_exec_time(delta, rq);
+ p->ravg.sum += delta;
+ if (unlikely(p->ravg.sum > walt_ravg_window))
+ p->ravg.sum = walt_ravg_window;
+}
+
+/*
+ * Account cpu demand of task and/or update task's cpu demand history
+ *
+ * ms = p->ravg.mark_start;
+ * wc = wallclock
+ * ws = rq->window_start
+ *
+ * Three possibilities:
+ *
+ * a) Task event is contained within one window.
+ * window_start < mark_start < wallclock
+ *
+ * ws ms wc
+ * | | |
+ * V V V
+ * |---------------|
+ *
+ * In this case, p->ravg.sum is updated *iff* event is appropriate
+ * (ex: event == PUT_PREV_TASK)
+ *
+ * b) Task event spans two windows.
+ * mark_start < window_start < wallclock
+ *
+ * ms ws wc
+ * | | |
+ * V V V
+ * -----|-------------------
+ *
+ * In this case, p->ravg.sum is updated with (ws - ms) *iff* event
+ * is appropriate, then a new window sample is recorded followed
+ * by p->ravg.sum being set to (wc - ws) *iff* event is appropriate.
+ *
+ * c) Task event spans more than two windows.
+ *
+ * ms ws_tmp ws wc
+ * | | | |
+ * V V V V
+ * ---|-------|-------|-------|-------|------
+ * | |
+ * |<------ nr_full_windows ------>|
+ *
+ * In this case, p->ravg.sum is updated with (ws_tmp - ms) first *iff*
+ * event is appropriate, window sample of p->ravg.sum is recorded,
+ * 'nr_full_window' samples of window_size is also recorded *iff*
+ * event is appropriate and finally p->ravg.sum is set to (wc - ws)
+ * *iff* event is appropriate.
+ *
+ * IMPORTANT : Leave p->ravg.mark_start unchanged, as update_cpu_busy_time()
+ * depends on it!
+ */
+static void update_task_demand(struct task_struct *p, struct rq *rq,
+ int event, u64 wallclock)
+{
+ u64 mark_start = p->ravg.mark_start;
+ u64 delta, window_start = rq->window_start;
+ int new_window, nr_full_windows;
+ u32 window_size = walt_ravg_window;
+
+ new_window = mark_start < window_start;
+ if (!account_busy_for_task_demand(p, event)) {
+ if (new_window)
+ /* If the time accounted isn't being accounted as
+ * busy time, and a new window started, only the
+ * previous window need be closed out with the
+ * pre-existing demand. Multiple windows may have
+ * elapsed, but since empty windows are dropped,
+ * it is not necessary to account those. */
+ update_history(rq, p, p->ravg.sum, 1, event);
+ return;
+ }
+
+ if (!new_window) {
+ /* The simple case - busy time contained within the existing
+ * window. */
+ add_to_task_demand(rq, p, wallclock - mark_start);
+ return;
+ }
+
+ /* Busy time spans at least two windows. Temporarily rewind
+ * window_start to first window boundary after mark_start. */
+ delta = window_start - mark_start;
+ nr_full_windows = div64_u64(delta, window_size);
+ window_start -= (u64)nr_full_windows * (u64)window_size;
+
+ /* Process (window_start - mark_start) first */
+ add_to_task_demand(rq, p, window_start - mark_start);
+
+ /* Push new sample(s) into task's demand history */
+ update_history(rq, p, p->ravg.sum, 1, event);
+ if (nr_full_windows)
+ update_history(rq, p, scale_exec_time(window_size, rq),
+ nr_full_windows, event);
+
+ /* Roll window_start back to current to process any remainder
+ * in current window. */
+ window_start += (u64)nr_full_windows * (u64)window_size;
+
+ /* Process (wallclock - window_start) next */
+ mark_start = window_start;
+ add_to_task_demand(rq, p, wallclock - mark_start);
+}
+
+/* Reflect task activity on its demand and cpu's busy time statistics */
+void walt_update_task_ravg(struct task_struct *p, struct rq *rq,
+ int event, u64 wallclock, u64 irqtime)
+{
+ if (walt_disabled || !rq->window_start)
+ return;
+
+ lockdep_assert_held(&rq->lock);
+
+ update_window_start(rq, wallclock);
+
+ if (!p->ravg.mark_start)
+ goto done;
+
+ update_task_demand(p, rq, event, wallclock);
+ update_cpu_busy_time(p, rq, event, wallclock, irqtime);
+
+done:
+ trace_walt_update_task_ravg(p, rq, event, wallclock, irqtime);
+
+ p->ravg.mark_start = wallclock;
+}
+
+unsigned long __weak arch_get_cpu_efficiency(int cpu)
+{
+ return SCHED_CAPACITY_SCALE;
+}
+
+void walt_init_cpu_efficiency(void)
+{
+ int i, efficiency;
+ unsigned int max = 0, min = UINT_MAX;
+
+ for_each_possible_cpu(i) {
+ efficiency = arch_get_cpu_efficiency(i);
+ cpu_rq(i)->efficiency = efficiency;
+
+ if (efficiency > max)
+ max = efficiency;
+ if (efficiency < min)
+ min = efficiency;
+ }
+
+ if (max)
+ max_possible_efficiency = max;
+
+ if (min)
+ min_possible_efficiency = min;
+}
+
+static void reset_task_stats(struct task_struct *p)
+{
+ u32 sum = 0;
+
+ if (exiting_task(p))
+ sum = EXITING_TASK_MARKER;
+
+ memset(&p->ravg, 0, sizeof(struct ravg));
+ /* Retain EXITING_TASK marker */
+ p->ravg.sum_history[0] = sum;
+}
+
+void walt_mark_task_starting(struct task_struct *p)
+{
+ u64 wallclock;
+ struct rq *rq = task_rq(p);
+
+ if (!rq->window_start) {
+ reset_task_stats(p);
+ return;
+ }
+
+ wallclock = walt_ktime_clock();
+ p->ravg.mark_start = wallclock;
+}
+
+void walt_set_window_start(struct rq *rq)
+{
+ int cpu = cpu_of(rq);
+ struct rq *sync_rq = cpu_rq(sync_cpu);
+
+ if (rq->window_start)
+ return;
+
+ if (cpu == sync_cpu) {
+ rq->window_start = walt_ktime_clock();
+ } else {
+ raw_spin_unlock(&rq->lock);
+ double_rq_lock(rq, sync_rq);
+ rq->window_start = cpu_rq(sync_cpu)->window_start;
+ rq->curr_runnable_sum = rq->prev_runnable_sum = 0;
+ raw_spin_unlock(&sync_rq->lock);
+ }
+
+ rq->curr->ravg.mark_start = rq->window_start;
+}
+
+void walt_migrate_sync_cpu(int cpu)
+{
+ if (cpu == sync_cpu)
+ sync_cpu = smp_processor_id();
+}
+
+void walt_fixup_busy_time(struct task_struct *p, int new_cpu)
+{
+ struct rq *src_rq = task_rq(p);
+ struct rq *dest_rq = cpu_rq(new_cpu);
+ u64 wallclock;
+
+ if (!p->on_rq && p->state != TASK_WAKING)
+ return;
+
+ if (exiting_task(p)) {
+ return;
+ }
+
+ if (p->state == TASK_WAKING)
+ double_rq_lock(src_rq, dest_rq);
+
+ wallclock = walt_ktime_clock();
+
+ walt_update_task_ravg(task_rq(p)->curr, task_rq(p),
+ TASK_UPDATE, wallclock, 0);
+ walt_update_task_ravg(dest_rq->curr, dest_rq,
+ TASK_UPDATE, wallclock, 0);
+
+ walt_update_task_ravg(p, task_rq(p), TASK_MIGRATE, wallclock, 0);
+
+ if (p->ravg.curr_window) {
+ src_rq->curr_runnable_sum -= p->ravg.curr_window;
+ dest_rq->curr_runnable_sum += p->ravg.curr_window;
+ }
+
+ if (p->ravg.prev_window) {
+ src_rq->prev_runnable_sum -= p->ravg.prev_window;
+ dest_rq->prev_runnable_sum += p->ravg.prev_window;
+ }
+
+ if ((s64)src_rq->prev_runnable_sum < 0) {
+ src_rq->prev_runnable_sum = 0;
+ WARN_ON(1);
+ }
+ if ((s64)src_rq->curr_runnable_sum < 0) {
+ src_rq->curr_runnable_sum = 0;
+ WARN_ON(1);
+ }
+
+ trace_walt_migration_update_sum(src_rq, p);
+ trace_walt_migration_update_sum(dest_rq, p);
+
+ if (p->state == TASK_WAKING)
+ double_rq_unlock(src_rq, dest_rq);
+}
+
+/* Keep track of max/min capacity possible across CPUs "currently" */
+static void __update_min_max_capacity(void)
+{
+ int i;
+ int max = 0, min = INT_MAX;
+
+ for_each_online_cpu(i) {
+ if (cpu_rq(i)->capacity > max)
+ max = cpu_rq(i)->capacity;
+ if (cpu_rq(i)->capacity < min)
+ min = cpu_rq(i)->capacity;
+ }
+
+ max_capacity = max;
+ min_capacity = min;
+}
+
+static void update_min_max_capacity(void)
+{
+ unsigned long flags;
+ int i;
+
+ local_irq_save(flags);
+ for_each_possible_cpu(i)
+ raw_spin_lock(&cpu_rq(i)->lock);
+
+ __update_min_max_capacity();
+
+ for_each_possible_cpu(i)
+ raw_spin_unlock(&cpu_rq(i)->lock);
+ local_irq_restore(flags);
+}
+
+/*
+ * Return 'capacity' of a cpu in reference to "least" efficient cpu, such that
+ * least efficient cpu gets capacity of 1024
+ */
+static unsigned long capacity_scale_cpu_efficiency(int cpu)
+{
+ return (1024 * cpu_rq(cpu)->efficiency) / min_possible_efficiency;
+}
+
+/*
+ * Return 'capacity' of a cpu in reference to cpu with lowest max_freq
+ * (min_max_freq), such that one with lowest max_freq gets capacity of 1024.
+ */
+static unsigned long capacity_scale_cpu_freq(int cpu)
+{
+ return (1024 * cpu_rq(cpu)->max_freq) / min_max_freq;
+}
+
+/*
+ * Return load_scale_factor of a cpu in reference to "most" efficient cpu, so
+ * that "most" efficient cpu gets a load_scale_factor of 1
+ */
+static unsigned long load_scale_cpu_efficiency(int cpu)
+{
+ return DIV_ROUND_UP(1024 * max_possible_efficiency,
+ cpu_rq(cpu)->efficiency);
+}
+
+/*
+ * Return load_scale_factor of a cpu in reference to cpu with best max_freq
+ * (max_possible_freq), so that one with best max_freq gets a load_scale_factor
+ * of 1.
+ */
+static unsigned long load_scale_cpu_freq(int cpu)
+{
+ return DIV_ROUND_UP(1024 * max_possible_freq, cpu_rq(cpu)->max_freq);
+}
+
+static int compute_capacity(int cpu)
+{
+ int capacity = 1024;
+
+ capacity *= capacity_scale_cpu_efficiency(cpu);
+ capacity >>= 10;
+
+ capacity *= capacity_scale_cpu_freq(cpu);
+ capacity >>= 10;
+
+ return capacity;
+}
+
+static int compute_load_scale_factor(int cpu)
+{
+ int load_scale = 1024;
+
+ /*
+ * load_scale_factor accounts for the fact that task load
+ * is in reference to "best" performing cpu. Task's load will need to be
+ * scaled (up) by a factor to determine suitability to be placed on a
+ * (little) cpu.
+ */
+ load_scale *= load_scale_cpu_efficiency(cpu);
+ load_scale >>= 10;
+
+ load_scale *= load_scale_cpu_freq(cpu);
+ load_scale >>= 10;
+
+ return load_scale;
+}
+
+static int cpufreq_notifier_policy(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ struct cpufreq_policy *policy = (struct cpufreq_policy *)data;
+ int i, update_max = 0;
+ u64 highest_mpc = 0, highest_mplsf = 0;
+ const struct cpumask *cpus = policy->related_cpus;
+ unsigned int orig_min_max_freq = min_max_freq;
+ unsigned int orig_max_possible_freq = max_possible_freq;
+ /* Initialized to policy->max in case policy->related_cpus is empty! */
+ unsigned int orig_max_freq = policy->max;
+
+ if (val != CPUFREQ_NOTIFY && val != CPUFREQ_REMOVE_POLICY &&
+ val != CPUFREQ_CREATE_POLICY)
+ return 0;
+
+ if (val == CPUFREQ_REMOVE_POLICY || val == CPUFREQ_CREATE_POLICY) {
+ update_min_max_capacity();
+ return 0;
+ }
+
+ for_each_cpu(i, policy->related_cpus) {
+ cpumask_copy(&cpu_rq(i)->freq_domain_cpumask,
+ policy->related_cpus);
+ orig_max_freq = cpu_rq(i)->max_freq;
+ cpu_rq(i)->min_freq = policy->min;
+ cpu_rq(i)->max_freq = policy->max;
+ cpu_rq(i)->cur_freq = policy->cur;
+ cpu_rq(i)->max_possible_freq = policy->cpuinfo.max_freq;
+ }
+
+ max_possible_freq = max(max_possible_freq, policy->cpuinfo.max_freq);
+ if (min_max_freq == 1)
+ min_max_freq = UINT_MAX;
+ min_max_freq = min(min_max_freq, policy->cpuinfo.max_freq);
+ BUG_ON(!min_max_freq);
+ BUG_ON(!policy->max);
+
+ /* Changes to policy other than max_freq don't require any updates */
+ if (orig_max_freq == policy->max)
+ return 0;
+
+ /*
+ * A changed min_max_freq or max_possible_freq (possible during bootup)
+ * needs to trigger re-computation of load_scale_factor and capacity for
+ * all possible cpus (even those offline). It also needs to trigger
+ * re-computation of nr_big_task count on all online cpus.
+ *
+ * A changed rq->max_freq otoh needs to trigger re-computation of
+ * load_scale_factor and capacity for just the cluster of cpus involved.
+ * Since small task definition depends on max_load_scale_factor, a
+ * changed load_scale_factor of one cluster could influence
+ * classification of tasks in another cluster. Hence a changed
+ * rq->max_freq will need to trigger re-computation of nr_big_task
+ * count on all online cpus.
+ *
+ * While it should be sufficient for nr_big_tasks to be
+ * re-computed for only online cpus, we have inadequate context
+ * information here (in policy notifier) with regard to hotplug-safety
+ * context in which notification is issued. As a result, we can't use
+ * get_online_cpus() here, as it can lead to deadlock. Until cpufreq is
+ * fixed up to issue notification always in hotplug-safe context,
+ * re-compute nr_big_task for all possible cpus.
+ */
+
+ if (orig_min_max_freq != min_max_freq ||
+ orig_max_possible_freq != max_possible_freq) {
+ cpus = cpu_possible_mask;
+ update_max = 1;
+ }
+
+ /*
+ * Changed load_scale_factor can trigger reclassification of tasks as
+ * big or small. Make this change "atomic" so that tasks are accounted
+ * properly due to changed load_scale_factor
+ */
+ for_each_cpu(i, cpus) {
+ struct rq *rq = cpu_rq(i);
+
+ rq->capacity = compute_capacity(i);
+ rq->load_scale_factor = compute_load_scale_factor(i);
+
+ if (update_max) {
+ u64 mpc, mplsf;
+
+ mpc = div_u64(((u64) rq->capacity) *
+ rq->max_possible_freq, rq->max_freq);
+ rq->max_possible_capacity = (int) mpc;
+
+ mplsf = div_u64(((u64) rq->load_scale_factor) *
+ rq->max_possible_freq, rq->max_freq);
+
+ if (mpc > highest_mpc) {
+ highest_mpc = mpc;
+ cpumask_clear(&mpc_mask);
+ cpumask_set_cpu(i, &mpc_mask);
+ } else if (mpc == highest_mpc) {
+ cpumask_set_cpu(i, &mpc_mask);
+ }
+
+ if (mplsf > highest_mplsf)
+ highest_mplsf = mplsf;
+ }
+ }
+
+ if (update_max) {
+ max_possible_capacity = highest_mpc;
+ max_load_scale_factor = highest_mplsf;
+ }
+
+ __update_min_max_capacity();
+
+ return 0;
+}
+
+static int cpufreq_notifier_trans(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ struct cpufreq_freqs *freq = (struct cpufreq_freqs *)data;
+ unsigned int cpu = freq->cpu, new_freq = freq->new;
+ unsigned long flags;
+ int i;
+
+ if (val != CPUFREQ_POSTCHANGE)
+ return 0;
+
+ BUG_ON(!new_freq);
+
+ if (cpu_rq(cpu)->cur_freq == new_freq)
+ return 0;
+
+ for_each_cpu(i, &cpu_rq(cpu)->freq_domain_cpumask) {
+ struct rq *rq = cpu_rq(i);
+
+ raw_spin_lock_irqsave(&rq->lock, flags);
+ walt_update_task_ravg(rq->curr, rq, TASK_UPDATE,
+ walt_ktime_clock(), 0);
+ rq->cur_freq = new_freq;
+ raw_spin_unlock_irqrestore(&rq->lock, flags);
+ }
+
+ return 0;
+}
+
+static struct notifier_block notifier_policy_block = {
+ .notifier_call = cpufreq_notifier_policy
+};
+
+static struct notifier_block notifier_trans_block = {
+ .notifier_call = cpufreq_notifier_trans
+};
+
+static int register_sched_callback(void)
+{
+ int ret;
+
+ ret = cpufreq_register_notifier(¬ifier_policy_block,
+ CPUFREQ_POLICY_NOTIFIER);
+
+ if (!ret)
+ ret = cpufreq_register_notifier(¬ifier_trans_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+
+ return 0;
+}
+
+/*
+ * cpufreq callbacks can be registered at core_initcall or later time.
+ * Any registration done prior to that is "forgotten" by cpufreq. See
+ * initialization of variable init_cpufreq_transition_notifier_list_called
+ * for further information.
+ */
+core_initcall(register_sched_callback);
+
+void walt_init_new_task_load(struct task_struct *p)
+{
+ int i;
+ u32 init_load_windows =
+ div64_u64((u64)sysctl_sched_walt_init_task_load_pct *
+ (u64)walt_ravg_window, 100);
+ u32 init_load_pct = current->init_load_pct;
+
+ p->init_load_pct = 0;
+ memset(&p->ravg, 0, sizeof(struct ravg));
+
+ if (init_load_pct) {
+ init_load_windows = div64_u64((u64)init_load_pct *
+ (u64)walt_ravg_window, 100);
+ }
+
+ p->ravg.demand = init_load_windows;
+ for (i = 0; i < RAVG_HIST_SIZE_MAX; ++i)
+ p->ravg.sum_history[i] = init_load_windows;
+}
diff --git a/kernel/sched/walt.h b/kernel/sched/walt.h
new file mode 100644
index 0000000..e181c87
--- /dev/null
+++ b/kernel/sched/walt.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WALT_H
+#define __WALT_H
+
+#ifdef CONFIG_SCHED_WALT
+
+void walt_update_task_ravg(struct task_struct *p, struct rq *rq, int event,
+ u64 wallclock, u64 irqtime);
+void walt_inc_cumulative_runnable_avg(struct rq *rq, struct task_struct *p);
+void walt_dec_cumulative_runnable_avg(struct rq *rq, struct task_struct *p);
+void walt_inc_cfs_cumulative_runnable_avg(struct cfs_rq *rq,
+ struct task_struct *p);
+void walt_dec_cfs_cumulative_runnable_avg(struct cfs_rq *rq,
+ struct task_struct *p);
+void walt_fixup_busy_time(struct task_struct *p, int new_cpu);
+void walt_init_new_task_load(struct task_struct *p);
+void walt_mark_task_starting(struct task_struct *p);
+void walt_set_window_start(struct rq *rq);
+void walt_migrate_sync_cpu(int cpu);
+void walt_init_cpu_efficiency(void);
+u64 walt_ktime_clock(void);
+void walt_account_irqtime(int cpu, struct task_struct *curr, u64 delta,
+ u64 wallclock);
+
+u64 walt_irqload(int cpu);
+int walt_cpu_high_irqload(int cpu);
+
+#else /* CONFIG_SCHED_WALT */
+
+static inline void walt_update_task_ravg(struct task_struct *p, struct rq *rq,
+ int event, u64 wallclock, u64 irqtime) { }
+static inline void walt_inc_cumulative_runnable_avg(struct rq *rq, struct task_struct *p) { }
+static inline void walt_dec_cumulative_runnable_avg(struct rq *rq, struct task_struct *p) { }
+static inline void walt_inc_cfs_cumulative_runnable_avg(struct cfs_rq *rq,
+ struct task_struct *p) { }
+static inline void walt_dec_cfs_cumulative_runnable_avg(struct cfs_rq *rq,
+ struct task_struct *p) { }
+static inline void walt_fixup_busy_time(struct task_struct *p, int new_cpu) { }
+static inline void walt_init_new_task_load(struct task_struct *p) { }
+static inline void walt_mark_task_starting(struct task_struct *p) { }
+static inline void walt_set_window_start(struct rq *rq) { }
+static inline void walt_migrate_sync_cpu(int cpu) { }
+static inline void walt_init_cpu_efficiency(void) { }
+static inline u64 walt_ktime_clock(void) { return 0; }
+
+#endif /* CONFIG_SCHED_WALT */
+
+extern unsigned int walt_disabled;
+
+#endif
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index db14070..d2a397f 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -530,6 +530,64 @@
.extra2 = &max_sched_granularity_ns,
},
{
+ .procname = "sched_is_big_little",
+ .data = &sysctl_sched_is_big_little,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+#ifdef CONFIG_SCHED_WALT
+ {
+ .procname = "sched_use_walt_cpu_util",
+ .data = &sysctl_sched_use_walt_cpu_util,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .procname = "sched_use_walt_task_util",
+ .data = &sysctl_sched_use_walt_task_util,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .procname = "sched_walt_init_task_load_pct",
+ .data = &sysctl_sched_walt_init_task_load_pct,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .procname = "sched_walt_cpu_high_irqload",
+ .data = &sysctl_sched_walt_cpu_high_irqload,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+#endif
+ {
+ .procname = "sched_sync_hint_enable",
+ .data = &sysctl_sched_sync_hint_enable,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .procname = "sched_initial_task_util",
+ .data = &sysctl_sched_initial_task_util,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .procname = "sched_cstate_aware",
+ .data = &sysctl_sched_cstate_aware,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
.procname = "sched_wakeup_granularity_ns",
.data = &sysctl_sched_wakeup_granularity,
.maxlen = sizeof(unsigned int),
@@ -2742,6 +2800,7 @@
break;
if (neg)
continue;
+ val = convmul * val / convdiv;
if ((min && val < *min) || (max && val > *max))
continue;
*i = val;
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c
index f6aae79..d2a20e8 100644
--- a/kernel/time/tick-broadcast.c
+++ b/kernel/time/tick-broadcast.c
@@ -871,6 +871,9 @@
{
int cpu = smp_processor_id();
+ if (!bc)
+ return;
+
/* Set it up only once ! */
if (bc->event_handler != tick_handle_oneshot_broadcast) {
int was_periodic = clockevent_state_periodic(bc);
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 37dec7e..bfe589e 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -299,10 +299,10 @@
static inline u32 arch_gettimeoffset(void) { return 0; }
#endif
-static inline s64 timekeeping_delta_to_ns(struct tk_read_base *tkr,
+static inline u64 timekeeping_delta_to_ns(struct tk_read_base *tkr,
cycle_t delta)
{
- s64 nsec;
+ u64 nsec;
nsec = delta * tkr->mult + tkr->xtime_nsec;
nsec >>= tkr->shift;
@@ -425,6 +425,35 @@
}
EXPORT_SYMBOL_GPL(ktime_get_raw_fast_ns);
+/**
+ * ktime_get_boot_fast_ns - NMI safe and fast access to boot clock.
+ *
+ * To keep it NMI safe since we're accessing from tracing, we're not using a
+ * separate timekeeper with updates to monotonic clock and boot offset
+ * protected with seqlocks. This has the following minor side effects:
+ *
+ * (1) Its possible that a timestamp be taken after the boot offset is updated
+ * but before the timekeeper is updated. If this happens, the new boot offset
+ * is added to the old timekeeping making the clock appear to update slightly
+ * earlier:
+ * CPU 0 CPU 1
+ * timekeeping_inject_sleeptime64()
+ * __timekeeping_inject_sleeptime(tk, delta);
+ * timestamp();
+ * timekeeping_update(tk, TK_CLEAR_NTP...);
+ *
+ * (2) On 32-bit systems, the 64-bit boot offset (tk->offs_boot) may be
+ * partially updated. Since the tk->offs_boot update is a rare event, this
+ * should be a rare occurrence which postprocessing should be able to handle.
+ */
+u64 notrace ktime_get_boot_fast_ns(void)
+{
+ struct timekeeper *tk = &tk_core.timekeeper;
+
+ return (ktime_get_mono_fast_ns() + ktime_to_ns(tk->offs_boot));
+}
+EXPORT_SYMBOL_GPL(ktime_get_boot_fast_ns);
+
/* Suspend-time cycles value for halted fast timekeeper. */
static cycle_t cycles_at_suspend;
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index df9cde4..79beb60 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -1126,6 +1126,7 @@
{ trace_clock, "perf", 1 },
{ ktime_get_mono_fast_ns, "mono", 1 },
{ ktime_get_raw_fast_ns, "mono_raw", 1 },
+ { ktime_get_boot_fast_ns, "boot", 1 },
ARCH_TRACE_CLOCKS
};
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index 305f535..3cb38f1 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -847,6 +847,10 @@
cpu_data = per_cpu_ptr(data->cpu_data, cpu);
+ /* If a graph tracer ignored set_graph_notrace */
+ if (call->depth < -1)
+ call->depth += FTRACE_NOTRACE_DEPTH;
+
/*
* Comments display at + 1 to depth. Since
* this is a leaf function, keep the comments
@@ -855,7 +859,8 @@
cpu_data->depth = call->depth - 1;
/* No need to keep this function around for this depth */
- if (call->depth < FTRACE_RETFUNC_DEPTH)
+ if (call->depth < FTRACE_RETFUNC_DEPTH &&
+ !WARN_ON_ONCE(call->depth < 0))
cpu_data->enter_funcs[call->depth] = 0;
}
@@ -885,11 +890,16 @@
struct fgraph_cpu_data *cpu_data;
int cpu = iter->cpu;
+ /* If a graph tracer ignored set_graph_notrace */
+ if (call->depth < -1)
+ call->depth += FTRACE_NOTRACE_DEPTH;
+
cpu_data = per_cpu_ptr(data->cpu_data, cpu);
cpu_data->depth = call->depth;
/* Save this function pointer to see if the exit matches */
- if (call->depth < FTRACE_RETFUNC_DEPTH)
+ if (call->depth < FTRACE_RETFUNC_DEPTH &&
+ !WARN_ON_ONCE(call->depth < 0))
cpu_data->enter_funcs[call->depth] = call->func;
}
@@ -1119,7 +1129,8 @@
*/
cpu_data->depth = trace->depth - 1;
- if (trace->depth < FTRACE_RETFUNC_DEPTH) {
+ if (trace->depth < FTRACE_RETFUNC_DEPTH &&
+ !WARN_ON_ONCE(trace->depth < 0)) {
if (cpu_data->enter_funcs[trace->depth] != trace->func)
func_match = 0;
cpu_data->enter_funcs[trace->depth] = 0;
diff --git a/kernel/trace/trace_hwlat.c b/kernel/trace/trace_hwlat.c
index b97286c..f00b013 100644
--- a/kernel/trace/trace_hwlat.c
+++ b/kernel/trace/trace_hwlat.c
@@ -266,7 +266,7 @@
static struct cpumask save_cpumask;
static bool disable_migrate;
-static void move_to_next_cpu(void)
+static void move_to_next_cpu(bool initmask)
{
static struct cpumask *current_mask;
int next_cpu;
@@ -275,7 +275,7 @@
return;
/* Just pick the first CPU on first iteration */
- if (!current_mask) {
+ if (initmask) {
current_mask = &save_cpumask;
get_online_cpus();
cpumask_and(current_mask, cpu_online_mask, tracing_buffer_mask);
@@ -330,10 +330,12 @@
static int kthread_fn(void *data)
{
u64 interval;
+ bool initmask = true;
while (!kthread_should_stop()) {
- move_to_next_cpu();
+ move_to_next_cpu(initmask);
+ initmask = false;
local_irq_disable();
get_sample();
diff --git a/kernel/ucount.c b/kernel/ucount.c
index 9d20d5d..4bbd38e 100644
--- a/kernel/ucount.c
+++ b/kernel/ucount.c
@@ -128,10 +128,10 @@
struct hlist_head *hashent = ucounts_hashentry(ns, uid);
struct ucounts *ucounts, *new;
- spin_lock(&ucounts_lock);
+ spin_lock_irq(&ucounts_lock);
ucounts = find_ucounts(ns, uid, hashent);
if (!ucounts) {
- spin_unlock(&ucounts_lock);
+ spin_unlock_irq(&ucounts_lock);
new = kzalloc(sizeof(*new), GFP_KERNEL);
if (!new)
@@ -141,7 +141,7 @@
new->uid = uid;
atomic_set(&new->count, 0);
- spin_lock(&ucounts_lock);
+ spin_lock_irq(&ucounts_lock);
ucounts = find_ucounts(ns, uid, hashent);
if (ucounts) {
kfree(new);
@@ -152,16 +152,18 @@
}
if (!atomic_add_unless(&ucounts->count, 1, INT_MAX))
ucounts = NULL;
- spin_unlock(&ucounts_lock);
+ spin_unlock_irq(&ucounts_lock);
return ucounts;
}
static void put_ucounts(struct ucounts *ucounts)
{
+ unsigned long flags;
+
if (atomic_dec_and_test(&ucounts->count)) {
- spin_lock(&ucounts_lock);
+ spin_lock_irqsave(&ucounts_lock, flags);
hlist_del_init(&ucounts->node);
- spin_unlock(&ucounts_lock);
+ spin_unlock_irqrestore(&ucounts_lock, flags);
kfree(ucounts);
}
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index f3631a3..bf9885e 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -421,7 +421,6 @@
*/
if (is_hardlockup()) {
int this_cpu = smp_processor_id();
- struct pt_regs *regs = get_irq_regs();
/* only print hardlockups once */
if (__this_cpu_read(hard_watchdog_warn) == true)
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index f2bd21b..efb0b4d 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -678,43 +678,50 @@
}
EXPORT_SYMBOL(iov_iter_copy_from_user_atomic);
-static void pipe_advance(struct iov_iter *i, size_t size)
+static inline void pipe_truncate(struct iov_iter *i)
{
struct pipe_inode_info *pipe = i->pipe;
- struct pipe_buffer *buf;
- int idx = i->idx;
- size_t off = i->iov_offset, orig_sz;
-
- if (unlikely(i->count < size))
- size = i->count;
- orig_sz = size;
-
- if (size) {
- if (off) /* make it relative to the beginning of buffer */
- size += off - pipe->bufs[idx].offset;
- while (1) {
- buf = &pipe->bufs[idx];
- if (size <= buf->len)
- break;
- size -= buf->len;
- idx = next_idx(idx, pipe);
- }
- buf->len = size;
- i->idx = idx;
- off = i->iov_offset = buf->offset + size;
- }
- if (off)
- idx = next_idx(idx, pipe);
if (pipe->nrbufs) {
- int unused = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1);
- /* [curbuf,unused) is in use. Free [idx,unused) */
- while (idx != unused) {
+ size_t off = i->iov_offset;
+ int idx = i->idx;
+ int nrbufs = (idx - pipe->curbuf) & (pipe->buffers - 1);
+ if (off) {
+ pipe->bufs[idx].len = off - pipe->bufs[idx].offset;
+ idx = next_idx(idx, pipe);
+ nrbufs++;
+ }
+ while (pipe->nrbufs > nrbufs) {
pipe_buf_release(pipe, &pipe->bufs[idx]);
idx = next_idx(idx, pipe);
pipe->nrbufs--;
}
}
- i->count -= orig_sz;
+}
+
+static void pipe_advance(struct iov_iter *i, size_t size)
+{
+ struct pipe_inode_info *pipe = i->pipe;
+ if (unlikely(i->count < size))
+ size = i->count;
+ if (size) {
+ struct pipe_buffer *buf;
+ size_t off = i->iov_offset, left = size;
+ int idx = i->idx;
+ if (off) /* make it relative to the beginning of buffer */
+ left += off - pipe->bufs[idx].offset;
+ while (1) {
+ buf = &pipe->bufs[idx];
+ if (left <= buf->len)
+ break;
+ left -= buf->len;
+ idx = next_idx(idx, pipe);
+ }
+ i->idx = idx;
+ i->iov_offset = buf->offset + left;
+ }
+ i->count -= size;
+ /* ... and discard everything past that point */
+ pipe_truncate(i);
}
void iov_iter_advance(struct iov_iter *i, size_t size)
@@ -774,6 +781,7 @@
size_t count)
{
BUG_ON(direction != ITER_PIPE);
+ WARN_ON(pipe->nrbufs == pipe->buffers);
i->type = direction;
i->pipe = pipe;
i->idx = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1);
diff --git a/lib/swiotlb.c b/lib/swiotlb.c
index 22e13a0..ad1d296 100644
--- a/lib/swiotlb.c
+++ b/lib/swiotlb.c
@@ -53,7 +53,7 @@
*/
#define IO_TLB_MIN_SLABS ((1<<20) >> IO_TLB_SHIFT)
-int swiotlb_force;
+enum swiotlb_force swiotlb_force;
/*
* Used to do a quick range check in swiotlb_tbl_unmap_single and
@@ -106,8 +106,12 @@
}
if (*str == ',')
++str;
- if (!strcmp(str, "force"))
- swiotlb_force = 1;
+ if (!strcmp(str, "force")) {
+ swiotlb_force = SWIOTLB_FORCE;
+ } else if (!strcmp(str, "noforce")) {
+ swiotlb_force = SWIOTLB_NO_FORCE;
+ io_tlb_nslabs = 1;
+ }
return 0;
}
@@ -541,8 +545,15 @@
map_single(struct device *hwdev, phys_addr_t phys, size_t size,
enum dma_data_direction dir)
{
- dma_addr_t start_dma_addr = phys_to_dma(hwdev, io_tlb_start);
+ dma_addr_t start_dma_addr;
+ if (swiotlb_force == SWIOTLB_NO_FORCE) {
+ dev_warn_ratelimited(hwdev, "Cannot do DMA to address %pa\n",
+ &phys);
+ return SWIOTLB_MAP_ERROR;
+ }
+
+ start_dma_addr = phys_to_dma(hwdev, io_tlb_start);
return swiotlb_tbl_map_single(hwdev, start_dma_addr, phys, size, dir);
}
@@ -707,6 +718,9 @@
swiotlb_full(struct device *dev, size_t size, enum dma_data_direction dir,
int do_panic)
{
+ if (swiotlb_force == SWIOTLB_NO_FORCE)
+ return;
+
/*
* Ran out of IOMMU space for this operation. This is very bad.
* Unfortunately the drivers cannot handle this operation properly.
@@ -749,7 +763,7 @@
* we can safely return the device addr and not worry about bounce
* buffering it.
*/
- if (dma_capable(dev, dev_addr, size) && !swiotlb_force)
+ if (dma_capable(dev, dev_addr, size) && swiotlb_force != SWIOTLB_FORCE)
return dev_addr;
trace_swiotlb_bounced(dev, dev_addr, size, swiotlb_force);
@@ -888,7 +902,7 @@
phys_addr_t paddr = sg_phys(sg);
dma_addr_t dev_addr = phys_to_dma(hwdev, paddr);
- if (swiotlb_force ||
+ if (swiotlb_force == SWIOTLB_FORCE ||
!dma_capable(hwdev, dev_addr, sg->length)) {
phys_addr_t map = map_single(hwdev, sg_phys(sg),
sg->length, dir);
diff --git a/mm/compaction.c b/mm/compaction.c
index 0409a4a..70e6bec 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -634,22 +634,6 @@
return pfn;
}
-/* Update the number of anon and file isolated pages in the zone */
-static void acct_isolated(struct zone *zone, struct compact_control *cc)
-{
- struct page *page;
- unsigned int count[2] = { 0, };
-
- if (list_empty(&cc->migratepages))
- return;
-
- list_for_each_entry(page, &cc->migratepages, lru)
- count[!!page_is_file_cache(page)]++;
-
- mod_node_page_state(zone->zone_pgdat, NR_ISOLATED_ANON, count[0]);
- mod_node_page_state(zone->zone_pgdat, NR_ISOLATED_FILE, count[1]);
-}
-
/* Similar to reclaim, but different enough that they don't share logic */
static bool too_many_isolated(struct zone *zone)
{
@@ -866,6 +850,8 @@
/* Successfully isolated */
del_page_from_lru_list(page, lruvec, page_lru(page));
+ inc_node_page_state(page,
+ NR_ISOLATED_ANON + page_is_file_cache(page));
isolate_success:
list_add(&page->lru, &cc->migratepages);
@@ -902,7 +888,6 @@
spin_unlock_irqrestore(zone_lru_lock(zone), flags);
locked = false;
}
- acct_isolated(zone, cc);
putback_movable_pages(&cc->migratepages);
cc->nr_migratepages = 0;
cc->last_migrated_pfn = 0;
@@ -988,7 +973,6 @@
if (cc->nr_migratepages == COMPACT_CLUSTER_MAX)
break;
}
- acct_isolated(cc->zone, cc);
return pfn;
}
@@ -1258,10 +1242,8 @@
low_pfn = isolate_migratepages_block(cc, low_pfn,
block_end_pfn, isolate_mode);
- if (!low_pfn || cc->contended) {
- acct_isolated(zone, cc);
+ if (!low_pfn || cc->contended)
return ISOLATE_ABORT;
- }
/*
* Either we isolated something and proceed with migration. Or
@@ -1271,7 +1253,6 @@
break;
}
- acct_isolated(zone, cc);
/* Record where migration scanner will be restarted. */
cc->migrate_pfn = low_pfn;
diff --git a/mm/filemap.c b/mm/filemap.c
index 50b52fe..d8d7df8 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -144,7 +144,7 @@
workingset_node_pages_dec(node);
/* Wakeup waiters for exceptional entry lock */
dax_wake_mapping_entry_waiter(mapping, page->index,
- false);
+ true);
}
}
radix_tree_replace_slot(slot, page);
@@ -1686,7 +1686,7 @@
int error = 0;
if (unlikely(*ppos >= inode->i_sb->s_maxbytes))
- return -EINVAL;
+ return 0;
iov_iter_truncate(iter, inode->i_sb->s_maxbytes);
index = *ppos >> PAGE_SHIFT;
@@ -1703,6 +1703,11 @@
cond_resched();
find_page:
+ if (fatal_signal_pending(current)) {
+ error = -EINTR;
+ goto out;
+ }
+
page = find_get_page(mapping, index);
if (!page) {
page_cache_sync_readahead(mapping,
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index d4a6e40..917555c 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -772,6 +772,12 @@
assert_spin_locked(pmd_lockptr(mm, pmd));
+ /*
+ * When we COW a devmap PMD entry, we split it into PTEs, so we should
+ * not be in this function with `flags & FOLL_COW` set.
+ */
+ WARN_ONCE(flags & FOLL_COW, "mm: In follow_devmap_pmd with FOLL_COW set");
+
if (flags & FOLL_WRITE && !pmd_write(*pmd))
return NULL;
@@ -872,15 +878,17 @@
{
pmd_t entry;
unsigned long haddr;
+ bool write = fe->flags & FAULT_FLAG_WRITE;
fe->ptl = pmd_lock(fe->vma->vm_mm, fe->pmd);
if (unlikely(!pmd_same(*fe->pmd, orig_pmd)))
goto unlock;
entry = pmd_mkyoung(orig_pmd);
+ if (write)
+ entry = pmd_mkdirty(entry);
haddr = fe->address & HPAGE_PMD_MASK;
- if (pmdp_set_access_flags(fe->vma, haddr, fe->pmd, entry,
- fe->flags & FAULT_FLAG_WRITE))
+ if (pmdp_set_access_flags(fe->vma, haddr, fe->pmd, entry, write))
update_mmu_cache_pmd(fe->vma, fe->address, fe->pmd);
unlock:
@@ -1116,6 +1124,16 @@
return ret;
}
+/*
+ * FOLL_FORCE can write to even unwritable pmd's, but only
+ * after we've gone through a COW cycle and they are dirty.
+ */
+static inline bool can_follow_write_pmd(pmd_t pmd, unsigned int flags)
+{
+ return pmd_write(pmd) ||
+ ((flags & FOLL_FORCE) && (flags & FOLL_COW) && pmd_dirty(pmd));
+}
+
struct page *follow_trans_huge_pmd(struct vm_area_struct *vma,
unsigned long addr,
pmd_t *pmd,
@@ -1126,7 +1144,7 @@
assert_spin_locked(pmd_lockptr(mm, pmd));
- if (flags & FOLL_WRITE && !pmd_write(*pmd))
+ if (flags & FOLL_WRITE && !can_follow_write_pmd(*pmd, flags))
goto out;
/* Avoid dumping huge zero page */
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 418bf01..b6adedb 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1773,23 +1773,32 @@
}
/*
- * When releasing a hugetlb pool reservation, any surplus pages that were
- * allocated to satisfy the reservation must be explicitly freed if they were
- * never used.
- * Called with hugetlb_lock held.
+ * This routine has two main purposes:
+ * 1) Decrement the reservation count (resv_huge_pages) by the value passed
+ * in unused_resv_pages. This corresponds to the prior adjustments made
+ * to the associated reservation map.
+ * 2) Free any unused surplus pages that may have been allocated to satisfy
+ * the reservation. As many as unused_resv_pages may be freed.
+ *
+ * Called with hugetlb_lock held. However, the lock could be dropped (and
+ * reacquired) during calls to cond_resched_lock. Whenever dropping the lock,
+ * we must make sure nobody else can claim pages we are in the process of
+ * freeing. Do this by ensuring resv_huge_page always is greater than the
+ * number of huge pages we plan to free when dropping the lock.
*/
static void return_unused_surplus_pages(struct hstate *h,
unsigned long unused_resv_pages)
{
unsigned long nr_pages;
- /* Uncommit the reservation */
- h->resv_huge_pages -= unused_resv_pages;
-
/* Cannot return gigantic pages currently */
if (hstate_is_gigantic(h))
- return;
+ goto out;
+ /*
+ * Part (or even all) of the reservation could have been backed
+ * by pre-allocated pages. Only free surplus pages.
+ */
nr_pages = min(unused_resv_pages, h->surplus_huge_pages);
/*
@@ -1799,12 +1808,22 @@
* when the nodes with surplus pages have no free pages.
* free_pool_huge_page() will balance the the freed pages across the
* on-line nodes with memory and will handle the hstate accounting.
+ *
+ * Note that we decrement resv_huge_pages as we free the pages. If
+ * we drop the lock, resv_huge_pages will still be sufficiently large
+ * to cover subsequent pages we may free.
*/
while (nr_pages--) {
+ h->resv_huge_pages--;
+ unused_resv_pages--;
if (!free_pool_huge_page(h, &node_states[N_MEMORY], 1))
- break;
+ goto out;
cond_resched_lock(&hugetlb_lock);
}
+
+out:
+ /* Fully uncommit the reservation */
+ h->resv_huge_pages -= unused_resv_pages;
}
@@ -3450,15 +3469,17 @@
* Keep the pte_same checks anyway to make transition from the mutex easier.
*/
static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma,
- unsigned long address, pte_t *ptep, pte_t pte,
- struct page *pagecache_page, spinlock_t *ptl)
+ unsigned long address, pte_t *ptep,
+ struct page *pagecache_page, spinlock_t *ptl)
{
+ pte_t pte;
struct hstate *h = hstate_vma(vma);
struct page *old_page, *new_page;
int ret = 0, outside_reserve = 0;
unsigned long mmun_start; /* For mmu_notifiers */
unsigned long mmun_end; /* For mmu_notifiers */
+ pte = huge_ptep_get(ptep);
old_page = pte_page(pte);
retry_avoidcopy:
@@ -3733,7 +3754,7 @@
hugetlb_count_add(pages_per_huge_page(h), mm);
if ((flags & FAULT_FLAG_WRITE) && !(vma->vm_flags & VM_SHARED)) {
/* Optimization, do the COW without a second fault */
- ret = hugetlb_cow(mm, vma, address, ptep, new_pte, page, ptl);
+ ret = hugetlb_cow(mm, vma, address, ptep, page, ptl);
}
spin_unlock(ptl);
@@ -3888,8 +3909,8 @@
if (flags & FAULT_FLAG_WRITE) {
if (!huge_pte_write(entry)) {
- ret = hugetlb_cow(mm, vma, address, ptep, entry,
- pagecache_page, ptl);
+ ret = hugetlb_cow(mm, vma, address, ptep,
+ pagecache_page, ptl);
goto out_put_page;
}
entry = huge_pte_mkdirty(entry);
diff --git a/mm/init-mm.c b/mm/init-mm.c
index a56a851..975e49f 100644
--- a/mm/init-mm.c
+++ b/mm/init-mm.c
@@ -6,6 +6,7 @@
#include <linux/cpumask.h>
#include <linux/atomic.h>
+#include <linux/user_namespace.h>
#include <asm/pgtable.h>
#include <asm/mmu.h>
@@ -21,5 +22,6 @@
.mmap_sem = __RWSEM_INITIALIZER(init_mm.mmap_sem),
.page_table_lock = __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock),
.mmlist = LIST_HEAD_INIT(init_mm.mmlist),
+ .user_ns = &init_user_ns,
INIT_MM_CONTEXT(init_mm)
};
diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index 87e1a7ca..5d7c006 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -1403,6 +1403,9 @@
spin_lock_irq(&mapping->tree_lock);
+ slot = radix_tree_lookup_slot(&mapping->page_tree, index);
+ VM_BUG_ON_PAGE(page != radix_tree_deref_slot_protected(slot,
+ &mapping->tree_lock), page);
VM_BUG_ON_PAGE(page_mapped(page), page);
/*
@@ -1426,6 +1429,7 @@
radix_tree_replace_slot(slot,
new_page + (index % HPAGE_PMD_NR));
+ slot = radix_tree_iter_next(&iter);
index++;
continue;
out_lru:
@@ -1521,9 +1525,11 @@
if (!page || iter.index < page->index) {
if (!nr_none)
break;
- /* Put holes back where they were */
- radix_tree_replace_slot(slot, NULL);
nr_none--;
+ /* Put holes back where they were */
+ radix_tree_delete(&mapping->page_tree,
+ iter.index);
+ slot = radix_tree_iter_next(&iter);
continue;
}
@@ -1537,6 +1543,7 @@
putback_lru_page(page);
unlock_page(page);
spin_lock_irq(&mapping->tree_lock);
+ slot = radix_tree_iter_next(&iter);
}
VM_BUG_ON(nr_none);
spin_unlock_irq(&mapping->tree_lock);
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index f60bd04..fc59ffb 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -625,8 +625,8 @@
unsigned long mem_cgroup_node_nr_lru_pages(struct mem_cgroup *memcg,
int nid, unsigned int lru_mask)
{
+ struct lruvec *lruvec = mem_cgroup_lruvec(NODE_DATA(nid), memcg);
unsigned long nr = 0;
- struct mem_cgroup_per_node *mz;
enum lru_list lru;
VM_BUG_ON((unsigned)nid >= nr_node_ids);
@@ -634,8 +634,7 @@
for_each_lru(lru) {
if (!(BIT(lru) & lru_mask))
continue;
- mz = mem_cgroup_nodeinfo(memcg, nid);
- nr += mz->lru_size[lru];
+ nr += mem_cgroup_get_lru_size(lruvec, lru);
}
return nr;
}
@@ -1002,6 +1001,7 @@
* mem_cgroup_update_lru_size - account for adding or removing an lru page
* @lruvec: mem_cgroup per zone lru vector
* @lru: index of lru list the page is sitting on
+ * @zid: zone id of the accounted pages
* @nr_pages: positive when adding or negative when removing
*
* This function must be called under lru_lock, just before a page is added
@@ -1009,27 +1009,25 @@
* so as to allow it to check that lru_size 0 is consistent with list_empty).
*/
void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,
- int nr_pages)
+ int zid, int nr_pages)
{
struct mem_cgroup_per_node *mz;
unsigned long *lru_size;
long size;
- bool empty;
if (mem_cgroup_disabled())
return;
mz = container_of(lruvec, struct mem_cgroup_per_node, lruvec);
- lru_size = mz->lru_size + lru;
- empty = list_empty(lruvec->lists + lru);
+ lru_size = &mz->lru_zone_size[zid][lru];
if (nr_pages < 0)
*lru_size += nr_pages;
size = *lru_size;
- if (WARN_ONCE(size < 0 || empty != !size,
- "%s(%p, %d, %d): lru_size %ld but %sempty\n",
- __func__, lruvec, lru, nr_pages, size, empty ? "" : "not ")) {
+ if (WARN_ONCE(size < 0,
+ "%s(%p, %d, %d): lru_size %ld\n",
+ __func__, lruvec, lru, nr_pages, size)) {
VM_BUG_ON(1);
*lru_size = 0;
}
@@ -4362,9 +4360,9 @@
return ret;
}
- /* Try charges one by one with reclaim */
+ /* Try charges one by one with reclaim, but do not retry */
while (count--) {
- ret = try_charge(mc.to, GFP_KERNEL & ~__GFP_NORETRY, 1);
+ ret = try_charge(mc.to, GFP_KERNEL | __GFP_NORETRY, 1);
if (ret)
return ret;
mc.precharge++;
diff --git a/mm/memory.c b/mm/memory.c
index e18c57b..cbb1e5e 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3868,7 +3868,7 @@
* Access another process' address space as given in mm. If non-NULL, use the
* given task for page fault accounting.
*/
-static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
+int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
unsigned long addr, void *buf, int len, unsigned int gup_flags)
{
struct vm_area_struct *vma;
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index cad4b91..ede13734 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1033,36 +1033,39 @@
node_set_state(node, N_MEMORY);
}
-int zone_can_shift(unsigned long pfn, unsigned long nr_pages,
- enum zone_type target)
+bool zone_can_shift(unsigned long pfn, unsigned long nr_pages,
+ enum zone_type target, int *zone_shift)
{
struct zone *zone = page_zone(pfn_to_page(pfn));
enum zone_type idx = zone_idx(zone);
int i;
+ *zone_shift = 0;
+
if (idx < target) {
/* pages must be at end of current zone */
if (pfn + nr_pages != zone_end_pfn(zone))
- return 0;
+ return false;
/* no zones in use between current zone and target */
for (i = idx + 1; i < target; i++)
if (zone_is_initialized(zone - idx + i))
- return 0;
+ return false;
}
if (target < idx) {
/* pages must be at beginning of current zone */
if (pfn != zone->zone_start_pfn)
- return 0;
+ return false;
/* no zones in use between current zone and target */
for (i = target + 1; i < idx; i++)
if (zone_is_initialized(zone - idx + i))
- return 0;
+ return false;
}
- return target - idx;
+ *zone_shift = target - idx;
+ return true;
}
/* Must be protected by mem_hotplug_begin() */
@@ -1089,10 +1092,13 @@
!can_online_high_movable(zone))
return -EINVAL;
- if (online_type == MMOP_ONLINE_KERNEL)
- zone_shift = zone_can_shift(pfn, nr_pages, ZONE_NORMAL);
- else if (online_type == MMOP_ONLINE_MOVABLE)
- zone_shift = zone_can_shift(pfn, nr_pages, ZONE_MOVABLE);
+ if (online_type == MMOP_ONLINE_KERNEL) {
+ if (!zone_can_shift(pfn, nr_pages, ZONE_NORMAL, &zone_shift))
+ return -EINVAL;
+ } else if (online_type == MMOP_ONLINE_MOVABLE) {
+ if (!zone_can_shift(pfn, nr_pages, ZONE_MOVABLE, &zone_shift))
+ return -EINVAL;
+ }
zone = move_pfn_range(zone_shift, pfn, pfn + nr_pages);
if (!zone)
@@ -1477,17 +1483,20 @@
}
/*
- * Confirm all pages in a range [start, end) is belongs to the same zone.
+ * Confirm all pages in a range [start, end) belong to the same zone.
+ * When true, return its valid [start, end).
*/
-int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn)
+int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn,
+ unsigned long *valid_start, unsigned long *valid_end)
{
unsigned long pfn, sec_end_pfn;
+ unsigned long start, end;
struct zone *zone = NULL;
struct page *page;
int i;
- for (pfn = start_pfn, sec_end_pfn = SECTION_ALIGN_UP(start_pfn);
+ for (pfn = start_pfn, sec_end_pfn = SECTION_ALIGN_UP(start_pfn + 1);
pfn < end_pfn;
- pfn = sec_end_pfn + 1, sec_end_pfn += PAGES_PER_SECTION) {
+ pfn = sec_end_pfn, sec_end_pfn += PAGES_PER_SECTION) {
/* Make sure the memory section is present first */
if (!present_section_nr(pfn_to_section_nr(pfn)))
continue;
@@ -1503,10 +1512,20 @@
page = pfn_to_page(pfn + i);
if (zone && page_zone(page) != zone)
return 0;
+ if (!zone)
+ start = pfn + i;
zone = page_zone(page);
+ end = pfn + MAX_ORDER_NR_PAGES;
}
}
- return 1;
+
+ if (zone) {
+ *valid_start = start;
+ *valid_end = end;
+ return 1;
+ } else {
+ return 0;
+ }
}
/*
@@ -1853,6 +1872,7 @@
long offlined_pages;
int ret, drain, retry_max, node;
unsigned long flags;
+ unsigned long valid_start, valid_end;
struct zone *zone;
struct memory_notify arg;
@@ -1863,10 +1883,10 @@
return -EINVAL;
/* This makes hotplug much easier...and readable.
we assume this for now. .*/
- if (!test_pages_in_a_zone(start_pfn, end_pfn))
+ if (!test_pages_in_a_zone(start_pfn, end_pfn, &valid_start, &valid_end))
return -EINVAL;
- zone = page_zone(pfn_to_page(start_pfn));
+ zone = page_zone(pfn_to_page(valid_start));
node = zone_to_nid(zone);
nr_pages = end_pfn - start_pfn;
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 238c4e8..0adc6f9 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -2024,8 +2024,8 @@
nmask = policy_nodemask(gfp, pol);
zl = policy_zonelist(gfp, pol, node);
- mpol_cond_put(pol);
page = __alloc_pages_nodemask(gfp, order, zl, nmask);
+ mpol_cond_put(pol);
out:
if (unlikely(!page && read_mems_allowed_retry(cpuset_mems_cookie)))
goto retry_cpuset;
diff --git a/mm/migrate.c b/mm/migrate.c
index 99250ae..66ce6b4 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -168,8 +168,6 @@
continue;
}
list_del(&page->lru);
- dec_node_page_state(page, NR_ISOLATED_ANON +
- page_is_file_cache(page));
/*
* We isolated non-lru movable page so here we can use
* __PageMovable because LRU page's mapping cannot have
@@ -186,6 +184,8 @@
put_page(page);
} else {
putback_lru_page(page);
+ dec_node_page_state(page, NR_ISOLATED_ANON +
+ page_is_file_cache(page));
}
}
}
@@ -1121,8 +1121,15 @@
* restored.
*/
list_del(&page->lru);
- dec_node_page_state(page, NR_ISOLATED_ANON +
- page_is_file_cache(page));
+
+ /*
+ * Compaction can migrate also non-LRU pages which are
+ * not accounted to NR_ISOLATED_*. They can be recognized
+ * as __PageMovable
+ */
+ if (likely(!__PageMovable(page)))
+ dec_node_page_state(page, NR_ISOLATED_ANON +
+ page_is_file_cache(page));
}
/*
diff --git a/mm/nommu.c b/mm/nommu.c
index 8b8faaf..44265e0 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -1808,7 +1808,7 @@
}
EXPORT_SYMBOL(filemap_map_pages);
-static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
+int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
unsigned long addr, void *buf, int len, unsigned int gup_flags)
{
struct vm_area_struct *vma;
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 8702d66..04b1c95 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -2192,7 +2192,7 @@
unsigned long count, struct list_head *list,
int migratetype, bool cold)
{
- int i;
+ int i, alloced = 0;
spin_lock(&zone->lock);
for (i = 0; i < count; ++i) {
@@ -2217,13 +2217,21 @@
else
list_add_tail(&page->lru, list);
list = &page->lru;
+ alloced++;
if (is_migrate_cma(get_pcppage_migratetype(page)))
__mod_zone_page_state(zone, NR_FREE_CMA_PAGES,
-(1 << order));
}
+
+ /*
+ * i pages were removed from the buddy list even if some leak due
+ * to check_pcp_refill failing so adjust NR_FREE_PAGES based
+ * on i. Do not confuse with 'alloced' which is the number of
+ * pages added to the pcp list.
+ */
__mod_zone_page_state(zone, NR_FREE_PAGES, -(i << order));
spin_unlock(&zone->lock);
- return i;
+ return alloced;
}
#ifdef CONFIG_NUMA
@@ -3494,12 +3502,13 @@
struct page *page = NULL;
unsigned int alloc_flags;
unsigned long did_some_progress;
- enum compact_priority compact_priority = DEF_COMPACT_PRIORITY;
+ enum compact_priority compact_priority;
enum compact_result compact_result;
- int compaction_retries = 0;
- int no_progress_loops = 0;
+ int compaction_retries;
+ int no_progress_loops;
unsigned long alloc_start = jiffies;
unsigned int stall_timeout = 10 * HZ;
+ unsigned int cpuset_mems_cookie;
/*
* In the slowpath, we sanity check order to avoid ever trying to
@@ -3520,6 +3529,23 @@
(__GFP_ATOMIC|__GFP_DIRECT_RECLAIM)))
gfp_mask &= ~__GFP_ATOMIC;
+retry_cpuset:
+ compaction_retries = 0;
+ no_progress_loops = 0;
+ compact_priority = DEF_COMPACT_PRIORITY;
+ cpuset_mems_cookie = read_mems_allowed_begin();
+ /*
+ * We need to recalculate the starting point for the zonelist iterator
+ * because we might have used different nodemask in the fast path, or
+ * there was a cpuset modification and we are retrying - otherwise we
+ * could end up iterating over non-eligible zones endlessly.
+ */
+ ac->preferred_zoneref = first_zones_zonelist(ac->zonelist,
+ ac->high_zoneidx, ac->nodemask);
+ if (!ac->preferred_zoneref->zone)
+ goto nopage;
+
+
/*
* The fast path uses conservative alloc_flags to succeed only until
* kswapd needs to be woken up, and to avoid the cost of setting up
@@ -3679,6 +3705,13 @@
&compaction_retries))
goto retry;
+ /*
+ * It's possible we raced with cpuset update so the OOM would be
+ * premature (see below the nopage: label for full explanation).
+ */
+ if (read_mems_allowed_retry(cpuset_mems_cookie))
+ goto retry_cpuset;
+
/* Reclaim has failed us, start killing things */
page = __alloc_pages_may_oom(gfp_mask, order, ac, &did_some_progress);
if (page)
@@ -3691,6 +3724,16 @@
}
nopage:
+ /*
+ * When updating a task's mems_allowed or mempolicy nodemask, it is
+ * possible to race with parallel threads in such a way that our
+ * allocation can fail while the mask is being updated. If we are about
+ * to fail, check if the cpuset changed during allocation and if so,
+ * retry.
+ */
+ if (read_mems_allowed_retry(cpuset_mems_cookie))
+ goto retry_cpuset;
+
warn_alloc(gfp_mask,
"page allocation failure: order:%u", order);
got_pg:
@@ -3705,7 +3748,6 @@
struct zonelist *zonelist, nodemask_t *nodemask)
{
struct page *page;
- unsigned int cpuset_mems_cookie;
unsigned int alloc_flags = ALLOC_WMARK_LOW;
gfp_t alloc_mask = gfp_mask; /* The gfp_t that was actually used for allocation */
struct alloc_context ac = {
@@ -3742,9 +3784,6 @@
if (IS_ENABLED(CONFIG_CMA) && ac.migratetype == MIGRATE_MOVABLE)
alloc_flags |= ALLOC_CMA;
-retry_cpuset:
- cpuset_mems_cookie = read_mems_allowed_begin();
-
/* Dirty zone balancing only done in the fast path */
ac.spread_dirty_pages = (gfp_mask & __GFP_WRITE);
@@ -3755,8 +3794,13 @@
*/
ac.preferred_zoneref = first_zones_zonelist(ac.zonelist,
ac.high_zoneidx, ac.nodemask);
- if (!ac.preferred_zoneref) {
+ if (!ac.preferred_zoneref->zone) {
page = NULL;
+ /*
+ * This might be due to race with cpuset_current_mems_allowed
+ * update, so make sure we retry with original nodemask in the
+ * slow path.
+ */
goto no_zone;
}
@@ -3765,6 +3809,7 @@
if (likely(page))
goto out;
+no_zone:
/*
* Runtime PM, block IO and its error handling path can deadlock
* because I/O on the device might not complete.
@@ -3776,21 +3821,10 @@
* Restore the original nodemask if it was potentially replaced with
* &cpuset_current_mems_allowed to optimize the fast-path attempt.
*/
- if (cpusets_enabled())
+ if (unlikely(ac.nodemask != nodemask))
ac.nodemask = nodemask;
- page = __alloc_pages_slowpath(alloc_mask, order, &ac);
-no_zone:
- /*
- * When updating a task's mems_allowed, it is possible to race with
- * parallel threads in such a way that an allocation can fail while
- * the mask is being updated. If a page allocation is about to fail,
- * check if the cpuset changed during allocation and if so, retry.
- */
- if (unlikely(!page && read_mems_allowed_retry(cpuset_mems_cookie))) {
- alloc_mask = gfp_mask;
- goto retry_cpuset;
- }
+ page = __alloc_pages_slowpath(alloc_mask, order, &ac);
out:
if (memcg_kmem_enabled() && (gfp_mask & __GFP_ACCOUNT) && page &&
diff --git a/mm/slab.c b/mm/slab.c
index 0b0550c..bd878f0 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -2475,7 +2475,6 @@
unsigned int pos;
unsigned int *list;
unsigned int count;
- unsigned int rand;
};
struct rnd_state rnd_state;
};
@@ -2501,8 +2500,7 @@
} else {
state->list = cachep->random_seq;
state->count = count;
- state->pos = 0;
- state->rand = rand;
+ state->pos = rand % count;
ret = true;
}
return ret;
@@ -2511,7 +2509,9 @@
/* Get the next entry on the list and randomize it using a random shift */
static freelist_idx_t next_random_slot(union freelist_init_state *state)
{
- return (state->list[state->pos++] + state->rand) % state->count;
+ if (state->pos >= state->count)
+ state->pos = 0;
+ return state->list[state->pos++];
}
/* Swap two freelist entries */
diff --git a/mm/swapfile.c b/mm/swapfile.c
index f304389..d76b2a1 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -943,11 +943,25 @@
count = page_trans_huge_mapcount(page, total_mapcount);
if (count <= 1 && PageSwapCache(page)) {
count += page_swapcount(page);
- if (count == 1 && !PageWriteback(page)) {
+ if (count != 1)
+ goto out;
+ if (!PageWriteback(page)) {
delete_from_swap_cache(page);
SetPageDirty(page);
+ } else {
+ swp_entry_t entry;
+ struct swap_info_struct *p;
+
+ entry.val = page_private(page);
+ p = swap_info_get(entry);
+ if (p->flags & SWP_STABLE_WRITES) {
+ spin_unlock(&p->lock);
+ return false;
+ }
+ spin_unlock(&p->lock);
}
}
+out:
return count <= 1;
}
@@ -2449,6 +2463,10 @@
error = -ENOMEM;
goto bad_swap;
}
+
+ if (bdi_cap_stable_pages_required(inode_to_bdi(inode)))
+ p->flags |= SWP_STABLE_WRITES;
+
if (p->bdev && blk_queue_nonrot(bdev_get_queue(p->bdev))) {
int cpu;
diff --git a/mm/vmscan.c b/mm/vmscan.c
index d75cdf3..fa30010 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -242,6 +242,16 @@
return node_page_state(lruvec_pgdat(lruvec), NR_LRU_BASE + lru);
}
+unsigned long lruvec_zone_lru_size(struct lruvec *lruvec, enum lru_list lru,
+ int zone_idx)
+{
+ if (!mem_cgroup_disabled())
+ return mem_cgroup_get_zone_lru_size(lruvec, lru, zone_idx);
+
+ return zone_page_state(&lruvec_pgdat(lruvec)->node_zones[zone_idx],
+ NR_ZONE_LRU_BASE + lru);
+}
+
/*
* Add a shrinker callback to be called from the vm.
*/
@@ -291,6 +301,7 @@
int nid = shrinkctl->nid;
long batch_size = shrinker->batch ? shrinker->batch
: SHRINK_BATCH;
+ long scanned = 0, next_deferred;
freeable = shrinker->count_objects(shrinker, shrinkctl);
if (freeable == 0)
@@ -312,7 +323,9 @@
pr_err("shrink_slab: %pF negative objects to delete nr=%ld\n",
shrinker->scan_objects, total_scan);
total_scan = freeable;
- }
+ next_deferred = nr;
+ } else
+ next_deferred = total_scan;
/*
* We need to avoid excessive windup on filesystem shrinkers
@@ -369,17 +382,22 @@
count_vm_events(SLABS_SCANNED, nr_to_scan);
total_scan -= nr_to_scan;
+ scanned += nr_to_scan;
cond_resched();
}
+ if (next_deferred >= scanned)
+ next_deferred -= scanned;
+ else
+ next_deferred = 0;
/*
* move the unused scan count back into the shrinker in a
* manner that handles concurrent updates. If we exhausted the
* scan, there is no need to do an update.
*/
- if (total_scan > 0)
- new_nr = atomic_long_add_return(total_scan,
+ if (next_deferred > 0)
+ new_nr = atomic_long_add_return(next_deferred,
&shrinker->nr_deferred[nid]);
else
new_nr = atomic_long_read(&shrinker->nr_deferred[nid]);
@@ -1374,8 +1392,7 @@
* be complete before mem_cgroup_update_lru_size due to a santity check.
*/
static __always_inline void update_lru_sizes(struct lruvec *lruvec,
- enum lru_list lru, unsigned long *nr_zone_taken,
- unsigned long nr_taken)
+ enum lru_list lru, unsigned long *nr_zone_taken)
{
int zid;
@@ -1384,11 +1401,11 @@
continue;
__update_lru_size(lruvec, lru, zid, -nr_zone_taken[zid]);
+#ifdef CONFIG_MEMCG
+ mem_cgroup_update_lru_size(lruvec, lru, zid, -nr_zone_taken[zid]);
+#endif
}
-#ifdef CONFIG_MEMCG
- mem_cgroup_update_lru_size(lruvec, lru, -nr_taken);
-#endif
}
/*
@@ -1493,7 +1510,7 @@
*nr_scanned = scan;
trace_mm_vmscan_lru_isolate(sc->reclaim_idx, sc->order, nr_to_scan, scan,
nr_taken, mode, is_file_lru(lru));
- update_lru_sizes(lruvec, lru, nr_zone_taken, nr_taken);
+ update_lru_sizes(lruvec, lru, nr_zone_taken);
return nr_taken;
}
@@ -2039,10 +2056,8 @@
if (!managed_zone(zone))
continue;
- inactive_zone = zone_page_state(zone,
- NR_ZONE_LRU_BASE + (file * LRU_FILE));
- active_zone = zone_page_state(zone,
- NR_ZONE_LRU_BASE + (file * LRU_FILE) + LRU_ACTIVE);
+ inactive_zone = lruvec_zone_lru_size(lruvec, file * LRU_FILE, zid);
+ active_zone = lruvec_zone_lru_size(lruvec, (file * LRU_FILE) + LRU_ACTIVE, zid);
inactive -= min(inactive, inactive_zone);
active -= min(active, active_zone);
diff --git a/mm/zswap.c b/mm/zswap.c
index 275b22c..dbef278 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -78,7 +78,13 @@
/* Enable/disable zswap (disabled by default) */
static bool zswap_enabled;
-module_param_named(enabled, zswap_enabled, bool, 0644);
+static int zswap_enabled_param_set(const char *,
+ const struct kernel_param *);
+static struct kernel_param_ops zswap_enabled_param_ops = {
+ .set = zswap_enabled_param_set,
+ .get = param_get_bool,
+};
+module_param_cb(enabled, &zswap_enabled_param_ops, &zswap_enabled, 0644);
/* Crypto compressor to use */
#define ZSWAP_COMPRESSOR_DEFAULT "lzo"
@@ -176,6 +182,9 @@
/* used by param callback function */
static bool zswap_init_started;
+/* fatal error during init */
+static bool zswap_init_failed;
+
/*********************************
* helpers and fwd declarations
**********************************/
@@ -706,6 +715,11 @@
char *s = strstrip((char *)val);
int ret;
+ if (zswap_init_failed) {
+ pr_err("can't set param, initialization failed\n");
+ return -ENODEV;
+ }
+
/* no change required */
if (!strcmp(s, *(char **)kp->arg))
return 0;
@@ -785,6 +799,17 @@
return __zswap_param_set(val, kp, NULL, zswap_compressor);
}
+static int zswap_enabled_param_set(const char *val,
+ const struct kernel_param *kp)
+{
+ if (zswap_init_failed) {
+ pr_err("can't enable, initialization failed\n");
+ return -ENODEV;
+ }
+
+ return param_set_bool(val, kp);
+}
+
/*********************************
* writeback code
**********************************/
@@ -1271,6 +1296,9 @@
dstmem_fail:
zswap_entry_cache_destroy();
cache_fail:
+ /* if built-in, we aren't unloaded on failure; don't allow use */
+ zswap_init_failed = true;
+ zswap_enabled = false;
return -ENOMEM;
}
/* must be late so crypto has time to come up */
diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c
index 655a7d4..983f0b5 100644
--- a/net/ax25/ax25_subr.c
+++ b/net/ax25/ax25_subr.c
@@ -264,7 +264,7 @@
{
ax25_clear_queues(ax25);
- if (!sock_flag(ax25->sk, SOCK_DESTROY))
+ if (!ax25->sk || !sock_flag(ax25->sk, SOCK_DESTROY))
ax25_stop_heartbeat(ax25);
ax25_stop_t1timer(ax25);
ax25_stop_t2timer(ax25);
diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c
index 2fe9345..7fbdbae 100644
--- a/net/bridge/br_netfilter_hooks.c
+++ b/net/bridge/br_netfilter_hooks.c
@@ -399,7 +399,7 @@
br_nf_hook_thresh(NF_BR_PRE_ROUTING,
net, sk, skb, skb->dev,
NULL,
- br_nf_pre_routing_finish);
+ br_nf_pre_routing_finish_bridge);
return 0;
}
ether_addr_copy(eth_hdr(skb)->h_dest, dev->dev_addr);
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index e99037c..0474106 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -781,20 +781,6 @@
return 0;
}
-static int br_dev_newlink(struct net *src_net, struct net_device *dev,
- struct nlattr *tb[], struct nlattr *data[])
-{
- struct net_bridge *br = netdev_priv(dev);
-
- if (tb[IFLA_ADDRESS]) {
- spin_lock_bh(&br->lock);
- br_stp_change_bridge_id(br, nla_data(tb[IFLA_ADDRESS]));
- spin_unlock_bh(&br->lock);
- }
-
- return register_netdevice(dev);
-}
-
static int br_port_slave_changelink(struct net_device *brdev,
struct net_device *dev,
struct nlattr *tb[],
@@ -1093,6 +1079,25 @@
return 0;
}
+static int br_dev_newlink(struct net *src_net, struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[])
+{
+ struct net_bridge *br = netdev_priv(dev);
+ int err;
+
+ if (tb[IFLA_ADDRESS]) {
+ spin_lock_bh(&br->lock);
+ br_stp_change_bridge_id(br, nla_data(tb[IFLA_ADDRESS]));
+ spin_unlock_bh(&br->lock);
+ }
+
+ err = br_changelink(dev, tb, data);
+ if (err)
+ return err;
+
+ return register_netdevice(dev);
+}
+
static size_t br_get_size(const struct net_device *brdev)
{
return nla_total_size(sizeof(u32)) + /* IFLA_BR_FORWARD_DELAY */
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 436a753..5e9ed5e 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -734,14 +734,23 @@
static void bcm_remove_op(struct bcm_op *op)
{
- hrtimer_cancel(&op->timer);
- hrtimer_cancel(&op->thrtimer);
+ if (op->tsklet.func) {
+ while (test_bit(TASKLET_STATE_SCHED, &op->tsklet.state) ||
+ test_bit(TASKLET_STATE_RUN, &op->tsklet.state) ||
+ hrtimer_active(&op->timer)) {
+ hrtimer_cancel(&op->timer);
+ tasklet_kill(&op->tsklet);
+ }
+ }
- if (op->tsklet.func)
- tasklet_kill(&op->tsklet);
-
- if (op->thrtsklet.func)
- tasklet_kill(&op->thrtsklet);
+ if (op->thrtsklet.func) {
+ while (test_bit(TASKLET_STATE_SCHED, &op->thrtsklet.state) ||
+ test_bit(TASKLET_STATE_RUN, &op->thrtsklet.state) ||
+ hrtimer_active(&op->thrtimer)) {
+ hrtimer_cancel(&op->thrtimer);
+ tasklet_kill(&op->thrtsklet);
+ }
+ }
if ((op->frames) && (op->frames != &op->sframe))
kfree(op->frames);
diff --git a/net/ceph/auth_x.c b/net/ceph/auth_x.c
index a0905f0..b216131 100644
--- a/net/ceph/auth_x.c
+++ b/net/ceph/auth_x.c
@@ -39,56 +39,58 @@
return need != 0;
}
+static int ceph_x_encrypt_offset(void)
+{
+ return sizeof(u32) + sizeof(struct ceph_x_encrypt_header);
+}
+
static int ceph_x_encrypt_buflen(int ilen)
{
- return sizeof(struct ceph_x_encrypt_header) + ilen + 16 +
- sizeof(u32);
+ return ceph_x_encrypt_offset() + ilen + 16;
}
-static int ceph_x_encrypt(struct ceph_crypto_key *secret,
- void *ibuf, int ilen, void *obuf, size_t olen)
+static int ceph_x_encrypt(struct ceph_crypto_key *secret, void *buf,
+ int buf_len, int plaintext_len)
{
- struct ceph_x_encrypt_header head = {
- .struct_v = 1,
- .magic = cpu_to_le64(CEPHX_ENC_MAGIC)
- };
- size_t len = olen - sizeof(u32);
+ struct ceph_x_encrypt_header *hdr = buf + sizeof(u32);
+ int ciphertext_len;
int ret;
- ret = ceph_encrypt2(secret, obuf + sizeof(u32), &len,
- &head, sizeof(head), ibuf, ilen);
+ hdr->struct_v = 1;
+ hdr->magic = cpu_to_le64(CEPHX_ENC_MAGIC);
+
+ ret = ceph_crypt(secret, true, buf + sizeof(u32), buf_len - sizeof(u32),
+ plaintext_len + sizeof(struct ceph_x_encrypt_header),
+ &ciphertext_len);
if (ret)
return ret;
- ceph_encode_32(&obuf, len);
- return len + sizeof(u32);
+
+ ceph_encode_32(&buf, ciphertext_len);
+ return sizeof(u32) + ciphertext_len;
}
-static int ceph_x_decrypt(struct ceph_crypto_key *secret,
- void **p, void *end, void **obuf, size_t olen)
+static int ceph_x_decrypt(struct ceph_crypto_key *secret, void **p, void *end)
{
- struct ceph_x_encrypt_header head;
- size_t head_len = sizeof(head);
- int len, ret;
+ struct ceph_x_encrypt_header *hdr = *p + sizeof(u32);
+ int ciphertext_len, plaintext_len;
+ int ret;
- len = ceph_decode_32(p);
- if (*p + len > end)
- return -EINVAL;
+ ceph_decode_32_safe(p, end, ciphertext_len, e_inval);
+ ceph_decode_need(p, end, ciphertext_len, e_inval);
- dout("ceph_x_decrypt len %d\n", len);
- if (*obuf == NULL) {
- *obuf = kmalloc(len, GFP_NOFS);
- if (!*obuf)
- return -ENOMEM;
- olen = len;
- }
-
- ret = ceph_decrypt2(secret, &head, &head_len, *obuf, &olen, *p, len);
+ ret = ceph_crypt(secret, false, *p, end - *p, ciphertext_len,
+ &plaintext_len);
if (ret)
return ret;
- if (head.struct_v != 1 || le64_to_cpu(head.magic) != CEPHX_ENC_MAGIC)
+
+ if (hdr->struct_v != 1 || le64_to_cpu(hdr->magic) != CEPHX_ENC_MAGIC)
return -EPERM;
- *p += len;
- return olen;
+
+ *p += ciphertext_len;
+ return plaintext_len - sizeof(struct ceph_x_encrypt_header);
+
+e_inval:
+ return -EINVAL;
}
/*
@@ -143,13 +145,10 @@
int type;
u8 tkt_struct_v, blob_struct_v;
struct ceph_x_ticket_handler *th;
- void *dbuf = NULL;
void *dp, *dend;
int dlen;
char is_enc;
struct timespec validity;
- struct ceph_crypto_key old_key;
- void *ticket_buf = NULL;
void *tp, *tpend;
void **ptp;
struct ceph_crypto_key new_session_key;
@@ -174,20 +173,17 @@
}
/* blob for me */
- dlen = ceph_x_decrypt(secret, p, end, &dbuf, 0);
- if (dlen <= 0) {
- ret = dlen;
+ dp = *p + ceph_x_encrypt_offset();
+ ret = ceph_x_decrypt(secret, p, end);
+ if (ret < 0)
goto out;
- }
- dout(" decrypted %d bytes\n", dlen);
- dp = dbuf;
- dend = dp + dlen;
+ dout(" decrypted %d bytes\n", ret);
+ dend = dp + ret;
tkt_struct_v = ceph_decode_8(&dp);
if (tkt_struct_v != 1)
goto bad;
- memcpy(&old_key, &th->session_key, sizeof(old_key));
ret = ceph_crypto_key_decode(&new_session_key, &dp, dend);
if (ret)
goto out;
@@ -203,15 +199,13 @@
ceph_decode_8_safe(p, end, is_enc, bad);
if (is_enc) {
/* encrypted */
- dout(" encrypted ticket\n");
- dlen = ceph_x_decrypt(&old_key, p, end, &ticket_buf, 0);
- if (dlen < 0) {
- ret = dlen;
+ tp = *p + ceph_x_encrypt_offset();
+ ret = ceph_x_decrypt(&th->session_key, p, end);
+ if (ret < 0)
goto out;
- }
- tp = ticket_buf;
+ dout(" encrypted ticket, decrypted %d bytes\n", ret);
ptp = &tp;
- tpend = *ptp + dlen;
+ tpend = tp + ret;
} else {
/* unencrypted */
ptp = p;
@@ -242,8 +236,6 @@
xi->have_keys |= th->service;
out:
- kfree(ticket_buf);
- kfree(dbuf);
return ret;
bad:
@@ -294,7 +286,7 @@
{
int maxlen;
struct ceph_x_authorize_a *msg_a;
- struct ceph_x_authorize_b msg_b;
+ struct ceph_x_authorize_b *msg_b;
void *p, *end;
int ret;
int ticket_blob_len =
@@ -308,8 +300,8 @@
if (ret)
goto out_au;
- maxlen = sizeof(*msg_a) + sizeof(msg_b) +
- ceph_x_encrypt_buflen(ticket_blob_len);
+ maxlen = sizeof(*msg_a) + ticket_blob_len +
+ ceph_x_encrypt_buflen(sizeof(*msg_b));
dout(" need len %d\n", maxlen);
if (au->buf && au->buf->alloc_len < maxlen) {
ceph_buffer_put(au->buf);
@@ -343,18 +335,19 @@
p += ticket_blob_len;
end = au->buf->vec.iov_base + au->buf->vec.iov_len;
+ msg_b = p + ceph_x_encrypt_offset();
+ msg_b->struct_v = 1;
get_random_bytes(&au->nonce, sizeof(au->nonce));
- msg_b.struct_v = 1;
- msg_b.nonce = cpu_to_le64(au->nonce);
- ret = ceph_x_encrypt(&au->session_key, &msg_b, sizeof(msg_b),
- p, end - p);
+ msg_b->nonce = cpu_to_le64(au->nonce);
+ ret = ceph_x_encrypt(&au->session_key, p, end - p, sizeof(*msg_b));
if (ret < 0)
goto out_au;
+
p += ret;
+ WARN_ON(p > end);
au->buf->vec.iov_len = p - au->buf->vec.iov_base;
dout(" built authorizer nonce %llx len %d\n", au->nonce,
(int)au->buf->vec.iov_len);
- BUG_ON(au->buf->vec.iov_len > maxlen);
return 0;
out_au:
@@ -452,8 +445,9 @@
if (need & CEPH_ENTITY_TYPE_AUTH) {
struct ceph_x_authenticate *auth = (void *)(head + 1);
void *p = auth + 1;
- struct ceph_x_challenge_blob tmp;
- char tmp_enc[40];
+ void *enc_buf = xi->auth_authorizer.enc_buf;
+ struct ceph_x_challenge_blob *blob = enc_buf +
+ ceph_x_encrypt_offset();
u64 *u;
if (p > end)
@@ -464,16 +458,16 @@
/* encrypt and hash */
get_random_bytes(&auth->client_challenge, sizeof(u64));
- tmp.client_challenge = auth->client_challenge;
- tmp.server_challenge = cpu_to_le64(xi->server_challenge);
- ret = ceph_x_encrypt(&xi->secret, &tmp, sizeof(tmp),
- tmp_enc, sizeof(tmp_enc));
+ blob->client_challenge = auth->client_challenge;
+ blob->server_challenge = cpu_to_le64(xi->server_challenge);
+ ret = ceph_x_encrypt(&xi->secret, enc_buf, CEPHX_AU_ENC_BUF_LEN,
+ sizeof(*blob));
if (ret < 0)
return ret;
auth->struct_v = 1;
auth->key = 0;
- for (u = (u64 *)tmp_enc; u + 1 <= (u64 *)(tmp_enc + ret); u++)
+ for (u = (u64 *)enc_buf; u + 1 <= (u64 *)(enc_buf + ret); u++)
auth->key ^= *(__le64 *)u;
dout(" server_challenge %llx client_challenge %llx key %llx\n",
xi->server_challenge, le64_to_cpu(auth->client_challenge),
@@ -600,8 +594,8 @@
auth->authorizer = (struct ceph_authorizer *) au;
auth->authorizer_buf = au->buf->vec.iov_base;
auth->authorizer_buf_len = au->buf->vec.iov_len;
- auth->authorizer_reply_buf = au->reply_buf;
- auth->authorizer_reply_buf_len = sizeof (au->reply_buf);
+ auth->authorizer_reply_buf = au->enc_buf;
+ auth->authorizer_reply_buf_len = CEPHX_AU_ENC_BUF_LEN;
auth->sign_message = ac->ops->sign_message;
auth->check_message_signature = ac->ops->check_message_signature;
@@ -632,24 +626,22 @@
struct ceph_authorizer *a, size_t len)
{
struct ceph_x_authorizer *au = (void *)a;
- int ret = 0;
- struct ceph_x_authorize_reply reply;
- void *preply = &reply;
- void *p = au->reply_buf;
- void *end = p + sizeof(au->reply_buf);
+ void *p = au->enc_buf;
+ struct ceph_x_authorize_reply *reply = p + ceph_x_encrypt_offset();
+ int ret;
- ret = ceph_x_decrypt(&au->session_key, &p, end, &preply, sizeof(reply));
+ ret = ceph_x_decrypt(&au->session_key, &p, p + CEPHX_AU_ENC_BUF_LEN);
if (ret < 0)
return ret;
- if (ret != sizeof(reply))
+ if (ret != sizeof(*reply))
return -EPERM;
- if (au->nonce + 1 != le64_to_cpu(reply.nonce_plus_one))
+ if (au->nonce + 1 != le64_to_cpu(reply->nonce_plus_one))
ret = -EPERM;
else
ret = 0;
dout("verify_authorizer_reply nonce %llx got %llx ret %d\n",
- au->nonce, le64_to_cpu(reply.nonce_plus_one), ret);
+ au->nonce, le64_to_cpu(reply->nonce_plus_one), ret);
return ret;
}
@@ -704,35 +696,48 @@
invalidate_ticket(ac, CEPH_ENTITY_TYPE_AUTH);
}
-static int calcu_signature(struct ceph_x_authorizer *au,
- struct ceph_msg *msg, __le64 *sig)
+static int calc_signature(struct ceph_x_authorizer *au, struct ceph_msg *msg,
+ __le64 *psig)
{
+ void *enc_buf = au->enc_buf;
+ struct {
+ __le32 len;
+ __le32 header_crc;
+ __le32 front_crc;
+ __le32 middle_crc;
+ __le32 data_crc;
+ } __packed *sigblock = enc_buf + ceph_x_encrypt_offset();
int ret;
- char tmp_enc[40];
- __le32 tmp[5] = {
- cpu_to_le32(16), msg->hdr.crc, msg->footer.front_crc,
- msg->footer.middle_crc, msg->footer.data_crc,
- };
- ret = ceph_x_encrypt(&au->session_key, &tmp, sizeof(tmp),
- tmp_enc, sizeof(tmp_enc));
+
+ sigblock->len = cpu_to_le32(4*sizeof(u32));
+ sigblock->header_crc = msg->hdr.crc;
+ sigblock->front_crc = msg->footer.front_crc;
+ sigblock->middle_crc = msg->footer.middle_crc;
+ sigblock->data_crc = msg->footer.data_crc;
+ ret = ceph_x_encrypt(&au->session_key, enc_buf, CEPHX_AU_ENC_BUF_LEN,
+ sizeof(*sigblock));
if (ret < 0)
return ret;
- *sig = *(__le64*)(tmp_enc + 4);
+
+ *psig = *(__le64 *)(enc_buf + sizeof(u32));
return 0;
}
static int ceph_x_sign_message(struct ceph_auth_handshake *auth,
struct ceph_msg *msg)
{
+ __le64 sig;
int ret;
if (ceph_test_opt(from_msgr(msg->con->msgr), NOMSGSIGN))
return 0;
- ret = calcu_signature((struct ceph_x_authorizer *)auth->authorizer,
- msg, &msg->footer.sig);
- if (ret < 0)
+ ret = calc_signature((struct ceph_x_authorizer *)auth->authorizer,
+ msg, &sig);
+ if (ret)
return ret;
+
+ msg->footer.sig = sig;
msg->footer.flags |= CEPH_MSG_FOOTER_SIGNED;
return 0;
}
@@ -746,9 +751,9 @@
if (ceph_test_opt(from_msgr(msg->con->msgr), NOMSGSIGN))
return 0;
- ret = calcu_signature((struct ceph_x_authorizer *)auth->authorizer,
- msg, &sig_check);
- if (ret < 0)
+ ret = calc_signature((struct ceph_x_authorizer *)auth->authorizer,
+ msg, &sig_check);
+ if (ret)
return ret;
if (sig_check == msg->footer.sig)
return 0;
diff --git a/net/ceph/auth_x.h b/net/ceph/auth_x.h
index 21a5af9..48e9ad4 100644
--- a/net/ceph/auth_x.h
+++ b/net/ceph/auth_x.h
@@ -24,6 +24,7 @@
unsigned long renew_after, expires;
};
+#define CEPHX_AU_ENC_BUF_LEN 128 /* big enough for encrypted blob */
struct ceph_x_authorizer {
struct ceph_authorizer base;
@@ -32,7 +33,7 @@
unsigned int service;
u64 nonce;
u64 secret_id;
- char reply_buf[128]; /* big enough for encrypted blob */
+ char enc_buf[CEPHX_AU_ENC_BUF_LEN] __aligned(8);
};
struct ceph_x_info {
diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c
index db2847a..292e33b 100644
--- a/net/ceph/crypto.c
+++ b/net/ceph/crypto.c
@@ -13,14 +13,60 @@
#include <linux/ceph/decode.h>
#include "crypto.h"
+/*
+ * Set ->key and ->tfm. The rest of the key should be filled in before
+ * this function is called.
+ */
+static int set_secret(struct ceph_crypto_key *key, void *buf)
+{
+ unsigned int noio_flag;
+ int ret;
+
+ key->key = NULL;
+ key->tfm = NULL;
+
+ switch (key->type) {
+ case CEPH_CRYPTO_NONE:
+ return 0; /* nothing to do */
+ case CEPH_CRYPTO_AES:
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ WARN_ON(!key->len);
+ key->key = kmemdup(buf, key->len, GFP_NOIO);
+ if (!key->key) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ /* crypto_alloc_skcipher() allocates with GFP_KERNEL */
+ noio_flag = memalloc_noio_save();
+ key->tfm = crypto_alloc_skcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
+ memalloc_noio_restore(noio_flag);
+ if (IS_ERR(key->tfm)) {
+ ret = PTR_ERR(key->tfm);
+ key->tfm = NULL;
+ goto fail;
+ }
+
+ ret = crypto_skcipher_setkey(key->tfm, key->key, key->len);
+ if (ret)
+ goto fail;
+
+ return 0;
+
+fail:
+ ceph_crypto_key_destroy(key);
+ return ret;
+}
+
int ceph_crypto_key_clone(struct ceph_crypto_key *dst,
const struct ceph_crypto_key *src)
{
memcpy(dst, src, sizeof(struct ceph_crypto_key));
- dst->key = kmemdup(src->key, src->len, GFP_NOFS);
- if (!dst->key)
- return -ENOMEM;
- return 0;
+ return set_secret(dst, src->key);
}
int ceph_crypto_key_encode(struct ceph_crypto_key *key, void **p, void *end)
@@ -37,16 +83,16 @@
int ceph_crypto_key_decode(struct ceph_crypto_key *key, void **p, void *end)
{
+ int ret;
+
ceph_decode_need(p, end, 2*sizeof(u16) + sizeof(key->created), bad);
key->type = ceph_decode_16(p);
ceph_decode_copy(p, &key->created, sizeof(key->created));
key->len = ceph_decode_16(p);
ceph_decode_need(p, end, key->len, bad);
- key->key = kmalloc(key->len, GFP_NOFS);
- if (!key->key)
- return -ENOMEM;
- ceph_decode_copy(p, key->key, key->len);
- return 0;
+ ret = set_secret(key, *p);
+ *p += key->len;
+ return ret;
bad:
dout("failed to decode crypto key\n");
@@ -80,9 +126,14 @@
return 0;
}
-static struct crypto_skcipher *ceph_crypto_alloc_cipher(void)
+void ceph_crypto_key_destroy(struct ceph_crypto_key *key)
{
- return crypto_alloc_skcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (key) {
+ kfree(key->key);
+ key->key = NULL;
+ crypto_free_skcipher(key->tfm);
+ key->tfm = NULL;
+ }
}
static const u8 *aes_iv = (u8 *)CEPH_AES_IV;
@@ -157,372 +208,82 @@
sg_free_table(sgt);
}
-static int ceph_aes_encrypt(const void *key, int key_len,
- void *dst, size_t *dst_len,
- const void *src, size_t src_len)
+static int ceph_aes_crypt(const struct ceph_crypto_key *key, bool encrypt,
+ void *buf, int buf_len, int in_len, int *pout_len)
{
- struct scatterlist sg_in[2], prealloc_sg;
- struct sg_table sg_out;
- struct crypto_skcipher *tfm = ceph_crypto_alloc_cipher();
- SKCIPHER_REQUEST_ON_STACK(req, tfm);
+ SKCIPHER_REQUEST_ON_STACK(req, key->tfm);
+ struct sg_table sgt;
+ struct scatterlist prealloc_sg;
+ char iv[AES_BLOCK_SIZE] __aligned(8);
+ int pad_byte = AES_BLOCK_SIZE - (in_len & (AES_BLOCK_SIZE - 1));
+ int crypt_len = encrypt ? in_len + pad_byte : in_len;
int ret;
- char iv[AES_BLOCK_SIZE];
- size_t zero_padding = (0x10 - (src_len & 0x0f));
- char pad[16];
- if (IS_ERR(tfm))
- return PTR_ERR(tfm);
-
- memset(pad, zero_padding, zero_padding);
-
- *dst_len = src_len + zero_padding;
-
- sg_init_table(sg_in, 2);
- sg_set_buf(&sg_in[0], src, src_len);
- sg_set_buf(&sg_in[1], pad, zero_padding);
- ret = setup_sgtable(&sg_out, &prealloc_sg, dst, *dst_len);
+ WARN_ON(crypt_len > buf_len);
+ if (encrypt)
+ memset(buf + in_len, pad_byte, pad_byte);
+ ret = setup_sgtable(&sgt, &prealloc_sg, buf, crypt_len);
if (ret)
- goto out_tfm;
+ return ret;
- crypto_skcipher_setkey((void *)tfm, key, key_len);
memcpy(iv, aes_iv, AES_BLOCK_SIZE);
-
- skcipher_request_set_tfm(req, tfm);
+ skcipher_request_set_tfm(req, key->tfm);
skcipher_request_set_callback(req, 0, NULL, NULL);
- skcipher_request_set_crypt(req, sg_in, sg_out.sgl,
- src_len + zero_padding, iv);
+ skcipher_request_set_crypt(req, sgt.sgl, sgt.sgl, crypt_len, iv);
/*
- print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1,
- key, key_len, 1);
- print_hex_dump(KERN_ERR, "enc src: ", DUMP_PREFIX_NONE, 16, 1,
- src, src_len, 1);
- print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1,
- pad, zero_padding, 1);
+ print_hex_dump(KERN_ERR, "key: ", DUMP_PREFIX_NONE, 16, 1,
+ key->key, key->len, 1);
+ print_hex_dump(KERN_ERR, " in: ", DUMP_PREFIX_NONE, 16, 1,
+ buf, crypt_len, 1);
*/
- ret = crypto_skcipher_encrypt(req);
- skcipher_request_zero(req);
- if (ret < 0) {
- pr_err("ceph_aes_crypt failed %d\n", ret);
- goto out_sg;
- }
- /*
- print_hex_dump(KERN_ERR, "enc out: ", DUMP_PREFIX_NONE, 16, 1,
- dst, *dst_len, 1);
- */
-
-out_sg:
- teardown_sgtable(&sg_out);
-out_tfm:
- crypto_free_skcipher(tfm);
- return ret;
-}
-
-static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
- size_t *dst_len,
- const void *src1, size_t src1_len,
- const void *src2, size_t src2_len)
-{
- struct scatterlist sg_in[3], prealloc_sg;
- struct sg_table sg_out;
- struct crypto_skcipher *tfm = ceph_crypto_alloc_cipher();
- SKCIPHER_REQUEST_ON_STACK(req, tfm);
- int ret;
- char iv[AES_BLOCK_SIZE];
- size_t zero_padding = (0x10 - ((src1_len + src2_len) & 0x0f));
- char pad[16];
-
- if (IS_ERR(tfm))
- return PTR_ERR(tfm);
-
- memset(pad, zero_padding, zero_padding);
-
- *dst_len = src1_len + src2_len + zero_padding;
-
- sg_init_table(sg_in, 3);
- sg_set_buf(&sg_in[0], src1, src1_len);
- sg_set_buf(&sg_in[1], src2, src2_len);
- sg_set_buf(&sg_in[2], pad, zero_padding);
- ret = setup_sgtable(&sg_out, &prealloc_sg, dst, *dst_len);
- if (ret)
- goto out_tfm;
-
- crypto_skcipher_setkey((void *)tfm, key, key_len);
- memcpy(iv, aes_iv, AES_BLOCK_SIZE);
-
- skcipher_request_set_tfm(req, tfm);
- skcipher_request_set_callback(req, 0, NULL, NULL);
- skcipher_request_set_crypt(req, sg_in, sg_out.sgl,
- src1_len + src2_len + zero_padding, iv);
-
- /*
- print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1,
- key, key_len, 1);
- print_hex_dump(KERN_ERR, "enc src1: ", DUMP_PREFIX_NONE, 16, 1,
- src1, src1_len, 1);
- print_hex_dump(KERN_ERR, "enc src2: ", DUMP_PREFIX_NONE, 16, 1,
- src2, src2_len, 1);
- print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1,
- pad, zero_padding, 1);
- */
- ret = crypto_skcipher_encrypt(req);
- skcipher_request_zero(req);
- if (ret < 0) {
- pr_err("ceph_aes_crypt2 failed %d\n", ret);
- goto out_sg;
- }
- /*
- print_hex_dump(KERN_ERR, "enc out: ", DUMP_PREFIX_NONE, 16, 1,
- dst, *dst_len, 1);
- */
-
-out_sg:
- teardown_sgtable(&sg_out);
-out_tfm:
- crypto_free_skcipher(tfm);
- return ret;
-}
-
-static int ceph_aes_decrypt(const void *key, int key_len,
- void *dst, size_t *dst_len,
- const void *src, size_t src_len)
-{
- struct sg_table sg_in;
- struct scatterlist sg_out[2], prealloc_sg;
- struct crypto_skcipher *tfm = ceph_crypto_alloc_cipher();
- SKCIPHER_REQUEST_ON_STACK(req, tfm);
- char pad[16];
- char iv[AES_BLOCK_SIZE];
- int ret;
- int last_byte;
-
- if (IS_ERR(tfm))
- return PTR_ERR(tfm);
-
- sg_init_table(sg_out, 2);
- sg_set_buf(&sg_out[0], dst, *dst_len);
- sg_set_buf(&sg_out[1], pad, sizeof(pad));
- ret = setup_sgtable(&sg_in, &prealloc_sg, src, src_len);
- if (ret)
- goto out_tfm;
-
- crypto_skcipher_setkey((void *)tfm, key, key_len);
- memcpy(iv, aes_iv, AES_BLOCK_SIZE);
-
- skcipher_request_set_tfm(req, tfm);
- skcipher_request_set_callback(req, 0, NULL, NULL);
- skcipher_request_set_crypt(req, sg_in.sgl, sg_out,
- src_len, iv);
-
- /*
- print_hex_dump(KERN_ERR, "dec key: ", DUMP_PREFIX_NONE, 16, 1,
- key, key_len, 1);
- print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1,
- src, src_len, 1);
- */
- ret = crypto_skcipher_decrypt(req);
- skcipher_request_zero(req);
- if (ret < 0) {
- pr_err("ceph_aes_decrypt failed %d\n", ret);
- goto out_sg;
- }
-
- if (src_len <= *dst_len)
- last_byte = ((char *)dst)[src_len - 1];
+ if (encrypt)
+ ret = crypto_skcipher_encrypt(req);
else
- last_byte = pad[src_len - *dst_len - 1];
- if (last_byte <= 16 && src_len >= last_byte) {
- *dst_len = src_len - last_byte;
- } else {
- pr_err("ceph_aes_decrypt got bad padding %d on src len %d\n",
- last_byte, (int)src_len);
- return -EPERM; /* bad padding */
- }
- /*
- print_hex_dump(KERN_ERR, "dec out: ", DUMP_PREFIX_NONE, 16, 1,
- dst, *dst_len, 1);
- */
-
-out_sg:
- teardown_sgtable(&sg_in);
-out_tfm:
- crypto_free_skcipher(tfm);
- return ret;
-}
-
-static int ceph_aes_decrypt2(const void *key, int key_len,
- void *dst1, size_t *dst1_len,
- void *dst2, size_t *dst2_len,
- const void *src, size_t src_len)
-{
- struct sg_table sg_in;
- struct scatterlist sg_out[3], prealloc_sg;
- struct crypto_skcipher *tfm = ceph_crypto_alloc_cipher();
- SKCIPHER_REQUEST_ON_STACK(req, tfm);
- char pad[16];
- char iv[AES_BLOCK_SIZE];
- int ret;
- int last_byte;
-
- if (IS_ERR(tfm))
- return PTR_ERR(tfm);
-
- sg_init_table(sg_out, 3);
- sg_set_buf(&sg_out[0], dst1, *dst1_len);
- sg_set_buf(&sg_out[1], dst2, *dst2_len);
- sg_set_buf(&sg_out[2], pad, sizeof(pad));
- ret = setup_sgtable(&sg_in, &prealloc_sg, src, src_len);
- if (ret)
- goto out_tfm;
-
- crypto_skcipher_setkey((void *)tfm, key, key_len);
- memcpy(iv, aes_iv, AES_BLOCK_SIZE);
-
- skcipher_request_set_tfm(req, tfm);
- skcipher_request_set_callback(req, 0, NULL, NULL);
- skcipher_request_set_crypt(req, sg_in.sgl, sg_out,
- src_len, iv);
-
- /*
- print_hex_dump(KERN_ERR, "dec key: ", DUMP_PREFIX_NONE, 16, 1,
- key, key_len, 1);
- print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1,
- src, src_len, 1);
- */
- ret = crypto_skcipher_decrypt(req);
+ ret = crypto_skcipher_decrypt(req);
skcipher_request_zero(req);
- if (ret < 0) {
- pr_err("ceph_aes_decrypt failed %d\n", ret);
- goto out_sg;
- }
-
- if (src_len <= *dst1_len)
- last_byte = ((char *)dst1)[src_len - 1];
- else if (src_len <= *dst1_len + *dst2_len)
- last_byte = ((char *)dst2)[src_len - *dst1_len - 1];
- else
- last_byte = pad[src_len - *dst1_len - *dst2_len - 1];
- if (last_byte <= 16 && src_len >= last_byte) {
- src_len -= last_byte;
- } else {
- pr_err("ceph_aes_decrypt got bad padding %d on src len %d\n",
- last_byte, (int)src_len);
- return -EPERM; /* bad padding */
- }
-
- if (src_len < *dst1_len) {
- *dst1_len = src_len;
- *dst2_len = 0;
- } else {
- *dst2_len = src_len - *dst1_len;
+ if (ret) {
+ pr_err("%s %scrypt failed: %d\n", __func__,
+ encrypt ? "en" : "de", ret);
+ goto out_sgt;
}
/*
- print_hex_dump(KERN_ERR, "dec out1: ", DUMP_PREFIX_NONE, 16, 1,
- dst1, *dst1_len, 1);
- print_hex_dump(KERN_ERR, "dec out2: ", DUMP_PREFIX_NONE, 16, 1,
- dst2, *dst2_len, 1);
+ print_hex_dump(KERN_ERR, "out: ", DUMP_PREFIX_NONE, 16, 1,
+ buf, crypt_len, 1);
*/
-out_sg:
- teardown_sgtable(&sg_in);
-out_tfm:
- crypto_free_skcipher(tfm);
- return ret;
-}
-
-
-int ceph_decrypt(struct ceph_crypto_key *secret, void *dst, size_t *dst_len,
- const void *src, size_t src_len)
-{
- switch (secret->type) {
- case CEPH_CRYPTO_NONE:
- if (*dst_len < src_len)
- return -ERANGE;
- memcpy(dst, src, src_len);
- *dst_len = src_len;
- return 0;
-
- case CEPH_CRYPTO_AES:
- return ceph_aes_decrypt(secret->key, secret->len, dst,
- dst_len, src, src_len);
-
- default:
- return -EINVAL;
- }
-}
-
-int ceph_decrypt2(struct ceph_crypto_key *secret,
- void *dst1, size_t *dst1_len,
- void *dst2, size_t *dst2_len,
- const void *src, size_t src_len)
-{
- size_t t;
-
- switch (secret->type) {
- case CEPH_CRYPTO_NONE:
- if (*dst1_len + *dst2_len < src_len)
- return -ERANGE;
- t = min(*dst1_len, src_len);
- memcpy(dst1, src, t);
- *dst1_len = t;
- src += t;
- src_len -= t;
- if (src_len) {
- t = min(*dst2_len, src_len);
- memcpy(dst2, src, t);
- *dst2_len = t;
+ if (encrypt) {
+ *pout_len = crypt_len;
+ } else {
+ pad_byte = *(char *)(buf + in_len - 1);
+ if (pad_byte > 0 && pad_byte <= AES_BLOCK_SIZE &&
+ in_len >= pad_byte) {
+ *pout_len = in_len - pad_byte;
+ } else {
+ pr_err("%s got bad padding %d on in_len %d\n",
+ __func__, pad_byte, in_len);
+ ret = -EPERM;
+ goto out_sgt;
}
- return 0;
-
- case CEPH_CRYPTO_AES:
- return ceph_aes_decrypt2(secret->key, secret->len,
- dst1, dst1_len, dst2, dst2_len,
- src, src_len);
-
- default:
- return -EINVAL;
}
+
+out_sgt:
+ teardown_sgtable(&sgt);
+ return ret;
}
-int ceph_encrypt(struct ceph_crypto_key *secret, void *dst, size_t *dst_len,
- const void *src, size_t src_len)
+int ceph_crypt(const struct ceph_crypto_key *key, bool encrypt,
+ void *buf, int buf_len, int in_len, int *pout_len)
{
- switch (secret->type) {
+ switch (key->type) {
case CEPH_CRYPTO_NONE:
- if (*dst_len < src_len)
- return -ERANGE;
- memcpy(dst, src, src_len);
- *dst_len = src_len;
+ *pout_len = in_len;
return 0;
-
case CEPH_CRYPTO_AES:
- return ceph_aes_encrypt(secret->key, secret->len, dst,
- dst_len, src, src_len);
-
+ return ceph_aes_crypt(key, encrypt, buf, buf_len, in_len,
+ pout_len);
default:
- return -EINVAL;
- }
-}
-
-int ceph_encrypt2(struct ceph_crypto_key *secret, void *dst, size_t *dst_len,
- const void *src1, size_t src1_len,
- const void *src2, size_t src2_len)
-{
- switch (secret->type) {
- case CEPH_CRYPTO_NONE:
- if (*dst_len < src1_len + src2_len)
- return -ERANGE;
- memcpy(dst, src1, src1_len);
- memcpy(dst + src1_len, src2, src2_len);
- *dst_len = src1_len + src2_len;
- return 0;
-
- case CEPH_CRYPTO_AES:
- return ceph_aes_encrypt2(secret->key, secret->len, dst, dst_len,
- src1, src1_len, src2, src2_len);
-
- default:
- return -EINVAL;
+ return -ENOTSUPP;
}
}
diff --git a/net/ceph/crypto.h b/net/ceph/crypto.h
index 2e9cab0..58d83aa 100644
--- a/net/ceph/crypto.h
+++ b/net/ceph/crypto.h
@@ -12,37 +12,19 @@
struct ceph_timespec created;
int len;
void *key;
+ struct crypto_skcipher *tfm;
};
-static inline void ceph_crypto_key_destroy(struct ceph_crypto_key *key)
-{
- if (key) {
- kfree(key->key);
- key->key = NULL;
- }
-}
-
int ceph_crypto_key_clone(struct ceph_crypto_key *dst,
const struct ceph_crypto_key *src);
int ceph_crypto_key_encode(struct ceph_crypto_key *key, void **p, void *end);
int ceph_crypto_key_decode(struct ceph_crypto_key *key, void **p, void *end);
int ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *in);
+void ceph_crypto_key_destroy(struct ceph_crypto_key *key);
/* crypto.c */
-int ceph_decrypt(struct ceph_crypto_key *secret,
- void *dst, size_t *dst_len,
- const void *src, size_t src_len);
-int ceph_encrypt(struct ceph_crypto_key *secret,
- void *dst, size_t *dst_len,
- const void *src, size_t src_len);
-int ceph_decrypt2(struct ceph_crypto_key *secret,
- void *dst1, size_t *dst1_len,
- void *dst2, size_t *dst2_len,
- const void *src, size_t src_len);
-int ceph_encrypt2(struct ceph_crypto_key *secret,
- void *dst, size_t *dst_len,
- const void *src1, size_t src1_len,
- const void *src2, size_t src2_len);
+int ceph_crypt(const struct ceph_crypto_key *key, bool encrypt,
+ void *buf, int buf_len, int in_len, int *pout_len);
int ceph_crypto_init(void);
void ceph_crypto_shutdown(void);
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index a550289..2efb335 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -2027,6 +2027,19 @@
dout("process_connect on %p tag %d\n", con, (int)con->in_tag);
+ if (con->auth_reply_buf) {
+ /*
+ * Any connection that defines ->get_authorizer()
+ * should also define ->verify_authorizer_reply().
+ * See get_connect_authorizer().
+ */
+ ret = con->ops->verify_authorizer_reply(con, 0);
+ if (ret < 0) {
+ con->error_msg = "bad authorize reply";
+ return ret;
+ }
+ }
+
switch (con->in_reply.tag) {
case CEPH_MSGR_TAG_FEATURES:
pr_err("%s%lld %s feature set mismatch,"
diff --git a/net/core/dev.c b/net/core/dev.c
index 46a4830..ab6dc94 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2815,9 +2815,9 @@
if (skb->ip_summed != CHECKSUM_NONE &&
!can_checksum_protocol(features, type)) {
features &= ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
- } else if (illegal_highdma(skb->dev, skb)) {
- features &= ~NETIF_F_SG;
}
+ if (illegal_highdma(skb->dev, skb))
+ features &= ~NETIF_F_SG;
return features;
}
@@ -4453,7 +4453,9 @@
pinfo->nr_frags &&
!PageHighMem(skb_frag_page(frag0))) {
NAPI_GRO_CB(skb)->frag0 = skb_frag_address(frag0);
- NAPI_GRO_CB(skb)->frag0_len = skb_frag_size(frag0);
+ NAPI_GRO_CB(skb)->frag0_len = min_t(unsigned int,
+ skb_frag_size(frag0),
+ skb->end - skb->tail);
}
}
diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c
index 72cfb0c..ca2c9c8 100644
--- a/net/core/drop_monitor.c
+++ b/net/core/drop_monitor.c
@@ -80,6 +80,7 @@
struct nlattr *nla;
struct sk_buff *skb;
unsigned long flags;
+ void *msg_header;
al = sizeof(struct net_dm_alert_msg);
al += dm_hit_limit * sizeof(struct net_dm_drop_point);
@@ -87,21 +88,41 @@
skb = genlmsg_new(al, GFP_KERNEL);
- if (skb) {
- genlmsg_put(skb, 0, 0, &net_drop_monitor_family,
- 0, NET_DM_CMD_ALERT);
- nla = nla_reserve(skb, NLA_UNSPEC,
- sizeof(struct net_dm_alert_msg));
- msg = nla_data(nla);
- memset(msg, 0, al);
- } else {
- mod_timer(&data->send_timer, jiffies + HZ / 10);
- }
+ if (!skb)
+ goto err;
+ msg_header = genlmsg_put(skb, 0, 0, &net_drop_monitor_family,
+ 0, NET_DM_CMD_ALERT);
+ if (!msg_header) {
+ nlmsg_free(skb);
+ skb = NULL;
+ goto err;
+ }
+ nla = nla_reserve(skb, NLA_UNSPEC,
+ sizeof(struct net_dm_alert_msg));
+ if (!nla) {
+ nlmsg_free(skb);
+ skb = NULL;
+ goto err;
+ }
+ msg = nla_data(nla);
+ memset(msg, 0, al);
+ goto out;
+
+err:
+ mod_timer(&data->send_timer, jiffies + HZ / 10);
+out:
spin_lock_irqsave(&data->lock, flags);
swap(data->skb, skb);
spin_unlock_irqrestore(&data->lock, flags);
+ if (skb) {
+ struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data;
+ struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlh);
+
+ genlmsg_end(skb, genlmsg_data(gnlh));
+ }
+
return skb;
}
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c
index a384a10..5de436a 100644
--- a/net/core/fib_rules.c
+++ b/net/core/fib_rules.c
@@ -18,6 +18,11 @@
#include <net/fib_rules.h>
#include <net/ip_tunnels.h>
+static const struct fib_kuid_range fib_kuid_range_unset = {
+ KUIDT_INIT(0),
+ KUIDT_INIT(~0),
+};
+
int fib_default_rule_add(struct fib_rules_ops *ops,
u32 pref, u32 table, u32 flags)
{
@@ -33,8 +38,7 @@
r->table = table;
r->flags = flags;
r->fr_net = ops->fro_net;
- r->uid_start = INVALID_UID;
- r->uid_end = INVALID_UID;
+ r->uid_range = fib_kuid_range_unset;
r->suppress_prefixlen = -1;
r->suppress_ifgroup = -1;
@@ -174,21 +178,32 @@
}
EXPORT_SYMBOL_GPL(fib_rules_unregister);
-static inline kuid_t fib_nl_uid(struct nlattr *nla)
+static int uid_range_set(struct fib_kuid_range *range)
{
- return make_kuid(current_user_ns(), nla_get_u32(nla));
+ return uid_valid(range->start) && uid_valid(range->end);
}
-static int nla_put_uid(struct sk_buff *skb, int idx, kuid_t uid)
+static struct fib_kuid_range nla_get_kuid_range(struct nlattr **tb)
{
- return nla_put_u32(skb, idx, from_kuid_munged(current_user_ns(), uid));
+ struct fib_rule_uid_range *in;
+ struct fib_kuid_range out;
+
+ in = (struct fib_rule_uid_range *)nla_data(tb[FRA_UID_RANGE]);
+
+ out.start = make_kuid(current_user_ns(), in->start);
+ out.end = make_kuid(current_user_ns(), in->end);
+
+ return out;
}
-static int fib_uid_range_match(struct flowi *fl, struct fib_rule *rule)
+static int nla_put_uid_range(struct sk_buff *skb, struct fib_kuid_range *range)
{
- return (!uid_valid(rule->uid_start) && !uid_valid(rule->uid_end)) ||
- (uid_gte(fl->flowi_uid, rule->uid_start) &&
- uid_lte(fl->flowi_uid, rule->uid_end));
+ struct fib_rule_uid_range out = {
+ from_kuid_munged(current_user_ns(), range->start),
+ from_kuid_munged(current_user_ns(), range->end)
+ };
+
+ return nla_put(skb, FRA_UID_RANGE, sizeof(out), &out);
}
static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
@@ -212,7 +227,8 @@
if (rule->l3mdev && !l3mdev_fib_rule_match(rule->fr_net, fl, arg))
goto out;
- if (!fib_uid_range_match(fl, rule))
+ if (uid_lt(fl->flowi_uid, rule->uid_range.start) ||
+ uid_gt(fl->flowi_uid, rule->uid_range.end))
goto out;
ret = ops->match(rule, fl, flags);
@@ -451,25 +467,27 @@
if (rule->l3mdev && rule->table)
goto errout_free;
+ if (tb[FRA_UID_RANGE]) {
+ if (current_user_ns() != net->user_ns) {
+ err = -EPERM;
+ goto errout_free;
+ }
+
+ rule->uid_range = nla_get_kuid_range(tb);
+
+ if (!uid_range_set(&rule->uid_range) ||
+ !uid_lte(rule->uid_range.start, rule->uid_range.end))
+ goto errout_free;
+ } else {
+ rule->uid_range = fib_kuid_range_unset;
+ }
+
if ((nlh->nlmsg_flags & NLM_F_EXCL) &&
rule_exists(ops, frh, tb, rule)) {
err = -EEXIST;
goto errout_free;
}
- /* UID start and end must either both be valid or both unspecified. */
- rule->uid_start = rule->uid_end = INVALID_UID;
- if (tb[FRA_UID_START] || tb[FRA_UID_END]) {
- if (tb[FRA_UID_START] && tb[FRA_UID_END]) {
- rule->uid_start = fib_nl_uid(tb[FRA_UID_START]);
- rule->uid_end = fib_nl_uid(tb[FRA_UID_END]);
- }
- if (!uid_valid(rule->uid_start) ||
- !uid_valid(rule->uid_end) ||
- !uid_lte(rule->uid_start, rule->uid_end))
- goto errout_free;
- }
-
err = ops->configure(rule, skb, frh, tb);
if (err < 0)
goto errout_free;
@@ -532,6 +550,7 @@
struct fib_rules_ops *ops = NULL;
struct fib_rule *rule, *tmp;
struct nlattr *tb[FRA_MAX+1];
+ struct fib_kuid_range range;
int err = -EINVAL;
if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh)))
@@ -551,6 +570,14 @@
if (err < 0)
goto errout;
+ if (tb[FRA_UID_RANGE]) {
+ range = nla_get_kuid_range(tb);
+ if (!uid_range_set(&range))
+ goto errout;
+ } else {
+ range = fib_kuid_range_unset;
+ }
+
list_for_each_entry(rule, &ops->rules_list, list) {
if (frh->action && (frh->action != rule->action))
continue;
@@ -587,12 +614,9 @@
(rule->l3mdev != nla_get_u8(tb[FRA_L3MDEV])))
continue;
- if (tb[FRA_UID_START] &&
- !uid_eq(rule->uid_start, fib_nl_uid(tb[FRA_UID_START])))
- continue;
-
- if (tb[FRA_UID_END] &&
- !uid_eq(rule->uid_end, fib_nl_uid(tb[FRA_UID_END])))
+ if (uid_range_set(&range) &&
+ (!uid_eq(rule->uid_range.start, range.start) ||
+ !uid_eq(rule->uid_range.end, range.end)))
continue;
if (!ops->compare(rule, frh, tb))
@@ -663,8 +687,7 @@
+ nla_total_size(4) /* FRA_FWMARK */
+ nla_total_size(4) /* FRA_FWMASK */
+ nla_total_size_64bit(8) /* FRA_TUN_ID */
- + nla_total_size(4) /* FRA_UID_START */
- + nla_total_size(4); /* FRA_UID_END */
+ + nla_total_size(sizeof(struct fib_kuid_range));
if (ops->nlmsg_payload)
payload += ops->nlmsg_payload(rule);
@@ -725,10 +748,8 @@
nla_put_be64(skb, FRA_TUN_ID, rule->tun_id, FRA_PAD)) ||
(rule->l3mdev &&
nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) ||
- (uid_valid(rule->uid_start) &&
- nla_put_uid(skb, FRA_UID_START, rule->uid_start)) ||
- (uid_valid(rule->uid_end) &&
- nla_put_uid(skb, FRA_UID_END, rule->uid_end)))
+ (uid_range_set(&rule->uid_range) &&
+ nla_put_uid_range(skb, &rule->uid_range)))
goto nla_put_failure;
if (rule->suppress_ifgroup != -1) {
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index c6d8207..32e4e01 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -445,8 +445,9 @@
if (hdr->flags & GRE_ACK)
offset += sizeof(((struct pptp_gre_header *)0)->ack);
- ppp_hdr = skb_header_pointer(skb, nhoff + offset,
- sizeof(_ppp_hdr), _ppp_hdr);
+ ppp_hdr = __skb_header_pointer(skb, nhoff + offset,
+ sizeof(_ppp_hdr),
+ data, hlen, _ppp_hdr);
if (!ppp_hdr)
goto out_bad;
diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c
index e5f84c2..afa64f0 100644
--- a/net/core/lwtunnel.c
+++ b/net/core/lwtunnel.c
@@ -26,6 +26,7 @@
#include <net/lwtunnel.h>
#include <net/rtnetlink.h>
#include <net/ip6_fib.h>
+#include <net/nexthop.h>
#ifdef CONFIG_MODULES
@@ -65,6 +66,15 @@
static const struct lwtunnel_encap_ops __rcu *
lwtun_encaps[LWTUNNEL_ENCAP_MAX + 1] __read_mostly;
+void lwtstate_free(struct lwtunnel_state *lws)
+{
+ const struct lwtunnel_encap_ops *ops = lwtun_encaps[lws->type];
+
+ kfree(lws);
+ module_put(ops->owner);
+}
+EXPORT_SYMBOL(lwtstate_free);
+
int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *ops,
unsigned int num)
{
@@ -110,26 +120,78 @@
ret = -EOPNOTSUPP;
rcu_read_lock();
ops = rcu_dereference(lwtun_encaps[encap_type]);
-#ifdef CONFIG_MODULES
- if (!ops) {
- const char *encap_type_str = lwtunnel_encap_str(encap_type);
-
- if (encap_type_str) {
- rcu_read_unlock();
- request_module("rtnl-lwt-%s", encap_type_str);
- rcu_read_lock();
- ops = rcu_dereference(lwtun_encaps[encap_type]);
- }
- }
-#endif
- if (likely(ops && ops->build_state))
+ if (likely(ops && ops->build_state && try_module_get(ops->owner))) {
ret = ops->build_state(dev, encap, family, cfg, lws);
+ if (ret)
+ module_put(ops->owner);
+ }
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL(lwtunnel_build_state);
+int lwtunnel_valid_encap_type(u16 encap_type)
+{
+ const struct lwtunnel_encap_ops *ops;
+ int ret = -EINVAL;
+
+ if (encap_type == LWTUNNEL_ENCAP_NONE ||
+ encap_type > LWTUNNEL_ENCAP_MAX)
+ return ret;
+
+ rcu_read_lock();
+ ops = rcu_dereference(lwtun_encaps[encap_type]);
+ rcu_read_unlock();
+#ifdef CONFIG_MODULES
+ if (!ops) {
+ const char *encap_type_str = lwtunnel_encap_str(encap_type);
+
+ if (encap_type_str) {
+ __rtnl_unlock();
+ request_module("rtnl-lwt-%s", encap_type_str);
+ rtnl_lock();
+
+ rcu_read_lock();
+ ops = rcu_dereference(lwtun_encaps[encap_type]);
+ rcu_read_unlock();
+ }
+ }
+#endif
+ return ops ? 0 : -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(lwtunnel_valid_encap_type);
+
+int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int remaining)
+{
+ struct rtnexthop *rtnh = (struct rtnexthop *)attr;
+ struct nlattr *nla_entype;
+ struct nlattr *attrs;
+ struct nlattr *nla;
+ u16 encap_type;
+ int attrlen;
+
+ while (rtnh_ok(rtnh, remaining)) {
+ attrlen = rtnh_attrlen(rtnh);
+ if (attrlen > 0) {
+ attrs = rtnh_attrs(rtnh);
+ nla = nla_find(attrs, attrlen, RTA_ENCAP);
+ nla_entype = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
+
+ if (nla_entype) {
+ encap_type = nla_get_u16(nla_entype);
+
+ if (lwtunnel_valid_encap_type(encap_type) != 0)
+ return -EOPNOTSUPP;
+ }
+ }
+ rtnh = rtnh_next(rtnh, &remaining);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(lwtunnel_valid_encap_type_attr);
+
int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate)
{
const struct lwtunnel_encap_ops *ops;
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index a6196cf..b7f9ae7 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -3886,6 +3886,9 @@
u32 filter_mask;
int err;
+ if (nlmsg_len(nlh) < sizeof(*ifsm))
+ return -EINVAL;
+
ifsm = nlmsg_data(nlh);
if (ifsm->ifindex > 0)
dev = __dev_get_by_index(net, ifsm->ifindex);
@@ -3935,6 +3938,9 @@
cb->seq = net->dev_base_seq;
+ if (nlmsg_len(cb->nlh) < sizeof(*ifsm))
+ return -EINVAL;
+
ifsm = nlmsg_data(cb->nlh);
filter_mask = ifsm->filter_mask;
if (!filter_mask)
diff --git a/net/core/sock.c b/net/core/sock.c
index 00a074d..87a740b 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -222,7 +222,7 @@
"sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN" , "sk_lock-AF_PHONET" ,
"sk_lock-AF_IEEE802154", "sk_lock-AF_CAIF" , "sk_lock-AF_ALG" ,
"sk_lock-AF_NFC" , "sk_lock-AF_VSOCK" , "sk_lock-AF_KCM" ,
- "sk_lock-AF_MAX"
+ "sk_lock-AF_QIPCRTR", "sk_lock-AF_MAX"
};
static const char *const af_family_slock_key_strings[AF_MAX+1] = {
"slock-AF_UNSPEC", "slock-AF_UNIX" , "slock-AF_INET" ,
@@ -239,7 +239,7 @@
"slock-AF_RXRPC" , "slock-AF_ISDN" , "slock-AF_PHONET" ,
"slock-AF_IEEE802154", "slock-AF_CAIF" , "slock-AF_ALG" ,
"slock-AF_NFC" , "slock-AF_VSOCK" ,"slock-AF_KCM" ,
- "slock-AF_MAX"
+ "slock-AF_QIPCRTR", "slock-AF_MAX"
};
static const char *const af_family_clock_key_strings[AF_MAX+1] = {
"clock-AF_UNSPEC", "clock-AF_UNIX" , "clock-AF_INET" ,
@@ -256,7 +256,7 @@
"clock-AF_RXRPC" , "clock-AF_ISDN" , "clock-AF_PHONET" ,
"clock-AF_IEEE802154", "clock-AF_CAIF" , "clock-AF_ALG" ,
"clock-AF_NFC" , "clock-AF_VSOCK" , "clock-AF_KCM" ,
- "clock-AF_MAX"
+ "clock-AF_QIPCRTR", "clock-AF_MAX"
};
/*
@@ -2436,8 +2436,11 @@
sk->sk_type = sock->type;
sk->sk_wq = sock->wq;
sock->sk = sk;
- } else
+ sk->sk_uid = SOCK_INODE(sock)->i_uid;
+ } else {
sk->sk_wq = NULL;
+ sk->sk_uid = make_kuid(sock_net(sk)->user_ns, 0);
+ }
rwlock_init(&sk->sk_callback_lock);
lockdep_set_class_and_name(&sk->sk_callback_lock,
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index 5fff951..da38621 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -394,9 +394,11 @@
return err;
}
- err = dsa_cpu_port_ethtool_setup(dst->ds[0]);
- if (err)
- return err;
+ if (dst->ds[0]) {
+ err = dsa_cpu_port_ethtool_setup(dst->ds[0]);
+ if (err)
+ return err;
+ }
/* If we use a tagging format that doesn't have an ethertype
* field, make sure that all packets from this point on get
@@ -433,7 +435,8 @@
dsa_ds_unapply(dst, ds);
}
- dsa_cpu_port_ethtool_restore(dst->ds[0]);
+ if (dst->ds[0])
+ dsa_cpu_port_ethtool_restore(dst->ds[0]);
pr_info("DSA: tree %d unapplied\n", dst->tree);
dst->applied = false;
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 30e2e21..3ff9d97 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -1201,6 +1201,8 @@
{
struct dsa_slave_priv *p = netdev_priv(slave_dev);
+ netif_device_detach(slave_dev);
+
if (p->phy) {
phy_stop(p->phy);
p->old_pause = -1;
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 121384b..18412f9 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -46,6 +46,7 @@
#include <net/rtnetlink.h>
#include <net/xfrm.h>
#include <net/l3mdev.h>
+#include <net/lwtunnel.h>
#include <trace/events/fib.h>
#ifndef CONFIG_IP_MULTIPLE_TABLES
@@ -85,7 +86,7 @@
if (tb)
return tb;
- if (id == RT_TABLE_LOCAL)
+ if (id == RT_TABLE_LOCAL && !net->ipv4.fib_has_custom_rules)
alias = fib_new_table(net, RT_TABLE_MAIN);
tb = fib_trie_table(id, alias);
@@ -677,6 +678,10 @@
cfg->fc_mx_len = nla_len(attr);
break;
case RTA_MULTIPATH:
+ err = lwtunnel_valid_encap_type_attr(nla_data(attr),
+ nla_len(attr));
+ if (err < 0)
+ goto errout;
cfg->fc_mp = nla_data(attr);
cfg->fc_mp_len = nla_len(attr);
break;
@@ -691,6 +696,9 @@
break;
case RTA_ENCAP_TYPE:
cfg->fc_encap_type = nla_get_u16(attr);
+ err = lwtunnel_valid_encap_type(cfg->fc_encap_type);
+ if (err < 0)
+ goto errout;
break;
}
}
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 388d3e2..6a40680 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -1278,8 +1278,9 @@
nla_put_u32(skb, RTA_FLOW, fi->fib_nh[0].nh_tclassid))
goto nla_put_failure;
#endif
- if (fi->fib_nh->nh_lwtstate)
- lwtunnel_fill_encap(skb, fi->fib_nh->nh_lwtstate);
+ if (fi->fib_nh->nh_lwtstate &&
+ lwtunnel_fill_encap(skb, fi->fib_nh->nh_lwtstate) < 0)
+ goto nla_put_failure;
}
#ifdef CONFIG_IP_ROUTE_MULTIPATH
if (fi->fib_nhs > 1) {
@@ -1315,8 +1316,10 @@
nla_put_u32(skb, RTA_FLOW, nh->nh_tclassid))
goto nla_put_failure;
#endif
- if (nh->nh_lwtstate)
- lwtunnel_fill_encap(skb, nh->nh_lwtstate);
+ if (nh->nh_lwtstate &&
+ lwtunnel_fill_encap(skb, nh->nh_lwtstate) < 0)
+ goto nla_put_failure;
+
/* length of rtnetlink header + attributes */
rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *) rtnh;
} endfor_nexthops(fi);
@@ -1617,8 +1620,13 @@
void fib_select_path(struct net *net, struct fib_result *res,
struct flowi4 *fl4, int mp_hash)
{
+ bool oif_check;
+
+ oif_check = (fl4->flowi4_oif == 0 ||
+ fl4->flowi4_flags & FLOWI_FLAG_SKIP_NH_OIF);
+
#ifdef CONFIG_IP_ROUTE_MULTIPATH
- if (res->fi->fib_nhs > 1 && fl4->flowi4_oif == 0) {
+ if (res->fi->fib_nhs > 1 && oif_check) {
if (mp_hash < 0)
mp_hash = get_hash_from_flowi4(fl4) >> 1;
@@ -1628,7 +1636,7 @@
#endif
if (!res->prefixlen &&
res->table->tb_num_default > 1 &&
- res->type == RTN_UNICAST && !fl4->flowi4_oif)
+ res->type == RTN_UNICAST && oif_check)
fib_select_default(fl4, res);
if (!fl4->saddr)
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 48734ee..691146a 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -425,6 +425,7 @@
fl4.daddr = daddr;
fl4.saddr = saddr;
fl4.flowi4_mark = mark;
+ fl4.flowi4_uid = sock_net_uid(net, NULL);
fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos);
fl4.flowi4_proto = IPPROTO_ICMP;
fl4.flowi4_oif = l3mdev_master_ifindex(skb->dev);
@@ -473,6 +474,7 @@
param->replyopts.opt.opt.faddr : iph->saddr);
fl4->saddr = saddr;
fl4->flowi4_mark = mark;
+ fl4->flowi4_uid = sock_net_uid(net, NULL);
fl4->flowi4_tos = RT_TOS(tos);
fl4->flowi4_proto = IPPROTO_ICMP;
fl4->fl4_icmp_type = type;
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 15db786..32a08bc 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -219,9 +219,14 @@
static void igmp_gq_start_timer(struct in_device *in_dev)
{
int tv = prandom_u32() % in_dev->mr_maxdelay;
+ unsigned long exp = jiffies + tv + 2;
+
+ if (in_dev->mr_gq_running &&
+ time_after_eq(exp, (in_dev->mr_gq_timer).expires))
+ return;
in_dev->mr_gq_running = 1;
- if (!mod_timer(&in_dev->mr_gq_timer, jiffies+tv+2))
+ if (!mod_timer(&in_dev->mr_gq_timer, exp))
in_dev_hold(in_dev);
}
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index a16b439..d5d3ead 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -415,7 +415,7 @@
sk->sk_protocol, inet_sk_flowi_flags(sk),
(opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr,
ireq->ir_loc_addr, ireq->ir_rmt_port,
- htons(ireq->ir_num), sock_i_uid((struct sock *)sk));
+ htons(ireq->ir_num), sk->sk_uid);
security_req_classify_flow(req, flowi4_to_flowi(fl4));
rt = ip_route_output_flow(net, fl4, sk);
if (IS_ERR(rt))
@@ -452,7 +452,7 @@
sk->sk_protocol, inet_sk_flowi_flags(sk),
(opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr,
ireq->ir_loc_addr, ireq->ir_rmt_port,
- htons(ireq->ir_num), sock_i_uid((struct sock *)sk));
+ htons(ireq->ir_num), sk->sk_uid);
security_req_classify_flow(req, flowi4_to_flowi(fl4));
rt = ip_route_output_flow(net, fl4, sk);
if (IS_ERR(rt))
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index b8a2d63..f226f408 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -137,7 +137,7 @@
const struct iphdr *iph = ip_hdr(skb);
__be16 *ports = (__be16 *)skb_transport_header(skb);
- if (skb_transport_offset(skb) + 4 > skb->len)
+ if (skb_transport_offset(skb) + 4 > (int)skb->len)
return;
/* All current transport protocols have the port numbers in the
@@ -1202,8 +1202,14 @@
* which has interface index (iif) as the first member of the
* underlying inet{6}_skb_parm struct. This code then overlays
* PKTINFO_SKB_CB and in_pktinfo also has iif as the first
- * element so the iif is picked up from the prior IPCB
+ * element so the iif is picked up from the prior IPCB. If iif
+ * is the loopback interface, then return the sending interface
+ * (e.g., process binds socket to eth0 for Tx which is
+ * redirected to loopback in the rtable/dst).
*/
+ if (pktinfo->ipi_ifindex == LOOPBACK_IFINDEX)
+ pktinfo->ipi_ifindex = inet_iif(skb);
+
pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb);
} else {
pktinfo->ipi_ifindex = 0;
diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c
index fed3d29..0fd1976 100644
--- a/net/ipv4/ip_tunnel_core.c
+++ b/net/ipv4/ip_tunnel_core.c
@@ -313,6 +313,7 @@
.fill_encap = ip_tun_fill_encap_info,
.get_encap_size = ip_tun_encap_nlsize,
.cmp_encap = ip_tun_cmp_encap,
+ .owner = THIS_MODULE,
};
static const struct nla_policy ip6_tun_policy[LWTUNNEL_IP6_MAX + 1] = {
@@ -403,6 +404,7 @@
.fill_encap = ip6_tun_fill_encap_info,
.get_encap_size = ip6_tun_encap_nlsize,
.cmp_encap = ip_tun_cmp_encap,
+ .owner = THIS_MODULE,
};
void __init ip_tunnel_core_init(void)
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index 2dcd16e..5b2635e 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -794,7 +794,7 @@
flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos,
RT_SCOPE_UNIVERSE, sk->sk_protocol,
inet_sk_flowi_flags(sk), faddr, saddr, 0, 0,
- sock_i_uid(sk));
+ sk->sk_uid);
security_sk_classify_flow(sk, flowi4_to_flowi(&fl4));
rt = ip_route_output_flow(net, &fl4, sk);
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 65c3bc9..7525f5e 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -604,8 +604,7 @@
inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol,
inet_sk_flowi_flags(sk) |
(inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0),
- daddr, saddr, 0, 0,
- sock_i_uid(sk));
+ daddr, saddr, 0, 0, sk->sk_uid);
if (!inet->hdrincl) {
rfv.msg = msg;
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 8cb3f52..5ba912d 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -507,7 +507,8 @@
}
EXPORT_SYMBOL(__ip_select_ident);
-static void __build_flow_key(struct flowi4 *fl4, struct sock *sk,
+static void __build_flow_key(const struct net *net, struct flowi4 *fl4,
+ const struct sock *sk,
const struct iphdr *iph,
int oif, u8 tos,
u8 prot, u32 mark, int flow_flags)
@@ -524,19 +525,20 @@
RT_SCOPE_UNIVERSE, prot,
flow_flags,
iph->daddr, iph->saddr, 0, 0,
- sk ? sock_i_uid(sk) : GLOBAL_ROOT_UID);
+ sock_net_uid(net, sk));
}
static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb,
struct sock *sk)
{
+ const struct net *net = dev_net(skb->dev);
const struct iphdr *iph = ip_hdr(skb);
int oif = skb->dev->ifindex;
u8 tos = RT_TOS(iph->tos);
u8 prot = iph->protocol;
u32 mark = skb->mark;
- __build_flow_key(fl4, sk, iph, oif, tos, prot, mark, 0);
+ __build_flow_key(net, fl4, sk, iph, oif, tos, prot, mark, 0);
}
static void build_sk_flow_key(struct flowi4 *fl4, struct sock *sk)
@@ -553,8 +555,7 @@
RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE,
inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol,
inet_sk_flowi_flags(sk),
- daddr, inet->inet_saddr, 0, 0,
- sock_i_uid(sk));
+ daddr, inet->inet_saddr, 0, 0, sk->sk_uid);
rcu_read_unlock();
}
@@ -804,7 +805,7 @@
rt = (struct rtable *) dst;
- __build_flow_key(&fl4, sk, iph, oif, tos, prot, mark, 0);
+ __build_flow_key(sock_net(sk), &fl4, sk, iph, oif, tos, prot, mark, 0);
__ip_do_redirect(rt, skb, &fl4, true);
}
@@ -1022,7 +1023,7 @@
if (!mark)
mark = IP4_REPLY_MARK(net, skb->mark);
- __build_flow_key(&fl4, NULL, iph, oif,
+ __build_flow_key(net, &fl4, NULL, iph, oif,
RT_TOS(iph->tos), protocol, mark, flow_flags);
rt = __ip_route_output_key(net, &fl4);
if (!IS_ERR(rt)) {
@@ -1038,7 +1039,7 @@
struct flowi4 fl4;
struct rtable *rt;
- __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0);
+ __build_flow_key(sock_net(sk), &fl4, sk, iph, 0, 0, 0, 0, 0);
if (!fl4.flowi4_mark)
fl4.flowi4_mark = IP4_REPLY_MARK(sock_net(sk), skb->mark);
@@ -1057,6 +1058,7 @@
struct rtable *rt;
struct dst_entry *odst = NULL;
bool new = false;
+ struct net *net = sock_net(sk);
bh_lock_sock(sk);
@@ -1070,7 +1072,7 @@
goto out;
}
- __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0);
+ __build_flow_key(net, &fl4, sk, iph, 0, 0, 0, 0, 0);
rt = (struct rtable *)odst;
if (odst->obsolete && !odst->ops->check(odst, 0)) {
@@ -1110,7 +1112,7 @@
struct flowi4 fl4;
struct rtable *rt;
- __build_flow_key(&fl4, NULL, iph, oif,
+ __build_flow_key(net, &fl4, NULL, iph, oif,
RT_TOS(iph->tos), protocol, mark, flow_flags);
rt = __ip_route_output_key(net, &fl4);
if (!IS_ERR(rt)) {
@@ -1125,9 +1127,10 @@
const struct iphdr *iph = (const struct iphdr *) skb->data;
struct flowi4 fl4;
struct rtable *rt;
+ struct net *net = sock_net(sk);
- __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0);
- rt = __ip_route_output_key(sock_net(sk), &fl4);
+ __build_flow_key(net, &fl4, sk, iph, 0, 0, 0, 0, 0);
+ rt = __ip_route_output_key(net, &fl4);
if (!IS_ERR(rt)) {
__ip_do_redirect(rt, skb, &fl4, false);
ip_rt_put(rt);
@@ -1904,7 +1907,8 @@
}
}
- rth = rt_dst_alloc(net->loopback_dev, flags | RTCF_LOCAL, res.type,
+ rth = rt_dst_alloc(l3mdev_master_dev_rcu(dev) ? : net->loopback_dev,
+ flags | RTCF_LOCAL, res.type,
IN_DEV_CONF_GET(in_dev, NOPOLICY), false, do_cache);
if (!rth)
goto e_nobufs;
@@ -2441,7 +2445,7 @@
r->rtm_dst_len = 32;
r->rtm_src_len = 0;
r->rtm_tos = fl4->flowi4_tos;
- r->rtm_table = table_id;
+ r->rtm_table = table_id < 256 ? table_id : RT_TABLE_COMPAT;
if (nla_put_u32(skb, RTA_TABLE, table_id))
goto nla_put_failure;
r->rtm_type = rt->rt_type;
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 63d07b8..0dc6286 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -371,9 +371,8 @@
flowi4_init_output(&fl4, ireq->ir_iif, ireq->ir_mark,
RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP,
inet_sk_flowi_flags(sk),
- (opt && opt->srr) ? opt->faddr : ireq->ir_rmt_addr,
- ireq->ir_loc_addr, th->source, th->dest,
- sock_i_uid(sk));
+ opt->srr ? opt->faddr : ireq->ir_rmt_addr,
+ ireq->ir_loc_addr, th->source, th->dest, sk->sk_uid);
security_req_classify_flow(req, flowi4_to_flowi(&fl4));
rt = ip_route_output_key(sock_net(sk), &fl4);
if (IS_ERR(rt)) {
diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c
index 4e777a3..dd2560c 100644
--- a/net/ipv4/tcp_fastopen.c
+++ b/net/ipv4/tcp_fastopen.c
@@ -113,7 +113,7 @@
struct tcp_fastopen_cookie tmp;
if (__tcp_fastopen_cookie_gen(&ip6h->saddr, &tmp)) {
- struct in6_addr *buf = (struct in6_addr *) tmp.val;
+ struct in6_addr *buf = &tmp.addr;
int i;
for (i = 0; i < 4; i++)
@@ -205,6 +205,7 @@
* scaled. So correct it appropriately.
*/
tp->snd_wnd = ntohs(tcp_hdr(skb)->window);
+ tp->max_window = tp->snd_wnd;
/* Activate the retrans timer so that SYNACK can be retransmitted.
* The request socket is not added to the ehash
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 2259114..eb5a0e1 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -691,6 +691,7 @@
offsetof(struct inet_timewait_sock, tw_bound_dev_if));
arg.tos = ip_hdr(skb)->tos;
+ arg.uid = sock_net_uid(net, sk && sk_fullsock(sk) ? sk : NULL);
local_bh_disable();
ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk),
skb, &TCP_SKB_CB(skb)->header.h4.opt,
@@ -711,7 +712,7 @@
outside socket context is ugly, certainly. What can I do?
*/
-static void tcp_v4_send_ack(struct net *net,
+static void tcp_v4_send_ack(const struct sock *sk,
struct sk_buff *skb, u32 seq, u32 ack,
u32 win, u32 tsval, u32 tsecr, int oif,
struct tcp_md5sig_key *key,
@@ -726,6 +727,7 @@
#endif
];
} rep;
+ struct net *net = sock_net(sk);
struct ip_reply_arg arg;
memset(&rep.th, 0, sizeof(struct tcphdr));
@@ -775,6 +777,7 @@
if (oif)
arg.bound_dev_if = oif;
arg.tos = tos;
+ arg.uid = sock_net_uid(net, sk_fullsock(sk) ? sk : NULL);
local_bh_disable();
ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk),
skb, &TCP_SKB_CB(skb)->header.h4.opt,
@@ -790,7 +793,7 @@
struct inet_timewait_sock *tw = inet_twsk(sk);
struct tcp_timewait_sock *tcptw = tcp_twsk(sk);
- tcp_v4_send_ack(sock_net(sk), skb,
+ tcp_v4_send_ack(sk, skb,
tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
tcp_time_stamp + tcptw->tw_ts_offset,
@@ -818,7 +821,7 @@
* exception of <SYN> segments, MUST be right-shifted by
* Rcv.Wind.Shift bits:
*/
- tcp_v4_send_ack(sock_net(sk), skb, seq,
+ tcp_v4_send_ack(sk, skb, seq,
tcp_rsk(req)->rcv_nxt,
req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale,
tcp_time_stamp,
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 3e6daef2..5093bb8 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1020,7 +1020,7 @@
RT_SCOPE_UNIVERSE, sk->sk_protocol,
flow_flags,
faddr, saddr, dport, inet->inet_sport,
- sock_i_uid(sk));
+ sk->sk_uid);
security_sk_classify_flow(sk, flowi4_to_flowi(fl4));
rt = ip_route_output_flow(net, fl4, sk);
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index e03efc6..965ca86 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -5554,8 +5554,7 @@
struct net_device *dev;
struct inet6_dev *idev;
- rcu_read_lock();
- for_each_netdev_rcu(net, dev) {
+ for_each_netdev(net, dev) {
idev = __in6_dev_get(dev);
if (idev) {
int changed = (!idev->cnf.disable_ipv6) ^ (!newf);
@@ -5564,7 +5563,6 @@
dev_disable_change(idev);
}
}
- rcu_read_unlock();
}
static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int newf)
@@ -5950,6 +5948,13 @@
#endif
#endif
{
+ .procname = "accept_ra_rt_table",
+ .data = &ipv6_devconf.accept_ra_rt_table,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
.procname = "proxy_ndp",
.data = &ipv6_devconf.proxy_ndp,
.maxlen = sizeof(int),
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index e29162d..0281645 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -694,7 +694,7 @@
fl6.flowi6_mark = sk->sk_mark;
fl6.fl6_dport = inet->inet_dport;
fl6.fl6_sport = inet->inet_sport;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
rcu_read_lock();
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c
index c52b8fc..189eb10 100644
--- a/net/ipv6/ah6.c
+++ b/net/ipv6/ah6.c
@@ -662,9 +662,10 @@
return 0;
if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, skb->dev->ifindex, 0);
+ ip6_redirect(skb, net, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
else
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
xfrm_state_put(x);
return 0;
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 50dacc8..1529833 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -54,7 +54,7 @@
fl6->fl6_dport = inet->inet_dport;
fl6->fl6_sport = inet->inet_sport;
fl6->flowlabel = np->flow_label;
- fl6->flowi6_uid = sock_i_uid(sk);
+ fl6->flowi6_uid = sk->sk_uid;
if (!fl6->flowi6_oif)
fl6->flowi6_oif = np->sticky_pktinfo.ipi6_ifindex;
@@ -701,7 +701,7 @@
struct sockaddr_in6 sin6;
__be16 *ports = (__be16 *) skb_transport_header(skb);
- if (skb_transport_offset(skb) + 4 <= skb->len) {
+ if (skb_transport_offset(skb) + 4 <= (int)skb->len) {
/* All current transport protocols have the port numbers in the
* first four bytes of the transport header and this function is
* written with this assumption in mind.
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index 17df6bb..cbcdd5d 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -474,9 +474,10 @@
return 0;
if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, skb->dev->ifindex, 0);
+ ip6_redirect(skb, net, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
else
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
xfrm_state_put(x);
return 0;
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 15db375..17fa28f 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -92,9 +92,10 @@
struct net *net = dev_net(skb->dev);
if (type == ICMPV6_PKT_TOOBIG)
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
else if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, skb->dev->ifindex, 0);
+ ip6_redirect(skb, net, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
if (!(type & ICMPV6_INFOMSG_MASK))
if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST)
@@ -486,6 +487,7 @@
fl6.flowi6_oif = iif;
fl6.fl6_icmp_type = type;
fl6.fl6_icmp_code = code;
+ fl6.flowi6_uid = sock_net_uid(net, NULL);
security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
sk = icmpv6_xmit_lock(net);
@@ -660,6 +662,7 @@
fl6.flowi6_oif = skb->dev->ifindex;
fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY;
fl6.flowi6_mark = mark;
+ fl6.flowi6_uid = sock_net_uid(net, NULL);
security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
sk = icmpv6_xmit_lock(net);
diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c
index e50c27a..f3db364 100644
--- a/net/ipv6/ila/ila_lwt.c
+++ b/net/ipv6/ila/ila_lwt.c
@@ -164,6 +164,7 @@
.fill_encap = ila_fill_encap_info,
.get_encap_size = ila_encap_nlsize,
.cmp_encap = ila_encap_cmp,
+ .owner = THIS_MODULE,
};
int ila_lwt_init(void)
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
index dd6f8aa..1c86c47 100644
--- a/net/ipv6/inet6_connection_sock.c
+++ b/net/ipv6/inet6_connection_sock.c
@@ -88,7 +88,7 @@
fl6->flowi6_mark = ireq->ir_mark;
fl6->fl6_dport = ireq->ir_rmt_port;
fl6->fl6_sport = htons(ireq->ir_num);
- fl6->flowi6_uid = sock_i_uid((struct sock *)sk);
+ fl6->flowi6_uid = sk->sk_uid;
security_req_classify_flow(req, flowi6_to_flowi(fl6));
dst = ip6_dst_lookup_flow(sk, fl6, final_p);
@@ -137,7 +137,7 @@
fl6->flowi6_mark = sk->sk_mark;
fl6->fl6_sport = inet->inet_sport;
fl6->fl6_dport = inet->inet_dport;
- fl6->flowi6_uid = sock_i_uid(sk);
+ fl6->flowi6_uid = sk->sk_uid;
security_sk_classify_flow(sk, flowi6_to_flowi(fl6));
rcu_read_lock();
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index d7d6d3a..710bc79 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -548,6 +548,8 @@
if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
fl6.flowi6_mark = skb->mark;
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
+
err = gre_handle_offloads(skb, !!(t->parms.o_flags & TUNNEL_CSUM));
if (err)
return -1;
@@ -602,6 +604,8 @@
if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
fl6.flowi6_mark = skb->mark;
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
+
if (gre_handle_offloads(skb, !!(t->parms.o_flags & TUNNEL_CSUM)))
return -1;
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index 89c59e6..fc7b401 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -191,6 +191,7 @@
ops = rcu_dereference(inet6_offloads[proto]);
if (!ops || !ops->callbacks.gro_receive) {
__pskb_pull(skb, skb_gro_offset(skb));
+ skb_gro_frag0_invalidate(skb);
proto = ipv6_gso_pull_exthdrs(skb, proto);
skb_gro_pull(skb, -skb_transport_offset(skb));
skb_reset_transport_header(skb);
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index d76674e..c1f497b 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1108,7 +1108,7 @@
t->parms.name);
goto tx_err_dst_release;
}
- mtu = dst_mtu(dst) - psh_hlen;
+ mtu = dst_mtu(dst) - psh_hlen - t->tun_hlen;
if (encap_limit >= 0) {
max_headroom += 8;
mtu -= 8;
@@ -1117,7 +1117,7 @@
mtu = IPV6_MIN_MTU;
if (skb_dst(skb) && !t->parms.collect_md)
skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
- if (skb->len > mtu && !skb_is_gso(skb)) {
+ if (skb->len - t->tun_hlen > mtu && !skb_is_gso(skb)) {
*pmtu = mtu;
err = -EMSGSIZE;
goto tx_err_dst_release;
@@ -1248,6 +1248,8 @@
fl6.flowi6_mark = skb->mark;
}
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
+
if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6))
return -1;
@@ -1326,6 +1328,8 @@
fl6.flowi6_mark = skb->mark;
}
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
+
if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6))
return -1;
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index d58480a..3bce120 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -608,9 +608,10 @@
return 0;
if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, skb->dev->ifindex, 0);
+ ip6_redirect(skb, net, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
else
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
xfrm_state_put(x);
return 0;
diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
index b247bac..54d165b 100644
--- a/net/ipv6/ipcomp6.c
+++ b/net/ipv6/ipcomp6.c
@@ -74,9 +74,10 @@
return 0;
if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, skb->dev->ifindex, 0);
+ ip6_redirect(skb, net, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
else
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
xfrm_state_put(x);
return 0;
diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
index d11c468..39970e2 100644
--- a/net/ipv6/netfilter.c
+++ b/net/ipv6/netfilter.c
@@ -26,6 +26,7 @@
struct flowi6 fl6 = {
.flowi6_oif = skb->sk ? skb->sk->sk_bound_dev_if : 0,
.flowi6_mark = skb->mark,
+ .flowi6_uid = sock_net_uid(net, skb->sk),
.daddr = iph->daddr,
.saddr = iph->saddr,
};
diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c
index 505c442..e1f8b34 100644
--- a/net/ipv6/ping.c
+++ b/net/ipv6/ping.c
@@ -113,7 +113,7 @@
fl6.daddr = *daddr;
fl6.flowi6_oif = oif;
fl6.flowi6_mark = sk->sk_mark;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
fl6.fl6_icmp_type = user_icmph.icmp6_type;
fl6.fl6_icmp_code = user_icmph.icmp6_code;
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index f7b0b59..5665375 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -589,7 +589,11 @@
}
offset += skb_transport_offset(skb);
- BUG_ON(skb_copy_bits(skb, offset, &csum, 2));
+ err = skb_copy_bits(skb, offset, &csum, 2);
+ if (err < 0) {
+ ip6_flush_pending_frames(sk);
+ goto out;
+ }
/* in case cksum was not initialized */
if (unlikely(csum))
@@ -774,7 +778,7 @@
memset(&fl6, 0, sizeof(fl6));
fl6.flowi6_mark = sk->sk_mark;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
ipc6.hlimit = -1;
ipc6.tclass = -1;
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 77e6547..477600f 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -1428,7 +1428,7 @@
struct dst_entry *dst;
ip6_update_pmtu(skb, sock_net(sk), mtu,
- sk->sk_bound_dev_if, sk->sk_mark, sock_i_uid(sk));
+ sk->sk_bound_dev_if, sk->sk_mark, sk->sk_uid);
dst = __sk_dst_get(sk);
if (!dst || !dst->obsolete ||
@@ -1520,7 +1520,8 @@
flags, __ip6_route_redirect);
}
-void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
+void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
+ kuid_t uid)
{
const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
struct dst_entry *dst;
@@ -1533,6 +1534,7 @@
fl6.daddr = iph->daddr;
fl6.saddr = iph->saddr;
fl6.flowlabel = ip6_flowinfo(iph);
+ fl6.flowi6_uid = uid;
dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr);
rt6_do_redirect(dst, NULL, skb);
@@ -1554,6 +1556,7 @@
fl6.flowi6_mark = mark;
fl6.daddr = msg->dest;
fl6.saddr = iph->daddr;
+ fl6.flowi6_uid = sock_net_uid(net, NULL);
dst = ip6_route_redirect(net, &fl6, &iph->saddr);
rt6_do_redirect(dst, NULL, skb);
@@ -1562,7 +1565,8 @@
void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
{
- ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
+ ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark,
+ sk->sk_uid);
}
EXPORT_SYMBOL_GPL(ip6_sk_redirect);
@@ -2329,12 +2333,12 @@
const struct in6_addr *prefix, int prefixlen,
const struct in6_addr *gwaddr)
{
+ u32 tb_id = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_INFO);
struct fib6_node *fn;
struct rt6_info *rt = NULL;
struct fib6_table *table;
- table = fib6_get_table(dev_net(dev),
- addrconf_rt_table(dev, RT6_TABLE_INFO));
+ table = fib6_get_table(dev_net(dev), tb_id);
if (!table)
return NULL;
@@ -2373,7 +2377,7 @@
.fc_nlinfo.nl_net = dev_net(dev),
};
- cfg.fc_table = l3mdev_fib_table_by_index(dev_net(dev), dev->ifindex) ? : addrconf_rt_table(dev, RT6_TABLE_INFO);
+ cfg.fc_table = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_INFO),
cfg.fc_dst = *prefix;
cfg.fc_gateway = *gwaddr;
@@ -2389,11 +2393,11 @@
struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
{
+ u32 tb_id = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_MAIN);
struct rt6_info *rt;
struct fib6_table *table;
- table = fib6_get_table(dev_net(dev),
- addrconf_rt_table(dev, RT6_TABLE_MAIN));
+ table = fib6_get_table(dev_net(dev), tb_id);
if (!table)
return NULL;
@@ -2438,7 +2442,6 @@
return rt6_get_dflt_router(gwaddr, dev);
}
-
int rt6_addrconf_purge(struct rt6_info *rt, void *arg) {
if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
(!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2))
@@ -2851,6 +2854,11 @@
if (tb[RTA_MULTIPATH]) {
cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
+
+ err = lwtunnel_valid_encap_type_attr(cfg->fc_mp,
+ cfg->fc_mp_len);
+ if (err < 0)
+ goto errout;
}
if (tb[RTA_PREF]) {
@@ -2864,9 +2872,14 @@
if (tb[RTA_ENCAP])
cfg->fc_encap = tb[RTA_ENCAP];
- if (tb[RTA_ENCAP_TYPE])
+ if (tb[RTA_ENCAP_TYPE]) {
cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
+ err = lwtunnel_valid_encap_type(cfg->fc_encap_type);
+ if (err < 0)
+ goto errout;
+ }
+
if (tb[RTA_EXPIRES]) {
unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
@@ -3272,7 +3285,8 @@
if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
goto nla_put_failure;
- lwtunnel_fill_encap(skb, rt->dst.lwtstate);
+ if (lwtunnel_fill_encap(skb, rt->dst.lwtstate) < 0)
+ goto nla_put_failure;
nlmsg_end(skb, nlh);
return 0;
@@ -3346,6 +3360,7 @@
nla_get_u32(tb[RTA_UID]));
else
fl6.flowi6_uid = iif ? INVALID_UID : current_uid();
+
if (iif) {
struct net_device *dev;
int flags = 0;
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index 7042046..97830a6 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -227,7 +227,7 @@
fl6.flowi6_mark = ireq->ir_mark;
fl6.fl6_dport = ireq->ir_rmt_port;
fl6.fl6_sport = inet_sk(sk)->inet_sport;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
security_req_classify_flow(req, flowi6_to_flowi(&fl6));
dst = ip6_dst_lookup_flow(sk, &fl6, final_p);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 6e24ed2..28ec0a2 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -233,7 +233,7 @@
fl6.flowi6_mark = sk->sk_mark;
fl6.fl6_dport = usin->sin6_port;
fl6.fl6_sport = inet->inet_sport;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
opt = rcu_dereference_protected(np->opt, lockdep_sock_is_held(sk));
final_p = fl6_update_dst(&fl6, opt, &final);
@@ -829,6 +829,7 @@
fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark);
fl6.fl6_dport = t1->dest;
fl6.fl6_sport = t1->source;
+ fl6.flowi6_uid = sock_net_uid(net, sk && sk_fullsock(sk) ? sk : NULL);
security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
/* Pass a socket to ip6_dst_lookup either it is for RST
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index d541bf1..f6fbd25 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1156,7 +1156,7 @@
fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex;
fl6.flowi6_mark = sk->sk_mark;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
sockc.tsflags = sk->sk_tsflags;
if (msg->msg_controllen) {
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index 02b45a8..91cbbf1 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -1036,7 +1036,8 @@
{
struct sock *sk = sock->sk;
struct iucv_sock *iucv = iucv_sk(sk);
- size_t headroom, linear;
+ size_t headroom = 0;
+ size_t linear;
struct sk_buff *skb;
struct iucv_message txmsg = {0};
struct cmsghdr *cmsg;
@@ -1114,18 +1115,20 @@
* this is fine for SOCK_SEQPACKET (unless we want to support
* segmented records using the MSG_EOR flag), but
* for SOCK_STREAM we might want to improve it in future */
- headroom = (iucv->transport == AF_IUCV_TRANS_HIPER)
- ? sizeof(struct af_iucv_trans_hdr) + ETH_HLEN : 0;
- if (headroom + len < PAGE_SIZE) {
+ if (iucv->transport == AF_IUCV_TRANS_HIPER) {
+ headroom = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN;
linear = len;
} else {
- /* In nonlinear "classic" iucv skb,
- * reserve space for iucv_array
- */
- if (iucv->transport != AF_IUCV_TRANS_HIPER)
- headroom += sizeof(struct iucv_array) *
- (MAX_SKB_FRAGS + 1);
- linear = PAGE_SIZE - headroom;
+ if (len < PAGE_SIZE) {
+ linear = len;
+ } else {
+ /* In nonlinear "classic" iucv skb,
+ * reserve space for iucv_array
+ */
+ headroom = sizeof(struct iucv_array) *
+ (MAX_SKB_FRAGS + 1);
+ linear = PAGE_SIZE - headroom;
+ }
}
skb = sock_alloc_send_pskb(sk, headroom + linear, len - linear,
noblock, &err, 0);
diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c
index aa821cb..f092ac4 100644
--- a/net/l2tp/l2tp_ip6.c
+++ b/net/l2tp/l2tp_ip6.c
@@ -525,6 +525,7 @@
memset(&fl6, 0, sizeof(fl6));
fl6.flowi6_mark = sk->sk_mark;
+ fl6.flowi6_uid = sk->sk_uid;
ipc6.hlimit = -1;
ipc6.tclass = -1;
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index f6749dc..3b5fd41 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -315,11 +315,7 @@
mutex_lock(&sta->ampdu_mlme.mtx);
if (test_bit(tid, sta->ampdu_mlme.agg_session_valid)) {
- tid_agg_rx = rcu_dereference_protected(
- sta->ampdu_mlme.tid_rx[tid],
- lockdep_is_held(&sta->ampdu_mlme.mtx));
-
- if (tid_agg_rx->dialog_token == dialog_token) {
+ if (sta->ampdu_mlme.tid_rx_token[tid] == dialog_token) {
ht_dbg_ratelimited(sta->sdata,
"updated AddBA Req from %pM on tid %u\n",
sta->sta.addr, tid);
@@ -396,7 +392,6 @@
}
/* update data */
- tid_agg_rx->dialog_token = dialog_token;
tid_agg_rx->ssn = start_seq_num;
tid_agg_rx->head_seq_num = start_seq_num;
tid_agg_rx->buf_size = buf_size;
@@ -418,6 +413,7 @@
if (status == WLAN_STATUS_SUCCESS) {
__set_bit(tid, sta->ampdu_mlme.agg_session_valid);
__clear_bit(tid, sta->ampdu_mlme.unexpected_agg);
+ sta->ampdu_mlme.tid_rx_token[tid] = dialog_token;
}
mutex_unlock(&sta->ampdu_mlme.mtx);
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index a2fcdb4..14ec63a 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -205,7 +205,7 @@
p += scnprintf(p, sizeof(buf) + buf - p, "%02d", i);
p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", !!tid_rx);
p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x",
- tid_rx ? tid_rx->dialog_token : 0);
+ tid_rx ? sta->ampdu_mlme.tid_rx_token[i] : 0);
p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.3x",
tid_rx ? tid_rx->ssn : 0);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 7486f2d..1118c61 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -2510,7 +2510,7 @@
}
static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
- bool assoc)
+ bool assoc, bool abandon)
{
struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
@@ -2533,6 +2533,9 @@
mutex_lock(&sdata->local->mtx);
ieee80211_vif_release_channel(sdata);
mutex_unlock(&sdata->local->mtx);
+
+ if (abandon)
+ cfg80211_abandon_assoc(sdata->dev, assoc_data->bss);
}
kfree(assoc_data);
@@ -2762,7 +2765,7 @@
bssid, reason_code,
ieee80211_get_reason_code_string(reason_code));
- ieee80211_destroy_assoc_data(sdata, false);
+ ieee80211_destroy_assoc_data(sdata, false, true);
cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
return;
@@ -3167,14 +3170,14 @@
if (status_code != WLAN_STATUS_SUCCESS) {
sdata_info(sdata, "%pM denied association (code=%d)\n",
mgmt->sa, status_code);
- ieee80211_destroy_assoc_data(sdata, false);
+ ieee80211_destroy_assoc_data(sdata, false, false);
event.u.mlme.status = MLME_DENIED;
event.u.mlme.reason = status_code;
drv_event_callback(sdata->local, sdata, &event);
} else {
if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) {
/* oops -- internal error -- send timeout for now */
- ieee80211_destroy_assoc_data(sdata, false);
+ ieee80211_destroy_assoc_data(sdata, false, false);
cfg80211_assoc_timeout(sdata->dev, bss);
return;
}
@@ -3187,7 +3190,7 @@
* recalc after assoc_data is NULL but before associated
* is set can cause the interface to go idle
*/
- ieee80211_destroy_assoc_data(sdata, true);
+ ieee80211_destroy_assoc_data(sdata, true, false);
/* get uapsd queues configuration */
uapsd_queues = 0;
@@ -3886,7 +3889,7 @@
.u.mlme.status = MLME_TIMEOUT,
};
- ieee80211_destroy_assoc_data(sdata, false);
+ ieee80211_destroy_assoc_data(sdata, false, false);
cfg80211_assoc_timeout(sdata->dev, bss);
drv_event_callback(sdata->local, sdata, &event);
}
@@ -4025,7 +4028,7 @@
WLAN_REASON_DEAUTH_LEAVING,
false, frame_buf);
if (ifmgd->assoc_data)
- ieee80211_destroy_assoc_data(sdata, false);
+ ieee80211_destroy_assoc_data(sdata, false, true);
if (ifmgd->auth_data)
ieee80211_destroy_auth_data(sdata, false);
cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
@@ -4907,7 +4910,7 @@
IEEE80211_STYPE_DEAUTH,
req->reason_code, tx,
frame_buf);
- ieee80211_destroy_assoc_data(sdata, false);
+ ieee80211_destroy_assoc_data(sdata, false, true);
ieee80211_report_disconnect(sdata, frame_buf,
sizeof(frame_buf), true,
req->reason_code);
@@ -4982,7 +4985,7 @@
sdata_lock(sdata);
if (ifmgd->assoc_data) {
struct cfg80211_bss *bss = ifmgd->assoc_data->bss;
- ieee80211_destroy_assoc_data(sdata, false);
+ ieee80211_destroy_assoc_data(sdata, false, false);
cfg80211_assoc_timeout(sdata->dev, bss);
}
if (ifmgd->auth_data)
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index a47bbc9..2384b4a 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -3939,21 +3939,31 @@
u64_stats_update_end(&stats->syncp);
if (fast_rx->internal_forward) {
- struct sta_info *dsta = sta_info_get(rx->sdata, skb->data);
+ struct sk_buff *xmit_skb = NULL;
+ bool multicast = is_multicast_ether_addr(skb->data);
- if (dsta) {
+ if (multicast) {
+ xmit_skb = skb_copy(skb, GFP_ATOMIC);
+ } else if (sta_info_get(rx->sdata, skb->data)) {
+ xmit_skb = skb;
+ skb = NULL;
+ }
+
+ if (xmit_skb) {
/*
* Send to wireless media and increase priority by 256
* to keep the received priority instead of
* reclassifying the frame (see cfg80211_classify8021d).
*/
- skb->priority += 256;
- skb->protocol = htons(ETH_P_802_3);
- skb_reset_network_header(skb);
- skb_reset_mac_header(skb);
- dev_queue_xmit(skb);
- return true;
+ xmit_skb->priority += 256;
+ xmit_skb->protocol = htons(ETH_P_802_3);
+ skb_reset_network_header(xmit_skb);
+ skb_reset_mac_header(xmit_skb);
+ dev_queue_xmit(xmit_skb);
}
+
+ if (!skb)
+ return true;
}
/* deliver to local stack */
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index ed5fcb9..dd06ef0 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -184,7 +184,6 @@
* @ssn: Starting Sequence Number expected to be aggregated.
* @buf_size: buffer size for incoming A-MPDUs
* @timeout: reset timer value (in TUs).
- * @dialog_token: dialog token for aggregation session
* @rcu_head: RCU head used for freeing this struct
* @reorder_lock: serializes access to reorder buffer, see below.
* @auto_seq: used for offloaded BA sessions to automatically pick head_seq_and
@@ -213,7 +212,6 @@
u16 ssn;
u16 buf_size;
u16 timeout;
- u8 dialog_token;
bool auto_seq;
bool removed;
};
@@ -225,6 +223,7 @@
* to tid_tx[idx], which are protected by the sta spinlock)
* tid_start_tx is also protected by sta->lock.
* @tid_rx: aggregation info for Rx per TID -- RCU protected
+ * @tid_rx_token: dialog tokens for valid aggregation sessions
* @tid_rx_timer_expired: bitmap indicating on which TIDs the
* RX timer expired until the work for it runs
* @tid_rx_stop_requested: bitmap indicating which BA sessions per TID the
@@ -243,6 +242,7 @@
struct mutex mtx;
/* rx */
struct tid_ampdu_rx __rcu *tid_rx[IEEE80211_NUM_TIDS];
+ u8 tid_rx_token[IEEE80211_NUM_TIDS];
unsigned long tid_rx_timer_expired[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
unsigned long tid_rx_stop_requested[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
unsigned long agg_session_valid[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index bd5f4be..dd190ff 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -3262,7 +3262,7 @@
int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2);
int hw_headroom = sdata->local->hw.extra_tx_headroom;
struct ethhdr eth;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_tx_info *info;
struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
struct ieee80211_tx_data tx;
ieee80211_tx_result r;
@@ -3326,6 +3326,7 @@
memcpy(skb->data + fast_tx->da_offs, eth.h_dest, ETH_ALEN);
memcpy(skb->data + fast_tx->sa_offs, eth.h_source, ETH_ALEN);
+ info = IEEE80211_SKB_CB(skb);
memset(info, 0, sizeof(*info));
info->band = fast_tx->band;
info->control.vif = &sdata->vif;
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index 15fe976..5b77377 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -98,18 +98,19 @@
}
EXPORT_SYMBOL_GPL(mpls_pkt_too_big);
-static u32 mpls_multipath_hash(struct mpls_route *rt,
- struct sk_buff *skb, bool bos)
+static u32 mpls_multipath_hash(struct mpls_route *rt, struct sk_buff *skb)
{
struct mpls_entry_decoded dec;
+ unsigned int mpls_hdr_len = 0;
struct mpls_shim_hdr *hdr;
bool eli_seen = false;
int label_index;
u32 hash = 0;
- for (label_index = 0; label_index < MAX_MP_SELECT_LABELS && !bos;
+ for (label_index = 0; label_index < MAX_MP_SELECT_LABELS;
label_index++) {
- if (!pskb_may_pull(skb, sizeof(*hdr) * label_index))
+ mpls_hdr_len += sizeof(*hdr);
+ if (!pskb_may_pull(skb, mpls_hdr_len))
break;
/* Read and decode the current label */
@@ -134,37 +135,38 @@
eli_seen = true;
}
- bos = dec.bos;
- if (bos && pskb_may_pull(skb, sizeof(*hdr) * label_index +
- sizeof(struct iphdr))) {
+ if (!dec.bos)
+ continue;
+
+ /* found bottom label; does skb have room for a header? */
+ if (pskb_may_pull(skb, mpls_hdr_len + sizeof(struct iphdr))) {
const struct iphdr *v4hdr;
- v4hdr = (const struct iphdr *)(mpls_hdr(skb) +
- label_index);
+ v4hdr = (const struct iphdr *)(hdr + 1);
if (v4hdr->version == 4) {
hash = jhash_3words(ntohl(v4hdr->saddr),
ntohl(v4hdr->daddr),
v4hdr->protocol, hash);
} else if (v4hdr->version == 6 &&
- pskb_may_pull(skb, sizeof(*hdr) * label_index +
- sizeof(struct ipv6hdr))) {
+ pskb_may_pull(skb, mpls_hdr_len +
+ sizeof(struct ipv6hdr))) {
const struct ipv6hdr *v6hdr;
- v6hdr = (const struct ipv6hdr *)(mpls_hdr(skb) +
- label_index);
-
+ v6hdr = (const struct ipv6hdr *)(hdr + 1);
hash = __ipv6_addr_jhash(&v6hdr->saddr, hash);
hash = __ipv6_addr_jhash(&v6hdr->daddr, hash);
hash = jhash_1word(v6hdr->nexthdr, hash);
}
}
+
+ break;
}
return hash;
}
static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt,
- struct sk_buff *skb, bool bos)
+ struct sk_buff *skb)
{
int alive = ACCESS_ONCE(rt->rt_nhn_alive);
u32 hash = 0;
@@ -180,7 +182,7 @@
if (alive <= 0)
return NULL;
- hash = mpls_multipath_hash(rt, skb, bos);
+ hash = mpls_multipath_hash(rt, skb);
nh_index = hash % alive;
if (alive == rt->rt_nhn)
goto out;
@@ -278,17 +280,11 @@
hdr = mpls_hdr(skb);
dec = mpls_entry_decode(hdr);
- /* Pop the label */
- skb_pull(skb, sizeof(*hdr));
- skb_reset_network_header(skb);
-
- skb_orphan(skb);
-
rt = mpls_route_input_rcu(net, dec.label);
if (!rt)
goto drop;
- nh = mpls_select_multipath(rt, skb, dec.bos);
+ nh = mpls_select_multipath(rt, skb);
if (!nh)
goto drop;
@@ -297,6 +293,12 @@
if (!mpls_output_possible(out_dev))
goto drop;
+ /* Pop the label */
+ skb_pull(skb, sizeof(*hdr));
+ skb_reset_network_header(skb);
+
+ skb_orphan(skb);
+
if (skb_warn_if_lro(skb))
goto drop;
diff --git a/net/mpls/mpls_iptunnel.c b/net/mpls/mpls_iptunnel.c
index cf52cf3..bc9aaf5 100644
--- a/net/mpls/mpls_iptunnel.c
+++ b/net/mpls/mpls_iptunnel.c
@@ -218,6 +218,7 @@
.fill_encap = mpls_fill_encap_info,
.get_encap_size = mpls_encap_nlsize,
.cmp_encap = mpls_encap_cmp,
+ .owner = THIS_MODULE,
};
static int __init mpls_iptunnel_init(void)
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
index fecefa2..eab210b 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -514,7 +514,7 @@
int hooknum, nh_off, err = NF_ACCEPT;
nh_off = skb_network_offset(skb);
- skb_pull(skb, nh_off);
+ skb_pull_rcsum(skb, nh_off);
/* See HOOK2MANIP(). */
if (maniptype == NF_NAT_MANIP_SRC)
@@ -579,6 +579,7 @@
err = nf_nat_packet(ct, ctinfo, hooknum, skb);
push:
skb_push(skb, nh_off);
+ skb_postpush_rcsum(skb, skb->data, nh_off);
return err;
}
@@ -890,7 +891,7 @@
/* The conntrack module expects to be working at L3. */
nh_ofs = skb_network_offset(skb);
- skb_pull(skb, nh_ofs);
+ skb_pull_rcsum(skb, nh_ofs);
if (key->ip.frag != OVS_FRAG_TYPE_NONE) {
err = handle_fragments(net, key, info->zone.id, skb);
@@ -904,6 +905,7 @@
err = ovs_ct_lookup(net, key, info, skb);
skb_push(skb, nh_ofs);
+ skb_postpush_rcsum(skb, skb->data, nh_ofs);
if (err)
kfree_skb(skb);
return err;
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index dd23323..94e4a59 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1972,7 +1972,7 @@
{
*vnet_hdr = (const struct virtio_net_hdr) { 0 };
- if (virtio_net_hdr_from_skb(skb, vnet_hdr, vio_le()))
+ if (virtio_net_hdr_from_skb(skb, vnet_hdr, vio_le(), true))
BUG();
return 0;
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index f893d18..c6c2a93 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -903,8 +903,6 @@
goto err;
}
act->order = i;
- if (event == RTM_GETACTION)
- act->tcfa_refcnt++;
list_add_tail(&act->list, &actions);
}
@@ -917,7 +915,8 @@
return ret;
}
err:
- tcf_action_destroy(&actions, 0);
+ if (event != RTM_GETACTION)
+ tcf_action_destroy(&actions, 0);
return ret;
}
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index b05d4a2..c1a4b5d 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -148,13 +148,15 @@
unsigned long cl;
unsigned long fh;
int err;
- int tp_created = 0;
+ int tp_created;
if ((n->nlmsg_type != RTM_GETTFILTER) &&
!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
return -EPERM;
replay:
+ tp_created = 0;
+
err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL);
if (err < 0)
return err;
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 9044424..eee299b 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -149,10 +149,14 @@
switch (ip_tunnel_info_af(info)) {
case AF_INET:
+ skb_key.enc_control.addr_type =
+ FLOW_DISSECTOR_KEY_IPV4_ADDRS;
skb_key.enc_ipv4.src = key->u.ipv4.src;
skb_key.enc_ipv4.dst = key->u.ipv4.dst;
break;
case AF_INET6:
+ skb_key.enc_control.addr_type =
+ FLOW_DISSECTOR_KEY_IPV6_ADDRS;
skb_key.enc_ipv6.src = key->u.ipv6.src;
skb_key.enc_ipv6.dst = key->u.ipv6.dst;
break;
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index f23ad91..ca12aa3 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -4479,9 +4479,10 @@
rcu_read_lock();
transport = sctp_addrs_lookup_transport(net, laddr, paddr);
- if (!transport || !sctp_transport_hold(transport))
+ if (!transport || !sctp_transport_hold(transport)) {
+ rcu_read_unlock();
goto out;
-
+ }
rcu_read_unlock();
err = cb(transport, p);
sctp_transport_put(transport);
diff --git a/net/socket.c b/net/socket.c
index 73dc69f..5f4ec66 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -533,8 +533,22 @@
return used;
}
+int sockfs_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+ int err = simple_setattr(dentry, iattr);
+
+ if (!err && (iattr->ia_valid & ATTR_UID)) {
+ struct socket *sock = SOCKET_I(d_inode(dentry));
+
+ sock->sk->sk_uid = iattr->ia_uid;
+ }
+
+ return err;
+}
+
static const struct inode_operations sockfs_inode_ops = {
.listxattr = sockfs_listxattr,
+ .setattr = sockfs_setattr,
};
/**
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index 3dfd769..16cea00 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -541,9 +541,13 @@
return gss_new;
gss_msg = gss_add_msg(gss_new);
if (gss_msg == gss_new) {
- int res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg);
+ int res;
+ atomic_inc(&gss_msg->count);
+ res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg);
if (res) {
gss_unhash_msg(gss_new);
+ atomic_dec(&gss_msg->count);
+ gss_release_msg(gss_new);
gss_msg = ERR_PTR(res);
}
} else
@@ -836,6 +840,7 @@
warn_gssd();
gss_release_msg(gss_msg);
}
+ gss_release_msg(gss_msg);
}
static void gss_pipe_dentry_destroy(struct dentry *dir,
diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c
index dc6fb79a..25d9a9c 100644
--- a/net/sunrpc/auth_gss/gss_rpc_xdr.c
+++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c
@@ -260,7 +260,7 @@
if (!oa->data)
return -ENOMEM;
- creds = kmalloc(sizeof(struct svc_cred), GFP_KERNEL);
+ creds = kzalloc(sizeof(struct svc_cred), GFP_KERNEL);
if (!creds) {
kfree(oa->data);
return -ENOMEM;
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 45662d7..6fdffde 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1489,7 +1489,7 @@
case RPC_GSS_PROC_DESTROY:
if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
goto auth_err;
- rsci->h.expiry_time = get_seconds();
+ rsci->h.expiry_time = seconds_since_boot();
set_bit(CACHE_NEGATIVE, &rsci->h.flags);
if (resv->iov_len + 4 > PAGE_SIZE)
goto drop;
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 62a4827..b2ae4f1 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -336,6 +336,11 @@
static DEFINE_IDA(rpc_clids);
+void rpc_cleanup_clids(void)
+{
+ ida_destroy(&rpc_clids);
+}
+
static int rpc_alloc_clid(struct rpc_clnt *clnt)
{
int clid;
diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c
index ee5d3d2..3142f38 100644
--- a/net/sunrpc/sunrpc_syms.c
+++ b/net/sunrpc/sunrpc_syms.c
@@ -119,6 +119,7 @@
static void __exit
cleanup_sunrpc(void)
{
+ rpc_cleanup_clids();
rpcauth_remove_module();
cleanup_socket_xprt();
svc_cleanup_xprt_sock();
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index 3bc1d61..9c9db55 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -799,6 +799,8 @@
if (test_bit(XPT_CLOSE, &xprt->xpt_flags)) {
dprintk("svc_recv: found XPT_CLOSE\n");
+ if (test_and_clear_bit(XPT_KILL_TEMP, &xprt->xpt_flags))
+ xprt->xpt_ops->xpo_kill_temp_xprt(xprt);
svc_delete_xprt(xprt);
/* Leave XPT_BUSY set on the dead xprt: */
goto out;
@@ -1020,9 +1022,11 @@
le = to_be_closed.next;
list_del_init(le);
xprt = list_entry(le, struct svc_xprt, xpt_list);
- dprintk("svc_age_temp_xprts_now: closing %p\n", xprt);
- xprt->xpt_ops->xpo_kill_temp_xprt(xprt);
- svc_close_xprt(xprt);
+ set_bit(XPT_CLOSE, &xprt->xpt_flags);
+ set_bit(XPT_KILL_TEMP, &xprt->xpt_flags);
+ dprintk("svc_age_temp_xprts_now: queuing xprt %p for closing\n",
+ xprt);
+ svc_xprt_enqueue(xprt);
}
}
EXPORT_SYMBOL_GPL(svc_age_temp_xprts_now);
diff --git a/net/sunrpc/xprtrdma/frwr_ops.c b/net/sunrpc/xprtrdma/frwr_ops.c
index 26b26be..adbf52c 100644
--- a/net/sunrpc/xprtrdma/frwr_ops.c
+++ b/net/sunrpc/xprtrdma/frwr_ops.c
@@ -421,7 +421,7 @@
IB_ACCESS_REMOTE_WRITE | IB_ACCESS_LOCAL_WRITE :
IB_ACCESS_REMOTE_READ;
- DECR_CQCOUNT(&r_xprt->rx_ep);
+ rpcrdma_set_signaled(&r_xprt->rx_ep, ®_wr->wr);
rc = ib_post_send(ia->ri_id->qp, ®_wr->wr, &bad_wr);
if (rc)
goto out_senderr;
@@ -486,7 +486,7 @@
struct rpcrdma_ia *ia = &r_xprt->rx_ia;
struct rpcrdma_mw *mw, *tmp;
struct rpcrdma_frmr *f;
- int rc;
+ int count, rc;
dprintk("RPC: %s: req %p\n", __func__, req);
@@ -496,6 +496,7 @@
* a single ib_post_send() call.
*/
f = NULL;
+ count = 0;
invalidate_wrs = pos = prev = NULL;
list_for_each_entry(mw, &req->rl_registered, mw_list) {
if ((rep->rr_wc_flags & IB_WC_WITH_INVALIDATE) &&
@@ -505,6 +506,7 @@
}
pos = __frwr_prepare_linv_wr(mw);
+ count++;
if (!invalidate_wrs)
invalidate_wrs = pos;
@@ -523,7 +525,12 @@
f->fr_invwr.send_flags = IB_SEND_SIGNALED;
f->fr_cqe.done = frwr_wc_localinv_wake;
reinit_completion(&f->fr_linv_done);
- INIT_CQCOUNT(&r_xprt->rx_ep);
+
+ /* Initialize CQ count, since there is always a signaled
+ * WR being posted here. The new cqcount depends on how
+ * many SQEs are about to be consumed.
+ */
+ rpcrdma_init_cqcount(&r_xprt->rx_ep, count);
/* Transport disconnect drains the receive CQ before it
* replaces the QP. The RPC reply handler won't call us
diff --git a/net/sunrpc/xprtrdma/svc_rdma_backchannel.c b/net/sunrpc/xprtrdma/svc_rdma_backchannel.c
index 20027f8..6035c5a 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_backchannel.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_backchannel.c
@@ -359,6 +359,7 @@
out_fail:
xprt_rdma_free_addresses(xprt);
args->bc_xprt->xpt_bc_xprt = NULL;
+ args->bc_xprt->xpt_bc_xps = NULL;
xprt_put(xprt);
xprt_free(xprt);
return ERR_PTR(-EINVAL);
diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
index ad1df97..a47c9bd 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
@@ -348,8 +348,6 @@
atomic_inc(&rdma_stat_read);
return ret;
err:
- ib_dma_unmap_sg(xprt->sc_cm_id->device,
- frmr->sg, frmr->sg_nents, frmr->direction);
svc_rdma_put_context(ctxt, 0);
svc_rdma_put_frmr(xprt, frmr);
return ret;
diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
index ec74289..8da7f6a 100644
--- a/net/sunrpc/xprtrdma/verbs.c
+++ b/net/sunrpc/xprtrdma/verbs.c
@@ -223,8 +223,8 @@
cdata->inline_rsize = rsize;
if (wsize < cdata->inline_wsize)
cdata->inline_wsize = wsize;
- pr_info("rpcrdma: max send %u, max recv %u\n",
- cdata->inline_wsize, cdata->inline_rsize);
+ dprintk("RPC: %s: max send %u, max recv %u\n",
+ __func__, cdata->inline_wsize, cdata->inline_rsize);
rpcrdma_set_max_header_sizes(r_xprt);
}
@@ -532,7 +532,7 @@
ep->rep_cqinit = ep->rep_attr.cap.max_send_wr/2 - 1;
if (ep->rep_cqinit <= 2)
ep->rep_cqinit = 0; /* always signal? */
- INIT_CQCOUNT(ep);
+ rpcrdma_init_cqcount(ep, 0);
init_waitqueue_head(&ep->rep_connect_wait);
INIT_DELAYED_WORK(&ep->rep_connect_worker, rpcrdma_connect_worker);
@@ -1311,13 +1311,7 @@
dprintk("RPC: %s: posting %d s/g entries\n",
__func__, send_wr->num_sge);
- if (DECR_CQCOUNT(ep) > 0)
- send_wr->send_flags = 0;
- else { /* Provider must take a send completion every now and then */
- INIT_CQCOUNT(ep);
- send_wr->send_flags = IB_SEND_SIGNALED;
- }
-
+ rpcrdma_set_signaled(ep, send_wr);
rc = ib_post_send(ia->ri_id->qp, send_wr, &send_wr_fail);
if (rc)
goto out_postsend_err;
diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h
index 6e1bba3..f6ae1b2 100644
--- a/net/sunrpc/xprtrdma/xprt_rdma.h
+++ b/net/sunrpc/xprtrdma/xprt_rdma.h
@@ -95,8 +95,24 @@
struct delayed_work rep_connect_worker;
};
-#define INIT_CQCOUNT(ep) atomic_set(&(ep)->rep_cqcount, (ep)->rep_cqinit)
-#define DECR_CQCOUNT(ep) atomic_sub_return(1, &(ep)->rep_cqcount)
+static inline void
+rpcrdma_init_cqcount(struct rpcrdma_ep *ep, int count)
+{
+ atomic_set(&ep->rep_cqcount, ep->rep_cqinit - count);
+}
+
+/* To update send queue accounting, provider must take a
+ * send completion every now and then.
+ */
+static inline void
+rpcrdma_set_signaled(struct rpcrdma_ep *ep, struct ib_send_wr *send_wr)
+{
+ send_wr->send_flags = 0;
+ if (unlikely(atomic_sub_return(1, &ep->rep_cqcount) <= 0)) {
+ rpcrdma_init_cqcount(ep, 0);
+ send_wr->send_flags = IB_SEND_SIGNALED;
+ }
+}
/* Pre-allocate extra Work Requests for handling backward receives
* and sends. This is a fixed value because the Work Queues are
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 2358f26..2d03d5b 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -995,6 +995,7 @@
unsigned int hash;
struct unix_address *addr;
struct hlist_head *list;
+ struct path path = { NULL, NULL };
err = -EINVAL;
if (sunaddr->sun_family != AF_UNIX)
@@ -1010,9 +1011,20 @@
goto out;
addr_len = err;
+ if (sun_path[0]) {
+ umode_t mode = S_IFSOCK |
+ (SOCK_INODE(sock)->i_mode & ~current_umask());
+ err = unix_mknod(sun_path, mode, &path);
+ if (err) {
+ if (err == -EEXIST)
+ err = -EADDRINUSE;
+ goto out;
+ }
+ }
+
err = mutex_lock_interruptible(&u->bindlock);
if (err)
- goto out;
+ goto out_put;
err = -EINVAL;
if (u->addr)
@@ -1029,16 +1041,6 @@
atomic_set(&addr->refcnt, 1);
if (sun_path[0]) {
- struct path path;
- umode_t mode = S_IFSOCK |
- (SOCK_INODE(sock)->i_mode & ~current_umask());
- err = unix_mknod(sun_path, mode, &path);
- if (err) {
- if (err == -EEXIST)
- err = -EADDRINUSE;
- unix_release_addr(addr);
- goto out_up;
- }
addr->hash = UNIX_HASH_SIZE;
hash = d_real_inode(path.dentry)->i_ino & (UNIX_HASH_SIZE - 1);
spin_lock(&unix_table_lock);
@@ -1065,6 +1067,9 @@
spin_unlock(&unix_table_lock);
out_up:
mutex_unlock(&u->bindlock);
+out_put:
+ if (err)
+ path_put(&path);
out:
return err;
}
diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
index a53b3a1..62c056e 100644
--- a/net/vmw_vsock/virtio_transport_common.c
+++ b/net/vmw_vsock/virtio_transport_common.c
@@ -606,9 +606,9 @@
return 0;
pkt = virtio_transport_alloc_pkt(&info, 0,
- le32_to_cpu(pkt->hdr.dst_cid),
+ le64_to_cpu(pkt->hdr.dst_cid),
le32_to_cpu(pkt->hdr.dst_port),
- le32_to_cpu(pkt->hdr.src_cid),
+ le64_to_cpu(pkt->hdr.src_cid),
le32_to_cpu(pkt->hdr.src_port));
if (!pkt)
return -ENOMEM;
@@ -823,7 +823,7 @@
struct virtio_vsock_pkt_info info = {
.op = VIRTIO_VSOCK_OP_RESPONSE,
.type = VIRTIO_VSOCK_TYPE_STREAM,
- .remote_cid = le32_to_cpu(pkt->hdr.src_cid),
+ .remote_cid = le64_to_cpu(pkt->hdr.src_cid),
.remote_port = le32_to_cpu(pkt->hdr.src_port),
.reply = true,
};
@@ -863,9 +863,9 @@
child->sk_state = SS_CONNECTED;
vchild = vsock_sk(child);
- vsock_addr_init(&vchild->local_addr, le32_to_cpu(pkt->hdr.dst_cid),
+ vsock_addr_init(&vchild->local_addr, le64_to_cpu(pkt->hdr.dst_cid),
le32_to_cpu(pkt->hdr.dst_port));
- vsock_addr_init(&vchild->remote_addr, le32_to_cpu(pkt->hdr.src_cid),
+ vsock_addr_init(&vchild->remote_addr, le64_to_cpu(pkt->hdr.src_cid),
le32_to_cpu(pkt->hdr.src_port));
vsock_insert_connected(vchild);
@@ -904,9 +904,9 @@
struct sock *sk;
bool space_available;
- vsock_addr_init(&src, le32_to_cpu(pkt->hdr.src_cid),
+ vsock_addr_init(&src, le64_to_cpu(pkt->hdr.src_cid),
le32_to_cpu(pkt->hdr.src_port));
- vsock_addr_init(&dst, le32_to_cpu(pkt->hdr.dst_cid),
+ vsock_addr_init(&dst, le64_to_cpu(pkt->hdr.dst_cid),
le32_to_cpu(pkt->hdr.dst_port));
trace_virtio_transport_recv_pkt(src.svm_cid, src.svm_port,
diff --git a/net/wireless/core.h b/net/wireless/core.h
index f0c0c8a..5f5867f 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -410,6 +410,7 @@
void cfg80211_sme_deauth(struct wireless_dev *wdev);
void cfg80211_sme_auth_timeout(struct wireless_dev *wdev);
void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev);
+void cfg80211_sme_abandon_assoc(struct wireless_dev *wdev);
/* internal helpers */
bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher);
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index cbb48e2..76775a2 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -149,6 +149,18 @@
}
EXPORT_SYMBOL(cfg80211_assoc_timeout);
+void cfg80211_abandon_assoc(struct net_device *dev, struct cfg80211_bss *bss)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+
+ cfg80211_sme_abandon_assoc(wdev);
+
+ cfg80211_unhold_bss(bss_from_pub(bss));
+ cfg80211_put_bss(wiphy, bss);
+}
+EXPORT_SYMBOL(cfg80211_abandon_assoc);
+
void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 57a332f..92db80d 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -414,6 +414,7 @@
[NL80211_ATTR_NAN_MASTER_PREF] = { .type = NLA_U8 },
[NL80211_ATTR_NAN_DUAL] = { .type = NLA_U8 },
[NL80211_ATTR_NAN_FUNC] = { .type = NLA_NESTED },
+ [NL80211_ATTR_BSSID] = { .len = ETH_ALEN },
};
/* policy for the key attributes */
@@ -6677,7 +6678,20 @@
request->no_cck =
nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
- if (info->attrs[NL80211_ATTR_MAC])
+ /* Initial implementation used NL80211_ATTR_MAC to set the specific
+ * BSSID to scan for. This was problematic because that same attribute
+ * was already used for another purpose (local random MAC address). The
+ * NL80211_ATTR_BSSID attribute was added to fix this. For backwards
+ * compatibility with older userspace components, also use the
+ * NL80211_ATTR_MAC value here if it can be determined to be used for
+ * the specific BSSID use case instead of the random MAC address
+ * (NL80211_ATTR_SCAN_FLAGS is used to enable random MAC address use).
+ */
+ if (info->attrs[NL80211_ATTR_BSSID])
+ memcpy(request->bssid,
+ nla_data(info->attrs[NL80211_ATTR_BSSID]), ETH_ALEN);
+ else if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) &&
+ info->attrs[NL80211_ATTR_MAC])
memcpy(request->bssid, nla_data(info->attrs[NL80211_ATTR_MAC]),
ETH_ALEN);
else
@@ -14391,13 +14405,17 @@
list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
bool schedule_destroy_work = false;
- bool schedule_scan_stop = false;
struct cfg80211_sched_scan_request *sched_scan_req =
rcu_dereference(rdev->sched_scan_req);
if (sched_scan_req && notify->portid &&
- sched_scan_req->owner_nlportid == notify->portid)
- schedule_scan_stop = true;
+ sched_scan_req->owner_nlportid == notify->portid) {
+ sched_scan_req->owner_nlportid = 0;
+
+ if (rdev->ops->sched_scan_stop &&
+ rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
+ schedule_work(&rdev->sched_scan_stop_wk);
+ }
list_for_each_entry_rcu(wdev, &rdev->wiphy.wdev_list, list) {
cfg80211_mlme_unregister_socket(wdev, notify->portid);
@@ -14428,12 +14446,6 @@
spin_unlock(&rdev->destroy_list_lock);
schedule_work(&rdev->destroy_work);
}
- } else if (schedule_scan_stop) {
- sched_scan_req->owner_nlportid = 0;
-
- if (rdev->ops->sched_scan_stop &&
- rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
- schedule_work(&rdev->sched_scan_stop_wk);
}
}
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 5cf182e..8ae2e20 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -39,6 +39,7 @@
CFG80211_CONN_ASSOCIATING,
CFG80211_CONN_ASSOC_FAILED,
CFG80211_CONN_DEAUTH,
+ CFG80211_CONN_ABANDON,
CFG80211_CONN_CONNECTED,
} state;
u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
@@ -229,6 +230,8 @@
cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
NULL, 0,
WLAN_REASON_DEAUTH_LEAVING, false);
+ /* fall through */
+ case CFG80211_CONN_ABANDON:
/* free directly, disconnected event already sent */
cfg80211_sme_free(wdev);
return 0;
@@ -446,6 +449,17 @@
schedule_work(&rdev->conn_work);
}
+void cfg80211_sme_abandon_assoc(struct wireless_dev *wdev)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+
+ if (!wdev->conn)
+ return;
+
+ wdev->conn->state = CFG80211_CONN_ABANDON;
+ schedule_work(&rdev->conn_work);
+}
+
static int cfg80211_sme_get_conn_ies(struct wireless_dev *wdev,
const u8 *ies, size_t ies_len,
const u8 **out_ies, size_t *out_ies_len)
diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h
index 950fd2e..12262c0 100644
--- a/scripts/gcc-plugins/gcc-common.h
+++ b/scripts/gcc-plugins/gcc-common.h
@@ -39,6 +39,9 @@
#include "hash-map.h"
#endif
+#if BUILDING_GCC_VERSION >= 7000
+#include "memmodel.h"
+#endif
#include "emit-rtl.h"
#include "debug.h"
#include "target.h"
@@ -91,6 +94,9 @@
#include "tree-ssa-alias.h"
#include "tree-ssa.h"
#include "stringpool.h"
+#if BUILDING_GCC_VERSION >= 7000
+#include "tree-vrp.h"
+#endif
#include "tree-ssanames.h"
#include "print-tree.h"
#include "tree-eh.h"
@@ -287,6 +293,22 @@
return NULL;
}
+static inline bool cgraph_for_node_and_aliases(cgraph_node_ptr node, bool (*callback)(cgraph_node_ptr, void *), void *data, bool include_overwritable)
+{
+ cgraph_node_ptr alias;
+
+ if (callback(node, data))
+ return true;
+
+ for (alias = node->same_body; alias; alias = alias->next) {
+ if (include_overwritable || cgraph_function_body_availability(alias) > AVAIL_OVERWRITABLE)
+ if (cgraph_for_node_and_aliases(alias, callback, data, include_overwritable))
+ return true;
+ }
+
+ return false;
+}
+
#define FOR_EACH_FUNCTION_WITH_GIMPLE_BODY(node) \
for ((node) = cgraph_first_function_with_gimple_body(); (node); \
(node) = cgraph_next_function_with_gimple_body(node))
@@ -399,6 +421,7 @@
typedef union gimple_statement_d gcall;
typedef union gimple_statement_d gcond;
typedef union gimple_statement_d gdebug;
+typedef union gimple_statement_d ggoto;
typedef union gimple_statement_d gphi;
typedef union gimple_statement_d greturn;
@@ -452,6 +475,16 @@
return stmt;
}
+static inline ggoto *as_a_ggoto(gimple stmt)
+{
+ return stmt;
+}
+
+static inline const ggoto *as_a_const_ggoto(const_gimple stmt)
+{
+ return stmt;
+}
+
static inline gphi *as_a_gphi(gimple stmt)
{
return stmt;
@@ -496,6 +529,14 @@
typedef struct rtx_def rtx_insn;
+static inline const char *get_decl_section_name(const_tree decl)
+{
+ if (DECL_SECTION_NAME(decl) == NULL_TREE)
+ return NULL;
+
+ return TREE_STRING_POINTER(DECL_SECTION_NAME(decl));
+}
+
static inline void set_decl_section_name(tree node, const char *value)
{
if (value)
@@ -511,6 +552,7 @@
typedef struct gimple_statement_call gcall;
typedef struct gimple_statement_base gcond;
typedef struct gimple_statement_base gdebug;
+typedef struct gimple_statement_base ggoto;
typedef struct gimple_statement_phi gphi;
typedef struct gimple_statement_base greturn;
@@ -564,6 +606,16 @@
return stmt;
}
+static inline ggoto *as_a_ggoto(gimple stmt)
+{
+ return stmt;
+}
+
+static inline const ggoto *as_a_const_ggoto(const_gimple stmt)
+{
+ return stmt;
+}
+
static inline gphi *as_a_gphi(gimple stmt)
{
return as_a<gphi>(stmt);
@@ -611,6 +663,11 @@
#define INSN_DELETED_P(insn) (insn)->deleted()
+static inline const char *get_decl_section_name(const_tree decl)
+{
+ return DECL_SECTION_NAME(decl);
+}
+
/* symtab/cgraph related */
#define debug_cgraph_node(node) (node)->debug()
#define cgraph_get_node(decl) cgraph_node::get(decl)
@@ -619,6 +676,7 @@
#define cgraph_n_nodes symtab->cgraph_count
#define cgraph_max_uid symtab->cgraph_max_uid
#define varpool_get_node(decl) varpool_node::get(decl)
+#define dump_varpool_node(file, node) (node)->dump(file)
#define cgraph_create_edge(caller, callee, call_stmt, count, freq, nest) \
(caller)->create_edge((callee), (call_stmt), (count), (freq))
@@ -674,6 +732,11 @@
return node->get_alias_target();
}
+static inline bool cgraph_for_node_and_aliases(cgraph_node_ptr node, bool (*callback)(cgraph_node_ptr, void *), void *data, bool include_overwritable)
+{
+ return node->call_for_symbol_thunks_and_aliases(callback, data, include_overwritable);
+}
+
static inline struct cgraph_node_hook_list *cgraph_add_function_insertion_hook(cgraph_node_hook hook, void *data)
{
return symtab->add_cgraph_insertion_hook(hook, data);
@@ -731,6 +794,13 @@
template <>
template <>
+inline bool is_a_helper<const ggoto *>::test(const_gimple gs)
+{
+ return gs->code == GIMPLE_GOTO;
+}
+
+template <>
+template <>
inline bool is_a_helper<const greturn *>::test(const_gimple gs)
{
return gs->code == GIMPLE_RETURN;
@@ -766,6 +836,16 @@
return as_a<const gcall *>(stmt);
}
+static inline ggoto *as_a_ggoto(gimple stmt)
+{
+ return as_a<ggoto *>(stmt);
+}
+
+static inline const ggoto *as_a_const_ggoto(const_gimple stmt)
+{
+ return as_a<const ggoto *>(stmt);
+}
+
static inline gphi *as_a_gphi(gimple stmt)
{
return as_a<gphi *>(stmt);
@@ -828,4 +908,9 @@
#define debug_gimple_stmt(s) debug_gimple_stmt(CONST_CAST_GIMPLE(s))
#endif
+#if BUILDING_GCC_VERSION >= 7000
+#define get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep, keep_aligning) \
+ get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep)
+#endif
+
#endif
diff --git a/scripts/gcc-plugins/latent_entropy_plugin.c b/scripts/gcc-plugins/latent_entropy_plugin.c
index 8160f1c..dff390f 100644
--- a/scripts/gcc-plugins/latent_entropy_plugin.c
+++ b/scripts/gcc-plugins/latent_entropy_plugin.c
@@ -328,9 +328,9 @@
op = LROTATE_EXPR;
/*
* This code limits the value of random_const to
- * the size of a wide int for the rotation
+ * the size of a long for the rotation
*/
- random_const &= HOST_BITS_PER_WIDE_INT - 1;
+ random_const %= TYPE_PRECISION(long_unsigned_type_node);
break;
}
diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c
index 8275f0e5..4b2f44c 100644
--- a/scripts/kconfig/nconf.gui.c
+++ b/scripts/kconfig/nconf.gui.c
@@ -364,12 +364,14 @@
WINDOW *prompt_win;
WINDOW *form_win;
PANEL *panel;
- int i, x, y;
+ int i, x, y, lines, columns, win_lines, win_cols;
int res = -1;
int cursor_position = strlen(init);
int cursor_form_win;
char *result = *resultp;
+ getmaxyx(stdscr, lines, columns);
+
if (strlen(init)+1 > *result_len) {
*result_len = strlen(init)+1;
*resultp = result = realloc(result, *result_len);
@@ -386,14 +388,19 @@
if (title)
prompt_width = max(prompt_width, strlen(title));
+ win_lines = min(prompt_lines+6, lines-2);
+ win_cols = min(prompt_width+7, columns-2);
+ prompt_lines = max(win_lines-6, 0);
+ prompt_width = max(win_cols-7, 0);
+
/* place dialog in middle of screen */
- y = (getmaxy(stdscr)-(prompt_lines+4))/2;
- x = (getmaxx(stdscr)-(prompt_width+4))/2;
+ y = (lines-win_lines)/2;
+ x = (columns-win_cols)/2;
strncpy(result, init, *result_len);
/* create the windows */
- win = newwin(prompt_lines+6, prompt_width+7, y, x);
+ win = newwin(win_lines, win_cols, y, x);
prompt_win = derwin(win, prompt_lines+1, prompt_width, 2, 2);
form_win = derwin(win, 1, prompt_width, prompt_lines+3, 2);
keypad(form_win, TRUE);
diff --git a/scripts/package/builddeb b/scripts/package/builddeb
index 8ea9fd2..3c575cd0 100755
--- a/scripts/package/builddeb
+++ b/scripts/package/builddeb
@@ -51,7 +51,7 @@
debarch=hppa ;;
mips*)
debarch=mips$(grep -q CPU_LITTLE_ENDIAN=y $KCONFIG_CONFIG && echo el || true) ;;
- arm64)
+ aarch64|arm64)
debarch=arm64 ;;
arm*)
if grep -q CONFIG_AEABI=y $KCONFIG_CONFIG; then
diff --git a/security/inode.c b/security/inode.c
index c83db05..b4531f2 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -100,7 +100,7 @@
dir = d_inode(parent);
inode_lock(dir);
- dentry = lookup_one_len(name, parent, strlen(name));
+ dentry = lookup_one_len2(name, mount, parent, strlen(name));
if (IS_ERR(dentry))
goto out;
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index c07a384..3df4690 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -401,7 +401,7 @@
const char *cause = valid_policy ? "completed" : "failed";
if ((file->f_flags & O_ACCMODE) == O_RDONLY)
- return 0;
+ return seq_release(inode, file);
if (valid_policy && ima_check_policy() < 0) {
cause = "failed";
diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c
index 4ad3bd7..f1657a4 100644
--- a/sound/firewire/tascam/tascam-stream.c
+++ b/sound/firewire/tascam/tascam-stream.c
@@ -343,7 +343,7 @@
if (err < 0)
amdtp_stream_destroy(&tscm->rx_stream);
- return 0;
+ return err;
}
/* At bus reset, streaming is stopped and some registers are clear. */
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
index 7f57a14..a03cf68 100644
--- a/sound/pci/hda/hda_auto_parser.c
+++ b/sound/pci/hda/hda_auto_parser.c
@@ -884,6 +884,8 @@
}
EXPORT_SYMBOL_GPL(snd_hda_apply_fixup);
+#define IGNORE_SEQ_ASSOC (~(AC_DEFCFG_SEQUENCE | AC_DEFCFG_DEF_ASSOC))
+
static bool pin_config_match(struct hda_codec *codec,
const struct hda_pintbl *pins)
{
@@ -901,7 +903,7 @@
for (; t_pins->nid; t_pins++) {
if (t_pins->nid == nid) {
found = 1;
- if (t_pins->val == cfg)
+ if ((t_pins->val & IGNORE_SEQ_ASSOC) == (cfg & IGNORE_SEQ_ASSOC))
break;
else if ((cfg & 0xf0000000) == 0x40000000 && (t_pins->val & 0xf0000000) == 0x40000000)
break;
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index ad06866..11b9b2f 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -780,6 +780,7 @@
static const struct snd_pci_quirk ca0132_quirks[] = {
SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15 2015", QUIRK_ALIENWARE),
SND_PCI_QUIRK(0x1028, 0x0688, "Alienware 17 2015", QUIRK_ALIENWARE),
+ SND_PCI_QUIRK(0x1028, 0x0708, "Alienware 15 R2 2016", QUIRK_ALIENWARE),
{}
};
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index ed62748..c15c51b 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -262,6 +262,7 @@
CXT_FIXUP_CAP_MIX_AMP_5047,
CXT_FIXUP_MUTE_LED_EAPD,
CXT_FIXUP_HP_SPECTRE,
+ CXT_FIXUP_HP_GATE_MIC,
};
/* for hda_fixup_thinkpad_acpi() */
@@ -633,6 +634,17 @@
(1 << AC_AMPCAP_MUTE_SHIFT));
}
+static void cxt_fixup_hp_gate_mic_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ /* the mic pin (0x19) doesn't give an unsolicited event;
+ * probe the mic pin together with the headphone pin (0x16)
+ */
+ if (action == HDA_FIXUP_ACT_PROBE)
+ snd_hda_jack_set_gating_jack(codec, 0x19, 0x16);
+}
+
/* ThinkPad X200 & co with cxt5051 */
static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
{ 0x16, 0x042140ff }, /* HP (seq# overridden) */
@@ -774,6 +786,10 @@
{ }
}
},
+ [CXT_FIXUP_HP_GATE_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_hp_gate_mic_jack,
+ },
};
static const struct snd_pci_quirk cxt5045_fixups[] = {
@@ -824,6 +840,7 @@
SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_ASPIRE_DMIC),
SND_PCI_QUIRK(0x1025, 0x054f, "Acer Aspire 4830T", CXT_FIXUP_ASPIRE_DMIC),
SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE),
+ SND_PCI_QUIRK(0x103c, 0x8115, "HP Z1 Gen3", CXT_FIXUP_HP_GATE_MIC),
SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index ea81c08..758ac86 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2230,6 +2230,7 @@
SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC),
SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601),
SND_PCI_QUIRK(0x1043, 0x84bc, "ASUS ET2700", ALC887_FIXUP_ASUS_BASS),
+ SND_PCI_QUIRK(0x1043, 0x8691, "ASUS ROG Ranger VIII", ALC882_FIXUP_GPIO3),
SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT),
SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP),
SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP),
@@ -5917,6 +5918,9 @@
{0x12, 0x90a60180},
{0x14, 0x90170120},
{0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x1b, 0x01011020},
+ {0x21, 0x02211010}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60160},
{0x14, 0x90170120},
@@ -6940,6 +6944,7 @@
SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16),
SND_PCI_QUIRK(0x1043, 0x177d, "ASUS N551", ALC668_FIXUP_ASUS_Nx51),
SND_PCI_QUIRK(0x1043, 0x17bd, "ASUS N751", ALC668_FIXUP_ASUS_Nx51),
+ SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71SL", ALC662_FIXUP_ASUS_MODE8),
SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16),
SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_MODE4_CHMAP),
SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),
diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
index 25c6d87..f5a8050 100644
--- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c
+++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
@@ -771,6 +771,9 @@
struct sst_data *drv = dev_get_drvdata(dev);
struct snd_soc_pcm_runtime *rtd;
+ if (!drv->soc_card)
+ return 0;
+
/* suspend all pcms first */
snd_soc_suspend(drv->soc_card->dev);
snd_soc_poweroff(drv->soc_card->dev);
@@ -793,6 +796,9 @@
struct sst_data *drv = dev_get_drvdata(dev);
struct snd_soc_pcm_runtime *rtd;
+ if (!drv->soc_card)
+ return;
+
/* restart SSPs */
list_for_each_entry(rtd, &drv->soc_card->rtd_list, list) {
struct snd_soc_dai *dai = rtd->cpu_dai;
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index 56056ed..16c94c4 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -44,6 +44,7 @@
struct cht_mc_private {
struct snd_soc_jack jack;
struct cht_acpi_card *acpi_card;
+ char codec_name[16];
};
static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
@@ -354,7 +355,6 @@
int i;
struct cht_mc_private *drv;
struct snd_soc_card *card = snd_soc_cards[0].soc_card;
- char codec_name[16];
struct sst_acpi_mach *mach;
const char *i2c_name = NULL;
int dai_index = 0;
@@ -374,12 +374,12 @@
}
card->dev = &pdev->dev;
mach = card->dev->platform_data;
- sprintf(codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
+ sprintf(drv->codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
/* set correct codec name */
for (i = 0; i < ARRAY_SIZE(cht_dailink); i++)
if (!strcmp(card->dai_link[i].codec_name, "i2c-10EC5645:00")) {
- card->dai_link[i].codec_name = kstrdup(codec_name, GFP_KERNEL);
+ card->dai_link[i].codec_name = drv->codec_name;
dai_index = i;
}
diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c
index 8dc0303..ea162fb 100644
--- a/sound/soc/intel/skylake/skl-sst-utils.c
+++ b/sound/soc/intel/skylake/skl-sst-utils.c
@@ -179,7 +179,7 @@
index = ffz(mask_val);
pvt_id = index + word1_mask + word2_mask;
if (pvt_id <= (max_inst - 1)) {
- *val |= 1 << (index + word1_mask);
+ *val |= 1ULL << (index + word1_mask);
return pvt_id;
}
}
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index b392e51..420d200 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -78,6 +78,9 @@
dma_ch = 0;
if (v->alloc_dma_channel)
dma_ch = v->alloc_dma_channel(drvdata, dir);
+ else
+ dma_ch = 0;
+
if (dma_ch < 0)
return dma_ch;
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 7825bff..85324e6 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -1029,12 +1029,13 @@
static int samsung_i2s_dai_remove(struct snd_soc_dai *dai)
{
struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai);
+ unsigned long flags;
if (!is_secondary(i2s)) {
if (i2s->quirks & QUIRK_NEED_RSTCLR) {
- spin_lock(i2s->lock);
+ spin_lock_irqsave(i2s->lock, flags);
writel(0, i2s->addr + I2SCON);
- spin_unlock(i2s->lock);
+ spin_unlock_irqrestore(i2s->lock, flags);
}
}
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 0d6aa49..90a4e68 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -206,7 +206,6 @@
if (! snd_usb_parse_audio_interface(chip, interface)) {
usb_set_interface(dev, interface, 0); /* reset the current interface */
usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
- return -EINVAL;
}
return 0;
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index c470251..c5251aa 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -534,6 +534,11 @@
alive, ep->ep_num);
clear_bit(EP_FLAG_STOPPING, &ep->flags);
+ ep->data_subs = NULL;
+ ep->sync_slave = NULL;
+ ep->retire_data_urb = NULL;
+ ep->prepare_data_urb = NULL;
+
return 0;
}
@@ -898,9 +903,7 @@
/**
* snd_usb_endpoint_start: start an snd_usb_endpoint
*
- * @ep: the endpoint to start
- * @can_sleep: flag indicating whether the operation is executed in
- * non-atomic context
+ * @ep: the endpoint to start
*
* A call to this function will increment the use count of the endpoint.
* In case it is not already running, the URBs for this endpoint will be
@@ -910,7 +913,7 @@
*
* Returns an error if the URB submission failed, 0 in all other cases.
*/
-int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep)
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
{
int err;
unsigned int i;
@@ -924,8 +927,6 @@
/* just to be sure */
deactivate_urbs(ep, false);
- if (can_sleep)
- wait_clear_urbs(ep);
ep->active_mask = 0;
ep->unlink_mask = 0;
@@ -1006,10 +1007,6 @@
if (--ep->use_count == 0) {
deactivate_urbs(ep, false);
- ep->data_subs = NULL;
- ep->sync_slave = NULL;
- ep->retire_data_urb = NULL;
- ep->prepare_data_urb = NULL;
set_bit(EP_FLAG_STOPPING, &ep->flags);
}
}
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index 6428392..584f295 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -18,7 +18,7 @@
struct audioformat *fmt,
struct snd_usb_endpoint *sync_ep);
-int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep);
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep);
int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
diff --git a/sound/usb/hiface/pcm.c b/sound/usb/hiface/pcm.c
index 2c44139..33db205 100644
--- a/sound/usb/hiface/pcm.c
+++ b/sound/usb/hiface/pcm.c
@@ -445,6 +445,8 @@
mutex_lock(&rt->stream_mutex);
+ hiface_pcm_stream_stop(rt);
+
sub->dma_off = 0;
sub->period_off = 0;
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 4df74b3..932ce3e 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -932,9 +932,10 @@
case USB_ID(0x046d, 0x0826): /* HD Webcam c525 */
case USB_ID(0x046d, 0x08ca): /* Logitech Quickcam Fusion */
case USB_ID(0x046d, 0x0991):
+ case USB_ID(0x046d, 0x09a2): /* QuickCam Communicate Deluxe/S7500 */
/* Most audio usb devices lie about volume resolution.
* Most Logitech webcams have res = 384.
- * Proboly there is some logitech magic behind this number --fishor
+ * Probably there is some logitech magic behind this number --fishor
*/
if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
usb_audio_info(chip,
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 44d178e..48afae0 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -218,7 +218,7 @@
}
}
-static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep)
+static int start_endpoints(struct snd_usb_substream *subs)
{
int err;
@@ -231,7 +231,7 @@
dev_dbg(&subs->dev->dev, "Starting data EP @%p\n", ep);
ep->data_subs = subs;
- err = snd_usb_endpoint_start(ep, can_sleep);
+ err = snd_usb_endpoint_start(ep);
if (err < 0) {
clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags);
return err;
@@ -260,7 +260,7 @@
dev_dbg(&subs->dev->dev, "Starting sync EP @%p\n", ep);
ep->sync_slave = subs->data_endpoint;
- err = snd_usb_endpoint_start(ep, can_sleep);
+ err = snd_usb_endpoint_start(ep);
if (err < 0) {
clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
return err;
@@ -839,7 +839,7 @@
/* for playback, submit the URBs now; otherwise, the first hwptr_done
* updates for all URBs would happen at the same time when starting */
if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK)
- ret = start_endpoints(subs, true);
+ ret = start_endpoints(subs);
unlock:
snd_usb_unlock_shutdown(subs->stream->chip);
@@ -1655,7 +1655,7 @@
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- err = start_endpoints(subs, false);
+ err = start_endpoints(subs);
if (err < 0)
return err;
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 2782155..93bb14e7 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1135,6 +1135,7 @@
case USB_ID(0x045E, 0x076F): /* MS Lifecam HD-6000 */
case USB_ID(0x045E, 0x0772): /* MS Lifecam Studio */
case USB_ID(0x045E, 0x0779): /* MS Lifecam HD-3000 */
+ case USB_ID(0x047F, 0x02F7): /* Plantronics BT-600 */
case USB_ID(0x047F, 0x0415): /* Plantronics BT-300 */
case USB_ID(0x047F, 0xAA05): /* Plantronics DA45 */
case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index 72edf83..cffdd9c 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -366,7 +366,7 @@
endif
ifdef PERF_HAVE_JITDUMP
- ifndef NO_DWARF
+ ifndef NO_LIBELF
$(call detected,CONFIG_JITDUMP)
CFLAGS += -DHAVE_JITDUMP
endif
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c
index d1ce29b..cd7bc4d 100644
--- a/tools/perf/builtin-mem.c
+++ b/tools/perf/builtin-mem.c
@@ -70,8 +70,8 @@
OPT_UINTEGER(0, "ldlat", &perf_mem_events__loads_ldlat, "mem-loads latency"),
OPT_INCR('v', "verbose", &verbose,
"be more verbose (show counter open errors, etc)"),
- OPT_BOOLEAN('U', "--all-user", &all_user, "collect only user level data"),
- OPT_BOOLEAN('K', "--all-kernel", &all_kernel, "collect only kernel level data"),
+ OPT_BOOLEAN('U', "all-user", &all_user, "collect only user level data"),
+ OPT_BOOLEAN('K', "all-kernel", &all_kernel, "collect only kernel level data"),
OPT_END()
};
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index c298bd3..21f8a81 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -1452,7 +1452,7 @@
duration = sample->time - ttrace->entry_time;
- printed = trace__fprintf_entry_head(trace, trace->current, duration, sample->time, trace->output);
+ printed = trace__fprintf_entry_head(trace, trace->current, duration, ttrace->entry_time, trace->output);
printed += fprintf(trace->output, "%-70s) ...\n", ttrace->entry_str);
ttrace->entry_pending = false;
@@ -1499,7 +1499,7 @@
if (sc->is_exit) {
if (!(trace->duration_filter || trace->summary_only || trace->min_stack)) {
- trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output);
+ trace__fprintf_entry_head(trace, thread, 1, ttrace->entry_time, trace->output);
fprintf(trace->output, "%-70s)\n", ttrace->entry_str);
}
} else {
@@ -1592,7 +1592,7 @@
if (trace->summary_only)
goto out;
- trace__fprintf_entry_head(trace, thread, duration, sample->time, trace->output);
+ trace__fprintf_entry_head(trace, thread, duration, ttrace->entry_time, trace->output);
if (ttrace->entry_pending) {
fprintf(trace->output, "%-70s", ttrace->entry_str);
diff --git a/tools/perf/trace/beauty/mmap.c b/tools/perf/trace/beauty/mmap.c
index fd710ab..af1cfde 100644
--- a/tools/perf/trace/beauty/mmap.c
+++ b/tools/perf/trace/beauty/mmap.c
@@ -42,7 +42,9 @@
P_MMAP_FLAG(SHARED);
P_MMAP_FLAG(PRIVATE);
+#ifdef MAP_32BIT
P_MMAP_FLAG(32BIT);
+#endif
P_MMAP_FLAG(ANONYMOUS);
P_MMAP_FLAG(DENYWRITE);
P_MMAP_FLAG(EXECUTABLE);
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index eb60e61..1dc67ef 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -120,7 +120,7 @@
ifdef CONFIG_JITDUMP
libperf-$(CONFIG_LIBELF) += jitdump.o
libperf-$(CONFIG_LIBELF) += genelf.o
-libperf-$(CONFIG_LIBELF) += genelf_debug.o
+libperf-$(CONFIG_DWARF) += genelf_debug.o
endif
CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index aeb5a44..430d039 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -593,7 +593,8 @@
pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr));
- if (addr < sym->start || addr >= sym->end) {
+ if ((addr < sym->start || addr >= sym->end) &&
+ (addr != sym->end || sym->start != sym->end)) {
pr_debug("%s(%d): ERANGE! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 "\n",
__func__, __LINE__, sym->name, sym->start, addr, sym->end);
return -ERANGE;
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 07fd30b..ae58b49 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -193,7 +193,6 @@
if (!strcmp(var, "record-mode"))
return parse_callchain_record_opt(value, &callchain_param);
-#ifdef HAVE_DWARF_UNWIND_SUPPORT
if (!strcmp(var, "dump-size")) {
unsigned long size = 0;
int ret;
@@ -203,7 +202,6 @@
return ret;
}
-#endif
if (!strcmp(var, "print-type"))
return parse_callchain_mode(value);
if (!strcmp(var, "order"))
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 13e7554..47cfd10 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -11,11 +11,7 @@
#define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace):\n\n"
-#ifdef HAVE_DWARF_UNWIND_SUPPORT
# define RECORD_MODE_HELP HELP_PAD "record_mode:\tcall graph recording mode (fp|dwarf|lbr)\n"
-#else
-# define RECORD_MODE_HELP HELP_PAD "record_mode:\tcall graph recording mode (fp|lbr)\n"
-#endif
#define RECORD_SIZE_HELP \
HELP_PAD "record_size:\tif record_mode is 'dwarf', max size of stack recording (<bytes>)\n" \
diff --git a/tools/perf/util/genelf.c b/tools/perf/util/genelf.c
index c1ef805..14a73ac 100644
--- a/tools/perf/util/genelf.c
+++ b/tools/perf/util/genelf.c
@@ -19,7 +19,9 @@
#include <limits.h>
#include <fcntl.h>
#include <err.h>
+#ifdef HAVE_DWARF_SUPPORT
#include <dwarf.h>
+#endif
#include "perf.h"
#include "genelf.h"
@@ -157,7 +159,7 @@
int
jit_write_elf(int fd, uint64_t load_addr, const char *sym,
const void *code, int csize,
- void *debug, int nr_debug_entries)
+ void *debug __maybe_unused, int nr_debug_entries __maybe_unused)
{
Elf *e;
Elf_Data *d;
@@ -386,11 +388,14 @@
shdr->sh_size = sizeof(bnote);
shdr->sh_entsize = 0;
+#ifdef HAVE_DWARF_SUPPORT
if (debug && nr_debug_entries) {
retval = jit_add_debug_info(e, load_addr, debug, nr_debug_entries);
if (retval)
goto error;
- } else {
+ } else
+#endif
+ {
if (elf_update(e, ELF_C_WRITE) < 0) {
warnx("elf_update 4 failed");
goto error;
diff --git a/tools/perf/util/genelf.h b/tools/perf/util/genelf.h
index 2fbeb59..5c933ac 100644
--- a/tools/perf/util/genelf.h
+++ b/tools/perf/util/genelf.h
@@ -4,8 +4,10 @@
/* genelf.c */
int jit_write_elf(int fd, uint64_t code_addr, const char *sym,
const void *code, int csize, void *debug, int nr_debug_entries);
+#ifdef HAVE_DWARF_SUPPORT
/* genelf_debug.c */
int jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries);
+#endif
#if defined(__arm__)
#define GEN_ELF_ARCH EM_ARM
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index aecff69..f7b35e1 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1459,7 +1459,8 @@
* Read the build id if possible. This is required for
* DSO_BINARY_TYPE__BUILDID_DEBUGINFO to work
*/
- if (is_regular_file(dso->long_name) &&
+ if (!dso->has_build_id &&
+ is_regular_file(dso->long_name) &&
filename__read_build_id(dso->long_name, build_id, BUILD_ID_SIZE) > 0)
dso__set_build_id(dso, build_id);
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c
index 9df6105..a2fd6e7 100644
--- a/tools/perf/util/trace-event-scripting.c
+++ b/tools/perf/util/trace-event-scripting.c
@@ -95,7 +95,8 @@
if (err)
die("error registering py script extension");
- scripting_context = malloc(sizeof(struct scripting_context));
+ if (scripting_context == NULL)
+ scripting_context = malloc(sizeof(*scripting_context));
}
#ifdef NO_LIBPYTHON
@@ -159,7 +160,8 @@
if (err)
die("error registering pl script extension");
- scripting_context = malloc(sizeof(struct scripting_context));
+ if (scripting_context == NULL)
+ scripting_context = malloc(sizeof(*scripting_context));
}
#ifdef NO_LIBPERL
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index f770dba..a899ef81 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -87,7 +87,7 @@
done;
@# Ask all targets to emit their test scripts
- echo "#!/bin/bash" > $(ALL_SCRIPT)
+ echo "#!/bin/sh" > $(ALL_SCRIPT)
echo "cd \$$(dirname \$$0)" >> $(ALL_SCRIPT)
echo "ROOT=\$$PWD" >> $(ALL_SCRIPT)
diff --git a/tools/testing/selftests/net/run_netsocktests b/tools/testing/selftests/net/run_netsocktests
index c09a682..16058bb 100755
--- a/tools/testing/selftests/net/run_netsocktests
+++ b/tools/testing/selftests/net/run_netsocktests
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
echo "--------------------"
echo "running socket test"
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c b/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c
index c22860a..30e1ac6 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c
@@ -66,7 +66,7 @@
FAIL_IF(ebb_event_enable(&event));
- mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+ mtspr(SPRN_PMC2, pmc_sample_period(sample_period));
mtspr(SPRN_PMC5, 0);
mtspr(SPRN_PMC6, 0);
diff --git a/tools/virtio/linux/compiler.h b/tools/virtio/linux/compiler.h
index 845960e..c9ccfd4 100644
--- a/tools/virtio/linux/compiler.h
+++ b/tools/virtio/linux/compiler.h
@@ -4,6 +4,6 @@
#define WRITE_ONCE(var, val) \
(*((volatile typeof(val) *)(&(var))) = (val))
-#define READ_ONCE(var) (*((volatile typeof(val) *)(&(var))))
+#define READ_ONCE(var) (*((volatile typeof(var) *)(&(var))))
#endif
diff --git a/tools/virtio/ringtest/run-on-all.sh b/tools/virtio/ringtest/run-on-all.sh
index 2e69ca8..29b0d39 100755
--- a/tools/virtio/ringtest/run-on-all.sh
+++ b/tools/virtio/ringtest/run-on-all.sh
@@ -1,12 +1,13 @@
#!/bin/sh
+CPUS_ONLINE=$(lscpu --online -p=cpu|grep -v -e '#')
#use last CPU for host. Why not the first?
#many devices tend to use cpu0 by default so
#it tends to be busier
-HOST_AFFINITY=$(lscpu -p=cpu | tail -1)
+HOST_AFFINITY=$(echo "${CPUS_ONLINE}"|tail -n 1)
#run command on all cpus
-for cpu in $(seq 0 $HOST_AFFINITY)
+for cpu in $CPUS_ONLINE
do
#Don't run guest and host on same CPU
#It actually works ok if using signalling
diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
index 8cebfbc..539d3f5 100644
--- a/virt/kvm/arm/vgic/vgic-init.c
+++ b/virt/kvm/arm/vgic/vgic-init.c
@@ -268,15 +268,11 @@
{
struct vgic_dist *dist = &kvm->arch.vgic;
- mutex_lock(&kvm->lock);
-
dist->ready = false;
dist->initialized = false;
kfree(dist->spis);
dist->nr_spis = 0;
-
- mutex_unlock(&kvm->lock);
}
void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu)
@@ -286,7 +282,8 @@
INIT_LIST_HEAD(&vgic_cpu->ap_list_head);
}
-void kvm_vgic_destroy(struct kvm *kvm)
+/* To be called with kvm->lock held */
+static void __kvm_vgic_destroy(struct kvm *kvm)
{
struct kvm_vcpu *vcpu;
int i;
@@ -297,6 +294,13 @@
kvm_vgic_vcpu_destroy(vcpu);
}
+void kvm_vgic_destroy(struct kvm *kvm)
+{
+ mutex_lock(&kvm->lock);
+ __kvm_vgic_destroy(kvm);
+ mutex_unlock(&kvm->lock);
+}
+
/**
* vgic_lazy_init: Lazy init is only allowed if the GIC exposed to the guest
* is a GICv2. A GICv3 must be explicitly initialized by the guest using the
@@ -348,6 +352,10 @@
ret = vgic_v2_map_resources(kvm);
else
ret = vgic_v3_map_resources(kvm);
+
+ if (ret)
+ __kvm_vgic_destroy(kvm);
+
out:
mutex_unlock(&kvm->lock);
return ret;
diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
index 9bab867..834137e 100644
--- a/virt/kvm/arm/vgic/vgic-v2.c
+++ b/virt/kvm/arm/vgic/vgic-v2.c
@@ -293,8 +293,6 @@
dist->ready = true;
out:
- if (ret)
- kvm_vgic_destroy(kvm);
return ret;
}
diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
index 5c9f974..e6b03fd 100644
--- a/virt/kvm/arm/vgic/vgic-v3.c
+++ b/virt/kvm/arm/vgic/vgic-v3.c
@@ -302,8 +302,6 @@
dist->ready = true;
out:
- if (ret)
- kvm_vgic_destroy(kvm);
return ret;
}
diff --git a/virt/lib/irqbypass.c b/virt/lib/irqbypass.c
index 52abac4..6d2fcd6 100644
--- a/virt/lib/irqbypass.c
+++ b/virt/lib/irqbypass.c
@@ -195,7 +195,7 @@
mutex_lock(&lock);
list_for_each_entry(tmp, &consumers, node) {
- if (tmp->token == consumer->token) {
+ if (tmp->token == consumer->token || tmp == consumer) {
mutex_unlock(&lock);
module_put(THIS_MODULE);
return -EBUSY;
@@ -245,7 +245,7 @@
mutex_lock(&lock);
list_for_each_entry(tmp, &consumers, node) {
- if (tmp->token != consumer->token)
+ if (tmp != consumer)
continue;
list_for_each_entry(producer, &producers, node) {