Merge "usb: gadget: f_rndis: allocate/free net device upon driver bind/unbind"
diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index fee35c0..0406076e4 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -32,7 +32,7 @@
Description of the physical chip / device for device X.
Typically a part number.
-What: /sys/bus/iio/devices/iio:deviceX/timestamp_clock
+What: /sys/bus/iio/devices/iio:deviceX/current_timestamp_clock
KernelVersion: 4.5
Contact: linux-iio@vger.kernel.org
Description:
diff --git a/Documentation/devicetree/bindings/arm/msm/clock-controller.txt b/Documentation/devicetree/bindings/arm/msm/clock-controller.txt
index 524cb7c..75f901b 100644
--- a/Documentation/devicetree/bindings/arm/msm/clock-controller.txt
+++ b/Documentation/devicetree/bindings/arm/msm/clock-controller.txt
@@ -25,6 +25,8 @@
"qcom,gcc-8992"
"qcom,gcc-8952"
"qcom,gcc-8937"
+ "qcom,gcc-sdm439"
+ "qcom,gcc-sdm429"
"qcom,gcc-8917"
"qcom,gcc-8940"
"qcom,gcc-8920"
diff --git a/Documentation/devicetree/bindings/arm/msm/clock-cpu-8939.txt b/Documentation/devicetree/bindings/arm/msm/clock-cpu-8939.txt
index 5d570d0..e2190df 100644
--- a/Documentation/devicetree/bindings/arm/msm/clock-cpu-8939.txt
+++ b/Documentation/devicetree/bindings/arm/msm/clock-cpu-8939.txt
@@ -7,7 +7,8 @@
Required properties:
- compatible: Must be one of "qcom,clock-cpu-8939" or
- "qcom,cpu-clock-8952", "qcom,cpu-clock-8917".
+ "qcom,cpu-clock-8952", "qcom,cpu-clock-8917",
+ "qcom,cpu-clock-sdm439", "qcom,cpu-clock-sdm429".
- reg: Pairs of physical base addresses and region sizes of
memory mapped registers.
- reg-names: Names of the bases for the above registers. Expected
diff --git a/Documentation/devicetree/bindings/display/panel/toppoly,td028ttec1.txt b/Documentation/devicetree/bindings/display/panel/tpo,td028ttec1.txt
similarity index 84%
rename from Documentation/devicetree/bindings/display/panel/toppoly,td028ttec1.txt
rename to Documentation/devicetree/bindings/display/panel/tpo,td028ttec1.txt
index 7175dc3..ed34253 100644
--- a/Documentation/devicetree/bindings/display/panel/toppoly,td028ttec1.txt
+++ b/Documentation/devicetree/bindings/display/panel/tpo,td028ttec1.txt
@@ -2,7 +2,7 @@
========================
Required properties:
-- compatible: "toppoly,td028ttec1"
+- compatible: "tpo,td028ttec1"
Optional properties:
- label: a symbolic name for the panel
@@ -14,7 +14,7 @@
-------
lcd-panel: td028ttec1@0 {
- compatible = "toppoly,td028ttec1";
+ compatible = "tpo,td028ttec1";
reg = <0>;
spi-max-frequency = <100000>;
spi-cpol;
diff --git a/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
index 806c458..24290c8 100644
--- a/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
+++ b/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
@@ -519,6 +519,8 @@
to identify the default topology for the
display. The first set is indexed by the
value 0.
+- qcom,mdss-dsi-dma-schedule-line: An integer value indicates the line number after vertical active
+ region, at which command DMA needs to be triggered.
Required properties for sub-nodes: None
Optional properties:
@@ -771,5 +773,6 @@
qcom,display-topology = <1 1 1>,
<2 2 1>;
qcom,default-topology-index = <0>;
+ qcom,mdss-dsi-dma-schedule-line = <5>;
};
};
diff --git a/Documentation/devicetree/bindings/fb/mdss-pll.txt b/Documentation/devicetree/bindings/fb/mdss-pll.txt
index 6b9238c..5c0bb2a 100644
--- a/Documentation/devicetree/bindings/fb/mdss-pll.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-pll.txt
@@ -14,7 +14,7 @@
"qcom,mdss_hdmi_pll_8996_v2", "qcom,mdss_dsi_pll_8996_v2",
"qcom,mdss_hdmi_pll_8996_v3", "qcom,mdss_dsi_pll_8952",
"qcom,mdss_dsi_pll_8937", "qcom,mdss_hdmi_pll_8996_v3_1p8",
- "qcom,mdss_dsi_pll_8953"
+ "qcom,mdss_dsi_pll_8953", "qcom,mdss_dsi_pll_sdm439"
- cell-index: Specifies the controller used
- reg: offset and length of the register set for the device.
- reg-names : names to refer to register sets related to this device
diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt
index 4fb47d6..a4e9ba7 100644
--- a/Documentation/devicetree/bindings/gpu/adreno.txt
+++ b/Documentation/devicetree/bindings/gpu/adreno.txt
@@ -127,6 +127,19 @@
mask - mask for the relevant bits in the efuse register.
shift - number of bits to right shift to get the speed bin
value.
+
+- qcom,gpu-speed-bin-vectors:
+ GPU speed bin vectors property is the series of all the vectors
+ in format specified below. Values from individual fuses are read,
+ masked and shifted to form a value. At the end all fuse values
+ are ordered together to form final speed bin value.
+ <offset mask shift>
+ <offset mask shift>
+ < .. .. .. >
+ offset - offset of the efuse register from the base.
+ mask - mask for the relevant bits in the efuse register.
+ shift - number of bits to right shift.
+
- qcom,gpu-disable-fuse: GPU disable fuse
<offset mask shift>
offset - offset of the efuse register from the base.
diff --git a/Documentation/devicetree/bindings/input/gen_vkeys.txt b/Documentation/devicetree/bindings/input/gen_vkeys.txt
new file mode 100644
index 0000000..2f8d65e
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/gen_vkeys.txt
@@ -0,0 +1,28 @@
+Touchscreen Virtual Keys Device
+
+Generate virtual keys sysfs entry for Android
+
+Required properties:
+
+ - compatible : should be "qcom,gen-vkeys"
+ - label : name of the touch controller
+ - qcom,disp-maxx : Maximum x-coordinate of display
+ - qcom,disp-maxy : Maximum y-coordinate of display
+ - qcom,panel-maxx : Maximum x-coordinate of touch panel
+ - qcom,panel-maxy : Maximum y-coordinate of touch panel
+ - qcom,key-codes : Array of key codes for virtual keys
+
+Optional properties:
+ - qcom,y-offset : Offset of y-location for virtual keys, default 0
+
+Example:
+ gen-vkeys {
+ compatible = "qcom,gen-vkeys";
+ label = "atmel_mxt_ts";
+ qcom,disp-maxx = <720>;
+ qcom,disp-maxy = <1280>;
+ qcom,panel-maxx = <760>;
+ qcom,panel-maxy = <1424>;
+ qcom,key-codes = <158 139 102 217>;
+ qcom,y-offset = <35>;
+ };
diff --git a/Documentation/devicetree/bindings/interrupt-controller/qti,mpm.txt b/Documentation/devicetree/bindings/interrupt-controller/qti,mpm.txt
index 833b108..18e8cf5 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/qti,mpm.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/qti,mpm.txt
@@ -52,7 +52,7 @@
Example:
wakegic: wake-gic@7781b8 {
- compatible = "qcom,mpm-gic", "qcom,mpm-gic-msm8953", "qcom,mpm-gic-msm8937";
+ compatible = "qcom,mpm-gic", "qcom,mpm-gic-msm8953", "qcom,mpm-gic-msm8937", "qcom,mpm-gic-msm8909";
interrupts = <GIC_SPI 171 IRQ_TYPE_EDGE_RISING>;
reg = <0x601d4 0x1000>,
<0xb011008 0x4>; /* MSM_APCS_GCC_BASE 4K */
@@ -85,7 +85,7 @@
Example:
wakegpio: wake-gpio {
- compatible = "qcom,mpm-gpio", "qcom,mpm-gpio-msm8953", "qcom,mpm-gpio-msm8937";
+ compatible = "qcom,mpm-gpio", "qcom,mpm-gpio-msm8953", "qcom,mpm-gpio-msm8937","qcom,mpm-gpio-msm8909";
interrupt-controller;
interrupt-parent = <&tlmm>;
#interrupt-cells = <2>;
diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt
index 8f3ad9a..b41d260 100644
--- a/Documentation/devicetree/bindings/mfd/axp20x.txt
+++ b/Documentation/devicetree/bindings/mfd/axp20x.txt
@@ -28,6 +28,9 @@
regulator to drive the OTG VBus, rather then as an input pin
which signals whether the board is driving OTG VBus or not.
+- x-powers,master-mode: Boolean (axp806 only). Set this when the PMIC is
+ wired for master mode. The default is slave mode.
+
- <input>-supply: a phandle to the regulator supply node. May be omitted if
inputs are unregulated, such as using the IPSOUT output
from the PMIC.
diff --git a/Documentation/devicetree/bindings/platform/msm/ipa.txt b/Documentation/devicetree/bindings/platform/msm/ipa.txt
index 73cd1db..8b63075 100644
--- a/Documentation/devicetree/bindings/platform/msm/ipa.txt
+++ b/Documentation/devicetree/bindings/platform/msm/ipa.txt
@@ -37,6 +37,8 @@
compatible "qcom,ipa-smmu-uc-cb"
- qcom,use-a2-service: determine if A2 service will be used
- qcom,use-ipa-tethering-bridge: determine if tethering bridge will be used
+- qcom,use-ipa-in-mhi-mode: Boolean context flag to indicate whether
+ device booting in MHI config or not.
- qcom,use-ipa-bamdma-a2-bridge: determine if a2/ipa hw bridge will be used
- qcom,ee: which EE is assigned to (non-secure) APPS from IPA-BAM POV. This
is a number
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-linear-charger.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-linear-charger.txt
new file mode 100644
index 0000000..6dccdd3
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-linear-charger.txt
@@ -0,0 +1,211 @@
+Qualcomm QPNP Linear Charger
+
+The charger module supports the linear battery charger peripherals on
+Qualcomm PMIC chips.
+
+There are four different peripherals in the charger module.
+Each of these peripherals are implemented as subnodes.
+
+- qcom,chgr: Supports charging control and status reporting
+- qcom,bat-if: Battery status reporting such as presence and
+ temperature reporting.
+- qcom,usb-chgpth: USB charge path detection and input current
+ limiting configuration.
+- qcom,chg-misc: Miscellaneous features such as comparator override
+ features etc.
+
+Parent node required properties:
+- qcom,vddmax-mv: Target voltage of battery in mV.
+- qcom,vddsafe-mv: Maximum Vdd voltage in mV.
+- qcom,vinmin-mv: Minimum input voltage in mV.
+- qcom,ibatsafe-ma: Safety battery current setting
+
+Parent node optional properties:
+- qcom,vbatweak-uv: Weak battery voltage threshold in uV,
+ above which fast charging can start.
+ The supported voltage range is from
+ 3000000uV to 3581250uV with a step
+ size of 18750000 uV.
+- qcom,charging-disabled: Set this property to disable charging
+ by default.
+- qcom,use-default-batt-values: Set this flag to force reporting of
+ fake battery.
+- qcom,warm-bat-decidegc: Warm battery temperature in decidegC.
+- qcom,cool-bat-decidegc: Cool battery temperature in decidegC.
+ Note that if both warm and cool
+ battery temperatures are set, the
+ corresponding ibatmax and bat-mv
+ properties are required to be set.
+- qcom,ibatmax-cool-ma: Maximum cool battery charge current.
+- qcom,ibatmax-warm-ma: Maximum warm battery charge current.
+- qcom,warm-bat-mv: Warm temperature battery target
+ voltage.
+- qcom,cool-bat-mv: Cool temperature battery target
+ voltage.
+- qcom,thermal-mitigation: Array of ibatmax values for different
+ system thermal mitigation level.
+- qcom,tchg-mins: Maximum total software initialized
+ charge time.
+- qcom,bpd-detection: Select a battery presence detection
+ scheme by specifying either "bpd_thm"
+ "bpd_id" or "bpd_thm_id". "bpd_thm"
+ selects the temperature pin, "bpd_id"
+ uses the id pin for battery presence
+ detection, "bpd_thm_id" selects both.
+ If the property is not set, the
+ temperatue pin will be used.
+- qcom,btc-disabled: If flag is set battery hot and cold
+ monitoring is disabled in hardware.
+ This monitoring is turned on by
+ default.
+- qcom,batt-hot-percentage: Specify a supported hot threshold
+ percentage.
+ Supported thresholds: 25% and 35%. If
+ none is specified hardware defaults
+ will be used.
+- qcom,batt-cold-percentage: Specify a supported cold threshold
+ percentage. Supported thresholds: 70%
+ and 80%. If none is specified
+ hardwaredefaults will be used.
+- qcom,chg-adc_tm Corresponding ADC TM device's phandle
+ to set recurring measurements and
+ receive notification for batt_therm.
+-qcom,float-charge If specified enable float charging.
+- qcom,chg-vadc Corresponding VADC device's phandle.
+- qcom,charger-detect-eoc If specified charger hardware will
+ detect end-of-charge.
+ If not specified charger driver
+ depends on BMSfor end-of-charge
+ detection.
+- qcom,disable-vbatdet-based-recharge If specified disable VBATDET irq
+ and charging can only be resumed
+ if charger is re-inserted or SOC
+ falls below resume SOC.
+ This property should always be used
+ along with the BMS property:
+ "qcom,disable-suspend-on-usb".
+- qcom,use-external-charger If specified the LBC module will
+ be disabled and the driver will not
+ register. It also enables BID for
+ BPD and disables BTC. Declare this node
+ only if you are using an external charger
+ and not the PMIC internal LBC.
+- qcom,chgr-led-support There is a current sink device in linear
+ charger module, it is used to control a
+ led which can act as a charger led as well
+ as a general notification led.
+- qcom,parallel-charger This is a bool property to indicate the
+ LBC will operate as a secondary charger
+ in the parallel mode. If this is enabled
+ the charging operations will be controlled by
+ the primary-charger.
+- qcom,collapsible-chgr-support If specified the collapsible charger feature
+ will be supported. LBC will disable VIN_MIN
+ comparator and use chg_gone interrupt to
+ detect charger removal.
+
+
+Sub node required structure:
+- A qcom,charger node must be a child of an SPMI node that has specified
+ the spmi-dev-container property. Each subnode reflects
+ a hardware peripheral which adds a unique set of features
+ to the collective charging device. For example USB detection
+ and the battery interface are each separate peripherals and
+ each should be their own subnode.
+
+Sub node required properties:
+- compatible: Must be "qcom,qpnp-linear-charger".
+- reg: Specifies the SPMI address and size for this
+ peripheral.
+- interrupts: Specifies the interrupt associated with the
+ peripheral.
+- interrupt-names: Specifies the interrupt names for the peripheral.
+ Every available interrupt needs to have an associated
+ name with it to indentify its purpose.
+
+ The following lists each subnode and their
+ corresponding required interrupt names:
+
+ qcom,usb-chgpth:
+ - usbin-valid
+
+ The following interrupts are available:
+
+ qcom,usb-chgpth:
+ - usbin-valid: Indicates valid USB
+ connection.
+ - coarse-det-usb: Coarse detect interrupt
+ triggers at low voltage on
+ USB_IN.
+ - chg-gone: Triggers on VCHG line.
+ - overtemp: Triggers on over temperature
+ condition
+
+ qcom,chgr:
+ - chg-done: Triggers on charge completion.
+ - chg-failed: Notifies of charge failures.
+ - fast-chg-on: Notifies of fast charging.
+ - vbat-det-lo: Triggers on vbat-det-lo
+ voltage.
+
+Example:
+ pm8916-chg: qcom,charger {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-linear-charger";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ qcom,vddmax-mv = <4200>;
+ qcom,vddsafe-mv = <4200>;
+ qcom,vinmin-mv = <4200>;
+ qcom,ibatsafe-ma = <1440>;
+ qcom,vbatweak-uv = <3200>;
+ qcom,thermal-mitigation = <1500 700 600 325>;
+ qcom,cool-bat-decidegc = <100>;
+ qcom,warm-bat-decidegc = <450>;
+ qcom,cool-bat-mv = <4100>;
+ qcom,ibatmax-warm-ma = <360>;
+ qcom,ibatmax-cool-ma = <360>;
+ qcom,warm-bat-mv = <4100>;
+ qcom,batt-hot-percentage = <25>;
+ qcom,batt-cold-percentage = <85>;
+ qcom,tchg-mins = <152>;
+ qcom,resume-soc = <99>;
+ qcom,btc-disabled = <0>;
+ qcom,chg-vadc = <&pm8916_vadc>;
+
+ qcom,chgr@1000 {
+ reg = <0x1000 0x100>;
+ interrupts = <0x0 0x10 0x7>,
+ <0x0 0x10 0x6>,
+ <0x0 0x10 0x5>,
+ <0x0 0x10 0x0>;
+
+ interrupt-names = "chg-done",
+ "chg-failed",
+ "fast-chg-on",
+ "vbat-det-lo";
+ };
+
+ qcom,bat-if@1200 {
+ reg = <0x1200 0x100>;
+ interrupts = <0x0 0x12 0x1>,
+ <0x0 0x12 0x0>;
+
+ interrupt-names = "bat-temp-ok",
+ "batt-pres";
+ };
+
+ qcom,usb-chgpth@1300 {
+ reg = <0x1300 0x100>;
+ interrupts = <0 0x13 0x2>,
+ <0 0x13 0x1>;
+
+ interrupt-names = "chg-gone",
+ "usbin-valid";
+ };
+
+ qcom,chg-misc@1600 {
+ reg = <0x1600 0x100>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt
index de273cd..8ee2749 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt
@@ -195,6 +195,12 @@
This is only applicable to certain PMICs like PMI632 which
has SCHGM_FLASH peripheral.
+- qcom,chg-vadc
+ Usage: optional
+ Value type: <phandle>
+ Definition: Phandle for the VADC node, it is used to obtain USBIN_V
+ and USBIN_I readings on PMIC632 based platform.
+
=============================================
Second Level Nodes - SMB5 Charger Peripherals
=============================================
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt
index efd64cd..d1d66cf 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt
@@ -282,6 +282,9 @@
battery.
- qcom,vchg_sns-vadc Phandle of the VADC node.
- qcom,vchg-adc-channel-id The ADC channel to which the VCHG is routed.
+- qcom,max-pulse-allowed The maximum number of pulses allowed in
+ HVDCP3 mode. It can be used to restrict VBUS
+ to a value.
Example:
qcom,qpnp-smbcharger {
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-vm-bms.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-vm-bms.txt
new file mode 100644
index 0000000..690a047
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-vm-bms.txt
@@ -0,0 +1,180 @@
+Qualcomm's QPNP Voltage-Mode(VM) PMIC Battery Management System
+
+QPNP PMIC VM BMS provides interface to clients to read properties related
+to the battery. Its main function is to calculate the SOC (state of charge)
+of the battery based on periodic sampling of the VBAT (battery voltage).
+
+Parent node required properties:
+- compatible : Must be "qcom,qpnp-vm-bms" for the BM driver.
+- reg : Offset and length of the PMIC peripheral register map.
+- interrupts : The interrupt mappings.
+ The format should be
+ <slave-id peripheral-id interrupt-number>.
+- interrupt-names : names for the mapped bms interrupt
+ The following interrupts are required:
+ 0 : leave CV state
+ 1 : enter CV state
+ 2 : good ocv generated
+ 3 : ocv_thr
+ 4 : fifo update
+ 5 : fsm state chnaged
+
+Additionally, optional subnodes may be included:
+- qcom,batt-pres-status : A subnode with a register address for the SMBB
+ battery interface's BATT_PRES_STATUS register. If this node is
+ added, then the BMS will try to detect offmode battery removal
+ via the battery interface's offmode battery removal circuit.
+- qcom,battery-data : A phandle to a node containing the available batterydata
+ profiles. See the batterydata bindings documentation for more
+ details.
+
+Parent node required properties:
+- qcom,v-cutoff-uv : cutoff voltage where the battery is considered dead in
+ micro-volts.
+- qcom,max-voltage-uv : maximum voltage for the battery in micro-volts.
+- qcom,r-conn-mohm : connector resistance in milli-ohms.
+- qcom,shutdown-soc-valid-limit : If the ocv upon restart is within this
+ distance of the shutdown ocv, the BMS will try to force
+ the new SoC to the old one to provide charge continuity.
+ That is to say,
+ if (abs(shutdown-soc - current-soc) < limit)
+ then use old SoC.
+- qcom,low-soc-calculate-soc-threshold : The SoC threshold for when
+ the periodic calculate_soc work speeds up. This ensures
+ SoC is updated in userspace constantly when we are near
+ shutdown.
+- qcom,low-voltage-threshold : The battery voltage threshold in micro-volts for
+ when the BMS tries to wake up and hold a wakelock to
+ ensure a clean shutdown.
+- qcom,low-voltage-calculate-soc-ms : The time period between subsequent
+ SoC recalculations when the current voltage is below
+ qcom,low-voltage threshold. This takes precedence over
+ qcom,low-soc-calculate-soc-ms.
+- qcom,low-soc-calculate-soc-ms : The time period between subsequent
+ SoC recalculations when the current SoC is below
+ qcom,low-soc-calculate-soc-threshold. This takes
+ precedence over qcom,calculate-soc-ms.
+- qcom,calculate-soc-ms : The time period between subsequent SoC
+ recalculations when the current SoC is above or equal
+ qcom,low-soc-calculate-soc-threshold.
+- qcom,volatge-soc-timeout-ms : The timeout period after which the module starts
+ reporting volage based SOC and does not use the VMBMS
+ algorithm for SOC calculation.
+- qcom,bms-vadc: Corresponding VADC device's phandle.
+- qcom,bms-adc_tm: Corresponding ADC_TM device's phandle to set recurring
+ measurements and receive notifications for vbatt.
+- qcom,pmic-revid : Phandle pointing to the revision peripheral node.
+
+Parent node Optional properties
+- qcom,s1-sample-interval-ms: The sampling rate in ms of the accumulator in state
+ S1. (i.e) the rate at which the accumulator is being
+ filled with vbat samples. Minimum value = 0 and
+ Maximum value = 2550ms.
+- qcom,s2-sample-interval-ms: The sampling rate in ms of the accumulator in state
+ S2. (i.e) the rate at which the accumulator is being
+ filled with vbat samples. Minimum value = 0 and
+ Maximum value = 2550ms.
+- qcom,s1-sample-count: The number of samples to be accululated for one FIFO in
+ state S1. Possible values are - 0, 4, 8, 16, 32, 64, 128,
+ 256.
+- qcom,s2-sample-count: The number of samples to be accululated for one FIFO in
+ state S2. Possible values are - 0, 4, 8, 16, 32, 64, 128,
+ 256.
+- qcom,s1-fifo-legth: Number of FIFO's to be filled in state S1, to generate
+ the fifo_update_done interrupt. Possile values - 0 to 8
+- qcom,s2-fifo-legth: Number of FIFO's to be filled in state S2, to generate
+ the fifo_update_done interrupt. Possible values- 0 to 8
+- qcom,force-s3-on-suspend : Bool property to force the BMS into S3 (sleep) state
+ while entering into system suspend.
+- qcom,force-bms-active-on-charger: Bool property to keep BMS FSM active
+ if charger is present.
+- qcom,report-charger-eoc : Bool property to indicate if BMS needs to indicate
+ EOC to charger.
+- qcom,ignore-shutdown-soc: A boolean that controls whether BMS will
+ try to force the startup SoC to be the same as the
+ shutdown SoC. Defining it will make BMS ignore the
+ shutdown SoC.
+- qcom,use-voltage-soc : A boolean that controls whether BMS will use
+ voltage-based SoC instead of a coulomb counter based
+ one. Voltage-based SoC will not guarantee linearity.
+- qcom,disable-bms : Bool property to disable the VMBMS hardware module.
+ Enable this property if BMS is not supported or an external
+ fuel gauge is used.
+- qcom,s3-ocv-tolerence-uv : The S3 state OCV tolerence threshold in uV. The
+ LSB value is 300uV and maximum value is 76500uV.
+- qcom,low-soc-fifo-length : The fifo length (of S2 STATE) to be used at lower
+ SOCs. If this value is not specified the system uses
+ default length.
+- qcom,resume-soc: Capacity in percent at which charging should resume
+ when a fully charged battery drops below this level.
+- qcom,low-temp-threshold : The temperature threshold below which the IBAT
+ averaging and UUC smoothening is disabled. This value
+ is in deci-degrees centigrade. If not specified it
+ defaults to 0.
+- qcom,ibat-avg-samples : The number of samples to be averaged for IBAT
+ estimation. If not specified it defaults to 16.
+ The possible values are 1 to 16.
+- qcom,batt-aging-comp : A boolean that defines if battery aging compensation
+ is enabled.
+- qcom,use-reported-soc : Bool property to enable the reported_soc logic. To
+ enable this feature, qcom,resume-soc must be defined as
+ a proper value. The BMS is also required to control the
+ charging, discharging and recharging.
+
+qcom,batt-pres-status node required properties:
+- reg : offset and length of the PMIC LBC battery interface BATT_PRES_STATUS
+ register.
+
+qcom,qpnp-chg-pres required properties:
+- reg : offset and length of the PMIC LBC charger interafce CHARGER_OPTION
+ register.
+
+Example:
+pm8916_bms: qcom,qpnp-vm-bms {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-vm-bms";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ status = "disabled";
+
+ qcom,v-cutoff-uv = <3400000>;
+ qcom,max-voltage-uv = <4200000>;
+ qcom,r-conn-mohm = <18>;
+ qcom,shutdown-soc-valid-limit = <20>;
+ qcom,low-soc-calculate-soc-threshold = <15>;
+ qcom,low-voltage-threshold = <3420000>;
+ qcom,low-voltage-calculate-soc-ms = <1000>;
+ qcom,low-soc-calculate-soc-ms = <5000>;
+ qcom,low-soc-fifo-length = <2>;
+ qcom,calculate-soc-ms = <20000>;
+ qcom,s3-ocv-tolerence-uv = <1200>;
+ qcom,volatge-soc-timeout-ms = <60000>;
+ qcom,battery-data = <&mtp_batterydata>;
+ qcom,bms-vadc = <&pm8916_vadc>;
+ qcom,bms-adc_tm = <&pm8916_adc_tm>;
+
+ qcom,batt-pres-status@1208 {
+ reg = <0x1208 0x1>;
+ }
+
+ qcom,qpnp-chg-pres@1208 {
+ reg = <0x1108 0x1>;
+ }
+
+ qcom,bms-bms@4000 {
+ reg = <0x4000 0x100>;
+ interrupts = <0x0 0x40 0x0>,
+ <0x0 0x40 0x1>,
+ <0x0 0x40 0x2>,
+ <0x0 0x40 0x3>,
+ <0x0 0x40 0x4>,
+ <0x0 0x40 0x5>,
+
+ interrupt-names = "leave_cv",
+ "enter_cv",
+ "good_ocv",
+ "ocv_thr",
+ "fifo_updtaed",
+ "fsm_state_change";
+ };
+};
diff --git a/Documentation/devicetree/bindings/power/smb1360-charger-fg.txt b/Documentation/devicetree/bindings/power/supply/qcom/smb1360-charger-fg.txt
similarity index 100%
rename from Documentation/devicetree/bindings/power/smb1360-charger-fg.txt
rename to Documentation/devicetree/bindings/power/supply/qcom/smb1360-charger-fg.txt
diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index 881f9ca..0cc8b00 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -82,6 +82,7 @@
- qcom,smmu-s1-bypass: If present, configure SMMU to bypass stage 1 translation.
- qcom,no-vbus-vote-with-type-C: If present, then do not try to get and enable VBUS
regulator in type-C host mode from dwc3-msm driver.
+- qcom,connector-type-uAB: HW platform is using micro-AB USB connector type.
Sub nodes:
- Sub node for "DWC3- USB3 controller".
diff --git a/Makefile b/Makefile
index 7c1b91b..41cc032 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 4
PATCHLEVEL = 9
-SUBLEVEL = 84
+SUBLEVEL = 92
EXTRAVERSION =
NAME = Roaring Lionus
@@ -349,6 +349,7 @@
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
REAL_CC = $(CROSS_COMPILE)gcc
+LDGOLD = $(CROSS_COMPILE)ld.gold
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
@@ -627,6 +628,20 @@
CFLAGS_KCOV := $(call cc-option,-fsanitize-coverage=trace-pc,)
export CFLAGS_GCOV CFLAGS_KCOV
+# Make toolchain changes before including arch/$(SRCARCH)/Makefile to ensure
+# ar/cc/ld-* macros return correct values.
+ifdef CONFIG_LTO_CLANG
+# use GNU gold with LLVMgold for LTO linking, and LD for vmlinux_link
+LDFINAL_vmlinux := $(LD)
+LD := $(LDGOLD)
+LDFLAGS += -plugin LLVMgold.so
+# use llvm-ar for building symbol tables from IR files, and llvm-dis instead
+# of objdump for processing symbol versions and exports
+LLVM_AR := llvm-ar
+LLVM_DIS := llvm-dis
+export LLVM_AR LLVM_DIS
+endif
+
# The arch Makefile can set ARCH_{CPP,A,C}FLAGS to override the default
# values of the respective KBUILD_* variables
ARCH_CPPFLAGS :=
@@ -645,6 +660,53 @@
KBUILD_CFLAGS += $(call cc-option,-fdata-sections,)
endif
+ifdef CONFIG_LTO_CLANG
+lto-clang-flags := -flto -fvisibility=hidden
+
+# allow disabling only clang LTO where needed
+DISABLE_LTO_CLANG := -fno-lto -fvisibility=default
+export DISABLE_LTO_CLANG
+endif
+
+ifdef CONFIG_LTO
+lto-flags := $(lto-clang-flags)
+KBUILD_CFLAGS += $(lto-flags)
+
+DISABLE_LTO := $(DISABLE_LTO_CLANG)
+export DISABLE_LTO
+
+# LDFINAL_vmlinux and LDFLAGS_FINAL_vmlinux can be set to override
+# the linker and flags for vmlinux_link.
+export LDFINAL_vmlinux LDFLAGS_FINAL_vmlinux
+endif
+
+ifdef CONFIG_CFI_CLANG
+cfi-clang-flags += -fsanitize=cfi
+DISABLE_CFI_CLANG := -fno-sanitize=cfi
+ifdef CONFIG_MODULES
+cfi-clang-flags += -fsanitize-cfi-cross-dso
+DISABLE_CFI_CLANG += -fno-sanitize-cfi-cross-dso
+endif
+ifdef CONFIG_CFI_PERMISSIVE
+cfi-clang-flags += -fsanitize-recover=cfi -fno-sanitize-trap=cfi
+endif
+
+# also disable CFI when LTO is disabled
+DISABLE_LTO_CLANG += $(DISABLE_CFI_CLANG)
+# allow disabling only clang CFI where needed
+export DISABLE_CFI_CLANG
+endif
+
+ifdef CONFIG_CFI
+# cfi-flags are re-tested in prepare-compiler-check
+cfi-flags := $(cfi-clang-flags)
+KBUILD_CFLAGS += $(cfi-flags)
+
+DISABLE_CFI := $(DISABLE_CFI_CLANG)
+DISABLE_LTO += $(DISABLE_CFI)
+export DISABLE_CFI
+endif
+
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
KBUILD_CFLAGS += $(call cc-option,-Oz,-Os)
KBUILD_CFLAGS += $(call cc-disable-warning,maybe-uninitialized,)
@@ -808,6 +870,15 @@
# disable invalid "can't wrap" optimizations for signed / pointers
KBUILD_CFLAGS += $(call cc-option,-fno-strict-overflow)
+# clang sets -fmerge-all-constants by default as optimization, but this
+# is non-conforming behavior for C and in fact breaks the kernel, so we
+# need to disable it here generally.
+KBUILD_CFLAGS += $(call cc-option,-fno-merge-all-constants)
+
+# for gcc -fno-merge-all-constants disables everything, but it is fine
+# to have actual conforming behavior enabled.
+KBUILD_CFLAGS += $(call cc-option,-fmerge-constants)
+
# Make sure -fstack-check isn't enabled (like gentoo apparently did)
KBUILD_CFLAGS += $(call cc-option,-fno-stack-check,)
@@ -1087,6 +1158,22 @@
# CC_STACKPROTECTOR_STRONG! Why did it build with _REGULAR?!")
PHONY += prepare-compiler-check
prepare-compiler-check: FORCE
+# Make sure we're using a supported toolchain with LTO_CLANG
+ifdef CONFIG_LTO_CLANG
+ ifneq ($(call clang-ifversion, -ge, 0500, y), y)
+ @echo Cannot use CONFIG_LTO_CLANG: requires clang 5.0 or later >&2 && exit 1
+ endif
+ ifneq ($(call gold-ifversion, -ge, 112000000, y), y)
+ @echo Cannot use CONFIG_LTO_CLANG: requires GNU gold 1.12 or later >&2 && exit 1
+ endif
+endif
+# Make sure compiler supports LTO flags
+ifdef lto-flags
+ ifeq ($(call cc-option, $(lto-flags)),)
+ @echo Cannot use CONFIG_LTO: $(lto-flags) not supported by compiler \
+ >&2 && exit 1
+ endif
+endif
# Make sure compiler supports requested stack protector flag.
ifdef stackp-name
ifeq ($(call cc-option, $(stackp-flag)),)
@@ -1101,6 +1188,11 @@
$(stackp-flag) available but compiler is broken >&2 && exit 1
endif
endif
+ifdef cfi-flags
+ ifeq ($(call cc-option, $(cfi-flags)),)
+ @echo Cannot use CONFIG_CFI: $(cfi-flags) not supported by compiler >&2 && exit 1
+ endif
+endif
@:
# Generate some files
@@ -1575,7 +1667,8 @@
-o -name modules.builtin -o -name '.tmp_*.o.*' \
-o -name '*.c.[012]*.*' \
-o -name '*.ll' \
- -o -name '*.gcno' \) -type f -print | xargs rm -f
+ -o -name '*.gcno' \
+ -o -name '*.*.symversions' \) -type f -print | xargs rm -f
# Generate tags for editors
# ---------------------------------------------------------------------------
diff --git a/arch/Kconfig b/arch/Kconfig
index a364ece..4fa799b 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -503,6 +503,74 @@
sections (e.g., '.text.init'). Typically '.' in section names
is used to distinguish them from label names / C identifiers.
+config LTO
+ def_bool n
+
+config ARCH_SUPPORTS_LTO_CLANG
+ bool
+ help
+ An architecture should select this option it supports:
+ - compiling with clang,
+ - compiling inline assembly with clang's integrated assembler,
+ - and linking with either lld or GNU gold w/ LLVMgold.
+
+choice
+ prompt "Link-Time Optimization (LTO) (EXPERIMENTAL)"
+ default LTO_NONE
+ help
+ This option turns on Link-Time Optimization (LTO).
+
+config LTO_NONE
+ bool "None"
+
+config LTO_CLANG
+ bool "Use clang Link Time Optimization (LTO) (EXPERIMENTAL)"
+ depends on ARCH_SUPPORTS_LTO_CLANG
+ depends on !FTRACE_MCOUNT_RECORD || HAVE_C_RECORDMCOUNT
+ select LTO
+ select THIN_ARCHIVES
+ select LD_DEAD_CODE_DATA_ELIMINATION
+ help
+ This option enables clang's Link Time Optimization (LTO), which allows
+ the compiler to optimize the kernel globally at link time. If you
+ enable this option, the compiler generates LLVM IR instead of object
+ files, and the actual compilation from IR occurs at the LTO link step,
+ which may take several minutes.
+
+ If you select this option, you must compile the kernel with clang >=
+ 5.0 (make CC=clang) and GNU gold from binutils >= 2.27, and have the
+ LLVMgold plug-in in LD_LIBRARY_PATH.
+
+endchoice
+
+config CFI
+ bool
+
+config CFI_PERMISSIVE
+ bool "Use CFI in permissive mode"
+ depends on CFI
+ help
+ When selected, Control Flow Integrity (CFI) violations result in a
+ warning instead of a kernel panic. This option is useful for finding
+ CFI violations in drivers during development.
+
+config CFI_CLANG
+ bool "Use clang Control Flow Integrity (CFI) (EXPERIMENTAL)"
+ depends on LTO_CLANG
+ depends on KALLSYMS
+ select CFI
+ help
+ This option enables clang Control Flow Integrity (CFI), which adds
+ runtime checking for indirect function calls.
+
+config CFI_CLANG_SHADOW
+ bool "Use CFI shadow to speed up cross-module checks"
+ default y
+ depends on CFI_CLANG
+ help
+ If you select this option, the kernel builds a fast look-up table of
+ CFI check functions in loaded modules to reduce overhead.
+
config HAVE_ARCH_WITHIN_STACK_FRAMES
bool
help
diff --git a/arch/alpha/kernel/console.c b/arch/alpha/kernel/console.c
index 6a61dee..ab228ed 100644
--- a/arch/alpha/kernel/console.c
+++ b/arch/alpha/kernel/console.c
@@ -20,6 +20,7 @@
struct pci_controller *pci_vga_hose;
static struct resource alpha_vga = {
.name = "alpha-vga+",
+ .flags = IORESOURCE_IO,
.start = 0x3C0,
.end = 0x3DF
};
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 41245ce..04b1c06 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1937,6 +1937,26 @@
Enabling this option will cause a concatenated zImage and list of
DTBs to be built by default (instead of a standalone zImage.)
The image will built in arch/arm/boot/zImage-dtb
+choice
+ prompt "Appended DTB Kernel Image name"
+ depends on BUILD_ARM_APPENDED_DTB_IMAGE
+ default ZIMG_DTB
+ help
+ Enabling this option will cause a specific kernel image Image or
+ Image.gz to be used for final image creation.
+ The image will built in arch/arm/boot/IMAGE-NAME-dtb
+
+ config ZIMG_DTB
+ bool "zImage-dtb"
+ config IMG_DTB
+ bool "Image-dtb"
+endchoice
+
+config BUILD_ARM_APPENDED_KERNEL_IMAGE_NAME
+ string
+ depends on BUILD_ARM_APPENDED_DTB_IMAGE
+ default "zImage-dtb" if ZIMG_DTB
+ default "Image-dtb" if IMG_DTB
config BUILD_ARM_APPENDED_DTB_IMAGE_NAMES
string "Default dtb names"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index f42fcd4..a6d5794 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -299,7 +299,7 @@
ifeq ($(CONFIG_XIP_KERNEL),y)
KBUILD_IMAGE := xipImage
else ifeq ($(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE),y)
-KBUILD_IMAGE := zImage-dtb
+KBUILD_IMAGE := $(subst $\",,$(CONFIG_BUILD_ARM_APPENDED_KERNEL_IMAGE_NAME))
else
KBUILD_IMAGE := zImage
endif
@@ -352,6 +352,10 @@
zImage-dtb: vmlinux scripts dtbs
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) DTSSUBDIR=$(DTSSUBDIR) $(boot)/$@
+Image-dtb: vmlinux scripts dtbs
+ $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) DTSSUBDIR=$(DTSSUBDIR) $(boot)/$@
+
+
# We use MRPROPER_FILES and CLEAN_FILES now
archclean:
$(Q)$(MAKE) $(clean)=$(boot)
diff --git a/arch/arm/boot/.gitignore b/arch/arm/boot/.gitignore
index ad7a025..271e124 100644
--- a/arch/arm/boot/.gitignore
+++ b/arch/arm/boot/.gitignore
@@ -4,4 +4,6 @@
bootpImage
uImage
*.dtb
-zImage-dtb
\ No newline at end of file
+zImage-dtb
+Image-dtb-hdr
+Image-dtb
diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile
index 4175dfe..2fa123e 100644
--- a/arch/arm/boot/Makefile
+++ b/arch/arm/boot/Makefile
@@ -59,6 +59,14 @@
$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
+$(obj)/Image-dtb-hdr: $(obj)/Image FORCE
+ echo -n 'UNCOMPRESSED_IMG' > $@ && \
+ $(call size_append, $(filter-out FORCE,$^)) >> $@
+
+$(obj)/Image-dtb: $(obj)/Image-dtb-hdr $(obj)/Image $(DTB_OBJS) FORCE
+ $(call if_changed,cat)
+ @echo ' Kernel: $@ is ready'
+
$(obj)/compressed/vmlinux: $(obj)/Image FORCE
$(Q)$(MAKE) $(build)=$(obj)/compressed $@
diff --git a/arch/arm/boot/dts/am335x-pepper.dts b/arch/arm/boot/dts/am335x-pepper.dts
index 30e2f87..42b62f5 100644
--- a/arch/arm/boot/dts/am335x-pepper.dts
+++ b/arch/arm/boot/dts/am335x-pepper.dts
@@ -139,7 +139,7 @@
&audio_codec {
status = "okay";
- gpio-reset = <&gpio1 16 GPIO_ACTIVE_LOW>;
+ reset-gpios = <&gpio1 16 GPIO_ACTIVE_LOW>;
AVDD-supply = <&ldo3_reg>;
IOVDD-supply = <&ldo3_reg>;
DRVDD-supply = <&ldo3_reg>;
diff --git a/arch/arm/boot/dts/aspeed-ast2500-evb.dts b/arch/arm/boot/dts/aspeed-ast2500-evb.dts
index 1b7a5ff..d7774d3 100644
--- a/arch/arm/boot/dts/aspeed-ast2500-evb.dts
+++ b/arch/arm/boot/dts/aspeed-ast2500-evb.dts
@@ -15,7 +15,7 @@
bootargs = "console=ttyS4,115200 earlyprintk";
};
- memory {
+ memory@80000000 {
reg = <0x80000000 0x20000000>;
};
};
diff --git a/arch/arm/boot/dts/bcm283x-rpi-smsc9512.dtsi b/arch/arm/boot/dts/bcm283x-rpi-smsc9512.dtsi
index 12c981e..9a0599f 100644
--- a/arch/arm/boot/dts/bcm283x-rpi-smsc9512.dtsi
+++ b/arch/arm/boot/dts/bcm283x-rpi-smsc9512.dtsi
@@ -1,6 +1,6 @@
/ {
aliases {
- ethernet = ðernet;
+ ethernet0 = ðernet;
};
};
diff --git a/arch/arm/boot/dts/bcm283x-rpi-smsc9514.dtsi b/arch/arm/boot/dts/bcm283x-rpi-smsc9514.dtsi
index 3f0a56e..dc7ae77 100644
--- a/arch/arm/boot/dts/bcm283x-rpi-smsc9514.dtsi
+++ b/arch/arm/boot/dts/bcm283x-rpi-smsc9514.dtsi
@@ -1,6 +1,6 @@
/ {
aliases {
- ethernet = ðernet;
+ ethernet0 = ðernet;
};
};
diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts b/arch/arm/boot/dts/exynos4412-trats2.dts
index 41ecd6d..75a6063 100644
--- a/arch/arm/boot/dts/exynos4412-trats2.dts
+++ b/arch/arm/boot/dts/exynos4412-trats2.dts
@@ -408,7 +408,7 @@
reg = <0>;
vdd3-supply = <&lcd_vdd3_reg>;
vci-supply = <&ldo25_reg>;
- reset-gpios = <&gpy4 5 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&gpf2 1 GPIO_ACTIVE_HIGH>;
power-on-delay= <50>;
reset-delay = <100>;
init-delay = <100>;
diff --git a/arch/arm/boot/dts/logicpd-som-lv.dtsi b/arch/arm/boot/dts/logicpd-som-lv.dtsi
index 4f2c5ec..e262fa9 100644
--- a/arch/arm/boot/dts/logicpd-som-lv.dtsi
+++ b/arch/arm/boot/dts/logicpd-som-lv.dtsi
@@ -97,6 +97,8 @@
};
&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
clock-frequency = <2600000>;
twl: twl@48 {
@@ -215,7 +217,12 @@
>;
};
-
+ i2c1_pins: pinmux_i2c1_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21ba, PIN_INPUT | MUX_MODE0) /* i2c1_scl.i2c1_scl */
+ OMAP3_CORE1_IOPAD(0x21bc, PIN_INPUT | MUX_MODE0) /* i2c1_sda.i2c1_sda */
+ >;
+ };
};
&omap3_pmx_wkup {
diff --git a/arch/arm/boot/dts/logicpd-torpedo-som.dtsi b/arch/arm/boot/dts/logicpd-torpedo-som.dtsi
index efe5399..08f0a35 100644
--- a/arch/arm/boot/dts/logicpd-torpedo-som.dtsi
+++ b/arch/arm/boot/dts/logicpd-torpedo-som.dtsi
@@ -100,6 +100,8 @@
};
&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
clock-frequency = <2600000>;
twl: twl@48 {
@@ -207,6 +209,12 @@
OMAP3_CORE1_IOPAD(0x21b8, PIN_INPUT | MUX_MODE0) /* hsusb0_data7.hsusb0_data7 */
>;
};
+ i2c1_pins: pinmux_i2c1_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21ba, PIN_INPUT | MUX_MODE0) /* i2c1_scl.i2c1_scl */
+ OMAP3_CORE1_IOPAD(0x21bc, PIN_INPUT | MUX_MODE0) /* i2c1_sda.i2c1_sda */
+ >;
+ };
};
&uart2 {
diff --git a/arch/arm/boot/dts/ls1021a-qds.dts b/arch/arm/boot/dts/ls1021a-qds.dts
index 9408753..67b4de0 100644
--- a/arch/arm/boot/dts/ls1021a-qds.dts
+++ b/arch/arm/boot/dts/ls1021a-qds.dts
@@ -215,7 +215,7 @@
reg = <0x2a>;
VDDA-supply = <®_3p3v>;
VDDIO-supply = <®_3p3v>;
- clocks = <&sys_mclk 1>;
+ clocks = <&sys_mclk>;
};
};
};
diff --git a/arch/arm/boot/dts/ls1021a-twr.dts b/arch/arm/boot/dts/ls1021a-twr.dts
index a8b148a..44715c8 100644
--- a/arch/arm/boot/dts/ls1021a-twr.dts
+++ b/arch/arm/boot/dts/ls1021a-twr.dts
@@ -187,7 +187,7 @@
reg = <0x0a>;
VDDA-supply = <®_3p3v>;
VDDIO-supply = <®_3p3v>;
- clocks = <&sys_mclk 1>;
+ clocks = <&sys_mclk>;
};
};
diff --git a/arch/arm/boot/dts/moxart-uc7112lx.dts b/arch/arm/boot/dts/moxart-uc7112lx.dts
index 10d088d..4a962a2 100644
--- a/arch/arm/boot/dts/moxart-uc7112lx.dts
+++ b/arch/arm/boot/dts/moxart-uc7112lx.dts
@@ -6,7 +6,7 @@
*/
/dts-v1/;
-/include/ "moxart.dtsi"
+#include "moxart.dtsi"
/ {
model = "MOXA UC-7112-LX";
diff --git a/arch/arm/boot/dts/moxart.dtsi b/arch/arm/boot/dts/moxart.dtsi
index 1fd27ed..64f2f44 100644
--- a/arch/arm/boot/dts/moxart.dtsi
+++ b/arch/arm/boot/dts/moxart.dtsi
@@ -6,6 +6,7 @@
*/
/include/ "skeleton.dtsi"
+#include <dt-bindings/interrupt-controller/irq.h>
/ {
compatible = "moxa,moxart";
@@ -36,8 +37,8 @@
ranges;
intc: interrupt-controller@98800000 {
- compatible = "moxa,moxart-ic";
- reg = <0x98800000 0x38>;
+ compatible = "moxa,moxart-ic", "faraday,ftintc010";
+ reg = <0x98800000 0x100>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-mask = <0x00080000>;
@@ -59,7 +60,7 @@
timer: timer@98400000 {
compatible = "moxa,moxart-timer";
reg = <0x98400000 0x42>;
- interrupts = <19 1>;
+ interrupts = <19 IRQ_TYPE_EDGE_FALLING>;
clocks = <&clk_apb>;
};
@@ -80,7 +81,7 @@
dma: dma@90500000 {
compatible = "moxa,moxart-dma";
reg = <0x90500080 0x40>;
- interrupts = <24 0>;
+ interrupts = <24 IRQ_TYPE_LEVEL_HIGH>;
#dma-cells = <1>;
};
@@ -93,7 +94,7 @@
sdhci: sdhci@98e00000 {
compatible = "moxa,moxart-sdhci";
reg = <0x98e00000 0x5C>;
- interrupts = <5 0>;
+ interrupts = <5 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk_apb>;
dmas = <&dma 5>,
<&dma 5>;
@@ -120,7 +121,7 @@
mac0: mac@90900000 {
compatible = "moxa,moxart-mac";
reg = <0x90900000 0x90>;
- interrupts = <25 0>;
+ interrupts = <25 IRQ_TYPE_LEVEL_HIGH>;
phy-handle = <ðphy0>;
phy-mode = "mii";
status = "disabled";
@@ -129,7 +130,7 @@
mac1: mac@92000000 {
compatible = "moxa,moxart-mac";
reg = <0x92000000 0x90>;
- interrupts = <27 0>;
+ interrupts = <27 IRQ_TYPE_LEVEL_HIGH>;
phy-handle = <ðphy1>;
phy-mode = "mii";
status = "disabled";
@@ -138,7 +139,7 @@
uart0: uart@98200000 {
compatible = "ns16550a";
reg = <0x98200000 0x20>;
- interrupts = <31 8>;
+ interrupts = <31 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
reg-io-width = <4>;
clock-frequency = <14745600>;
diff --git a/arch/arm/boot/dts/omap3-n900.dts b/arch/arm/boot/dts/omap3-n900.dts
index 4d448f1..6003b29 100644
--- a/arch/arm/boot/dts/omap3-n900.dts
+++ b/arch/arm/boot/dts/omap3-n900.dts
@@ -510,7 +510,7 @@
tlv320aic3x: tlv320aic3x@18 {
compatible = "ti,tlv320aic3x";
reg = <0x18>;
- gpio-reset = <&gpio2 28 GPIO_ACTIVE_HIGH>; /* 60 */
+ reset-gpios = <&gpio2 28 GPIO_ACTIVE_LOW>; /* 60 */
ai3x-gpio-func = <
0 /* AIC3X_GPIO1_FUNC_DISABLED */
5 /* AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT */
@@ -527,7 +527,7 @@
tlv320aic3x_aux: tlv320aic3x@19 {
compatible = "ti,tlv320aic3x";
reg = <0x19>;
- gpio-reset = <&gpio2 28 GPIO_ACTIVE_HIGH>; /* 60 */
+ reset-gpios = <&gpio2 28 GPIO_ACTIVE_LOW>; /* 60 */
AVDD-supply = <&vmmc2>;
DRVDD-supply = <&vmmc2>;
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-atp.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-atp.dtsi
index 6a3b391..519367c 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-atp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-atp.dtsi
@@ -23,3 +23,13 @@
&qnand_1 {
status = "ok";
};
+
+&vbus_detect {
+ status = "okay";
+};
+
+&usb {
+ status = "okay";
+ qcom,connector-type-uAB;
+ extcon = <0>, <0>, <0>, <&vbus_detect>;
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-coresight.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-coresight.dtsi
index c652a44..9da25abd 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-coresight.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-coresight.dtsi
@@ -520,6 +520,34 @@
<&tpda_modem_out_funnel_in1>;
};
};
+
+ port@4 {
+ reg = <0>;
+ funnel_in1_in_etm0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm0_out_funnel_in1>;
+ };
+ };
+ };
+ };
+
+ etm0: etm@7002000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b956>;
+
+ reg = <0x7002000 0x1000>;
+ cpu = <&CPU0>;
+
+ coresight-name = "coresight-etm0";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ port {
+ etm0_out_funnel_in1: endpoint {
+ remote-endpoint = <&funnel_in1_in_etm0>;
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp-256.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp-256.dtsi
index 518d8a1..dafd0b8 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp-256.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp-256.dtsi
@@ -25,6 +25,10 @@
status = "okay";
};
+&ipa_hw {
+ qcom,use-ipa-in-mhi-mode;
+};
+
&pcie0 {
status = "disabled";
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.dtsi
index 518d8a1..dafd0b8 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.dtsi
@@ -25,6 +25,10 @@
status = "okay";
};
+&ipa_hw {
+ qcom,use-ipa-in-mhi-mode;
+};
+
&pcie0 {
status = "disabled";
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp-256.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp-256.dtsi
index eb544e6..6c5f3c3 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp-256.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp-256.dtsi
@@ -25,6 +25,10 @@
status = "okay";
};
+&ipa_hw {
+ qcom,use-ipa-in-mhi-mode;
+};
+
&pcie0 {
status = "disabled";
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.dtsi
index eb544e6..6c5f3c3 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.dtsi
@@ -25,6 +25,10 @@
status = "okay";
};
+&ipa_hw {
+ qcom,use-ipa-in-mhi-mode;
+};
+
&pcie0 {
status = "disabled";
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index 38ba7fc..d0f484c 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -691,6 +691,34 @@
qcom,glinkpkt-dev-name = "glink_pkt_loopback";
};
+ qcom,glinkpkt-data5-cntl {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "mpss";
+ qcom,glinkpkt-ch-name = "DATA5_CNTL";
+ qcom,glinkpkt-dev-name = "smdcntl0";
+ };
+
+ qcom,glinkpkt-data6-cntl {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "mpss";
+ qcom,glinkpkt-ch-name = "DATA6_CNTL";
+ qcom,glinkpkt-dev-name = "smdcntl1";
+ };
+
+ qcom,glinkpkt-apr-apps2 {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "mpss";
+ qcom,glinkpkt-ch-name = "apr_apps2";
+ qcom,glinkpkt-dev-name = "apr_apps2";
+ };
+
+ qcom,glinkpkt-data22 {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "mpss";
+ qcom,glinkpkt-ch-name = "DATA22";
+ qcom,glinkpkt-dev-name = "smd22";
+ };
+
qcom,glinkpkt-data40-cntl {
qcom,glinkpkt-transport = "smem";
qcom,glinkpkt-edge = "mpss";
@@ -718,6 +746,13 @@
qcom,glinkpkt-ch-name = "DATA11";
qcom,glinkpkt-dev-name = "smd11";
};
+
+ qcom,glinkpkt-data21 {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "mpss";
+ qcom,glinkpkt-ch-name = "DATA21";
+ qcom,glinkpkt-dev-name = "smd21";
+ };
};
pil_modem: qcom,mss@4080000 {
diff --git a/arch/arm/boot/dts/r7s72100.dtsi b/arch/arm/boot/dts/r7s72100.dtsi
index fb9ef9c..959e3ed 100644
--- a/arch/arm/boot/dts/r7s72100.dtsi
+++ b/arch/arm/boot/dts/r7s72100.dtsi
@@ -112,7 +112,7 @@
#clock-cells = <1>;
compatible = "renesas,r7s72100-mstp-clocks", "renesas,cpg-mstp-clocks";
reg = <0xfcfe0430 4>;
- clocks = <&p0_clk>;
+ clocks = <&b_clk>;
clock-indices = <R7S72100_CLK_ETHER>;
clock-output-names = "ether";
};
diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi
index b6c6410..262a512 100644
--- a/arch/arm/boot/dts/r8a7790.dtsi
+++ b/arch/arm/boot/dts/r8a7790.dtsi
@@ -1437,8 +1437,11 @@
compatible = "renesas,r8a7790-mstp-clocks", "renesas,cpg-mstp-clocks";
reg = <0 0xe6150998 0 4>, <0 0xe61509a8 0 4>;
clocks = <&p_clk>,
- <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>,
- <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>,
+ <&mstp10_clks R8A7790_CLK_SSI_ALL>, <&mstp10_clks R8A7790_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7790_CLK_SSI_ALL>, <&mstp10_clks R8A7790_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7790_CLK_SSI_ALL>, <&mstp10_clks R8A7790_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7790_CLK_SSI_ALL>, <&mstp10_clks R8A7790_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7790_CLK_SSI_ALL>, <&mstp10_clks R8A7790_CLK_SSI_ALL>,
<&p_clk>,
<&mstp10_clks R8A7790_CLK_SCU_ALL>, <&mstp10_clks R8A7790_CLK_SCU_ALL>,
<&mstp10_clks R8A7790_CLK_SCU_ALL>, <&mstp10_clks R8A7790_CLK_SCU_ALL>,
diff --git a/arch/arm/boot/dts/r8a7791-koelsch.dts b/arch/arm/boot/dts/r8a7791-koelsch.dts
index f8a7d09..12841e9 100644
--- a/arch/arm/boot/dts/r8a7791-koelsch.dts
+++ b/arch/arm/boot/dts/r8a7791-koelsch.dts
@@ -279,7 +279,7 @@
x2_clk: x2-clock {
compatible = "fixed-clock";
#clock-cells = <0>;
- clock-frequency = <148500000>;
+ clock-frequency = <74250000>;
};
x13_clk: x13-clock {
diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi
index 162b55c..59405eb 100644
--- a/arch/arm/boot/dts/r8a7791.dtsi
+++ b/arch/arm/boot/dts/r8a7791.dtsi
@@ -74,9 +74,8 @@
next-level-cache = <&L2_CA15>;
};
- L2_CA15: cache-controller@0 {
+ L2_CA15: cache-controller-0 {
compatible = "cache";
- reg = <0>;
power-domains = <&sysc R8A7791_PD_CA15_SCU>;
cache-unified;
cache-level = <2>;
@@ -1438,8 +1437,11 @@
compatible = "renesas,r8a7791-mstp-clocks", "renesas,cpg-mstp-clocks";
reg = <0 0xe6150998 0 4>, <0 0xe61509a8 0 4>;
clocks = <&p_clk>,
- <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>,
- <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>,
+ <&mstp10_clks R8A7791_CLK_SSI_ALL>, <&mstp10_clks R8A7791_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7791_CLK_SSI_ALL>, <&mstp10_clks R8A7791_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7791_CLK_SSI_ALL>, <&mstp10_clks R8A7791_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7791_CLK_SSI_ALL>, <&mstp10_clks R8A7791_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7791_CLK_SSI_ALL>, <&mstp10_clks R8A7791_CLK_SSI_ALL>,
<&p_clk>,
<&mstp10_clks R8A7791_CLK_SCU_ALL>, <&mstp10_clks R8A7791_CLK_SCU_ALL>,
<&mstp10_clks R8A7791_CLK_SCU_ALL>, <&mstp10_clks R8A7791_CLK_SCU_ALL>,
diff --git a/arch/arm/boot/dts/r8a7792.dtsi b/arch/arm/boot/dts/r8a7792.dtsi
index 713141d..0b50c67 100644
--- a/arch/arm/boot/dts/r8a7792.dtsi
+++ b/arch/arm/boot/dts/r8a7792.dtsi
@@ -58,9 +58,8 @@
next-level-cache = <&L2_CA15>;
};
- L2_CA15: cache-controller@0 {
+ L2_CA15: cache-controller-0 {
compatible = "cache";
- reg = <0>;
cache-unified;
cache-level = <2>;
power-domains = <&sysc R8A7792_PD_CA15_SCU>;
diff --git a/arch/arm/boot/dts/r8a7793.dtsi b/arch/arm/boot/dts/r8a7793.dtsi
index 8d02aac..e9625cb 100644
--- a/arch/arm/boot/dts/r8a7793.dtsi
+++ b/arch/arm/boot/dts/r8a7793.dtsi
@@ -65,9 +65,8 @@
power-domains = <&sysc R8A7793_PD_CA15_CPU1>;
};
- L2_CA15: cache-controller@0 {
+ L2_CA15: cache-controller-0 {
compatible = "cache";
- reg = <0>;
power-domains = <&sysc R8A7793_PD_CA15_SCU>;
cache-unified;
cache-level = <2>;
@@ -1235,8 +1234,11 @@
compatible = "renesas,r8a7793-mstp-clocks", "renesas,cpg-mstp-clocks";
reg = <0 0xe6150998 0 4>, <0 0xe61509a8 0 4>;
clocks = <&p_clk>,
- <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>,
- <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>,
+ <&mstp10_clks R8A7793_CLK_SSI_ALL>, <&mstp10_clks R8A7793_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7793_CLK_SSI_ALL>, <&mstp10_clks R8A7793_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7793_CLK_SSI_ALL>, <&mstp10_clks R8A7793_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7793_CLK_SSI_ALL>, <&mstp10_clks R8A7793_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7793_CLK_SSI_ALL>, <&mstp10_clks R8A7793_CLK_SSI_ALL>,
<&p_clk>,
<&mstp10_clks R8A7793_CLK_SCU_ALL>, <&mstp10_clks R8A7793_CLK_SCU_ALL>,
<&mstp10_clks R8A7793_CLK_SCU_ALL>, <&mstp10_clks R8A7793_CLK_SCU_ALL>,
diff --git a/arch/arm/boot/dts/r8a7794-silk.dts b/arch/arm/boot/dts/r8a7794-silk.dts
index cf880ac..8874451 100644
--- a/arch/arm/boot/dts/r8a7794-silk.dts
+++ b/arch/arm/boot/dts/r8a7794-silk.dts
@@ -425,7 +425,7 @@
status = "okay";
clocks = <&mstp7_clks R8A7794_CLK_DU0>,
- <&mstp7_clks R8A7794_CLK_DU0>,
+ <&mstp7_clks R8A7794_CLK_DU1>,
<&x2_clk>, <&x3_clk>;
clock-names = "du.0", "du.1", "dclkin.0", "dclkin.1";
diff --git a/arch/arm/boot/dts/r8a7794.dtsi b/arch/arm/boot/dts/r8a7794.dtsi
index 7e860d3..d8f4ca8 100644
--- a/arch/arm/boot/dts/r8a7794.dtsi
+++ b/arch/arm/boot/dts/r8a7794.dtsi
@@ -56,9 +56,8 @@
next-level-cache = <&L2_CA7>;
};
- L2_CA7: cache-controller@0 {
+ L2_CA7: cache-controller-0 {
compatible = "cache";
- reg = <0>;
power-domains = <&sysc R8A7794_PD_CA7_SCU>;
cache-unified;
cache-level = <2>;
@@ -917,7 +916,7 @@
interrupts = <GIC_SPI 256 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp7_clks R8A7794_CLK_DU0>,
- <&mstp7_clks R8A7794_CLK_DU0>;
+ <&mstp7_clks R8A7794_CLK_DU1>;
clock-names = "du.0", "du.1";
status = "disabled";
@@ -1262,19 +1261,21 @@
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>;
+ <&zx_clk>, <&zx_clk>;
#clock-cells = <1>;
clock-indices = <
R8A7794_CLK_EHCI R8A7794_CLK_HSUSB
R8A7794_CLK_HSCIF2 R8A7794_CLK_SCIF5
R8A7794_CLK_SCIF4 R8A7794_CLK_HSCIF1 R8A7794_CLK_HSCIF0
R8A7794_CLK_SCIF3 R8A7794_CLK_SCIF2 R8A7794_CLK_SCIF1
- R8A7794_CLK_SCIF0 R8A7794_CLK_DU0
+ R8A7794_CLK_SCIF0
+ R8A7794_CLK_DU1 R8A7794_CLK_DU0
>;
clock-output-names =
"ehci", "hsusb",
"hscif2", "scif5", "scif4", "hscif1", "hscif0",
- "scif3", "scif2", "scif1", "scif0", "du0";
+ "scif3", "scif2", "scif1", "scif0",
+ "du1", "du0";
};
mstp8_clks: mstp8_clks@e6150990 {
compatible = "renesas,r8a7794-mstp-clocks", "renesas,cpg-mstp-clocks";
diff --git a/arch/arm/configs/bcm2835_defconfig b/arch/arm/configs/bcm2835_defconfig
index 79de828..e32b055 100644
--- a/arch/arm/configs/bcm2835_defconfig
+++ b/arch/arm/configs/bcm2835_defconfig
@@ -1,6 +1,5 @@
# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_SYSVIPC=y
-CONFIG_FHANDLE=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_BSD_PROCESS_ACCT=y
@@ -32,6 +31,7 @@
CONFIG_AEABI=y
CONFIG_KSM=y
CONFIG_CLEANCACHE=y
+CONFIG_CMA=y
CONFIG_SECCOMP=y
CONFIG_KEXEC=y
CONFIG_CRASH_DUMP=y
@@ -52,6 +52,7 @@
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_STANDALONE is not set
+CONFIG_DMA_CMA=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_SCSI_CONSTANTS=y
@@ -62,7 +63,6 @@
CONFIG_ZD1211RW=y
CONFIG_INPUT_EVDEV=y
# CONFIG_LEGACY_PTYS is not set
-# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_AMBA_PL011=y
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
CONFIG_TTY_PRINTK=y
diff --git a/arch/arm/configs/msm8909-perf_defconfig b/arch/arm/configs/msm8909-perf_defconfig
new file mode 100644
index 0000000..6f3cee4
--- /dev/null
+++ b/arch/arm/configs/msm8909-perf_defconfig
@@ -0,0 +1,499 @@
+CONFIG_LOCALVERSION="-perf"
+# CONFIG_AUDITSYSCALL is not set
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_SCHED_WALT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_RCU_EXPERT=y
+CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHEDTUNE=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_CGROUP_BPF=y
+CONFIG_SCHED_CORE_CTL=y
+CONFIG_NAMESPACES=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_SCHED_TUNE=y
+CONFIG_DEFAULT_USE_ENERGY_AWARE=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_LZ4 is not set
+CONFIG_KALLSYMS_ALL=y
+CONFIG_BPF_SYSCALL=y
+# CONFIG_MEMBARRIER is not set
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
+CONFIG_ARCH_MMAP_RND_BITS=16
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SIG=y
+CONFIG_MODULE_SIG_FORCE=y
+CONFIG_MODULE_SIG_SHA512=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_ARCH_QCOM=y
+CONFIG_ARCH_MSM8909=y
+CONFIG_SMP=y
+CONFIG_SCHED_MC=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_CMA=y
+CONFIG_ZSMALLOC=y
+CONFIG_BALANCE_ANON_FILE_RECLAIM=y
+CONFIG_SECCOMP=y
+CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_KERNEL_MODE_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_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_XFRM_STATISTICS=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_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
+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_IPV6_SUBTREES=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_HARDIDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_LOG=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
+CONFIG_NETFILTER_XT_TARGET_TEE=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_DSCP=y
+CONFIG_NETFILTER_XT_MATCH_ESP=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_MULTIPORT=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_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=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_MATCH_RPFILTER=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_BRIDGE_NF_EBTABLES=y
+CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_L2TP=y
+CONFIG_L2TP_V3=y
+CONFIG_L2TP_IP=y
+CONFIG_L2TP_ETH=y
+CONFIG_BRIDGE=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_SCH_MULTIQ=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_CLS_FW=y
+CONFIG_NET_CLS_U32=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_FLOW=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_CMP=y
+CONFIG_NET_EMATCH_NBYTE=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_EMATCH_META=y
+CONFIG_NET_EMATCH_TEXT=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_NET_ACT_SKBEDIT=y
+CONFIG_DNS_RESOLVER=y
+CONFIG_RMNET_DATA=y
+CONFIG_RMNET_DATA_FC=y
+CONFIG_RMNET_DATA_DEBUG_PKT=y
+CONFIG_BT=y
+CONFIG_MSM_BT_POWER=y
+CONFIG_CFG80211=y
+CONFIG_CFG80211_INTERNAL_REGDB=y
+CONFIG_RFKILL=y
+CONFIG_NFC_NQ=y
+CONFIG_IPC_ROUTER=y
+CONFIG_IPC_ROUTER_SECURITY=y
+CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+CONFIG_DMA_CMA=y
+CONFIG_ZRAM=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_HDCP_QSEECOM=y
+CONFIG_QSEECOM=y
+CONFIG_UID_SYS_STATS=y
+CONFIG_MEMORY_STATE_TIME=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_SCSI_UFSHCD=y
+CONFIG_SCSI_UFSHCD_PLATFORM=y
+CONFIG_SCSI_UFS_QCOM=y
+CONFIG_SCSI_UFS_QCOM_ICE=y
+CONFIG_SCSI_UFSHCD_CMD_LOGGING=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_DEBUG=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_TUN=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOE=y
+CONFIG_PPPOL2TP=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
+CONFIG_CLD_LL_CORE=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_QPNP_POWER_ON=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+CONFIG_SERIAL_MSM_SMD=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM_LEGACY=y
+CONFIG_MSM_SMD_PKT=y
+CONFIG_MSM_ADSPRPC=y
+CONFIG_MSM_RDBG=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MSM_V2=y
+CONFIG_SPI=y
+CONFIG_SPI_QUP=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_SLIMBUS_MSM_NGD=y
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
+CONFIG_PINCTRL_MSM8909=y
+CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_QCOM=y
+CONFIG_QCOM_DLOAD_MODE=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_SMB1360_CHARGER_FG=y
+CONFIG_QPNP_VM_BMS=y
+CONFIG_QPNP_LINEAR_CHARGER=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_QPNP=y
+CONFIG_THERMAL_QPNP_ADC_TM=y
+CONFIG_THERMAL_TSENS=y
+CONFIG_MSM_BCL_PERIPHERAL_CTL=y
+CONFIG_QTI_THERMAL_LIMITS_DCVS=y
+CONFIG_MFD_QCOM_RPM=y
+CONFIG_MFD_SPMI_PMIC=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_PROXY_CONSUMER=y
+CONFIG_REGULATOR_QCOM_RPM=y
+CONFIG_REGULATOR_QCOM_SPMI=y
+CONFIG_REGULATOR_CPR=y
+CONFIG_REGULATOR_MEM_ACC=y
+CONFIG_REGULATOR_MSM_GFX_LDO=y
+CONFIG_REGULATOR_QPNP=y
+CONFIG_REGULATOR_RPM_SMD=y
+CONFIG_REGULATOR_SPM=y
+CONFIG_REGULATOR_STUB=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_FB=y
+CONFIG_FB_VIRTUAL=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_LOGO=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_DYNAMIC_MINORS=y
+CONFIG_SND_SOC=y
+CONFIG_UHID=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_USB_DWC3=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_DUAL_ROLE_USB_INTF=y
+CONFIG_USB_MSM_SSPHY_QMP=y
+CONFIG_MSM_QUSB_PHY=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_CI13XXX_MSM=y
+CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_SERIAL=y
+CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_USB_CONFIGFS_UEVENT=y
+CONFIG_USB_CONFIGFS_F_DIAG=y
+CONFIG_USB_CONFIGFS_F_CDEV=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_RING_BUFFER=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
+CONFIG_MMC_TEST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_MSM=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_QPNP=y
+CONFIG_LEDS_QPNP_VIBRATOR=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_QPNP=y
+CONFIG_DMADEVICES=y
+CONFIG_QCOM_SPS_DMA=y
+CONFIG_SYNC_FILE=y
+CONFIG_UIO=y
+CONFIG_UIO_MSM_SHAREDMEM=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_ION=y
+CONFIG_ION_MSM=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_REVID=y
+CONFIG_USB_BAM=y
+CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MAILBOX=y
+CONFIG_ARM_SMMU=y
+CONFIG_QCOM_PM=y
+CONFIG_MSM_SPM=y
+CONFIG_MSM_L2_SPM=y
+CONFIG_MSM_BOOT_STATS=y
+CONFIG_MSM_CORE_HANG_DETECT=y
+CONFIG_MSM_GLADIATOR_HANG_DETECT=y
+CONFIG_QCOM_WATCHDOG_V2=y
+CONFIG_QCOM_MEMORY_DUMP_V2=y
+CONFIG_QCOM_BUS_SCALING=y
+CONFIG_QCOM_SECURE_BUFFER=y
+CONFIG_QCOM_EARLY_RANDOM=y
+CONFIG_MSM_SMEM=y
+CONFIG_MSM_SMD=y
+CONFIG_MSM_SMD_DEBUG=y
+CONFIG_MSM_GLINK=y
+CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
+CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
+CONFIG_MSM_GLINK_SPI_XPRT=y
+CONFIG_TRACER_PKT=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
+CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
+CONFIG_MSM_QMI_INTERFACE=y
+CONFIG_MSM_GLINK_PKT=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_PIL=y
+CONFIG_MSM_EVENT_TIMER=y
+CONFIG_QTI_RPM_STATS_LOG=y
+CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_PWM=y
+CONFIG_PWM_QPNP=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_SENSORS_SSC=y
+CONFIG_MSM_TZ_LOG=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+CONFIG_FUSE_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_ECRYPT_FS=y
+CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+CONFIG_FRAME_WARN=2048
+CONFIG_PAGE_OWNER=y
+CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_LOCKUP_DETECTOR=y
+# CONFIG_DETECT_HUNG_TASK is not set
+CONFIG_WQ_WATCHDOG=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_PANIC_ON_SCHED_BUG=y
+CONFIG_PANIC_ON_RT_THROTTLING=y
+CONFIG_SCHEDSTATS=y
+CONFIG_SCHED_STACK_END_CHECK=y
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_FTRACE is not set
+CONFIG_LKDTM=y
+CONFIG_PANIC_ON_DATA_CORRUPTION=y
+# CONFIG_ARM_UNWIND is not set
+CONFIG_PID_IN_CONTEXTIDR=y
+CONFIG_DEBUG_SET_MODULE_RONX=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_REMOTE_ETM=y
+CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_TPDA=y
+CONFIG_CORESIGHT_TPDM=y
+CONFIG_CORESIGHT_CTI=y
+CONFIG_CORESIGHT_EVENT=y
+CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
+CONFIG_SECURITY=y
+CONFIG_LSM_MMAP_MIN_ADDR=4096
+CONFIG_HARDENED_USERCOPY=y
+CONFIG_SECURITY_SELINUX=y
+CONFIG_SECURITY_SMACK=y
+CONFIG_SECURITY_APPARMOR=y
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_CRYPTO_CTR=y
+CONFIG_CRYPTO_XTS=y
+CONFIG_CRYPTO_XCBC=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_ANSI_CPRNG=y
+CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
+CONFIG_CRYPTO_DEV_OTA_CRYPTO=y
+CONFIG_CRYPTO_DEV_QCOM_ICE=y
+CONFIG_ARM_CRYPTO=y
+CONFIG_CRYPTO_SHA1_ARM_NEON=y
+CONFIG_CRYPTO_SHA2_ARM_CE=y
+CONFIG_CRYPTO_AES_ARM_BS=y
+CONFIG_CRYPTO_AES_ARM_CE=y
+CONFIG_XZ_DEC=y
+CONFIG_QMI_ENCDEC=y
diff --git a/arch/arm/configs/msm8909_defconfig b/arch/arm/configs/msm8909_defconfig
new file mode 100644
index 0000000..f57e45d
--- /dev/null
+++ b/arch/arm/configs/msm8909_defconfig
@@ -0,0 +1,524 @@
+# CONFIG_AUDITSYSCALL is not set
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_SCHED_WALT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_RCU_EXPERT=y
+CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHEDTUNE=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_CGROUP_BPF=y
+CONFIG_SCHED_CORE_CTL=y
+CONFIG_NAMESPACES=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_SCHED_TUNE=y
+CONFIG_DEFAULT_USE_ENERGY_AWARE=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_EMBEDDED=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SIG=y
+CONFIG_MODULE_SIG_FORCE=y
+CONFIG_MODULE_SIG_SHA512=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_ARCH_QCOM=y
+CONFIG_ARCH_MSM8909=y
+CONFIG_SMP=y
+CONFIG_SCHED_MC=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_CMA=y
+CONFIG_CMA_DEBUGFS=y
+CONFIG_ZSMALLOC=y
+CONFIG_BALANCE_ANON_FILE_RECLAIM=y
+CONFIG_SECCOMP=y
+CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_KERNEL_MODE_NEON=y
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+CONFIG_PM_DEBUG=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_XFRM_STATISTICS=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_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
+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_IPV6_SUBTREES=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_HARDIDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_LOG=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
+CONFIG_NETFILTER_XT_TARGET_TEE=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_DSCP=y
+CONFIG_NETFILTER_XT_MATCH_ESP=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_MULTIPORT=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_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=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_MATCH_RPFILTER=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_BRIDGE_NF_EBTABLES=y
+CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_L2TP=y
+CONFIG_L2TP_DEBUGFS=y
+CONFIG_L2TP_V3=y
+CONFIG_L2TP_IP=y
+CONFIG_L2TP_ETH=y
+CONFIG_BRIDGE=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_SCH_MULTIQ=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_CLS_FW=y
+CONFIG_NET_CLS_U32=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_FLOW=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_CMP=y
+CONFIG_NET_EMATCH_NBYTE=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_EMATCH_META=y
+CONFIG_NET_EMATCH_TEXT=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_NET_ACT_SKBEDIT=y
+CONFIG_DNS_RESOLVER=y
+CONFIG_RMNET_DATA=y
+CONFIG_RMNET_DATA_FC=y
+CONFIG_RMNET_DATA_DEBUG_PKT=y
+CONFIG_BT=y
+CONFIG_MSM_BT_POWER=y
+CONFIG_CFG80211=y
+CONFIG_CFG80211_INTERNAL_REGDB=y
+CONFIG_RFKILL=y
+CONFIG_NFC_NQ=y
+CONFIG_IPC_ROUTER=y
+CONFIG_IPC_ROUTER_SECURITY=y
+CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+CONFIG_DMA_CMA=y
+CONFIG_ZRAM=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_HDCP_QSEECOM=y
+CONFIG_QSEECOM=y
+CONFIG_UID_SYS_STATS=y
+CONFIG_MEMORY_STATE_TIME=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_SCSI_UFSHCD=y
+CONFIG_SCSI_UFSHCD_PLATFORM=y
+CONFIG_SCSI_UFS_QCOM=y
+CONFIG_SCSI_UFS_QCOM_ICE=y
+CONFIG_SCSI_UFSHCD_CMD_LOGGING=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_DEBUG=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_TUN=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOE=y
+CONFIG_PPPOL2TP=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
+CONFIG_CLD_LL_CORE=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_QPNP_POWER_ON=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+CONFIG_SERIAL_MSM_HS=y
+CONFIG_SERIAL_MSM_SMD=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM_LEGACY=y
+CONFIG_MSM_SMD_PKT=y
+CONFIG_MSM_ADSPRPC=y
+CONFIG_MSM_RDBG=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MSM_V2=y
+CONFIG_SPI=y
+CONFIG_SPI_QUP=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_SLIMBUS_MSM_NGD=y
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
+CONFIG_PINCTRL_MSM8909=y
+CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_QCOM=y
+CONFIG_QCOM_DLOAD_MODE=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_SMB1360_CHARGER_FG=y
+CONFIG_QPNP_VM_BMS=y
+CONFIG_QPNP_LINEAR_CHARGER=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_QPNP=y
+CONFIG_THERMAL_QPNP_ADC_TM=y
+CONFIG_THERMAL_TSENS=y
+CONFIG_MSM_BCL_PERIPHERAL_CTL=y
+CONFIG_QTI_THERMAL_LIMITS_DCVS=y
+CONFIG_MFD_QCOM_RPM=y
+CONFIG_MFD_SPMI_PMIC=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_PROXY_CONSUMER=y
+CONFIG_REGULATOR_QCOM_RPM=y
+CONFIG_REGULATOR_QCOM_SPMI=y
+CONFIG_REGULATOR_CPR=y
+CONFIG_REGULATOR_MEM_ACC=y
+CONFIG_REGULATOR_MSM_GFX_LDO=y
+CONFIG_REGULATOR_QPNP=y
+CONFIG_REGULATOR_RPM_SMD=y
+CONFIG_REGULATOR_SPM=y
+CONFIG_REGULATOR_STUB=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_FB=y
+CONFIG_FB_VIRTUAL=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_LOGO=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_DYNAMIC_MINORS=y
+CONFIG_SND_SOC=y
+CONFIG_UHID=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_USB_DWC3=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_DUAL_ROLE_USB_INTF=y
+CONFIG_USB_MSM_SSPHY_QMP=y
+CONFIG_MSM_QUSB_PHY=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_CI13XXX_MSM=y
+CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_SERIAL=y
+CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_USB_CONFIGFS_UEVENT=y
+CONFIG_USB_CONFIGFS_F_DIAG=y
+CONFIG_USB_CONFIGFS_F_CDEV=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_RING_BUFFER=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
+CONFIG_MMC_TEST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_MSM=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_QPNP=y
+CONFIG_LEDS_QPNP_VIBRATOR=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_QPNP=y
+CONFIG_DMADEVICES=y
+CONFIG_QCOM_SPS_DMA=y
+CONFIG_SYNC_FILE=y
+CONFIG_UIO=y
+CONFIG_UIO_MSM_SHAREDMEM=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_ION=y
+CONFIG_ION_MSM=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_REVID=y
+CONFIG_USB_BAM=y
+CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MAILBOX=y
+CONFIG_ARM_SMMU=y
+CONFIG_QCOM_PM=y
+CONFIG_MSM_SPM=y
+CONFIG_MSM_L2_SPM=y
+CONFIG_MSM_BOOT_STATS=y
+CONFIG_MSM_CORE_HANG_DETECT=y
+CONFIG_MSM_GLADIATOR_HANG_DETECT=y
+CONFIG_QCOM_WATCHDOG_V2=y
+CONFIG_QCOM_MEMORY_DUMP_V2=y
+CONFIG_QCOM_BUS_SCALING=y
+CONFIG_QCOM_SECURE_BUFFER=y
+CONFIG_QCOM_EARLY_RANDOM=y
+CONFIG_MSM_SMEM=y
+CONFIG_MSM_SMD=y
+CONFIG_MSM_SMD_DEBUG=y
+CONFIG_MSM_GLINK=y
+CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
+CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
+CONFIG_MSM_GLINK_SPI_XPRT=y
+CONFIG_TRACER_PKT=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
+CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
+CONFIG_MSM_QMI_INTERFACE=y
+CONFIG_MSM_GLINK_PKT=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_PIL=y
+CONFIG_MSM_EVENT_TIMER=y
+CONFIG_QTI_RPM_STATS_LOG=y
+CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_PWM=y
+CONFIG_PWM_QPNP=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_SENSORS_SSC=y
+CONFIG_MSM_TZ_LOG=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+CONFIG_FUSE_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_ECRYPT_FS=y
+CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_INFO=y
+CONFIG_FRAME_WARN=2048
+CONFIG_PAGE_OWNER=y
+CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_PAGEALLOC=y
+CONFIG_SLUB_DEBUG_PANIC_ON=y
+CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y
+CONFIG_DEBUG_OBJECTS=y
+CONFIG_DEBUG_OBJECTS_FREE=y
+CONFIG_DEBUG_OBJECTS_TIMERS=y
+CONFIG_DEBUG_OBJECTS_WORK=y
+CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
+CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
+CONFIG_SLUB_DEBUG_ON=y
+CONFIG_DEBUG_KMEMLEAK=y
+CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000
+CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
+CONFIG_DEBUG_STACK_USAGE=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_LOCKUP_DETECTOR=y
+# CONFIG_DETECT_HUNG_TASK is not set
+CONFIG_WQ_WATCHDOG=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_PANIC_ON_SCHED_BUG=y
+CONFIG_PANIC_ON_RT_THROTTLING=y
+CONFIG_SCHEDSTATS=y
+CONFIG_SCHED_STACK_END_CHECK=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+CONFIG_DEBUG_LIST=y
+CONFIG_FAULT_INJECTION=y
+CONFIG_FAIL_PAGE_ALLOC=y
+CONFIG_FAULT_INJECTION_DEBUG_FS=y
+CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
+CONFIG_IPC_LOGGING=y
+CONFIG_QCOM_RTB=y
+CONFIG_QCOM_RTB_SEPARATE_CPUS=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_IRQSOFF_TRACER=y
+CONFIG_PREEMPT_TRACER=y
+CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_CPU_FREQ_SWITCH_PROFILER=y
+CONFIG_LKDTM=y
+CONFIG_MEMTEST=y
+CONFIG_PANIC_ON_DATA_CORRUPTION=y
+CONFIG_DEBUG_USER=y
+CONFIG_PID_IN_CONTEXTIDR=y
+CONFIG_DEBUG_SET_MODULE_RONX=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_REMOTE_ETM=y
+CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_TPDA=y
+CONFIG_CORESIGHT_TPDM=y
+CONFIG_CORESIGHT_CTI=y
+CONFIG_CORESIGHT_EVENT=y
+CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
+CONFIG_SECURITY=y
+CONFIG_LSM_MMAP_MIN_ADDR=4096
+CONFIG_HARDENED_USERCOPY=y
+CONFIG_SECURITY_SELINUX=y
+CONFIG_SECURITY_SMACK=y
+CONFIG_SECURITY_APPARMOR=y
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_CRYPTO_CTR=y
+CONFIG_CRYPTO_XTS=y
+CONFIG_CRYPTO_XCBC=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_ANSI_CPRNG=y
+CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
+CONFIG_CRYPTO_DEV_OTA_CRYPTO=y
+CONFIG_CRYPTO_DEV_QCOM_ICE=y
+CONFIG_ARM_CRYPTO=y
+CONFIG_CRYPTO_SHA1_ARM_NEON=y
+CONFIG_CRYPTO_SHA2_ARM_CE=y
+CONFIG_CRYPTO_AES_ARM_BS=y
+CONFIG_CRYPTO_AES_ARM_CE=y
+CONFIG_QMI_ENCDEC=y
diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig
index 98fb09b..9b55740 100644
--- a/arch/arm/configs/msm8909w-perf_defconfig
+++ b/arch/arm/configs/msm8909w-perf_defconfig
@@ -4,10 +4,6 @@
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
CONFIG_SCHED_WALT=y
-CONFIG_TASKSTATS=y
-CONFIG_TASK_DELAY_ACCT=y
-CONFIG_TASK_XACCT=y
-CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
CONFIG_RCU_NOCB_CPU=y
@@ -49,10 +45,10 @@
CONFIG_SCHED_MC=y
CONFIG_PREEMPT=y
CONFIG_AEABI=y
-CONFIG_HIGHMEM=y
CONFIG_CMA=y
CONFIG_ZSMALLOC=y
CONFIG_BALANCE_ANON_FILE_RECLAIM=y
+CONFIG_PROCESS_RECLAIM=y
CONFIG_SECCOMP=y
CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
@@ -108,14 +104,12 @@
CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_CT=y
CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y
-CONFIG_NETFILTER_XT_TARGET_LOG=y
CONFIG_NETFILTER_XT_TARGET_MARK=y
CONFIG_NETFILTER_XT_TARGET_NFLOG=y
CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
-CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
-CONFIG_NETFILTER_XT_TARGET_TEE=y
CONFIG_NETFILTER_XT_TARGET_TPROXY=y
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
@@ -125,7 +119,6 @@
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
CONFIG_NETFILTER_XT_MATCH_DSCP=y
-CONFIG_NETFILTER_XT_MATCH_ESP=y
CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
CONFIG_NETFILTER_XT_MATCH_HELPER=y
CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
@@ -146,6 +139,8 @@
CONFIG_NETFILTER_XT_MATCH_TIME=y
CONFIG_NETFILTER_XT_MATCH_U32=y
CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_NF_DUP_IPV4=y
+CONFIG_NF_LOG_IPV4=y
CONFIG_IP_NF_IPTABLES=y
CONFIG_IP_NF_MATCH_AH=y
CONFIG_IP_NF_MATCH_ECN=y
@@ -164,6 +159,8 @@
CONFIG_IP_NF_ARPFILTER=y
CONFIG_IP_NF_ARP_MANGLE=y
CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_NF_DUP_IPV6=y
+CONFIG_NF_LOG_IPV6=y
CONFIG_IP6_NF_IPTABLES=y
CONFIG_IP6_NF_MATCH_RPFILTER=y
CONFIG_IP6_NF_FILTER=y
@@ -216,7 +213,6 @@
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_HDCP_QSEECOM=y
CONFIG_QSEECOM=y
-CONFIG_UID_SYS_STATS=y
CONFIG_MEMORY_STATE_TIME=y
CONFIG_QPNP_MISC=y
CONFIG_SCSI=y
@@ -229,11 +225,9 @@
CONFIG_SCSI_UFSHCD=y
CONFIG_SCSI_UFSHCD_PLATFORM=y
CONFIG_SCSI_UFS_QCOM=y
-CONFIG_SCSI_UFS_QCOM_ICE=y
CONFIG_SCSI_UFSHCD_CMD_LOGGING=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
-CONFIG_DM_DEBUG=y
CONFIG_DM_CRYPT=y
CONFIG_DM_UEVENT=y
CONFIG_DM_VERITY=y
@@ -257,7 +251,6 @@
CONFIG_CLD_LL_CORE=y
CONFIG_INPUT_EVDEV=y
CONFIG_KEYBOARD_GPIO=y
-CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v26=y
@@ -346,11 +339,7 @@
CONFIG_HID_MICROSOFT=y
CONFIG_HID_MONTEREY=y
CONFIG_HID_MULTITOUCH=y
-CONFIG_USB_DWC3=y
-CONFIG_NOP_USB_XCEIV=y
CONFIG_DUAL_ROLE_USB_INTF=y
-CONFIG_USB_MSM_SSPHY_QMP=y
-CONFIG_MSM_QUSB_PHY=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
@@ -361,11 +350,10 @@
CONFIG_USB_CONFIGFS_ACM=y
CONFIG_USB_CONFIGFS_NCM=y
CONFIG_USB_CONFIGFS_ECM=y
+CONFIG_USB_CONFIGFS_RMNET_BAM=y
CONFIG_USB_CONFIGFS_EEM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_FS=y
-CONFIG_USB_CONFIGFS_F_MTP=y
-CONFIG_USB_CONFIGFS_F_PTP=y
CONFIG_USB_CONFIGFS_F_ACC=y
CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y
CONFIG_USB_CONFIGFS_UEVENT=y
@@ -373,7 +361,6 @@
CONFIG_USB_CONFIGFS_F_DIAG=y
CONFIG_USB_CONFIGFS_F_CDEV=y
CONFIG_USB_CONFIGFS_F_CCID=y
-CONFIG_USB_CONFIGFS_F_GSI=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
CONFIG_MMC_RING_BUFFER=y
@@ -381,7 +368,6 @@
CONFIG_MMC_CLKGATE=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
-CONFIG_MMC_TEST=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
@@ -400,10 +386,13 @@
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
CONFIG_ION=y
CONFIG_ION_MSM=y
+CONFIG_IPA=y
+CONFIG_RMNET_IPA=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_QPNP_REVID=y
CONFIG_USB_BAM=y
+CONFIG_MSM_RMNET_BAM=y
CONFIG_MSM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_MAILBOX=y
@@ -440,7 +429,6 @@
CONFIG_MSM_PIL_SSR_GENERIC=y
CONFIG_MSM_PIL_MSS_QDSP6V5=y
CONFIG_MSM_EVENT_TIMER=y
-CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MSM_BAM_DMUX=y
CONFIG_MSM_GLINK_BGCOM_XPRT=y
@@ -457,18 +445,18 @@
CONFIG_QCOM_SPMI_VADC=y
CONFIG_QCOM_RRADC=y
CONFIG_PWM=y
+CONFIG_QTI_MPM=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_STM=y
CONFIG_SENSORS_SSC=y
CONFIG_MSM_TZ_LOG=y
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
+CONFIG_QFMT_V2=y
CONFIG_FUSE_FS=y
-CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_ECRYPT_FS=y
@@ -490,21 +478,12 @@
CONFIG_SCHEDSTATS=y
CONFIG_SCHED_STACK_END_CHECK=y
# CONFIG_DEBUG_PREEMPT is not set
-# CONFIG_FTRACE is not set
+CONFIG_IPC_LOGGING=y
CONFIG_LKDTM=y
CONFIG_PANIC_ON_DATA_CORRUPTION=y
# CONFIG_ARM_UNWIND is not set
CONFIG_PID_IN_CONTEXTIDR=y
CONFIG_DEBUG_SET_MODULE_RONX=y
-CONFIG_CORESIGHT=y
-CONFIG_CORESIGHT_REMOTE_ETM=y
-CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
-CONFIG_CORESIGHT_STM=y
-CONFIG_CORESIGHT_TPDA=y
-CONFIG_CORESIGHT_TPDM=y
-CONFIG_CORESIGHT_CTI=y
-CONFIG_CORESIGHT_EVENT=y
-CONFIG_CORESIGHT_HWEVENT=y
CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
CONFIG_SECURITY=y
CONFIG_SECURITYFS=y
@@ -513,13 +492,13 @@
CONFIG_HARDENED_USERCOPY=y
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SMACK=y
-CONFIG_CRYPTO_CTR=y
-CONFIG_CRYPTO_XTS=y
CONFIG_CRYPTO_XCBC=y
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_TWOFISH=y
CONFIG_CRYPTO_ANSI_CPRNG=y
+CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
+CONFIG_CRYPTO_DEV_QCEDEV=y
CONFIG_CRYPTO_DEV_OTA_CRYPTO=y
CONFIG_CRYPTO_DEV_QCOM_ICE=y
CONFIG_ARM_CRYPTO=y
diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig
index 9d1fc06..bb6bc88 100644
--- a/arch/arm/configs/msm8909w_defconfig
+++ b/arch/arm/configs/msm8909w_defconfig
@@ -366,6 +366,7 @@
CONFIG_USB_CONFIGFS_ACM=y
CONFIG_USB_CONFIGFS_NCM=y
CONFIG_USB_CONFIGFS_ECM=y
+CONFIG_USB_CONFIGFS_RMNET_BAM=y
CONFIG_USB_CONFIGFS_EEM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_FS=y
@@ -405,10 +406,13 @@
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
CONFIG_ION=y
CONFIG_ION_MSM=y
+CONFIG_IPA=y
+CONFIG_RMNET_IPA=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_QPNP_REVID=y
CONFIG_USB_BAM=y
+CONFIG_MSM_RMNET_BAM=y
CONFIG_MSM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_MAILBOX=y
@@ -462,6 +466,7 @@
CONFIG_QCOM_SPMI_VADC=y
CONFIG_QCOM_RRADC=y
CONFIG_PWM=y
+CONFIG_QTI_MPM=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_SENSORS_SSC=y
@@ -472,6 +477,7 @@
CONFIG_EXT4_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
+CONFIG_QFMT_V2=y
CONFIG_FUSE_FS=y
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
@@ -556,6 +562,8 @@
CONFIG_CRYPTO_TWOFISH=y
CONFIG_CRYPTO_ANSI_CPRNG=y
CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
+CONFIG_CRYPTO_DEV_QCRYPTO=y
+CONFIG_CRYPTO_DEV_QCEDEV=y
CONFIG_CRYPTO_DEV_OTA_CRYPTO=y
CONFIG_CRYPTO_DEV_QCOM_ICE=y
CONFIG_ARM_CRYPTO=y
diff --git a/arch/arm/configs/msm8937-perf_defconfig b/arch/arm/configs/msm8937-perf_defconfig
index df466cf..585e4d6 100644
--- a/arch/arm/configs/msm8937-perf_defconfig
+++ b/arch/arm/configs/msm8937-perf_defconfig
@@ -6,6 +6,9 @@
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
CONFIG_SCHED_WALT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
CONFIG_RCU_NOCB_CPU=y
@@ -52,6 +55,8 @@
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_MSM8937=y
CONFIG_ARCH_MSM8917=y
+CONFIG_ARCH_SDM439=y
+CONFIG_ARCH_SDM429=y
# CONFIG_VDSO is not set
CONFIG_SMP=y
CONFIG_SCHED_MC=y
@@ -239,6 +244,7 @@
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_HDCP_QSEECOM=y
CONFIG_QSEECOM=y
+CONFIG_UID_SYS_STATS=y
CONFIG_MEMORY_STATE_TIME=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
@@ -530,6 +536,7 @@
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL=y
CONFIG_MSM_PIL_SSR_GENERIC=y
CONFIG_MSM_PIL_MSS_QDSP6V5=y
@@ -538,9 +545,11 @@
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
CONFIG_MSM_PM=y
+CONFIG_QCOM_DCC=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
+# CONFIG_MSM_JTAGV8 is not set
CONFIG_MSM_BAM_DMUX=y
CONFIG_WCNSS_CORE=y
CONFIG_WCNSS_CORE_PRONTO=y
@@ -587,8 +596,12 @@
CONFIG_IPC_LOGGING=y
CONFIG_CPU_FREQ_SWITCH_PROFILER=y
CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
+CONFIG_CORESIGHT_SOURCE_ETM4X=y
CONFIG_CORESIGHT_REMOTE_ETM=y
CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_QCOM_REPLICATOR=y
+CONFIG_CORESIGHT_DBGUI=y
CONFIG_CORESIGHT_STM=y
CONFIG_CORESIGHT_TPDA=y
CONFIG_CORESIGHT_TPDM=y
diff --git a/arch/arm/configs/msm8937_defconfig b/arch/arm/configs/msm8937_defconfig
index e1c655b..d99d3d2 100644
--- a/arch/arm/configs/msm8937_defconfig
+++ b/arch/arm/configs/msm8937_defconfig
@@ -58,6 +58,8 @@
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_MSM8937=y
CONFIG_ARCH_MSM8917=y
+CONFIG_ARCH_SDM439=y
+CONFIG_ARCH_SDM429=y
# CONFIG_VDSO is not set
CONFIG_SMP=y
CONFIG_SCHED_MC=y
@@ -550,6 +552,7 @@
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL=y
CONFIG_MSM_PIL_SSR_GENERIC=y
CONFIG_MSM_PIL_MSS_QDSP6V5=y
@@ -558,9 +561,11 @@
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
CONFIG_MSM_PM=y
+CONFIG_QCOM_DCC=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
+# CONFIG_MSM_JTAGV8 is not set
CONFIG_MSM_BAM_DMUX=y
CONFIG_WCNSS_CORE=y
CONFIG_WCNSS_CORE_PRONTO=y
@@ -650,8 +655,12 @@
CONFIG_PID_IN_CONTEXTIDR=y
CONFIG_DEBUG_SET_MODULE_RONX=y
CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
+CONFIG_CORESIGHT_SOURCE_ETM4X=y
CONFIG_CORESIGHT_REMOTE_ETM=y
CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_QCOM_REPLICATOR=y
+CONFIG_CORESIGHT_DBGUI=y
CONFIG_CORESIGHT_STM=y
CONFIG_CORESIGHT_TPDA=y
CONFIG_CORESIGHT_TPDM=y
diff --git a/arch/arm/configs/msm8953-perf_defconfig b/arch/arm/configs/msm8953-perf_defconfig
index 7627adb..bfa6cc7 100644
--- a/arch/arm/configs/msm8953-perf_defconfig
+++ b/arch/arm/configs/msm8953-perf_defconfig
@@ -308,6 +308,7 @@
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_FT5X06=y
+CONFIG_TOUCHSCREEN_GEN_VKEYS=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_QPNP_POWER_ON=y
diff --git a/arch/arm/configs/msm8953_defconfig b/arch/arm/configs/msm8953_defconfig
index e86e30d..b7dc23d 100644
--- a/arch/arm/configs/msm8953_defconfig
+++ b/arch/arm/configs/msm8953_defconfig
@@ -313,6 +313,7 @@
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_FT5X06=y
+CONFIG_TOUCHSCREEN_GEN_VKEYS=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_QPNP_POWER_ON=y
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index e3c1c1a..646b52d 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -1,3 +1,4 @@
+CONFIG_AUDIT=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IKCONFIG=y
@@ -60,6 +61,7 @@
CONFIG_NETFILTER=y
CONFIG_NETFILTER_DEBUG=y
CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
CONFIG_NF_CONNTRACK_EVENTS=y
CONFIG_NF_CONNTRACK_TIMEOUT=y
CONFIG_NF_CONNTRACK_TIMESTAMP=y
@@ -77,6 +79,7 @@
CONFIG_NF_CT_NETLINK_TIMEOUT=y
CONFIG_NF_CT_NETLINK_HELPER=y
CONFIG_NETFILTER_NETLINK_GLUE_CT=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
CONFIG_NETFILTER_XT_TARGET_LOG=y
CONFIG_NETFILTER_XT_TARGET_MARK=y
CONFIG_NETFILTER_XT_TARGET_NFLOG=y
@@ -84,6 +87,7 @@
CONFIG_NETFILTER_XT_TARGET_NOTRACK=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_ADDRTYPE=y
CONFIG_NETFILTER_XT_MATCH_CONNLABEL=y
@@ -115,6 +119,7 @@
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
@@ -374,6 +379,7 @@
CONFIG_QCOM_DCC_V2=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_QMP_DEBUGFS_CLIENT=y
CONFIG_EXTCON_QCOM_SPMI_MISC=y
CONFIG_IIO=y
CONFIG_PWM=y
@@ -407,6 +413,10 @@
CONFIG_CORESIGHT_CTI=y
CONFIG_CORESIGHT_EVENT=y
CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_SELINUX=y
+# CONFIG_SECURITY_SELINUX_AVC_STATS is not set
CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
CONFIG_CRYPTO_DEV_QCRYPTO=y
CONFIG_CRYPTO_DEV_QCEDEV=y
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
index 7a54d68..4232b47 100644
--- a/arch/arm/configs/sdxpoorwills_defconfig
+++ b/arch/arm/configs/sdxpoorwills_defconfig
@@ -1,3 +1,4 @@
+CONFIG_AUDIT=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IKCONFIG=y
@@ -62,6 +63,7 @@
CONFIG_NETFILTER=y
CONFIG_NETFILTER_DEBUG=y
CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
CONFIG_NF_CONNTRACK_EVENTS=y
CONFIG_NF_CONNTRACK_TIMEOUT=y
CONFIG_NF_CONNTRACK_TIMESTAMP=y
@@ -79,6 +81,7 @@
CONFIG_NF_CT_NETLINK_TIMEOUT=y
CONFIG_NF_CT_NETLINK_HELPER=y
CONFIG_NETFILTER_NETLINK_GLUE_CT=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
CONFIG_NETFILTER_XT_TARGET_LOG=y
CONFIG_NETFILTER_XT_TARGET_MARK=y
CONFIG_NETFILTER_XT_TARGET_NFLOG=y
@@ -86,6 +89,7 @@
CONFIG_NETFILTER_XT_TARGET_NOTRACK=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_ADDRTYPE=y
CONFIG_NETFILTER_XT_MATCH_CONNLABEL=y
@@ -117,6 +121,7 @@
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
@@ -380,6 +385,7 @@
CONFIG_QCOM_DCC_V2=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_QMP_DEBUGFS_CLIENT=y
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_EXTCON_QCOM_SPMI_MISC=y
CONFIG_IIO=y
@@ -423,6 +429,7 @@
CONFIG_DEBUG_USER=y
CONFIG_CORESIGHT=y
CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
+CONFIG_CORESIGHT_SOURCE_ETM3X=y
CONFIG_CORESIGHT_REMOTE_ETM=y
CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
CONFIG_CORESIGHT_QCOM_REPLICATOR=y
@@ -434,6 +441,10 @@
CONFIG_CORESIGHT_TGU=y
CONFIG_CORESIGHT_HWEVENT=y
CONFIG_CORESIGHT_DUMMY=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_SELINUX=y
+# CONFIG_SECURITY_SELINUX_AVC_STATS is not set
CONFIG_CRYPTO_CMAC=y
CONFIG_CRYPTO_SHA256=y
CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c
index 3f17594..414e60e 100644
--- a/arch/arm/kernel/ftrace.c
+++ b/arch/arm/kernel/ftrace.c
@@ -29,11 +29,6 @@
#endif
#ifdef CONFIG_DYNAMIC_FTRACE
-#ifdef CONFIG_OLD_MCOUNT
-#define OLD_MCOUNT_ADDR ((unsigned long) mcount)
-#define OLD_FTRACE_ADDR ((unsigned long) ftrace_caller_old)
-
-#define OLD_NOP 0xe1a00000 /* mov r0, r0 */
static int __ftrace_modify_code(void *data)
{
@@ -51,6 +46,12 @@
stop_machine(__ftrace_modify_code, &command, NULL);
}
+#ifdef CONFIG_OLD_MCOUNT
+#define OLD_MCOUNT_ADDR ((unsigned long) mcount)
+#define OLD_FTRACE_ADDR ((unsigned long) ftrace_caller_old)
+
+#define OLD_NOP 0xe1a00000 /* mov r0, r0 */
+
static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec)
{
return rec->arch.old_mcount ? OLD_NOP : NOP;
diff --git a/arch/arm/kvm/hyp/Makefile b/arch/arm/kvm/hyp/Makefile
index 92eab1d..6104921 100644
--- a/arch/arm/kvm/hyp/Makefile
+++ b/arch/arm/kvm/hyp/Makefile
@@ -6,6 +6,8 @@
KVM=../../../../virt/kvm
+CFLAGS_ARMV7VE :=$(call cc-option, -march=armv7ve)
+
obj-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/hyp/vgic-v2-sr.o
obj-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/hyp/vgic-v3-sr.o
obj-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/hyp/timer-sr.o
@@ -14,7 +16,10 @@
obj-$(CONFIG_KVM_ARM_HOST) += cp15-sr.o
obj-$(CONFIG_KVM_ARM_HOST) += vfp.o
obj-$(CONFIG_KVM_ARM_HOST) += banked-sr.o
+CFLAGS_banked-sr.o += $(CFLAGS_ARMV7VE)
+
obj-$(CONFIG_KVM_ARM_HOST) += entry.o
obj-$(CONFIG_KVM_ARM_HOST) += hyp-entry.o
obj-$(CONFIG_KVM_ARM_HOST) += switch.o
+CFLAGS_switch.o += $(CFLAGS_ARMV7VE)
obj-$(CONFIG_KVM_ARM_HOST) += s2-setup.o
diff --git a/arch/arm/kvm/hyp/banked-sr.c b/arch/arm/kvm/hyp/banked-sr.c
index 111bda8..be4b8b0 100644
--- a/arch/arm/kvm/hyp/banked-sr.c
+++ b/arch/arm/kvm/hyp/banked-sr.c
@@ -20,6 +20,10 @@
#include <asm/kvm_hyp.h>
+/*
+ * gcc before 4.9 doesn't understand -march=armv7ve, so we have to
+ * trick the assembler.
+ */
__asm__(".arch_extension virt");
void __hyp_text __banked_save_state(struct kvm_cpu_context *ctxt)
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index 2a35c19..7f868d9 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -1284,7 +1284,7 @@
return -EFAULT;
}
- if (vma_kernel_pagesize(vma) && !logging_active) {
+ if (vma_kernel_pagesize(vma) == PMD_SIZE && !logging_active) {
hugetlb = true;
gfn = (fault_ipa & PMD_MASK) >> PAGE_SHIFT;
} else {
diff --git a/arch/arm/lib/csumpartialcopyuser.S b/arch/arm/lib/csumpartialcopyuser.S
index 1712f13..b83fdc0 100644
--- a/arch/arm/lib/csumpartialcopyuser.S
+++ b/arch/arm/lib/csumpartialcopyuser.S
@@ -85,7 +85,11 @@
.pushsection .text.fixup,"ax"
.align 4
9001: mov r4, #-EFAULT
+#ifdef CONFIG_CPU_SW_DOMAIN_PAN
+ ldr r5, [sp, #9*4] @ *err_ptr
+#else
ldr r5, [sp, #8*4] @ *err_ptr
+#endif
str r4, [r5]
ldmia sp, {r1, r2} @ retrieve dst, len
add r2, r2, r1
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index a0e66d8..403db76 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -199,6 +199,7 @@
select BRCMSTB_L2_IRQ
select BCM7120_L2_IRQ
select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
+ select ZONE_DMA if ARM_LPAE
select SOC_BRCMSTB
select SOC_BUS
help
diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
index 541647f..895c074 100644
--- a/arch/arm/mach-mvebu/Kconfig
+++ b/arch/arm/mach-mvebu/Kconfig
@@ -42,7 +42,7 @@
depends on ARCH_MULTI_V7
select ARMADA_370_XP_IRQ
select ARM_ERRATA_720789
- select ARM_ERRATA_753970
+ select PL310_ERRATA_753970
select ARM_GIC
select ARMADA_375_CLK
select HAVE_ARM_SCU
@@ -58,7 +58,7 @@
bool "Marvell Armada 380/385 boards"
depends on ARCH_MULTI_V7
select ARM_ERRATA_720789
- select ARM_ERRATA_753970
+ select PL310_ERRATA_753970
select ARM_GIC
select ARMADA_370_XP_IRQ
select ARMADA_38X_CLK
diff --git a/arch/arm/mach-omap2/clockdomains7xx_data.c b/arch/arm/mach-omap2/clockdomains7xx_data.c
index ef9ed36..e3416b4 100644
--- a/arch/arm/mach-omap2/clockdomains7xx_data.c
+++ b/arch/arm/mach-omap2/clockdomains7xx_data.c
@@ -524,7 +524,7 @@
.dep_bit = DRA7XX_PCIE_STATDEP_SHIFT,
.wkdep_srcs = pcie_wkup_sleep_deps,
.sleepdep_srcs = pcie_wkup_sleep_deps,
- .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .flags = CLKDM_CAN_SWSUP,
};
static struct clockdomain atl_7xx_clkdm = {
diff --git a/arch/arm/mach-omap2/omap-secure.c b/arch/arm/mach-omap2/omap-secure.c
index 9ff9205..fa7f308 100644
--- a/arch/arm/mach-omap2/omap-secure.c
+++ b/arch/arm/mach-omap2/omap-secure.c
@@ -73,6 +73,7 @@
return omap_secure_memblock_base;
}
+#if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM)
u32 omap3_save_secure_ram(void __iomem *addr, int size)
{
u32 ret;
@@ -91,6 +92,7 @@
return ret;
}
+#endif
/**
* rx51_secure_dispatcher: Routine to dispatch secure PPA API calls
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 7dd114f..1b135e5 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -16,6 +16,7 @@
select ARCH_HAS_SG_CHAIN
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
select ARCH_USE_CMPXCHG_LOCKREF
+ select ARCH_SUPPORTS_LTO_CLANG
select ARCH_SUPPORTS_ATOMIC_RMW
select ARCH_SUPPORTS_NUMA_BALANCING
select ARCH_WANT_COMPAT_IPC_PARSE_VERSION
@@ -423,7 +424,7 @@
config ARM64_ERRATUM_843419
bool "Cortex-A53: 843419: A load or store might access an incorrect address"
- default y
+ default y if !LTO_CLANG
select ARM64_MODULE_CMODEL_LARGE if MODULES
help
This option links the kernel with '--fix-cortex-a53-843419' and
@@ -1024,7 +1025,7 @@
config RANDOMIZE_MODULE_REGION_FULL
bool "Randomize the module region independently from the core kernel"
- depends on RANDOMIZE_BASE && !DYNAMIC_FTRACE
+ depends on RANDOMIZE_BASE && !DYNAMIC_FTRACE && !LTO_CLANG
default y
help
Randomizes the location of the module region without considering the
@@ -1150,6 +1151,29 @@
Space separated list of names of dtbs to append when
building a concatenated Image.gz-dtb.
+choice
+ prompt "Kernel compression method"
+ default BUILD_ARM64_KERNEL_COMPRESSION_GZIP
+ help
+ Allows choice between gzip compressed or uncompressed
+ kernel image
+
+config BUILD_ARM64_KERNEL_COMPRESSION_GZIP
+ bool "Build compressed kernel image"
+ help
+ Build a kernel image using gzip
+ compression with concatenated dtb.
+ gzip is based on the DEFLATE
+ algorithm.
+
+config BUILD_ARM64_UNCOMPRESSED_KERNEL
+ bool "Build uncompressed kernel image"
+ help
+ Build a kernel image without
+ compression and with
+ concatenated dtb.
+endchoice
+
config BUILD_ARM64_DT_OVERLAY
bool "enable DT overlay compilation support"
depends on OF
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
index 712352b..2b265a7 100644
--- a/arch/arm64/Makefile
+++ b/arch/arm64/Makefile
@@ -26,8 +26,17 @@
ifeq ($(call ld-option, --fix-cortex-a53-843419),)
$(warning ld does not support --fix-cortex-a53-843419; kernel may be susceptible to erratum)
else
+ ifeq ($(call gold-ifversion, -lt, 114000000, y), y)
+$(warning This version of GNU gold may generate incorrect code with --fix-cortex-a53-843419;\
+ see https://sourceware.org/bugzilla/show_bug.cgi?id=21491)
+ endif
LDFLAGS_vmlinux += --fix-cortex-a53-843419
endif
+else
+ ifeq ($(ld-name),gold)
+# Pass --no-fix-cortex-a53-843419 to ensure the erratum fix is disabled
+LDFLAGS += --no-fix-cortex-a53-843419
+ endif
endif
KBUILD_DEFCONFIG := defconfig
@@ -70,6 +79,10 @@
ifeq ($(CONFIG_ARM64_MODULE_CMODEL_LARGE), y)
KBUILD_CFLAGS_MODULE += -mcmodel=large
+ifeq ($(CONFIG_LTO_CLANG), y)
+# Code model is not stored in LLVM IR, so we need to pass it also to LLVMgold
+LDFLAGS += -plugin-opt=-code-model=large
+endif
endif
ifeq ($(CONFIG_ARM64_MODULE_PLTS),y)
@@ -109,11 +122,15 @@
libs-y := arch/arm64/lib/ $(libs-y)
core-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
+ifeq ($(CONFIG_BUILD_ARM64_KERNEL_COMPRESSION_GZIP),y)
+KBUILD_IMAGE := Image.gz
+else
+KBUILD_IMAGE := Image
+endif
+
# Default target when executing plain make
ifeq ($(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE),y)
-KBUILD_IMAGE := $(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME))
-else
-KBUILD_IMAGE := Image.gz
+KBUILD_IMAGE := $(addsuffix -dtb,$(KBUILD_IMAGE))
endif
KBUILD_DTBS := dtbs
diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile
index b97f1de..ccdfa1f 100644
--- a/arch/arm64/boot/Makefile
+++ b/arch/arm64/boot/Makefile
@@ -45,7 +45,11 @@
$(obj)/Image.bz2: $(obj)/Image FORCE
$(call if_changed,bzip2)
-$(obj)/Image-dtb: $(obj)/Image $(DTB_OBJS) FORCE
+$(obj)/Image-dtb-hdr: $(obj)/Image FORCE
+ echo -n 'UNCOMPRESSED_IMG' > $@ && \
+ $(call size_append, $(filter-out FORCE,$^)) >> $@
+
+$(obj)/Image-dtb: $(obj)/Image-dtb-hdr $(obj)/Image $(DTB_OBJS) FORCE
$(call if_changed,cat)
$(obj)/Image.gz: $(obj)/Image FORCE
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index 85c4df1..31d4b0d 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -421,7 +421,8 @@
dtb-$(CONFIG_ARCH_MSM8909) += msm8909w-bg-wtp-v2.dtb \
apq8009w-bg-wtp-v2.dtb \
apq8009w-bg-alpha.dtb \
- apq8009-mtp-wcd9326-refboard.dtb
+ apq8009-mtp-wcd9326-refboard.dtb \
+ apq8009-dragon.dtb
dtb-$(CONFIG_ARCH_SDM450) += sdm450-rcm.dtb \
sdm450-cdp.dtb \
diff --git a/arch/arm64/boot/dts/qcom/apq8009-dragon.dts b/arch/arm64/boot/dts/qcom/apq8009-dragon.dts
new file mode 100644
index 0000000..1446c9c
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8009-dragon.dts
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2018, 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.
+ */
+
+/dts-v1/;
+
+#include "msm8909-mtp.dtsi"
+#include "8909-pm8916.dtsi"
+#include "msm8909-pm8916-mtp.dtsi"
+#include "apq8009-audio-external_codec.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8009 Dragon Board";
+ compatible = "qcom,apq8009-mtp", "qcom,apq8009", "qcom,mtp";
+ qcom,msm-id = <265 2>;
+ qcom,board-id = <32 0x0>;
+};
+
+&soc {
+ ext-codec {
+ qcom,msm-mbhc-hphl-swh = <0>;
+ qcom,audio-routing =
+ "AIF4 VI", "MCLK",
+ "RX_BIAS", "MCLK",
+ "MADINPUT", "MCLK",
+ "AMIC2", "MIC BIAS2",
+ "MIC BIAS2", "Headset Mic",
+ "DMIC0", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic0",
+ "DMIC1", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic1",
+ "DMIC2", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic2",
+ "DMIC3", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic3",
+ "SpkrLeft IN", "SPK1 OUT",
+ "SpkrRight IN", "SPK2 OUT";
+ };
+
+ sound-9335 {
+ status = "disabled";
+ };
+
+ i2c@78b8000 {
+ wcd9xxx_codec@d {
+ status = "disabled";
+ };
+ };
+
+ vph_pwr_vreg: vph_pwr_vreg {
+ compatible = "regulator-fixed";
+ status = "ok";
+ regulator-name = "vph_pwr";
+ regulator-always-on;
+ };
+
+ mdss_mdp: qcom,mdss_mdp@1a00000 {
+ status = "disabled";
+ };
+};
+
+&sdhc_2 {
+ status = "disabled";
+};
+
+&usb_otg {
+ interrupts = <0 134 0>,<0 140 0>,<0 136 0>;
+ interrupt-names = "core_irq", "async_irq", "phy_irq";
+ qcom,hsusb-otg-mode = <3>;
+ qcom,switch-vbus-w-id;
+ vbus_otg-supply = <&vph_pwr_vreg>;
+};
+
+&external_image_mem {
+ reg = <0x0 0x87a00000 0x0 0x0600000>;
+};
+
+&modem_adsp_mem {
+ reg = <0x0 0x88000000 0x0 0x01e00000>;
+};
+
+&peripheral_mem {
+ reg = <0x0 0x89e00000 0x0 0x0700000>;
+};
+
+&i2c_4 {
+ smb1360_otg_supply: smb1360-chg-fg@14 {
+ compatible = "qcom,smb1360-chg-fg";
+ reg = <0x14>;
+ interrupt-parent = <&msm_gpio>;
+ interrupts = <58 8>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb_int_default>;
+ qcom,charging-disabled;
+ qcom,empty-soc-disabled;
+ qcom,chg-inhibit-disabled;
+ qcom,float-voltage-mv = <4200>;
+ qcom,iterm-ma = <200>;
+ qcom,recharge-thresh-mv = <100>;
+ qcom,thermal-mitigation = <1500 700 600 0>;
+ regulator-name = "smb1360_otg_vreg";
+ status= "disabled";
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts b/arch/arm64/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts
index 9efd808..a0a9c54 100644
--- a/arch/arm64/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts
+++ b/arch/arm64/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts
@@ -268,6 +268,7 @@
};
&i2c_4 {
+ status= "okay";
smb1360_otg_supply: smb1360-chg-fg@14 {
compatible = "qcom,smb1360-chg-fg";
reg = <0x14>;
@@ -283,15 +284,12 @@
qcom,recharge-thresh-mv = <100>;
qcom,thermal-mitigation = <1500 700 600 0>;
regulator-name = "smb1360_otg_vreg";
+ status= "okay";
};
};
&usb_otg {
- interrupts = <0 134 0>, <0 140 0>;
- interrupt-names = "core_irq", "async_irq";
-
- qcom,hsusb-otg-mode = <3>;
- vbus_otg-supply = <&vbus_otg_supply>;
+ extcon = <&smb1360_otg_supply>;
};
&mdss_fb0 {
diff --git a/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts b/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts
index 57a28d0..89e1b76 100644
--- a/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts
+++ b/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts
@@ -296,3 +296,8 @@
output-high;
};
};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_auo_390p_cmd>;
+ qcom,platform-bklight-en-gpio = <&msm_gpio 52 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts
index 454ba81..9fb6626 100644
--- a/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts
+++ b/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts
@@ -313,3 +313,8 @@
output-high;
};
};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_auo_390p_cmd>;
+ qcom,platform-bklight-en-gpio = <&msm_gpio 52 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
index 00f9024..5e3ddce 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
@@ -87,6 +87,21 @@
compatible = "qca,qca6174";
qca,bt-reset-gpio = <&tlmm 76 0>; /* BT_EN */
};
+
+ cdc_dmic_gpios: cdc_dmic_pinctrl {
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&cdc_dmic0_clk_act &cdc_dmic0_data_act>;
+ pinctrl-1 = <&cdc_dmic0_clk_sus &cdc_dmic0_data_sus>;
+ };
+
+ cdc_quat_mi2s_gpios: msm_cdc_pinctrl_quat {
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&sec_tlmm_lines_act>;
+ pinctrl-1 = <&sec_tlmm_lines_sus>;
+ };
+
};
&firmware {
@@ -125,10 +140,45 @@
};
};
+&int_codec {
+ status = "ok";
+ qcom,model = "msm8953-openq624-snd-card";
+
+ /delete-property/ qcom,cdc-us-euro-gpios;
+ qcom,msm-hs-micbias-type = "internal";
+
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "SPK_RX_BIAS", "MCLK",
+ "INT_LDO_H", "MCLK",
+ "MIC BIAS Internal1", "Handset Mic",
+ "MIC BIAS Internal2", "Headset Mic",
+ "MIC BIAS Internal1", "Secondary Mic",
+ "AMIC1", "MIC BIAS Internal1",
+ "AMIC2", "MIC BIAS Internal2",
+ "AMIC3", "MIC BIAS Internal1",
+ "DMIC1", "MIC BIAS Internal1",
+ "MIC BIAS Internal1", "Digital Mic1",
+ "DMIC2", "MIC BIAS Internal1",
+ "MIC BIAS Internal1", "Digital Mic2";
+
+ qcom,quat-mi2s-gpios = <&cdc_quat_mi2s_gpios>;
+ qcom,cdc-dmic-gpios = <&cdc_dmic_gpios>;
+
+ /delete-property/ asoc-wsa-codec-names;
+ /delete-property/ asoc-wsa-codec-prefixes;
+ /delete-property/ msm-vdd-wsa-switch-supply;
+ /delete-property/ qcom,msm-vdd-wsa-switch-voltage;
+ /delete-property/ qcom,msm-vdd-wsa-switch-current;
+};
&spi_3 {
status = "disabled";
};
+&i2c_1 {
+ status = "okay";
+};
+
&i2c_2 {
status = "okay";
};
@@ -238,34 +288,18 @@
};
&sdhc_2 {
- /* device core power supply */
- vdd-supply = <&pm8953_l11>;
- qcom,vdd-voltage-level = <2950000 2950000>;
- qcom,vdd-current-level = <15000 800000>;
-
/* device communication power supply */
vdd-io-supply = <&pm8953_l12>;
- qcom,vdd-io-always-on;
qcom,vdd-io-voltage-level = <1800000 1800000>;
qcom,vdd-io-current-level = <200 22000>;
+
qcom,core_3_0v_support;
qcom,nonremovable;
pinctrl-names = "active", "sleep";
- pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on
- &sdc2_wlan_gpio_on>;
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_wlan_gpio_on>;
pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off
- &sdc2_wlan_gpio_off>;
-
- #address-cells = <0>;
- interrupt-parent = <&sdhc_2>;
- interrupts = <0 1 2>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0xffffffff>;
- interrupt-map = <0 &intc 0 125 0
- 1 &intc 0 221 0
- 2 &tlmm 133 0>;
- interrupt-names = "hc_irq", "pwr_irq", "status_irq";
+ &sdc2_wlan_gpio_off>;
qcom,clk-rates = <400000 20000000 25000000 50000000>;
qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi
new file mode 100644
index 0000000..b4ac287
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi
@@ -0,0 +1,98 @@
+/* Copyright (c) 2017-2018, 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.
+ */
+
+&mdss_mdp {
+ dsi_auo_390p_cmd: qcom,mdss_dsi_auo_390p_cmd {
+ qcom,mdss-dsi-panel-name = "AUO 390p command mode dsi panel";
+ qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
+ qcom,mdss-dsi-panel-framerate = <45>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <390>;
+ qcom,mdss-dsi-panel-height = <390>;
+ qcom,mdss-pan-physical-height-dimension = <29>;
+ qcom,mdss-pan-physical-width-dimension = <29>;
+ qcom,mdss-dsi-h-front-porch = <4>;
+ qcom,mdss-dsi-h-back-porch = <4>;
+ qcom,mdss-dsi-h-pulse-width = <4>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <8>;
+ qcom,mdss-dsi-v-front-porch = <8>;
+ qcom,mdss-dsi-v-pulse-width = <8>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-color-order = "rgb_swap_rgb";
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-tear-check-frame-rate = <4500>;
+ qcom,mdss-dsi-on-command = [
+ 15 01 00 00 00 00 02 fe 01
+ 15 01 00 00 00 00 02 0a f0
+ 15 01 00 00 00 00 02 fe 00
+ 15 01 00 00 00 00 02 35 00
+ 29 01 00 00 00 00 05 2a 00 04 01 89
+ 29 01 00 00 00 00 05 2b 00 00 01 85
+ 29 01 00 00 00 00 05 30 00 00 01 85
+ 29 01 00 00 00 00 05 31 00 04 01 89
+ 05 01 00 00 00 00 02 12 00
+ 15 01 00 00 00 00 02 53 20
+ 05 01 00 00 96 00 02 11 00
+ 05 01 00 00 00 00 02 29 00
+ 39 01 00 00 00 00 06 f0 55 aa 52 08 01
+ 39 01 00 00 00 00 07 ff 00 55 aa 52 08 01
+ ];
+ qcom,mdss-dsi-off-command = [
+ 05 01 00 00 00 00 02 28 00
+ 05 01 00 00 78 00 02 10 00
+ ];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+
+ qcom,mdss-dsi-idle-on-command = [
+ 05 01 00 00 00 00 01 39 /* Idle-Mode On */
+ ];
+ qcom,mdss-dsi-idle-on-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-idle-off-command = [
+ 05 01 00 00 00 00 01 38 /* Idle-Mode Off */
+ /* Reset column start address*/
+ 29 01 00 00 00 00 05 2a 00 04 01 89
+ /* Reset row start address */
+ 29 01 00 00 00 00 05 2b 00 00 01 85
+ ];
+ qcom,mdss-dsi-traffic-mode = "burst_mode";
+ qcom,mdss-dsi-lane-map = "lane_map_0123";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-te-pin-select = <1>;
+ qcom,mdss-dsi-te-dcs-command = <1>;
+ qcom,mdss-dsi-te-using-te-pin;
+ qcom,mdss-dsi-te-check-enable;
+ qcom,mdss-dsi-panel-timings = [5f 12 0a 00 32 34 10
+ 16 0f 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x05>;
+ qcom,mdss-dsi-t-clk-pre = <0x11>;
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <255>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
+ qcom,mdss-dsi-reset-sequence = <1 20>, <0 20>, <1 20>;
+ /* clk = totlaH * totalV * bpp* 66fps */
+ qcom,mdss-dsi-panel-clockrate = <276705792>;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "te_signal_check";
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi
index 9b703fe..1ccde7b 100644
--- a/arch/arm64/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi
@@ -31,6 +31,8 @@
qcom,mdss-dsi-h-right-border = <0>;
qcom,mdss-dsi-v-top-border = <0>;
qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-pan-physical-width-dimension = <65>;
+ qcom,mdss-pan-physical-height-dimension = <129>;
qcom,mdss-dsi-bpp = <24>;
qcom,mdss-dsi-color-order = "rgb_swap_rgb";
qcom,mdss-dsi-underflow-color = <0xff>;
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi
index 2c54504..b2a541d 100644
--- a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -37,6 +37,7 @@
qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
qcom,mdss-pan-physical-width-dimension = <74>;
qcom,mdss-pan-physical-height-dimension = <131>;
+ qcom,mdss-dsi-dma-schedule-line = <5>;
qcom,mdss-dsi-display-timings {
timing@0{
diff --git a/arch/arm64/boot/dts/qcom/msm-audio-lpass.dtsi b/arch/arm64/boot/dts/qcom/msm-audio-lpass.dtsi
index b20feef8..9467297 100644
--- a/arch/arm64/boot/dts/qcom/msm-audio-lpass.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-audio-lpass.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, 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
@@ -262,6 +262,11 @@
qcom,msm-dai-q6-dev-id = <240>;
};
+ afe_loopback_tx: qcom,msm-dai-q6-afe-loopback-tx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <24577>;
+ };
+
incall_record_rx: qcom,msm-dai-q6-incall-record-rx {
compatible = "qcom,msm-dai-q6-dev";
qcom,msm-dai-q6-dev-id = <32771>;
@@ -297,6 +302,10 @@
compatible = "qcom,msm-pcm-hostless";
};
+ audio_apr: qcom,msm-audio-apr {
+ compatible = "qcom,msm-audio-apr";
+ };
+
dai_pri_auxpcm: qcom,msm-pri-auxpcm {
compatible = "qcom,msm-auxpcm-dev";
qcom,msm-cpudai-auxpcm-mode = <0>, <0>;
diff --git a/arch/arm64/boot/dts/qcom/msm8909-mdss-panels.dtsi b/arch/arm64/boot/dts/qcom/msm8909-mdss-panels.dtsi
new file mode 100644
index 0000000..0a657da
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8909-mdss-panels.dtsi
@@ -0,0 +1,75 @@
+/* Copyright (c) 2014-2018, 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 "dsi-panel-390p-auo-cmd.dtsi"
+
+&soc {
+ dsi_panel_pwr_supply: dsi_panel_pwr_supply {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,panel-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdd";
+ qcom,supply-min-voltage = <2850000>;
+ qcom,supply-max-voltage = <2850000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@1 {
+ reg = <1>;
+ qcom,supply-name = "vddio";
+ qcom,supply-min-voltage = <1800000>;
+ qcom,supply-max-voltage = <1800000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+ };
+
+ dsi_pm660_panel_pwr_supply: dsi_pm660_panel_pwr_supply {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,panel-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdd";
+ qcom,supply-min-voltage = <3000000>;
+ qcom,supply-max-voltage = <3000000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@1 {
+ reg = <1>;
+ qcom,supply-name = "vddio";
+ qcom,supply-min-voltage = <1800000>;
+ qcom,supply-max-voltage = <1800000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@2 {
+ reg = <2>;
+ qcom,supply-name = "bklt";
+ qcom,supply-min-voltage = <0>;
+ qcom,supply-max-voltage = <0>;
+ qcom,supply-enable-load = <0>;
+ qcom,supply-disable-load = <0>;
+ };
+ };
+};
+
+&dsi_auo_390p_cmd {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
+ qcom,panel-supply-entries = <&dsi_pm660_panel_pwr_supply>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8909-mdss.dtsi b/arch/arm64/boot/dts/qcom/msm8909-mdss.dtsi
index 0d824e0..67c6d06 100644
--- a/arch/arm64/boot/dts/qcom/msm8909-mdss.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909-mdss.dtsi
@@ -38,9 +38,11 @@
smmu_mdp_unsec: qcom,smmu_mdp_unsec_cb {
compatible = "qcom,smmu_mdp_unsec";
+ iommus = <&apps_iommu 0xC00 0>; /* For NS ctx bank */
};
smmu_mdp_sec: qcom,smmu_mdp_sec_cb {
compatible = "qcom,smmu_mdp_sec";
+ iommus = <&apps_iommu 0xC01 0>; /* For SEC Ctx Bank */
};
};
@@ -51,8 +53,8 @@
#size-cells = <1>;
qcom,mdss-fb-map-prim = <&mdss_fb0>;
gdsc-supply = <&gdsc_mdss>;
- vdda-supply = <&pm8909_l2>;
- vddio-supply = <&pm8909_l6>;
+ vdda-supply = <&pm660_l2>;
+ vddio-supply = <&pm660_l6>;
/* Bus Scale Settings */
qcom,msm-bus,name = "mdss_dsi";
@@ -133,8 +135,8 @@
interrupts = <0 80 0>;
qcom,mdss-mdp = <&mdss_mdp>;
- vdd-supply = <&pm8909_l17>;
- vddio-supply = <&pm8909_l6>;
+ vdd-supply = <&pm660_l17>;
+ vddio-supply = <&pm660_l6>;
clocks = <&clock_gcc_mdss clk_gcc_mdss_byte0_clk>,
<&clock_gcc_mdss clk_gcc_mdss_pclk0_clk>,
@@ -157,4 +159,4 @@
};
};
-/* #include "msm8909-mdss-panels.dtsi" */
+#include "msm8909-mdss-panels.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/msm8909-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/msm8909-pinctrl.dtsi
index 25688ff..c86da64 100644
--- a/arch/arm64/boot/dts/qcom/msm8909-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909-pinctrl.dtsi
@@ -15,9 +15,11 @@
compatible = "qcom,msm8909-pinctrl";
reg = <0x1000000 0x300000>;
interrupts = <0 208 0>;
+ interrupts-extended = <&wakegic GIC_SPI 208 IRQ_TYPE_NONE>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
+ interrupt-parent = <&wakegpio>;
#interrupt-cells = <2>;
/* sensors */
diff --git a/arch/arm64/boot/dts/qcom/msm8909.dtsi b/arch/arm64/boot/dts/qcom/msm8909.dtsi
index eed3623..a6d42f1 100644
--- a/arch/arm64/boot/dts/qcom/msm8909.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909.dtsi
@@ -21,7 +21,7 @@
<258 0>,
<265 0>,
<275 0>;
- interrupt-parent = <&intc>;
+ interrupt-parent = <&wakegic>;
aliases {
/* smdtty devices */
@@ -251,11 +251,30 @@
intc: interrupt-controller@b000000 {
compatible = "qcom,msm-qgic2";
interrupt-controller;
+ interrupt-parent = <&intc>;
#interrupt-cells = <3>;
reg = <0x0b000000 0x1000>,
<0x0b002000 0x1000>;
};
+ wakegic: wake-gic@601d0 {
+ compatible = "qcom,mpm-gic-msm8909", "qcom,mpm-gic";
+ interrupts = <GIC_SPI 171 IRQ_TYPE_EDGE_RISING>;
+ reg = <0x601d0 0x1000>,
+ <0xb011008 0x4>; /* MSM_APCS_GCC_BASE 4K */
+ reg-names = "vmpm", "ipc";
+ interrupt-controller;
+ interrupt-parent = <&intc>;
+ #interrupt-cells = <3>;
+ };
+
+ wakegpio: wake-gpio {
+ compatible = "qcom,mpm-gpio-msm8909", "qcom,mpm-gpio";
+ interrupt-controller;
+ interrupt-parent = <&intc>;
+ #interrupt-cells = <2>;
+ };
+
restart@4ab000 {
compatible = "qcom,pshold";
reg = <0x4ab000 0x4>,
@@ -681,6 +700,16 @@
rpm-channel-type = <15>; /* SMD_APPS_RPM */
};
+ qcom,bam_dmux@4044000 {
+ compatible = "qcom,bam_dmux";
+ reg = <0x4044000 0x19000>;
+ interrupts = <0 29 1>;
+ qcom,rx-ring-size = <32>;
+ qcom,max-rx-mtu = <4096>;
+ qcom,fast-shutdown;
+ qcom,no-cpu-affinity;
+ };
+
qcom,smdtty {
compatible = "qcom,smdtty";
diff --git a/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
index d9fa6fb..8149eb2 100644
--- a/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
+++ b/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
@@ -316,3 +316,8 @@
output-high;
};
};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_auo_390p_cmd>;
+ qcom,platform-bklight-en-gpio = <&msm_gpio 52 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8909w.dtsi b/arch/arm64/boot/dts/qcom/msm8909w.dtsi
index 707b56e..c2e28d1 100644
--- a/arch/arm64/boot/dts/qcom/msm8909w.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909w.dtsi
@@ -94,3 +94,14 @@
<55 512 196800 196800>,
<55 512 393600 393600>;
};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_auo_cx_qvga_cmd>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+
+ qcom,platform-te-gpio = <&msm_gpio 24 0>;
+ qcom,platform-reset-gpio = <&msm_gpio 25 0>;
+ qcom,platform-bklight-en-gpio = <&msm_gpio 37 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pmi8937-qrd-sku5.dts b/arch/arm64/boot/dts/qcom/msm8917-pmi8937-qrd-sku5.dts
index ab86021..29ef47c 100644
--- a/arch/arm64/boot/dts/qcom/msm8917-pmi8937-qrd-sku5.dts
+++ b/arch/arm64/boot/dts/qcom/msm8917-pmi8937-qrd-sku5.dts
@@ -103,4 +103,15 @@
focaltech,resume-in-workqueue;
};
};
+
+ gen-vkeys {
+ compatible = "qcom,gen-vkeys";
+ label = "ft5x06_ts";
+ qcom,disp-maxx = <720>;
+ qcom,disp-maxy = <1280>;
+ qcom,panel-maxx = <720>;
+ qcom,panel-maxy = <1400>;
+ qcom,key-codes = <139 172 158>;
+ qcom,y-offset = <0>;
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-vidc.dtsi b/arch/arm64/boot/dts/qcom/msm8917-vidc.dtsi
new file mode 100644
index 0000000..c3a93af
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-vidc.dtsi
@@ -0,0 +1,167 @@
+/* Copyright (c) 2015-2016, 2018 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.
+ */
+
+&soc {
+ qcom,vidc@1d00000 {
+ compatible = "qcom,msm-vidc";
+ reg = <0x01d00000 0xff000>;
+ interrupts = <0 44 0>;
+ qcom,hfi-version = "3xx";
+ venus-supply = <&gdsc_venus>;
+ venus-core0-supply = <&gdsc_venus_core0>;
+ clocks = <&clock_gcc clk_gcc_venus0_vcodec0_clk>,
+ <&clock_gcc clk_gcc_venus0_core0_vcodec0_clk>,
+ <&clock_gcc clk_gcc_venus0_ahb_clk>,
+ <&clock_gcc clk_gcc_venus0_axi_clk>;
+ clock-names = "core_clk", "core0_clk", "iface_clk", "bus_clk";
+ qcom,clock-configs = <0x1 0x0 0x0 0x0>;
+ qcom,sw-power-collapse;
+ qcom,slave-side-cp;
+ qcom,dcvs-tbl =
+ <108000 108000 244800 0x00000004>, /* Encoder */
+ <108000 108000 244800 0x0c000000>; /* Decoder */
+ qcom,dcvs-limit =
+ <8160 30>, /* Encoder */
+ <8160 30>; /* Decoder */
+ qcom,hfi = "venus";
+ qcom,reg-presets = <0xe0020 0x05555556>,
+ <0xe0024 0x05555556>,
+ <0x80124 0x00000003>;
+ qcom,qdss-presets = <0x826000 0x1000>,
+ <0x827000 0x1000>,
+ <0x822000 0x1000>,
+ <0x803000 0x1000>,
+ <0x9180000 0x1000>,
+ <0x9181000 0x1000>;
+ qcom,max-hw-load = <352800>; /* 1080p@30 + 720p@30 */
+ qcom,pm-qos-latency-us = <651>;
+ qcom,firmware-name = "venus";
+ qcom,allowed-clock-rates = <360000000 329140000
+ 308570000 270000000 200000000>;
+ qcom,clock-freq-tbl {
+ qcom,profile-enc {
+ qcom,codec-mask = <0x55555555>;
+ qcom,cycles-per-mb = <2470>;
+ qcom,low-power-mode-factor = <32768>;
+ };
+ qcom,profile-dec {
+ qcom,codec-mask = <0xf3ffffff>;
+ qcom,cycles-per-mb = <788>;
+ };
+ qcom,profile-hevcdec {
+ qcom,codec-mask = <0x0c000000>;
+ qcom,cycles-per-mb = <1015>;
+ };
+ };
+ /* MMUs */
+ non_secure_cb {
+ compatible = "qcom,msm-vidc,context-bank";
+ label = "venus_ns";
+ iommus = <&apps_iommu 0x800 0x00>,
+ <&apps_iommu 0x807 0x00>,
+ <&apps_iommu 0x808 0x27>,
+ <&apps_iommu 0x811 0x20>;
+ buffer-types = <0xfff>;
+ virtual-addr-pool = <0x5dc00000 0x7f000000
+ 0xdcc00000 0x1000000>;
+ };
+
+ secure_bitstream_cb {
+ compatible = "qcom,msm-vidc,context-bank";
+ label = "venus_sec_bitstream";
+ iommus = <&apps_iommu 0x900 0x00>,
+ <&apps_iommu 0x90a 0x04>,
+ <&apps_iommu 0x909 0x22>;
+ buffer-types = <0x241>;
+ virtual-addr-pool = <0x4b000000 0x12c00000>;
+ qcom,secure-context-bank;
+ };
+
+ secure_pixel_cb {
+ compatible = "qcom,msm-vidc,context-bank";
+ label = "venus_sec_pixel";
+ iommus = <&apps_iommu 0x90c 0x20>;
+ buffer-types = <0x106>;
+ virtual-addr-pool = <0x25800000 0x25800000>;
+ qcom,secure-context-bank;
+ };
+
+ secure_non_pixel_cb {
+ compatible = "qcom,msm-vidc,context-bank";
+ label = "venus_sec_non_pixel";
+ iommus = <&apps_iommu 0x940 0x00>,
+ <&apps_iommu 0x907 0x08>,
+ <&apps_iommu 0x908 0x20>,
+ <&apps_iommu 0x90d 0x20>;
+ buffer-types = <0x480>;
+ virtual-addr-pool = <0x1000000 0x24800000>;
+ qcom,secure-context-bank;
+ };
+
+ 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 = "venus-ddr-gov";
+ qcom,bus-range-kbps = <1000 917000>;
+ };
+
+ arm9_bus_ddr {
+ compatible = "qcom,msm-vidc,bus";
+ label = "venus-arm9-ddr";
+ qcom,bus-master = <MSM_BUS_MASTER_VIDEO_P0>;
+ qcom,bus-slave = <MSM_BUS_SLAVE_EBI_CH0>;
+ qcom,bus-governor = "performance";
+ qcom,bus-range-kbps = <1 1>;
+ };
+ };
+
+ venus-ddr-gov {
+ compatible = "qcom,msm-vidc,governor,table";
+ name = "venus-ddr-gov";
+ status = "ok";
+ qcom,bus-freq-table {
+ qcom,profile-enc {
+ qcom,codec-mask = <0x55555555>;
+ qcom,load-busfreq-tbl =
+ <244800 841000>, /* 1080p30E */
+ <216000 740000>, /* 720p60E */
+ <194400 680000>, /* FWVGA120E */
+ <144000 496000>, /* VGA120E */
+ <108000 370000>, /* 720p30E */
+ <97200 340000>, /* FWVGA60E */
+ <48600 170000>, /* FWVGA30E */
+ <72000 248000>, /* VGA60E */
+ <36000 124000>, /* VGA30E */
+ <18000 70000>, /* QVGA60E */
+ <9000 35000>, /* QVGA30E */
+ <0 0>;
+ };
+ qcom,profile-dec {
+ qcom,codec-mask = <0xffffffff>;
+ qcom,load-busfreq-tbl =
+ <244800 605000>, /* 1080p30D */
+ <216000 540000>, /* 720p60D */
+ <194400 484000>, /* FWVGA120D */
+ <144000 360000>, /* VGA120D */
+ <108000 270000>, /* 720p30D */
+ <97200 242000>, /* FWVGA60D */
+ <48600 121000>, /* FWVGA30D */
+ <72000 180000>, /* VGA60D */
+ <36000 90000>, /* VGA30D */
+ <18000 45000>, /* HVGA30D */
+ <0 0>;
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917.dtsi b/arch/arm64/boot/dts/qcom/msm8917.dtsi
index 78410b3..dbdd9d9 100644
--- a/arch/arm64/boot/dts/qcom/msm8917.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8917.dtsi
@@ -166,6 +166,7 @@
#include "msm8917-mdss-pll.dtsi"
#include "msm-arm-smmu-8917.dtsi"
#include "msm8917-gpu.dtsi"
+#include "msm8917-vidc.dtsi"
&soc {
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm429.dtsi b/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm429.dtsi
index 5ae93333..53d415a 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm429.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm429.dtsi
@@ -23,6 +23,10 @@
/delete-node/ cti@61b9000;
/delete-node/ cti@61ba000;
/delete-node/ cti@61bb000;
+ /delete-node/ jtagmm@619c000;
+ /delete-node/ jtagmm@619d000;
+ /delete-node/ jtagmm@619e000;
+ /delete-node/ jtagmm@619f000;
qcom,spm@b1d2000 {
qcom,cpu-vctl-list = <&CPU0 &CPU1 &CPU2 &CPU3>;
diff --git a/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm439.dtsi b/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm439.dtsi
index 716ef73..ad6511c 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm439.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm439.dtsi
@@ -177,4 +177,5 @@
&mdss_dsi {
vdda-supply = <&pm8953_l2>;
+ vddio-supply = <&pm8953_l6>;
};
diff --git a/arch/arm64/boot/dts/qcom/msm8937-mdss-panels.dtsi b/arch/arm64/boot/dts/qcom/msm8937-mdss-panels.dtsi
index 436b8a7..ef4f4b0 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-mdss-panels.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937-mdss-panels.dtsi
@@ -23,6 +23,8 @@
#include "dsi-adv7533-720p.dtsi"
#include "dsi-panel-hx8399c-fhd-plus-video.dtsi"
#include "dsi-panel-hx8399c-hd-plus-video.dtsi"
+#include "dsi-panel-nt35695b-truly-fhd-video.dtsi"
+#include "dsi-panel-nt35695b-truly-fhd-cmd.dtsi"
&soc {
dsi_panel_pwr_supply: dsi_panel_pwr_supply {
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/msm8937-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/msm8937-pinctrl.dtsi
index 87dd30e..86e41a3 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937-pinctrl.dtsi
@@ -141,39 +141,67 @@
};
};
- pmx_rd_nfc_int {
- /*qcom,pins = <&gp 17>;*/
- pins = "gpio17";
- qcom,pin-func = <0>;
- qcom,num-grp-pins = <1>;
- label = "pmx_nfc_int";
+ nfc {
+ nfc_int_active: nfc_int_active {
+ /* active state */
+ mux {
+ /* GPIO 17 NFC Read Interrupt */
+ pins = "gpio17";
+ function = "gpio";
+ };
- nfc_int_active: active {
- drive-strength = <6>;
- bias-pull-up;
+ config {
+ pins = "gpio17";
+ drive-strength = <2>; /* 2 MA */
+ bias-pull-up;
+ };
};
- nfc_int_suspend: suspend {
- drive-strength = <6>;
- bias-pull-up;
- };
- };
+ nfc_int_suspend: nfc_int_suspend {
+ /* sleep state */
+ mux {
+ /* GPIO 17 NFC Read Interrupt */
+ pins = "gpio17";
+ function = "gpio";
+ };
- pmx_nfc_reset {
- /*qcom,pins = <&gp 16>;*/
- pins = "gpio16";
- qcom,pin-func = <0>;
- qcom,num-grp-pins = <1>;
- label = "pmx_nfc_disable";
-
- nfc_disable_active: active {
- drive-strength = <6>;
- bias-pull-up;
+ config {
+ pins = "gpio17";
+ drive-strength = <2>; /* 2 MA */
+ bias-pull-up;
+ };
};
- nfc_disable_suspend: suspend {
- drive-strength = <6>;
- bias-disable;
+ nfc_disable_active: nfc_disable_active {
+ /* active state */
+ mux {
+ /* 16: NFC ENABLE 130: FW DNLD */
+ /* 93: ESE Enable */
+ pins = "gpio16", "gpio130", "gpio93";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio16", "gpio130", "gpio93";
+ drive-strength = <2>; /* 2 MA */
+ bias-pull-up;
+ };
+ };
+
+ nfc_disable_suspend: nfc_disable_suspend {
+ /* sleep state */
+ mux {
+ /* 16: NFC ENABLE 130: FW DNLD */
+ /* 93: ESE Enable */
+ pins = "gpio16", "gpio130", "gpio93";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio16", "gpio130", "gpio93";
+ drive-strength = <2>; /* 2 MA */
+ bias-disable;
+ };
};
};
diff --git a/arch/arm64/boot/dts/qcom/msm8937-vidc.dtsi b/arch/arm64/boot/dts/qcom/msm8937-vidc.dtsi
index c4d5b90..ce6692d 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-vidc.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937-vidc.dtsi
@@ -78,7 +78,9 @@
secure_bitstream_cb {
compatible = "qcom,msm-vidc,context-bank";
label = "venus_sec_bitstream";
- iommus = <&apps_iommu 0x90c 0x20>;
+ iommus = <&apps_iommu 0x900 0x00>,
+ <&apps_iommu 0x90a 0x04>,
+ <&apps_iommu 0x909 0x22>;
buffer-types = <0x241>;
virtual-addr-pool = <0x4b000000 0x12c00000>;
qcom,secure-context-bank;
@@ -87,10 +89,7 @@
secure_pixel_cb {
compatible = "qcom,msm-vidc,context-bank";
label = "venus_sec_pixel";
- iommus = <&apps_iommu 0x940 0x00>,
- <&apps_iommu 0x907 0x08>,
- <&apps_iommu 0x908 0x20>,
- <&apps_iommu 0x90d 0x20>;
+ iommus = <&apps_iommu 0x90c 0x20>;
buffer-types = <0x106>;
virtual-addr-pool = <0x25800000 0x25800000>;
qcom,secure-context-bank;
@@ -99,9 +98,10 @@
secure_non_pixel_cb {
compatible = "qcom,msm-vidc,context-bank";
label = "venus_sec_non_pixel";
- iommus = <&apps_iommu 0x900 0x00>,
- <&apps_iommu 0x90a 0x04>,
- <&apps_iommu 0x909 0x22>;
+ iommus = <&apps_iommu 0x940 0x00>,
+ <&apps_iommu 0x907 0x08>,
+ <&apps_iommu 0x908 0x20>,
+ <&apps_iommu 0x90d 0x20>;
buffer-types = <0x480>;
virtual-addr-pool = <0x1000000 0x24800000>;
qcom,secure-context-bank;
diff --git a/arch/arm64/boot/dts/qcom/msm8937.dtsi b/arch/arm64/boot/dts/qcom/msm8937.dtsi
index 7b9f74b..9ef154a 100644
--- a/arch/arm64/boot/dts/qcom/msm8937.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937.dtsi
@@ -208,7 +208,7 @@
reg = <0x601d0 0x1000>,
<0xb011008 0x4>; /* MSM_APCS_GCC_BASE 4K */
reg-names = "vmpm", "ipc";
- qcom,num-mpm-irqs = <96>;
+ qcom,num-mpm-irqs = <64>;
interrupt-controller;
interrupt-parent = <&intc>;
#interrupt-cells = <3>;
@@ -325,16 +325,6 @@
compatible = "qcom,mem-dump";
memory-region = <&dump_mem>;
- rpmh_dump {
- qcom,dump-size = <0x2000000>;
- qcom,dump-id = <0xec>;
- };
-
- fcm_dump {
- qcom,dump-size = <0x8400>;
- qcom,dump-id = <0xee>;
- };
-
rpm_sw_dump {
qcom,dump-size = <0x28000>;
qcom,dump-id = <0xea>;
@@ -1111,6 +1101,94 @@
};
};
+ jtag_mm0: jtagmm@61bc000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61bc000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU0>;
+
+ clocks = <&clock_gcc clk_qdss_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm1: jtagmm@61bd000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61bd000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU1>;
+
+ clocks = <&clock_gcc clk_qdss_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm2: jtagmm@61be000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61be000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU2>;
+
+ clocks = <&clock_gcc clk_qdss_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm3: jtagmm@61bf000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61bf000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU3>;
+
+ clocks = <&clock_gcc clk_qdss_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm4: jtagmm@619c000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x619c000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU4>;
+
+ clocks = <&clock_gcc clk_qdss_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm5: jtagmm@619d000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x619d000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU5>;
+
+ clocks = <&clock_gcc clk_qdss_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm6: jtagmm@619e000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x619e000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU6>;
+
+ clocks = <&clock_gcc clk_qdss_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm7: jtagmm@619f000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x619f000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU7>;
+
+ clocks = <&clock_gcc clk_qdss_clk>;
+ clock-names = "core_clk";
+ };
+
qcom,smdtty {
compatible = "qcom,smdtty";
diff --git a/arch/arm64/boot/dts/qcom/msm8953-audio.dtsi b/arch/arm64/boot/dts/qcom/msm8953-audio.dtsi
index fc10b4d..f862a07 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-audio.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-audio.dtsi
@@ -82,6 +82,8 @@
qcom,pri-mi2s-gpios = <&cdc_pri_mi2s_gpios>;
qcom,quin-mi2s-gpios = <&cdc_quin_mi2s_gpios>;
+ qcom,afe-rxtx-lb;
+
asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
<&loopback>, <&compress>, <&hostless>,
<&afe>, <&lsm>, <&routing>, <&pcm_noirq>;
@@ -102,7 +104,8 @@
<&afe_pcm_rx>, <&afe_pcm_tx>,
<&afe_proxy_rx>, <&afe_proxy_tx>,
<&incall_record_rx>, <&incall_record_tx>,
- <&incall_music_rx>, <&incall_music_2_rx>;
+ <&incall_music_rx>, <&incall_music_2_rx>,
+ <&afe_loopback_tx>;
asoc-cpu-names = "msm-dai-q6-auxpcm.1",
"msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
@@ -117,7 +120,8 @@
"msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
"msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
"msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
- "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770";
+ "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770",
+ "msm-dai-q6-dev.24577";
asoc-codec = <&stub_codec>, <&msm_digital_codec>,
<&pmic_analog_codec>;
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
index 7d73a69..cc3c392 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
@@ -466,6 +466,87 @@
};
};
+ cdc_dmic0_clk_act: cdc_dmic0_clk_act_default {
+ mux {
+ pins = "gpio89";
+ function = "dmic0_clk";
+ };
+
+ config {
+ pins = "gpio89";
+ drive-strength = <8>;
+ };
+ };
+
+ cdc_dmic0_clk_sus: cdc_dmic0_clk_sus_sleep {
+ mux {
+ pins = "gpio89";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio89";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ cdc_dmic0_data_act: cdc_dmic0_data_act_default {
+ mux {
+ pins = "gpio90";
+ function = "dmic0_data";
+ };
+
+ config {
+ pins = "gpio90";
+ drive-strength = <8>;
+ };
+ };
+
+ cdc_dmic0_data_sus: cdc_dmic0_data_sus_sleep {
+ mux {
+ pins = "gpio90";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio90";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ sec-tlmm-lines {
+ sec_tlmm_lines_act: sec_tlmm_lines_act {
+ mux {
+ pins = "gpio135", "gpio136",
+ "gpio137", "gpio138";
+ function = "sec_mi2s";
+ };
+
+ config {
+ pins = "gpio135", "gpio136",
+ "gpio137", "gpio138";
+ drive-strength = <8>;
+ };
+ };
+
+ sec_tlmm_lines_sus: sec_tlmm_lines_sus {
+ mux {
+ pins = "gpio135", "gpio136",
+ "gpio137", "gpio138";
+ function = "sec_mi2s";
+ };
+
+ config {
+ pins = "gpio135", "gpio136",
+ "gpio137", "gpio138";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+ };
+
hsuart_active: default {
mux {
pins = "gpio12", "gpio13", "gpio14", "gpio15";
@@ -656,6 +737,32 @@
};
};
+ i2c_1 {
+ i2c_1_active: i2c_1_active {
+ mux {
+ pins = "gpio2", "gpio3";
+ function = "blsp_i2c1";
+ };
+ config {
+ pins = "gpio2", "gpio3";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+
+ i2c_1_sleep: i2c_1_sleep {
+ mux {
+ pins = "gpio2", "gpio3";
+ function = "blsp_i2c1";
+ };
+ config {
+ pins = "gpio2", "gpio3";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+ };
+
i2c_2 {
i2c_2_active: i2c_2_active {
/* active state */
diff --git a/arch/arm64/boot/dts/qcom/msm8953.dtsi b/arch/arm64/boot/dts/qcom/msm8953.dtsi
index b33a7db..cd0afec 100644
--- a/arch/arm64/boot/dts/qcom/msm8953.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953.dtsi
@@ -161,6 +161,7 @@
smd36 = &smdtty_loopback;
sdhc1 = &sdhc_1; /* SDC1 eMMC slot */
sdhc2 = &sdhc_2; /* SDC2 for SD card */
+ i2c1 = &i2c_1;
i2c2 = &i2c_2;
i2c3 = &i2c_3;
i2c5 = &i2c_5;
@@ -349,11 +350,6 @@
compatible = "qcom,mem-dump";
memory-region = <&dump_mem>;
- fcm_dump {
- qcom,dump-size = <0x8400>;
- qcom,dump-id = <0xee>;
- };
-
rpm_sw_dump {
qcom,dump-size = <0x28000>;
qcom,dump-id = <0xea>;
@@ -644,6 +640,30 @@
qcom,master-id = <86>;
status = "disabled";
};
+ i2c_1: i2c@78b5000 { /* BLSP1 QUP1 */
+ compatible = "qcom,i2c-msm-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ reg = <0x78b5000 0x600>;
+ interrupt-names = "qup_irq";
+ interrupts = <0 95 0>;
+ qcom,master-id = <86>;
+ qcom,clk-freq-out = <100000>;
+ qcom,clk-freq-in = <19200000>;
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>,
+ <&clock_gcc clk_gcc_blsp1_qup1_i2c_apps_clk>;
+ pinctrl-names = "i2c_active", "i2c_sleep";
+ pinctrl-0 = <&i2c_1_active>;
+ pinctrl-1 = <&i2c_1_sleep>;
+ qcom,noise-rjct-scl = <0>;
+ qcom,noise-rjct-sda = <0>;
+ dmas = <&dma_blsp1 4 64 0x20000020 0x20>,
+ <&dma_blsp1 5 32 0x20000020 0x20>;
+ dma-names = "tx", "rx";
+ status = "disabled";
+ };
i2c_2: i2c@78b6000 { /* BLSP1 QUP2 */
compatible = "qcom,i2c-msm-v2";
@@ -1297,6 +1317,103 @@
label = "modem";
};
};
+
+ jtag_mm0: jtagmm@619c000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x619c000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU0>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm1: jtagmm@619d000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x619d000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU1>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm2: jtagmm@619e000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x619e000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU2>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm3: jtagmm@619f000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x619f000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU3>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm4: jtagmm@61bc000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61bc000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU4>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm5: jtagmm@61bd000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61bd000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU5>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm6: jtagmm@61be000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61be000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU6>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm7: jtagmm@61bf000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61bf000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU7>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk";
+ };
+
sdcc1_ice: sdcc1ice@7803000 {
compatible = "qcom,ice";
reg = <0x7803000 0x8000>;
diff --git a/arch/arm64/boot/dts/qcom/pm8916.dtsi b/arch/arm64/boot/dts/qcom/pm8916.dtsi
index 7d38151..af29080 100644
--- a/arch/arm64/boot/dts/qcom/pm8916.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm8916.dtsi
@@ -169,6 +169,121 @@
qcom,pmic-revid = <&pm8916_revid>;
};
+ pm8916_chg: qcom,charger {
+ compatible = "qcom,qpnp-linear-charger";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ qcom,vddmax-mv = <4200>;
+ qcom,vddsafe-mv = <4200>;
+ qcom,vinmin-mv = <4308>;
+ qcom,ibatsafe-ma = <1440>;
+ qcom,thermal-mitigation = <1440 720 630 0>;
+ qcom,cool-bat-decidegc = <100>;
+ qcom,warm-bat-decidegc = <450>;
+ qcom,cool-bat-mv = <4100>;
+ qcom,warm-bat-mv = <4100>;
+ qcom,ibatmax-warm-ma = <360>;
+ qcom,ibatmax-cool-ma = <360>;
+ qcom,batt-hot-percentage = <25>;
+ qcom,batt-cold-percentage = <80>;
+ qcom,tchg-mins = <232>;
+ qcom,resume-soc = <99>;
+ qcom,chg-vadc = <&pm8916_vadc>;
+ qcom,chg-adc_tm = <&pm8916_adc_tm>;
+
+ status = "disabled";
+
+ qcom,chgr@1000 {
+ reg = <0x1000 0x100>;
+ interrupts =
+ <0x0 0x10 0x7 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x10 0x6 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x10 0x5 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x10 0x0 IRQ_TYPE_EDGE_FALLING>;
+ interrupt-names = "chg-done",
+ "chg-failed",
+ "fast-chg-on",
+ "vbat-det-lo";
+ };
+
+ qcom,bat-if@1200 {
+ reg = <0x1200 0x100>;
+ interrupts = <0x0 0x12 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x12 0x0 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "bat-temp-ok",
+ "batt-pres";
+ };
+
+ qcom,usb-chgpth@1300 {
+ reg = <0x1300 0x100>;
+ interrupts =
+ <0 0x13 0x4 IRQ_TYPE_EDGE_BOTH>,
+ <0 0x13 0x2 IRQ_TYPE_EDGE_RISING>,
+ <0 0x13 0x1 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "usb-over-temp",
+ "chg-gone",
+ "usbin-valid";
+ };
+
+ qcom,chg-misc@1600 {
+ reg = <0x1600 0x100>;
+ };
+ };
+
+ pm8916_bms: qcom,vmbms {
+ compatible = "qcom,qpnp-vm-bms";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ status = "disabled";
+
+ qcom,v-cutoff-uv = <3400000>;
+ qcom,max-voltage-uv = <4200000>;
+ qcom,r-conn-mohm = <0>;
+ qcom,shutdown-soc-valid-limit = <100>;
+ qcom,low-soc-calculate-soc-threshold = <15>;
+ qcom,low-voltage-calculate-soc-ms = <1000>;
+ qcom,low-soc-calculate-soc-ms = <5000>;
+ qcom,calculate-soc-ms = <20000>;
+ qcom,volatge-soc-timeout-ms = <60000>;
+ qcom,low-voltage-threshold = <3450000>;
+ qcom,s3-ocv-tolerence-uv = <1200>;
+ qcom,s2-fifo-length = <5>;
+ qcom,low-soc-fifo-length = <2>;
+ qcom,bms-vadc = <&pm8916_vadc>;
+ qcom,bms-adc_tm = <&pm8916_adc_tm>;
+ qcom,pmic-revid = <&pm8916_revid>;
+
+ qcom,force-s3-on-suspend;
+ qcom,force-s2-in-charging;
+ qcom,report-charger-eoc;
+
+ qcom,batt-pres-status@1208 {
+ reg = <0x1208 0x1>;
+ };
+
+ qcom,qpnp-chg-pres@1008 {
+ reg = <0x1008 0x1>;
+ };
+
+ qcom,vm-bms@4000 {
+ reg = <0x4000 0x100>;
+ interrupts = <0x0 0x40 0x0 IRQ_TYPE_NONE>,
+ <0x0 0x40 0x1 IRQ_TYPE_NONE>,
+ <0x0 0x40 0x2 IRQ_TYPE_NONE>,
+ <0x0 0x40 0x3 IRQ_TYPE_NONE>,
+ <0x0 0x40 0x4 IRQ_TYPE_NONE>,
+ <0x0 0x40 0x5 IRQ_TYPE_NONE>;
+
+ interrupt-names = "leave_cv",
+ "enter_cv",
+ "good_ocv",
+ "ocv_thr",
+ "fifo_update_done",
+ "fsm_state_change";
+ };
+ };
+
pm8916_leds: qcom,leds@a100 {
compatible = "qcom,leds-qpnp";
reg = <0xa100 0x100>;
diff --git a/arch/arm64/boot/dts/qcom/pmi632.dtsi b/arch/arm64/boot/dts/qcom/pmi632.dtsi
index b8f8b68..48bda61 100644
--- a/arch/arm64/boot/dts/qcom/pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi632.dtsi
@@ -154,6 +154,30 @@
qcom,cal-val = <0>;
};
+ chan@2a {
+ label = "bat_therm_PU30";
+ reg = <0x2a>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <24>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,cal-val = <0>;
+ };
+
+ chan@6a {
+ label = "bat_therm_PU400";
+ reg = <0x6a>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <25>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,cal-val = <0>;
+ };
+
chan@4b {
label = "bat_id";
reg = <0x4b>;
diff --git a/arch/arm64/boot/dts/qcom/qcs605-lc.dtsi b/arch/arm64/boot/dts/qcom/qcs605-lc.dtsi
index 15b23de..f8dde39 100644
--- a/arch/arm64/boot/dts/qcom/qcs605-lc.dtsi
+++ b/arch/arm64/boot/dts/qcom/qcs605-lc.dtsi
@@ -369,3 +369,13 @@
&int_codec {
/delete-property/ qcom,ext-disp-audio-rx;
};
+
+&bluetooth {
+ qca,bt-vdd-core-supply = <&pm660_l9>;
+ qca,bt-vdd-pa-supply = <&pm660_l3>;
+ /delete-property/ qca,bt-vdd-ldo-supply;
+};
+
+&qupv3_se6_4uart {
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm429-cpu.dtsi b/arch/arm64/boot/dts/qcom/sdm429-cpu.dtsi
index ab874c9..6c4ea9f 100644
--- a/arch/arm64/boot/dts/qcom/sdm429-cpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm429-cpu.dtsi
@@ -129,10 +129,10 @@
CPU_COST_0: core-cost0 {
busy-cost-data = <
960000 159
- 1001600 165
1305600 207
1497600 256
1708800 327
+ 1804800 343
1958400 445
>;
idle-cost-data = <
@@ -141,11 +141,11 @@
};
CLUSTER_COST_0: cluster-cost0 {
busy-cost-data = <
- 960000 52
- 1001600 53
+ 960000 53
1305600 61
1497600 71
1708800 85
+ 1804800 88
1958400 110
>;
idle-cost-data = <
@@ -156,5 +156,15 @@
};
&soc {
- /delete-node/ cpuss_dump;
+ cpuss_dump {
+ /delete-node/ qcom,l2_dump0;
+ /delete-node/ qcom,l1_i_cache0;
+ /delete-node/ qcom,l1_i_cache1;
+ /delete-node/ qcom,l1_i_cache2;
+ /delete-node/ qcom,l1_i_cache3;
+ /delete-node/ qcom,l1_d_cache0;
+ /delete-node/ qcom,l1_d_cache1;
+ /delete-node/ qcom,l1_d_cache2;
+ /delete-node/ qcom,l1_d_cache3;
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm429.dtsi b/arch/arm64/boot/dts/qcom/sdm429.dtsi
index 42407af..f372ec4 100644
--- a/arch/arm64/boot/dts/qcom/sdm429.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm429.dtsi
@@ -28,6 +28,10 @@
/delete-node/ cti@61b9000;
/delete-node/ cti@61ba000;
/delete-node/ cti@61bb000;
+ /delete-node/ jtagmm@619c000;
+ /delete-node/ jtagmm@619d000;
+ /delete-node/ jtagmm@619e000;
+ /delete-node/ jtagmm@619f000;
qcom,spm@b1d2000 {
qcom,cpu-vctl-list = <&CPU0 &CPU1 &CPU2 &CPU3>;
@@ -39,6 +43,7 @@
};
};
+ /delete-node/ qcom,msm-cpufreq;
msm_cpufreq: qcom,msm-cpufreq {
compatible = "qcom,msm-cpufreq";
clock-names =
@@ -54,35 +59,39 @@
< 1305600 >,
< 1497600 >,
< 1708800 >,
+ < 1804800 >,
< 1958400 >;
};
+ /delete-node/ devfreq-cpufreq;
devfreq-cpufreq {
cpubw-cpufreq {
target-dev = <&cpubw>;
cpu-to-dev-map =
- < 960000 2929 >,
- < 1305600 5053 >,
- < 1497600 5712 >,
- < 1708800 7031 >,
- < 1958400 7031 >;
+ < 960000 2929 >,
+ < 1305600 5126 >,
+ < 1497600 5859 >,
+ < 1708800 6445 >,
+ < 1804800 7104 >,
+ < 1958400 7104 >;
};
cci-cpufreq {
target-dev = <&cci_cache>;
cpu-to-dev-map =
- < 960000 400000 >,
+ < 960000 400000 >,
< 1305600 400000 >,
- < 1497600 533333 >,
- < 1708800 533333 >,
- < 1958400 533333 >;
+ < 1497600 400000 >,
+ < 1708800 533000 >,
+ < 1804800 576000 >,
+ < 1958400 576000 >;
};
mincpubw-cpufreq {
target-dev = <&mincpubw>;
cpu-to-dev-map =
< 1305600 2929 >,
- < 1958400 4248 >;
+ < 1804800 5859 >;
};
};
};
@@ -108,3 +117,84 @@
/delete-node/ cpuss0-step;
};
+
+&clock_gcc {
+ compatible = "qcom,gcc-sdm429";
+ reg = <0x1800000 0x80000>,
+ <0xb016000 0x00040>;
+ reg-names = "cc_base", "apcs_c1_base";
+ vdd_dig-supply = <&pm8953_s2_level>;
+ vdd_hf_dig-supply = <&pm8953_s2_level_ao>;
+ vdd_hf_pll-supply = <&pm8953_l7_ao>;
+};
+
+&clock_debug {
+ compatible = "qcom,cc-debug-8917";
+ reg = <0x1874000 0x4>,
+ <0xb01101c 0x8>;
+ reg-names = "cc_base", "meas";
+ #clock-cells = <1>;
+};
+
+&soc {
+ /delete-node/ qcom,cpu-clock-8939@b111050;
+ clock_cpu: qcom,cpu-clock-8939@b111050 {
+ compatible = "qcom,cpu-clock-sdm429";
+
+ reg = <0xb011050 0x8>,
+ <0xb1d1050 0x8>,
+ <0x00a412c 0x8>;
+ reg-names = "apcs-c1-rcg-base",
+ "apcs-cci-rcg-base", "efuse";
+
+ qcom,num-cluster;
+ vdd-c1-supply = <&apc_vreg_corner>;
+ vdd-cci-supply = <&apc_vreg_corner>;
+
+ clocks = <&clock_gcc clk_gpll0_ao_clk_src>,
+ <&clock_gcc clk_a53ss_c1_pll>,
+ <&clock_gcc clk_gpll0_ao_clk_src>,
+ <&clock_gcc clk_gpll0_ao_clk_src>;
+ clock-names = "clk-c1-4", "clk-c1-5",
+ "clk-cci-4", "clk-cci-2";
+
+ qcom,speed0-bin-v0-c1 =
+ < 0 0>,
+ < 960000000 1>,
+ < 1305600000 1>,
+ < 1497600000 2>,
+ < 1708800000 3>,
+ < 1958400000 5>;
+
+ qcom,speed0-bin-v0-cci =
+ < 0 0>,
+ < 400000000 1>,
+ < 533333333 3>;
+
+ qcom,speed1-bin-v0-c1 =
+ < 0 0>,
+ < 960000000 1>,
+ < 1305600000 1>,
+ < 1497600000 2>,
+ < 1708800000 3>,
+ < 1804800000 5>;
+
+ qcom,speed1-bin-v0-cci =
+ < 0 0>,
+ < 400000000 1>,
+ < 533333333 3>;
+
+ #clock-cells = <1>;
+ };
+};
+
+&clock_gcc_mdss {
+ compatible = "qcom,gcc-mdss-sdm429";
+ clocks = <&mdss_dsi0_pll clk_dsi0pll_pixel_clk_src>,
+ <&mdss_dsi0_pll clk_dsi0pll_byte_clk_src>,
+ <&mdss_dsi1_pll clk_dsi1pll_pixel_clk_src>,
+ <&mdss_dsi1_pll clk_dsi1pll_byte_clk_src>;
+ clock-names = "pclk0_src", "byte0_src", "pclk1_src",
+ "byte1_src";
+ #clock-cells = <1>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi
index b106e45..08745cd 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi
@@ -17,6 +17,40 @@
status = "ok";
};
+&pm8953_gpios {
+ nfc_clk {
+ nfc_clk_default: nfc_clk_default {
+ pins = "gpio2";
+ function = "normal";
+ input-enable;
+ power-source = <1>;
+ };
+ };
+};
+
+&i2c_5 { /* BLSP2 QUP1 (NFC) */
+ status = "ok";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 17 0x00>;
+ qcom,nq-ven = <&tlmm 16 0x00>;
+ qcom,nq-firm = <&tlmm 130 0x00>;
+ qcom,nq-clkreq = <&pm8953_gpios 2 0x00>;
+ qcom,nq-esepwr = <&tlmm 93 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK2";
+ interrupts = <17 0>;
+ interrupt-names = "nfc_irq";
+ pinctrl-names = "nfc_active", "nfc_suspend";
+ pinctrl-0 = <&nfc_int_active &nfc_disable_active
+ &nfc_clk_default>;
+ pinctrl-1 = <&nfc_int_suspend &nfc_disable_suspend>;
+ clocks = <&clock_gcc clk_bb_clk2_pin>;
+ clock-names = "ref_clk";
+ };
+};
+
&sdhc_1 {
/* device core power supply */
vdd-supply = <&pm8953_l8>;
@@ -141,12 +175,15 @@
qcom,platform-reset-gpio = <&tlmm 60 0>;
lab-supply = <&lcdb_ldo_vreg>;
ibb-supply = <&lcdb_ncp_vreg>;
+};
+&mdss_dsi1 {
+ status = "disabled";
};
&dsi_hx8399c_truly_vid {
- qcom,mdss-dsi-panel-timings =
- [f3 3a 26 00 6c 6e 2c 3e 2f 03 04 00];
+ /delete-property/ qcom,mdss-dsi-panel-timings;
+ qcom,mdss-dsi-panel-timings-phy-12nm = [18 0a 10 06 03 08 06 0e];
qcom,mdss-dsi-t-clk-post = <0x02>;
qcom,mdss-dsi-t-clk-pre = <0x2d>;
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
@@ -154,17 +191,343 @@
qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
qcom,mdss-dsi-bl-pmic-bank-select = <0>;
qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-status-read-length = <4>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+ qcom,mdss-dsi-min-refresh-rate = <48>;
+ qcom,mdss-dsi-max-refresh-rate = <60>;
qcom,mdss-dsi-pan-enable-dynamic-fps;
- qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
+ qcom,mdss-dsi-pan-fps-update =
+ "dfps_immediate_porch_mode_vfp";
};
&dsi_hx8399c_hd_vid {
+ /delete-property/ qcom,mdss-dsi-panel-timings;
+ qcom,mdss-dsi-panel-timings-phy-12nm = [08 06 0a 02 00 04 02 08];
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
- qcom,mdss-dsi-pan-enable-dynamic-fps;
- qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
qcom,mdss-dsi-bl-pmic-bank-select = <0>;
qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
};
+
+&dsi_nt35695b_truly_fhd_cmd {
+ qcom,mdss-dsi-panel-width = <1080>;
+ qcom,mdss-dsi-panel-height = <1920>;
+ qcom,mdss-dsi-h-front-porch = <120>;
+ qcom,mdss-dsi-h-back-porch = <60>;
+ qcom,mdss-dsi-h-pulse-width = <12>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <2>;
+ qcom,mdss-dsi-v-front-porch = <12>;
+ qcom,mdss-dsi-v-pulse-width = <2>;
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-on-command =
+ [15 01 00 00 10 00 02 ff 20
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 00 01
+ 15 01 00 00 00 00 02 01 55
+ 15 01 00 00 00 00 02 02 45
+ 15 01 00 00 00 00 02 03 55
+ 15 01 00 00 00 00 02 05 50
+ 15 01 00 00 00 00 02 06 a8
+ 15 01 00 00 00 00 02 07 ad
+ 15 01 00 00 00 00 02 08 0c
+ 15 01 00 00 00 00 02 0b aa
+ 15 01 00 00 00 00 02 0c aa
+ 15 01 00 00 00 00 02 0e b0
+ 15 01 00 00 00 00 02 0f b3
+ 15 01 00 00 00 00 02 11 28
+ 15 01 00 00 00 00 02 12 10
+ 15 01 00 00 00 00 02 13 01
+ 15 01 00 00 00 00 02 14 4a
+ 15 01 00 00 00 00 02 15 12
+ 15 01 00 00 00 00 02 16 12
+ 15 01 00 00 00 00 02 30 01
+ 15 01 00 00 00 00 02 72 11
+ 15 01 00 00 00 00 02 58 82
+ 15 01 00 00 00 00 02 59 00
+ 15 01 00 00 00 00 02 5a 02
+ 15 01 00 00 00 00 02 5b 00
+ 15 01 00 00 00 00 02 5c 82
+ 15 01 00 00 00 00 02 5d 80
+ 15 01 00 00 00 00 02 5e 02
+ 15 01 00 00 00 00 02 5f 00
+ 15 01 00 00 00 00 02 ff 24
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 00 01
+ 15 01 00 00 00 00 02 01 0b
+ 15 01 00 00 00 00 02 02 0c
+ 15 01 00 00 00 00 02 03 89
+ 15 01 00 00 00 00 02 04 8a
+ 15 01 00 00 00 00 02 05 0f
+ 15 01 00 00 00 00 02 06 10
+ 15 01 00 00 00 00 02 07 10
+ 15 01 00 00 00 00 02 08 1c
+ 15 01 00 00 00 00 02 09 00
+ 15 01 00 00 00 00 02 0a 00
+ 15 01 00 00 00 00 02 0b 00
+ 15 01 00 00 00 00 02 0c 00
+ 15 01 00 00 00 00 02 0d 13
+ 15 01 00 00 00 00 02 0e 15
+ 15 01 00 00 00 00 02 0f 17
+ 15 01 00 00 00 00 02 10 01
+ 15 01 00 00 00 00 02 11 0b
+ 15 01 00 00 00 00 02 12 0c
+ 15 01 00 00 00 00 02 13 89
+ 15 01 00 00 00 00 02 14 8a
+ 15 01 00 00 00 00 02 15 0f
+ 15 01 00 00 00 00 02 16 10
+ 15 01 00 00 00 00 02 17 10
+ 15 01 00 00 00 00 02 18 1c
+ 15 01 00 00 00 00 02 19 00
+ 15 01 00 00 00 00 02 1a 00
+ 15 01 00 00 00 00 02 1b 00
+ 15 01 00 00 00 00 02 1c 00
+ 15 01 00 00 00 00 02 1d 13
+ 15 01 00 00 00 00 02 1e 15
+ 15 01 00 00 00 00 02 1f 17
+ 15 01 00 00 00 00 02 20 00
+ 15 01 00 00 00 00 02 21 01
+ 15 01 00 00 00 00 02 22 00
+ 15 01 00 00 00 00 02 23 40
+ 15 01 00 00 00 00 02 24 40
+ 15 01 00 00 00 00 02 25 6d
+ 15 01 00 00 00 00 02 26 40
+ 15 01 00 00 00 00 02 27 40
+ 15 01 00 00 00 00 02 29 d8
+ 15 01 00 00 00 00 02 2a 2a
+ 15 01 00 00 00 00 02 4b 03
+ 15 01 00 00 00 00 02 4c 11
+ 15 01 00 00 00 00 02 4d 10
+ 15 01 00 00 00 00 02 4e 01
+ 15 01 00 00 00 00 02 4f 01
+ 15 01 00 00 00 00 02 50 10
+ 15 01 00 00 00 00 02 51 00
+ 15 01 00 00 00 00 02 52 80
+ 15 01 00 00 00 00 02 53 00
+ 15 01 00 00 00 00 02 54 07
+ 15 01 00 00 00 00 02 55 25
+ 15 01 00 00 00 00 02 56 00
+ 15 01 00 00 00 00 02 58 07
+ 15 01 00 00 00 00 02 5b 43
+ 15 01 00 00 00 00 02 5c 00
+ 15 01 00 00 00 00 02 5f 73
+ 15 01 00 00 00 00 02 60 73
+ 15 01 00 00 00 00 02 63 22
+ 15 01 00 00 00 00 02 64 00
+ 15 01 00 00 00 00 02 67 08
+ 15 01 00 00 00 00 02 68 04
+ 15 01 00 00 00 00 02 7a 80
+ 15 01 00 00 00 00 02 7b 91
+ 15 01 00 00 00 00 02 7c d8
+ 15 01 00 00 00 00 02 7d 60
+ 15 01 00 00 00 00 02 93 06
+ 15 01 00 00 00 00 02 94 06
+ 15 01 00 00 00 00 02 8a 00
+ 15 01 00 00 00 00 02 9b 0f
+ 15 01 00 00 00 00 02 b3 c0
+ 15 01 00 00 00 00 02 b4 00
+ 15 01 00 00 00 00 02 b5 00
+ 15 01 00 00 00 00 02 b6 21
+ 15 01 00 00 00 00 02 b7 22
+ 15 01 00 00 00 00 02 b8 07
+ 15 01 00 00 00 00 02 b9 07
+ 15 01 00 00 00 00 02 ba 22
+ 15 01 00 00 00 00 02 bd 20
+ 15 01 00 00 00 00 02 be 07
+ 15 01 00 00 00 00 02 bf 07
+ 15 01 00 00 00 00 02 c1 6d
+ 15 01 00 00 00 00 02 c4 24
+ 15 01 00 00 00 00 02 e3 00
+ 15 01 00 00 00 00 02 ec 00
+ 15 01 00 00 00 00 02 ff 10
+ 15 01 00 00 00 00 02 bb 10
+ 15 01 00 00 00 00 02 35 00
+ 05 01 00 00 78 00 02 11 00
+ 05 01 00 00 78 00 02 29 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00 14
+ 00 02 28 00 05 01 00 00 78 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-timings-phy-12nm = [17 0a 0f 06 03 08 06 0e];
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
+ qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
+ qcom,mdss-dsi-bl-pmic-bank-select = <0>;
+ qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+ qcom,ulps-enabled;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+ /delete-node/ qcom,mdss-dsi-display-timings;
+};
+
+&dsi_nt35695b_truly_fhd_video {
+ qcom,mdss-dsi-panel-width = <1080>;
+ qcom,mdss-dsi-panel-height = <1920>;
+ qcom,mdss-dsi-h-front-porch = <120>;
+ qcom,mdss-dsi-h-back-porch = <60>;
+ qcom,mdss-dsi-h-pulse-width = <12>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-v-back-porch = <2>;
+ qcom,mdss-dsi-v-front-porch = <12>;
+ qcom,mdss-dsi-v-pulse-width = <2>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-on-command =
+ [15 01 00 00 10 00 02 ff 20
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 00 01
+ 15 01 00 00 00 00 02 01 55
+ 15 01 00 00 00 00 02 02 45
+ 15 01 00 00 00 00 02 03 55
+ 15 01 00 00 00 00 02 05 50
+ 15 01 00 00 00 00 02 06 a8
+ 15 01 00 00 00 00 02 07 ad
+ 15 01 00 00 00 00 02 08 0c
+ 15 01 00 00 00 00 02 0b aa
+ 15 01 00 00 00 00 02 0c aa
+ 15 01 00 00 00 00 02 0e b0
+ 15 01 00 00 00 00 02 0f b3
+ 15 01 00 00 00 00 02 11 28
+ 15 01 00 00 00 00 02 12 10
+ 15 01 00 00 00 00 02 13 01
+ 15 01 00 00 00 00 02 14 4a
+ 15 01 00 00 00 00 02 15 12
+ 15 01 00 00 00 00 02 16 12
+ 15 01 00 00 00 00 02 30 01
+ 15 01 00 00 00 00 02 72 11
+ 15 01 00 00 00 00 02 58 82
+ 15 01 00 00 00 00 02 59 00
+ 15 01 00 00 00 00 02 5a 02
+ 15 01 00 00 00 00 02 5b 00
+ 15 01 00 00 00 00 02 5c 82
+ 15 01 00 00 00 00 02 5d 80
+ 15 01 00 00 00 00 02 5e 02
+ 15 01 00 00 00 00 02 5f 00
+ 15 01 00 00 00 00 02 ff 24
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 00 01
+ 15 01 00 00 00 00 02 01 0b
+ 15 01 00 00 00 00 02 02 0c
+ 15 01 00 00 00 00 02 03 89
+ 15 01 00 00 00 00 02 04 8a
+ 15 01 00 00 00 00 02 05 0f
+ 15 01 00 00 00 00 02 06 10
+ 15 01 00 00 00 00 02 07 10
+ 15 01 00 00 00 00 02 08 1c
+ 15 01 00 00 00 00 02 09 00
+ 15 01 00 00 00 00 02 0a 00
+ 15 01 00 00 00 00 02 0b 00
+ 15 01 00 00 00 00 02 0c 00
+ 15 01 00 00 00 00 02 0d 13
+ 15 01 00 00 00 00 02 0e 15
+ 15 01 00 00 00 00 02 0f 17
+ 15 01 00 00 00 00 02 10 01
+ 15 01 00 00 00 00 02 11 0b
+ 15 01 00 00 00 00 02 12 0c
+ 15 01 00 00 00 00 02 13 89
+ 15 01 00 00 00 00 02 14 8a
+ 15 01 00 00 00 00 02 15 0f
+ 15 01 00 00 00 00 02 16 10
+ 15 01 00 00 00 00 02 17 10
+ 15 01 00 00 00 00 02 18 1c
+ 15 01 00 00 00 00 02 19 00
+ 15 01 00 00 00 00 02 1a 00
+ 15 01 00 00 00 00 02 1b 00
+ 15 01 00 00 00 00 02 1c 00
+ 15 01 00 00 00 00 02 1d 13
+ 15 01 00 00 00 00 02 1e 15
+ 15 01 00 00 00 00 02 1f 17
+ 15 01 00 00 00 00 02 20 00
+ 15 01 00 00 00 00 02 21 01
+ 15 01 00 00 00 00 02 22 00
+ 15 01 00 00 00 00 02 23 40
+ 15 01 00 00 00 00 02 24 40
+ 15 01 00 00 00 00 02 25 6d
+ 15 01 00 00 00 00 02 26 40
+ 15 01 00 00 00 00 02 27 40
+ 15 01 00 00 00 00 02 29 d8
+ 15 01 00 00 00 00 02 2a 2a
+ 15 01 00 00 00 00 02 4b 03
+ 15 01 00 00 00 00 02 4c 11
+ 15 01 00 00 00 00 02 4d 10
+ 15 01 00 00 00 00 02 4e 01
+ 15 01 00 00 00 00 02 4f 01
+ 15 01 00 00 00 00 02 50 10
+ 15 01 00 00 00 00 02 51 00
+ 15 01 00 00 00 00 02 52 80
+ 15 01 00 00 00 00 02 53 00
+ 15 01 00 00 00 00 02 54 07
+ 15 01 00 00 00 00 02 55 25
+ 15 01 00 00 00 00 02 56 00
+ 15 01 00 00 00 00 02 58 07
+ 15 01 00 00 00 00 02 5b 43
+ 15 01 00 00 00 00 02 5c 00
+ 15 01 00 00 00 00 02 5f 73
+ 15 01 00 00 00 00 02 60 73
+ 15 01 00 00 00 00 02 63 22
+ 15 01 00 00 00 00 02 64 00
+ 15 01 00 00 00 00 02 67 08
+ 15 01 00 00 00 00 02 68 04
+ 15 01 00 00 00 00 02 7a 80
+ 15 01 00 00 00 00 02 7b 91
+ 15 01 00 00 00 00 02 7c d8
+ 15 01 00 00 00 00 02 7d 60
+ 15 01 00 00 00 00 02 93 06
+ 15 01 00 00 00 00 02 94 06
+ 15 01 00 00 00 00 02 8a 00
+ 15 01 00 00 00 00 02 9b 0f
+ 15 01 00 00 00 00 02 b3 c0
+ 15 01 00 00 00 00 02 b4 00
+ 15 01 00 00 00 00 02 b5 00
+ 15 01 00 00 00 00 02 b6 21
+ 15 01 00 00 00 00 02 b7 22
+ 15 01 00 00 00 00 02 b8 07
+ 15 01 00 00 00 00 02 b9 07
+ 15 01 00 00 00 00 02 ba 22
+ 15 01 00 00 00 00 02 bd 20
+ 15 01 00 00 00 00 02 be 07
+ 15 01 00 00 00 00 02 bf 07
+ 15 01 00 00 00 00 02 c1 6d
+ 15 01 00 00 00 00 02 c4 24
+ 15 01 00 00 00 00 02 e3 00
+ 15 01 00 00 00 00 02 ec 00
+ 15 01 00 00 00 00 02 ff 10
+ 15 01 00 00 00 00 02 bb 03
+ 05 01 00 00 78 00 02 11 00
+ 05 01 00 00 78 00 02 29 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00
+ 14 00 02 28 00 05 01 00 00 78 00
+ 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-timings-phy-12nm = [17 0a 0f 06 03 08 06 0e];
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
+ qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
+ qcom,mdss-dsi-bl-pmic-bank-select = <0>;
+ qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+ /delete-node/ qcom,mdss-dsi-display-timings;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm439-mtp.dtsi
index 01d7144..29e0d72 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-mtp.dtsi
@@ -17,6 +17,40 @@
status = "ok";
};
+&pm8953_gpios {
+ nfc_clk {
+ nfc_clk_default: nfc_clk_default {
+ pins = "gpio2";
+ function = "normal";
+ input-enable;
+ power-source = <1>;
+ };
+ };
+};
+
+&i2c_5 { /* BLSP2 QUP1 (NFC) */
+ status = "ok";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 17 0x00>;
+ qcom,nq-ven = <&tlmm 16 0x00>;
+ qcom,nq-firm = <&tlmm 130 0x00>;
+ qcom,nq-clkreq = <&pm8953_gpios 2 0x00>;
+ qcom,nq-esepwr = <&tlmm 93 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK2";
+ interrupts = <17 0>;
+ interrupt-names = "nfc_irq";
+ pinctrl-names = "nfc_active", "nfc_suspend";
+ pinctrl-0 = <&nfc_int_active &nfc_disable_active
+ &nfc_clk_default>;
+ pinctrl-1 = <&nfc_int_suspend &nfc_disable_suspend>;
+ clocks = <&clock_gcc clk_bb_clk2_pin>;
+ clock-names = "ref_clk";
+ };
+};
+
&sdhc_1 {
/* device core power supply */
vdd-supply = <&pm8953_l8>;
@@ -141,12 +175,15 @@
qcom,platform-reset-gpio = <&tlmm 60 0>;
lab-supply = <&lcdb_ldo_vreg>;
ibb-supply = <&lcdb_ncp_vreg>;
+};
+&mdss_dsi1 {
+ status = "disabled";
};
&dsi_hx8399c_truly_vid {
- qcom,mdss-dsi-panel-timings =
- [f3 3a 26 00 6c 6e 2c 3e 2f 03 04 00];
+ /delete-property/ qcom,mdss-dsi-panel-timings;
+ qcom,mdss-dsi-panel-timings-phy-12nm = [18 0a 10 06 03 08 06 0e];
qcom,mdss-dsi-t-clk-post = <0x02>;
qcom,mdss-dsi-t-clk-pre = <0x2d>;
qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
@@ -154,14 +191,25 @@
qcom,mdss-dsi-bl-pmic-bank-select = <0>;
qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-status-read-length = <4>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+ qcom,mdss-dsi-min-refresh-rate = <48>;
+ qcom,mdss-dsi-max-refresh-rate = <60>;
qcom,mdss-dsi-pan-enable-dynamic-fps;
- qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
+ qcom,mdss-dsi-pan-fps-update =
+ "dfps_immediate_porch_mode_vfp";
};
&dsi_hx8399c_hd_vid {
+ /delete-property/ qcom,mdss-dsi-panel-timings;
+ qcom,mdss-dsi-panel-timings-phy-12nm = [08 06 0a 02 00 04 02 08];
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
- qcom,mdss-dsi-pan-enable-dynamic-fps;
- qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
qcom,mdss-dsi-bl-pmic-bank-select = <0>;
diff --git a/arch/arm64/boot/dts/qcom/sdm439-pm8953.dtsi b/arch/arm64/boot/dts/qcom/sdm439-pm8953.dtsi
index 452f0c7..d34c34a 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-pm8953.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-pm8953.dtsi
@@ -126,7 +126,22 @@
&mdss_dsi {
vdda-supply = <&pm8953_l23>;
- vddio-supply = <&pm8953_l6>;
+ vddio-supply = <&pm8953_l5>;
+
+ qcom,ctrl-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,ctrl-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdda";
+ qcom,supply-min-voltage = <800000>;
+ qcom,supply-max-voltage = <800000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ qcom,supply-post-on-sleep = <20>;
+ };
+ };
};
&usb_otg {
diff --git a/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi
index e54e46e..18714ce 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi
@@ -61,6 +61,40 @@
status = "okay";
};
+&pm8953_gpios {
+ nfc_clk {
+ nfc_clk_default: nfc_clk_default {
+ pins = "gpio2";
+ function = "normal";
+ input-enable;
+ power-source = <1>;
+ };
+ };
+};
+
+&i2c_5 { /* BLSP2 QUP1 (NFC) */
+ status = "ok";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 17 0x00>;
+ qcom,nq-ven = <&tlmm 16 0x00>;
+ qcom,nq-firm = <&tlmm 130 0x00>;
+ qcom,nq-clkreq = <&pm8953_gpios 2 0x00>;
+ qcom,nq-esepwr = <&tlmm 93 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK2";
+ interrupts = <17 0>;
+ interrupt-names = "nfc_irq";
+ pinctrl-names = "nfc_active", "nfc_suspend";
+ pinctrl-0 = <&nfc_int_active &nfc_disable_active
+ &nfc_clk_default>;
+ pinctrl-1 = <&nfc_int_suspend &nfc_disable_suspend>;
+ clocks = <&clock_gcc clk_bb_clk2_pin>;
+ clock-names = "ref_clk";
+ };
+};
+
&sdhc_2 {
/* device core power supply */
vdd-supply = <&pm8953_l11>;
@@ -245,10 +279,21 @@
qcom,mdss-dsi-bl-pmic-bank-select = <0>;
qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
- qcom,mdss-dsi-panel-timings =
- [7e 48 3c 00 64 59 3b 4a 5e 03 04 00];
- qcom,mdss-dsi-t-clk-post = <0x1e>;
- qcom,mdss-dsi-t-clk-pre = <0x32>;
+ /delete-property/ qcom,mdss-dsi-panel-timings;
+ qcom,mdss-dsi-panel-timings-phy-12nm = [18 0a 10 06 03 08 06 0e];
+ qcom,mdss-dsi-t-clk-post = <0x02>;
+ qcom,mdss-dsi-t-clk-pre = <0x2d>;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-status-read-length = <4>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+ qcom,mdss-dsi-min-refresh-rate = <48>;
+ qcom,mdss-dsi-max-refresh-rate = <60>;
qcom,mdss-dsi-pan-enable-dynamic-fps;
- qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
+ qcom,mdss-dsi-pan-fps-update =
+ "dfps_immediate_porch_mode_vfp";
};
diff --git a/arch/arm64/boot/dts/qcom/sdm439.dtsi b/arch/arm64/boot/dts/qcom/sdm439.dtsi
index 812c0ad..8093c61 100644
--- a/arch/arm64/boot/dts/qcom/sdm439.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439.dtsi
@@ -23,15 +23,19 @@
&soc {
qcom,csid@1b30000 {
+ qcom,csi-vdd-voltage = <800000>;
qcom,mipi-csi-vdd-supply = <&pm8953_l23>;
};
qcom,csid@1b30400 {
+ qcom,csi-vdd-voltage = <800000>;
qcom,mipi-csi-vdd-supply = <&pm8953_l23>;
};
qcom,csid@1b30800 {
+ qcom,csi-vdd-voltage = <800000>;
qcom,mipi-csi-vdd-supply = <&pm8953_l23>;
};
+ /delete-node/ qcom,msm-cpufreq;
msm_cpufreq: qcom,msm-cpufreq {
compatible = "qcom,msm-cpufreq";
clock-names =
@@ -48,30 +52,87 @@
< 1305600 >,
< 1497600 >,
< 1708800 >,
+ < 1804800 >,
< 1958400 >;
qcom,cpufreq-table-4 =
< 768000 >,
- < 1001600 >,
+ < 998400 >,
< 1171200 >,
< 1305600 >,
< 1459200 >;
};
+ /delete-node/ qcom,cpubw;
+ cpubw: qcom,cpubw {
+ compatible = "qcom,devbw";
+ governor = "cpufreq";
+ qcom,src-dst-ports = <1 512>;
+ qcom,active-only;
+ qcom,bw-tbl =
+ < 769 /* 100.8 MHz */ >,
+ < 1611 /* 211.2 MHz */ >, /*Low SVS*/
+ < 2124 /* 278.4 MHz */ >,
+ < 2929 /* 384 MHz */ >, /* SVS */
+ < 3221 /* 422.4 MHz */ >,
+ < 4248 /* 556.8 MHz */ >,
+ < 5126 /* 662.4 MHz */ >, /* SVS+ */
+ < 5859 /* 748.8 MHz */ >, /* NOM */
+ < 6152 /* 806.4 MHz */ >, /* NOM+ */
+ < 6445 /* 844.8 MHz */ >,
+ < 7104 /* 931.2 MHz */ >; /* TURBO */
+ };
+
+ /delete-node/ qcom,mincpubw;
+ mincpubw: qcom,mincpubw {
+ compatible = "qcom,devbw";
+ governor = "cpufreq";
+ qcom,src-dst-ports = <1 512>;
+ qcom,active-only;
+ qcom,bw-tbl =
+ < 769 /* 100.8 MHz */ >,
+ < 1611 /* 211.2 MHz */ >, /*Low SVS*/
+ < 2124 /* 278.4 MHz */ >,
+ < 2929 /* 384 MHz */ >, /* SVS */
+ < 3221 /* 422.4 MHz */ >,
+ < 4248 /* 556.8 MHz */ >,
+ < 5126 /* 662.4 MHz */ >, /* SVS+ */
+ < 5859 /* 748.8 MHz */ >, /* NOM */
+ < 6152 /* 806.4 MHz */ >, /* NOM+ */
+ < 6445 /* 844.8 MHz */ >,
+ < 7104 /* 931.2 MHz */ >; /* TURBO */
+ };
+
+ /delete-node/ qcom,cci;
+ cci_cache: qcom,cci {
+ compatible = "devfreq-simple-dev";
+ clock-names = "devfreq_clk";
+ clocks = <&clock_cpu clk_cci_clk>;
+ governor = "cpufreq";
+ freq-tbl-khz =
+ < 400000 >,
+ < 400000 >,
+ < 400000 >,
+ < 533000 >,
+ < 576000 >;
+ };
+
+ /delete-node/ devfreq-cpufreq;
devfreq-cpufreq {
cpubw-cpufreq {
target-dev = <&cpubw>;
cpu-to-dev-map-0 =
- < 1305600 2929 >,
- < 1497600 5053 >,
- < 1708800 5712 >,
- < 1958400 7031 >;
+ < 1305600 5126 >,
+ < 1497600 5859 >,
+ < 1708800 6445 >,
+ < 1804800 7104 >,
+ < 1958400 7104 >;
cpu-to-dev-map-4 =
< 768000 2929 >,
- < 1001600 4101 >,
- < 1171200 5053 >,
+ < 998400 5126 >,
+ < 1171200 5859 >,
< 1305600 6152 >,
- < 1459200 7031 >;
+ < 1459200 7104 >;
};
@@ -80,80 +141,84 @@
cpu-to-dev-map-0 =
< 1305600 400000 >,
< 1497600 400000 >,
- < 1708800 533333 >,
- < 1958400 533333 >;
+ < 1708800 533000 >,
+ < 1804800 576000 >,
+ < 1958400 576000 >;
cpu-to-dev-map-4 =
< 768000 400000 >,
- < 1001600 400000 >,
- < 1171200 533333 >,
- < 1305600 533333 >,
- < 1459200 533333 >;
+ < 998400 400000 >,
+ < 1171200 400000 >,
+ < 1305600 533000 >,
+ < 1459200 576000 >;
};
mincpubw-cpufreq {
target-dev = <&mincpubw>;
cpu-to-dev-map-0 =
< 1305600 2929 >,
- < 1958400 4248 >;
+ < 1804800 5859 >;
cpu-to-dev-map-4 =
< 1171200 2929 >,
- < 1459200 4248 >;
+ < 1459200 5859 >;
};
};
};
-&energy_costs {
- compatible = "sched-energy";
+/{
+ /delete-node/ energy-costs;
+ energy_costs: energy-costs {
+ compatible = "sched-energy";
- CPU_COST_0: core-cost0 {
- busy-cost-data = <
- 800000 137
- 1001600 165
- 1305600 207
- 1497600 256
- 1708800 327
- 1958400 445
- >;
- idle-cost-data = <
- 100 80 60 40
- >;
- };
- CPU_COST_1: core-cost1 {
- busy-cost-data = <
- 768000 43
- 1001600 56
- 1171200 71
- 1305600 89
- 1459200 120
- >;
- idle-cost-data = <
- 40 20 10 8
- >;
- };
- CLUSTER_COST_0: cluster-cost0 {
+ CPU_COST_0: core-cost0 {
busy-cost-data = <
- 800000 49
- 1001600 53
- 1305600 61
- 1497600 71
- 1708800 85
- 1958400 110
- >;
- idle-cost-data = <
- 4 3 2 1
- >;
- };
- CLUSTER_COST_1: cluster-cost1 {
+ 800000 137
+ 1305600 207
+ 1497600 256
+ 1708800 327
+ 1804800 343
+ 1958400 445
+ >;
+ idle-cost-data = <
+ 100 80 60 40
+ >;
+ };
+ CPU_COST_1: core-cost1 {
busy-cost-data = <
- 768000 8
- 1001600 10
- 1171200 13
- 1305600 15
- 1459200 20
- >;
- idle-cost-data = <
- 4 3 2 1
- >;
+ 768000 43
+ 998400 56
+ 1171200 71
+ 1305600 89
+ 1459200 120
+ >;
+ idle-cost-data = <
+ 40 20 10 8
+ >;
+ };
+ CLUSTER_COST_0: cluster-cost0 {
+ busy-cost-data = <
+ 800000 49
+ 1305600 61
+ 1497600 71
+ 1708800 85
+ 1804800 88
+ 1958400 110
+ >;
+ idle-cost-data = <
+ 4 3 2 1
+ >;
+ };
+ CLUSTER_COST_1: cluster-cost1 {
+ busy-cost-data = <
+ 768000 8
+ 998400 10
+ 1171200 13
+ 1305600 15
+ 1459200 20
+ >;
+ idle-cost-data = <
+ 4 3 2 1
+ >;
+ };
};
};
@@ -197,3 +262,128 @@
memory-region = <&secure_mem>;
};
};
+
+&clock_cpu {
+ compatible = "qcom,cpu-clock-sdm439";
+ vdd-c0-supply = <&apc_vreg_corner>;
+ vdd-c1-supply = <&apc_vreg_corner>;
+ vdd-cci-supply = <&apc_vreg_corner>;
+ qcom,speed0-bin-v0-c0 =
+ < 0 0>,
+ < 768000000 1>,
+ < 998400000 1>,
+ < 1171200000 2>,
+ < 1305600000 3>,
+ < 1459200000 5>;
+
+ qcom,speed0-bin-v0-c1 =
+ < 0 0>,
+ < 1305600000 1>,
+ < 1497600000 2>,
+ < 1708800000 3>,
+ < 1958400000 5>;
+
+ qcom,speed0-bin-v0-cci =
+ < 0 0>,
+ < 400000000 1>,
+ < 533333333 3>;
+
+ qcom,speed1-bin-v0-c0 =
+ < 0 0>,
+ < 768000000 1>,
+ < 998400000 1>,
+ < 1171200000 2>,
+ < 1305600000 3>,
+ < 1459200000 5>;
+
+ qcom,speed1-bin-v0-c1 =
+ < 0 0>,
+ < 1305600000 1>,
+ < 1497600000 2>,
+ < 1708800000 3>,
+ < 1804800000 5>;
+
+ qcom,speed1-bin-v0-cci =
+ < 0 0>,
+ < 400000000 1>,
+ < 533333333 3>;
+};
+
+&clock_gcc {
+ compatible = "qcom,gcc-sdm439";
+ reg = <0x1800000 0x80000>,
+ <0xb016000 0x00040>,
+ <0xb116000 0x00040>,
+ <0x00a6018 0x00004>;
+ reg-names = "cc_base", "apcs_c1_base",
+ "apcs_c0_base", "efuse";
+ vdd_dig-supply = <&pm8953_s2_level>;
+ vdd_sr2_dig-supply = <&pm8953_s2_level_ao>;
+ vdd_sr2_pll-supply = <&pm8953_l7_ao>;
+ vdd_hf_dig-supply = <&pm8953_s2_level_ao>;
+ vdd_hf_pll-supply = <&pm8953_l7_ao>;
+};
+
+&clock_gcc_mdss {
+ compatible = "qcom,gcc-mdss-sdm439";
+ clocks = <&mdss_dsi0_pll clk_dsi0pll_pixel_clk_src>,
+ <&mdss_dsi0_pll clk_dsi0pll_byte_clk_src>,
+ <&mdss_dsi1_pll clk_dsi1pll_pixel_clk_src>,
+ <&mdss_dsi1_pll clk_dsi1pll_byte_clk_src>;
+ clock-names = "pclk0_src", "byte0_src", "pclk1_src",
+ "byte1_src";
+ #clock-cells = <1>;
+};
+
+&mdss_dsi0_pll {
+ compatible = "qcom,mdss_dsi_pll_sdm439";
+ reg = <0x001a94400 0x400>,
+ <0x0184d074 0x8>;
+ reg-names = "pll_base", "gdsc_base";
+ /delete-property/ qcom,dsi-pll-ssc-en;
+ /delete-property/ qcom,dsi-pll-ssc-mode;
+ /delete-property/ qcom,ssc-frequency-hz;
+ /delete-property/ qcom,ssc-ppm;
+};
+
+&mdss_dsi1_pll {
+ compatible = "qcom,mdss_dsi_pll_sdm439";
+ reg = <0x001a96400 0x400>,
+ <0x0184d074 0x8>;
+ reg-names = "pll_base", "gdsc_base";
+ /delete-property/ qcom,dsi-pll-ssc-en;
+ /delete-property/ qcom,dsi-pll-ssc-mode;
+ /delete-property/ qcom,ssc-frequency-hz;
+ /delete-property/ qcom,ssc-ppm;
+};
+
+&mdss_dsi {
+ ranges = <0x1a94000 0x1a94000 0x300
+ 0x1a94400 0x1a94400 0x400
+ 0x193e000 0x193e000 0x30
+ 0x1a96000 0x1a96000 0x300
+ 0x1a96400 0x1a96400 0x400
+ 0x193e000 0x193e000 0x30>;
+};
+
+&mdss_dsi0 {
+ reg = <0x1a94000 0x300>,
+ <0x1a94400 0x400>,
+ <0x193e000 0x30>;
+ reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
+ /delete-property/ qcom,platform-strength-ctrl;
+ /delete-property/ qcom,platform-bist-ctrl;
+ /delete-property/ qcom,platform-regulator-settings;
+ /delete-property/ qcom,platform-lane-config;
+};
+
+&mdss_dsi1 {
+ reg = <0x1a96000 0x300>,
+ <0x1a96400 0x400>,
+ <0x193e000 0x30>;
+ reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
+ /delete-property/ qcom,platform-strength-ctrl;
+ /delete-property/ qcom,platform-bist-ctrl;
+ /delete-property/ qcom,platform-regulator-settings;
+ /delete-property/ qcom,platform-lane-config;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm632-coresight.dtsi b/arch/arm64/boot/dts/qcom/sdm632-coresight.dtsi
new file mode 100644
index 0000000..62eeb65
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm632-coresight.dtsi
@@ -0,0 +1,154 @@
+/* Copyright (c) 2018, 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.
+ */
+&soc {
+
+ /delete-node/ etm@61bc000;
+ /delete-node/ etm@61bd000;
+ /delete-node/ etm@61be000;
+ /delete-node/ etm@61bf000;
+ /delete-node/ cti@61b8000;
+ /delete-node/ cti@61b9000;
+ /delete-node/ cti@61ba000;
+ /delete-node/ cti@61bb000;
+
+ etm4: etm@61b3000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb959>;
+
+ reg = <0x61b3000 0x1000>;
+ cpu = <&CPU4>;
+ coresight-name = "coresight-etm4";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ port {
+ etm4_out_funnel_apss0: endpoint {
+ remote-endpoint = <&funnel_apss0_in_etm4>;
+ };
+ };
+ };
+
+ etm5: etm@61b7000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb959>;
+
+ reg = <0x61b7000 0x1000>;
+ cpu = <&CPU5>;
+ coresight-name = "coresight-etm5";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ port {
+ etm5_out_funnel_apss0: endpoint {
+ remote-endpoint = <&funnel_apss0_in_etm5>;
+ };
+ };
+ };
+
+ etm6: etm@61bb000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb959>;
+
+ reg = <0x61bb000 0x1000>;
+ cpu = <&CPU6>;
+ coresight-name = "coresight-etm6";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ port {
+ etm6_out_funnel_apss0: endpoint {
+ remote-endpoint = <&funnel_apss0_in_etm6>;
+ };
+ };
+ };
+
+ etm7: etm@61bf000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb959>;
+
+ reg = <0x61bf000 0x1000>;
+ coresight-name = "coresight-etm7";
+ cpu = <&CPU7>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ port {
+ etm7_out_funnel_apss0: endpoint {
+ remote-endpoint = <&funnel_apss0_in_etm7>;
+ };
+ };
+ };
+
+ cti_cpu4: cti@61b1000{
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x61b1000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti-cpu4";
+ cpu = <&CPU4>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti_cpu5: cti@61b5000{
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x61b5000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti-cpu5";
+ cpu = <&CPU5>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti_cpu6: cti@61b9000{
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x61b9000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti-cpu6";
+ cpu = <&CPU6>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti_cpu7: cti@61bd000{
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x61bd000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti-cpu7";
+ cpu = <&CPU7>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm632.dtsi b/arch/arm64/boot/dts/qcom/sdm632.dtsi
index 62689f5..69cb36f 100644
--- a/arch/arm64/boot/dts/qcom/sdm632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm632.dtsi
@@ -983,3 +983,4 @@
/delete-node/ case-therm-step;
};
+#include "sdm632-coresight.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sdm670-thermal.dtsi b/arch/arm64/boot/dts/qcom/sdm670-thermal.dtsi
index 8707af2..a448732 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-thermal.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-thermal.dtsi
@@ -471,7 +471,9 @@
};
gpu_vdd_cdev {
trip = <&aoss0_trip>;
- cooling-device = <&msm_gpu 0 0>;
+ /* Vote for SVS_L1 for GPU */
+ cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-3)
+ (THERMAL_MAX_LIMIT-3)>;
};
cx_vdd_cdev {
trip = <&aoss0_trip>;
@@ -521,7 +523,9 @@
};
gpu_vdd_cdev {
trip = <&aoss1_trip>;
- cooling-device = <&msm_gpu 0 0>;
+ /* Vote for SVS_L1 for GPU */
+ cooling-device = <&msm_gpu (THERMAL_MAX_LIMIT-3)
+ (THERMAL_MAX_LIMIT-3)>;
};
cx_vdd_cdev {
trip = <&aoss1_trip>;
diff --git a/arch/arm64/boot/dts/renesas/r8a7796.dtsi b/arch/arm64/boot/dts/renesas/r8a7796.dtsi
index 9217da9..53d03cb 100644
--- a/arch/arm64/boot/dts/renesas/r8a7796.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a7796.dtsi
@@ -36,9 +36,8 @@
enable-method = "psci";
};
- L2_CA57: cache-controller@0 {
+ L2_CA57: cache-controller-0 {
compatible = "cache";
- reg = <0>;
power-domains = <&sysc R8A7796_PD_CA57_SCU>;
cache-unified;
cache-level = <2>;
diff --git a/arch/arm64/configs/msm8937-perf_defconfig b/arch/arm64/configs/msm8937-perf_defconfig
index df62bd1..78f09fc 100644
--- a/arch/arm64/configs/msm8937-perf_defconfig
+++ b/arch/arm64/configs/msm8937-perf_defconfig
@@ -7,6 +7,9 @@
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
CONFIG_SCHED_WALT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
CONFIG_RCU_NOCB_CPU=y
@@ -242,6 +245,7 @@
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_HDCP_QSEECOM=y
CONFIG_QSEECOM=y
+CONFIG_UID_SYS_STATS=y
CONFIG_MEMORY_STATE_TIME=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
@@ -541,6 +545,7 @@
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL=y
CONFIG_MSM_PIL_SSR_GENERIC=y
CONFIG_MSM_PIL_MSS_QDSP6V5=y
@@ -549,6 +554,7 @@
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
CONFIG_MSM_PM=y
+CONFIG_QCOM_DCC=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
diff --git a/arch/arm64/configs/msm8937_defconfig b/arch/arm64/configs/msm8937_defconfig
index 9d35d7d..b4a8e29 100644
--- a/arch/arm64/configs/msm8937_defconfig
+++ b/arch/arm64/configs/msm8937_defconfig
@@ -563,6 +563,7 @@
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL=y
CONFIG_MSM_PIL_SSR_GENERIC=y
CONFIG_MSM_PIL_MSS_QDSP6V5=y
diff --git a/arch/arm64/configs/msm8953-perf_defconfig b/arch/arm64/configs/msm8953-perf_defconfig
index 46b34b3..a532aec 100644
--- a/arch/arm64/configs/msm8953-perf_defconfig
+++ b/arch/arm64/configs/msm8953-perf_defconfig
@@ -147,6 +147,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -225,6 +226,8 @@
CONFIG_BT=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
+CONFIG_CFG80211_CERTIFICATION_ONUS=y
+CONFIG_CFG80211_REG_CELLULAR_HINTS=y
CONFIG_CFG80211_INTERNAL_REGDB=y
# CONFIG_CFG80211_CRDA_SUPPORT is not set
CONFIG_RFKILL=y
diff --git a/arch/arm64/configs/msm8953_defconfig b/arch/arm64/configs/msm8953_defconfig
index dc58fc3..8b24dd0 100644
--- a/arch/arm64/configs/msm8953_defconfig
+++ b/arch/arm64/configs/msm8953_defconfig
@@ -151,6 +151,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -231,6 +232,8 @@
CONFIG_BT=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
+CONFIG_CFG80211_CERTIFICATION_ONUS=y
+CONFIG_CFG80211_REG_CELLULAR_HINTS=y
CONFIG_CFG80211_INTERNAL_REGDB=y
# CONFIG_CFG80211_CRDA_SUPPORT is not set
CONFIG_RFKILL=y
diff --git a/arch/arm64/configs/sdm670-perf_defconfig b/arch/arm64/configs/sdm670-perf_defconfig
index f82678e..93f7621 100644
--- a/arch/arm64/configs/sdm670-perf_defconfig
+++ b/arch/arm64/configs/sdm670-perf_defconfig
@@ -146,6 +146,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -229,6 +230,8 @@
CONFIG_BT=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
+CONFIG_CFG80211_CERTIFICATION_ONUS=y
+CONFIG_CFG80211_REG_CELLULAR_HINTS=y
CONFIG_CFG80211_INTERNAL_REGDB=y
CONFIG_RFKILL=y
CONFIG_NFC_NQ=y
diff --git a/arch/arm64/configs/sdm670_defconfig b/arch/arm64/configs/sdm670_defconfig
index ee3418b..3e8d865 100644
--- a/arch/arm64/configs/sdm670_defconfig
+++ b/arch/arm64/configs/sdm670_defconfig
@@ -151,6 +151,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -237,6 +238,8 @@
CONFIG_BT=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
+CONFIG_CFG80211_CERTIFICATION_ONUS=y
+CONFIG_CFG80211_REG_CELLULAR_HINTS=y
CONFIG_CFG80211_INTERNAL_REGDB=y
# CONFIG_CFG80211_CRDA_SUPPORT is not set
CONFIG_RFKILL=y
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index f5dc2a8..55f45ae 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -145,6 +145,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -228,6 +229,8 @@
CONFIG_BT=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
+CONFIG_CFG80211_CERTIFICATION_ONUS=y
+CONFIG_CFG80211_REG_CELLULAR_HINTS=y
CONFIG_CFG80211_INTERNAL_REGDB=y
CONFIG_RFKILL=y
CONFIG_NFC_NQ=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 8dc560d..b890dee 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -148,6 +148,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -233,6 +234,8 @@
CONFIG_BT=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
+CONFIG_CFG80211_CERTIFICATION_ONUS=y
+CONFIG_CFG80211_REG_CELLULAR_HINTS=y
CONFIG_CFG80211_INTERNAL_REGDB=y
# CONFIG_CFG80211_CRDA_SUPPORT is not set
CONFIG_RFKILL=y
diff --git a/arch/arm64/crypto/Kconfig b/arch/arm64/crypto/Kconfig
index 2cf32e9..2b10984 100644
--- a/arch/arm64/crypto/Kconfig
+++ b/arch/arm64/crypto/Kconfig
@@ -53,4 +53,11 @@
tristate "CRC32 and CRC32C using optional ARMv8 instructions"
depends on ARM64
select CRYPTO_HASH
+
+config CRYPTO_SPECK_NEON
+ tristate "NEON accelerated Speck cipher algorithms"
+ depends on KERNEL_MODE_NEON
+ select CRYPTO_BLKCIPHER
+ select CRYPTO_GF128MUL
+ select CRYPTO_SPECK
endif
diff --git a/arch/arm64/crypto/Makefile b/arch/arm64/crypto/Makefile
index abb79b3..9908765 100644
--- a/arch/arm64/crypto/Makefile
+++ b/arch/arm64/crypto/Makefile
@@ -18,7 +18,8 @@
ghash-ce-y := ghash-ce-glue.o ghash-ce-core.o
obj-$(CONFIG_CRYPTO_AES_ARM64_CE) += aes-ce-cipher.o
-CFLAGS_aes-ce-cipher.o += -march=armv8-a+crypto
+aes-ce-cipher-y := aes-ce-cipher-glue.o aes-ce-cipher-core.o
+CFLAGS_aes-ce-cipher-core.o += -march=armv8-a+crypto -Wa,-march=armv8-a+crypto $(DISABLE_LTO)
obj-$(CONFIG_CRYPTO_AES_ARM64_CE_CCM) += aes-ce-ccm.o
aes-ce-ccm-y := aes-ce-ccm-glue.o aes-ce-ccm-core.o
@@ -29,6 +30,9 @@
obj-$(CONFIG_CRYPTO_AES_ARM64_NEON_BLK) += aes-neon-blk.o
aes-neon-blk-y := aes-glue-neon.o aes-neon.o
+obj-$(CONFIG_CRYPTO_SPECK_NEON) += speck-neon.o
+speck-neon-y := speck-neon-core.o speck-neon-glue.o
+
AFLAGS_aes-ce.o := -DINTERLEAVE=4
AFLAGS_aes-neon.o := -DINTERLEAVE=4
diff --git a/arch/arm64/crypto/aes-ce-cipher.c b/arch/arm64/crypto/aes-ce-cipher-core.c
similarity index 77%
rename from arch/arm64/crypto/aes-ce-cipher.c
rename to arch/arm64/crypto/aes-ce-cipher-core.c
index 50d9fe1..9f41917 100644
--- a/arch/arm64/crypto/aes-ce-cipher.c
+++ b/arch/arm64/crypto/aes-ce-cipher-core.c
@@ -1,5 +1,5 @@
/*
- * aes-ce-cipher.c - core AES cipher using ARMv8 Crypto Extensions
+ * aes-ce-cipher-core.c - core AES cipher using ARMv8 Crypto Extensions
*
* Copyright (C) 2013 - 2014 Linaro Ltd <ard.biesheuvel@linaro.org>
*
@@ -10,16 +10,10 @@
#include <asm/neon.h>
#include <crypto/aes.h>
-#include <linux/cpufeature.h>
#include <linux/crypto.h>
-#include <linux/module.h>
#include "aes-ce-setkey.h"
-MODULE_DESCRIPTION("Synchronous AES cipher using ARMv8 Crypto Extensions");
-MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
-MODULE_LICENSE("GPL v2");
-
struct aes_block {
u8 b[AES_BLOCK_SIZE];
};
@@ -36,7 +30,7 @@
return 6 + ctx->key_length / 4;
}
-static void aes_cipher_encrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
+void aes_cipher_encrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
{
struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm);
struct aes_block *out = (struct aes_block *)dst;
@@ -81,7 +75,7 @@
kernel_neon_end();
}
-static void aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
+void aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
{
struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm);
struct aes_block *out = (struct aes_block *)dst;
@@ -223,48 +217,3 @@
return 0;
}
EXPORT_SYMBOL(ce_aes_expandkey);
-
-int ce_aes_setkey(struct crypto_tfm *tfm, const u8 *in_key,
- unsigned int key_len)
-{
- struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm);
- int ret;
-
- ret = ce_aes_expandkey(ctx, in_key, key_len);
- if (!ret)
- return 0;
-
- tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
- return -EINVAL;
-}
-EXPORT_SYMBOL(ce_aes_setkey);
-
-static struct crypto_alg aes_alg = {
- .cra_name = "aes",
- .cra_driver_name = "aes-ce",
- .cra_priority = 250,
- .cra_flags = CRYPTO_ALG_TYPE_CIPHER,
- .cra_blocksize = AES_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct crypto_aes_ctx),
- .cra_module = THIS_MODULE,
- .cra_cipher = {
- .cia_min_keysize = AES_MIN_KEY_SIZE,
- .cia_max_keysize = AES_MAX_KEY_SIZE,
- .cia_setkey = ce_aes_setkey,
- .cia_encrypt = aes_cipher_encrypt,
- .cia_decrypt = aes_cipher_decrypt
- }
-};
-
-static int __init aes_mod_init(void)
-{
- return crypto_register_alg(&aes_alg);
-}
-
-static void __exit aes_mod_exit(void)
-{
- crypto_unregister_alg(&aes_alg);
-}
-
-module_cpu_feature_match(AES, aes_mod_init);
-module_exit(aes_mod_exit);
diff --git a/arch/arm64/crypto/aes-ce-cipher-glue.c b/arch/arm64/crypto/aes-ce-cipher-glue.c
new file mode 100644
index 0000000..442949e
--- /dev/null
+++ b/arch/arm64/crypto/aes-ce-cipher-glue.c
@@ -0,0 +1,83 @@
+/*
+ * aes-ce-cipher.c - core AES cipher using ARMv8 Crypto Extensions
+ *
+ * Copyright (C) 2013 - 2014 Linaro Ltd <ard.biesheuvel@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 <crypto/aes.h>
+#include <linux/cpufeature.h>
+#include <linux/crypto.h>
+#include <linux/module.h>
+
+#include "aes-ce-setkey.h"
+
+MODULE_DESCRIPTION("Synchronous AES cipher using ARMv8 Crypto Extensions");
+MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
+MODULE_LICENSE("GPL v2");
+
+extern void aes_cipher_encrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[]);
+extern void aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[]);
+
+#ifdef CONFIG_CFI_CLANG
+static inline void __cfi_aes_cipher_encrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
+{
+ aes_cipher_encrypt(tfm, dst, src);
+}
+
+static inline void __cfi_aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
+{
+ aes_cipher_decrypt(tfm, dst, src);
+}
+
+#define aes_cipher_encrypt __cfi_aes_cipher_encrypt
+#define aes_cipher_decrypt __cfi_aes_cipher_decrypt
+#endif
+
+int ce_aes_setkey(struct crypto_tfm *tfm, const u8 *in_key,
+ unsigned int key_len)
+{
+ struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+ int ret;
+
+ ret = ce_aes_expandkey(ctx, in_key, key_len);
+ if (!ret)
+ return 0;
+
+ tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ return -EINVAL;
+}
+EXPORT_SYMBOL(ce_aes_setkey);
+
+static struct crypto_alg aes_alg = {
+ .cra_name = "aes",
+ .cra_driver_name = "aes-ce",
+ .cra_priority = 250,
+ .cra_flags = CRYPTO_ALG_TYPE_CIPHER,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct crypto_aes_ctx),
+ .cra_module = THIS_MODULE,
+ .cra_cipher = {
+ .cia_min_keysize = AES_MIN_KEY_SIZE,
+ .cia_max_keysize = AES_MAX_KEY_SIZE,
+ .cia_setkey = ce_aes_setkey,
+ .cia_encrypt = aes_cipher_encrypt,
+ .cia_decrypt = aes_cipher_decrypt
+ }
+};
+
+static int __init aes_mod_init(void)
+{
+ return crypto_register_alg(&aes_alg);
+}
+
+static void __exit aes_mod_exit(void)
+{
+ crypto_unregister_alg(&aes_alg);
+}
+
+module_cpu_feature_match(AES, aes_mod_init);
+module_exit(aes_mod_exit);
diff --git a/arch/arm64/crypto/sha1-ce-glue.c b/arch/arm64/crypto/sha1-ce-glue.c
index ea319c0..f08ecf4 100644
--- a/arch/arm64/crypto/sha1-ce-glue.c
+++ b/arch/arm64/crypto/sha1-ce-glue.c
@@ -28,6 +28,14 @@
asmlinkage void sha1_ce_transform(struct sha1_ce_state *sst, u8 const *src,
int blocks);
+#ifdef CONFIG_CFI_CLANG
+static inline void __cfi_sha1_ce_transform(struct sha1_state *sst,
+ u8 const *src, int blocks)
+{
+ sha1_ce_transform((struct sha1_ce_state *)sst, src, blocks);
+}
+#define sha1_ce_transform __cfi_sha1_ce_transform
+#endif
const u32 sha1_ce_offsetof_count = offsetof(struct sha1_ce_state, sst.count);
const u32 sha1_ce_offsetof_finalize = offsetof(struct sha1_ce_state, finalize);
diff --git a/arch/arm64/crypto/sha2-ce-glue.c b/arch/arm64/crypto/sha2-ce-glue.c
index 0ed9486..c243221 100644
--- a/arch/arm64/crypto/sha2-ce-glue.c
+++ b/arch/arm64/crypto/sha2-ce-glue.c
@@ -28,6 +28,14 @@
asmlinkage void sha2_ce_transform(struct sha256_ce_state *sst, u8 const *src,
int blocks);
+#ifdef CONFIG_CFI_CLANG
+static inline void __cfi_sha2_ce_transform(struct sha256_state *sst,
+ u8 const *src, int blocks)
+{
+ sha2_ce_transform((struct sha256_ce_state *)sst, src, blocks);
+}
+#define sha2_ce_transform __cfi_sha2_ce_transform
+#endif
const u32 sha256_ce_offsetof_count = offsetof(struct sha256_ce_state,
sst.count);
diff --git a/arch/arm64/crypto/speck-neon-core.S b/arch/arm64/crypto/speck-neon-core.S
new file mode 100644
index 0000000..b144634
--- /dev/null
+++ b/arch/arm64/crypto/speck-neon-core.S
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ARM64 NEON-accelerated implementation of Speck128-XTS and Speck64-XTS
+ *
+ * Copyright (c) 2018 Google, Inc
+ *
+ * Author: Eric Biggers <ebiggers@google.com>
+ */
+
+#include <linux/linkage.h>
+
+ .text
+
+ // arguments
+ ROUND_KEYS .req x0 // const {u64,u32} *round_keys
+ NROUNDS .req w1 // int nrounds
+ NROUNDS_X .req x1
+ DST .req x2 // void *dst
+ SRC .req x3 // const void *src
+ NBYTES .req w4 // unsigned int nbytes
+ TWEAK .req x5 // void *tweak
+
+ // registers which hold the data being encrypted/decrypted
+ // (underscores avoid a naming collision with ARM64 registers x0-x3)
+ X_0 .req v0
+ Y_0 .req v1
+ X_1 .req v2
+ Y_1 .req v3
+ X_2 .req v4
+ Y_2 .req v5
+ X_3 .req v6
+ Y_3 .req v7
+
+ // the round key, duplicated in all lanes
+ ROUND_KEY .req v8
+
+ // index vector for tbl-based 8-bit rotates
+ ROTATE_TABLE .req v9
+ ROTATE_TABLE_Q .req q9
+
+ // temporary registers
+ TMP0 .req v10
+ TMP1 .req v11
+ TMP2 .req v12
+ TMP3 .req v13
+
+ // multiplication table for updating XTS tweaks
+ GFMUL_TABLE .req v14
+ GFMUL_TABLE_Q .req q14
+
+ // next XTS tweak value(s)
+ TWEAKV_NEXT .req v15
+
+ // XTS tweaks for the blocks currently being encrypted/decrypted
+ TWEAKV0 .req v16
+ TWEAKV1 .req v17
+ TWEAKV2 .req v18
+ TWEAKV3 .req v19
+ TWEAKV4 .req v20
+ TWEAKV5 .req v21
+ TWEAKV6 .req v22
+ TWEAKV7 .req v23
+
+ .align 4
+.Lror64_8_table:
+ .octa 0x080f0e0d0c0b0a090007060504030201
+.Lror32_8_table:
+ .octa 0x0c0f0e0d080b0a090407060500030201
+.Lrol64_8_table:
+ .octa 0x0e0d0c0b0a09080f0605040302010007
+.Lrol32_8_table:
+ .octa 0x0e0d0c0f0a09080b0605040702010003
+.Lgf128mul_table:
+ .octa 0x00000000000000870000000000000001
+.Lgf64mul_table:
+ .octa 0x0000000000000000000000002d361b00
+
+/*
+ * _speck_round_128bytes() - Speck encryption round on 128 bytes at a time
+ *
+ * Do one Speck encryption round on the 128 bytes (8 blocks for Speck128, 16 for
+ * Speck64) stored in X0-X3 and Y0-Y3, using the round key stored in all lanes
+ * of ROUND_KEY. 'n' is the lane size: 64 for Speck128, or 32 for Speck64.
+ * 'lanes' is the lane specifier: "2d" for Speck128 or "4s" for Speck64.
+ */
+.macro _speck_round_128bytes n, lanes
+
+ // x = ror(x, 8)
+ tbl X_0.16b, {X_0.16b}, ROTATE_TABLE.16b
+ tbl X_1.16b, {X_1.16b}, ROTATE_TABLE.16b
+ tbl X_2.16b, {X_2.16b}, ROTATE_TABLE.16b
+ tbl X_3.16b, {X_3.16b}, ROTATE_TABLE.16b
+
+ // x += y
+ add X_0.\lanes, X_0.\lanes, Y_0.\lanes
+ add X_1.\lanes, X_1.\lanes, Y_1.\lanes
+ add X_2.\lanes, X_2.\lanes, Y_2.\lanes
+ add X_3.\lanes, X_3.\lanes, Y_3.\lanes
+
+ // x ^= k
+ eor X_0.16b, X_0.16b, ROUND_KEY.16b
+ eor X_1.16b, X_1.16b, ROUND_KEY.16b
+ eor X_2.16b, X_2.16b, ROUND_KEY.16b
+ eor X_3.16b, X_3.16b, ROUND_KEY.16b
+
+ // y = rol(y, 3)
+ shl TMP0.\lanes, Y_0.\lanes, #3
+ shl TMP1.\lanes, Y_1.\lanes, #3
+ shl TMP2.\lanes, Y_2.\lanes, #3
+ shl TMP3.\lanes, Y_3.\lanes, #3
+ sri TMP0.\lanes, Y_0.\lanes, #(\n - 3)
+ sri TMP1.\lanes, Y_1.\lanes, #(\n - 3)
+ sri TMP2.\lanes, Y_2.\lanes, #(\n - 3)
+ sri TMP3.\lanes, Y_3.\lanes, #(\n - 3)
+
+ // y ^= x
+ eor Y_0.16b, TMP0.16b, X_0.16b
+ eor Y_1.16b, TMP1.16b, X_1.16b
+ eor Y_2.16b, TMP2.16b, X_2.16b
+ eor Y_3.16b, TMP3.16b, X_3.16b
+.endm
+
+/*
+ * _speck_unround_128bytes() - Speck decryption round on 128 bytes at a time
+ *
+ * This is the inverse of _speck_round_128bytes().
+ */
+.macro _speck_unround_128bytes n, lanes
+
+ // y ^= x
+ eor TMP0.16b, Y_0.16b, X_0.16b
+ eor TMP1.16b, Y_1.16b, X_1.16b
+ eor TMP2.16b, Y_2.16b, X_2.16b
+ eor TMP3.16b, Y_3.16b, X_3.16b
+
+ // y = ror(y, 3)
+ ushr Y_0.\lanes, TMP0.\lanes, #3
+ ushr Y_1.\lanes, TMP1.\lanes, #3
+ ushr Y_2.\lanes, TMP2.\lanes, #3
+ ushr Y_3.\lanes, TMP3.\lanes, #3
+ sli Y_0.\lanes, TMP0.\lanes, #(\n - 3)
+ sli Y_1.\lanes, TMP1.\lanes, #(\n - 3)
+ sli Y_2.\lanes, TMP2.\lanes, #(\n - 3)
+ sli Y_3.\lanes, TMP3.\lanes, #(\n - 3)
+
+ // x ^= k
+ eor X_0.16b, X_0.16b, ROUND_KEY.16b
+ eor X_1.16b, X_1.16b, ROUND_KEY.16b
+ eor X_2.16b, X_2.16b, ROUND_KEY.16b
+ eor X_3.16b, X_3.16b, ROUND_KEY.16b
+
+ // x -= y
+ sub X_0.\lanes, X_0.\lanes, Y_0.\lanes
+ sub X_1.\lanes, X_1.\lanes, Y_1.\lanes
+ sub X_2.\lanes, X_2.\lanes, Y_2.\lanes
+ sub X_3.\lanes, X_3.\lanes, Y_3.\lanes
+
+ // x = rol(x, 8)
+ tbl X_0.16b, {X_0.16b}, ROTATE_TABLE.16b
+ tbl X_1.16b, {X_1.16b}, ROTATE_TABLE.16b
+ tbl X_2.16b, {X_2.16b}, ROTATE_TABLE.16b
+ tbl X_3.16b, {X_3.16b}, ROTATE_TABLE.16b
+.endm
+
+.macro _next_xts_tweak next, cur, tmp, n
+.if \n == 64
+ /*
+ * Calculate the next tweak by multiplying the current one by x,
+ * modulo p(x) = x^128 + x^7 + x^2 + x + 1.
+ */
+ sshr \tmp\().2d, \cur\().2d, #63
+ and \tmp\().16b, \tmp\().16b, GFMUL_TABLE.16b
+ shl \next\().2d, \cur\().2d, #1
+ ext \tmp\().16b, \tmp\().16b, \tmp\().16b, #8
+ eor \next\().16b, \next\().16b, \tmp\().16b
+.else
+ /*
+ * Calculate the next two tweaks by multiplying the current ones by x^2,
+ * modulo p(x) = x^64 + x^4 + x^3 + x + 1.
+ */
+ ushr \tmp\().2d, \cur\().2d, #62
+ shl \next\().2d, \cur\().2d, #2
+ tbl \tmp\().16b, {GFMUL_TABLE.16b}, \tmp\().16b
+ eor \next\().16b, \next\().16b, \tmp\().16b
+.endif
+.endm
+
+/*
+ * _speck_xts_crypt() - Speck-XTS encryption/decryption
+ *
+ * Encrypt or decrypt NBYTES bytes of data from the SRC buffer to the DST buffer
+ * using Speck-XTS, specifically the variant with a block size of '2n' and round
+ * count given by NROUNDS. The expanded round keys are given in ROUND_KEYS, and
+ * the current XTS tweak value is given in TWEAK. It's assumed that NBYTES is a
+ * nonzero multiple of 128.
+ */
+.macro _speck_xts_crypt n, lanes, decrypting
+
+ /*
+ * If decrypting, modify the ROUND_KEYS parameter to point to the last
+ * round key rather than the first, since for decryption the round keys
+ * are used in reverse order.
+ */
+.if \decrypting
+ mov NROUNDS, NROUNDS /* zero the high 32 bits */
+.if \n == 64
+ add ROUND_KEYS, ROUND_KEYS, NROUNDS_X, lsl #3
+ sub ROUND_KEYS, ROUND_KEYS, #8
+.else
+ add ROUND_KEYS, ROUND_KEYS, NROUNDS_X, lsl #2
+ sub ROUND_KEYS, ROUND_KEYS, #4
+.endif
+.endif
+
+ // Load the index vector for tbl-based 8-bit rotates
+.if \decrypting
+ ldr ROTATE_TABLE_Q, .Lrol\n\()_8_table
+.else
+ ldr ROTATE_TABLE_Q, .Lror\n\()_8_table
+.endif
+
+ // One-time XTS preparation
+.if \n == 64
+ // Load first tweak
+ ld1 {TWEAKV0.16b}, [TWEAK]
+
+ // Load GF(2^128) multiplication table
+ ldr GFMUL_TABLE_Q, .Lgf128mul_table
+.else
+ // Load first tweak
+ ld1 {TWEAKV0.8b}, [TWEAK]
+
+ // Load GF(2^64) multiplication table
+ ldr GFMUL_TABLE_Q, .Lgf64mul_table
+
+ // Calculate second tweak, packing it together with the first
+ ushr TMP0.2d, TWEAKV0.2d, #63
+ shl TMP1.2d, TWEAKV0.2d, #1
+ tbl TMP0.8b, {GFMUL_TABLE.16b}, TMP0.8b
+ eor TMP0.8b, TMP0.8b, TMP1.8b
+ mov TWEAKV0.d[1], TMP0.d[0]
+.endif
+
+.Lnext_128bytes_\@:
+
+ // Calculate XTS tweaks for next 128 bytes
+ _next_xts_tweak TWEAKV1, TWEAKV0, TMP0, \n
+ _next_xts_tweak TWEAKV2, TWEAKV1, TMP0, \n
+ _next_xts_tweak TWEAKV3, TWEAKV2, TMP0, \n
+ _next_xts_tweak TWEAKV4, TWEAKV3, TMP0, \n
+ _next_xts_tweak TWEAKV5, TWEAKV4, TMP0, \n
+ _next_xts_tweak TWEAKV6, TWEAKV5, TMP0, \n
+ _next_xts_tweak TWEAKV7, TWEAKV6, TMP0, \n
+ _next_xts_tweak TWEAKV_NEXT, TWEAKV7, TMP0, \n
+
+ // Load the next source blocks into {X,Y}[0-3]
+ ld1 {X_0.16b-Y_1.16b}, [SRC], #64
+ ld1 {X_2.16b-Y_3.16b}, [SRC], #64
+
+ // XOR the source blocks with their XTS tweaks
+ eor TMP0.16b, X_0.16b, TWEAKV0.16b
+ eor Y_0.16b, Y_0.16b, TWEAKV1.16b
+ eor TMP1.16b, X_1.16b, TWEAKV2.16b
+ eor Y_1.16b, Y_1.16b, TWEAKV3.16b
+ eor TMP2.16b, X_2.16b, TWEAKV4.16b
+ eor Y_2.16b, Y_2.16b, TWEAKV5.16b
+ eor TMP3.16b, X_3.16b, TWEAKV6.16b
+ eor Y_3.16b, Y_3.16b, TWEAKV7.16b
+
+ /*
+ * De-interleave the 'x' and 'y' elements of each block, i.e. make it so
+ * that the X[0-3] registers contain only the second halves of blocks,
+ * and the Y[0-3] registers contain only the first halves of blocks.
+ * (Speck uses the order (y, x) rather than the more intuitive (x, y).)
+ */
+ uzp2 X_0.\lanes, TMP0.\lanes, Y_0.\lanes
+ uzp1 Y_0.\lanes, TMP0.\lanes, Y_0.\lanes
+ uzp2 X_1.\lanes, TMP1.\lanes, Y_1.\lanes
+ uzp1 Y_1.\lanes, TMP1.\lanes, Y_1.\lanes
+ uzp2 X_2.\lanes, TMP2.\lanes, Y_2.\lanes
+ uzp1 Y_2.\lanes, TMP2.\lanes, Y_2.\lanes
+ uzp2 X_3.\lanes, TMP3.\lanes, Y_3.\lanes
+ uzp1 Y_3.\lanes, TMP3.\lanes, Y_3.\lanes
+
+ // Do the cipher rounds
+ mov x6, ROUND_KEYS
+ mov w7, NROUNDS
+.Lnext_round_\@:
+.if \decrypting
+ ld1r {ROUND_KEY.\lanes}, [x6]
+ sub x6, x6, #( \n / 8 )
+ _speck_unround_128bytes \n, \lanes
+.else
+ ld1r {ROUND_KEY.\lanes}, [x6], #( \n / 8 )
+ _speck_round_128bytes \n, \lanes
+.endif
+ subs w7, w7, #1
+ bne .Lnext_round_\@
+
+ // Re-interleave the 'x' and 'y' elements of each block
+ zip1 TMP0.\lanes, Y_0.\lanes, X_0.\lanes
+ zip2 Y_0.\lanes, Y_0.\lanes, X_0.\lanes
+ zip1 TMP1.\lanes, Y_1.\lanes, X_1.\lanes
+ zip2 Y_1.\lanes, Y_1.\lanes, X_1.\lanes
+ zip1 TMP2.\lanes, Y_2.\lanes, X_2.\lanes
+ zip2 Y_2.\lanes, Y_2.\lanes, X_2.\lanes
+ zip1 TMP3.\lanes, Y_3.\lanes, X_3.\lanes
+ zip2 Y_3.\lanes, Y_3.\lanes, X_3.\lanes
+
+ // XOR the encrypted/decrypted blocks with the tweaks calculated earlier
+ eor X_0.16b, TMP0.16b, TWEAKV0.16b
+ eor Y_0.16b, Y_0.16b, TWEAKV1.16b
+ eor X_1.16b, TMP1.16b, TWEAKV2.16b
+ eor Y_1.16b, Y_1.16b, TWEAKV3.16b
+ eor X_2.16b, TMP2.16b, TWEAKV4.16b
+ eor Y_2.16b, Y_2.16b, TWEAKV5.16b
+ eor X_3.16b, TMP3.16b, TWEAKV6.16b
+ eor Y_3.16b, Y_3.16b, TWEAKV7.16b
+ mov TWEAKV0.16b, TWEAKV_NEXT.16b
+
+ // Store the ciphertext in the destination buffer
+ st1 {X_0.16b-Y_1.16b}, [DST], #64
+ st1 {X_2.16b-Y_3.16b}, [DST], #64
+
+ // Continue if there are more 128-byte chunks remaining
+ subs NBYTES, NBYTES, #128
+ bne .Lnext_128bytes_\@
+
+ // Store the next tweak and return
+.if \n == 64
+ st1 {TWEAKV_NEXT.16b}, [TWEAK]
+.else
+ st1 {TWEAKV_NEXT.8b}, [TWEAK]
+.endif
+ ret
+.endm
+
+ENTRY(speck128_xts_encrypt_neon)
+ _speck_xts_crypt n=64, lanes=2d, decrypting=0
+ENDPROC(speck128_xts_encrypt_neon)
+
+ENTRY(speck128_xts_decrypt_neon)
+ _speck_xts_crypt n=64, lanes=2d, decrypting=1
+ENDPROC(speck128_xts_decrypt_neon)
+
+ENTRY(speck64_xts_encrypt_neon)
+ _speck_xts_crypt n=32, lanes=4s, decrypting=0
+ENDPROC(speck64_xts_encrypt_neon)
+
+ENTRY(speck64_xts_decrypt_neon)
+ _speck_xts_crypt n=32, lanes=4s, decrypting=1
+ENDPROC(speck64_xts_decrypt_neon)
diff --git a/arch/arm64/crypto/speck-neon-glue.c b/arch/arm64/crypto/speck-neon-glue.c
new file mode 100644
index 0000000..c7d1856
--- /dev/null
+++ b/arch/arm64/crypto/speck-neon-glue.c
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * NEON-accelerated implementation of Speck128-XTS and Speck64-XTS
+ * (64-bit version; based on the 32-bit version)
+ *
+ * Copyright (c) 2018 Google, Inc
+ */
+
+#include <asm/hwcap.h>
+#include <asm/neon.h>
+#include <asm/simd.h>
+#include <crypto/algapi.h>
+#include <crypto/gf128mul.h>
+#include <crypto/speck.h>
+#include <crypto/xts.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+/* The assembly functions only handle multiples of 128 bytes */
+#define SPECK_NEON_CHUNK_SIZE 128
+
+/* Speck128 */
+
+struct speck128_xts_tfm_ctx {
+ struct speck128_tfm_ctx main_key;
+ struct speck128_tfm_ctx tweak_key;
+};
+
+asmlinkage void speck128_xts_encrypt_neon(const u64 *round_keys, int nrounds,
+ void *dst, const void *src,
+ unsigned int nbytes, void *tweak);
+
+asmlinkage void speck128_xts_decrypt_neon(const u64 *round_keys, int nrounds,
+ void *dst, const void *src,
+ unsigned int nbytes, void *tweak);
+
+typedef void (*speck128_crypt_one_t)(const struct speck128_tfm_ctx *,
+ u8 *, const u8 *);
+typedef void (*speck128_xts_crypt_many_t)(const u64 *, int, void *,
+ const void *, unsigned int, void *);
+
+static __always_inline int
+__speck128_xts_crypt(struct blkcipher_desc *desc, struct scatterlist *dst,
+ struct scatterlist *src, unsigned int nbytes,
+ speck128_crypt_one_t crypt_one,
+ speck128_xts_crypt_many_t crypt_many)
+{
+ struct crypto_blkcipher *tfm = desc->tfm;
+ const struct speck128_xts_tfm_ctx *ctx = crypto_blkcipher_ctx(tfm);
+ struct blkcipher_walk walk;
+ le128 tweak;
+ int err;
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+ err = blkcipher_walk_virt_block(desc, &walk, SPECK_NEON_CHUNK_SIZE);
+
+ crypto_speck128_encrypt(&ctx->tweak_key, (u8 *)&tweak, walk.iv);
+
+ while (walk.nbytes > 0) {
+ unsigned int nbytes = walk.nbytes;
+ u8 *dst = walk.dst.virt.addr;
+ const u8 *src = walk.src.virt.addr;
+
+ if (nbytes >= SPECK_NEON_CHUNK_SIZE && may_use_simd()) {
+ unsigned int count;
+
+ count = round_down(nbytes, SPECK_NEON_CHUNK_SIZE);
+ kernel_neon_begin();
+ (*crypt_many)(ctx->main_key.round_keys,
+ ctx->main_key.nrounds,
+ dst, src, count, &tweak);
+ kernel_neon_end();
+ dst += count;
+ src += count;
+ nbytes -= count;
+ }
+
+ /* Handle any remainder with generic code */
+ while (nbytes >= sizeof(tweak)) {
+ le128_xor((le128 *)dst, (const le128 *)src, &tweak);
+ (*crypt_one)(&ctx->main_key, dst, dst);
+ le128_xor((le128 *)dst, (const le128 *)dst, &tweak);
+ gf128mul_x_ble((be128 *)&tweak, (const be128 *)&tweak);
+
+ dst += sizeof(tweak);
+ src += sizeof(tweak);
+ nbytes -= sizeof(tweak);
+ }
+ err = blkcipher_walk_done(desc, &walk, nbytes);
+ }
+
+ return err;
+}
+
+static int speck128_xts_encrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst,
+ struct scatterlist *src,
+ unsigned int nbytes)
+{
+ return __speck128_xts_crypt(desc, dst, src, nbytes,
+ crypto_speck128_encrypt,
+ speck128_xts_encrypt_neon);
+}
+
+static int speck128_xts_decrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst,
+ struct scatterlist *src,
+ unsigned int nbytes)
+{
+ return __speck128_xts_crypt(desc, dst, src, nbytes,
+ crypto_speck128_decrypt,
+ speck128_xts_decrypt_neon);
+}
+
+static int speck128_xts_setkey(struct crypto_tfm *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct speck128_xts_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
+ int err;
+
+ if (keylen % 2)
+ return -EINVAL;
+
+ keylen /= 2;
+
+ err = crypto_speck128_setkey(&ctx->main_key, key, keylen);
+ if (err)
+ return err;
+
+ return crypto_speck128_setkey(&ctx->tweak_key, key + keylen, keylen);
+}
+
+/* Speck64 */
+
+struct speck64_xts_tfm_ctx {
+ struct speck64_tfm_ctx main_key;
+ struct speck64_tfm_ctx tweak_key;
+};
+
+asmlinkage void speck64_xts_encrypt_neon(const u32 *round_keys, int nrounds,
+ void *dst, const void *src,
+ unsigned int nbytes, void *tweak);
+
+asmlinkage void speck64_xts_decrypt_neon(const u32 *round_keys, int nrounds,
+ void *dst, const void *src,
+ unsigned int nbytes, void *tweak);
+
+typedef void (*speck64_crypt_one_t)(const struct speck64_tfm_ctx *,
+ u8 *, const u8 *);
+typedef void (*speck64_xts_crypt_many_t)(const u32 *, int, void *,
+ const void *, unsigned int, void *);
+
+static __always_inline int
+__speck64_xts_crypt(struct blkcipher_desc *desc, struct scatterlist *dst,
+ struct scatterlist *src, unsigned int nbytes,
+ speck64_crypt_one_t crypt_one,
+ speck64_xts_crypt_many_t crypt_many)
+{
+ struct crypto_blkcipher *tfm = desc->tfm;
+ const struct speck64_xts_tfm_ctx *ctx = crypto_blkcipher_ctx(tfm);
+ struct blkcipher_walk walk;
+ __le64 tweak;
+ int err;
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+ err = blkcipher_walk_virt_block(desc, &walk, SPECK_NEON_CHUNK_SIZE);
+
+ crypto_speck64_encrypt(&ctx->tweak_key, (u8 *)&tweak, walk.iv);
+
+ while (walk.nbytes > 0) {
+ unsigned int nbytes = walk.nbytes;
+ u8 *dst = walk.dst.virt.addr;
+ const u8 *src = walk.src.virt.addr;
+
+ if (nbytes >= SPECK_NEON_CHUNK_SIZE && may_use_simd()) {
+ unsigned int count;
+
+ count = round_down(nbytes, SPECK_NEON_CHUNK_SIZE);
+ kernel_neon_begin();
+ (*crypt_many)(ctx->main_key.round_keys,
+ ctx->main_key.nrounds,
+ dst, src, count, &tweak);
+ kernel_neon_end();
+ dst += count;
+ src += count;
+ nbytes -= count;
+ }
+
+ /* Handle any remainder with generic code */
+ while (nbytes >= sizeof(tweak)) {
+ *(__le64 *)dst = *(__le64 *)src ^ tweak;
+ (*crypt_one)(&ctx->main_key, dst, dst);
+ *(__le64 *)dst ^= tweak;
+ tweak = cpu_to_le64((le64_to_cpu(tweak) << 1) ^
+ ((tweak & cpu_to_le64(1ULL << 63)) ?
+ 0x1B : 0));
+ dst += sizeof(tweak);
+ src += sizeof(tweak);
+ nbytes -= sizeof(tweak);
+ }
+ err = blkcipher_walk_done(desc, &walk, nbytes);
+ }
+
+ return err;
+}
+
+static int speck64_xts_encrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ return __speck64_xts_crypt(desc, dst, src, nbytes,
+ crypto_speck64_encrypt,
+ speck64_xts_encrypt_neon);
+}
+
+static int speck64_xts_decrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ return __speck64_xts_crypt(desc, dst, src, nbytes,
+ crypto_speck64_decrypt,
+ speck64_xts_decrypt_neon);
+}
+
+static int speck64_xts_setkey(struct crypto_tfm *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct speck64_xts_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
+ int err;
+
+ if (keylen % 2)
+ return -EINVAL;
+
+ keylen /= 2;
+
+ err = crypto_speck64_setkey(&ctx->main_key, key, keylen);
+ if (err)
+ return err;
+
+ return crypto_speck64_setkey(&ctx->tweak_key, key + keylen, keylen);
+}
+
+static struct crypto_alg speck_algs[] = {
+ {
+ .cra_name = "xts(speck128)",
+ .cra_driver_name = "xts-speck128-neon",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER,
+ .cra_blocksize = SPECK128_BLOCK_SIZE,
+ .cra_type = &crypto_blkcipher_type,
+ .cra_ctxsize = sizeof(struct speck128_xts_tfm_ctx),
+ .cra_alignmask = 7,
+ .cra_module = THIS_MODULE,
+ .cra_u = {
+ .blkcipher = {
+ .min_keysize = 2 * SPECK128_128_KEY_SIZE,
+ .max_keysize = 2 * SPECK128_256_KEY_SIZE,
+ .ivsize = SPECK128_BLOCK_SIZE,
+ .setkey = speck128_xts_setkey,
+ .encrypt = speck128_xts_encrypt,
+ .decrypt = speck128_xts_decrypt,
+ }
+ }
+ }, {
+ .cra_name = "xts(speck64)",
+ .cra_driver_name = "xts-speck64-neon",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER,
+ .cra_blocksize = SPECK64_BLOCK_SIZE,
+ .cra_type = &crypto_blkcipher_type,
+ .cra_ctxsize = sizeof(struct speck64_xts_tfm_ctx),
+ .cra_alignmask = 7,
+ .cra_module = THIS_MODULE,
+ .cra_u = {
+ .blkcipher = {
+ .min_keysize = 2 * SPECK64_96_KEY_SIZE,
+ .max_keysize = 2 * SPECK64_128_KEY_SIZE,
+ .ivsize = SPECK64_BLOCK_SIZE,
+ .setkey = speck64_xts_setkey,
+ .encrypt = speck64_xts_encrypt,
+ .decrypt = speck64_xts_decrypt,
+ }
+ }
+ }
+};
+
+static int __init speck_neon_module_init(void)
+{
+ if (!(elf_hwcap & HWCAP_ASIMD))
+ return -ENODEV;
+ return crypto_register_algs(speck_algs, ARRAY_SIZE(speck_algs));
+}
+
+static void __exit speck_neon_module_exit(void)
+{
+ crypto_unregister_algs(speck_algs, ARRAY_SIZE(speck_algs));
+}
+
+module_init(speck_neon_module_init);
+module_exit(speck_neon_module_exit);
+
+MODULE_DESCRIPTION("Speck block cipher (NEON-accelerated)");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Eric Biggers <ebiggers@google.com>");
+MODULE_ALIAS_CRYPTO("xts(speck128)");
+MODULE_ALIAS_CRYPTO("xts-speck128-neon");
+MODULE_ALIAS_CRYPTO("xts(speck64)");
+MODULE_ALIAS_CRYPTO("xts-speck64-neon");
diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h
index f1ace59..c4cc771 100644
--- a/arch/arm64/include/asm/arch_gicv3.h
+++ b/arch/arm64/include/asm/arch_gicv3.h
@@ -84,14 +84,20 @@
#define read_gicreg(r) \
({ \
u64 reg; \
- asm volatile("mrs_s %0, " __stringify(r) : "=r" (reg)); \
+ asm volatile(DEFINE_MRS_S \
+ "mrs_s %0, " __stringify(r) "\n" \
+ UNDEFINE_MRS_S \
+ : "=r" (reg)); \
reg; \
})
#define write_gicreg(v,r) \
do { \
u64 __val = (v); \
- asm volatile("msr_s " __stringify(r) ", %0" : : "r" (__val));\
+ asm volatile(DEFINE_MSR_S \
+ "msr_s " __stringify(r) ", %0\n" \
+ UNDEFINE_MSR_S \
+ : : "r" (__val)); \
} while (0)
/*
@@ -103,13 +109,19 @@
static inline void gic_write_eoir(u32 irq)
{
- asm volatile("msr_s " __stringify(ICC_EOIR1_EL1) ", %0" : : "r" ((u64)irq));
+ asm volatile(DEFINE_MSR_S
+ "msr_s " __stringify(ICC_EOIR1_EL1) ", %0\n"
+ UNDEFINE_MSR_S
+ : : "r" ((u64)irq));
isb();
}
static inline void gic_write_dir(u32 irq)
{
- asm volatile("msr_s " __stringify(ICC_DIR_EL1) ", %0" : : "r" ((u64)irq));
+ asm volatile(DEFINE_MSR_S
+ "msr_s " __stringify(ICC_DIR_EL1) ", %0\n"
+ UNDEFINE_MSR_S
+ : : "r" ((u64)irq));
isb();
}
@@ -117,7 +129,10 @@
{
u64 irqstat;
- asm volatile("mrs_s %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat));
+ asm volatile(DEFINE_MRS_S
+ "mrs_s %0, " __stringify(ICC_IAR1_EL1) "\n"
+ UNDEFINE_MRS_S
+ : "=r" (irqstat));
dsb(sy);
return irqstat;
}
@@ -136,7 +151,9 @@
asm volatile(
"nop;nop;nop;nop\n\t"
"nop;nop;nop;nop\n\t"
+ DEFINE_MRS_S
"mrs_s %0, " __stringify(ICC_IAR1_EL1) "\n\t"
+ UNDEFINE_MRS_S
"nop;nop;nop;nop"
: "=r" (irqstat));
mb();
@@ -146,26 +163,38 @@
static inline void gic_write_pmr(u32 val)
{
- asm volatile("msr_s " __stringify(ICC_PMR_EL1) ", %0" : : "r" ((u64)val));
+ asm volatile(DEFINE_MSR_S
+ "msr_s " __stringify(ICC_PMR_EL1) ", %0\n"
+ UNDEFINE_MSR_S
+ : : "r" ((u64)val));
/* As per the architecture specification */
mb();
}
static inline void gic_write_ctlr(u32 val)
{
- asm volatile("msr_s " __stringify(ICC_CTLR_EL1) ", %0" : : "r" ((u64)val));
+ asm volatile(DEFINE_MSR_S
+ "msr_s " __stringify(ICC_CTLR_EL1) ", %0\n"
+ UNDEFINE_MSR_S
+ : : "r" ((u64)val));
isb();
}
static inline void gic_write_grpen1(u32 val)
{
- asm volatile("msr_s " __stringify(ICC_GRPEN1_EL1) ", %0" : : "r" ((u64)val));
+ asm volatile(DEFINE_MSR_S
+ "msr_s " __stringify(ICC_GRPEN1_EL1) ", %0\n"
+ UNDEFINE_MSR_S
+ : : "r" ((u64)val));
isb();
}
static inline void gic_write_sgi1r(u64 val)
{
- asm volatile("msr_s " __stringify(ICC_SGI1R_EL1) ", %0" : : "r" (val));
+ asm volatile(DEFINE_MSR_S
+ "msr_s " __stringify(ICC_SGI1R_EL1) ", %0\n"
+ UNDEFINE_MSR_S
+ : : "r" (val));
/* As per the architecture specification */
mb();
}
@@ -174,19 +203,28 @@
{
u64 val;
- asm volatile("mrs_s %0, " __stringify(ICC_SRE_EL1) : "=r" (val));
+ asm volatile(DEFINE_MRS_S
+ "mrs_s %0, " __stringify(ICC_SRE_EL1) "\n"
+ UNDEFINE_MRS_S
+ : "=r" (val));
return val;
}
static inline void gic_write_sre(u32 val)
{
- asm volatile("msr_s " __stringify(ICC_SRE_EL1) ", %0" : : "r" ((u64)val));
+ asm volatile(DEFINE_MSR_S
+ "msr_s " __stringify(ICC_SRE_EL1) ", %0\n"
+ UNDEFINE_MSR_S
+ : : "r" ((u64)val));
isb();
}
static inline void gic_write_bpr1(u32 val)
{
- asm volatile("msr_s " __stringify(ICC_BPR1_EL1) ", %0" : : "r" (val));
+ asm volatile(DEFINE_MSR_S
+ "msr_s " __stringify(ICC_BPR1_EL1) ", %x0\n"
+ UNDEFINE_MSR_S
+ : : "rZ" (val));
}
#define gic_read_typer(c) readq_relaxed_no_log(c)
diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h
index b18e852..36d6758 100644
--- a/arch/arm64/include/asm/kvm_hyp.h
+++ b/arch/arm64/include/asm/kvm_hyp.h
@@ -29,7 +29,9 @@
({ \
u64 reg; \
asm volatile(ALTERNATIVE("mrs %0, " __stringify(r##nvh),\
- "mrs_s %0, " __stringify(r##vh),\
+ DEFINE_MRS_S \
+ "mrs_s %0, " __stringify(r##vh) "\n"\
+ UNDEFINE_MRS_S, \
ARM64_HAS_VIRT_HOST_EXTN) \
: "=r" (reg)); \
reg; \
@@ -39,7 +41,9 @@
do { \
u64 __val = (u64)(v); \
asm volatile(ALTERNATIVE("msr " __stringify(r##nvh) ", %x0",\
- "msr_s " __stringify(r##vh) ", %x0",\
+ DEFINE_MSR_S \
+ "msr_s " __stringify(r##vh) ", %x0\n"\
+ UNDEFINE_MSR_S, \
ARM64_HAS_VIRT_HOST_EXTN) \
: : "rZ" (__val)); \
} while (0)
diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
index a0fac5c..f2ea8a2 100644
--- a/arch/arm64/include/asm/mmu_context.h
+++ b/arch/arm64/include/asm/mmu_context.h
@@ -134,7 +134,7 @@
* Atomically replaces the active TTBR1_EL1 PGD with a new VA-compatible PGD,
* avoiding the possibility of conflicting TLB entries being allocated.
*/
-static inline void cpu_replace_ttbr1(pgd_t *pgd)
+static inline void __nocfi cpu_replace_ttbr1(pgd_t *pgd)
{
typedef void (ttbr_replace_func)(phys_addr_t);
extern ttbr_replace_func idmap_cpu_replace_ttbr1;
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index 88bbe36..8546afc 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -246,20 +246,39 @@
#include <linux/types.h>
-asm(
-" .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30\n"
-" .equ .L__reg_num_x\\num, \\num\n"
-" .endr\n"
+#define __DEFINE_MRS_MSR_S_REGNUM \
+" .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30\n" \
+" .equ .L__reg_num_x\\num, \\num\n" \
+" .endr\n" \
" .equ .L__reg_num_xzr, 31\n"
-"\n"
-" .macro mrs_s, rt, sreg\n"
-" .inst 0xd5200000|(\\sreg)|(.L__reg_num_\\rt)\n"
+
+#define DEFINE_MRS_S \
+ __DEFINE_MRS_MSR_S_REGNUM \
+" .macro mrs_s, rt, sreg\n" \
+" .inst 0xd5200000|(\\sreg)|(.L__reg_num_\\rt)\n" \
" .endm\n"
-"\n"
-" .macro msr_s, sreg, rt\n"
-" .inst 0xd5000000|(\\sreg)|(.L__reg_num_\\rt)\n"
+
+#define DEFINE_MSR_S \
+ __DEFINE_MRS_MSR_S_REGNUM \
+" .macro msr_s, sreg, rt\n" \
+" .inst 0xd5000000|(\\sreg)|(.L__reg_num_\\rt)\n" \
" .endm\n"
-);
+
+#define UNDEFINE_MRS_S \
+" .purgem mrs_s\n"
+
+#define UNDEFINE_MSR_S \
+" .purgem msr_s\n"
+
+#define __mrs_s(r, v) \
+ DEFINE_MRS_S \
+" mrs_s %0, " __stringify(r) "\n" \
+ UNDEFINE_MRS_S : "=r" (v)
+
+#define __msr_s(r, v) \
+ DEFINE_MSR_S \
+" msr_s " __stringify(r) ", %x0\n" \
+ UNDEFINE_MSR_S : : "rZ" (v)
/*
* Unlike read_cpuid, calls to read_sysreg are never expected to be
@@ -285,15 +304,15 @@
* For registers without architectural names, or simply unsupported by
* GAS.
*/
-#define read_sysreg_s(r) ({ \
- u64 __val; \
- asm volatile("mrs_s %0, " __stringify(r) : "=r" (__val)); \
- __val; \
+#define read_sysreg_s(r) ({ \
+ u64 __val; \
+ asm volatile(__mrs_s(r, __val)); \
+ __val; \
})
-#define write_sysreg_s(v, r) do { \
- u64 __val = (u64)v; \
- asm volatile("msr_s " __stringify(r) ", %x0" : : "rZ" (__val)); \
+#define write_sysreg_s(v, r) do { \
+ u64 __val = (u64)(v); \
+ asm volatile(__msr_s(r, __val)); \
} while (0)
static inline void config_sctlr_el1(u32 clear, u32 set)
diff --git a/arch/arm64/kernel/module.lds b/arch/arm64/kernel/module.lds
index f7c9781..eacb5c6 100644
--- a/arch/arm64/kernel/module.lds
+++ b/arch/arm64/kernel/module.lds
@@ -1,4 +1,4 @@
SECTIONS {
- .plt (NOLOAD) : { BYTE(0) }
- .init.plt (NOLOAD) : { BYTE(0) }
+ .plt : { BYTE(0) }
+ .init.plt : { BYTE(0) }
}
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index fcd2b33..bfaead1 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -53,7 +53,7 @@
"Error"
};
-int show_unhandled_signals = 1;
+int show_unhandled_signals = 0;
/*
* Dump out the contents of some kernel memory nicely...
diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile
index 62c84f7..88fef38 100644
--- a/arch/arm64/kernel/vdso/Makefile
+++ b/arch/arm64/kernel/vdso/Makefile
@@ -14,6 +14,7 @@
ccflags-y := -shared -fno-common -fno-builtin
ccflags-y += -nostdlib -Wl,-soname=linux-vdso.so.1 \
$(call cc-ldoption, -Wl$(comma)--hash-style=sysv)
+ccflags-y += $(DISABLE_LTO)
# Disable gcov profiling for VDSO code
GCOV_PROFILE := n
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index 34d3ed6..402a621 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -60,7 +60,7 @@
#define TRAMP_TEXT \
. = ALIGN(PAGE_SIZE); \
VMLINUX_SYMBOL(__entry_tramp_text_start) = .; \
- *(.entry.tramp.text) \
+ KEEP(*(.entry.tramp.text)) \
. = ALIGN(PAGE_SIZE); \
VMLINUX_SYMBOL(__entry_tramp_text_end) = .;
#else
@@ -179,11 +179,11 @@
. = ALIGN(4);
.altinstructions : {
__alt_instructions = .;
- *(.altinstructions)
+ KEEP(*(.altinstructions))
__alt_instructions_end = .;
}
.altinstr_replacement : {
- *(.altinstr_replacement)
+ KEEP(*(.altinstr_replacement))
}
.rela : ALIGN(8) {
*(.rela .rela*)
diff --git a/arch/arm64/kvm/hyp/Makefile b/arch/arm64/kvm/hyp/Makefile
index 48b0354..c470a7c 100644
--- a/arch/arm64/kvm/hyp/Makefile
+++ b/arch/arm64/kvm/hyp/Makefile
@@ -4,6 +4,10 @@
ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING
+ifeq ($(cc-name),clang)
+ccflags-y += -fno-jump-tables
+endif
+
KVM=../../../../virt/kvm
obj-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/hyp/vgic-v2-sr.o
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index d8cdb16..e7bf3c3 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -861,3 +861,13 @@
pmd_clear(pmd);
return 1;
}
+
+int pud_free_pmd_page(pud_t *pud)
+{
+ return pud_none(*pud);
+}
+
+int pmd_free_pte_page(pmd_t *pmd)
+{
+ return pmd_none(*pmd);
+}
diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
index d8199e1..b47a26f 100644
--- a/arch/arm64/net/bpf_jit_comp.c
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -234,8 +234,9 @@
off = offsetof(struct bpf_array, map.max_entries);
emit_a64_mov_i64(tmp, off, ctx);
emit(A64_LDR32(tmp, r2, tmp), ctx);
+ emit(A64_MOV(0, r3, r3), ctx);
emit(A64_CMP(0, r3, tmp), ctx);
- emit(A64_B_(A64_COND_GE, jmp_offset), ctx);
+ emit(A64_B_(A64_COND_CS, jmp_offset), ctx);
/* if (tail_call_cnt > MAX_TAIL_CALL_CNT)
* goto out;
@@ -243,7 +244,7 @@
*/
emit_a64_mov_i64(tmp, MAX_TAIL_CALL_CNT, ctx);
emit(A64_CMP(1, tcc, tmp), ctx);
- emit(A64_B_(A64_COND_GT, jmp_offset), ctx);
+ emit(A64_B_(A64_COND_HI, jmp_offset), ctx);
emit(A64_ADD_I(1, tcc, tcc, 1), ctx);
/* prog = array->ptrs[index];
diff --git a/arch/ia64/kernel/module.c b/arch/ia64/kernel/module.c
index 6ab0ae7..d1d945c 100644
--- a/arch/ia64/kernel/module.c
+++ b/arch/ia64/kernel/module.c
@@ -153,7 +153,7 @@
static int
apply_imm64 (struct module *mod, struct insn *insn, uint64_t val)
{
- if (slot(insn) != 2) {
+ if (slot(insn) != 1 && slot(insn) != 2) {
printk(KERN_ERR "%s: invalid slot number %d for IMM64\n",
mod->name, slot(insn));
return 0;
@@ -165,7 +165,7 @@
static int
apply_imm60 (struct module *mod, struct insn *insn, uint64_t val)
{
- if (slot(insn) != 2) {
+ if (slot(insn) != 1 && slot(insn) != 2) {
printk(KERN_ERR "%s: invalid slot number %d for IMM60\n",
mod->name, slot(insn));
return 0;
diff --git a/arch/mips/ath25/board.c b/arch/mips/ath25/board.c
index 9ab48ff..6d11ae5 100644
--- a/arch/mips/ath25/board.c
+++ b/arch/mips/ath25/board.c
@@ -135,6 +135,8 @@
}
board_data = kzalloc(BOARD_CONFIG_BUFSZ, GFP_KERNEL);
+ if (!board_data)
+ goto error;
ath25_board.config = (struct ath25_boarddata *)board_data;
memcpy_fromio(board_data, bcfg, 0x100);
if (broken_boarddata) {
diff --git a/arch/mips/cavium-octeon/octeon-irq.c b/arch/mips/cavium-octeon/octeon-irq.c
index c1eb1ff..6ed1ded 100644
--- a/arch/mips/cavium-octeon/octeon-irq.c
+++ b/arch/mips/cavium-octeon/octeon-irq.c
@@ -2277,6 +2277,8 @@
}
host_data = kzalloc(sizeof(*host_data), GFP_KERNEL);
+ if (!host_data)
+ return -ENOMEM;
raw_spin_lock_init(&host_data->lock);
addr = of_get_address(ciu_node, 0, NULL, NULL);
diff --git a/arch/mips/kernel/mips-r2-to-r6-emul.c b/arch/mips/kernel/mips-r2-to-r6-emul.c
index d8227f2..9a4aed6 100644
--- a/arch/mips/kernel/mips-r2-to-r6-emul.c
+++ b/arch/mips/kernel/mips-r2-to-r6-emul.c
@@ -1096,10 +1096,20 @@
}
break;
- case beql_op:
- case bnel_op:
case blezl_op:
case bgtzl_op:
+ /*
+ * For BLEZL and BGTZL, rt field must be set to 0. If this
+ * is not the case, this may be an encoding of a MIPS R6
+ * instruction, so return to CPU execution if this occurs
+ */
+ if (MIPSInst_RT(inst)) {
+ err = SIGILL;
+ break;
+ }
+ /* fall through */
+ case beql_op:
+ case bnel_op:
if (delay_slot(regs)) {
err = SIGILL;
break;
@@ -2329,6 +2339,8 @@
__this_cpu_write((mipsr2bremustats).bgezl, 0);
__this_cpu_write((mipsr2bremustats).bltzll, 0);
__this_cpu_write((mipsr2bremustats).bgezll, 0);
+ __this_cpu_write((mipsr2bremustats).bltzall, 0);
+ __this_cpu_write((mipsr2bremustats).bgezall, 0);
__this_cpu_write((mipsr2bremustats).bltzal, 0);
__this_cpu_write((mipsr2bremustats).bgezal, 0);
__this_cpu_write((mipsr2bremustats).beql, 0);
diff --git a/arch/mips/kernel/smp-bmips.c b/arch/mips/kernel/smp-bmips.c
index 47c9646..d4a293b 100644
--- a/arch/mips/kernel/smp-bmips.c
+++ b/arch/mips/kernel/smp-bmips.c
@@ -166,11 +166,11 @@
return;
}
- if (request_irq(IPI0_IRQ, bmips_ipi_interrupt, IRQF_PERCPU,
- "smp_ipi0", NULL))
+ if (request_irq(IPI0_IRQ, bmips_ipi_interrupt,
+ IRQF_PERCPU | IRQF_NO_SUSPEND, "smp_ipi0", NULL))
panic("Can't request IPI0 interrupt");
- if (request_irq(IPI1_IRQ, bmips_ipi_interrupt, IRQF_PERCPU,
- "smp_ipi1", NULL))
+ if (request_irq(IPI1_IRQ, bmips_ipi_interrupt,
+ IRQF_PERCPU | IRQF_NO_SUSPEND, "smp_ipi1", NULL))
panic("Can't request IPI1 interrupt");
}
diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
index 0344e57..fba4ca5 100644
--- a/arch/mips/lib/Makefile
+++ b/arch/mips/lib/Makefile
@@ -15,4 +15,5 @@
obj-$(CONFIG_CPU_TX39XX) += r3k_dump_tlb.o
# libgcc-style stuff needed in the kernel
-obj-y += ashldi3.o ashrdi3.o bswapsi.o bswapdi.o cmpdi2.o lshrdi3.o ucmpdi2.o
+obj-y += ashldi3.o ashrdi3.o bswapsi.o bswapdi.o cmpdi2.o lshrdi3.o multi3.o \
+ ucmpdi2.o
diff --git a/arch/mips/lib/libgcc.h b/arch/mips/lib/libgcc.h
index 05909d58..56ea0df 100644
--- a/arch/mips/lib/libgcc.h
+++ b/arch/mips/lib/libgcc.h
@@ -9,10 +9,18 @@
struct DWstruct {
int high, low;
};
+
+struct TWstruct {
+ long long high, low;
+};
#elif defined(__LITTLE_ENDIAN)
struct DWstruct {
int low, high;
};
+
+struct TWstruct {
+ long long low, high;
+};
#else
#error I feel sick.
#endif
@@ -22,4 +30,13 @@
long long ll;
} DWunion;
+#if defined(CONFIG_64BIT) && defined(CONFIG_CPU_MIPSR6)
+typedef int ti_type __attribute__((mode(TI)));
+
+typedef union {
+ struct TWstruct s;
+ ti_type ti;
+} TWunion;
+#endif
+
#endif /* __ASM_LIBGCC_H */
diff --git a/arch/mips/lib/multi3.c b/arch/mips/lib/multi3.c
new file mode 100644
index 0000000..111ad47
--- /dev/null
+++ b/arch/mips/lib/multi3.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/export.h>
+
+#include "libgcc.h"
+
+/*
+ * GCC 7 suboptimally generates __multi3 calls for mips64r6, so for that
+ * specific case only we'll implement it here.
+ *
+ * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82981
+ */
+#if defined(CONFIG_64BIT) && defined(CONFIG_CPU_MIPSR6) && (__GNUC__ == 7)
+
+/* multiply 64-bit values, low 64-bits returned */
+static inline long long notrace dmulu(long long a, long long b)
+{
+ long long res;
+
+ asm ("dmulu %0,%1,%2" : "=r" (res) : "r" (a), "r" (b));
+ return res;
+}
+
+/* multiply 64-bit unsigned values, high 64-bits of 128-bit result returned */
+static inline long long notrace dmuhu(long long a, long long b)
+{
+ long long res;
+
+ asm ("dmuhu %0,%1,%2" : "=r" (res) : "r" (a), "r" (b));
+ return res;
+}
+
+/* multiply 128-bit values, low 128-bits returned */
+ti_type notrace __multi3(ti_type a, ti_type b)
+{
+ TWunion res, aa, bb;
+
+ aa.ti = a;
+ bb.ti = b;
+
+ /*
+ * a * b = (a.lo * b.lo)
+ * + 2^64 * (a.hi * b.lo + a.lo * b.hi)
+ * [+ 2^128 * (a.hi * b.hi)]
+ */
+ res.s.low = dmulu(aa.s.low, bb.s.low);
+ res.s.high = dmuhu(aa.s.low, bb.s.low);
+ res.s.high += dmulu(aa.s.high, bb.s.low);
+ res.s.high += dmulu(aa.s.low, bb.s.high);
+
+ return res.ti;
+}
+EXPORT_SYMBOL(__multi3);
+
+#endif /* 64BIT && CPU_MIPSR6 && GCC7 */
diff --git a/arch/mips/net/bpf_jit.c b/arch/mips/net/bpf_jit.c
index 49a2e22..2486037 100644
--- a/arch/mips/net/bpf_jit.c
+++ b/arch/mips/net/bpf_jit.c
@@ -526,7 +526,8 @@
u32 sflags, tmp_flags;
/* Adjust the stack pointer */
- emit_stack_offset(-align_sp(offset), ctx);
+ if (offset)
+ emit_stack_offset(-align_sp(offset), ctx);
tmp_flags = sflags = ctx->flags >> SEEN_SREG_SFT;
/* sflags is essentially a bitmap */
@@ -578,7 +579,8 @@
emit_load_stack_reg(r_ra, r_sp, real_off, ctx);
/* Restore the sp and discard the scrach memory */
- emit_stack_offset(align_sp(offset), ctx);
+ if (offset)
+ emit_stack_offset(align_sp(offset), ctx);
}
static unsigned int get_stack_depth(struct jit_ctx *ctx)
@@ -625,8 +627,14 @@
if (ctx->flags & SEEN_X)
emit_jit_reg_move(r_X, r_zero, ctx);
- /* Do not leak kernel data to userspace */
- if (bpf_needs_clear_a(&ctx->skf->insns[0]))
+ /*
+ * Do not leak kernel data to userspace, we only need to clear
+ * r_A if it is ever used. In fact if it is never used, we
+ * will not save/restore it, so clearing it in this case would
+ * corrupt the state of the caller.
+ */
+ if (bpf_needs_clear_a(&ctx->skf->insns[0]) &&
+ (ctx->flags & SEEN_A))
emit_jit_reg_move(r_A, r_zero, ctx);
}
diff --git a/arch/mips/net/bpf_jit_asm.S b/arch/mips/net/bpf_jit_asm.S
index 5d2e0c8..88a2075 100644
--- a/arch/mips/net/bpf_jit_asm.S
+++ b/arch/mips/net/bpf_jit_asm.S
@@ -90,18 +90,14 @@
is_offset_in_header(2, half)
/* Offset within header boundaries */
PTR_ADDU t1, $r_skb_data, offset
- .set reorder
- lh $r_A, 0(t1)
- .set noreorder
+ lhu $r_A, 0(t1)
#ifdef CONFIG_CPU_LITTLE_ENDIAN
# if defined(__mips_isa_rev) && (__mips_isa_rev >= 2)
- wsbh t0, $r_A
- seh $r_A, t0
+ wsbh $r_A, $r_A
# else
- sll t0, $r_A, 24
- andi t1, $r_A, 0xff00
- sra t0, t0, 16
- srl t1, t1, 8
+ sll t0, $r_A, 8
+ srl t1, $r_A, 8
+ andi t0, t0, 0xff00
or $r_A, t0, t1
# endif
#endif
@@ -115,7 +111,7 @@
is_offset_in_header(1, byte)
/* Offset within header boundaries */
PTR_ADDU t1, $r_skb_data, offset
- lb $r_A, 0(t1)
+ lbu $r_A, 0(t1)
jr $r_ra
move $r_ret, zero
END(sk_load_byte)
@@ -139,6 +135,11 @@
* (void *to) is returned in r_s0
*
*/
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+#define DS_OFFSET(SIZE) (4 * SZREG)
+#else
+#define DS_OFFSET(SIZE) ((4 * SZREG) + (4 - SIZE))
+#endif
#define bpf_slow_path_common(SIZE) \
/* Quick check. Are we within reasonable boundaries? */ \
LONG_ADDIU $r_s1, $r_skb_len, -SIZE; \
@@ -150,7 +151,7 @@
PTR_LA t0, skb_copy_bits; \
PTR_S $r_ra, (5 * SZREG)($r_sp); \
/* Assign low slot to a2 */ \
- move a2, $r_sp; \
+ PTR_ADDIU a2, $r_sp, DS_OFFSET(SIZE); \
jalr t0; \
/* Reset our destination slot (DS but it's ok) */ \
INT_S zero, (4 * SZREG)($r_sp); \
diff --git a/arch/mips/ralink/reset.c b/arch/mips/ralink/reset.c
index 64543d6..e9531fe 100644
--- a/arch/mips/ralink/reset.c
+++ b/arch/mips/ralink/reset.c
@@ -96,16 +96,9 @@
unreachable();
}
-static void ralink_halt(void)
-{
- local_irq_disable();
- unreachable();
-}
-
static int __init mips_reboot_setup(void)
{
_machine_restart = ralink_restart;
- _machine_halt = ralink_halt;
return 0;
}
diff --git a/arch/parisc/include/asm/cacheflush.h b/arch/parisc/include/asm/cacheflush.h
index 1d8c24d..88290d3 100644
--- a/arch/parisc/include/asm/cacheflush.h
+++ b/arch/parisc/include/asm/cacheflush.h
@@ -25,6 +25,7 @@
void flush_kernel_icache_range_asm(unsigned long, unsigned long);
void flush_user_dcache_range_asm(unsigned long, unsigned long);
void flush_kernel_dcache_range_asm(unsigned long, unsigned long);
+void purge_kernel_dcache_range_asm(unsigned long, unsigned long);
void flush_kernel_dcache_page_asm(void *);
void flush_kernel_icache_page(void *);
void flush_user_dcache_range(unsigned long, unsigned long);
diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c
index df757c9..8a08221 100644
--- a/arch/parisc/kernel/cache.c
+++ b/arch/parisc/kernel/cache.c
@@ -464,10 +464,10 @@
int __flush_tlb_range(unsigned long sid, unsigned long start,
unsigned long end)
{
- unsigned long flags, size;
+ unsigned long flags;
- size = (end - start);
- if (size >= parisc_tlb_flush_threshold) {
+ if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
+ end - start >= parisc_tlb_flush_threshold) {
flush_tlb_all();
return 1;
}
@@ -538,13 +538,12 @@
struct vm_area_struct *vma;
pgd_t *pgd;
- /* Flush the TLB to avoid speculation if coherency is required. */
- if (parisc_requires_coherency())
- flush_tlb_all();
-
/* Flushing the whole cache on each cpu takes forever on
rp3440, etc. So, avoid it if the mm isn't too big. */
- if (mm_total_size(mm) >= parisc_cache_flush_threshold) {
+ if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
+ mm_total_size(mm) >= parisc_cache_flush_threshold) {
+ if (mm->context)
+ flush_tlb_all();
flush_cache_all();
return;
}
@@ -552,9 +551,9 @@
if (mm->context == mfsp(3)) {
for (vma = mm->mmap; vma; vma = vma->vm_next) {
flush_user_dcache_range_asm(vma->vm_start, vma->vm_end);
- if ((vma->vm_flags & VM_EXEC) == 0)
- continue;
- flush_user_icache_range_asm(vma->vm_start, vma->vm_end);
+ if (vma->vm_flags & VM_EXEC)
+ flush_user_icache_range_asm(vma->vm_start, vma->vm_end);
+ flush_tlb_range(vma, vma->vm_start, vma->vm_end);
}
return;
}
@@ -572,6 +571,8 @@
pfn = pte_pfn(*ptep);
if (!pfn_valid(pfn))
continue;
+ if (unlikely(mm->context))
+ flush_tlb_page(vma, addr);
__flush_cache_page(vma, addr, PFN_PHYS(pfn));
}
}
@@ -598,30 +599,45 @@
void flush_cache_range(struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
- BUG_ON(!vma->vm_mm->context);
+ pgd_t *pgd;
+ unsigned long addr;
- /* Flush the TLB to avoid speculation if coherency is required. */
- if (parisc_requires_coherency())
- flush_tlb_range(vma, start, end);
-
- if ((end - start) >= parisc_cache_flush_threshold
- || vma->vm_mm->context != mfsp(3)) {
+ if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
+ end - start >= parisc_cache_flush_threshold) {
+ if (vma->vm_mm->context)
+ flush_tlb_range(vma, start, end);
flush_cache_all();
return;
}
- flush_user_dcache_range_asm(start, end);
- if (vma->vm_flags & VM_EXEC)
- flush_user_icache_range_asm(start, end);
+ if (vma->vm_mm->context == mfsp(3)) {
+ flush_user_dcache_range_asm(start, end);
+ if (vma->vm_flags & VM_EXEC)
+ flush_user_icache_range_asm(start, end);
+ flush_tlb_range(vma, start, end);
+ return;
+ }
+
+ pgd = vma->vm_mm->pgd;
+ for (addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE) {
+ unsigned long pfn;
+ pte_t *ptep = get_ptep(pgd, addr);
+ if (!ptep)
+ continue;
+ pfn = pte_pfn(*ptep);
+ if (pfn_valid(pfn)) {
+ if (unlikely(vma->vm_mm->context))
+ flush_tlb_page(vma, addr);
+ __flush_cache_page(vma, addr, PFN_PHYS(pfn));
+ }
+ }
}
void
flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long pfn)
{
- BUG_ON(!vma->vm_mm->context);
-
if (pfn_valid(pfn)) {
- if (parisc_requires_coherency())
+ if (likely(vma->vm_mm->context))
flush_tlb_page(vma, vmaddr);
__flush_cache_page(vma, vmaddr, PFN_PHYS(pfn));
}
@@ -630,21 +646,33 @@
void flush_kernel_vmap_range(void *vaddr, int size)
{
unsigned long start = (unsigned long)vaddr;
+ unsigned long end = start + size;
- if ((unsigned long)size > parisc_cache_flush_threshold)
+ if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
+ (unsigned long)size >= parisc_cache_flush_threshold) {
+ flush_tlb_kernel_range(start, end);
flush_data_cache();
- else
- flush_kernel_dcache_range_asm(start, start + size);
+ return;
+ }
+
+ flush_kernel_dcache_range_asm(start, end);
+ flush_tlb_kernel_range(start, end);
}
EXPORT_SYMBOL(flush_kernel_vmap_range);
void invalidate_kernel_vmap_range(void *vaddr, int size)
{
unsigned long start = (unsigned long)vaddr;
+ unsigned long end = start + size;
- if ((unsigned long)size > parisc_cache_flush_threshold)
+ if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
+ (unsigned long)size >= parisc_cache_flush_threshold) {
+ flush_tlb_kernel_range(start, end);
flush_data_cache();
- else
- flush_kernel_dcache_range_asm(start, start + size);
+ return;
+ }
+
+ purge_kernel_dcache_range_asm(start, end);
+ flush_tlb_kernel_range(start, end);
}
EXPORT_SYMBOL(invalidate_kernel_vmap_range);
diff --git a/arch/parisc/kernel/pacache.S b/arch/parisc/kernel/pacache.S
index 2d40c4f..67b0f75 100644
--- a/arch/parisc/kernel/pacache.S
+++ b/arch/parisc/kernel/pacache.S
@@ -1110,6 +1110,28 @@
.procend
ENDPROC_CFI(flush_kernel_dcache_range_asm)
+ENTRY_CFI(purge_kernel_dcache_range_asm)
+ .proc
+ .callinfo NO_CALLS
+ .entry
+
+ ldil L%dcache_stride, %r1
+ ldw R%dcache_stride(%r1), %r23
+ ldo -1(%r23), %r21
+ ANDCM %r26, %r21, %r26
+
+1: cmpb,COND(<<),n %r26, %r25,1b
+ pdc,m %r23(%r26)
+
+ sync
+ syncdma
+ bv %r0(%r2)
+ nop
+ .exit
+
+ .procend
+ENDPROC_CFI(purge_kernel_dcache_range_asm)
+
ENTRY_CFI(flush_user_icache_range_asm)
.proc
.callinfo NO_CALLS
diff --git a/arch/powerpc/include/asm/code-patching.h b/arch/powerpc/include/asm/code-patching.h
index 2015b07..b4ab1f4 100644
--- a/arch/powerpc/include/asm/code-patching.h
+++ b/arch/powerpc/include/asm/code-patching.h
@@ -30,6 +30,7 @@
int patch_instruction(unsigned int *addr, unsigned int instr);
int instr_is_relative_branch(unsigned int instr);
+int instr_is_relative_link_branch(unsigned int instr);
int instr_is_branch_to_addr(const unsigned int *instr, unsigned long addr);
unsigned long branch_target(const unsigned int *instr);
unsigned int translate_branch(const unsigned int *dest,
diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h
index ab68d0e..4e54282 100644
--- a/arch/powerpc/include/asm/cputable.h
+++ b/arch/powerpc/include/asm/cputable.h
@@ -474,7 +474,8 @@
CPU_FTR_ICSWX | CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \
CPU_FTR_DBELL | CPU_FTR_HAS_PPR | CPU_FTR_DAWR | \
CPU_FTR_ARCH_207S | CPU_FTR_TM_COMP | CPU_FTR_ARCH_300)
-#define CPU_FTRS_POWER9_DD1 (CPU_FTRS_POWER9 | CPU_FTR_POWER9_DD1)
+#define CPU_FTRS_POWER9_DD1 ((CPU_FTRS_POWER9 | CPU_FTR_POWER9_DD1) & \
+ (~CPU_FTR_SAO))
#define CPU_FTRS_CELL (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \
CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \
CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \
diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
index 183368e..99407cf 100644
--- a/arch/powerpc/kernel/module_64.c
+++ b/arch/powerpc/kernel/module_64.c
@@ -494,7 +494,17 @@
restore r2. */
static int restore_r2(u32 *instruction, struct module *me)
{
- if (is_early_mcount_callsite(instruction - 1))
+ u32 *prev_insn = instruction - 1;
+
+ if (is_early_mcount_callsite(prev_insn))
+ return 1;
+
+ /*
+ * Make sure the branch isn't a sibling call. Sibling calls aren't
+ * "link" branches and they don't return, so they don't need the r2
+ * restore afterwards.
+ */
+ if (!instr_is_relative_link_branch(*prev_insn))
return 1;
if (*instruction != PPC_INST_NOP) {
diff --git a/arch/powerpc/kvm/book3s_64_mmu_host.c b/arch/powerpc/kvm/book3s_64_mmu_host.c
index a587e8f..4b4e927 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_host.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_host.c
@@ -177,12 +177,15 @@
ret = mmu_hash_ops.hpte_insert(hpteg, vpn, hpaddr, rflags, vflags,
hpsize, hpsize, MMU_SEGSIZE_256M);
- if (ret < 0) {
+ if (ret == -1) {
/* If we couldn't map a primary PTE, try a secondary */
hash = ~hash;
vflags ^= HPTE_V_SECONDARY;
attempt++;
goto map_again;
+ } else if (ret < 0) {
+ r = -EIO;
+ goto out_unlock;
} else {
trace_kvm_book3s_64_mmu_map(rflags, hpteg,
vpn, hpaddr, orig_pte);
diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c
index 826c541..e0d88d0 100644
--- a/arch/powerpc/kvm/book3s_pr.c
+++ b/arch/powerpc/kvm/book3s_pr.c
@@ -627,7 +627,11 @@
kvmppc_mmu_unmap_page(vcpu, &pte);
}
/* The guest's PTE is not mapped yet. Map on the host */
- kvmppc_mmu_map_page(vcpu, &pte, iswrite);
+ if (kvmppc_mmu_map_page(vcpu, &pte, iswrite) == -EIO) {
+ /* Exit KVM if mapping failed */
+ run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
+ return RESUME_HOST;
+ }
if (data)
vcpu->stat.sp_storage++;
else if (vcpu->arch.mmu.is_dcbz32(vcpu) &&
diff --git a/arch/powerpc/lib/code-patching.c b/arch/powerpc/lib/code-patching.c
index d5edbeb..753d591 100644
--- a/arch/powerpc/lib/code-patching.c
+++ b/arch/powerpc/lib/code-patching.c
@@ -95,6 +95,11 @@
return instr_is_branch_iform(instr) || instr_is_branch_bform(instr);
}
+int instr_is_relative_link_branch(unsigned int instr)
+{
+ return instr_is_relative_branch(instr) && (instr & BRANCH_SET_LINK);
+}
+
static unsigned long branch_iform_target(const unsigned int *instr)
{
signed long imm;
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
index d0b137d..9376e8e 100644
--- a/arch/powerpc/mm/fault.c
+++ b/arch/powerpc/mm/fault.c
@@ -294,7 +294,7 @@
* can result in fault, which will cause a deadlock when called with
* mmap_sem held
*/
- if (user_mode(regs))
+ if (!is_exec && user_mode(regs))
store_update_sp = store_updates_sp(regs);
if (user_mode(regs))
diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c
index a5d3ecd..035dfb6 100644
--- a/arch/powerpc/mm/hugetlbpage.c
+++ b/arch/powerpc/mm/hugetlbpage.c
@@ -765,6 +765,24 @@
if ((mmu_psize = shift_to_mmu_psize(shift)) < 0)
return -EINVAL;
+#ifdef CONFIG_PPC_BOOK3S_64
+ /*
+ * We need to make sure that for different page sizes reported by
+ * firmware we only add hugetlb support for page sizes that can be
+ * supported by linux page table layout.
+ * For now we have
+ * Radix: 2M
+ * Hash: 16M and 16G
+ */
+ if (radix_enabled()) {
+ if (mmu_psize != MMU_PAGE_2M)
+ return -EINVAL;
+ } else {
+ if (mmu_psize != MMU_PAGE_16M && mmu_psize != MMU_PAGE_16G)
+ return -EINVAL;
+ }
+#endif
+
BUG_ON(mmu_psize_defs[mmu_psize].shift != shift);
/* Return if huge page size has already been setup */
diff --git a/arch/powerpc/mm/tlb_nohash.c b/arch/powerpc/mm/tlb_nohash.c
index 050badc..0b50019 100644
--- a/arch/powerpc/mm/tlb_nohash.c
+++ b/arch/powerpc/mm/tlb_nohash.c
@@ -751,7 +751,7 @@
* avoid going over total available memory just in case...
*/
#ifdef CONFIG_PPC_FSL_BOOK3E
- if (mmu_has_feature(MMU_FTR_TYPE_FSL_E)) {
+ if (early_mmu_has_feature(MMU_FTR_TYPE_FSL_E)) {
unsigned long linear_sz;
unsigned int num_cams;
diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c
index 0fe98a5..be9d968 100644
--- a/arch/powerpc/net/bpf_jit_comp64.c
+++ b/arch/powerpc/net/bpf_jit_comp64.c
@@ -245,6 +245,7 @@
* goto out;
*/
PPC_LWZ(b2p[TMP_REG_1], b2p_bpf_array, offsetof(struct bpf_array, map.max_entries));
+ PPC_RLWINM(b2p_index, b2p_index, 0, 0, 31);
PPC_CMPLW(b2p_index, b2p[TMP_REG_1]);
PPC_BCC(COND_GE, out);
diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c
index 66e7227..85018a1 100644
--- a/arch/powerpc/platforms/pseries/cmm.c
+++ b/arch/powerpc/platforms/pseries/cmm.c
@@ -708,7 +708,7 @@
* Return value:
* 0 on success / other on failure
**/
-static int cmm_set_disable(const char *val, struct kernel_param *kp)
+static int cmm_set_disable(const char *val, const struct kernel_param *kp)
{
int disable = simple_strtoul(val, NULL, 10);
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index 29d8744..6257898 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -372,7 +372,7 @@
rc = kstrtobool(str, &enabled);
if (!rc && !enabled)
- S390_lowcore.machine_flags &= ~MACHINE_HAS_TOPOLOGY;
+ S390_lowcore.machine_flags &= ~MACHINE_FLAG_TOPOLOGY;
return rc;
}
early_param("topology", topology_setup);
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 5ba494e..a70ff09 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -1601,6 +1601,7 @@
/* we still need the basic sca for the ipte control */
vcpu->arch.sie_block->scaoh = (__u32)(((__u64)sca) >> 32);
vcpu->arch.sie_block->scaol = (__u32)(__u64)sca;
+ return;
}
read_lock(&vcpu->kvm->arch.sca_lock);
if (vcpu->kvm->arch.use_esca) {
diff --git a/arch/sh/boards/mach-se/770x/setup.c b/arch/sh/boards/mach-se/770x/setup.c
index 658326f..5e02676 100644
--- a/arch/sh/boards/mach-se/770x/setup.c
+++ b/arch/sh/boards/mach-se/770x/setup.c
@@ -8,6 +8,7 @@
*/
#include <linux/init.h>
#include <linux/platform_device.h>
+#include <linux/sh_eth.h>
#include <mach-se/mach/se.h>
#include <mach-se/mach/mrshpc.h>
#include <asm/machvec.h>
@@ -114,6 +115,11 @@
#if defined(CONFIG_CPU_SUBTYPE_SH7710) ||\
defined(CONFIG_CPU_SUBTYPE_SH7712)
/* SH771X Ethernet driver */
+static struct sh_eth_plat_data sh_eth_plat = {
+ .phy = PHY_ID,
+ .phy_interface = PHY_INTERFACE_MODE_MII,
+};
+
static struct resource sh_eth0_resources[] = {
[0] = {
.start = SH_ETH0_BASE,
@@ -131,7 +137,7 @@
.name = "sh771x-ether",
.id = 0,
.dev = {
- .platform_data = PHY_ID,
+ .platform_data = &sh_eth_plat,
},
.num_resources = ARRAY_SIZE(sh_eth0_resources),
.resource = sh_eth0_resources,
@@ -154,7 +160,7 @@
.name = "sh771x-ether",
.id = 1,
.dev = {
- .platform_data = PHY_ID,
+ .platform_data = &sh_eth_plat,
},
.num_resources = ARRAY_SIZE(sh_eth1_resources),
.resource = sh_eth1_resources,
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index ea24114..93b170e 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -193,6 +193,15 @@
LDFLAGS := -m elf_$(UTS_MACHINE)
+#
+# The 64-bit kernel must be aligned to 2MB. Pass -z max-page-size=0x200000 to
+# the linker to force 2MB page size regardless of the default page size used
+# by the linker.
+#
+ifdef CONFIG_X86_64
+LDFLAGS += $(call ld-option, -z max-page-size=0x200000)
+endif
+
# Speed up the build
KBUILD_CFLAGS += -pipe
# Workaround for a gcc prelease that unfortunately was shipped in a suse release
@@ -205,7 +214,10 @@
# Avoid indirect branches in kernel to deal with Spectre
ifdef CONFIG_RETPOLINE
- RETPOLINE_CFLAGS += $(call cc-option,-mindirect-branch=thunk-extern -mindirect-branch-register)
+ RETPOLINE_CFLAGS_GCC := -mindirect-branch=thunk-extern -mindirect-branch-register
+ RETPOLINE_CFLAGS_CLANG := -mretpoline-external-thunk
+
+ RETPOLINE_CFLAGS += $(call cc-option,$(RETPOLINE_CFLAGS_GCC),$(call cc-option,$(RETPOLINE_CFLAGS_CLANG)))
ifneq ($(RETPOLINE_CFLAGS),)
KBUILD_CFLAGS += $(RETPOLINE_CFLAGS) -DRETPOLINE
endif
diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
index 6de58f1..eb5ff66 100644
--- a/arch/x86/boot/compressed/kaslr.c
+++ b/arch/x86/boot/compressed/kaslr.c
@@ -460,10 +460,17 @@
add_identity_map(random_addr, output_size);
*output = random_addr;
}
+
+ /*
+ * This loads the identity mapping page table.
+ * This should only be done if a new physical address
+ * is found for the kernel, otherwise we should keep
+ * the old page table to make it be like the "nokaslr"
+ * case.
+ */
+ finalize_identity_maps();
}
- /* This actually loads the identity pagetable on x86_64. */
- finalize_identity_maps();
/* Pick random virtual address starting from LOAD_PHYSICAL_ADDR. */
if (IS_ENABLED(CONFIG_X86_64))
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c
index 5955954..4884098 100644
--- a/arch/x86/boot/compressed/misc.c
+++ b/arch/x86/boot/compressed/misc.c
@@ -299,6 +299,10 @@
switch (phdr->p_type) {
case PT_LOAD:
+#ifdef CONFIG_X86_64
+ if ((phdr->p_align % 0x200000) != 0)
+ error("Alignment of LOAD segment isn't multiple of 2MB");
+#endif
#ifdef CONFIG_RELOCATABLE
dest = output;
dest += (phdr->p_paddr - LOAD_PHYSICAL_ADDR);
diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S
index f5434b4..a76dc73 100644
--- a/arch/x86/entry/entry_32.S
+++ b/arch/x86/entry/entry_32.S
@@ -237,8 +237,7 @@
* exist, overwrite the RSB with entries which capture
* speculative execution to prevent attack.
*/
- /* Clobbers %ebx */
- FILL_RETURN_BUFFER RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW
+ FILL_RETURN_BUFFER %ebx, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW
#endif
/* restore callee-saved registers */
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index 8be2aaa..3a31fd4 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -176,13 +176,26 @@
pushq %r8 /* pt_regs->r8 */
pushq %r9 /* pt_regs->r9 */
pushq %r10 /* pt_regs->r10 */
+ /*
+ * Clear extra registers that a speculation attack might
+ * otherwise want to exploit. Interleave XOR with PUSH
+ * for better uop scheduling:
+ */
+ xorq %r10, %r10 /* nospec r10 */
pushq %r11 /* pt_regs->r11 */
+ xorq %r11, %r11 /* nospec r11 */
pushq %rbx /* pt_regs->rbx */
+ xorl %ebx, %ebx /* nospec rbx */
pushq %rbp /* pt_regs->rbp */
+ xorl %ebp, %ebp /* nospec rbp */
pushq %r12 /* pt_regs->r12 */
+ xorq %r12, %r12 /* nospec r12 */
pushq %r13 /* pt_regs->r13 */
+ xorq %r13, %r13 /* nospec r13 */
pushq %r14 /* pt_regs->r14 */
+ xorq %r14, %r14 /* nospec r14 */
pushq %r15 /* pt_regs->r15 */
+ xorq %r15, %r15 /* nospec r15 */
/* IRQs are off. */
movq %rsp, %rdi
@@ -318,8 +331,7 @@
* exist, overwrite the RSB with entries which capture
* speculative execution to prevent attack.
*/
- /* Clobbers %rbx */
- FILL_RETURN_BUFFER RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW
+ FILL_RETURN_BUFFER %r12, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW
#endif
/* restore callee-saved registers */
@@ -926,7 +938,7 @@
#endif /* CONFIG_HYPERV */
idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK
-idtentry int3 do_int3 has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK
+idtentry int3 do_int3 has_error_code=0
idtentry stack_segment do_stack_segment has_error_code=1
#ifdef CONFIG_XEN
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 0bd0c1c..6f353a8 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -3025,7 +3025,7 @@
X86_CONFIG(.event=0xc0, .umask=0x01)) {
if (left < 128)
left = 128;
- left &= ~0x3fu;
+ left &= ~0x3fULL;
}
return left;
}
diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c
index afe8024..6bc3694 100644
--- a/arch/x86/events/intel/uncore_snbep.c
+++ b/arch/x86/events/intel/uncore_snbep.c
@@ -3522,24 +3522,27 @@
NULL,
};
+/*
+ * To determine the number of CHAs, it should read bits 27:0 in the CAPID6
+ * register which located at Device 30, Function 3, Offset 0x9C. PCI ID 0x2083.
+ */
+#define SKX_CAPID6 0x9c
+#define SKX_CHA_BIT_MASK GENMASK(27, 0)
+
static int skx_count_chabox(void)
{
- struct pci_dev *chabox_dev = NULL;
- int bus, count = 0;
+ struct pci_dev *dev = NULL;
+ u32 val = 0;
- while (1) {
- chabox_dev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x208d, chabox_dev);
- if (!chabox_dev)
- break;
- if (count == 0)
- bus = chabox_dev->bus->number;
- if (bus != chabox_dev->bus->number)
- break;
- count++;
- }
+ dev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x2083, dev);
+ if (!dev)
+ goto out;
- pci_dev_put(chabox_dev);
- return count;
+ pci_read_config_dword(dev, SKX_CAPID6, &val);
+ val &= SKX_CHA_BIT_MASK;
+out:
+ pci_dev_put(dev);
+ return hweight32(val);
}
void skx_uncore_cpu_init(void)
@@ -3566,7 +3569,7 @@
};
static struct attribute *skx_upi_uncore_formats_attr[] = {
- &format_attr_event_ext.attr,
+ &format_attr_event.attr,
&format_attr_umask_ext.attr,
&format_attr_edge.attr,
&format_attr_inv.attr,
diff --git a/arch/x86/include/asm/apm.h b/arch/x86/include/asm/apm.h
index 93eebc63..46e40ae 100644
--- a/arch/x86/include/asm/apm.h
+++ b/arch/x86/include/asm/apm.h
@@ -6,6 +6,8 @@
#ifndef _ASM_X86_MACH_DEFAULT_APM_H
#define _ASM_X86_MACH_DEFAULT_APM_H
+#include <asm/nospec-branch.h>
+
#ifdef APM_ZERO_SEGS
# define APM_DO_ZERO_SEGS \
"pushl %%ds\n\t" \
@@ -31,6 +33,7 @@
* N.B. We do NOT need a cld after the BIOS call
* because we always save and restore the flags.
*/
+ firmware_restrict_branch_speculation_start();
__asm__ __volatile__(APM_DO_ZERO_SEGS
"pushl %%edi\n\t"
"pushl %%ebp\n\t"
@@ -43,6 +46,7 @@
"=S" (*esi)
: "a" (func), "b" (ebx_in), "c" (ecx_in)
: "memory", "cc");
+ firmware_restrict_branch_speculation_end();
}
static inline bool apm_bios_call_simple_asm(u32 func, u32 ebx_in,
@@ -55,6 +59,7 @@
* N.B. We do NOT need a cld after the BIOS call
* because we always save and restore the flags.
*/
+ firmware_restrict_branch_speculation_start();
__asm__ __volatile__(APM_DO_ZERO_SEGS
"pushl %%edi\n\t"
"pushl %%ebp\n\t"
@@ -67,6 +72,7 @@
"=S" (si)
: "a" (func), "b" (ebx_in), "c" (ecx_in)
: "memory", "cc");
+ firmware_restrict_branch_speculation_end();
return error;
}
diff --git a/arch/x86/include/asm/asm-prototypes.h b/arch/x86/include/asm/asm-prototypes.h
index 1666542..5a25ada 100644
--- a/arch/x86/include/asm/asm-prototypes.h
+++ b/arch/x86/include/asm/asm-prototypes.h
@@ -37,7 +37,4 @@
INDIRECT_THUNK(si)
INDIRECT_THUNK(di)
INDIRECT_THUNK(bp)
-asmlinkage void __fill_rsb(void);
-asmlinkage void __clear_rsb(void);
-
#endif /* CONFIG_RETPOLINE */
diff --git a/arch/x86/include/asm/asm.h b/arch/x86/include/asm/asm.h
index 08684b3..8d8c24f 100644
--- a/arch/x86/include/asm/asm.h
+++ b/arch/x86/include/asm/asm.h
@@ -129,6 +129,7 @@
#endif
#ifndef __ASSEMBLY__
+#ifndef __BPF__
/*
* This output constraint should be used for any inline asm which has a "call"
* instruction. Otherwise the asm may be inserted before the frame pointer
@@ -138,5 +139,6 @@
register unsigned long current_stack_pointer asm(_ASM_SP);
#define ASM_CALL_CONSTRAINT "+r" (current_stack_pointer)
#endif
+#endif
#endif /* _ASM_X86_ASM_H */
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 8eb23f5..a248531 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -203,6 +203,7 @@
#define X86_FEATURE_KAISER ( 7*32+31) /* CONFIG_PAGE_TABLE_ISOLATION w/o nokaiser */
#define X86_FEATURE_USE_IBPB ( 7*32+21) /* "" Indirect Branch Prediction Barrier enabled */
+#define X86_FEATURE_USE_IBRS_FW ( 7*32+22) /* "" Use IBRS during runtime firmware calls */
/* Virtualization flags: Linux defined, word 8 */
#define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */
@@ -301,6 +302,7 @@
/* Intel-defined CPU features, CPUID level 0x00000007:0 (EDX), word 18 */
#define X86_FEATURE_AVX512_4VNNIW (18*32+ 2) /* AVX-512 Neural Network Instructions */
#define X86_FEATURE_AVX512_4FMAPS (18*32+ 3) /* AVX-512 Multiply Accumulation Single precision */
+#define X86_FEATURE_PCONFIG (18*32+18) /* Intel PCONFIG */
#define X86_FEATURE_SPEC_CTRL (18*32+26) /* "" Speculation Control (IBRS + IBPB) */
#define X86_FEATURE_INTEL_STIBP (18*32+27) /* "" Single Thread Indirect Branch Predictors */
#define X86_FEATURE_ARCH_CAPABILITIES (18*32+29) /* IA32_ARCH_CAPABILITIES MSR (Intel) */
diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
index 389d700..9df22bb 100644
--- a/arch/x86/include/asm/efi.h
+++ b/arch/x86/include/asm/efi.h
@@ -5,6 +5,7 @@
#include <asm/pgtable.h>
#include <asm/processor-flags.h>
#include <asm/tlb.h>
+#include <asm/nospec-branch.h>
/*
* We map the EFI regions needed for runtime services non-contiguously,
@@ -35,8 +36,18 @@
extern unsigned long asmlinkage efi_call_phys(void *, ...);
-#define arch_efi_call_virt_setup() kernel_fpu_begin()
-#define arch_efi_call_virt_teardown() kernel_fpu_end()
+#define arch_efi_call_virt_setup() \
+({ \
+ kernel_fpu_begin(); \
+ firmware_restrict_branch_speculation_start(); \
+})
+
+#define arch_efi_call_virt_teardown() \
+({ \
+ firmware_restrict_branch_speculation_end(); \
+ kernel_fpu_end(); \
+})
+
/*
* Wrap all the virtual calls in a way that forces the parameters on the stack.
@@ -72,6 +83,7 @@
efi_sync_low_kernel_mappings(); \
preempt_disable(); \
__kernel_fpu_begin(); \
+ firmware_restrict_branch_speculation_start(); \
\
if (efi_scratch.use_pgd) { \
efi_scratch.prev_cr3 = read_cr3(); \
@@ -90,6 +102,7 @@
__flush_tlb_all(); \
} \
\
+ firmware_restrict_branch_speculation_end(); \
__kernel_fpu_end(); \
preempt_enable(); \
})
diff --git a/arch/x86/include/asm/mmu.h b/arch/x86/include/asm/mmu.h
index 8b272a0..e2e0934 100644
--- a/arch/x86/include/asm/mmu.h
+++ b/arch/x86/include/asm/mmu.h
@@ -3,12 +3,18 @@
#include <linux/spinlock.h>
#include <linux/mutex.h>
+#include <linux/atomic.h>
/*
- * The x86 doesn't have a mmu context, but
- * we put the segment information here.
+ * x86 has arch-specific MMU state beyond what lives in mm_struct.
*/
typedef struct {
+ /*
+ * ctx_id uniquely identifies this mm_struct. A ctx_id will never
+ * be reused, and zero is not a valid ctx_id.
+ */
+ u64 ctx_id;
+
#ifdef CONFIG_MODIFY_LDT_SYSCALL
struct ldt_struct *ldt;
#endif
@@ -33,6 +39,11 @@
#endif
} mm_context_t;
+#define INIT_MM_CONTEXT(mm) \
+ .context = { \
+ .ctx_id = 1, \
+ }
+
void leave_mm(int cpu);
#endif /* _ASM_X86_MMU_H */
diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h
index d23e355..5a295bb 100644
--- a/arch/x86/include/asm/mmu_context.h
+++ b/arch/x86/include/asm/mmu_context.h
@@ -12,6 +12,9 @@
#include <asm/tlbflush.h>
#include <asm/paravirt.h>
#include <asm/mpx.h>
+
+extern atomic64_t last_mm_ctx_id;
+
#ifndef CONFIG_PARAVIRT
static inline void paravirt_activate_mm(struct mm_struct *prev,
struct mm_struct *next)
@@ -106,6 +109,8 @@
static inline int init_new_context(struct task_struct *tsk,
struct mm_struct *mm)
{
+ mm->context.ctx_id = atomic64_inc_return(&last_mm_ctx_id);
+
#ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS
if (cpu_feature_enabled(X86_FEATURE_OSPKE)) {
/* pkey 0 is the default and always allocated */
diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h
index 76b0585..f928ad9 100644
--- a/arch/x86/include/asm/nospec-branch.h
+++ b/arch/x86/include/asm/nospec-branch.h
@@ -8,6 +8,50 @@
#include <asm/cpufeatures.h>
#include <asm/msr-index.h>
+/*
+ * Fill the CPU return stack buffer.
+ *
+ * Each entry in the RSB, if used for a speculative 'ret', contains an
+ * infinite 'pause; lfence; jmp' loop to capture speculative execution.
+ *
+ * This is required in various cases for retpoline and IBRS-based
+ * mitigations for the Spectre variant 2 vulnerability. Sometimes to
+ * eliminate potentially bogus entries from the RSB, and sometimes
+ * purely to ensure that it doesn't get empty, which on some CPUs would
+ * allow predictions from other (unwanted!) sources to be used.
+ *
+ * We define a CPP macro such that it can be used from both .S files and
+ * inline assembly. It's possible to do a .macro and then include that
+ * from C via asm(".include <asm/nospec-branch.h>") but let's not go there.
+ */
+
+#define RSB_CLEAR_LOOPS 32 /* To forcibly overwrite all entries */
+#define RSB_FILL_LOOPS 16 /* To avoid underflow */
+
+/*
+ * Google experimented with loop-unrolling and this turned out to be
+ * the optimal version — two calls, each with their own speculation
+ * trap should their return address end up getting used, in a loop.
+ */
+#define __FILL_RETURN_BUFFER(reg, nr, sp) \
+ mov $(nr/2), reg; \
+771: \
+ call 772f; \
+773: /* speculation trap */ \
+ pause; \
+ lfence; \
+ jmp 773b; \
+772: \
+ call 774f; \
+775: /* speculation trap */ \
+ pause; \
+ lfence; \
+ jmp 775b; \
+774: \
+ dec reg; \
+ jnz 771b; \
+ add $(BITS_PER_LONG/8) * nr, sp;
+
#ifdef __ASSEMBLY__
/*
@@ -24,6 +68,18 @@
.endm
/*
+ * This should be used immediately before an indirect jump/call. It tells
+ * objtool the subsequent indirect jump/call is vouched safe for retpoline
+ * builds.
+ */
+.macro ANNOTATE_RETPOLINE_SAFE
+ .Lannotate_\@:
+ .pushsection .discard.retpoline_safe
+ _ASM_PTR .Lannotate_\@
+ .popsection
+.endm
+
+/*
* These are the bare retpoline primitives for indirect jmp and call.
* Do not use these directly; they only exist to make the ALTERNATIVE
* invocation below less ugly.
@@ -59,9 +115,9 @@
.macro JMP_NOSPEC reg:req
#ifdef CONFIG_RETPOLINE
ANNOTATE_NOSPEC_ALTERNATIVE
- ALTERNATIVE_2 __stringify(jmp *\reg), \
+ ALTERNATIVE_2 __stringify(ANNOTATE_RETPOLINE_SAFE; jmp *\reg), \
__stringify(RETPOLINE_JMP \reg), X86_FEATURE_RETPOLINE, \
- __stringify(lfence; jmp *\reg), X86_FEATURE_RETPOLINE_AMD
+ __stringify(lfence; ANNOTATE_RETPOLINE_SAFE; jmp *\reg), X86_FEATURE_RETPOLINE_AMD
#else
jmp *\reg
#endif
@@ -70,18 +126,25 @@
.macro CALL_NOSPEC reg:req
#ifdef CONFIG_RETPOLINE
ANNOTATE_NOSPEC_ALTERNATIVE
- ALTERNATIVE_2 __stringify(call *\reg), \
+ ALTERNATIVE_2 __stringify(ANNOTATE_RETPOLINE_SAFE; call *\reg), \
__stringify(RETPOLINE_CALL \reg), X86_FEATURE_RETPOLINE,\
- __stringify(lfence; call *\reg), X86_FEATURE_RETPOLINE_AMD
+ __stringify(lfence; ANNOTATE_RETPOLINE_SAFE; call *\reg), X86_FEATURE_RETPOLINE_AMD
#else
call *\reg
#endif
.endm
-/* This clobbers the BX register */
-.macro FILL_RETURN_BUFFER nr:req ftr:req
+ /*
+ * A simpler FILL_RETURN_BUFFER macro. Don't make people use the CPP
+ * monstrosity above, manually.
+ */
+.macro FILL_RETURN_BUFFER reg:req nr:req ftr:req
#ifdef CONFIG_RETPOLINE
- ALTERNATIVE "", "call __clear_rsb", \ftr
+ ANNOTATE_NOSPEC_ALTERNATIVE
+ ALTERNATIVE "jmp .Lskip_rsb_\@", \
+ __stringify(__FILL_RETURN_BUFFER(\reg,\nr,%_ASM_SP)) \
+ \ftr
+.Lskip_rsb_\@:
#endif
.endm
@@ -93,6 +156,12 @@
".long 999b - .\n\t" \
".popsection\n\t"
+#define ANNOTATE_RETPOLINE_SAFE \
+ "999:\n\t" \
+ ".pushsection .discard.retpoline_safe\n\t" \
+ _ASM_PTR " 999b\n\t" \
+ ".popsection\n\t"
+
#if defined(CONFIG_X86_64) && defined(RETPOLINE)
/*
@@ -102,6 +171,7 @@
# define CALL_NOSPEC \
ANNOTATE_NOSPEC_ALTERNATIVE \
ALTERNATIVE( \
+ ANNOTATE_RETPOLINE_SAFE \
"call *%[thunk_target]\n", \
"call __x86_indirect_thunk_%V[thunk_target]\n", \
X86_FEATURE_RETPOLINE)
@@ -113,7 +183,10 @@
* otherwise we'll run out of registers. We don't care about CET
* here, anyway.
*/
-# define CALL_NOSPEC ALTERNATIVE("call *%[thunk_target]\n", \
+# define CALL_NOSPEC \
+ ALTERNATIVE( \
+ ANNOTATE_RETPOLINE_SAFE \
+ "call *%[thunk_target]\n", \
" jmp 904f;\n" \
" .align 16\n" \
"901: call 903f;\n" \
@@ -156,25 +229,90 @@
static inline void vmexit_fill_RSB(void)
{
#ifdef CONFIG_RETPOLINE
- alternative_input("",
- "call __fill_rsb",
- X86_FEATURE_RETPOLINE,
- ASM_NO_INPUT_CLOBBER(_ASM_BX, "memory"));
+ unsigned long loops;
+
+ asm volatile (ANNOTATE_NOSPEC_ALTERNATIVE
+ ALTERNATIVE("jmp 910f",
+ __stringify(__FILL_RETURN_BUFFER(%0, RSB_CLEAR_LOOPS, %1)),
+ X86_FEATURE_RETPOLINE)
+ "910:"
+ : "=r" (loops), ASM_CALL_CONSTRAINT
+ : : "memory" );
#endif
}
+#define alternative_msr_write(_msr, _val, _feature) \
+ asm volatile(ALTERNATIVE("", \
+ "movl %[msr], %%ecx\n\t" \
+ "movl %[val], %%eax\n\t" \
+ "movl $0, %%edx\n\t" \
+ "wrmsr", \
+ _feature) \
+ : : [msr] "i" (_msr), [val] "i" (_val) \
+ : "eax", "ecx", "edx", "memory")
+
static inline void indirect_branch_prediction_barrier(void)
{
- asm volatile(ALTERNATIVE("",
- "movl %[msr], %%ecx\n\t"
- "movl %[val], %%eax\n\t"
- "movl $0, %%edx\n\t"
- "wrmsr",
- X86_FEATURE_USE_IBPB)
- : : [msr] "i" (MSR_IA32_PRED_CMD),
- [val] "i" (PRED_CMD_IBPB)
- : "eax", "ecx", "edx", "memory");
+ alternative_msr_write(MSR_IA32_PRED_CMD, PRED_CMD_IBPB,
+ X86_FEATURE_USE_IBPB);
}
+/*
+ * With retpoline, we must use IBRS to restrict branch prediction
+ * before calling into firmware.
+ *
+ * (Implemented as CPP macros due to header hell.)
+ */
+#define firmware_restrict_branch_speculation_start() \
+do { \
+ preempt_disable(); \
+ alternative_msr_write(MSR_IA32_SPEC_CTRL, SPEC_CTRL_IBRS, \
+ X86_FEATURE_USE_IBRS_FW); \
+} while (0)
+
+#define firmware_restrict_branch_speculation_end() \
+do { \
+ alternative_msr_write(MSR_IA32_SPEC_CTRL, 0, \
+ X86_FEATURE_USE_IBRS_FW); \
+ preempt_enable(); \
+} while (0)
+
#endif /* __ASSEMBLY__ */
+
+/*
+ * Below is used in the eBPF JIT compiler and emits the byte sequence
+ * for the following assembly:
+ *
+ * With retpolines configured:
+ *
+ * callq do_rop
+ * spec_trap:
+ * pause
+ * lfence
+ * jmp spec_trap
+ * do_rop:
+ * mov %rax,(%rsp)
+ * retq
+ *
+ * Without retpolines configured:
+ *
+ * jmp *%rax
+ */
+#ifdef CONFIG_RETPOLINE
+# define RETPOLINE_RAX_BPF_JIT_SIZE 17
+# define RETPOLINE_RAX_BPF_JIT() \
+ EMIT1_off32(0xE8, 7); /* callq do_rop */ \
+ /* spec_trap: */ \
+ EMIT2(0xF3, 0x90); /* pause */ \
+ EMIT3(0x0F, 0xAE, 0xE8); /* lfence */ \
+ EMIT2(0xEB, 0xF9); /* jmp spec_trap */ \
+ /* do_rop: */ \
+ EMIT4(0x48, 0x89, 0x04, 0x24); /* mov %rax,(%rsp) */ \
+ EMIT1(0xC3); /* retq */
+#else
+# define RETPOLINE_RAX_BPF_JIT_SIZE 2
+# define RETPOLINE_RAX_BPF_JIT() \
+ EMIT2(0xFF, 0xE0); /* jmp *%rax */
+#endif
+
#endif /* _ASM_X86_NOSPEC_BRANCH_H_ */
diff --git a/arch/x86/include/asm/paravirt.h b/arch/x86/include/asm/paravirt.h
index ce93281..24af8b1 100644
--- a/arch/x86/include/asm/paravirt.h
+++ b/arch/x86/include/asm/paravirt.h
@@ -6,6 +6,7 @@
#ifdef CONFIG_PARAVIRT
#include <asm/pgtable_types.h>
#include <asm/asm.h>
+#include <asm/nospec-branch.h>
#include <asm/paravirt_types.h>
@@ -869,23 +870,27 @@
#define INTERRUPT_RETURN \
PARA_SITE(PARA_PATCH(pv_cpu_ops, PV_CPU_iret), CLBR_NONE, \
- jmp PARA_INDIRECT(pv_cpu_ops+PV_CPU_iret))
+ ANNOTATE_RETPOLINE_SAFE; \
+ jmp PARA_INDIRECT(pv_cpu_ops+PV_CPU_iret);)
#define DISABLE_INTERRUPTS(clobbers) \
PARA_SITE(PARA_PATCH(pv_irq_ops, PV_IRQ_irq_disable), clobbers, \
PV_SAVE_REGS(clobbers | CLBR_CALLEE_SAVE); \
+ ANNOTATE_RETPOLINE_SAFE; \
call PARA_INDIRECT(pv_irq_ops+PV_IRQ_irq_disable); \
PV_RESTORE_REGS(clobbers | CLBR_CALLEE_SAVE);)
#define ENABLE_INTERRUPTS(clobbers) \
PARA_SITE(PARA_PATCH(pv_irq_ops, PV_IRQ_irq_enable), clobbers, \
PV_SAVE_REGS(clobbers | CLBR_CALLEE_SAVE); \
+ ANNOTATE_RETPOLINE_SAFE; \
call PARA_INDIRECT(pv_irq_ops+PV_IRQ_irq_enable); \
PV_RESTORE_REGS(clobbers | CLBR_CALLEE_SAVE);)
#ifdef CONFIG_X86_32
#define GET_CR0_INTO_EAX \
push %ecx; push %edx; \
+ ANNOTATE_RETPOLINE_SAFE; \
call PARA_INDIRECT(pv_cpu_ops+PV_CPU_read_cr0); \
pop %edx; pop %ecx
#else /* !CONFIG_X86_32 */
@@ -907,11 +912,13 @@
*/
#define SWAPGS \
PARA_SITE(PARA_PATCH(pv_cpu_ops, PV_CPU_swapgs), CLBR_NONE, \
- call PARA_INDIRECT(pv_cpu_ops+PV_CPU_swapgs) \
+ ANNOTATE_RETPOLINE_SAFE; \
+ call PARA_INDIRECT(pv_cpu_ops+PV_CPU_swapgs); \
)
#define GET_CR2_INTO_RAX \
- call PARA_INDIRECT(pv_mmu_ops+PV_MMU_read_cr2)
+ ANNOTATE_RETPOLINE_SAFE; \
+ call PARA_INDIRECT(pv_mmu_ops+PV_MMU_read_cr2);
#define PARAVIRT_ADJUST_EXCEPTION_FRAME \
PARA_SITE(PARA_PATCH(pv_irq_ops, PV_IRQ_adjust_exception_frame), \
@@ -921,7 +928,8 @@
#define USERGS_SYSRET64 \
PARA_SITE(PARA_PATCH(pv_cpu_ops, PV_CPU_usergs_sysret64), \
CLBR_NONE, \
- jmp PARA_INDIRECT(pv_cpu_ops+PV_CPU_usergs_sysret64))
+ ANNOTATE_RETPOLINE_SAFE; \
+ jmp PARA_INDIRECT(pv_cpu_ops+PV_CPU_usergs_sysret64);)
#endif /* CONFIG_X86_32 */
#endif /* __ASSEMBLY__ */
diff --git a/arch/x86/include/asm/paravirt_types.h b/arch/x86/include/asm/paravirt_types.h
index bc5902c..98dbad6 100644
--- a/arch/x86/include/asm/paravirt_types.h
+++ b/arch/x86/include/asm/paravirt_types.h
@@ -42,6 +42,7 @@
#include <asm/desc_defs.h>
#include <asm/kmap_types.h>
#include <asm/pgtable_types.h>
+#include <asm/nospec-branch.h>
struct page;
struct thread_struct;
@@ -391,7 +392,9 @@
* offset into the paravirt_patch_template structure, and can therefore be
* freely converted back into a structure offset.
*/
-#define PARAVIRT_CALL "call *%c[paravirt_opptr];"
+#define PARAVIRT_CALL \
+ ANNOTATE_RETPOLINE_SAFE \
+ "call *%c[paravirt_opptr];"
/*
* These macros are intended to wrap calls through one of the paravirt
diff --git a/arch/x86/include/asm/reboot.h b/arch/x86/include/asm/reboot.h
index 2cb1cc2..fc62ba8 100644
--- a/arch/x86/include/asm/reboot.h
+++ b/arch/x86/include/asm/reboot.h
@@ -15,6 +15,7 @@
};
extern struct machine_ops machine_ops;
+extern int crashing_cpu;
void native_machine_crash_shutdown(struct pt_regs *regs);
void native_machine_shutdown(void);
diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h
index 94146f6..99185a0 100644
--- a/arch/x86/include/asm/tlbflush.h
+++ b/arch/x86/include/asm/tlbflush.h
@@ -68,6 +68,8 @@
struct tlb_state {
struct mm_struct *active_mm;
int state;
+ /* last user mm's ctx id */
+ u64 last_ctx_id;
/*
* Access to this CR4 shadow and to H/W CR4 is protected by
diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index 6899cf1..9cbfbef 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -309,6 +309,7 @@
#define INTR_TYPE_NMI_INTR (2 << 8) /* NMI */
#define INTR_TYPE_HARD_EXCEPTION (3 << 8) /* processor exception */
#define INTR_TYPE_SOFT_INTR (4 << 8) /* software interrupt */
+#define INTR_TYPE_PRIV_SW_EXCEPTION (5 << 8) /* ICE breakpoint - undocumented */
#define INTR_TYPE_SOFT_EXCEPTION (6 << 8) /* software exception */
/* GUEST_INTERRUPTIBILITY_INFO flags. */
diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c
index b5229ab..4922ab6 100644
--- a/arch/x86/kernel/apic/vector.c
+++ b/arch/x86/kernel/apic/vector.c
@@ -93,8 +93,12 @@
return NULL;
}
-static void free_apic_chip_data(struct apic_chip_data *data)
+static void free_apic_chip_data(unsigned int virq, struct apic_chip_data *data)
{
+#ifdef CONFIG_X86_IO_APIC
+ if (virq < nr_legacy_irqs())
+ legacy_irq_data[virq] = NULL;
+#endif
if (data) {
free_cpumask_var(data->domain);
free_cpumask_var(data->old_domain);
@@ -318,11 +322,7 @@
apic_data = irq_data->chip_data;
irq_domain_reset_irq_data(irq_data);
raw_spin_unlock_irqrestore(&vector_lock, flags);
- free_apic_chip_data(apic_data);
-#ifdef CONFIG_X86_IO_APIC
- if (virq + i < nr_legacy_irqs())
- legacy_irq_data[virq + i] = NULL;
-#endif
+ free_apic_chip_data(virq + i, apic_data);
}
}
}
@@ -363,7 +363,7 @@
err = assign_irq_vector_policy(virq + i, node, data, info);
if (err) {
irq_data->chip_data = NULL;
- free_apic_chip_data(data);
+ free_apic_chip_data(virq + i, data);
goto error;
}
}
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
index baddc9e..b8b0b6e 100644
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -299,6 +299,15 @@
setup_force_cpu_cap(X86_FEATURE_USE_IBPB);
pr_info("Spectre v2 mitigation: Enabling Indirect Branch Prediction Barrier\n");
}
+
+ /*
+ * Retpoline means the kernel is safe because it has no indirect
+ * branches. But firmware isn't, so use IBRS to protect that.
+ */
+ if (boot_cpu_has(X86_FEATURE_IBRS)) {
+ setup_force_cpu_cap(X86_FEATURE_USE_IBRS_FW);
+ pr_info("Enabling Restricted Speculation for firmware calls\n");
+ }
}
#undef pr_fmt
@@ -325,8 +334,9 @@
if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
return sprintf(buf, "Not affected\n");
- return sprintf(buf, "%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
+ return sprintf(buf, "%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
boot_cpu_has(X86_FEATURE_USE_IBPB) ? ", IBPB" : "",
+ boot_cpu_has(X86_FEATURE_USE_IBRS_FW) ? ", IBRS_FW" : "",
spectre_v2_module_string());
}
#endif
diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index 6ed206b..8fb1d65 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -64,7 +64,7 @@
/*
* Early microcode releases for the Spectre v2 mitigation were broken.
* Information taken from;
- * - https://newsroom.intel.com/wp-content/uploads/sites/11/2018/01/microcode-update-guidance.pdf
+ * - https://newsroom.intel.com/wp-content/uploads/sites/11/2018/03/microcode-update-guidance.pdf
* - https://kb.vmware.com/s/article/52345
* - Microcode revisions observed in the wild
* - Release note from 20180108 microcode release
@@ -82,7 +82,6 @@
{ INTEL_FAM6_KABYLAKE_MOBILE, 0x09, 0x80 },
{ INTEL_FAM6_SKYLAKE_X, 0x03, 0x0100013e },
{ INTEL_FAM6_SKYLAKE_X, 0x04, 0x0200003c },
- { INTEL_FAM6_SKYLAKE_DESKTOP, 0x03, 0xc2 },
{ INTEL_FAM6_BROADWELL_CORE, 0x04, 0x28 },
{ INTEL_FAM6_BROADWELL_GT3E, 0x01, 0x1b },
{ INTEL_FAM6_BROADWELL_XEON_D, 0x02, 0x14 },
@@ -103,6 +102,13 @@
{
int i;
+ /*
+ * We know that the hypervisor lie to us on the microcode version so
+ * we may as well hope that it is running the correct version.
+ */
+ if (cpu_has(c, X86_FEATURE_HYPERVISOR))
+ return false;
+
for (i = 0; i < ARRAY_SIZE(spectre_bad_microcodes); i++) {
if (c->x86_model == spectre_bad_microcodes[i].model &&
c->x86_stepping == spectre_bad_microcodes[i].stepping)
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c
index fe5cd6e..7bbd50f 100644
--- a/arch/x86/kernel/cpu/mcheck/mce.c
+++ b/arch/x86/kernel/cpu/mcheck/mce.c
@@ -48,6 +48,7 @@
#include <asm/tlbflush.h>
#include <asm/mce.h>
#include <asm/msr.h>
+#include <asm/reboot.h>
#include "mce-internal.h"
@@ -61,6 +62,9 @@
smp_load_acquire(&(p)); \
})
+/* sysfs synchronization */
+static DEFINE_MUTEX(mce_sysfs_mutex);
+
#define CREATE_TRACE_POINTS
#include <trace/events/mce.h>
@@ -1078,9 +1082,22 @@
* on Intel.
*/
int lmce = 1;
+ int cpu = smp_processor_id();
- /* If this CPU is offline, just bail out. */
- if (cpu_is_offline(smp_processor_id())) {
+ /*
+ * Cases where we avoid rendezvous handler timeout:
+ * 1) If this CPU is offline.
+ *
+ * 2) If crashing_cpu was set, e.g. we're entering kdump and we need to
+ * skip those CPUs which remain looping in the 1st kernel - see
+ * crash_nmi_callback().
+ *
+ * Note: there still is a small window between kexec-ing and the new,
+ * kdump kernel establishing a new #MC handler where a broadcasted MCE
+ * might not get handled properly.
+ */
+ if (cpu_is_offline(cpu) ||
+ (crashing_cpu != -1 && crashing_cpu != cpu)) {
u64 mcgstatus;
mcgstatus = mce_rdmsrl(MSR_IA32_MCG_STATUS);
@@ -1678,6 +1695,25 @@
return 0;
}
+/*
+ * Init basic CPU features needed for early decoding of MCEs.
+ */
+static void __mcheck_cpu_init_early(struct cpuinfo_x86 *c)
+{
+ if (c->x86_vendor == X86_VENDOR_AMD) {
+ mce_flags.overflow_recov = !!cpu_has(c, X86_FEATURE_OVERFLOW_RECOV);
+ mce_flags.succor = !!cpu_has(c, X86_FEATURE_SUCCOR);
+ mce_flags.smca = !!cpu_has(c, X86_FEATURE_SMCA);
+
+ if (mce_flags.smca) {
+ msr_ops.ctl = smca_ctl_reg;
+ msr_ops.status = smca_status_reg;
+ msr_ops.addr = smca_addr_reg;
+ msr_ops.misc = smca_misc_reg;
+ }
+ }
+}
+
static void __mcheck_cpu_init_vendor(struct cpuinfo_x86 *c)
{
switch (c->x86_vendor) {
@@ -1687,21 +1723,7 @@
break;
case X86_VENDOR_AMD: {
- mce_flags.overflow_recov = !!cpu_has(c, X86_FEATURE_OVERFLOW_RECOV);
- mce_flags.succor = !!cpu_has(c, X86_FEATURE_SUCCOR);
- mce_flags.smca = !!cpu_has(c, X86_FEATURE_SMCA);
-
- /*
- * Install proper ops for Scalable MCA enabled processors
- */
- if (mce_flags.smca) {
- msr_ops.ctl = smca_ctl_reg;
- msr_ops.status = smca_status_reg;
- msr_ops.addr = smca_addr_reg;
- msr_ops.misc = smca_misc_reg;
- }
mce_amd_feature_init(c);
-
break;
}
@@ -1787,6 +1809,7 @@
machine_check_vector = do_machine_check;
+ __mcheck_cpu_init_early(c);
__mcheck_cpu_init_generic();
__mcheck_cpu_init_vendor(c);
__mcheck_cpu_init_clear_banks();
@@ -2308,6 +2331,7 @@
if (kstrtou64(buf, 0, &new) < 0)
return -EINVAL;
+ mutex_lock(&mce_sysfs_mutex);
if (mca_cfg.ignore_ce ^ !!new) {
if (new) {
/* disable ce features */
@@ -2320,6 +2344,8 @@
on_each_cpu(mce_enable_ce, (void *)1, 1);
}
}
+ mutex_unlock(&mce_sysfs_mutex);
+
return size;
}
@@ -2332,6 +2358,7 @@
if (kstrtou64(buf, 0, &new) < 0)
return -EINVAL;
+ mutex_lock(&mce_sysfs_mutex);
if (mca_cfg.cmci_disabled ^ !!new) {
if (new) {
/* disable cmci */
@@ -2343,6 +2370,8 @@
on_each_cpu(mce_enable_ce, NULL, 1);
}
}
+ mutex_unlock(&mce_sysfs_mutex);
+
return size;
}
@@ -2350,8 +2379,19 @@
struct device_attribute *attr,
const char *buf, size_t size)
{
- ssize_t ret = device_store_int(s, attr, buf, size);
+ unsigned long old_check_interval = check_interval;
+ ssize_t ret = device_store_ulong(s, attr, buf, size);
+
+ if (check_interval == old_check_interval)
+ return ret;
+
+ if (check_interval < 1)
+ check_interval = 1;
+
+ mutex_lock(&mce_sysfs_mutex);
mce_restart();
+ mutex_unlock(&mce_sysfs_mutex);
+
return ret;
}
diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S
index 67cd7c1..9d72cf5 100644
--- a/arch/x86/kernel/head_64.S
+++ b/arch/x86/kernel/head_64.S
@@ -22,6 +22,7 @@
#include <asm/nops.h>
#include "../entry/calling.h"
#include <asm/export.h>
+#include <asm/nospec-branch.h>
#ifdef CONFIG_PARAVIRT
#include <asm/asm-offsets.h>
@@ -200,6 +201,7 @@
/* Ensure I am executing from virtual addresses */
movq $1f, %rax
+ ANNOTATE_RETPOLINE_SAFE
jmp *%rax
1:
diff --git a/arch/x86/kernel/i8259.c b/arch/x86/kernel/i8259.c
index be22f5a..4e3b8a5 100644
--- a/arch/x86/kernel/i8259.c
+++ b/arch/x86/kernel/i8259.c
@@ -418,6 +418,7 @@
};
struct legacy_pic *legacy_pic = &default_legacy_pic;
+EXPORT_SYMBOL(legacy_pic);
static int __init i8259A_init_ops(void)
{
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
index b55d07b..b8d3f1b 100644
--- a/arch/x86/kernel/kprobes/core.c
+++ b/arch/x86/kernel/kprobes/core.c
@@ -199,6 +199,8 @@
return (opcode != 0x62 && opcode != 0x67);
case 0x70:
return 0; /* can't boost conditional jump */
+ case 0x90:
+ return opcode != 0x9a; /* can't boost call far */
case 0xc0:
/* can't boost software-interruptions */
return (0xc1 < opcode && opcode < 0xcc) || opcode == 0xcf;
@@ -407,6 +409,8 @@
{
int ret;
+ set_memory_rw((unsigned long)p->ainsn.insn & PAGE_MASK, 1);
+
/* Copy an instruction with recovering if other optprobe modifies it.*/
ret = __copy_instruction(p->ainsn.insn, p->addr);
if (!ret)
@@ -421,6 +425,8 @@
else
p->ainsn.boostable = -1;
+ set_memory_ro((unsigned long)p->ainsn.insn & PAGE_MASK, 1);
+
/* Check whether the instruction modifies Interrupt Flag or not */
p->ainsn.if_modifier = is_IF_modifier(p->ainsn.insn);
diff --git a/arch/x86/kernel/kprobes/opt.c b/arch/x86/kernel/kprobes/opt.c
index dc20da1c..fa671b9 100644
--- a/arch/x86/kernel/kprobes/opt.c
+++ b/arch/x86/kernel/kprobes/opt.c
@@ -371,6 +371,7 @@
}
buf = (u8 *)op->optinsn.insn;
+ set_memory_rw((unsigned long)buf & PAGE_MASK, 1);
/* Copy instructions into the out-of-line buffer */
ret = copy_optimized_instructions(buf + TMPL_END_IDX, op->kp.addr);
@@ -393,6 +394,8 @@
synthesize_reljump(buf + TMPL_END_IDX + op->optinsn.size,
(u8 *)op->kp.addr + op->optinsn.size);
+ set_memory_ro((unsigned long)buf & PAGE_MASK, 1);
+
flush_icache_range((unsigned long) buf,
(unsigned long) buf + TMPL_END_IDX +
op->optinsn.size + RELATIVEJUMP_SIZE);
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index 8c1f218..a5784a1 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -524,6 +524,7 @@
goto overflow;
break;
case R_X86_64_PC32:
+ case R_X86_64_PLT32:
value -= (u64)address;
*(u32 *)location = value;
break;
diff --git a/arch/x86/kernel/module.c b/arch/x86/kernel/module.c
index 477ae80..19977d2 100644
--- a/arch/x86/kernel/module.c
+++ b/arch/x86/kernel/module.c
@@ -171,19 +171,28 @@
case R_X86_64_NONE:
break;
case R_X86_64_64:
+ if (*(u64 *)loc != 0)
+ goto invalid_relocation;
*(u64 *)loc = val;
break;
case R_X86_64_32:
+ if (*(u32 *)loc != 0)
+ goto invalid_relocation;
*(u32 *)loc = val;
if (val != *(u32 *)loc)
goto overflow;
break;
case R_X86_64_32S:
+ if (*(s32 *)loc != 0)
+ goto invalid_relocation;
*(s32 *)loc = val;
if ((s64)val != *(s32 *)loc)
goto overflow;
break;
case R_X86_64_PC32:
+ case R_X86_64_PLT32:
+ if (*(u32 *)loc != 0)
+ goto invalid_relocation;
val -= (u64)loc;
*(u32 *)loc = val;
#if 0
@@ -199,6 +208,11 @@
}
return 0;
+invalid_relocation:
+ pr_err("x86/modules: Skipping invalid relocation target, existing value is nonzero for type %d, loc %p, val %Lx\n",
+ (int)ELF64_R_TYPE(rel[i].r_info), loc, val);
+ return -ENOEXEC;
+
overflow:
pr_err("overflow in relocation type %d val %Lx\n",
(int)ELF64_R_TYPE(rel[i].r_info), val);
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index ce020a6..03f21db 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -769,10 +769,11 @@
#endif
+/* This is the CPU performing the emergency shutdown work. */
+int crashing_cpu = -1;
+
#if defined(CONFIG_SMP)
-/* This keeps a track of which one is crashing cpu. */
-static int crashing_cpu;
static nmi_shootdown_cb shootdown_callback;
static atomic_t waiting_for_crash_ipi;
diff --git a/arch/x86/kernel/setup_percpu.c b/arch/x86/kernel/setup_percpu.c
index 2bbd27f..f9da471 100644
--- a/arch/x86/kernel/setup_percpu.c
+++ b/arch/x86/kernel/setup_percpu.c
@@ -287,4 +287,25 @@
/* Setup cpu initialized, callin, callout masks */
setup_cpu_local_masks();
+
+#ifdef CONFIG_X86_32
+ /*
+ * Sync back kernel address range again. We already did this in
+ * setup_arch(), but percpu data also needs to be available in
+ * the smpboot asm. We can't reliably pick up percpu mappings
+ * using vmalloc_fault(), because exception dispatch needs
+ * percpu data.
+ */
+ clone_pgd_range(initial_page_table + KERNEL_PGD_BOUNDARY,
+ swapper_pg_dir + KERNEL_PGD_BOUNDARY,
+ KERNEL_PGD_PTRS);
+
+ /*
+ * sync back low identity map too. It is used for example
+ * in the 32-bit EFI stub.
+ */
+ clone_pgd_range(initial_page_table,
+ swapper_pg_dir + KERNEL_PGD_BOUNDARY,
+ min(KERNEL_PGD_PTRS, KERNEL_PGD_BOUNDARY));
+#endif
}
diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c
index ca69967..ea217ca 100644
--- a/arch/x86/kernel/smp.c
+++ b/arch/x86/kernel/smp.c
@@ -33,6 +33,7 @@
#include <asm/mce.h>
#include <asm/trace/irq_vectors.h>
#include <asm/kexec.h>
+#include <asm/virtext.h>
/*
* Some notes on x86 processor bugs affecting SMP operation:
@@ -162,6 +163,7 @@
if (raw_smp_processor_id() == atomic_read(&stopping_cpu))
return NMI_HANDLED;
+ cpu_emergency_vmxoff();
stop_this_cpu(NULL);
return NMI_HANDLED;
@@ -174,6 +176,7 @@
asmlinkage __visible void smp_reboot_interrupt(void)
{
ipi_entering_ack_irq();
+ cpu_emergency_vmxoff();
stop_this_cpu(NULL);
irq_exit();
}
diff --git a/arch/x86/kernel/sys_x86_64.c b/arch/x86/kernel/sys_x86_64.c
index 1119414..1d4e7fd 100644
--- a/arch/x86/kernel/sys_x86_64.c
+++ b/arch/x86/kernel/sys_x86_64.c
@@ -16,6 +16,7 @@
#include <linux/uaccess.h>
#include <linux/elf.h>
+#include <asm/compat.h>
#include <asm/ia32.h>
#include <asm/syscalls.h>
@@ -100,7 +101,7 @@
static void find_start_end(unsigned long flags, unsigned long *begin,
unsigned long *end)
{
- if (!test_thread_flag(TIF_ADDR32) && (flags & MAP_32BIT)) {
+ if (!in_compat_syscall() && (flags & MAP_32BIT)) {
/* This is usually used needed to map code in small
model, so it needs to be in the first 31bit. Limit
it to that. This means we need to move the
@@ -175,7 +176,7 @@
return addr;
/* for MAP_32BIT mappings we force the legacy mmap base */
- if (!test_thread_flag(TIF_ADDR32) && (flags & MAP_32BIT))
+ if (!in_compat_syscall() && (flags & MAP_32BIT))
goto bottomup;
/* requesting a specific address */
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 322f433..f214293 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -526,7 +526,6 @@
}
NOKPROBE_SYMBOL(do_general_protection);
-/* May run on IST stack. */
dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code)
{
#ifdef CONFIG_DYNAMIC_FTRACE
@@ -541,7 +540,15 @@
if (poke_int3_handler(regs))
return;
+ /*
+ * Use ist_enter despite the fact that we don't use an IST stack.
+ * We can be called from a kprobe in non-CONTEXT_KERNEL kernel
+ * mode or even during context tracking state changes.
+ *
+ * This means that we can't schedule. That's okay.
+ */
ist_enter(regs);
+
RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
#ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
@@ -558,17 +565,11 @@
SIGTRAP) == NOTIFY_STOP)
goto exit;
- /*
- * Let others (NMI) know that the debug stack is in use
- * as we may switch to the interrupt stack.
- */
- debug_stack_usage_inc();
preempt_disable();
cond_local_irq_enable(regs);
do_trap(X86_TRAP_BP, SIGTRAP, "int3", regs, error_code, NULL);
cond_local_irq_disable(regs);
preempt_enable_no_resched();
- debug_stack_usage_dec();
exit:
ist_exit(regs);
}
@@ -989,19 +990,16 @@
cpu_init();
/*
- * X86_TRAP_DB and X86_TRAP_BP have been set
- * in early_trap_init(). However, ITS works only after
- * cpu_init() loads TSS. See comments in early_trap_init().
+ * X86_TRAP_DB was installed in early_trap_init(). However,
+ * IST works only after cpu_init() loads TSS. See comments
+ * in early_trap_init().
*/
set_intr_gate_ist(X86_TRAP_DB, &debug, DEBUG_STACK);
- /* int3 can be called from all */
- set_system_intr_gate_ist(X86_TRAP_BP, &int3, DEBUG_STACK);
x86_init.irqs.trap_init();
#ifdef CONFIG_X86_64
memcpy(&debug_idt_table, &idt_table, IDT_ENTRIES * 16);
set_nmi_gate(X86_TRAP_DB, &debug);
- set_nmi_gate(X86_TRAP_BP, &int3);
#endif
}
diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c
index 8a1d635..961831b 100644
--- a/arch/x86/kernel/vm86_32.c
+++ b/arch/x86/kernel/vm86_32.c
@@ -719,7 +719,8 @@
return;
check_vip:
- if (VEFLAGS & X86_EFLAGS_VIP) {
+ if ((VEFLAGS & (X86_EFLAGS_VIP | X86_EFLAGS_VIF)) ==
+ (X86_EFLAGS_VIP | X86_EFLAGS_VIF)) {
save_v86_state(regs, VM86_STI);
return;
}
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index be644af..8c99f2f 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -44,6 +44,7 @@
#include <asm/debugreg.h>
#include <asm/kvm_para.h>
#include <asm/irq_remapping.h>
+#include <asm/microcode.h>
#include <asm/nospec-branch.h>
#include <asm/virtext.h>
@@ -4919,7 +4920,7 @@
* being speculatively taken.
*/
if (svm->spec_ctrl)
- wrmsrl(MSR_IA32_SPEC_CTRL, svm->spec_ctrl);
+ native_wrmsrl(MSR_IA32_SPEC_CTRL, svm->spec_ctrl);
asm volatile (
"push %%" _ASM_BP "; \n\t"
@@ -5028,11 +5029,11 @@
* If the L02 MSR bitmap does not intercept the MSR, then we need to
* save it.
*/
- if (!msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL))
- rdmsrl(MSR_IA32_SPEC_CTRL, svm->spec_ctrl);
+ if (unlikely(!msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL)))
+ svm->spec_ctrl = native_read_msr(MSR_IA32_SPEC_CTRL);
if (svm->spec_ctrl)
- wrmsrl(MSR_IA32_SPEC_CTRL, 0);
+ native_wrmsrl(MSR_IA32_SPEC_CTRL, 0);
/* Eliminate branch target predictions from guest mode */
vmexit_fill_RSB();
@@ -5448,6 +5449,12 @@
avic_handle_ldr_update(vcpu);
}
+static void svm_setup_mce(struct kvm_vcpu *vcpu)
+{
+ /* [63:9] are reserved. */
+ vcpu->arch.mcg_cap &= 0x1ff;
+}
+
static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
.cpu_has_kvm_support = has_svm,
.disabled_by_bios = is_disabled,
@@ -5563,6 +5570,7 @@
.pmu_ops = &amd_pmu_ops,
.deliver_posted_interrupt = svm_deliver_avic_intr,
.update_pi_irte = svm_update_pi_irte,
+ .setup_mce = svm_setup_mce,
};
static int __init svm_init(void)
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index f2cd6bd..9c51710 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -49,6 +49,7 @@
#include <asm/kexec.h>
#include <asm/apic.h>
#include <asm/irq_remapping.h>
+#include <asm/microcode.h>
#include <asm/nospec-branch.h>
#include "trace.h"
@@ -1052,6 +1053,13 @@
(INTR_TYPE_HARD_EXCEPTION | MC_VECTOR | INTR_INFO_VALID_MASK);
}
+/* Undocumented: icebp/int1 */
+static inline bool is_icebp(u32 intr_info)
+{
+ return (intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VALID_MASK))
+ == (INTR_TYPE_PRIV_SW_EXCEPTION | INTR_INFO_VALID_MASK);
+}
+
static inline bool cpu_has_vmx_msr_bitmap(void)
{
return vmcs_config.cpu_based_exec_ctrl & CPU_BASED_USE_MSR_BITMAPS;
@@ -5732,7 +5740,7 @@
(KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP))) {
vcpu->arch.dr6 &= ~15;
vcpu->arch.dr6 |= dr6 | DR6_RTM;
- if (!(dr6 & ~DR6_RESERVED)) /* icebp */
+ if (is_icebp(intr_info))
skip_emulated_instruction(vcpu);
kvm_queue_exception(vcpu, DB_VECTOR);
@@ -8905,7 +8913,7 @@
* being speculatively taken.
*/
if (vmx->spec_ctrl)
- wrmsrl(MSR_IA32_SPEC_CTRL, vmx->spec_ctrl);
+ native_wrmsrl(MSR_IA32_SPEC_CTRL, vmx->spec_ctrl);
vmx->__launched = vmx->loaded_vmcs->launched;
asm(
@@ -9040,11 +9048,11 @@
* If the L02 MSR bitmap does not intercept the MSR, then we need to
* save it.
*/
- if (!msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL))
- rdmsrl(MSR_IA32_SPEC_CTRL, vmx->spec_ctrl);
+ if (unlikely(!msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL)))
+ vmx->spec_ctrl = native_read_msr(MSR_IA32_SPEC_CTRL);
if (vmx->spec_ctrl)
- wrmsrl(MSR_IA32_SPEC_CTRL, 0);
+ native_wrmsrl(MSR_IA32_SPEC_CTRL, 0);
/* Eliminate branch target predictions from guest mode */
vmexit_fill_RSB();
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 4b19ec1..3aaaf30 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3070,7 +3070,8 @@
return -EINVAL;
if (events->exception.injected &&
- (events->exception.nr > 31 || events->exception.nr == NMI_VECTOR))
+ (events->exception.nr > 31 || events->exception.nr == NMI_VECTOR ||
+ is_guest_mode(vcpu)))
return -EINVAL;
/* INITs are latched while in SMM */
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index 4ad7c4d..6bf1898 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -26,7 +26,6 @@
lib-$(CONFIG_INSTRUCTION_DECODER) += insn.o inat.o
lib-$(CONFIG_RANDOMIZE_BASE) += kaslr.o
lib-$(CONFIG_RETPOLINE) += retpoline.o
-OBJECT_FILES_NON_STANDARD_retpoline.o :=y
obj-y += msr.o msr-reg.o msr-reg-export.o hweight.o
diff --git a/arch/x86/lib/retpoline.S b/arch/x86/lib/retpoline.S
index 480edc3..c909961 100644
--- a/arch/x86/lib/retpoline.S
+++ b/arch/x86/lib/retpoline.S
@@ -7,7 +7,6 @@
#include <asm/alternative-asm.h>
#include <asm/export.h>
#include <asm/nospec-branch.h>
-#include <asm/bitsperlong.h>
.macro THUNK reg
.section .text.__x86.indirect_thunk
@@ -47,58 +46,3 @@
GENERATE_THUNK(r14)
GENERATE_THUNK(r15)
#endif
-
-/*
- * Fill the CPU return stack buffer.
- *
- * Each entry in the RSB, if used for a speculative 'ret', contains an
- * infinite 'pause; lfence; jmp' loop to capture speculative execution.
- *
- * This is required in various cases for retpoline and IBRS-based
- * mitigations for the Spectre variant 2 vulnerability. Sometimes to
- * eliminate potentially bogus entries from the RSB, and sometimes
- * purely to ensure that it doesn't get empty, which on some CPUs would
- * allow predictions from other (unwanted!) sources to be used.
- *
- * Google experimented with loop-unrolling and this turned out to be
- * the optimal version - two calls, each with their own speculation
- * trap should their return address end up getting used, in a loop.
- */
-.macro STUFF_RSB nr:req sp:req
- mov $(\nr / 2), %_ASM_BX
- .align 16
-771:
- call 772f
-773: /* speculation trap */
- pause
- lfence
- jmp 773b
- .align 16
-772:
- call 774f
-775: /* speculation trap */
- pause
- lfence
- jmp 775b
- .align 16
-774:
- dec %_ASM_BX
- jnz 771b
- add $((BITS_PER_LONG/8) * \nr), \sp
-.endm
-
-#define RSB_FILL_LOOPS 16 /* To avoid underflow */
-
-ENTRY(__fill_rsb)
- STUFF_RSB RSB_FILL_LOOPS, %_ASM_SP
- ret
-END(__fill_rsb)
-EXPORT_SYMBOL_GPL(__fill_rsb)
-
-#define RSB_CLEAR_LOOPS 32 /* To forcibly overwrite all entries */
-
-ENTRY(__clear_rsb)
- STUFF_RSB RSB_CLEAR_LOOPS, %_ASM_SP
- ret
-END(__clear_rsb)
-EXPORT_SYMBOL_GPL(__clear_rsb)
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 3d7f60f..54efa85 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -343,7 +343,7 @@
if (!pmd_k)
return -1;
- if (pmd_huge(*pmd_k))
+ if (pmd_large(*pmd_k))
return 0;
pte_k = pte_offset_kernel(pmd_k, address);
@@ -463,7 +463,7 @@
if (pud_none(*pud) || pud_pfn(*pud) != pud_pfn(*pud_ref))
BUG();
- if (pud_huge(*pud))
+ if (pud_large(*pud))
return 0;
pmd = pmd_offset(pud, address);
@@ -474,7 +474,7 @@
if (pmd_none(*pmd) || pmd_pfn(*pmd) != pmd_pfn(*pmd_ref))
BUG();
- if (pmd_huge(*pmd))
+ if (pmd_large(*pmd))
return 0;
pte_ref = pte_offset_kernel(pmd_ref, address);
diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c
index 209b946..b97ef29 100644
--- a/arch/x86/mm/pgtable.c
+++ b/arch/x86/mm/pgtable.c
@@ -643,4 +643,52 @@
return 0;
}
+
+/**
+ * pud_free_pmd_page - Clear pud entry and free pmd page.
+ * @pud: Pointer to a PUD.
+ *
+ * Context: The pud range has been unmaped and TLB purged.
+ * Return: 1 if clearing the entry succeeded. 0 otherwise.
+ */
+int pud_free_pmd_page(pud_t *pud)
+{
+ pmd_t *pmd;
+ int i;
+
+ if (pud_none(*pud))
+ return 1;
+
+ pmd = (pmd_t *)pud_page_vaddr(*pud);
+
+ for (i = 0; i < PTRS_PER_PMD; i++)
+ if (!pmd_free_pte_page(&pmd[i]))
+ return 0;
+
+ pud_clear(pud);
+ free_page((unsigned long)pmd);
+
+ return 1;
+}
+
+/**
+ * pmd_free_pte_page - Clear pmd entry and free pte page.
+ * @pmd: Pointer to a PMD.
+ *
+ * Context: The pmd range has been unmaped and TLB purged.
+ * Return: 1 if clearing the entry succeeded. 0 otherwise.
+ */
+int pmd_free_pte_page(pmd_t *pmd)
+{
+ pte_t *pte;
+
+ if (pmd_none(*pmd))
+ return 1;
+
+ pte = (pte_t *)pmd_page_vaddr(*pmd);
+ pmd_clear(pmd);
+ free_page((unsigned long)pte);
+
+ return 1;
+}
#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index 578973a..eac92e2 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -10,6 +10,7 @@
#include <asm/tlbflush.h>
#include <asm/mmu_context.h>
+#include <asm/nospec-branch.h>
#include <asm/cache.h>
#include <asm/apic.h>
#include <asm/uv/uv.h>
@@ -29,6 +30,8 @@
* Implement flush IPI by CALL_FUNCTION_VECTOR, Alex Shi
*/
+atomic64_t last_mm_ctx_id = ATOMIC64_INIT(1);
+
struct flush_tlb_info {
struct mm_struct *flush_mm;
unsigned long flush_start;
@@ -104,6 +107,28 @@
unsigned cpu = smp_processor_id();
if (likely(prev != next)) {
+ u64 last_ctx_id = this_cpu_read(cpu_tlbstate.last_ctx_id);
+
+ /*
+ * Avoid user/user BTB poisoning by flushing the branch
+ * predictor when switching between processes. This stops
+ * one process from doing Spectre-v2 attacks on another.
+ *
+ * As an optimization, flush indirect branches only when
+ * switching into processes that disable dumping. This
+ * protects high value processes like gpg, without having
+ * too high performance overhead. IBPB is *expensive*!
+ *
+ * This will not flush branches when switching into kernel
+ * threads. It will also not flush if we switch to idle
+ * thread and back to the same process. It will flush if we
+ * switch to a different non-dumpable process.
+ */
+ if (tsk && tsk->mm &&
+ tsk->mm->context.ctx_id != last_ctx_id &&
+ get_dumpable(tsk->mm) != SUID_DUMP_USER)
+ indirect_branch_prediction_barrier();
+
if (IS_ENABLED(CONFIG_VMAP_STACK)) {
/*
* If our current stack is in vmalloc space and isn't
@@ -118,6 +143,14 @@
set_pgd(pgd, init_mm.pgd[stack_pgd_index]);
}
+ /*
+ * Record last user mm's context id, so we can avoid
+ * flushing branch buffer with IBPB if we switch back
+ * to the same user.
+ */
+ if (next != &init_mm)
+ this_cpu_write(cpu_tlbstate.last_ctx_id, next->context.ctx_id);
+
this_cpu_write(cpu_tlbstate.state, TLBSTATE_OK);
this_cpu_write(cpu_tlbstate.active_mm, next);
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index 7840331..cd97645 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -12,6 +12,7 @@
#include <linux/filter.h>
#include <linux/if_vlan.h>
#include <asm/cacheflush.h>
+#include <asm/nospec-branch.h>
#include <linux/bpf.h>
int bpf_jit_enable __read_mostly;
@@ -281,7 +282,7 @@
EMIT2(0x89, 0xD2); /* mov edx, edx */
EMIT3(0x39, 0x56, /* cmp dword ptr [rsi + 16], edx */
offsetof(struct bpf_array, map.max_entries));
-#define OFFSET1 43 /* number of bytes to jump */
+#define OFFSET1 (41 + RETPOLINE_RAX_BPF_JIT_SIZE) /* number of bytes to jump */
EMIT2(X86_JBE, OFFSET1); /* jbe out */
label1 = cnt;
@@ -290,7 +291,7 @@
*/
EMIT2_off32(0x8B, 0x85, -STACKSIZE + 36); /* mov eax, dword ptr [rbp - 516] */
EMIT3(0x83, 0xF8, MAX_TAIL_CALL_CNT); /* cmp eax, MAX_TAIL_CALL_CNT */
-#define OFFSET2 32
+#define OFFSET2 (30 + RETPOLINE_RAX_BPF_JIT_SIZE)
EMIT2(X86_JA, OFFSET2); /* ja out */
label2 = cnt;
EMIT3(0x83, 0xC0, 0x01); /* add eax, 1 */
@@ -304,7 +305,7 @@
* goto out;
*/
EMIT3(0x48, 0x85, 0xC0); /* test rax,rax */
-#define OFFSET3 10
+#define OFFSET3 (8 + RETPOLINE_RAX_BPF_JIT_SIZE)
EMIT2(X86_JE, OFFSET3); /* je out */
label3 = cnt;
@@ -317,7 +318,7 @@
* rdi == ctx (1st arg)
* rax == prog->bpf_func + prologue_size
*/
- EMIT2(0xFF, 0xE0); /* jmp rax */
+ RETPOLINE_RAX_BPF_JIT();
/* out: */
BUILD_BUG_ON(cnt - label1 != OFFSET1);
@@ -1134,7 +1135,7 @@
* may converge on the last pass. In such case do one more
* pass to emit the final image
*/
- for (pass = 0; pass < 10 || image; pass++) {
+ for (pass = 0; pass < 20 || image; pass++) {
proglen = do_jit(prog, addrs, image, oldproglen, &ctx);
if (proglen <= 0) {
image = NULL;
@@ -1161,6 +1162,7 @@
}
}
oldproglen = proglen;
+ cond_resched();
}
if (bpf_jit_enable > 1)
diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c
index 28c0412..18f9dad 100644
--- a/arch/x86/oprofile/nmi_int.c
+++ b/arch/x86/oprofile/nmi_int.c
@@ -472,7 +472,7 @@
goto fail;
for_each_possible_cpu(cpu) {
- if (!cpu)
+ if (!IS_ENABLED(CONFIG_SMP) || !cpu)
continue;
memcpy(per_cpu(cpu_msrs, cpu).counters,
@@ -615,7 +615,7 @@
static int force_cpu_type;
-static int set_cpu_type(const char *str, struct kernel_param *kp)
+static int set_cpu_type(const char *str, const struct kernel_param *kp)
{
if (!strcmp(str, "timer")) {
force_cpu_type = timer;
diff --git a/arch/x86/platform/intel-mid/intel-mid.c b/arch/x86/platform/intel-mid/intel-mid.c
index 7850128..834783b 100644
--- a/arch/x86/platform/intel-mid/intel-mid.c
+++ b/arch/x86/platform/intel-mid/intel-mid.c
@@ -79,7 +79,7 @@
static void intel_mid_reboot(void)
{
- intel_scu_ipc_simple_command(IPCMSG_COLD_BOOT, 0);
+ intel_scu_ipc_simple_command(IPCMSG_COLD_RESET, 0);
}
static unsigned long __init intel_mid_calibrate_tsc(void)
diff --git a/arch/x86/tools/relocs.c b/arch/x86/tools/relocs.c
index 73eb7fd..5b6c848 100644
--- a/arch/x86/tools/relocs.c
+++ b/arch/x86/tools/relocs.c
@@ -769,9 +769,12 @@
break;
case R_X86_64_PC32:
+ case R_X86_64_PLT32:
/*
* PC relative relocations don't need to be adjusted unless
* referencing a percpu symbol.
+ *
+ * NB: R_X86_64_PLT32 can be treated as R_X86_64_PC32.
*/
if (is_percpu_sym(sym, symname))
add_reloc(&relocs32neg, offset);
diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c
index 137afbb..a11540e 100644
--- a/arch/x86/xen/smp.c
+++ b/arch/x86/xen/smp.c
@@ -299,35 +299,46 @@
}
-static void __init xen_smp_prepare_boot_cpu(void)
+static void __init xen_pv_smp_prepare_boot_cpu(void)
{
BUG_ON(smp_processor_id() != 0);
native_smp_prepare_boot_cpu();
- if (xen_pv_domain()) {
- if (!xen_feature(XENFEAT_writable_page_tables))
- /* We've switched to the "real" per-cpu gdt, so make
- * sure the old memory can be recycled. */
- make_lowmem_page_readwrite(xen_initial_gdt);
+ if (!xen_feature(XENFEAT_writable_page_tables))
+ /* We've switched to the "real" per-cpu gdt, so make
+ * sure the old memory can be recycled. */
+ make_lowmem_page_readwrite(xen_initial_gdt);
#ifdef CONFIG_X86_32
- /*
- * Xen starts us with XEN_FLAT_RING1_DS, but linux code
- * expects __USER_DS
- */
- loadsegment(ds, __USER_DS);
- loadsegment(es, __USER_DS);
+ /*
+ * Xen starts us with XEN_FLAT_RING1_DS, but linux code
+ * expects __USER_DS
+ */
+ loadsegment(ds, __USER_DS);
+ loadsegment(es, __USER_DS);
#endif
- xen_filter_cpu_maps();
- xen_setup_vcpu_info_placement();
- }
+ xen_filter_cpu_maps();
+ xen_setup_vcpu_info_placement();
+
+ /*
+ * The alternative logic (which patches the unlock/lock) runs before
+ * the smp bootup up code is activated. Hence we need to set this up
+ * the core kernel is being patched. Otherwise we will have only
+ * modules patched but not core code.
+ */
+ xen_init_spinlocks();
+}
+
+static void __init xen_hvm_smp_prepare_boot_cpu(void)
+{
+ BUG_ON(smp_processor_id() != 0);
+ native_smp_prepare_boot_cpu();
/*
* Setup vcpu_info for boot CPU.
*/
- if (xen_hvm_domain())
- xen_vcpu_setup(0);
+ xen_vcpu_setup(0);
/*
* The alternative logic (which patches the unlock/lock) runs before
@@ -733,7 +744,7 @@
}
static const struct smp_ops xen_smp_ops __initconst = {
- .smp_prepare_boot_cpu = xen_smp_prepare_boot_cpu,
+ .smp_prepare_boot_cpu = xen_pv_smp_prepare_boot_cpu,
.smp_prepare_cpus = xen_smp_prepare_cpus,
.smp_cpus_done = xen_smp_cpus_done,
@@ -772,5 +783,5 @@
smp_ops.cpu_die = xen_cpu_die;
smp_ops.send_call_func_ipi = xen_smp_send_call_function_ipi;
smp_ops.send_call_func_single_ipi = xen_smp_send_call_function_single_ipi;
- smp_ops.smp_prepare_boot_cpu = xen_smp_prepare_boot_cpu;
+ smp_ops.smp_prepare_boot_cpu = xen_hvm_smp_prepare_boot_cpu;
}
diff --git a/arch/x86/xen/suspend.c b/arch/x86/xen/suspend.c
index 7f664c4..4ecd0de 100644
--- a/arch/x86/xen/suspend.c
+++ b/arch/x86/xen/suspend.c
@@ -1,11 +1,14 @@
#include <linux/types.h>
#include <linux/tick.h>
+#include <linux/percpu-defs.h>
#include <xen/xen.h>
#include <xen/interface/xen.h>
#include <xen/grant_table.h>
#include <xen/events.h>
+#include <asm/cpufeatures.h>
+#include <asm/msr-index.h>
#include <asm/xen/hypercall.h>
#include <asm/xen/page.h>
#include <asm/fixmap.h>
@@ -68,6 +71,8 @@
xen_mm_unpin_all();
}
+static DEFINE_PER_CPU(u64, spec_ctrl);
+
void xen_arch_pre_suspend(void)
{
if (xen_pv_domain())
@@ -84,6 +89,9 @@
static void xen_vcpu_notify_restore(void *data)
{
+ if (xen_pv_domain() && boot_cpu_has(X86_FEATURE_SPEC_CTRL))
+ wrmsrl(MSR_IA32_SPEC_CTRL, this_cpu_read(spec_ctrl));
+
/* Boot processor notified via generic timekeeping_resume() */
if (smp_processor_id() == 0)
return;
@@ -93,7 +101,15 @@
static void xen_vcpu_notify_suspend(void *data)
{
+ u64 tmp;
+
tick_suspend_local();
+
+ if (xen_pv_domain() && boot_cpu_has(X86_FEATURE_SPEC_CTRL)) {
+ rdmsrl(MSR_IA32_SPEC_CTRL, tmp);
+ this_cpu_write(spec_ctrl, tmp);
+ wrmsrl(MSR_IA32_SPEC_CTRL, 0);
+ }
}
void xen_arch_resume(void)
diff --git a/arch/xtensa/mm/init.c b/arch/xtensa/mm/init.c
index 80e4cfb..d5abdf8 100644
--- a/arch/xtensa/mm/init.c
+++ b/arch/xtensa/mm/init.c
@@ -77,19 +77,75 @@
free_area_init_node(0, zones_size, ARCH_PFN_OFFSET, NULL);
}
+#ifdef CONFIG_HIGHMEM
+static void __init free_area_high(unsigned long pfn, unsigned long end)
+{
+ for (; pfn < end; pfn++)
+ free_highmem_page(pfn_to_page(pfn));
+}
+
+static void __init free_highpages(void)
+{
+ unsigned long max_low = max_low_pfn;
+ struct memblock_region *mem, *res;
+
+ reset_all_zones_managed_pages();
+ /* set highmem page free */
+ for_each_memblock(memory, mem) {
+ unsigned long start = memblock_region_memory_base_pfn(mem);
+ unsigned long end = memblock_region_memory_end_pfn(mem);
+
+ /* Ignore complete lowmem entries */
+ if (end <= max_low)
+ continue;
+
+ if (memblock_is_nomap(mem))
+ continue;
+
+ /* Truncate partial highmem entries */
+ if (start < max_low)
+ start = max_low;
+
+ /* Find and exclude any reserved regions */
+ for_each_memblock(reserved, res) {
+ unsigned long res_start, res_end;
+
+ res_start = memblock_region_reserved_base_pfn(res);
+ res_end = memblock_region_reserved_end_pfn(res);
+
+ if (res_end < start)
+ continue;
+ if (res_start < start)
+ res_start = start;
+ if (res_start > end)
+ res_start = end;
+ if (res_end > end)
+ res_end = end;
+ if (res_start != start)
+ free_area_high(start, res_start);
+ start = res_end;
+ if (start == end)
+ break;
+ }
+
+ /* And now free anything which remains */
+ if (start < end)
+ free_area_high(start, end);
+ }
+}
+#else
+static void __init free_highpages(void)
+{
+}
+#endif
+
/*
* Initialize memory pages.
*/
void __init mem_init(void)
{
-#ifdef CONFIG_HIGHMEM
- unsigned long tmp;
-
- reset_all_zones_managed_pages();
- for (tmp = max_low_pfn; tmp < max_pfn; tmp++)
- free_highmem_page(pfn_to_page(tmp));
-#endif
+ free_highpages();
max_mapnr = max_pfn - ARCH_PFN_OFFSET;
high_memory = (void *)__va(max_low_pfn << PAGE_SHIFT);
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index d673a69..a28ce59 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -1079,10 +1079,8 @@
if (preloaded)
radix_tree_preload_end();
- if (IS_ERR(blkg)) {
- blkg_free(new_blkg);
+ if (IS_ERR(blkg))
return PTR_ERR(blkg);
- }
q->root_blkg = blkg;
q->root_rl.blkg = blkg;
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 74ff73f..8c74712 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -2014,15 +2014,15 @@
blk_mq_init_cpu_queues(q, set->nr_hw_queues);
- get_online_cpus();
mutex_lock(&all_q_mutex);
+ get_online_cpus();
list_add_tail(&q->all_q_node, &all_q_list);
blk_mq_add_queue_tag_set(set, q);
blk_mq_map_swqueue(q, cpu_online_mask);
- mutex_unlock(&all_q_mutex);
put_online_cpus();
+ mutex_unlock(&all_q_mutex);
return q;
diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index a3ea826..3a4c9a3 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -499,6 +499,17 @@
static void throtl_schedule_pending_timer(struct throtl_service_queue *sq,
unsigned long expires)
{
+ unsigned long max_expire = jiffies + 8 * throtl_slice;
+
+ /*
+ * Since we are adjusting the throttle limit dynamically, the sleep
+ * time calculated according to previous limit might be invalid. It's
+ * possible the cgroup sleep time is very long and no other cgroups
+ * have IO running so notify the limit changes. Make sure the cgroup
+ * doesn't sleep too long to avoid the missed notification.
+ */
+ if (time_after(expires, max_expire))
+ expires = max_expire;
mod_timer(&sq->pending_timer, expires);
throtl_log(sq, "schedule timer. delay=%lu jiffies=%lu",
expires - jiffies, jiffies);
diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c
index 5a37962..df1bde2 100644
--- a/crypto/asymmetric_keys/pkcs7_verify.c
+++ b/crypto/asymmetric_keys/pkcs7_verify.c
@@ -261,7 +261,7 @@
sinfo->index);
return 0;
}
- ret = public_key_verify_signature(p->pub, p->sig);
+ ret = public_key_verify_signature(p->pub, x509->sig);
if (ret < 0)
return ret;
x509->signer = p;
diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index 4955eb6..8525fe4 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -93,9 +93,11 @@
BUG_ON(!pkey);
BUG_ON(!sig);
- BUG_ON(!sig->digest);
BUG_ON(!sig->s);
+ if (!sig->digest)
+ return -ENOPKG;
+
alg_name = sig->pkey_algo;
if (strcmp(sig->pkey_algo, "rsa") == 0) {
/* The data wangled by the RSA algorithm is typically padded
diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c
index 19d1afb9..09b1374 100644
--- a/crypto/asymmetric_keys/restrict.c
+++ b/crypto/asymmetric_keys/restrict.c
@@ -66,8 +66,9 @@
*
* Returns 0 if the new certificate was accepted, -ENOKEY if we couldn't find a
* matching parent certificate in the trusted list, -EKEYREJECTED if the
- * signature check fails or the key is blacklisted and some other error if
- * there is a matching certificate but the signature check cannot be performed.
+ * signature check fails or the key is blacklisted, -ENOPKG if the signature
+ * uses unsupported crypto, or some other error if there is a matching
+ * certificate but the signature check cannot be performed.
*/
int restrict_link_by_signature(struct key *trust_keyring,
const struct key_type *type,
@@ -86,6 +87,8 @@
return -EOPNOTSUPP;
sig = payload->data[asym_auth];
+ if (!sig)
+ return -ENOPKG;
if (!sig->auth_ids[0] && !sig->auth_ids[1])
return -ENOKEY;
diff --git a/drivers/acpi/acpi_watchdog.c b/drivers/acpi/acpi_watchdog.c
index 13caebd..ce8fc68 100644
--- a/drivers/acpi/acpi_watchdog.c
+++ b/drivers/acpi/acpi_watchdog.c
@@ -74,10 +74,10 @@
res.start = gas->address;
if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
res.flags = IORESOURCE_MEM;
- res.end = res.start + ALIGN(gas->access_width, 4);
+ res.end = res.start + ALIGN(gas->access_width, 4) - 1;
} else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
res.flags = IORESOURCE_IO;
- res.end = res.start + gas->access_width;
+ res.end = res.start + gas->access_width - 1;
} else {
pr_warn("Unsupported address space: %u\n",
gas->space_id);
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index e19f530..f7c4301 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -556,7 +556,8 @@
return 0;
}
-static int param_set_lid_init_state(const char *val, struct kernel_param *kp)
+static int param_set_lid_init_state(const char *val,
+ const struct kernel_param *kp)
{
int result = 0;
@@ -574,7 +575,8 @@
return result;
}
-static int param_get_lid_init_state(char *buffer, struct kernel_param *kp)
+static int param_get_lid_init_state(char *buffer,
+ const struct kernel_param *kp)
{
switch (lid_init_state) {
case ACPI_BUTTON_LID_INIT_OPEN:
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index c3bcb7f..b4a2c31 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -1891,7 +1891,8 @@
SET_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend, acpi_ec_resume)
};
-static int param_set_event_clearing(const char *val, struct kernel_param *kp)
+static int param_set_event_clearing(const char *val,
+ const struct kernel_param *kp)
{
int result = 0;
@@ -1909,7 +1910,8 @@
return result;
}
-static int param_get_event_clearing(char *buffer, struct kernel_param *kp)
+static int param_get_event_clearing(char *buffer,
+ const struct kernel_param *kp)
{
switch (ec_event_clearing) {
case ACPI_EC_EVT_TIMING_STATUS:
diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c
index ce3a7a1..17b518c 100644
--- a/drivers/acpi/numa.c
+++ b/drivers/acpi/numa.c
@@ -103,25 +103,27 @@
*/
int acpi_map_pxm_to_online_node(int pxm)
{
- int node, n, dist, min_dist;
+ int node, min_node;
node = acpi_map_pxm_to_node(pxm);
if (node == NUMA_NO_NODE)
node = 0;
+ min_node = node;
if (!node_online(node)) {
- min_dist = INT_MAX;
+ int min_dist = INT_MAX, dist, n;
+
for_each_online_node(n) {
dist = node_distance(node, n);
if (dist < min_dist) {
min_dist = dist;
- node = n;
+ min_node = n;
}
}
}
- return node;
+ return min_node;
}
EXPORT_SYMBOL(acpi_map_pxm_to_online_node);
diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
index e6e991a..0c6874e 100644
--- a/drivers/acpi/pmic/intel_pmic_xpower.c
+++ b/drivers/acpi/pmic/intel_pmic_xpower.c
@@ -28,97 +28,97 @@
.address = 0x00,
.reg = 0x13,
.bit = 0x05,
- },
+ }, /* ALD1 */
{
.address = 0x04,
.reg = 0x13,
.bit = 0x06,
- },
+ }, /* ALD2 */
{
.address = 0x08,
.reg = 0x13,
.bit = 0x07,
- },
+ }, /* ALD3 */
{
.address = 0x0c,
.reg = 0x12,
.bit = 0x03,
- },
+ }, /* DLD1 */
{
.address = 0x10,
.reg = 0x12,
.bit = 0x04,
- },
+ }, /* DLD2 */
{
.address = 0x14,
.reg = 0x12,
.bit = 0x05,
- },
+ }, /* DLD3 */
{
.address = 0x18,
.reg = 0x12,
.bit = 0x06,
- },
+ }, /* DLD4 */
{
.address = 0x1c,
.reg = 0x12,
.bit = 0x00,
- },
+ }, /* ELD1 */
{
.address = 0x20,
.reg = 0x12,
.bit = 0x01,
- },
+ }, /* ELD2 */
{
.address = 0x24,
.reg = 0x12,
.bit = 0x02,
- },
+ }, /* ELD3 */
{
.address = 0x28,
.reg = 0x13,
.bit = 0x02,
- },
+ }, /* FLD1 */
{
.address = 0x2c,
.reg = 0x13,
.bit = 0x03,
- },
+ }, /* FLD2 */
{
.address = 0x30,
.reg = 0x13,
.bit = 0x04,
- },
+ }, /* FLD3 */
+ {
+ .address = 0x34,
+ .reg = 0x10,
+ .bit = 0x03,
+ }, /* BUC1 */
{
.address = 0x38,
.reg = 0x10,
- .bit = 0x03,
- },
+ .bit = 0x06,
+ }, /* BUC2 */
{
.address = 0x3c,
.reg = 0x10,
- .bit = 0x06,
- },
+ .bit = 0x05,
+ }, /* BUC3 */
{
.address = 0x40,
.reg = 0x10,
- .bit = 0x05,
- },
+ .bit = 0x04,
+ }, /* BUC4 */
{
.address = 0x44,
.reg = 0x10,
- .bit = 0x04,
- },
+ .bit = 0x01,
+ }, /* BUC5 */
{
.address = 0x48,
.reg = 0x10,
- .bit = 0x01,
- },
- {
- .address = 0x4c,
- .reg = 0x10,
.bit = 0x00
- },
+ }, /* BUC6 */
};
/* TMP0 - TMP5 are the same, all from GPADC */
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 1c2b846..3a6c9b7 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -864,6 +864,16 @@
mutex_unlock(&resource->resource_lock);
}
+
+ mutex_unlock(&power_resource_list_lock);
+}
+
+void acpi_turn_off_unused_power_resources(void)
+{
+ struct acpi_power_resource *resource;
+
+ mutex_lock(&power_resource_list_lock);
+
list_for_each_entry_reverse(resource, &acpi_power_resource_list, list_node) {
int result, state;
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 9d5f0c7..8697a82 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -251,6 +251,9 @@
if (ACPI_SUCCESS(status))
return 0;
+ result = -ENODEV;
+ acpi_pss_perf_exit(pr, device);
+
err_power_exit:
acpi_processor_power_exit(pr);
return result;
@@ -259,11 +262,16 @@
static int acpi_processor_start(struct device *dev)
{
struct acpi_device *device = ACPI_COMPANION(dev);
+ int ret;
if (!device)
return -ENODEV;
- return __acpi_processor_start(device);
+ /* Protect against concurrent CPU hotplug operations */
+ get_online_cpus();
+ ret = __acpi_processor_start(device);
+ put_online_cpus();
+ return ret;
}
static int acpi_processor_stop(struct device *dev)
diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c
index d51ca1c..207e9bb 100644
--- a/drivers/acpi/processor_throttling.c
+++ b/drivers/acpi/processor_throttling.c
@@ -62,8 +62,8 @@
#define THROTTLING_POSTCHANGE (2)
static int acpi_processor_get_throttling(struct acpi_processor *pr);
-int acpi_processor_set_throttling(struct acpi_processor *pr,
- int state, bool force);
+static int __acpi_processor_set_throttling(struct acpi_processor *pr,
+ int state, bool force, bool direct);
static int acpi_processor_update_tsd_coord(void)
{
@@ -891,7 +891,8 @@
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Invalid throttling state, reset\n"));
state = 0;
- ret = acpi_processor_set_throttling(pr, state, true);
+ ret = __acpi_processor_set_throttling(pr, state, true,
+ true);
if (ret)
return ret;
}
@@ -901,36 +902,31 @@
return 0;
}
+static long __acpi_processor_get_throttling(void *data)
+{
+ struct acpi_processor *pr = data;
+
+ return pr->throttling.acpi_processor_get_throttling(pr);
+}
+
static int acpi_processor_get_throttling(struct acpi_processor *pr)
{
- cpumask_var_t saved_mask;
- int ret;
-
if (!pr)
return -EINVAL;
if (!pr->flags.throttling)
return -ENODEV;
- if (!alloc_cpumask_var(&saved_mask, GFP_KERNEL))
- return -ENOMEM;
-
/*
- * Migrate task to the cpu pointed by pr.
+ * This is either called from the CPU hotplug callback of
+ * processor_driver or via the ACPI probe function. In the latter
+ * case the CPU is not guaranteed to be online. Both call sites are
+ * protected against CPU hotplug.
*/
- cpumask_copy(saved_mask, ¤t->cpus_allowed);
- /* FIXME: use work_on_cpu() */
- if (set_cpus_allowed_ptr(current, cpumask_of(pr->id))) {
- /* Can't migrate to the target pr->id CPU. Exit */
- free_cpumask_var(saved_mask);
+ if (!cpu_online(pr->id))
return -ENODEV;
- }
- ret = pr->throttling.acpi_processor_get_throttling(pr);
- /* restore the previous state */
- set_cpus_allowed_ptr(current, saved_mask);
- free_cpumask_var(saved_mask);
- return ret;
+ return work_on_cpu(pr->id, __acpi_processor_get_throttling, pr);
}
static int acpi_processor_get_fadt_info(struct acpi_processor *pr)
@@ -1080,8 +1076,15 @@
arg->target_state, arg->force);
}
-int acpi_processor_set_throttling(struct acpi_processor *pr,
- int state, bool force)
+static int call_on_cpu(int cpu, long (*fn)(void *), void *arg, bool direct)
+{
+ if (direct)
+ return fn(arg);
+ return work_on_cpu(cpu, fn, arg);
+}
+
+static int __acpi_processor_set_throttling(struct acpi_processor *pr,
+ int state, bool force, bool direct)
{
int ret = 0;
unsigned int i;
@@ -1130,7 +1133,8 @@
arg.pr = pr;
arg.target_state = state;
arg.force = force;
- ret = work_on_cpu(pr->id, acpi_processor_throttling_fn, &arg);
+ ret = call_on_cpu(pr->id, acpi_processor_throttling_fn, &arg,
+ direct);
} else {
/*
* When the T-state coordination is SW_ALL or HW_ALL,
@@ -1163,8 +1167,8 @@
arg.pr = match_pr;
arg.target_state = state;
arg.force = force;
- ret = work_on_cpu(pr->id, acpi_processor_throttling_fn,
- &arg);
+ ret = call_on_cpu(pr->id, acpi_processor_throttling_fn,
+ &arg, direct);
}
}
/*
@@ -1182,6 +1186,12 @@
return ret;
}
+int acpi_processor_set_throttling(struct acpi_processor *pr, int state,
+ bool force)
+{
+ return __acpi_processor_set_throttling(pr, state, force, false);
+}
+
int acpi_processor_get_throttling_info(struct acpi_processor *pr)
{
int result = 0;
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index a4327af..097d630 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -474,6 +474,7 @@
*/
static void acpi_pm_end(void)
{
+ acpi_turn_off_unused_power_resources();
acpi_scan_lock_release();
/*
* This is necessary in case acpi_pm_finish() is not called during a
diff --git a/drivers/acpi/sleep.h b/drivers/acpi/sleep.h
index a9cc34e..a82ff74 100644
--- a/drivers/acpi/sleep.h
+++ b/drivers/acpi/sleep.h
@@ -6,6 +6,7 @@
extern struct mutex acpi_device_lock;
extern void acpi_resume_power_resources(void);
+extern void acpi_turn_off_unused_power_resources(void);
static inline acpi_status acpi_set_waking_vector(u32 wakeup_address)
{
diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c
index cf05ae9..34328e1 100644
--- a/drivers/acpi/sysfs.c
+++ b/drivers/acpi/sysfs.c
@@ -227,7 +227,8 @@
module_param_cb(trace_debug_layer, ¶m_ops_trace_attrib, &acpi_gbl_trace_dbg_layer, 0644);
module_param_cb(trace_debug_level, ¶m_ops_trace_attrib, &acpi_gbl_trace_dbg_level, 0644);
-static int param_set_trace_state(const char *val, struct kernel_param *kp)
+static int param_set_trace_state(const char *val,
+ const struct kernel_param *kp)
{
acpi_status status;
const char *method = trace_method_name;
@@ -263,7 +264,7 @@
return 0;
}
-static int param_get_trace_state(char *buffer, struct kernel_param *kp)
+static int param_get_trace_state(char *buffer, const struct kernel_param *kp)
{
if (!(acpi_gbl_trace_flags & ACPI_TRACE_ENABLED))
return sprintf(buffer, "disable");
@@ -292,7 +293,8 @@
"To enable/disable the ACPI Debug Object output.");
/* /sys/module/acpi/parameters/acpica_version */
-static int param_get_acpica_version(char *buffer, struct kernel_param *kp)
+static int param_get_acpica_version(char *buffer,
+ const struct kernel_param *kp)
{
int result;
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index a56fa2a..7394aac 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -83,7 +83,8 @@
struct amba_device *dev = to_amba_device(_dev);
char *driver_override, *old = dev->driver_override, *cp;
- if (count > PATH_MAX)
+ /* We need to keep extra room for a newline */
+ if (count >= (PAGE_SIZE - 1))
return -EINVAL;
driver_override = kstrndup(buf, count, GFP_KERNEL);
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index ff367b8fa..e7e4560 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -151,7 +151,7 @@
static int binder_stop_on_user_error;
static int binder_set_stop_on_user_error(const char *val,
- struct kernel_param *kp)
+ const struct kernel_param *kp)
{
int ret;
@@ -2147,8 +2147,14 @@
&target_thread->reply_error.work);
wake_up_interruptible(&target_thread->wait);
} else {
- WARN(1, "Unexpected reply error: %u\n",
- target_thread->reply_error.cmd);
+ /*
+ * Cannot get here for normal operation, but
+ * we can if multiple synchronous transactions
+ * are sent without blocking for responses.
+ * Just ignore the 2nd error in this case.
+ */
+ pr_warn("Unexpected reply error: %u\n",
+ target_thread->reply_error.cmd);
}
binder_inner_proc_unlock(target_thread->proc);
binder_thread_dec_tmpref(target_thread);
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 9b46ef4..4d4b5f6 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -539,7 +539,9 @@
.driver_data = board_ahci_yes_fbs },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9230),
.driver_data = board_ahci_yes_fbs },
- { PCI_DEVICE(PCI_VENDOR_ID_TTI, 0x0642),
+ { PCI_DEVICE(PCI_VENDOR_ID_TTI, 0x0642), /* highpoint rocketraid 642L */
+ .driver_data = board_ahci_yes_fbs },
+ { PCI_DEVICE(PCI_VENDOR_ID_TTI, 0x0645), /* highpoint rocketraid 644L */
.driver_data = board_ahci_yes_fbs },
/* Promise */
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index aee3952..e08c09f 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4403,6 +4403,25 @@
{ "PIONEER DVD-RW DVR-212D", NULL, ATA_HORKAGE_NOSETXFER },
{ "PIONEER DVD-RW DVR-216D", NULL, ATA_HORKAGE_NOSETXFER },
+ /* Crucial BX100 SSD 500GB has broken LPM support */
+ { "CT500BX100SSD1", NULL, ATA_HORKAGE_NOLPM },
+
+ /* 512GB MX100 with MU01 firmware has both queued TRIM and LPM issues */
+ { "Crucial_CT512MX100*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM |
+ ATA_HORKAGE_NOLPM, },
+ /* 512GB MX100 with newer firmware has only LPM issues */
+ { "Crucial_CT512MX100*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM |
+ ATA_HORKAGE_NOLPM, },
+
+ /* 480GB+ M500 SSDs have both queued TRIM and LPM issues */
+ { "Crucial_CT480M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM |
+ ATA_HORKAGE_NOLPM, },
+ { "Crucial_CT960M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM |
+ ATA_HORKAGE_NOLPM, },
+
/* devices that don't properly handle queued TRIM commands */
{ "Micron_M500_*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
@@ -4414,7 +4433,9 @@
ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "Crucial_CT*MX100*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
- { "Samsung SSD 8*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
+ { "Samsung SSD 840*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM, },
+ { "Samsung SSD 850*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "FCCT*M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
@@ -5265,8 +5286,7 @@
* We guarantee to LLDs that they will have at least one
* non-zero sg if the command is a data command.
*/
- if (WARN_ON_ONCE(ata_is_data(prot) &&
- (!qc->sg || !qc->n_elem || !qc->nbytes)))
+ if (ata_is_data(prot) && (!qc->sg || !qc->n_elem || !qc->nbytes))
goto sys_err;
if (ata_is_dma(prot) || (ata_is_pio(prot) &&
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index e3e10e8..9babbc8 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -3226,6 +3226,12 @@
goto invalid_fld;
}
+ /* We may not issue NCQ commands to devices not supporting NCQ */
+ if (ata_is_ncq(tf->protocol) && !ata_ncq_enabled(dev)) {
+ fp = 1;
+ goto invalid_fld;
+ }
+
/* sanity check for pio multi commands */
if ((cdb[1] & 0xe0) && !is_multi_taskfile(tf)) {
fp = 1;
@@ -4177,7 +4183,9 @@
if (likely((scsi_op != ATA_16) || !atapi_passthru16)) {
/* relay SCSI command to ATAPI device */
int len = COMMAND_SIZE(scsi_op);
- if (unlikely(len > scmd->cmd_len || len > dev->cdb_len))
+ if (unlikely(len > scmd->cmd_len ||
+ len > dev->cdb_len ||
+ scmd->cmd_len > ATAPI_CDB_LEN))
goto bad_cdb_len;
xlat_func = atapi_xlat;
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c
index a7c5b79..94001aa 100644
--- a/drivers/base/power/opp/core.c
+++ b/drivers/base/power/opp/core.c
@@ -708,7 +708,7 @@
struct opp_table *opp_table)
{
opp_debug_unregister(opp_dev, opp_table);
- list_del(&opp_dev->node);
+ list_del_rcu(&opp_dev->node);
call_srcu(&opp_table->srcu_head.srcu, &opp_dev->rcu_head,
_kfree_opp_dev_rcu);
}
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 402254d..68bfcef 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -263,7 +263,7 @@
struct iov_iter i;
ssize_t bw;
- iov_iter_bvec(&i, ITER_BVEC, bvec, 1, bvec->bv_len);
+ iov_iter_bvec(&i, ITER_BVEC | WRITE, bvec, 1, bvec->bv_len);
file_start_write(file);
bw = vfs_iter_write(file, &i, ppos);
diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c
index 3cfd879..b86273f 100644
--- a/drivers/block/mtip32xx/mtip32xx.c
+++ b/drivers/block/mtip32xx/mtip32xx.c
@@ -169,6 +169,25 @@
return false; /* device present */
}
+/* we have to use runtime tag to setup command header */
+static void mtip_init_cmd_header(struct request *rq)
+{
+ struct driver_data *dd = rq->q->queuedata;
+ struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq);
+ u32 host_cap_64 = readl(dd->mmio + HOST_CAP) & HOST_CAP_64;
+
+ /* Point the command headers at the command tables. */
+ cmd->command_header = dd->port->command_list +
+ (sizeof(struct mtip_cmd_hdr) * rq->tag);
+ cmd->command_header_dma = dd->port->command_list_dma +
+ (sizeof(struct mtip_cmd_hdr) * rq->tag);
+
+ if (host_cap_64)
+ cmd->command_header->ctbau = __force_bit2int cpu_to_le32((cmd->command_dma >> 16) >> 16);
+
+ cmd->command_header->ctba = __force_bit2int cpu_to_le32(cmd->command_dma & 0xFFFFFFFF);
+}
+
static struct mtip_cmd *mtip_get_int_command(struct driver_data *dd)
{
struct request *rq;
@@ -180,6 +199,9 @@
if (IS_ERR(rq))
return NULL;
+ /* Internal cmd isn't submitted via .queue_rq */
+ mtip_init_cmd_header(rq);
+
return blk_mq_rq_to_pdu(rq);
}
@@ -3811,6 +3833,8 @@
struct request *rq = bd->rq;
int ret;
+ mtip_init_cmd_header(rq);
+
if (unlikely(mtip_check_unal_depth(hctx, rq)))
return BLK_MQ_RQ_QUEUE_BUSY;
@@ -3842,7 +3866,6 @@
{
struct driver_data *dd = data;
struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq);
- u32 host_cap_64 = readl(dd->mmio + HOST_CAP) & HOST_CAP_64;
/*
* For flush requests, request_idx starts at the end of the
@@ -3859,17 +3882,6 @@
memset(cmd->command, 0, CMD_DMA_ALLOC_SZ);
- /* Point the command headers at the command tables. */
- cmd->command_header = dd->port->command_list +
- (sizeof(struct mtip_cmd_hdr) * request_idx);
- cmd->command_header_dma = dd->port->command_list_dma +
- (sizeof(struct mtip_cmd_hdr) * request_idx);
-
- if (host_cap_64)
- cmd->command_header->ctbau = __force_bit2int cpu_to_le32((cmd->command_dma >> 16) >> 16);
-
- cmd->command_header->ctba = __force_bit2int cpu_to_le32(cmd->command_dma & 0xFFFFFFFF);
-
sg_init_table(cmd->sg, MTIP_MAX_SG);
return 0;
}
diff --git a/drivers/bluetooth/btqcomsmd.c b/drivers/bluetooth/btqcomsmd.c
index 08c2c93..3aac78a 100644
--- a/drivers/bluetooth/btqcomsmd.c
+++ b/drivers/bluetooth/btqcomsmd.c
@@ -85,7 +85,8 @@
break;
}
- kfree_skb(skb);
+ if (!ret)
+ kfree_skb(skb);
return ret;
}
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 3257647..f8ba5c7 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -217,7 +217,6 @@
{ USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0x0036), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0x311e), .driver_info = BTUSB_ATH3012 },
@@ -250,6 +249,7 @@
{ USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },
/* QCA ROME chipset */
+ { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_QCA_ROME },
{ USB_DEVICE(0x0cf3, 0xe007), .driver_info = BTUSB_QCA_ROME },
{ USB_DEVICE(0x0cf3, 0xe009), .driver_info = BTUSB_QCA_ROME },
{ USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME },
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 9497c46..2230f93 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -113,16 +113,21 @@
{
struct sk_buff *skb = hu->tx_skb;
- if (!skb)
- skb = hu->proto->dequeue(hu);
- else
+ if (!skb) {
+ if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
+ skb = hu->proto->dequeue(hu);
+ } else {
hu->tx_skb = NULL;
+ }
return skb;
}
int hci_uart_tx_wakeup(struct hci_uart *hu)
{
+ if (!test_bit(HCI_UART_PROTO_READY, &hu->flags))
+ return 0;
+
if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) {
set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
return 0;
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 6c867fb..74b2f4a 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -936,6 +936,9 @@
if (!ret) {
set_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags);
qca_debugfs_init(hdev);
+ } else if (ret == -ENOENT) {
+ /* No patch/nvm-config found, run with original fw/config */
+ ret = 0;
}
/* Setup bdaddr */
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 7f8a158..4d734bf 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -1236,6 +1236,25 @@
}
+
+static void fastrpc_notify_users_staticpd_pdr(struct fastrpc_file *me)
+{
+ struct smq_invoke_ctx *ictx;
+ struct hlist_node *n;
+
+ spin_lock(&me->hlock);
+ hlist_for_each_entry_safe(ictx, n, &me->clst.pending, hn) {
+ if (ictx->msg.pid)
+ complete(&ictx->work);
+ }
+ hlist_for_each_entry_safe(ictx, n, &me->clst.interrupted, hn) {
+ if (ictx->msg.pid)
+ complete(&ictx->work);
+ }
+ spin_unlock(&me->hlock);
+}
+
+
static void fastrpc_notify_drivers(struct fastrpc_apps *me, int cid)
{
struct fastrpc_file *fl;
@@ -1258,7 +1277,7 @@
spin_lock(&me->hlock);
hlist_for_each_entry_safe(fl, n, &me->drivers, hn) {
if (fl->spdname && !strcmp(spdname, fl->spdname))
- fastrpc_notify_users(fl);
+ fastrpc_notify_users_staticpd_pdr(fl);
}
spin_unlock(&me->hlock);
diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c
index 0f7d28a..a7cc5b7 100644
--- a/drivers/char/agp/intel-gtt.c
+++ b/drivers/char/agp/intel-gtt.c
@@ -871,6 +871,8 @@
}
}
wmb();
+ if (intel_private.driver->chipset_flush)
+ intel_private.driver->chipset_flush();
}
EXPORT_SYMBOL(intel_gtt_insert_sg_entries);
diff --git a/drivers/char/diag/diag_usb.c b/drivers/char/diag/diag_usb.c
index ae2e00d..24a9f8a 100644
--- a/drivers/char/diag/diag_usb.c
+++ b/drivers/char/diag/diag_usb.c
@@ -139,8 +139,11 @@
* Remove reference from the table if it is the
* only instance of the buffer
*/
- if (atomic_read(&entry->ref_count) == 0)
+ if (atomic_read(&entry->ref_count) == 0) {
list_del(&entry->track);
+ kfree(entry);
+ entry = NULL;
+ }
break;
}
}
@@ -332,6 +335,7 @@
buf = entry->buf;
len = entry->len;
kfree(entry);
+ entry = NULL;
diag_ws_on_copy_complete(DIAG_WS_MUX);
if (ch->ops && ch->ops->write_done)
diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c
index 9f2e3be..676c910 100644
--- a/drivers/char/ipmi/ipmi_poweroff.c
+++ b/drivers/char/ipmi/ipmi_poweroff.c
@@ -66,7 +66,7 @@
/* Holds the old poweroff function so we can restore it on removal. */
static void (*old_poweroff_func)(void);
-static int set_param_ifnum(const char *val, struct kernel_param *kp)
+static int set_param_ifnum(const char *val, const struct kernel_param *kp)
{
int rv = param_set_int(val, kp);
if (rv)
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index e0a5315..89adeb4 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -1344,7 +1344,7 @@
#define IPMI_MEM_ADDR_SPACE 1
static const char * const addr_space_to_str[] = { "i/o", "mem" };
-static int hotmod_handler(const char *val, struct kernel_param *kp);
+static int hotmod_handler(const char *val, const struct kernel_param *kp);
module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200);
MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See"
@@ -1814,7 +1814,7 @@
return info;
}
-static int hotmod_handler(const char *val, struct kernel_param *kp)
+static int hotmod_handler(const char *val, const struct kernel_param *kp)
{
char *str = kstrdup(val, GFP_KERNEL);
int rv;
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c
index 9093110..055d2ce 100644
--- a/drivers/char/ipmi/ipmi_watchdog.c
+++ b/drivers/char/ipmi/ipmi_watchdog.c
@@ -515,7 +515,7 @@
msg.cmd = IPMI_WDOG_RESET_TIMER;
msg.data = NULL;
msg.data_len = 0;
- atomic_add(2, &panic_done_count);
+ atomic_add(1, &panic_done_count);
rv = ipmi_request_supply_msgs(watchdog_user,
(struct ipmi_addr *) &addr,
0,
@@ -525,7 +525,7 @@
&panic_halt_heartbeat_recv_msg,
1);
if (rv)
- atomic_sub(2, &panic_done_count);
+ atomic_sub(1, &panic_done_count);
}
static struct ipmi_smi_msg panic_halt_smi_msg = {
@@ -549,12 +549,12 @@
/* Wait for the messages to be free. */
while (atomic_read(&panic_done_count) != 0)
ipmi_poll_interface(watchdog_user);
- atomic_add(2, &panic_done_count);
+ atomic_add(1, &panic_done_count);
rv = i_ipmi_set_timeout(&panic_halt_smi_msg,
&panic_halt_recv_msg,
&send_heartbeat_now);
if (rv) {
- atomic_sub(2, &panic_done_count);
+ atomic_sub(1, &panic_done_count);
printk(KERN_WARNING PFX
"Unable to extend the watchdog timeout.");
} else {
diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c
index 6f060c7..7205e6d 100644
--- a/drivers/char/tpm/st33zp24/st33zp24.c
+++ b/drivers/char/tpm/st33zp24/st33zp24.c
@@ -458,7 +458,7 @@
size_t count)
{
int size = 0;
- int expected;
+ u32 expected;
if (!chip)
return -EBUSY;
@@ -475,7 +475,7 @@
}
expected = be32_to_cpu(*(__be32 *)(buf + 2));
- if (expected > count) {
+ if (expected > count || expected < TPM_HEADER_SIZE) {
size = -EIO;
goto out;
}
diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c
index 912ad30..65b8249 100644
--- a/drivers/char/tpm/tpm-dev.c
+++ b/drivers/char/tpm/tpm-dev.c
@@ -136,6 +136,12 @@
return -EFAULT;
}
+ if (in_size < 6 ||
+ in_size < be32_to_cpu(*((__be32 *) (priv->data_buffer + 2)))) {
+ mutex_unlock(&priv->buffer_mutex);
+ return -EINVAL;
+ }
+
/* atomic tpm command send and result receive. We only hold the ops
* lock during this period so that the tpm can be unregistered even if
* the char dev is held open.
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index d0ac2d5..830d7e3 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -1078,6 +1078,11 @@
break;
recd = be32_to_cpu(tpm_cmd.params.getrandom_out.rng_data_len);
+ if (recd > num_bytes) {
+ total = -EFAULT;
+ break;
+ }
+
memcpy(dest, tpm_cmd.params.getrandom_out.rng_data, recd);
dest += recd;
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 17896d6..a5780eb 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -668,6 +668,11 @@
if (!rc) {
data_len = be16_to_cpup(
(__be16 *) &buf.data[TPM_HEADER_SIZE + 4]);
+ if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE + 1) {
+ rc = -EFAULT;
+ goto out;
+ }
+
data = &buf.data[TPM_HEADER_SIZE + 6];
memcpy(payload->key, data, data_len - 1);
@@ -675,6 +680,7 @@
payload->migratable = data[data_len - 1];
}
+out:
tpm_buf_destroy(&buf);
return rc;
}
diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c
index 62ee44e..da69dde 100644
--- a/drivers/char/tpm/tpm_i2c_infineon.c
+++ b/drivers/char/tpm/tpm_i2c_infineon.c
@@ -437,7 +437,8 @@
static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
{
int size = 0;
- int expected, status;
+ int status;
+ u32 expected;
if (count < TPM_HEADER_SIZE) {
size = -EIO;
@@ -452,7 +453,7 @@
}
expected = be32_to_cpu(*(__be32 *)(buf + 2));
- if ((size_t) expected > count) {
+ if (((size_t) expected > count) || (expected < TPM_HEADER_SIZE)) {
size = -EIO;
goto out;
}
diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c
index c642877..caa86b1 100644
--- a/drivers/char/tpm/tpm_i2c_nuvoton.c
+++ b/drivers/char/tpm/tpm_i2c_nuvoton.c
@@ -281,7 +281,11 @@
struct device *dev = chip->dev.parent;
struct i2c_client *client = to_i2c_client(dev);
s32 rc;
- int expected, status, burst_count, retries, size = 0;
+ int status;
+ int burst_count;
+ int retries;
+ int size = 0;
+ u32 expected;
if (count < TPM_HEADER_SIZE) {
i2c_nuvoton_ready(chip); /* return to idle */
@@ -323,7 +327,7 @@
* to machine native
*/
expected = be32_to_cpu(*(__be32 *) (buf + 2));
- if (expected > count) {
+ if (expected > count || expected < size) {
dev_err(dev, "%s() expected > count\n", __func__);
size = -EIO;
continue;
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 8022bea..06173d2 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -98,7 +98,7 @@
}
static int tpm_tcg_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
- u8 *value)
+ const u8 *value)
{
struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index 4d24ec3..f9aa47e 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -208,7 +208,8 @@
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
int size = 0;
- int expected, status;
+ int status;
+ u32 expected;
if (count < TPM_HEADER_SIZE) {
size = -EIO;
@@ -223,7 +224,7 @@
}
expected = be32_to_cpu(*(__be32 *) (buf + 2));
- if (expected > count) {
+ if (expected > count || expected < TPM_HEADER_SIZE) {
size = -EIO;
goto out;
}
@@ -256,7 +257,7 @@
* tpm.c can skip polling for the data to be available as the interrupt is
* waited for here
*/
-static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
+static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len)
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
int rc, status, burstcnt;
@@ -345,7 +346,7 @@
* tpm.c can skip polling for the data to be available as the interrupt is
* waited for here
*/
-static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len)
+static int tpm_tis_send_main(struct tpm_chip *chip, const u8 *buf, size_t len)
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
int rc;
diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h
index 9191aab..e1c2193 100644
--- a/drivers/char/tpm/tpm_tis_core.h
+++ b/drivers/char/tpm/tpm_tis_core.h
@@ -98,7 +98,7 @@
int (*read_bytes)(struct tpm_tis_data *data, u32 addr, u16 len,
u8 *result);
int (*write_bytes)(struct tpm_tis_data *data, u32 addr, u16 len,
- u8 *value);
+ const u8 *value);
int (*read16)(struct tpm_tis_data *data, u32 addr, u16 *result);
int (*read32)(struct tpm_tis_data *data, u32 addr, u32 *result);
int (*write32)(struct tpm_tis_data *data, u32 addr, u32 src);
@@ -128,7 +128,7 @@
}
static inline int tpm_tis_write_bytes(struct tpm_tis_data *data, u32 addr,
- u16 len, u8 *value)
+ u16 len, const u8 *value)
{
return data->phy_ops->write_bytes(data, addr, len, value);
}
diff --git a/drivers/char/tpm/tpm_tis_spi.c b/drivers/char/tpm/tpm_tis_spi.c
index 3b97b14..01eccb1 100644
--- a/drivers/char/tpm/tpm_tis_spi.c
+++ b/drivers/char/tpm/tpm_tis_spi.c
@@ -47,9 +47,7 @@
struct tpm_tis_spi_phy {
struct tpm_tis_data priv;
struct spi_device *spi_device;
-
- u8 tx_buf[4];
- u8 rx_buf[4];
+ u8 *iobuf;
};
static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *data)
@@ -58,7 +56,7 @@
}
static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
- u8 *buffer, u8 direction)
+ u8 *in, const u8 *out)
{
struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
int ret = 0;
@@ -72,14 +70,14 @@
while (len) {
transfer_len = min_t(u16, len, MAX_SPI_FRAMESIZE);
- phy->tx_buf[0] = direction | (transfer_len - 1);
- phy->tx_buf[1] = 0xd4;
- phy->tx_buf[2] = addr >> 8;
- phy->tx_buf[3] = addr;
+ phy->iobuf[0] = (in ? 0x80 : 0) | (transfer_len - 1);
+ phy->iobuf[1] = 0xd4;
+ phy->iobuf[2] = addr >> 8;
+ phy->iobuf[3] = addr;
memset(&spi_xfer, 0, sizeof(spi_xfer));
- spi_xfer.tx_buf = phy->tx_buf;
- spi_xfer.rx_buf = phy->rx_buf;
+ spi_xfer.tx_buf = phy->iobuf;
+ spi_xfer.rx_buf = phy->iobuf;
spi_xfer.len = 4;
spi_xfer.cs_change = 1;
@@ -89,9 +87,9 @@
if (ret < 0)
goto exit;
- if ((phy->rx_buf[3] & 0x01) == 0) {
+ if ((phy->iobuf[3] & 0x01) == 0) {
// handle SPI wait states
- phy->tx_buf[0] = 0;
+ phy->iobuf[0] = 0;
for (i = 0; i < TPM_RETRY; i++) {
spi_xfer.len = 1;
@@ -100,7 +98,7 @@
ret = spi_sync_locked(phy->spi_device, &m);
if (ret < 0)
goto exit;
- if (phy->rx_buf[0] & 0x01)
+ if (phy->iobuf[0] & 0x01)
break;
}
@@ -114,12 +112,12 @@
spi_xfer.len = transfer_len;
spi_xfer.delay_usecs = 5;
- if (direction) {
+ if (in) {
spi_xfer.tx_buf = NULL;
- spi_xfer.rx_buf = buffer;
- } else {
- spi_xfer.tx_buf = buffer;
+ } else if (out) {
spi_xfer.rx_buf = NULL;
+ memcpy(phy->iobuf, out, transfer_len);
+ out += transfer_len;
}
spi_message_init(&m);
@@ -128,8 +126,12 @@
if (ret < 0)
goto exit;
+ if (in) {
+ memcpy(in, phy->iobuf, transfer_len);
+ in += transfer_len;
+ }
+
len -= transfer_len;
- buffer += transfer_len;
}
exit:
@@ -140,13 +142,13 @@
static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr,
u16 len, u8 *result)
{
- return tpm_tis_spi_transfer(data, addr, len, result, 0x80);
+ return tpm_tis_spi_transfer(data, addr, len, result, NULL);
}
static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr,
- u16 len, u8 *value)
+ u16 len, const u8 *value)
{
- return tpm_tis_spi_transfer(data, addr, len, value, 0);
+ return tpm_tis_spi_transfer(data, addr, len, NULL, value);
}
static int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
@@ -195,6 +197,10 @@
phy->spi_device = dev;
+ phy->iobuf = devm_kmalloc(&dev->dev, MAX_SPI_FRAMESIZE, GFP_KERNEL);
+ if (!phy->iobuf)
+ return -ENOMEM;
+
return tpm_tis_core_init(&dev->dev, &phy->priv, -1, &tpm_spi_phy_ops,
NULL);
}
diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
index 2acaa77..abdc149 100644
--- a/drivers/clk/bcm/clk-bcm2835.c
+++ b/drivers/clk/bcm/clk-bcm2835.c
@@ -401,17 +401,17 @@
static const struct bcm2835_pll_ana_bits bcm2835_ana_default = {
.mask0 = 0,
.set0 = 0,
- .mask1 = (u32)~(A2W_PLL_KI_MASK | A2W_PLL_KP_MASK),
+ .mask1 = A2W_PLL_KI_MASK | A2W_PLL_KP_MASK,
.set1 = (2 << A2W_PLL_KI_SHIFT) | (8 << A2W_PLL_KP_SHIFT),
- .mask3 = (u32)~A2W_PLL_KA_MASK,
+ .mask3 = A2W_PLL_KA_MASK,
.set3 = (2 << A2W_PLL_KA_SHIFT),
.fb_prediv_mask = BIT(14),
};
static const struct bcm2835_pll_ana_bits bcm2835_ana_pllh = {
- .mask0 = (u32)~(A2W_PLLH_KA_MASK | A2W_PLLH_KI_LOW_MASK),
+ .mask0 = A2W_PLLH_KA_MASK | A2W_PLLH_KI_LOW_MASK,
.set0 = (2 << A2W_PLLH_KA_SHIFT) | (2 << A2W_PLLH_KI_LOW_SHIFT),
- .mask1 = (u32)~(A2W_PLLH_KI_HIGH_MASK | A2W_PLLH_KP_MASK),
+ .mask1 = A2W_PLLH_KI_HIGH_MASK | A2W_PLLH_KP_MASK,
.set1 = (6 << A2W_PLLH_KP_SHIFT),
.mask3 = 0,
.set3 = 0,
@@ -566,8 +566,10 @@
~A2W_PLL_CTRL_PWRDN);
/* Take the PLL out of reset. */
+ spin_lock(&cprman->regs_lock);
cprman_write(cprman, data->cm_ctrl_reg,
cprman_read(cprman, data->cm_ctrl_reg) & ~CM_PLL_ANARST);
+ spin_unlock(&cprman->regs_lock);
/* Wait for the PLL to lock. */
timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS);
@@ -644,9 +646,11 @@
}
/* Unmask the reference clock from the oscillator. */
+ spin_lock(&cprman->regs_lock);
cprman_write(cprman, A2W_XOSC_CTRL,
cprman_read(cprman, A2W_XOSC_CTRL) |
data->reference_enable_mask);
+ spin_unlock(&cprman->regs_lock);
if (do_ana_setup_first)
bcm2835_pll_write_ana(cprman, data->ana_reg_base, ana);
diff --git a/drivers/clk/bcm/clk-ns2.c b/drivers/clk/bcm/clk-ns2.c
index a564e92..adc1414 100644
--- a/drivers/clk/bcm/clk-ns2.c
+++ b/drivers/clk/bcm/clk-ns2.c
@@ -103,7 +103,7 @@
static const struct iproc_pll_ctrl genpll_sw = {
.flags = IPROC_CLK_AON | IPROC_CLK_PLL_SPLIT_STAT_CTRL,
- .aon = AON_VAL(0x0, 2, 9, 8),
+ .aon = AON_VAL(0x0, 1, 11, 10),
.reset = RESET_VAL(0x4, 2, 1),
.dig_filter = DF_VAL(0x0, 9, 3, 5, 4, 2, 3),
.ndiv_int = REG_VAL(0x8, 4, 10),
diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
index 5e918e7..95a6e98 100644
--- a/drivers/clk/clk-axi-clkgen.c
+++ b/drivers/clk/clk-axi-clkgen.c
@@ -40,6 +40,10 @@
#define MMCM_REG_FILTER1 0x4e
#define MMCM_REG_FILTER2 0x4f
+#define MMCM_CLKOUT_NOCOUNT BIT(6)
+
+#define MMCM_CLK_DIV_NOCOUNT BIT(12)
+
struct axi_clkgen {
void __iomem *base;
struct clk_hw clk_hw;
@@ -315,12 +319,27 @@
unsigned int reg;
unsigned long long tmp;
- axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, ®);
- dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+ axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_2, ®);
+ if (reg & MMCM_CLKOUT_NOCOUNT) {
+ dout = 1;
+ } else {
+ axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, ®);
+ dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+ }
+
axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, ®);
- d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
- axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, ®);
- m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+ if (reg & MMCM_CLK_DIV_NOCOUNT)
+ d = 1;
+ else
+ d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+
+ axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB2, ®);
+ if (reg & MMCM_CLKOUT_NOCOUNT) {
+ m = 1;
+ } else {
+ axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, ®);
+ m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+ }
if (d == 0 || dout == 0)
return 0;
diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c
index b051db4..136a860 100644
--- a/drivers/clk/clk-si5351.c
+++ b/drivers/clk/clk-si5351.c
@@ -72,7 +72,7 @@
"xtal", "clkin"
};
static const char * const si5351_pll_names[] = {
- "plla", "pllb", "vxco"
+ "si5351_plla", "si5351_pllb", "si5351_vxco"
};
static const char * const si5351_msynth_names[] = {
"ms0", "ms1", "ms2", "ms3", "ms4", "ms5", "ms6", "ms7"
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 929aef0..f41307d 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -3263,6 +3263,21 @@
core->rate = core->req_rate = rate;
/*
+ * Enable CLK_IS_CRITICAL clocks so newly added critical clocks
+ * don't get accidentally disabled when walking the orphan tree and
+ * reparenting clocks
+ */
+ if (core->flags & CLK_IS_CRITICAL) {
+ unsigned long flags;
+
+ clk_core_prepare(core);
+
+ flags = clk_enable_lock();
+ clk_core_enable(core);
+ clk_enable_unlock(flags);
+ }
+
+ /*
* walk the list of orphan clocks and reparent any that newly finds a
* parent.
*/
@@ -3270,10 +3285,13 @@
struct clk_core *parent = __clk_init_parent(orphan);
/*
- * we could call __clk_set_parent, but that would result in a
- * redundant call to the .set_rate op, if it exists
+ * We need to use __clk_set_parent_before() and _after() to
+ * to properly migrate any prepare/enable count of the orphan
+ * clock. This is important for CLK_IS_CRITICAL clocks, which
+ * are enabled during init but might not have a parent yet.
*/
if (parent) {
+ /* update the clk tree topology */
__clk_set_parent_before(orphan, parent);
__clk_set_parent_after(orphan, parent, NULL);
__clk_recalc_accuracies(orphan);
@@ -3292,16 +3310,6 @@
if (core->ops->init)
core->ops->init(core->hw);
- if (core->flags & CLK_IS_CRITICAL) {
- unsigned long flags;
-
- clk_core_prepare(core);
-
- flags = clk_enable_lock();
- clk_core_enable(core);
- clk_enable_unlock(flags);
- }
-
/*
* enable clocks with the CLK_ENABLE_HAND_OFF flag set
*
diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c
index 9d9af44..37e05d6 100644
--- a/drivers/clk/meson/gxbb.c
+++ b/drivers/clk/meson/gxbb.c
@@ -572,7 +572,7 @@
static MESON_GATE(gxbb_periphs, HHI_GCLK_MPEG0, 7);
static MESON_GATE(gxbb_spicc, HHI_GCLK_MPEG0, 8);
static MESON_GATE(gxbb_i2c, HHI_GCLK_MPEG0, 9);
-static MESON_GATE(gxbb_sar_adc, HHI_GCLK_MPEG0, 10);
+static MESON_GATE(gxbb_sana, HHI_GCLK_MPEG0, 10);
static MESON_GATE(gxbb_smart_card, HHI_GCLK_MPEG0, 11);
static MESON_GATE(gxbb_rng0, HHI_GCLK_MPEG0, 12);
static MESON_GATE(gxbb_uart0, HHI_GCLK_MPEG0, 13);
@@ -623,7 +623,7 @@
static MESON_GATE(gxbb_mmc_pclk, HHI_GCLK_MPEG2, 11);
static MESON_GATE(gxbb_dvin, HHI_GCLK_MPEG2, 12);
static MESON_GATE(gxbb_uart2, HHI_GCLK_MPEG2, 15);
-static MESON_GATE(gxbb_sana, HHI_GCLK_MPEG2, 22);
+static MESON_GATE(gxbb_sar_adc, HHI_GCLK_MPEG2, 22);
static MESON_GATE(gxbb_vpu_intr, HHI_GCLK_MPEG2, 25);
static MESON_GATE(gxbb_sec_ahb_ahb3_bridge, HHI_GCLK_MPEG2, 26);
static MESON_GATE(gxbb_clk81_a53, HHI_GCLK_MPEG2, 29);
diff --git a/drivers/clk/msm/clock-cpu-8939.c b/drivers/clk/msm/clock-cpu-8939.c
index dd1745a..5ba6bd0 100644
--- a/drivers/clk/msm/clock-cpu-8939.c
+++ b/drivers/clk/msm/clock-cpu-8939.c
@@ -130,7 +130,16 @@
static long cpu_clk_8939_round_rate(struct clk *c, unsigned long rate)
{
- return clk_round_rate(c->parent, rate);
+ int level;
+
+ for (level = 0; level < c->num_fmax; level++)
+ if (rate <= c->fmax[level])
+ break;
+
+ if (level == c->num_fmax)
+ return c->fmax[level-1];
+
+ return c->fmax[level];
}
static int cpu_clk_8939_set_rate(struct clk *c, unsigned long rate)
@@ -222,6 +231,12 @@
CLK_LIST(a53_bc_clk),
};
+static struct clk_lookup cpu_clocks_sdm429[] = {
+ CLK_LIST(a53ssmux_bc),
+ CLK_LIST(a53ssmux_cci),
+ CLK_LIST(a53_bc_clk),
+ CLK_LIST(cci_clk),
+};
static struct mux_div_clk *a53ssmux[] = {&a53ssmux_bc,
&a53ssmux_lc, &a53ssmux_cci};
@@ -691,6 +706,26 @@
return NOTIFY_DONE;
}
+static int clock_sdm429_pm_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ switch (event) {
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ clk_unprepare(&a53_bc_clk.c);
+ clk_unprepare(&cci_clk.c);
+ break;
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ clk_prepare(&a53_bc_clk.c);
+ clk_prepare(&cci_clk.c);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
static int clock_8939_pm_event_single_cluster(struct notifier_block *this,
unsigned long event, void *ptr)
{
@@ -713,6 +748,10 @@
.notifier_call = clock_8939_pm_event,
};
+static struct notifier_block clock_sdm429_pm_notifier = {
+ .notifier_call = clock_sdm429_pm_event,
+};
+
static struct notifier_block clock_8939_pm_notifier_single_cluster = {
.notifier_call = clock_8939_pm_event_single_cluster,
};
@@ -762,10 +801,14 @@
char prop_name[] = "qcom,speedX-bin-vX-XXX";
int mux_num;
bool single_cluster;
+ bool is_sdm429 = false;
single_cluster = of_property_read_bool(pdev->dev.of_node,
"qcom,num-cluster");
+ is_sdm429 = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,cpu-clock-sdm429");
+
get_speed_bin(pdev, &speed_bin, &version);
mux_num = single_cluster ? A53SS_MUX_LC:A53SS_MUX_NUM;
@@ -798,11 +841,35 @@
dev_info(&pdev->dev, "Safe voltage plan loaded.\n");
}
}
- if (single_cluster)
- rc = of_msm_clock_register(pdev->dev.of_node,
+
+ if (is_sdm429) {
+ rc = cpu_parse_devicetree(pdev, A53SS_MUX_CCI);
+ if (rc)
+ return rc;
+
+ snprintf(prop_name, ARRAY_SIZE(prop_name),
+ "qcom,speed%d-bin-v%d-%s",
+ speed_bin, version, mux_names[A53SS_MUX_CCI]);
+
+ rc = of_get_fmax_vdd_class(pdev, &cpuclk[A53SS_MUX_CCI]->c,
+ prop_name);
+ if (rc) {
+ dev_err(&pdev->dev, "Unable to load voltage plan %s!\n",
+ prop_name);
+ return rc;
+ }
+ }
+
+ if (single_cluster) {
+ if (is_sdm429)
+ rc = of_msm_clock_register(pdev->dev.of_node,
+ cpu_clocks_sdm429,
+ ARRAY_SIZE(cpu_clocks_sdm429));
+ else
+ rc = of_msm_clock_register(pdev->dev.of_node,
cpu_clocks_8939_single_cluster,
ARRAY_SIZE(cpu_clocks_8939_single_cluster));
- else
+ } else
rc = of_msm_clock_register(pdev->dev.of_node,
cpu_clocks_8939, ARRAY_SIZE(cpu_clocks_8939));
@@ -811,7 +878,7 @@
return rc;
}
- if (!single_cluster) {
+ if (!single_cluster || is_sdm429) {
rate = clk_get_rate(&cci_clk.c);
clk_set_rate(&cci_clk.c, rate);
}
@@ -832,7 +899,7 @@
for_each_online_cpu(cpu) {
WARN(clk_prepare_enable(&cpuclk[cpu/4]->c),
"Unable to turn on CPU clock");
- if (!single_cluster)
+ if (!single_cluster || is_sdm429)
clk_prepare_enable(&cci_clk.c);
}
put_online_cpus();
@@ -847,9 +914,13 @@
a53_lc_clk.hw_low_power_ctrl = true;
a53_bc_clk.hw_low_power_ctrl = true;
- if (single_cluster)
- register_pm_notifier(&clock_8939_pm_notifier_single_cluster);
- else
+ if (single_cluster) {
+ if (is_sdm429)
+ register_pm_notifier(&clock_sdm429_pm_notifier);
+ else
+ register_pm_notifier(
+ &clock_8939_pm_notifier_single_cluster);
+ } else
register_pm_notifier(&clock_8939_pm_notifier);
populate_opp_table(pdev, single_cluster);
@@ -863,6 +934,8 @@
static const struct of_device_id clock_a53_match_table[] = {
{.compatible = "qcom,cpu-clock-8939"},
{.compatible = "qcom,cpu-clock-8917"},
+ {.compatible = "qcom,cpu-clock-sdm429"},
+ {.compatible = "qcom,cpu-clock-sdm439"},
{}
};
@@ -890,9 +963,16 @@
if (!ofnode)
ofnode = of_find_compatible_node(NULL, NULL,
- "qcom,cpu-clock-gold");
+ "qcom,cpu-clock-8917");
if (!ofnode)
+ ofnode = of_find_compatible_node(NULL, NULL,
+ "qcom,cpu-clock-sdm439");
+
+ if (!ofnode)
+ ofnode = of_find_compatible_node(NULL, NULL,
+ "qcom,cpu-clock-sdm429");
+ if (!ofnode)
return 0;
single_cluster = of_property_read_bool(ofnode,
@@ -935,7 +1015,7 @@
#define SRC_SEL 0x4
#define SRC_DIV 0x3
-static void __init configure_enable_sr2_pll(void __iomem *base)
+static void __init configure_enable_sr2_pll(void __iomem *base, bool is_sdm439)
{
/* Disable Mode */
writel_relaxed(0x0, base + C0_PLL_MODE);
@@ -947,7 +1027,11 @@
/* Configure USER_CTL and CONFIG_CTL value */
writel_relaxed(0x0100000f, base + C0_PLL_USER_CTL);
- writel_relaxed(0x4c015765, base + C0_PLL_CONFIG_CTL);
+
+ if (is_sdm439)
+ writel_relaxed(0x44024665, base + C0_PLL_CONFIG_CTL);
+ else
+ writel_relaxed(0x4c015765, base + C0_PLL_CONFIG_CTL);
/* Enable PLL now */
writel_relaxed(0x2, base + C0_PLL_MODE);
@@ -965,13 +1049,21 @@
{
void __iomem *base;
int regval = 0, count;
+ bool is_sdm439 = false;
struct device_node *ofnode = of_find_compatible_node(NULL, NULL,
"qcom,cpu-clock-8939");
+
+ if (!ofnode)
+ ofnode = of_find_compatible_node(NULL, NULL,
+ "qcom,cpu-clock-sdm439");
+
if (!ofnode)
return 0;
+ is_sdm439 = of_device_is_compatible(ofnode, "qcom,cpu-clock-sdm439");
+
base = ioremap_nocache(APCS_C0_PLL, SZ_32);
- configure_enable_sr2_pll(base);
+ configure_enable_sr2_pll(base, is_sdm439);
iounmap(base);
base = ioremap_nocache(APCS_ALIAS0_CMD_RCGR, SZ_8);
diff --git a/drivers/clk/msm/clock-cpu-8953.c b/drivers/clk/msm/clock-cpu-8953.c
index 4ba2543..fb393c9 100644
--- a/drivers/clk/msm/clock-cpu-8953.c
+++ b/drivers/clk/msm/clock-cpu-8953.c
@@ -796,11 +796,13 @@
.priority = 1,
};
+static unsigned long pwrcl_boot_rate = 883200000;
+
static int clock_cpu_probe(struct platform_device *pdev)
{
int speed_bin, version, rc, cpu, mux_id;
char prop_name[] = "qcom,speedX-bin-vX-XXX";
- unsigned long ccirate, pwrcl_boot_rate = 883200000;
+ unsigned long ccirate;
get_speed_bin(pdev, &speed_bin, &version);
@@ -952,7 +954,7 @@
#define SRC_DIV 0x1
/* Configure PLL at Low frequency */
-unsigned long pwrcl_early_boot_rate = 652800000;
+static unsigned long pwrcl_early_boot_rate = 652800000;
static int __init cpu_clock_pwr_init(void)
{
@@ -968,9 +970,20 @@
clk_ops_variable_rate = clk_ops_variable_rate_pll_hwfsm;
clk_ops_variable_rate.list_registers = variable_pll_list_registers;
- __variable_rate_pll_init(&apcs_hf_pll.c);
- apcs_hf_pll.c.ops->set_rate(&apcs_hf_pll.c, pwrcl_early_boot_rate);
- clk_ops_variable_rate_pll.enable(&apcs_hf_pll.c);
+ /* Read back the L-val of PLL */
+ regval = readl_relaxed(virt_bases[APCS_C0_PLL_BASE] + APCS_PLL_L_VAL);
+ if (regval) {
+ pr_debug("PLL preconfigured for frequency %ld\n",
+ (19200000UL * regval));
+ pwrcl_boot_rate = (apcs_hf_pll.src_rate * regval);
+ apcs_hf_pll.c.ops->set_rate(&apcs_hf_pll.c, pwrcl_boot_rate);
+ clk_ops_variable_rate_pll_hwfsm.enable(&apcs_hf_pll.c);
+ } else {
+ __variable_rate_pll_init(&apcs_hf_pll.c);
+ apcs_hf_pll.c.ops->set_rate(&apcs_hf_pll.c,
+ pwrcl_early_boot_rate);
+ clk_ops_variable_rate_pll.enable(&apcs_hf_pll.c);
+ }
base = ioremap_nocache(APCS_ALIAS1_CMD_RCGR, SZ_8);
regval = readl_relaxed(base);
diff --git a/drivers/clk/msm/clock-gcc-8952.c b/drivers/clk/msm/clock-gcc-8952.c
index 9ac58bd..6d7727f 100644
--- a/drivers/clk/msm/clock-gcc-8952.c
+++ b/drivers/clk/msm/clock-gcc-8952.c
@@ -202,7 +202,10 @@
F_APCS_PLL( 921600000, 48, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL( 998400000, 52, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL(1094400000, 57, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1171200000, 61, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL(1209600000, 63, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1305600000, 68, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1459200000, 76, 0x0, 0x1, 0x0, 0x0, 0x0),
};
static struct pll_clk a53ss_c0_pll = {
@@ -257,6 +260,7 @@
F_APCS_PLL(1209600000, 63, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL(1248000000, 65, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL(1267200000, 66, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1305600000, 68, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL(1344000000, 70, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL(1401000000, 73, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL(1420800000, 74, 0x0, 0x1, 0x0, 0x0, 0x0),
@@ -266,6 +270,9 @@
F_APCS_PLL(1516800000, 79, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL(1536000000, 80, 0x0, 0x1, 0x0, 0x0, 0x0),
F_APCS_PLL(1651200000, 86, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1708800000, 89, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1804800000, 94, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(1958400000, 102, 0x0, 0x1, 0x0, 0x0, 0x0),
};
static struct pll_clk a53ss_c1_pll = {
@@ -754,6 +761,29 @@
F_END
};
+static struct clk_freq_tbl ftbl_gcc_oxili_gfx3d_clk_sdm439[] = {
+ F_SLEW( 19200000, FIXED_CLK_SRC, xo, 1, 0, 0),
+ F_SLEW( 50000000, FIXED_CLK_SRC, gpll0, 16, 0, 0),
+ F_SLEW( 80000000, FIXED_CLK_SRC, gpll0, 10, 0, 0),
+ F_SLEW( 100000000, FIXED_CLK_SRC, gpll0, 8, 0, 0),
+ F_SLEW( 160000000, FIXED_CLK_SRC, gpll0, 5, 0, 0),
+ F_SLEW( 200000000, FIXED_CLK_SRC, gpll0, 4, 0, 0),
+ F_SLEW( 216000000, FIXED_CLK_SRC, gpll6_aux, 5, 0, 0),
+ F_SLEW( 228570000, FIXED_CLK_SRC, gpll0, 3.5, 0, 0),
+ F_SLEW( 240000000, FIXED_CLK_SRC, gpll6_aux, 4.5, 0, 0),
+ F_SLEW( 259200000, 1296000000, gpll3, 2.5, 0, 0),
+ F_SLEW( 266670000, FIXED_CLK_SRC, gpll0, 3, 0, 0),
+ F_SLEW( 320000000, FIXED_CLK_SRC, gpll0, 2.5, 0, 0),
+ F_SLEW( 355200000, 710400000, gpll3, 1, 0, 0),
+ F_SLEW( 375000000, 750000000, gpll3, 1, 0, 0),
+ F_SLEW( 400000000, FIXED_CLK_SRC, gpll0, 2, 0, 0),
+ F_SLEW( 450000000, 900000000, gpll3, 1, 0, 0),
+ F_SLEW( 510000000, 1020000000, gpll3, 1, 0, 0),
+ F_SLEW( 560000000, 1120000000, gpll3, 1, 0, 0),
+ F_SLEW( 650000000, 1300000000, gpll3, 1, 0, 0),
+ F_END
+};
+
static struct clk_freq_tbl ftbl_gcc_oxili_gfx3d_clk_8937_475MHz[] = {
F_SLEW( 19200000, FIXED_CLK_SRC, xo, 1, 0, 0),
F_SLEW( 50000000, FIXED_CLK_SRC, gpll0, 16, 0, 0),
@@ -4046,6 +4076,26 @@
CLK_LIST(gcc_oxili_timer_clk),
};
+static struct clk_lookup msm_clocks_lookup_sdm429[] = {
+ CLK_LIST(gpll0_clk_src_8937),
+ CLK_LIST(gpll0_ao_clk_src_8937),
+ CLK_LIST(gpll0_sleep_clk_src),
+ CLK_LIST(esc1_clk_src),
+ CLK_LIST(gcc_mdss_esc1_clk),
+ CLK_LIST(gcc_dcc_clk),
+ CLK_LIST(gcc_oxili_aon_clk),
+ CLK_LIST(gcc_qdss_dap_clk),
+ CLK_LIST(blsp1_qup1_i2c_apps_clk_src),
+ CLK_LIST(blsp1_qup1_spi_apps_clk_src),
+ CLK_LIST(gcc_blsp1_qup1_i2c_apps_clk),
+ CLK_LIST(gcc_blsp1_qup1_spi_apps_clk),
+ CLK_LIST(blsp2_qup4_i2c_apps_clk_src),
+ CLK_LIST(blsp2_qup4_spi_apps_clk_src),
+ CLK_LIST(gcc_blsp2_qup4_i2c_apps_clk),
+ CLK_LIST(gcc_blsp2_qup4_spi_apps_clk),
+ CLK_LIST(gcc_oxili_timer_clk),
+};
+
static struct clk_lookup msm_clocks_lookup_8917[] = {
CLK_LIST(gpll0_clk_src_8937),
CLK_LIST(gpll0_ao_clk_src_8937),
@@ -4329,6 +4379,8 @@
bool compat_bin2 = false;
bool compat_bin3 = false;
bool compat_bin4 = false;
+ bool compat_bin5 = false;
+ bool compat_bin6 = false;
compat_bin = of_device_is_compatible(pdev->dev.of_node,
"qcom,gcc-8937");
@@ -4342,11 +4394,17 @@
compat_bin4 = of_device_is_compatible(pdev->dev.of_node,
"qcom,gcc-8920");
+ compat_bin5 = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,gcc-sdm429");
+
+ compat_bin6 = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,gcc-sdm439");
+
ret = vote_bimc(&bimc_clk, INT_MAX);
if (ret < 0)
return ret;
- if (compat_bin2 || compat_bin4)
+ if (compat_bin2 || compat_bin4 || compat_bin5)
nbases = APCS_C0_PLL_BASE;
ret = get_mmio_addr(pdev, nbases);
@@ -4374,7 +4432,7 @@
return PTR_ERR(vdd_dig.regulator[0]);
}
- if (!compat_bin2 && !compat_bin4) {
+ if (!compat_bin2 && !compat_bin4 && !compat_bin5) {
vdd_sr2_pll.regulator[0] = devm_regulator_get(&pdev->dev,
"vdd_sr2_pll");
if (IS_ERR(vdd_sr2_pll.regulator[0])) {
@@ -4417,16 +4475,43 @@
regval |= BIT(0);
writel_relaxed(regval, GCC_REG_BASE(APCS_GPLL_ENA_VOTE));
- if (compat_bin || compat_bin3) {
+ if (compat_bin || compat_bin3 || compat_bin5 || compat_bin6) {
gpll0_clk_src.c.parent = &gpll0_clk_src_8937.c;
gpll0_ao_clk_src.c.parent = &gpll0_ao_clk_src_8937.c;
/* Oxili Ocmem in GX rail: OXILI_GMEM_CLAMP_IO */
regval = readl_relaxed(GCC_REG_BASE(GX_DOMAIN_MISC));
regval &= ~BIT(0);
writel_relaxed(regval, GCC_REG_BASE(GX_DOMAIN_MISC));
- get_speed_bin(pdev, &speed_bin);
+
+ if (compat_bin5 || compat_bin6)
+ speed_bin = 0;
+ else
+ get_speed_bin(pdev, &speed_bin);
+
override_for_8937(speed_bin);
+ if (compat_bin5 || compat_bin6) {
+ vdd_dig.num_levels = VDD_DIG_NUM_439;
+ vdd_dig.cur_level = VDD_DIG_NUM_439;
+ vdd_sr2_pll.num_levels = VDD_SR2_PLL_NUM_439;
+ vdd_sr2_pll.cur_level = VDD_SR2_PLL_NUM_439;
+ vdd_hf_pll.num_levels = VDD_HF_PLL_NUM_439;
+ vdd_hf_pll.cur_level = VDD_HF_PLL_NUM_439;
+
+ gpll3_clk_src.vco_tbl = p_vco;
+ gpll3_clk_src.num_vco = ARRAY_SIZE(p_vco);
+ gpll3_clk_src.c.fmax[VDD_DIG_LOW] = 800000000;
+ gpll3_clk_src.c.fmax[VDD_DIG_NOMINAL] = 1400000000;
+ gfx3d_clk_src.freq_tbl =
+ ftbl_gcc_oxili_gfx3d_clk_sdm439;
+
+ gfx3d_clk_src.c.fmax[VDD_DIG_LOWER] = 320000000;
+ gfx3d_clk_src.c.fmax[VDD_DIG_LOW] = 400000000;
+ gfx3d_clk_src.c.fmax[VDD_DIG_NOMINAL] = 510000000;
+ gfx3d_clk_src.c.fmax[VDD_DIG_NOM_PLUS] = 560000000;
+ gfx3d_clk_src.c.fmax[VDD_DIG_HIGH] = 650000000;
+ }
+
if (compat_bin3) {
if (speed_bin) {
gfx3d_clk_src.freq_tbl =
@@ -4475,7 +4560,7 @@
if (ret)
return ret;
- if (compat_bin)
+ if (compat_bin || compat_bin6)
ret = of_msm_clock_register(pdev->dev.of_node,
msm_clocks_lookup_8937,
ARRAY_SIZE(msm_clocks_lookup_8937));
@@ -4491,6 +4576,10 @@
ret = of_msm_clock_register(pdev->dev.of_node,
msm_clocks_lookup_8920,
ARRAY_SIZE(msm_clocks_lookup_8920));
+ else if (compat_bin5)
+ ret = of_msm_clock_register(pdev->dev.of_node,
+ msm_clocks_lookup_sdm429,
+ ARRAY_SIZE(msm_clocks_lookup_sdm429));
else
ret = of_msm_clock_register(pdev->dev.of_node,
msm_clocks_lookup_8952,
@@ -4513,7 +4602,7 @@
clk_prepare_enable(&xo_a_clk_src.c);
- if (!compat_bin && !compat_bin3) {
+ if (!compat_bin && !compat_bin3 && !compat_bin5 && !compat_bin6) {
/* Configure Sleep and Wakeup cycles for GMEM clock */
regval = readl_relaxed(GCC_REG_BASE(OXILI_GMEM_CBCR));
regval ^= 0xFF0;
@@ -4546,6 +4635,8 @@
{ .compatible = "qcom,gcc-8917" },
{ .compatible = "qcom,gcc-8940" },
{ .compatible = "qcom,gcc-8920" },
+ { .compatible = "qcom,gcc-sdm439" },
+ { .compatible = "qcom,gcc-sdm429" },
{}
};
diff --git a/drivers/clk/msm/mdss/Makefile b/drivers/clk/msm/mdss/Makefile
index 6285714..3b884dc 100644
--- a/drivers/clk/msm/mdss/Makefile
+++ b/drivers/clk/msm/mdss/Makefile
@@ -5,3 +5,5 @@
obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-8996.o
obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-8996-util.o
obj-$(CONFIG_MSM_MDSS_PLL) += mdss-hdmi-pll-8996.o
+obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-12nm.o
+obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-12nm-util.o
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-12nm-util.c b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm-util.c
new file mode 100644
index 0000000..f2ed36c
--- /dev/null
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm-util.c
@@ -0,0 +1,820 @@
+/* Copyright (c) 2018, 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/kernel.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/delay.h>
+#include <linux/clk/msm-clock-generic.h>
+
+#include "mdss-pll.h"
+#include "mdss-dsi-pll.h"
+#include "mdss-dsi-pll-12nm.h"
+
+#define DSI_PLL_POLL_MAX_READS 15
+#define DSI_PLL_POLL_TIMEOUT_US 1000
+
+int pixel_div_set_div(struct div_clk *clk, int div)
+{
+ struct mdss_pll_resources *pll = clk->priv;
+ struct dsi_pll_db *pdb;
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+
+ /* Programming during vco_prepare. Keep this value */
+ pdb->param.pixel_divhf = (div - 1);
+
+ pr_debug("ndx=%d div=%d divhf=%d\n",
+ pll->index, div, pdb->param.pixel_divhf);
+
+ return 0;
+}
+
+int pixel_div_get_div(struct div_clk *clk)
+{
+ u32 div;
+ int rc;
+ struct mdss_pll_resources *pll = clk->priv;
+
+ if (is_gdsc_disabled(pll))
+ return 0;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_SSC9);
+ div &= 0x7f;
+ pr_debug("pixel_div = %d\n", (div+1));
+
+ mdss_pll_resource_enable(pll, false);
+
+ return (div + 1);
+}
+
+int set_post_div_mux_sel(struct mux_clk *clk, int sel)
+{
+ struct mdss_pll_resources *pll = clk->priv;
+ struct dsi_pll_db *pdb;
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+
+ /* Programming during vco_prepare. Keep this value */
+ pdb->param.post_div_mux = sel;
+
+ pr_debug("ndx=%d post_div_mux_sel=%d p_div=%d\n",
+ pll->index, sel, (u32) BIT(sel));
+
+ return 0;
+}
+
+int get_post_div_mux_sel(struct mux_clk *clk)
+{
+ u32 sel = 0;
+ u32 vco_cntrl = 0, cpbias_cntrl = 0;
+ int rc;
+ struct mdss_pll_resources *pll = clk->priv;
+
+ if (is_gdsc_disabled(pll))
+ return 0;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ vco_cntrl = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_PLL_VCO_CTRL);
+ vco_cntrl &= 0x30;
+
+ cpbias_cntrl = MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_CHAR_PUMP_BIAS_CTRL);
+ cpbias_cntrl = ((cpbias_cntrl >> 6) & 0x1);
+
+ if (cpbias_cntrl == 0) {
+ if (vco_cntrl == 0x00)
+ sel = 0;
+ else if (vco_cntrl == 0x10)
+ sel = 2;
+ else if (vco_cntrl == 0x20)
+ sel = 3;
+ else if (vco_cntrl == 0x30)
+ sel = 4;
+ } else if (cpbias_cntrl == 1) {
+ if (vco_cntrl == 0x30)
+ sel = 2;
+ else if (vco_cntrl == 0x00)
+ sel = 5;
+ }
+
+ mdss_pll_resource_enable(pll, false);
+
+ return sel;
+}
+
+int set_gp_mux_sel(struct mux_clk *clk, int sel)
+{
+ struct mdss_pll_resources *pll = clk->priv;
+ struct dsi_pll_db *pdb;
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+
+ /* Programming during vco_prepare. Keep this value */
+ pdb->param.gp_div_mux = sel;
+
+ pr_debug("ndx=%d gp_div_mux_sel=%d gp_cntrl=%d\n",
+ pll->index, sel, (u32) BIT(sel));
+
+ return 0;
+}
+
+int get_gp_mux_sel(struct mux_clk *clk)
+{
+ u32 sel = 0;
+ int rc;
+ struct mdss_pll_resources *pll = clk->priv;
+
+ if (is_gdsc_disabled(pll))
+ return 0;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ sel = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_PLL_CTRL);
+ sel = (sel >> 5) & 0x7;
+ pr_debug("gp_cntrl = %d\n", sel);
+
+ mdss_pll_resource_enable(pll, false);
+
+ return sel;
+}
+
+static bool pll_is_pll_locked_12nm(struct mdss_pll_resources *pll)
+{
+ u32 status;
+ bool pll_locked;
+
+ /* poll for PLL ready status */
+ if (readl_poll_timeout_atomic((pll->pll_base +
+ DSIPHY_STAT0),
+ status,
+ ((status & BIT(1)) > 0),
+ DSI_PLL_POLL_MAX_READS,
+ DSI_PLL_POLL_TIMEOUT_US)) {
+ pr_err("DSI PLL ndx=%d status=%x failed to Lock\n",
+ pll->index, status);
+ pll_locked = false;
+ } else {
+ pll_locked = true;
+ }
+
+ return pll_locked;
+}
+
+int dsi_pll_enable_seq_12nm(struct mdss_pll_resources *pll)
+{
+ int rc = 0;
+ struct dsi_pll_db *pdb;
+ void __iomem *pll_base;
+
+ if (!pll) {
+ pr_err("Invalid PLL resources\n");
+ return -EINVAL;
+ }
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+ if (!pdb) {
+ pr_err("No priv found\n");
+ return -EINVAL;
+ }
+
+ pll_base = pll->pll_base;
+
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SYS_CTRL, 0x49);
+ wmb(); /* make sure register committed before enabling branch clocks */
+ udelay(5); /* h/w recommended delay */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SYS_CTRL, 0xc9);
+ wmb(); /* make sure register committed before enabling branch clocks */
+ udelay(50); /* h/w recommended delay */
+
+ if (!pll_is_pll_locked_12nm(pll)) {
+ pr_err("DSI PLL ndx=%d lock failed!\n",
+ pll->index);
+ rc = -EINVAL;
+ goto init_lock_err;
+ }
+
+ pr_debug("DSI PLL ndx:%d Locked!\n", pll->index);
+
+init_lock_err:
+ return rc;
+}
+
+static int dsi_pll_enable(struct clk *c)
+{
+ int i, rc = 0;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+
+ /* Try all enable sequences until one succeeds */
+ for (i = 0; i < vco->pll_en_seq_cnt; i++) {
+ rc = vco->pll_enable_seqs[i](pll);
+ pr_debug("DSI PLL %s after sequence #%d\n",
+ rc ? "unlocked" : "locked", i + 1);
+ if (!rc)
+ break;
+ }
+
+ if (rc)
+ pr_err("ndx=%d DSI PLL failed to lock\n", pll->index);
+ else
+ pll->pll_on = true;
+
+ return rc;
+}
+
+static int dsi_pll_relock(struct mdss_pll_resources *pll)
+{
+ void __iomem *pll_base = pll->pll_base;
+ u32 data = 0;
+ int rc = 0;
+
+ data = MDSS_PLL_REG_R(pll_base, DSIPHY_PLL_POWERUP_CTRL);
+ data &= ~BIT(1); /* remove ONPLL_OVR_EN bit */
+ data |= 0x1; /* set ONPLL_OVN to 0x1 */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_POWERUP_CTRL, data);
+ ndelay(500); /* h/w recommended delay */
+
+ if (!pll_is_pll_locked_12nm(pll)) {
+ pr_err("DSI PLL ndx=%d lock failed!\n",
+ pll->index);
+ rc = -EINVAL;
+ goto relock_err;
+ }
+ ndelay(50); /* h/w recommended delay */
+
+ data = MDSS_PLL_REG_R(pll_base, DSIPHY_PLL_CTRL);
+ data |= 0x01; /* set CLK_SEL bits to 0x1 */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_CTRL, data);
+ ndelay(500); /* h/w recommended delay */
+ wmb(); /* make sure register committed before enabling branch clocks */
+ pll->pll_on = true;
+relock_err:
+ return rc;
+}
+
+static void dsi_pll_disable(struct clk *c)
+{
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+ void __iomem *pll_base = pll->pll_base;
+ u32 data = 0;
+
+ if (!pll->pll_on &&
+ mdss_pll_resource_enable(pll, true)) {
+ pr_err("Failed to enable mdss dsi pll=%d\n", pll->index);
+ return;
+ }
+
+ data = MDSS_PLL_REG_R(pll_base, DSIPHY_SSC0);
+ data &= ~BIT(6); /* disable GP_CLK_EN */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC0, data);
+ ndelay(500); /* h/w recommended delay */
+
+ data = MDSS_PLL_REG_R(pll_base, DSIPHY_PLL_CTRL);
+ data &= ~0x03; /* remove CLK_SEL bits */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_CTRL, data);
+ ndelay(500); /* h/w recommended delay */
+
+ data = MDSS_PLL_REG_R(pll_base, DSIPHY_PLL_POWERUP_CTRL);
+ data &= ~0x1; /* remove ONPLL_OVR bit */
+ data |= BIT(1); /* set ONPLL_OVR_EN to 0x1 */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_POWERUP_CTRL, data);
+ ndelay(500); /* h/w recommended delay */
+ wmb(); /* make sure register committed before disabling branch clocks */
+ pll->handoff_resources = false;
+
+ mdss_pll_resource_enable(pll, false);
+
+ pll->pll_on = false;
+
+ pr_debug("DSI PLL ndx=%d Disabled\n", pll->index);
+}
+
+static u32 __mdss_dsi_get_hsfreqrange(u64 target_freq)
+{
+ u64 bitclk_rate_mhz = div_u64((target_freq * 2), 1000000);
+
+ if (bitclk_rate_mhz >= 80 && bitclk_rate_mhz < 90)
+ return 0x00;
+ else if (bitclk_rate_mhz >= 90 && bitclk_rate_mhz < 100)
+ return 0x10;
+ else if (bitclk_rate_mhz >= 100 && bitclk_rate_mhz < 110)
+ return 0x20;
+ else if (bitclk_rate_mhz >= 110 && bitclk_rate_mhz < 120)
+ return 0x30;
+ else if (bitclk_rate_mhz >= 120 && bitclk_rate_mhz < 130)
+ return 0x01;
+ else if (bitclk_rate_mhz >= 130 && bitclk_rate_mhz < 140)
+ return 0x11;
+ else if (bitclk_rate_mhz >= 140 && bitclk_rate_mhz < 150)
+ return 0x21;
+ else if (bitclk_rate_mhz >= 150 && bitclk_rate_mhz < 160)
+ return 0x31;
+ else if (bitclk_rate_mhz >= 160 && bitclk_rate_mhz < 170)
+ return 0x02;
+ else if (bitclk_rate_mhz >= 170 && bitclk_rate_mhz < 180)
+ return 0x12;
+ else if (bitclk_rate_mhz >= 180 && bitclk_rate_mhz < 190)
+ return 0x22;
+ else if (bitclk_rate_mhz >= 190 && bitclk_rate_mhz < 205)
+ return 0x32;
+ else if (bitclk_rate_mhz >= 205 && bitclk_rate_mhz < 220)
+ return 0x03;
+ else if (bitclk_rate_mhz >= 220 && bitclk_rate_mhz < 235)
+ return 0x13;
+ else if (bitclk_rate_mhz >= 235 && bitclk_rate_mhz < 250)
+ return 0x23;
+ else if (bitclk_rate_mhz >= 250 && bitclk_rate_mhz < 275)
+ return 0x33;
+ else if (bitclk_rate_mhz >= 275 && bitclk_rate_mhz < 300)
+ return 0x04;
+ else if (bitclk_rate_mhz >= 300 && bitclk_rate_mhz < 325)
+ return 0x14;
+ else if (bitclk_rate_mhz >= 325 && bitclk_rate_mhz < 350)
+ return 0x25;
+ else if (bitclk_rate_mhz >= 350 && bitclk_rate_mhz < 400)
+ return 0x35;
+ else if (bitclk_rate_mhz >= 400 && bitclk_rate_mhz < 450)
+ return 0x05;
+ else if (bitclk_rate_mhz >= 450 && bitclk_rate_mhz < 500)
+ return 0x16;
+ else if (bitclk_rate_mhz >= 500 && bitclk_rate_mhz < 550)
+ return 0x26;
+ else if (bitclk_rate_mhz >= 550 && bitclk_rate_mhz < 600)
+ return 0x37;
+ else if (bitclk_rate_mhz >= 600 && bitclk_rate_mhz < 650)
+ return 0x07;
+ else if (bitclk_rate_mhz >= 650 && bitclk_rate_mhz < 700)
+ return 0x18;
+ else if (bitclk_rate_mhz >= 700 && bitclk_rate_mhz < 750)
+ return 0x28;
+ else if (bitclk_rate_mhz >= 750 && bitclk_rate_mhz < 800)
+ return 0x39;
+ else if (bitclk_rate_mhz >= 800 && bitclk_rate_mhz < 850)
+ return 0x09;
+ else if (bitclk_rate_mhz >= 850 && bitclk_rate_mhz < 900)
+ return 0x19;
+ else if (bitclk_rate_mhz >= 900 && bitclk_rate_mhz < 950)
+ return 0x29;
+ else if (bitclk_rate_mhz >= 950 && bitclk_rate_mhz < 1000)
+ return 0x3a;
+ else if (bitclk_rate_mhz >= 1000 && bitclk_rate_mhz < 1050)
+ return 0x0a;
+ else if (bitclk_rate_mhz >= 1050 && bitclk_rate_mhz < 1100)
+ return 0x1a;
+ else if (bitclk_rate_mhz >= 1100 && bitclk_rate_mhz < 1150)
+ return 0x2a;
+ else if (bitclk_rate_mhz >= 1150 && bitclk_rate_mhz < 1200)
+ return 0x3b;
+ else if (bitclk_rate_mhz >= 1200 && bitclk_rate_mhz < 1250)
+ return 0x0b;
+ else if (bitclk_rate_mhz >= 1250 && bitclk_rate_mhz < 1300)
+ return 0x1b;
+ else if (bitclk_rate_mhz >= 1300 && bitclk_rate_mhz < 1350)
+ return 0x2b;
+ else if (bitclk_rate_mhz >= 1350 && bitclk_rate_mhz < 1400)
+ return 0x3c;
+ else if (bitclk_rate_mhz >= 1400 && bitclk_rate_mhz < 1450)
+ return 0x0c;
+ else if (bitclk_rate_mhz >= 1450 && bitclk_rate_mhz < 1500)
+ return 0x1c;
+ else if (bitclk_rate_mhz >= 1500 && bitclk_rate_mhz < 1550)
+ return 0x2c;
+ else if (bitclk_rate_mhz >= 1550 && bitclk_rate_mhz < 1600)
+ return 0x3d;
+ else if (bitclk_rate_mhz >= 1600 && bitclk_rate_mhz < 1650)
+ return 0x0d;
+ else if (bitclk_rate_mhz >= 1650 && bitclk_rate_mhz < 1700)
+ return 0x1d;
+ else if (bitclk_rate_mhz >= 1700 && bitclk_rate_mhz < 1750)
+ return 0x2e;
+ else if (bitclk_rate_mhz >= 1750 && bitclk_rate_mhz < 1800)
+ return 0x3e;
+ else if (bitclk_rate_mhz >= 1800 && bitclk_rate_mhz < 1850)
+ return 0x0e;
+ else if (bitclk_rate_mhz >= 1850 && bitclk_rate_mhz < 1900)
+ return 0x1e;
+ else if (bitclk_rate_mhz >= 1900 && bitclk_rate_mhz < 1950)
+ return 0x2f;
+ else if (bitclk_rate_mhz >= 1950 && bitclk_rate_mhz < 2000)
+ return 0x3f;
+ else if (bitclk_rate_mhz >= 2000 && bitclk_rate_mhz < 2050)
+ return 0x0f;
+ else if (bitclk_rate_mhz >= 2050 && bitclk_rate_mhz < 2100)
+ return 0x40;
+ else if (bitclk_rate_mhz >= 2100 && bitclk_rate_mhz < 2150)
+ return 0x41;
+ else if (bitclk_rate_mhz >= 2150 && bitclk_rate_mhz < 2200)
+ return 0x42;
+ else if (bitclk_rate_mhz >= 2200 && bitclk_rate_mhz < 2250)
+ return 0x43;
+ else if (bitclk_rate_mhz >= 2250 && bitclk_rate_mhz < 2300)
+ return 0x44;
+ else if (bitclk_rate_mhz >= 2300 && bitclk_rate_mhz < 2350)
+ return 0x45;
+ else if (bitclk_rate_mhz >= 2350 && bitclk_rate_mhz < 2400)
+ return 0x46;
+ else if (bitclk_rate_mhz >= 2400 && bitclk_rate_mhz < 2450)
+ return 0x47;
+ else if (bitclk_rate_mhz >= 2450 && bitclk_rate_mhz < 2500)
+ return 0x48;
+ else
+ return 0x49;
+}
+
+static void __mdss_dsi_get_pll_vco_cntrl(u64 target_freq, u32 post_div_mux,
+ u32 *vco_cntrl, u32 *cpbias_cntrl)
+{
+ u64 target_freq_mhz = div_u64(target_freq, 1000000);
+ u32 p_div = BIT(post_div_mux);
+
+ if (p_div == 1) {
+ *vco_cntrl = 0x00;
+ *cpbias_cntrl = 0;
+ } else if (p_div == 2) {
+ *vco_cntrl = 0x30;
+ *cpbias_cntrl = 1;
+ } else if (p_div == 4) {
+ *vco_cntrl = 0x10;
+ *cpbias_cntrl = 0;
+ } else if (p_div == 8) {
+ *vco_cntrl = 0x20;
+ *cpbias_cntrl = 0;
+ } else if (p_div == 16) {
+ *vco_cntrl = 0x30;
+ *cpbias_cntrl = 0;
+ } else {
+ *vco_cntrl = 0x00;
+ *cpbias_cntrl = 1;
+ }
+
+ if (target_freq_mhz <= 1250 && target_freq_mhz >= 1092)
+ *vco_cntrl = *vco_cntrl | 2;
+ else if (target_freq_mhz < 1092 && target_freq_mhz >= 950)
+ *vco_cntrl = *vco_cntrl | 3;
+ else if (target_freq_mhz < 950 && target_freq_mhz >= 712)
+ *vco_cntrl = *vco_cntrl | 1;
+ else if (target_freq_mhz < 712 && target_freq_mhz >= 546)
+ *vco_cntrl = *vco_cntrl | 2;
+ else if (target_freq_mhz < 546 && target_freq_mhz >= 475)
+ *vco_cntrl = *vco_cntrl | 3;
+ else if (target_freq_mhz < 475 && target_freq_mhz >= 356)
+ *vco_cntrl = *vco_cntrl | 1;
+ else if (target_freq_mhz < 356 && target_freq_mhz >= 273)
+ *vco_cntrl = *vco_cntrl | 2;
+ else if (target_freq_mhz < 273 && target_freq_mhz >= 237)
+ *vco_cntrl = *vco_cntrl | 3;
+ else if (target_freq_mhz < 237 && target_freq_mhz >= 178)
+ *vco_cntrl = *vco_cntrl | 1;
+ else if (target_freq_mhz < 178 && target_freq_mhz >= 136)
+ *vco_cntrl = *vco_cntrl | 2;
+ else if (target_freq_mhz < 136 && target_freq_mhz >= 118)
+ *vco_cntrl = *vco_cntrl | 3;
+ else if (target_freq_mhz < 118 && target_freq_mhz >= 89)
+ *vco_cntrl = *vco_cntrl | 1;
+ else if (target_freq_mhz < 89 && target_freq_mhz >= 68)
+ *vco_cntrl = *vco_cntrl | 2;
+ else if (target_freq_mhz < 68 && target_freq_mhz >= 57)
+ *vco_cntrl = *vco_cntrl | 3;
+ else if (target_freq_mhz < 57 && target_freq_mhz >= 44)
+ *vco_cntrl = *vco_cntrl | 1;
+ else
+ *vco_cntrl = *vco_cntrl | 2;
+}
+
+static u32 __mdss_dsi_get_osc_freq_target(u64 target_freq)
+{
+ u64 target_freq_mhz = div_u64(target_freq, 1000000);
+
+ if (target_freq_mhz <= 1000)
+ return 1315;
+ else if (target_freq_mhz > 1000 && target_freq_mhz <= 1500)
+ return 1839;
+ else
+ return 0;
+}
+
+static u64 __mdss_dsi_pll_get_m_div(u64 vco_rate)
+{
+ return div_u64((vco_rate * 4), 19200000);
+}
+
+static u32 __mdss_dsi_get_fsm_ovr_ctrl(u64 target_freq)
+{
+ u64 bitclk_rate_mhz = div_u64((target_freq * 2), 1000000);
+
+ if (bitclk_rate_mhz > 1500 && bitclk_rate_mhz <= 2500)
+ return 0;
+ else
+ return BIT(6);
+}
+
+static void mdss_dsi_pll_12nm_calc_reg(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ struct dsi_pll_param *param = &pdb->param;
+ u64 target_freq = 0;
+
+ target_freq = div_u64(pll->vco_current_rate,
+ BIT(pdb->param.post_div_mux));
+
+ param->hsfreqrange = __mdss_dsi_get_hsfreqrange(target_freq);
+ __mdss_dsi_get_pll_vco_cntrl(target_freq, param->post_div_mux,
+ ¶m->vco_cntrl, ¶m->cpbias_cntrl);
+ param->osc_freq_target = __mdss_dsi_get_osc_freq_target(target_freq);
+ param->m_div = (u32) __mdss_dsi_pll_get_m_div(pll->vco_current_rate);
+ param->fsm_ovr_ctrl = __mdss_dsi_get_fsm_ovr_ctrl(target_freq);
+ param->prop_cntrl = 0x05;
+ param->int_cntrl = 0x00;
+ param->gmp_cntrl = 0x1;
+}
+
+static void pll_db_commit_12nm(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ void __iomem *pll_base = pll->pll_base;
+ struct dsi_pll_param *param = &pdb->param;
+ char data = 0;
+
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CTRL0, 0x01);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_CTRL, 0x05);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SLEWRATE_DDL_LOOP_CTRL, 0x01);
+
+ data = ((param->hsfreqrange & 0x7f) | BIT(7));
+ MDSS_PLL_REG_W(pll_base, DSIPHY_HS_FREQ_RAN_SEL, data);
+
+ data = ((param->vco_cntrl & 0x3f) | BIT(6));
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_CTRL, data);
+
+ data = (param->osc_freq_target & 0x7f);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SLEWRATE_DDL_CYC_FRQ_ADJ_0, data);
+
+ data = ((param->osc_freq_target & 0xf80) >> 7);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SLEWRATE_DDL_CYC_FRQ_ADJ_1, data);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_INPUT_LOOP_DIV_RAT_CTRL, 0x30);
+
+ data = (param->m_div & 0x3f);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_LOOP_DIV_RATIO_0, data);
+
+ data = ((param->m_div & 0xfc0) >> 6);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_LOOP_DIV_RATIO_1, data);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_INPUT_DIV_PLL_OVR, 0x60);
+
+ data = (param->prop_cntrl & 0x3f);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PROP_CHRG_PUMP_CTRL, data);
+
+ data = (param->int_cntrl & 0x3f);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_INTEG_CHRG_PUMP_CTRL, data);
+
+ data = ((param->gmp_cntrl & 0x3) << 4);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_GMP_CTRL_DIG_TST, data);
+
+ data = ((param->cpbias_cntrl & 0x1) << 6) | BIT(4);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_CHAR_PUMP_BIAS_CTRL, data);
+
+ data = ((param->gp_div_mux & 0x7) << 5) | 0x5;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_CTRL, data);
+
+ data = (param->pixel_divhf & 0x7f);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC9, data);
+
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_ANA_PROG_CTRL, 0x03);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_ANA_TST_LOCK_ST_OVR_CTRL, 0x50);
+ MDSS_PLL_REG_W(pll_base,
+ DSIPHY_SLEWRATE_FSM_OVR_CTRL, param->fsm_ovr_ctrl);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PHA_ERR_CTRL_0, 0x01);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PHA_ERR_CTRL_1, 0x00);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_LOCK_FILTER, 0xff);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_UNLOCK_FILTER, 0x03);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PRO_DLY_RELOCK, 0x0c);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_LOCK_DET_MODE_SEL, 0x02);
+
+ pr_debug("pll:%d\n", pll->index);
+ wmb(); /* make sure register committed before preparing the clocks */
+}
+
+int pll_vco_set_rate_12nm(struct clk *c, unsigned long rate)
+{
+ int rc = 0;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+ struct dsi_pll_db *pdb;
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+ if (!pdb) {
+ pr_err("pll pdb not found\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ pr_debug("%s: ndx=%d rate=%lu\n", __func__, pll->index, rate);
+
+ pll->vco_current_rate = rate;
+ pll->vco_ref_clk_rate = vco->ref_clk_rate;
+error:
+ return rc;
+}
+
+static unsigned long pll_vco_get_rate_12nm(struct clk *c)
+{
+ u64 vco_rate = 0;
+ u32 m_div_5_0 = 0, m_div_11_6 = 0, m_div = 0;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ u64 ref_clk = vco->ref_clk_rate;
+ int rc;
+ struct mdss_pll_resources *pll = vco->priv;
+
+ if (is_gdsc_disabled(pll))
+ return 0;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll=%d\n", pll->index);
+ return rc;
+ }
+
+ m_div_5_0 = MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_LOOP_DIV_RATIO_0);
+ m_div_5_0 &= 0x3f;
+ pr_debug("m_div_5_0 = 0x%x\n", m_div_5_0);
+
+ m_div_11_6 = MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_LOOP_DIV_RATIO_1);
+ m_div_11_6 &= 0x3f;
+ pr_debug("m_div_11_6 = 0x%x\n", m_div_11_6);
+
+ m_div = ((m_div_11_6 << 6) | (m_div_5_0));
+
+ vco_rate = div_u64((ref_clk * m_div), 4);
+
+ pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate);
+
+ mdss_pll_resource_enable(pll, false);
+
+ return (unsigned long)vco_rate;
+}
+
+long pll_vco_round_rate_12nm(struct clk *c, unsigned long rate)
+{
+ unsigned long rrate = rate;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+
+ if (rate < vco->min_rate)
+ rrate = vco->min_rate;
+ if (rate > vco->max_rate)
+ rrate = vco->max_rate;
+
+ return rrate;
+}
+
+enum handoff pll_vco_handoff_12nm(struct clk *c)
+{
+ int rc;
+ enum handoff ret = HANDOFF_DISABLED_CLK;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+
+ if (is_gdsc_disabled(pll))
+ return HANDOFF_DISABLED_CLK;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll=%d\n", pll->index);
+ return ret;
+ }
+
+ if (pll_is_pll_locked_12nm(pll)) {
+ pll->handoff_resources = true;
+ pll->pll_on = true;
+ c->rate = pll_vco_get_rate_12nm(c);
+ ret = HANDOFF_ENABLED_CLK;
+ } else {
+ mdss_pll_resource_enable(pll, false);
+ }
+
+ return ret;
+}
+
+int pll_vco_prepare_12nm(struct clk *c)
+{
+ int rc = 0;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+ struct dsi_pll_db *pdb;
+ u32 data = 0;
+
+ if (!pll) {
+ pr_err("Dsi pll resources are not available\n");
+ return -EINVAL;
+ }
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+ if (!pdb) {
+ pr_err("No prov found\n");
+ return -EINVAL;
+ }
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("ndx=%d Failed to enable mdss dsi pll resources\n",
+ pll->index);
+ return rc;
+ }
+
+ if ((pll->vco_cached_rate != 0)
+ && (pll->vco_cached_rate == c->rate)) {
+ rc = c->ops->set_rate(c, pll->vco_cached_rate);
+ if (rc) {
+ pr_err("index=%d vco_set_rate failed. rc=%d\n",
+ rc, pll->index);
+ goto error;
+ }
+
+ data = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_SYS_CTRL);
+ if (data & BIT(7)) { /* DSI PHY in LP-11 or ULPS */
+ rc = dsi_pll_relock(pll);
+ if (rc)
+ goto error;
+ else
+ goto end;
+ }
+ }
+
+ mdss_dsi_pll_12nm_calc_reg(pll, pdb);
+
+ /* commit DSI vco */
+ pll_db_commit_12nm(pll, pdb);
+
+ rc = dsi_pll_enable(c);
+
+error:
+ if (rc) {
+ mdss_pll_resource_enable(pll, false);
+ pr_err("ndx=%d failed to enable dsi pll\n", pll->index);
+ }
+
+end:
+ return rc;
+}
+
+void pll_vco_unprepare_12nm(struct clk *c)
+{
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+
+ if (!pll) {
+ pr_err("Dsi pll resources are not available\n");
+ return;
+ }
+
+ pll->vco_cached_rate = c->rate;
+ dsi_pll_disable(c);
+}
+
+int pll_vco_enable_12nm(struct clk *c)
+{
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+
+ if (!pll) {
+ pr_err("Dsi pll resources are not available\n");
+ return -EINVAL;
+ }
+
+ if (!pll->pll_on) {
+ pr_err("DSI PLL not enabled, return\n");
+ return -EINVAL;
+ }
+
+ MDSS_PLL_REG_W(pll->pll_base, DSIPHY_SSC0, 0x40);
+ wmb(); /* make sure register committed before enabling branch clocks */
+
+ return 0;
+}
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.c b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.c
new file mode 100644
index 0000000..210742b
--- /dev/null
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.c
@@ -0,0 +1,738 @@
+/* Copyright (c) 2018, 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/kernel.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <linux/clk/msm-clk.h>
+#include <linux/workqueue.h>
+#include <linux/clk/msm-clock-generic.h>
+#include <dt-bindings/clock/msm-clocks-8952.h>
+
+#include "mdss-pll.h"
+#include "mdss-dsi-pll.h"
+#include "mdss-dsi-pll-12nm.h"
+
+/*
+ * Clock tree model for generating DSI byte clock and pclk for 12nm DSI PLL
+ *
+ *
+ * +---------------+
+ * +----------| vco_clk |----------+
+ * | +---------------+ |
+ * | |
+ * | |
+ * | |
+ * +---------+---------+----+----+---------+---------+ |
+ * | | | | | | |
+ * | | | | | | |
+ * | | | | | | |
+ * +---v---+ +---v---+ +---v---+ +---v---+ +---v---+ +---v---+ |
+ * | DIV(1)| | DIV(2)| | DIV(4)| | DIV(8)| |DIV(16)| |DIV(32)| |
+ * +---+---+ +---+---+ +---+---+ +---+---+ +---+---+ +---+---+ |
+ * | | | | | | |
+ * | | +---+ +---+ | | |
+ * | +-----------+ | | +-----------+ | |
+ * +-------------------+ | | | | +-------------------+ |
+ * | | | | | | |
+ * +--v-v-v-v-v-v---+ |
+ * \ post_div_mux / |
+ * \ / |
+ * +-----+----+ +---------------------+
+ * | |
+ * +------------------------+ |
+ * | |
+ * +----v----+ +---------+---------+----+----+---------+---------+
+ * | DIV-4 | | | | | | |
+ * +----+----+ | | | | | |
+ * | +---v---+ +---v---+ +---v---+ +---v---+ +---v---+ +---v---+
+ * | | DIV(1)| | DIV(2)| | DIV(4)| | DIV(8)| |DIV(16)| |DIV(32)|
+ * | +---+---+ +---+---+ +---+---+ +---+---+ +---+---+ +---+---+
+ * | | | | | | |
+ * v | | +---+ +---+ | |
+ * byte_clk_src | +-----------+ | | +-----------+ |
+ * +-------------------+ | | | | +-------------------+
+ * | | | | | |
+ * +--v-v-v-v-v-v---+
+ * \ gp_cntrl_mux /
+ * \ /
+ * +-----+----+
+ * |
+ * |
+ * +-------v-------+
+ * | (DIV + 1) |
+ * | DIV = 0...127 |
+ * +-------+-------+
+ * |
+ * |
+ * v
+ * dsi_pclk input to Clock Controller MND
+ */
+
+static struct dsi_pll_db pll_db[DSI_PLL_NUM];
+
+static struct clk_ops pixel_div_clk_src_ops;
+
+/* Op structures */
+static const struct clk_ops clk_ops_dsi_vco = {
+ .set_rate = pll_vco_set_rate_12nm,
+ .round_rate = pll_vco_round_rate_12nm,
+ .handoff = pll_vco_handoff_12nm,
+ .prepare = pll_vco_prepare_12nm,
+ .unprepare = pll_vco_unprepare_12nm,
+ .enable = pll_vco_enable_12nm,
+};
+
+static struct clk_div_ops pixel_div_ops = {
+ .set_div = pixel_div_set_div,
+ .get_div = pixel_div_get_div,
+};
+
+static struct clk_mux_ops post_div_mux_ops = {
+ .set_mux_sel = set_post_div_mux_sel,
+ .get_mux_sel = get_post_div_mux_sel,
+};
+
+static struct clk_mux_ops gp_div_mux_ops = {
+ .set_mux_sel = set_gp_mux_sel,
+ .get_mux_sel = get_gp_mux_sel,
+};
+
+static struct dsi_pll_vco_clk dsi0pll_vco_clk = {
+ .ref_clk_rate = 19200000UL,
+ .min_rate = 1000000000UL,
+ .max_rate = 2000000000UL,
+ .pll_en_seq_cnt = 1,
+ .pll_enable_seqs[0] = dsi_pll_enable_seq_12nm,
+ .c = {
+ .dbg_name = "dsi0pll_vco_clk_12nm",
+ .ops = &clk_ops_dsi_vco,
+ CLK_INIT(dsi0pll_vco_clk.c),
+ },
+};
+
+static struct dsi_pll_vco_clk dsi1pll_vco_clk = {
+ .ref_clk_rate = 19200000UL,
+ .min_rate = 1000000000UL,
+ .max_rate = 2000000000UL,
+ .pll_en_seq_cnt = 1,
+ .pll_enable_seqs[0] = dsi_pll_enable_seq_12nm,
+ .c = {
+ .dbg_name = "dsi1pll_vco_clk_12nm",
+ .ops = &clk_ops_dsi_vco,
+ CLK_INIT(dsi1pll_vco_clk.c),
+ },
+};
+
+static struct div_clk dsi0pll_post_div1 = {
+ .data = {
+ .div = 1,
+ .min_div = 1,
+ .max_div = 1,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_post_div1",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_post_div1.c),
+ },
+};
+
+static struct div_clk dsi0pll_post_div2 = {
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_post_div2",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_post_div2.c),
+ },
+};
+
+static struct div_clk dsi0pll_post_div4 = {
+ .data = {
+ .div = 4,
+ .min_div = 4,
+ .max_div = 4,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_post_div4",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_post_div4.c),
+ },
+};
+
+static struct div_clk dsi0pll_post_div8 = {
+ .data = {
+ .div = 8,
+ .min_div = 8,
+ .max_div = 8,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_post_div8",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_post_div8.c),
+ },
+};
+
+static struct div_clk dsi0pll_post_div16 = {
+ .data = {
+ .div = 16,
+ .min_div = 16,
+ .max_div = 16,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_post_div16",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_post_div16.c),
+ },
+};
+
+static struct div_clk dsi0pll_post_div32 = {
+ .data = {
+ .div = 32,
+ .min_div = 32,
+ .max_div = 32,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_post_div32",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_post_div32.c),
+ },
+};
+
+static struct mux_clk dsi0pll_post_div_mux = {
+ .num_parents = 6,
+ .parents = (struct clk_src[]) {
+ {&dsi0pll_post_div1.c, 0},
+ {&dsi0pll_post_div2.c, 1},
+ {&dsi0pll_post_div4.c, 2},
+ {&dsi0pll_post_div8.c, 3},
+ {&dsi0pll_post_div16.c, 4},
+ {&dsi0pll_post_div32.c, 5},
+ },
+ .ops = &post_div_mux_ops,
+ .c = {
+ .parent = &dsi0pll_post_div1.c,
+ .dbg_name = "dsi0pll_post_div_mux",
+ .ops = &clk_ops_gen_mux,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_post_div_mux.c),
+ }
+};
+
+static struct div_clk dsi1pll_post_div1 = {
+ .data = {
+ .div = 1,
+ .min_div = 1,
+ .max_div = 1,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_post_div1",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_post_div1.c),
+ },
+};
+
+static struct div_clk dsi1pll_post_div2 = {
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_post_div2",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_post_div2.c),
+ },
+};
+
+static struct div_clk dsi1pll_post_div4 = {
+ .data = {
+ .div = 4,
+ .min_div = 4,
+ .max_div = 4,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_post_div4",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_post_div4.c),
+ },
+};
+
+static struct div_clk dsi1pll_post_div8 = {
+ .data = {
+ .div = 8,
+ .min_div = 8,
+ .max_div = 8,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_post_div8",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_post_div8.c),
+ },
+};
+
+static struct div_clk dsi1pll_post_div16 = {
+ .data = {
+ .div = 16,
+ .min_div = 16,
+ .max_div = 16,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_post_div16",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_post_div16.c),
+ },
+};
+
+static struct div_clk dsi1pll_post_div32 = {
+ .data = {
+ .div = 32,
+ .min_div = 32,
+ .max_div = 32,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_post_div32",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_post_div32.c),
+ },
+};
+
+static struct mux_clk dsi1pll_post_div_mux = {
+ .num_parents = 6,
+ .parents = (struct clk_src[]) {
+ {&dsi1pll_post_div1.c, 0},
+ {&dsi1pll_post_div2.c, 1},
+ {&dsi1pll_post_div4.c, 2},
+ {&dsi1pll_post_div8.c, 3},
+ {&dsi1pll_post_div16.c, 4},
+ {&dsi1pll_post_div32.c, 5},
+ },
+ .ops = &post_div_mux_ops,
+ .c = {
+ .parent = &dsi1pll_post_div1.c,
+ .dbg_name = "dsi1pll_post_div_mux",
+ .ops = &clk_ops_gen_mux,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_post_div_mux.c),
+ }
+};
+
+static struct div_clk dsi0pll_gp_div1 = {
+ .data = {
+ .div = 1,
+ .min_div = 1,
+ .max_div = 1,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_gp_div1",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_gp_div1.c),
+ },
+};
+
+static struct div_clk dsi0pll_gp_div2 = {
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_gp_div2",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_gp_div2.c),
+ },
+};
+
+static struct div_clk dsi0pll_gp_div4 = {
+ .data = {
+ .div = 4,
+ .min_div = 4,
+ .max_div = 4,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_gp_div4",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_gp_div4.c),
+ },
+};
+
+static struct div_clk dsi0pll_gp_div8 = {
+ .data = {
+ .div = 8,
+ .min_div = 8,
+ .max_div = 8,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_gp_div8",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_gp_div8.c),
+ },
+};
+
+static struct div_clk dsi0pll_gp_div16 = {
+ .data = {
+ .div = 16,
+ .min_div = 16,
+ .max_div = 16,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_gp_div16",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_gp_div16.c),
+ },
+};
+
+static struct div_clk dsi0pll_gp_div32 = {
+ .data = {
+ .div = 32,
+ .min_div = 32,
+ .max_div = 32,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_gp_div32",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_gp_div32.c),
+ },
+};
+
+static struct mux_clk dsi0pll_gp_div_mux = {
+ .num_parents = 6,
+ .parents = (struct clk_src[]) {
+ {&dsi0pll_gp_div1.c, 0},
+ {&dsi0pll_gp_div2.c, 1},
+ {&dsi0pll_gp_div4.c, 2},
+ {&dsi0pll_gp_div8.c, 3},
+ {&dsi0pll_gp_div16.c, 4},
+ {&dsi0pll_gp_div32.c, 5},
+ },
+ .ops = &gp_div_mux_ops,
+ .c = {
+ .parent = &dsi0pll_gp_div1.c,
+ .dbg_name = "dsi0pll_gp_div_mux",
+ .ops = &clk_ops_gen_mux,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_gp_div_mux.c),
+ }
+};
+
+static struct div_clk dsi1pll_gp_div1 = {
+ .data = {
+ .div = 1,
+ .min_div = 1,
+ .max_div = 1,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_gp_div1",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_gp_div1.c),
+ },
+};
+
+static struct div_clk dsi1pll_gp_div2 = {
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_gp_div2",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_gp_div2.c),
+ },
+};
+
+static struct div_clk dsi1pll_gp_div4 = {
+ .data = {
+ .div = 4,
+ .min_div = 4,
+ .max_div = 4,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_gp_div4",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_gp_div4.c),
+ },
+};
+
+static struct div_clk dsi1pll_gp_div8 = {
+ .data = {
+ .div = 8,
+ .min_div = 8,
+ .max_div = 8,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_gp_div8",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_gp_div8.c),
+ },
+};
+
+static struct div_clk dsi1pll_gp_div16 = {
+ .data = {
+ .div = 16,
+ .min_div = 16,
+ .max_div = 16,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_gp_div16",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_gp_div16.c),
+ },
+};
+
+static struct div_clk dsi1pll_gp_div32 = {
+ .data = {
+ .div = 32,
+ .min_div = 32,
+ .max_div = 32,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_gp_div32",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_gp_div32.c),
+ },
+};
+
+static struct mux_clk dsi1pll_gp_div_mux = {
+ .num_parents = 6,
+ .parents = (struct clk_src[]) {
+ {&dsi1pll_gp_div1.c, 0},
+ {&dsi1pll_gp_div2.c, 1},
+ {&dsi1pll_gp_div4.c, 2},
+ {&dsi1pll_gp_div8.c, 3},
+ {&dsi1pll_gp_div16.c, 4},
+ {&dsi1pll_gp_div32.c, 5},
+ },
+ .ops = &gp_div_mux_ops,
+ .c = {
+ .parent = &dsi1pll_gp_div1.c,
+ .dbg_name = "dsi1pll_gp_div_mux",
+ .ops = &clk_ops_gen_mux,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_gp_div_mux.c),
+ }
+};
+
+static struct div_clk dsi0pll_pixel_clk_src = {
+ .data = {
+ .max_div = 128,
+ .min_div = 1,
+ },
+ .ops = &pixel_div_ops,
+ .c = {
+ .parent = &dsi0pll_gp_div_mux.c,
+ .dbg_name = "dsi0pll_pixel_clk_src",
+ .ops = &pixel_div_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_pixel_clk_src.c),
+ },
+};
+
+static struct div_clk dsi1pll_pixel_clk_src = {
+ .data = {
+ .max_div = 128,
+ .min_div = 1,
+ },
+ .ops = &pixel_div_ops,
+ .c = {
+ .parent = &dsi1pll_gp_div_mux.c,
+ .dbg_name = "dsi1pll_pixel_clk_src",
+ .ops = &pixel_div_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_pixel_clk_src.c),
+ },
+};
+
+static struct div_clk dsi0pll_byte_clk_src = {
+ .data = {
+ .div = 4,
+ .min_div = 4,
+ .max_div = 4,
+ },
+ .c = {
+ .parent = &dsi0pll_post_div_mux.c,
+ .dbg_name = "dsi0pll_byte_clk_src",
+ .ops = &clk_ops_div,
+ CLK_INIT(dsi0pll_byte_clk_src.c),
+ },
+};
+
+static struct div_clk dsi1pll_byte_clk_src = {
+ .data = {
+ .div = 4,
+ .min_div = 4,
+ .max_div = 4,
+ },
+ .c = {
+ .parent = &dsi1pll_post_div_mux.c,
+ .dbg_name = "dsi1pll_byte_clk_src",
+ .ops = &clk_ops_div,
+ CLK_INIT(dsi1pll_byte_clk_src.c),
+ },
+};
+
+static struct clk_lookup mdss_dsi_pllcc_12nm[] = {
+ CLK_LIST(dsi0pll_byte_clk_src),
+ CLK_LIST(dsi0pll_pixel_clk_src),
+};
+
+static struct clk_lookup mdss_dsi_pllcc_12nm_1[] = {
+ CLK_LIST(dsi1pll_byte_clk_src),
+ CLK_LIST(dsi1pll_pixel_clk_src),
+};
+
+int dsi_pll_clock_register_12nm(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ int rc = 0, ndx;
+ struct dsi_pll_db *pdb;
+
+ if (!pdev || !pdev->dev.of_node) {
+ pr_err("Invalid input parameters\n");
+ return -EINVAL;
+ }
+
+ if (!pll_res || !pll_res->pll_base) {
+ pr_err("Invalid PLL resources\n");
+ return -EPROBE_DEFER;
+ }
+
+ if (pll_res->index >= DSI_PLL_NUM) {
+ pr_err("pll ndx=%d is NOT supported\n", pll_res->index);
+ return -EINVAL;
+ }
+
+ ndx = pll_res->index;
+ pdb = &pll_db[ndx];
+ pll_res->priv = pdb;
+ pdb->pll = pll_res;
+ ndx++;
+ ndx %= DSI_PLL_NUM;
+ pdb->next = &pll_db[ndx];
+
+ /* Set clock source operations */
+
+ /* pixel_clk */
+ pixel_div_clk_src_ops = clk_ops_div;
+ pixel_div_clk_src_ops.prepare = dsi_pll_div_prepare;
+
+ /* Set client data to mux, div and vco clocks. */
+ if (pll_res->index == DSI_PLL_1) {
+ dsi1pll_byte_clk_src.priv = pll_res;
+ dsi1pll_post_div_mux.priv = pll_res;
+ dsi1pll_post_div1.priv = pll_res;
+ dsi1pll_post_div2.priv = pll_res;
+ dsi1pll_post_div4.priv = pll_res;
+ dsi1pll_post_div8.priv = pll_res;
+ dsi1pll_post_div16.priv = pll_res;
+ dsi1pll_post_div32.priv = pll_res;
+ dsi1pll_pixel_clk_src.priv = pll_res;
+ dsi1pll_gp_div_mux.priv = pll_res;
+ dsi1pll_gp_div1.priv = pll_res;
+ dsi1pll_gp_div2.priv = pll_res;
+ dsi1pll_gp_div4.priv = pll_res;
+ dsi1pll_gp_div8.priv = pll_res;
+ dsi1pll_gp_div16.priv = pll_res;
+ dsi1pll_gp_div32.priv = pll_res;
+ dsi1pll_vco_clk.priv = pll_res;
+
+ if (pll_res->target_id == MDSS_PLL_TARGET_SDM439)
+ rc = of_msm_clock_register(pdev->dev.of_node,
+ mdss_dsi_pllcc_12nm_1,
+ ARRAY_SIZE(mdss_dsi_pllcc_12nm_1));
+ } else {
+ dsi0pll_byte_clk_src.priv = pll_res;
+ dsi0pll_post_div_mux.priv = pll_res;
+ dsi0pll_post_div1.priv = pll_res;
+ dsi0pll_post_div2.priv = pll_res;
+ dsi0pll_post_div4.priv = pll_res;
+ dsi0pll_post_div8.priv = pll_res;
+ dsi0pll_post_div16.priv = pll_res;
+ dsi0pll_post_div32.priv = pll_res;
+ dsi0pll_pixel_clk_src.priv = pll_res;
+ dsi0pll_gp_div_mux.priv = pll_res;
+ dsi0pll_gp_div1.priv = pll_res;
+ dsi0pll_gp_div2.priv = pll_res;
+ dsi0pll_gp_div4.priv = pll_res;
+ dsi0pll_gp_div8.priv = pll_res;
+ dsi0pll_gp_div16.priv = pll_res;
+ dsi0pll_gp_div32.priv = pll_res;
+ dsi0pll_vco_clk.priv = pll_res;
+
+ if (pll_res->target_id == MDSS_PLL_TARGET_SDM439)
+ rc = of_msm_clock_register(pdev->dev.of_node,
+ mdss_dsi_pllcc_12nm,
+ ARRAY_SIZE(mdss_dsi_pllcc_12nm));
+ }
+
+ if (!rc) {
+ pr_info("Registered DSI PLL ndx=%d clocks successfully\n",
+ pll_res->index);
+ }
+
+ return rc;
+}
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.h b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.h
new file mode 100644
index 0000000..6912ff4
--- /dev/null
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.h
@@ -0,0 +1,89 @@
+/* Copyright (c) 2018, 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 MDSS_DSI_PLL_12NM_H
+#define MDSS_DSI_PLL_12NM_H
+
+#define DSIPHY_PLL_POWERUP_CTRL 0x034
+#define DSIPHY_PLL_PROP_CHRG_PUMP_CTRL 0x038
+#define DSIPHY_PLL_INTEG_CHRG_PUMP_CTRL 0x03c
+#define DSIPHY_PLL_ANA_TST_LOCK_ST_OVR_CTRL 0x044
+#define DSIPHY_PLL_VCO_CTRL 0x048
+#define DSIPHY_PLL_GMP_CTRL_DIG_TST 0x04c
+#define DSIPHY_PLL_PHA_ERR_CTRL_0 0x050
+#define DSIPHY_PLL_LOCK_FILTER 0x054
+#define DSIPHY_PLL_UNLOCK_FILTER 0x058
+#define DSIPHY_PLL_INPUT_DIV_PLL_OVR 0x05c
+#define DSIPHY_PLL_LOOP_DIV_RATIO_0 0x060
+#define DSIPHY_PLL_INPUT_LOOP_DIV_RAT_CTRL 0x064
+#define DSIPHY_PLL_PRO_DLY_RELOCK 0x06c
+#define DSIPHY_PLL_CHAR_PUMP_BIAS_CTRL 0x070
+#define DSIPHY_PLL_LOCK_DET_MODE_SEL 0x074
+#define DSIPHY_PLL_ANA_PROG_CTRL 0x07c
+#define DSIPHY_HS_FREQ_RAN_SEL 0x110
+#define DSIPHY_SLEWRATE_FSM_OVR_CTRL 0x280
+#define DSIPHY_SLEWRATE_DDL_LOOP_CTRL 0x28c
+#define DSIPHY_SLEWRATE_DDL_CYC_FRQ_ADJ_0 0x290
+#define DSIPHY_PLL_PHA_ERR_CTRL_1 0x2e4
+#define DSIPHY_PLL_LOOP_DIV_RATIO_1 0x2e8
+#define DSIPHY_SLEWRATE_DDL_CYC_FRQ_ADJ_1 0x328
+#define DSIPHY_SSC0 0x394
+#define DSIPHY_SSC9 0x3b8
+#define DSIPHY_STAT0 0x3e0
+#define DSIPHY_CTRL0 0x3e8
+#define DSIPHY_SYS_CTRL 0x3f0
+#define DSIPHY_PLL_CTRL 0x3f8
+
+struct dsi_pll_param {
+ u32 hsfreqrange;
+ u32 vco_cntrl;
+ u32 osc_freq_target;
+ u32 m_div;
+ u32 prop_cntrl;
+ u32 int_cntrl;
+ u32 gmp_cntrl;
+ u32 cpbias_cntrl;
+
+ /* mux and dividers */
+ u32 gp_div_mux;
+ u32 post_div_mux;
+ u32 pixel_divhf;
+ u32 fsm_ovr_ctrl;
+};
+
+enum {
+ DSI_PLL_0,
+ DSI_PLL_1,
+ DSI_PLL_NUM
+};
+
+struct dsi_pll_db {
+ struct dsi_pll_db *next;
+ struct mdss_pll_resources *pll;
+ struct dsi_pll_param param;
+};
+
+int pll_vco_set_rate_12nm(struct clk *c, unsigned long rate);
+long pll_vco_round_rate_12nm(struct clk *c, unsigned long rate);
+enum handoff pll_vco_handoff_12nm(struct clk *c);
+int pll_vco_prepare_12nm(struct clk *c);
+void pll_vco_unprepare_12nm(struct clk *c);
+int pll_vco_enable_12nm(struct clk *c);
+int pixel_div_set_div(struct div_clk *clk, int div);
+int pixel_div_get_div(struct div_clk *clk);
+int set_post_div_mux_sel(struct mux_clk *clk, int sel);
+int get_post_div_mux_sel(struct mux_clk *clk);
+int set_gp_mux_sel(struct mux_clk *clk, int sel);
+int get_gp_mux_sel(struct mux_clk *clk);
+int dsi_pll_enable_seq_12nm(struct mdss_pll_resources *pll);
+
+#endif /* MDSS_DSI_PLL_12NM_H */
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll.h b/drivers/clk/msm/mdss/mdss-dsi-pll.h
index 4a9bb64..8498259 100644
--- a/drivers/clk/msm/mdss/mdss-dsi-pll.h
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2015, 2018, 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
@@ -81,6 +81,8 @@
struct mdss_pll_resources *pll_res);
int dsi_pll_clock_register_8996(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
+int dsi_pll_clock_register_12nm(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
int set_byte_mux_sel(struct mux_clk *clk, int sel);
int get_byte_mux_sel(struct mux_clk *clk);
diff --git a/drivers/clk/msm/mdss/mdss-pll.c b/drivers/clk/msm/mdss/mdss-pll.c
index 49f3d7b..b8b6883 100644
--- a/drivers/clk/msm/mdss/mdss-pll.c
+++ b/drivers/clk/msm/mdss/mdss-pll.c
@@ -136,6 +136,9 @@
} else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8909")) {
pll_res->pll_interface_type = MDSS_DSI_PLL_LPM;
pll_res->target_id = MDSS_PLL_TARGET_8909;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_sdm439")) {
+ pll_res->pll_interface_type = MDSS_DSI_PLL_12NM;
+ pll_res->target_id = MDSS_PLL_TARGET_SDM439;
} else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8996")) {
pll_res->pll_interface_type = MDSS_DSI_PLL_8996;
pll_res->target_id = MDSS_PLL_TARGET_8996;
@@ -186,6 +189,9 @@
case MDSS_DSI_PLL_8996:
rc = dsi_pll_clock_register_8996(pdev, pll_res);
break;
+ case MDSS_DSI_PLL_12NM:
+ rc = dsi_pll_clock_register_12nm(pdev, pll_res);
+ break;
case MDSS_HDMI_PLL_8996:
rc = hdmi_8996_v1_pll_clock_register(pdev, pll_res);
break;
@@ -403,6 +409,7 @@
{.compatible = "qcom,mdss_dsi_pll_8937"},
{.compatible = "qcom,mdss_dsi_pll_8909"},
{.compatible = "qcom,mdss_dsi_pll_8953"},
+ {.compatible = "qcom,mdss_dsi_pll_sdm439"},
{}
};
diff --git a/drivers/clk/msm/mdss/mdss-pll.h b/drivers/clk/msm/mdss/mdss-pll.h
index 1fa5cff..026ac8e 100644
--- a/drivers/clk/msm/mdss/mdss-pll.h
+++ b/drivers/clk/msm/mdss/mdss-pll.h
@@ -31,6 +31,7 @@
enum {
MDSS_DSI_PLL_LPM,
MDSS_DSI_PLL_8996,
+ MDSS_DSI_PLL_12NM,
MDSS_HDMI_PLL_8996,
MDSS_HDMI_PLL_8996_V2,
MDSS_HDMI_PLL_8996_V3,
@@ -44,6 +45,7 @@
MDSS_PLL_TARGET_8937,
MDSS_PLL_TARGET_8953,
MDSS_PLL_TARGET_8909,
+ MDSS_PLL_TARGET_SDM439,
};
struct mdss_pll_resources {
diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c
index 5c4e193..8dd7134 100644
--- a/drivers/clk/qcom/gcc-msm8916.c
+++ b/drivers/clk/qcom/gcc-msm8916.c
@@ -1437,6 +1437,7 @@
static struct clk_rcg2 codec_digcodec_clk_src = {
.cmd_rcgr = 0x1c09c,
+ .mnd_width = 8,
.hid_width = 5,
.parent_map = gcc_xo_gpll1_emclk_sleep_map,
.freq_tbl = ftbl_codec_clk,
diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c b/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
index 874c229..7b23db4 100644
--- a/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
+++ b/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
@@ -174,6 +174,7 @@
unsigned int *val)
{
int rc = 0;
+ u32 data;
struct mdss_pll_resources *rsc = context;
rc = mdss_pll_resource_enable(rsc, true);
@@ -182,7 +183,19 @@
return rc;
}
+ /*
+ * DSI PHY/PLL should be both powered on when reading PLL
+ * registers. Since PHY power has been enabled in DSI PHY
+ * driver, only PLL power is needed to enable here.
+ */
+ data = MDSS_PLL_REG_R(rsc->phy_base, PHY_CMN_CTRL_0);
+ MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_CTRL_0, data | BIT(5));
+ ndelay(250);
+
*val = MDSS_PLL_REG_R(rsc->pll_base, reg);
+
+ MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_CTRL_0, data);
+
(void)mdss_pll_resource_enable(rsc, false);
return rc;
diff --git a/drivers/clk/qcom/mmcc-msm8996.c b/drivers/clk/qcom/mmcc-msm8996.c
index ca97e11..3b171be 100644
--- a/drivers/clk/qcom/mmcc-msm8996.c
+++ b/drivers/clk/qcom/mmcc-msm8996.c
@@ -2984,7 +2984,7 @@
.cxcs = (unsigned int []){ 0x36ac },
.cxc_count = 1,
.pd = {
- .name = "vfe0",
+ .name = "vfe1",
},
.parent = &camss_gdsc.pd,
.pwrsts = PWRSTS_OFF_ON,
diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c
index 9fe0939..6ea5401 100644
--- a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c
+++ b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c
@@ -750,7 +750,7 @@
.features = CCU_FEATURE_FIXED_PREDIV,
.hw.init = CLK_HW_INIT_PARENTS("out-a",
clk_out_parents,
- &ccu_div_ops,
+ &ccu_mp_ops,
0),
},
};
@@ -771,7 +771,7 @@
.features = CCU_FEATURE_FIXED_PREDIV,
.hw.init = CLK_HW_INIT_PARENTS("out-b",
clk_out_parents,
- &ccu_div_ops,
+ &ccu_mp_ops,
0),
},
};
@@ -792,7 +792,7 @@
.features = CCU_FEATURE_FIXED_PREDIV,
.hw.init = CLK_HW_INIT_PARENTS("out-c",
clk_out_parents,
- &ccu_div_ops,
+ &ccu_mp_ops,
0),
},
};
diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c
index 7b596fa..6bebc1f 100644
--- a/drivers/cpufreq/s3c24xx-cpufreq.c
+++ b/drivers/cpufreq/s3c24xx-cpufreq.c
@@ -351,7 +351,13 @@
static int s3c_cpufreq_init(struct cpufreq_policy *policy)
{
policy->clk = clk_arm;
- return cpufreq_generic_init(policy, ftab, cpu_cur.info->latency);
+
+ policy->cpuinfo.transition_latency = cpu_cur.info->latency;
+
+ if (ftab)
+ return cpufreq_table_validate_and_show(policy, ftab);
+
+ return 0;
}
static int __init s3c_cpufreq_initclks(void)
diff --git a/drivers/cpufreq/sh-cpufreq.c b/drivers/cpufreq/sh-cpufreq.c
index 86628e2..719c3d9 100644
--- a/drivers/cpufreq/sh-cpufreq.c
+++ b/drivers/cpufreq/sh-cpufreq.c
@@ -30,11 +30,51 @@
static DEFINE_PER_CPU(struct clk, sh_cpuclk);
+struct cpufreq_target {
+ struct cpufreq_policy *policy;
+ unsigned int freq;
+};
+
static unsigned int sh_cpufreq_get(unsigned int cpu)
{
return (clk_get_rate(&per_cpu(sh_cpuclk, cpu)) + 500) / 1000;
}
+static long __sh_cpufreq_target(void *arg)
+{
+ struct cpufreq_target *target = arg;
+ struct cpufreq_policy *policy = target->policy;
+ int cpu = policy->cpu;
+ struct clk *cpuclk = &per_cpu(sh_cpuclk, cpu);
+ struct cpufreq_freqs freqs;
+ struct device *dev;
+ long freq;
+
+ if (smp_processor_id() != cpu)
+ return -ENODEV;
+
+ dev = get_cpu_device(cpu);
+
+ /* Convert target_freq from kHz to Hz */
+ freq = clk_round_rate(cpuclk, target->freq * 1000);
+
+ if (freq < (policy->min * 1000) || freq > (policy->max * 1000))
+ return -EINVAL;
+
+ dev_dbg(dev, "requested frequency %u Hz\n", target->freq * 1000);
+
+ freqs.old = sh_cpufreq_get(cpu);
+ freqs.new = (freq + 500) / 1000;
+ freqs.flags = 0;
+
+ cpufreq_freq_transition_begin(target->policy, &freqs);
+ clk_set_rate(cpuclk, freq);
+ cpufreq_freq_transition_end(target->policy, &freqs, 0);
+
+ dev_dbg(dev, "set frequency %lu Hz\n", freq);
+ return 0;
+}
+
/*
* Here we notify other drivers of the proposed change and the final change.
*/
@@ -42,40 +82,9 @@
unsigned int target_freq,
unsigned int relation)
{
- unsigned int cpu = policy->cpu;
- struct clk *cpuclk = &per_cpu(sh_cpuclk, cpu);
- cpumask_t cpus_allowed;
- struct cpufreq_freqs freqs;
- struct device *dev;
- long freq;
+ struct cpufreq_target data = { .policy = policy, .freq = target_freq };
- cpus_allowed = current->cpus_allowed;
- set_cpus_allowed_ptr(current, cpumask_of(cpu));
-
- BUG_ON(smp_processor_id() != cpu);
-
- dev = get_cpu_device(cpu);
-
- /* Convert target_freq from kHz to Hz */
- freq = clk_round_rate(cpuclk, target_freq * 1000);
-
- if (freq < (policy->min * 1000) || freq > (policy->max * 1000))
- return -EINVAL;
-
- dev_dbg(dev, "requested frequency %u Hz\n", target_freq * 1000);
-
- freqs.old = sh_cpufreq_get(cpu);
- freqs.new = (freq + 500) / 1000;
- freqs.flags = 0;
-
- cpufreq_freq_transition_begin(policy, &freqs);
- set_cpus_allowed_ptr(current, &cpus_allowed);
- clk_set_rate(cpuclk, freq);
- cpufreq_freq_transition_end(policy, &freqs, 0);
-
- dev_dbg(dev, "set frequency %lu Hz\n", freq);
-
- return 0;
+ return work_on_cpu(policy->cpu, __sh_cpufreq_target, &data);
}
static int sh_cpufreq_verify(struct cpufreq_policy *policy)
diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c
index ad0be45..fe3ef48 100644
--- a/drivers/cpuidle/lpm-levels-of.c
+++ b/drivers/cpuidle/lpm-levels-of.c
@@ -474,7 +474,7 @@
/* Set default_level to 0 as default */
c->default_level = 0;
- return ret;
+ return 0;
fail:
pr_err("Failed to read key: %s ret: %d\n", key, ret);
diff --git a/drivers/dax/dax.c b/drivers/dax/dax.c
index 40be374..473b44c 100644
--- a/drivers/dax/dax.c
+++ b/drivers/dax/dax.c
@@ -453,9 +453,21 @@
return rc;
}
+static int dax_dev_split(struct vm_area_struct *vma, unsigned long addr)
+{
+ struct file *filp = vma->vm_file;
+ struct dax_dev *dax_dev = filp->private_data;
+ struct dax_region *dax_region = dax_dev->region;
+
+ if (!IS_ALIGNED(addr, dax_region->align))
+ return -EINVAL;
+ return 0;
+}
+
static const struct vm_operations_struct dax_dev_vm_ops = {
.fault = dax_dev_fault,
.pmd_fault = dax_dev_pmd_fault,
+ .split = dax_dev_split,
};
static int dax_mmap(struct file *filp, struct vm_area_struct *vma)
diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c
index 6775f2c..c756886 100644
--- a/drivers/dma/fsl-edma.c
+++ b/drivers/dma/fsl-edma.c
@@ -863,11 +863,11 @@
}
}
-static void fsl_disable_clocks(struct fsl_edma_engine *fsl_edma)
+static void fsl_disable_clocks(struct fsl_edma_engine *fsl_edma, int nr_clocks)
{
int i;
- for (i = 0; i < DMAMUX_NR; i++)
+ for (i = 0; i < nr_clocks; i++)
clk_disable_unprepare(fsl_edma->muxclk[i]);
}
@@ -904,25 +904,25 @@
res = platform_get_resource(pdev, IORESOURCE_MEM, 1 + i);
fsl_edma->muxbase[i] = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(fsl_edma->muxbase[i]))
+ if (IS_ERR(fsl_edma->muxbase[i])) {
+ /* on error: disable all previously enabled clks */
+ fsl_disable_clocks(fsl_edma, i);
return PTR_ERR(fsl_edma->muxbase[i]);
+ }
sprintf(clkname, "dmamux%d", i);
fsl_edma->muxclk[i] = devm_clk_get(&pdev->dev, clkname);
if (IS_ERR(fsl_edma->muxclk[i])) {
dev_err(&pdev->dev, "Missing DMAMUX block clock.\n");
+ /* on error: disable all previously enabled clks */
+ fsl_disable_clocks(fsl_edma, i);
return PTR_ERR(fsl_edma->muxclk[i]);
}
ret = clk_prepare_enable(fsl_edma->muxclk[i]);
- if (ret) {
- /* disable only clks which were enabled on error */
- for (; i >= 0; i--)
- clk_disable_unprepare(fsl_edma->muxclk[i]);
-
- dev_err(&pdev->dev, "DMAMUX clk block failed.\n");
- return ret;
- }
+ if (ret)
+ /* on error: disable all previously enabled clks */
+ fsl_disable_clocks(fsl_edma, i);
}
@@ -976,7 +976,7 @@
if (ret) {
dev_err(&pdev->dev,
"Can't register Freescale eDMA engine. (%d)\n", ret);
- fsl_disable_clocks(fsl_edma);
+ fsl_disable_clocks(fsl_edma, DMAMUX_NR);
return ret;
}
@@ -985,7 +985,7 @@
dev_err(&pdev->dev,
"Can't register Freescale eDMA of_dma. (%d)\n", ret);
dma_async_device_unregister(&fsl_edma->dma_dev);
- fsl_disable_clocks(fsl_edma);
+ fsl_disable_clocks(fsl_edma, DMAMUX_NR);
return ret;
}
@@ -1015,7 +1015,7 @@
fsl_edma_cleanup_vchan(&fsl_edma->dma_dev);
of_dma_controller_free(np);
dma_async_device_unregister(&fsl_edma->dma_dev);
- fsl_disable_clocks(fsl_edma);
+ fsl_disable_clocks(fsl_edma, DMAMUX_NR);
return 0;
}
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index d1651a5..21726a2 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -937,6 +937,21 @@
return 0;
}
+static int sdma_disable_channel_with_delay(struct dma_chan *chan)
+{
+ sdma_disable_channel(chan);
+
+ /*
+ * According to NXP R&D team a delay of one BD SDMA cost time
+ * (maximum is 1ms) should be added after disable of the channel
+ * bit, to ensure SDMA core has really been stopped after SDMA
+ * clients call .device_terminate_all.
+ */
+ mdelay(1);
+
+ return 0;
+}
+
static void sdma_set_watermarklevel_for_p2p(struct sdma_channel *sdmac)
{
struct sdma_engine *sdma = sdmac->sdma;
@@ -1828,7 +1843,7 @@
sdma->dma_device.device_prep_slave_sg = sdma_prep_slave_sg;
sdma->dma_device.device_prep_dma_cyclic = sdma_prep_dma_cyclic;
sdma->dma_device.device_config = sdma_config;
- sdma->dma_device.device_terminate_all = sdma_disable_channel;
+ sdma->dma_device.device_terminate_all = sdma_disable_channel_with_delay;
sdma->dma_device.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
sdma->dma_device.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
sdma->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
diff --git a/drivers/dma/ti-dma-crossbar.c b/drivers/dma/ti-dma-crossbar.c
index 43e88d8..8c3c588 100644
--- a/drivers/dma/ti-dma-crossbar.c
+++ b/drivers/dma/ti-dma-crossbar.c
@@ -54,7 +54,15 @@
static inline void ti_am335x_xbar_write(void __iomem *iomem, int event, u8 val)
{
- writeb_relaxed(val, iomem + event);
+ /*
+ * TPCC_EVT_MUX_60_63 register layout is different than the
+ * rest, in the sense, that event 63 is mapped to lowest byte
+ * and event 60 is mapped to highest, handle it separately.
+ */
+ if (event >= 60 && event <= 63)
+ writeb_relaxed(val, iomem + (63 - event % 4));
+ else
+ writeb_relaxed(val, iomem + event);
}
static void ti_am335x_xbar_free(struct device *dev, void *route_data)
diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c
index 6d221e5..2265805 100644
--- a/drivers/dma/xilinx/zynqmp_dma.c
+++ b/drivers/dma/xilinx/zynqmp_dma.c
@@ -933,7 +933,8 @@
if (!chan)
return;
- devm_free_irq(chan->zdev->dev, chan->irq, chan);
+ if (chan->irq)
+ devm_free_irq(chan->zdev->dev, chan->irq, chan);
tasklet_kill(&chan->tasklet);
list_del(&chan->common.device_node);
clk_disable_unprepare(chan->clk_apb);
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index 58d3e2b..61262a7 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -1020,13 +1020,23 @@
return ret;
}
+static int socfpga_is_a10(void)
+{
+ return of_machine_is_compatible("altr,socfpga-arria10");
+}
+
static int validate_parent_available(struct device_node *np);
static const struct of_device_id altr_edac_a10_device_of_match[];
static int __init __maybe_unused altr_init_a10_ecc_device_type(char *compat)
{
int irq;
- struct device_node *child, *np = of_find_compatible_node(NULL, NULL,
- "altr,socfpga-a10-ecc-manager");
+ struct device_node *child, *np;
+
+ if (!socfpga_is_a10())
+ return -ENODEV;
+
+ np = of_find_compatible_node(NULL, NULL,
+ "altr,socfpga-a10-ecc-manager");
if (!np) {
edac_printk(KERN_ERR, EDAC_DEVICE, "ECC Manager not found\n");
return -ENODEV;
@@ -1542,8 +1552,12 @@
static int __init socfpga_init_sdmmc_ecc(void)
{
int rc = -ENODEV;
- struct device_node *child = of_find_compatible_node(NULL, NULL,
- "altr,socfpga-sdmmc-ecc");
+ struct device_node *child;
+
+ if (!socfpga_is_a10())
+ return -ENODEV;
+
+ child = of_find_compatible_node(NULL, NULL, "altr,socfpga-sdmmc-ecc");
if (!child) {
edac_printk(KERN_WARNING, EDAC_DEVICE, "SDMMC node not found\n");
return -ENODEV;
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index 4e0f8e7..bea71fb 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -50,7 +50,7 @@
return edac_mc_poll_msec;
}
-static int edac_set_poll_msec(const char *val, struct kernel_param *kp)
+static int edac_set_poll_msec(const char *val, const struct kernel_param *kp)
{
unsigned long l;
int ret;
diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c
index 5f8543b..b0d3284 100644
--- a/drivers/edac/edac_module.c
+++ b/drivers/edac/edac_module.c
@@ -19,7 +19,8 @@
#ifdef CONFIG_EDAC_DEBUG
-static int edac_set_debug_level(const char *buf, struct kernel_param *kp)
+static int edac_set_debug_level(const char *buf,
+ const struct kernel_param *kp)
{
unsigned long val;
int ret;
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index 219556c..cf2ed6c 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -19,7 +19,8 @@
KBUILD_CFLAGS := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
-D__NO_FORTIFY \
$(call cc-option,-ffreestanding) \
- $(call cc-option,-fno-stack-protector)
+ $(call cc-option,-fno-stack-protector) \
+ $(DISABLE_LTO)
GCOV_PROFILE := n
KASAN_SANITIZE := n
diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c
index d0ddba7..5ef4980 100644
--- a/drivers/gpio/gpio-wcove.c
+++ b/drivers/gpio/gpio-wcove.c
@@ -51,6 +51,8 @@
#define GROUP1_NR_IRQS 6
#define IRQ_MASK_BASE 0x4e19
#define IRQ_STATUS_BASE 0x4e0b
+#define GPIO_IRQ0_MASK GENMASK(6, 0)
+#define GPIO_IRQ1_MASK GENMASK(5, 0)
#define UPDATE_IRQ_TYPE BIT(0)
#define UPDATE_IRQ_MASK BIT(1)
@@ -310,7 +312,7 @@
return IRQ_NONE;
}
- pending = p[0] | (p[1] << 8);
+ pending = (p[0] & GPIO_IRQ0_MASK) | ((p[1] & GPIO_IRQ1_MASK) << 7);
if (!pending)
return IRQ_NONE;
@@ -318,7 +320,7 @@
while (pending) {
/* One iteration is for all pending bits */
for_each_set_bit(gpio, (const unsigned long *)&pending,
- GROUP0_NR_IRQS) {
+ WCOVE_GPIO_NUM) {
offset = (gpio > GROUP0_NR_IRQS) ? 1 : 0;
mask = (offset == 1) ? BIT(gpio - GROUP0_NR_IRQS) :
BIT(gpio);
@@ -334,7 +336,7 @@
break;
}
- pending = p[0] | (p[1] << 8);
+ pending = (p[0] & GPIO_IRQ0_MASK) | ((p[1] & GPIO_IRQ1_MASK) << 7);
}
return IRQ_HANDLED;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
index 5796539..648ecf6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
@@ -540,6 +540,9 @@
size_t size;
u32 retry = 3;
+ if (amdgpu_acpi_pcie_notify_device_ready(adev))
+ return -EINVAL;
+
/* Get the device handle */
handle = ACPI_HANDLE(&adev->pdev->dev);
if (!handle)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
index 6c343a9..0e8f897 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
@@ -14,6 +14,16 @@
#include "amd_acpi.h"
+#define AMDGPU_PX_QUIRK_FORCE_ATPX (1 << 0)
+
+struct amdgpu_px_quirk {
+ u32 chip_vendor;
+ u32 chip_device;
+ u32 subsys_vendor;
+ u32 subsys_device;
+ u32 px_quirk_flags;
+};
+
struct amdgpu_atpx_functions {
bool px_params;
bool power_cntl;
@@ -35,6 +45,7 @@
static struct amdgpu_atpx_priv {
bool atpx_detected;
bool bridge_pm_usable;
+ unsigned int quirks;
/* handle for device - and atpx */
acpi_handle dhandle;
acpi_handle other_handle;
@@ -205,13 +216,19 @@
atpx->is_hybrid = false;
if (valid_bits & ATPX_MS_HYBRID_GFX_SUPPORTED) {
- printk("ATPX Hybrid Graphics\n");
- /*
- * Disable legacy PM methods only when pcie port PM is usable,
- * otherwise the device might fail to power off or power on.
- */
- atpx->functions.power_cntl = !amdgpu_atpx_priv.bridge_pm_usable;
- atpx->is_hybrid = true;
+ if (amdgpu_atpx_priv.quirks & AMDGPU_PX_QUIRK_FORCE_ATPX) {
+ printk("ATPX Hybrid Graphics, forcing to ATPX\n");
+ atpx->functions.power_cntl = true;
+ atpx->is_hybrid = false;
+ } else {
+ printk("ATPX Hybrid Graphics\n");
+ /*
+ * Disable legacy PM methods only when pcie port PM is usable,
+ * otherwise the device might fail to power off or power on.
+ */
+ atpx->functions.power_cntl = !amdgpu_atpx_priv.bridge_pm_usable;
+ atpx->is_hybrid = true;
+ }
}
atpx->dgpu_req_power_for_displays = false;
@@ -547,6 +564,31 @@
.get_client_id = amdgpu_atpx_get_client_id,
};
+static const struct amdgpu_px_quirk amdgpu_px_quirk_list[] = {
+ /* HG _PR3 doesn't seem to work on this A+A weston board */
+ { 0x1002, 0x6900, 0x1002, 0x0124, AMDGPU_PX_QUIRK_FORCE_ATPX },
+ { 0x1002, 0x6900, 0x1028, 0x0812, AMDGPU_PX_QUIRK_FORCE_ATPX },
+ { 0x1002, 0x6900, 0x1028, 0x0813, AMDGPU_PX_QUIRK_FORCE_ATPX },
+ { 0, 0, 0, 0, 0 },
+};
+
+static void amdgpu_atpx_get_quirks(struct pci_dev *pdev)
+{
+ const struct amdgpu_px_quirk *p = amdgpu_px_quirk_list;
+
+ /* Apply PX quirks */
+ while (p && p->chip_device != 0) {
+ if (pdev->vendor == p->chip_vendor &&
+ pdev->device == p->chip_device &&
+ pdev->subsystem_vendor == p->subsys_vendor &&
+ pdev->subsystem_device == p->subsys_device) {
+ amdgpu_atpx_priv.quirks |= p->px_quirk_flags;
+ break;
+ }
+ ++p;
+ }
+}
+
/**
* amdgpu_atpx_detect - detect whether we have PX
*
@@ -570,6 +612,7 @@
parent_pdev = pci_upstream_bridge(pdev);
d3_supported |= parent_pdev && parent_pdev->bridge_d3;
+ amdgpu_atpx_get_quirks(pdev);
}
while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) {
@@ -579,6 +622,7 @@
parent_pdev = pci_upstream_bridge(pdev);
d3_supported |= parent_pdev && parent_pdev->bridge_d3;
+ amdgpu_atpx_get_quirks(pdev);
}
if (has_atpx && vga_count == 2) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
index 086aa5c..e9311eb 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
@@ -69,25 +69,18 @@
/* don't do anything if sink is not display port, i.e.,
* passive dp->(dvi|hdmi) adaptor
*/
- if (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) {
- int saved_dpms = connector->dpms;
- /* Only turn off the display if it's physically disconnected */
- if (!amdgpu_display_hpd_sense(adev, amdgpu_connector->hpd.hpd)) {
- drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
- } else if (amdgpu_atombios_dp_needs_link_train(amdgpu_connector)) {
- /* Don't try to start link training before we
- * have the dpcd */
- if (amdgpu_atombios_dp_get_dpcd(amdgpu_connector))
- return;
+ if (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT &&
+ amdgpu_display_hpd_sense(adev, amdgpu_connector->hpd.hpd) &&
+ amdgpu_atombios_dp_needs_link_train(amdgpu_connector)) {
+ /* Don't start link training before we have the DPCD */
+ if (amdgpu_atombios_dp_get_dpcd(amdgpu_connector))
+ return;
- /* set it to OFF so that drm_helper_connector_dpms()
- * won't return immediately since the current state
- * is ON at this point.
- */
- connector->dpms = DRM_MODE_DPMS_OFF;
- drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
- }
- connector->dpms = saved_dpms;
+ /* Turn the connector off and back on immediately, which
+ * will trigger link training
+ */
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
}
}
}
@@ -739,9 +732,11 @@
enum drm_connector_status ret = connector_status_disconnected;
int r;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
if (encoder) {
struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
@@ -760,8 +755,12 @@
/* check acpi lid status ??? */
amdgpu_connector_update_scratch_regs(connector, ret);
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
+
return ret;
}
@@ -871,9 +870,11 @@
enum drm_connector_status ret = connector_status_disconnected;
int r;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
encoder = amdgpu_connector_best_single_encoder(connector);
if (!encoder)
@@ -927,8 +928,10 @@
amdgpu_connector_update_scratch_regs(connector, ret);
out:
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
return ret;
}
@@ -991,9 +994,11 @@
enum drm_connector_status ret = connector_status_disconnected;
bool dret = false, broken_edid = false;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
if (!force && amdgpu_connector_check_hpd_status_unchanged(connector)) {
ret = connector->status;
@@ -1118,8 +1123,10 @@
amdgpu_connector_update_scratch_regs(connector, ret);
exit:
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
return ret;
}
@@ -1362,9 +1369,11 @@
struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector);
int r;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
if (!force && amdgpu_connector_check_hpd_status_unchanged(connector)) {
ret = connector->status;
@@ -1432,8 +1441,10 @@
amdgpu_connector_update_scratch_regs(connector, ret);
out:
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
return ret;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index ce9797b..d0c3e56 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -1678,8 +1678,6 @@
* ignore it */
vga_client_register(adev->pdev, adev, NULL, amdgpu_vga_set_decode);
- if (amdgpu_runtime_pm == 1)
- runtime = true;
if (amdgpu_device_is_px(ddev))
runtime = true;
vga_switcheroo_register_client(adev->pdev, &amdgpu_switcheroo_ops, runtime);
@@ -2239,7 +2237,7 @@
for (i = 0; i < AMDGPU_MAX_RINGS; ++i) {
struct amdgpu_ring *ring = adev->rings[i];
- if (!ring)
+ if (!ring || !ring->sched.thread)
continue;
kthread_park(ring->sched.thread);
amd_sched_hw_job_reset(&ring->sched);
@@ -2328,7 +2326,8 @@
}
for (i = 0; i < AMDGPU_MAX_RINGS; ++i) {
struct amdgpu_ring *ring = adev->rings[i];
- if (!ring)
+
+ if (!ring || !ring->sched.thread)
continue;
amd_sched_job_recovery(&ring->sched);
@@ -2337,7 +2336,7 @@
} else {
dev_err(adev->dev, "asic resume failed (%d).\n", r);
for (i = 0; i < AMDGPU_MAX_RINGS; ++i) {
- if (adev->rings[i]) {
+ if (adev->rings[i] && adev->rings[i]->sched.thread) {
kthread_unpark(adev->rings[i]->sched.thread);
}
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
index 083e2b4..15a2d8f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
@@ -533,6 +533,12 @@
return ERR_PTR(-ENOENT);
}
+ /* Handle is imported dma-buf, so cannot be migrated to VRAM for scanout */
+ if (obj->import_attach) {
+ DRM_DEBUG_KMS("Cannot create framebuffer from imported dma_buf\n");
+ return ERR_PTR(-EINVAL);
+ }
+
amdgpu_fb = kzalloc(sizeof(*amdgpu_fb), GFP_KERNEL);
if (amdgpu_fb == NULL) {
drm_gem_object_unreference_unlocked(obj);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
index a7ea9a3..d5e4748 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
@@ -36,8 +36,6 @@
struct amdgpu_bo *robj = gem_to_amdgpu_bo(gobj);
if (robj) {
- if (robj->gem_base.import_attach)
- drm_prime_gem_destroy(&robj->gem_base, robj->tbo.sg);
amdgpu_mn_unregister(robj);
amdgpu_bo_unref(&robj);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
index f3efb1c..5afe727 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
@@ -94,6 +94,8 @@
amdgpu_update_memory_usage(bo->adev, &bo->tbo.mem, NULL);
+ if (bo->gem_base.import_attach)
+ drm_prime_gem_destroy(&bo->gem_base, bo->tbo.sg);
drm_gem_object_release(&bo->gem_base);
amdgpu_bo_unref(&bo->parent);
if (!list_empty(&bo->shadow_list)) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
index e3281ca..5caf517 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
@@ -273,12 +273,15 @@
if (adev->uvd.vcpu_bo == NULL)
return 0;
- for (i = 0; i < adev->uvd.max_handles; ++i)
- if (atomic_read(&adev->uvd.handles[i]))
- break;
+ /* only valid for physical mode */
+ if (adev->asic_type < CHIP_POLARIS10) {
+ for (i = 0; i < adev->uvd.max_handles; ++i)
+ if (atomic_read(&adev->uvd.handles[i]))
+ break;
- if (i == AMDGPU_MAX_UVD_HANDLES)
- return 0;
+ if (i == adev->uvd.max_handles)
+ return 0;
+ }
cancel_delayed_work_sync(&adev->uvd.idle_work);
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c
index 71116da..e040a89 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c
@@ -4475,34 +4475,8 @@
case CHIP_KAVERI:
adev->gfx.config.max_shader_engines = 1;
adev->gfx.config.max_tile_pipes = 4;
- if ((adev->pdev->device == 0x1304) ||
- (adev->pdev->device == 0x1305) ||
- (adev->pdev->device == 0x130C) ||
- (adev->pdev->device == 0x130F) ||
- (adev->pdev->device == 0x1310) ||
- (adev->pdev->device == 0x1311) ||
- (adev->pdev->device == 0x131C)) {
- adev->gfx.config.max_cu_per_sh = 8;
- adev->gfx.config.max_backends_per_se = 2;
- } else if ((adev->pdev->device == 0x1309) ||
- (adev->pdev->device == 0x130A) ||
- (adev->pdev->device == 0x130D) ||
- (adev->pdev->device == 0x1313) ||
- (adev->pdev->device == 0x131D)) {
- adev->gfx.config.max_cu_per_sh = 6;
- adev->gfx.config.max_backends_per_se = 2;
- } else if ((adev->pdev->device == 0x1306) ||
- (adev->pdev->device == 0x1307) ||
- (adev->pdev->device == 0x130B) ||
- (adev->pdev->device == 0x130E) ||
- (adev->pdev->device == 0x1315) ||
- (adev->pdev->device == 0x131B)) {
- adev->gfx.config.max_cu_per_sh = 4;
- adev->gfx.config.max_backends_per_se = 1;
- } else {
- adev->gfx.config.max_cu_per_sh = 3;
- adev->gfx.config.max_backends_per_se = 1;
- }
+ adev->gfx.config.max_cu_per_sh = 8;
+ adev->gfx.config.max_backends_per_se = 2;
adev->gfx.config.max_sh_per_se = 1;
adev->gfx.config.max_texture_channel_caches = 4;
adev->gfx.config.max_gprs = 256;
diff --git a/drivers/gpu/drm/amd/amdgpu/si_dpm.c b/drivers/gpu/drm/amd/amdgpu/si_dpm.c
index 4cb347e..002862b 100644
--- a/drivers/gpu/drm/amd/amdgpu/si_dpm.c
+++ b/drivers/gpu/drm/amd/amdgpu/si_dpm.c
@@ -3507,6 +3507,11 @@
max_sclk = 75000;
max_mclk = 80000;
}
+ if ((adev->pdev->revision == 0xC3) ||
+ (adev->pdev->device == 0x6665)) {
+ max_sclk = 60000;
+ max_mclk = 80000;
+ }
} else if (adev->asic_type == CHIP_OLAND) {
if ((adev->pdev->revision == 0xC7) ||
(adev->pdev->revision == 0x80) ||
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
index 1e50647..8c6e47c 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
@@ -519,11 +519,17 @@
return ret;
}
+static void kfd_topology_kobj_release(struct kobject *kobj)
+{
+ kfree(kobj);
+}
+
static const struct sysfs_ops sysprops_ops = {
.show = sysprops_show,
};
static struct kobj_type sysprops_type = {
+ .release = kfd_topology_kobj_release,
.sysfs_ops = &sysprops_ops,
};
@@ -559,6 +565,7 @@
};
static struct kobj_type iolink_type = {
+ .release = kfd_topology_kobj_release,
.sysfs_ops = &iolink_ops,
};
@@ -586,6 +593,7 @@
};
static struct kobj_type mem_type = {
+ .release = kfd_topology_kobj_release,
.sysfs_ops = &mem_ops,
};
@@ -625,6 +633,7 @@
};
static struct kobj_type cache_type = {
+ .release = kfd_topology_kobj_release,
.sysfs_ops = &cache_ops,
};
@@ -747,6 +756,7 @@
};
static struct kobj_type node_type = {
+ .release = kfd_topology_kobj_release,
.sysfs_ops = &node_ops,
};
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 4e5ba7e..03a9f20 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -115,6 +115,9 @@
/* AEO model 0 reports 8 bpc, but is a 6 bpc panel */
{ "AEO", 0, EDID_QUIRK_FORCE_6BPC },
+ /* CPT panel of Asus UX303LA reports 8 bpc, but is a 6 bpc panel */
+ { "CPT", 0x17df, EDID_QUIRK_FORCE_6BPC },
+
/* Belinea 10 15 55 */
{ "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 },
{ "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 },
@@ -3806,8 +3809,7 @@
* @edid: EDID to parse
*
* Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The
- * Conn_Type, HDCP and Port_ID ELD fields are left for the graphics driver to
- * fill in.
+ * HDCP and Port_ID ELD fields are left for the graphics driver to fill in.
*/
void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
{
@@ -3888,6 +3890,12 @@
}
eld[5] |= total_sad_count << 4;
+ if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
+ connector->connector_type == DRM_MODE_CONNECTOR_eDP)
+ eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_CONN_TYPE_DP;
+ else
+ eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_CONN_TYPE_HDMI;
+
eld[DRM_ELD_BASELINE_ELD_LEN] =
DIV_ROUND_UP(drm_eld_calc_baseline_block_size(eld), 4);
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 48a6167..00c815a 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -1202,9 +1202,9 @@
if (atomic_dec_and_test(&vblank->refcount)) {
if (drm_vblank_offdelay == 0)
return;
- else if (dev->vblank_disable_immediate || drm_vblank_offdelay < 0)
+ else if (drm_vblank_offdelay < 0)
vblank_disable_fn((unsigned long)vblank);
- else
+ else if (!dev->vblank_disable_immediate)
mod_timer(&vblank->disable_timer,
jiffies + ((drm_vblank_offdelay * HZ)/1000));
}
@@ -1819,6 +1819,16 @@
wake_up(&vblank->queue);
drm_handle_vblank_events(dev, pipe);
+ /* With instant-off, we defer disabling the interrupt until after
+ * we finish processing the following vblank. The disable has to
+ * be last (after drm_handle_vblank_events) so that the timestamp
+ * is always accurate.
+ */
+ if (dev->vblank_disable_immediate &&
+ drm_vblank_offdelay > 0 &&
+ !atomic_read(&vblank->refcount))
+ vblank_disable_fn((unsigned long)vblank);
+
spin_unlock_irqrestore(&dev->event_lock, irqflags);
return true;
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index 276474d..d7822be 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -461,6 +461,26 @@
}
/**
+ * drm_kms_helper_is_poll_worker - is %current task an output poll worker?
+ *
+ * Determine if %current task is an output poll worker. This can be used
+ * to select distinct code paths for output polling versus other contexts.
+ *
+ * One use case is to avoid a deadlock between the output poll worker and
+ * the autosuspend worker wherein the latter waits for polling to finish
+ * upon calling drm_kms_helper_poll_disable(), while the former waits for
+ * runtime suspend to finish upon calling pm_runtime_get_sync() in a
+ * connector ->detect hook.
+ */
+bool drm_kms_helper_is_poll_worker(void)
+{
+ struct work_struct *work = current_work();
+
+ return work && work->func == output_poll_execute;
+}
+EXPORT_SYMBOL(drm_kms_helper_is_poll_worker);
+
+/**
* drm_kms_helper_poll_disable - disable output polling
* @dev: drm_device
*
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 7513e76..bae62cf 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1703,6 +1703,8 @@
if (IS_BROXTON(dev_priv) ||
!(dev_priv->suspended_to_idle && dev_priv->csr.dmc_payload))
intel_power_domains_init_hw(dev_priv, true);
+ else
+ intel_display_set_init_power(dev_priv, true);
enable_rpm_wakeref_asserts(dev_priv);
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 13c3061..0c935de 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1452,12 +1452,20 @@
struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
struct edid *edid;
bool connected = false;
+ struct i2c_adapter *i2c;
intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
- edid = drm_get_edid(connector,
- intel_gmbus_get_adapter(dev_priv,
- intel_hdmi->ddc_bus));
+ i2c = intel_gmbus_get_adapter(dev_priv, intel_hdmi->ddc_bus);
+
+ edid = drm_get_edid(connector, i2c);
+
+ if (!edid && !intel_gmbus_is_forced_bit(i2c)) {
+ DRM_DEBUG_KMS("HDMI GMBUS EDID read failed, retry using GPIO bit-banging\n");
+ intel_gmbus_force_bit(i2c, true);
+ edid = drm_get_edid(connector, i2c);
+ intel_gmbus_force_bit(i2c, false);
+ }
intel_hdmi_dp_dual_mode_detect(connector, edid != NULL);
diff --git a/drivers/gpu/drm/msm/dp/dp_usbpd.c b/drivers/gpu/drm/msm/dp/dp_usbpd.c
index 42eb9b0..3a14f7c 100644
--- a/drivers/gpu/drm/msm/dp/dp_usbpd.c
+++ b/drivers/gpu/drm/msm/dp/dp_usbpd.c
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/device.h>
+#include <linux/delay.h>
#include "dp_usbpd.h"
@@ -314,11 +315,44 @@
return ret;
}
+
+static int dp_usbpd_get_ss_lanes(struct dp_usbpd_private *pd)
+{
+ int rc = 0;
+ int timeout = 250;
+
+ /*
+ * By default, USB reserves two lanes for Super Speed.
+ * Which means DP has remaining two lanes to operate on.
+ * If multi-function is not supported, request USB to
+ * release the Super Speed lanes so that DP can use
+ * all four lanes in case DPCD indicates support for
+ * four lanes.
+ */
+ if (!pd->dp_usbpd.multi_func) {
+ while (timeout) {
+ rc = pd->svid_handler.request_usb_ss_lane(
+ pd->pd, &pd->svid_handler);
+ if (rc != -EBUSY)
+ break;
+
+ pr_warn("USB busy, retry\n");
+
+ /* wait for hw recommended delay for usb */
+ msleep(20);
+ timeout--;
+ }
+ }
+
+ return rc;
+}
+
static void dp_usbpd_response_cb(struct usbpd_svid_handler *hdlr, u8 cmd,
enum usbpd_svdm_cmd_type cmd_type,
const u32 *vdos, int num_vdos)
{
struct dp_usbpd_private *pd;
+ int rc = 0;
pd = container_of(hdlr, struct dp_usbpd_private, svid_handler);
@@ -380,17 +414,11 @@
pd->dp_usbpd.orientation = usbpd_get_plug_orientation(pd->pd);
- /*
- * By default, USB reserves two lanes for Super Speed.
- * Which means DP has remaining two lanes to operate on.
- * If multi-function is not supported, request USB to
- * release the Super Speed lanes so that DP can use
- * all four lanes in case DPCD indicates support for
- * four lanes.
- */
- if (!pd->dp_usbpd.multi_func)
- pd->svid_handler.request_usb_ss_lane(pd->pd,
- &pd->svid_handler);
+ rc = dp_usbpd_get_ss_lanes(pd);
+ if (rc) {
+ pr_err("failed to get SuperSpeed lanes\n");
+ break;
+ }
if (pd->dp_cb && pd->dp_cb->configure)
pd->dp_cb->configure(pd->dev);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
index 93e364de..c7c640f5 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
@@ -1133,6 +1133,11 @@
}
kickoff:
+ /* check if custom dma scheduling line needed */
+ if ((dsi_ctrl->host_config.panel_mode == DSI_OP_VIDEO_MODE) &&
+ (flags & DSI_CTRL_CMD_CUSTOM_DMA_SCHED))
+ line_no = dsi_ctrl->host_config.u.video_engine.dma_sched_line;
+
timing = &(dsi_ctrl->host_config.video_timing);
if (timing)
line_no += timing->v_back_porch + timing->v_sync_width +
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
index 4f85cda..e08ef99 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
@@ -38,6 +38,8 @@
* @DSI_CTRL_CMD_LAST_COMMAND: Trigger the DMA cmd transfer if this is last
* command in the batch.
* @DSI_CTRL_CMD_NON_EMBEDDED_MODE:Trasfer cmd packets in non embedded mode.
+ * @DSI_CTRL_CMD_CUSTOM_DMA_SCHED: Use the dma scheduling line number defined in
+ * display panel dtsi file instead of default.
*/
#define DSI_CTRL_CMD_READ 0x1
#define DSI_CTRL_CMD_BROADCAST 0x2
@@ -47,6 +49,7 @@
#define DSI_CTRL_CMD_FETCH_MEMORY 0x20
#define DSI_CTRL_CMD_LAST_COMMAND 0x40
#define DSI_CTRL_CMD_NON_EMBEDDED_MODE 0x80
+#define DSI_CTRL_CMD_CUSTOM_DMA_SCHED 0x100
/* DSI embedded mode fifo size
* If the command is greater than 256 bytes it is sent in non-embedded mode.
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
index d45f849..6540182 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-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, 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
@@ -438,6 +438,8 @@
* @bllp_lp11_en: Enter low power stop mode (LP-11) during BLLP.
* @traffic_mode: Traffic mode for video stream.
* @vc_id: Virtual channel identifier.
+ * @dma_sched_line: Line number, after vactive end, at which command dma
+ * needs to be triggered.
*/
struct dsi_video_engine_cfg {
bool last_line_interleave_en;
@@ -449,6 +451,7 @@
bool bllp_lp11_en;
enum dsi_video_traffic_mode traffic_mode;
u32 vc_id;
+ u32 dma_sched_line;
};
/**
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index 199833d..adcec15 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -566,7 +566,8 @@
lenp = config->status_valid_params ?: config->status_cmds_rlen;
count = config->status_cmd.count;
cmds = config->status_cmd.cmds;
- flags |= (DSI_CTRL_CMD_FETCH_MEMORY | DSI_CTRL_CMD_READ);
+ flags |= (DSI_CTRL_CMD_FETCH_MEMORY | DSI_CTRL_CMD_READ |
+ DSI_CTRL_CMD_CUSTOM_DMA_SCHED);
for (i = 0; i < count; ++i) {
memset(config->status_buf, 0x0, SZ_4K);
@@ -3050,6 +3051,8 @@
pr_err("%s: failed to disable ulps. rc=%d\n",
__func__, rc);
}
+ /* dsi will not be able to serve irqs from here on */
+ dsi_display_ctrl_irq_update(display, false);
}
return rc;
@@ -3157,9 +3160,6 @@
if ((clk_type & DSI_CORE_CLK) &&
(curr_state == DSI_CLK_OFF)) {
- /* dsi will not be able to serve irqs from here */
- dsi_display_ctrl_irq_update(display, false);
-
rc = dsi_display_phy_power_off(display);
if (rc)
pr_err("[%s] failed to power off PHY, rc=%d\n",
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
index e62c65e..79df631 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
@@ -1217,6 +1217,7 @@
const char *traffic_mode;
u32 vc_id = 0;
u32 val = 0;
+ u32 line_no = 0;
rc = of_property_read_u32(of_node, "qcom,mdss-dsi-h-sync-pulse", &val);
if (rc) {
@@ -1279,6 +1280,17 @@
cfg->vc_id = vc_id;
}
+ rc = of_property_read_u32(of_node, "qcom,mdss-dsi-dma-schedule-line",
+ &line_no);
+ if (rc) {
+ pr_debug("[%s] set default dma scheduling line no\n", name);
+ cfg->dma_sched_line = 0x1;
+ /* do not fail since we have default value */
+ rc = 0;
+ } else {
+ cfg->dma_sched_line = line_no;
+ }
+
error:
return rc;
}
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index d9aad88..277b421 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -96,13 +96,16 @@
return p;
}
+ msm_obj->pages = p;
+
msm_obj->sgt = drm_prime_pages_to_sg(p, npages);
if (IS_ERR(msm_obj->sgt)) {
- dev_err(dev->dev, "failed to allocate sgt\n");
- return ERR_CAST(msm_obj->sgt);
- }
+ void *ptr = ERR_CAST(msm_obj->sgt);
- msm_obj->pages = p;
+ dev_err(dev->dev, "failed to allocate sgt\n");
+ msm_obj->sgt = NULL;
+ return ptr;
+ }
/*
* Make sure to flush the CPU cache for newly allocated memory
@@ -121,7 +124,9 @@
struct msm_gem_object *msm_obj = to_msm_bo(obj);
if (msm_obj->pages) {
- sg_free_table(msm_obj->sgt);
+ if (msm_obj->sgt)
+ sg_free_table(msm_obj->sgt);
+
kfree(msm_obj->sgt);
if (use_pages(obj))
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 3021496..bea6693 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -3402,22 +3402,42 @@
static void _sde_encoder_setup_dither(struct sde_encoder_phys *phys)
{
void *dither_cfg;
- int ret = 0;
+ int ret = 0, rc;
size_t len = 0;
enum sde_rm_topology_name topology;
+ struct drm_encoder *drm_enc;
+ struct msm_mode_info mode_info;
+ struct msm_display_dsc_info *dsc = NULL;
+ struct sde_encoder_virt *sde_enc;
if (!phys || !phys->connector || !phys->hw_pp ||
- !phys->hw_pp->ops.setup_dither)
+ !phys->hw_pp->ops.setup_dither || !phys->parent)
return;
+
topology = sde_connector_get_topology_name(phys->connector);
if ((topology == SDE_RM_TOPOLOGY_PPSPLIT) &&
(phys->split_role == ENC_ROLE_SLAVE))
return;
- ret = sde_connector_get_dither_cfg(phys->connector,
+ drm_enc = phys->parent;
+ sde_enc = to_sde_encoder_virt(drm_enc);
+ rc = _sde_encoder_get_mode_info(&sde_enc->base, &mode_info);
+ if (rc) {
+ SDE_ERROR_ENC(sde_enc, "failed to get mode info\n");
+ return;
+ }
+
+ dsc = &mode_info.comp_info.dsc_info;
+ /* disable dither for 10 bpp or 10bpc dsc config */
+ if (dsc->bpp == 10 || dsc->bpc == 10) {
+ phys->hw_pp->ops.setup_dither(phys->hw_pp, NULL, 0);
+ } else {
+ ret = sde_connector_get_dither_cfg(phys->connector,
phys->connector->state, &dither_cfg, &len);
- if (!ret)
- phys->hw_pp->ops.setup_dither(phys->hw_pp, dither_cfg, len);
+ if (!ret)
+ phys->hw_pp->ops.setup_dither(phys->hw_pp,
+ dither_cfg, len);
+ }
}
static u32 _sde_encoder_calculate_linetime(struct sde_encoder_virt *sde_enc,
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index c108408..56c288f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -271,9 +271,15 @@
nv_connector->edid = NULL;
}
- ret = pm_runtime_get_sync(connector->dev->dev);
- if (ret < 0 && ret != -EACCES)
- return conn_status;
+ /* Outputs are only polled while runtime active, so acquiring a
+ * runtime PM ref here is unnecessary (and would deadlock upon
+ * runtime suspend because it waits for polling to finish).
+ */
+ if (!drm_kms_helper_is_poll_worker()) {
+ ret = pm_runtime_get_sync(connector->dev->dev);
+ if (ret < 0 && ret != -EACCES)
+ return conn_status;
+ }
nv_encoder = nouveau_connector_ddc_detect(connector);
if (nv_encoder && (i2c = nv_encoder->i2c) != NULL) {
@@ -348,8 +354,10 @@
out:
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
return conn_status;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 2c2b86d..6526a33 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -106,7 +106,7 @@
};
struct nouveau_display *disp = nouveau_display(crtc->dev);
struct drm_vblank_crtc *vblank = &crtc->dev->vblank[drm_crtc_index(crtc)];
- int ret, retry = 1;
+ int ret, retry = 20;
do {
ret = nvif_mthd(&disp->disp, 0, &args, sizeof(args));
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c
index a4cb824..245c946 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c
@@ -136,6 +136,13 @@
return ret;
pci->irq = pdev->irq;
+
+ /* Ensure MSI interrupts are armed, for the case where there are
+ * already interrupts pending (for whatever reason) at load time.
+ */
+ if (pci->msi)
+ pci->func->msi_rearm(pci);
+
return ret;
}
diff --git a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c
index e859b3f..6112c32 100644
--- a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c
+++ b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c
@@ -456,6 +456,8 @@
}
static const struct of_device_id td028ttec1_of_match[] = {
+ { .compatible = "omapdss,tpo,td028ttec1", },
+ /* keep to not break older DTB */
{ .compatible = "omapdss,toppoly,td028ttec1", },
{},
};
@@ -475,6 +477,7 @@
module_spi_driver(td028ttec1_spi_driver);
+MODULE_ALIAS("spi:tpo,td028ttec1");
MODULE_ALIAS("spi:toppoly,td028ttec1");
MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
MODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver");
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
index 4b83e9e..7def040 100644
--- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
@@ -298,7 +298,12 @@
msecs_to_jiffies(100))) {
dev_err(dmm->dev, "timed out waiting for done\n");
ret = -ETIMEDOUT;
+ goto cleanup;
}
+
+ /* Check the engine status before continue */
+ ret = wait_status(engine, DMM_PATSTATUS_READY |
+ DMM_PATSTATUS_VALID | DMM_PATSTATUS_DONE);
}
cleanup:
diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c
index 2cd879a..cdbb6e6 100644
--- a/drivers/gpu/drm/qxl/qxl_fb.c
+++ b/drivers/gpu/drm/qxl/qxl_fb.c
@@ -387,9 +387,11 @@
int qxl_fbdev_init(struct qxl_device *qdev)
{
+ int ret = 0;
+
+#ifdef CONFIG_DRM_FBDEV_EMULATION
struct qxl_fbdev *qfbdev;
int bpp_sel = 32; /* TODO: parameter from somewhere? */
- int ret;
qfbdev = kzalloc(sizeof(struct qxl_fbdev), GFP_KERNEL);
if (!qfbdev)
@@ -423,6 +425,8 @@
drm_fb_helper_fini(&qfbdev->helper);
free:
kfree(qfbdev);
+#endif
+
return ret;
}
@@ -438,6 +442,9 @@
void qxl_fbdev_set_suspend(struct qxl_device *qdev, int state)
{
+ if (!qdev->mode_info.qfbdev)
+ return;
+
drm_fb_helper_set_suspend(&qdev->mode_info.qfbdev->helper, state);
}
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c
index edee6a5..b99f3e5 100644
--- a/drivers/gpu/drm/radeon/cik.c
+++ b/drivers/gpu/drm/radeon/cik.c
@@ -3244,35 +3244,8 @@
case CHIP_KAVERI:
rdev->config.cik.max_shader_engines = 1;
rdev->config.cik.max_tile_pipes = 4;
- if ((rdev->pdev->device == 0x1304) ||
- (rdev->pdev->device == 0x1305) ||
- (rdev->pdev->device == 0x130C) ||
- (rdev->pdev->device == 0x130F) ||
- (rdev->pdev->device == 0x1310) ||
- (rdev->pdev->device == 0x1311) ||
- (rdev->pdev->device == 0x131C)) {
- rdev->config.cik.max_cu_per_sh = 8;
- rdev->config.cik.max_backends_per_se = 2;
- } else if ((rdev->pdev->device == 0x1309) ||
- (rdev->pdev->device == 0x130A) ||
- (rdev->pdev->device == 0x130D) ||
- (rdev->pdev->device == 0x1313) ||
- (rdev->pdev->device == 0x131D)) {
- rdev->config.cik.max_cu_per_sh = 6;
- rdev->config.cik.max_backends_per_se = 2;
- } else if ((rdev->pdev->device == 0x1306) ||
- (rdev->pdev->device == 0x1307) ||
- (rdev->pdev->device == 0x130B) ||
- (rdev->pdev->device == 0x130E) ||
- (rdev->pdev->device == 0x1315) ||
- (rdev->pdev->device == 0x1318) ||
- (rdev->pdev->device == 0x131B)) {
- rdev->config.cik.max_cu_per_sh = 4;
- rdev->config.cik.max_backends_per_se = 1;
- } else {
- rdev->config.cik.max_cu_per_sh = 3;
- rdev->config.cik.max_backends_per_se = 1;
- }
+ rdev->config.cik.max_cu_per_sh = 8;
+ rdev->config.cik.max_backends_per_se = 2;
rdev->config.cik.max_sh_per_se = 1;
rdev->config.cik.max_texture_channel_caches = 4;
rdev->config.cik.max_gprs = 256;
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 27affbd..f416f5c 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -90,25 +90,18 @@
/* don't do anything if sink is not display port, i.e.,
* passive dp->(dvi|hdmi) adaptor
*/
- if (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) {
- int saved_dpms = connector->dpms;
- /* Only turn off the display if it's physically disconnected */
- if (!radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) {
- drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
- } else if (radeon_dp_needs_link_train(radeon_connector)) {
- /* Don't try to start link training before we
- * have the dpcd */
- if (!radeon_dp_getdpcd(radeon_connector))
- return;
+ if (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT &&
+ radeon_hpd_sense(rdev, radeon_connector->hpd.hpd) &&
+ radeon_dp_needs_link_train(radeon_connector)) {
+ /* Don't start link training before we have the DPCD */
+ if (!radeon_dp_getdpcd(radeon_connector))
+ return;
- /* set it to OFF so that drm_helper_connector_dpms()
- * won't return immediately since the current state
- * is ON at this point.
- */
- connector->dpms = DRM_MODE_DPMS_OFF;
- drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
- }
- connector->dpms = saved_dpms;
+ /* Turn the connector off and back on immediately, which
+ * will trigger link training
+ */
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
}
}
}
@@ -897,9 +890,11 @@
enum drm_connector_status ret = connector_status_disconnected;
int r;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
if (encoder) {
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
@@ -922,8 +917,12 @@
/* check acpi lid status ??? */
radeon_connector_update_scratch_regs(connector, ret);
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
+
return ret;
}
@@ -1037,9 +1036,11 @@
enum drm_connector_status ret = connector_status_disconnected;
int r;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
encoder = radeon_best_single_encoder(connector);
if (!encoder)
@@ -1106,8 +1107,10 @@
radeon_connector_update_scratch_regs(connector, ret);
out:
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
return ret;
}
@@ -1171,9 +1174,11 @@
if (!radeon_connector->dac_load_detect)
return ret;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
encoder = radeon_best_single_encoder(connector);
if (!encoder)
@@ -1185,8 +1190,12 @@
if (ret == connector_status_connected)
ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, false);
radeon_connector_update_scratch_regs(connector, ret);
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
+
return ret;
}
@@ -1249,9 +1258,11 @@
enum drm_connector_status ret = connector_status_disconnected;
bool dret = false, broken_edid = false;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
if (radeon_connector->detected_hpd_without_ddc) {
force = true;
@@ -1434,8 +1445,10 @@
}
exit:
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
return ret;
}
@@ -1686,9 +1699,11 @@
if (radeon_dig_connector->is_mst)
return connector_status_disconnected;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
if (!force && radeon_check_hpd_status_unchanged(connector)) {
ret = connector->status;
@@ -1775,8 +1790,10 @@
}
out:
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
return ret;
}
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index cdb8cb56..ca1caf4 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -1352,6 +1352,12 @@
return ERR_PTR(-ENOENT);
}
+ /* Handle is imported dma-buf, so cannot be migrated to VRAM for scanout */
+ if (obj->import_attach) {
+ DRM_DEBUG_KMS("Cannot create framebuffer from imported dma_buf\n");
+ return ERR_PTR(-EINVAL);
+ }
+
radeon_fb = kzalloc(sizeof(*radeon_fb), GFP_KERNEL);
if (radeon_fb == NULL) {
drm_gem_object_unreference_unlocked(obj);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 3322b15..1c4d95d 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -512,6 +512,13 @@
rcar_du_crtc_stop(rcrtc);
rcar_du_crtc_put(rcrtc);
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (crtc->state->event) {
+ drm_crtc_send_vblank_event(crtc, crtc->state->event);
+ crtc->state->event = NULL;
+ }
+ spin_unlock_irq(&crtc->dev->event_lock);
+
rcrtc->outputs = 0;
}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index c7eba30..6e3c4ac 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -503,7 +503,7 @@
ret = pm_runtime_get_sync(vop->dev);
if (ret < 0) {
dev_err(vop->dev, "failed to get pm runtime: %d\n", ret);
- goto err_put_pm_runtime;
+ return ret;
}
ret = clk_enable(vop->hclk);
@@ -1348,10 +1348,16 @@
return PTR_ERR(vop->dclk);
}
+ ret = pm_runtime_get_sync(vop->dev);
+ if (ret < 0) {
+ dev_err(vop->dev, "failed to get pm runtime: %d\n", ret);
+ return ret;
+ }
+
ret = clk_prepare(vop->dclk);
if (ret < 0) {
dev_err(vop->dev, "failed to prepare dclk\n");
- return ret;
+ goto err_put_pm_runtime;
}
/* Enable both the hclk and aclk to setup the vop */
@@ -1411,6 +1417,8 @@
vop->is_enabled = false;
+ pm_runtime_put_sync(vop->dev);
+
return 0;
err_disable_aclk:
@@ -1419,6 +1427,8 @@
clk_disable_unprepare(vop->hclk);
err_unprepare_dclk:
clk_unprepare(vop->dclk);
+err_put_pm_runtime:
+ pm_runtime_put_sync(vop->dev);
return ret;
}
@@ -1519,12 +1529,6 @@
if (!vop->regsbak)
return -ENOMEM;
- ret = vop_initial(vop);
- if (ret < 0) {
- dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret);
- return ret;
- }
-
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "cannot find irq for vop\n");
@@ -1551,8 +1555,17 @@
pm_runtime_enable(&pdev->dev);
+ ret = vop_initial(vop);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret);
+ goto err_disable_pm_runtime;
+ }
+
return 0;
+err_disable_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+ vop_destroy_crtc(vop);
err_enable_irq:
enable_irq(vop->irq); /* To balance out the disable_irq above */
return ret;
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
index 4a19221..caba031 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
@@ -19,6 +19,7 @@
#include <linux/clk-provider.h>
#include <linux/ioport.h>
#include <linux/of_address.h>
+#include <linux/of_graph.h>
#include <linux/of_irq.h>
#include <linux/regmap.h>
@@ -136,5 +137,9 @@
drm_crtc_helper_add(&scrtc->crtc, &sun4i_crtc_helper_funcs);
+ /* Set crtc.port to output port node of the tcon */
+ scrtc->crtc.port = of_graph_get_port_by_id(drv->tcon->dev->of_node,
+ 1);
+
return scrtc;
}
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 1feec34..9e77fc0 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -145,7 +145,7 @@
ret = component_bind_all(drm->dev, drm);
if (ret) {
dev_err(drm->dev, "Couldn't bind all pipelines components\n");
- goto free_drm;
+ goto cleanup_mode_config;
}
/* Create our layers */
@@ -153,7 +153,7 @@
if (IS_ERR(drv->layers)) {
dev_err(drm->dev, "Couldn't create the planes\n");
ret = PTR_ERR(drv->layers);
- goto free_drm;
+ goto cleanup_mode_config;
}
/* Create our CRTC */
@@ -161,7 +161,7 @@
if (!drv->crtc) {
dev_err(drm->dev, "Couldn't create the CRTC\n");
ret = -EINVAL;
- goto free_drm;
+ goto cleanup_mode_config;
}
drm->irq_enabled = true;
@@ -173,7 +173,7 @@
if (IS_ERR(drv->fbdev)) {
dev_err(drm->dev, "Couldn't create our framebuffer\n");
ret = PTR_ERR(drv->fbdev);
- goto free_drm;
+ goto cleanup_mode_config;
}
/* Enable connectors polling */
@@ -181,10 +181,16 @@
ret = drm_dev_register(drm, 0);
if (ret)
- goto free_drm;
+ goto finish_poll;
return 0;
+finish_poll:
+ drm_kms_helper_poll_fini(drm);
+ sun4i_framebuffer_free(drm);
+cleanup_mode_config:
+ drm_mode_config_cleanup(drm);
+ drm_vblank_cleanup(drm);
free_drm:
drm_dev_unref(drm);
return ret;
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index c6afb24..f2975a1 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -336,12 +336,11 @@
}
}
- return sun4i_dclk_create(dev, tcon);
+ return 0;
}
static void sun4i_tcon_free_clocks(struct sun4i_tcon *tcon)
{
- sun4i_dclk_free(tcon);
clk_disable_unprepare(tcon->clk);
}
@@ -506,22 +505,28 @@
return ret;
}
- ret = sun4i_tcon_init_regmap(dev, tcon);
- if (ret) {
- dev_err(dev, "Couldn't init our TCON regmap\n");
- goto err_assert_reset;
- }
-
ret = sun4i_tcon_init_clocks(dev, tcon);
if (ret) {
dev_err(dev, "Couldn't init our TCON clocks\n");
goto err_assert_reset;
}
+ ret = sun4i_tcon_init_regmap(dev, tcon);
+ if (ret) {
+ dev_err(dev, "Couldn't init our TCON regmap\n");
+ goto err_free_clocks;
+ }
+
+ ret = sun4i_dclk_create(dev, tcon);
+ if (ret) {
+ dev_err(dev, "Couldn't create our TCON dot clock\n");
+ goto err_free_clocks;
+ }
+
ret = sun4i_tcon_init_irq(dev, tcon);
if (ret) {
dev_err(dev, "Couldn't init our TCON interrupts\n");
- goto err_free_clocks;
+ goto err_free_dotclock;
}
ret = sun4i_rgb_init(drm);
@@ -530,6 +535,8 @@
return 0;
+err_free_dotclock:
+ sun4i_dclk_free(tcon);
err_free_clocks:
sun4i_tcon_free_clocks(tcon);
err_assert_reset:
@@ -542,6 +549,7 @@
{
struct sun4i_tcon *tcon = dev_get_drvdata(dev);
+ sun4i_dclk_free(tcon);
sun4i_tcon_free_clocks(tcon);
}
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_regs.h b/drivers/gpu/drm/tilcdc/tilcdc_regs.h
index f57c0d6..19d8dc3 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_regs.h
+++ b/drivers/gpu/drm/tilcdc/tilcdc_regs.h
@@ -124,7 +124,7 @@
struct tilcdc_drm_private *priv = dev->dev_private;
volatile void __iomem *addr = priv->mmio + reg;
-#ifdef iowrite64
+#if defined(iowrite64) && !defined(iowrite64_is_nonatomic)
iowrite64(data, addr);
#else
__iowmb();
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 94983e8..5a18408 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -1209,18 +1209,20 @@
if (likely(!ret))
ret = ttm_bo_validate(bo, placement, interruptible, false);
- if (!resv) {
+ if (!resv)
ttm_bo_unreserve(bo);
- } else if (!(bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) {
+ if (unlikely(ret)) {
+ ttm_bo_unref(&bo);
+ return ret;
+ }
+
+ if (resv && !(bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) {
spin_lock(&bo->glob->lru_lock);
ttm_bo_add_to_lru(bo);
spin_unlock(&bo->glob->lru_lock);
}
- if (unlikely(ret))
- ttm_bo_unref(&bo);
-
return ret;
}
EXPORT_SYMBOL(ttm_bo_init);
diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c
index ddd6badd..c756b2b 100644
--- a/drivers/gpu/drm/ttm/ttm_page_alloc.c
+++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c
@@ -818,6 +818,8 @@
pr_info("Initializing pool allocator\n");
_manager = kzalloc(sizeof(*_manager), GFP_KERNEL);
+ if (!_manager)
+ return -ENOMEM;
ttm_page_pool_init_locked(&_manager->wc_pool, GFP_HIGHUSER, "wc");
diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
index 611b6b9..67ea2ce 100644
--- a/drivers/gpu/drm/udl/udl_fb.c
+++ b/drivers/gpu/drm/udl/udl_fb.c
@@ -158,10 +158,15 @@
{
unsigned long start = vma->vm_start;
unsigned long size = vma->vm_end - vma->vm_start;
- unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long offset;
unsigned long page, pos;
- if (offset + size > info->fix.smem_len)
+ if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
+ return -EINVAL;
+
+ offset = vma->vm_pgoff << PAGE_SHIFT;
+
+ if (offset > info->fix.smem_len || size > info->fix.smem_len - offset)
return -EINVAL;
pos = (unsigned long)info->fix.smem_start + offset;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
index d2d9395..aec6e9e 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
@@ -433,7 +433,7 @@
set.y = 0;
set.mode = NULL;
set.fb = NULL;
- set.num_connectors = 1;
+ set.num_connectors = 0;
set.connectors = &par->con;
ret = drm_mode_set_config_internal(&set);
if (ret) {
@@ -821,7 +821,9 @@
flush_delayed_work(&par->local_work);
mutex_lock(&par->bo_mutex);
+ drm_modeset_lock_all(vmw_priv->dev);
(void) vmw_fb_kms_detach(par, true, false);
+ drm_modeset_unlock_all(vmw_priv->dev);
mutex_unlock(&par->bo_mutex);
return 0;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index bf28ccc..87086af 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -27,7 +27,6 @@
#include "vmwgfx_kms.h"
-
/* Might need a hrtimer here? */
#define VMWGFX_PRESENT_RATE ((HZ / 60 > 0) ? HZ / 60 : 1)
@@ -1933,9 +1932,12 @@
* Helper to be used if an error forces the caller to undo the actions of
* vmw_kms_helper_resource_prepare.
*/
-void vmw_kms_helper_resource_revert(struct vmw_resource *res)
+void vmw_kms_helper_resource_revert(struct vmw_validation_ctx *ctx)
{
- vmw_kms_helper_buffer_revert(res->backup);
+ struct vmw_resource *res = ctx->res;
+
+ vmw_kms_helper_buffer_revert(ctx->buf);
+ vmw_dmabuf_unreference(&ctx->buf);
vmw_resource_unreserve(res, false, NULL, 0);
mutex_unlock(&res->dev_priv->cmdbuf_mutex);
}
@@ -1952,10 +1954,14 @@
* interrupted by a signal.
*/
int vmw_kms_helper_resource_prepare(struct vmw_resource *res,
- bool interruptible)
+ bool interruptible,
+ struct vmw_validation_ctx *ctx)
{
int ret = 0;
+ ctx->buf = NULL;
+ ctx->res = res;
+
if (interruptible)
ret = mutex_lock_interruptible(&res->dev_priv->cmdbuf_mutex);
else
@@ -1974,6 +1980,8 @@
res->dev_priv->has_mob);
if (ret)
goto out_unreserve;
+
+ ctx->buf = vmw_dmabuf_reference(res->backup);
}
ret = vmw_resource_validate(res);
if (ret)
@@ -1981,7 +1989,7 @@
return 0;
out_revert:
- vmw_kms_helper_buffer_revert(res->backup);
+ vmw_kms_helper_buffer_revert(ctx->buf);
out_unreserve:
vmw_resource_unreserve(res, false, NULL, 0);
out_unlock:
@@ -1997,11 +2005,13 @@
* @out_fence: Optional pointer to a fence pointer. If non-NULL, a
* ref-counted fence pointer is returned here.
*/
-void vmw_kms_helper_resource_finish(struct vmw_resource *res,
- struct vmw_fence_obj **out_fence)
+void vmw_kms_helper_resource_finish(struct vmw_validation_ctx *ctx,
+ struct vmw_fence_obj **out_fence)
{
- if (res->backup || out_fence)
- vmw_kms_helper_buffer_finish(res->dev_priv, NULL, res->backup,
+ struct vmw_resource *res = ctx->res;
+
+ if (ctx->buf || out_fence)
+ vmw_kms_helper_buffer_finish(res->dev_priv, NULL, ctx->buf,
out_fence, NULL);
vmw_resource_unreserve(res, false, NULL, 0);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
index ff4803c..2dd0539 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
@@ -183,6 +183,11 @@
int set_gui_y;
};
+struct vmw_validation_ctx {
+ struct vmw_resource *res;
+ struct vmw_dma_buffer *buf;
+};
+
#define vmw_crtc_to_du(x) \
container_of(x, struct vmw_display_unit, crtc)
#define vmw_connector_to_du(x) \
@@ -233,9 +238,10 @@
struct drm_vmw_fence_rep __user *
user_fence_rep);
int vmw_kms_helper_resource_prepare(struct vmw_resource *res,
- bool interruptible);
-void vmw_kms_helper_resource_revert(struct vmw_resource *res);
-void vmw_kms_helper_resource_finish(struct vmw_resource *res,
+ bool interruptible,
+ struct vmw_validation_ctx *ctx);
+void vmw_kms_helper_resource_revert(struct vmw_validation_ctx *ctx);
+void vmw_kms_helper_resource_finish(struct vmw_validation_ctx *ctx,
struct vmw_fence_obj **out_fence);
int vmw_kms_readback(struct vmw_private *dev_priv,
struct drm_file *file_priv,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
index f423590..a6ca218 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
@@ -753,12 +753,13 @@
struct vmw_framebuffer_surface *vfbs =
container_of(framebuffer, typeof(*vfbs), base);
struct vmw_kms_sou_surface_dirty sdirty;
+ struct vmw_validation_ctx ctx;
int ret;
if (!srf)
srf = &vfbs->surface->res;
- ret = vmw_kms_helper_resource_prepare(srf, true);
+ ret = vmw_kms_helper_resource_prepare(srf, true, &ctx);
if (ret)
return ret;
@@ -777,7 +778,7 @@
ret = vmw_kms_helper_dirty(dev_priv, framebuffer, clips, vclips,
dest_x, dest_y, num_clips, inc,
&sdirty.base);
- vmw_kms_helper_resource_finish(srf, out_fence);
+ vmw_kms_helper_resource_finish(&ctx, out_fence);
return ret;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
index 94ad8d2..8b91450 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
@@ -977,12 +977,13 @@
struct vmw_framebuffer_surface *vfbs =
container_of(framebuffer, typeof(*vfbs), base);
struct vmw_stdu_dirty sdirty;
+ struct vmw_validation_ctx ctx;
int ret;
if (!srf)
srf = &vfbs->surface->res;
- ret = vmw_kms_helper_resource_prepare(srf, true);
+ ret = vmw_kms_helper_resource_prepare(srf, true, &ctx);
if (ret)
return ret;
@@ -1005,7 +1006,7 @@
dest_x, dest_y, num_clips, inc,
&sdirty.base);
out_finish:
- vmw_kms_helper_resource_finish(srf, out_fence);
+ vmw_kms_helper_resource_finish(&ctx, out_fence);
return ret;
}
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index cb916ae..757f9d73 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -279,6 +279,59 @@
return 0;
}
+void adreno_efuse_speed_bin_array(struct adreno_device *adreno_dev)
+{
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+ int ret, count, i = 0;
+ unsigned int val, vector_size = 3;
+ unsigned int *bin_vector;
+
+ /*
+ * Here count is no of 32 bit elements in the
+ * speed-bin-vector array. If there are two fuses
+ * i.e If no of fuses are 2 then no of elements will be
+ * 2 * 3 = 6(elements of 32 bit each).
+ */
+ count = of_property_count_u32_elems(device->pdev->dev.of_node,
+ "qcom,gpu-speed-bin-vectors");
+ if (count <= 0)
+ return;
+
+ bin_vector = kmalloc(sizeof(count * sizeof(unsigned int)),
+ GFP_KERNEL);
+ if (bin_vector == NULL) {
+ KGSL_DRV_ERR(device,
+ "Unable to allocate memory for speed-bin vector\n");
+ return;
+ }
+
+ if (of_property_read_u32_array(device->pdev->
+ dev.of_node, "qcom,gpu-speed-bin-vectors",
+ bin_vector, count)) {
+ KGSL_DRV_ERR(device,
+ "Speed-bin-vectors is invalid\n");
+ kfree(bin_vector);
+ return;
+ }
+
+ /*
+ * Final value of adreno_dev->speed_bin is the value formed by
+ * OR'ing the values read from all the fuses.
+ */
+ while (i < count) {
+ ret = adreno_efuse_read_u32(adreno_dev, bin_vector[i], &val);
+
+ if (ret < 0)
+ break;
+
+ adreno_dev->speed_bin |= (val & bin_vector[i+1])
+ >> bin_vector[i+2];
+ i += vector_size;
+ }
+
+ kfree(bin_vector);
+}
+
static int _get_counter(struct adreno_device *adreno_dev,
int group, int countable, unsigned int *lo,
unsigned int *hi)
@@ -613,6 +666,7 @@
struct adreno_irq *irq_params = gpudev->irq;
irqreturn_t ret = IRQ_NONE;
unsigned int status = 0, fence = 0, fence_retries = 0, tmp, int_bit;
+ unsigned int shadow_status = 0;
int i;
atomic_inc(&adreno_dev->pending_irq_refcnt);
@@ -635,18 +689,29 @@
* and change the fence back to ALLOW. Poll so that this can happen.
*/
if (kgsl_gmu_isenabled(device)) {
- do {
+ adreno_readreg(adreno_dev,
+ ADRENO_REG_GMU_AO_AHB_FENCE_CTRL,
+ &fence);
+
+ while (fence != 0) {
+ /* Wait for small time before trying again */
+ udelay(1);
adreno_readreg(adreno_dev,
ADRENO_REG_GMU_AO_AHB_FENCE_CTRL,
&fence);
- if (fence_retries == FENCE_RETRY_MAX) {
+ if (fence_retries == FENCE_RETRY_MAX && fence != 0) {
+ adreno_readreg(adreno_dev,
+ ADRENO_REG_GMU_RBBM_INT_UNMASKED_STATUS,
+ &shadow_status);
+
KGSL_DRV_CRIT_RATELIMIT(device,
- "AHB fence stuck in ISR\n");
+ "AHB fence stuck in ISR: Shadow INT status=%8.8X\n",
+ shadow_status & irq_params->mask);
goto done;
}
fence_retries++;
- } while (fence != 0);
+ }
}
adreno_readreg(adreno_dev, ADRENO_REG_RBBM_INT_0_STATUS, &status);
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 686ed34..8b88aad 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -722,6 +722,7 @@
ADRENO_REG_GMU_HOST2GMU_INTR_RAW_INFO,
ADRENO_REG_GMU_NMI_CONTROL_STATUS,
ADRENO_REG_GMU_CM3_CFG,
+ ADRENO_REG_GMU_RBBM_INT_UNMASKED_STATUS,
ADRENO_REG_GPMU_POWER_COUNTER_ENABLE,
ADRENO_REG_REGISTER_MAX,
};
@@ -1130,6 +1131,7 @@
int adreno_efuse_read_u32(struct adreno_device *adreno_dev, unsigned int offset,
unsigned int *val);
void adreno_efuse_unmap(struct adreno_device *adreno_dev);
+void adreno_efuse_speed_bin_array(struct adreno_device *adreno_dev);
bool adreno_is_cx_dbgc_register(struct kgsl_device *device,
unsigned int offset);
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index 6ef74de..27cb8f4 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -122,14 +122,34 @@
adreno_dev->speed_bin = (val & speed_bin[1]) >> speed_bin[2];
}
+static void a5xx_efuse_speed_bin(struct adreno_device *adreno_dev)
+{
+ unsigned int val;
+ unsigned int speed_bin[3];
+ struct kgsl_device *device = &adreno_dev->dev;
+
+ if (of_get_property(device->pdev->dev.of_node,
+ "qcom,gpu-speed-bin-vectors", NULL)) {
+ adreno_efuse_speed_bin_array(adreno_dev);
+ return;
+ }
+
+ if (!of_property_read_u32_array(device->pdev->dev.of_node,
+ "qcom,gpu-speed-bin", speed_bin, 3)) {
+ adreno_efuse_read_u32(adreno_dev, speed_bin[0], &val);
+ adreno_dev->speed_bin = (val & speed_bin[1]) >> speed_bin[2];
+ return;
+ }
+}
+
static const struct {
int (*check)(struct adreno_device *adreno_dev);
void (*func)(struct adreno_device *adreno_dev);
} a5xx_efuse_funcs[] = {
{ adreno_is_a530, a530_efuse_leakage },
{ adreno_is_a530, a530_efuse_speed_bin },
- { adreno_is_a504, a530_efuse_speed_bin },
- { adreno_is_a505, a530_efuse_speed_bin },
+ { adreno_is_a504, a5xx_efuse_speed_bin },
+ { adreno_is_a505, a5xx_efuse_speed_bin },
{ adreno_is_a512, a530_efuse_speed_bin },
{ adreno_is_a508, a530_efuse_speed_bin },
};
@@ -1356,13 +1376,13 @@
/* todo double check the reg writes */
while ((cur - opcode) < length) {
- if (cur[0] == 1 && ((cur + 4) - opcode) <= length) {
+ if (cur[0] == 1 && (length - (cur - opcode) >= 4)) {
/* Write a 32 bit value to a 64 bit reg */
reg = cur[2];
reg = (reg << 32) | cur[1];
kgsl_regwrite(KGSL_DEVICE(adreno_dev), reg, cur[3]);
cur += 4;
- } else if (cur[0] == 2 && ((cur + 5) - opcode) <= length) {
+ } else if (cur[0] == 2 && (length - (cur - opcode) >= 5)) {
/* Write a 64 bit value to a 64 bit reg */
reg = cur[2];
reg = (reg << 32) | cur[1];
@@ -1370,7 +1390,7 @@
val = (val << 32) | cur[3];
kgsl_regwrite(KGSL_DEVICE(adreno_dev), reg, val);
cur += 5;
- } else if (cur[0] == 3 && ((cur + 2) - opcode) <= length) {
+ } else if (cur[0] == 3 && (length - (cur - opcode) >= 2)) {
/* Delay for X usec */
udelay(cur[1]);
cur += 2;
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index 7096d7c..14e1b1d 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -1708,6 +1708,10 @@
struct device *dev = &gmu->pdev->dev;
int val;
+ /* Only trigger wakeup sequence if sleep sequence was done earlier */
+ if (!test_bit(GMU_RSCC_SLEEP_SEQ_DONE, &gmu->flags))
+ return 0;
+
kgsl_gmu_regread(device, A6XX_GPU_CC_GX_DOMAIN_MISC, &val);
if (!(val & 0x1))
dev_err_ratelimited(&gmu->pdev->dev,
@@ -1737,6 +1741,9 @@
kgsl_gmu_regwrite(device, A6XX_GMU_RSCC_CONTROL_REQ, 0);
+ /* Clear sleep sequence flag as wakeup sequence is successful */
+ clear_bit(GMU_RSCC_SLEEP_SEQ_DONE, &gmu->flags);
+
/* Enable the power counter because it was disabled before slumber */
kgsl_gmu_regwrite(device, A6XX_GMU_CX_GMU_POWER_COUNTER_ENABLE, 1);
@@ -1752,6 +1759,9 @@
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
int ret;
+ if (test_bit(GMU_RSCC_SLEEP_SEQ_DONE, &gmu->flags))
+ return 0;
+
/* RSC sleep sequence is different on v1 */
if (adreno_is_a630v1(adreno_dev))
kgsl_gmu_regwrite(device, A6XX_RSCC_TIMESTAMP_UNIT1_EN_DRV0, 1);
@@ -1793,6 +1803,7 @@
test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag))
kgsl_gmu_regwrite(device, A6XX_GMU_AO_SPARE_CNTL, 0);
+ set_bit(GMU_RSCC_SLEEP_SEQ_DONE, &gmu->flags);
return 0;
}
@@ -1811,15 +1822,13 @@
unsigned int chipid = 0;
switch (boot_state) {
- case GMU_RESET:
- /* fall through */
case GMU_COLD_BOOT:
/* Turn on TCM retention */
kgsl_gmu_regwrite(device, A6XX_GMU_GENERAL_7, 1);
if (!test_and_set_bit(GMU_BOOT_INIT_DONE, &gmu->flags))
_load_gmu_rpmh_ucode(device);
- else if (boot_state != GMU_RESET) {
+ else {
ret = a6xx_rpmh_power_on_gpu(device);
if (ret)
return ret;
@@ -3776,6 +3785,8 @@
A6XX_GMU_NMI_CONTROL_STATUS),
ADRENO_REG_DEFINE(ADRENO_REG_GMU_CM3_CFG,
A6XX_GMU_CM3_CFG),
+ ADRENO_REG_DEFINE(ADRENO_REG_GMU_RBBM_INT_UNMASKED_STATUS,
+ A6XX_GMU_RBBM_INT_UNMASKED_STATUS),
ADRENO_REG_DEFINE(ADRENO_REG_RBBM_SECVID_TRUST_CONTROL,
A6XX_RBBM_SECVID_TRUST_CNTL),
ADRENO_REG_DEFINE(ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_BASE,
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index bb9f9ff..a2d6071 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -458,6 +458,10 @@
type = kgsl_memdesc_usermem_type(&entry->memdesc);
entry->priv->stats[type].cur -= entry->memdesc.size;
+
+ if (type != KGSL_MEM_ENTRY_ION)
+ entry->priv->gpumem_mapped -= entry->memdesc.mapsize;
+
spin_unlock(&entry->priv->mem_lock);
kgsl_mmu_put_gpuaddr(&entry->memdesc);
@@ -4143,13 +4147,18 @@
kgsl_gpumem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct kgsl_mem_entry *entry = vma->vm_private_data;
+ int ret;
if (!entry)
return VM_FAULT_SIGBUS;
if (!entry->memdesc.ops || !entry->memdesc.ops->vmfault)
return VM_FAULT_SIGBUS;
- return entry->memdesc.ops->vmfault(&entry->memdesc, vma, vmf);
+ ret = entry->memdesc.ops->vmfault(&entry->memdesc, vma, vmf);
+ if ((ret == 0) || (ret == VM_FAULT_NOPAGE))
+ entry->priv->gpumem_mapped += PAGE_SIZE;
+
+ return ret;
}
static void
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index c737801..c8c6456 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -446,6 +446,7 @@
* @kobj: Pointer to a kobj for the sysfs directory for this process
* @debug_root: Pointer to the debugfs root for this process
* @stats: Memory allocation statistics for this process
+ * @gpumem_mapped: KGSL memory mapped in the process address space
* @syncsource_idr: sync sources created by this process
* @syncsource_lock: Spinlock to protect the syncsource idr
* @fd_count: Counter for the number of FDs for this process
@@ -467,6 +468,7 @@
uint64_t cur;
uint64_t max;
} stats[KGSL_MEM_ENTRY_MAX];
+ uint64_t gpumem_mapped;
struct idr syncsource_idr;
spinlock_t syncsource_lock;
int fd_count;
diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index dbf517a..dbc3d53 100644
--- a/drivers/gpu/msm/kgsl_gmu.c
+++ b/drivers/gpu/msm/kgsl_gmu.c
@@ -131,6 +131,10 @@
fault_type = "translation";
else if (flags & IOMMU_FAULT_PERMISSION)
fault_type = "permission";
+ else if (flags & IOMMU_FAULT_EXTERNAL)
+ fault_type = "external";
+ else if (flags & IOMMU_FAULT_TRANSACTION_STALLED)
+ fault_type = "transaction stalled";
dev_err(dev, "GMU fault addr = %lX, context=%s (%s %s fault)\n",
addr, name,
@@ -1318,7 +1322,7 @@
return -ETIMEDOUT;
}
-static int gmu_suspend(struct kgsl_device *device)
+int gmu_suspend(struct kgsl_device *device)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
@@ -1404,6 +1408,7 @@
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
struct gmu_device *gmu = &device->gmu;
+ unsigned int boot_state = GMU_WARM_BOOT;
switch (device->state) {
case KGSL_STATE_INIT:
@@ -1440,12 +1445,21 @@
gmu_enable_clks(gmu);
gmu_irq_enable(device);
+ /*
+ * If unrecovered is set that means last
+ * wakeup from SLUMBER state failed. Use GMU
+ * and HFI boot state as COLD as this is a
+ * boot after RESET.
+ */
+ if (gmu->unrecovered)
+ boot_state = GMU_COLD_BOOT;
+
ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START,
- GMU_WARM_BOOT, 0);
+ boot_state, 0);
if (ret)
goto error_gmu;
- ret = hfi_start(gmu, GMU_WARM_BOOT);
+ ret = hfi_start(gmu, boot_state);
if (ret)
goto error_gmu;
@@ -1461,7 +1475,7 @@
gmu_irq_enable(device);
ret = gpudev->rpmh_gpu_pwrctrl(
- adreno_dev, GMU_FW_START, GMU_RESET, 0);
+ adreno_dev, GMU_FW_START, GMU_COLD_BOOT, 0);
if (ret)
goto error_gmu;
@@ -1478,7 +1492,7 @@
hfi_stop(gmu);
ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START,
- GMU_RESET, 0);
+ GMU_COLD_BOOT, 0);
if (ret)
goto error_gmu;
@@ -1491,6 +1505,8 @@
break;
}
+ /* Clear unrecovered as GMU start is successful */
+ gmu->unrecovered = false;
return ret;
error_gmu:
diff --git a/drivers/gpu/msm/kgsl_gmu.h b/drivers/gpu/msm/kgsl_gmu.h
index 19fa972..130d400 100644
--- a/drivers/gpu/msm/kgsl_gmu.h
+++ b/drivers/gpu/msm/kgsl_gmu.h
@@ -102,6 +102,7 @@
GMU_HFI_ON = 2,
GMU_FAULT = 3,
GMU_DCVS_REPLAY = 4,
+ GMU_RSCC_SLEEP_SEQ_DONE = 5,
};
/**
@@ -139,13 +140,11 @@
/*
* These are the different ways the GMU can boot. GMU_WARM_BOOT is waking up
- * from slumber. GMU_COLD_BOOT is booting for the first time. GMU_RESET
- * is a soft reset of the GMU.
+ * from slumber. GMU_COLD_BOOT is booting for the first time.
*/
enum gmu_boot {
GMU_WARM_BOOT = 0,
GMU_COLD_BOOT = 1,
- GMU_RESET = 2
};
enum gmu_load_mode {
@@ -213,6 +212,7 @@
* @ccl: CNOC BW scaling client
* @idle_level: Minimal GPU idle power level
* @fault_count: GMU fault count
+ * @unrecovered: Indicates whether GMU recovery failed or not
*/
struct gmu_device {
unsigned int ver;
@@ -247,6 +247,7 @@
unsigned int ccl;
unsigned int idle_level;
unsigned int fault_count;
+ bool unrecovered;
};
void gmu_snapshot(struct kgsl_device *device);
@@ -256,6 +257,7 @@
int allocate_gmu_image(struct gmu_device *gmu, unsigned int size);
int gmu_start(struct kgsl_device *device);
void gmu_stop(struct kgsl_device *device);
+int gmu_suspend(struct kgsl_device *device);
int gmu_dcvs_set(struct gmu_device *gmu, unsigned int gpu_pwrlevel,
unsigned int bus_level);
#endif /* __KGSL_GMU_H */
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 0ce72f6..325d44a 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -789,6 +789,10 @@
fault_type = "translation";
else if (flags & IOMMU_FAULT_PERMISSION)
fault_type = "permission";
+ else if (flags & IOMMU_FAULT_EXTERNAL)
+ fault_type = "external";
+ else if (flags & IOMMU_FAULT_TRANSACTION_STALLED)
+ fault_type = "transaction stalled";
if (kgsl_iommu_suppress_pagefault(addr, write, context)) {
iommu->pagefault_suppression_count++;
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index ee9e5df..2a149ac 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -2798,6 +2798,25 @@
WARN_ONCE(1, "Failed to recover GMU\n");
if (device->snapshot)
device->snapshot->recovered = false;
+ /*
+ * On recovery failure, we are clearing
+ * GMU_FAULT bit and also not keeping
+ * the state as RESET to make sure any
+ * attempt to wake GMU/GPU after this
+ * is treated as a fresh start. But on
+ * recovery failure, GMU HS, clocks and
+ * IRQs are still ON/enabled because of
+ * which next GMU/GPU wakeup results in
+ * multiple warnings from GMU start as HS,
+ * clocks and IRQ were ON while doing a
+ * fresh start i.e. wake from SLUMBER.
+ *
+ * Suspend the GMU on recovery failure
+ * to make sure next attempt to wake up
+ * GMU/GPU is indeed a fresh start.
+ */
+ gmu_suspend(device);
+ gmu->unrecovered = true;
kgsl_pwrctrl_set_state(device, state);
} else {
if (device->snapshot)
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index 3702893..2e84bad 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -97,6 +97,75 @@
static void kgsl_cma_unlock_secure(struct kgsl_memdesc *memdesc);
+static ssize_t
+imported_mem_show(struct kgsl_process_private *priv,
+ int type, char *buf)
+{
+ struct kgsl_mem_entry *entry;
+ uint64_t imported_mem = 0;
+ int id = 0;
+
+ spin_lock(&priv->mem_lock);
+ for (entry = idr_get_next(&priv->mem_idr, &id); entry;
+ id++, entry = idr_get_next(&priv->mem_idr, &id)) {
+
+ int egl_surface_count = 0, egl_image_count = 0;
+ struct kgsl_memdesc *m;
+
+ if (kgsl_mem_entry_get(entry) == 0)
+ continue;
+ spin_unlock(&priv->mem_lock);
+
+ m = &entry->memdesc;
+ if (kgsl_memdesc_usermem_type(m) == KGSL_MEM_ENTRY_ION) {
+ kgsl_get_egl_counts(entry, &egl_surface_count,
+ &egl_image_count);
+
+ if (kgsl_memdesc_get_memtype(m) ==
+ KGSL_MEMTYPE_EGL_SURFACE)
+ imported_mem += m->size;
+ else if (egl_surface_count == 0) {
+ uint64_t size = m->size;
+
+ do_div(size, (egl_image_count ?
+ egl_image_count : 1));
+ imported_mem += size;
+ }
+ }
+
+ kgsl_mem_entry_put(entry);
+ spin_lock(&priv->mem_lock);
+ }
+ spin_unlock(&priv->mem_lock);
+
+ return scnprintf(buf, PAGE_SIZE, "%llu\n", imported_mem);
+}
+
+static ssize_t
+gpumem_mapped_show(struct kgsl_process_private *priv,
+ int type, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%llu\n",
+ priv->gpumem_mapped);
+}
+
+static ssize_t
+gpumem_unmapped_show(struct kgsl_process_private *priv, int type, char *buf)
+{
+ if (priv->gpumem_mapped > priv->stats[type].cur)
+ return -EIO;
+
+ return scnprintf(buf, PAGE_SIZE, "%llu\n",
+ priv->stats[type].cur - priv->gpumem_mapped);
+}
+
+static struct kgsl_mem_entry_attribute debug_memstats[] = {
+ __MEM_ENTRY_ATTR(0, imported_mem, imported_mem_show),
+ __MEM_ENTRY_ATTR(0, gpumem_mapped, gpumem_mapped_show),
+ __MEM_ENTRY_ATTR(KGSL_MEM_ENTRY_KERNEL, gpumem_unmapped,
+ gpumem_unmapped_show),
+};
+
/**
* Show the current amount of memory allocated for the given memtype
*/
@@ -219,7 +288,13 @@
&mem_stats[i].max_attr.attr))
WARN(1, "Couldn't create sysfs file '%s'\n",
mem_stats[i].max_attr.attr.name);
+ }
+ for (i = 0; i < ARRAY_SIZE(debug_memstats); i++) {
+ if (sysfs_create_file(&private->kobj,
+ &debug_memstats[i].attr))
+ WARN(1, "Couldn't create sysfs file '%s'\n",
+ debug_memstats[i].attr.name);
}
}
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index b5888db..ac7d2c6 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2444,6 +2444,9 @@
{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTIME) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYPH) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERANALYSERCASSY) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CONVERTERCONTROLLERCASSY) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETESTCASSY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) },
diff --git a/drivers/hid/hid-elo.c b/drivers/hid/hid-elo.c
index 0cd4f72..5eea6fe 100644
--- a/drivers/hid/hid-elo.c
+++ b/drivers/hid/hid-elo.c
@@ -42,6 +42,12 @@
{
struct input_dev *input = hidinput->input;
+ /*
+ * ELO devices have one Button usage in GenDesk field, which makes
+ * hid-input map it to BTN_LEFT; that confuses userspace, which then
+ * considers the device to be a mouse/touchpad instead of touchscreen.
+ */
+ clear_bit(BTN_LEFT, input->keybit);
set_bit(BTN_TOUCH, input->keybit);
set_bit(ABS_PRESSURE, input->absbit);
input_set_abs_params(input, ABS_PRESSURE, 0, 256, 0, 0);
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 244b97c..9347b37 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -608,6 +608,9 @@
#define USB_DEVICE_ID_LD_MICROCASSYTIME 0x1033
#define USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE 0x1035
#define USB_DEVICE_ID_LD_MICROCASSYPH 0x1038
+#define USB_DEVICE_ID_LD_POWERANALYSERCASSY 0x1040
+#define USB_DEVICE_ID_LD_CONVERTERCONTROLLERCASSY 0x1042
+#define USB_DEVICE_ID_LD_MACHINETESTCASSY 0x1043
#define USB_DEVICE_ID_LD_JWM 0x1080
#define USB_DEVICE_ID_LD_DMMP 0x1081
#define USB_DEVICE_ID_LD_UMIP 0x1090
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index fb9ace1..40233315 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1149,18 +1149,26 @@
/*
* Ignore out-of-range values as per HID specification,
- * section 5.10 and 6.2.25.
+ * section 5.10 and 6.2.25, when NULL state bit is present.
+ * When it's not, clamp the value to match Microsoft's input
+ * driver as mentioned in "Required HID usages for digitizers":
+ * https://msdn.microsoft.com/en-us/library/windows/hardware/dn672278(v=vs.85).asp
*
* The logical_minimum < logical_maximum check is done so that we
* don't unintentionally discard values sent by devices which
* don't specify logical min and max.
*/
if ((field->flags & HID_MAIN_ITEM_VARIABLE) &&
- (field->logical_minimum < field->logical_maximum) &&
- (value < field->logical_minimum ||
- value > field->logical_maximum)) {
- dbg_hid("Ignoring out-of-range value %x\n", value);
- return;
+ (field->logical_minimum < field->logical_maximum)) {
+ if (field->flags & HID_MAIN_ITEM_NULL_STATE &&
+ (value < field->logical_minimum ||
+ value > field->logical_maximum)) {
+ dbg_hid("Ignoring out-of-range value %x\n", value);
+ return;
+ }
+ value = clamp(value,
+ field->logical_minimum,
+ field->logical_maximum);
}
/*
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 20b40ad..42ed887 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -34,7 +34,8 @@
MODULE_PARM_DESC(emulate_scroll_wheel, "Emulate a scroll wheel");
static unsigned int scroll_speed = 32;
-static int param_set_scroll_speed(const char *val, struct kernel_param *kp) {
+static int param_set_scroll_speed(const char *val,
+ const struct kernel_param *kp) {
unsigned long speed;
if (!val || kstrtoul(val, 0, &speed) || speed > 63)
return -EINVAL;
diff --git a/drivers/hsi/clients/ssi_protocol.c b/drivers/hsi/clients/ssi_protocol.c
index 6031cd1..802afc9 100644
--- a/drivers/hsi/clients/ssi_protocol.c
+++ b/drivers/hsi/clients/ssi_protocol.c
@@ -989,7 +989,7 @@
goto drop;
/* Pad to 32-bits - FIXME: Revisit*/
if ((skb->len & 3) && skb_pad(skb, 4 - (skb->len & 3)))
- goto drop;
+ goto inc_dropped;
/*
* Modem sends Phonet messages over SSI with its own endianess...
@@ -1041,8 +1041,9 @@
drop2:
hsi_free_msg(msg);
drop:
- dev->stats.tx_dropped++;
dev_kfree_skb(skb);
+inc_dropped:
+ dev->stats.tx_dropped++;
return 0;
}
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index 3baa4f4a..d659a02 100644
--- a/drivers/hwmon/pmbus/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -101,8 +101,8 @@
[0] = { 27169, 0, -1 }, /* voltage */
[1] = { 806, 20475, -1 }, /* current, irange25 */
[2] = { 404, 20475, -1 }, /* current, irange50 */
- [3] = { 0, -1, 8549 }, /* power, irange25 */
- [4] = { 0, -1, 4279 }, /* power, irange50 */
+ [3] = { 8549, 0, -1 }, /* power, irange25 */
+ [4] = { 4279, 0, -1 }, /* power, irange50 */
};
static const struct coefficients adm1275_coefficients[] = {
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index 62ad4f4..f396c76 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -751,6 +751,154 @@
{107, 980}
};
+/* Voltage to temperature */
+static const struct qpnp_vadc_map_pt adcmap_batt_therm_pu30[] = {
+ {1842, -400},
+ {1838, -380},
+ {1833, -360},
+ {1828, -340},
+ {1822, -320},
+ {1816, -300},
+ {1809, -280},
+ {1801, -260},
+ {1793, -240},
+ {1784, -220},
+ {1774, -200},
+ {1763, -180},
+ {1752, -160},
+ {1739, -140},
+ {1726, -120},
+ {1712, -100},
+ {1697, -80},
+ {1680, -60},
+ {1663, -40},
+ {1645, -20},
+ {1625, 0},
+ {1605, 20},
+ {1583, 40},
+ {1561, 60},
+ {1537, 80},
+ {1513, 100},
+ {1487, 120},
+ {1461, 140},
+ {1433, 160},
+ {1405, 180},
+ {1376, 200},
+ {1347, 220},
+ {1316, 240},
+ {1286, 260},
+ {1254, 280},
+ {1223, 300},
+ {1191, 320},
+ {1159, 340},
+ {1126, 360},
+ {1094, 380},
+ {1062, 400},
+ {1029, 420},
+ {997, 440},
+ {966, 460},
+ {934, 480},
+ {903, 500},
+ {873, 520},
+ {843, 540},
+ {813, 560},
+ {784, 580},
+ {756, 600},
+ {728, 620},
+ {702, 640},
+ {675, 660},
+ {650, 680},
+ {625, 700},
+ {601, 720},
+ {578, 740},
+ {556, 760},
+ {534, 780},
+ {513, 800},
+ {493, 820},
+ {474, 840},
+ {455, 860},
+ {437, 880},
+ {420, 900},
+ {403, 920},
+ {387, 940},
+ {372, 960},
+ {357, 980}
+};
+
+/* Voltage to temp0erature */
+static const struct qpnp_vadc_map_pt adcmap_batt_therm_pu400[] = {
+ {1516, -400},
+ {1478, -380},
+ {1438, -360},
+ {1396, -340},
+ {1353, -320},
+ {1307, -300},
+ {1261, -280},
+ {1213, -260},
+ {1164, -240},
+ {1115, -220},
+ {1066, -200},
+ {1017, -180},
+ {968, -160},
+ {920, -140},
+ {872, -120},
+ {826, -100},
+ {781, -80},
+ {737, -60},
+ {694, -40},
+ {654, -20},
+ {615, 0},
+ {578, 20},
+ {542, 40},
+ {509, 60},
+ {477, 80},
+ {447, 100},
+ {419, 120},
+ {392, 140},
+ {367, 160},
+ {343, 180},
+ {321, 200},
+ {301, 220},
+ {282, 240},
+ {264, 260},
+ {247, 280},
+ {231, 300},
+ {216, 320},
+ {203, 340},
+ {190, 360},
+ {178, 380},
+ {167, 400},
+ {157, 420},
+ {147, 440},
+ {138, 460},
+ {130, 480},
+ {122, 500},
+ {115, 520},
+ {108, 540},
+ {102, 560},
+ {96, 580},
+ {90, 600},
+ {85, 620},
+ {80, 640},
+ {76, 660},
+ {72, 680},
+ {68, 700},
+ {64, 720},
+ {61, 740},
+ {57, 760},
+ {54, 780},
+ {52, 800},
+ {49, 820},
+ {46, 840},
+ {44, 860},
+ {42, 880},
+ {40, 900},
+ {38, 920},
+ {36, 940},
+ {34, 960},
+ {32, 980}
+};
+
/*
* Voltage to temperature table for 100k pull up for NTCG104EF104 with
* 1.875V reference.
@@ -1120,6 +1268,62 @@
}
EXPORT_SYMBOL(qpnp_adc_batt_therm_qrd);
+int32_t qpnp_adc_batt_therm_pu30(struct qpnp_vadc_chip *chip,
+ int32_t adc_code,
+ const struct qpnp_adc_properties *adc_properties,
+ const struct qpnp_vadc_chan_properties *chan_properties,
+ struct qpnp_vadc_result *adc_chan_result)
+{
+ int64_t batt_thm_voltage = 0;
+
+ if (!chan_properties || !chan_properties->offset_gain_numerator ||
+ !chan_properties->offset_gain_denominator || !adc_properties
+ || !adc_chan_result)
+ return -EINVAL;
+
+ /* (code * vref_vadc (1.875V) * 1000) / (scale_code * 1000) */
+ if (adc_code > QPNP_VADC_HC_MAX_CODE)
+ adc_code = 0;
+ batt_thm_voltage = (int64_t) adc_code;
+ batt_thm_voltage *= (adc_properties->adc_vdd_reference
+ * 1000);
+ batt_thm_voltage = div64_s64(batt_thm_voltage,
+ adc_properties->full_scale_code * 1000);
+ qpnp_adc_map_voltage_temp(adcmap_batt_therm_pu30,
+ ARRAY_SIZE(adcmap_batt_therm_pu30),
+ batt_thm_voltage, &adc_chan_result->physical);
+ return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_batt_therm_pu30);
+
+int32_t qpnp_adc_batt_therm_pu400(struct qpnp_vadc_chip *chip,
+ int32_t adc_code,
+ const struct qpnp_adc_properties *adc_properties,
+ const struct qpnp_vadc_chan_properties *chan_properties,
+ struct qpnp_vadc_result *adc_chan_result)
+{
+ int64_t batt_thm_voltage = 0;
+
+ if (!chan_properties || !chan_properties->offset_gain_numerator ||
+ !chan_properties->offset_gain_denominator || !adc_properties
+ || !adc_chan_result)
+ return -EINVAL;
+
+ /* (code * vref_vadc (1.875V) * 1000) / (scale_code * 1000) */
+ if (adc_code > QPNP_VADC_HC_MAX_CODE)
+ adc_code = 0;
+ batt_thm_voltage = (int64_t) adc_code;
+ batt_thm_voltage *= (adc_properties->adc_vdd_reference
+ * 1000);
+ batt_thm_voltage = div64_s64(batt_thm_voltage,
+ adc_properties->full_scale_code * 1000);
+ qpnp_adc_map_voltage_temp(adcmap_batt_therm_pu400,
+ ARRAY_SIZE(adcmap_batt_therm_pu400),
+ batt_thm_voltage, &adc_chan_result->physical);
+ return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_batt_therm_pu400);
+
int32_t qpnp_adc_scale_batt_therm(struct qpnp_vadc_chip *chip,
int32_t adc_code,
const struct qpnp_adc_properties *adc_properties,
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
index 1f38574..ed5222e 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -228,6 +228,8 @@
[SCALE_USBIN_I] = {qpnp_adc_scale_usbin_curr},
[SCALE_BATT_THERM_TEMP_QRD] = {qpnp_adc_batt_therm_qrd},
[SCALE_SMB1390_DIE_TEMP] = {qpnp_adc_scale_die_temp_1390},
+ [SCALE_BATT_THERM_TEMP_PU30] = {qpnp_adc_batt_therm_pu30},
+ [SCALE_BATT_THERM_TEMP_PU400] = {qpnp_adc_batt_therm_pu400},
};
static struct qpnp_vadc_rscale_fn adc_vadc_rscale_fn[] = {
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index 95c887c..2d4da74 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -63,7 +63,6 @@
config CORESIGHT_SOURCE_ETM4X
bool "CoreSight Embedded Trace Macrocell 4.x driver"
- depends on ARM64
select CORESIGHT_LINKS_AND_SINKS
help
This driver provides support for the ETM4.x tracer module, tracing the
diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
index d7325c65..7e041d9 100644
--- a/drivers/hwtracing/coresight/coresight-etb10.c
+++ b/drivers/hwtracing/coresight/coresight-etb10.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2012,2018 The Linux Foundation. All rights reserved.
*
* Description: CoreSight Embedded Trace Buffer driver
*
@@ -669,7 +669,6 @@
spin_lock_init(&drvdata->spinlock);
drvdata->buffer_depth = etb_get_buffer_depth(drvdata);
- pm_runtime_put(&adev->dev);
if (drvdata->buffer_depth & 0x80000000)
return -EINVAL;
@@ -698,6 +697,7 @@
ret = misc_register(&drvdata->miscdev);
if (ret)
goto err_misc_register;
+ pm_runtime_put(&adev->dev);
return 0;
diff --git a/drivers/hwtracing/coresight/coresight-event.c b/drivers/hwtracing/coresight/coresight-event.c
index d5f304f..86e2b29 100644
--- a/drivers/hwtracing/coresight/coresight-event.c
+++ b/drivers/hwtracing/coresight/coresight-event.c
@@ -20,12 +20,13 @@
#include <trace/events/exception.h>
static int event_abort_enable;
-static int event_abort_set(const char *val, struct kernel_param *kp);
+static int event_abort_set(const char *val, const struct kernel_param *kp);
module_param_call(event_abort_enable, event_abort_set, param_get_int,
&event_abort_enable, 0644);
static int event_abort_early_panic = 1;
-static int event_abort_on_panic_set(const char *val, struct kernel_param *kp);
+static int event_abort_on_panic_set(const char *val,
+ const struct kernel_param *kp);
module_param_call(event_abort_early_panic, event_abort_on_panic_set,
param_get_int, &event_abort_early_panic, 0644);
@@ -96,7 +97,7 @@
unregister_trace_unhandled_abort(event_abort_unhandled_abort, NULL);
}
-static int event_abort_set(const char *val, struct kernel_param *kp)
+static int event_abort_set(const char *val, const struct kernel_param *kp)
{
int ret;
@@ -114,7 +115,8 @@
return ret;
}
-static int event_abort_on_panic_set(const char *val, struct kernel_param *kp)
+static int event_abort_on_panic_set(const char *val,
+ const struct kernel_param *kp)
{
int ret;
diff --git a/drivers/hwtracing/coresight/coresight-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-replicator-qcom.c
index 98547a9..0990164 100644
--- a/drivers/hwtracing/coresight/coresight-replicator-qcom.c
+++ b/drivers/hwtracing/coresight/coresight-replicator-qcom.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2015, 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2015,2017-2018, 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
@@ -132,7 +132,6 @@
drvdata->base = base;
dev_set_drvdata(dev, drvdata);
- pm_runtime_put(&adev->dev);
desc.type = CORESIGHT_DEV_TYPE_LINK;
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
@@ -143,6 +142,7 @@
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
+ pm_runtime_put(&adev->dev);
dev_info(dev, "%s initialized\n", (char *)id->data);
return 0;
}
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 3948a50..87d9162 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -689,8 +689,6 @@
drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4;
}
- pm_runtime_put(&adev->dev);
-
ctidata = of_get_coresight_cti_data(dev, adev->dev.of_node);
if (IS_ERR(ctidata)) {
dev_err(dev, "invalid cti data\n");
@@ -754,6 +752,10 @@
ret = misc_register(&drvdata->miscdev);
if (ret)
coresight_unregister(drvdata->csdev);
+
+ if (!ret)
+ pm_runtime_put(&adev->dev);
+
out:
return ret;
}
diff --git a/drivers/hwtracing/coresight/coresight-tpda.c b/drivers/hwtracing/coresight/coresight-tpda.c
index 5d2d087..261b8c8 100644
--- a/drivers/hwtracing/coresight/coresight-tpda.c
+++ b/drivers/hwtracing/coresight/coresight-tpda.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, 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
@@ -678,8 +678,6 @@
if (!coresight_authstatus_enabled(drvdata->base))
goto err;
- pm_runtime_put(&adev->dev);
-
tpda_init_default_data(drvdata);
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
@@ -695,6 +693,8 @@
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
+ pm_runtime_put(&adev->dev);
+
dev_dbg(drvdata->dev, "TPDA initialized\n");
return 0;
err:
diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
index 7cdb459..6463a47 100644
--- a/drivers/hwtracing/coresight/coresight-tpdm.c
+++ b/drivers/hwtracing/coresight/coresight-tpdm.c
@@ -4029,8 +4029,6 @@
drvdata->bc_counters_avail = BMVAL(devid, 6, 10) + 1;
drvdata->tc_counters_avail = BMVAL(devid, 4, 5) + 1;
- pm_runtime_put(&adev->dev);
-
drvdata->traceid = traceid++;
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
@@ -4051,6 +4049,8 @@
if (boot_enable)
coresight_enable(drvdata->csdev);
+ pm_runtime_put(&adev->dev);
+
return 0;
}
diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c
index be810fe..5e2f89a 100644
--- a/drivers/hwtracing/coresight/of_coresight.c
+++ b/drivers/hwtracing/coresight/of_coresight.c
@@ -152,7 +152,7 @@
continue;
/* The local out port number */
- pdata->outports[i] = endpoint.id;
+ pdata->outports[i] = endpoint.port;
/*
* Get a handle on the remote port and parent
diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c
index 809f4d4..340e037 100644
--- a/drivers/i2c/busses/i2c-designware-core.c
+++ b/drivers/i2c/busses/i2c-designware-core.c
@@ -507,7 +507,7 @@
i2c_dw_disable_int(dev);
/* Enable the adapter */
- __i2c_dw_enable(dev, true);
+ __i2c_dw_enable_and_wait(dev, true);
/* Clear and enable interrupts */
dw_readl(dev, DW_IC_CLR_INTR);
diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c
index dfc98df..7aa7b9c 100644
--- a/drivers/i2c/busses/i2c-scmi.c
+++ b/drivers/i2c/busses/i2c-scmi.c
@@ -18,6 +18,9 @@
#define ACPI_SMBUS_HC_CLASS "smbus"
#define ACPI_SMBUS_HC_DEVICE_NAME "cmi"
+/* SMBUS HID definition as supported by Microsoft Windows */
+#define ACPI_SMBUS_MS_HID "SMB0001"
+
ACPI_MODULE_NAME("smbus_cmi");
struct smbus_methods_t {
@@ -51,6 +54,7 @@
static const struct acpi_device_id acpi_smbus_cmi_ids[] = {
{"SMBUS01", (kernel_ulong_t)&smbus_methods},
{ACPI_SMBUS_IBM_HID, (kernel_ulong_t)&ibm_smbus_methods},
+ {ACPI_SMBUS_MS_HID, (kernel_ulong_t)&smbus_methods},
{"", 0}
};
MODULE_DEVICE_TABLE(acpi, acpi_smbus_cmi_ids);
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
index d127ace..6ee866f 100644
--- a/drivers/ide/ide.c
+++ b/drivers/ide/ide.c
@@ -244,7 +244,7 @@
static unsigned int ide_disks;
static struct chs_geom ide_disks_chs[MAX_HWIFS * MAX_DRIVES];
-static int ide_set_disk_chs(const char *str, struct kernel_param *kp)
+static int ide_set_disk_chs(const char *str, const struct kernel_param *kp)
{
unsigned int a, b, c = 0, h = 0, s = 0, i, j = 1;
@@ -328,7 +328,7 @@
static unsigned int ide_ignore_cable;
-static int ide_set_ignore_cable(const char *s, struct kernel_param *kp)
+static int ide_set_ignore_cable(const char *s, const struct kernel_param *kp)
{
int i, j = 1;
diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c
index 3a557e3..32cd64c 100644
--- a/drivers/iio/accel/st_accel_core.c
+++ b/drivers/iio/accel/st_accel_core.c
@@ -827,6 +827,8 @@
int st_accel_common_probe(struct iio_dev *indio_dev)
{
struct st_sensor_data *adata = iio_priv(indio_dev);
+ struct st_sensors_platform_data *pdata =
+ (struct st_sensors_platform_data *)adata->dev->platform_data;
int irq = adata->get_irq_data_ready(indio_dev);
int err;
@@ -853,11 +855,10 @@
&adata->sensor_settings->fs.fs_avl[0];
adata->odr = adata->sensor_settings->odr.odr_avl[0].hz;
- if (!adata->dev->platform_data)
- adata->dev->platform_data =
- (struct st_sensors_platform_data *)&default_accel_pdata;
+ if (!pdata)
+ pdata = (struct st_sensors_platform_data *)&default_accel_pdata;
- err = st_sensors_init_sensor(indio_dev, adata->dev->platform_data);
+ err = st_sensors_init_sensor(indio_dev, pdata);
if (err < 0)
goto st_accel_power_off;
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
index ab646a9..af85db5 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
@@ -215,7 +215,7 @@
ret = sensor_hub_set_feature(st->hsdev, st->poll.report_id,
st->poll.index, sizeof(value), &value);
if (ret < 0 || value < 0)
- ret = -EINVAL;
+ return -EINVAL;
ret = sensor_hub_get_feature(st->hsdev,
st->poll.report_id,
@@ -265,7 +265,7 @@
st->sensitivity.index, sizeof(value),
&value);
if (ret < 0 || value < 0)
- ret = -EINVAL;
+ return -EINVAL;
ret = sensor_hub_get_feature(st->hsdev,
st->sensitivity.report_id,
diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c
index f53e9a8..93b99bd 100644
--- a/drivers/iio/imu/adis_trigger.c
+++ b/drivers/iio/imu/adis_trigger.c
@@ -47,6 +47,10 @@
if (adis->trig == NULL)
return -ENOMEM;
+ adis->trig->dev.parent = &adis->spi->dev;
+ adis->trig->ops = &adis_trigger_ops;
+ iio_trigger_set_drvdata(adis->trig, adis);
+
ret = request_irq(adis->spi->irq,
&iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_RISING,
@@ -55,9 +59,6 @@
if (ret)
goto error_free_trig;
- adis->trig->dev.parent = &adis->spi->dev;
- adis->trig->ops = &adis_trigger_ops;
- iio_trigger_set_drvdata(adis->trig, adis);
ret = iio_trigger_register(adis->trig);
indio_dev->trig = iio_trigger_get(adis->trig);
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 158aaf4..5d05c38 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -174,7 +174,7 @@
struct iio_dev *indio_dev = filp->private_data;
struct iio_buffer *rb = indio_dev->buffer;
- if (!indio_dev->info)
+ if (!indio_dev->info || rb == NULL)
return 0;
poll_wait(filp, &rb->pollq, wait);
diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c
index 44e46c1..3458418 100644
--- a/drivers/iio/pressure/st_pressure_core.c
+++ b/drivers/iio/pressure/st_pressure_core.c
@@ -638,6 +638,8 @@
int st_press_common_probe(struct iio_dev *indio_dev)
{
struct st_sensor_data *press_data = iio_priv(indio_dev);
+ struct st_sensors_platform_data *pdata =
+ (struct st_sensors_platform_data *)press_data->dev->platform_data;
int irq = press_data->get_irq_data_ready(indio_dev);
int err;
@@ -673,12 +675,10 @@
press_data->odr = press_data->sensor_settings->odr.odr_avl[0].hz;
/* Some devices don't support a data ready pin. */
- if (!press_data->dev->platform_data &&
- press_data->sensor_settings->drdy_irq.addr)
- press_data->dev->platform_data =
- (struct st_sensors_platform_data *)&default_press_pdata;
+ if (!pdata && press_data->sensor_settings->drdy_irq.addr)
+ pdata = (struct st_sensors_platform_data *)&default_press_pdata;
- err = st_sensors_init_sensor(indio_dev, press_data->dev->platform_data);
+ err = st_sensors_init_sensor(indio_dev, pdata);
if (err < 0)
goto st_press_power_off;
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 30f0161..cbe5324 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -4039,6 +4039,9 @@
struct cma_multicast *mc;
int ret;
+ if (!id->device)
+ return -EINVAL;
+
id_priv = container_of(id, struct rdma_id_private, id);
if (!cma_comp(id_priv, RDMA_CM_ADDR_BOUND) &&
!cma_comp(id_priv, RDMA_CM_ADDR_RESOLVED))
@@ -4336,7 +4339,7 @@
RDMA_NL_RDMA_CM_ATTR_SRC_ADDR))
goto out;
if (ibnl_put_attr(skb, nlh,
- rdma_addr_size(cma_src_addr(id_priv)),
+ rdma_addr_size(cma_dst_addr(id_priv)),
cma_dst_addr(id_priv),
RDMA_NL_RDMA_CM_ATTR_DST_ADDR))
goto out;
diff --git a/drivers/infiniband/core/iwpm_util.c b/drivers/infiniband/core/iwpm_util.c
index ade71e7..2fe4c2c 100644
--- a/drivers/infiniband/core/iwpm_util.c
+++ b/drivers/infiniband/core/iwpm_util.c
@@ -664,6 +664,7 @@
}
skb_num++;
spin_lock_irqsave(&iwpm_mapinfo_lock, flags);
+ ret = -EINVAL;
for (i = 0; i < IWPM_MAPINFO_HASH_SIZE; i++) {
hlist_for_each_entry(map_info, &iwpm_hash_bucket[i],
hlist_node) {
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index 9520154..017a09c 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -1139,6 +1139,9 @@
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT;
+ if (cmd.qp_state > IB_QPS_ERR)
+ return -EINVAL;
+
ctx = ucma_get_ctx(file, cmd.id);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
@@ -1275,6 +1278,9 @@
if (IS_ERR(ctx))
return PTR_ERR(ctx);
+ if (unlikely(cmd.optval > KMALLOC_MAX_SIZE))
+ return -EINVAL;
+
optval = memdup_user((void __user *) (unsigned long) cmd.optval,
cmd.optlen);
if (IS_ERR(optval)) {
@@ -1324,7 +1330,7 @@
return -ENOSPC;
addr = (struct sockaddr *) &cmd->addr;
- if (!cmd->addr_size || (cmd->addr_size != rdma_addr_size(addr)))
+ if (cmd->addr_size != rdma_addr_size(addr))
return -EINVAL;
if (cmd->join_flags == RDMA_MC_JOIN_FLAG_FULLMEMBER)
@@ -1392,6 +1398,9 @@
join_cmd.uid = cmd.uid;
join_cmd.id = cmd.id;
join_cmd.addr_size = rdma_addr_size((struct sockaddr *) &cmd.addr);
+ if (!join_cmd.addr_size)
+ return -EINVAL;
+
join_cmd.join_flags = RDMA_MC_JOIN_FLAG_FULLMEMBER;
memcpy(&join_cmd.addr, &cmd.addr, join_cmd.addr_size);
@@ -1407,6 +1416,9 @@
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT;
+ if (!rdma_addr_size((struct sockaddr *)&cmd.addr))
+ return -EINVAL;
+
return ucma_process_join(file, &cmd, out_len);
}
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index c22fde6..e74aa1d 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -193,7 +193,7 @@
sg_list_start = umem->sg_head.sgl;
while (npages) {
- ret = get_user_pages(cur_base,
+ ret = get_user_pages_longterm(cur_base,
min_t(unsigned long, npages,
PAGE_SIZE / sizeof (struct page *)),
gup_flags, page_list, vma_list);
@@ -357,7 +357,7 @@
return -EINVAL;
}
- ret = sg_pcopy_to_buffer(umem->sg_head.sgl, umem->nmap, dst, length,
+ ret = sg_pcopy_to_buffer(umem->sg_head.sgl, umem->npages, dst, length,
offset + ib_umem_offset(umem));
if (ret < 0)
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index d118ffe..4b717cf 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -2491,9 +2491,13 @@
static void *alloc_wr(size_t wr_size, __u32 num_sge)
{
+ if (num_sge >= (U32_MAX - ALIGN(wr_size, sizeof (struct ib_sge))) /
+ sizeof (struct ib_sge))
+ return NULL;
+
return kmalloc(ALIGN(wr_size, sizeof (struct ib_sge)) +
num_sge * sizeof (struct ib_sge), GFP_KERNEL);
-};
+}
ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,
struct ib_device *ib_dev,
@@ -2720,6 +2724,13 @@
goto err;
}
+ if (user_wr->num_sge >=
+ (U32_MAX - ALIGN(sizeof *next, sizeof (struct ib_sge))) /
+ sizeof (struct ib_sge)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
next = kmalloc(ALIGN(sizeof *next, sizeof (struct ib_sge)) +
user_wr->num_sge * sizeof (struct ib_sge),
GFP_KERNEL);
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index 44b1104..c5e921b 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -735,12 +735,21 @@
return -1;
}
+static bool verify_command_idx(u32 command, bool extended)
+{
+ if (extended)
+ return command < ARRAY_SIZE(uverbs_ex_cmd_table);
+
+ return command < ARRAY_SIZE(uverbs_cmd_table);
+}
+
static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
size_t count, loff_t *pos)
{
struct ib_uverbs_file *file = filp->private_data;
struct ib_device *ib_dev;
struct ib_uverbs_cmd_hdr hdr;
+ bool extended_command;
__u32 command;
__u32 flags;
int srcu_key;
@@ -770,6 +779,15 @@
}
command = hdr.command & IB_USER_VERBS_CMD_COMMAND_MASK;
+ flags = (hdr.command &
+ IB_USER_VERBS_CMD_FLAGS_MASK) >> IB_USER_VERBS_CMD_FLAGS_SHIFT;
+
+ extended_command = flags & IB_USER_VERBS_CMD_FLAG_EXTENDED;
+ if (!verify_command_idx(command, extended_command)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
if (verify_command_mask(ib_dev, command)) {
ret = -EOPNOTSUPP;
goto out;
@@ -781,12 +799,8 @@
goto out;
}
- flags = (hdr.command &
- IB_USER_VERBS_CMD_FLAGS_MASK) >> IB_USER_VERBS_CMD_FLAGS_SHIFT;
-
if (!flags) {
- if (command >= ARRAY_SIZE(uverbs_cmd_table) ||
- !uverbs_cmd_table[command]) {
+ if (!uverbs_cmd_table[command]) {
ret = -EINVAL;
goto out;
}
@@ -807,8 +821,7 @@
struct ib_udata uhw;
size_t written_count = count;
- if (command >= ARRAY_SIZE(uverbs_ex_cmd_table) ||
- !uverbs_ex_cmd_table[command]) {
+ if (!uverbs_ex_cmd_table[command]) {
ret = -ENOSYS;
goto out;
}
diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c
index 4682909..7853b0c 100644
--- a/drivers/infiniband/hw/hfi1/chip.c
+++ b/drivers/infiniband/hw/hfi1/chip.c
@@ -6379,18 +6379,17 @@
*
* The expectation is that the caller of this routine would have taken
* care of properly transitioning the link into the correct state.
+ * NOTE: the caller needs to acquire the dd->dc8051_lock lock
+ * before calling this function.
*/
-static void dc_shutdown(struct hfi1_devdata *dd)
+static void _dc_shutdown(struct hfi1_devdata *dd)
{
- unsigned long flags;
+ lockdep_assert_held(&dd->dc8051_lock);
- spin_lock_irqsave(&dd->dc8051_lock, flags);
- if (dd->dc_shutdown) {
- spin_unlock_irqrestore(&dd->dc8051_lock, flags);
+ if (dd->dc_shutdown)
return;
- }
+
dd->dc_shutdown = 1;
- spin_unlock_irqrestore(&dd->dc8051_lock, flags);
/* Shutdown the LCB */
lcb_shutdown(dd, 1);
/*
@@ -6401,35 +6400,45 @@
write_csr(dd, DC_DC8051_CFG_RST, 0x1);
}
+static void dc_shutdown(struct hfi1_devdata *dd)
+{
+ mutex_lock(&dd->dc8051_lock);
+ _dc_shutdown(dd);
+ mutex_unlock(&dd->dc8051_lock);
+}
+
/*
* Calling this after the DC has been brought out of reset should not
* do any damage.
+ * NOTE: the caller needs to acquire the dd->dc8051_lock lock
+ * before calling this function.
*/
-static void dc_start(struct hfi1_devdata *dd)
+static void _dc_start(struct hfi1_devdata *dd)
{
- unsigned long flags;
- int ret;
+ lockdep_assert_held(&dd->dc8051_lock);
- spin_lock_irqsave(&dd->dc8051_lock, flags);
if (!dd->dc_shutdown)
- goto done;
- spin_unlock_irqrestore(&dd->dc8051_lock, flags);
+ return;
+
/* Take the 8051 out of reset */
write_csr(dd, DC_DC8051_CFG_RST, 0ull);
/* Wait until 8051 is ready */
- ret = wait_fm_ready(dd, TIMEOUT_8051_START);
- if (ret) {
+ if (wait_fm_ready(dd, TIMEOUT_8051_START))
dd_dev_err(dd, "%s: timeout starting 8051 firmware\n",
__func__);
- }
+
/* Take away reset for LCB and RX FPE (set in lcb_shutdown). */
write_csr(dd, DCC_CFG_RESET, 0x10);
/* lcb_shutdown() with abort=1 does not restore these */
write_csr(dd, DC_LCB_ERR_EN, dd->lcb_err_en);
- spin_lock_irqsave(&dd->dc8051_lock, flags);
dd->dc_shutdown = 0;
-done:
- spin_unlock_irqrestore(&dd->dc8051_lock, flags);
+}
+
+static void dc_start(struct hfi1_devdata *dd)
+{
+ mutex_lock(&dd->dc8051_lock);
+ _dc_start(dd);
+ mutex_unlock(&dd->dc8051_lock);
}
/*
@@ -8418,16 +8427,11 @@
{
u64 reg, completed;
int return_code;
- unsigned long flags;
unsigned long timeout;
hfi1_cdbg(DC8051, "type %d, data 0x%012llx", type, in_data);
- /*
- * Alternative to holding the lock for a long time:
- * - keep busy wait - have other users bounce off
- */
- spin_lock_irqsave(&dd->dc8051_lock, flags);
+ mutex_lock(&dd->dc8051_lock);
/* We can't send any commands to the 8051 if it's in reset */
if (dd->dc_shutdown) {
@@ -8453,10 +8457,8 @@
return_code = -ENXIO;
goto fail;
}
- spin_unlock_irqrestore(&dd->dc8051_lock, flags);
- dc_shutdown(dd);
- dc_start(dd);
- spin_lock_irqsave(&dd->dc8051_lock, flags);
+ _dc_shutdown(dd);
+ _dc_start(dd);
}
/*
@@ -8534,8 +8536,7 @@
write_csr(dd, DC_DC8051_CFG_HOST_CMD_0, 0);
fail:
- spin_unlock_irqrestore(&dd->dc8051_lock, flags);
-
+ mutex_unlock(&dd->dc8051_lock);
return return_code;
}
@@ -9489,8 +9490,11 @@
int ret;
u8 status;
- /* report success if not a QSFP */
- if (ppd->port_type != PORT_TYPE_QSFP)
+ /*
+ * Report success if not a QSFP or, if it is a QSFP, but the cable is
+ * not present
+ */
+ if (ppd->port_type != PORT_TYPE_QSFP || !qsfp_mod_present(ppd))
return 0;
/* read byte 2, the status byte */
@@ -11846,6 +11850,10 @@
dd->scntrs = NULL;
kfree(dd->cntrnames);
dd->cntrnames = NULL;
+ if (dd->update_cntr_wq) {
+ destroy_workqueue(dd->update_cntr_wq);
+ dd->update_cntr_wq = NULL;
+ }
}
static u64 read_dev_port_cntr(struct hfi1_devdata *dd, struct cntr_entry *entry,
@@ -12001,7 +12009,7 @@
return write_dev_port_cntr(ppd->dd, entry, sval, ppd, vl, data);
}
-static void update_synth_timer(unsigned long opaque)
+static void do_update_synth_timer(struct work_struct *work)
{
u64 cur_tx;
u64 cur_rx;
@@ -12010,8 +12018,8 @@
int i, j, vl;
struct hfi1_pportdata *ppd;
struct cntr_entry *entry;
-
- struct hfi1_devdata *dd = (struct hfi1_devdata *)opaque;
+ struct hfi1_devdata *dd = container_of(work, struct hfi1_devdata,
+ update_cntr_work);
/*
* Rather than keep beating on the CSRs pick a minimal set that we can
@@ -12094,7 +12102,13 @@
} else {
hfi1_cdbg(CNTR, "[%d] No update necessary", dd->unit);
}
+}
+static void update_synth_timer(unsigned long opaque)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)opaque;
+
+ queue_work(dd->update_cntr_wq, &dd->update_cntr_work);
mod_timer(&dd->synth_stats_timer, jiffies + HZ * SYNTH_CNT_TIME);
}
@@ -12330,6 +12344,13 @@
if (init_cpu_counters(dd))
goto bail;
+ dd->update_cntr_wq = alloc_ordered_workqueue("hfi1_update_cntr_%d",
+ WQ_MEM_RECLAIM, dd->unit);
+ if (!dd->update_cntr_wq)
+ goto bail;
+
+ INIT_WORK(&dd->update_cntr_work, do_update_synth_timer);
+
mod_timer(&dd->synth_stats_timer, jiffies + HZ * SYNTH_CNT_TIME);
return 0;
bail:
diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h
index cc87fd4..a3279f3d 100644
--- a/drivers/infiniband/hw/hfi1/hfi.h
+++ b/drivers/infiniband/hw/hfi1/hfi.h
@@ -475,7 +475,7 @@
#define HFI1_PART_ENFORCE_OUT 0x2
/* how often we check for synthetic counter wrap around */
-#define SYNTH_CNT_TIME 2
+#define SYNTH_CNT_TIME 3
/* Counter flags */
#define CNTR_NORMAL 0x0 /* Normal counters, just read register */
@@ -929,8 +929,9 @@
spinlock_t rcvctrl_lock; /* protect changes to RcvCtrl */
/* around rcd and (user ctxts) ctxt_cnt use (intr vs free) */
spinlock_t uctxt_lock; /* rcd and user context changes */
- /* exclusive access to 8051 */
- spinlock_t dc8051_lock;
+ struct mutex dc8051_lock; /* exclusive access to 8051 */
+ struct workqueue_struct *update_cntr_wq;
+ struct work_struct update_cntr_work;
/* exclusive access to 8051 memory */
spinlock_t dc8051_memlock;
int dc8051_timed_out; /* remember if the 8051 timed out */
diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c
index a3dd27b..84a97f3 100644
--- a/drivers/infiniband/hw/hfi1/init.c
+++ b/drivers/infiniband/hw/hfi1/init.c
@@ -1078,11 +1078,11 @@
spin_lock_init(&dd->uctxt_lock);
spin_lock_init(&dd->hfi1_diag_trans_lock);
spin_lock_init(&dd->sc_init_lock);
- spin_lock_init(&dd->dc8051_lock);
spin_lock_init(&dd->dc8051_memlock);
seqlock_init(&dd->sc2vl_lock);
spin_lock_init(&dd->sde_map_lock);
spin_lock_init(&dd->pio_map_lock);
+ mutex_init(&dd->dc8051_lock);
init_waitqueue_head(&dd->event_queue);
dd->int_counter = alloc_percpu(u64);
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index c41c8d0..19bc1c2 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -1168,7 +1168,7 @@
/* need to protect from a race on closing the vma as part of
* mlx4_ib_vma_close().
*/
- down_read(&owning_mm->mmap_sem);
+ down_write(&owning_mm->mmap_sem);
for (i = 0; i < HW_BAR_COUNT; i++) {
vma = context->hw_bar_info[i].vma;
if (!vma)
@@ -1182,11 +1182,13 @@
BUG_ON(1);
}
+ context->hw_bar_info[i].vma->vm_flags &=
+ ~(VM_SHARED | VM_MAYSHARE);
/* context going to be destroyed, should not access ops any more */
context->hw_bar_info[i].vma->vm_ops = NULL;
}
- up_read(&owning_mm->mmap_sem);
+ up_write(&owning_mm->mmap_sem);
mmput(owning_mm);
put_task_struct(owning_process);
}
diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c
index 5d73989..ae41623 100644
--- a/drivers/infiniband/hw/mlx4/mr.c
+++ b/drivers/infiniband/hw/mlx4/mr.c
@@ -406,7 +406,6 @@
goto err_free_mr;
mr->max_pages = max_num_sg;
-
err = mlx4_mr_enable(dev->dev, &mr->mmr);
if (err)
goto err_free_pl;
@@ -417,6 +416,7 @@
return &mr->ibmr;
err_free_pl:
+ mr->ibmr.device = pd->device;
mlx4_free_priv_pages(mr);
err_free_mr:
(void) mlx4_mr_free(dev->dev, &mr->mmr);
diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c
index fcd04b8..fc62a7d 100644
--- a/drivers/infiniband/hw/mlx5/cq.c
+++ b/drivers/infiniband/hw/mlx5/cq.c
@@ -172,6 +172,8 @@
struct mlx5_ib_srq *srq;
struct mlx5_ib_wq *wq;
u16 wqe_ctr;
+ u8 roce_packet_type;
+ bool vlan_present;
u8 g;
if (qp->ibqp.srq || qp->ibqp.xrcd) {
@@ -223,7 +225,6 @@
break;
}
wc->slid = be16_to_cpu(cqe->slid);
- wc->sl = (be32_to_cpu(cqe->flags_rqpn) >> 24) & 0xf;
wc->src_qp = be32_to_cpu(cqe->flags_rqpn) & 0xffffff;
wc->dlid_path_bits = cqe->ml_path;
g = (be32_to_cpu(cqe->flags_rqpn) >> 28) & 3;
@@ -237,10 +238,22 @@
wc->pkey_index = 0;
}
- if (ll != IB_LINK_LAYER_ETHERNET)
+ if (ll != IB_LINK_LAYER_ETHERNET) {
+ wc->sl = (be32_to_cpu(cqe->flags_rqpn) >> 24) & 0xf;
return;
+ }
- switch (wc->sl & 0x3) {
+ vlan_present = cqe->l4_l3_hdr_type & 0x1;
+ roce_packet_type = (be32_to_cpu(cqe->flags_rqpn) >> 24) & 0x3;
+ if (vlan_present) {
+ wc->vlan_id = (be16_to_cpu(cqe->vlan_info)) & 0xfff;
+ wc->sl = (be16_to_cpu(cqe->vlan_info) >> 13) & 0x7;
+ wc->wc_flags |= IB_WC_WITH_VLAN;
+ } else {
+ wc->sl = 0;
+ }
+
+ switch (roce_packet_type) {
case MLX5_CQE_ROCE_L3_HEADER_TYPE_GRH:
wc->network_hdr_type = RDMA_NETWORK_IB;
break;
@@ -1117,7 +1130,12 @@
if (ucmd.reserved0 || ucmd.reserved1)
return -EINVAL;
- umem = ib_umem_get(context, ucmd.buf_addr, entries * ucmd.cqe_size,
+ /* check multiplication overflow */
+ if (ucmd.cqe_size && SIZE_MAX / ucmd.cqe_size <= entries - 1)
+ return -EINVAL;
+
+ umem = ib_umem_get(context, ucmd.buf_addr,
+ (size_t)ucmd.cqe_size * entries,
IB_ACCESS_LOCAL_WRITE, 1);
if (IS_ERR(umem)) {
err = PTR_ERR(umem);
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index 5e29fbd..d7da1dc 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -1313,7 +1313,7 @@
/* need to protect from a race on closing the vma as part of
* mlx5_ib_vma_close.
*/
- down_read(&owning_mm->mmap_sem);
+ down_write(&owning_mm->mmap_sem);
list_for_each_entry_safe(vma_private, n, &context->vma_private_list,
list) {
vma = vma_private->vma;
@@ -1323,11 +1323,12 @@
/* context going to be destroyed, should
* not access ops any more.
*/
+ vma->vm_flags &= ~(VM_SHARED | VM_MAYSHARE);
vma->vm_ops = NULL;
list_del(&vma_private->list);
kfree(vma_private);
}
- up_read(&owning_mm->mmap_sem);
+ up_write(&owning_mm->mmap_sem);
mmput(owning_mm);
put_task_struct(owning_process);
}
diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c
index 0a260a0..7e466f0 100644
--- a/drivers/infiniband/hw/mlx5/mr.c
+++ b/drivers/infiniband/hw/mlx5/mr.c
@@ -1648,6 +1648,7 @@
MLX5_SET(mkc, mkc, access_mode, mr->access_mode);
MLX5_SET(mkc, mkc, umr_en, 1);
+ mr->ibmr.device = pd->device;
err = mlx5_core_create_mkey(dev->mdev, &mr->mmkey, in, inlen);
if (err)
goto err_destroy_psv;
@@ -1820,7 +1821,6 @@
mr->ibmr.iova = sg_dma_address(sg) + sg_offset;
mr->ibmr.length = 0;
- mr->ndescs = sg_nents;
for_each_sg(sgl, sg, sg_nents, i) {
if (unlikely(i >= mr->max_descs))
@@ -1832,6 +1832,7 @@
sg_offset = 0;
}
+ mr->ndescs = i;
if (sg_offset_p)
*sg_offset_p = sg_offset;
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
index fdd1561..403df35 100644
--- a/drivers/infiniband/hw/mlx5/qp.c
+++ b/drivers/infiniband/hw/mlx5/qp.c
@@ -1130,7 +1130,7 @@
ib_umem_release(sq->ubuffer.umem);
}
-static int get_rq_pas_size(void *qpc)
+static size_t get_rq_pas_size(void *qpc)
{
u32 log_page_size = MLX5_GET(qpc, qpc, log_page_size) + 12;
u32 log_rq_stride = MLX5_GET(qpc, qpc, log_rq_stride);
@@ -1146,7 +1146,8 @@
}
static int create_raw_packet_qp_rq(struct mlx5_ib_dev *dev,
- struct mlx5_ib_rq *rq, void *qpin)
+ struct mlx5_ib_rq *rq, void *qpin,
+ size_t qpinlen)
{
struct mlx5_ib_qp *mqp = rq->base.container_mibqp;
__be64 *pas;
@@ -1155,9 +1156,12 @@
void *rqc;
void *wq;
void *qpc = MLX5_ADDR_OF(create_qp_in, qpin, qpc);
- int inlen;
+ size_t rq_pas_size = get_rq_pas_size(qpc);
+ size_t inlen;
int err;
- u32 rq_pas_size = get_rq_pas_size(qpc);
+
+ if (qpinlen < rq_pas_size + MLX5_BYTE_OFF(create_qp_in, pas))
+ return -EINVAL;
inlen = MLX5_ST_SZ_BYTES(create_rq_in) + rq_pas_size;
in = mlx5_vzalloc(inlen);
@@ -1235,7 +1239,7 @@
}
static int create_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
- u32 *in,
+ u32 *in, size_t inlen,
struct ib_pd *pd)
{
struct mlx5_ib_raw_packet_qp *raw_packet_qp = &qp->raw_packet_qp;
@@ -1262,7 +1266,7 @@
if (qp->rq.wqe_cnt) {
rq->base.container_mibqp = qp;
- err = create_raw_packet_qp_rq(dev, rq, in);
+ err = create_raw_packet_qp_rq(dev, rq, in, inlen);
if (err)
goto err_destroy_sq;
@@ -1753,10 +1757,15 @@
qp->flags |= MLX5_IB_QP_LSO;
}
+ if (inlen < 0) {
+ err = -EINVAL;
+ goto err;
+ }
+
if (init_attr->qp_type == IB_QPT_RAW_PACKET) {
qp->raw_packet_qp.sq.ubuffer.buf_addr = ucmd.sq_buf_addr;
raw_packet_qp_copy_info(qp, &qp->raw_packet_qp);
- err = create_raw_packet_qp(dev, qp, in, pd);
+ err = create_raw_packet_qp(dev, qp, in, inlen, pd);
} else {
err = mlx5_core_create_qp(dev->mdev, &base->mqp, in, inlen);
}
@@ -1796,6 +1805,7 @@
else if (qp->create_type == MLX5_QP_KERNEL)
destroy_qp_kernel(dev, qp);
+err:
kvfree(in);
return err;
}
diff --git a/drivers/infiniband/hw/mlx5/srq.c b/drivers/infiniband/hw/mlx5/srq.c
index d61fd2c..5c1dbe2 100644
--- a/drivers/infiniband/hw/mlx5/srq.c
+++ b/drivers/infiniband/hw/mlx5/srq.c
@@ -243,8 +243,8 @@
{
struct mlx5_ib_dev *dev = to_mdev(pd->device);
struct mlx5_ib_srq *srq;
- int desc_size;
- int buf_size;
+ size_t desc_size;
+ size_t buf_size;
int err;
struct mlx5_srq_attr in = {0};
__u32 max_srq_wqes = 1 << MLX5_CAP_GEN(dev->mdev, log_max_srq_sz);
@@ -268,15 +268,18 @@
desc_size = sizeof(struct mlx5_wqe_srq_next_seg) +
srq->msrq.max_gs * sizeof(struct mlx5_wqe_data_seg);
+ if (desc_size == 0 || srq->msrq.max_gs > desc_size)
+ return ERR_PTR(-EINVAL);
desc_size = roundup_pow_of_two(desc_size);
- desc_size = max_t(int, 32, desc_size);
+ desc_size = max_t(size_t, 32, desc_size);
+ if (desc_size < sizeof(struct mlx5_wqe_srq_next_seg))
+ return ERR_PTR(-EINVAL);
srq->msrq.max_avail_gather = (desc_size - sizeof(struct mlx5_wqe_srq_next_seg)) /
sizeof(struct mlx5_wqe_data_seg);
srq->msrq.wqe_shift = ilog2(desc_size);
buf_size = srq->msrq.max * desc_size;
- 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);
+ if (buf_size < desc_size)
+ return ERR_PTR(-EINVAL);
in.type = init_attr->srq_type;
if (pd->uobject)
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_stats.c b/drivers/infiniband/hw/ocrdma/ocrdma_stats.c
index 8bef09a..2659430 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_stats.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_stats.c
@@ -836,7 +836,7 @@
dev->reset_stats.type = OCRDMA_RESET_STATS;
dev->reset_stats.dev = dev;
- if (!debugfs_create_file("reset_stats", S_IRUSR, dev->dir,
+ if (!debugfs_create_file("reset_stats", 0200, dev->dir,
&dev->reset_stats, &ocrdma_dbg_ops))
goto err;
diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c
index cedb447..228cb4c 100644
--- a/drivers/infiniband/hw/qib/qib_iba7322.c
+++ b/drivers/infiniband/hw/qib/qib_iba7322.c
@@ -150,7 +150,7 @@
.string = txselect_list,
.maxlen = MAX_ATTEN_LEN
};
-static int setup_txselect(const char *, struct kernel_param *);
+static int setup_txselect(const char *, const struct kernel_param *);
module_param_call(txselect, setup_txselect, param_get_string,
&kp_txselect, S_IWUSR | S_IRUGO);
MODULE_PARM_DESC(txselect,
@@ -6177,7 +6177,7 @@
}
/* handle the txselect parameter changing */
-static int setup_txselect(const char *str, struct kernel_param *kp)
+static int setup_txselect(const char *str, const struct kernel_param *kp)
{
struct qib_devdata *dd;
unsigned long val;
diff --git a/drivers/infiniband/sw/rdmavt/ah.c b/drivers/infiniband/sw/rdmavt/ah.c
index 16c4461..b0f09fb 100644
--- a/drivers/infiniband/sw/rdmavt/ah.c
+++ b/drivers/infiniband/sw/rdmavt/ah.c
@@ -119,7 +119,7 @@
spin_lock_irqsave(&dev->n_ahs_lock, flags);
if (dev->n_ahs_allocated == dev->dparms.props.max_ah) {
- spin_unlock(&dev->n_ahs_lock);
+ spin_unlock_irqrestore(&dev->n_ahs_lock, flags);
kfree(ah);
return ERR_PTR(-ENOMEM);
}
diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c
index 39e31b2..2152c71 100644
--- a/drivers/infiniband/sw/rxe/rxe_resp.c
+++ b/drivers/infiniband/sw/rxe/rxe_resp.c
@@ -471,8 +471,6 @@
state = RESPST_ERR_LENGTH;
goto err;
}
-
- qp->resp.resid = mtu;
} else {
if (pktlen != resid) {
state = RESPST_ERR_LENGTH;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
index 335bd2c..34122c9 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
@@ -974,6 +974,19 @@
*/
priv->dev->broadcast[8] = priv->pkey >> 8;
priv->dev->broadcast[9] = priv->pkey & 0xff;
+
+ /*
+ * Update the broadcast address in the priv->broadcast object,
+ * in case it already exists, otherwise no one will do that.
+ */
+ if (priv->broadcast) {
+ spin_lock_irq(&priv->lock);
+ memcpy(priv->broadcast->mcmember.mgid.raw,
+ priv->dev->broadcast + 4,
+ sizeof(union ib_gid));
+ spin_unlock_irq(&priv->lock);
+ }
+
return 0;
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 183db0c..0df7d45 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -799,6 +799,22 @@
spin_lock_irqsave(&priv->lock, flags);
if (!IS_ERR_OR_NULL(ah)) {
+ /*
+ * pathrec.dgid is used as the database key from the LLADDR,
+ * it must remain unchanged even if the SA returns a different
+ * GID to use in the AH.
+ */
+ if (memcmp(pathrec->dgid.raw, path->pathrec.dgid.raw,
+ sizeof(union ib_gid))) {
+ ipoib_dbg(
+ priv,
+ "%s got PathRec for gid %pI6 while asked for %pI6\n",
+ dev->name, pathrec->dgid.raw,
+ path->pathrec.dgid.raw);
+ memcpy(pathrec->dgid.raw, path->pathrec.dgid.raw,
+ sizeof(union ib_gid));
+ }
+
path->pathrec = *pathrec;
old_ah = path->ah;
@@ -919,8 +935,8 @@
return 0;
}
-static void neigh_add_path(struct sk_buff *skb, u8 *daddr,
- struct net_device *dev)
+static struct ipoib_neigh *neigh_add_path(struct sk_buff *skb, u8 *daddr,
+ struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_path *path;
@@ -933,7 +949,15 @@
spin_unlock_irqrestore(&priv->lock, flags);
++dev->stats.tx_dropped;
dev_kfree_skb_any(skb);
- return;
+ return NULL;
+ }
+
+ /* To avoid race condition, make sure that the
+ * neigh will be added only once.
+ */
+ if (unlikely(!list_empty(&neigh->list))) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return neigh;
}
path = __path_find(dev, daddr + 4);
@@ -971,7 +995,7 @@
spin_unlock_irqrestore(&priv->lock, flags);
ipoib_send(dev, skb, path->ah, IPOIB_QPN(daddr));
ipoib_neigh_put(neigh);
- return;
+ return NULL;
}
} else {
neigh->ah = NULL;
@@ -988,7 +1012,7 @@
spin_unlock_irqrestore(&priv->lock, flags);
ipoib_neigh_put(neigh);
- return;
+ return NULL;
err_path:
ipoib_neigh_free(neigh);
@@ -998,6 +1022,8 @@
spin_unlock_irqrestore(&priv->lock, flags);
ipoib_neigh_put(neigh);
+
+ return NULL;
}
static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev,
@@ -1103,8 +1129,9 @@
case htons(ETH_P_TIPC):
neigh = ipoib_neigh_get(dev, phdr->hwaddr);
if (unlikely(!neigh)) {
- neigh_add_path(skb, phdr->hwaddr, dev);
- return NETDEV_TX_OK;
+ neigh = neigh_add_path(skb, phdr->hwaddr, dev);
+ if (likely(!neigh))
+ return NETDEV_TX_OK;
}
break;
case htons(ETH_P_ARP):
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index fddff40..8f79cae 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -487,6 +487,9 @@
!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags))
return -EINVAL;
+ init_completion(&mcast->done);
+ set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
+
ipoib_dbg_mcast(priv, "joining MGID %pI6\n", mcast->mcmember.mgid.raw);
rec.mgid = mcast->mcmember.mgid;
@@ -645,8 +648,6 @@
if (mcast->backoff == 1 ||
time_after_eq(jiffies, mcast->delay_until)) {
/* Found the next unjoined group */
- init_completion(&mcast->done);
- set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
if (ipoib_mcast_join(dev, mcast)) {
spin_unlock_irq(&priv->lock);
return;
@@ -666,11 +667,9 @@
queue_delayed_work(priv->wq, &priv->mcast_task,
delay_until - jiffies);
}
- if (mcast) {
- init_completion(&mcast->done);
- set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
+ if (mcast)
ipoib_mcast_join(dev, mcast);
- }
+
spin_unlock_irq(&priv->lock);
}
@@ -818,7 +817,10 @@
spin_lock_irqsave(&priv->lock, flags);
if (!neigh) {
neigh = ipoib_neigh_alloc(daddr, dev);
- if (neigh) {
+ /* Make sure that the neigh will be added only
+ * once to mcast list.
+ */
+ if (neigh && list_empty(&neigh->list)) {
kref_get(&mcast->ah->ref);
neigh->ah = mcast->ah;
list_add_tail(&neigh->list, &mcast->neigh_list);
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
index 0983470..b879d21 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.c
+++ b/drivers/infiniband/ulp/isert/ib_isert.c
@@ -2098,6 +2098,9 @@
u32 rkey, offset;
int ret;
+ if (cmd->ctx_init_done)
+ goto rdma_ctx_post;
+
if (dir == DMA_FROM_DEVICE) {
addr = cmd->write_va;
rkey = cmd->write_stag;
@@ -2125,11 +2128,15 @@
se_cmd->t_data_sg, se_cmd->t_data_nents,
offset, addr, rkey, dir);
}
+
if (ret < 0) {
isert_err("Cmd: %p failed to prepare RDMA res\n", cmd);
return ret;
}
+ cmd->ctx_init_done = true;
+
+rdma_ctx_post:
ret = rdma_rw_ctx_post(&cmd->rw, conn->qp, port_num, cqe, chain_wr);
if (ret < 0)
isert_err("Cmd: %p failed to post RDMA res\n", cmd);
diff --git a/drivers/infiniband/ulp/isert/ib_isert.h b/drivers/infiniband/ulp/isert/ib_isert.h
index c02ada5..85bd197 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.h
+++ b/drivers/infiniband/ulp/isert/ib_isert.h
@@ -124,6 +124,7 @@
struct rdma_rw_ctx rw;
struct work_struct comp_work;
struct scatterlist sg;
+ bool ctx_init_done;
};
static inline struct isert_cmd *tx_desc_to_cmd(struct iser_tx_desc *desc)
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index 29ab814..7cf468a 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -80,7 +80,7 @@
MODULE_PARM_DESC(srpt_srq_size,
"Shared receive queue (SRQ) size.");
-static int srpt_get_u64_x(char *buffer, struct kernel_param *kp)
+static int srpt_get_u64_x(char *buffer, const struct kernel_param *kp)
{
return sprintf(buffer, "0x%016llx", *(u64 *)kp->arg);
}
diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
index 7f12b65..795fa35 100644
--- a/drivers/input/keyboard/matrix_keypad.c
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -216,8 +216,10 @@
{
struct matrix_keypad *keypad = input_get_drvdata(dev);
+ spin_lock_irq(&keypad->lock);
keypad->stopped = true;
- mb();
+ spin_unlock_irq(&keypad->lock);
+
flush_work(&keypad->work.work);
/*
* matrix_keypad_scan() will leave IRQs enabled;
diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c
index 5a57787..76bb513 100644
--- a/drivers/input/keyboard/qt1070.c
+++ b/drivers/input/keyboard/qt1070.c
@@ -274,9 +274,18 @@
};
MODULE_DEVICE_TABLE(i2c, qt1070_id);
+#ifdef CONFIG_OF
+static const struct of_device_id qt1070_of_match[] = {
+ { .compatible = "qt1070", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, qt1070_of_match);
+#endif
+
static struct i2c_driver qt1070_driver = {
.driver = {
.name = "qt1070",
+ .of_match_table = of_match_ptr(qt1070_of_match),
.pm = &qt1070_pm_ops,
},
.id_table = qt1070_id,
diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c
index 3048ef3..a5e8998 100644
--- a/drivers/input/keyboard/tca8418_keypad.c
+++ b/drivers/input/keyboard/tca8418_keypad.c
@@ -189,8 +189,6 @@
input_event(input, EV_MSC, MSC_SCAN, code);
input_report_key(input, keymap[code], state);
- /* Read for next loop */
- error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, ®);
} while (1);
input_sync(input);
diff --git a/drivers/input/misc/keychord.c b/drivers/input/misc/keychord.c
index fdcc146..82fefdf 100644
--- a/drivers/input/misc/keychord.c
+++ b/drivers/input/misc/keychord.c
@@ -276,7 +276,7 @@
size_t resid = count;
size_t key_bytes;
- if (count < sizeof(struct input_keychord))
+ if (count < sizeof(struct input_keychord) || count > PAGE_SIZE)
return -EINVAL;
keychords = kzalloc(count, GFP_KERNEL);
if (!keychords)
diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c
index 603fc2f..12b2084 100644
--- a/drivers/input/misc/twl4030-pwrbutton.c
+++ b/drivers/input/misc/twl4030-pwrbutton.c
@@ -70,7 +70,7 @@
pwr->phys = "twl4030_pwrbutton/input0";
pwr->dev.parent = &pdev->dev;
- err = devm_request_threaded_irq(&pwr->dev, irq, NULL, powerbutton_irq,
+ err = devm_request_threaded_irq(&pdev->dev, irq, NULL, powerbutton_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
IRQF_ONESHOT,
"twl4030_pwrbutton", pwr);
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index b705f4a..25791c2 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1258,6 +1258,17 @@
To compile this driver as a module, choose M here: the
module will be called ft5x06_ts.
+config TOUCHSCREEN_GEN_VKEYS
+ tristate "Touchscreen Virtual Keys Driver"
+ help
+ Say Y here if you want to generate a sysfs entry for virtual
+ keys on Android.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gen_vkeys.
+
config FT_SECURE_TOUCH
bool "Secure Touch support for Focaltech Touchscreen"
depends on TOUCHSCREEN_FT5X06
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index b2f6911..34e8823 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -40,6 +40,7 @@
obj-$(CONFIG_TOUCHSCREEN_FT5X06) += ft5x06_ts.o
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
+obj-$(CONFIG_TOUCHSCREEN_GEN_VKEYS) += gen_vkeys.o
obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC) += imx6ul_tsc.o
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c
index 71b5a63..e7bb155 100644
--- a/drivers/input/touchscreen/ar1021_i2c.c
+++ b/drivers/input/touchscreen/ar1021_i2c.c
@@ -152,7 +152,7 @@
static SIMPLE_DEV_PM_OPS(ar1021_i2c_pm, ar1021_i2c_suspend, ar1021_i2c_resume);
static const struct i2c_device_id ar1021_i2c_id[] = {
- { "MICROCHIP_AR1021_I2C", 0 },
+ { "ar1021", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id);
diff --git a/drivers/input/touchscreen/gen_vkeys.c b/drivers/input/touchscreen/gen_vkeys.c
new file mode 100644
index 0000000..67b32ea
--- /dev/null
+++ b/drivers/input/touchscreen/gen_vkeys.c
@@ -0,0 +1,226 @@
+/* Copyright (c) 2013, 2018 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/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/input.h>
+#include <linux/input/gen_vkeys.h>
+
+#define MAX_BUF_SIZE 256
+#define VKEY_VER_CODE "0x01"
+
+#define HEIGHT_SCALE_NUM 8
+#define HEIGHT_SCALE_DENOM 10
+
+#define VKEY_Y_OFFSET_DEFAULT 0
+
+/* numerator and denomenator for border equations */
+#define BORDER_ADJUST_NUM 3
+#define BORDER_ADJUST_DENOM 4
+
+static struct kobject *vkey_obj;
+static char *vkey_buf;
+
+static ssize_t vkey_show(struct kobject *obj,
+ struct kobj_attribute *attr, char *buf)
+{
+ strlcpy(buf, vkey_buf, MAX_BUF_SIZE);
+ return strnlen(buf, MAX_BUF_SIZE);
+}
+
+static struct kobj_attribute vkey_obj_attr = {
+ .attr = {
+ .mode = 0444,
+ },
+ .show = vkey_show,
+};
+
+static struct attribute *vkey_attr[] = {
+ &vkey_obj_attr.attr,
+ NULL,
+};
+
+static struct attribute_group vkey_grp = {
+ .attrs = vkey_attr,
+};
+
+static int vkey_parse_dt(struct device *dev,
+ struct vkeys_platform_data *pdata)
+{
+ struct device_node *np = dev->of_node;
+ struct property *prop;
+ int rc, val;
+
+ rc = of_property_read_string(np, "label", &pdata->name);
+ if (rc) {
+ dev_err(dev, "Failed to read label\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(np, "qcom,disp-maxx", &pdata->disp_maxx);
+ if (rc) {
+ dev_err(dev, "Failed to read display max x\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(np, "qcom,disp-maxy", &pdata->disp_maxy);
+ if (rc) {
+ dev_err(dev, "Failed to read display max y\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(np, "qcom,panel-maxx", &pdata->panel_maxx);
+ if (rc) {
+ dev_err(dev, "Failed to read panel max x\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(np, "qcom,panel-maxy", &pdata->panel_maxy);
+ if (rc) {
+ dev_err(dev, "Failed to read panel max y\n");
+ return -EINVAL;
+ }
+
+ prop = of_find_property(np, "qcom,key-codes", NULL);
+ if (prop) {
+ pdata->num_keys = prop->length / sizeof(u32);
+ pdata->keycodes = devm_kzalloc(dev,
+ sizeof(u32) * pdata->num_keys, GFP_KERNEL);
+ if (!pdata->keycodes)
+ return -ENOMEM;
+ rc = of_property_read_u32_array(np, "qcom,key-codes",
+ pdata->keycodes, pdata->num_keys);
+ if (rc) {
+ dev_err(dev, "Failed to read key codes\n");
+ return -EINVAL;
+ }
+ }
+
+ pdata->y_offset = VKEY_Y_OFFSET_DEFAULT;
+ rc = of_property_read_u32(np, "qcom,y-offset", &val);
+ if (!rc)
+ pdata->y_offset = val;
+ else if (rc != -EINVAL) {
+ dev_err(dev, "Failed to read y position offset\n");
+ return rc;
+ }
+ return 0;
+}
+
+static int vkeys_probe(struct platform_device *pdev)
+{
+ struct vkeys_platform_data *pdata;
+ int width, height, center_x, center_y;
+ int x1 = 0, x2 = 0, i, c = 0, ret, border;
+ char *name;
+
+ vkey_buf = devm_kzalloc(&pdev->dev, MAX_BUF_SIZE, GFP_KERNEL);
+ if (!vkey_buf)
+ return -ENOMEM;
+
+ if (pdev->dev.of_node) {
+ pdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct vkeys_platform_data), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ ret = vkey_parse_dt(&pdev->dev, pdata);
+ if (ret) {
+ dev_err(&pdev->dev, "Parsing DT failed(%d)", ret);
+ return ret;
+ }
+ } else
+ pdata = pdev->dev.platform_data;
+
+ if (!pdata || !pdata->name || !pdata->keycodes || !pdata->num_keys ||
+ !pdata->disp_maxx || !pdata->disp_maxy || !pdata->panel_maxy) {
+ dev_err(&pdev->dev, "pdata is invalid\n");
+ return -EINVAL;
+ }
+
+ border = (pdata->panel_maxx - pdata->disp_maxx) * 2;
+ width = ((pdata->disp_maxx - (border * (pdata->num_keys - 1)))
+ / pdata->num_keys);
+ height = (pdata->panel_maxy - pdata->disp_maxy);
+ center_y = pdata->disp_maxy + (height / 2) + pdata->y_offset;
+ height = height * HEIGHT_SCALE_NUM / HEIGHT_SCALE_DENOM;
+
+ x2 -= border * BORDER_ADJUST_NUM / BORDER_ADJUST_DENOM;
+
+ for (i = 0; i < pdata->num_keys; i++) {
+ x1 = x2 + border;
+ x2 = x2 + border + width;
+ center_x = x1 + (x2 - x1) / 2;
+ c += snprintf(vkey_buf + c, MAX_BUF_SIZE - c,
+ "%s:%d:%d:%d:%d:%d\n",
+ VKEY_VER_CODE, pdata->keycodes[i],
+ center_x, center_y, width, height);
+ }
+
+ vkey_buf[c] = '\0';
+
+ name = devm_kzalloc(&pdev->dev, sizeof(*name) * MAX_BUF_SIZE,
+ GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+
+ snprintf(name, MAX_BUF_SIZE,
+ "virtualkeys.%s", pdata->name);
+ vkey_obj_attr.attr.name = name;
+
+ vkey_obj = kobject_create_and_add("board_properties", NULL);
+ if (!vkey_obj) {
+ dev_err(&pdev->dev, "unable to create kobject\n");
+ return -ENOMEM;
+ }
+
+ ret = sysfs_create_group(vkey_obj, &vkey_grp);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to create attributes\n");
+ goto destroy_kobj;
+ }
+ return 0;
+
+destroy_kobj:
+ kobject_put(vkey_obj);
+
+ return ret;
+}
+
+static int vkeys_remove(struct platform_device *pdev)
+{
+ sysfs_remove_group(vkey_obj, &vkey_grp);
+ kobject_put(vkey_obj);
+
+ return 0;
+}
+
+static const struct of_device_id vkey_match_table[] = {
+ { .compatible = "qcom,gen-vkeys",},
+ { },
+};
+
+static struct platform_driver vkeys_driver = {
+ .probe = vkeys_probe,
+ .remove = vkeys_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "gen_vkeys",
+ .of_match_table = vkey_match_table,
+ },
+};
+
+module_platform_driver(vkeys_driver);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
index 5d0cd51..a4b7b4c 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -455,6 +455,14 @@
tsc2007_stop(ts);
+ /* power down the chip (TSC2007_SETUP does not ACK on I2C) */
+ err = tsc2007_xfer(ts, PWRDOWN);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "Failed to setup chip: %d\n", err);
+ return err; /* usually, chip does not respond */
+ }
+
err = input_register_device(input_dev);
if (err) {
dev_err(&client->dev,
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 8005ab8..e0b2f63 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -292,6 +292,7 @@
#define TTBCR2_SEP_SHIFT 15
#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT)
+#define TTBCR2_AS (1 << 4)
#define TTBRn_ASID_SHIFT 48
@@ -369,6 +370,15 @@
bool valid;
};
+struct arm_smmu_cb {
+ u64 ttbr[2];
+ u32 tcr[2];
+ u32 mair[2];
+ struct arm_smmu_cfg *cfg;
+ u32 actlr;
+ u32 attributes;
+};
+
struct arm_smmu_master_cfg {
struct arm_smmu_device *smmu;
s16 smendx[];
@@ -440,6 +450,7 @@
u32 num_s2_context_banks;
DECLARE_BITMAP(context_map, ARM_SMMU_MAX_CBS);
DECLARE_BITMAP(secure_context_map, ARM_SMMU_MAX_CBS);
+ struct arm_smmu_cb *cbs;
atomic_t irptndx;
u32 num_mapping_groups;
@@ -1625,17 +1636,76 @@
static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
struct io_pgtable_cfg *pgtbl_cfg)
{
- u32 reg, reg2;
- u64 reg64;
- bool stage1;
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
- struct arm_smmu_device *smmu = smmu_domain->smmu;
+ struct arm_smmu_cb *cb = &smmu_domain->smmu->cbs[cfg->cbndx];
+ bool stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS;
+
+ cb->cfg = cfg;
+
+ /* TTBCR */
+ if (stage1) {
+ if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
+ cb->tcr[0] = pgtbl_cfg->arm_v7s_cfg.tcr;
+ } else {
+ cb->tcr[0] = pgtbl_cfg->arm_lpae_s1_cfg.tcr;
+ cb->tcr[1] = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32;
+ cb->tcr[1] |= TTBCR2_SEP_UPSTREAM;
+ if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64)
+ cb->tcr[1] |= TTBCR2_AS;
+ }
+ } else {
+ cb->tcr[0] = pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
+ }
+
+ /* TTBRs */
+ if (stage1) {
+ if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
+ cb->ttbr[0] = pgtbl_cfg->arm_v7s_cfg.ttbr[0];
+ cb->ttbr[1] = pgtbl_cfg->arm_v7s_cfg.ttbr[1];
+ } else {
+ cb->ttbr[0] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
+ cb->ttbr[0] |= (u64)cfg->asid << TTBRn_ASID_SHIFT;
+ cb->ttbr[1] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1];
+ cb->ttbr[1] |= (u64)cfg->asid << TTBRn_ASID_SHIFT;
+ }
+ } else {
+ cb->ttbr[0] = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
+ }
+
+ /* MAIRs (stage-1 only) */
+ if (stage1) {
+ if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
+ cb->mair[0] = pgtbl_cfg->arm_v7s_cfg.prrr;
+ cb->mair[1] = pgtbl_cfg->arm_v7s_cfg.nmrr;
+ } else {
+ cb->mair[0] = pgtbl_cfg->arm_lpae_s1_cfg.mair[0];
+ cb->mair[1] = pgtbl_cfg->arm_lpae_s1_cfg.mair[1];
+ }
+ }
+
+ cb->attributes = smmu_domain->attributes;
+}
+
+static void arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx)
+{
+ u32 reg;
+ bool stage1;
+ struct arm_smmu_cb *cb = &smmu->cbs[idx];
+ struct arm_smmu_cfg *cfg = cb->cfg;
void __iomem *cb_base, *gr1_base;
+ cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, idx);
+
+ /* Unassigned context banks only need disabling */
+ if (!cfg) {
+ writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR);
+ return;
+ }
+
gr1_base = ARM_SMMU_GR1(smmu);
stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS;
- cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
+ /* CBA2R */
if (smmu->version > ARM_SMMU_V1) {
if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64)
reg = CBA2R_RW64_64BIT;
@@ -1645,7 +1715,7 @@
if (smmu->features & ARM_SMMU_FEAT_VMID16)
reg |= ARM_SMMU_CB_VMID(smmu, cfg) << CBA2R_VMID_SHIFT;
- writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBA2R(cfg->cbndx));
+ writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBA2R(idx));
}
/* CBAR */
@@ -1664,81 +1734,57 @@
/* 8-bit VMIDs live in CBAR */
reg |= ARM_SMMU_CB_VMID(smmu, cfg) << CBAR_VMID_SHIFT;
}
- writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBAR(cfg->cbndx));
+ writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBAR(idx));
+
+ /*
+ * TTBCR
+ * We must write this before the TTBRs, since it determines the
+ * access behaviour of some fields (in particular, ASID[15:8]).
+ */
+ if (stage1 && smmu->version > ARM_SMMU_V1)
+ writel_relaxed(cb->tcr[1], cb_base + ARM_SMMU_CB_TTBCR2);
+ writel_relaxed(cb->tcr[0], cb_base + ARM_SMMU_CB_TTBCR);
/* TTBRs */
- if (stage1) {
- u16 asid = ARM_SMMU_CB_ASID(smmu, cfg);
-
- if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
- reg = pgtbl_cfg->arm_v7s_cfg.ttbr[0];
- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0);
- reg = pgtbl_cfg->arm_v7s_cfg.ttbr[1];
- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR1);
- writel_relaxed(asid, cb_base + ARM_SMMU_CB_CONTEXTIDR);
- } else {
- reg64 = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
- reg64 |= (u64)asid << TTBRn_ASID_SHIFT;
- writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR0);
- reg64 = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1];
- reg64 |= (u64)asid << TTBRn_ASID_SHIFT;
- writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR1);
- }
+ if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
+ writel_relaxed(cfg->asid, cb_base + ARM_SMMU_CB_CONTEXTIDR);
+ writel_relaxed(cb->ttbr[0], cb_base + ARM_SMMU_CB_TTBR0);
+ writel_relaxed(cb->ttbr[1], cb_base + ARM_SMMU_CB_TTBR1);
} else {
- reg64 = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
- writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR0);
+ writeq_relaxed(cb->ttbr[0], cb_base + ARM_SMMU_CB_TTBR0);
+ if (stage1)
+ writeq_relaxed(cb->ttbr[1], cb_base + ARM_SMMU_CB_TTBR1);
}
- /* TTBCR */
- if (stage1) {
- if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
- reg = pgtbl_cfg->arm_v7s_cfg.tcr;
- reg2 = 0;
- } else {
- reg = pgtbl_cfg->arm_lpae_s1_cfg.tcr;
- reg2 = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32;
- reg2 |= TTBCR2_SEP_UPSTREAM;
- }
- if (smmu->version > ARM_SMMU_V1)
- writel_relaxed(reg2, cb_base + ARM_SMMU_CB_TTBCR2);
- } else {
- reg = pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
- }
- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR);
-
/* MAIRs (stage-1 only) */
if (stage1) {
- if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
- reg = pgtbl_cfg->arm_v7s_cfg.prrr;
- reg2 = pgtbl_cfg->arm_v7s_cfg.nmrr;
- } else {
- reg = pgtbl_cfg->arm_lpae_s1_cfg.mair[0];
- reg2 = pgtbl_cfg->arm_lpae_s1_cfg.mair[1];
- }
- writel_relaxed(reg, cb_base + ARM_SMMU_CB_S1_MAIR0);
- writel_relaxed(reg2, cb_base + ARM_SMMU_CB_S1_MAIR1);
+ writel_relaxed(cb->mair[0], cb_base + ARM_SMMU_CB_S1_MAIR0);
+ writel_relaxed(cb->mair[1], cb_base + ARM_SMMU_CB_S1_MAIR1);
}
+ /* ACTLR (implementation defined) */
+ writel_relaxed(cb->actlr, cb_base + ARM_SMMU_CB_ACTLR);
+
/* SCTLR */
reg = SCTLR_CFCFG | SCTLR_CFIE | SCTLR_CFRE | SCTLR_AFE | SCTLR_TRE;
/* Ensure bypass transactions are Non-shareable */
reg |= SCTLR_SHCFG_NSH << SCTLR_SHCFG_SHIFT;
- if (smmu_domain->attributes & (1 << DOMAIN_ATTR_CB_STALL_DISABLE)) {
+ if (cb->attributes & (1 << DOMAIN_ATTR_CB_STALL_DISABLE)) {
reg &= ~SCTLR_CFCFG;
reg |= SCTLR_HUPCF;
}
- if ((!(smmu_domain->attributes & (1 << DOMAIN_ATTR_S1_BYPASS)) &&
- !(smmu_domain->attributes & (1 << DOMAIN_ATTR_EARLY_MAP))) ||
+ if ((!(cb->attributes & (1 << DOMAIN_ATTR_S1_BYPASS)) &&
+ !(cb->attributes & (1 << DOMAIN_ATTR_EARLY_MAP))) ||
!stage1)
reg |= SCTLR_M;
if (stage1)
reg |= SCTLR_S1_ASIDPNE;
-#ifdef __BIG_ENDIAN
- reg |= SCTLR_E;
-#endif
+ if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
+ reg |= SCTLR_E;
+
writel_relaxed(reg, cb_base + ARM_SMMU_CB_SCTLR);
}
@@ -1988,7 +2034,9 @@
if (!dynamic) {
/* Initialise the context bank with our page table cfg */
arm_smmu_init_context_bank(smmu_domain,
- &smmu_domain->pgtbl_cfg);
+ &smmu_domain->pgtbl_cfg);
+ arm_smmu_arch_init_context_bank(smmu_domain, dev);
+ arm_smmu_write_context_bank(smmu, cfg->cbndx);
/* for slave side secure, we may have to force the pagetable
* format to V8L.
*/
@@ -1997,8 +2045,6 @@
if (ret)
goto out_clear_smmu;
- arm_smmu_arch_init_context_bank(smmu_domain, dev);
-
/*
* Request context fault interrupt. Do this last to avoid the
* handler seeing a half-initialised domain state.
@@ -2046,7 +2092,6 @@
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct arm_smmu_device *smmu = smmu_domain->smmu;
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
- void __iomem *cb_base;
int irq;
bool dynamic;
int ret;
@@ -2078,8 +2123,8 @@
* Disable the context bank and free the page tables before freeing
* it.
*/
- cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
- writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR);
+ smmu->cbs[cfg->cbndx].cfg = NULL;
+ arm_smmu_write_context_bank(smmu, cfg->cbndx);
if (cfg->irptndx != INVALID_IRPTNDX) {
irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx];
@@ -3502,19 +3547,16 @@
{
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
struct arm_smmu_device *smmu = smmu_domain->smmu;
- void __iomem *cb_base;
- u32 reg;
+ struct arm_smmu_cb *cb = &smmu->cbs[cfg->cbndx];
int ret;
- cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
ret = arm_smmu_power_on(smmu->pwr);
if (ret)
return ret;
- reg = readl_relaxed(cb_base + ARM_SMMU_CB_SCTLR);
- reg |= SCTLR_M;
+ cb->attributes &= ~(1 << DOMAIN_ATTR_EARLY_MAP);
+ arm_smmu_write_context_bank(smmu, cfg->cbndx);
- writel_relaxed(reg, cb_base + ARM_SMMU_CB_SCTLR);
arm_smmu_power_off(smmu->pwr);
return ret;
}
@@ -3701,18 +3743,17 @@
int i;
u32 val;
struct arm_smmu_impl_def_reg *regs = smmu->impl_def_attach_registers;
- void __iomem *cb_base;
-
/*
* SCTLR.M must be disabled here per ARM SMMUv2 spec
* to prevent table walks with an inconsistent state.
*/
for (i = 0; i < smmu->num_context_banks; ++i) {
- cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, i);
+ struct arm_smmu_cb *cb = &smmu->cbs[i];
+
val = ACTLR_QCOM_ISH << ACTLR_QCOM_ISH_SHIFT |
ACTLR_QCOM_OSH << ACTLR_QCOM_OSH_SHIFT |
ACTLR_QCOM_NSH << ACTLR_QCOM_NSH_SHIFT;
- writel_relaxed(val, cb_base + ARM_SMMU_CB_ACTLR);
+ cb->actlr = val;
}
/* Program implementation defined registers */
@@ -3777,7 +3818,6 @@
int i;
u32 reg, major;
void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
- void __iomem *cb_base;
/*
* Before clearing ARM_MMU500_ACTLR_CPRE, need to
@@ -3794,8 +3834,10 @@
/* Make sure all context banks are disabled and clear CB_FSR */
for (i = 0; i < smmu->num_context_banks; ++i) {
- cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, i);
- writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR);
+ void __iomem *cb_base = ARM_SMMU_CB_BASE(smmu) +
+ ARM_SMMU_CB(smmu, i);
+
+ arm_smmu_write_context_bank(smmu, i);
writel_relaxed(FSR_FAULT, cb_base + ARM_SMMU_CB_FSR);
/*
* Disable MMU-500's not-particularly-beneficial next-page
@@ -4425,6 +4467,10 @@
&cavium_smmu_context_count);
smmu->cavium_id_base -= smmu->num_context_banks;
}
+ smmu->cbs = devm_kcalloc(smmu->dev, smmu->num_context_banks,
+ sizeof(*smmu->cbs), GFP_KERNEL);
+ if (!smmu->cbs)
+ return -ENOMEM;
/* ID2 */
id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID2);
@@ -5385,20 +5431,14 @@
struct device *dev)
{
struct arm_smmu_device *smmu = smmu_domain->smmu;
+ struct arm_smmu_cb *cb = &smmu->cbs[smmu_domain->cfg.cbndx];
struct qsmmuv500_group_iommudata *iommudata =
to_qsmmuv500_group_iommudata(dev->iommu_group);
- void __iomem *cb_base;
- const struct iommu_gather_ops *tlb;
if (!iommudata->has_actlr)
return;
- tlb = smmu_domain->pgtbl_cfg.tlb;
- cb_base = ARM_SMMU_CB_BASE(smmu) +
- ARM_SMMU_CB(smmu, smmu_domain->cfg.cbndx);
-
- writel_relaxed(iommudata->actlr, cb_base + ARM_SMMU_CB_ACTLR);
-
+ cb->actlr = iommudata->actlr;
/*
* Prefetch only works properly if the start and end of all
* buffers in the page table are aligned to 16 Kb.
@@ -5406,12 +5446,6 @@
if ((iommudata->actlr >> QSMMUV500_ACTLR_DEEP_PREFETCH_SHIFT) &
QSMMUV500_ACTLR_DEEP_PREFETCH_MASK)
smmu_domain->qsmmuv500_errata2_min_align = true;
-
- /*
- * Flush the context bank after modifying ACTLR to ensure there
- * are no cache entries with stale state
- */
- tlb->tlb_flush_all(smmu_domain);
}
static int qsmmuv500_tbu_register(struct device *dev, void *cookie)
diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c
index cb72e00..3a1c406 100644
--- a/drivers/iommu/intel-svm.c
+++ b/drivers/iommu/intel-svm.c
@@ -127,6 +127,7 @@
pr_err("IOMMU: %s: Failed to request IRQ for page request queue\n",
iommu->name);
dmar_free_hwirq(irq);
+ iommu->pr_irq = 0;
goto err;
}
dmar_writeq(iommu->reg + DMAR_PQH_REG, 0ULL);
@@ -142,9 +143,11 @@
dmar_writeq(iommu->reg + DMAR_PQT_REG, 0ULL);
dmar_writeq(iommu->reg + DMAR_PQA_REG, 0ULL);
- free_irq(iommu->pr_irq, iommu);
- dmar_free_hwirq(iommu->pr_irq);
- iommu->pr_irq = 0;
+ if (iommu->pr_irq) {
+ free_irq(iommu->pr_irq, iommu);
+ dmar_free_hwirq(iommu->pr_irq);
+ iommu->pr_irq = 0;
+ }
free_pages((unsigned long)iommu->prq, PRQ_ORDER);
iommu->prq = NULL;
diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
index e2583cc..5455671 100644
--- a/drivers/iommu/omap-iommu.c
+++ b/drivers/iommu/omap-iommu.c
@@ -1299,6 +1299,7 @@
const unsigned long flags = SLAB_HWCACHE_ALIGN;
size_t align = 1 << 10; /* L2 pagetable alignement */
struct device_node *np;
+ int ret;
np = of_find_matching_node(NULL, omap_iommu_of_match);
if (!np)
@@ -1312,11 +1313,25 @@
return -ENOMEM;
iopte_cachep = p;
- bus_set_iommu(&platform_bus_type, &omap_iommu_ops);
-
omap_iommu_debugfs_init();
- return platform_driver_register(&omap_iommu_driver);
+ ret = platform_driver_register(&omap_iommu_driver);
+ if (ret) {
+ pr_err("%s: failed to register driver\n", __func__);
+ goto fail_driver;
+ }
+
+ ret = bus_set_iommu(&platform_bus_type, &omap_iommu_ops);
+ if (ret)
+ goto fail_bus;
+
+ return 0;
+
+fail_bus:
+ platform_driver_unregister(&omap_iommu_driver);
+fail_driver:
+ kmem_cache_destroy(iopte_cachep);
+ return ret;
}
subsys_initcall(omap_iommu_init);
/* must be ready before omap3isp is probed */
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index acb9d25..ac15e5d 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -684,7 +684,7 @@
* This gives us (((1UL << id_bits) - 8192) >> 5) possible allocations.
*/
#define IRQS_PER_CHUNK_SHIFT 5
-#define IRQS_PER_CHUNK (1 << IRQS_PER_CHUNK_SHIFT)
+#define IRQS_PER_CHUNK (1UL << IRQS_PER_CHUNK_SHIFT)
static unsigned long *lpi_bitmap;
static u32 lpi_chunks;
@@ -1320,11 +1320,10 @@
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
/*
- * At least one bit of EventID is being used, hence a minimum
- * of two entries. No, the architecture doesn't let you
- * express an ITT with a single entry.
+ * We allocate at least one chunk worth of LPIs bet device,
+ * and thus that many ITEs. The device may require less though.
*/
- nr_ites = max(2UL, roundup_pow_of_two(nvecs));
+ nr_ites = max(IRQS_PER_CHUNK, roundup_pow_of_two(nvecs));
sz = nr_ites * its->ite_size;
sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1;
itt = kzalloc(sz, GFP_KERNEL);
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 2519c92..307d545 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -721,7 +721,7 @@
* Ensure that stores to Normal memory are visible to the
* other CPUs before issuing the IPI.
*/
- smp_wmb();
+ wmb();
for_each_cpu(cpu, mask) {
unsigned long cluster_id = cpu_logical_map(cpu) & ~0xffUL;
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index bd189a4..8f3aa54 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -41,6 +41,7 @@
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/arm-gic.h>
+#include <linux/msm_rtb.h>
#include <asm/cputype.h>
#include <asm/irq.h>
@@ -364,6 +365,7 @@
writel_relaxed_no_log(irqstat,
cpu_base + GIC_CPU_EOI);
handle_domain_irq(gic->domain, irqnr, regs);
+ uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);
continue;
}
if (irqnr < 16) {
@@ -382,6 +384,7 @@
smp_rmb();
handle_IPI(irqnr, regs);
#endif
+ uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);
continue;
}
break;
diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c
index d74374f..abf696b 100644
--- a/drivers/irqchip/irq-mips-gic.c
+++ b/drivers/irqchip/irq-mips-gic.c
@@ -55,6 +55,7 @@
static unsigned int timer_cpu_pin;
static struct irq_chip gic_level_irq_controller, gic_edge_irq_controller;
DECLARE_BITMAP(ipi_resrv, GIC_MAX_INTRS);
+DECLARE_BITMAP(ipi_available, GIC_MAX_INTRS);
static void __gic_irq_dispatch(void);
@@ -746,17 +747,17 @@
return gic_setup_dev_chip(d, virq, spec->hwirq);
} else {
- base_hwirq = find_first_bit(ipi_resrv, gic_shared_intrs);
+ base_hwirq = find_first_bit(ipi_available, gic_shared_intrs);
if (base_hwirq == gic_shared_intrs) {
return -ENOMEM;
}
/* check that we have enough space */
for (i = base_hwirq; i < nr_irqs; i++) {
- if (!test_bit(i, ipi_resrv))
+ if (!test_bit(i, ipi_available))
return -EBUSY;
}
- bitmap_clear(ipi_resrv, base_hwirq, nr_irqs);
+ bitmap_clear(ipi_available, base_hwirq, nr_irqs);
/* map the hwirq for each cpu consecutively */
i = 0;
@@ -787,7 +788,7 @@
return 0;
error:
- bitmap_set(ipi_resrv, base_hwirq, nr_irqs);
+ bitmap_set(ipi_available, base_hwirq, nr_irqs);
return ret;
}
@@ -802,7 +803,7 @@
return;
base_hwirq = GIC_HWIRQ_TO_SHARED(irqd_to_hwirq(data));
- bitmap_set(ipi_resrv, base_hwirq, nr_irqs);
+ bitmap_set(ipi_available, base_hwirq, nr_irqs);
}
int gic_irq_domain_match(struct irq_domain *d, struct device_node *node,
@@ -1066,6 +1067,7 @@
2 * gic_vpes);
}
+ bitmap_copy(ipi_available, ipi_resrv, GIC_MAX_INTRS);
gic_basic_init();
}
diff --git a/drivers/irqchip/qcom/Makefile b/drivers/irqchip/qcom/Makefile
index 8871f9a..e30f074 100644
--- a/drivers/irqchip/qcom/Makefile
+++ b/drivers/irqchip/qcom/Makefile
@@ -2,4 +2,4 @@
obj-$(CONFIG_QTI_PDC_SDM845) += pdc-sdm845.o
obj-$(CONFIG_QTI_PDC_SDM670) += pdc-sdm670.o
obj-$(CONFIG_QTI_PDC_SDXPOORWILLS) += pdc-sdxpoorwills.o
-obj-$(CONFIG_QTI_MPM) += mpm.o mpm-8953.o mpm-8937.o
+obj-$(CONFIG_QTI_MPM) += mpm.o mpm-8909.o mpm-8953.o mpm-8937.o
diff --git a/drivers/irqchip/qcom/mpm-8909.c b/drivers/irqchip/qcom/mpm-8909.c
new file mode 100644
index 0000000..2767171
--- /dev/null
+++ b/drivers/irqchip/qcom/mpm-8909.c
@@ -0,0 +1,70 @@
+/* Copyright (c) 2018, 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 "mpm.h"
+
+const struct mpm_pin mpm_msm8909_gic_chip_data[] = {
+ {2, 216}, /* tsens_upper_lower_int */
+ {48, 168}, /* usb_phy_id_irq */
+ {49, 172}, /* usb1_hs_async_wakeup_irq */
+ {58, 166}, /* usb_hs_irq */
+ {53, 104}, /* mdss_irq */
+ {62, 222}, /* ee0_krait_hlos_spmi_periph_irq */
+ {-1},
+};
+
+const struct mpm_pin mpm_msm8909_gpio_chip_data[] = {
+ {3, 65 },
+ {4, 5},
+ {5, 11},
+ {6, 12},
+ {7, 64},
+ {8, 58},
+ {9, 50},
+ {10, 13},
+ {11, 49},
+ {12, 20},
+ {13, 21},
+ {14, 25},
+ {15, 46},
+ {16, 45},
+ {17, 28},
+ {18, 44},
+ {19, 31},
+ {20, 43},
+ {21, 42},
+ {22, 34},
+ {23, 35},
+ {24, 36},
+ {25, 37},
+ {26, 38},
+ {27, 39},
+ {28, 40},
+ {29, 41},
+ {30, 90},
+ {32, 91},
+ {33, 92},
+ {34, 94},
+ {35, 95},
+ {36, 96},
+ {37, 97},
+ {38, 98},
+ {39, 110},
+ {40, 111},
+ {41, 112},
+ {42, 105},
+ {43, 107},
+ {50, 47},
+ {51, 48},
+ {-1},
+};
diff --git a/drivers/irqchip/qcom/mpm.c b/drivers/irqchip/qcom/mpm.c
index 2de64b6..5e352f4 100644
--- a/drivers/irqchip/qcom/mpm.c
+++ b/drivers/irqchip/qcom/mpm.c
@@ -597,6 +597,10 @@
.compatible = "qcom,mpm-gic-msm8937",
.data = mpm_msm8937_gic_chip_data,
},
+ {
+ .compatible = "qcom,mpm-gic-msm8909",
+ .data = mpm_msm8909_gic_chip_data,
+ },
{}
};
@@ -611,6 +615,10 @@
.compatible = "qcom,mpm-gpio-msm8937",
.data = mpm_msm8937_gpio_chip_data,
},
+ {
+ .compatible = "qcom,mpm-gpio-msm8909",
+ .data = mpm_msm8909_gpio_chip_data,
+ },
{}
};
diff --git a/drivers/irqchip/qcom/mpm.h b/drivers/irqchip/qcom/mpm.h
index 50a127f..c82191e 100644
--- a/drivers/irqchip/qcom/mpm.h
+++ b/drivers/irqchip/qcom/mpm.h
@@ -27,4 +27,7 @@
extern const struct mpm_pin mpm_msm8937_gic_chip_data[];
extern const struct mpm_pin mpm_msm8937_gpio_chip_data[];
+extern const struct mpm_pin mpm_msm8909_gic_chip_data[];
+extern const struct mpm_pin mpm_msm8909_gpio_chip_data[];
+
#endif /* __QCOM_MPM_H__ */
diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c
index e3fa1cd..a57b04f 100644
--- a/drivers/isdn/hardware/mISDN/avmfritz.c
+++ b/drivers/isdn/hardware/mISDN/avmfritz.c
@@ -156,7 +156,7 @@
}
static int
-set_debug(const char *val, struct kernel_param *kp)
+set_debug(const char *val, const struct kernel_param *kp)
{
int ret;
struct fritzcard *card;
diff --git a/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/drivers/isdn/hardware/mISDN/mISDNinfineon.c
index d5bdbaf..1fc2906 100644
--- a/drivers/isdn/hardware/mISDN/mISDNinfineon.c
+++ b/drivers/isdn/hardware/mISDN/mISDNinfineon.c
@@ -244,7 +244,7 @@
}
static int
-set_debug(const char *val, struct kernel_param *kp)
+set_debug(const char *val, const struct kernel_param *kp)
{
int ret;
struct inf_hw *card;
diff --git a/drivers/isdn/hardware/mISDN/netjet.c b/drivers/isdn/hardware/mISDN/netjet.c
index afde4ed..e9fcae4 100644
--- a/drivers/isdn/hardware/mISDN/netjet.c
+++ b/drivers/isdn/hardware/mISDN/netjet.c
@@ -111,7 +111,7 @@
}
static int
-set_debug(const char *val, struct kernel_param *kp)
+set_debug(const char *val, const struct kernel_param *kp)
{
int ret;
struct tiger_hw *card;
diff --git a/drivers/isdn/hardware/mISDN/speedfax.c b/drivers/isdn/hardware/mISDN/speedfax.c
index 9815bb4..1f1446e 100644
--- a/drivers/isdn/hardware/mISDN/speedfax.c
+++ b/drivers/isdn/hardware/mISDN/speedfax.c
@@ -94,7 +94,7 @@
}
static int
-set_debug(const char *val, struct kernel_param *kp)
+set_debug(const char *val, const struct kernel_param *kp)
{
int ret;
struct sfax_hw *card;
diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c
index 3b067ea..0db6783 100644
--- a/drivers/isdn/hardware/mISDN/w6692.c
+++ b/drivers/isdn/hardware/mISDN/w6692.c
@@ -101,7 +101,7 @@
}
static int
-set_debug(const char *val, struct kernel_param *kp)
+set_debug(const char *val, const struct kernel_param *kp)
{
int ret;
struct w6692_hw *card;
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 787bda3..f931a2c 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -723,6 +723,16 @@
peripheral found on Qualcomm Technologies, Inc. QPNP PMICs.
The vibrator-ldo peripheral is capable of driving ERM vibrators.
+config LEDS_QPNP_VIBRATOR
+ tristate "Vibrator support for QPNP PMIC"
+ depends on LEDS_CLASS && MFD_SPMI_PMIC
+ help
+ This option enables device driver support for the vibrator
+ on the Qualcomm technologies Inc's QPNP PMICs. The vibrator
+ is connected on the VIB_DRV_N line and can be controlled
+ manually or by the DTEST lines.It uses the android timed-output
+ framework.
+
comment "LED Triggers"
source "drivers/leds/trigger/Kconfig"
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index e9eaa50..31d29f0 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -78,6 +78,7 @@
obj-$(CONFIG_LEDS_QPNP_WLED) += leds-qpnp-wled.o
obj-$(CONFIG_LEDS_QPNP_HAPTICS) += leds-qpnp-haptics.o
obj-$(CONFIG_LEDS_QPNP_VIBRATOR_LDO) += leds-qpnp-vibrator-ldo.o
+obj-$(CONFIG_LEDS_QPNP_VIBRATOR) += leds-qpnp-vibrator.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index 3bce448..454ed4d 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -188,6 +188,7 @@
{
del_timer_sync(&led_cdev->blink_timer);
+ led_cdev->flags &= ~LED_BLINK_SW;
led_cdev->flags &= ~LED_BLINK_ONESHOT;
led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
diff --git a/drivers/leds/leds-pm8058.c b/drivers/leds/leds-pm8058.c
index a526743..8988ba3 100644
--- a/drivers/leds/leds-pm8058.c
+++ b/drivers/leds/leds-pm8058.c
@@ -106,7 +106,7 @@
if (!led)
return -ENOMEM;
- led->ledtype = (u32)of_device_get_match_data(&pdev->dev);
+ led->ledtype = (u32)(unsigned long)of_device_get_match_data(&pdev->dev);
map = dev_get_regmap(pdev->dev.parent, NULL);
if (!map) {
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index d4b64ba..759d853 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -353,7 +353,7 @@
if (!ires_ua || !target_curr_ma || (target_curr_ma < (ires_ua / 1000)))
return 0;
- return DIV_ROUND_UP(target_curr_ma * 1000, ires_ua) - 1;
+ return DIV_ROUND_CLOSEST(target_curr_ma * 1000, ires_ua) - 1;
}
static int qpnp_flash_led_read(struct qpnp_flash_led *led, u16 addr, u8 *data)
diff --git a/drivers/leds/leds-qpnp-vibrator.c b/drivers/leds/leds-qpnp-vibrator.c
new file mode 100644
index 0000000..cc2615d
--- /dev/null
+++ b/drivers/leds/leds-qpnp-vibrator.c
@@ -0,0 +1,526 @@
+/* Copyright (c) 2013-2015, 2018, 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/regmap.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/hrtimer.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/leds.h>
+#include <linux/device.h>
+#include <linux/qpnp/pwm.h>
+
+#define QPNP_VIB_VTG_CTL(base) (base + 0x41)
+#define QPNP_VIB_EN_CTL(base) (base + 0x46)
+
+#define QPNP_VIB_MAX_LEVEL 31
+#define QPNP_VIB_MIN_LEVEL 12
+
+#define QPNP_VIB_DEFAULT_TIMEOUT 15000
+#define QPNP_VIB_DEFAULT_VTG_LVL 3100
+
+#define QPNP_VIB_EN BIT(7)
+#define QPNP_VIB_VTG_SET_MASK 0x1F
+#define QPNP_VIB_LOGIC_SHIFT 4
+
+enum qpnp_vib_mode {
+ QPNP_VIB_MANUAL,
+ QPNP_VIB_DTEST1,
+ QPNP_VIB_DTEST2,
+ QPNP_VIB_DTEST3,
+};
+
+struct qpnp_pwm_info {
+ struct pwm_device *pwm_dev;
+ u32 pwm_channel;
+ u32 duty_us;
+ u32 period_us;
+};
+
+struct qpnp_vib {
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ struct hrtimer vib_timer;
+ struct led_classdev cdev;
+ struct work_struct work;
+ struct qpnp_pwm_info pwm_info;
+ enum qpnp_vib_mode mode;
+
+ u8 reg_vtg_ctl;
+ u8 reg_en_ctl;
+ u8 active_low;
+ u16 base;
+ int state;
+ int vtg_level;
+ int timeout;
+ u32 vib_play_ms;
+ struct mutex lock;
+};
+
+static int qpnp_vib_read_u8(struct qpnp_vib *vib, u8 *data, u16 reg)
+{
+ int rc;
+
+ rc = regmap_read(vib->regmap, reg, (unsigned int *)data);
+ if (rc < 0)
+ dev_err(&vib->pdev->dev,
+ "Error reading address: %X - ret %X\n", reg, rc);
+
+ return rc;
+}
+
+static int qpnp_vib_write_u8(struct qpnp_vib *vib, u8 *data, u16 reg)
+{
+ int rc;
+
+ rc = regmap_write(vib->regmap, reg, (unsigned int)*data);
+ if (rc < 0)
+ dev_err(&vib->pdev->dev,
+ "Error writing address: %X - ret %X\n", reg, rc);
+
+ return rc;
+}
+
+static int qpnp_vibrator_config(struct qpnp_vib *vib)
+{
+ u8 reg = 0;
+ int rc;
+
+ /* Configure the VTG CTL regiser */
+ rc = qpnp_vib_read_u8(vib, ®, QPNP_VIB_VTG_CTL(vib->base));
+ if (rc < 0)
+ return rc;
+ reg &= ~QPNP_VIB_VTG_SET_MASK;
+ reg |= (vib->vtg_level & QPNP_VIB_VTG_SET_MASK);
+ rc = qpnp_vib_write_u8(vib, ®, QPNP_VIB_VTG_CTL(vib->base));
+ if (rc)
+ return rc;
+ vib->reg_vtg_ctl = reg;
+
+ /* Configure the VIB ENABLE regiser */
+ rc = qpnp_vib_read_u8(vib, ®, QPNP_VIB_EN_CTL(vib->base));
+ if (rc < 0)
+ return rc;
+ reg |= (!!vib->active_low) << QPNP_VIB_LOGIC_SHIFT;
+ if (vib->mode != QPNP_VIB_MANUAL) {
+ vib->pwm_info.pwm_dev = pwm_request(vib->pwm_info.pwm_channel,
+ "qpnp-vib");
+ if (IS_ERR_OR_NULL(vib->pwm_info.pwm_dev)) {
+ dev_err(&vib->pdev->dev, "vib pwm request failed\n");
+ return -ENODEV;
+ }
+
+ rc = pwm_config(vib->pwm_info.pwm_dev, vib->pwm_info.duty_us,
+ vib->pwm_info.period_us);
+ if (rc < 0) {
+ dev_err(&vib->pdev->dev, "vib pwm config failed\n");
+ pwm_free(vib->pwm_info.pwm_dev);
+ return -ENODEV;
+ }
+
+ reg |= BIT(vib->mode - 1);
+ }
+
+ rc = qpnp_vib_write_u8(vib, ®, QPNP_VIB_EN_CTL(vib->base));
+ if (rc < 0)
+ return rc;
+ vib->reg_en_ctl = reg;
+
+ return rc;
+}
+
+static int qpnp_vib_set(struct qpnp_vib *vib, int on)
+{
+ int rc;
+ u8 val;
+
+ if (on) {
+ if (vib->mode != QPNP_VIB_MANUAL) {
+ pwm_enable(vib->pwm_info.pwm_dev);
+ } else {
+ val = vib->reg_en_ctl;
+ val |= QPNP_VIB_EN;
+ rc = qpnp_vib_write_u8(vib, &val,
+ QPNP_VIB_EN_CTL(vib->base));
+ if (rc < 0)
+ return rc;
+ vib->reg_en_ctl = val;
+ }
+ } else {
+ if (vib->mode != QPNP_VIB_MANUAL) {
+ pwm_disable(vib->pwm_info.pwm_dev);
+ } else {
+ val = vib->reg_en_ctl;
+ val &= ~QPNP_VIB_EN;
+ rc = qpnp_vib_write_u8(vib, &val,
+ QPNP_VIB_EN_CTL(vib->base));
+ if (rc < 0)
+ return rc;
+ vib->reg_en_ctl = val;
+ }
+ }
+
+ return 0;
+}
+
+static void qpnp_vib_update(struct work_struct *work)
+{
+ struct qpnp_vib *vib = container_of(work, struct qpnp_vib,
+ work);
+ qpnp_vib_set(vib, vib->state);
+}
+
+static enum hrtimer_restart qpnp_vib_timer_func(struct hrtimer *timer)
+{
+ struct qpnp_vib *vib = container_of(timer, struct qpnp_vib,
+ vib_timer);
+
+ vib->state = 0;
+ schedule_work(&vib->work);
+
+ return HRTIMER_NORESTART;
+}
+
+#ifdef CONFIG_PM
+static int qpnp_vibrator_suspend(struct device *dev)
+{
+ struct qpnp_vib *vib = dev_get_drvdata(dev);
+
+ hrtimer_cancel(&vib->vib_timer);
+ cancel_work_sync(&vib->work);
+ /* turn-off vibrator */
+ qpnp_vib_set(vib, 0);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(qpnp_vibrator_pm_ops, qpnp_vibrator_suspend, NULL);
+
+static int qpnp_vib_parse_dt(struct qpnp_vib *vib)
+{
+ struct platform_device *pdev = vib->pdev;
+ struct device_node *node = pdev->dev.of_node;
+ const char *mode;
+ u32 temp_val;
+ int rc;
+
+ vib->timeout = QPNP_VIB_DEFAULT_TIMEOUT;
+
+ rc = of_property_read_u32(node, "reg", &temp_val);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Couldn't find reg in node = %s rc = %d\n",
+ node->full_name, rc);
+ return rc;
+ }
+ vib->base = temp_val;
+
+ rc = of_property_read_u32(node,
+ "qcom,vib-timeout-ms", &temp_val);
+ if (!rc) {
+ vib->timeout = temp_val;
+ } else if (rc != -EINVAL) {
+ dev_err(&pdev->dev, "Unable to read vib timeout\n");
+ return rc;
+ }
+
+ vib->vtg_level = QPNP_VIB_DEFAULT_VTG_LVL;
+ rc = of_property_read_u32(node,
+ "qcom,vib-vtg-level-mV", &temp_val);
+ if (!rc) {
+ vib->vtg_level = temp_val;
+ } else if (rc != -EINVAL) {
+ dev_err(&pdev->dev, "Unable to read vtg level\n");
+ return rc;
+ }
+
+ vib->vtg_level /= 100;
+ if (vib->vtg_level < QPNP_VIB_MIN_LEVEL)
+ vib->vtg_level = QPNP_VIB_MIN_LEVEL;
+ else if (vib->vtg_level > QPNP_VIB_MAX_LEVEL)
+ vib->vtg_level = QPNP_VIB_MAX_LEVEL;
+
+ vib->mode = QPNP_VIB_MANUAL;
+ rc = of_property_read_string(node, "qcom,mode", &mode);
+ if (!rc) {
+ if (strcmp(mode, "manual") == 0) {
+ vib->mode = QPNP_VIB_MANUAL;
+ } else if (strcmp(mode, "dtest1") == 0) {
+ vib->mode = QPNP_VIB_DTEST1;
+ } else if (strcmp(mode, "dtest2") == 0) {
+ vib->mode = QPNP_VIB_DTEST2;
+ } else if (strcmp(mode, "dtest3") == 0) {
+ vib->mode = QPNP_VIB_DTEST3;
+ } else {
+ dev_err(&pdev->dev, "Invalid mode\n");
+ return -EINVAL;
+ }
+ } else if (rc != -EINVAL) {
+ dev_err(&pdev->dev, "Unable to read mode\n");
+ return rc;
+ }
+
+ if (vib->mode != QPNP_VIB_MANUAL) {
+ rc = of_property_read_u32(node,
+ "qcom,pwm-channel", &temp_val);
+ if (!rc)
+ vib->pwm_info.pwm_channel = temp_val;
+ else
+ return rc;
+
+ rc = of_property_read_u32(node,
+ "qcom,period-us", &temp_val);
+ if (!rc)
+ vib->pwm_info.period_us = temp_val;
+ else
+ return rc;
+
+ rc = of_property_read_u32(node,
+ "qcom,duty-us", &temp_val);
+ if (!rc)
+ vib->pwm_info.duty_us = temp_val;
+ else
+ return rc;
+ }
+
+ vib->active_low = of_property_read_bool(node,
+ "qcom,active-low");
+
+ return 0;
+}
+
+static ssize_t qpnp_vib_get_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *cdev = dev_get_drvdata(dev);
+ struct qpnp_vib *chip = container_of(cdev, struct qpnp_vib, cdev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", !!chip->reg_en_ctl);
+}
+
+static ssize_t qpnp_vib_get_duration(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *cdev = dev_get_drvdata(dev);
+ struct qpnp_vib *vib = container_of(cdev, struct qpnp_vib, cdev);
+ ktime_t time_rem;
+ s64 time_us = 0;
+
+ if (hrtimer_active(&vib->vib_timer)) {
+ time_rem = hrtimer_get_remaining(&vib->vib_timer);
+ time_us = ktime_to_us(time_rem);
+ }
+
+ return snprintf(buf, PAGE_SIZE, "%lld\n", div_s64(time_us, 1000));
+}
+
+
+static ssize_t qpnp_vib_set_duration(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct led_classdev *cdev = dev_get_drvdata(dev);
+ struct qpnp_vib *vib = container_of(cdev, struct qpnp_vib, cdev);
+ u32 value;
+ int rc;
+
+ rc = kstrtouint(buf, 0, &value);
+ if (rc < 0)
+ return rc;
+
+ /* setting 0 on duration is NOP for now */
+ if (value <= 0)
+ return count;
+
+ if (value > vib->timeout)
+ value = vib->timeout;
+
+ mutex_lock(&vib->lock);
+ vib->vib_play_ms = value;
+ mutex_unlock(&vib->lock);
+ return count;
+}
+
+static ssize_t qpnp_vib_get_activate(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *cdev = dev_get_drvdata(dev);
+ struct qpnp_vib *vib = container_of(cdev, struct qpnp_vib, cdev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", vib->state);
+}
+
+static ssize_t qpnp_vib_set_activate(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct led_classdev *cdev = dev_get_drvdata(dev);
+ struct qpnp_vib *vib = container_of(cdev, struct qpnp_vib, cdev);
+ u32 value;
+ int rc;
+
+ rc = kstrtouint(buf, 0, &value);
+ if (rc < 0)
+ return rc;
+
+ if (value != 0 && value != 1)
+ return count;
+
+ vib->state = value;
+ pr_debug("state = %d, time = %ums\n", vib->state,
+ vib->vib_play_ms);
+ mutex_lock(&vib->lock);
+ if (vib->state) {
+ hrtimer_cancel(&vib->vib_timer);
+ hrtimer_start(&vib->vib_timer,
+ ktime_set(vib->vib_play_ms / 1000,
+ (vib->vib_play_ms % 1000) * 1000000),
+ HRTIMER_MODE_REL);
+ }
+ vib->vib_play_ms = 0;
+ mutex_unlock(&vib->lock);
+ schedule_work(&vib->work);
+
+ return count;
+}
+
+static struct device_attribute qpnp_vib_attrs[] = {
+ __ATTR(state, 0444, qpnp_vib_get_state, NULL),
+ __ATTR(duration, 0664, qpnp_vib_get_duration, qpnp_vib_set_duration),
+ __ATTR(activate, 0664, qpnp_vib_get_activate, qpnp_vib_set_activate),
+};
+
+/* Dummy functions for brightness */
+static
+enum led_brightness qpnp_brightness_get(struct led_classdev *cdev)
+{
+ return 0;
+}
+
+static void qpnp_brightness_set(struct led_classdev *cdev,
+ enum led_brightness level)
+{
+
+}
+
+static int qpnp_vibrator_probe(struct platform_device *pdev)
+{
+ struct qpnp_vib *vib;
+ int rc;
+ int i;
+
+ vib = devm_kzalloc(&pdev->dev, sizeof(*vib), GFP_KERNEL);
+ if (!vib)
+ return -ENOMEM;
+
+ vib->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!vib->regmap) {
+ dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+ return -EINVAL;
+ }
+
+ vib->pdev = pdev;
+
+ rc = qpnp_vib_parse_dt(vib);
+ if (rc) {
+ dev_err(&pdev->dev, "DT parsing failed\n");
+ return rc;
+ }
+
+ rc = qpnp_vibrator_config(vib);
+ if (rc) {
+ dev_err(&pdev->dev, "vib config failed\n");
+ return rc;
+ }
+
+ mutex_init(&vib->lock);
+ INIT_WORK(&vib->work, qpnp_vib_update);
+
+ hrtimer_init(&vib->vib_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ vib->vib_timer.function = qpnp_vib_timer_func;
+
+ vib->cdev.name = "vibrator";
+ vib->cdev.brightness_get = qpnp_brightness_get;
+ vib->cdev.brightness_set = qpnp_brightness_set;
+ vib->cdev.max_brightness = 100;
+ rc = devm_led_classdev_register(&pdev->dev, &vib->cdev);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "Error in registering led class device, rc=%d\n",
+ rc);
+ goto led_reg_fail;
+ }
+
+ /* Enabling sysfs entries */
+ for (i = 0; i < ARRAY_SIZE(qpnp_vib_attrs); i++) {
+ rc = sysfs_create_file(&vib->cdev.dev->kobj,
+ &qpnp_vib_attrs[i].attr);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "Error in creating sysfs file, rc=%d\n",
+ rc);
+ goto sysfs_fail;
+ }
+ }
+
+ dev_set_drvdata(&pdev->dev, vib);
+ return rc;
+
+sysfs_fail:
+ dev_set_drvdata(&pdev->dev, NULL);
+led_reg_fail:
+ hrtimer_cancel(&vib->vib_timer);
+ cancel_work_sync(&vib->work);
+ mutex_destroy(&vib->lock);
+ return -EINVAL;
+}
+
+static int qpnp_vibrator_remove(struct platform_device *pdev)
+{
+ struct qpnp_vib *vib = dev_get_drvdata(&pdev->dev);
+ int i;
+
+/* Removing sysfs entries */
+ for (i = 0; i < ARRAY_SIZE(qpnp_vib_attrs); i++)
+ sysfs_remove_file(&vib->cdev.dev->kobj,
+ &qpnp_vib_attrs[i].attr);
+
+ dev_set_drvdata(&pdev->dev, NULL);
+ hrtimer_cancel(&vib->vib_timer);
+ cancel_work_sync(&vib->work);
+ mutex_destroy(&vib->lock);
+ return 0;
+}
+
+static const struct of_device_id spmi_match_table[] = {
+ { .compatible = "qcom,qpnp-vibrator",
+ },
+ {}
+};
+
+static struct platform_driver qpnp_vibrator_driver = {
+ .driver = {
+ .name = "qcom,qpnp-vibrator",
+ .of_match_table = spmi_match_table,
+ .pm = &qpnp_vibrator_pm_ops,
+ },
+ .probe = qpnp_vibrator_probe,
+ .remove = qpnp_vibrator_remove,
+};
+
+module_platform_driver(qpnp_vibrator_driver);
+
+MODULE_DESCRIPTION("qpnp vibrator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index e44816f..28864ad 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -937,6 +937,7 @@
uint32_t rtime = cpu_to_le32(get_seconds());
struct uuid_entry *u;
char buf[BDEVNAME_SIZE];
+ struct cached_dev *exist_dc, *t;
bdevname(dc->bdev, buf);
@@ -960,6 +961,16 @@
return -EINVAL;
}
+ /* Check whether already attached */
+ list_for_each_entry_safe(exist_dc, t, &c->cached_devs, list) {
+ if (!memcmp(dc->sb.uuid, exist_dc->sb.uuid, 16)) {
+ pr_err("Tried to attach %s but duplicate UUID already attached",
+ buf);
+
+ return -EINVAL;
+ }
+ }
+
u = uuid_find(c, dc->sb.uuid);
if (u &&
@@ -1182,7 +1193,7 @@
return;
err:
- pr_notice("error opening %s: %s", bdevname(bdev, name), err);
+ pr_notice("error %s: %s", bdevname(bdev, name), err);
bcache_device_stop(&dc->disk);
}
@@ -1853,6 +1864,8 @@
const char *err = NULL; /* must be set for any error case */
int ret = 0;
+ bdevname(bdev, name);
+
memcpy(&ca->sb, sb, sizeof(struct cache_sb));
ca->bdev = bdev;
ca->bdev->bd_holder = ca;
@@ -1863,11 +1876,12 @@
ca->sb_bio.bi_io_vec[0].bv_page = sb_page;
get_page(sb_page);
- if (blk_queue_discard(bdev_get_queue(ca->bdev)))
+ if (blk_queue_discard(bdev_get_queue(bdev)))
ca->discard = CACHE_DISCARD(&ca->sb);
ret = cache_alloc(ca);
if (ret != 0) {
+ blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);
if (ret == -ENOMEM)
err = "cache_alloc(): -ENOMEM";
else
@@ -1890,14 +1904,14 @@
goto out;
}
- pr_info("registered cache device %s", bdevname(bdev, name));
+ pr_info("registered cache device %s", name);
out:
kobject_put(&ca->kobj);
err:
if (err)
- pr_notice("error opening %s: %s", bdevname(bdev, name), err);
+ pr_notice("error %s: %s", name, err);
return ret;
}
@@ -1986,6 +2000,7 @@
if (err)
goto err_close;
+ err = "failed to register device";
if (SB_IS_BDEV(sb)) {
struct cached_dev *dc = kzalloc(sizeof(*dc), GFP_KERNEL);
if (!dc)
@@ -2000,7 +2015,7 @@
goto err_close;
if (register_cache(sb, sb_page, bdev, ca) != 0)
- goto err_close;
+ goto err;
}
out:
if (sb_page)
@@ -2013,7 +2028,7 @@
err_close:
blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);
err:
- pr_info("error opening %s: %s", path, err);
+ pr_info("error %s: %s", path, err);
ret = -EINVAL;
goto out;
}
diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c
index 0bf1a12..ee6045d 100644
--- a/drivers/md/dm-io.c
+++ b/drivers/md/dm-io.c
@@ -302,6 +302,7 @@
special_cmd_max_sectors = q->limits.max_write_same_sectors;
if ((op == REQ_OP_DISCARD || op == REQ_OP_WRITE_SAME) &&
special_cmd_max_sectors == 0) {
+ atomic_inc(&io->count);
dec_count(io, region, -EOPNOTSUPP);
return;
}
diff --git a/drivers/md/md.c b/drivers/md/md.c
index df7d606..93059dd 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -4826,8 +4826,10 @@
return err;
/* cluster raid doesn't support change array_sectors */
- if (mddev_is_clustered(mddev))
+ if (mddev_is_clustered(mddev)) {
+ mddev_unlock(mddev);
return -EINVAL;
+ }
if (strncmp(buf, "default", 7) == 0) {
if (mddev->pers)
@@ -5095,7 +5097,7 @@
return NULL;
}
-static int add_named_array(const char *val, struct kernel_param *kp)
+static int add_named_array(const char *val, const struct kernel_param *kp)
{
/* val must be "md_*" where * is not all digits.
* We allocate an array with a large free minor number, and
@@ -8224,6 +8226,10 @@
int removed = 0;
bool remove_some = false;
+ if (this && test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
+ /* Mustn't remove devices when resync thread is running */
+ return 0;
+
rdev_for_each(rdev, mddev) {
if ((this == NULL || rdev == this) &&
rdev->raid_disk >= 0 &&
@@ -8965,11 +8971,11 @@
subsys_initcall(md_init);
module_exit(md_exit)
-static int get_ro(char *buffer, struct kernel_param *kp)
+static int get_ro(char *buffer, const struct kernel_param *kp)
{
return sprintf(buffer, "%d", start_readonly);
}
-static int set_ro(const char *val, struct kernel_param *kp)
+static int set_ro(const char *val, const struct kernel_param *kp)
{
return kstrtouint(val, 10, (unsigned int *)&start_readonly);
}
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 8d40238..c17c882 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -2704,6 +2704,11 @@
list_add(&r10_bio->retry_list, &conf->bio_end_io_list);
conf->nr_queued++;
spin_unlock_irq(&conf->device_lock);
+ /*
+ * In case freeze_array() is waiting for condition
+ * nr_pending == nr_queued + extra to be true.
+ */
+ wake_up(&conf->wait_barrier);
md_wakeup_thread(conf->mddev->thread);
} else {
if (test_bit(R10BIO_WriteError,
@@ -4084,6 +4089,7 @@
diff = 0;
if (first || diff < min_offset_diff)
min_offset_diff = diff;
+ first = 0;
}
}
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 90d863e..edf37fd 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -3391,9 +3391,20 @@
BUG_ON(test_bit(R5_Wantcompute, &dev->flags));
BUG_ON(test_bit(R5_Wantread, &dev->flags));
BUG_ON(sh->batch_head);
+
+ /*
+ * In the raid6 case if the only non-uptodate disk is P
+ * then we already trusted P to compute the other failed
+ * drives. It is safe to compute rather than re-read P.
+ * In other cases we only compute blocks from failed
+ * devices, otherwise check/repair might fail to detect
+ * a real inconsistency.
+ */
+
if ((s->uptodate == disks - 1) &&
+ ((sh->qd_idx >= 0 && sh->pd_idx == disk_idx) ||
(s->failed && (disk_idx == s->failed_num[0] ||
- disk_idx == s->failed_num[1]))) {
+ disk_idx == s->failed_num[1])))) {
/* have disk failed, and we're requested to fetch it;
* do compute it
*/
diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c
index b5b5b19..42ce88c 100644
--- a/drivers/media/dvb-core/dvb_ca_en50221.c
+++ b/drivers/media/dvb-core/dvb_ca_en50221.c
@@ -779,6 +779,29 @@
goto exit;
}
+ /*
+ * It may need some time for the CAM to settle down, or there might
+ * be a race condition between the CAM, writing HC and our last
+ * check for DA. This happens, if the CAM asserts DA, just after
+ * checking DA before we are setting HC. In this case it might be
+ * a bug in the CAM to keep the FR bit, the lower layer/HW
+ * communication requires a longer timeout or the CAM needs more
+ * time internally. But this happens in reality!
+ * We need to read the status from the HW again and do the same
+ * we did for the previous check for DA
+ */
+ status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
+ if (status < 0)
+ goto exit;
+
+ if (status & (STATUSREG_DA | STATUSREG_RE)) {
+ if (status & STATUSREG_DA)
+ dvb_ca_en50221_thread_wakeup(ca);
+
+ status = -EAGAIN;
+ goto exit;
+ }
+
/* send the amount of data */
if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH, bytes_write >> 8)) != 0)
goto exit;
diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index e0fe5bc..31f1610 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -1262,11 +1262,12 @@
* New users must use I2C client binding directly!
*/
struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg,
- struct i2c_adapter *i2c, struct i2c_adapter **tuner_i2c_adapter)
+ struct i2c_adapter *i2c,
+ struct i2c_adapter **tuner_i2c_adapter)
{
struct i2c_client *client;
struct i2c_board_info board_info;
- struct m88ds3103_platform_data pdata;
+ struct m88ds3103_platform_data pdata = {};
pdata.clk = cfg->clock;
pdata.i2c_wr_max = cfg->i2c_wr_max;
@@ -1409,6 +1410,8 @@
case M88DS3103_CHIP_ID:
break;
default:
+ ret = -ENODEV;
+ dev_err(&client->dev, "Unknown device. Chip_id=%02x\n", dev->chip_id);
goto err_kfree;
}
diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c
index 20b4a65..0fe69f7 100644
--- a/drivers/media/dvb-frontends/si2168.c
+++ b/drivers/media/dvb-frontends/si2168.c
@@ -14,6 +14,8 @@
* GNU General Public License for more details.
*/
+#include <linux/delay.h>
+
#include "si2168_priv.h"
static const struct dvb_frontend_ops si2168_ops;
@@ -378,6 +380,7 @@
if (ret)
goto err;
+ udelay(100);
memcpy(cmd.args, "\x85", 1);
cmd.wlen = 1;
cmd.rlen = 1;
diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c
index 4bf2995..8f85910 100644
--- a/drivers/media/i2c/soc_camera/ov6650.c
+++ b/drivers/media/i2c/soc_camera/ov6650.c
@@ -1033,7 +1033,7 @@
priv->code = MEDIA_BUS_FMT_YUYV8_2X8;
priv->colorspace = V4L2_COLORSPACE_JPEG;
- priv->clk = v4l2_clk_get(&client->dev, "mclk");
+ priv->clk = v4l2_clk_get(&client->dev, NULL);
if (IS_ERR(priv->clk)) {
ret = PTR_ERR(priv->clk);
goto eclkget;
diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c
index 26d999c..0f572bf 100644
--- a/drivers/media/i2c/tc358743.c
+++ b/drivers/media/i2c/tc358743.c
@@ -222,7 +222,7 @@
static void i2c_wr8_and_or(struct v4l2_subdev *sd, u16 reg,
u8 mask, u8 val)
{
- i2c_wrreg(sd, reg, (i2c_rdreg(sd, reg, 2) & mask) | val, 2);
+ i2c_wrreg(sd, reg, (i2c_rdreg(sd, reg, 1) & mask) | val, 1);
}
static u16 i2c_rd16(struct v4l2_subdev *sd, u16 reg)
diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index 4462d8c..cc8de56 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -58,9 +58,10 @@
return 0;
}
-static int media_device_get_info(struct media_device *dev,
- struct media_device_info *info)
+static long media_device_get_info(struct media_device *dev, void *arg)
{
+ struct media_device_info *info = (struct media_device_info *)arg;
+
memset(info, 0, sizeof(*info));
if (dev->driver_name[0])
@@ -97,9 +98,9 @@
return NULL;
}
-static long media_device_enum_entities(struct media_device *mdev,
- struct media_entity_desc *entd)
+static long media_device_enum_entities(struct media_device *mdev, void *arg)
{
+ struct media_entity_desc *entd = (struct media_entity_desc *)arg;
struct media_entity *ent;
ent = find_entity(mdev, entd->id);
@@ -150,9 +151,9 @@
upad->flags = kpad->flags;
}
-static long media_device_enum_links(struct media_device *mdev,
- struct media_links_enum *links)
+static long media_device_enum_links(struct media_device *mdev, void *arg)
{
+ struct media_links_enum *links = (struct media_links_enum *)arg;
struct media_entity *entity;
entity = find_entity(mdev, links->entity);
@@ -198,9 +199,9 @@
return 0;
}
-static long media_device_setup_link(struct media_device *mdev,
- struct media_link_desc *linkd)
+static long media_device_setup_link(struct media_device *mdev, void *arg)
{
+ struct media_link_desc *linkd = (struct media_link_desc *)arg;
struct media_link *link = NULL;
struct media_entity *source;
struct media_entity *sink;
@@ -226,9 +227,9 @@
return __media_entity_setup_link(link, linkd->flags);
}
-static long media_device_get_topology(struct media_device *mdev,
- struct media_v2_topology *topo)
+static long media_device_get_topology(struct media_device *mdev, void *arg)
{
+ struct media_v2_topology *topo = (struct media_v2_topology *)arg;
struct media_entity *entity;
struct media_interface *intf;
struct media_pad *pad;
diff --git a/drivers/media/pci/bt8xx/bt878.c b/drivers/media/pci/bt8xx/bt878.c
index 8aa7266..90fcccc 100644
--- a/drivers/media/pci/bt8xx/bt878.c
+++ b/drivers/media/pci/bt8xx/bt878.c
@@ -422,8 +422,7 @@
bt878_num);
if (bt878_num >= BT878_MAX) {
printk(KERN_ERR "bt878: Too many devices inserted\n");
- result = -ENOMEM;
- goto fail0;
+ return -ENOMEM;
}
if (pci_enable_device(dev))
return -EIO;
diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2.c b/drivers/media/pci/solo6x10/solo6x10-v4l2.c
index b4be479..e17d6b9 100644
--- a/drivers/media/pci/solo6x10/solo6x10-v4l2.c
+++ b/drivers/media/pci/solo6x10/solo6x10-v4l2.c
@@ -341,6 +341,17 @@
struct solo_dev *solo_dev = vb2_get_drv_priv(q);
solo_stop_thread(solo_dev);
+
+ spin_lock(&solo_dev->slock);
+ while (!list_empty(&solo_dev->vidq_active)) {
+ struct solo_vb2_buf *buf = list_entry(
+ solo_dev->vidq_active.next,
+ struct solo_vb2_buf, list);
+
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock(&solo_dev->slock);
INIT_LIST_HEAD(&solo_dev->vidq_active);
}
diff --git a/drivers/media/pci/tw686x/tw686x-core.c b/drivers/media/pci/tw686x/tw686x-core.c
index 71a0453..279d447 100644
--- a/drivers/media/pci/tw686x/tw686x-core.c
+++ b/drivers/media/pci/tw686x/tw686x-core.c
@@ -72,12 +72,12 @@
}
}
-static int tw686x_dma_mode_get(char *buffer, struct kernel_param *kp)
+static int tw686x_dma_mode_get(char *buffer, const struct kernel_param *kp)
{
return sprintf(buffer, dma_mode_name(dma_mode));
}
-static int tw686x_dma_mode_set(const char *val, struct kernel_param *kp)
+static int tw686x_dma_mode_set(const char *val, const struct kernel_param *kp)
{
if (!strcasecmp(val, dma_mode_name(TW686X_DMA_MODE_MEMCPY)))
dma_mode = TW686X_DMA_MODE_MEMCPY;
diff --git a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_intf.h b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_intf.h
index f556780..2c364e01 100644
--- a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_intf.h
+++ b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_intf.h
@@ -121,6 +121,12 @@
int hfi_set_debug_level(u64 a5_dbg_type, uint32_t lvl);
/**
+ * hfi_set_fw_dump_level() - set firmware dump level
+ * @lvl: level of firmware dump level
+ */
+int hfi_set_fw_dump_level(uint32_t lvl);
+
+/**
* hfi_enable_ipe_bps_pc() - Enable interframe pc
* Host sends a command to firmware to enable interframe
* power collapse for IPE and BPS hardware.
diff --git a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_sys_defs.h b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_sys_defs.h
index 91190b6..311886f 100644
--- a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_sys_defs.h
+++ b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_sys_defs.h
@@ -160,6 +160,7 @@
#define HFI_PROP_SYS_IMAGE_VER (HFI_PROPERTY_ICP_COMMON_START + 0x3)
#define HFI_PROP_SYS_SUPPORTED (HFI_PROPERTY_ICP_COMMON_START + 0x4)
#define HFI_PROP_SYS_IPEBPS_PC (HFI_PROPERTY_ICP_COMMON_START + 0x5)
+#define HFI_PROP_SYS_FW_DUMP_CFG (HFI_PROPERTY_ICP_COMMON_START + 0x8)
/* Capabilities reported at sys init */
#define HFI_CAPS_PLACEHOLDER_1 (HFI_COMMON_BASE + 0x1)
@@ -180,6 +181,18 @@
/* Disable ARM9 watchdog. */
#define HFI_DEBUG_CFG_ARM9WD 0x10000000
+
+/*
+ * HFI_FW_DUMP levels
+ * HFI_FW_DUMP_xx
+ */
+#define HFI_FW_DUMP_DISABLED 0x00000000
+#define HFI_FW_DUMP_ON_FAILURE 0x00000001
+#define HFI_FW_DUMP_ALWAYS 0x00000002
+
+/* Number of available dump levels. */
+#define NUM_HFI_DUMP_LVL 0x00000003
+
/* Debug Msg Communication types:
* Section describes different modes (HFI_DEBUG_MODE_X)
* available to communicate the debug messages
diff --git a/drivers/media/platform/msm/camera/cam_icp/hfi.c b/drivers/media/platform/msm/camera/cam_icp/hfi.c
index b75719b..a0752f5 100644
--- a/drivers/media/platform/msm/camera/cam_icp/hfi.c
+++ b/drivers/media/platform/msm/camera/cam_icp/hfi.c
@@ -324,6 +324,42 @@
return 0;
}
+int hfi_set_fw_dump_level(uint32_t lvl)
+{
+ uint8_t *prop = NULL;
+ struct hfi_cmd_prop *fw_dump_level_switch_prop = NULL;
+ uint32_t size = 0;
+
+ CAM_DBG(CAM_HFI, "fw dump ENTER");
+
+ size = sizeof(struct hfi_cmd_prop) + sizeof(lvl);
+ prop = kzalloc(size, GFP_KERNEL);
+ if (!prop)
+ return -ENOMEM;
+
+ fw_dump_level_switch_prop = (struct hfi_cmd_prop *)prop;
+ fw_dump_level_switch_prop->size = size;
+ fw_dump_level_switch_prop->pkt_type = HFI_CMD_SYS_SET_PROPERTY;
+ fw_dump_level_switch_prop->num_prop = 1;
+ fw_dump_level_switch_prop->prop_data[0] = HFI_PROP_SYS_FW_DUMP_CFG;
+ fw_dump_level_switch_prop->prop_data[1] = lvl;
+
+ CAM_DBG(CAM_HFI, "prop->size = %d\n"
+ "prop->pkt_type = %d\n"
+ "prop->num_prop = %d\n"
+ "prop->prop_data[0] = %d\n"
+ "prop->prop_data[1] = %d\n",
+ fw_dump_level_switch_prop->size,
+ fw_dump_level_switch_prop->pkt_type,
+ fw_dump_level_switch_prop->num_prop,
+ fw_dump_level_switch_prop->prop_data[0],
+ fw_dump_level_switch_prop->prop_data[1]);
+
+ hfi_write_cmd(prop);
+ kfree(prop);
+ return 0;
+}
+
void hfi_send_system_cmd(uint32_t type, uint64_t data, uint32_t size)
{
switch (type) {
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
index ba4385f5..16e97ea 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
@@ -1285,6 +1285,22 @@
DEFINE_SIMPLE_ATTRIBUTE(cam_icp_debug_type_fs, cam_icp_get_a5_dbg_type,
cam_icp_set_a5_dbg_type, "%08llu");
+static int cam_icp_set_a5_fw_dump_lvl(void *data, u64 val)
+{
+ if (val < NUM_HFI_DUMP_LVL)
+ icp_hw_mgr.a5_fw_dump_lvl = val;
+ return 0;
+}
+
+static int cam_icp_get_a5_fw_dump_lvl(void *data, u64 *val)
+{
+ *val = icp_hw_mgr.a5_fw_dump_lvl;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cam_icp_debug_fw_dump, cam_icp_get_a5_fw_dump_lvl,
+ cam_icp_set_a5_fw_dump_lvl, "%08llu");
+
static int cam_icp_hw_mgr_create_debugfs_entry(void)
{
int rc = 0;
@@ -1335,7 +1351,7 @@
0644,
icp_hw_mgr.dentry,
NULL, &cam_icp_debug_type_fs)) {
- CAM_ERR(CAM_ICP, "failed to create a5_debug_type\n");
+ CAM_ERR(CAM_ICP, "failed to create a5_debug_type");
rc = -ENOMEM;
goto err;
}
@@ -1344,7 +1360,16 @@
0644,
icp_hw_mgr.dentry,
NULL, &cam_icp_debug_fs)) {
- CAM_ERR(CAM_ICP, "failed to create a5_dbg_lvl\n");
+ CAM_ERR(CAM_ICP, "failed to create a5_dbg_lvl");
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ if (!debugfs_create_file("a5_fw_dump_lvl",
+ 0644,
+ icp_hw_mgr.dentry,
+ NULL, &cam_icp_debug_fw_dump)) {
+ CAM_ERR(CAM_ICP, "failed to create a5_fw_dump_lvl");
rc = -ENOMEM;
goto err;
}
@@ -2260,13 +2285,7 @@
unsigned long rem_jiffies;
size_t packet_size;
int timeout = 100;
- struct hfi_cmd_work_data *task_data;
struct hfi_cmd_ipebps_async *abort_cmd;
- struct crm_workq_task *task;
-
- task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work);
- if (!task)
- return -ENOMEM;
packet_size =
sizeof(struct hfi_cmd_ipebps_async) +
@@ -2292,13 +2311,7 @@
abort_cmd->user_data1 = (uint64_t)ctx_data;
abort_cmd->user_data2 = (uint64_t)0x0;
- task_data = (struct hfi_cmd_work_data *)task->payload;
- task_data->data = (void *)abort_cmd;
- task_data->request_id = 0;
- task_data->type = ICP_WORKQ_TASK_CMD_TYPE;
- task->process_cb = cam_icp_mgr_process_cmd;
- rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr,
- CRM_TASK_PRIORITY_0);
+ rc = hfi_write_cmd(abort_cmd);
if (rc) {
kfree(abort_cmd);
return rc;
@@ -2312,6 +2325,7 @@
CAM_ERR(CAM_ICP, "FW timeout/err in abort handle command");
}
+ kfree(abort_cmd);
return rc;
}
@@ -2322,13 +2336,7 @@
int timeout = 100;
unsigned long rem_jiffies;
size_t packet_size;
- struct hfi_cmd_work_data *task_data;
struct hfi_cmd_ipebps_async *destroy_cmd;
- struct crm_workq_task *task;
-
- task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work);
- if (!task)
- return -ENOMEM;
packet_size =
sizeof(struct hfi_cmd_ipebps_async) +
@@ -2355,13 +2363,7 @@
memcpy(destroy_cmd->payload.direct, &ctx_data->temp_payload,
sizeof(uint64_t));
- task_data = (struct hfi_cmd_work_data *)task->payload;
- task_data->data = (void *)destroy_cmd;
- task_data->request_id = 0;
- task_data->type = ICP_WORKQ_TASK_CMD_TYPE;
- task->process_cb = cam_icp_mgr_process_cmd;
- rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr,
- CRM_TASK_PRIORITY_0);
+ rc = hfi_write_cmd(destroy_cmd);
if (rc) {
kfree(destroy_cmd);
return rc;
@@ -2378,6 +2380,7 @@
HFI_DEBUG_MODE_QUEUE)
cam_icp_mgr_process_dbg_buf();
}
+ kfree(destroy_cmd);
return rc;
}
@@ -3866,6 +3869,8 @@
hfi_set_debug_level(icp_hw_mgr.a5_debug_type,
icp_hw_mgr.a5_dbg_lvl);
+ hfi_set_fw_dump_level(icp_hw_mgr.a5_fw_dump_lvl);
+
rc = cam_icp_send_ubwc_cfg(hw_mgr);
if (rc)
goto ubwc_cfg_failed;
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
index c94550d..8746ee2 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
@@ -279,6 +279,7 @@
* @a5_jtag_debug: entry to enable A5 JTAG debugging
* @a5_debug_type : entry to enable FW debug message/qdss
* @a5_dbg_lvl : debug level set to FW.
+ * @a5_fw_dump_lvl : level set for dumping the FW data
* @ipe0_enable: Flag for IPE0
* @ipe1_enable: Flag for IPE1
* @bps_enable: Flag for BPS
@@ -325,6 +326,7 @@
bool a5_jtag_debug;
u64 a5_debug_type;
u64 a5_dbg_lvl;
+ u64 a5_fw_dump_lvl;
bool ipe0_enable;
bool ipe1_enable;
bool bps_enable;
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
index be4db8a..3c37b83 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
@@ -106,6 +106,7 @@
struct cam_vfe_bus_irq_evt_payload evt_payload[
CAM_VFE_BUS_VER2_PAYLOAD_MAX];
struct list_head free_payload_list;
+ spinlock_t spin_lock;
struct mutex bus_mutex;
uint32_t secure_mode;
uint32_t num_sec_out;
@@ -214,16 +215,23 @@
struct cam_vfe_bus_ver2_common_data *common_data,
struct cam_vfe_bus_irq_evt_payload **evt_payload)
{
+ int rc;
+
+ spin_lock(&common_data->spin_lock);
if (list_empty(&common_data->free_payload_list)) {
*evt_payload = NULL;
CAM_ERR_RATE_LIMIT(CAM_ISP, "No free payload");
- return -ENODEV;
+ rc = -ENODEV;
+ goto done;
}
*evt_payload = list_first_entry(&common_data->free_payload_list,
struct cam_vfe_bus_irq_evt_payload, list);
list_del_init(&(*evt_payload)->list);
- return 0;
+ rc = 0;
+done:
+ spin_unlock(&common_data->spin_lock);
+ return rc;
}
static enum cam_vfe_bus_comp_grp_id
@@ -254,6 +262,7 @@
struct cam_vfe_bus_ver2_common_data *common_data = NULL;
uint32_t *ife_irq_regs = NULL;
uint32_t status_reg0, status_reg1, status_reg2;
+ unsigned long flags;
if (!core_info) {
CAM_ERR(CAM_ISP, "Invalid param core_info NULL");
@@ -276,8 +285,12 @@
}
common_data = core_info;
+
+ spin_lock_irqsave(&common_data->spin_lock, flags);
list_add_tail(&(*evt_payload)->list,
&common_data->free_payload_list);
+ spin_unlock_irqrestore(&common_data->spin_lock, flags);
+
*evt_payload = NULL;
CAM_DBG(CAM_ISP, "Done");
@@ -2943,6 +2956,7 @@
}
}
+ spin_lock_init(&bus_priv->common_data.spin_lock);
INIT_LIST_HEAD(&bus_priv->common_data.free_payload_list);
for (i = 0; i < CAM_VFE_BUS_VER2_PAYLOAD_MAX; i++) {
INIT_LIST_HEAD(&bus_priv->common_data.evt_payload[i].list);
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
index aaba481..65052e1 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
@@ -230,7 +230,7 @@
traverse_data->in_q, curr_idx);
apply_data[tbl->pd].idx = curr_idx;
- CAM_DBG(CAM_CRM, "req_id: %d with pd of %d",
+ CAM_DBG(CAM_CRM, "req_id: %lld with pd of %d",
apply_data[tbl->pd].req_id,
apply_data[tbl->pd].pd);
/*
@@ -248,6 +248,11 @@
}
} else {
/* This pd table is not ready to proceed with asked idx */
+ CAM_INFO(CAM_CRM,
+ "Skip Frame: req: %lld not ready pd: %d open_req count: %d",
+ CRM_GET_REQ_ID(traverse_data->in_q, curr_idx),
+ tbl->pd,
+ traverse_data->open_req_cnt);
SET_FAILURE_BIT(traverse_data->result, tbl->pd);
return -EAGAIN;
}
@@ -450,6 +455,9 @@
rc = dev->ops->apply_req(&apply_req);
if (rc < 0)
break;
+
+ if (pd == link->max_delay)
+ link->open_req_cnt--;
}
}
}
@@ -460,6 +468,7 @@
for (; i >= 0; i--) {
dev = &link->l_dev[i];
evt_data.evt_type = CAM_REQ_MGR_LINK_EVT_ERR;
+ evt_data.dev_hdl = dev->dev_hdl;
evt_data.link_hdl = link->link_hdl;
evt_data.req_id = apply_req.request_id;
evt_data.u.error = CRM_KMD_ERR_BUBBLE;
@@ -509,6 +518,7 @@
traverse_data.result = 0;
traverse_data.validate_only = validate_only;
traverse_data.self_link = self_link;
+ traverse_data.open_req_cnt = link->open_req_cnt;
/*
* Traverse through all pd tables, if result is success,
* apply the settings
@@ -1513,6 +1523,7 @@
slot->sync_mode = sched_req->sync_mode;
slot->skip_idx = 0;
slot->recover = sched_req->bubble_enable;
+ link->open_req_cnt++;
__cam_req_mgr_inc_idx(&in_q->wr_idx, 1, in_q->num_slots);
mutex_unlock(&link->req.lock);
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
index e15b3b0..5ad23b7 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
@@ -134,6 +134,7 @@
* @validate_only : Whether to validate only and/or update settings
* @self_link : To indicate whether the check is for the given link or the
* other sync link
+ * @open_req_cnt : Count of open requests yet to be serviced in the kernel.
*/
struct cam_req_mgr_traverse {
int32_t idx;
@@ -143,6 +144,7 @@
struct cam_req_mgr_req_queue *in_q;
bool validate_only;
bool self_link;
+ int32_t open_req_cnt;
};
/**
@@ -301,6 +303,8 @@
* @sync_link_sof_skip : flag determines if a pkt is not available for a given
* frame in a particular link skip corresponding
* frame in sync link as well.
+ * @open_req_cnt : Counter to keep track of open requests that are yet
+ * to be serviced in the kernel.
*
*/
struct cam_req_mgr_core_link {
@@ -324,6 +328,7 @@
int64_t sync_self_ref;
bool frame_skip_flag;
bool sync_link_sof_skip;
+ int32_t open_req_cnt;
};
/**
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
index 56cb49a..2f74765 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
@@ -636,6 +636,12 @@
return -EINVAL;
}
+ if (cmd->handle_type != CAM_HANDLE_USER_POINTER) {
+ CAM_ERR(CAM_ACTUATOR, "Invalid handle type: %d",
+ cmd->handle_type);
+ return -EINVAL;
+ }
+
CAM_DBG(CAM_ACTUATOR, "Opcode to Actuator: %d", cmd->op_code);
mutex_lock(&(a_ctrl->actuator_mutex));
@@ -769,6 +775,7 @@
rc = cam_actuator_i2c_pkt_parse(a_ctrl, arg);
if (rc < 0) {
CAM_ERR(CAM_ACTUATOR, "Failed in actuator Parsing");
+ goto release_mutex;
}
if (a_ctrl->setting_apply_state ==
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
index e978a40..dbbac08 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
@@ -438,6 +438,12 @@
return -EINVAL;
}
+ if (cmd->handle_type != CAM_HANDLE_USER_POINTER) {
+ CAM_ERR(CAM_CSIPHY, "Invalid handle type: %d",
+ cmd->handle_type);
+ return -EINVAL;
+ }
+
CAM_DBG(CAM_CSIPHY, "Opcode received: %d", cmd->op_code);
mutex_lock(&csiphy_dev->mutex);
switch (cmd->op_code) {
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
index b0fbead..30b9d96 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -707,11 +707,6 @@
ioctl_ctrl = (struct cam_control *)arg;
- if (ioctl_ctrl->handle_type != CAM_HANDLE_USER_POINTER) {
- CAM_ERR(CAM_EEPROM, "Invalid Handle Type");
- return -EINVAL;
- }
-
if (copy_from_user(&dev_config, (void __user *) ioctl_ctrl->handle,
sizeof(dev_config)))
return -EFAULT;
@@ -852,8 +847,14 @@
struct cam_eeprom_query_cap_t eeprom_cap = {0};
struct cam_control *cmd = (struct cam_control *)arg;
- if (!e_ctrl) {
- CAM_ERR(CAM_EEPROM, "e_ctrl is NULL");
+ if (!e_ctrl || !cmd) {
+ CAM_ERR(CAM_EEPROM, "Invalid Arguments");
+ return -EINVAL;
+ }
+
+ if (cmd->handle_type != CAM_HANDLE_USER_POINTER) {
+ CAM_ERR(CAM_EEPROM, "Invalid handle type: %d",
+ cmd->handle_type);
return -EINVAL;
}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.c
index 6120e02..f9411fc 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.c
@@ -29,6 +29,12 @@
return -EINVAL;
}
+ if (cmd->handle_type != CAM_HANDLE_USER_POINTER) {
+ CAM_ERR(CAM_FLASH, "Invalid handle type: %d",
+ cmd->handle_type);
+ return -EINVAL;
+ }
+
mutex_lock(&(fctrl->flash_mutex));
switch (cmd->op_code) {
case CAM_ACQUIRE_DEV: {
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.c
index c792be4..196df08 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.c
@@ -672,8 +672,14 @@
struct cam_ois_query_cap_t ois_cap = {0};
struct cam_control *cmd = (struct cam_control *)arg;
- if (!o_ctrl) {
- CAM_ERR(CAM_OIS, "e_ctrl is NULL");
+ if (!o_ctrl || !arg) {
+ CAM_ERR(CAM_OIS, "Invalid arguments");
+ return -EINVAL;
+ }
+
+ if (cmd->handle_type != CAM_HANDLE_USER_POINTER) {
+ CAM_ERR(CAM_OIS, "Invalid handle type: %d",
+ cmd->handle_type);
return -EINVAL;
}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
index e9dd1ad..d5bee96 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
@@ -568,6 +568,14 @@
return -EINVAL;
}
+ if (cmd->op_code != CAM_SENSOR_PROBE_CMD) {
+ if (cmd->handle_type != CAM_HANDLE_USER_POINTER) {
+ CAM_ERR(CAM_SENSOR, "Invalid handle type: %d",
+ cmd->handle_type);
+ return -EINVAL;
+ }
+ }
+
mutex_lock(&(s_ctrl->cam_sensor_mutex));
switch (cmd->op_code) {
case CAM_SENSOR_PROBE_CMD: {
@@ -607,6 +615,7 @@
} else {
CAM_ERR(CAM_SENSOR, "Invalid Command Type: %d",
cmd->handle_type);
+ return -EINVAL;
}
/* Parse and fill vreg params for powerup settings */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
index 7a8e621..19b1aac 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
@@ -35,12 +35,13 @@
#define VFE40_STATS_BURST_LEN_8916_VERSION 2
#define VFE40_FETCH_BURST_LEN 3
#define VFE40_UB_SIZE 1536 /* 1536 * 128 bits = 24KB */
+#define VFE40_STATS_SIZE 392
#define VFE40_UB_SIZE_8952 2048 /* 2048 * 128 bits = 32KB */
#define VFE40_UB_SIZE_8916 3072 /* 3072 * 128 bits = 48KB */
#define VFE40_EQUAL_SLICE_UB 190 /* (UB_SIZE - STATS SIZE)/6 */
#define VFE40_EQUAL_SLICE_UB_8916 236
#define VFE40_TOTAL_WM_UB 1144 /* UB_SIZE - STATS SIZE */
-#define VFE40_TOTAL_WM_UB_8916 1656
+#define VFE40_TOTAL_WM_UB_8916 2680
#define VFE40_WM_BASE(idx) (0x6C + 0x24 * idx)
#define VFE40_RDI_BASE(idx) (0x2E8 + 0x4 * idx)
#define VFE40_XBAR_BASE(idx) (0x58 + 0x4 * (idx / 2))
@@ -104,7 +105,11 @@
static uint32_t msm_vfe40_get_ub_size(struct vfe_device *vfe_dev)
{
- if (vfe_dev->vfe_hw_version == VFE40_8916_VERSION) {
+ if (vfe_dev->vfe_hw_version == VFE40_8916_VERSION ||
+ vfe_dev->vfe_hw_version == VFE40_8939_VERSION ||
+ vfe_dev->vfe_hw_version == VFE40_8937_VERSION ||
+ vfe_dev->vfe_hw_version == VFE40_8953_VERSION ||
+ vfe_dev->vfe_hw_version == VFE40_8917_VERSION) {
vfe_dev->ub_info->wm_ub = VFE40_TOTAL_WM_UB_8916;
return VFE40_TOTAL_WM_UB_8916;
}
@@ -580,6 +585,11 @@
*irq_status0 &= vfe_dev->irq0_mask;
*irq_status1 &= vfe_dev->irq1_mask;
+ if (*irq_status0 &&
+ (*irq_status0 == msm_camera_io_r(vfe_dev->vfe_base + 0x38))) {
+ msm_camera_io_w(*irq_status0, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x24);
+ }
if (*irq_status1 & (1 << 0)) {
vfe_dev->error_info.camif_status =
@@ -2208,7 +2218,7 @@
.num_comp_mask = 3,
.num_rdi = 3,
.num_rdi_master = 3,
- .min_wm_ub = 64,
+ .min_wm_ub = 96,
.scratch_buf_range = SZ_32M + SZ_4M,
};
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 f3814e0..ce50dcd 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -3385,7 +3385,7 @@
*/
int sde_rotator_session_open(struct sde_rot_mgr *mgr,
struct sde_rot_file_private **pprivate, int session_id,
- struct sde_rot_queue *queue)
+ struct sde_rot_queue_v1 *queue)
{
int ret;
struct sde_rot_file_private *private;
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 8421873..f6fdb90 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-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 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
@@ -242,6 +242,12 @@
struct sde_rot_hw_resource *hw;
};
+struct sde_rot_queue_v1 {
+ struct kthread_worker *rot_kw;
+ struct task_struct *rot_thread;
+ struct sde_rot_timeline *timeline;
+ struct sde_rot_hw_resource *hw;
+};
/*
* struct sde_rot_entry_container - rotation request
* @list: list of active requests managed by rotator manager
@@ -294,7 +300,7 @@
struct kthread_work commit_work;
struct kthread_work done_work;
struct sde_rot_queue *commitq;
- struct sde_rot_queue *fenceq;
+ struct sde_rot_queue_v1 *fenceq;
struct sde_rot_queue *doneq;
struct sde_rot_entry_container *request;
@@ -351,7 +357,7 @@
struct list_head req_list;
struct list_head perf_list;
struct sde_rot_mgr *mgr;
- struct sde_rot_queue *fenceq;
+ struct sde_rot_queue_v1 *fenceq;
};
/*
@@ -586,7 +592,7 @@
*/
int sde_rotator_session_open(struct sde_rot_mgr *mgr,
struct sde_rot_file_private **pprivate, int session_id,
- struct sde_rot_queue *queue);
+ struct sde_rot_queue_v1 *queue);
/*
* sde_rotator_session_close - close the given rotator per file session
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 a46194f..38c3d79 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-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 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
@@ -942,6 +942,7 @@
ctx->rotate = 0;
ctx->secure = 0;
ctx->abort_pending = 0;
+ ctx->kthread_id = -1;
ctx->format_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ctx->format_cap.fmt.pix.pixelformat = SDE_PIX_FMT_Y_CBCR_H2V2;
ctx->format_cap.fmt.pix.width = 640;
@@ -980,6 +981,7 @@
ctx, sde_rotator_queue_init);
if (IS_ERR_OR_NULL(ctx->fh.m2m_ctx)) {
ret = PTR_ERR(ctx->fh.m2m_ctx);
+ ctx->fh.m2m_ctx = NULL;
goto error_m2m_init;
}
}
@@ -999,18 +1001,22 @@
goto error_create_sysfs;
}
- snprintf(name, sizeof(name), "rot_fenceq_%d_%d", rot_dev->dev->id,
- ctx->session_id);
- kthread_init_worker(&ctx->work_queue.rot_kw);
- ctx->work_queue.rot_thread = kthread_run(kthread_worker_fn,
- &ctx->work_queue.rot_kw, name);
- if (IS_ERR(ctx->work_queue.rot_thread)) {
- SDEDEV_ERR(ctx->rot_dev->dev, "fail allocate kthread\n");
- ret = -EPERM;
- ctx->work_queue.rot_thread = NULL;
- goto error_alloc_workqueue;
+ for (i = 0; i < MAX_ROT_OPEN_SESSION; i++) {
+ if (rot_dev->kthread_free[i]) {
+ rot_dev->kthread_free[i] = false;
+ ctx->kthread_id = i;
+ ctx->work_queue.rot_kw = &rot_dev->rot_kw[i];
+ ctx->work_queue.rot_thread = rot_dev->rot_thread[i];
+ break;
+ }
}
- SDEDEV_DBG(ctx->rot_dev->dev, "kthread name=%s\n", name);
+
+ if (ctx->kthread_id < 0) {
+ SDEDEV_ERR(ctx->rot_dev->dev,
+ "fail to acquire the kthread\n");
+ ret = -EINVAL;
+ goto error_alloc_kthread;
+ }
snprintf(name, sizeof(name), "%d_%d", rot_dev->dev->id,
ctx->session_id);
@@ -1069,13 +1075,17 @@
error_open_session:
sde_rot_mgr_unlock(rot_dev->mgr);
sde_rotator_destroy_timeline(ctx->work_queue.timeline);
- kthread_flush_worker(&ctx->work_queue.rot_kw);
- kthread_stop(ctx->work_queue.rot_thread);
-error_alloc_workqueue:
+ rot_dev->kthread_free[ctx->kthread_id] = true;
+ ctx->kthread_id = -1;
+error_alloc_kthread:
sysfs_remove_group(&ctx->kobj, &sde_rotator_fs_attr_group);
error_create_sysfs:
kobject_put(&ctx->kobj);
error_kobj_init:
+ if (ctx->file) {
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+ ctx->fh.m2m_ctx = NULL;
+ }
error_m2m_init:
if (ctx->file) {
v4l2_fh_del(&ctx->fh);
@@ -1113,10 +1123,12 @@
if (ctx->file) {
v4l2_ctrl_handler_free(&ctx->ctrl_handler);
SDEDEV_DBG(rot_dev->dev, "release streams s:%d\n", session_id);
- v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx,
+ if (ctx->fh.m2m_ctx) {
+ v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx,
V4L2_BUF_TYPE_VIDEO_OUTPUT);
- v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx,
+ 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\n", session_id);
@@ -1146,14 +1158,19 @@
mutex_lock(&rot_dev->lock);
SDEDEV_DBG(rot_dev->dev, "release context s:%d\n", session_id);
sde_rotator_destroy_timeline(ctx->work_queue.timeline);
- kthread_flush_worker(&ctx->work_queue.rot_kw);
- kthread_stop(ctx->work_queue.rot_thread);
+ if (ctx->kthread_id >= 0 && ctx->work_queue.rot_kw) {
+ kthread_flush_worker(ctx->work_queue.rot_kw);
+ rot_dev->kthread_free[ctx->kthread_id] = true;
+ }
sysfs_remove_group(&ctx->kobj, &sde_rotator_fs_attr_group);
kobject_put(&ctx->kobj);
if (ctx->file) {
- v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
- v4l2_fh_del(&ctx->fh);
- v4l2_fh_exit(&ctx->fh);
+ if (ctx->fh.m2m_ctx)
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+ if (ctx->fh.vdev) {
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ }
}
kfree(ctx->vbinfo_out);
kfree(ctx->vbinfo_cap);
@@ -1834,7 +1851,7 @@
} else {
SDEROT_ERR("invalid stats timestamp\n");
}
- req->retire_kw = &ctx->work_queue.rot_kw;
+ req->retire_kw = ctx->work_queue.rot_kw;
req->retire_work = &request->retire_work;
trace_rot_entry_fence(
@@ -3187,7 +3204,7 @@
goto error_init_request;
}
- req->retire_kw = &ctx->work_queue.rot_kw;
+ req->retire_kw = ctx->work_queue.rot_kw;
req->retire_work = &request->retire_work;
ret = sde_rotator_handle_request_common(
@@ -3485,7 +3502,7 @@
list_del_init(&request->list);
list_add_tail(&request->list, &ctx->pending_list);
spin_unlock(&ctx->list_lock);
- kthread_queue_work(&ctx->work_queue.rot_kw,
+ kthread_queue_work(ctx->work_queue.rot_kw,
&request->submit_work);
}
} else if (request && !atomic_read(&request->req->pending_count)) {
@@ -3539,7 +3556,8 @@
{
struct sde_rotator_device *rot_dev;
struct video_device *vdev;
- int ret;
+ int ret, i;
+ char name[32];
SDEDEV_DBG(&pdev->dev, "SDE v4l2 rotator probed\n");
@@ -3622,9 +3640,29 @@
rot_dev->debugfs_root = sde_rotator_create_debugfs(rot_dev);
+ for (i = 0; i < MAX_ROT_OPEN_SESSION; i++) {
+ snprintf(name, sizeof(name), "rot_fenceq_%d_%d",
+ rot_dev->dev->id, i);
+ kthread_init_worker(&rot_dev->rot_kw[i]);
+ rot_dev->rot_thread[i] = kthread_run(kthread_worker_fn,
+ &rot_dev->rot_kw[i], name);
+ if (IS_ERR(rot_dev->rot_thread[i])) {
+ SDEDEV_ERR(rot_dev->dev,
+ "fail allocate kthread i:%d\n", i);
+ ret = -EPERM;
+ goto error_kthread_create;
+ }
+ rot_dev->kthread_free[i] = true;
+ }
+
SDEDEV_INFO(&pdev->dev, "SDE v4l2 rotator probe success\n");
return 0;
+error_kthread_create:
+ for (i--; i >= 0; i--)
+ kthread_stop(rot_dev->rot_thread[i]);
+ sde_rotator_destroy_debugfs(rot_dev->debugfs_root);
+ video_unregister_device(rot_dev->vdev);
error_video_register:
video_device_release(vdev);
error_alloc_video_device:
@@ -3647,6 +3685,7 @@
static int sde_rotator_remove(struct platform_device *pdev)
{
struct sde_rotator_device *rot_dev;
+ int i;
rot_dev = platform_get_drvdata(pdev);
if (rot_dev == NULL) {
@@ -3655,6 +3694,8 @@
}
sde_rotator_pm_qos_remove(rot_dev->mdata);
+ for (i = MAX_ROT_OPEN_SESSION - 1; i >= 0; i--)
+ kthread_stop(rot_dev->rot_thread[i]);
sde_rotator_destroy_debugfs(rot_dev->debugfs_root);
video_unregister_device(rot_dev->vdev);
video_device_release(rot_dev->vdev);
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 ab27043..f818016 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 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
@@ -42,6 +42,8 @@
/* maximum number of outstanding requests per ctx session */
#define SDE_ROTATOR_REQUEST_MAX 2
+#define MAX_ROT_OPEN_SESSION 16
+
struct sde_rotator_device;
struct sde_rotator_ctx;
@@ -140,6 +142,7 @@
* @retired_list: list of retired/free request
* @requests: static allocation of free requests
* @rotcfg: current core rotation configuration
+ * @kthread_id: thread_id used for fence management
*/
struct sde_rotator_ctx {
struct kobject kobj;
@@ -164,7 +167,7 @@
struct sde_rotator_vbinfo *vbinfo_cap;
struct sde_rotator_vbinfo *vbinfo_out;
wait_queue_head_t wait_queue;
- struct sde_rot_queue work_queue;
+ struct sde_rot_queue_v1 work_queue;
struct sde_rot_file_private *private;
struct llcc_slice_desc *slice;
u32 commit_sequence_id;
@@ -174,6 +177,8 @@
struct list_head retired_list;
struct sde_rotator_request requests[SDE_ROTATOR_REQUEST_MAX];
struct sde_rotation_config rotcfg;
+
+ int kthread_id;
};
/*
@@ -212,6 +217,9 @@
* @open_timeout: maximum wait time for ctx open in msec
* @open_wq: wait queue for ctx open
* @excl_ctx: Pointer to exclusive ctx
+ * @rot_kw: rotator thread work
+ * @rot_thread: rotator threads
+ * @kthread_free: check if thread is available or not
*/
struct sde_rotator_device {
struct mutex lock;
@@ -237,6 +245,10 @@
u32 open_timeout;
wait_queue_head_t open_wq;
struct sde_rotator_ctx *excl_ctx;
+
+ struct kthread_worker rot_kw[MAX_ROT_OPEN_SESSION];
+ struct task_struct *rot_thread[MAX_ROT_OPEN_SESSION];
+ bool kthread_free[MAX_ROT_OPEN_SESSION];
};
static inline
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index e3a5cc7..9ef9d05 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -1239,6 +1239,51 @@
struct hfi_h264_entropy_control);
break;
}
+ case HAL_CONFIG_HEIC_FRAME_QUALITY:
+ {
+ struct hfi_heic_frame_quality *hfi;
+
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_CONFIG_HEIC_FRAME_QUALITY;
+ hfi =
+ (struct hfi_heic_frame_quality *) &pkt->rg_property_data[1];
+ hfi->frame_quality =
+ ((struct hal_heic_frame_quality *)pdata)->frame_quality;
+ pkt->size += sizeof(u32) +
+ sizeof(struct hfi_heic_frame_quality);
+ break;
+ }
+ case HAL_CONFIG_HEIC_GRID_ENABLE:
+ {
+ struct hfi_heic_grid_enable *hfi;
+
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_CONFIG_HEIC_GRID_ENABLE;
+ hfi = (struct hfi_heic_grid_enable *) &pkt->rg_property_data[1];
+ hfi->grid_enable =
+ ((struct hal_heic_grid_enable *)pdata)->grid_enable;
+ pkt->size += sizeof(u32) + sizeof(struct hfi_heic_grid_enable);
+ break;
+ }
+ case HAL_CONFIG_HEIC_FRAME_CROP_INFO:
+ {
+ struct hfi_frame_crop *hfi_crop_info;
+ struct hal_frame_crop *hal_crop_info =
+ (struct hal_frame_crop *) pdata;
+
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_CONFIG_HEIC_FRAME_CROP_INFO;
+ hfi_crop_info =
+ (struct hfi_frame_crop *) &pkt->rg_property_data[1];
+
+ hfi_crop_info->left = hal_crop_info->left;
+ hfi_crop_info->top = hal_crop_info->top;
+ hfi_crop_info->width = hal_crop_info->width;
+ hfi_crop_info->height = hal_crop_info->height;
+
+ pkt->size += sizeof(u32) + sizeof(struct hfi_frame_crop);
+ break;
+ }
case HAL_PARAM_VENC_RATE_CONTROL:
{
u32 *rc;
@@ -1266,6 +1311,9 @@
case HAL_RATE_CONTROL_MBR_VFR:
pkt->rg_property_data[1] = HFI_RATE_CONTROL_MBR_VFR;
break;
+ case HAL_RATE_CONTROL_CQ:
+ pkt->rg_property_data[1] = HFI_RATE_CONTROL_CQ;
+ break;
default:
dprintk(VIDC_ERR,
"Invalid Rate control setting: %pK\n",
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index 97f5ca2..0cdcbd6 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -599,6 +599,9 @@
case HFI_CAPABILITY_UBWC_CR_STATS:
hal_cap = HAL_CAPABILITY_UBWC_CR_STATS;
break;
+ case HFI_CAPABILITY_IMG_GRID_DIMENSION:
+ hal_cap = HAL_CAPABILITY_IMG_GRID_DIMENSION;
+ break;
default:
dprintk(VIDC_DBG, "%s: unknown capablity %#x\n",
__func__, capability_type);
@@ -723,6 +726,9 @@
case HFI_CAPABILITY_UBWC_CR_STATS:
out = &capability->ubwc_cr_stats;
break;
+ case HFI_CAPABILITY_IMG_GRID_DIMENSION:
+ out = &capability->img_grid_dimension;
+ break;
default:
dprintk(VIDC_DBG, "%s: unknown capablity %#x\n",
__func__, in->capability_type);
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index be24f8d..9a7d272 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -24,6 +24,14 @@
#define BIT_RATE_STEP 1
#define DEFAULT_FRAME_RATE 15
#define OPERATING_FRAME_RATE_STEP (1 << 16)
+#define MIN_FRAME_QUALITY 0
+#define MAX_FRAME_QUALITY 100
+#define DEFAULT_FRAME_QUALITY 80
+#define FRAME_QUALITY_STEP 1
+#define MIN_GRID_DIMENSION 512
+#define MAX_GRID_DIMENSION 8192
+#define DEFAULT_GRID_DIMENSION 512
+#define GRID_DIMENSION_STEP 256
#define MAX_SLICE_BYTE_SIZE ((MAX_BIT_RATE)>>3)
#define MIN_SLICE_BYTE_SIZE 512
#define MAX_SLICE_MB_SIZE ((4096 * 2304) >> 8)
@@ -50,6 +58,7 @@
"CBR CFR",
"MBR CFR",
"MBR VFR",
+ "CQ",
NULL
};
@@ -358,7 +367,7 @@
.name = "Video Framerate and Bitrate Control",
.type = V4L2_CTRL_TYPE_MENU,
.minimum = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF,
- .maximum = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR,
+ .maximum = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CQ,
.default_value = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF,
.step = 0,
.menu_skip_mask = ~(
@@ -368,11 +377,35 @@
(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_VFR) |
(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR) |
(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR) |
- (1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR)
+ (1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR) |
+ (1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CQ)
),
.qmenu = mpeg_video_rate_control,
},
{
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_FRAME_QUALITY,
+ .name = "Frame quality",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = MIN_FRAME_QUALITY,
+ .maximum = MAX_FRAME_QUALITY,
+ .default_value = DEFAULT_FRAME_QUALITY,
+ .step = FRAME_QUALITY_STEP,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_IMG_GRID_DIMENSION,
+ .name = "Image grid",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = MIN_GRID_DIMENSION,
+ .maximum = MAX_GRID_DIMENSION,
+ .default_value = DEFAULT_GRID_DIMENSION,
+ .step = GRID_DIMENSION_STEP,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ .flags = V4L2_CTRL_FLAG_VOLATILE,
+ },
+ {
.id = V4L2_CID_MPEG_VIDEO_BITRATE,
.name = "Bit Rate",
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -1383,6 +1416,8 @@
enum hal_iframesize_type iframesize_type = HAL_IFRAMESIZE_TYPE_DEFAULT;
u32 color_primaries, custom_matrix;
struct hal_nal_stream_format_select stream_format;
+ struct hal_heic_frame_quality frame_quality;
+ struct hal_heic_grid_enable grid_enable;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
@@ -1483,6 +1518,13 @@
rc = -ENOTSUPP;
break;
}
+ if ((ctrl->val ==
+ V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CQ) &&
+ inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_HEVC) {
+ dprintk(VIDC_ERR, "CQ supported only for HEVC\n");
+ rc = -ENOTSUPP;
+ break;
+ }
property_id = HAL_PARAM_VENC_RATE_CONTROL;
property_val = ctrl->val;
pdata = &property_val;
@@ -1497,6 +1539,67 @@
inst->clk_data.bitrate = ctrl->val;
break;
}
+ case V4L2_CID_MPEG_VIDC_VIDEO_FRAME_QUALITY:
+ {
+ property_id = HAL_CONFIG_HEIC_FRAME_QUALITY;
+ frame_quality.frame_quality = ctrl->val;
+ pdata = &frame_quality;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDC_IMG_GRID_DIMENSION:
+ {
+ int i = 0, j = 0;
+ u32 width = 0, height = 0;
+ u32 trows, tcols;
+
+ property_id = HAL_CONFIG_HEIC_GRID_ENABLE;
+ if (inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_HEVC) {
+ dprintk(VIDC_ERR, "Grid is supported only for HEVC\n");
+ rc = -ENOTSUPP;
+ break;
+ }
+ if (ctrl->val == 0) {
+ dprintk(VIDC_ERR, "Dimension 0 is not supported\n");
+ rc = -ENOTSUPP;
+ break;
+ }
+ grid_enable.grid_enable = ctrl->val;
+ inst->img_grid_dimension = ctrl->val;
+ pdata = &grid_enable;
+
+ /* Update tile info table */
+ width = inst->prop.width[OUTPUT_PORT];
+ height = inst->prop.height[OUTPUT_PORT];
+ tcols = (width + inst->img_grid_dimension - 1) /
+ inst->img_grid_dimension;
+ trows = (height + inst->img_grid_dimension - 1) /
+ inst->img_grid_dimension;
+ inst->tinfo.count = trows * tcols;
+ if (inst->tinfo.count > MAX_HEIC_TILES_COUNT) {
+ dprintk(VIDC_ERR, "Tiles count exceeds maximum\n");
+ rc = -ENOTSUPP;
+ break;
+ }
+
+ dprintk(VIDC_DBG,
+ "Grid dimension %d width %d height %d row %d col %d\n",
+ inst->img_grid_dimension, width, height,
+ trows, tcols);
+
+ for (j = 0; j < trows; ++j) {
+ for (i = 0; i < tcols; ++i) {
+ inst->tinfo.tile_rects[j*tcols+i].left =
+ (i * inst->img_grid_dimension);
+ inst->tinfo.tile_rects[j*tcols+i].top =
+ (j * inst->img_grid_dimension);
+ inst->tinfo.tile_rects[j*tcols+i].width =
+ inst->img_grid_dimension;
+ inst->tinfo.tile_rects[j*tcols+i].height =
+ inst->img_grid_dimension;
+ }
+ }
+ break;
+ }
case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
{
struct v4l2_ctrl *avg_bitrate = TRY_GET_CTRL(
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index f077e3b..8e32504 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -1480,7 +1480,9 @@
V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE,
inst->profile);
break;
-
+ case V4L2_CID_MPEG_VIDC_IMG_GRID_DIMENSION:
+ ctrl->val = inst->img_grid_dimension;
+ break;
case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
ctrl->val = msm_comm_hal_to_v4l2(
V4L2_CID_MPEG_VIDEO_H264_LEVEL,
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 7d9bbed..b392610 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -1564,6 +1564,7 @@
print_cap("max_video_cores", &inst->capability.max_video_cores);
print_cap("max_work_modes", &inst->capability.max_work_modes);
print_cap("ubwc_cr_stats", &inst->capability.ubwc_cr_stats);
+ print_cap("image_grid_dimension", &inst->capability.img_grid_dimension);
dprintk(VIDC_DBG, "profile count : %u",
inst->capability.profile_level.profile_count);
@@ -2397,6 +2398,14 @@
}
empty_buf_done = (struct vidc_hal_ebd *)&response->input_done;
+ if ((get_hal_codec(inst->fmts[CAPTURE_PORT].fourcc) ==
+ HAL_VIDEO_CODEC_HEVC) &&
+ (inst->img_grid_dimension > 0) &&
+ (empty_buf_done->input_tag < inst->tinfo.count - 1)) {
+ dprintk(VIDC_DBG, "Wait for last tile. Current tile no: %d\n",
+ empty_buf_done->input_tag);
+ return;
+ }
/* If this is internal EOS buffer, handle it in driver */
if (is_eos_buffer(inst, empty_buf_done->packet_buffer)) {
dprintk(VIDC_DBG, "Received EOS buffer 0x%x\n",
@@ -4148,6 +4157,58 @@
}
}
+static int msm_comm_qbuf_heic_tiles(struct msm_vidc_inst *inst,
+ struct vidc_frame_data *frame_data)
+{
+ int rc = 0, tindex;
+ struct hfi_device *hdev;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ if (inst->tinfo.count > MAX_HEIC_TILES_COUNT) {
+ dprintk(VIDC_ERR, "%s tiles count exceeds maximum\n", __func__);
+ return -EINVAL;
+ }
+
+ hdev = inst->core->device;
+
+ for (tindex = 0; tindex < inst->tinfo.count; ++tindex) {
+ dprintk(VIDC_DBG,
+ "%s tile# %d Crop: left %u top %u width %u height %u\n",
+ __func__, tindex,
+ inst->tinfo.tile_rects[tindex].left,
+ inst->tinfo.tile_rects[tindex].top,
+ inst->tinfo.tile_rects[tindex].width,
+ inst->tinfo.tile_rects[tindex].height);
+
+ rc = call_hfi_op(hdev, session_set_property,
+ (void *)inst->session,
+ HAL_CONFIG_HEIC_FRAME_CROP_INFO,
+ &inst->tinfo.tile_rects[tindex]);
+ if (rc) {
+ dprintk(VIDC_WARN,
+ "Failed to set HEIC crop info %d\n", rc);
+ goto err_bad_input;
+ }
+
+ frame_data->input_tag = tindex;
+
+ rc = call_hfi_op(hdev, session_etb,
+ (void *)inst->session, frame_data);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to issue etb: %d\n", rc);
+ goto err_bad_input;
+ }
+ }
+
+err_bad_input:
+ return rc;
+}
+
static int msm_comm_qbuf_rbr(struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf)
{
@@ -4355,12 +4416,26 @@
for (c = 0; c < etbs.count; ++c) {
struct vidc_frame_data *frame_data = &etbs.data[c];
- rc = call_hfi_op(hdev, session_etb, inst->session,
- frame_data);
- if (rc) {
- dprintk(VIDC_ERR, "Failed to issue etb: %d\n",
+ if (get_hal_codec(inst->fmts[CAPTURE_PORT].fourcc) ==
+ HAL_VIDEO_CODEC_HEVC &&
+ (inst->img_grid_dimension > 0)) {
+ rc = msm_comm_qbuf_heic_tiles(inst, frame_data);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to send tiles: %d\n",
rc);
- goto err_bad_input;
+ goto err_bad_input;
+ }
+ } else {
+
+ rc = call_hfi_op(hdev, session_etb,
+ inst->session, frame_data);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to issue etb: %d\n",
+ rc);
+ goto err_bad_input;
+ }
}
log_frame(inst, frame_data,
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index 60cdd18..e107609 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -51,6 +51,7 @@
#define MAX_NUM_CAPTURE_BUFFERS VIDEO_MAX_FRAME // same as VB2_MAX_FRAME
#define MAX_SUPPORTED_INSTANCES 16
+#define MAX_HEIC_TILES_COUNT 256
/* Maintains the number of FTB's between each FBD over a window */
#define DCVS_FTB_WINDOW 16
@@ -264,6 +265,11 @@
u32 height;
};
+struct tile_info {
+ u32 count;
+ struct session_crop tile_rects[MAX_HEIC_TILES_COUNT];
+};
+
struct session_prop {
u32 width[MAX_PORT_NUM];
u32 height[MAX_PORT_NUM];
@@ -419,6 +425,8 @@
u32 profile;
u32 level;
u32 entropy_mode;
+ u32 img_grid_dimension;
+ struct tile_info tinfo;
struct msm_vidc_codec_data *codec_data;
struct hal_hdr10_pq_sei hdr10_sei_params;
};
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h
index e899af3..0cfe693 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, 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
@@ -298,6 +298,7 @@
#define HFI_RATE_CONTROL_CBR_CFR (HFI_OX_BASE + 0x5)
#define HFI_RATE_CONTROL_MBR_CFR (HFI_OX_BASE + 0x6)
#define HFI_RATE_CONTROL_MBR_VFR (HFI_OX_BASE + 0x7)
+#define HFI_RATE_CONTROL_CQ (HFI_OX_BASE + 0x8)
struct hfi_uncompressed_plane_actual_constraints_info {
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 9290c48..f69b98e 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -229,6 +229,9 @@
HAL_PARAM_VIDEO_WORK_MODE,
HAL_PARAM_SECURE,
HAL_PARAM_VENC_HDR10_PQ_SEI,
+ HAL_CONFIG_HEIC_FRAME_CROP_INFO,
+ HAL_CONFIG_HEIC_FRAME_QUALITY,
+ HAL_CONFIG_HEIC_GRID_ENABLE,
};
enum hal_domain {
@@ -604,6 +607,7 @@
HAL_RATE_CONTROL_CBR_CFR,
HAL_RATE_CONTROL_MBR_CFR,
HAL_RATE_CONTROL_MBR_VFR,
+ HAL_RATE_CONTROL_CQ,
HAL_UNUSED_RC = 0x10000000,
};
@@ -651,6 +655,14 @@
u32 idr_period;
};
+struct hal_heic_frame_quality {
+ u32 frame_quality;
+};
+
+struct hal_heic_grid_enable {
+ u32 grid_enable;
+};
+
enum hal_rotate {
HAL_ROTATE_NONE,
HAL_ROTATE_90,
@@ -795,6 +807,7 @@
HAL_CAPABILITY_MAX_VIDEOCORES,
HAL_CAPABILITY_MAX_WORKMODES,
HAL_CAPABILITY_UBWC_CR_STATS,
+ HAL_CAPABILITY_IMG_GRID_DIMENSION,
HAL_UNUSED_CAPABILITY = 0x10000000,
};
@@ -834,6 +847,13 @@
u32 aspect_height;
};
+struct hal_frame_crop {
+ u32 left;
+ u32 top;
+ u32 width;
+ u32 height;
+};
+
struct hal_codec_supported {
u32 decoder_codec_supported;
u32 encoder_codec_supported;
@@ -1232,6 +1252,7 @@
struct hal_capability_supported max_video_cores;
struct hal_capability_supported max_work_modes;
struct hal_capability_supported ubwc_cr_stats;
+ struct hal_capability_supported img_grid_dimension;
struct hal_profile_level_supported profile_level;
struct hal_uncompressed_format_supported uncomp_format;
struct hal_interlace_format_supported HAL_format;
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index 7615736..f4ea86b 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -356,6 +356,12 @@
(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x010)
#define HFI_PROPERTY_CONFIG_VENC_FRAME_QP \
(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x012)
+#define HFI_PROPERTY_CONFIG_HEIC_FRAME_CROP_INFO \
+ (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x013)
+#define HFI_PROPERTY_CONFIG_HEIC_FRAME_QUALITY \
+ (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x014)
+#define HFI_PROPERTY_CONFIG_HEIC_GRID_ENABLE \
+ (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x015)
#define HFI_PROPERTY_PARAM_VPE_COMMON_START \
(HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x7000)
@@ -380,6 +386,14 @@
u32 colour_space;
};
+struct hfi_frame_crop {
+ u32 left;
+ u32 top;
+ u32 width;
+ u32 height;
+ u32 reserved[3];
+};
+
#define HFI_CAPABILITY_FRAME_WIDTH (HFI_COMMON_BASE + 0x1)
#define HFI_CAPABILITY_FRAME_HEIGHT (HFI_COMMON_BASE + 0x2)
#define HFI_CAPABILITY_MBS_PER_FRAME (HFI_COMMON_BASE + 0x3)
@@ -414,6 +428,8 @@
#define HFI_CAPABILITY_MAX_VIDEOCORES (HFI_COMMON_BASE + 0X2B)
#define HFI_CAPABILITY_MAX_WORKMODES (HFI_COMMON_BASE + 0X2C)
#define HFI_CAPABILITY_UBWC_CR_STATS (HFI_COMMON_BASE + 0X2D)
+#define HFI_CAPABILITY_CQ_QUALITY_LEVEL (HFI_COMMON_BASE + 0X32)
+#define HFI_CAPABILITY_IMG_GRID_DIMENSION (HFI_COMMON_BASE + 0X33)
struct hfi_capability_supported {
u32 capability_type;
@@ -474,6 +490,14 @@
u32 frame_rate;
};
+struct hfi_heic_frame_quality {
+ u32 frame_quality;
+};
+
+struct hfi_heic_grid_enable {
+ u32 grid_enable;
+};
+
#define HFI_INTRA_REFRESH_NONE (HFI_COMMON_BASE + 0x1)
#define HFI_INTRA_REFRESH_CYCLIC (HFI_COMMON_BASE + 0x2)
#define HFI_INTRA_REFRESH_RANDOM (HFI_COMMON_BASE + 0x5)
diff --git a/drivers/media/platform/msm/vidc_3x/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc_3x/msm_v4l2_vidc.c
index 9d5f255..9d05086 100644
--- a/drivers/media/platform/msm/vidc_3x/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc_3x/msm_v4l2_vidc.c
@@ -628,6 +628,12 @@
msm_vidc_read_efuse_version(
pdev, core->resources.pf_cap_tbl, "efuse2");
+ rc = call_hfi_op(core->device, core_early_init,
+ core->device->hfi_device_data);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to early init core\n");
+ goto err_fail_sub_device_probe;
+ }
dprintk(VIDC_DBG, "populating sub devices\n");
/*
* Trigger probe for each sub-device i.e. qcom,msm-vidc,context-bank.
@@ -639,8 +645,12 @@
&pdev->dev);
if (rc) {
dprintk(VIDC_ERR, "Failed to trigger probe for sub-devices\n");
+ call_hfi_op(core->device, core_early_release,
+ core->device->hfi_device_data);
goto err_fail_sub_device_probe;
}
+ call_hfi_op(core->device, core_early_release,
+ core->device->hfi_device_data);
return rc;
diff --git a/drivers/media/platform/msm/vidc_3x/msm_venc.c b/drivers/media/platform/msm/vidc_3x/msm_venc.c
index a728b69..d129dc2 100644
--- a/drivers/media/platform/msm/vidc_3x/msm_venc.c
+++ b/drivers/media/platform/msm/vidc_3x/msm_venc.c
@@ -2278,6 +2278,21 @@
default:
goto unknown_value;
}
+ case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE:
+ switch (value) {
+ case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_NONE:
+ return HAL_INTRA_REFRESH_NONE;
+ case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_CYCLIC:
+ return HAL_INTRA_REFRESH_CYCLIC;
+ case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_RANDOM:
+ return HAL_INTRA_REFRESH_RANDOM;
+ case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_ADAPTIVE:
+ return HAL_INTRA_REFRESH_ADAPTIVE;
+ case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_CYCLIC_ADAPTIVE:
+ return HAL_INTRA_REFRESH_CYCLIC_ADAPTIVE;
+ default:
+ goto unknown_value;
+ }
}
unknown_value:
@@ -2327,6 +2342,11 @@
}
max = temp_ctrl->maximum;
temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_MIN_QP);
+ if (!temp_ctrl) {
+ dprintk(VIDC_ERR,
+ "failed to get control");
+ return -EINVAL;
+ }
min = temp_ctrl->minimum;
if (!VALIDATE_BOUNDARIES(min, max, qp_value))
rc = -EINVAL;
@@ -2341,6 +2361,11 @@
}
max = temp_ctrl->maximum;
temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP);
+ if (!temp_ctrl) {
+ dprintk(VIDC_ERR,
+ "failed to get control");
+ return -EINVAL;
+ }
min = temp_ctrl->minimum;
if (!VALIDATE_BOUNDARIES(min, max, qp_value))
rc = -EINVAL;
@@ -2355,6 +2380,11 @@
}
max = temp_ctrl->maximum;
temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_MIN_QP);
+ if (!temp_ctrl) {
+ dprintk(VIDC_ERR,
+ "failed to get control");
+ return -EINVAL;
+ }
min = temp_ctrl->minimum;
if (!VALIDATE_BOUNDARIES(min, max, qp_value))
rc = -EINVAL;
@@ -3123,8 +3153,10 @@
}
property_id = HAL_PARAM_VENC_INTRA_REFRESH;
+ intra_refresh.mode = venc_v4l2_to_hal(
+ V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE,
+ ctrl->val);
- intra_refresh.mode = ctrl->val;
intra_refresh.air_mbs = air_mbs->val;
intra_refresh.air_ref = air_ref->val;
intra_refresh.cir_mbs = cir_mbs->val;
@@ -3143,7 +3175,9 @@
property_id = HAL_PARAM_VENC_INTRA_REFRESH;
intra_refresh.air_mbs = ctrl->val;
- intra_refresh.mode = ir_mode->val;
+ intra_refresh.mode = venc_v4l2_to_hal(
+ V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE,
+ ir_mode->val);
intra_refresh.air_ref = air_ref->val;
intra_refresh.cir_mbs = cir_mbs->val;
@@ -3162,7 +3196,9 @@
intra_refresh.air_ref = ctrl->val;
intra_refresh.air_mbs = air_mbs->val;
- intra_refresh.mode = ir_mode->val;
+ intra_refresh.mode = venc_v4l2_to_hal(
+ V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE,
+ ir_mode->val);
intra_refresh.cir_mbs = cir_mbs->val;
pdata = &intra_refresh;
@@ -3181,7 +3217,9 @@
intra_refresh.cir_mbs = ctrl->val;
intra_refresh.air_mbs = air_mbs->val;
intra_refresh.air_ref = air_ref->val;
- intra_refresh.mode = ir_mode->val;
+ intra_refresh.mode = venc_v4l2_to_hal(
+ V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE,
+ ir_mode->val);
pdata = &intra_refresh;
break;
diff --git a/drivers/media/platform/msm/vidc_3x/msm_vidc_common.c b/drivers/media/platform/msm/vidc_3x/msm_vidc_common.c
index 4dcb3b1..bd58117 100644
--- a/drivers/media/platform/msm/vidc_3x/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc_3x/msm_vidc_common.c
@@ -2474,7 +2474,6 @@
}
hdev = inst->core->device;
abort_completion = SESSION_MSG_INDEX(HAL_SESSION_ABORT_DONE);
- init_completion(&inst->completions[abort_completion]);
rc = call_hfi_op(hdev, session_abort, (void *)inst->session);
if (rc) {
@@ -2632,8 +2631,6 @@
__func__);
}
- init_completion(&core->completions
- [SYS_MSG_INDEX(HAL_SYS_INIT_DONE)]);
rc = call_hfi_op(hdev, core_init, hdev->hfi_device_data);
if (rc) {
dprintk(VIDC_ERR, "Failed to init core, id = %d\n",
@@ -2737,8 +2734,6 @@
dprintk(VIDC_ERR, "Invalid session\n");
return -EINVAL;
}
- init_completion(
- &inst->completions[SESSION_MSG_INDEX(HAL_SESSION_INIT_DONE)]);
rc = call_hfi_op(hdev, session_init, hdev->hfi_device_data,
inst, get_hal_domain(inst->session_type),
@@ -2877,8 +2872,6 @@
inst, inst->state);
goto exit;
}
- init_completion(
- &inst->completions[SESSION_MSG_INDEX(HAL_SESSION_START_DONE)]);
rc = call_hfi_op(hdev, session_start, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
@@ -2908,8 +2901,6 @@
goto exit;
}
dprintk(VIDC_DBG, "Send Stop to hal\n");
- init_completion(
- &inst->completions[SESSION_MSG_INDEX(HAL_SESSION_STOP_DONE)]);
rc = call_hfi_op(hdev, session_stop, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR, "Failed to send stop\n");
@@ -2939,8 +2930,6 @@
}
dprintk(VIDC_DBG,
"Send release res to hal\n");
- init_completion(&inst->completions[
- SESSION_MSG_INDEX(HAL_SESSION_RELEASE_RESOURCE_DONE)]);
rc = call_hfi_op(hdev, session_release_res, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
@@ -2971,8 +2960,6 @@
}
dprintk(VIDC_DBG,
"Send session close to hal\n");
- init_completion(
- &inst->completions[SESSION_MSG_INDEX(HAL_SESSION_END_DONE)]);
rc = call_hfi_op(hdev, session_end, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
@@ -4021,8 +4008,6 @@
}
mutex_unlock(&inst->sync_lock);
- init_completion(&inst->completions[
- SESSION_MSG_INDEX(HAL_SESSION_PROPERTY_INFO)]);
switch (ptype) {
case HAL_PARAM_PROFILE_LEVEL_CURRENT:
case HAL_CONFIG_VDEC_ENTROPY:
@@ -4248,8 +4233,6 @@
if (inst->state != MSM_VIDC_CORE_INVALID &&
core->state != VIDC_CORE_INVALID) {
buffer_info.response_required = true;
- init_completion(&inst->completions[SESSION_MSG_INDEX
- (HAL_SESSION_RELEASE_BUFFER_DONE)]);
rc = call_hfi_op(hdev, session_release_buffers,
(void *)inst->session, &buffer_info);
if (rc) {
@@ -4321,9 +4304,6 @@
if (inst->state != MSM_VIDC_CORE_INVALID &&
core->state != VIDC_CORE_INVALID) {
buffer_info.response_required = true;
- init_completion(
- &inst->completions[SESSION_MSG_INDEX
- (HAL_SESSION_RELEASE_BUFFER_DONE)]);
rc = call_hfi_op(hdev, session_release_buffers,
(void *)inst->session, &buffer_info);
if (rc) {
diff --git a/drivers/media/platform/msm/vidc_3x/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc_3x/msm_vidc_res_parse.c
index 82e3b40..f2c93cd 100644
--- a/drivers/media/platform/msm/vidc_3x/msm_vidc_res_parse.c
+++ b/drivers/media/platform/msm/vidc_3x/msm_vidc_res_parse.c
@@ -22,16 +22,11 @@
#include "venus_boot.h"
#include "soc/qcom/secure_buffer.h"
#include <linux/io.h>
-#include <linux/clk.h>
-#include <linux/regulator/consumer.h>
enum clock_properties {
CLOCK_PROP_HAS_SCALING = 1 << 0,
};
-struct regulator *gdsc_venus;
-struct regulator *gdsc_venus_core0;
-
static inline struct device *msm_iommu_get_ctx(const char *ctx_name)
{
return NULL;
@@ -64,118 +59,6 @@
return 0;
}
-static int venus_regulator_setup(struct msm_vidc_platform_resources *res)
-{
- const char *reg_name = "venus";
- const char *reg_name_core0 = "venus-core0";
- int rc = 0;
-
- gdsc_venus = devm_regulator_get(&res->pdev->dev, reg_name);
- if (IS_ERR(gdsc_venus))
- dprintk(VIDC_ERR, "Failed to get Venus GDSC\n");
-
- rc = regulator_enable(gdsc_venus);
- if (rc)
- dprintk(VIDC_ERR, "Venus GDSC enable failed\n");
-
- gdsc_venus_core0 = devm_regulator_get(&res->pdev->dev, reg_name_core0);
- if (IS_ERR(gdsc_venus_core0))
- dprintk(VIDC_ERR, "Failed to get Venus-Core0 GDSC\n");
-
- rc = regulator_enable(gdsc_venus_core0);
- if (rc)
- dprintk(VIDC_ERR, "Venus-Core0 GDSC enable failed\n");
-
- dprintk(VIDC_DBG, "Vensu, Venus-Core0 GDSC's are enabled\n");
- return rc;
-}
-
-
-static int venus_clock_setup(struct msm_vidc_platform_resources *res,
- unsigned long rate)
-{
- int i, rc = 0;
- struct clock_info *cl;
- struct clk *clk = NULL;
-
- dprintk(VIDC_DBG, " %s In\n", __func__);
- for (i = 0; i < res->clock_set.count; i++) {
- cl = &res->clock_set.clock_tbl[i];
- if (!cl->has_scaling)
- continue;
-
- clk = clk_get(&res->pdev->dev, cl->name);
- rc = clk_set_rate(clk, clk_round_rate(clk, rate));
-
- if (rc)
- dprintk(VIDC_ERR,
- "%s: Failed to set clock rate %s: %d\n",
- __func__, cl->name, rc);
-
- dprintk(VIDC_DBG, "%s clock set clock rate to %lu\n",
- cl->name, rate);
- }
-
- dprintk(VIDC_DBG, " %s exit\n", __func__);
- return rc;
-}
-
-static int venus_clock_prepare_enable(struct msm_vidc_platform_resources *res)
-{
- int i, rc = 0;
- struct clock_info *cl;
- struct clk *clk = NULL;
-
- dprintk(VIDC_DBG, " %s In\n", __func__);
- for (i = 0; i < res->clock_set.count; i++) {
- cl = &res->clock_set.clock_tbl[i];
- clk = clk_get(&res->pdev->dev, cl->name);
- rc = clk_prepare_enable(clk);
-
- if (rc) {
- dprintk(VIDC_ERR, "failed to enable %s clock\n",
- cl->name);
- for (i--; i >= 0; i--) {
- cl = &res->clock_set.clock_tbl[i];
- clk = clk_get(&res->pdev->dev, cl->name);
- clk_disable_unprepare(clk);
- dprintk(VIDC_ERR, "clock %s unprepared\n",
- cl->name);
- }
- return rc;
- }
- dprintk(VIDC_DBG, " Clock : %s enabled\n", cl->name);
- }
-
- dprintk(VIDC_DBG, " %s exit\n", __func__);
- return rc;
-}
-
-static void venus_clk_disable_unprepare(struct msm_vidc_platform_resources *res)
-{
- int i;
- struct clock_info *cl;
- struct clk *clk = NULL;
-
- for (i = 0; i < res->clock_set.count; i++) {
- cl = &res->clock_set.clock_tbl[i];
- clk = clk_get(&res->pdev->dev, cl->name);
- dprintk(VIDC_DBG, "clock %s unprepared\n", cl->name);
- clk_disable_unprepare(clk);
- }
-
- if (gdsc_venus) {
- regulator_disable(gdsc_venus);
- dprintk(VIDC_DBG, "Venus Regulator disabled\n");
- gdsc_venus = NULL;
- }
- if (gdsc_venus_core0) {
- regulator_disable(gdsc_venus_core0);
- dprintk(VIDC_DBG, "Venus-Core0 Regulator disabled\n");
- gdsc_venus_core0 = NULL;
- }
-}
-
static inline enum imem_type read_imem_type(struct platform_device *pdev)
{
bool is_compatible(char *compat)
@@ -961,7 +844,6 @@
dprintk(VIDC_DBG, "Found bus %s [%d->%d] with governor %s\n",
bus->name, bus->master, bus->slave, bus->governor);
- venus_clk_disable_unprepare(res);
err_bus:
return rc;
}
@@ -1352,11 +1234,6 @@
"qcom,max-secure-instances",
&res->max_secure_inst_count);
- venus_regulator_setup(res);
- venus_clock_setup(res, 0);
- venus_clock_prepare_enable(res);
- venus_clock_setup(res, 1);
-
return rc;
err_setup_legacy_cb:
diff --git a/drivers/media/platform/msm/vidc_3x/venus_hfi.c b/drivers/media/platform/msm/vidc_3x/venus_hfi.c
index ec6f9b0..b83606a 100644
--- a/drivers/media/platform/msm/vidc_3x/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc_3x/venus_hfi.c
@@ -4001,6 +4001,32 @@
return rc;
}
+static int __early_init_resources(struct venus_hfi_device *device,
+ struct msm_vidc_platform_resources *res)
+{
+ int rc = 0;
+
+ rc = __init_regulators(device);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to get all regulators\n");
+ return -ENODEV;
+ }
+
+ rc = __init_clocks(device);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to init clocks\n");
+ rc = -ENODEV;
+ goto err_init_clocks;
+ }
+
+ return rc;
+
+err_init_clocks:
+ __deinit_regulators(device);
+ return rc;
+}
+
+
static void __deinit_resources(struct venus_hfi_device *device)
{
__deinit_bus(device);
@@ -4010,6 +4036,12 @@
device->sys_init_capabilities = NULL;
}
+static void __early_deinit_resources(struct venus_hfi_device *device)
+{
+ __deinit_clocks(device);
+ __deinit_regulators(device);
+}
+
static int __protect_cp_mem(struct venus_hfi_device *device)
{
struct tzbsp_memprot memprot;
@@ -4235,6 +4267,41 @@
return rc;
}
+static int __venus_early_power_on(struct venus_hfi_device *device)
+{
+ int rc = 0;
+
+ if (device->power_enabled)
+ return 0;
+
+ device->power_enabled = true;
+
+ rc = __enable_regulators(device);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to enable GDSC, err = %d\n", rc);
+ goto fail_enable_gdsc;
+ }
+
+ rc = __prepare_enable_clks(device);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to enable clocks: %d\n", rc);
+ goto fail_enable_clks;
+ }
+
+ rc = __scale_clocks(device, 0, NULL, 0);
+ if (rc) {
+ dprintk(VIDC_WARN,
+ "Failed to scale clocks, performance might be affected\n");
+ rc = 0;
+ }
+ return rc;
+
+fail_enable_clks:
+ __disable_regulators(device);
+fail_enable_gdsc:
+ return rc;
+}
+
static void __venus_power_off(struct venus_hfi_device *device, bool halt_axi)
{
if (!device->power_enabled)
@@ -4261,6 +4328,14 @@
device->power_enabled = false;
}
+static void __venus_early_power_off(struct venus_hfi_device *device)
+{
+ __disable_unprepare_clks(device);
+ if (__disable_regulators(device))
+ dprintk(VIDC_WARN, "Failed to disable regulators\n");
+ device->power_enabled = false;
+}
+
static inline int __suspend(struct venus_hfi_device *device)
{
int rc = 0;
@@ -4446,7 +4521,7 @@
struct venus_hfi_device *device = dev;
u32 smem_block_size = 0;
u8 *smem_table_ptr;
- char version[VENUS_VERSION_LENGTH];
+ char version[VENUS_VERSION_LENGTH] = "";
const u32 smem_image_index_venus = 14 * 128;
if (!device || !fw_info) {
@@ -4652,10 +4727,35 @@
}
}
+static int venus_hfi_core_early_init(void *device)
+{
+ int rc = 0;
+ struct venus_hfi_device *dev = device;
+
+ mutex_lock(&dev->lock);
+ rc = __early_init_resources(dev, dev->res);
+ rc = __venus_early_power_on(device);
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+static int venus_hfi_core_early_release(void *device)
+{
+ struct venus_hfi_device *dev = device;
+
+ mutex_lock(&dev->lock);
+ __venus_early_power_off(dev);
+ __early_deinit_resources(dev);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
static void venus_init_hfi_callbacks(struct hfi_device *hdev)
{
hdev->core_init = venus_hfi_core_init;
hdev->core_release = venus_hfi_core_release;
+ hdev->core_early_init = venus_hfi_core_early_init;
+ hdev->core_early_release = venus_hfi_core_early_release;
hdev->core_ping = venus_hfi_core_ping;
hdev->core_trigger_ssr = venus_hfi_core_trigger_ssr;
hdev->session_init = venus_hfi_session_init;
diff --git a/drivers/media/platform/msm/vidc_3x/vidc_hfi_api.h b/drivers/media/platform/msm/vidc_3x/vidc_hfi_api.h
index 04bf5a8..1a25a58 100644
--- a/drivers/media/platform/msm/vidc_3x/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc_3x/vidc_hfi_api.h
@@ -1483,7 +1483,9 @@
/*Add function pointers for all the hfi functions below*/
int (*core_init)(void *device);
+ int (*core_early_init)(void *device);
int (*core_release)(void *device);
+ int (*core_early_release)(void *device);
int (*core_ping)(void *device);
int (*core_trigger_ssr)(void *device, enum hal_ssr_trigger_type);
int (*session_init)(void *device, void *session_id,
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
index 30c148b..06e2cfd 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
@@ -83,7 +83,7 @@
static void channel_swdemux_tsklet(unsigned long data)
{
struct channel_info *channel = (struct channel_info *)data;
- struct c8sectpfei *fei = channel->fei;
+ struct c8sectpfei *fei;
unsigned long wp, rp;
int pos, num_packets, n, size;
u8 *buf;
@@ -91,6 +91,8 @@
if (unlikely(!channel || !channel->irec))
return;
+ fei = channel->fei;
+
wp = readl(channel->irec + DMA_PRDS_BUSWP_TP(0));
rp = readl(channel->irec + DMA_PRDS_BUSRP_TP(0));
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index cd209dc..8e2aa3f 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -596,6 +596,7 @@
pipe->bru = &vsp1->bru->entity;
pipe->lif = &vsp1->lif->entity;
pipe->output = vsp1->wpf[0];
+ pipe->output->pipe = pipe;
return 0;
}
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index 57c713a..4ac1ff4 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -509,7 +509,13 @@
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
- vsp1_pipelines_suspend(vsp1);
+ /*
+ * When used as part of a display pipeline, the VSP is stopped and
+ * restarted explicitly by the DU.
+ */
+ if (!vsp1->drm)
+ vsp1_pipelines_suspend(vsp1);
+
pm_runtime_force_suspend(vsp1->dev);
return 0;
@@ -520,7 +526,13 @@
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
pm_runtime_force_resume(vsp1->dev);
- vsp1_pipelines_resume(vsp1);
+
+ /*
+ * When used as part of a display pipeline, the VSP is stopped and
+ * restarted explicitly by the DU.
+ */
+ if (!vsp1->drm)
+ vsp1_pipelines_resume(vsp1);
return 0;
}
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index d351b9c..743aa0f 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -792,6 +792,7 @@
{
struct vsp1_video *video = vb2_get_drv_priv(vq);
struct vsp1_pipeline *pipe = video->rwpf->pipe;
+ bool start_pipeline = false;
unsigned long flags;
int ret;
@@ -802,11 +803,23 @@
mutex_unlock(&pipe->lock);
return ret;
}
+
+ start_pipeline = true;
}
pipe->stream_count++;
mutex_unlock(&pipe->lock);
+ /*
+ * vsp1_pipeline_ready() is not sufficient to establish that all streams
+ * are prepared and the pipeline is configured, as multiple streams
+ * can race through streamon with buffers already queued; Therefore we
+ * don't even attempt to start the pipeline until the last stream has
+ * called through here.
+ */
+ if (!start_pipeline)
+ return 0;
+
spin_lock_irqsave(&pipe->irqlock, flags);
if (vsp1_pipeline_ready(pipe))
vsp1_video_pipeline_run(pipe);
diff --git a/drivers/media/radio/radio-iris-transport.c b/drivers/media/radio/radio-iris-transport.c
index 98afd19..b03cb27 100644
--- a/drivers/media/radio/radio-iris-transport.c
+++ b/drivers/media/radio/radio-iris-transport.c
@@ -40,7 +40,7 @@
DEFINE_MUTEX(fm_smd_enable);
static int fmsmd_set;
static bool chan_opened;
-static int hcismd_fm_set_enable(const char *val, struct kernel_param *kp);
+static int hcismd_fm_set_enable(const char *val, const struct kernel_param *kp);
module_param_call(fmsmd_set, hcismd_fm_set_enable, NULL, &fmsmd_set, 0644);
static struct work_struct *reset_worker;
static void radio_hci_smd_deregister(void);
@@ -247,7 +247,7 @@
chan_opened = false;
}
-static int hcismd_fm_set_enable(const char *val, struct kernel_param *kp)
+static int hcismd_fm_set_enable(const char *val, const struct kernel_param *kp)
{
int ret = 0;
diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c
index 9caea83..d793c63 100644
--- a/drivers/media/usb/cpia2/cpia2_v4l.c
+++ b/drivers/media/usb/cpia2/cpia2_v4l.c
@@ -812,7 +812,7 @@
struct camera_data *cam = video_drvdata(file);
if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- buf->index > cam->num_frames)
+ buf->index >= cam->num_frames)
return -EINVAL;
buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer;
@@ -863,7 +863,7 @@
if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
buf->memory != V4L2_MEMORY_MMAP ||
- buf->index > cam->num_frames)
+ buf->index >= cam->num_frames)
return -EINVAL;
DBG("QBUF #%d\n", buf->index);
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index cde43b6..8412dfc 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -2184,7 +2184,7 @@
* Module parameters
*/
-static int uvc_clock_param_get(char *buffer, struct kernel_param *kp)
+static int uvc_clock_param_get(char *buffer, const struct kernel_param *kp)
{
if (uvc_clock_param == CLOCK_MONOTONIC)
return sprintf(buffer, "CLOCK_MONOTONIC");
@@ -2192,7 +2192,7 @@
return sprintf(buffer, "CLOCK_REALTIME");
}
-static int uvc_clock_param_set(const char *val, struct kernel_param *kp)
+static int uvc_clock_param_set(const char *val, const struct kernel_param *kp)
{
if (strncasecmp(val, "clock_", strlen("clock_")) == 0)
val += strlen("clock_");
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index d4ee140..9ae687b 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -2548,11 +2548,8 @@
unsigned int ioctl;
u32 flags;
const char * const name;
- union {
- u32 offset;
- int (*func)(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *p);
- } u;
+ int (*func)(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *fh, void *p);
void (*debug)(const void *arg, bool write_only);
};
@@ -2560,25 +2557,21 @@
#define INFO_FL_PRIO (1 << 0)
/* This control can be valid if the filehandle passes a control handler. */
#define INFO_FL_CTRL (1 << 1)
-/* This is a standard ioctl, no need for special code */
-#define INFO_FL_STD (1 << 2)
/* This is ioctl has its own function */
-#define INFO_FL_FUNC (1 << 3)
+#define INFO_FL_FUNC (1 << 2)
/* Queuing ioctl */
-#define INFO_FL_QUEUE (1 << 4)
+#define INFO_FL_QUEUE (1 << 3)
/* Zero struct from after the field to the end */
#define INFO_FL_CLEAR(v4l2_struct, field) \
((offsetof(struct v4l2_struct, field) + \
sizeof(((struct v4l2_struct *)0)->field)) << 16)
#define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16)
-#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags) \
- [_IOC_NR(_ioctl)] = { \
- .ioctl = _ioctl, \
- .flags = _flags | INFO_FL_STD, \
- .name = #_ioctl, \
- .u.offset = offsetof(struct v4l2_ioctl_ops, _vidioc), \
- .debug = _debug, \
+#define DEFINE_IOCTL_STD_FNC(_vidioc) \
+ static int __v4l_ ## _vidioc ## _fnc( \
+ const struct v4l2_ioctl_ops *ops, \
+ struct file *file, void *fh, void *p) { \
+ return ops->_vidioc(file, fh, p); \
}
#define IOCTL_INFO_FNC(_ioctl, _func, _debug, _flags) \
@@ -2586,10 +2579,44 @@
.ioctl = _ioctl, \
.flags = _flags | INFO_FL_FUNC, \
.name = #_ioctl, \
- .u.func = _func, \
+ .func = _func, \
.debug = _debug, \
}
+#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags) \
+ IOCTL_INFO_FNC(_ioctl, __v4l_ ## _vidioc ## _fnc, _debug, _flags)
+
+DEFINE_IOCTL_STD_FNC(vidioc_g_fbuf)
+DEFINE_IOCTL_STD_FNC(vidioc_s_fbuf)
+DEFINE_IOCTL_STD_FNC(vidioc_expbuf)
+DEFINE_IOCTL_STD_FNC(vidioc_g_std)
+DEFINE_IOCTL_STD_FNC(vidioc_g_audio)
+DEFINE_IOCTL_STD_FNC(vidioc_s_audio)
+DEFINE_IOCTL_STD_FNC(vidioc_g_input)
+DEFINE_IOCTL_STD_FNC(vidioc_g_edid)
+DEFINE_IOCTL_STD_FNC(vidioc_s_edid)
+DEFINE_IOCTL_STD_FNC(vidioc_g_output)
+DEFINE_IOCTL_STD_FNC(vidioc_g_audout)
+DEFINE_IOCTL_STD_FNC(vidioc_s_audout)
+DEFINE_IOCTL_STD_FNC(vidioc_g_selection)
+DEFINE_IOCTL_STD_FNC(vidioc_s_selection)
+DEFINE_IOCTL_STD_FNC(vidioc_g_jpegcomp)
+DEFINE_IOCTL_STD_FNC(vidioc_s_jpegcomp)
+DEFINE_IOCTL_STD_FNC(vidioc_enumaudio)
+DEFINE_IOCTL_STD_FNC(vidioc_enumaudout)
+DEFINE_IOCTL_STD_FNC(vidioc_enum_framesizes)
+DEFINE_IOCTL_STD_FNC(vidioc_enum_frameintervals)
+DEFINE_IOCTL_STD_FNC(vidioc_g_enc_index)
+DEFINE_IOCTL_STD_FNC(vidioc_encoder_cmd)
+DEFINE_IOCTL_STD_FNC(vidioc_try_encoder_cmd)
+DEFINE_IOCTL_STD_FNC(vidioc_decoder_cmd)
+DEFINE_IOCTL_STD_FNC(vidioc_try_decoder_cmd)
+DEFINE_IOCTL_STD_FNC(vidioc_s_dv_timings)
+DEFINE_IOCTL_STD_FNC(vidioc_g_dv_timings)
+DEFINE_IOCTL_STD_FNC(vidioc_enum_dv_timings)
+DEFINE_IOCTL_STD_FNC(vidioc_query_dv_timings)
+DEFINE_IOCTL_STD_FNC(vidioc_dv_timings_cap)
+
static struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),
@@ -2774,14 +2801,8 @@
}
write_only = _IOC_DIR(cmd) == _IOC_WRITE;
- if (info->flags & INFO_FL_STD) {
- typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
- const void *p = vfd->ioctl_ops;
- const vidioc_op *vidioc = p + info->u.offset;
-
- ret = (*vidioc)(file, fh, arg);
- } else if (info->flags & INFO_FL_FUNC) {
- ret = info->u.func(ops, file, fh, arg);
+ if (info->flags & INFO_FL_FUNC) {
+ ret = info->func(ops, file, fh, arg);
} else if (!ops->vidioc_default) {
ret = -ENOTTY;
} else {
diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c
index 1db0af6..b6189a4 100644
--- a/drivers/media/v4l2-core/videobuf-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf-dma-sg.c
@@ -185,12 +185,13 @@
dprintk(1, "init user [0x%lx+0x%lx => %d pages]\n",
data, size, dma->nr_pages);
- err = get_user_pages(data & PAGE_MASK, dma->nr_pages,
+ err = get_user_pages_longterm(data & PAGE_MASK, dma->nr_pages,
flags, dma->pages, NULL);
if (err != dma->nr_pages) {
dma->nr_pages = (err >= 0) ? err : 0;
- dprintk(1, "get_user_pages: err=%d [%d]\n", err, dma->nr_pages);
+ dprintk(1, "get_user_pages_longterm: err=%d [%d]\n", err,
+ dma->nr_pages);
return err < 0 ? err : -EINVAL;
}
return 0;
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index 89c7ed1..1cdab6d 100644
--- a/drivers/message/fusion/mptbase.c
+++ b/drivers/message/fusion/mptbase.c
@@ -99,7 +99,7 @@
MODULE_PARM_DESC(mpt_channel_mapping, " Mapping id's to channels (default=0)");
static int mpt_debug_level;
-static int mpt_set_debug_level(const char *val, struct kernel_param *kp);
+static int mpt_set_debug_level(const char *val, const struct kernel_param *kp);
module_param_call(mpt_debug_level, mpt_set_debug_level, param_get_int,
&mpt_debug_level, 0600);
MODULE_PARM_DESC(mpt_debug_level,
@@ -242,7 +242,7 @@
pci_write_config_word(pdev, PCI_COMMAND, command_reg);
}
-static int mpt_set_debug_level(const char *val, struct kernel_param *kp)
+static int mpt_set_debug_level(const char *val, const struct kernel_param *kp)
{
int ret = param_set_int(val, kp);
MPT_ADAPTER *ioc;
diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c
index 8f8bacb..a6b5259 100644
--- a/drivers/mfd/palmas.c
+++ b/drivers/mfd/palmas.c
@@ -430,6 +430,20 @@
{
unsigned int addr;
int ret, slave;
+ struct device_node *np = palmas_dev->dev->of_node;
+
+ if (of_property_read_bool(np, "ti,palmas-override-powerhold")) {
+ addr = PALMAS_BASE_TO_REG(PALMAS_PU_PD_OD_BASE,
+ PALMAS_PRIMARY_SECONDARY_PAD2);
+ slave = PALMAS_BASE_TO_SLAVE(PALMAS_PU_PD_OD_BASE);
+
+ ret = regmap_update_bits(palmas_dev->regmap[slave], addr,
+ PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_7_MASK, 0);
+ if (ret)
+ dev_err(palmas_dev->dev,
+ "Unable to write PRIMARY_SECONDARY_PAD2 %d\n",
+ ret);
+ }
if (!palmas_dev)
return;
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index fb07ce4..f1c9467 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -69,6 +69,9 @@
lkdtm-$(CONFIG_LKDTM) += lkdtm_rodata_objcopy.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_usercopy.o
+CFLAGS_lkdtm_rodata.o += $(DISABLE_LTO)
+KCOV_INSTRUMENT_lkdtm_rodata.o := n
+
OBJCOPYFLAGS :=
OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \
--set-section-flags .text=alloc,readonly \
diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c
index cc91f7b..eb29113 100644
--- a/drivers/misc/enclosure.c
+++ b/drivers/misc/enclosure.c
@@ -148,7 +148,7 @@
for (i = 0; i < components; i++) {
edev->component[i].number = -1;
edev->component[i].slot = -1;
- edev->component[i].power_status = 1;
+ edev->component[i].power_status = -1;
}
mutex_lock(&container_list_lock);
@@ -600,6 +600,11 @@
if (edev->cb->get_power_status)
edev->cb->get_power_status(edev, ecomp);
+
+ /* If still uninitialized, the callback failed or does not exist. */
+ if (ecomp->power_status == -1)
+ return (edev->cb->get_power_status) ? -EIO : -ENOTTY;
+
return snprintf(buf, 40, "%s\n", ecomp->power_status ? "on" : "off");
}
diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c
index 99635dd..2290845 100644
--- a/drivers/misc/kgdbts.c
+++ b/drivers/misc/kgdbts.c
@@ -1130,7 +1130,8 @@
ts.run_test(0, chr);
}
-static int param_set_kgdbts_var(const char *kmessage, struct kernel_param *kp)
+static int param_set_kgdbts_var(const char *kmessage,
+ const struct kernel_param *kp)
{
int len = strlen(kmessage);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 1b961a9..8700e72 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -4706,9 +4706,11 @@
int err = 0;
switch (mode) {
- case PM_HIBERNATION_PREPARE:
- case PM_SUSPEND_PREPARE:
case PM_RESTORE_PREPARE:
+ case PM_HIBERNATION_PREPARE:
+ if (host->bus_ops && host->bus_ops->pre_hibernate)
+ host->bus_ops->pre_hibernate(host);
+ case PM_SUSPEND_PREPARE:
spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 1;
spin_unlock_irqrestore(&host->lock, flags);
@@ -4723,6 +4725,14 @@
if (!err)
break;
+ if (!mmc_card_is_removable(host)) {
+ dev_warn(mmc_dev(host),
+ "pre_suspend failed for non-removable host: "
+ "%d\n", err);
+ /* Avoid removing non-removable hosts */
+ break;
+ }
+
/* Calling bus_ops->remove() with a claimed host can deadlock */
host->bus_ops->remove(host);
mmc_claim_host(host);
@@ -4732,9 +4742,11 @@
host->pm_flags = 0;
break;
- case PM_POST_SUSPEND:
- case PM_POST_HIBERNATION:
case PM_POST_RESTORE:
+ case PM_POST_HIBERNATION:
+ if (host->bus_ops && host->bus_ops->post_hibernate)
+ host->bus_ops->post_hibernate(host);
+ case PM_POST_SUSPEND:
spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 0;
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index ff4f84f..99165f7 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -3058,6 +3058,73 @@
return 0;
}
+static int mmc_pre_hibernate(struct mmc_host *host)
+{
+ int ret = 0;
+
+ mmc_get_card(host->card);
+ host->cached_caps2 = host->caps2;
+
+ /*
+ * Increase usage_count of card and host device till
+ * hibernation is over. This will ensure they will not runtime suspend.
+ */
+ pm_runtime_get_noresume(mmc_dev(host));
+ pm_runtime_get_noresume(&host->card->dev);
+
+ if (!mmc_can_scale_clk(host))
+ goto out;
+ /*
+ * Suspend clock scaling and mask host capability so that
+ * we will run in max frequency during:
+ * 1. Hibernation preparation and image creation
+ * 2. After finding hibernation image during reboot
+ * 3. Once hibernation image is loaded and till hibernation
+ * restore is complete.
+ */
+ if (host->clk_scaling.enable)
+ mmc_suspend_clk_scaling(host);
+ host->caps2 &= ~MMC_CAP2_CLK_SCALE;
+ host->clk_scaling.state = MMC_LOAD_HIGH;
+ ret = mmc_clk_update_freq(host, host->card->clk_scaling_highest,
+ host->clk_scaling.state);
+ if (ret)
+ pr_err("%s: %s: Setting clk frequency to max failed: %d\n",
+ mmc_hostname(host), __func__, ret);
+out:
+ mmc_host_clk_hold(host);
+ mmc_put_card(host->card);
+ return ret;
+}
+
+static int mmc_post_hibernate(struct mmc_host *host)
+{
+ int ret = 0;
+
+ mmc_get_card(host->card);
+ if (!(host->cached_caps2 & MMC_CAP2_CLK_SCALE))
+ goto enable_pm;
+ /* Enable the clock scaling and set the host capability */
+ host->caps2 |= MMC_CAP2_CLK_SCALE;
+ if (!host->clk_scaling.enable)
+ ret = mmc_resume_clk_scaling(host);
+ if (ret)
+ pr_err("%s: %s: Resuming clk scaling failed: %d\n",
+ mmc_hostname(host), __func__, ret);
+enable_pm:
+ /*
+ * Reduce usage count of card and host device so that they may
+ * runtime suspend.
+ */
+ pm_runtime_put_noidle(&host->card->dev);
+ pm_runtime_put_noidle(mmc_dev(host));
+
+ mmc_host_clk_release(host);
+
+ mmc_put_card(host->card);
+ return ret;
+}
+
static const struct mmc_bus_ops mmc_ops = {
.remove = mmc_remove,
.detect = mmc_detect,
@@ -3069,6 +3136,8 @@
.change_bus_speed = mmc_change_bus_speed,
.reset = mmc_reset,
.shutdown = mmc_shutdown,
+ .pre_hibernate = mmc_pre_hibernate,
+ .post_hibernate = mmc_post_hibernate
};
/*
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index f81f417..d382dbd 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -490,6 +490,7 @@
(sizeof(struct idmac_desc_64addr) *
(i + 1))) >> 32;
/* Initialize reserved and buffer size fields to "0" */
+ p->des0 = 0;
p->des1 = 0;
p->des2 = 0;
p->des3 = 0;
@@ -512,6 +513,7 @@
i++, p++) {
p->des3 = cpu_to_le32(host->sg_dma +
(sizeof(struct idmac_desc) * (i + 1)));
+ p->des0 = 0;
p->des1 = 0;
}
@@ -2878,8 +2880,8 @@
}
if (host->use_dma == TRANS_MODE_IDMAC)
- /* It is also recommended that we reset and reprogram idmac */
- dw_mci_idmac_reset(host);
+ /* It is also required that we reinit idmac */
+ dw_mci_idmac_init(host);
ret = true;
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 5f2f24a..a082aa3 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1762,8 +1762,8 @@
*/
if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING) {
struct pinctrl *p = devm_pinctrl_get(host->dev);
- if (!p) {
- ret = -ENODEV;
+ if (IS_ERR(p)) {
+ ret = PTR_ERR(p);
goto err_free_irq;
}
if (IS_ERR(pinctrl_lookup_state(p, PINCTRL_STATE_DEFAULT))) {
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 3c27401..a51d636 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -432,6 +432,20 @@
if (esdhc->vendor_ver < VENDOR_V_23)
pre_div = 2;
+ /*
+ * Limit SD clock to 167MHz for ls1046a according to its datasheet
+ */
+ if (clock > 167000000 &&
+ of_find_compatible_node(NULL, NULL, "fsl,ls1046a-esdhc"))
+ clock = 167000000;
+
+ /*
+ * Limit SD clock to 125MHz for ls1012a according to its datasheet
+ */
+ if (clock > 125000000 &&
+ of_find_compatible_node(NULL, NULL, "fsl,ls1012a-esdhc"))
+ clock = 125000000;
+
/* Workaround to reduce the clock frequency for p1010 esdhc */
if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) {
if (clock > 20000000)
diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c
index 7c887f1..62fd690 100644
--- a/drivers/mtd/devices/block2mtd.c
+++ b/drivers/mtd/devices/block2mtd.c
@@ -431,7 +431,7 @@
}
-static int block2mtd_setup(const char *val, struct kernel_param *kp)
+static int block2mtd_setup(const char *val, const struct kernel_param *kp)
{
#ifdef MODULE
return block2mtd_setup2(val);
diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c
index 8b66e52..7287696 100644
--- a/drivers/mtd/devices/phram.c
+++ b/drivers/mtd/devices/phram.c
@@ -266,7 +266,7 @@
return ret;
}
-static int phram_param_call(const char *val, struct kernel_param *kp)
+static int phram_param_call(const char *val, const struct kernel_param *kp)
{
#ifdef MODULE
return phram_setup(val);
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 2a47a3f..b4092ea 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -487,7 +487,7 @@
for (i = 0; i < MTD_MAX_ECCPOS_ENTRIES;) {
u32 eccpos;
- ret = mtd_ooblayout_ecc(mtd, section, &oobregion);
+ ret = mtd_ooblayout_ecc(mtd, section++, &oobregion);
if (ret < 0) {
if (ret != -ERANGE)
return ret;
@@ -534,7 +534,7 @@
for (i = 0; i < ARRAY_SIZE(to->eccpos);) {
u32 eccpos;
- ret = mtd_ooblayout_ecc(mtd, section, &oobregion);
+ ret = mtd_ooblayout_ecc(mtd, section++, &oobregion);
if (ret < 0) {
if (ret != -ERANGE)
return ret;
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c
index 1a4a790..ef9a6b2 100644
--- a/drivers/mtd/nand/brcmnand/brcmnand.c
+++ b/drivers/mtd/nand/brcmnand/brcmnand.c
@@ -1763,7 +1763,7 @@
err = brcmstb_nand_verify_erased_page(mtd, chip, buf,
addr);
/* erased page bitflips corrected */
- if (err > 0)
+ if (err >= 0)
return err;
}
diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
index d1570f5..2f6b552 100644
--- a/drivers/mtd/nand/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/fsl_ifc_nand.c
@@ -201,14 +201,9 @@
/* returns nonzero if entire page is blank */
static int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl,
- u32 *eccstat, unsigned int bufnum)
+ u32 eccstat, unsigned int bufnum)
{
- u32 reg = eccstat[bufnum / 4];
- int errors;
-
- errors = (reg >> ((3 - bufnum % 4) * 8)) & 15;
-
- return errors;
+ return (eccstat >> ((3 - bufnum % 4) * 8)) & 15;
}
/*
@@ -221,7 +216,7 @@
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl;
struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
- u32 eccstat[4];
+ u32 eccstat;
int i;
/* set the chip select for NAND Transaction */
@@ -256,19 +251,17 @@
if (nctrl->eccread) {
int errors;
int bufnum = nctrl->page & priv->bufnum_mask;
- int sector = bufnum * chip->ecc.steps;
- int sector_end = sector + chip->ecc.steps - 1;
+ int sector_start = bufnum * chip->ecc.steps;
+ int sector_end = sector_start + chip->ecc.steps - 1;
__be32 *eccstat_regs;
- if (ctrl->version >= FSL_IFC_VERSION_2_0_0)
- eccstat_regs = ifc->ifc_nand.v2_nand_eccstat;
- else
- eccstat_regs = ifc->ifc_nand.v1_nand_eccstat;
+ eccstat_regs = ifc->ifc_nand.nand_eccstat;
+ eccstat = ifc_in32(&eccstat_regs[sector_start / 4]);
- for (i = sector / 4; i <= sector_end / 4; i++)
- eccstat[i] = ifc_in32(&eccstat_regs[i]);
+ for (i = sector_start; i <= sector_end; i++) {
+ if (i != sector_start && !(i % 4))
+ eccstat = ifc_in32(&eccstat_regs[i / 4]);
- for (i = sector; i <= sector_end; i++) {
errors = check_read_ecc(mtd, ctrl, eccstat, i);
if (errors == 15) {
@@ -656,6 +649,7 @@
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
u32 nand_fsr;
+ int status;
/* Use READ_STATUS command, but wait for the device to be ready */
ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
@@ -670,12 +664,12 @@
fsl_ifc_run_command(mtd);
nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr);
-
+ status = nand_fsr >> 24;
/*
* The chip always seems to report that it is
* write-protected, even when it is not.
*/
- return nand_fsr | NAND_STATUS_WP;
+ return status | NAND_STATUS_WP;
}
static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
@@ -907,6 +901,13 @@
if (ctrl->version == FSL_IFC_VERSION_1_1_0)
fsl_ifc_sram_init(priv);
+ /*
+ * As IFC version 2.0.0 has 16KB of internal SRAM as compared to older
+ * versions which had 8KB. Hence bufnum mask needs to be updated.
+ */
+ if (ctrl->version >= FSL_IFC_VERSION_2_0_0)
+ priv->bufnum_mask = (priv->bufnum_mask * 2) + 1;
+
return 0;
}
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
index 6c062b8..427039b 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
@@ -1059,9 +1059,6 @@
return ret;
}
- /* handle the block mark swapping */
- block_mark_swapping(this, payload_virt, auxiliary_virt);
-
/* Loop over status bytes, accumulating ECC status. */
status = auxiliary_virt + nfc_geo->auxiliary_status_offset;
@@ -1150,6 +1147,9 @@
max_bitflips = max_t(unsigned int, max_bitflips, *status);
}
+ /* handle the block mark swapping */
+ block_mark_swapping(this, buf, auxiliary_virt);
+
if (oob_required) {
/*
* It's time to deliver the OOB bytes. See gpmi_ecc_read_oob()
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 21c0308..a3e86e5 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -715,7 +715,8 @@
chip->cmd_ctrl(mtd, readcmd, ctrl);
ctrl &= ~NAND_CTRL_CHANGE;
}
- chip->cmd_ctrl(mtd, command, ctrl);
+ if (command != NAND_CMD_NONE)
+ chip->cmd_ctrl(mtd, command, ctrl);
/* Address cycle, when necessary */
ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;
@@ -744,6 +745,7 @@
*/
switch (command) {
+ case NAND_CMD_NONE:
case NAND_CMD_PAGEPROG:
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
@@ -806,7 +808,9 @@
}
/* Command latch cycle */
- chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+ if (command != NAND_CMD_NONE)
+ chip->cmd_ctrl(mtd, command,
+ NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
if (column != -1 || page_addr != -1) {
int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
@@ -842,6 +846,7 @@
*/
switch (command) {
+ case NAND_CMD_NONE:
case NAND_CMD_CACHEDPROG:
case NAND_CMD_PAGEPROG:
case NAND_CMD_ERASE1:
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 85d54f3..e1da78f 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -1389,7 +1389,7 @@
* This function returns zero in case of success and a negative error code in
* case of error.
*/
-static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp)
+static int __init ubi_mtd_param_parse(const char *val, const struct kernel_param *kp)
{
int i, len;
struct mtd_dev_param *p;
diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c
index 7ac78c1..1bcb25b 100644
--- a/drivers/mtd/ubi/vmt.c
+++ b/drivers/mtd/ubi/vmt.c
@@ -265,6 +265,12 @@
vol->last_eb_bytes = vol->usable_leb_size;
}
+ /* Make volume "available" before it becomes accessible via sysfs */
+ spin_lock(&ubi->volumes_lock);
+ ubi->volumes[vol_id] = vol;
+ ubi->vol_count += 1;
+ spin_unlock(&ubi->volumes_lock);
+
/* Register character device for the volume */
cdev_init(&vol->cdev, &ubi_vol_cdev_operations);
vol->cdev.owner = THIS_MODULE;
@@ -304,11 +310,6 @@
if (err)
goto out_sysfs;
- spin_lock(&ubi->volumes_lock);
- ubi->volumes[vol_id] = vol;
- ubi->vol_count += 1;
- spin_unlock(&ubi->volumes_lock);
-
ubi_volume_notify(ubi, vol, UBI_VOLUME_ADDED);
self_check_volumes(ubi);
return err;
@@ -328,6 +329,10 @@
out_cdev:
cdev_del(&vol->cdev);
out_mapping:
+ spin_lock(&ubi->volumes_lock);
+ ubi->volumes[vol_id] = NULL;
+ ubi->vol_count -= 1;
+ spin_unlock(&ubi->volumes_lock);
if (do_free)
ubi_eba_destroy_table(eba_tbl);
out_acc:
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 63d61c0..4907c9b 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -371,9 +371,10 @@
/* Get link speed and duplex from the slave's base driver
* using ethtool. If for some reason the call fails or the
* values are invalid, set speed and duplex to -1,
- * and return.
+ * and return. Return 1 if speed or duplex settings are
+ * UNKNOWN; 0 otherwise.
*/
-static void bond_update_speed_duplex(struct slave *slave)
+static int bond_update_speed_duplex(struct slave *slave)
{
struct net_device *slave_dev = slave->dev;
struct ethtool_link_ksettings ecmd;
@@ -383,24 +384,27 @@
slave->duplex = DUPLEX_UNKNOWN;
res = __ethtool_get_link_ksettings(slave_dev, &ecmd);
- if (res < 0)
- return;
-
- if (ecmd.base.speed == 0 || ecmd.base.speed == ((__u32)-1))
- return;
-
+ if (res < 0) {
+ slave->link = BOND_LINK_DOWN;
+ return 1;
+ }
+ if (ecmd.base.speed == 0 || ecmd.base.speed == ((__u32)-1)) {
+ slave->link = BOND_LINK_DOWN;
+ return 1;
+ }
switch (ecmd.base.duplex) {
case DUPLEX_FULL:
case DUPLEX_HALF:
break;
default:
- return;
+ slave->link = BOND_LINK_DOWN;
+ return 1;
}
slave->speed = ecmd.base.speed;
slave->duplex = ecmd.base.duplex;
- return;
+ return 0;
}
const char *bond_slave_link_status(s8 link)
@@ -2063,6 +2067,7 @@
(bond->params.downdelay - slave->delay) *
bond->params.miimon,
slave->dev->name);
+ commit++;
continue;
}
@@ -2100,7 +2105,7 @@
(bond->params.updelay - slave->delay) *
bond->params.miimon,
slave->dev->name);
-
+ commit++;
continue;
}
@@ -3327,12 +3332,17 @@
for (i = 0; i < sizeof(*_res) / sizeof(u64); i++) {
u64 nv = new[i];
u64 ov = old[i];
+ s64 delta = nv - ov;
/* detects if this particular field is 32bit only */
if (((nv | ov) >> 32) == 0)
- res[i] += (u32)nv - (u32)ov;
- else
- res[i] += nv - ov;
+ delta = (s64)(s32)((u32)nv - (u32)ov);
+
+ /* filter anomalies, some drivers reset their stats
+ * at down/up events.
+ */
+ if (delta > 0)
+ res[i] += delta;
}
}
diff --git a/drivers/net/can/cc770/cc770.c b/drivers/net/can/cc770/cc770.c
index 1e37313..6da69af 100644
--- a/drivers/net/can/cc770/cc770.c
+++ b/drivers/net/can/cc770/cc770.c
@@ -390,37 +390,23 @@
return 0;
}
-static netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static void cc770_tx(struct net_device *dev, int mo)
{
struct cc770_priv *priv = netdev_priv(dev);
- struct net_device_stats *stats = &dev->stats;
- struct can_frame *cf = (struct can_frame *)skb->data;
- unsigned int mo = obj2msgobj(CC770_OBJ_TX);
+ struct can_frame *cf = (struct can_frame *)priv->tx_skb->data;
u8 dlc, rtr;
u32 id;
int i;
- if (can_dropped_invalid_skb(dev, skb))
- return NETDEV_TX_OK;
-
- if ((cc770_read_reg(priv,
- msgobj[mo].ctrl1) & TXRQST_UNC) == TXRQST_SET) {
- netdev_err(dev, "TX register is still occupied!\n");
- return NETDEV_TX_BUSY;
- }
-
- netif_stop_queue(dev);
-
dlc = cf->can_dlc;
id = cf->can_id;
- if (cf->can_id & CAN_RTR_FLAG)
- rtr = 0;
- else
- rtr = MSGCFG_DIR;
+ rtr = cf->can_id & CAN_RTR_FLAG ? 0 : MSGCFG_DIR;
+
+ cc770_write_reg(priv, msgobj[mo].ctrl0,
+ MSGVAL_RES | TXIE_RES | RXIE_RES | INTPND_RES);
cc770_write_reg(priv, msgobj[mo].ctrl1,
RMTPND_RES | TXRQST_RES | CPUUPD_SET | NEWDAT_RES);
- cc770_write_reg(priv, msgobj[mo].ctrl0,
- MSGVAL_SET | TXIE_SET | RXIE_RES | INTPND_RES);
+
if (id & CAN_EFF_FLAG) {
id &= CAN_EFF_MASK;
cc770_write_reg(priv, msgobj[mo].config,
@@ -439,22 +425,30 @@
for (i = 0; i < dlc; i++)
cc770_write_reg(priv, msgobj[mo].data[i], cf->data[i]);
- /* Store echo skb before starting the transfer */
- can_put_echo_skb(skb, dev, 0);
-
cc770_write_reg(priv, msgobj[mo].ctrl1,
- RMTPND_RES | TXRQST_SET | CPUUPD_RES | NEWDAT_UNC);
-
- stats->tx_bytes += dlc;
-
-
- /*
- * HM: We had some cases of repeated IRQs so make sure the
- * INT is acknowledged I know it's already further up, but
- * doing again fixed the issue
- */
+ RMTPND_UNC | TXRQST_SET | CPUUPD_RES | NEWDAT_UNC);
cc770_write_reg(priv, msgobj[mo].ctrl0,
- MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES);
+ MSGVAL_SET | TXIE_SET | RXIE_SET | INTPND_UNC);
+}
+
+static netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+ unsigned int mo = obj2msgobj(CC770_OBJ_TX);
+
+ if (can_dropped_invalid_skb(dev, skb))
+ return NETDEV_TX_OK;
+
+ netif_stop_queue(dev);
+
+ if ((cc770_read_reg(priv,
+ msgobj[mo].ctrl1) & TXRQST_UNC) == TXRQST_SET) {
+ netdev_err(dev, "TX register is still occupied!\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ priv->tx_skb = skb;
+ cc770_tx(dev, mo);
return NETDEV_TX_OK;
}
@@ -680,19 +674,46 @@
struct cc770_priv *priv = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
unsigned int mo = obj2msgobj(o);
+ struct can_frame *cf;
+ u8 ctrl1;
- /* Nothing more to send, switch off interrupts */
+ ctrl1 = cc770_read_reg(priv, msgobj[mo].ctrl1);
+
cc770_write_reg(priv, msgobj[mo].ctrl0,
MSGVAL_RES | TXIE_RES | RXIE_RES | INTPND_RES);
- /*
- * We had some cases of repeated IRQ so make sure the
- * INT is acknowledged
- */
- cc770_write_reg(priv, msgobj[mo].ctrl0,
- MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES);
+ cc770_write_reg(priv, msgobj[mo].ctrl1,
+ RMTPND_RES | TXRQST_RES | MSGLST_RES | NEWDAT_RES);
+ if (unlikely(!priv->tx_skb)) {
+ netdev_err(dev, "missing tx skb in tx interrupt\n");
+ return;
+ }
+
+ if (unlikely(ctrl1 & MSGLST_SET)) {
+ stats->rx_over_errors++;
+ stats->rx_errors++;
+ }
+
+ /* When the CC770 is sending an RTR message and it receives a regular
+ * message that matches the id of the RTR message, it will overwrite the
+ * outgoing message in the TX register. When this happens we must
+ * process the received message and try to transmit the outgoing skb
+ * again.
+ */
+ if (unlikely(ctrl1 & NEWDAT_SET)) {
+ cc770_rx(dev, mo, ctrl1);
+ cc770_tx(dev, mo);
+ return;
+ }
+
+ cf = (struct can_frame *)priv->tx_skb->data;
+ stats->tx_bytes += cf->can_dlc;
stats->tx_packets++;
+
+ can_put_echo_skb(priv->tx_skb, dev, 0);
can_get_echo_skb(dev, 0);
+ priv->tx_skb = NULL;
+
netif_wake_queue(dev);
}
@@ -804,6 +825,7 @@
priv->can.do_set_bittiming = cc770_set_bittiming;
priv->can.do_set_mode = cc770_set_mode;
priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
+ priv->tx_skb = NULL;
memcpy(priv->obj_flags, cc770_obj_flags, sizeof(cc770_obj_flags));
diff --git a/drivers/net/can/cc770/cc770.h b/drivers/net/can/cc770/cc770.h
index a1739db..95752e1 100644
--- a/drivers/net/can/cc770/cc770.h
+++ b/drivers/net/can/cc770/cc770.h
@@ -193,6 +193,8 @@
u8 cpu_interface; /* CPU interface register */
u8 clkout; /* Clock out register */
u8 bus_config; /* Bus conffiguration register */
+
+ struct sk_buff *tx_skb;
};
struct net_device *alloc_cc770dev(int sizeof_priv);
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 16f7cad..47f43bd 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -493,7 +493,7 @@
data = be32_to_cpup((__be32 *)&cf->data[0]);
flexcan_write(data, ®s->mb[FLEXCAN_TX_BUF_ID].data[0]);
}
- if (cf->can_dlc > 3) {
+ if (cf->can_dlc > 4) {
data = be32_to_cpup((__be32 *)&cf->data[4]);
flexcan_write(data, ®s->mb[FLEXCAN_TX_BUF_ID].data[1]);
}
diff --git a/drivers/net/can/ifi_canfd/ifi_canfd.c b/drivers/net/can/ifi_canfd/ifi_canfd.c
index c06ef43..6c67640 100644
--- a/drivers/net/can/ifi_canfd/ifi_canfd.c
+++ b/drivers/net/can/ifi_canfd/ifi_canfd.c
@@ -30,6 +30,7 @@
#define IFI_CANFD_STCMD_ERROR_ACTIVE BIT(2)
#define IFI_CANFD_STCMD_ERROR_PASSIVE BIT(3)
#define IFI_CANFD_STCMD_BUSOFF BIT(4)
+#define IFI_CANFD_STCMD_ERROR_WARNING BIT(5)
#define IFI_CANFD_STCMD_BUSMONITOR BIT(16)
#define IFI_CANFD_STCMD_LOOPBACK BIT(18)
#define IFI_CANFD_STCMD_DISABLE_CANFD BIT(24)
@@ -52,7 +53,10 @@
#define IFI_CANFD_TXSTCMD_OVERFLOW BIT(13)
#define IFI_CANFD_INTERRUPT 0xc
+#define IFI_CANFD_INTERRUPT_ERROR_BUSOFF BIT(0)
#define IFI_CANFD_INTERRUPT_ERROR_WARNING BIT(1)
+#define IFI_CANFD_INTERRUPT_ERROR_STATE_CHG BIT(2)
+#define IFI_CANFD_INTERRUPT_ERROR_REC_TEC_INC BIT(3)
#define IFI_CANFD_INTERRUPT_ERROR_COUNTER BIT(10)
#define IFI_CANFD_INTERRUPT_TXFIFO_EMPTY BIT(16)
#define IFI_CANFD_INTERRUPT_TXFIFO_REMOVE BIT(22)
@@ -61,6 +65,10 @@
#define IFI_CANFD_INTERRUPT_SET_IRQ ((u32)BIT(31))
#define IFI_CANFD_IRQMASK 0x10
+#define IFI_CANFD_IRQMASK_ERROR_BUSOFF BIT(0)
+#define IFI_CANFD_IRQMASK_ERROR_WARNING BIT(1)
+#define IFI_CANFD_IRQMASK_ERROR_STATE_CHG BIT(2)
+#define IFI_CANFD_IRQMASK_ERROR_REC_TEC_INC BIT(3)
#define IFI_CANFD_IRQMASK_SET_ERR BIT(7)
#define IFI_CANFD_IRQMASK_SET_TS BIT(15)
#define IFI_CANFD_IRQMASK_TXFIFO_EMPTY BIT(16)
@@ -136,6 +144,8 @@
#define IFI_CANFD_SYSCLOCK 0x50
#define IFI_CANFD_VER 0x54
+#define IFI_CANFD_VER_REV_MASK 0xff
+#define IFI_CANFD_VER_REV_MIN_SUPPORTED 0x15
#define IFI_CANFD_IP_ID 0x58
#define IFI_CANFD_IP_ID_VALUE 0xD073CAFD
@@ -220,7 +230,10 @@
if (enable) {
enirq = IFI_CANFD_IRQMASK_TXFIFO_EMPTY |
- IFI_CANFD_IRQMASK_RXFIFO_NEMPTY;
+ IFI_CANFD_IRQMASK_RXFIFO_NEMPTY |
+ IFI_CANFD_IRQMASK_ERROR_STATE_CHG |
+ IFI_CANFD_IRQMASK_ERROR_WARNING |
+ IFI_CANFD_IRQMASK_ERROR_BUSOFF;
if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
enirq |= IFI_CANFD_INTERRUPT_ERROR_COUNTER;
}
@@ -361,12 +374,13 @@
return 1;
}
-static int ifi_canfd_handle_lec_err(struct net_device *ndev, const u32 errctr)
+static int ifi_canfd_handle_lec_err(struct net_device *ndev)
{
struct ifi_canfd_priv *priv = netdev_priv(ndev);
struct net_device_stats *stats = &ndev->stats;
struct can_frame *cf;
struct sk_buff *skb;
+ u32 errctr = readl(priv->base + IFI_CANFD_ERROR_CTR);
const u32 errmask = IFI_CANFD_ERROR_CTR_OVERLOAD_FIRST |
IFI_CANFD_ERROR_CTR_ACK_ERROR_FIRST |
IFI_CANFD_ERROR_CTR_BIT0_ERROR_FIRST |
@@ -449,6 +463,11 @@
switch (new_state) {
case CAN_STATE_ERROR_ACTIVE:
+ /* error active state */
+ priv->can.can_stats.error_warning++;
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ break;
+ case CAN_STATE_ERROR_WARNING:
/* error warning state */
priv->can.can_stats.error_warning++;
priv->can.state = CAN_STATE_ERROR_WARNING;
@@ -477,7 +496,7 @@
ifi_canfd_get_berr_counter(ndev, &bec);
switch (new_state) {
- case CAN_STATE_ERROR_ACTIVE:
+ case CAN_STATE_ERROR_WARNING:
/* error warning state */
cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = (bec.txerr > bec.rxerr) ?
@@ -510,22 +529,21 @@
return 1;
}
-static int ifi_canfd_handle_state_errors(struct net_device *ndev, u32 stcmd)
+static int ifi_canfd_handle_state_errors(struct net_device *ndev)
{
struct ifi_canfd_priv *priv = netdev_priv(ndev);
+ u32 stcmd = readl(priv->base + IFI_CANFD_STCMD);
int work_done = 0;
- u32 isr;
- /*
- * The ErrWarn condition is a little special, since the bit is
- * located in the INTERRUPT register instead of STCMD register.
- */
- isr = readl(priv->base + IFI_CANFD_INTERRUPT);
- if ((isr & IFI_CANFD_INTERRUPT_ERROR_WARNING) &&
+ if ((stcmd & IFI_CANFD_STCMD_ERROR_ACTIVE) &&
+ (priv->can.state != CAN_STATE_ERROR_ACTIVE)) {
+ netdev_dbg(ndev, "Error, entered active state\n");
+ work_done += ifi_canfd_handle_state_change(ndev,
+ CAN_STATE_ERROR_ACTIVE);
+ }
+
+ if ((stcmd & IFI_CANFD_STCMD_ERROR_WARNING) &&
(priv->can.state != CAN_STATE_ERROR_WARNING)) {
- /* Clear the interrupt */
- writel(IFI_CANFD_INTERRUPT_ERROR_WARNING,
- priv->base + IFI_CANFD_INTERRUPT);
netdev_dbg(ndev, "Error, entered warning state\n");
work_done += ifi_canfd_handle_state_change(ndev,
CAN_STATE_ERROR_WARNING);
@@ -552,18 +570,11 @@
{
struct net_device *ndev = napi->dev;
struct ifi_canfd_priv *priv = netdev_priv(ndev);
- const u32 stcmd_state_mask = IFI_CANFD_STCMD_ERROR_PASSIVE |
- IFI_CANFD_STCMD_BUSOFF;
+ u32 rxstcmd = readl(priv->base + IFI_CANFD_RXSTCMD);
int work_done = 0;
- u32 stcmd = readl(priv->base + IFI_CANFD_STCMD);
- u32 rxstcmd = readl(priv->base + IFI_CANFD_RXSTCMD);
- u32 errctr = readl(priv->base + IFI_CANFD_ERROR_CTR);
-
/* Handle bus state changes */
- if ((stcmd & stcmd_state_mask) ||
- ((stcmd & IFI_CANFD_STCMD_ERROR_ACTIVE) == 0))
- work_done += ifi_canfd_handle_state_errors(ndev, stcmd);
+ work_done += ifi_canfd_handle_state_errors(ndev);
/* Handle lost messages on RX */
if (rxstcmd & IFI_CANFD_RXSTCMD_OVERFLOW)
@@ -571,7 +582,7 @@
/* Handle lec errors on the bus */
if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
- work_done += ifi_canfd_handle_lec_err(ndev, errctr);
+ work_done += ifi_canfd_handle_lec_err(ndev);
/* Handle normal messages on RX */
if (!(rxstcmd & IFI_CANFD_RXSTCMD_EMPTY))
@@ -592,12 +603,13 @@
struct net_device_stats *stats = &ndev->stats;
const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY |
IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER |
+ IFI_CANFD_INTERRUPT_ERROR_COUNTER |
+ IFI_CANFD_INTERRUPT_ERROR_STATE_CHG |
IFI_CANFD_INTERRUPT_ERROR_WARNING |
- IFI_CANFD_INTERRUPT_ERROR_COUNTER;
+ IFI_CANFD_INTERRUPT_ERROR_BUSOFF;
const u32 tx_irq_mask = IFI_CANFD_INTERRUPT_TXFIFO_EMPTY |
IFI_CANFD_INTERRUPT_TXFIFO_REMOVE;
- const u32 clr_irq_mask = ~((u32)(IFI_CANFD_INTERRUPT_SET_IRQ |
- IFI_CANFD_INTERRUPT_ERROR_WARNING));
+ const u32 clr_irq_mask = ~((u32)IFI_CANFD_INTERRUPT_SET_IRQ);
u32 isr;
isr = readl(priv->base + IFI_CANFD_INTERRUPT);
@@ -933,7 +945,7 @@
struct resource *res;
void __iomem *addr;
int irq, ret;
- u32 id;
+ u32 id, rev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
addr = devm_ioremap_resource(dev, res);
@@ -947,6 +959,13 @@
return -EINVAL;
}
+ rev = readl(addr + IFI_CANFD_VER) & IFI_CANFD_VER_REV_MASK;
+ if (rev < IFI_CANFD_VER_REV_MIN_SUPPORTED) {
+ dev_err(dev, "This block is too old (rev %i), minimum supported is rev %i\n",
+ rev, IFI_CANFD_VER_REV_MIN_SUPPORTED);
+ return -EINVAL;
+ }
+
ndev = alloc_candev(sizeof(*priv), 1);
if (!ndev)
return -ENOMEM;
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
index 5390ae8..71611bd 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
@@ -560,6 +560,7 @@
xgene_enet_rd_csr(pdata, CLE_BYPASS_REG0_0_ADDR, &cb);
cb |= CFG_CLE_BYPASS_EN0;
CFG_CLE_IP_PROTOCOL0_SET(&cb, 3);
+ CFG_CLE_IP_HDR_LEN_SET(&cb, 0);
xgene_enet_wr_csr(pdata, CLE_BYPASS_REG0_0_ADDR, cb);
xgene_enet_rd_csr(pdata, CLE_BYPASS_REG1_0_ADDR, &cb);
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
index 06e598c..c82faf1 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
@@ -163,6 +163,7 @@
#define CFG_RXCLK_MUXSEL0_SET(dst, val) xgene_set_bits(dst, val, 26, 3)
#define CFG_CLE_IP_PROTOCOL0_SET(dst, val) xgene_set_bits(dst, val, 16, 2)
+#define CFG_CLE_IP_HDR_LEN_SET(dst, val) xgene_set_bits(dst, val, 8, 5)
#define CFG_CLE_DSTQID0_SET(dst, val) xgene_set_bits(dst, val, 0, 12)
#define CFG_CLE_FPSEL0_SET(dst, val) xgene_set_bits(dst, val, 16, 4)
#define CFG_MACMODE_SET(dst, val) xgene_set_bits(dst, val, 18, 2)
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
index 8158d46..651f308 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
@@ -505,14 +505,24 @@
return NETDEV_TX_OK;
}
-static void xgene_enet_skip_csum(struct sk_buff *skb)
+static void xgene_enet_rx_csum(struct sk_buff *skb)
{
+ struct net_device *ndev = skb->dev;
struct iphdr *iph = ip_hdr(skb);
- if (!ip_is_fragment(iph) ||
- (iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_UDP)) {
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- }
+ if (!(ndev->features & NETIF_F_RXCSUM))
+ return;
+
+ if (skb->protocol != htons(ETH_P_IP))
+ return;
+
+ if (ip_is_fragment(iph))
+ return;
+
+ if (iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_UDP)
+ return;
+
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
}
static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring,
@@ -537,9 +547,9 @@
buf_pool->rx_skb[skb_index] = NULL;
/* checking for error */
- status = (GET_VAL(ELERR, le64_to_cpu(raw_desc->m0)) << LERR_LEN) ||
+ status = (GET_VAL(ELERR, le64_to_cpu(raw_desc->m0)) << LERR_LEN) |
GET_VAL(LERR, le64_to_cpu(raw_desc->m0));
- if (unlikely(status > 2)) {
+ if (unlikely(status)) {
dev_kfree_skb_any(skb);
xgene_enet_parse_error(rx_ring, netdev_priv(rx_ring->ndev),
status);
@@ -555,10 +565,7 @@
skb_checksum_none_assert(skb);
skb->protocol = eth_type_trans(skb, ndev);
- if (likely((ndev->features & NETIF_F_IP_CSUM) &&
- skb->protocol == htons(ETH_P_IP))) {
- xgene_enet_skip_csum(skb);
- }
+ xgene_enet_rx_csum(skb);
rx_ring->rx_packets++;
rx_ring->rx_bytes += datalen;
@@ -1725,7 +1732,7 @@
xgene_enet_setup_ops(pdata);
if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) {
- ndev->features |= NETIF_F_TSO;
+ ndev->features |= NETIF_F_TSO | NETIF_F_RXCSUM;
spin_lock_init(&pdata->mss_lock);
}
ndev->hw_features = ndev->features;
diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c
index be865b4..aba4853 100644
--- a/drivers/net/ethernet/arc/emac_main.c
+++ b/drivers/net/ethernet/arc/emac_main.c
@@ -210,39 +210,48 @@
continue;
}
- pktlen = info & LEN_MASK;
- stats->rx_packets++;
- stats->rx_bytes += pktlen;
- skb = rx_buff->skb;
- skb_put(skb, pktlen);
- skb->dev = ndev;
- skb->protocol = eth_type_trans(skb, ndev);
-
- dma_unmap_single(&ndev->dev, dma_unmap_addr(rx_buff, addr),
- dma_unmap_len(rx_buff, len), DMA_FROM_DEVICE);
-
- /* Prepare the BD for next cycle */
- rx_buff->skb = netdev_alloc_skb_ip_align(ndev,
- EMAC_BUFFER_SIZE);
- if (unlikely(!rx_buff->skb)) {
+ /* Prepare the BD for next cycle. netif_receive_skb()
+ * only if new skb was allocated and mapped to avoid holes
+ * in the RX fifo.
+ */
+ skb = netdev_alloc_skb_ip_align(ndev, EMAC_BUFFER_SIZE);
+ if (unlikely(!skb)) {
+ if (net_ratelimit())
+ netdev_err(ndev, "cannot allocate skb\n");
+ /* Return ownership to EMAC */
+ rxbd->info = cpu_to_le32(FOR_EMAC | EMAC_BUFFER_SIZE);
stats->rx_errors++;
- /* Because receive_skb is below, increment rx_dropped */
stats->rx_dropped++;
continue;
}
- /* receive_skb only if new skb was allocated to avoid holes */
- netif_receive_skb(skb);
-
- addr = dma_map_single(&ndev->dev, (void *)rx_buff->skb->data,
+ addr = dma_map_single(&ndev->dev, (void *)skb->data,
EMAC_BUFFER_SIZE, DMA_FROM_DEVICE);
if (dma_mapping_error(&ndev->dev, addr)) {
if (net_ratelimit())
- netdev_err(ndev, "cannot dma map\n");
- dev_kfree_skb(rx_buff->skb);
+ netdev_err(ndev, "cannot map dma buffer\n");
+ dev_kfree_skb(skb);
+ /* Return ownership to EMAC */
+ rxbd->info = cpu_to_le32(FOR_EMAC | EMAC_BUFFER_SIZE);
stats->rx_errors++;
+ stats->rx_dropped++;
continue;
}
+
+ /* unmap previosly mapped skb */
+ dma_unmap_single(&ndev->dev, dma_unmap_addr(rx_buff, addr),
+ dma_unmap_len(rx_buff, len), DMA_FROM_DEVICE);
+
+ pktlen = info & LEN_MASK;
+ stats->rx_packets++;
+ stats->rx_bytes += pktlen;
+ skb_put(rx_buff->skb, pktlen);
+ rx_buff->skb->dev = ndev;
+ rx_buff->skb->protocol = eth_type_trans(rx_buff->skb, ndev);
+
+ netif_receive_skb(rx_buff->skb);
+
+ rx_buff->skb = skb;
dma_unmap_addr_set(rx_buff, addr, addr);
dma_unmap_len_set(rx_buff, len, EMAC_BUFFER_SIZE);
diff --git a/drivers/net/ethernet/arc/emac_rockchip.c b/drivers/net/ethernet/arc/emac_rockchip.c
index c616387..c770ca3 100644
--- a/drivers/net/ethernet/arc/emac_rockchip.c
+++ b/drivers/net/ethernet/arc/emac_rockchip.c
@@ -169,8 +169,10 @@
/* Optional regulator for PHY */
priv->regulator = devm_regulator_get_optional(dev, "phy");
if (IS_ERR(priv->regulator)) {
- if (PTR_ERR(priv->regulator) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ if (PTR_ERR(priv->regulator) == -EPROBE_DEFER) {
+ err = -EPROBE_DEFER;
+ goto out_clk_disable;
+ }
dev_err(dev, "no regulator found\n");
priv->regulator = NULL;
}
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c
index 744ed6d..91fbba5 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.c
+++ b/drivers/net/ethernet/broadcom/bcmsysport.c
@@ -707,37 +707,33 @@
struct bcm_sysport_tx_ring *ring)
{
struct net_device *ndev = priv->netdev;
- unsigned int c_index, last_c_index, last_tx_cn, num_tx_cbs;
unsigned int pkts_compl = 0, bytes_compl = 0;
+ unsigned int txbds_processed = 0;
struct bcm_sysport_cb *cb;
+ unsigned int txbds_ready;
+ unsigned int c_index;
u32 hw_ind;
/* 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;
- ring->p_index = (hw_ind & RING_PROD_INDEX_MASK);
-
- last_c_index = ring->c_index;
- num_tx_cbs = ring->size;
-
- c_index &= (num_tx_cbs - 1);
-
- if (c_index >= last_c_index)
- last_tx_cn = c_index - last_c_index;
- else
- last_tx_cn = num_tx_cbs - last_c_index + c_index;
+ txbds_ready = (c_index - ring->c_index) & RING_CONS_INDEX_MASK;
netif_dbg(priv, tx_done, ndev,
- "ring=%d c_index=%d last_tx_cn=%d last_c_index=%d\n",
- ring->index, c_index, last_tx_cn, last_c_index);
+ "ring=%d old_c_index=%u c_index=%u txbds_ready=%u\n",
+ ring->index, ring->c_index, c_index, txbds_ready);
- while (last_tx_cn-- > 0) {
- cb = ring->cbs + last_c_index;
+ while (txbds_processed < txbds_ready) {
+ cb = &ring->cbs[ring->clean_index];
bcm_sysport_tx_reclaim_one(priv, cb, &bytes_compl, &pkts_compl);
ring->desc_count++;
- last_c_index++;
- last_c_index &= (num_tx_cbs - 1);
+ txbds_processed++;
+
+ if (likely(ring->clean_index < ring->size - 1))
+ ring->clean_index++;
+ else
+ ring->clean_index = 0;
}
ring->c_index = c_index;
@@ -1207,6 +1203,7 @@
netif_tx_napi_add(priv->netdev, &ring->napi, bcm_sysport_tx_poll, 64);
ring->index = index;
ring->size = size;
+ ring->clean_index = 0;
ring->alloc_size = ring->size;
ring->desc_cpu = p;
ring->desc_count = ring->size;
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.h b/drivers/net/ethernet/broadcom/bcmsysport.h
index 1c82e3d..07b0aaa 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.h
+++ b/drivers/net/ethernet/broadcom/bcmsysport.h
@@ -638,7 +638,7 @@
unsigned int desc_count; /* Number of descriptors */
unsigned int curr_desc; /* Current descriptor */
unsigned int c_index; /* Last consumer index */
- unsigned int p_index; /* Current producer index */
+ unsigned int clean_index; /* Current clean index */
struct bcm_sysport_cb *cbs; /* Transmit control blocks */
struct dma_desc *desc_cpu; /* CPU view of the descriptor */
struct bcm_sysport_priv *priv; /* private context backpointer */
diff --git a/drivers/net/ethernet/broadcom/bgmac-bcma.c b/drivers/net/ethernet/broadcom/bgmac-bcma.c
index c16ec3a..1ee11b6 100644
--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c
+++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c
@@ -11,6 +11,7 @@
#include <linux/bcma/bcma.h>
#include <linux/brcmphy.h>
#include <linux/etherdevice.h>
+#include <linux/of_net.h>
#include "bgmac.h"
static inline bool bgmac_is_bcm4707_family(struct bcma_device *core)
@@ -96,7 +97,7 @@
struct ssb_sprom *sprom = &core->bus->sprom;
struct mii_bus *mii_bus;
struct bgmac *bgmac;
- u8 *mac;
+ const u8 *mac = NULL;
int err;
bgmac = kzalloc(sizeof(*bgmac), GFP_KERNEL);
@@ -110,21 +111,27 @@
bcma_set_drvdata(core, bgmac);
- switch (core->core_unit) {
- case 0:
- mac = sprom->et0mac;
- break;
- case 1:
- mac = sprom->et1mac;
- break;
- case 2:
- mac = sprom->et2mac;
- break;
- default:
- dev_err(bgmac->dev, "Unsupported core_unit %d\n",
- core->core_unit);
- err = -ENOTSUPP;
- goto err;
+ if (bgmac->dev->of_node)
+ mac = of_get_mac_address(bgmac->dev->of_node);
+
+ /* If no MAC address assigned via device tree, check SPROM */
+ if (!mac) {
+ switch (core->core_unit) {
+ case 0:
+ mac = sprom->et0mac;
+ break;
+ case 1:
+ mac = sprom->et1mac;
+ break;
+ case 2:
+ mac = sprom->et2mac;
+ break;
+ default:
+ dev_err(bgmac->dev, "Unsupported core_unit %d\n",
+ core->core_unit);
+ err = -ENOTSUPP;
+ goto err;
+ }
}
ether_addr_copy(bgmac->mac_addr, mac);
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
index 0a5ee1d..ca6c471 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
@@ -2026,6 +2026,7 @@
ETH_OVREHEAD +
mtu +
BNX2X_FW_RX_ALIGN_END;
+ fp->rx_buf_size = SKB_DATA_ALIGN(fp->rx_buf_size);
/* Note : rx_buf_size doesn't take into account NET_SKB_PAD */
if (fp->rx_buf_size + NET_SKB_PAD <= PAGE_SIZE)
fp->rx_frag_size = fp->rx_buf_size + NET_SKB_PAD;
@@ -3034,7 +3035,7 @@
del_timer_sync(&bp->timer);
- if (IS_PF(bp)) {
+ if (IS_PF(bp) && !BP_NOMCP(bp)) {
/* Set ALWAYS_ALIVE bit in shmem */
bp->fw_drv_pulse_wr_seq |= DRV_PULSE_ALWAYS_ALIVE;
bnx2x_drv_pulse(bp);
@@ -3120,7 +3121,7 @@
bp->cnic_loaded = false;
/* Clear driver version indication in shmem */
- if (IS_PF(bp))
+ if (IS_PF(bp) && !BP_NOMCP(bp))
bnx2x_update_mng_version(bp);
/* Check if there are pending parity attentions. If there are - set
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index 5d958b5..554c408 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -9578,6 +9578,15 @@
do {
bp->common.shmem_base = REG_RD(bp, MISC_REG_SHARED_MEM_ADDR);
+
+ /* If we read all 0xFFs, means we are in PCI error state and
+ * should bail out to avoid crashes on adapter's FW reads.
+ */
+ if (bp->common.shmem_base == 0xFFFFFFFF) {
+ bp->flags |= NO_MCP_FLAG;
+ return -ENODEV;
+ }
+
if (bp->common.shmem_base) {
val = SHMEM_RD(bp, validity_map[BP_PORT(bp)]);
if (val & SHR_MEM_VALIDITY_MB)
@@ -14312,7 +14321,10 @@
BNX2X_ERR("IO slot reset --> driver unload\n");
/* MCP should have been reset; Need to wait for validity */
- bnx2x_init_shmem(bp);
+ if (bnx2x_init_shmem(bp)) {
+ rtnl_unlock();
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
if (IS_PF(bp) && SHMEM2_HAS(bp, drv_capabilities_flag)) {
u32 v;
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index bbb3641..3aa993b 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -1498,12 +1498,16 @@
if (BNXT_VF(bp))
goto async_event_process_exit;
- if (data1 & 0x20000) {
+
+ /* print unsupported speed warning in forced speed mode only */
+ if (!(link_info->autoneg & BNXT_AUTONEG_SPEED) &&
+ (data1 & 0x20000)) {
u16 fw_speed = link_info->force_link_speed;
u32 speed = bnxt_fw_to_ethtool_speed(fw_speed);
- netdev_warn(bp->dev, "Link speed %d no longer supported\n",
- speed);
+ if (speed != SPEED_UNKNOWN)
+ netdev_warn(bp->dev, "Link speed %d no longer supported\n",
+ speed);
}
set_bit(BNXT_LINK_SPEED_CHNG_SP_EVENT, &bp->sp_event);
/* fall thru */
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
index 60e2af8..393cce3 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
@@ -68,7 +68,7 @@
netdev_err(bp->dev, "vf ndo called though sriov is disabled\n");
return -EINVAL;
}
- if (vf_id >= bp->pf.max_vfs) {
+ if (vf_id >= bp->pf.active_vfs) {
netdev_err(bp->dev, "Invalid VF id %d\n", vf_id);
return -EINVAL;
}
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index bb22d32..795a133 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -10049,6 +10049,16 @@
tw32(GRC_MODE, tp->grc_mode | val);
+ /* On one of the AMD platform, MRRS is restricted to 4000 because of
+ * south bridge limitation. As a workaround, Driver is setting MRRS
+ * to 2048 instead of default 4096.
+ */
+ if (tp->pdev->subsystem_vendor == PCI_VENDOR_ID_DELL &&
+ tp->pdev->subsystem_device == TG3PCI_SUBDEVICE_ID_DELL_5762) {
+ val = tr32(TG3PCI_DEV_STATUS_CTRL) & ~MAX_READ_REQ_MASK;
+ tw32(TG3PCI_DEV_STATUS_CTRL, val | MAX_READ_REQ_SIZE_2048);
+ }
+
/* Setup the timer prescalar register. Clock is always 66Mhz. */
val = tr32(GRC_MISC_CFG);
val &= ~0xff;
@@ -14228,7 +14238,8 @@
*/
if (tg3_asic_rev(tp) == ASIC_REV_57766 ||
tg3_asic_rev(tp) == ASIC_REV_5717 ||
- tg3_asic_rev(tp) == ASIC_REV_5719)
+ tg3_asic_rev(tp) == ASIC_REV_5719 ||
+ tg3_asic_rev(tp) == ASIC_REV_5720)
reset_phy = true;
err = tg3_restart_hw(tp, reset_phy);
diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h
index 3b5e98e..6e51b79 100644
--- a/drivers/net/ethernet/broadcom/tg3.h
+++ b/drivers/net/ethernet/broadcom/tg3.h
@@ -95,6 +95,7 @@
#define TG3PCI_SUBDEVICE_ID_DELL_JAGUAR 0x0106
#define TG3PCI_SUBDEVICE_ID_DELL_MERLOT 0x0109
#define TG3PCI_SUBDEVICE_ID_DELL_SLIM_MERLOT 0x010a
+#define TG3PCI_SUBDEVICE_ID_DELL_5762 0x07f0
#define TG3PCI_SUBVENDOR_ID_COMPAQ PCI_VENDOR_ID_COMPAQ
#define TG3PCI_SUBDEVICE_ID_COMPAQ_BANSHEE 0x007c
#define TG3PCI_SUBDEVICE_ID_COMPAQ_BANSHEE_2 0x009a
@@ -280,6 +281,9 @@
#define TG3PCI_STD_RING_PROD_IDX 0x00000098 /* 64-bit */
#define TG3PCI_RCV_RET_RING_CON_IDX 0x000000a0 /* 64-bit */
/* 0xa8 --> 0xb8 unused */
+#define TG3PCI_DEV_STATUS_CTRL 0x000000b4
+#define MAX_READ_REQ_SIZE_2048 0x00004000
+#define MAX_READ_REQ_MASK 0x00007000
#define TG3PCI_DUAL_MAC_CTRL 0x000000b8
#define DUAL_MAC_CTRL_CH_MASK 0x00000003
#define DUAL_MAC_CTRL_ID 0x00000004
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
index 8a37012..c75d4ea9 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
@@ -1576,6 +1576,11 @@
nic->pdev = pdev;
nic->pnicvf = nic;
nic->max_queues = qcount;
+ /* If no of CPUs are too low, there won't be any queues left
+ * for XDP_TX, hence double it.
+ */
+ if (!nic->t88)
+ nic->max_queues *= 2;
/* MAP VF's configuration registers */
nic->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
index 9e073fb..6fd3be6 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
@@ -2596,7 +2596,6 @@
}
#define EEPROM_STAT_ADDR 0x7bfc
-#define VPD_SIZE 0x800
#define VPD_BASE 0x400
#define VPD_BASE_OLD 0
#define VPD_LEN 1024
@@ -2634,15 +2633,6 @@
if (!vpd)
return -ENOMEM;
- /* We have two VPD data structures stored in the adapter VPD area.
- * By default, Linux calculates the size of the VPD area by traversing
- * the first VPD area at offset 0x0, so we need to tell the OS what
- * our real VPD size is.
- */
- ret = pci_set_vpd_size(adapter->pdev, VPD_SIZE);
- if (ret < 0)
- goto out;
-
/* Card information normally starts at VPD_BASE but early cards had
* it at 0.
*/
diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index 2625872..0437149 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -28,6 +28,7 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/netdevice.h>
+#include <linux/of.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
#include <net/ip.h>
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 9170918..05e5b38 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -3209,7 +3209,7 @@
}
#ifdef CONFIG_OF
-static void fec_reset_phy(struct platform_device *pdev)
+static int fec_reset_phy(struct platform_device *pdev)
{
int err, phy_reset;
bool active_high = false;
@@ -3217,7 +3217,7 @@
struct device_node *np = pdev->dev.of_node;
if (!np)
- return;
+ return 0;
of_property_read_u32(np, "phy-reset-duration", &msec);
/* A sane reset duration should not be longer than 1s */
@@ -3225,8 +3225,10 @@
msec = 1;
phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
- if (!gpio_is_valid(phy_reset))
- return;
+ if (phy_reset == -EPROBE_DEFER)
+ return phy_reset;
+ else if (!gpio_is_valid(phy_reset))
+ return 0;
active_high = of_property_read_bool(np, "phy-reset-active-high");
@@ -3235,7 +3237,7 @@
"phy-reset");
if (err) {
dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err);
- return;
+ return err;
}
if (msec > 20)
@@ -3244,14 +3246,17 @@
usleep_range(msec * 1000, msec * 1000 + 1000);
gpio_set_value_cansleep(phy_reset, !active_high);
+
+ return 0;
}
#else /* CONFIG_OF */
-static void fec_reset_phy(struct platform_device *pdev)
+static int fec_reset_phy(struct platform_device *pdev)
{
/*
* In case of platform probe, the reset has been done
* by machine code.
*/
+ return 0;
}
#endif /* CONFIG_OF */
@@ -3422,6 +3427,7 @@
if (ret) {
dev_err(&pdev->dev,
"Failed to enable phy regulator: %d\n", ret);
+ clk_disable_unprepare(fep->clk_ipg);
goto failed_regulator;
}
} else {
@@ -3434,7 +3440,9 @@
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
- fec_reset_phy(pdev);
+ ret = fec_reset_phy(pdev);
+ if (ret)
+ goto failed_reset;
if (fep->bufdesc_ex)
fec_ptp_init(pdev);
@@ -3495,8 +3503,10 @@
fec_ptp_stop(pdev);
if (fep->reg_phy)
regulator_disable(fep->reg_phy);
+failed_reset:
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
failed_regulator:
- clk_disable_unprepare(fep->clk_ipg);
failed_clk_ipg:
fec_enet_clk_enable(ndev, false);
failed_clk:
@@ -3523,6 +3533,8 @@
fec_enet_mii_remove(fep);
if (fep->reg_phy)
regulator_disable(fep->reg_phy);
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
if (of_phy_is_fixed_link(np))
of_phy_deregister_fixed_link(np);
of_node_put(fep->phy_node);
diff --git a/drivers/net/ethernet/freescale/gianfar_ptp.c b/drivers/net/ethernet/freescale/gianfar_ptp.c
index 5779881..ec4b699 100644
--- a/drivers/net/ethernet/freescale/gianfar_ptp.c
+++ b/drivers/net/ethernet/freescale/gianfar_ptp.c
@@ -314,11 +314,10 @@
now = tmr_cnt_read(etsects);
now += delta;
tmr_cnt_write(etsects, now);
+ set_fipers(etsects);
spin_unlock_irqrestore(&etsects->lock, flags);
- set_fipers(etsects);
-
return 0;
}
diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c
index f76d332..ef9bc26 100644
--- a/drivers/net/ethernet/freescale/ucc_geth.c
+++ b/drivers/net/ethernet/freescale/ucc_geth.c
@@ -2594,11 +2594,10 @@
} else if (ugeth->ug_info->uf_info.bd_mem_part ==
MEM_PART_MURAM) {
out_be32(&ugeth->p_send_q_mem_reg->sqqd[i].bd_ring_base,
- (u32) immrbar_virt_to_phys(ugeth->
- p_tx_bd_ring[i]));
+ (u32)qe_muram_dma(ugeth->p_tx_bd_ring[i]));
out_be32(&ugeth->p_send_q_mem_reg->sqqd[i].
last_bd_completed_address,
- (u32) immrbar_virt_to_phys(endOfRing));
+ (u32)qe_muram_dma(endOfRing));
}
}
@@ -2844,8 +2843,7 @@
} else if (ugeth->ug_info->uf_info.bd_mem_part ==
MEM_PART_MURAM) {
out_be32(&ugeth->p_rx_bd_qs_tbl[i].externalbdbaseptr,
- (u32) immrbar_virt_to_phys(ugeth->
- p_rx_bd_ring[i]));
+ (u32)qe_muram_dma(ugeth->p_rx_bd_ring[i]));
}
/* rest of fields handled by QE */
}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
index 2d0cb60..b7c8433 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
@@ -773,8 +773,9 @@
memcpy(key, ppe_cb->rss_key, HNS_PPEV2_RSS_KEY_SIZE);
/* update the current hash->queue mappings from the shadow RSS table */
- memcpy(indir, ppe_cb->rss_indir_table,
- HNS_PPEV2_RSS_IND_TBL_SIZE * sizeof(*indir));
+ if (indir)
+ memcpy(indir, ppe_cb->rss_indir_table,
+ HNS_PPEV2_RSS_IND_TBL_SIZE * sizeof(*indir));
return 0;
}
@@ -785,15 +786,19 @@
struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle);
/* set the RSS Hash Key if specififed by the user */
- if (key)
- hns_ppe_set_rss_key(ppe_cb, (u32 *)key);
+ if (key) {
+ memcpy(ppe_cb->rss_key, key, HNS_PPEV2_RSS_KEY_SIZE);
+ hns_ppe_set_rss_key(ppe_cb, ppe_cb->rss_key);
+ }
- /* update the shadow RSS table with user specified qids */
- memcpy(ppe_cb->rss_indir_table, indir,
- HNS_PPEV2_RSS_IND_TBL_SIZE * sizeof(*indir));
+ if (indir) {
+ /* update the shadow RSS table with user specified qids */
+ memcpy(ppe_cb->rss_indir_table, indir,
+ HNS_PPEV2_RSS_IND_TBL_SIZE * sizeof(*indir));
- /* now update the hardware */
- hns_ppe_set_indir_table(ppe_cb, ppe_cb->rss_indir_table);
+ /* now update the hardware */
+ hns_ppe_set_indir_table(ppe_cb, ppe_cb->rss_indir_table);
+ }
return 0;
}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c
index 1e1eb92..34b5e69 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c
@@ -86,12 +86,11 @@
dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_RX_EN_B, 0);
}
-/**
-*hns_gmac_get_en - get port enable
-*@mac_drv:mac device
-*@rx:rx enable
-*@tx:tx enable
-*/
+/* hns_gmac_get_en - get port enable
+ * @mac_drv:mac device
+ * @rx:rx enable
+ * @tx:tx enable
+ */
static void hns_gmac_get_en(void *mac_drv, u32 *rx, u32 *tx)
{
struct mac_driver *drv = (struct mac_driver *)mac_drv;
@@ -672,7 +671,7 @@
static int hns_gmac_get_sset_count(int stringset)
{
- if (stringset == ETH_SS_STATS)
+ if (stringset == ETH_SS_STATS || stringset == ETH_SS_PRIV_FLAGS)
return ARRAY_SIZE(g_gmac_stats_string);
return 0;
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h
index c494fc5..62a1299 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h
@@ -70,7 +70,7 @@
};
#define DSAF_STATS_READ(p, offset) (*((u64 *)((u8 *)(p) + (offset))))
-#define HNS_DSAF_IS_DEBUG(dev) (dev->dsaf_mode == DSAF_MODE_DISABLE_SP)
+#define HNS_DSAF_IS_DEBUG(dev) ((dev)->dsaf_mode == DSAF_MODE_DISABLE_SP)
enum hal_dsaf_mode {
HRD_DSAF_NO_DSAF_MODE = 0x0,
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c
index 6ea8722..4ecb809 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c
@@ -422,7 +422,7 @@
int hns_ppe_get_sset_count(int stringset)
{
- if (stringset == ETH_SS_STATS)
+ if (stringset == ETH_SS_STATS || stringset == ETH_SS_PRIV_FLAGS)
return ETH_PPE_STATIC_NUM;
return 0;
}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c
index f0ed80d6..fbbbbff 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c
@@ -430,7 +430,6 @@
static int hns_rcb_get_port_in_comm(
struct rcb_common_cb *rcb_common, int ring_idx)
{
-
return ring_idx / (rcb_common->max_q_per_vf * rcb_common->max_vfn);
}
@@ -799,7 +798,7 @@
*/
int hns_rcb_get_ring_sset_count(int stringset)
{
- if (stringset == ETH_SS_STATS)
+ if (stringset == ETH_SS_STATS || stringset == ETH_SS_PRIV_FLAGS)
return HNS_RING_STATIC_REG_NUM;
return 0;
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c
index 8f4f0e8..d1c868c 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c
@@ -776,7 +776,7 @@
*/
static int hns_xgmac_get_sset_count(int stringset)
{
- if (stringset == ETH_SS_STATS)
+ if (stringset == ETH_SS_STATS || stringset == ETH_SS_PRIV_FLAGS)
return ARRAY_SIZE(g_xgmac_stats_string);
return 0;
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c
index c06845b..111e1aa 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c
@@ -299,9 +299,9 @@
mtu);
}
-int hns_nic_net_xmit_hw(struct net_device *ndev,
- struct sk_buff *skb,
- struct hns_nic_ring_data *ring_data)
+netdev_tx_t hns_nic_net_xmit_hw(struct net_device *ndev,
+ struct sk_buff *skb,
+ struct hns_nic_ring_data *ring_data)
{
struct hns_nic_priv *priv = netdev_priv(ndev);
struct hnae_ring *ring = ring_data->ring;
@@ -360,6 +360,10 @@
dev_queue = netdev_get_tx_queue(ndev, skb->queue_mapping);
netdev_tx_sent_queue(dev_queue, skb->len);
+ netif_trans_update(ndev);
+ ndev->stats.tx_bytes += skb->len;
+ ndev->stats.tx_packets++;
+
wmb(); /* commit all data before submit */
assert(skb->queue_mapping < priv->ae_handle->q_num);
hnae_queue_xmit(priv->ae_handle->qs[skb->queue_mapping], buf_num);
@@ -511,7 +515,8 @@
int last_offset;
bool twobufs;
- twobufs = ((PAGE_SIZE < 8192) && hnae_buf_size(ring) == HNS_BUFFER_SIZE_2048);
+ twobufs = ((PAGE_SIZE < 8192) &&
+ hnae_buf_size(ring) == HNS_BUFFER_SIZE_2048);
desc = &ring->desc[ring->next_to_clean];
size = le16_to_cpu(desc->rx.size);
@@ -1407,17 +1412,11 @@
struct net_device *ndev)
{
struct hns_nic_priv *priv = netdev_priv(ndev);
- int ret;
assert(skb->queue_mapping < ndev->ae_handle->q_num);
- ret = hns_nic_net_xmit_hw(ndev, skb,
- &tx_ring_data(priv, skb->queue_mapping));
- if (ret == NETDEV_TX_OK) {
- netif_trans_update(ndev);
- ndev->stats.tx_bytes += skb->len;
- ndev->stats.tx_packets++;
- }
- return (netdev_tx_t)ret;
+
+ return hns_nic_net_xmit_hw(ndev, skb,
+ &tx_ring_data(priv, skb->queue_mapping));
}
static int hns_nic_change_mtu(struct net_device *ndev, int new_mtu)
@@ -1700,7 +1699,7 @@
static void hns_nic_service_event_complete(struct hns_nic_priv *priv)
{
WARN_ON(!test_bit(NIC_STATE_SERVICE_SCHED, &priv->state));
-
+ /* make sure to commit the things */
smp_mb__before_atomic();
clear_bit(NIC_STATE_SERVICE_SCHED, &priv->state);
}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.h b/drivers/net/ethernet/hisilicon/hns/hns_enet.h
index 5b412de..7bc6a6e 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_enet.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.h
@@ -91,8 +91,8 @@
void hns_nic_net_reset(struct net_device *ndev);
void hns_nic_net_reinit(struct net_device *netdev);
int hns_nic_init_phy(struct net_device *ndev, struct hnae_handle *h);
-int hns_nic_net_xmit_hw(struct net_device *ndev,
- struct sk_buff *skb,
- struct hns_nic_ring_data *ring_data);
+netdev_tx_t hns_nic_net_xmit_hw(struct net_device *ndev,
+ struct sk_buff *skb,
+ struct hns_nic_ring_data *ring_data);
#endif /**__HNS_ENET_H */
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
index 87d5c94..86a496d 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
@@ -1252,12 +1252,10 @@
ops = priv->ae_handle->dev->ops;
- /* currently hfunc can only be Toeplitz hash */
- if (key ||
- (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
+ if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) {
+ netdev_err(netdev, "Invalid hfunc!\n");
return -EOPNOTSUPP;
- if (!indir)
- return 0;
+ }
return ops->set_rss(priv->ae_handle, indir, key, hfunc);
}
diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c
index 7c6c146..49094c9 100644
--- a/drivers/net/ethernet/ibm/ibmvnic.c
+++ b/drivers/net/ethernet/ibm/ibmvnic.c
@@ -511,6 +511,23 @@
return -ENOMEM;
}
+static void disable_sub_crqs(struct ibmvnic_adapter *adapter)
+{
+ int i;
+
+ if (adapter->tx_scrq) {
+ for (i = 0; i < adapter->req_tx_queues; i++)
+ if (adapter->tx_scrq[i])
+ disable_irq(adapter->tx_scrq[i]->irq);
+ }
+
+ if (adapter->rx_scrq) {
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ if (adapter->rx_scrq[i])
+ disable_irq(adapter->rx_scrq[i]->irq);
+ }
+}
+
static int ibmvnic_close(struct net_device *netdev)
{
struct ibmvnic_adapter *adapter = netdev_priv(netdev);
@@ -519,6 +536,7 @@
int i;
adapter->closing = true;
+ disable_sub_crqs(adapter);
for (i = 0; i < adapter->req_rx_queues; i++)
napi_disable(&adapter->napi[i]);
diff --git a/drivers/net/ethernet/intel/e1000/e1000.h b/drivers/net/ethernet/intel/e1000/e1000.h
index d7bdea7..8fd2458 100644
--- a/drivers/net/ethernet/intel/e1000/e1000.h
+++ b/drivers/net/ethernet/intel/e1000/e1000.h
@@ -331,7 +331,8 @@
enum e1000_state_t {
__E1000_TESTING,
__E1000_RESETTING,
- __E1000_DOWN
+ __E1000_DOWN,
+ __E1000_DISABLED
};
#undef pr_fmt
diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c
index f42129d..dd112aa 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_main.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_main.c
@@ -940,7 +940,7 @@
static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct net_device *netdev;
- struct e1000_adapter *adapter;
+ struct e1000_adapter *adapter = NULL;
struct e1000_hw *hw;
static int cards_found;
@@ -950,6 +950,7 @@
u16 tmp = 0;
u16 eeprom_apme_mask = E1000_EEPROM_APME;
int bars, need_ioport;
+ bool disable_dev = false;
/* do not allocate ioport bars when not needed */
need_ioport = e1000_is_need_ioport(pdev);
@@ -1250,11 +1251,13 @@
iounmap(hw->ce4100_gbe_mdio_base_virt);
iounmap(hw->hw_addr);
err_ioremap:
+ disable_dev = !test_and_set_bit(__E1000_DISABLED, &adapter->flags);
free_netdev(netdev);
err_alloc_etherdev:
pci_release_selected_regions(pdev, bars);
err_pci_reg:
- pci_disable_device(pdev);
+ if (!adapter || disable_dev)
+ pci_disable_device(pdev);
return err;
}
@@ -1272,6 +1275,7 @@
struct net_device *netdev = pci_get_drvdata(pdev);
struct e1000_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
+ bool disable_dev;
e1000_down_and_stop(adapter);
e1000_release_manageability(adapter);
@@ -1290,9 +1294,11 @@
iounmap(hw->flash_address);
pci_release_selected_regions(pdev, adapter->bars);
+ disable_dev = !test_and_set_bit(__E1000_DISABLED, &adapter->flags);
free_netdev(netdev);
- pci_disable_device(pdev);
+ if (disable_dev)
+ pci_disable_device(pdev);
}
/**
@@ -5166,7 +5172,8 @@
if (netif_running(netdev))
e1000_free_irq(adapter);
- pci_disable_device(pdev);
+ if (!test_and_set_bit(__E1000_DISABLED, &adapter->flags))
+ pci_disable_device(pdev);
return 0;
}
@@ -5210,6 +5217,10 @@
pr_err("Cannot enable PCI device from suspend\n");
return err;
}
+
+ /* flush memory to make sure state is correct */
+ smp_mb__before_atomic();
+ clear_bit(__E1000_DISABLED, &adapter->flags);
pci_set_master(pdev);
pci_enable_wake(pdev, PCI_D3hot, 0);
@@ -5284,7 +5295,9 @@
if (netif_running(netdev))
e1000_down(adapter);
- pci_disable_device(pdev);
+
+ if (!test_and_set_bit(__E1000_DISABLED, &adapter->flags))
+ pci_disable_device(pdev);
/* Request a slot slot reset. */
return PCI_ERS_RESULT_NEED_RESET;
@@ -5312,6 +5325,10 @@
pr_err("Cannot re-enable PCI device after reset.\n");
return PCI_ERS_RESULT_DISCONNECT;
}
+
+ /* flush memory to make sure state is correct */
+ smp_mb__before_atomic();
+ clear_bit(__E1000_DISABLED, &adapter->flags);
pci_set_master(pdev);
pci_enable_wake(pdev, PCI_D3hot, 0);
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index 0feddf3..528a926 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -3528,6 +3528,12 @@
switch (hw->mac.type) {
case e1000_pch2lan:
+ /* Stable 96MHz frequency */
+ incperiod = INCPERIOD_96MHz;
+ incvalue = INCVALUE_96MHz;
+ shift = INCVALUE_SHIFT_96MHz;
+ adapter->cc.shift = shift + INCPERIOD_SHIFT_96MHz;
+ break;
case e1000_pch_lpt:
if (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI) {
/* Stable 96MHz frequency */
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
index 5241e08..7041d83 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
@@ -942,7 +942,7 @@
memset(data, 0, sizeof(*data) * FM10K_TEST_LEN);
- if (FM10K_REMOVED(hw)) {
+ if (FM10K_REMOVED(hw->hw_addr)) {
netif_err(interface, drv, dev,
"Interface removed - test blocked\n");
eth_test->flags |= ETH_TEST_FL_FAILED;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
index 92bc884..f456946 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
@@ -1135,6 +1135,11 @@
struct i40e_hw *hw = &np->vsi->back->hw;
u32 val;
+#define X722_EEPROM_SCOPE_LIMIT 0x5B9FFF
+ if (hw->mac.type == I40E_MAC_X722) {
+ val = X722_EEPROM_SCOPE_LIMIT + 1;
+ return val;
+ }
val = (rd32(hw, I40E_GLPCI_LBARCTRL)
& I40E_GLPCI_LBARCTRL_FL_SIZE_MASK)
>> I40E_GLPCI_LBARCTRL_FL_SIZE_SHIFT;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index becffd1..57c7456 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -11142,10 +11142,12 @@
round_jiffies(jiffies + pf->service_timer_period));
/* add this PF to client device list and launch a client service task */
- err = i40e_lan_add_device(pf);
- if (err)
- dev_info(&pdev->dev, "Failed to add PF to client API service list: %d\n",
- err);
+ if (pf->flags & I40E_FLAG_IWARP_ENABLED) {
+ err = i40e_lan_add_device(pf);
+ if (err)
+ dev_info(&pdev->dev, "Failed to add PF to client API service list: %d\n",
+ err);
+ }
#ifdef I40E_FCOE
/* create FCoE interface */
@@ -11323,10 +11325,11 @@
i40e_vsi_release(pf->vsi[pf->lan_vsi]);
/* remove attached clients */
- ret_code = i40e_lan_del_device(pf);
- if (ret_code) {
- dev_warn(&pdev->dev, "Failed to delete client device: %d\n",
- ret_code);
+ if (pf->flags & I40E_FLAG_IWARP_ENABLED) {
+ ret_code = i40e_lan_del_device(pf);
+ if (ret_code)
+ dev_warn(&pdev->dev, "Failed to delete client device: %d\n",
+ ret_code);
}
/* shutdown and destroy the HMC */
diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c
index 954efe3..abe290b 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c
@@ -292,14 +292,14 @@
{
enum i40e_status_code ret_code = 0;
- if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) {
- ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
- if (!ret_code) {
+ ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
+ if (!ret_code) {
+ if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) {
ret_code = i40e_read_nvm_word_aq(hw, offset, data);
- i40e_release_nvm(hw);
+ } else {
+ ret_code = i40e_read_nvm_word_srctl(hw, offset, data);
}
- } else {
- ret_code = i40e_read_nvm_word_srctl(hw, offset, data);
+ i40e_release_nvm(hw);
}
return ret_code;
}
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
index c543039..2e12ccf 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
@@ -1820,6 +1820,7 @@
*/
if (unlikely(i40e_test_staterr(rx_desc, BIT(I40E_RXD_QW1_ERROR_SHIFT)))) {
dev_kfree_skb_any(skb);
+ skb = NULL;
continue;
}
@@ -2670,10 +2671,30 @@
/* Walk through fragments adding latest fragment, testing it, and
* then removing stale fragments from the sum.
*/
- stale = &skb_shinfo(skb)->frags[0];
- for (;;) {
+ for (stale = &skb_shinfo(skb)->frags[0];; stale++) {
+ int stale_size = skb_frag_size(stale);
+
sum += skb_frag_size(frag++);
+ /* The stale fragment may present us with a smaller
+ * descriptor than the actual fragment size. To account
+ * for that we need to remove all the data on the front and
+ * figure out what the remainder would be in the last
+ * descriptor associated with the fragment.
+ */
+ if (stale_size > I40E_MAX_DATA_PER_TXD) {
+ int align_pad = -(stale->page_offset) &
+ (I40E_MAX_READ_REQ_SIZE - 1);
+
+ sum -= align_pad;
+ stale_size -= align_pad;
+
+ do {
+ sum -= I40E_MAX_DATA_PER_TXD_ALIGNED;
+ stale_size -= I40E_MAX_DATA_PER_TXD_ALIGNED;
+ } while (stale_size > I40E_MAX_DATA_PER_TXD);
+ }
+
/* if sum is negative we failed to make sufficient progress */
if (sum < 0)
return true;
@@ -2681,7 +2702,7 @@
if (!nr_frags--)
break;
- sum -= skb_frag_size(stale++);
+ sum -= stale_size;
}
return false;
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
index c03800d..7bfed44 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
@@ -1262,6 +1262,7 @@
*/
if (unlikely(i40e_test_staterr(rx_desc, BIT(I40E_RXD_QW1_ERROR_SHIFT)))) {
dev_kfree_skb_any(skb);
+ skb = NULL;
continue;
}
@@ -1872,10 +1873,30 @@
/* Walk through fragments adding latest fragment, testing it, and
* then removing stale fragments from the sum.
*/
- stale = &skb_shinfo(skb)->frags[0];
- for (;;) {
+ for (stale = &skb_shinfo(skb)->frags[0];; stale++) {
+ int stale_size = skb_frag_size(stale);
+
sum += skb_frag_size(frag++);
+ /* The stale fragment may present us with a smaller
+ * descriptor than the actual fragment size. To account
+ * for that we need to remove all the data on the front and
+ * figure out what the remainder would be in the last
+ * descriptor associated with the fragment.
+ */
+ if (stale_size > I40E_MAX_DATA_PER_TXD) {
+ int align_pad = -(stale->page_offset) &
+ (I40E_MAX_READ_REQ_SIZE - 1);
+
+ sum -= align_pad;
+ stale_size -= align_pad;
+
+ do {
+ sum -= I40E_MAX_DATA_PER_TXD_ALIGNED;
+ stale_size -= I40E_MAX_DATA_PER_TXD_ALIGNED;
+ } while (stale_size > I40E_MAX_DATA_PER_TXD);
+ }
+
/* if sum is negative we failed to make sufficient progress */
if (sum < 0)
return true;
@@ -1883,7 +1904,7 @@
if (!nr_frags--)
break;
- sum -= skb_frag_size(stale++);
+ sum -= stale_size;
}
return false;
diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
index 508e72c..d665de8 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
@@ -80,7 +80,7 @@
#define IXGBEVF_QUEUE_STATS_LEN ( \
(((struct ixgbevf_adapter *)netdev_priv(netdev))->num_tx_queues + \
((struct ixgbevf_adapter *)netdev_priv(netdev))->num_rx_queues) * \
- (sizeof(struct ixgbe_stats) / sizeof(u64)))
+ (sizeof(struct ixgbevf_stats) / sizeof(u64)))
#define IXGBEVF_GLOBAL_STATS_LEN ARRAY_SIZE(ixgbevf_gstrings_stats)
#define IXGBEVF_STATS_LEN (IXGBEVF_GLOBAL_STATS_LEN + IXGBEVF_QUEUE_STATS_LEN)
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 4832223..20de37a 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -1842,11 +1842,12 @@
/* set GE2 TUNE */
regmap_write(eth->pctl, GPIO_BIAS_CTRL, 0x0);
- /* GE1, Force 1000M/FD, FC ON */
- mtk_w32(eth, MAC_MCR_FIXED_LINK, MTK_MAC_MCR(0));
-
- /* GE2, Force 1000M/FD, FC ON */
- mtk_w32(eth, MAC_MCR_FIXED_LINK, MTK_MAC_MCR(1));
+ /* Set linkdown as the default for each GMAC. Its own MCR would be set
+ * up with the more appropriate value when mtk_phy_link_adjust call is
+ * being invoked.
+ */
+ for (i = 0; i < MTK_MAC_COUNT; i++)
+ mtk_w32(eth, 0, MTK_MAC_MCR(i));
/* Enable RX VLan Offloading */
mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index 1e2c8ec..bea9ae3 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -809,6 +809,7 @@
bool dynamic)
{
char *sfd_pl;
+ u8 num_rec;
int err;
sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
@@ -818,9 +819,16 @@
mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
mlxsw_reg_sfd_uc_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic),
mac, fid, action, local_port);
+ num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
- kfree(sfd_pl);
+ if (err)
+ goto out;
+ if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl))
+ err = -EBUSY;
+
+out:
+ kfree(sfd_pl);
return err;
}
@@ -845,6 +853,7 @@
bool adding, bool dynamic)
{
char *sfd_pl;
+ u8 num_rec;
int err;
sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
@@ -855,9 +864,16 @@
mlxsw_reg_sfd_uc_lag_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic),
mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP,
lag_vid, lag_id);
+ num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
- kfree(sfd_pl);
+ if (err)
+ goto out;
+ if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl))
+ err = -EBUSY;
+
+out:
+ kfree(sfd_pl);
return err;
}
@@ -891,6 +907,7 @@
u16 fid, u16 mid, bool adding)
{
char *sfd_pl;
+ u8 num_rec;
int err;
sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
@@ -900,7 +917,15 @@
mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
mlxsw_reg_sfd_mc_pack(sfd_pl, 0, addr, fid,
MLXSW_REG_SFD_REC_ACTION_NOP, mid);
+ num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
+ if (err)
+ goto out;
+
+ if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl))
+ err = -EBUSY;
+
+out:
kfree(sfd_pl);
return err;
}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.c b/drivers/net/ethernet/qlogic/qed/qed_cxt.c
index ed014bd..457e304 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c
@@ -271,16 +271,34 @@
u32 per_vf_tids;
};
-static void qed_cxt_tm_iids(struct qed_cxt_mngr *p_mngr,
+static void qed_cxt_tm_iids(struct qed_hwfn *p_hwfn,
+ struct qed_cxt_mngr *p_mngr,
struct qed_tm_iids *iids)
{
- u32 i, j;
+ bool tm_vf_required = false;
+ bool tm_required = false;
+ int i, j;
- for (i = 0; i < MAX_CONN_TYPES; i++) {
+ /* Timers is a special case -> we don't count how many cids require
+ * timers but what's the max cid that will be used by the timer block.
+ * therefore we traverse in reverse order, and once we hit a protocol
+ * that requires the timers memory, we'll sum all the protocols up
+ * to that one.
+ */
+ for (i = MAX_CONN_TYPES - 1; i >= 0; i--) {
struct qed_conn_type_cfg *p_cfg = &p_mngr->conn_cfg[i];
- if (tm_cid_proto(i)) {
+ if (tm_cid_proto(i) || tm_required) {
+ if (p_cfg->cid_count)
+ tm_required = true;
+
iids->pf_cids += p_cfg->cid_count;
+ }
+
+ if (tm_cid_proto(i) || tm_vf_required) {
+ if (p_cfg->cids_per_vf)
+ tm_vf_required = true;
+
iids->per_vf_cids += p_cfg->cids_per_vf;
}
}
@@ -696,7 +714,7 @@
/* TM PF */
p_cli = &p_mngr->clients[ILT_CLI_TM];
- qed_cxt_tm_iids(p_mngr, &tm_iids);
+ qed_cxt_tm_iids(p_hwfn, p_mngr, &tm_iids);
total = tm_iids.pf_cids + tm_iids.pf_tids_total;
if (total) {
p_blk = &p_cli->pf_blks[0];
@@ -1591,7 +1609,7 @@
u8 i;
memset(&tm_iids, 0, sizeof(tm_iids));
- qed_cxt_tm_iids(p_mngr, &tm_iids);
+ qed_cxt_tm_iids(p_hwfn, p_mngr, &tm_iids);
/* @@@TBD No pre-scan for now */
diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c
index 333c744..dba3fbe 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_main.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_main.c
@@ -711,7 +711,8 @@
cdev->int_params.fp_msix_cnt = cdev->int_params.out.num_vectors -
cdev->num_hwfns;
- if (!IS_ENABLED(CONFIG_QED_RDMA))
+ if (!IS_ENABLED(CONFIG_QED_RDMA) ||
+ QED_LEADING_HWFN(cdev)->hw_info.personality != QED_PCI_ETH_ROCE)
return 0;
for_each_hwfn(cdev, i)
diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c
index d2d6621..48bc5c1 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c
@@ -3573,6 +3573,7 @@
void qed_inform_vf_link_state(struct qed_hwfn *hwfn)
{
+ struct qed_hwfn *lead_hwfn = QED_LEADING_HWFN(hwfn->cdev);
struct qed_mcp_link_capabilities caps;
struct qed_mcp_link_params params;
struct qed_mcp_link_state link;
@@ -3589,9 +3590,15 @@
if (!vf_info)
continue;
- memcpy(¶ms, qed_mcp_get_link_params(hwfn), sizeof(params));
- memcpy(&link, qed_mcp_get_link_state(hwfn), sizeof(link));
- memcpy(&caps, qed_mcp_get_link_capabilities(hwfn),
+ /* Only hwfn0 is actually interested in the link speed.
+ * But since only it would receive an MFW indication of link,
+ * need to take configuration from it - otherwise things like
+ * rate limiting for hwfn1 VF would not work.
+ */
+ memcpy(¶ms, qed_mcp_get_link_params(lead_hwfn),
+ sizeof(params));
+ memcpy(&link, qed_mcp_get_link_state(lead_hwfn), sizeof(link));
+ memcpy(&caps, qed_mcp_get_link_capabilities(lead_hwfn),
sizeof(caps));
/* Modify link according to the VF's configured link state */
diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.c b/drivers/net/ethernet/qlogic/qed/qed_vf.c
index abf5bf1..0645124 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_vf.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_vf.c
@@ -204,7 +204,7 @@
/* send acquire request */
rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp));
if (rc)
- return rc;
+ goto exit;
/* copy acquire response from buffer to p_hwfn */
memcpy(&p_iov->acquire_resp, resp, sizeof(p_iov->acquire_resp));
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
index d710705..2f656f3 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
@@ -128,6 +128,8 @@
return 0;
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
+ if (!pos)
+ return 0;
pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset);
pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
index 10d6059..f4074e2 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
@@ -38,6 +38,7 @@
{
u32 value = readl(ioaddr + PTP_TCR);
unsigned long data;
+ u32 reg_value;
/* For GMAC3.x, 4.x versions, convert the ptp_clock to nano second
* formula = (1/ptp_clock) * 1000000000
@@ -54,10 +55,11 @@
data &= PTP_SSIR_SSINC_MASK;
+ reg_value = data;
if (gmac4)
- data = data << GMAC4_PTP_SSIR_SSINC_SHIFT;
+ reg_value <<= GMAC4_PTP_SSIR_SSINC_SHIFT;
- writel(data, ioaddr + PTP_SSIR);
+ writel(reg_value, ioaddr + PTP_SSIR);
return data;
}
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 3f1971d..2bd1282 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -901,7 +901,8 @@
/* set speed_in input in case RMII mode is used in 100Mbps */
if (phy->speed == 100)
mac_control |= BIT(15);
- else if (phy->speed == 10)
+ /* in band mode only works in 10Mbps RGMII mode */
+ else if ((phy->speed == 10) && phy_interface_is_rgmii(phy))
mac_control |= BIT(18); /* In Band mode */
if (priv->rx_pause)
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index c2ac39a..14f58b6 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -151,6 +151,13 @@
sizeof(struct nvsp_message),
(unsigned long)revoke_packet,
VM_PKT_DATA_INBAND, 0);
+ /* If the failure is because the channel is rescinded;
+ * ignore the failure since we cannot send on a rescinded
+ * channel. This would allow us to properly cleanup
+ * even when the channel is rescinded.
+ */
+ if (device->channel->rescind)
+ ret = 0;
/*
* If we failed here, we might as well return and
* have a leak rather than continue and a bugchk
@@ -211,6 +218,15 @@
sizeof(struct nvsp_message),
(unsigned long)revoke_packet,
VM_PKT_DATA_INBAND, 0);
+
+ /* If the failure is because the channel is rescinded;
+ * ignore the failure since we cannot send on a rescinded
+ * channel. This would allow us to properly cleanup
+ * even when the channel is rescinded.
+ */
+ if (device->channel->rescind)
+ ret = 0;
+
/* If we failed here, we might as well return and
* have a leak rather than continue and a bugchk
*/
diff --git a/drivers/net/ieee802154/adf7242.c b/drivers/net/ieee802154/adf7242.c
index f355df7..1b980f1 100644
--- a/drivers/net/ieee802154/adf7242.c
+++ b/drivers/net/ieee802154/adf7242.c
@@ -888,7 +888,7 @@
.set_cca_ed_level = adf7242_set_cca_ed_level,
};
-static void adf7242_debug(u8 irq1)
+static void adf7242_debug(struct adf7242_local *lp, u8 irq1)
{
#ifdef DEBUG
u8 stat;
@@ -932,7 +932,7 @@
dev_err(&lp->spi->dev, "%s :ERROR IRQ1 = 0x%X\n",
__func__, irq1);
- adf7242_debug(irq1);
+ adf7242_debug(lp, irq1);
xmit = test_bit(FLAG_XMIT, &lp->flags);
diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c
index 627eb82..c747ab6 100644
--- a/drivers/net/ipvlan/ipvlan_core.c
+++ b/drivers/net/ipvlan/ipvlan_core.c
@@ -299,6 +299,10 @@
if (dev_forward_skb(ipvlan->dev, skb) == NET_RX_SUCCESS)
success = true;
} else {
+ if (!ether_addr_equal_64bits(eth_hdr(skb)->h_dest,
+ ipvlan->phy_dev->dev_addr))
+ skb->pkt_type = PACKET_OTHERHOST;
+
ret = RX_HANDLER_ANOTHER;
success = true;
}
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 6d55049..e8ad4d0 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -1377,9 +1377,14 @@
return 0;
unregister_netdev:
+ /* macvlan_uninit would free the macvlan port */
unregister_netdevice(dev);
+ return err;
destroy_macvlan_port:
- if (create)
+ /* the macvlan port may be freed by macvlan_uninit when fail to register.
+ * so we destroy the macvlan port only when it's valid.
+ */
+ if (create && macvlan_port_get_rtnl(dev))
macvlan_port_destroy(port->dev);
return err;
}
diff --git a/drivers/net/phy/mdio-sun4i.c b/drivers/net/phy/mdio-sun4i.c
index 1352965..6425ce0 100644
--- a/drivers/net/phy/mdio-sun4i.c
+++ b/drivers/net/phy/mdio-sun4i.c
@@ -118,8 +118,10 @@
data->regulator = devm_regulator_get(&pdev->dev, "phy");
if (IS_ERR(data->regulator)) {
- if (PTR_ERR(data->regulator) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ if (PTR_ERR(data->regulator) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto err_out_free_mdiobus;
+ }
dev_info(&pdev->dev, "no regulator found\n");
data->regulator = NULL;
diff --git a/drivers/net/phy/mdio-xgene.c b/drivers/net/phy/mdio-xgene.c
index 92af182..39be3b8 100644
--- a/drivers/net/phy/mdio-xgene.c
+++ b/drivers/net/phy/mdio-xgene.c
@@ -197,8 +197,11 @@
}
ret = xgene_enet_ecc_init(pdata);
- if (ret)
+ if (ret) {
+ if (pdata->dev->of_node)
+ clk_disable_unprepare(pdata->clk);
return ret;
+ }
xgene_gmac_reset(pdata);
return 0;
@@ -229,7 +232,7 @@
val = SET_VAL(HSTPHYADX, phy_id) | SET_VAL(HSTREGADX, reg) |
SET_VAL(HSTMIIMWRDAT, data);
- xgene_enet_wr_mdio_csr(addr, MIIM_FIELD_ADDR, data);
+ xgene_enet_wr_mdio_csr(addr, MIIM_FIELD_ADDR, val);
val = HSTLDCMD | SET_VAL(HSTMIIMCMD, MIIM_CMD_LEGACY_WRITE);
xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, val);
@@ -364,8 +367,10 @@
return ret;
mdio_bus = mdiobus_alloc();
- if (!mdio_bus)
- return -ENOMEM;
+ if (!mdio_bus) {
+ ret = -ENOMEM;
+ goto out_clk;
+ }
mdio_bus->name = "APM X-Gene MDIO bus";
@@ -394,7 +399,7 @@
mdio_bus->phy_mask = ~0;
ret = mdiobus_register(mdio_bus);
if (ret)
- goto out;
+ goto out_mdiobus;
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_HANDLE(dev), 1,
acpi_register_phy, NULL, mdio_bus, NULL);
@@ -402,16 +407,20 @@
}
if (ret)
- goto out;
+ goto out_mdiobus;
pdata->mdio_bus = mdio_bus;
xgene_mdio_status = true;
return 0;
-out:
+out_mdiobus:
mdiobus_free(mdio_bus);
+out_clk:
+ if (dev->of_node)
+ clk_disable_unprepare(pdata->clk);
+
return ret;
}
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 6e12401..e2d9ca6 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -925,7 +925,7 @@
break;
case PHY_HALTED:
/* make sure interrupts are re-enabled for the PHY */
- if (phydev->irq != PHY_POLL) {
+ if (phy_interrupt_is_valid(phydev)) {
err = phy_enable_interrupts(phydev);
if (err < 0)
break;
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index fc4c2cc..1e4969d 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -255,7 +255,7 @@
/* Prototypes. */
static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
struct file *file, unsigned int cmd, unsigned long arg);
-static void ppp_xmit_process(struct ppp *ppp);
+static void ppp_xmit_process(struct ppp *ppp, struct sk_buff *skb);
static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
static void ppp_push(struct ppp *ppp);
static void ppp_channel_push(struct channel *pch);
@@ -511,13 +511,12 @@
goto out;
}
- skb_queue_tail(&pf->xq, skb);
-
switch (pf->kind) {
case INTERFACE:
- ppp_xmit_process(PF_TO_PPP(pf));
+ ppp_xmit_process(PF_TO_PPP(pf), skb);
break;
case CHANNEL:
+ skb_queue_tail(&pf->xq, skb);
ppp_channel_push(PF_TO_CHANNEL(pf));
break;
}
@@ -1261,8 +1260,8 @@
put_unaligned_be16(proto, pp);
skb_scrub_packet(skb, !net_eq(ppp->ppp_net, dev_net(dev)));
- skb_queue_tail(&ppp->file.xq, skb);
- ppp_xmit_process(ppp);
+ ppp_xmit_process(ppp, skb);
+
return NETDEV_TX_OK;
outf:
@@ -1416,13 +1415,14 @@
*/
/* Called to do any work queued up on the transmit side that can now be done */
-static void __ppp_xmit_process(struct ppp *ppp)
+static void __ppp_xmit_process(struct ppp *ppp, struct sk_buff *skb)
{
- struct sk_buff *skb;
-
ppp_xmit_lock(ppp);
if (!ppp->closing) {
ppp_push(ppp);
+
+ if (skb)
+ skb_queue_tail(&ppp->file.xq, skb);
while (!ppp->xmit_pending &&
(skb = skb_dequeue(&ppp->file.xq)))
ppp_send_frame(ppp, skb);
@@ -1436,7 +1436,7 @@
ppp_xmit_unlock(ppp);
}
-static void ppp_xmit_process(struct ppp *ppp)
+static void ppp_xmit_process(struct ppp *ppp, struct sk_buff *skb)
{
local_bh_disable();
@@ -1444,7 +1444,7 @@
goto err;
(*this_cpu_ptr(ppp->xmit_recursion))++;
- __ppp_xmit_process(ppp);
+ __ppp_xmit_process(ppp, skb);
(*this_cpu_ptr(ppp->xmit_recursion))--;
local_bh_enable();
@@ -1454,6 +1454,8 @@
err:
local_bh_enable();
+ kfree_skb(skb);
+
if (net_ratelimit())
netdev_err(ppp->dev, "recursion detected\n");
}
@@ -1938,7 +1940,7 @@
if (skb_queue_empty(&pch->file.xq)) {
ppp = pch->ppp;
if (ppp)
- __ppp_xmit_process(ppp);
+ __ppp_xmit_process(ppp, NULL);
}
}
@@ -3157,6 +3159,15 @@
goto outl;
ppp_lock(ppp);
+ spin_lock_bh(&pch->downl);
+ if (!pch->chan) {
+ /* Don't connect unregistered channels */
+ spin_unlock_bh(&pch->downl);
+ ppp_unlock(ppp);
+ ret = -ENOTCONN;
+ goto outl;
+ }
+ spin_unlock_bh(&pch->downl);
if (pch->file.hdrlen > ppp->file.hdrlen)
ppp->file.hdrlen = pch->file.hdrlen;
hdrlen = pch->file.hdrlen + 2; /* for protocol bytes */
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 2668170..a0a9c9d 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -2403,7 +2403,7 @@
if (!nlh) {
err = __send_and_alloc_skb(&skb, team, portid, send_func);
if (err)
- goto errout;
+ return err;
goto send_done;
}
@@ -2688,7 +2688,7 @@
if (!nlh) {
err = __send_and_alloc_skb(&skb, team, portid, send_func);
if (err)
- goto errout;
+ return err;
goto send_done;
}
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index e1e5e84..973e90f 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -531,7 +531,7 @@
static const struct driver_info qmi_wwan_info = {
.description = "WWAN/QMI device",
- .flags = FLAG_WWAN,
+ .flags = FLAG_WWAN | FLAG_SEND_ZLP,
.bind = qmi_wwan_bind,
.unbind = qmi_wwan_unbind,
.manage_power = qmi_wwan_manage_power,
@@ -540,7 +540,7 @@
static const struct driver_info qmi_wwan_info_quirk_dtr = {
.description = "WWAN/QMI device",
- .flags = FLAG_WWAN,
+ .flags = FLAG_WWAN | FLAG_SEND_ZLP,
.bind = qmi_wwan_bind,
.unbind = qmi_wwan_unbind,
.manage_power = qmi_wwan_manage_power,
@@ -805,6 +805,7 @@
{QMI_FIXED_INTF(0x05c6, 0x9084, 4)},
{QMI_FIXED_INTF(0x05c6, 0x920d, 0)},
{QMI_FIXED_INTF(0x05c6, 0x920d, 5)},
+ {QMI_QUIRK_SET_DTR(0x05c6, 0x9625, 4)}, /* YUGA CLM920-NC5 */
{QMI_FIXED_INTF(0x0846, 0x68a2, 8)},
{QMI_FIXED_INTF(0x12d1, 0x140c, 1)}, /* Huawei E173 */
{QMI_FIXED_INTF(0x12d1, 0x14ac, 1)}, /* Huawei E1820 */
@@ -914,6 +915,7 @@
{QMI_FIXED_INTF(0x2357, 0x9000, 4)}, /* TP-LINK MA260 */
{QMI_QUIRK_SET_DTR(0x1bc7, 0x1040, 2)}, /* Telit LE922A */
{QMI_FIXED_INTF(0x1bc7, 0x1100, 3)}, /* Telit ME910 */
+ {QMI_FIXED_INTF(0x1bc7, 0x1101, 3)}, /* Telit ME910 dual modem */
{QMI_FIXED_INTF(0x1bc7, 0x1200, 5)}, /* Telit LE920 */
{QMI_FIXED_INTF(0x1bc7, 0x1201, 2)}, /* Telit LE920 */
{QMI_FIXED_INTF(0x1c9e, 0x9b01, 3)}, /* XS Stick W100-2 from 4G Systems */
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index fbc853e..ee7460e 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -425,6 +425,9 @@
if (ifmp && (dev->ifindex != 0))
peer->ifindex = ifmp->ifi_index;
+ peer->gso_max_size = dev->gso_max_size;
+ peer->gso_max_segs = dev->gso_max_segs;
+
err = register_netdevice(peer);
put_net(net);
net = NULL;
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 983e941..0f5dfb8 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -2816,17 +2816,21 @@
static int vxlan_sock_add(struct vxlan_dev *vxlan)
{
- bool ipv6 = vxlan->flags & VXLAN_F_IPV6;
bool metadata = vxlan->flags & VXLAN_F_COLLECT_METADATA;
+ bool ipv6 = vxlan->flags & VXLAN_F_IPV6 || metadata;
+ bool ipv4 = !ipv6 || metadata;
int ret = 0;
RCU_INIT_POINTER(vxlan->vn4_sock, NULL);
#if IS_ENABLED(CONFIG_IPV6)
RCU_INIT_POINTER(vxlan->vn6_sock, NULL);
- if (ipv6 || metadata)
+ if (ipv6) {
ret = __vxlan_sock_add(vxlan, true);
+ if (ret < 0 && ret != -EAFNOSUPPORT)
+ ipv4 = false;
+ }
#endif
- if (!ret && (!ipv6 || metadata))
+ if (ipv4)
ret = __vxlan_sock_add(vxlan, false);
if (ret < 0)
vxlan_sock_release(vxlan);
@@ -2912,6 +2916,11 @@
return -EINVAL;
}
+ if (lowerdev) {
+ dev->gso_max_size = lowerdev->gso_max_size;
+ dev->gso_max_segs = lowerdev->gso_max_segs;
+ }
+
if (conf->mtu) {
err = __vxlan_change_mtu(dev, lowerdev, dst, conf->mtu, false);
if (err)
diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c
index 47fdb87..8a9aced 100644
--- a/drivers/net/wan/hdlc_ppp.c
+++ b/drivers/net/wan/hdlc_ppp.c
@@ -574,7 +574,10 @@
ppp_cp_event(proto->dev, proto->pid, TO_GOOD, 0, 0,
0, NULL);
proto->restart_counter--;
- } else
+ } else if (netif_carrier_ok(proto->dev))
+ ppp_cp_event(proto->dev, proto->pid, TO_GOOD, 0, 0,
+ 0, NULL);
+ else
ppp_cp_event(proto->dev, proto->pid, TO_BAD, 0, 0,
0, NULL);
break;
diff --git a/drivers/net/wan/pc300too.c b/drivers/net/wan/pc300too.c
index db36385..2b06499 100644
--- a/drivers/net/wan/pc300too.c
+++ b/drivers/net/wan/pc300too.c
@@ -347,6 +347,7 @@
card->rambase == NULL) {
pr_err("ioremap() failed\n");
pc300_pci_remove_one(pdev);
+ return -ENOMEM;
}
/* PLX PCI 9050 workaround for local configuration register read bug */
diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
index 0b4d796..041ef3b 100644
--- a/drivers/net/wireless/ath/ath10k/ce.c
+++ b/drivers/net/wireless/ath/ath10k/ce.c
@@ -1059,7 +1059,7 @@
*/
BUILD_BUG_ON(2 * TARGET_NUM_MSDU_DESC >
(CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
- BUILD_BUG_ON(2 * TARGET_10X_NUM_MSDU_DESC >
+ BUILD_BUG_ON(2 * TARGET_10_4_NUM_MSDU_DESC_PFC >
(CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
BUILD_BUG_ON(2 * TARGET_TLV_NUM_MSDU_DESC >
(CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 82a4c67..0dadc60 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -624,17 +624,21 @@
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
- char buf[32];
+ char buf[32] = {0};
+ ssize_t rc;
int ret;
- simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+ /* filter partial writes and invalid commands */
+ if (*ppos != 0 || count >= sizeof(buf) || count == 0)
+ return -EINVAL;
- /* make sure that buf is null terminated */
- buf[sizeof(buf) - 1] = 0;
+ rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+ if (rc < 0)
+ return rc;
/* drop the possible '\n' from the end */
- if (buf[count - 1] == '\n')
- buf[count - 1] = 0;
+ if (buf[*ppos - 1] == '\n')
+ buf[*ppos - 1] = '\0';
mutex_lock(&ar->conf_mutex);
@@ -1942,6 +1946,15 @@
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
+ struct ath10k_vif *arvif;
+
+ /* Just check for for the first vif alone, as all the vifs will be
+ * sharing the same channel and if the channel is disabled, all the
+ * vifs will share the same 'is_started' state.
+ */
+ arvif = list_first_entry(&ar->arvifs, typeof(*arvif), list);
+ if (!arvif->is_started)
+ return -EINVAL;
ieee80211_radar_detected(ar->hw);
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 17ab8ef..a497bf3 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -2505,7 +2505,7 @@
}
break;
case WMI_VDEV_TYPE_STA:
- if (vif->bss_conf.qos)
+ if (sta->wme)
arg->peer_flags |= arvif->ar->wmi.peer_flags->qos;
break;
case WMI_VDEV_TYPE_IBSS:
@@ -6054,6 +6054,16 @@
"mac vdev %d peer delete %pM sta %pK (sta gone)\n",
arvif->vdev_id, sta->addr, sta);
+ if (sta->tdls) {
+ ret = ath10k_mac_tdls_peer_update(ar, arvif->vdev_id,
+ sta,
+ WMI_TDLS_PEER_STATE_TEARDOWN);
+ if (ret)
+ ath10k_warn(ar, "failed to update tdls peer state for %pM state %d: %i\n",
+ sta->addr,
+ WMI_TDLS_PEER_STATE_TEARDOWN, ret);
+ }
+
ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
if (ret)
ath10k_warn(ar, "failed to delete peer %pM for vdev %d: %i\n",
@@ -7070,7 +7080,7 @@
lockdep_assert_held(&ar->data_lock);
WARN_ON(ctx && vifs);
- WARN_ON(vifs && n_vifs != 1);
+ WARN_ON(vifs && !n_vifs);
/* FIXME: Sort of an optimization and a workaround. Peers and vifs are
* on a linked list now. Doing a lookup peer -> vif -> chanctx for each
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 54df425..e518b64 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -3638,6 +3638,11 @@
spin_lock_bh(&ar->data_lock);
ch = ar->rx_channel;
+
+ /* fetch target operating channel during channel change */
+ if (!ch)
+ ch = ar->tgt_oper_chan;
+
spin_unlock_bh(&ar->data_lock);
if (!ch) {
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 1b243c8..9b8562f 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -5017,7 +5017,8 @@
#define WMI_VDEV_PARAM_TXBF_MU_TX_BFER BIT(3)
#define WMI_TXBF_STS_CAP_OFFSET_LSB 4
-#define WMI_TXBF_STS_CAP_OFFSET_MASK 0xf0
+#define WMI_TXBF_STS_CAP_OFFSET_MASK 0x70
+#define WMI_TXBF_CONF_IMPLICIT_BF BIT(7)
#define WMI_BF_SOUND_DIM_OFFSET_LSB 8
#define WMI_BF_SOUND_DIM_OFFSET_MASK 0xf00
diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
index f850603..404fc30 100644
--- a/drivers/net/wireless/ath/regd.c
+++ b/drivers/net/wireless/ath/regd.c
@@ -254,8 +254,12 @@
EXPORT_SYMBOL(ath_is_49ghz_allowed);
/* Frequency is one where radar detection is required */
-static bool ath_is_radar_freq(u16 center_freq)
+static bool ath_is_radar_freq(u16 center_freq,
+ struct ath_regulatory *reg)
+
{
+ if (reg->country_code == CTRY_INDIA)
+ return (center_freq >= 5500 && center_freq <= 5700);
return (center_freq >= 5260 && center_freq <= 5700);
}
@@ -306,7 +310,7 @@
enum nl80211_reg_initiator initiator,
struct ieee80211_channel *ch)
{
- if (ath_is_radar_freq(ch->center_freq) ||
+ if (ath_is_radar_freq(ch->center_freq, reg) ||
(ch->flags & IEEE80211_CHAN_RADAR))
return;
@@ -395,8 +399,9 @@
}
}
-/* Always apply Radar/DFS rules on freq range 5260 MHz - 5700 MHz */
-static void ath_reg_apply_radar_flags(struct wiphy *wiphy)
+/* Always apply Radar/DFS rules on freq range 5500 MHz - 5700 MHz */
+static void ath_reg_apply_radar_flags(struct wiphy *wiphy,
+ struct ath_regulatory *reg)
{
struct ieee80211_supported_band *sband;
struct ieee80211_channel *ch;
@@ -409,7 +414,7 @@
for (i = 0; i < sband->n_channels; i++) {
ch = &sband->channels[i];
- if (!ath_is_radar_freq(ch->center_freq))
+ if (!ath_is_radar_freq(ch->center_freq, reg))
continue;
/* We always enable radar detection/DFS on this
* frequency range. Additionally we also apply on
@@ -505,7 +510,7 @@
struct ath_common *common = container_of(reg, struct ath_common,
regulatory);
/* We always apply this */
- ath_reg_apply_radar_flags(wiphy);
+ ath_reg_apply_radar_flags(wiphy, reg);
/*
* This would happen when we have sent a custom regulatory request
@@ -653,7 +658,7 @@
}
wiphy_apply_custom_regulatory(wiphy, regd);
- ath_reg_apply_radar_flags(wiphy);
+ ath_reg_apply_radar_flags(wiphy, reg);
ath_reg_apply_world_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER, reg);
return 0;
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
index 85d949e..f78d91b 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
@@ -462,25 +462,23 @@
* @dev_addr: optional device address.
*
* P2P needs mac addresses for P2P device and interface. If no device
- * address it specified, these are derived from the primary net device, ie.
- * the permanent ethernet address of the device.
+ * address it specified, these are derived from a random ethernet
+ * address.
*/
static void brcmf_p2p_generate_bss_mac(struct brcmf_p2p_info *p2p, u8 *dev_addr)
{
- struct brcmf_if *pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
- bool local_admin = false;
+ bool random_addr = false;
- if (!dev_addr || is_zero_ether_addr(dev_addr)) {
- dev_addr = pri_ifp->mac_addr;
- local_admin = true;
- }
+ if (!dev_addr || is_zero_ether_addr(dev_addr))
+ random_addr = true;
- /* Generate the P2P Device Address. This consists of the device's
- * primary MAC address with the locally administered bit set.
+ /* Generate the P2P Device Address obtaining a random ethernet
+ * address with the locally administered bit set.
*/
- memcpy(p2p->dev_addr, dev_addr, ETH_ALEN);
- if (local_admin)
- p2p->dev_addr[0] |= 0x02;
+ if (random_addr)
+ eth_random_addr(p2p->dev_addr);
+ else
+ memcpy(p2p->dev_addr, dev_addr, ETH_ALEN);
/* Generate the P2P Interface Address. If the discovery and connection
* BSSCFGs need to simultaneously co-exist, then this address must be
diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c
index 9142758..4c4c761 100644
--- a/drivers/net/wireless/cnss2/main.c
+++ b/drivers/net/wireless/cnss2/main.c
@@ -1799,20 +1799,7 @@
subsys_info = &plat_priv->subsys_info;
- switch (plat_priv->device_id) {
- case QCA6174_DEVICE_ID:
- subsys_info->subsys_desc.name = "AR6320";
- break;
- case QCA6290_EMULATION_DEVICE_ID:
- case QCA6290_DEVICE_ID:
- subsys_info->subsys_desc.name = "QCA6290";
- break;
- default:
- cnss_pr_err("Unknown device ID: 0x%lx\n", plat_priv->device_id);
- ret = -ENODEV;
- goto out;
- }
-
+ subsys_info->subsys_desc.name = "wlan";
subsys_info->subsys_desc.owner = THIS_MODULE;
subsys_info->subsys_desc.powerup = cnss_subsys_powerup;
subsys_info->subsys_desc.shutdown = cnss_subsys_shutdown;
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-a000.c b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c
index ea16185..e267377 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-a000.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c
@@ -65,12 +65,12 @@
#define IWL_A000_TX_POWER_VERSION 0xffff /* meaningless */
/* Memory offsets and lengths */
-#define IWL_A000_DCCM_OFFSET 0x800000
-#define IWL_A000_DCCM_LEN 0x18000
+#define IWL_A000_DCCM_OFFSET 0x800000 /* LMAC1 */
+#define IWL_A000_DCCM_LEN 0x10000 /* LMAC1 */
#define IWL_A000_DCCM2_OFFSET 0x880000
#define IWL_A000_DCCM2_LEN 0x8000
#define IWL_A000_SMEM_OFFSET 0x400000
-#define IWL_A000_SMEM_LEN 0x68000
+#define IWL_A000_SMEM_LEN 0xD0000
#define IWL_A000_FW_PRE "iwlwifi-Qu-a0-jf-b0-"
#define IWL_A000_MODULE_FIRMWARE(api) \
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c
index 88f260d..68412ff 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c
@@ -76,8 +76,8 @@
}
IWL_EXPORT_SYMBOL(iwl_notification_wait_init);
-void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_wait,
- struct iwl_rx_packet *pkt)
+bool iwl_notification_wait(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt)
{
bool triggered = false;
@@ -118,13 +118,11 @@
}
}
spin_unlock(¬if_wait->notif_wait_lock);
-
}
- if (triggered)
- wake_up_all(¬if_wait->notif_waitq);
+ return triggered;
}
-IWL_EXPORT_SYMBOL(iwl_notification_wait_notify);
+IWL_EXPORT_SYMBOL(iwl_notification_wait);
void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait)
{
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h
index 0f9995e..368884b 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h
@@ -6,7 +6,7 @@
* GPL LICENSE SUMMARY
*
* Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2015 Intel Deutschland GmbH
+ * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -32,6 +32,7 @@
* BSD LICENSE
*
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -89,10 +90,10 @@
*
* This structure is not used directly, to wait for a
* notification declare it on the stack, and call
- * iwlagn_init_notification_wait() with appropriate
+ * iwl_init_notification_wait() with appropriate
* parameters. Then do whatever will cause the ucode
* to notify the driver, and to wait for that then
- * call iwlagn_wait_notification().
+ * call iwl_wait_notification().
*
* Each notification is one-shot. If at some point we
* need to support multi-shot notifications (which
@@ -114,10 +115,24 @@
/* caller functions */
void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_data);
-void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_data,
- struct iwl_rx_packet *pkt);
+bool iwl_notification_wait(struct iwl_notif_wait_data *notif_data,
+ struct iwl_rx_packet *pkt);
void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_data);
+static inline void
+iwl_notification_notify(struct iwl_notif_wait_data *notif_data)
+{
+ wake_up_all(¬if_data->notif_waitq);
+}
+
+static inline void
+iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_data,
+ struct iwl_rx_packet *pkt)
+{
+ if (iwl_notification_wait(notif_data, pkt))
+ iwl_notification_notify(notif_data);
+}
+
/* user functions */
void __acquires(wait_entry)
iwl_init_notification_wait(struct iwl_notif_wait_data *notif_data,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
index 227c5ed..0aea476 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
@@ -1867,12 +1867,10 @@
struct rs_rate *rate = &search_tbl->rate;
const struct rs_tx_column *column = &rs_tx_columns[col_id];
const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column];
- u32 sz = (sizeof(struct iwl_scale_tbl_info) -
- (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
unsigned long rate_mask = 0;
u32 rate_idx = 0;
- memcpy(search_tbl, tbl, sz);
+ memcpy(search_tbl, tbl, offsetof(struct iwl_scale_tbl_info, win));
rate->sgi = column->sgi;
rate->ant = column->ant;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
index 0e60e38..b78e60e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
@@ -104,7 +104,20 @@
u8 crypt_len,
struct iwl_rx_cmd_buffer *rxb)
{
- unsigned int hdrlen, fraglen;
+ unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ unsigned int fraglen;
+
+ /*
+ * The 'hdrlen' (plus the 8 bytes for the SNAP and the crypt_len,
+ * but those are all multiples of 4 long) all goes away, but we
+ * want the *end* of it, which is going to be the start of the IP
+ * header, to be aligned when it gets pulled in.
+ * The beginning of the skb->data is aligned on at least a 4-byte
+ * boundary after allocation. Everything here is aligned at least
+ * on a 2-byte boundary so we can just take hdrlen & 3 and pad by
+ * the result.
+ */
+ skb_reserve(skb, hdrlen & 3);
/* If frame is small enough to fit in skb->head, pull it completely.
* If not, only pull ieee80211_hdr (including crypto if present, and
@@ -118,8 +131,7 @@
* If the latter changes (there are efforts in the standards group
* to do so) we should revisit this and ieee80211_data_to_8023().
*/
- hdrlen = (len <= skb_tailroom(skb)) ? len :
- sizeof(*hdr) + crypt_len + 8;
+ hdrlen = (len <= skb_tailroom(skb)) ? len : hdrlen + crypt_len + 8;
memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
fraglen = len - hdrlen;
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 4b462dc..4182c37 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -552,8 +552,6 @@
/* wmediumd portid responsible for netgroup of this radio */
u32 wmediumd;
- int power_level;
-
/* difference between this hw's clock and the real clock, in usecs */
s64 tsf_offset;
s64 bcn_delta;
@@ -730,16 +728,21 @@
val != PS_MANUAL_POLL)
return -EINVAL;
+ if (val == PS_MANUAL_POLL) {
+ if (data->ps != PS_ENABLED)
+ return -EINVAL;
+ local_bh_disable();
+ ieee80211_iterate_active_interfaces_atomic(
+ data->hw, IEEE80211_IFACE_ITER_NORMAL,
+ hwsim_send_ps_poll, data);
+ local_bh_enable();
+ return 0;
+ }
old_ps = data->ps;
data->ps = val;
local_bh_disable();
- if (val == PS_MANUAL_POLL) {
- ieee80211_iterate_active_interfaces_atomic(
- data->hw, IEEE80211_IFACE_ITER_NORMAL,
- hwsim_send_ps_poll, data);
- data->ps_poll_pending = true;
- } else if (old_ps == PS_DISABLED && val != PS_DISABLED) {
+ if (old_ps == PS_DISABLED && val != PS_DISABLED) {
ieee80211_iterate_active_interfaces_atomic(
data->hw, IEEE80211_IFACE_ITER_NORMAL,
hwsim_send_nullfunc_ps, data);
@@ -1208,7 +1211,9 @@
if (info->control.rates[0].flags & IEEE80211_TX_RC_SHORT_GI)
rx_status.flag |= RX_FLAG_SHORT_GI;
/* TODO: simulate real signal strength (and optional packet loss) */
- rx_status.signal = data->power_level - 50;
+ rx_status.signal = -50;
+ if (info->control.vif)
+ rx_status.signal += info->control.vif->bss_conf.txpower;
if (data->ps != PS_DISABLED)
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
@@ -1607,7 +1612,6 @@
WARN_ON(data->channel && data->use_chanctx);
- data->power_level = conf->power_level;
if (!data->started || !data->beacon_int)
tasklet_hrtimer_cancel(&data->beacon_timer);
else if (!hrtimer_is_queued(&data->beacon_timer.timer)) {
@@ -2212,7 +2216,6 @@
"d_tx_failed",
"d_ps_mode",
"d_group",
- "d_tx_power",
};
#define MAC80211_HWSIM_SSTATS_LEN ARRAY_SIZE(mac80211_hwsim_gstrings_stats)
@@ -2249,7 +2252,6 @@
data[i++] = ar->tx_failed;
data[i++] = ar->ps;
data[i++] = ar->group;
- data[i++] = ar->power_level;
WARN_ON(i != MAC80211_HWSIM_SSTATS_LEN);
}
@@ -3154,7 +3156,7 @@
if (!net_eq(wiphy_net(data->hw->wiphy), genl_info_net(info)))
continue;
- skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
if (!skb) {
res = -ENOMEM;
goto out_err;
diff --git a/drivers/net/wireless/marvell/libertas/if_spi.c b/drivers/net/wireless/marvell/libertas/if_spi.c
index c3a53cd..7b4955c 100644
--- a/drivers/net/wireless/marvell/libertas/if_spi.c
+++ b/drivers/net/wireless/marvell/libertas/if_spi.c
@@ -1181,6 +1181,10 @@
/* Initialize interrupt handling stuff. */
card->workqueue = alloc_workqueue("libertas_spi", WQ_MEM_RECLAIM, 0);
+ if (!card->workqueue) {
+ err = -ENOMEM;
+ goto remove_card;
+ }
INIT_WORK(&card->packet_work, if_spi_host_to_card_worker);
INIT_WORK(&card->resume_work, if_spi_resume_worker);
@@ -1209,6 +1213,7 @@
free_irq(spi->irq, card);
terminate_workqueue:
destroy_workqueue(card->workqueue);
+remove_card:
lbs_remove_card(priv); /* will call free_netdev */
free_card:
free_if_spi_card(card);
diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
index 8677a53..48d51be 100644
--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
@@ -1109,6 +1109,12 @@
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
enum nl80211_iftype curr_iftype = dev->ieee80211_ptr->iftype;
+ if (priv->scan_request) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "change virtual interface: scan in process\n");
+ return -EBUSY;
+ }
+
switch (curr_iftype) {
case NL80211_IFTYPE_ADHOC:
switch (type) {
diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c
index 2478ccd..1e36f11 100644
--- a/drivers/net/wireless/marvell/mwifiex/main.c
+++ b/drivers/net/wireless/marvell/mwifiex/main.c
@@ -146,7 +146,6 @@
kfree(adapter->regd);
- vfree(adapter->chan_stats);
kfree(adapter);
return 0;
}
@@ -636,6 +635,7 @@
goto done;
err_add_intf:
+ vfree(adapter->chan_stats);
wiphy_unregister(adapter->wiphy);
wiphy_free(adapter->wiphy);
err_init_fw:
@@ -1429,6 +1429,7 @@
mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev);
rtnl_unlock();
}
+ vfree(adapter->chan_stats);
up(sem);
exit_sem_err:
@@ -1729,6 +1730,7 @@
mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev);
rtnl_unlock();
}
+ vfree(adapter->chan_stats);
wiphy_unregister(adapter->wiphy);
wiphy_free(adapter->wiphy);
diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
index 8d601dc..486b8c7 100644
--- a/drivers/net/wireless/marvell/mwifiex/sdio.c
+++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
@@ -1458,7 +1458,7 @@
}
if (card->mpa_rx.pkt_cnt == 1)
- mport = adapter->ioport + port;
+ mport = adapter->ioport + card->mpa_rx.start_port;
if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf,
card->mpa_rx.buf_len, mport, 1))
@@ -1891,7 +1891,7 @@
}
if (card->mpa_tx.pkt_cnt == 1)
- mport = adapter->ioport + port;
+ mport = adapter->ioport + card->mpa_tx.start_port;
ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf,
card->mpa_tx.buf_len, mport);
diff --git a/drivers/net/wireless/mediatek/mt7601u/mcu.c b/drivers/net/wireless/mediatek/mt7601u/mcu.c
index dbdfb3f..a9f5f39 100644
--- a/drivers/net/wireless/mediatek/mt7601u/mcu.c
+++ b/drivers/net/wireless/mediatek/mt7601u/mcu.c
@@ -66,8 +66,10 @@
WARN_ON(len % 4); /* if length is not divisible by 4 we need to pad */
skb = alloc_skb(len + MT_DMA_HDR_LEN + 4, GFP_KERNEL);
- skb_reserve(skb, MT_DMA_HDR_LEN);
- memcpy(skb_put(skb, len), data, len);
+ if (skb) {
+ skb_reserve(skb, MT_DMA_HDR_LEN);
+ memcpy(skb_put(skb, len), data, len);
+ }
return skb;
}
@@ -170,6 +172,8 @@
};
skb = mt7601u_mcu_msg_alloc(dev, &msg, sizeof(msg));
+ if (!skb)
+ return -ENOMEM;
return mt7601u_mcu_msg_send(dev, skb, CMD_FUN_SET_OP, func == 5);
}
@@ -205,6 +209,8 @@
};
skb = mt7601u_mcu_msg_alloc(dev, &msg, sizeof(msg));
+ if (!skb)
+ return -ENOMEM;
return mt7601u_mcu_msg_send(dev, skb, CMD_CALIBRATION_OP, true);
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
index 75ffeaa..e15b462 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.c
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
@@ -1572,7 +1572,14 @@
dev_kfree_skb_irq(skb);
ring->idx = (ring->idx + 1) % ring->entries;
}
+
+ if (rtlpriv->use_new_trx_flow) {
+ rtlpci->tx_ring[i].cur_tx_rp = 0;
+ rtlpci->tx_ring[i].cur_tx_wp = 0;
+ }
+
ring->idx = 0;
+ ring->entries = rtlpci->txringcount[i];
}
}
spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c
index aba60c3..618e509 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c
@@ -1125,7 +1125,8 @@
/* Configuration Space offset 0x70f BIT7 is used to control L0S */
tmp8 = _rtl8723be_dbi_read(rtlpriv, 0x70f);
- _rtl8723be_dbi_write(rtlpriv, 0x70f, tmp8 | BIT(7));
+ _rtl8723be_dbi_write(rtlpriv, 0x70f, tmp8 | BIT(7) |
+ ASPM_L1_LATENCY << 3);
/* Configuration Space offset 0x719 Bit3 is for L1
* BIT4 is for clock request
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 280196a..ae87b39 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -3434,6 +3434,10 @@
/* because rndis_command() sleeps we need to use workqueue */
priv->workqueue = create_singlethread_workqueue("rndis_wlan");
+ if (!priv->workqueue) {
+ wiphy_free(wiphy);
+ return -ENOMEM;
+ }
INIT_WORK(&priv->work, rndis_wlan_worker);
INIT_DELAYED_WORK(&priv->dev_poller_work, rndis_device_poller);
INIT_DELAYED_WORK(&priv->scan_work, rndis_get_scan_results);
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
index c5effd6c..01ca1d5 100644
--- a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
@@ -1278,6 +1278,9 @@
u8 bulk_out_ep;
int r;
+ if (iface_desc->desc.bNumEndpoints < 2)
+ return -ENODEV;
+
/* Find bulk out endpoint */
for (r = 1; r >= 0; r--) {
endpoint = &iface_desc->endpoint[r].desc;
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index 1a9dadf..b09c81e 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -1345,6 +1345,7 @@
netif_carrier_off(netdev);
+ xenbus_switch_state(dev, XenbusStateInitialising);
return netdev;
exit:
diff --git a/drivers/nfc/nfcmrvl/fw_dnld.c b/drivers/nfc/nfcmrvl/fw_dnld.c
index af62c4c..b4f31da 100644
--- a/drivers/nfc/nfcmrvl/fw_dnld.c
+++ b/drivers/nfc/nfcmrvl/fw_dnld.c
@@ -17,7 +17,7 @@
*/
#include <linux/module.h>
-#include <linux/unaligned/access_ok.h>
+#include <asm/unaligned.h>
#include <linux/firmware.h>
#include <linux/nfc.h>
#include <net/nfc/nci.h>
diff --git a/drivers/nfc/nfcmrvl/spi.c b/drivers/nfc/nfcmrvl/spi.c
index a7faa0b..fc8e78a 100644
--- a/drivers/nfc/nfcmrvl/spi.c
+++ b/drivers/nfc/nfcmrvl/spi.c
@@ -96,10 +96,9 @@
/* Send the SPI packet */
err = nci_spi_send(drv_data->nci_spi, &drv_data->handshake_completion,
skb);
- if (err != 0) {
+ if (err)
nfc_err(priv->dev, "spi_send failed %d", err);
- kfree_skb(skb);
- }
+
return err;
}
diff --git a/drivers/nfc/pn533/i2c.c b/drivers/nfc/pn533/i2c.c
index 1dc8924..11d78b4 100644
--- a/drivers/nfc/pn533/i2c.c
+++ b/drivers/nfc/pn533/i2c.c
@@ -242,10 +242,10 @@
dev_dbg(&client->dev, "%s\n", __func__);
- pn533_unregister_device(phy->priv);
-
free_irq(client->irq, phy);
+ pn533_unregister_device(phy->priv);
+
return 0;
}
diff --git a/drivers/nvdimm/blk.c b/drivers/nvdimm/blk.c
index 9faaa96..77db979 100644
--- a/drivers/nvdimm/blk.c
+++ b/drivers/nvdimm/blk.c
@@ -286,8 +286,6 @@
disk->queue = q;
disk->flags = GENHD_FL_EXT_DEVT;
nvdimm_namespace_disk_name(&nsblk->common, disk->disk_name);
- set_capacity(disk, 0);
- device_add_disk(dev, disk);
if (devm_add_action_or_reset(dev, nd_blk_release_disk, disk))
return -ENOMEM;
@@ -300,6 +298,7 @@
}
set_capacity(disk, available_disk_size >> SECTOR_SHIFT);
+ device_add_disk(dev, disk);
revalidate_disk(disk);
return 0;
}
diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
index 7121453..0c46ada 100644
--- a/drivers/nvdimm/btt.c
+++ b/drivers/nvdimm/btt.c
@@ -1392,8 +1392,6 @@
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, btt->btt_queue);
btt->btt_queue->queuedata = btt;
- set_capacity(btt->btt_disk, 0);
- device_add_disk(&btt->nd_btt->dev, btt->btt_disk);
if (btt_meta_size(btt)) {
int rc = nd_integrity_init(btt->btt_disk, btt_meta_size(btt));
@@ -1405,6 +1403,7 @@
}
}
set_capacity(btt->btt_disk, btt->nlba * btt->sector_size >> 9);
+ device_add_disk(&btt->nd_btt->dev, btt->btt_disk);
btt->nd_btt->size = btt->nlba * (u64)btt->sector_size;
revalidate_disk(btt->btt_disk);
diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c
index 0392eb8..8311a93 100644
--- a/drivers/nvdimm/bus.c
+++ b/drivers/nvdimm/bus.c
@@ -812,16 +812,17 @@
int read_only, unsigned int ioctl_cmd, unsigned long arg)
{
struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
- size_t buf_len = 0, in_len = 0, out_len = 0;
static char out_env[ND_CMD_MAX_ENVELOPE];
static char in_env[ND_CMD_MAX_ENVELOPE];
const struct nd_cmd_desc *desc = NULL;
unsigned int cmd = _IOC_NR(ioctl_cmd);
void __user *p = (void __user *) arg;
struct device *dev = &nvdimm_bus->dev;
- struct nd_cmd_pkg pkg;
const char *cmd_name, *dimm_name;
+ u32 in_len = 0, out_len = 0;
unsigned long cmd_mask;
+ struct nd_cmd_pkg pkg;
+ u64 buf_len = 0;
void *buf;
int rc, i;
@@ -882,7 +883,7 @@
}
if (cmd == ND_CMD_CALL) {
- dev_dbg(dev, "%s:%s, idx: %llu, in: %zu, out: %zu, len %zu\n",
+ dev_dbg(dev, "%s:%s, idx: %llu, in: %u, out: %u, len %llu\n",
__func__, dimm_name, pkg.nd_command,
in_len, out_len, buf_len);
@@ -912,9 +913,9 @@
out_len += out_size;
}
- buf_len = out_len + in_len;
+ buf_len = (u64) out_len + (u64) in_len;
if (buf_len > ND_IOCTL_MAX_BUFLEN) {
- dev_dbg(dev, "%s:%s cmd: %s buf_len: %zu > %d\n", __func__,
+ dev_dbg(dev, "%s:%s cmd: %s buf_len: %llu > %d\n", __func__,
dimm_name, cmd_name, buf_len,
ND_IOCTL_MAX_BUFLEN);
return -EINVAL;
diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c
index 42abdd2..d6aa59c 100644
--- a/drivers/nvdimm/pfn_devs.c
+++ b/drivers/nvdimm/pfn_devs.c
@@ -563,6 +563,12 @@
return altmap;
}
+static u64 phys_pmem_align_down(struct nd_pfn *nd_pfn, u64 phys)
+{
+ return min_t(u64, PHYS_SECTION_ALIGN_DOWN(phys),
+ ALIGN_DOWN(phys, nd_pfn->align));
+}
+
static int nd_pfn_init(struct nd_pfn *nd_pfn)
{
u32 dax_label_reserve = is_nd_dax(&nd_pfn->dev) ? SZ_128K : 0;
@@ -618,13 +624,16 @@
start = nsio->res.start;
size = PHYS_SECTION_ALIGN_UP(start + size) - start;
if (region_intersects(start, size, IORESOURCE_SYSTEM_RAM,
- IORES_DESC_NONE) == REGION_MIXED) {
+ IORES_DESC_NONE) == REGION_MIXED
+ || !IS_ALIGNED(start + resource_size(&nsio->res),
+ nd_pfn->align)) {
size = resource_size(&nsio->res);
- end_trunc = start + size - PHYS_SECTION_ALIGN_DOWN(start + size);
+ end_trunc = start + size - phys_pmem_align_down(nd_pfn,
+ start + size);
}
if (start_pad + end_trunc)
- dev_info(&nd_pfn->dev, "%s section collision, truncate %d bytes\n",
+ dev_info(&nd_pfn->dev, "%s alignment collision, truncate %d bytes\n",
dev_name(&ndns->dev), start_pad + end_trunc);
/*
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 719ee5f..ad9d82e 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -1204,7 +1204,8 @@
blk_queue_max_hw_sectors(q, ctrl->max_hw_sectors);
blk_queue_max_segments(q, min_t(u32, max_segments, USHRT_MAX));
}
- if (ctrl->quirks & NVME_QUIRK_STRIPE_SIZE)
+ if ((ctrl->quirks & NVME_QUIRK_STRIPE_SIZE) &&
+ is_power_of_2(ctrl->max_hw_sectors))
blk_queue_chunk_sectors(q, ctrl->max_hw_sectors);
blk_queue_virt_boundary(q, ctrl->page_size - 1);
if (ctrl->vwc & NVME_CTRL_VWC_PRESENT)
diff --git a/drivers/of/device.c b/drivers/of/device.c
index f7a9701..3cda60c 100644
--- a/drivers/of/device.c
+++ b/drivers/of/device.c
@@ -223,7 +223,7 @@
str[i] = '_';
}
- return tsize;
+ return repend;
}
EXPORT_SYMBOL_GPL(of_device_get_modalias);
diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c
index dafb4cd..d392a55 100644
--- a/drivers/pci/host/pci-hyperv.c
+++ b/drivers/pci/host/pci-hyperv.c
@@ -351,6 +351,7 @@
hv_pcibus_init = 0,
hv_pcibus_probed,
hv_pcibus_installed,
+ hv_pcibus_removed,
hv_pcibus_maximum
};
@@ -1205,9 +1206,11 @@
hbus->pci_bus->msi = &hbus->msi_chip;
hbus->pci_bus->msi->dev = &hbus->hdev->device;
+ pci_lock_rescan_remove();
pci_scan_child_bus(hbus->pci_bus);
pci_bus_assign_resources(hbus->pci_bus);
pci_bus_add_devices(hbus->pci_bus);
+ pci_unlock_rescan_remove();
hbus->state = hv_pcibus_installed;
return 0;
}
@@ -1489,13 +1492,24 @@
put_pcichild(hpdev, hv_pcidev_ref_initial);
}
- /* Tell the core to rescan bus because there may have been changes. */
- if (hbus->state == hv_pcibus_installed) {
+ switch(hbus->state) {
+ case hv_pcibus_installed:
+ /*
+ * Tell the core to rescan bus
+ * because there may have been changes.
+ */
pci_lock_rescan_remove();
pci_scan_child_bus(hbus->pci_bus);
pci_unlock_rescan_remove();
- } else {
+ break;
+
+ case hv_pcibus_init:
+ case hv_pcibus_probed:
survey_child_resources(hbus);
+ break;
+
+ default:
+ break;
}
up(&hbus->enum_sem);
@@ -1585,8 +1599,10 @@
pdev = pci_get_domain_bus_and_slot(hpdev->hbus->sysdata.domain, 0,
wslot);
if (pdev) {
+ pci_lock_rescan_remove();
pci_stop_and_remove_bus_device(pdev);
pci_dev_put(pdev);
+ pci_unlock_rescan_remove();
}
memset(&ctxt, 0, sizeof(ctxt));
@@ -2170,6 +2186,7 @@
hbus = kzalloc(sizeof(*hbus), GFP_KERNEL);
if (!hbus)
return -ENOMEM;
+ hbus->state = hv_pcibus_init;
/*
* The PCI bus "domain" is what is called "segment" in ACPI and
@@ -2312,6 +2329,7 @@
pci_stop_root_bus(hbus->pci_bus);
pci_remove_root_bus(hbus->pci_bus);
pci_unlock_rescan_remove();
+ hbus->state = hv_pcibus_removed;
}
ret = hv_send_resources_released(hdev);
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
index af8f6e9..b3a8715 100644
--- a/drivers/pci/host/pcie-designware.c
+++ b/drivers/pci/host/pcie-designware.c
@@ -861,7 +861,7 @@
/* setup bus numbers */
val = dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS);
val &= 0xff000000;
- val |= 0x00010100;
+ val |= 0x00ff0100;
dw_pcie_writel_rc(pp, PCI_PRIMARY_BUS, val);
/* setup command register */
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 802997e..d81ad84 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -463,8 +463,6 @@
if (drv && drv->shutdown)
drv->shutdown(pci_dev);
- pci_msi_shutdown(pci_dev);
- pci_msix_shutdown(pci_dev);
/*
* If this is a kexec reboot, turn off Bus Master bit on the
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index b0916b1..e38bb48 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -526,10 +526,14 @@
/*
* Root Ports and PCI/PCI-X to PCIe Bridges are roots of PCIe
- * hierarchies.
+ * hierarchies. Note that some PCIe host implementations omit
+ * the root ports entirely, in which case a downstream port on
+ * a switch may become the root of the link state chain for all
+ * its subordinate endpoints.
*/
if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
- pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE) {
+ pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE ||
+ !pdev->bus->parent->self) {
link->root = link;
} else {
struct pcie_link_state *parent;
@@ -782,7 +786,8 @@
}
EXPORT_SYMBOL(pci_disable_link_state);
-static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp)
+static int pcie_aspm_set_policy(const char *val,
+ const struct kernel_param *kp)
{
int i;
struct pcie_link_state *link;
@@ -809,7 +814,7 @@
return 0;
}
-static int pcie_aspm_get_policy(char *buffer, struct kernel_param *kp)
+static int pcie_aspm_get_policy(char *buffer, const struct kernel_param *kp)
{
int i, cnt = 0;
for (i = 0; i < ARRAY_SIZE(policy_str); i++)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 98eba91..fb177dc 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -3369,22 +3369,29 @@
static void quirk_chelsio_extend_vpd(struct pci_dev *dev)
{
- pci_set_vpd_size(dev, 8192);
+ int chip = (dev->device & 0xf000) >> 12;
+ int func = (dev->device & 0x0f00) >> 8;
+ int prod = (dev->device & 0x00ff) >> 0;
+
+ /*
+ * If this is a T3-based adapter, there's a 1KB VPD area at offset
+ * 0xc00 which contains the preferred VPD values. If this is a T4 or
+ * later based adapter, the special VPD is at offset 0x400 for the
+ * Physical Functions (the SR-IOV Virtual Functions have no VPD
+ * Capabilities). The PCI VPD Access core routines will normally
+ * compute the size of the VPD by parsing the VPD Data Structure at
+ * offset 0x000. This will result in silent failures when attempting
+ * to accesses these other VPD areas which are beyond those computed
+ * limits.
+ */
+ if (chip == 0x0 && prod >= 0x20)
+ pci_set_vpd_size(dev, 8192);
+ else if (chip >= 0x4 && func < 0x8)
+ pci_set_vpd_size(dev, 2048);
}
-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);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID,
+ quirk_chelsio_extend_vpd);
#ifdef CONFIG_ACPI
/*
@@ -3870,6 +3877,8 @@
quirk_dma_func1_alias);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TTI, 0x0642,
quirk_dma_func1_alias);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TTI, 0x0645,
+ quirk_dma_func1_alias);
/* https://bugs.gentoo.org/show_bug.cgi?id=497630 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_JMICRON,
PCI_DEVICE_ID_JMICRON_JMB388_ESD,
@@ -4097,6 +4106,9 @@
*/
acs_flags &= ~(PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_SV | PCI_ACS_UF);
+ if (!((dev->device >= 0xa000) && (dev->device <= 0xa0ff)))
+ return -ENOTTY;
+
return acs_flags ? 0 : 1;
}
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index ffcd001..df02f98 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -325,10 +325,16 @@
return 0;
}
+static struct arm_pmu_platdata *armpmu_get_platdata(struct arm_pmu *armpmu)
+{
+ struct platform_device *pdev = armpmu->plat_device;
+
+ return pdev ? dev_get_platdata(&pdev->dev) : NULL;
+}
+
static irqreturn_t armpmu_dispatch_irq(int irq, void *dev)
{
struct arm_pmu *armpmu;
- struct platform_device *plat_device;
struct arm_pmu_platdata *plat;
int ret;
u64 start_clock, finish_clock;
@@ -340,8 +346,8 @@
* dereference.
*/
armpmu = *(void **)dev;
- plat_device = armpmu->plat_device;
- plat = dev_get_platdata(&plat_device->dev);
+
+ plat = armpmu_get_platdata(armpmu);
start_clock = sched_clock();
if (plat && plat->handle_irq)
@@ -1108,7 +1114,7 @@
const struct pmu_probe_info *probe_table)
{
const struct of_device_id *of_id;
- const int (*init_fn)(struct arm_pmu *);
+ int (*init_fn)(struct arm_pmu *);
struct device_node *node = pdev->dev.of_node;
struct arm_pmu *pmu;
int ret = -ENODEV;
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index fb38e20..735d8f7 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -992,19 +992,16 @@
EXPORT_SYMBOL_GPL(pinctrl_lookup_state);
/**
- * pinctrl_select_state() - select/activate/program a pinctrl state to HW
+ * pinctrl_commit_state() - select/activate/program a pinctrl state to HW
* @p: the pinctrl handle for the device that requests configuration
* @state: the state handle to select/activate/program
*/
-int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)
+static int pinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state)
{
struct pinctrl_setting *setting, *setting2;
struct pinctrl_state *old_state = p->state;
int ret;
- if (p->state == state)
- return 0;
-
if (p->state) {
/*
* For each pinmux setting in the old state, forget SW's record
@@ -1068,6 +1065,19 @@
return ret;
}
+
+/**
+ * pinctrl_select_state() - select/activate/program a pinctrl state to HW
+ * @p: the pinctrl handle for the device that requests configuration
+ * @state: the state handle to select/activate/program
+ */
+int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)
+{
+ if (p->state == state)
+ return 0;
+
+ return pinctrl_commit_state(p, state);
+}
EXPORT_SYMBOL_GPL(pinctrl_select_state);
static void devm_pinctrl_release(struct device *dev, void *res)
@@ -1236,7 +1246,7 @@
int pinctrl_force_sleep(struct pinctrl_dev *pctldev)
{
if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_sleep))
- return pinctrl_select_state(pctldev->p, pctldev->hog_sleep);
+ return pinctrl_commit_state(pctldev->p, pctldev->hog_sleep);
return 0;
}
EXPORT_SYMBOL_GPL(pinctrl_force_sleep);
@@ -1248,7 +1258,7 @@
int pinctrl_force_default(struct pinctrl_dev *pctldev)
{
if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_default))
- return pinctrl_select_state(pctldev->p, pctldev->hog_default);
+ return pinctrl_commit_state(pctldev->p, pctldev->hog_default);
return 0;
}
EXPORT_SYMBOL_GPL(pinctrl_force_default);
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index 49bf7dc..f826793 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -1278,8 +1278,16 @@
{
struct rockchip_pin_bank *bank = gpiochip_get_data(chip);
u32 data;
+ int ret;
+ ret = clk_enable(bank->clk);
+ if (ret < 0) {
+ dev_err(bank->drvdata->dev,
+ "failed to enable clock for bank %s\n", bank->name);
+ return ret;
+ }
data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
+ clk_disable(bank->clk);
return !(data & BIT(offset));
}
diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
index 04053fe..cfa3e85 100644
--- a/drivers/platform/chrome/cros_ec_proto.c
+++ b/drivers/platform/chrome/cros_ec_proto.c
@@ -60,12 +60,14 @@
struct cros_ec_command *msg)
{
int ret;
+ int (*xfer_fxn)(struct cros_ec_device *ec, struct cros_ec_command *msg);
if (ec_dev->proto_version > 2)
- ret = ec_dev->pkt_xfer(ec_dev, msg);
+ xfer_fxn = ec_dev->pkt_xfer;
else
- ret = ec_dev->cmd_xfer(ec_dev, msg);
+ xfer_fxn = ec_dev->cmd_xfer;
+ ret = (*xfer_fxn)(ec_dev, msg);
if (msg->result == EC_RES_IN_PROGRESS) {
int i;
struct cros_ec_command *status_msg;
@@ -88,7 +90,7 @@
for (i = 0; i < EC_COMMAND_RETRIES; i++) {
usleep_range(10000, 11000);
- ret = ec_dev->cmd_xfer(ec_dev, status_msg);
+ ret = (*xfer_fxn)(ec_dev, status_msg);
if (ret < 0)
break;
diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c
index f3baf99..24f1630a 100644
--- a/drivers/platform/chrome/cros_ec_sysfs.c
+++ b/drivers/platform/chrome/cros_ec_sysfs.c
@@ -187,7 +187,7 @@
count += scnprintf(buf + count, PAGE_SIZE - count,
"Build info: EC error %d\n", msg->result);
else {
- msg->data[sizeof(msg->data) - 1] = '\0';
+ msg->data[EC_HOST_PARAM_SIZE - 1] = '\0';
count += scnprintf(buf + count, PAGE_SIZE - count,
"Build info: %s\n", msg->data);
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
index b38cde5..6285130 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
@@ -1249,7 +1249,8 @@
struct ipa_hdr_offset_entry *off_next;
struct ipa_hdr_proc_ctx_offset_entry *ctx_off_entry;
struct ipa_hdr_proc_ctx_offset_entry *ctx_off_next;
- int i;
+ int i, end = 0;
+ bool user_rule = false;
/*
* issue a reset on the routing module since routing rules point to
@@ -1287,6 +1288,9 @@
return -EFAULT;
}
+ if (entry->ipacm_installed)
+ user_rule = true;
+
if (!user_only || entry->ipacm_installed) {
if (entry->is_hdr_proc_ctx) {
dma_unmap_single(ipa_ctx->pdev,
@@ -1296,6 +1300,7 @@
entry->proc_ctx = NULL;
}
list_del(&entry->link);
+ ipa_ctx->hdr_tbl.hdr_cnt--;
entry->ref_cnt = 0;
entry->cookie = 0;
@@ -1321,6 +1326,13 @@
list_del(&off_entry->link);
kmem_cache_free(ipa_ctx->hdr_offset_cache,
off_entry);
+ } else {
+ if (off_entry->offset +
+ ipa_hdr_bin_sz[off_entry->bin] > end) {
+ end = off_entry->offset +
+ ipa_hdr_bin_sz[off_entry->bin];
+ IPADBG("replace end = %d\n", end);
+ }
}
}
list_for_each_entry_safe(off_entry, off_next,
@@ -1335,11 +1347,15 @@
}
}
}
- /* there is one header of size 8 */
- ipa_ctx->hdr_tbl.end = 8;
- ipa_ctx->hdr_tbl.hdr_cnt = 1;
+ IPADBG("hdr_tbl.end = %d\n", end);
+ if (user_rule) {
+ ipa_ctx->hdr_tbl.end = end;
+ IPADBG("hdr_tbl.end = %d\n", end);
+ }
IPADBG("reset hdr proc ctx\n");
+ user_rule = false;
+ end = 0;
list_for_each_entry_safe(
ctx_entry,
ctx_next,
@@ -1352,9 +1368,13 @@
return -EFAULT;
}
+ if (entry->ipacm_installed)
+ user_rule = true;
+
if (!user_only ||
ctx_entry->ipacm_installed) {
list_del(&ctx_entry->link);
+ ipa_ctx->hdr_proc_ctx_tbl.proc_ctx_cnt--;
ctx_entry->ref_cnt = 0;
ctx_entry->cookie = 0;
@@ -1375,6 +1395,14 @@
kmem_cache_free(
ipa_ctx->hdr_proc_ctx_offset_cache,
ctx_off_entry);
+ } else {
+ if (ctx_off_entry->offset +
+ ipa_hdr_bin_sz[ctx_off_entry->bin]
+ > end) {
+ end = ctx_off_entry->offset +
+ ipa_hdr_bin_sz[ctx_off_entry->bin];
+ IPADBG("replace hdr_proc as %d\n", end);
+ }
}
}
list_for_each_entry_safe(ctx_off_entry, ctx_off_next,
@@ -1390,8 +1418,12 @@
}
}
}
- ipa_ctx->hdr_proc_ctx_tbl.end = 0;
- ipa_ctx->hdr_proc_ctx_tbl.proc_ctx_cnt = 0;
+
+ IPADBG("hdr_proc_tbl.end = %d\n", end);
+ if (user_rule) {
+ ipa_ctx->hdr_proc_ctx_tbl.end = end;
+ IPADBG("hdr_proc_tbl.end = %d\n", end);
+ }
mutex_unlock(&ipa_ctx->lock);
return 0;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 465b66b..af926fb 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -737,6 +737,7 @@
u32 pyld_sz;
u8 header[128] = { 0 };
u8 *param = NULL;
+ bool is_vlan_mode;
struct ipa_ioc_nat_alloc_mem nat_mem;
struct ipa_ioc_nat_ipv6ct_table_alloc table_alloc;
struct ipa_ioc_v4_nat_init nat_init;
@@ -746,6 +747,7 @@
struct ipa_ioc_nat_pdn_entry mdfy_pdn;
struct ipa_ioc_rm_dependency rm_depend;
struct ipa_ioc_nat_dma_cmd *table_dma_cmd;
+ struct ipa_ioc_get_vlan_mode vlan_mode;
size_t sz;
int pre_entry;
@@ -1843,6 +1845,28 @@
}
break;
+ case IPA_IOC_GET_VLAN_MODE:
+ if (copy_from_user(&vlan_mode, (const void __user *)arg,
+ sizeof(struct ipa_ioc_get_vlan_mode))) {
+ retval = -EFAULT;
+ break;
+ }
+ retval = ipa3_is_vlan_mode(
+ vlan_mode.iface,
+ &is_vlan_mode);
+ if (retval)
+ break;
+
+ vlan_mode.is_vlan_mode = is_vlan_mode;
+
+ if (copy_to_user((void __user *)arg,
+ &vlan_mode,
+ sizeof(struct ipa_ioc_get_vlan_mode))) {
+ retval = -EFAULT;
+ break;
+ }
+ break;
+
case IPA_IOC_ADD_VLAN_IFACE:
if (ipa3_send_vlan_l2tp_msg(arg, ADD_VLAN_IFACE)) {
retval = -EFAULT;
@@ -5071,6 +5095,7 @@
ipa3_ctx->gsi_ch20_wa = resource_p->gsi_ch20_wa;
ipa3_ctx->use_ipa_pm = resource_p->use_ipa_pm;
ipa3_ctx->ipa3_active_clients_logging.log_rdy = false;
+ ipa3_ctx->ipa_config_is_mhi = resource_p->ipa_mhi_dynamic_config;
ipa3_ctx->mhi_evid_limits[0] = resource_p->mhi_evid_limits[0];
ipa3_ctx->mhi_evid_limits[1] = resource_p->mhi_evid_limits[1];
@@ -5601,6 +5626,7 @@
ipa_drv_res->ipa3_hw_mode = 0;
ipa_drv_res->modem_cfg_emb_pipe_flt = false;
ipa_drv_res->ipa_wdi2 = false;
+ ipa_drv_res->ipa_mhi_dynamic_config = false;
ipa_drv_res->use_64_bit_dma_mask = false;
ipa_drv_res->use_bw_vote = false;
ipa_drv_res->wan_rx_ring_size = IPA_GENERIC_RX_POOL_SZ;
@@ -5663,6 +5689,13 @@
ipa_drv_res->use_ipa_teth_bridge
? "True" : "False");
+ ipa_drv_res->ipa_mhi_dynamic_config =
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,use-ipa-in-mhi-mode");
+ IPADBG(": ipa_mhi_dynamic_config (%s)\n",
+ ipa_drv_res->ipa_mhi_dynamic_config
+ ? "True" : "False");
+
ipa_drv_res->modem_cfg_emb_pipe_flt =
of_property_read_bool(pdev->dev.of_node,
"qcom,modem-cfg-emb-pipe-flt");
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
index 757f420..cecbef0 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
@@ -657,7 +657,6 @@
return 0;
}
-
int __ipa3_del_hdr(u32 hdr_hdl, bool by_user)
{
struct ipa3_hdr_entry *entry;
@@ -997,7 +996,8 @@
struct ipa_hdr_offset_entry *off_next;
struct ipa3_hdr_proc_ctx_offset_entry *ctx_off_entry;
struct ipa3_hdr_proc_ctx_offset_entry *ctx_off_next;
- int i;
+ int i, end = 0;
+ bool user_rule = false;
/*
* issue a reset on the routing module since routing rules point to
@@ -1035,6 +1035,9 @@
return -EFAULT;
}
+ if (entry->ipacm_installed)
+ user_rule = true;
+
if (!user_only || entry->ipacm_installed) {
if (entry->is_hdr_proc_ctx) {
dma_unmap_single(ipa3_ctx->pdev,
@@ -1044,6 +1047,7 @@
entry->proc_ctx = NULL;
}
list_del(&entry->link);
+ ipa3_ctx->hdr_tbl.hdr_cnt--;
entry->ref_cnt = 0;
entry->cookie = 0;
@@ -1069,6 +1073,13 @@
list_del(&off_entry->link);
kmem_cache_free(ipa3_ctx->hdr_offset_cache,
off_entry);
+ } else {
+ if (off_entry->offset +
+ ipa_hdr_bin_sz[off_entry->bin] > end) {
+ end = off_entry->offset +
+ ipa_hdr_bin_sz[off_entry->bin];
+ IPADBG("replace end = %d\n", end);
+ }
}
}
list_for_each_entry_safe(off_entry, off_next,
@@ -1083,11 +1094,15 @@
}
}
}
- /* there is one header of size 8 */
- ipa3_ctx->hdr_tbl.end = 8;
- ipa3_ctx->hdr_tbl.hdr_cnt = 1;
+ IPADBG("hdr_tbl.end = %d\n", end);
+ if (user_rule) {
+ ipa3_ctx->hdr_tbl.end = end;
+ IPADBG("hdr_tbl.end = %d\n", end);
+ }
IPADBG("reset hdr proc ctx\n");
+ user_rule = false;
+ end = 0;
list_for_each_entry_safe(
ctx_entry,
ctx_next,
@@ -1100,9 +1115,13 @@
return -EFAULT;
}
+ if (entry->ipacm_installed)
+ user_rule = true;
+
if (!user_only ||
ctx_entry->ipacm_installed) {
list_del(&ctx_entry->link);
+ ipa3_ctx->hdr_proc_ctx_tbl.proc_ctx_cnt--;
ctx_entry->ref_cnt = 0;
ctx_entry->cookie = 0;
@@ -1123,6 +1142,14 @@
kmem_cache_free(
ipa3_ctx->hdr_proc_ctx_offset_cache,
ctx_off_entry);
+ } else {
+ if (ctx_off_entry->offset +
+ ipa_hdr_bin_sz[ctx_off_entry->bin]
+ > end) {
+ end = ctx_off_entry->offset +
+ ipa_hdr_bin_sz[ctx_off_entry->bin];
+ IPADBG("replace hdr_proc as %d\n", end);
+ }
}
}
list_for_each_entry_safe(ctx_off_entry, ctx_off_next,
@@ -1138,8 +1165,12 @@
}
}
}
- ipa3_ctx->hdr_proc_ctx_tbl.end = 0;
- ipa3_ctx->hdr_proc_ctx_tbl.proc_ctx_cnt = 0;
+
+ IPADBG("hdr_proc_tbl.end = %d\n", end);
+ if (user_rule) {
+ ipa3_ctx->hdr_proc_ctx_tbl.end = end;
+ IPADBG("hdr_proc_tbl.end = %d\n", end);
+ }
mutex_unlock(&ipa3_ctx->lock);
return 0;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 08263b5..56b5740 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -1441,6 +1441,7 @@
bool gsi_ch20_wa;
bool tethered_flow_control;
u32 mhi_evid_limits[2]; /* start and end values */
+ bool ipa_mhi_dynamic_config;
u32 ipa_tz_unlock_reg_num;
struct ipa_tz_unlock_reg_info *ipa_tz_unlock_reg;
bool use_ipa_pm;
@@ -2379,4 +2380,5 @@
int ipa3_tz_unlock_reg(struct ipa_tz_unlock_reg_info *reg_info, u16 num_regs);
void ipa3_init_imm_cmd_desc(struct ipa3_desc *desc,
struct ipahal_imm_cmd_pyld *cmd_pyld);
+int ipa3_is_vlan_mode(enum ipa_vlan_ifaces iface, bool *res);
#endif /* _IPA3_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index 80155f9..5d6d3cd 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -1279,31 +1279,31 @@
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_PCIE,
+ QMB_MASTER_SELECT_DDR,
{ 11, 6, 9, 9, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST1_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_PCIE,
+ QMB_MASTER_SELECT_DDR,
{ 11, 6, 9, 9, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST2_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_PCIE,
+ QMB_MASTER_SELECT_DDR,
{ 12, 2, 5, 5, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST3_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_PCIE,
+ QMB_MASTER_SELECT_DDR,
{ 19, 12, 9, 9, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST4_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_PCIE,
+ QMB_MASTER_SELECT_DDR,
{ 21, 14, 9, 9, IPA_EE_AP } },
/* Dummy consumer (pipe 31) is used in L2TP rt rule */
[IPA_4_0][IPA_CLIENT_DUMMY_CONS] = {
@@ -1363,7 +1363,7 @@
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
{0, 8, 8, 16, IPA_EE_AP } },
- [IPA_4_0][IPA_CLIENT_TEST1_PROD] = {
+ [IPA_4_0_MHI][IPA_CLIENT_TEST1_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
@@ -4326,7 +4326,7 @@
*
* Returns: 0 on success, negative on failure
*/
-static int ipa3_is_vlan_mode(enum ipa_vlan_ifaces iface, bool *res)
+int ipa3_is_vlan_mode(enum ipa_vlan_ifaces iface, bool *res)
{
if (!res) {
IPAERR("NULL out param\n");
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index 6eb2837..687cc5b 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -120,6 +120,10 @@
.xusb2pr = 0x01D9,
};
+static struct quirk_entry quirk_asus_ux330uak = {
+ .wmi_force_als_set = true,
+};
+
static int dmi_matched(const struct dmi_system_id *dmi)
{
quirks = dmi->driver_data;
@@ -152,6 +156,15 @@
},
{
.callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X302UA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X302UA"),
+ },
+ .driver_data = &quirk_asus_wapf4,
+ },
+ {
+ .callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. X401U",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
@@ -413,6 +426,15 @@
},
{
.callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. UX330UAK",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "UX330UAK"),
+ },
+ .driver_data = &quirk_asus_ux330uak,
+ },
+ {
+ .callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. X550LB",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 8499d3a..8a1bfd4 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -1109,6 +1109,15 @@
}
/*
+ * Some devices dont support or have borcken get_als method
+ * but still support set method.
+ */
+static void asus_wmi_set_als(void)
+{
+ asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL);
+}
+
+/*
* Hwmon device
*/
static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan,
@@ -2120,6 +2129,9 @@
goto fail_rfkill;
}
+ if (asus->driver->quirks->wmi_force_als_set)
+ asus_wmi_set_als();
+
/* Some Asus desktop boards export an acpi-video backlight interface,
stop this from showing up */
chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h
index fdff626..5db052d 100644
--- a/drivers/platform/x86/asus-wmi.h
+++ b/drivers/platform/x86/asus-wmi.h
@@ -45,6 +45,7 @@
bool store_backlight_power;
bool wmi_backlight_power;
bool wmi_backlight_native;
+ bool wmi_force_als_set;
int wapf;
/*
* For machines with AMD graphic chips, it will send out WMI event
diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c
index 7808076..a74340d 100644
--- a/drivers/platform/x86/intel-vbtn.c
+++ b/drivers/platform/x86/intel-vbtn.c
@@ -37,6 +37,10 @@
static const struct key_entry intel_vbtn_keymap[] = {
{ KE_IGNORE, 0xC0, { KEY_POWER } }, /* power key press */
{ KE_KEY, 0xC1, { KEY_POWER } }, /* power key release */
+ { KE_KEY, 0xC4, { KEY_VOLUMEUP } }, /* volume-up key press */
+ { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* volume-up key release */
+ { KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* volume-down key press */
+ { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* volume-down key release */
{ KE_END },
};
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index b65ce75..60ee94e 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -9526,7 +9526,7 @@
},
};
-static int __init set_ibm_param(const char *val, struct kernel_param *kp)
+static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
{
unsigned int i;
struct ibm_struct *ibm;
diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c
index 5b31889..1a28e56 100644
--- a/drivers/power/reset/msm-poweroff.c
+++ b/drivers/power/reset/msm-poweroff.c
@@ -76,7 +76,7 @@
#endif
static bool scm_dload_supported;
-static int dload_set(const char *val, struct kernel_param *kp);
+static int dload_set(const char *val, const struct kernel_param *kp);
/* interface for exporting attributes */
struct reset_attribute {
struct attribute attr;
@@ -179,7 +179,7 @@
pr_err("Failed to set secure EDLOAD mode: %d\n", ret);
}
-static int dload_set(const char *val, struct kernel_param *kp)
+static int dload_set(const char *val, const struct kernel_param *kp)
{
int ret;
diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c
index 5cee9aa..48a11fd 100644
--- a/drivers/power/supply/ab8500_charger.c
+++ b/drivers/power/supply/ab8500_charger.c
@@ -3218,11 +3218,13 @@
}
/* Enable backup battery charging */
- abx500_mask_and_set_register_interruptible(di->dev,
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
AB8500_RTC, AB8500_RTC_CTRL_REG,
RTC_BUP_CH_ENA, RTC_BUP_CH_ENA);
- if (ret < 0)
+ if (ret < 0) {
dev_err(di->dev, "%s mask and set failed\n", __func__);
+ goto out;
+ }
if (is_ab8540(di->parent)) {
ret = abx500_mask_and_set_register_interruptible(di->dev,
diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c
index 50171fd..8ed2782 100644
--- a/drivers/power/supply/bq24190_charger.c
+++ b/drivers/power/supply/bq24190_charger.c
@@ -506,6 +506,9 @@
int ret, limit = 100;
u8 v;
+ if (device_property_read_bool(bdi->dev, "disable-reset"))
+ return 0;
+
/* Reset the registers */
ret = bq24190_write_mask(bdi, BQ24190_REG_POC,
BQ24190_REG_POC_RESET_MASK,
@@ -1184,8 +1187,13 @@
}
} while (f_reg && ++i < 2);
+ /* ignore over/under voltage fault after disconnect */
+ if (f_reg == (1 << BQ24190_REG_F_CHRG_FAULT_SHIFT) &&
+ !(ss_reg & BQ24190_REG_SS_PG_STAT_MASK))
+ f_reg = 0;
+
if (f_reg != bdi->f_reg) {
- dev_info(bdi->dev,
+ dev_warn(bdi->dev,
"Fault: boost %d, charge %d, battery %d, ntc %d\n",
!!(f_reg & BQ24190_REG_F_BOOST_FAULT_MASK),
!!(f_reg & BQ24190_REG_F_CHRG_FAULT_MASK),
diff --git a/drivers/power/supply/isp1704_charger.c b/drivers/power/supply/isp1704_charger.c
index 4cd6899..95af5f3 100644
--- a/drivers/power/supply/isp1704_charger.c
+++ b/drivers/power/supply/isp1704_charger.c
@@ -418,6 +418,10 @@
pdata = devm_kzalloc(&pdev->dev,
sizeof(struct isp1704_charger_data), GFP_KERNEL);
+ if (!pdata) {
+ ret = -ENOMEM;
+ goto fail0;
+ }
pdata->enable_gpio = gpio;
dev_info(&pdev->dev, "init gpio %d\n", pdata->enable_gpio);
diff --git a/drivers/power/supply/pda_power.c b/drivers/power/supply/pda_power.c
index dfe1ee8..922a867 100644
--- a/drivers/power/supply/pda_power.c
+++ b/drivers/power/supply/pda_power.c
@@ -30,9 +30,9 @@
static struct device *dev;
static struct pda_power_pdata *pdata;
static struct resource *ac_irq, *usb_irq;
-static struct timer_list charger_timer;
-static struct timer_list supply_timer;
-static struct timer_list polling_timer;
+static struct delayed_work charger_work;
+static struct delayed_work polling_work;
+static struct delayed_work supply_work;
static int polling;
static struct power_supply *pda_psy_ac, *pda_psy_usb;
@@ -140,7 +140,7 @@
}
}
-static void supply_timer_func(unsigned long unused)
+static void supply_work_func(struct work_struct *work)
{
if (ac_status == PDA_PSY_TO_CHANGE) {
ac_status = new_ac_status;
@@ -161,11 +161,12 @@
* Okay, charger set. Now wait a bit before notifying supplicants,
* charge power should stabilize.
*/
- mod_timer(&supply_timer,
- jiffies + msecs_to_jiffies(pdata->wait_for_charger));
+ cancel_delayed_work(&supply_work);
+ schedule_delayed_work(&supply_work,
+ msecs_to_jiffies(pdata->wait_for_charger));
}
-static void charger_timer_func(unsigned long unused)
+static void charger_work_func(struct work_struct *work)
{
update_status();
psy_changed();
@@ -184,13 +185,14 @@
* Wait a bit before reading ac/usb line status and setting charger,
* because ac/usb status readings may lag from irq.
*/
- mod_timer(&charger_timer,
- jiffies + msecs_to_jiffies(pdata->wait_for_status));
+ cancel_delayed_work(&charger_work);
+ schedule_delayed_work(&charger_work,
+ msecs_to_jiffies(pdata->wait_for_status));
return IRQ_HANDLED;
}
-static void polling_timer_func(unsigned long unused)
+static void polling_work_func(struct work_struct *work)
{
int changed = 0;
@@ -211,8 +213,9 @@
if (changed)
psy_changed();
- mod_timer(&polling_timer,
- jiffies + msecs_to_jiffies(pdata->polling_interval));
+ cancel_delayed_work(&polling_work);
+ schedule_delayed_work(&polling_work,
+ msecs_to_jiffies(pdata->polling_interval));
}
#if IS_ENABLED(CONFIG_USB_PHY)
@@ -250,8 +253,9 @@
* Wait a bit before reading ac/usb line status and setting charger,
* because ac/usb status readings may lag from irq.
*/
- mod_timer(&charger_timer,
- jiffies + msecs_to_jiffies(pdata->wait_for_status));
+ cancel_delayed_work(&charger_work);
+ schedule_delayed_work(&charger_work,
+ msecs_to_jiffies(pdata->wait_for_status));
return NOTIFY_OK;
}
@@ -300,8 +304,8 @@
if (!pdata->ac_max_uA)
pdata->ac_max_uA = 500000;
- setup_timer(&charger_timer, charger_timer_func, 0);
- setup_timer(&supply_timer, supply_timer_func, 0);
+ INIT_DELAYED_WORK(&charger_work, charger_work_func);
+ INIT_DELAYED_WORK(&supply_work, supply_work_func);
ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac");
usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb");
@@ -385,9 +389,10 @@
if (polling) {
dev_dbg(dev, "will poll for status\n");
- setup_timer(&polling_timer, polling_timer_func, 0);
- mod_timer(&polling_timer,
- jiffies + msecs_to_jiffies(pdata->polling_interval));
+ INIT_DELAYED_WORK(&polling_work, polling_work_func);
+ cancel_delayed_work(&polling_work);
+ schedule_delayed_work(&polling_work,
+ msecs_to_jiffies(pdata->polling_interval));
}
if (ac_irq || usb_irq)
@@ -433,9 +438,9 @@
free_irq(ac_irq->start, pda_psy_ac);
if (polling)
- del_timer_sync(&polling_timer);
- del_timer_sync(&charger_timer);
- del_timer_sync(&supply_timer);
+ cancel_delayed_work_sync(&polling_work);
+ cancel_delayed_work_sync(&charger_work);
+ cancel_delayed_work_sync(&supply_work);
if (pdata->is_usb_online)
power_supply_unregister(pda_psy_usb);
diff --git a/drivers/power/supply/qcom/Kconfig b/drivers/power/supply/qcom/Kconfig
index e571eb4..1c8f116 100644
--- a/drivers/power/supply/qcom/Kconfig
+++ b/drivers/power/supply/qcom/Kconfig
@@ -29,6 +29,17 @@
The driver reports the charger status via the power supply framework.
A charger status change triggers an IRQ via the device STAT pin.
+config SMB1360_CHARGER_FG
+ tristate "SMB1360 Charger and Fuel Gauge"
+ depends on I2C
+ help
+ Say Y to include support for SMB1360 Charger and Fuel Gauge.
+ SMB1360 is a single path switching mode charger capable of charging
+ the battery with 1.5Amps of current. It supports a fuel gauge which
+ uses voltage and coloumb counting for state of charge reporting.
+ The driver reports the status via the power supply framework.
+ A status change triggers an IRQ via the device STAT pin.
+
config SMB1355_SLAVE_CHARGER
tristate "SMB1355 Slave Battery Charger"
depends on MFD_I2C_PMIC
@@ -104,6 +115,26 @@
module. It also allows userspace code to read diagnostics of voltage
and current measured during certain phases of the pulses.
+config QPNP_VM_BMS
+ tristate "QPNP Voltage-Mode Battery Monitoring System driver"
+ depends on MFD_SPMI_PMIC
+ help
+ Say Y here to enable support for QPNP chip vm-bms device.
+ The voltage-mode (vm) BMS driver uses periodic VBATT
+ readings from the battery to calculate the State of
+ Charge.
+
+config QPNP_LINEAR_CHARGER
+ tristate "QPNP Linear Charger driver"
+ depends on MFD_SPMI_PMIC
+ depends on THERMAL_QPNP_ADC_TM
+ help
+ Say Y here to enable the Linear battery charger which supports USB
+ detection and charging. The driver also offers relevant information
+ to userspace via the power supply framework.
+ The power supply framework is used to communicate battery and
+ usb properties to userspace and other driver consumers like USB.
+
config QPNP_TYPEC
tristate "QPNP Type-C driver"
depends on MFD_SPMI_PMIC
diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile
index de76a5b..da20d93 100644
--- a/drivers/power/supply/qcom/Makefile
+++ b/drivers/power/supply/qcom/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_QPNP_FG_GEN3) += qpnp-fg-gen3.o fg-memif.o fg-util.o
obj-$(CONFIG_QPNP_SMBCHARGER) += qpnp-smbcharger.o pmic-voter.o
obj-$(CONFIG_SMB135X_CHARGER) += smb135x-charger.o pmic-voter.o
+obj-$(CONFIG_SMB1360_CHARGER_FG) += smb1360-charger-fg.o
obj-$(CONFIG_SMB1355_SLAVE_CHARGER) += smb1355-charger.o pmic-voter.o
obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o battery.o
obj-$(CONFIG_QPNP_SMB2) += step-chg-jeita.o battery.o qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o
@@ -11,3 +12,5 @@
obj-$(CONFIG_QPNP_TYPEC) += qpnp-typec.o
obj-$(CONFIG_QPNP_SMB5) += step-chg-jeita.o battery.o qpnp-smb5.o smb5-lib.o pmic-voter.o storm-watch.o schgm-flash.o
obj-$(CONFIG_SMB1390_CHARGE_PUMP) += smb1390-charger.o pmic-voter.o
+obj-$(CONFIG_QPNP_VM_BMS) += qpnp-vm-bms.o batterydata-lib.o batterydata-interface.o
+obj-$(CONFIG_QPNP_LINEAR_CHARGER) += qpnp-linear-charger.o
diff --git a/drivers/power/supply/qcom/batterydata-interface.c b/drivers/power/supply/qcom/batterydata-interface.c
new file mode 100644
index 0000000..0187827
--- /dev/null
+++ b/drivers/power/supply/qcom/batterydata-interface.c
@@ -0,0 +1,218 @@
+/* Copyright (c) 2014, 2018, 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) "BATTERY: %s: " fmt, __func__
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/uaccess.h>
+#include <linux/batterydata-lib.h>
+#include <linux/batterydata-interface.h>
+
+struct battery_data {
+ dev_t dev_no;
+ struct class *battery_class;
+ struct device *battery_device;
+ struct cdev battery_cdev;
+ struct bms_battery_data *profile;
+};
+static struct battery_data *the_battery;
+
+static int battery_data_open(struct inode *inode, struct file *file)
+{
+ struct battery_data *battery = container_of(inode->i_cdev,
+ struct battery_data, battery_cdev);
+
+ pr_debug("battery_data device opened\n");
+
+ file->private_data = battery;
+
+ return 0;
+}
+
+static long battery_data_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct battery_data *battery = file->private_data;
+ struct battery_params __user *bp_user =
+ (struct battery_params __user *)arg;
+ struct battery_params bp;
+ int soc, rbatt_sf, slope, fcc_mah;
+ int rc = 0;
+
+ if (!battery->profile) {
+ pr_err("Battery data not set!\n");
+ return -EINVAL;
+ }
+ if (copy_from_user(&bp, bp_user, sizeof(bp))) {
+ pr_err("copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ switch (cmd) {
+ case BPIOCXSOC:
+ soc = interpolate_pc(battery->profile->pc_temp_ocv_lut,
+ bp.batt_temp, bp.ocv_uv / 1000);
+ rc = put_user(soc, &bp_user->soc);
+ if (rc) {
+ pr_err("BPIOCXSOC: Failed to 'put_user' rc=%d\n", rc);
+ goto ret_err;
+ }
+ pr_debug("BPIOCXSOC: ocv=%d batt_temp=%d soc=%d\n",
+ bp.ocv_uv / 1000, bp.batt_temp, soc);
+ break;
+ case BPIOCXRBATT:
+ rbatt_sf = interpolate_scalingfactor(
+ battery->profile->rbatt_sf_lut,
+ bp.batt_temp, bp.soc);
+ rc = put_user(rbatt_sf, &bp_user->rbatt_sf);
+ if (rc) {
+ pr_err("BPIOCXRBATT: Failed to 'put_user' rc=%d\n", rc);
+ goto ret_err;
+ }
+ pr_debug("BPIOCXRBATT: soc=%d batt_temp=%d rbatt_sf=%d\n",
+ bp.soc, bp.batt_temp, rbatt_sf);
+ break;
+ case BPIOCXSLOPE:
+ slope = interpolate_slope(battery->profile->pc_temp_ocv_lut,
+ bp.batt_temp, bp.soc);
+ rc = put_user(slope, &bp_user->slope);
+ if (rc) {
+ pr_err("BPIOCXSLOPE: Failed to 'put_user' rc=%d\n", rc);
+ goto ret_err;
+ }
+ pr_debug("BPIOCXSLOPE: soc=%d batt_temp=%d slope=%d\n",
+ bp.soc, bp.batt_temp, slope);
+ break;
+ case BPIOCXFCC:
+ fcc_mah = interpolate_fcc(battery->profile->fcc_temp_lut,
+ bp.batt_temp);
+ rc = put_user(fcc_mah, &bp_user->fcc_mah);
+ if (rc) {
+ pr_err("BPIOCXFCC: Failed to 'put_user' rc=%d\n", rc);
+ goto ret_err;
+ }
+ pr_debug("BPIOCXFCC: batt_temp=%d fcc_mah=%d\n",
+ bp.batt_temp, fcc_mah);
+ break;
+ default:
+ pr_err("IOCTL %d not supported\n", cmd);
+ rc = -EINVAL;
+
+ }
+ret_err:
+ return rc;
+}
+
+static int battery_data_release(struct inode *inode, struct file *file)
+{
+ pr_debug("battery_data device closed\n");
+
+ return 0;
+}
+
+static const struct file_operations battery_data_fops = {
+ .owner = THIS_MODULE,
+ .open = battery_data_open,
+ .unlocked_ioctl = battery_data_ioctl,
+ .compat_ioctl = battery_data_ioctl,
+ .release = battery_data_release,
+};
+
+int config_battery_data(struct bms_battery_data *profile)
+{
+ if (!the_battery) {
+ pr_err("Battery data not initialized\n");
+ return -ENODEV;
+ }
+
+ the_battery->profile = profile;
+
+ pr_debug("Battery profile set - %s\n",
+ the_battery->profile->battery_type);
+
+ return 0;
+}
+
+static int batterydata_init(void)
+{
+ int rc;
+ struct battery_data *battery;
+
+ battery = kzalloc(sizeof(*battery), GFP_KERNEL);
+ if (!battery)
+ return -ENOMEM;
+
+ /* character device to access the battery-data from userspace */
+ rc = alloc_chrdev_region(&battery->dev_no, 0, 1, "battery_data");
+ if (rc) {
+ pr_err("Unable to allocate chrdev rc=%d\n", rc);
+ return rc;
+ }
+ cdev_init(&battery->battery_cdev, &battery_data_fops);
+ rc = cdev_add(&battery->battery_cdev, battery->dev_no, 1);
+ if (rc) {
+ pr_err("Unable to add battery_cdev rc=%d\n", rc);
+ goto unregister_chrdev;
+ }
+
+ battery->battery_class = class_create(THIS_MODULE, "battery_data");
+ if (IS_ERR_OR_NULL(battery->battery_class)) {
+ pr_err("Fail to create battery class\n");
+ rc = -ENODEV;
+ goto delete_cdev;
+ }
+
+ battery->battery_device = device_create(battery->battery_class,
+ NULL, battery->dev_no,
+ NULL, "battery_data");
+ if (IS_ERR(battery->battery_device)) {
+ pr_err("Fail to create battery_device device\n");
+ rc = -ENODEV;
+ goto delete_cdev;
+ }
+
+ the_battery = battery;
+
+ pr_info("Battery-data device created!\n");
+
+ return 0;
+
+delete_cdev:
+ cdev_del(&battery->battery_cdev);
+unregister_chrdev:
+ unregister_chrdev_region(battery->dev_no, 1);
+ the_battery = NULL;
+ return rc;
+}
+subsys_initcall(batterydata_init);
+
+static void batterydata_exit(void)
+{
+ if (the_battery) {
+ device_destroy(the_battery->battery_class, the_battery->dev_no);
+ cdev_del(&the_battery->battery_cdev);
+ unregister_chrdev_region(the_battery->dev_no, 1);
+ }
+ kfree(the_battery);
+ the_battery = NULL;
+}
+module_exit(batterydata_exit);
+
+MODULE_DESCRIPTION("Battery-data Interface driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/supply/qcom/batterydata-lib.c b/drivers/power/supply/qcom/batterydata-lib.c
new file mode 100644
index 0000000..fae5658
--- /dev/null
+++ b/drivers/power/supply/qcom/batterydata-lib.c
@@ -0,0 +1,493 @@
+/* Copyright (c) 2012-2014, 2018, 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/batterydata-lib.h>
+
+int linear_interpolate(int y0, int x0, int y1, int x1, int x)
+{
+ if (y0 == y1 || x == x0)
+ return y0;
+ if (x1 == x0 || x == x1)
+ return y1;
+
+ return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
+}
+
+static int interpolate_single_lut_scaled(struct single_row_lut *lut,
+ int x, int scale)
+{
+ int i, result;
+
+ if (x < lut->x[0] * scale) {
+ pr_debug("x %d less than known range return y = %d lut = %pS\n",
+ x, lut->y[0], lut);
+ return lut->y[0];
+ }
+ if (x > lut->x[lut->cols - 1] * scale) {
+ pr_debug("x %d more than known range return y = %d lut = %pS\n",
+ x, lut->y[lut->cols - 1], lut);
+ return lut->y[lut->cols - 1];
+ }
+
+ for (i = 0; i < lut->cols; i++)
+ if (x <= lut->x[i] * scale)
+ break;
+ if (x == lut->x[i] * scale) {
+ result = lut->y[i];
+ } else {
+ result = linear_interpolate(
+ lut->y[i - 1],
+ lut->x[i - 1] * scale,
+ lut->y[i],
+ lut->x[i] * scale,
+ x);
+ }
+ return result;
+}
+
+int interpolate_fcc(struct single_row_lut *fcc_temp_lut, int batt_temp)
+{
+ return interpolate_single_lut_scaled(fcc_temp_lut,
+ batt_temp,
+ DEGC_SCALE);
+}
+
+int interpolate_scalingfactor_fcc(struct single_row_lut *fcc_sf_lut,
+ int cycles)
+{
+ /*
+ * sf table could be null when no battery aging data is available, in
+ * that case return 100%
+ */
+ if (fcc_sf_lut)
+ return interpolate_single_lut_scaled(fcc_sf_lut, cycles, 1);
+ else
+ return 100;
+}
+
+int interpolate_scalingfactor(struct sf_lut *sf_lut, int row_entry, int pc)
+{
+ int i, scalefactorrow1, scalefactorrow2, scalefactor, rows, cols;
+ int row1 = 0;
+ int row2 = 0;
+
+ /*
+ * sf table could be null when no battery aging data is available, in
+ * that case return 100%
+ */
+ if (!sf_lut)
+ return 100;
+
+ rows = sf_lut->rows;
+ cols = sf_lut->cols;
+ if (pc > sf_lut->percent[0]) {
+ pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
+ row1 = 0;
+ row2 = 0;
+ } else if (pc < sf_lut->percent[rows - 1]) {
+ pr_debug("pc %d less than known pc ranges for sf\n", pc);
+ row1 = rows - 1;
+ row2 = rows - 1;
+ } else {
+ for (i = 0; i < rows; i++) {
+ if (pc == sf_lut->percent[i]) {
+ row1 = i;
+ row2 = i;
+ break;
+ }
+ if (pc > sf_lut->percent[i]) {
+ row1 = i - 1;
+ row2 = i;
+ break;
+ }
+ }
+ }
+
+ if (row_entry < sf_lut->row_entries[0] * DEGC_SCALE)
+ row_entry = sf_lut->row_entries[0] * DEGC_SCALE;
+ if (row_entry > sf_lut->row_entries[cols - 1] * DEGC_SCALE)
+ row_entry = sf_lut->row_entries[cols - 1] * DEGC_SCALE;
+
+ for (i = 0; i < cols; i++)
+ if (row_entry <= sf_lut->row_entries[i] * DEGC_SCALE)
+ break;
+ if (row_entry == sf_lut->row_entries[i] * DEGC_SCALE) {
+ scalefactor = linear_interpolate(
+ sf_lut->sf[row1][i],
+ sf_lut->percent[row1],
+ sf_lut->sf[row2][i],
+ sf_lut->percent[row2],
+ pc);
+ return scalefactor;
+ }
+
+ scalefactorrow1 = linear_interpolate(
+ sf_lut->sf[row1][i - 1],
+ sf_lut->row_entries[i - 1] * DEGC_SCALE,
+ sf_lut->sf[row1][i],
+ sf_lut->row_entries[i] * DEGC_SCALE,
+ row_entry);
+
+ scalefactorrow2 = linear_interpolate(
+ sf_lut->sf[row2][i - 1],
+ sf_lut->row_entries[i - 1] * DEGC_SCALE,
+ sf_lut->sf[row2][i],
+ sf_lut->row_entries[i] * DEGC_SCALE,
+ row_entry);
+
+ scalefactor = linear_interpolate(
+ scalefactorrow1,
+ sf_lut->percent[row1],
+ scalefactorrow2,
+ sf_lut->percent[row2],
+ pc);
+
+ return scalefactor;
+}
+
+/* get ocv given a soc -- reverse lookup */
+int interpolate_ocv(struct pc_temp_ocv_lut *pc_temp_ocv,
+ int batt_temp, int pc)
+{
+ int i, ocvrow1, ocvrow2, ocv, rows, cols;
+ int row1 = 0;
+ int row2 = 0;
+
+ rows = pc_temp_ocv->rows;
+ cols = pc_temp_ocv->cols;
+ if (pc > pc_temp_ocv->percent[0]) {
+ pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
+ row1 = 0;
+ row2 = 0;
+ } else if (pc < pc_temp_ocv->percent[rows - 1]) {
+ pr_debug("pc %d less than known pc ranges for sf\n", pc);
+ row1 = rows - 1;
+ row2 = rows - 1;
+ } else {
+ for (i = 0; i < rows; i++) {
+ if (pc == pc_temp_ocv->percent[i]) {
+ row1 = i;
+ row2 = i;
+ break;
+ }
+ if (pc > pc_temp_ocv->percent[i]) {
+ row1 = i - 1;
+ row2 = i;
+ break;
+ }
+ }
+ }
+
+ if (batt_temp < pc_temp_ocv->temp[0] * DEGC_SCALE)
+ batt_temp = pc_temp_ocv->temp[0] * DEGC_SCALE;
+ if (batt_temp > pc_temp_ocv->temp[cols - 1] * DEGC_SCALE)
+ batt_temp = pc_temp_ocv->temp[cols - 1] * DEGC_SCALE;
+
+ for (i = 0; i < cols; i++)
+ if (batt_temp <= pc_temp_ocv->temp[i] * DEGC_SCALE)
+ break;
+ if (batt_temp == pc_temp_ocv->temp[i] * DEGC_SCALE) {
+ ocv = linear_interpolate(
+ pc_temp_ocv->ocv[row1][i],
+ pc_temp_ocv->percent[row1],
+ pc_temp_ocv->ocv[row2][i],
+ pc_temp_ocv->percent[row2],
+ pc);
+ return ocv;
+ }
+
+ ocvrow1 = linear_interpolate(
+ pc_temp_ocv->ocv[row1][i - 1],
+ pc_temp_ocv->temp[i - 1] * DEGC_SCALE,
+ pc_temp_ocv->ocv[row1][i],
+ pc_temp_ocv->temp[i] * DEGC_SCALE,
+ batt_temp);
+
+ ocvrow2 = linear_interpolate(
+ pc_temp_ocv->ocv[row2][i - 1],
+ pc_temp_ocv->temp[i - 1] * DEGC_SCALE,
+ pc_temp_ocv->ocv[row2][i],
+ pc_temp_ocv->temp[i] * DEGC_SCALE,
+ batt_temp);
+
+ ocv = linear_interpolate(
+ ocvrow1,
+ pc_temp_ocv->percent[row1],
+ ocvrow2,
+ pc_temp_ocv->percent[row2],
+ pc);
+
+ return ocv;
+}
+
+int interpolate_pc(struct pc_temp_ocv_lut *pc_temp_ocv,
+ int batt_temp, int ocv)
+{
+ int i, j, pcj, pcj_minus_one, pc;
+ int rows = pc_temp_ocv->rows;
+ int cols = pc_temp_ocv->cols;
+
+ if (batt_temp < pc_temp_ocv->temp[0] * DEGC_SCALE) {
+ pr_debug("batt_temp %d < known temp range\n", batt_temp);
+ batt_temp = pc_temp_ocv->temp[0] * DEGC_SCALE;
+ }
+
+ if (batt_temp > pc_temp_ocv->temp[cols - 1] * DEGC_SCALE) {
+ pr_debug("batt_temp %d > known temp range\n", batt_temp);
+ batt_temp = pc_temp_ocv->temp[cols - 1] * DEGC_SCALE;
+ }
+
+ for (j = 0; j < cols; j++)
+ if (batt_temp <= pc_temp_ocv->temp[j] * DEGC_SCALE)
+ break;
+ if (batt_temp == pc_temp_ocv->temp[j] * DEGC_SCALE) {
+ /* found an exact match for temp in the table */
+ if (ocv >= pc_temp_ocv->ocv[0][j])
+ return pc_temp_ocv->percent[0];
+ if (ocv <= pc_temp_ocv->ocv[rows - 1][j])
+ return pc_temp_ocv->percent[rows - 1];
+ for (i = 0; i < rows; i++) {
+ if (ocv >= pc_temp_ocv->ocv[i][j]) {
+ if (ocv == pc_temp_ocv->ocv[i][j])
+ return pc_temp_ocv->percent[i];
+ pc = linear_interpolate(
+ pc_temp_ocv->percent[i],
+ pc_temp_ocv->ocv[i][j],
+ pc_temp_ocv->percent[i - 1],
+ pc_temp_ocv->ocv[i - 1][j],
+ ocv);
+ return pc;
+ }
+ }
+ }
+
+ /*
+ * batt_temp is within temperature for
+ * column j-1 and j
+ */
+ if (ocv >= pc_temp_ocv->ocv[0][j])
+ return pc_temp_ocv->percent[0];
+ if (ocv <= pc_temp_ocv->ocv[rows - 1][j - 1])
+ return pc_temp_ocv->percent[rows - 1];
+
+ pcj_minus_one = 0;
+ pcj = 0;
+ for (i = 0; i < rows-1; i++) {
+ if (pcj == 0
+ && is_between(pc_temp_ocv->ocv[i][j],
+ pc_temp_ocv->ocv[i+1][j], ocv)) {
+ pcj = linear_interpolate(
+ pc_temp_ocv->percent[i],
+ pc_temp_ocv->ocv[i][j],
+ pc_temp_ocv->percent[i + 1],
+ pc_temp_ocv->ocv[i+1][j],
+ ocv);
+ }
+
+ if (pcj_minus_one == 0
+ && is_between(pc_temp_ocv->ocv[i][j-1],
+ pc_temp_ocv->ocv[i+1][j-1], ocv)) {
+ pcj_minus_one = linear_interpolate(
+ pc_temp_ocv->percent[i],
+ pc_temp_ocv->ocv[i][j-1],
+ pc_temp_ocv->percent[i + 1],
+ pc_temp_ocv->ocv[i+1][j-1],
+ ocv);
+ }
+
+ if (pcj && pcj_minus_one) {
+ pc = linear_interpolate(
+ pcj_minus_one,
+ pc_temp_ocv->temp[j-1] * DEGC_SCALE,
+ pcj,
+ pc_temp_ocv->temp[j] * DEGC_SCALE,
+ batt_temp);
+ return pc;
+ }
+ }
+
+ if (pcj)
+ return pcj;
+
+ if (pcj_minus_one)
+ return pcj_minus_one;
+
+ pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%\n",
+ ocv, batt_temp);
+ return 100;
+}
+
+int interpolate_slope(struct pc_temp_ocv_lut *pc_temp_ocv,
+ int batt_temp, int pc)
+{
+ int i, ocvrow1, ocvrow2, rows, cols;
+ int row1 = 0;
+ int row2 = 0;
+ int slope;
+
+ rows = pc_temp_ocv->rows;
+ cols = pc_temp_ocv->cols;
+ if (pc >= pc_temp_ocv->percent[0]) {
+ pr_debug("pc %d >= max pc range - use the slope at pc=%d\n",
+ pc, pc_temp_ocv->percent[0]);
+ row1 = 0;
+ row2 = 1;
+ } else if (pc <= pc_temp_ocv->percent[rows - 1]) {
+ pr_debug("pc %d is <= min pc range - use the slope at pc=%d\n",
+ pc, pc_temp_ocv->percent[rows - 1]);
+ row1 = rows - 2;
+ row2 = rows - 1;
+ } else {
+ for (i = 0; i < rows; i++) {
+ if (pc == pc_temp_ocv->percent[i]) {
+ row1 = i - 1;
+ row2 = i;
+ break;
+ }
+ if (pc > pc_temp_ocv->percent[i]) {
+ row1 = i - 1;
+ row2 = i;
+ break;
+ }
+ }
+ }
+
+ if (batt_temp < pc_temp_ocv->temp[0] * DEGC_SCALE)
+ batt_temp = pc_temp_ocv->temp[0] * DEGC_SCALE;
+ if (batt_temp > pc_temp_ocv->temp[cols - 1] * DEGC_SCALE)
+ batt_temp = pc_temp_ocv->temp[cols - 1] * DEGC_SCALE;
+
+ for (i = 0; i < cols; i++)
+ if (batt_temp <= pc_temp_ocv->temp[i] * DEGC_SCALE)
+ break;
+
+ if (batt_temp == pc_temp_ocv->temp[i] * DEGC_SCALE) {
+ slope = (pc_temp_ocv->ocv[row1][i] -
+ pc_temp_ocv->ocv[row2][i]);
+ if (slope <= 0) {
+ pr_warn("Slope=%d for pc=%d, using 1\n", slope, pc);
+ slope = 1;
+ }
+ slope *= 1000;
+ slope /= (pc_temp_ocv->percent[row1] -
+ pc_temp_ocv->percent[row2]);
+ return slope;
+ }
+ ocvrow1 = linear_interpolate(
+ pc_temp_ocv->ocv[row1][i - 1],
+ pc_temp_ocv->temp[i - 1] * DEGC_SCALE,
+ pc_temp_ocv->ocv[row1][i],
+ pc_temp_ocv->temp[i] * DEGC_SCALE,
+ batt_temp);
+
+ ocvrow2 = linear_interpolate(
+ pc_temp_ocv->ocv[row2][i - 1],
+ pc_temp_ocv->temp[i - 1] * DEGC_SCALE,
+ pc_temp_ocv->ocv[row2][i],
+ pc_temp_ocv->temp[i] * DEGC_SCALE,
+ batt_temp);
+
+ slope = (ocvrow1 - ocvrow2);
+ if (slope <= 0) {
+ pr_warn("Slope=%d for pc=%d, using 1\n", slope, pc);
+ slope = 1;
+ }
+ slope *= 1000;
+ slope /= (pc_temp_ocv->percent[row1] - pc_temp_ocv->percent[row2]);
+
+ return slope;
+}
+
+
+int interpolate_acc(struct ibat_temp_acc_lut *ibat_acc_lut,
+ int batt_temp, int ibat)
+{
+ int i, accrow1, accrow2, rows, cols;
+ int row1 = 0;
+ int row2 = 0;
+ int acc;
+
+ rows = ibat_acc_lut->rows;
+ cols = ibat_acc_lut->cols;
+
+ if (ibat > ibat_acc_lut->ibat[rows - 1]) {
+ pr_debug("ibatt(%d) > max range(%d)\n", ibat,
+ ibat_acc_lut->ibat[rows - 1]);
+ row1 = rows - 1;
+ row2 = rows - 2;
+ } else if (ibat < ibat_acc_lut->ibat[0]) {
+ pr_debug("ibatt(%d) < max range(%d)\n", ibat,
+ ibat_acc_lut->ibat[0]);
+ row1 = 0;
+ row2 = 0;
+ } else {
+ for (i = 0; i < rows; i++) {
+ if (ibat == ibat_acc_lut->ibat[i]) {
+ row1 = i;
+ row2 = i;
+ break;
+ }
+ if (ibat < ibat_acc_lut->ibat[i]) {
+ row1 = i;
+ row2 = i - 1;
+ break;
+ }
+ }
+ }
+
+ if (batt_temp < ibat_acc_lut->temp[0] * DEGC_SCALE)
+ batt_temp = ibat_acc_lut->temp[0] * DEGC_SCALE;
+ if (batt_temp > ibat_acc_lut->temp[cols - 1] * DEGC_SCALE)
+ batt_temp = ibat_acc_lut->temp[cols - 1] * DEGC_SCALE;
+
+ for (i = 0; i < cols; i++)
+ if (batt_temp <= ibat_acc_lut->temp[i] * DEGC_SCALE)
+ break;
+
+ if (batt_temp == (ibat_acc_lut->temp[i] * DEGC_SCALE)) {
+ acc = linear_interpolate(
+ ibat_acc_lut->acc[row1][i],
+ ibat_acc_lut->ibat[row1],
+ ibat_acc_lut->acc[row2][i],
+ ibat_acc_lut->ibat[row2],
+ ibat);
+ return acc;
+ }
+
+ accrow1 = linear_interpolate(
+ ibat_acc_lut->acc[row1][i - 1],
+ ibat_acc_lut->temp[i - 1] * DEGC_SCALE,
+ ibat_acc_lut->acc[row1][i],
+ ibat_acc_lut->temp[i] * DEGC_SCALE,
+ batt_temp);
+
+ accrow2 = linear_interpolate(
+ ibat_acc_lut->acc[row2][i - 1],
+ ibat_acc_lut->temp[i - 1] * DEGC_SCALE,
+ ibat_acc_lut->acc[row2][i],
+ ibat_acc_lut->temp[i] * DEGC_SCALE,
+ batt_temp);
+
+ acc = linear_interpolate(accrow1,
+ ibat_acc_lut->ibat[row1],
+ accrow2,
+ ibat_acc_lut->ibat[row2],
+ ibat);
+
+ if (acc < 0)
+ acc = 0;
+
+ return acc;
+}
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index 440bf74..7ba0ce5 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -1417,16 +1417,16 @@
QNOVO_CL_SKEW_DECIPCT, chip->cl.final_cc_uah);
chip->cl.final_cc_uah = chip->cl.final_cc_uah *
(1000 + QNOVO_CL_SKEW_DECIPCT);
- div64_s64(chip->cl.final_cc_uah, 1000);
+ chip->cl.final_cc_uah = div64_u64(chip->cl.final_cc_uah, 1000);
}
max_inc_val = chip->cl.learned_cc_uah
* (1000 + chip->dt.cl_max_cap_inc);
- div64_s64(max_inc_val, 1000);
+ max_inc_val = div64_u64(max_inc_val, 1000);
min_dec_val = chip->cl.learned_cc_uah
* (1000 - chip->dt.cl_max_cap_dec);
- div64_s64(min_dec_val, 1000);
+ min_dec_val = div64_u64(min_dec_val, 1000);
old_cap = chip->cl.learned_cc_uah;
if (chip->cl.final_cc_uah > max_inc_val)
@@ -1440,7 +1440,7 @@
if (chip->dt.cl_max_cap_limit) {
max_inc_val = (int64_t)chip->cl.nom_cap_uah * (1000 +
chip->dt.cl_max_cap_limit);
- div64_s64(max_inc_val, 1000);
+ max_inc_val = div64_u64(max_inc_val, 1000);
if (chip->cl.final_cc_uah > max_inc_val) {
fg_dbg(chip, FG_CAP_LEARN, "learning capacity %lld goes above max limit %lld\n",
chip->cl.final_cc_uah, max_inc_val);
@@ -1451,7 +1451,7 @@
if (chip->dt.cl_min_cap_limit) {
min_dec_val = (int64_t)chip->cl.nom_cap_uah * (1000 -
chip->dt.cl_min_cap_limit);
- div64_s64(min_dec_val, 1000);
+ min_dec_val = div64_u64(min_dec_val, 1000);
if (chip->cl.final_cc_uah < min_dec_val) {
fg_dbg(chip, FG_CAP_LEARN, "learning capacity %lld goes below min limit %lld\n",
chip->cl.final_cc_uah, min_dec_val);
@@ -1955,7 +1955,7 @@
}
val *= scaling_factor;
- div64_s64(val, 1000);
+ val = div64_u64(val, 1000);
rc = fg_sram_write(chip, ESR_RSLOW_CHG_WORD,
ESR_RSLOW_CHG_OFFSET, (u8 *)&val, 1, FG_IMA_DEFAULT);
if (rc < 0) {
@@ -1972,7 +1972,7 @@
}
val *= scaling_factor;
- div64_s64(val, 1000);
+ val = div64_u64(val, 1000);
rc = fg_sram_write(chip, ESR_RSLOW_DISCHG_WORD,
ESR_RSLOW_DISCHG_OFFSET, (u8 *)&val, 1, FG_IMA_DEFAULT);
if (rc < 0) {
diff --git a/drivers/power/supply/qcom/qpnp-linear-charger.c b/drivers/power/supply/qcom/qpnp-linear-charger.c
new file mode 100644
index 0000000..aedc77a
--- /dev/null
+++ b/drivers/power/supply/qcom/qpnp-linear-charger.c
@@ -0,0 +1,3544 @@
+/* Copyright (c) 2013-2015, 2017-2018, 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) "CHG: %s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/extcon.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/interrupt.h>
+#include <linux/power_supply.h>
+#include <linux/qpnp/qpnp-adc.h>
+#include <linux/alarmtimer.h>
+#include <linux/bitops.h>
+#include <linux/leds.h>
+#include <linux/debugfs.h>
+#include <linux/regmap.h>
+#include <linux/spmi.h>
+
+#define CREATE_MASK(NUM_BITS, POS) \
+ ((unsigned char) (((1 << (NUM_BITS)) - 1) << (POS)))
+#define LBC_MASK(MSB_BIT, LSB_BIT) \
+ CREATE_MASK(MSB_BIT - LSB_BIT + 1, LSB_BIT)
+
+/* Interrupt offsets */
+#define INT_RT_STS_REG 0x10
+#define FAST_CHG_ON_IRQ BIT(5)
+#define OVERTEMP_ON_IRQ BIT(4)
+#define BAT_TEMP_OK_IRQ BIT(1)
+#define BATT_PRES_IRQ BIT(0)
+
+/* USB CHARGER PATH peripheral register offsets */
+#define USB_IN_VALID_MASK BIT(1)
+#define CHG_GONE_BIT BIT(2)
+#define USB_SUSP_REG 0x47
+#define USB_SUSPEND_BIT BIT(0)
+#define USB_COMP_OVR1_REG 0xEA
+#define USBIN_LLIMIT_OK_MASK LBC_MASK(1, 0)
+#define USBIN_LLIMIT_OK_NO_OVERRIDE 0x00
+#define USBIN_LLIMIT_OK_OVERRIDE_1 0x03
+#define USB_OVP_TST5_REG 0xE7
+#define CHG_GONE_OK_EN_BIT BIT(2)
+
+/* CHARGER peripheral register offset */
+#define CHG_OPTION_REG 0x08
+#define CHG_OPTION_MASK BIT(7)
+#define CHG_STATUS_REG 0x09
+#define CHG_ON_BIT BIT(0)
+#define CHG_VDD_LOOP_BIT BIT(1)
+#define VINMIN_LOOP_BIT BIT(3)
+#define CHG_VDD_MAX_REG 0x40
+#define CHG_VDD_SAFE_REG 0x41
+#define CHG_IBAT_MAX_REG 0x44
+#define CHG_IBAT_SAFE_REG 0x45
+#define CHG_VIN_MIN_REG 0x47
+#define CHG_CTRL_REG 0x49
+#define CHG_ENABLE BIT(7)
+#define CHG_FORCE_BATT_ON BIT(0)
+#define CHG_EN_MASK (BIT(7) | BIT(0))
+#define CHG_FAILED_REG 0x4A
+#define CHG_FAILED_BIT BIT(7)
+#define CHG_VBAT_WEAK_REG 0x52
+#define CHG_IBATTERM_EN_REG 0x5B
+#define CHG_USB_ENUM_T_STOP_REG 0x4E
+#define CHG_TCHG_MAX_EN_REG 0x60
+#define CHG_TCHG_MAX_EN_BIT BIT(7)
+#define CHG_TCHG_MAX_MASK LBC_MASK(6, 0)
+#define CHG_TCHG_MAX_REG 0x61
+#define CHG_WDOG_EN_REG 0x65
+#define CHG_PERPH_RESET_CTRL3_REG 0xDA
+#define CHG_COMP_OVR1 0xEE
+#define CHG_VBAT_DET_OVR_MASK LBC_MASK(1, 0)
+#define CHG_TEST_LOOP_REG 0xE5
+#define VIN_MIN_LOOP_DISABLE_BIT BIT(0)
+#define OVERRIDE_0 0x2
+#define OVERRIDE_NONE 0x0
+
+/* BATTIF peripheral register offset */
+#define BAT_IF_PRES_STATUS_REG 0x08
+#define BATT_PRES_MASK BIT(7)
+#define BAT_IF_TEMP_STATUS_REG 0x09
+#define BATT_TEMP_HOT_MASK BIT(6)
+#define BATT_TEMP_COLD_MASK LBC_MASK(7, 6)
+#define BATT_TEMP_OK_MASK BIT(7)
+#define BAT_IF_VREF_BAT_THM_CTRL_REG 0x4A
+#define VREF_BATT_THERM_FORCE_ON LBC_MASK(7, 6)
+#define VREF_BAT_THM_ENABLED_FSM BIT(7)
+#define BAT_IF_BPD_CTRL_REG 0x48
+#define BATT_BPD_CTRL_SEL_MASK LBC_MASK(1, 0)
+#define BATT_BPD_OFFMODE_EN BIT(3)
+#define BATT_THM_EN BIT(1)
+#define BATT_ID_EN BIT(0)
+#define BAT_IF_BTC_CTRL 0x49
+#define BTC_COMP_EN_MASK BIT(7)
+#define BTC_COLD_MASK BIT(1)
+#define BTC_HOT_MASK BIT(0)
+#define BTC_COMP_OVERRIDE_REG 0xE5
+
+/* MISC peripheral register offset */
+#define MISC_REV2_REG 0x01
+#define MISC_BOOT_DONE_REG 0x42
+#define MISC_BOOT_DONE BIT(7)
+#define MISC_TRIM3_REG 0xF3
+#define MISC_TRIM3_VDD_MASK LBC_MASK(5, 4)
+#define MISC_TRIM4_REG 0xF4
+#define MISC_TRIM4_VDD_MASK BIT(4)
+
+#define PERP_SUBTYPE_REG 0x05
+#define SEC_ACCESS 0xD0
+
+/* Linear peripheral subtype values */
+#define LBC_CHGR_SUBTYPE 0x15
+#define LBC_BAT_IF_SUBTYPE 0x16
+#define LBC_USB_PTH_SUBTYPE 0x17
+#define LBC_MISC_SUBTYPE 0x18
+
+#define QPNP_CHG_I_MAX_MIN_90 90
+
+/* Feature flags */
+#define VDD_TRIM_SUPPORTED BIT(0)
+
+#define QPNP_CHARGER_DEV_NAME "qcom,qpnp-linear-charger"
+
+/* usb_interrupts */
+
+struct qpnp_lbc_irq {
+ int irq;
+ unsigned long disabled;
+ bool is_wake;
+};
+
+enum {
+ USBIN_VALID = 0,
+ USB_OVER_TEMP,
+ USB_CHG_GONE,
+ BATT_PRES,
+ BATT_TEMPOK,
+ CHG_DONE,
+ CHG_FAILED,
+ CHG_FAST_CHG,
+ CHG_VBAT_DET_LO,
+ MAX_IRQS,
+};
+
+enum {
+ USER = BIT(0),
+ THERMAL = BIT(1),
+ CURRENT = BIT(2),
+ SOC = BIT(3),
+ PARALLEL = BIT(4),
+ COLLAPSE = BIT(5),
+};
+
+enum bpd_type {
+ BPD_TYPE_BAT_ID,
+ BPD_TYPE_BAT_THM,
+ BPD_TYPE_BAT_THM_BAT_ID,
+};
+
+static const char * const bpd_label[] = {
+ [BPD_TYPE_BAT_ID] = "bpd_id",
+ [BPD_TYPE_BAT_THM] = "bpd_thm",
+ [BPD_TYPE_BAT_THM_BAT_ID] = "bpd_thm_id",
+};
+
+enum btc_type {
+ HOT_THD_25_PCT = 25,
+ HOT_THD_35_PCT = 35,
+ COLD_THD_70_PCT = 70,
+ COLD_THD_80_PCT = 80,
+};
+
+static u8 btc_value[] = {
+ [HOT_THD_25_PCT] = 0x0,
+ [HOT_THD_35_PCT] = BIT(0),
+ [COLD_THD_70_PCT] = 0x0,
+ [COLD_THD_80_PCT] = BIT(1),
+};
+
+static inline int get_bpd(const char *name)
+{
+ int i = 0;
+
+ for (i = 0; i < ARRAY_SIZE(bpd_label); i++) {
+ if (strcmp(bpd_label[i], name) == 0)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static enum power_supply_property msm_batt_power_props[] = {
+ POWER_SUPPLY_PROP_CHARGING_ENABLED,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_COOL_TEMP,
+ POWER_SUPPLY_PROP_WARM_TEMP,
+ POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL,
+};
+
+static char *pm_batt_supplied_to[] = {
+ "bms",
+};
+
+struct vddtrim_map {
+ int trim_uv;
+ int trim_val;
+};
+
+/*
+ * VDDTRIM is a 3 bit value which is split across two
+ * register TRIM3(bit 5:4) -> VDDTRIM bit(2:1)
+ * register TRIM4(bit 4) -> VDDTRIM bit(0)
+ */
+#define TRIM_CENTER 4
+#define MAX_VDD_EA_TRIM_CFG 8
+#define VDD_TRIM3_MASK LBC_MASK(2, 1)
+#define VDD_TRIM3_SHIFT 3
+#define VDD_TRIM4_MASK BIT(0)
+#define VDD_TRIM4_SHIFT 4
+#define AVG(VAL1, VAL2) ((VAL1 + VAL2) / 2)
+
+/*
+ * VDDTRIM table containing map of trim voltage and
+ * corresponding trim value.
+ */
+static struct vddtrim_map vddtrim_map[] = {
+ {36700, 0x00},
+ {28000, 0x01},
+ {19800, 0x02},
+ {10760, 0x03},
+ {0, 0x04},
+ {-8500, 0x05},
+ {-16800, 0x06},
+ {-25440, 0x07},
+};
+
+static const unsigned int qpnp_lbc_extcon_cable[] = {
+ EXTCON_USB,
+ EXTCON_USB_HOST,
+ EXTCON_NONE,
+};
+
+/*
+ * struct qpnp_lbc_chip - device information
+ * @dev: device pointer to access the parent
+ * @pdev: pdev pointer to access platform information
+ * @chgr_base: charger peripheral base address
+ * @bat_if_base: battery interface peripheral base address
+ * @usb_chgpth_base: USB charge path peripheral base address
+ * @misc_base: misc peripheral base address
+ * @bat_is_cool: indicates that battery is cool
+ * @bat_is_warm: indicates that battery is warm
+ * @chg_done: indicates that charging is completed
+ * @usb_present: present status of USB
+ * @batt_present: present status of battery
+ * @cfg_charging_disabled: disable drawing current from USB.
+ * @cfg_use_fake_battery: flag to report default battery properties
+ * @fastchg_on: indicate charger in fast charge mode
+ * @cfg_btc_disabled: flag to disable btc (disables hot and cold
+ * irqs)
+ * @cfg_max_voltage_mv: the max volts the batt should be charged up to
+ * @cfg_min_voltage_mv: VIN_MIN configuration
+ * @cfg_batt_weak_voltage_uv: weak battery voltage threshold
+ * @cfg_warm_bat_chg_ma: warm battery maximum charge current in mA
+ * @cfg_cool_bat_chg_ma: cool battery maximum charge current in mA
+ * @cfg_safe_voltage_mv: safe voltage to which battery can charge
+ * @cfg_warm_bat_mv: warm temperature battery target voltage
+ * @cfg_warm_bat_mv: warm temperature battery target voltage
+ * @cfg_cool_bat_mv: cool temperature battery target voltage
+ * @cfg_soc_resume_limit: SOC at which battery resumes charging
+ * @cfg_float_charge: enable float charging
+ * @charger_disabled: maintain USB path state.
+ * @cfg_charger_detect_eoc: charger can detect end of charging
+ * @cfg_disable_vbatdet_based_recharge: keep VBATDET comparator overridden to
+ * low and VBATDET irq disabled.
+ * @cfg_collapsible_chgr_support: support collapsible charger
+ * @cfg_chgr_led_support: support charger led work.
+ * @cfg_safe_current: battery safety current setting
+ * @cfg_hot_batt_p: hot battery threshold setting
+ * @cfg_cold_batt_p: eold battery threshold setting
+ * @cfg_warm_bat_decidegc: warm battery temperature in degree Celsius
+ * @cfg_cool_bat_decidegc: cool battery temperature in degree Celsius
+ * @fake_battery_soc: SOC value to be reported to userspace
+ * @cfg_tchg_mins: maximum allowed software initiated charge time
+ * @chg_failed_count: counter to maintained number of times charging
+ * failed
+ * @cfg_bpd_detection: battery present detection mechanism selection
+ * @cfg_thermal_levels: amount of thermal mitigation levels
+ * @cfg_thermal_mitigation: thermal mitigation level values
+ * @therm_lvl_sel: thermal mitigation level selection
+ * @jeita_configure_lock: lock to serialize jeita configuration request
+ * @hw_access_lock: lock to serialize access to charger registers
+ * @ibat_change_lock: lock to serialize ibat change requests from
+ * USB and thermal.
+ * @irq_lock lock to serialize enabling/disabling of irq
+ * @supported_feature_flag bitmask for all supported features
+ * @vddtrim_alarm alarm to schedule trim work at regular
+ * interval
+ * @vddtrim_work work to perform actual vddmax trimming
+ * @init_trim_uv initial trim voltage at bootup
+ * @delta_vddmax_uv current vddmax trim voltage
+ * @chg_enable_lock: lock to serialize charging enable/disable for
+ * SOC based resume charging
+ * @usb_psy: power supply to export information to
+ * userspace
+ * @bms_psy: power supply to export information to
+ * userspace
+ * @batt_psy: power supply to export information to
+ * userspace
+ */
+struct qpnp_lbc_chip {
+ struct device *dev;
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ u16 chgr_base;
+ u16 bat_if_base;
+ u16 usb_chgpth_base;
+ u16 misc_base;
+ bool bat_is_cool;
+ bool bat_is_warm;
+ bool chg_done;
+ bool usb_present;
+ bool batt_present;
+ bool cfg_charging_disabled;
+ bool cfg_btc_disabled;
+ bool cfg_use_fake_battery;
+ bool fastchg_on;
+ bool cfg_use_external_charger;
+ bool cfg_chgr_led_support;
+ bool non_collapsible_chgr_detected;
+ unsigned int cfg_warm_bat_chg_ma;
+ unsigned int cfg_cool_bat_chg_ma;
+ unsigned int cfg_safe_voltage_mv;
+ unsigned int cfg_max_voltage_mv;
+ unsigned int cfg_min_voltage_mv;
+ unsigned int cfg_charger_detect_eoc;
+ unsigned int cfg_disable_vbatdet_based_recharge;
+ unsigned int cfg_batt_weak_voltage_uv;
+ unsigned int cfg_collapsible_chgr_support;
+ unsigned int cfg_warm_bat_mv;
+ unsigned int cfg_cool_bat_mv;
+ unsigned int cfg_hot_batt_p;
+ unsigned int cfg_cold_batt_p;
+ unsigned int cfg_thermal_levels;
+ unsigned int therm_lvl_sel;
+ unsigned int *thermal_mitigation;
+ unsigned int cfg_safe_current;
+ unsigned int cfg_tchg_mins;
+ unsigned int chg_failed_count;
+ unsigned int supported_feature_flag;
+ int usb_online;
+ int cfg_bpd_detection;
+ int cfg_warm_bat_decidegc;
+ int cfg_cool_bat_decidegc;
+ int fake_battery_soc;
+ int cfg_soc_resume_limit;
+ int cfg_float_charge;
+ int charger_disabled;
+ int prev_max_ma;
+ int usb_psy_ma;
+ int delta_vddmax_uv;
+ int init_trim_uv;
+ enum power_supply_type usb_supply_type;
+ struct delayed_work collapsible_detection_work;
+
+ /* parallel-chg params */
+ int parallel_charging_enabled;
+ int lbc_max_chg_current;
+ int ichg_now;
+
+ struct alarm vddtrim_alarm;
+ struct work_struct vddtrim_work;
+ struct qpnp_lbc_irq irqs[MAX_IRQS];
+ struct mutex jeita_configure_lock;
+ struct mutex chg_enable_lock;
+ spinlock_t ibat_change_lock;
+ spinlock_t hw_access_lock;
+ spinlock_t irq_lock;
+ struct power_supply *usb_psy;
+ struct power_supply_desc usb_psy_d;
+ struct power_supply *bms_psy;
+ struct power_supply *batt_psy;
+ struct power_supply_desc batt_psy_d;
+ struct qpnp_adc_tm_btm_param adc_param;
+ struct qpnp_vadc_chip *vadc_dev;
+ struct qpnp_adc_tm_chip *adc_tm_dev;
+ struct led_classdev led_cdev;
+ struct dentry *debug_root;
+
+ /* parallel-chg params */
+ struct power_supply *parallel_psy;
+ struct power_supply_desc parallel_psy_d;
+ struct delayed_work parallel_work;
+ struct extcon_dev *extcon;
+};
+
+static void qpnp_lbc_enable_irq(struct qpnp_lbc_chip *chip,
+ struct qpnp_lbc_irq *irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->irq_lock, flags);
+ if (__test_and_clear_bit(0, &irq->disabled)) {
+ pr_debug("number = %d\n", irq->irq);
+ enable_irq(irq->irq);
+ if (irq->is_wake)
+ enable_irq_wake(irq->irq);
+ }
+ spin_unlock_irqrestore(&chip->irq_lock, flags);
+}
+
+static void qpnp_lbc_disable_irq(struct qpnp_lbc_chip *chip,
+ struct qpnp_lbc_irq *irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->irq_lock, flags);
+ if (!__test_and_set_bit(0, &irq->disabled)) {
+ pr_debug("number = %d\n", irq->irq);
+ disable_irq_nosync(irq->irq);
+ if (irq->is_wake)
+ disable_irq_wake(irq->irq);
+ }
+ spin_unlock_irqrestore(&chip->irq_lock, flags);
+}
+
+static int __qpnp_lbc_read(struct qpnp_lbc_chip *chip, u16 base,
+ u8 *val, int count)
+{
+ int rc = 0;
+
+ if (base == 0) {
+ pr_err("base addr cannot be zero\n");
+ return -EINVAL;
+ }
+
+ rc = regmap_bulk_read(chip->regmap, base, val, count);
+ if (rc)
+ pr_err("SPMI read failed base=0x%02x rc=%d\n", base, rc);
+
+ return rc;
+}
+
+static int __qpnp_lbc_write(struct qpnp_lbc_chip *chip, u16 base,
+ u8 *val, int count)
+{
+ int rc;
+
+ if (base == 0) {
+ pr_err("base addr cannot be zero\n");
+ return -EINVAL;
+ }
+
+ rc = regmap_bulk_write(chip->regmap, base, val, count);
+ if (rc)
+ pr_err("SPMI write failed base=0x%02x rc=%d\n", base, rc);
+
+ return rc;
+}
+
+static int __qpnp_lbc_secure_write(struct qpnp_lbc_chip *chip, u16 base,
+ u16 offset, u8 *val, int count)
+{
+ int rc;
+ u8 reg_val;
+
+ reg_val = 0xA5;
+ rc = __qpnp_lbc_write(chip, base + SEC_ACCESS, ®_val, 1);
+ if (rc)
+ return rc;
+
+ return __qpnp_lbc_write(chip, base + offset, val, 1);
+}
+
+static int qpnp_lbc_read(struct qpnp_lbc_chip *chip, u16 base,
+ u8 *val, int count)
+{
+ int rc = 0;
+ unsigned long flags;
+
+ if (base == 0) {
+ pr_err("base addr cannot be zero\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&chip->hw_access_lock, flags);
+ rc = __qpnp_lbc_read(chip, base, val, count);
+ spin_unlock_irqrestore(&chip->hw_access_lock, flags);
+
+ return rc;
+}
+
+static int qpnp_lbc_write(struct qpnp_lbc_chip *chip, u16 base,
+ u8 *val, int count)
+{
+ int rc = 0;
+ unsigned long flags;
+
+ if (base == 0) {
+ pr_err("base addr cannot be zero\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&chip->hw_access_lock, flags);
+ rc = __qpnp_lbc_write(chip, base, val, count);
+ spin_unlock_irqrestore(&chip->hw_access_lock, flags);
+
+ return rc;
+}
+
+static int qpnp_lbc_masked_write(struct qpnp_lbc_chip *chip, u16 base,
+ u8 mask, u8 val)
+{
+ int rc;
+ u8 reg_val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->hw_access_lock, flags);
+ rc = __qpnp_lbc_read(chip, base, ®_val, 1);
+ if (rc)
+ goto out;
+
+ pr_debug("addr = 0x%x read 0x%x\n", base, reg_val);
+
+ reg_val &= ~mask;
+ reg_val |= val & mask;
+
+ pr_debug("writing to base=%x val=%x\n", base, reg_val);
+
+ rc = __qpnp_lbc_write(chip, base, ®_val, 1);
+
+out:
+ spin_unlock_irqrestore(&chip->hw_access_lock, flags);
+ return rc;
+}
+
+static int __qpnp_lbc_secure_masked_write(struct qpnp_lbc_chip *chip, u16 base,
+ u16 offset, u8 mask, u8 val)
+{
+ int rc;
+ u8 reg_val, reg_val1;
+
+ rc = __qpnp_lbc_read(chip, base + offset, ®_val, 1);
+ if (rc)
+ return rc;
+
+ pr_debug("addr = 0x%x read 0x%x\n", base, reg_val);
+
+ reg_val &= ~mask;
+ reg_val |= val & mask;
+ pr_debug("writing to base=%x val=%x\n", base, reg_val);
+
+ reg_val1 = 0xA5;
+ rc = __qpnp_lbc_write(chip, base + SEC_ACCESS, ®_val1, 1);
+ if (rc)
+ return rc;
+
+ rc = __qpnp_lbc_write(chip, base + offset, ®_val, 1);
+
+ return rc;
+}
+
+static int qpnp_lbc_get_trim_voltage(u8 trim_reg)
+{
+ int i;
+
+ for (i = 0; i < MAX_VDD_EA_TRIM_CFG; i++)
+ if (trim_reg == vddtrim_map[i].trim_val)
+ return vddtrim_map[i].trim_uv;
+
+ pr_err("Invalid trim reg reg_val=%x\n", trim_reg);
+ return -EINVAL;
+}
+
+static u8 qpnp_lbc_get_trim_val(struct qpnp_lbc_chip *chip)
+{
+ int i, sign;
+ int delta_uv;
+
+ sign = (chip->delta_vddmax_uv >= 0) ? -1 : 1;
+
+ switch (sign) {
+ case -1:
+ for (i = TRIM_CENTER; i >= 0; i--) {
+ if (vddtrim_map[i].trim_uv > chip->delta_vddmax_uv) {
+ delta_uv = AVG(vddtrim_map[i].trim_uv,
+ vddtrim_map[i + 1].trim_uv);
+ if (chip->delta_vddmax_uv >= delta_uv)
+ return vddtrim_map[i].trim_val;
+ else
+ return vddtrim_map[i + 1].trim_val;
+ }
+ }
+ i = 0;
+ break;
+ case 1:
+ for (i = TRIM_CENTER; i < ARRAY_SIZE(vddtrim_map); i++) {
+ if (vddtrim_map[i].trim_uv < chip->delta_vddmax_uv) {
+ delta_uv = AVG(vddtrim_map[i].trim_uv,
+ vddtrim_map[i - 1].trim_uv);
+ if (chip->delta_vddmax_uv >= delta_uv)
+ return vddtrim_map[i - 1].trim_val;
+ else
+ return vddtrim_map[i].trim_val;
+ }
+ }
+ i = ARRAY_SIZE(vddtrim_map) - 1;
+ break;
+ }
+
+ return vddtrim_map[i].trim_val;
+}
+
+static int qpnp_lbc_is_usb_chg_plugged_in(struct qpnp_lbc_chip *chip)
+{
+ u8 usbin_valid_rt_sts;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->usb_chgpth_base + INT_RT_STS_REG,
+ &usbin_valid_rt_sts, 1);
+ if (rc)
+ return rc;
+
+ pr_debug("rt_sts 0x%x\n", usbin_valid_rt_sts);
+
+ return (usbin_valid_rt_sts & USB_IN_VALID_MASK) ? 1 : 0;
+}
+
+static int qpnp_lbc_is_chg_gone(struct qpnp_lbc_chip *chip)
+{
+ u8 rt_sts;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->usb_chgpth_base + INT_RT_STS_REG,
+ &rt_sts, 1);
+ if (rc)
+ return rc;
+
+ pr_debug("rt_sts 0x%x\n", rt_sts);
+
+ return (rt_sts & CHG_GONE_BIT) ? 1 : 0;
+}
+
+static int qpnp_lbc_charger_enable(struct qpnp_lbc_chip *chip, int reason,
+ int enable)
+{
+ int disabled = chip->charger_disabled;
+ u8 reg_val;
+ int rc = 0;
+
+ pr_debug("reason=%d requested_enable=%d disabled_status=%d\n",
+ reason, enable, disabled);
+ if (enable)
+ disabled &= ~reason;
+ else
+ disabled |= reason;
+
+ if (!!chip->charger_disabled == !!disabled)
+ goto skip;
+
+ reg_val = !!disabled ? CHG_FORCE_BATT_ON : CHG_ENABLE;
+ rc = qpnp_lbc_masked_write(chip, chip->chgr_base + CHG_CTRL_REG,
+ CHG_EN_MASK, reg_val);
+ if (rc) {
+ pr_err("Failed to %s charger\n",
+ reg_val ? "enable" : "disable");
+ return rc;
+ }
+skip:
+ chip->charger_disabled = disabled;
+ return rc;
+}
+
+static int qpnp_lbc_is_batt_present(struct qpnp_lbc_chip *chip)
+{
+ u8 batt_pres_rt_sts;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->bat_if_base + INT_RT_STS_REG,
+ &batt_pres_rt_sts, 1);
+ if (rc)
+ return rc;
+
+ return (batt_pres_rt_sts & BATT_PRES_IRQ) ? 1 : 0;
+}
+
+static int qpnp_lbc_bat_if_configure_btc(struct qpnp_lbc_chip *chip)
+{
+ u8 btc_cfg = 0, mask = 0, rc;
+
+ /* Do nothing if battery peripheral not present */
+ if (!chip->bat_if_base)
+ return 0;
+
+ if ((chip->cfg_hot_batt_p == HOT_THD_25_PCT)
+ || (chip->cfg_hot_batt_p == HOT_THD_35_PCT)) {
+ btc_cfg |= btc_value[chip->cfg_hot_batt_p];
+ mask |= BTC_HOT_MASK;
+ }
+
+ if ((chip->cfg_cold_batt_p == COLD_THD_70_PCT) ||
+ (chip->cfg_cold_batt_p == COLD_THD_80_PCT)) {
+ btc_cfg |= btc_value[chip->cfg_cold_batt_p];
+ mask |= BTC_COLD_MASK;
+ }
+
+ mask |= BTC_COMP_EN_MASK;
+ if (!chip->cfg_btc_disabled)
+ btc_cfg |= BTC_COMP_EN_MASK;
+
+ pr_debug("BTC configuration mask=%x\n", btc_cfg);
+
+ rc = qpnp_lbc_masked_write(chip,
+ chip->bat_if_base + BAT_IF_BTC_CTRL,
+ mask, btc_cfg);
+ if (rc)
+ pr_err("Failed to configure BTC\n");
+
+ return rc;
+}
+
+static int qpnp_chg_collapsible_chgr_config(struct qpnp_lbc_chip *chip,
+ bool enable)
+{
+ u8 reg_val;
+ int rc;
+
+ pr_debug("Configure for %scollapsible charger\n",
+ enable ? "" : "non-");
+ /*
+ * The flow to enable/disable the collapsible charger configuration:
+ * Enable: Override USBIN_LLIMIT_OK -->
+ * Disable VIN_MIN comparator -->
+ * Enable CHG_GONE comparator
+ * Disable: Enable VIN_MIN comparator -->
+ * Enable USBIN_LLIMIT_OK -->
+ * Disable CHG_GONE comparator
+ */
+ if (enable) {
+ /* Override USBIN_LLIMIT_OK */
+ reg_val = USBIN_LLIMIT_OK_OVERRIDE_1;
+ rc = __qpnp_lbc_secure_masked_write(chip,
+ chip->usb_chgpth_base,
+ USB_COMP_OVR1_REG,
+ USBIN_LLIMIT_OK_MASK, reg_val);
+ if (rc) {
+ pr_err("Failed to override USB_LLIMIT_OK\n");
+ return rc;
+ }
+ }
+
+ /* Configure VIN_MIN comparator */
+ rc = __qpnp_lbc_secure_masked_write(chip,
+ chip->chgr_base, CHG_TEST_LOOP_REG,
+ VIN_MIN_LOOP_DISABLE_BIT,
+ enable ? VIN_MIN_LOOP_DISABLE_BIT : 0);
+ if (rc) {
+ pr_err("Failed to %s VIN_MIN comparator\n",
+ enable ? "disable" : "enable");
+ return rc;
+ }
+
+ if (!enable) {
+ /* Enable USBIN_LLIMIT_OK */
+ reg_val = USBIN_LLIMIT_OK_NO_OVERRIDE;
+ rc = __qpnp_lbc_secure_masked_write(chip,
+ chip->usb_chgpth_base,
+ USB_COMP_OVR1_REG,
+ USBIN_LLIMIT_OK_MASK, reg_val);
+ if (rc) {
+ pr_err("Failed to override USB_LLIMIT_OK\n");
+ return rc;
+ }
+ }
+
+ /* Configure CHG_GONE comparator */
+ reg_val = enable ? CHG_GONE_OK_EN_BIT : 0;
+ rc = __qpnp_lbc_secure_masked_write(chip,
+ chip->usb_chgpth_base, USB_OVP_TST5_REG,
+ CHG_GONE_OK_EN_BIT, reg_val);
+ if (rc) {
+ pr_err("Failed to write CHG_GONE comparator\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+#define QPNP_LBC_VBATWEAK_MIN_UV 3000000
+#define QPNP_LBC_VBATWEAK_MAX_UV 3581250
+#define QPNP_LBC_VBATWEAK_STEP_UV 18750
+static int qpnp_lbc_vbatweak_set(struct qpnp_lbc_chip *chip, int voltage)
+{
+ u8 reg_val;
+ int rc;
+
+ if (voltage < QPNP_LBC_VBATWEAK_MIN_UV ||
+ voltage > QPNP_LBC_VBATWEAK_MAX_UV) {
+ rc = -EINVAL;
+ } else {
+ reg_val = (voltage - QPNP_LBC_VBATWEAK_MIN_UV) /
+ QPNP_LBC_VBATWEAK_STEP_UV;
+ pr_debug("VBAT_WEAK=%d setting %02x\n",
+ chip->cfg_batt_weak_voltage_uv, reg_val);
+ rc = qpnp_lbc_write(chip, chip->chgr_base + CHG_VBAT_WEAK_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to set VBAT_WEAK\n");
+ }
+
+ return rc;
+}
+
+#define QPNP_LBC_VBAT_MIN_MV 4000
+#define QPNP_LBC_VBAT_MAX_MV 4775
+#define QPNP_LBC_VBAT_STEP_MV 25
+static int qpnp_lbc_vddsafe_set(struct qpnp_lbc_chip *chip, int voltage)
+{
+ u8 reg_val;
+ int rc;
+
+ if (voltage < QPNP_LBC_VBAT_MIN_MV
+ || voltage > QPNP_LBC_VBAT_MAX_MV) {
+ pr_err("Invalid vddsafe voltage mV=%d min=%d max=%d\n",
+ voltage, QPNP_LBC_VBAT_MIN_MV,
+ QPNP_LBC_VBAT_MAX_MV);
+ return -EINVAL;
+ }
+ reg_val = (voltage - QPNP_LBC_VBAT_MIN_MV) / QPNP_LBC_VBAT_STEP_MV;
+ pr_debug("voltage=%d setting %02x\n", voltage, reg_val);
+ rc = qpnp_lbc_write(chip, chip->chgr_base + CHG_VDD_SAFE_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to set VDD_SAFE\n");
+
+ return rc;
+}
+
+static int qpnp_lbc_vddmax_set(struct qpnp_lbc_chip *chip, int voltage)
+{
+ u8 reg_val;
+ int rc, trim_val;
+ unsigned long flags;
+
+ if (voltage < QPNP_LBC_VBAT_MIN_MV
+ || voltage > QPNP_LBC_VBAT_MAX_MV) {
+ pr_err("Invalid vddmax voltage mV=%d min=%d max=%d\n",
+ voltage, QPNP_LBC_VBAT_MIN_MV,
+ QPNP_LBC_VBAT_MAX_MV);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&chip->hw_access_lock, flags);
+ reg_val = (voltage - QPNP_LBC_VBAT_MIN_MV) / QPNP_LBC_VBAT_STEP_MV;
+ pr_debug("voltage=%d setting %02x\n", voltage, reg_val);
+ rc = __qpnp_lbc_write(chip, chip->chgr_base + CHG_VDD_MAX_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to set VDD_MAX\n");
+ goto out;
+ }
+
+ /* Update trim value */
+ if (chip->supported_feature_flag & VDD_TRIM_SUPPORTED) {
+ trim_val = qpnp_lbc_get_trim_val(chip);
+ reg_val = (trim_val & VDD_TRIM3_MASK) << VDD_TRIM3_SHIFT;
+ rc = __qpnp_lbc_secure_masked_write(chip,
+ chip->misc_base, MISC_TRIM3_REG,
+ MISC_TRIM3_VDD_MASK, reg_val);
+ if (rc) {
+ pr_err("Failed to set MISC_TRIM3_REG\n");
+ goto out;
+ }
+
+ reg_val = (trim_val & VDD_TRIM4_MASK) << VDD_TRIM4_SHIFT;
+ rc = __qpnp_lbc_secure_masked_write(chip,
+ chip->misc_base, MISC_TRIM4_REG,
+ MISC_TRIM4_VDD_MASK, reg_val);
+ if (rc) {
+ pr_err("Failed to set MISC_TRIM4_REG\n");
+ goto out;
+ }
+
+ chip->delta_vddmax_uv = qpnp_lbc_get_trim_voltage(trim_val);
+ if (chip->delta_vddmax_uv == -EINVAL) {
+ pr_err("Invalid trim voltage=%d\n",
+ chip->delta_vddmax_uv);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ pr_debug("VDD_MAX delta=%d trim value=%x\n",
+ chip->delta_vddmax_uv, trim_val);
+ }
+
+out:
+ spin_unlock_irqrestore(&chip->hw_access_lock, flags);
+ return rc;
+}
+
+static int qpnp_lbc_set_appropriate_vddmax(struct qpnp_lbc_chip *chip)
+{
+ int rc;
+
+ if (chip->bat_is_cool)
+ rc = qpnp_lbc_vddmax_set(chip, chip->cfg_cool_bat_mv);
+ else if (chip->bat_is_warm)
+ rc = qpnp_lbc_vddmax_set(chip, chip->cfg_warm_bat_mv);
+ else
+ rc = qpnp_lbc_vddmax_set(chip, chip->cfg_max_voltage_mv);
+ if (rc)
+ pr_err("Failed to set appropriate vddmax\n");
+
+ return rc;
+}
+
+#define QPNP_LBC_MIN_DELTA_UV 13000
+static void qpnp_lbc_adjust_vddmax(struct qpnp_lbc_chip *chip, int vbat_uv)
+{
+ int delta_uv, prev_delta_uv, rc;
+
+ prev_delta_uv = chip->delta_vddmax_uv;
+ delta_uv = (int)(chip->cfg_max_voltage_mv * 1000) - vbat_uv;
+
+ /*
+ * If delta_uv is positive, apply trim if delta_uv > 13mv
+ * If delta_uv is negative always apply trim.
+ */
+ if (delta_uv > 0 && delta_uv < QPNP_LBC_MIN_DELTA_UV) {
+ pr_debug("vbat is not low enough to increase vdd\n");
+ return;
+ }
+
+ pr_debug("vbat=%d current delta_uv=%d prev delta_vddmax_uv=%d\n",
+ vbat_uv, delta_uv, chip->delta_vddmax_uv);
+ chip->delta_vddmax_uv = delta_uv + chip->delta_vddmax_uv;
+ pr_debug("new delta_vddmax_uv %d\n", chip->delta_vddmax_uv);
+ rc = qpnp_lbc_set_appropriate_vddmax(chip);
+ if (rc)
+ chip->delta_vddmax_uv = prev_delta_uv;
+}
+
+#define QPNP_LBC_VINMIN_MIN_MV 4200
+#define QPNP_LBC_VINMIN_MAX_MV 5037
+#define QPNP_LBC_VINMIN_STEP_MV 27
+static int qpnp_lbc_vinmin_set(struct qpnp_lbc_chip *chip, int voltage)
+{
+ u8 reg_val;
+ int rc;
+
+ if ((voltage < QPNP_LBC_VINMIN_MIN_MV)
+ || (voltage > QPNP_LBC_VINMIN_MAX_MV)) {
+ pr_err("Invalid vinmin voltage mV=%d min=%d max=%d\n",
+ voltage, QPNP_LBC_VINMIN_MIN_MV,
+ QPNP_LBC_VINMIN_MAX_MV);
+ return -EINVAL;
+ }
+
+ reg_val = (voltage - QPNP_LBC_VINMIN_MIN_MV) / QPNP_LBC_VINMIN_STEP_MV;
+ pr_debug("VIN_MIN=%d setting %02x\n", voltage, reg_val);
+ rc = qpnp_lbc_write(chip, chip->chgr_base + CHG_VIN_MIN_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to set VIN_MIN\n");
+
+ return rc;
+}
+
+#define QPNP_LBC_IBATSAFE_MIN_MA 90
+#define QPNP_LBC_IBATSAFE_MAX_MA 1440
+#define QPNP_LBC_I_STEP_MA 90
+static int qpnp_lbc_ibatsafe_set(struct qpnp_lbc_chip *chip, int safe_current)
+{
+ u8 reg_val;
+ int rc;
+
+ if (safe_current < QPNP_LBC_IBATSAFE_MIN_MA
+ || safe_current > QPNP_LBC_IBATSAFE_MAX_MA) {
+ pr_err("Invalid safecurrent mA=%d min=%d max=%d\n",
+ safe_current, QPNP_LBC_IBATSAFE_MIN_MA,
+ QPNP_LBC_IBATSAFE_MAX_MA);
+ return -EINVAL;
+ }
+
+ reg_val = (safe_current - QPNP_LBC_IBATSAFE_MIN_MA)
+ / QPNP_LBC_I_STEP_MA;
+ pr_debug("Ibate_safe=%d setting %02x\n", safe_current, reg_val);
+
+ rc = qpnp_lbc_write(chip, chip->chgr_base + CHG_IBAT_SAFE_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to set IBAT_SAFE\n");
+
+ return rc;
+}
+
+#define QPNP_LBC_IBATMAX_MIN 90
+#define QPNP_LBC_IBATMAX_MAX 1440
+/*
+ * Set maximum current limit from charger
+ * ibat = System current + charging current
+ */
+static int qpnp_lbc_ibatmax_set(struct qpnp_lbc_chip *chip, int chg_current)
+{
+ u8 reg_val;
+ int rc;
+
+ if (chg_current > QPNP_LBC_IBATMAX_MAX)
+ pr_debug("Invalid max charge current mA=%d max=%d\n",
+ chg_current,
+ QPNP_LBC_IBATMAX_MAX);
+
+ chg_current = clamp(chg_current, QPNP_LBC_IBATMAX_MIN,
+ QPNP_LBC_IBATMAX_MAX);
+ reg_val = (chg_current - QPNP_LBC_IBATMAX_MIN) / QPNP_LBC_I_STEP_MA;
+
+ rc = qpnp_lbc_write(chip, chip->chgr_base + CHG_IBAT_MAX_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to set IBAT_MAX\n");
+ else
+ chip->prev_max_ma = chg_current;
+
+ return rc;
+}
+
+#define QPNP_LBC_TCHG_MIN 4
+#define QPNP_LBC_TCHG_MAX 512
+#define QPNP_LBC_TCHG_STEP 4
+static int qpnp_lbc_tchg_max_set(struct qpnp_lbc_chip *chip, int minutes)
+{
+ u8 reg_val = 0;
+ int rc;
+
+ /* Disable timer */
+ rc = qpnp_lbc_masked_write(chip, chip->chgr_base + CHG_TCHG_MAX_EN_REG,
+ CHG_TCHG_MAX_EN_BIT, 0);
+ if (rc) {
+ pr_err("Failed to write tchg_max_en\n");
+ return rc;
+ }
+
+ /* If minutes is 0, just disable timer */
+ if (!minutes) {
+ pr_debug("Charger safety timer disabled\n");
+ return rc;
+ }
+
+ minutes = clamp(minutes, QPNP_LBC_TCHG_MIN, QPNP_LBC_TCHG_MAX);
+
+ reg_val = (minutes / QPNP_LBC_TCHG_STEP) - 1;
+
+ pr_debug("TCHG_MAX=%d mins setting %x\n", minutes, reg_val);
+ rc = qpnp_lbc_masked_write(chip, chip->chgr_base + CHG_TCHG_MAX_REG,
+ CHG_TCHG_MAX_MASK, reg_val);
+ if (rc) {
+ pr_err("Failed to write tchg_max_reg\n");
+ return rc;
+ }
+
+ /* Enable timer */
+ rc = qpnp_lbc_masked_write(chip, chip->chgr_base + CHG_TCHG_MAX_EN_REG,
+ CHG_TCHG_MAX_EN_BIT, CHG_TCHG_MAX_EN_BIT);
+ if (rc)
+ pr_err("Failed to write tchg_max_en\n");
+
+ return rc;
+}
+
+#define LBC_CHGR_LED 0x4D
+#define CHGR_LED_ON BIT(0)
+#define CHGR_LED_OFF 0x0
+#define CHGR_LED_STAT_MASK LBC_MASK(1, 0)
+static void qpnp_lbc_chgr_led_brightness_set(struct led_classdev *cdev,
+ enum led_brightness value)
+{
+ struct qpnp_lbc_chip *chip = container_of(cdev, struct qpnp_lbc_chip,
+ led_cdev);
+ u8 reg;
+ int rc;
+
+ if (value > LED_FULL)
+ value = LED_FULL;
+
+ pr_debug("set the charger led brightness to value=%d\n", value);
+ reg = (value > LED_OFF) ? CHGR_LED_ON : CHGR_LED_OFF;
+
+ rc = qpnp_lbc_masked_write(chip, chip->chgr_base + LBC_CHGR_LED,
+ CHGR_LED_STAT_MASK, reg);
+ if (rc)
+ pr_err("Failed to write charger led\n");
+}
+
+static enum
+led_brightness qpnp_lbc_chgr_led_brightness_get(struct led_classdev *cdev)
+{
+
+ struct qpnp_lbc_chip *chip = container_of(cdev, struct qpnp_lbc_chip,
+ led_cdev);
+ u8 reg_val, chgr_led_sts;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->chgr_base + LBC_CHGR_LED,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read charger led\n");
+ return rc;
+ }
+
+ chgr_led_sts = reg_val & CHGR_LED_STAT_MASK;
+ pr_debug("charger led brightness chgr_led_sts=%d\n", chgr_led_sts);
+
+ return (chgr_led_sts == CHGR_LED_ON) ? LED_FULL : LED_OFF;
+}
+
+static int qpnp_lbc_register_chgr_led(struct qpnp_lbc_chip *chip)
+{
+ int rc;
+
+ chip->led_cdev.name = "red";
+ chip->led_cdev.brightness_set = qpnp_lbc_chgr_led_brightness_set;
+ chip->led_cdev.brightness_get = qpnp_lbc_chgr_led_brightness_get;
+
+ rc = led_classdev_register(chip->dev, &chip->led_cdev);
+ if (rc)
+ pr_err("unable to register charger led, rc=%d\n", rc);
+
+ return rc;
+};
+
+static int is_vinmin_set(struct qpnp_lbc_chip *chip)
+{
+ u8 reg;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->chgr_base + CHG_STATUS_REG, ®, 1);
+ if (rc) {
+ pr_err("Unable to read charger status\n");
+ return false;
+ }
+ pr_debug("chg_status=0x%x\n", reg);
+
+ return !!(reg & VINMIN_LOOP_BIT);
+
+}
+
+static int is_battery_charging(struct qpnp_lbc_chip *chip)
+{
+ u8 reg;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->chgr_base + CHG_STATUS_REG, ®, 1);
+ if (rc) {
+ pr_err("Unable to read charger status\n");
+ return false;
+ }
+ pr_debug("chg_status=0x%x\n", reg);
+
+ return !!(reg & CHG_ON_BIT);
+}
+
+static int qpnp_lbc_vbatdet_override(struct qpnp_lbc_chip *chip, int ovr_val)
+{
+ int rc;
+ u8 reg_val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->hw_access_lock, flags);
+
+ rc = __qpnp_lbc_read(chip, chip->chgr_base + CHG_COMP_OVR1,
+ ®_val, 1);
+ if (rc)
+ goto out;
+
+ pr_debug("addr = 0x%x read 0x%x\n", chip->chgr_base, reg_val);
+
+ reg_val &= ~CHG_VBAT_DET_OVR_MASK;
+ reg_val |= ovr_val & CHG_VBAT_DET_OVR_MASK;
+
+ pr_debug("writing to base=%x val=%x\n", chip->chgr_base, reg_val);
+
+ rc = __qpnp_lbc_secure_write(chip, chip->chgr_base, CHG_COMP_OVR1,
+ ®_val, 1);
+
+out:
+ spin_unlock_irqrestore(&chip->hw_access_lock, flags);
+ return rc;
+}
+
+static int get_prop_battery_voltage_now(struct qpnp_lbc_chip *chip)
+{
+ int rc = 0;
+ struct qpnp_vadc_result results;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, VBAT_SNS, &results);
+ if (rc) {
+ pr_err("Unable to read vbat rc=%d\n", rc);
+ return 0;
+ }
+
+ return results.physical;
+}
+
+static int get_prop_batt_present(struct qpnp_lbc_chip *chip)
+{
+ u8 reg_val;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->bat_if_base + BAT_IF_PRES_STATUS_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read battery status read failed\n");
+ return 0;
+ }
+
+ return (reg_val & BATT_PRES_MASK) ? 1 : 0;
+}
+
+static int get_prop_batt_health(struct qpnp_lbc_chip *chip)
+{
+ u8 reg_val;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->bat_if_base + BAT_IF_TEMP_STATUS_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read battery health\n");
+ return POWER_SUPPLY_HEALTH_UNKNOWN;
+ }
+
+ if (BATT_TEMP_HOT_MASK & reg_val)
+ return POWER_SUPPLY_HEALTH_OVERHEAT;
+ if (!(BATT_TEMP_COLD_MASK & reg_val))
+ return POWER_SUPPLY_HEALTH_COLD;
+ if (chip->bat_is_cool)
+ return POWER_SUPPLY_HEALTH_COOL;
+ if (chip->bat_is_warm)
+ return POWER_SUPPLY_HEALTH_WARM;
+
+ return POWER_SUPPLY_HEALTH_GOOD;
+}
+
+static int get_prop_charge_type(struct qpnp_lbc_chip *chip)
+{
+ int rc;
+ u8 reg_val;
+
+ if (!get_prop_batt_present(chip))
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+
+ rc = qpnp_lbc_read(chip, chip->chgr_base + INT_RT_STS_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read interrupt sts\n");
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ if (reg_val & FAST_CHG_ON_IRQ)
+ return POWER_SUPPLY_CHARGE_TYPE_FAST;
+
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+}
+
+static int get_prop_batt_status(struct qpnp_lbc_chip *chip)
+{
+ int rc;
+ u8 reg_val;
+
+ if (qpnp_lbc_is_usb_chg_plugged_in(chip) && chip->chg_done)
+ return POWER_SUPPLY_STATUS_FULL;
+
+ rc = qpnp_lbc_read(chip, chip->chgr_base + INT_RT_STS_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read interrupt sts\n");
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ if (reg_val & FAST_CHG_ON_IRQ)
+ return POWER_SUPPLY_STATUS_CHARGING;
+
+ return POWER_SUPPLY_STATUS_DISCHARGING;
+}
+
+static int get_prop_current_now(struct qpnp_lbc_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->bms_psy) {
+ power_supply_get_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_CURRENT_NOW, &ret);
+ return ret.intval;
+ }
+
+ pr_debug("No BMS supply registered return 0\n");
+ return 0;
+}
+
+#define DEFAULT_CAPACITY 50
+static int get_prop_capacity(struct qpnp_lbc_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+ int soc;
+
+ if (!chip->bms_psy)
+ chip->bms_psy = power_supply_get_by_name("bms");
+
+ if (chip->fake_battery_soc >= 0)
+ return chip->fake_battery_soc;
+
+ if (chip->cfg_use_fake_battery || !get_prop_batt_present(chip))
+ return DEFAULT_CAPACITY;
+
+ if (chip->bms_psy) {
+ power_supply_get_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_CAPACITY, &ret);
+ soc = ret.intval;
+ if (soc == 0) {
+ if (!qpnp_lbc_is_usb_chg_plugged_in(chip))
+ pr_warn_ratelimited("Batt 0, CHG absent\n");
+ }
+ return soc;
+ }
+ pr_debug("No BMS supply registered return %d\n", DEFAULT_CAPACITY);
+
+ /*
+ * Return default capacity to avoid userspace
+ * from shutting down unecessarily
+ */
+ return DEFAULT_CAPACITY;
+}
+
+static int get_prop_charge_count(struct qpnp_lbc_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (!chip->bms_psy)
+ chip->bms_psy = power_supply_get_by_name("bms");
+
+ if (chip->bms_psy) {
+ power_supply_get_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_CHARGE_COUNTER, &ret);
+ } else {
+ pr_debug("No BMS supply registered return 0\n");
+ }
+
+ return ret.intval;
+}
+
+#define DEFAULT_TEMP 250
+static int get_prop_batt_temp(struct qpnp_lbc_chip *chip)
+{
+ int rc = 0;
+ struct qpnp_vadc_result results;
+
+ if (chip->cfg_use_fake_battery || !get_prop_batt_present(chip))
+ return DEFAULT_TEMP;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX1_BATT_THERM, &results);
+ if (rc) {
+ pr_debug("Unable to read batt temperature rc=%d\n", rc);
+ return DEFAULT_TEMP;
+ }
+ pr_debug("get_bat_temp %d, %lld\n", results.adc_code,
+ results.physical);
+
+ return (int)results.physical;
+}
+
+static void qpnp_lbc_set_appropriate_current(struct qpnp_lbc_chip *chip)
+{
+ unsigned int chg_current = chip->usb_psy_ma;
+
+ if (chip->bat_is_cool && chip->cfg_cool_bat_chg_ma)
+ chg_current = min(chg_current, chip->cfg_cool_bat_chg_ma);
+ if (chip->bat_is_warm && chip->cfg_warm_bat_chg_ma)
+ chg_current = min(chg_current, chip->cfg_warm_bat_chg_ma);
+ if (chip->therm_lvl_sel != 0 && chip->thermal_mitigation)
+ chg_current = min(chg_current,
+ chip->thermal_mitigation[chip->therm_lvl_sel]);
+
+ pr_debug("setting charger current %d mA\n", chg_current);
+ qpnp_lbc_ibatmax_set(chip, chg_current);
+}
+
+static int qpnp_lbc_system_temp_level_set(struct qpnp_lbc_chip *chip,
+ int lvl_sel)
+{
+ int rc = 0;
+ int prev_therm_lvl;
+ unsigned long flags;
+
+ if (!chip->thermal_mitigation) {
+ pr_err("Thermal mitigation not supported\n");
+ return -EINVAL;
+ }
+
+ if (lvl_sel < 0) {
+ pr_err("Unsupported level selected %d\n", lvl_sel);
+ return -EINVAL;
+ }
+
+ if (lvl_sel >= chip->cfg_thermal_levels) {
+ pr_err("Unsupported level selected %d forcing %d\n", lvl_sel,
+ chip->cfg_thermal_levels - 1);
+ lvl_sel = chip->cfg_thermal_levels - 1;
+ }
+
+ if (lvl_sel == chip->therm_lvl_sel)
+ return 0;
+
+ spin_lock_irqsave(&chip->ibat_change_lock, flags);
+ prev_therm_lvl = chip->therm_lvl_sel;
+ chip->therm_lvl_sel = lvl_sel;
+ if (chip->therm_lvl_sel == (chip->cfg_thermal_levels - 1)) {
+ /* Disable charging if highest value selected by */
+ rc = qpnp_lbc_charger_enable(chip, THERMAL, 0);
+ if (rc < 0)
+ pr_err("Failed to set disable charging\n");
+ goto out;
+ }
+
+ qpnp_lbc_set_appropriate_current(chip);
+
+ if (prev_therm_lvl == chip->cfg_thermal_levels - 1) {
+ /*
+ * If previously highest value was selected charging must have
+ * been disabed. Enable charging.
+ */
+ rc = qpnp_lbc_charger_enable(chip, THERMAL, 1);
+ if (rc < 0)
+ pr_err("Failed to enable charging\n");
+ }
+out:
+ spin_unlock_irqrestore(&chip->ibat_change_lock, flags);
+ return rc;
+}
+
+#define MIN_COOL_TEMP -300
+#define MAX_WARM_TEMP 1000
+#define HYSTERISIS_DECIDEGC 20
+
+static int qpnp_lbc_configure_jeita(struct qpnp_lbc_chip *chip,
+ enum power_supply_property psp, int temp_degc)
+{
+ int rc = 0;
+
+ if ((temp_degc < MIN_COOL_TEMP) || (temp_degc > MAX_WARM_TEMP)) {
+ pr_err("Invalid temp range=%d min=%d max=%d\n",
+ temp_degc, MIN_COOL_TEMP, MAX_WARM_TEMP);
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->jeita_configure_lock);
+ switch (psp) {
+ case POWER_SUPPLY_PROP_COOL_TEMP:
+ if (temp_degc >=
+ (chip->cfg_warm_bat_decidegc - HYSTERISIS_DECIDEGC)) {
+ pr_err("Can't set cool %d higher than warm %d - hysterisis %d\n",
+ temp_degc,
+ chip->cfg_warm_bat_decidegc,
+ HYSTERISIS_DECIDEGC);
+ rc = -EINVAL;
+ goto mutex_unlock;
+ }
+ if (chip->bat_is_cool)
+ chip->adc_param.high_temp =
+ temp_degc + HYSTERISIS_DECIDEGC;
+ else if (!chip->bat_is_warm)
+ chip->adc_param.low_temp = temp_degc;
+
+ chip->cfg_cool_bat_decidegc = temp_degc;
+ break;
+ case POWER_SUPPLY_PROP_WARM_TEMP:
+ if (temp_degc <=
+ (chip->cfg_cool_bat_decidegc + HYSTERISIS_DECIDEGC)) {
+ pr_err("Can't set warm %d higher than cool %d + hysterisis %d\n",
+ temp_degc,
+ chip->cfg_warm_bat_decidegc,
+ HYSTERISIS_DECIDEGC);
+ rc = -EINVAL;
+ goto mutex_unlock;
+ }
+ if (chip->bat_is_warm)
+ chip->adc_param.low_temp =
+ temp_degc - HYSTERISIS_DECIDEGC;
+ else if (!chip->bat_is_cool)
+ chip->adc_param.high_temp = temp_degc;
+
+ chip->cfg_warm_bat_decidegc = temp_degc;
+ break;
+ default:
+ rc = -EINVAL;
+ goto mutex_unlock;
+ }
+
+ if (qpnp_adc_tm_channel_measure(chip->adc_tm_dev, &chip->adc_param))
+ pr_err("request ADC error\n");
+
+mutex_unlock:
+ mutex_unlock(&chip->jeita_configure_lock);
+ return rc;
+}
+
+static int qpnp_batt_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ case POWER_SUPPLY_PROP_CAPACITY:
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ case POWER_SUPPLY_PROP_COOL_TEMP:
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ case POWER_SUPPLY_PROP_WARM_TEMP:
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * End of charge happens only when BMS reports the battery status as full. For
+ * charging to end the s/w must put the usb path in suspend. Note that there
+ * is no battery fet and usb path suspend is the only control to prevent any
+ * current going in to the battery (and the system)
+ * Charging can begin only when VBATDET comparator outputs 0. This indicates
+ * that the battery is a at a lower voltage than 4% of the vddmax value.
+ * S/W can override this comparator to output a favourable value - this is
+ * used while resuming charging when the battery hasnt fallen below 4% but
+ * the SOC has fallen below the resume threshold.
+ *
+ * In short, when SOC resume happens:
+ * a. overide the comparator to output 0
+ * b. enable charging
+ *
+ * When vbatdet based resume happens:
+ * a. enable charging
+ *
+ * When end of charge happens:
+ * a. disable the overrides in the comparator
+ * (may be from a previous soc resume)
+ * b. disable charging
+ */
+static int qpnp_batt_power_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct qpnp_lbc_chip *chip = power_supply_get_drvdata(psy);
+ int rc = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ mutex_lock(&chip->chg_enable_lock);
+ switch (val->intval) {
+ case POWER_SUPPLY_STATUS_FULL:
+ if (chip->cfg_float_charge)
+ break;
+ /* Disable charging */
+ rc = qpnp_lbc_charger_enable(chip, SOC, 0);
+ if (!rc)
+ chip->chg_done = true;
+
+ /*
+ * Enable VBAT_DET based charging:
+ * To enable charging when VBAT falls below VBAT_DET
+ * and device stays suspended after EOC.
+ */
+ if (!chip->cfg_disable_vbatdet_based_recharge) {
+ /* No override for VBAT_DET_LO comp */
+ rc = qpnp_lbc_vbatdet_override(chip,
+ OVERRIDE_NONE);
+ if (rc)
+ pr_err("Failed to override VBAT_DET rc=%d\n",
+ rc);
+ else
+ qpnp_lbc_enable_irq(chip,
+ &chip->irqs[CHG_VBAT_DET_LO]);
+ }
+ break;
+ case POWER_SUPPLY_STATUS_CHARGING:
+ chip->chg_done = false;
+ pr_debug("resuming charging by bms\n");
+ if (!chip->cfg_disable_vbatdet_based_recharge)
+ qpnp_lbc_vbatdet_override(chip, OVERRIDE_0);
+
+ qpnp_lbc_charger_enable(chip, SOC, 1);
+ break;
+ case POWER_SUPPLY_STATUS_DISCHARGING:
+ chip->chg_done = false;
+ pr_debug("status = DISCHARGING chg_done = %d\n",
+ chip->chg_done);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&chip->chg_enable_lock);
+ break;
+ case POWER_SUPPLY_PROP_COOL_TEMP:
+ rc = qpnp_lbc_configure_jeita(chip, psp, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_WARM_TEMP:
+ rc = qpnp_lbc_configure_jeita(chip, psp, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ chip->fake_battery_soc = val->intval;
+ pr_debug("power supply changed batt_psy\n");
+ break;
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ chip->cfg_charging_disabled = !(val->intval);
+ rc = qpnp_lbc_charger_enable(chip, USER,
+ !chip->cfg_charging_disabled);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ qpnp_lbc_vinmin_set(chip, val->intval / 1000);
+ break;
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+ qpnp_lbc_system_temp_level_set(chip, val->intval);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ power_supply_changed(chip->batt_psy);
+ return rc;
+}
+
+static int qpnp_batt_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct qpnp_lbc_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = get_prop_batt_status(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ val->intval = get_prop_charge_type(chip);
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = get_prop_batt_health(chip);
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = get_prop_batt_present(chip);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = chip->cfg_max_voltage_mv * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = chip->cfg_min_voltage_mv * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = get_prop_battery_voltage_now(chip);
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = get_prop_batt_temp(chip);
+ break;
+ case POWER_SUPPLY_PROP_COOL_TEMP:
+ val->intval = chip->cfg_cool_bat_decidegc;
+ break;
+ case POWER_SUPPLY_PROP_WARM_TEMP:
+ val->intval = chip->cfg_warm_bat_decidegc;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = get_prop_capacity(chip);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = get_prop_current_now(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+ val->intval = get_prop_charge_count(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ val->intval = !(chip->cfg_charging_disabled);
+ break;
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+ val->intval = chip->therm_lvl_sel;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define VINMIN_DELAY msecs_to_jiffies(500)
+static void qpnp_lbc_parallel_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct qpnp_lbc_chip *chip = container_of(dwork,
+ struct qpnp_lbc_chip, parallel_work);
+
+ if (is_vinmin_set(chip)) {
+ /* vinmin-loop triggered - stop ibat increase */
+ pr_debug("vinmin_loop triggered ichg_now=%d\n", chip->ichg_now);
+ goto exit_work;
+ } else {
+ int temp = chip->ichg_now + QPNP_LBC_I_STEP_MA;
+
+ if (temp > chip->lbc_max_chg_current) {
+ pr_debug("ichg_now=%d beyond max_chg_limit=%d - stopping\n",
+ temp, chip->lbc_max_chg_current);
+ goto exit_work;
+ }
+ chip->ichg_now = temp;
+ qpnp_lbc_ibatmax_set(chip, chip->ichg_now);
+ pr_debug("ichg_now increased to %d\n", chip->ichg_now);
+ }
+
+ schedule_delayed_work(&chip->parallel_work, VINMIN_DELAY);
+
+ return;
+
+exit_work:
+ pm_relax(chip->dev);
+}
+
+static int qpnp_lbc_parallel_charging_config(struct qpnp_lbc_chip *chip,
+ int enable)
+{
+ chip->parallel_charging_enabled = !!enable;
+
+ if (enable) {
+ /* Prevent sleep until charger is configured */
+ chip->ichg_now = QPNP_LBC_IBATMAX_MIN;
+ qpnp_lbc_ibatmax_set(chip, chip->ichg_now);
+ qpnp_lbc_charger_enable(chip, PARALLEL, 1);
+ pm_stay_awake(chip->dev);
+ schedule_delayed_work(&chip->parallel_work, VINMIN_DELAY);
+ } else {
+ cancel_delayed_work_sync(&chip->parallel_work);
+ pm_relax(chip->dev);
+ /* set minimum charging current and disable charging */
+ chip->ichg_now = 0;
+ chip->lbc_max_chg_current = 0;
+ qpnp_lbc_ibatmax_set(chip, 0);
+ qpnp_lbc_charger_enable(chip, PARALLEL, 0);
+ }
+
+ pr_debug("charging=%d ichg_now=%d max_chg_current=%d\n",
+ enable, chip->ichg_now, chip->lbc_max_chg_current);
+
+ return 0;
+}
+
+static void qpnp_lbc_set_current(struct qpnp_lbc_chip *chip, int current_ma)
+{
+ pr_debug("USB present=%d current_ma=%dmA\n", chip->usb_present,
+ current_ma);
+
+ if (current_ma <= 2 && get_prop_batt_present(chip)) {
+ qpnp_lbc_charger_enable(chip, CURRENT, 0);
+ chip->usb_psy_ma = QPNP_CHG_I_MAX_MIN_90;
+ qpnp_lbc_set_appropriate_current(chip);
+ } else {
+ chip->usb_psy_ma = current_ma;
+ qpnp_lbc_set_appropriate_current(chip);
+ qpnp_lbc_charger_enable(chip, CURRENT, 1);
+ }
+}
+
+static enum power_supply_property qpnp_lbc_usb_properties[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_TYPE,
+ POWER_SUPPLY_PROP_REAL_TYPE,
+ POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
+};
+
+static int qpnp_lbc_usb_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct qpnp_lbc_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_SDP_CURRENT_MAX:
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = chip->usb_psy_ma * 1000;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = chip->usb_present;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ if (is_battery_charging(chip))
+ val->intval = 1;
+ else
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_TYPE:
+ val->intval = POWER_SUPPLY_TYPE_USB;
+ if (chip->usb_present &&
+ (chip->usb_supply_type != POWER_SUPPLY_TYPE_UNKNOWN))
+ val->intval = chip->usb_supply_type;
+ break;
+ case POWER_SUPPLY_PROP_REAL_TYPE:
+ val->intval = POWER_SUPPLY_TYPE_UNKNOWN;
+ if (chip->usb_present &&
+ (chip->usb_supply_type != POWER_SUPPLY_TYPE_UNKNOWN))
+ val->intval = chip->usb_supply_type;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int qpnp_lbc_usb_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct qpnp_lbc_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_SDP_CURRENT_MAX:
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ qpnp_lbc_set_current(chip, (val->intval / 1000));
+ break;
+ case POWER_SUPPLY_PROP_TYPE:
+ case POWER_SUPPLY_PROP_REAL_TYPE:
+ chip->usb_supply_type = val->intval;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ power_supply_changed(psy);
+ return 0;
+}
+static int qpnp_lbc_usb_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ return 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static enum power_supply_property qpnp_lbc_parallel_properties[] = {
+ POWER_SUPPLY_PROP_CHARGING_ENABLED,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION,
+};
+
+static int qpnp_lbc_parallel_set_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ const union power_supply_propval *val)
+{
+ int rc = 0;
+ struct qpnp_lbc_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ qpnp_lbc_parallel_charging_config(chip, !!val->intval);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ chip->lbc_max_chg_current = val->intval / 1000;
+ pr_debug("lbc_max_current=%d\n", chip->lbc_max_chg_current);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int qpnp_lbc_parallel_is_writeable(struct power_supply *psy,
+ enum power_supply_property prop)
+{
+ int rc;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ rc = 1;
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+ return rc;
+}
+
+static int qpnp_lbc_parallel_get_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct qpnp_lbc_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ val->intval = chip->parallel_charging_enabled;
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ val->intval = chip->lbc_max_chg_current * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = chip->ichg_now * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ val->intval = get_prop_charge_type(chip);
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = get_prop_batt_status(chip);
+ break;
+ case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION:
+ val->intval = is_vinmin_set(chip);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+static void qpnp_lbc_jeita_adc_notification(enum qpnp_tm_state state, void *ctx)
+{
+ struct qpnp_lbc_chip *chip = ctx;
+ bool bat_warm = 0, bat_cool = 0;
+ int temp;
+ unsigned long flags;
+
+ if (state >= ADC_TM_STATE_NUM) {
+ pr_err("invalid notification %d\n", state);
+ return;
+ }
+
+ temp = get_prop_batt_temp(chip);
+
+ pr_debug("temp = %d state = %s\n", temp,
+ state == ADC_TM_WARM_STATE ? "warm" : "cool");
+
+ if (state == ADC_TM_WARM_STATE) {
+ if (temp >= chip->cfg_warm_bat_decidegc) {
+ /* Normal to warm */
+ bat_warm = true;
+ bat_cool = false;
+ chip->adc_param.low_temp =
+ chip->cfg_warm_bat_decidegc
+ - HYSTERISIS_DECIDEGC;
+ chip->adc_param.state_request =
+ ADC_TM_COOL_THR_ENABLE;
+ } else if (temp >=
+ chip->cfg_cool_bat_decidegc + HYSTERISIS_DECIDEGC) {
+ /* Cool to normal */
+ bat_warm = false;
+ bat_cool = false;
+
+ chip->adc_param.low_temp =
+ chip->cfg_cool_bat_decidegc;
+ chip->adc_param.high_temp =
+ chip->cfg_warm_bat_decidegc;
+ chip->adc_param.state_request =
+ ADC_TM_HIGH_LOW_THR_ENABLE;
+ }
+ } else {
+ if (temp <= chip->cfg_cool_bat_decidegc) {
+ /* Normal to cool */
+ bat_warm = false;
+ bat_cool = true;
+ chip->adc_param.high_temp =
+ chip->cfg_cool_bat_decidegc
+ + HYSTERISIS_DECIDEGC;
+ chip->adc_param.state_request =
+ ADC_TM_WARM_THR_ENABLE;
+ } else if (temp <= (chip->cfg_warm_bat_decidegc -
+ HYSTERISIS_DECIDEGC)){
+ /* Warm to normal */
+ bat_warm = false;
+ bat_cool = false;
+
+ chip->adc_param.low_temp =
+ chip->cfg_cool_bat_decidegc;
+ chip->adc_param.high_temp =
+ chip->cfg_warm_bat_decidegc;
+ chip->adc_param.state_request =
+ ADC_TM_HIGH_LOW_THR_ENABLE;
+ }
+ }
+
+ if (chip->bat_is_cool ^ bat_cool || chip->bat_is_warm ^ bat_warm) {
+ spin_lock_irqsave(&chip->ibat_change_lock, flags);
+ chip->bat_is_cool = bat_cool;
+ chip->bat_is_warm = bat_warm;
+ qpnp_lbc_set_appropriate_vddmax(chip);
+ qpnp_lbc_set_appropriate_current(chip);
+ spin_unlock_irqrestore(&chip->ibat_change_lock, flags);
+ }
+
+ pr_debug("warm %d, cool %d, low = %d deciDegC, high = %d deciDegC\n",
+ chip->bat_is_warm, chip->bat_is_cool,
+ chip->adc_param.low_temp, chip->adc_param.high_temp);
+
+ if (qpnp_adc_tm_channel_measure(chip->adc_tm_dev, &chip->adc_param))
+ pr_err("request ADC error\n");
+}
+
+#define IBAT_TERM_EN_MASK BIT(3)
+static int qpnp_lbc_chg_init(struct qpnp_lbc_chip *chip)
+{
+ int rc;
+ u8 reg_val;
+
+ qpnp_lbc_vbatweak_set(chip, chip->cfg_batt_weak_voltage_uv);
+ rc = qpnp_lbc_vinmin_set(chip, chip->cfg_min_voltage_mv);
+ if (rc) {
+ pr_err("Failed to set vin_min rc=%d\n", rc);
+ return rc;
+ }
+ rc = qpnp_lbc_vddsafe_set(chip, chip->cfg_safe_voltage_mv);
+ if (rc) {
+ pr_err("Failed to set vdd_safe rc=%d\n", rc);
+ return rc;
+ }
+ rc = qpnp_lbc_vddmax_set(chip, chip->cfg_max_voltage_mv);
+ if (rc) {
+ pr_err("Failed to set vdd_safe rc=%d\n", rc);
+ return rc;
+ }
+ rc = qpnp_lbc_ibatsafe_set(chip, chip->cfg_safe_current);
+ if (rc) {
+ pr_err("Failed to set ibat_safe rc=%d\n", rc);
+ return rc;
+ }
+
+ if (of_find_property(chip->dev->of_node, "qcom,tchg-mins", NULL)) {
+ rc = qpnp_lbc_tchg_max_set(chip, chip->cfg_tchg_mins);
+ if (rc) {
+ pr_err("Failed to set tchg_mins rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ /*
+ * Override VBAT_DET comparator to enable charging
+ * irrespective of VBAT above VBAT_DET.
+ */
+ rc = qpnp_lbc_vbatdet_override(chip, OVERRIDE_0);
+ if (rc) {
+ pr_err("Failed to override comp rc=%d\n", rc);
+ return rc;
+ }
+
+ /*
+ * Disable iterm comparator of linear charger to disable charger
+ * detecting end of charge condition based on DT configuration
+ * and float charge configuration.
+ */
+ if (!chip->cfg_charger_detect_eoc || chip->cfg_float_charge) {
+ rc = qpnp_lbc_masked_write(chip,
+ chip->chgr_base + CHG_IBATTERM_EN_REG,
+ IBAT_TERM_EN_MASK, 0);
+ if (rc) {
+ pr_err("Failed to disable EOC comp rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ /* Disable charger watchdog */
+ reg_val = 0;
+ rc = qpnp_lbc_write(chip, chip->chgr_base + CHG_WDOG_EN_REG,
+ ®_val, 1);
+
+ return rc;
+}
+
+static int qpnp_lbc_bat_if_init(struct qpnp_lbc_chip *chip)
+{
+ u8 reg_val;
+ int rc;
+
+ /* Select battery presence detection */
+ switch (chip->cfg_bpd_detection) {
+ case BPD_TYPE_BAT_THM:
+ reg_val = BATT_THM_EN;
+ break;
+ case BPD_TYPE_BAT_ID:
+ reg_val = BATT_ID_EN;
+ break;
+ case BPD_TYPE_BAT_THM_BAT_ID:
+ reg_val = BATT_THM_EN | BATT_ID_EN;
+ break;
+ default:
+ reg_val = BATT_THM_EN;
+ break;
+ }
+
+ rc = qpnp_lbc_masked_write(chip,
+ chip->bat_if_base + BAT_IF_BPD_CTRL_REG,
+ BATT_BPD_CTRL_SEL_MASK, reg_val);
+ if (rc) {
+ pr_err("Failed to choose BPD rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Force on VREF_BAT_THM */
+ reg_val = VREF_BATT_THERM_FORCE_ON;
+ rc = qpnp_lbc_write(chip,
+ chip->bat_if_base + BAT_IF_VREF_BAT_THM_CTRL_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to force on VREF_BAT_THM rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int qpnp_lbc_usb_path_init(struct qpnp_lbc_chip *chip)
+{
+ int rc;
+ u8 reg_val;
+
+ if (qpnp_lbc_is_usb_chg_plugged_in(chip)) {
+ reg_val = 0;
+ rc = qpnp_lbc_write(chip,
+ chip->usb_chgpth_base + CHG_USB_ENUM_T_STOP_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to write enum stop rc=%d\n", rc);
+ return -ENXIO;
+ }
+ }
+
+ if (chip->cfg_charging_disabled) {
+ rc = qpnp_lbc_charger_enable(chip, USER, 0);
+ if (rc)
+ pr_err("Failed to disable charging rc=%d\n", rc);
+
+ /*
+ * Disable follow-on-reset if charging is explicitly disabled,
+ * this forces the charging to be disabled across reset.
+ * Note: Explicitly disabling charging is only a debug/test
+ * configuration
+ */
+ reg_val = 0x0;
+ rc = __qpnp_lbc_secure_write(chip, chip->chgr_base,
+ CHG_PERPH_RESET_CTRL3_REG, ®_val, 1);
+ if (rc)
+ pr_err("Failed to configure PERPH_CTRL3 rc=%d\n", rc);
+ else
+ pr_debug("Charger is not following PMIC reset\n");
+ } else {
+ /*
+ * Enable charging explicitly,
+ * because not sure the default behavior.
+ */
+ reg_val = CHG_ENABLE;
+ rc = qpnp_lbc_masked_write(chip, chip->chgr_base + CHG_CTRL_REG,
+ CHG_EN_MASK, reg_val);
+ if (rc)
+ pr_err("Failed to enable charger rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
+#define LBC_MISC_DIG_VERSION_1 0x01
+static int qpnp_lbc_misc_init(struct qpnp_lbc_chip *chip)
+{
+ int rc;
+ u8 reg_val, reg_val1, trim_center;
+
+ /* Check if this LBC MISC version supports VDD trimming */
+ rc = qpnp_lbc_read(chip, chip->misc_base + MISC_REV2_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read VDD_EA TRIM3 reg rc=%d\n", rc);
+ return rc;
+ }
+
+ if (reg_val >= LBC_MISC_DIG_VERSION_1) {
+ chip->supported_feature_flag |= VDD_TRIM_SUPPORTED;
+ /* Read initial VDD trim value */
+ rc = qpnp_lbc_read(chip, chip->misc_base + MISC_TRIM3_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read VDD_EA TRIM3 reg rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_lbc_read(chip, chip->misc_base + MISC_TRIM4_REG,
+ ®_val1, 1);
+ if (rc) {
+ pr_err("Failed to read VDD_EA TRIM3 reg rc=%d\n", rc);
+ return rc;
+ }
+
+ trim_center = ((reg_val & MISC_TRIM3_VDD_MASK)
+ >> VDD_TRIM3_SHIFT)
+ | ((reg_val1 & MISC_TRIM4_VDD_MASK)
+ >> VDD_TRIM4_SHIFT);
+ chip->init_trim_uv = qpnp_lbc_get_trim_voltage(trim_center);
+ chip->delta_vddmax_uv = chip->init_trim_uv;
+ pr_debug("Initial trim center %x trim_uv %d\n",
+ trim_center, chip->init_trim_uv);
+ }
+
+ pr_debug("Setting BOOT_DONE\n");
+ reg_val = MISC_BOOT_DONE;
+ rc = qpnp_lbc_write(chip, chip->misc_base + MISC_BOOT_DONE_REG,
+ ®_val, 1);
+
+ return rc;
+}
+
+static int show_lbc_config(struct seq_file *m, void *data)
+{
+ struct qpnp_lbc_chip *chip = m->private;
+
+ seq_printf(m, "cfg_charging_disabled\t=\t%d\n"
+ "cfg_btc_disabled\t=\t%d\n"
+ "cfg_use_fake_battery\t=\t%d\n"
+ "cfg_use_external_charger\t=\t%d\n"
+ "cfg_chgr_led_support\t=\t%d\n"
+ "cfg_warm_bat_chg_ma\t=\t%d\n"
+ "cfg_cool_bat_chg_ma\t=\t%d\n"
+ "cfg_safe_voltage_mv\t=\t%d\n"
+ "cfg_max_voltage_mv\t=\t%d\n"
+ "cfg_min_voltage_mv\t=\t%d\n"
+ "cfg_charger_detect_eoc\t=\t%d\n"
+ "cfg_disable_vbatdet_based_recharge\t=\t%d\n"
+ "cfg_collapsible_chgr_support\t=\t%d\n"
+ "cfg_batt_weak_voltage_uv\t=\t%d\n"
+ "cfg_warm_bat_mv\t=\t%d\n"
+ "cfg_cool_bat_mv\t=\t%d\n"
+ "cfg_hot_batt_p\t=\t%d\n"
+ "cfg_cold_batt_p\t=\t%d\n"
+ "cfg_thermal_levels\t=\t%d\n"
+ "cfg_safe_current\t=\t%d\n"
+ "cfg_tchg_mins\t=\t%d\n"
+ "cfg_bpd_detection\t=\t%d\n"
+ "cfg_warm_bat_decidegc\t=\t%d\n"
+ "cfg_cool_bat_decidegc\t=\t%d\n"
+ "cfg_soc_resume_limit\t=\t%d\n"
+ "cfg_float_charge\t=\t%d\n",
+ chip->cfg_charging_disabled,
+ chip->cfg_btc_disabled,
+ chip->cfg_use_fake_battery,
+ chip->cfg_use_external_charger,
+ chip->cfg_chgr_led_support,
+ chip->cfg_warm_bat_chg_ma,
+ chip->cfg_cool_bat_chg_ma,
+ chip->cfg_safe_voltage_mv,
+ chip->cfg_max_voltage_mv,
+ chip->cfg_min_voltage_mv,
+ chip->cfg_charger_detect_eoc,
+ chip->cfg_disable_vbatdet_based_recharge,
+ chip->cfg_collapsible_chgr_support,
+ chip->cfg_batt_weak_voltage_uv,
+ chip->cfg_warm_bat_mv,
+ chip->cfg_cool_bat_mv,
+ chip->cfg_hot_batt_p,
+ chip->cfg_cold_batt_p,
+ chip->cfg_thermal_levels,
+ chip->cfg_safe_current,
+ chip->cfg_tchg_mins,
+ chip->cfg_bpd_detection,
+ chip->cfg_warm_bat_decidegc,
+ chip->cfg_cool_bat_decidegc,
+ chip->cfg_soc_resume_limit,
+ chip->cfg_float_charge);
+
+ return 0;
+}
+
+static int qpnp_lbc_config_open(struct inode *inode, struct file *file)
+{
+ struct qpnp_lbc_chip *chip = inode->i_private;
+
+ return single_open(file, show_lbc_config, chip);
+}
+
+static const struct file_operations qpnp_lbc_config_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = qpnp_lbc_config_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+#define OF_PROP_READ(chip, prop, qpnp_dt_property, retval, optional) \
+do { \
+ if (retval) \
+ break; \
+ \
+ retval = of_property_read_u32(chip->dev->of_node, \
+ "qcom," qpnp_dt_property, \
+ &chip->prop); \
+ \
+ if ((retval == -EINVAL) && optional) \
+ retval = 0; \
+ else if (retval) \
+ pr_err("Error reading " #qpnp_dt_property \
+ " property rc = %d\n", rc); \
+} while (0)
+
+static int qpnp_charger_read_dt_props(struct qpnp_lbc_chip *chip)
+{
+ int rc = 0;
+ const char *bpd;
+
+ OF_PROP_READ(chip, cfg_max_voltage_mv, "vddmax-mv", rc, 0);
+ OF_PROP_READ(chip, cfg_safe_voltage_mv, "vddsafe-mv", rc, 0);
+ OF_PROP_READ(chip, cfg_min_voltage_mv, "vinmin-mv", rc, 0);
+ OF_PROP_READ(chip, cfg_safe_current, "ibatsafe-ma", rc, 0);
+ if (rc)
+ pr_err("Error reading required property rc=%d\n", rc);
+
+ OF_PROP_READ(chip, cfg_tchg_mins, "tchg-mins", rc, 1);
+ OF_PROP_READ(chip, cfg_warm_bat_decidegc, "warm-bat-decidegc", rc, 1);
+ OF_PROP_READ(chip, cfg_cool_bat_decidegc, "cool-bat-decidegc", rc, 1);
+ OF_PROP_READ(chip, cfg_hot_batt_p, "batt-hot-percentage", rc, 1);
+ OF_PROP_READ(chip, cfg_cold_batt_p, "batt-cold-percentage", rc, 1);
+ OF_PROP_READ(chip, cfg_batt_weak_voltage_uv, "vbatweak-uv", rc, 1);
+ OF_PROP_READ(chip, cfg_soc_resume_limit, "resume-soc", rc, 1);
+ if (rc) {
+ pr_err("Error reading optional property rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_string(chip->dev->of_node,
+ "qcom,bpd-detection", &bpd);
+ if (rc) {
+
+ chip->cfg_bpd_detection = BPD_TYPE_BAT_THM;
+ rc = 0;
+ } else {
+ chip->cfg_bpd_detection = get_bpd(bpd);
+ if (chip->cfg_bpd_detection < 0) {
+ pr_err("Failed to determine bpd schema rc=%d\n", rc);
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * Look up JEITA compliance parameters if cool and warm temp
+ * provided
+ */
+ if (chip->cfg_cool_bat_decidegc || chip->cfg_warm_bat_decidegc) {
+ chip->adc_tm_dev = qpnp_get_adc_tm(chip->dev, "chg");
+ if (IS_ERR(chip->adc_tm_dev)) {
+ rc = PTR_ERR(chip->adc_tm_dev);
+ if (rc != -EPROBE_DEFER)
+ pr_err("Failed to get adc-tm rc=%d\n", rc);
+ return rc;
+ }
+
+ OF_PROP_READ(chip, cfg_warm_bat_chg_ma, "ibatmax-warm-ma",
+ rc, 1);
+ OF_PROP_READ(chip, cfg_cool_bat_chg_ma, "ibatmax-cool-ma",
+ rc, 1);
+ OF_PROP_READ(chip, cfg_warm_bat_mv, "warm-bat-mv", rc, 1);
+ OF_PROP_READ(chip, cfg_cool_bat_mv, "cool-bat-mv", rc, 1);
+ if (rc) {
+ pr_err("Error reading battery temp prop rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ /* Get the btc-disabled property */
+ chip->cfg_btc_disabled = of_property_read_bool(
+ chip->dev->of_node, "qcom,btc-disabled");
+
+ /* Get the charging-disabled property */
+ chip->cfg_charging_disabled =
+ of_property_read_bool(chip->dev->of_node,
+ "qcom,charging-disabled");
+
+ /* Get the fake-batt-values property */
+ chip->cfg_use_fake_battery =
+ of_property_read_bool(chip->dev->of_node,
+ "qcom,use-default-batt-values");
+
+ /* Get the float charging property */
+ chip->cfg_float_charge =
+ of_property_read_bool(chip->dev->of_node,
+ "qcom,float-charge");
+
+ /* Get the charger EOC detect property */
+ chip->cfg_charger_detect_eoc =
+ of_property_read_bool(chip->dev->of_node,
+ "qcom,charger-detect-eoc");
+
+ /* Get the vbatdet disable property */
+ chip->cfg_disable_vbatdet_based_recharge =
+ of_property_read_bool(chip->dev->of_node,
+ "qcom,disable-vbatdet-based-recharge");
+
+ /* Get the charger led support property */
+ chip->cfg_chgr_led_support =
+ of_property_read_bool(chip->dev->of_node,
+ "qcom,chgr-led-support");
+
+ /* Get the collapsible charger support property */
+ chip->cfg_collapsible_chgr_support =
+ of_property_read_bool(chip->dev->of_node,
+ "qcom,collapsible-chgr-support");
+
+ /* Disable charging when faking battery values */
+ if (chip->cfg_use_fake_battery)
+ chip->cfg_charging_disabled = true;
+
+ chip->cfg_use_external_charger = of_property_read_bool(
+ chip->dev->of_node, "qcom,use-external-charger");
+
+ if (of_find_property(chip->dev->of_node,
+ "qcom,thermal-mitigation",
+ &chip->cfg_thermal_levels)) {
+ chip->thermal_mitigation = devm_kzalloc(chip->dev,
+ chip->cfg_thermal_levels,
+ GFP_KERNEL);
+
+ if (chip->thermal_mitigation == NULL) {
+ pr_err("thermal mitigation kzalloc() failed.\n");
+ return -ENOMEM;
+ }
+
+ chip->cfg_thermal_levels /= sizeof(int);
+ rc = of_property_read_u32_array(chip->dev->of_node,
+ "qcom,thermal-mitigation",
+ chip->thermal_mitigation,
+ chip->cfg_thermal_levels);
+ if (rc) {
+ pr_err("Failed to read threm limits rc = %d\n", rc);
+ return rc;
+ }
+ }
+
+ pr_debug("vddmax-mv=%d, vddsafe-mv=%d, vinmin-mv=%d, ibatsafe-ma=$=%d\n",
+ chip->cfg_max_voltage_mv,
+ chip->cfg_safe_voltage_mv,
+ chip->cfg_min_voltage_mv,
+ chip->cfg_safe_current);
+ pr_debug("warm-bat-decidegc=%d, cool-bat-decidegc=%d, batt-hot-percentage=%d, batt-cold-percentage=%d\n",
+ chip->cfg_warm_bat_decidegc,
+ chip->cfg_cool_bat_decidegc,
+ chip->cfg_hot_batt_p,
+ chip->cfg_cold_batt_p);
+ pr_debug("tchg-mins=%d, vbatweak-uv=%d, resume-soc=%d\n",
+ chip->cfg_tchg_mins,
+ chip->cfg_batt_weak_voltage_uv,
+ chip->cfg_soc_resume_limit);
+ pr_debug("bpd-detection=%d, ibatmax-warm-ma=%d, ibatmax-cool-ma=%d, warm-bat-mv=%d, cool-bat-mv=%d\n",
+ chip->cfg_bpd_detection,
+ chip->cfg_warm_bat_chg_ma,
+ chip->cfg_cool_bat_chg_ma,
+ chip->cfg_warm_bat_mv,
+ chip->cfg_cool_bat_mv);
+ pr_debug("btc-disabled=%d, charging-disabled=%d, use-default-batt-values=%d, float-charge=%d\n",
+ chip->cfg_btc_disabled,
+ chip->cfg_charging_disabled,
+ chip->cfg_use_fake_battery,
+ chip->cfg_float_charge);
+ pr_debug("charger-detect-eoc=%d, disable-vbatdet-based-recharge=%d, chgr-led-support=%d\n",
+ chip->cfg_charger_detect_eoc,
+ chip->cfg_disable_vbatdet_based_recharge,
+ chip->cfg_chgr_led_support);
+ pr_debug("collapsible-chg-support=%d, use-external-charger=%d, thermal_levels=%d\n",
+ chip->cfg_collapsible_chgr_support,
+ chip->cfg_use_external_charger,
+ chip->cfg_thermal_levels);
+ return rc;
+}
+
+#define CHG_REMOVAL_DETECT_DLY_MS 300
+static irqreturn_t qpnp_lbc_chg_gone_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_lbc_chip *chip = _chip;
+ int chg_gone;
+
+ if (chip->cfg_collapsible_chgr_support) {
+ chg_gone = qpnp_lbc_is_chg_gone(chip);
+ pr_debug("chg-gone triggered, rt_sts: %d\n", chg_gone);
+ if (chg_gone) {
+ /*
+ * Disable charger to prevent fastchg irq storming
+ * if a non-collapsible charger is being used.
+ */
+ pr_debug("disable charging for non-collapsbile charger\n");
+ qpnp_lbc_charger_enable(chip, COLLAPSE, 0);
+ qpnp_lbc_disable_irq(chip, &chip->irqs[USBIN_VALID]);
+ qpnp_lbc_disable_irq(chip, &chip->irqs[USB_CHG_GONE]);
+ qpnp_chg_collapsible_chgr_config(chip, 0);
+ /*
+ * Check after a delay if the charger is still
+ * inserted. It decides if a non-collapsible
+ * charger is being used, or charger has been
+ * removed.
+ */
+ schedule_delayed_work(&chip->collapsible_detection_work,
+ msecs_to_jiffies(CHG_REMOVAL_DETECT_DLY_MS));
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_lbc_usbin_valid_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_lbc_chip *chip = _chip;
+ int usb_present;
+ unsigned long flags;
+
+ usb_present = qpnp_lbc_is_usb_chg_plugged_in(chip);
+ pr_debug("usbin-valid triggered: %d\n", usb_present);
+
+ if (chip->usb_present ^ usb_present) {
+ chip->usb_present = usb_present;
+ if (!usb_present) {
+ chip->usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
+ qpnp_lbc_charger_enable(chip, CURRENT, 0);
+ spin_lock_irqsave(&chip->ibat_change_lock, flags);
+ chip->usb_psy_ma = QPNP_CHG_I_MAX_MIN_90;
+ qpnp_lbc_set_appropriate_current(chip);
+ spin_unlock_irqrestore(&chip->ibat_change_lock,
+ flags);
+ if (chip->cfg_collapsible_chgr_support)
+ chip->non_collapsible_chgr_detected = false;
+
+ if (chip->supported_feature_flag & VDD_TRIM_SUPPORTED)
+ alarm_try_to_cancel(&chip->vddtrim_alarm);
+ } else {
+ /*
+ * Override VBAT_DET comparator to start charging
+ * even if VBAT > VBAT_DET.
+ */
+ if (!chip->cfg_disable_vbatdet_based_recharge)
+ qpnp_lbc_vbatdet_override(chip, OVERRIDE_0);
+
+ /*
+ * If collapsible charger supported, enable chgr_gone
+ * irq, and configure for collapsible charger.
+ */
+ if (chip->cfg_collapsible_chgr_support &&
+ !chip->non_collapsible_chgr_detected) {
+ qpnp_lbc_enable_irq(chip,
+ &chip->irqs[USB_CHG_GONE]);
+ qpnp_chg_collapsible_chgr_config(chip, 1);
+ }
+ /*
+ * Enable SOC based charging to make sure
+ * charging gets enabled on USB insertion
+ * irrespective of battery SOC above resume_soc.
+ */
+ qpnp_lbc_charger_enable(chip, SOC, 1);
+ }
+
+ pr_debug("Updating usb_psy PRESENT property\n");
+ if (chip->usb_present)
+ extcon_set_cable_state_(chip->extcon,
+ EXTCON_USB, true);
+ else
+ extcon_set_cable_state_(chip->extcon,
+ EXTCON_USB, false);
+ power_supply_changed(chip->usb_psy);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int qpnp_lbc_is_batt_temp_ok(struct qpnp_lbc_chip *chip)
+{
+ u8 reg_val;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->bat_if_base + INT_RT_STS_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("reg read failed: addr=%03X, rc=%d\n",
+ chip->bat_if_base + INT_RT_STS_REG, rc);
+ return rc;
+ }
+
+ return (reg_val & BAT_TEMP_OK_IRQ) ? 1 : 0;
+}
+
+static irqreturn_t qpnp_lbc_batt_temp_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_lbc_chip *chip = _chip;
+ int batt_temp_good;
+
+ batt_temp_good = qpnp_lbc_is_batt_temp_ok(chip);
+ pr_debug("batt-temp triggered: %d\n", batt_temp_good);
+
+ pr_debug("power supply changed batt_psy\n");
+ power_supply_changed(chip->batt_psy);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_lbc_batt_pres_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_lbc_chip *chip = _chip;
+ int batt_present;
+
+ batt_present = qpnp_lbc_is_batt_present(chip);
+ pr_debug("batt-pres triggered: %d\n", batt_present);
+
+ if (chip->batt_present ^ batt_present) {
+ chip->batt_present = batt_present;
+ pr_debug("power supply changed batt_psy\n");
+ power_supply_changed(chip->batt_psy);
+
+ if ((chip->cfg_cool_bat_decidegc
+ || chip->cfg_warm_bat_decidegc)
+ && batt_present) {
+ pr_debug("enabling vadc notifications\n");
+ if (qpnp_adc_tm_channel_measure(chip->adc_tm_dev,
+ &chip->adc_param))
+ pr_err("request ADC error\n");
+ } else if ((chip->cfg_cool_bat_decidegc
+ || chip->cfg_warm_bat_decidegc)
+ && !batt_present) {
+ qpnp_adc_tm_disable_chan_meas(chip->adc_tm_dev,
+ &chip->adc_param);
+ pr_debug("disabling vadc notifications\n");
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_lbc_chg_failed_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_lbc_chip *chip = _chip;
+ int rc;
+ u8 reg_val = CHG_FAILED_BIT;
+
+ pr_debug("chg_failed triggered count=%u\n", ++chip->chg_failed_count);
+ rc = qpnp_lbc_write(chip, chip->chgr_base + CHG_FAILED_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to write chg_fail clear bit rc=%d\n", rc);
+
+ if (chip->bat_if_base) {
+ pr_debug("power supply changed batt_psy\n");
+ power_supply_changed(chip->batt_psy);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int qpnp_lbc_is_fastchg_on(struct qpnp_lbc_chip *chip)
+{
+ u8 reg_val;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->chgr_base + INT_RT_STS_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read interrupt status rc=%d\n", rc);
+ return rc;
+ }
+ pr_debug("charger status %x\n", reg_val);
+ return (reg_val & FAST_CHG_ON_IRQ) ? 1 : 0;
+}
+
+#define TRIM_PERIOD_NS (50LL * NSEC_PER_SEC)
+static irqreturn_t qpnp_lbc_fastchg_irq_handler(int irq, void *_chip)
+{
+ ktime_t kt;
+ struct qpnp_lbc_chip *chip = _chip;
+ bool fastchg_on = false;
+
+ fastchg_on = qpnp_lbc_is_fastchg_on(chip);
+
+ pr_debug("FAST_CHG IRQ triggered, fastchg_on: %d\n", fastchg_on);
+
+ if (chip->fastchg_on ^ fastchg_on) {
+ chip->fastchg_on = fastchg_on;
+ if (fastchg_on) {
+ mutex_lock(&chip->chg_enable_lock);
+ chip->chg_done = false;
+ mutex_unlock(&chip->chg_enable_lock);
+ /*
+ * Start alarm timer to periodically calculate
+ * and update VDD_MAX trim value.
+ */
+ if (chip->supported_feature_flag &
+ VDD_TRIM_SUPPORTED) {
+ kt = ns_to_ktime(TRIM_PERIOD_NS);
+ alarm_start_relative(&chip->vddtrim_alarm,
+ kt);
+ }
+ }
+
+ if (chip->bat_if_base) {
+ pr_debug("power supply changed batt_psy\n");
+ power_supply_changed(chip->batt_psy);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_lbc_chg_done_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_lbc_chip *chip = _chip;
+
+ pr_debug("charging done triggered\n");
+
+ chip->chg_done = true;
+ pr_debug("power supply changed batt_psy\n");
+ power_supply_changed(chip->batt_psy);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_lbc_vbatdet_lo_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_lbc_chip *chip = _chip;
+ int rc;
+
+ pr_debug("vbatdet-lo triggered\n");
+
+ /*
+ * Disable vbatdet irq to prevent interrupt storm when VBAT is
+ * close to VBAT_DET.
+ */
+ qpnp_lbc_disable_irq(chip, &chip->irqs[CHG_VBAT_DET_LO]);
+
+ /*
+ * Override VBAT_DET comparator to 0 to fix comparator toggling
+ * near VBAT_DET threshold.
+ */
+ qpnp_lbc_vbatdet_override(chip, OVERRIDE_0);
+
+ /*
+ * Battery has fallen below the vbatdet threshold and it is
+ * time to resume charging.
+ */
+ rc = qpnp_lbc_charger_enable(chip, SOC, 1);
+ if (rc)
+ pr_err("Failed to enable charging\n");
+
+ return IRQ_HANDLED;
+}
+
+static int qpnp_lbc_is_overtemp(struct qpnp_lbc_chip *chip)
+{
+ u8 reg_val;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->usb_chgpth_base + INT_RT_STS_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read interrupt status rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_debug("OVERTEMP rt status %x\n", reg_val);
+ return (reg_val & OVERTEMP_ON_IRQ) ? 1 : 0;
+}
+
+static irqreturn_t qpnp_lbc_usb_overtemp_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_lbc_chip *chip = _chip;
+ int overtemp = qpnp_lbc_is_overtemp(chip);
+
+ pr_warn_ratelimited("charger %s temperature limit\n",
+ overtemp ? "exceeds" : "within");
+
+ return IRQ_HANDLED;
+}
+
+static int qpnp_disable_lbc_charger(struct qpnp_lbc_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ reg = CHG_FORCE_BATT_ON;
+ rc = qpnp_lbc_masked_write(chip, chip->chgr_base + CHG_CTRL_REG,
+ CHG_EN_MASK, reg);
+ /* disable BTC */
+ rc |= qpnp_lbc_masked_write(chip, chip->bat_if_base + BAT_IF_BTC_CTRL,
+ BTC_COMP_EN_MASK, 0);
+ /* Enable BID and disable THM based BPD */
+ reg = BATT_ID_EN | BATT_BPD_OFFMODE_EN;
+ rc |= qpnp_lbc_write(chip, chip->bat_if_base + BAT_IF_BPD_CTRL_REG,
+ ®, 1);
+ return rc;
+}
+
+#define REQUEST_IRQ(chip, idx, rc, irq_name, threaded, flags, wake)\
+do { \
+ if (rc) \
+ break; \
+ if (chip->irqs[idx].irq) { \
+ if (threaded) \
+ rc = devm_request_threaded_irq(chip->dev, \
+ chip->irqs[idx].irq, NULL, \
+ qpnp_lbc_##irq_name##_irq_handler, \
+ flags, #irq_name, chip); \
+ else \
+ rc = devm_request_irq(chip->dev, \
+ chip->irqs[idx].irq, \
+ qpnp_lbc_##irq_name##_irq_handler, \
+ flags, #irq_name, chip); \
+ if (rc < 0) { \
+ pr_err("Unable to request " #irq_name " %d\n", \
+ rc); \
+ } else { \
+ rc = 0; \
+ if (wake) { \
+ enable_irq_wake(chip->irqs[idx].irq); \
+ chip->irqs[idx].is_wake = true; \
+ } \
+ } \
+ } \
+} while (0)
+
+static inline void get_irq_resource(struct qpnp_lbc_chip *chip, int idx,
+ const char *name, struct device_node *child)
+{
+ int rc = 0;
+
+ rc = of_irq_get_byname(child, name);
+ if (rc < 0)
+ pr_err("Unable to get irq resource for %s - %d\n", name, rc);
+ else
+ chip->irqs[idx].irq = rc;
+}
+
+static int qpnp_lbc_request_irqs(struct qpnp_lbc_chip *chip)
+{
+ int rc = 0;
+
+ REQUEST_IRQ(chip, CHG_FAILED, rc, chg_failed, 0,
+ IRQF_TRIGGER_RISING, 1);
+
+ REQUEST_IRQ(chip, CHG_FAST_CHG, rc, fastchg, 1,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+ | IRQF_ONESHOT, 1);
+
+ REQUEST_IRQ(chip, CHG_DONE, rc, chg_done, 0,
+ IRQF_TRIGGER_RISING, 0);
+
+ REQUEST_IRQ(chip, CHG_VBAT_DET_LO, rc, vbatdet_lo, 0,
+ IRQF_TRIGGER_FALLING, 1);
+
+ REQUEST_IRQ(chip, BATT_PRES, rc, batt_pres, 1,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+ | IRQF_ONESHOT, 1);
+
+ REQUEST_IRQ(chip, BATT_TEMPOK, rc, batt_temp, 0,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 1);
+
+ REQUEST_IRQ(chip, USBIN_VALID, rc, usbin_valid, 1,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 1);
+
+ REQUEST_IRQ(chip, USB_CHG_GONE, rc, chg_gone, 0,
+ IRQF_TRIGGER_RISING, 1);
+
+ REQUEST_IRQ(chip, USB_OVER_TEMP, rc, usb_overtemp, 0,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 0);
+
+ return 0;
+}
+
+static int qpnp_lbc_get_irqs(struct qpnp_lbc_chip *chip, u8 subtype,
+ struct device_node *child)
+{
+ switch (subtype) {
+ case LBC_CHGR_SUBTYPE:
+ get_irq_resource(chip, CHG_FAST_CHG, "fast-chg-on", child);
+ get_irq_resource(chip, CHG_FAILED, "chg-failed", child);
+
+ if (!chip->cfg_disable_vbatdet_based_recharge)
+ get_irq_resource(chip, CHG_VBAT_DET_LO,
+ "vbat-det-lo", child);
+ if (chip->cfg_charger_detect_eoc)
+ get_irq_resource(chip, CHG_DONE, "chg-done", child);
+ break;
+
+ case LBC_BAT_IF_SUBTYPE:
+ get_irq_resource(chip, BATT_PRES, "batt-pres", child);
+ get_irq_resource(chip, BATT_TEMPOK, "bat-temp-ok", child);
+ break;
+
+ case LBC_USB_PTH_SUBTYPE:
+ get_irq_resource(chip, USBIN_VALID, "usbin-valid", child);
+ get_irq_resource(chip, USB_OVER_TEMP, "usb-over-temp", child);
+ get_irq_resource(chip, USB_CHG_GONE, "chg-gone", child);
+ break;
+ };
+ return 0;
+}
+
+/* Get/Set initial state of charger */
+static void determine_initial_status(struct qpnp_lbc_chip *chip)
+{
+ chip->usb_present = qpnp_lbc_is_usb_chg_plugged_in(chip);
+ power_supply_changed(chip->usb_psy);
+ /*
+ * Set USB psy online to avoid userspace from shutting down if battery
+ * capacity is at zero and no chargers online.
+ */
+ if (chip->usb_present) {
+ if (chip->cfg_collapsible_chgr_support &&
+ !chip->non_collapsible_chgr_detected) {
+ qpnp_lbc_enable_irq(chip,
+ &chip->irqs[USB_CHG_GONE]);
+ qpnp_chg_collapsible_chgr_config(chip, 1);
+ }
+ extcon_set_cable_state_(chip->extcon, EXTCON_USB, true);
+ } else {
+ extcon_set_cable_state_(chip->extcon, EXTCON_USB, false);
+ }
+ power_supply_changed(chip->usb_psy);
+}
+
+static void qpnp_lbc_collapsible_detection_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct qpnp_lbc_chip *chip = container_of(dwork,
+ struct qpnp_lbc_chip,
+ collapsible_detection_work);
+
+ if (qpnp_lbc_is_usb_chg_plugged_in(chip)) {
+ chip->non_collapsible_chgr_detected = true;
+ pr_debug("Non-collapsible charger detected\n");
+ } else {
+ chip->non_collapsible_chgr_detected = false;
+ pr_debug("Charger removal detected\n");
+ }
+ qpnp_lbc_charger_enable(chip, COLLAPSE, 1);
+ qpnp_lbc_enable_irq(chip, &chip->irqs[USBIN_VALID]);
+}
+
+#define IBAT_TRIM -300
+static void qpnp_lbc_vddtrim_work_fn(struct work_struct *work)
+{
+ int rc, vbat_now_uv, ibat_now;
+ u8 reg_val;
+ ktime_t kt;
+ struct qpnp_lbc_chip *chip = container_of(work, struct qpnp_lbc_chip,
+ vddtrim_work);
+
+ vbat_now_uv = get_prop_battery_voltage_now(chip);
+ ibat_now = get_prop_current_now(chip) / 1000;
+ pr_debug("vbat %d ibat %d capacity %d\n",
+ vbat_now_uv, ibat_now, get_prop_capacity(chip));
+
+ /*
+ * Stop trimming under following condition:
+ * USB removed
+ * Charging Stopped
+ */
+ if (!qpnp_lbc_is_fastchg_on(chip) ||
+ !qpnp_lbc_is_usb_chg_plugged_in(chip)) {
+ pr_debug("stop trim charging stopped\n");
+ goto exit;
+ } else {
+ rc = qpnp_lbc_read(chip, chip->chgr_base + CHG_STATUS_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read chg status rc=%d\n", rc);
+ goto out;
+ }
+
+ /*
+ * Update VDD trim voltage only if following conditions are
+ * met:
+ * If charger is in VDD loop AND
+ * If ibat is between 0 ma and -300 ma
+ */
+ if ((reg_val & CHG_VDD_LOOP_BIT) &&
+ ((ibat_now < 0) && (ibat_now > IBAT_TRIM)))
+ qpnp_lbc_adjust_vddmax(chip, vbat_now_uv);
+ }
+
+out:
+ kt = ns_to_ktime(TRIM_PERIOD_NS);
+ alarm_start_relative(&chip->vddtrim_alarm, kt);
+exit:
+ pm_relax(chip->dev);
+}
+
+static enum alarmtimer_restart vddtrim_callback(struct alarm *alarm,
+ ktime_t now)
+{
+ struct qpnp_lbc_chip *chip = container_of(alarm, struct qpnp_lbc_chip,
+ vddtrim_alarm);
+
+ pm_stay_awake(chip->dev);
+ schedule_work(&chip->vddtrim_work);
+
+ return ALARMTIMER_NORESTART;
+}
+
+static int qpnp_lbc_parallel_charger_init(struct qpnp_lbc_chip *chip)
+{
+ u8 reg_val;
+ int rc;
+
+ rc = qpnp_lbc_vinmin_set(chip, chip->cfg_min_voltage_mv);
+ if (rc) {
+ pr_err("Failed to set vin_min rc=%d\n", rc);
+ return rc;
+ }
+ rc = qpnp_lbc_vddsafe_set(chip, chip->cfg_max_voltage_mv);
+ if (rc) {
+ pr_err("Failed to set vdd_safe rc=%d\n", rc);
+ return rc;
+ }
+ rc = qpnp_lbc_vddmax_set(chip, chip->cfg_max_voltage_mv);
+ if (rc) {
+ pr_err("Failed to set vdd_max rc=%d\n", rc);
+ return rc;
+ }
+
+ /* set the minimum charging current */
+ rc = qpnp_lbc_ibatmax_set(chip, 0);
+ if (rc) {
+ pr_err("Failed to set IBAT_MAX to 0 rc=%d\n", rc);
+ return rc;
+ }
+
+ /* disable charging */
+ rc = qpnp_lbc_charger_enable(chip, PARALLEL, 0);
+ if (rc) {
+ pr_err("Unable to disable charging rc=%d\n", rc);
+ return 0;
+ }
+
+ /* Enable BID and disable THM based BPD */
+ reg_val = BATT_ID_EN | BATT_BPD_OFFMODE_EN;
+ rc = qpnp_lbc_write(chip, chip->bat_if_base + BAT_IF_BPD_CTRL_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to override BPD configuration rc=%d\n", rc);
+
+ /* Disable and override BTC */
+ reg_val = 0x2A;
+ rc = __qpnp_lbc_secure_write(chip, chip->bat_if_base,
+ BTC_COMP_OVERRIDE_REG, ®_val, 1);
+ if (rc)
+ pr_err("Failed to disable BTC override rc=%d\n", rc);
+
+ reg_val = 0;
+ rc = qpnp_lbc_write(chip,
+ chip->bat_if_base + BAT_IF_BTC_CTRL, ®_val, 1);
+ if (rc)
+ pr_err("Failed to disable BTC rc=%d\n", rc);
+
+ /* override VBAT_DET */
+ rc = qpnp_lbc_vbatdet_override(chip, OVERRIDE_0);
+ if (rc)
+ pr_err("Failed to override VBAT_DET rc=%d\n", rc);
+
+ /* Set BOOT_DONE and ENUM complete */
+ reg_val = 0;
+ rc = qpnp_lbc_write(chip,
+ chip->usb_chgpth_base + CHG_USB_ENUM_T_STOP_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to stop enum-timer rc=%d\n", rc);
+
+ reg_val = MISC_BOOT_DONE;
+ rc = qpnp_lbc_write(chip, chip->misc_base + MISC_BOOT_DONE_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to set boot-done rc=%d\n", rc);
+
+ return rc;
+}
+
+static int qpnp_lbc_parse_resources(struct qpnp_lbc_chip *chip)
+{
+ u8 subtype;
+ int rc = 0;
+ struct platform_device *pdev = chip->pdev;
+ struct device_node *child;
+ unsigned int base;
+
+ if (of_get_available_child_count(pdev->dev.of_node) == 0) {
+ pr_err("no child nodes\n");
+ goto fail_charger_enable;
+ }
+
+ for_each_available_child_of_node(pdev->dev.of_node, child) {
+ rc = of_property_read_u32(child, "reg", &base);
+ pr_debug("register address = %#X rc = %d\n", base, rc);
+ if (rc < 0) {
+ pr_err("Couldn`t find reg in node = %s rc = %d\n",
+ child->full_name, rc);
+ goto fail_charger_enable;
+ }
+
+ rc = qpnp_lbc_read(chip, base + PERP_SUBTYPE_REG, &subtype, 1);
+ if (rc) {
+ pr_err("Peripheral subtype read failed rc=%d\n", rc);
+ return rc;
+ }
+
+ switch (subtype) {
+ case LBC_CHGR_SUBTYPE:
+ chip->chgr_base = base;
+ rc = qpnp_lbc_get_irqs(chip, subtype, child);
+ if (rc) {
+ pr_err("Failed to get CHGR irqs rc=%d\n", rc);
+ return rc;
+ }
+ break;
+ case LBC_USB_PTH_SUBTYPE:
+ chip->usb_chgpth_base = base;
+ rc = qpnp_lbc_get_irqs(chip, subtype, child);
+ if (rc) {
+ pr_err("Failed to get USB_PTH irqs rc=%d\n",
+ rc);
+ return rc;
+ }
+ break;
+ case LBC_BAT_IF_SUBTYPE:
+ chip->bat_if_base = base;
+ rc = qpnp_lbc_get_irqs(chip, subtype, child);
+ if (rc) {
+ pr_err("Failed to get BAT_IF irqs rc=%d\n", rc);
+ return rc;
+ }
+ break;
+ case LBC_MISC_SUBTYPE:
+ chip->misc_base = base;
+ break;
+ default:
+ pr_err("Invalid peripheral subtype=0x%x\n", subtype);
+ rc = -EINVAL;
+ }
+ }
+
+ pr_debug("chgr_base=%x usb_chgpth_base=%x bat_if_base=%x misc_base=%x\n",
+ chip->chgr_base, chip->usb_chgpth_base,
+ chip->bat_if_base, chip->misc_base);
+
+ return rc;
+
+fail_charger_enable:
+ dev_set_drvdata(&pdev->dev, NULL);
+ return -ENXIO;
+}
+
+static int qpnp_lbc_parallel_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct qpnp_lbc_chip *chip;
+ struct power_supply_config parallel_psy_cfg = {};
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_lbc_chip),
+ GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!chip->regmap) {
+ pr_err("Couldn't get parent's regmap\n");
+ return -EINVAL;
+ }
+
+ chip->dev = &pdev->dev;
+ chip->pdev = pdev;
+ dev_set_drvdata(&pdev->dev, chip);
+ device_init_wakeup(&pdev->dev, 1);
+ spin_lock_init(&chip->hw_access_lock);
+ spin_lock_init(&chip->ibat_change_lock);
+ INIT_DELAYED_WORK(&chip->parallel_work, qpnp_lbc_parallel_work);
+
+ OF_PROP_READ(chip, cfg_max_voltage_mv, "vddmax-mv", rc, 0);
+ if (rc)
+ return rc;
+ OF_PROP_READ(chip, cfg_min_voltage_mv, "vinmin-mv", rc, 0);
+ if (rc)
+ return rc;
+
+ rc = qpnp_lbc_parse_resources(chip);
+ if (rc) {
+ pr_err("Unable to parse LBC(parallel) resources rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_lbc_parallel_charger_init(chip);
+ if (rc) {
+ pr_err("Unable to initialize LBC(parallel) rc=%d\n", rc);
+ return rc;
+ }
+
+ chip->parallel_psy_d.name = "parallel";
+ chip->parallel_psy_d.type = POWER_SUPPLY_TYPE_PARALLEL;
+ chip->parallel_psy_d.get_property = qpnp_lbc_parallel_get_property;
+ chip->parallel_psy_d.set_property = qpnp_lbc_parallel_set_property;
+ chip->parallel_psy_d.properties = qpnp_lbc_parallel_properties;
+ chip->parallel_psy_d.property_is_writeable =
+ qpnp_lbc_parallel_is_writeable;
+ chip->parallel_psy_d.num_properties =
+ ARRAY_SIZE(qpnp_lbc_parallel_properties);
+
+ parallel_psy_cfg.drv_data = chip;
+ parallel_psy_cfg.num_supplicants = 0;
+
+ chip->parallel_psy = devm_power_supply_register(chip->dev,
+ &chip->parallel_psy_d,
+ ¶llel_psy_cfg);
+ if (IS_ERR(chip->parallel_psy)) {
+ pr_err("Unable to register LBC parallel_psy rc = %ld\n",
+ PTR_ERR(chip->parallel_psy));
+ return PTR_ERR(chip->parallel_psy);
+ }
+
+ pr_debug("LBC (parallel) registered successfully!\n");
+
+ return 0;
+}
+
+static int qpnp_lbc_main_probe(struct platform_device *pdev)
+{
+ ktime_t kt;
+ struct qpnp_lbc_chip *chip;
+ struct power_supply_config batt_psy_cfg = {};
+ struct power_supply_config usb_psy_cfg = {};
+ int rc = 0;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_lbc_chip),
+ GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!chip->regmap)
+ return -EINVAL;
+
+ chip->dev = &pdev->dev;
+ chip->pdev = pdev;
+ dev_set_drvdata(&pdev->dev, chip);
+ device_init_wakeup(&pdev->dev, 1);
+ chip->fake_battery_soc = -EINVAL;
+ chip->usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
+
+ chip->extcon = devm_extcon_dev_allocate(chip->dev,
+ qpnp_lbc_extcon_cable);
+ if (IS_ERR(chip->extcon)) {
+ pr_err("failed to allocate extcon device\n");
+ rc = PTR_ERR(chip->extcon);
+ return rc;
+ }
+
+ rc = devm_extcon_dev_register(chip->dev, chip->extcon);
+ if (rc) {
+ pr_err("failed to register extcon device\n");
+ return rc;
+ }
+
+ mutex_init(&chip->jeita_configure_lock);
+ mutex_init(&chip->chg_enable_lock);
+ spin_lock_init(&chip->hw_access_lock);
+ spin_lock_init(&chip->ibat_change_lock);
+ spin_lock_init(&chip->irq_lock);
+ INIT_WORK(&chip->vddtrim_work, qpnp_lbc_vddtrim_work_fn);
+ alarm_init(&chip->vddtrim_alarm, ALARM_REALTIME, vddtrim_callback);
+ INIT_DELAYED_WORK(&chip->collapsible_detection_work,
+ qpnp_lbc_collapsible_detection_work);
+
+ /* Get all device-tree properties */
+ rc = qpnp_charger_read_dt_props(chip);
+ if (rc) {
+ pr_err("Failed to read DT properties rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_lbc_parse_resources(chip);
+ if (rc) {
+ pr_err("Unable to parse LBC resources rc=%d\n", rc);
+ goto fail_chg_enable;
+ }
+
+ if (chip->cfg_use_external_charger) {
+ pr_warn("Disabling Linear Charger (e-external-charger = 1)\n");
+ rc = qpnp_disable_lbc_charger(chip);
+ if (rc)
+ pr_err("Unable to disable charger rc=%d\n", rc);
+ return -ENODEV;
+ }
+
+ chip->usb_psy_d.name = "usb";
+ chip->usb_psy_d.type = POWER_SUPPLY_TYPE_USB;
+ chip->usb_psy_d.properties = qpnp_lbc_usb_properties;
+ chip->usb_psy_d.num_properties = ARRAY_SIZE(qpnp_lbc_usb_properties);
+ chip->usb_psy_d.get_property = qpnp_lbc_usb_get_property;
+ chip->usb_psy_d.set_property = qpnp_lbc_usb_set_property;
+ chip->usb_psy_d.property_is_writeable = qpnp_lbc_usb_is_writeable;
+
+ usb_psy_cfg.drv_data = chip;
+ usb_psy_cfg.num_supplicants = 0;
+
+ chip->usb_psy = devm_power_supply_register(chip->dev,
+ &chip->usb_psy_d, &usb_psy_cfg);
+ if (IS_ERR(chip->usb_psy)) {
+ pr_err("Unable to register usb_psy rc = %ld\n",
+ PTR_ERR(chip->usb_psy));
+ rc = PTR_ERR(chip->usb_psy);
+ goto fail_chg_enable;
+ }
+
+ chip->vadc_dev = qpnp_get_vadc(chip->dev, "chg");
+ if (IS_ERR(chip->vadc_dev)) {
+ rc = PTR_ERR(chip->vadc_dev);
+ if (rc != -EPROBE_DEFER)
+ pr_err("vadc prop missing rc=%d\n",
+ rc);
+ goto fail_chg_enable;
+ }
+
+ /* Initialize h/w */
+ rc = qpnp_lbc_misc_init(chip);
+ if (rc) {
+ pr_err("unable to initialize LBC MISC rc=%d\n", rc);
+ return rc;
+ }
+ rc = qpnp_lbc_chg_init(chip);
+ if (rc) {
+ pr_err("unable to initialize LBC charger rc=%d\n", rc);
+ return rc;
+ }
+ rc = qpnp_lbc_bat_if_init(chip);
+ if (rc) {
+ pr_err("unable to initialize LBC BAT_IF rc=%d\n", rc);
+ return rc;
+ }
+ rc = qpnp_lbc_usb_path_init(chip);
+ if (rc) {
+ pr_err("unable to initialize LBC USB path rc=%d\n", rc);
+ return rc;
+ }
+
+ if (chip->cfg_chgr_led_support) {
+ rc = qpnp_lbc_register_chgr_led(chip);
+ if (rc) {
+ pr_err("unable to register charger led rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->bat_if_base) {
+ chip->batt_present = qpnp_lbc_is_batt_present(chip);
+ chip->batt_psy_d.name = "battery";
+ chip->batt_psy_d.type = POWER_SUPPLY_TYPE_BATTERY;
+ chip->batt_psy_d.properties = msm_batt_power_props;
+ chip->batt_psy_d.num_properties =
+ ARRAY_SIZE(msm_batt_power_props);
+ chip->batt_psy_d.get_property = qpnp_batt_power_get_property;
+ chip->batt_psy_d.set_property = qpnp_batt_power_set_property;
+ chip->batt_psy_d.property_is_writeable =
+ qpnp_batt_property_is_writeable;
+
+ batt_psy_cfg.drv_data = chip;
+ batt_psy_cfg.supplied_to = pm_batt_supplied_to;
+ batt_psy_cfg.num_supplicants =
+ ARRAY_SIZE(pm_batt_supplied_to);
+
+ chip->batt_psy = devm_power_supply_register(chip->dev,
+ &chip->batt_psy_d,
+ &batt_psy_cfg);
+ if (IS_ERR(chip->batt_psy)) {
+ pr_err("Unable to register LBC batt_psy rc = %ld\n",
+ PTR_ERR(chip->batt_psy));
+ goto fail_chg_enable;
+ }
+ }
+
+ if ((chip->cfg_cool_bat_decidegc || chip->cfg_warm_bat_decidegc)
+ && chip->bat_if_base) {
+ chip->adc_param.low_temp = chip->cfg_cool_bat_decidegc;
+ chip->adc_param.high_temp = chip->cfg_warm_bat_decidegc;
+ chip->adc_param.timer_interval = ADC_MEAS1_INTERVAL_1S;
+ chip->adc_param.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
+ chip->adc_param.btm_ctx = chip;
+ chip->adc_param.threshold_notification =
+ qpnp_lbc_jeita_adc_notification;
+ chip->adc_param.channel = LR_MUX1_BATT_THERM;
+
+ if (get_prop_batt_present(chip)) {
+ rc = qpnp_adc_tm_channel_measure(chip->adc_tm_dev,
+ &chip->adc_param);
+ if (rc) {
+ pr_err("request ADC error rc=%d\n", rc);
+ goto unregister_batt;
+ }
+ }
+ }
+
+ rc = qpnp_lbc_bat_if_configure_btc(chip);
+ if (rc) {
+ pr_err("Failed to configure btc rc=%d\n", rc);
+ goto unregister_batt;
+ }
+
+ /* Get/Set charger's initial status */
+ determine_initial_status(chip);
+
+ rc = qpnp_lbc_request_irqs(chip);
+ if (rc) {
+ pr_err("unable to initialize LBC MISC rc=%d\n", rc);
+ goto unregister_batt;
+ }
+
+ if (chip->cfg_charging_disabled && !get_prop_batt_present(chip))
+ pr_info("Battery absent and charging disabled\n");
+
+ /* Configure initial alarm for VDD trim */
+ if ((chip->supported_feature_flag & VDD_TRIM_SUPPORTED) &&
+ qpnp_lbc_is_fastchg_on(chip)) {
+ kt = ns_to_ktime(TRIM_PERIOD_NS);
+ alarm_start_relative(&chip->vddtrim_alarm, kt);
+ }
+
+ chip->debug_root = debugfs_create_dir("qpnp_lbc", NULL);
+ if (!chip->debug_root)
+ pr_err("Couldn't create debug dir\n");
+
+ if (chip->debug_root) {
+ struct dentry *ent;
+
+ ent = debugfs_create_file("lbc_config", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &qpnp_lbc_config_debugfs_ops);
+ if (!ent)
+ pr_err("Couldn't create lbc_config debug file\n");
+ }
+
+ pr_debug("Probe chg_dis=%d bpd=%d usb=%d batt_pres=%d batt_volt=%d soc=%d\n",
+ chip->cfg_charging_disabled,
+ chip->cfg_bpd_detection,
+ qpnp_lbc_is_usb_chg_plugged_in(chip),
+ get_prop_batt_present(chip),
+ get_prop_battery_voltage_now(chip),
+ get_prop_capacity(chip));
+
+ return 0;
+
+unregister_batt:
+ if (chip->bat_if_base)
+ power_supply_unregister(chip->batt_psy);
+fail_chg_enable:
+ power_supply_unregister(chip->usb_psy);
+ dev_set_drvdata(&pdev->dev, NULL);
+ return rc;
+}
+
+static int is_parallel_charger(struct platform_device *pdev)
+{
+ return of_property_read_bool(pdev->dev.of_node,
+ "qcom,parallel-charger");
+}
+
+static int qpnp_lbc_probe(struct platform_device *pdev)
+{
+ if (is_parallel_charger(pdev))
+ return qpnp_lbc_parallel_probe(pdev);
+ else
+ return qpnp_lbc_main_probe(pdev);
+}
+
+
+static int qpnp_lbc_remove(struct platform_device *pdev)
+{
+ struct qpnp_lbc_chip *chip = dev_get_drvdata(&pdev->dev);
+
+ if (chip->supported_feature_flag & VDD_TRIM_SUPPORTED) {
+ alarm_cancel(&chip->vddtrim_alarm);
+ cancel_work_sync(&chip->vddtrim_work);
+ }
+ cancel_delayed_work_sync(&chip->collapsible_detection_work);
+ debugfs_remove_recursive(chip->debug_root);
+ if (chip->bat_if_base)
+ power_supply_unregister(chip->batt_psy);
+ power_supply_unregister(chip->usb_psy);
+ mutex_destroy(&chip->jeita_configure_lock);
+ mutex_destroy(&chip->chg_enable_lock);
+ dev_set_drvdata(&pdev->dev, NULL);
+ return 0;
+}
+
+static const struct of_device_id qpnp_lbc_match_table[] = {
+ { .compatible = QPNP_CHARGER_DEV_NAME, },
+ {}
+};
+
+static struct platform_driver qpnp_lbc_driver = {
+ .probe = qpnp_lbc_probe,
+ .remove = qpnp_lbc_remove,
+ .driver = {
+ .name = QPNP_CHARGER_DEV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = qpnp_lbc_match_table,
+ },
+};
+
+/*
+ * qpnp_lbc_init() - register platform driver for qpnp-chg
+ */
+static int __init qpnp_lbc_init(void)
+{
+ return platform_driver_register(&qpnp_lbc_driver);
+}
+module_init(qpnp_lbc_init);
+
+static void __exit qpnp_lbc_exit(void)
+{
+ platform_driver_unregister(&qpnp_lbc_driver);
+}
+module_exit(qpnp_lbc_exit);
+
+MODULE_DESCRIPTION("QPNP Linear charger driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" QPNP_CHARGER_DEV_NAME);
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index 286d305..5df241f 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -1891,7 +1891,8 @@
switch (pmic_rev_id->pmic_subtype) {
case PMI8998_SUBTYPE:
chip->chg.smb_version = PMI8998_SUBTYPE;
- chip->chg.wa_flags |= BOOST_BACK_WA | QC_AUTH_INTERRUPT_WA_BIT;
+ chip->chg.wa_flags |= BOOST_BACK_WA | QC_AUTH_INTERRUPT_WA_BIT
+ | TYPEC_PBS_WA_BIT;
if (pmic_rev_id->rev4 == PMI8998_V1P1_REV4) /* PMI rev 1.1 */
chg->wa_flags |= QC_CHARGER_DETECTION_WA_BIT;
if (pmic_rev_id->rev4 == PMI8998_V2P0_REV4) /* PMI rev 2.0 */
@@ -1906,7 +1907,8 @@
break;
case PM660_SUBTYPE:
chip->chg.smb_version = PM660_SUBTYPE;
- chip->chg.wa_flags |= BOOST_BACK_WA | OTG_WA;
+ chip->chg.wa_flags |= BOOST_BACK_WA | OTG_WA | OV_IRQ_WA_BIT
+ | TYPEC_PBS_WA_BIT;
chg->param.freq_buck = pm660_params.freq_buck;
chg->param.freq_boost = pm660_params.freq_boost;
chg->chg_freq.freq_5V = 650;
diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c
index 7a3a4dc..d3aaf26 100644
--- a/drivers/power/supply/qcom/qpnp-smb5.c
+++ b/drivers/power/supply/qcom/qpnp-smb5.c
@@ -25,6 +25,7 @@
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
#include <linux/pmic-voter.h>
+#include <linux/qpnp/qpnp-adc.h>
#include "smb5-reg.h"
#include "smb5-lib.h"
#include "schgm-flash.h"
@@ -181,6 +182,11 @@
weak_chg_icl_ua, __weak_chg_icl_ua, int, 0600
);
+enum {
+ USBIN_CURRENT,
+ USBIN_VOLTAGE,
+};
+
#define PMI632_MAX_ICL_UA 3000000
static int smb5_chg_config_init(struct smb5 *chip)
{
@@ -361,6 +367,61 @@
return 0;
}
+static int smb5_get_adc_data(struct smb_charger *chg, int channel,
+ union power_supply_propval *val)
+{
+ int rc;
+ struct qpnp_vadc_result result;
+
+ if (!chg->vadc_dev) {
+ if (of_find_property(chg->dev->of_node, "qcom,chg-vadc",
+ NULL)) {
+ chg->vadc_dev = qpnp_get_vadc(chg->dev, "chg");
+ if (IS_ERR(chg->vadc_dev)) {
+ rc = PTR_ERR(chg->vadc_dev);
+ if (rc != -EPROBE_DEFER)
+ pr_debug("Failed to find VADC node, rc=%d\n",
+ rc);
+ else
+ chg->vadc_dev = NULL;
+
+ return rc;
+ }
+ } else {
+ return -ENODATA;
+ }
+ }
+
+ if (IS_ERR(chg->vadc_dev))
+ return PTR_ERR(chg->vadc_dev);
+
+ switch (channel) {
+ case USBIN_VOLTAGE:
+ rc = qpnp_vadc_read(chg->vadc_dev, VADC_USB_IN_V_DIV_16_PM5,
+ &result);
+ if (rc < 0) {
+ pr_err("Failed to read USBIN_V over vadc, rc=%d\n", rc);
+ return rc;
+ }
+ val->intval = result.physical;
+ break;
+ case USBIN_CURRENT:
+ rc = qpnp_vadc_read(chg->vadc_dev, VADC_USB_IN_I_PM5, &result);
+ if (rc < 0) {
+ pr_err("Failed to read USBIN_I over vadc, rc=%d\n", rc);
+ return rc;
+ }
+ val->intval = result.physical;
+ break;
+ default:
+ pr_debug("Invalid channel\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
/************************
* USB PSY REGISTRATION *
************************/
@@ -389,6 +450,8 @@
POWER_SUPPLY_PROP_CONNECTOR_TYPE,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_SCOPE,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_HVDCP_OPTI_ALLOWED,
};
static int smb5_usb_get_prop(struct power_supply *psy,
@@ -504,6 +567,22 @@
: chg->otg_present ? POWER_SUPPLY_SCOPE_SYSTEM
: POWER_SUPPLY_SCOPE_UNKNOWN;
break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_NOW:
+ rc = smblib_get_prop_usb_present(chg, &pval);
+ if (rc < 0 || !pval.intval) {
+ val->intval = 0;
+ return rc;
+ }
+ if (chg->smb_version == PMI632_SUBTYPE)
+ rc = smb5_get_adc_data(chg, USBIN_CURRENT, val);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ if (chg->smb_version == PMI632_SUBTYPE)
+ rc = smb5_get_adc_data(chg, USBIN_VOLTAGE, val);
+ break;
+ case POWER_SUPPLY_PROP_HVDCP_OPTI_ALLOWED:
+ val->intval = !chg->flash_active;
+ break;
default:
pr_err("get prop %d is not supported in usb\n", psp);
rc = -EINVAL;
@@ -789,6 +868,7 @@
{
struct smb5 *chip = power_supply_get_drvdata(psy);
struct smb_charger *chg = &chip->chg;
+ union power_supply_propval pval = {0, };
int rc = 0;
switch (psp) {
@@ -802,7 +882,31 @@
rc = smblib_set_icl_current(chg, val->intval);
break;
case POWER_SUPPLY_PROP_FLASH_ACTIVE:
- chg->flash_active = val->intval;
+ if ((chg->smb_version == PMI632_SUBTYPE)
+ && (chg->flash_active != val->intval)) {
+ chg->flash_active = val->intval;
+
+ rc = smblib_get_prop_usb_present(chg, &pval);
+ if (rc < 0)
+ pr_err("Failed to get USB preset status rc=%d\n",
+ rc);
+ if (pval.intval) {
+ rc = smblib_force_vbus_voltage(chg,
+ chg->flash_active ? FORCE_5V_BIT
+ : IDLE_BIT);
+ if (rc < 0)
+ pr_err("Failed to force 5V\n");
+ else
+ chg->pulse_cnt = 0;
+ }
+
+ pr_debug("flash active VBUS 5V restriction %s\n",
+ chg->flash_active ? "applied" : "removed");
+
+ /* Update userspace */
+ if (chg->batt_psy)
+ power_supply_changed(chg->batt_psy);
+ }
break;
default:
pr_err("set prop %d is not supported\n", psp);
@@ -1146,7 +1250,8 @@
rc = smblib_rerun_aicl(chg);
break;
case POWER_SUPPLY_PROP_DP_DM:
- rc = smblib_dp_dm(chg, val->intval);
+ if (!chg->flash_active)
+ rc = smblib_dp_dm(chg, val->intval);
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
rc = smblib_set_prop_input_current_limited(chg, val);
diff --git a/drivers/power/supply/qcom/qpnp-smbcharger.c b/drivers/power/supply/qcom/qpnp-smbcharger.c
index 59478a9..6fe38e0 100644
--- a/drivers/power/supply/qcom/qpnp-smbcharger.c
+++ b/drivers/power/supply/qcom/qpnp-smbcharger.c
@@ -176,6 +176,7 @@
int n_vbat_samples;
/* status variables */
+ int max_pulse_allowed;
int wake_reasons;
int previous_soc;
int usb_online;
@@ -287,7 +288,7 @@
struct votable *hw_aicl_rerun_disable_votable;
struct votable *hw_aicl_rerun_enable_indirect_votable;
struct votable *aicl_deglitch_short_votable;
-
+ struct votable *hvdcp_enable_votable;
/* extcon for VBUS / ID notification to USB */
struct extcon_dev *extcon;
};
@@ -413,6 +414,10 @@
"VARB_WRKARND_SHORT_DEGLITCH_VOTER"
/* QC 2.0 */
#define HVDCP_SHORT_DEGLITCH_VOTER "HVDCP_SHORT_DEGLITCH_VOTER"
+/* Hvdcp enable voters*/
+#define HVDCP_PMIC_VOTER "HVDCP_PMIC_VOTER"
+#define HVDCP_OTG_VOTER "HVDCP_OTG_VOTER"
+#define HVDCP_PULSING_VOTER "HVDCP_PULSING_VOTER"
static const unsigned int smbchg_extcon_cable[] = {
EXTCON_USB,
@@ -2435,6 +2440,27 @@
return rc;
}
+#define HVDCP_EN_BIT BIT(3)
+static int smbchg_hvdcp_enable_cb(struct votable *votable,
+ void *data,
+ int enable,
+ const char *client)
+{
+ int rc = 0;
+ struct smbchg_chip *chip = data;
+
+ pr_err("smbchg_hvdcp_enable_cb HVDCP %s\n",
+ enable ? "enabled" : "disabled");
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + CHGPTH_CFG,
+ HVDCP_EN_BIT, enable ? HVDCP_EN_BIT : 0);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't %s HVDCP rc=%d\n",
+ enable ? "enable" : "disable", rc);
+
+ return rc;
+}
+
static int set_fastchg_current_vote_cb(struct votable *votable,
void *data,
int fcc_ma,
@@ -3795,7 +3821,6 @@
#define USBIN_ADAPTER_9V 0x3
#define USBIN_ADAPTER_5V_9V_CONT 0x2
#define USBIN_ADAPTER_5V_UNREGULATED_9V 0x5
-#define HVDCP_EN_BIT BIT(3)
static int smbchg_external_otg_regulator_enable(struct regulator_dev *rdev)
{
int rc = 0;
@@ -3819,9 +3844,7 @@
* allowance to 9V, so that the audio boost operating in reverse never
* gets detected as a valid input
*/
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, 0);
+ rc = vote(chip->hvdcp_enable_votable, HVDCP_OTG_VOTER, true, 0);
if (rc < 0) {
dev_err(chip->dev, "Couldn't disable HVDCP rc=%d\n", rc);
return rc;
@@ -3855,9 +3878,7 @@
* value in order to allow normal USBs to be recognized as a valid
* input.
*/
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, HVDCP_EN_BIT);
+ rc = vote(chip->hvdcp_enable_votable, HVDCP_OTG_VOTER, false, 1);
if (rc < 0) {
dev_err(chip->dev, "Couldn't enable HVDCP rc=%d\n", rc);
return rc;
@@ -4435,10 +4456,13 @@
goto out;
}
- /* otherwise if it is unknown, set type after the vote */
- if (type == POWER_SUPPLY_TYPE_UNKNOWN)
+ /* otherwise if it is unknown, set type after removing the vote */
+ if (type == POWER_SUPPLY_TYPE_UNKNOWN) {
+ rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true, 0);
+ if (rc < 0)
+ pr_err("Couldn't vote for new USB ICL rc=%d\n", rc);
chip->usb_supply_type = type;
-
+ }
/*
* Update TYPE property to DCP for HVDCP/HVDCP3 charger types
* so that they can be recongized as AC chargers by healthd.
@@ -4577,9 +4601,7 @@
pr_err("Couldn't configure HVDCP 9V rc=%d\n", rc);
/* enable HVDCP */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, HVDCP_EN_BIT);
+ rc = vote(chip->hvdcp_enable_votable, HVDCP_PULSING_VOTER, false, 1);
if (rc < 0)
pr_err("Couldn't enable HVDCP rc=%d\n", rc);
@@ -4647,6 +4669,7 @@
chip->typec_current_ma = 0;
/* cancel/wait for hvdcp pending work if any */
cancel_delayed_work_sync(&chip->hvdcp_det_work);
+ smbchg_relax(chip, PM_DETECT_HVDCP);
smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_UNKNOWN);
extcon_set_cable_state_(chip->extcon, EXTCON_USB, chip->usb_present);
if (chip->dpdm_reg)
@@ -5118,8 +5141,7 @@
/* disable HVDCP */
pr_smb(PR_MISC, "Disable HVDCP\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, 0);
+ rc = vote(chip->hvdcp_enable_votable, HVDCP_PULSING_VOTER, true, 0);
if (rc < 0) {
pr_err("Couldn't disable HVDCP rc=%d\n", rc);
goto out;
@@ -5228,9 +5250,7 @@
/* enable HVDCP */
pr_smb(PR_MISC, "Enable HVDCP\n");
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, HVDCP_EN_BIT);
+ rc = vote(chip->hvdcp_enable_votable, HVDCP_PULSING_VOTER, false, 1);
if (rc < 0) {
pr_err("Couldn't enable HVDCP rc=%d\n", rc);
return rc;
@@ -5813,6 +5833,7 @@
POWER_SUPPLY_PROP_RERUN_AICL,
POWER_SUPPLY_PROP_RESTRICTED_CHARGING,
POWER_SUPPLY_PROP_ALLOW_HVDCP3,
+ POWER_SUPPLY_PROP_MAX_PULSE_ALLOWED,
};
static int smbchg_battery_set_property(struct power_supply *psy,
@@ -6029,6 +6050,9 @@
case POWER_SUPPLY_PROP_ALLOW_HVDCP3:
val->intval = chip->allow_hvdcp3_detection;
break;
+ case POWER_SUPPLY_PROP_MAX_PULSE_ALLOWED:
+ val->intval = chip->max_pulse_allowed;
+ break;
default:
return -EINVAL;
}
@@ -6250,7 +6274,8 @@
struct smbchg_chip *chip = _chip;
pr_smb(PR_INTERRUPT, "p2f triggered\n");
- smbchg_parallel_usb_check_ok(chip);
+ if (is_usb_present(chip) || is_dc_present(chip))
+ smbchg_parallel_usb_check_ok(chip);
if (chip->batt_psy)
power_supply_changed(chip->batt_psy);
smbchg_charging_status_change(chip);
@@ -6863,7 +6888,22 @@
chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]);
/* Setup 9V HVDCP */
- if (!chip->hvdcp_not_supported) {
+ if (chip->hvdcp_not_supported) {
+ rc = vote(chip->hvdcp_enable_votable, HVDCP_PMIC_VOTER,
+ true, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't disable HVDCP rc=%d\n",
+ rc);
+ return rc;
+ }
+ } else {
+ rc = vote(chip->hvdcp_enable_votable, HVDCP_PMIC_VOTER,
+ true, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't enable HVDCP rc=%d\n",
+ rc);
+ return rc;
+ }
rc = smbchg_sec_masked_write(chip,
chip->usb_chgpth_base + CHGPTH_CFG,
HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
@@ -7380,6 +7420,7 @@
#define DEFAULT_VLED_MAX_UV 3500000
#define DEFAULT_FCC_MA 2000
+#define DEFAULT_NUM_OF_PULSE_ALLOWED 20
static int smb_parse_dt(struct smbchg_chip *chip)
{
int rc = 0, ocp_thresh = -EINVAL;
@@ -7438,6 +7479,11 @@
if (chip->parallel.min_current_thr_ma != -EINVAL
&& chip->parallel.min_9v_current_thr_ma != -EINVAL)
chip->parallel.avail = true;
+
+ OF_PROP_READ(chip, chip->max_pulse_allowed,
+ "max-pulse-allowed", rc, 1);
+ if (chip->max_pulse_allowed == -EINVAL)
+ chip->max_pulse_allowed = DEFAULT_NUM_OF_PULSE_ALLOWED;
/*
* use the dt values if they exist, otherwise do not touch the params
*/
@@ -8211,6 +8257,15 @@
goto votables_cleanup;
}
+ chip->hvdcp_enable_votable = create_votable(
+ "HVDCP_ENABLE",
+ VOTE_MIN,
+ smbchg_hvdcp_enable_cb, chip);
+ if (IS_ERR(chip->hvdcp_enable_votable)) {
+ rc = PTR_ERR(chip->hvdcp_enable_votable);
+ goto votables_cleanup;
+ }
+
INIT_WORK(&chip->usb_set_online_work, smbchg_usb_update_online_work);
INIT_DELAYED_WORK(&chip->parallel_en_work,
smbchg_parallel_usb_en_work);
@@ -8397,6 +8452,7 @@
rerun_hvdcp_det_if_necessary(chip);
+ update_usb_status(chip, is_usb_present(chip), false);
dump_regs(chip);
create_debugfs_entries(chip);
dev_info(chip->dev,
@@ -8493,11 +8549,9 @@
disable_irq(chip->otg_oc_irq);
disable_irq(chip->power_ok_irq);
disable_irq(chip->recharge_irq);
- disable_irq(chip->src_detect_irq);
disable_irq(chip->taper_irq);
disable_irq(chip->usbid_change_irq);
disable_irq(chip->usbin_ov_irq);
- disable_irq(chip->usbin_uv_irq);
disable_irq(chip->vbat_low_irq);
disable_irq(chip->wdog_timeout_irq);
@@ -8540,8 +8594,7 @@
/* disable HVDCP */
pr_smb(PR_MISC, "Disable HVDCP\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, 0);
+ rc = vote(chip->hvdcp_enable_votable, HVDCP_PMIC_VOTER, true, 0);
if (rc < 0)
pr_err("Couldn't disable HVDCP rc=%d\n", rc);
@@ -8558,6 +8611,9 @@
if (rc < 0)
pr_err("Couldn't fake insertion rc=%d\n", rc);
+ disable_irq(chip->src_detect_irq);
+ disable_irq(chip->usbin_uv_irq);
+
pr_smb(PR_MISC, "Wait 1S to settle\n");
msleep(1000);
chip->hvdcp_3_det_ignore_uv = false;
diff --git a/drivers/power/supply/qcom/qpnp-vm-bms.c b/drivers/power/supply/qcom/qpnp-vm-bms.c
new file mode 100644
index 0000000..042ce99
--- /dev/null
+++ b/drivers/power/supply/qcom/qpnp-vm-bms.c
@@ -0,0 +1,4255 @@
+/* Copyright (c) 2014-2016, 2018, 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) "VBMS: %s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/rtc.h>
+#include <linux/power_supply.h>
+#include <linux/fcntl.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/input/qpnp-power-on.h>
+#include <linux/qpnp/qpnp-adc.h>
+#include <linux/of_batterydata.h>
+#include <linux/batterydata-interface.h>
+#include <linux/qpnp/qpnp-revid.h>
+#include <uapi/linux/vm_bms.h>
+
+#define _BMS_MASK(BITS, POS) \
+ ((unsigned char)(((1 << (BITS)) - 1) << (POS)))
+#define BMS_MASK(LEFT_BIT_POS, RIGHT_BIT_POS) \
+ _BMS_MASK((LEFT_BIT_POS) - (RIGHT_BIT_POS) + 1, \
+ (RIGHT_BIT_POS))
+
+/* Config / Data registers */
+#define REVISION1_REG 0x0
+#define STATUS1_REG 0x8
+#define FSM_STATE_MASK BMS_MASK(5, 3)
+#define FSM_STATE_SHIFT 3
+
+#define STATUS2_REG 0x9
+#define FIFO_CNT_SD_MASK BMS_MASK(7, 4)
+#define FIFO_CNT_SD_SHIFT 4
+
+#define MODE_CTL_REG 0x40
+#define FORCE_S3_MODE BIT(0)
+#define ENABLE_S3_MODE BIT(1)
+#define FORCE_S2_MODE BIT(2)
+#define ENABLE_S2_MODE BIT(3)
+#define S2_MODE_MASK BMS_MASK(3, 2)
+#define S3_MODE_MASK BMS_MASK(1, 0)
+
+#define DATA_CTL1_REG 0x42
+#define MASTER_HOLD_BIT BIT(0)
+
+#define DATA_CTL2_REG 0x43
+#define FIFO_CNT_SD_CLR_BIT BIT(2)
+#define ACC_DATA_SD_CLR_BIT BIT(1)
+#define ACC_CNT_SD_CLR_BIT BIT(0)
+
+#define S3_OCV_TOL_CTL_REG 0x44
+
+#define EN_CTL_REG 0x46
+#define BMS_EN_BIT BIT(7)
+
+#define FIFO_LENGTH_REG 0x47
+#define S1_FIFO_LENGTH_MASK BMS_MASK(3, 0)
+#define S2_FIFO_LENGTH_MASK BMS_MASK(7, 4)
+#define S2_FIFO_LENGTH_SHIFT 4
+
+#define S1_SAMPLE_INTVL_REG 0x55
+#define S2_SAMPLE_INTVL_REG 0x56
+#define S3_SAMPLE_INTVL_REG 0x57
+
+#define S1_ACC_CNT_REG 0x5E
+#define S2_ACC_CNT_REG 0x5F
+#define ACC_CNT_MASK BMS_MASK(2, 0)
+
+#define ACC_DATA0_SD_REG 0x63
+#define ACC_CNT_SD_REG 0x67
+#define OCV_DATA0_REG 0x6A
+#define FIFO_0_LSB_REG 0xC0
+
+#define BMS_SOC_REG 0xB0
+#define BMS_OCV_REG 0xB1 /* B1 & B2 */
+#define SOC_STORAGE_MASK 0xFE
+
+#define CHARGE_INCREASE_STORAGE 0xB3
+#define CHARGE_CYCLE_STORAGE_LSB 0xB4 /* B4 & B5 */
+
+#define SEC_ACCESS 0xD0
+
+#define QPNP_CHARGER_PRESENT BIT(7)
+
+/* Constants */
+#define OCV_TOL_LSB_UV 300
+#define MAX_OCV_TOL_THRESHOLD (OCV_TOL_LSB_UV * 0xFF)
+#define MAX_SAMPLE_COUNT 256
+#define MAX_SAMPLE_INTERVAL 2550
+#define BMS_READ_TIMEOUT 500
+#define BMS_DEFAULT_TEMP 250
+#define OCV_INVALID 0xFFFF
+#define SOC_INVALID 0xFF
+#define OCV_UNINITIALIZED 0xFFFF
+#define VBATT_ERROR_MARGIN 20000
+#define CV_DROP_MARGIN 10000
+#define MIN_OCV_UV 2000000
+#define TIME_PER_PERCENT_UUC 60
+#define IAVG_SAMPLES 16
+#define MIN_SOC_UUC 3
+
+#define QPNP_VM_BMS_DEV_NAME "qcom,qpnp-vm-bms"
+
+/* indicates the state of BMS */
+enum {
+ IDLE_STATE,
+ S1_STATE,
+ S2_STATE,
+ S3_STATE,
+ S7_STATE,
+};
+
+enum {
+ WRKARND_PON_OCV_COMP = BIT(0),
+};
+
+struct bms_irq {
+ int irq;
+ unsigned long disabled;
+};
+
+struct bms_wakeup_source {
+ struct wakeup_source source;
+ unsigned long disabled;
+};
+
+struct temp_curr_comp_map {
+ int temp_decideg;
+ int current_ma;
+};
+
+struct bms_dt_cfg {
+ bool cfg_report_charger_eoc;
+ bool cfg_force_bms_active_on_charger;
+ bool cfg_force_s3_on_suspend;
+ bool cfg_ignore_shutdown_soc;
+ bool cfg_use_voltage_soc;
+ int cfg_v_cutoff_uv;
+ int cfg_max_voltage_uv;
+ int cfg_r_conn_mohm;
+ int cfg_shutdown_soc_valid_limit;
+ int cfg_low_soc_calc_threshold;
+ int cfg_low_soc_calculate_soc_ms;
+ int cfg_low_voltage_threshold;
+ int cfg_low_voltage_calculate_soc_ms;
+ int cfg_low_soc_fifo_length;
+ int cfg_calculate_soc_ms;
+ int cfg_voltage_soc_timeout_ms;
+ int cfg_s1_sample_interval_ms;
+ int cfg_s2_sample_interval_ms;
+ int cfg_s1_sample_count;
+ int cfg_s2_sample_count;
+ int cfg_s1_fifo_length;
+ int cfg_s2_fifo_length;
+ int cfg_disable_bms;
+ int cfg_s3_ocv_tol_uv;
+ int cfg_soc_resume_limit;
+ int cfg_low_temp_threshold;
+ int cfg_ibat_avg_samples;
+ int cfg_battery_aging_comp;
+ bool cfg_use_reported_soc;
+};
+
+struct qpnp_bms_chip {
+ struct device *dev;
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ dev_t dev_no;
+ u16 base;
+ u8 revision[2];
+ u32 batt_pres_addr;
+ u32 chg_pres_addr;
+
+ /* status variables */
+ u8 current_fsm_state;
+ bool last_soc_invalid;
+ bool warm_reset;
+ bool bms_psy_registered;
+ bool battery_full;
+ bool bms_dev_open;
+ bool data_ready;
+ bool apply_suspend_config;
+ bool in_cv_state;
+ bool low_soc_fifo_set;
+ int battery_status;
+ int calculated_soc;
+ int current_now;
+ int prev_current_now;
+ int prev_voltage_based_soc;
+ int calculate_soc_ms;
+ int voltage_soc_uv;
+ int battery_present;
+ int last_soc;
+ int last_soc_unbound;
+ int last_soc_change_sec;
+ int charge_start_tm_sec;
+ int catch_up_time_sec;
+ int delta_time_s;
+ int uuc_delta_time_s;
+ int ocv_at_100;
+ int last_ocv_uv;
+ int s2_fifo_length;
+ int last_acc;
+ int hi_power_state;
+ unsigned int vadc_v0625;
+ unsigned int vadc_v1250;
+ unsigned long tm_sec;
+ unsigned long workaround_flag;
+ unsigned long uuc_tm_sec;
+ u32 seq_num;
+ u8 shutdown_soc;
+ bool shutdown_soc_invalid;
+ u16 last_ocv_raw;
+ u32 shutdown_ocv;
+ bool suspend_data_valid;
+ int iavg_num_samples;
+ unsigned int iavg_index;
+ int iavg_samples_ma[IAVG_SAMPLES];
+ int iavg_ma;
+ int prev_soc_uuc;
+ int eoc_reported;
+ u8 charge_increase;
+ u16 charge_cycles;
+ unsigned int start_soc;
+ unsigned int end_soc;
+ unsigned int chg_start_soc;
+
+ struct bms_battery_data *batt_data;
+ struct bms_dt_cfg dt;
+
+ struct dentry *debug_root;
+ struct bms_wakeup_source vbms_lv_wake_source;
+ struct bms_wakeup_source vbms_cv_wake_source;
+ struct bms_wakeup_source vbms_soc_wake_source;
+ wait_queue_head_t bms_wait_q;
+ struct delayed_work monitor_soc_work;
+ struct delayed_work voltage_soc_timeout_work;
+ struct mutex bms_data_mutex;
+ struct mutex bms_device_mutex;
+ struct mutex last_soc_mutex;
+ struct mutex state_change_mutex;
+ struct class *bms_class;
+ struct device *bms_device;
+ struct cdev bms_cdev;
+ struct qpnp_vm_bms_data bms_data;
+ struct qpnp_vadc_chip *vadc_dev;
+ struct qpnp_adc_tm_chip *adc_tm_dev;
+ struct pmic_revid_data *revid_data;
+ struct qpnp_adc_tm_btm_param vbat_monitor_params;
+ struct bms_irq fifo_update_done_irq;
+ struct bms_irq fsm_state_change_irq;
+ struct power_supply_desc bms_psy_d;
+ struct power_supply *bms_psy;
+ struct power_supply *batt_psy;
+ struct power_supply *usb_psy;
+ bool reported_soc_in_use;
+ bool charger_removed_since_full;
+ bool charger_reinserted;
+ bool reported_soc_high_current;
+ int reported_soc;
+ int reported_soc_change_sec;
+ int reported_soc_delta;
+};
+
+static struct qpnp_bms_chip *the_chip;
+
+static struct temp_curr_comp_map temp_curr_comp_lut[] = {
+ {-300, 15},
+ {250, 17},
+ {850, 28},
+};
+
+static void disable_bms_irq(struct bms_irq *irq)
+{
+ if (!__test_and_set_bit(0, &irq->disabled)) {
+ disable_irq(irq->irq);
+ pr_debug("disabled irq %d\n", irq->irq);
+ }
+}
+
+static void enable_bms_irq(struct bms_irq *irq)
+{
+ if (__test_and_clear_bit(0, &irq->disabled)) {
+ enable_irq(irq->irq);
+ pr_debug("enable irq %d\n", irq->irq);
+ }
+}
+
+static void bms_stay_awake(struct bms_wakeup_source *source)
+{
+ if (__test_and_clear_bit(0, &source->disabled)) {
+ __pm_stay_awake(&source->source);
+ pr_debug("enabled source %s\n", source->source.name);
+ }
+}
+
+static void bms_relax(struct bms_wakeup_source *source)
+{
+ if (!__test_and_set_bit(0, &source->disabled)) {
+ __pm_relax(&source->source);
+ pr_debug("disabled source %s\n", source->source.name);
+ }
+}
+
+static bool bms_wake_active(struct bms_wakeup_source *source)
+{
+ return !source->disabled;
+}
+
+static int bound_soc(int soc)
+{
+ soc = max(0, soc);
+ soc = min(100, soc);
+
+ return soc;
+}
+
+static char *qpnp_vm_bms_supplicants[] = {
+ "battery",
+};
+
+static int qpnp_read_wrapper(struct qpnp_bms_chip *chip, u8 *val,
+ u16 base, int count)
+{
+ int rc;
+
+ rc = regmap_bulk_read(chip->regmap, base, val, count);
+ if (rc)
+ pr_err("Regmap read failed rc=%d\n", rc);
+
+ return rc;
+}
+
+static int qpnp_write_wrapper(struct qpnp_bms_chip *chip, u8 *val,
+ u16 base, int count)
+{
+ int rc;
+
+ rc = regmap_bulk_write(chip->regmap, base, val, count);
+ if (rc)
+ pr_err("Regmap write failed rc=%d\n", rc);
+
+ return rc;
+}
+
+static int qpnp_masked_write_base(struct qpnp_bms_chip *chip, u16 addr,
+ u8 mask, u8 val)
+{
+ int rc;
+ u8 reg;
+
+ rc = qpnp_read_wrapper(chip, ®, addr, 1);
+ if (rc) {
+ pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
+ return rc;
+ }
+ reg &= ~mask;
+ reg |= val & mask;
+ rc = qpnp_write_wrapper(chip, ®, addr, 1);
+ if (rc)
+ pr_err("write failed addr = %03X, val = %02x, mask = %02x, reg = %02x, rc = %d\n",
+ addr, val, mask, reg, rc);
+
+ return rc;
+}
+
+static int qpnp_secure_write_wrapper(struct qpnp_bms_chip *chip, u8 *val,
+ u16 base)
+{
+ int rc;
+ u8 reg;
+
+ reg = 0xA5;
+ rc = qpnp_write_wrapper(chip, ®, chip->base + SEC_ACCESS, 1);
+ if (rc) {
+ pr_err("Error %d writing 0xA5 to 0x%x reg\n",
+ rc, SEC_ACCESS);
+ return rc;
+ }
+ rc = qpnp_write_wrapper(chip, val, base, 1);
+ if (rc)
+ pr_err("Error %d writing %d to 0x%x reg\n", rc, *val, base);
+
+ return rc;
+}
+
+static int backup_ocv_soc(struct qpnp_bms_chip *chip, int ocv_uv, int soc)
+{
+ int rc;
+ u16 ocv_mv = ocv_uv / 1000;
+
+ rc = qpnp_write_wrapper(chip, (u8 *)&ocv_mv,
+ chip->base + BMS_OCV_REG, 2);
+ if (rc)
+ pr_err("Unable to backup OCV rc=%d\n", rc);
+
+ rc = qpnp_masked_write_base(chip, chip->base + BMS_SOC_REG,
+ SOC_STORAGE_MASK, (soc + 1) << 1);
+ if (rc)
+ pr_err("Unable to backup SOC rc=%d\n", rc);
+
+ pr_debug("ocv_mv=%d soc=%d\n", ocv_mv, soc);
+
+ return rc;
+}
+
+static int get_current_time(unsigned long *now_tm_sec)
+{
+ struct rtc_time tm;
+ struct rtc_device *rtc;
+ int rc;
+
+ rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+ if (rtc == NULL) {
+ pr_err("%s: unable to open rtc device (%s)\n",
+ __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
+ return -EINVAL;
+ }
+
+ rc = rtc_read_time(rtc, &tm);
+ if (rc) {
+ pr_err("Error reading rtc device (%s) : %d\n",
+ CONFIG_RTC_HCTOSYS_DEVICE, rc);
+ goto close_time;
+ }
+
+ rc = rtc_valid_tm(&tm);
+ if (rc) {
+ pr_err("Invalid RTC time (%s): %d\n",
+ CONFIG_RTC_HCTOSYS_DEVICE, rc);
+ goto close_time;
+ }
+ rtc_tm_to_time(&tm, now_tm_sec);
+
+close_time:
+ rtc_class_close(rtc);
+ return rc;
+}
+
+static int calculate_delta_time(unsigned long *time_stamp, int *delta_time_s)
+{
+ unsigned long now_tm_sec = 0;
+
+ /* default to delta time = 0 if anything fails */
+ *delta_time_s = 0;
+
+ if (get_current_time(&now_tm_sec)) {
+ pr_err("RTC read failed\n");
+ return 0;
+ }
+
+ *delta_time_s = (now_tm_sec - *time_stamp);
+
+ /* remember this time */
+ *time_stamp = now_tm_sec;
+ return 0;
+}
+
+static bool is_charger_present(struct qpnp_bms_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->usb_psy == NULL)
+ chip->usb_psy = power_supply_get_by_name("usb");
+ if (chip->usb_psy) {
+ power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_PRESENT, &ret);
+ return ret.intval;
+ }
+
+ return false;
+}
+
+static bool is_battery_charging(struct qpnp_bms_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->batt_psy == NULL)
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (chip->batt_psy) {
+ /* if battery has been registered, use the type property */
+ power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_TYPE, &ret);
+ return ret.intval != POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ /* Default to false if the battery power supply is not registered. */
+ pr_debug("battery power supply is not registered\n");
+ return false;
+}
+
+#define BAT_PRES_BIT BIT(7)
+static bool is_battery_present(struct qpnp_bms_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+ int rc;
+ u8 batt_pres;
+
+ /* first try to use the batt_pres register if given */
+ if (chip->batt_pres_addr) {
+ rc = qpnp_read_wrapper(chip, &batt_pres,
+ chip->batt_pres_addr, 1);
+ if (!rc && (batt_pres & BAT_PRES_BIT))
+ return true;
+ else
+ return false;
+ }
+ if (chip->batt_psy == NULL)
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (chip->batt_psy) {
+ /* if battery has been registered, use the present property */
+ power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_PRESENT, &ret);
+ return ret.intval;
+ }
+
+ /* Default to false if the battery power supply is not registered. */
+ pr_debug("battery power supply is not registered\n");
+ return false;
+}
+
+#define BAT_REMOVED_OFFMODE_BIT BIT(6)
+static bool is_battery_replaced_in_offmode(struct qpnp_bms_chip *chip)
+{
+ u8 batt_pres;
+ int rc;
+
+ if (chip->batt_pres_addr) {
+ rc = qpnp_read_wrapper(chip, &batt_pres,
+ chip->batt_pres_addr, 1);
+ pr_debug("offmode removed: %02x\n", batt_pres);
+ if (!rc && (batt_pres & BAT_REMOVED_OFFMODE_BIT))
+ return true;
+ }
+
+ return false;
+}
+
+static bool is_battery_taper_charging(struct qpnp_bms_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->batt_psy == NULL)
+ chip->batt_psy = power_supply_get_by_name("battery");
+
+ if (chip->batt_psy) {
+ power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_TYPE, &ret);
+ return ret.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER;
+ }
+
+ return false;
+}
+
+static int master_hold_control(struct qpnp_bms_chip *chip, bool enable)
+{
+ u8 reg = 0;
+ int rc;
+
+ reg = enable ? MASTER_HOLD_BIT : 0;
+
+ rc = qpnp_secure_write_wrapper(chip, ®,
+ chip->base + DATA_CTL1_REG);
+ if (rc)
+ pr_err("Unable to write reg=%x rc=%d\n", DATA_CTL1_REG, rc);
+
+ return rc;
+}
+
+static int force_fsm_state(struct qpnp_bms_chip *chip, u8 state)
+{
+ int rc;
+ u8 mode_ctl = 0;
+
+ switch (state) {
+ case S2_STATE:
+ mode_ctl = (FORCE_S2_MODE | ENABLE_S2_MODE);
+ break;
+ case S3_STATE:
+ mode_ctl = (FORCE_S3_MODE | ENABLE_S3_MODE);
+ break;
+ default:
+ pr_debug("Invalid state %d\n", state);
+ return -EINVAL;
+ }
+
+ rc = qpnp_secure_write_wrapper(chip, &mode_ctl,
+ chip->base + MODE_CTL_REG);
+ if (rc) {
+ pr_err("Unable to write reg=%x rc=%d\n", MODE_CTL_REG, rc);
+ return rc;
+ }
+ /* delay for the FSM state to take affect in hardware */
+ usleep_range(500, 600);
+
+ pr_debug("force_mode=%d mode_cntl_reg=%x\n", state, mode_ctl);
+
+ return 0;
+}
+
+static int get_sample_interval(struct qpnp_bms_chip *chip,
+ u8 fsm_state, u32 *interval)
+{
+ int rc;
+ u8 val = 0, reg;
+
+ *interval = 0;
+
+ switch (fsm_state) {
+ case S1_STATE:
+ reg = S1_SAMPLE_INTVL_REG;
+ break;
+ case S2_STATE:
+ reg = S2_SAMPLE_INTVL_REG;
+ break;
+ case S3_STATE:
+ reg = S3_SAMPLE_INTVL_REG;
+ break;
+ default:
+ pr_err("Invalid state %d\n", fsm_state);
+ return -EINVAL;
+ }
+
+ rc = qpnp_read_wrapper(chip, &val, chip->base + reg, 1);
+ if (rc) {
+ pr_err("Failed to get state(%d) sample_interval, rc=%d\n",
+ fsm_state, rc);
+ return rc;
+ }
+
+ *interval = val * 10;
+
+ return 0;
+}
+
+static int get_sample_count(struct qpnp_bms_chip *chip,
+ u8 fsm_state, u32 *count)
+{
+ int rc;
+ u8 val = 0, reg;
+
+ *count = 0;
+
+ switch (fsm_state) {
+ case S1_STATE:
+ reg = S1_ACC_CNT_REG;
+ break;
+ case S2_STATE:
+ reg = S2_ACC_CNT_REG;
+ break;
+ default:
+ pr_err("Invalid state %d\n", fsm_state);
+ return -EINVAL;
+ }
+
+ rc = qpnp_read_wrapper(chip, &val, chip->base + reg, 1);
+ if (rc) {
+ pr_err("Failed to get state(%d) sample_count, rc=%d\n",
+ fsm_state, rc);
+ return rc;
+ }
+ val &= ACC_CNT_MASK;
+
+ *count = val ? (1 << (val + 1)) : 1;
+
+ return 0;
+}
+
+static int get_fifo_length(struct qpnp_bms_chip *chip,
+ u8 fsm_state, u32 *fifo_length)
+{
+ int rc;
+ u8 val = 0, reg, mask = 0, shift = 0;
+
+ *fifo_length = 0;
+
+ switch (fsm_state) {
+ case S1_STATE:
+ reg = FIFO_LENGTH_REG;
+ mask = S1_FIFO_LENGTH_MASK;
+ shift = 0;
+ break;
+ case S2_STATE:
+ reg = FIFO_LENGTH_REG;
+ mask = S2_FIFO_LENGTH_MASK;
+ shift = S2_FIFO_LENGTH_SHIFT;
+ break;
+ default:
+ pr_err("Invalid state %d\n", fsm_state);
+ return -EINVAL;
+ }
+
+ rc = qpnp_read_wrapper(chip, &val, chip->base + reg, 1);
+ if (rc) {
+ pr_err("Failed to get state(%d) fifo_length, rc=%d\n",
+ fsm_state, rc);
+ return rc;
+ }
+
+ val &= mask;
+ val >>= shift;
+
+ *fifo_length = val;
+
+ return 0;
+}
+
+static int set_fifo_length(struct qpnp_bms_chip *chip,
+ u8 fsm_state, u32 fifo_length)
+{
+ int rc;
+ u8 reg, mask = 0, shift = 0;
+
+ /* fifo_length of 1 is not supported due to a hardware issue */
+ if ((fifo_length <= 1) || (fifo_length > MAX_FIFO_REGS)) {
+ pr_err("Invalid FIFO length = %d\n", fifo_length);
+ return -EINVAL;
+ }
+
+ switch (fsm_state) {
+ case S1_STATE:
+ reg = FIFO_LENGTH_REG;
+ mask = S1_FIFO_LENGTH_MASK;
+ shift = 0;
+ break;
+ case S2_STATE:
+ reg = FIFO_LENGTH_REG;
+ mask = S2_FIFO_LENGTH_MASK;
+ shift = S2_FIFO_LENGTH_SHIFT;
+ break;
+ default:
+ pr_err("Invalid state %d\n", fsm_state);
+ return -EINVAL;
+ }
+
+ rc = master_hold_control(chip, true);
+ if (rc)
+ pr_err("Unable to apply master_hold rc=%d\n", rc);
+
+ rc = qpnp_masked_write_base(chip, chip->base + reg, mask,
+ fifo_length << shift);
+ if (rc)
+ pr_err("Unable to set fifo length rc=%d\n", rc);
+
+ rc = master_hold_control(chip, false);
+ if (rc)
+ pr_err("Unable to apply master_hold rc=%d\n", rc);
+
+ return rc;
+}
+
+static int get_fsm_state(struct qpnp_bms_chip *chip, u8 *state)
+{
+ int rc;
+
+ /*
+ * To read the STATUS1 register, write a value(any) to this register,
+ * wait for 10ms and then read the register.
+ */
+ *state = 0;
+ rc = qpnp_write_wrapper(chip, state, chip->base + STATUS1_REG, 1);
+ if (rc) {
+ pr_err("Unable to write STATUS1_REG rc=%d\n", rc);
+ return rc;
+ }
+ usleep_range(10000, 11000);
+
+ /* read the current FSM state */
+ rc = qpnp_read_wrapper(chip, state, chip->base + STATUS1_REG, 1);
+ if (rc) {
+ pr_err("Unable to read STATUS1_REG rc=%d\n", rc);
+ return rc;
+ }
+ *state = (*state & FSM_STATE_MASK) >> FSM_STATE_SHIFT;
+
+ return rc;
+}
+
+static int update_fsm_state(struct qpnp_bms_chip *chip)
+{
+ u8 state = 0;
+ int rc;
+
+ mutex_lock(&chip->state_change_mutex);
+ rc = get_fsm_state(chip, &state);
+ if (rc) {
+ pr_err("Unable to get fsm_state rc=%d\n", rc);
+ goto fail_fsm;
+ }
+
+ chip->current_fsm_state = state;
+
+fail_fsm:
+ mutex_unlock(&chip->state_change_mutex);
+ return rc;
+}
+
+static int backup_charge_cycle(struct qpnp_bms_chip *chip)
+{
+ int rc = 0;
+
+ if (chip->charge_increase >= 0) {
+ rc = qpnp_write_wrapper(chip, &chip->charge_increase,
+ chip->base + CHARGE_INCREASE_STORAGE, 1);
+ if (rc)
+ pr_err("Unable to backup charge_increase rc=%d\n", rc);
+ }
+
+ if (chip->charge_cycles >= 0) {
+ rc = qpnp_write_wrapper(chip, (u8 *)&chip->charge_cycles,
+ chip->base + CHARGE_CYCLE_STORAGE_LSB, 2);
+ if (rc)
+ pr_err("Unable to backup charge_cycles rc=%d\n", rc);
+ }
+
+ pr_debug("%s storing charge_increase=%u charge_cycle=%u\n",
+ rc ? "Unable to" : "Successfully",
+ chip->charge_increase, chip->charge_cycles);
+
+ return rc;
+}
+
+static int read_chgcycle_data_from_backup(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ uint16_t temp_u16 = 0;
+ u8 temp_u8 = 0;
+
+ rc = qpnp_read_wrapper(chip, &temp_u8,
+ chip->base + CHARGE_INCREASE_STORAGE, 1);
+ if (rc) {
+ pr_err("Unable to read charge_increase rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_read_wrapper(chip, (u8 *)&temp_u16,
+ chip->base + CHARGE_CYCLE_STORAGE_LSB, 2);
+ if (rc) {
+ pr_err("Unable to read charge_cycle rc=%d\n", rc);
+ return rc;
+ }
+
+ if ((temp_u8 == 0xFF) || (temp_u16 == 0xFFFF)) {
+ chip->charge_cycles = 0;
+ chip->charge_increase = 0;
+ pr_info("rejecting aging data charge_increase=%u charge_cycle=%u\n",
+ temp_u8, temp_u16);
+ rc = backup_charge_cycle(chip);
+ if (rc)
+ pr_err("Unable to reset charge cycles rc=%d\n", rc);
+ } else {
+ chip->charge_increase = temp_u8;
+ chip->charge_cycles = temp_u16;
+ }
+
+ pr_debug("charge_increase=%u charge_cycle=%u\n",
+ chip->charge_increase, chip->charge_cycles);
+ return rc;
+}
+
+static int calculate_uuc_iavg(struct qpnp_bms_chip *chip)
+{
+ int i;
+ int iavg_ma = chip->current_now / 1000;
+
+ /* only continue if ibat has changed */
+ if (chip->current_now == chip->prev_current_now)
+ goto ibat_unchanged;
+ else
+ chip->prev_current_now = chip->current_now;
+
+ chip->iavg_samples_ma[chip->iavg_index] = iavg_ma;
+ chip->iavg_index = (chip->iavg_index + 1) %
+ chip->dt.cfg_ibat_avg_samples;
+ chip->iavg_num_samples++;
+ if (chip->iavg_num_samples >= chip->dt.cfg_ibat_avg_samples)
+ chip->iavg_num_samples = chip->dt.cfg_ibat_avg_samples;
+
+ if (chip->iavg_num_samples) {
+ iavg_ma = 0;
+ /* maintain a 16 sample average of ibat */
+ for (i = 0; i < chip->iavg_num_samples; i++) {
+ pr_debug("iavg_samples_ma[%d] = %d\n", i,
+ chip->iavg_samples_ma[i]);
+ iavg_ma += chip->iavg_samples_ma[i];
+ }
+
+ chip->iavg_ma = DIV_ROUND_CLOSEST(iavg_ma,
+ chip->iavg_num_samples);
+ }
+
+ibat_unchanged:
+ pr_debug("current_now_ma=%d averaged_iavg_ma=%d\n",
+ chip->current_now / 1000, chip->iavg_ma);
+
+ return chip->iavg_ma;
+}
+
+static int adjust_uuc(struct qpnp_bms_chip *chip, int soc_uuc)
+{
+ int max_percent_change;
+
+ calculate_delta_time(&chip->uuc_tm_sec, &chip->uuc_delta_time_s);
+
+ /* make sure that the UUC changes 1% at a time */
+ max_percent_change = max(chip->uuc_delta_time_s
+ / TIME_PER_PERCENT_UUC, 1);
+
+ if (chip->prev_soc_uuc == -EINVAL) {
+ /* start with a minimum UUC if the initial UUC is high */
+ if (soc_uuc > MIN_SOC_UUC)
+ chip->prev_soc_uuc = MIN_SOC_UUC;
+ else
+ chip->prev_soc_uuc = soc_uuc;
+ } else {
+ if (abs(chip->prev_soc_uuc - soc_uuc) <= max_percent_change)
+ chip->prev_soc_uuc = soc_uuc;
+ else if (soc_uuc > chip->prev_soc_uuc)
+ chip->prev_soc_uuc += max_percent_change;
+ else
+ chip->prev_soc_uuc -= max_percent_change;
+ }
+
+ pr_debug("soc_uuc=%d new_soc_uuc=%d\n", soc_uuc, chip->prev_soc_uuc);
+
+ return chip->prev_soc_uuc;
+}
+
+static int lookup_soc_ocv(struct qpnp_bms_chip *chip, int ocv_uv, int batt_temp)
+{
+ int soc_ocv = 0, soc_cutoff = 0, soc_final = 0;
+ int fcc, acc, soc_uuc = 0, soc_acc = 0, iavg_ma = 0;
+
+ soc_ocv = interpolate_pc(chip->batt_data->pc_temp_ocv_lut,
+ batt_temp, ocv_uv / 1000);
+ soc_cutoff = interpolate_pc(chip->batt_data->pc_temp_ocv_lut,
+ batt_temp, chip->dt.cfg_v_cutoff_uv / 1000);
+
+ soc_final = DIV_ROUND_CLOSEST(100 * (soc_ocv - soc_cutoff),
+ (100 - soc_cutoff));
+
+ if (chip->batt_data->ibat_acc_lut) {
+ /* Apply ACC logic only if we discharging */
+ if (chip->current_now > 0) {
+
+ /*
+ * IBAT averaging is disabled at low temp.
+ * allowing the SOC to catcup quickly.
+ */
+ if (batt_temp > chip->dt.cfg_low_temp_threshold)
+ iavg_ma = calculate_uuc_iavg(chip);
+ else
+ iavg_ma = chip->current_now / 1000;
+
+ fcc = interpolate_fcc(chip->batt_data->fcc_temp_lut,
+ batt_temp);
+ acc = interpolate_acc(chip->batt_data->ibat_acc_lut,
+ batt_temp, iavg_ma);
+ if (acc <= 0) {
+ if (chip->last_acc)
+ acc = chip->last_acc;
+ else
+ acc = fcc;
+ }
+ soc_uuc = ((fcc - acc) * 100) / fcc;
+
+ if (batt_temp > chip->dt.cfg_low_temp_threshold)
+ soc_uuc = adjust_uuc(chip, soc_uuc);
+
+ soc_acc = DIV_ROUND_CLOSEST(100 * (soc_ocv - soc_uuc),
+ (100 - soc_uuc));
+
+ pr_debug("fcc=%d acc=%d soc_final=%d soc_uuc=%d soc_acc=%d current_now=%d iavg_ma=%d\n",
+ fcc, acc, soc_final, soc_uuc,
+ soc_acc, chip->current_now / 1000, iavg_ma);
+
+ soc_final = soc_acc;
+ chip->last_acc = acc;
+ } else {
+ /* charging - reset all the counters */
+ chip->last_acc = 0;
+ chip->iavg_num_samples = 0;
+ chip->iavg_index = 0;
+ chip->iavg_ma = 0;
+ chip->prev_current_now = 0;
+ chip->prev_soc_uuc = -EINVAL;
+ }
+ }
+
+ soc_final = bound_soc(soc_final);
+
+ pr_debug("soc_final=%d soc_ocv=%d soc_cutoff=%d ocv_uv=%u batt_temp=%d\n",
+ soc_final, soc_ocv, soc_cutoff, ocv_uv, batt_temp);
+
+ return soc_final;
+}
+
+#define V_PER_BIT_MUL_FACTOR 97656
+#define V_PER_BIT_DIV_FACTOR 1000
+#define VADC_INTRINSIC_OFFSET 0x6000
+static int vadc_reading_to_uv(int reading, bool vadc_bms)
+{
+ int64_t value;
+
+ if (!vadc_bms) {
+ /*
+ * All the BMS H/W VADC values are pre-compensated
+ * for VADC_INTRINSIC_OFFSET, subtract this offset
+ * only if this reading is not obtained from BMS
+ */
+
+ if (reading <= VADC_INTRINSIC_OFFSET)
+ return 0;
+
+ reading -= VADC_INTRINSIC_OFFSET;
+ }
+
+ value = (reading * V_PER_BIT_MUL_FACTOR);
+
+ return div_u64(value, (u32)V_PER_BIT_DIV_FACTOR);
+}
+
+static int get_calculation_delay_ms(struct qpnp_bms_chip *chip)
+{
+ if (bms_wake_active(&chip->vbms_lv_wake_source))
+ return chip->dt.cfg_low_voltage_calculate_soc_ms;
+ if (chip->calculated_soc < chip->dt.cfg_low_soc_calc_threshold)
+ return chip->dt.cfg_low_soc_calculate_soc_ms;
+ else
+ return chip->dt.cfg_calculate_soc_ms;
+}
+
+#define VADC_CALIB_UV 625000
+#define VBATT_MUL_FACTOR 3
+static int adjust_vbatt_reading(struct qpnp_bms_chip *chip, int reading_uv)
+{
+ s64 numerator, denominator;
+
+ if (reading_uv == 0)
+ return 0;
+
+ /* don't adjust if not calibrated */
+ if (chip->vadc_v0625 == 0 || chip->vadc_v1250 == 0) {
+ pr_debug("No cal yet return %d\n",
+ VBATT_MUL_FACTOR * reading_uv);
+ return VBATT_MUL_FACTOR * reading_uv;
+ }
+
+ numerator = ((s64)reading_uv - chip->vadc_v0625) * VADC_CALIB_UV;
+ denominator = (s64)chip->vadc_v1250 - chip->vadc_v0625;
+
+ if (denominator == 0)
+ return reading_uv * VBATT_MUL_FACTOR;
+
+ return (VADC_CALIB_UV + div_s64(numerator, denominator))
+ * VBATT_MUL_FACTOR;
+}
+
+static int calib_vadc(struct qpnp_bms_chip *chip)
+{
+ int rc, raw_0625, raw_1250;
+ struct qpnp_vadc_result result;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, REF_625MV, &result);
+ if (rc) {
+ pr_debug("vadc read failed with rc = %d\n", rc);
+ return rc;
+ }
+ raw_0625 = result.adc_code;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, REF_125V, &result);
+ if (rc) {
+ pr_debug("vadc read failed with rc = %d\n", rc);
+ return rc;
+ }
+ raw_1250 = result.adc_code;
+
+ chip->vadc_v0625 = vadc_reading_to_uv(raw_0625, false);
+ chip->vadc_v1250 = vadc_reading_to_uv(raw_1250, false);
+
+ pr_debug("vadc calib: 0625=%d raw (%d uv), 1250=%d raw (%d uv)\n",
+ raw_0625, chip->vadc_v0625, raw_1250, chip->vadc_v1250);
+
+ return 0;
+}
+
+static int convert_vbatt_raw_to_uv(struct qpnp_bms_chip *chip,
+ u16 reading, bool is_pon_ocv)
+{
+ int64_t uv, vbatt;
+ int rc;
+
+ uv = vadc_reading_to_uv(reading, true);
+ pr_debug("%u raw converted into %lld uv\n", reading, uv);
+
+ uv = adjust_vbatt_reading(chip, uv);
+ pr_debug("adjusted into %lld uv\n", uv);
+
+ vbatt = uv;
+ rc = qpnp_vbat_sns_comp_result(chip->vadc_dev, &uv, is_pon_ocv);
+ if (rc) {
+ pr_debug("Vbatt compensation failed rc = %d\n", rc);
+ uv = vbatt;
+ } else {
+ pr_debug("temp-compensated %lld into %lld uv\n", vbatt, uv);
+ }
+
+ return uv;
+}
+
+static void convert_and_store_ocv(struct qpnp_bms_chip *chip,
+ int batt_temp, bool is_pon_ocv)
+{
+ int rc;
+
+ rc = calib_vadc(chip);
+ if (rc)
+ pr_err("Vadc reference voltage read failed, rc = %d\n", rc);
+
+ chip->last_ocv_uv = convert_vbatt_raw_to_uv(chip,
+ chip->last_ocv_raw, is_pon_ocv);
+
+ pr_debug("last_ocv_uv = %d\n", chip->last_ocv_uv);
+}
+
+static int read_and_update_ocv(struct qpnp_bms_chip *chip, int batt_temp,
+ bool is_pon_ocv)
+{
+ int rc, ocv_uv;
+ u16 ocv_data = 0;
+
+ /* read the BMS h/w OCV */
+ rc = qpnp_read_wrapper(chip, (u8 *)&ocv_data,
+ chip->base + OCV_DATA0_REG, 2);
+ if (rc) {
+ pr_err("Error reading ocv: rc = %d\n", rc);
+ return -ENXIO;
+ }
+
+ /* check if OCV is within limits */
+ ocv_uv = convert_vbatt_raw_to_uv(chip, ocv_data, is_pon_ocv);
+ if (ocv_uv < MIN_OCV_UV) {
+ pr_err("OCV too low or invalid (%d)- rejecting it\n", ocv_uv);
+ return 0;
+ }
+
+ if ((chip->last_ocv_raw == OCV_UNINITIALIZED) ||
+ (chip->last_ocv_raw != ocv_data)) {
+ pr_debug("new OCV!\n");
+ chip->last_ocv_raw = ocv_data;
+ convert_and_store_ocv(chip, batt_temp, is_pon_ocv);
+ }
+
+ pr_debug("ocv_raw=0x%x last_ocv_raw=0x%x last_ocv_uv=%d\n",
+ ocv_data, chip->last_ocv_raw, chip->last_ocv_uv);
+
+ return 0;
+}
+
+static int get_battery_voltage(struct qpnp_bms_chip *chip, int *result_uv)
+{
+ int rc;
+ struct qpnp_vadc_result adc_result;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, VBAT_SNS, &adc_result);
+ if (rc) {
+ pr_err("error reading adc channel = %d, rc = %d\n",
+ VBAT_SNS, rc);
+ return rc;
+ }
+ pr_debug("mvolts phy=%lld meas=0x%llx\n", adc_result.physical,
+ adc_result.measurement);
+ *result_uv = (int)adc_result.physical;
+
+ return 0;
+}
+
+static int get_battery_status(struct qpnp_bms_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->batt_psy == NULL)
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (chip->batt_psy) {
+ /* if battery has been registered, use the status property */
+ power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &ret);
+ return ret.intval;
+ }
+
+ /* Default to false if the battery power supply is not registered. */
+ pr_debug("battery power supply is not registered\n");
+ return POWER_SUPPLY_STATUS_UNKNOWN;
+}
+
+static int get_batt_therm(struct qpnp_bms_chip *chip, int *batt_temp)
+{
+ int rc;
+ struct qpnp_vadc_result result;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX1_BATT_THERM, &result);
+ if (rc) {
+ pr_err("error reading adc channel = %d, rc = %d\n",
+ LR_MUX1_BATT_THERM, rc);
+ return rc;
+ }
+ pr_debug("batt_temp phy = %lld meas = 0x%llx\n",
+ result.physical, result.measurement);
+
+ *batt_temp = (int)result.physical;
+
+ return 0;
+}
+
+static int get_prop_bms_rbatt(struct qpnp_bms_chip *chip)
+{
+ return chip->batt_data->default_rbatt_mohm;
+}
+
+static int get_rbatt(struct qpnp_bms_chip *chip, int soc, int batt_temp)
+{
+ int rbatt_mohm, scalefactor;
+
+ rbatt_mohm = chip->batt_data->default_rbatt_mohm;
+ if (chip->batt_data->rbatt_sf_lut == NULL) {
+ pr_debug("RBATT = %d\n", rbatt_mohm);
+ return rbatt_mohm;
+ }
+
+ scalefactor = interpolate_scalingfactor(chip->batt_data->rbatt_sf_lut,
+ batt_temp, soc);
+ rbatt_mohm = (rbatt_mohm * scalefactor) / 100;
+
+ if (chip->dt.cfg_r_conn_mohm > 0)
+ rbatt_mohm += chip->dt.cfg_r_conn_mohm;
+
+ return rbatt_mohm;
+}
+
+static void charging_began(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ u8 state;
+
+ mutex_lock(&chip->last_soc_mutex);
+
+ chip->charge_start_tm_sec = 0;
+ chip->catch_up_time_sec = 0;
+ chip->start_soc = chip->last_soc;
+
+ /*
+ * reset ocv_at_100 to -EINVAL to indicate
+ * start of charging.
+ */
+ chip->ocv_at_100 = -EINVAL;
+
+ mutex_unlock(&chip->last_soc_mutex);
+
+ /*
+ * If the BMS state is not in S2, force it in S2. Such
+ * a condition can only occur if we are coming out of
+ * suspend.
+ */
+ mutex_lock(&chip->state_change_mutex);
+ rc = get_fsm_state(chip, &state);
+ if (rc)
+ pr_err("Unable to get FSM state rc=%d\n", rc);
+ if (rc || (state != S2_STATE)) {
+ pr_debug("Forcing S2 state\n");
+ rc = force_fsm_state(chip, S2_STATE);
+ if (rc)
+ pr_err("Unable to set FSM state rc=%d\n", rc);
+ }
+ mutex_unlock(&chip->state_change_mutex);
+}
+
+static void charging_ended(struct qpnp_bms_chip *chip)
+{
+ u8 state;
+ int rc, status = get_battery_status(chip);
+
+ mutex_lock(&chip->last_soc_mutex);
+
+ chip->charge_start_tm_sec = 0;
+ chip->catch_up_time_sec = 0;
+ chip->end_soc = chip->last_soc;
+
+ if (status == POWER_SUPPLY_STATUS_FULL)
+ chip->last_soc_invalid = true;
+
+ mutex_unlock(&chip->last_soc_mutex);
+
+ /*
+ * If the BMS state is not in S2, force it in S2. Such
+ * a condition can only occur if we are coming out of
+ * suspend.
+ */
+ mutex_lock(&chip->state_change_mutex);
+ rc = get_fsm_state(chip, &state);
+ if (rc)
+ pr_err("Unable to get FSM state rc=%d\n", rc);
+ if (rc || (state != S2_STATE)) {
+ pr_debug("Forcing S2 state\n");
+ rc = force_fsm_state(chip, S2_STATE);
+ if (rc)
+ pr_err("Unable to set FSM state rc=%d\n", rc);
+ }
+ mutex_unlock(&chip->state_change_mutex);
+
+ /* Calculate charge accumulated and update charge cycle */
+ if (chip->dt.cfg_battery_aging_comp &&
+ (chip->end_soc > chip->start_soc)) {
+ chip->charge_increase += (chip->end_soc - chip->start_soc);
+ if (chip->charge_increase > 100) {
+ chip->charge_cycles++;
+ chip->charge_increase %= 100;
+ }
+ pr_debug("start_soc=%u end_soc=%u charge_cycles=%u charge_increase=%u\n",
+ chip->start_soc, chip->end_soc,
+ chip->charge_cycles, chip->charge_increase);
+ rc = backup_charge_cycle(chip);
+ if (rc)
+ pr_err("Unable to store charge cycles rc=%d\n", rc);
+ }
+}
+
+static int estimate_ocv(struct qpnp_bms_chip *chip)
+{
+ int i, rc, vbatt = 0, vbatt_final = 0;
+
+ for (i = 0; i < 5; i++) {
+ rc = get_battery_voltage(chip, &vbatt);
+ if (rc) {
+ pr_err("Unable to read battery-voltage rc=%d\n", rc);
+ return rc;
+ }
+ /*
+ * Conservatively select the lowest vbatt to avoid reporting
+ * a higher ocv due to variations in bootup current.
+ */
+
+ if (i == 0)
+ vbatt_final = vbatt;
+ else if (vbatt < vbatt_final)
+ vbatt_final = vbatt;
+
+ msleep(20);
+ }
+
+ /*
+ * TODO: Revisit the OCV calcuations to use approximate ibatt
+ * and rbatt.
+ */
+ return vbatt_final;
+}
+
+static int scale_soc_while_chg(struct qpnp_bms_chip *chip, int chg_time_sec,
+ int catch_up_sec, int new_soc, int prev_soc)
+{
+ int scaled_soc;
+ int numerator;
+
+ /*
+ * Don't report a high value immediately slowly scale the
+ * value from prev_soc to the new soc based on a charge time
+ * weighted average
+ */
+ pr_debug("cts=%d catch_up_sec=%d\n", chg_time_sec, catch_up_sec);
+ if (catch_up_sec == 0)
+ return new_soc;
+
+ if (chg_time_sec > catch_up_sec)
+ return new_soc;
+
+ numerator = (catch_up_sec - chg_time_sec) * prev_soc
+ + chg_time_sec * new_soc;
+ scaled_soc = numerator / catch_up_sec;
+
+ pr_debug("cts=%d new_soc=%d prev_soc=%d scaled_soc=%d\n",
+ chg_time_sec, new_soc, prev_soc, scaled_soc);
+
+ return scaled_soc;
+}
+
+static int report_eoc(struct qpnp_bms_chip *chip)
+{
+ int rc = -EINVAL;
+ union power_supply_propval ret = {0,};
+
+ if (chip->batt_psy == NULL)
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (chip->batt_psy) {
+ power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &ret);
+ if (rc) {
+ pr_err("Unable to get battery 'STATUS' rc=%d\n", rc);
+ } else if (ret.intval != POWER_SUPPLY_STATUS_FULL) {
+ pr_debug("Report EOC to charger\n");
+ ret.intval = POWER_SUPPLY_STATUS_FULL;
+ rc = power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &ret);
+ if (rc) {
+ pr_err("Unable to set 'STATUS' rc=%d\n", rc);
+ return rc;
+ }
+ chip->eoc_reported = true;
+ }
+ } else {
+ pr_err("battery psy not registered\n");
+ }
+
+ return rc;
+}
+
+static void check_recharge_condition(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ union power_supply_propval ret = {0,};
+ int status = get_battery_status(chip);
+
+ if (chip->last_soc > chip->dt.cfg_soc_resume_limit)
+ return;
+
+ if (status == POWER_SUPPLY_STATUS_UNKNOWN) {
+ pr_debug("Unable to read battery status\n");
+ return;
+ }
+
+ /* Report recharge to charger for SOC based resume of charging */
+ if ((status != POWER_SUPPLY_STATUS_CHARGING) && chip->eoc_reported) {
+ ret.intval = POWER_SUPPLY_STATUS_CHARGING;
+ rc = power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &ret);
+ if (rc < 0) {
+ pr_err("Unable to set battery property rc=%d\n", rc);
+ } else {
+ pr_info("soc dropped below resume_soc soc=%d resume_soc=%d, restart charging\n",
+ chip->last_soc,
+ chip->dt.cfg_soc_resume_limit);
+ chip->eoc_reported = false;
+ }
+ }
+}
+
+static void check_eoc_condition(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ int status = get_battery_status(chip);
+ union power_supply_propval ret = {0,};
+
+ if (status == POWER_SUPPLY_STATUS_UNKNOWN) {
+ pr_err("Unable to read battery status\n");
+ return;
+ }
+
+ /*
+ * Check battery status:
+ * if last_soc is 100 and battery status is still charging
+ * reset ocv_at_100 and force reporting of eoc to charger.
+ */
+ if ((chip->last_soc == 100) &&
+ (status == POWER_SUPPLY_STATUS_CHARGING))
+ chip->ocv_at_100 = -EINVAL;
+
+ /*
+ * Store the OCV value at 100. If the new ocv is greater than
+ * ocv_at_100 (battery settles), update ocv_at_100. Else
+ * if the SOC drops, reset ocv_at_100.
+ */
+ if (chip->ocv_at_100 == -EINVAL) {
+ if (chip->last_soc == 100) {
+ if (chip->dt.cfg_report_charger_eoc) {
+ rc = report_eoc(chip);
+ if (!rc) {
+ /*
+ * update ocv_at_100 only if EOC is
+ * reported successfully.
+ */
+ chip->ocv_at_100 = chip->last_ocv_uv;
+ pr_debug("Battery FULL\n");
+ } else {
+ pr_err("Unable to report eoc rc=%d\n",
+ rc);
+ chip->ocv_at_100 = -EINVAL;
+ }
+ }
+ if (chip->dt.cfg_use_reported_soc) {
+ /* begin reported_soc process */
+ chip->reported_soc_in_use = true;
+ chip->charger_removed_since_full = false;
+ chip->charger_reinserted = false;
+ chip->reported_soc = 100;
+ pr_debug("Begin reported_soc process\n");
+ }
+ }
+ } else {
+ if (chip->last_ocv_uv >= chip->ocv_at_100) {
+ pr_debug("new_ocv(%d) > ocv_at_100(%d) maintaining SOC to 100\n",
+ chip->last_ocv_uv, chip->ocv_at_100);
+ chip->ocv_at_100 = chip->last_ocv_uv;
+ chip->last_soc = 100;
+ } else if (chip->last_soc != 100) {
+ /*
+ * Report that the battery is discharging.
+ * This gets called once when the SOC falls
+ * below 100.
+ */
+ if (chip->reported_soc_in_use
+ && chip->reported_soc == 100) {
+ pr_debug("reported_soc=100, last_soc=%d, do not send DISCHARING status\n",
+ chip->last_soc);
+ } else {
+ ret.intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &ret);
+ }
+ pr_debug("SOC dropped (%d) discarding ocv_at_100\n",
+ chip->last_soc);
+ chip->ocv_at_100 = -EINVAL;
+ }
+ }
+}
+
+static int report_voltage_based_soc(struct qpnp_bms_chip *chip)
+{
+ pr_debug("Reported voltage based soc = %d\n",
+ chip->prev_voltage_based_soc);
+ return chip->prev_voltage_based_soc;
+}
+
+static int prepare_reported_soc(struct qpnp_bms_chip *chip)
+{
+ if (chip->charger_removed_since_full == false) {
+ /*
+ * charger is not removed since full,
+ * keep reported_soc as 100 and calculate the delta soc
+ * between reported_soc and last_soc
+ */
+ chip->reported_soc = 100;
+ chip->reported_soc_delta = 100 - chip->last_soc;
+ pr_debug("Keep at reported_soc 100, reported_soc_delta=%d, last_soc=%d\n",
+ chip->reported_soc_delta,
+ chip->last_soc);
+ } else {
+ /* charger is removed since full */
+ if (chip->charger_reinserted) {
+ /*
+ * charger reinserted, keep the reported_soc
+ * until it equals to last_soc.
+ */
+ if (chip->reported_soc == chip->last_soc) {
+ chip->reported_soc_in_use = false;
+ chip->reported_soc_high_current = false;
+ pr_debug("reported_soc equals to last_soc, stop reported_soc process\n");
+ }
+ chip->reported_soc_change_sec = 0;
+ }
+ }
+ pr_debug("Reporting reported_soc=%d, last_soc=%d\n",
+ chip->reported_soc, chip->last_soc);
+ return chip->reported_soc;
+}
+
+#define SOC_CATCHUP_SEC_MAX 600
+#define SOC_CATCHUP_SEC_PER_PERCENT 60
+#define MAX_CATCHUP_SOC (SOC_CATCHUP_SEC_MAX / SOC_CATCHUP_SEC_PER_PERCENT)
+#define SOC_CHANGE_PER_SEC 5
+static int report_vm_bms_soc(struct qpnp_bms_chip *chip)
+{
+ int soc, soc_change, batt_temp, rc;
+ int time_since_last_change_sec = 0, charge_time_sec = 0;
+ unsigned long last_change_sec;
+ bool charging;
+
+ soc = chip->calculated_soc;
+
+ last_change_sec = chip->last_soc_change_sec;
+ calculate_delta_time(&last_change_sec, &time_since_last_change_sec);
+
+ charging = is_battery_charging(chip);
+
+ pr_debug("charging=%d last_soc=%d last_soc_unbound=%d\n",
+ charging, chip->last_soc, chip->last_soc_unbound);
+ /*
+ * account for charge time - limit it to SOC_CATCHUP_SEC to
+ * avoid overflows when charging continues for extended periods
+ */
+ if (charging && chip->last_soc != -EINVAL) {
+ if (chip->charge_start_tm_sec == 0 ||
+ (chip->catch_up_time_sec == 0 &&
+ (abs(soc - chip->last_soc) >= MIN_SOC_UUC))) {
+ /*
+ * calculating soc for the first time
+ * after start of chg. Initialize catchup time
+ */
+ if (abs(soc - chip->last_soc) < MAX_CATCHUP_SOC)
+ chip->catch_up_time_sec =
+ (soc - chip->last_soc)
+ * SOC_CATCHUP_SEC_PER_PERCENT;
+ else
+ chip->catch_up_time_sec = SOC_CATCHUP_SEC_MAX;
+
+ chip->chg_start_soc = chip->last_soc;
+
+ if (chip->catch_up_time_sec < 0)
+ chip->catch_up_time_sec = 0;
+ chip->charge_start_tm_sec = last_change_sec;
+
+ pr_debug("chg_start_soc=%d charge_start_tm_sec=%d catch_up_time_sec=%d\n",
+ chip->chg_start_soc, chip->charge_start_tm_sec,
+ chip->catch_up_time_sec);
+ }
+
+ charge_time_sec = min(SOC_CATCHUP_SEC_MAX, (int)last_change_sec
+ - chip->charge_start_tm_sec);
+
+ /* end catchup if calculated soc and last soc are same */
+ if (chip->last_soc == soc) {
+ chip->catch_up_time_sec = 0;
+ chip->chg_start_soc = chip->last_soc;
+ }
+ }
+
+ if (chip->last_soc != -EINVAL) {
+ /*
+ * last_soc < soc ... if we have not been charging at all
+ * since the last time this was called, report previous SoC.
+ * Otherwise, scale and catch up.
+ */
+ rc = get_batt_therm(chip, &batt_temp);
+ if (rc)
+ batt_temp = BMS_DEFAULT_TEMP;
+
+ if (chip->last_soc < soc && !charging)
+ soc = chip->last_soc;
+ else if (chip->last_soc < soc && soc != 100)
+ soc = scale_soc_while_chg(chip, charge_time_sec,
+ chip->catch_up_time_sec,
+ soc, chip->chg_start_soc);
+
+ /*
+ * if the battery is close to cutoff or if the batt_temp
+ * is under the low-temp threshold allow bigger change
+ */
+ if (bms_wake_active(&chip->vbms_lv_wake_source) ||
+ (batt_temp <= chip->dt.cfg_low_temp_threshold))
+ soc_change = min((int)abs(chip->last_soc - soc),
+ time_since_last_change_sec);
+ else
+ soc_change = min((int)abs(chip->last_soc - soc),
+ time_since_last_change_sec
+ / SOC_CHANGE_PER_SEC);
+
+ if (chip->last_soc_unbound) {
+ chip->last_soc_unbound = false;
+ } else {
+ /*
+ * if soc have not been unbound by resume,
+ * only change reported SoC by 1.
+ */
+ soc_change = min(1, soc_change);
+ }
+
+ if (soc < chip->last_soc && soc != 0)
+ soc = chip->last_soc - soc_change;
+ if (soc > chip->last_soc && soc != 100)
+ soc = chip->last_soc + soc_change;
+ }
+
+ if (chip->last_soc != soc && !chip->last_soc_unbound)
+ chip->last_soc_change_sec = last_change_sec;
+
+ /*
+ * Check/update eoc under following condition:
+ * if there is change in soc:
+ * soc != chip->last_soc
+ * during bootup if soc is 100:
+ */
+ soc = bound_soc(soc);
+ if ((soc != chip->last_soc) || (soc == 100)) {
+ chip->last_soc = soc;
+ check_eoc_condition(chip);
+ if ((chip->dt.cfg_soc_resume_limit > 0) && !charging)
+ check_recharge_condition(chip);
+ }
+
+ pr_debug("last_soc=%d calculated_soc=%d soc=%d time_since_last_change=%d\n",
+ chip->last_soc, chip->calculated_soc,
+ soc, time_since_last_change_sec);
+
+ /*
+ * Backup the actual ocv (last_ocv_uv) and not the
+ * last_soc-interpolated ocv. This makes sure that
+ * the BMS algorithm always uses the correct ocv and
+ * can catch up on the last_soc (across reboots).
+ * We do not want the algorithm to be based of a wrong
+ * initial OCV.
+ */
+
+ backup_ocv_soc(chip, chip->last_ocv_uv, chip->last_soc);
+
+ if (chip->reported_soc_in_use)
+ return prepare_reported_soc(chip);
+
+ pr_debug("Reported SOC=%d\n", chip->last_soc);
+
+ return chip->last_soc;
+}
+
+static int report_state_of_charge(struct qpnp_bms_chip *chip)
+{
+ int soc;
+
+ mutex_lock(&chip->last_soc_mutex);
+
+ if (chip->dt.cfg_use_voltage_soc)
+ soc = report_voltage_based_soc(chip);
+ else
+ soc = report_vm_bms_soc(chip);
+
+ mutex_unlock(&chip->last_soc_mutex);
+
+ return soc;
+}
+
+static void btm_notify_vbat(enum qpnp_tm_state state, void *ctx)
+{
+ struct qpnp_bms_chip *chip = ctx;
+ int vbat_uv;
+ int rc;
+
+ rc = get_battery_voltage(chip, &vbat_uv);
+ if (rc) {
+ pr_err("error reading vbat_sns adc channel=%d, rc=%d\n",
+ VBAT_SNS, rc);
+ goto out;
+ }
+
+ pr_debug("vbat is at %d, state is at %d\n", vbat_uv, state);
+
+ if (state == ADC_TM_LOW_STATE) {
+ pr_debug("low voltage btm notification triggered\n");
+ if (vbat_uv <= (chip->vbat_monitor_params.low_thr
+ + VBATT_ERROR_MARGIN)) {
+ if (!bms_wake_active(&chip->vbms_lv_wake_source))
+ bms_stay_awake(&chip->vbms_lv_wake_source);
+
+ chip->vbat_monitor_params.state_request =
+ ADC_TM_HIGH_THR_ENABLE;
+ } else {
+ pr_debug("faulty btm trigger, discarding\n");
+ goto out;
+ }
+ } else if (state == ADC_TM_HIGH_STATE) {
+ pr_debug("high voltage btm notification triggered\n");
+ if (vbat_uv > chip->vbat_monitor_params.high_thr) {
+ chip->vbat_monitor_params.state_request =
+ ADC_TM_LOW_THR_ENABLE;
+ if (bms_wake_active(&chip->vbms_lv_wake_source))
+ bms_relax(&chip->vbms_lv_wake_source);
+ } else {
+ pr_debug("faulty btm trigger, discarding\n");
+ goto out;
+ }
+ } else {
+ pr_debug("unknown voltage notification state: %d\n", state);
+ goto out;
+ }
+
+ if (chip->bms_psy_registered)
+ power_supply_changed(chip->bms_psy);
+
+out:
+ qpnp_adc_tm_channel_measure(chip->adc_tm_dev,
+ &chip->vbat_monitor_params);
+}
+
+static int reset_vbat_monitoring(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ chip->vbat_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_DISABLE;
+ rc = qpnp_adc_tm_channel_measure(chip->adc_tm_dev,
+ &chip->vbat_monitor_params);
+ if (rc) {
+ pr_err("tm disable failed: %d\n", rc);
+ return rc;
+ }
+
+ if (bms_wake_active(&chip->vbms_lv_wake_source))
+ bms_relax(&chip->vbms_lv_wake_source);
+
+ return 0;
+}
+
+static int setup_vbat_monitoring(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ chip->vbat_monitor_params.low_thr =
+ chip->dt.cfg_low_voltage_threshold;
+ chip->vbat_monitor_params.high_thr =
+ chip->dt.cfg_low_voltage_threshold
+ + VBATT_ERROR_MARGIN;
+ chip->vbat_monitor_params.state_request = ADC_TM_LOW_THR_ENABLE;
+ chip->vbat_monitor_params.channel = VBAT_SNS;
+ chip->vbat_monitor_params.btm_ctx = chip;
+ chip->vbat_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
+ chip->vbat_monitor_params.threshold_notification = &btm_notify_vbat;
+ pr_debug("set low thr to %d and high to %d\n",
+ chip->vbat_monitor_params.low_thr,
+ chip->vbat_monitor_params.high_thr);
+
+ rc = qpnp_adc_tm_channel_measure(chip->adc_tm_dev,
+ &chip->vbat_monitor_params);
+ if (rc) {
+ pr_err("adc-tm setup failed: %d\n", rc);
+ return rc;
+ }
+
+ pr_debug("vbat monitoring setup complete\n");
+ return 0;
+}
+
+static void very_low_voltage_check(struct qpnp_bms_chip *chip, int vbat_uv)
+{
+ if (!bms_wake_active(&chip->vbms_lv_wake_source)
+ && (vbat_uv <= chip->dt.cfg_low_voltage_threshold)) {
+ pr_debug("voltage=%d holding low voltage ws\n", vbat_uv);
+ bms_stay_awake(&chip->vbms_lv_wake_source);
+ } else if (bms_wake_active(&chip->vbms_lv_wake_source)
+ && (vbat_uv > chip->dt.cfg_low_voltage_threshold)) {
+ pr_debug("voltage=%d releasing low voltage ws\n", vbat_uv);
+ bms_relax(&chip->vbms_lv_wake_source);
+ }
+}
+
+static void cv_voltage_check(struct qpnp_bms_chip *chip, int vbat_uv)
+{
+ if (bms_wake_active(&chip->vbms_cv_wake_source)) {
+ if ((vbat_uv < (chip->dt.cfg_max_voltage_uv -
+ VBATT_ERROR_MARGIN + CV_DROP_MARGIN))
+ && !is_battery_taper_charging(chip)) {
+ pr_debug("Fell below CV, releasing cv ws\n");
+ chip->in_cv_state = false;
+ bms_relax(&chip->vbms_cv_wake_source);
+ } else if (!is_battery_charging(chip)) {
+ pr_debug("charging stopped, releasing cv ws\n");
+ chip->in_cv_state = false;
+ bms_relax(&chip->vbms_cv_wake_source);
+ }
+ } else if (!bms_wake_active(&chip->vbms_cv_wake_source)
+ && is_battery_charging(chip)
+ && ((vbat_uv > (chip->dt.cfg_max_voltage_uv -
+ VBATT_ERROR_MARGIN))
+ || is_battery_taper_charging(chip))) {
+ pr_debug("CC_TO_CV voltage=%d holding cv ws\n", vbat_uv);
+ chip->in_cv_state = true;
+ bms_stay_awake(&chip->vbms_cv_wake_source);
+ }
+}
+
+static void low_soc_check(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ if (chip->dt.cfg_low_soc_fifo_length < 1)
+ return;
+
+ mutex_lock(&chip->state_change_mutex);
+
+ if (chip->calculated_soc <= chip->dt.cfg_low_soc_calc_threshold) {
+ if (!chip->low_soc_fifo_set) {
+ pr_debug("soc=%d (low-soc) setting fifo_length to %d\n",
+ chip->calculated_soc,
+ chip->dt.cfg_low_soc_fifo_length);
+ rc = get_fifo_length(chip, S2_STATE,
+ &chip->s2_fifo_length);
+ if (rc) {
+ pr_err("Unable to get_fifo_length rc=%d", rc);
+ goto low_soc_exit;
+ }
+ rc = set_fifo_length(chip, S2_STATE,
+ chip->dt.cfg_low_soc_fifo_length);
+ if (rc) {
+ pr_err("Unable to set_fifo_length rc=%d", rc);
+ goto low_soc_exit;
+ }
+ chip->low_soc_fifo_set = true;
+ }
+ } else {
+ if (chip->low_soc_fifo_set) {
+ pr_debug("soc=%d setting back fifo_length to %d\n",
+ chip->calculated_soc,
+ chip->s2_fifo_length);
+ rc = set_fifo_length(chip, S2_STATE,
+ chip->s2_fifo_length);
+ if (rc) {
+ pr_err("Unable to set_fifo_length rc=%d", rc);
+ goto low_soc_exit;
+ }
+ chip->low_soc_fifo_set = false;
+ }
+ }
+
+low_soc_exit:
+ mutex_unlock(&chip->state_change_mutex);
+}
+
+static int calculate_soc_from_voltage(struct qpnp_bms_chip *chip)
+{
+ int voltage_range_uv, voltage_remaining_uv, voltage_based_soc;
+ int rc, vbat_uv;
+
+ /* check if we have the averaged fifo data */
+ if (chip->voltage_soc_uv) {
+ vbat_uv = chip->voltage_soc_uv;
+ } else {
+ rc = get_battery_voltage(chip, &vbat_uv);
+ if (rc < 0) {
+ pr_err("adc vbat failed err = %d\n", rc);
+ return rc;
+ }
+ pr_debug("instant-voltage based voltage-soc\n");
+ }
+
+ voltage_range_uv = chip->dt.cfg_max_voltage_uv -
+ chip->dt.cfg_v_cutoff_uv;
+ voltage_remaining_uv = vbat_uv - chip->dt.cfg_v_cutoff_uv;
+ voltage_based_soc = voltage_remaining_uv * 100 / voltage_range_uv;
+
+ voltage_based_soc = clamp(voltage_based_soc, 0, 100);
+
+ if (chip->prev_voltage_based_soc != voltage_based_soc
+ && chip->bms_psy_registered) {
+ pr_debug("update bms_psy\n");
+ power_supply_changed(chip->bms_psy);
+ }
+ chip->prev_voltage_based_soc = voltage_based_soc;
+
+ pr_debug("vbat used = %duv\n", vbat_uv);
+ pr_debug("Calculated voltage based soc=%d\n", voltage_based_soc);
+
+ if (voltage_based_soc == 100)
+ if (chip->dt.cfg_report_charger_eoc)
+ report_eoc(chip);
+
+ return 0;
+}
+
+static void calculate_reported_soc(struct qpnp_bms_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->last_soc < 0) {
+ pr_debug("last_soc is not ready, return\n");
+ return;
+ }
+
+ if (chip->reported_soc > chip->last_soc) {
+ /*send DISCHARGING status if the reported_soc drops from 100 */
+ if (chip->reported_soc == 100) {
+ ret.intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &ret);
+ pr_debug("Report discharging status, reported_soc=%d, last_soc=%d\n",
+ chip->reported_soc, chip->last_soc);
+ }
+ /*
+ * reported_soc_delta is used to prevent
+ * the big change in last_soc,
+ * this is not used in high current mode
+ */
+ if (chip->reported_soc_delta > 0)
+ chip->reported_soc_delta--;
+
+ if (chip->reported_soc_high_current)
+ chip->reported_soc--;
+ else
+ chip->reported_soc = chip->last_soc
+ + chip->reported_soc_delta;
+
+ pr_debug("New reported_soc=%d, last_soc is=%d\n",
+ chip->reported_soc, chip->last_soc);
+ } else {
+ chip->reported_soc_in_use = false;
+ chip->reported_soc_high_current = false;
+ pr_debug("reported_soc equals last_soc,stop reported_soc process\n");
+ }
+ pr_debug("bms power_supply_changed\n");
+ power_supply_changed(chip->bms_psy);
+}
+
+static int clamp_soc_based_on_voltage(struct qpnp_bms_chip *chip, int soc)
+{
+ int rc, vbat_uv;
+
+ rc = get_battery_voltage(chip, &vbat_uv);
+ if (rc < 0) {
+ pr_err("adc vbat failed err = %d\n", rc);
+ return soc;
+ }
+
+ /* only clamp when discharging */
+ if (is_battery_charging(chip))
+ return soc;
+
+ if (soc <= 0 && vbat_uv > chip->dt.cfg_v_cutoff_uv) {
+ pr_debug("clamping soc to 1, vbat (%d) > cutoff (%d)\n",
+ vbat_uv, chip->dt.cfg_v_cutoff_uv);
+ return 1;
+ }
+ pr_debug("not clamping, using soc = %d, vbat = %d and cutoff = %d\n",
+ soc, vbat_uv, chip->dt.cfg_v_cutoff_uv);
+ return soc;
+}
+
+static void battery_voltage_check(struct qpnp_bms_chip *chip)
+{
+ int rc, vbat_uv = 0;
+
+ rc = get_battery_voltage(chip, &vbat_uv);
+ if (rc < 0) {
+ pr_err("Failed to read battery-voltage rc=%d\n", rc);
+ } else {
+ very_low_voltage_check(chip, vbat_uv);
+ cv_voltage_check(chip, vbat_uv);
+ }
+}
+
+#define UI_SOC_CATCHUP_TIME (60)
+static void monitor_soc_work(struct work_struct *work)
+{
+ struct qpnp_bms_chip *chip = container_of(work,
+ struct qpnp_bms_chip,
+ monitor_soc_work.work);
+ int rc, new_soc = 0, batt_temp;
+
+ bms_stay_awake(&chip->vbms_soc_wake_source);
+
+ calculate_delta_time(&chip->tm_sec, &chip->delta_time_s);
+ pr_debug("elapsed_time=%d\n", chip->delta_time_s);
+
+ mutex_lock(&chip->last_soc_mutex);
+
+ if (!is_battery_present(chip)) {
+ /* if battery is not preset report 100% SOC */
+ pr_debug("battery gone, reporting 100\n");
+ chip->last_soc_invalid = true;
+ chip->last_soc = -EINVAL;
+ new_soc = 100;
+ } else {
+ battery_voltage_check(chip);
+
+ if (chip->dt.cfg_use_voltage_soc) {
+ calculate_soc_from_voltage(chip);
+ } else {
+ rc = get_batt_therm(chip, &batt_temp);
+ if (rc < 0) {
+ pr_err("Unable to read batt temp rc=%d, using default=%d\n",
+ rc, BMS_DEFAULT_TEMP);
+ batt_temp = BMS_DEFAULT_TEMP;
+ }
+
+ if (chip->last_soc_invalid) {
+ chip->last_soc_invalid = false;
+ chip->last_soc = -EINVAL;
+ }
+ new_soc = lookup_soc_ocv(chip, chip->last_ocv_uv,
+ batt_temp);
+ /* clamp soc due to BMS hw/sw immaturities */
+ new_soc = clamp_soc_based_on_voltage(chip, new_soc);
+
+ if (chip->calculated_soc != new_soc) {
+ pr_debug("SOC changed! new_soc=%d prev_soc=%d\n",
+ new_soc, chip->calculated_soc);
+ chip->calculated_soc = new_soc;
+ /*
+ * To recalculate the catch-up time, clear it
+ * when SOC changes.
+ */
+ chip->catch_up_time_sec = 0;
+
+ if (chip->calculated_soc == 100)
+ /* update last_soc immediately */
+ report_vm_bms_soc(chip);
+
+ pr_debug("update bms_psy\n");
+ power_supply_changed(chip->bms_psy);
+ } else if (chip->last_soc != chip->calculated_soc) {
+ pr_debug("update bms_psy\n");
+ power_supply_changed(chip->bms_psy);
+ } else {
+ report_vm_bms_soc(chip);
+ }
+ }
+ /* low SOC configuration */
+ low_soc_check(chip);
+ }
+ /*
+ * schedule the work only if last_soc has not caught up with
+ * the calculated soc or if we are using voltage based soc
+ */
+ if ((chip->last_soc != chip->calculated_soc) ||
+ chip->dt.cfg_use_voltage_soc)
+ schedule_delayed_work(&chip->monitor_soc_work,
+ msecs_to_jiffies(get_calculation_delay_ms(chip)));
+
+ if (chip->reported_soc_in_use && chip->charger_removed_since_full
+ && !chip->charger_reinserted) {
+ /* record the elapsed time after last reported_soc change */
+ chip->reported_soc_change_sec += chip->delta_time_s;
+ pr_debug("reported_soc_change_sec=%d\n",
+ chip->reported_soc_change_sec);
+
+ /* above the catch up time, calculate new reported_soc */
+ if (chip->reported_soc_change_sec > UI_SOC_CATCHUP_TIME) {
+ calculate_reported_soc(chip);
+ chip->reported_soc_change_sec = 0;
+ }
+ }
+
+ mutex_unlock(&chip->last_soc_mutex);
+
+ bms_relax(&chip->vbms_soc_wake_source);
+}
+
+static void voltage_soc_timeout_work(struct work_struct *work)
+{
+ struct qpnp_bms_chip *chip = container_of(work,
+ struct qpnp_bms_chip,
+ voltage_soc_timeout_work.work);
+
+ mutex_lock(&chip->bms_device_mutex);
+ if (!chip->bms_dev_open) {
+ pr_warn("BMS device not opened, using voltage based SOC\n");
+ chip->dt.cfg_use_voltage_soc = true;
+ }
+ mutex_unlock(&chip->bms_device_mutex);
+}
+
+static int get_prop_bms_capacity(struct qpnp_bms_chip *chip)
+{
+ return report_state_of_charge(chip);
+}
+
+static bool is_hi_power_state_requested(struct qpnp_bms_chip *chip)
+{
+
+ pr_debug("hi_power_state=0x%x\n", chip->hi_power_state);
+
+ if (chip->hi_power_state & VMBMS_IGNORE_ALL_BIT)
+ return false;
+ else
+ return !!chip->hi_power_state;
+
+}
+
+static int qpnp_vm_bms_config_power_state(struct qpnp_bms_chip *chip,
+ int usecase, bool hi_power_enable)
+{
+ if (usecase < 0) {
+ pr_err("Invalid power-usecase %x\n", usecase);
+ return -EINVAL;
+ }
+
+ if (hi_power_enable)
+ chip->hi_power_state |= usecase;
+ else
+ chip->hi_power_state &= ~usecase;
+
+ pr_debug("hi_power_state=%x usecase=%x hi_power_enable=%d\n",
+ chip->hi_power_state, usecase, hi_power_enable);
+
+ return 0;
+}
+
+static int get_prop_bms_current_now(struct qpnp_bms_chip *chip)
+{
+ return chip->current_now;
+}
+
+static int get_current_cc(struct qpnp_bms_chip *chip)
+{
+ int soc, cc_full;
+ int64_t current_charge;
+
+ if (chip->batt_data == NULL)
+ return -EINVAL;
+
+ cc_full = chip->batt_data->fcc;
+ if (chip->dt.cfg_use_voltage_soc)
+ soc = chip->prev_voltage_based_soc;
+ else
+ soc = chip->last_soc;
+
+ /*
+ * Full charge capacity is in mAh and soc is in %
+ * current_charge capacity is defined in uAh
+ * Hence conversion ((mAh * pct * 1000) / 100) => (mAh * pct * 10)
+ */
+ current_charge = cc_full * soc * 10;
+
+ return current_charge;
+}
+
+static enum power_supply_property bms_power_props[] = {
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_RESISTANCE,
+ POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE,
+ POWER_SUPPLY_PROP_RESISTANCE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_OCV,
+ POWER_SUPPLY_PROP_HI_POWER,
+ POWER_SUPPLY_PROP_LOW_POWER,
+ POWER_SUPPLY_PROP_BATTERY_TYPE,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_CHARGE_COUNTER,
+};
+
+static int
+qpnp_vm_bms_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+ case POWER_SUPPLY_PROP_HI_POWER:
+ case POWER_SUPPLY_PROP_LOW_POWER:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int qpnp_vm_bms_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct qpnp_bms_chip *chip = power_supply_get_drvdata(psy);
+ int value = 0, rc;
+
+ val->intval = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = get_prop_bms_capacity(chip);
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = chip->battery_status;
+ break;
+ case POWER_SUPPLY_PROP_RESISTANCE:
+ val->intval = get_prop_bms_rbatt(chip);
+ break;
+ case POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE:
+ if (chip->batt_data->rbatt_capacitive_mohm > 0)
+ val->intval = chip->batt_data->rbatt_capacitive_mohm;
+ if (chip->dt.cfg_r_conn_mohm > 0)
+ val->intval += chip->dt.cfg_r_conn_mohm;
+ break;
+ case POWER_SUPPLY_PROP_RESISTANCE_NOW:
+ rc = get_batt_therm(chip, &value);
+ if (rc < 0)
+ value = BMS_DEFAULT_TEMP;
+ val->intval = get_rbatt(chip, chip->calculated_soc, value);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = get_prop_bms_current_now(chip);
+ break;
+ case POWER_SUPPLY_PROP_BATTERY_TYPE:
+ val->strval = chip->batt_data->battery_type;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+ val->intval = chip->last_ocv_uv;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ rc = get_batt_therm(chip, &value);
+ if (rc < 0)
+ value = BMS_DEFAULT_TEMP;
+ val->intval = value;
+ break;
+ case POWER_SUPPLY_PROP_HI_POWER:
+ val->intval = is_hi_power_state_requested(chip);
+ break;
+ case POWER_SUPPLY_PROP_LOW_POWER:
+ val->intval = !is_hi_power_state_requested(chip);
+ break;
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
+ if (chip->dt.cfg_battery_aging_comp)
+ val->intval = chip->charge_cycles;
+ else
+ val->intval = -EINVAL;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+ val->intval = get_current_cc(chip);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int qpnp_vm_bms_power_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ int rc = 0;
+ struct qpnp_bms_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ chip->current_now = val->intval;
+ pr_debug("IBATT = %d\n", val->intval);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+ cancel_delayed_work_sync(&chip->monitor_soc_work);
+ chip->last_ocv_uv = val->intval;
+ pr_debug("OCV = %d\n", val->intval);
+ schedule_delayed_work(&chip->monitor_soc_work, 0);
+ break;
+ case POWER_SUPPLY_PROP_HI_POWER:
+ rc = qpnp_vm_bms_config_power_state(chip, val->intval, true);
+ if (rc)
+ pr_err("Unable to set power-state rc=%d\n", rc);
+ break;
+ case POWER_SUPPLY_PROP_LOW_POWER:
+ rc = qpnp_vm_bms_config_power_state(chip, val->intval, false);
+ if (rc)
+ pr_err("Unable to set power-state rc=%d\n", rc);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return rc;
+}
+
+static void bms_new_battery_setup(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ mutex_lock(&chip->bms_data_mutex);
+
+ chip->last_soc_invalid = true;
+ /*
+ * disable and re-enable the BMS hardware to reset
+ * the realtime-FIFO data and restart accumulation
+ */
+ rc = qpnp_masked_write_base(chip, chip->base + EN_CTL_REG,
+ BMS_EN_BIT, 0);
+ /* delay for the BMS hardware to reset its state */
+ msleep(200);
+ rc |= qpnp_masked_write_base(chip, chip->base + EN_CTL_REG,
+ BMS_EN_BIT, BMS_EN_BIT);
+ /* delay for the BMS hardware to re-start */
+ msleep(200);
+ if (rc)
+ pr_err("Unable to reset BMS rc=%d\n", rc);
+
+ chip->last_ocv_uv = estimate_ocv(chip);
+
+ memset(&chip->bms_data, 0, sizeof(chip->bms_data));
+
+ /* update the sequence number */
+ chip->bms_data.seq_num = chip->seq_num++;
+
+ /* signal the read thread */
+ chip->data_ready = 1;
+ wake_up_interruptible(&chip->bms_wait_q);
+
+ /* hold a wake lock until the read thread is scheduled */
+ if (chip->bms_dev_open)
+ pm_stay_awake(chip->dev);
+
+ mutex_unlock(&chip->bms_data_mutex);
+
+ /* reset aging variables */
+ if (chip->dt.cfg_battery_aging_comp) {
+ chip->charge_cycles = 0;
+ chip->charge_increase = 0;
+ rc = backup_charge_cycle(chip);
+ if (rc)
+ pr_err("Unable to reset aging data rc=%d\n", rc);
+ }
+}
+
+static void battery_insertion_check(struct qpnp_bms_chip *chip)
+{
+ int present = (int)is_battery_present(chip);
+
+ if (chip->battery_present != present) {
+ pr_debug("shadow_sts=%d status=%d\n",
+ chip->battery_present, present);
+ if (chip->battery_present != -EINVAL) {
+ if (present) {
+ /* new battery inserted */
+ bms_new_battery_setup(chip);
+ setup_vbat_monitoring(chip);
+ pr_debug("New battery inserted!\n");
+ } else {
+ /* battery removed */
+ reset_vbat_monitoring(chip);
+ pr_debug("Battery removed\n");
+ }
+ }
+ chip->battery_present = present;
+ }
+}
+
+static void battery_status_check(struct qpnp_bms_chip *chip)
+{
+ int status = get_battery_status(chip);
+
+ if (chip->battery_status != status) {
+ if (status == POWER_SUPPLY_STATUS_CHARGING) {
+ pr_debug("charging started\n");
+ charging_began(chip);
+ } else if (chip->battery_status ==
+ POWER_SUPPLY_STATUS_CHARGING) {
+ pr_debug("charging stopped\n");
+ charging_ended(chip);
+ }
+
+ if (status == POWER_SUPPLY_STATUS_FULL) {
+ pr_debug("battery full\n");
+ chip->battery_full = true;
+ } else if (chip->battery_status == POWER_SUPPLY_STATUS_FULL) {
+ pr_debug("battery not-full anymore\n");
+ chip->battery_full = false;
+ }
+ chip->battery_status = status;
+ }
+}
+
+#define HIGH_CURRENT_TH 2
+static void reported_soc_check_status(struct qpnp_bms_chip *chip)
+{
+ u8 present;
+
+ present = is_charger_present(chip);
+ pr_debug("usb_present=%d\n", present);
+
+ if (!present && !chip->charger_removed_since_full) {
+ chip->charger_removed_since_full = true;
+ pr_debug("reported_soc: charger removed since full\n");
+ return;
+ }
+ if (chip->reported_soc_high_current) {
+ pr_debug("reported_soc in high current mode, return\n");
+ return;
+ }
+ if ((chip->reported_soc - chip->last_soc) >
+ (100 - chip->dt.cfg_soc_resume_limit
+ + HIGH_CURRENT_TH)) {
+ chip->reported_soc_high_current = true;
+ chip->charger_removed_since_full = true;
+ chip->charger_reinserted = false;
+ pr_debug("reported_soc enters high current mode\n");
+ return;
+ }
+ if (present && chip->charger_removed_since_full) {
+ chip->charger_reinserted = true;
+ pr_debug("reported_soc: charger reinserted\n");
+ }
+ if (!present && chip->charger_removed_since_full) {
+ chip->charger_reinserted = false;
+ pr_debug("reported_soc: charger removed again\n");
+ }
+}
+
+static void qpnp_vm_bms_ext_power_changed(struct power_supply *psy)
+{
+ struct qpnp_bms_chip *chip = power_supply_get_drvdata(psy);
+
+ pr_debug("Triggered!\n");
+ battery_status_check(chip);
+ battery_insertion_check(chip);
+
+ mutex_lock(&chip->last_soc_mutex);
+ battery_voltage_check(chip);
+ mutex_unlock(&chip->last_soc_mutex);
+
+ if (chip->reported_soc_in_use)
+ reported_soc_check_status(chip);
+}
+
+
+static void dump_bms_data(const char *func, struct qpnp_bms_chip *chip)
+{
+ int i;
+
+ pr_debug("%s: fifo_count=%d acc_count=%d seq_num=%d\n",
+ func, chip->bms_data.num_fifo,
+ chip->bms_data.acc_count,
+ chip->bms_data.seq_num);
+
+ for (i = 0; i < chip->bms_data.num_fifo; i++)
+ pr_debug("fifo=%d fifo_uv=%d sample_interval=%d sample_count=%d\n",
+ i, chip->bms_data.fifo_uv[i],
+ chip->bms_data.sample_interval_ms,
+ chip->bms_data.sample_count);
+ pr_debug("avg_acc_data=%d\n", chip->bms_data.acc_uv);
+}
+
+static int read_and_populate_fifo_data(struct qpnp_bms_chip *chip)
+{
+ u8 fifo_count = 0, val = 0;
+ u8 fifo_data_raw[MAX_FIFO_REGS * 2];
+ u16 fifo_data;
+ int rc, i, j;
+ int64_t voltage_soc_avg = 0;
+
+ /* read the completed FIFO count */
+ rc = qpnp_read_wrapper(chip, &val, chip->base + STATUS2_REG, 1);
+ if (rc) {
+ pr_err("Unable to read STATUS2 register rc=%d\n", rc);
+ return rc;
+ }
+ fifo_count = (val & FIFO_CNT_SD_MASK) >> FIFO_CNT_SD_SHIFT;
+ pr_debug("fifo_count=%d\n", fifo_count);
+ if (!fifo_count) {
+ pr_debug("No data in FIFO\n");
+ return 0;
+ } else if (fifo_count > MAX_FIFO_REGS) {
+ pr_err("Invalid fifo-length %d rejecting data\n", fifo_count);
+ chip->bms_data.num_fifo = 0;
+ return 0;
+ }
+
+ /* read the FIFO data */
+ for (i = 0; i < fifo_count * 2; i++) {
+ rc = qpnp_read_wrapper(chip, &fifo_data_raw[i],
+ chip->base + FIFO_0_LSB_REG + i, 1);
+ if (rc) {
+ pr_err("Unable to read FIFO register(%d) rc=%d\n",
+ i, rc);
+ return rc;
+ }
+ }
+
+ /* populate the structure */
+ chip->bms_data.num_fifo = fifo_count;
+
+ rc = get_sample_interval(chip, chip->current_fsm_state,
+ &chip->bms_data.sample_interval_ms);
+ if (rc) {
+ pr_err("Unable to read state=%d sample_interval rc=%d\n",
+ chip->current_fsm_state, rc);
+ return rc;
+ }
+
+ rc = get_sample_count(chip, chip->current_fsm_state,
+ &chip->bms_data.sample_count);
+ if (rc) {
+ pr_err("Unable to read state=%d sample_count rc=%d\n",
+ chip->current_fsm_state, rc);
+ return rc;
+ }
+
+ for (i = 0, j = 0; i < fifo_count * 2; i = i + 2, j++) {
+ fifo_data = fifo_data_raw[i] | (fifo_data_raw[i + 1] << 8);
+ chip->bms_data.fifo_uv[j] = convert_vbatt_raw_to_uv(chip,
+ fifo_data, 0);
+ voltage_soc_avg += chip->bms_data.fifo_uv[j];
+ }
+ /* store the fifo average for voltage-based-soc */
+ chip->voltage_soc_uv = div_u64(voltage_soc_avg, fifo_count);
+
+ return 0;
+}
+
+static int read_and_populate_acc_data(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ u32 acc_data_sd = 0, acc_count_sd = 0, avg_acc_data = 0;
+
+ /* read ACC SD count */
+ rc = qpnp_read_wrapper(chip, (u8 *)&acc_count_sd,
+ chip->base + ACC_CNT_SD_REG, 1);
+ if (rc) {
+ pr_err("Unable to read ACC_CNT_SD_REG rc=%d\n", rc);
+ return rc;
+ }
+ if (!acc_count_sd) {
+ pr_debug("No data in accumulator\n");
+ return 0;
+ }
+ /* read ACC SD data */
+ rc = qpnp_read_wrapper(chip, (u8 *)&acc_data_sd,
+ chip->base + ACC_DATA0_SD_REG, 3);
+ if (rc) {
+ pr_err("Unable to read ACC_DATA0_SD_REG rc=%d\n", rc);
+ return rc;
+ }
+ avg_acc_data = div_u64(acc_data_sd, acc_count_sd);
+
+ chip->bms_data.acc_uv = convert_vbatt_raw_to_uv(chip,
+ avg_acc_data, 0);
+ chip->bms_data.acc_count = acc_count_sd;
+
+ rc = get_sample_interval(chip, chip->current_fsm_state,
+ &chip->bms_data.sample_interval_ms);
+ if (rc) {
+ pr_err("Unable to read state=%d sample_interval rc=%d\n",
+ chip->current_fsm_state, rc);
+ return rc;
+ }
+
+ rc = get_sample_count(chip, chip->current_fsm_state,
+ &chip->bms_data.sample_count);
+ if (rc) {
+ pr_err("Unable to read state=%d sample_count rc=%d\n",
+ chip->current_fsm_state, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int clear_fifo_acc_data(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ u8 reg = 0;
+
+ reg = FIFO_CNT_SD_CLR_BIT | ACC_DATA_SD_CLR_BIT | ACC_CNT_SD_CLR_BIT;
+ rc = qpnp_masked_write_base(chip, chip->base + DATA_CTL2_REG, reg, reg);
+ if (rc)
+ pr_err("Unable to write DATA_CTL2_REG rc=%d\n", rc);
+
+ return rc;
+}
+
+static irqreturn_t bms_fifo_update_done_irq_handler(int irq, void *_chip)
+{
+ int rc;
+ struct qpnp_bms_chip *chip = _chip;
+
+ pr_debug("fifo_update_done triggered\n");
+
+ mutex_lock(&chip->bms_data_mutex);
+
+ if (chip->suspend_data_valid) {
+ pr_debug("Suspend data not processed yet\n");
+ goto fail_fifo;
+ }
+
+ rc = calib_vadc(chip);
+ if (rc)
+ pr_err("Unable to calibrate vadc rc=%d\n", rc);
+
+ /* clear old data */
+ memset(&chip->bms_data, 0, sizeof(chip->bms_data));
+ /*
+ * 1. Read FIFO and populate the bms_data
+ * 2. Clear FIFO data
+ * 3. Notify userspace
+ */
+ rc = update_fsm_state(chip);
+ if (rc) {
+ pr_err("Unable to read FSM state rc=%d\n", rc);
+ goto fail_fifo;
+ }
+ pr_debug("fsm_state=%d\n", chip->current_fsm_state);
+
+ rc = read_and_populate_fifo_data(chip);
+ if (rc) {
+ pr_err("Unable to read FIFO data rc=%d\n", rc);
+ goto fail_fifo;
+ }
+
+ rc = clear_fifo_acc_data(chip);
+ if (rc)
+ pr_err("Unable to clear FIFO/ACC data rc=%d\n", rc);
+
+ /* update the sequence number */
+ chip->bms_data.seq_num = chip->seq_num++;
+
+ dump_bms_data(__func__, chip);
+
+ /* signal the read thread */
+ chip->data_ready = 1;
+ wake_up_interruptible(&chip->bms_wait_q);
+
+ /* hold a wake lock until the read thread is scheduled */
+ if (chip->bms_dev_open)
+ pm_stay_awake(chip->dev);
+fail_fifo:
+ mutex_unlock(&chip->bms_data_mutex);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t bms_fsm_state_change_irq_handler(int irq, void *_chip)
+{
+ int rc;
+ struct qpnp_bms_chip *chip = _chip;
+
+ pr_debug("fsm_state_changed triggered\n");
+
+ mutex_lock(&chip->bms_data_mutex);
+
+ if (chip->suspend_data_valid) {
+ pr_debug("Suspend data not processed yet\n");
+ goto fail_state;
+ }
+
+ rc = calib_vadc(chip);
+ if (rc)
+ pr_err("Unable to calibrate vadc rc=%d\n", rc);
+
+ /* clear old data */
+ memset(&chip->bms_data, 0, sizeof(chip->bms_data));
+ /*
+ * 1. Read FIFO and ACC_DATA and populate the bms_data
+ * 2. Clear FIFO & ACC data
+ * 3. Notify userspace
+ */
+ pr_debug("prev_fsm_state=%d\n", chip->current_fsm_state);
+
+ rc = read_and_populate_fifo_data(chip);
+ if (rc) {
+ pr_err("Unable to read FIFO data rc=%d\n", rc);
+ goto fail_state;
+ }
+
+ /* read accumulator data */
+ rc = read_and_populate_acc_data(chip);
+ if (rc) {
+ pr_err("Unable to read ACC_SD data rc=%d\n", rc);
+ goto fail_state;
+ }
+
+ rc = update_fsm_state(chip);
+ if (rc) {
+ pr_err("Unable to read FSM state rc=%d\n", rc);
+ goto fail_state;
+ }
+
+ rc = clear_fifo_acc_data(chip);
+ if (rc)
+ pr_err("Unable to clear FIFO/ACC data rc=%d\n", rc);
+
+ /* update the sequence number */
+ chip->bms_data.seq_num = chip->seq_num++;
+
+ dump_bms_data(__func__, chip);
+
+ /* signal the read thread */
+ chip->data_ready = 1;
+ wake_up_interruptible(&chip->bms_wait_q);
+
+ /* hold a wake lock until the read thread is scheduled */
+ if (chip->bms_dev_open)
+ pm_stay_awake(chip->dev);
+fail_state:
+ mutex_unlock(&chip->bms_data_mutex);
+ return IRQ_HANDLED;
+}
+
+static int read_shutdown_ocv_soc(struct qpnp_bms_chip *chip)
+{
+ u8 stored_soc = 0;
+ u16 stored_ocv = 0;
+ int rc;
+
+ rc = qpnp_read_wrapper(chip, (u8 *)&stored_ocv,
+ chip->base + BMS_OCV_REG, 2);
+ if (rc) {
+ pr_err("failed to read addr = %d %d\n",
+ chip->base + BMS_OCV_REG, rc);
+ return -EINVAL;
+ }
+
+ /* if shutdwon ocv is invalid, reject shutdown soc too */
+ if (!stored_ocv || (stored_ocv == OCV_INVALID)) {
+ pr_debug("shutdown OCV %d - invalid\n", stored_ocv);
+ chip->shutdown_ocv = OCV_INVALID;
+ chip->shutdown_soc = SOC_INVALID;
+ return -EINVAL;
+ }
+ chip->shutdown_ocv = stored_ocv * 1000;
+
+ /*
+ * The previous SOC is stored in the first 7 bits of the register as
+ * (Shutdown SOC + 1). This allows for register reset values of both
+ * 0x00 and 0xFF.
+ */
+ rc = qpnp_read_wrapper(chip, &stored_soc, chip->base + BMS_SOC_REG, 1);
+ if (rc) {
+ pr_err("failed to read addr = %d %d\n",
+ chip->base + BMS_SOC_REG, rc);
+ return -EINVAL;
+ }
+
+ if (!stored_soc || stored_soc == SOC_INVALID) {
+ chip->shutdown_soc = SOC_INVALID;
+ chip->shutdown_ocv = OCV_INVALID;
+ return -EINVAL;
+ }
+ chip->shutdown_soc = (stored_soc >> 1) - 1;
+
+ pr_debug("shutdown_ocv=%d shutdown_soc=%d\n",
+ chip->shutdown_ocv, chip->shutdown_soc);
+
+ return 0;
+}
+
+static int interpolate_current_comp(int die_temp)
+{
+ int i;
+ int num_rows = ARRAY_SIZE(temp_curr_comp_lut);
+
+ if (die_temp <= (temp_curr_comp_lut[0].temp_decideg))
+ return temp_curr_comp_lut[0].current_ma;
+
+ if (die_temp >= (temp_curr_comp_lut[num_rows - 1].temp_decideg))
+ return temp_curr_comp_lut[num_rows - 1].current_ma;
+
+ for (i = 0; i < num_rows - 1; i++)
+ if (die_temp <= (temp_curr_comp_lut[i].temp_decideg))
+ break;
+
+ if (die_temp == (temp_curr_comp_lut[i].temp_decideg))
+ return temp_curr_comp_lut[i].current_ma;
+
+ return linear_interpolate(
+ temp_curr_comp_lut[i - 1].current_ma,
+ temp_curr_comp_lut[i - 1].temp_decideg,
+ temp_curr_comp_lut[i].current_ma,
+ temp_curr_comp_lut[i].temp_decideg,
+ die_temp);
+}
+
+static void adjust_pon_ocv(struct qpnp_bms_chip *chip, int batt_temp)
+{
+ int rc, current_ma, rbatt_mohm, die_temp, delta_uv, pc;
+ struct qpnp_vadc_result result;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, DIE_TEMP, &result);
+ if (rc) {
+ pr_err("error reading adc channel=%d, rc=%d\n", DIE_TEMP, rc);
+ } else {
+ pc = interpolate_pc(chip->batt_data->pc_temp_ocv_lut,
+ batt_temp, chip->last_ocv_uv / 1000);
+ /*
+ * For pc < 2, use the rbatt of pc = 2. This is to avoid
+ * the huge rbatt values at pc < 2 which can disrupt the pon_ocv
+ * calculations.
+ */
+ if (pc < 2)
+ pc = 2;
+ rbatt_mohm = get_rbatt(chip, pc, batt_temp);
+ /* convert die_temp to DECIDEGC */
+ die_temp = (int)result.physical / 100;
+ current_ma = interpolate_current_comp(die_temp);
+ delta_uv = rbatt_mohm * current_ma;
+ pr_debug("PON OCV changed from %d to %d pc=%d rbatt=%d current_ma=%d die_temp=%d batt_temp=%d delta_uv=%d\n",
+ chip->last_ocv_uv, chip->last_ocv_uv + delta_uv, pc,
+ rbatt_mohm, current_ma, die_temp, batt_temp, delta_uv);
+
+ chip->last_ocv_uv += delta_uv;
+ }
+}
+
+static int calculate_initial_soc(struct qpnp_bms_chip *chip)
+{
+ int rc, batt_temp = 0, est_ocv = 0;
+
+ rc = get_batt_therm(chip, &batt_temp);
+ if (rc < 0) {
+ pr_err("Unable to read batt temp, using default=%d\n",
+ BMS_DEFAULT_TEMP);
+ batt_temp = BMS_DEFAULT_TEMP;
+ }
+
+ rc = read_and_update_ocv(chip, batt_temp, true);
+ if (rc) {
+ pr_err("Unable to read PON OCV rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = read_shutdown_ocv_soc(chip);
+ if (rc < 0 || chip->dt.cfg_ignore_shutdown_soc)
+ chip->shutdown_soc_invalid = true;
+
+ if (chip->warm_reset) {
+ /*
+ * if we have powered on from warm reset -
+ * Always use shutdown SOC. If shudown SOC is invalid then
+ * estimate OCV
+ */
+ if (chip->shutdown_soc_invalid) {
+ pr_debug("Estimate OCV\n");
+ est_ocv = estimate_ocv(chip);
+ if (est_ocv <= 0) {
+ pr_err("Unable to estimate OCV rc=%d\n",
+ est_ocv);
+ return -EINVAL;
+ }
+ chip->last_ocv_uv = est_ocv;
+ chip->calculated_soc = lookup_soc_ocv(chip, est_ocv,
+ batt_temp);
+ } else {
+ chip->last_ocv_uv = chip->shutdown_ocv;
+ chip->last_soc = chip->shutdown_soc;
+ chip->calculated_soc = lookup_soc_ocv(chip,
+ chip->shutdown_ocv, batt_temp);
+ pr_debug("Using shutdown SOC\n");
+ }
+ } else {
+ /*
+ * In PM8916 2.0 PON OCV calculation is delayed due to
+ * change in the ordering of power-on sequence of LDO6.
+ * Adjust PON OCV to include current during PON.
+ */
+ if (chip->workaround_flag & WRKARND_PON_OCV_COMP)
+ adjust_pon_ocv(chip, batt_temp);
+
+ /* !warm_reset use PON OCV only if shutdown SOC is invalid */
+ chip->calculated_soc = lookup_soc_ocv(chip,
+ chip->last_ocv_uv, batt_temp);
+ if (!chip->shutdown_soc_invalid &&
+ (abs(chip->shutdown_soc - chip->calculated_soc) <
+ chip->dt.cfg_shutdown_soc_valid_limit)) {
+ chip->last_ocv_uv = chip->shutdown_ocv;
+ chip->last_soc = chip->shutdown_soc;
+ chip->calculated_soc = lookup_soc_ocv(chip,
+ chip->shutdown_ocv, batt_temp);
+ pr_debug("Using shutdown SOC\n");
+ } else {
+ chip->shutdown_soc_invalid = true;
+ pr_debug("Using PON SOC\n");
+ }
+ }
+ /* store the start-up OCV for voltage-based-soc */
+ chip->voltage_soc_uv = chip->last_ocv_uv;
+
+ pr_info("warm_reset=%d est_ocv=%d shutdown_soc_invalid=%d shutdown_ocv=%d shutdown_soc=%d last_soc=%d calculated_soc=%d last_ocv_uv=%d\n",
+ chip->warm_reset, est_ocv, chip->shutdown_soc_invalid,
+ chip->shutdown_ocv, chip->shutdown_soc, chip->last_soc,
+ chip->calculated_soc, chip->last_ocv_uv);
+
+ return 0;
+}
+
+static int calculate_initial_aging_comp(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ bool battery_removed = is_battery_replaced_in_offmode(chip);
+
+ if (battery_removed || chip->shutdown_soc_invalid) {
+ pr_info("Clearing aging data battery_removed=%d shutdown_soc_invalid=%d\n",
+ battery_removed, chip->shutdown_soc_invalid);
+ chip->charge_cycles = 0;
+ chip->charge_increase = 0;
+ rc = backup_charge_cycle(chip);
+ if (rc)
+ pr_err("Unable to reset aging data rc=%d\n", rc);
+ } else {
+ rc = read_chgcycle_data_from_backup(chip);
+ if (rc)
+ pr_err("Unable to read aging data rc=%d\n", rc);
+ }
+
+ pr_debug("Initial aging data charge_cycles=%u charge_increase=%u\n",
+ chip->charge_cycles, chip->charge_increase);
+ return rc;
+}
+
+static int bms_load_hw_defaults(struct qpnp_bms_chip *chip)
+{
+ u8 val, bms_en = 0;
+ u32 interval[2], count[2], fifo[2];
+ int rc;
+
+ /* S3 OCV tolerence threshold */
+ if (chip->dt.cfg_s3_ocv_tol_uv >= 0 &&
+ chip->dt.cfg_s3_ocv_tol_uv <= MAX_OCV_TOL_THRESHOLD) {
+ val = chip->dt.cfg_s3_ocv_tol_uv / OCV_TOL_LSB_UV;
+ rc = qpnp_masked_write_base(chip,
+ chip->base + S3_OCV_TOL_CTL_REG, 0xFF, val);
+ if (rc) {
+ pr_err("Unable to write s3_ocv_tol_threshold rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ /* S1 accumulator threshold */
+ if (chip->dt.cfg_s1_sample_count >= 1 &&
+ chip->dt.cfg_s1_sample_count <= MAX_SAMPLE_COUNT) {
+ val = (chip->dt.cfg_s1_sample_count > 1) ?
+ (ilog2(chip->dt.cfg_s1_sample_count) - 1) : 0;
+ rc = qpnp_masked_write_base(chip,
+ chip->base + S1_ACC_CNT_REG,
+ ACC_CNT_MASK, val);
+ if (rc) {
+ pr_err("Unable to write s1 sample count rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ /* S2 accumulator threshold */
+ if (chip->dt.cfg_s2_sample_count >= 1 &&
+ chip->dt.cfg_s2_sample_count <= MAX_SAMPLE_COUNT) {
+ val = (chip->dt.cfg_s2_sample_count > 1) ?
+ (ilog2(chip->dt.cfg_s2_sample_count) - 1) : 0;
+ rc = qpnp_masked_write_base(chip,
+ chip->base + S2_ACC_CNT_REG,
+ ACC_CNT_MASK, val);
+ if (rc) {
+ pr_err("Unable to write s2 sample count rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->dt.cfg_s1_sample_interval_ms >= 0 &&
+ chip->dt.cfg_s1_sample_interval_ms <= MAX_SAMPLE_INTERVAL) {
+ val = chip->dt.cfg_s1_sample_interval_ms / 10;
+ rc = qpnp_write_wrapper(chip, &val,
+ chip->base + S1_SAMPLE_INTVL_REG, 1);
+ if (rc) {
+ pr_err("Unable to write s1 sample inteval rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->dt.cfg_s2_sample_interval_ms >= 0 &&
+ chip->dt.cfg_s2_sample_interval_ms <= MAX_SAMPLE_INTERVAL) {
+ val = chip->dt.cfg_s2_sample_interval_ms / 10;
+ rc = qpnp_write_wrapper(chip, &val,
+ chip->base + S2_SAMPLE_INTVL_REG, 1);
+ if (rc) {
+ pr_err("Unable to write s2 sample inteval rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->dt.cfg_s1_fifo_length >= 0 &&
+ chip->dt.cfg_s1_fifo_length <= MAX_FIFO_REGS) {
+ rc = qpnp_masked_write_base(chip, chip->base + FIFO_LENGTH_REG,
+ S1_FIFO_LENGTH_MASK,
+ chip->dt.cfg_s1_fifo_length);
+ if (rc) {
+ pr_err("Unable to write s1 fifo length rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->dt.cfg_s2_fifo_length >= 0 &&
+ chip->dt.cfg_s2_fifo_length <= MAX_FIFO_REGS) {
+ rc = qpnp_masked_write_base(chip, chip->base +
+ FIFO_LENGTH_REG, S2_FIFO_LENGTH_MASK,
+ chip->dt.cfg_s2_fifo_length
+ << S2_FIFO_LENGTH_SHIFT);
+ if (rc) {
+ pr_err("Unable to write s2 fifo length rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ get_sample_interval(chip, S1_STATE, &interval[0]);
+ get_sample_interval(chip, S2_STATE, &interval[1]);
+ get_sample_count(chip, S1_STATE, &count[0]);
+ get_sample_count(chip, S2_STATE, &count[1]);
+ get_fifo_length(chip, S1_STATE, &fifo[0]);
+ get_fifo_length(chip, S2_STATE, &fifo[1]);
+
+ /* Force the BMS state to S2 at boot-up */
+ rc = force_fsm_state(chip, S2_STATE);
+ if (rc) {
+ pr_err("Unable to force S2 state rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_read_wrapper(chip, &bms_en, chip->base + EN_CTL_REG, 1);
+ if (rc) {
+ pr_err("Unable to read BMS_EN state rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = update_fsm_state(chip);
+ if (rc) {
+ pr_err("Unable to read FSM state rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_info("BMS_EN=%d Sample_Interval-S1=[%d]S2=[%d] Sample_Count-S1=[%d]S2=[%d] Fifo_Length-S1=[%d]S2=[%d] FSM_state=%d\n",
+ !!bms_en, interval[0], interval[1], count[0],
+ count[1], fifo[0], fifo[1],
+ chip->current_fsm_state);
+
+ return 0;
+}
+
+static ssize_t vm_bms_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ int rc;
+ struct qpnp_bms_chip *chip = file->private_data;
+
+ if (!chip->data_ready && (file->f_flags & O_NONBLOCK)) {
+ rc = -EAGAIN;
+ goto fail_read;
+ }
+
+ rc = wait_event_interruptible(chip->bms_wait_q, chip->data_ready);
+ if (rc) {
+ pr_debug("wait failed! rc=%d\n", rc);
+ goto fail_read;
+ }
+
+ if (!chip->data_ready) {
+ pr_debug("No Data, false wakeup\n");
+ rc = -EFAULT;
+ goto fail_read;
+ }
+
+ mutex_lock(&chip->bms_data_mutex);
+
+ if (copy_to_user(buf, &chip->bms_data, sizeof(chip->bms_data))) {
+ pr_err("Failed in copy_to_user\n");
+ mutex_unlock(&chip->bms_data_mutex);
+ rc = -EFAULT;
+ goto fail_read;
+ }
+ pr_debug("Data copied!!\n");
+ chip->data_ready = 0;
+
+ mutex_unlock(&chip->bms_data_mutex);
+ /* wakelock-timeout for userspace to pick up */
+ pm_wakeup_event(chip->dev, BMS_READ_TIMEOUT);
+
+ return sizeof(chip->bms_data);
+
+fail_read:
+ pm_relax(chip->dev);
+ return rc;
+}
+
+static int vm_bms_open(struct inode *inode, struct file *file)
+{
+ struct qpnp_bms_chip *chip = container_of(inode->i_cdev,
+ struct qpnp_bms_chip, bms_cdev);
+
+ mutex_lock(&chip->bms_device_mutex);
+
+ if (chip->bms_dev_open) {
+ pr_debug("BMS device already open\n");
+ mutex_unlock(&chip->bms_device_mutex);
+ return -EBUSY;
+ }
+
+ chip->bms_dev_open = true;
+ file->private_data = chip;
+ pr_debug("BMS device opened\n");
+
+ mutex_unlock(&chip->bms_device_mutex);
+
+ return 0;
+}
+
+static int vm_bms_release(struct inode *inode, struct file *file)
+{
+ struct qpnp_bms_chip *chip = container_of(inode->i_cdev,
+ struct qpnp_bms_chip, bms_cdev);
+
+ mutex_lock(&chip->bms_device_mutex);
+
+ chip->bms_dev_open = false;
+ pm_relax(chip->dev);
+ pr_debug("BMS device closed\n");
+
+ mutex_unlock(&chip->bms_device_mutex);
+
+ return 0;
+}
+
+static const struct file_operations bms_fops = {
+ .owner = THIS_MODULE,
+ .open = vm_bms_open,
+ .read = vm_bms_read,
+ .release = vm_bms_release,
+};
+
+static void bms_init_defaults(struct qpnp_bms_chip *chip)
+{
+ chip->data_ready = 0;
+ chip->last_ocv_raw = OCV_UNINITIALIZED;
+ chip->battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
+ chip->battery_present = -EINVAL;
+ chip->calculated_soc = -EINVAL;
+ chip->last_soc = -EINVAL;
+ chip->vbms_lv_wake_source.disabled = 1;
+ chip->vbms_cv_wake_source.disabled = 1;
+ chip->vbms_soc_wake_source.disabled = 1;
+ chip->ocv_at_100 = -EINVAL;
+ chip->prev_soc_uuc = -EINVAL;
+ chip->charge_cycles = 0;
+ chip->start_soc = 0;
+ chip->end_soc = 0;
+ chip->charge_increase = 0;
+}
+
+#define REQUEST_IRQ(chip, rc, irq_name) \
+do { \
+ rc = devm_request_threaded_irq(chip->dev, \
+ chip->irq_name##_irq.irq, NULL, \
+ bms_##irq_name##_irq_handler, \
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT, \
+ #irq_name, chip); \
+ if (rc < 0) \
+ pr_err("Unable to request " #irq_name " irq: %d\n", rc);\
+} while (0)
+
+#define FIND_IRQ(chip, pdev, irq_name, rc) \
+do { \
+ chip->irq_name##_irq.irq = of_irq_get_byname(child, \
+ #irq_name); \
+ if (chip->irq_name##_irq.irq < 0) { \
+ rc = chip->irq_name##_irq.irq; \
+ pr_err("Unable to get " #irq_name " irq rc=%d\n", rc); \
+ } \
+} while (0)
+
+static int bms_request_irqs(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ REQUEST_IRQ(chip, rc, fifo_update_done);
+ if (rc < 0)
+ return rc;
+
+ REQUEST_IRQ(chip, rc, fsm_state_change);
+ if (rc < 0)
+ return rc;
+
+ /* Disable the state change IRQ */
+ disable_bms_irq(&chip->fsm_state_change_irq);
+ enable_irq_wake(chip->fifo_update_done_irq.irq);
+
+ return 0;
+}
+
+static int bms_find_irqs(struct qpnp_bms_chip *chip, struct device_node *child)
+{
+ int rc = 0;
+
+ FIND_IRQ(chip, child, fifo_update_done, rc);
+ if (rc < 0)
+ return rc;
+ FIND_IRQ(chip, child, fsm_state_change, rc);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+
+static int64_t read_battery_id(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ struct qpnp_vadc_result result;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX2_BAT_ID, &result);
+ if (rc) {
+ pr_err("error reading batt id channel = %d, rc = %d\n",
+ LR_MUX2_BAT_ID, rc);
+ return rc;
+ }
+
+ return result.physical;
+}
+
+static int show_bms_config(struct seq_file *m, void *data)
+{
+ struct qpnp_bms_chip *chip = m->private;
+ int s1_sample_interval, s2_sample_interval;
+ int s1_sample_count, s2_sample_count;
+ int s1_fifo_length, s2_fifo_length;
+
+ get_sample_interval(chip, S1_STATE, &s1_sample_interval);
+ get_sample_interval(chip, S2_STATE, &s2_sample_interval);
+ get_sample_count(chip, S1_STATE, &s1_sample_count);
+ get_sample_count(chip, S2_STATE, &s2_sample_count);
+ get_fifo_length(chip, S1_STATE, &s1_fifo_length);
+ get_fifo_length(chip, S2_STATE, &s2_fifo_length);
+
+ seq_printf(m, "r_conn_mohm\t=\t%d\n"
+ "v_cutoff_uv\t=\t%d\n"
+ "max_voltage_uv\t=\t%d\n"
+ "use_voltage_soc\t=\t%d\n"
+ "low_soc_calc_threshold\t=\t%d\n"
+ "low_soc_calculate_soc_ms\t=\t%d\n"
+ "low_voltage_threshold\t=\t%d\n"
+ "low_voltage_calculate_soc_ms\t=\t%d\n"
+ "calculate_soc_ms\t=\t%d\n"
+ "voltage_soc_timeout_ms\t=\t%d\n"
+ "ignore_shutdown_soc\t=\t%d\n"
+ "shutdown_soc_valid_limit\t=\t%d\n"
+ "force_s3_on_suspend\t=\t%d\n"
+ "report_charger_eoc\t=\t%d\n"
+ "aging_compensation\t=\t%d\n"
+ "use_reported_soc\t=\t%d\n"
+ "s1_sample_interval_ms\t=\t%d\n"
+ "s2_sample_interval_ms\t=\t%d\n"
+ "s1_sample_count\t=\t%d\n"
+ "s2_sample_count\t=\t%d\n"
+ "s1_fifo_length\t=\t%d\n"
+ "s2_fifo_length\t=\t%d\n",
+ chip->dt.cfg_r_conn_mohm,
+ chip->dt.cfg_v_cutoff_uv,
+ chip->dt.cfg_max_voltage_uv,
+ chip->dt.cfg_use_voltage_soc,
+ chip->dt.cfg_low_soc_calc_threshold,
+ chip->dt.cfg_low_soc_calculate_soc_ms,
+ chip->dt.cfg_low_voltage_threshold,
+ chip->dt.cfg_low_voltage_calculate_soc_ms,
+ chip->dt.cfg_calculate_soc_ms,
+ chip->dt.cfg_voltage_soc_timeout_ms,
+ chip->dt.cfg_ignore_shutdown_soc,
+ chip->dt.cfg_shutdown_soc_valid_limit,
+ chip->dt.cfg_force_s3_on_suspend,
+ chip->dt.cfg_report_charger_eoc,
+ chip->dt.cfg_battery_aging_comp,
+ chip->dt.cfg_use_reported_soc,
+ s1_sample_interval,
+ s2_sample_interval,
+ s1_sample_count,
+ s2_sample_count,
+ s1_fifo_length,
+ s2_fifo_length);
+
+ return 0;
+}
+
+static int bms_config_open(struct inode *inode, struct file *file)
+{
+ struct qpnp_bms_chip *chip = inode->i_private;
+
+ return single_open(file, show_bms_config, chip);
+}
+
+static const struct file_operations bms_config_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = bms_config_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int show_bms_status(struct seq_file *m, void *data)
+{
+ struct qpnp_bms_chip *chip = m->private;
+
+ seq_printf(m, "bms_psy_registered\t=\t%d\n"
+ "bms_dev_open\t=\t%d\n"
+ "warm_reset\t=\t%d\n"
+ "battery_status\t=\t%d\n"
+ "battery_present\t=\t%d\n"
+ "in_cv_state\t=\t%d\n"
+ "calculated_soc\t=\t%d\n"
+ "last_soc\t=\t%d\n"
+ "last_ocv_uv\t=\t%d\n"
+ "last_ocv_raw\t=\t%d\n"
+ "last_soc_unbound\t=\t%d\n"
+ "current_fsm_state\t=\t%d\n"
+ "current_now\t=\t%d\n"
+ "ocv_at_100\t=\t%d\n"
+ "low_voltage_ws_active\t=\t%d\n"
+ "cv_ws_active\t=\t%d\n",
+ chip->bms_psy_registered,
+ chip->bms_dev_open,
+ chip->warm_reset,
+ chip->battery_status,
+ chip->battery_present,
+ chip->in_cv_state,
+ chip->calculated_soc,
+ chip->last_soc,
+ chip->last_ocv_uv,
+ chip->last_ocv_raw,
+ chip->last_soc_unbound,
+ chip->current_fsm_state,
+ chip->current_now,
+ chip->ocv_at_100,
+ bms_wake_active(&chip->vbms_lv_wake_source),
+ bms_wake_active(&chip->vbms_cv_wake_source));
+ return 0;
+}
+
+static int bms_status_open(struct inode *inode, struct file *file)
+{
+ struct qpnp_bms_chip *chip = inode->i_private;
+
+ return single_open(file, show_bms_status, chip);
+}
+
+static const struct file_operations bms_status_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = bms_status_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int show_bms_data(struct seq_file *m, void *data)
+{
+ struct qpnp_bms_chip *chip = m->private;
+ int i;
+
+ mutex_lock(&chip->bms_data_mutex);
+
+ seq_printf(m, "seq_num=%d\n", chip->bms_data.seq_num);
+ for (i = 0; i < chip->bms_data.num_fifo; i++)
+ seq_printf(m, "fifo_uv[%d]=%d sample_count=%d interval_ms=%d\n",
+ i, chip->bms_data.fifo_uv[i],
+ chip->bms_data.sample_count,
+ chip->bms_data.sample_interval_ms);
+ seq_printf(m, "acc_uv=%d sample_count=%d sample_interval=%d\n",
+ chip->bms_data.acc_uv, chip->bms_data.acc_count,
+ chip->bms_data.sample_interval_ms);
+
+ mutex_unlock(&chip->bms_data_mutex);
+
+ return 0;
+}
+
+static int bms_data_open(struct inode *inode, struct file *file)
+{
+ struct qpnp_bms_chip *chip = inode->i_private;
+
+ return single_open(file, show_bms_data, chip);
+}
+
+static const struct file_operations bms_data_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = bms_data_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int set_battery_data(struct qpnp_bms_chip *chip)
+{
+ int64_t battery_id;
+ int rc = 0;
+ struct bms_battery_data *batt_data;
+ struct device_node *node;
+
+ battery_id = read_battery_id(chip);
+ if (battery_id < 0) {
+ pr_err("cannot read battery id err = %lld\n", battery_id);
+ return battery_id;
+ }
+ node = of_find_node_by_name(chip->pdev->dev.of_node,
+ "qcom,battery-data");
+ if (!node) {
+ pr_err("No available batterydata\n");
+ return -EINVAL;
+ }
+
+ batt_data = devm_kzalloc(chip->dev,
+ sizeof(struct bms_battery_data), GFP_KERNEL);
+ if (!batt_data)
+ return -EINVAL;
+
+ batt_data->fcc_temp_lut = devm_kzalloc(chip->dev,
+ sizeof(struct single_row_lut), GFP_KERNEL);
+ batt_data->pc_temp_ocv_lut = devm_kzalloc(chip->dev,
+ sizeof(struct pc_temp_ocv_lut), GFP_KERNEL);
+ batt_data->rbatt_sf_lut = devm_kzalloc(chip->dev,
+ sizeof(struct sf_lut), GFP_KERNEL);
+ batt_data->ibat_acc_lut = devm_kzalloc(chip->dev,
+ sizeof(struct ibat_temp_acc_lut), GFP_KERNEL);
+
+ batt_data->max_voltage_uv = -1;
+ batt_data->cutoff_uv = -1;
+ batt_data->iterm_ua = -1;
+
+ /*
+ * if the alloced luts are 0s, of_batterydata_read_data ignores
+ * them.
+ */
+ rc = of_batterydata_read_data(node, batt_data, battery_id);
+ if (rc || !batt_data->pc_temp_ocv_lut
+ || !batt_data->fcc_temp_lut
+ || !batt_data->rbatt_sf_lut
+ || !batt_data->ibat_acc_lut) {
+ pr_err("battery data load failed\n");
+ devm_kfree(chip->dev, batt_data->fcc_temp_lut);
+ devm_kfree(chip->dev, batt_data->pc_temp_ocv_lut);
+ devm_kfree(chip->dev, batt_data->rbatt_sf_lut);
+ devm_kfree(chip->dev, batt_data->ibat_acc_lut);
+ devm_kfree(chip->dev, batt_data);
+ return rc;
+ }
+
+ if (batt_data->pc_temp_ocv_lut == NULL) {
+ pr_err("temp ocv lut table has not been loaded\n");
+ devm_kfree(chip->dev, batt_data->fcc_temp_lut);
+ devm_kfree(chip->dev, batt_data->pc_temp_ocv_lut);
+ devm_kfree(chip->dev, batt_data->rbatt_sf_lut);
+ devm_kfree(chip->dev, batt_data->ibat_acc_lut);
+ devm_kfree(chip->dev, batt_data);
+
+ return -EINVAL;
+ }
+
+ /* check if ibat_acc_lut is valid */
+ if (!batt_data->ibat_acc_lut->rows) {
+ pr_info("ibat_acc_lut not present\n");
+ devm_kfree(chip->dev, batt_data->ibat_acc_lut);
+ batt_data->ibat_acc_lut = NULL;
+ }
+
+ /* Override battery properties if specified in the battery profile */
+ if (batt_data->max_voltage_uv >= 0)
+ chip->dt.cfg_max_voltage_uv = batt_data->max_voltage_uv;
+ if (batt_data->cutoff_uv >= 0)
+ chip->dt.cfg_v_cutoff_uv = batt_data->cutoff_uv;
+
+ chip->batt_data = batt_data;
+
+ return 0;
+}
+
+static int parse_pdev_dt_properties(struct qpnp_bms_chip *chip,
+ struct platform_device *pdev)
+{
+ struct device_node *child;
+ int rc;
+ unsigned int base;
+
+ chip->dev = &(pdev->dev);
+ chip->pdev = pdev;
+
+ if (of_get_available_child_count(pdev->dev.of_node) == 0) {
+ pr_err("no child nodes found\n");
+ return -ENXIO;
+ }
+
+
+ for_each_available_child_of_node(pdev->dev.of_node, child) {
+ rc = of_property_read_u32(child, "reg", &base);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Couldn't find reg in node = %s rc = %d\n",
+ child->full_name, rc);
+ return -ENXIO;
+ }
+
+ pr_debug("Node name = %s\n", child->name);
+
+ if (strcmp("qcom,batt-pres-status",
+ child->name) == 0) {
+ chip->batt_pres_addr = base;
+ continue;
+ }
+
+ if (strcmp("qcom,qpnp-chg-pres",
+ child->name) == 0) {
+ chip->chg_pres_addr = base;
+ continue;
+ }
+
+ chip->base = base;
+ rc = bms_find_irqs(chip, child);
+ if (rc) {
+ pr_err("Could not find irqs rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->base == 0) {
+ dev_err(&pdev->dev, "BMS peripheral was not registered\n");
+ return -EINVAL;
+ }
+
+ pr_debug("bms-base=0x%04x bat-pres-reg=0x%04x qpnp-chg-pres=0x%04x\n",
+ chip->base, chip->batt_pres_addr, chip->chg_pres_addr);
+
+ return 0;
+}
+
+#define PROP_READ(chip_prop, qpnp_pdev_property, retval) \
+do { \
+ if (retval) \
+ break; \
+ retval = of_property_read_u32(chip->pdev->dev.of_node, \
+ "qcom," qpnp_pdev_property, \
+ &chip->dt.chip_prop); \
+ if (retval) { \
+ pr_err("Error reading " #qpnp_pdev_property \
+ " property %d\n", retval); \
+ } \
+} while (0)
+
+#define PROP_READ_OPTIONAL(chip_prop, qpnp_pdev_property, retval) \
+do { \
+ retval = of_property_read_u32(chip->pdev->dev.of_node, \
+ "qcom," qpnp_pdev_property, \
+ &chip->dt.chip_prop); \
+ if (retval) \
+ chip->dt.chip_prop = -EINVAL; \
+} while (0)
+
+static int parse_bms_dt_properties(struct qpnp_bms_chip *chip)
+{
+ int rc = 0;
+
+ PROP_READ(cfg_v_cutoff_uv, "v-cutoff-uv", rc);
+ PROP_READ(cfg_max_voltage_uv, "max-voltage-uv", rc);
+ PROP_READ(cfg_r_conn_mohm, "r-conn-mohm", rc);
+ PROP_READ(cfg_shutdown_soc_valid_limit,
+ "shutdown-soc-valid-limit", rc);
+ PROP_READ(cfg_low_soc_calc_threshold,
+ "low-soc-calculate-soc-threshold", rc);
+ PROP_READ(cfg_low_soc_calculate_soc_ms,
+ "low-soc-calculate-soc-ms", rc);
+ PROP_READ(cfg_low_voltage_calculate_soc_ms,
+ "low-voltage-calculate-soc-ms", rc);
+ PROP_READ(cfg_calculate_soc_ms, "calculate-soc-ms", rc);
+ PROP_READ(cfg_low_voltage_threshold, "low-voltage-threshold", rc);
+ PROP_READ(cfg_voltage_soc_timeout_ms,
+ "volatge-soc-timeout-ms", rc);
+
+ if (rc) {
+ pr_err("Missing required properties rc=%d\n", rc);
+ return rc;
+ }
+
+ PROP_READ_OPTIONAL(cfg_s1_sample_interval_ms,
+ "s1-sample-interval-ms", rc);
+ PROP_READ_OPTIONAL(cfg_s2_sample_interval_ms,
+ "s2-sample-interval-ms", rc);
+ PROP_READ_OPTIONAL(cfg_s1_sample_count, "s1-sample-count", rc);
+ PROP_READ_OPTIONAL(cfg_s2_sample_count, "s2-sample-count", rc);
+ PROP_READ_OPTIONAL(cfg_s1_fifo_length, "s1-fifo-length", rc);
+ PROP_READ_OPTIONAL(cfg_s2_fifo_length, "s2-fifo-length", rc);
+ PROP_READ_OPTIONAL(cfg_s3_ocv_tol_uv, "s3-ocv-tolerence-uv", rc);
+ PROP_READ_OPTIONAL(cfg_low_soc_fifo_length,
+ "low-soc-fifo-length", rc);
+ PROP_READ_OPTIONAL(cfg_soc_resume_limit, "resume-soc", rc);
+ PROP_READ_OPTIONAL(cfg_low_temp_threshold,
+ "low-temp-threshold", rc);
+ if (rc)
+ chip->dt.cfg_low_temp_threshold = 0;
+
+ PROP_READ_OPTIONAL(cfg_ibat_avg_samples,
+ "ibat-avg-samples", rc);
+ if (rc || (chip->dt.cfg_ibat_avg_samples <= 0) ||
+ (chip->dt.cfg_ibat_avg_samples > IAVG_SAMPLES))
+ chip->dt.cfg_ibat_avg_samples = IAVG_SAMPLES;
+
+ chip->dt.cfg_ignore_shutdown_soc = of_property_read_bool(
+ chip->pdev->dev.of_node, "qcom,ignore-shutdown-soc");
+ chip->dt.cfg_use_voltage_soc = of_property_read_bool(
+ chip->pdev->dev.of_node, "qcom,use-voltage-soc");
+ chip->dt.cfg_force_s3_on_suspend = of_property_read_bool(
+ chip->pdev->dev.of_node, "qcom,force-s3-on-suspend");
+ chip->dt.cfg_report_charger_eoc = of_property_read_bool(
+ chip->pdev->dev.of_node, "qcom,report-charger-eoc");
+ chip->dt.cfg_disable_bms = of_property_read_bool(
+ chip->pdev->dev.of_node, "qcom,disable-bms");
+ chip->dt.cfg_force_bms_active_on_charger = of_property_read_bool(
+ chip->pdev->dev.of_node,
+ "qcom,force-bms-active-on-charger");
+ chip->dt.cfg_battery_aging_comp = of_property_read_bool(
+ chip->pdev->dev.of_node, "qcom,batt-aging-comp");
+ chip->dt.cfg_use_reported_soc = of_property_read_bool(
+ chip->pdev->dev.of_node, "qcom,use-reported-soc");
+ pr_debug("v_cutoff_uv=%d, max_v=%d\n", chip->dt.cfg_v_cutoff_uv,
+ chip->dt.cfg_max_voltage_uv);
+ pr_debug("r_conn=%d shutdown_soc_valid_limit=%d low_temp_threshold=%d ibat_avg_samples=%d\n",
+ chip->dt.cfg_r_conn_mohm,
+ chip->dt.cfg_shutdown_soc_valid_limit,
+ chip->dt.cfg_low_temp_threshold,
+ chip->dt.cfg_ibat_avg_samples);
+ pr_debug("ignore_shutdown_soc=%d, use_voltage_soc=%d low_soc_fifo_length=%d\n",
+ chip->dt.cfg_ignore_shutdown_soc,
+ chip->dt.cfg_use_voltage_soc,
+ chip->dt.cfg_low_soc_fifo_length);
+ pr_debug("force-s3-on-suspend=%d report-charger-eoc=%d disable-bms=%d disable-suspend-on-usb=%d aging_compensation=%d\n",
+ chip->dt.cfg_force_s3_on_suspend,
+ chip->dt.cfg_report_charger_eoc,
+ chip->dt.cfg_disable_bms,
+ chip->dt.cfg_force_bms_active_on_charger,
+ chip->dt.cfg_battery_aging_comp);
+ pr_debug("use-reported-soc is %d\n",
+ chip->dt.cfg_use_reported_soc);
+
+ return 0;
+}
+
+static int bms_get_adc(struct qpnp_bms_chip *chip,
+ struct platform_device *pdev)
+{
+ int rc = 0;
+
+ chip->vadc_dev = qpnp_get_vadc(&pdev->dev, "bms");
+ if (IS_ERR(chip->vadc_dev)) {
+ rc = PTR_ERR(chip->vadc_dev);
+ if (rc == -EPROBE_DEFER)
+ pr_err("vadc not found - defer probe rc=%d\n", rc);
+ else
+ pr_err("vadc property missing, rc=%d\n", rc);
+
+ return rc;
+ }
+
+ chip->adc_tm_dev = qpnp_get_adc_tm(&pdev->dev, "bms");
+ if (IS_ERR(chip->adc_tm_dev)) {
+ rc = PTR_ERR(chip->adc_tm_dev);
+ if (rc == -EPROBE_DEFER)
+ pr_err("adc-tm not found - defer probe rc=%d\n", rc);
+ else
+ pr_err("adc-tm property missing, rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
+static int register_bms_char_device(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ rc = alloc_chrdev_region(&chip->dev_no, 0, 1, "vm_bms");
+ if (rc) {
+ pr_err("Unable to allocate chrdev rc=%d\n", rc);
+ return rc;
+ }
+ cdev_init(&chip->bms_cdev, &bms_fops);
+ rc = cdev_add(&chip->bms_cdev, chip->dev_no, 1);
+ if (rc) {
+ pr_err("Unable to add bms_cdev rc=%d\n", rc);
+ goto unregister_chrdev;
+ }
+
+ chip->bms_class = class_create(THIS_MODULE, "vm_bms");
+ if (IS_ERR_OR_NULL(chip->bms_class)) {
+ pr_err("Fail to create bms class\n");
+ rc = -EINVAL;
+ goto delete_cdev;
+ }
+ chip->bms_device = device_create(chip->bms_class,
+ NULL, chip->dev_no,
+ NULL, "vm_bms");
+ if (IS_ERR(chip->bms_device)) {
+ pr_err("Fail to create bms_device device\n");
+ rc = -EINVAL;
+ goto delete_cdev;
+ }
+
+ return 0;
+
+delete_cdev:
+ cdev_del(&chip->bms_cdev);
+unregister_chrdev:
+ unregister_chrdev_region(chip->dev_no, 1);
+ return rc;
+}
+
+static int qpnp_vm_bms_probe(struct platform_device *pdev)
+{
+ struct qpnp_bms_chip *chip;
+ struct device_node *revid_dev_node;
+ struct power_supply_config bms_psy_cfg;
+ int rc, vbatt = 0;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!chip->regmap) {
+ dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+ return -EINVAL;
+ }
+
+ rc = bms_get_adc(chip, pdev);
+ if (rc < 0) {
+ pr_err("Failed to get adc rc=%d\n", rc);
+ return rc;
+ }
+
+ revid_dev_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,pmic-revid", 0);
+ if (!revid_dev_node) {
+ pr_err("Missing qcom,pmic-revid property\n");
+ return -EINVAL;
+ }
+
+ chip->revid_data = get_revid_data(revid_dev_node);
+ if (IS_ERR(chip->revid_data)) {
+ pr_err("revid error rc = %ld\n", PTR_ERR(chip->revid_data));
+ return -EINVAL;
+ }
+ if ((chip->revid_data->pmic_subtype == PM8916_SUBTYPE) &&
+ chip->revid_data->rev4 == PM8916_V2P0_REV4)
+ chip->workaround_flag |= WRKARND_PON_OCV_COMP;
+
+ rc = qpnp_pon_is_warm_reset();
+ if (rc < 0) {
+ pr_err("Error reading warm reset status rc=%d\n", rc);
+ return rc;
+ }
+ chip->warm_reset = !!rc;
+
+ rc = parse_pdev_dt_properties(chip, pdev);
+ if (rc) {
+ pr_err("Error registering pdev resource rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = parse_bms_dt_properties(chip);
+ if (rc) {
+ pr_err("Unable to read all bms properties, rc = %d\n", rc);
+ return rc;
+ }
+
+ if (chip->dt.cfg_disable_bms) {
+ pr_info("VMBMS disabled (disable-bms = 1)\n");
+ rc = qpnp_masked_write_base(chip, chip->base + EN_CTL_REG,
+ BMS_EN_BIT, 0);
+ if (rc)
+ pr_err("Unable to disable VMBMS rc=%d\n", rc);
+ return -ENODEV;
+ }
+
+ rc = qpnp_read_wrapper(chip, chip->revision,
+ chip->base + REVISION1_REG, 2);
+ if (rc) {
+ pr_err("Error reading version register rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_debug("BMS version: %hhu.%hhu\n",
+ chip->revision[1], chip->revision[0]);
+
+ dev_set_drvdata(&pdev->dev, chip);
+ device_init_wakeup(&pdev->dev, 1);
+ mutex_init(&chip->bms_data_mutex);
+ mutex_init(&chip->bms_device_mutex);
+ mutex_init(&chip->last_soc_mutex);
+ mutex_init(&chip->state_change_mutex);
+ init_waitqueue_head(&chip->bms_wait_q);
+
+ /* read battery-id and select the battery profile */
+ rc = set_battery_data(chip);
+ if (rc) {
+ pr_err("Unable to read battery data %d\n", rc);
+ goto fail_init;
+ }
+
+ /* set the battery profile */
+ rc = config_battery_data(chip->batt_data);
+ if (rc) {
+ pr_err("Unable to config battery data %d\n", rc);
+ goto fail_init;
+ }
+
+ wakeup_source_init(&chip->vbms_lv_wake_source.source, "vbms_lv_wake");
+ wakeup_source_init(&chip->vbms_cv_wake_source.source, "vbms_cv_wake");
+ wakeup_source_init(&chip->vbms_soc_wake_source.source, "vbms_soc_wake");
+ INIT_DELAYED_WORK(&chip->monitor_soc_work, monitor_soc_work);
+ INIT_DELAYED_WORK(&chip->voltage_soc_timeout_work,
+ voltage_soc_timeout_work);
+
+ bms_init_defaults(chip);
+ bms_load_hw_defaults(chip);
+
+ if (is_battery_present(chip)) {
+ rc = setup_vbat_monitoring(chip);
+ if (rc) {
+ pr_err("fail to configure vbat monitoring rc=%d\n",
+ rc);
+ goto fail_setup;
+ }
+ }
+
+ rc = bms_request_irqs(chip);
+ if (rc) {
+ pr_err("error requesting bms irqs, rc = %d\n", rc);
+ goto fail_irq;
+ }
+
+ battery_insertion_check(chip);
+ battery_status_check(chip);
+
+ /* character device to pass data to the userspace */
+ rc = register_bms_char_device(chip);
+ if (rc) {
+ pr_err("Unable to regiter '/dev/vm_bms' rc=%d\n", rc);
+ goto fail_bms_device;
+ }
+
+ the_chip = chip;
+ calculate_initial_soc(chip);
+ if (chip->dt.cfg_battery_aging_comp) {
+ rc = calculate_initial_aging_comp(chip);
+ if (rc)
+ pr_err("Unable to calculate initial aging data rc=%d\n",
+ rc);
+ }
+
+ /* setup & register the battery power supply */
+ chip->bms_psy_d.name = "bms";
+ chip->bms_psy_d.type = POWER_SUPPLY_TYPE_BMS;
+ chip->bms_psy_d.properties = bms_power_props;
+ chip->bms_psy_d.num_properties = ARRAY_SIZE(bms_power_props);
+ chip->bms_psy_d.get_property = qpnp_vm_bms_power_get_property;
+ chip->bms_psy_d.set_property = qpnp_vm_bms_power_set_property;
+ chip->bms_psy_d.external_power_changed = qpnp_vm_bms_ext_power_changed;
+ chip->bms_psy_d.property_is_writeable =
+ qpnp_vm_bms_property_is_writeable;
+
+ bms_psy_cfg.supplied_to = qpnp_vm_bms_supplicants;
+ bms_psy_cfg.num_supplicants = ARRAY_SIZE(qpnp_vm_bms_supplicants);
+ bms_psy_cfg.drv_data = chip;
+ bms_psy_cfg.of_node = NULL;
+
+ chip->bms_psy = devm_power_supply_register(chip->dev,
+ &chip->bms_psy_d,
+ &bms_psy_cfg);
+ if (IS_ERR(chip->bms_psy)) {
+ pr_err("power_supply_register bms failed rc = %ld\n",
+ PTR_ERR(chip->bms_psy));
+ goto fail_psy;
+ }
+ chip->bms_psy_registered = true;
+
+ rc = get_battery_voltage(chip, &vbatt);
+ if (rc) {
+ pr_err("error reading vbat_sns adc channel=%d, rc=%d\n",
+ VBAT_SNS, rc);
+ goto fail_get_vtg;
+ }
+
+ chip->debug_root = debugfs_create_dir("qpnp_vmbms", NULL);
+ if (!chip->debug_root)
+ pr_err("Couldn't create debug dir\n");
+
+ if (chip->debug_root) {
+ struct dentry *ent;
+
+ ent = debugfs_create_file("bms_data", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &bms_data_debugfs_ops);
+ if (!ent)
+ pr_err("Couldn't create bms_data debug file\n");
+
+ ent = debugfs_create_file("bms_config", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &bms_config_debugfs_ops);
+ if (!ent)
+ pr_err("Couldn't create bms_config debug file\n");
+
+ ent = debugfs_create_file("bms_status", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &bms_status_debugfs_ops);
+ if (!ent)
+ pr_err("Couldn't create bms_status debug file\n");
+ }
+
+ schedule_delayed_work(&chip->monitor_soc_work, 0);
+
+ /*
+ * schedule a work to check if the userspace vmbms module
+ * has registered. Fall-back to voltage-based-soc reporting
+ * if it has not.
+ */
+ schedule_delayed_work(&chip->voltage_soc_timeout_work,
+ msecs_to_jiffies(chip->dt.cfg_voltage_soc_timeout_ms));
+
+ pr_info("probe success: soc=%d vbatt=%d ocv=%d warm_reset=%d\n",
+ get_prop_bms_capacity(chip), vbatt,
+ chip->last_ocv_uv, chip->warm_reset);
+
+ return rc;
+
+fail_get_vtg:
+ power_supply_unregister(chip->bms_psy);
+fail_psy:
+ device_destroy(chip->bms_class, chip->dev_no);
+ cdev_del(&chip->bms_cdev);
+ unregister_chrdev_region(chip->dev_no, 1);
+fail_bms_device:
+ chip->bms_psy_registered = false;
+fail_irq:
+ reset_vbat_monitoring(chip);
+fail_setup:
+ wakeup_source_trash(&chip->vbms_lv_wake_source.source);
+ wakeup_source_trash(&chip->vbms_cv_wake_source.source);
+ wakeup_source_trash(&chip->vbms_soc_wake_source.source);
+fail_init:
+ mutex_destroy(&chip->bms_data_mutex);
+ mutex_destroy(&chip->last_soc_mutex);
+ mutex_destroy(&chip->state_change_mutex);
+ mutex_destroy(&chip->bms_device_mutex);
+ the_chip = NULL;
+
+ return rc;
+}
+
+static int qpnp_vm_bms_remove(struct platform_device *pdev)
+{
+ struct qpnp_bms_chip *chip = dev_get_drvdata(&pdev->dev);
+
+ cancel_delayed_work_sync(&chip->monitor_soc_work);
+ debugfs_remove_recursive(chip->debug_root);
+ device_destroy(chip->bms_class, chip->dev_no);
+ cdev_del(&chip->bms_cdev);
+ unregister_chrdev_region(chip->dev_no, 1);
+ reset_vbat_monitoring(chip);
+ wakeup_source_trash(&chip->vbms_lv_wake_source.source);
+ wakeup_source_trash(&chip->vbms_cv_wake_source.source);
+ wakeup_source_trash(&chip->vbms_soc_wake_source.source);
+ mutex_destroy(&chip->bms_data_mutex);
+ mutex_destroy(&chip->last_soc_mutex);
+ mutex_destroy(&chip->state_change_mutex);
+ mutex_destroy(&chip->bms_device_mutex);
+ power_supply_unregister(chip->bms_psy);
+ dev_set_drvdata(&pdev->dev, NULL);
+ the_chip = NULL;
+
+ return 0;
+}
+
+static void process_suspend_data(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ mutex_lock(&chip->bms_data_mutex);
+
+ chip->suspend_data_valid = false;
+
+ memset(&chip->bms_data, 0, sizeof(chip->bms_data));
+
+ rc = read_and_populate_fifo_data(chip);
+ if (rc)
+ pr_err("Unable to read FIFO data rc=%d\n", rc);
+
+ rc = read_and_populate_acc_data(chip);
+ if (rc)
+ pr_err("Unable to read ACC_SD data rc=%d\n", rc);
+
+ rc = clear_fifo_acc_data(chip);
+ if (rc)
+ pr_err("Unable to clear FIFO/ACC data rc=%d\n", rc);
+
+ if (chip->bms_data.num_fifo || chip->bms_data.acc_count) {
+ pr_debug("suspend data valid\n");
+ chip->suspend_data_valid = true;
+ }
+
+ mutex_unlock(&chip->bms_data_mutex);
+}
+
+static void process_resume_data(struct qpnp_bms_chip *chip)
+{
+ int rc, batt_temp = 0;
+ int old_ocv = 0;
+ bool ocv_updated = false;
+
+ rc = get_batt_therm(chip, &batt_temp);
+ if (rc < 0) {
+ pr_err("Unable to read batt temp, using default=%d\n",
+ BMS_DEFAULT_TEMP);
+ batt_temp = BMS_DEFAULT_TEMP;
+ }
+
+ mutex_lock(&chip->bms_data_mutex);
+ /*
+ * We can get a h/w OCV update when the sleep_b
+ * is low, which is possible when APPS is suspended.
+ * So check for an OCV update only in bms_resume
+ */
+ old_ocv = chip->last_ocv_uv;
+ rc = read_and_update_ocv(chip, batt_temp, false);
+ if (rc)
+ pr_err("Unable to read/upadate OCV rc=%d\n", rc);
+
+ if (old_ocv != chip->last_ocv_uv) {
+ ocv_updated = true;
+ /* new OCV, clear suspended data */
+ chip->suspend_data_valid = false;
+ memset(&chip->bms_data, 0, sizeof(chip->bms_data));
+ chip->calculated_soc = lookup_soc_ocv(chip,
+ chip->last_ocv_uv, batt_temp);
+ pr_debug("OCV in sleep SOC=%d\n", chip->calculated_soc);
+ chip->last_soc_unbound = true;
+ chip->voltage_soc_uv = chip->last_ocv_uv;
+ pr_debug("update bms_psy\n");
+ power_supply_changed(chip->bms_psy);
+ }
+
+ if (ocv_updated || chip->suspend_data_valid) {
+ /* there is data to be sent */
+ pr_debug("ocv_updated=%d suspend_data_valid=%d\n",
+ ocv_updated, chip->suspend_data_valid);
+ chip->bms_data.seq_num = chip->seq_num++;
+ dump_bms_data(__func__, chip);
+
+ chip->data_ready = 1;
+ wake_up_interruptible(&chip->bms_wait_q);
+ if (chip->bms_dev_open)
+ pm_stay_awake(chip->dev);
+
+ }
+ chip->suspend_data_valid = false;
+ mutex_unlock(&chip->bms_data_mutex);
+}
+
+static int bms_suspend(struct device *dev)
+{
+ struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
+ bool battery_charging = is_battery_charging(chip);
+ bool hi_power_state = is_hi_power_state_requested(chip);
+ bool charger_present = is_charger_present(chip);
+ bool bms_suspend_config;
+
+ /*
+ * Keep BMS FSM active if 'cfg_force_bms_active_on_charger' property
+ * is present and charger inserted. This ensures that recharge
+ * starts once battery SOC falls below resume_soc.
+ */
+ bms_suspend_config = chip->dt.cfg_force_bms_active_on_charger
+ && charger_present;
+
+ chip->apply_suspend_config = false;
+ if (!battery_charging && !hi_power_state && !bms_suspend_config)
+ chip->apply_suspend_config = true;
+
+ pr_debug("battery_charging=%d power_state=%s hi_power_state=0x%x apply_suspend_config=%d bms_suspend_config=%d usb_present=%d\n",
+ battery_charging, hi_power_state ? "hi" : "low",
+ chip->hi_power_state,
+ chip->apply_suspend_config, bms_suspend_config,
+ charger_present);
+
+ if (chip->apply_suspend_config) {
+ if (chip->dt.cfg_force_s3_on_suspend) {
+ disable_bms_irq(&chip->fifo_update_done_irq);
+ pr_debug("Forcing S3 state\n");
+ mutex_lock(&chip->state_change_mutex);
+ force_fsm_state(chip, S3_STATE);
+ mutex_unlock(&chip->state_change_mutex);
+ /* Store accumulated data if any */
+ process_suspend_data(chip);
+ }
+ }
+
+ cancel_delayed_work_sync(&chip->monitor_soc_work);
+
+ return 0;
+}
+
+static int bms_resume(struct device *dev)
+{
+ u8 state = 0;
+ int rc, monitor_soc_delay = 0;
+ unsigned long tm_now_sec;
+ struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
+
+ if (chip->apply_suspend_config) {
+ if (chip->dt.cfg_force_s3_on_suspend) {
+ /*
+ * Update the state to S2 only if we are in S3. There is
+ * a possibility of being in S2 if we resumed on
+ * a charger insertion
+ */
+ mutex_lock(&chip->state_change_mutex);
+ rc = get_fsm_state(chip, &state);
+ if (rc)
+ pr_err("Unable to get FSM state rc=%d\n", rc);
+ if (rc || (state == S3_STATE)) {
+ pr_debug("Unforcing S3 state, setting S2 state\n");
+ force_fsm_state(chip, S2_STATE);
+ }
+ mutex_unlock(&chip->state_change_mutex);
+ enable_bms_irq(&chip->fifo_update_done_irq);
+ /*
+ * if we were charging while suspended, we will
+ * be woken up by the fifo done interrupt and no
+ * additional processing is needed.
+ */
+ process_resume_data(chip);
+ }
+ }
+
+ /* Start monitor_soc_work based on when it last executed */
+ rc = get_current_time(&tm_now_sec);
+ if (rc) {
+ pr_err("Could not read current time: %d\n", rc);
+ } else {
+ monitor_soc_delay = get_calculation_delay_ms(chip) -
+ ((tm_now_sec - chip->tm_sec) * 1000);
+ monitor_soc_delay = max(0, monitor_soc_delay);
+ }
+ pr_debug("monitor_soc_delay_sec=%d tm_now_sec=%ld chip->tm_sec=%ld\n",
+ monitor_soc_delay / 1000, tm_now_sec, chip->tm_sec);
+ schedule_delayed_work(&chip->monitor_soc_work,
+ msecs_to_jiffies(monitor_soc_delay));
+
+ return 0;
+}
+
+static const struct dev_pm_ops qpnp_vm_bms_pm_ops = {
+ .suspend = bms_suspend,
+ .resume = bms_resume,
+};
+
+static const struct of_device_id qpnp_vm_bms_match_table[] = {
+ { .compatible = QPNP_VM_BMS_DEV_NAME },
+ {}
+};
+
+static struct platform_driver qpnp_vm_bms_driver = {
+ .probe = qpnp_vm_bms_probe,
+ .remove = qpnp_vm_bms_remove,
+ .driver = {
+ .name = QPNP_VM_BMS_DEV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = qpnp_vm_bms_match_table,
+ .pm = &qpnp_vm_bms_pm_ops,
+ },
+};
+module_platform_driver(qpnp_vm_bms_driver);
+
+MODULE_DESCRIPTION("QPNP VM-BMS Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" QPNP_VM_BMS_DEV_NAME);
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 36a3be7..39005f6 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -689,6 +689,7 @@
vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0);
vote(chg->usb_icl_votable, USBIN_USBIN_BOOST_VOTER, false, 0);
vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER, false, 0);
+ vote(chg->hvdcp_hw_inov_dis_votable, OV_VOTER, false, 0);
cancel_delayed_work_sync(&chg->hvdcp_detect_work);
@@ -1120,19 +1121,21 @@
return -EINVAL;
}
- if (power_role == UFP_EN_CMD_BIT) {
- /* disable PBS workaround when forcing sink mode */
- rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0x0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't write to TM_IO_DTEST4_SEL rc=%d\n",
- rc);
- }
- } else {
- /* restore it back to 0xA5 */
- rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0xA5);
- if (rc < 0) {
- smblib_err(chg, "Couldn't write to TM_IO_DTEST4_SEL rc=%d\n",
- rc);
+ if (chg->wa_flags & TYPEC_PBS_WA_BIT) {
+ if (power_role == UFP_EN_CMD_BIT) {
+ /* disable PBS workaround when forcing sink mode */
+ rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0x0);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't write to TM_IO_DTEST4_SEL rc=%d\n",
+ rc);
+ }
+ } else {
+ /* restore it back to 0xA5 */
+ rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0xA5);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't write to TM_IO_DTEST4_SEL rc=%d\n",
+ rc);
+ }
}
}
@@ -3638,6 +3641,33 @@
rising ? "rising" : "falling");
}
+#define MICRO_10P3V 10300000
+static void smblib_check_ov_condition(struct smb_charger *chg)
+{
+ union power_supply_propval pval = {0, };
+ int rc;
+
+ if (chg->wa_flags & OV_IRQ_WA_BIT) {
+ rc = power_supply_get_property(chg->usb_psy,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get current voltage, rc=%d\n",
+ rc);
+ return;
+ }
+
+ if (pval.intval > MICRO_10P3V) {
+ smblib_err(chg, "USBIN OV detected\n");
+ vote(chg->hvdcp_hw_inov_dis_votable, OV_VOTER, true,
+ 0);
+ pval.intval = POWER_SUPPLY_DP_DM_FORCE_5V;
+ rc = power_supply_set_property(chg->batt_psy,
+ POWER_SUPPLY_PROP_DP_DM, &pval);
+ return;
+ }
+ }
+}
+
#define QC3_PULSES_FOR_6V 5
#define QC3_PULSES_FOR_9V 20
#define QC3_PULSES_FOR_12V 35
@@ -3647,6 +3677,7 @@
u8 stat;
int pulses;
+ smblib_check_ov_condition(chg);
power_supply_changed(chg->usb_main_psy);
if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP) {
rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat);
@@ -4198,6 +4229,7 @@
/* reset hvdcp voters */
vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0);
vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, true, 0);
+ vote(chg->hvdcp_hw_inov_dis_votable, OV_VOTER, false, 0);
/* reset power delivery voters */
vote(chg->pd_allowed_votable, PD_VOTER, false, 0);
@@ -4279,10 +4311,13 @@
if (rc < 0)
smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n", rc);
- /* restore crude sensor */
- rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0xA5);
- if (rc < 0)
- smblib_err(chg, "Couldn't restore crude sensor rc=%d\n", rc);
+ /* restore crude sensor if PM660/PMI8998 */
+ if (chg->wa_flags & TYPEC_PBS_WA_BIT) {
+ rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0xA5);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't restore crude sensor rc=%d\n",
+ rc);
+ }
mutex_lock(&chg->vconn_oc_lock);
if (!chg->vconn_en)
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index b9d1992..dc8cbc7 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -70,6 +70,7 @@
#define WBC_VOTER "WBC_VOTER"
#define MOISTURE_VOTER "MOISTURE_VOTER"
#define HVDCP2_ICL_VOTER "HVDCP2_ICL_VOTER"
+#define OV_VOTER "OV_VOTER"
#define VCONN_MAX_ATTEMPTS 3
#define OTG_MAX_ATTEMPTS 3
@@ -88,6 +89,8 @@
TYPEC_CC2_REMOVAL_WA_BIT = BIT(2),
QC_AUTH_INTERRUPT_WA_BIT = BIT(3),
OTG_WA = BIT(4),
+ OV_IRQ_WA_BIT = BIT(5),
+ TYPEC_PBS_WA_BIT = BIT(6),
};
enum smb_irq_index {
diff --git a/drivers/power/supply/qcom/smb1360-charger-fg.c b/drivers/power/supply/qcom/smb1360-charger-fg.c
new file mode 100644
index 0000000..ed9c610
--- /dev/null
+++ b/drivers/power/supply/qcom/smb1360-charger-fg.c
@@ -0,0 +1,5372 @@
+/* Copyright (c) 2013-2015, 2018, 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) "SMB:%s: " fmt, __func__
+
+#include <linux/i2c.h>
+#include <linux/debugfs.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/extcon.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/math64.h>
+#include <linux/slab.h>
+#include <linux/power_supply.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/machine.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/bitops.h>
+#include <linux/qpnp/qpnp-adc.h>
+#include <linux/completion.h>
+#include <linux/pm_wakeup.h>
+
+#define _SMB1360_MASK(BITS, POS) \
+ ((unsigned char)(((1 << (BITS)) - 1) << (POS)))
+#define SMB1360_MASK(LEFT_BIT_POS, RIGHT_BIT_POS) \
+ _SMB1360_MASK((LEFT_BIT_POS) - (RIGHT_BIT_POS) + 1, \
+ (RIGHT_BIT_POS))
+
+/* Charger Registers */
+#define CFG_BATT_CHG_REG 0x00
+#define CHG_ITERM_MASK SMB1360_MASK(2, 0)
+#define CHG_ITERM_25MA 0x0
+#define CHG_ITERM_200MA 0x7
+#define RECHG_MV_MASK SMB1360_MASK(6, 5)
+#define RECHG_MV_SHIFT 5
+#define OTG_CURRENT_MASK SMB1360_MASK(4, 3)
+#define OTG_CURRENT_SHIFT 3
+
+#define CFG_BATT_CHG_ICL_REG 0x05
+#define AC_INPUT_ICL_PIN_BIT BIT(7)
+#define AC_INPUT_PIN_HIGH_BIT BIT(6)
+#define RESET_STATE_USB_500 BIT(5)
+#define INPUT_CURR_LIM_MASK SMB1360_MASK(3, 0)
+#define INPUT_CURR_LIM_300MA 0x0
+
+#define CFG_GLITCH_FLT_REG 0x06
+#define AICL_ENABLED_BIT BIT(0)
+#define INPUT_UV_GLITCH_FLT_20MS_BIT BIT(7)
+
+#define CFG_CHG_MISC_REG 0x7
+#define CHG_EN_BY_PIN_BIT BIT(7)
+#define CHG_EN_ACTIVE_LOW_BIT BIT(6)
+#define PRE_TO_FAST_REQ_CMD_BIT BIT(5)
+#define CFG_BAT_OV_ENDS_CHG_CYC BIT(4)
+#define CHG_CURR_TERM_DIS_BIT BIT(3)
+#define CFG_AUTO_RECHG_DIS_BIT BIT(2)
+#define CFG_CHG_INHIBIT_EN_BIT BIT(0)
+
+#define CFG_CHG_FUNC_CTRL_REG 0x08
+#define CHG_RECHG_THRESH_FG_SRC_BIT BIT(1)
+
+#define CFG_STAT_CTRL_REG 0x09
+#define CHG_STAT_IRQ_ONLY_BIT BIT(4)
+#define CHG_TEMP_CHG_ERR_BLINK_BIT BIT(3)
+#define CHG_STAT_ACTIVE_HIGH_BIT BIT(1)
+#define CHG_STAT_DISABLE_BIT BIT(0)
+
+#define CFG_SFY_TIMER_CTRL_REG 0x0A
+#define SAFETY_TIME_DISABLE_BIT BIT(5)
+#define SAFETY_TIME_MINUTES_SHIFT 2
+#define SAFETY_TIME_MINUTES_MASK SMB1360_MASK(3, 2)
+
+#define CFG_BATT_MISSING_REG 0x0D
+#define BATT_MISSING_SRC_THERM_BIT BIT(1)
+
+#define CFG_FG_BATT_CTRL_REG 0x0E
+#define CFG_FG_OTP_BACK_UP_ENABLE BIT(7)
+#define BATT_ID_ENABLED_BIT BIT(5)
+#define CHG_BATT_ID_FAIL BIT(4)
+#define BATT_ID_FAIL_SELECT_PROFILE BIT(3)
+#define BATT_PROFILE_SELECT_MASK SMB1360_MASK(3, 0)
+#define BATT_PROFILEA_MASK 0x0
+#define BATT_PROFILEB_MASK 0xF
+
+#define IRQ_CFG_REG 0x0F
+#define IRQ_BAT_HOT_COLD_HARD_BIT BIT(7)
+#define IRQ_BAT_HOT_COLD_SOFT_BIT BIT(6)
+#define IRQ_DCIN_UV_BIT BIT(2)
+#define IRQ_AICL_DONE_BIT BIT(1)
+#define IRQ_INTERNAL_TEMPERATURE_BIT BIT(0)
+
+#define IRQ2_CFG_REG 0x10
+#define IRQ2_SAFETY_TIMER_BIT BIT(7)
+#define IRQ2_CHG_ERR_BIT BIT(6)
+#define IRQ2_CHG_PHASE_CHANGE_BIT BIT(4)
+#define IRQ2_POWER_OK_BIT BIT(2)
+#define IRQ2_BATT_MISSING_BIT BIT(1)
+#define IRQ2_VBAT_LOW_BIT BIT(0)
+
+#define IRQ3_CFG_REG 0x11
+#define IRQ3_FG_ACCESS_OK_BIT BIT(6)
+#define IRQ3_SOC_CHANGE_BIT BIT(4)
+#define IRQ3_SOC_MIN_BIT BIT(3)
+#define IRQ3_SOC_MAX_BIT BIT(2)
+#define IRQ3_SOC_EMPTY_BIT BIT(1)
+#define IRQ3_SOC_FULL_BIT BIT(0)
+
+#define CHG_CURRENT_REG 0x13
+#define FASTCHG_CURR_MASK SMB1360_MASK(4, 2)
+#define FASTCHG_CURR_SHIFT 2
+
+#define CHG_CMP_CFG 0x14
+#define JEITA_COMP_CURR_MASK SMB1360_MASK(3, 0)
+#define JEITA_COMP_EN_MASK SMB1360_MASK(7, 4)
+#define JEITA_COMP_EN_SHIFT 4
+#define JEITA_COMP_EN_BIT SMB1360_MASK(7, 4)
+#define BATT_CHG_FLT_VTG_REG 0x15
+#define VFLOAT_MASK SMB1360_MASK(6, 0)
+#define CFG_FVC_REG 0x16
+#define FLT_VTG_COMP_MASK SMB1360_MASK(6, 0)
+
+#define SHDN_CTRL_REG 0x1A
+#define SHDN_CMD_USE_BIT BIT(1)
+#define SHDN_CMD_POLARITY_BIT BIT(2)
+
+#define CURRENT_GAIN_LSB_REG 0x1D
+#define CURRENT_GAIN_MSB_REG 0x1E
+
+/* Command Registers */
+#define CMD_I2C_REG 0x40
+#define ALLOW_VOLATILE_BIT BIT(6)
+#define FG_ACCESS_ENABLED_BIT BIT(5)
+#define FG_RESET_BIT BIT(4)
+#define CYCLE_STRETCH_CLEAR_BIT BIT(3)
+
+#define CMD_IL_REG 0x41
+#define USB_CTRL_MASK SMB1360_MASK(1, 0)
+#define USB_100_BIT 0x01
+#define USB_500_BIT 0x00
+#define USB_AC_BIT 0x02
+#define SHDN_CMD_BIT BIT(7)
+
+#define CMD_CHG_REG 0x42
+#define CMD_CHG_EN BIT(1)
+#define CMD_OTG_EN_BIT BIT(0)
+
+/* Status Registers */
+#define STATUS_1_REG 0x48
+#define AICL_CURRENT_STATUS_MASK SMB1360_MASK(6, 0)
+#define AICL_LIMIT_1500MA 0xF
+
+#define STATUS_3_REG 0x4B
+#define CHG_HOLD_OFF_BIT BIT(3)
+#define CHG_TYPE_MASK SMB1360_MASK(2, 1)
+#define CHG_TYPE_SHIFT 1
+#define BATT_NOT_CHG_VAL 0x0
+#define BATT_PRE_CHG_VAL 0x1
+#define BATT_FAST_CHG_VAL 0x2
+#define BATT_TAPER_CHG_VAL 0x3
+#define CHG_EN_BIT BIT(0)
+
+#define STATUS_4_REG 0x4C
+#define CYCLE_STRETCH_ACTIVE_BIT BIT(5)
+
+#define REVISION_CTRL_REG 0x4F
+#define DEVICE_REV_MASK SMB1360_MASK(3, 0)
+
+/* IRQ Status Registers */
+#define IRQ_A_REG 0x50
+#define IRQ_A_HOT_HARD_BIT BIT(6)
+#define IRQ_A_COLD_HARD_BIT BIT(4)
+#define IRQ_A_HOT_SOFT_BIT BIT(2)
+#define IRQ_A_COLD_SOFT_BIT BIT(0)
+
+#define IRQ_B_REG 0x51
+#define IRQ_B_BATT_TERMINAL_BIT BIT(6)
+#define IRQ_B_BATT_MISSING_BIT BIT(4)
+
+#define IRQ_C_REG 0x52
+#define IRQ_C_CHG_TERM BIT(0)
+
+#define IRQ_D_REG 0x53
+#define IRQ_E_REG 0x54
+#define IRQ_E_USBIN_UV_BIT BIT(0)
+
+#define IRQ_F_REG 0x55
+
+#define IRQ_G_REG 0x56
+
+#define IRQ_H_REG 0x57
+#define IRQ_I_REG 0x58
+#define FG_ACCESS_ALLOWED_BIT BIT(0)
+#define BATT_ID_RESULT_BIT SMB1360_MASK(6, 4)
+#define BATT_ID_SHIFT 4
+
+/* FG registers - IRQ config register */
+#define SOC_MAX_REG 0x24
+#define SOC_MIN_REG 0x25
+#define VTG_EMPTY_REG 0x26
+#define SOC_DELTA_REG 0x28
+#define JEITA_SOFT_COLD_REG 0x29
+#define JEITA_SOFT_HOT_REG 0x2A
+#define VTG_MIN_REG 0x2B
+
+/* FG SHADOW registers */
+#define SHDW_FG_ESR_ACTUAL 0x20
+#define SHDW_FG_BATT_STATUS 0x60
+#define BATTERY_PROFILE_BIT BIT(0)
+
+#define SHDW_FG_MSYS_SOC 0x61
+#define SHDW_FG_CAPACITY 0x62
+#define SHDW_FG_VTG_NOW 0x69
+#define SHDW_FG_CURR_NOW 0x6B
+#define SHDW_FG_BATT_TEMP 0x6D
+
+#define VOLTAGE_PREDICTED_REG 0x80
+#define CC_TO_SOC_COEFF 0xBA
+#define NOMINAL_CAPACITY_REG 0xBC
+#define ACTUAL_CAPACITY_REG 0xBE
+#define FG_AUTO_RECHARGE_SOC 0xD2
+#define FG_SYS_CUTOFF_V_REG 0xD3
+#define FG_CC_TO_CV_V_REG 0xD5
+#define FG_ITERM_REG 0xD9
+#define FG_THERM_C1_COEFF_REG 0xDB
+#define FG_IBATT_STANDBY_REG 0xCF
+
+#define FG_I2C_CFG_MASK SMB1360_MASK(2, 1)
+#define FG_CFG_I2C_ADDR 0x2
+#define FG_PROFILE_A_ADDR 0x4
+#define FG_PROFILE_B_ADDR 0x6
+
+/* Constants */
+#define CURRENT_100_MA 100
+#define CURRENT_500_MA 500
+#define MAX_8_BITS 255
+#define JEITA_WORK_MS 3000
+
+#define FG_RESET_THRESHOLD_MV 15
+#define SMB1360_REV_1 0x01
+
+#define SMB1360_POWERON_DELAY_MS 2000
+#define SMB1360_FG_RESET_DELAY_MS 1500
+
+enum {
+ WRKRND_FG_CONFIG_FAIL = BIT(0),
+ WRKRND_BATT_DET_FAIL = BIT(1),
+ WRKRND_USB100_FAIL = BIT(2),
+ WRKRND_HARD_JEITA = BIT(3),
+};
+
+enum {
+ USER = BIT(0),
+};
+
+enum {
+ PARALLEL_USER = BIT(0),
+ PARALLEL_CURRENT = BIT(1),
+ PARALLEL_JEITA_SOFT = BIT(2),
+ PARALLEL_JEITA_HARD = BIT(3),
+ PARALLEL_EOC = BIT(4),
+};
+
+enum fg_i2c_access_type {
+ FG_ACCESS_CFG = 0x1,
+ FG_ACCESS_PROFILE_A = 0x2,
+ FG_ACCESS_PROFILE_B = 0x3
+};
+
+enum {
+ BATTERY_PROFILE_A,
+ BATTERY_PROFILE_B,
+ BATTERY_PROFILE_MAX,
+};
+
+static int otg_curr_ma[] = {350, 550, 950, 1500};
+
+struct otp_backup_pool {
+ u8 reg_start;
+ u8 reg_end;
+ u8 start_now;
+ u16 alg_bitmap;
+ bool initialized;
+ struct mutex lock;
+};
+
+enum otp_backup_alg {
+ OTP_BACKUP_NOT_USE = 0,
+ OTP_BACKUP_FG_USE,
+ OTP_BACKUP_PROF_A_USE,
+ OTP_BACKUP_PROF_B_USE,
+};
+
+struct smb1360_otg_regulator {
+ struct regulator_desc rdesc;
+ struct regulator_dev *rdev;
+};
+
+enum wakeup_src {
+ WAKEUP_SRC_FG_ACCESS = 0,
+ WAKEUP_SRC_JEITA_SOFT,
+ WAKEUP_SRC_PARALLEL,
+ WAKEUP_SRC_MIN_SOC,
+ WAKEUP_SRC_EMPTY_SOC,
+ WAKEUP_SRC_JEITA_HYSTERSIS,
+ WAKEUP_SRC_MAX,
+};
+#define WAKEUP_SRC_MASK (~(~0 << WAKEUP_SRC_MAX))
+
+struct smb1360_wakeup_source {
+ struct wakeup_source source;
+ unsigned long enabled_bitmap;
+ spinlock_t ws_lock;
+};
+
+static const unsigned int smb1360_extcon_cable[] = {
+ EXTCON_USB,
+ EXTCON_USB_HOST,
+ EXTCON_NONE,
+};
+
+struct smb1360_chip {
+ struct i2c_client *client;
+ struct device *dev;
+ u8 revision;
+ u8 soft_hot_rt_stat;
+ u8 soft_cold_rt_stat;
+ struct delayed_work jeita_work;
+ struct delayed_work delayed_init_work;
+ unsigned short default_i2c_addr;
+ unsigned short fg_i2c_addr;
+ bool pulsed_irq;
+ struct completion fg_mem_access_granted;
+
+ /* wakeup source */
+ struct smb1360_wakeup_source smb1360_ws;
+
+ /* configuration data - charger */
+ int fake_battery_soc;
+ bool batt_id_disabled;
+ bool charging_disabled;
+ bool recharge_disabled;
+ bool chg_inhibit_disabled;
+ bool iterm_disabled;
+ bool shdn_after_pwroff;
+ bool config_hard_thresholds;
+ bool soft_jeita_supported;
+ bool ov_ends_chg_cycle_disabled;
+ int iterm_ma;
+ int vfloat_mv;
+ int safety_time;
+ int resume_delta_mv;
+ u32 default_batt_profile;
+ unsigned int thermal_levels;
+ unsigned int therm_lvl_sel;
+ unsigned int *thermal_mitigation;
+ int otg_batt_curr_limit;
+ bool min_icl_usb100;
+ int cold_bat_decidegc;
+ int hot_bat_decidegc;
+ int cool_bat_decidegc;
+ int warm_bat_decidegc;
+ int cool_bat_mv;
+ int warm_bat_mv;
+ int cool_bat_ma;
+ int warm_bat_ma;
+ int soft_cold_thresh;
+ int soft_hot_thresh;
+
+ /* parallel-chg params */
+ int fastchg_current;
+ int parallel_chg_disable_status;
+ int max_parallel_chg_current;
+ bool parallel_charging;
+
+ /* configuration data - fg */
+ int soc_max;
+ int soc_min;
+ int delta_soc;
+ int voltage_min_mv;
+ int voltage_empty_mv;
+ int batt_capacity_mah;
+ int cc_soc_coeff;
+ int v_cutoff_mv;
+ int fg_iterm_ma;
+ int fg_ibatt_standby_ma;
+ int fg_thermistor_c1_coeff;
+ int fg_cc_to_cv_mv;
+ int fg_auto_recharge_soc;
+ bool empty_soc_disabled;
+ int fg_reset_threshold_mv;
+ bool fg_reset_at_pon;
+ bool rsense_10mohm;
+ bool otg_fet_present;
+ bool fet_gain_enabled;
+ int otg_fet_enable_gpio;
+
+ /* status tracking */
+ int voltage_now;
+ int current_now;
+ int resistance_now;
+ int temp_now;
+ int soc_now;
+ int fcc_mah;
+ bool usb_present;
+ bool batt_present;
+ bool batt_hot;
+ bool batt_cold;
+ bool batt_warm;
+ bool batt_cool;
+ bool batt_full;
+ bool resume_completed;
+ bool irq_waiting;
+ bool irq_disabled;
+ bool empty_soc;
+ bool awake_min_soc;
+ int workaround_flags;
+ u8 irq_cfg_mask[3];
+ int usb_psy_ma;
+ int charging_disabled_status;
+ u32 connected_rid;
+ u32 profile_rid[BATTERY_PROFILE_MAX];
+
+ u32 peek_poke_address;
+ u32 fg_access_type;
+ u32 fg_peek_poke_address;
+ int skip_writes;
+ int skip_reads;
+ enum power_supply_type usb_supply_type;
+ struct dentry *debug_root;
+
+ struct qpnp_vadc_chip *vadc_dev;
+ struct power_supply *parallel_psy;
+ struct power_supply_desc parallel_psy_d;
+ struct power_supply *usb_psy;
+ struct power_supply_desc usb_psy_d;
+ struct power_supply *batt_psy;
+ struct power_supply_desc batt_psy_d;
+ struct smb1360_otg_regulator otg_vreg;
+ struct mutex irq_complete;
+ struct mutex charging_disable_lock;
+ struct mutex current_change_lock;
+ struct mutex read_write_lock;
+ struct mutex parallel_chg_lock;
+ struct work_struct parallel_work;
+ struct mutex otp_gain_lock;
+ struct mutex fg_access_request_lock;
+ struct otp_backup_pool otp_backup;
+ u8 current_gain_otp_reg;
+ bool otp_hard_jeita_config;
+ int otp_cold_bat_decidegc;
+ int otp_hot_bat_decidegc;
+ u8 hard_jeita_otp_reg;
+ struct work_struct jeita_hysteresis_work;
+ int cold_hysteresis;
+ int hot_hysteresis;
+ struct extcon_dev *extcon;
+};
+
+static int chg_time[] = {
+ 192,
+ 384,
+ 768,
+ 1536,
+};
+
+static int input_current_limit[] = {
+ 300, 400, 450, 500, 600, 700, 800, 850, 900,
+ 950, 1000, 1100, 1200, 1300, 1400, 1500,
+};
+
+static int fastchg_current[] = {
+ 450, 600, 750, 900, 1050, 1200, 1350, 1500,
+};
+
+static void smb1360_stay_awake(struct smb1360_wakeup_source *source,
+ enum wakeup_src wk_src)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&source->ws_lock, flags);
+
+ if (!__test_and_set_bit(wk_src, &source->enabled_bitmap)) {
+ __pm_stay_awake(&source->source);
+ pr_debug("enabled source %s, wakeup_src %d\n",
+ source->source.name, wk_src);
+ }
+ spin_unlock_irqrestore(&source->ws_lock, flags);
+}
+
+static void smb1360_relax(struct smb1360_wakeup_source *source,
+ enum wakeup_src wk_src)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&source->ws_lock, flags);
+ if (__test_and_clear_bit(wk_src, &source->enabled_bitmap) &&
+ !(source->enabled_bitmap & WAKEUP_SRC_MASK)) {
+ __pm_relax(&source->source);
+ pr_debug("disabled source %s\n", source->source.name);
+ }
+ spin_unlock_irqrestore(&source->ws_lock, flags);
+
+ pr_debug("relax source %s, wakeup_src %d\n",
+ source->source.name, wk_src);
+}
+
+static void smb1360_wakeup_src_init(struct smb1360_chip *chip)
+{
+ spin_lock_init(&chip->smb1360_ws.ws_lock);
+ wakeup_source_init(&chip->smb1360_ws.source, "smb1360");
+}
+
+static int is_between(int value, int left, int right)
+{
+ if (left >= right && left >= value && value >= right)
+ return 1;
+ if (left <= right && left <= value && value <= right)
+ return 1;
+
+ return 0;
+}
+
+static int bound(int val, int min, int max)
+{
+ if (val < min)
+ return min;
+ if (val > max)
+ return max;
+
+ return val;
+}
+
+static int __smb1360_read(struct smb1360_chip *chip, int reg,
+ u8 *val)
+{
+ s32 ret;
+
+ ret = i2c_smbus_read_byte_data(chip->client, reg);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "i2c read fail: can't read from %02x: %d\n", reg, ret);
+ return ret;
+ }
+ *val = ret;
+ pr_debug("Reading 0x%02x=0x%02x\n", reg, *val);
+
+ return 0;
+}
+
+static int __smb1360_write(struct smb1360_chip *chip, int reg,
+ u8 val)
+{
+ s32 ret;
+
+ ret = i2c_smbus_write_byte_data(chip->client, reg, val);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "i2c write fail: can't write %02x to %02x: %d\n",
+ val, reg, ret);
+ return ret;
+ }
+ pr_debug("Writing 0x%02x=0x%02x\n", reg, val);
+ return 0;
+}
+
+static int smb1360_read(struct smb1360_chip *chip, int reg,
+ u8 *val)
+{
+ int rc;
+
+ if (chip->skip_reads) {
+ *val = 0;
+ return 0;
+ }
+ mutex_lock(&chip->read_write_lock);
+ rc = __smb1360_read(chip, reg, val);
+ mutex_unlock(&chip->read_write_lock);
+
+ return rc;
+}
+
+static int smb1360_write(struct smb1360_chip *chip, int reg,
+ u8 val)
+{
+ int rc;
+
+ if (chip->skip_writes)
+ return 0;
+
+ mutex_lock(&chip->read_write_lock);
+ rc = __smb1360_write(chip, reg, val);
+ mutex_unlock(&chip->read_write_lock);
+
+ return rc;
+}
+
+static int smb1360_fg_read(struct smb1360_chip *chip, int reg,
+ u8 *val)
+{
+ int rc;
+
+ if (chip->skip_reads) {
+ *val = 0;
+ return 0;
+ }
+
+ mutex_lock(&chip->read_write_lock);
+ chip->client->addr = chip->fg_i2c_addr;
+ rc = __smb1360_read(chip, reg, val);
+ chip->client->addr = chip->default_i2c_addr;
+ mutex_unlock(&chip->read_write_lock);
+
+ return rc;
+}
+
+static int smb1360_fg_write(struct smb1360_chip *chip, int reg,
+ u8 val)
+{
+ int rc;
+
+ if (chip->skip_writes)
+ return 0;
+
+ mutex_lock(&chip->read_write_lock);
+ chip->client->addr = chip->fg_i2c_addr;
+ rc = __smb1360_write(chip, reg, val);
+ chip->client->addr = chip->default_i2c_addr;
+ mutex_unlock(&chip->read_write_lock);
+
+ return rc;
+}
+
+static int smb1360_read_bytes(struct smb1360_chip *chip, int reg,
+ u8 *val, u8 bytes)
+{
+ s32 rc;
+
+ if (chip->skip_reads) {
+ *val = 0;
+ return 0;
+ }
+
+ mutex_lock(&chip->read_write_lock);
+ rc = i2c_smbus_read_i2c_block_data(chip->client, reg, bytes, val);
+ if (rc < 0)
+ dev_err(chip->dev,
+ "i2c read fail: can't read %d bytes from %02x: %d\n",
+ bytes, reg, rc);
+ mutex_unlock(&chip->read_write_lock);
+
+ return (rc < 0) ? rc : 0;
+}
+
+static int smb1360_write_bytes(struct smb1360_chip *chip, int reg,
+ u8 *val, u8 bytes)
+{
+ s32 rc;
+
+ if (chip->skip_writes) {
+ *val = 0;
+ return 0;
+ }
+
+ mutex_lock(&chip->read_write_lock);
+ rc = i2c_smbus_write_i2c_block_data(chip->client, reg, bytes, val);
+ if (rc < 0)
+ dev_err(chip->dev,
+ "i2c write fail: can't read %d bytes from %02x: %d\n",
+ bytes, reg, rc);
+ mutex_unlock(&chip->read_write_lock);
+
+ return (rc < 0) ? rc : 0;
+}
+
+static int smb1360_masked_write(struct smb1360_chip *chip, int reg,
+ u8 mask, u8 val)
+{
+ s32 rc;
+ u8 temp;
+
+ if (chip->skip_writes || chip->skip_reads)
+ return 0;
+
+ mutex_lock(&chip->read_write_lock);
+ rc = __smb1360_read(chip, reg, &temp);
+ if (rc < 0) {
+ dev_err(chip->dev, "read failed: reg=%03X, rc=%d\n", reg, rc);
+ goto out;
+ }
+ temp &= ~mask;
+ temp |= val & mask;
+ rc = __smb1360_write(chip, reg, temp);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "write failed: reg=%03X, rc=%d\n", reg, rc);
+ }
+out:
+ mutex_unlock(&chip->read_write_lock);
+ return rc;
+}
+
+static int smb1360_select_fg_i2c_address(struct smb1360_chip *chip)
+{
+ unsigned short addr = chip->default_i2c_addr << 0x1;
+
+ switch (chip->fg_access_type) {
+ case FG_ACCESS_CFG:
+ addr = (addr & ~FG_I2C_CFG_MASK) | FG_CFG_I2C_ADDR;
+ break;
+ case FG_ACCESS_PROFILE_A:
+ addr = (addr & ~FG_I2C_CFG_MASK) | FG_PROFILE_A_ADDR;
+ break;
+ case FG_ACCESS_PROFILE_B:
+ addr = (addr & ~FG_I2C_CFG_MASK) | FG_PROFILE_B_ADDR;
+ break;
+ default:
+ pr_err("Invalid FG access type=%d\n", chip->fg_access_type);
+ return -EINVAL;
+ }
+
+ chip->fg_i2c_addr = addr >> 0x1;
+ pr_debug("FG_access_type=%d fg_i2c_addr=%x\n", chip->fg_access_type,
+ chip->fg_i2c_addr);
+
+ return 0;
+}
+
+#define EXPONENT_MASK 0xF800
+#define MANTISSA_MASK 0x3FF
+#define SIGN_MASK 0x400
+#define EXPONENT_SHIFT 11
+#define SIGN_SHIFT 10
+#define MICRO_UNIT 1000000ULL
+static int64_t float_decode(u16 reg)
+{
+ int64_t final_val, exponent_val, mantissa_val;
+ int exponent, mantissa, n;
+ bool sign;
+
+ exponent = (reg & EXPONENT_MASK) >> EXPONENT_SHIFT;
+ mantissa = (reg & MANTISSA_MASK);
+ sign = !!(reg & SIGN_MASK);
+
+ pr_debug("exponent=%d mantissa=%d sign=%d\n", exponent, mantissa, sign);
+
+ mantissa_val = mantissa * MICRO_UNIT;
+
+ n = exponent - 15;
+ if (n < 0)
+ exponent_val = MICRO_UNIT >> -n;
+ else
+ exponent_val = MICRO_UNIT << n;
+
+ n = n - 10;
+ if (n < 0)
+ mantissa_val >>= -n;
+ else
+ mantissa_val <<= n;
+
+ final_val = exponent_val + mantissa_val;
+
+ if (sign)
+ final_val *= -1;
+
+ return final_val;
+}
+
+#define MAX_MANTISSA (1023 * 1000000ULL)
+static unsigned int float_encode(int64_t float_val)
+{
+ int exponent = 0, sign = 0;
+ unsigned int final_val = 0;
+
+ if (float_val == 0)
+ return 0;
+
+ if (float_val < 0) {
+ sign = 1;
+ float_val = -float_val;
+ }
+
+ /* Reduce large mantissa until it fits into 10 bit */
+ while (float_val >= MAX_MANTISSA) {
+ exponent++;
+ float_val >>= 1;
+ }
+
+ /* Increase small mantissa to improve precision */
+ while (float_val < MAX_MANTISSA && exponent > -25) {
+ exponent--;
+ float_val <<= 1;
+ }
+
+ exponent = exponent + 25;
+
+ /* Convert mantissa from micro-units to units */
+ float_val = div_s64((float_val + MICRO_UNIT), (int)MICRO_UNIT);
+
+ if (float_val == 1024) {
+ exponent--;
+ float_val <<= 1;
+ }
+
+ float_val -= 1024;
+
+ /* Ensure that resulting number is within range */
+ if (float_val > MANTISSA_MASK)
+ float_val = MANTISSA_MASK;
+
+ /* Convert to 5 bit exponent, 11 bit mantissa */
+ final_val = (float_val & MANTISSA_MASK) | (sign << SIGN_SHIFT) |
+ ((exponent << EXPONENT_SHIFT) & EXPONENT_MASK);
+
+ return final_val;
+}
+
+/* FG reset could only be done after FG access being granted */
+static int smb1360_force_fg_reset(struct smb1360_chip *chip)
+{
+ int rc;
+
+ rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_RESET_BIT,
+ FG_RESET_BIT);
+ if (rc) {
+ pr_err("Couldn't reset FG rc=%d\n", rc);
+ return rc;
+ }
+
+ msleep(SMB1360_FG_RESET_DELAY_MS);
+
+ rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_RESET_BIT, 0);
+ if (rc)
+ pr_err("Couldn't un-reset FG rc=%d\n", rc);
+
+ return rc;
+}
+
+/*
+ * Requesting FG access relys on the FG_ACCESS_ALLOWED IRQ.
+ * This function can only be called after interrupt handler
+ * being installed successfully.
+ */
+#define SMB1360_FG_ACCESS_TIMEOUT_MS 5000
+#define SMB1360_FG_ACCESS_RETRY_COUNT 3
+static int smb1360_enable_fg_access(struct smb1360_chip *chip)
+{
+ int rc = 0;
+ u8 reg, retry = SMB1360_FG_ACCESS_RETRY_COUNT;
+
+ pr_debug("request FG memory access\n");
+ /*
+ * read the ACCESS_ALLOW status bit firstly to
+ * check if the access was granted before
+ */
+ mutex_lock(&chip->fg_access_request_lock);
+ smb1360_stay_awake(&chip->smb1360_ws, WAKEUP_SRC_FG_ACCESS);
+ rc = smb1360_read(chip, IRQ_I_REG, ®);
+ if (rc) {
+ pr_err("Couldn't read IRQ_I_REG, rc=%d\n", rc);
+ goto bail_i2c;
+ } else if (reg & FG_ACCESS_ALLOWED_BIT) {
+ pr_debug("FG access was granted\n");
+ goto bail_i2c;
+ }
+
+ /* request FG access */
+ rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_ACCESS_ENABLED_BIT,
+ FG_ACCESS_ENABLED_BIT);
+ if (rc) {
+ pr_err("Couldn't enable FG access rc=%d\n", rc);
+ goto bail_i2c;
+ }
+
+ while (retry--) {
+ rc = wait_for_completion_interruptible_timeout(
+ &chip->fg_mem_access_granted,
+ msecs_to_jiffies(SMB1360_FG_ACCESS_TIMEOUT_MS));
+ if (rc <= 0)
+ pr_debug("FG access timeout, retry: %d\n", retry);
+ else
+ break;
+ }
+ if (rc == 0) /* timed out */
+ rc = -ETIMEDOUT;
+ else if (rc > 0) /* completed */
+ rc = 0;
+
+ /* Clear the FG access bit if request failed */
+ if (rc < 0) {
+ rc = smb1360_masked_write(chip, CMD_I2C_REG,
+ FG_ACCESS_ENABLED_BIT, 0);
+ if (rc)
+ pr_err("Couldn't disable FG access rc=%d\n", rc);
+ }
+
+bail_i2c:
+ smb1360_relax(&chip->smb1360_ws, WAKEUP_SRC_FG_ACCESS);
+ mutex_unlock(&chip->fg_access_request_lock);
+ return rc;
+}
+
+static inline bool is_device_suspended(struct smb1360_chip *chip)
+{
+ return !chip->resume_completed;
+}
+
+static int smb1360_disable_fg_access(struct smb1360_chip *chip)
+{
+ int rc;
+
+ rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_ACCESS_ENABLED_BIT, 0);
+ if (rc)
+ pr_err("Couldn't disable FG access rc=%d\n", rc);
+
+ init_completion(&chip->fg_mem_access_granted);
+
+ return rc;
+}
+
+static int smb1360_enable_volatile_writes(struct smb1360_chip *chip)
+{
+ int rc;
+
+ rc = smb1360_masked_write(chip, CMD_I2C_REG,
+ ALLOW_VOLATILE_BIT, ALLOW_VOLATILE_BIT);
+ if (rc < 0)
+ dev_err(chip->dev,
+ "Couldn't set VOLATILE_W_PERM_BIT rc=%d\n", rc);
+
+ return rc;
+}
+
+static void smb1360_otp_backup_pool_init(struct smb1360_chip *chip)
+{
+ struct otp_backup_pool *pool = &chip->otp_backup;
+
+ pool->reg_start = 0xE0;
+ pool->reg_end = 0xEF;
+ pool->start_now = pool->reg_start;
+ mutex_init(&pool->lock);
+}
+
+static int smb1360_alloc_otp_backup_register(struct smb1360_chip *chip,
+ u8 size, int usage)
+{
+ int rc = 0, i;
+ u8 inv_pos;
+ struct otp_backup_pool *pool = &chip->otp_backup;
+
+ if (size % 2) {
+ pr_err("Must be allocated with pairs\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&pool->lock);
+ if (pool->start_now + size > pool->reg_end) {
+ pr_err("Allocation fail: start = 0x%x, size = %d\n",
+ pool->start_now, size);
+ mutex_unlock(&pool->lock);
+ return -EBUSY;
+ }
+ rc = pool->start_now;
+ inv_pos = pool->reg_end - pool->start_now + 1;
+ for (i = 0; i < size; i = i + 2) {
+ inv_pos -= (i ? 2 : 0);
+ pool->alg_bitmap |= usage << (inv_pos - 2);
+ }
+ pr_debug("Allocation success, start = 0x%x, size = %d, alg_bitmap = 0x%x\n",
+ rc, size, pool->alg_bitmap);
+ pool->start_now += size;
+ mutex_unlock(&pool->lock);
+
+ return rc;
+}
+
+#define OTP_BACKUP_WA_ALG_1 0xF0
+#define OTP_BACKUP_WA_ALG_2 0xF1
+static int smb1360_otp_backup_alg_update(struct smb1360_chip *chip)
+{
+ int rc = 0;
+ struct otp_backup_pool *pool = &chip->otp_backup;
+
+ mutex_lock(&pool->lock);
+ rc = smb1360_fg_write(chip, OTP_BACKUP_WA_ALG_1,
+ (u8)(pool->alg_bitmap >> 8));
+ rc |= smb1360_fg_write(chip, OTP_BACKUP_WA_ALG_2,
+ (u8)(pool->alg_bitmap));
+ if (rc)
+ pr_err("Write FG address F0/F1 failed, rc = %d\n", rc);
+ mutex_unlock(&pool->lock);
+
+ return rc;
+}
+
+#define TRIM_1C_REG 0x1C
+#define CHECK_USB100_GOOD_BIT BIT(6)
+static bool is_usb100_broken(struct smb1360_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ rc = smb1360_read(chip, TRIM_1C_REG, ®);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read trim 1C reg rc = %d\n", rc);
+ return rc;
+ }
+ return !!(reg & CHECK_USB100_GOOD_BIT);
+}
+
+static int read_revision(struct smb1360_chip *chip, u8 *revision)
+{
+ int rc;
+
+ *revision = 0;
+ rc = smb1360_read(chip, REVISION_CTRL_REG, revision);
+ if (rc)
+ dev_err(chip->dev, "Couldn't read REVISION_CTRL_REG rc=%d", rc);
+
+ *revision &= DEVICE_REV_MASK;
+
+ return rc;
+}
+
+#define MIN_FLOAT_MV 3460
+#define MAX_FLOAT_MV 4730
+#define VFLOAT_STEP_MV 10
+static int smb1360_float_voltage_set(struct smb1360_chip *chip, int vfloat_mv)
+{
+ u8 temp;
+
+ if ((vfloat_mv < MIN_FLOAT_MV) || (vfloat_mv > MAX_FLOAT_MV)) {
+ dev_err(chip->dev, "bad float voltage mv =%d asked to set\n",
+ vfloat_mv);
+ return -EINVAL;
+ }
+
+ temp = (vfloat_mv - MIN_FLOAT_MV) / VFLOAT_STEP_MV;
+
+ return smb1360_masked_write(chip, BATT_CHG_FLT_VTG_REG,
+ VFLOAT_MASK, temp);
+}
+
+#define MIN_RECHG_MV 50
+#define MAX_RECHG_MV 300
+static int smb1360_recharge_threshold_set(struct smb1360_chip *chip,
+ int resume_mv)
+{
+ u8 temp;
+
+ if ((resume_mv < MIN_RECHG_MV) || (resume_mv > MAX_RECHG_MV)) {
+ dev_err(chip->dev, "bad rechg_thrsh =%d asked to set\n",
+ resume_mv);
+ return -EINVAL;
+ }
+
+ temp = resume_mv / 100;
+
+ return smb1360_masked_write(chip, CFG_BATT_CHG_REG,
+ RECHG_MV_MASK, temp << RECHG_MV_SHIFT);
+}
+
+static int __smb1360_charging_disable(struct smb1360_chip *chip, bool disable)
+{
+ int rc;
+
+ rc = smb1360_masked_write(chip, CMD_CHG_REG,
+ CMD_CHG_EN, disable ? 0 : CMD_CHG_EN);
+ if (rc < 0)
+ pr_err("Couldn't set CHG_ENABLE_BIT disable=%d rc = %d\n",
+ disable, rc);
+ else
+ pr_debug("CHG_EN status=%d\n", !disable);
+
+ return rc;
+}
+
+static int smb1360_charging_disable(struct smb1360_chip *chip, int reason,
+ int disable)
+{
+ int rc = 0;
+ int disabled;
+
+ mutex_lock(&chip->charging_disable_lock);
+
+ disabled = chip->charging_disabled_status;
+
+ pr_debug("reason=%d requested_disable=%d disabled_status=%d\n",
+ reason, disable, disabled);
+
+ if (disable == true)
+ disabled |= reason;
+ else
+ disabled &= ~reason;
+
+ if (disabled)
+ rc = __smb1360_charging_disable(chip, true);
+ else
+ rc = __smb1360_charging_disable(chip, false);
+
+ if (rc)
+ pr_err("Couldn't disable charging for reason=%d rc=%d\n",
+ rc, reason);
+ else
+ chip->charging_disabled_status = disabled;
+
+ mutex_unlock(&chip->charging_disable_lock);
+
+ return rc;
+}
+
+static int smb1360_soft_jeita_comp_enable(struct smb1360_chip *chip,
+ bool enable)
+{
+ int rc = 0;
+
+ rc = smb1360_masked_write(chip, CHG_CMP_CFG, JEITA_COMP_EN_MASK,
+ enable ? JEITA_COMP_EN_BIT : 0);
+ if (rc)
+ pr_err("Couldn't %s JEITA compensation\n", enable ?
+ "enable" : "disable");
+
+ return rc;
+}
+
+static enum power_supply_property smb1360_battery_properties[] = {
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_CHARGING_ENABLED,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_RESISTANCE,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL,
+};
+
+static int smb1360_get_prop_batt_present(struct smb1360_chip *chip)
+{
+ return chip->batt_present;
+}
+
+static int smb1360_get_prop_batt_status(struct smb1360_chip *chip)
+{
+ int rc;
+ u8 reg = 0, chg_type;
+
+ if (is_device_suspended(chip))
+ return POWER_SUPPLY_STATUS_UNKNOWN;
+
+ if (chip->batt_full)
+ return POWER_SUPPLY_STATUS_FULL;
+
+ rc = smb1360_read(chip, STATUS_3_REG, ®);
+ if (rc) {
+ pr_err("Couldn't read STATUS_3_REG rc=%d\n", rc);
+ return POWER_SUPPLY_STATUS_UNKNOWN;
+ }
+
+ pr_debug("STATUS_3_REG = %x\n", reg);
+
+ if (reg & CHG_HOLD_OFF_BIT)
+ return POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+ chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT;
+
+ if (chg_type == BATT_NOT_CHG_VAL)
+ return POWER_SUPPLY_STATUS_DISCHARGING;
+ else
+ return POWER_SUPPLY_STATUS_CHARGING;
+}
+
+static int smb1360_get_prop_charge_type(struct smb1360_chip *chip)
+{
+ int rc;
+ u8 reg = 0;
+ u8 chg_type;
+
+ if (is_device_suspended(chip))
+ return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+
+ rc = smb1360_read(chip, STATUS_3_REG, ®);
+ if (rc) {
+ pr_err("Couldn't read STATUS_3_REG rc=%d\n", rc);
+ return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+ }
+
+ chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT;
+ if (chg_type == BATT_NOT_CHG_VAL)
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ else if ((chg_type == BATT_FAST_CHG_VAL) ||
+ (chg_type == BATT_TAPER_CHG_VAL))
+ return POWER_SUPPLY_CHARGE_TYPE_FAST;
+ else if (chg_type == BATT_PRE_CHG_VAL)
+ return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+}
+
+static int smb1360_get_prop_batt_health(struct smb1360_chip *chip)
+{
+ union power_supply_propval ret = {0, };
+
+ if (chip->batt_hot)
+ ret.intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else if (chip->batt_cold)
+ ret.intval = POWER_SUPPLY_HEALTH_COLD;
+ else if (chip->batt_warm)
+ ret.intval = POWER_SUPPLY_HEALTH_WARM;
+ else if (chip->batt_cool)
+ ret.intval = POWER_SUPPLY_HEALTH_COOL;
+ else
+ ret.intval = POWER_SUPPLY_HEALTH_GOOD;
+
+ return ret.intval;
+}
+
+static int smb1360_get_prop_batt_capacity(struct smb1360_chip *chip)
+{
+ u8 reg;
+ u32 temp = 0;
+ int rc, soc = 0;
+
+ if (chip->fake_battery_soc >= 0)
+ return chip->fake_battery_soc;
+
+ if (chip->empty_soc) {
+ pr_debug("empty_soc\n");
+ return 0;
+ }
+
+ if (is_device_suspended(chip))
+ return chip->soc_now;
+
+ rc = smb1360_read(chip, SHDW_FG_MSYS_SOC, ®);
+ if (rc) {
+ pr_err("Failed to read FG_MSYS_SOC rc=%d\n", rc);
+ return rc;
+ }
+ soc = (100 * reg) / MAX_8_BITS;
+
+ temp = (100 * reg) % MAX_8_BITS;
+ if (temp > (MAX_8_BITS / 2))
+ soc += 1;
+
+ pr_debug("msys_soc_reg=0x%02x, fg_soc=%d batt_full = %d\n", reg,
+ soc, chip->batt_full);
+
+ chip->soc_now = (chip->batt_full ? 100 : bound(soc, 0, 100));
+
+ return chip->soc_now;
+}
+
+static int smb1360_get_prop_chg_full_design(struct smb1360_chip *chip)
+{
+ u8 reg[2];
+ int rc, fcc_mah = 0;
+
+ if (is_device_suspended(chip))
+ return chip->fcc_mah;
+
+ rc = smb1360_read_bytes(chip, SHDW_FG_CAPACITY, reg, 2);
+ if (rc) {
+ pr_err("Failed to read SHDW_FG_CAPACITY rc=%d\n", rc);
+ return rc;
+ }
+ fcc_mah = (reg[1] << 8) | reg[0];
+
+ pr_debug("reg[0]=0x%02x reg[1]=0x%02x fcc_mah=%d\n",
+ reg[0], reg[1], fcc_mah);
+
+ chip->fcc_mah = fcc_mah * 1000;
+
+ return chip->fcc_mah;
+}
+
+static int smb1360_get_prop_batt_temp(struct smb1360_chip *chip)
+{
+ u8 reg[2];
+ int rc, temp = 0;
+
+ if (is_device_suspended(chip))
+ return chip->temp_now;
+
+ rc = smb1360_read_bytes(chip, SHDW_FG_BATT_TEMP, reg, 2);
+ if (rc) {
+ pr_err("Failed to read SHDW_FG_BATT_TEMP rc=%d\n", rc);
+ return rc;
+ }
+
+ temp = (reg[1] << 8) | reg[0];
+ temp = div_u64(temp * 625, 10000UL); /* temperature in kelvin */
+ temp = (temp - 273) * 10; /* temperature in decideg */
+
+ pr_debug("reg[0]=0x%02x reg[1]=0x%02x temperature=%d\n",
+ reg[0], reg[1], temp);
+
+ chip->temp_now = temp;
+
+ return chip->temp_now;
+}
+
+static int smb1360_get_prop_voltage_now(struct smb1360_chip *chip)
+{
+ u8 reg[2];
+ int rc, temp = 0;
+
+ if (is_device_suspended(chip))
+ return chip->voltage_now;
+
+ rc = smb1360_read_bytes(chip, SHDW_FG_VTG_NOW, reg, 2);
+ if (rc) {
+ pr_err("Failed to read SHDW_FG_VTG_NOW rc=%d\n", rc);
+ return rc;
+ }
+
+ temp = (reg[1] << 8) | reg[0];
+ temp = div_u64(temp * 5000, 0x7FFF);
+
+ pr_debug("reg[0]=0x%02x reg[1]=0x%02x voltage=%d\n",
+ reg[0], reg[1], temp * 1000);
+
+ chip->voltage_now = temp * 1000;
+
+ return chip->voltage_now;
+}
+
+static int smb1360_get_prop_batt_resistance(struct smb1360_chip *chip)
+{
+ u8 reg[2];
+ u16 temp;
+ int rc;
+ int64_t resistance;
+
+ if (is_device_suspended(chip))
+ return chip->resistance_now;
+
+ rc = smb1360_read_bytes(chip, SHDW_FG_ESR_ACTUAL, reg, 2);
+ if (rc) {
+ pr_err("Failed to read FG_ESR_ACTUAL rc=%d\n", rc);
+ return rc;
+ }
+ temp = (reg[1] << 8) | reg[0];
+
+ resistance = float_decode(temp) * 2;
+
+ pr_debug("reg=0x%02x resistance=%lld\n", temp, resistance);
+
+ /* resistance in uohms */
+ chip->resistance_now = resistance;
+
+ return chip->resistance_now;
+}
+
+static int smb1360_get_prop_current_now(struct smb1360_chip *chip)
+{
+ u8 reg[2];
+ int rc, temp = 0;
+
+ if (is_device_suspended(chip))
+ return chip->current_now;
+
+ rc = smb1360_read_bytes(chip, SHDW_FG_CURR_NOW, reg, 2);
+ if (rc) {
+ pr_err("Failed to read SHDW_FG_CURR_NOW rc=%d\n", rc);
+ return rc;
+ }
+
+ temp = ((s8)reg[1] << 8) | reg[0];
+ temp = div_s64(temp * 2500, 0x7FFF);
+
+ pr_debug("reg[0]=0x%02x reg[1]=0x%02x current=%d\n",
+ reg[0], reg[1], temp * 1000);
+
+ chip->current_now = temp * 1000;
+
+ return chip->current_now;
+}
+
+static int smb1360_set_minimum_usb_current(struct smb1360_chip *chip)
+{
+ int rc = 0;
+
+ if (chip->min_icl_usb100) {
+ pr_debug("USB min current set to 100mA\n");
+ /* set input current limit to minimum (300mA) */
+ rc = smb1360_masked_write(chip, CFG_BATT_CHG_ICL_REG,
+ INPUT_CURR_LIM_MASK,
+ INPUT_CURR_LIM_300MA);
+ if (rc)
+ pr_err("Couldn't set ICL mA rc=%d\n", rc);
+
+ if (!(chip->workaround_flags & WRKRND_USB100_FAIL))
+ rc = smb1360_masked_write(chip, CMD_IL_REG,
+ USB_CTRL_MASK, USB_100_BIT);
+ if (rc)
+ pr_err("Couldn't configure for USB100 rc=%d\n",
+ rc);
+ } else {
+ pr_debug("USB min current set to 500mA\n");
+ rc = smb1360_masked_write(chip, CMD_IL_REG,
+ USB_CTRL_MASK, USB_500_BIT);
+ if (rc)
+ pr_err("Couldn't configure for USB100 rc=%d\n",
+ rc);
+ }
+
+ return rc;
+}
+
+static struct power_supply *get_parallel_psy(struct smb1360_chip *chip)
+{
+ if (chip->parallel_psy)
+ return chip->parallel_psy;
+ chip->parallel_psy = power_supply_get_by_name("usb-parallel");
+ if (!chip->parallel_psy)
+ pr_debug("parallel charger not found\n");
+ return chip->parallel_psy;
+}
+
+static int __smb1360_parallel_charger_enable(struct smb1360_chip *chip,
+ bool enable)
+{
+ struct power_supply *parallel_psy = get_parallel_psy(chip);
+ union power_supply_propval pval = {0, };
+
+ if (!parallel_psy)
+ return 0;
+
+ pval.intval = (enable ? (chip->max_parallel_chg_current * 1000) : 0);
+ chip->parallel_psy_d.set_property(parallel_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+ pval.intval = (enable ? 1 : 0);
+ chip->parallel_psy_d.set_property(parallel_psy,
+ POWER_SUPPLY_PROP_CHARGING_ENABLED, &pval);
+
+ pr_debug("Parallel-charger %s max_chg_current=%d\n",
+ enable ? "enabled" : "disabled",
+ enable ? (chip->max_parallel_chg_current * 1000) : 0);
+
+ return 0;
+}
+
+static int smb1360_parallel_charger_enable(struct smb1360_chip *chip,
+ int reason, bool enable)
+{
+ int disabled, *disabled_status;
+
+ mutex_lock(&chip->parallel_chg_lock);
+
+ disabled = chip->parallel_chg_disable_status;
+ disabled_status = &chip->parallel_chg_disable_status;
+
+ pr_debug("reason=0x%x requested=%s disabled_status=0x%x\n",
+ reason, enable ? "enable" : "disable", disabled);
+
+ if (enable == true)
+ disabled &= ~reason;
+ else
+ disabled |= reason;
+
+ if (*disabled_status && !disabled)
+ __smb1360_parallel_charger_enable(chip, true);
+
+ if (!(*disabled_status) && disabled)
+ __smb1360_parallel_charger_enable(chip, false);
+
+ *disabled_status = disabled;
+
+ pr_debug("disabled_status = %x\n", *disabled_status);
+
+ mutex_unlock(&chip->parallel_chg_lock);
+
+ return 0;
+}
+
+static void smb1360_parallel_work(struct work_struct *work)
+{
+ u8 reg;
+ int rc, i;
+ struct smb1360_chip *chip = container_of(work,
+ struct smb1360_chip, parallel_work);
+
+ /* check the AICL settled value */
+ rc = smb1360_read(chip, STATUS_1_REG, ®);
+ if (rc) {
+ pr_debug("Unable to read AICL status rc=%d\n", rc);
+ goto exit_work;
+ }
+ pr_debug("STATUS_1 (aicl status)=0x%x\n", reg);
+ if ((reg & AICL_CURRENT_STATUS_MASK) == AICL_LIMIT_1500MA) {
+ /* Strong Charger - Enable parallel path */
+ /* find the new fastchg current */
+ chip->fastchg_current += (chip->max_parallel_chg_current / 2);
+ for (i = 0; i < ARRAY_SIZE(fastchg_current) - 1; i++) {
+ if (fastchg_current[i] >= chip->fastchg_current)
+ break;
+ }
+ if (i == ARRAY_SIZE(fastchg_current))
+ i--;
+
+ rc = smb1360_masked_write(chip, CHG_CURRENT_REG,
+ FASTCHG_CURR_MASK, i << FASTCHG_CURR_SHIFT);
+ if (rc)
+ pr_err("Couldn't set fastchg mA rc=%d\n", rc);
+
+ pr_debug("fast-chg (parallel-mode) current set to = %d\n",
+ fastchg_current[i]);
+
+ smb1360_parallel_charger_enable(chip, PARALLEL_CURRENT, true);
+ } else {
+ /* Weak-charger - Disable parallel path */
+ smb1360_parallel_charger_enable(chip, PARALLEL_CURRENT, false);
+ }
+
+exit_work:
+ smb1360_relax(&chip->smb1360_ws, WAKEUP_SRC_PARALLEL);
+}
+
+static int smb1360_set_appropriate_usb_current(struct smb1360_chip *chip)
+{
+ int rc = 0, i, therm_ma, current_ma;
+ int path_current = chip->usb_psy_ma;
+
+ /*
+ * If battery is absent do not modify the current at all, these
+ * would be some appropriate values set by the bootloader or default
+ * configuration and since it is the only source of power we should
+ * not change it
+ */
+ if (!chip->batt_present) {
+ pr_debug("ignoring current request since battery is absent\n");
+ return 0;
+ }
+
+ if (chip->therm_lvl_sel > 0
+ && chip->therm_lvl_sel < (chip->thermal_levels - 1))
+ /*
+ * consider thermal limit only when it is active and not at
+ * the highest level
+ */
+ therm_ma = chip->thermal_mitigation[chip->therm_lvl_sel];
+ else
+ therm_ma = path_current;
+
+ current_ma = min(therm_ma, path_current);
+
+ if (chip->workaround_flags & WRKRND_HARD_JEITA) {
+ if (chip->batt_warm)
+ current_ma = min(current_ma, chip->warm_bat_ma);
+ else if (chip->batt_cool)
+ current_ma = min(current_ma, chip->cool_bat_ma);
+ }
+
+ if (current_ma <= 2) {
+ /*
+ * SMB1360 does not support USB suspend -
+ * so set the current-limit to minimum in suspend.
+ */
+ pr_debug("current_ma=%d <= 2 set USB current to minimum\n",
+ current_ma);
+ rc = smb1360_set_minimum_usb_current(chip);
+ if (rc < 0)
+ pr_err("Couldn't to set minimum USB current rc = %d\n",
+ rc);
+ /* disable parallel charger */
+ if (chip->parallel_charging)
+ smb1360_parallel_charger_enable(chip,
+ PARALLEL_CURRENT, false);
+
+ return rc;
+ }
+
+ for (i = ARRAY_SIZE(input_current_limit) - 1; i >= 0; i--) {
+ if (input_current_limit[i] <= current_ma)
+ break;
+ }
+ if (i < 0) {
+ pr_debug("Couldn't find ICL mA rc=%d\n", rc);
+ i = 0;
+ }
+ /* set input current limit */
+ rc = smb1360_masked_write(chip, CFG_BATT_CHG_ICL_REG,
+ INPUT_CURR_LIM_MASK, i);
+ if (rc)
+ pr_err("Couldn't set ICL mA rc=%d\n", rc);
+
+ pr_debug("ICL set to = %d\n", input_current_limit[i]);
+
+ if ((current_ma <= CURRENT_100_MA) &&
+ ((chip->workaround_flags & WRKRND_USB100_FAIL) ||
+ !chip->min_icl_usb100)) {
+ pr_debug("usb100 not supported: usb100_wrkrnd=%d min_icl_100=%d\n",
+ !!(chip->workaround_flags & WRKRND_USB100_FAIL),
+ chip->min_icl_usb100);
+ current_ma = CURRENT_500_MA;
+ }
+
+ if (current_ma <= CURRENT_100_MA) {
+ /* USB 100 */
+ rc = smb1360_masked_write(chip, CMD_IL_REG,
+ USB_CTRL_MASK, USB_100_BIT);
+ if (rc)
+ pr_err("Couldn't configure for USB100 rc=%d\n", rc);
+ pr_debug("Setting USB 100\n");
+ } else if (current_ma <= CURRENT_500_MA) {
+ /* USB 500 */
+ rc = smb1360_masked_write(chip, CMD_IL_REG,
+ USB_CTRL_MASK, USB_500_BIT);
+ if (rc)
+ pr_err("Couldn't configure for USB500 rc=%d\n", rc);
+ pr_debug("Setting USB 500\n");
+ } else {
+ /* USB AC */
+ if (chip->rsense_10mohm)
+ current_ma /= 2;
+
+ for (i = ARRAY_SIZE(fastchg_current) - 1; i >= 0; i--) {
+ if (fastchg_current[i] <= current_ma)
+ break;
+ }
+ if (i < 0) {
+ pr_debug("Couldn't find fastchg mA rc=%d\n", rc);
+ i = 0;
+ }
+
+ chip->fastchg_current = fastchg_current[i];
+
+ /* set fastchg limit */
+ rc = smb1360_masked_write(chip, CHG_CURRENT_REG,
+ FASTCHG_CURR_MASK, i << FASTCHG_CURR_SHIFT);
+ if (rc)
+ pr_err("Couldn't set fastchg mA rc=%d\n", rc);
+
+ /*
+ * To move to a new (higher) input-current setting,
+ * first set USB500 and then USBAC. This makes sure
+ * that the new ICL setting takes affect.
+ */
+ rc = smb1360_masked_write(chip, CMD_IL_REG,
+ USB_CTRL_MASK, USB_500_BIT);
+ if (rc)
+ pr_err("Couldn't configure for USB500 rc=%d\n", rc);
+
+ rc = smb1360_masked_write(chip, CMD_IL_REG,
+ USB_CTRL_MASK, USB_AC_BIT);
+ if (rc)
+ pr_err("Couldn't configure for USB AC rc=%d\n", rc);
+
+ pr_debug("fast-chg current set to = %d\n", fastchg_current[i]);
+ }
+
+ return rc;
+}
+
+static int smb1360_set_jeita_comp_curr(struct smb1360_chip *chip,
+ int current_ma)
+{
+ int i;
+ int rc = 0;
+
+ for (i = ARRAY_SIZE(fastchg_current) - 1; i >= 0; i--) {
+ if (fastchg_current[i] <= current_ma)
+ break;
+ }
+ if (i < 0) {
+ pr_debug("Couldn't find fastchg_current %dmA\n", current_ma);
+ i = 0;
+ }
+
+ rc = smb1360_masked_write(chip, CHG_CMP_CFG,
+ JEITA_COMP_CURR_MASK, i);
+ if (rc)
+ pr_err("Couldn't configure for Icomp, rc = %d\n", rc);
+
+ return rc;
+}
+
+#define TEMP_THRE_SET(x) ((x + 300) / 10)
+#define TEMP_THRE_GET(x) ((x * 10) - 300)
+static int smb1360_set_soft_jeita_threshold(struct smb1360_chip *chip,
+ int cold_threshold, int hot_threshold)
+{
+ int rc = 0;
+
+ rc = smb1360_write(chip, JEITA_SOFT_COLD_REG,
+ TEMP_THRE_SET(cold_threshold));
+ if (rc) {
+ pr_err("Couldn't set soft cold threshold, rc = %d\n", rc);
+ return rc;
+ }
+ chip->soft_cold_thresh = cold_threshold;
+
+ rc = smb1360_write(chip, JEITA_SOFT_HOT_REG,
+ TEMP_THRE_SET(hot_threshold));
+ if (rc) {
+ pr_err("Couldn't set soft hot threshold, rc = %d\n", rc);
+ return rc;
+ }
+ chip->soft_hot_thresh = hot_threshold;
+
+ return rc;
+}
+
+static int smb1360_get_soft_jeita_threshold(struct smb1360_chip *chip,
+ int *cold_threshold, int *hot_threshold)
+{
+ int rc = 0;
+ u8 value;
+
+ rc = smb1360_read(chip, JEITA_SOFT_COLD_REG, &value);
+ if (rc) {
+ pr_err("Couldn't get soft cold threshold, rc = %d\n", rc);
+ return rc;
+ }
+ *cold_threshold = TEMP_THRE_GET(value);
+
+ rc = smb1360_read(chip, JEITA_SOFT_HOT_REG, &value);
+ if (rc) {
+ pr_err("Couldn't get soft hot threshold, rc = %d\n", rc);
+ return rc;
+ }
+ *hot_threshold = TEMP_THRE_GET(value);
+
+ return rc;
+}
+
+#define OTP_HARD_COLD_REG_ADDR 0x12
+#define OTP_HARD_HOT_REG_ADDR 0x13
+static int smb1360_set_otp_hard_jeita_threshold(struct smb1360_chip *chip,
+ int cold_threshold, int hot_threshold)
+{
+ int rc = 0, i;
+ u8 reg[4] = { 0 };
+ u8 otp_reg = 0;
+ int temp_code;
+
+ if (cold_threshold > chip->cool_bat_decidegc ||
+ chip->cool_bat_decidegc >= chip->warm_bat_decidegc ||
+ chip->warm_bat_decidegc > hot_threshold) {
+ pr_err("cold:%d, cool:%d, warm:%d, hot:%d should be ordered in size\n",
+ cold_threshold, chip->cool_bat_decidegc,
+ chip->warm_bat_decidegc, hot_threshold);
+ return -EINVAL;
+ }
+ pr_debug("cold:%d, cool:%d, warm:%d, hot:%d\n",
+ cold_threshold, chip->cool_bat_decidegc,
+ chip->warm_bat_decidegc, hot_threshold);
+ if (!chip->hard_jeita_otp_reg) {
+ otp_reg = smb1360_alloc_otp_backup_register(chip,
+ ARRAY_SIZE(reg), OTP_BACKUP_FG_USE);
+ if (otp_reg <= 0) {
+ pr_err("OTP reg allocation failed for hard JEITA\n");
+ return otp_reg;
+ }
+
+ chip->hard_jeita_otp_reg = otp_reg;
+ } else {
+ otp_reg = chip->hard_jeita_otp_reg;
+ }
+ pr_debug("hard_jeita_otp_reg = 0x%x\n", chip->hard_jeita_otp_reg);
+
+ reg[0] = (u8)OTP_HARD_HOT_REG_ADDR;
+ temp_code = TEMP_THRE_SET(hot_threshold);
+ if (temp_code < 0) {
+ pr_err("hard hot temp encode failed\n");
+ return temp_code;
+ }
+ reg[1] = (u8)temp_code;
+ reg[2] = (u8)OTP_HARD_COLD_REG_ADDR;
+ temp_code = TEMP_THRE_SET(cold_threshold);
+ if (temp_code < 0) {
+ pr_err("hard cold temp encode failed\n");
+ return temp_code;
+ }
+ reg[3] = (u8)temp_code;
+
+ rc = smb1360_enable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't request FG access rc = %d\n", rc);
+ return rc;
+ }
+ chip->fg_access_type = FG_ACCESS_CFG;
+
+ rc = smb1360_select_fg_i2c_address(chip);
+ if (rc) {
+ pr_err("Unable to set FG access I2C address\n");
+ goto restore_fg;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(reg); i++) {
+ rc = smb1360_fg_write(chip, (otp_reg + i), reg[i]);
+ if (rc) {
+ pr_err("Write FG address 0x%x: 0x%x failed, rc = %d\n",
+ otp_reg + i, reg[i], rc);
+ goto restore_fg;
+ }
+ pr_debug("Write FG addr=0x%x, value=0x%x\n",
+ otp_reg + i, reg[i]);
+ }
+ rc = smb1360_otp_backup_alg_update(chip);
+ if (rc) {
+ pr_err("Update OTP backup algorithm failed\n");
+ goto restore_fg;
+ }
+
+ rc = smb1360_masked_write(chip, CFG_FG_BATT_CTRL_REG,
+ CFG_FG_OTP_BACK_UP_ENABLE, CFG_FG_OTP_BACK_UP_ENABLE);
+ if (rc) {
+ pr_err("Write reg 0x0E failed, rc = %d\n", rc);
+ goto restore_fg;
+ }
+
+restore_fg:
+ rc = smb1360_disable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't disable FG access rc = %d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int smb1360_hard_jeita_otp_init(struct smb1360_chip *chip)
+{
+ int rc = 0;
+
+ if (!chip->otp_hard_jeita_config)
+ return rc;
+
+ rc = smb1360_set_otp_hard_jeita_threshold(chip,
+ chip->otp_cold_bat_decidegc, chip->otp_hot_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't set OTP hard jeita threshold,rc = %d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int smb1360_system_temp_level_set(struct smb1360_chip *chip,
+ int lvl_sel)
+{
+ int rc = 0;
+ int prev_therm_lvl;
+
+ if (!chip->thermal_mitigation) {
+ pr_err("Thermal mitigation not supported\n");
+ return -EINVAL;
+ }
+
+ if (lvl_sel < 0) {
+ pr_err("Unsupported level selected %d\n", lvl_sel);
+ return -EINVAL;
+ }
+
+ if (lvl_sel >= chip->thermal_levels) {
+ pr_err("Unsupported level selected %d forcing %d\n", lvl_sel,
+ chip->thermal_levels - 1);
+ lvl_sel = chip->thermal_levels - 1;
+ }
+
+ if (lvl_sel == chip->therm_lvl_sel)
+ return 0;
+
+ mutex_lock(&chip->current_change_lock);
+ prev_therm_lvl = chip->therm_lvl_sel;
+ chip->therm_lvl_sel = lvl_sel;
+
+ if (chip->therm_lvl_sel == (chip->thermal_levels - 1)) {
+ rc = smb1360_set_minimum_usb_current(chip);
+ if (rc)
+ pr_err("Couldn't set USB current to minimum rc = %d\n",
+ rc);
+ } else {
+ rc = smb1360_set_appropriate_usb_current(chip);
+ if (rc)
+ pr_err("Couldn't set USB current rc = %d\n", rc);
+ }
+
+ mutex_unlock(&chip->current_change_lock);
+ return rc;
+}
+
+static enum power_supply_property smb1360_usb_properties[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_TYPE,
+ POWER_SUPPLY_PROP_REAL_TYPE,
+ POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
+};
+
+static int smb1360_usb_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int is_battery_charging = 0;
+ struct smb1360_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_SDP_CURRENT_MAX:
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = chip->usb_psy_ma * 1000;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = chip->usb_present;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ is_battery_charging = smb1360_get_prop_batt_status(chip);
+ val->intval = chip->usb_present &&
+ (is_battery_charging == POWER_SUPPLY_STATUS_CHARGING);
+ break;
+ case POWER_SUPPLY_PROP_REAL_TYPE:
+ val->intval = POWER_SUPPLY_TYPE_UNKNOWN;
+ if (chip->usb_present &&
+ (chip->usb_supply_type != POWER_SUPPLY_TYPE_UNKNOWN))
+ val->intval = chip->usb_supply_type;
+ break;
+ case POWER_SUPPLY_PROP_TYPE:
+ val->intval = POWER_SUPPLY_TYPE_USB;
+ if (chip->usb_present &&
+ (chip->usb_supply_type != POWER_SUPPLY_TYPE_UNKNOWN))
+ val->intval = chip->usb_supply_type;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int smb1360_usb_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct smb1360_chip *chip = power_supply_get_drvdata(psy);
+ int rc = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_SDP_CURRENT_MAX:
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ chip->usb_psy_ma = val->intval / 1000;
+ rc = smb1360_set_appropriate_usb_current(chip);
+ break;
+ case POWER_SUPPLY_PROP_TYPE:
+ case POWER_SUPPLY_PROP_REAL_TYPE:
+ chip->usb_supply_type = val->intval;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ power_supply_changed(psy);
+ return 0;
+}
+
+static int smb1360_usb_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ return 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+static int smb1360_battery_set_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ const union power_supply_propval *val)
+{
+ struct smb1360_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ smb1360_charging_disable(chip, USER, !val->intval);
+ if (chip->parallel_charging)
+ smb1360_parallel_charger_enable(chip,
+ PARALLEL_USER, val->intval);
+ power_supply_changed(chip->batt_psy);
+ power_supply_changed(chip->usb_psy);
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ chip->fake_battery_soc = val->intval;
+ pr_info("fake_soc set to %d\n", chip->fake_battery_soc);
+ power_supply_changed(chip->batt_psy);
+ break;
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+ smb1360_system_temp_level_set(chip, val->intval);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smb1360_battery_is_writeable(struct power_supply *psy,
+ enum power_supply_property prop)
+{
+ int rc;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ case POWER_SUPPLY_PROP_CAPACITY:
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+ rc = 1;
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+ return rc;
+}
+
+static int smb1360_battery_get_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct smb1360_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = smb1360_get_prop_batt_health(chip);
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = smb1360_get_prop_batt_present(chip);
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = smb1360_get_prop_batt_status(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ val->intval = !chip->charging_disabled_status;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ val->intval = smb1360_get_prop_charge_type(chip);
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = smb1360_get_prop_batt_capacity(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = smb1360_get_prop_chg_full_design(chip);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = smb1360_get_prop_voltage_now(chip);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = smb1360_get_prop_current_now(chip);
+ break;
+ case POWER_SUPPLY_PROP_RESISTANCE:
+ val->intval = smb1360_get_prop_batt_resistance(chip);
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = smb1360_get_prop_batt_temp(chip);
+ break;
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+ val->intval = chip->therm_lvl_sel;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int hot_hard_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("rt_stat = 0x%02x\n", rt_stat);
+ chip->batt_hot = !!rt_stat;
+
+ if (chip->parallel_charging) {
+ pr_debug("%s parallel-charging\n", chip->batt_hot ?
+ "Disable" : "Enable");
+ smb1360_parallel_charger_enable(chip,
+ PARALLEL_JEITA_HARD, !chip->batt_hot);
+ }
+ if (chip->hot_hysteresis) {
+ smb1360_stay_awake(&chip->smb1360_ws,
+ WAKEUP_SRC_JEITA_HYSTERSIS);
+ schedule_work(&chip->jeita_hysteresis_work);
+ }
+
+ return 0;
+}
+
+static int cold_hard_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("rt_stat = 0x%02x\n", rt_stat);
+ chip->batt_cold = !!rt_stat;
+
+ if (chip->parallel_charging) {
+ pr_debug("%s parallel-charging\n", chip->batt_cold ?
+ "Disable" : "Enable");
+ smb1360_parallel_charger_enable(chip,
+ PARALLEL_JEITA_HARD, !chip->batt_cold);
+ }
+ if (chip->cold_hysteresis) {
+ smb1360_stay_awake(&chip->smb1360_ws,
+ WAKEUP_SRC_JEITA_HYSTERSIS);
+ schedule_work(&chip->jeita_hysteresis_work);
+ }
+
+ return 0;
+}
+
+static void smb1360_jeita_hysteresis_work(struct work_struct *work)
+{
+ int rc = 0;
+ int hard_hot, hard_cold;
+ struct smb1360_chip *chip = container_of(work,
+ struct smb1360_chip, jeita_hysteresis_work);
+
+ /* disable hard JEITA IRQ first */
+ rc = smb1360_masked_write(chip, IRQ_CFG_REG,
+ IRQ_BAT_HOT_COLD_HARD_BIT, 0);
+ if (rc) {
+ pr_err("disable hard JEITA IRQ failed, rc = %d\n", rc);
+ goto exit_worker;
+ }
+ hard_hot = chip->otp_hot_bat_decidegc;
+ hard_cold = chip->otp_cold_bat_decidegc;
+ if (chip->batt_hot)
+ hard_hot -= chip->hot_hysteresis;
+ else if (chip->batt_cold)
+ hard_cold += chip->cold_hysteresis;
+
+ rc = smb1360_set_otp_hard_jeita_threshold(chip, hard_cold, hard_hot);
+ if (rc) {
+ pr_err("set hard JEITA threshold failed\n");
+ goto exit_worker;
+ }
+ pr_debug("hard cold: %d, hard hot: %d reprogramed\n",
+ hard_cold, hard_hot);
+ /* enable hard JEITA IRQ at the end */
+ rc = smb1360_masked_write(chip, IRQ_CFG_REG,
+ IRQ_BAT_HOT_COLD_HARD_BIT, IRQ_BAT_HOT_COLD_HARD_BIT);
+ if (rc)
+ pr_err("enable hard JEITA IRQ failed\n");
+exit_worker:
+ smb1360_relax(&chip->smb1360_ws, WAKEUP_SRC_JEITA_HYSTERSIS);
+}
+
+/*
+ * This worker thread should only be called when WRKRND_HARD_JEITA
+ * is set.
+ * It is needed to re-program JEITA soft thresholds, compensate
+ * target voltage and charging current manually.
+ * The function is required as JEITA hard thresholds can't be programmed.
+ */
+static void smb1360_jeita_work_fn(struct work_struct *work)
+{
+ int temp;
+ int rc = 0;
+ struct smb1360_chip *chip = container_of(work,
+ struct smb1360_chip, jeita_work.work);
+
+ temp = smb1360_get_prop_batt_temp(chip);
+
+ if (temp > chip->hot_bat_decidegc) {
+ /* battery status is hot, only config thresholds */
+ rc = smb1360_set_soft_jeita_threshold(chip,
+ chip->warm_bat_decidegc, chip->hot_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set jeita threshold\n");
+ goto end;
+ }
+ } else if (temp > chip->warm_bat_decidegc ||
+ (temp == chip->warm_bat_decidegc && !!chip->soft_hot_rt_stat)) {
+ /* battery status is warm, do compensation manually */
+ chip->batt_warm = true;
+ chip->batt_cool = false;
+ rc = smb1360_float_voltage_set(chip, chip->warm_bat_mv);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set float voltage\n");
+ goto end;
+ }
+ rc = smb1360_set_appropriate_usb_current(chip);
+ if (rc)
+ pr_err("Couldn't set USB current\n");
+ rc = smb1360_set_soft_jeita_threshold(chip,
+ chip->warm_bat_decidegc, chip->hot_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set jeita threshold\n");
+ goto end;
+ }
+ } else if (temp > chip->cool_bat_decidegc ||
+ (temp == chip->cool_bat_decidegc && !chip->soft_cold_rt_stat)) {
+ /* battery status is good, do the normal charging */
+ chip->batt_warm = false;
+ chip->batt_cool = false;
+ rc = smb1360_float_voltage_set(chip, chip->vfloat_mv);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set float voltage\n");
+ goto end;
+ }
+ rc = smb1360_set_appropriate_usb_current(chip);
+ if (rc)
+ pr_err("Couldn't set USB current\n");
+ rc = smb1360_set_soft_jeita_threshold(chip,
+ chip->cool_bat_decidegc, chip->warm_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set jeita threshold\n");
+ goto end;
+ }
+ } else if (temp > chip->cold_bat_decidegc) {
+ /* battery status is cool, do compensation manually */
+ chip->batt_cool = true;
+ chip->batt_warm = false;
+ rc = smb1360_float_voltage_set(chip, chip->cool_bat_mv);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set float voltage\n");
+ goto end;
+ }
+ rc = smb1360_set_soft_jeita_threshold(chip,
+ chip->cold_bat_decidegc, chip->cool_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set jeita threshold\n");
+ goto end;
+ }
+ } else {
+ /* battery status is cold, only config thresholds */
+ rc = smb1360_set_soft_jeita_threshold(chip,
+ chip->cold_bat_decidegc, chip->cool_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set jeita threshold\n");
+ goto end;
+ }
+ }
+
+ pr_debug("warm %d, cool %d, soft_cold_rt_sts %d, soft_hot_rt_sts %d, jeita supported %d, threshold_now %d %d\n",
+ chip->batt_warm, chip->batt_cool, !!chip->soft_cold_rt_stat,
+ !!chip->soft_hot_rt_stat, chip->soft_jeita_supported,
+ chip->soft_cold_thresh, chip->soft_hot_thresh);
+end:
+ smb1360_relax(&chip->smb1360_ws, WAKEUP_SRC_JEITA_SOFT);
+}
+
+static int hot_soft_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ chip->soft_hot_rt_stat = rt_stat;
+ pr_debug("rt_stat = 0x%02x\n", rt_stat);
+ if (!chip->config_hard_thresholds)
+ chip->batt_warm = !!rt_stat;
+
+ if (chip->workaround_flags & WRKRND_HARD_JEITA) {
+ cancel_delayed_work_sync(&chip->jeita_work);
+ schedule_delayed_work(&chip->jeita_work,
+ msecs_to_jiffies(JEITA_WORK_MS));
+ smb1360_stay_awake(&chip->smb1360_ws,
+ WAKEUP_SRC_JEITA_SOFT);
+ }
+
+ if (chip->parallel_charging) {
+ pr_debug("%s parallel-charging\n", chip->batt_warm ?
+ "Disable" : "Enable");
+ smb1360_parallel_charger_enable(chip,
+ PARALLEL_JEITA_SOFT, !chip->batt_warm);
+ }
+ return 0;
+}
+
+static int cold_soft_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ chip->soft_cold_rt_stat = rt_stat;
+ pr_debug("rt_stat = 0x%02x\n", rt_stat);
+ if (!chip->config_hard_thresholds)
+ chip->batt_cool = !!rt_stat;
+
+ if (chip->workaround_flags & WRKRND_HARD_JEITA) {
+ cancel_delayed_work_sync(&chip->jeita_work);
+ schedule_delayed_work(&chip->jeita_work,
+ msecs_to_jiffies(JEITA_WORK_MS));
+ smb1360_stay_awake(&chip->smb1360_ws,
+ WAKEUP_SRC_JEITA_SOFT);
+ }
+
+ if (chip->parallel_charging) {
+ pr_debug("%s parallel-charging\n", chip->batt_cool ?
+ "Disable" : "Enable");
+ smb1360_parallel_charger_enable(chip,
+ PARALLEL_JEITA_SOFT, !chip->batt_cool);
+ }
+
+ return 0;
+}
+
+static int battery_missing_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("rt_stat = 0x%02x\n", rt_stat);
+ chip->batt_present = !rt_stat;
+ return 0;
+}
+
+static int vbat_low_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("vbat low\n");
+
+ return 0;
+}
+
+static int chg_hot_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_warn_ratelimited("chg hot\n");
+ return 0;
+}
+
+static int chg_term_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("rt_stat = 0x%02x\n", rt_stat);
+ chip->batt_full = !!rt_stat;
+
+ if (chip->parallel_charging) {
+ pr_debug("%s parallel-charging\n", chip->batt_full ?
+ "Disable" : "Enable");
+ smb1360_parallel_charger_enable(chip,
+ PARALLEL_EOC, !chip->batt_full);
+ }
+
+ return 0;
+}
+
+static int chg_fastchg_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("rt_stat = 0x%02x\n", rt_stat);
+
+ return 0;
+}
+
+static int usbin_uv_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ bool usb_present = !rt_stat;
+
+ pr_debug("chip->usb_present = %d usb_present = %d\n",
+ chip->usb_present, usb_present);
+ if (chip->usb_present && !usb_present) {
+ /* USB removed */
+ chip->usb_present = usb_present;
+ extcon_set_cable_state_(chip->extcon, EXTCON_USB, false);
+ chip->usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
+ }
+
+ if (!chip->usb_present && usb_present) {
+ /* USB inserted */
+ chip->usb_present = usb_present;
+ extcon_set_cable_state_(chip->extcon, EXTCON_USB, true);
+ }
+ power_supply_changed(chip->usb_psy);
+
+ return 0;
+}
+
+static int aicl_done_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ bool aicl_done = !!rt_stat;
+
+ pr_debug("AICL done=%d\n", aicl_done);
+
+ if (chip->parallel_charging && aicl_done) {
+ cancel_work_sync(&chip->parallel_work);
+ smb1360_stay_awake(&chip->smb1360_ws, WAKEUP_SRC_PARALLEL);
+ schedule_work(&chip->parallel_work);
+ }
+
+ return 0;
+}
+
+static int chg_inhibit_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ /*
+ * charger is inserted when the battery voltage is high
+ * so h/w won't start charging just yet. Treat this as
+ * battery full
+ */
+ pr_debug("rt_stat = 0x%02x\n", rt_stat);
+ chip->batt_full = !!rt_stat;
+ return 0;
+}
+
+static int delta_soc_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("SOC changed! - rt_stat = 0x%02x\n", rt_stat);
+
+ return 0;
+}
+
+static int min_soc_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("SOC dropped below min SOC, rt_stat = 0x%02x\n", rt_stat);
+
+ if (chip->awake_min_soc)
+ rt_stat ? smb1360_stay_awake(&chip->smb1360_ws,
+ WAKEUP_SRC_MIN_SOC) :
+ smb1360_relax(&chip->smb1360_ws,
+ WAKEUP_SRC_MIN_SOC);
+
+ return 0;
+}
+
+static int empty_soc_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("SOC empty! rt_stat = 0x%02x\n", rt_stat);
+
+ if (!chip->empty_soc_disabled) {
+ if (rt_stat) {
+ chip->empty_soc = true;
+ smb1360_stay_awake(&chip->smb1360_ws,
+ WAKEUP_SRC_EMPTY_SOC);
+ pr_warn_ratelimited("SOC is 0\n");
+ } else {
+ chip->empty_soc = false;
+ smb1360_relax(&chip->smb1360_ws,
+ WAKEUP_SRC_EMPTY_SOC);
+ }
+ }
+
+ return 0;
+}
+
+static int full_soc_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ if (rt_stat)
+ pr_debug("SOC is 100\n");
+
+ return 0;
+}
+
+static int fg_access_allowed_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("stat=%d\n", !!rt_stat);
+
+ if (rt_stat & FG_ACCESS_ALLOWED_BIT) {
+ pr_debug("FG access granted\n");
+ complete_all(&chip->fg_mem_access_granted);
+ }
+
+ return 0;
+}
+
+static int batt_id_complete_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("batt_id = %x\n", (rt_stat & BATT_ID_RESULT_BIT)
+ >> BATT_ID_SHIFT);
+
+ return 0;
+}
+
+static int smb1360_adjust_current_gain(struct smb1360_chip *chip,
+ int gain_factor)
+{
+ int i, rc;
+ int64_t current_gain, new_current_gain;
+ u16 reg_value1 = 0, reg_value2 = 0;
+ u8 reg[4] = {0x1D, 0x00, 0x1E, 0x00};
+ u8 otp_reg = 0;
+
+ if (!chip->current_gain_otp_reg) {
+ otp_reg = smb1360_alloc_otp_backup_register(chip,
+ ARRAY_SIZE(reg), OTP_BACKUP_FG_USE);
+ if (otp_reg <= 0) {
+ pr_err("OTP reg allocation fail for adjusting current gain\n");
+ return otp_reg;
+ }
+ chip->current_gain_otp_reg = otp_reg;
+ } else {
+ otp_reg = chip->current_gain_otp_reg;
+ }
+ pr_debug("current_gain_otp_reg = 0x%x\n", chip->current_gain_otp_reg);
+
+ if (gain_factor) {
+ rc = smb1360_fg_read(chip, CURRENT_GAIN_LSB_REG, ®[1]);
+ if (rc) {
+ pr_err("Unable to set FG access I2C address rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = smb1360_fg_read(chip, CURRENT_GAIN_MSB_REG, ®[3]);
+ if (rc) {
+ pr_err("Unable to set FG access I2C address rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ reg_value1 = (reg[3] << 8) | reg[1];
+ current_gain = float_decode(reg_value1);
+ new_current_gain = MICRO_UNIT + (gain_factor * current_gain);
+ reg_value2 = float_encode(new_current_gain);
+ reg[1] = reg_value2 & 0xFF;
+ reg[3] = (reg_value2 & 0xFF00) >> 8;
+ pr_debug("current_gain_reg=0x%x current_gain_decoded=%lld new_current_gain_decoded=%lld new_current_gain_reg=0x%x\n",
+ reg_value1, current_gain, new_current_gain, reg_value2);
+
+ for (i = 0; i < ARRAY_SIZE(reg); i++) {
+ pr_debug("Writing reg_add=%x value=%x\n",
+ otp_reg + i, reg[i]);
+
+ rc = smb1360_fg_write(chip, (otp_reg + i), reg[i]);
+ if (rc) {
+ pr_err("Write FG address 0x%x failed, rc = %d\n",
+ otp_reg + i, rc);
+ return rc;
+ }
+ }
+ rc = smb1360_otp_backup_alg_update(chip);
+ if (rc) {
+ pr_err("Update OTP backup algorithm failed\n");
+ return rc;
+ }
+ } else {
+ pr_debug("Disabling gain correction\n");
+ rc = smb1360_fg_write(chip, 0xF0, 0x00);
+ if (rc) {
+ pr_err("Write fg address 0x%x failed, rc = %d\n",
+ 0xF0, rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int smb1360_otp_gain_config(struct smb1360_chip *chip, int gain_factor)
+{
+ int rc = 0;
+
+ rc = smb1360_enable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't request FG access rc = %d\n", rc);
+ return rc;
+ }
+ chip->fg_access_type = FG_ACCESS_CFG;
+
+ rc = smb1360_select_fg_i2c_address(chip);
+ if (rc) {
+ pr_err("Unable to set FG access I2C address\n");
+ goto restore_fg;
+ }
+
+ rc = smb1360_adjust_current_gain(chip, gain_factor);
+ if (rc) {
+ pr_err("Unable to modify current gain rc=%d\n", rc);
+ goto restore_fg;
+ }
+
+ rc = smb1360_masked_write(chip, CFG_FG_BATT_CTRL_REG,
+ CFG_FG_OTP_BACK_UP_ENABLE, CFG_FG_OTP_BACK_UP_ENABLE);
+ if (rc) {
+ pr_err("Write reg 0x0E failed, rc = %d\n", rc);
+ goto restore_fg;
+ }
+
+restore_fg:
+ rc = smb1360_disable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't disable FG access rc = %d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int smb1360_otg_disable(struct smb1360_chip *chip)
+{
+ int rc;
+
+ rc = smb1360_masked_write(chip, CMD_CHG_REG, CMD_OTG_EN_BIT, 0);
+ if (rc) {
+ pr_err("Couldn't disable OTG mode rc=%d\n", rc);
+ return rc;
+ }
+
+ mutex_lock(&chip->otp_gain_lock);
+ /* Disable current gain configuration */
+ if (chip->otg_fet_present && chip->fet_gain_enabled) {
+ /* Disable FET */
+ gpio_set_value(chip->otg_fet_enable_gpio, 1);
+ rc = smb1360_otp_gain_config(chip, 0);
+ if (rc < 0)
+ pr_err("Couldn't config OTP gain config rc=%d\n", rc);
+ else
+ chip->fet_gain_enabled = false;
+ }
+ mutex_unlock(&chip->otp_gain_lock);
+
+ return rc;
+}
+
+static int otg_fail_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ int rc;
+
+ pr_debug("OTG Failed stat=%d\n", rt_stat);
+ rc = smb1360_otg_disable(chip);
+ if (rc)
+ pr_err("Couldn't disable OTG mode rc=%d\n", rc);
+
+ return 0;
+}
+
+static int otg_oc_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ int rc;
+
+ pr_debug("OTG over-current stat=%d\n", rt_stat);
+ rc = smb1360_otg_disable(chip);
+ if (rc)
+ pr_err("Couldn't disable OTG mode rc=%d\n", rc);
+
+ return 0;
+}
+
+struct smb_irq_info {
+ const char *name;
+ int (*smb_irq)(struct smb1360_chip *chip,
+ u8 rt_stat);
+ int high;
+ int low;
+};
+
+struct irq_handler_info {
+ u8 stat_reg;
+ u8 val;
+ u8 prev_val;
+ struct smb_irq_info irq_info[4];
+};
+
+static struct irq_handler_info handlers[] = {
+ {IRQ_A_REG, 0, 0,
+ {
+ {
+ .name = "cold_soft",
+ .smb_irq = cold_soft_handler,
+ },
+ {
+ .name = "hot_soft",
+ .smb_irq = hot_soft_handler,
+ },
+ {
+ .name = "cold_hard",
+ .smb_irq = cold_hard_handler,
+ },
+ {
+ .name = "hot_hard",
+ .smb_irq = hot_hard_handler,
+ },
+ },
+ },
+ {IRQ_B_REG, 0, 0,
+ {
+ {
+ .name = "chg_hot",
+ .smb_irq = chg_hot_handler,
+ },
+ {
+ .name = "vbat_low",
+ .smb_irq = vbat_low_handler,
+ },
+ {
+ .name = "battery_missing",
+ .smb_irq = battery_missing_handler,
+ },
+ {
+ .name = "battery_missing",
+ .smb_irq = battery_missing_handler,
+ },
+ },
+ },
+ {IRQ_C_REG, 0, 0,
+ {
+ {
+ .name = "chg_term",
+ .smb_irq = chg_term_handler,
+ },
+ {
+ .name = "taper",
+ },
+ {
+ .name = "recharge",
+ },
+ {
+ .name = "fast_chg",
+ .smb_irq = chg_fastchg_handler,
+ },
+ },
+ },
+ {IRQ_D_REG, 0, 0,
+ {
+ {
+ .name = "prechg_timeout",
+ },
+ {
+ .name = "safety_timeout",
+ },
+ {
+ .name = "aicl_done",
+ .smb_irq = aicl_done_handler,
+ },
+ {
+ .name = "battery_ov",
+ },
+ },
+ },
+ {IRQ_E_REG, 0, 0,
+ {
+ {
+ .name = "usbin_uv",
+ .smb_irq = usbin_uv_handler,
+ },
+ {
+ .name = "usbin_ov",
+ },
+ {
+ .name = "unused",
+ },
+ {
+ .name = "chg_inhibit",
+ .smb_irq = chg_inhibit_handler,
+ },
+ },
+ },
+ {IRQ_F_REG, 0, 0,
+ {
+ {
+ .name = "power_ok",
+ },
+ {
+ .name = "unused",
+ },
+ {
+ .name = "otg_fail",
+ .smb_irq = otg_fail_handler,
+ },
+ {
+ .name = "otg_oc",
+ .smb_irq = otg_oc_handler,
+ },
+ },
+ },
+ {IRQ_G_REG, 0, 0,
+ {
+ {
+ .name = "delta_soc",
+ .smb_irq = delta_soc_handler,
+ },
+ {
+ .name = "chg_error",
+ },
+ {
+ .name = "wd_timeout",
+ },
+ {
+ .name = "unused",
+ },
+ },
+ },
+ {IRQ_H_REG, 0, 0,
+ {
+ {
+ .name = "min_soc",
+ .smb_irq = min_soc_handler,
+ },
+ {
+ .name = "max_soc",
+ },
+ {
+ .name = "empty_soc",
+ .smb_irq = empty_soc_handler,
+ },
+ {
+ .name = "full_soc",
+ .smb_irq = full_soc_handler,
+ },
+ },
+ },
+ {IRQ_I_REG, 0, 0,
+ {
+ {
+ .name = "fg_access_allowed",
+ .smb_irq = fg_access_allowed_handler,
+ },
+ {
+ .name = "fg_data_recovery",
+ },
+ {
+ .name = "batt_id_complete",
+ .smb_irq = batt_id_complete_handler,
+ },
+ },
+ },
+};
+
+#define IRQ_LATCHED_MASK 0x02
+#define IRQ_STATUS_MASK 0x01
+#define BATT_ID_LATCHED_MASK 0x08
+#define BATT_ID_STATUS_MASK 0x07
+#define BITS_PER_IRQ 2
+static irqreturn_t smb1360_stat_handler(int irq, void *dev_id)
+{
+ struct smb1360_chip *chip = dev_id;
+ int i, j;
+ u8 triggered;
+ u8 changed;
+ u8 rt_stat, prev_rt_stat, irq_latched_mask, irq_status_mask;
+ int rc;
+ int handler_count = 0;
+
+ mutex_lock(&chip->irq_complete);
+ chip->irq_waiting = true;
+ if (!chip->resume_completed) {
+ dev_dbg(chip->dev, "IRQ triggered before device-resume\n");
+ if (!chip->irq_disabled) {
+ disable_irq_nosync(irq);
+ chip->irq_disabled = true;
+ }
+ mutex_unlock(&chip->irq_complete);
+ return IRQ_HANDLED;
+ }
+ chip->irq_waiting = false;
+
+ for (i = 0; i < ARRAY_SIZE(handlers); i++) {
+ rc = smb1360_read(chip, handlers[i].stat_reg,
+ &handlers[i].val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read %d rc = %d\n",
+ handlers[i].stat_reg, rc);
+ continue;
+ }
+
+ for (j = 0; j < ARRAY_SIZE(handlers[i].irq_info); j++) {
+ if (handlers[i].stat_reg == IRQ_I_REG && j == 2) {
+ irq_latched_mask = BATT_ID_LATCHED_MASK;
+ irq_status_mask = BATT_ID_STATUS_MASK;
+ } else {
+ irq_latched_mask = IRQ_LATCHED_MASK;
+ irq_status_mask = IRQ_STATUS_MASK;
+ }
+ triggered = handlers[i].val
+ & (irq_latched_mask << (j * BITS_PER_IRQ));
+ rt_stat = handlers[i].val
+ & (irq_status_mask << (j * BITS_PER_IRQ));
+ prev_rt_stat = handlers[i].prev_val
+ & (irq_status_mask << (j * BITS_PER_IRQ));
+ changed = prev_rt_stat ^ rt_stat;
+
+ if (triggered || changed)
+ rt_stat ? handlers[i].irq_info[j].high++ :
+ handlers[i].irq_info[j].low++;
+
+ if ((triggered || changed)
+ && handlers[i].irq_info[j].smb_irq != NULL) {
+ handler_count++;
+ rc = handlers[i].irq_info[j].smb_irq(chip,
+ rt_stat);
+ if (rc < 0)
+ dev_err(chip->dev,
+ "Couldn't handle %d irq for reg 0x%02x rc = %d\n",
+ j, handlers[i].stat_reg, rc);
+ }
+ }
+ handlers[i].prev_val = handlers[i].val;
+ }
+
+ pr_debug("handler count = %d\n", handler_count);
+ if (handler_count)
+ power_supply_changed(chip->batt_psy);
+
+ mutex_unlock(&chip->irq_complete);
+
+ return IRQ_HANDLED;
+}
+
+static int show_irq_count(struct seq_file *m, void *data)
+{
+ int i, j, total = 0;
+
+ for (i = 0; i < ARRAY_SIZE(handlers); i++)
+ for (j = 0; j < 4; j++) {
+ if (!handlers[i].irq_info[j].name)
+ continue;
+ seq_printf(m, "%s=%d\t(high=%d low=%d)\n",
+ handlers[i].irq_info[j].name,
+ handlers[i].irq_info[j].high
+ + handlers[i].irq_info[j].low,
+ handlers[i].irq_info[j].high,
+ handlers[i].irq_info[j].low);
+ total += (handlers[i].irq_info[j].high
+ + handlers[i].irq_info[j].low);
+ }
+
+ seq_printf(m, "\n\tTotal = %d\n", total);
+
+ return 0;
+}
+
+static int irq_count_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct smb1360_chip *chip = inode->i_private;
+
+ return single_open(file, show_irq_count, chip);
+}
+
+static const struct file_operations irq_count_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = irq_count_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int get_reg(void *data, u64 *val)
+{
+ struct smb1360_chip *chip = data;
+ int rc;
+ u8 temp;
+
+ rc = smb1360_read(chip, chip->peek_poke_address, &temp);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't read reg %x rc = %d\n",
+ chip->peek_poke_address, rc);
+ return -EAGAIN;
+ }
+ *val = temp;
+ return 0;
+}
+
+static int set_reg(void *data, u64 val)
+{
+ struct smb1360_chip *chip = data;
+ int rc;
+ u8 temp;
+
+ temp = (u8) val;
+ rc = smb1360_write(chip, chip->peek_poke_address, temp);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't write 0x%02x to 0x%02x rc= %d\n",
+ chip->peek_poke_address, temp, rc);
+ return -EAGAIN;
+ }
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(poke_poke_debug_ops, get_reg, set_reg, "0x%02llx\n");
+
+static int fg_get_reg(void *data, u64 *val)
+{
+ struct smb1360_chip *chip = data;
+ int rc;
+ u8 temp;
+
+ rc = smb1360_select_fg_i2c_address(chip);
+ if (rc) {
+ pr_err("Unable to set FG access I2C address\n");
+ return -EINVAL;
+ }
+
+ rc = smb1360_fg_read(chip, chip->fg_peek_poke_address, &temp);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't read reg %x rc = %d\n",
+ chip->fg_peek_poke_address, rc);
+ return -EAGAIN;
+ }
+ *val = temp;
+ return 0;
+}
+
+static int fg_set_reg(void *data, u64 val)
+{
+ struct smb1360_chip *chip = data;
+ int rc;
+ u8 temp;
+
+ rc = smb1360_select_fg_i2c_address(chip);
+ if (rc) {
+ pr_err("Unable to set FG access I2C address\n");
+ return -EINVAL;
+ }
+
+ temp = (u8) val;
+ rc = smb1360_fg_write(chip, chip->fg_peek_poke_address, temp);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't write 0x%02x to 0x%02x rc= %d\n",
+ chip->fg_peek_poke_address, temp, rc);
+ return -EAGAIN;
+ }
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(fg_poke_poke_debug_ops, fg_get_reg,
+ fg_set_reg, "0x%02llx\n");
+
+#define LAST_CNFG_REG 0x17
+static int show_cnfg_regs(struct seq_file *m, void *data)
+{
+ struct smb1360_chip *chip = m->private;
+ int rc;
+ u8 reg;
+ u8 addr;
+
+ for (addr = 0; addr <= LAST_CNFG_REG; addr++) {
+ rc = smb1360_read(chip, addr, ®);
+ if (!rc)
+ seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
+ }
+
+ return 0;
+}
+
+static int cnfg_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct smb1360_chip *chip = inode->i_private;
+
+ return single_open(file, show_cnfg_regs, chip);
+}
+
+static const struct file_operations cnfg_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = cnfg_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+#define FIRST_CMD_REG 0x40
+#define LAST_CMD_REG 0x42
+static int show_cmd_regs(struct seq_file *m, void *data)
+{
+ struct smb1360_chip *chip = m->private;
+ int rc;
+ u8 reg;
+ u8 addr;
+
+ for (addr = FIRST_CMD_REG; addr <= LAST_CMD_REG; addr++) {
+ rc = smb1360_read(chip, addr, ®);
+ if (!rc)
+ seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
+ }
+
+ return 0;
+}
+
+static int cmd_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct smb1360_chip *chip = inode->i_private;
+
+ return single_open(file, show_cmd_regs, chip);
+}
+
+static const struct file_operations cmd_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = cmd_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+#define FIRST_STATUS_REG 0x48
+#define LAST_STATUS_REG 0x4B
+static int show_status_regs(struct seq_file *m, void *data)
+{
+ struct smb1360_chip *chip = m->private;
+ int rc;
+ u8 reg;
+ u8 addr;
+
+ for (addr = FIRST_STATUS_REG; addr <= LAST_STATUS_REG; addr++) {
+ rc = smb1360_read(chip, addr, ®);
+ if (!rc)
+ seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
+ }
+
+ return 0;
+}
+
+static int status_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct smb1360_chip *chip = inode->i_private;
+
+ return single_open(file, show_status_regs, chip);
+}
+
+static const struct file_operations status_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = status_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+#define FIRST_IRQ_REG 0x50
+#define LAST_IRQ_REG 0x58
+static int show_irq_stat_regs(struct seq_file *m, void *data)
+{
+ struct smb1360_chip *chip = m->private;
+ int rc;
+ u8 reg;
+ u8 addr;
+
+ for (addr = FIRST_IRQ_REG; addr <= LAST_IRQ_REG; addr++) {
+ rc = smb1360_read(chip, addr, ®);
+ if (!rc)
+ seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
+ }
+
+ return 0;
+}
+
+static int irq_stat_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct smb1360_chip *chip = inode->i_private;
+
+ return single_open(file, show_irq_stat_regs, chip);
+}
+
+static const struct file_operations irq_stat_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = irq_stat_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int data_8(u8 *reg)
+{
+ return reg[0];
+}
+static int data_16(u8 *reg)
+{
+ return (reg[1] << 8) | reg[0];
+}
+static int data_24(u8 *reg)
+{
+ return (reg[2] << 16) | (reg[1] << 8) | reg[0];
+}
+static int data_28(u8 *reg)
+{
+ return ((reg[3] & 0xF) << 24) | (reg[2] << 16) |
+ (reg[1] << 8) | reg[0];
+}
+static int data_32(u8 *reg)
+{
+ return (reg[3] << 24) | (reg[2] << 16) |
+ (reg[1] << 8) | reg[0];
+}
+
+struct fg_regs {
+ int index;
+ int length;
+ char *param_name;
+ int (*calc_func)(u8 *);
+};
+
+static struct fg_regs fg_scratch_pad[] = {
+ {0, 2, "v_current_predicted", data_16},
+ {2, 2, "v_cutoff_predicted", data_16},
+ {4, 2, "v_full_predicted", data_16},
+ {6, 2, "ocv_estimate", data_16},
+ {8, 2, "rslow_drop", data_16},
+ {10, 2, "voltage_old", data_16},
+ {12, 2, "current_old", data_16},
+ {14, 4, "current_average_full", data_32},
+ {18, 2, "temperature", data_16},
+ {20, 2, "temp_last_track", data_16},
+ {22, 2, "ESR_nominal", data_16},
+ {26, 2, "Rslow", data_16},
+ {28, 2, "counter_imptr", data_16},
+ {30, 2, "counter_pulse", data_16},
+ {32, 1, "IRQ_delta_prev", data_8},
+ {33, 1, "cap_learning_counter", data_8},
+ {34, 4, "Vact_int_error", data_32},
+ {38, 3, "SOC_cutoff", data_24},
+ {41, 3, "SOC_full", data_24},
+ {44, 3, "SOC_auto_rechrge_temp", data_24},
+ {47, 3, "Battery_SOC", data_24},
+ {50, 4, "CC_SOC", data_28},
+ {54, 2, "SOC_filtered", data_16},
+ {56, 2, "SOC_Monotonic", data_16},
+ {58, 2, "CC_SOC_coeff", data_16},
+ {60, 2, "nominal_capacity", data_16},
+ {62, 2, "actual_capacity", data_16},
+ {68, 1, "temperature_counter", data_8},
+ {69, 3, "Vbatt_filtered", data_24},
+ {72, 3, "Ibatt_filtered", data_24},
+ {75, 2, "Current_CC_shadow", data_16},
+ {79, 2, "Ibatt_standby", data_16},
+ {82, 1, "Auto_recharge_SOC_threshold", data_8},
+ {83, 2, "System_cutoff_voltage", data_16},
+ {85, 2, "System_CC_to_CV_voltage", data_16},
+ {87, 2, "System_term_current", data_16},
+ {89, 2, "System_fake_term_current", data_16},
+ {91, 2, "thermistor_c1_coeff", data_16},
+};
+
+static struct fg_regs fg_cfg[] = {
+ {0, 2, "ESR_actual", data_16},
+ {4, 1, "IRQ_SOC_max", data_8},
+ {5, 1, "IRQ_SOC_min", data_8},
+ {6, 1, "IRQ_volt_empty", data_8},
+ {7, 1, "Temp_external", data_8},
+ {8, 1, "IRQ_delta_threshold", data_8},
+ {9, 1, "JIETA_soft_cold", data_8},
+ {10, 1, "JIETA_soft_hot", data_8},
+ {11, 1, "IRQ_volt_min", data_8},
+ {14, 2, "ESR_sys_replace", data_16},
+};
+
+static struct fg_regs fg_shdw[] = {
+ {0, 1, "Latest_battery_info", data_8},
+ {1, 1, "Latest_Msys_SOC", data_8},
+ {2, 2, "Battery_capacity", data_16},
+ {4, 2, "Rslow_drop", data_16},
+ {6, 1, "Latest_SOC", data_8},
+ {7, 1, "Latest_Cutoff_SOC", data_8},
+ {8, 1, "Latest_full_SOC", data_8},
+ {9, 2, "Voltage_shadow", data_16},
+ {11, 2, "Current_shadow", data_16},
+ {13, 2, "Latest_temperature", data_16},
+ {15, 1, "Latest_system_sbits", data_8},
+};
+
+#define FIRST_FG_CFG_REG 0x20
+#define LAST_FG_CFG_REG 0x2F
+#define FIRST_FG_SHDW_REG 0x60
+#define LAST_FG_SHDW_REG 0x6F
+#define FG_SCRATCH_PAD_MAX 93
+#define FG_SCRATCH_PAD_BASE_REG 0x80
+#define SMB1360_I2C_READ_LENGTH 32
+
+static int smb1360_check_cycle_stretch(struct smb1360_chip *chip)
+{
+ int rc = 0;
+ u8 reg;
+
+ rc = smb1360_read(chip, STATUS_4_REG, ®);
+ if (rc) {
+ pr_err("Unable to read status regiseter\n");
+ } else if (reg & CYCLE_STRETCH_ACTIVE_BIT) {
+ /* clear cycle stretch */
+ rc = smb1360_masked_write(chip, CMD_I2C_REG,
+ CYCLE_STRETCH_CLEAR_BIT, CYCLE_STRETCH_CLEAR_BIT);
+ if (rc)
+ pr_err("Unable to clear cycle stretch\n");
+ }
+
+ return rc;
+}
+
+static int show_fg_regs(struct seq_file *m, void *data)
+{
+ struct smb1360_chip *chip = m->private;
+ int rc, i, j, rem_length;
+ u8 reg[FG_SCRATCH_PAD_MAX];
+
+ rc = smb1360_check_cycle_stretch(chip);
+ if (rc)
+ pr_err("Unable to check cycle-stretch\n");
+
+ rc = smb1360_enable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't request FG access rc=%d\n", rc);
+ return rc;
+ }
+
+ for (i = 0; i < (FG_SCRATCH_PAD_MAX / SMB1360_I2C_READ_LENGTH); i++) {
+ j = i * SMB1360_I2C_READ_LENGTH;
+ rc = smb1360_read_bytes(chip, FG_SCRATCH_PAD_BASE_REG + j,
+ ®[j], SMB1360_I2C_READ_LENGTH);
+ if (rc) {
+ pr_err("Couldn't read scratch registers rc=%d\n", rc);
+ break;
+ }
+ }
+
+ j = i * SMB1360_I2C_READ_LENGTH;
+ rem_length = (FG_SCRATCH_PAD_MAX % SMB1360_I2C_READ_LENGTH);
+ if (rem_length) {
+ rc = smb1360_read_bytes(chip, FG_SCRATCH_PAD_BASE_REG + j,
+ ®[j], rem_length);
+ if (rc)
+ pr_err("Couldn't read scratch registers rc=%d\n", rc);
+ }
+
+ rc = smb1360_disable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't disable FG access rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smb1360_check_cycle_stretch(chip);
+ if (rc)
+ pr_err("Unable to check cycle-stretch\n");
+
+
+ seq_puts(m, "FG scratch-pad registers\n");
+ for (i = 0; i < ARRAY_SIZE(fg_scratch_pad); i++)
+ seq_printf(m, "\t%s = %x\n", fg_scratch_pad[i].param_name,
+ fg_scratch_pad[i].calc_func(®[fg_scratch_pad[i].index]));
+
+ rem_length = LAST_FG_CFG_REG - FIRST_FG_CFG_REG + 1;
+ rc = smb1360_read_bytes(chip, FIRST_FG_CFG_REG,
+ ®[0], rem_length);
+ if (rc)
+ pr_err("Couldn't read config registers rc=%d\n", rc);
+
+ seq_puts(m, "FG config registers\n");
+ for (i = 0; i < ARRAY_SIZE(fg_cfg); i++)
+ seq_printf(m, "\t%s = %x\n", fg_cfg[i].param_name,
+ fg_cfg[i].calc_func(®[fg_cfg[i].index]));
+
+ rem_length = LAST_FG_SHDW_REG - FIRST_FG_SHDW_REG + 1;
+ rc = smb1360_read_bytes(chip, FIRST_FG_SHDW_REG,
+ ®[0], rem_length);
+ if (rc)
+ pr_err("Couldn't read shadow registers rc=%d\n", rc);
+
+ seq_puts(m, "FG shadow registers\n");
+ for (i = 0; i < ARRAY_SIZE(fg_shdw); i++)
+ seq_printf(m, "\t%s = %x\n", fg_shdw[i].param_name,
+ fg_shdw[i].calc_func(®[fg_shdw[i].index]));
+
+ return rc;
+}
+
+static int fg_regs_open(struct inode *inode, struct file *file)
+{
+ struct smb1360_chip *chip = inode->i_private;
+
+ return single_open(file, show_fg_regs, chip);
+}
+
+static const struct file_operations fg_regs_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = fg_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int smb1360_otg_regulator_enable(struct regulator_dev *rdev)
+{
+ int rc = 0;
+ struct smb1360_chip *chip = rdev_get_drvdata(rdev);
+
+ rc = smb1360_masked_write(chip, CMD_CHG_REG, CMD_OTG_EN_BIT,
+ CMD_OTG_EN_BIT);
+ if (rc) {
+ pr_err("Couldn't enable OTG mode rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_debug("OTG mode enabled\n");
+ /* Enable current gain configuration */
+ mutex_lock(&chip->otp_gain_lock);
+ if (chip->otg_fet_present) {
+ /* Enable FET */
+ gpio_set_value(chip->otg_fet_enable_gpio, 0);
+ rc = smb1360_otp_gain_config(chip, 3);
+ if (rc < 0)
+ pr_err("Couldn't config OTP gain config rc=%d\n", rc);
+ else
+ chip->fet_gain_enabled = true;
+ }
+ mutex_unlock(&chip->otp_gain_lock);
+
+ return rc;
+}
+
+static int smb1360_otg_regulator_disable(struct regulator_dev *rdev)
+{
+ int rc = 0;
+ struct smb1360_chip *chip = rdev_get_drvdata(rdev);
+
+ rc = smb1360_otg_disable(chip);
+ if (rc)
+ pr_err("Couldn't disable OTG regulator rc=%d\n", rc);
+
+ pr_debug("OTG mode disabled\n");
+ return rc;
+}
+
+static int smb1360_otg_regulator_is_enable(struct regulator_dev *rdev)
+{
+ u8 reg = 0;
+ int rc = 0;
+ struct smb1360_chip *chip = rdev_get_drvdata(rdev);
+
+ rc = smb1360_read(chip, CMD_CHG_REG, ®);
+ if (rc) {
+ pr_err("Couldn't read OTG enable bit rc=%d\n", rc);
+ return rc;
+ }
+
+ return (reg & CMD_OTG_EN_BIT) ? 1 : 0;
+}
+
+static struct regulator_ops smb1360_otg_reg_ops = {
+ .enable = smb1360_otg_regulator_enable,
+ .disable = smb1360_otg_regulator_disable,
+ .is_enabled = smb1360_otg_regulator_is_enable,
+};
+
+static int smb1360_regulator_init(struct smb1360_chip *chip)
+{
+ int rc = 0;
+ struct regulator_config cfg = {};
+
+ chip->otg_vreg.rdesc.owner = THIS_MODULE;
+ chip->otg_vreg.rdesc.type = REGULATOR_VOLTAGE;
+ chip->otg_vreg.rdesc.ops = &smb1360_otg_reg_ops;
+ chip->otg_vreg.rdesc.of_match = chip->dev->of_node->name;
+ chip->otg_vreg.rdesc.name = chip->dev->of_node->name;
+
+ cfg.dev = chip->dev;
+ cfg.driver_data = chip;
+ cfg.of_node = chip->dev->of_node;
+
+ chip->otg_vreg.rdev = regulator_register(
+ &chip->otg_vreg.rdesc, &cfg);
+ if (IS_ERR(chip->otg_vreg.rdev)) {
+ rc = PTR_ERR(chip->otg_vreg.rdev);
+ chip->otg_vreg.rdev = NULL;
+ if (rc != -EPROBE_DEFER)
+ dev_err(chip->dev,
+ "OTG reg failed, rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
+static int smb1360_check_batt_profile(struct smb1360_chip *chip)
+{
+ int rc, i, timeout = 50;
+ u8 reg = 0, loaded_profile, new_profile = 0, bid_mask;
+
+ if (!chip->connected_rid) {
+ pr_debug("Skip batt-profile loading connected_rid=%d\n",
+ chip->connected_rid);
+ return 0;
+ }
+
+ rc = smb1360_read(chip, SHDW_FG_BATT_STATUS, ®);
+ if (rc) {
+ pr_err("Couldn't read FG_BATT_STATUS rc=%d\n", rc);
+ return rc;
+ }
+
+ loaded_profile = !!(reg & BATTERY_PROFILE_BIT) ?
+ BATTERY_PROFILE_B : BATTERY_PROFILE_A;
+
+ pr_debug("fg_batt_status=%x loaded_profile=%d\n", reg, loaded_profile);
+
+ for (i = 0; i < BATTERY_PROFILE_MAX; i++) {
+ pr_debug("profile=%d profile_rid=%d connected_rid=%d\n", i,
+ chip->profile_rid[i],
+ chip->connected_rid);
+ if (abs(chip->profile_rid[i] - chip->connected_rid) <
+ (div_u64(chip->connected_rid, 10)))
+ break;
+ }
+
+ if (i == BATTERY_PROFILE_MAX) {
+ pr_err("None of the battery-profiles match the connected-RID\n");
+ return 0;
+ }
+
+ if (i == loaded_profile) {
+ pr_debug("Loaded Profile-RID == connected-RID\n");
+ return 0;
+ }
+
+ new_profile = (loaded_profile == BATTERY_PROFILE_A) ?
+ BATTERY_PROFILE_B : BATTERY_PROFILE_A;
+ bid_mask = (new_profile == BATTERY_PROFILE_A) ?
+ BATT_PROFILEA_MASK : BATT_PROFILEB_MASK;
+ pr_info("Loaded Profile-RID != connected-RID, switch-profile old_profile=%d new_profile=%d\n",
+ loaded_profile, new_profile);
+
+ /* set the BID mask */
+ rc = smb1360_masked_write(chip, CFG_FG_BATT_CTRL_REG,
+ BATT_PROFILE_SELECT_MASK, bid_mask);
+ if (rc) {
+ pr_err("Couldn't reset battery-profile rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smb1360_enable_fg_access(chip);
+ if (rc) {
+ pr_err("FG access timed-out, rc = %d\n", rc);
+ return rc;
+ }
+ /* delay after handshaking for profile-switch to continue */
+ msleep(1500);
+
+ rc = smb1360_force_fg_reset(chip);
+ if (rc) {
+ pr_err("Couldn't reset FG rc=%d\n", rc);
+ goto restore_fg;
+ }
+
+ rc = smb1360_disable_fg_access(chip);
+ if (rc) {
+ pr_err("disable FG access failed, rc = %d\n", rc);
+ return rc;
+ }
+
+ timeout = 10;
+ while (timeout) {
+ /* delay for profile to change */
+ msleep(500);
+ rc = smb1360_read(chip, SHDW_FG_BATT_STATUS, ®);
+ if (rc) {
+ pr_err("Could't read FG_BATT_STATUS rc=%d\n", rc);
+ return rc;
+ }
+
+ reg = !!(reg & BATTERY_PROFILE_BIT);
+ if (reg == new_profile) {
+ pr_info("New profile=%d loaded\n", new_profile);
+ break;
+ }
+ timeout--;
+ }
+
+ if (!timeout) {
+ pr_err("New profile could not be loaded\n");
+ return -EBUSY;
+ }
+
+ return 0;
+
+restore_fg:
+ smb1360_disable_fg_access(chip);
+ return rc;
+}
+
+#define UPDATE_IRQ_STAT(irq_reg, value) \
+ handlers[irq_reg - IRQ_A_REG].prev_val = value
+
+static int determine_initial_status(struct smb1360_chip *chip)
+{
+ int rc;
+ u8 reg = 0;
+
+ /*
+ * It is okay to read the IRQ status as the irq's are
+ * not registered yet.
+ */
+ chip->batt_present = true;
+ rc = smb1360_read(chip, IRQ_B_REG, ®);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read IRQ_B_REG rc = %d\n", rc);
+ return rc;
+ }
+ UPDATE_IRQ_STAT(IRQ_B_REG, reg);
+
+ if (reg & IRQ_B_BATT_TERMINAL_BIT || reg & IRQ_B_BATT_MISSING_BIT)
+ chip->batt_present = false;
+
+ rc = smb1360_read(chip, IRQ_C_REG, ®);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't read IRQ_C_REG rc = %d\n", rc);
+ return rc;
+ }
+ UPDATE_IRQ_STAT(IRQ_C_REG, reg);
+
+ if (reg & IRQ_C_CHG_TERM)
+ chip->batt_full = true;
+
+ rc = smb1360_read(chip, IRQ_A_REG, ®);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read irq A rc = %d\n", rc);
+ return rc;
+ }
+ UPDATE_IRQ_STAT(IRQ_A_REG, reg);
+
+ if (chip->workaround_flags & WRKRND_HARD_JEITA) {
+ schedule_delayed_work(&chip->jeita_work, 0);
+ } else {
+ if (reg & IRQ_A_HOT_HARD_BIT)
+ chip->batt_hot = true;
+ if (reg & IRQ_A_COLD_HARD_BIT)
+ chip->batt_cold = true;
+ if (!chip->config_hard_thresholds) {
+ if (reg & IRQ_A_HOT_SOFT_BIT)
+ chip->batt_warm = true;
+ if (reg & IRQ_A_COLD_SOFT_BIT)
+ chip->batt_cool = true;
+ }
+ }
+
+ rc = smb1360_read(chip, IRQ_E_REG, ®);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read irq E rc = %d\n", rc);
+ return rc;
+ }
+ UPDATE_IRQ_STAT(IRQ_E_REG, reg);
+
+ /* Check usb charger presence and notify */
+ chip->usb_present = (reg & IRQ_E_USBIN_UV_BIT) ? false : true;
+ /* USB removed */
+ if (!chip->usb_present)
+ extcon_set_cable_state_(chip->extcon, EXTCON_USB, false);
+ /* USB inserted */
+ else
+ extcon_set_cable_state_(chip->extcon, EXTCON_USB, true);
+
+ power_supply_changed(chip->usb_psy);
+ return 0;
+}
+
+static int smb1360_fg_config(struct smb1360_chip *chip)
+{
+ int rc = 0, temp, fcc_mah;
+ u8 reg = 0, reg2[2];
+
+ if (chip->fg_reset_at_pon) {
+ int v_predicted, v_now;
+
+ rc = smb1360_enable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't enable FG access rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smb1360_read_bytes(chip, VOLTAGE_PREDICTED_REG, reg2, 2);
+ if (rc) {
+ pr_err("Failed to read VOLTAGE_PREDICTED rc=%d\n", rc);
+ goto disable_fg_reset;
+ }
+ v_predicted = (reg2[1] << 8) | reg2[0];
+ v_predicted = div_u64(v_predicted * 5000, 0x7FFF);
+
+ rc = smb1360_read_bytes(chip, SHDW_FG_VTG_NOW, reg2, 2);
+ if (rc) {
+ pr_err("Failed to read SHDW_FG_VTG_NOW rc=%d\n", rc);
+ goto disable_fg_reset;
+ }
+ v_now = (reg2[1] << 8) | reg2[0];
+ v_now = div_u64(v_now * 5000, 0x7FFF);
+
+ pr_debug("v_predicted=%d v_now=%d reset_threshold=%d\n",
+ v_predicted, v_now, chip->fg_reset_threshold_mv);
+
+ /*
+ * Reset FG if the predicted voltage is off wrt
+ * the real-time voltage.
+ */
+ temp = abs(v_predicted - v_now);
+ if (temp >= chip->fg_reset_threshold_mv) {
+ pr_info("Resetting FG - v_delta=%d threshold=%d\n",
+ temp, chip->fg_reset_threshold_mv);
+ /* delay for the FG access to settle */
+ msleep(1500);
+ rc = smb1360_force_fg_reset(chip);
+ if (rc) {
+ pr_err("Couldn't reset FG rc=%d\n", rc);
+ goto disable_fg_reset;
+ }
+ }
+disable_fg_reset:
+ smb1360_disable_fg_access(chip);
+ }
+
+ /*
+ * The below IRQ thresholds are not accessible in REV_1
+ * of SMB1360.
+ */
+ if (!(chip->workaround_flags & WRKRND_FG_CONFIG_FAIL)) {
+ if (chip->delta_soc != -EINVAL) {
+ reg = abs(((chip->delta_soc * MAX_8_BITS) / 100) - 1);
+ pr_debug("delta_soc=%d reg=%x\n", chip->delta_soc, reg);
+ rc = smb1360_write(chip, SOC_DELTA_REG, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't write to SOC_DELTA_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ if (chip->soc_min != -EINVAL) {
+ if (is_between(chip->soc_min, 0, 100)) {
+ reg = DIV_ROUND_UP(chip->soc_min * MAX_8_BITS,
+ 100);
+ pr_debug("soc_min=%d reg=%x\n",
+ chip->soc_min, reg);
+ rc = smb1360_write(chip, SOC_MIN_REG, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't write to SOC_MIN_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ }
+
+ if (chip->soc_max != -EINVAL) {
+ if (is_between(chip->soc_max, 0, 100)) {
+ reg = DIV_ROUND_UP(chip->soc_max * MAX_8_BITS,
+ 100);
+ pr_debug("soc_max=%d reg=%x\n",
+ chip->soc_max, reg);
+ rc = smb1360_write(chip, SOC_MAX_REG, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't write to SOC_MAX_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ }
+
+ if (chip->voltage_min_mv != -EINVAL) {
+ temp = (chip->voltage_min_mv - 2500) * MAX_8_BITS;
+ reg = DIV_ROUND_UP(temp, 2500);
+ pr_debug("voltage_min=%d reg=%x\n",
+ chip->voltage_min_mv, reg);
+ rc = smb1360_write(chip, VTG_MIN_REG, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't write to VTG_MIN_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ if (chip->voltage_empty_mv != -EINVAL) {
+ temp = (chip->voltage_empty_mv - 2500) * MAX_8_BITS;
+ reg = DIV_ROUND_UP(temp, 2500);
+ pr_debug("voltage_empty=%d reg=%x\n",
+ chip->voltage_empty_mv, reg);
+ rc = smb1360_write(chip, VTG_EMPTY_REG, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't write to VTG_EMPTY_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ }
+
+ /* scratch-pad register config */
+ if (chip->batt_capacity_mah != -EINVAL
+ || chip->v_cutoff_mv != -EINVAL
+ || chip->fg_iterm_ma != -EINVAL
+ || chip->fg_ibatt_standby_ma != -EINVAL
+ || chip->fg_thermistor_c1_coeff != -EINVAL
+ || chip->fg_cc_to_cv_mv != -EINVAL
+ || chip->fg_auto_recharge_soc != -EINVAL) {
+
+ rc = smb1360_enable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't enable FG access rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Update battery capacity */
+ if (chip->batt_capacity_mah != -EINVAL) {
+ rc = smb1360_read_bytes(chip, ACTUAL_CAPACITY_REG,
+ reg2, 2);
+ if (rc) {
+ pr_err("Failed to read ACTUAL CAPACITY rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+ fcc_mah = (reg2[1] << 8) | reg2[0];
+ if (fcc_mah == chip->batt_capacity_mah) {
+ pr_debug("battery capacity correct\n");
+ } else {
+ /* Update the battery capacity */
+ reg2[1] =
+ (chip->batt_capacity_mah & 0xFF00) >> 8;
+ reg2[0] = (chip->batt_capacity_mah & 0xFF);
+ rc = smb1360_write_bytes(chip,
+ ACTUAL_CAPACITY_REG, reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write batt-capacity rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+ rc = smb1360_write_bytes(chip,
+ NOMINAL_CAPACITY_REG, reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write batt-capacity rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+
+ /* Update CC to SOC COEFF */
+ if (chip->cc_soc_coeff != -EINVAL) {
+ reg2[1] =
+ (chip->cc_soc_coeff & 0xFF00) >> 8;
+ reg2[0] = (chip->cc_soc_coeff & 0xFF);
+ rc = smb1360_write_bytes(chip,
+ CC_TO_SOC_COEFF, reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write cc_soc_coeff rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+ }
+ }
+ }
+
+ /* Update cutoff voltage for SOC = 0 */
+ if (chip->v_cutoff_mv != -EINVAL) {
+ temp = (u16) div_u64(chip->v_cutoff_mv * 0x7FFF, 5000);
+ reg2[1] = (temp & 0xFF00) >> 8;
+ reg2[0] = temp & 0xFF;
+ rc = smb1360_write_bytes(chip, FG_SYS_CUTOFF_V_REG,
+ reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write cutoff_mv rc=%d\n", rc);
+ goto disable_fg;
+ }
+ }
+
+ /*
+ * Update FG iterm for SOC = 100, this value is always assumed
+ * to be -ve
+ */
+ if (chip->fg_iterm_ma != -EINVAL) {
+ int iterm = chip->fg_iterm_ma * -1;
+
+ temp = (s16) div_s64(iterm * 0x7FFF, 2500);
+ reg2[1] = (temp & 0xFF00) >> 8;
+ reg2[0] = temp & 0xFF;
+ rc = smb1360_write_bytes(chip, FG_ITERM_REG,
+ reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write fg_iterm rc=%d\n", rc);
+ goto disable_fg;
+ }
+ }
+
+ /*
+ * Update FG iterm standby for SOC = 0, this value is always
+ * assumed to be +ve
+ */
+ if (chip->fg_ibatt_standby_ma != -EINVAL) {
+ int iterm = chip->fg_ibatt_standby_ma;
+
+ temp = (u16) div_u64(iterm * 0x7FFF, 2500);
+ reg2[1] = (temp & 0xFF00) >> 8;
+ reg2[0] = temp & 0xFF;
+ rc = smb1360_write_bytes(chip, FG_IBATT_STANDBY_REG,
+ reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write fg_iterm rc=%d\n", rc);
+ goto disable_fg;
+ }
+ }
+
+ /* Update CC_to_CV voltage threshold */
+ if (chip->fg_cc_to_cv_mv != -EINVAL) {
+ temp = (u16) div_u64(chip->fg_cc_to_cv_mv * 0x7FFF,
+ 5000);
+ reg2[1] = (temp & 0xFF00) >> 8;
+ reg2[0] = temp & 0xFF;
+ rc = smb1360_write_bytes(chip, FG_CC_TO_CV_V_REG,
+ reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write cc_to_cv_mv rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+ }
+
+ /* Update the thermistor c1 coefficient */
+ if (chip->fg_thermistor_c1_coeff != -EINVAL) {
+ reg2[1] = (chip->fg_thermistor_c1_coeff & 0xFF00) >> 8;
+ reg2[0] = (chip->fg_thermistor_c1_coeff & 0xFF);
+ rc = smb1360_write_bytes(chip, FG_THERM_C1_COEFF_REG,
+ reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write thermistor_c1_coeff rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+ }
+
+ /* Update SoC based resume charging threshold */
+ if (chip->fg_auto_recharge_soc != -EINVAL) {
+ rc = smb1360_masked_write(chip, CFG_CHG_FUNC_CTRL_REG,
+ CHG_RECHG_THRESH_FG_SRC_BIT,
+ CHG_RECHG_THRESH_FG_SRC_BIT);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't write to CFG_CHG_FUNC_CTRL_REG rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+
+ reg = DIV_ROUND_UP(chip->fg_auto_recharge_soc *
+ MAX_8_BITS, 100);
+ pr_debug("fg_auto_recharge_soc=%d reg=%x\n",
+ chip->fg_auto_recharge_soc, reg);
+ rc = smb1360_write(chip, FG_AUTO_RECHARGE_SOC, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't write to FG_AUTO_RECHARGE_SOC rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+ }
+
+disable_fg:
+ /* disable FG access */
+ smb1360_disable_fg_access(chip);
+ }
+
+ return rc;
+}
+
+static void smb1360_check_feature_support(struct smb1360_chip *chip)
+{
+
+ if (is_usb100_broken(chip)) {
+ pr_debug("USB100 is not supported\n");
+ chip->workaround_flags |= WRKRND_USB100_FAIL;
+ }
+
+ /*
+ * FG Configuration
+ *
+ * The REV_1 of the chip does not allow access to
+ * FG config registers (20-2FH). Set the workaround flag.
+ * Also, the battery detection does not work when the DCIN is absent,
+ * add a workaround flag for it.
+ */
+ if (chip->revision == SMB1360_REV_1) {
+ pr_debug("FG config and Battery detection is not supported\n");
+ chip->workaround_flags |=
+ WRKRND_FG_CONFIG_FAIL | WRKRND_BATT_DET_FAIL;
+ }
+}
+
+static int smb1360_enable(struct smb1360_chip *chip, bool enable)
+{
+ int rc = 0;
+ u8 val = 0, shdn_cmd_polar;
+
+ rc = smb1360_read(chip, SHDN_CTRL_REG, &val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read 0x1A reg rc = %d\n", rc);
+ return rc;
+ }
+
+ /* Ignore if a CMD based shutdown is not enabled */
+ if (!(val & SHDN_CMD_USE_BIT)) {
+ pr_debug("SMB not configured for CMD based shutdown\n");
+ return 0;
+ }
+
+ shdn_cmd_polar = !!(val & SHDN_CMD_POLARITY_BIT);
+ val = (shdn_cmd_polar ^ enable) ? SHDN_CMD_BIT : 0;
+
+ pr_debug("enable=%d shdn_polarity=%d value=%d\n", enable,
+ shdn_cmd_polar, val);
+
+ rc = smb1360_masked_write(chip, CMD_IL_REG, SHDN_CMD_BIT, val);
+ if (rc < 0)
+ pr_err("Couldn't shutdown smb1360 rc = %d\n", rc);
+
+ return rc;
+}
+
+static inline int smb1360_poweroff(struct smb1360_chip *chip)
+{
+ pr_debug("power off smb1360\n");
+ return smb1360_enable(chip, false);
+}
+
+static inline int smb1360_poweron(struct smb1360_chip *chip)
+{
+ pr_debug("power on smb1360\n");
+ return smb1360_enable(chip, true);
+}
+
+static int smb1360_jeita_init(struct smb1360_chip *chip)
+{
+ int rc = 0;
+ int temp;
+
+ if (chip->config_hard_thresholds) {
+ if (chip->soft_jeita_supported) {
+ chip->workaround_flags |= WRKRND_HARD_JEITA;
+ rc = smb1360_set_soft_jeita_threshold(chip,
+ chip->cool_bat_decidegc, chip->warm_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't set jeita threshold\n");
+ return rc;
+ }
+ } else {
+ rc = smb1360_set_soft_jeita_threshold(chip,
+ chip->cold_bat_decidegc, chip->hot_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't set jeita threshold\n");
+ return rc;
+ }
+ }
+ } else {
+ if (chip->soft_jeita_supported) {
+ temp = min(chip->warm_bat_ma, chip->cool_bat_ma);
+ rc = smb1360_set_jeita_comp_curr(chip, temp);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set comp current\n");
+ return rc;
+ }
+
+ temp = (chip->vfloat_mv - chip->warm_bat_mv) / 10;
+ rc = smb1360_masked_write(chip, CFG_FVC_REG,
+ FLT_VTG_COMP_MASK, temp);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set VFLT compensation = %d",
+ rc);
+ return rc;
+ }
+
+ rc = smb1360_set_soft_jeita_threshold(chip,
+ chip->cool_bat_decidegc, chip->warm_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't set jeita threshold\n");
+ return rc;
+ }
+
+ rc = smb1360_soft_jeita_comp_enable(chip, true);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't enable jeita\n");
+ return rc;
+ }
+ }
+ }
+
+ return rc;
+}
+
+static int smb1360_otp_gain_init(struct smb1360_chip *chip)
+{
+ int rc = 0, gain_factor;
+ bool otp_gain_config = false;
+
+ if (chip->rsense_10mohm) {
+ gain_factor = 2;
+ otp_gain_config = true;
+ }
+
+ mutex_lock(&chip->otp_gain_lock);
+ if (chip->otg_fet_present) {
+ /*
+ * Reset current gain to the default value if OTG
+ * is not enabled
+ */
+ if (!chip->fet_gain_enabled) {
+ otp_gain_config = true;
+ gain_factor = 0;
+ }
+ }
+
+ if (otp_gain_config) {
+ rc = smb1360_otp_gain_config(chip, gain_factor);
+ if (rc < 0)
+ pr_err("Couldn't config OTP gain rc=%d\n", rc);
+ }
+ mutex_unlock(&chip->otp_gain_lock);
+
+ return rc;
+}
+
+static int smb1360_hw_init(struct smb1360_chip *chip)
+{
+ int rc;
+ int i;
+ u8 reg, mask;
+
+ smb1360_check_feature_support(chip);
+
+ rc = smb1360_enable_volatile_writes(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't configure for volatile rc = %d\n",
+ rc);
+ return rc;
+ }
+
+ /* Bring SMB1360 out of shutdown, if it was enabled by default */
+ rc = smb1360_poweron(chip);
+ if (rc < 0) {
+ pr_err("smb1360 power on failed\n");
+ return rc;
+ }
+
+ /*
+ * A 2 seconds delay is mandatory after bringing the chip out
+ * of shutdown. This guarantees that FG is in a proper state.
+ */
+ schedule_delayed_work(&chip->delayed_init_work,
+ msecs_to_jiffies(SMB1360_POWERON_DELAY_MS));
+
+ /*
+ * set chg en by cmd register, set chg en by writing bit 1,
+ * enable auto pre to fast
+ */
+ rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG,
+ CHG_EN_BY_PIN_BIT
+ | CHG_EN_ACTIVE_LOW_BIT
+ | PRE_TO_FAST_REQ_CMD_BIT,
+ 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set CFG_CHG_MISC_REG rc=%d\n", rc);
+ return rc;
+ }
+
+ /* USB/AC pin settings */
+ rc = smb1360_masked_write(chip, CFG_BATT_CHG_ICL_REG,
+ AC_INPUT_ICL_PIN_BIT
+ | AC_INPUT_PIN_HIGH_BIT
+ | RESET_STATE_USB_500,
+ AC_INPUT_PIN_HIGH_BIT
+ | RESET_STATE_USB_500);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set CFG_BATT_CHG_ICL_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* AICL enable and set input-uv glitch flt to 20ms*/
+ reg = AICL_ENABLED_BIT | INPUT_UV_GLITCH_FLT_20MS_BIT;
+ rc = smb1360_masked_write(chip, CFG_GLITCH_FLT_REG, reg, reg);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set CFG_GLITCH_FLT_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* set the float voltage */
+ if (chip->vfloat_mv != -EINVAL) {
+ rc = smb1360_float_voltage_set(chip, chip->vfloat_mv);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't set float voltage rc = %d\n", rc);
+ return rc;
+ }
+ }
+
+ /* set iterm */
+ if (chip->iterm_ma != -EINVAL) {
+ if (chip->iterm_disabled) {
+ dev_err(chip->dev, "Error: Both iterm_disabled and iterm_ma set\n");
+ return -EINVAL;
+ }
+
+ if (chip->rsense_10mohm)
+ chip->iterm_ma /= 2;
+
+ if (chip->iterm_ma < 25)
+ reg = CHG_ITERM_25MA;
+ else if (chip->iterm_ma > 200)
+ reg = CHG_ITERM_200MA;
+ else
+ reg = DIV_ROUND_UP(chip->iterm_ma, 25) - 1;
+
+ rc = smb1360_masked_write(chip, CFG_BATT_CHG_REG,
+ CHG_ITERM_MASK, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set iterm rc = %d\n", rc);
+ return rc;
+ }
+
+ rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG,
+ CHG_CURR_TERM_DIS_BIT, 0);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't enable iterm rc = %d\n", rc);
+ return rc;
+ }
+ } else if (chip->iterm_disabled) {
+ rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG,
+ CHG_CURR_TERM_DIS_BIT,
+ CHG_CURR_TERM_DIS_BIT);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set iterm rc = %d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ /* set the safety time voltage */
+ if (chip->safety_time != -EINVAL) {
+ if (chip->safety_time == 0) {
+ /* safety timer disabled */
+ rc = smb1360_masked_write(chip, CFG_SFY_TIMER_CTRL_REG,
+ SAFETY_TIME_DISABLE_BIT, SAFETY_TIME_DISABLE_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't disable safety timer rc = %d\n",
+ rc);
+ return rc;
+ }
+ } else {
+ for (i = 0; i < ARRAY_SIZE(chg_time); i++) {
+ if (chip->safety_time <= chg_time[i]) {
+ reg = i << SAFETY_TIME_MINUTES_SHIFT;
+ break;
+ }
+ }
+ rc = smb1360_masked_write(chip, CFG_SFY_TIMER_CTRL_REG,
+ SAFETY_TIME_DISABLE_BIT | SAFETY_TIME_MINUTES_MASK,
+ reg);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't set safety timer rc = %d\n", rc);
+ return rc;
+ }
+ }
+ }
+
+ /* configure resume threshold, auto recharge and charge inhibit */
+ if (chip->resume_delta_mv != -EINVAL) {
+ if (chip->recharge_disabled && chip->chg_inhibit_disabled) {
+ dev_err(chip->dev,
+ "Error: Both recharge_disabled and recharge_mv set\n");
+ return -EINVAL;
+ }
+ rc = smb1360_recharge_threshold_set(chip,
+ chip->resume_delta_mv);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't set rechg thresh rc = %d\n", rc);
+ return rc;
+ }
+ }
+
+ rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG,
+ CFG_AUTO_RECHG_DIS_BIT,
+ chip->recharge_disabled ?
+ CFG_AUTO_RECHG_DIS_BIT : 0);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set rechg-cfg rc = %d\n", rc);
+ return rc;
+ }
+ rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG,
+ CFG_CHG_INHIBIT_EN_BIT,
+ chip->chg_inhibit_disabled ?
+ 0 : CFG_CHG_INHIBIT_EN_BIT);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set chg_inhibit rc = %d\n", rc);
+ return rc;
+ }
+
+ rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG,
+ CFG_BAT_OV_ENDS_CHG_CYC,
+ chip->ov_ends_chg_cycle_disabled ?
+ 0 : CFG_BAT_OV_ENDS_CHG_CYC);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set bat_ov_ends_charge rc = %d\n"
+ , rc);
+ return rc;
+ }
+
+ /* battery missing detection */
+ rc = smb1360_masked_write(chip, CFG_BATT_MISSING_REG,
+ BATT_MISSING_SRC_THERM_BIT,
+ BATT_MISSING_SRC_THERM_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set batt_missing config = %d\n",
+ rc);
+ return rc;
+ }
+
+ rc = smb1360_jeita_init(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't init jeita, rc = %d\n", rc);
+ return rc;
+ }
+
+ /* interrupt enabling - active low */
+ if (chip->client->irq) {
+ mask = CHG_STAT_IRQ_ONLY_BIT
+ | CHG_STAT_ACTIVE_HIGH_BIT
+ | CHG_STAT_DISABLE_BIT
+ | CHG_TEMP_CHG_ERR_BLINK_BIT;
+
+ if (!chip->pulsed_irq)
+ reg = CHG_STAT_IRQ_ONLY_BIT;
+ else
+ reg = CHG_TEMP_CHG_ERR_BLINK_BIT;
+ rc = smb1360_masked_write(chip, CFG_STAT_CTRL_REG, mask, reg);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set irq config rc = %d\n",
+ rc);
+ return rc;
+ }
+
+ /* enabling only interesting interrupts */
+ rc = smb1360_write(chip, IRQ_CFG_REG,
+ IRQ_BAT_HOT_COLD_HARD_BIT
+ | IRQ_BAT_HOT_COLD_SOFT_BIT
+ | IRQ_INTERNAL_TEMPERATURE_BIT
+ | IRQ_DCIN_UV_BIT
+ | IRQ_AICL_DONE_BIT);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set irq1 config rc = %d\n",
+ rc);
+ return rc;
+ }
+
+ rc = smb1360_write(chip, IRQ2_CFG_REG,
+ IRQ2_SAFETY_TIMER_BIT
+ | IRQ2_CHG_ERR_BIT
+ | IRQ2_CHG_PHASE_CHANGE_BIT
+ | IRQ2_POWER_OK_BIT
+ | IRQ2_BATT_MISSING_BIT
+ | IRQ2_VBAT_LOW_BIT);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set irq2 config rc = %d\n",
+ rc);
+ return rc;
+ }
+
+ rc = smb1360_write(chip, IRQ3_CFG_REG,
+ IRQ3_FG_ACCESS_OK_BIT
+ | IRQ3_SOC_CHANGE_BIT
+ | IRQ3_SOC_MIN_BIT
+ | IRQ3_SOC_MAX_BIT
+ | IRQ3_SOC_EMPTY_BIT
+ | IRQ3_SOC_FULL_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set irq3 enable rc = %d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ /* batt-id configuration */
+ if (chip->batt_id_disabled) {
+ mask = BATT_ID_ENABLED_BIT | CHG_BATT_ID_FAIL;
+ reg = CHG_BATT_ID_FAIL;
+ rc = smb1360_masked_write(chip, CFG_FG_BATT_CTRL_REG,
+ mask, reg);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set batt_id_reg rc = %d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ /* USB OTG current limit configuration */
+ if (chip->otg_batt_curr_limit != -EINVAL) {
+ for (i = 0; i < ARRAY_SIZE(otg_curr_ma); i++) {
+ if (otg_curr_ma[i] >= chip->otg_batt_curr_limit)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(otg_curr_ma))
+ i = i - 1;
+
+ rc = smb1360_masked_write(chip, CFG_BATT_CHG_REG,
+ OTG_CURRENT_MASK,
+ i << OTG_CURRENT_SHIFT);
+ if (rc)
+ pr_err("Couldn't set OTG current limit, rc = %d\n", rc);
+ }
+
+ rc = smb1360_charging_disable(chip, USER, !!chip->charging_disabled);
+ if (rc)
+ dev_err(chip->dev, "Couldn't '%s' charging rc = %d\n",
+ chip->charging_disabled ? "disable" : "enable", rc);
+
+ if (chip->parallel_charging) {
+ rc = smb1360_parallel_charger_enable(chip, PARALLEL_USER,
+ !chip->charging_disabled);
+ if (rc)
+ dev_err(chip->dev, "Couldn't '%s' parallel-charging rc = %d\n",
+ chip->charging_disabled ? "disable" : "enable", rc);
+ }
+
+ return rc;
+}
+
+static int smb1360_delayed_hw_init(struct smb1360_chip *chip)
+{
+ int rc;
+
+ pr_debug("delayed hw init start!\n");
+
+ if (chip->otp_hard_jeita_config) {
+ rc = smb1360_hard_jeita_otp_init(chip);
+ if (rc) {
+ pr_err("Unable to change the OTP hard jeita, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ rc = smb1360_check_batt_profile(chip);
+ if (rc) {
+ pr_err("Unable to modify battery profile, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smb1360_otp_gain_init(chip);
+ if (rc) {
+ pr_err("Unable to config otp gain, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smb1360_fg_config(chip);
+ if (rc) {
+ pr_err("Couldn't configure FG rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smb1360_check_cycle_stretch(chip);
+ if (rc) {
+ pr_err("Unable to check cycle-stretch\n");
+ return rc;
+ }
+
+ pr_debug("delayed hw init complete!\n");
+ return rc;
+}
+
+static void smb1360_delayed_init_work_fn(struct work_struct *work)
+{
+ int rc = 0;
+ struct smb1360_chip *chip = container_of(work, struct smb1360_chip,
+ delayed_init_work.work);
+
+ rc = smb1360_delayed_hw_init(chip);
+
+ if (!rc) {
+ /*
+ * If the delayed hw init successfully, update battery
+ * power_supply to make sure the correct SoC reported
+ * timely.
+ */
+ power_supply_changed(chip->batt_psy);
+ } else if (rc == -ETIMEDOUT) {
+ /*
+ * If the delayed hw init failed causing by waiting for
+ * FG access timed-out, force a FG reset and queue the
+ * worker again to retry the initialization.
+ */
+ pr_debug("delayed hw init timed-out, retry!");
+ rc = smb1360_force_fg_reset(chip);
+ if (rc) {
+ pr_err("couldn't reset FG, rc = %d\n", rc);
+ return;
+ }
+ schedule_delayed_work(&chip->delayed_init_work, 0);
+ } else {
+ pr_err("delayed hw init failed, rc=%d\n", rc);
+ }
+}
+
+static int smb_parse_batt_id(struct smb1360_chip *chip)
+{
+ int rc = 0, rpull = 0, vref = 0;
+ int64_t denom, batt_id_uv;
+ struct device_node *node = chip->dev->of_node;
+ struct qpnp_vadc_result result;
+
+ chip->vadc_dev = qpnp_get_vadc(chip->dev, "smb1360");
+ if (IS_ERR(chip->vadc_dev)) {
+ rc = PTR_ERR(chip->vadc_dev);
+ if (rc == -EPROBE_DEFER)
+ pr_err("vadc not found - defer rc=%d\n", rc);
+ else
+ pr_err("vadc property missing, rc=%d\n", rc);
+
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,profile-a-rid-kohm",
+ &chip->profile_rid[0]);
+ if (rc < 0) {
+ pr_err("Couldn't read profile-a-rid-kohm rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,profile-b-rid-kohm",
+ &chip->profile_rid[1]);
+ if (rc < 0) {
+ pr_err("Couldn't read profile-b-rid-kohm rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,batt-id-vref-uv", &vref);
+ if (rc < 0) {
+ pr_err("Couldn't read batt-id-vref-uv rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,batt-id-rpullup-kohm", &rpull);
+ if (rc < 0) {
+ pr_err("Couldn't read batt-id-rpullup-kohm rc=%d\n", rc);
+ return rc;
+ }
+
+ /* read battery ID */
+ rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX2_BAT_ID, &result);
+ if (rc) {
+ pr_err("error reading batt id channel = %d, rc = %d\n",
+ LR_MUX2_BAT_ID, rc);
+ return rc;
+ }
+ batt_id_uv = result.physical;
+
+ if (batt_id_uv == 0) {
+ /* vadc not correct or batt id line grounded, report 0 kohms */
+ pr_err("batt_id_uv = 0, batt-id grounded using same profile\n");
+ return 0;
+ }
+
+ denom = div64_s64(vref * 1000000LL, batt_id_uv) - 1000000LL;
+ if (denom == 0) {
+ /* batt id connector might be open, return 0 kohms */
+ return 0;
+ }
+ chip->connected_rid = div64_s64(rpull * 1000000LL + denom/2, denom);
+
+ pr_debug("batt_id_voltage = %lld, connected_rid = %d\n",
+ batt_id_uv, chip->connected_rid);
+
+ return 0;
+}
+
+/*
+ * Note the below:
+ * 1. if both qcom,soft-jeita-supported and qcom,config-hard-thresholds
+ * are not defined, SMB continues with default OTP configuration.
+ * 2. if both are enabled, the hard thresholds are modified.
+ * 3. if only qcom,config-hard-thresholds is defined, the soft JEITA is disabled
+ * 4. if only qcom,soft-jeita-supported is defined, the soft JEITA thresholds
+ * are modified.
+ */
+static int smb1360_parse_jeita_params(struct smb1360_chip *chip)
+{
+ int rc = 0;
+ struct device_node *node = chip->dev->of_node;
+ int temp[2];
+
+ if (of_property_read_bool(node, "qcom,config-hard-thresholds")) {
+ rc = of_property_read_u32(node,
+ "qcom,cold-bat-decidegc", &chip->cold_bat_decidegc);
+ if (rc) {
+ pr_err("cold_bat_decidegc property error, rc = %d\n",
+ rc);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(node,
+ "qcom,hot-bat-decidegc", &chip->hot_bat_decidegc);
+ if (rc) {
+ pr_err("hot_bat_decidegc property error, rc = %d\n",
+ rc);
+ return -EINVAL;
+ }
+
+ chip->config_hard_thresholds = true;
+ pr_debug("config_hard_thresholds = %d, cold_bat_decidegc = %d, hot_bat_decidegc = %d\n",
+ chip->config_hard_thresholds, chip->cold_bat_decidegc,
+ chip->hot_bat_decidegc);
+ } else if (of_property_read_bool(node, "qcom,otp-hard-jeita-config")) {
+ rc = of_property_read_u32(node, "qcom,otp-cold-bat-decidegc",
+ &chip->otp_cold_bat_decidegc);
+ if (rc) {
+ pr_err("otp-cold-bat-decidegc property error, rc = %d\n",
+ rc);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(node, "qcom,otp-hot-bat-decidegc",
+ &chip->otp_hot_bat_decidegc);
+
+ if (rc) {
+ pr_err("otp-hot-bat-decidegc property error, rc = %d\n",
+ rc);
+ return -EINVAL;
+ }
+
+ chip->otp_hard_jeita_config = true;
+ rc = of_property_read_u32_array(node,
+ "qcom,otp-hard-jeita-hysteresis", temp, 2);
+ if (rc) {
+ if (rc != -EINVAL) {
+ pr_err("read otp-hard-jeita-hysteresis failed, rc = %d\n",
+ rc);
+ return rc;
+ }
+ } else {
+ chip->cold_hysteresis = temp[0];
+ chip->hot_hysteresis = temp[1];
+ }
+
+ pr_debug("otp_hard_jeita_config = %d, otp_cold_bat_decidegc = %d\n"
+ "otp_hot_bat_decidegc = %d, cold_hysteresis = %d\n"
+ "hot_hysteresis = %d\n",
+ chip->otp_hard_jeita_config,
+ chip->otp_cold_bat_decidegc,
+ chip->otp_hot_bat_decidegc, chip->cold_hysteresis,
+ chip->hot_hysteresis);
+ }
+
+ if (of_property_read_bool(node, "qcom,soft-jeita-supported")) {
+ rc = of_property_read_u32(node, "qcom,warm-bat-decidegc",
+ &chip->warm_bat_decidegc);
+ if (rc) {
+ pr_err("warm_bat_decidegc property error, rc = %d\n",
+ rc);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(node, "qcom,cool-bat-decidegc",
+ &chip->cool_bat_decidegc);
+ if (rc) {
+ pr_err("cool_bat_decidegc property error, rc = %d\n",
+ rc);
+ return -EINVAL;
+ }
+ rc = of_property_read_u32(node, "qcom,cool-bat-mv",
+ &chip->cool_bat_mv);
+ if (rc) {
+ pr_err("cool_bat_mv property error, rc = %d\n", rc);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(node, "qcom,warm-bat-mv",
+ &chip->warm_bat_mv);
+ if (rc) {
+ pr_err("warm_bat_mv property error, rc = %d\n", rc);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(node, "qcom,cool-bat-ma",
+ &chip->cool_bat_ma);
+ if (rc) {
+ pr_err("cool_bat_ma property error, rc = %d\n", rc);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(node, "qcom,warm-bat-ma",
+ &chip->warm_bat_ma);
+
+ if (rc) {
+ pr_err("warm_bat_ma property error, rc = %d\n", rc);
+ return -EINVAL;
+ }
+
+ chip->soft_jeita_supported = true;
+ } else {
+ /*
+ * If no soft JEITA configuration required from devicetree,
+ * read the default soft JEITA setting for hard JEITA
+ * configuration sanity check.
+ */
+ rc = smb1360_get_soft_jeita_threshold(chip,
+ &chip->cool_bat_decidegc,
+ &chip->warm_bat_decidegc);
+ if (rc) {
+ pr_err("get default soft JEITA threshold failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ pr_debug("soft-jeita-enabled = %d, warm-bat-decidegc = %d, cool-bat-decidegc = %d, cool-bat-mv = %d, warm-bat-mv = %d, cool-bat-ma = %d, warm-bat-ma = %d\n",
+ chip->soft_jeita_supported, chip->warm_bat_decidegc,
+ chip->cool_bat_decidegc, chip->cool_bat_mv, chip->warm_bat_mv,
+ chip->cool_bat_ma, chip->warm_bat_ma);
+
+ return rc;
+}
+
+#define MAX_PARALLEL_CURRENT 540
+static int smb1360_parse_parallel_charging_params(struct smb1360_chip *chip)
+{
+ struct device_node *node = chip->dev->of_node;
+
+ if (of_property_read_bool(node, "qcom,parallel-charging-enabled")) {
+
+ if (!chip->rsense_10mohm) {
+ pr_err("10mohm-rsense configuration not enabled - parallel-charging disabled\n");
+ return 0;
+ }
+ chip->parallel_charging = true;
+ chip->max_parallel_chg_current = MAX_PARALLEL_CURRENT;
+ of_property_read_u32(node, "qcom,max-parallel-current-ma",
+ &chip->max_parallel_chg_current);
+
+ pr_debug("Max parallel charger current = %dma\n",
+ chip->max_parallel_chg_current);
+
+ /* mark the parallel-charger as disabled */
+ chip->parallel_chg_disable_status |= PARALLEL_CURRENT;
+ }
+
+ return 0;
+}
+
+static int smb_parse_dt(struct smb1360_chip *chip)
+{
+ int rc;
+ struct device_node *node = chip->dev->of_node;
+
+ if (!node) {
+ dev_err(chip->dev, "device tree info. missing\n");
+ return -EINVAL;
+ }
+
+ chip->rsense_10mohm = of_property_read_bool(node, "qcom,rsense-10mhom");
+
+ if (of_property_read_bool(node, "qcom,batt-profile-select")) {
+ rc = smb_parse_batt_id(chip);
+ if (rc < 0) {
+ if (rc != -EPROBE_DEFER)
+ pr_err("Unable to parse batt-id rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ chip->otg_fet_present = of_property_read_bool(node,
+ "qcom,otg-fet-present");
+ if (chip->otg_fet_present) {
+ chip->otg_fet_enable_gpio = of_get_named_gpio(node,
+ "qcom,otg-fet-enable-gpio", 0);
+ if (!gpio_is_valid(chip->otg_fet_enable_gpio)) {
+ if (chip->otg_fet_enable_gpio != -EPROBE_DEFER)
+ pr_err("Unable to get OTG FET enable gpio=%d\n",
+ chip->otg_fet_enable_gpio);
+ return chip->otg_fet_enable_gpio;
+ }
+ /* Configure OTG FET control gpio */
+ rc = devm_gpio_request_one(chip->dev,
+ chip->otg_fet_enable_gpio,
+ GPIOF_OPEN_DRAIN | GPIOF_INIT_HIGH,
+ "smb1360_otg_fet_gpio");
+ if (rc) {
+ pr_err("Unable to request gpio rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ chip->pulsed_irq = of_property_read_bool(node, "qcom,stat-pulsed-irq");
+
+ rc = of_property_read_u32(node, "qcom,float-voltage-mv",
+ &chip->vfloat_mv);
+ if (rc < 0)
+ chip->vfloat_mv = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,charging-timeout",
+ &chip->safety_time);
+ if (rc < 0)
+ chip->safety_time = -EINVAL;
+
+ if (!rc && (chip->safety_time > chg_time[ARRAY_SIZE(chg_time) - 1])) {
+ dev_err(chip->dev, "Bad charging-timeout %d\n",
+ chip->safety_time);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(node, "qcom,recharge-thresh-mv",
+ &chip->resume_delta_mv);
+ if (rc < 0)
+ chip->resume_delta_mv = -EINVAL;
+
+ chip->recharge_disabled = of_property_read_bool(node,
+ "qcom,recharge-disabled");
+
+ rc = of_property_read_u32(node, "qcom,iterm-ma", &chip->iterm_ma);
+ if (rc < 0)
+ chip->iterm_ma = -EINVAL;
+
+ chip->iterm_disabled = of_property_read_bool(node,
+ "qcom,iterm-disabled");
+
+ chip->chg_inhibit_disabled = of_property_read_bool(node,
+ "qcom,chg-inhibit-disabled");
+
+ chip->charging_disabled = of_property_read_bool(node,
+ "qcom,charging-disabled");
+
+ chip->batt_id_disabled = of_property_read_bool(node,
+ "qcom,batt-id-disabled");
+
+ chip->shdn_after_pwroff = of_property_read_bool(node,
+ "qcom,shdn-after-pwroff");
+
+ chip->min_icl_usb100 = of_property_read_bool(node,
+ "qcom,min-icl-100ma");
+
+ chip->ov_ends_chg_cycle_disabled = of_property_read_bool(node,
+ "qcom,disable-ov-ends-chg-cycle");
+
+ rc = smb1360_parse_parallel_charging_params(chip);
+ if (rc) {
+ pr_err("Couldn't parse parallel charginng params rc=%d\n", rc);
+ return rc;
+ }
+
+ if (of_find_property(node, "qcom,thermal-mitigation",
+ &chip->thermal_levels)) {
+ chip->thermal_mitigation = devm_kzalloc(chip->dev,
+ chip->thermal_levels,
+ GFP_KERNEL);
+
+ if (chip->thermal_mitigation == NULL) {
+ pr_err("thermal mitigation kzalloc() failed.\n");
+ return -ENOMEM;
+ }
+
+ chip->thermal_levels /= sizeof(int);
+ rc = of_property_read_u32_array(node,
+ "qcom,thermal-mitigation",
+ chip->thermal_mitigation, chip->thermal_levels);
+ if (rc) {
+ pr_err("Couldn't read threm limits rc = %d\n", rc);
+ return rc;
+ }
+ }
+
+ rc = smb1360_parse_jeita_params(chip);
+ if (rc < 0) {
+ pr_err("Couldn't parse jeita params, rc = %d\n", rc);
+ return rc;
+ }
+
+ /* fg params */
+ chip->empty_soc_disabled = of_property_read_bool(node,
+ "qcom,empty-soc-disabled");
+
+ rc = of_property_read_u32(node, "qcom,fg-delta-soc", &chip->delta_soc);
+ if (rc < 0)
+ chip->delta_soc = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-soc-max", &chip->soc_max);
+ if (rc < 0)
+ chip->soc_max = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-soc-min", &chip->soc_min);
+ if (rc < 0)
+ chip->soc_min = -EINVAL;
+
+ chip->awake_min_soc = of_property_read_bool(node,
+ "qcom,awake-min-soc");
+
+ rc = of_property_read_u32(node, "qcom,fg-voltage-min-mv",
+ &chip->voltage_min_mv);
+ if (rc < 0)
+ chip->voltage_min_mv = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-voltage-empty-mv",
+ &chip->voltage_empty_mv);
+ if (rc < 0)
+ chip->voltage_empty_mv = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-batt-capacity-mah",
+ &chip->batt_capacity_mah);
+ if (rc < 0)
+ chip->batt_capacity_mah = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-cc-soc-coeff",
+ &chip->cc_soc_coeff);
+ if (rc < 0)
+ chip->cc_soc_coeff = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-cutoff-voltage-mv",
+ &chip->v_cutoff_mv);
+ if (rc < 0)
+ chip->v_cutoff_mv = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-iterm-ma",
+ &chip->fg_iterm_ma);
+ if (rc < 0)
+ chip->fg_iterm_ma = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-ibatt-standby-ma",
+ &chip->fg_ibatt_standby_ma);
+ if (rc < 0)
+ chip->fg_ibatt_standby_ma = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,thermistor-c1-coeff",
+ &chip->fg_thermistor_c1_coeff);
+ if (rc < 0)
+ chip->fg_thermistor_c1_coeff = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-cc-to-cv-mv",
+ &chip->fg_cc_to_cv_mv);
+ if (rc < 0)
+ chip->fg_cc_to_cv_mv = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,otg-batt-curr-limit",
+ &chip->otg_batt_curr_limit);
+ if (rc < 0)
+ chip->otg_batt_curr_limit = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-auto-recharge-soc",
+ &chip->fg_auto_recharge_soc);
+ if (rc < 0)
+ chip->fg_auto_recharge_soc = -EINVAL;
+
+ if (of_property_read_bool(node, "qcom,fg-reset-at-pon")) {
+ chip->fg_reset_at_pon = true;
+ rc = of_property_read_u32(node, "qcom,fg-reset-thresold-mv",
+ &chip->fg_reset_threshold_mv);
+ if (rc) {
+ pr_debug("FG reset voltage threshold not specified using 50mV\n");
+ chip->fg_reset_threshold_mv = FG_RESET_THRESHOLD_MV;
+ }
+ }
+
+ return 0;
+}
+
+static int smb1360_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ u8 reg;
+ int rc;
+ struct smb1360_chip *chip;
+ struct power_supply_config batt_psy_cfg = {};
+ struct power_supply_config usb_psy_cfg = {};
+
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->resume_completed = true;
+ chip->client = client;
+ chip->dev = &client->dev;
+ chip->fake_battery_soc = -EINVAL;
+ chip->usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
+
+ chip->extcon = devm_extcon_dev_allocate(chip->dev,
+ smb1360_extcon_cable);
+ if (IS_ERR(chip->extcon)) {
+ pr_err("failed to allocate extcon device\n");
+ rc = PTR_ERR(chip->extcon);
+ return rc;
+ }
+
+ rc = devm_extcon_dev_register(chip->dev, chip->extcon);
+ if (rc) {
+ pr_err("failed to register extcon device\n");
+ return rc;
+ }
+
+ mutex_init(&chip->read_write_lock);
+ mutex_init(&chip->parallel_chg_lock);
+ mutex_init(&chip->otp_gain_lock);
+ mutex_init(&chip->fg_access_request_lock);
+ mutex_init(&chip->irq_complete);
+ mutex_init(&chip->charging_disable_lock);
+ mutex_init(&chip->current_change_lock);
+
+ INIT_DELAYED_WORK(&chip->jeita_work, smb1360_jeita_work_fn);
+ INIT_DELAYED_WORK(&chip->delayed_init_work,
+ smb1360_delayed_init_work_fn);
+ init_completion(&chip->fg_mem_access_granted);
+ smb1360_wakeup_src_init(chip);
+
+ chip->usb_psy_d.name = "usb";
+ chip->usb_psy_d.type = POWER_SUPPLY_TYPE_USB;
+ chip->usb_psy_d.get_property = smb1360_usb_get_property;
+ chip->usb_psy_d.set_property = smb1360_usb_set_property;
+ chip->usb_psy_d.properties = smb1360_usb_properties;
+ chip->usb_psy_d.num_properties = ARRAY_SIZE(smb1360_usb_properties);
+ chip->usb_psy_d.property_is_writeable = smb1360_usb_is_writeable;
+
+ usb_psy_cfg.drv_data = chip;
+ usb_psy_cfg.num_supplicants = 0;
+
+ chip->usb_psy = devm_power_supply_register(chip->dev,
+ &chip->usb_psy_d, &usb_psy_cfg);
+ if (IS_ERR(chip->usb_psy)) {
+ dev_err(chip->dev, "Unable to register usb_psy rc = %ld\n",
+ PTR_ERR(chip->usb_psy));
+ rc = PTR_ERR(chip->usb_psy);
+ return rc;
+ }
+
+ /* probe the device to check if its actually connected */
+ rc = smb1360_read(chip, CFG_BATT_CHG_REG, ®);
+ if (rc) {
+ pr_err("Failed to detect SMB1360, device may be absent\n");
+ goto destroy_mutex;
+ }
+
+ rc = read_revision(chip, &chip->revision);
+ if (rc)
+ dev_err(chip->dev, "Couldn't read revision rc = %d\n", rc);
+
+ rc = smb_parse_dt(chip);
+ if (rc < 0) {
+ dev_err(&client->dev, "Unable to parse DT nodes\n");
+ goto destroy_mutex;
+ }
+
+ device_init_wakeup(chip->dev, 1);
+ i2c_set_clientdata(client, chip);
+ chip->default_i2c_addr = client->addr;
+ INIT_WORK(&chip->parallel_work, smb1360_parallel_work);
+ if (chip->cold_hysteresis || chip->hot_hysteresis)
+ INIT_WORK(&chip->jeita_hysteresis_work,
+ smb1360_jeita_hysteresis_work);
+
+ pr_debug("default_i2c_addr=%x\n", chip->default_i2c_addr);
+ smb1360_otp_backup_pool_init(chip);
+ rc = smb1360_hw_init(chip);
+ if (rc < 0) {
+ dev_err(&client->dev,
+ "Unable to initialize hardware rc = %d\n", rc);
+ goto destroy_mutex;
+ }
+
+ rc = smb1360_regulator_init(chip);
+ if (rc) {
+ dev_err(&client->dev,
+ "Couldn't initialize smb1360 ragulator rc=%d\n", rc);
+ goto fail_hw_init;
+ }
+
+ rc = determine_initial_status(chip);
+ if (rc < 0) {
+ dev_err(&client->dev,
+ "Unable to determine init status rc = %d\n", rc);
+ goto fail_hw_init;
+ }
+
+ chip->batt_psy_d.name = "battery";
+ chip->batt_psy_d.type = POWER_SUPPLY_TYPE_BATTERY;
+ chip->batt_psy_d.get_property = smb1360_battery_get_property;
+ chip->batt_psy_d.set_property = smb1360_battery_set_property;
+ chip->batt_psy_d.properties = smb1360_battery_properties;
+ chip->batt_psy_d.num_properties =
+ ARRAY_SIZE(smb1360_battery_properties);
+ chip->batt_psy_d.property_is_writeable = smb1360_battery_is_writeable;
+
+ batt_psy_cfg.drv_data = chip;
+ batt_psy_cfg.num_supplicants = 0;
+
+ chip->batt_psy = devm_power_supply_register(chip->dev,
+ &chip->batt_psy_d, &batt_psy_cfg);
+ if (IS_ERR(chip->batt_psy)) {
+ dev_err(&client->dev, "Unable to register batt_psy rc = %ld\n",
+ PTR_ERR(chip->batt_psy));
+ goto unregister_batt_psy;
+ }
+
+ /* STAT irq configuration */
+ if (client->irq) {
+ rc = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ smb1360_stat_handler, IRQF_ONESHOT,
+ "smb1360_stat_irq", chip);
+ if (rc < 0) {
+ dev_err(&client->dev,
+ "request_irq for irq=%d failed rc = %d\n",
+ client->irq, rc);
+ goto unregister_batt_psy;
+ }
+ enable_irq_wake(client->irq);
+ }
+
+ chip->debug_root = debugfs_create_dir("smb1360", NULL);
+ if (!chip->debug_root)
+ dev_err(chip->dev, "Couldn't create debug dir\n");
+
+ if (chip->debug_root) {
+ struct dentry *ent;
+
+ ent = debugfs_create_file("config_registers", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &cnfg_debugfs_ops);
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create cnfg debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_file("status_registers", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &status_debugfs_ops);
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create status debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_file("irq_status", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &irq_stat_debugfs_ops);
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create irq_stat debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_file("cmd_registers", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &cmd_debugfs_ops);
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create cmd debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_file("fg_regs",
+ S_IFREG | 0444, chip->debug_root, chip,
+ &fg_regs_debugfs_ops);
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create fg_scratch_pad debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_x32("address", S_IFREG | 0644,
+ chip->debug_root,
+ &(chip->peek_poke_address));
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create address debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_file("data", S_IFREG | 0644,
+ chip->debug_root, chip,
+ &poke_poke_debug_ops);
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create data debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_x32("fg_address",
+ S_IFREG | 0644,
+ chip->debug_root,
+ &(chip->fg_peek_poke_address));
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create address debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_file("fg_data",
+ S_IFREG | 0644,
+ chip->debug_root, chip,
+ &fg_poke_poke_debug_ops);
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create data debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_x32("fg_access_type",
+ S_IFREG | 0644,
+ chip->debug_root,
+ &(chip->fg_access_type));
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create data debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_x32("skip_writes",
+ S_IFREG | 0644,
+ chip->debug_root,
+ &(chip->skip_writes));
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create data debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_x32("skip_reads",
+ S_IFREG | 0644,
+ chip->debug_root,
+ &(chip->skip_reads));
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create data debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_file("irq_count", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &irq_count_debugfs_ops);
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create count debug file rc = %d\n",
+ rc);
+ }
+
+ dev_info(chip->dev, "SMB1360 revision=0x%x probe success! batt=%d usb=%d soc=%d\n",
+ chip->revision,
+ smb1360_get_prop_batt_present(chip),
+ chip->usb_present,
+ smb1360_get_prop_batt_capacity(chip));
+
+ return 0;
+
+unregister_batt_psy:
+ power_supply_unregister(chip->batt_psy);
+fail_hw_init:
+ regulator_unregister(chip->otg_vreg.rdev);
+destroy_mutex:
+ power_supply_unregister(chip->usb_psy);
+ wakeup_source_trash(&chip->smb1360_ws.source);
+ mutex_destroy(&chip->read_write_lock);
+ mutex_destroy(&chip->parallel_chg_lock);
+ mutex_destroy(&chip->otp_gain_lock);
+ mutex_destroy(&chip->fg_access_request_lock);
+ mutex_destroy(&chip->irq_complete);
+ mutex_destroy(&chip->charging_disable_lock);
+ mutex_destroy(&chip->current_change_lock);
+ return rc;
+}
+
+static int smb1360_remove(struct i2c_client *client)
+{
+ struct smb1360_chip *chip = i2c_get_clientdata(client);
+
+ regulator_unregister(chip->otg_vreg.rdev);
+ power_supply_unregister(chip->usb_psy);
+ power_supply_unregister(chip->batt_psy);
+ wakeup_source_trash(&chip->smb1360_ws.source);
+ mutex_destroy(&chip->charging_disable_lock);
+ mutex_destroy(&chip->current_change_lock);
+ mutex_destroy(&chip->read_write_lock);
+ mutex_destroy(&chip->parallel_chg_lock);
+ mutex_destroy(&chip->irq_complete);
+ mutex_destroy(&chip->otp_gain_lock);
+ mutex_destroy(&chip->fg_access_request_lock);
+ debugfs_remove_recursive(chip->debug_root);
+
+ return 0;
+}
+
+static int smb1360_suspend(struct device *dev)
+{
+ int i, rc;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct smb1360_chip *chip = i2c_get_clientdata(client);
+
+ /* Save the current IRQ config */
+ for (i = 0; i < 3; i++) {
+ rc = smb1360_read(chip, IRQ_CFG_REG + i,
+ &chip->irq_cfg_mask[i]);
+ if (rc)
+ pr_err("Couldn't save irq cfg regs rc=%d\n", rc);
+ }
+
+ /* enable only important IRQs */
+ rc = smb1360_write(chip, IRQ_CFG_REG, IRQ_DCIN_UV_BIT
+ | IRQ_AICL_DONE_BIT
+ | IRQ_BAT_HOT_COLD_SOFT_BIT
+ | IRQ_BAT_HOT_COLD_HARD_BIT);
+ if (rc < 0)
+ pr_err("Couldn't set irq_cfg rc=%d\n", rc);
+
+ rc = smb1360_write(chip, IRQ2_CFG_REG, IRQ2_BATT_MISSING_BIT
+ | IRQ2_VBAT_LOW_BIT
+ | IRQ2_POWER_OK_BIT);
+ if (rc < 0)
+ pr_err("Couldn't set irq2_cfg rc=%d\n", rc);
+
+ rc = smb1360_write(chip, IRQ3_CFG_REG, IRQ3_SOC_FULL_BIT
+ | IRQ3_SOC_MIN_BIT
+ | IRQ3_SOC_EMPTY_BIT);
+ if (rc < 0)
+ pr_err("Couldn't set irq3_cfg rc=%d\n", rc);
+
+ mutex_lock(&chip->irq_complete);
+ chip->resume_completed = false;
+ mutex_unlock(&chip->irq_complete);
+
+ return 0;
+}
+
+static int smb1360_suspend_noirq(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct smb1360_chip *chip = i2c_get_clientdata(client);
+
+ if (chip->irq_waiting) {
+ pr_err_ratelimited("Aborting suspend, an interrupt was detected while suspending\n");
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static int smb1360_resume(struct device *dev)
+{
+ int i, rc;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct smb1360_chip *chip = i2c_get_clientdata(client);
+
+ /* Restore the IRQ config */
+ for (i = 0; i < 3; i++) {
+ rc = smb1360_write(chip, IRQ_CFG_REG + i,
+ chip->irq_cfg_mask[i]);
+ if (rc)
+ pr_err("Couldn't restore irq cfg regs rc=%d\n", rc);
+ }
+
+ mutex_lock(&chip->irq_complete);
+ chip->resume_completed = true;
+ if (chip->irq_waiting) {
+ chip->irq_disabled = false;
+ enable_irq(client->irq);
+ mutex_unlock(&chip->irq_complete);
+ smb1360_stat_handler(client->irq, chip);
+ } else {
+ mutex_unlock(&chip->irq_complete);
+ }
+
+ power_supply_changed(chip->batt_psy);
+
+ return 0;
+}
+
+static void smb1360_shutdown(struct i2c_client *client)
+{
+ int rc;
+ struct smb1360_chip *chip = i2c_get_clientdata(client);
+
+ rc = smb1360_otg_disable(chip);
+ if (rc)
+ pr_err("Couldn't disable OTG mode rc=%d\n", rc);
+
+ if (chip->shdn_after_pwroff) {
+ rc = smb1360_poweroff(chip);
+ if (rc)
+ pr_err("Couldn't shutdown smb1360, rc = %d\n", rc);
+ pr_info("smb1360 power off\n");
+ }
+}
+
+static const struct dev_pm_ops smb1360_pm_ops = {
+ .resume = smb1360_resume,
+ .suspend_noirq = smb1360_suspend_noirq,
+ .suspend = smb1360_suspend,
+};
+
+static const struct of_device_id smb1360_match_table[] = {
+ { .compatible = "qcom,smb1360-chg-fg",},
+ { },
+};
+
+static const struct i2c_device_id smb1360_id[] = {
+ {"smb1360-chg-fg", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, smb1360_id);
+
+static struct i2c_driver smb1360_driver = {
+ .driver = {
+ .name = "smb1360-chg-fg",
+ .owner = THIS_MODULE,
+ .of_match_table = smb1360_match_table,
+ .pm = &smb1360_pm_ops,
+ },
+ .probe = smb1360_probe,
+ .remove = smb1360_remove,
+ .shutdown = smb1360_shutdown,
+ .id_table = smb1360_id,
+};
+
+module_i2c_driver(smb1360_driver);
+
+MODULE_DESCRIPTION("SMB1360 Charger and Fuel Gauge");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:smb1360-chg-fg");
diff --git a/drivers/power/supply/qcom/smb1390-charger.c b/drivers/power/supply/qcom/smb1390-charger.c
index 636265b..55a1f45 100644
--- a/drivers/power/supply/qcom/smb1390-charger.c
+++ b/drivers/power/supply/qcom/smb1390-charger.c
@@ -67,6 +67,15 @@
#define CORE_FTRIM_ILIM_REG 0x1030
#define CFG_ILIM_MASK GENMASK(4, 0)
+#define CORE_FTRIM_LVL_REG 0x1033
+#define CFG_WIN_HI_MASK GENMASK(3, 2)
+#define WIN_OV_LVL_1000MV 0x08
+
+#define CORE_FTRIM_MISC_REG 0x1034
+#define TR_WIN_1P5X_BIT BIT(0)
+#define WINDOW_DETECTION_DELTA_X1P0 0
+#define WINDOW_DETECTION_DELTA_X1P5 1
+
#define CP_VOTER "CP_VOTER"
#define USER_VOTER "USER_VOTER"
#define ILIM_VOTER "ILIM_VOTER"
@@ -591,11 +600,30 @@
static int smb1390_init_hw(struct smb1390 *chip)
{
+ int rc;
+
/*
* charge pump is initially disabled; this indirectly votes to allow
* traditional parallel charging if present
*/
vote(chip->disable_votable, USER_VOTER, true, 0);
+
+ /*
+ * Improve ILIM accuracy:
+ * - Configure window (Vin - 2Vout) OV level to 1000mV
+ * - Configure VOUT tracking value to 1.0
+ */
+ rc = smb1390_masked_write(chip, CORE_FTRIM_LVL_REG,
+ CFG_WIN_HI_MASK, WIN_OV_LVL_1000MV);
+ if (rc < 0)
+ return rc;
+
+ rc = smb1390_masked_write(chip, CORE_FTRIM_MISC_REG,
+ TR_WIN_1P5X_BIT, WINDOW_DETECTION_DELTA_X1P0);
+ if (rc < 0)
+ return rc;
+
+
return 0;
}
diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c
index 3152669..8756186 100644
--- a/drivers/power/supply/qcom/smb5-lib.c
+++ b/drivers/power/supply/qcom/smb5-lib.c
@@ -1026,13 +1026,25 @@
{
struct smb_charger *chg = rdev_get_drvdata(rdev);
int rc = 0;
+ u8 stat, orientation;
smblib_dbg(chg, PR_OTG, "enabling VCONN\n");
- rc = smblib_masked_write(chg, TYPE_C_VCONN_CONTROL_REG,
- VCONN_EN_VALUE_BIT, VCONN_EN_VALUE_BIT);
+ rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
if (rc < 0) {
- smblib_err(chg, "Couldn't enable vconn setting rc=%d\n", rc);
+ smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
+ return rc;
+ }
+
+ /* VCONN orientation is opposite to that of CC */
+ orientation =
+ stat & TYPEC_CCOUT_VALUE_BIT ? 0 : VCONN_EN_ORIENTATION_BIT;
+ rc = smblib_masked_write(chg, TYPE_C_VCONN_CONTROL_REG,
+ VCONN_EN_VALUE_BIT | VCONN_EN_ORIENTATION_BIT,
+ VCONN_EN_VALUE_BIT | orientation);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read TYPE_C_CCOUT_CONTROL_REG rc=%d\n",
+ rc);
return rc;
}
@@ -1590,7 +1602,7 @@
return rc;
}
-static int smblib_force_vbus_voltage(struct smb_charger *chg, u8 val)
+int smblib_force_vbus_voltage(struct smb_charger *chg, u8 val)
{
int rc;
@@ -3107,13 +3119,6 @@
if (rc < 0)
smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n", rc);
-
- rc = smblib_masked_write(chg, TYPE_C_VCONN_CONTROL_REG,
- VCONN_EN_SRC_BIT, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't set TYPE_C_VCONN_CONTROL_REG rc=%d\n",
- rc);
-
/* clear exit sink based on cc */
rc = smblib_masked_write(chg, TYPE_C_EXIT_STATE_CFG_REG,
EXIT_SNK_BASED_ON_CC_BIT, 0);
diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h
index 668ce5f..5d6d3a6 100644
--- a/drivers/power/supply/qcom/smb5-lib.h
+++ b/drivers/power/supply/qcom/smb5-lib.h
@@ -264,6 +264,7 @@
int smb_version;
int otg_delay_ms;
int *weak_chg_icl_ua;
+ struct qpnp_vadc_chip *vadc_dev;
/* locks */
struct mutex lock;
@@ -528,6 +529,7 @@
const union power_supply_propval *val);
int smblib_stat_sw_override_cfg(struct smb_charger *chg, bool override);
int smblib_configure_wdog(struct smb_charger *chg, bool enable);
+int smblib_force_vbus_voltage(struct smb_charger *chg, u8 val);
int smblib_init(struct smb_charger *chg);
int smblib_deinit(struct smb_charger *chg);
diff --git a/drivers/power/supply/qcom/smb5-reg.h b/drivers/power/supply/qcom/smb5-reg.h
index bb28423..e199087 100644
--- a/drivers/power/supply/qcom/smb5-reg.h
+++ b/drivers/power/supply/qcom/smb5-reg.h
@@ -194,6 +194,7 @@
#define FORCE_12V_BIT BIT(5)
#define FORCE_9V_BIT BIT(4)
#define FORCE_5V_BIT BIT(3)
+#define IDLE_BIT BIT(2)
#define SINGLE_DECREMENT_BIT BIT(1)
#define SINGLE_INCREMENT_BIT BIT(0)
@@ -281,10 +282,12 @@
#define TYPEC_DISABLE_CMD_BIT BIT(0)
#define TYPE_C_VCONN_CONTROL_REG (TYPEC_BASE + 0x46)
+#define VCONN_EN_ORIENTATION_BIT BIT(2)
#define VCONN_EN_VALUE_BIT BIT(1)
#define VCONN_EN_SRC_BIT BIT(0)
#define TYPE_C_CCOUT_CONTROL_REG (TYPEC_BASE + 0x48)
+#define TYPEC_CCOUT_VALUE_BIT BIT(1)
#define TYPEC_CCOUT_SRC_BIT BIT(0)
#define TYPE_C_EXIT_STATE_CFG_REG (TYPEC_BASE + 0x50)
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index 86280b7..2aa5b37 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c
@@ -97,30 +97,26 @@
/* posix clock implementation */
-static int ptp_clock_getres(struct posix_clock *pc, struct timespec *tp)
+static int ptp_clock_getres(struct posix_clock *pc, struct timespec64 *tp)
{
tp->tv_sec = 0;
tp->tv_nsec = 1;
return 0;
}
-static int ptp_clock_settime(struct posix_clock *pc, const struct timespec *tp)
+static int ptp_clock_settime(struct posix_clock *pc, const struct timespec64 *tp)
{
struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
- struct timespec64 ts = timespec_to_timespec64(*tp);
- return ptp->info->settime64(ptp->info, &ts);
+ return ptp->info->settime64(ptp->info, tp);
}
-static int ptp_clock_gettime(struct posix_clock *pc, struct timespec *tp)
+static int ptp_clock_gettime(struct posix_clock *pc, struct timespec64 *tp)
{
struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
- struct timespec64 ts;
int err;
- err = ptp->info->gettime64(ptp->info, &ts);
- if (!err)
- *tp = timespec64_to_timespec(ts);
+ err = ptp->info->gettime64(ptp->info, tp);
return err;
}
@@ -133,7 +129,7 @@
ops = ptp->info;
if (tx->modes & ADJ_SETOFFSET) {
- struct timespec ts;
+ struct timespec64 ts;
ktime_t kt;
s64 delta;
@@ -146,7 +142,7 @@
if ((unsigned long) ts.tv_nsec >= NSEC_PER_SEC)
return -EINVAL;
- kt = timespec_to_ktime(ts);
+ kt = timespec64_to_ktime(ts);
delta = ktime_to_ns(kt);
err = ops->adjtime(ops, delta);
} else if (tx->modes & ADJ_FREQUENCY) {
diff --git a/drivers/pwm/pwm-stmpe.c b/drivers/pwm/pwm-stmpe.c
index e464582..3439f1e 100644
--- a/drivers/pwm/pwm-stmpe.c
+++ b/drivers/pwm/pwm-stmpe.c
@@ -145,7 +145,7 @@
break;
case 2:
- offset = STMPE24XX_PWMIC1;
+ offset = STMPE24XX_PWMIC2;
break;
default:
diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
index e464784..7e8906d6 100644
--- a/drivers/pwm/pwm-tegra.c
+++ b/drivers/pwm/pwm-tegra.c
@@ -76,6 +76,7 @@
struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
unsigned long long c = duty_ns;
unsigned long rate, hz;
+ unsigned long long ns100 = NSEC_PER_SEC;
u32 val = 0;
int err;
@@ -95,9 +96,11 @@
* cycles at the PWM clock rate will take period_ns nanoseconds.
*/
rate = clk_get_rate(pc->clk) >> PWM_DUTY_WIDTH;
- hz = NSEC_PER_SEC / period_ns;
- rate = (rate + (hz / 2)) / hz;
+ /* Consider precision in PWM_SCALE_WIDTH rate calculation */
+ ns100 *= 100;
+ hz = DIV_ROUND_CLOSEST_ULL(ns100, period_ns);
+ rate = DIV_ROUND_CLOSEST(rate * 100, hz);
/*
* Since the actual PWM divider is the register's frequency divider
diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c
index 3a6d029..c5e272e 100644
--- a/drivers/regulator/anatop-regulator.c
+++ b/drivers/regulator/anatop-regulator.c
@@ -296,6 +296,11 @@
if (!sreg->sel && !strcmp(sreg->name, "vddpu"))
sreg->sel = 22;
+ /* set the default voltage of the pcie phy to be 1.100v */
+ if (!sreg->sel && rdesc->name &&
+ !strcmp(rdesc->name, "vddpcie"))
+ sreg->sel = 0x10;
+
if (!sreg->bypass && !sreg->sel) {
dev_err(&pdev->dev, "Failed to read a valid default voltage selector.\n");
return -EINVAL;
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 1f60635..1a25499 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -2498,7 +2498,7 @@
ret = ops->list_voltage(rdev, selector);
if (lock)
mutex_unlock(&rdev->mutex);
- } else if (rdev->supply) {
+ } else if (rdev->is_switch && rdev->supply) {
ret = _regulator_list_voltage(rdev->supply, selector, lock);
} else {
return -EINVAL;
@@ -2556,7 +2556,7 @@
if (rdev->desc->n_voltages)
return rdev->desc->n_voltages;
- if (!rdev->supply)
+ if (!rdev->is_switch || !rdev->supply)
return -EINVAL;
return regulator_count_voltages(rdev->supply);
@@ -4453,6 +4453,11 @@
mutex_unlock(®ulator_list_mutex);
}
+ if (!rdev->desc->ops->get_voltage &&
+ !rdev->desc->ops->list_voltage &&
+ !rdev->desc->fixed_uV)
+ rdev->is_switch = true;
+
ret = device_register(&rdev->dev);
if (ret != 0) {
put_device(&rdev->dev);
diff --git a/drivers/regulator/cpr4-apss-regulator.c b/drivers/regulator/cpr4-apss-regulator.c
index 1f8f8be..d5a0e33 100644
--- a/drivers/regulator/cpr4-apss-regulator.c
+++ b/drivers/regulator/cpr4-apss-regulator.c
@@ -1840,20 +1840,29 @@
return 0;
}
-static int cpr4_apss_regulator_suspend(struct platform_device *pdev,
- pm_message_t state)
+#if CONFIG_PM
+static int cpr4_apss_regulator_suspend(struct device *dev)
{
- struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+ struct cpr3_controller *ctrl = dev_get_drvdata(dev);
return cpr3_regulator_suspend(ctrl);
}
-static int cpr4_apss_regulator_resume(struct platform_device *pdev)
+static int cpr4_apss_regulator_resume(struct device *dev)
{
- struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+ struct cpr3_controller *ctrl = dev_get_drvdata(dev);
return cpr3_regulator_resume(ctrl);
}
+#else
+#define cpr4_apss_regulator_suspend NULL
+#define cpr4_apss_regulator_resume NULL
+#endif
+
+static const struct dev_pm_ops cpr4_apss_regulator_pm_ops = {
+ .suspend = cpr4_apss_regulator_suspend,
+ .resume = cpr4_apss_regulator_resume,
+};
/* Data corresponds to the SoC revision */
static const struct of_device_id cpr4_regulator_match_table[] = {
@@ -1977,11 +1986,10 @@
.name = "qcom,cpr4-apss-regulator",
.of_match_table = cpr4_regulator_match_table,
.owner = THIS_MODULE,
+ .pm = &cpr4_apss_regulator_pm_ops,
},
.probe = cpr4_apss_regulator_probe,
.remove = cpr4_apss_regulator_remove,
- .suspend = cpr4_apss_regulator_suspend,
- .resume = cpr4_apss_regulator_resume,
};
static int cpr4_regulator_init(void)
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index 7030d7c..c554e52 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -41,6 +41,9 @@
#include <linux/pm.h>
#include <linux/of.h>
#include <linux/of_platform.h>
+#ifdef CONFIG_X86
+#include <asm/i8259.h>
+#endif
/* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */
#include <linux/mc146818rtc.h>
@@ -1117,17 +1120,23 @@
{
cmos_wake_setup(&pnp->dev);
- if (pnp_port_start(pnp, 0) == 0x70 && !pnp_irq_valid(pnp, 0))
+ if (pnp_port_start(pnp, 0) == 0x70 && !pnp_irq_valid(pnp, 0)) {
+ unsigned int irq = 0;
+#ifdef CONFIG_X86
/* Some machines contain a PNP entry for the RTC, but
* don't define the IRQ. It should always be safe to
- * hardcode it in these cases
+ * hardcode it on systems with a legacy PIC.
*/
+ if (nr_legacy_irqs())
+ irq = 8;
+#endif
return cmos_do_probe(&pnp->dev,
- pnp_get_resource(pnp, IORESOURCE_IO, 0), 8);
- else
+ pnp_get_resource(pnp, IORESOURCE_IO, 0), irq);
+ } else {
return cmos_do_probe(&pnp->dev,
pnp_get_resource(pnp, IORESOURCE_IO, 0),
pnp_irq(pnp, 0));
+ }
}
static void cmos_pnp_remove(struct pnp_dev *pnp)
diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c
index 3b3049c..c0eb113 100644
--- a/drivers/rtc/rtc-ds1374.c
+++ b/drivers/rtc/rtc-ds1374.c
@@ -527,6 +527,10 @@
if (get_user(new_margin, (int __user *)arg))
return -EFAULT;
+ /* the hardware's tick rate is 4096 Hz, so
+ * the counter value needs to be scaled accordingly
+ */
+ new_margin <<= 12;
if (new_margin < 1 || new_margin > 16777216)
return -EINVAL;
@@ -535,7 +539,8 @@
ds1374_wdt_ping();
/* fallthrough */
case WDIOC_GETTIMEOUT:
- return put_user(wdt_margin, (int __user *)arg);
+ /* when returning ... inverse is true */
+ return put_user((wdt_margin >> 12), (int __user *)arg);
case WDIOC_SETOPTIONS:
if (copy_from_user(&options, (int __user *)arg, sizeof(int)))
return -EFAULT;
@@ -543,14 +548,15 @@
if (options & WDIOS_DISABLECARD) {
pr_info("disable watchdog\n");
ds1374_wdt_disable();
+ return 0;
}
if (options & WDIOS_ENABLECARD) {
pr_info("enable watchdog\n");
ds1374_wdt_settimeout(wdt_margin);
ds1374_wdt_ping();
+ return 0;
}
-
return -EINVAL;
}
return -ENOTTY;
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c
index 8305ab6..a20dc29 100644
--- a/drivers/s390/block/dasd_3990_erp.c
+++ b/drivers/s390/block/dasd_3990_erp.c
@@ -2755,6 +2755,16 @@
erp = dasd_3990_erp_handle_match_erp(cqr, erp);
}
+
+ /*
+ * For path verification work we need to stick with the path that was
+ * originally chosen so that the per path configuration data is
+ * assigned correctly.
+ */
+ if (test_bit(DASD_CQR_VERIFY_PATH, &erp->flags) && cqr->lpm) {
+ erp->lpm = cqr->lpm;
+ }
+
if (device->features & DASD_FEATURE_ERPLOG) {
/* print current erp_chain */
dev_err(&device->cdev->dev,
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index 9b5fc50..403712b 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -592,6 +592,11 @@
void (*callback) (struct qeth_channel *, struct qeth_cmd_buffer *);
};
+static inline struct qeth_ipa_cmd *__ipa_cmd(struct qeth_cmd_buffer *iob)
+{
+ return (struct qeth_ipa_cmd *)(iob->data + IPA_PDU_HEADER_SIZE);
+}
+
/**
* definition of a qeth channel, used for read and write
*/
@@ -849,7 +854,7 @@
*/
static inline int qeth_get_elements_for_range(addr_t start, addr_t end)
{
- return PFN_UP(end - 1) - PFN_DOWN(start);
+ return PFN_UP(end) - PFN_DOWN(start);
}
static inline int qeth_get_micros(void)
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index df8f74c..283416a 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -522,8 +522,7 @@
queue == card->qdio.no_in_queues - 1;
}
-
-static int qeth_issue_next_read(struct qeth_card *card)
+static int __qeth_issue_next_read(struct qeth_card *card)
{
int rc;
struct qeth_cmd_buffer *iob;
@@ -554,6 +553,17 @@
return rc;
}
+static int qeth_issue_next_read(struct qeth_card *card)
+{
+ int ret;
+
+ spin_lock_irq(get_ccwdev_lock(CARD_RDEV(card)));
+ ret = __qeth_issue_next_read(card);
+ spin_unlock_irq(get_ccwdev_lock(CARD_RDEV(card)));
+
+ return ret;
+}
+
static struct qeth_reply *qeth_alloc_reply(struct qeth_card *card)
{
struct qeth_reply *reply;
@@ -957,7 +967,7 @@
spin_lock_irqsave(&card->thread_mask_lock, flags);
card->thread_running_mask &= ~thread;
spin_unlock_irqrestore(&card->thread_mask_lock, flags);
- wake_up(&card->wait_q);
+ wake_up_all(&card->wait_q);
}
EXPORT_SYMBOL_GPL(qeth_clear_thread_running_bit);
@@ -1161,6 +1171,7 @@
}
rc = qeth_get_problem(cdev, irb);
if (rc) {
+ card->read_or_write_problem = 1;
qeth_clear_ipacmd_list(card);
qeth_schedule_recovery(card);
goto out;
@@ -1179,7 +1190,7 @@
return;
if (channel == &card->read &&
channel->state == CH_STATE_UP)
- qeth_issue_next_read(card);
+ __qeth_issue_next_read(card);
iob = channel->iob;
index = channel->buf_no;
@@ -2050,7 +2061,7 @@
unsigned long flags;
struct qeth_reply *reply = NULL;
unsigned long timeout, event_timeout;
- struct qeth_ipa_cmd *cmd;
+ struct qeth_ipa_cmd *cmd = NULL;
QETH_CARD_TEXT(card, 2, "sendctl");
@@ -2064,23 +2075,27 @@
}
reply->callback = reply_cb;
reply->param = reply_param;
- if (card->state == CARD_STATE_DOWN)
- reply->seqno = QETH_IDX_COMMAND_SEQNO;
- else
- reply->seqno = card->seqno.ipa++;
+
init_waitqueue_head(&reply->wait_q);
- spin_lock_irqsave(&card->lock, flags);
- list_add_tail(&reply->list, &card->cmd_waiter_list);
- spin_unlock_irqrestore(&card->lock, flags);
QETH_DBF_HEX(CTRL, 2, iob->data, QETH_DBF_CTRL_LEN);
while (atomic_cmpxchg(&card->write.irq_pending, 0, 1)) ;
+
+ if (IS_IPA(iob->data)) {
+ cmd = __ipa_cmd(iob);
+ cmd->hdr.seqno = card->seqno.ipa++;
+ reply->seqno = cmd->hdr.seqno;
+ event_timeout = QETH_IPA_TIMEOUT;
+ } else {
+ reply->seqno = QETH_IDX_COMMAND_SEQNO;
+ event_timeout = QETH_TIMEOUT;
+ }
qeth_prepare_control_data(card, len, iob);
- if (IS_IPA(iob->data))
- event_timeout = QETH_IPA_TIMEOUT;
- else
- event_timeout = QETH_TIMEOUT;
+ spin_lock_irqsave(&card->lock, flags);
+ list_add_tail(&reply->list, &card->cmd_waiter_list);
+ spin_unlock_irqrestore(&card->lock, flags);
+
timeout = jiffies + event_timeout;
QETH_CARD_TEXT(card, 6, "noirqpnd");
@@ -2105,9 +2120,8 @@
/* we have only one long running ipassist, since we can ensure
process context of this command we can sleep */
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
- if ((cmd->hdr.command == IPA_CMD_SETIP) &&
- (cmd->hdr.prot_version == QETH_PROT_IPV4)) {
+ if (cmd && cmd->hdr.command == IPA_CMD_SETIP &&
+ cmd->hdr.prot_version == QETH_PROT_IPV4) {
if (!wait_event_timeout(reply->wait_q,
atomic_read(&reply->received), event_timeout))
goto time_err;
@@ -2871,7 +2885,7 @@
memset(cmd, 0, sizeof(struct qeth_ipa_cmd));
cmd->hdr.command = command;
cmd->hdr.initiator = IPA_CMD_INITIATOR_HOST;
- cmd->hdr.seqno = card->seqno.ipa;
+ /* cmd->hdr.seqno is set by qeth_send_control_data() */
cmd->hdr.adapter_type = qeth_get_ipa_adp_type(card->info.link_type);
cmd->hdr.rel_adapter_no = (__u8) card->info.portno;
if (card->options.layer2)
@@ -3852,10 +3866,12 @@
int qeth_get_elements_no(struct qeth_card *card,
struct sk_buff *skb, int extra_elems, int data_offset)
{
- int elements = qeth_get_elements_for_range(
- (addr_t)skb->data + data_offset,
- (addr_t)skb->data + skb_headlen(skb)) +
- qeth_get_elements_for_frags(skb);
+ addr_t end = (addr_t)skb->data + skb_headlen(skb);
+ int elements = qeth_get_elements_for_frags(skb);
+ addr_t start = (addr_t)skb->data + data_offset;
+
+ if (start != end)
+ elements += qeth_get_elements_for_range(start, end);
if ((elements + extra_elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {
QETH_DBF_MESSAGE(2, "Invalid size of IP packet "
@@ -4984,8 +5000,6 @@
QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
qeth_clean_channel(&card->read);
qeth_clean_channel(&card->write);
- if (card->dev)
- free_netdev(card->dev);
qeth_free_qdio_buffers(card);
unregister_service_level(&card->qeth_service_level);
kfree(card);
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 5082dfe..e94e957 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -1057,8 +1057,8 @@
qeth_l2_set_offline(cgdev);
if (card->dev) {
- netif_napi_del(&card->napi);
unregister_netdev(card->dev);
+ free_netdev(card->dev);
card->dev = NULL;
}
return;
diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h
index eedf9b0..5735694 100644
--- a/drivers/s390/net/qeth_l3.h
+++ b/drivers/s390/net/qeth_l3.h
@@ -39,8 +39,40 @@
unsigned int pfxlen;
} a6;
} u;
-
};
+
+static inline bool qeth_l3_addr_match_ip(struct qeth_ipaddr *a1,
+ struct qeth_ipaddr *a2)
+{
+ if (a1->proto != a2->proto)
+ return false;
+ if (a1->proto == QETH_PROT_IPV6)
+ return ipv6_addr_equal(&a1->u.a6.addr, &a2->u.a6.addr);
+ return a1->u.a4.addr == a2->u.a4.addr;
+}
+
+static inline bool qeth_l3_addr_match_all(struct qeth_ipaddr *a1,
+ struct qeth_ipaddr *a2)
+{
+ /* Assumes that the pair was obtained via qeth_l3_addr_find_by_ip(),
+ * so 'proto' and 'addr' match for sure.
+ *
+ * For ucast:
+ * - 'mac' is always 0.
+ * - 'mask'/'pfxlen' for RXIP/VIPA is always 0. For NORMAL, matching
+ * values are required to avoid mixups in takeover eligibility.
+ *
+ * For mcast,
+ * - 'mac' is mapped from the IP, and thus always matches.
+ * - 'mask'/'pfxlen' is always 0.
+ */
+ if (a1->type != a2->type)
+ return false;
+ if (a1->proto == QETH_PROT_IPV6)
+ return a1->u.a6.pfxlen == a2->u.a6.pfxlen;
+ return a1->u.a4.mask == a2->u.a4.mask;
+}
+
static inline u64 qeth_l3_ipaddr_hash(struct qeth_ipaddr *addr)
{
u64 ret = 0;
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 1487f8a..4ca161b 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -154,6 +154,24 @@
return -EINVAL;
}
+static struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card,
+ struct qeth_ipaddr *query)
+{
+ u64 key = qeth_l3_ipaddr_hash(query);
+ struct qeth_ipaddr *addr;
+
+ if (query->is_multicast) {
+ hash_for_each_possible(card->ip_mc_htable, addr, hnode, key)
+ if (qeth_l3_addr_match_ip(addr, query))
+ return addr;
+ } else {
+ hash_for_each_possible(card->ip_htable, addr, hnode, key)
+ if (qeth_l3_addr_match_ip(addr, query))
+ return addr;
+ }
+ return NULL;
+}
+
static void qeth_l3_convert_addr_to_bits(u8 *addr, u8 *bits, int len)
{
int i, j;
@@ -207,34 +225,6 @@
return rc;
}
-inline int
-qeth_l3_ipaddrs_is_equal(struct qeth_ipaddr *addr1, struct qeth_ipaddr *addr2)
-{
- return addr1->proto == addr2->proto &&
- !memcmp(&addr1->u, &addr2->u, sizeof(addr1->u)) &&
- !memcmp(&addr1->mac, &addr2->mac, sizeof(addr1->mac));
-}
-
-static struct qeth_ipaddr *
-qeth_l3_ip_from_hash(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
-{
- struct qeth_ipaddr *addr;
-
- if (tmp_addr->is_multicast) {
- hash_for_each_possible(card->ip_mc_htable, addr,
- hnode, qeth_l3_ipaddr_hash(tmp_addr))
- if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr))
- return addr;
- } else {
- hash_for_each_possible(card->ip_htable, addr,
- hnode, qeth_l3_ipaddr_hash(tmp_addr))
- if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr))
- return addr;
- }
-
- return NULL;
-}
-
int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
{
int rc = 0;
@@ -249,8 +239,8 @@
QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8);
}
- addr = qeth_l3_ip_from_hash(card, tmp_addr);
- if (!addr)
+ addr = qeth_l3_find_addr_by_ip(card, tmp_addr);
+ if (!addr || !qeth_l3_addr_match_all(addr, tmp_addr))
return -ENOENT;
addr->ref_counter--;
@@ -259,12 +249,8 @@
if (addr->in_progress)
return -EINPROGRESS;
- if (!qeth_card_hw_is_reachable(card)) {
- addr->disp_flag = QETH_DISP_ADDR_DELETE;
- return 0;
- }
-
- rc = qeth_l3_deregister_addr_entry(card, addr);
+ if (qeth_card_hw_is_reachable(card))
+ rc = qeth_l3_deregister_addr_entry(card, addr);
hash_del(&addr->hnode);
kfree(addr);
@@ -276,6 +262,7 @@
{
int rc = 0;
struct qeth_ipaddr *addr;
+ char buf[40];
QETH_CARD_TEXT(card, 4, "addip");
@@ -286,8 +273,20 @@
QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8);
}
- addr = qeth_l3_ip_from_hash(card, tmp_addr);
- if (!addr) {
+ addr = qeth_l3_find_addr_by_ip(card, tmp_addr);
+ if (addr) {
+ if (tmp_addr->type != QETH_IP_TYPE_NORMAL)
+ return -EADDRINUSE;
+ if (qeth_l3_addr_match_all(addr, tmp_addr)) {
+ addr->ref_counter++;
+ return 0;
+ }
+ qeth_l3_ipaddr_to_string(tmp_addr->proto, (u8 *)&tmp_addr->u,
+ buf);
+ dev_warn(&card->gdev->dev,
+ "Registering IP address %s failed\n", buf);
+ return -EADDRINUSE;
+ } else {
addr = qeth_l3_get_addr_buffer(tmp_addr->proto);
if (!addr)
return -ENOMEM;
@@ -327,18 +326,15 @@
(rc == IPA_RC_LAN_OFFLINE)) {
addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
if (addr->ref_counter < 1) {
- qeth_l3_delete_ip(card, addr);
+ qeth_l3_deregister_addr_entry(card, addr);
+ hash_del(&addr->hnode);
kfree(addr);
}
} else {
hash_del(&addr->hnode);
kfree(addr);
}
- } else {
- if (addr->type == QETH_IP_TYPE_NORMAL)
- addr->ref_counter++;
}
-
return rc;
}
@@ -406,11 +402,7 @@
spin_lock_bh(&card->ip_lock);
hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) {
- if (addr->disp_flag == QETH_DISP_ADDR_DELETE) {
- qeth_l3_deregister_addr_entry(card, addr);
- hash_del(&addr->hnode);
- kfree(addr);
- } else if (addr->disp_flag == QETH_DISP_ADDR_ADD) {
+ if (addr->disp_flag == QETH_DISP_ADDR_ADD) {
if (addr->proto == QETH_PROT_IPV4) {
addr->in_progress = 1;
spin_unlock_bh(&card->ip_lock);
@@ -726,12 +718,7 @@
return -ENOMEM;
spin_lock_bh(&card->ip_lock);
-
- if (qeth_l3_ip_from_hash(card, ipaddr))
- rc = -EEXIST;
- else
- qeth_l3_add_ip(card, ipaddr);
-
+ rc = qeth_l3_add_ip(card, ipaddr);
spin_unlock_bh(&card->ip_lock);
kfree(ipaddr);
@@ -794,12 +781,7 @@
return -ENOMEM;
spin_lock_bh(&card->ip_lock);
-
- if (qeth_l3_ip_from_hash(card, ipaddr))
- rc = -EEXIST;
- else
- qeth_l3_add_ip(card, ipaddr);
-
+ rc = qeth_l3_add_ip(card, ipaddr);
spin_unlock_bh(&card->ip_lock);
kfree(ipaddr);
@@ -1444,8 +1426,9 @@
memcpy(tmp->mac, buf, sizeof(tmp->mac));
tmp->is_multicast = 1;
- ipm = qeth_l3_ip_from_hash(card, tmp);
+ ipm = qeth_l3_find_addr_by_ip(card, tmp);
if (ipm) {
+ /* for mcast, by-IP match means full match */
ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
} else {
ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);
@@ -1528,8 +1511,9 @@
sizeof(struct in6_addr));
tmp->is_multicast = 1;
- ipm = qeth_l3_ip_from_hash(card, tmp);
+ ipm = qeth_l3_find_addr_by_ip(card, tmp);
if (ipm) {
+ /* for mcast, by-IP match means full match */
ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
continue;
}
@@ -2784,11 +2768,12 @@
static int qeth_l3_get_elements_no_tso(struct qeth_card *card,
struct sk_buff *skb, int extra_elems)
{
- addr_t tcpdptr = (addr_t)tcp_hdr(skb) + tcp_hdrlen(skb);
- int elements = qeth_get_elements_for_range(
- tcpdptr,
- (addr_t)skb->data + skb_headlen(skb)) +
- qeth_get_elements_for_frags(skb);
+ addr_t start = (addr_t)tcp_hdr(skb) + tcp_hdrlen(skb);
+ addr_t end = (addr_t)skb->data + skb_headlen(skb);
+ int elements = qeth_get_elements_for_frags(skb);
+
+ if (start != end)
+ elements += qeth_get_elements_for_range(start, end);
if ((elements + extra_elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {
QETH_DBF_MESSAGE(2,
@@ -3207,8 +3192,8 @@
qeth_l3_set_offline(cgdev);
if (card->dev) {
- netif_napi_del(&card->napi);
unregister_netdev(card->dev);
+ free_netdev(card->dev);
card->dev = NULL;
}
diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c
index be65da2..838edbb 100644
--- a/drivers/scsi/be2iscsi/be_cmds.c
+++ b/drivers/scsi/be2iscsi/be_cmds.c
@@ -246,6 +246,12 @@
{
int rc = 0;
+ if (!tag || tag > MAX_MCC_CMD) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BC_%d : invalid tag %u\n", tag);
+ return -EINVAL;
+ }
+
if (beiscsi_hba_in_error(phba)) {
clear_bit(MCC_TAG_STATE_RUNNING,
&phba->ctrl.ptag_state[tag].tag_state);
diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c
index 375c536..c5eb0c4 100644
--- a/drivers/scsi/fcoe/fcoe_transport.c
+++ b/drivers/scsi/fcoe/fcoe_transport.c
@@ -32,13 +32,13 @@
MODULE_DESCRIPTION("FIP discovery protocol and FCoE transport for FCoE HBAs");
MODULE_LICENSE("GPL v2");
-static int fcoe_transport_create(const char *, struct kernel_param *);
-static int fcoe_transport_destroy(const char *, struct kernel_param *);
+static int fcoe_transport_create(const char *, const struct kernel_param *);
+static int fcoe_transport_destroy(const char *, const struct kernel_param *);
static int fcoe_transport_show(char *buffer, const struct kernel_param *kp);
static struct fcoe_transport *fcoe_transport_lookup(struct net_device *device);
static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *device);
-static int fcoe_transport_enable(const char *, struct kernel_param *);
-static int fcoe_transport_disable(const char *, struct kernel_param *);
+static int fcoe_transport_enable(const char *, const struct kernel_param *);
+static int fcoe_transport_disable(const char *, const struct kernel_param *);
static int libfcoe_device_notification(struct notifier_block *notifier,
ulong event, void *ptr);
@@ -865,7 +865,8 @@
*
* Returns: 0 for success
*/
-static int fcoe_transport_create(const char *buffer, struct kernel_param *kp)
+static int fcoe_transport_create(const char *buffer,
+ const struct kernel_param *kp)
{
int rc = -ENODEV;
struct net_device *netdev = NULL;
@@ -930,7 +931,8 @@
*
* Returns: 0 for success
*/
-static int fcoe_transport_destroy(const char *buffer, struct kernel_param *kp)
+static int fcoe_transport_destroy(const char *buffer,
+ const struct kernel_param *kp)
{
int rc = -ENODEV;
struct net_device *netdev = NULL;
@@ -974,7 +976,8 @@
*
* Returns: 0 for success
*/
-static int fcoe_transport_disable(const char *buffer, struct kernel_param *kp)
+static int fcoe_transport_disable(const char *buffer,
+ const struct kernel_param *kp)
{
int rc = -ENODEV;
struct net_device *netdev = NULL;
@@ -1008,7 +1011,8 @@
*
* Returns: 0 for success
*/
-static int fcoe_transport_enable(const char *buffer, struct kernel_param *kp)
+static int fcoe_transport_enable(const char *buffer,
+ const struct kernel_param *kp)
{
int rc = -ENODEV;
struct net_device *netdev = NULL;
diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c
index 44dd372..c056b81 100644
--- a/drivers/scsi/fnic/fnic_scsi.c
+++ b/drivers/scsi/fnic/fnic_scsi.c
@@ -1127,12 +1127,6 @@
else
CMD_ABTS_STATUS(sc) = hdr_status;
- atomic64_dec(&fnic_stats->io_stats.active_ios);
- if (atomic64_read(&fnic->io_cmpl_skip))
- atomic64_dec(&fnic->io_cmpl_skip);
- else
- atomic64_inc(&fnic_stats->io_stats.io_completions);
-
if (!(CMD_FLAGS(sc) & (FNIC_IO_ABORTED | FNIC_IO_DONE)))
atomic64_inc(&misc_stats->no_icmnd_itmf_cmpls);
@@ -1173,6 +1167,11 @@
(((u64)CMD_FLAGS(sc) << 32) |
CMD_STATE(sc)));
sc->scsi_done(sc);
+ atomic64_dec(&fnic_stats->io_stats.active_ios);
+ if (atomic64_read(&fnic->io_cmpl_skip))
+ atomic64_dec(&fnic->io_cmpl_skip);
+ else
+ atomic64_inc(&fnic_stats->io_stats.io_completions);
}
}
@@ -1962,6 +1961,11 @@
/* Call SCSI completion function to complete the IO */
sc->result = (DID_ABORT << 16);
sc->scsi_done(sc);
+ atomic64_dec(&fnic_stats->io_stats.active_ios);
+ if (atomic64_read(&fnic->io_cmpl_skip))
+ atomic64_dec(&fnic->io_cmpl_skip);
+ else
+ atomic64_inc(&fnic_stats->io_stats.io_completions);
}
fnic_abort_cmd_end:
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index 9a0696f..b81a53c 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -367,7 +367,7 @@
};
struct ibmvfc_fcp_rsp_info {
- __be16 reserved;
+ u8 reserved[3];
u8 rsp_code;
u8 reserved2[4];
}__attribute__((packed, aligned (2)));
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index 5324741..c5bc41d 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -836,8 +836,10 @@
qc->err_mask |= AC_ERR_OTHER;
sata_port->ioasa.status |= ATA_BUSY;
- list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
ata_qc_complete(qc);
+ if (ipr_cmd->eh_comp)
+ complete(ipr_cmd->eh_comp);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
}
/**
@@ -5947,8 +5949,10 @@
res->in_erp = 0;
}
scsi_dma_unmap(ipr_cmd->scsi_cmd);
- list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
scsi_cmd->scsi_done(scsi_cmd);
+ if (ipr_cmd->eh_comp)
+ complete(ipr_cmd->eh_comp);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
}
/**
@@ -6338,8 +6342,10 @@
}
scsi_dma_unmap(ipr_cmd->scsi_cmd);
- list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
scsi_cmd->scsi_done(scsi_cmd);
+ if (ipr_cmd->eh_comp)
+ complete(ipr_cmd->eh_comp);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
}
/**
@@ -6365,8 +6371,10 @@
scsi_dma_unmap(scsi_cmd);
spin_lock_irqsave(ipr_cmd->hrrq->lock, lock_flags);
- list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
scsi_cmd->scsi_done(scsi_cmd);
+ if (ipr_cmd->eh_comp)
+ complete(ipr_cmd->eh_comp);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
spin_unlock_irqrestore(ipr_cmd->hrrq->lock, lock_flags);
} else {
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index f7e3f27..e9ea8f4 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -11312,6 +11312,7 @@
lpfc_fof_queue_create(struct lpfc_hba *phba)
{
struct lpfc_queue *qdesc;
+ uint32_t wqesize;
/* Create FOF EQ */
qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.eq_esize,
@@ -11332,8 +11333,11 @@
phba->sli4_hba.oas_cq = qdesc;
/* Create OAS WQ */
- qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.wq_esize,
+ wqesize = (phba->fcp_embed_io) ?
+ LPFC_WQE128_SIZE : phba->sli4_hba.wq_esize;
+ qdesc = lpfc_sli4_queue_alloc(phba, wqesize,
phba->sli4_hba.wq_ecount);
+
if (!qdesc)
goto out_error;
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 8f1df76..0902ed2 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -13696,6 +13696,9 @@
case LPFC_Q_CREATE_VERSION_1:
bf_set(lpfc_mbx_wq_create_wqe_count, &wq_create->u.request_1,
wq->entry_count);
+ bf_set(lpfc_mbox_hdr_version, &shdr->request,
+ LPFC_Q_CREATE_VERSION_1);
+
switch (wq->entry_size) {
default:
case 64:
diff --git a/drivers/scsi/mac_esp.c b/drivers/scsi/mac_esp.c
index 14c0334..26c67c4 100644
--- a/drivers/scsi/mac_esp.c
+++ b/drivers/scsi/mac_esp.c
@@ -55,6 +55,7 @@
int error;
};
static struct esp *esp_chips[2];
+static DEFINE_SPINLOCK(esp_chips_lock);
#define MAC_ESP_GET_PRIV(esp) ((struct mac_esp_priv *) \
platform_get_drvdata((struct platform_device *) \
@@ -562,15 +563,18 @@
}
host->irq = IRQ_MAC_SCSI;
- esp_chips[dev->id] = esp;
- mb();
- if (esp_chips[!dev->id] == NULL) {
- err = request_irq(host->irq, mac_scsi_esp_intr, 0, "ESP", NULL);
- if (err < 0) {
- esp_chips[dev->id] = NULL;
- goto fail_free_priv;
- }
+
+ /* The request_irq() call is intended to succeed for the first device
+ * and fail for the second device.
+ */
+ err = request_irq(host->irq, mac_scsi_esp_intr, 0, "ESP", NULL);
+ spin_lock(&esp_chips_lock);
+ if (err < 0 && esp_chips[!dev->id] == NULL) {
+ spin_unlock(&esp_chips_lock);
+ goto fail_free_priv;
}
+ esp_chips[dev->id] = esp;
+ spin_unlock(&esp_chips_lock);
err = scsi_esp_register(esp, &dev->dev);
if (err)
@@ -579,8 +583,13 @@
return 0;
fail_free_irq:
- if (esp_chips[!dev->id] == NULL)
+ spin_lock(&esp_chips_lock);
+ esp_chips[dev->id] = NULL;
+ if (esp_chips[!dev->id] == NULL) {
+ spin_unlock(&esp_chips_lock);
free_irq(host->irq, esp);
+ } else
+ spin_unlock(&esp_chips_lock);
fail_free_priv:
kfree(mep);
fail_free_command_block:
@@ -599,9 +608,13 @@
scsi_esp_unregister(esp);
+ spin_lock(&esp_chips_lock);
esp_chips[dev->id] = NULL;
- if (!(esp_chips[0] || esp_chips[1]))
+ if (esp_chips[!dev->id] == NULL) {
+ spin_unlock(&esp_chips_lock);
free_irq(irq, NULL);
+ } else
+ spin_unlock(&esp_chips_lock);
kfree(mep);
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c
index a1a5ceb..8e83e34 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.c
@@ -105,7 +105,7 @@
*
*/
static int
-_scsih_set_fwfault_debug(const char *val, struct kernel_param *kp)
+_scsih_set_fwfault_debug(const char *val, const struct kernel_param *kp)
{
int ret = param_set_int(val, kp);
struct MPT3SAS_ADAPTER *ioc;
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index 468acab..7568e06 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -281,7 +281,7 @@
* Note: The logging levels are defined in mpt3sas_debug.h.
*/
static int
-_scsih_set_debug_level(const char *val, struct kernel_param *kp)
+_scsih_set_debug_level(const char *val, const struct kernel_param *kp)
{
int ret = param_set_int(val, kp);
struct MPT3SAS_ADAPTER *ioc;
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 8f12f6b..4441a55 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -369,6 +369,7 @@
srb_t *sp = (srb_t *)ptr;
struct srb_iocb *abt = &sp->u.iocb_cmd;
+ del_timer(&sp->u.iocb_cmd.timer);
complete(&abt->u.abt.comp);
}
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 94630d4..baccd11 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -1443,7 +1443,7 @@
void
qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res)
{
- int que, cnt;
+ int que, cnt, status;
unsigned long flags;
srb_t *sp;
struct qla_hw_data *ha = vha->hw;
@@ -1473,8 +1473,12 @@
*/
sp_get(sp);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
- qla2xxx_eh_abort(GET_CMD_SP(sp));
+ status = qla2xxx_eh_abort(GET_CMD_SP(sp));
spin_lock_irqsave(&ha->hardware_lock, flags);
+ /* Get rid of extra reference if immediate exit
+ * from ql2xxx_eh_abort */
+ if (status == FAILED && (qla2x00_isp_reg_stat(ha)))
+ atomic_dec(&sp->ref_count);
}
req->outstanding_cmds[cnt] = NULL;
sp->done(vha, sp, res);
diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c
index 59059ff..11f45cb 100644
--- a/drivers/scsi/qla2xxx/qla_target.c
+++ b/drivers/scsi/qla2xxx/qla_target.c
@@ -5789,7 +5789,7 @@
fc_port_t *fcport;
int rc;
- fcport = kzalloc(sizeof(*fcport), GFP_KERNEL);
+ fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
if (!fcport) {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06f,
"qla_target(%d): Allocation of tmp FC port failed",
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
index 26e6b05..43d4b30 100644
--- a/drivers/scsi/scsi_devinfo.c
+++ b/drivers/scsi/scsi_devinfo.c
@@ -180,7 +180,7 @@
{"HITACHI", "6586-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
{"HITACHI", "6588-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
{"HP", "A6189A", NULL, BLIST_SPARSELUN | BLIST_LARGELUN}, /* HP VA7400 */
- {"HP", "OPEN-", "*", BLIST_REPORTLUN2}, /* HP XP Arrays */
+ {"HP", "OPEN-", "*", BLIST_REPORTLUN2 | BLIST_TRY_VPD_PAGES}, /* HP XP Arrays */
{"HP", "NetRAID-4M", NULL, BLIST_FORCELUN},
{"HP", "HSV100", NULL, BLIST_REPORTLUN2 | BLIST_NOSTARTONADD},
{"HP", "C1557A", NULL, BLIST_FORCELUN},
@@ -596,17 +596,12 @@
int key)
{
struct scsi_dev_info_list *devinfo;
- int err;
devinfo = scsi_dev_info_list_find(vendor, model, key);
if (!IS_ERR(devinfo))
return devinfo->flags;
- err = PTR_ERR(devinfo);
- if (err != -ENOENT)
- return err;
-
- /* nothing found, return nothing */
+ /* key or device not found: return nothing */
if (key != SCSI_DEVINFO_GLOBAL)
return 0;
diff --git a/drivers/scsi/scsi_dh.c b/drivers/scsi/scsi_dh.c
index 84addee..a5e30e9 100644
--- a/drivers/scsi/scsi_dh.c
+++ b/drivers/scsi/scsi_dh.c
@@ -56,10 +56,13 @@
{"IBM", "1815", "rdac", },
{"IBM", "1818", "rdac", },
{"IBM", "3526", "rdac", },
+ {"IBM", "3542", "rdac", },
+ {"IBM", "3552", "rdac", },
{"SGI", "TP9", "rdac", },
{"SGI", "IS", "rdac", },
- {"STK", "OPENstorage D280", "rdac", },
+ {"STK", "OPENstorage", "rdac", },
{"STK", "FLEXLINE 380", "rdac", },
+ {"STK", "BladeCtlr", "rdac", },
{"SUN", "CSM", "rdac", },
{"SUN", "LCSM100", "rdac", },
{"SUN", "STK6580_6780", "rdac", },
diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c
index 50adabb..69046d3 100644
--- a/drivers/scsi/ses.c
+++ b/drivers/scsi/ses.c
@@ -548,7 +548,6 @@
ecomp = &edev->component[components++];
if (!IS_ERR(ecomp)) {
- ses_get_power_status(edev, ecomp);
if (addl_desc_ptr)
ses_process_descriptor(
ecomp,
@@ -579,13 +578,16 @@
}
static void ses_match_to_enclosure(struct enclosure_device *edev,
- struct scsi_device *sdev)
+ struct scsi_device *sdev,
+ int refresh)
{
+ struct scsi_device *edev_sdev = to_scsi_device(edev->edev.parent);
struct efd efd = {
.addr = 0,
};
- ses_enclosure_data_process(edev, to_scsi_device(edev->edev.parent), 0);
+ if (refresh)
+ ses_enclosure_data_process(edev, edev_sdev, 0);
if (scsi_is_sas_rphy(sdev->sdev_target->dev.parent))
efd.addr = sas_get_address(sdev);
@@ -616,7 +618,7 @@
struct enclosure_device *prev = NULL;
while ((edev = enclosure_find(&sdev->host->shost_gendev, prev)) != NULL) {
- ses_match_to_enclosure(edev, sdev);
+ ses_match_to_enclosure(edev, sdev, 1);
prev = edev;
}
return -ENODEV;
@@ -728,7 +730,7 @@
shost_for_each_device(tmp_sdev, sdev->host) {
if (tmp_sdev->lun != 0 || scsi_device_enclosure(tmp_sdev))
continue;
- ses_match_to_enclosure(edev, tmp_sdev);
+ ses_match_to_enclosure(edev, tmp_sdev, 0);
}
return 0;
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 3a7a86d..3d9ad4c 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -524,6 +524,7 @@
} else
count = (old_hdr->result == 0) ? 0 : -EIO;
sg_finish_rem_req(srp);
+ sg_remove_request(sfp, srp);
retval = count;
free_old_hdr:
kfree(old_hdr);
@@ -564,6 +565,7 @@
}
err_out:
err2 = sg_finish_rem_req(srp);
+ sg_remove_request(sfp, srp);
return err ? : err2 ? : count;
}
@@ -663,18 +665,14 @@
* is a non-zero input_size, so emit a warning.
*/
if (hp->dxfer_direction == SG_DXFER_TO_FROM_DEV) {
- static char cmd[TASK_COMM_LEN];
- if (strcmp(current->comm, cmd)) {
- printk_ratelimited(KERN_WARNING
- "sg_write: data in/out %d/%d bytes "
- "for SCSI command 0x%x-- guessing "
- "data in;\n program %s not setting "
- "count and/or reply_len properly\n",
- old_hdr.reply_len - (int)SZ_SG_HEADER,
- input_size, (unsigned int) cmnd[0],
- current->comm);
- strcpy(cmd, current->comm);
- }
+ printk_ratelimited(KERN_WARNING
+ "sg_write: data in/out %d/%d bytes "
+ "for SCSI command 0x%x-- guessing "
+ "data in;\n program %s not setting "
+ "count and/or reply_len properly\n",
+ old_hdr.reply_len - (int)SZ_SG_HEADER,
+ input_size, (unsigned int) cmnd[0],
+ current->comm);
}
k = sg_common_write(sfp, srp, cmnd, sfp->timeout, blocking);
return (k < 0) ? k : count;
@@ -773,11 +771,15 @@
"sg_common_write: scsi opcode=0x%02x, cmd_size=%d\n",
(int) cmnd[0], (int) hp->cmd_len));
+ if (hp->dxfer_len >= SZ_256M)
+ return -EINVAL;
+
k = sg_start_req(srp, cmnd);
if (k) {
SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sfp->parentdp,
"sg_common_write: start_req err=%d\n", k));
sg_finish_rem_req(srp);
+ sg_remove_request(sfp, srp);
return k; /* probably out of space --> ENOMEM */
}
if (atomic_read(&sdp->detaching)) {
@@ -790,6 +792,7 @@
}
sg_finish_rem_req(srp);
+ sg_remove_request(sfp, srp);
return -ENODEV;
}
@@ -1287,6 +1290,7 @@
struct sg_fd *sfp = srp->parentfp;
sg_finish_rem_req(srp);
+ sg_remove_request(sfp, srp);
kref_put(&sfp->f_ref, sg_remove_sfp);
}
@@ -1828,8 +1832,6 @@
else
sg_remove_scat(sfp, req_schp);
- sg_remove_request(sfp, srp);
-
return ret;
}
@@ -2066,11 +2068,12 @@
if ((1 == resp->done) && (!resp->sg_io_owned) &&
((-1 == pack_id) || (resp->header.pack_id == pack_id))) {
resp->done = 2; /* guard against other readers */
- break;
+ write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ return resp;
}
}
write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
- return resp;
+ return NULL;
}
/* always adds to end of list */
@@ -2176,12 +2179,17 @@
struct sg_fd *sfp = container_of(work, struct sg_fd, ew.work);
struct sg_device *sdp = sfp->parentdp;
Sg_request *srp;
+ unsigned long iflags;
/* Cleanup any responses which were never read(). */
+ write_lock_irqsave(&sfp->rq_list_lock, iflags);
while (!list_empty(&sfp->rq_list)) {
srp = list_first_entry(&sfp->rq_list, Sg_request, entry);
sg_finish_rem_req(srp);
+ list_del(&srp->entry);
+ srp->parentfp = NULL;
}
+ write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
if (sfp->reserve.bufflen > 0) {
SCSI_LOG_TIMEOUT(6, sg_printk(KERN_INFO, sdp,
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index 2bf96d3..0dd1984 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -915,10 +915,11 @@
case TEST_UNIT_READY:
break;
default:
- set_host_byte(scmnd, DID_TARGET_FAILURE);
+ set_host_byte(scmnd, DID_ERROR);
}
break;
case SRB_STATUS_INVALID_LUN:
+ set_host_byte(scmnd, DID_NO_CONNECT);
do_work = true;
process_err_fn = storvsc_remove_lun;
break;
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
index c680d76..8f4adc1 100644
--- a/drivers/scsi/virtio_scsi.c
+++ b/drivers/scsi/virtio_scsi.c
@@ -28,6 +28,7 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_devinfo.h>
#include <linux/seqlock.h>
#define VIRTIO_SCSI_MEMPOOL_SZ 64
@@ -705,6 +706,28 @@
return virtscsi_tmf(vscsi, cmd);
}
+static int virtscsi_device_alloc(struct scsi_device *sdevice)
+{
+ /*
+ * Passed through SCSI targets (e.g. with qemu's 'scsi-block')
+ * may have transfer limits which come from the host SCSI
+ * controller or something on the host side other than the
+ * target itself.
+ *
+ * To make this work properly, the hypervisor can adjust the
+ * target's VPD information to advertise these limits. But
+ * for that to work, the guest has to look at the VPD pages,
+ * which we won't do by default if it is an SPC-2 device, even
+ * if it does actually support it.
+ *
+ * So, set the blist to always try to read the VPD pages.
+ */
+ sdevice->sdev_bflags = BLIST_TRY_VPD_PAGES;
+
+ return 0;
+}
+
+
/**
* virtscsi_change_queue_depth() - Change a virtscsi target's queue depth
* @sdev: Virtscsi target whose queue depth to change
@@ -776,6 +799,7 @@
.change_queue_depth = virtscsi_change_queue_depth,
.eh_abort_handler = virtscsi_abort,
.eh_device_reset_handler = virtscsi_device_reset,
+ .slave_alloc = virtscsi_device_alloc,
.can_queue = 1024,
.dma_boundary = UINT_MAX,
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index 5fb0c29..26dfd3f 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, 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
@@ -425,6 +425,7 @@
const u8 *old_wbuf = NULL;
bool report_sat = false;
bool sync_wr = true;
+ bool txn_async = txn->async;
memset(wbuf, 0, sizeof(wbuf));
if (txn->mc & SLIM_MSG_CLK_PAUSE_SEQ_FLG)
@@ -769,9 +770,9 @@
msm_slim_put_ctrl(dev);
}
ngd_xfer_ret:
- if (txn->wbuf == wbuf)
+ if (!txn_async && txn->wbuf == wbuf)
txn->wbuf = old_wbuf;
- if (txn->comp == &done)
+ if (!txn_async && txn->comp == &done)
txn->comp = NULL;
return ret ? ret : dev->err;
}
diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c
index 119054b..2caacd9 100644
--- a/drivers/soc/fsl/qbman/qman.c
+++ b/drivers/soc/fsl/qbman/qman.c
@@ -2429,39 +2429,21 @@
struct completion completion;
};
-static int qman_delete_cgr_thread(void *p)
+static void qman_delete_cgr_smp_call(void *p)
{
- struct cgr_comp *cgr_comp = (struct cgr_comp *)p;
- int ret;
-
- ret = qman_delete_cgr(cgr_comp->cgr);
- complete(&cgr_comp->completion);
-
- return ret;
+ qman_delete_cgr((struct qman_cgr *)p);
}
void qman_delete_cgr_safe(struct qman_cgr *cgr)
{
- struct task_struct *thread;
- struct cgr_comp cgr_comp;
-
preempt_disable();
if (qman_cgr_cpus[cgr->cgrid] != smp_processor_id()) {
- init_completion(&cgr_comp.completion);
- cgr_comp.cgr = cgr;
- thread = kthread_create(qman_delete_cgr_thread, &cgr_comp,
- "cgr_del");
-
- if (IS_ERR(thread))
- goto out;
-
- kthread_bind(thread, qman_cgr_cpus[cgr->cgrid]);
- wake_up_process(thread);
- wait_for_completion(&cgr_comp.completion);
+ smp_call_function_single(qman_cgr_cpus[cgr->cgrid],
+ qman_delete_cgr_smp_call, cgr, true);
preempt_enable();
return;
}
-out:
+
qman_delete_cgr(cgr);
preempt_enable();
}
diff --git a/drivers/soc/fsl/qe/qe.c b/drivers/soc/fsl/qe/qe.c
index 2707a82..5482302 100644
--- a/drivers/soc/fsl/qe/qe.c
+++ b/drivers/soc/fsl/qe/qe.c
@@ -163,11 +163,15 @@
*/
static unsigned int brg_clk = 0;
+#define CLK_GRAN (1000)
+#define CLK_GRAN_LIMIT (5)
+
unsigned int qe_get_brg_clk(void)
{
struct device_node *qe;
int size;
const u32 *prop;
+ unsigned int mod;
if (brg_clk)
return brg_clk;
@@ -185,6 +189,15 @@
of_node_put(qe);
+ /* round this if near to a multiple of CLK_GRAN */
+ mod = brg_clk % CLK_GRAN;
+ if (mod) {
+ if (mod < CLK_GRAN_LIMIT)
+ brg_clk -= mod;
+ else if (mod > (CLK_GRAN - CLK_GRAN_LIMIT))
+ brg_clk += CLK_GRAN - mod;
+ }
+
return brg_clk;
}
EXPORT_SYMBOL(qe_get_brg_clk);
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 2e012a2..683b074 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -471,6 +471,8 @@
u16 line_number;
char function_name[QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1];
struct mutex dev_lock;
+ uint32_t fw_error_fatal_irq;
+ uint32_t fw_early_crash_irq;
} *penv;
#ifdef CONFIG_ICNSS_DEBUG
@@ -1216,17 +1218,21 @@
return IRQ_HANDLED;
}
-static void register_fw_error_notifications(struct icnss_priv *priv)
+static void register_fw_error_notifications(struct device *dev)
{
- int gpio, irq, ret;
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+ int gpio = 0, irq = 0, ret = 0;
- if (!of_find_property(priv->pdev->dev.of_node,
- "qcom,gpio-force-fatal-error", NULL)) {
+ if (!priv)
+ return;
+
+ if (!of_find_property(dev->of_node, "qcom,gpio-force-fatal-error",
+ NULL)) {
icnss_pr_dbg("Error fatal smp2p handler not registered\n");
return;
}
- gpio = of_get_named_gpio(priv->pdev->dev.of_node,
- "qcom,gpio-force-fatal-error", 0);
+ gpio = of_get_named_gpio(dev->of_node, "qcom,gpio-force-fatal-error",
+ 0);
if (!gpio_is_valid(gpio)) {
icnss_pr_err("Invalid GPIO for error fatal smp2p %d\n", gpio);
return;
@@ -1236,22 +1242,31 @@
icnss_pr_err("Invalid IRQ for error fatal smp2p %u\n", irq);
return;
}
- ret = request_irq(irq, fw_error_fatal_handler,
- IRQF_TRIGGER_RISING, "wlanfw-err", priv);
+ ret = devm_request_irq(dev, irq, fw_error_fatal_handler,
+ IRQF_TRIGGER_RISING, "wlanfw-err", priv);
if (ret < 0) {
icnss_pr_err("Unable to register for error fatal IRQ handler %d",
- irq);
+ irq);
return;
}
- icnss_pr_dbg("FW force error fatal handler registered\n");
+ icnss_pr_dbg("FW force error fatal handler registered irq = %d\n", irq);
+ priv->fw_error_fatal_irq = irq;
+}
- if (!of_find_property(priv->pdev->dev.of_node,
- "qcom,gpio-early-crash-ind", NULL)) {
+static void register_early_crash_notifications(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+ int gpio = 0, irq = 0, ret = 0;
+
+ if (!priv)
+ return;
+
+ if (!of_find_property(dev->of_node, "qcom,gpio-early-crash-ind",
+ NULL)) {
icnss_pr_dbg("FW early crash indication handler not registered\n");
return;
}
- gpio = of_get_named_gpio(priv->pdev->dev.of_node,
- "qcom,gpio-early-crash-ind", 0);
+ gpio = of_get_named_gpio(dev->of_node, "qcom,gpio-early-crash-ind", 0);
if (!gpio_is_valid(gpio)) {
icnss_pr_err("Invalid GPIO for early crash indication %d\n",
gpio);
@@ -1263,14 +1278,16 @@
irq);
return;
}
- ret = request_irq(irq, fw_crash_indication_handler,
- IRQF_TRIGGER_RISING, "wlanfw-early-crash-ind", priv);
+ ret = devm_request_irq(dev, irq, fw_crash_indication_handler,
+ IRQF_TRIGGER_RISING, "wlanfw-early-crash-ind",
+ priv);
if (ret < 0) {
icnss_pr_err("Unable to register for early crash indication IRQ handler %d",
irq);
return;
}
- icnss_pr_dbg("FW crash indication handler registered\n");
+ icnss_pr_dbg("FW crash indication handler registered irq = %d\n", irq);
+ priv->fw_early_crash_irq = irq;
}
static int wlfw_msa_mem_info_send_sync_msg(void)
@@ -2162,7 +2179,11 @@
icnss_init_vph_monitor(penv);
- register_fw_error_notifications(penv);
+ if (!penv->fw_error_fatal_irq)
+ register_fw_error_notifications(&penv->pdev->dev);
+
+ if (!penv->fw_early_crash_irq)
+ register_early_crash_notifications(&penv->pdev->dev);
return ret;
diff --git a/drivers/soc/qcom/ramdump.c b/drivers/soc/qcom/ramdump.c
index 7758c64..789eee7 100644
--- a/drivers/soc/qcom/ramdump.c
+++ b/drivers/soc/qcom/ramdump.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, 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
@@ -455,19 +455,19 @@
}
static inline unsigned int set_section_name(const char *name,
- struct elfhdr *ehdr)
+ struct elfhdr *ehdr,
+ int *strtable_idx)
{
char *strtab = elf_str_table(ehdr);
- static int strtable_idx = 1;
int idx, ret = 0;
- idx = strtable_idx;
+ idx = *strtable_idx;
if ((strtab == NULL) || (name == NULL))
return 0;
ret = idx;
idx += strlcpy((strtab + idx), name, MAX_NAME_LENGTH);
- strtable_idx = idx + 1;
+ *strtable_idx = idx + 1;
return ret;
}
@@ -480,6 +480,7 @@
struct elfhdr *ehdr;
struct elf_shdr *shdr;
unsigned long offset, strtbl_off;
+ int strtable_idx = 1;
if (!rd_dev->consumer_present) {
pr_err("Ramdump(%s): No consumers. Aborting..\n", rd_dev->name);
@@ -519,13 +520,14 @@
shdr->sh_size = MAX_STRTBL_SIZE;
shdr->sh_entsize = 0;
shdr->sh_flags = 0;
- shdr->sh_name = set_section_name("STR_TBL", ehdr);
+ shdr->sh_name = set_section_name("STR_TBL", ehdr, &strtable_idx);
shdr++;
for (i = 0; i < nsegments; i++, shdr++) {
/* Update elf header */
shdr->sh_type = SHT_PROGBITS;
- shdr->sh_name = set_section_name(segments[i].name, ehdr);
+ shdr->sh_name = set_section_name(segments[i].name, ehdr,
+ &strtable_idx);
shdr->sh_addr = (elf_addr_t)segments[i].address;
shdr->sh_size = segments[i].size;
shdr->sh_flags = SHF_WRITE;
diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c
index 9dfe281..5abc093 100644
--- a/drivers/soc/qcom/service-locator.c
+++ b/drivers/soc/qcom/service-locator.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, 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,6 +34,7 @@
#define QMI_SERVREG_LOC_SERVER_INITIAL_TIMEOUT 2000
#define QMI_SERVREG_LOC_SERVER_TIMEOUT 2000
#define INITIAL_TIMEOUT 100000
+#define LOCATOR_SERVICE_TIMEOUT 300000
#define LOCATOR_NOT_PRESENT 0
#define LOCATOR_PRESENT 1
@@ -302,13 +303,20 @@
static int init_service_locator(void)
{
int rc = 0;
+ static bool service_timedout;
- mutex_lock(&service_init_mutex);
+ rc = mutex_lock_interruptible(&service_init_mutex);
+ if (rc)
+ return rc;
if (locator_status == LOCATOR_NOT_PRESENT) {
pr_err("Service Locator not enabled\n");
rc = -ENODEV;
goto inited;
}
+ if (service_timedout) {
+ rc = -ETIME;
+ goto inited;
+ }
if (service_inited)
goto inited;
@@ -336,7 +344,20 @@
goto inited;
}
- wait_for_completion(&service_locator.service_available);
+ rc = wait_for_completion_interruptible_timeout(
+ &service_locator.service_available,
+ msecs_to_jiffies(LOCATOR_SERVICE_TIMEOUT));
+ if (rc < 0) {
+ pr_err("Wait for locator service interrupted by signal\n");
+ goto inited;
+ }
+ if (!rc) {
+ pr_err("%s: wait for locator service timed out\n", __func__);
+ service_timedout = true;
+ rc = -ETIME;
+ goto inited;
+ }
+
service_inited = true;
mutex_unlock(&service_init_mutex);
pr_info("Service locator initialized\n");
diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c
index 0b037d4..d568014 100644
--- a/drivers/soc/qcom/spcom.c
+++ b/drivers/soc/qcom/spcom.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, 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
@@ -374,7 +374,7 @@
switch (cb_info->link_state) {
case GLINK_LINK_STATE_UP:
- pr_info("GLINK_LINK_STATE_UP.\n");
+ pr_debug("GLINK_LINK_STATE_UP.\n");
spcom_create_predefined_channels_chardev();
break;
case GLINK_LINK_STATE_DOWN:
@@ -1170,7 +1170,7 @@
kfree(client);
client = NULL;
} else {
- pr_info("remote side connect to channel [%s].\n", name);
+ pr_debug("remote side connect to channel [%s].\n", name);
}
return client;
@@ -2010,6 +2010,11 @@
pr_debug("cmd_id [0x%x] cmd_name [%s].\n", cmd_id, cmd_name);
+ if (!ch && cmd_id != SPCOM_CMD_CREATE_CHANNEL) {
+ pr_err("channel context is null\n");
+ return -EINVAL;
+ }
+
switch (cmd_id) {
case SPCOM_CMD_SEND:
ret = spcom_handle_send_command(ch, buf, buf_size);
@@ -2342,8 +2347,12 @@
ch = filp->private_data;
if (!ch) {
- pr_err("invalid ch pointer, command not allowed.\n");
- return -EINVAL;
+ if (strcmp(name, DEVICE_NAME) == 0) {
+ pr_debug("control device - no channel context.\n");
+ } else {
+ pr_err("NULL ch pointer, command not allowed.\n");
+ return -EINVAL;
+ }
} else {
/* Check if remote side connect */
if (!spcom_is_channel_connected(ch)) {
@@ -2784,7 +2793,7 @@
goto fail_ion_client;
}
- pr_info("Driver Initialization ok.\n");
+ pr_debug("Driver Initialization ok.\n");
return 0;
@@ -2821,7 +2830,7 @@
{
int ret;
- pr_info("spcom driver version 1.3 28-Dec-2017.\n");
+ pr_debug("spcom driver version 1.4 30-Apr-2018.\n");
ret = platform_driver_register(&spcom_driver);
if (ret)
diff --git a/drivers/soc/qcom/wcnss/wcnss_wlan.c b/drivers/soc/qcom/wcnss/wcnss_wlan.c
index 481dbd0..c3fb30b 100644
--- a/drivers/soc/qcom/wcnss/wcnss_wlan.c
+++ b/drivers/soc/qcom/wcnss/wcnss_wlan.c
@@ -1673,7 +1673,7 @@
static int enable_wcnss_suspend_notify;
static int enable_wcnss_suspend_notify_set(const char *val,
- struct kernel_param *kp)
+ const struct kernel_param *kp)
{
int ret;
@@ -1771,6 +1771,15 @@
return 0;
}
+static int wcnss_wlan_suspend_noirq(struct device *dev)
+{
+ if (penv && dev && (dev == &penv->pdev->dev) &&
+ penv->smd_channel_ready &&
+ penv->pm_ops && penv->pm_ops->suspend_noirq)
+ return penv->pm_ops->suspend_noirq(dev);
+ return 0;
+}
+
static int wcnss_wlan_resume(struct device *dev)
{
if (penv && dev && (dev == &penv->pdev->dev) &&
@@ -1780,6 +1789,15 @@
return 0;
}
+static int wcnss_wlan_resume_noirq(struct device *dev)
+{
+ if (penv && dev && (dev == &penv->pdev->dev) &&
+ penv->smd_channel_ready &&
+ penv->pm_ops && penv->pm_ops->resume_noirq)
+ return penv->pm_ops->resume_noirq(dev);
+ return 0;
+}
+
void wcnss_prevent_suspend(void)
{
if (penv)
@@ -3535,6 +3553,8 @@
static const struct dev_pm_ops wcnss_wlan_pm_ops = {
.suspend = wcnss_wlan_suspend,
.resume = wcnss_wlan_resume,
+ .suspend_noirq = wcnss_wlan_suspend_noirq,
+ .resume_noirq = wcnss_wlan_resume_noirq,
};
#ifdef CONFIG_WCNSS_CORE_PRONTO
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 8feac59..44be6b5 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -1669,12 +1669,12 @@
pm_runtime_get_sync(&pdev->dev);
/* reset the hardware and block queue progress */
- spin_lock_irq(&as->lock);
if (as->use_dma) {
atmel_spi_stop_dma(as);
atmel_spi_release_dma(as);
}
+ spin_lock_irq(&as->lock);
spi_writel(as, CR, SPI_BIT(SWRST));
spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */
spi_readl(as, SR);
diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c
index 447497e..d25cc40 100644
--- a/drivers/spi/spi-dw-mmio.c
+++ b/drivers/spi/spi-dw-mmio.c
@@ -115,8 +115,8 @@
{
struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev);
- clk_disable_unprepare(dwsmmio->clk);
dw_spi_remove_host(&dwsmmio->dws);
+ clk_disable_unprepare(dwsmmio->clk);
return 0;
}
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index d5157b2..a47cf63 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -454,6 +454,8 @@
int elements = 0;
int word_len, element_count;
struct omap2_mcspi_cs *cs = spi->controller_state;
+ void __iomem *chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
+
mcspi = spi_master_get_devdata(spi->master);
mcspi_dma = &mcspi->dma_channels[spi->chip_select];
count = xfer->len;
@@ -549,8 +551,8 @@
if (l & OMAP2_MCSPI_CHCONF_TURBO) {
elements--;
- if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
- & OMAP2_MCSPI_CHSTAT_RXS)) {
+ if (!mcspi_wait_for_reg_bit(chstat_reg,
+ OMAP2_MCSPI_CHSTAT_RXS)) {
u32 w;
w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);
@@ -568,8 +570,7 @@
return count;
}
}
- if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
- & OMAP2_MCSPI_CHSTAT_RXS)) {
+ if (!mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_RXS)) {
u32 w;
w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);
diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c
index 9918a57..7e7da97 100644
--- a/drivers/spi/spi-sun6i.c
+++ b/drivers/spi/spi-sun6i.c
@@ -464,7 +464,7 @@
static int sun6i_spi_remove(struct platform_device *pdev)
{
- pm_runtime_disable(&pdev->dev);
+ pm_runtime_force_suspend(&pdev->dev);
return 0;
}
diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c
index 35697e6..f4ffac4 100644
--- a/drivers/staging/android/ashmem.c
+++ b/drivers/staging/android/ashmem.c
@@ -343,24 +343,23 @@
mutex_lock(&ashmem_mutex);
if (asma->size == 0) {
- ret = -EINVAL;
- goto out;
+ mutex_unlock(&ashmem_mutex);
+ return -EINVAL;
}
if (!asma->file) {
- ret = -EBADF;
- goto out;
+ mutex_unlock(&ashmem_mutex);
+ return -EBADF;
}
+ mutex_unlock(&ashmem_mutex);
+
ret = vfs_llseek(asma->file, offset, origin);
if (ret < 0)
- goto out;
+ return ret;
/** Copy f_pos from backing file, since f_ops->llseek() sets it */
file->f_pos = asma->file->f_pos;
-
-out:
- mutex_unlock(&ashmem_mutex);
return ret;
}
@@ -711,16 +710,14 @@
size_t pgstart, pgend;
int ret = -EINVAL;
+ if (unlikely(copy_from_user(&pin, p, sizeof(pin))))
+ return -EFAULT;
+
mutex_lock(&ashmem_mutex);
if (unlikely(!asma->file))
goto out_unlock;
- if (unlikely(copy_from_user(&pin, p, sizeof(pin)))) {
- ret = -EFAULT;
- goto out_unlock;
- }
-
/* per custom, you can pass zero for len to mean "everything onward" */
if (!pin.len)
pin.len = PAGE_ALIGN(asma->size) - pin.offset;
diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c
index 800c167..3ac71ed 100644
--- a/drivers/staging/android/ion/ion_system_heap.c
+++ b/drivers/staging/android/ion/ion_system_heap.c
@@ -2,7 +2,7 @@
* drivers/staging/android/ion/ion_system_heap.c
*
* Copyright (C) 2011 Google, Inc.
- * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2018, 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
@@ -36,7 +36,11 @@
static gfp_t low_order_gfp_flags = (GFP_HIGHUSER | __GFP_NOWARN);
#ifndef CONFIG_ALLOC_BUFFERS_IN_4K_CHUNKS
-static const unsigned int orders[] = {9, 8, 4, 0};
+#if defined(CONFIG_IOMMU_IO_PGTABLE_ARMV7S)
+static const unsigned int orders[] = {8, 4, 0};
+#else
+static const unsigned int orders[] = {9, 4, 0};
+#endif
#else
static const unsigned int orders[] = {0};
#endif
diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c
index a5bf2cc..1736248 100644
--- a/drivers/staging/comedi/drivers.c
+++ b/drivers/staging/comedi/drivers.c
@@ -484,8 +484,7 @@
struct comedi_cmd *cmd = &async->cmd;
if (cmd->stop_src == TRIG_COUNT) {
- unsigned int nscans = nsamples / cmd->scan_end_arg;
- unsigned int scans_left = __comedi_nscans_left(s, nscans);
+ unsigned int scans_left = __comedi_nscans_left(s, cmd->stop_arg);
unsigned int scan_pos =
comedi_bytes_to_samples(s, async->scan_progress);
unsigned long long samples_left = 0;
diff --git a/drivers/staging/lustre/lustre/mdc/mdc_request.c b/drivers/staging/lustre/lustre/mdc/mdc_request.c
index f56ea64..6a146f4 100644
--- a/drivers/staging/lustre/lustre/mdc/mdc_request.c
+++ b/drivers/staging/lustre/lustre/mdc/mdc_request.c
@@ -1219,9 +1219,9 @@
* in PAGE_SIZE (if PAGE_SIZE greater than LU_PAGE_SIZE), and the
* lu_dirpage for this integrated page will be adjusted.
**/
-static int mdc_read_page_remote(void *data, struct page *page0)
+static int mdc_read_page_remote(struct file *data, struct page *page0)
{
- struct readpage_param *rp = data;
+ struct readpage_param *rp = (struct readpage_param *)data;
struct page **page_pool;
struct page *page;
struct lu_dirpage *dp;
diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec.c b/drivers/staging/lustre/lustre/ptlrpc/sec.c
index a7416cd..7b0587d 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/sec.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/sec.c
@@ -838,7 +838,7 @@
if (req->rq_pool || !req->rq_reqbuf)
return;
- kfree(req->rq_reqbuf);
+ kvfree(req->rq_reqbuf);
req->rq_reqbuf = NULL;
req->rq_reqbuf_len = 0;
}
diff --git a/drivers/staging/speakup/kobjects.c b/drivers/staging/speakup/kobjects.c
index e744aa9..dea018c 100644
--- a/drivers/staging/speakup/kobjects.c
+++ b/drivers/staging/speakup/kobjects.c
@@ -834,7 +834,9 @@
struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
unsigned long flags;
- BUG_ON(!group);
+ if (WARN_ON(!group))
+ return -EINVAL;
+
spin_lock_irqsave(&speakup_info.spinlock, flags);
retval = message_show_helper(buf, group->start, group->end);
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
@@ -846,7 +848,9 @@
{
struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
- BUG_ON(!group);
+ if (WARN_ON(!group))
+ return -EINVAL;
+
return message_store_helper(buf, count, group);
}
diff --git a/drivers/staging/unisys/visorhba/visorhba_main.c b/drivers/staging/unisys/visorhba/visorhba_main.c
index 5a7a87e..28b5392 100644
--- a/drivers/staging/unisys/visorhba/visorhba_main.c
+++ b/drivers/staging/unisys/visorhba/visorhba_main.c
@@ -842,7 +842,7 @@
do_scsi_nolinuxstat(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd)
{
struct scsi_device *scsidev;
- unsigned char buf[36];
+ unsigned char *buf;
struct scatterlist *sg;
unsigned int i;
char *this_page;
@@ -857,6 +857,10 @@
if (cmdrsp->scsi.no_disk_result == 0)
return;
+ buf = kzalloc(sizeof(char) * 36, GFP_KERNEL);
+ if (!buf)
+ return;
+
/* Linux scsi code wants a device at Lun 0
* to issue report luns, but we don't want
* a disk there so we'll present a processor
@@ -868,6 +872,7 @@
if (scsi_sg_count(scsicmd) == 0) {
memcpy(scsi_sglist(scsicmd), buf,
cmdrsp->scsi.bufflen);
+ kfree(buf);
return;
}
@@ -879,6 +884,7 @@
memcpy(this_page, buf + bufind, sg[i].length);
kunmap_atomic(this_page_orig);
}
+ kfree(buf);
} else {
devdata = (struct visorhba_devdata *)scsidev->host->hostdata;
for_each_vdisk_match(vdisk, devdata, scsidev) {
diff --git a/drivers/staging/wilc1000/host_interface.c b/drivers/staging/wilc1000/host_interface.c
index 6ab7443..6326375 100644
--- a/drivers/staging/wilc1000/host_interface.c
+++ b/drivers/staging/wilc1000/host_interface.c
@@ -1930,6 +1930,8 @@
wid.type = WID_STR;
wid.size = ETH_ALEN;
wid.val = kmalloc(wid.size, GFP_KERNEL);
+ if (!wid.val)
+ return -ENOMEM;
stamac = wid.val;
memcpy(stamac, strHostIfStaInactiveT->mac, ETH_ALEN);
diff --git a/drivers/staging/wilc1000/linux_mon.c b/drivers/staging/wilc1000/linux_mon.c
index 242f82f..d8ab4cb 100644
--- a/drivers/staging/wilc1000/linux_mon.c
+++ b/drivers/staging/wilc1000/linux_mon.c
@@ -197,6 +197,8 @@
if (skb->data[0] == 0xc0 && (!(memcmp(broadcast, &skb->data[4], 6)))) {
skb2 = dev_alloc_skb(skb->len + sizeof(struct wilc_wfi_radiotap_cb_hdr));
+ if (!skb2)
+ return -ENOMEM;
memcpy(skb_put(skb2, skb->len), skb->data, skb->len);
diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c
index 97928b4..57a3d393 100644
--- a/drivers/target/target_core_file.c
+++ b/drivers/target/target_core_file.c
@@ -276,12 +276,11 @@
else
ret = vfs_iter_read(fd, &iter, &pos);
- kfree(bvec);
-
if (is_write) {
if (ret < 0 || ret != data_length) {
pr_err("%s() write returned %d\n", __func__, ret);
- return (ret < 0 ? ret : -EINVAL);
+ if (ret >= 0)
+ ret = -EINVAL;
}
} else {
/*
@@ -294,17 +293,29 @@
pr_err("%s() returned %d, expecting %u for "
"S_ISBLK\n", __func__, ret,
data_length);
- return (ret < 0 ? ret : -EINVAL);
+ if (ret >= 0)
+ ret = -EINVAL;
}
} else {
if (ret < 0) {
pr_err("%s() returned %d for non S_ISBLK\n",
__func__, ret);
- return ret;
+ } else if (ret != data_length) {
+ /*
+ * Short read case:
+ * Probably some one truncate file under us.
+ * We must explicitly zero sg-pages to prevent
+ * expose uninizialized pages to userspace.
+ */
+ if (ret < data_length)
+ ret += iov_iter_zero(data_length - ret, &iter);
+ else
+ ret = -EINVAL;
}
}
}
- return 1;
+ kfree(bvec);
+ return ret;
}
static sense_reason_t
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index f8a9a2f..cc3af57 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -1565,7 +1565,7 @@
* based on the MACRO determine the default state to use or the
* offset from the max_state.
*/
- if (upper > (THERMAL_MAX_LIMIT - max_state)) {
+ if (upper >= (THERMAL_MAX_LIMIT - max_state)) {
/* upper default max_state */
if (upper == THERMAL_NO_LIMIT)
upper = max_state;
@@ -1573,7 +1573,7 @@
upper = max_state - (THERMAL_MAX_LIMIT - upper);
}
- if (lower > (THERMAL_MAX_LIMIT - max_state)) {
+ if (lower >= (THERMAL_MAX_LIMIT - max_state)) {
/* lower default 0 */
if (lower == THERMAL_NO_LIMIT)
lower = 0;
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 459d726..3eb01a71 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -464,7 +464,8 @@
/* If no clock rate is defined, fail. */
if (!p->uartclk) {
dev_err(dev, "clock rate not defined\n");
- return -EINVAL;
+ err = -EINVAL;
+ goto err_clk;
}
data->pclk = devm_clk_get(dev, "apb_pclk");
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index b80ea87..e82b347 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -5100,6 +5100,17 @@
PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* 135a.0dc0 */
pbn_b2_4_115200 },
/*
+ * BrainBoxes UC-260
+ */
+ { PCI_VENDOR_ID_INTASHIELD, 0x0D21,
+ PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00,
+ pbn_b2_4_115200 },
+ { PCI_VENDOR_ID_INTASHIELD, 0x0E34,
+ PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00,
+ pbn_b2_4_115200 },
+ /*
* Perle PCI-RAS cards
*/
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030,
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index e2c33b9..b42d7f1 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -1302,14 +1302,15 @@
pl011_dma_tx_stop(uap);
}
-static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq);
+static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq);
/* Start TX with programmed I/O only (no DMA) */
static void pl011_start_tx_pio(struct uart_amba_port *uap)
{
- uap->im |= UART011_TXIM;
- pl011_write(uap->im, uap, REG_IMSC);
- pl011_tx_chars(uap, false);
+ if (pl011_tx_chars(uap, false)) {
+ uap->im |= UART011_TXIM;
+ pl011_write(uap->im, uap, REG_IMSC);
+ }
}
static void pl011_start_tx(struct uart_port *port)
@@ -1389,25 +1390,26 @@
return true;
}
-static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
+/* Returns true if tx interrupts have to be (kept) enabled */
+static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
{
struct circ_buf *xmit = &uap->port.state->xmit;
int count = uap->fifosize >> 1;
if (uap->port.x_char) {
if (!pl011_tx_char(uap, uap->port.x_char, from_irq))
- return;
+ return true;
uap->port.x_char = 0;
--count;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) {
pl011_stop_tx(&uap->port);
- return;
+ return false;
}
/* If we are using DMA mode, try to send some characters. */
if (pl011_dma_tx_irq(uap))
- return;
+ return true;
do {
if (likely(from_irq) && count-- == 0)
@@ -1422,8 +1424,11 @@
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&uap->port);
- if (uart_circ_empty(xmit))
+ if (uart_circ_empty(xmit)) {
pl011_stop_tx(&uap->port);
+ return false;
+ }
+ return true;
}
static void pl011_modem_status(struct uart_amba_port *uap)
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 4d079cd..addb287 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -1780,6 +1780,7 @@
switch (version) {
case 0x302:
case 0x10213:
+ case 0x10302:
dev_dbg(port->dev, "This version is usart\n");
atmel_port->has_frac_baudrate = true;
atmel_port->has_hw_timer = true;
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index c365154..3b31fd8 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -253,11 +253,12 @@
}
port->mapbase = addr;
port->uartclk = BASE_BAUD * 16;
- port->membase = earlycon_map(port->mapbase, SZ_4K);
val = of_get_flat_dt_prop(node, "reg-offset", NULL);
if (val)
port->mapbase += be32_to_cpu(*val);
+ port->membase = earlycon_map(port->mapbase, SZ_4K);
+
val = of_get_flat_dt_prop(node, "reg-shift", NULL);
if (val)
port->regshift = be32_to_cpu(*val);
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 521a6e4..f575a33 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -1316,19 +1316,10 @@
if (!is_imx1_uart(sport)) {
temp = readl(sport->port.membase + UCR3);
- /*
- * The effect of RI and DCD differs depending on the UFCR_DCEDTE
- * bit. In DCE mode they control the outputs, in DTE mode they
- * enable the respective irqs. At least the DCD irq cannot be
- * cleared on i.MX25 at least, so it's not usable and must be
- * disabled. I don't have test hardware to check if RI has the
- * same problem but I consider this likely so it's disabled for
- * now, too.
- */
- temp |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP |
- UCR3_DTRDEN | UCR3_RI | UCR3_DCD;
+ temp |= UCR3_DTRDEN | UCR3_RI | UCR3_DCD;
if (sport->dte_mode)
+ /* disable broken interrupts */
temp &= ~(UCR3_RI | UCR3_DCD);
writel(temp, sport->port.membase + UCR3);
@@ -1583,8 +1574,6 @@
ufcr = readl(sport->port.membase + UFCR);
ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
- if (sport->dte_mode)
- ufcr |= UFCR_DCEDTE;
writel(ufcr, sport->port.membase + UFCR);
writel(num, sport->port.membase + UBIR);
@@ -2149,6 +2138,27 @@
UCR1_TXMPTYEN | UCR1_RTSDEN);
writel_relaxed(reg, sport->port.membase + UCR1);
+ if (!is_imx1_uart(sport) && sport->dte_mode) {
+ /*
+ * The DCEDTE bit changes the direction of DSR, DCD, DTR and RI
+ * and influences if UCR3_RI and UCR3_DCD changes the level of RI
+ * and DCD (when they are outputs) or enables the respective
+ * irqs. So set this bit early, i.e. before requesting irqs.
+ */
+ writel(UFCR_DCEDTE, sport->port.membase + UFCR);
+
+ /*
+ * Disable UCR3_RI and UCR3_DCD irqs. They are also not
+ * enabled later because they cannot be cleared
+ * (confirmed on i.MX25) which makes them unusable.
+ */
+ writel(IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP | UCR3_DSR,
+ sport->port.membase + UCR3);
+
+ } else {
+ writel(0, sport->port.membase + UFCR);
+ }
+
clk_disable_unprepare(sport->clk_ipg);
/*
diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c
index a260cde..5532c44 100644
--- a/drivers/tty/serial/kgdboc.c
+++ b/drivers/tty/serial/kgdboc.c
@@ -245,7 +245,8 @@
kgdb_tty_line, chr);
}
-static int param_set_kgdboc_var(const char *kmessage, struct kernel_param *kp)
+static int param_set_kgdboc_var(const char *kmessage,
+ const struct kernel_param *kp)
{
int len = strlen(kmessage);
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 792fb3b..a2522c6 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -3,7 +3,7 @@
* MSM 7k High speed uart driver
*
* Copyright (c) 2008 Google Inc.
- * Copyright (c) 2007-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2007-2018, The Linux Foundation. All rights reserved.
* Modified: Nick Pelly <npelly@google.com>
*
* All source code in this file is licensed under the following license
@@ -2617,6 +2617,33 @@
/* turn on uart clk */
msm_hs_resource_vote(msm_uport);
+ /* Set up Uart Receive */
+ msm_hs_write(uport, UART_DM_RFWR, 32);
+ /* Write to BADR explicitly to set up FIFO sizes */
+ msm_hs_write(uport, UARTDM_BADR_ADDR, 64);
+
+ /* configure the CR Protection to Enable */
+ msm_hs_write(uport, UART_DM_CR, CR_PROTECTION_EN);
+
+ /*
+ * Enable Command register protection before going ahead as this hw
+ * configuration makes sure that issued cmd to CR register gets complete
+ * before next issued cmd start. Hence mb() requires here.
+ */
+ mb();
+
+ /*
+ * Set RX_BREAK_ZERO_CHAR_OFF and RX_ERROR_CHAR_OFF
+ * so any rx_break and character having parity of framing
+ * error don't enter inside UART RX FIFO.
+ */
+ data = msm_hs_read(uport, UART_DM_MR2);
+ data |= (UARTDM_MR2_RX_BREAK_ZERO_CHAR_OFF |
+ UARTDM_MR2_RX_ERROR_CHAR_OFF);
+ msm_hs_write(uport, UART_DM_MR2, data);
+ /* Ensure register IO completion */
+ mb();
+
if (is_use_low_power_wakeup(msm_uport)) {
ret = request_threaded_irq(msm_uport->wakeup.irq, NULL,
msm_hs_wakeup_isr,
@@ -2797,10 +2824,6 @@
goto exit_lh_init;
}
- /* Set up Uart Receive */
- msm_hs_write(uport, UART_DM_RFWR, 32);
- /* Write to BADR explicitly to set up FIFO sizes */
- msm_hs_write(uport, UARTDM_BADR_ADDR, 64);
INIT_DELAYED_WORK(&rx->flip_insert_work, flip_insert_work);
@@ -3331,7 +3354,6 @@
struct resource *bam_resource;
int core_irqres, bam_irqres, wakeup_irqres;
struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data;
- unsigned long data;
char name[30];
if (pdev->dev.of_node) {
@@ -3551,27 +3573,6 @@
if (unlikely(ret))
goto err_clock;
- /* configure the CR Protection to Enable */
- msm_hs_write(uport, UART_DM_CR, CR_PROTECTION_EN);
-
- /*
- * Enable Command register protection before going ahead as this hw
- * configuration makes sure that issued cmd to CR register gets complete
- * before next issued cmd start. Hence mb() requires here.
- */
- mb();
-
- /*
- * Set RX_BREAK_ZERO_CHAR_OFF and RX_ERROR_CHAR_OFF
- * so any rx_break and character having parity of framing
- * error don't enter inside UART RX FIFO.
- */
- data = msm_hs_read(uport, UART_DM_MR2);
- data |= (UARTDM_MR2_RX_BREAK_ZERO_CHAR_OFF |
- UARTDM_MR2_RX_ERROR_CHAR_OFF);
- msm_hs_write(uport, UART_DM_MR2, data);
- /* Ensure register IO completion */
- mb();
ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_clock.attr);
if (unlikely(ret)) {
@@ -3761,8 +3762,8 @@
.runtime_suspend = msm_hs_runtime_suspend,
.runtime_resume = msm_hs_runtime_resume,
.runtime_idle = NULL,
- .suspend_noirq = msm_hs_pm_sys_suspend_noirq,
- .resume_noirq = msm_hs_pm_sys_resume_noirq,
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(msm_hs_pm_sys_suspend_noirq,
+ msm_hs_pm_sys_resume_noirq)
};
static struct platform_driver msm_serial_hs_platform_driver = {
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index dce39de..839cee4 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -1138,6 +1138,8 @@
uport->ops->config_port(uport, flags);
ret = uart_startup(tty, state, 1);
+ if (ret == 0)
+ tty_port_set_initialized(port, true);
if (ret > 0)
ret = 0;
}
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 15eaea5..a55c94d 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -935,6 +935,8 @@
/* Tell the rest of the system the news. New characters! */
tty_flip_buffer_push(tport);
} else {
+ /* TTY buffers full; read from RX reg to prevent lockup */
+ serial_port_in(port, SCxRDR);
serial_port_in(port, SCxSR); /* dummy read */
sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port));
}
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 8d9f9a8..fb9bada 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -1702,6 +1702,8 @@
if (tty->link)
tty->link->port->itty = NULL;
tty_buffer_cancel_work(tty->port);
+ if (tty->link)
+ tty_buffer_cancel_work(tty->link->port);
tty_kref_put(tty->link);
tty_kref_put(tty);
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index ce2c3c6..68c7bb0 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -1727,7 +1727,7 @@
default_attr(vc);
update_attr(vc);
- vc->vc_tab_stop[0] = 0x01010100;
+ vc->vc_tab_stop[0] =
vc->vc_tab_stop[1] =
vc->vc_tab_stop[2] =
vc->vc_tab_stop[3] =
@@ -1771,7 +1771,7 @@
vc->vc_pos -= (vc->vc_x << 1);
while (vc->vc_x < vc->vc_cols - 1) {
vc->vc_x++;
- if (vc->vc_tab_stop[vc->vc_x >> 5] & (1 << (vc->vc_x & 31)))
+ if (vc->vc_tab_stop[7 & (vc->vc_x >> 5)] & (1 << (vc->vc_x & 31)))
break;
}
vc->vc_pos += (vc->vc_x << 1);
@@ -1831,7 +1831,7 @@
lf(vc);
return;
case 'H':
- vc->vc_tab_stop[vc->vc_x >> 5] |= (1 << (vc->vc_x & 31));
+ vc->vc_tab_stop[7 & (vc->vc_x >> 5)] |= (1 << (vc->vc_x & 31));
return;
case 'Z':
respond_ID(tty);
@@ -2024,7 +2024,7 @@
return;
case 'g':
if (!vc->vc_par[0])
- vc->vc_tab_stop[vc->vc_x >> 5] &= ~(1 << (vc->vc_x & 31));
+ vc->vc_tab_stop[7 & (vc->vc_x >> 5)] &= ~(1 << (vc->vc_x & 31));
else if (vc->vc_par[0] == 3) {
vc->vc_tab_stop[0] =
vc->vc_tab_stop[1] =
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 4f8221e..9016a9b 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -148,6 +148,10 @@
ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
+ /* Linger a bit, prior to the next control message. */
+ if (dev->quirks & USB_QUIRK_DELAY_CTRL_MSG)
+ msleep(200);
+
kfree(dr);
return ret;
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index c05c4f8..4f1c6f8 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -225,8 +225,12 @@
{ USB_DEVICE(0x1a0a, 0x0200), .driver_info =
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
+ /* Corsair K70 RGB */
+ { USB_DEVICE(0x1b1c, 0x1b13), .driver_info = USB_QUIRK_DELAY_INIT },
+
/* Corsair Strafe RGB */
- { USB_DEVICE(0x1b1c, 0x1b20), .driver_info = USB_QUIRK_DELAY_INIT },
+ { USB_DEVICE(0x1b1c, 0x1b20), .driver_info = USB_QUIRK_DELAY_INIT |
+ USB_QUIRK_DELAY_CTRL_MSG },
/* Corsair K70 LUX */
{ USB_DEVICE(0x1b1c, 0x1b36), .driver_info = USB_QUIRK_DELAY_INIT },
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index df5a065..dfc0566 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -3220,6 +3220,7 @@
dwc2_core_init(hsotg, false);
dwc2_enable_global_interrupts(hsotg);
spin_lock_irqsave(&hsotg->lock, flags);
+ dwc2_hsotg_disconnect(hsotg);
dwc2_hsotg_core_init_disconnected(hsotg, false);
spin_unlock_irqrestore(&hsotg->lock, flags);
dwc2_hsotg_core_connect(hsotg);
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 2095579..ef3f542 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -252,7 +252,7 @@
/* core exits U1/U2/U3 only in PHY power state P1/P2/P3 respectively */
if (dwc->revision <= DWC3_REVISION_310A)
- reg |= DWC3_GUSB3PIPECTL_UX_EXIT_IN_PX;
+ reg |= DWC3_GUSB3PIPECTL_UX_EXIT_PX;
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
@@ -571,6 +571,12 @@
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
/*
+ * Make sure UX_EXIT_PX is cleared as that causes issues with some
+ * PHYs. Also, this bit is not supposed to be used in normal operation.
+ */
+ reg &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX;
+
+ /*
* Above 1.94a, it is recommended to set DWC3_GUSB3PIPECTL_SUSPHY
* to '0' during coreConsultant configuration. So default value
* will be '0' when the core is reset. Application needs to set it
@@ -913,6 +919,17 @@
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
+ /*
+ * Enable evicting endpoint cache after flow control for bulk
+ * endpoints for dwc3 core version 3.00a and 3.20a
+ */
+ if (dwc->revision == DWC3_REVISION_300A ||
+ dwc->revision == DWC3_REVISION_320A) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
+ reg |= DWC3_GUCTL2_ENABLE_EP_CACHE_EVICT;
+ dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
+ }
+
return 0;
err2:
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 4407a83..e5fe7a4 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -171,13 +171,15 @@
#define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0)
#define DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(n) (((n) >> 16) & 0xffff)
-#define DWC3_TXFIFOQ 1
-#define DWC3_RXFIFOQ 3
-#define DWC3_TXREQQ 5
-#define DWC3_RXREQQ 7
-#define DWC3_RXINFOQ 9
-#define DWC3_DESCFETCHQ 13
-#define DWC3_EVENTQ 15
+#define DWC3_TXFIFOQ 0
+#define DWC3_RXFIFOQ 1
+#define DWC3_TXREQQ 2
+#define DWC3_RXREQQ 3
+#define DWC3_RXINFOQ 4
+#define DWC3_PSTATQ 5
+#define DWC3_DESCFETCHQ 6
+#define DWC3_EVENTQ 7
+#define DWC3_AUXEVENTQ 8
/* Global RX Threshold Configuration Register */
#define DWC3_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 19)
@@ -220,6 +222,9 @@
/* Global User Control 1 Register */
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW (1 << 24)
+/* Global User Control 2 Register */
+#define DWC3_GUCTL2_ENABLE_EP_CACHE_EVICT (1 << 12)
+
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
#define DWC3_GUSB2PHYCFG_ENBLSLPM (1 << 8)
@@ -249,7 +254,7 @@
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
#define DWC3_GUSB3PIPECTL_U2SSINP3OK (1 << 29)
#define DWC3_GUSB3PIPECTL_DISRXDETINP3 (1 << 28)
-#define DWC3_GUSB3PIPECTL_UX_EXIT_IN_PX (1 << 27)
+#define DWC3_GUSB3PIPECTL_UX_EXIT_PX (1 << 27)
#define DWC3_GUSB3PIPECTL_REQP1P2P3 (1 << 24)
#define DWC3_GUSB3PIPECTL_DEP1P2P3(n) ((n) << 19)
#define DWC3_GUSB3PIPECTL_DEP1P2P3_MASK DWC3_GUSB3PIPECTL_DEP1P2P3(7)
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 989956d..810546a 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -3593,6 +3593,8 @@
mutex_init(&mdwc->suspend_resume_mutex);
/* Mark type-C as true by default */
mdwc->type_c = true;
+ if (of_property_read_bool(node, "qcom,connector-type-uAB"))
+ mdwc->type_c = false;
mdwc->usb_psy = power_supply_get_by_name("usb");
if (!mdwc->usb_psy) {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 243c078..73caa97 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -3263,6 +3263,8 @@
break;
}
+ dwc->eps[1]->endpoint.maxpacket = dwc->gadget.ep0->maxpacket;
+
/* Enable USB2 LPM Capability */
if ((dwc->revision > DWC3_REVISION_194A) &&
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index a9344a5..b8fa93e 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -64,7 +64,7 @@
obj-$(CONFIG_USB_F_GSI) += usb_f_gsi.o
usb_f_qdss-y := f_qdss.o u_qdss.o
obj-$(CONFIG_USB_F_QDSS) += usb_f_qdss.o
-usb_f_qcrndis-y := f_qc_rndis.o u_data_ipa.o
+usb_f_qcrndis-y := f_qc_rndis.o
obj-$(CONFIG_USB_F_QCRNDIS) += usb_f_qcrndis.o
-usb_f_rmnet_bam-y := f_rmnet.o u_ctrl_qti.o u_bam_dmux.o
+usb_f_rmnet_bam-y := f_rmnet.o u_ctrl_qti.o u_bam_dmux.o u_data_ipa.o
obj-$(CONFIG_USB_F_RMNET_BAM) += usb_f_rmnet_bam.o
diff --git a/drivers/usb/gadget/function/f_diag.c b/drivers/usb/gadget/function/f_diag.c
index f08e443..b1bccd0 100644
--- a/drivers/usb/gadget/function/f_diag.c
+++ b/drivers/usb/gadget/function/f_diag.c
@@ -2,7 +2,7 @@
* Diag Function Device - Route ARM9 and ARM11 DIAG messages
* between HOST and DEVICE.
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2008-2018, The Linux Foundation. All rights reserved.
* Author: Brian Swetland <swetland@google.com>
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -492,6 +492,7 @@
}
EXPORT_SYMBOL(usb_diag_alloc_req);
#define DWC3_MAX_REQUEST_SIZE (16 * 1024 * 1024)
+#define CI_MAX_REQUEST_SIZE (16 * 1024)
/**
* usb_diag_request_size - Max request size for controller
* @ch: Channel handler
@@ -501,6 +502,12 @@
*/
int usb_diag_request_size(struct usb_diag_ch *ch)
{
+ struct diag_context *ctxt = ch->priv_usb;
+ struct usb_composite_dev *cdev = ctxt->cdev;
+
+ if (cdev->gadget->is_chipidea)
+ return CI_MAX_REQUEST_SIZE;
+
return DWC3_MAX_REQUEST_SIZE;
}
EXPORT_SYMBOL(usb_diag_request_size);
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 7897f68..8eed1b3 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -3415,8 +3415,8 @@
struct ffs_data *ffs = func->ffs;
const int full = !!func->ffs->fs_descs_count;
- const int high = func->ffs->hs_descs_count;
- const int super = func->ffs->ss_descs_count;
+ const int high = !!func->ffs->hs_descs_count;
+ const int super = !!func->ffs->ss_descs_count;
int fs_len, hs_len, ss_len, ret, i;
struct ffs_ep *eps_ptr;
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index b6d4b48..5815120 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -284,6 +284,7 @@
size_t count, loff_t *offp)
{
struct f_hidg *hidg = file->private_data;
+ struct usb_request *req;
unsigned long flags;
ssize_t status = -ENOMEM;
@@ -293,7 +294,7 @@
spin_lock_irqsave(&hidg->write_spinlock, flags);
#define WRITE_COND (!hidg->write_pending)
-
+try_again:
/* write queue */
while (!WRITE_COND) {
spin_unlock_irqrestore(&hidg->write_spinlock, flags);
@@ -308,6 +309,7 @@
}
hidg->write_pending = 1;
+ req = hidg->req;
count = min_t(unsigned, count, hidg->report_length);
spin_unlock_irqrestore(&hidg->write_spinlock, flags);
@@ -320,24 +322,38 @@
goto release_write_pending;
}
- hidg->req->status = 0;
- hidg->req->zero = 0;
- hidg->req->length = count;
- hidg->req->complete = f_hidg_req_complete;
- hidg->req->context = hidg;
+ spin_lock_irqsave(&hidg->write_spinlock, flags);
+
+ /* we our function has been disabled by host */
+ if (!hidg->req) {
+ free_ep_req(hidg->in_ep, hidg->req);
+ /*
+ * TODO
+ * Should we fail with error here?
+ */
+ goto try_again;
+ }
+
+ req->status = 0;
+ req->zero = 0;
+ req->length = count;
+ req->complete = f_hidg_req_complete;
+ req->context = hidg;
status = usb_ep_queue(hidg->in_ep, hidg->req, GFP_ATOMIC);
if (status < 0) {
ERROR(hidg->func.config->cdev,
"usb_ep_queue error on int endpoint %zd\n", status);
- goto release_write_pending;
+ goto release_write_pending_unlocked;
} else {
status = count;
}
+ spin_unlock_irqrestore(&hidg->write_spinlock, flags);
return status;
release_write_pending:
spin_lock_irqsave(&hidg->write_spinlock, flags);
+release_write_pending_unlocked:
hidg->write_pending = 0;
spin_unlock_irqrestore(&hidg->write_spinlock, flags);
@@ -541,12 +557,23 @@
kfree(list);
}
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
+
+ spin_lock_irqsave(&hidg->write_spinlock, flags);
+ if (!hidg->write_pending) {
+ free_ep_req(hidg->in_ep, hidg->req);
+ hidg->write_pending = 1;
+ }
+
+ hidg->req = NULL;
+ spin_unlock_irqrestore(&hidg->write_spinlock, flags);
}
static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct usb_composite_dev *cdev = f->config->cdev;
struct f_hidg *hidg = func_to_hidg(f);
+ struct usb_request *req_in = NULL;
+ unsigned long flags;
int i, status = 0;
VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
@@ -567,6 +594,12 @@
goto fail;
}
hidg->in_ep->driver_data = hidg;
+
+ req_in = hidg_alloc_ep_req(hidg->in_ep, hidg->report_length);
+ if (!req_in) {
+ status = -ENOMEM;
+ goto disable_ep_in;
+ }
}
@@ -578,12 +611,12 @@
hidg->out_ep);
if (status) {
ERROR(cdev, "config_ep_by_speed FAILED!\n");
- goto fail;
+ goto free_req_in;
}
status = usb_ep_enable(hidg->out_ep);
if (status < 0) {
ERROR(cdev, "Enable OUT endpoint FAILED!\n");
- goto fail;
+ goto free_req_in;
}
hidg->out_ep->driver_data = hidg;
@@ -599,17 +632,37 @@
req->context = hidg;
status = usb_ep_queue(hidg->out_ep, req,
GFP_ATOMIC);
- if (status)
+ if (status) {
ERROR(cdev, "%s queue req --> %d\n",
hidg->out_ep->name, status);
+ free_ep_req(hidg->out_ep, req);
+ }
} else {
- usb_ep_disable(hidg->out_ep);
status = -ENOMEM;
- goto fail;
+ goto disable_out_ep;
}
}
}
+ if (hidg->in_ep != NULL) {
+ spin_lock_irqsave(&hidg->write_spinlock, flags);
+ hidg->req = req_in;
+ hidg->write_pending = 0;
+ spin_unlock_irqrestore(&hidg->write_spinlock, flags);
+
+ wake_up(&hidg->write_queue);
+ }
+ return 0;
+disable_out_ep:
+ usb_ep_disable(hidg->out_ep);
+free_req_in:
+ if (req_in)
+ free_ep_req(hidg->in_ep, req_in);
+
+disable_ep_in:
+ if (hidg->in_ep)
+ usb_ep_disable(hidg->in_ep);
+
fail:
return status;
}
@@ -658,12 +711,6 @@
goto fail;
hidg->out_ep = ep;
- /* preallocate request and buffer */
- status = -ENOMEM;
- hidg->req = alloc_ep_req(hidg->in_ep, hidg->report_length);
- if (!hidg->req)
- goto fail;
-
/* set descriptor dynamic values */
hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
@@ -690,6 +737,8 @@
goto fail;
spin_lock_init(&hidg->write_spinlock);
+ hidg->write_pending = 1;
+ hidg->req = NULL;
spin_lock_init(&hidg->read_spinlock);
init_waitqueue_head(&hidg->write_queue);
init_waitqueue_head(&hidg->read_queue);
@@ -954,10 +1003,6 @@
device_destroy(hidg_class, MKDEV(major, hidg->minor));
cdev_del(&hidg->cdev);
- /* disable/free request and end point */
- usb_ep_disable(hidg->in_ep);
- free_ep_req(hidg->in_ep, hidg->req);
-
usb_free_all_descriptors(f);
}
diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c
index ccb9c21..e9bd8d4 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_core.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_core.c
@@ -475,7 +475,7 @@
bdc->dev = dev;
dev_dbg(bdc->dev, "bdc->regs: %p irq=%d\n", bdc->regs, bdc->irq);
- temp = bdc_readl(bdc->regs, BDC_BDCSC);
+ temp = bdc_readl(bdc->regs, BDC_BDCCAP1);
if ((temp & BDC_P64) &&
!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) {
dev_dbg(bdc->dev, "Using 64-bit address\n");
diff --git a/drivers/usb/gadget/udc/bdc/bdc_pci.c b/drivers/usb/gadget/udc/bdc/bdc_pci.c
index 0296884..708e36f 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_pci.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_pci.c
@@ -82,6 +82,7 @@
if (ret) {
dev_err(&pci->dev,
"couldn't add resources to bdc device\n");
+ platform_device_put(bdc);
return ret;
}
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index b62a3de..ff4d6ca 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -2103,16 +2103,13 @@
}
break;
case USB_PORT_FEAT_POWER:
- if (hcd->speed == HCD_USB3) {
- if (dum_hcd->port_status & USB_PORT_STAT_POWER)
- dev_dbg(dummy_dev(dum_hcd),
- "power-off\n");
- } else
- if (dum_hcd->port_status &
- USB_SS_PORT_STAT_POWER)
- dev_dbg(dummy_dev(dum_hcd),
- "power-off\n");
- /* FALLS THROUGH */
+ dev_dbg(dummy_dev(dum_hcd), "power-off\n");
+ if (hcd->speed == HCD_USB3)
+ dum_hcd->port_status &= ~USB_SS_PORT_STAT_POWER;
+ else
+ dum_hcd->port_status &= ~USB_PORT_STAT_POWER;
+ set_link_state(dum_hcd);
+ break;
default:
dum_hcd->port_status &= ~(1 << wValue);
set_link_state(dum_hcd);
@@ -2283,14 +2280,13 @@
if ((dum_hcd->port_status &
USB_SS_PORT_STAT_POWER) != 0) {
dum_hcd->port_status |= (1 << wValue);
- set_link_state(dum_hcd);
}
} else
if ((dum_hcd->port_status &
USB_PORT_STAT_POWER) != 0) {
dum_hcd->port_status |= (1 << wValue);
- set_link_state(dum_hcd);
}
+ set_link_state(dum_hcd);
}
break;
case GetPortErrorCount:
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 5635a33..2c39571 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -73,6 +73,7 @@
#define STATECHANGE_DELAY msecs_to_jiffies(300)
#define IO_WATCHDOG_DELAY msecs_to_jiffies(275)
+#define IO_WATCHDOG_OFF 0xffffff00
#include "ohci.h"
#include "pci-quirks.h"
@@ -230,7 +231,7 @@
}
/* Start up the I/O watchdog timer, if it's not running */
- if (!timer_pending(&ohci->io_watchdog) &&
+ if (ohci->prev_frame_no == IO_WATCHDOG_OFF &&
list_empty(&ohci->eds_in_use) &&
!(ohci->flags & OHCI_QUIRK_QEMU)) {
ohci->prev_frame_no = ohci_frame_no(ohci);
@@ -501,6 +502,7 @@
setup_timer(&ohci->io_watchdog, io_watchdog_func,
(unsigned long) ohci);
+ ohci->prev_frame_no = IO_WATCHDOG_OFF;
ohci->hcca = dma_alloc_coherent (hcd->self.controller,
sizeof(*ohci->hcca), &ohci->hcca_dma, GFP_KERNEL);
@@ -730,7 +732,7 @@
u32 head;
struct ed *ed;
struct td *td, *td_start, *td_next;
- unsigned frame_no;
+ unsigned frame_no, prev_frame_no = IO_WATCHDOG_OFF;
unsigned long flags;
spin_lock_irqsave(&ohci->lock, flags);
@@ -835,7 +837,7 @@
}
}
if (!list_empty(&ohci->eds_in_use)) {
- ohci->prev_frame_no = frame_no;
+ prev_frame_no = frame_no;
ohci->prev_wdh_cnt = ohci->wdh_cnt;
ohci->prev_donehead = ohci_readl(ohci,
&ohci->regs->donehead);
@@ -845,6 +847,7 @@
}
done:
+ ohci->prev_frame_no = prev_frame_no;
spin_unlock_irqrestore(&ohci->lock, flags);
}
@@ -973,6 +976,7 @@
if (quirk_nec(ohci))
flush_work(&ohci->nec_work);
del_timer_sync(&ohci->io_watchdog);
+ ohci->prev_frame_no = IO_WATCHDOG_OFF;
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
ohci_usb_reset(ohci);
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index ed678c17..798b2d2 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -310,8 +310,10 @@
rc = ohci_rh_suspend (ohci, 0);
spin_unlock_irq (&ohci->lock);
- if (rc == 0)
+ if (rc == 0) {
del_timer_sync(&ohci->io_watchdog);
+ ohci->prev_frame_no = IO_WATCHDOG_OFF;
+ }
return rc;
}
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
index 4365dc3..48200a8 100644
--- a/drivers/usb/host/ohci-q.c
+++ b/drivers/usb/host/ohci-q.c
@@ -1018,6 +1018,8 @@
* have modified this list. normally it's just prepending
* entries (which we'd ignore), but paranoia won't hurt.
*/
+ *last = ed->ed_next;
+ ed->ed_next = NULL;
modified = 0;
/* unlink urbs as requested, but rescan the list after
@@ -1076,21 +1078,22 @@
goto rescan_this;
/*
- * If no TDs are queued, take ED off the ed_rm_list.
+ * If no TDs are queued, ED is now idle.
* Otherwise, if the HC is running, reschedule.
- * If not, leave it on the list for further dequeues.
+ * If the HC isn't running, add ED back to the
+ * start of the list for later processing.
*/
if (list_empty(&ed->td_list)) {
- *last = ed->ed_next;
- ed->ed_next = NULL;
ed->state = ED_IDLE;
list_del(&ed->in_use_list);
} else if (ohci->rh_state == OHCI_RH_RUNNING) {
- *last = ed->ed_next;
- ed->ed_next = NULL;
ed_schedule(ohci, ed);
} else {
- last = &ed->ed_next;
+ ed->ed_next = ohci->ed_rm_list;
+ ohci->ed_rm_list = ed;
+ /* Don't loop on the same ED */
+ if (last == &ohci->ed_rm_list)
+ last = &ed->ed_next;
}
if (modified)
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index 9ca5956..c5e3032 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -46,6 +46,9 @@
#define USB_DEVICE_ID_LD_MICROCASSYTIME 0x1033 /* USB Product ID of Micro-CASSY Time (reserved) */
#define USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE 0x1035 /* USB Product ID of Micro-CASSY Temperature */
#define USB_DEVICE_ID_LD_MICROCASSYPH 0x1038 /* USB Product ID of Micro-CASSY pH */
+#define USB_DEVICE_ID_LD_POWERANALYSERCASSY 0x1040 /* USB Product ID of Power Analyser CASSY */
+#define USB_DEVICE_ID_LD_CONVERTERCONTROLLERCASSY 0x1042 /* USB Product ID of Converter Controller CASSY */
+#define USB_DEVICE_ID_LD_MACHINETESTCASSY 0x1043 /* USB Product ID of Machine Test CASSY */
#define USB_DEVICE_ID_LD_JWM 0x1080 /* USB Product ID of Joule and Wattmeter */
#define USB_DEVICE_ID_LD_DMMP 0x1081 /* USB Product ID of Digital Multimeter P (reserved) */
#define USB_DEVICE_ID_LD_UMIP 0x1090 /* USB Product ID of UMI P */
@@ -88,6 +91,9 @@
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTIME) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYPH) },
+ { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERANALYSERCASSY) },
+ { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CONVERTERCONTROLLERCASSY) },
+ { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETESTCASSY) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) },
diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c
index 934e10f..baa57bc 100644
--- a/drivers/usb/misc/lvstest.c
+++ b/drivers/usb/misc/lvstest.c
@@ -481,6 +481,7 @@
struct lvs_rh *lvs = usb_get_intfdata(intf);
sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group);
+ usb_poison_urb(lvs->urb); /* used in scheduled work */
flush_work(&lvs->rh_work);
usb_free_urb(lvs->urb);
}
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index 57e038d..794c553 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -83,6 +83,8 @@
wait_queue_head_t wait;
int printf_size;
+ size_t printf_offset;
+ size_t printf_togo;
char *printf_buf;
struct mutex printf_lock;
@@ -374,75 +376,103 @@
return rc;
}
-/*
- * For simplicity, we read one record in one system call and throw out
- * what does not fit. This means that the following does not work:
- * dd if=/dbg/usbmon/0t bs=10
- * Also, we do not allow seeks and do not bother advancing the offset.
- */
-static ssize_t mon_text_read_t(struct file *file, char __user *buf,
- size_t nbytes, loff_t *ppos)
+static ssize_t mon_text_copy_to_user(struct mon_reader_text *rp,
+ char __user * const buf, const size_t nbytes)
{
- struct mon_reader_text *rp = file->private_data;
- struct mon_event_text *ep;
- struct mon_text_ptr ptr;
+ const size_t togo = min(nbytes, rp->printf_togo);
- ep = mon_text_read_wait(rp, file);
- if (IS_ERR(ep))
- return PTR_ERR(ep);
- mutex_lock(&rp->printf_lock);
- ptr.cnt = 0;
- ptr.pbuf = rp->printf_buf;
- ptr.limit = rp->printf_size;
-
- mon_text_read_head_t(rp, &ptr, ep);
- mon_text_read_statset(rp, &ptr, ep);
- ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
- " %d", ep->length);
- mon_text_read_data(rp, &ptr, ep);
-
- if (copy_to_user(buf, rp->printf_buf, ptr.cnt))
- ptr.cnt = -EFAULT;
- mutex_unlock(&rp->printf_lock);
- kmem_cache_free(rp->e_slab, ep);
- return ptr.cnt;
+ if (copy_to_user(buf, &rp->printf_buf[rp->printf_offset], togo))
+ return -EFAULT;
+ rp->printf_togo -= togo;
+ rp->printf_offset += togo;
+ return togo;
}
-static ssize_t mon_text_read_u(struct file *file, char __user *buf,
- size_t nbytes, loff_t *ppos)
+/* ppos is not advanced since the llseek operation is not permitted. */
+static ssize_t mon_text_read_t(struct file *file, char __user *buf,
+ size_t nbytes, loff_t *ppos)
{
struct mon_reader_text *rp = file->private_data;
struct mon_event_text *ep;
struct mon_text_ptr ptr;
+ ssize_t ret;
- ep = mon_text_read_wait(rp, file);
- if (IS_ERR(ep))
- return PTR_ERR(ep);
mutex_lock(&rp->printf_lock);
- ptr.cnt = 0;
- ptr.pbuf = rp->printf_buf;
- ptr.limit = rp->printf_size;
- mon_text_read_head_u(rp, &ptr, ep);
- if (ep->type == 'E') {
+ if (rp->printf_togo == 0) {
+
+ ep = mon_text_read_wait(rp, file);
+ if (IS_ERR(ep)) {
+ mutex_unlock(&rp->printf_lock);
+ return PTR_ERR(ep);
+ }
+ ptr.cnt = 0;
+ ptr.pbuf = rp->printf_buf;
+ ptr.limit = rp->printf_size;
+
+ mon_text_read_head_t(rp, &ptr, ep);
mon_text_read_statset(rp, &ptr, ep);
- } else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) {
- mon_text_read_isostat(rp, &ptr, ep);
- mon_text_read_isodesc(rp, &ptr, ep);
- } else if (ep->xfertype == USB_ENDPOINT_XFER_INT) {
- mon_text_read_intstat(rp, &ptr, ep);
- } else {
- mon_text_read_statset(rp, &ptr, ep);
+ ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
+ " %d", ep->length);
+ mon_text_read_data(rp, &ptr, ep);
+
+ rp->printf_togo = ptr.cnt;
+ rp->printf_offset = 0;
+
+ kmem_cache_free(rp->e_slab, ep);
}
- ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
- " %d", ep->length);
- mon_text_read_data(rp, &ptr, ep);
- if (copy_to_user(buf, rp->printf_buf, ptr.cnt))
- ptr.cnt = -EFAULT;
+ ret = mon_text_copy_to_user(rp, buf, nbytes);
mutex_unlock(&rp->printf_lock);
- kmem_cache_free(rp->e_slab, ep);
- return ptr.cnt;
+ return ret;
+}
+
+/* ppos is not advanced since the llseek operation is not permitted. */
+static ssize_t mon_text_read_u(struct file *file, char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct mon_reader_text *rp = file->private_data;
+ struct mon_event_text *ep;
+ struct mon_text_ptr ptr;
+ ssize_t ret;
+
+ mutex_lock(&rp->printf_lock);
+
+ if (rp->printf_togo == 0) {
+
+ ep = mon_text_read_wait(rp, file);
+ if (IS_ERR(ep)) {
+ mutex_unlock(&rp->printf_lock);
+ return PTR_ERR(ep);
+ }
+ ptr.cnt = 0;
+ ptr.pbuf = rp->printf_buf;
+ ptr.limit = rp->printf_size;
+
+ mon_text_read_head_u(rp, &ptr, ep);
+ if (ep->type == 'E') {
+ mon_text_read_statset(rp, &ptr, ep);
+ } else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) {
+ mon_text_read_isostat(rp, &ptr, ep);
+ mon_text_read_isodesc(rp, &ptr, ep);
+ } else if (ep->xfertype == USB_ENDPOINT_XFER_INT) {
+ mon_text_read_intstat(rp, &ptr, ep);
+ } else {
+ mon_text_read_statset(rp, &ptr, ep);
+ }
+ ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
+ " %d", ep->length);
+ mon_text_read_data(rp, &ptr, ep);
+
+ rp->printf_togo = ptr.cnt;
+ rp->printf_offset = 0;
+
+ kmem_cache_free(rp->e_slab, ep);
+ }
+
+ ret = mon_text_copy_to_user(rp, buf, nbytes);
+ mutex_unlock(&rp->printf_lock);
+ return ret;
}
static struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp,
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 55c624f..4303389 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -418,13 +418,7 @@
}
}
- /*
- * The pipe must be broken if current urb->status is set, so don't
- * start next urb.
- * TODO: to minimize the risk of regression, only check urb->status
- * for RX, until we have a test case to understand the behavior of TX.
- */
- if ((!status || !is_in) && qh && qh->is_ready) {
+ if (qh != NULL && qh->is_ready) {
musb_dbg(musb, "... next ep%d %cX urb %p",
hw_ep->epnum, is_in ? 'R' : 'T', next_urb(qh));
musb_start_urb(musb, is_in, qh);
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index 7706a48..39086bf 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -215,6 +215,8 @@
bool vbus_low_as_hostmode;
};
+#define SDP_CHECK_DELAY_MS 10000 /* in ms */
+
#define MSM_USB_BASE (motg->regs)
#define MSM_USB_PHY_CSR_BASE (motg->phy_csr_regs)
@@ -1808,23 +1810,41 @@
}
#endif
-static void msm_otg_notify_chg_current(struct msm_otg *motg, unsigned int mA)
+static int get_psy_type(struct msm_otg *motg)
{
- struct usb_gadget *g = motg->phy.otg->gadget;
union power_supply_propval pval = {0};
- if (g && g->is_a_peripheral)
- return;
-
- dev_dbg(motg->phy.dev, "Requested curr from USB = %u\n", mA);
if (!psy) {
- dev_dbg(motg->phy.dev, "no usb power supply registered\n");
- return;
+ dev_err(motg->phy.dev, "no usb power supply registered\n");
+ return -ENODEV;
}
power_supply_get_property(psy, POWER_SUPPLY_PROP_REAL_TYPE, &pval);
- if (motg->cur_power == mA || pval.intval != POWER_SUPPLY_TYPE_USB)
+ return pval.intval;
+}
+
+static void msm_otg_notify_chg_current(struct msm_otg *motg, unsigned int mA)
+{
+ struct usb_gadget *g = motg->phy.otg->gadget;
+ union power_supply_propval pval = {0};
+ int psy_type;
+
+ if (g && g->is_a_peripheral)
+ return;
+
+ psy_type = get_psy_type(motg);
+ if (psy_type == POWER_SUPPLY_TYPE_USB_FLOAT) {
+ if (!mA)
+ pval.intval = -ETIMEDOUT;
+ else
+ pval.intval = 1000 * mA;
+ goto set_prop;
+ }
+
+ dev_dbg(motg->phy.dev, "Requested curr from USB = %u\n", mA);
+
+ if (motg->cur_power == mA || psy_type != POWER_SUPPLY_TYPE_USB)
return;
dev_info(motg->phy.dev, "Avail curr from USB = %u\n", mA);
@@ -1833,6 +1853,7 @@
/* Set max current limit in uA */
pval.intval = 1000 * mA;
+set_prop:
if (power_supply_set_property(psy, POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
&pval)) {
dev_dbg(motg->phy.dev, "power supply error when setting property\n");
@@ -2335,6 +2356,21 @@
USB_ID_GROUND;
}
+static void check_for_sdp_connection(struct work_struct *w)
+{
+ struct msm_otg *motg = container_of(w, struct msm_otg, sdp_check.work);
+
+ /* Cable disconnected or device enumerated as SDP */
+ if (!motg->vbus_state || motg->phy.otg->gadget->state >=
+ USB_STATE_DEFAULT)
+ return;
+
+ /* floating D+/D- lines detected */
+ motg->vbus_state = 0;
+ msm_otg_dbg_log_event(&motg->phy, "Q RW SDP CHK", motg->vbus_state, 0);
+ msm_otg_set_vbus_state(motg->vbus_state);
+}
+
static void msm_otg_sm_work(struct work_struct *w)
{
struct msm_otg *motg = container_of(w, struct msm_otg, sm_work);
@@ -2408,6 +2444,11 @@
break;
}
+ if (get_psy_type(motg) == POWER_SUPPLY_TYPE_USB_FLOAT)
+ queue_delayed_work(motg->otg_wq,
+ &motg->sdp_check,
+ msecs_to_jiffies(SDP_CHECK_DELAY_MS));
+
pm_runtime_get_sync(otg->usb_phy->dev);
msm_otg_start_peripheral(otg, 1);
otg->state = OTG_STATE_B_PERIPHERAL;
@@ -2421,6 +2462,7 @@
break;
case OTG_STATE_B_PERIPHERAL:
if (!test_bit(B_SESS_VLD, &motg->inputs)) {
+ cancel_delayed_work_sync(&motg->sdp_check);
msm_otg_start_peripheral(otg, 0);
msm_otg_dbg_log_event(&motg->phy, "RT PM: B_PERI A PUT",
get_pm_runtime_counter(dev), 0);
@@ -2443,6 +2485,7 @@
break;
case OTG_STATE_B_SUSPEND:
if (!test_bit(B_SESS_VLD, &motg->inputs)) {
+ cancel_delayed_work_sync(&motg->sdp_check);
msm_otg_start_peripheral(otg, 0);
otg->state = OTG_STATE_B_IDLE;
/* Schedule work to finish cable disconnect processing*/
@@ -3824,6 +3867,7 @@
INIT_WORK(&motg->sm_work, msm_otg_sm_work);
INIT_DELAYED_WORK(&motg->id_status_work, msm_id_status_w);
INIT_DELAYED_WORK(&motg->perf_vote_work, msm_otg_perf_vote_work);
+ INIT_DELAYED_WORK(&motg->sdp_check, check_for_sdp_connection);
INIT_WORK(&motg->notify_chg_current_work,
msm_otg_notify_chg_current_work);
motg->otg_wq = alloc_ordered_workqueue("k_otg", 0);
@@ -4176,6 +4220,7 @@
if (psy)
power_supply_put(psy);
msm_otg_debugfs_cleanup();
+ cancel_delayed_work_sync(&motg->sdp_check);
cancel_delayed_work_sync(&motg->id_status_work);
cancel_delayed_work_sync(&motg->perf_vote_work);
msm_otg_perf_vote_update(motg, false);
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index 6c6a3a8..968ade5 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -1001,6 +1001,10 @@
if ((uintptr_t)pkt->buf & (USBHS_USB_DMAC_XFER_SIZE - 1))
goto usbhsf_pio_prepare_pop;
+ /* return at this time if the pipe is running */
+ if (usbhs_pipe_is_running(pipe))
+ return 0;
+
usbhs_pipe_config_change_bfre(pipe, 1);
ret = usbhsf_fifo_select(pipe, fifo, 0);
@@ -1191,6 +1195,7 @@
usbhsf_fifo_clear(pipe, fifo);
pkt->actual = usbhs_dma_calc_received_size(pkt, chan, rcv_len);
+ usbhs_pipe_running(pipe, 0);
usbhsf_dma_stop(pipe, fifo);
usbhsf_dma_unmap(pkt);
usbhsf_fifo_unselect(pipe, pipe->fifo);
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 6891e90..a96dcc6 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -1076,7 +1076,7 @@
return 0;
err = uas_configure_endpoints(devinfo);
- if (err && err != ENODEV)
+ if (err && err != -ENODEV)
shost_printk(KERN_ERR, shost,
"%s: alloc streams error %d after reset",
__func__, err);
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index b605115..ca3a5d4 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -2137,6 +2137,13 @@
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_BROKEN_FUA ),
+/* Reported by Teijo Kinnunen <teijo.kinnunen@code-q.fi> */
+UNUSUAL_DEV( 0x152d, 0x2567, 0x0117, 0x0117,
+ "JMicron",
+ "USB to ATA/ATAPI Bridge",
+ 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",
diff --git a/drivers/usb/usbip/vudc_sysfs.c b/drivers/usb/usbip/vudc_sysfs.c
index 0f98f2c..7efa374 100644
--- a/drivers/usb/usbip/vudc_sysfs.c
+++ b/drivers/usb/usbip/vudc_sysfs.c
@@ -117,10 +117,14 @@
if (rv != 0)
return -EINVAL;
+ if (!udc) {
+ dev_err(dev, "no device");
+ return -ENODEV;
+ }
spin_lock_irqsave(&udc->lock, flags);
/* Don't export what we don't have */
- if (!udc || !udc->driver || !udc->pullup) {
- dev_err(dev, "no device or gadget not bound");
+ if (!udc->driver || !udc->pullup) {
+ dev_err(dev, "gadget not bound");
ret = -ENODEV;
goto unlock;
}
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
index 59b3f62..70c748a 100644
--- a/drivers/vfio/vfio_iommu_spapr_tce.c
+++ b/drivers/vfio/vfio_iommu_spapr_tce.c
@@ -195,6 +195,11 @@
return ret;
tcemem = kzalloc(sizeof(*tcemem), GFP_KERNEL);
+ if (!tcemem) {
+ mm_iommu_put(container->mm, mem);
+ return -ENOMEM;
+ }
+
tcemem->mem = mem;
list_add(&tcemem->next, &container->prereg_list);
@@ -1332,8 +1337,16 @@
if (!table_group->ops || !table_group->ops->take_ownership ||
!table_group->ops->release_ownership) {
+ if (container->v2) {
+ ret = -EPERM;
+ goto unlock_exit;
+ }
ret = tce_iommu_take_ownership(container, table_group);
} else {
+ if (!container->v2) {
+ ret = -EPERM;
+ goto unlock_exit;
+ }
ret = tce_iommu_take_ownership_ddw(container, table_group);
if (!tce_groups_attached(container) && !container->tables[0])
container->def_window_pending = true;
diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c
index b90ef96..25d5bc3 100644
--- a/drivers/video/console/dummycon.c
+++ b/drivers/video/console/dummycon.c
@@ -41,12 +41,55 @@
vc_resize(vc, DUMMY_COLUMNS, DUMMY_ROWS);
}
-static int dummycon_dummy(void)
+static void dummycon_deinit(struct vc_data *vc)
+{
+}
+
+static void dummycon_clear(struct vc_data *vc, int a, int b, int c, int d)
+{
+}
+
+static void dummycon_putc(struct vc_data *vc, int a, int b, int c)
+{
+}
+
+static void dummycon_putcs(struct vc_data *vc, const unsigned short *s, int a, int b, int c)
+{
+}
+
+static void dummycon_cursor(struct vc_data *vc, int a)
+{
+}
+
+static int dummycon_scroll(struct vc_data *vc, int a, int b, int c, int d)
{
return 0;
}
-#define DUMMY (void *)dummycon_dummy
+static int dummycon_switch(struct vc_data *vc)
+{
+ return 0;
+}
+
+static int dummycon_blank(struct vc_data *vc, int a, int b)
+{
+ return 0;
+}
+
+static int dummycon_font_set(struct vc_data *vc, struct console_font *f, unsigned u)
+{
+ return 0;
+}
+
+static int dummycon_font_default(struct vc_data *vc, struct console_font *f, char *c)
+{
+ return 0;
+}
+
+static int dummycon_font_copy(struct vc_data *vc, int a)
+{
+ return 0;
+}
/*
* The console `switch' structure for the dummy console
@@ -58,16 +101,16 @@
.owner = THIS_MODULE,
.con_startup = dummycon_startup,
.con_init = dummycon_init,
- .con_deinit = DUMMY,
- .con_clear = DUMMY,
- .con_putc = DUMMY,
- .con_putcs = DUMMY,
- .con_cursor = DUMMY,
- .con_scroll = DUMMY,
- .con_switch = DUMMY,
- .con_blank = DUMMY,
- .con_font_set = DUMMY,
- .con_font_default = DUMMY,
- .con_font_copy = DUMMY,
+ .con_deinit = dummycon_deinit,
+ .con_clear = dummycon_clear,
+ .con_putc = dummycon_putc,
+ .con_putcs = dummycon_putcs,
+ .con_cursor = dummycon_cursor,
+ .con_scroll = dummycon_scroll,
+ .con_switch = dummycon_switch,
+ .con_blank = dummycon_blank,
+ .con_font_set = dummycon_font_set,
+ .con_font_default = dummycon_font_default,
+ .con_font_copy = dummycon_font_copy,
};
EXPORT_SYMBOL_GPL(dummy_con);
diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c
index 1157661..dda1c4b 100644
--- a/drivers/video/console/vgacon.c
+++ b/drivers/video/console/vgacon.c
@@ -405,7 +405,10 @@
vga_video_port_val = VGA_CRT_DM;
if ((screen_info.orig_video_ega_bx & 0xff) != 0x10) {
static struct resource ega_console_resource =
- { .name = "ega", .start = 0x3B0, .end = 0x3BF };
+ { .name = "ega",
+ .flags = IORESOURCE_IO,
+ .start = 0x3B0,
+ .end = 0x3BF };
vga_video_type = VIDEO_TYPE_EGAM;
vga_vram_size = 0x8000;
display_desc = "EGA+";
@@ -413,9 +416,15 @@
&ega_console_resource);
} else {
static struct resource mda1_console_resource =
- { .name = "mda", .start = 0x3B0, .end = 0x3BB };
+ { .name = "mda",
+ .flags = IORESOURCE_IO,
+ .start = 0x3B0,
+ .end = 0x3BB };
static struct resource mda2_console_resource =
- { .name = "mda", .start = 0x3BF, .end = 0x3BF };
+ { .name = "mda",
+ .flags = IORESOURCE_IO,
+ .start = 0x3BF,
+ .end = 0x3BF };
vga_video_type = VIDEO_TYPE_MDA;
vga_vram_size = 0x2000;
display_desc = "*MDA";
@@ -437,15 +446,21 @@
vga_vram_size = 0x8000;
if (!screen_info.orig_video_isVGA) {
- static struct resource ega_console_resource
- = { .name = "ega", .start = 0x3C0, .end = 0x3DF };
+ static struct resource ega_console_resource =
+ { .name = "ega",
+ .flags = IORESOURCE_IO,
+ .start = 0x3C0,
+ .end = 0x3DF };
vga_video_type = VIDEO_TYPE_EGAC;
display_desc = "EGA";
request_resource(&ioport_resource,
&ega_console_resource);
} else {
- static struct resource vga_console_resource
- = { .name = "vga+", .start = 0x3C0, .end = 0x3DF };
+ static struct resource vga_console_resource =
+ { .name = "vga+",
+ .flags = IORESOURCE_IO,
+ .start = 0x3C0,
+ .end = 0x3DF };
vga_video_type = VIDEO_TYPE_VGAC;
display_desc = "VGA+";
request_resource(&ioport_resource,
@@ -489,7 +504,10 @@
}
} else {
static struct resource cga_console_resource =
- { .name = "cga", .start = 0x3D4, .end = 0x3D5 };
+ { .name = "cga",
+ .flags = IORESOURCE_IO,
+ .start = 0x3D4,
+ .end = 0x3D5 };
vga_video_type = VIDEO_TYPE_CGA;
vga_vram_size = 0x2000;
display_desc = "*CGA";
diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c
index ec2671d..89880b7 100644
--- a/drivers/video/fbdev/amba-clcd.c
+++ b/drivers/video/fbdev/amba-clcd.c
@@ -892,8 +892,8 @@
if (err)
return err;
- framesize = fb->panel->mode.xres * fb->panel->mode.yres *
- fb->panel->bpp / 8;
+ framesize = PAGE_ALIGN(fb->panel->mode.xres * fb->panel->mode.yres *
+ fb->panel->bpp / 8);
fb->fb.screen_base = dma_alloc_coherent(&fb->dev->dev, framesize,
&dma, GFP_KERNEL);
if (!fb->fb.screen_base)
diff --git a/drivers/video/fbdev/msm/mdp3.c b/drivers/video/fbdev/msm/mdp3.c
index 00183f8..591c240 100644
--- a/drivers/video/fbdev/msm/mdp3.c
+++ b/drivers/video/fbdev/msm/mdp3.c
@@ -44,6 +44,7 @@
#include <linux/msm-bus.h>
#include <linux/msm-bus-board.h>
+#include <linux/vmalloc.h>
#include <linux/msm_dma_iommu_mapping.h>
@@ -1367,7 +1368,7 @@
return -EINVAL;
}
if (client == MDP3_CLIENT_PPP || client == MDP3_CLIENT_DMA_P) {
- kfree(data->tab_clone->sgl);
+ vfree(data->tab_clone->sgl);
kfree(data->tab_clone);
}
return 0;
@@ -1496,7 +1497,7 @@
dma_buf_put(data->srcp_dma_buf);
if (client == MDP3_CLIENT_PPP || client == MDP3_CLIENT_DMA_P) {
- kfree(data->tab_clone->sgl);
+ vfree(data->tab_clone->sgl);
kfree(data->tab_clone);
}
return ret;
diff --git a/drivers/video/fbdev/msm/mdp3_ppp.c b/drivers/video/fbdev/msm/mdp3_ppp.c
index 612cccc..9253a69 100644
--- a/drivers/video/fbdev/msm/mdp3_ppp.c
+++ b/drivers/video/fbdev/msm/mdp3_ppp.c
@@ -22,6 +22,7 @@
#include <linux/mutex.h>
#include "linux/proc_fs.h"
#include <linux/delay.h>
+#include <linux/fence.h>
#include "mdss_fb.h"
#include "mdp3_ppp.h"
@@ -1645,6 +1646,7 @@
}
} else {
fence = req->cur_rel_fence;
+ fence_get((struct fence *) fence);
}
mdp3_ppp_req_push(req_q, req);
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h
index 12a3171..e6d55b5 100644
--- a/drivers/video/fbdev/msm/mdss.h
+++ b/drivers/video/fbdev/msm/mdss.h
@@ -42,6 +42,7 @@
MDSS_CLK_MDP_CORE,
MDSS_CLK_MDP_LUT,
MDSS_CLK_MDP_VSYNC,
+ MDSS_CLK_MNOC_AHB,
MDSS_MAX_CLK
};
diff --git a/drivers/video/fbdev/msm/mdss_dsi_phy_12nm.c b/drivers/video/fbdev/msm/mdss_dsi_phy_12nm.c
index 7b9a536..9bd2c4d 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_phy_12nm.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_phy_12nm.c
@@ -25,6 +25,7 @@
#define HSTX_CLKLANE_REQSTATE_TIM_CTRL 0x180
#define HSTX_CLKLANE_HS0STATE_TIM_CTRL 0x188
#define HSTX_CLKLANE_TRALSTATE_TIM_CTRL 0x18c
+#define HSTX_CLKLANE_EXITSTATE_TIM_CTRL 0x190
#define HSTX_CLKLANE_CLKPOSTSTATE_TIM_CTRL 0x194
#define HSTX_DATALANE_REQSTATE_TIM_CTRL 0x1c0
#define HSTX_DATALANE_HS0STATE_TIM_CTRL 0x1c8
@@ -70,6 +71,8 @@
(pd->timing_12nm[2] | BIT(6)));
DSI_PHY_W32(ctrl->phy_io.base, HSTX_CLKLANE_REQSTATE_TIM_CTRL,
pd->timing_12nm[3]);
+ DSI_PHY_W32(ctrl->phy_io.base, HSTX_CLKLANE_EXITSTATE_TIM_CTRL,
+ (pd->timing_12nm[7] | BIT(6) | BIT(7)));
/* DSI PHY data lane timings */
DSI_PHY_W32(ctrl->phy_io.base, HSTX_DATALANE_HS0STATE_TIM_CTRL,
diff --git a/drivers/video/fbdev/msm/mdss_dsi_status.c b/drivers/video/fbdev/msm/mdss_dsi_status.c
index 992d687..c5af962 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_status.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_status.c
@@ -133,14 +133,16 @@
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
struct mdss_panel_info *pinfo;
struct msm_fb_data_type *mfd;
+ char fb_id[7] = {'\0'};
if (!evdata) {
pr_err("%s: event data not available\n", __func__);
return NOTIFY_BAD;
}
+ strlcpy(fb_id, evdata->info->fix.id, 7);
/* handle only mdss fb device */
- if (strcmp("mdssfb", evdata->info->fix.id))
+ if (strcmp("mdssfb", fb_id))
return NOTIFY_DONE;
mfd = evdata->info->par;
@@ -189,7 +191,8 @@
return 0;
}
-static int param_dsi_status_disable(const char *val, struct kernel_param *kp)
+static int param_dsi_status_disable(const char *val,
+ const struct kernel_param *kp)
{
int ret = 0;
int int_val;
@@ -204,7 +207,7 @@
return ret;
}
-static int param_set_interval(const char *val, struct kernel_param *kp)
+static int param_set_interval(const char *val, const struct kernel_param *kp)
{
int ret = 0;
int int_val;
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index d07650b..e472e7f 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -1270,6 +1270,7 @@
mdss_update_reg_bus_vote(mdata->reg_bus_clt,
VOTE_INDEX_LOW);
mdss_bus_rt_bw_vote(true);
+ mdss_mdp_clk_update(MDSS_CLK_MNOC_AHB, 1);
mdss_mdp_clk_update(MDSS_CLK_AHB, 1);
mdss_mdp_clk_update(MDSS_CLK_AXI, 1);
mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, 1);
@@ -1278,6 +1279,7 @@
mdss_mdp_clk_update(MDSS_CLK_AXI, 0);
mdss_mdp_clk_update(MDSS_CLK_AHB, 0);
mdss_bus_rt_bw_vote(false);
+ mdss_mdp_clk_update(MDSS_CLK_MNOC_AHB, 0);
mdss_update_reg_bus_vote(mdata->reg_bus_clt,
VOTE_INDEX_DISABLE);
}
@@ -1558,6 +1560,7 @@
mdata->clk_ena = enable;
spin_unlock_irqrestore(&mdp_lock, flags);
+ mdss_mdp_clk_update(MDSS_CLK_MNOC_AHB, enable);
mdss_mdp_clk_update(MDSS_CLK_AHB, enable);
mdss_mdp_clk_update(MDSS_CLK_AXI, enable);
mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, enable);
@@ -1721,6 +1724,9 @@
/* vsync_clk is optional for non-smart panels */
mdss_mdp_irq_clk_register(mdata, "vsync_clk", MDSS_CLK_MDP_VSYNC);
+ /* this clk is not present on all MDSS revisions */
+ mdss_mdp_irq_clk_register(mdata, "mnoc_clk", MDSS_CLK_MNOC_AHB);
+
/* Setting the default clock rate to the max supported.*/
mdss_mdp_set_clk_rate(mdata->max_mdp_clk_rate);
pr_debug("mdp clk rate=%ld\n",
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
index 4442f19..764b830 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
@@ -575,7 +575,9 @@
}
/* Get both controllers in the correct order for dual displays */
- mdss_mdp_get_split_display_ctls(&ctl, &sctl);
+ rc = mdss_mdp_get_split_display_ctls(&ctl, &sctl);
+ if (rc)
+ goto exit;
ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX];
if (!ctx) {
diff --git a/drivers/video/fbdev/msm/mdss_mdp_util.c b/drivers/video/fbdev/msm/mdss_mdp_util.c
index 29f6c1a..c030109 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_util.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_util.c
@@ -31,6 +31,7 @@
#include "mdss_panel.h"
#define PHY_ADDR_4G (1ULL<<32)
+#define ALIGN_UP(x, align) ((DIV_ROUND_UP((x), (align))) * (align))
void mdss_mdp_format_flag_removal(u32 *table, u32 num, u32 remove_bits)
{
@@ -451,13 +452,13 @@
}
/* Y bitstream stride and plane size */
- ps->ystride[0] = ALIGN(width, y_stride_alignment);
+ ps->ystride[0] = ALIGN_UP(width, y_stride_alignment);
ps->ystride[0] = (ps->ystride[0] * y_bpp_numer) / y_bpp_denom;
ps->plane_size[0] = ALIGN(ps->ystride[0] *
ALIGN(height, y_height_alignment), 4096);
/* CbCr bitstream stride and plane size */
- ps->ystride[1] = ALIGN(width / 2, uv_stride_alignment);
+ ps->ystride[1] = ALIGN_UP(width / 2, uv_stride_alignment);
ps->ystride[1] = (ps->ystride[1] * uv_bpp_numer) / uv_bpp_denom;
ps->plane_size[1] = ALIGN(ps->ystride[1] *
ALIGN(height / 2, uv_height_alignment), 4096);
diff --git a/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c
index f51a1b8..4952e7e 100644
--- a/drivers/video/fbdev/msm/mdss_rotator.c
+++ b/drivers/video/fbdev/msm/mdss_rotator.c
@@ -23,6 +23,7 @@
#include <linux/msm-bus.h>
#include <linux/msm-bus-board.h>
#include <linux/regulator/consumer.h>
+#include <linux/sync_file.h>
#include "mdss_rotator_internal.h"
#include "mdss_mdp.h"
@@ -374,6 +375,24 @@
return false;
}
+static int mdss_rotator_install_fence_fd(struct mdss_rot_entry_container *req)
+{
+ int i;
+ int ret = 0;
+ struct sync_file *sync_file;
+
+ for (i = 0; i < req->count; i++) {
+ sync_file = sync_file_create((struct fence *)
+ (req->entries[i].output_fence));
+ if (!sync_file) {
+ ret = -ENOMEM;
+ break;
+ }
+ fd_install(req->entries[i].output_fence_fd, sync_file->file);
+ }
+ return ret;
+}
+
static int mdss_rotator_create_fence(struct mdss_rot_entry *entry)
{
int ret = 0, fd;
@@ -395,9 +414,10 @@
pr_err("cannot create sync point\n");
goto sync_pt_create_err;
}
- fd = mdss_get_sync_fence_fd(fence);
+
+ fd = get_unused_fd_flags(O_CLOEXEC);
if (fd < 0) {
- pr_err("get_unused_fd_flags failed error:0x%x\n", fd);
+ pr_err("fail to get unused fd\n");
ret = fd;
goto get_fd_err;
}
@@ -2245,6 +2265,13 @@
goto handle_request_err1;
}
+ ret = mdss_rotator_install_fence_fd(req);
+ if (ret) {
+ pr_err("get_unused_fd_flags failed error:0x%x\n", ret);
+ mdss_rotator_remove_request(mgr, private, req);
+ goto handle_request_err1;
+ }
+
mdss_rotator_queue_request(mgr, private, req);
mutex_unlock(&mgr->lock);
@@ -2404,6 +2431,13 @@
goto handle_request32_err1;
}
+ ret = mdss_rotator_install_fence_fd(req);
+ if (ret) {
+ pr_err("get_unused_fd_flags failed error:0x%x\n", ret);
+ mdss_rotator_remove_request(mgr, private, req);
+ goto handle_request32_err1;
+ }
+
mdss_rotator_queue_request(mgr, private, req);
mutex_unlock(&mgr->lock);
diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c
index fbbdaf2..2d8b5d5 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.c
+++ b/drivers/video/fbdev/msm/mdss_smmu.c
@@ -25,6 +25,7 @@
#include <linux/dma-buf.h>
#include <linux/of_platform.h>
#include <linux/msm_dma_iommu_mapping.h>
+#include <linux/vmalloc.h>
#include <asm/dma-iommu.h>
#include "soc/qcom/secure_buffer.h"
@@ -522,7 +523,7 @@
* either sides of sgl.
*
* Returns:
- * Pointer to new kmalloced sg list, ERR_PTR() on error
+ * Pointer to new vmalloced sg list, ERR_PTR() on error
*
*/
static struct scatterlist *sg_clone(struct scatterlist *orig_sgl, u64 len,
@@ -538,7 +539,7 @@
if (padding)
nents += 2;
- head = kmalloc_array(nents, sizeof(struct scatterlist), gfp_mask);
+ head = vmalloc(nents * sizeof(struct scatterlist));
if (!head)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
index f0e46f7..153b39f 100644
--- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c
+++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
@@ -2280,7 +2280,8 @@
if (ctrl->phy_power_off || mmss_clamp)
mdss_dsi_phy_power_on(ctrl, mmss_clamp);
}
- if ((clk & MDSS_DSI_LINK_CLK) && (l_type == MDSS_DSI_LINK_HS_CLK)) {
+
+ if ((clk & MDSS_DSI_LINK_CLK) && (l_type == MDSS_DSI_LINK_LP_CLK)) {
if (ctrl->ulps) {
/*
* ULPS Entry Request. This is needed if the lanes were
@@ -2311,9 +2312,12 @@
goto error;
}
}
- /* Enable HS TX driver in DSI PHY if applicable */
- mdss_dsi_phy_hstx_drv_ctrl(ctrl, true);
}
+
+ /* Enable HS TX driver in DSI PHY if applicable */
+ if ((clk & MDSS_DSI_LINK_CLK) && (l_type == MDSS_DSI_LINK_HS_CLK))
+ mdss_dsi_phy_hstx_drv_ctrl(ctrl, true);
+
error:
return rc;
}
@@ -2429,6 +2433,26 @@
if (ctrl->mdss_util->dyn_clk_gating_ctrl)
ctrl->mdss_util->dyn_clk_gating_ctrl(0);
+ if ((clk_type & MDSS_DSI_LINK_CLK) &&
+ (l_type == MDSS_DSI_LINK_HS_CLK)) {
+ u32 data = 0;
+
+ data = MIPI_INP((ctrl->ctrl_io.base) + 0x0120);
+ /*
+ * For 12nm PHY, the PLL unlock bit in DSI_CLK_STATUS gets set
+ * when PLL is turned off. When device comes out of static
+ * screen without the DSI controller getting power collapsed,
+ * the bit might not clear sometimes. Clear the bit before
+ * turning ON the PLL. This avoids false error interrupt due to
+ * PLL unlocked bit after PLL is turned ON.
+ */
+ if (data & BIT(16)) {
+ pr_debug("pll unlocked: 0x%x\n", data);
+ MIPI_OUTP((ctrl->ctrl_io.base) + 0x120, BIT(16));
+ }
+
+ }
+
return rc;
}
diff --git a/drivers/video/fbdev/omap2/omapfb/displays/panel-tpo-td028ttec1.c b/drivers/video/fbdev/omap2/omapfb/displays/panel-tpo-td028ttec1.c
index b529a8c..3984716 100644
--- a/drivers/video/fbdev/omap2/omapfb/displays/panel-tpo-td028ttec1.c
+++ b/drivers/video/fbdev/omap2/omapfb/displays/panel-tpo-td028ttec1.c
@@ -455,6 +455,8 @@
}
static const struct of_device_id td028ttec1_of_match[] = {
+ { .compatible = "omapdss,tpo,td028ttec1", },
+ /* keep to not break older DTB */
{ .compatible = "omapdss,toppoly,td028ttec1", },
{},
};
@@ -474,6 +476,7 @@
module_spi_driver(td028ttec1_spi_driver);
+MODULE_ALIAS("spi:tpo,td028ttec1");
MODULE_ALIAS("spi:toppoly,td028ttec1");
MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
MODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver");
diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dss.c b/drivers/video/fbdev/omap2/omapfb/dss/dss.c
index 47d7f69..48c6500 100644
--- a/drivers/video/fbdev/omap2/omapfb/dss/dss.c
+++ b/drivers/video/fbdev/omap2/omapfb/dss/dss.c
@@ -941,11 +941,13 @@
return 0;
}
+static void dss_uninit_ports(struct platform_device *pdev);
+
static int dss_init_ports(struct platform_device *pdev)
{
struct device_node *parent = pdev->dev.of_node;
struct device_node *port;
- int r;
+ int r, ret = 0;
if (parent == NULL)
return 0;
@@ -972,17 +974,21 @@
switch (port_type) {
case OMAP_DISPLAY_TYPE_DPI:
- dpi_init_port(pdev, port);
+ ret = dpi_init_port(pdev, port);
break;
case OMAP_DISPLAY_TYPE_SDI:
- sdi_init_port(pdev, port);
+ ret = sdi_init_port(pdev, port);
break;
default:
break;
}
- } while ((port = omapdss_of_get_next_port(parent, port)) != NULL);
+ } while (!ret &&
+ (port = omapdss_of_get_next_port(parent, port)) != NULL);
- return 0;
+ if (ret)
+ dss_uninit_ports(pdev);
+
+ return ret;
}
static void dss_uninit_ports(struct platform_device *pdev)
diff --git a/drivers/video/fbdev/sm501fb.c b/drivers/video/fbdev/sm501fb.c
index d0a4e2f..d215faa 100644
--- a/drivers/video/fbdev/sm501fb.c
+++ b/drivers/video/fbdev/sm501fb.c
@@ -1600,6 +1600,7 @@
info->fbmem = ioremap(res->start, resource_size(res));
if (info->fbmem == NULL) {
dev_err(dev, "cannot remap framebuffer\n");
+ ret = -ENXIO;
goto err_mem_res;
}
diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c
index 53326ba..2add8de 100644
--- a/drivers/video/fbdev/udlfb.c
+++ b/drivers/video/fbdev/udlfb.c
@@ -1487,15 +1487,25 @@
static int dlfb_select_std_channel(struct dlfb_data *dev)
{
int ret;
- u8 set_def_chn[] = { 0x57, 0xCD, 0xDC, 0xA7,
+ void *buf;
+ static const u8 set_def_chn[] = {
+ 0x57, 0xCD, 0xDC, 0xA7,
0x1C, 0x88, 0x5E, 0x15,
0x60, 0xFE, 0xC6, 0x97,
0x16, 0x3D, 0x47, 0xF2 };
+ buf = kmemdup(set_def_chn, sizeof(set_def_chn), GFP_KERNEL);
+
+ if (!buf)
+ return -ENOMEM;
+
ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
NR_USB_REQUEST_CHANNEL,
(USB_DIR_OUT | USB_TYPE_VENDOR), 0, 0,
- set_def_chn, sizeof(set_def_chn), USB_CTRL_SET_TIMEOUT);
+ buf, sizeof(set_def_chn), USB_CTRL_SET_TIMEOUT);
+
+ kfree(buf);
+
return ret;
}
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
index 1cf907e..111a0ab 100644
--- a/drivers/video/hdmi.c
+++ b/drivers/video/hdmi.c
@@ -321,6 +321,17 @@
}
EXPORT_SYMBOL(hdmi_vendor_infoframe_init);
+static int hdmi_vendor_infoframe_length(const struct hdmi_vendor_infoframe *frame)
+{
+ /* for side by side (half) we also need to provide 3D_Ext_Data */
+ if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
+ return 6;
+ else if (frame->vic != 0 || frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID)
+ return 5;
+ else
+ return 4;
+}
+
/**
* hdmi_vendor_infoframe_pack() - write a HDMI vendor infoframe to binary buffer
* @frame: HDMI infoframe
@@ -341,19 +352,11 @@
u8 *ptr = buffer;
size_t length;
- /* empty info frame */
- if (frame->vic == 0 && frame->s3d_struct == HDMI_3D_STRUCTURE_INVALID)
- return -EINVAL;
-
/* only one of those can be supplied */
if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID)
return -EINVAL;
- /* for side by side (half) we also need to provide 3D_Ext_Data */
- if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
- frame->length = 6;
- else
- frame->length = 5;
+ frame->length = hdmi_vendor_infoframe_length(frame);
length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
@@ -372,14 +375,16 @@
ptr[5] = 0x0c;
ptr[6] = 0x00;
- if (frame->vic) {
- ptr[7] = 0x1 << 5; /* video format */
- ptr[8] = frame->vic;
- } else {
+ if (frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID) {
ptr[7] = 0x2 << 5; /* video format */
ptr[8] = (frame->s3d_struct & 0xf) << 4;
if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
ptr[9] = (frame->s3d_ext_data & 0xf) << 4;
+ } else if (frame->vic) {
+ ptr[7] = 0x1 << 5; /* video format */
+ ptr[8] = frame->vic;
+ } else {
+ ptr[7] = 0x0 << 5; /* video format */
}
hdmi_infoframe_set_checksum(buffer, length);
@@ -1165,7 +1170,7 @@
if (ptr[0] != HDMI_INFOFRAME_TYPE_VENDOR ||
ptr[1] != 1 ||
- (ptr[2] != 5 && ptr[2] != 6))
+ (ptr[2] != 4 && ptr[2] != 5 && ptr[2] != 6))
return -EINVAL;
length = ptr[2];
@@ -1193,16 +1198,22 @@
hvf->length = length;
- if (hdmi_video_format == 0x1) {
- hvf->vic = ptr[4];
- } else if (hdmi_video_format == 0x2) {
+ if (hdmi_video_format == 0x2) {
+ if (length != 5 && length != 6)
+ return -EINVAL;
hvf->s3d_struct = ptr[4] >> 4;
if (hvf->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) {
- if (length == 6)
- hvf->s3d_ext_data = ptr[5] >> 4;
- else
+ if (length != 6)
return -EINVAL;
+ hvf->s3d_ext_data = ptr[5] >> 4;
}
+ } else if (hdmi_video_format == 0x1) {
+ if (length != 5)
+ return -EINVAL;
+ hvf->vic = ptr[4];
+ } else {
+ if (length != 4)
+ return -EINVAL;
}
return 0;
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index 489bfc6..8977f40 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -423,8 +423,6 @@
i = vq->vring.desc[i].next;
}
- vq->vq.num_free += total_sg;
-
if (indirect)
kfree(desc);
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
index 70c7194..b0a1580 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -28,16 +28,7 @@
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/watchdog.h>
-#ifdef CONFIG_HPWDT_NMI_DECODING
-#include <linux/dmi.h>
-#include <linux/spinlock.h>
-#include <linux/nmi.h>
-#include <linux/kdebug.h>
-#include <linux/notifier.h>
-#include <asm/cacheflush.h>
-#endif /* CONFIG_HPWDT_NMI_DECODING */
#include <asm/nmi.h>
-#include <asm/frame.h>
#define HPWDT_VERSION "1.4.0"
#define SECS_TO_TICKS(secs) ((secs) * 1000 / 128)
@@ -48,10 +39,14 @@
static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */
static unsigned int reload; /* the computed soft_margin */
static bool nowayout = WATCHDOG_NOWAYOUT;
+#ifdef CONFIG_HPWDT_NMI_DECODING
+static unsigned int allow_kdump = 1;
+#endif
static char expect_release;
static unsigned long hpwdt_is_open;
static void __iomem *pci_mem_addr; /* the PCI-memory address */
+static unsigned long __iomem *hpwdt_nmistat;
static unsigned long __iomem *hpwdt_timer_reg;
static unsigned long __iomem *hpwdt_timer_con;
@@ -62,373 +57,6 @@
};
MODULE_DEVICE_TABLE(pci, hpwdt_devices);
-#ifdef CONFIG_HPWDT_NMI_DECODING
-#define PCI_BIOS32_SD_VALUE 0x5F32335F /* "_32_" */
-#define CRU_BIOS_SIGNATURE_VALUE 0x55524324
-#define PCI_BIOS32_PARAGRAPH_LEN 16
-#define PCI_ROM_BASE1 0x000F0000
-#define ROM_SIZE 0x10000
-
-struct bios32_service_dir {
- u32 signature;
- u32 entry_point;
- u8 revision;
- u8 length;
- u8 checksum;
- u8 reserved[5];
-};
-
-/* type 212 */
-struct smbios_cru64_info {
- u8 type;
- u8 byte_length;
- u16 handle;
- u32 signature;
- u64 physical_address;
- u32 double_length;
- u32 double_offset;
-};
-#define SMBIOS_CRU64_INFORMATION 212
-
-/* type 219 */
-struct smbios_proliant_info {
- u8 type;
- u8 byte_length;
- u16 handle;
- u32 power_features;
- u32 omega_features;
- u32 reserved;
- u32 misc_features;
-};
-#define SMBIOS_ICRU_INFORMATION 219
-
-
-struct cmn_registers {
- union {
- struct {
- u8 ral;
- u8 rah;
- u16 rea2;
- };
- u32 reax;
- } u1;
- union {
- struct {
- u8 rbl;
- u8 rbh;
- u8 reb2l;
- u8 reb2h;
- };
- u32 rebx;
- } u2;
- union {
- struct {
- u8 rcl;
- u8 rch;
- u16 rec2;
- };
- u32 recx;
- } u3;
- union {
- struct {
- u8 rdl;
- u8 rdh;
- u16 red2;
- };
- u32 redx;
- } u4;
-
- u32 resi;
- u32 redi;
- u16 rds;
- u16 res;
- u32 reflags;
-} __attribute__((packed));
-
-static unsigned int hpwdt_nmi_decoding;
-static unsigned int allow_kdump = 1;
-static unsigned int is_icru;
-static unsigned int is_uefi;
-static DEFINE_SPINLOCK(rom_lock);
-static void *cru_rom_addr;
-static struct cmn_registers cmn_regs;
-
-extern asmlinkage void asminline_call(struct cmn_registers *pi86Regs,
- unsigned long *pRomEntry);
-
-#ifdef CONFIG_X86_32
-/* --32 Bit Bios------------------------------------------------------------ */
-
-#define HPWDT_ARCH 32
-
-asm(".text \n\t"
- ".align 4 \n\t"
- ".globl asminline_call \n"
- "asminline_call: \n\t"
- "pushl %ebp \n\t"
- "movl %esp, %ebp \n\t"
- "pusha \n\t"
- "pushf \n\t"
- "push %es \n\t"
- "push %ds \n\t"
- "pop %es \n\t"
- "movl 8(%ebp),%eax \n\t"
- "movl 4(%eax),%ebx \n\t"
- "movl 8(%eax),%ecx \n\t"
- "movl 12(%eax),%edx \n\t"
- "movl 16(%eax),%esi \n\t"
- "movl 20(%eax),%edi \n\t"
- "movl (%eax),%eax \n\t"
- "push %cs \n\t"
- "call *12(%ebp) \n\t"
- "pushf \n\t"
- "pushl %eax \n\t"
- "movl 8(%ebp),%eax \n\t"
- "movl %ebx,4(%eax) \n\t"
- "movl %ecx,8(%eax) \n\t"
- "movl %edx,12(%eax) \n\t"
- "movl %esi,16(%eax) \n\t"
- "movl %edi,20(%eax) \n\t"
- "movw %ds,24(%eax) \n\t"
- "movw %es,26(%eax) \n\t"
- "popl %ebx \n\t"
- "movl %ebx,(%eax) \n\t"
- "popl %ebx \n\t"
- "movl %ebx,28(%eax) \n\t"
- "pop %es \n\t"
- "popf \n\t"
- "popa \n\t"
- "leave \n\t"
- "ret \n\t"
- ".previous");
-
-
-/*
- * cru_detect
- *
- * Routine Description:
- * This function uses the 32-bit BIOS Service Directory record to
- * search for a $CRU record.
- *
- * Return Value:
- * 0 : SUCCESS
- * <0 : FAILURE
- */
-static int cru_detect(unsigned long map_entry,
- unsigned long map_offset)
-{
- void *bios32_map;
- unsigned long *bios32_entrypoint;
- unsigned long cru_physical_address;
- unsigned long cru_length;
- unsigned long physical_bios_base = 0;
- unsigned long physical_bios_offset = 0;
- int retval = -ENODEV;
-
- bios32_map = ioremap(map_entry, (2 * PAGE_SIZE));
-
- if (bios32_map == NULL)
- return -ENODEV;
-
- bios32_entrypoint = bios32_map + map_offset;
-
- cmn_regs.u1.reax = CRU_BIOS_SIGNATURE_VALUE;
-
- set_memory_x((unsigned long)bios32_map, 2);
- asminline_call(&cmn_regs, bios32_entrypoint);
-
- if (cmn_regs.u1.ral != 0) {
- pr_warn("Call succeeded but with an error: 0x%x\n",
- cmn_regs.u1.ral);
- } else {
- physical_bios_base = cmn_regs.u2.rebx;
- physical_bios_offset = cmn_regs.u4.redx;
- cru_length = cmn_regs.u3.recx;
- cru_physical_address =
- physical_bios_base + physical_bios_offset;
-
- /* If the values look OK, then map it in. */
- if ((physical_bios_base + physical_bios_offset)) {
- cru_rom_addr =
- ioremap(cru_physical_address, cru_length);
- if (cru_rom_addr) {
- set_memory_x((unsigned long)cru_rom_addr & PAGE_MASK,
- (cru_length + PAGE_SIZE - 1) >> PAGE_SHIFT);
- retval = 0;
- }
- }
-
- pr_debug("CRU Base Address: 0x%lx\n", physical_bios_base);
- pr_debug("CRU Offset Address: 0x%lx\n", physical_bios_offset);
- pr_debug("CRU Length: 0x%lx\n", cru_length);
- pr_debug("CRU Mapped Address: %p\n", &cru_rom_addr);
- }
- iounmap(bios32_map);
- return retval;
-}
-
-/*
- * bios_checksum
- */
-static int bios_checksum(const char __iomem *ptr, int len)
-{
- char sum = 0;
- int i;
-
- /*
- * calculate checksum of size bytes. This should add up
- * to zero if we have a valid header.
- */
- for (i = 0; i < len; i++)
- sum += ptr[i];
-
- return ((sum == 0) && (len > 0));
-}
-
-/*
- * bios32_present
- *
- * Routine Description:
- * This function finds the 32-bit BIOS Service Directory
- *
- * Return Value:
- * 0 : SUCCESS
- * <0 : FAILURE
- */
-static int bios32_present(const char __iomem *p)
-{
- struct bios32_service_dir *bios_32_ptr;
- int length;
- unsigned long map_entry, map_offset;
-
- bios_32_ptr = (struct bios32_service_dir *) p;
-
- /*
- * Search for signature by checking equal to the swizzled value
- * instead of calling another routine to perform a strcmp.
- */
- if (bios_32_ptr->signature == PCI_BIOS32_SD_VALUE) {
- length = bios_32_ptr->length * PCI_BIOS32_PARAGRAPH_LEN;
- if (bios_checksum(p, length)) {
- /*
- * According to the spec, we're looking for the
- * first 4KB-aligned address below the entrypoint
- * listed in the header. The Service Directory code
- * is guaranteed to occupy no more than 2 4KB pages.
- */
- map_entry = bios_32_ptr->entry_point & ~(PAGE_SIZE - 1);
- map_offset = bios_32_ptr->entry_point - map_entry;
-
- return cru_detect(map_entry, map_offset);
- }
- }
- return -ENODEV;
-}
-
-static int detect_cru_service(void)
-{
- char __iomem *p, *q;
- int rc = -1;
-
- /*
- * Search from 0x0f0000 through 0x0fffff, inclusive.
- */
- p = ioremap(PCI_ROM_BASE1, ROM_SIZE);
- if (p == NULL)
- return -ENOMEM;
-
- for (q = p; q < p + ROM_SIZE; q += 16) {
- rc = bios32_present(q);
- if (!rc)
- break;
- }
- iounmap(p);
- return rc;
-}
-/* ------------------------------------------------------------------------- */
-#endif /* CONFIG_X86_32 */
-#ifdef CONFIG_X86_64
-/* --64 Bit Bios------------------------------------------------------------ */
-
-#define HPWDT_ARCH 64
-
-asm(".text \n\t"
- ".align 4 \n\t"
- ".globl asminline_call \n\t"
- ".type asminline_call, @function \n\t"
- "asminline_call: \n\t"
- FRAME_BEGIN
- "pushq %rax \n\t"
- "pushq %rbx \n\t"
- "pushq %rdx \n\t"
- "pushq %r12 \n\t"
- "pushq %r9 \n\t"
- "movq %rsi, %r12 \n\t"
- "movq %rdi, %r9 \n\t"
- "movl 4(%r9),%ebx \n\t"
- "movl 8(%r9),%ecx \n\t"
- "movl 12(%r9),%edx \n\t"
- "movl 16(%r9),%esi \n\t"
- "movl 20(%r9),%edi \n\t"
- "movl (%r9),%eax \n\t"
- "call *%r12 \n\t"
- "pushfq \n\t"
- "popq %r12 \n\t"
- "movl %eax, (%r9) \n\t"
- "movl %ebx, 4(%r9) \n\t"
- "movl %ecx, 8(%r9) \n\t"
- "movl %edx, 12(%r9) \n\t"
- "movl %esi, 16(%r9) \n\t"
- "movl %edi, 20(%r9) \n\t"
- "movq %r12, %rax \n\t"
- "movl %eax, 28(%r9) \n\t"
- "popq %r9 \n\t"
- "popq %r12 \n\t"
- "popq %rdx \n\t"
- "popq %rbx \n\t"
- "popq %rax \n\t"
- FRAME_END
- "ret \n\t"
- ".previous");
-
-/*
- * dmi_find_cru
- *
- * Routine Description:
- * This function checks whether or not a SMBIOS/DMI record is
- * the 64bit CRU info or not
- */
-static void dmi_find_cru(const struct dmi_header *dm, void *dummy)
-{
- struct smbios_cru64_info *smbios_cru64_ptr;
- unsigned long cru_physical_address;
-
- if (dm->type == SMBIOS_CRU64_INFORMATION) {
- smbios_cru64_ptr = (struct smbios_cru64_info *) dm;
- if (smbios_cru64_ptr->signature == CRU_BIOS_SIGNATURE_VALUE) {
- cru_physical_address =
- smbios_cru64_ptr->physical_address +
- smbios_cru64_ptr->double_offset;
- cru_rom_addr = ioremap(cru_physical_address,
- smbios_cru64_ptr->double_length);
- set_memory_x((unsigned long)cru_rom_addr & PAGE_MASK,
- smbios_cru64_ptr->double_length >> PAGE_SHIFT);
- }
- }
-}
-
-static int detect_cru_service(void)
-{
- cru_rom_addr = NULL;
-
- dmi_walk(dmi_find_cru, NULL);
-
- /* if cru_rom_addr has been set then we found a CRU service */
- return ((cru_rom_addr != NULL) ? 0 : -ENODEV);
-}
-/* ------------------------------------------------------------------------- */
-#endif /* CONFIG_X86_64 */
-#endif /* CONFIG_HPWDT_NMI_DECODING */
/*
* Watchdog operations
@@ -475,32 +103,22 @@
}
#ifdef CONFIG_HPWDT_NMI_DECODING
+static int hpwdt_my_nmi(void)
+{
+ return ioread8(hpwdt_nmistat) & 0x6;
+}
+
/*
* NMI Handler
*/
static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
{
- unsigned long rom_pl;
- static int die_nmi_called;
-
- if (!hpwdt_nmi_decoding)
+ if ((ulReason == NMI_UNKNOWN) && !hpwdt_my_nmi())
return NMI_DONE;
- spin_lock_irqsave(&rom_lock, rom_pl);
- if (!die_nmi_called && !is_icru && !is_uefi)
- asminline_call(&cmn_regs, cru_rom_addr);
- die_nmi_called = 1;
- spin_unlock_irqrestore(&rom_lock, rom_pl);
-
if (allow_kdump)
hpwdt_stop();
- if (!is_icru && !is_uefi) {
- if (cmn_regs.u1.ral == 0) {
- nmi_panic(regs, "An NMI occurred, but unable to determine source.\n");
- return NMI_HANDLED;
- }
- }
nmi_panic(regs, "An NMI occurred. Depending on your system the reason "
"for the NMI is logged in any one of the following "
"resources:\n"
@@ -666,84 +284,11 @@
* Init & Exit
*/
-#ifdef CONFIG_HPWDT_NMI_DECODING
-#ifdef CONFIG_X86_LOCAL_APIC
-static void hpwdt_check_nmi_decoding(struct pci_dev *dev)
-{
- /*
- * If nmi_watchdog is turned off then we can turn on
- * our nmi decoding capability.
- */
- hpwdt_nmi_decoding = 1;
-}
-#else
-static void hpwdt_check_nmi_decoding(struct pci_dev *dev)
-{
- dev_warn(&dev->dev, "NMI decoding is disabled. "
- "Your kernel does not support a NMI Watchdog.\n");
-}
-#endif /* CONFIG_X86_LOCAL_APIC */
-
-/*
- * dmi_find_icru
- *
- * Routine Description:
- * This function checks whether or not we are on an iCRU-based server.
- * This check is independent of architecture and needs to be made for
- * any ProLiant system.
- */
-static void dmi_find_icru(const struct dmi_header *dm, void *dummy)
-{
- struct smbios_proliant_info *smbios_proliant_ptr;
-
- if (dm->type == SMBIOS_ICRU_INFORMATION) {
- smbios_proliant_ptr = (struct smbios_proliant_info *) dm;
- if (smbios_proliant_ptr->misc_features & 0x01)
- is_icru = 1;
- if (smbios_proliant_ptr->misc_features & 0x408)
- is_uefi = 1;
- }
-}
static int hpwdt_init_nmi_decoding(struct pci_dev *dev)
{
+#ifdef CONFIG_HPWDT_NMI_DECODING
int retval;
-
- /*
- * On typical CRU-based systems we need to map that service in
- * the BIOS. For 32 bit Operating Systems we need to go through
- * the 32 Bit BIOS Service Directory. For 64 bit Operating
- * Systems we get that service through SMBIOS.
- *
- * On systems that support the new iCRU service all we need to
- * do is call dmi_walk to get the supported flag value and skip
- * the old cru detect code.
- */
- dmi_walk(dmi_find_icru, NULL);
- if (!is_icru && !is_uefi) {
-
- /*
- * We need to map the ROM to get the CRU service.
- * For 32 bit Operating Systems we need to go through the 32 Bit
- * BIOS Service Directory
- * For 64 bit Operating Systems we get that service through SMBIOS.
- */
- retval = detect_cru_service();
- if (retval < 0) {
- dev_warn(&dev->dev,
- "Unable to detect the %d Bit CRU Service.\n",
- HPWDT_ARCH);
- return retval;
- }
-
- /*
- * We know this is the only CRU call we need to make so lets keep as
- * few instructions as possible once the NMI comes in.
- */
- cmn_regs.u1.rah = 0x0D;
- cmn_regs.u1.ral = 0x02;
- }
-
/*
* Only one function can register for NMI_UNKNOWN
*/
@@ -771,33 +316,19 @@
dev_warn(&dev->dev,
"Unable to register a die notifier (err=%d).\n",
retval);
- if (cru_rom_addr)
- iounmap(cru_rom_addr);
return retval;
-}
-
-static void hpwdt_exit_nmi_decoding(void)
-{
- unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
- unregister_nmi_handler(NMI_SERR, "hpwdt");
- unregister_nmi_handler(NMI_IO_CHECK, "hpwdt");
- if (cru_rom_addr)
- iounmap(cru_rom_addr);
-}
-#else /* !CONFIG_HPWDT_NMI_DECODING */
-static void hpwdt_check_nmi_decoding(struct pci_dev *dev)
-{
-}
-
-static int hpwdt_init_nmi_decoding(struct pci_dev *dev)
-{
+#endif /* CONFIG_HPWDT_NMI_DECODING */
return 0;
}
static void hpwdt_exit_nmi_decoding(void)
{
+#ifdef CONFIG_HPWDT_NMI_DECODING
+ unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
+ unregister_nmi_handler(NMI_SERR, "hpwdt");
+ unregister_nmi_handler(NMI_IO_CHECK, "hpwdt");
+#endif
}
-#endif /* CONFIG_HPWDT_NMI_DECODING */
static int hpwdt_init_one(struct pci_dev *dev,
const struct pci_device_id *ent)
@@ -805,11 +336,6 @@
int retval;
/*
- * Check if we can do NMI decoding or not
- */
- hpwdt_check_nmi_decoding(dev);
-
- /*
* First let's find out if we are on an iLO2+ server. We will
* not run on a legacy ASM box.
* So we only support the G5 ProLiant servers and higher.
@@ -842,6 +368,7 @@
retval = -ENOMEM;
goto error_pci_iomap;
}
+ hpwdt_nmistat = pci_mem_addr + 0x6e;
hpwdt_timer_reg = pci_mem_addr + 0x70;
hpwdt_timer_con = pci_mem_addr + 0x72;
@@ -912,6 +439,6 @@
#ifdef CONFIG_HPWDT_NMI_DECODING
module_param(allow_kdump, int, 0);
MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs");
-#endif /* !CONFIG_HPWDT_NMI_DECODING */
+#endif /* CONFIG_HPWDT_NMI_DECODING */
module_pci_driver(hpwdt_driver);
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index 32930a0..977fe74 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -760,6 +760,7 @@
{
struct watchdog_core_data *wd_data;
struct watchdog_device *wdd;
+ bool hw_running;
int err;
/* Get the corresponding watchdog device */
@@ -779,7 +780,8 @@
* If the /dev/watchdog device is open, we don't want the module
* to be unloaded.
*/
- if (!watchdog_hw_running(wdd) && !try_module_get(wdd->ops->owner)) {
+ hw_running = watchdog_hw_running(wdd);
+ if (!hw_running && !try_module_get(wdd->ops->owner)) {
err = -EBUSY;
goto out_clear;
}
@@ -790,7 +792,7 @@
file->private_data = wd_data;
- if (!watchdog_hw_running(wdd))
+ if (!hw_running)
kref_get(&wd_data->kref);
/* dev/watchdog is a virtual (and thus non-seekable) filesystem */
diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c
index 6d1fbda..0da9943 100644
--- a/drivers/watchdog/wdat_wdt.c
+++ b/drivers/watchdog/wdat_wdt.c
@@ -392,7 +392,7 @@
memset(&r, 0, sizeof(r));
r.start = gas->address;
- r.end = r.start + gas->access_width;
+ r.end = r.start + gas->access_width - 1;
if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
r.flags = IORESOURCE_MEM;
} else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index 8feab810..7bd882d 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -7,8 +7,10 @@
nostackp := $(call cc-option, -fno-stack-protector)
CFLAGS_features.o := $(nostackp)
+ifndef CONFIG_LTO_CLANG
CFLAGS_efi.o += -fshort-wchar
LDFLAGS += $(call ld-option, --no-wchar-size-warning)
+endif
dom0-$(CONFIG_ARM64) += arm-device.o
dom0-$(CONFIG_PCI) += pci.o
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index 79b8ab4..910b5d4 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -378,10 +378,8 @@
}
range = 0;
while (range < pages) {
- if (map->unmap_ops[offset+range].handle == -1) {
- range--;
+ if (map->unmap_ops[offset+range].handle == -1)
break;
- }
range++;
}
err = __unmap_grant_pages(map, offset, range);
@@ -1079,8 +1077,10 @@
out_unlock_put:
mutex_unlock(&priv->lock);
out_put_map:
- if (use_ptemod)
+ if (use_ptemod) {
map->vma = NULL;
+ unmap_grant_pages(map, 0, map->count);
+ }
gntdev_put_map(priv, map);
return err;
}
diff --git a/fs/afs/file.c b/fs/afs/file.c
index 7237297..37f97ba 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -123,11 +123,10 @@
/*
* read page from file, directory or symlink, given a key to use
*/
-int afs_page_filler(void *data, struct page *page)
+static int __afs_page_filler(struct key *key, struct page *page)
{
struct inode *inode = page->mapping->host;
struct afs_vnode *vnode = AFS_FS_I(inode);
- struct key *key = data;
size_t len;
off_t offset;
int ret;
@@ -209,6 +208,13 @@
return ret;
}
+int afs_page_filler(struct file *data, struct page *page)
+{
+ struct key *key = (struct key *)data;
+
+ return __afs_page_filler(key, page);
+}
+
/*
* read page from file, directory or symlink, given a file to nominate the key
* to be used
@@ -221,14 +227,14 @@
if (file) {
key = file->private_data;
ASSERT(key != NULL);
- ret = afs_page_filler(key, page);
+ ret = __afs_page_filler(key, page);
} else {
struct inode *inode = page->mapping->host;
key = afs_request_key(AFS_FS_S(inode->i_sb)->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
} else {
- ret = afs_page_filler(key, page);
+ ret = __afs_page_filler(key, page);
key_put(key);
}
}
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index dd98dcd..b4165cc 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -497,7 +497,7 @@
extern int afs_open(struct inode *, struct file *);
extern int afs_release(struct inode *, struct file *);
-extern int afs_page_filler(void *, struct page *);
+extern int afs_page_filler(struct file *, struct page *);
/*
* flock.c
diff --git a/fs/aio.c b/fs/aio.c
index 0fcb49a..0606f03 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -68,9 +68,9 @@
#define AIO_RING_PAGES 8
struct kioctx_table {
- struct rcu_head rcu;
- unsigned nr;
- struct kioctx *table[];
+ struct rcu_head rcu;
+ unsigned nr;
+ struct kioctx __rcu *table[];
};
struct kioctx_cpu {
@@ -115,7 +115,8 @@
struct page **ring_pages;
long nr_pages;
- struct work_struct free_work;
+ struct rcu_head free_rcu;
+ struct work_struct free_work; /* see free_ioctx() */
/*
* signals when all in-flight requests are done
@@ -329,7 +330,7 @@
for (i = 0; i < table->nr; i++) {
struct kioctx *ctx;
- ctx = table->table[i];
+ ctx = rcu_dereference(table->table[i]);
if (ctx && ctx->aio_ring_file == file) {
if (!atomic_read(&ctx->dead)) {
ctx->user_id = ctx->mmap_base = vma->vm_start;
@@ -581,6 +582,12 @@
return cancel(&kiocb->common);
}
+/*
+ * free_ioctx() should be RCU delayed to synchronize against the RCU
+ * protected lookup_ioctx() and also needs process context to call
+ * aio_free_ring(), so the double bouncing through kioctx->free_rcu and
+ * ->free_work.
+ */
static void free_ioctx(struct work_struct *work)
{
struct kioctx *ctx = container_of(work, struct kioctx, free_work);
@@ -594,6 +601,14 @@
kmem_cache_free(kioctx_cachep, ctx);
}
+static void free_ioctx_rcufn(struct rcu_head *head)
+{
+ struct kioctx *ctx = container_of(head, struct kioctx, free_rcu);
+
+ INIT_WORK(&ctx->free_work, free_ioctx);
+ schedule_work(&ctx->free_work);
+}
+
static void free_ioctx_reqs(struct percpu_ref *ref)
{
struct kioctx *ctx = container_of(ref, struct kioctx, reqs);
@@ -602,8 +617,8 @@
if (ctx->rq_wait && atomic_dec_and_test(&ctx->rq_wait->count))
complete(&ctx->rq_wait->comp);
- INIT_WORK(&ctx->free_work, free_ioctx);
- schedule_work(&ctx->free_work);
+ /* Synchronize against RCU protected table->table[] dereferences */
+ call_rcu(&ctx->free_rcu, free_ioctx_rcufn);
}
/*
@@ -644,9 +659,9 @@
while (1) {
if (table)
for (i = 0; i < table->nr; i++)
- if (!table->table[i]) {
+ if (!rcu_access_pointer(table->table[i])) {
ctx->id = i;
- table->table[i] = ctx;
+ rcu_assign_pointer(table->table[i], ctx);
spin_unlock(&mm->ioctx_lock);
/* While kioctx setup is in progress,
@@ -821,11 +836,11 @@
}
table = rcu_dereference_raw(mm->ioctx_table);
- WARN_ON(ctx != table->table[ctx->id]);
- table->table[ctx->id] = NULL;
+ WARN_ON(ctx != rcu_access_pointer(table->table[ctx->id]));
+ RCU_INIT_POINTER(table->table[ctx->id], NULL);
spin_unlock(&mm->ioctx_lock);
- /* percpu_ref_kill() will do the necessary call_rcu() */
+ /* free_ioctx_reqs() will do the necessary RCU synchronization */
wake_up_all(&ctx->wait);
/*
@@ -867,7 +882,8 @@
skipped = 0;
for (i = 0; i < table->nr; ++i) {
- struct kioctx *ctx = table->table[i];
+ struct kioctx *ctx =
+ rcu_dereference_protected(table->table[i], true);
if (!ctx) {
skipped++;
@@ -1056,7 +1072,7 @@
if (!table || id >= table->nr)
goto out;
- ctx = table->table[id];
+ ctx = rcu_dereference(table->table[id]);
if (ctx && ctx->user_id == ctx_id) {
percpu_ref_get(&ctx->users);
ret = ctx;
diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c
index 8d8370d..1ba49eb 100644
--- a/fs/btrfs/acl.c
+++ b/fs/btrfs/acl.c
@@ -114,13 +114,17 @@
int btrfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
{
int ret;
+ umode_t old_mode = inode->i_mode;
if (type == ACL_TYPE_ACCESS && acl) {
ret = posix_acl_update_mode(inode, &inode->i_mode, &acl);
if (ret)
return ret;
}
- return __btrfs_set_acl(NULL, inode, acl, type);
+ ret = __btrfs_set_acl(NULL, inode, acl, type);
+ if (ret)
+ inode->i_mode = old_mode;
+ return ret;
}
/*
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 3286a6e..c95ff09 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -2817,8 +2817,10 @@
}
ret = btrfs_qgroup_reserve_data(inode, cur_offset,
last_byte - cur_offset);
- if (ret < 0)
+ if (ret < 0) {
+ free_extent_map(em);
break;
+ }
} else {
/*
* Do not need to reserve unwritten extent for this
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index d196ce4..ffd5831 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -567,8 +567,10 @@
PAGE_SET_WRITEBACK |
page_error_op |
PAGE_END_WRITEBACK);
- btrfs_free_reserved_data_space_noquota(inode, start,
- end - start + 1);
+ if (ret == 0)
+ btrfs_free_reserved_data_space_noquota(inode,
+ start,
+ end - start + 1);
goto free_pages_out;
}
}
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index 9a47b55..d040afc 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -5156,13 +5156,19 @@
while (key.offset < ekey->offset + left_len) {
ei = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
right_type = btrfs_file_extent_type(eb, ei);
- if (right_type != BTRFS_FILE_EXTENT_REG) {
+ if (right_type != BTRFS_FILE_EXTENT_REG &&
+ right_type != BTRFS_FILE_EXTENT_INLINE) {
ret = 0;
goto out;
}
right_disknr = btrfs_file_extent_disk_bytenr(eb, ei);
- right_len = btrfs_file_extent_num_bytes(eb, ei);
+ if (right_type == BTRFS_FILE_EXTENT_INLINE) {
+ right_len = btrfs_file_extent_inline_len(eb, slot, ei);
+ right_len = PAGE_ALIGN(right_len);
+ } else {
+ right_len = btrfs_file_extent_num_bytes(eb, ei);
+ }
right_offset = btrfs_file_extent_offset(eb, ei);
right_gen = btrfs_file_extent_generation(eb, ei);
@@ -5176,6 +5182,19 @@
goto out;
}
+ /*
+ * We just wanted to see if when we have an inline extent, what
+ * follows it is a regular extent (wanted to check the above
+ * condition for inline extents too). This should normally not
+ * happen but it's possible for example when we have an inline
+ * compressed extent representing data with a size matching
+ * the page size (currently the same as sector size).
+ */
+ if (right_type == BTRFS_FILE_EXTENT_INLINE) {
+ ret = 0;
+ goto out;
+ }
+
left_offset_fixed = left_offset;
if (key.offset < ekey->offset) {
/* Fix the right offset for 2a and 7. */
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index fad7b37..39b917b7 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -583,6 +583,7 @@
btrfs_sysfs_remove_fsid(fs_devs);
list_del(&fs_devs->list);
free_fs_devices(fs_devs);
+ break;
} else {
fs_devs->num_devices--;
list_del(&dev->dev_list);
@@ -3764,6 +3765,7 @@
struct btrfs_ioctl_balance_args *bargs)
{
struct btrfs_fs_info *fs_info = bctl->fs_info;
+ u64 meta_target, data_target;
u64 allowed;
int mixed = 0;
int ret;
@@ -3860,11 +3862,16 @@
}
} while (read_seqretry(&fs_info->profiles_lock, seq));
- if (btrfs_get_num_tolerated_disk_barrier_failures(bctl->meta.target) <
- btrfs_get_num_tolerated_disk_barrier_failures(bctl->data.target)) {
+ /* if we're not converting, the target field is uninitialized */
+ meta_target = (bctl->meta.flags & BTRFS_BALANCE_ARGS_CONVERT) ?
+ bctl->meta.target : fs_info->avail_metadata_alloc_bits;
+ data_target = (bctl->data.flags & BTRFS_BALANCE_ARGS_CONVERT) ?
+ bctl->data.target : fs_info->avail_data_alloc_bits;
+ if (btrfs_get_num_tolerated_disk_barrier_failures(meta_target) <
+ btrfs_get_num_tolerated_disk_barrier_failures(data_target)) {
btrfs_warn(fs_info,
"metadata profile 0x%llx has lower redundancy than data profile 0x%llx",
- bctl->meta.target, bctl->data.target);
+ meta_target, data_target);
}
if (bctl->sys.flags & BTRFS_BALANCE_ARGS_CONVERT) {
@@ -4748,10 +4755,13 @@
if (devs_max && ndevs > devs_max)
ndevs = devs_max;
/*
- * the primary goal is to maximize the number of stripes, so use as many
- * devices as possible, even if the stripes are not maximum sized.
+ * The primary goal is to maximize the number of stripes, so use as
+ * many devices as possible, even if the stripes are not maximum sized.
+ *
+ * The DUP profile stores more than one stripe per device, the
+ * max_avail is the total size so we have to adjust.
*/
- stripe_size = devices_info[ndevs-1].max_avail;
+ stripe_size = div_u64(devices_info[ndevs - 1].max_avail, dev_stripes);
num_stripes = ndevs * dev_stripes;
/*
@@ -4791,8 +4801,6 @@
stripe_size = devices_info[ndevs-1].max_avail;
}
- stripe_size = div_u64(stripe_size, dev_stripes);
-
/* align to BTRFS_STRIPE_LEN */
stripe_size = div_u64(stripe_size, raid_stripe_len);
stripe_size *= raid_stripe_len;
diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c
index abae6dd..cc88f4f 100644
--- a/fs/cifs/netmisc.c
+++ b/fs/cifs/netmisc.c
@@ -980,10 +980,10 @@
cifs_dbg(VFS, "illegal hours %d\n", st->Hours);
days = sd->Day;
month = sd->Month;
- if ((days > 31) || (month > 12)) {
+ if (days < 1 || days > 31 || month < 1 || month > 12) {
cifs_dbg(VFS, "illegal date, month %d day: %d\n", month, days);
- if (month > 12)
- month = 12;
+ days = clamp(days, 1, 31);
+ month = clamp(month, 1, 12);
}
month -= 1;
days += total_days_of_prev_months[month];
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index 538d9b5..c3db2a8 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -344,13 +344,12 @@
/* BB is NTLMV2 session security format easier to use here? */
flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET |
NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
- NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC;
- if (ses->server->sign) {
+ NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
+ NTLMSSP_NEGOTIATE_SEAL;
+ if (ses->server->sign)
flags |= NTLMSSP_NEGOTIATE_SIGN;
- if (!ses->server->session_estab ||
- ses->ntlmssp->sesskey_per_smbsess)
- flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
- }
+ if (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
+ flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
sec_blob->NegotiateFlags = cpu_to_le32(flags);
@@ -407,13 +406,12 @@
flags = NTLMSSP_NEGOTIATE_56 |
NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_TARGET_INFO |
NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
- NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC;
- if (ses->server->sign) {
+ NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
+ NTLMSSP_NEGOTIATE_SEAL;
+ if (ses->server->sign)
flags |= NTLMSSP_NEGOTIATE_SIGN;
- if (!ses->server->session_estab ||
- ses->ntlmssp->sesskey_per_smbsess)
- flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
- }
+ if (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
+ flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE);
sec_blob->NegotiateFlags = cpu_to_le32(flags);
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 94c4c19..7c26286 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -707,15 +707,13 @@
struct cifs_ses *ses = sess_data->ses;
mutex_lock(&ses->server->srv_mutex);
- if (ses->server->sign && ses->server->ops->generate_signingkey) {
+ if (ses->server->ops->generate_signingkey) {
rc = ses->server->ops->generate_signingkey(ses);
- kfree(ses->auth_key.response);
- ses->auth_key.response = NULL;
if (rc) {
cifs_dbg(FYI,
"SMB3 session key generation failed\n");
mutex_unlock(&ses->server->srv_mutex);
- goto keygen_exit;
+ return rc;
}
}
if (!ses->server->session_estab) {
@@ -729,12 +727,6 @@
ses->status = CifsGood;
ses->need_reconnect = false;
spin_unlock(&GlobalMid_Lock);
-
-keygen_exit:
- if (!ses->server->sign) {
- kfree(ses->auth_key.response);
- ses->auth_key.response = NULL;
- }
return rc;
}
@@ -1712,6 +1704,9 @@
} else
iov[0].iov_len = get_rfc1002_length(req) + 4;
+ /* validate negotiate request must be signed - see MS-SMB2 3.2.5.5 */
+ if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO)
+ req->hdr.Flags |= SMB2_FLAGS_SIGNED;
rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base;
diff --git a/fs/dax.c b/fs/dax.c
index 800748f..71f87d7 100644
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -785,6 +785,7 @@
if (ret < 0)
return ret;
}
+ start_index = indices[pvec.nr - 1] + 1;
}
return 0;
}
diff --git a/fs/dcache.c b/fs/dcache.c
index 227a4f9..210a135 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -637,11 +637,16 @@
spin_unlock(&parent->d_lock);
goto again;
}
- rcu_read_unlock();
- if (parent != dentry)
+ if (parent != dentry) {
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
- else
+ if (unlikely(dentry->d_lockref.count < 0)) {
+ spin_unlock(&parent->d_lock);
+ parent = NULL;
+ }
+ } else {
parent = NULL;
+ }
+ rcu_read_unlock();
return parent;
}
diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c
index d8072bc..7f5b735 100644
--- a/fs/exofs/inode.c
+++ b/fs/exofs/inode.c
@@ -377,9 +377,8 @@
* and will start a new collection. Eventually caller must submit the last
* segment if present.
*/
-static int readpage_strip(void *data, struct page *page)
+static int __readpage_strip(struct page_collect *pcol, struct page *page)
{
- struct page_collect *pcol = data;
struct inode *inode = pcol->inode;
struct exofs_i_info *oi = exofs_i(inode);
loff_t i_size = i_size_read(inode);
@@ -470,6 +469,13 @@
return ret;
}
+static int readpage_strip(struct file *data, struct page *page)
+{
+ struct page_collect *pcol = (struct page_collect *)data;
+
+ return __readpage_strip(pcol, page);
+}
+
static int exofs_readpages(struct file *file, struct address_space *mapping,
struct list_head *pages, unsigned nr_pages)
{
@@ -499,7 +505,7 @@
_pcol_init(&pcol, 1, page->mapping->host);
pcol.read_4_write = read_4_write;
- ret = readpage_strip(&pcol, page);
+ ret = __readpage_strip(&pcol, page);
if (ret) {
EXOFS_ERR("_readpage => %d\n", ret);
return ret;
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 3eeed8f..3fadfab 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -837,8 +837,6 @@
if (!IS_LAST_ENTRY(s->first))
ext4_xattr_rehash(header(s->base),
s->here);
- ext4_xattr_cache_insert(ext4_mb_cache,
- bs->bh);
}
ext4_xattr_block_csum_set(inode, bs->bh);
unlock_buffer(bs->bh);
@@ -959,6 +957,7 @@
} else if (bs->bh && s->base == bs->bh->b_data) {
/* We were modifying this block in-place. */
ea_bdebug(bs->bh, "keeping this block");
+ ext4_xattr_cache_insert(ext4_mb_cache, bs->bh);
new_bh = bs->bh;
get_bh(new_bh);
} else {
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index 6ef3c83..a352ace5 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -94,14 +94,12 @@
struct f2fs_dir_entry *de;
struct f2fs_dentry_ptr d;
- dentry_blk = (struct f2fs_dentry_block *)kmap(dentry_page);
+ dentry_blk = (struct f2fs_dentry_block *)page_address(dentry_page);
make_dentry_ptr_block(NULL, &d, dentry_blk);
de = find_target_dentry(fname, namehash, max_slots, &d);
if (de)
*res_page = dentry_page;
- else
- kunmap(dentry_page);
return de;
}
@@ -287,7 +285,6 @@
de = f2fs_find_entry(dir, qstr, page);
if (de) {
res = le32_to_cpu(de->ino);
- f2fs_dentry_kunmap(dir, *page);
f2fs_put_page(*page, 0);
}
@@ -302,7 +299,6 @@
f2fs_wait_on_page_writeback(page, type, true);
de->ino = cpu_to_le32(inode->i_ino);
set_de_type(de, inode->i_mode);
- f2fs_dentry_kunmap(dir, page);
set_page_dirty(page);
dir->i_mtime = dir->i_ctime = current_time(dir);
@@ -350,13 +346,11 @@
if (IS_ERR(dentry_page))
return PTR_ERR(dentry_page);
- dentry_blk = kmap_atomic(dentry_page);
+ dentry_blk = page_address(dentry_page);
make_dentry_ptr_block(NULL, &d, dentry_blk);
do_make_empty_dir(inode, parent, &d);
- kunmap_atomic(dentry_blk);
-
set_page_dirty(dentry_page);
f2fs_put_page(dentry_page, 1);
return 0;
@@ -547,13 +541,12 @@
if (IS_ERR(dentry_page))
return PTR_ERR(dentry_page);
- dentry_blk = kmap(dentry_page);
+ dentry_blk = page_address(dentry_page);
bit_pos = room_for_filename(&dentry_blk->dentry_bitmap,
slots, NR_DENTRY_IN_BLOCK);
if (bit_pos < NR_DENTRY_IN_BLOCK)
goto add_dentry;
- kunmap(dentry_page);
f2fs_put_page(dentry_page, 1);
}
@@ -588,7 +581,6 @@
if (inode)
up_write(&F2FS_I(inode)->i_sem);
- kunmap(dentry_page);
f2fs_put_page(dentry_page, 1);
return err;
@@ -642,7 +634,6 @@
F2FS_I(dir)->task = NULL;
}
if (de) {
- f2fs_dentry_kunmap(dir, page);
f2fs_put_page(page, 0);
err = -EEXIST;
} else if (IS_ERR(page)) {
@@ -730,7 +721,6 @@
bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap,
NR_DENTRY_IN_BLOCK,
0);
- kunmap(page); /* kunmap - pair of f2fs_find_entry */
set_page_dirty(page);
dir->i_ctime = dir->i_mtime = current_time(dir);
@@ -775,7 +765,7 @@
return false;
}
- dentry_blk = kmap_atomic(dentry_page);
+ dentry_blk = page_address(dentry_page);
if (bidx == 0)
bit_pos = 2;
else
@@ -783,7 +773,6 @@
bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap,
NR_DENTRY_IN_BLOCK,
bit_pos);
- kunmap_atomic(dentry_blk);
f2fs_put_page(dentry_page, 1);
@@ -901,19 +890,17 @@
}
}
- dentry_blk = kmap(dentry_page);
+ dentry_blk = page_address(dentry_page);
make_dentry_ptr_block(inode, &d, dentry_blk);
err = f2fs_fill_dentries(ctx, &d,
n * NR_DENTRY_IN_BLOCK, &fstr);
if (err) {
- kunmap(dentry_page);
f2fs_put_page(dentry_page, 1);
break;
}
- kunmap(dentry_page);
f2fs_put_page(dentry_page, 1);
}
out_free:
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index a8434af..e5d3e22 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -2397,12 +2397,6 @@
return is_inode_flag_set(inode, FI_INLINE_DENTRY);
}
-static inline void f2fs_dentry_kunmap(struct inode *dir, struct page *page)
-{
- if (!f2fs_has_inline_dentry(dir))
- kunmap(page);
-}
-
static inline int is_file(struct inode *inode, int type)
{
return F2FS_I(inode)->i_advise & type;
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index 364114a..1925814 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -387,7 +387,7 @@
f2fs_wait_on_page_writeback(page, DATA, true);
zero_user_segment(page, MAX_INLINE_DATA(dir), PAGE_SIZE);
- dentry_blk = kmap_atomic(page);
+ dentry_blk = page_address(page);
make_dentry_ptr_inline(dir, &src, inline_dentry);
make_dentry_ptr_block(dir, &dst, dentry_blk);
@@ -404,7 +404,6 @@
memcpy(dst.dentry, src.dentry, SIZE_OF_DIR_ENTRY * src.max);
memcpy(dst.filename, src.filename, src.max * F2FS_SLOT_LEN);
- kunmap_atomic(dentry_blk);
if (!PageUptodate(page))
SetPageUptodate(page);
set_page_dirty(page);
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 89c838b..10be247 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -328,7 +328,7 @@
inode->i_op = &f2fs_dir_inode_operations;
inode->i_fop = &f2fs_dir_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops;
- mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO);
+ inode_nohighmem(inode);
} else if (S_ISLNK(inode->i_mode)) {
if (f2fs_encrypted_inode(inode))
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index 070b3a7..59e8dca 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -317,7 +317,6 @@
de = f2fs_find_entry(dir, &dot, &page);
if (de) {
- f2fs_dentry_kunmap(dir, page);
f2fs_put_page(page, 0);
} else if (IS_ERR(page)) {
err = PTR_ERR(page);
@@ -329,14 +328,12 @@
}
de = f2fs_find_entry(dir, &dotdot, &page);
- if (de) {
- f2fs_dentry_kunmap(dir, page);
+ if (de)
f2fs_put_page(page, 0);
- } else if (IS_ERR(page)) {
+ else if (IS_ERR(page))
err = PTR_ERR(page);
- } else {
+ else
err = __f2fs_add_link(dir, &dotdot, NULL, pino, S_IFDIR);
- }
out:
if (!err)
clear_inode_flag(dir, FI_INLINE_DOTS);
@@ -377,7 +374,6 @@
}
ino = le32_to_cpu(de->ino);
- f2fs_dentry_kunmap(dir, page);
f2fs_put_page(page, 0);
inode = f2fs_iget(dir->i_sb, ino);
@@ -452,7 +448,6 @@
err = acquire_orphan_inode(sbi);
if (err) {
f2fs_unlock_op(sbi);
- f2fs_dentry_kunmap(dir, page);
f2fs_put_page(page, 0);
goto fail;
}
@@ -613,7 +608,7 @@
inode->i_op = &f2fs_dir_inode_operations;
inode->i_fop = &f2fs_dir_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops;
- mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO);
+ inode_nohighmem(inode);
set_inode_flag(inode, FI_INC_LINK);
f2fs_lock_op(sbi);
@@ -927,13 +922,11 @@
}
if (old_dir_entry) {
- if (old_dir != new_dir && !whiteout) {
+ if (old_dir != new_dir && !whiteout)
f2fs_set_link(old_inode, old_dir_entry,
old_dir_page, new_dir);
- } else {
- f2fs_dentry_kunmap(old_inode, old_dir_page);
+ else
f2fs_put_page(old_dir_page, 0);
- }
f2fs_i_links_write(old_dir, false);
}
add_ino_entry(sbi, new_dir->i_ino, TRANS_DIR_INO);
@@ -946,20 +939,15 @@
put_out_dir:
f2fs_unlock_op(sbi);
- if (new_page) {
- f2fs_dentry_kunmap(new_dir, new_page);
+ if (new_page)
f2fs_put_page(new_page, 0);
- }
out_whiteout:
if (whiteout)
iput(whiteout);
out_dir:
- if (old_dir_entry) {
- f2fs_dentry_kunmap(old_inode, old_dir_page);
+ if (old_dir_entry)
f2fs_put_page(old_dir_page, 0);
- }
out_old:
- f2fs_dentry_kunmap(old_dir, old_page);
f2fs_put_page(old_page, 0);
out:
return err;
@@ -1101,19 +1089,15 @@
return 0;
out_new_dir:
if (new_dir_entry) {
- f2fs_dentry_kunmap(new_inode, new_dir_page);
f2fs_put_page(new_dir_page, 0);
}
out_old_dir:
if (old_dir_entry) {
- f2fs_dentry_kunmap(old_inode, old_dir_page);
f2fs_put_page(old_dir_page, 0);
}
out_new:
- f2fs_dentry_kunmap(new_dir, new_page);
f2fs_put_page(new_page, 0);
out_old:
- f2fs_dentry_kunmap(old_dir, old_page);
f2fs_put_page(old_page, 0);
out:
return err;
diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c
index b6d1ec6..210de28 100644
--- a/fs/f2fs/recovery.c
+++ b/fs/f2fs/recovery.c
@@ -144,7 +144,7 @@
retry:
de = __f2fs_find_entry(dir, &fname, &page);
if (de && inode->i_ino == le32_to_cpu(de->ino))
- goto out_unmap_put;
+ goto out_put;
if (de) {
einode = f2fs_iget_retry(inode->i_sb, le32_to_cpu(de->ino));
@@ -153,19 +153,19 @@
err = PTR_ERR(einode);
if (err == -ENOENT)
err = -EEXIST;
- goto out_unmap_put;
+ goto out_put;
}
err = dquot_initialize(einode);
if (err) {
iput(einode);
- goto out_unmap_put;
+ goto out_put;
}
err = acquire_orphan_inode(F2FS_I_SB(inode));
if (err) {
iput(einode);
- goto out_unmap_put;
+ goto out_put;
}
f2fs_delete_entry(de, page, dir, einode);
iput(einode);
@@ -180,8 +180,7 @@
goto retry;
goto out;
-out_unmap_put:
- f2fs_dentry_kunmap(dir, page);
+out_put:
f2fs_put_page(page, 0);
out:
if (file_enc_name(inode))
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 21d829b..7adf871 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -857,9 +857,9 @@
unsigned nr_pages;
};
-static int fuse_readpages_fill(void *_data, struct page *page)
+static int fuse_readpages_fill(struct file *_data, struct page *page)
{
- struct fuse_fill_data *data = _data;
+ struct fuse_fill_data *data = (struct fuse_fill_data *)_data;
struct fuse_req *req = data->req;
struct inode *inode = data->inode;
struct fuse_conn *fc = get_fuse_conn(inode);
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index f1512c8..8aa98b1 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -30,7 +30,7 @@
struct list_head fuse_conn_list;
DEFINE_MUTEX(fuse_mutex);
-static int set_global_limit(const char *val, struct kernel_param *kp);
+static int set_global_limit(const char *val, const struct kernel_param *kp);
unsigned max_user_bgreq;
module_param_call(max_user_bgreq, set_global_limit, param_get_uint,
@@ -827,7 +827,7 @@
*limit = (1 << 16) - 1;
}
-static int set_global_limit(const char *val, struct kernel_param *kp)
+static int set_global_limit(const char *val, const struct kernel_param *kp)
{
int rv;
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index 5a6f52e..c6c507c 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -511,7 +511,7 @@
*
*/
-static int __gfs2_readpage(void *file, struct page *page)
+static int __gfs2_readpage(struct file *file, struct page *page)
{
struct gfs2_inode *ip = GFS2_I(page->mapping->host);
struct gfs2_sbd *sdp = GFS2_SB(page->mapping->host);
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index 6cb3a8c..f06c39c 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -691,8 +691,21 @@
{
int err = 0;
- jbd2_might_wait_for_commit(journal);
read_lock(&journal->j_state_lock);
+#ifdef CONFIG_PROVE_LOCKING
+ /*
+ * Some callers make sure transaction is already committing and in that
+ * case we cannot block on open handles anymore. So don't warn in that
+ * case.
+ */
+ if (tid_gt(tid, journal->j_commit_sequence) &&
+ (!journal->j_committing_transaction ||
+ journal->j_committing_transaction->t_tid != tid)) {
+ read_unlock(&journal->j_state_lock);
+ jbd2_might_wait_for_commit(journal);
+ read_lock(&journal->j_state_lock);
+ }
+#endif
#ifdef CONFIG_JBD2_DEBUG
if (!tid_geq(journal->j_commit_request, tid)) {
printk(KERN_ERR
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index 9d37324..a770da3 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -596,7 +596,7 @@
*/
#define param_set_min_max(name, type, which_strtol, min, max) \
-static int param_set_##name(const char *val, struct kernel_param *kp) \
+static int param_set_##name(const char *val, const struct kernel_param *kp) \
{ \
char *endp; \
__typeof__(type) num = which_strtol(val, &endp, 0); \
diff --git a/fs/logfs/dev_bdev.c b/fs/logfs/dev_bdev.c
index a8329cc..fdf844f 100644
--- a/fs/logfs/dev_bdev.c
+++ b/fs/logfs/dev_bdev.c
@@ -34,9 +34,9 @@
return submit_bio_wait(&bio);
}
-static int bdev_readpage(void *_sb, struct page *page)
+static int bdev_readpage(struct file *_sb, struct page *page)
{
- struct super_block *sb = _sb;
+ struct super_block *sb = (struct super_block *)_sb;
struct block_device *bdev = logfs_super(sb)->s_bdev;
int err;
diff --git a/fs/logfs/dev_mtd.c b/fs/logfs/dev_mtd.c
index b76a62b..9ec8e8f 100644
--- a/fs/logfs/dev_mtd.c
+++ b/fs/logfs/dev_mtd.c
@@ -122,9 +122,9 @@
mtd_sync(mtd);
}
-static int logfs_mtd_readpage(void *_sb, struct page *page)
+static int logfs_mtd_readpage(struct file *_sb, struct page *page)
{
- struct super_block *sb = _sb;
+ struct super_block *sb = (struct super_block *)_sb;
int err;
err = logfs_mtd_read(sb, page->index << PAGE_SHIFT, PAGE_SIZE,
diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c
index c87ea52..8ddacc1 100644
--- a/fs/logfs/dir.c
+++ b/fs/logfs/dir.c
@@ -174,7 +174,7 @@
if (!logfs_exist_block(dir, index))
continue;
page = read_cache_page(dir->i_mapping, index,
- (filler_t *)logfs_readpage, NULL);
+ logfs_readpage, NULL);
if (IS_ERR(page))
return page;
dd = kmap_atomic(page);
@@ -306,7 +306,7 @@
continue;
}
page = read_cache_page(dir->i_mapping, pos,
- (filler_t *)logfs_readpage, NULL);
+ logfs_readpage, NULL);
if (IS_ERR(page))
return PTR_ERR(page);
dd = kmap(page);
diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h
index 27d040e..1a7c0b0 100644
--- a/fs/logfs/logfs.h
+++ b/fs/logfs/logfs.h
@@ -151,7 +151,7 @@
struct page *(*find_first_sb)(struct super_block *sb, u64 *ofs);
struct page *(*find_last_sb)(struct super_block *sb, u64 *ofs);
int (*write_sb)(struct super_block *sb, struct page *page);
- int (*readpage)(void *_sb, struct page *page);
+ int (*readpage)(struct file *_sb, struct page *page);
void (*writeseg)(struct super_block *sb, u64 ofs, size_t len);
int (*erase)(struct super_block *sb, loff_t ofs, size_t len,
int ensure_write);
diff --git a/fs/namei.c b/fs/namei.c
index 339f7c7..02fc285 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -593,9 +593,10 @@
static bool path_connected(const struct path *path)
{
struct vfsmount *mnt = path->mnt;
+ struct super_block *sb = mnt->mnt_sb;
- /* Only bind mounts can have disconnected paths */
- if (mnt->mnt_root == mnt->mnt_sb->s_root)
+ /* Bind mounts and multi-root filesystems can have disconnected paths */
+ if (!(sb->s_iflags & SB_I_MULTIROOT) && (mnt->mnt_root == sb->s_root))
return true;
return is_subdir(path->dentry, mnt->mnt_root);
@@ -1136,9 +1137,6 @@
path->dentry->d_inode)
return -EISDIR;
- if (path->dentry->d_sb->s_user_ns != &init_user_ns)
- return -EACCES;
-
nd->total_link_count++;
if (nd->total_link_count >= 40)
return -ELOOP;
diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c
index 88dbbc9..f571570 100644
--- a/fs/ncpfs/ncplib_kernel.c
+++ b/fs/ncpfs/ncplib_kernel.c
@@ -980,6 +980,10 @@
goto out;
}
*bytes_read = ncp_reply_be16(server, 0);
+ if (*bytes_read > to_read) {
+ result = -EINVAL;
+ goto out;
+ }
source = ncp_reply_data(server, 2 + (offset & 1));
memcpy(target, source, *bytes_read);
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 1ac1593..1ab9112 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -86,10 +86,10 @@
struct nfs_direct_mirror mirrors[NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX];
int mirror_count;
+ loff_t io_start; /* Start offset for I/O */
ssize_t count, /* bytes actually processed */
max_count, /* max expected count */
bytes_left, /* bytes left to be sent */
- io_start, /* start of IO */
error; /* any reported error */
struct completion completion; /* wait for i/o completion */
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index 3d17fc8..892c885 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -1262,8 +1262,10 @@
mirror = &desc->pg_mirrors[midx];
if (!list_empty(&mirror->pg_list)) {
prev = nfs_list_entry(mirror->pg_list.prev);
- if (index != prev->wb_index + 1)
- nfs_pageio_complete_mirror(desc, midx);
+ if (index != prev->wb_index + 1) {
+ nfs_pageio_complete(desc);
+ break;
+ }
}
}
}
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index b8e4474..0e008db 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -1953,8 +1953,6 @@
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()
@@ -2308,10 +2306,20 @@
enum pnfs_try_status trypnfs;
trypnfs = pnfs_try_to_read_data(hdr, call_ops, lseg);
- if (trypnfs == PNFS_TRY_AGAIN)
- pnfs_read_resend_pnfs(hdr);
- if (trypnfs == PNFS_NOT_ATTEMPTED || hdr->task.tk_status)
+ switch (trypnfs) {
+ case PNFS_NOT_ATTEMPTED:
pnfs_read_through_mds(desc, hdr);
+ case PNFS_ATTEMPTED:
+ break;
+ case PNFS_TRY_AGAIN:
+ /* cleanup hdr and prepare to redo pnfs */
+ if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) {
+ struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
+ list_splice_init(&hdr->pages, &mirror->pg_list);
+ mirror->pg_recoalesce = 1;
+ }
+ hdr->mds_ops->rpc_release(hdr);
+ }
}
static void pnfs_readhdr_free(struct nfs_pgio_header *hdr)
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index defc923..67b8000 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -346,7 +346,7 @@
};
static int
-readpage_async_filler(void *data, struct page *page)
+readpage_async_filler(struct file *data, struct page *page)
{
struct nfs_readdesc *desc = (struct nfs_readdesc *)data;
struct nfs_page *new;
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 51bf1f9..2fdb8f5 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -2613,6 +2613,8 @@
/* initial superblock/root creation */
mount_info->fill_super(s, mount_info);
nfs_get_cache_cookie(s, mount_info->parsed, mount_info->cloned);
+ if (!(server->flags & NFS_MOUNT_UNSHARED))
+ s->s_iflags |= SB_I_MULTIROOT;
}
mntroot = nfs_get_root(s, mount_info->mntfh, dev_name);
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 9a3b382..a8b786a 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -1847,40 +1847,43 @@
return status;
}
-int nfs_commit_inode(struct inode *inode, int how)
+static int __nfs_commit_inode(struct inode *inode, int how,
+ struct writeback_control *wbc)
{
LIST_HEAD(head);
struct nfs_commit_info cinfo;
int may_wait = how & FLUSH_SYNC;
- int error = 0;
- int res;
+ int ret, nscan;
nfs_init_cinfo_from_inode(&cinfo, inode);
nfs_commit_begin(cinfo.mds);
- res = nfs_scan_commit(inode, &head, &cinfo);
- if (res)
- error = nfs_generic_commit_list(inode, &head, how, &cinfo);
+ for (;;) {
+ ret = nscan = nfs_scan_commit(inode, &head, &cinfo);
+ if (ret <= 0)
+ break;
+ ret = nfs_generic_commit_list(inode, &head, how, &cinfo);
+ if (ret < 0)
+ break;
+ ret = 0;
+ if (wbc && wbc->sync_mode == WB_SYNC_NONE) {
+ if (nscan < wbc->nr_to_write)
+ wbc->nr_to_write -= nscan;
+ else
+ wbc->nr_to_write = 0;
+ }
+ if (nscan < INT_MAX)
+ break;
+ cond_resched();
+ }
nfs_commit_end(cinfo.mds);
- if (res == 0)
- return res;
- if (error < 0)
- goto out_error;
- if (!may_wait)
- goto out_mark_dirty;
- error = wait_on_commit(cinfo.mds);
- if (error < 0)
- return error;
- return res;
-out_error:
- res = error;
- /* Note: If we exit without ensuring that the commit is complete,
- * we must mark the inode as dirty. Otherwise, future calls to
- * sync_inode() with the WB_SYNC_ALL flag set will fail to ensure
- * that the data is on the disk.
- */
-out_mark_dirty:
- __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
- return res;
+ if (ret || !may_wait)
+ return ret;
+ return wait_on_commit(cinfo.mds);
+}
+
+int nfs_commit_inode(struct inode *inode, int how)
+{
+ return __nfs_commit_inode(inode, how, NULL);
}
EXPORT_SYMBOL_GPL(nfs_commit_inode);
@@ -1890,11 +1893,11 @@
int flags = FLUSH_SYNC;
int ret = 0;
- /* no commits means nothing needs to be done */
- if (!nfsi->commit_info.ncommit)
- return ret;
-
if (wbc->sync_mode == WB_SYNC_NONE) {
+ /* no commits means nothing needs to be done */
+ if (!nfsi->commit_info.ncommit)
+ goto check_requests_outstanding;
+
/* Don't commit yet if this is a non-blocking flush and there
* are a lot of outstanding writes for this mapping.
*/
@@ -1905,16 +1908,16 @@
flags = 0;
}
- ret = nfs_commit_inode(inode, flags);
- if (ret >= 0) {
- if (wbc->sync_mode == WB_SYNC_NONE) {
- if (ret < wbc->nr_to_write)
- wbc->nr_to_write -= ret;
- else
- wbc->nr_to_write = 0;
- }
- return 0;
- }
+ ret = __nfs_commit_inode(inode, flags, wbc);
+ if (!ret) {
+ if (flags & FLUSH_SYNC)
+ return 0;
+ } else if (nfsi->commit_info.ncommit)
+ goto out_mark_dirty;
+
+check_requests_outstanding:
+ if (!atomic_read(&nfsi->commit_info.rpcs_out))
+ return ret;
out_mark_dirty:
__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
return ret;
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 022d958..eef0caf 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1338,14 +1338,14 @@
const struct nfsd4_layout_ops *ops;
struct nfs4_layout_stateid *ls;
__be32 nfserr;
- int accmode;
+ int accmode = NFSD_MAY_READ_IF_EXEC;
switch (lgp->lg_seg.iomode) {
case IOMODE_READ:
- accmode = NFSD_MAY_READ;
+ accmode |= NFSD_MAY_READ;
break;
case IOMODE_RW:
- accmode = NFSD_MAY_READ | NFSD_MAY_WRITE;
+ accmode |= NFSD_MAY_READ | NFSD_MAY_WRITE;
break;
default:
dprintk("%s: invalid iomode %d\n",
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f463c4e..12d7807 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -263,6 +263,35 @@
kfree(nbl);
}
+static void
+remove_blocked_locks(struct nfs4_lockowner *lo)
+{
+ struct nfs4_client *clp = lo->lo_owner.so_client;
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+ struct nfsd4_blocked_lock *nbl;
+ LIST_HEAD(reaplist);
+
+ /* Dequeue all blocked locks */
+ spin_lock(&nn->blocked_locks_lock);
+ while (!list_empty(&lo->lo_blocked)) {
+ nbl = list_first_entry(&lo->lo_blocked,
+ struct nfsd4_blocked_lock,
+ nbl_list);
+ list_del_init(&nbl->nbl_list);
+ list_move(&nbl->nbl_lru, &reaplist);
+ }
+ spin_unlock(&nn->blocked_locks_lock);
+
+ /* Now free them */
+ while (!list_empty(&reaplist)) {
+ nbl = list_first_entry(&reaplist, struct nfsd4_blocked_lock,
+ nbl_lru);
+ list_del_init(&nbl->nbl_lru);
+ posix_unblock_lock(&nbl->nbl_lock);
+ free_blocked_lock(nbl);
+ }
+}
+
static int
nfsd4_cb_notify_lock_done(struct nfsd4_callback *cb, struct rpc_task *task)
{
@@ -1854,6 +1883,7 @@
static void
__destroy_client(struct nfs4_client *clp)
{
+ int i;
struct nfs4_openowner *oo;
struct nfs4_delegation *dp;
struct list_head reaplist;
@@ -1883,6 +1913,16 @@
nfs4_get_stateowner(&oo->oo_owner);
release_openowner(oo);
}
+ for (i = 0; i < OWNER_HASH_SIZE; i++) {
+ struct nfs4_stateowner *so, *tmp;
+
+ list_for_each_entry_safe(so, tmp, &clp->cl_ownerstr_hashtbl[i],
+ so_strhash) {
+ /* Should be no openowners at this point */
+ WARN_ON_ONCE(so->so_is_open_owner);
+ remove_blocked_locks(lockowner(so));
+ }
+ }
nfsd4_return_all_client_layouts(clp);
nfsd4_shutdown_callback(clp);
if (clp->cl_cb_conn.cb_xprt)
@@ -6266,6 +6306,7 @@
}
spin_unlock(&clp->cl_lock);
free_ol_stateid_reaplist(&reaplist);
+ remove_blocked_locks(lo);
nfs4_put_stateowner(&lo->lo_owner);
return status;
@@ -7051,6 +7092,8 @@
}
}
+ WARN_ON(!list_empty(&nn->blocked_locks_lru));
+
for (i = 0; i < CLIENT_HASH_SIZE; i++) {
while (!list_empty(&nn->unconf_id_hashtbl[i])) {
clp = list_entry(nn->unconf_id_hashtbl[i].next, struct nfs4_client, cl_idhash);
@@ -7117,7 +7160,6 @@
struct nfs4_delegation *dp = NULL;
struct list_head *pos, *next, reaplist;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- struct nfsd4_blocked_lock *nbl;
cancel_delayed_work_sync(&nn->laundromat_work);
locks_end_grace(&nn->nfsd4_manager);
@@ -7138,24 +7180,6 @@
nfs4_put_stid(&dp->dl_stid);
}
- BUG_ON(!list_empty(&reaplist));
- spin_lock(&nn->blocked_locks_lock);
- while (!list_empty(&nn->blocked_locks_lru)) {
- nbl = list_first_entry(&nn->blocked_locks_lru,
- struct nfsd4_blocked_lock, nbl_lru);
- list_move(&nbl->nbl_lru, &reaplist);
- list_del_init(&nbl->nbl_list);
- }
- spin_unlock(&nn->blocked_locks_lock);
-
- while (!list_empty(&reaplist)) {
- nbl = list_first_entry(&reaplist,
- struct nfsd4_blocked_lock, nbl_lru);
- list_del_init(&nbl->nbl_lru);
- posix_unblock_lock(&nbl->nbl_lock);
- free_blocked_lock(nbl);
- }
-
nfsd4_client_tracking_exit(net);
nfs4_state_destroy_net(net);
}
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index b829cc9..8f0b19a 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -94,6 +94,12 @@
err = follow_down(&path);
if (err < 0)
goto out;
+ if (path.mnt == exp->ex_path.mnt && path.dentry == dentry &&
+ nfsd_mountpoint(dentry, exp) == 2) {
+ /* This is only a mountpoint in some other namespace */
+ path_put(&path);
+ goto out;
+ }
exp2 = rqst_exp_get_by_name(rqstp, &path);
if (IS_ERR(exp2)) {
@@ -167,16 +173,26 @@
/*
* For nfsd purposes, we treat V4ROOT exports as though there was an
* export at *every* directory.
+ * We return:
+ * '1' if this dentry *must* be an export point,
+ * '2' if it might be, if there is really a mount here, and
+ * '0' if there is no chance of an export point here.
*/
int nfsd_mountpoint(struct dentry *dentry, struct svc_export *exp)
{
- if (d_mountpoint(dentry))
+ if (!d_inode(dentry))
+ return 0;
+ if (exp->ex_flags & NFSEXP_V4ROOT)
return 1;
if (nfsd4_is_junction(dentry))
return 1;
- if (!(exp->ex_flags & NFSEXP_V4ROOT))
- return 0;
- return d_inode(dentry) != NULL;
+ if (d_mountpoint(dentry))
+ /*
+ * Might only be a mountpoint in a different namespace,
+ * but we need to check.
+ */
+ return 2;
+ return 0;
}
__be32
diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c
index 1079fae..47e08e4 100644
--- a/fs/ocfs2/dlmfs/dlmfs.c
+++ b/fs/ocfs2/dlmfs/dlmfs.c
@@ -88,13 +88,13 @@
*/
#define DLMFS_CAPABILITIES "bast stackglue"
static int param_set_dlmfs_capabilities(const char *val,
- struct kernel_param *kp)
+ const struct kernel_param *kp)
{
printk(KERN_ERR "%s: readonly parameter\n", kp->name);
return -EINVAL;
}
static int param_get_dlmfs_capabilities(char *buffer,
- struct kernel_param *kp)
+ const struct kernel_param *kp)
{
return strlcpy(buffer, DLMFS_CAPABILITIES,
strlen(DLMFS_CAPABILITIES) + 1);
diff --git a/fs/orangefs/waitqueue.c b/fs/orangefs/waitqueue.c
index f61b008..cbca58b 100644
--- a/fs/orangefs/waitqueue.c
+++ b/fs/orangefs/waitqueue.c
@@ -124,7 +124,14 @@
gossip_debug(GOSSIP_WAIT_DEBUG,
"%s:client core is NOT in service.\n",
__func__);
- timeout = op_timeout_secs * HZ;
+ /*
+ * Don't wait for the userspace component to return if
+ * the filesystem is being umounted anyway.
+ */
+ if (op->upcall.type == ORANGEFS_VFS_OP_FS_UMOUNT)
+ timeout = 0;
+ else
+ timeout = op_timeout_secs * HZ;
}
spin_unlock(&orangefs_request_list_lock);
diff --git a/fs/read_write.c b/fs/read_write.c
index ba28059..2f1027d 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -23,9 +23,6 @@
#include <asm/uaccess.h>
#include <asm/unistd.h>
-typedef ssize_t (*io_fn_t)(struct file *, char __user *, size_t, loff_t *);
-typedef ssize_t (*iter_fn_t)(struct kiocb *, struct iov_iter *);
-
const struct file_operations generic_ro_fops = {
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
@@ -675,7 +672,7 @@
EXPORT_SYMBOL(iov_shorten);
static ssize_t do_iter_readv_writev(struct file *filp, struct iov_iter *iter,
- loff_t *ppos, iter_fn_t fn, int flags)
+ loff_t *ppos, int type, int flags)
{
struct kiocb kiocb;
ssize_t ret;
@@ -692,7 +689,10 @@
kiocb.ki_flags |= (IOCB_DSYNC | IOCB_SYNC);
kiocb.ki_pos = *ppos;
- ret = fn(&kiocb, iter);
+ if (type == READ)
+ ret = filp->f_op->read_iter(&kiocb, iter);
+ else
+ ret = filp->f_op->write_iter(&kiocb, iter);
BUG_ON(ret == -EIOCBQUEUED);
*ppos = kiocb.ki_pos;
return ret;
@@ -700,7 +700,7 @@
/* Do it by hand, with file-ops */
static ssize_t do_loop_readv_writev(struct file *filp, struct iov_iter *iter,
- loff_t *ppos, io_fn_t fn, int flags)
+ loff_t *ppos, int type, int flags)
{
ssize_t ret = 0;
@@ -711,7 +711,13 @@
struct iovec iovec = iov_iter_iovec(iter);
ssize_t nr;
- nr = fn(filp, iovec.iov_base, iovec.iov_len, ppos);
+ if (type == READ) {
+ nr = filp->f_op->read(filp, iovec.iov_base,
+ iovec.iov_len, ppos);
+ } else {
+ nr = filp->f_op->write(filp, iovec.iov_base,
+ iovec.iov_len, ppos);
+ }
if (nr < 0) {
if (!ret)
@@ -844,8 +850,6 @@
struct iovec *iov = iovstack;
struct iov_iter iter;
ssize_t ret;
- io_fn_t fn;
- iter_fn_t iter_fn;
ret = import_iovec(type, uvector, nr_segs,
ARRAY_SIZE(iovstack), &iov, &iter);
@@ -859,19 +863,14 @@
if (ret < 0)
goto out;
- if (type == READ) {
- fn = file->f_op->read;
- iter_fn = file->f_op->read_iter;
- } else {
- fn = (io_fn_t)file->f_op->write;
- iter_fn = file->f_op->write_iter;
+ if (type != READ)
file_start_write(file);
- }
- if (iter_fn)
- ret = do_iter_readv_writev(file, &iter, pos, iter_fn, flags);
+ if ((type == READ && file->f_op->read_iter) ||
+ (type == WRITE && file->f_op->write_iter))
+ ret = do_iter_readv_writev(file, &iter, pos, type, flags);
else
- ret = do_loop_readv_writev(file, &iter, pos, fn, flags);
+ ret = do_loop_readv_writev(file, &iter, pos, type, flags);
if (type != READ)
file_end_write(file);
@@ -1069,8 +1068,6 @@
struct iovec *iov = iovstack;
struct iov_iter iter;
ssize_t ret;
- io_fn_t fn;
- iter_fn_t iter_fn;
ret = compat_import_iovec(type, uvector, nr_segs,
UIO_FASTIOV, &iov, &iter);
@@ -1084,19 +1081,14 @@
if (ret < 0)
goto out;
- if (type == READ) {
- fn = file->f_op->read;
- iter_fn = file->f_op->read_iter;
- } else {
- fn = (io_fn_t)file->f_op->write;
- iter_fn = file->f_op->write_iter;
+ if (type != READ)
file_start_write(file);
- }
- if (iter_fn)
- ret = do_iter_readv_writev(file, &iter, pos, iter_fn, flags);
+ if ((type == READ && file->f_op->read_iter) ||
+ (type == WRITE && file->f_op->write_iter))
+ ret = do_iter_readv_writev(file, &iter, pos, type, flags);
else
- ret = do_loop_readv_writev(file, &iter, pos, fn, flags);
+ ret = do_loop_readv_writev(file, &iter, pos, type, flags);
if (type != READ)
file_end_write(file);
diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c
index bc2dde2..7610818 100644
--- a/fs/reiserfs/journal.c
+++ b/fs/reiserfs/journal.c
@@ -1959,7 +1959,7 @@
* will be requeued because superblock is being shutdown and doesn't
* have MS_ACTIVE set.
*/
- cancel_delayed_work_sync(&REISERFS_SB(sb)->old_work);
+ reiserfs_cancel_old_flush(sb);
/* wait for all commits to finish */
cancel_delayed_work_sync(&SB_JOURNAL(sb)->j_work);
diff --git a/fs/reiserfs/reiserfs.h b/fs/reiserfs/reiserfs.h
index 5dcf3ab..6ca0047 100644
--- a/fs/reiserfs/reiserfs.h
+++ b/fs/reiserfs/reiserfs.h
@@ -2948,6 +2948,7 @@
struct reiserfs_list_bitmap *, unsigned int);
void reiserfs_schedule_old_flush(struct super_block *s);
+void reiserfs_cancel_old_flush(struct super_block *s);
void add_save_link(struct reiserfs_transaction_handle *th,
struct inode *inode, int truncate);
int remove_save_link(struct inode *inode, int truncate);
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
index e101d70..dec6c93 100644
--- a/fs/reiserfs/super.c
+++ b/fs/reiserfs/super.c
@@ -90,7 +90,9 @@
s = sbi->s_journal->j_work_sb;
spin_lock(&sbi->old_work_lock);
- sbi->work_queued = 0;
+ /* Avoid clobbering the cancel state... */
+ if (sbi->work_queued == 1)
+ sbi->work_queued = 0;
spin_unlock(&sbi->old_work_lock);
reiserfs_sync_fs(s, 1);
@@ -117,21 +119,22 @@
spin_unlock(&sbi->old_work_lock);
}
-static void cancel_old_flush(struct super_block *s)
+void reiserfs_cancel_old_flush(struct super_block *s)
{
struct reiserfs_sb_info *sbi = REISERFS_SB(s);
- cancel_delayed_work_sync(&REISERFS_SB(s)->old_work);
spin_lock(&sbi->old_work_lock);
- sbi->work_queued = 0;
+ /* Make sure no new flushes will be queued */
+ sbi->work_queued = 2;
spin_unlock(&sbi->old_work_lock);
+ cancel_delayed_work_sync(&REISERFS_SB(s)->old_work);
}
static int reiserfs_freeze(struct super_block *s)
{
struct reiserfs_transaction_handle th;
- cancel_old_flush(s);
+ reiserfs_cancel_old_flush(s);
reiserfs_write_lock(s);
if (!(s->s_flags & MS_RDONLY)) {
@@ -152,7 +155,13 @@
static int reiserfs_unfreeze(struct super_block *s)
{
+ struct reiserfs_sb_info *sbi = REISERFS_SB(s);
+
reiserfs_allow_writes(s);
+ spin_lock(&sbi->old_work_lock);
+ /* Allow old_work to run again */
+ sbi->work_queued = 0;
+ spin_unlock(&sbi->old_work_lock);
return 0;
}
@@ -2194,7 +2203,7 @@
if (sbi->commit_wq)
destroy_workqueue(sbi->commit_wq);
- cancel_delayed_work_sync(&REISERFS_SB(s)->old_work);
+ reiserfs_cancel_old_flush(s);
reiserfs_free_bitmap_cache(s);
if (SB_BUFFER_WITH_SB(s))
diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c
index 2879d12..1461254 100644
--- a/fs/sdcardfs/file.c
+++ b/fs/sdcardfs/file.c
@@ -414,7 +414,7 @@
fsstack_copy_inode_size(inode, file_inode(lower_file));
fsstack_copy_attr_times(inode, file_inode(lower_file));
if (sizeof(loff_t) > sizeof(long))
- inode_lock(inode);
+ inode_unlock(inode);
}
out:
return err;
diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c
index 6da0c21..4b9a563 100644
--- a/fs/sdcardfs/packagelist.c
+++ b/fs/sdcardfs/packagelist.c
@@ -659,6 +659,7 @@
return ERR_PTR(-ENOMEM);
}
qstr_init(&extension_details->name, tmp);
+ extension_details->num = extensions_value->num;
ret = insert_ext_gid_entry(&extension_details->name, extensions_value->num);
if (ret) {
diff --git a/fs/super.c b/fs/super.c
index 847d82d..ce404a2 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -519,7 +519,11 @@
hlist_add_head(&s->s_instances, &type->fs_supers);
spin_unlock(&sb_lock);
get_filesystem(type);
- register_shrinker(&s->s_shrink);
+ err = register_shrinker(&s->s_shrink);
+ if (err) {
+ deactivate_locked_super(s);
+ s = ERR_PTR(err);
+ }
return s;
}
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index 1fdd3fa..e3edaa5 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -47,7 +47,7 @@
STATIC int xfs_qm_init_quotainos(xfs_mount_t *);
STATIC int xfs_qm_init_quotainfo(xfs_mount_t *);
-
+STATIC void xfs_qm_destroy_quotainos(xfs_quotainfo_t *qi);
STATIC void xfs_qm_dqfree_one(struct xfs_dquot *dqp);
/*
* We use the batch lookup interface to iterate over the dquots as it
@@ -694,9 +694,17 @@
qinf->qi_shrinker.scan_objects = xfs_qm_shrink_scan;
qinf->qi_shrinker.seeks = DEFAULT_SEEKS;
qinf->qi_shrinker.flags = SHRINKER_NUMA_AWARE;
- register_shrinker(&qinf->qi_shrinker);
+
+ error = register_shrinker(&qinf->qi_shrinker);
+ if (error)
+ goto out_free_inos;
+
return 0;
+out_free_inos:
+ mutex_destroy(&qinf->qi_quotaofflock);
+ mutex_destroy(&qinf->qi_tree_lock);
+ xfs_qm_destroy_quotainos(qinf);
out_free_lru:
list_lru_destroy(&qinf->qi_lru);
out_free_qinf:
@@ -705,7 +713,6 @@
return error;
}
-
/*
* Gets called when unmounting a filesystem or when all quotas get
* turned off.
@@ -722,19 +729,8 @@
unregister_shrinker(&qi->qi_shrinker);
list_lru_destroy(&qi->qi_lru);
-
- if (qi->qi_uquotaip) {
- IRELE(qi->qi_uquotaip);
- qi->qi_uquotaip = NULL; /* paranoia */
- }
- if (qi->qi_gquotaip) {
- IRELE(qi->qi_gquotaip);
- qi->qi_gquotaip = NULL;
- }
- if (qi->qi_pquotaip) {
- IRELE(qi->qi_pquotaip);
- qi->qi_pquotaip = NULL;
- }
+ xfs_qm_destroy_quotainos(qi);
+ mutex_destroy(&qi->qi_tree_lock);
mutex_destroy(&qi->qi_quotaofflock);
kmem_free(qi);
mp->m_quotainfo = NULL;
@@ -1620,6 +1616,24 @@
}
STATIC void
+xfs_qm_destroy_quotainos(
+ xfs_quotainfo_t *qi)
+{
+ if (qi->qi_uquotaip) {
+ IRELE(qi->qi_uquotaip);
+ qi->qi_uquotaip = NULL; /* paranoia */
+ }
+ if (qi->qi_gquotaip) {
+ IRELE(qi->qi_gquotaip);
+ qi->qi_gquotaip = NULL;
+ }
+ if (qi->qi_pquotaip) {
+ IRELE(qi->qi_pquotaip);
+ qi->qi_pquotaip = NULL;
+ }
+}
+
+STATIC void
xfs_qm_dqfree_one(
struct xfs_dquot *dqp)
{
diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h
index c93dbad..db7d15b 100644
--- a/include/acpi/actbl2.h
+++ b/include/acpi/actbl2.h
@@ -783,6 +783,15 @@
#define ACPI_IORT_SMMU_DVM_SUPPORTED (1)
#define ACPI_IORT_SMMU_COHERENT_WALK (1<<1)
+/* Global interrupt format */
+
+struct acpi_iort_smmu_gsi {
+ u32 nsg_irpt;
+ u32 nsg_irpt_flags;
+ u32 nsg_cfg_irpt;
+ u32 nsg_cfg_irpt_flags;
+};
+
struct acpi_iort_smmu_v3 {
u64 base_address; /* SMMUv3 base address */
u32 flags;
diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h
index c4f8fd2..f6ea0f3 100644
--- a/include/asm-generic/pgtable.h
+++ b/include/asm-generic/pgtable.h
@@ -764,6 +764,8 @@
int pmd_set_huge(pmd_t *pmd, phys_addr_t addr, pgprot_t prot);
int pud_clear_huge(pud_t *pud);
int pmd_clear_huge(pmd_t *pmd);
+int pud_free_pmd_page(pud_t *pud);
+int pmd_free_pte_page(pmd_t *pmd);
#else /* !CONFIG_HAVE_ARCH_HUGE_VMAP */
static inline int pud_set_huge(pud_t *pud, phys_addr_t addr, pgprot_t prot)
{
@@ -781,6 +783,14 @@
{
return 0;
}
+static inline int pud_free_pmd_page(pud_t *pud)
+{
+ return 0;
+}
+static inline int pmd_free_pte_page(pmd_t *pmd)
+{
+ return 0;
+}
#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
#ifndef __HAVE_ARCH_FLUSH_PMD_TLB_RANGE
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 3a396a9..8236dbd 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -67,10 +67,12 @@
*/
#ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION
#define TEXT_MAIN .text .text.[0-9a-zA-Z_]*
+#define TEXT_CFI_MAIN .text.cfi .text.[0-9a-zA-Z_]*.cfi
#define DATA_MAIN .data .data.[0-9a-zA-Z_]*
#define BSS_MAIN .bss .bss.[0-9a-zA-Z_]*
#else
#define TEXT_MAIN .text
+#define TEXT_CFI_MAIN .text.cfi
#define DATA_MAIN .data
#define BSS_MAIN .bss
#endif
@@ -105,7 +107,7 @@
#ifdef CONFIG_FTRACE_MCOUNT_RECORD
#define MCOUNT_REC() . = ALIGN(8); \
VMLINUX_SYMBOL(__start_mcount_loc) = .; \
- *(__mcount_loc) \
+ KEEP(*(__mcount_loc)) \
VMLINUX_SYMBOL(__stop_mcount_loc) = .;
#else
#define MCOUNT_REC()
@@ -139,10 +141,10 @@
#ifdef CONFIG_EVENT_TRACING
#define FTRACE_EVENTS() . = ALIGN(8); \
VMLINUX_SYMBOL(__start_ftrace_events) = .; \
- *(_ftrace_events) \
+ KEEP(*(_ftrace_events)) \
VMLINUX_SYMBOL(__stop_ftrace_events) = .; \
VMLINUX_SYMBOL(__start_ftrace_enum_maps) = .; \
- *(_ftrace_enum_map) \
+ KEEP(*(_ftrace_enum_map)) \
VMLINUX_SYMBOL(__stop_ftrace_enum_maps) = .;
#else
#define FTRACE_EVENTS()
@@ -185,8 +187,8 @@
#define _OF_TABLE_1(name) \
. = ALIGN(8); \
VMLINUX_SYMBOL(__##name##_of_table) = .; \
- *(__##name##_of_table) \
- *(__##name##_of_table_end)
+ KEEP(*(__##name##_of_table)) \
+ KEEP(*(__##name##_of_table_end))
#define CLKSRC_OF_TABLES() OF_TABLE(CONFIG_CLKSRC_OF, clksrc)
#define IRQCHIP_OF_MATCH_TABLE() OF_TABLE(CONFIG_IRQCHIP, irqchip)
@@ -304,28 +306,28 @@
/* PCI quirks */ \
.pci_fixup : AT(ADDR(.pci_fixup) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start_pci_fixups_early) = .; \
- *(.pci_fixup_early) \
+ KEEP(*(.pci_fixup_early)) \
VMLINUX_SYMBOL(__end_pci_fixups_early) = .; \
VMLINUX_SYMBOL(__start_pci_fixups_header) = .; \
- *(.pci_fixup_header) \
+ KEEP(*(.pci_fixup_header)) \
VMLINUX_SYMBOL(__end_pci_fixups_header) = .; \
VMLINUX_SYMBOL(__start_pci_fixups_final) = .; \
- *(.pci_fixup_final) \
+ KEEP(*(.pci_fixup_final)) \
VMLINUX_SYMBOL(__end_pci_fixups_final) = .; \
VMLINUX_SYMBOL(__start_pci_fixups_enable) = .; \
- *(.pci_fixup_enable) \
+ KEEP(*(.pci_fixup_enable)) \
VMLINUX_SYMBOL(__end_pci_fixups_enable) = .; \
VMLINUX_SYMBOL(__start_pci_fixups_resume) = .; \
- *(.pci_fixup_resume) \
+ KEEP(*(.pci_fixup_resume)) \
VMLINUX_SYMBOL(__end_pci_fixups_resume) = .; \
VMLINUX_SYMBOL(__start_pci_fixups_resume_early) = .; \
- *(.pci_fixup_resume_early) \
+ KEEP(*(.pci_fixup_resume_early)) \
VMLINUX_SYMBOL(__end_pci_fixups_resume_early) = .; \
VMLINUX_SYMBOL(__start_pci_fixups_suspend) = .; \
- *(.pci_fixup_suspend) \
+ KEEP(*(.pci_fixup_suspend)) \
VMLINUX_SYMBOL(__end_pci_fixups_suspend) = .; \
VMLINUX_SYMBOL(__start_pci_fixups_suspend_late) = .; \
- *(.pci_fixup_suspend_late) \
+ KEEP(*(.pci_fixup_suspend_late)) \
VMLINUX_SYMBOL(__end_pci_fixups_suspend_late) = .; \
} \
\
@@ -423,7 +425,7 @@
/* Built-in module parameters. */ \
__param : AT(ADDR(__param) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start___param) = .; \
- *(__param) \
+ KEEP(*(__param)) \
VMLINUX_SYMBOL(__stop___param) = .; \
} \
\
@@ -460,6 +462,8 @@
#define TEXT_TEXT \
ALIGN_FUNCTION(); \
*(.text.hot TEXT_MAIN .text.fixup .text.unlikely) \
+ *(.text..ftrace) \
+ *(TEXT_CFI_MAIN) \
*(.ref.text) \
MEM_KEEP(init.text) \
MEM_KEEP(exit.text) \
@@ -512,7 +516,7 @@
VMLINUX_SYMBOL(__softirqentry_text_end) = .;
/* Section used for early init (in .S files) */
-#define HEAD_TEXT *(.head.text)
+#define HEAD_TEXT KEEP(*(.head.text))
#define HEAD_TEXT_SECTION \
.head.text : AT(ADDR(.head.text) - LOAD_OFFSET) { \
@@ -526,7 +530,7 @@
. = ALIGN(align); \
__ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start___ex_table) = .; \
- *(__ex_table) \
+ KEEP(*(__ex_table)) \
VMLINUX_SYMBOL(__stop___ex_table) = .; \
}
@@ -557,7 +561,7 @@
MEM_DISCARD(init.data) \
KERNEL_CTORS() \
MCOUNT_REC() \
- *(.init.rodata) \
+ *(.init.rodata .init.rodata.*) \
FTRACE_EVENTS() \
TRACE_SYSCALLS() \
KPROBE_BLACKLIST() \
@@ -575,7 +579,7 @@
EARLYCON_TABLE()
#define INIT_TEXT \
- *(.init.text) \
+ *(.init.text .init.text.*) \
*(.text.startup) \
MEM_DISCARD(init.text)
@@ -592,7 +596,7 @@
MEM_DISCARD(exit.text)
#define EXIT_CALL \
- *(.exitcall.exit)
+ KEEP(*(.exitcall.exit))
/*
* bss (Block Started by Symbol) - uninitialized data
@@ -668,7 +672,7 @@
. = ALIGN(8); \
__bug_table : AT(ADDR(__bug_table) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start___bug_table) = .; \
- *(__bug_table) \
+ KEEP(*(__bug_table)) \
VMLINUX_SYMBOL(__stop___bug_table) = .; \
}
#else
@@ -697,7 +701,7 @@
#define INIT_SETUP(initsetup_align) \
. = ALIGN(initsetup_align); \
VMLINUX_SYMBOL(__setup_start) = .; \
- *(.init.setup) \
+ KEEP(*(.init.setup)) \
VMLINUX_SYMBOL(__setup_end) = .;
#define INIT_CALLS_LEVEL(level) \
diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h
index 982c299..642d059 100644
--- a/include/drm/drm_crtc_helper.h
+++ b/include/drm/drm_crtc_helper.h
@@ -74,5 +74,6 @@
extern void drm_kms_helper_poll_disable(struct drm_device *dev);
extern void drm_kms_helper_poll_enable(struct drm_device *dev);
extern void drm_kms_helper_poll_enable_locked(struct drm_device *dev);
+extern bool drm_kms_helper_is_poll_worker(void);
#endif
diff --git a/include/dt-bindings/clock/msm-clocks-8952.h b/include/dt-bindings/clock/msm-clocks-8952.h
index 4547751..3190d4f 100644
--- a/include/dt-bindings/clock/msm-clocks-8952.h
+++ b/include/dt-bindings/clock/msm-clocks-8952.h
@@ -235,6 +235,11 @@
#define clk_ext_pclk0_clk_src 0x087c1612
#define clk_ext_byte0_clk_src 0xfb32f31e
+#define clk_dsi0pll_byte_clk_src 0xbbaa30be
+#define clk_dsi0pll_pixel_clk_src 0x45b3260f
+#define clk_dsi1pll_byte_clk_src 0x63930a8f
+#define clk_dsi1pll_pixel_clk_src 0x0e4c9b56
+
#define clk_dsi_pll0_byte_clk_src 0x44539836
#define clk_dsi_pll0_pixel_clk_src 0x5767c287
#define clk_dsi_pll1_byte_clk_src 0x73e88d02
diff --git a/include/dt-bindings/clock/msm-clocks-hwio-8952.h b/include/dt-bindings/clock/msm-clocks-hwio-8952.h
index 888c2c7..cba5a86 100644
--- a/include/dt-bindings/clock/msm-clocks-hwio-8952.h
+++ b/include/dt-bindings/clock/msm-clocks-hwio-8952.h
@@ -432,6 +432,32 @@
VDD_HF_PLL_NUM_8917,
};
+enum vdd_sr2_pll_levels_439 {
+ VDD_SR2_PLL_OFF_439,
+ VDD_SR2_PLL_SVS_439,
+ VDD_SR2_PLL_NOM_439,
+ VDD_SR2_PLL_TUR_439,
+ VDD_SR2_PLL_NUM_439,
+};
+
+enum vdd_dig_levels_439 {
+ VDD_DIG_NONE_439,
+ VDD_DIG_LOWER_439,
+ VDD_DIG_LOW_439,
+ VDD_DIG_NOMINAL_439,
+ VDD_DIG_NOM_PLUS_439,
+ VDD_DIG_HIGH_439,
+ VDD_DIG_NUM_439
+};
+
+enum vdd_hf_pll_levels_439 {
+ VDD_HF_PLL_OFF_439,
+ VDD_HF_PLL_SVS_439,
+ VDD_HF_PLL_NOM_439,
+ VDD_HF_PLL_TUR_439,
+ VDD_HF_PLL_NUM_439,
+};
+
static int vdd_corner[] = {
RPM_REGULATOR_LEVEL_NONE, /* VDD_DIG_NONE */
RPM_REGULATOR_LEVEL_SVS, /* VDD_DIG_SVS */
diff --git a/include/dt-bindings/clock/r8a7794-clock.h b/include/dt-bindings/clock/r8a7794-clock.h
index 88e6484..cdeafd9 100644
--- a/include/dt-bindings/clock/r8a7794-clock.h
+++ b/include/dt-bindings/clock/r8a7794-clock.h
@@ -81,6 +81,7 @@
#define R8A7794_CLK_SCIF2 19
#define R8A7794_CLK_SCIF1 20
#define R8A7794_CLK_SCIF0 21
+#define R8A7794_CLK_DU1 23
#define R8A7794_CLK_DU0 24
/* MSTP8 */
diff --git a/include/linux/batterydata-interface.h b/include/linux/batterydata-interface.h
new file mode 100644
index 0000000..4318eb9
--- /dev/null
+++ b/include/linux/batterydata-interface.h
@@ -0,0 +1,15 @@
+/* Copyright (c) 2014-2015, 2018, 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 <uapi/linux/batterydata-interface.h>
+
+int config_battery_data(struct bms_battery_data *profile);
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 193c40e..cd6aaf0 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -306,6 +306,9 @@
/* verify correctness of eBPF program */
int bpf_check(struct bpf_prog **fp, union bpf_attr *attr);
+
+struct bpf_prog *bpf_prog_get_type_path(const char *name, enum bpf_prog_type type);
+
#else
static inline void bpf_register_prog_type(struct bpf_prog_type_list *tl)
{
@@ -333,6 +336,16 @@
{
return ERR_PTR(-EOPNOTSUPP);
}
+static inline int bpf_obj_get_user(const char __user *pathname)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline struct bpf_prog *bpf_prog_get_type_path(const char *name,
+ enum bpf_prog_type type)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
#endif /* CONFIG_BPF_SYSCALL */
/* verifier prototypes for helper functions called from eBPF programs */
diff --git a/include/linux/cfi.h b/include/linux/cfi.h
new file mode 100644
index 0000000..e27033d
--- /dev/null
+++ b/include/linux/cfi.h
@@ -0,0 +1,38 @@
+#ifndef _LINUX_CFI_H
+#define _LINUX_CFI_H
+
+#include <linux/stringify.h>
+
+#ifdef CONFIG_CFI_CLANG
+#ifdef CONFIG_MODULES
+
+typedef void (*cfi_check_fn)(uint64_t, void *, void *);
+
+/* Compiler-generated function in each module, and the kernel */
+#define CFI_CHECK_FN __cfi_check
+#define CFI_CHECK_FN_NAME __stringify(CFI_CHECK_FN)
+
+extern void CFI_CHECK_FN(uint64_t, void *, void *);
+
+#ifdef CONFIG_CFI_CLANG_SHADOW
+extern void cfi_module_add(struct module *mod, unsigned long min_addr,
+ unsigned long max_addr);
+
+extern void cfi_module_remove(struct module *mod, unsigned long min_addr,
+ unsigned long max_addr);
+#else
+static inline void cfi_module_add(struct module *mod, unsigned long min_addr,
+ unsigned long max_addr)
+{
+}
+
+static inline void cfi_module_remove(struct module *mod, unsigned long min_addr,
+ unsigned long max_addr)
+{
+}
+#endif /* CONFIG_CFI_CLANG_SHADOW */
+
+#endif /* CONFIG_MODULES */
+#endif /* CONFIG_CFI_CLANG */
+
+#endif /* _LINUX_CFI_H */
diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h
index c5a8afd..34d3f27 100644
--- a/include/linux/cgroup-defs.h
+++ b/include/linux/cgroup-defs.h
@@ -614,13 +614,13 @@
* updaters and return part of the previous pointer as the prioidx or
* classid. Such races are short-lived and the result isn't critical.
*/
-static inline u16 sock_cgroup_prioidx(struct sock_cgroup_data *skcd)
+static inline u16 sock_cgroup_prioidx(const struct sock_cgroup_data *skcd)
{
/* fallback to 1 which is always the ID of the root cgroup */
return (skcd->is_data & 1) ? skcd->prioidx : 1;
}
-static inline u32 sock_cgroup_classid(struct sock_cgroup_data *skcd)
+static inline u32 sock_cgroup_classid(const struct sock_cgroup_data *skcd)
{
/* fallback to 0 which is the unconfigured default classid */
return (skcd->is_data & 1) ? skcd->classid : 0;
diff --git a/include/linux/compiler-clang.h b/include/linux/compiler-clang.h
index de17999..e4f142c 100644
--- a/include/linux/compiler-clang.h
+++ b/include/linux/compiler-clang.h
@@ -15,3 +15,25 @@
* with any version that can compile the kernel
*/
#define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
+
+#ifdef CONFIG_LTO_CLANG
+#ifdef CONFIG_FTRACE_MCOUNT_RECORD
+#define __norecordmcount \
+ __attribute__((__section__(".text..ftrace")))
+#endif
+
+#define __nocfi __attribute__((no_sanitize("cfi")))
+#endif
+
+/* all clang versions usable with the kernel support KASAN ABI version 5 */
+#define KASAN_ABI_VERSION 5
+
+/* emulate gcc's __SANITIZE_ADDRESS__ flag */
+#if __has_feature(address_sanitizer)
+#define __SANITIZE_ADDRESS__
+#endif
+
+/* Clang doesn't have a way to turn it off per-function, yet. */
+#ifdef __noretpoline
+#undef __noretpoline
+#endif
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index 3a8c897..27101bb 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -92,6 +92,10 @@
#define __weak __attribute__((weak))
#define __alias(symbol) __attribute__((alias(#symbol)))
+#ifdef RETPOLINE
+#define __noretpoline __attribute__((indirect_branch("keep")))
+#endif
+
/*
* it doesn't make sense on ARM (currently the only user of __naked)
* to trace naked functions because then mcount is called without
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index 5ce911d..81bcdca 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -451,6 +451,14 @@
#define __visible
#endif
+#ifndef __norecordmcount
+#define __norecordmcount
+#endif
+
+#ifndef __nocfi
+#define __nocfi
+#endif
+
/*
* Assume alignment of return value.
*/
diff --git a/include/linux/dax.h b/include/linux/dax.h
index add6c4b..ed9cf2f 100644
--- a/include/linux/dax.h
+++ b/include/linux/dax.h
@@ -61,11 +61,6 @@
int dax_pfn_mkwrite(struct vm_area_struct *, struct vm_fault *);
#define dax_mkwrite(vma, vmf, gb) dax_fault(vma, vmf, gb)
-static inline bool vma_is_dax(struct vm_area_struct *vma)
-{
- return vma->vm_file && IS_DAX(vma->vm_file->f_mapping->host);
-}
-
static inline bool dax_mapping(struct address_space *mapping)
{
return mapping->host && IS_DAX(mapping->host);
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index 95fe239..3c7ec22 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2018, 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
@@ -146,10 +146,10 @@
* a new RANGE of SSIDs to the msg_mask_tbl.
*/
#define MSG_MASK_TBL_CNT 26
-#define APPS_EVENT_LAST_ID 0x0B3F
+#define APPS_EVENT_LAST_ID 0x0C5A
#define MSG_SSID_0 0
-#define MSG_SSID_0_LAST 121
+#define MSG_SSID_0_LAST 125
#define MSG_SSID_1 500
#define MSG_SSID_1_LAST 506
#define MSG_SSID_2 1000
@@ -161,11 +161,11 @@
#define MSG_SSID_5 4000
#define MSG_SSID_5_LAST 4010
#define MSG_SSID_6 4500
-#define MSG_SSID_6_LAST 4583
+#define MSG_SSID_6_LAST 4584
#define MSG_SSID_7 4600
-#define MSG_SSID_7_LAST 4615
+#define MSG_SSID_7_LAST 4616
#define MSG_SSID_8 5000
-#define MSG_SSID_8_LAST 5033
+#define MSG_SSID_8_LAST 5034
#define MSG_SSID_9 5500
#define MSG_SSID_9_LAST 5516
#define MSG_SSID_10 6000
@@ -265,7 +265,7 @@
MSG_MASK_9 | MSG_MASK_10,
MSG_LVL_MED,
MSG_LVL_LOW,
- MSG_LVL_LOW,
+ MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_LOW,
MSG_LVL_LOW,
@@ -319,7 +319,7 @@
MSG_LVL_FATAL,
MSG_LVL_MED,
MSG_LVL_HIGH,
- MSG_LVL_LOW,
+ MSG_LVL_MED,
MSG_LVL_HIGH,
MSG_LVL_LOW | MSG_LVL_MED | MSG_LVL_HIGH | MSG_LVL_ERROR |
MSG_LVL_FATAL,
@@ -497,6 +497,7 @@
MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW,
+ MSG_LVL_LOW,
MSG_LVL_LOW
};
@@ -516,7 +517,9 @@
MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW,
- MSG_LVL_LOW | MSG_LVL_MED | MSG_LVL_HIGH | MSG_LVL_ERROR | MSG_LVL_FATAL
+ MSG_LVL_LOW | MSG_LVL_MED | MSG_LVL_HIGH | MSG_LVL_ERROR |
+ MSG_LVL_FATAL,
+ MSG_LVL_LOW
};
static const uint32_t msg_bld_masks_8[] = {
@@ -536,9 +539,6 @@
MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_MED,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_MED,
@@ -553,6 +553,10 @@
MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_HIGH,
MSG_LVL_HIGH
};
@@ -655,14 +659,14 @@
MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_LOW,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
MSG_LVL_MED
};
@@ -812,7 +816,9 @@
};
static const uint32_t msg_bld_masks_20[] = {
- MSG_LVL_LOW,
+ MSG_LVL_LOW | MSG_MASK_5 | MSG_MASK_6 | MSG_MASK_7 |
+ MSG_MASK_8 | MSG_MASK_9 | MSG_MASK_10 | MSG_MASK_11 |
+ MSG_MASK_12,
MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW,
@@ -890,7 +896,7 @@
/* LOG CODES */
static const uint32_t log_code_last_tbl[] = {
0x0, /* EQUIP ID 0 */
- 0x1A11, /* EQUIP ID 1 */
+ 0x1C68, /* EQUIP ID 1 */
0x0, /* EQUIP ID 2 */
0x0, /* EQUIP ID 3 */
0x4910, /* EQUIP ID 4 */
@@ -900,7 +906,7 @@
0x0, /* EQUIP ID 8 */
0x0, /* EQUIP ID 9 */
0xA38A, /* EQUIP ID 10 */
- 0xB201, /* EQUIP ID 11 */
+ 0xB9FF, /* EQUIP ID 11 */
0x0, /* EQUIP ID 12 */
0xD1FF, /* EQUIP ID 13 */
0x0, /* EQUIP ID 14 */
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index 58aecb6..393b880 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -46,7 +46,6 @@
/* This flag is used by node and meta inodes, and by recovery */
#define GFP_F2FS_ZERO (GFP_NOFS | __GFP_ZERO)
-#define GFP_F2FS_HIGH_ZERO (GFP_NOFS | __GFP_ZERO | __GFP_HIGHMEM)
/*
* For further optimization on multi-head logs, on-disk layout supports maximum
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 4abfa9c..db3bdb2 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -18,6 +18,7 @@
#include <linux/bug.h>
#include <linux/mutex.h>
#include <linux/rwsem.h>
+#include <linux/mm_types.h>
#include <linux/capability.h>
#include <linux/semaphore.h>
#include <linux/fiemap.h>
@@ -1319,6 +1320,7 @@
#define SB_I_CGROUPWB 0x00000001 /* cgroup-aware writeback enabled */
#define SB_I_NOEXEC 0x00000002 /* Ignore executables on this fs */
#define SB_I_NODEV 0x00000004 /* Ignore devices on this fs */
+#define SB_I_MULTIROOT 0x00000008 /* Multiple roots to the dentry tree */
/* sb->s_iflags to limit user namespace mounts */
#define SB_I_USERNS_VISIBLE 0x00000010 /* fstype already mounted */
@@ -3059,6 +3061,25 @@
return (filp->f_flags & O_DIRECT) || IS_DAX(filp->f_mapping->host);
}
+static inline bool vma_is_dax(struct vm_area_struct *vma)
+{
+ return vma->vm_file && IS_DAX(vma->vm_file->f_mapping->host);
+}
+
+static inline bool vma_is_fsdax(struct vm_area_struct *vma)
+{
+ struct inode *inode;
+
+ if (!vma->vm_file)
+ return false;
+ if (!vma_is_dax(vma))
+ return false;
+ inode = file_inode(vma->vm_file);
+ if (S_ISCHR(inode->i_mode))
+ return false; /* device-dax */
+ return true;
+}
+
static inline int iocb_flags(struct file *file)
{
int res = 0;
diff --git a/include/linux/fsl_ifc.h b/include/linux/fsl_ifc.h
index c332f0a..3fdfede 100644
--- a/include/linux/fsl_ifc.h
+++ b/include/linux/fsl_ifc.h
@@ -734,11 +734,7 @@
u32 res19[0x10];
__be32 nand_fsr;
u32 res20;
- /* The V1 nand_eccstat is actually 4 words that overlaps the
- * V2 nand_eccstat.
- */
- __be32 v1_nand_eccstat[2];
- __be32 v2_nand_eccstat[6];
+ __be32 nand_eccstat[8];
u32 res21[0x1c];
__be32 nanndcr;
u32 res22[0x2];
diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index 16ef407..51649c9 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -523,7 +523,7 @@
void page_alloc_init(void);
void drain_zone_pages(struct zone *zone, struct per_cpu_pages *pcp);
void drain_all_pages(struct zone *zone);
-void drain_local_pages(struct zone *zone);
+void drain_local_pages(void *zone);
void page_alloc_init_late(void);
diff --git a/include/linux/init.h b/include/linux/init.h
index 8e346d1..906ec32 100644
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -5,10 +5,10 @@
#include <linux/types.h>
/* Built-in __init functions needn't be compiled with retpoline */
-#if defined(RETPOLINE) && !defined(MODULE)
-#define __noretpoline __attribute__((indirect_branch("keep")))
+#if defined(__noretpoline) && !defined(MODULE)
+#define __noinitretpoline __noretpoline
#else
-#define __noretpoline
+#define __noinitretpoline
#endif
/* These macros are used to mark some functions or
@@ -46,7 +46,7 @@
/* These are for everybody (although not all archs will actually
discard it in modules) */
-#define __init __section(.init.text) __cold notrace __latent_entropy __noretpoline
+#define __init __section(.init.text) __cold notrace __latent_entropy __noinitretpoline __nocfi
#define __initdata __section(.init.data)
#define __initconst __section(.init.rodata)
#define __exitdata __section(.exit.data)
@@ -147,6 +147,15 @@
#ifndef __ASSEMBLY__
+#ifdef CONFIG_LTO_CLANG
+ /* prepend the variable name with __COUNTER__ to ensure correct ordering */
+ #define ___initcall_name2(c, fn, id) __initcall_##c##_##fn##id
+ #define ___initcall_name1(c, fn, id) ___initcall_name2(c, fn, id)
+ #define __initcall_name(fn, id) ___initcall_name1(__COUNTER__, fn, id)
+#else
+ #define __initcall_name(fn, id) __initcall_##fn##id
+#endif
+
/*
* initcalls are now grouped by functionality into separate
* subsections. Ordering inside the subsections is determined
@@ -164,7 +173,7 @@
*/
#define __define_initcall(fn, id) \
- static initcall_t __initcall_##fn##id __used \
+ static initcall_t __initcall_name(fn, id) __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn;
/*
diff --git a/include/linux/input/gen_vkeys.h b/include/linux/input/gen_vkeys.h
new file mode 100644
index 0000000..b152147
--- /dev/null
+++ b/include/linux/input/gen_vkeys.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2013, 2018 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 __GEN_VKEYS_
+struct vkeys_platform_data {
+ const char *name;
+ int disp_maxx;
+ int disp_maxy;
+ int panel_maxx;
+ int panel_maxy;
+ int *keycodes;
+ int num_keys;
+ int y_offset;
+};
+#endif
diff --git a/include/linux/ipa.h b/include/linux/ipa.h
index f966e0c..1a1c68e 100644
--- a/include/linux/ipa.h
+++ b/include/linux/ipa.h
@@ -117,19 +117,6 @@
};
/**
-* enum ipa_vlan_ifaces - vlan interfaces types
-* @IPA_VLAN_IF_EMAC: used for EMAC ethernet device
-* @IPA_VLAN_IF_RNDIS: used for RNDIS USB device
-* @IPA_VLAN_IF_ECM: used for ECM USB device
-*/
-enum ipa_vlan_ifaces {
- IPA_VLAN_IF_EMAC,
- IPA_VLAN_IF_RNDIS,
- IPA_VLAN_IF_ECM,
- IPA_VLAN_IF_MAX
-};
-
-/**
* struct ipa_ep_cfg_nat - NAT configuration in IPA end-point
* @nat_en: This defines the default NAT mode for the pipe: in case of
* filter miss - the default NAT mode defines the NATing operation
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index bc6ed52..61054f1 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -46,6 +46,7 @@
#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x))
#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
+#define ALIGN_DOWN(x, a) __ALIGN_KERNEL((x) - ((a) - 1), (a))
#define __ALIGN_MASK(x, mask) __ALIGN_KERNEL_MASK((x), (mask))
#define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a)))
#define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0)
diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
index 6a620e0..7751d72 100644
--- a/include/linux/mlx5/driver.h
+++ b/include/linux/mlx5/driver.h
@@ -380,8 +380,8 @@
struct mlx5_core_rsc_common common; /* must be first */
u32 srqn;
int max;
- int max_gs;
- int max_avail_gather;
+ size_t max_gs;
+ size_t max_avail_gather;
int wqe_shift;
void (*event) (struct mlx5_core_srq *, enum mlx5_event);
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 932e99c..270f032 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1291,6 +1291,19 @@
struct page **pages, unsigned int gup_flags);
long get_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
struct page **pages, unsigned int gup_flags);
+#ifdef CONFIG_FS_DAX
+long get_user_pages_longterm(unsigned long start, unsigned long nr_pages,
+ unsigned int gup_flags, struct page **pages,
+ struct vm_area_struct **vmas);
+#else
+static inline long get_user_pages_longterm(unsigned long start,
+ unsigned long nr_pages, unsigned int gup_flags,
+ struct page **pages, struct vm_area_struct **vmas)
+{
+ return get_user_pages(start, nr_pages, gup_flags, pages, vmas);
+}
+#endif /* CONFIG_FS_DAX */
+
int get_user_pages_fast(unsigned long start, int nr_pages, int write,
struct page **pages);
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index c5a4a25..48b7281 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -138,6 +138,8 @@
int (*shutdown)(struct mmc_host *);
int (*reset)(struct mmc_host *);
int (*change_bus_speed)(struct mmc_host *, unsigned long *);
+ int (*pre_hibernate)(struct mmc_host *);
+ int (*post_hibernate)(struct mmc_host *);
};
struct mmc_card;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 2530fcc..7228bcd 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -454,6 +454,7 @@
#define MMC_CAP_HW_RESET (1 << 31) /* Hardware reset */
u32 caps2; /* More host capabilities */
+ u32 cached_caps2;
#define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */
#define MMC_CAP2_FULL_PWR_CYCLE (1 << 2) /* Can do full power cycle */
diff --git a/include/linux/module.h b/include/linux/module.h
index fd9e121..9d6fd1d 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -20,6 +20,7 @@
#include <linux/export.h>
#include <linux/extable.h> /* only as arch move module.h -> extable.h */
#include <linux/rbtree_latch.h>
+#include <linux/cfi.h>
#include <linux/percpu.h>
#include <asm/module.h>
@@ -349,6 +350,10 @@
const unsigned long *crcs;
unsigned int num_syms;
+#ifdef CONFIG_CFI_CLANG
+ cfi_check_fn cfi_check;
+#endif
+
/* Kernel parameters. */
#ifdef CONFIG_SYSFS
struct mutex param_lock;
diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h
index 52666d9..060db5c 100644
--- a/include/linux/moduleparam.h
+++ b/include/linux/moduleparam.h
@@ -225,19 +225,11 @@
VERIFY_OCTAL_PERMISSIONS(perm), level, flags, { arg } }
/* Obsolete - use module_param_cb() */
-#define module_param_call(name, set, get, arg, perm) \
- static const struct kernel_param_ops __param_ops_##name = \
- { .flags = 0, (void *)set, (void *)get }; \
+#define module_param_call(name, _set, _get, arg, perm) \
+ static const struct kernel_param_ops __param_ops_##name = \
+ { .flags = 0, .set = _set, .get = _get }; \
__module_param_call(MODULE_PARAM_PREFIX, \
- name, &__param_ops_##name, arg, \
- (perm) + sizeof(__check_old_set_param(set))*0, -1, 0)
-
-/* We don't get oldget: it's often a new-style param_get_uint, etc. */
-static inline int
-__check_old_set_param(int (*oldset)(const char *, struct kernel_param *))
-{
- return 0;
-}
+ name, &__param_ops_##name, arg, perm, -1, 0)
#ifdef CONFIG_SYSFS
extern void kernel_param_lock(struct module *mod);
diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h
index 2ad1a2b..9bfeb88 100644
--- a/include/linux/netfilter/x_tables.h
+++ b/include/linux/netfilter/x_tables.h
@@ -375,38 +375,14 @@
return ret;
}
+struct xt_percpu_counter_alloc_state {
+ unsigned int off;
+ const char __percpu *mem;
+};
-/* On SMP, ip(6)t_entry->counters.pcnt holds address of the
- * real (percpu) counter. On !SMP, its just the packet count,
- * so nothing needs to be done there.
- *
- * xt_percpu_counter_alloc returns the address of the percpu
- * counter, or 0 on !SMP. We force an alignment of 16 bytes
- * so that bytes/packets share a common cache line.
- *
- * Hence caller must use IS_ERR_VALUE to check for error, this
- * allows us to return 0 for single core systems without forcing
- * callers to deal with SMP vs. NONSMP issues.
- */
-static inline unsigned long xt_percpu_counter_alloc(void)
-{
- if (nr_cpu_ids > 1) {
- void __percpu *res = __alloc_percpu(sizeof(struct xt_counters),
- sizeof(struct xt_counters));
-
- if (res == NULL)
- return -ENOMEM;
-
- return (__force unsigned long) res;
- }
-
- return 0;
-}
-static inline void xt_percpu_counter_free(u64 pcnt)
-{
- if (nr_cpu_ids > 1)
- free_percpu((void __percpu *) (unsigned long) pcnt);
-}
+bool xt_percpu_counter_alloc(struct xt_percpu_counter_alloc_state *state,
+ struct xt_counters *counter);
+void xt_percpu_counter_free(struct xt_counters *cnt);
static inline struct xt_counters *
xt_get_this_cpu_counter(struct xt_counters *cnt)
diff --git a/include/linux/nospec.h b/include/linux/nospec.h
index fbc98e2..e791ebc 100644
--- a/include/linux/nospec.h
+++ b/include/linux/nospec.h
@@ -5,6 +5,7 @@
#ifndef _LINUX_NOSPEC_H
#define _LINUX_NOSPEC_H
+#include <asm/barrier.h>
/**
* array_index_mask_nospec() - generate a ~0 mask when index < size, 0 otherwise
@@ -30,26 +31,6 @@
#endif
/*
- * Warn developers about inappropriate array_index_nospec() usage.
- *
- * Even if the CPU speculates past the WARN_ONCE branch, the
- * sign bit of @index is taken into account when generating the
- * mask.
- *
- * This warning is compiled out when the compiler can infer that
- * @index and @size are less than LONG_MAX.
- */
-#define array_index_mask_nospec_check(index, size) \
-({ \
- if (WARN_ONCE(index > LONG_MAX || size > LONG_MAX, \
- "array_index_nospec() limited to range of [0, LONG_MAX]\n")) \
- _mask = 0; \
- else \
- _mask = array_index_mask_nospec(index, size); \
- _mask; \
-})
-
-/*
* array_index_nospec - sanitize an array index after a bounds check
*
* For a code sequence like:
@@ -67,12 +48,11 @@
({ \
typeof(index) _i = (index); \
typeof(size) _s = (size); \
- unsigned long _mask = array_index_mask_nospec_check(_i, _s); \
+ unsigned long _mask = array_index_mask_nospec(_i, _s); \
\
BUILD_BUG_ON(sizeof(_i) > sizeof(long)); \
BUILD_BUG_ON(sizeof(_s) > sizeof(long)); \
\
- _i &= _mask; \
- _i; \
+ (typeof(_i)) (_i & _mask); \
})
#endif /* _LINUX_NOSPEC_H */
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 7dbe914..791c547 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -148,7 +148,7 @@
#ifdef CONFIG_TINY_RCU
# ifdef CONFIG_PREEMPT_COUNT
- VM_BUG_ON(!in_atomic());
+ VM_BUG_ON(!in_atomic() && !irqs_disabled());
# endif
/*
* Preempt must be disabled here - we rely on rcu_read_lock doing
@@ -186,7 +186,7 @@
#if !defined(CONFIG_SMP) && defined(CONFIG_TREE_RCU)
# ifdef CONFIG_PREEMPT_COUNT
- VM_BUG_ON(!in_atomic());
+ VM_BUG_ON(!in_atomic() && !irqs_disabled());
# endif
VM_BUG_ON_PAGE(page_count(page) == 0, page);
page_ref_add(page, count);
@@ -225,7 +225,7 @@
__GFP_COLD | __GFP_NORETRY | __GFP_NOWARN;
}
-typedef int filler_t(void *, struct page *);
+typedef int filler_t(struct file *, struct page *);
pgoff_t page_cache_next_hole(struct address_space *mapping,
pgoff_t index, unsigned long max_scan);
@@ -369,7 +369,7 @@
static inline struct page *read_mapping_page(struct address_space *mapping,
pgoff_t index, void *data)
{
- filler_t *filler = (filler_t *)mapping->a_ops->readpage;
+ filler_t *filler = mapping->a_ops->readpage;
return read_cache_page(mapping, index, filler, data);
}
diff --git a/include/linux/platform_data/isl9305.h b/include/linux/platform_data/isl9305.h
index 1419133..4ac1a07 100644
--- a/include/linux/platform_data/isl9305.h
+++ b/include/linux/platform_data/isl9305.h
@@ -24,7 +24,7 @@
struct regulator_init_data;
struct isl9305_pdata {
- struct regulator_init_data *init_data[ISL9305_MAX_REGULATOR];
+ struct regulator_init_data *init_data[ISL9305_MAX_REGULATOR + 1];
};
#endif
diff --git a/include/linux/posix-clock.h b/include/linux/posix-clock.h
index 34c4498..83b22ae 100644
--- a/include/linux/posix-clock.h
+++ b/include/linux/posix-clock.h
@@ -59,23 +59,23 @@
int (*clock_adjtime)(struct posix_clock *pc, struct timex *tx);
- int (*clock_gettime)(struct posix_clock *pc, struct timespec *ts);
+ int (*clock_gettime)(struct posix_clock *pc, struct timespec64 *ts);
- int (*clock_getres) (struct posix_clock *pc, struct timespec *ts);
+ int (*clock_getres) (struct posix_clock *pc, struct timespec64 *ts);
int (*clock_settime)(struct posix_clock *pc,
- const struct timespec *ts);
+ const struct timespec64 *ts);
int (*timer_create) (struct posix_clock *pc, struct k_itimer *kit);
int (*timer_delete) (struct posix_clock *pc, struct k_itimer *kit);
void (*timer_gettime)(struct posix_clock *pc,
- struct k_itimer *kit, struct itimerspec *tsp);
+ struct k_itimer *kit, struct itimerspec64 *tsp);
int (*timer_settime)(struct posix_clock *pc,
struct k_itimer *kit, int flags,
- struct itimerspec *tsp, struct itimerspec *old);
+ struct itimerspec64 *tsp, struct itimerspec64 *old);
/*
* Optional character device methods:
*/
diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h
index a083370..031573d 100644
--- a/include/linux/qpnp/qpnp-adc.h
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -401,6 +401,10 @@
* %SCALE_USBIN_I: Conversion for USB input current.
* %SCALE_BATT_THERM_TEMP_QRD: Conversion to temperature(decidegC) based on btm
* parameters for QRD.
+ * %SCALE_BATT_THERM_TEMP_PU30: Conversion to temperature(decidegC) for 30k
+ * pullup.
+ * %SCALE_BATT_THERM_TEMP_PU400: Conversion to temperature(decidegC) for 400k
+ * pullup.
* %SCALE_SMB1390_DIE_TEMP: Conversion for SMB1390 die temp
* %SCALE_NONE: Do not use this scaling type.
*/
@@ -426,6 +430,8 @@
SCALE_USBIN_I,
SCALE_BATT_THERM_TEMP_QRD,
SCALE_SMB1390_DIE_TEMP,
+ SCALE_BATT_THERM_TEMP_PU30,
+ SCALE_BATT_THERM_TEMP_PU400,
SCALE_NONE,
};
@@ -1465,12 +1471,48 @@
const struct qpnp_vadc_chan_properties *chan_prop,
struct qpnp_vadc_result *chan_rslt);
/**
+ * qpnp_adc_batt_therm_pu30() - Scales the pre-calibrated digital output
+ * of an ADC to the ADC reference and compensates for the
+ * gain and offset. Returns the temperature in decidegC.
+ * It uses a mapping table computed for a 30K pull-up.
+ * @dev: Structure device for qpnp vadc
+ * @adc_code: pre-calibrated digital output of the ADC.
+ * @adc_prop: adc properties of the pm8xxx adc such as bit resolution,
+ * reference voltage.
+ * @chan_prop: individual channel properties to compensate the i/p scaling,
+ * slope and offset.
+ * @chan_rslt: physical result to be stored.
+ */
+int32_t qpnp_adc_batt_therm_pu30(struct qpnp_vadc_chip *dev,
+ int32_t adc_code,
+ const struct qpnp_adc_properties *adc_prop,
+ const struct qpnp_vadc_chan_properties *chan_prop,
+ struct qpnp_vadc_result *chan_rslt);
+/**
+ * qpnp_adc_batt_therm_pu400() - Scales the pre-calibrated digital output
+ * of an ADC to the ADC reference and compensates for the
+ * gain and offset. Returns the temperature in decidegC.
+ * It uses a mapping table computed for a 400K pull-up.
+ * @dev: Structure device for qpnp vadc
+ * @adc_code: pre-calibrated digital output of the ADC.
+ * @adc_prop: adc properties of the adc such as bit resolution,
+ * reference voltage.
+ * @chan_prop: individual channel properties to compensate the i/p scaling,
+ * slope and offset.
+ * @chan_rslt: physical result to be stored.
+ */
+int32_t qpnp_adc_batt_therm_pu400(struct qpnp_vadc_chip *dev,
+ int32_t adc_code,
+ const struct qpnp_adc_properties *adc_prop,
+ const struct qpnp_vadc_chan_properties *chan_prop,
+ struct qpnp_vadc_result *chan_rslt);
+/**
* qpnp_adc_scale_batt_therm() - Scales the pre-calibrated digital output
* of an ADC to the ADC reference and compensates for the
* gain and offset. Returns the temperature in decidegC.
* @dev: Structure device for qpnp vadc
* @adc_code: pre-calibrated digital output of the ADC.
- * @adc_prop: adc properties of the pm8xxx adc such as bit resolution,
+ * @adc_prop: adc properties of the adc such as bit resolution,
* reference voltage.
* @chan_prop: individual channel properties to compensate the i/p scaling,
* slope and offset.
@@ -2091,6 +2133,18 @@
const struct qpnp_vadc_chan_properties *chan_prop,
struct qpnp_vadc_result *chan_rslt)
{ return -ENXIO; }
+static inline int32_t qpnp_adc_batt_therm_pu30(struct qpnp_vadc_chip *vadc,
+ int32_t adc_code,
+ const struct qpnp_adc_properties *adc_prop,
+ const struct qpnp_vadc_chan_properties *chan_prop,
+ struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_batt_therm_pu400(struct qpnp_vadc_chip *vadc,
+ int32_t adc_code,
+ const struct qpnp_adc_properties *adc_prop,
+ const struct qpnp_vadc_chan_properties *chan_prop,
+ struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
static inline int32_t qpnp_adc_scale_batt_therm(struct qpnp_vadc_chip *vadc,
int32_t adc_code,
const struct qpnp_adc_properties *adc_prop,
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index ee79113..9760b53 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -432,6 +432,8 @@
struct regulator_enable_gpio *ena_pin;
unsigned int ena_gpio_state:1;
+ unsigned int is_switch:1;
+
/* time when this regulator was disabled last time */
unsigned long last_off_jiffy;
struct proxy_consumer *proxy_consumer;
diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h
index 5c132d3..85d1ffc 100644
--- a/include/linux/rhashtable.h
+++ b/include/linux/rhashtable.h
@@ -706,8 +706,10 @@
if (!key ||
(params.obj_cmpfn ?
params.obj_cmpfn(&arg, rht_obj(ht, head)) :
- rhashtable_compare(&arg, rht_obj(ht, head))))
+ rhashtable_compare(&arg, rht_obj(ht, head)))) {
+ pprev = &head->next;
continue;
+ }
data = rht_obj(ht, head);
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index b7ccd4e..c40355f 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -148,6 +148,7 @@
* @buf: Dynamic Debug Buffer.
* @max_nominal_system_clk_rate: max freq at which system clock can run in
nominal mode.
+ * @sdp_check: SDP detection work in case of USB_FLOAT power supply
*/
struct msm_otg {
struct usb_phy phy;
@@ -276,6 +277,7 @@
unsigned int notify_current_mA;
struct pm_qos_request pm_qos_req_dma;
struct delayed_work perf_vote_work;
+ struct delayed_work sdp_check;
struct work_struct notify_chg_current_work;
};
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
index de2a722..ea4f81c 100644
--- a/include/linux/usb/quirks.h
+++ b/include/linux/usb/quirks.h
@@ -56,4 +56,7 @@
*/
#define USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL BIT(11)
+/* Device needs a pause after every control message. */
+#define USB_QUIRK_DELAY_CTRL_MSG BIT(13)
+
#endif /* __LINUX_USB_QUIRKS_H */
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index 1061add..1def337 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -453,6 +453,7 @@
extern void workqueue_set_max_active(struct workqueue_struct *wq,
int max_active);
+extern struct work_struct *current_work(void);
extern bool current_is_workqueue_rescuer(void);
extern bool workqueue_congested(int cpu, struct workqueue_struct *wq);
extern unsigned int work_busy(struct work_struct *work);
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h
index 0406073..bc7a1d9 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -332,7 +332,7 @@
struct kernel_param;
-int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp);
+int nf_conntrack_set_hashsize(const char *val, const struct kernel_param *kp);
int nf_conntrack_hash_resize(unsigned int hashsize);
extern struct hlist_nulls_head *nf_conntrack_hash;
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index f18fc1a..538f3c4 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -675,6 +675,16 @@
*to_free = skb;
}
+static inline void __qdisc_drop_all(struct sk_buff *skb,
+ struct sk_buff **to_free)
+{
+ if (skb->prev)
+ skb->prev->next = *to_free;
+ else
+ skb->next = *to_free;
+ *to_free = skb;
+}
+
static inline unsigned int __qdisc_queue_drop_head(struct Qdisc *sch,
struct qdisc_skb_head *qh,
struct sk_buff **to_free)
@@ -795,6 +805,15 @@
return NET_XMIT_DROP;
}
+static inline int qdisc_drop_all(struct sk_buff *skb, struct Qdisc *sch,
+ struct sk_buff **to_free)
+{
+ __qdisc_drop_all(skb, to_free);
+ qdisc_qstats_drop(sch);
+
+ return NET_XMIT_DROP;
+}
+
/* Length to Time (L2T) lookup in a qdisc_rate_table, to determine how
long it will take to send a packet given its size.
*/
diff --git a/include/net/tcp.h b/include/net/tcp.h
index e7ac114..76d7c97 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1281,9 +1281,11 @@
static inline int tcp_win_from_space(int space)
{
- return sysctl_tcp_adv_win_scale<=0 ?
- (space>>(-sysctl_tcp_adv_win_scale)) :
- space - (space>>sysctl_tcp_adv_win_scale);
+ int tcp_adv_win_scale = sysctl_tcp_adv_win_scale;
+
+ return tcp_adv_win_scale <= 0 ?
+ (space>>(-tcp_adv_win_scale)) :
+ space - (space>>tcp_adv_win_scale);
}
/* Note: caller must be prepared to deal with negative returns */
diff --git a/include/net/udplite.h b/include/net/udplite.h
index 8076193..8228155 100644
--- a/include/net/udplite.h
+++ b/include/net/udplite.h
@@ -62,6 +62,7 @@
UDP_SKB_CB(skb)->cscov = cscov;
if (skb->ip_summed == CHECKSUM_COMPLETE)
skb->ip_summed = CHECKSUM_NONE;
+ skb->csum_valid = 0;
}
return 0;
diff --git a/include/soc/fsl/qe/qe.h b/include/soc/fsl/qe/qe.h
index 70339d7..0cd4c1147 100644
--- a/include/soc/fsl/qe/qe.h
+++ b/include/soc/fsl/qe/qe.h
@@ -243,6 +243,7 @@
#define qe_muram_free cpm_muram_free
#define qe_muram_addr cpm_muram_addr
#define qe_muram_offset cpm_muram_offset
+#define qe_muram_dma cpm_muram_dma
#define qe_setbits32(_addr, _v) iowrite32be(ioread32be(_addr) | (_v), (_addr))
#define qe_clrbits32(_addr, _v) iowrite32be(ioread32be(_addr) & ~(_v), (_addr))
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 75aa98ff..c9248e5a 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -65,6 +65,7 @@
header-y += auxvec.h
header-y += ax25.h
header-y += b1lli.h
+header-y += batterydata-interface.h
header-y += baycom.h
header-y += bcm933xx_hcs.h
header-y += bfs_fs.h
@@ -490,6 +491,7 @@
header-y += virtio_scsi.h
header-y += virtio_types.h
header-y += virtio_vsock.h
+header-y += vm_bms.h
header-y += vm_sockets.h
header-y += vt.h
header-y += vtpm_proxy.h
diff --git a/include/uapi/linux/batterydata-interface.h b/include/uapi/linux/batterydata-interface.h
new file mode 100644
index 0000000..498f4e0
--- /dev/null
+++ b/include/uapi/linux/batterydata-interface.h
@@ -0,0 +1,30 @@
+#ifndef __BATTERYDATA_LIB_H__
+#define __BATTERYDATA_LIB_H__
+
+#include <linux/ioctl.h>
+
+/**
+ * struct battery_params - Battery profile data to be exchanged.
+ * @soc: SOC (state of charge) of the battery
+ * @ocv_uv: OCV (open circuit voltage) of the battery
+ * @rbatt_sf: RBATT scaling factor
+ * @batt_temp: Battery temperature in deci-degree.
+ * @slope: Slope of the OCV-SOC curve.
+ * @fcc_mah: FCC (full charge capacity) of the battery.
+ */
+struct battery_params {
+ int soc;
+ int ocv_uv;
+ int rbatt_sf;
+ int batt_temp;
+ int slope;
+ int fcc_mah;
+};
+
+/* IOCTLs to query battery profile data */
+#define BPIOCXSOC _IOWR('B', 0x01, struct battery_params) /* SOC */
+#define BPIOCXRBATT _IOWR('B', 0x02, struct battery_params) /* RBATT SF */
+#define BPIOCXSLOPE _IOWR('B', 0x03, struct battery_params) /* SLOPE */
+#define BPIOCXFCC _IOWR('B', 0x04, struct battery_params) /* FCC */
+
+#endif
diff --git a/include/uapi/linux/libc-compat.h b/include/uapi/linux/libc-compat.h
index 44b8a6b..774cb2db 100644
--- a/include/uapi/linux/libc-compat.h
+++ b/include/uapi/linux/libc-compat.h
@@ -167,46 +167,99 @@
/* If we did not see any headers from any supported C libraries,
* or we are being included in the kernel, then define everything
- * that we need. */
+ * that we need. Check for previous __UAPI_* definitions to give
+ * unsupported C libraries a way to opt out of any kernel definition. */
#else /* !defined(__GLIBC__) */
/* Definitions for if.h */
+#ifndef __UAPI_DEF_IF_IFCONF
#define __UAPI_DEF_IF_IFCONF 1
+#endif
+#ifndef __UAPI_DEF_IF_IFMAP
#define __UAPI_DEF_IF_IFMAP 1
+#endif
+#ifndef __UAPI_DEF_IF_IFNAMSIZ
#define __UAPI_DEF_IF_IFNAMSIZ 1
+#endif
+#ifndef __UAPI_DEF_IF_IFREQ
#define __UAPI_DEF_IF_IFREQ 1
+#endif
/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */
+#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS
#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1
+#endif
/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */
+#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO
#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1
+#endif
/* Definitions for in.h */
+#ifndef __UAPI_DEF_IN_ADDR
#define __UAPI_DEF_IN_ADDR 1
+#endif
+#ifndef __UAPI_DEF_IN_IPPROTO
#define __UAPI_DEF_IN_IPPROTO 1
+#endif
+#ifndef __UAPI_DEF_IN_PKTINFO
#define __UAPI_DEF_IN_PKTINFO 1
+#endif
+#ifndef __UAPI_DEF_IP_MREQ
#define __UAPI_DEF_IP_MREQ 1
+#endif
+#ifndef __UAPI_DEF_SOCKADDR_IN
#define __UAPI_DEF_SOCKADDR_IN 1
+#endif
+#ifndef __UAPI_DEF_IN_CLASS
#define __UAPI_DEF_IN_CLASS 1
+#endif
/* Definitions for in6.h */
+#ifndef __UAPI_DEF_IN6_ADDR
#define __UAPI_DEF_IN6_ADDR 1
+#endif
+#ifndef __UAPI_DEF_IN6_ADDR_ALT
#define __UAPI_DEF_IN6_ADDR_ALT 1
+#endif
+#ifndef __UAPI_DEF_SOCKADDR_IN6
#define __UAPI_DEF_SOCKADDR_IN6 1
+#endif
+#ifndef __UAPI_DEF_IPV6_MREQ
#define __UAPI_DEF_IPV6_MREQ 1
+#endif
+#ifndef __UAPI_DEF_IPPROTO_V6
#define __UAPI_DEF_IPPROTO_V6 1
+#endif
+#ifndef __UAPI_DEF_IPV6_OPTIONS
#define __UAPI_DEF_IPV6_OPTIONS 1
+#endif
+#ifndef __UAPI_DEF_IN6_PKTINFO
#define __UAPI_DEF_IN6_PKTINFO 1
+#endif
+#ifndef __UAPI_DEF_IP6_MTUINFO
#define __UAPI_DEF_IP6_MTUINFO 1
+#endif
/* Definitions for ipx.h */
+#ifndef __UAPI_DEF_SOCKADDR_IPX
#define __UAPI_DEF_SOCKADDR_IPX 1
+#endif
+#ifndef __UAPI_DEF_IPX_ROUTE_DEFINITION
#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1
+#endif
+#ifndef __UAPI_DEF_IPX_INTERFACE_DEFINITION
#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1
+#endif
+#ifndef __UAPI_DEF_IPX_CONFIG_DATA
#define __UAPI_DEF_IPX_CONFIG_DATA 1
+#endif
+#ifndef __UAPI_DEF_IPX_ROUTE_DEF
#define __UAPI_DEF_IPX_ROUTE_DEF 1
+#endif
/* Definitions for xattr.h */
+#ifndef __UAPI_DEF_XATTR
#define __UAPI_DEF_XATTR 1
+#endif
#endif /* __GLIBC__ */
diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h
index 062a1b9..1c39daf 100644
--- a/include/uapi/linux/msm_ipa.h
+++ b/include/uapi/linux/msm_ipa.h
@@ -97,6 +97,7 @@
#define IPA_IOCTL_DEL_IPV6CT_TABLE 55
#define IPA_IOCTL_CLEANUP 56
#define IPA_IOCTL_QUERY_WLAN_CLIENT 57
+#define IPA_IOCTL_GET_VLAN_MODE 58
/**
* max size of the header to be inserted
@@ -1848,6 +1849,29 @@
};
/**
+ * enum ipa_vlan_ifaces - vlan interfaces types
+ */
+enum ipa_vlan_ifaces {
+ IPA_VLAN_IF_ETH,
+ IPA_VLAN_IF_RNDIS,
+ IPA_VLAN_IF_ECM
+};
+
+#define IPA_VLAN_IF_EMAC IPA_VLAN_IF_ETH
+#define IPA_VLAN_IF_MAX (IPA_VLAN_IF_ECM + 1)
+
+/**
+ * struct ipa_get_vlan_mode - get vlan mode of a Lan interface
+ * @iface: Lan interface type to be queried.
+ * @is_vlan_mode: output parameter, is interface in vlan mode, valid only when
+ * ioctl return val is non-negative
+ */
+struct ipa_ioc_get_vlan_mode {
+ enum ipa_vlan_ifaces iface;
+ uint32_t is_vlan_mode;
+};
+
+/**
* actual IOCTLs supported by IPA driver
*/
#define IPA_IOC_ADD_HDR _IOWR(IPA_IOC_MAGIC, \
@@ -2029,6 +2053,9 @@
IPA_IOCTL_CLEANUP)
#define IPA_IOC_QUERY_WLAN_CLIENT _IO(IPA_IOC_MAGIC,\
IPA_IOCTL_QUERY_WLAN_CLIENT)
+#define IPA_IOC_GET_VLAN_MODE _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_GET_VLAN_MODE, \
+ struct ipa_ioc_get_vlan_mode *)
/*
* unique magic number of the Tethering bridge ioctls
*/
diff --git a/include/uapi/linux/netfilter/xt_bpf.h b/include/uapi/linux/netfilter/xt_bpf.h
index 1fad2c2..da161b5 100644
--- a/include/uapi/linux/netfilter/xt_bpf.h
+++ b/include/uapi/linux/netfilter/xt_bpf.h
@@ -2,9 +2,11 @@
#define _XT_BPF_H
#include <linux/filter.h>
+#include <linux/limits.h>
#include <linux/types.h>
#define XT_BPF_MAX_NUM_INSTR 64
+#define XT_BPF_PATH_MAX (XT_BPF_MAX_NUM_INSTR * sizeof(struct sock_filter))
struct bpf_prog;
@@ -16,4 +18,24 @@
struct bpf_prog *filter __attribute__((aligned(8)));
};
+enum xt_bpf_modes {
+ XT_BPF_MODE_BYTECODE,
+ XT_BPF_MODE_FD_PINNED,
+ XT_BPF_MODE_FD_ELF,
+};
+#define XT_BPF_MODE_PATH_PINNED XT_BPF_MODE_FD_PINNED
+
+struct xt_bpf_info_v1 {
+ __u16 mode;
+ __u16 bpf_program_num_elem;
+ __s32 fd;
+ union {
+ struct sock_filter bpf_program[XT_BPF_MAX_NUM_INSTR];
+ char path[XT_BPF_PATH_MAX];
+ };
+
+ /* only used in the kernel */
+ struct bpf_prog *filter __attribute__((aligned(8)));
+};
+
#endif /*_XT_BPF_H */
diff --git a/include/uapi/linux/usb/audio.h b/include/uapi/linux/usb/audio.h
index c6f5b09..6f6d93f 100644
--- a/include/uapi/linux/usb/audio.h
+++ b/include/uapi/linux/usb/audio.h
@@ -370,7 +370,7 @@
{
return (protocol == UAC_VERSION_1) ?
desc->baSourceID[desc->bNrInPins + 4] :
- desc->baSourceID[desc->bNrInPins + 6];
+ 2; /* in UAC2, this value is constant */
}
static inline __u8 *uac_processing_unit_bmControls(struct uac_processing_unit_descriptor *desc,
@@ -378,7 +378,7 @@
{
return (protocol == UAC_VERSION_1) ?
&desc->baSourceID[desc->bNrInPins + 5] :
- &desc->baSourceID[desc->bNrInPins + 7];
+ &desc->baSourceID[desc->bNrInPins + 6];
}
static inline __u8 uac_processing_unit_iProcessing(struct uac_processing_unit_descriptor *desc,
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 0b5ff0f..d14cd8f 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -696,6 +696,7 @@
V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR = 4,
V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR = 5,
V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR = 6,
+ V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CQ = 7,
};
#define V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR \
V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR
@@ -1343,6 +1344,12 @@
V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_6 = 2,
};
+#define V4L2_CID_MPEG_VIDC_VIDEO_FRAME_QUALITY \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE+160)
+
+#define V4L2_CID_MPEG_VIDC_IMG_GRID_DIMENSION \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE+161)
+
enum v4l2_mpeg_vidc_video_mbi_statistics_mode {
V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_DEFAULT = 0,
V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_1 = 1,
diff --git a/include/uapi/linux/vm_bms.h b/include/uapi/linux/vm_bms.h
new file mode 100644
index 0000000..364efc8
--- /dev/null
+++ b/include/uapi/linux/vm_bms.h
@@ -0,0 +1,34 @@
+#ifndef __VM_BMS_H__
+#define __VM_BMS_H__
+
+#define VM_BMS_DEVICE "/dev/vm_bms"
+#define MAX_FIFO_REGS 8
+
+/**
+ * struct qpnp_vm_bms_data - vm-bms data (passed to usersapce)
+ * @data_type: type of data filled up
+ * @num_fifo: count of valid fifo averages
+ * @fifo_uv: array of fifo averages in uv
+ * @sample_interval sample interval of the fifo data in ms
+ * @sample_count total samples in one fifo
+ * @acc_uv averaged accumulator value in uv
+ * @acc_count num of accumulated samples
+ * @seq_num sequence number of the data
+ */
+struct qpnp_vm_bms_data {
+ unsigned int num_fifo;
+ unsigned int fifo_uv[MAX_FIFO_REGS];
+ unsigned int sample_interval_ms;
+ unsigned int sample_count;
+ unsigned int acc_uv;
+ unsigned int acc_count;
+ unsigned int seq_num;
+};
+
+enum vmbms_power_usecase {
+ VMBMS_IGNORE_ALL_BIT = 1,
+ VMBMS_VOICE_CALL_BIT = (1 << 4),
+ VMBMS_STATIC_DISPLAY_BIT = (1 << 5),
+};
+
+#endif /* __VM_BMS_H__ */
diff --git a/init/Kconfig b/init/Kconfig
index 2e57658..854bd5d 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -2267,7 +2267,7 @@
config MODULES_TREE_LOOKUP
def_bool y
- depends on PERF_EVENTS || TRACING
+ depends on PERF_EVENTS || TRACING || CFI_CLANG
config INIT_ALL_POSSIBLE
bool
diff --git a/kernel/Makefile b/kernel/Makefile
index 314e7d6..108f5be 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -32,6 +32,9 @@
# cond_syscall is currently not LTO compatible
CFLAGS_sys_ni.o = $(DISABLE_LTO)
+# Don't instrument error handlers
+CFLAGS_cfi.o = $(DISABLE_CFI_CLANG)
+
obj-y += sched/
obj-y += locking/
obj-y += power/
@@ -101,6 +104,7 @@
obj-$(CONFIG_IRQ_WORK) += irq_work.o
obj-$(CONFIG_CPU_PM) += cpu_pm.o
obj-$(CONFIG_BPF) += bpf/
+obj-$(CONFIG_CFI_CLANG) += cfi.o
obj-$(CONFIG_PERF_EVENTS) += events/
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 2eb9b7f..4db6a67 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -23,8 +23,10 @@
{
int i;
- for (i = 0; i < array->map.max_entries; i++)
+ for (i = 0; i < array->map.max_entries; i++) {
free_percpu(array->pptrs[i]);
+ cond_resched();
+ }
}
static int bpf_array_alloc_percpu(struct bpf_array *array)
@@ -40,6 +42,7 @@
return -ENOMEM;
}
array->pptrs[i] = ptr;
+ cond_resched();
}
return 0;
@@ -51,8 +54,9 @@
bool percpu = attr->map_type == BPF_MAP_TYPE_PERCPU_ARRAY;
u32 elem_size, index_mask, max_entries;
bool unpriv = !capable(CAP_SYS_ADMIN);
+ u64 cost, array_size, mask64;
struct bpf_array *array;
- u64 array_size, mask64;
+ int ret;
/* check sanity of attributes */
if (attr->max_entries == 0 || attr->key_size != 4 ||
@@ -96,8 +100,19 @@
array_size += (u64) max_entries * elem_size;
/* make sure there is no u32 overflow later in round_up() */
- if (array_size >= U32_MAX - PAGE_SIZE)
+ cost = array_size;
+ if (cost >= U32_MAX - PAGE_SIZE)
return ERR_PTR(-ENOMEM);
+ if (percpu) {
+ cost += (u64)attr->max_entries * elem_size * num_possible_cpus();
+ if (cost >= U32_MAX - PAGE_SIZE)
+ return ERR_PTR(-ENOMEM);
+ }
+ cost = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT;
+
+ ret = bpf_map_precharge_memlock(cost);
+ if (ret < 0)
+ return ERR_PTR(ret);
/* allocate all map elements and zero-initialize them */
array = bpf_map_area_alloc(array_size);
@@ -111,20 +126,16 @@
array->map.key_size = attr->key_size;
array->map.value_size = attr->value_size;
array->map.max_entries = attr->max_entries;
+ array->map.map_flags = attr->map_flags;
+ array->map.pages = cost;
array->elem_size = elem_size;
- if (!percpu)
- goto out;
-
- array_size += (u64) attr->max_entries * elem_size * num_possible_cpus();
-
- if (array_size >= U32_MAX - PAGE_SIZE ||
- elem_size > PCPU_MIN_UNIT_SIZE || bpf_array_alloc_percpu(array)) {
+ if (percpu &&
+ (elem_size > PCPU_MIN_UNIT_SIZE ||
+ bpf_array_alloc_percpu(array))) {
bpf_map_area_free(array);
return ERR_PTR(-ENOMEM);
}
-out:
- array->map.pages = round_up(array_size, PAGE_SIZE) >> PAGE_SHIFT;
return &array->map;
}
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 879ca84..66d64db 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -466,7 +466,7 @@
*
* Decode and execute eBPF instructions.
*/
-static unsigned int __bpf_prog_run(void *ctx, const struct bpf_insn *insn)
+static unsigned int __bpf_prog_run(const struct sk_buff *ctx, const struct bpf_insn *insn)
{
u64 stack[MAX_BPF_STACK / sizeof(u64)];
u64 regs[MAX_BPF_REG], tmp;
diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c
index 2d23c32..7e48fa6 100644
--- a/kernel/bpf/inode.c
+++ b/kernel/bpf/inode.c
@@ -322,6 +322,42 @@
return ret;
}
+static struct bpf_prog *__get_prog_inode(struct inode *inode, enum bpf_prog_type type)
+{
+ struct bpf_prog *prog;
+ int ret = inode_permission(inode, MAY_READ | MAY_WRITE);
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (inode->i_op == &bpf_map_iops)
+ return ERR_PTR(-EINVAL);
+ if (inode->i_op != &bpf_prog_iops)
+ return ERR_PTR(-EACCES);
+
+ prog = inode->i_private;
+
+ ret = security_bpf_prog(prog);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ return bpf_prog_inc(prog);
+}
+
+struct bpf_prog *bpf_prog_get_type_path(const char *name, enum bpf_prog_type type)
+{
+ struct bpf_prog *prog;
+ struct path path;
+ int ret = kern_path(name, LOOKUP_FOLLOW, &path);
+ if (ret)
+ return ERR_PTR(ret);
+ prog = __get_prog_inode(d_backing_inode(path.dentry), type);
+ if (!IS_ERR(prog))
+ touch_atime(&path);
+ path_put(&path);
+ return prog;
+}
+EXPORT_SYMBOL(bpf_prog_get_type_path);
+
static void bpf_evict_inode(struct inode *inode)
{
enum bpf_type type;
diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c
index 230aced..6039db3 100644
--- a/kernel/bpf/stackmap.c
+++ b/kernel/bpf/stackmap.c
@@ -91,6 +91,7 @@
smap->map.key_size = attr->key_size;
smap->map.value_size = value_size;
smap->map.max_entries = attr->max_entries;
+ smap->map.map_flags = attr->map_flags;
smap->n_buckets = n_buckets;
smap->map.pages = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT;
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 590ce0a..41aa664 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -970,7 +970,7 @@
union bpf_attr attr = {};
int err;
- if (!capable(CAP_SYS_ADMIN) && sysctl_unprivileged_bpf_disabled)
+ if (sysctl_unprivileged_bpf_disabled && !capable(CAP_SYS_ADMIN))
return -EPERM;
if (!access_ok(VERIFY_READ, uattr, 1))
diff --git a/kernel/cfi.c b/kernel/cfi.c
new file mode 100644
index 0000000..87053e2
--- /dev/null
+++ b/kernel/cfi.c
@@ -0,0 +1,299 @@
+/*
+ * CFI (Control Flow Integrity) error and slowpath handling
+ *
+ * Copyright (C) 2017 Google, Inc.
+ */
+
+#include <linux/gfp.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/rcupdate.h>
+#include <linux/spinlock.h>
+#include <asm/bug.h>
+#include <asm/cacheflush.h>
+#include <asm/memory.h>
+
+/* Compiler-defined handler names */
+#ifdef CONFIG_CFI_PERMISSIVE
+#define cfi_failure_handler __ubsan_handle_cfi_check_fail
+#define cfi_slowpath_handler __cfi_slowpath_diag
+#else /* enforcing */
+#define cfi_failure_handler __ubsan_handle_cfi_check_fail_abort
+#define cfi_slowpath_handler __cfi_slowpath
+#endif /* CONFIG_CFI_PERMISSIVE */
+
+static inline void handle_cfi_failure()
+{
+#ifdef CONFIG_CFI_PERMISSIVE
+ WARN_RATELIMIT(1, "CFI failure:\n");
+#else
+ pr_err("CFI failure:\n");
+ BUG();
+#endif
+}
+
+#ifdef CONFIG_MODULES
+#ifdef CONFIG_CFI_CLANG_SHADOW
+struct shadow_range {
+ /* Module address range */
+ unsigned long mod_min_addr;
+ unsigned long mod_max_addr;
+ /* Module page range */
+ unsigned long min_page;
+ unsigned long max_page;
+};
+
+#define SHADOW_ORDER 1
+#define SHADOW_PAGES (1 << SHADOW_ORDER)
+#define SHADOW_SIZE \
+ ((SHADOW_PAGES * PAGE_SIZE - sizeof(struct shadow_range)) / sizeof(u16))
+#define SHADOW_INVALID 0xFFFF
+
+struct cfi_shadow {
+ /* Page range covered by the shadow */
+ struct shadow_range r;
+ /* Page offsets to __cfi_check functions in modules */
+ u16 shadow[SHADOW_SIZE];
+};
+
+static DEFINE_SPINLOCK(shadow_update_lock);
+static struct cfi_shadow __rcu *cfi_shadow __read_mostly = NULL;
+
+static inline int ptr_to_shadow(const struct cfi_shadow *s, unsigned long ptr)
+{
+ unsigned long index;
+ unsigned long page = ptr >> PAGE_SHIFT;
+
+ if (unlikely(page < s->r.min_page))
+ return -1; /* Outside of module area */
+
+ index = page - s->r.min_page;
+
+ if (index >= SHADOW_SIZE)
+ return -1; /* Cannot be addressed with shadow */
+
+ return (int)index;
+}
+
+static inline unsigned long shadow_to_ptr(const struct cfi_shadow *s,
+ int index)
+{
+ BUG_ON(index < 0 || index >= SHADOW_SIZE);
+
+ if (unlikely(s->shadow[index] == SHADOW_INVALID))
+ return 0;
+
+ return (s->r.min_page + s->shadow[index]) << PAGE_SHIFT;
+}
+
+static void prepare_next_shadow(const struct cfi_shadow __rcu *prev,
+ struct cfi_shadow *next)
+{
+ int i, index, check;
+
+ /* Mark everything invalid */
+ memset(next->shadow, 0xFF, sizeof(next->shadow));
+
+ if (!prev)
+ return; /* No previous shadow */
+
+ /* If the base address didn't change, update is not needed */
+ if (prev->r.min_page == next->r.min_page) {
+ memcpy(next->shadow, prev->shadow, sizeof(next->shadow));
+ return;
+ }
+
+ /* Convert the previous shadow to the new address range */
+ for (i = 0; i < SHADOW_SIZE; ++i) {
+ if (prev->shadow[i] == SHADOW_INVALID)
+ continue;
+
+ index = ptr_to_shadow(next, shadow_to_ptr(prev, i));
+ if (index < 0)
+ continue;
+
+ check = ptr_to_shadow(next,
+ shadow_to_ptr(prev, prev->shadow[i]));
+ if (check < 0)
+ continue;
+
+ next->shadow[index] = (u16)check;
+ }
+}
+
+static void add_module_to_shadow(struct cfi_shadow *s, struct module *mod)
+{
+ unsigned long ptr;
+ unsigned long min_page_addr;
+ unsigned long max_page_addr;
+ unsigned long check = (unsigned long)mod->cfi_check;
+ int check_index = ptr_to_shadow(s, check);
+
+ BUG_ON((check & PAGE_MASK) != check); /* Must be page aligned */
+
+ if (check_index < 0)
+ return; /* Module not addressable with shadow */
+
+ min_page_addr = (unsigned long)mod->core_layout.base & PAGE_MASK;
+ max_page_addr = (unsigned long)mod->core_layout.base +
+ mod->core_layout.text_size;
+ max_page_addr &= PAGE_MASK;
+
+ /* For each page, store the check function index in the shadow */
+ for (ptr = min_page_addr; ptr <= max_page_addr; ptr += PAGE_SIZE) {
+ int index = ptr_to_shadow(s, ptr);
+ if (index >= 0) {
+ /* Assume a page only contains code for one module */
+ BUG_ON(s->shadow[index] != SHADOW_INVALID);
+ s->shadow[index] = (u16)check_index;
+ }
+ }
+}
+
+static void remove_module_from_shadow(struct cfi_shadow *s, struct module *mod)
+{
+ unsigned long ptr;
+ unsigned long min_page_addr;
+ unsigned long max_page_addr;
+
+ min_page_addr = (unsigned long)mod->core_layout.base & PAGE_MASK;
+ max_page_addr = (unsigned long)mod->core_layout.base +
+ mod->core_layout.text_size;
+ max_page_addr &= PAGE_MASK;
+
+ for (ptr = min_page_addr; ptr <= max_page_addr; ptr += PAGE_SIZE) {
+ int index = ptr_to_shadow(s, ptr);
+ if (index >= 0)
+ s->shadow[index] = SHADOW_INVALID;
+ }
+}
+
+typedef void (*update_shadow_fn)(struct cfi_shadow *, struct module *);
+
+static void update_shadow(struct module *mod, unsigned long min_addr,
+ unsigned long max_addr, update_shadow_fn fn)
+{
+ struct cfi_shadow *prev;
+ struct cfi_shadow *next = (struct cfi_shadow *)
+ __get_free_pages(GFP_KERNEL, SHADOW_ORDER);
+
+ BUG_ON(!next);
+
+ next->r.mod_min_addr = min_addr;
+ next->r.mod_max_addr = max_addr;
+ next->r.min_page = min_addr >> PAGE_SHIFT;
+ next->r.max_page = max_addr >> PAGE_SHIFT;
+
+ spin_lock(&shadow_update_lock);
+ prev = rcu_dereference_protected(cfi_shadow, 1);
+ prepare_next_shadow(prev, next);
+
+ fn(next, mod);
+ set_memory_ro((unsigned long)next, SHADOW_PAGES);
+ rcu_assign_pointer(cfi_shadow, next);
+
+ spin_unlock(&shadow_update_lock);
+ synchronize_rcu();
+
+ if (prev) {
+ set_memory_rw((unsigned long)prev, SHADOW_PAGES);
+ free_pages((unsigned long)prev, SHADOW_ORDER);
+ }
+}
+
+void cfi_module_add(struct module *mod, unsigned long min_addr,
+ unsigned long max_addr)
+{
+ update_shadow(mod, min_addr, max_addr, add_module_to_shadow);
+}
+EXPORT_SYMBOL(cfi_module_add);
+
+void cfi_module_remove(struct module *mod, unsigned long min_addr,
+ unsigned long max_addr)
+{
+ update_shadow(mod, min_addr, max_addr, remove_module_from_shadow);
+}
+EXPORT_SYMBOL(cfi_module_remove);
+
+static inline cfi_check_fn ptr_to_check_fn(const struct cfi_shadow __rcu *s,
+ unsigned long ptr)
+{
+ int index;
+ unsigned long check;
+
+ if (unlikely(!s))
+ return NULL; /* No shadow available */
+
+ if (ptr < s->r.mod_min_addr || ptr > s->r.mod_max_addr)
+ return NULL; /* Not in a mapped module */
+
+ index = ptr_to_shadow(s, ptr);
+ if (index < 0)
+ return NULL; /* Cannot be addressed with shadow */
+
+ return (cfi_check_fn)shadow_to_ptr(s, index);
+}
+#endif /* CONFIG_CFI_CLANG_SHADOW */
+
+static inline cfi_check_fn find_module_cfi_check(void *ptr)
+{
+ struct module *mod;
+
+ preempt_disable();
+ mod = __module_address((unsigned long)ptr);
+ preempt_enable();
+
+ if (mod)
+ return mod->cfi_check;
+
+ return CFI_CHECK_FN;
+}
+
+static inline cfi_check_fn find_cfi_check(void *ptr)
+{
+#ifdef CONFIG_CFI_CLANG_SHADOW
+ cfi_check_fn f;
+
+ if (!rcu_access_pointer(cfi_shadow))
+ return CFI_CHECK_FN; /* No loaded modules */
+
+ /* Look up the __cfi_check function to use */
+ rcu_read_lock();
+ f = ptr_to_check_fn(rcu_dereference(cfi_shadow), (unsigned long)ptr);
+ rcu_read_unlock();
+
+ if (f)
+ return f;
+
+ /*
+ * Fall back to find_module_cfi_check, which works also for a larger
+ * module address space, but is slower.
+ */
+#endif /* CONFIG_CFI_CLANG_SHADOW */
+
+ return find_module_cfi_check(ptr);
+}
+
+void cfi_slowpath_handler(uint64_t id, void *ptr, void *diag)
+{
+ cfi_check_fn check = find_cfi_check(ptr);
+
+ if (likely(check))
+ check(id, ptr, diag);
+ else /* Don't allow unchecked modules */
+ handle_cfi_failure();
+}
+EXPORT_SYMBOL(cfi_slowpath_handler);
+#endif /* CONFIG_MODULES */
+
+void cfi_failure_handler(void *data, void *value, void *vtable)
+{
+ handle_cfi_failure();
+}
+EXPORT_SYMBOL(cfi_failure_handler);
+
+void __cfi_check_fail(void *data, void *value)
+{
+ handle_cfi_failure();
+}
diff --git a/kernel/irq/debug.h b/kernel/irq/debug.h
index e75e29e..3514d95 100644
--- a/kernel/irq/debug.h
+++ b/kernel/irq/debug.h
@@ -11,6 +11,11 @@
static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc)
{
+ static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 5);
+
+ if (!__ratelimit(&ratelimit))
+ return;
+
printk("irq %d, desc: %p, depth: %d, count: %d, unhandled: %d\n",
irq, desc, desc->depth, desc->irq_count, desc->irqs_unhandled);
printk("->handle_irq(): %p, ", desc->handle_irq);
diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
index fafd1a3..a0c3365 100644
--- a/kernel/kallsyms.c
+++ b/kernel/kallsyms.c
@@ -306,6 +306,24 @@
return !!module_address_lookup(addr, symbolsize, offset, NULL, namebuf);
}
+#ifdef CONFIG_CFI_CLANG
+/*
+ * LLVM appends .cfi to function names when CONFIG_CFI_CLANG is enabled,
+ * which causes confusion and potentially breaks user space tools, so we
+ * will strip the postfix from expanded symbol names.
+ */
+static inline void cleanup_symbol_name(char *s)
+{
+ char *res;
+
+ res = strrchr(s, '.');
+ if (res && !strcmp(res, ".cfi"))
+ *res = '\0';
+}
+#else
+static inline void cleanup_symbol_name(char *s) {}
+#endif
+
/*
* Lookup an address
* - modname is set to NULL if it's in the kernel.
@@ -330,16 +348,23 @@
namebuf, KSYM_NAME_LEN);
if (modname)
*modname = NULL;
- return namebuf;
+ goto found;
}
/* See if it's in a module. */
- return module_address_lookup(addr, symbolsize, offset, modname,
- namebuf);
+ if (!module_address_lookup(addr, symbolsize, offset, modname,
+ namebuf))
+ return NULL;
+
+found:
+ cleanup_symbol_name(namebuf);
+ return namebuf;
}
int lookup_symbol_name(unsigned long addr, char *symname)
{
+ int res;
+
symname[0] = '\0';
symname[KSYM_NAME_LEN - 1] = '\0';
@@ -350,15 +375,23 @@
/* Grab name */
kallsyms_expand_symbol(get_symbol_offset(pos),
symname, KSYM_NAME_LEN);
- return 0;
+ goto found;
}
/* See if it's in a module. */
- return lookup_module_symbol_name(addr, symname);
+ res = lookup_module_symbol_name(addr, symname);
+ if (res)
+ return res;
+
+found:
+ cleanup_symbol_name(symname);
+ return 0;
}
int lookup_symbol_attrs(unsigned long addr, unsigned long *size,
unsigned long *offset, char *modname, char *name)
{
+ int res;
+
name[0] = '\0';
name[KSYM_NAME_LEN - 1] = '\0';
@@ -370,10 +403,16 @@
kallsyms_expand_symbol(get_symbol_offset(pos),
name, KSYM_NAME_LEN);
modname[0] = '\0';
- return 0;
+ goto found;
}
/* See if it's in a module. */
- return lookup_module_symbol_attrs(addr, size, offset, modname, name);
+ res = lookup_module_symbol_attrs(addr, size, offset, modname, name);
+ if (res)
+ return res;
+
+found:
+ cleanup_symbol_name(name);
+ return 0;
}
/* Look up a kernel symbol and return it in a text buffer. */
diff --git a/kernel/locking/locktorture.c b/kernel/locking/locktorture.c
index d3de04b..babc67c 100644
--- a/kernel/locking/locktorture.c
+++ b/kernel/locking/locktorture.c
@@ -641,8 +641,7 @@
{
bool fail = 0;
int i, n_stress;
- long max = 0;
- long min = statp[0].n_lock_acquired;
+ long max = 0, min = statp ? statp[0].n_lock_acquired : 0;
long long sum = 0;
n_stress = write ? cxt.nrealwriters_stress : cxt.nrealreaders_stress;
@@ -749,7 +748,7 @@
* such, only perform the underlying torture-specific cleanups,
* and avoid anything related to locktorture.
*/
- if (!cxt.lwsa)
+ if (!cxt.lwsa && !cxt.lrsa)
goto end;
if (writer_tasks) {
@@ -823,6 +822,13 @@
firsterr = -EINVAL;
goto unwind;
}
+
+ if (nwriters_stress == 0 && nreaders_stress == 0) {
+ pr_alert("lock-torture: must run at least one locking thread\n");
+ firsterr = -EINVAL;
+ goto unwind;
+ }
+
if (cxt.cur_ops->init)
cxt.cur_ops->init();
@@ -846,17 +852,19 @@
#endif
/* Initialize the statistics so that each run gets its own numbers. */
+ if (nwriters_stress) {
+ lock_is_write_held = 0;
+ cxt.lwsa = kmalloc(sizeof(*cxt.lwsa) * cxt.nrealwriters_stress, GFP_KERNEL);
+ if (cxt.lwsa == NULL) {
+ VERBOSE_TOROUT_STRING("cxt.lwsa: Out of memory");
+ firsterr = -ENOMEM;
+ goto unwind;
+ }
- lock_is_write_held = 0;
- cxt.lwsa = kmalloc(sizeof(*cxt.lwsa) * cxt.nrealwriters_stress, GFP_KERNEL);
- if (cxt.lwsa == NULL) {
- VERBOSE_TOROUT_STRING("cxt.lwsa: Out of memory");
- firsterr = -ENOMEM;
- goto unwind;
- }
- for (i = 0; i < cxt.nrealwriters_stress; i++) {
- cxt.lwsa[i].n_lock_fail = 0;
- cxt.lwsa[i].n_lock_acquired = 0;
+ for (i = 0; i < cxt.nrealwriters_stress; i++) {
+ cxt.lwsa[i].n_lock_fail = 0;
+ cxt.lwsa[i].n_lock_acquired = 0;
+ }
}
if (cxt.cur_ops->readlock) {
@@ -873,19 +881,21 @@
cxt.nrealreaders_stress = cxt.nrealwriters_stress;
}
- lock_is_read_held = 0;
- cxt.lrsa = kmalloc(sizeof(*cxt.lrsa) * cxt.nrealreaders_stress, GFP_KERNEL);
- if (cxt.lrsa == NULL) {
- VERBOSE_TOROUT_STRING("cxt.lrsa: Out of memory");
- firsterr = -ENOMEM;
- kfree(cxt.lwsa);
- cxt.lwsa = NULL;
- goto unwind;
- }
+ if (nreaders_stress) {
+ lock_is_read_held = 0;
+ cxt.lrsa = kmalloc(sizeof(*cxt.lrsa) * cxt.nrealreaders_stress, GFP_KERNEL);
+ if (cxt.lrsa == NULL) {
+ VERBOSE_TOROUT_STRING("cxt.lrsa: Out of memory");
+ firsterr = -ENOMEM;
+ kfree(cxt.lwsa);
+ cxt.lwsa = NULL;
+ goto unwind;
+ }
- for (i = 0; i < cxt.nrealreaders_stress; i++) {
- cxt.lrsa[i].n_lock_fail = 0;
- cxt.lrsa[i].n_lock_acquired = 0;
+ for (i = 0; i < cxt.nrealreaders_stress; i++) {
+ cxt.lrsa[i].n_lock_fail = 0;
+ cxt.lrsa[i].n_lock_acquired = 0;
+ }
}
}
@@ -915,12 +925,14 @@
goto unwind;
}
- writer_tasks = kzalloc(cxt.nrealwriters_stress * sizeof(writer_tasks[0]),
- GFP_KERNEL);
- if (writer_tasks == NULL) {
- VERBOSE_TOROUT_ERRSTRING("writer_tasks: Out of memory");
- firsterr = -ENOMEM;
- goto unwind;
+ if (nwriters_stress) {
+ writer_tasks = kzalloc(cxt.nrealwriters_stress * sizeof(writer_tasks[0]),
+ GFP_KERNEL);
+ if (writer_tasks == NULL) {
+ VERBOSE_TOROUT_ERRSTRING("writer_tasks: Out of memory");
+ firsterr = -ENOMEM;
+ goto unwind;
+ }
}
if (cxt.cur_ops->readlock) {
diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
index 2c49d76..196cc46 100644
--- a/kernel/locking/rtmutex.c
+++ b/kernel/locking/rtmutex.c
@@ -236,8 +236,7 @@
* then right waiter has a dl_prio() too.
*/
if (dl_prio(left->prio))
- return dl_time_before(left->task->dl.deadline,
- right->task->dl.deadline);
+ return dl_time_before(left->deadline, right->deadline);
return 0;
}
@@ -704,7 +703,26 @@
/* [7] Requeue the waiter in the lock waiter tree. */
rt_mutex_dequeue(lock, waiter);
+
+ /*
+ * Update the waiter prio fields now that we're dequeued.
+ *
+ * These values can have changed through either:
+ *
+ * sys_sched_set_scheduler() / sys_sched_setattr()
+ *
+ * or
+ *
+ * DL CBS enforcement advancing the effective deadline.
+ *
+ * Even though pi_waiters also uses these fields, and that tree is only
+ * updated in [11], we can do this here, since we hold [L], which
+ * serializes all pi_waiters access and rb_erase() does not care about
+ * the values of the node being removed.
+ */
waiter->prio = task->prio;
+ waiter->deadline = task->dl.deadline;
+
rt_mutex_enqueue(lock, waiter);
/* [8] Release the task */
@@ -831,6 +849,8 @@
static int try_to_take_rt_mutex(struct rt_mutex *lock, struct task_struct *task,
struct rt_mutex_waiter *waiter)
{
+ lockdep_assert_held(&lock->wait_lock);
+
/*
* Before testing whether we can acquire @lock, we set the
* RT_MUTEX_HAS_WAITERS bit in @lock->owner. This forces all
@@ -958,6 +978,8 @@
struct rt_mutex *next_lock;
int chain_walk = 0, res;
+ lockdep_assert_held(&lock->wait_lock);
+
/*
* Early deadlock detection. We really don't want the task to
* enqueue on itself just to untangle the mess later. It's not
@@ -975,6 +997,7 @@
waiter->task = task;
waiter->lock = lock;
waiter->prio = task->prio;
+ waiter->deadline = task->dl.deadline;
/* Get the top priority waiter on the lock */
if (rt_mutex_has_waiters(lock))
@@ -1080,6 +1103,8 @@
struct task_struct *owner = rt_mutex_owner(lock);
struct rt_mutex *next_lock;
+ lockdep_assert_held(&lock->wait_lock);
+
raw_spin_lock(¤t->pi_lock);
rt_mutex_dequeue(lock, waiter);
current->pi_blocked_on = NULL;
diff --git a/kernel/locking/rtmutex_common.h b/kernel/locking/rtmutex_common.h
index e317e1c..50848b4 100644
--- a/kernel/locking/rtmutex_common.h
+++ b/kernel/locking/rtmutex_common.h
@@ -33,6 +33,7 @@
struct rt_mutex *deadlock_lock;
#endif
int prio;
+ u64 deadline;
};
/*
diff --git a/kernel/memremap.c b/kernel/memremap.c
index 426547a..f61a8c3 100644
--- a/kernel/memremap.c
+++ b/kernel/memremap.c
@@ -194,7 +194,7 @@
}
EXPORT_SYMBOL(put_zone_device_page);
-static void pgmap_radix_release(struct resource *res)
+static void pgmap_radix_release(struct resource *res, resource_size_t end_key)
{
resource_size_t key, align_start, align_size, align_end;
@@ -203,8 +203,11 @@
align_end = align_start + align_size - 1;
mutex_lock(&pgmap_lock);
- for (key = res->start; key <= res->end; key += SECTION_SIZE)
+ for (key = res->start; key <= res->end; key += SECTION_SIZE) {
+ if (key >= end_key)
+ break;
radix_tree_delete(&pgmap_radix, key >> PA_SECTION_SHIFT);
+ }
mutex_unlock(&pgmap_lock);
}
@@ -255,7 +258,7 @@
unlock_device_hotplug();
untrack_pfn(NULL, PHYS_PFN(align_start), align_size);
- pgmap_radix_release(res);
+ pgmap_radix_release(res, -1);
dev_WARN_ONCE(dev, pgmap->altmap && pgmap->altmap->alloc,
"%s: failed to free all reserved pages\n", __func__);
}
@@ -289,7 +292,7 @@
void *devm_memremap_pages(struct device *dev, struct resource *res,
struct percpu_ref *ref, struct vmem_altmap *altmap)
{
- resource_size_t key, align_start, align_size, align_end;
+ resource_size_t key = 0, align_start, align_size, align_end;
pgprot_t pgprot = PAGE_KERNEL;
struct dev_pagemap *pgmap;
struct page_map *page_map;
@@ -392,7 +395,7 @@
untrack_pfn(NULL, PHYS_PFN(align_start), align_size);
err_pfn_remap:
err_radix:
- pgmap_radix_release(res);
+ pgmap_radix_release(res, key);
devres_free(page_map);
return ERR_PTR(error);
}
diff --git a/kernel/module.c b/kernel/module.c
index 07bfb99..dec20c7 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2085,6 +2085,8 @@
{
}
+static void cfi_cleanup(struct module *mod);
+
/* Free a module, remove from lists, etc. */
static void free_module(struct module *mod)
{
@@ -2126,6 +2128,10 @@
/* This may be empty, but that's OK */
disable_ro_nx(&mod->init_layout);
+
+ /* Clean up CFI for the module. */
+ cfi_cleanup(mod);
+
module_arch_freeing_init(mod);
module_memfree(mod->init_layout.base);
kfree(mod->args);
@@ -3307,6 +3313,8 @@
return 0;
}
+static void cfi_init(struct module *mod);
+
static int post_relocation(struct module *mod, const struct load_info *info)
{
/* Sort exception table now relocations are done. */
@@ -3319,6 +3327,9 @@
/* Setup kallsyms-specific fields. */
add_kallsyms(mod, info);
+ /* Setup CFI for the module. */
+ cfi_init(mod);
+
/* Arch-specific module finalizing. */
return module_finalize(info->hdr, info->sechdrs, mod);
}
@@ -4053,6 +4064,22 @@
}
#endif /* CONFIG_KALLSYMS */
+static void cfi_init(struct module *mod)
+{
+#ifdef CONFIG_CFI_CLANG
+ mod->cfi_check =
+ (cfi_check_fn)mod_find_symname(mod, CFI_CHECK_FN_NAME);
+ cfi_module_add(mod, module_addr_min, module_addr_max);
+#endif
+}
+
+static void cfi_cleanup(struct module *mod)
+{
+#ifdef CONFIG_CFI_CLANG
+ cfi_module_remove(mod, module_addr_min, module_addr_max);
+#endif
+}
+
static char *module_flags(struct module *mod, char *buf)
{
int bx = 0;
diff --git a/kernel/printk/braille.c b/kernel/printk/braille.c
index d5760c4..61d41ca 100644
--- a/kernel/printk/braille.c
+++ b/kernel/printk/braille.c
@@ -2,12 +2,13 @@
#include <linux/kernel.h>
#include <linux/console.h>
+#include <linux/errno.h>
#include <linux/string.h>
#include "console_cmdline.h"
#include "braille.h"
-char *_braille_console_setup(char **str, char **brl_options)
+int _braille_console_setup(char **str, char **brl_options)
{
if (!strncmp(*str, "brl,", 4)) {
*brl_options = "";
@@ -15,14 +16,14 @@
} else if (!strncmp(*str, "brl=", 4)) {
*brl_options = *str + 4;
*str = strchr(*brl_options, ',');
- if (!*str)
+ if (!*str) {
pr_err("need port name after brl=\n");
- else
- *((*str)++) = 0;
- } else
- return NULL;
+ return -EINVAL;
+ }
+ *((*str)++) = 0;
+ }
- return *str;
+ return 0;
}
int
diff --git a/kernel/printk/braille.h b/kernel/printk/braille.h
index 769d771..749a675 100644
--- a/kernel/printk/braille.h
+++ b/kernel/printk/braille.h
@@ -9,7 +9,14 @@
c->brl_options = brl_options;
}
-char *
+/*
+ * Setup console according to braille options.
+ * Return -EINVAL on syntax error, 0 on success (or no braille option was
+ * actually given).
+ * Modifies str to point to the serial options
+ * Sets brl_options to the parsed braille options.
+ */
+int
_braille_console_setup(char **str, char **brl_options);
int
@@ -25,10 +32,10 @@
{
}
-static inline char *
+static inline int
_braille_console_setup(char **str, char **brl_options)
{
- return NULL;
+ return 0;
}
static inline int
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 20fc294..3012f95 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2350,7 +2350,7 @@
}
/*
- * Console drivers are called under logbuf_lock, so
+ * Console drivers are called with interrupts disabled, so
* @console_may_schedule should be cleared before; however, we may
* end up dumping a lot of lines, for example, if called from
* console registration path, and should invoke cond_resched()
@@ -2358,11 +2358,15 @@
* scheduling stall on a slow console leading to RCU stall and
* softlockup warnings which exacerbate the issue with more
* messages practically incapacitating the system.
+ *
+ * console_trylock() is not able to detect the preemptive
+ * context reliably. Therefore the value must be stored before
+ * and cleared after the the "again" goto label.
*/
do_cond_resched = console_may_schedule;
+again:
console_may_schedule = 0;
-again:
/*
* We released the console_sem lock, so we need to recheck if
* cpu is online and (if not) is there at least one CON_ANYTIME
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index ef34eb5..467801c 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -526,7 +526,8 @@
unsigned long flags;
raw_spin_lock_irqsave(&rq->lock, flags);
- resched_curr(rq);
+ if (cpu_online(cpu) || cpu == smp_processor_id())
+ resched_curr(rq);
raw_spin_unlock_irqrestore(&rq->lock, flags);
}
diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c
index c0a8a2a..26c9cf4 100644
--- a/kernel/sched/core_ctl.c
+++ b/kernel/sched/core_ctl.c
@@ -588,7 +588,12 @@
if (new_need > cluster->active_cpus) {
ret = 1;
} else {
- if (new_need == last_need) {
+ /*
+ * When there is no change in need and there are no more
+ * active CPUs than currently needed, just update the
+ * need time stamp and return.
+ */
+ if (new_need == last_need && new_need == cluster->active_cpus) {
cluster->need_ts = now;
spin_unlock_irqrestore(&state_lock, flags);
return 0;
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index e6abbb4..7a32e5a 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -2492,7 +2492,7 @@
if (tsk_nr_cpus_allowed(p) > 1 && rq->rt.overloaded)
queue_push_tasks(rq);
#endif /* CONFIG_SMP */
- if (p->prio < rq->curr->prio)
+ if (p->prio < rq->curr->prio && cpu_online(cpu_of(rq)))
resched_curr(rq);
}
}
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index 38b008e..11e4af2f 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -1113,7 +1113,12 @@
cpu_base = raw_cpu_ptr(&hrtimer_bases);
- if (clock_id == CLOCK_REALTIME && mode != HRTIMER_MODE_ABS)
+ /*
+ * POSIX magic: Relative CLOCK_REALTIME timers are not affected by
+ * clock modifications, so they needs to become CLOCK_MONOTONIC to
+ * ensure POSIX compliance.
+ */
+ if (clock_id == CLOCK_REALTIME && mode & HRTIMER_MODE_REL)
clock_id = CLOCK_MONOTONIC;
base = hrtimer_clockid_to_base(clock_id);
diff --git a/kernel/time/posix-clock.c b/kernel/time/posix-clock.c
index 9cff0ab..e24008c 100644
--- a/kernel/time/posix-clock.c
+++ b/kernel/time/posix-clock.c
@@ -300,14 +300,17 @@
static int pc_clock_gettime(clockid_t id, struct timespec *ts)
{
struct posix_clock_desc cd;
+ struct timespec64 ts64;
int err;
err = get_clock_desc(id, &cd);
if (err)
return err;
- if (cd.clk->ops.clock_gettime)
- err = cd.clk->ops.clock_gettime(cd.clk, ts);
+ if (cd.clk->ops.clock_gettime) {
+ err = cd.clk->ops.clock_gettime(cd.clk, &ts64);
+ *ts = timespec64_to_timespec(ts64);
+ }
else
err = -EOPNOTSUPP;
@@ -319,14 +322,17 @@
static int pc_clock_getres(clockid_t id, struct timespec *ts)
{
struct posix_clock_desc cd;
+ struct timespec64 ts64;
int err;
err = get_clock_desc(id, &cd);
if (err)
return err;
- if (cd.clk->ops.clock_getres)
- err = cd.clk->ops.clock_getres(cd.clk, ts);
+ if (cd.clk->ops.clock_getres) {
+ err = cd.clk->ops.clock_getres(cd.clk, &ts64);
+ *ts = timespec64_to_timespec(ts64);
+ }
else
err = -EOPNOTSUPP;
@@ -337,6 +343,7 @@
static int pc_clock_settime(clockid_t id, const struct timespec *ts)
{
+ struct timespec64 ts64 = timespec_to_timespec64(*ts);
struct posix_clock_desc cd;
int err;
@@ -350,7 +357,7 @@
}
if (cd.clk->ops.clock_settime)
- err = cd.clk->ops.clock_settime(cd.clk, ts);
+ err = cd.clk->ops.clock_settime(cd.clk, &ts64);
else
err = -EOPNOTSUPP;
out:
@@ -403,29 +410,36 @@
{
clockid_t id = kit->it_clock;
struct posix_clock_desc cd;
+ struct itimerspec64 ts64;
if (get_clock_desc(id, &cd))
return;
- if (cd.clk->ops.timer_gettime)
- cd.clk->ops.timer_gettime(cd.clk, kit, ts);
-
+ if (cd.clk->ops.timer_gettime) {
+ cd.clk->ops.timer_gettime(cd.clk, kit, &ts64);
+ *ts = itimerspec64_to_itimerspec(&ts64);
+ }
put_clock_desc(&cd);
}
static int pc_timer_settime(struct k_itimer *kit, int flags,
struct itimerspec *ts, struct itimerspec *old)
{
+ struct itimerspec64 ts64 = itimerspec_to_itimerspec64(ts);
clockid_t id = kit->it_clock;
struct posix_clock_desc cd;
+ struct itimerspec64 old64;
int err;
err = get_clock_desc(id, &cd);
if (err)
return err;
- if (cd.clk->ops.timer_settime)
- err = cd.clk->ops.timer_settime(cd.clk, kit, flags, ts, old);
+ if (cd.clk->ops.timer_settime) {
+ err = cd.clk->ops.timer_settime(cd.clk, kit, flags, &ts64, &old64);
+ if (old)
+ *old = itimerspec64_to_itimerspec(&old64);
+ }
else
err = -EOPNOTSUPP;
diff --git a/kernel/time/sched_clock.c b/kernel/time/sched_clock.c
index bad8a6b..4776585 100644
--- a/kernel/time/sched_clock.c
+++ b/kernel/time/sched_clock.c
@@ -208,6 +208,11 @@
update_clock_read_data(&rd);
+ if (sched_clock_timer.function != NULL) {
+ /* update timeout for clock wrap */
+ hrtimer_start(&sched_clock_timer, cd.wrap_kt, HRTIMER_MODE_REL);
+ }
+
r = rate;
if (r >= 4000000) {
r /= 1000000;
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 41481dc..d397432 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -1902,6 +1902,12 @@
spin_lock_irqsave(&new_base->lock, flags);
spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING);
+ /*
+ * The current CPUs base clock might be stale. Update it
+ * before moving the timers over.
+ */
+ forward_timer_base(new_base);
+
if (!cpu_online(cpu))
BUG_ON(old_base->running_timer);
diff --git a/kernel/time/timer_list.c b/kernel/time/timer_list.c
index 83aa1f8..998d730 100644
--- a/kernel/time/timer_list.c
+++ b/kernel/time/timer_list.c
@@ -16,6 +16,7 @@
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/kallsyms.h>
+#include <linux/nmi.h>
#include <asm/uaccess.h>
@@ -86,6 +87,9 @@
next_one:
i = 0;
+
+ touch_nmi_watchdog();
+
raw_spin_lock_irqsave(&base->cpu_base->lock, flags);
curr = timerqueue_getnext(&base->active);
@@ -197,6 +201,8 @@
{
struct clock_event_device *dev = td->evtdev;
+ touch_nmi_watchdog();
+
SEQ_printf(m, "Tick Device: mode: %d\n", td->mode);
if (cpu < 0)
SEQ_printf(m, "Broadcast device\n");
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 2884fe0..063dd22 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -4874,9 +4874,9 @@
return 0;
}
-static int ftrace_process_locs(struct module *mod,
- unsigned long *start,
- unsigned long *end)
+static int __norecordmcount ftrace_process_locs(struct module *mod,
+ unsigned long *start,
+ unsigned long *end)
{
struct ftrace_page *start_pg;
struct ftrace_page *pg;
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 5ff45ca..ea3ed03 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -607,7 +607,7 @@
bool is_return = false, is_delete = false;
char *symbol = NULL, *event = NULL, *group = NULL;
char *arg;
- unsigned long offset = 0;
+ long offset = 0;
void *addr = NULL;
char buf[MAX_EVENT_NAME_LEN];
@@ -675,7 +675,7 @@
symbol = argv[1];
/* TODO: support .init module functions */
ret = traceprobe_split_symbol_offset(symbol, &offset);
- if (ret) {
+ if (ret || offset < 0 || offset > UINT_MAX) {
pr_info("Failed to parse either an address or a symbol.\n");
return ret;
}
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index 8c0553d..5ea191b 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -319,7 +319,7 @@
}
/* Split symbol and offset. */
-int traceprobe_split_symbol_offset(char *symbol, unsigned long *offset)
+int traceprobe_split_symbol_offset(char *symbol, long *offset)
{
char *tmp;
int ret;
@@ -327,13 +327,11 @@
if (!offset)
return -EINVAL;
- tmp = strchr(symbol, '+');
+ tmp = strpbrk(symbol, "+-");
if (tmp) {
- /* skip sign because kstrtoul doesn't accept '+' */
- ret = kstrtoul(tmp + 1, 0, offset);
+ ret = kstrtol(tmp, 0, offset);
if (ret)
return ret;
-
*tmp = '\0';
} else
*offset = 0;
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index 0c0ae54..2b84c0d 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -354,7 +354,7 @@
extern void traceprobe_update_arg(struct probe_arg *arg);
extern void traceprobe_free_probe_arg(struct probe_arg *arg);
-extern int traceprobe_split_symbol_offset(char *symbol, unsigned long *offset);
+extern int traceprobe_split_symbol_offset(char *symbol, long *offset);
extern ssize_t traceprobe_probes_write(struct file *file,
const char __user *buffer, size_t count, loff_t *ppos,
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 5ef61bd..04c2289 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -4137,6 +4137,22 @@
EXPORT_SYMBOL_GPL(workqueue_set_max_active);
/**
+ * current_work - retrieve %current task's work struct
+ *
+ * Determine if %current task is a workqueue worker and what it's working on.
+ * Useful to find out the context that the %current task is running in.
+ *
+ * Return: work struct if %current task is a workqueue worker, %NULL otherwise.
+ */
+struct work_struct *current_work(void)
+{
+ struct worker *worker = current_wq_worker();
+
+ return worker ? worker->current_work : NULL;
+}
+EXPORT_SYMBOL(current_work);
+
+/**
* current_is_workqueue_rescuer - is %current workqueue rescuer?
*
* Determine whether %current is a workqueue rescuer. Can be used from
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index 056052dc..19572a4 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -293,10 +293,13 @@
return;
limit++;
- if (is_on_stack)
- pr_warn("object is on stack, but not annotated\n");
- else
- pr_warn("object is not on stack, but annotated\n");
+ if (is_on_stack) {
+ pr_warn("object %p is on stack %p, but NOT annotated\n", addr,
+ task_stack_page(current));
+ } else {
+ pr_warn("object %p is NOT on stack %p, but annotated\n", addr,
+ task_stack_page(current));
+ }
WARN_ON(1);
}
diff --git a/lib/ioremap.c b/lib/ioremap.c
index 86c8911..5323b59 100644
--- a/lib/ioremap.c
+++ b/lib/ioremap.c
@@ -83,7 +83,8 @@
if (ioremap_pmd_enabled() &&
((next - addr) == PMD_SIZE) &&
- IS_ALIGNED(phys_addr + addr, PMD_SIZE)) {
+ IS_ALIGNED(phys_addr + addr, PMD_SIZE) &&
+ pmd_free_pte_page(pmd)) {
if (pmd_set_huge(pmd, phys_addr + addr, prot))
continue;
}
@@ -109,7 +110,8 @@
if (ioremap_pud_enabled() &&
((next - addr) == PUD_SIZE) &&
- IS_ALIGNED(phys_addr + addr, PUD_SIZE)) {
+ IS_ALIGNED(phys_addr + addr, PUD_SIZE) &&
+ pud_free_pmd_page(pud)) {
if (pud_set_huge(pud, phys_addr + addr, prot))
continue;
}
diff --git a/lib/mpi/longlong.h b/lib/mpi/longlong.h
index 9333650..0f64fce 100644
--- a/lib/mpi/longlong.h
+++ b/lib/mpi/longlong.h
@@ -671,7 +671,23 @@
************** MIPS/64 **************
***************************************/
#if (defined(__mips) && __mips >= 3) && W_TYPE_SIZE == 64
-#if (__GNUC__ >= 5) || (__GNUC__ >= 4 && __GNUC_MINOR__ >= 4)
+#if defined(__mips_isa_rev) && __mips_isa_rev >= 6
+/*
+ * GCC ends up emitting a __multi3 intrinsic call for MIPS64r6 with the plain C
+ * code below, so we special case MIPS64r6 until the compiler can do better.
+ */
+#define umul_ppmm(w1, w0, u, v) \
+do { \
+ __asm__ ("dmulu %0,%1,%2" \
+ : "=d" ((UDItype)(w0)) \
+ : "d" ((UDItype)(u)), \
+ "d" ((UDItype)(v))); \
+ __asm__ ("dmuhu %0,%1,%2" \
+ : "=d" ((UDItype)(w1)) \
+ : "d" ((UDItype)(u)), \
+ "d" ((UDItype)(v))); \
+} while (0)
+#elif (__GNUC__ >= 5) || (__GNUC__ >= 4 && __GNUC_MINOR__ >= 4)
#define umul_ppmm(w1, w0, u, v) \
do { \
typedef unsigned int __ll_UTItype __attribute__((mode(TI))); \
diff --git a/lib/rhashtable.c b/lib/rhashtable.c
index 32d0ad0..895961c 100644
--- a/lib/rhashtable.c
+++ b/lib/rhashtable.c
@@ -448,8 +448,10 @@
if (!key ||
(ht->p.obj_cmpfn ?
ht->p.obj_cmpfn(&arg, rht_obj(ht, head)) :
- rhashtable_compare(&arg, rht_obj(ht, head))))
+ rhashtable_compare(&arg, rht_obj(ht, head)))) {
+ pprev = &head->next;
continue;
+ }
if (!ht->rhlist)
return rht_obj(ht, head);
diff --git a/lib/test_kasan.c b/lib/test_kasan.c
index 0e70ecc..8fcad61 100644
--- a/lib/test_kasan.c
+++ b/lib/test_kasan.c
@@ -440,6 +440,26 @@
p[1023] = 1;
}
+static noinline void __init kasan_alloca_oob_left(void)
+{
+ volatile int i = 10;
+ char alloca_array[i];
+ char *p = alloca_array - 1;
+
+ pr_info("out-of-bounds to left on alloca\n");
+ *(volatile char *)p;
+}
+
+static noinline void __init kasan_alloca_oob_right(void)
+{
+ volatile int i = 10;
+ char alloca_array[i];
+ char *p = alloca_array + i;
+
+ pr_info("out-of-bounds to right on alloca\n");
+ *(volatile char *)p;
+}
+
static int __init kmalloc_tests_init(void)
{
/*
@@ -469,6 +489,8 @@
kmem_cache_oob();
kasan_stack_oob();
kasan_global_oob();
+ kasan_alloca_oob_left();
+ kasan_alloca_oob_right();
ksize_unpoisons_memory();
copy_user_test();
use_after_scope_test();
diff --git a/mm/filemap.c b/mm/filemap.c
index b4c09ec..03c79ef 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -2390,7 +2390,7 @@
static struct page *do_read_cache_page(struct address_space *mapping,
pgoff_t index,
- int (*filler)(void *, struct page *),
+ int (*filler)(struct file *, struct page *),
void *data,
gfp_t gfp)
{
@@ -2497,7 +2497,7 @@
*/
struct page *read_cache_page(struct address_space *mapping,
pgoff_t index,
- int (*filler)(void *, struct page *),
+ int (*filler)(struct file *, struct page *),
void *data)
{
return do_read_cache_page(mapping, index, filler, data, mapping_gfp_mask(mapping));
@@ -2519,7 +2519,7 @@
pgoff_t index,
gfp_t gfp)
{
- filler_t *filler = (filler_t *)mapping->a_ops->readpage;
+ filler_t *filler = mapping->a_ops->readpage;
return do_read_cache_page(mapping, index, filler, NULL, gfp);
}
diff --git a/mm/frame_vector.c b/mm/frame_vector.c
index db77dcb..375a103 100644
--- a/mm/frame_vector.c
+++ b/mm/frame_vector.c
@@ -52,6 +52,18 @@
ret = -EFAULT;
goto out;
}
+
+ /*
+ * While get_vaddr_frames() could be used for transient (kernel
+ * controlled lifetime) pinning of memory pages all current
+ * users establish long term (userspace controlled lifetime)
+ * page pinning. Treat get_vaddr_frames() like
+ * get_user_pages_longterm() and disallow it for filesystem-dax
+ * mappings.
+ */
+ if (vma_is_fsdax(vma))
+ return -EOPNOTSUPP;
+
if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) {
vec->got_ref = true;
vec->is_pfns = false;
diff --git a/mm/gup.c b/mm/gup.c
index c63a034..6c3b4e8 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -982,6 +982,70 @@
}
EXPORT_SYMBOL(get_user_pages);
+#ifdef CONFIG_FS_DAX
+/*
+ * This is the same as get_user_pages() in that it assumes we are
+ * operating on the current task's mm, but it goes further to validate
+ * that the vmas associated with the address range are suitable for
+ * longterm elevated page reference counts. For example, filesystem-dax
+ * mappings are subject to the lifetime enforced by the filesystem and
+ * we need guarantees that longterm users like RDMA and V4L2 only
+ * establish mappings that have a kernel enforced revocation mechanism.
+ *
+ * "longterm" == userspace controlled elevated page count lifetime.
+ * Contrast this to iov_iter_get_pages() usages which are transient.
+ */
+long get_user_pages_longterm(unsigned long start, unsigned long nr_pages,
+ unsigned int gup_flags, struct page **pages,
+ struct vm_area_struct **vmas_arg)
+{
+ struct vm_area_struct **vmas = vmas_arg;
+ struct vm_area_struct *vma_prev = NULL;
+ long rc, i;
+
+ if (!pages)
+ return -EINVAL;
+
+ if (!vmas) {
+ vmas = kcalloc(nr_pages, sizeof(struct vm_area_struct *),
+ GFP_KERNEL);
+ if (!vmas)
+ return -ENOMEM;
+ }
+
+ rc = get_user_pages(start, nr_pages, gup_flags, pages, vmas);
+
+ for (i = 0; i < rc; i++) {
+ struct vm_area_struct *vma = vmas[i];
+
+ if (vma == vma_prev)
+ continue;
+
+ vma_prev = vma;
+
+ if (vma_is_fsdax(vma))
+ break;
+ }
+
+ /*
+ * Either get_user_pages() failed, or the vma validation
+ * succeeded, in either case we don't need to put_page() before
+ * returning.
+ */
+ if (i >= rc)
+ goto out;
+
+ for (i = 0; i < rc; i++)
+ put_page(pages[i]);
+ rc = -EOPNOTSUPP;
+out:
+ if (vmas != vmas_arg)
+ kfree(vmas);
+ return rc;
+}
+EXPORT_SYMBOL(get_user_pages_longterm);
+#endif /* CONFIG_FS_DAX */
+
/**
* populate_vma_page_range() - populate a range of pages in the vma.
* @vma: target vma
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index c234c07..e2982ea 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -2279,11 +2279,13 @@
list_for_each_safe(pos, next, &list) {
page = list_entry((void *)pos, struct page, mapping);
- lock_page(page);
+ if (!trylock_page(page))
+ goto next;
/* split_huge_page() removes page from list on success */
if (!split_huge_page(page))
split++;
unlock_page(page);
+next:
put_page(page);
}
diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c
index 7a6d1a1..ab47d93 100644
--- a/mm/kasan/kasan.c
+++ b/mm/kasan/kasan.c
@@ -794,6 +794,55 @@
}
EXPORT_SYMBOL(__asan_unpoison_stack_memory);
+/* Emitted by compiler to poison alloca()ed objects. */
+void __asan_alloca_poison(unsigned long addr, size_t size)
+{
+ size_t rounded_up_size = round_up(size, KASAN_SHADOW_SCALE_SIZE);
+ size_t padding_size = round_up(size, KASAN_ALLOCA_REDZONE_SIZE) -
+ rounded_up_size;
+ size_t rounded_down_size = round_down(size, KASAN_SHADOW_SCALE_SIZE);
+
+ const void *left_redzone = (const void *)(addr -
+ KASAN_ALLOCA_REDZONE_SIZE);
+ const void *right_redzone = (const void *)(addr + rounded_up_size);
+
+ WARN_ON(!IS_ALIGNED(addr, KASAN_ALLOCA_REDZONE_SIZE));
+
+ kasan_unpoison_shadow((const void *)(addr + rounded_down_size),
+ size - rounded_down_size);
+ kasan_poison_shadow(left_redzone, KASAN_ALLOCA_REDZONE_SIZE,
+ KASAN_ALLOCA_LEFT);
+ kasan_poison_shadow(right_redzone,
+ padding_size + KASAN_ALLOCA_REDZONE_SIZE,
+ KASAN_ALLOCA_RIGHT);
+}
+EXPORT_SYMBOL(__asan_alloca_poison);
+
+/* Emitted by compiler to unpoison alloca()ed areas when the stack unwinds. */
+void __asan_allocas_unpoison(const void *stack_top, const void *stack_bottom)
+{
+ if (unlikely(!stack_top || stack_top > stack_bottom))
+ return;
+
+ kasan_unpoison_shadow(stack_top, stack_bottom - stack_top);
+}
+EXPORT_SYMBOL(__asan_allocas_unpoison);
+
+/* Emitted by the compiler to [un]poison local variables. */
+#define DEFINE_ASAN_SET_SHADOW(byte) \
+ void __asan_set_shadow_##byte(const void *addr, size_t size) \
+ { \
+ __memset((void *)addr, 0x##byte, size); \
+ } \
+ EXPORT_SYMBOL(__asan_set_shadow_##byte)
+
+DEFINE_ASAN_SET_SHADOW(00);
+DEFINE_ASAN_SET_SHADOW(f1);
+DEFINE_ASAN_SET_SHADOW(f2);
+DEFINE_ASAN_SET_SHADOW(f3);
+DEFINE_ASAN_SET_SHADOW(f5);
+DEFINE_ASAN_SET_SHADOW(f8);
+
#ifdef CONFIG_MEMORY_HOTPLUG
static int kasan_mem_notifier(struct notifier_block *nb,
unsigned long action, void *data)
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index 1229298..d9cf9e2 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -23,6 +23,14 @@
#define KASAN_STACK_PARTIAL 0xF4
#define KASAN_USE_AFTER_SCOPE 0xF8
+/*
+ * alloca redzone shadow values
+ */
+#define KASAN_ALLOCA_LEFT 0xCA
+#define KASAN_ALLOCA_RIGHT 0xCB
+
+#define KASAN_ALLOCA_REDZONE_SIZE 32
+
/* Don't break randconfig/all*config builds */
#ifndef KASAN_ABI_VERSION
#define KASAN_ABI_VERSION 1
@@ -112,4 +120,48 @@
static inline void quarantine_remove_cache(struct kmem_cache *cache) { }
#endif
+/*
+ * Exported functions for interfaces called from assembly or from generated
+ * code. Declarations here to avoid warning about missing declarations.
+ */
+asmlinkage void kasan_unpoison_task_stack_below(const void *watermark);
+void __asan_register_globals(struct kasan_global *globals, size_t size);
+void __asan_unregister_globals(struct kasan_global *globals, size_t size);
+void __asan_loadN(unsigned long addr, size_t size);
+void __asan_storeN(unsigned long addr, size_t size);
+void __asan_handle_no_return(void);
+void __asan_poison_stack_memory(const void *addr, size_t size);
+void __asan_unpoison_stack_memory(const void *addr, size_t size);
+void __asan_alloca_poison(unsigned long addr, size_t size);
+void __asan_allocas_unpoison(const void *stack_top, const void *stack_bottom);
+
+void __asan_load1(unsigned long addr);
+void __asan_store1(unsigned long addr);
+void __asan_load2(unsigned long addr);
+void __asan_store2(unsigned long addr);
+void __asan_load4(unsigned long addr);
+void __asan_store4(unsigned long addr);
+void __asan_load8(unsigned long addr);
+void __asan_store8(unsigned long addr);
+void __asan_load16(unsigned long addr);
+void __asan_store16(unsigned long addr);
+
+void __asan_load1_noabort(unsigned long addr);
+void __asan_store1_noabort(unsigned long addr);
+void __asan_load2_noabort(unsigned long addr);
+void __asan_store2_noabort(unsigned long addr);
+void __asan_load4_noabort(unsigned long addr);
+void __asan_store4_noabort(unsigned long addr);
+void __asan_load8_noabort(unsigned long addr);
+void __asan_store8_noabort(unsigned long addr);
+void __asan_load16_noabort(unsigned long addr);
+void __asan_store16_noabort(unsigned long addr);
+
+void __asan_set_shadow_00(const void *addr, size_t size);
+void __asan_set_shadow_f1(const void *addr, size_t size);
+void __asan_set_shadow_f2(const void *addr, size_t size);
+void __asan_set_shadow_f3(const void *addr, size_t size);
+void __asan_set_shadow_f5(const void *addr, size_t size);
+void __asan_set_shadow_f8(const void *addr, size_t size);
+
#endif
diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index 0b8cf43..5f2b9eb 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c
@@ -102,6 +102,10 @@
case KASAN_USE_AFTER_SCOPE:
bug_type = "use-after-scope";
break;
+ case KASAN_ALLOCA_LEFT:
+ case KASAN_ALLOCA_RIGHT:
+ bug_type = "alloca-out-of-bounds";
+ break;
}
return bug_type;
diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index 5d7c006..898eb26 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -528,7 +528,12 @@
goto out;
}
- VM_BUG_ON_PAGE(PageCompound(page), page);
+ /* TODO: teach khugepaged to collapse THP mapped with pte */
+ if (PageCompound(page)) {
+ result = SCAN_PAGE_COMPOUND;
+ goto out;
+ }
+
VM_BUG_ON_PAGE(!PageAnon(page), page);
VM_BUG_ON_PAGE(!PageSwapBacked(page), page);
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 43622c6..351d94a 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -921,6 +921,7 @@
int ret;
int kill = 1, forcekill;
struct page *hpage = *hpagep;
+ bool mlocked = PageMlocked(hpage);
/*
* Here we are interested only in user-mapped pages, so skip any
@@ -985,6 +986,13 @@
pfn, page_mapcount(hpage));
/*
+ * try_to_unmap() might put mlocked page in lru cache, so call
+ * shake_page() again to ensure that it's flushed.
+ */
+ if (mlocked)
+ shake_page(hpage, 0);
+
+ /*
* Now that the dirty bit has been propagated to the
* struct page and all unmaps done we can decide if
* killing is needed or not. Only kill when the page
diff --git a/mm/memory.c b/mm/memory.c
index f882280..cc6ab38 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -2842,6 +2842,17 @@
return ret;
}
+/*
+ * The ordering of these checks is important for pmds with _PAGE_DEVMAP set.
+ * If we check pmd_trans_unstable() first we will trip the bad_pmd() check
+ * inside of pmd_none_or_trans_huge_or_clear_bad(). This will end up correctly
+ * returning 1 but not before it spams dmesg with the pmd_clear_bad() output.
+ */
+static int pmd_devmap_trans_unstable(pmd_t *pmd)
+{
+ return pmd_devmap(*pmd) || pmd_trans_unstable(pmd);
+}
+
static int pte_alloc_one_map(struct fault_env *fe)
{
struct vm_area_struct *vma = fe->vma;
@@ -2865,18 +2876,27 @@
map_pte:
/*
* If a huge pmd materialized under us just retry later. Use
- * pmd_trans_unstable() instead of pmd_trans_huge() to ensure the pmd
- * didn't become pmd_trans_huge under us and then back to pmd_none, as
- * a result of MADV_DONTNEED running immediately after a huge pmd fault
- * in a different thread of this mm, in turn leading to a misleading
- * pmd_trans_huge() retval. All we have to ensure is that it is a
- * regular pmd that we can walk with pte_offset_map() and we can do that
- * through an atomic read in C, which is what pmd_trans_unstable()
- * provides.
+ * pmd_trans_unstable() via pmd_devmap_trans_unstable() instead of
+ * pmd_trans_huge() to ensure the pmd didn't become pmd_trans_huge
+ * under us and then back to pmd_none, as a result of MADV_DONTNEED
+ * running immediately after a huge pmd fault in a different thread of
+ * this mm, in turn leading to a misleading pmd_trans_huge() retval.
+ * All we have to ensure is that it is a regular pmd that we can walk
+ * with pte_offset_map() and we can do that through an atomic read in
+ * C, which is what pmd_trans_unstable() provides.
*/
- if (pmd_trans_unstable(fe->pmd) || pmd_devmap(*fe->pmd))
+ if (pmd_devmap_trans_unstable(fe->pmd))
return VM_FAULT_NOPAGE;
+ /*
+ * At this point we know that our vmf->pmd points to a page of ptes
+ * and it cannot become pmd_none(), pmd_devmap() or pmd_trans_huge()
+ * for the duration of the fault. If a racing MADV_DONTNEED runs and
+ * we zap the ptes pointed to by our vmf->pmd, the vmf->ptl will still
+ * be valid and we will re-check to make sure the vmf->pte isn't
+ * pte_none() under vmf->ptl protection when we return to
+ * alloc_set_pte().
+ */
fe->pte = pte_offset_map_lock(vma->vm_mm, fe->pmd, fe->address,
&fe->ptl);
return 0;
@@ -3450,7 +3470,7 @@
fe->pte = NULL;
} else {
/* See comment in pte_alloc_one_map() */
- if (pmd_trans_unstable(fe->pmd) || pmd_devmap(*fe->pmd))
+ if (pmd_devmap_trans_unstable(fe->pmd))
return 0;
/*
* A regular pmd is established and it can't morph into a huge
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index d3ea11f..b74f30e 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -2402,8 +2402,9 @@
* The CPU has to be pinned. When zone parameter is non-NULL, spill just
* the single zone's pages.
*/
-void drain_local_pages(struct zone *zone)
+void drain_local_pages(void *z)
{
+ struct zone *zone = (struct zone *)z;
int cpu = smp_processor_id();
if (zone)
@@ -2463,8 +2464,7 @@
else
cpumask_clear_cpu(cpu, &cpus_with_pcps);
}
- on_each_cpu_mask(&cpus_with_pcps, (smp_call_func_t) drain_local_pages,
- zone, 1);
+ on_each_cpu_mask(&cpus_with_pcps, drain_local_pages, zone, 1);
}
#ifdef CONFIG_HIBERNATION
diff --git a/mm/readahead.c b/mm/readahead.c
index 7dc48ba..8b2a755 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -81,7 +81,7 @@
* Hides the details of the LRU cache etc from the filesystems.
*/
int read_cache_pages(struct address_space *mapping, struct list_head *pages,
- int (*filler)(void *, struct page *), void *data)
+ int (*filler)(struct file *, struct page *), void *data)
{
struct page *page;
int ret = 0;
diff --git a/mm/shmem.c b/mm/shmem.c
index 0a6ac6a..61a39aa 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -466,36 +466,45 @@
info = list_entry(pos, struct shmem_inode_info, shrinklist);
inode = &info->vfs_inode;
- if (nr_to_split && split >= nr_to_split) {
- iput(inode);
- continue;
- }
+ if (nr_to_split && split >= nr_to_split)
+ goto leave;
- page = find_lock_page(inode->i_mapping,
+ page = find_get_page(inode->i_mapping,
(inode->i_size & HPAGE_PMD_MASK) >> PAGE_SHIFT);
if (!page)
goto drop;
+ /* No huge page at the end of the file: nothing to split */
if (!PageTransHuge(page)) {
- unlock_page(page);
put_page(page);
goto drop;
}
+ /*
+ * Leave the inode on the list if we failed to lock
+ * the page at this time.
+ *
+ * Waiting for the lock may lead to deadlock in the
+ * reclaim path.
+ */
+ if (!trylock_page(page)) {
+ put_page(page);
+ goto leave;
+ }
+
ret = split_huge_page(page);
unlock_page(page);
put_page(page);
- if (ret) {
- /* split failed: leave it on the list */
- iput(inode);
- continue;
- }
+ /* If split failed leave the inode on the list */
+ if (ret)
+ goto leave;
split++;
drop:
list_del_init(&info->shrinklist);
removed++;
+leave:
iput(inode);
}
diff --git a/mm/vmstat.c b/mm/vmstat.c
index 26f0e49..f97fb09 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -1390,18 +1390,24 @@
zone->present_pages,
zone->managed_pages);
- for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
- seq_printf(m, "\n %-12s %lu", vmstat_text[i],
- zone_page_state(zone, i));
-
seq_printf(m,
"\n protection: (%ld",
zone->lowmem_reserve[0]);
for (i = 1; i < ARRAY_SIZE(zone->lowmem_reserve); i++)
seq_printf(m, ", %ld", zone->lowmem_reserve[i]);
- seq_printf(m,
- ")"
- "\n pagesets");
+ seq_putc(m, ')');
+
+ /* If unpopulated, no other information is useful */
+ if (!populated_zone(zone)) {
+ seq_putc(m, '\n');
+ return;
+ }
+
+ for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
+ seq_printf(m, "\n %-12s %lu", vmstat_text[i],
+ zone_page_state(zone, i));
+
+ seq_printf(m, "\n pagesets");
for_each_online_cpu(i) {
struct per_cpu_pageset *pageset;
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index fbfacd5..7671441 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -562,8 +562,7 @@
NETIF_F_HIGHDMA | NETIF_F_SCTP_CRC |
NETIF_F_ALL_FCOE;
- dev->features |= real_dev->vlan_features | NETIF_F_LLTX |
- NETIF_F_GSO_SOFTWARE;
+ dev->features |= dev->hw_features | NETIF_F_LLTX;
dev->gso_max_size = real_dev->gso_max_size;
dev->gso_max_segs = real_dev->gso_max_segs;
if (dev->features & NETIF_F_VLAN_FEATURES)
diff --git a/net/Kconfig b/net/Kconfig
index 0b8c255..78694c4 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -295,6 +295,7 @@
bool "enable BPF Just In Time compiler"
depends on HAVE_CBPF_JIT || HAVE_EBPF_JIT
depends on MODULES
+ depends on !CFI
---help---
Berkeley Packet Filter filtering capabilities are normally handled
by an interpreter. This option allows kernel to generate a native
diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index e7f690b..5419b12 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -1964,10 +1964,22 @@
/* if yes, the client has roamed and we have
* to unclaim it.
*/
- batadv_handle_unclaim(bat_priv, primary_if,
- primary_if->net_dev->dev_addr,
- ethhdr->h_source, vid);
- goto allow;
+ if (batadv_has_timed_out(claim->lasttime, 100)) {
+ /* only unclaim if the last claim entry is
+ * older than 100 ms to make sure we really
+ * have a roaming client here.
+ */
+ batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_tx(): Roaming client %pM detected. Unclaim it.\n",
+ ethhdr->h_source);
+ batadv_handle_unclaim(bat_priv, primary_if,
+ primary_if->net_dev->dev_addr,
+ ethhdr->h_source, vid);
+ goto allow;
+ } else {
+ batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_tx(): Race for claim %pM detected. Drop packet.\n",
+ ethhdr->h_source);
+ goto handled;
+ }
}
/* check if it is a multicast/broadcast frame */
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 1904a93..de7b82e 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -755,7 +755,8 @@
}
static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
- struct lowpan_btle_dev *dev)
+ struct lowpan_btle_dev *dev,
+ bool new_netdev)
{
struct lowpan_peer *peer;
@@ -786,7 +787,8 @@
spin_unlock(&devices_lock);
/* Notifying peers about us needs to be done without locks held */
- INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
+ if (new_netdev)
+ INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100));
return peer->chan;
@@ -843,6 +845,7 @@
static inline void chan_ready_cb(struct l2cap_chan *chan)
{
struct lowpan_btle_dev *dev;
+ bool new_netdev = false;
dev = lookup_dev(chan->conn);
@@ -853,12 +856,13 @@
l2cap_chan_del(chan, -ENOENT);
return;
}
+ new_netdev = true;
}
if (!try_module_get(THIS_MODULE))
return;
- add_peer_chan(chan, dev);
+ add_peer_chan(chan, dev, new_netdev);
ifup(dev->netdev);
}
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 4b32525..ec313c9 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -192,6 +192,9 @@
}
EXPORT_SYMBOL(bt_accept_enqueue);
+/* Calling function must hold the sk lock.
+ * bt_sk(sk)->parent must be non-NULL meaning sk is in the parent list.
+ */
void bt_accept_unlink(struct sock *sk)
{
BT_DBG("sk %p state %d", sk, sk->sk_state);
@@ -210,11 +213,32 @@
BT_DBG("parent %p", parent);
+restart:
list_for_each_entry_safe(s, n, &bt_sk(parent)->accept_q, accept_q) {
sk = (struct sock *)s;
+ /* Prevent early freeing of sk due to unlink and sock_kill */
+ sock_hold(sk);
lock_sock(sk);
+ /* Check sk has not already been unlinked via
+ * bt_accept_unlink() due to serialisation caused by sk locking
+ */
+ if (!bt_sk(sk)->parent) {
+ BT_DBG("sk %p, already unlinked", sk);
+ release_sock(sk);
+ sock_put(sk);
+
+ /* Restart the loop as sk is no longer in the list
+ * and also avoid a potential infinite loop because
+ * list_for_each_entry_safe() is not thread safe.
+ */
+ goto restart;
+ }
+
+ /* sk is safely in the parent list so reduce reference count */
+ sock_put(sk);
+
/* FIXME: Is this check still needed */
if (sk->sk_state == BT_CLOSED) {
bt_accept_unlink(sk);
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index 8bd5696..abf7111 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -230,6 +230,9 @@
struct brport_attribute *brport_attr = to_brport_attr(attr);
struct net_bridge_port *p = to_brport(kobj);
+ if (!brport_attr->show)
+ return -EINVAL;
+
return brport_attr->show(p, buf);
}
diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c
index 9024283..9637a68 100644
--- a/net/bridge/netfilter/ebt_among.c
+++ b/net/bridge/netfilter/ebt_among.c
@@ -172,18 +172,35 @@
return true;
}
+static bool poolsize_invalid(const struct ebt_mac_wormhash *w)
+{
+ return w && w->poolsize >= (INT_MAX / sizeof(struct ebt_mac_wormhash_tuple));
+}
+
static int ebt_among_mt_check(const struct xt_mtchk_param *par)
{
const struct ebt_among_info *info = par->matchinfo;
const struct ebt_entry_match *em =
container_of(par->matchinfo, const struct ebt_entry_match, data);
- int expected_length = sizeof(struct ebt_among_info);
+ unsigned int expected_length = sizeof(struct ebt_among_info);
const struct ebt_mac_wormhash *wh_dst, *wh_src;
int err;
+ if (expected_length > em->match_size)
+ return -EINVAL;
+
wh_dst = ebt_among_wh_dst(info);
- wh_src = ebt_among_wh_src(info);
+ if (poolsize_invalid(wh_dst))
+ return -EINVAL;
+
expected_length += ebt_mac_wormhash_size(wh_dst);
+ if (expected_length > em->match_size)
+ return -EINVAL;
+
+ wh_src = ebt_among_wh_src(info);
+ if (poolsize_invalid(wh_src))
+ return -EINVAL;
+
expected_length += ebt_mac_wormhash_size(wh_src);
if (em->match_size != EBT_ALIGN(expected_length)) {
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index f5c11bb..5a89a4a 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -2031,7 +2031,9 @@
if (match_kern)
match_kern->match_size = ret;
- WARN_ON(type == EBT_COMPAT_TARGET && size_left);
+ if (WARN_ON(type == EBT_COMPAT_TARGET && size_left))
+ return -EINVAL;
+
match32 = (struct compat_ebt_entry_mwt *) buf;
}
@@ -2087,6 +2089,15 @@
*
* offsets are relative to beginning of struct ebt_entry (i.e., 0).
*/
+ for (i = 0; i < 4 ; ++i) {
+ if (offsets[i] >= *total)
+ return -EINVAL;
+ if (i == 0)
+ continue;
+ if (offsets[i-1] > offsets[i])
+ return -EINVAL;
+ }
+
for (i = 0, j = 1 ; j < 4 ; j++, i++) {
struct compat_ebt_entry_mwt *match32;
unsigned int size;
diff --git a/net/core/dev.c b/net/core/dev.c
index 72fb37c..fe1dc21 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2201,8 +2201,11 @@
*/
int netif_set_real_num_tx_queues(struct net_device *dev, unsigned int txq)
{
+ bool disabling;
int rc;
+ disabling = txq < dev->real_num_tx_queues;
+
if (txq < 1 || txq > dev->num_tx_queues)
return -EINVAL;
@@ -2218,15 +2221,19 @@
if (dev->num_tc)
netif_setup_tc(dev, txq);
- if (txq < dev->real_num_tx_queues) {
+ dev->real_num_tx_queues = txq;
+
+ if (disabling) {
+ synchronize_net();
qdisc_reset_all_tx_gt(dev, txq);
#ifdef CONFIG_XPS
netif_reset_xps_queues_gt(dev, txq);
#endif
}
+ } else {
+ dev->real_num_tx_queues = txq;
}
- dev->real_num_tx_queues = txq;
return 0;
}
EXPORT_SYMBOL(netif_set_real_num_tx_queues);
@@ -3228,15 +3235,23 @@
#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)
static void skb_update_prio(struct sk_buff *skb)
{
- struct netprio_map *map = rcu_dereference_bh(skb->dev->priomap);
+ const struct netprio_map *map;
+ const struct sock *sk;
+ unsigned int prioidx;
- if (!skb->priority && skb->sk && map) {
- unsigned int prioidx =
- sock_cgroup_prioidx(&skb->sk->sk_cgrp_data);
+ if (skb->priority)
+ return;
+ map = rcu_dereference_bh(skb->dev->priomap);
+ if (!map)
+ return;
+ sk = skb_to_full_sk(skb);
+ if (!sk)
+ return;
- if (prioidx < map->priomap_len)
- skb->priority = map->priomap[prioidx];
- }
+ prioidx = sock_cgroup_prioidx(&sk->sk_cgrp_data);
+
+ if (prioidx < map->priomap_len)
+ skb->priority = map->priomap[prioidx];
}
#else
#define skb_update_prio(skb)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 26ef78a..2cc4a1b 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3723,7 +3723,7 @@
skb_queue_tail(&sk->sk_error_queue, skb);
if (!sock_flag(sk, SOCK_DEAD))
- sk->sk_data_ready(sk);
+ sk->sk_error_report(sk);
return 0;
}
EXPORT_SYMBOL(sock_queue_err_skb);
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 9d43c1f..ff3b058 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -789,6 +789,11 @@
if (skb == NULL)
goto out_release;
+ if (sk->sk_state == DCCP_CLOSED) {
+ rc = -ENOTCONN;
+ goto out_discard;
+ }
+
skb_reserve(skb, sk->sk_prot->max_header);
rc = memcpy_from_msg(skb_put(skb, len), msg, len);
if (rc != 0)
diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
index d7efbf0..83af533 100644
--- a/net/ieee802154/6lowpan/core.c
+++ b/net/ieee802154/6lowpan/core.c
@@ -204,9 +204,13 @@
static int lowpan_device_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
- struct net_device *wdev = netdev_notifier_info_to_dev(ptr);
+ struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
+ struct wpan_dev *wpan_dev;
- if (wdev->type != ARPHRD_IEEE802154)
+ if (ndev->type != ARPHRD_IEEE802154)
+ return NOTIFY_DONE;
+ wpan_dev = ndev->ieee802154_ptr;
+ if (!wpan_dev)
return NOTIFY_DONE;
switch (event) {
@@ -215,8 +219,8 @@
* also delete possible lowpan interfaces which belongs
* to the wpan interface.
*/
- if (wdev->ieee802154_ptr->lowpan_dev)
- lowpan_dellink(wdev->ieee802154_ptr->lowpan_dev, NULL);
+ if (wpan_dev->lowpan_dev)
+ lowpan_dellink(wpan_dev->lowpan_dev, NULL);
break;
default:
return NOTIFY_DONE;
diff --git a/net/ipc_router/ipc_router_core.c b/net/ipc_router/ipc_router_core.c
index f3c81c2..3403108 100644
--- a/net/ipc_router/ipc_router_core.c
+++ b/net/ipc_router/ipc_router_core.c
@@ -223,6 +223,25 @@
is_wakeup_source_allowed = flag;
}
+/**
+ * is_sensor_port() - Check if the remote port is sensor service or not
+ * @rport: Pointer to the remote port.
+ *
+ * Return: true if the remote port is sensor service else false.
+ */
+static int is_sensor_port(struct msm_ipc_router_remote_port *rport)
+{
+ u32 svcid = 0;
+
+ if (rport && rport->server) {
+ svcid = rport->server->name.service;
+ if (svcid == 400 || (svcid >= 256 && svcid <= 320))
+ return true;
+ }
+
+ return false;
+}
+
static void init_routing_table(void)
{
int i;
@@ -2729,7 +2748,6 @@
struct rr_packet *pkt = NULL;
struct msm_ipc_port *port_ptr;
struct msm_ipc_router_remote_port *rport_ptr;
- int ret;
struct msm_ipc_router_xprt_info *xprt_info =
container_of(work,
@@ -2737,16 +2755,7 @@
read_data);
while ((pkt = rr_read(xprt_info)) != NULL) {
- if (pkt->length < calc_rx_header_size(xprt_info) ||
- pkt->length > MAX_IPC_PKT_SIZE) {
- IPC_RTR_ERR("%s: Invalid pkt length %d\n", __func__,
- pkt->length);
- goto read_next_pkt1;
- }
- ret = extract_header(pkt);
- if (ret < 0)
- goto read_next_pkt1;
hdr = &pkt->hdr;
if ((hdr->dst_node_id != IPC_ROUTER_NID_LOCAL) &&
@@ -4173,6 +4182,7 @@
{
struct msm_ipc_router_xprt_info *xprt_info = xprt->priv;
struct msm_ipc_router_xprt_work *xprt_work;
+ struct msm_ipc_router_remote_port *rport_ptr = NULL;
struct rr_packet *pkt;
int ret;
@@ -4223,16 +4233,40 @@
if (!pkt)
return;
+ if (pkt->length < calc_rx_header_size(xprt_info) ||
+ pkt->length > MAX_IPC_PKT_SIZE) {
+ IPC_RTR_ERR("%s: Invalid pkt length %d\n",
+ __func__, pkt->length);
+ release_pkt(pkt);
+ return;
+ }
+
+ ret = extract_header(pkt);
+ if (ret < 0) {
+ release_pkt(pkt);
+ return;
+ }
+
pkt->ws_need = false;
+
+ if (pkt->hdr.type == IPC_ROUTER_CTRL_CMD_DATA)
+ rport_ptr = ipc_router_get_rport_ref(pkt->hdr.src_node_id,
+ pkt->hdr.src_port_id);
+
mutex_lock(&xprt_info->rx_lock_lhb2);
list_add_tail(&pkt->list, &xprt_info->pkt_list);
- if (!xprt_info->dynamic_ws) {
- __pm_stay_awake(&xprt_info->ws);
- pkt->ws_need = true;
- } else {
- if (is_wakeup_source_allowed) {
+ /* check every pkt is from SENSOR services or not and
+ * avoid holding both edge and port specific wake-up sources
+ */
+ if (!is_sensor_port(rport_ptr)) {
+ if (!xprt_info->dynamic_ws) {
__pm_stay_awake(&xprt_info->ws);
pkt->ws_need = true;
+ } else {
+ if (is_wakeup_source_allowed) {
+ __pm_stay_awake(&xprt_info->ws);
+ pkt->ws_need = true;
+ }
}
}
mutex_unlock(&xprt_info->rx_lock_lhb2);
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 38c1c97..7e7b7a3 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -640,6 +640,11 @@
fi->fib_nh, cfg))
return 1;
}
+#ifdef CONFIG_IP_ROUTE_CLASSID
+ if (cfg->fc_flow &&
+ cfg->fc_flow != fi->fib_nh->nh_tclassid)
+ return 1;
+#endif
if ((!cfg->fc_oif || cfg->fc_oif == fi->fib_nh->nh_oif) &&
(!cfg->fc_gw || cfg->fc_gw == fi->fib_nh->nh_gw))
return 0;
diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
index 631c0d0..8effac0 100644
--- a/net/ipv4/inet_fragment.c
+++ b/net/ipv4/inet_fragment.c
@@ -119,6 +119,9 @@
static bool inet_fragq_should_evict(const struct inet_frag_queue *q)
{
+ if (!hlist_unhashed(&q->list_evictor))
+ return false;
+
return q->net->low_thresh == 0 ||
frag_mem_limit(q->net) >= q->net->low_thresh;
}
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index bf62fa4..5ddd649 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -242,7 +242,8 @@
src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
if (!ipv6_addr_v4mapped(&src_info->ipi6_addr))
return -EINVAL;
- ipc->oif = src_info->ipi6_ifindex;
+ if (src_info->ipi6_ifindex)
+ ipc->oif = src_info->ipi6_ifindex;
ipc->addr = src_info->ipi6_addr.s6_addr32[3];
continue;
}
@@ -272,7 +273,8 @@
if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo)))
return -EINVAL;
info = (struct in_pktinfo *)CMSG_DATA(cmsg);
- ipc->oif = info->ipi_ifindex;
+ if (info->ipi_ifindex)
+ ipc->oif = info->ipi_ifindex;
ipc->addr = info->ipi_spec_dst.s_addr;
break;
}
@@ -1552,10 +1554,7 @@
if (get_user(len, optlen))
return -EFAULT;
- lock_sock(sk);
- err = nf_getsockopt(sk, PF_INET, optname, optval,
- &len);
- release_sock(sk);
+ err = nf_getsockopt(sk, PF_INET, optname, optval, &len);
if (err >= 0)
err = put_user(len, optlen);
return err;
@@ -1587,9 +1586,7 @@
if (get_user(len, optlen))
return -EFAULT;
- lock_sock(sk);
err = compat_nf_getsockopt(sk, PF_INET, optname, optval, &len);
- release_sock(sk);
if (err >= 0)
err = put_user(len, optlen);
return err;
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
index 6975384..d35815e 100644
--- a/net/ipv4/netfilter/arp_tables.c
+++ b/net/ipv4/netfilter/arp_tables.c
@@ -261,6 +261,10 @@
}
if (table_base + v
!= arpt_next_entry(e)) {
+ if (unlikely(stackidx >= private->stacksize)) {
+ verdict = NF_DROP;
+ break;
+ }
jumpstack[stackidx++] = e;
}
@@ -415,17 +419,15 @@
}
static inline int
-find_check_entry(struct arpt_entry *e, const char *name, unsigned int size)
+find_check_entry(struct arpt_entry *e, const char *name, unsigned int size,
+ struct xt_percpu_counter_alloc_state *alloc_state)
{
struct xt_entry_target *t;
struct xt_target *target;
- unsigned long pcnt;
int ret;
- pcnt = xt_percpu_counter_alloc();
- if (IS_ERR_VALUE(pcnt))
+ if (!xt_percpu_counter_alloc(alloc_state, &e->counters))
return -ENOMEM;
- e->counters.pcnt = pcnt;
t = arpt_get_target(e);
target = xt_request_find_target(NFPROTO_ARP, t->u.user.name,
@@ -443,7 +445,7 @@
err:
module_put(t->u.kernel.target->me);
out:
- xt_percpu_counter_free(e->counters.pcnt);
+ xt_percpu_counter_free(&e->counters);
return ret;
}
@@ -523,7 +525,7 @@
if (par.target->destroy != NULL)
par.target->destroy(&par);
module_put(par.target->me);
- xt_percpu_counter_free(e->counters.pcnt);
+ xt_percpu_counter_free(&e->counters);
}
/* Checks and translates the user-supplied table segment (held in
@@ -532,6 +534,7 @@
static int translate_table(struct xt_table_info *newinfo, void *entry0,
const struct arpt_replace *repl)
{
+ struct xt_percpu_counter_alloc_state alloc_state = { 0 };
struct arpt_entry *iter;
unsigned int *offsets;
unsigned int i;
@@ -594,7 +597,8 @@
/* Finally, each sanity check must pass */
i = 0;
xt_entry_foreach(iter, entry0, newinfo->size) {
- ret = find_check_entry(iter, repl->name, repl->size);
+ ret = find_check_entry(iter, repl->name, repl->size,
+ &alloc_state);
if (ret != 0)
break;
++i;
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index 7c00ce9..e78f652 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -345,8 +345,13 @@
continue;
}
if (table_base + v != ipt_next_entry(e) &&
- !(e->ip.flags & IPT_F_GOTO))
+ !(e->ip.flags & IPT_F_GOTO)) {
+ if (unlikely(stackidx >= private->stacksize)) {
+ verdict = NF_DROP;
+ break;
+ }
jumpstack[stackidx++] = e;
+ }
e = get_entry(table_base, v);
continue;
@@ -535,7 +540,8 @@
static int
find_check_entry(struct ipt_entry *e, struct net *net, const char *name,
- unsigned int size)
+ unsigned int size,
+ struct xt_percpu_counter_alloc_state *alloc_state)
{
struct xt_entry_target *t;
struct xt_target *target;
@@ -543,12 +549,9 @@
unsigned int j;
struct xt_mtchk_param mtpar;
struct xt_entry_match *ematch;
- unsigned long pcnt;
- pcnt = xt_percpu_counter_alloc();
- if (IS_ERR_VALUE(pcnt))
+ if (!xt_percpu_counter_alloc(alloc_state, &e->counters))
return -ENOMEM;
- e->counters.pcnt = pcnt;
j = 0;
mtpar.net = net;
@@ -586,7 +589,7 @@
cleanup_match(ematch, net);
}
- xt_percpu_counter_free(e->counters.pcnt);
+ xt_percpu_counter_free(&e->counters);
return ret;
}
@@ -674,7 +677,7 @@
if (par.target->destroy != NULL)
par.target->destroy(&par);
module_put(par.target->me);
- xt_percpu_counter_free(e->counters.pcnt);
+ xt_percpu_counter_free(&e->counters);
}
/* Checks and translates the user-supplied table segment (held in
@@ -683,6 +686,7 @@
translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0,
const struct ipt_replace *repl)
{
+ struct xt_percpu_counter_alloc_state alloc_state = { 0 };
struct ipt_entry *iter;
unsigned int *offsets;
unsigned int i;
@@ -742,7 +746,8 @@
/* Finally, each sanity check must pass */
i = 0;
xt_entry_foreach(iter, entry0, newinfo->size) {
- ret = find_check_entry(iter, net, repl->name, repl->size);
+ ret = find_check_entry(iter, net, repl->name, repl->size,
+ &alloc_state);
if (ret != 0)
break;
++i;
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 03728c6..e83fdf6 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -126,10 +126,13 @@
static int ip_rt_error_cost __read_mostly = HZ;
static int ip_rt_error_burst __read_mostly = 5 * HZ;
static int ip_rt_mtu_expires __read_mostly = 10 * 60 * HZ;
-static int ip_rt_min_pmtu __read_mostly = 512 + 20 + 20;
+static u32 ip_rt_min_pmtu __read_mostly = 512 + 20 + 20;
static int ip_rt_min_advmss __read_mostly = 256;
static int ip_rt_gc_timeout __read_mostly = RT_GC_TIMEOUT;
+
+static int ip_min_valid_pmtu __read_mostly = IPV4_MIN_MTU;
+
/*
* Interface to generic destination cache.
*/
@@ -2790,7 +2793,8 @@
.data = &ip_rt_min_pmtu,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = proc_dointvec,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &ip_min_valid_pmtu,
},
{
.procname = "min_adv_mss",
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 8ce03d8..c14c213 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -5608,10 +5608,6 @@
else
tp->pred_flags = 0;
- if (!sock_flag(sk, SOCK_DEAD)) {
- sk->sk_state_change(sk);
- sk_wake_async(sk, SOCK_WAKE_IO, POLL_OUT);
- }
}
static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack,
@@ -5680,6 +5676,7 @@
struct tcp_sock *tp = tcp_sk(sk);
struct tcp_fastopen_cookie foc = { .len = -1 };
int saved_clamp = tp->rx_opt.mss_clamp;
+ bool fastopen_fail;
tcp_parse_options(skb, &tp->rx_opt, 0, &foc);
if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr)
@@ -5783,10 +5780,15 @@
tcp_finish_connect(sk, skb);
- if ((tp->syn_fastopen || tp->syn_data) &&
- tcp_rcv_fastopen_synack(sk, skb, &foc))
- return -1;
+ fastopen_fail = (tp->syn_fastopen || tp->syn_data) &&
+ tcp_rcv_fastopen_synack(sk, skb, &foc);
+ if (!sock_flag(sk, SOCK_DEAD)) {
+ sk->sk_state_change(sk);
+ sk_wake_async(sk, SOCK_WAKE_IO, POLL_OUT);
+ }
+ if (fastopen_fail)
+ return -1;
if (sk->sk_write_pending ||
icsk->icsk_accept_queue.rskq_defer_accept ||
icsk->icsk_ack.pingpong) {
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 20634ea..16a473a 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -1580,7 +1580,7 @@
*/
segs = max_t(u32, bytes / mss_now, min_tso_segs);
- return min_t(u32, segs, sk->sk_gso_max_segs);
+ return segs;
}
EXPORT_SYMBOL(tcp_tso_autosize);
@@ -1592,8 +1592,10 @@
const struct tcp_congestion_ops *ca_ops = inet_csk(sk)->icsk_ca_ops;
u32 tso_segs = ca_ops->tso_segs_goal ? ca_ops->tso_segs_goal(sk) : 0;
- return tso_segs ? :
- tcp_tso_autosize(sk, mss_now, sysctl_tcp_min_tso_segs);
+ if (!tso_segs)
+ tso_segs = tcp_tso_autosize(sk, mss_now,
+ sysctl_tcp_min_tso_segs);
+ return min_t(u32, tso_segs, sk->sk_gso_max_segs);
}
/* Returns the portion of skb which can be sent right away */
@@ -1907,6 +1909,24 @@
}
}
+static bool tcp_can_coalesce_send_queue_head(struct sock *sk, int len)
+{
+ struct sk_buff *skb, *next;
+
+ skb = tcp_send_head(sk);
+ tcp_for_write_queue_from_safe(skb, next, sk) {
+ if (len <= skb->len)
+ break;
+
+ if (unlikely(TCP_SKB_CB(skb)->eor))
+ return false;
+
+ len -= skb->len;
+ }
+
+ return true;
+}
+
/* Create a new MTU probe if we are ready.
* MTU probe is regularly attempting to increase the path MTU by
* deliberately sending larger packets. This discovers routing
@@ -1979,6 +1999,9 @@
return 0;
}
+ if (!tcp_can_coalesce_send_queue_head(sk, probe_size))
+ return -1;
+
/* We're allowed to probe. Build it now. */
nskb = sk_stream_alloc_skb(sk, probe_size, GFP_ATOMIC, false);
if (!nskb)
@@ -2014,6 +2037,10 @@
/* We've eaten all the data from this skb.
* Throw it away. */
TCP_SKB_CB(nskb)->tcp_flags |= TCP_SKB_CB(skb)->tcp_flags;
+ /* If this is the last SKB we copy and eor is set
+ * we need to propagate it to the new skb.
+ */
+ TCP_SKB_CB(nskb)->eor = TCP_SKB_CB(skb)->eor;
tcp_unlink_write_queue(skb, sk);
sk_wmem_free_skb(sk, skb);
} else {
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 1589e6a..5af27b9 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1723,6 +1723,11 @@
err = udplite_checksum_init(skb, uh);
if (err)
return err;
+
+ if (UDP_SKB_CB(skb)->partial_cov) {
+ skb->csum = inet_compute_pseudo(skb, proto);
+ return 0;
+ }
}
/* Note, we are only interested in != 0 or == 0, thus the
diff --git a/net/ipv6/ip6_checksum.c b/net/ipv6/ip6_checksum.c
index c0cbcb2..1dc023c 100644
--- a/net/ipv6/ip6_checksum.c
+++ b/net/ipv6/ip6_checksum.c
@@ -72,6 +72,11 @@
err = udplite_checksum_init(skb, uh);
if (err)
return err;
+
+ if (UDP_SKB_CB(skb)->partial_cov) {
+ skb->csum = ip6_compute_pseudo(skb, proto);
+ return 0;
+ }
}
/* To support RFC 6936 (allow zero checksum in UDP/IPV6 for tunnels)
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 4c52236..8ff62e4 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1127,8 +1127,13 @@
max_headroom += 8;
mtu -= 8;
}
- if (mtu < IPV6_MIN_MTU)
- mtu = IPV6_MIN_MTU;
+ if (skb->protocol == htons(ETH_P_IPV6)) {
+ if (mtu < IPV6_MIN_MTU)
+ mtu = IPV6_MIN_MTU;
+ } else if (mtu < 576) {
+ mtu = 576;
+ }
+
if (skb_dst(skb) && !t->parms.collect_md)
skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
if (skb->len - t->tun_hlen - eth_hlen > mtu && !skb_is_gso(skb)) {
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index afc30a0..2e3f4f1 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -626,6 +626,7 @@
{
struct net_device *dev = t->dev;
struct __ip6_tnl_parm *p = &t->parms;
+ struct net_device *tdev = NULL;
memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr));
memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr));
@@ -638,6 +639,25 @@
dev->flags |= IFF_POINTOPOINT;
else
dev->flags &= ~IFF_POINTOPOINT;
+
+ if (p->flags & IP6_TNL_F_CAP_XMIT) {
+ int strict = (ipv6_addr_type(&p->raddr) &
+ (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL));
+ struct rt6_info *rt = rt6_lookup(t->net,
+ &p->raddr, &p->laddr,
+ p->link, strict);
+
+ if (rt)
+ tdev = rt->dst.dev;
+ ip6_rt_put(rt);
+ }
+
+ if (!tdev && p->link)
+ tdev = __dev_get_by_index(t->net, p->link);
+
+ if (tdev)
+ dev->mtu = max_t(int, tdev->mtu - dev->hard_header_len,
+ IPV6_MIN_MTU);
}
/**
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 493a32f..c66b9a8 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -1343,10 +1343,7 @@
if (get_user(len, optlen))
return -EFAULT;
- lock_sock(sk);
- err = nf_getsockopt(sk, PF_INET6, optname, optval,
- &len);
- release_sock(sk);
+ err = nf_getsockopt(sk, PF_INET6, optname, optval, &len);
if (err >= 0)
err = put_user(len, optlen);
}
@@ -1385,10 +1382,7 @@
if (get_user(len, optlen))
return -EFAULT;
- lock_sock(sk);
- err = compat_nf_getsockopt(sk, PF_INET6,
- optname, optval, &len);
- release_sock(sk);
+ err = compat_nf_getsockopt(sk, PF_INET6, optname, optval, &len);
if (err >= 0)
err = put_user(len, optlen);
}
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 01858ac..52236be 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1518,7 +1518,8 @@
*(opt++) = (rd_len >> 3);
opt += 6;
- memcpy(opt, ipv6_hdr(orig_skb), rd_len - 8);
+ skb_copy_bits(orig_skb, skb_network_offset(orig_skb), opt,
+ rd_len - 8);
}
void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
@@ -1725,6 +1726,8 @@
case NETDEV_CHANGEADDR:
neigh_changeaddr(&nd_tbl, dev);
fib6_run_gc(0, net, false);
+ /* fallthrough */
+ case NETDEV_UP:
idev = in6_dev_get(dev);
if (!idev)
break;
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index 1cee193..aa82858 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -385,6 +385,10 @@
}
if (table_base + v != ip6t_next_entry(e) &&
!(e->ipv6.flags & IP6T_F_GOTO)) {
+ if (unlikely(stackidx >= private->stacksize)) {
+ verdict = NF_DROP;
+ break;
+ }
jumpstack[stackidx++] = e;
}
@@ -575,7 +579,8 @@
static int
find_check_entry(struct ip6t_entry *e, struct net *net, const char *name,
- unsigned int size)
+ unsigned int size,
+ struct xt_percpu_counter_alloc_state *alloc_state)
{
struct xt_entry_target *t;
struct xt_target *target;
@@ -583,12 +588,9 @@
unsigned int j;
struct xt_mtchk_param mtpar;
struct xt_entry_match *ematch;
- unsigned long pcnt;
- pcnt = xt_percpu_counter_alloc();
- if (IS_ERR_VALUE(pcnt))
+ if (!xt_percpu_counter_alloc(alloc_state, &e->counters))
return -ENOMEM;
- e->counters.pcnt = pcnt;
j = 0;
mtpar.net = net;
@@ -625,7 +627,7 @@
cleanup_match(ematch, net);
}
- xt_percpu_counter_free(e->counters.pcnt);
+ xt_percpu_counter_free(&e->counters);
return ret;
}
@@ -712,8 +714,7 @@
if (par.target->destroy != NULL)
par.target->destroy(&par);
module_put(par.target->me);
-
- xt_percpu_counter_free(e->counters.pcnt);
+ xt_percpu_counter_free(&e->counters);
}
/* Checks and translates the user-supplied table segment (held in
@@ -722,6 +723,7 @@
translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0,
const struct ip6t_replace *repl)
{
+ struct xt_percpu_counter_alloc_state alloc_state = { 0 };
struct ip6t_entry *iter;
unsigned int *offsets;
unsigned int i;
@@ -781,7 +783,8 @@
/* Finally, each sanity check must pass */
i = 0;
xt_entry_foreach(iter, entry0, newinfo->size) {
- ret = find_check_entry(iter, net, repl->name, repl->size);
+ ret = find_check_entry(iter, net, repl->name, repl->size,
+ &alloc_state);
if (ret != 0)
break;
++i;
diff --git a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
index e0be97e..322c765 100644
--- a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
@@ -99,6 +99,10 @@
!l4proto->manip_pkt(skb, &nf_nat_l3proto_ipv6, iphdroff, hdroff,
target, maniptype))
return false;
+
+ /* must reload, offset might have changed */
+ ipv6h = (void *)skb->data + iphdroff;
+
manip_addr:
if (maniptype == NF_NAT_MANIP_SRC)
ipv6h->saddr = target->src.u3.in6;
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 558d566..0d54cc5 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -1647,6 +1647,7 @@
}
rt->dst.flags |= DST_HOST;
+ rt->dst.input = ip6_input;
rt->dst.output = ip6_output;
atomic_set(&rt->dst.__refcnt, 1);
rt->rt6i_gateway = fl6->daddr;
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index db6d437..d4d84da 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -176,7 +176,7 @@
#ifdef CONFIG_IPV6_SIT_6RD
struct ip_tunnel *t = netdev_priv(dev);
- if (t->dev == sitn->fb_tunnel_dev) {
+ if (dev == sitn->fb_tunnel_dev) {
ipv6_addr_set(&t->ip6rd.prefix, htonl(0x20020000), 0, 0, 0);
t->ip6rd.relay_prefix = 0;
t->ip6rd.prefixlen = 16;
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index 91cbbf1..c2dfc32 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -2418,9 +2418,11 @@
af_iucv_dev->driver = &af_iucv_driver;
err = device_register(af_iucv_dev);
if (err)
- goto out_driver;
+ goto out_iucv_dev;
return 0;
+out_iucv_dev:
+ put_device(af_iucv_dev);
out_driver:
driver_unregister(&af_iucv_driver);
out_iucv:
diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c
index 179cd9b..63e6d08 100644
--- a/net/kcm/kcmsock.c
+++ b/net/kcm/kcmsock.c
@@ -1375,24 +1375,32 @@
struct list_head *head;
int index = 0;
struct strp_callbacks cb;
- int err;
+ int err = 0;
csk = csock->sk;
if (!csk)
return -EINVAL;
+ lock_sock(csk);
+
/* Only allow TCP sockets to be attached for now */
if ((csk->sk_family != AF_INET && csk->sk_family != AF_INET6) ||
- csk->sk_protocol != IPPROTO_TCP)
- return -EOPNOTSUPP;
+ csk->sk_protocol != IPPROTO_TCP) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
/* Don't allow listeners or closed sockets */
- if (csk->sk_state == TCP_LISTEN || csk->sk_state == TCP_CLOSE)
- return -EOPNOTSUPP;
+ if (csk->sk_state == TCP_LISTEN || csk->sk_state == TCP_CLOSE) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
psock = kmem_cache_zalloc(kcm_psockp, GFP_KERNEL);
- if (!psock)
- return -ENOMEM;
+ if (!psock) {
+ err = -ENOMEM;
+ goto out;
+ }
psock->mux = mux;
psock->sk = csk;
@@ -1406,7 +1414,7 @@
err = strp_init(&psock->strp, csk, &cb);
if (err) {
kmem_cache_free(kcm_psockp, psock);
- return err;
+ goto out;
}
write_lock_bh(&csk->sk_callback_lock);
@@ -1418,7 +1426,8 @@
write_unlock_bh(&csk->sk_callback_lock);
strp_done(&psock->strp);
kmem_cache_free(kcm_psockp, psock);
- return -EALREADY;
+ err = -EALREADY;
+ goto out;
}
psock->save_data_ready = csk->sk_data_ready;
@@ -1454,7 +1463,10 @@
/* Schedule RX work in case there are already bytes queued */
strp_check_rcv(&psock->strp);
- return 0;
+out:
+ release_sock(csk);
+
+ return err;
}
static int kcm_attach_ioctl(struct socket *sock, struct kcm_attach *info)
@@ -1506,6 +1518,7 @@
if (WARN_ON(psock->rx_kcm)) {
write_unlock_bh(&csk->sk_callback_lock);
+ release_sock(csk);
return;
}
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index cfc4dd8..ead98e8 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -1612,9 +1612,14 @@
encap = cfg->encap;
/* Quick sanity checks */
+ err = -EPROTONOSUPPORT;
+ if (sk->sk_type != SOCK_DGRAM) {
+ pr_debug("tunl %hu: fd %d wrong socket type\n",
+ tunnel_id, fd);
+ goto err;
+ }
switch (encap) {
case L2TP_ENCAPTYPE_UDP:
- err = -EPROTONOSUPPORT;
if (sk->sk_protocol != IPPROTO_UDP) {
pr_err("tunl %hu: fd %d wrong protocol, got %d, expected %d\n",
tunnel_id, fd, sk->sk_protocol, IPPROTO_UDP);
@@ -1622,7 +1627,6 @@
}
break;
case L2TP_ENCAPTYPE_IP:
- err = -EPROTONOSUPPORT;
if (sk->sk_protocol != IPPROTO_L2TP) {
pr_err("tunl %hu: fd %d wrong protocol, got %d, expected %d\n",
tunnel_id, fd, sk->sk_protocol, IPPROTO_L2TP);
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 07001b6..efa2a2f 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -620,10 +620,11 @@
int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
u16 brate;
- sband = sta->local->hw.wiphy->bands[
- ieee80211_get_sdata_band(sta->sdata)];
- brate = sband->bitrates[rate->idx].bitrate;
- rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
+ sband = ieee80211_get_sband(sta->sdata);
+ if (sband) {
+ brate = sband->bitrates[rate->idx].bitrate;
+ rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
+ }
}
if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
rinfo->bw = RATE_INFO_BW_40;
@@ -1218,10 +1219,11 @@
int ret = 0;
struct ieee80211_supported_band *sband;
struct ieee80211_sub_if_data *sdata = sta->sdata;
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
u32 mask, set;
- sband = local->hw.wiphy->bands[band];
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return -EINVAL;
mask = params->sta_flags_mask;
set = params->sta_flags_set;
@@ -1354,7 +1356,7 @@
ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
sband, params->supported_rates,
params->supported_rates_len,
- &sta->sta.supp_rates[band]);
+ &sta->sta.supp_rates[sband->band]);
}
if (params->ht_capa)
@@ -1370,8 +1372,8 @@
/* returned value is only needed for rc update, but the
* rc isn't initialized here yet, so ignore it
*/
- __ieee80211_vht_handle_opmode(sdata, sta,
- params->opmode_notif, band);
+ __ieee80211_vht_handle_opmode(sdata, sta, params->opmode_notif,
+ sband->band);
}
if (params->support_p2p_ps >= 0)
@@ -2017,13 +2019,15 @@
struct bss_parameters *params)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- enum nl80211_band band;
+ struct ieee80211_supported_band *sband;
u32 changed = 0;
if (!sdata_dereference(sdata->u.ap.beacon, sdata))
return -ENOENT;
- band = ieee80211_get_sdata_band(sdata);
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return -EINVAL;
if (params->use_cts_prot >= 0) {
sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot;
@@ -2036,7 +2040,7 @@
}
if (!sdata->vif.bss_conf.use_short_slot &&
- band == NL80211_BAND_5GHZ) {
+ sband->band == NL80211_BAND_5GHZ) {
sdata->vif.bss_conf.use_short_slot = true;
changed |= BSS_CHANGED_ERP_SLOT;
}
@@ -2049,7 +2053,7 @@
if (params->basic_rates) {
ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
- wiphy->bands[band],
+ wiphy->bands[sband->band],
params->basic_rates,
params->basic_rates_len,
&sdata->vif.bss_conf.basic_rates);
@@ -2792,7 +2796,7 @@
}
if (beacon->probe_resp_len) {
new_beacon->probe_resp_len = beacon->probe_resp_len;
- beacon->probe_resp = pos;
+ new_beacon->probe_resp = pos;
memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
pos += beacon->probe_resp_len;
}
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 62d13ea..d31818e 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -994,7 +994,7 @@
enum nl80211_band band = rx_status->band;
enum nl80211_bss_scan_width scan_width;
struct ieee80211_local *local = sdata->local;
- struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
+ struct ieee80211_supported_band *sband;
bool rates_updated = false;
u32 supp_rates = 0;
@@ -1004,6 +1004,10 @@
if (!ether_addr_equal(mgmt->bssid, sdata->u.ibss.bssid))
return;
+ sband = local->hw.wiphy->bands[band];
+ if (WARN_ON(!sband))
+ return;
+
rcu_read_lock();
sta = sta_info_get(sdata, mgmt->sa);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 03dbc6b..7fd544d 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -991,21 +991,6 @@
lockdep_assert_held(&sdata->wdev.mtx);
}
-static inline enum nl80211_band
-ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata)
-{
- enum nl80211_band band = NL80211_BAND_2GHZ;
- struct ieee80211_chanctx_conf *chanctx_conf;
-
- rcu_read_lock();
- chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!WARN_ON(!chanctx_conf))
- band = chanctx_conf->def.chan->band;
- rcu_read_unlock();
-
- return band;
-}
-
static inline int
ieee80211_chandef_get_shift(struct cfg80211_chan_def *chandef)
{
@@ -1410,6 +1395,27 @@
return container_of(wdev, struct ieee80211_sub_if_data, wdev);
}
+static inline struct ieee80211_supported_band *
+ieee80211_get_sband(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ enum nl80211_band band;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return NULL;
+ }
+
+ band = chanctx_conf->def.chan->band;
+ rcu_read_unlock();
+
+ return local->hw.wiphy->bands[band];
+}
+
/* this struct represents 802.11n's RA/TID combination */
struct ieee80211_ra_tid {
u8 ra[ETH_ALEN];
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index a7aa54f..fa7d757 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1520,7 +1520,7 @@
break;
case NL80211_IFTYPE_UNSPECIFIED:
case NUM_NL80211_IFTYPES:
- BUG();
+ WARN_ON(1);
break;
}
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index b4b3fe0..b2a2726 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -63,6 +63,7 @@
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
u32 basic_rates = 0;
struct cfg80211_chan_def sta_chan_def;
+ struct ieee80211_supported_band *sband;
/*
* As support for each feature is added, check for matching
@@ -83,7 +84,11 @@
(ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth)))
return false;
- ieee80211_sta_get_rates(sdata, ie, ieee80211_get_sdata_band(sdata),
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return false;
+
+ ieee80211_sta_get_rates(sdata, ie, sband->band,
&basic_rates);
if (sdata->vif.bss_conf.basic_rates != basic_rates)
@@ -399,12 +404,13 @@
int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
- struct ieee80211_local *local = sdata->local;
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband;
u8 *pos;
- sband = local->hw.wiphy->bands[band];
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return -EINVAL;
+
if (!sband->ht_cap.ht_supported ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
@@ -462,12 +468,13 @@
int mesh_add_vht_cap_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
- struct ieee80211_local *local = sdata->local;
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband;
u8 *pos;
- sband = local->hw.wiphy->bands[band];
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return -EINVAL;
+
if (!sband->vht_cap.vht_supported ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
@@ -916,12 +923,16 @@
struct cfg80211_csa_settings params;
struct ieee80211_csa_ie csa_ie;
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
+ struct ieee80211_supported_band *sband;
int err;
u32 sta_flags;
sdata_assert_lock(sdata);
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return false;
+
sta_flags = IEEE80211_STA_DISABLE_VHT;
switch (sdata->vif.bss_conf.chandef.width) {
case NL80211_CHAN_WIDTH_20_NOHT:
@@ -935,7 +946,7 @@
memset(¶ms, 0, sizeof(params));
memset(&csa_ie, 0, sizeof(csa_ie));
- err = ieee80211_parse_ch_switch_ie(sdata, elems, band,
+ err = ieee80211_parse_ch_switch_ie(sdata, elems, sband->band,
sta_flags, sdata->vif.addr,
&csa_ie);
if (err < 0)
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index fcba70e5..2d3c469 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -93,19 +93,23 @@
static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
- struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
+ struct ieee80211_supported_band *sband;
struct sta_info *sta;
u32 erp_rates = 0, changed = 0;
int i;
bool short_slot = false;
- if (band == NL80211_BAND_5GHZ) {
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return changed;
+
+ if (sband->band == NL80211_BAND_5GHZ) {
/* (IEEE 802.11-2012 19.4.5) */
short_slot = true;
goto out;
- } else if (band != NL80211_BAND_2GHZ)
+ } else if (sband->band != NL80211_BAND_2GHZ) {
goto out;
+ }
for (i = 0; i < sband->n_bitrates; i++)
if (sband->bitrates[i].flags & IEEE80211_RATE_ERP_G)
@@ -121,7 +125,7 @@
continue;
short_slot = false;
- if (erp_rates & sta->sta.supp_rates[band])
+ if (erp_rates & sta->sta.supp_rates[sband->band])
short_slot = true;
else
break;
@@ -247,7 +251,15 @@
mgmt->u.action.u.self_prot.action_code = action;
if (action != WLAN_SP_MESH_PEERING_CLOSE) {
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
+ struct ieee80211_supported_band *sband;
+ enum nl80211_band band;
+
+ sband = ieee80211_get_sband(sdata);
+ if (!sband) {
+ err = -EINVAL;
+ goto free;
+ }
+ band = sband->band;
/* capability info */
pos = skb_put(skb, 2);
@@ -393,13 +405,16 @@
struct ieee802_11_elems *elems, bool insert)
{
struct ieee80211_local *local = sdata->local;
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband;
u32 rates, basic_rates = 0, changed = 0;
enum ieee80211_sta_rx_bandwidth bw = sta->sta.bandwidth;
- sband = local->hw.wiphy->bands[band];
- rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates);
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return;
+
+ rates = ieee80211_sta_get_rates(sdata, elems, sband->band,
+ &basic_rates);
spin_lock_bh(&sta->mesh->plink_lock);
sta->rx_stats.last_rx = jiffies;
@@ -410,9 +425,9 @@
goto out;
sta->mesh->processed_beacon = true;
- if (sta->sta.supp_rates[band] != rates)
+ if (sta->sta.supp_rates[sband->band] != rates)
changed |= IEEE80211_RC_SUPP_RATES_CHANGED;
- sta->sta.supp_rates[band] = rates;
+ sta->sta.supp_rates[sband->band] = rates;
if (ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems->ht_cap_elem, sta))
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index b2c706c..0519edb 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1851,11 +1851,16 @@
u16 capab, bool erp_valid, u8 erp)
{
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
+ struct ieee80211_supported_band *sband;
u32 changed = 0;
bool use_protection;
bool use_short_preamble;
bool use_short_slot;
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return changed;
+
if (erp_valid) {
use_protection = (erp & WLAN_ERP_USE_PROTECTION) != 0;
use_short_preamble = (erp & WLAN_ERP_BARKER_PREAMBLE) == 0;
@@ -1865,7 +1870,7 @@
}
use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME);
- if (ieee80211_get_sdata_band(sdata) == NL80211_BAND_5GHZ)
+ if (sband->band == NL80211_BAND_5GHZ)
use_short_slot = true;
if (use_protection != bss_conf->use_cts_prot) {
@@ -2994,7 +2999,12 @@
goto out;
}
- sband = local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
+ sband = ieee80211_get_sband(sdata);
+ if (!sband) {
+ mutex_unlock(&sdata->local->sta_mtx);
+ ret = false;
+ goto out;
+ }
/* Set up internal HT/VHT capabilities */
if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 206698b..dbceb42 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -875,7 +875,9 @@
struct ieee80211_sta_rates *old;
struct ieee80211_supported_band *sband;
- sband = hw->wiphy->bands[ieee80211_get_sdata_band(sta->sdata)];
+ sband = ieee80211_get_sband(sta->sdata);
+ if (!sband)
+ return -EINVAL;
rate_control_apply_mask_ratetbl(sta, sband, rates);
/*
* mac80211 guarantees that this function will not be called
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 439e597..404284a 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -3611,6 +3611,8 @@
}
return true;
case NL80211_IFTYPE_MESH_POINT:
+ if (ether_addr_equal(sdata->vif.addr, hdr->addr2))
+ return false;
if (multicast)
return true;
return ether_addr_equal(sdata->vif.addr, hdr->addr1);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 348700b4..1ecf3d0 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -395,10 +395,15 @@
sta->sta.smps_mode = IEEE80211_SMPS_OFF;
if (sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
- struct ieee80211_supported_band *sband =
- hw->wiphy->bands[ieee80211_get_sdata_band(sdata)];
- u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >>
- IEEE80211_HT_CAP_SM_PS_SHIFT;
+ struct ieee80211_supported_band *sband;
+ u8 smps;
+
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ goto free_txq;
+
+ smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >>
+ IEEE80211_HT_CAP_SM_PS_SHIFT;
/*
* Assume that hostapd advertises our caps in the beacon and
* this is the known_smps_mode for a station that just assciated
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index ad37b4e..72fe9bc 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -200,6 +200,7 @@
}
if (ieee80211_is_action(mgmt->frame_control) &&
+ !ieee80211_has_protected(mgmt->frame_control) &&
mgmt->u.action.category == WLAN_CATEGORY_HT &&
mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS &&
ieee80211_sdata_running(sdata)) {
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index afca7d1..f20dcf1 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -47,8 +47,7 @@
NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
bool wider_band = ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) &&
!ifmgd->tdls_wider_bw_prohibited;
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
- struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
+ struct ieee80211_supported_band *sband = ieee80211_get_sband(sdata);
bool vht = sband && sband->vht_cap.vht_supported;
u8 *pos = (void *)skb_put(skb, 10);
@@ -180,11 +179,14 @@
static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
u16 status_code)
{
+ struct ieee80211_supported_band *sband;
+
/* The capability will be 0 when sending a failure code */
if (status_code != 0)
return 0;
- if (ieee80211_get_sdata_band(sdata) == NL80211_BAND_2GHZ) {
+ sband = ieee80211_get_sband(sdata);
+ if (sband && sband->band == NL80211_BAND_2GHZ) {
return WLAN_CAPABILITY_SHORT_SLOT_TIME |
WLAN_CAPABILITY_SHORT_PREAMBLE;
}
@@ -358,17 +360,20 @@
u8 action_code, bool initiator,
const u8 *extra_ies, size_t extra_ies_len)
{
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
- struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
+ struct ieee80211_local *local = sdata->local;
struct ieee80211_sta_ht_cap ht_cap;
struct ieee80211_sta_vht_cap vht_cap;
struct sta_info *sta = NULL;
size_t offset = 0, noffset;
u8 *pos;
- ieee80211_add_srates_ie(sdata, skb, false, band);
- ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return;
+
+ ieee80211_add_srates_ie(sdata, skb, false, sband->band);
+ ieee80211_add_ext_srates_ie(sdata, skb, false, sband->band);
ieee80211_tdls_add_supp_channels(sdata, skb);
/* add any custom IEs that go before Extended Capabilities */
@@ -439,7 +444,6 @@
* the same on all bands. The specification limits the setup to a
* single HT-cap, so use the current band for now.
*/
- sband = local->hw.wiphy->bands[band];
memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
@@ -545,9 +549,13 @@
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
size_t offset = 0, noffset;
struct sta_info *sta, *ap_sta;
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
+ struct ieee80211_supported_band *sband;
u8 *pos;
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return;
+
mutex_lock(&local->sta_mtx);
sta = sta_info_get(sdata, peer);
@@ -612,7 +620,8 @@
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
/* only include VHT-operation if not on the 2.4GHz band */
- if (band != NL80211_BAND_2GHZ && sta->sta.vht_cap.vht_supported) {
+ if (sband->band != NL80211_BAND_2GHZ &&
+ sta->sta.vht_cap.vht_supported) {
/*
* if both peers support WIDER_BW, we can expand the chandef to
* a wider compatible one, up to 80MHz
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 1ffd1e1..8458299 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -4182,7 +4182,10 @@
return bcn;
shift = ieee80211_vif_get_shift(vif);
- sband = hw->wiphy->bands[ieee80211_get_sdata_band(vif_to_sdata(vif))];
+ sband = ieee80211_get_sband(vif_to_sdata(vif));
+ if (!sband)
+ return bcn;
+
ieee80211_tx_monitor(hw_to_local(hw), copy, sband, 1, shift, false);
return bcn;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 031273a..ae91a3c 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1590,14 +1590,14 @@
size_t num_rates;
u32 supp_rates, rate_flags;
int i, j, shift;
+
sband = sdata->local->hw.wiphy->bands[band];
+ if (WARN_ON(!sband))
+ return 1;
rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
shift = ieee80211_vif_get_shift(&sdata->vif);
- if (WARN_ON(!sband))
- return 1;
-
num_rates = sband->n_bitrates;
supp_rates = 0;
for (i = 0; i < elems->supp_rates_len +
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index c5a5a69..ffab94d 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -7,6 +7,7 @@
#include <linux/if_arp.h>
#include <linux/ipv6.h>
#include <linux/mpls.h>
+#include <linux/nospec.h>
#include <linux/vmalloc.h>
#include <net/ip.h>
#include <net/dst.h>
@@ -756,6 +757,22 @@
return err;
}
+static bool mpls_label_ok(struct net *net, unsigned int *index)
+{
+ bool is_ok = true;
+
+ /* Reserved labels may not be set */
+ if (*index < MPLS_LABEL_FIRST_UNRESERVED)
+ is_ok = false;
+
+ /* The full 20 bit range may not be supported. */
+ if (is_ok && *index >= net->mpls.platform_labels)
+ is_ok = false;
+
+ *index = array_index_nospec(*index, net->mpls.platform_labels);
+ return is_ok;
+}
+
static int mpls_route_add(struct mpls_route_config *cfg)
{
struct mpls_route __rcu **platform_label;
@@ -774,12 +791,7 @@
index = find_free_label(net);
}
- /* Reserved labels may not be set */
- if (index < MPLS_LABEL_FIRST_UNRESERVED)
- goto errout;
-
- /* The full 20 bit range may not be supported. */
- if (index >= net->mpls.platform_labels)
+ if (!mpls_label_ok(net, &index))
goto errout;
/* Append makes no sense with mpls */
@@ -840,12 +852,7 @@
index = cfg->rc_label;
- /* Reserved labels may not be removed */
- if (index < MPLS_LABEL_FIRST_UNRESERVED)
- goto errout;
-
- /* The full 20 bit range may not be supported */
- if (index >= net->mpls.platform_labels)
+ if (!mpls_label_ok(net, &index))
goto errout;
mpls_route_update(net, index, NULL, &cfg->rc_nlinfo);
@@ -1279,10 +1286,9 @@
&cfg->rc_label))
goto errout;
- /* Reserved labels may not be set */
- if (cfg->rc_label < MPLS_LABEL_FIRST_UNRESERVED)
+ if (!mpls_label_ok(cfg->rc_nlinfo.nl_net,
+ &cfg->rc_label))
goto errout;
-
break;
}
case RTA_VIA:
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index 2155c24..74d1195 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -3092,6 +3092,17 @@
return skb->len;
}
+static bool ip_vs_is_af_valid(int af)
+{
+ if (af == AF_INET)
+ return true;
+#ifdef CONFIG_IP_VS_IPV6
+ if (af == AF_INET6 && ipv6_mod_enabled())
+ return true;
+#endif
+ return false;
+}
+
static int ip_vs_genl_parse_service(struct netns_ipvs *ipvs,
struct ip_vs_service_user_kern *usvc,
struct nlattr *nla, int full_entry,
@@ -3118,11 +3129,7 @@
memset(usvc, 0, sizeof(*usvc));
usvc->af = nla_get_u16(nla_af);
-#ifdef CONFIG_IP_VS_IPV6
- if (usvc->af != AF_INET && usvc->af != AF_INET6)
-#else
- if (usvc->af != AF_INET)
-#endif
+ if (!ip_vs_is_af_valid(usvc->af))
return -EAFNOSUPPORT;
if (nla_fwmark) {
@@ -3624,6 +3631,11 @@
if (udest.af == 0)
udest.af = svc->af;
+ if (!ip_vs_is_af_valid(udest.af)) {
+ ret = -EAFNOSUPPORT;
+ goto out;
+ }
+
if (udest.af != svc->af && cmd != IPVS_CMD_DEL_DEST) {
/* The synchronization protocol is incompatible
* with mixed family services
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index fa4d1ec..f66c08f 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -1873,7 +1873,7 @@
return 0;
}
-int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
+int nf_conntrack_set_hashsize(const char *val, const struct kernel_param *kp)
{
unsigned int hashsize;
int rc;
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
index 6dc44d9..92e69af 100644
--- a/net/netfilter/nf_conntrack_helper.c
+++ b/net/netfilter/nf_conntrack_helper.c
@@ -379,17 +379,33 @@
struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
unsigned int h = helper_hash(&me->tuple);
struct nf_conntrack_helper *cur;
- int ret = 0;
+ int ret = 0, i;
BUG_ON(me->expect_policy == NULL);
BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
mutex_lock(&nf_ct_helper_mutex);
- hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) {
- if (nf_ct_tuple_src_mask_cmp(&cur->tuple, &me->tuple, &mask)) {
- ret = -EEXIST;
- goto out;
+ for (i = 0; i < nf_ct_helper_hsize; i++) {
+ hlist_for_each_entry(cur, &nf_ct_helper_hash[i], hnode) {
+ if (!strcmp(cur->name, me->name) &&
+ (cur->tuple.src.l3num == NFPROTO_UNSPEC ||
+ cur->tuple.src.l3num == me->tuple.src.l3num) &&
+ cur->tuple.dst.protonum == me->tuple.dst.protonum) {
+ ret = -EEXIST;
+ goto out;
+ }
+ }
+ }
+
+ /* avoid unpredictable behaviour for auto_assign_helper */
+ if (!(me->flags & NF_CT_HELPER_F_USERSPACE)) {
+ hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) {
+ if (nf_ct_tuple_src_mask_cmp(&cur->tuple, &me->tuple,
+ &mask)) {
+ ret = -EEXIST;
+ goto out;
+ }
}
}
hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
diff --git a/net/netfilter/nf_nat_ftp.c b/net/netfilter/nf_nat_ftp.c
index e84a578..d76afaf 100644
--- a/net/netfilter/nf_nat_ftp.c
+++ b/net/netfilter/nf_nat_ftp.c
@@ -134,7 +134,7 @@
}
/* Prior to 2.6.11, we had a ports param. No longer, but don't break users. */
-static int warn_set(const char *val, struct kernel_param *kp)
+static int warn_set(const char *val, const struct kernel_param *kp)
{
printk(KERN_INFO KBUILD_MODNAME
": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n");
diff --git a/net/netfilter/nf_nat_irc.c b/net/netfilter/nf_nat_irc.c
index 1fb2258..8039bcd 100644
--- a/net/netfilter/nf_nat_irc.c
+++ b/net/netfilter/nf_nat_irc.c
@@ -107,7 +107,7 @@
}
/* Prior to 2.6.11, we had a ports param. No longer, but don't break users. */
-static int warn_set(const char *val, struct kernel_param *kp)
+static int warn_set(const char *val, const struct kernel_param *kp)
{
printk(KERN_INFO KBUILD_MODNAME
": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n");
diff --git a/net/netfilter/nf_nat_proto_common.c b/net/netfilter/nf_nat_proto_common.c
index fbce552..7d7466d 100644
--- a/net/netfilter/nf_nat_proto_common.c
+++ b/net/netfilter/nf_nat_proto_common.c
@@ -41,7 +41,7 @@
const struct nf_conn *ct,
u16 *rover)
{
- unsigned int range_size, min, i;
+ unsigned int range_size, min, max, i;
__be16 *portptr;
u_int16_t off;
@@ -71,7 +71,10 @@
}
} else {
min = ntohs(range->min_proto.all);
- range_size = ntohs(range->max_proto.all) - min + 1;
+ max = ntohs(range->max_proto.all);
+ if (unlikely(max < min))
+ swap(max, min);
+ range_size = max - min + 1;
}
if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) {
diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
index 31ca947..b9dd4e9 100644
--- a/net/netfilter/nft_dynset.c
+++ b/net/netfilter/nft_dynset.c
@@ -82,8 +82,7 @@
nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
timeout = priv->timeout ? : set->timeout;
*nft_set_ext_expiration(ext) = jiffies + timeout;
- } else if (sexpr == NULL)
- goto out;
+ }
if (sexpr != NULL)
sexpr->ops->eval(sexpr, regs, pkt);
@@ -92,7 +91,7 @@
regs->verdict.code = NFT_BREAK;
return;
}
-out:
+
if (!priv->invert)
regs->verdict.code = NFT_BREAK;
}
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
index e47ade3..7ad1a86 100644
--- a/net/netfilter/x_tables.c
+++ b/net/netfilter/x_tables.c
@@ -39,6 +39,8 @@
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
MODULE_DESCRIPTION("{ip,ip6,arp,eb}_tables backend module");
+#define XT_PCPU_BLOCK_SIZE 4096
+
struct compat_delta {
unsigned int offset; /* offset in kernel */
int delta; /* delta in 32bit user land */
@@ -1004,8 +1006,10 @@
list_for_each_entry(t, &init_net.xt.tables[af], list) {
if (strcmp(t->name, name))
continue;
- if (!try_module_get(t->me))
+ if (!try_module_get(t->me)) {
+ mutex_unlock(&xt[af].mutex);
return NULL;
+ }
mutex_unlock(&xt[af].mutex);
if (t->table_init(net) != 0) {
@@ -1619,6 +1623,59 @@
}
EXPORT_SYMBOL_GPL(xt_proto_fini);
+/**
+ * xt_percpu_counter_alloc - allocate x_tables rule counter
+ *
+ * @state: pointer to xt_percpu allocation state
+ * @counter: pointer to counter struct inside the ip(6)/arpt_entry struct
+ *
+ * On SMP, the packet counter [ ip(6)t_entry->counters.pcnt ] will then
+ * contain the address of the real (percpu) counter.
+ *
+ * Rule evaluation needs to use xt_get_this_cpu_counter() helper
+ * to fetch the real percpu counter.
+ *
+ * To speed up allocation and improve data locality, a 4kb block is
+ * allocated.
+ *
+ * xt_percpu_counter_alloc_state contains the base address of the
+ * allocated page and the current sub-offset.
+ *
+ * returns false on error.
+ */
+bool xt_percpu_counter_alloc(struct xt_percpu_counter_alloc_state *state,
+ struct xt_counters *counter)
+{
+ BUILD_BUG_ON(XT_PCPU_BLOCK_SIZE < (sizeof(*counter) * 2));
+
+ if (nr_cpu_ids <= 1)
+ return true;
+
+ if (!state->mem) {
+ state->mem = __alloc_percpu(XT_PCPU_BLOCK_SIZE,
+ XT_PCPU_BLOCK_SIZE);
+ if (!state->mem)
+ return false;
+ }
+ counter->pcnt = (__force unsigned long)(state->mem + state->off);
+ state->off += sizeof(*counter);
+ if (state->off > (XT_PCPU_BLOCK_SIZE - sizeof(*counter))) {
+ state->mem = NULL;
+ state->off = 0;
+ }
+ return true;
+}
+EXPORT_SYMBOL_GPL(xt_percpu_counter_alloc);
+
+void xt_percpu_counter_free(struct xt_counters *counters)
+{
+ unsigned long pcnt = counters->pcnt;
+
+ if (nr_cpu_ids > 1 && (pcnt & (XT_PCPU_BLOCK_SIZE - 1)) == 0)
+ free_percpu((void __percpu *)pcnt);
+}
+EXPORT_SYMBOL_GPL(xt_percpu_counter_free);
+
static int __net_init xt_net_init(struct net *net)
{
int i;
diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c
index 6669e68..00ef824 100644
--- a/net/netfilter/xt_CT.c
+++ b/net/netfilter/xt_CT.c
@@ -168,8 +168,10 @@
goto err_put_timeout;
}
timeout_ext = nf_ct_timeout_ext_add(ct, timeout, GFP_ATOMIC);
- if (timeout_ext == NULL)
+ if (!timeout_ext) {
ret = -ENOMEM;
+ goto err_put_timeout;
+ }
rcu_read_unlock();
return ret;
@@ -201,6 +203,7 @@
struct xt_ct_target_info_v1 *info)
{
struct nf_conntrack_zone zone;
+ struct nf_conn_help *help;
struct nf_conn *ct;
int ret = -EOPNOTSUPP;
@@ -249,7 +252,7 @@
if (info->timeout[0]) {
ret = xt_ct_set_timeout(ct, par, info->timeout);
if (ret < 0)
- goto err3;
+ goto err4;
}
__set_bit(IPS_CONFIRMED_BIT, &ct->status);
nf_conntrack_get(&ct->ct_general);
@@ -257,6 +260,10 @@
info->ct = ct;
return 0;
+err4:
+ help = nfct_help(ct);
+ if (help)
+ module_put(help->helper->me);
err3:
nf_ct_tmpl_free(ct);
err2:
diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c
index 14e3d85..3d871c4 100644
--- a/net/netfilter/xt_IDLETIMER.c
+++ b/net/netfilter/xt_IDLETIMER.c
@@ -331,11 +331,11 @@
printk(KERN_WARNING "[%s] Failed to register pm notifier %d\n",
__func__, ret);
+ INIT_WORK(&info->timer->work, idletimer_tg_work);
+
mod_timer(&info->timer->timer,
msecs_to_jiffies(info->timeout * 1000) + jiffies);
- INIT_WORK(&info->timer->work, idletimer_tg_work);
-
return 0;
out_free_attr:
@@ -420,7 +420,10 @@
pr_debug("timeout value is zero\n");
return -EINVAL;
}
-
+ if (info->timeout >= INT_MAX / 1000) {
+ pr_debug("timeout value is too big\n");
+ return -EINVAL;
+ }
if (info->label[0] == '\0' ||
strnlen(info->label,
MAX_IDLETIMER_LABEL_SIZE) == MAX_IDLETIMER_LABEL_SIZE) {
diff --git a/net/netfilter/xt_LED.c b/net/netfilter/xt_LED.c
index 3ba31c1..0858fe1 100644
--- a/net/netfilter/xt_LED.c
+++ b/net/netfilter/xt_LED.c
@@ -141,10 +141,11 @@
goto exit_alloc;
}
- /* See if we need to set up a timer */
- if (ledinfo->delay > 0)
- setup_timer(&ledinternal->timer, led_timeout_callback,
- (unsigned long)ledinternal);
+ /* Since the letinternal timer can be shared between multiple targets,
+ * always set it up, even if the current target does not need it
+ */
+ setup_timer(&ledinternal->timer, led_timeout_callback,
+ (unsigned long)ledinternal);
list_add_tail(&ledinternal->list, &xt_led_triggers);
@@ -181,8 +182,7 @@
list_del(&ledinternal->list);
- if (ledinfo->delay > 0)
- del_timer_sync(&ledinternal->timer);
+ del_timer_sync(&ledinternal->timer);
led_trigger_unregister(&ledinternal->netfilter_led_trigger);
diff --git a/net/netfilter/xt_bpf.c b/net/netfilter/xt_bpf.c
index dffee9d47..a8df0a9 100644
--- a/net/netfilter/xt_bpf.c
+++ b/net/netfilter/xt_bpf.c
@@ -8,8 +8,10 @@
*/
#include <linux/module.h>
+#include <linux/syscalls.h>
#include <linux/skbuff.h>
#include <linux/filter.h>
+#include <linux/bpf.h>
#include <linux/netfilter/xt_bpf.h>
#include <linux/netfilter/x_tables.h>
@@ -20,15 +22,18 @@
MODULE_ALIAS("ipt_bpf");
MODULE_ALIAS("ip6t_bpf");
-static int bpf_mt_check(const struct xt_mtchk_param *par)
+static int __bpf_mt_check_bytecode(struct sock_filter *insns, __u16 len,
+ struct bpf_prog **ret)
{
- struct xt_bpf_info *info = par->matchinfo;
struct sock_fprog_kern program;
- program.len = info->bpf_program_num_elem;
- program.filter = info->bpf_program;
+ if (len > XT_BPF_MAX_NUM_INSTR)
+ return -EINVAL;
- if (bpf_prog_create(&info->filter, &program)) {
+ program.len = len;
+ program.filter = insns;
+
+ if (bpf_prog_create(ret, &program)) {
pr_info("bpf: check failed: parse error\n");
return -EINVAL;
}
@@ -36,6 +41,53 @@
return 0;
}
+static int __bpf_mt_check_fd(int fd, struct bpf_prog **ret)
+{
+ struct bpf_prog *prog;
+
+ prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_SOCKET_FILTER);
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+
+ *ret = prog;
+ return 0;
+}
+
+static int __bpf_mt_check_path(const char *path, struct bpf_prog **ret)
+{
+ if (strnlen(path, XT_BPF_PATH_MAX) == XT_BPF_PATH_MAX)
+ return -EINVAL;
+
+ *ret = bpf_prog_get_type_path(path, BPF_PROG_TYPE_SOCKET_FILTER);
+ return PTR_ERR_OR_ZERO(*ret);
+
+}
+
+static int bpf_mt_check(const struct xt_mtchk_param *par)
+{
+ struct xt_bpf_info *info = par->matchinfo;
+
+ return __bpf_mt_check_bytecode(info->bpf_program,
+ info->bpf_program_num_elem,
+ &info->filter);
+}
+
+static int bpf_mt_check_v1(const struct xt_mtchk_param *par)
+{
+ struct xt_bpf_info_v1 *info = par->matchinfo;
+
+ if (info->mode == XT_BPF_MODE_BYTECODE)
+ return __bpf_mt_check_bytecode(info->bpf_program,
+ info->bpf_program_num_elem,
+ &info->filter);
+ else if (info->mode == XT_BPF_MODE_FD_ELF)
+ return __bpf_mt_check_fd(info->fd, &info->filter);
+ else if (info->mode == XT_BPF_MODE_PATH_PINNED)
+ return __bpf_mt_check_path(info->path, &info->filter);
+ else
+ return -EINVAL;
+}
+
static bool bpf_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_bpf_info *info = par->matchinfo;
@@ -43,31 +95,58 @@
return BPF_PROG_RUN(info->filter, skb);
}
+static bool bpf_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
+{
+ const struct xt_bpf_info_v1 *info = par->matchinfo;
+
+ return !!bpf_prog_run_save_cb(info->filter, (struct sk_buff *) skb);
+}
+
static void bpf_mt_destroy(const struct xt_mtdtor_param *par)
{
const struct xt_bpf_info *info = par->matchinfo;
+
bpf_prog_destroy(info->filter);
}
-static struct xt_match bpf_mt_reg __read_mostly = {
- .name = "bpf",
- .revision = 0,
- .family = NFPROTO_UNSPEC,
- .checkentry = bpf_mt_check,
- .match = bpf_mt,
- .destroy = bpf_mt_destroy,
- .matchsize = sizeof(struct xt_bpf_info),
- .me = THIS_MODULE,
+static void bpf_mt_destroy_v1(const struct xt_mtdtor_param *par)
+{
+ const struct xt_bpf_info_v1 *info = par->matchinfo;
+
+ bpf_prog_destroy(info->filter);
+}
+
+static struct xt_match bpf_mt_reg[] __read_mostly = {
+ {
+ .name = "bpf",
+ .revision = 0,
+ .family = NFPROTO_UNSPEC,
+ .checkentry = bpf_mt_check,
+ .match = bpf_mt,
+ .destroy = bpf_mt_destroy,
+ .matchsize = sizeof(struct xt_bpf_info),
+ .me = THIS_MODULE,
+ },
+ {
+ .name = "bpf",
+ .revision = 1,
+ .family = NFPROTO_UNSPEC,
+ .checkentry = bpf_mt_check_v1,
+ .match = bpf_mt_v1,
+ .destroy = bpf_mt_destroy_v1,
+ .matchsize = sizeof(struct xt_bpf_info_v1),
+ .me = THIS_MODULE,
+ },
};
static int __init bpf_mt_init(void)
{
- return xt_register_match(&bpf_mt_reg);
+ return xt_register_matches(bpf_mt_reg, ARRAY_SIZE(bpf_mt_reg));
}
static void __exit bpf_mt_exit(void)
{
- xt_unregister_match(&bpf_mt_reg);
+ xt_unregister_matches(bpf_mt_reg, ARRAY_SIZE(bpf_mt_reg));
}
module_init(bpf_mt_init);
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index e1c123d..c1f59a0 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -2258,7 +2258,7 @@
if (cb->start) {
ret = cb->start(cb);
if (ret)
- goto error_unlock;
+ goto error_put;
}
nlk->cb_running = true;
@@ -2278,6 +2278,8 @@
*/
return -EINTR;
+error_put:
+ module_put(control->module);
error_unlock:
sock_put(sk);
mutex_unlock(nlk->cb_mutex);
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index b09d475..a1fae01 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -1103,6 +1103,7 @@
{
struct sk_buff *tmp;
struct net *net, *prev = NULL;
+ bool delivered = false;
int err;
for_each_net_rcu(net) {
@@ -1114,14 +1115,21 @@
}
err = nlmsg_multicast(prev->genl_sock, tmp,
portid, group, flags);
- if (err)
+ if (!err)
+ delivered = true;
+ else if (err != -ESRCH)
goto error;
}
prev = net;
}
- return nlmsg_multicast(prev->genl_sock, skb, portid, group, flags);
+ err = nlmsg_multicast(prev->genl_sock, skb, portid, group, flags);
+ if (!err)
+ delivered = true;
+ else if (err != -ESRCH)
+ return err;
+ return delivered ? 0 : -ESRCH;
error:
kfree_skb(skb);
return err;
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
index b28e45b..4663939 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -396,10 +396,38 @@
u16 proto, const struct sk_buff *skb)
{
struct nf_conntrack_tuple tuple;
+ struct nf_conntrack_expect *exp;
if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), proto, net, &tuple))
return NULL;
- return __nf_ct_expect_find(net, zone, &tuple);
+
+ exp = __nf_ct_expect_find(net, zone, &tuple);
+ if (exp) {
+ struct nf_conntrack_tuple_hash *h;
+
+ /* Delete existing conntrack entry, if it clashes with the
+ * expectation. This can happen since conntrack ALGs do not
+ * check for clashes between (new) expectations and existing
+ * conntrack entries. nf_conntrack_in() will check the
+ * expectations only if a conntrack entry can not be found,
+ * which can lead to OVS finding the expectation (here) in the
+ * init direction, but which will not be removed by the
+ * nf_conntrack_in() call, if a matching conntrack entry is
+ * found instead. In this case all init direction packets
+ * would be reported as new related packets, while reply
+ * direction packets would be reported as un-related
+ * established packets.
+ */
+ h = nf_conntrack_find_get(net, zone, &tuple);
+ if (h) {
+ struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h);
+
+ nf_ct_delete(ct, 0, 0);
+ nf_conntrack_put(&ct->ct_general);
+ }
+ }
+
+ return exp;
}
/* This replicates logic from nf_conntrack_core.c that is not exported. */
diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c
index 5dab1ff..59d3286 100644
--- a/net/rxrpc/output.c
+++ b/net/rxrpc/output.c
@@ -391,7 +391,7 @@
(char *)&opt, sizeof(opt));
if (ret == 0) {
ret = kernel_sendmsg(conn->params.local->socket, &msg,
- iov, 1, iov[0].iov_len);
+ iov, 2, len);
opt = IPV6_PMTUDISC_DO;
kernel_setsockopt(conn->params.local->socket,
diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c
index e0defce..24a0c66 100644
--- a/net/sched/act_csum.c
+++ b/net/sched/act_csum.c
@@ -180,6 +180,9 @@
struct tcphdr *tcph;
const struct iphdr *iph;
+ if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4)
+ return 1;
+
tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph));
if (tcph == NULL)
return 0;
@@ -201,6 +204,9 @@
struct tcphdr *tcph;
const struct ipv6hdr *ip6h;
+ if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6)
+ return 1;
+
tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph));
if (tcph == NULL)
return 0;
@@ -224,6 +230,9 @@
const struct iphdr *iph;
u16 ul;
+ if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
+ return 1;
+
/*
* Support both UDP and UDPLITE checksum algorithms, Don't use
* udph->len to get the real length without any protocol check,
@@ -277,6 +286,9 @@
const struct ipv6hdr *ip6h;
u16 ul;
+ if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
+ return 1;
+
/*
* Support both UDP and UDPLITE checksum algorithms, Don't use
* udph->len to get the real length without any protocol check,
diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c
index af47bdf..b6e3abe 100644
--- a/net/sched/act_tunnel_key.c
+++ b/net/sched/act_tunnel_key.c
@@ -141,6 +141,7 @@
metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX;
break;
default:
+ ret = -EINVAL;
goto err_out;
}
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 9f7b380..e899d9e 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -462,7 +462,7 @@
/* If a delay is expected, orphan the skb. (orphaning usually takes
* place at TX completion time, so _before_ the link transit delay)
*/
- if (q->latency || q->jitter)
+ if (q->latency || q->jitter || q->rate)
skb_orphan_partial(skb);
/*
@@ -513,7 +513,7 @@
}
if (unlikely(sch->q.qlen >= sch->limit))
- return qdisc_drop(skb, sch, to_free);
+ return qdisc_drop_all(skb, sch, to_free);
qdisc_qstats_backlog_inc(sch, skb);
@@ -530,21 +530,31 @@
now = psched_get_time();
if (q->rate) {
- struct sk_buff *last;
+ struct netem_skb_cb *last = NULL;
- if (sch->q.qlen)
- last = sch->q.tail;
- else
- last = netem_rb_to_skb(rb_last(&q->t_root));
+ if (sch->q.tail)
+ last = netem_skb_cb(sch->q.tail);
+ if (q->t_root.rb_node) {
+ struct sk_buff *t_skb;
+ struct netem_skb_cb *t_last;
+
+ t_skb = netem_rb_to_skb(rb_last(&q->t_root));
+ t_last = netem_skb_cb(t_skb);
+ if (!last ||
+ t_last->time_to_send > last->time_to_send) {
+ last = t_last;
+ }
+ }
+
if (last) {
/*
* Last packet in queue is reference point (now),
* calculate this time bonus and subtract
* from delay.
*/
- delay -= netem_skb_cb(last)->time_to_send - now;
+ delay -= last->time_to_send - now;
delay = max_t(psched_tdiff_t, 0, delay);
- now = netem_skb_cb(last)->time_to_send;
+ now = last->time_to_send;
}
delay += packet_len_2_sched_time(qdisc_pkt_len(skb), q);
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 5d01527..11f69d4 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -324,8 +324,10 @@
final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final);
bdst = ip6_dst_lookup_flow(sk, fl6, final_p);
- if (!IS_ERR(bdst) &&
- ipv6_chk_addr(dev_net(bdst->dev),
+ if (IS_ERR(bdst))
+ continue;
+
+ if (ipv6_chk_addr(dev_net(bdst->dev),
&laddr->a.v6.sin6_addr, bdst->dev, 1)) {
if (!IS_ERR_OR_NULL(dst))
dst_release(dst);
@@ -334,8 +336,10 @@
}
bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
- if (matchlen > bmatchlen)
+ if (matchlen > bmatchlen) {
+ dst_release(bdst);
continue;
+ }
if (!IS_ERR_OR_NULL(dst))
dst_release(dst);
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 7b523e3..fb7b763 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -510,22 +510,20 @@
if (IS_ERR(rt))
continue;
- if (!dst)
- dst = &rt->dst;
-
/* Ensure the src address belongs to the output
* interface.
*/
odev = __ip_dev_find(sock_net(sk), laddr->a.v4.sin_addr.s_addr,
false);
if (!odev || odev->ifindex != fl4->flowi4_oif) {
- if (&rt->dst != dst)
+ if (!dst)
+ dst = &rt->dst;
+ else
dst_release(&rt->dst);
continue;
}
- if (dst != &rt->dst)
- dst_release(dst);
+ dst_release(dst);
dst = &rt->dst;
break;
}
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 9e9690b..fc67d35 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -1373,9 +1373,14 @@
sctp_chunkhdr_t *chunk_hdr;
struct sk_buff *skb;
struct sock *sk;
+ int chunklen;
+
+ chunklen = SCTP_PAD4(sizeof(*chunk_hdr) + paylen);
+ if (chunklen > SCTP_MAX_CHUNK_LEN)
+ goto nodata;
/* No need to allocate LL here, as this is only a chunk. */
- skb = alloc_skb(SCTP_PAD4(sizeof(sctp_chunkhdr_t) + paylen), gfp);
+ skb = alloc_skb(chunklen, gfp);
if (!skb)
goto nodata;
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index fd5b9d5..8cdd6bb 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -4765,7 +4765,7 @@
len = sizeof(int);
if (put_user(len, optlen))
return -EFAULT;
- if (copy_to_user(optval, &sctp_sk(sk)->autoclose, sizeof(int)))
+ if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len))
return -EFAULT;
return 0;
}
@@ -5342,6 +5342,9 @@
err = -EFAULT;
goto out;
}
+ /* XXX: We should have accounted for sizeof(struct sctp_getaddrs) too,
+ * but we can't change it anymore.
+ */
if (put_user(bytes_copied, optlen))
err = -EFAULT;
out:
@@ -5778,7 +5781,7 @@
params.assoc_id = 0;
} else if (len >= sizeof(struct sctp_assoc_value)) {
len = sizeof(struct sctp_assoc_value);
- if (copy_from_user(¶ms, optval, sizeof(params)))
+ if (copy_from_user(¶ms, optval, len))
return -EFAULT;
} else
return -EINVAL;
@@ -5947,7 +5950,9 @@
if (len < sizeof(struct sctp_authkeyid))
return -EINVAL;
- if (copy_from_user(&val, optval, sizeof(struct sctp_authkeyid)))
+
+ len = sizeof(struct sctp_authkeyid);
+ if (copy_from_user(&val, optval, len))
return -EFAULT;
asoc = sctp_id2assoc(sk, val.scact_assoc_id);
@@ -5959,7 +5964,6 @@
else
val.scact_keynumber = ep->active_key_id;
- len = sizeof(struct sctp_authkeyid);
if (put_user(len, optlen))
return -EFAULT;
if (copy_to_user(optval, &val, len))
@@ -5985,7 +5989,7 @@
if (len < sizeof(struct sctp_authchunks))
return -EINVAL;
- if (copy_from_user(&val, optval, sizeof(struct sctp_authchunks)))
+ if (copy_from_user(&val, optval, sizeof(val)))
return -EFAULT;
to = p->gauth_chunks;
@@ -6030,7 +6034,7 @@
if (len < sizeof(struct sctp_authchunks))
return -EINVAL;
- if (copy_from_user(&val, optval, sizeof(struct sctp_authchunks)))
+ if (copy_from_user(&val, optval, sizeof(val)))
return -EFAULT;
to = p->gauth_chunks;
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 272c345..b43818c 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -50,7 +50,7 @@
static DEFINE_MUTEX(svc_pool_map_mutex);/* protects svc_pool_map.count only */
static int
-param_set_pool_mode(const char *val, struct kernel_param *kp)
+param_set_pool_mode(const char *val, const struct kernel_param *kp)
{
int *ip = (int *)kp->arg;
struct svc_pool_map *m = &svc_pool_map;
@@ -80,7 +80,7 @@
}
static int
-param_get_pool_mode(char *buf, struct kernel_param *kp)
+param_get_pool_mode(char *buf, const struct kernel_param *kp)
{
int *ip = (int *)kp->arg;
diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
index 69502fa..1a2b1f6 100644
--- a/net/sunrpc/xprtrdma/verbs.c
+++ b/net/sunrpc/xprtrdma/verbs.c
@@ -1054,6 +1054,7 @@
rpcrdma_buffer_destroy(struct rpcrdma_buffer *buf)
{
cancel_delayed_work_sync(&buf->rb_recovery_worker);
+ cancel_delayed_work_sync(&buf->rb_refresh_worker);
while (!list_empty(&buf->rb_recv_bufs)) {
struct rpcrdma_rep *rep;
diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
index 52d7476..ca68db2 100644
--- a/net/tipc/bearer.c
+++ b/net/tipc/bearer.c
@@ -322,6 +322,7 @@
if (res) {
pr_warn("Bearer <%s> rejected, enable failure (%d)\n",
name, -res);
+ kfree(b);
return -EINVAL;
}
@@ -345,8 +346,10 @@
if (skb)
tipc_bearer_xmit_skb(net, bearer_id, skb, &b->bcast_addr);
- if (tipc_mon_create(net, bearer_id))
+ if (tipc_mon_create(net, bearer_id)) {
+ bearer_disable(net, b);
return -ENOMEM;
+ }
pr_info("Enabled bearer <%s>, discovery domain %s, priority %u\n",
name,
diff --git a/net/tipc/monitor.c b/net/tipc/monitor.c
index 9e109bb..0fcfb39 100644
--- a/net/tipc/monitor.c
+++ b/net/tipc/monitor.c
@@ -633,9 +633,13 @@
{
struct tipc_net *tn = tipc_net(net);
struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
- struct tipc_peer *self = get_self(net, bearer_id);
+ struct tipc_peer *self;
struct tipc_peer *peer, *tmp;
+ if (!mon)
+ return;
+
+ self = get_self(net, bearer_id);
write_lock_bh(&mon->lock);
tn->monitors[bearer_id] = NULL;
list_for_each_entry_safe(peer, tmp, &self->list, list) {
diff --git a/net/tipc/node.c b/net/tipc/node.c
index 5b3e1ea..db8fbc0 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -2094,6 +2094,8 @@
int err;
msg.skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg.skb)
+ return -ENOMEM;
msg.portid = info->snd_portid;
msg.seq = info->snd_seq;
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index eb4cc00..30cc249 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -40,6 +40,7 @@
(5150 - 5250 @ 80), (23), AUTO-BW
(5250 - 5350 @ 80), (23), DFS, AUTO-BW
(5470 - 5710 @ 160), (30), DFS
+ (5725 - 5875 @ 80), (14)
country AM: DFS-ETSI
(2402 - 2482 @ 40), (20)
@@ -51,6 +52,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
+ (5725 - 5875 @ 80), (14)
country AR:
(2402 - 2482 @ 40), (36)
@@ -74,15 +76,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -112,6 +106,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
+ (5725 - 5875 @ 80), (14)
country BB: DFS-FCC
(2402 - 2482 @ 40), (20)
@@ -128,15 +123,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -152,15 +139,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -250,15 +229,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -312,15 +283,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -331,15 +294,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -354,15 +309,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -371,15 +318,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -415,15 +354,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -437,15 +368,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -460,15 +383,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -484,15 +399,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -501,15 +408,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -530,6 +429,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
+ (5725 - 5875 @ 80), (14)
country GH: DFS-FCC
(2402 - 2482 @ 40), (20)
@@ -561,15 +461,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -614,15 +506,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -638,15 +522,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -660,15 +536,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -695,15 +563,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -712,15 +572,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -811,15 +663,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -841,15 +685,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -858,15 +694,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -875,15 +703,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -899,18 +719,21 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
+ (5725 - 5875 @ 80), (14)
country MD: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
+ (5725 - 5875 @ 80), (14)
country ME: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
+ (5725 - 5875 @ 80), (14)
country MF: DFS-ETSI
(2402 - 2482 @ 40), (20)
@@ -930,6 +753,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
+ (5725 - 5875 @ 80), (14)
country MN: DFS-FCC
(2402 - 2482 @ 40), (20)
@@ -957,6 +781,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
+ (5725 - 5875 @ 80), (14)
country MR: DFS-ETSI
(2402 - 2482 @ 40), (20)
@@ -969,15 +794,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1044,15 +861,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1061,15 +870,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1111,6 +912,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
+ (5725 - 5875 @ 80), (14)
country PG: DFS-FCC
(2402 - 2482 @ 40), (20)
@@ -1137,15 +939,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1154,6 +948,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
+ (5725 - 5875 @ 80), (14)
country PR: DFS-FCC
(2402 - 2472 @ 40), (30)
@@ -1174,15 +969,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1220,15 +1007,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1239,6 +1018,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1268,15 +1048,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1294,15 +1066,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1311,15 +1075,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5725 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1379,6 +1135,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
+ (5725 - 5875 @ 80), (14)
country TT:
(2402 - 2482 @ 40), (20)
@@ -1430,18 +1187,6 @@
(5250 - 5330 @ 80), (24), DFS, AUTO-BW
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
- # 5.9ghz band
- # reference: https://apps.fcc.gov/edocs_public/attachmatch/FCC-03-324A1.pdf
- (5842 - 5863 @ 5), (30)
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5865 - 5885 @ 20), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5895 - 5915 @ 20), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
# 60g band
# reference: http://cfr.regstoday.com/47cfr15.aspx#47_CFR_15p255
# channels 1,2,3,4,5,6 EIRP=40dBm(43dBm peak)
@@ -1465,6 +1210,7 @@
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
(5490 - 5710 @ 160), (30), DFS
+ (5725 - 5875 @ 80), (14)
country VE: DFS-FCC
(2402 - 2482 @ 40), (20)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index febd5ab..76e308f 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -11058,7 +11058,8 @@
break;
case NL80211_NAN_FUNC_FOLLOW_UP:
if (!tb[NL80211_NAN_FUNC_FOLLOW_UP_ID] ||
- !tb[NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID]) {
+ !tb[NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID] ||
+ !tb[NL80211_NAN_FUNC_FOLLOW_UP_DEST]) {
err = -EINVAL;
goto out;
}
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 2d64e0f..0dc41fd 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -1012,6 +1012,8 @@
wdev->current_bss = NULL;
wdev->ssid_len = 0;
wdev->conn_owner_nlportid = 0;
+ kzfree(wdev->connect_keys);
+ wdev->connect_keys = NULL;
nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include
index 140a0fa2..0f101f7 100644
--- a/scripts/Kbuild.include
+++ b/scripts/Kbuild.include
@@ -147,6 +147,37 @@
# Expands to either gcc or clang
cc-name = $(shell $(CC) -v 2>&1 | grep -q "clang version" && echo clang || echo gcc)
+# __cc-version
+# Returns compiler version
+__cc-version = $(shell $(CONFIG_SHELL) $(srctree)/scripts/$(cc-name)-version.sh $(CC))
+
+# __cc-fullversion
+# Returns full compiler version
+__cc-fullversion = $(shell $(CONFIG_SHELL) \
+ $(srctree)/scripts/$(cc-name)-version.sh -p $(CC))
+
+# __cc-ifversion
+# Matches compiler name and version
+# Usage: EXTRA_CFLAGS += $(call cc-if-name-version, gcc, -lt, 0402, -O1)
+__cc-ifversion = $(shell [ $(cc-name) = $(1) ] && [ $(__cc-version) $(2) $(3) ] && echo $(4) || echo $(5))
+
+# __cc-if-fullversion
+# Matches compiler name and full version
+# Usage: EXTRA_CFLAGS += $(call cc-if-name-fullversion, gcc, -lt, 040502, -O1)
+__cc-if-fullversion = $(shell [ $(cc-name) = $(1) ] && [ $(__cc-fullversion) $(2) $(3) ] && echo $(4) || echo $(5))
+
+# gcc-ifversion
+gcc-ifversion = $(call __cc-ifversion, gcc, $(1), $(2), $(3), $(4))
+
+# gcc-if-fullversion
+gcc-if-fullversion = (call __cc-if-fullversion, gcc, $(1), $(2), $(3), $(4))
+
+# clang-ifversion
+clang-ifversion = $(call __cc-ifversion, clang, $(1), $(2), $(3), $(4))
+
+# clang-if-fullversion
+clang-if-fullversion = (call __cc-if-fullversion, clang, $(1), $(2), $(3), $(4))
+
# cc-version
cc-version = $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh $(CC))
@@ -154,9 +185,9 @@
cc-fullversion = $(shell $(CONFIG_SHELL) \
$(srctree)/scripts/gcc-version.sh -p $(CC))
-# cc-ifversion
-# Usage: EXTRA_CFLAGS += $(call cc-ifversion, -lt, 0402, -O1)
-cc-ifversion = $(shell [ $(cc-version) $(1) $(2) ] && echo $(3) || echo $(4))
+# backward compatibility
+cc-ifversion = $(gcc-ifversion)
+cc-if-fullversion = $(gcc-if-fullversion)
# cc-ldoption
# Usage: ldflags += $(call cc-ldoption, -Wl$(comma)--hash-style=both)
@@ -173,6 +204,10 @@
# Important: no spaces around options
ar-option = $(call try-run, $(AR) rc$(1) "$$TMP",$(1),$(2))
+# ld-name
+# Expands to either bfd or gold
+ld-name = $(shell $(LD) -v 2>&1 | grep -q "GNU gold" && echo gold || echo bfd)
+
# ld-version
# Note this is mainly for HJ Lu's 3 number binutil versions
ld-version = $(shell $(LD) --version | $(srctree)/scripts/ld-version.sh)
@@ -181,6 +216,18 @@
# Usage: $(call ld-ifversion, -ge, 22252, y)
ld-ifversion = $(shell [ $(ld-version) $(1) $(2) ] && echo $(3) || echo $(4))
+# __ld-ifversion
+# Usage: $(call __ld-ifversion, gold, -ge, 112000000, y)
+__ld-ifversion = $(shell [ $(ld-name) = $(1) ] && [ $(ld-version) $(2) $(3) ] && echo $(4) || echo $(5))
+
+# bfd-ifversion
+# Usage: $(call bfd-ifversion, -ge, 227000000, y)
+bfd-ifversion = $(call __ld-ifversion, bfd, $(1), $(2), $(3), $(4))
+
+# gold-ifversion
+# Usage: $(call gold-ifversion, -ge, 112000000, y)
+gold-ifversion = $(call __ld-ifversion, gold, $(1), $(2), $(3), $(4))
+
######
###
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 3ecec72..a9c1da5 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -213,6 +213,23 @@
cmd_cc_o_c = $(CC) $(c_flags) -c -o $(@D)/.tmp_$(@F) $<
+ifdef CONFIG_LTO_CLANG
+# Generate .o.symversions files for each .o with exported symbols, and link these
+# to the kernel and/or modules at the end.
+cmd_modversions_c = \
+ if $(OBJDUMP) -h $(@D)/.tmp_$(@F) >/dev/null 2>/dev/null; then \
+ if $(OBJDUMP) -h $(@D)/.tmp_$(@F) | grep -q __ksymtab; then \
+ $(call cmd_gensymtypes_c,$(KBUILD_SYMTYPES),$(@:.o=.symtypes)) \
+ > $(@D)/$(@F).symversions; \
+ fi; \
+ else \
+ if $(LLVM_DIS) -o=- $(@D)/.tmp_$(@F) | grep -q __ksymtab; then \
+ $(call cmd_gensymtypes_c,$(KBUILD_SYMTYPES),$(@:.o=.symtypes)) \
+ > $(@D)/$(@F).symversions; \
+ fi; \
+ fi; \
+ mv -f $(@D)/.tmp_$(@F) $@;
+else
cmd_modversions_c = \
if $(OBJDUMP) -h $(@D)/.tmp_$(@F) | grep -q __ksymtab; then \
$(call cmd_gensymtypes_c,$(KBUILD_SYMTYPES),$(@:.o=.symtypes)) \
@@ -225,12 +242,19 @@
mv -f $(@D)/.tmp_$(@F) $@; \
fi;
endif
+endif
ifdef CONFIG_FTRACE_MCOUNT_RECORD
ifdef BUILD_C_RECORDMCOUNT
ifeq ("$(origin RECORDMCOUNT_WARN)", "command line")
RECORDMCOUNT_FLAGS = -w
endif
+
+ifdef CONFIG_LTO_CLANG
+# With LTO, we postpone running recordmcount until after the LTO link step, so
+# let's export the parameters for the link script.
+export RECORDMCOUNT_FLAGS
+else
# Due to recursion, we must skip empty.o.
# The empty.o file is created in the make process in order to determine
# the target endianness and word size. It is made before all other C
@@ -239,23 +263,29 @@
if [ $(@) != "scripts/mod/empty.o" ]; then \
$(objtree)/scripts/recordmcount $(RECORDMCOUNT_FLAGS) "$(@)"; \
fi;
+endif
+
recordmcount_source := $(srctree)/scripts/recordmcount.c \
$(srctree)/scripts/recordmcount.h
-else
+else # !BUILD_C_RECORDMCOUNT
sub_cmd_record_mcount = set -e ; perl $(srctree)/scripts/recordmcount.pl "$(ARCH)" \
"$(if $(CONFIG_CPU_BIG_ENDIAN),big,little)" \
"$(if $(CONFIG_64BIT),64,32)" \
"$(OBJDUMP)" "$(OBJCOPY)" "$(CC) $(KBUILD_CFLAGS)" \
"$(LD)" "$(NM)" "$(RM)" "$(MV)" \
"$(if $(part-of-module),1,0)" "$(@)";
+
recordmcount_source := $(srctree)/scripts/recordmcount.pl
-endif
+endif # BUILD_C_RECORDMCOUNT
+
+ifndef CONFIG_LTO_CLANG
cmd_record_mcount = \
if [ "$(findstring $(CC_FLAGS_FTRACE),$(_c_flags))" = \
"$(CC_FLAGS_FTRACE)" ]; then \
$(sub_cmd_record_mcount) \
fi;
endif
+endif # CONFIG_FTRACE_MCOUNT_RECORD
ifdef CONFIG_STACK_VALIDATION
ifneq ($(SKIP_STACK_VALIDATION),1)
@@ -439,9 +469,30 @@
#
ifdef builtin-target
+ifdef CONFIG_LTO_CLANG
+ ifdef CONFIG_MODVERSIONS
+ # combine symversions for later processing
+ update_lto_symversions = \
+ rm -f $@.symversions; \
+ for i in $(filter-out FORCE,$^); do \
+ if [ -f $$i.symversions ]; then \
+ cat $$i.symversions \
+ >> $@.symversions; \
+ fi; \
+ done;
+ endif
+ # rebuild the symbol table with llvm-ar to include IR files
+ update_lto_symtable = ; \
+ mv -f $@ $@.tmp; \
+ $(LLVM_AR) rcsT$(KBUILD_ARFLAGS) $@ \
+ $$($(AR) t $@.tmp); \
+ rm -f $@.tmp
+endif
+
ifdef CONFIG_THIN_ARCHIVES
- cmd_make_builtin = rm -f $@; $(AR) rcST$(KBUILD_ARFLAGS)
- cmd_make_empty_builtin = rm -f $@; $(AR) rcST$(KBUILD_ARFLAGS)
+ cmd_make_builtin = $(update_lto_symversions) \
+ rm -f $@; $(AR) rcSTP$(KBUILD_ARFLAGS)
+ cmd_make_empty_builtin = rm -f $@; $(AR) rcSTP$(KBUILD_ARFLAGS)
quiet_cmd_link_o_target = AR $@
else
cmd_make_builtin = $(LD) $(ld_flags) -r -o
@@ -481,7 +532,11 @@
quiet_cmd_link_l_target = AR $@
ifdef CONFIG_THIN_ARCHIVES
- cmd_link_l_target = rm -f $@; $(AR) rcsT$(KBUILD_ARFLAGS) $@ $(lib-y)
+ cmd_link_l_target = \
+ $(update_lto_symversions) \
+ rm -f $@; \
+ $(AR) rcsTP$(KBUILD_ARFLAGS) $@ $(lib-y) \
+ $(update_lto_symtable)
else
cmd_link_l_target = rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@ $(lib-y)
endif
@@ -499,14 +554,36 @@
ref_prefix = EXTERN(
endif
-quiet_cmd_export_list = EXPORTS $@
-cmd_export_list = $(OBJDUMP) -h $< | \
- sed -ne '/___ksymtab/{s/.*+/$(ref_prefix)/;s/ .*/)/;p}' >$(ksyms-lds);\
- rm -f $(dummy-object);\
- $(AR) rcs$(KBUILD_ARFLAGS) $(dummy-object);\
+filter_export_list = sed -ne '/___ksymtab/s/.*+\([^ "]*\).*/$(ref_prefix)\1)/p'
+link_export_list = rm -f $(dummy-object);\
+ echo | $(CC) $(a_flags) -c -o $(dummy-object) -x assembler -;\
$(LD) $(ld_flags) -r -o $@ -T $(ksyms-lds) $(dummy-object);\
rm $(dummy-object) $(ksyms-lds)
+quiet_cmd_export_list = EXPORTS $@
+
+ifdef CONFIG_LTO_CLANG
+# objdump doesn't understand IR files and llvm-dis doesn't support archives,
+# so we'll walk through each file in the archive separately
+cmd_export_list = \
+ rm -f $(ksyms-lds); \
+ for o in $$($(AR) t $<); do \
+ if $(OBJDUMP) -h $$o >/dev/null 2>/dev/null; then \
+ $(OBJDUMP) -h $$o | \
+ $(filter_export_list) \
+ >>$(ksyms-lds); \
+ else \
+ $(LLVM_DIS) -o=- $$o | \
+ $(filter_export_list) \
+ >>$(ksyms-lds); \
+ fi; \
+ done; \
+ $(link_export_list)
+else
+cmd_export_list = $(OBJDUMP) -h $< | $(filter_export_list) >$(ksyms-lds); \
+ $(link_export_list)
+endif
+
$(obj)/lib-ksyms.o: $(lib-target) FORCE
$(call if_changed,export_list)
@@ -530,20 +607,36 @@
$($(subst $(obj)/,,$(@:.o=-y))) \
$($(subst $(obj)/,,$(@:.o=-m)))), $^)
-quiet_cmd_link_multi-y = LD $@
-cmd_link_multi-y = $(LD) $(ld_flags) -r -o $@ $(link_multi_deps) $(cmd_secanalysis)
+cmd_link_multi-link = $(LD) $(ld_flags) -r -o $@ $(link_multi_deps) $(cmd_secanalysis)
+
+ifdef CONFIG_THIN_ARCHIVES
+ quiet_cmd_link_multi-y = AR $@
+ cmd_link_multi-y = $(update_lto_symversions) \
+ rm -f $@; $(AR) rcSTP$(KBUILD_ARFLAGS) $@ $(link_multi_deps) \
+ $(update_lto_symtable)
+else
+ quiet_cmd_link_multi-y = LD $@
+ cmd_link_multi-y = $(cmd_link_multi-link)
+endif
quiet_cmd_link_multi-m = LD [M] $@
-cmd_link_multi-m = $(cmd_link_multi-y)
+
+ifdef CONFIG_LTO_CLANG
+ # don't compile IR until needed
+ cmd_link_multi-m = $(cmd_link_multi-y)
+else
+ cmd_link_multi-m = $(cmd_link_multi-link)
+endif
$(multi-used-y): FORCE
$(call if_changed,link_multi-y)
-$(call multi_depend, $(multi-used-y), .o, -objs -y)
$(multi-used-m): FORCE
$(call if_changed,link_multi-m)
@{ echo $(@:.o=.ko); echo $(link_multi_deps); \
$(cmd_undef_syms); } > $(MODVERDIR)/$(@F:.o=.mod)
+
+$(call multi_depend, $(multi-used-y), .o, -objs -y)
$(call multi_depend, $(multi-used-m), .o, -objs -y -m)
targets += $(multi-used-y) $(multi-used-m)
diff --git a/scripts/Makefile.kasan b/scripts/Makefile.kasan
index 37323b0..8c69cd1 100644
--- a/scripts/Makefile.kasan
+++ b/scripts/Makefile.kasan
@@ -9,10 +9,7 @@
CFLAGS_KASAN_MINIMAL := -fsanitize=kernel-address
-CFLAGS_KASAN := $(call cc-option, -fsanitize=kernel-address \
- -fasan-shadow-offset=$(KASAN_SHADOW_OFFSET) \
- --param asan-stack=1 --param asan-globals=1 \
- --param asan-instrumentation-with-call-threshold=$(call_threshold))
+cc-param = $(call cc-option, -mllvm -$(1), $(call cc-option, --param $(1)))
ifeq ($(call cc-option, $(CFLAGS_KASAN_MINIMAL) -Werror),)
ifneq ($(CONFIG_COMPILE_TEST),y)
@@ -20,12 +17,23 @@
-fsanitize=kernel-address is not supported by compiler)
endif
else
- ifeq ($(CFLAGS_KASAN),)
- ifneq ($(CONFIG_COMPILE_TEST),y)
- $(warning CONFIG_KASAN: compiler does not support all options.\
- Trying minimal configuration)
- endif
- CFLAGS_KASAN := $(CFLAGS_KASAN_MINIMAL)
- endif
+ # -fasan-shadow-offset fails without -fsanitize
+ CFLAGS_KASAN_SHADOW := $(call cc-option, -fsanitize=kernel-address \
+ -fasan-shadow-offset=$(KASAN_SHADOW_OFFSET), \
+ $(call cc-option, -fsanitize=kernel-address \
+ -mllvm -asan-mapping-offset=$(KASAN_SHADOW_OFFSET)))
+
+ ifeq ($(strip $(CFLAGS_KASAN_SHADOW)),)
+ CFLAGS_KASAN := $(CFLAGS_KASAN_MINIMAL)
+ else
+ # Now add all the compiler specific options that are valid standalone
+ CFLAGS_KASAN := $(CFLAGS_KASAN_SHADOW) \
+ $(call cc-param,asan-globals=1) \
+ $(call cc-param,asan-instrumentation-with-call-threshold=$(call_threshold)) \
+ $(call cc-param,asan-stack=1) \
+ $(call cc-param,asan-use-after-scope=1) \
+ $(call cc-param,asan-instrument-allocas=1)
+ endif
+
endif
endif
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 6bc33ee..ea98308 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -290,11 +290,11 @@
echo '\#include <asm-generic/vmlinux.lds.h>'; \
echo '.section .dtb.init.rodata,"a"'; \
echo '.balign STRUCT_ALIGNMENT'; \
- echo '.global __dtb_$(*F)_begin'; \
- echo '__dtb_$(*F)_begin:'; \
+ echo '.global __dtb_$(subst -,_,$(*F))_begin'; \
+ echo '__dtb_$(subst -,_,$(*F))_begin:'; \
echo '.incbin "$<" '; \
- echo '__dtb_$(*F)_end:'; \
- echo '.global __dtb_$(*F)_end'; \
+ echo '__dtb_$(subst -,_,$(*F))_end:'; \
+ echo '.global __dtb_$(subst -,_,$(*F))_end'; \
echo '.balign STRUCT_ALIGNMENT'; \
) > $@
diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost
index 16923ba..b7c50cb 100644
--- a/scripts/Makefile.modpost
+++ b/scripts/Makefile.modpost
@@ -82,12 +82,28 @@
MODPOST_OPT=$(subst -i,-n,$(filter -i,$(MAKEFLAGS)))
+# If CONFIG_LTO_CLANG is enabled, .o files are either LLVM IR, or empty, so we
+# need to link them into actual objects before passing them to modpost
+modpost-ext = $(if $(CONFIG_LTO_CLANG),.lto,)
+
+ifdef CONFIG_LTO_CLANG
+quiet_cmd_cc_lto_link_modules = LD [M] $@
+cmd_cc_lto_link_modules = \
+ $(LD) $(ld_flags) -r -o $(@) \
+ $(shell [ -s $(@:$(modpost-ext).o=.o.symversions) ] && \
+ echo -T $(@:$(modpost-ext).o=.o.symversions)) \
+ --whole-archive $(filter-out FORCE,$^)
+
+$(modules:.ko=$(modpost-ext).o): %$(modpost-ext).o: %.o FORCE
+ $(call if_changed,cc_lto_link_modules)
+endif
+
# We can go over command line length here, so be careful.
quiet_cmd_modpost = MODPOST $(words $(filter-out vmlinux FORCE, $^)) modules
- cmd_modpost = $(MODLISTCMD) | sed 's/\.ko$$/.o/' | $(modpost) $(MODPOST_OPT) -s -T -
+ cmd_modpost = $(MODLISTCMD) | sed 's/\.ko$$/$(modpost-ext)\.o/' | $(modpost) $(MODPOST_OPT) -s -T -
PHONY += __modpost
-__modpost: $(modules:.ko=.o) FORCE
+__modpost: $(modules:.ko=$(modpost-ext).o) FORCE
$(call cmd,modpost) $(wildcard vmlinux)
quiet_cmd_kernel-mod = MODPOST $@
@@ -98,8 +114,7 @@
# Declare generated files as targets for modpost
$(symverfile): __modpost ;
-$(modules:.ko=.mod.c): __modpost ;
-
+$(modules:.ko=$(modpost-ext).mod.c): __modpost ;
# Step 5), compile all *.mod.c files
@@ -110,22 +125,37 @@
cmd_cc_o_c = $(CC) $(c_flags) $(KBUILD_CFLAGS_MODULE) $(CFLAGS_MODULE) \
-c -o $@ $<
-$(modules:.ko=.mod.o): %.mod.o: %.mod.c FORCE
+$(modules:.ko=.mod.o): %.mod.o: %$(modpost-ext).mod.c FORCE
$(call if_changed_dep,cc_o_c)
-targets += $(modules:.ko=.mod.o)
+targets += $(modules:.ko=$(modpost-ext).mod.o)
ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
# Step 6), final link of the modules with optional arch pass after final link
quiet_cmd_ld_ko_o = LD [M] $@
+
+ifdef CONFIG_LTO_CLANG
+ cmd_ld_ko_o = \
+ $(LD) -r $(LDFLAGS) \
+ $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
+ $(shell [ -s $(@:.ko=.o.symversions) ] && \
+ echo -T $(@:.ko=.o.symversions)) \
+ -o $@ --whole-archive \
+ $(filter-out FORCE,$(^:$(modpost-ext).o=.o))
+
+ ifdef CONFIG_FTRACE_MCOUNT_RECORD
+ cmd_ld_ko_o += ; $(objtree)/scripts/recordmcount $(RECORDMCOUNT_FLAGS) $@
+ endif
+else
cmd_ld_ko_o = \
$(LD) -r $(LDFLAGS) \
$(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
-o $@ $(filter-out FORCE,$^) ; \
$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
+endif
-$(modules): %.ko :%.o %.mod.o FORCE
+$(modules): %.ko: %$(modpost-ext).o %.mod.o FORCE
+$(call if_changed,ld_ko_o)
targets += $(modules)
diff --git a/scripts/clang-version.sh b/scripts/clang-version.sh
new file mode 100755
index 0000000..9780efa
--- /dev/null
+++ b/scripts/clang-version.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+#
+# clang-version [-p] clang-command
+#
+# Prints the compiler version of `clang-command' in a canonical 4-digit form
+# such as `0500' for clang-5.0 etc.
+#
+# With the -p option, prints the patchlevel as well, for example `050001' for
+# clang-5.0.1 etc.
+#
+
+if [ "$1" = "-p" ] ; then
+ with_patchlevel=1;
+ shift;
+fi
+
+compiler="$*"
+
+if [ ${#compiler} -eq 0 ]; then
+ echo "Error: No compiler specified."
+ printf "Usage:\n\t$0 <clang-command>\n"
+ exit 1
+fi
+
+MAJOR=$(echo __clang_major__ | $compiler -E -x c - | tail -n 1)
+MINOR=$(echo __clang_minor__ | $compiler -E -x c - | tail -n 1)
+if [ "x$with_patchlevel" != "x" ] ; then
+ PATCHLEVEL=$(echo __clang_patchlevel__ | $compiler -E -x c - | tail -n 1)
+ printf "%02d%02d%02d\\n" $MAJOR $MINOR $PATCHLEVEL
+else
+ printf "%02d%02d\\n" $MAJOR $MINOR
+fi
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index 1b9e67b..7858b30 100644
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -53,9 +53,40 @@
${AR} rcsT${KBUILD_ARFLAGS} built-in.o \
${KBUILD_VMLINUX_INIT} \
${KBUILD_VMLINUX_MAIN}
+
+ if [ -n "${CONFIG_LTO_CLANG}" ]; then
+ mv -f built-in.o built-in.o.tmp
+ ${LLVM_AR} rcsT${KBUILD_ARFLAGS} built-in.o $(${AR} t built-in.o.tmp)
+ rm -f built-in.o.tmp
+ fi
fi
}
+# If CONFIG_LTO_CLANG is selected, collect generated symbol versions into
+# .tmp_symversions
+modversions()
+{
+ if [ -z "${CONFIG_LTO_CLANG}" ]; then
+ return
+ fi
+
+ if [ -z "${CONFIG_MODVERSIONS}" ]; then
+ return
+ fi
+
+ rm -f .tmp_symversions
+
+ for a in built-in.o ${KBUILD_VMLINUX_LIBS}; do
+ for o in $(${AR} t $a); do
+ if [ -f ${o}.symversions ]; then
+ cat ${o}.symversions >> .tmp_symversions
+ fi
+ done
+ done
+
+ echo "-T .tmp_symversions"
+}
+
# Link of vmlinux.o used for section mismatch analysis
# ${1} output file
modpost_link()
@@ -70,7 +101,29 @@
${KBUILD_VMLINUX_MAIN} \
--end-group"
fi
- ${LD} ${LDFLAGS} -r -o ${1} ${objects}
+
+ if [ -n "${CONFIG_LTO_CLANG}" ]; then
+ # This might take a while, so indicate that we're doing
+ # an LTO link
+ info LTO vmlinux.o
+ else
+ info LD vmlinux.o
+ fi
+
+ ${LD} ${LDFLAGS} -r -o ${1} $(modversions) ${objects}
+}
+
+# If CONFIG_LTO_CLANG is selected, we postpone running recordmcount until
+# we have compiled LLVM IR to an object file.
+recordmcount()
+{
+ if [ -z "${CONFIG_LTO_CLANG}" ]; then
+ return
+ fi
+
+ if [ -n "${CONFIG_FTRACE_MCOUNT_RECORD}" ]; then
+ scripts/recordmcount ${RECORDMCOUNT_FLAGS} $*
+ fi
}
# Link of vmlinux
@@ -82,7 +135,15 @@
local objects
if [ "${SRCARCH}" != "um" ]; then
- if [ -n "${CONFIG_THIN_ARCHIVES}" ]; then
+ local ld=${LD}
+ local ldflags="${LDFLAGS} ${LDFLAGS_vmlinux}"
+
+ if [ -n "${LDFINAL_vmlinux}" ]; then
+ ld=${LDFINAL_vmlinux}
+ ldflags="${LDFLAGS_FINAL_vmlinux} ${LDFLAGS_vmlinux}"
+ fi
+
+ if [[ -n "${CONFIG_THIN_ARCHIVES}" && -z "${CONFIG_LTO_CLANG}" ]]; then
objects="--whole-archive built-in.o ${1}"
else
objects="${KBUILD_VMLINUX_INIT} \
@@ -92,8 +153,7 @@
${1}"
fi
- ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2} \
- -T ${lds} ${objects}
+ ${ld} ${ldflags} -o ${2} -T ${lds} ${objects}
else
if [ -n "${CONFIG_THIN_ARCHIVES}" ]; then
objects="-Wl,--whole-archive built-in.o ${1}"
@@ -113,7 +173,6 @@
fi
}
-
# Create ${2} .o file with all symbols from the ${1} object file
kallsyms()
{
@@ -183,6 +242,7 @@
rm -f .tmp_System.map
rm -f .tmp_kallsyms*
rm -f .tmp_version
+ rm -f .tmp_symversions
rm -f .tmp_vmlinux*
rm -f built-in.o
rm -f System.map
@@ -230,15 +290,6 @@
. "./${KCONFIG_CONFIG}"
esac
-archive_builtin
-
-#link vmlinux.o
-info LD vmlinux.o
-modpost_link vmlinux.o
-
-# modpost vmlinux.o to check for section mismatches
-${MAKE} -f "${srctree}/scripts/Makefile.modpost" vmlinux.o
-
# Update version
info GEN .version
if [ ! -r .version ]; then
@@ -249,9 +300,27 @@
expr 0$(cat .old_version) + 1 >.version;
fi;
+archive_builtin
+
+#link vmlinux.o
+modpost_link vmlinux.o
+
+# modpost vmlinux.o to check for section mismatches
+${MAKE} -f "${srctree}/scripts/Makefile.modpost" vmlinux.o
+
# final build of init/
${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init GCC_PLUGINS_CFLAGS="${GCC_PLUGINS_CFLAGS}"
+if [ -n "${CONFIG_LTO_CLANG}" ]; then
+ # Re-use vmlinux.o, so we can avoid the slow LTO link step in
+ # vmlinux_link
+ KBUILD_VMLINUX_INIT=
+ KBUILD_VMLINUX_MAIN=vmlinux.o
+
+ # Call recordmcount if needed
+ recordmcount vmlinux.o
+fi
+
# Generate RTIC MP placeholder compile unit of the correct size
# and add it to the list of link objects
# this needs to be done before generating kallsyms
diff --git a/scripts/mod/Makefile b/scripts/mod/Makefile
index b497d97..247cf1f 100644
--- a/scripts/mod/Makefile
+++ b/scripts/mod/Makefile
@@ -1,4 +1,5 @@
OBJECT_FILES_NON_STANDARD := y
+CFLAGS_empty.o += $(DISABLE_LTO)
hostprogs-y := modpost mk_elfconfig
always := $(hostprogs-y) empty.o
diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c
index 5423a58..e0c5082 100644
--- a/scripts/recordmcount.c
+++ b/scripts/recordmcount.c
@@ -366,7 +366,8 @@
strcmp(".softirqentry.text", txtname) == 0 ||
strcmp(".kprobes.text", txtname) == 0 ||
strcmp(".cpuidle.text", txtname) == 0 ||
- strcmp(".text.unlikely", txtname) == 0;
+ (strncmp(".text.", txtname, 6) == 0 &&
+ strcmp(".text..ftrace", txtname) != 0);
}
/* 32 bit and 64 bit are very similar */
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 935752c..777e132 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -656,11 +656,11 @@
.get = param_get_aalockpolicy
};
-static int param_set_audit(const char *val, struct kernel_param *kp);
-static int param_get_audit(char *buffer, struct kernel_param *kp);
+static int param_set_audit(const char *val, const struct kernel_param *kp);
+static int param_get_audit(char *buffer, const struct kernel_param *kp);
-static int param_set_mode(const char *val, struct kernel_param *kp);
-static int param_get_mode(char *buffer, struct kernel_param *kp);
+static int param_set_mode(const char *val, const struct kernel_param *kp);
+static int param_get_mode(char *buffer, const struct kernel_param *kp);
/* Flag values, also controllable via /sys/module/apparmor/parameters
* We define special types as we want to do additional mediation.
@@ -707,7 +707,7 @@
/* Maximum pathname length before accesses will start getting rejected */
unsigned int aa_g_path_max = 2 * PATH_MAX;
-module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR | S_IWUSR);
+module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR);
/* Determines how paranoid loading of policy is and how much verification
* on the loaded policy is done.
@@ -774,7 +774,7 @@
return param_get_uint(buffer, kp);
}
-static int param_get_audit(char *buffer, struct kernel_param *kp)
+static int param_get_audit(char *buffer, const struct kernel_param *kp)
{
if (!policy_view_capable())
return -EPERM;
@@ -785,7 +785,7 @@
return sprintf(buffer, "%s", audit_mode_names[aa_g_audit]);
}
-static int param_set_audit(const char *val, struct kernel_param *kp)
+static int param_set_audit(const char *val, const struct kernel_param *kp)
{
int i;
if (!policy_admin_capable())
@@ -807,7 +807,7 @@
return -EINVAL;
}
-static int param_get_mode(char *buffer, struct kernel_param *kp)
+static int param_get_mode(char *buffer, const struct kernel_param *kp)
{
if (!policy_admin_capable())
return -EPERM;
@@ -818,7 +818,7 @@
return sprintf(buffer, "%s", aa_profile_mode_names[aa_g_profile_mode]);
}
-static int param_set_mode(const char *val, struct kernel_param *kp)
+static int param_set_mode(const char *val, const struct kernel_param *kp)
{
int i;
if (!policy_admin_capable())
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 6830d24..7bf8b00 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -207,7 +207,8 @@
if (opened & FILE_CREATED)
iint->flags |= IMA_NEW_FILE;
if ((iint->flags & IMA_NEW_FILE) &&
- !(iint->flags & IMA_DIGSIG_REQUIRED))
+ (!(iint->flags & IMA_DIGSIG_REQUIRED) ||
+ (inode->i_size == 0)))
status = INTEGRITY_PASS;
goto out;
}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 3b02b82..da939fd 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -4347,10 +4347,18 @@
u32 sid, node_perm;
if (family == PF_INET) {
+ if (addrlen < sizeof(struct sockaddr_in)) {
+ err = -EINVAL;
+ goto out;
+ }
addr4 = (struct sockaddr_in *)address;
snum = ntohs(addr4->sin_port);
addrp = (char *)&addr4->sin_addr.s_addr;
} else {
+ if (addrlen < SIN6_LEN_RFC2133) {
+ err = -EINVAL;
+ goto out;
+ }
addr6 = (struct sockaddr_in6 *)address;
snum = ntohs(addr6->sin6_port);
addrp = (char *)&addr6->sin6_addr.s6_addr;
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index 3321348..3e7c357 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -1814,10 +1814,9 @@
return -ENOMEM;
_snd_pcm_hw_params_any(params);
err = snd_pcm_hw_refine(substream, params);
- format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
- kfree(params);
if (err < 0)
- return err;
+ goto error;
+ format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
for (fmt = 0; fmt < 32; ++fmt) {
if (snd_mask_test(&format_mask, fmt)) {
int f = snd_pcm_oss_format_to(fmt);
@@ -1825,7 +1824,10 @@
formats |= f;
}
}
- return formats;
+
+ error:
+ kfree(params);
+ return err < 0 ? err : formats;
}
static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int format)
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 0b40861..ecd1c5f 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -255,12 +255,12 @@
if (!client)
return 0;
- snd_seq_delete_all_ports(client);
- snd_seq_queue_client_leave(client->number);
spin_lock_irqsave(&clients_lock, flags);
clienttablock[client->number] = 1;
clienttab[client->number] = NULL;
spin_unlock_irqrestore(&clients_lock, flags);
+ snd_seq_delete_all_ports(client);
+ snd_seq_queue_client_leave(client->number);
snd_use_lock_sync(&client->use_lock);
snd_seq_queue_client_termination(client->number);
if (client->pool)
@@ -906,7 +906,8 @@
static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
struct snd_seq_event *event,
struct file *file, int blocking,
- int atomic, int hop)
+ int atomic, int hop,
+ struct mutex *mutexp)
{
struct snd_seq_event_cell *cell;
int err;
@@ -944,7 +945,8 @@
return -ENXIO; /* queue is not allocated */
/* allocate an event cell */
- err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic, file);
+ err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic,
+ file, mutexp);
if (err < 0)
return err;
@@ -1013,12 +1015,11 @@
return -ENXIO;
/* allocate the pool now if the pool is not allocated yet */
+ mutex_lock(&client->ioctl_mutex);
if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) {
- mutex_lock(&client->ioctl_mutex);
err = snd_seq_pool_init(client->pool);
- mutex_unlock(&client->ioctl_mutex);
if (err < 0)
- return -ENOMEM;
+ goto out;
}
/* only process whole events */
@@ -1069,7 +1070,7 @@
/* ok, enqueue it */
err = snd_seq_client_enqueue_event(client, &event, file,
!(file->f_flags & O_NONBLOCK),
- 0, 0);
+ 0, 0, &client->ioctl_mutex);
if (err < 0)
break;
@@ -1080,6 +1081,8 @@
written += len;
}
+ out:
+ mutex_unlock(&client->ioctl_mutex);
return written ? written : err;
}
@@ -1835,6 +1838,9 @@
(! snd_seq_write_pool_allocated(client) ||
info->output_pool != client->pool->size)) {
if (snd_seq_write_pool_allocated(client)) {
+ /* is the pool in use? */
+ if (atomic_read(&client->pool->counter))
+ return -EBUSY;
/* remove all existing cells */
snd_seq_pool_mark_closing(client->pool);
snd_seq_queue_client_leave_cells(client->number);
@@ -2259,7 +2265,8 @@
if (! cptr->accept_output)
result = -EPERM;
else /* send it */
- result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, atomic, hop);
+ result = snd_seq_client_enqueue_event(cptr, ev, file, blocking,
+ atomic, hop, NULL);
snd_seq_client_unlock(cptr);
return result;
diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c
index 3490d21..9acbed1 100644
--- a/sound/core/seq/seq_fifo.c
+++ b/sound/core/seq/seq_fifo.c
@@ -123,7 +123,7 @@
return -EINVAL;
snd_use_lock_use(&f->use_lock);
- err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */
+ err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL, NULL); /* always non-blocking */
if (err < 0) {
if ((err == -ENOMEM) || (err == -EAGAIN))
atomic_inc(&f->overflow);
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
index 5847c44..4c8cbcd 100644
--- a/sound/core/seq/seq_memory.c
+++ b/sound/core/seq/seq_memory.c
@@ -221,7 +221,8 @@
*/
static int snd_seq_cell_alloc(struct snd_seq_pool *pool,
struct snd_seq_event_cell **cellp,
- int nonblock, struct file *file)
+ int nonblock, struct file *file,
+ struct mutex *mutexp)
{
struct snd_seq_event_cell *cell;
unsigned long flags;
@@ -245,7 +246,11 @@
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&pool->output_sleep, &wait);
spin_unlock_irq(&pool->lock);
+ if (mutexp)
+ mutex_unlock(mutexp);
schedule();
+ if (mutexp)
+ mutex_lock(mutexp);
spin_lock_irq(&pool->lock);
remove_wait_queue(&pool->output_sleep, &wait);
/* interrupted? */
@@ -288,7 +293,7 @@
*/
int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
struct snd_seq_event_cell **cellp, int nonblock,
- struct file *file)
+ struct file *file, struct mutex *mutexp)
{
int ncells, err;
unsigned int extlen;
@@ -305,7 +310,7 @@
if (ncells >= pool->total_elements)
return -ENOMEM;
- err = snd_seq_cell_alloc(pool, &cell, nonblock, file);
+ err = snd_seq_cell_alloc(pool, &cell, nonblock, file, mutexp);
if (err < 0)
return err;
@@ -331,7 +336,8 @@
int size = sizeof(struct snd_seq_event);
if (len < size)
size = len;
- err = snd_seq_cell_alloc(pool, &tmp, nonblock, file);
+ err = snd_seq_cell_alloc(pool, &tmp, nonblock, file,
+ mutexp);
if (err < 0)
goto __error;
if (cell->event.data.ext.ptr == NULL)
diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h
index 32f959c..3abe306 100644
--- a/sound/core/seq/seq_memory.h
+++ b/sound/core/seq/seq_memory.h
@@ -66,7 +66,8 @@
void snd_seq_cell_free(struct snd_seq_event_cell *cell);
int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
- struct snd_seq_event_cell **cellp, int nonblock, struct file *file);
+ struct snd_seq_event_cell **cellp, int nonblock,
+ struct file *file, struct mutex *mutexp);
/* return number of unused (free) cells */
static inline int snd_seq_unused_cells(struct snd_seq_pool *pool)
diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c
index bc1c848..2bc6759 100644
--- a/sound/core/seq/seq_prioq.c
+++ b/sound/core/seq/seq_prioq.c
@@ -87,7 +87,7 @@
if (f->cells > 0) {
/* drain prioQ */
while (f->cells > 0)
- snd_seq_cell_free(snd_seq_prioq_cell_out(f));
+ snd_seq_cell_free(snd_seq_prioq_cell_out(f, NULL));
}
kfree(f);
@@ -214,8 +214,18 @@
return 0;
}
+/* return 1 if the current time >= event timestamp */
+static int event_is_ready(struct snd_seq_event *ev, void *current_time)
+{
+ if ((ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK)
+ return snd_seq_compare_tick_time(current_time, &ev->time.tick);
+ else
+ return snd_seq_compare_real_time(current_time, &ev->time.time);
+}
+
/* dequeue cell from prioq */
-struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f)
+struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f,
+ void *current_time)
{
struct snd_seq_event_cell *cell;
unsigned long flags;
@@ -227,6 +237,8 @@
spin_lock_irqsave(&f->lock, flags);
cell = f->head;
+ if (cell && current_time && !event_is_ready(&cell->event, current_time))
+ cell = NULL;
if (cell) {
f->head = cell->next;
@@ -252,18 +264,6 @@
return f->cells;
}
-
-/* peek at cell at the head of the prioq */
-struct snd_seq_event_cell *snd_seq_prioq_cell_peek(struct snd_seq_prioq * f)
-{
- if (f == NULL) {
- pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n");
- return NULL;
- }
- return f->head;
-}
-
-
static inline int prioq_match(struct snd_seq_event_cell *cell,
int client, int timestamp)
{
diff --git a/sound/core/seq/seq_prioq.h b/sound/core/seq/seq_prioq.h
index d38bb78..2c315ca 100644
--- a/sound/core/seq/seq_prioq.h
+++ b/sound/core/seq/seq_prioq.h
@@ -44,14 +44,12 @@
int snd_seq_prioq_cell_in(struct snd_seq_prioq *f, struct snd_seq_event_cell *cell);
/* dequeue cell from prioq */
-struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f);
+struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f,
+ void *current_time);
/* return number of events available in prioq */
int snd_seq_prioq_avail(struct snd_seq_prioq *f);
-/* peek at cell at the head of the prioq */
-struct snd_seq_event_cell *snd_seq_prioq_cell_peek(struct snd_seq_prioq *f);
-
/* client left queue */
void snd_seq_prioq_leave(struct snd_seq_prioq *f, int client, int timestamp);
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index 79e0c56..1a6dc4f 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -277,30 +277,20 @@
__again:
/* Process tick queue... */
- while ((cell = snd_seq_prioq_cell_peek(q->tickq)) != NULL) {
- if (snd_seq_compare_tick_time(&q->timer->tick.cur_tick,
- &cell->event.time.tick)) {
- cell = snd_seq_prioq_cell_out(q->tickq);
- if (cell)
- snd_seq_dispatch_event(cell, atomic, hop);
- } else {
- /* event remains in the queue */
+ for (;;) {
+ cell = snd_seq_prioq_cell_out(q->tickq,
+ &q->timer->tick.cur_tick);
+ if (!cell)
break;
- }
+ snd_seq_dispatch_event(cell, atomic, hop);
}
-
/* Process time queue... */
- while ((cell = snd_seq_prioq_cell_peek(q->timeq)) != NULL) {
- if (snd_seq_compare_real_time(&q->timer->cur_time,
- &cell->event.time.time)) {
- cell = snd_seq_prioq_cell_out(q->timeq);
- if (cell)
- snd_seq_dispatch_event(cell, atomic, hop);
- } else {
- /* event remains in the queue */
+ for (;;) {
+ cell = snd_seq_prioq_cell_out(q->timeq, &q->timer->cur_time);
+ if (!cell)
break;
- }
+ snd_seq_dispatch_event(cell, atomic, hop);
}
/* free lock */
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index cbd20cb..dc91002 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -192,6 +192,11 @@
dpcm->timer.expires = 0;
}
+static inline void loopback_timer_stop_sync(struct loopback_pcm *dpcm)
+{
+ del_timer_sync(&dpcm->timer);
+}
+
#define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK)
#define CABLE_VALID_CAPTURE (1 << SNDRV_PCM_STREAM_CAPTURE)
#define CABLE_VALID_BOTH (CABLE_VALID_PLAYBACK|CABLE_VALID_CAPTURE)
@@ -326,6 +331,8 @@
struct loopback_cable *cable = dpcm->cable;
int bps, salign;
+ loopback_timer_stop_sync(dpcm);
+
salign = (snd_pcm_format_width(runtime->format) *
runtime->channels) / 8;
bps = salign * runtime->rate;
@@ -659,7 +666,9 @@
return;
if (cable->streams[!substream->stream]) {
/* other stream is still alive */
+ spin_lock_irq(&cable->lock);
cable->streams[substream->stream] = NULL;
+ spin_unlock_irq(&cable->lock);
} else {
/* free the cable */
loopback->cables[substream->number][dev] = NULL;
@@ -699,7 +708,6 @@
loopback->cables[substream->number][dev] = cable;
}
dpcm->cable = cable;
- cable->streams[substream->stream] = dpcm;
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
@@ -731,6 +739,11 @@
runtime->hw = loopback_pcm_hardware;
else
runtime->hw = cable->hw;
+
+ spin_lock_irq(&cable->lock);
+ cable->streams[substream->stream] = dpcm;
+ spin_unlock_irq(&cable->lock);
+
unlock:
if (err < 0) {
free_cable(substream);
@@ -745,7 +758,7 @@
struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm = substream->runtime->private_data;
- loopback_timer_stop(dpcm);
+ loopback_timer_stop_sync(dpcm);
mutex_lock(&loopback->cable_lock);
free_cable(substream);
mutex_unlock(&loopback->cable_lock);
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index 9741757..d0ad61f 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -471,8 +471,9 @@
* This module supports 'Two-quadlet CIP header with SYT field'.
* For convenience, also check FMT field is AM824 or not.
*/
- if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
- ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) {
+ if ((((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
+ ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) &&
+ (!(s->flags & CIP_HEADER_WITHOUT_EOH))) {
dev_info_ratelimited(&s->unit->device,
"Invalid CIP header for AMDTP: %08X:%08X\n",
cip_header[0], cip_header[1]);
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h
index f7c054b..8136bd2 100644
--- a/sound/firewire/amdtp-stream.h
+++ b/sound/firewire/amdtp-stream.h
@@ -29,6 +29,8 @@
* @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
* packet is larger than IEC 61883-6 defines. Current implementation
* allows 5 times as large as IEC 61883-6 defines.
+ * @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include
+ * valid EOH.
*/
enum cip_flags {
CIP_NONBLOCKING = 0x00,
@@ -39,6 +41,7 @@
CIP_SKIP_DBC_ZERO_CHECK = 0x10,
CIP_EMPTY_HAS_WRONG_DBC = 0x20,
CIP_JUMBO_PAYLOAD = 0x40,
+ CIP_HEADER_WITHOUT_EOH = 0x80,
};
/**
diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c
index b3cffd0..a468854 100644
--- a/sound/firewire/digi00x/amdtp-dot.c
+++ b/sound/firewire/digi00x/amdtp-dot.c
@@ -28,6 +28,9 @@
*/
#define MAX_MIDI_RX_BLOCKS 8
+/* 3 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) + 1. */
+#define MAX_MIDI_PORTS 3
+
/*
* The double-oh-three algorithm was discovered by Robin Gareus and Damien
* Zammit in 2012, with reverse-engineering for Digi 003 Rack.
@@ -42,10 +45,8 @@
unsigned int pcm_channels;
struct dot_state state;
- unsigned int midi_ports;
- /* 2 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) */
- struct snd_rawmidi_substream *midi[2];
- int midi_fifo_used[2];
+ struct snd_rawmidi_substream *midi[MAX_MIDI_PORTS];
+ int midi_fifo_used[MAX_MIDI_PORTS];
int midi_fifo_limit;
void (*transfer_samples)(struct amdtp_stream *s,
@@ -124,8 +125,8 @@
return -EBUSY;
/*
- * A first data channel is for MIDI conformant data channel, the rest is
- * Multi Bit Linear Audio data channel.
+ * A first data channel is for MIDI messages, the rest is Multi Bit
+ * Linear Audio data channel.
*/
err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1);
if (err < 0)
@@ -135,11 +136,6 @@
p->pcm_channels = pcm_channels;
- if (s->direction == AMDTP_IN_STREAM)
- p->midi_ports = DOT_MIDI_IN_PORTS;
- else
- p->midi_ports = DOT_MIDI_OUT_PORTS;
-
/*
* We do not know the actual MIDI FIFO size of most devices. Just
* assume two bytes, i.e., one byte can be received over the bus while
@@ -281,13 +277,25 @@
b = (u8 *)&buffer[0];
len = 0;
- if (port < p->midi_ports &&
+ if (port < MAX_MIDI_PORTS &&
midi_ratelimit_per_packet(s, port) &&
p->midi[port] != NULL)
len = snd_rawmidi_transmit(p->midi[port], b + 1, 2);
if (len > 0) {
- b[3] = (0x10 << port) | len;
+ /*
+ * Upper 4 bits of LSB represent port number.
+ * - 0000b: physical MIDI port 1.
+ * - 0010b: physical MIDI port 2.
+ * - 1110b: console MIDI port.
+ */
+ if (port == 2)
+ b[3] = 0xe0;
+ else if (port == 1)
+ b[3] = 0x20;
+ else
+ b[3] = 0x00;
+ b[3] |= len;
midi_use_bytes(s, port, len);
} else {
b[1] = 0;
@@ -309,11 +317,22 @@
for (f = 0; f < data_blocks; f++) {
b = (u8 *)&buffer[0];
- port = b[3] >> 4;
- len = b[3] & 0x0f;
- if (port < p->midi_ports && p->midi[port] && len > 0)
- snd_rawmidi_receive(p->midi[port], b + 1, len);
+ len = b[3] & 0x0f;
+ if (len > 0) {
+ /*
+ * Upper 4 bits of LSB represent port number.
+ * - 0000b: physical MIDI port 1. Use port 0.
+ * - 1110b: console MIDI port. Use port 2.
+ */
+ if (b[3] >> 4 > 0)
+ port = 2;
+ else
+ port = 0;
+
+ if (port < MAX_MIDI_PORTS && p->midi[port])
+ snd_rawmidi_receive(p->midi[port], b + 1, len);
+ }
buffer += s->data_block_quadlets;
}
@@ -364,7 +383,7 @@
{
struct amdtp_dot *p = s->protocol;
- if (port < p->midi_ports)
+ if (port < MAX_MIDI_PORTS)
ACCESS_ONCE(p->midi[port]) = midi;
}
diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c
index cc4776c..1f5e1d2 100644
--- a/sound/firewire/digi00x/digi00x.c
+++ b/sound/firewire/digi00x/digi00x.c
@@ -13,7 +13,8 @@
MODULE_LICENSE("GPL v2");
#define VENDOR_DIGIDESIGN 0x00a07e
-#define MODEL_DIGI00X 0x000002
+#define MODEL_CONSOLE 0x000001
+#define MODEL_RACK 0x000002
static int name_card(struct snd_dg00x *dg00x)
{
@@ -129,6 +130,8 @@
spin_lock_init(&dg00x->lock);
init_waitqueue_head(&dg00x->hwdep_wait);
+ dg00x->is_console = entry->model_id == MODEL_CONSOLE;
+
/* Allocate and register this sound card later. */
INIT_DEFERRABLE_WORK(&dg00x->dwork, do_registration);
snd_fw_schedule_registration(unit, &dg00x->dwork);
@@ -183,7 +186,13 @@
.match_flags = IEEE1394_MATCH_VENDOR_ID |
IEEE1394_MATCH_MODEL_ID,
.vendor_id = VENDOR_DIGIDESIGN,
- .model_id = MODEL_DIGI00X,
+ .model_id = MODEL_CONSOLE,
+ },
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = VENDOR_DIGIDESIGN,
+ .model_id = MODEL_RACK,
},
{}
};
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h
index 2cd465c..43bcb0c 100644
--- a/sound/firewire/digi00x/digi00x.h
+++ b/sound/firewire/digi00x/digi00x.h
@@ -60,6 +60,7 @@
/* For asynchronous MIDI controls. */
struct snd_rawmidi_substream *in_control;
struct snd_fw_async_midi_port out_control;
+ bool is_console;
};
#define DG00X_ADDR_BASE 0xffffe0000000ull
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 293f3f2..733b342 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -185,6 +185,10 @@
MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
"(in second, 0 = disable).");
+static bool pm_blacklist = true;
+module_param(pm_blacklist, bool, 0644);
+MODULE_PARM_DESC(pm_blacklist, "Enable power-management blacklist");
+
/* reset the HD-audio controller in power save mode.
* this may give more power-saving, but will take longer time to
* wake up.
@@ -369,8 +373,10 @@
#define IS_KBL_LP(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9d71)
#define IS_KBL_H(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa2f0)
#define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98)
+#define IS_GLK(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x3198)
#define IS_SKL_PLUS(pci) (IS_SKL(pci) || IS_SKL_LP(pci) || IS_BXT(pci)) || \
- IS_KBL(pci) || IS_KBL_LP(pci) || IS_KBL_H(pci)
+ IS_KBL(pci) || IS_KBL_LP(pci) || IS_KBL_H(pci) || \
+ IS_GLK(pci)
static char *driver_short_names[] = {
[AZX_DRIVER_ICH] = "HDA Intel",
@@ -2042,6 +2048,24 @@
return err;
}
+#ifdef CONFIG_PM
+/* On some boards setting power_save to a non 0 value leads to clicking /
+ * popping sounds when ever we enter/leave powersaving mode. Ideally we would
+ * figure out how to avoid these sounds, but that is not always feasible.
+ * So we keep a list of devices where we disable powersaving as its known
+ * to causes problems on these devices.
+ */
+static struct snd_pci_quirk power_save_blacklist[] = {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x1849, 0x0c0c, "Asrock B85M-ITX", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x1043, 0x8733, "Asus Prime X370-Pro", 0),
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=198611 */
+ SND_PCI_QUIRK(0x17aa, 0x2227, "Lenovo X1 Carbon 3rd Gen", 0),
+ {}
+};
+#endif /* CONFIG_PM */
+
/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */
static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = {
[AZX_DRIVER_NVIDIA] = 8,
@@ -2054,6 +2078,7 @@
struct hdac_bus *bus = azx_bus(chip);
struct pci_dev *pci = chip->pci;
int dev = chip->dev_index;
+ int val;
int err;
hda->probe_continued = 1;
@@ -2129,7 +2154,21 @@
chip->running = 1;
azx_add_card_list(chip);
- snd_hda_set_power_save(&chip->bus, power_save * 1000);
+
+ val = power_save;
+#ifdef CONFIG_PM
+ if (pm_blacklist) {
+ const struct snd_pci_quirk *q;
+
+ q = snd_pci_quirk_lookup(chip->pci, power_save_blacklist);
+ if (q && val) {
+ dev_info(chip->card->dev, "device %04x:%04x is on the power_save blacklist, forcing power_save to 0\n",
+ q->subvendor, q->subdevice);
+ val = 0;
+ }
+ }
+#endif /* CONFIG_PM */
+ snd_hda_set_power_save(&chip->bus, val * 1000);
if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo)
pm_runtime_put_autosuspend(&pci->dev);
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 2c3065c..b3851b9 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -849,6 +849,8 @@
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, 0x8079, "HP EliteBook 840 G3", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x807C, "HP EliteBook 820 G3", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x80FD, "HP ProBook 640 G2", CXT_FIXUP_HP_DOCK),
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),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 89c166b..e2230be 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -3261,8 +3261,12 @@
pinval = snd_hda_codec_get_pin_target(codec, spec->mute_led_nid);
pinval &= ~AC_PINCTL_VREFEN;
pinval |= enabled ? AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_80;
- if (spec->mute_led_nid)
+ if (spec->mute_led_nid) {
+ /* temporarily power up/down for setting VREF */
+ snd_hda_power_up_pm(codec);
snd_hda_set_pin_ctl_cache(codec, spec->mute_led_nid, pinval);
+ snd_hda_power_down_pm(codec);
+ }
}
/* Make sure the led works even in runtime suspend */
@@ -4480,13 +4484,14 @@
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ } else if (action == HDA_FIXUP_ACT_INIT) {
/* Enable DOCK device */
snd_hda_codec_write(codec, 0x17, 0,
AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0);
/* Enable DOCK device */
snd_hda_codec_write(codec, 0x19, 0,
AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0);
- snd_hda_apply_pincfgs(codec, pincfgs);
}
}
@@ -4759,6 +4764,16 @@
}
}
+/* disable DAC3 (0x06) selection on NID 0x17 as it has no volume amp control */
+static void alc295_fixup_disable_dac3(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ hda_nid_t conn[2] = { 0x02, 0x03 };
+ snd_hda_override_conn_list(codec, 0x17, 2, conn);
+ }
+}
+
/* Hook to update amp GPIO4 for automute */
static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec,
struct hda_jack_callback *jack)
@@ -4908,6 +4923,7 @@
ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY,
ALC255_FIXUP_DELL_SPK_NOISE,
ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC295_FIXUP_DISABLE_DAC3,
ALC280_FIXUP_HP_HEADSET_MIC,
ALC221_FIXUP_HP_FRONT_MIC,
ALC292_FIXUP_TPT460,
@@ -5600,6 +5616,10 @@
.chained = true,
.chain_id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE,
},
+ [ALC295_FIXUP_DISABLE_DAC3] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc295_fixup_disable_dac3,
+ },
[ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
@@ -5663,6 +5683,7 @@
SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE),
SND_PCI_QUIRK(0x1028, 0x075b, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
SND_PCI_QUIRK(0x1028, 0x075d, "Dell AIO", ALC298_FIXUP_SPK_VOLUME),
+ SND_PCI_QUIRK(0x1028, 0x07b0, "Dell Precision 7520", ALC295_FIXUP_DISABLE_DAC3),
SND_PCI_QUIRK(0x1028, 0x0798, "Dell Inspiron 17 7000 Gaming", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER),
SND_PCI_QUIRK(0x1028, 0x082a, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
@@ -5784,9 +5805,11 @@
SND_PCI_QUIRK(0x17aa, 0x2245, "Thinkpad T470", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2246, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2247, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2249, "Thinkpad", ALC292_FIXUP_TPT460),
SND_PCI_QUIRK(0x17aa, 0x224b, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x3112, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
@@ -6761,6 +6784,7 @@
ALC668_FIXUP_DELL_DISABLE_AAMIX,
ALC668_FIXUP_DELL_XPS13,
ALC662_FIXUP_ASUS_Nx50,
+ ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE,
ALC668_FIXUP_ASUS_Nx51,
ALC891_FIXUP_HEADSET_MODE,
ALC891_FIXUP_DELL_MIC_NO_PRESENCE,
@@ -7012,14 +7036,21 @@
.chained = true,
.chain_id = ALC662_FIXUP_BASS_1A
},
+ [ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_alc668,
+ .chain_id = ALC662_FIXUP_BASS_CHMAP
+ },
[ALC668_FIXUP_ASUS_Nx51] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
- {0x1a, 0x90170151}, /* bass speaker */
+ { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+ { 0x1a, 0x90170151 }, /* bass speaker */
+ { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */
{}
},
.chained = true,
- .chain_id = ALC662_FIXUP_BASS_CHMAP,
+ .chain_id = ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE,
},
[ALC891_FIXUP_HEADSET_MODE] = {
.type = HDA_FIXUP_FUNC,
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index f9f2737..a4871c4 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -882,6 +882,7 @@
switch (event) {
case SND_SOC_DAPM_POST_PMU:
+ msleep(125);
regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC);
break;
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index f5d3415..f0c9e25 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -1736,6 +1736,7 @@
.num_reg_defaults = ARRAY_SIZE(rt5651_reg),
.ranges = rt5651_ranges,
.num_ranges = ARRAY_SIZE(rt5651_ranges),
+ .use_single_rw = true,
};
#if defined(CONFIG_OF)
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index abc802a..65ac4518 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -5035,6 +5035,12 @@
};
MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id);
+static const struct of_device_id rt5677_of_match[] = {
+ { .compatible = "realtek,rt5677", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5677_of_match);
+
static const struct acpi_gpio_params plug_det_gpio = { RT5677_GPIO_PLUG_DET, 0, false };
static const struct acpi_gpio_params mic_present_gpio = { RT5677_GPIO_MIC_PRESENT_L, 0, false };
static const struct acpi_gpio_params headphone_enable_gpio = { RT5677_GPIO_HP_AMP_SHDN_L, 0, false };
@@ -5294,6 +5300,7 @@
static struct i2c_driver rt5677_i2c_driver = {
.driver = {
.name = "rt5677",
+ .of_match_table = rt5677_of_match,
},
.probe = rt5677_i2c_probe,
.remove = rt5677_i2c_remove,
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 1589325..3dba555 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -774,15 +774,26 @@
static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct sgtl5000_priv *sgtl = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
case SND_SOC_BIAS_STANDBY:
+ regcache_cache_only(sgtl->regmap, false);
+ ret = regcache_sync(sgtl->regmap);
+ if (ret) {
+ regcache_cache_only(sgtl->regmap, true);
+ return ret;
+ }
+
snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_REFTOP_POWERUP,
SGTL5000_REFTOP_POWERUP);
break;
case SND_SOC_BIAS_OFF:
+ regcache_cache_only(sgtl->regmap, true);
snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_REFTOP_POWERUP, 0);
break;
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
index 0bfa688..cd16b43 100644
--- a/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -420,7 +420,21 @@
.callback = byt_thinkpad10_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_NAME, "20C3001VHH"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"),
+ },
+ },
+ {
+ .callback = byt_thinkpad10_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"),
+ },
+ },
+ {
+ .callback = byt_thinkpad10_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"),
},
},
{ }
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index 06fa5e8..3e526fb 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -457,7 +457,7 @@
struct hdac_bus *bus = ebus_to_hbus(ebus);
unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
(AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
- unsigned int res;
+ unsigned int res = -1;
mutex_lock(&bus->cmd_mutex);
snd_hdac_bus_send_cmd(bus, cmd);
diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c
index b6615af..fde974d 100644
--- a/sound/soc/nuc900/nuc900-ac97.c
+++ b/sound/soc/nuc900/nuc900-ac97.c
@@ -67,7 +67,7 @@
/* polling the AC_R_FINISH */
while (!(AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON) & AC_R_FINISH)
- && timeout--)
+ && --timeout)
mdelay(1);
if (!timeout) {
@@ -121,7 +121,7 @@
/* polling the AC_W_FINISH */
while ((AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON) & AC_W_FINISH)
- && timeout--)
+ && --timeout)
mdelay(1);
if (!timeout)
diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c
index 7d92a24..98835e9 100644
--- a/sound/soc/sh/rcar/cmd.c
+++ b/sound/soc/sh/rcar/cmd.c
@@ -82,6 +82,9 @@
[9] = 0x2,
};
+ if (unlikely(!src))
+ return -EIO;
+
data = path[rsnd_mod_id(src)] |
cmd_case[rsnd_mod_id(src)] << 16;
}
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index a9a43ac..fefa6ad 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -233,6 +233,15 @@
for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
/*
+ * It will set SSIWSR.CONT here, but SSICR.CKDV = 000
+ * with it is not allowed. (SSIWSR.WS_MODE with
+ * SSICR.CKDV = 000 is not allowed either).
+ * Skip it. See SSICR.CKDV
+ */
+ if (j == 0)
+ continue;
+
+ /*
* this driver is assuming that
* system word is 32bit x chan
* see rsnd_ssi_init()
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 4f6c777..b761761 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -34,6 +34,7 @@
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
@@ -2665,6 +2666,8 @@
if (ret != 0)
return ret;
+ arch_setup_dma_ops(card->dev, 0, 0, NULL, 0);
+
/* deactivate pins to sleep state */
list_for_each_entry(rtd, &card->rtd_list, list) {
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 8a59d47..69bf5cf 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -3277,4 +3277,51 @@
}
},
+{
+ /*
+ * Bower's & Wilkins PX headphones only support the 48 kHz sample rate
+ * even though it advertises more. The capture interface doesn't work
+ * even on windows.
+ */
+ USB_DEVICE(0x19b5, 0x0021),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_STANDARD_MIXER,
+ },
+ /* Capture */
+ {
+ .ifnum = 1,
+ .type = QUIRK_IGNORE_INTERFACE,
+ },
+ /* Playback */
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels = 2,
+ .iface = 2,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_FILL_MAX |
+ UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x03,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) {
+ 48000
+ }
+ }
+ },
+ }
+ }
+},
+
#undef USB_DEVICE_VENDOR_SPEC
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index f87996b..9a250c7 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -442,9 +442,9 @@
}
if (ret == -ENOENT && ret2 == -ENOENT)
- pr_debug("\"%s\" does not hit any event.\n", str);
- /* Note that this is silently ignored */
- ret = 0;
+ pr_warning("\"%s\" does not hit any event.\n", str);
+ else
+ ret = 0;
error:
if (kfd >= 0)
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 688dea7..68861e8 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -146,6 +146,7 @@
static bool append_file;
static const char *output_name;
static int output_fd;
+static int print_free_counters_hint;
struct perf_stat {
bool record;
@@ -310,8 +311,12 @@
struct perf_counts_values *count;
count = perf_counts(counter->counts, cpu, thread);
- if (perf_evsel__read(counter, cpu, thread, count))
+ if (perf_evsel__read(counter, cpu, thread, count)) {
+ counter->counts->scaled = -1;
+ perf_counts(counter->counts, cpu, thread)->ena = 0;
+ perf_counts(counter->counts, cpu, thread)->run = 0;
return -1;
+ }
if (STAT_RECORD) {
if (perf_evsel__write_stat_event(counter, cpu, thread, count)) {
@@ -336,12 +341,14 @@
static void read_counters(void)
{
struct perf_evsel *counter;
+ int ret;
evlist__for_each_entry(evsel_list, counter) {
- if (read_counter(counter))
+ ret = read_counter(counter);
+ if (ret)
pr_debug("failed to read counter %s\n", counter->name);
- if (perf_stat_process_counter(&stat_config, counter))
+ if (ret == 0 && perf_stat_process_counter(&stat_config, counter))
pr_warning("failed to process counter %s\n", counter->name);
}
}
@@ -869,7 +876,7 @@
char buf[64], *vals, *ends;
if (unit == NULL || fmt == NULL) {
- fprintf(out, "%s%s%s%s", csv_sep, csv_sep, csv_sep, csv_sep);
+ fprintf(out, "%s%s", csv_sep, csv_sep);
return;
}
snprintf(buf, sizeof(buf), fmt, val);
@@ -1109,6 +1116,9 @@
counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
csv_sep);
+ if (counter->supported)
+ print_free_counters_hint = 1;
+
fprintf(stat_config.output, "%-*s%s",
csv_output ? 0 : unit_width,
counter->unit, csv_sep);
@@ -1477,6 +1487,13 @@
avg_stats(&walltime_nsecs_stats));
}
fprintf(output, "\n\n");
+
+ if (print_free_counters_hint)
+ fprintf(output,
+"Some events weren't counted. Try disabling the NMI watchdog:\n"
+" echo 0 > /proc/sys/kernel/nmi_watchdog\n"
+" perf stat ...\n"
+" echo 1 > /proc/sys/kernel/nmi_watchdog\n");
}
static void print_counters(struct timespec *ts, int argc, const char **argv)
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 21f8a81..4c596ba 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -822,12 +822,21 @@
void **arg_parm;
};
-static size_t fprintf_duration(unsigned long t, FILE *fp)
+/*
+ * We need to have this 'calculated' boolean because in some cases we really
+ * don't know what is the duration of a syscall, for instance, when we start
+ * a session and some threads are waiting for a syscall to finish, say 'poll',
+ * in which case all we can do is to print "( ? ) for duration and for the
+ * start timestamp.
+ */
+static size_t fprintf_duration(unsigned long t, bool calculated, FILE *fp)
{
double duration = (double)t / NSEC_PER_MSEC;
size_t printed = fprintf(fp, "(");
- if (duration >= 1.0)
+ if (!calculated)
+ printed += fprintf(fp, " ? ");
+ else if (duration >= 1.0)
printed += color_fprintf(fp, PERF_COLOR_RED, "%6.3f ms", duration);
else if (duration >= 0.01)
printed += color_fprintf(fp, PERF_COLOR_YELLOW, "%6.3f ms", duration);
@@ -1030,13 +1039,27 @@
return t < (trace->duration_filter * NSEC_PER_MSEC);
}
-static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp)
+static size_t __trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp)
{
double ts = (double)(tstamp - trace->base_time) / NSEC_PER_MSEC;
return fprintf(fp, "%10.3f ", ts);
}
+/*
+ * We're handling tstamp=0 as an undefined tstamp, i.e. like when we are
+ * using ttrace->entry_time for a thread that receives a sys_exit without
+ * first having received a sys_enter ("poll" issued before tracing session
+ * starts, lost sys_enter exit due to ring buffer overflow).
+ */
+static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp)
+{
+ if (tstamp > 0)
+ return __trace__fprintf_tstamp(trace, tstamp, fp);
+
+ return fprintf(fp, " ? ");
+}
+
static bool done = false;
static bool interrupted = false;
@@ -1047,10 +1070,10 @@
}
static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread,
- u64 duration, u64 tstamp, FILE *fp)
+ u64 duration, bool duration_calculated, u64 tstamp, FILE *fp)
{
size_t printed = trace__fprintf_tstamp(trace, tstamp, fp);
- printed += fprintf_duration(duration, fp);
+ printed += fprintf_duration(duration, duration_calculated, fp);
if (trace->multiple_threads) {
if (trace->show_comm)
@@ -1452,7 +1475,7 @@
duration = sample->time - ttrace->entry_time;
- printed = trace__fprintf_entry_head(trace, trace->current, duration, ttrace->entry_time, trace->output);
+ printed = trace__fprintf_entry_head(trace, trace->current, duration, true, ttrace->entry_time, trace->output);
printed += fprintf(trace->output, "%-70s) ...\n", ttrace->entry_str);
ttrace->entry_pending = false;
@@ -1499,7 +1522,7 @@
if (sc->is_exit) {
if (!(trace->duration_filter || trace->summary_only || trace->min_stack)) {
- trace__fprintf_entry_head(trace, thread, 1, ttrace->entry_time, trace->output);
+ trace__fprintf_entry_head(trace, thread, 0, false, ttrace->entry_time, trace->output);
fprintf(trace->output, "%-70s)\n", ttrace->entry_str);
}
} else {
@@ -1547,6 +1570,7 @@
{
long ret;
u64 duration = 0;
+ bool duration_calculated = false;
struct thread *thread;
int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1, callchain_ret = 0;
struct syscall *sc = trace__syscall_info(trace, evsel, id);
@@ -1577,6 +1601,7 @@
duration = sample->time - ttrace->entry_time;
if (trace__filter_duration(trace, duration))
goto out;
+ duration_calculated = true;
} else if (trace->duration_filter)
goto out;
@@ -1592,7 +1617,7 @@
if (trace->summary_only)
goto out;
- trace__fprintf_entry_head(trace, thread, duration, ttrace->entry_time, trace->output);
+ trace__fprintf_entry_head(trace, thread, duration, duration_calculated, ttrace->entry_time, trace->output);
if (ttrace->entry_pending) {
fprintf(trace->output, "%-70s", ttrace->entry_str);
@@ -1855,7 +1880,7 @@
thread__find_addr_location(thread, sample->cpumode, MAP__FUNCTION,
sample->ip, &al);
- trace__fprintf_entry_head(trace, thread, 0, sample->time, trace->output);
+ trace__fprintf_entry_head(trace, thread, 0, true, sample->time, trace->output);
fprintf(trace->output, "%sfault [",
evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ ?
diff --git a/tools/perf/tests/kmod-path.c b/tools/perf/tests/kmod-path.c
index 76f41f2..6cd9e51 100644
--- a/tools/perf/tests/kmod-path.c
+++ b/tools/perf/tests/kmod-path.c
@@ -61,6 +61,7 @@
M("/xxxx/xxxx/x-x.ko", PERF_RECORD_MISC_KERNEL, true);
M("/xxxx/xxxx/x-x.ko", PERF_RECORD_MISC_USER, false);
+#ifdef HAVE_ZLIB_SUPPORT
/* path alloc_name alloc_ext kmod comp name ext */
T("/xxxx/xxxx/x.ko.gz", true , true , true, true, "[x]", "gz");
T("/xxxx/xxxx/x.ko.gz", false , true , true, true, NULL , "gz");
@@ -96,6 +97,7 @@
M("x.ko.gz", PERF_RECORD_MISC_CPUMODE_UNKNOWN, true);
M("x.ko.gz", PERF_RECORD_MISC_KERNEL, true);
M("x.ko.gz", PERF_RECORD_MISC_USER, false);
+#endif
/* path alloc_name alloc_ext kmod comp name ext */
T("[test_module]", true , true , true, false, "[test_module]", NULL);
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 430d039..a38227e 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -1250,6 +1250,7 @@
{
char linkname[PATH_MAX];
char *build_id_filename;
+ char *build_id_path = NULL;
if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS &&
!dso__is_kcore(dso))
@@ -1265,8 +1266,14 @@
goto fallback;
}
+ build_id_path = strdup(filename);
+ if (!build_id_path)
+ return -1;
+
+ dirname(build_id_path);
+
if (dso__is_kcore(dso) ||
- readlink(filename, linkname, sizeof(linkname)) < 0 ||
+ readlink(build_id_path, linkname, sizeof(linkname)) < 0 ||
strstr(linkname, DSO__NAME_KALLSYMS) ||
access(filename, R_OK)) {
fallback:
@@ -1278,6 +1285,7 @@
__symbol__join_symfs(filename, filename_size, dso->long_name);
}
+ free(build_id_path);
return 0;
}
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index e528c40..993ef27 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -182,13 +182,17 @@
char buf[PATH_MAX];
char *ret = NULL, *p;
size_t offs = 5; /* == strlen("../..") */
+ ssize_t len;
linkname = build_id_cache__linkname(sbuild_id, NULL, 0);
if (!linkname)
return NULL;
- if (readlink(linkname, buf, PATH_MAX) < 0)
+ len = readlink(linkname, buf, sizeof(buf) - 1);
+ if (len <= 0)
goto out;
+ buf[len] = '\0';
+
/* The link should be "../..<origpath>/<sbuild_id>" */
p = strrchr(buf, '/'); /* Cut off the "/<sbuild_id>" */
if (p && (p > buf + offs)) {
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 8ab0d7d..6631923 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -255,8 +255,8 @@
if (machine__is_default_guest(machine))
return 0;
- snprintf(filename, sizeof(filename), "%s/proc/%d/maps",
- machine->root_dir, pid);
+ snprintf(filename, sizeof(filename), "%s/proc/%d/task/%d/maps",
+ machine->root_dir, pid, pid);
fp = fopen(filename, "r");
if (fp == NULL) {
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 8bc2711..bce80f8 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -1221,7 +1221,7 @@
if (FD(evsel, cpu, thread) < 0)
return -EINVAL;
- if (readn(FD(evsel, cpu, thread), count, sizeof(*count)) < 0)
+ if (readn(FD(evsel, cpu, thread), count, sizeof(*count)) <= 0)
return -errno;
return 0;
@@ -1239,7 +1239,7 @@
if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1, thread + 1) < 0)
return -ENOMEM;
- if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0)
+ if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) <= 0)
return -errno;
perf_evsel__compute_deltas(evsel, cpu, thread, &count);
@@ -2400,11 +2400,17 @@
int err, char *msg, size_t size)
{
char sbuf[STRERR_BUFSIZE];
+ int printed = 0;
switch (err) {
case EPERM:
case EACCES:
- return scnprintf(msg, size,
+ if (err == EPERM)
+ printed = scnprintf(msg, size,
+ "No permission to enable %s event.\n\n",
+ perf_evsel__name(evsel));
+
+ return scnprintf(msg + printed, size - printed,
"You may not have permission to collect %sstats.\n\n"
"Consider tweaking /proc/sys/kernel/perf_event_paranoid,\n"
"which controls use of the performance events system by\n"
diff --git a/tools/perf/util/ordered-events.c b/tools/perf/util/ordered-events.c
index fe84df1..e70e935 100644
--- a/tools/perf/util/ordered-events.c
+++ b/tools/perf/util/ordered-events.c
@@ -79,7 +79,7 @@
static void free_dup_event(struct ordered_events *oe, union perf_event *event)
{
- if (oe->copy_on_queue) {
+ if (event && oe->copy_on_queue) {
oe->cur_alloc_size -= event->header.size;
free(event);
}
@@ -150,6 +150,7 @@
list_move(&event->list, &oe->cache);
oe->nr_events--;
free_dup_event(oe, event->event);
+ event->event = NULL;
}
int ordered_events__queue(struct ordered_events *oe, union perf_event *event,
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 6a6f44d..4d2e22f 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -3060,7 +3060,7 @@
struct probe_trace_event *new_tevs;
int ret = 0;
- if (ntevs == 0) {
+ if (*ntevs == 0) {
*tevs = *tevs2;
*ntevs = ntevs2;
*tevs2 = NULL;
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 5d61242..7e0573e 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -139,8 +139,14 @@
if (perf_session__open(session) < 0)
goto out_close;
- perf_session__set_id_hdr_size(session);
- perf_session__set_comm_exec(session);
+ /*
+ * set session attributes that are present in perf.data
+ * but not in pipe-mode.
+ */
+ if (!file->is_pipe) {
+ perf_session__set_id_hdr_size(session);
+ perf_session__set_comm_exec(session);
+ }
}
} else {
session->machines.host.env = &perf_env;
@@ -155,7 +161,11 @@
pr_warning("Cannot read kernel map\n");
}
- if (tool && tool->ordering_requires_timestamps &&
+ /*
+ * In pipe-mode, evlist is empty until PERF_RECORD_HEADER_ATTR is
+ * processed, so perf_evlist__sample_id_all is not meaningful here.
+ */
+ if ((!file || !file->is_pipe) && tool && tool->ordering_requires_timestamps &&
tool->ordered_events && !perf_evlist__sample_id_all(session->evlist)) {
dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n");
tool->ordered_events = false;
@@ -1628,6 +1638,7 @@
buf = malloc(cur_size);
if (!buf)
return -errno;
+ ordered_events__set_copy_on_queue(oe, true);
more:
event = buf;
err = readn(fd, event, sizeof(struct perf_event_header));
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 452e15a..031e64c 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -846,6 +846,9 @@
static int64_t
sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right)
{
+ if (!left->branch_info || !right->branch_info)
+ return cmp_null(left->branch_info, right->branch_info);
+
return left->branch_info->flags.cycles -
right->branch_info->flags.cycles;
}
@@ -853,6 +856,8 @@
static int hist_entry__cycles_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
+ if (!he->branch_info)
+ return scnprintf(bf, size, "%-.*s", width, "N/A");
if (he->branch_info->flags.cycles == 0)
return repsep_snprintf(bf, size, "%-*s", width, "-");
return repsep_snprintf(bf, size, "%-*hd", width,
diff --git a/tools/perf/util/trigger.h b/tools/perf/util/trigger.h
index e97d701..ce7e6e5 100644
--- a/tools/perf/util/trigger.h
+++ b/tools/perf/util/trigger.h
@@ -11,7 +11,7 @@
* States and transits:
*
*
- * OFF--(on)--> READY --(hit)--> HIT
+ * OFF--> ON --> READY --(hit)--> HIT
* ^ |
* | (ready)
* | |
@@ -26,8 +26,9 @@
volatile enum {
TRIGGER_ERROR = -2,
TRIGGER_OFF = -1,
- TRIGGER_READY = 0,
- TRIGGER_HIT = 1,
+ TRIGGER_ON = 0,
+ TRIGGER_READY = 1,
+ TRIGGER_HIT = 2,
} state;
const char *name;
};
@@ -49,7 +50,7 @@
static inline void trigger_on(struct trigger *t)
{
TRIGGER_WARN_ONCE(t, TRIGGER_OFF);
- t->state = TRIGGER_READY;
+ t->state = TRIGGER_ON;
}
static inline void trigger_ready(struct trigger *t)
diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c
index 71620fa..d025eaf 100644
--- a/tools/testing/nvdimm/test/nfit.c
+++ b/tools/testing/nvdimm/test/nfit.c
@@ -1908,6 +1908,7 @@
put_device(&pdev->dev);
goto err_register;
}
+ get_device(&pdev->dev);
rc = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (rc)
@@ -1926,6 +1927,10 @@
if (instances[i])
platform_device_unregister(&instances[i]->pdev);
nfit_test_teardown();
+ for (i = 0; i < NUM_NFITS; i++)
+ if (instances[i])
+ put_device(&instances[i]->pdev.dev);
+
return rc;
}
@@ -1933,10 +1938,13 @@
{
int i;
- platform_driver_unregister(&nfit_test_driver);
for (i = 0; i < NUM_NFITS; i++)
platform_device_unregister(&instances[i]->pdev);
+ platform_driver_unregister(&nfit_test_driver);
nfit_test_teardown();
+
+ for (i = 0; i < NUM_NFITS; i++)
+ put_device(&instances[i]->pdev.dev);
class_destroy(nfit_test_dimm);
}
diff --git a/tools/testing/selftests/firmware/fw_filesystem.sh b/tools/testing/selftests/firmware/fw_filesystem.sh
index d8ac9ba..17e16fc 100755
--- a/tools/testing/selftests/firmware/fw_filesystem.sh
+++ b/tools/testing/selftests/firmware/fw_filesystem.sh
@@ -28,7 +28,10 @@
if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then
echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout
fi
- echo -n "$OLD_PATH" >/sys/module/firmware_class/parameters/path
+ if [ "$OLD_FWPATH" = "" ]; then
+ OLD_FWPATH=" "
+ fi
+ echo -n "$OLD_FWPATH" >/sys/module/firmware_class/parameters/path
rm -f "$FW"
rmdir "$FWPATH"
}
diff --git a/tools/testing/selftests/rcutorture/bin/configinit.sh b/tools/testing/selftests/rcutorture/bin/configinit.sh
index 3f81a10..50a6371 100755
--- a/tools/testing/selftests/rcutorture/bin/configinit.sh
+++ b/tools/testing/selftests/rcutorture/bin/configinit.sh
@@ -51,7 +51,7 @@
mkdir $builddir
fi
else
- echo Bad build directory: \"$builddir\"
+ echo Bad build directory: \"$buildloc\"
exit 2
fi
fi
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 6eb5015..e5b459a 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -17,7 +17,7 @@
BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32)
BINARIES_64 := $(TARGETS_C_64BIT_ALL:%=%_64)
-CFLAGS := -O2 -g -std=gnu99 -pthread -Wall
+CFLAGS := -O2 -g -std=gnu99 -pthread -Wall -no-pie
UNAME_M := $(shell uname -m)
CAN_BUILD_I386 := $(shell ./check_cc.sh $(CC) trivial_32bit_program.c -m32)
diff --git a/tools/testing/selftests/x86/entry_from_vm86.c b/tools/testing/selftests/x86/entry_from_vm86.c
index d075ea0..ade443a 100644
--- a/tools/testing/selftests/x86/entry_from_vm86.c
+++ b/tools/testing/selftests/x86/entry_from_vm86.c
@@ -95,6 +95,31 @@
"int3\n\t"
"vmcode_int80:\n\t"
"int $0x80\n\t"
+ "vmcode_popf_hlt:\n\t"
+ "push %ax\n\t"
+ "popf\n\t"
+ "hlt\n\t"
+ "vmcode_umip:\n\t"
+ /* addressing via displacements */
+ "smsw (2052)\n\t"
+ "sidt (2054)\n\t"
+ "sgdt (2060)\n\t"
+ /* addressing via registers */
+ "mov $2066, %bx\n\t"
+ "smsw (%bx)\n\t"
+ "mov $2068, %bx\n\t"
+ "sidt (%bx)\n\t"
+ "mov $2074, %bx\n\t"
+ "sgdt (%bx)\n\t"
+ /* register operands, only for smsw */
+ "smsw %ax\n\t"
+ "mov %ax, (2080)\n\t"
+ "int3\n\t"
+ "vmcode_umip_str:\n\t"
+ "str %eax\n\t"
+ "vmcode_umip_sldt:\n\t"
+ "sldt %eax\n\t"
+ "int3\n\t"
".size vmcode, . - vmcode\n\t"
"end_vmcode:\n\t"
".code32\n\t"
@@ -103,7 +128,8 @@
extern unsigned char vmcode[], end_vmcode[];
extern unsigned char vmcode_bound[], vmcode_sysenter[], vmcode_syscall[],
- vmcode_sti[], vmcode_int3[], vmcode_int80[];
+ vmcode_sti[], vmcode_int3[], vmcode_int80[], vmcode_popf_hlt[],
+ vmcode_umip[], vmcode_umip_str[], vmcode_umip_sldt[];
/* Returns false if the test was skipped. */
static bool do_test(struct vm86plus_struct *v86, unsigned long eip,
@@ -153,13 +179,75 @@
(VM86_TYPE(ret) == rettype && VM86_ARG(ret) == retarg)) {
printf("[OK]\tReturned correctly\n");
} else {
- printf("[FAIL]\tIncorrect return reason\n");
+ printf("[FAIL]\tIncorrect return reason (started at eip = 0x%lx, ended at eip = 0x%lx)\n", eip, v86->regs.eip);
nerrs++;
}
return true;
}
+void do_umip_tests(struct vm86plus_struct *vm86, unsigned char *test_mem)
+{
+ struct table_desc {
+ unsigned short limit;
+ unsigned long base;
+ } __attribute__((packed));
+
+ /* Initialize variables with arbitrary values */
+ struct table_desc gdt1 = { .base = 0x3c3c3c3c, .limit = 0x9999 };
+ struct table_desc gdt2 = { .base = 0x1a1a1a1a, .limit = 0xaeae };
+ struct table_desc idt1 = { .base = 0x7b7b7b7b, .limit = 0xf1f1 };
+ struct table_desc idt2 = { .base = 0x89898989, .limit = 0x1313 };
+ unsigned short msw1 = 0x1414, msw2 = 0x2525, msw3 = 3737;
+
+ /* UMIP -- exit with INT3 unless kernel emulation did not trap #GP */
+ do_test(vm86, vmcode_umip - vmcode, VM86_TRAP, 3, "UMIP tests");
+
+ /* Results from displacement-only addressing */
+ msw1 = *(unsigned short *)(test_mem + 2052);
+ memcpy(&idt1, test_mem + 2054, sizeof(idt1));
+ memcpy(&gdt1, test_mem + 2060, sizeof(gdt1));
+
+ /* Results from register-indirect addressing */
+ msw2 = *(unsigned short *)(test_mem + 2066);
+ memcpy(&idt2, test_mem + 2068, sizeof(idt2));
+ memcpy(&gdt2, test_mem + 2074, sizeof(gdt2));
+
+ /* Results when using register operands */
+ msw3 = *(unsigned short *)(test_mem + 2080);
+
+ printf("[INFO]\tResult from SMSW:[0x%04x]\n", msw1);
+ printf("[INFO]\tResult from SIDT: limit[0x%04x]base[0x%08lx]\n",
+ idt1.limit, idt1.base);
+ printf("[INFO]\tResult from SGDT: limit[0x%04x]base[0x%08lx]\n",
+ gdt1.limit, gdt1.base);
+
+ if (msw1 != msw2 || msw1 != msw3)
+ printf("[FAIL]\tAll the results of SMSW should be the same.\n");
+ else
+ printf("[PASS]\tAll the results from SMSW are identical.\n");
+
+ if (memcmp(&gdt1, &gdt2, sizeof(gdt1)))
+ printf("[FAIL]\tAll the results of SGDT should be the same.\n");
+ else
+ printf("[PASS]\tAll the results from SGDT are identical.\n");
+
+ if (memcmp(&idt1, &idt2, sizeof(idt1)))
+ printf("[FAIL]\tAll the results of SIDT should be the same.\n");
+ else
+ printf("[PASS]\tAll the results from SIDT are identical.\n");
+
+ sethandler(SIGILL, sighandler, 0);
+ do_test(vm86, vmcode_umip_str - vmcode, VM86_SIGNAL, 0,
+ "STR instruction");
+ clearhandler(SIGILL);
+
+ sethandler(SIGILL, sighandler, 0);
+ do_test(vm86, vmcode_umip_sldt - vmcode, VM86_SIGNAL, 0,
+ "SLDT instruction");
+ clearhandler(SIGILL);
+}
+
int main(void)
{
struct vm86plus_struct v86;
@@ -180,6 +268,9 @@
v86.regs.ds = load_addr / 16;
v86.regs.es = load_addr / 16;
+ /* Use the end of the page as our stack. */
+ v86.regs.esp = 4096;
+
assert((v86.regs.cs & 3) == 0); /* Looks like RPL = 0 */
/* #BR -- should deliver SIG??? */
@@ -211,6 +302,23 @@
v86.regs.eflags &= ~X86_EFLAGS_IF;
do_test(&v86, vmcode_sti - vmcode, VM86_STI, 0, "STI with VIP set");
+ /* POPF with VIP set but IF clear: should not trap */
+ v86.regs.eflags = X86_EFLAGS_VIP;
+ v86.regs.eax = 0;
+ do_test(&v86, vmcode_popf_hlt - vmcode, VM86_UNKNOWN, 0, "POPF with VIP set and IF clear");
+
+ /* POPF with VIP set and IF set: should trap */
+ v86.regs.eflags = X86_EFLAGS_VIP;
+ v86.regs.eax = X86_EFLAGS_IF;
+ do_test(&v86, vmcode_popf_hlt - vmcode, VM86_STI, 0, "POPF with VIP and IF set");
+
+ /* POPF with VIP clear and IF set: should not trap */
+ v86.regs.eflags = 0;
+ v86.regs.eax = X86_EFLAGS_IF;
+ do_test(&v86, vmcode_popf_hlt - vmcode, VM86_UNKNOWN, 0, "POPF with VIP clear and IF set");
+
+ v86.regs.eflags = 0;
+
/* INT3 -- should cause #BP */
do_test(&v86, vmcode_int3 - vmcode, VM86_TRAP, 3, "INT3");
@@ -218,6 +326,9 @@
v86.regs.eax = (unsigned int)-1;
do_test(&v86, vmcode_int80 - vmcode, VM86_INTx, 0x80, "int80");
+ /* UMIP -- should exit with INTx 0x80 unless UMIP was not disabled */
+ do_umip_tests(&v86, addr);
+
/* Execute a null pointer */
v86.regs.cs = 0;
v86.regs.ss = 0;
@@ -231,7 +342,7 @@
clearhandler(SIGSEGV);
/* Make sure nothing explodes if we fork. */
- if (fork() > 0)
+ if (fork() == 0)
return 0;
return (nerrs == 0 ? 0 : 1);
diff --git a/tools/testing/selftests/x86/mpx-mini-test.c b/tools/testing/selftests/x86/mpx-mini-test.c
index 79e1d13..5838418 100644
--- a/tools/testing/selftests/x86/mpx-mini-test.c
+++ b/tools/testing/selftests/x86/mpx-mini-test.c
@@ -419,8 +419,7 @@
br_count++;
dprintf1("#BR 0x%jx (total seen: %d)\n", status, br_count);
-#define __SI_FAULT (3 << 16)
-#define SEGV_BNDERR (__SI_FAULT|3) /* failed address bound checks */
+#define SEGV_BNDERR 3 /* failed address bound checks */
dprintf2("Saw a #BR! status 0x%jx at %016lx br_reason: %jx\n",
status, ip, br_reason);
diff --git a/tools/testing/selftests/x86/protection_keys.c b/tools/testing/selftests/x86/protection_keys.c
index 2842a5f..85a78eb 100644
--- a/tools/testing/selftests/x86/protection_keys.c
+++ b/tools/testing/selftests/x86/protection_keys.c
@@ -188,17 +188,29 @@
#define u64 uint64_t
#ifdef __i386__
-#define SYS_mprotect_key 380
-#define SYS_pkey_alloc 381
-#define SYS_pkey_free 382
+
+#ifndef SYS_mprotect_key
+# define SYS_mprotect_key 380
+#endif
+#ifndef SYS_pkey_alloc
+# define SYS_pkey_alloc 381
+# define SYS_pkey_free 382
+#endif
#define REG_IP_IDX REG_EIP
-#define si_pkey_offset 0x18
+#define si_pkey_offset 0x14
+
#else
-#define SYS_mprotect_key 329
-#define SYS_pkey_alloc 330
-#define SYS_pkey_free 331
+
+#ifndef SYS_mprotect_key
+# define SYS_mprotect_key 329
+#endif
+#ifndef SYS_pkey_alloc
+# define SYS_pkey_alloc 330
+# define SYS_pkey_free 331
+#endif
#define REG_IP_IDX REG_RIP
#define si_pkey_offset 0x20
+
#endif
void dump_mem(void *dumpme, int len_bytes)
@@ -212,19 +224,18 @@
}
}
-#define __SI_FAULT (3 << 16)
-#define SEGV_BNDERR (__SI_FAULT|3) /* failed address bound checks */
-#define SEGV_PKUERR (__SI_FAULT|4)
+#define SEGV_BNDERR 3 /* failed address bound checks */
+#define SEGV_PKUERR 4
static char *si_code_str(int si_code)
{
- if (si_code & SEGV_MAPERR)
+ if (si_code == SEGV_MAPERR)
return "SEGV_MAPERR";
- if (si_code & SEGV_ACCERR)
+ if (si_code == SEGV_ACCERR)
return "SEGV_ACCERR";
- if (si_code & SEGV_BNDERR)
+ if (si_code == SEGV_BNDERR)
return "SEGV_BNDERR";
- if (si_code & SEGV_PKUERR)
+ if (si_code == SEGV_PKUERR)
return "SEGV_PKUERR";
return "UNKNOWN";
}
@@ -238,7 +249,7 @@
unsigned long ip;
char *fpregs;
u32 *pkru_ptr;
- u64 si_pkey;
+ u64 siginfo_pkey;
u32 *si_pkey_ptr;
int pkru_offset;
fpregset_t fpregset;
@@ -280,9 +291,9 @@
si_pkey_ptr = (u32 *)(((u8 *)si) + si_pkey_offset);
dprintf1("si_pkey_ptr: %p\n", si_pkey_ptr);
dump_mem(si_pkey_ptr - 8, 24);
- si_pkey = *si_pkey_ptr;
- pkey_assert(si_pkey < NR_PKEYS);
- last_si_pkey = si_pkey;
+ siginfo_pkey = *si_pkey_ptr;
+ pkey_assert(siginfo_pkey < NR_PKEYS);
+ last_si_pkey = siginfo_pkey;
if ((si->si_code == SEGV_MAPERR) ||
(si->si_code == SEGV_ACCERR) ||
@@ -294,7 +305,7 @@
dprintf1("signal pkru from xsave: %08x\n", *pkru_ptr);
/* need __rdpkru() version so we do not do shadow_pkru checking */
dprintf1("signal pkru from pkru: %08x\n", __rdpkru());
- dprintf1("si_pkey from siginfo: %jx\n", si_pkey);
+ dprintf1("pkey from siginfo: %jx\n", siginfo_pkey);
*(u64 *)pkru_ptr = 0x00000000;
dprintf1("WARNING: set PRKU=0 to allow faulting instruction to continue\n");
pkru_faults++;
diff --git a/tools/testing/selftests/x86/ptrace_syscall.c b/tools/testing/selftests/x86/ptrace_syscall.c
index eaea924..1e3da13 100644
--- a/tools/testing/selftests/x86/ptrace_syscall.c
+++ b/tools/testing/selftests/x86/ptrace_syscall.c
@@ -182,8 +182,10 @@
if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0)
err(1, "PTRACE_TRACEME");
+ pid_t pid = getpid(), tid = syscall(SYS_gettid);
+
printf("\tChild will make one syscall\n");
- raise(SIGSTOP);
+ syscall(SYS_tgkill, pid, tid, SIGSTOP);
syscall(SYS_gettid, 10, 11, 12, 13, 14, 15);
_exit(0);
@@ -300,9 +302,11 @@
if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0)
err(1, "PTRACE_TRACEME");
+ pid_t pid = getpid(), tid = syscall(SYS_gettid);
+
printf("\tChild will take a nap until signaled\n");
setsigign(SIGUSR1, SA_RESTART);
- raise(SIGSTOP);
+ syscall(SYS_tgkill, pid, tid, SIGSTOP);
syscall(SYS_pause, 0, 0, 0, 0, 0, 0);
_exit(0);
diff --git a/tools/usb/usbip/src/usbipd.c b/tools/usb/usbip/src/usbipd.c
index a0972de..1935565 100644
--- a/tools/usb/usbip/src/usbipd.c
+++ b/tools/usb/usbip/src/usbipd.c
@@ -463,7 +463,7 @@
sigaction(SIGTERM, &act, NULL);
sigaction(SIGINT, &act, NULL);
act.sa_handler = SIG_IGN;
- sigaction(SIGCLD, &act, NULL);
+ sigaction(SIGCHLD, &act, NULL);
}
static const char *pid_file;
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 1b20768..eaae725 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -976,8 +976,7 @@
/* Check for overlaps */
r = -EEXIST;
kvm_for_each_memslot(slot, __kvm_memslots(kvm, as_id)) {
- if ((slot->id >= KVM_USER_MEM_SLOTS) ||
- (slot->id == id))
+ if (slot->id == id)
continue;
if (!((base_gfn + npages <= slot->base_gfn) ||
(base_gfn >= slot->base_gfn + slot->npages)))