Merge "msm: camera: isp: Enable request notification for IFE before streamon" into dev/msm-4.9-camx
diff --git a/.gitignore b/.gitignore
index c2ed4ec..d47ecbb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -114,3 +114,6 @@
# Kdevelop4
*.kdev4
+
+# fetched Android config fragments
+kernel/configs/android-*.cfg
diff --git a/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt b/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt
index a6537eb..105dcac 100644
--- a/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt
+++ b/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt
@@ -110,6 +110,10 @@
on behalf of the subsystem driver.
- qcom,mdm-link-info: a string indicating additional info about the physical link.
For example: "devID_domain.bus.slot" in case of PCIe.
+- qcom,mdm-auto-boot: Boolean. To indicate this instance of esoc boots independently.
+- qcom,mdm-statusline-not-a-powersource: Boolean. If set, status line to esoc device is not a
+ power source.
+- qcom,mdm-userspace-handle-shutdown: Boolean. If set, userspace handles shutdown requests.
Example:
mdm0: qcom,mdm0 {
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_core.txt b/Documentation/devicetree/bindings/arm/msm/msm_core.txt
deleted file mode 100644
index f385915..0000000
--- a/Documentation/devicetree/bindings/arm/msm/msm_core.txt
+++ /dev/null
@@ -1,71 +0,0 @@
-MSM Core Energy Aware driver
-
-The Energy Aware driver provides per core power and temperature
-information to the scheduler for it to make more power efficient
-scheduling decision.
-
-The required properties for the Energy-aware driver are:
-
-- compatible: "qcom,apss-core-ea"
-- reg: Physical address mapped to this device
-
-Required nodes:
-- ea@X: Parent node that has the sensor mapping for each cpu.
- This node's phandle is provided within cpu node
- to invoke/probe energy-aware only for available cpus.
- There should be one such node present for each cpu.
-
-Optional properties:
-- qcom,low-hyst-temp: Degrees C below which the power numbers
- need to be recomputed for the cores and reset
- the threshold. If this is not present, the default
- value is 10C.
-- qcom,high-hyst-temp: Degrees C above which the power numbers
- need to be recomputed for the cores and reset
- the threshold. If this property is not present,
- the default value is 5C.
-- qcom,polling-interval: Interval for which the power numbers
- need to be recomputed for the cores if there
- is no change in threshold. If this property is not
- present, the power is recalculated only on
- temperature threshold notifications.
--qcom,throttling-temp: Temperature threshold for cpu frequency mitigation.
- The value should be set same as the threshold temperature
- in thermal module - 5 C, such that there is a bandwidth to
- control the cores before frequency mitigation happens.
-
-[Second level nodes]
-Require properties to define per core characteristics:
-- sensor: Sensor phandle to map a particular sensor to the core.
- If this property is not present, then the core is assumed
- to be at 40C for all the power estimations. No sensor
- threshold is set. This phandle's compatible property is
- "qcom,sensor-information". This driver relies on the
- sensor-type and scaling-factor information provided in this
- phandle.
-
-Example
-
-qcom,msm-core@0xfc4b0000 {
- compatible = "qcom,apss-core-ea";
- reg = <0xfc4b0000 0x1000>;
- qcom,low-hyst-temp = <10>;
- qcom,high-hyst-temp = <5>;
- qcom,polling-interval = <50>;
-
- ea0: ea0 {
- sensor = <&sensor_information0>;
- };
-
- ea1: ea1 {
- sensor = <&sensor_information1>;
- };
-
-};
-
-CPU0: cpu@0 {
- device_type = "cpu";
- compatible = "arm,cortex-a53";
- reg = <0x0>;
- qcom,ea = <&ea0>;
-};
diff --git a/Documentation/devicetree/bindings/display/bridge/ti,ths8135.txt b/Documentation/devicetree/bindings/display/bridge/ti,ths8135.txt
new file mode 100644
index 0000000..6ec1a88
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/ti,ths8135.txt
@@ -0,0 +1,46 @@
+THS8135 Video DAC
+-----------------
+
+This is the binding for Texas Instruments THS8135 Video DAC bridge.
+
+Required properties:
+
+- compatible: Must be "ti,ths8135"
+
+Required nodes:
+
+This device has two video ports. Their connections are modelled using the OF
+graph bindings specified in Documentation/devicetree/bindings/graph.txt.
+
+- Video port 0 for RGB input
+- Video port 1 for VGA output
+
+Example
+-------
+
+vga-bridge {
+ compatible = "ti,ths8135";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ vga_bridge_in: endpoint {
+ remote-endpoint = <&lcdc_out_vga>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ vga_bridge_out: endpoint {
+ remote-endpoint = <&vga_con_in>;
+ };
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt
index 22b4e91..59654d9 100644
--- a/Documentation/devicetree/bindings/display/msm/sde.txt
+++ b/Documentation/devicetree/bindings/display/msm/sde.txt
@@ -366,6 +366,8 @@
- qcom,sde-cdp-setting: Array of 2 cell property, with a format of
<read enable, write enable> for cdp use cases in
order of <real_time>, and <non_real_time>.
+- qcom,sde-qos-cpu-mask: A u32 value indicating desired PM QoS CPU affine mask.
+- qcom,sde-qos-cpu-dma-latency: A u32 value indicating desired PM QoS CPU DMA latency in usec.
- qcom,sde-inline-rot-xin: An integer array of xin-ids related to inline
rotation.
- qcom,sde-inline-rot-xin-type: A string array indicating the type of xin,
@@ -611,6 +613,9 @@
qcom,sde-cdp-setting = <1 1>, <1 0>;
+ qcom,sde-qos-cpu-mask = <0x3>;
+ qcom,sde-qos-cpu-dma-latency = <300>;
+
qcom,sde-vbif-off = <0 0>;
qcom,sde-vbif-id = <0 1>;
qcom,sde-vbif-default-ot-rd-limit = <32>;
diff --git a/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
index 32c31af..806c458 100644
--- a/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
+++ b/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
@@ -340,13 +340,15 @@
"single_roi": default enable mode, only single roi is sent to panel
"dual_roi": two rois are merged into one big roi. Panel ddic should be able
to process two roi's along with the DCS command to send two rois.
- disabled if property is not specified.
+ disabled if property is not specified. This property is specified
+ per timing node to support resolution restrictions.
- qcom,mdss-dsi-horizontal-line-idle: List of width ranges (EC - SC) in pixels indicating
additional idle time in dsi clock cycles that is needed
to compensate for smaller line width.
- qcom,partial-update-roi-merge: Boolean indicates roi combination is need
and function has been provided for dcs
- 2A/2B command.
+ 2A/2B command. This property is specified per timing node to support
+ resolution restrictions.
- qcom,dcs-cmd-by-left: Boolean to indicate that dcs command are sent
through the left DSI controller only in a dual-dsi configuration
- qcom,mdss-dsi-panel-hdr-enabled: Boolean to indicate HDR support in panel.
@@ -383,7 +385,8 @@
- qcom,suspend-ulps-enabled: Boolean to enable support for ULPS mode for panels during suspend state.
- qcom,panel-roi-alignment: Specifies the panel ROI alignment restrictions on its
left, top, width, height alignments and minimum width and
- height values
+ height values. This property is specified per timing node to support
+ resolution's alignment restrictions.
- qcom,esd-check-enabled: Boolean used to enable ESD recovery feature.
- qcom,mdss-dsi-panel-status-command: A byte stream formed by multiple dcs packets based on
qcom dsi controller protocol, to read the panel status.
@@ -654,7 +657,6 @@
qcom,mdss-tear-check-rd-ptr-trigger-intr = <1281>;
qcom,mdss-tear-check-frame-rate = <6000>;
qcom,mdss-dsi-reset-sequence = <1 2>, <0 10>, <1 10>;
- qcom,partial-update-enabled = "single_roi";
qcom,dcs-cmd-by-left;
qcom,mdss-dsi-lp11-init;
qcom,mdss-dsi-init-delay-us = <100>;
@@ -662,7 +664,6 @@
mdss-dsi-tx-eot-append;
qcom,ulps-enabled;
qcom,suspend-ulps-enabled;
- qcom,panel-roi-alignment = <4 4 2 2 20 20>;
qcom,esd-check-enabled;
qcom,mdss-dsi-panel-status-command = [06 01 00 01 05 00 02 0A 08];
qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
@@ -721,6 +722,8 @@
qcom,mdss-dsc-config-by-manufacture-cmd;
qcom,display-topology = <1 1 1>;
qcom,default-topology-index = <0>;
+ qcom,partial-update-enabled = "single_roi";
+ qcom,panel-roi-alignment = <4 4 2 2 20 20>;
};
};
qcom,panel-supply-entries {
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
index 62ba54b..b0c5b57 100644
--- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
@@ -18,7 +18,6 @@
"high-thr-en-set" for high threshold interrupts and
"low-thr-en-set" for low threshold interrupts. High and low threshold
interrupts are to be enabled if VADC_USR needs to support recurring measurement.
-- qcom,adc-bit-resolution : Bit resolution of the ADC.
- qcom,adc-vdd-reference : Voltage reference used by the ADC.
Channel nodes
@@ -46,6 +45,12 @@
0 : The calibration values used for measurement are from a timer.
1 : Forces a fresh measurement for calibration values at the same time
measurement is taken.
+- qcom,adc-full-scale-code: Full scale code with offset removed.
+- pinctrl-names: should be "default" (see pinctrl binding [0]).
+- pinctrl-0: a phandle pointing to the pin settings for the
+ device (see pinctrl binding [0]).
+
+[0]: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
Client required property:
- qcom,<consumer name>-vadc : The phandle to the corresponding vadc device.
diff --git a/Documentation/devicetree/bindings/iio/adc/avia-hx711.txt b/Documentation/devicetree/bindings/iio/adc/avia-hx711.txt
new file mode 100644
index 0000000..b362940
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/avia-hx711.txt
@@ -0,0 +1,18 @@
+* AVIA HX711 ADC chip for weight cells
+ Bit-banging driver
+
+Required properties:
+ - compatible: Should be "avia,hx711"
+ - sck-gpios: Definition of the GPIO for the clock
+ - dout-gpios: Definition of the GPIO for data-out
+ See Documentation/devicetree/bindings/gpio/gpio.txt
+ - avdd-supply: Definition of the regulator used as analog supply
+
+Example:
+weight@0 {
+ compatible = "avia,hx711";
+ sck-gpios = <&gpio3 10 GPIO_ACTIVE_HIGH>;
+ dout-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
+ avdd-suppy = <&avdd>;
+};
+
diff --git a/Documentation/devicetree/bindings/platform/msm/ipa.txt b/Documentation/devicetree/bindings/platform/msm/ipa.txt
index aede546..6b40b30 100644
--- a/Documentation/devicetree/bindings/platform/msm/ipa.txt
+++ b/Documentation/devicetree/bindings/platform/msm/ipa.txt
@@ -79,6 +79,8 @@
- qcom,rx-polling-sleep-ms: Receive Polling Timeout in millisecond,
default is 1 millisecond.
- qcom,ipa-polling-iteration: IPA Polling Iteration Count,default is 40.
+- qcom,mhi-event-ring-id-limits: Two elements property. Start and End limits
+ for MHI event rings ids.
- qcom,ipa-tz-unlock-reg: Register start addresses and ranges which
need to be unlocked by TZ.
diff --git a/Documentation/devicetree/bindings/soc/qcom/dcc.txt b/Documentation/devicetree/bindings/soc/qcom/dcc.txt
index 8a9761c..5150459 100644
--- a/Documentation/devicetree/bindings/soc/qcom/dcc.txt
+++ b/Documentation/devicetree/bindings/soc/qcom/dcc.txt
@@ -35,6 +35,14 @@
"atb" : To send captured data over ATB to a trace sink
"sram" : To save captured data in dcc internal SRAM.
+- qcom,curr-link-list: int, To specify the link list to use for the default list.
+
+- qcom,link-list: The values to be programmed into the default link list.
+ The enum values for DCC operations is defined in dt-bindings/soc/qcom,dcc_v2.h
+ The following gives basic structure to be used for each operation:
+ <DCC_operation addr val apb_bus>
+ val is to be interpreted based on what operation is to be performed.
+
Example:
dcc: dcc@4b3000 {
@@ -47,6 +55,13 @@
clocks = <&clock_gcc clk_gcc_dcc_ahb_clk>;
clock-names = "dcc_clk";
+ qcom,curr-link-list = <2>;
+ qcom,link-list = <DCC_READ 0x1740300 6 0>,
+ <DCC_READ 0x1620500 4 0>,
+ <DCC_READ 0x7840000 1 0>,
+ <DCC_READ 0x7841010 12 0>,
+ <DCC_READ 0x7842000 16 0>,
+ <DCC_READ 0x7842500 2 0>;
qcom,save-reg;
};
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,msm-eud.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,msm-eud.txt
index 45e309c..bf2a91a 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,msm-eud.txt
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,msm-eud.txt
@@ -14,6 +14,9 @@
Documentation/devicetree/bindings/clock/clock-bindings.txt
- clock-names: Names of the clocks in 1-1 correspondence with
the "clocks" property.
+ - <supply-name>-supply: phandle to the regulator device tree node
+ Required "supply-name" examples are:
+ "vdda33" : 3.3v supply to eud.
Driver notifies clients via extcon for VBUS spoof attach/detach
and charger enable/disable events. Clients registered for these
@@ -29,6 +32,7 @@
reg-names = "eud_base";
clocks = <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>;
clock-names = "cfg_ahb_clk";
+ vdda33-supply = <&pm8998_l24>;
};
An example for EUD extcon client:
diff --git a/Documentation/devicetree/bindings/usb/msm-phy.txt b/Documentation/devicetree/bindings/usb/msm-phy.txt
index d23cb46..6109fad 100644
--- a/Documentation/devicetree/bindings/usb/msm-phy.txt
+++ b/Documentation/devicetree/bindings/usb/msm-phy.txt
@@ -159,7 +159,6 @@
"efuse_addr": EFUSE address to read and update analog tune parameter.
"emu_phy_base" : phy base address used for programming emulation target phy.
"ref_clk_addr" : ref_clk bcr address used for on/off ref_clk before reset.
- "eud_base" : EUD device register address space to use EUD pet functionality.
- clocks: a list of phandles to the PHY clocks. Use as per
Documentation/devicetree/bindings/clock/clock-bindings.txt
- clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 3bdc8967..a491bd7 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -38,6 +38,7 @@
auo AU Optronics Corporation
auvidea Auvidea GmbH
avago Avago Technologies
+avia avia semiconductor
avic Shanghai AVIC Optoelectronics Co., Ltd.
axis Axis Communications AB
boe BOE Technology Group Co., Ltd.
diff --git a/Makefile b/Makefile
index 665104d..dd43551 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 4
PATCHLEVEL = 9
-SUBLEVEL = 51
+SUBLEVEL = 58
EXTRAVERSION =
NAME = Roaring Lionus
diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S
index 1eea99b..85d9ea4 100644
--- a/arch/arc/kernel/entry.S
+++ b/arch/arc/kernel/entry.S
@@ -92,6 +92,12 @@
lr r0, [efa]
mov r1, sp
+ ; hardware auto-disables MMU, re-enable it to allow kernel vaddr
+ ; access for say stack unwinding of modules for crash dumps
+ lr r3, [ARC_REG_PID]
+ or r3, r3, MMU_ENABLE
+ sr r3, [ARC_REG_PID]
+
lsr r3, r2, 8
bmsk r3, r3, 7
brne r3, ECR_C_MCHK_DUP_TLB, 1f
diff --git a/arch/arc/mm/tlb.c b/arch/arc/mm/tlb.c
index bdb295e..a4dc881 100644
--- a/arch/arc/mm/tlb.c
+++ b/arch/arc/mm/tlb.c
@@ -896,9 +896,6 @@
local_irq_save(flags);
- /* re-enable the MMU */
- write_aux_reg(ARC_REG_PID, MMU_ENABLE | read_aux_reg(ARC_REG_PID));
-
/* loop thru all sets of TLB */
for (set = 0; set < mmu->sets; set++) {
diff --git a/arch/arm/Kconfig-nommu b/arch/arm/Kconfig-nommu
index aed66d5..b757634 100644
--- a/arch/arm/Kconfig-nommu
+++ b/arch/arm/Kconfig-nommu
@@ -34,8 +34,7 @@
used instead of the auto-probing which utilizes the register.
config REMAP_VECTORS_TO_RAM
- bool 'Install vectors to the beginning of RAM' if DRAM_BASE
- depends on DRAM_BASE
+ bool 'Install vectors to the beginning of RAM'
help
The kernel needs to change the hardware exception vectors.
In nommu mode, the hardware exception vectors are normally
diff --git a/arch/arm/boot/dts/am335x-chilisom.dtsi b/arch/arm/boot/dts/am335x-chilisom.dtsi
index f9ee585..1b43ebd 100644
--- a/arch/arm/boot/dts/am335x-chilisom.dtsi
+++ b/arch/arm/boot/dts/am335x-chilisom.dtsi
@@ -124,6 +124,14 @@
&rtc {
system-power-controller;
+
+ pinctrl-0 = <&ext_wakeup>;
+ pinctrl-names = "default";
+
+ ext_wakeup: ext-wakeup {
+ pins = "ext_wakeup0";
+ input-enable;
+ };
};
/* NAND Flash */
diff --git a/arch/arm/boot/dts/bcm953012k.dts b/arch/arm/boot/dts/bcm953012k.dts
index 05a985a..6208e85 100644
--- a/arch/arm/boot/dts/bcm953012k.dts
+++ b/arch/arm/boot/dts/bcm953012k.dts
@@ -48,7 +48,7 @@
};
memory {
- reg = <0x00000000 0x10000000>;
+ reg = <0x80000000 0x10000000>;
};
};
diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi
index 8aa19ba..5282d69 100644
--- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi
+++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi
@@ -97,11 +97,11 @@
thermal-zones {
cpu_thermal: cpu-thermal {
cooling-maps {
- map0 {
+ cooling_map0: map0 {
/* Corresponds to 800MHz at freq_table */
cooling-device = <&cpu0 7 7>;
};
- map1 {
+ cooling_map1: map1 {
/* Corresponds to 200MHz at freq_table */
cooling-device = <&cpu0 13 13>;
};
diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
index 99634c5..7504a5a 100644
--- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
@@ -13,6 +13,7 @@
/dts-v1/;
#include "exynos4412-odroid-common.dtsi"
+#include "exynos4412-prime.dtsi"
/ {
model = "Hardkernel ODROID-U3 board based on Exynos4412";
@@ -47,11 +48,11 @@
cooling-maps {
map0 {
trip = <&cpu_alert1>;
- cooling-device = <&cpu0 7 7>;
+ cooling-device = <&cpu0 9 9>;
};
map1 {
trip = <&cpu_alert2>;
- cooling-device = <&cpu0 13 13>;
+ cooling-device = <&cpu0 15 15>;
};
map2 {
trip = <&cpu_alert0>;
diff --git a/arch/arm/boot/dts/exynos4412-odroidx2.dts b/arch/arm/boot/dts/exynos4412-odroidx2.dts
index 4d22885..d6e92eb 100644
--- a/arch/arm/boot/dts/exynos4412-odroidx2.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidx2.dts
@@ -12,6 +12,7 @@
*/
#include "exynos4412-odroidx.dts"
+#include "exynos4412-prime.dtsi"
/ {
model = "Hardkernel ODROID-X2 board based on Exynos4412";
diff --git a/arch/arm/boot/dts/exynos4412-prime.dtsi b/arch/arm/boot/dts/exynos4412-prime.dtsi
new file mode 100644
index 0000000..e75bc17
--- /dev/null
+++ b/arch/arm/boot/dts/exynos4412-prime.dtsi
@@ -0,0 +1,41 @@
+/*
+ * Samsung's Exynos4412 Prime SoC device tree source
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * Exynos4412 Prime SoC revision supports higher CPU frequencies than
+ * non-Prime version. Therefore we need to update OPPs table and
+ * thermal maps accordingly.
+ */
+
+&cpu0_opp_1500 {
+ /delete-property/turbo-mode;
+};
+
+&cpu0_opp_table {
+ opp@1600000000 {
+ opp-hz = /bits/ 64 <1600000000>;
+ opp-microvolt = <1350000>;
+ clock-latency-ns = <200000>;
+ };
+ opp@1704000000 {
+ opp-hz = /bits/ 64 <1704000000>;
+ opp-microvolt = <1350000>;
+ clock-latency-ns = <200000>;
+ };
+};
+
+&cooling_map0 {
+ cooling-device = <&cpu0 9 9>;
+};
+
+&cooling_map1 {
+ cooling-device = <&cpu0 15 15>;
+};
diff --git a/arch/arm/boot/dts/exynos4412.dtsi b/arch/arm/boot/dts/exynos4412.dtsi
index 40beede..3ebdf01 100644
--- a/arch/arm/boot/dts/exynos4412.dtsi
+++ b/arch/arm/boot/dts/exynos4412.dtsi
@@ -130,7 +130,7 @@
opp-microvolt = <1287500>;
clock-latency-ns = <200000>;
};
- opp@1500000000 {
+ cpu0_opp_1500: opp@1500000000 {
opp-hz = /bits/ 64 <1500000000>;
opp-microvolt = <1350000>;
clock-latency-ns = <200000>;
diff --git a/arch/arm/boot/dts/mt2701.dtsi b/arch/arm/boot/dts/mt2701.dtsi
index 18596a2..77c6b93 100644
--- a/arch/arm/boot/dts/mt2701.dtsi
+++ b/arch/arm/boot/dts/mt2701.dtsi
@@ -174,4 +174,40 @@
clocks = <&uart_clk>;
status = "disabled";
};
+
+ mmsys: syscon@14000000 {
+ compatible = "mediatek,mt2701-mmsys", "syscon";
+ reg = <0 0x14000000 0 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ imgsys: syscon@15000000 {
+ compatible = "mediatek,mt2701-imgsys", "syscon";
+ reg = <0 0x15000000 0 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ vdecsys: syscon@16000000 {
+ compatible = "mediatek,mt2701-vdecsys", "syscon";
+ reg = <0 0x16000000 0 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ hifsys: syscon@1a000000 {
+ compatible = "mediatek,mt2701-hifsys", "syscon";
+ reg = <0 0x1a000000 0 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ ethsys: syscon@1b000000 {
+ compatible = "mediatek,mt2701-ethsys", "syscon";
+ reg = <0 0x1b000000 0 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ bdpsys: syscon@1c000000 {
+ compatible = "mediatek,mt2701-bdpsys", "syscon";
+ reg = <0 0x1c000000 0 0x1000>;
+ #clock-cells = <1>;
+ };
};
diff --git a/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi b/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi
index e3f154b..2106759 100644
--- a/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi
@@ -85,6 +85,55 @@
interrupts = <0x0 0x61 0x1 IRQ_TYPE_NONE>;
};
};
+
+ pmxpoorwills_vadc: vadc@3100 {
+ compatible = "qcom,qpnp-vadc-hc";
+ reg = <0x3100 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "eoc-int-en-set";
+ qcom,adc-full-scale-code = <0x70e4>;
+ qcom,adc-vdd-reference = <1875>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&ambient_therm_default>;
+
+ chan@6 {
+ label = "die_temp";
+ reg = <6>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <3>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,cal-val = <0>;
+ };
+
+ chan@0 {
+ label = "ref_gnd";
+ reg = <0>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,cal-val = <0>;
+ };
+
+ chan@1 {
+ label = "ref_1250v";
+ reg = <1>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,cal-val = <0>;
+ };
+ };
};
qcom,pmxpoorwills@1 {
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.dts
index 8b5eee0..15129c7 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.dts
@@ -32,3 +32,76 @@
&qnand_1 {
status = "ok";
};
+
+&pmxpoorwills_vadc {
+ chan@83 {
+ label = "vph_pwr";
+ reg = <0x83>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@4c {
+ label = "xo_therm";
+ reg = <0x4c>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@4d {
+ label = "pa_therm1";
+ reg = <0x4d>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@4e {
+ label = "pa_therm2";
+ reg = <0x4e>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@4f {
+ label = "mdm_case_therm";
+ reg = <0x4f>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@52 {
+ label = "ambient_therm";
+ reg = <0x52>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts
index 0f84228..8d7e377 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts
@@ -32,3 +32,76 @@
&qnand_1 {
status = "ok";
};
+
+&pmxpoorwills_vadc {
+ chan@83 {
+ label = "vph_pwr";
+ reg = <0x83>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@4c {
+ label = "xo_therm";
+ reg = <0x4c>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@4d {
+ label = "pa_therm1";
+ reg = <0x4d>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@4e {
+ label = "pa_therm2";
+ reg = <0x4e>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@4f {
+ label = "mdm_case_therm";
+ reg = <0x4f>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@52 {
+ label = "ambient_therm";
+ reg = <0x52>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi
index 8181fa8..2b0fa5c 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi
@@ -921,3 +921,12 @@
};
};
};
+
+&pmxpoorwills_gpios {
+ ambient_therm {
+ ambient_therm_default: ambient_therm_default {
+ pins = "gpio2";
+ bias-high-impedance;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-thermal.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-thermal.dtsi
new file mode 100644
index 0000000..5a4810a
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-thermal.dtsi
@@ -0,0 +1,293 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/thermal/thermal.h>
+
+&soc {
+ qmi-tmd-devices {
+ compatible = "qcom,qmi_cooling_devices";
+
+ modem {
+ qcom,instance-id = <0x0>;
+
+ modem_pa: modem_pa {
+ qcom,qmi-dev-name = "pa";
+ #cooling-cells = <2>;
+ };
+
+ modem_proc: modem_proc {
+ qcom,qmi-dev-name = "modem";
+ #cooling-cells = <2>;
+ };
+
+ modem_current: modem_current {
+ qcom,qmi-dev-name = "modem_current";
+ #cooling-cells = <2>;
+ };
+
+ modem_skin: modem_skin {
+ qcom,qmi-dev-name = "modem_skin";
+ #cooling-cells = <2>;
+ };
+
+ modem_vdd: modem_vdd {
+ qcom,qmi-dev-name = "cpuv_restriction_cold";
+ #cooling-cells = <2>;
+ };
+ };
+
+ adsp {
+ qcom,instance-id = <0x1>;
+
+ adsp_vdd: adsp_vdd {
+ qcom,qmi-dev-name = "cpuv_restriction_cold";
+ #cooling-cells = <2>;
+ };
+ };
+ };
+};
+
+&thermal_zones {
+ aoss-usr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "user_space";
+ thermal-sensors = <&tsens0 0>;
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ mdm-q6-usr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "user_space";
+ thermal-sensors = <&tsens0 1>;
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ ddrss-usr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "user_space";
+ thermal-sensors = <&tsens0 2>;
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ cpu-usr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "user_space";
+ thermal-sensors = <&tsens0 3>;
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ mdm-core-usr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "user_space";
+ thermal-sensors = <&tsens0 4>;
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ mdm-vpe-usr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "user_space";
+ thermal-sensors = <&tsens0 5>;
+ trips {
+ active-config0 {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ aoss-lowf {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 0>;
+ tracks-low;
+ trips {
+ aoss_trip: aoss-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ modem_vdd_cdev {
+ trip = <&aoss_trip>;
+ cooling-device = <&modem_vdd 0 0>;
+ };
+ adsp_vdd_cdev {
+ trip = <&aoss_trip>;
+ cooling-device = <&adsp_vdd 0 0>;
+ };
+ };
+ };
+
+ mdm-q6-lowf {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 1>;
+ tracks-low;
+ trips {
+ mdm_q6_trip: mdm-q6-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ modem_vdd_cdev {
+ trip = <&mdm_q6_trip>;
+ cooling-device = <&modem_vdd 0 0>;
+ };
+ adsp_vdd_cdev {
+ trip = <&mdm_q6_trip>;
+ cooling-device = <&adsp_vdd 0 0>;
+ };
+ };
+ };
+
+ ddrss-lowf {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 2>;
+ tracks-low;
+ trips {
+ ddrss_trip: ddrss-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ modem_vdd_cdev {
+ trip = <&ddrss_trip>;
+ cooling-device = <&modem_vdd 0 0>;
+ };
+ adsp_vdd_cdev {
+ trip = <&ddrss_trip>;
+ cooling-device = <&adsp_vdd 0 0>;
+ };
+ };
+ };
+
+ cpu-lowf {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 3>;
+ tracks-low;
+ trips {
+ cpu_trip: cpu-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ modem_vdd_cdev {
+ trip = <&cpu_trip>;
+ cooling-device = <&modem_vdd 0 0>;
+ };
+ adsp_vdd_cdev {
+ trip = <&cpu_trip>;
+ cooling-device = <&adsp_vdd 0 0>;
+ };
+ };
+ };
+
+ mdm-core-lowf {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 4>;
+ tracks-low;
+ trips {
+ mdm_trip: mdm-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ modem_vdd_cdev {
+ trip = <&mdm_trip>;
+ cooling-device = <&modem_vdd 0 0>;
+ };
+ adsp_vdd_cdev {
+ trip = <&mdm_trip>;
+ cooling-device = <&adsp_vdd 0 0>;
+ };
+ };
+ };
+
+ mdm-vpe-lowf {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 5>;
+ tracks-low;
+ trips {
+ mdm_vpe_trip: mdm-vpe-trip {
+ temperature = <125000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ modem_vdd_cdev {
+ trip = <&mdm_vpe_trip>;
+ cooling-device = <&modem_vdd 0 0>;
+ };
+ adsp_vdd_cdev {
+ trip = <&mdm_vpe_trip>;
+ cooling-device = <&adsp_vdd 0 0>;
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index 7875f19..e1136da 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -51,6 +51,7 @@
device-type = "cpu";
compatible = "arm,cortex-a7";
reg = <0x0>;
+ #cooling-cells = <2>;
};
};
@@ -263,77 +264,7 @@
#thermal-sensor-cells = <1>;
};
- thermal_zones: thermal-zones {
- mpm-usr {
- polling-delay-passive = <0>;
- polling-delay = <0>;
- thermal-governor = "user_space";
- thermal-sensors = <&tsens0 0>;
- trips {
- active-config0 {
- temperature = <125000>;
- hysteresis = <1000>;
- type = "passive";
- };
- };
- };
-
- q6-usr {
- polling-delay-passive = <0>;
- polling-delay = <0>;
- thermal-governor = "user_space";
- thermal-sensors = <&tsens0 1>;
- trips {
- active-config0 {
- temperature = <125000>;
- hysteresis = <1000>;
- type = "passive";
- };
- };
- };
-
- ctile-usr {
- polling-delay-passive = <0>;
- polling-delay = <0>;
- thermal-governor = "user_space";
- thermal-sensors = <&tsens0 2>;
- trips {
- active-config0 {
- temperature = <125000>;
- hysteresis = <1000>;
- type = "passive";
- };
- };
- };
-
- cpu-usr {
- polling-delay-passive = <0>;
- polling-delay = <0>;
- thermal-governor = "user_space";
- thermal-sensors = <&tsens0 3>;
- trips {
- active-config0 {
- temperature = <125000>;
- hysteresis = <1000>;
- type = "passive";
- };
- };
- };
-
- mdm-usr {
- polling-delay-passive = <0>;
- polling-delay = <0>;
- thermal-governor = "user_space";
- thermal-sensors = <&tsens0 4>;
- trips {
- active-config0 {
- temperature = <125000>;
- hysteresis = <1000>;
- type = "passive";
- };
- };
- };
- };
+ thermal_zones: thermal-zones { };
qcom,ipa_fws {
compatible = "qcom,pil-tz-generic";
@@ -381,8 +312,8 @@
reg = <0x8fe40000 0xc0000>,
<0x17811008 0x4>;
reg-names = "smem", "irq-reg-base";
- qcom,irq-mask = <0x1000>;
- interrupts = <GIC_SPI 111 IRQ_TYPE_EDGE_RISING>;
+ qcom,irq-mask = <0x8000>;
+ interrupts = <GIC_SPI 114 IRQ_TYPE_EDGE_RISING>;
label = "mpss";
};
@@ -492,3 +423,4 @@
#include "sdxpoorwills-smp2p.dtsi"
#include "sdxpoorwills-usb.dtsi"
#include "sdxpoorwills-bus.dtsi"
+#include "sdxpoorwills-thermal.dtsi"
diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi
index 351fcc2..b6c6410 100644
--- a/arch/arm/boot/dts/r8a7790.dtsi
+++ b/arch/arm/boot/dts/r8a7790.dtsi
@@ -1493,7 +1493,8 @@
};
msiof0: spi@e6e20000 {
- compatible = "renesas,msiof-r8a7790";
+ compatible = "renesas,msiof-r8a7790",
+ "renesas,rcar-gen2-msiof";
reg = <0 0xe6e20000 0 0x0064>;
interrupts = <GIC_SPI 156 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp0_clks R8A7790_CLK_MSIOF0>;
@@ -1507,7 +1508,8 @@
};
msiof1: spi@e6e10000 {
- compatible = "renesas,msiof-r8a7790";
+ compatible = "renesas,msiof-r8a7790",
+ "renesas,rcar-gen2-msiof";
reg = <0 0xe6e10000 0 0x0064>;
interrupts = <GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp2_clks R8A7790_CLK_MSIOF1>;
@@ -1521,7 +1523,8 @@
};
msiof2: spi@e6e00000 {
- compatible = "renesas,msiof-r8a7790";
+ compatible = "renesas,msiof-r8a7790",
+ "renesas,rcar-gen2-msiof";
reg = <0 0xe6e00000 0 0x0064>;
interrupts = <GIC_SPI 158 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp2_clks R8A7790_CLK_MSIOF2>;
@@ -1535,7 +1538,8 @@
};
msiof3: spi@e6c90000 {
- compatible = "renesas,msiof-r8a7790";
+ compatible = "renesas,msiof-r8a7790",
+ "renesas,rcar-gen2-msiof";
reg = <0 0xe6c90000 0 0x0064>;
interrupts = <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp2_clks R8A7790_CLK_MSIOF3>;
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index 877406f..0d35667 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -217,6 +217,7 @@
CONFIG_QCOM_DLOAD_MODE=y
CONFIG_POWER_SUPPLY=y
CONFIG_SMB138X_CHARGER=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_THERMAL=y
CONFIG_THERMAL_QPNP=y
CONFIG_THERMAL_TSENS=y
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
index d860595..6c24910 100644
--- a/arch/arm/configs/sdxpoorwills_defconfig
+++ b/arch/arm/configs/sdxpoorwills_defconfig
@@ -210,6 +210,7 @@
CONFIG_QCOM_DLOAD_MODE=y
CONFIG_POWER_SUPPLY=y
CONFIG_SMB138X_CHARGER=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_THERMAL=y
CONFIG_THERMAL_QPNP=y
CONFIG_THERMAL_TSENS=y
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index 31dde8b..8ba0e2e 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -335,7 +335,7 @@
at91_ramc_write(1, AT91_SDRAMC_LPR, saved_lpr1);
}
-static const struct of_device_id const ramc_ids[] __initconst = {
+static const struct of_device_id ramc_ids[] __initconst = {
{ .compatible = "atmel,at91rm9200-sdramc", .data = at91rm9200_standby },
{ .compatible = "atmel,at91sam9260-sdramc", .data = at91sam9_sdram_standby },
{ .compatible = "atmel,at91sam9g45-ddramc", .data = at91_ddr_standby },
diff --git a/arch/arm/mach-bcm/bcm_kona_smc.c b/arch/arm/mach-bcm/bcm_kona_smc.c
index cf3f865..a55a7ec 100644
--- a/arch/arm/mach-bcm/bcm_kona_smc.c
+++ b/arch/arm/mach-bcm/bcm_kona_smc.c
@@ -33,7 +33,7 @@
unsigned result;
};
-static const struct of_device_id const bcm_kona_smc_ids[] __initconst = {
+static const struct of_device_id bcm_kona_smc_ids[] __initconst = {
{.compatible = "brcm,kona-smc"},
{.compatible = "bcm,kona-smc"}, /* deprecated name */
{},
diff --git a/arch/arm/mach-cns3xxx/core.c b/arch/arm/mach-cns3xxx/core.c
index 03da381..7d5a44a 100644
--- a/arch/arm/mach-cns3xxx/core.c
+++ b/arch/arm/mach-cns3xxx/core.c
@@ -346,7 +346,7 @@
.power_off = csn3xxx_usb_power_off,
};
-static const struct of_dev_auxdata const cns3xxx_auxdata[] __initconst = {
+static const struct of_dev_auxdata cns3xxx_auxdata[] __initconst = {
{ "intel,usb-ehci", CNS3XXX_USB_BASE, "ehci-platform", &cns3xxx_usb_ehci_pdata },
{ "intel,usb-ohci", CNS3XXX_USB_OHCI_BASE, "ohci-platform", &cns3xxx_usb_ohci_pdata },
{ "cavium,cns3420-ahci", CNS3XXX_SATA2_BASE, "ahci", NULL },
diff --git a/arch/arm/mach-omap2/prm_common.c b/arch/arm/mach-omap2/prm_common.c
index 5b2f513..f1ca947 100644
--- a/arch/arm/mach-omap2/prm_common.c
+++ b/arch/arm/mach-omap2/prm_common.c
@@ -713,7 +713,7 @@
};
#endif
-static const struct of_device_id const omap_prcm_dt_match_table[] __initconst = {
+static const struct of_device_id omap_prcm_dt_match_table[] __initconst = {
#ifdef CONFIG_SOC_AM33XX
{ .compatible = "ti,am3-prcm", .data = &am3_prm_data },
#endif
diff --git a/arch/arm/mach-omap2/vc.c b/arch/arm/mach-omap2/vc.c
index 2028167f..d76b1e5 100644
--- a/arch/arm/mach-omap2/vc.c
+++ b/arch/arm/mach-omap2/vc.c
@@ -559,7 +559,7 @@
u8 hsscll_12;
};
-static const struct i2c_init_data const omap4_i2c_timing_data[] __initconst = {
+static const struct i2c_init_data omap4_i2c_timing_data[] __initconst = {
{
.load = 50,
.loadbits = 0x3,
diff --git a/arch/arm/mach-spear/time.c b/arch/arm/mach-spear/time.c
index 9ccffc1..aaaa678 100644
--- a/arch/arm/mach-spear/time.c
+++ b/arch/arm/mach-spear/time.c
@@ -204,7 +204,7 @@
setup_irq(irq, &spear_timer_irq);
}
-static const struct of_device_id const timer_of_match[] __initconst = {
+static const struct of_device_id timer_of_match[] __initconst = {
{ .compatible = "st,spear-timer", },
{ },
};
diff --git a/arch/arm/xen/mm.c b/arch/arm/xen/mm.c
index d062f08..4b24964 100644
--- a/arch/arm/xen/mm.c
+++ b/arch/arm/xen/mm.c
@@ -199,6 +199,7 @@
.unmap_page = xen_swiotlb_unmap_page,
.dma_supported = xen_swiotlb_dma_supported,
.set_dma_mask = xen_swiotlb_set_dma_mask,
+ .mmap = xen_swiotlb_dma_mmap,
};
int __init xen_mm_init(void)
diff --git a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi
index 25a332b..9e0f1f4 100644
--- a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi
@@ -63,6 +63,7 @@
#iommu-cells = <2>;
qcom,skip-init;
qcom,use-3-lvl-tables;
+ qcom,no-asid-retention;
#global-interrupts = <1>;
#size-cells = <1>;
#address-cells = <1>;
@@ -293,6 +294,7 @@
};
kgsl_iommu_test_device {
+ status = "disabled";
compatible = "iommu-debug-test";
/*
* 0x7 isn't a valid sid, but should pass the sid sanity check.
diff --git a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
index b5cfd89..e4fe2e3 100644
--- a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
@@ -346,6 +346,19 @@
};
&apps_smmu {
+ qcom,actlr = <0x0880 0x8 0x103>,
+ <0x0881 0x8 0x103>,
+ <0x0c80 0x8 0x103>,
+ <0x0c81 0x8 0x103>,
+ <0x1090 0x0 0x103>,
+ <0x1091 0x0 0x103>,
+ <0x10a0 0x8 0x103>,
+ <0x10b0 0x0 0x103>,
+ <0x10a1 0x8 0x103>,
+ <0x10a3 0x8 0x103>,
+ <0x10a4 0x8 0x103>,
+ <0x10b4 0x0 0x103>,
+ <0x10a5 0x8 0x103>;
qcom,mmu500-errata-1 = <0x800 0x3ff>,
<0xc00 0x3ff>;
};
diff --git a/arch/arm64/boot/dts/qcom/msm8953.dtsi b/arch/arm64/boot/dts/qcom/msm8953.dtsi
index f17ac32..c4a30bf 100644
--- a/arch/arm64/boot/dts/qcom/msm8953.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953.dtsi
@@ -13,6 +13,7 @@
#include "skeleton64.dtsi"
#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
/ {
model = "Qualcomm Technologies, Inc. MSM 8953";
@@ -601,5 +602,22 @@
status = "disabled";
};
+ spmi_bus: qcom,spmi@200f000 {
+ compatible = "qcom,spmi-pmic-arb";
+ reg = <0x200f000 0x1000>,
+ <0x2400000 0x800000>,
+ <0x2c00000 0x800000>,
+ <0x3800000 0x200000>,
+ <0x200a000 0x2100>;
+ reg-names = "core", "chnls", "obsrvr", "intr", "cnfg";
+ interrupt-names = "periph_irq";
+ interrupts = <GIC_SPI 190 IRQ_TYPE_NONE>;
+ qcom,ee = <0>;
+ qcom,channel = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ cell-index = <0>;
+ };
};
-
diff --git a/arch/arm64/boot/dts/qcom/pm660.dtsi b/arch/arm64/boot/dts/qcom/pm660.dtsi
index df5a970..502b2fe 100644
--- a/arch/arm64/boot/dts/qcom/pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm660.dtsi
@@ -116,7 +116,6 @@
#size-cells = <0>;
interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "eoc-int-en-set";
- qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1875>;
chan@6 {
@@ -280,7 +279,6 @@
#size-cells = <0>;
interrupts = <0x0 0x34 0x0 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "eoc-int-en-set";
- qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1875>;
qcom,adc_tm-vadc = <&pm660_vadc>;
qcom,decimation = <0>;
@@ -338,7 +336,7 @@
qcom,scale-function = <2>;
qcom,hw-settle-time = <2>;
qcom,btm-channel-number = <0x80>;
- qcom,vadc-thermal-node;
+ qcom,thermal-node;
};
chan@4f {
@@ -349,7 +347,7 @@
qcom,scale-function = <2>;
qcom,hw-settle-time = <2>;
qcom,btm-channel-number = <0x88>;
- qcom,vadc-thermal-node;
+ qcom,thermal-node;
};
};
diff --git a/arch/arm64/boot/dts/qcom/pm8998.dtsi b/arch/arm64/boot/dts/qcom/pm8998.dtsi
index 013ac48..dc3ffda 100644
--- a/arch/arm64/boot/dts/qcom/pm8998.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm8998.dtsi
@@ -138,7 +138,6 @@
#size-cells = <0>;
interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "eoc-int-en-set";
- qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1875>;
chan@6 {
@@ -185,7 +184,6 @@
#size-cells = <0>;
interrupts = <0x0 0x34 0x0 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "eoc-int-en-set";
- qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1875>;
qcom,adc_tm-vadc = <&pm8998_vadc>;
qcom,decimation = <0>;
diff --git a/arch/arm64/boot/dts/qcom/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi
index 8d8bd63..c65430b1 100644
--- a/arch/arm64/boot/dts/qcom/pmi8998.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi8998.dtsi
@@ -102,6 +102,7 @@
qcom,thermal-mitigation
= <3000000 1500000 1000000 500000>;
+ qcom,auto-recharge-soc;
qcom,chgr@1000 {
reg = <0x1000 0x100>;
@@ -282,6 +283,9 @@
qcom,fg-esr-timer-asleep = <256 256>;
qcom,fg-esr-timer-charging = <0 96>;
qcom,cycle-counter-en;
+ qcom,hold-soc-while-full;
+ qcom,fg-auto-recharge-soc;
+ qcom,fg-recharge-soc-thr = <98>;
status = "okay";
qcom,fg-batt-soc@4000 {
diff --git a/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi b/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi
index 6f22264..4f5a9b1 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi
@@ -12,6 +12,7 @@
#include <dt-bindings/msm/msm-bus-ids.h>
#include <dt-bindings/soc/qcom,tcs-mbox.h>
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
&soc {
ad_hoc_bus: ad-hoc-bus {
@@ -309,7 +310,6 @@
qcom,bcm-dev;
};
-
/*Buses*/
fab_aggre1_noc: fab-aggre1_noc {
cell-id = <MSM_BUS_FAB_A1_NOC>;
@@ -318,7 +318,6 @@
qcom,base-name = "aggre1_noc-base";
qcom,qos-off = <4096>;
qcom,base-offset = <16384>;
- qcom,bypass-qos-prg;
qcom,bus-type = <1>;
clocks = <>;
};
@@ -330,7 +329,6 @@
qcom,base-name = "aggre2_noc-base";
qcom,qos-off = <2048>;
qcom,base-offset = <12288>;
- qcom,bypass-qos-prg;
qcom,bus-type = <1>;
clocks = <>;
};
@@ -340,8 +338,6 @@
label = "fab-camnoc_virt";
qcom,fab-dev;
qcom,base-name = "camnoc_virt-base";
- qcom,qos-off = <0>;
- qcom,base-offset = <0>;
qcom,bypass-qos-prg;
clocks = <>;
};
@@ -351,8 +347,6 @@
label = "fab-config_noc";
qcom,fab-dev;
qcom,base-name = "config_noc-base";
- qcom,qos-off = <0>;
- qcom,base-offset = <0>;
qcom,bypass-qos-prg;
qcom,bus-type = <1>;
clocks = <>;
@@ -363,8 +357,6 @@
label = "fab-dc_noc";
qcom,fab-dev;
qcom,base-name = "dc_noc-base";
- qcom,qos-off = <0>;
- qcom,base-offset = <0>;
qcom,bypass-qos-prg;
qcom,bus-type = <1>;
clocks = <>;
@@ -375,8 +367,6 @@
label = "fab-gladiator_noc";
qcom,fab-dev;
qcom,base-name = "gladiator_noc-base";
- qcom,qos-off = <0>;
- qcom,base-offset = <0>;
qcom,bypass-qos-prg;
qcom,bus-type = <1>;
clocks = <>;
@@ -387,8 +377,6 @@
label = "fab-ipa_virt";
qcom,fab-dev;
qcom,base-name = "ipa_virt-base";
- qcom,qos-off = <0>;
- qcom,base-offset = <0>;
qcom,bypass-qos-prg;
clocks = <>;
};
@@ -398,8 +386,6 @@
label = "fab-mc_virt";
qcom,fab-dev;
qcom,base-name = "mc_virt-base";
- qcom,qos-off = <0>;
- qcom,base-offset = <0>;
qcom,bypass-qos-prg;
clocks = <>;
};
@@ -411,7 +397,6 @@
qcom,base-name = "mem_noc-base";
qcom,qos-off = <4096>;
qcom,base-offset = <65536>;
- qcom,bypass-qos-prg;
qcom,bus-type = <1>;
clocks = <>;
};
@@ -423,7 +408,6 @@
qcom,base-name = "mmss_noc-base";
qcom,qos-off = <4096>;
qcom,base-offset = <36864>;
- qcom,bypass-qos-prg;
qcom,bus-type = <1>;
clocks = <>;
};
@@ -435,7 +419,6 @@
qcom,base-name = "system_noc-base";
qcom,qos-off = <4096>;
qcom,base-offset = <36864>;
- qcom,bypass-qos-prg;
qcom,bus-type = <1>;
clocks = <>;
};
@@ -445,8 +428,6 @@
label = "fab-mc_virt_display";
qcom,fab-dev;
qcom,base-name = "mc_virt-base";
- qcom,qos-off = <0>;
- qcom,base-offset = <0>;
qcom,bypass-qos-prg;
clocks = <>;
};
@@ -468,14 +449,11 @@
label = "fab-mmss_noc_display";
qcom,fab-dev;
qcom,base-name = "mmss_noc-base";
- qcom,qos-off = <4096>;
- qcom,base-offset = <36864>;
qcom,bypass-qos-prg;
qcom,bus-type = <1>;
clocks = <>;
};
-
/*Masters*/
mas_qhm_a1noc_cfg: mas-qhm-a1noc-cfg {
@@ -492,12 +470,9 @@
label = "mas-qhm-qup1";
qcom,buswidth = <4>;
qcom,agg-ports = <1>;
- qcom,qport = <16>;
qcom,connections = <&slv_qns_a1noc_snoc>;
qcom,bus-dev = <&fab_aggre1_noc>;
qcom,bcms = <&bcm_qup0>;
- qcom,ap-owned;
- qcom,prio = <0>;
};
mas_qhm_tsif: mas-qhm-tsif {
@@ -571,11 +546,8 @@
label = "mas-qhm-qdss-bam";
qcom,buswidth = <4>;
qcom,agg-ports = <1>;
- qcom,qport = <17>;
qcom,connections = <&slv_qns_a2noc_snoc>;
qcom,bus-dev = <&fab_aggre2_noc>;
- qcom,ap-owned;
- qcom,prio = <0>;
};
mas_qhm_qup2: mas-qhm-qup2 {
@@ -583,12 +555,9 @@
label = "mas-qhm-qup2";
qcom,buswidth = <4>;
qcom,agg-ports = <1>;
- qcom,qport = <0>;
qcom,connections = <&slv_qns_a2noc_snoc>;
qcom,bus-dev = <&fab_aggre2_noc>;
qcom,bcms = <&bcm_qup0>;
- qcom,ap-owned;
- qcom,prio = <0>;
};
mas_qnm_cnoc: mas-qnm-cnoc {
@@ -626,6 +595,8 @@
qcom,bus-dev = <&fab_aggre2_noc>;
qcom,ap-owned;
qcom,prio = <2>;
+ qcom,defer-init-qos;
+ qcom,node-qos-bcms = <7035 0 1>;
};
mas_xm_qdss_etr: mas-xm-qdss-etr {
@@ -650,6 +621,12 @@
qcom,bus-dev = <&fab_aggre2_noc>;
qcom,ap-owned;
qcom,prio = <2>;
+ qcom,node-qos-clks {
+ clocks =
+ <&clock_gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>;
+ clock-names =
+ "clk-usb3-prim-axi-no-rate";
+ };
};
mas_qxm_camnoc_hf0_uncomp: mas-qxm-camnoc-hf0-uncomp {
@@ -837,7 +814,7 @@
qcom,bus-dev = <&fab_mem_noc>;
qcom,bcms = <&bcm_sh3>;
qcom,ap-owned;
- qcom,prio = <6>;
+ qcom,prio = <7>;
};
mas_qhm_memnoc_cfg: mas-qhm-memnoc-cfg {
@@ -874,6 +851,7 @@
qcom,ap-owned;
qcom,prio = <0>;
qcom,forwarding;
+ qcom,node-qos-bcms = <7012 0 1>;
};
mas_qnm_mnoc_sf: mas-qnm-mnoc-sf {
@@ -888,6 +866,7 @@
qcom,ap-owned;
qcom,prio = <0>;
qcom,forwarding;
+ qcom,node-qos-bcms = <7012 0 1>;
};
mas_qnm_snoc_gc: mas-qnm-snoc-gc {
@@ -950,6 +929,7 @@
qcom,ap-owned;
qcom,prio = <0>;
qcom,forwarding;
+ qcom,node-qos-bcms = <7012 0 1>;
};
mas_qxm_camnoc_hf1: mas-qxm-camnoc-hf1 {
@@ -964,6 +944,7 @@
qcom,ap-owned;
qcom,prio = <0>;
qcom,forwarding;
+ qcom,node-qos-bcms = <7012 0 1>;
};
mas_qxm_camnoc_sf: mas-qxm-camnoc-sf {
@@ -978,6 +959,7 @@
qcom,ap-owned;
qcom,prio = <0>;
qcom,forwarding;
+ qcom,node-qos-bcms = <7012 0 1>;
};
mas_qxm_mdp0: mas-qxm-mdp0 {
@@ -992,6 +974,7 @@
qcom,ap-owned;
qcom,prio = <0>;
qcom,forwarding;
+ qcom,node-qos-bcms = <7012 0 1>;
};
mas_qxm_mdp1: mas-qxm-mdp1 {
@@ -1006,6 +989,7 @@
qcom,ap-owned;
qcom,prio = <0>;
qcom,forwarding;
+ qcom,node-qos-bcms = <7012 0 1>;
};
mas_qxm_rot: mas-qxm-rot {
@@ -1020,6 +1004,7 @@
qcom,ap-owned;
qcom,prio = <0>;
qcom,forwarding;
+ qcom,node-qos-bcms = <7012 0 1>;
};
mas_qxm_venus0: mas-qxm-venus0 {
@@ -1034,6 +1019,7 @@
qcom,ap-owned;
qcom,prio = <0>;
qcom,forwarding;
+ qcom,node-qos-bcms = <7012 0 1>;
};
mas_qxm_venus1: mas-qxm-venus1 {
@@ -1048,6 +1034,7 @@
qcom,ap-owned;
qcom,prio = <0>;
qcom,forwarding;
+ qcom,node-qos-bcms = <7012 0 1>;
};
mas_qxm_venus_arm9: mas-qxm-venus-arm9 {
@@ -1062,6 +1049,7 @@
qcom,ap-owned;
qcom,prio = <0>;
qcom,forwarding;
+ qcom,node-qos-bcms = <7012 0 1>;
};
mas_qhm_snoc_cfg: mas-qhm-snoc-cfg {
diff --git a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
index 47ec472..163420a 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
@@ -54,7 +54,27 @@
};
&qupv3_se3_i2c {
- status = "disabled";
+ status = "ok";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 44 0x00>;
+ qcom,nq-ven = <&tlmm 12 0x00>;
+ qcom,nq-firm = <&tlmm 43 0x00>;
+ qcom,nq-clkreq = <&pm660_gpios 4 0x00>;
+ qcom,nq-esepwr = <&tlmm 116 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK3";
+ interrupts = <44 0>;
+ interrupt-names = "nfc_irq";
+ pinctrl-names = "nfc_active", "nfc_suspend";
+ pinctrl-0 = <&nfc_int_active
+ &nfc_enable_active
+ &nfc_clk_default>;
+ pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>;
+ clocks = <&clock_rpmh RPMH_LN_BB_CLK3>;
+ clock-names = "ref_clk";
+ };
};
&qupv3_se10_i2c {
diff --git a/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi b/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
index d054164..980ade2 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
@@ -625,20 +625,20 @@
};
port@6 {
- reg = <10>;
- tpda_in_tpdm_qm: endpoint {
+ reg = <11>;
+ tpda_in_tpdm_north: endpoint {
slave-mode;
remote-endpoint =
- <&tpdm_qm_out_tpda>;
+ <&tpdm_north_out_tpda>;
};
};
port@7 {
- reg = <11>;
- tpda_in_tpdm_north: endpoint {
+ reg = <12>;
+ tpda_in_tpdm_qm: endpoint {
slave-mode;
remote-endpoint =
- <&tpdm_north_out_tpda>;
+ <&tpdm_qm_out_tpda>;
};
};
@@ -1995,7 +1995,7 @@
};
port@2 {
- reg = <1>;
+ reg = <2>;
funnel_apss_merg_in_tpda_olc: endpoint {
slave-mode;
remote-endpoint =
@@ -2004,7 +2004,7 @@
};
port@3 {
- reg = <3>;
+ reg = <4>;
funnel_apss_merg_in_tpda_apss: endpoint {
slave-mode;
remote-endpoint =
diff --git a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
index ab137cd..307444d 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
@@ -55,7 +55,27 @@
};
&qupv3_se3_i2c {
- status = "disabled";
+ status = "ok";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 44 0x00>;
+ qcom,nq-ven = <&tlmm 12 0x00>;
+ qcom,nq-firm = <&tlmm 43 0x00>;
+ qcom,nq-clkreq = <&pm660_gpios 4 0x00>;
+ qcom,nq-esepwr = <&tlmm 116 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK3";
+ interrupts = <44 0>;
+ interrupt-names = "nfc_irq";
+ pinctrl-names = "nfc_active", "nfc_suspend";
+ pinctrl-0 = <&nfc_int_active
+ &nfc_enable_active
+ &nfc_clk_default>;
+ pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>;
+ clocks = <&clock_rpmh RPMH_LN_BB_CLK3>;
+ clock-names = "ref_clk";
+ };
};
&qupv3_se10_i2c {
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
index 17b61a0..188da58 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
@@ -908,7 +908,7 @@
qupv3_se12_2uart_active: qupv3_se12_2uart_active {
mux {
pins = "gpio51", "gpio52";
- function = "qup9";
+ function = "qup12";
};
config {
@@ -1371,6 +1371,70 @@
};
};
+ nfc {
+ nfc_int_active: nfc_int_active {
+ /* active state */
+ mux {
+ /* GPIO 44 NFC Read Interrupt */
+ pins = "gpio44";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio44";
+ drive-strength = <2>; /* 2 MA */
+ bias-pull-up;
+ };
+ };
+
+ nfc_int_suspend: nfc_int_suspend {
+ /* sleep state */
+ mux {
+ /* GPIO 44 NFC Read Interrupt */
+ pins = "gpio44";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio44";
+ drive-strength = <2>; /* 2 MA */
+ bias-pull-up;
+ };
+ };
+
+ nfc_enable_active: nfc_enable_active {
+ /* active state */
+ mux {
+ /* 12: NFC ENABLE 43: FW DNLD */
+ /* 116: ESE Enable */
+ pins = "gpio12", "gpio43", "gpio116";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio12", "gpio43", "gpio116";
+ drive-strength = <2>; /* 2 MA */
+ bias-pull-up;
+ };
+ };
+
+ nfc_enable_suspend: nfc_enable_suspend {
+ /* sleep state */
+ mux {
+ /* 12: NFC ENABLE 43: FW DNLD */
+ /* 116: ESE Enable */
+ pins = "gpio12", "gpio43", "gpio116";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio12", "gpio43", "gpio116";
+ drive-strength = <2>; /* 2 MA */
+ bias-disable;
+ };
+ };
+ };
+
/* WSA speaker reset pins */
spkr_1_sd_n {
spkr_1_sd_n_sleep: spkr_1_sd_n_sleep {
@@ -1926,6 +1990,14 @@
};
&pm660_gpios {
+ nfc_clk {
+ nfc_clk_default: nfc_clk_default {
+ pins = "gpio4";
+ function = "normal";
+ input-enable;
+ power-source = <1>;
+ };
+ };
smb_shutdown_default: smb_shutdown_default {
pins = "gpio11";
function = "normal";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
index c39978e..220487a 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
@@ -35,6 +35,7 @@
qcom,thermal-mitigation
= <3000000 2500000 2000000 1500000
1000000 500000>;
+ qcom,auto-recharge-soc;
qcom,chgr@1000 {
reg = <0x1000 0x100>;
@@ -178,6 +179,9 @@
qcom,fg-esr-timer-asleep = <256 256>;
qcom,fg-esr-timer-charging = <0 96>;
qcom,cycle-counter-en;
+ qcom,hold-soc-while-full;
+ qcom,fg-auto-recharge-soc;
+ qcom,fg-recharge-soc-thr = <98>;
status = "okay";
qcom,fg-batt-soc@4000 {
@@ -376,5 +380,5 @@
};
&usb0 {
- extcon = <&pm660_pdphy>, <&pm660_pdphy>, <0> /* <&eud> */;
+ extcon = <&pm660_pdphy>, <&pm660_pdphy>, <&eud>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
index 1925989..96b8cfb 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
@@ -28,7 +28,27 @@
};
&qupv3_se3_i2c {
- status = "disabled";
+ status = "ok";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 44 0x00>;
+ qcom,nq-ven = <&tlmm 12 0x00>;
+ qcom,nq-firm = <&tlmm 43 0x00>;
+ qcom,nq-clkreq = <&pm660_gpios 4 0x00>;
+ qcom,nq-esepwr = <&tlmm 116 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK3";
+ interrupts = <44 0>;
+ interrupt-names = "nfc_irq";
+ pinctrl-names = "nfc_active", "nfc_suspend";
+ pinctrl-0 = <&nfc_int_active
+ &nfc_enable_active
+ &nfc_clk_default>;
+ pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>;
+ clocks = <&clock_rpmh RPMH_LN_BB_CLK3>;
+ clock-names = "ref_clk";
+ };
};
&qupv3_se10_i2c {
@@ -123,7 +143,7 @@
};
&int_codec {
- qcom,model = "sdm660-skuw-snd-card";
+ qcom,model = "sdm670-skuw-snd-card";
qcom,audio-routing =
"RX_BIAS", "INT_MCLK0",
"SPK_RX_BIAS", "INT_MCLK0",
@@ -145,3 +165,39 @@
qcom,wsa-devs = <&wsa881x_211_en>, <&wsa881x_213_en>;
qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrLeft";
};
+
+&sdhc_1 {
+ vdd-supply = <&pm660l_l4>;
+ qcom,vdd-voltage-level = <2960000 2960000>;
+ qcom,vdd-current-level = <200 570000>;
+
+ vdd-io-supply = <&pm660_l8>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-lpm-sup;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <200 325000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on &sdc1_rclk_on>;
+ pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off &sdc1_rclk_off>;
+
+ status = "ok";
+};
+
+&sdhc_2 {
+ vdd-supply = <&pm660l_l5>;
+ qcom,vdd-voltage-level = <2960000 2960000>;
+ qcom,vdd-current-level = <200 800000>;
+
+ vdd-io-supply = <&pm660l_l2>;
+ qcom,vdd-io-voltage-level = <1800000 2960000>;
+ qcom,vdd-io-current-level = <200 22000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off>;
+
+ cd-gpios = <&tlmm 96 0>;
+
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
index 2a61e18..53213f8 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
@@ -482,109 +482,12 @@
compatible = "qcom,msm-ext-disp-audio-codec-rx";
};
};
-
- sde_dp: qcom,dp_display@0{
- cell-index = <0>;
- compatible = "qcom,dp-display";
-
- gdsc-supply = <&mdss_core_gdsc>;
- vdda-1p2-supply = <&pm660_l1>;
- vdda-0p9-supply = <&pm660l_l1>;
-
- reg = <0xae90000 0xa84>,
- <0x88eaa00 0x200>,
- <0x88ea200 0x200>,
- <0x88ea600 0x200>,
- <0xaf02000 0x1a0>,
- <0x780000 0x621c>,
- <0x88ea030 0x10>,
- <0x88e8000 0x20>,
- <0x0aee1000 0x034>;
- reg-names = "dp_ctrl", "dp_phy", "dp_ln_tx0", "dp_ln_tx1",
- "dp_mmss_cc", "qfprom_physical", "dp_pll",
- "usb3_dp_com", "hdcp_physical";
-
- interrupt-parent = <&mdss_mdp>;
- interrupts = <12 0>;
-
- clocks = <&clock_dispcc DISP_CC_MDSS_DP_AUX_CLK>,
- <&clock_rpmh RPMH_CXO_CLK>,
- <&clock_gcc GCC_USB3_PRIM_CLKREF_CLK>,
- <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
- <&clock_gcc GCC_USB3_PRIM_PHY_PIPE_CLK>,
- <&clock_dispcc DISP_CC_MDSS_DP_LINK_CLK>,
- <&clock_dispcc DISP_CC_MDSS_DP_LINK_INTF_CLK>,
- <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK>,
- <&clock_dispcc DISP_CC_MDSS_DP_CRYPTO_CLK>,
- <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK_SRC>,
- <&mdss_dp_pll DP_VCO_DIVIDED_CLK_SRC_MUX>;
- clock-names = "core_aux_clk", "core_usb_ref_clk_src",
- "core_usb_ref_clk", "core_usb_cfg_ahb_clk",
- "core_usb_pipe_clk", "ctrl_link_clk",
- "ctrl_link_iface_clk", "ctrl_pixel_clk",
- "crypto_clk", "pixel_clk_rcg", "pixel_parent";
-
- qcom,dp-usbpd-detection = <&pm660_pdphy>;
- qcom,ext-disp = <&ext_disp>;
-
- qcom,aux-cfg0-settings = [20 00];
- qcom,aux-cfg1-settings = [24 13 23 1d];
- qcom,aux-cfg2-settings = [28 24];
- qcom,aux-cfg3-settings = [2c 00];
- qcom,aux-cfg4-settings = [30 0a];
- qcom,aux-cfg5-settings = [34 26];
- qcom,aux-cfg6-settings = [38 0a];
- qcom,aux-cfg7-settings = [3c 03];
- qcom,aux-cfg8-settings = [40 bb];
- qcom,aux-cfg9-settings = [44 03];
-
- qcom,max-pclk-frequency-khz = <675000>;
-
- qcom,core-supply-entries {
- #address-cells = <1>;
- #size-cells = <0>;
-
- qcom,core-supply-entry@0 {
- reg = <0>;
- qcom,supply-name = "gdsc";
- qcom,supply-min-voltage = <0>;
- qcom,supply-max-voltage = <0>;
- qcom,supply-enable-load = <0>;
- qcom,supply-disable-load = <0>;
- };
- };
-
- qcom,ctrl-supply-entries {
- #address-cells = <1>;
- #size-cells = <0>;
-
- qcom,ctrl-supply-entry@0 {
- reg = <0>;
- qcom,supply-name = "vdda-1p2";
- qcom,supply-min-voltage = <1200000>;
- qcom,supply-max-voltage = <1200000>;
- qcom,supply-enable-load = <21800>;
- qcom,supply-disable-load = <4>;
- };
- };
-
- qcom,phy-supply-entries {
- #address-cells = <1>;
- #size-cells = <0>;
-
- qcom,phy-supply-entry@0 {
- reg = <0>;
- qcom,supply-name = "vdda-0p9";
- qcom,supply-min-voltage = <880000>;
- qcom,supply-max-voltage = <880000>;
- qcom,supply-enable-load = <36000>;
- qcom,supply-disable-load = <32>;
- };
- };
- };
};
&sde_dp {
+ qcom,dp-usbpd-detection = <&pm660_pdphy>;
+ qcom,ext-disp = <&ext_disp>;
+
pinctrl-names = "mdss_dp_active", "mdss_dp_sleep";
pinctrl-0 = <&sde_dp_aux_active &sde_dp_usbplug_cc_active>;
pinctrl-1 = <&sde_dp_aux_suspend &sde_dp_usbplug_cc_suspend>;
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi
index 2878184..babe591e 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi
@@ -9,6 +9,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
+#include <dt-bindings/clock/mdss-10nm-pll-clk.h>
&soc {
mdss_mdp: qcom,mdss_mdp@ae00000 {
@@ -45,6 +46,8 @@
#address-cells = <1>;
#size-cells = <0>;
+ #power-domain-cells = <0>;
+
/* hw blocks */
qcom,sde-off = <0x1000>;
qcom,sde-len = <0x45C>;
@@ -52,10 +55,13 @@
qcom,sde-ctl-off = <0x2000 0x2200 0x2400
0x2600 0x2800>;
qcom,sde-ctl-size = <0xE4>;
+ qcom,sde-ctl-display-pref = "primary", "primary", "none",
+ "none", "none";
- qcom,sde-mixer-off = <0x45000 0x46000 0x47000
- 0x48000 0x49000 0x4a000>;
+ qcom,sde-mixer-off = <0x45000 0x46000 0x47000 0x0 0x0 0x4a000>;
qcom,sde-mixer-size = <0x320>;
+ qcom,sde-mixer-display-pref = "primary", "primary", "none",
+ "none", "none", "none";
qcom,sde-dspp-top-off = <0x1300>;
qcom,sde-dspp-top-size = <0xc>;
@@ -354,6 +360,8 @@
interrupt-parent = <&mdss_mdp>;
interrupts = <2 0>;
+ power-domains = <&mdss_mdp>;
+
/* Offline rotator QoS setting */
qcom,mdss-rot-vbif-qos-setting = <3 3 3 3 3 3 3 3>;
qcom,mdss-rot-vbif-memtype = <3 3>;
@@ -533,4 +541,85 @@
};
};
+ sde_dp: qcom,dp_display@0{
+ cell-index = <0>;
+ compatible = "qcom,dp-display";
+
+ vdda-1p2-supply = <&pm660_l1>;
+ vdda-0p9-supply = <&pm660l_l1>;
+
+ reg = <0xae90000 0xa84>,
+ <0x88eaa00 0x200>,
+ <0x88ea200 0x200>,
+ <0x88ea600 0x200>,
+ <0xaf02000 0x1a0>,
+ <0x780000 0x621c>,
+ <0x88ea030 0x10>,
+ <0x88e8000 0x20>,
+ <0x0aee1000 0x034>;
+ reg-names = "dp_ctrl", "dp_phy", "dp_ln_tx0", "dp_ln_tx1",
+ "dp_mmss_cc", "qfprom_physical", "dp_pll",
+ "usb3_dp_com", "hdcp_physical";
+
+ interrupt-parent = <&mdss_mdp>;
+ interrupts = <12 0>;
+
+ clocks = <&clock_dispcc DISP_CC_MDSS_DP_AUX_CLK>,
+ <&clock_rpmh RPMH_CXO_CLK>,
+ <&clock_gcc GCC_USB3_PRIM_CLKREF_CLK>,
+ <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
+ <&clock_gcc GCC_USB3_PRIM_PHY_PIPE_CLK>,
+ <&clock_dispcc DISP_CC_MDSS_DP_LINK_CLK>,
+ <&clock_dispcc DISP_CC_MDSS_DP_LINK_INTF_CLK>,
+ <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK>,
+ <&clock_dispcc DISP_CC_MDSS_DP_CRYPTO_CLK>,
+ <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK_SRC>,
+ <&mdss_dp_pll DP_VCO_DIVIDED_CLK_SRC_MUX>;
+ clock-names = "core_aux_clk", "core_usb_ref_clk_src",
+ "core_usb_ref_clk", "core_usb_cfg_ahb_clk",
+ "core_usb_pipe_clk", "ctrl_link_clk",
+ "ctrl_link_iface_clk", "ctrl_pixel_clk",
+ "crypto_clk", "pixel_clk_rcg", "pixel_parent";
+
+ qcom,aux-cfg0-settings = [20 00];
+ qcom,aux-cfg1-settings = [24 13 23 1d];
+ qcom,aux-cfg2-settings = [28 24];
+ qcom,aux-cfg3-settings = [2c 00];
+ qcom,aux-cfg4-settings = [30 0a];
+ qcom,aux-cfg5-settings = [34 26];
+ qcom,aux-cfg6-settings = [38 0a];
+ qcom,aux-cfg7-settings = [3c 03];
+ qcom,aux-cfg8-settings = [40 bb];
+ qcom,aux-cfg9-settings = [44 03];
+
+ qcom,max-pclk-frequency-khz = <675000>;
+
+ qcom,ctrl-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,ctrl-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdda-1p2";
+ qcom,supply-min-voltage = <1200000>;
+ qcom,supply-max-voltage = <1200000>;
+ qcom,supply-enable-load = <21800>;
+ qcom,supply-disable-load = <4>;
+ };
+ };
+
+ qcom,phy-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,phy-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdda-0p9";
+ qcom,supply-min-voltage = <880000>;
+ qcom,supply-max-voltage = <880000>;
+ qcom,supply-enable-load = <36000>;
+ qcom,supply-disable-load = <32>;
+ };
+ };
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi b/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi
index a74f9d8..01d4057 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi
@@ -139,7 +139,7 @@
"bus_clk", "core0_clk", "core0_bus_clk";
qcom,clock-configs = <0x1 0x0 0x0 0x1 0x0>;
qcom,allowed-clock-rates = <100000000 200000000 320000000
- 364800000>;
+ 364700000>;
/* Buses */
bus_cnoc {
diff --git a/arch/arm64/boot/dts/qcom/sdm670.dtsi b/arch/arm64/boot/dts/qcom/sdm670.dtsi
index 3109a3c..ed15959 100644
--- a/arch/arm64/boot/dts/qcom/sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670.dtsi
@@ -347,23 +347,14 @@
CPU_COST_0: core-cost0 {
busy-cost-data = <
300000 14
- 403200 18
- 480000 21
576000 25
- 652800 27
748800 31
- 825600 40
- 902400 43
- 979200 46
- 1056000 50
- 1132800 53
- 1228800 57
+ 998400 46
+ 1209600 57
1324800 84
- 1420800 90
1516800 96
1612800 114
- 1689600 135
- 1766400 141
+ 1708000 139
>;
idle-cost-data = <
12 10 8 6
@@ -372,32 +363,23 @@
CPU_COST_1: core-cost1 {
busy-cost-data = <
300000 256
- 403200 271
- 480000 282
- 576000 296
652800 307
- 748800 321
825600 332
- 902400 369
979200 382
- 1056000 395
1132800 408
- 1209600 421
- 1286400 434
1363200 448
- 1459200 567
- 1536000 586
- 1612800 604
- 1689600 622
- 1766400 641
+ 1563000 586
+ 1747200 641
1843200 659
- 1920000 678
1996800 696
- 2092800 876
+ 2054400 876
2169600 900
- 2246400 924
- 2323200 948
+ 2208000 924
+ 2361600 948
2400000 1170
+ 2457600 1200
+ 2515200 1300
+ 2611200 1400
>;
idle-cost-data = <
100 80 60 40
@@ -406,23 +388,14 @@
CLUSTER_COST_0: cluster-cost0 {
busy-cost-data = <
300000 5
- 403200 7
- 480000 7
576000 7
- 652800 8
748800 8
- 825600 9
- 902400 9
- 979200 9
- 1056000 10
- 1132800 10
- 1228800 10
+ 998400 9
+ 1209600 10
1324800 13
- 1420800 14
1516800 15
1612800 16
- 1689600 19
- 1766400 19
+ 1708000 19
>;
idle-cost-data = <
4 3 2 1
@@ -431,32 +404,23 @@
CLUSTER_COST_1: cluster-cost1 {
busy-cost-data = <
300000 25
- 403200 27
- 480000 28
- 576000 29
652800 30
- 748800 32
825600 33
- 902400 36
979200 38
- 1056000 39
1132800 40
- 1209600 42
- 1286400 43
1363200 44
- 1459200 56
- 1536000 58
- 1612800 60
- 1689600 62
- 1766400 64
+ 1563000 58
+ 1747200 64
1843200 65
- 1920000 67
1996800 69
- 2092800 87
+ 2054400 87
2169600 90
- 2246400 92
- 2323200 94
+ 2208000 92
+ 2361600 94
2400000 117
+ 2457600 120
+ 2515200 130
+ 2611200 140
>;
idle-cost-data = <
4 3 2 1
@@ -489,7 +453,7 @@
dev = "/dev/block/platform/soc/1d84000.ufshc/by-name/vendor";
type = "ext4";
mnt_flags = "ro,barrier=1,discard";
- fsmgr_flags = "wait,slotselect";
+ fsmgr_flags = "wait,slotselect,avb";
};
};
};
@@ -1620,7 +1584,8 @@
interrupts = <GIC_SPI 492 IRQ_TYPE_LEVEL_HIGH>;
reg = <0x88e0000 0x2000>;
reg-names = "eud_base";
- status = "disabled";
+ clocks = <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>;
+ clock-names = "cfg_ahb_clk";
};
qcom,llcc@1100000 {
@@ -1892,7 +1857,6 @@
qcom,ipa-wdi2;
qcom,use-64-bit-dma-mask;
qcom,arm-smmu;
- qcom,smmu-s1-bypass;
qcom,bandwidth-vote-for-ipa;
qcom,msm-bus,name = "ipa";
qcom,msm-bus,num-cases = <4>;
@@ -2009,17 +1973,20 @@
ipa_smmu_ap: ipa_smmu_ap {
compatible = "qcom,ipa-smmu-ap-cb";
+ qcom,smmu-s1-bypass;
iommus = <&apps_smmu 0x720 0x0>;
qcom,iova-mapping = <0x20000000 0x40000000>;
};
ipa_smmu_wlan: ipa_smmu_wlan {
compatible = "qcom,ipa-smmu-wlan-cb";
+ qcom,smmu-s1-bypass;
iommus = <&apps_smmu 0x721 0x0>;
};
ipa_smmu_uc: ipa_smmu_uc {
compatible = "qcom,ipa-smmu-uc-cb";
+ qcom,smmu-s1-bypass;
iommus = <&apps_smmu 0x722 0x0>;
qcom,iova-mapping = <0x40000000 0x20000000>;
};
@@ -2180,31 +2147,31 @@
qcom,msm-bus,num-paths = <2>;
qcom,msm-bus,vectors-KBps =
/* No vote */
- <150 512 0 0>, <1 606 0 0>,
+ <150 512 0 0>, <1 782 0 0>,
/* 400 KB/s*/
<150 512 1046 1600>,
- <1 606 1600 1600>,
+ <1 782 1600 1600>,
/* 20 MB/s */
<150 512 52286 80000>,
- <1 606 80000 80000>,
+ <1 782 80000 80000>,
/* 25 MB/s */
<150 512 65360 100000>,
- <1 606 100000 100000>,
+ <1 782 100000 100000>,
/* 50 MB/s */
<150 512 130718 200000>,
- <1 606 133320 133320>,
+ <1 782 133320 133320>,
/* 100 MB/s */
<150 512 130718 200000>,
- <1 606 150000 150000>,
+ <1 782 150000 150000>,
/* 200 MB/s */
<150 512 261438 400000>,
- <1 606 300000 300000>,
+ <1 782 300000 300000>,
/* 400 MB/s */
<150 512 261438 400000>,
- <1 606 300000 300000>,
+ <1 782 300000 300000>,
/* Max. bandwidth */
<150 512 1338562 4096000>,
- <1 606 1338562 4096000>;
+ <1 782 1338562 4096000>;
qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
100000000 200000000 400000000 4294967295>;
@@ -2503,8 +2470,8 @@
< 748800 MHZ_TO_MBPS( 300, 4) >,
< 998400 MHZ_TO_MBPS( 451, 4) >,
< 1209600 MHZ_TO_MBPS( 547, 4) >,
- < 1497600 MHZ_TO_MBPS( 768, 4) >,
- < 1728000 MHZ_TO_MBPS(1017, 4) >;
+ < 1516800 MHZ_TO_MBPS( 768, 4) >,
+ < 1708000 MHZ_TO_MBPS(1017, 4) >;
};
devfreq_memlat_6: qcom,cpu6-memlat-mon {
@@ -2513,11 +2480,11 @@
qcom,target-dev = <&memlat_cpu6>;
qcom,cachemiss-ev = <0x2a>;
qcom,core-dev-table =
- < 787200 MHZ_TO_MBPS( 300, 4) >,
- < 1113600 MHZ_TO_MBPS( 547, 4) >,
- < 1344000 MHZ_TO_MBPS(1017, 4) >,
- < 1900800 MHZ_TO_MBPS(1555, 4) >,
- < 2438400 MHZ_TO_MBPS(1804, 4) >;
+ < 825600 MHZ_TO_MBPS( 300, 4) >,
+ < 1132800 MHZ_TO_MBPS( 547, 4) >,
+ < 1363200 MHZ_TO_MBPS(1017, 4) >,
+ < 1996800 MHZ_TO_MBPS(1555, 4) >,
+ < 2457600 MHZ_TO_MBPS(1804, 4) >;
};
l3_cpu0: qcom,l3-cpu0 {
@@ -2540,12 +2507,12 @@
qcom,target-dev = <&l3_cpu0>;
qcom,cachemiss-ev = <0x17>;
qcom,core-dev-table =
- < 748800 566400000 >,
- < 998400 787200000 >,
+ < 748800 556800000 >,
+ < 998400 806400000 >,
< 1209660 940800000 >,
- < 1497600 1190400000 >,
+ < 1516800 1190400000 >,
< 1612800 1382400000 >,
- < 1728000 1440000000 >;
+ < 1708000 1440000000 >;
};
devfreq_l3lat_6: qcom,cpu6-l3lat-mon {
@@ -2554,11 +2521,11 @@
qcom,target-dev = <&l3_cpu6>;
qcom,cachemiss-ev = <0x17>;
qcom,core-dev-table =
- < 1113600 566400000 >,
- < 1344000 787200000 >,
- < 1728000 940800000 >,
- < 1900800 1190400000 >,
- < 2438400 1440000000 >;
+ < 1132800 556800000 >,
+ < 1363200 806400000 >,
+ < 1747200 940800000 >,
+ < 1996800 1190400000 >,
+ < 2457600 1440000000 >;
};
mincpubw: qcom,mincpubw {
@@ -2585,15 +2552,15 @@
target-dev = <&mincpubw>;
cpu-to-dev-map-0 =
< 748800 MHZ_TO_MBPS( 300, 4) >,
- < 1209600 MHZ_TO_MBPS( 451, 4) >,
- < 1612000 MHZ_TO_MBPS( 547, 4) >,
- < 1728000 MHZ_TO_MBPS( 768, 4) >;
+ < 1209660 MHZ_TO_MBPS( 451, 4) >,
+ < 1612800 MHZ_TO_MBPS( 547, 4) >,
+ < 1708000 MHZ_TO_MBPS( 768, 4) >;
cpu-to-dev-map-6 =
- < 1113600 MHZ_TO_MBPS( 300, 4) >,
- < 1344000 MHZ_TO_MBPS( 547, 4) >,
- < 1728000 MHZ_TO_MBPS( 768, 4) >,
- < 1900800 MHZ_TO_MBPS(1017, 4) >,
- < 2438400 MHZ_TO_MBPS(1804, 4) >;
+ < 1132800 MHZ_TO_MBPS( 300, 4) >,
+ < 1363200 MHZ_TO_MBPS( 547, 4) >,
+ < 1747200 MHZ_TO_MBPS( 768, 4) >,
+ < 1996800 MHZ_TO_MBPS(1017, 4) >,
+ < 2457600 MHZ_TO_MBPS(1804, 4) >;
};
};
@@ -2743,6 +2710,22 @@
};
};
+&sde_dp {
+ qcom,core-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,core-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "refgen";
+ qcom,supply-min-voltage = <0>;
+ qcom,supply-max-voltage = <0>;
+ qcom,supply-enable-load = <0>;
+ qcom,supply-disable-load = <0>;
+ };
+ };
+};
+
#include "sdm670-audio.dtsi"
#include "sdm670-usb.dtsi"
#include "sdm670-gpu.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dtsi
index 1265d2a..9313a75 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dtsi
@@ -37,3 +37,7 @@
&pcie0 {
status = "disabled";
};
+
+&eud {
+ vdda33-supply = <&pm660l_l7>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dtsi
index 9d722df..e7ff910 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dtsi
@@ -42,3 +42,7 @@
&pcie0 {
status = "disabled";
};
+
+&eud {
+ vdda33-supply = <&pm660l_l7>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-audio-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qvr-audio-overlay.dtsi
new file mode 100644
index 0000000..77a89f0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-qvr-audio-overlay.dtsi
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdm845-audio-overlay.dtsi"
+
+&snd_934x {
+ qcom,model = "sdm845-qvr-tavil-snd-card";
+
+ qcom,audio-routing =
+ "AIF4 VI", "MCLK",
+ "RX_BIAS", "MCLK",
+ "MADINPUT", "MCLK",
+ "AMIC1", "Handset Mic",
+ "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",
+ "DMIC4", "MIC BIAS4",
+ "MIC BIAS4", "Digital Mic4",
+ "DMIC5", "MIC BIAS4",
+ "MIC BIAS4", "Digital Mic5",
+ "SpkrLeft IN", "SPK1 OUT";
+
+ qcom,msm-mbhc-hphl-swh = <0>;
+ /delete-property/ qcom,hph-en0-gpio;
+ /delete-property/ qcom,hph-en1-gpio;
+ /delete-property/ qcom,usbc-analog-en1-gpio;
+ /delete-property/ qcom,usbc-analog-en2-gpio;
+ /delete-property/ pinctrl-names;
+ /delete-property/ pinctrl-0;
+ /delete-property/ pinctrl-1;
+
+ qcom,wsa-max-devs = <1>;
+ qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0213>;
+ qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrLeft";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
index fb99157..b1b81d1 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
@@ -21,6 +21,7 @@
#include "sdm845-sde-display.dtsi"
#include "sdm845-qvr.dtsi"
+#include "sdm845-qvr-audio-overlay.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi
index 4254fcd..4f70e411 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi
@@ -518,6 +518,7 @@
&dsi_nt35597_truly_dsc_cmd {
qcom,mdss-dsi-t-clk-post = <0x0b>;
qcom,mdss-dsi-t-clk-pre = <0x23>;
+ qcom,ulps-enabled;
qcom,mdss-dsi-display-timings {
timing@0{
qcom,mdss-dsi-panel-phy-timings = [00 15 05 05 20 1f 05
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde-pll.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde-pll.dtsi
index b9eac3c..967865b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde-pll.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde-pll.dtsi
@@ -23,6 +23,8 @@
clocks = <&clock_dispcc DISP_CC_MDSS_AHB_CLK>;
clock-names = "iface_clk";
clock-rate = <0>;
+ qcom,dsi-pll-ssc-en;
+ qcom,dsi-pll-ssc-mode = "down-spread";
gdsc-supply = <&mdss_core_gdsc>;
qcom,platform-supply-entries {
#address-cells = <1>;
@@ -50,6 +52,8 @@
clocks = <&clock_dispcc DISP_CC_MDSS_AHB_CLK>;
clock-names = "iface_clk";
clock-rate = <0>;
+ qcom,dsi-pll-ssc-en;
+ qcom,dsi-pll-ssc-mode = "down-spread";
gdsc-supply = <&mdss_core_gdsc>;
qcom,platform-supply-entries {
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
index 7c8eab4..0dfe7ae 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
@@ -202,6 +202,9 @@
qcom,sde-cdp-setting = <1 1>, <1 0>;
+ qcom,sde-qos-cpu-mask = <0x3>;
+ qcom,sde-qos-cpu-dma-latency = <300>;
+
qcom,sde-inline-rotator = <&mdss_rotator 0>;
qcom,sde-inline-rot-xin = <10 11>;
qcom,sde-inline-rot-xin-type = "sspp", "wb";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
index ba397e5..3541a1c 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
@@ -118,9 +118,8 @@
qusb_phy0: qusb@88e2000 {
compatible = "qcom,qusb2phy-v2";
reg = <0x088e2000 0x400>,
- <0x007801e8 0x4>,
- <0x088e0000 0x2000>;
- reg-names = "qusb_phy_base", "efuse_addr", "eud_base";
+ <0x007801e8 0x4>;
+ reg-names = "qusb_phy_base", "efuse_addr";
qcom,efuse-bit-pos = <25>;
qcom,efuse-num-bits = <3>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.1.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dtsi
index ff8c01a..b298272 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2.1.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dtsi
@@ -25,3 +25,7 @@
&clock_gcc {
compatible = "qcom,gcc-sdm845-v2.1", "syscon";
};
+
+&apps_smmu {
+ /delete-property/ qcom,no-asid-retention;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
index cfa4517..360d113 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
@@ -432,6 +432,9 @@
2553600 12045
2649600 15686
2745600 25586
+ 2764800 30000
+ 2784000 35000
+ 2803200 40000
>;
idle-cost-data = <
100 80 60 40
@@ -495,6 +498,9 @@
2553600 145
2649600 150
2745600 155
+ 2764800 160
+ 2784000 165
+ 2803200 170
>;
idle-cost-data = <
4 3 2 1
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index a239ddd..86705d6 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -25,6 +25,7 @@
#include <dt-bindings/spmi/spmi.h>
#include <dt-bindings/thermal/thermal.h>
#include <dt-bindings/msm/msm-bus-ids.h>
+#include <dt-bindings/soc/qcom,dcc_v2.h>
#define MHZ_TO_MBPS(mhz, w) ((mhz * 1000000 * w) / (1024 * 1024))
@@ -1088,6 +1089,13 @@
< 1958400 1305600000 >;
};
+ l3_cdsp: qcom,l3-cdsp {
+ compatible = "devfreq-simple-dev";
+ clock-names = "devfreq_clk";
+ clocks = <&clock_cpucc L3_MISC_VOTE_CLK>;
+ governor = "powersave";
+ };
+
cpu_pmu: cpu-pmu {
compatible = "arm,armv8-pmuv3";
qcom,irq-is-percpu;
@@ -1215,7 +1223,7 @@
<0x17d45800 0x1400>;
reg-names = "osm_l3_base", "osm_pwrcl_base", "osm_perfcl_base";
- l3-devs = <&l3_cpu0 &l3_cpu4>;
+ l3-devs = <&l3_cpu0 &l3_cpu4 &l3_cdsp>;
clock-names = "xo_ao";
clocks = <&clock_rpmh RPMH_CXO_CLK_A>;
@@ -1647,6 +1655,7 @@
reg-names = "eud_base";
clocks = <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>;
clock-names = "cfg_ahb_clk";
+ vdda33-supply = <&pm8998_l24>;
status = "ok";
};
@@ -2785,6 +2794,214 @@
reg-names = "dcc-base", "dcc-ram-base";
dcc-ram-offset = <0x6000>;
+
+ qcom,curr-link-list = <2>;
+ qcom,link-list = <DCC_READ 0x1740300 6 0>,
+ <DCC_READ 0x1620500 4 0>,
+ <DCC_READ 0x7840000 1 0>,
+ <DCC_READ 0x7841010 12 0>,
+ <DCC_READ 0x7842000 16 0>,
+ <DCC_READ 0x7842500 2 0>,
+ <DCC_LOOP 7 0 0>,
+ <DCC_READ 0x7841000 1 0>,
+ <DCC_LOOP 1 0 0>,
+ <DCC_LOOP 165 0 0>,
+ <DCC_READ 0x7841008 2 0>,
+ <DCC_LOOP 1 0 0>,
+ <DCC_READ 0x17dc3a84 2 0>,
+ <DCC_READ 0x17db3a84 1 0>,
+ <DCC_READ 0x1301000 2 0>,
+ <DCC_READ 0x17990044 1 0>,
+ <DCC_READ 0x17d45f00 1 0>,
+ <DCC_READ 0x17d45f08 6 0>,
+ <DCC_READ 0x17d45f80 1 0>,
+ <DCC_READ 0x17d47418 1 0>,
+ <DCC_READ 0x17d47570 1 0>,
+ <DCC_READ 0x17d47588 1 0>,
+ <DCC_READ 0x17d43700 1 0>,
+ <DCC_READ 0x17d43708 6 0>,
+ <DCC_READ 0x17d43780 1 0>,
+ <DCC_READ 0x17d44c18 1 0>,
+ <DCC_READ 0x17d44d70 1 0>,
+ <DCC_READ 0x17d44d88 1 0>,
+ <DCC_READ 0x17d41700 1 0>,
+ <DCC_READ 0x17d41708 6 0>,
+ <DCC_READ 0x17d41780 1 0>,
+ <DCC_READ 0x17d42c18 1 0>,
+ <DCC_READ 0x17d42d70 1 0>,
+ <DCC_READ 0x17d42d88 1 0>,
+ <DCC_WRITE 0x69ea00c 0x600007 1>,
+ <DCC_WRITE 0x69ea01c 0x136800 1>,
+ <DCC_READ 0x69ea014 1 1>,
+ <DCC_WRITE 0x69ea01c 0x136810 1>,
+ <DCC_READ 0x69ea014 1 1>,
+ <DCC_WRITE 0x69ea01c 0x136820 1>,
+ <DCC_READ 0x69ea014 1 1>,
+ <DCC_WRITE 0x69ea01c 0x136830 1>,
+ <DCC_READ 0x69ea014 1 1>,
+ <DCC_WRITE 0x69ea01c 0x136840 1>,
+ <DCC_READ 0x69ea014 1 1>,
+ <DCC_WRITE 0x69ea01c 0x136850 1>,
+ <DCC_READ 0x69ea014 1 1>,
+ <DCC_WRITE 0x69ea01c 0x136860 1>,
+ <DCC_READ 0x69ea014 1 1>,
+ <DCC_WRITE 0x69ea01c 0x136870 1>,
+ <DCC_READ 0x69ea014 1 1>,
+ <DCC_WRITE 0x069ea01C 0x0003e9a0 1>,
+ <DCC_WRITE 0x069ea01C 0x001368a0 1>,
+ <DCC_READ 0x069ea014 1 1>,
+ <DCC_WRITE 0x069ea01c 0x0003c0a0 1>,
+ <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+ <DCC_READ 0x069ea014 1 1>,
+ <DCC_WRITE 0x069ea01c 0x0003d1a0 1>,
+ <DCC_WRITE 0x069ea01C 0x001368a0 1>,
+ <DCC_READ 0x069ea014 1 1>,
+ <DCC_WRITE 0x069ea01c 0x0003d2a0 1>,
+ <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+ <DCC_READ 0x069ea014 1 1>,
+ <DCC_WRITE 0x069ea01C 0x0003d5a0 1>,
+ <DCC_WRITE 0x069ea01C 0x001368a0 1>,
+ <DCC_READ 0x069ea014 1 1>,
+ <DCC_WRITE 0x069ea01C 0x0003d6a0 1>,
+ <DCC_WRITE 0x069ea01C 0x001368a0 1>,
+ <DCC_READ 0x069ea014 1 1>,
+ <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+ <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+ <DCC_READ 0x069ea014 1 1>,
+ <DCC_WRITE 0x069ea01c 0x0003b1a0 1>,
+ <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+ <DCC_READ 0x069ea014 1 1>,
+ <DCC_WRITE 0x069ea01c 0x0003b2a0 1>,
+ <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+ <DCC_READ 0x069ea014 1 1>,
+ <DCC_WRITE 0x069ea01c 0x0003b5a0 1>,
+ <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+ <DCC_READ 0x069ea014 1 1>,
+ <DCC_WRITE 0x069ea01c 0x0003b6a0 1>,
+ <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+ <DCC_READ 0x069ea014 1 1>,
+ <DCC_WRITE 0x069ea01c 0x0003c2a0 1>,
+ <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+ <DCC_READ 0x069ea014 1 1>,
+ <DCC_WRITE 0x069ea01c 0x0003c5a0 1>,
+ <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+ <DCC_READ 0x069ea014 1 1>,
+ <DCC_WRITE 0x069ea01c 0x0003c6a0 1>,
+ <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+ <DCC_READ 0x069ea014 1 1>,
+ <DCC_WRITE 0x069ea01c 0x00f1e000 1>,
+ <DCC_WRITE 0x069ea008 0x00000007 1>,
+ <DCC_READ 0x013e7e00 31 0>,
+ <DCC_READ 0x01132100 1 0>,
+ <DCC_READ 0x01136044 4 0>,
+ <DCC_READ 0x011360b0 1 0>,
+ <DCC_READ 0x0113e030 2 0>,
+ <DCC_READ 0x01141000 1 0>,
+ <DCC_READ 0x01142028 1 0>,
+ <DCC_READ 0x01148058 4 0>,
+ <DCC_READ 0x01160410 3 0>,
+ <DCC_READ 0x011604a0 1 0>,
+ <DCC_READ 0x011604b8 1 0>,
+ <DCC_READ 0x01165804 1 0>,
+ <DCC_READ 0x01166418 1 0>,
+ <DCC_READ 0x011b2100 1 0>,
+ <DCC_READ 0x011b6044 4 0>,
+ <DCC_READ 0x011be030 2 0>,
+ <DCC_READ 0x011c1000 1 0>,
+ <DCC_READ 0x011c2028 1 0>,
+ <DCC_READ 0x011c8058 4 0>,
+ <DCC_READ 0x011e0410 3 0>,
+ <DCC_READ 0x011e04a0 1 0>,
+ <DCC_READ 0x011e04b8 1 0>,
+ <DCC_READ 0x011e5804 1 0>,
+ <DCC_READ 0x011e6418 1 0>,
+ <DCC_READ 0x01232100 1 0>,
+ <DCC_READ 0x01236044 4 0>,
+ <DCC_READ 0x012360B0 1 0>,
+ <DCC_READ 0x0123E030 2 0>,
+ <DCC_READ 0x01241000 1 0>,
+ <DCC_READ 0x01242028 1 0>,
+ <DCC_READ 0x01248058 4 0>,
+ <DCC_READ 0x01260410 3 0>,
+ <DCC_READ 0x012604a0 1 0>,
+ <DCC_READ 0x012604b8 1 0>,
+ <DCC_READ 0x01265804 1 0>,
+ <DCC_READ 0x01266418 1 0>,
+ <DCC_READ 0x012b2100 1 0>,
+ <DCC_READ 0x012b6044 3 0>,
+ <DCC_READ 0x012b6050 1 0>,
+ <DCC_READ 0x012b60b0 1 0>,
+ <DCC_READ 0x012be030 2 0>,
+ <DCC_READ 0x012c1000 1 0>,
+ <DCC_READ 0x012c2028 1 0>,
+ <DCC_READ 0x012c8058 4 0>,
+ <DCC_READ 0x012e0410 3 0>,
+ <DCC_READ 0x012e04a0 1 0>,
+ <DCC_READ 0x012e04b8 1 0>,
+ <DCC_READ 0x012e5804 1 0>,
+ <DCC_READ 0x012e6418 1 0>,
+ <DCC_READ 0x01380900 8 0>,
+ <DCC_READ 0x01380d00 5 0>,
+ <DCC_READ 0x01350110 4 0>,
+ <DCC_READ 0x01430280 1 0>,
+ <DCC_READ 0x01430288 1 0>,
+ <DCC_READ 0x0143028c 7 0>,
+ <DCC_READ 0x01132100 1 0>,
+ <DCC_READ 0x01136044 4 0>,
+ <DCC_READ 0x011360b0 1 0>,
+ <DCC_READ 0x0113e030 2 0>,
+ <DCC_READ 0x01141000 1 0>,
+ <DCC_READ 0x01142028 1 0>,
+ <DCC_READ 0x01148058 4 0>,
+ <DCC_READ 0x01160410 3 0>,
+ <DCC_READ 0x011604a0 1 0>,
+ <DCC_READ 0x011604b8 1 0>,
+ <DCC_READ 0x01165804 1 0>,
+ <DCC_READ 0x01166418 1 0>,
+ <DCC_READ 0x011b2100 1 0>,
+ <DCC_READ 0x011b6044 4 0>,
+ <DCC_READ 0x011be030 2 0>,
+ <DCC_READ 0x011c1000 1 0>,
+ <DCC_READ 0x011c2028 1 0>,
+ <DCC_READ 0x011c8058 4 0>,
+ <DCC_READ 0x011e0410 3 0>,
+ <DCC_READ 0x011e04a0 1 0>,
+ <DCC_READ 0x011e04b8 1 0>,
+ <DCC_READ 0x011e5804 1 0>,
+ <DCC_READ 0x011e6418 1 0>,
+ <DCC_READ 0x01232100 1 0>,
+ <DCC_READ 0x01236044 4 0>,
+ <DCC_READ 0x012360b0 1 0>,
+ <DCC_READ 0x0123e030 2 0>,
+ <DCC_READ 0x01241000 1 0>,
+ <DCC_READ 0x01242028 1 0>,
+ <DCC_READ 0x01248058 4 0>,
+ <DCC_READ 0x01260410 3 0>,
+ <DCC_READ 0x012604a0 1 0>,
+ <DCC_READ 0x012604b8 1 0>,
+ <DCC_READ 0x01265804 1 0>,
+ <DCC_READ 0x01266418 1 0>,
+ <DCC_READ 0x012b2100 1 0>,
+ <DCC_READ 0x012b6044 3 0>,
+ <DCC_READ 0x012b6050 1 0>,
+ <DCC_READ 0x012b60b0 1 0>,
+ <DCC_READ 0x012be030 2 0>,
+ <DCC_READ 0x012C1000 1 0>,
+ <DCC_READ 0x012C2028 1 0>,
+ <DCC_READ 0x012C8058 4 0>,
+ <DCC_READ 0x012e0410 3 0>,
+ <DCC_READ 0x012e04a0 1 0>,
+ <DCC_READ 0x012e04b8 1 0>,
+ <DCC_READ 0x012e5804 1 0>,
+ <DCC_READ 0x012e6418 1 0>,
+ <DCC_READ 0x01380900 8 0>,
+ <DCC_READ 0x01380d00 5 0>,
+ <DCC_READ 0x01350110 4 0>,
+ <DCC_READ 0x01430280 1 0>,
+ <DCC_READ 0x01430288 1 0>,
+ <DCC_READ 0x0143028c 7 0>,
+ <DCC_READ 0x0c201244 1 0>,
+ <DCC_READ 0x0c202244 1 0>;
};
qcom,msm-core@780000 {
diff --git a/arch/arm64/configs/sdm670-perf_defconfig b/arch/arm64/configs/sdm670-perf_defconfig
index 784a986..0228c36 100644
--- a/arch/arm64/configs/sdm670-perf_defconfig
+++ b/arch/arm64/configs/sdm670-perf_defconfig
@@ -301,6 +301,7 @@
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM_LEGACY=y
+# CONFIG_DEVPORT is not set
CONFIG_MSM_ADSPRPC=y
CONFIG_MSM_RDBG=m
CONFIG_I2C_CHARDEV=y
@@ -441,6 +442,7 @@
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
+CONFIG_MMC_SDHCI_MSM_ICE=y
CONFIG_MMC_CQ_HCI=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
diff --git a/arch/arm64/configs/sdm670_defconfig b/arch/arm64/configs/sdm670_defconfig
index 6c29dff..87f710e 100644
--- a/arch/arm64/configs/sdm670_defconfig
+++ b/arch/arm64/configs/sdm670_defconfig
@@ -305,6 +305,7 @@
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM_LEGACY=y
+# CONFIG_DEVPORT is not set
CONFIG_MSM_ADSPRPC=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_QCOM_GENI=y
@@ -386,7 +387,11 @@
CONFIG_SND_SOC=y
CONFIG_UHID=y
CONFIG_HID_APPLE=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_MAGICMOUSE=y
CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_HID_PLANTRONICS=y
CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
@@ -435,6 +440,7 @@
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
+CONFIG_MMC_SDHCI_MSM_ICE=y
CONFIG_MMC_CQ_HCI=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index cdfa1eb..0300ef5 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-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
@@ -237,6 +240,7 @@
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
+CONFIG_UID_SYS_STATS=y
CONFIG_MEMORY_STATE_TIME=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index a2a9c12..e893e73 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -301,8 +301,6 @@
# CONFIG_SERIO_SERPORT is not set
# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set
-# CONFIG_DEVMEM is not set
-# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_MSM_GENI=y
CONFIG_SERIAL_MSM_GENI_CONSOLE=y
CONFIG_DIAG_CHAR=y
@@ -620,7 +618,6 @@
CONFIG_PANIC_ON_RT_THROTTLING=y
CONFIG_SCHEDSTATS=y
CONFIG_SCHED_STACK_END_CHECK=y
-# CONFIG_DEBUG_PREEMPT is not set
CONFIG_DEBUG_SPINLOCK=y
CONFIG_DEBUG_MUTEXES=y
CONFIG_DEBUG_ATOMIC_SLEEP=y
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 7ee6d74..c186586 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -487,6 +487,7 @@
* booted in EL1 or EL2 respectively.
*/
ENTRY(el2_setup)
+ msr SPsel, #1 // We want to use SP_EL{1,2}
mrs x0, CurrentEL
cmp x0, #CurrentEL_EL2
b.ne 1f
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index 15fef4c..9f205df 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -1981,7 +1981,7 @@
bool min_iova_align = 0;
iommu_domain_get_attr(mapping->domain,
- DOMAIN_ATTR_QCOM_MMU500_ERRATA_MIN_ALIGN,
+ DOMAIN_ATTR_MMU500_ERRATA_MIN_ALIGN,
&min_iova_align);
iommu_domain_get_attr(mapping->domain,
DOMAIN_ATTR_SECURE_VMID, &vmid);
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 792dac8..b5d88f8 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -537,7 +537,7 @@
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 0 translation fault" },
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 1 translation fault" },
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 2 translation fault" },
- { do_page_fault, SIGSEGV, SEGV_MAPERR, "level 3 translation fault" },
+ { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 3 translation fault" },
{ do_bad, SIGBUS, 0, "unknown 8" },
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 1 access flag fault" },
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 access flag fault" },
diff --git a/arch/mips/ath79/clock.c b/arch/mips/ath79/clock.c
index cc3a1e3..7e2bb12 100644
--- a/arch/mips/ath79/clock.c
+++ b/arch/mips/ath79/clock.c
@@ -508,16 +508,19 @@
ar9330_clk_init(ref_clk, pll_base);
else {
pr_err("%s: could not find any appropriate clk_init()\n", dnfn);
- goto err_clk;
+ goto err_iounmap;
}
if (of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data)) {
pr_err("%s: could not register clk provider\n", dnfn);
- goto err_clk;
+ goto err_iounmap;
}
return;
+err_iounmap:
+ iounmap(pll_base);
+
err_clk:
clk_put(ref_clk);
diff --git a/arch/mips/include/asm/irq.h b/arch/mips/include/asm/irq.h
index 956db6e..c5d3517 100644
--- a/arch/mips/include/asm/irq.h
+++ b/arch/mips/include/asm/irq.h
@@ -18,9 +18,24 @@
#include <irq.h>
#define IRQ_STACK_SIZE THREAD_SIZE
+#define IRQ_STACK_START (IRQ_STACK_SIZE - 16)
extern void *irq_stack[NR_CPUS];
+/*
+ * The highest address on the IRQ stack contains a dummy frame put down in
+ * genex.S (handle_int & except_vec_vi_handler) which is structured as follows:
+ *
+ * top ------------
+ * | task sp | <- irq_stack[cpu] + IRQ_STACK_START
+ * ------------
+ * | | <- First frame of IRQ context
+ * ------------
+ *
+ * task sp holds a copy of the task stack pointer where the struct pt_regs
+ * from exception entry can be found.
+ */
+
static inline bool on_irq_stack(int cpu, unsigned long sp)
{
unsigned long low = (unsigned long)irq_stack[cpu];
diff --git a/arch/mips/kernel/asm-offsets.c b/arch/mips/kernel/asm-offsets.c
index 4be2763..bfff6ea 100644
--- a/arch/mips/kernel/asm-offsets.c
+++ b/arch/mips/kernel/asm-offsets.c
@@ -103,6 +103,7 @@
DEFINE(_THREAD_SIZE, THREAD_SIZE);
DEFINE(_THREAD_MASK, THREAD_MASK);
DEFINE(_IRQ_STACK_SIZE, IRQ_STACK_SIZE);
+ DEFINE(_IRQ_STACK_START, IRQ_STACK_START);
BLANK();
}
diff --git a/arch/mips/kernel/cps-vec.S b/arch/mips/kernel/cps-vec.S
index 59476a6..a00e87b 100644
--- a/arch/mips/kernel/cps-vec.S
+++ b/arch/mips/kernel/cps-vec.S
@@ -361,7 +361,7 @@
END(mips_cps_get_bootcfg)
LEAF(mips_cps_boot_vpes)
- PTR_L ta2, COREBOOTCFG_VPEMASK(a0)
+ lw ta2, COREBOOTCFG_VPEMASK(a0)
PTR_L ta3, COREBOOTCFG_VPECONFIG(a0)
#if defined(CONFIG_CPU_MIPSR6)
diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S
index 2ac6c26..ae810da 100644
--- a/arch/mips/kernel/genex.S
+++ b/arch/mips/kernel/genex.S
@@ -215,9 +215,11 @@
beq t0, t1, 2f
/* Switch to IRQ stack */
- li t1, _IRQ_STACK_SIZE
+ li t1, _IRQ_STACK_START
PTR_ADD sp, t0, t1
+ /* Save task's sp on IRQ stack so that unwinding can follow it */
+ LONG_S s1, 0(sp)
2:
jal plat_irq_dispatch
@@ -325,9 +327,11 @@
beq t0, t1, 2f
/* Switch to IRQ stack */
- li t1, _IRQ_STACK_SIZE
+ li t1, _IRQ_STACK_START
PTR_ADD sp, t0, t1
+ /* Save task's sp on IRQ stack so that unwinding can follow it */
+ LONG_S s1, 0(sp)
2:
jalr v0
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index fbbf5fc..1b50958 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -487,31 +487,52 @@
unsigned long pc,
unsigned long *ra)
{
+ unsigned long low, high, irq_stack_high;
struct mips_frame_info info;
unsigned long size, ofs;
+ struct pt_regs *regs;
int leaf;
- extern void ret_from_irq(void);
- extern void ret_from_exception(void);
if (!stack_page)
return 0;
/*
- * If we reached the bottom of interrupt context,
- * return saved pc in pt_regs.
+ * IRQ stacks start at IRQ_STACK_START
+ * task stacks at THREAD_SIZE - 32
*/
- if (pc == (unsigned long)ret_from_irq ||
- pc == (unsigned long)ret_from_exception) {
- struct pt_regs *regs;
- if (*sp >= stack_page &&
- *sp + sizeof(*regs) <= stack_page + THREAD_SIZE - 32) {
- regs = (struct pt_regs *)*sp;
- pc = regs->cp0_epc;
- if (!user_mode(regs) && __kernel_text_address(pc)) {
- *sp = regs->regs[29];
- *ra = regs->regs[31];
- return pc;
- }
+ low = stack_page;
+ if (!preemptible() && on_irq_stack(raw_smp_processor_id(), *sp)) {
+ high = stack_page + IRQ_STACK_START;
+ irq_stack_high = high;
+ } else {
+ high = stack_page + THREAD_SIZE - 32;
+ irq_stack_high = 0;
+ }
+
+ /*
+ * If we reached the top of the interrupt stack, start unwinding
+ * the interrupted task stack.
+ */
+ if (unlikely(*sp == irq_stack_high)) {
+ unsigned long task_sp = *(unsigned long *)*sp;
+
+ /*
+ * Check that the pointer saved in the IRQ stack head points to
+ * something within the stack of the current task
+ */
+ if (!object_is_on_stack((void *)task_sp))
+ return 0;
+
+ /*
+ * Follow pointer to tasks kernel stack frame where interrupted
+ * state was saved.
+ */
+ regs = (struct pt_regs *)task_sp;
+ pc = regs->cp0_epc;
+ if (!user_mode(regs) && __kernel_text_address(pc)) {
+ *sp = regs->regs[29];
+ *ra = regs->regs[31];
+ return pc;
}
return 0;
}
@@ -532,8 +553,7 @@
if (leaf < 0)
return 0;
- if (*sp < stack_page ||
- *sp + info.frame_size > stack_page + THREAD_SIZE - 32)
+ if (*sp < low || *sp + info.frame_size > high)
return 0;
if (leaf)
diff --git a/arch/mips/kernel/vmlinux.lds.S b/arch/mips/kernel/vmlinux.lds.S
index d5de675..f0a0e6d 100644
--- a/arch/mips/kernel/vmlinux.lds.S
+++ b/arch/mips/kernel/vmlinux.lds.S
@@ -182,7 +182,7 @@
* Force .bss to 64K alignment so that .bss..swapper_pg_dir
* gets that alignment. .sbss should be empty, so there will be
* no holes after __init_end. */
- BSS_SECTION(0, 0x10000, 0)
+ BSS_SECTION(0, 0x10000, 8)
_end = . ;
diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c
index 9056547..95bec46 100644
--- a/arch/mips/lantiq/xway/sysctrl.c
+++ b/arch/mips/lantiq/xway/sysctrl.c
@@ -469,8 +469,8 @@
panic("Failed to load xbar nodes from devicetree");
if (of_address_to_resource(np_xbar, 0, &res_xbar))
panic("Failed to get xbar resources");
- if (request_mem_region(res_xbar.start, resource_size(&res_xbar),
- res_xbar.name) < 0)
+ if (!request_mem_region(res_xbar.start, resource_size(&res_xbar),
+ res_xbar.name))
panic("Failed to get xbar resources");
ltq_xbar_membase = ioremap_nocache(res_xbar.start,
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
index e9385bc..9ade60c 100644
--- a/arch/mips/math-emu/cp1emu.c
+++ b/arch/mips/math-emu/cp1emu.c
@@ -2386,7 +2386,6 @@
break;
default:
/* Reserved R6 ops */
- pr_err("Reserved MIPS R6 CMP.condn.S operation\n");
return SIGILL;
}
}
@@ -2460,7 +2459,6 @@
break;
default:
/* Reserved R6 ops */
- pr_err("Reserved MIPS R6 CMP.condn.D operation\n");
return SIGILL;
}
}
diff --git a/arch/mips/math-emu/dp_fmax.c b/arch/mips/math-emu/dp_fmax.c
index fd71b8d..5bec64f 100644
--- a/arch/mips/math-emu/dp_fmax.c
+++ b/arch/mips/math-emu/dp_fmax.c
@@ -47,14 +47,26 @@
case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
return ieee754dp_nanxcpt(x);
- /* numbers are preferred to NaNs */
+ /*
+ * Quiet NaN handling
+ */
+
+ /*
+ * The case of both inputs quiet NaNs
+ */
+ case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
+ return x;
+
+ /*
+ * The cases of exactly one input quiet NaN (numbers
+ * are here preferred as returned values to NaNs)
+ */
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
return x;
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
@@ -80,9 +92,7 @@
return ys ? x : y;
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
- if (xs == ys)
- return x;
- return ieee754dp_zero(1);
+ return ieee754dp_zero(xs & ys);
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
DPDNORMX;
@@ -106,16 +116,32 @@
else if (xs < ys)
return x;
- /* Compare exponent */
- if (xe > ye)
- return x;
- else if (xe < ye)
- return y;
+ /* Signs of inputs are equal, let's compare exponents */
+ if (xs == 0) {
+ /* Inputs are both positive */
+ if (xe > ye)
+ return x;
+ else if (xe < ye)
+ return y;
+ } else {
+ /* Inputs are both negative */
+ if (xe > ye)
+ return y;
+ else if (xe < ye)
+ return x;
+ }
- /* Compare mantissa */
+ /* Signs and exponents of inputs are equal, let's compare mantissas */
+ if (xs == 0) {
+ /* Inputs are both positive, with equal signs and exponents */
+ if (xm <= ym)
+ return y;
+ return x;
+ }
+ /* Inputs are both negative, with equal signs and exponents */
if (xm <= ym)
- return y;
- return x;
+ return x;
+ return y;
}
union ieee754dp ieee754dp_fmaxa(union ieee754dp x, union ieee754dp y)
@@ -147,14 +173,26 @@
case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
return ieee754dp_nanxcpt(x);
- /* numbers are preferred to NaNs */
+ /*
+ * Quiet NaN handling
+ */
+
+ /*
+ * The case of both inputs quiet NaNs
+ */
+ case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
+ return x;
+
+ /*
+ * The cases of exactly one input quiet NaN (numbers
+ * are here preferred as returned values to NaNs)
+ */
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
return x;
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
@@ -164,6 +202,9 @@
/*
* Infinity and zero handling
*/
+ case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
+ return ieee754dp_inf(xs & ys);
+
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_NORM):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_DNORM):
@@ -171,7 +212,6 @@
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_ZERO):
return x;
- case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_INF):
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_INF):
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_INF):
@@ -180,9 +220,7 @@
return y;
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
- if (xs == ys)
- return x;
- return ieee754dp_zero(1);
+ return ieee754dp_zero(xs & ys);
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
DPDNORMX;
@@ -207,7 +245,11 @@
return y;
/* Compare mantissa */
- if (xm <= ym)
+ if (xm < ym)
return y;
- return x;
+ else if (xm > ym)
+ return x;
+ else if (xs == 0)
+ return x;
+ return y;
}
diff --git a/arch/mips/math-emu/dp_fmin.c b/arch/mips/math-emu/dp_fmin.c
index c1072b0..a287b23 100644
--- a/arch/mips/math-emu/dp_fmin.c
+++ b/arch/mips/math-emu/dp_fmin.c
@@ -47,14 +47,26 @@
case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
return ieee754dp_nanxcpt(x);
- /* numbers are preferred to NaNs */
+ /*
+ * Quiet NaN handling
+ */
+
+ /*
+ * The case of both inputs quiet NaNs
+ */
+ case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
+ return x;
+
+ /*
+ * The cases of exactly one input quiet NaN (numbers
+ * are here preferred as returned values to NaNs)
+ */
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
return x;
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
@@ -80,9 +92,7 @@
return ys ? y : x;
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
- if (xs == ys)
- return x;
- return ieee754dp_zero(1);
+ return ieee754dp_zero(xs | ys);
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
DPDNORMX;
@@ -106,16 +116,32 @@
else if (xs < ys)
return y;
- /* Compare exponent */
- if (xe > ye)
- return y;
- else if (xe < ye)
- return x;
+ /* Signs of inputs are the same, let's compare exponents */
+ if (xs == 0) {
+ /* Inputs are both positive */
+ if (xe > ye)
+ return y;
+ else if (xe < ye)
+ return x;
+ } else {
+ /* Inputs are both negative */
+ if (xe > ye)
+ return x;
+ else if (xe < ye)
+ return y;
+ }
- /* Compare mantissa */
+ /* Signs and exponents of inputs are equal, let's compare mantissas */
+ if (xs == 0) {
+ /* Inputs are both positive, with equal signs and exponents */
+ if (xm <= ym)
+ return x;
+ return y;
+ }
+ /* Inputs are both negative, with equal signs and exponents */
if (xm <= ym)
- return x;
- return y;
+ return y;
+ return x;
}
union ieee754dp ieee754dp_fmina(union ieee754dp x, union ieee754dp y)
@@ -147,14 +173,26 @@
case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
return ieee754dp_nanxcpt(x);
- /* numbers are preferred to NaNs */
+ /*
+ * Quiet NaN handling
+ */
+
+ /*
+ * The case of both inputs quiet NaNs
+ */
+ case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
+ return x;
+
+ /*
+ * The cases of exactly one input quiet NaN (numbers
+ * are here preferred as returned values to NaNs)
+ */
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
return x;
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
@@ -164,25 +202,25 @@
/*
* Infinity and zero handling
*/
+ case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
+ return ieee754dp_inf(xs | ys);
+
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_NORM):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_DNORM):
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_ZERO):
- return x;
+ return y;
- case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_INF):
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_INF):
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_INF):
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_NORM):
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_DNORM):
- return y;
+ return x;
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
- if (xs == ys)
- return x;
- return ieee754dp_zero(1);
+ return ieee754dp_zero(xs | ys);
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
DPDNORMX;
@@ -207,7 +245,11 @@
return x;
/* Compare mantissa */
- if (xm <= ym)
+ if (xm < ym)
+ return x;
+ else if (xm > ym)
+ return y;
+ else if (xs == 1)
return x;
return y;
}
diff --git a/arch/mips/math-emu/dp_maddf.c b/arch/mips/math-emu/dp_maddf.c
index 4a2d03c..e0d9be5 100644
--- a/arch/mips/math-emu/dp_maddf.c
+++ b/arch/mips/math-emu/dp_maddf.c
@@ -14,22 +14,45 @@
#include "ieee754dp.h"
-enum maddf_flags {
- maddf_negate_product = 1 << 0,
-};
+
+/* 128 bits shift right logical with rounding. */
+void srl128(u64 *hptr, u64 *lptr, int count)
+{
+ u64 low;
+
+ if (count >= 128) {
+ *lptr = *hptr != 0 || *lptr != 0;
+ *hptr = 0;
+ } else if (count >= 64) {
+ if (count == 64) {
+ *lptr = *hptr | (*lptr != 0);
+ } else {
+ low = *lptr;
+ *lptr = *hptr >> (count - 64);
+ *lptr |= (*hptr << (128 - count)) != 0 || low != 0;
+ }
+ *hptr = 0;
+ } else {
+ low = *lptr;
+ *lptr = low >> count | *hptr << (64 - count);
+ *lptr |= (low << (64 - count)) != 0;
+ *hptr = *hptr >> count;
+ }
+}
static union ieee754dp _dp_maddf(union ieee754dp z, union ieee754dp x,
union ieee754dp y, enum maddf_flags flags)
{
int re;
int rs;
- u64 rm;
unsigned lxm;
unsigned hxm;
unsigned lym;
unsigned hym;
u64 lrm;
u64 hrm;
+ u64 lzm;
+ u64 hzm;
u64 t;
u64 at;
int s;
@@ -48,52 +71,34 @@
ieee754_clearcx();
- switch (zc) {
- case IEEE754_CLASS_SNAN:
- ieee754_setcx(IEEE754_INVALID_OPERATION);
+ /*
+ * Handle the cases when at least one of x, y or z is a NaN.
+ * Order of precedence is sNaN, qNaN and z, x, y.
+ */
+ if (zc == IEEE754_CLASS_SNAN)
return ieee754dp_nanxcpt(z);
- case IEEE754_CLASS_DNORM:
- DPDNORMZ;
- /* QNAN is handled separately below */
- }
-
- switch (CLPAIR(xc, yc)) {
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_SNAN):
- case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_SNAN):
- case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_SNAN):
- case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_SNAN):
- case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_SNAN):
- return ieee754dp_nanxcpt(y);
-
- case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN):
- case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN):
- case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_ZERO):
- case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_NORM):
- case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_DNORM):
- case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
+ if (xc == IEEE754_CLASS_SNAN)
return ieee754dp_nanxcpt(x);
-
- case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
- case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
- case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
- case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
+ if (yc == IEEE754_CLASS_SNAN)
+ return ieee754dp_nanxcpt(y);
+ if (zc == IEEE754_CLASS_QNAN)
+ return z;
+ if (xc == IEEE754_CLASS_QNAN)
+ return x;
+ if (yc == IEEE754_CLASS_QNAN)
return y;
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_INF):
- return x;
+ if (zc == IEEE754_CLASS_DNORM)
+ DPDNORMZ;
+ /* ZERO z cases are handled separately below */
+ switch (CLPAIR(xc, yc)) {
/*
* Infinity handling
*/
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_INF):
- if (zc == IEEE754_CLASS_QNAN)
- return z;
ieee754_setcx(IEEE754_INVALID_OPERATION);
return ieee754dp_indef();
@@ -102,9 +107,27 @@
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_NORM):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_DNORM):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
- if (zc == IEEE754_CLASS_QNAN)
- return z;
- return ieee754dp_inf(xs ^ ys);
+ if ((zc == IEEE754_CLASS_INF) &&
+ ((!(flags & MADDF_NEGATE_PRODUCT) && (zs != (xs ^ ys))) ||
+ ((flags & MADDF_NEGATE_PRODUCT) && (zs == (xs ^ ys))))) {
+ /*
+ * Cases of addition of infinities with opposite signs
+ * or subtraction of infinities with same signs.
+ */
+ ieee754_setcx(IEEE754_INVALID_OPERATION);
+ return ieee754dp_indef();
+ }
+ /*
+ * z is here either not an infinity, or an infinity having the
+ * same sign as product (x*y) (in case of MADDF.D instruction)
+ * or product -(x*y) (in MSUBF.D case). The result must be an
+ * infinity, and its sign is determined only by the value of
+ * (flags & MADDF_NEGATE_PRODUCT) and the signs of x and y.
+ */
+ if (flags & MADDF_NEGATE_PRODUCT)
+ return ieee754dp_inf(1 ^ (xs ^ ys));
+ else
+ return ieee754dp_inf(xs ^ ys);
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_NORM):
@@ -113,32 +136,42 @@
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_ZERO):
if (zc == IEEE754_CLASS_INF)
return ieee754dp_inf(zs);
- /* Multiplication is 0 so just return z */
+ if (zc == IEEE754_CLASS_ZERO) {
+ /* Handle cases +0 + (-0) and similar ones. */
+ if ((!(flags & MADDF_NEGATE_PRODUCT)
+ && (zs == (xs ^ ys))) ||
+ ((flags & MADDF_NEGATE_PRODUCT)
+ && (zs != (xs ^ ys))))
+ /*
+ * Cases of addition of zeros of equal signs
+ * or subtraction of zeroes of opposite signs.
+ * The sign of the resulting zero is in any
+ * such case determined only by the sign of z.
+ */
+ return z;
+
+ return ieee754dp_zero(ieee754_csr.rm == FPU_CSR_RD);
+ }
+ /* x*y is here 0, and z is not 0, so just return z */
return z;
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
DPDNORMX;
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_DNORM):
- if (zc == IEEE754_CLASS_QNAN)
- return z;
- else if (zc == IEEE754_CLASS_INF)
+ if (zc == IEEE754_CLASS_INF)
return ieee754dp_inf(zs);
DPDNORMY;
break;
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_NORM):
- if (zc == IEEE754_CLASS_QNAN)
- return z;
- else if (zc == IEEE754_CLASS_INF)
+ if (zc == IEEE754_CLASS_INF)
return ieee754dp_inf(zs);
DPDNORMX;
break;
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_NORM):
- if (zc == IEEE754_CLASS_QNAN)
- return z;
- else if (zc == IEEE754_CLASS_INF)
+ if (zc == IEEE754_CLASS_INF)
return ieee754dp_inf(zs);
/* fall through to real computations */
}
@@ -157,7 +190,7 @@
re = xe + ye;
rs = xs ^ ys;
- if (flags & maddf_negate_product)
+ if (flags & MADDF_NEGATE_PRODUCT)
rs ^= 1;
/* shunt to top of word */
@@ -165,7 +198,7 @@
ym <<= 64 - (DP_FBITS + 1);
/*
- * Multiply 64 bits xm, ym to give high 64 bits rm with stickness.
+ * Multiply 64 bits xm and ym to give 128 bits result in hrm:lrm.
*/
/* 32 * 32 => 64 */
@@ -195,78 +228,110 @@
hrm = hrm + (t >> 32);
- rm = hrm | (lrm != 0);
-
- /*
- * Sticky shift down to normal rounding precision.
- */
- if ((s64) rm < 0) {
- rm = (rm >> (64 - (DP_FBITS + 1 + 3))) |
- ((rm << (DP_FBITS + 1 + 3)) != 0);
+ /* Put explicit bit at bit 126 if necessary */
+ if ((int64_t)hrm < 0) {
+ lrm = (hrm << 63) | (lrm >> 1);
+ hrm = hrm >> 1;
re++;
- } else {
- rm = (rm >> (64 - (DP_FBITS + 1 + 3 + 1))) |
- ((rm << (DP_FBITS + 1 + 3 + 1)) != 0);
}
- assert(rm & (DP_HIDDEN_BIT << 3));
- /* And now the addition */
- assert(zm & DP_HIDDEN_BIT);
+ assert(hrm & (1 << 62));
- /*
- * Provide guard,round and stick bit space.
- */
- zm <<= 3;
+ if (zc == IEEE754_CLASS_ZERO) {
+ /*
+ * Move explicit bit from bit 126 to bit 55 since the
+ * ieee754dp_format code expects the mantissa to be
+ * 56 bits wide (53 + 3 rounding bits).
+ */
+ srl128(&hrm, &lrm, (126 - 55));
+ return ieee754dp_format(rs, re, lrm);
+ }
+ /* Move explicit bit from bit 52 to bit 126 */
+ lzm = 0;
+ hzm = zm << 10;
+ assert(hzm & (1 << 62));
+
+ /* Make the exponents the same */
if (ze > re) {
/*
* Have to shift y fraction right to align.
*/
s = ze - re;
- rm = XDPSRS(rm, s);
+ srl128(&hrm, &lrm, s);
re += s;
} else if (re > ze) {
/*
* Have to shift x fraction right to align.
*/
s = re - ze;
- zm = XDPSRS(zm, s);
+ srl128(&hzm, &lzm, s);
ze += s;
}
assert(ze == re);
assert(ze <= DP_EMAX);
+ /* Do the addition */
if (zs == rs) {
/*
- * Generate 28 bit result of adding two 27 bit numbers
- * leaving result in xm, xs and xe.
+ * Generate 128 bit result by adding two 127 bit numbers
+ * leaving result in hzm:lzm, zs and ze.
*/
- zm = zm + rm;
-
- if (zm >> (DP_FBITS + 1 + 3)) { /* carry out */
- zm = XDPSRS1(zm);
+ hzm = hzm + hrm + (lzm > (lzm + lrm));
+ lzm = lzm + lrm;
+ if ((int64_t)hzm < 0) { /* carry out */
+ srl128(&hzm, &lzm, 1);
ze++;
}
} else {
- if (zm >= rm) {
- zm = zm - rm;
+ if (hzm > hrm || (hzm == hrm && lzm >= lrm)) {
+ hzm = hzm - hrm - (lzm < lrm);
+ lzm = lzm - lrm;
} else {
- zm = rm - zm;
+ hzm = hrm - hzm - (lrm < lzm);
+ lzm = lrm - lzm;
zs = rs;
}
- if (zm == 0)
+ if (lzm == 0 && hzm == 0)
return ieee754dp_zero(ieee754_csr.rm == FPU_CSR_RD);
/*
- * Normalize to rounding precision.
+ * Put explicit bit at bit 126 if necessary.
*/
- while ((zm >> (DP_FBITS + 3)) == 0) {
- zm <<= 1;
- ze--;
+ if (hzm == 0) {
+ /* left shift by 63 or 64 bits */
+ if ((int64_t)lzm < 0) {
+ /* MSB of lzm is the explicit bit */
+ hzm = lzm >> 1;
+ lzm = lzm << 63;
+ ze -= 63;
+ } else {
+ hzm = lzm;
+ lzm = 0;
+ ze -= 64;
+ }
+ }
+
+ t = 0;
+ while ((hzm >> (62 - t)) == 0)
+ t++;
+
+ assert(t <= 62);
+ if (t) {
+ hzm = hzm << t | lzm >> (64 - t);
+ lzm = lzm << t;
+ ze -= t;
}
}
- return ieee754dp_format(zs, ze, zm);
+ /*
+ * Move explicit bit from bit 126 to bit 55 since the
+ * ieee754dp_format code expects the mantissa to be
+ * 56 bits wide (53 + 3 rounding bits).
+ */
+ srl128(&hzm, &lzm, (126 - 55));
+
+ return ieee754dp_format(zs, ze, lzm);
}
union ieee754dp ieee754dp_maddf(union ieee754dp z, union ieee754dp x,
@@ -278,5 +343,5 @@
union ieee754dp ieee754dp_msubf(union ieee754dp z, union ieee754dp x,
union ieee754dp y)
{
- return _dp_maddf(z, x, y, maddf_negate_product);
+ return _dp_maddf(z, x, y, MADDF_NEGATE_PRODUCT);
}
diff --git a/arch/mips/math-emu/ieee754int.h b/arch/mips/math-emu/ieee754int.h
index 8bc2f69..dd2071f 100644
--- a/arch/mips/math-emu/ieee754int.h
+++ b/arch/mips/math-emu/ieee754int.h
@@ -26,6 +26,10 @@
#define CLPAIR(x, y) ((x)*6+(y))
+enum maddf_flags {
+ MADDF_NEGATE_PRODUCT = 1 << 0,
+};
+
static inline void ieee754_clearcx(void)
{
ieee754_csr.cx = 0;
diff --git a/arch/mips/math-emu/ieee754sp.h b/arch/mips/math-emu/ieee754sp.h
index 8476067..0f63e42 100644
--- a/arch/mips/math-emu/ieee754sp.h
+++ b/arch/mips/math-emu/ieee754sp.h
@@ -45,6 +45,10 @@
return SPBEXP(x) != SP_EMAX + 1 + SP_EBIAS;
}
+/* 64 bit right shift with rounding */
+#define XSPSRS64(v, rs) \
+ (((rs) >= 64) ? ((v) != 0) : ((v) >> (rs)) | ((v) << (64-(rs)) != 0))
+
/* 3bit extended single precision sticky right shift */
#define XSPSRS(v, rs) \
((rs > (SP_FBITS+3))?1:((v) >> (rs)) | ((v) << (32-(rs)) != 0))
diff --git a/arch/mips/math-emu/sp_fmax.c b/arch/mips/math-emu/sp_fmax.c
index 4d00084..74a5a00 100644
--- a/arch/mips/math-emu/sp_fmax.c
+++ b/arch/mips/math-emu/sp_fmax.c
@@ -47,14 +47,26 @@
case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
return ieee754sp_nanxcpt(x);
- /* numbers are preferred to NaNs */
+ /*
+ * Quiet NaN handling
+ */
+
+ /*
+ * The case of both inputs quiet NaNs
+ */
+ case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
+ return x;
+
+ /*
+ * The cases of exactly one input quiet NaN (numbers
+ * are here preferred as returned values to NaNs)
+ */
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
return x;
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
@@ -80,9 +92,7 @@
return ys ? x : y;
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
- if (xs == ys)
- return x;
- return ieee754sp_zero(1);
+ return ieee754sp_zero(xs & ys);
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
SPDNORMX;
@@ -106,16 +116,32 @@
else if (xs < ys)
return x;
- /* Compare exponent */
- if (xe > ye)
- return x;
- else if (xe < ye)
- return y;
+ /* Signs of inputs are equal, let's compare exponents */
+ if (xs == 0) {
+ /* Inputs are both positive */
+ if (xe > ye)
+ return x;
+ else if (xe < ye)
+ return y;
+ } else {
+ /* Inputs are both negative */
+ if (xe > ye)
+ return y;
+ else if (xe < ye)
+ return x;
+ }
- /* Compare mantissa */
+ /* Signs and exponents of inputs are equal, let's compare mantissas */
+ if (xs == 0) {
+ /* Inputs are both positive, with equal signs and exponents */
+ if (xm <= ym)
+ return y;
+ return x;
+ }
+ /* Inputs are both negative, with equal signs and exponents */
if (xm <= ym)
- return y;
- return x;
+ return x;
+ return y;
}
union ieee754sp ieee754sp_fmaxa(union ieee754sp x, union ieee754sp y)
@@ -147,14 +173,26 @@
case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
return ieee754sp_nanxcpt(x);
- /* numbers are preferred to NaNs */
+ /*
+ * Quiet NaN handling
+ */
+
+ /*
+ * The case of both inputs quiet NaNs
+ */
+ case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
+ return x;
+
+ /*
+ * The cases of exactly one input quiet NaN (numbers
+ * are here preferred as returned values to NaNs)
+ */
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
return x;
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
@@ -164,6 +202,9 @@
/*
* Infinity and zero handling
*/
+ case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
+ return ieee754sp_inf(xs & ys);
+
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_NORM):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_DNORM):
@@ -171,7 +212,6 @@
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_ZERO):
return x;
- case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_INF):
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_INF):
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_INF):
@@ -180,9 +220,7 @@
return y;
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
- if (xs == ys)
- return x;
- return ieee754sp_zero(1);
+ return ieee754sp_zero(xs & ys);
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
SPDNORMX;
@@ -207,7 +245,11 @@
return y;
/* Compare mantissa */
- if (xm <= ym)
+ if (xm < ym)
return y;
- return x;
+ else if (xm > ym)
+ return x;
+ else if (xs == 0)
+ return x;
+ return y;
}
diff --git a/arch/mips/math-emu/sp_fmin.c b/arch/mips/math-emu/sp_fmin.c
index 4eb1bb9..c51385f 100644
--- a/arch/mips/math-emu/sp_fmin.c
+++ b/arch/mips/math-emu/sp_fmin.c
@@ -47,14 +47,26 @@
case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
return ieee754sp_nanxcpt(x);
- /* numbers are preferred to NaNs */
+ /*
+ * Quiet NaN handling
+ */
+
+ /*
+ * The case of both inputs quiet NaNs
+ */
+ case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
+ return x;
+
+ /*
+ * The cases of exactly one input quiet NaN (numbers
+ * are here preferred as returned values to NaNs)
+ */
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
return x;
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
@@ -80,9 +92,7 @@
return ys ? y : x;
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
- if (xs == ys)
- return x;
- return ieee754sp_zero(1);
+ return ieee754sp_zero(xs | ys);
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
SPDNORMX;
@@ -106,16 +116,32 @@
else if (xs < ys)
return y;
- /* Compare exponent */
- if (xe > ye)
- return y;
- else if (xe < ye)
- return x;
+ /* Signs of inputs are the same, let's compare exponents */
+ if (xs == 0) {
+ /* Inputs are both positive */
+ if (xe > ye)
+ return y;
+ else if (xe < ye)
+ return x;
+ } else {
+ /* Inputs are both negative */
+ if (xe > ye)
+ return x;
+ else if (xe < ye)
+ return y;
+ }
- /* Compare mantissa */
+ /* Signs and exponents of inputs are equal, let's compare mantissas */
+ if (xs == 0) {
+ /* Inputs are both positive, with equal signs and exponents */
+ if (xm <= ym)
+ return x;
+ return y;
+ }
+ /* Inputs are both negative, with equal signs and exponents */
if (xm <= ym)
- return x;
- return y;
+ return y;
+ return x;
}
union ieee754sp ieee754sp_fmina(union ieee754sp x, union ieee754sp y)
@@ -147,14 +173,26 @@
case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
return ieee754sp_nanxcpt(x);
- /* numbers are preferred to NaNs */
+ /*
+ * Quiet NaN handling
+ */
+
+ /*
+ * The case of both inputs quiet NaNs
+ */
+ case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
+ return x;
+
+ /*
+ * The cases of exactly one input quiet NaN (numbers
+ * are here preferred as returned values to NaNs)
+ */
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
return x;
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
@@ -164,25 +202,25 @@
/*
* Infinity and zero handling
*/
+ case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
+ return ieee754sp_inf(xs | ys);
+
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_NORM):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_DNORM):
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_ZERO):
- return x;
+ return y;
- case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_INF):
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_INF):
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_INF):
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_NORM):
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_DNORM):
- return y;
+ return x;
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
- if (xs == ys)
- return x;
- return ieee754sp_zero(1);
+ return ieee754sp_zero(xs | ys);
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
SPDNORMX;
@@ -207,7 +245,11 @@
return x;
/* Compare mantissa */
- if (xm <= ym)
+ if (xm < ym)
+ return x;
+ else if (xm > ym)
+ return y;
+ else if (xs == 1)
return x;
return y;
}
diff --git a/arch/mips/math-emu/sp_maddf.c b/arch/mips/math-emu/sp_maddf.c
index a8cd8b4..7195fe7 100644
--- a/arch/mips/math-emu/sp_maddf.c
+++ b/arch/mips/math-emu/sp_maddf.c
@@ -14,9 +14,6 @@
#include "ieee754sp.h"
-enum maddf_flags {
- maddf_negate_product = 1 << 0,
-};
static union ieee754sp _sp_maddf(union ieee754sp z, union ieee754sp x,
union ieee754sp y, enum maddf_flags flags)
@@ -24,14 +21,8 @@
int re;
int rs;
unsigned rm;
- unsigned short lxm;
- unsigned short hxm;
- unsigned short lym;
- unsigned short hym;
- unsigned lrm;
- unsigned hrm;
- unsigned t;
- unsigned at;
+ uint64_t rm64;
+ uint64_t zm64;
int s;
COMPXSP;
@@ -48,51 +39,35 @@
ieee754_clearcx();
- switch (zc) {
- case IEEE754_CLASS_SNAN:
- ieee754_setcx(IEEE754_INVALID_OPERATION);
+ /*
+ * Handle the cases when at least one of x, y or z is a NaN.
+ * Order of precedence is sNaN, qNaN and z, x, y.
+ */
+ if (zc == IEEE754_CLASS_SNAN)
return ieee754sp_nanxcpt(z);
- case IEEE754_CLASS_DNORM:
- SPDNORMZ;
- /* QNAN is handled separately below */
- }
-
- switch (CLPAIR(xc, yc)) {
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_SNAN):
- case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_SNAN):
- case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_SNAN):
- case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_SNAN):
- case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_SNAN):
- return ieee754sp_nanxcpt(y);
-
- case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN):
- case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN):
- case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_ZERO):
- case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_NORM):
- case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_DNORM):
- case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
+ if (xc == IEEE754_CLASS_SNAN)
return ieee754sp_nanxcpt(x);
-
- case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
- case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
- case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
- case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
+ if (yc == IEEE754_CLASS_SNAN)
+ return ieee754sp_nanxcpt(y);
+ if (zc == IEEE754_CLASS_QNAN)
+ return z;
+ if (xc == IEEE754_CLASS_QNAN)
+ return x;
+ if (yc == IEEE754_CLASS_QNAN)
return y;
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
- case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_INF):
- return x;
+ if (zc == IEEE754_CLASS_DNORM)
+ SPDNORMZ;
+ /* ZERO z cases are handled separately below */
+
+ switch (CLPAIR(xc, yc)) {
+
/*
* Infinity handling
*/
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_INF):
- if (zc == IEEE754_CLASS_QNAN)
- return z;
ieee754_setcx(IEEE754_INVALID_OPERATION);
return ieee754sp_indef();
@@ -101,9 +76,27 @@
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_NORM):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_DNORM):
case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
- if (zc == IEEE754_CLASS_QNAN)
- return z;
- return ieee754sp_inf(xs ^ ys);
+ if ((zc == IEEE754_CLASS_INF) &&
+ ((!(flags & MADDF_NEGATE_PRODUCT) && (zs != (xs ^ ys))) ||
+ ((flags & MADDF_NEGATE_PRODUCT) && (zs == (xs ^ ys))))) {
+ /*
+ * Cases of addition of infinities with opposite signs
+ * or subtraction of infinities with same signs.
+ */
+ ieee754_setcx(IEEE754_INVALID_OPERATION);
+ return ieee754sp_indef();
+ }
+ /*
+ * z is here either not an infinity, or an infinity having the
+ * same sign as product (x*y) (in case of MADDF.D instruction)
+ * or product -(x*y) (in MSUBF.D case). The result must be an
+ * infinity, and its sign is determined only by the value of
+ * (flags & MADDF_NEGATE_PRODUCT) and the signs of x and y.
+ */
+ if (flags & MADDF_NEGATE_PRODUCT)
+ return ieee754sp_inf(1 ^ (xs ^ ys));
+ else
+ return ieee754sp_inf(xs ^ ys);
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_NORM):
@@ -112,32 +105,42 @@
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_ZERO):
if (zc == IEEE754_CLASS_INF)
return ieee754sp_inf(zs);
- /* Multiplication is 0 so just return z */
+ if (zc == IEEE754_CLASS_ZERO) {
+ /* Handle cases +0 + (-0) and similar ones. */
+ if ((!(flags & MADDF_NEGATE_PRODUCT)
+ && (zs == (xs ^ ys))) ||
+ ((flags & MADDF_NEGATE_PRODUCT)
+ && (zs != (xs ^ ys))))
+ /*
+ * Cases of addition of zeros of equal signs
+ * or subtraction of zeroes of opposite signs.
+ * The sign of the resulting zero is in any
+ * such case determined only by the sign of z.
+ */
+ return z;
+
+ return ieee754sp_zero(ieee754_csr.rm == FPU_CSR_RD);
+ }
+ /* x*y is here 0, and z is not 0, so just return z */
return z;
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
SPDNORMX;
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_DNORM):
- if (zc == IEEE754_CLASS_QNAN)
- return z;
- else if (zc == IEEE754_CLASS_INF)
+ if (zc == IEEE754_CLASS_INF)
return ieee754sp_inf(zs);
SPDNORMY;
break;
case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_NORM):
- if (zc == IEEE754_CLASS_QNAN)
- return z;
- else if (zc == IEEE754_CLASS_INF)
+ if (zc == IEEE754_CLASS_INF)
return ieee754sp_inf(zs);
SPDNORMX;
break;
case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_NORM):
- if (zc == IEEE754_CLASS_QNAN)
- return z;
- else if (zc == IEEE754_CLASS_INF)
+ if (zc == IEEE754_CLASS_INF)
return ieee754sp_inf(zs);
/* fall through to real computations */
}
@@ -158,108 +161,93 @@
re = xe + ye;
rs = xs ^ ys;
- if (flags & maddf_negate_product)
+ if (flags & MADDF_NEGATE_PRODUCT)
rs ^= 1;
- /* shunt to top of word */
- xm <<= 32 - (SP_FBITS + 1);
- ym <<= 32 - (SP_FBITS + 1);
+ /* Multiple 24 bit xm and ym to give 48 bit results */
+ rm64 = (uint64_t)xm * ym;
- /*
- * Multiply 32 bits xm, ym to give high 32 bits rm with stickness.
- */
- lxm = xm & 0xffff;
- hxm = xm >> 16;
- lym = ym & 0xffff;
- hym = ym >> 16;
+ /* Shunt to top of word */
+ rm64 = rm64 << 16;
- lrm = lxm * lym; /* 16 * 16 => 32 */
- hrm = hxm * hym; /* 16 * 16 => 32 */
-
- t = lxm * hym; /* 16 * 16 => 32 */
- at = lrm + (t << 16);
- hrm += at < lrm;
- lrm = at;
- hrm = hrm + (t >> 16);
-
- t = hxm * lym; /* 16 * 16 => 32 */
- at = lrm + (t << 16);
- hrm += at < lrm;
- lrm = at;
- hrm = hrm + (t >> 16);
-
- rm = hrm | (lrm != 0);
-
- /*
- * Sticky shift down to normal rounding precision.
- */
- if ((int) rm < 0) {
- rm = (rm >> (32 - (SP_FBITS + 1 + 3))) |
- ((rm << (SP_FBITS + 1 + 3)) != 0);
+ /* Put explicit bit at bit 62 if necessary */
+ if ((int64_t) rm64 < 0) {
+ rm64 = rm64 >> 1;
re++;
- } else {
- rm = (rm >> (32 - (SP_FBITS + 1 + 3 + 1))) |
- ((rm << (SP_FBITS + 1 + 3 + 1)) != 0);
}
- assert(rm & (SP_HIDDEN_BIT << 3));
- /* And now the addition */
+ assert(rm64 & (1 << 62));
- assert(zm & SP_HIDDEN_BIT);
+ if (zc == IEEE754_CLASS_ZERO) {
+ /*
+ * Move explicit bit from bit 62 to bit 26 since the
+ * ieee754sp_format code expects the mantissa to be
+ * 27 bits wide (24 + 3 rounding bits).
+ */
+ rm = XSPSRS64(rm64, (62 - 26));
+ return ieee754sp_format(rs, re, rm);
+ }
- /*
- * Provide guard,round and stick bit space.
- */
- zm <<= 3;
+ /* Move explicit bit from bit 23 to bit 62 */
+ zm64 = (uint64_t)zm << (62 - 23);
+ assert(zm64 & (1 << 62));
+ /* Make the exponents the same */
if (ze > re) {
/*
* Have to shift r fraction right to align.
*/
s = ze - re;
- rm = XSPSRS(rm, s);
+ rm64 = XSPSRS64(rm64, s);
re += s;
} else if (re > ze) {
/*
* Have to shift z fraction right to align.
*/
s = re - ze;
- zm = XSPSRS(zm, s);
+ zm64 = XSPSRS64(zm64, s);
ze += s;
}
assert(ze == re);
assert(ze <= SP_EMAX);
+ /* Do the addition */
if (zs == rs) {
/*
- * Generate 28 bit result of adding two 27 bit numbers
- * leaving result in zm, zs and ze.
+ * Generate 64 bit result by adding two 63 bit numbers
+ * leaving result in zm64, zs and ze.
*/
- zm = zm + rm;
-
- if (zm >> (SP_FBITS + 1 + 3)) { /* carry out */
- zm = XSPSRS1(zm);
+ zm64 = zm64 + rm64;
+ if ((int64_t)zm64 < 0) { /* carry out */
+ zm64 = XSPSRS1(zm64);
ze++;
}
} else {
- if (zm >= rm) {
- zm = zm - rm;
+ if (zm64 >= rm64) {
+ zm64 = zm64 - rm64;
} else {
- zm = rm - zm;
+ zm64 = rm64 - zm64;
zs = rs;
}
- if (zm == 0)
+ if (zm64 == 0)
return ieee754sp_zero(ieee754_csr.rm == FPU_CSR_RD);
/*
- * Normalize in extended single precision
+ * Put explicit bit at bit 62 if necessary.
*/
- while ((zm >> (SP_MBITS + 3)) == 0) {
- zm <<= 1;
+ while ((zm64 >> 62) == 0) {
+ zm64 <<= 1;
ze--;
}
-
}
+
+ /*
+ * Move explicit bit from bit 62 to bit 26 since the
+ * ieee754sp_format code expects the mantissa to be
+ * 27 bits wide (24 + 3 rounding bits).
+ */
+ zm = XSPSRS64(zm64, (62 - 26));
+
return ieee754sp_format(zs, ze, zm);
}
@@ -272,5 +260,5 @@
union ieee754sp ieee754sp_msubf(union ieee754sp z, union ieee754sp x,
union ieee754sp y)
{
- return _sp_maddf(z, x, y, maddf_negate_product);
+ return _sp_maddf(z, x, y, MADDF_NEGATE_PRODUCT);
}
diff --git a/arch/mips/ralink/mt7620.c b/arch/mips/ralink/mt7620.c
index 3c7c9bf..6f892c1 100644
--- a/arch/mips/ralink/mt7620.c
+++ b/arch/mips/ralink/mt7620.c
@@ -176,7 +176,7 @@
static struct rt2880_pmx_func spis_grp_mt7628[] = {
FUNC("pwm_uart2", 3, 14, 4),
- FUNC("util", 2, 14, 4),
+ FUNC("utif", 2, 14, 4),
FUNC("gpio", 1, 14, 4),
FUNC("spis", 0, 14, 4),
};
@@ -190,28 +190,28 @@
static struct rt2880_pmx_func p4led_kn_grp_mt7628[] = {
FUNC("jtag", 3, 30, 1),
- FUNC("util", 2, 30, 1),
+ FUNC("utif", 2, 30, 1),
FUNC("gpio", 1, 30, 1),
FUNC("p4led_kn", 0, 30, 1),
};
static struct rt2880_pmx_func p3led_kn_grp_mt7628[] = {
FUNC("jtag", 3, 31, 1),
- FUNC("util", 2, 31, 1),
+ FUNC("utif", 2, 31, 1),
FUNC("gpio", 1, 31, 1),
FUNC("p3led_kn", 0, 31, 1),
};
static struct rt2880_pmx_func p2led_kn_grp_mt7628[] = {
FUNC("jtag", 3, 32, 1),
- FUNC("util", 2, 32, 1),
+ FUNC("utif", 2, 32, 1),
FUNC("gpio", 1, 32, 1),
FUNC("p2led_kn", 0, 32, 1),
};
static struct rt2880_pmx_func p1led_kn_grp_mt7628[] = {
FUNC("jtag", 3, 33, 1),
- FUNC("util", 2, 33, 1),
+ FUNC("utif", 2, 33, 1),
FUNC("gpio", 1, 33, 1),
FUNC("p1led_kn", 0, 33, 1),
};
@@ -232,28 +232,28 @@
static struct rt2880_pmx_func p4led_an_grp_mt7628[] = {
FUNC("jtag", 3, 39, 1),
- FUNC("util", 2, 39, 1),
+ FUNC("utif", 2, 39, 1),
FUNC("gpio", 1, 39, 1),
FUNC("p4led_an", 0, 39, 1),
};
static struct rt2880_pmx_func p3led_an_grp_mt7628[] = {
FUNC("jtag", 3, 40, 1),
- FUNC("util", 2, 40, 1),
+ FUNC("utif", 2, 40, 1),
FUNC("gpio", 1, 40, 1),
FUNC("p3led_an", 0, 40, 1),
};
static struct rt2880_pmx_func p2led_an_grp_mt7628[] = {
FUNC("jtag", 3, 41, 1),
- FUNC("util", 2, 41, 1),
+ FUNC("utif", 2, 41, 1),
FUNC("gpio", 1, 41, 1),
FUNC("p2led_an", 0, 41, 1),
};
static struct rt2880_pmx_func p1led_an_grp_mt7628[] = {
FUNC("jtag", 3, 42, 1),
- FUNC("util", 2, 42, 1),
+ FUNC("utif", 2, 42, 1),
FUNC("gpio", 1, 42, 1),
FUNC("p1led_an", 0, 42, 1),
};
diff --git a/arch/mips/ralink/rt3883.c b/arch/mips/ralink/rt3883.c
index 9e4631a..3e68e35 100644
--- a/arch/mips/ralink/rt3883.c
+++ b/arch/mips/ralink/rt3883.c
@@ -145,5 +145,5 @@
rt2880_pinmux_data = rt3883_pinmux_data;
- ralink_soc == RT3883_SOC;
+ ralink_soc = RT3883_SOC;
}
diff --git a/arch/parisc/kernel/perf.c b/arch/parisc/kernel/perf.c
index 518f4f5..d63d425 100644
--- a/arch/parisc/kernel/perf.c
+++ b/arch/parisc/kernel/perf.c
@@ -39,7 +39,7 @@
* the PDC INTRIGUE calls. This is done to eliminate bugs introduced
* in various PDC revisions. The code is much more maintainable
* and reliable this way vs having to debug on every version of PDC
- * on every box.
+ * on every box.
*/
#include <linux/capability.h>
@@ -195,8 +195,8 @@
static int perf_release(struct inode *inode, struct file *file);
static int perf_open(struct inode *inode, struct file *file);
static ssize_t perf_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos);
-static ssize_t perf_write(struct file *file, const char __user *buf, size_t count,
- loff_t *ppos);
+static ssize_t perf_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos);
static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
static void perf_start_counters(void);
static int perf_stop_counters(uint32_t *raddr);
@@ -222,7 +222,7 @@
/*
* configure:
*
- * Configure the cpu with a given data image. First turn off the counters,
+ * Configure the cpu with a given data image. First turn off the counters,
* then download the image, then turn the counters back on.
*/
static int perf_config(uint32_t *image_ptr)
@@ -234,7 +234,7 @@
error = perf_stop_counters(raddr);
if (error != 0) {
printk("perf_config: perf_stop_counters = %ld\n", error);
- return -EINVAL;
+ return -EINVAL;
}
printk("Preparing to write image\n");
@@ -242,7 +242,7 @@
error = perf_write_image((uint64_t *)image_ptr);
if (error != 0) {
printk("perf_config: DOWNLOAD = %ld\n", error);
- return -EINVAL;
+ return -EINVAL;
}
printk("Preparing to start counters\n");
@@ -254,7 +254,7 @@
}
/*
- * Open the device and initialize all of its memory. The device is only
+ * Open the device and initialize all of its memory. The device is only
* opened once, but can be "queried" by multiple processes that know its
* file descriptor.
*/
@@ -298,8 +298,8 @@
* called on the processor that the download should happen
* on.
*/
-static ssize_t perf_write(struct file *file, const char __user *buf, size_t count,
- loff_t *ppos)
+static ssize_t perf_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
{
int err;
size_t image_size;
@@ -307,11 +307,11 @@
uint32_t interface_type;
uint32_t test;
- if (perf_processor_interface == ONYX_INTF)
+ if (perf_processor_interface == ONYX_INTF)
image_size = PCXU_IMAGE_SIZE;
- else if (perf_processor_interface == CUDA_INTF)
+ else if (perf_processor_interface == CUDA_INTF)
image_size = PCXW_IMAGE_SIZE;
- else
+ else
return -EFAULT;
if (!capable(CAP_SYS_ADMIN))
@@ -331,22 +331,22 @@
/* First check the machine type is correct for
the requested image */
- if (((perf_processor_interface == CUDA_INTF) &&
- (interface_type != CUDA_INTF)) ||
- ((perf_processor_interface == ONYX_INTF) &&
- (interface_type != ONYX_INTF)))
+ if (((perf_processor_interface == CUDA_INTF) &&
+ (interface_type != CUDA_INTF)) ||
+ ((perf_processor_interface == ONYX_INTF) &&
+ (interface_type != ONYX_INTF)))
return -EINVAL;
/* Next check to make sure the requested image
is valid */
- if (((interface_type == CUDA_INTF) &&
+ if (((interface_type == CUDA_INTF) &&
(test >= MAX_CUDA_IMAGES)) ||
- ((interface_type == ONYX_INTF) &&
- (test >= MAX_ONYX_IMAGES)))
+ ((interface_type == ONYX_INTF) &&
+ (test >= MAX_ONYX_IMAGES)))
return -EINVAL;
/* Copy the image into the processor */
- if (interface_type == CUDA_INTF)
+ if (interface_type == CUDA_INTF)
return perf_config(cuda_images[test]);
else
return perf_config(onyx_images[test]);
@@ -360,7 +360,7 @@
static void perf_patch_images(void)
{
#if 0 /* FIXME!! */
-/*
+/*
* NOTE: this routine is VERY specific to the current TLB image.
* If the image is changed, this routine might also need to be changed.
*/
@@ -368,9 +368,9 @@
extern void $i_dtlb_miss_2_0();
extern void PA2_0_iva();
- /*
+ /*
* We can only use the lower 32-bits, the upper 32-bits should be 0
- * anyway given this is in the kernel
+ * anyway given this is in the kernel
*/
uint32_t itlb_addr = (uint32_t)&($i_itlb_miss_2_0);
uint32_t dtlb_addr = (uint32_t)&($i_dtlb_miss_2_0);
@@ -378,21 +378,21 @@
if (perf_processor_interface == ONYX_INTF) {
/* clear last 2 bytes */
- onyx_images[TLBMISS][15] &= 0xffffff00;
+ onyx_images[TLBMISS][15] &= 0xffffff00;
/* set 2 bytes */
onyx_images[TLBMISS][15] |= (0x000000ff&((dtlb_addr) >> 24));
onyx_images[TLBMISS][16] = (dtlb_addr << 8)&0xffffff00;
onyx_images[TLBMISS][17] = itlb_addr;
/* clear last 2 bytes */
- onyx_images[TLBHANDMISS][15] &= 0xffffff00;
+ onyx_images[TLBHANDMISS][15] &= 0xffffff00;
/* set 2 bytes */
onyx_images[TLBHANDMISS][15] |= (0x000000ff&((dtlb_addr) >> 24));
onyx_images[TLBHANDMISS][16] = (dtlb_addr << 8)&0xffffff00;
onyx_images[TLBHANDMISS][17] = itlb_addr;
/* clear last 2 bytes */
- onyx_images[BIG_CPI][15] &= 0xffffff00;
+ onyx_images[BIG_CPI][15] &= 0xffffff00;
/* set 2 bytes */
onyx_images[BIG_CPI][15] |= (0x000000ff&((dtlb_addr) >> 24));
onyx_images[BIG_CPI][16] = (dtlb_addr << 8)&0xffffff00;
@@ -405,24 +405,24 @@
} else if (perf_processor_interface == CUDA_INTF) {
/* Cuda interface */
- cuda_images[TLBMISS][16] =
+ cuda_images[TLBMISS][16] =
(cuda_images[TLBMISS][16]&0xffff0000) |
((dtlb_addr >> 8)&0x0000ffff);
- cuda_images[TLBMISS][17] =
+ cuda_images[TLBMISS][17] =
((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff);
cuda_images[TLBMISS][18] = (itlb_addr << 16)&0xffff0000;
- cuda_images[TLBHANDMISS][16] =
+ cuda_images[TLBHANDMISS][16] =
(cuda_images[TLBHANDMISS][16]&0xffff0000) |
((dtlb_addr >> 8)&0x0000ffff);
- cuda_images[TLBHANDMISS][17] =
+ cuda_images[TLBHANDMISS][17] =
((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff);
cuda_images[TLBHANDMISS][18] = (itlb_addr << 16)&0xffff0000;
- cuda_images[BIG_CPI][16] =
+ cuda_images[BIG_CPI][16] =
(cuda_images[BIG_CPI][16]&0xffff0000) |
((dtlb_addr >> 8)&0x0000ffff);
- cuda_images[BIG_CPI][17] =
+ cuda_images[BIG_CPI][17] =
((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff);
cuda_images[BIG_CPI][18] = (itlb_addr << 16)&0xffff0000;
} else {
@@ -434,7 +434,7 @@
/*
* ioctl routine
- * All routines effect the processor that they are executed on. Thus you
+ * All routines effect the processor that they are executed on. Thus you
* must be running on the processor that you wish to change.
*/
@@ -460,7 +460,7 @@
}
/* copy out the Counters */
- if (copy_to_user((void __user *)arg, raddr,
+ if (copy_to_user((void __user *)arg, raddr,
sizeof (raddr)) != 0) {
error = -EFAULT;
break;
@@ -488,7 +488,7 @@
.open = perf_open,
.release = perf_release
};
-
+
static struct miscdevice perf_dev = {
MISC_DYNAMIC_MINOR,
PA_PERF_DEV,
@@ -596,7 +596,7 @@
/* OR sticky2 (bit 1496) to counter2 bit 32 */
tmp64 |= (userbuf[23] >> 8) & 0x0000000080000000;
raddr[2] = (uint32_t)tmp64;
-
+
/* Counter3 is bits 1497 to 1528 */
tmp64 = (userbuf[23] >> 7) & 0x00000000ffffffff;
/* OR sticky3 (bit 1529) to counter3 bit 32 */
@@ -618,7 +618,7 @@
userbuf[22] = 0;
userbuf[23] = 0;
- /*
+ /*
* Write back the zeroed bytes + the image given
* the read was destructive.
*/
@@ -626,13 +626,13 @@
} else {
/*
- * Read RDR-15 which contains the counters and sticky bits
+ * Read RDR-15 which contains the counters and sticky bits
*/
if (!perf_rdr_read_ubuf(15, userbuf)) {
return -13;
}
- /*
+ /*
* Clear out the counters
*/
perf_rdr_clear(15);
@@ -645,7 +645,7 @@
raddr[2] = (uint32_t)((userbuf[1] >> 32) & 0x00000000ffffffffUL);
raddr[3] = (uint32_t)(userbuf[1] & 0x00000000ffffffffUL);
}
-
+
return 0;
}
@@ -683,7 +683,7 @@
i = tentry->num_words;
while (i--) {
buffer[i] = 0;
- }
+ }
/* Check for bits an even number of 64 */
if ((xbits = width & 0x03f) != 0) {
@@ -809,18 +809,22 @@
}
runway = ioremap_nocache(cpu_device->hpa.start, 4096);
+ if (!runway) {
+ pr_err("perf_write_image: ioremap failed!\n");
+ return -ENOMEM;
+ }
/* Merge intrigue bits into Runway STATUS 0 */
tmp64 = __raw_readq(runway + RUNWAY_STATUS) & 0xffecfffffffffffful;
- __raw_writeq(tmp64 | (*memaddr++ & 0x0013000000000000ul),
+ __raw_writeq(tmp64 | (*memaddr++ & 0x0013000000000000ul),
runway + RUNWAY_STATUS);
-
+
/* Write RUNWAY DEBUG registers */
for (i = 0; i < 8; i++) {
__raw_writeq(*memaddr++, runway + RUNWAY_DEBUG);
}
- return 0;
+ return 0;
}
/*
@@ -844,7 +848,7 @@
perf_rdr_shift_out_U(rdr_num, buffer[i]);
} else {
perf_rdr_shift_out_W(rdr_num, buffer[i]);
- }
+ }
}
printk("perf_rdr_write done\n");
}
diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c
index b2da7c8..292458b 100644
--- a/arch/powerpc/kernel/align.c
+++ b/arch/powerpc/kernel/align.c
@@ -235,6 +235,28 @@
#define SWIZ_PTR(p) ((unsigned char __user *)((p) ^ swiz))
+#define __get_user_or_set_dar(_regs, _dest, _addr) \
+ ({ \
+ int rc = 0; \
+ typeof(_addr) __addr = (_addr); \
+ if (__get_user_inatomic(_dest, __addr)) { \
+ _regs->dar = (unsigned long)__addr; \
+ rc = -EFAULT; \
+ } \
+ rc; \
+ })
+
+#define __put_user_or_set_dar(_regs, _src, _addr) \
+ ({ \
+ int rc = 0; \
+ typeof(_addr) __addr = (_addr); \
+ if (__put_user_inatomic(_src, __addr)) { \
+ _regs->dar = (unsigned long)__addr; \
+ rc = -EFAULT; \
+ } \
+ rc; \
+ })
+
static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr,
unsigned int reg, unsigned int nb,
unsigned int flags, unsigned int instr,
@@ -263,9 +285,10 @@
} else {
unsigned long pc = regs->nip ^ (swiz & 4);
- if (__get_user_inatomic(instr,
- (unsigned int __user *)pc))
+ if (__get_user_or_set_dar(regs, instr,
+ (unsigned int __user *)pc))
return -EFAULT;
+
if (swiz == 0 && (flags & SW))
instr = cpu_to_le32(instr);
nb = (instr >> 11) & 0x1f;
@@ -309,31 +332,31 @@
((nb0 + 3) / 4) * sizeof(unsigned long));
for (i = 0; i < nb; ++i, ++p)
- if (__get_user_inatomic(REG_BYTE(rptr, i ^ bswiz),
- SWIZ_PTR(p)))
+ if (__get_user_or_set_dar(regs, REG_BYTE(rptr, i ^ bswiz),
+ SWIZ_PTR(p)))
return -EFAULT;
if (nb0 > 0) {
rptr = ®s->gpr[0];
addr += nb;
for (i = 0; i < nb0; ++i, ++p)
- if (__get_user_inatomic(REG_BYTE(rptr,
- i ^ bswiz),
- SWIZ_PTR(p)))
+ if (__get_user_or_set_dar(regs,
+ REG_BYTE(rptr, i ^ bswiz),
+ SWIZ_PTR(p)))
return -EFAULT;
}
} else {
for (i = 0; i < nb; ++i, ++p)
- if (__put_user_inatomic(REG_BYTE(rptr, i ^ bswiz),
- SWIZ_PTR(p)))
+ if (__put_user_or_set_dar(regs, REG_BYTE(rptr, i ^ bswiz),
+ SWIZ_PTR(p)))
return -EFAULT;
if (nb0 > 0) {
rptr = ®s->gpr[0];
addr += nb;
for (i = 0; i < nb0; ++i, ++p)
- if (__put_user_inatomic(REG_BYTE(rptr,
- i ^ bswiz),
- SWIZ_PTR(p)))
+ if (__put_user_or_set_dar(regs,
+ REG_BYTE(rptr, i ^ bswiz),
+ SWIZ_PTR(p)))
return -EFAULT;
}
}
@@ -345,29 +368,32 @@
* Only POWER6 has these instructions, and it does true little-endian,
* so we don't need the address swizzling.
*/
-static int emulate_fp_pair(unsigned char __user *addr, unsigned int reg,
- unsigned int flags)
+static int emulate_fp_pair(struct pt_regs *regs, unsigned char __user *addr,
+ unsigned int reg, unsigned int flags)
{
char *ptr0 = (char *) ¤t->thread.TS_FPR(reg);
char *ptr1 = (char *) ¤t->thread.TS_FPR(reg+1);
- int i, ret, sw = 0;
+ int i, sw = 0;
if (reg & 1)
return 0; /* invalid form: FRS/FRT must be even */
if (flags & SW)
sw = 7;
- ret = 0;
+
for (i = 0; i < 8; ++i) {
if (!(flags & ST)) {
- ret |= __get_user(ptr0[i^sw], addr + i);
- ret |= __get_user(ptr1[i^sw], addr + i + 8);
+ if (__get_user_or_set_dar(regs, ptr0[i^sw], addr + i))
+ return -EFAULT;
+ if (__get_user_or_set_dar(regs, ptr1[i^sw], addr + i + 8))
+ return -EFAULT;
} else {
- ret |= __put_user(ptr0[i^sw], addr + i);
- ret |= __put_user(ptr1[i^sw], addr + i + 8);
+ if (__put_user_or_set_dar(regs, ptr0[i^sw], addr + i))
+ return -EFAULT;
+ if (__put_user_or_set_dar(regs, ptr1[i^sw], addr + i + 8))
+ return -EFAULT;
}
}
- if (ret)
- return -EFAULT;
+
return 1; /* exception handled and fixed up */
}
@@ -377,24 +403,27 @@
{
char *ptr0 = (char *)®s->gpr[reg];
char *ptr1 = (char *)®s->gpr[reg+1];
- int i, ret, sw = 0;
+ int i, sw = 0;
if (reg & 1)
return 0; /* invalid form: GPR must be even */
if (flags & SW)
sw = 7;
- ret = 0;
+
for (i = 0; i < 8; ++i) {
if (!(flags & ST)) {
- ret |= __get_user(ptr0[i^sw], addr + i);
- ret |= __get_user(ptr1[i^sw], addr + i + 8);
+ if (__get_user_or_set_dar(regs, ptr0[i^sw], addr + i))
+ return -EFAULT;
+ if (__get_user_or_set_dar(regs, ptr1[i^sw], addr + i + 8))
+ return -EFAULT;
} else {
- ret |= __put_user(ptr0[i^sw], addr + i);
- ret |= __put_user(ptr1[i^sw], addr + i + 8);
+ if (__put_user_or_set_dar(regs, ptr0[i^sw], addr + i))
+ return -EFAULT;
+ if (__put_user_or_set_dar(regs, ptr1[i^sw], addr + i + 8))
+ return -EFAULT;
}
}
- if (ret)
- return -EFAULT;
+
return 1; /* exception handled and fixed up */
}
#endif /* CONFIG_PPC64 */
@@ -687,9 +716,14 @@
for (j = 0; j < length; j += elsize) {
for (i = 0; i < elsize; ++i) {
if (flags & ST)
- ret |= __put_user(ptr[i^sw], addr + i);
+ ret = __put_user_or_set_dar(regs, ptr[i^sw],
+ addr + i);
else
- ret |= __get_user(ptr[i^sw], addr + i);
+ ret = __get_user_or_set_dar(regs, ptr[i^sw],
+ addr + i);
+
+ if (ret)
+ return ret;
}
ptr += elsize;
#ifdef __LITTLE_ENDIAN__
@@ -739,7 +773,7 @@
unsigned int dsisr;
unsigned char __user *addr;
unsigned long p, swiz;
- int ret, i;
+ int i;
union data {
u64 ll;
double dd;
@@ -936,7 +970,7 @@
if (flags & F) {
/* Special case for 16-byte FP loads and stores */
PPC_WARN_ALIGNMENT(fp_pair, regs);
- return emulate_fp_pair(addr, reg, flags);
+ return emulate_fp_pair(regs, addr, reg, flags);
} else {
#ifdef CONFIG_PPC64
/* Special case for 16-byte loads and stores */
@@ -966,15 +1000,12 @@
}
data.ll = 0;
- ret = 0;
p = (unsigned long)addr;
for (i = 0; i < nb; i++)
- ret |= __get_user_inatomic(data.v[start + i],
- SWIZ_PTR(p++));
-
- if (unlikely(ret))
- return -EFAULT;
+ if (__get_user_or_set_dar(regs, data.v[start + i],
+ SWIZ_PTR(p++)))
+ return -EFAULT;
} else if (flags & F) {
data.ll = current->thread.TS_FPR(reg);
@@ -1046,15 +1077,13 @@
break;
}
- ret = 0;
p = (unsigned long)addr;
for (i = 0; i < nb; i++)
- ret |= __put_user_inatomic(data.v[start + i],
- SWIZ_PTR(p++));
+ if (__put_user_or_set_dar(regs, data.v[start + i],
+ SWIZ_PTR(p++)))
+ return -EFAULT;
- if (unlikely(ret))
- return -EFAULT;
} else if (flags & F)
current->thread.TS_FPR(reg) = data.ll;
else
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index 767ef6d..caa6596 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -1235,10 +1235,14 @@
stdu r1,-SWITCH_FRAME_SIZE(r1)
/* Save all gprs to pt_regs */
- SAVE_8GPRS(0,r1)
- SAVE_8GPRS(8,r1)
- SAVE_8GPRS(16,r1)
- SAVE_8GPRS(24,r1)
+ SAVE_GPR(0, r1)
+ SAVE_10GPRS(2, r1)
+ SAVE_10GPRS(12, r1)
+ SAVE_10GPRS(22, r1)
+
+ /* Save previous stack pointer (r1) */
+ addi r8, r1, SWITCH_FRAME_SIZE
+ std r8, GPR1(r1)
/* Load special regs for save below */
mfmsr r8
@@ -1292,10 +1296,10 @@
#endif
/* Restore gprs */
- REST_8GPRS(0,r1)
- REST_8GPRS(8,r1)
- REST_8GPRS(16,r1)
- REST_8GPRS(24,r1)
+ REST_GPR(0,r1)
+ REST_10GPRS(2,r1)
+ REST_10GPRS(12,r1)
+ REST_10GPRS(22,r1)
/* Restore callee's TOC */
ld r2, 24(r1)
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index 2e2fc1e..fd68e19 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -764,7 +764,29 @@
EXC_VIRT(program_check, 0x4700, 0x4800, 0x700)
TRAMP_KVM(PACA_EXGEN, 0x700)
EXC_COMMON_BEGIN(program_check_common)
- EXCEPTION_PROLOG_COMMON(0x700, PACA_EXGEN)
+ /*
+ * It's possible to receive a TM Bad Thing type program check with
+ * userspace register values (in particular r1), but with SRR1 reporting
+ * that we came from the kernel. Normally that would confuse the bad
+ * stack logic, and we would report a bad kernel stack pointer. Instead
+ * we switch to the emergency stack if we're taking a TM Bad Thing from
+ * the kernel.
+ */
+ li r10,MSR_PR /* Build a mask of MSR_PR .. */
+ oris r10,r10,0x200000@h /* .. and SRR1_PROGTM */
+ and r10,r10,r12 /* Mask SRR1 with that. */
+ srdi r10,r10,8 /* Shift it so we can compare */
+ cmpldi r10,(0x200000 >> 8) /* .. with an immediate. */
+ bne 1f /* If != go to normal path. */
+
+ /* SRR1 had PR=0 and SRR1_PROGTM=1, so use the emergency stack */
+ andi. r10,r12,MSR_PR; /* Set CR0 correctly for label */
+ /* 3 in EXCEPTION_PROLOG_COMMON */
+ mr r10,r1 /* Save r1 */
+ ld r1,PACAEMERGSP(r13) /* Use emergency stack */
+ subi r1,r1,INT_FRAME_SIZE /* alloc stack frame */
+ b 3f /* Jump into the macro !! */
+1: EXCEPTION_PROLOG_COMMON(0x700, PACA_EXGEN)
bl save_nvgprs
RECONCILE_IRQ_STATE(r10, r11)
addi r3,r1,STACK_FRAME_OVERHEAD
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index dcbb914..d973708 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -131,7 +131,7 @@
* in the appropriate thread structures from live.
*/
- if (tsk != current)
+ if ((!cpu_has_feature(CPU_FTR_TM)) || (tsk != current))
return;
if (MSR_TM_SUSPENDED(mfmsr())) {
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index 96698fd..04e9225 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -452,9 +452,20 @@
if (MSR_TM_RESV(msr))
return -EINVAL;
- /* pull in MSR TM from user context */
+ /* pull in MSR TS bits from user context */
regs->msr = (regs->msr & ~MSR_TS_MASK) | (msr & MSR_TS_MASK);
+ /*
+ * Ensure that TM is enabled in regs->msr before we leave the signal
+ * handler. It could be the case that (a) user disabled the TM bit
+ * through the manipulation of the MSR bits in uc_mcontext or (b) the
+ * TM bit was disabled because a sufficient number of context switches
+ * happened whilst in the signal handler and load_tm overflowed,
+ * disabling the TM bit. In either case we can end up with an illegal
+ * TM state leading to a TM Bad Thing when we return to userspace.
+ */
+ regs->msr |= MSR_TM;
+
/* pull in MSR LE from user context */
regs->msr = (regs->msr & ~MSR_LE) | (msr & MSR_LE);
diff --git a/arch/powerpc/kvm/book3s_64_vio.c b/arch/powerpc/kvm/book3s_64_vio.c
index c379ff5..da2a7ec 100644
--- a/arch/powerpc/kvm/book3s_64_vio.c
+++ b/arch/powerpc/kvm/book3s_64_vio.c
@@ -129,8 +129,11 @@
static int kvm_spapr_tce_release(struct inode *inode, struct file *filp)
{
struct kvmppc_spapr_tce_table *stt = filp->private_data;
+ struct kvm *kvm = stt->kvm;
+ mutex_lock(&kvm->lock);
list_del_rcu(&stt->list);
+ mutex_unlock(&kvm->lock);
kvm_put_kvm(stt->kvm);
@@ -150,6 +153,7 @@
struct kvm_create_spapr_tce_64 *args)
{
struct kvmppc_spapr_tce_table *stt = NULL;
+ struct kvmppc_spapr_tce_table *siter;
unsigned long npages, size;
int ret = -ENOMEM;
int i;
@@ -157,24 +161,16 @@
if (!args->size)
return -EINVAL;
- /* Check this LIOBN hasn't been previously allocated */
- list_for_each_entry(stt, &kvm->arch.spapr_tce_tables, list) {
- if (stt->liobn == args->liobn)
- return -EBUSY;
- }
-
size = args->size;
npages = kvmppc_tce_pages(size);
ret = kvmppc_account_memlimit(kvmppc_stt_pages(npages), true);
- if (ret) {
- stt = NULL;
- goto fail;
- }
+ if (ret)
+ return ret;
stt = kzalloc(sizeof(*stt) + npages * sizeof(struct page *),
GFP_KERNEL);
if (!stt)
- goto fail;
+ goto fail_acct;
stt->liobn = args->liobn;
stt->page_shift = args->page_shift;
@@ -188,24 +184,39 @@
goto fail;
}
- kvm_get_kvm(kvm);
-
mutex_lock(&kvm->lock);
- list_add_rcu(&stt->list, &kvm->arch.spapr_tce_tables);
+
+ /* Check this LIOBN hasn't been previously allocated */
+ ret = 0;
+ list_for_each_entry(siter, &kvm->arch.spapr_tce_tables, list) {
+ if (siter->liobn == args->liobn) {
+ ret = -EBUSY;
+ break;
+ }
+ }
+
+ if (!ret)
+ ret = anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops,
+ stt, O_RDWR | O_CLOEXEC);
+
+ if (ret >= 0) {
+ list_add_rcu(&stt->list, &kvm->arch.spapr_tce_tables);
+ kvm_get_kvm(kvm);
+ }
mutex_unlock(&kvm->lock);
- return anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops,
- stt, O_RDWR | O_CLOEXEC);
+ if (ret >= 0)
+ return ret;
-fail:
- if (stt) {
- for (i = 0; i < npages; i++)
- if (stt->pages[i])
- __free_page(stt->pages[i]);
+ fail:
+ for (i = 0; i < npages; i++)
+ if (stt->pages[i])
+ __free_page(stt->pages[i]);
- kfree(stt);
- }
+ kfree(stt);
+ fail_acct:
+ kvmppc_account_memlimit(kvmppc_stt_pages(npages), false);
return ret;
}
diff --git a/arch/powerpc/perf/isa207-common.h b/arch/powerpc/perf/isa207-common.h
index 4d0a4e5..8e6dd17 100644
--- a/arch/powerpc/perf/isa207-common.h
+++ b/arch/powerpc/perf/isa207-common.h
@@ -201,6 +201,10 @@
CNST_PMC_VAL(1) | CNST_PMC_VAL(2) | CNST_PMC_VAL(3) | \
CNST_PMC_VAL(4) | CNST_PMC_VAL(5) | CNST_PMC_VAL(6) | CNST_NC_VAL
+/*
+ * Lets restrict use of PMC5 for instruction counting.
+ */
+#define P9_DD1_TEST_ADDER (ISA207_TEST_ADDER | CNST_PMC_VAL(5))
/* Bits in MMCR1 for PowerISA v2.07 */
#define MMCR1_UNIT_SHIFT(pmc) (60 - (4 * ((pmc) - 1)))
diff --git a/arch/powerpc/perf/power9-pmu.c b/arch/powerpc/perf/power9-pmu.c
index 8e9a819..9abcd8f 100644
--- a/arch/powerpc/perf/power9-pmu.c
+++ b/arch/powerpc/perf/power9-pmu.c
@@ -295,7 +295,7 @@
.name = "POWER9",
.n_counter = MAX_PMU_COUNTERS,
.add_fields = ISA207_ADD_FIELDS,
- .test_adder = ISA207_TEST_ADDER,
+ .test_adder = P9_DD1_TEST_ADDER,
.compute_mmcr = isa207_compute_mmcr,
.config_bhrb = power9_config_bhrb,
.bhrb_filter_map = power9_bhrb_filter_map,
diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c
index a560a98..6a5e746 100644
--- a/arch/powerpc/platforms/pseries/mobility.c
+++ b/arch/powerpc/platforms/pseries/mobility.c
@@ -225,8 +225,10 @@
return -ENOENT;
dn = dlpar_configure_connector(drc_index, parent_dn);
- if (!dn)
+ if (!dn) {
+ of_node_put(parent_dn);
return -ENOENT;
+ }
rc = dlpar_attach_node(dn);
if (rc)
diff --git a/arch/s390/include/asm/mmu.h b/arch/s390/include/asm/mmu.h
index bea785d..af85d6b 100644
--- a/arch/s390/include/asm/mmu.h
+++ b/arch/s390/include/asm/mmu.h
@@ -5,6 +5,7 @@
#include <linux/errno.h>
typedef struct {
+ spinlock_t lock;
cpumask_t cpu_attach_mask;
atomic_t flush_count;
unsigned int flush_mm;
@@ -25,6 +26,7 @@
} mm_context_t;
#define INIT_MM_CONTEXT(name) \
+ .context.lock = __SPIN_LOCK_UNLOCKED(name.context.lock), \
.context.pgtable_lock = \
__SPIN_LOCK_UNLOCKED(name.context.pgtable_lock), \
.context.pgtable_list = LIST_HEAD_INIT(name.context.pgtable_list), \
diff --git a/arch/s390/include/asm/mmu_context.h b/arch/s390/include/asm/mmu_context.h
index 515fea5..f65a708 100644
--- a/arch/s390/include/asm/mmu_context.h
+++ b/arch/s390/include/asm/mmu_context.h
@@ -15,6 +15,7 @@
static inline int init_new_context(struct task_struct *tsk,
struct mm_struct *mm)
{
+ spin_lock_init(&mm->context.lock);
spin_lock_init(&mm->context.pgtable_lock);
INIT_LIST_HEAD(&mm->context.pgtable_list);
spin_lock_init(&mm->context.gmap_lock);
@@ -93,7 +94,6 @@
if (prev == next)
return;
cpumask_set_cpu(cpu, &next->context.cpu_attach_mask);
- cpumask_set_cpu(cpu, mm_cpumask(next));
/* Clear old ASCE by loading the kernel ASCE. */
__ctl_load(S390_lowcore.kernel_asce, 1, 1);
__ctl_load(S390_lowcore.kernel_asce, 7, 7);
@@ -111,9 +111,8 @@
preempt_disable();
while (atomic_read(&mm->context.flush_count))
cpu_relax();
-
- if (mm->context.flush_mm)
- __tlb_flush_mm(mm);
+ cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm));
+ __tlb_flush_mm_lazy(mm);
preempt_enable();
}
set_fs(current->thread.mm_segment);
@@ -126,6 +125,7 @@
struct mm_struct *next)
{
switch_mm(prev, next, current);
+ cpumask_set_cpu(smp_processor_id(), mm_cpumask(next));
set_user_asce(next);
}
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h
index d33f245..db74d39 100644
--- a/arch/s390/include/asm/pgtable.h
+++ b/arch/s390/include/asm/pgtable.h
@@ -1359,7 +1359,9 @@
static inline void pmdp_invalidate(struct vm_area_struct *vma,
unsigned long addr, pmd_t *pmdp)
{
- pmdp_xchg_direct(vma->vm_mm, addr, pmdp, __pmd(_SEGMENT_ENTRY_INVALID));
+ pmd_t pmd = __pmd(pmd_val(*pmdp) | _SEGMENT_ENTRY_INVALID);
+
+ pmdp_xchg_direct(vma->vm_mm, addr, pmdp, pmd);
}
#define __HAVE_ARCH_PMDP_SET_WRPROTECT
diff --git a/arch/s390/include/asm/tlbflush.h b/arch/s390/include/asm/tlbflush.h
index 3984610..eed927a 100644
--- a/arch/s390/include/asm/tlbflush.h
+++ b/arch/s390/include/asm/tlbflush.h
@@ -43,23 +43,6 @@
* Flush TLB entries for a specific mm on all CPUs (in case gmap is used
* this implicates multiple ASCEs!).
*/
-static inline void __tlb_flush_full(struct mm_struct *mm)
-{
- preempt_disable();
- atomic_inc(&mm->context.flush_count);
- if (cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id()))) {
- /* Local TLB flush */
- __tlb_flush_local();
- } else {
- /* Global TLB flush */
- __tlb_flush_global();
- /* Reset TLB flush mask */
- cpumask_copy(mm_cpumask(mm), &mm->context.cpu_attach_mask);
- }
- atomic_dec(&mm->context.flush_count);
- preempt_enable();
-}
-
static inline void __tlb_flush_mm(struct mm_struct *mm)
{
unsigned long gmap_asce;
@@ -71,16 +54,18 @@
*/
preempt_disable();
atomic_inc(&mm->context.flush_count);
+ /* Reset TLB flush mask */
+ cpumask_copy(mm_cpumask(mm), &mm->context.cpu_attach_mask);
+ barrier();
gmap_asce = READ_ONCE(mm->context.gmap_asce);
if (MACHINE_HAS_IDTE && gmap_asce != -1UL) {
if (gmap_asce)
__tlb_flush_idte(gmap_asce);
__tlb_flush_idte(mm->context.asce);
} else {
- __tlb_flush_full(mm);
+ /* Global TLB flush */
+ __tlb_flush_global();
}
- /* Reset TLB flush mask */
- cpumask_copy(mm_cpumask(mm), &mm->context.cpu_attach_mask);
atomic_dec(&mm->context.flush_count);
preempt_enable();
}
@@ -94,7 +79,6 @@
}
#else
#define __tlb_flush_global() __tlb_flush_local()
-#define __tlb_flush_full(mm) __tlb_flush_local()
/*
* Flush TLB entries for a specific ASCE on all CPUs.
@@ -112,10 +96,12 @@
static inline void __tlb_flush_mm_lazy(struct mm_struct * mm)
{
+ spin_lock(&mm->context.lock);
if (mm->context.flush_mm) {
- __tlb_flush_mm(mm);
mm->context.flush_mm = 0;
+ __tlb_flush_mm(mm);
}
+ spin_unlock(&mm->context.lock);
}
/*
diff --git a/arch/s390/mm/gup.c b/arch/s390/mm/gup.c
index 18d4107e..97fc449 100644
--- a/arch/s390/mm/gup.c
+++ b/arch/s390/mm/gup.c
@@ -56,13 +56,12 @@
static inline int gup_huge_pmd(pmd_t *pmdp, pmd_t pmd, unsigned long addr,
unsigned long end, int write, struct page **pages, int *nr)
{
- unsigned long mask, result;
struct page *head, *page;
+ unsigned long mask;
int refs;
- result = write ? 0 : _SEGMENT_ENTRY_PROTECT;
- mask = result | _SEGMENT_ENTRY_INVALID;
- if ((pmd_val(pmd) & mask) != result)
+ mask = (write ? _SEGMENT_ENTRY_PROTECT : 0) | _SEGMENT_ENTRY_INVALID;
+ if ((pmd_val(pmd) & mask) != 0)
return 0;
VM_BUG_ON(!pfn_valid(pmd_val(pmd) >> PAGE_SHIFT));
diff --git a/arch/sparc/include/asm/setup.h b/arch/sparc/include/asm/setup.h
index be0cc1b..3fae200 100644
--- a/arch/sparc/include/asm/setup.h
+++ b/arch/sparc/include/asm/setup.h
@@ -59,8 +59,11 @@
extern atomic_t dcpage_flushes_xcall;
extern int sysctl_tsb_ratio;
-#endif
+#ifdef CONFIG_SERIAL_SUNHV
+void sunhv_migrate_hvcons_irq(int cpu);
+#endif
+#endif
void sun_do_break(void);
extern int stop_a_enabled;
extern int scons_pwroff;
diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c
index 2deb89e..ca7cb8e 100644
--- a/arch/sparc/kernel/smp_64.c
+++ b/arch/sparc/kernel/smp_64.c
@@ -1465,8 +1465,12 @@
int cpu;
if (tlb_type == hypervisor) {
+ int this_cpu = smp_processor_id();
+#ifdef CONFIG_SERIAL_SUNHV
+ sunhv_migrate_hvcons_irq(this_cpu);
+#endif
for_each_online_cpu(cpu) {
- if (cpu == smp_processor_id())
+ if (cpu == this_cpu)
continue;
#ifdef CONFIG_SUN_LDOMS
if (ldom_domaining_enabled) {
diff --git a/arch/x86/include/asm/alternative-asm.h b/arch/x86/include/asm/alternative-asm.h
index e7636ba..6c98821 100644
--- a/arch/x86/include/asm/alternative-asm.h
+++ b/arch/x86/include/asm/alternative-asm.h
@@ -62,8 +62,10 @@
#define new_len2 145f-144f
/*
- * max without conditionals. Idea adapted from:
+ * gas compatible max based on the idea from:
* http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
+ *
+ * The additional "-" is needed because gas uses a "true" value of -1.
*/
#define alt_max_short(a, b) ((a) ^ (((a) ^ (b)) & -(-((a) < (b)))))
diff --git a/arch/x86/include/asm/alternative.h b/arch/x86/include/asm/alternative.h
index 1b02038..d4aea31 100644
--- a/arch/x86/include/asm/alternative.h
+++ b/arch/x86/include/asm/alternative.h
@@ -103,12 +103,12 @@
alt_end_marker ":\n"
/*
- * max without conditionals. Idea adapted from:
+ * gas compatible max based on the idea from:
* http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
*
- * The additional "-" is needed because gas works with s32s.
+ * The additional "-" is needed because gas uses a "true" value of -1.
*/
-#define alt_max_short(a, b) "((" a ") ^ (((" a ") ^ (" b ")) & -(-((" a ") - (" b ")))))"
+#define alt_max_short(a, b) "((" a ") ^ (((" a ") ^ (" b ")) & -(-((" a ") < (" b ")))))"
/*
* Pad the second replacement alternative with additional NOPs if it is
diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h
index 19d14ac..fc3c7e4 100644
--- a/arch/x86/include/asm/kvm_emulate.h
+++ b/arch/x86/include/asm/kvm_emulate.h
@@ -296,6 +296,7 @@
bool perm_ok; /* do not check permissions if true */
bool ud; /* inject an #UD if host doesn't support insn */
+ bool tf; /* TF value before instruction (after for syscall/sysret) */
bool have_exception;
struct x86_exception exception;
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index d3e0d04..b89bef9 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -176,10 +176,15 @@
return -EINVAL;
}
+ if (!enabled) {
+ ++disabled_cpus;
+ return -EINVAL;
+ }
+
if (boot_cpu_physical_apicid != -1U)
ver = boot_cpu_apic_version;
- cpu = __generic_processor_info(id, ver, enabled);
+ cpu = generic_processor_info(id, ver);
if (cpu >= 0)
early_per_cpu(x86_cpu_to_acpiid, cpu) = acpiid;
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index f223491..e2ead34 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -2070,7 +2070,7 @@
return nr_logical_cpuids++;
}
-int __generic_processor_info(int apicid, int version, bool enabled)
+int generic_processor_info(int apicid, int version)
{
int cpu, max = nr_cpu_ids;
bool boot_cpu_detected = physid_isset(boot_cpu_physical_apicid,
@@ -2128,11 +2128,9 @@
if (num_processors >= nr_cpu_ids) {
int thiscpu = max + disabled_cpus;
- if (enabled) {
- pr_warning("APIC: NR_CPUS/possible_cpus limit of %i "
- "reached. Processor %d/0x%x ignored.\n",
- max, thiscpu, apicid);
- }
+ pr_warning("APIC: NR_CPUS/possible_cpus limit of %i "
+ "reached. Processor %d/0x%x ignored.\n",
+ max, thiscpu, apicid);
disabled_cpus++;
return -EINVAL;
@@ -2184,23 +2182,13 @@
apic->x86_32_early_logical_apicid(cpu);
#endif
set_cpu_possible(cpu, true);
-
- if (enabled) {
- num_processors++;
- physid_set(apicid, phys_cpu_present_map);
- set_cpu_present(cpu, true);
- } else {
- disabled_cpus++;
- }
+ physid_set(apicid, phys_cpu_present_map);
+ set_cpu_present(cpu, true);
+ num_processors++;
return cpu;
}
-int generic_processor_info(int apicid, int version)
-{
- return __generic_processor_info(apicid, version, true);
-}
-
int hard_smp_processor_id(void)
{
return read_apic_id();
diff --git a/arch/x86/kernel/fpu/regset.c b/arch/x86/kernel/fpu/regset.c
index c114b13..7052d9a 100644
--- a/arch/x86/kernel/fpu/regset.c
+++ b/arch/x86/kernel/fpu/regset.c
@@ -130,11 +130,16 @@
fpu__activate_fpstate_write(fpu);
- if (boot_cpu_has(X86_FEATURE_XSAVES))
+ if (boot_cpu_has(X86_FEATURE_XSAVES)) {
ret = copyin_to_xsaves(kbuf, ubuf, xsave);
- else
+ } else {
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, xsave, 0, -1);
+ /* xcomp_bv must be 0 when using uncompacted format */
+ if (!ret && xsave->header.xcomp_bv)
+ ret = -EINVAL;
+ }
+
/*
* In case of failure, mark all states as init:
*/
diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c
index a184c21..3ec0d2d 100644
--- a/arch/x86/kernel/fpu/signal.c
+++ b/arch/x86/kernel/fpu/signal.c
@@ -329,6 +329,10 @@
} else {
err = __copy_from_user(&fpu->state.xsave,
buf_fx, state_size);
+
+ /* xcomp_bv must be 0 when using uncompacted format */
+ if (!err && state_size > offsetof(struct xregs_state, header) && fpu->state.xsave.header.xcomp_bv)
+ err = -EINVAL;
}
if (err || __copy_from_user(&env, buf, sizeof(env))) {
diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c
index 55ffd9d..77f17cb 100644
--- a/arch/x86/kernel/kvm.c
+++ b/arch/x86/kernel/kvm.c
@@ -141,7 +141,8 @@
n.token = token;
n.cpu = smp_processor_id();
- n.halted = is_idle_task(current) || preempt_count() > 1;
+ n.halted = is_idle_task(current) || preempt_count() > 1 ||
+ rcu_preempt_depth();
init_swait_queue_head(&n.wq);
hlist_add_head(&n.link, &b->list);
raw_spin_unlock(&b->lock);
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index de36660..72b737b 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -2738,6 +2738,7 @@
ctxt->eflags &= ~(X86_EFLAGS_VM | X86_EFLAGS_IF);
}
+ ctxt->tf = (ctxt->eflags & X86_EFLAGS_TF) != 0;
return X86EMUL_CONTINUE;
}
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index 5f24127..d29c745 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -3649,19 +3649,19 @@
unsigned level, unsigned gpte)
{
/*
- * PT_PAGE_TABLE_LEVEL always terminates. The RHS has bit 7 set
- * iff level <= PT_PAGE_TABLE_LEVEL, which for our purpose means
- * level == PT_PAGE_TABLE_LEVEL; set PT_PAGE_SIZE_MASK in gpte then.
- */
- gpte |= level - PT_PAGE_TABLE_LEVEL - 1;
-
- /*
* The RHS has bit 7 set iff level < mmu->last_nonleaf_level.
* If it is clear, there are no large pages at this level, so clear
* PT_PAGE_SIZE_MASK in gpte if that is the case.
*/
gpte &= level - mmu->last_nonleaf_level;
+ /*
+ * PT_PAGE_TABLE_LEVEL always terminates. The RHS has bit 7 set
+ * iff level <= PT_PAGE_TABLE_LEVEL, which for our purpose means
+ * level == PT_PAGE_TABLE_LEVEL; set PT_PAGE_SIZE_MASK in gpte then.
+ */
+ gpte |= level - PT_PAGE_TABLE_LEVEL - 1;
+
return gpte & PT_PAGE_SIZE_MASK;
}
@@ -4169,6 +4169,7 @@
update_permission_bitmask(vcpu, context, true);
update_pkru_bitmask(vcpu, context, true);
+ update_last_nonleaf_level(vcpu, context);
reset_rsvds_bits_mask_ept(vcpu, context, execonly);
reset_ept_shadow_zero_bits_mask(vcpu, context, execonly);
}
diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h
index a011054..3736390 100644
--- a/arch/x86/kvm/paging_tmpl.h
+++ b/arch/x86/kvm/paging_tmpl.h
@@ -324,10 +324,11 @@
--walker->level;
index = PT_INDEX(addr, walker->level);
-
table_gfn = gpte_to_gfn(pte);
offset = index * sizeof(pt_element_t);
pte_gpa = gfn_to_gpa(table_gfn) + offset;
+
+ BUG_ON(walker->level < 1);
walker->table_gfn[walker->level - 1] = table_gfn;
walker->pte_gpa[walker->level - 1] = pte_gpa;
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 3dc6d80..a8ae57a 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -2167,46 +2167,44 @@
struct pi_desc old, new;
unsigned int dest;
- if (!kvm_arch_has_assigned_device(vcpu->kvm) ||
- !irq_remapping_cap(IRQ_POSTING_CAP) ||
- !kvm_vcpu_apicv_active(vcpu))
+ /*
+ * In case of hot-plug or hot-unplug, we may have to undo
+ * vmx_vcpu_pi_put even if there is no assigned device. And we
+ * always keep PI.NDST up to date for simplicity: it makes the
+ * code easier, and CPU migration is not a fast path.
+ */
+ if (!pi_test_sn(pi_desc) && vcpu->cpu == cpu)
return;
+ /*
+ * First handle the simple case where no cmpxchg is necessary; just
+ * allow posting non-urgent interrupts.
+ *
+ * If the 'nv' field is POSTED_INTR_WAKEUP_VECTOR, do not change
+ * PI.NDST: pi_post_block will do it for us and the wakeup_handler
+ * expects the VCPU to be on the blocked_vcpu_list that matches
+ * PI.NDST.
+ */
+ if (pi_desc->nv == POSTED_INTR_WAKEUP_VECTOR ||
+ vcpu->cpu == cpu) {
+ pi_clear_sn(pi_desc);
+ return;
+ }
+
+ /* The full case. */
do {
old.control = new.control = pi_desc->control;
- /*
- * If 'nv' field is POSTED_INTR_WAKEUP_VECTOR, there
- * are two possible cases:
- * 1. After running 'pre_block', context switch
- * happened. For this case, 'sn' was set in
- * vmx_vcpu_put(), so we need to clear it here.
- * 2. After running 'pre_block', we were blocked,
- * and woken up by some other guy. For this case,
- * we don't need to do anything, 'pi_post_block'
- * will do everything for us. However, we cannot
- * check whether it is case #1 or case #2 here
- * (maybe, not needed), so we also clear sn here,
- * I think it is not a big deal.
- */
- if (pi_desc->nv != POSTED_INTR_WAKEUP_VECTOR) {
- if (vcpu->cpu != cpu) {
- dest = cpu_physical_id(cpu);
+ dest = cpu_physical_id(cpu);
- if (x2apic_enabled())
- new.ndst = dest;
- else
- new.ndst = (dest << 8) & 0xFF00;
- }
+ if (x2apic_enabled())
+ new.ndst = dest;
+ else
+ new.ndst = (dest << 8) & 0xFF00;
- /* set 'NV' to 'notification vector' */
- new.nv = POSTED_INTR_VECTOR;
- }
-
- /* Allow posting non-urgent interrupts */
new.sn = 0;
- } while (cmpxchg(&pi_desc->control, old.control,
- new.control) != old.control);
+ } while (cmpxchg64(&pi_desc->control, old.control,
+ new.control) != old.control);
}
static void decache_tsc_multiplier(struct vcpu_vmx *vmx)
@@ -4761,21 +4759,30 @@
{
#ifdef CONFIG_SMP
if (vcpu->mode == IN_GUEST_MODE) {
- struct vcpu_vmx *vmx = to_vmx(vcpu);
-
/*
- * Currently, we don't support urgent interrupt,
- * all interrupts are recognized as non-urgent
- * interrupt, so we cannot post interrupts when
- * 'SN' is set.
+ * The vector of interrupt to be delivered to vcpu had
+ * been set in PIR before this function.
*
- * If the vcpu is in guest mode, it means it is
- * running instead of being scheduled out and
- * waiting in the run queue, and that's the only
- * case when 'SN' is set currently, warning if
- * 'SN' is set.
+ * Following cases will be reached in this block, and
+ * we always send a notification event in all cases as
+ * explained below.
+ *
+ * Case 1: vcpu keeps in non-root mode. Sending a
+ * notification event posts the interrupt to vcpu.
+ *
+ * Case 2: vcpu exits to root mode and is still
+ * runnable. PIR will be synced to vIRR before the
+ * next vcpu entry. Sending a notification event in
+ * this case has no effect, as vcpu is not in root
+ * mode.
+ *
+ * Case 3: vcpu exits to root mode and is blocked.
+ * vcpu_block() has already synced PIR to vIRR and
+ * never blocks vcpu if vIRR is not cleared. Therefore,
+ * a blocked vcpu here does not wait for any requested
+ * interrupts in PIR, and sending a notification event
+ * which has no effect is safe here.
*/
- WARN_ON_ONCE(pi_test_sn(&vmx->pi_desc));
apic->send_IPI_mask(get_cpu_mask(vcpu->cpu),
POSTED_INTR_VECTOR);
@@ -9187,6 +9194,13 @@
vmx->msr_ia32_feature_control_valid_bits = FEATURE_CONTROL_LOCKED;
+ /*
+ * Enforce invariant: pi_desc.nv is always either POSTED_INTR_VECTOR
+ * or POSTED_INTR_WAKEUP_VECTOR.
+ */
+ vmx->pi_desc.nv = POSTED_INTR_VECTOR;
+ vmx->pi_desc.sn = 1;
+
return &vmx->vcpu;
free_vmcs:
@@ -9996,6 +10010,11 @@
vmcs_write64(VIRTUAL_APIC_PAGE_ADDR,
page_to_phys(vmx->nested.virtual_apic_page));
vmcs_write32(TPR_THRESHOLD, vmcs12->tpr_threshold);
+ } else {
+#ifdef CONFIG_X86_64
+ exec_control |= CPU_BASED_CR8_LOAD_EXITING |
+ CPU_BASED_CR8_STORE_EXITING;
+#endif
}
if (cpu_has_vmx_msr_bitmap() &&
@@ -10671,7 +10690,7 @@
* (KVM doesn't change it)- no reason to call set_cr4_guest_host_mask();
*/
vcpu->arch.cr4_guest_owned_bits = ~vmcs_readl(CR4_GUEST_HOST_MASK);
- kvm_set_cr4(vcpu, vmcs12->host_cr4);
+ vmx_set_cr4(vcpu, vmcs12->host_cr4);
nested_ept_uninit_mmu_context(vcpu);
@@ -11000,6 +11019,37 @@
kvm_mmu_clear_dirty_pt_masked(kvm, memslot, offset, mask);
}
+static void __pi_post_block(struct kvm_vcpu *vcpu)
+{
+ struct pi_desc *pi_desc = vcpu_to_pi_desc(vcpu);
+ struct pi_desc old, new;
+ unsigned int dest;
+
+ do {
+ old.control = new.control = pi_desc->control;
+ WARN(old.nv != POSTED_INTR_WAKEUP_VECTOR,
+ "Wakeup handler not enabled while the VCPU is blocked\n");
+
+ dest = cpu_physical_id(vcpu->cpu);
+
+ if (x2apic_enabled())
+ new.ndst = dest;
+ else
+ new.ndst = (dest << 8) & 0xFF00;
+
+ /* set 'NV' to 'notification vector' */
+ new.nv = POSTED_INTR_VECTOR;
+ } while (cmpxchg64(&pi_desc->control, old.control,
+ new.control) != old.control);
+
+ if (!WARN_ON_ONCE(vcpu->pre_pcpu == -1)) {
+ spin_lock(&per_cpu(blocked_vcpu_on_cpu_lock, vcpu->pre_pcpu));
+ list_del(&vcpu->blocked_vcpu_list);
+ spin_unlock(&per_cpu(blocked_vcpu_on_cpu_lock, vcpu->pre_pcpu));
+ vcpu->pre_pcpu = -1;
+ }
+}
+
/*
* This routine does the following things for vCPU which is going
* to be blocked if VT-d PI is enabled.
@@ -11015,7 +11065,6 @@
*/
static int pi_pre_block(struct kvm_vcpu *vcpu)
{
- unsigned long flags;
unsigned int dest;
struct pi_desc old, new;
struct pi_desc *pi_desc = vcpu_to_pi_desc(vcpu);
@@ -11025,34 +11074,20 @@
!kvm_vcpu_apicv_active(vcpu))
return 0;
- vcpu->pre_pcpu = vcpu->cpu;
- spin_lock_irqsave(&per_cpu(blocked_vcpu_on_cpu_lock,
- vcpu->pre_pcpu), flags);
- list_add_tail(&vcpu->blocked_vcpu_list,
- &per_cpu(blocked_vcpu_on_cpu,
- vcpu->pre_pcpu));
- spin_unlock_irqrestore(&per_cpu(blocked_vcpu_on_cpu_lock,
- vcpu->pre_pcpu), flags);
+ WARN_ON(irqs_disabled());
+ local_irq_disable();
+ if (!WARN_ON_ONCE(vcpu->pre_pcpu != -1)) {
+ vcpu->pre_pcpu = vcpu->cpu;
+ spin_lock(&per_cpu(blocked_vcpu_on_cpu_lock, vcpu->pre_pcpu));
+ list_add_tail(&vcpu->blocked_vcpu_list,
+ &per_cpu(blocked_vcpu_on_cpu,
+ vcpu->pre_pcpu));
+ spin_unlock(&per_cpu(blocked_vcpu_on_cpu_lock, vcpu->pre_pcpu));
+ }
do {
old.control = new.control = pi_desc->control;
- /*
- * We should not block the vCPU if
- * an interrupt is posted for it.
- */
- if (pi_test_on(pi_desc) == 1) {
- spin_lock_irqsave(&per_cpu(blocked_vcpu_on_cpu_lock,
- vcpu->pre_pcpu), flags);
- list_del(&vcpu->blocked_vcpu_list);
- spin_unlock_irqrestore(
- &per_cpu(blocked_vcpu_on_cpu_lock,
- vcpu->pre_pcpu), flags);
- vcpu->pre_pcpu = -1;
-
- return 1;
- }
-
WARN((pi_desc->sn == 1),
"Warning: SN field of posted-interrupts "
"is set before blocking\n");
@@ -11074,10 +11109,15 @@
/* set 'NV' to 'wakeup vector' */
new.nv = POSTED_INTR_WAKEUP_VECTOR;
- } while (cmpxchg(&pi_desc->control, old.control,
- new.control) != old.control);
+ } while (cmpxchg64(&pi_desc->control, old.control,
+ new.control) != old.control);
- return 0;
+ /* We should not block the vCPU if an interrupt is posted for it. */
+ if (pi_test_on(pi_desc) == 1)
+ __pi_post_block(vcpu);
+
+ local_irq_enable();
+ return (vcpu->pre_pcpu == -1);
}
static int vmx_pre_block(struct kvm_vcpu *vcpu)
@@ -11093,44 +11133,13 @@
static void pi_post_block(struct kvm_vcpu *vcpu)
{
- struct pi_desc *pi_desc = vcpu_to_pi_desc(vcpu);
- struct pi_desc old, new;
- unsigned int dest;
- unsigned long flags;
-
- if (!kvm_arch_has_assigned_device(vcpu->kvm) ||
- !irq_remapping_cap(IRQ_POSTING_CAP) ||
- !kvm_vcpu_apicv_active(vcpu))
+ if (vcpu->pre_pcpu == -1)
return;
- do {
- old.control = new.control = pi_desc->control;
-
- dest = cpu_physical_id(vcpu->cpu);
-
- if (x2apic_enabled())
- new.ndst = dest;
- else
- new.ndst = (dest << 8) & 0xFF00;
-
- /* Allow posting non-urgent interrupts */
- new.sn = 0;
-
- /* set 'NV' to 'notification vector' */
- new.nv = POSTED_INTR_VECTOR;
- } while (cmpxchg(&pi_desc->control, old.control,
- new.control) != old.control);
-
- if(vcpu->pre_pcpu != -1) {
- spin_lock_irqsave(
- &per_cpu(blocked_vcpu_on_cpu_lock,
- vcpu->pre_pcpu), flags);
- list_del(&vcpu->blocked_vcpu_list);
- spin_unlock_irqrestore(
- &per_cpu(blocked_vcpu_on_cpu_lock,
- vcpu->pre_pcpu), flags);
- vcpu->pre_pcpu = -1;
- }
+ WARN_ON(irqs_disabled());
+ local_irq_disable();
+ __pi_post_block(vcpu);
+ local_irq_enable();
}
static void vmx_post_block(struct kvm_vcpu *vcpu)
@@ -11158,7 +11167,7 @@
struct kvm_lapic_irq irq;
struct kvm_vcpu *vcpu;
struct vcpu_data vcpu_info;
- int idx, ret = -EINVAL;
+ int idx, ret = 0;
if (!kvm_arch_has_assigned_device(kvm) ||
!irq_remapping_cap(IRQ_POSTING_CAP) ||
@@ -11167,7 +11176,12 @@
idx = srcu_read_lock(&kvm->irq_srcu);
irq_rt = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu);
- BUG_ON(guest_irq >= irq_rt->nr_rt_entries);
+ if (guest_irq >= irq_rt->nr_rt_entries ||
+ hlist_empty(&irq_rt->map[guest_irq])) {
+ pr_warn_once("no route for guest_irq %u/%u (broken user space?)\n",
+ guest_irq, irq_rt->nr_rt_entries);
+ goto out;
+ }
hlist_for_each_entry(e, &irq_rt->map[guest_irq], link) {
if (e->type != KVM_IRQ_ROUTING_MSI)
@@ -11210,12 +11224,8 @@
if (set)
ret = irq_set_vcpu_affinity(host_irq, &vcpu_info);
- else {
- /* suppress notification event before unposting */
- pi_set_sn(vcpu_to_pi_desc(vcpu));
+ else
ret = irq_set_vcpu_affinity(host_irq, NULL);
- pi_clear_sn(vcpu_to_pi_desc(vcpu));
- }
if (ret < 0) {
printk(KERN_INFO "%s: failed to update PI IRTE\n",
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 3dbcb09..595f814 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -5250,6 +5250,8 @@
kvm_x86_ops->get_cs_db_l_bits(vcpu, &cs_db, &cs_l);
ctxt->eflags = kvm_get_rflags(vcpu);
+ ctxt->tf = (ctxt->eflags & X86_EFLAGS_TF) != 0;
+
ctxt->eip = kvm_rip_read(vcpu);
ctxt->mode = (!is_protmode(vcpu)) ? X86EMUL_MODE_REAL :
(ctxt->eflags & X86_EFLAGS_VM) ? X86EMUL_MODE_VM86 :
@@ -5465,37 +5467,26 @@
return dr6;
}
-static void kvm_vcpu_check_singlestep(struct kvm_vcpu *vcpu, unsigned long rflags, int *r)
+static void kvm_vcpu_do_singlestep(struct kvm_vcpu *vcpu, int *r)
{
struct kvm_run *kvm_run = vcpu->run;
- /*
- * rflags is the old, "raw" value of the flags. The new value has
- * not been saved yet.
- *
- * This is correct even for TF set by the guest, because "the
- * processor will not generate this exception after the instruction
- * that sets the TF flag".
- */
- if (unlikely(rflags & X86_EFLAGS_TF)) {
- if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
- kvm_run->debug.arch.dr6 = DR6_BS | DR6_FIXED_1 |
- DR6_RTM;
- kvm_run->debug.arch.pc = vcpu->arch.singlestep_rip;
- kvm_run->debug.arch.exception = DB_VECTOR;
- kvm_run->exit_reason = KVM_EXIT_DEBUG;
- *r = EMULATE_USER_EXIT;
- } else {
- vcpu->arch.emulate_ctxt.eflags &= ~X86_EFLAGS_TF;
- /*
- * "Certain debug exceptions may clear bit 0-3. The
- * remaining contents of the DR6 register are never
- * cleared by the processor".
- */
- vcpu->arch.dr6 &= ~15;
- vcpu->arch.dr6 |= DR6_BS | DR6_RTM;
- kvm_queue_exception(vcpu, DB_VECTOR);
- }
+ if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
+ kvm_run->debug.arch.dr6 = DR6_BS | DR6_FIXED_1 | DR6_RTM;
+ kvm_run->debug.arch.pc = vcpu->arch.singlestep_rip;
+ kvm_run->debug.arch.exception = DB_VECTOR;
+ kvm_run->exit_reason = KVM_EXIT_DEBUG;
+ *r = EMULATE_USER_EXIT;
+ } else {
+ vcpu->arch.emulate_ctxt.eflags &= ~X86_EFLAGS_TF;
+ /*
+ * "Certain debug exceptions may clear bit 0-3. The
+ * remaining contents of the DR6 register are never
+ * cleared by the processor".
+ */
+ vcpu->arch.dr6 &= ~15;
+ vcpu->arch.dr6 |= DR6_BS | DR6_RTM;
+ kvm_queue_exception(vcpu, DB_VECTOR);
}
}
@@ -5650,8 +5641,9 @@
toggle_interruptibility(vcpu, ctxt->interruptibility);
vcpu->arch.emulate_regs_need_sync_to_vcpu = false;
kvm_rip_write(vcpu, ctxt->eip);
- if (r == EMULATE_DONE)
- kvm_vcpu_check_singlestep(vcpu, rflags, &r);
+ if (r == EMULATE_DONE &&
+ (ctxt->tf || (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)))
+ kvm_vcpu_do_singlestep(vcpu, &r);
if (!ctxt->have_exception ||
exception_type(ctxt->exception.vector) == EXCPT_TRAP)
__kvm_set_rflags(vcpu, ctxt->eflags);
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 9f72ca3..1dd7960 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -191,8 +191,7 @@
* 6. T1 : reaches here, sees vma_pkey(vma)=5, when we really
* faulted on a pte with its pkey=4.
*/
-static void fill_sig_info_pkey(int si_code, siginfo_t *info,
- struct vm_area_struct *vma)
+static void fill_sig_info_pkey(int si_code, siginfo_t *info, u32 *pkey)
{
/* This is effectively an #ifdef */
if (!boot_cpu_has(X86_FEATURE_OSPKE))
@@ -208,7 +207,7 @@
* valid VMA, so we should never reach this without a
* valid VMA.
*/
- if (!vma) {
+ if (!pkey) {
WARN_ONCE(1, "PKU fault with no VMA passed in");
info->si_pkey = 0;
return;
@@ -218,13 +217,12 @@
* absolutely guranteed to be 100% accurate because of
* the race explained above.
*/
- info->si_pkey = vma_pkey(vma);
+ info->si_pkey = *pkey;
}
static void
force_sig_info_fault(int si_signo, int si_code, unsigned long address,
- struct task_struct *tsk, struct vm_area_struct *vma,
- int fault)
+ struct task_struct *tsk, u32 *pkey, int fault)
{
unsigned lsb = 0;
siginfo_t info;
@@ -239,7 +237,7 @@
lsb = PAGE_SHIFT;
info.si_addr_lsb = lsb;
- fill_sig_info_pkey(si_code, &info, vma);
+ fill_sig_info_pkey(si_code, &info, pkey);
force_sig_info(si_signo, &info, tsk);
}
@@ -718,8 +716,6 @@
struct task_struct *tsk = current;
unsigned long flags;
int sig;
- /* No context means no VMA to pass down */
- struct vm_area_struct *vma = NULL;
/* Are we prepared to handle this kernel fault? */
if (fixup_exception(regs, X86_TRAP_PF)) {
@@ -744,7 +740,7 @@
/* XXX: hwpoison faults will set the wrong code. */
force_sig_info_fault(signal, si_code, address,
- tsk, vma, 0);
+ tsk, NULL, 0);
}
/*
@@ -853,8 +849,7 @@
static void
__bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
- unsigned long address, struct vm_area_struct *vma,
- int si_code)
+ unsigned long address, u32 *pkey, int si_code)
{
struct task_struct *tsk = current;
@@ -902,7 +897,7 @@
tsk->thread.error_code = error_code;
tsk->thread.trap_nr = X86_TRAP_PF;
- force_sig_info_fault(SIGSEGV, si_code, address, tsk, vma, 0);
+ force_sig_info_fault(SIGSEGV, si_code, address, tsk, pkey, 0);
return;
}
@@ -915,9 +910,9 @@
static noinline void
bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
- unsigned long address, struct vm_area_struct *vma)
+ unsigned long address, u32 *pkey)
{
- __bad_area_nosemaphore(regs, error_code, address, vma, SEGV_MAPERR);
+ __bad_area_nosemaphore(regs, error_code, address, pkey, SEGV_MAPERR);
}
static void
@@ -925,6 +920,10 @@
unsigned long address, struct vm_area_struct *vma, int si_code)
{
struct mm_struct *mm = current->mm;
+ u32 pkey;
+
+ if (vma)
+ pkey = vma_pkey(vma);
/*
* Something tried to access memory that isn't in our memory map..
@@ -932,7 +931,8 @@
*/
up_read(&mm->mmap_sem);
- __bad_area_nosemaphore(regs, error_code, address, vma, si_code);
+ __bad_area_nosemaphore(regs, error_code, address,
+ (vma) ? &pkey : NULL, si_code);
}
static noinline void
@@ -975,7 +975,7 @@
static void
do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address,
- struct vm_area_struct *vma, unsigned int fault)
+ u32 *pkey, unsigned int fault)
{
struct task_struct *tsk = current;
int code = BUS_ADRERR;
@@ -1002,13 +1002,12 @@
code = BUS_MCEERR_AR;
}
#endif
- force_sig_info_fault(SIGBUS, code, address, tsk, vma, fault);
+ force_sig_info_fault(SIGBUS, code, address, tsk, pkey, fault);
}
static noinline void
mm_fault_error(struct pt_regs *regs, unsigned long error_code,
- unsigned long address, struct vm_area_struct *vma,
- unsigned int fault)
+ unsigned long address, u32 *pkey, unsigned int fault)
{
if (fatal_signal_pending(current) && !(error_code & PF_USER)) {
no_context(regs, error_code, address, 0, 0);
@@ -1032,9 +1031,9 @@
} else {
if (fault & (VM_FAULT_SIGBUS|VM_FAULT_HWPOISON|
VM_FAULT_HWPOISON_LARGE))
- do_sigbus(regs, error_code, address, vma, fault);
+ do_sigbus(regs, error_code, address, pkey, fault);
else if (fault & VM_FAULT_SIGSEGV)
- bad_area_nosemaphore(regs, error_code, address, vma);
+ bad_area_nosemaphore(regs, error_code, address, pkey);
else
BUG();
}
@@ -1220,6 +1219,7 @@
struct mm_struct *mm;
int fault, major = 0;
unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
+ u32 pkey;
tsk = current;
mm = tsk->mm;
@@ -1420,9 +1420,10 @@
return;
}
+ pkey = vma_pkey(vma);
up_read(&mm->mmap_sem);
if (unlikely(fault & VM_FAULT_ERROR)) {
- mm_fault_error(regs, error_code, address, vma, fault);
+ mm_fault_error(regs, error_code, address, &pkey, fault);
return;
}
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index 9a324fc..3e27ded 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -689,7 +689,7 @@
if (PageReserved(page)) {
__ClearPageReserved(page);
- magic = (unsigned long)page->lru.next;
+ magic = (unsigned long)page->freelist;
if (magic == SECTION_INFO || magic == MIX_SECTION_INFO) {
while (nr_pages--)
put_page_bootmem(page++);
diff --git a/arch/x86/purgatory/Makefile b/arch/x86/purgatory/Makefile
index 555b9fa..7dbdb78 100644
--- a/arch/x86/purgatory/Makefile
+++ b/arch/x86/purgatory/Makefile
@@ -8,6 +8,7 @@
LDFLAGS_purgatory.ro := -e purgatory_start -r --no-undefined -nostdlib -z nodefaultlib
targets += purgatory.ro
+KASAN_SANITIZE := n
KCOV_INSTRUMENT := n
# Default KBUILD_CFLAGS can have -pg option set when FTRACE is enabled. That
diff --git a/block/bio.c b/block/bio.c
index 655c901..07f287b 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1171,8 +1171,8 @@
*/
bmd->is_our_pages = map_data ? 0 : 1;
memcpy(bmd->iov, iter->iov, sizeof(struct iovec) * iter->nr_segs);
- iov_iter_init(&bmd->iter, iter->type, bmd->iov,
- iter->nr_segs, iter->count);
+ bmd->iter = *iter;
+ bmd->iter.iov = bmd->iov;
ret = -ENOMEM;
bio = bio_kmalloc(gfp_mask, nr_pages);
@@ -1266,6 +1266,7 @@
int ret, offset;
struct iov_iter i;
struct iovec iov;
+ struct bio_vec *bvec;
iov_for_each(iov, i, *iter) {
unsigned long uaddr = (unsigned long) iov.iov_base;
@@ -1310,7 +1311,12 @@
ret = get_user_pages_fast(uaddr, local_nr_pages,
(iter->type & WRITE) != WRITE,
&pages[cur_page]);
- if (ret < local_nr_pages) {
+ if (unlikely(ret < local_nr_pages)) {
+ for (j = cur_page; j < page_limit; j++) {
+ if (!pages[j])
+ break;
+ put_page(pages[j]);
+ }
ret = -EFAULT;
goto out_unmap;
}
@@ -1318,6 +1324,7 @@
offset = offset_in_page(uaddr);
for (j = cur_page; j < page_limit; j++) {
unsigned int bytes = PAGE_SIZE - offset;
+ unsigned short prev_bi_vcnt = bio->bi_vcnt;
if (len <= 0)
break;
@@ -1332,6 +1339,13 @@
bytes)
break;
+ /*
+ * check if vector was merged with previous
+ * drop page reference if needed
+ */
+ if (bio->bi_vcnt == prev_bi_vcnt)
+ put_page(pages[j]);
+
len -= bytes;
offset = 0;
}
@@ -1364,10 +1378,8 @@
return bio;
out_unmap:
- for (j = 0; j < nr_pages; j++) {
- if (!pages[j])
- break;
- put_page(pages[j]);
+ bio_for_each_segment_all(bvec, bio, j) {
+ put_page(bvec->bv_page);
}
out:
kfree(pages);
diff --git a/block/blk-core.c b/block/blk-core.c
index d8fba67..9fc567c 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -235,7 +235,7 @@
**/
void blk_start_queue(struct request_queue *q)
{
- WARN_ON(!irqs_disabled());
+ WARN_ON(!in_interrupt() && !irqs_disabled());
queue_flag_clear(QUEUE_FLAG_STOPPED, q);
__blk_run_queue(q);
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index c7c3d4e..4ac4910 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -2948,10 +2948,11 @@
/*
* SSD device without seek penalty, disable idling. But only do so
- * for devices that support queuing, otherwise we still have a problem
- * with sync vs async workloads.
+ * for devices that support queuing (and when group idle is 0),
+ * otherwise we still have a problem with sync vs async workloads.
*/
- if (blk_queue_nonrot(cfqd->queue) && cfqd->hw_tag)
+ if (blk_queue_nonrot(cfqd->queue) && cfqd->hw_tag &&
+ !cfqd->cfq_group_idle)
return;
WARN_ON(!RB_EMPTY_ROOT(&cfqq->sort_list));
diff --git a/block/partitions/efi.c b/block/partitions/efi.c
index bcd86e5..39f70d9 100644
--- a/block/partitions/efi.c
+++ b/block/partitions/efi.c
@@ -293,7 +293,7 @@
if (!gpt)
return NULL;
- count = le32_to_cpu(gpt->num_partition_entries) *
+ count = (size_t)le32_to_cpu(gpt->num_partition_entries) *
le32_to_cpu(gpt->sizeof_partition_entry);
if (!count)
return NULL;
@@ -352,7 +352,7 @@
gpt_header **gpt, gpt_entry **ptes)
{
u32 crc, origcrc;
- u64 lastlba;
+ u64 lastlba, pt_size;
if (!ptes)
return 0;
@@ -434,13 +434,20 @@
goto fail;
}
+ /* Sanity check partition table size */
+ pt_size = (u64)le32_to_cpu((*gpt)->num_partition_entries) *
+ le32_to_cpu((*gpt)->sizeof_partition_entry);
+ if (pt_size > KMALLOC_MAX_SIZE) {
+ pr_debug("GUID Partition Table is too large: %llu > %lu bytes\n",
+ (unsigned long long)pt_size, KMALLOC_MAX_SIZE);
+ goto fail;
+ }
+
if (!(*ptes = alloc_read_gpt_entries(state, *gpt)))
goto fail;
/* Check the GUID Partition Entry Array CRC */
- crc = efi_crc32((const unsigned char *) (*ptes),
- le32_to_cpu((*gpt)->num_partition_entries) *
- le32_to_cpu((*gpt)->sizeof_partition_entry));
+ crc = efi_crc32((const unsigned char *) (*ptes), pt_size);
if (crc != le32_to_cpu((*gpt)->partition_entry_array_crc32)) {
pr_debug("GUID Partition Entry Array CRC check failed.\n");
diff --git a/crypto/Kconfig b/crypto/Kconfig
index 84d7148..fa98ad7 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -360,6 +360,7 @@
select CRYPTO_BLKCIPHER
select CRYPTO_MANAGER
select CRYPTO_GF128MUL
+ select CRYPTO_ECB
help
XTS: IEEE1619/D16 narrow block cipher use with aes-xts-plain,
key size 256, 384 or 512 bits. This implementation currently
diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c
index 45af0fe..aaf2f81 100644
--- a/crypto/algif_skcipher.c
+++ b/crypto/algif_skcipher.c
@@ -143,8 +143,10 @@
sg_init_table(sgl->sg, MAX_SGL_ENTS + 1);
sgl->cur = 0;
- if (sg)
+ if (sg) {
sg_chain(sg, MAX_SGL_ENTS + 1, sgl->sg);
+ sg_unmark_end(sg + (MAX_SGL_ENTS - 1));
+ }
list_add_tail(&sgl->list, &ctx->tsgl);
}
diff --git a/crypto/drbg.c b/crypto/drbg.c
index 8cac3d3..942ddff 100644
--- a/crypto/drbg.c
+++ b/crypto/drbg.c
@@ -1133,10 +1133,10 @@
{
if (!drbg)
return;
- kzfree(drbg->V);
- drbg->Vbuf = NULL;
- kzfree(drbg->C);
- drbg->Cbuf = NULL;
+ kzfree(drbg->Vbuf);
+ drbg->V = NULL;
+ kzfree(drbg->Cbuf);
+ drbg->C = NULL;
kzfree(drbg->scratchpadbuf);
drbg->scratchpadbuf = NULL;
drbg->reseed_ctr = 0;
diff --git a/crypto/shash.c b/crypto/shash.c
index a051541..4d8a671 100644
--- a/crypto/shash.c
+++ b/crypto/shash.c
@@ -274,12 +274,14 @@
int shash_ahash_digest(struct ahash_request *req, struct shash_desc *desc)
{
- struct scatterlist *sg = req->src;
- unsigned int offset = sg->offset;
unsigned int nbytes = req->nbytes;
+ struct scatterlist *sg;
+ unsigned int offset;
int err;
- if (nbytes < min(sg->length, ((unsigned int)(PAGE_SIZE)) - offset)) {
+ if (nbytes &&
+ (sg = req->src, offset = sg->offset,
+ nbytes < min(sg->length, ((unsigned int)(PAGE_SIZE)) - offset))) {
void *data;
data = kmap_atomic(sg_page(sg));
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index fcf85be..256a1d5 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -1153,6 +1153,10 @@
task->pid, desired.prio,
to_kernel_prio(policy, priority));
+ trace_binder_set_priority(task->tgid, task->pid, task->normal_prio,
+ to_kernel_prio(policy, priority),
+ desired.prio);
+
/* Set the actual priority */
if (task->policy != policy || is_rt_policy(policy)) {
struct sched_param params;
@@ -2102,6 +2106,26 @@
}
/**
+ * binder_cleanup_transaction() - cleans up undelivered transaction
+ * @t: transaction that needs to be cleaned up
+ * @reason: reason the transaction wasn't delivered
+ * @error_code: error to return to caller (if synchronous call)
+ */
+static void binder_cleanup_transaction(struct binder_transaction *t,
+ const char *reason,
+ uint32_t error_code)
+{
+ if (t->buffer->target_node && !(t->flags & TF_ONE_WAY)) {
+ binder_send_failed_reply(t, error_code);
+ } else {
+ binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
+ "undelivered transaction %d, %s\n",
+ t->debug_id, reason);
+ binder_free_transaction(t);
+ }
+}
+
+/**
* binder_validate_object() - checks for a valid metadata object in a buffer.
* @buffer: binder_buffer that we're parsing.
* @offset: offset in the buffer at which to validate an object.
@@ -4183,12 +4207,20 @@
if (put_user(cmd, (uint32_t __user *)ptr)) {
if (t_from)
binder_thread_dec_tmpref(t_from);
+
+ binder_cleanup_transaction(t, "put_user failed",
+ BR_FAILED_REPLY);
+
return -EFAULT;
}
ptr += sizeof(uint32_t);
if (copy_to_user(ptr, &tr, sizeof(tr))) {
if (t_from)
binder_thread_dec_tmpref(t_from);
+
+ binder_cleanup_transaction(t, "copy_to_user failed",
+ BR_FAILED_REPLY);
+
return -EFAULT;
}
ptr += sizeof(tr);
@@ -4258,15 +4290,9 @@
struct binder_transaction *t;
t = container_of(w, struct binder_transaction, work);
- if (t->buffer->target_node &&
- !(t->flags & TF_ONE_WAY)) {
- binder_send_failed_reply(t, BR_DEAD_REPLY);
- } else {
- binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
- "undelivered transaction %d\n",
- t->debug_id);
- binder_free_transaction(t);
- }
+
+ binder_cleanup_transaction(t, "process died.",
+ BR_DEAD_REPLY);
} break;
case BINDER_WORK_RETURN_ERROR: {
struct binder_error *e = container_of(
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
index e026894..9057411 100644
--- a/drivers/android/binder_alloc.c
+++ b/drivers/android/binder_alloc.c
@@ -186,12 +186,12 @@
}
static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
- void *start, void *end,
- struct vm_area_struct *vma)
+ void *start, void *end)
{
void *page_addr;
unsigned long user_page_addr;
struct binder_lru_page *page;
+ struct vm_area_struct *vma = NULL;
struct mm_struct *mm = NULL;
bool need_mm = false;
@@ -215,17 +215,12 @@
}
}
- if (!vma && need_mm)
- mm = get_task_mm(alloc->tsk);
+ if (need_mm && mmget_not_zero(alloc->vma_vm_mm))
+ mm = alloc->vma_vm_mm;
if (mm) {
down_write(&mm->mmap_sem);
vma = alloc->vma;
- if (vma && mm != alloc->vma_vm_mm) {
- pr_err("%d: vma mm and task mm mismatch\n",
- alloc->pid);
- vma = NULL;
- }
}
if (!vma && need_mm) {
@@ -442,7 +437,7 @@
if (end_page_addr > has_page_addr)
end_page_addr = has_page_addr;
ret = binder_update_page_range(alloc, 1,
- (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL);
+ (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr);
if (ret)
return ERR_PTR(ret);
@@ -483,7 +478,7 @@
err_alloc_buf_struct_failed:
binder_update_page_range(alloc, 0,
(void *)PAGE_ALIGN((uintptr_t)buffer->data),
- end_page_addr, NULL);
+ end_page_addr);
return ERR_PTR(-ENOMEM);
}
@@ -567,8 +562,7 @@
alloc->pid, buffer->data,
prev->data, next->data);
binder_update_page_range(alloc, 0, buffer_start_page(buffer),
- buffer_start_page(buffer) + PAGE_SIZE,
- NULL);
+ buffer_start_page(buffer) + PAGE_SIZE);
}
list_del(&buffer->entry);
kfree(buffer);
@@ -605,8 +599,7 @@
binder_update_page_range(alloc, 0,
(void *)PAGE_ALIGN((uintptr_t)buffer->data),
- (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK),
- NULL);
+ (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK));
rb_erase(&buffer->rb_node, &alloc->allocated_buffers);
buffer->free = 1;
@@ -720,6 +713,8 @@
barrier();
alloc->vma = vma;
alloc->vma_vm_mm = vma->vm_mm;
+ /* Same as mmgrab() in later kernel versions */
+ atomic_inc(&alloc->vma_vm_mm->mm_count);
return 0;
@@ -795,6 +790,8 @@
vfree(alloc->buffer);
}
mutex_unlock(&alloc->mutex);
+ if (alloc->vma_vm_mm)
+ mmdrop(alloc->vma_vm_mm);
binder_alloc_debug(BINDER_DEBUG_OPEN_CLOSE,
"%s: %d buffers %d, pages %d\n",
@@ -889,7 +886,6 @@
void binder_alloc_vma_close(struct binder_alloc *alloc)
{
WRITE_ONCE(alloc->vma, NULL);
- WRITE_ONCE(alloc->vma_vm_mm, NULL);
}
/**
@@ -926,9 +922,9 @@
page_addr = (uintptr_t)alloc->buffer + index * PAGE_SIZE;
vma = alloc->vma;
if (vma) {
- mm = get_task_mm(alloc->tsk);
- if (!mm)
- goto err_get_task_mm_failed;
+ if (!mmget_not_zero(alloc->vma_vm_mm))
+ goto err_mmget;
+ mm = alloc->vma_vm_mm;
if (!down_write_trylock(&mm->mmap_sem))
goto err_down_write_mmap_sem_failed;
}
@@ -964,7 +960,7 @@
err_down_write_mmap_sem_failed:
mmput_async(mm);
-err_get_task_mm_failed:
+err_mmget:
err_page_already_freed:
mutex_unlock(&alloc->mutex);
err_get_alloc_mutex_failed:
@@ -1003,7 +999,6 @@
*/
void binder_alloc_init(struct binder_alloc *alloc)
{
- alloc->tsk = current->group_leader;
alloc->pid = current->group_leader->pid;
mutex_init(&alloc->mutex);
INIT_LIST_HEAD(&alloc->buffers);
diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h
index a3a3602..2dd33b6 100644
--- a/drivers/android/binder_alloc.h
+++ b/drivers/android/binder_alloc.h
@@ -100,7 +100,6 @@
*/
struct binder_alloc {
struct mutex mutex;
- struct task_struct *tsk;
struct vm_area_struct *vma;
struct mm_struct *vma_vm_mm;
void *buffer;
diff --git a/drivers/android/binder_trace.h b/drivers/android/binder_trace.h
index 76e3b9c..b11dffc 100644
--- a/drivers/android/binder_trace.h
+++ b/drivers/android/binder_trace.h
@@ -85,6 +85,30 @@
DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_write_done);
DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_read_done);
+TRACE_EVENT(binder_set_priority,
+ TP_PROTO(int proc, int thread, unsigned int old_prio,
+ unsigned int desired_prio, unsigned int new_prio),
+ TP_ARGS(proc, thread, old_prio, new_prio, desired_prio),
+
+ TP_STRUCT__entry(
+ __field(int, proc)
+ __field(int, thread)
+ __field(unsigned int, old_prio)
+ __field(unsigned int, new_prio)
+ __field(unsigned int, desired_prio)
+ ),
+ TP_fast_assign(
+ __entry->proc = proc;
+ __entry->thread = thread;
+ __entry->old_prio = old_prio;
+ __entry->new_prio = new_prio;
+ __entry->desired_prio = desired_prio;
+ ),
+ TP_printk("proc=%d thread=%d old=%d => new=%d desired=%d",
+ __entry->proc, __entry->thread, __entry->old_prio,
+ __entry->new_prio, __entry->desired_prio)
+);
+
TRACE_EVENT(binder_wait_for_work,
TP_PROTO(bool proc_work, bool transaction_stack, bool thread_todo),
TP_ARGS(proc_work, transaction_stack, thread_todo),
diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c
index 7ef16c0..20e2b7a 100644
--- a/drivers/ata/libata-transport.c
+++ b/drivers/ata/libata-transport.c
@@ -224,7 +224,6 @@
static void ata_tport_release(struct device *dev)
{
- put_device(dev->parent);
}
/**
@@ -284,7 +283,7 @@
device_initialize(dev);
dev->type = &ata_port_type;
- dev->parent = get_device(parent);
+ dev->parent = parent;
dev->release = ata_tport_release;
dev_set_name(dev, "ata%d", ap->print_id);
transport_setup_device(dev);
@@ -348,7 +347,6 @@
static void ata_tlink_release(struct device *dev)
{
- put_device(dev->parent);
}
/**
@@ -410,7 +408,7 @@
int error;
device_initialize(dev);
- dev->parent = get_device(&ap->tdev);
+ dev->parent = &ap->tdev;
dev->release = ata_tlink_release;
if (ata_is_host_link(link))
dev_set_name(dev, "link%d", ap->print_id);
@@ -589,7 +587,6 @@
static void ata_tdev_release(struct device *dev)
{
- put_device(dev->parent);
}
/**
@@ -662,7 +659,7 @@
int error;
device_initialize(dev);
- dev->parent = get_device(&link->tdev);
+ dev->parent = &link->tdev;
dev->release = ata_tdev_release;
if (ata_is_host_link(link))
dev_set_name(dev, "dev%d.%d", ap->print_id,ata_dev->devno);
diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c
index 0636d84..f3f538e 100644
--- a/drivers/ata/sata_via.c
+++ b/drivers/ata/sata_via.c
@@ -644,14 +644,16 @@
pci_write_config_byte(pdev, SATA_NATIVE_MODE, tmp8);
}
- /* enable IRQ on hotplug */
- pci_read_config_byte(pdev, SVIA_MISC_3, &tmp8);
- if ((tmp8 & SATA_HOTPLUG) != SATA_HOTPLUG) {
- dev_dbg(&pdev->dev,
- "enabling SATA hotplug (0x%x)\n",
- (int) tmp8);
- tmp8 |= SATA_HOTPLUG;
- pci_write_config_byte(pdev, SVIA_MISC_3, tmp8);
+ if (board_id == vt6421) {
+ /* enable IRQ on hotplug */
+ pci_read_config_byte(pdev, SVIA_MISC_3, &tmp8);
+ if ((tmp8 & SATA_HOTPLUG) != SATA_HOTPLUG) {
+ dev_dbg(&pdev->dev,
+ "enabling SATA hotplug (0x%x)\n",
+ (int) tmp8);
+ tmp8 |= SATA_HOTPLUG;
+ pci_write_config_byte(pdev, SVIA_MISC_3, tmp8);
+ }
}
/*
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 5eba478..14ff403 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -858,7 +858,8 @@
struct platform_device *pdev = to_platform_device(dev);
char *driver_override, *old, *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/base/power/main.c b/drivers/base/power/main.c
index 4f99101..dc259d2 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -1762,10 +1762,13 @@
{
spin_lock_irq(&dev->power.lock);
dev->power.no_pm_callbacks =
- (!dev->bus || pm_ops_is_empty(dev->bus->pm)) &&
- (!dev->class || pm_ops_is_empty(dev->class->pm)) &&
+ (!dev->bus || (pm_ops_is_empty(dev->bus->pm) &&
+ !dev->bus->suspend && !dev->bus->resume)) &&
+ (!dev->class || (pm_ops_is_empty(dev->class->pm) &&
+ !dev->class->suspend && !dev->class->resume)) &&
(!dev->type || pm_ops_is_empty(dev->type->pm)) &&
(!dev->pm_domain || pm_ops_is_empty(&dev->pm_domain->ops)) &&
- (!dev->driver || pm_ops_is_empty(dev->driver->pm));
+ (!dev->driver || (pm_ops_is_empty(dev->driver->pm) &&
+ !dev->driver->suspend && !dev->driver->resume));
spin_unlock_irq(&dev->power.lock);
}
diff --git a/drivers/base/property.c b/drivers/base/property.c
index 06f6668..7b313b5 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -20,6 +20,7 @@
#include <linux/phy.h>
struct property_set {
+ struct device *dev;
struct fwnode_handle fwnode;
struct property_entry *properties;
};
@@ -817,6 +818,7 @@
void device_remove_properties(struct device *dev)
{
struct fwnode_handle *fwnode;
+ struct property_set *pset;
fwnode = dev_fwnode(dev);
if (!fwnode)
@@ -826,16 +828,16 @@
* the pset. If there is no real firmware node (ACPI/DT) primary
* will hold the pset.
*/
- if (is_pset_node(fwnode)) {
+ pset = to_pset_node(fwnode);
+ if (pset) {
set_primary_fwnode(dev, NULL);
- pset_free_set(to_pset_node(fwnode));
} else {
- fwnode = fwnode->secondary;
- if (!IS_ERR(fwnode) && is_pset_node(fwnode)) {
+ pset = to_pset_node(fwnode->secondary);
+ if (pset && dev == pset->dev)
set_secondary_fwnode(dev, NULL);
- pset_free_set(to_pset_node(fwnode));
- }
}
+ if (pset && dev == pset->dev)
+ pset_free_set(pset);
}
EXPORT_SYMBOL_GPL(device_remove_properties);
@@ -863,6 +865,7 @@
p->fwnode.type = FWNODE_PDATA;
set_secondary_fwnode(dev, &p->fwnode);
+ p->dev = dev;
return 0;
}
EXPORT_SYMBOL_GPL(device_add_properties);
diff --git a/drivers/block/skd_main.c b/drivers/block/skd_main.c
index 3822eae..6f78cea7 100644
--- a/drivers/block/skd_main.c
+++ b/drivers/block/skd_main.c
@@ -2163,6 +2163,9 @@
*/
qcmd |= FIT_QCMD_MSGSIZE_64;
+ /* Make sure skd_msg_buf is written before the doorbell is triggered. */
+ smp_wmb();
+
SKD_WRITEQ(skdev, qcmd, FIT_Q_COMMAND);
}
@@ -2209,6 +2212,9 @@
qcmd = skspcl->mb_dma_address;
qcmd |= FIT_QCMD_QID_NORMAL + FIT_QCMD_MSGSIZE_128;
+ /* Make sure skd_msg_buf is written before the doorbell is triggered. */
+ smp_wmb();
+
SKD_WRITEQ(skdev, qcmd, FIT_Q_COMMAND);
}
@@ -4622,15 +4628,16 @@
{
struct gendisk *disk = skdev->disk;
- if (disk != NULL) {
- struct request_queue *q = disk->queue;
+ if (disk && (disk->flags & GENHD_FL_UP))
+ del_gendisk(disk);
- if (disk->flags & GENHD_FL_UP)
- del_gendisk(disk);
- if (q)
- blk_cleanup_queue(q);
- put_disk(disk);
+ if (skdev->queue) {
+ blk_cleanup_queue(skdev->queue);
+ skdev->queue = NULL;
+ disk->queue = NULL;
}
+
+ put_disk(disk);
skdev->disk = NULL;
}
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c
index d02f2c1..c738bae 100644
--- a/drivers/bluetooth/btmrvl_sdio.c
+++ b/drivers/bluetooth/btmrvl_sdio.c
@@ -1682,8 +1682,12 @@
/* Disable platform specific wakeup interrupt */
if (card->plt_wake_cfg && card->plt_wake_cfg->irq_bt >= 0) {
disable_irq_wake(card->plt_wake_cfg->irq_bt);
- if (!card->plt_wake_cfg->wake_by_bt)
- disable_irq(card->plt_wake_cfg->irq_bt);
+ disable_irq(card->plt_wake_cfg->irq_bt);
+ if (card->plt_wake_cfg->wake_by_bt)
+ /* Undo our disable, since interrupt handler already
+ * did this.
+ */
+ enable_irq(card->plt_wake_cfg->irq_bt);
}
return 0;
diff --git a/drivers/char/hw_random/msm_rng.c b/drivers/char/hw_random/msm_rng.c
index 7641a6a..d5dd8ae 100644
--- a/drivers/char/hw_random/msm_rng.c
+++ b/drivers/char/hw_random/msm_rng.c
@@ -138,8 +138,6 @@
/* read FIFO */
val = readl_relaxed(base + PRNG_DATA_OUT_OFFSET);
- if (!val)
- break; /* no data to read so just bail */
/* write data back to callers pointer */
*(retdata++) = val;
diff --git a/drivers/clk/axs10x/i2s_pll_clock.c b/drivers/clk/axs10x/i2s_pll_clock.c
index 411310d..02d3bcd 100644
--- a/drivers/clk/axs10x/i2s_pll_clock.c
+++ b/drivers/clk/axs10x/i2s_pll_clock.c
@@ -182,6 +182,7 @@
if (IS_ERR(pll_clk->base))
return PTR_ERR(pll_clk->base);
+ memset(&init, 0, sizeof(init));
clk_name = node->name;
init.name = clk_name;
init.ops = &i2s_pll_ops;
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
index 21c427d..a26c8a1 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
@@ -803,6 +803,13 @@
.num_resets = ARRAY_SIZE(sun8i_h3_ccu_resets),
};
+static struct ccu_mux_nb sun8i_h3_cpu_nb = {
+ .common = &cpux_clk.common,
+ .cm = &cpux_clk.mux,
+ .delay_us = 1, /* > 8 clock cycles at 24 MHz */
+ .bypass_index = 1, /* index of 24 MHz oscillator */
+};
+
static void __init sun8i_h3_ccu_setup(struct device_node *node)
{
void __iomem *reg;
@@ -821,6 +828,9 @@
writel(val | (3 << 16), reg + SUN8I_H3_PLL_AUDIO_REG);
sunxi_ccu_probe(node, reg, &sun8i_h3_ccu_desc);
+
+ ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk,
+ &sun8i_h3_cpu_nb);
}
CLK_OF_DECLARE(sun8i_h3_ccu, "allwinner,sun8i-h3-ccu",
sun8i_h3_ccu_setup);
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index e2023bd..fd5984f 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -244,7 +244,7 @@
config ACPI_CPPC_CPUFREQ
tristate "CPUFreq driver based on the ACPI CPPC spec"
- depends on ACPI
+ depends on ACPI_PROCESSOR
select ACPI_CPPC_LIB
default n
help
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 80fa656..a59ae8e 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -609,6 +609,7 @@
static int pid_param_set(void *data, u64 val)
{
*(u32 *)data = val;
+ pid_params.sample_rate_ns = pid_params.sample_rate_ms * NSEC_PER_MSEC;
intel_pstate_reset_all_pid();
return 0;
}
diff --git a/drivers/crypto/ccp/ccp-crypto-aes-xts.c b/drivers/crypto/ccp/ccp-crypto-aes-xts.c
index 58a4244..3f26a41 100644
--- a/drivers/crypto/ccp/ccp-crypto-aes-xts.c
+++ b/drivers/crypto/ccp/ccp-crypto-aes-xts.c
@@ -1,8 +1,9 @@
/*
* AMD Cryptographic Coprocessor (CCP) AES XTS crypto API support
*
- * Copyright (C) 2013 Advanced Micro Devices, Inc.
+ * Copyright (C) 2013,2017 Advanced Micro Devices, Inc.
*
+ * Author: Gary R Hook <gary.hook@amd.com>
* Author: Tom Lendacky <thomas.lendacky@amd.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -164,6 +165,7 @@
memset(&rctx->cmd, 0, sizeof(rctx->cmd));
INIT_LIST_HEAD(&rctx->cmd.entry);
rctx->cmd.engine = CCP_ENGINE_XTS_AES_128;
+ rctx->cmd.u.xts.type = CCP_AES_TYPE_128;
rctx->cmd.u.xts.action = (encrypt) ? CCP_AES_ACTION_ENCRYPT
: CCP_AES_ACTION_DECRYPT;
rctx->cmd.u.xts.unit_size = unit_size;
diff --git a/drivers/crypto/ccp/ccp-dev-v5.c b/drivers/crypto/ccp/ccp-dev-v5.c
index 2c0ce5f..17b19a6 100644
--- a/drivers/crypto/ccp/ccp-dev-v5.c
+++ b/drivers/crypto/ccp/ccp-dev-v5.c
@@ -131,6 +131,7 @@
#define CCP_AES_MODE(p) ((p)->aes.mode)
#define CCP_AES_TYPE(p) ((p)->aes.type)
#define CCP_XTS_SIZE(p) ((p)->aes_xts.size)
+#define CCP_XTS_TYPE(p) ((p)->aes_xts.type)
#define CCP_XTS_ENCRYPT(p) ((p)->aes_xts.encrypt)
#define CCP_SHA_TYPE(p) ((p)->sha.type)
#define CCP_RSA_SIZE(p) ((p)->rsa.size)
@@ -318,6 +319,7 @@
CCP5_CMD_PROT(&desc) = 0;
function.raw = 0;
+ CCP_XTS_TYPE(&function) = op->u.xts.type;
CCP_XTS_ENCRYPT(&function) = op->u.xts.action;
CCP_XTS_SIZE(&function) = op->u.xts.unit_size;
CCP5_CMD_FUNCTION(&desc) = function.raw;
diff --git a/drivers/crypto/ccp/ccp-dev.h b/drivers/crypto/ccp/ccp-dev.h
index 8ac7ae1..e23c36c 100644
--- a/drivers/crypto/ccp/ccp-dev.h
+++ b/drivers/crypto/ccp/ccp-dev.h
@@ -187,6 +187,7 @@
#define CCP_AES_CTX_SB_COUNT 1
#define CCP_XTS_AES_KEY_SB_COUNT 1
+#define CCP5_XTS_AES_KEY_SB_COUNT 2
#define CCP_XTS_AES_CTX_SB_COUNT 1
#define CCP_SHA_SB_COUNT 1
@@ -472,6 +473,7 @@
};
struct ccp_xts_aes_op {
+ enum ccp_aes_type type;
enum ccp_aes_action action;
enum ccp_xts_aes_unit_size unit_size;
};
diff --git a/drivers/crypto/ccp/ccp-ops.c b/drivers/crypto/ccp/ccp-ops.c
index 50fae44..64deb00 100644
--- a/drivers/crypto/ccp/ccp-ops.c
+++ b/drivers/crypto/ccp/ccp-ops.c
@@ -779,6 +779,8 @@
struct ccp_op op;
unsigned int unit_size, dm_offset;
bool in_place = false;
+ unsigned int sb_count;
+ enum ccp_aes_type aestype;
int ret;
switch (xts->unit_size) {
@@ -802,7 +804,9 @@
return -EINVAL;
}
- if (xts->key_len != AES_KEYSIZE_128)
+ if (xts->key_len == AES_KEYSIZE_128)
+ aestype = CCP_AES_TYPE_128;
+ else
return -EINVAL;
if (!xts->final && (xts->src_len & (AES_BLOCK_SIZE - 1)))
@@ -824,23 +828,44 @@
op.sb_key = cmd_q->sb_key;
op.sb_ctx = cmd_q->sb_ctx;
op.init = 1;
+ op.u.xts.type = aestype;
op.u.xts.action = xts->action;
op.u.xts.unit_size = xts->unit_size;
- /* All supported key sizes fit in a single (32-byte) SB entry
- * and must be in little endian format. Use the 256-bit byte
- * swap passthru option to convert from big endian to little
- * endian.
+ /* A version 3 device only supports 128-bit keys, which fits into a
+ * single SB entry. A version 5 device uses a 512-bit vector, so two
+ * SB entries.
*/
+ if (cmd_q->ccp->vdata->version == CCP_VERSION(3, 0))
+ sb_count = CCP_XTS_AES_KEY_SB_COUNT;
+ else
+ sb_count = CCP5_XTS_AES_KEY_SB_COUNT;
ret = ccp_init_dm_workarea(&key, cmd_q,
- CCP_XTS_AES_KEY_SB_COUNT * CCP_SB_BYTES,
+ sb_count * CCP_SB_BYTES,
DMA_TO_DEVICE);
if (ret)
return ret;
- dm_offset = CCP_SB_BYTES - AES_KEYSIZE_128;
- ccp_set_dm_area(&key, dm_offset, xts->key, 0, xts->key_len);
- ccp_set_dm_area(&key, 0, xts->key, dm_offset, xts->key_len);
+ if (cmd_q->ccp->vdata->version == CCP_VERSION(3, 0)) {
+ /* All supported key sizes must be in little endian format.
+ * Use the 256-bit byte swap passthru option to convert from
+ * big endian to little endian.
+ */
+ dm_offset = CCP_SB_BYTES - AES_KEYSIZE_128;
+ ccp_set_dm_area(&key, dm_offset, xts->key, 0, xts->key_len);
+ ccp_set_dm_area(&key, 0, xts->key, xts->key_len, xts->key_len);
+ } else {
+ /* Version 5 CCPs use a 512-bit space for the key: each portion
+ * occupies 256 bits, or one entire slot, and is zero-padded.
+ */
+ unsigned int pad;
+
+ dm_offset = CCP_SB_BYTES;
+ pad = dm_offset - xts->key_len;
+ ccp_set_dm_area(&key, pad, xts->key, 0, xts->key_len);
+ ccp_set_dm_area(&key, dm_offset + pad, xts->key, xts->key_len,
+ xts->key_len);
+ }
ret = ccp_copy_to_sb(cmd_q, &key, op.jobid, op.sb_key,
CCP_PASSTHRU_BYTESWAP_256BIT);
if (ret) {
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
index 571de2f..e2d323f 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -1756,9 +1756,9 @@
req_ctx->swinit = 0;
} else {
desc->ptr[1] = zero_entry;
- /* Indicate next op is not the first. */
- req_ctx->first = 0;
}
+ /* Indicate next op is not the first. */
+ req_ctx->first = 0;
/* HMAC key */
if (ctx->keylen)
@@ -1769,7 +1769,7 @@
sg_count = edesc->src_nents ?: 1;
if (is_sec1 && sg_count > 1)
- sg_copy_to_buffer(areq->src, sg_count, edesc->buf, length);
+ sg_copy_to_buffer(req_ctx->psrc, sg_count, edesc->buf, length);
else
sg_count = dma_map_sg(dev, req_ctx->psrc, sg_count,
DMA_TO_DEVICE);
@@ -3057,7 +3057,8 @@
t_alg->algt.alg.hash.final = ahash_final;
t_alg->algt.alg.hash.finup = ahash_finup;
t_alg->algt.alg.hash.digest = ahash_digest;
- t_alg->algt.alg.hash.setkey = ahash_setkey;
+ if (!strncmp(alg->cra_name, "hmac", 4))
+ t_alg->algt.alg.hash.setkey = ahash_setkey;
t_alg->algt.alg.hash.import = ahash_import;
t_alg->algt.alg.hash.export = ahash_export;
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 8f582f6..b263696 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -598,7 +598,7 @@
err = device_register(&devfreq->dev);
if (err) {
mutex_unlock(&devfreq->lock);
- goto err_out;
+ goto err_dev;
}
devfreq->trans_table = devm_kzalloc(&devfreq->dev, sizeof(unsigned int) *
@@ -642,6 +642,9 @@
mutex_unlock(&devfreq_list_lock);
device_unregister(&devfreq->dev);
+err_dev:
+ if (devfreq)
+ kfree(devfreq);
err_out:
return ERR_PTR(err);
}
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c
index 77242b3..57962bf 100644
--- a/drivers/dma/edma.c
+++ b/drivers/dma/edma.c
@@ -1143,11 +1143,24 @@
struct edma_desc *edesc;
struct device *dev = chan->device->dev;
struct edma_chan *echan = to_edma_chan(chan);
- unsigned int width, pset_len;
+ unsigned int width, pset_len, array_size;
if (unlikely(!echan || !len))
return NULL;
+ /* Align the array size (acnt block) with the transfer properties */
+ switch (__ffs((src | dest | len))) {
+ case 0:
+ array_size = SZ_32K - 1;
+ break;
+ case 1:
+ array_size = SZ_32K - 2;
+ break;
+ default:
+ array_size = SZ_32K - 4;
+ break;
+ }
+
if (len < SZ_64K) {
/*
* Transfer size less than 64K can be handled with one paRAM
@@ -1169,7 +1182,7 @@
* When the full_length is multibple of 32767 one slot can be
* used to complete the transfer.
*/
- width = SZ_32K - 1;
+ width = array_size;
pset_len = rounddown(len, width);
/* One slot is enough for lengths multiple of (SZ_32K -1) */
if (unlikely(pset_len == len))
@@ -1217,7 +1230,7 @@
}
dest += pset_len;
src += pset_len;
- pset_len = width = len % (SZ_32K - 1);
+ pset_len = width = len % array_size;
ret = edma_config_pset(chan, &edesc->pset[1], src, dest, 1,
width, pset_len, DMA_MEM_TO_MEM);
diff --git a/drivers/dma/ti-dma-crossbar.c b/drivers/dma/ti-dma-crossbar.c
index 2403475..88a00d0 100644
--- a/drivers/dma/ti-dma-crossbar.c
+++ b/drivers/dma/ti-dma-crossbar.c
@@ -262,13 +262,14 @@
mutex_lock(&xbar->mutex);
map->xbar_out = find_first_zero_bit(xbar->dma_inuse,
xbar->dma_requests);
- mutex_unlock(&xbar->mutex);
if (map->xbar_out == xbar->dma_requests) {
+ mutex_unlock(&xbar->mutex);
dev_err(&pdev->dev, "Run out of free DMA requests\n");
kfree(map);
return ERR_PTR(-ENOMEM);
}
set_bit(map->xbar_out, xbar->dma_inuse);
+ mutex_unlock(&xbar->mutex);
map->xbar_in = (u16)dma_spec->args[0];
diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c
index daaac2c..7db692e 100644
--- a/drivers/edac/mce_amd.c
+++ b/drivers/edac/mce_amd.c
@@ -981,20 +981,19 @@
pr_cont("]: 0x%016llx\n", m->status);
if (m->status & MCI_STATUS_ADDRV)
- pr_emerg(HW_ERR "Error Addr: 0x%016llx", m->addr);
+ pr_emerg(HW_ERR "Error Addr: 0x%016llx\n", m->addr);
if (boot_cpu_has(X86_FEATURE_SMCA)) {
+ pr_emerg(HW_ERR "IPID: 0x%016llx", m->ipid);
+
if (m->status & MCI_STATUS_SYNDV)
pr_cont(", Syndrome: 0x%016llx", m->synd);
- pr_cont(", IPID: 0x%016llx", m->ipid);
-
pr_cont("\n");
decode_smca_errors(m);
goto err_code;
- } else
- pr_cont("\n");
+ }
if (!fam_ops)
goto err_code;
diff --git a/drivers/esoc/Kconfig b/drivers/esoc/Kconfig
index a56c7e0..3c65f69 100644
--- a/drivers/esoc/Kconfig
+++ b/drivers/esoc/Kconfig
@@ -38,7 +38,7 @@
allow logging of different esoc driver traces.
config ESOC_MDM_4x
- bool "Add support for external mdm9x25/mdm9x35/mdm9x45/mdm9x55"
+ bool "Add support for external mdm9x25/mdm9x35/mdm9x55"
help
In some Qualcomm Technologies, Inc. boards, an external modem such as
mdm9x25 or mdm9x35 is connected to a primary msm. The primary soc can
@@ -49,7 +49,7 @@
tristate "Command engine for 4x series external modems"
help
Provides a command engine to control the behavior of an external modem
- such as mdm9x25/mdm9x35/mdm9x45/mdm9x55/QSC. Allows the primary soc to put the
+ such as mdm9x25/mdm9x35/mdm9x55/QSC. Allows the primary soc to put the
external modem in a specific mode. Also listens for events on the
external modem.
diff --git a/drivers/esoc/esoc-mdm-4x.c b/drivers/esoc/esoc-mdm-4x.c
index 334278b..677e21d 100644
--- a/drivers/esoc/esoc-mdm-4x.c
+++ b/drivers/esoc/esoc-mdm-4x.c
@@ -88,12 +88,10 @@
return;
if (mdm->irq_mask & IRQ_ERRFATAL) {
enable_irq(mdm->errfatal_irq);
- irq_set_irq_wake(mdm->errfatal_irq, 1);
mdm->irq_mask &= ~IRQ_ERRFATAL;
}
if (mdm->irq_mask & IRQ_STATUS) {
enable_irq(mdm->status_irq);
- irq_set_irq_wake(mdm->status_irq, 1);
mdm->irq_mask &= ~IRQ_STATUS;
}
if (mdm->irq_mask & IRQ_PBLRDY) {
@@ -107,12 +105,10 @@
if (!mdm)
return;
if (!(mdm->irq_mask & IRQ_ERRFATAL)) {
- irq_set_irq_wake(mdm->errfatal_irq, 0);
disable_irq_nosync(mdm->errfatal_irq);
mdm->irq_mask |= IRQ_ERRFATAL;
}
if (!(mdm->irq_mask & IRQ_STATUS)) {
- irq_set_irq_wake(mdm->status_irq, 0);
disable_irq_nosync(mdm->status_irq);
mdm->irq_mask |= IRQ_STATUS;
}
@@ -179,26 +175,48 @@
struct device *dev = mdm->dev;
int ret;
bool graceful_shutdown = false;
+ u32 status, err_fatal;
switch (cmd) {
case ESOC_PWR_ON:
+ if (esoc->auto_boot) {
+ /*
+ * If esoc has already booted, we would have missed
+ * status change interrupt. Read status and err_fatal
+ * signals to arrive at the state of esoc.
+ */
+ esoc->clink_ops->get_status(&status, esoc);
+ esoc->clink_ops->get_err_fatal(&err_fatal, esoc);
+ if (err_fatal)
+ return -EIO;
+ if (status && !mdm->ready) {
+ mdm->ready = true;
+ esoc->clink_ops->notify(ESOC_BOOT_DONE, esoc);
+ }
+ }
gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
- mdm_enable_irqs(mdm);
mdm->init = 1;
mdm_do_first_power_on(mdm);
+ mdm_enable_irqs(mdm);
break;
case ESOC_PWR_OFF:
mdm_disable_irqs(mdm);
mdm->debug = 0;
mdm->ready = false;
mdm->trig_cnt = 0;
+ if (esoc->primary)
+ break;
graceful_shutdown = true;
- ret = sysmon_send_shutdown(&esoc->subsys);
- if (ret) {
- dev_err(mdm->dev, "sysmon shutdown fail, ret = %d\n",
- ret);
- graceful_shutdown = false;
- goto force_poff;
+ if (!esoc->userspace_handle_shutdown) {
+ ret = sysmon_send_shutdown(&esoc->subsys);
+ if (ret) {
+ dev_err(mdm->dev,
+ "sysmon shutdown fail, ret = %d\n", ret);
+ graceful_shutdown = false;
+ goto force_poff;
+ }
+ } else {
+ esoc_clink_queue_request(ESOC_REQ_SEND_SHUTDOWN, esoc);
}
dev_dbg(mdm->dev, "Waiting for status gpio go low\n");
status_down = false;
@@ -229,12 +247,17 @@
esoc->subsys.sysmon_shutdown_ret);
}
+ if (esoc->primary)
+ break;
/*
* Force a shutdown of the mdm. This is required in order
* to prevent the mdm from immediately powering back on
- * after the shutdown
+ * after the shutdown. Avoid setting status to 0, if line is
+ * monitored by multiple mdms(might be wrongly interpreted as
+ * a primary crash).
*/
- gpio_set_value(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
+ if (esoc->statusline_not_a_powersource == false)
+ gpio_set_value(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
esoc_clink_queue_request(ESOC_REQ_SHUTDOWN, esoc);
mdm_power_down(mdm);
mdm_update_gpio_configs(mdm, GPIO_UPDATE_BOOTING_CONFIG);
@@ -250,9 +273,12 @@
*/
mdm->ready = false;
cancel_delayed_work(&mdm->mdm2ap_status_check_work);
- gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1);
- dev_dbg(mdm->dev, "set ap2mdm errfatal to force reset\n");
- msleep(mdm->ramdump_delay_ms);
+ if (!mdm->esoc->auto_boot) {
+ gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1);
+ dev_dbg(mdm->dev,
+ "set ap2mdm errfatal to force reset\n");
+ msleep(mdm->ramdump_delay_ms);
+ }
break;
case ESOC_EXE_DEBUG:
mdm->debug = 1;
@@ -380,6 +406,8 @@
status_down = false;
dev_dbg(dev, "signal apq err fatal for graceful restart\n");
gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1);
+ if (esoc->primary)
+ break;
timeout = local_clock();
do_div(timeout, NSEC_PER_MSEC);
timeout += MDM_MODEM_TIMEOUT;
@@ -421,7 +449,8 @@
goto mdm_pwroff_irq;
esoc = mdm->esoc;
dev_err(dev, "%s: mdm sent errfatal interrupt\n",
- __func__);
+ __func__);
+ subsys_set_crash_status(esoc->subsys_dev, true);
/* disable irq ?*/
esoc_clink_evt_notify(ESOC_ERR_FATAL, esoc);
return IRQ_HANDLED;
@@ -442,11 +471,26 @@
return IRQ_HANDLED;
dev = mdm->dev;
esoc = mdm->esoc;
+ /*
+ * On auto boot devices, there is a possibility of receiving
+ * status change interrupt before esoc_clink structure is
+ * initialized. Ignore them.
+ */
+ if (!esoc)
+ return IRQ_HANDLED;
value = gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS));
if (value == 0 && mdm->ready) {
dev_err(dev, "unexpected reset external modem\n");
+ subsys_set_crash_status(esoc->subsys_dev, true);
esoc_clink_evt_notify(ESOC_UNEXPECTED_RESET, esoc);
} else if (value == 1) {
+ /*
+ * In auto_boot cases, bailout early if mdm
+ * is up already.
+ */
+ if (esoc->auto_boot && mdm->ready)
+ return IRQ_HANDLED;
+
cancel_delayed_work(&mdm->mdm2ap_status_check_work);
dev_dbg(dev, "status = 1: mdm is now ready\n");
mdm->ready = true;
@@ -454,6 +498,8 @@
queue_work(mdm->mdm_queue, &mdm->mdm_status_work);
if (mdm->get_restart_reason)
queue_work(mdm->mdm_queue, &mdm->restart_reason_work);
+ if (esoc->auto_boot)
+ esoc->clink_ops->notify(ESOC_BOOT_DONE, esoc);
}
return IRQ_HANDLED;
}
@@ -482,7 +528,7 @@
return IRQ_HANDLED;
}
-static int mdm_get_status(u32 *status, struct esoc_clink *esoc)
+static void mdm_get_status(u32 *status, struct esoc_clink *esoc)
{
struct mdm_ctrl *mdm = get_esoc_clink_data(esoc);
@@ -490,7 +536,16 @@
*status = 0;
else
*status = 1;
- return 0;
+}
+
+static void mdm_get_err_fatal(u32 *status, struct esoc_clink *esoc)
+{
+ struct mdm_ctrl *mdm = get_esoc_clink_data(esoc);
+
+ if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_ERRFATAL)) == 0)
+ *status = 0;
+ else
+ *status = 1;
}
static void mdm_configure_debug(struct mdm_ctrl *mdm)
@@ -573,13 +628,21 @@
&mdm->ramdump_delay_ms);
if (ret)
mdm->ramdump_delay_ms = DEF_RAMDUMP_DELAY;
- /* Multilple gpio_request calls are allowed */
+ /*
+ * In certain scenarios, multiple esoc devices are monitoring
+ * same AP2MDM_STATUS line. But only one of them will have a
+ * successful gpio_request call. Initialize gpio only if request
+ * succeeds.
+ */
if (gpio_request(MDM_GPIO(mdm, AP2MDM_STATUS), "AP2MDM_STATUS"))
dev_err(dev, "Failed to configure AP2MDM_STATUS gpio\n");
- /* Multilple gpio_request calls are allowed */
+ else
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
if (gpio_request(MDM_GPIO(mdm, AP2MDM_ERRFATAL), "AP2MDM_ERRFATAL"))
dev_err(dev, "%s Failed to configure AP2MDM_ERRFATAL gpio\n",
__func__);
+ else
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
if (gpio_request(MDM_GPIO(mdm, MDM2AP_STATUS), "MDM2AP_STATUS")) {
dev_err(dev, "%s Failed to configure MDM2AP_STATUS gpio\n",
__func__);
@@ -612,9 +675,6 @@
}
}
- gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
- gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
-
if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_CHNLRDY)))
gpio_direction_output(MDM_GPIO(mdm, AP2MDM_CHNLRDY), 0);
@@ -637,6 +697,7 @@
goto errfatal_err;
}
mdm->errfatal_irq = irq;
+ irq_set_irq_wake(mdm->errfatal_irq, 1);
errfatal_err:
/* status irq */
@@ -655,6 +716,7 @@
goto status_err;
}
mdm->status_irq = irq;
+ irq_set_irq_wake(mdm->status_irq, 1);
status_err:
if (gpio_is_valid(MDM_GPIO(mdm, MDM2AP_PBLRDY))) {
irq = platform_get_irq_byname(pdev, "plbrdy_irq");
@@ -748,6 +810,7 @@
dev_err(mdm->dev, "cannot allocate esoc device\n");
return PTR_ERR(esoc);
}
+ esoc->pdev = pdev;
mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
if (!mdm->mdm_queue) {
dev_err(mdm->dev, "could not create mdm_queue\n");
@@ -818,6 +881,7 @@
dev_err(mdm->dev, "cannot allocate esoc device\n");
return PTR_ERR(esoc);
}
+ esoc->pdev = pdev;
mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
if (!mdm->mdm_queue) {
dev_err(mdm->dev, "could not create mdm_queue\n");
@@ -906,6 +970,7 @@
dev_err(mdm->dev, "cannot allocate esoc device\n");
return PTR_ERR(esoc);
}
+ esoc->pdev = pdev;
mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
if (!mdm->mdm_queue) {
dev_err(mdm->dev, "could not create mdm_queue\n");
@@ -966,6 +1031,7 @@
static struct esoc_clink_ops mdm_cops = {
.cmd_exe = mdm_cmd_exe,
.get_status = mdm_get_status,
+ .get_err_fatal = mdm_get_err_fatal,
.notify = mdm_notify,
};
diff --git a/drivers/esoc/esoc-mdm-dbg-eng.c b/drivers/esoc/esoc-mdm-dbg-eng.c
index 309c820..a61588a 100644
--- a/drivers/esoc/esoc-mdm-dbg-eng.c
+++ b/drivers/esoc/esoc-mdm-dbg-eng.c
@@ -269,7 +269,7 @@
{
unsigned int i;
unsigned long flags;
- size_t count;
+ size_t count = 0;
spin_lock_irqsave(&req_lock, flags);
for (i = 0; i < ARRAY_SIZE(req_to_str); i++) {
diff --git a/drivers/esoc/esoc-mdm-drv.c b/drivers/esoc/esoc-mdm-drv.c
index 31cd8c4..77ae84b 100644
--- a/drivers/esoc/esoc-mdm-drv.c
+++ b/drivers/esoc/esoc-mdm-drv.c
@@ -13,6 +13,7 @@
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/reboot.h>
+#include <linux/of.h>
#include "esoc.h"
#include "mdm-dbg.h"
@@ -74,7 +75,14 @@
break;
case ESOC_UNEXPECTED_RESET:
case ESOC_ERR_FATAL:
- if (mdm_drv->mode == CRASH)
+ /*
+ * Modem can crash while we are waiting for boot_done during
+ * a subsystem_get(). Setting mode to CRASH will prevent a
+ * subsequent subsystem_get() from entering poweron ops. Avoid
+ * this by seting mode to CRASH only if device was up and
+ * running.
+ */
+ if (mdm_drv->mode == CRASH || mdm_drv->mode != RUN)
return;
mdm_drv->mode = CRASH;
queue_work(mdm_drv->mdm_queue, &mdm_drv->ssr_work);
@@ -164,8 +172,9 @@
subsys);
struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);
const struct esoc_clink_ops * const clink_ops = esoc_clink->clink_ops;
+ int timeout = INT_MAX;
- if (!esoc_req_eng_enabled(esoc_clink)) {
+ if (!esoc_clink->auto_boot && !esoc_req_eng_enabled(esoc_clink)) {
dev_dbg(&esoc_clink->dev, "Wait for req eng registration\n");
wait_for_completion(&mdm_drv->req_eng_wait);
}
@@ -190,8 +199,17 @@
return ret;
}
}
- wait_for_completion(&mdm_drv->boot_done);
- if (mdm_drv->boot_fail) {
+
+ /*
+ * In autoboot case, it is possible that we can forever wait for
+ * boot completion, when esoc fails to boot. This is because there
+ * is no helper application which can alert esoc driver about boot
+ * failure. Prevent going to wait forever in such case.
+ */
+ if (esoc_clink->auto_boot)
+ timeout = 10 * HZ;
+ ret = wait_for_completion_timeout(&mdm_drv->boot_done, timeout);
+ if (mdm_drv->boot_fail || ret <= 0) {
dev_err(&esoc_clink->dev, "booting failed\n");
return -EIO;
}
@@ -219,10 +237,12 @@
static int mdm_register_ssr(struct esoc_clink *esoc_clink)
{
- esoc_clink->subsys.shutdown = mdm_subsys_shutdown;
- esoc_clink->subsys.ramdump = mdm_subsys_ramdumps;
- esoc_clink->subsys.powerup = mdm_subsys_powerup;
- esoc_clink->subsys.crash_shutdown = mdm_crash_shutdown;
+ struct subsys_desc *subsys = &esoc_clink->subsys;
+
+ subsys->shutdown = mdm_subsys_shutdown;
+ subsys->ramdump = mdm_subsys_ramdumps;
+ subsys->powerup = mdm_subsys_powerup;
+ subsys->crash_shutdown = mdm_crash_shutdown;
return esoc_clink_register_ssr(esoc_clink);
}
diff --git a/drivers/esoc/esoc-mdm-pon.c b/drivers/esoc/esoc-mdm-pon.c
index 47d54db..0e85776 100644
--- a/drivers/esoc/esoc-mdm-pon.c
+++ b/drivers/esoc/esoc-mdm-pon.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -68,6 +68,9 @@
struct device *dev = mdm->dev;
dev_dbg(dev, "Powering on modem for the first time\n");
+ if (mdm->esoc->auto_boot)
+ return 0;
+
mdm_toggle_soft_reset(mdm, false);
/* Add a delay to allow PON sequence to complete*/
mdelay(50);
@@ -134,6 +137,9 @@
static void mdm4x_cold_reset(struct mdm_ctrl *mdm)
{
+ if (!gpio_is_valid(MDM_GPIO(mdm, AP2MDM_SOFT_RESET)))
+ return;
+
dev_dbg(mdm->dev, "Triggering mdm cold reset");
gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
!!mdm->soft_reset_inverted);
@@ -201,15 +207,6 @@
.setup = mdm4x_pon_setup,
};
-struct mdm_pon_ops mdm9x45_pon_ops = {
- .pon = mdm4x_do_first_power_on,
- .soft_reset = mdm4x_toggle_soft_reset,
- .poff_force = mdm4x_power_down,
- .cold_reset = mdm4x_cold_reset,
- .dt_init = mdm4x_pon_dt_init,
- .setup = mdm4x_pon_setup,
-};
-
struct mdm_pon_ops mdm9x55_pon_ops = {
.pon = mdm4x_do_first_power_on,
.soft_reset = mdm9x55_toggle_soft_reset,
diff --git a/drivers/esoc/esoc-mdm.h b/drivers/esoc/esoc-mdm.h
index fa3a576..621d913 100644
--- a/drivers/esoc/esoc-mdm.h
+++ b/drivers/esoc/esoc-mdm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -33,8 +33,6 @@
#define MDM9x35_PCIE "PCIe"
#define MDM9x35_DUAL_LINK "HSIC+PCIe"
#define MDM9x35_HSIC "HSIC"
-#define MDM9x45_LABEL "MDM9x45"
-#define MDM9x45_PCIE "PCIe"
#define MDM9x55_LABEL "MDM9x55"
#define MDM9x55_PCIE "PCIe"
#define MDM2AP_STATUS_TIMEOUT_MS 120000L
@@ -151,6 +149,5 @@
extern struct mdm_pon_ops mdm9x25_pon_ops;
extern struct mdm_pon_ops mdm9x35_pon_ops;
-extern struct mdm_pon_ops mdm9x45_pon_ops;
extern struct mdm_pon_ops mdm9x55_pon_ops;
#endif
diff --git a/drivers/esoc/esoc.h b/drivers/esoc/esoc.h
index 9fc3192..df3c9df 100644
--- a/drivers/esoc/esoc.h
+++ b/drivers/esoc/esoc.h
@@ -49,6 +49,7 @@
* @link_info: additional info about the physical link.
* @parent: parent device.
* @dev: device for userspace interface.
+ * @pdev: platform device to interface with SSR driver.
* @id: id of the external device.
* @owner: owner of the device.
* @clink_ops: control operations for the control link
@@ -59,6 +60,12 @@
* @subsys_desc: descriptor for subsystem restart
* @subsys_dev: ssr device handle.
* @np: device tree node for esoc_clink.
+ * @auto_boot: boots independently.
+ * @primary: primary esoc controls(reset/poweroff) all secondary
+ * esocs, but not otherway around.
+ * @statusline_not_a_powersource: True if status line to esoc is not a
+ * power source.
+ * @userspace_handle_shutdown: True if user space handles shutdown requests.
*/
struct esoc_clink {
const char *name;
@@ -66,6 +73,7 @@
const char *link_info;
struct device *parent;
struct device dev;
+ struct platform_device *pdev;
unsigned int id;
struct module *owner;
const struct esoc_clink_ops *clink_ops;
@@ -77,17 +85,23 @@
struct subsys_desc subsys;
struct subsys_device *subsys_dev;
struct device_node *np;
+ bool auto_boot;
+ bool primary;
+ bool statusline_not_a_powersource;
+ bool userspace_handle_shutdown;
};
/**
* struct esoc_clink_ops: Operations to control external soc
* @cmd_exe: Execute control command
* @get_status: Get current status, or response to previous command
+ * @get_err_fatal: Get status of err fatal signal
* @notify_esoc: notify external soc of events
*/
struct esoc_clink_ops {
int (*cmd_exe)(enum esoc_cmd cmd, struct esoc_clink *dev);
- int (*get_status)(u32 *status, struct esoc_clink *dev);
+ void (*get_status)(u32 *status, struct esoc_clink *dev);
+ void (*get_err_fatal)(u32 *status, struct esoc_clink *dev);
void (*notify)(enum esoc_notify notify, struct esoc_clink *dev);
};
diff --git a/drivers/esoc/esoc_bus.c b/drivers/esoc/esoc_bus.c
index cef570b..d9ab993 100644
--- a/drivers/esoc/esoc_bus.c
+++ b/drivers/esoc/esoc_bus.c
@@ -189,7 +189,7 @@
snprintf(subsys_name, len, "esoc%d", esoc_clink->id);
esoc_clink->subsys.name = subsys_name;
esoc_clink->dev.of_node = esoc_clink->np;
- esoc_clink->subsys.dev = &esoc_clink->dev;
+ esoc_clink->subsys.dev = &esoc_clink->pdev->dev;
esoc_clink->subsys_dev = subsys_register(&esoc_clink->subsys);
if (IS_ERR_OR_NULL(esoc_clink->subsys_dev)) {
dev_err(&esoc_clink->dev, "failed to register ssr node\n");
diff --git a/drivers/esoc/esoc_dev.c b/drivers/esoc/esoc_dev.c
index 0c9e428..1d9e623 100644
--- a/drivers/esoc/esoc_dev.c
+++ b/drivers/esoc/esoc_dev.c
@@ -224,9 +224,11 @@
clink_ops->notify(esoc_cmd, esoc_clink);
break;
case ESOC_GET_STATUS:
- err = clink_ops->get_status(&status, esoc_clink);
- if (err)
- return err;
+ clink_ops->get_status(&status, esoc_clink);
+ put_user(status, (unsigned int __user *)uarg);
+ break;
+ case ESOC_GET_ERR_FATAL:
+ clink_ops->get_err_fatal(&status, esoc_clink);
put_user(status, (unsigned int __user *)uarg);
break;
case ESOC_WAIT_FOR_CRASH:
@@ -336,7 +338,6 @@
esoc_udev = esoc_udev_get_by_minor(esoc_clink->id);
if (!esoc_udev)
return 0;
- return_esoc_udev(esoc_udev);
device_destroy(esoc_class, MKDEV(esoc_major, esoc_clink->id));
return_esoc_udev(esoc_udev);
return 0;
diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c
index 42f41e8..27f67c2 100644
--- a/drivers/extcon/extcon-axp288.c
+++ b/drivers/extcon/extcon-axp288.c
@@ -168,7 +168,7 @@
return ret;
}
- vbus_attach = (pwr_stat & PS_STAT_VBUS_PRESENT);
+ vbus_attach = (pwr_stat & PS_STAT_VBUS_VALID);
if (!vbus_attach)
goto notify_otg;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index 264899d..05ff98b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -491,6 +491,9 @@
case TTM_PL_TT:
break;
case TTM_PL_VRAM:
+ if (mem->start == AMDGPU_BO_INVALID_OFFSET)
+ return -EINVAL;
+
mem->bus.offset = mem->start << PAGE_SHIFT;
/* check if it's visible */
if ((mem->bus.offset + mem->bus.size) > adev->mc.visible_vram_size)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
index a6a4b2b..6a3470f 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
@@ -739,8 +739,10 @@
struct kfd_event_data event_data;
if (copy_from_user(&event_data, &events[i],
- sizeof(struct kfd_event_data)))
+ sizeof(struct kfd_event_data))) {
+ ret = -EFAULT;
goto fail;
+ }
ret = init_event_waiter(p, &event_waiters[i],
event_data.event_id, i);
diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c
index 82c193e..afe0480 100644
--- a/drivers/gpu/drm/arm/malidp_planes.c
+++ b/drivers/gpu/drm/arm/malidp_planes.c
@@ -150,13 +150,8 @@
/* convert src values from Q16 fixed point to integer */
src_w = plane->state->src_w >> 16;
src_h = plane->state->src_h >> 16;
- if (plane->state->rotation & MALIDP_ROTATED_MASK) {
- dest_w = plane->state->crtc_h;
- dest_h = plane->state->crtc_w;
- } else {
- dest_w = plane->state->crtc_w;
- dest_h = plane->state->crtc_h;
- }
+ dest_w = plane->state->crtc_w;
+ dest_h = plane->state->crtc_h;
malidp_hw_write(mp->hwdev, format_id, mp->layer->base);
@@ -189,9 +184,9 @@
if (plane->state->rotation & DRM_ROTATE_MASK)
val = ilog2(plane->state->rotation & DRM_ROTATE_MASK) << LAYER_ROT_OFFSET;
if (plane->state->rotation & DRM_REFLECT_X)
- val |= LAYER_V_FLIP;
- if (plane->state->rotation & DRM_REFLECT_Y)
val |= LAYER_H_FLIP;
+ if (plane->state->rotation & DRM_REFLECT_Y)
+ val |= LAYER_V_FLIP;
/* set the 'enable layer' bit */
val |= LAYER_ENABLE;
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 4e16dff..33778bf 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1871,10 +1871,10 @@
struct drm_atomic_state *state;
struct drm_modeset_acquire_ctx ctx;
struct drm_plane *plane;
- struct drm_out_fence_state *fence_state = NULL;
+ struct drm_out_fence_state *fence_state;
unsigned plane_mask;
int ret = 0;
- unsigned int i, j, num_fences = 0;
+ unsigned int i, j, num_fences;
/* disallow for drivers not supporting atomic: */
if (!drm_core_check_feature(dev, DRIVER_ATOMIC))
@@ -1915,6 +1915,8 @@
plane_mask = 0;
copied_objs = 0;
copied_props = 0;
+ fence_state = NULL;
+ num_fences = 0;
for (i = 0; i < arg->count_objs; i++) {
uint32_t obj_id, count_props;
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
index 0370b84..82dd57d 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
@@ -549,12 +549,15 @@
void etnaviv_gem_free_object(struct drm_gem_object *obj)
{
struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+ struct etnaviv_drm_private *priv = obj->dev->dev_private;
struct etnaviv_vram_mapping *mapping, *tmp;
/* object should not be active */
WARN_ON(is_active(etnaviv_obj));
+ mutex_lock(&priv->gem_lock);
list_del(&etnaviv_obj->gem_node);
+ mutex_unlock(&priv->gem_lock);
list_for_each_entry_safe(mapping, tmp, &etnaviv_obj->vram_list,
obj_node) {
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index 4ac36e3..80c5cc5 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -1152,6 +1152,13 @@
is_hdmi = is_dvi && (child->common.device_type & DEVICE_TYPE_NOT_HDMI_OUTPUT) == 0;
is_edp = is_dp && (child->common.device_type & DEVICE_TYPE_INTERNAL_CONNECTOR);
+ if (port == PORT_A && is_dvi) {
+ DRM_DEBUG_KMS("VBT claims port A supports DVI%s, ignoring\n",
+ is_hdmi ? "/HDMI" : "");
+ is_dvi = false;
+ is_hdmi = false;
+ }
+
info->supports_dvi = is_dvi;
info->supports_hdmi = is_hdmi;
info->supports_dp = is_dp;
@@ -1212,7 +1219,7 @@
{
enum port port;
- if (!HAS_DDI(dev_priv))
+ if (!HAS_DDI(dev_priv) && !IS_CHERRYVIEW(dev_priv))
return;
if (!dev_priv->vbt.child_dev_num)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index f8efd20..ce32303 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -11471,13 +11471,10 @@
{
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- enum transcoder cpu_transcoder = intel_crtc->config->cpu_transcoder;
+ enum transcoder cpu_transcoder;
struct drm_display_mode *mode;
struct intel_crtc_state *pipe_config;
- int htot = I915_READ(HTOTAL(cpu_transcoder));
- int hsync = I915_READ(HSYNC(cpu_transcoder));
- int vtot = I915_READ(VTOTAL(cpu_transcoder));
- int vsync = I915_READ(VSYNC(cpu_transcoder));
+ u32 htot, hsync, vtot, vsync;
enum pipe pipe = intel_crtc->pipe;
mode = kzalloc(sizeof(*mode), GFP_KERNEL);
@@ -11505,6 +11502,13 @@
i9xx_crtc_clock_get(intel_crtc, pipe_config);
mode->clock = pipe_config->port_clock / pipe_config->pixel_multiplier;
+
+ cpu_transcoder = pipe_config->cpu_transcoder;
+ htot = I915_READ(HTOTAL(cpu_transcoder));
+ hsync = I915_READ(HSYNC(cpu_transcoder));
+ vtot = I915_READ(VTOTAL(cpu_transcoder));
+ vsync = I915_READ(VSYNC(cpu_transcoder));
+
mode->hdisplay = (htot & 0xffff) + 1;
mode->htotal = ((htot & 0xffff0000) >> 16) + 1;
mode->hsync_start = (hsync & 0xffff) + 1;
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 7b06280..afa3d01 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -2193,8 +2193,8 @@
I915_WRITE(pp_ctrl_reg, pp);
POSTING_READ(pp_ctrl_reg);
- intel_dp->panel_power_off_time = ktime_get_boottime();
wait_panel_off(intel_dp);
+ intel_dp->panel_power_off_time = ktime_get_boottime();
/* We got a reference when we enabled the VDD. */
power_domain = intel_display_port_aux_power_domain(intel_encoder);
diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c
index a2655cd..8ab6f30 100644
--- a/drivers/gpu/drm/i915/intel_overlay.c
+++ b/drivers/gpu/drm/i915/intel_overlay.c
@@ -272,8 +272,30 @@
return intel_overlay_do_wait_request(overlay, req, NULL);
}
+static void intel_overlay_flip_prepare(struct intel_overlay *overlay,
+ struct i915_vma *vma)
+{
+ enum pipe pipe = overlay->crtc->pipe;
+
+ WARN_ON(overlay->old_vma);
+
+ i915_gem_track_fb(overlay->vma ? overlay->vma->obj : NULL,
+ vma ? vma->obj : NULL,
+ INTEL_FRONTBUFFER_OVERLAY(pipe));
+
+ intel_frontbuffer_flip_prepare(overlay->i915,
+ INTEL_FRONTBUFFER_OVERLAY(pipe));
+
+ overlay->old_vma = overlay->vma;
+ if (vma)
+ overlay->vma = i915_vma_get(vma);
+ else
+ overlay->vma = NULL;
+}
+
/* overlay needs to be enabled in OCMD reg */
static int intel_overlay_continue(struct intel_overlay *overlay,
+ struct i915_vma *vma,
bool load_polyphase_filter)
{
struct drm_i915_private *dev_priv = overlay->i915;
@@ -308,27 +330,35 @@
intel_ring_emit(ring, flip_addr);
intel_ring_advance(ring);
+ intel_overlay_flip_prepare(overlay, vma);
+
intel_overlay_submit_request(overlay, req, NULL);
return 0;
}
+static void intel_overlay_release_old_vma(struct intel_overlay *overlay)
+{
+ struct i915_vma *vma;
+
+ vma = fetch_and_zero(&overlay->old_vma);
+ if (WARN_ON(!vma))
+ return;
+
+ intel_frontbuffer_flip_complete(overlay->i915,
+ INTEL_FRONTBUFFER_OVERLAY(overlay->crtc->pipe));
+
+ i915_gem_object_unpin_from_display_plane(vma);
+ i915_vma_put(vma);
+}
+
static void intel_overlay_release_old_vid_tail(struct i915_gem_active *active,
struct drm_i915_gem_request *req)
{
struct intel_overlay *overlay =
container_of(active, typeof(*overlay), last_flip);
- struct i915_vma *vma;
- vma = fetch_and_zero(&overlay->old_vma);
- if (WARN_ON(!vma))
- return;
-
- i915_gem_track_fb(vma->obj, NULL,
- INTEL_FRONTBUFFER_OVERLAY(overlay->crtc->pipe));
-
- i915_gem_object_unpin_from_display_plane(vma);
- i915_vma_put(vma);
+ intel_overlay_release_old_vma(overlay);
}
static void intel_overlay_off_tail(struct i915_gem_active *active,
@@ -336,15 +366,8 @@
{
struct intel_overlay *overlay =
container_of(active, typeof(*overlay), last_flip);
- struct i915_vma *vma;
- /* never have the overlay hw on without showing a frame */
- vma = fetch_and_zero(&overlay->vma);
- if (WARN_ON(!vma))
- return;
-
- i915_gem_object_unpin_from_display_plane(vma);
- i915_vma_put(vma);
+ intel_overlay_release_old_vma(overlay);
overlay->crtc->overlay = NULL;
overlay->crtc = NULL;
@@ -398,6 +421,8 @@
}
intel_ring_advance(ring);
+ intel_overlay_flip_prepare(overlay, NULL);
+
return intel_overlay_do_wait_request(overlay, req,
intel_overlay_off_tail);
}
@@ -836,18 +861,10 @@
intel_overlay_unmap_regs(overlay, regs);
- ret = intel_overlay_continue(overlay, scale_changed);
+ ret = intel_overlay_continue(overlay, vma, scale_changed);
if (ret)
goto out_unpin;
- i915_gem_track_fb(overlay->vma ? overlay->vma->obj : NULL,
- vma->obj, INTEL_FRONTBUFFER_OVERLAY(pipe));
-
- overlay->old_vma = overlay->vma;
- overlay->vma = vma;
-
- intel_frontbuffer_flip(dev_priv, INTEL_FRONTBUFFER_OVERLAY(pipe));
-
return 0;
out_unpin:
@@ -1215,6 +1232,7 @@
mutex_unlock(&dev->struct_mutex);
drm_modeset_unlock_all(dev);
+ i915_gem_object_put(new_bo);
kfree(params);
diff --git a/drivers/gpu/drm/i915/intel_psr.c b/drivers/gpu/drm/i915/intel_psr.c
index 9b307ce..dff4784 100644
--- a/drivers/gpu/drm/i915/intel_psr.c
+++ b/drivers/gpu/drm/i915/intel_psr.c
@@ -387,6 +387,13 @@
return false;
}
+ /* PSR2 is restricted to work with panel resolutions upto 3200x2000 */
+ if (intel_crtc->config->pipe_src_w > 3200 ||
+ intel_crtc->config->pipe_src_h > 2000) {
+ dev_priv->psr.psr2_support = false;
+ return false;
+ }
+
dev_priv->psr.source_ok = true;
return true;
}
@@ -425,7 +432,6 @@
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
struct drm_device *dev = intel_dig_port->base.base.dev;
struct drm_i915_private *dev_priv = to_i915(dev);
- struct intel_crtc *crtc = to_intel_crtc(intel_dig_port->base.base.crtc);
if (!HAS_PSR(dev)) {
DRM_DEBUG_KMS("PSR not supported on this platform\n");
@@ -452,12 +458,7 @@
hsw_psr_setup_vsc(intel_dp);
if (dev_priv->psr.psr2_support) {
- /* PSR2 is restricted to work with panel resolutions upto 3200x2000 */
- if (crtc->config->pipe_src_w > 3200 ||
- crtc->config->pipe_src_h > 2000)
- dev_priv->psr.psr2_support = false;
- else
- skl_psr_setup_su_vsc(intel_dp);
+ skl_psr_setup_su_vsc(intel_dp);
}
/*
diff --git a/drivers/gpu/drm/msm/dp/dp_audio.c b/drivers/gpu/drm/msm/dp/dp_audio.c
index c4a60dc..6ac692f 100644
--- a/drivers/gpu/drm/msm/dp/dp_audio.c
+++ b/drivers/gpu/drm/msm/dp/dp_audio.c
@@ -428,7 +428,7 @@
audio->engine_on = enable;
}
-static struct dp_audio_private *get_audio_get_data(struct platform_device *pdev)
+static struct dp_audio_private *dp_audio_get_data(struct platform_device *pdev)
{
struct msm_ext_disp_data *ext_data;
struct dp_audio *dp_audio;
@@ -459,7 +459,7 @@
int rc = 0;
struct dp_audio_private *audio;
- audio = get_audio_get_data(pdev);
+ audio = dp_audio_get_data(pdev);
if (IS_ERR(audio)) {
rc = PTR_ERR(audio);
goto end;
@@ -482,7 +482,7 @@
struct dp_audio_private *audio;
struct sde_edid_ctrl *edid;
- audio = get_audio_get_data(pdev);
+ audio = dp_audio_get_data(pdev);
if (IS_ERR(audio)) {
rc = PTR_ERR(audio);
goto end;
@@ -510,18 +510,12 @@
int rc = 0;
struct dp_audio_private *audio;
- audio = get_audio_get_data(pdev);
+ audio = dp_audio_get_data(pdev);
if (IS_ERR(audio)) {
rc = PTR_ERR(audio);
goto end;
}
- if (!audio->panel) {
- pr_err("invalid panel data\n");
- rc = -EINVAL;
- goto end;
- }
-
return audio->session_on;
end:
return rc;
@@ -532,7 +526,7 @@
int rc = 0;
struct dp_audio_private *audio;
- audio = get_audio_get_data(pdev);
+ audio = dp_audio_get_data(pdev);
if (IS_ERR(audio)) {
rc = PTR_ERR(audio);
goto end;
@@ -547,15 +541,10 @@
{
struct dp_audio_private *audio;
- audio = get_audio_get_data(pdev);
+ audio = dp_audio_get_data(pdev);
if (IS_ERR(audio))
return;
- if (!audio->panel) {
- pr_err("invalid panel data\n");
- return;
- }
-
dp_audio_enable(audio, false);
complete_all(&audio->hpd_comp);
@@ -568,7 +557,7 @@
int rc = 0, ack_hpd;
struct dp_audio_private *audio;
- audio = get_audio_get_data(pdev);
+ audio = dp_audio_get_data(pdev);
if (IS_ERR(audio)) {
rc = PTR_ERR(audio);
goto end;
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index b9b996a..e11681e 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -550,8 +550,6 @@
pr_debug("DP_CONFIGURATION_CTRL=0x%x\n", cfg);
dp_write(base + DP_CONFIGURATION_CTRL, cfg);
- dp_write(base + DP_MAINLINK_LEVELS, 0xa08);
- dp_write(base + MMSS_DP_ASYNC_FIFO_CONFIG, 0x1);
}
static void dp_catalog_ctrl_lane_mapping(struct dp_catalog_ctrl *ctrl)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 13ca6b2..debc0a5 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -973,7 +973,7 @@
}
/* print success info as this is a result of user initiated action */
- pr_debug("link training #2 successful\n");
+ pr_info("link training #2 successful\n");
end:
dp_ctrl_state_ctrl(ctrl, 0);
@@ -1442,6 +1442,7 @@
ctrl->aux = in->aux;
ctrl->link = in->link;
ctrl->catalog = in->catalog;
+ ctrl->dev = in->dev;
dp_ctrl = &ctrl->dp_ctrl;
diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c
index 92ac0ec..9ef070c 100644
--- a/drivers/gpu/drm/msm/dp/dp_debug.c
+++ b/drivers/gpu/drm/msm/dp/dp_debug.c
@@ -29,6 +29,11 @@
struct dp_debug_private {
struct dentry *root;
+ u8 *edid;
+ u32 edid_size;
+
+ u8 *dpcd;
+ u32 dpcd_size;
struct dp_usbpd *usbpd;
struct dp_link *link;
@@ -39,6 +44,138 @@
struct dp_debug dp_debug;
};
+static ssize_t dp_debug_write_edid(struct file *file,
+ const char __user *user_buff, size_t count, loff_t *ppos)
+{
+ struct dp_debug_private *debug = file->private_data;
+ u8 *buf = NULL, *buf_t = NULL, *edid = NULL;
+ const int char_to_nib = 2;
+ size_t edid_size = 0;
+ size_t size = 0, edid_buf_index = 0;
+ ssize_t rc = count;
+
+ if (!debug)
+ return -ENODEV;
+
+ if (*ppos)
+ goto bail;
+
+ size = min_t(size_t, count, SZ_1K);
+
+ buf = kzalloc(size, GFP_KERNEL);
+ if (!buf) {
+ rc = -ENOMEM;
+ goto bail;
+ }
+
+ if (copy_from_user(buf, user_buff, size))
+ goto bail;
+
+ edid_size = size / char_to_nib;
+ buf_t = buf;
+
+ memset(debug->edid, 0, debug->edid_size);
+
+ if (edid_size != debug->edid_size) {
+ pr_debug("clearing debug edid\n");
+ goto bail;
+ }
+
+ while (edid_size--) {
+ char t[3];
+ int d;
+
+ memcpy(t, buf_t, sizeof(char) * char_to_nib);
+ t[char_to_nib] = '\0';
+
+ if (kstrtoint(t, 16, &d)) {
+ pr_err("kstrtoint error\n");
+ goto bail;
+ }
+
+ if (edid_buf_index < debug->edid_size)
+ debug->edid[edid_buf_index++] = d;
+
+ buf_t += char_to_nib;
+ }
+
+ print_hex_dump(KERN_DEBUG, "DEBUG EDID: ", DUMP_PREFIX_NONE,
+ 16, 1, debug->edid, debug->edid_size, false);
+
+ edid = debug->edid;
+bail:
+ kfree(buf);
+ debug->panel->set_edid(debug->panel, edid);
+ return rc;
+}
+
+static ssize_t dp_debug_write_dpcd(struct file *file,
+ const char __user *user_buff, size_t count, loff_t *ppos)
+{
+ struct dp_debug_private *debug = file->private_data;
+ u8 *buf = NULL, *buf_t = NULL, *dpcd = NULL;
+ const int char_to_nib = 2;
+ size_t dpcd_size = 0;
+ size_t size = 0, dpcd_buf_index = 0;
+ ssize_t rc = count;
+
+ pr_debug("count=%zu\n", count);
+
+ if (!debug)
+ return -ENODEV;
+
+ if (*ppos)
+ goto bail;
+
+ size = min_t(size_t, count, SZ_32);
+
+ buf = kzalloc(size, GFP_KERNEL);
+ if (!buf) {
+ rc = -ENOMEM;
+ goto bail;
+ }
+
+ if (copy_from_user(buf, user_buff, size))
+ goto bail;
+
+ dpcd_size = size / char_to_nib;
+ buf_t = buf;
+
+ memset(debug->dpcd, 0, debug->dpcd_size);
+
+ if (dpcd_size != debug->dpcd_size) {
+ pr_debug("clearing debug dpcd\n");
+ goto bail;
+ }
+
+ while (dpcd_size--) {
+ char t[3];
+ int d;
+
+ memcpy(t, buf_t, sizeof(char) * char_to_nib);
+ t[char_to_nib] = '\0';
+
+ if (kstrtoint(t, 16, &d)) {
+ pr_err("kstrtoint error\n");
+ goto bail;
+ }
+
+ if (dpcd_buf_index < debug->dpcd_size)
+ debug->dpcd[dpcd_buf_index++] = d;
+
+ buf_t += char_to_nib;
+ }
+
+ print_hex_dump(KERN_DEBUG, "DEBUG DPCD: ", DUMP_PREFIX_NONE,
+ 8, 1, debug->dpcd, debug->dpcd_size, false);
+
+ dpcd = debug->dpcd;
+bail:
+ kfree(buf);
+ debug->panel->set_dpcd(debug->panel, dpcd);
+ return rc;
+}
+
static ssize_t dp_debug_write_hpd(struct file *file,
const char __user *user_buff, size_t count, loff_t *ppos)
{
@@ -431,6 +568,16 @@
.write = dp_debug_write_hpd,
};
+static const struct file_operations edid_fops = {
+ .open = simple_open,
+ .write = dp_debug_write_edid,
+};
+
+static const struct file_operations dpcd_fops = {
+ .open = simple_open,
+ .write = dp_debug_write_dpcd,
+};
+
static const struct file_operations connected_fops = {
.open = simple_open,
.read = dp_debug_read_connected,
@@ -447,10 +594,7 @@
int rc = 0;
struct dp_debug_private *debug = container_of(dp_debug,
struct dp_debug_private, dp_debug);
- struct dentry *dir, *file, *edid_modes;
- struct dentry *hpd, *connected;
- struct dentry *max_bw_code;
- struct dentry *root = debug->root;
+ struct dentry *dir, *file;
dir = debugfs_create_dir(DEBUG_NAME, NULL);
if (IS_ERR_OR_NULL(dir)) {
@@ -460,6 +604,8 @@
goto error;
}
+ debug->root = dir;
+
file = debugfs_create_file("dp_debug", 0444, dir,
debug, &dp_debug_fops);
if (IS_ERR_OR_NULL(file)) {
@@ -469,46 +615,64 @@
goto error_remove_dir;
}
- edid_modes = debugfs_create_file("edid_modes", 0644, dir,
+ file = debugfs_create_file("edid_modes", 0644, dir,
debug, &edid_modes_fops);
- if (IS_ERR_OR_NULL(edid_modes)) {
- rc = PTR_ERR(edid_modes);
+ if (IS_ERR_OR_NULL(file)) {
+ rc = PTR_ERR(file);
pr_err("[%s] debugfs create edid_modes failed, rc=%d\n",
DEBUG_NAME, rc);
goto error_remove_dir;
}
- hpd = debugfs_create_file("hpd", 0644, dir,
+ file = debugfs_create_file("hpd", 0644, dir,
debug, &hpd_fops);
- if (IS_ERR_OR_NULL(hpd)) {
- rc = PTR_ERR(hpd);
+ if (IS_ERR_OR_NULL(file)) {
+ rc = PTR_ERR(file);
pr_err("[%s] debugfs hpd failed, rc=%d\n",
DEBUG_NAME, rc);
goto error_remove_dir;
}
- connected = debugfs_create_file("connected", 0444, dir,
+ file = debugfs_create_file("connected", 0444, dir,
debug, &connected_fops);
- if (IS_ERR_OR_NULL(connected)) {
- rc = PTR_ERR(connected);
+ if (IS_ERR_OR_NULL(file)) {
+ rc = PTR_ERR(file);
pr_err("[%s] debugfs connected failed, rc=%d\n",
DEBUG_NAME, rc);
goto error_remove_dir;
}
- max_bw_code = debugfs_create_file("max_bw_code", 0644, dir,
+ file = debugfs_create_file("max_bw_code", 0644, dir,
debug, &bw_code_fops);
- if (IS_ERR_OR_NULL(max_bw_code)) {
- rc = PTR_ERR(max_bw_code);
+ if (IS_ERR_OR_NULL(file)) {
+ rc = PTR_ERR(file);
pr_err("[%s] debugfs max_bw_code failed, rc=%d\n",
DEBUG_NAME, rc);
goto error_remove_dir;
}
- root = dir;
- return rc;
+ file = debugfs_create_file("edid", 0644, dir,
+ debug, &edid_fops);
+ if (IS_ERR_OR_NULL(file)) {
+ rc = PTR_ERR(file);
+ pr_err("[%s] debugfs edid failed, rc=%d\n",
+ DEBUG_NAME, rc);
+ goto error_remove_dir;
+ }
+
+ file = debugfs_create_file("dpcd", 0644, dir,
+ debug, &dpcd_fops);
+ if (IS_ERR_OR_NULL(file)) {
+ rc = PTR_ERR(file);
+ pr_err("[%s] debugfs dpcd failed, rc=%d\n",
+ DEBUG_NAME, rc);
+ goto error_remove_dir;
+ }
+
+ return 0;
+
error_remove_dir:
- debugfs_remove(dir);
+ debugfs_remove_recursive(dir);
error:
return rc;
}
@@ -533,6 +697,24 @@
goto error;
}
+ debug->edid = devm_kzalloc(dev, SZ_256, GFP_KERNEL);
+ if (!debug->edid) {
+ rc = -ENOMEM;
+ kfree(debug);
+ goto error;
+ }
+
+ debug->edid_size = SZ_256;
+
+ debug->dpcd = devm_kzalloc(dev, SZ_16, GFP_KERNEL);
+ if (!debug->dpcd) {
+ rc = -ENOMEM;
+ kfree(debug);
+ goto error;
+ }
+
+ debug->dpcd_size = SZ_16;
+
debug->dp_debug.debug_en = false;
debug->usbpd = usbpd;
debug->link = link;
@@ -565,7 +747,7 @@
debug = container_of(dp_debug, struct dp_debug_private, dp_debug);
- debugfs_remove(debug->root);
+ debugfs_remove_recursive(debug->root);
return 0;
}
@@ -581,5 +763,7 @@
dp_debug_deinit(dp_debug);
+ devm_kfree(debug->dev, debug->edid);
+ devm_kfree(debug->dev, debug->dpcd);
devm_kfree(debug->dev, debug);
}
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 7fbc63a..7eb2306 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -375,12 +375,6 @@
goto end;
}
- rc = dp->panel->sde_edid_register(dp->panel);
- if (rc) {
- pr_err("DRM DP EDID register failed\n");
- goto end;
- }
-
rc = dp->power->power_client_init(dp->power, &priv->phandle);
if (rc) {
pr_err("Power client create failed\n");
@@ -414,7 +408,6 @@
}
(void)dp->power->power_client_deinit(dp->power);
- (void)dp->panel->sde_edid_deregister(dp->panel);
(void)dp->aux->drm_aux_deregister(dp->aux);
dp_display_deinitialize_hdcp(dp);
}
@@ -512,7 +505,6 @@
static int dp_display_process_hpd_high(struct dp_display_private *dp)
{
int rc = 0;
- u32 max_pclk_from_edid = 0;
struct edid *edid;
dp->aux->init(dp->aux, dp->parser->aux_cfg);
@@ -538,11 +530,7 @@
dp->panel->handle_sink_request(dp->panel);
- max_pclk_from_edid = dp->panel->get_max_pclk(dp->panel);
-
- dp->dp_display.max_pclk_khz = min(max_pclk_from_edid,
- dp->parser->max_pclk_khz);
-
+ dp->dp_display.max_pclk_khz = dp->parser->max_pclk_khz;
notify:
dp_display_send_hpd_notification(dp, true);
@@ -909,6 +897,7 @@
static int dp_display_set_mode(struct dp_display *dp_display,
struct dp_display_mode *mode)
{
+ const u32 num_components = 3, default_bpp = 24;
struct dp_display_private *dp;
if (!dp_display) {
@@ -918,8 +907,16 @@
dp = container_of(dp_display, struct dp_display_private, dp_display);
mutex_lock(&dp->session_lock);
+ mode->timing.bpp =
+ dp_display->connector->display_info.bpc * num_components;
+ if (!mode->timing.bpp)
+ mode->timing.bpp = default_bpp;
+
+ mode->timing.bpp = dp->panel->get_mode_bpp(dp->panel,
+ mode->timing.bpp, mode->timing.pixel_clk_khz);
+
dp->panel->pinfo = mode->timing;
- dp->panel->init_info(dp->panel);
+ dp->panel->init(dp->panel);
mutex_unlock(&dp->session_lock);
return 0;
@@ -1054,6 +1051,7 @@
}
dp->ctrl->off(dp->ctrl);
+ dp->panel->deinit(dp->panel);
dp->power_on = false;
@@ -1113,10 +1111,35 @@
return 0;
}
-static int dp_display_validate_mode(struct dp_display *dp,
- struct dp_display_mode *mode)
+static int dp_display_validate_mode(struct dp_display *dp, u32 mode_pclk_khz)
{
- return 0;
+ const u32 num_components = 3, default_bpp = 24;
+ struct dp_display_private *dp_display;
+ struct drm_dp_link *link_info;
+ u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0;
+
+ if (!dp || !mode_pclk_khz) {
+ pr_err("invalid params\n");
+ return -EINVAL;
+ }
+
+ dp_display = container_of(dp, struct dp_display_private, dp_display);
+ link_info = &dp_display->panel->link_info;
+
+ mode_bpp = dp->connector->display_info.bpc * num_components;
+ if (!mode_bpp)
+ mode_bpp = default_bpp;
+
+ mode_bpp = dp_display->panel->get_mode_bpp(dp_display->panel,
+ mode_bpp, mode_pclk_khz);
+
+ mode_rate_khz = mode_pclk_khz * mode_bpp;
+ supported_rate_khz = link_info->num_lanes * link_info->rate * 8;
+
+ if (mode_rate_khz > supported_rate_khz)
+ return MODE_BAD;
+
+ return MODE_OK;
}
static int dp_display_get_modes(struct dp_display *dp,
@@ -1139,38 +1162,6 @@
return ret;
}
-static bool dp_display_check_video_test(struct dp_display *dp)
-{
- struct dp_display_private *dp_display;
-
- if (!dp) {
- pr_err("invalid params\n");
- return false;
- }
-
- dp_display = container_of(dp, struct dp_display_private, dp_display);
-
- if (dp_display->panel->video_test)
- return true;
-
- return false;
-}
-
-static int dp_display_get_test_bpp(struct dp_display *dp)
-{
- struct dp_display_private *dp_display;
-
- if (!dp) {
- pr_err("invalid params\n");
- return 0;
- }
-
- dp_display = container_of(dp, struct dp_display_private, dp_display);
-
- return dp_link_bit_depth_to_bpp(
- dp_display->link->test_video.test_bit_depth);
-}
-
static int dp_display_probe(struct platform_device *pdev)
{
int rc = 0;
@@ -1212,8 +1203,6 @@
g_dp_display->request_irq = dp_request_irq;
g_dp_display->get_debug = dp_get_debug;
g_dp_display->send_hpd_event = dp_display_send_hpd_event;
- g_dp_display->is_video_test = dp_display_check_video_test;
- g_dp_display->get_test_bpp = dp_display_get_test_bpp;
rc = component_add(&pdev->dev, &dp_display_comp_ops);
if (rc) {
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 5539d61..844db61 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -34,8 +34,7 @@
int (*set_mode)(struct dp_display *dp_display,
struct dp_display_mode *mode);
- int (*validate_mode)(struct dp_display *dp_display,
- struct dp_display_mode *mode);
+ int (*validate_mode)(struct dp_display *dp_display, u32 mode_pclk_khz);
int (*get_modes)(struct dp_display *dp_display,
struct dp_display_mode *dp_mode);
int (*prepare)(struct dp_display *dp_display);
@@ -43,8 +42,6 @@
int (*request_irq)(struct dp_display *dp_display);
struct dp_debug *(*get_debug)(struct dp_display *dp_display);
void (*send_hpd_event)(struct dp_display *dp_display);
- bool (*is_video_test)(struct dp_display *dp_display);
- int (*get_test_bpp)(struct dp_display *dp_display);
};
int dp_display_get_num_of_displays(void);
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index 170734f..f7a041d 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -29,8 +29,6 @@
static void convert_to_dp_mode(const struct drm_display_mode *drm_mode,
struct dp_display_mode *dp_mode, struct dp_display *dp)
{
- const u32 num_components = 3;
-
memset(dp_mode, 0, sizeof(*dp_mode));
dp_mode->timing.h_active = drm_mode->hdisplay;
@@ -49,15 +47,6 @@
dp_mode->timing.v_front_porch = drm_mode->vsync_start -
drm_mode->vdisplay;
- if (dp->is_video_test(dp))
- dp_mode->timing.bpp = dp->get_test_bpp(dp);
- else
- dp_mode->timing.bpp = dp->connector->display_info.bpc *
- num_components;
-
- if (!dp_mode->timing.bpp)
- dp_mode->timing.bpp = 24;
-
dp_mode->timing.refresh_rate = drm_mode->vrefresh;
dp_mode->timing.pixel_clk_khz = drm_mode->clock;
@@ -254,7 +243,6 @@
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
- int rc = 0;
bool ret = true;
struct dp_display_mode dp_mode;
struct dp_bridge *bridge;
@@ -270,14 +258,7 @@
dp = bridge->display;
convert_to_dp_mode(mode, &dp_mode, dp);
-
- rc = dp->validate_mode(dp, &dp_mode);
- if (rc) {
- pr_err("[%d] mode is not valid, rc=%d\n", bridge->id, rc);
- ret = false;
- } else {
- convert_to_drm_mode(&dp_mode, adjusted_mode);
- }
+ convert_to_drm_mode(&dp_mode, adjusted_mode);
end:
return ret;
}
@@ -293,8 +274,7 @@
};
int dp_connector_post_init(struct drm_connector *connector,
- void *info,
- void *display)
+ void *info, void *display, struct msm_mode_info *mode_info)
{
struct dp_display *dp_display = display;
@@ -528,5 +508,5 @@
mode->picture_aspect_ratio != debug->aspect_ratio))
return MODE_BAD;
- return MODE_OK;
+ return dp_disp->validate_mode(dp_disp, mode->clock);
}
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h
index eb78e71..eede197 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_drm.h
@@ -36,11 +36,13 @@
* @connector: Pointer to drm connector structure
* @info: Pointer to sde connector info structure
* @display: Pointer to private display handle
+ * @mode_info: Pointer to mode info structure
* Returns: Zero on success
*/
int dp_connector_post_init(struct drm_connector *connector,
void *info,
- void *display);
+ void *display,
+ struct msm_mode_info *mode_info);
/**
* dp_connector_detect - callback to determine if connector is connected
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index fc3fb56..bee0ad5 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -30,6 +30,8 @@
struct dp_link *link;
struct dp_catalog_panel *catalog;
bool aux_cfg_update_done;
+ bool custom_edid;
+ bool custom_dpcd;
};
static const struct dp_panel_info fail_safe = {
@@ -69,12 +71,17 @@
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
link_info = &dp_panel->link_info;
- rlen = drm_dp_dpcd_read(panel->aux->drm_aux, DP_DPCD_REV,
- dpcd, (DP_RECEIVER_CAP_SIZE + 1));
- if (rlen < (DP_RECEIVER_CAP_SIZE + 1)) {
- pr_err("dpcd read failed, rlen=%d\n", rlen);
- rc = -EINVAL;
- goto end;
+ if (!panel->custom_dpcd) {
+ rlen = drm_dp_dpcd_read(panel->aux->drm_aux, DP_DPCD_REV,
+ dp_panel->dpcd, (DP_RECEIVER_CAP_SIZE + 1));
+ if (rlen < (DP_RECEIVER_CAP_SIZE + 1)) {
+ pr_err("dpcd read failed, rlen=%d\n", rlen);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ print_hex_dump(KERN_DEBUG, "[drm-dp] SINK DPCD: ",
+ DUMP_PREFIX_NONE, 8, 1, dp_panel->dpcd, rlen, false);
}
link_info->revision = dp_panel->dpcd[DP_DPCD_REV];
@@ -133,6 +140,52 @@
link_info->num_lanes = default_num_lanes;
pr_debug("link_rate=%d num_lanes=%d\n",
link_info->rate, link_info->num_lanes);
+
+ return 0;
+}
+
+static int dp_panel_set_edid(struct dp_panel *dp_panel, u8 *edid)
+{
+ struct dp_panel_private *panel;
+
+ if (!dp_panel) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+ if (edid) {
+ dp_panel->edid_ctrl->edid = (struct edid *)edid;
+ panel->custom_edid = true;
+ } else {
+ panel->custom_edid = false;
+ }
+
+ return 0;
+}
+
+static int dp_panel_set_dpcd(struct dp_panel *dp_panel, u8 *dpcd)
+{
+ struct dp_panel_private *panel;
+ u8 *dp_dpcd;
+
+ if (!dp_panel) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ dp_dpcd = dp_panel->dpcd;
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+ if (dpcd) {
+ memcpy(dp_dpcd, dpcd, DP_RECEIVER_CAP_SIZE + 1);
+ panel->custom_dpcd = true;
+ } else {
+ panel->custom_dpcd = false;
+ }
+
return 0;
}
@@ -150,6 +203,11 @@
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+ if (panel->custom_edid) {
+ pr_debug("skip edid read in debug mode\n");
+ return 0;
+ }
+
do {
sde_get_edid(connector, &panel->aux->drm_aux->ddc,
(void **)&dp_panel->edid_ctrl);
@@ -159,6 +217,12 @@
panel->aux->reconfig(panel->aux);
panel->aux_cfg_update_done = true;
} else {
+ u8 *buf = (u8 *)dp_panel->edid_ctrl->edid;
+ u32 size = buf[0x7F] ? 256 : 128;
+
+ print_hex_dump(KERN_DEBUG, "[drm-dp] SINK EDID: ",
+ DUMP_PREFIX_NONE, 16, 1, buf, size, false);
+
return 0;
}
} while (retry_cnt < max_retry);
@@ -204,32 +268,48 @@
return 0;
}
-static u32 dp_panel_get_max_pclk(struct dp_panel *dp_panel)
+static u32 dp_panel_get_supported_bpp(struct dp_panel *dp_panel,
+ u32 mode_edid_bpp, u32 mode_pclk_khz)
{
struct drm_dp_link *link_info;
- const u8 num_components = 3;
- u32 bpc = 0, bpp = 0, max_data_rate_khz = 0, max_pclk_rate_khz = 0;
+ const u32 max_supported_bpp = 30, min_supported_bpp = 18;
+ u32 bpp = 0, data_rate_khz = 0;
- if (!dp_panel) {
+ bpp = min_t(u32, mode_edid_bpp, max_supported_bpp);
+
+ link_info = &dp_panel->link_info;
+ data_rate_khz = link_info->num_lanes * link_info->rate * 8;
+
+ while (bpp > min_supported_bpp) {
+ if (mode_pclk_khz * bpp <= data_rate_khz)
+ break;
+ bpp -= 6;
+ }
+
+ return bpp;
+}
+
+static u32 dp_panel_get_mode_bpp(struct dp_panel *dp_panel,
+ u32 mode_edid_bpp, u32 mode_pclk_khz)
+{
+ struct dp_panel_private *panel;
+ u32 bpp = mode_edid_bpp;
+
+ if (!dp_panel || !mode_edid_bpp || !mode_pclk_khz) {
pr_err("invalid input\n");
return 0;
}
- link_info = &dp_panel->link_info;
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
- bpc = sde_get_sink_bpc(dp_panel->edid_ctrl);
- bpp = bpc * num_components;
- if (!bpp)
- bpp = DP_PANEL_DEFAULT_BPP;
+ if (dp_panel->video_test)
+ bpp = dp_link_bit_depth_to_bpp(
+ panel->link->test_video.test_bit_depth);
+ else
+ bpp = dp_panel_get_supported_bpp(dp_panel, mode_edid_bpp,
+ mode_pclk_khz);
- max_data_rate_khz = (link_info->num_lanes * link_info->rate * 8);
- max_pclk_rate_khz = max_data_rate_khz / bpp;
-
- pr_debug("bpp=%d, max_lane_cnt=%d\n", bpp, link_info->num_lanes);
- pr_debug("max_data_rate=%dKHz, max_pclk_rate=%dKHz\n",
- max_data_rate_khz, max_pclk_rate_khz);
-
- return max_pclk_rate_khz;
+ return bpp;
}
static void dp_panel_set_test_mode(struct dp_panel_private *panel,
@@ -383,34 +463,22 @@
return rc;
}
-static int dp_panel_edid_register(struct dp_panel *dp_panel)
+static int dp_panel_edid_register(struct dp_panel_private *panel)
{
int rc = 0;
- if (!dp_panel) {
- pr_err("invalid input\n");
- rc = -EINVAL;
- goto end;
- }
-
- dp_panel->edid_ctrl = sde_edid_init();
- if (!dp_panel->edid_ctrl) {
+ panel->dp_panel.edid_ctrl = sde_edid_init();
+ if (!panel->dp_panel.edid_ctrl) {
pr_err("sde edid init for DP failed\n");
rc = -ENOMEM;
- goto end;
}
-end:
+
return rc;
}
-static void dp_panel_edid_deregister(struct dp_panel *dp_panel)
+static void dp_panel_edid_deregister(struct dp_panel_private *panel)
{
- if (!dp_panel) {
- pr_err("invalid input\n");
- return;
- }
-
- sde_edid_deinit((void **)&dp_panel->edid_ctrl);
+ sde_edid_deinit((void **)&panel->dp_panel.edid_ctrl);
}
static int dp_panel_init_panel_info(struct dp_panel *dp_panel)
@@ -445,13 +513,30 @@
pr_info("bpp = %d\n", pinfo->bpp);
pr_info("active low (h|v)=(%d|%d)\n", pinfo->h_active_low,
pinfo->v_active_low);
-
- pinfo->bpp = max_t(u32, 18, min_t(u32, pinfo->bpp, 30));
- pr_info("updated bpp = %d\n", pinfo->bpp);
end:
return rc;
}
+static int dp_panel_deinit_panel_info(struct dp_panel *dp_panel)
+{
+ int rc = 0;
+ struct dp_panel_private *panel;
+
+ if (!dp_panel) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+ if (!panel->custom_edid)
+ sde_free_edid((void **)&dp_panel->edid_ctrl);
+
+ memset(&dp_panel->pinfo, 0, sizeof(dp_panel->pinfo));
+
+ return rc;
+}
+
static u32 dp_panel_get_min_req_link_rate(struct dp_panel *dp_panel)
{
const u32 encoding_factx10 = 8;
@@ -505,15 +590,18 @@
panel->aux_cfg_update_done = false;
dp_panel->max_bw_code = DP_LINK_BW_8_1;
- dp_panel->sde_edid_register = dp_panel_edid_register;
- dp_panel->sde_edid_deregister = dp_panel_edid_deregister;
- dp_panel->init_info = dp_panel_init_panel_info;
+ dp_panel->init = dp_panel_init_panel_info;
+ dp_panel->deinit = dp_panel_deinit_panel_info;
dp_panel->timing_cfg = dp_panel_timing_cfg;
dp_panel->read_sink_caps = dp_panel_read_sink_caps;
dp_panel->get_min_req_link_rate = dp_panel_get_min_req_link_rate;
- dp_panel->get_max_pclk = dp_panel_get_max_pclk;
+ dp_panel->get_mode_bpp = dp_panel_get_mode_bpp;
dp_panel->get_modes = dp_panel_get_modes;
dp_panel->handle_sink_request = dp_panel_handle_sink_request;
+ dp_panel->set_edid = dp_panel_set_edid;
+ dp_panel->set_dpcd = dp_panel_set_dpcd;
+
+ dp_panel_edid_register(panel);
return dp_panel;
error:
@@ -529,5 +617,6 @@
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+ dp_panel_edid_deregister(panel);
devm_kfree(panel->dev, panel);
}
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
index 01a978a..d1924d7 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -59,12 +59,11 @@
struct dp_panel {
/* dpcd raw data */
- u8 dpcd[DP_RECEIVER_CAP_SIZE];
+ u8 dpcd[DP_RECEIVER_CAP_SIZE + 1];
u8 ds_ports[DP_MAX_DOWNSTREAM_PORTS];
struct drm_dp_link link_info;
struct sde_edid_ctrl *edid_ctrl;
- struct drm_connector *connector;
struct dp_panel_info pinfo;
bool video_test;
@@ -74,17 +73,19 @@
/* debug */
u32 max_bw_code;
- int (*sde_edid_register)(struct dp_panel *dp_panel);
- void (*sde_edid_deregister)(struct dp_panel *dp_panel);
- int (*init_info)(struct dp_panel *dp_panel);
+ int (*init)(struct dp_panel *dp_panel);
+ int (*deinit)(struct dp_panel *dp_panel);
int (*timing_cfg)(struct dp_panel *dp_panel);
int (*read_sink_caps)(struct dp_panel *dp_panel,
struct drm_connector *connector);
u32 (*get_min_req_link_rate)(struct dp_panel *dp_panel);
- u32 (*get_max_pclk)(struct dp_panel *dp_panel);
+ u32 (*get_mode_bpp)(struct dp_panel *dp_panel, u32 mode_max_bpp,
+ u32 mode_pclk_khz);
int (*get_modes)(struct dp_panel *dp_panel,
struct drm_connector *connector, struct dp_display_mode *mode);
void (*handle_sink_request)(struct dp_panel *dp_panel);
+ int (*set_edid)(struct dp_panel *dp_panel, u8 *edid);
+ int (*set_dpcd)(struct dp_panel *dp_panel, u8 *dpcd);
};
/**
diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c
index f7201ec..42d9429 100644
--- a/drivers/gpu/drm/msm/dp/dp_parser.c
+++ b/drivers/gpu/drm/msm/dp/dp_parser.c
@@ -441,6 +441,22 @@
mp->num_clk = 0;
}
+static void dp_parser_put_gpio_data(struct device *dev,
+ struct dss_module_power *mp)
+{
+ if (!mp) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ if (mp->gpio_config) {
+ devm_kfree(dev, mp->gpio_config);
+ mp->gpio_config = NULL;
+ }
+
+ mp->num_gpio = 0;
+}
+
static int dp_parser_init_clk_data(struct dp_parser *parser)
{
int num_clk = 0, i = 0, rc = 0;
@@ -637,11 +653,9 @@
power = parser->mp;
for (i = 0; i < DP_MAX_PM; i++) {
- struct dss_module_power *mp = &power[i];
-
- devm_kfree(&parser->pdev->dev, mp->clk_config);
- devm_kfree(&parser->pdev->dev, mp->vreg_config);
- devm_kfree(&parser->pdev->dev, mp->gpio_config);
+ dp_parser_put_clk_data(&parser->pdev->dev, &power[i]);
+ dp_parser_put_vreg_data(&parser->pdev->dev, &power[i]);
+ dp_parser_put_gpio_data(&parser->pdev->dev, &power[i]);
}
devm_kfree(&parser->pdev->dev, parser);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
index 21a23e2..02ff005 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
@@ -889,7 +889,6 @@
if (packet->payload_length > 0)
buf[3] |= BIT(6);
- buf[3] |= BIT(7);
/* send embedded BTA for read commands */
if ((buf[2] & 0x3f) == MIPI_DSI_DCS_READ)
@@ -953,20 +952,21 @@
goto error;
}
+ rc = dsi_ctrl_copy_and_pad_cmd(dsi_ctrl,
+ &packet,
+ &buffer,
+ &length);
+ if (rc) {
+ pr_err("[%s] failed to copy message, rc=%d\n",
+ dsi_ctrl->name, rc);
+ goto error;
+ }
+
+ if ((msg->flags & MIPI_DSI_MSG_LASTCOMMAND))
+ buffer[3] |= BIT(7);//set the last cmd bit in header.
+
if (flags & DSI_CTRL_CMD_FETCH_MEMORY) {
- rc = dsi_ctrl_copy_and_pad_cmd(dsi_ctrl,
- &packet,
- &buffer,
- &length);
-
- if (rc) {
- pr_err("[%s] failed to copy message, rc=%d\n",
- dsi_ctrl->name, rc);
- goto error;
- }
-
cmd_mem.offset = dsi_ctrl->cmd_buffer_iova;
- cmd_mem.length = length;
cmd_mem.en_broadcast = (flags & DSI_CTRL_CMD_BROADCAST) ?
true : false;
cmd_mem.is_master = (flags & DSI_CTRL_CMD_BROADCAST_MASTER) ?
@@ -975,19 +975,20 @@
true : false;
cmdbuf = (u8 *)(dsi_ctrl->vaddr);
+
for (cnt = 0; cnt < length; cnt++)
- cmdbuf[cnt] = buffer[cnt];
+ cmdbuf[dsi_ctrl->cmd_len + cnt] = buffer[cnt];
+
+ dsi_ctrl->cmd_len += length;
+
+ if (!(msg->flags & MIPI_DSI_MSG_LASTCOMMAND)) {
+ goto error;
+ } else {
+ cmd_mem.length = dsi_ctrl->cmd_len;
+ dsi_ctrl->cmd_len = 0;
+ }
} else if (flags & DSI_CTRL_CMD_FIFO_STORE) {
- rc = dsi_ctrl_copy_and_pad_cmd(dsi_ctrl,
- &packet,
- &buffer,
- &length);
- if (rc) {
- pr_err("[%s] failed to copy message, rc=%d\n",
- dsi_ctrl->name, rc);
- goto error;
- }
cmd.command = (u32 *)buffer;
cmd.size = length;
cmd.en_broadcast = (flags & DSI_CTRL_CMD_BROADCAST) ?
@@ -1001,6 +1002,9 @@
hw_flags |= (flags & DSI_CTRL_CMD_DEFER_TRIGGER) ?
DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER : 0;
+ if ((msg->flags & MIPI_DSI_MSG_LASTCOMMAND))
+ hw_flags |= DSI_CTRL_CMD_LAST_COMMAND;
+
if (flags & DSI_CTRL_CMD_DEFER_TRIGGER) {
if (flags & DSI_CTRL_CMD_FETCH_MEMORY) {
dsi_ctrl->hw.ops.kickoff_command(&dsi_ctrl->hw,
@@ -1034,7 +1038,8 @@
msecs_to_jiffies(DSI_CTRL_TX_TO_MS));
if (ret == 0) {
- u32 status = 0;
+ u32 status = dsi_ctrl->hw.ops.get_interrupt_status(
+ &dsi_ctrl->hw);
u32 mask = DSI_CMD_MODE_DMA_DONE;
if (status & mask) {
@@ -2487,6 +2492,10 @@
return -EINVAL;
}
+ /* Dont trigger the command if this is not the last ocmmand */
+ if (!(flags & DSI_CTRL_CMD_LAST_COMMAND))
+ return rc;
+
mutex_lock(&dsi_ctrl->ctrl_lock);
if (!(flags & DSI_CTRL_CMD_BROADCAST_MASTER))
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
index 4781299..0e5c0bd 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
@@ -35,6 +35,8 @@
* reading data from memory.
* @DSI_CTRL_CMD_FETCH_MEMORY: Fetch command from memory through AXI bus
* and transfer it.
+ * @DSI_CTRL_CMD_LAST_COMMAND: Trigger the DMA cmd transfer if this is last
+ * command in the batch.
*/
#define DSI_CTRL_CMD_READ 0x1
#define DSI_CTRL_CMD_BROADCAST 0x2
@@ -42,6 +44,7 @@
#define DSI_CTRL_CMD_DEFER_TRIGGER 0x8
#define DSI_CTRL_CMD_FIFO_STORE 0x10
#define DSI_CTRL_CMD_FETCH_MEMORY 0x20
+#define DSI_CTRL_CMD_LAST_COMMAND 0x40
/**
* enum dsi_power_state - defines power states for dsi controller.
@@ -225,6 +228,7 @@
struct drm_gem_object *tx_cmd_buf;
u32 cmd_buffer_size;
u32 cmd_buffer_iova;
+ u32 cmd_len;
void *vaddr;
/* Debug Information */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
index 2f0d25f..01a2478 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
@@ -358,6 +358,7 @@
* @clk_rate_hz: DSI bit clock rate per lane in Hz.
* @dsc_enabled: DSC compression enabled.
* @dsc: DSC compression configuration.
+ * @roi_caps: Panel ROI capabilities.
*/
struct dsi_mode_info {
u32 h_active;
@@ -377,6 +378,7 @@
u64 clk_rate_hz;
bool dsc_enabled;
struct msm_display_dsc_info *dsc;
+ struct msm_roi_caps roi_caps;
};
/**
@@ -505,6 +507,7 @@
* @topology: Topology selected for the panel
* @dsc: DSC compression info
* @dsc_enabled: DSC compression enabled
+ * @roi_caps: Panel ROI capabilities
*/
struct dsi_display_mode_priv_info {
struct dsi_panel_cmd_set cmd_sets[DSI_CMD_SET_MAX];
@@ -520,6 +523,7 @@
struct msm_display_topology topology;
struct msm_display_dsc_info dsc;
bool dsc_enabled;
+ struct msm_roi_caps roi_caps;
};
/**
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index 722c531..2355444 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -1707,6 +1707,10 @@
flags = (DSI_CTRL_CMD_BROADCAST | DSI_CTRL_CMD_DEFER_TRIGGER |
DSI_CTRL_CMD_FETCH_MEMORY);
+ if ((msg->flags & MIPI_DSI_MSG_LASTCOMMAND)) {
+ flags |= DSI_CTRL_CMD_LAST_COMMAND;
+ m_flags |= DSI_CTRL_CMD_LAST_COMMAND;
+ }
/*
* 1. Setup commands in FIFO
* 2. Trigger commands
@@ -3618,9 +3622,6 @@
if (display->panel->esd_config.esd_enabled)
info->capabilities |= MSM_DISPLAY_ESD_ENABLED;
- memcpy(&info->roi_caps, &display->panel->roi_caps,
- sizeof(info->roi_caps));
-
error:
mutex_unlock(&display->display_lock);
return rc;
@@ -4151,13 +4152,20 @@
struct dsi_rect *out_roi)
{
const struct dsi_rect *bounds = &ctrl->ctrl->mode_bounds;
+ struct dsi_display_mode *cur_mode;
+ struct msm_roi_caps *roi_caps;
struct dsi_rect req_roi = { 0 };
int rc = 0;
- if (req_rois->num_rects > display->panel->roi_caps.num_roi) {
+ cur_mode = display->panel->cur_mode;
+ if (!cur_mode)
+ return 0;
+
+ roi_caps = &cur_mode->priv_info->roi_caps;
+ if (req_rois->num_rects > roi_caps->num_roi) {
pr_err("request for %d rois greater than max %d\n",
req_rois->num_rects,
- display->panel->roi_caps.num_roi);
+ roi_caps->num_roi);
rc = -EINVAL;
goto exit;
}
@@ -4194,13 +4202,20 @@
static int dsi_display_set_roi(struct dsi_display *display,
struct msm_roi_list *rois)
{
+ struct dsi_display_mode *cur_mode;
+ struct msm_roi_caps *roi_caps;
int rc = 0;
int i;
if (!display || !rois || !display->panel)
return -EINVAL;
- if (!display->panel->roi_caps.enabled)
+ cur_mode = display->panel->cur_mode;
+ if (!cur_mode)
+ return 0;
+
+ roi_caps = &cur_mode->priv_info->roi_caps;
+ if (!roi_caps->enabled)
return 0;
for (i = 0; i < display->ctrl_count; i++) {
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
index 280c754..d8c55d4 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
@@ -330,6 +330,11 @@
sizeof(dsi_mode.priv_info->dsc));
}
+ if (dsi_mode.priv_info->roi_caps.enabled) {
+ memcpy(&mode_info->roi_caps, &dsi_mode.priv_info->roi_caps,
+ sizeof(dsi_mode.priv_info->roi_caps));
+ }
+
return 0;
}
@@ -344,8 +349,7 @@
};
int dsi_conn_post_init(struct drm_connector *connector,
- void *info,
- void *display)
+ void *info, void *display, struct msm_mode_info *mode_info)
{
struct dsi_display *dsi_display = display;
struct dsi_panel *panel;
@@ -444,23 +448,23 @@
break;
}
- if (panel->roi_caps.enabled) {
+ if (mode_info && mode_info->roi_caps.enabled) {
sde_kms_info_add_keyint(info, "partial_update_num_roi",
- panel->roi_caps.num_roi);
+ mode_info->roi_caps.num_roi);
sde_kms_info_add_keyint(info, "partial_update_xstart",
- panel->roi_caps.align.xstart_pix_align);
+ mode_info->roi_caps.align.xstart_pix_align);
sde_kms_info_add_keyint(info, "partial_update_walign",
- panel->roi_caps.align.width_pix_align);
+ mode_info->roi_caps.align.width_pix_align);
sde_kms_info_add_keyint(info, "partial_update_wmin",
- panel->roi_caps.align.min_width);
+ mode_info->roi_caps.align.min_width);
sde_kms_info_add_keyint(info, "partial_update_ystart",
- panel->roi_caps.align.ystart_pix_align);
+ mode_info->roi_caps.align.ystart_pix_align);
sde_kms_info_add_keyint(info, "partial_update_halign",
- panel->roi_caps.align.height_pix_align);
+ mode_info->roi_caps.align.height_pix_align);
sde_kms_info_add_keyint(info, "partial_update_hmin",
- panel->roi_caps.align.min_height);
+ mode_info->roi_caps.align.min_height);
sde_kms_info_add_keyint(info, "partial_update_roimerge",
- panel->roi_caps.merge_rois);
+ mode_info->roi_caps.merge_rois);
}
end:
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h
index 828e65d..5b09034 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h
@@ -37,11 +37,13 @@
* @connector: Pointer to drm connector structure
* @info: Pointer to sde connector info structure
* @display: Pointer to private display handle
+ * @mode_info: Pointer to mode info structure
* Returns: Zero on success
*/
int dsi_conn_post_init(struct drm_connector *connector,
void *info,
- void *display);
+ void *display,
+ struct msm_mode_info *mode_info);
/**
* dsi_conn_detect - callback to determine if connector is connected
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
index 4688741..b8bfc87 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
@@ -497,6 +497,9 @@
if (state == DSI_CMD_SET_STATE_LP)
cmds->msg.flags |= MIPI_DSI_MSG_USE_LPM;
+ if (cmds->last_command)
+ cmds->msg.flags |= MIPI_DSI_MSG_LASTCOMMAND;
+
len = ops->transfer(panel->host, &cmds->msg);
if (len < 0) {
rc = len;
@@ -743,6 +746,10 @@
if (rc)
pr_err("qcom,mdss-dsi-h-sync-skew is not defined, rc=%d\n", rc);
+ pr_debug("panel horz active:%d front_portch:%d back_porch:%d sync_skew:%d\n",
+ mode->h_active, mode->h_front_porch, mode->h_back_porch,
+ mode->h_sync_width);
+
rc = of_property_read_u32(of_node, "qcom,mdss-dsi-panel-height",
&mode->v_active);
if (rc) {
@@ -774,6 +781,9 @@
rc);
goto error;
}
+ pr_debug("panel vert active:%d front_portch:%d back_porch:%d pulse_width:%d\n",
+ mode->v_active, mode->v_front_porch, mode->v_back_porch,
+ mode->v_sync_width);
error:
return rc;
@@ -2173,7 +2183,9 @@
intf_width = mode->timing.h_active;
if (intf_width % priv_info->dsc.slice_width) {
- pr_err("invalid slice width for the panel\n");
+ pr_err("invalid slice width for the intf width:%d slice width:%d\n",
+ intf_width, priv_info->dsc.slice_width);
+ rc = -EINVAL;
goto error;
}
@@ -2392,21 +2404,37 @@
return rc;
}
-static int dsi_panel_parse_partial_update_caps(struct dsi_panel *panel,
- struct device_node *of_node)
+static int dsi_panel_parse_partial_update_caps(struct dsi_display_mode *mode,
+ struct device_node *of_node)
{
- struct msm_roi_caps *roi_caps = &panel->roi_caps;
+ struct msm_roi_caps *roi_caps = NULL;
const char *data;
int rc = 0;
+ if (!mode || !mode->priv_info) {
+ pr_err("invalid arguments\n");
+ return -EINVAL;
+ }
+
+ roi_caps = &mode->priv_info->roi_caps;
+
memset(roi_caps, 0, sizeof(*roi_caps));
data = of_get_property(of_node, "qcom,partial-update-enabled", NULL);
if (data) {
if (!strcmp(data, "dual_roi"))
roi_caps->num_roi = 2;
- else
+ else if (!strcmp(data, "single_roi"))
roi_caps->num_roi = 1;
+ else {
+ pr_info(
+ "invalid value for qcom,partial-update-enabled: %s\n",
+ data);
+ return 0;
+ }
+ } else {
+ pr_info("partial update disabled as the property is not set\n");
+ return 0;
}
roi_caps->merge_rois = of_property_read_bool(of_node,
@@ -2419,7 +2447,7 @@
if (roi_caps->enabled)
rc = dsi_panel_parse_roi_alignment(of_node,
- &panel->roi_caps.align);
+ &roi_caps->align);
if (rc)
memset(roi_caps, 0, sizeof(*roi_caps));
@@ -2736,10 +2764,6 @@
if (rc)
pr_err("failed to parse hdr config, rc=%d\n", rc);
- rc = dsi_panel_parse_partial_update_caps(panel, of_node);
- if (rc)
- pr_debug("failed to partial update caps, rc=%d\n", rc);
-
rc = dsi_panel_get_mode_count(panel, of_node);
if (rc) {
pr_err("failed to get mode count, rc=%d\n", rc);
@@ -3048,6 +3072,10 @@
"failed to parse panel phy timings, rc=%d\n", rc);
goto parse_fail;
}
+
+ rc = dsi_panel_parse_partial_update_caps(mode, child_np);
+ if (rc)
+ pr_err("failed to partial update caps, rc=%d\n", rc);
}
goto done;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
index f63fd27..0e81538 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
@@ -156,8 +156,6 @@
enum dsi_op_mode panel_mode;
struct dsi_dfps_capabilities dfps_caps;
- struct msm_roi_caps roi_caps;
-
struct dsi_panel_phy_props phy_props;
struct dsi_display_mode *cur_mode;
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
index 28f2e7c..95bdc36 100644
--- a/drivers/gpu/drm/msm/msm_atomic.c
+++ b/drivers/gpu/drm/msm/msm_atomic.c
@@ -16,6 +16,9 @@
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/msm_drm_notify.h>
+#include <linux/notifier.h>
+
#include "msm_drv.h"
#include "msm_kms.h"
#include "msm_gem.h"
@@ -30,6 +33,47 @@
struct kthread_work commit_work;
};
+static BLOCKING_NOTIFIER_HEAD(msm_drm_notifier_list);
+
+/**
+ * msm_drm_register_client - register a client notifier
+ * @nb: notifier block to callback on events
+ *
+ * This function registers a notifier callback function
+ * to msm_drm_notifier_list, which would be called when
+ * received unblank/power down event.
+ */
+int msm_drm_register_client(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&msm_drm_notifier_list,
+ nb);
+}
+
+/**
+ * msm_drm_unregister_client - unregister a client notifier
+ * @nb: notifier block to callback on events
+ *
+ * This function unregisters the callback function from
+ * msm_drm_notifier_list.
+ */
+int msm_drm_unregister_client(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&msm_drm_notifier_list,
+ nb);
+}
+
+/**
+ * msm_drm_notifier_call_chain - notify clients of drm_events
+ * @val: event MSM_DRM_EARLY_EVENT_BLANK or MSM_DRM_EVENT_BLANK
+ * @v: notifier data, inculde display id and display blank
+ * event(unblank or power down).
+ */
+static int msm_drm_notifier_call_chain(unsigned long val, void *v)
+{
+ return blocking_notifier_call_chain(&msm_drm_notifier_list, val,
+ v);
+}
+
/* block until specified crtcs are no longer pending update, and
* atomically mark them as pending update
*/
@@ -97,7 +141,8 @@
struct drm_connector_state *old_conn_state;
struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state;
- int i;
+ struct msm_drm_notifier notifier_data;
+ int i, blank;
SDE_ATRACE_BEGIN("msm_disable");
for_each_connector_in_state(old_state, connector, old_conn_state, i) {
@@ -144,6 +189,11 @@
DRM_DEBUG_ATOMIC("disabling [ENCODER:%d:%s]\n",
encoder->base.id, encoder->name);
+ blank = MSM_DRM_BLANK_POWERDOWN;
+ notifier_data.data = ␣
+ notifier_data.id = crtc_idx;
+ msm_drm_notifier_call_chain(MSM_DRM_EARLY_EVENT_BLANK,
+ ¬ifier_data);
/*
* Each encoder has at most one connector (since we always steal
* it away), so we won't call disable hooks twice.
@@ -159,6 +209,8 @@
funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
drm_bridge_post_disable(encoder->bridge);
+ msm_drm_notifier_call_chain(MSM_DRM_EVENT_BLANK,
+ ¬ifier_data);
}
for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
@@ -296,10 +348,11 @@
struct drm_crtc_state *old_crtc_state;
struct drm_connector *connector;
struct drm_connector_state *old_conn_state;
+ struct msm_drm_notifier notifier_data;
struct msm_drm_private *priv = dev->dev_private;
struct msm_kms *kms = priv->kms;
int bridge_enable_count = 0;
- int i;
+ int i, blank;
SDE_ATRACE_BEGIN("msm_enable");
for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
@@ -350,6 +403,12 @@
DRM_DEBUG_ATOMIC("enabling [ENCODER:%d:%s]\n",
encoder->base.id, encoder->name);
+ blank = MSM_DRM_BLANK_UNBLANK;
+ notifier_data.data = ␣
+ notifier_data.id =
+ connector->state->crtc->index;
+ msm_drm_notifier_call_chain(MSM_DRM_EARLY_EVENT_BLANK,
+ ¬ifier_data);
/*
* Each encoder has at most one connector (since we always steal
* it away), so we won't call enable hooks twice.
@@ -391,6 +450,8 @@
encoder->base.id, encoder->name);
drm_bridge_enable(encoder->bridge);
+ msm_drm_notifier_call_chain(MSM_DRM_EVENT_BLANK,
+ ¬ifier_data);
}
SDE_ATRACE_END("msm_enable");
}
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index c0f796c..70cd5c6 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -215,12 +215,16 @@
struct msm_kms *kms = priv->kms;
struct vblank_event *vbl_ev, *tmp;
unsigned long flags;
+ LIST_HEAD(tmp_head);
spin_lock_irqsave(&vbl_ctrl->lock, flags);
list_for_each_entry_safe(vbl_ev, tmp, &vbl_ctrl->event_list, node) {
list_del(&vbl_ev->node);
- spin_unlock_irqrestore(&vbl_ctrl->lock, flags);
+ list_add_tail(&vbl_ev->node, &tmp_head);
+ }
+ spin_unlock_irqrestore(&vbl_ctrl->lock, flags);
+ list_for_each_entry_safe(vbl_ev, tmp, &tmp_head, node) {
if (vbl_ev->enable)
kms->funcs->enable_vblank(kms,
priv->crtcs[vbl_ev->crtc_id]);
@@ -229,11 +233,7 @@
priv->crtcs[vbl_ev->crtc_id]);
kfree(vbl_ev);
-
- spin_lock_irqsave(&vbl_ctrl->lock, flags);
}
-
- spin_unlock_irqrestore(&vbl_ctrl->lock, flags);
}
static int vblank_ctrl_queue_work(struct msm_drm_private *priv,
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 8554574..9a0204b 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -409,6 +409,7 @@
* @clk_rate: DSI bit clock per lane in HZ.
* @topology: supported topology for the mode
* @comp_info: compression info supported
+ * @roi_caps: panel roi capabilities
*/
struct msm_mode_info {
uint32_t frame_rate;
@@ -419,6 +420,7 @@
uint64_t clk_rate;
struct msm_display_topology topology;
struct msm_compression_info comp_info;
+ struct msm_roi_caps roi_caps;
};
/**
diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c
index f5cdf64..e8bf244 100644
--- a/drivers/gpu/drm/msm/msm_fb.c
+++ b/drivers/gpu/drm/msm/msm_fb.c
@@ -42,15 +42,31 @@
struct drm_file *file_priv,
unsigned int *handle)
{
- struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
+ struct msm_framebuffer *msm_fb;
+
+ if (!fb) {
+ DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+ return -EINVAL;
+ }
+
+ msm_fb = to_msm_framebuffer(fb);
+
return drm_gem_handle_create(file_priv,
msm_fb->planes[0], handle);
}
static void msm_framebuffer_destroy(struct drm_framebuffer *fb)
{
- struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
- int i, n = drm_format_num_planes(fb->pixel_format);
+ struct msm_framebuffer *msm_fb;
+ int i, n;
+
+ if (!fb) {
+ DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+ return;
+ }
+
+ msm_fb = to_msm_framebuffer(fb);
+ n = drm_format_num_planes(fb->pixel_format);
DBG("destroy: FB ID: %d (%p)", fb->base.id, fb);
@@ -73,9 +89,16 @@
#ifdef CONFIG_DEBUG_FS
void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
{
- struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
- int i, n = drm_format_num_planes(fb->pixel_format);
+ struct msm_framebuffer *msm_fb;
+ int i, n;
+ if (!fb) {
+ DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+ return;
+ }
+
+ msm_fb = to_msm_framebuffer(fb);
+ n = drm_format_num_planes(fb->pixel_format);
seq_printf(m, "fb: %dx%d@%4.4s (%2d, ID:%d)\n",
fb->width, fb->height, (char *)&fb->pixel_format,
drm_framebuffer_read_refcount(fb), fb->base.id);
@@ -90,8 +113,14 @@
void msm_framebuffer_set_kmap(struct drm_framebuffer *fb, bool enable)
{
- struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
+ struct msm_framebuffer *msm_fb;
+ if (!fb) {
+ DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+ return;
+ }
+
+ msm_fb = to_msm_framebuffer(fb);
if (enable)
msm_fb->flags |= MSM_FRAMEBUFFER_FLAG_KMAP;
else
@@ -100,10 +129,17 @@
static int msm_framebuffer_kmap(struct drm_framebuffer *fb)
{
- struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
- int i, n = drm_format_num_planes(fb->pixel_format);
+ struct msm_framebuffer *msm_fb;
+ int i, n;
struct drm_gem_object *bo;
+ if (!fb) {
+ DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+ return -EINVAL;
+ }
+
+ msm_fb = to_msm_framebuffer(fb);
+ n = drm_format_num_planes(fb->pixel_format);
if (atomic_inc_return(&msm_fb->kmap_count) > 1)
return 0;
@@ -124,10 +160,17 @@
static void msm_framebuffer_kunmap(struct drm_framebuffer *fb)
{
- struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
- int i, n = drm_format_num_planes(fb->pixel_format);
+ struct msm_framebuffer *msm_fb;
+ int i, n;
struct drm_gem_object *bo;
+ if (!fb) {
+ DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+ return;
+ }
+
+ msm_fb = to_msm_framebuffer(fb);
+ n = drm_format_num_planes(fb->pixel_format);
if (atomic_dec_return(&msm_fb->kmap_count) > 0)
return;
@@ -151,10 +194,17 @@
int msm_framebuffer_prepare(struct drm_framebuffer *fb,
struct msm_gem_address_space *aspace)
{
- struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
- int ret, i, n = drm_format_num_planes(fb->pixel_format);
+ struct msm_framebuffer *msm_fb;
+ int ret, i, n;
uint32_t iova;
+ if (!fb) {
+ DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+ return -EINVAL;
+ }
+
+ msm_fb = to_msm_framebuffer(fb);
+ n = drm_format_num_planes(fb->pixel_format);
for (i = 0; i < n; i++) {
ret = msm_gem_get_iova(msm_fb->planes[i], aspace, &iova);
DBG("FB[%u]: iova[%d]: %08x (%d)", fb->base.id, i, iova, ret);
@@ -171,8 +221,16 @@
void msm_framebuffer_cleanup(struct drm_framebuffer *fb,
struct msm_gem_address_space *aspace)
{
- struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
- int i, n = drm_format_num_planes(fb->pixel_format);
+ struct msm_framebuffer *msm_fb;
+ int i, n;
+
+ if (fb == NULL) {
+ DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+ return;
+ }
+
+ msm_fb = to_msm_framebuffer(fb);
+ n = drm_format_num_planes(fb->pixel_format);
if (msm_fb->flags & MSM_FRAMEBUFFER_FLAG_KMAP)
msm_framebuffer_kunmap(fb);
@@ -184,7 +242,14 @@
uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb,
struct msm_gem_address_space *aspace, int plane)
{
- struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
+ struct msm_framebuffer *msm_fb;
+
+ if (!fb) {
+ DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+ return -EINVAL;
+ }
+
+ msm_fb = to_msm_framebuffer(fb);
if (!msm_fb->planes[plane])
return 0;
return msm_gem_iova(msm_fb->planes[plane], aspace) + fb->offsets[plane];
@@ -193,9 +258,15 @@
uint32_t msm_framebuffer_phys(struct drm_framebuffer *fb,
int plane)
{
- struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
+ struct msm_framebuffer *msm_fb;
dma_addr_t phys_addr;
+ if (!fb) {
+ DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+ return -EINVAL;
+ }
+
+ msm_fb = to_msm_framebuffer(fb);
if (!msm_fb->planes[plane])
return 0;
@@ -208,7 +279,14 @@
struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane)
{
- struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
+ struct msm_framebuffer *msm_fb;
+
+ if (!fb) {
+ DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+ return ERR_PTR(-EINVAL);
+ }
+
+ msm_fb = to_msm_framebuffer(fb);
return msm_fb->planes[plane];
}
diff --git a/drivers/gpu/drm/msm/msm_smmu.c b/drivers/gpu/drm/msm/msm_smmu.c
index 92d1865..7c879651 100644
--- a/drivers/gpu/drm/msm/msm_smmu.c
+++ b/drivers/gpu/drm/msm/msm_smmu.c
@@ -33,6 +33,10 @@
#define SZ_4G (((size_t) SZ_1G) * 4)
#endif
+#ifndef SZ_2G
+#define SZ_2G (((size_t) SZ_1G) * 2)
+#endif
+
struct msm_smmu_client {
struct device *dev;
struct dma_iommu_mapping *mmu_mapping;
@@ -300,26 +304,26 @@
static struct msm_smmu_domain msm_smmu_domains[MSM_SMMU_DOMAIN_MAX] = {
[MSM_SMMU_DOMAIN_UNSECURE] = {
.label = "mdp_ns",
- .va_start = SZ_128K,
- .va_size = SZ_4G - SZ_128K,
+ .va_start = SZ_2G,
+ .va_size = SZ_4G - SZ_2G,
.secure = false,
},
[MSM_SMMU_DOMAIN_SECURE] = {
.label = "mdp_s",
- .va_start = SZ_128K,
- .va_size = SZ_4G - SZ_128K,
+ .va_start = SZ_2G,
+ .va_size = SZ_4G - SZ_2G,
.secure = true,
},
[MSM_SMMU_DOMAIN_NRT_UNSECURE] = {
.label = "rot_ns",
- .va_start = SZ_128K,
- .va_size = SZ_4G - SZ_128K,
+ .va_start = SZ_2G,
+ .va_size = SZ_4G - SZ_2G,
.secure = false,
},
[MSM_SMMU_DOMAIN_NRT_SECURE] = {
.label = "rot_s",
- .va_start = SZ_128K,
- .va_size = SZ_4G - SZ_128K,
+ .va_start = SZ_2G,
+ .va_size = SZ_4G - SZ_2G,
.secure = true,
},
};
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index 88dd60da..74f9291 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -16,6 +16,7 @@
#include "sde_kms.h"
#include "sde_connector.h"
+#include "sde_encoder.h"
#include <linux/backlight.h>
#include "dsi_drm.h"
#include "dsi_display.h"
@@ -345,6 +346,23 @@
return 0;
}
+int sde_connector_get_mode_info(struct drm_connector_state *conn_state,
+ struct msm_mode_info *mode_info)
+{
+ struct sde_connector_state *sde_conn_state = NULL;
+
+ if (!conn_state || !mode_info) {
+ SDE_ERROR("Invalid arguments\n");
+ return -EINVAL;
+ }
+
+ sde_conn_state = to_sde_connector_state(conn_state);
+ memcpy(mode_info, &sde_conn_state->mode_info,
+ sizeof(sde_conn_state->mode_info));
+
+ return 0;
+}
+
int sde_connector_get_info(struct drm_connector *connector,
struct msm_display_info *info)
{
@@ -441,6 +459,54 @@
return rc;
}
+static int _sde_connector_update_bl_scale(struct sde_connector *c_conn, int idx)
+{
+ struct drm_connector conn;
+ struct dsi_display *dsi_display;
+ struct dsi_backlight_config *bl_config;
+ uint64_t value;
+ int rc = 0;
+
+ if (!c_conn) {
+ SDE_ERROR("Invalid params sde_connector null\n");
+ return -EINVAL;
+ }
+
+ conn = c_conn->base;
+ dsi_display = c_conn->display;
+ if (!dsi_display || !dsi_display->panel) {
+ SDE_ERROR("Invalid params(s) dsi_display %pK, panel %pK\n",
+ dsi_display,
+ ((dsi_display) ? dsi_display->panel : NULL));
+ return -EINVAL;
+ }
+
+ bl_config = &dsi_display->panel->bl_config;
+ value = sde_connector_get_property(conn.state, idx);
+
+ if (idx == CONNECTOR_PROP_BL_SCALE) {
+ if (value > MAX_BL_SCALE_LEVEL)
+ bl_config->bl_scale = MAX_BL_SCALE_LEVEL;
+ else
+ bl_config->bl_scale = (u32)value;
+ } else if (idx == CONNECTOR_PROP_AD_BL_SCALE) {
+ if (value > MAX_AD_BL_SCALE_LEVEL)
+ bl_config->bl_scale_ad = MAX_AD_BL_SCALE_LEVEL;
+ else
+ bl_config->bl_scale_ad = (u32)value;
+ } else {
+ SDE_DEBUG("invalid idx %d\n", idx);
+ return 0;
+ }
+
+ SDE_DEBUG("bl_scale = %u, bl_scale_ad = %u, bl_level = %u\n",
+ bl_config->bl_scale, bl_config->bl_scale_ad,
+ bl_config->bl_level);
+ rc = c_conn->ops.set_backlight(dsi_display, bl_config->bl_level);
+
+ return rc;
+}
+
int sde_connector_pre_kickoff(struct drm_connector *connector)
{
struct sde_connector *c_conn;
@@ -471,6 +537,10 @@
_sde_connector_update_power_locked(c_conn);
mutex_unlock(&c_conn->lock);
break;
+ case CONNECTOR_PROP_BL_SCALE:
+ case CONNECTOR_PROP_AD_BL_SCALE:
+ _sde_connector_update_bl_scale(c_conn, idx);
+ break;
default:
/* nothing to do for most properties */
break;
@@ -671,44 +741,80 @@
return &c_state->base;
}
-static int _sde_connector_roi_v1_check_roi(
- struct sde_connector *c_conn,
- struct drm_clip_rect *roi_conn,
- const struct msm_roi_caps *caps)
+int sde_connector_roi_v1_check_roi(struct drm_connector_state *conn_state)
{
- const struct msm_roi_alignment *align = &caps->align;
- int w = roi_conn->x2 - roi_conn->x1;
- int h = roi_conn->y2 - roi_conn->y1;
+ const struct msm_roi_alignment *align = NULL;
+ struct sde_connector *c_conn = NULL;
+ struct msm_mode_info mode_info;
+ struct sde_connector_state *c_state;
+ int i, w, h;
- if (w <= 0 || h <= 0) {
- SDE_ERROR_CONN(c_conn, "invalid conn roi w %d h %d\n", w, h);
+ if (!conn_state)
return -EINVAL;
- }
- if (w < align->min_width || w % align->width_pix_align) {
- SDE_ERROR_CONN(c_conn,
- "invalid conn roi width %d min %d align %d\n",
- w, align->min_width, align->width_pix_align);
- return -EINVAL;
- }
+ memset(&mode_info, 0, sizeof(mode_info));
- if (h < align->min_height || h % align->height_pix_align) {
- SDE_ERROR_CONN(c_conn,
- "invalid conn roi height %d min %d align %d\n",
- h, align->min_height, align->height_pix_align);
- return -EINVAL;
- }
+ c_state = to_sde_connector_state(conn_state);
+ c_conn = to_sde_connector(conn_state->connector);
- if (roi_conn->x1 % align->xstart_pix_align) {
- SDE_ERROR_CONN(c_conn, "invalid conn roi x1 %d align %d\n",
- roi_conn->x1, align->xstart_pix_align);
- return -EINVAL;
- }
+ memcpy(&mode_info, &c_state->mode_info, sizeof(c_state->mode_info));
- if (roi_conn->y1 % align->ystart_pix_align) {
- SDE_ERROR_CONN(c_conn, "invalid conn roi y1 %d align %d\n",
- roi_conn->y1, align->ystart_pix_align);
- return -EINVAL;
+ if (!mode_info.roi_caps.enabled)
+ return 0;
+
+ if (c_state->rois.num_rects > mode_info.roi_caps.num_roi) {
+ SDE_ERROR_CONN(c_conn, "too many rects specified: %d > %d\n",
+ c_state->rois.num_rects,
+ mode_info.roi_caps.num_roi);
+ return -E2BIG;
+ };
+
+ align = &mode_info.roi_caps.align;
+ for (i = 0; i < c_state->rois.num_rects; ++i) {
+ struct drm_clip_rect *roi_conn;
+
+ roi_conn = &c_state->rois.roi[i];
+ w = roi_conn->x2 - roi_conn->x1;
+ h = roi_conn->y2 - roi_conn->y1;
+
+ SDE_EVT32_VERBOSE(roi_conn->x1, roi_conn->y1,
+ roi_conn->x2, roi_conn->y2);
+
+ if (w <= 0 || h <= 0) {
+ SDE_ERROR_CONN(c_conn, "invalid conn roi w %d h %d\n",
+ w, h);
+ return -EINVAL;
+ }
+
+ if (w < align->min_width || w % align->width_pix_align) {
+ SDE_ERROR_CONN(c_conn,
+ "invalid conn roi width %d min %d align %d\n",
+ w, align->min_width,
+ align->width_pix_align);
+ return -EINVAL;
+ }
+
+ if (h < align->min_height || h % align->height_pix_align) {
+ SDE_ERROR_CONN(c_conn,
+ "invalid conn roi height %d min %d align %d\n",
+ h, align->min_height,
+ align->height_pix_align);
+ return -EINVAL;
+ }
+
+ if (roi_conn->x1 % align->xstart_pix_align) {
+ SDE_ERROR_CONN(c_conn,
+ "invalid conn roi x1 %d align %d\n",
+ roi_conn->x1, align->xstart_pix_align);
+ return -EINVAL;
+ }
+
+ if (roi_conn->y1 % align->ystart_pix_align) {
+ SDE_ERROR_CONN(c_conn,
+ "invalid conn roi y1 %d align %d\n",
+ roi_conn->y1, align->ystart_pix_align);
+ return -EINVAL;
+ }
}
return 0;
@@ -720,27 +826,13 @@
void *usr_ptr)
{
struct sde_drm_roi_v1 roi_v1;
- struct msm_display_info display_info;
- struct msm_roi_caps *caps;
- int i, rc;
+ int i;
if (!c_conn || !c_state) {
SDE_ERROR("invalid args\n");
return -EINVAL;
}
- rc = sde_connector_get_info(&c_conn->base, &display_info);
- if (rc) {
- SDE_ERROR_CONN(c_conn, "display get info error: %d\n", rc);
- return rc;
- }
-
- caps = &display_info.roi_caps;
- if (!caps->enabled) {
- SDE_ERROR_CONN(c_conn, "display roi capability is disabled\n");
- return -ENOTSUPP;
- }
-
memset(&c_state->rois, 0, sizeof(c_state->rois));
if (!usr_ptr) {
@@ -760,22 +852,14 @@
return 0;
}
- if (roi_v1.num_rects > SDE_MAX_ROI_V1 ||
- roi_v1.num_rects > caps->num_roi) {
- SDE_ERROR_CONN(c_conn, "too many rects specified: %d\n",
+ if (roi_v1.num_rects > SDE_MAX_ROI_V1) {
+ SDE_ERROR_CONN(c_conn, "num roi rects more than supported: %d",
roi_v1.num_rects);
return -EINVAL;
}
c_state->rois.num_rects = roi_v1.num_rects;
for (i = 0; i < roi_v1.num_rects; ++i) {
- int rc;
-
- rc = _sde_connector_roi_v1_check_roi(c_conn, &roi_v1.roi[i],
- caps);
- if (rc)
- return rc;
-
c_state->rois.roi[i] = roi_v1.roi[i];
SDE_DEBUG_CONN(c_conn, "roi%d: roi (%d,%d) (%d,%d)\n", i,
c_state->rois.roi[i].x1,
@@ -787,42 +871,6 @@
return 0;
}
-static int _sde_connector_update_bl_scale(struct sde_connector *c_conn,
- int idx,
- uint64_t value)
-{
- struct dsi_display *dsi_display = c_conn->display;
- struct dsi_backlight_config *bl_config;
- int rc = 0;
-
- if (!dsi_display || !dsi_display->panel) {
- pr_err("Invalid params(s) dsi_display %pK, panel %pK\n",
- dsi_display,
- ((dsi_display) ? dsi_display->panel : NULL));
- return -EINVAL;
- }
-
- bl_config = &dsi_display->panel->bl_config;
- if (idx == CONNECTOR_PROP_BL_SCALE) {
- bl_config->bl_scale = value;
- if (value > MAX_BL_SCALE_LEVEL)
- bl_config->bl_scale = MAX_BL_SCALE_LEVEL;
- SDE_DEBUG("set to panel: bl_scale = %u, bl_level = %u\n",
- bl_config->bl_scale, bl_config->bl_level);
- rc = c_conn->ops.set_backlight(dsi_display,
- bl_config->bl_level);
- } else if (idx == CONNECTOR_PROP_AD_BL_SCALE) {
- bl_config->bl_scale_ad = value;
- if (value > MAX_AD_BL_SCALE_LEVEL)
- bl_config->bl_scale_ad = MAX_AD_BL_SCALE_LEVEL;
- SDE_DEBUG("set to panel: bl_scale_ad = %u, bl_level = %u\n",
- bl_config->bl_scale_ad, bl_config->bl_level);
- rc = c_conn->ops.set_backlight(dsi_display,
- bl_config->bl_level);
- }
- return rc;
-}
-
static int sde_connector_atomic_set_property(struct drm_connector *connector,
struct drm_connector_state *state,
struct drm_property *property,
@@ -869,10 +917,6 @@
c_conn->fb_kmap);
}
break;
- case CONNECTOR_PROP_BL_SCALE:
- case CONNECTOR_PROP_AD_BL_SCALE:
- rc = _sde_connector_update_bl_scale(c_conn, idx, val);
- break;
default:
break;
}
@@ -1316,6 +1360,64 @@
.best_encoder = sde_connector_best_encoder,
};
+int sde_connector_set_info(struct drm_connector *conn,
+ struct drm_connector_state *state)
+{
+ struct sde_kms_info *info;
+ struct sde_connector *c_conn = NULL;
+ struct sde_connector_state *sde_conn_state = NULL;
+ struct msm_mode_info mode_info;
+ int rc = 0;
+
+ c_conn = to_sde_connector(conn);
+ if (!c_conn) {
+ SDE_ERROR("invalid argument\n");
+ return -EINVAL;
+ }
+
+ if (!c_conn->ops.post_init) {
+ SDE_DEBUG_CONN(c_conn, "post_init not defined\n");
+ return 0;
+ }
+
+ memset(&mode_info, 0, sizeof(mode_info));
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ if (state) {
+ sde_conn_state = to_sde_connector_state(state);
+ memcpy(&mode_info, &sde_conn_state->mode_info,
+ sizeof(sde_conn_state->mode_info));
+ } else {
+ /**
+ * connector state is assigned only on first atomic_commit.
+ * But this function is allowed to be invoked during
+ * probe/init sequence. So not throwing an error.
+ */
+ SDE_DEBUG_CONN(c_conn, "invalid connector state\n");
+ }
+
+ sde_kms_info_reset(info);
+ rc = c_conn->ops.post_init(&c_conn->base, info,
+ c_conn->display, &mode_info);
+ if (rc) {
+ SDE_ERROR_CONN(c_conn, "post-init failed, %d\n", rc);
+ goto exit;
+ }
+
+ msm_property_set_blob(&c_conn->property_info,
+ &c_conn->blob_caps,
+ SDE_KMS_INFO_DATA(info),
+ SDE_KMS_INFO_DATALEN(info),
+ CONNECTOR_PROP_SDE_INFO);
+exit:
+ kfree(info);
+
+ return rc;
+}
+
struct drm_connector *sde_connector_init(struct drm_device *dev,
struct drm_encoder *encoder,
struct drm_panel *panel,
@@ -1326,7 +1428,6 @@
{
struct msm_drm_private *priv;
struct sde_kms *sde_kms;
- struct sde_kms_info *info;
struct sde_connector *c_conn = NULL;
struct dsi_display *dsi_display;
struct msm_display_info display_info;
@@ -1423,33 +1524,16 @@
CONNECTOR_PROP_COUNT, CONNECTOR_PROP_BLOBCOUNT,
sizeof(struct sde_connector_state));
- if (c_conn->ops.post_init) {
- info = kmalloc(sizeof(*info), GFP_KERNEL);
- if (!info) {
- SDE_ERROR("failed to allocate info buffer\n");
- rc = -ENOMEM;
- goto error_cleanup_fence;
- }
+ msm_property_install_blob(&c_conn->property_info,
+ "capabilities",
+ DRM_MODE_PROP_IMMUTABLE,
+ CONNECTOR_PROP_SDE_INFO);
- sde_kms_info_reset(info);
- rc = c_conn->ops.post_init(&c_conn->base, info, display);
- if (rc) {
- SDE_ERROR("post-init failed, %d\n", rc);
- kfree(info);
- goto error_cleanup_fence;
- }
-
- msm_property_install_blob(&c_conn->property_info,
- "capabilities",
- DRM_MODE_PROP_IMMUTABLE,
- CONNECTOR_PROP_SDE_INFO);
-
- msm_property_set_blob(&c_conn->property_info,
- &c_conn->blob_caps,
- SDE_KMS_INFO_DATA(info),
- SDE_KMS_INFO_DATALEN(info),
- CONNECTOR_PROP_SDE_INFO);
- kfree(info);
+ rc = sde_connector_set_info(&c_conn->base, c_conn->base.state);
+ if (rc) {
+ SDE_ERROR_CONN(c_conn,
+ "failed to setup connector info, rc = %d\n", rc);
+ goto error_cleanup_fence;
}
if (connector_type == DRM_MODE_CONNECTOR_DSI) {
@@ -1469,12 +1553,10 @@
}
}
- rc = sde_connector_get_info(&c_conn->base, &display_info);
- if (!rc && display_info.roi_caps.enabled) {
- msm_property_install_volatile_range(
+ msm_property_install_volatile_range(
&c_conn->property_info, "sde_drm_roi_v1", 0x0,
0, ~0, 0, CONNECTOR_PROP_ROI_V1);
- }
+
/* install PP_DITHER properties */
_sde_connector_install_dither_property(dev, sde_kms, c_conn);
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index 005d050..d0f146b 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -38,11 +38,13 @@
* @connector: Pointer to drm connector structure
* @info: Pointer to sde connector info structure
* @display: Pointer to private display handle
+ * @mode_info: Pointer to mode info structure
* Returns: Zero on success
*/
int (*post_init)(struct drm_connector *connector,
void *info,
- void *display);
+ void *display,
+ struct msm_mode_info *mode_info);
/**
* detect - determine if connector is connected
@@ -354,6 +356,7 @@
* @property_values: Local cache of current connector property values
* @rois: Regions of interest structure for mapping CRTC to Connector output
* @property_blobs: blob properties
+ * @mode_info: local copy of msm_mode_info struct
*/
struct sde_connector_state {
struct drm_connector_state base;
@@ -363,6 +366,7 @@
struct msm_roi_list rois;
struct drm_property_blob *property_blobs[CONNECTOR_PROP_BLOBCOUNT];
+ struct msm_mode_info mode_info;
};
/**
@@ -593,6 +597,22 @@
struct drm_connector_state *state, void **cfg, size_t *len);
/**
+ * sde_connector_set_info - set connector property value
+ * @conn: Pointer to drm_connector struct
+ * @state: Pointer to the drm_connector_state struct
+ * Returns: Zero on success
+ */
+int sde_connector_set_info(struct drm_connector *conn,
+ struct drm_connector_state *state);
+
+/**
+ * sde_connector_roi_v1_check_roi - validate connector ROI
+ * @conn_state: Pointer to drm_connector_state struct
+ * Returns: Zero on success
+ */
+int sde_connector_roi_v1_check_roi(struct drm_connector_state *conn_state);
+
+/**
* sde_connector_schedule_status_work - manage ESD thread
* conn: Pointer to drm_connector struct
* @en: flag to start/stop ESD thread
@@ -610,4 +630,13 @@
struct drm_connector *connector,
struct drm_connector_state *connector_state);
+/**
+ * sde_connector_get_mode_info - get information of the current mode in the
+ * given connector state.
+ * conn_state: Pointer to the DRM connector state object
+ * mode_info: Pointer to the mode info structure
+ */
+int sde_connector_get_mode_info(struct drm_connector_state *conn_state,
+ struct msm_mode_info *mode_info);
+
#endif /* _SDE_CONNECTOR_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.c b/drivers/gpu/drm/msm/sde/sde_core_irq.c
index a0846ff..b6c6234 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.c
@@ -55,9 +55,22 @@
spin_unlock_irqrestore(&sde_kms->irq_obj.cb_lock, irq_flags);
if (cb_tbl_error) {
- SDE_ERROR("irq has no registered callback, idx %d enables %d\n",
- irq_idx, enable_counts);
- SDE_EVT32_IRQ(irq_idx, enable_counts, SDE_EVTLOG_ERROR);
+ /*
+ * If enable count is zero and callback list is empty, then it's
+ * not a fatal issue. Log this case as debug. If the enable
+ * count is nonzero and callback list is empty, then its a real
+ * issue. Log this case as error to ensure we don't have silent
+ * IRQs running.
+ */
+ if (!enable_counts) {
+ SDE_DEBUG("irq has no callback, idx %d enables %d\n",
+ irq_idx, enable_counts);
+ SDE_EVT32_IRQ(irq_idx, enable_counts);
+ } else {
+ SDE_ERROR("irq has no callback, idx %d enables %d\n",
+ irq_idx, enable_counts);
+ SDE_EVT32_IRQ(irq_idx, enable_counts, SDE_EVTLOG_ERROR);
+ }
}
/*
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 6f76e20..7be9a4d 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -1178,14 +1178,56 @@
struct drm_crtc_state *state)
{
struct sde_crtc *sde_crtc;
- int lm_idx;
- int rc;
+ struct sde_crtc_state *sde_crtc_state;
+ struct msm_mode_info mode_info;
+ struct drm_connector *conn;
+ struct drm_connector_state *conn_state;
+ int rc, lm_idx, i;
if (!crtc || !state)
return -EINVAL;
+ memset(&mode_info, 0, sizeof(mode_info));
+
sde_crtc = to_sde_crtc(crtc);
+ if (hweight_long(state->connector_mask) != 1) {
+ SDE_ERROR("invalid connector count(%d) for crtc: %d\n",
+ (int)hweight_long(state->connector_mask),
+ crtc->base.id);
+ return -EINVAL;
+ }
+
+ for_each_connector_in_state(state->state, conn, conn_state, i) {
+ rc = sde_connector_get_mode_info(conn_state, &mode_info);
+ if (rc) {
+ SDE_ERROR("failed to get mode info\n");
+ return -EINVAL;
+ }
+ break;
+ }
+
+ if (!mode_info.roi_caps.enabled)
+ return 0;
+
+ sde_crtc_state = to_sde_crtc_state(state);
+ if (sde_crtc_state->user_roi_list.num_rects >
+ mode_info.roi_caps.num_roi) {
+ SDE_ERROR("roi count is more than supported limit, %d > %d\n",
+ sde_crtc_state->user_roi_list.num_rects,
+ mode_info.roi_caps.num_roi);
+ return -E2BIG;
+ }
+
+ /**
+ * TODO: Need to check against ROI alignment restrictions if partial
+ * update support is added for destination scalar configurations
+ */
+ if (sde_crtc_state->num_ds_enabled) {
+ SDE_ERROR("DS and PU concurrency is not supported\n");
+ return -EINVAL;
+ }
+
rc = _sde_crtc_set_crtc_roi(crtc, state);
if (rc)
return rc;
@@ -1432,7 +1474,7 @@
* _sde_crtc_blend_setup - configure crtc mixers
* @crtc: Pointer to drm crtc structure
*/
-static void _sde_crtc_blend_setup(struct drm_crtc *crtc)
+static void _sde_crtc_blend_setup(struct drm_crtc *crtc, bool add_planes)
{
struct sde_crtc *sde_crtc;
struct sde_crtc_state *sde_crtc_state;
@@ -1478,7 +1520,8 @@
/* initialize stage cfg */
memset(&sde_crtc->stage_cfg, 0, sizeof(struct sde_hw_stage_cfg));
- _sde_crtc_blend_setup_mixer(crtc, sde_crtc, mixer);
+ if (add_planes)
+ _sde_crtc_blend_setup_mixer(crtc, sde_crtc, mixer);
for (i = 0; i < sde_crtc->num_mixers; i++) {
const struct sde_rect *lm_roi = &sde_crtc_state->lm_roi[i];
@@ -3093,7 +3136,7 @@
if (unlikely(!sde_crtc->num_mixers))
return;
- _sde_crtc_blend_setup(crtc);
+ _sde_crtc_blend_setup(crtc, true);
_sde_crtc_dest_scaler_setup(crtc);
/*
@@ -3384,7 +3427,158 @@
}
}
-void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
+/**
+ * _sde_crtc_reset_hw - attempt hardware reset on errors
+ * @crtc: Pointer to DRM crtc instance
+ * @old_state: Pointer to crtc state for previous commit
+ * Returns: Zero if current commit should still be attempted
+ */
+static int _sde_crtc_reset_hw(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+{
+ struct drm_plane *plane_halt[MAX_PLANES];
+ struct drm_plane *plane;
+ const struct drm_plane_state *pstate;
+ struct sde_crtc *sde_crtc;
+ struct sde_hw_ctl *ctl;
+ signed int i, plane_count;
+ int rc;
+
+ if (!crtc || !old_state)
+ return -EINVAL;
+ sde_crtc = to_sde_crtc(crtc);
+
+ for (i = 0; i < sde_crtc->num_mixers; ++i) {
+ ctl = sde_crtc->mixers[i].hw_ctl;
+ if (!ctl || !ctl->ops.reset)
+ continue;
+
+ rc = ctl->ops.reset(ctl);
+ if (rc) {
+ SDE_DEBUG("crtc%d: ctl%d reset failure\n",
+ crtc->base.id, ctl->idx - CTL_0);
+ SDE_EVT32(DRMID(crtc), ctl->idx - CTL_0,
+ SDE_EVTLOG_ERROR);
+ break;
+ }
+ }
+
+ /* early out if simple ctl reset succeeded */
+ if (i == sde_crtc->num_mixers) {
+ SDE_EVT32(DRMID(crtc), i);
+ return false;
+ }
+
+ SDE_DEBUG("crtc%d: issuing hard reset\n", DRMID(crtc));
+
+ /* force all components in the system into reset at the same time */
+ for (i = 0; i < sde_crtc->num_mixers; ++i) {
+ ctl = sde_crtc->mixers[i].hw_ctl;
+ if (!ctl || !ctl->ops.hard_reset)
+ continue;
+
+ SDE_EVT32(DRMID(crtc), ctl->idx - CTL_0);
+ ctl->ops.hard_reset(ctl, true);
+ }
+
+ plane_count = 0;
+ drm_atomic_crtc_state_for_each_plane(plane, old_state) {
+ if (plane_count >= ARRAY_SIZE(plane_halt))
+ break;
+
+ plane_halt[plane_count++] = plane;
+ sde_plane_halt_requests(plane, true);
+ sde_plane_set_revalidate(plane, true);
+ }
+
+ /* reset both previous... */
+ for_each_plane_in_state(old_state->state, plane, pstate, i) {
+ if (pstate->crtc != crtc)
+ continue;
+
+ sde_plane_reset_rot(plane, (struct drm_plane_state *)pstate);
+ }
+
+ /* ...and current rotation attempts, if applicable */
+ drm_atomic_crtc_for_each_plane(plane, crtc) {
+ pstate = plane->state;
+ if (!pstate)
+ continue;
+
+ sde_plane_reset_rot(plane, (struct drm_plane_state *)pstate);
+ }
+
+ /* take h/w components out of reset */
+ for (i = plane_count - 1; i >= 0; --i)
+ sde_plane_halt_requests(plane_halt[i], false);
+
+ for (i = 0; i < sde_crtc->num_mixers; ++i) {
+ ctl = sde_crtc->mixers[i].hw_ctl;
+ if (!ctl || !ctl->ops.hard_reset)
+ continue;
+
+ ctl->ops.hard_reset(ctl, false);
+ }
+
+ return -EAGAIN;
+}
+
+/**
+ * _sde_crtc_prepare_for_kickoff_rot - rotator related kickoff preparation
+ * @dev: Pointer to drm device
+ * @crtc: Pointer to crtc structure
+ * Returns: true on preparation errors
+ */
+static bool _sde_crtc_prepare_for_kickoff_rot(struct drm_device *dev,
+ struct drm_crtc *crtc)
+{
+ struct drm_encoder *encoder;
+ struct sde_crtc *sde_crtc;
+ struct sde_crtc_state *cstate;
+
+ if (!crtc || !dev) {
+ SDE_ERROR("invalid argument(s)\n");
+ return false;
+ }
+ sde_crtc = to_sde_crtc(crtc);
+ cstate = to_sde_crtc_state(crtc->state);
+
+ /* default to ASYNC mode for inline rotation */
+ cstate->sbuf_cfg.rot_op_mode = sde_crtc->sbuf_flush_mask ?
+ SDE_CTL_ROT_OP_MODE_INLINE_ASYNC : SDE_CTL_ROT_OP_MODE_OFFLINE;
+
+ if (cstate->sbuf_cfg.rot_op_mode == SDE_CTL_ROT_OP_MODE_OFFLINE)
+ return false;
+
+ /* extra steps needed for inline ASYNC modes */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ if (encoder->crtc != crtc)
+ continue;
+
+ /*
+ * For inline ASYNC modes, the flush bits are not written
+ * to hardware atomically, so avoid using it if a video
+ * mode encoder is active on this CRTC.
+ */
+ if (sde_encoder_get_intf_mode(encoder) == INTF_MODE_VIDEO) {
+ cstate->sbuf_cfg.rot_op_mode =
+ SDE_CTL_ROT_OP_MODE_INLINE_SYNC;
+ return false;
+ }
+ }
+
+ /*
+ * For ASYNC inline modes, kick off the rotator now so that the H/W
+ * can start as soon as it's ready.
+ */
+ if (_sde_crtc_commit_kickoff_rot(crtc, cstate))
+ return true;
+
+ return false;
+}
+
+void sde_crtc_commit_kickoff(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
{
struct drm_encoder *encoder;
struct drm_device *dev;
@@ -3392,7 +3586,7 @@
struct msm_drm_private *priv;
struct sde_kms *sde_kms;
struct sde_crtc_state *cstate;
- bool is_error;
+ bool is_error, reset_req;
int ret;
if (!crtc) {
@@ -3402,7 +3596,7 @@
dev = crtc->dev;
sde_crtc = to_sde_crtc(crtc);
sde_kms = _sde_crtc_get_kms(crtc);
- is_error = false;
+ reset_req = false;
if (!sde_kms || !sde_kms->dev || !sde_kms->dev->dev_private) {
SDE_ERROR("invalid argument\n");
@@ -3422,9 +3616,7 @@
SDE_ATRACE_BEGIN("crtc_commit");
- /* default to ASYNC mode for inline rotation */
- cstate->sbuf_cfg.rot_op_mode = sde_crtc->sbuf_flush_mask ?
- SDE_CTL_ROT_OP_MODE_INLINE_ASYNC : SDE_CTL_ROT_OP_MODE_OFFLINE;
+ is_error = _sde_crtc_prepare_for_kickoff_rot(dev, crtc);
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
struct sde_encoder_kickoff_params params = { 0 };
@@ -3439,29 +3631,24 @@
params.inline_rotate_prefill = cstate->sbuf_prefill_line;
params.affected_displays = _sde_crtc_get_displays_affected(crtc,
crtc->state);
- sde_encoder_prepare_for_kickoff(encoder, ¶ms);
-
- /*
- * For inline ASYNC modes, the flush bits are not written
- * to hardware atomically, so avoid using it if a video
- * mode encoder is active on this CRTC.
- */
- if (cstate->sbuf_cfg.rot_op_mode ==
- SDE_CTL_ROT_OP_MODE_INLINE_ASYNC &&
- sde_encoder_get_intf_mode(encoder) ==
- INTF_MODE_VIDEO)
- cstate->sbuf_cfg.rot_op_mode =
- SDE_CTL_ROT_OP_MODE_INLINE_SYNC;
+ if (sde_encoder_prepare_for_kickoff(encoder, ¶ms))
+ reset_req = true;
}
/*
- * For ASYNC inline modes, kick off the rotator now so that the H/W
- * can start as soon as it's ready.
+ * Optionally attempt h/w recovery if any errors were detected while
+ * preparing for the kickoff
*/
- if (cstate->sbuf_cfg.rot_op_mode == SDE_CTL_ROT_OP_MODE_INLINE_ASYNC)
- if (_sde_crtc_commit_kickoff_rot(crtc, cstate))
+ if (reset_req) {
+ if (_sde_crtc_reset_hw(crtc, old_state))
is_error = true;
+ /* force offline rotation mode since the commit has no pipes */
+ if (is_error)
+ cstate->sbuf_cfg.rot_op_mode =
+ SDE_CTL_ROT_OP_MODE_OFFLINE;
+ }
+
/* wait for frame_event_done completion */
SDE_ATRACE_BEGIN("wait_for_frame_done_event");
ret = _sde_crtc_wait_for_frame_done(crtc);
@@ -3502,14 +3689,16 @@
sde_vbif_clear_errors(sde_kms);
- if (is_error)
+ if (is_error) {
_sde_crtc_remove_pipe_flush(sde_crtc);
+ _sde_crtc_blend_setup(crtc, false);
+ }
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
if (encoder->crtc != crtc)
continue;
- sde_encoder_kickoff(encoder, is_error);
+ sde_encoder_kickoff(encoder, false);
}
reinit_completion(&sde_crtc->frame_done_comp);
@@ -4714,6 +4903,7 @@
}
sde_kms_info_add_keyint(info, "has_src_split", catalog->has_src_split);
+ sde_kms_info_add_keyint(info, "has_hdr", catalog->has_hdr);
if (catalog->perf.max_bw_low)
sde_kms_info_add_keyint(info, "max_bandwidth_low",
catalog->perf.max_bw_low * 1000LL);
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index 704095a..cdf2ed1 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -508,8 +508,10 @@
/**
* sde_crtc_commit_kickoff - trigger kickoff of the commit for this crtc
* @crtc: Pointer to drm crtc object
+ * @old_state: Pointer to drm crtc old state object
*/
-void sde_crtc_commit_kickoff(struct drm_crtc *crtc);
+void sde_crtc_commit_kickoff(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state);
/**
* sde_crtc_prepare_commit - callback to prepare for output fences
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 7d1e4bf..53d874d 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -73,6 +73,10 @@
#define IDLE_SHORT_TIMEOUT 1
+#define FAULT_TOLERENCE_DELTA_IN_MS 2
+
+#define FAULT_TOLERENCE_WAIT_IN_MS 5
+
/* Maximum number of VSYNC wait attempts for RSC state transition */
#define MAX_RSC_WAIT 5
@@ -177,13 +181,10 @@
* Bit0 = phys_encs[0] etc.
* @crtc_frame_event_cb: callback handler for frame event
* @crtc_frame_event_cb_data: callback handler private data
- * @frame_done_timeout: frame done timeout in Hz
- * @frame_done_timer: watchdog timer for frame done event
* @vsync_event_timer: vsync timer
* @rsc_client: rsc client pointer
* @rsc_state_init: boolean to indicate rsc config init
* @disp_info: local copy of msm_display_info struct
- * @mode_info: local copy of msm_mode_info struct
* @misr_enable: misr enable/disable status
* @misr_frame_count: misr frame count before start capturing the data
* @idle_pc_supported: indicate if idle power collaps is supported
@@ -224,14 +225,11 @@
void (*crtc_frame_event_cb)(void *, u32 event);
void *crtc_frame_event_cb_data;
- atomic_t frame_done_timeout;
- struct timer_list frame_done_timer;
struct timer_list vsync_event_timer;
struct sde_rsc_client *rsc_client;
bool rsc_state_init;
struct msm_display_info disp_info;
- struct msm_mode_info mode_info;
bool misr_enable;
u32 misr_frame_count;
@@ -252,17 +250,67 @@
#define to_sde_encoder_virt(x) container_of(x, struct sde_encoder_virt, base)
-bool sde_encoder_is_dsc_enabled(struct drm_encoder *drm_enc)
-
+static struct drm_connector_state *_sde_encoder_get_conn_state(
+ struct drm_encoder *drm_enc)
{
- struct sde_encoder_virt *sde_enc;
+ struct msm_drm_private *priv;
+ struct sde_kms *sde_kms;
+ struct list_head *connector_list;
+ struct drm_connector *conn_iter;
+
+ if (!drm_enc) {
+ SDE_ERROR("invalid argument\n");
+ return NULL;
+ }
+
+ priv = drm_enc->dev->dev_private;
+ sde_kms = to_sde_kms(priv->kms);
+ connector_list = &sde_kms->dev->mode_config.connector_list;
+
+ list_for_each_entry(conn_iter, connector_list, head)
+ if (conn_iter->encoder == drm_enc)
+ return conn_iter->state;
+
+ return NULL;
+}
+
+static int _sde_encoder_get_mode_info(struct drm_encoder *drm_enc,
+ struct msm_mode_info *mode_info)
+{
+ struct drm_connector_state *conn_state;
+
+ if (!drm_enc || !mode_info) {
+ SDE_ERROR("invalid arguments\n");
+ return -EINVAL;
+ }
+
+ conn_state = _sde_encoder_get_conn_state(drm_enc);
+ if (!conn_state) {
+ SDE_ERROR("invalid connector state for the encoder: %d\n",
+ drm_enc->base.id);
+ return -EINVAL;
+ }
+
+ return sde_connector_get_mode_info(conn_state, mode_info);
+}
+
+static bool _sde_encoder_is_dsc_enabled(struct drm_encoder *drm_enc)
+{
struct msm_compression_info *comp_info;
+ struct msm_mode_info mode_info;
+ int rc = 0;
if (!drm_enc)
return false;
- sde_enc = to_sde_encoder_virt(drm_enc);
- comp_info = &sde_enc->mode_info.comp_info;
+ rc = _sde_encoder_get_mode_info(drm_enc, &mode_info);
+ if (rc) {
+ SDE_ERROR("failed to get mode info, enc: %d\n",
+ drm_enc->base.id);
+ return false;
+ }
+
+ comp_info = &mode_info.comp_info;
return (comp_info->comp_type == MSM_DISPLAY_COMPRESSION_DSC);
}
@@ -536,7 +584,8 @@
struct drm_connector_state *conn_state)
{
struct sde_encoder_virt *sde_enc = NULL;
- int i = 0;
+ struct msm_mode_info mode_info;
+ int rc, i = 0;
if (!hw_res || !drm_enc || !conn_state) {
SDE_ERROR("invalid argument(s), drm_enc %d, res %d, state %d\n",
@@ -558,7 +607,18 @@
phys->ops.get_hw_resources(phys, hw_res, conn_state);
}
- hw_res->topology = sde_enc->mode_info.topology;
+ /**
+ * NOTE: Do not use sde_encoder_get_mode_info here as this function is
+ * called from atomic_check phase. Use the below API to get mode
+ * information of the temporary conn_state passed.
+ */
+ rc = sde_connector_get_mode_info(conn_state, &mode_info);
+ if (rc) {
+ SDE_ERROR_ENC(sde_enc, "failed to get mode info\n");
+ return;
+ }
+
+ hw_res->topology = mode_info.topology;
hw_res->is_primary = sde_enc->disp_info.is_primary;
}
@@ -699,6 +759,7 @@
const struct drm_display_mode *mode;
struct drm_display_mode *adj_mode;
struct sde_connector *sde_conn = NULL;
+ struct sde_connector_state *sde_conn_state = NULL;
int i = 0;
int ret = 0;
@@ -716,6 +777,7 @@
mode = &crtc_state->mode;
adj_mode = &crtc_state->adjusted_mode;
sde_conn = to_sde_connector(conn_state->connector);
+ sde_conn_state = to_sde_connector_state(conn_state);
SDE_EVT32(DRMID(drm_enc));
/*
@@ -750,7 +812,7 @@
struct msm_display_topology *topology = NULL;
ret = sde_conn->ops.get_mode_info(adj_mode,
- &sde_enc->mode_info,
+ &sde_conn_state->mode_info,
sde_kms->catalog->max_mixer_width,
sde_conn->display);
if (ret) {
@@ -775,7 +837,7 @@
* de-activating crtc.
*/
if (crtc_state->active)
- topology = &sde_enc->mode_info.topology;
+ topology = &sde_conn_state->mode_info.topology;
ret = sde_rm_update_topology(conn_state, topology);
if (ret) {
@@ -783,6 +845,22 @@
"RM failed to update topology, rc: %d\n", ret);
return ret;
}
+
+ ret = sde_connector_set_info(conn_state->connector, conn_state);
+ if (ret) {
+ SDE_ERROR_ENC(sde_enc,
+ "connector failed to update info, rc: %d\n",
+ ret);
+ return ret;
+ }
+
+ }
+
+ ret = sde_connector_roi_v1_check_roi(conn_state);
+ if (ret) {
+ SDE_ERROR_ENC(sde_enc, "connector roi check failed, rc: %d",
+ ret);
+ return ret;
}
if (!ret)
@@ -948,14 +1026,23 @@
struct sde_hw_dsc *hw_dsc = sde_enc->hw_dsc[0];
struct sde_encoder_phys *enc_master = sde_enc->cur_master;
const struct sde_rect *roi = &sde_enc->cur_conn_roi;
- struct msm_display_dsc_info *dsc =
- &sde_enc->mode_info.comp_info.dsc_info;
+ struct msm_mode_info mode_info;
+ struct msm_display_dsc_info *dsc = NULL;
+ int rc;
- if (dsc == NULL || hw_dsc == NULL || hw_pp == NULL || !enc_master) {
+ if (hw_dsc == NULL || hw_pp == NULL || !enc_master) {
SDE_ERROR_ENC(sde_enc, "invalid params for DSC\n");
return -EINVAL;
}
+ 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 -EINVAL;
+ }
+
+ dsc = &mode_info.comp_info.dsc_info;
+
_sde_encoder_dsc_update_pic_dim(dsc, roi->w, roi->h);
this_frame_slices = roi->w / dsc->slice_width;
@@ -992,8 +1079,9 @@
struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
struct msm_display_dsc_info dsc[MAX_CHANNELS_PER_ENC];
+ struct msm_mode_info mode_info;
bool half_panel_partial_update;
- int i;
+ int i, rc;
for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
hw_pp[i] = sde_enc->hw_pp[i];
@@ -1005,6 +1093,12 @@
}
}
+ 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 -EINVAL;
+ }
+
half_panel_partial_update =
hweight_long(params->affected_displays) == 1;
@@ -1014,8 +1108,8 @@
if (enc_master->intf_mode == INTF_MODE_VIDEO)
dsc_common_mode |= DSC_MODE_VIDEO;
- memcpy(&dsc[0], &sde_enc->mode_info.comp_info.dsc_info, sizeof(dsc[0]));
- memcpy(&dsc[1], &sde_enc->mode_info.comp_info.dsc_info, sizeof(dsc[1]));
+ memcpy(&dsc[0], &mode_info.comp_info.dsc_info, sizeof(dsc[0]));
+ memcpy(&dsc[1], &mode_info.comp_info.dsc_info, sizeof(dsc[1]));
/*
* Since both DSC use same pic dimension, set same pic dimension
@@ -1078,10 +1172,10 @@
const struct sde_rect *roi = &sde_enc->cur_conn_roi;
struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
- struct msm_display_dsc_info *dsc =
- &sde_enc->mode_info.comp_info.dsc_info;
+ struct msm_display_dsc_info *dsc = NULL;
+ struct msm_mode_info mode_info;
bool half_panel_partial_update;
- int i;
+ int i, rc;
for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
hw_pp[i] = sde_enc->hw_pp[i];
@@ -1093,6 +1187,14 @@
}
}
+ 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 -EINVAL;
+ }
+
+ dsc = &mode_info.comp_info.dsc_info;
+
half_panel_partial_update =
hweight_long(params->affected_displays) == 1;
@@ -1215,8 +1317,8 @@
struct sde_kms *sde_kms;
struct sde_hw_mdp *hw_mdptop;
struct drm_encoder *drm_enc;
- struct msm_mode_info *mode_info;
- int i;
+ struct msm_mode_info mode_info;
+ int i, rc = 0;
if (!sde_enc || !disp_info) {
SDE_ERROR("invalid param sde_enc:%d or disp_info:%d\n",
@@ -1245,9 +1347,9 @@
return;
}
- mode_info = &sde_enc->mode_info;
- if (!mode_info) {
- SDE_ERROR("invalid mode info\n");
+ rc = _sde_encoder_get_mode_info(drm_enc, &mode_info);
+ if (rc) {
+ SDE_ERROR_ENC(sde_enc, "failed to get mode info\n");
return;
}
@@ -1257,7 +1359,7 @@
vsync_cfg.ppnumber[i] = sde_enc->hw_pp[i]->idx;
vsync_cfg.pp_count = sde_enc->num_phys_encs;
- vsync_cfg.frame_rate = mode_info->frame_rate;
+ vsync_cfg.frame_rate = mode_info.frame_rate;
if (is_dummy)
vsync_cfg.vsync_source = SDE_VSYNC_SOURCE_WD_TIMER_1;
else if (disp_info->is_te_using_watchdog_timer)
@@ -1308,11 +1410,12 @@
struct sde_rsc_cmd_config *rsc_config;
int ret, prefill_lines;
struct msm_display_info *disp_info;
- struct msm_mode_info *mode_info;
+ struct msm_mode_info mode_info;
int wait_vblank_crtc_id = SDE_RSC_INVALID_CRTC_ID;
int wait_count = 0;
struct drm_crtc *primary_crtc;
int pipe = -1;
+ int rc = 0;
if (!drm_enc || !drm_enc->crtc || !drm_enc->dev) {
SDE_ERROR("invalid arguments\n");
@@ -1322,7 +1425,6 @@
sde_enc = to_sde_encoder_virt(drm_enc);
crtc = drm_enc->crtc;
disp_info = &sde_enc->disp_info;
- mode_info = &sde_enc->mode_info;
rsc_config = &sde_enc->rsc_config;
if (!sde_enc->rsc_client) {
@@ -1330,6 +1432,12 @@
return 0;
}
+ rc = _sde_encoder_get_mode_info(drm_enc, &mode_info);
+ if (rc) {
+ SDE_ERROR_ENC(sde_enc, "failed to mode info\n");
+ return 0;
+ }
+
/**
* only primary command mode panel can request CMD state.
* all other panels/displays can request for VID state including
@@ -1339,20 +1447,20 @@
(((disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) &&
disp_info->is_primary) ? SDE_RSC_CMD_STATE :
SDE_RSC_VID_STATE) : SDE_RSC_IDLE_STATE;
- prefill_lines = config ? mode_info->prefill_lines +
- config->inline_rotate_prefill : mode_info->prefill_lines;
+ prefill_lines = config ? mode_info.prefill_lines +
+ config->inline_rotate_prefill : mode_info.prefill_lines;
/* compare specific items and reconfigure the rsc */
- if ((rsc_config->fps != mode_info->frame_rate) ||
- (rsc_config->vtotal != mode_info->vtotal) ||
+ if ((rsc_config->fps != mode_info.frame_rate) ||
+ (rsc_config->vtotal != mode_info.vtotal) ||
(rsc_config->prefill_lines != prefill_lines) ||
- (rsc_config->jitter_numer != mode_info->jitter_numer) ||
- (rsc_config->jitter_denom != mode_info->jitter_denom)) {
- rsc_config->fps = mode_info->frame_rate;
- rsc_config->vtotal = mode_info->vtotal;
+ (rsc_config->jitter_numer != mode_info.jitter_numer) ||
+ (rsc_config->jitter_denom != mode_info.jitter_denom)) {
+ rsc_config->fps = mode_info.frame_rate;
+ rsc_config->vtotal = mode_info.vtotal;
rsc_config->prefill_lines = prefill_lines;
- rsc_config->jitter_numer = mode_info->jitter_numer;
- rsc_config->jitter_denom = mode_info->jitter_denom;
+ rsc_config->jitter_numer = mode_info.jitter_numer;
+ rsc_config->jitter_denom = mode_info.jitter_denom;
sde_enc->rsc_state_init = false;
}
@@ -1761,8 +1869,10 @@
break;
case SDE_ENC_RC_EVENT_STOP:
- mutex_lock(&sde_enc->rc_lock);
+ /* cancel vsync event work */
+ kthread_cancel_work_sync(&sde_enc->vsync_event_work);
+ mutex_lock(&sde_enc->rc_lock);
/* return if the resource control is already in OFF state */
if (sde_enc->rc_state == SDE_ENC_RC_STATE_OFF) {
SDE_DEBUG_ENC(sde_enc, "sw_event:%d, rc in OFF state\n",
@@ -1927,6 +2037,7 @@
struct sde_kms *sde_kms;
struct list_head *connector_list;
struct drm_connector *conn = NULL, *conn_iter;
+ struct sde_connector_state *sde_conn_state = NULL;
struct sde_connector *sde_conn = NULL;
struct sde_rm_hw_iter dsc_iter, pp_iter;
int i = 0, ret;
@@ -1958,13 +2069,15 @@
}
sde_conn = to_sde_connector(conn);
- if (sde_conn) {
- ret = sde_conn->ops.get_mode_info(adj_mode, &sde_enc->mode_info,
+ sde_conn_state = to_sde_connector_state(conn->state);
+ if (sde_conn && sde_conn_state) {
+ ret = sde_conn->ops.get_mode_info(adj_mode,
+ &sde_conn_state->mode_info,
sde_kms->catalog->max_mixer_width,
sde_conn->display);
if (ret) {
SDE_ERROR_ENC(sde_enc,
- "invalid topology for the mode\n");
+ "failed to get mode info from the display\n");
return;
}
}
@@ -2107,13 +2220,21 @@
int i, ret = 0;
struct msm_compression_info *comp_info = NULL;
struct drm_display_mode *cur_mode = NULL;
+ struct msm_mode_info mode_info;
if (!drm_enc) {
SDE_ERROR("invalid encoder\n");
return;
}
sde_enc = to_sde_encoder_virt(drm_enc);
- comp_info = &sde_enc->mode_info.comp_info;
+
+ ret = _sde_encoder_get_mode_info(drm_enc, &mode_info);
+ if (ret) {
+ SDE_ERROR_ENC(sde_enc, "failed to get mode info\n");
+ return;
+ }
+
+ comp_info = &mode_info.comp_info;
cur_mode = &sde_enc->base.crtc->state->adjusted_mode;
SDE_DEBUG_ENC(sde_enc, "\n");
@@ -2222,12 +2343,6 @@
*/
_sde_encoder_dsc_disable(sde_enc);
- /* after phys waits for frame-done, should be no more frames pending */
- if (atomic_xchg(&sde_enc->frame_done_timeout, 0)) {
- SDE_ERROR("enc%d timeout pending\n", drm_enc->base.id);
- del_timer_sync(&sde_enc->frame_done_timer);
- }
-
sde_encoder_resource_control(drm_enc, SDE_ENC_RC_EVENT_STOP);
for (i = 0; i < sde_enc->num_phys_encs; i++) {
@@ -2383,9 +2498,6 @@
}
if (!sde_enc->frame_busy_mask[0]) {
- atomic_set(&sde_enc->frame_done_timeout, 0);
- del_timer(&sde_enc->frame_done_timer);
-
sde_encoder_resource_control(drm_enc,
SDE_ENC_RC_EVENT_FRAME_DONE);
@@ -2466,11 +2578,12 @@
phys->ops.trigger_flush(phys);
if (ctl->ops.get_pending_flush)
- SDE_EVT32(DRMID(drm_enc), phys->intf_idx, pending_kickoff_cnt,
- ctl->idx, ctl->ops.get_pending_flush(ctl));
+ SDE_EVT32(DRMID(drm_enc), phys->intf_idx - INTF_0,
+ pending_kickoff_cnt, ctl->idx - CTL_0,
+ ctl->ops.get_pending_flush(ctl));
else
- SDE_EVT32(DRMID(drm_enc), phys->intf_idx, ctl->idx,
- pending_kickoff_cnt);
+ SDE_EVT32(DRMID(drm_enc), phys->intf_idx - INTF_0,
+ ctl->idx - CTL_0, pending_kickoff_cnt);
}
/**
@@ -2529,30 +2642,50 @@
ctl = phys_enc->hw_ctl;
if (ctl && ctl->ops.trigger_start) {
ctl->ops.trigger_start(ctl);
- SDE_EVT32(DRMID(phys_enc->parent), ctl->idx);
+ SDE_EVT32(DRMID(phys_enc->parent), ctl->idx - CTL_0);
}
}
-int sde_encoder_helper_wait_event_timeout(
- int32_t drm_id,
- int32_t hw_id,
- struct sde_encoder_wait_info *info)
+static int _sde_encoder_wait_timeout(int32_t drm_id, int32_t hw_id,
+ s64 timeout_ms, struct sde_encoder_wait_info *info)
{
int rc = 0;
- s64 expected_time = ktime_to_ms(ktime_get()) + info->timeout_ms;
- s64 jiffies = msecs_to_jiffies(info->timeout_ms);
- s64 time;
+ s64 wait_time_jiffies = msecs_to_jiffies(timeout_ms);
+ ktime_t cur_ktime;
+ ktime_t exp_ktime = ktime_add_ms(ktime_get(), timeout_ms);
do {
rc = wait_event_timeout(*(info->wq),
- atomic_read(info->atomic_cnt) == 0, jiffies);
- time = ktime_to_ms(ktime_get());
+ atomic_read(info->atomic_cnt) == 0, wait_time_jiffies);
+ cur_ktime = ktime_get();
- SDE_EVT32_VERBOSE(drm_id, hw_id, rc, time, expected_time,
- atomic_read(info->atomic_cnt));
+ SDE_EVT32(drm_id, hw_id, rc, ktime_to_ms(cur_ktime),
+ timeout_ms, atomic_read(info->atomic_cnt));
/* If we timed out, counter is valid and time is less, wait again */
} while (atomic_read(info->atomic_cnt) && (rc == 0) &&
- (time < expected_time));
+ (ktime_compare_safe(exp_ktime, cur_ktime) > 0));
+
+ return rc;
+}
+
+int sde_encoder_helper_wait_event_timeout(int32_t drm_id, int32_t hw_id,
+ struct sde_encoder_wait_info *info)
+{
+ int rc;
+ ktime_t exp_ktime = ktime_add_ms(ktime_get(), info->timeout_ms);
+
+ rc = _sde_encoder_wait_timeout(drm_id, hw_id, info->timeout_ms, info);
+
+ /**
+ * handle disabled irq case where timer irq is also delayed.
+ * wait for additional timeout of FAULT_TOLERENCE_WAIT_IN_MS
+ * if it event_timeout expired late detected.
+ */
+ if (atomic_read(info->atomic_cnt) && (!rc) &&
+ (ktime_compare_safe(ktime_get(), ktime_add_ms(exp_ktime,
+ FAULT_TOLERENCE_DELTA_IN_MS)) > 0))
+ rc = _sde_encoder_wait_timeout(drm_id, hw_id,
+ FAULT_TOLERENCE_WAIT_IN_MS, info);
return rc;
}
@@ -2594,12 +2727,6 @@
}
}
- rc = ctl->ops.reset(ctl);
- if (rc) {
- SDE_ERROR_ENC(sde_enc, "ctl %d reset failure\n", ctl->idx);
- SDE_DBG_DUMP("all", "dbg_bus", "vbif_dbg_bus", "panic");
- }
-
phys_enc->enable_state = SDE_ENC_ENABLED;
}
@@ -2985,7 +3112,6 @@
struct sde_encoder_virt *sde_enc;
struct msm_drm_private *priv;
struct msm_drm_thread *event_thread;
- bool autorefresh_enabled = false;
if (!drm_enc || !drm_enc->dev || !drm_enc->dev->dev_private ||
!drm_enc->crtc) {
@@ -3007,28 +3133,16 @@
return;
}
- if (sde_enc->cur_master &&
- sde_enc->cur_master->ops.is_autorefresh_enabled)
- autorefresh_enabled =
- sde_enc->cur_master->ops.is_autorefresh_enabled(
- sde_enc->cur_master);
-
- /*
- * Queue work to update the vsync event timer
- * if autorefresh is enabled.
- */
- SDE_EVT32_VERBOSE(autorefresh_enabled);
- if (autorefresh_enabled)
- kthread_queue_work(&event_thread->worker,
+ kthread_queue_work(&event_thread->worker,
&sde_enc->vsync_event_work);
- else
- del_timer(&sde_enc->vsync_event_timer);
}
static void sde_encoder_vsync_event_work_handler(struct kthread_work *work)
{
struct sde_encoder_virt *sde_enc = container_of(work,
struct sde_encoder_virt, vsync_event_work);
+ bool autorefresh_enabled = false;
+ int rc = 0;
ktime_t wakeup_time;
if (!sde_enc) {
@@ -3036,6 +3150,24 @@
return;
}
+ rc = _sde_encoder_power_enable(sde_enc, true);
+ if (rc) {
+ SDE_ERROR_ENC(sde_enc, "sde enc power enabled failed:%d\n", rc);
+ return;
+ }
+
+ if (sde_enc->cur_master &&
+ sde_enc->cur_master->ops.is_autorefresh_enabled)
+ autorefresh_enabled =
+ sde_enc->cur_master->ops.is_autorefresh_enabled(
+ sde_enc->cur_master);
+
+ _sde_encoder_power_enable(sde_enc, false);
+
+ /* Update timer if autorefresh is enabled else return */
+ if (!autorefresh_enabled)
+ return;
+
if (_sde_encoder_wakeup_time(&sde_enc->base, &wakeup_time))
return;
@@ -3044,18 +3176,18 @@
nsecs_to_jiffies(ktime_to_ns(wakeup_time)));
}
-void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc,
+int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc,
struct sde_encoder_kickoff_params *params)
{
struct sde_encoder_virt *sde_enc;
struct sde_encoder_phys *phys;
bool needs_hw_reset = false;
unsigned int i;
- int rc;
+ int rc, ret = 0;
if (!drm_enc || !params) {
SDE_ERROR("invalid args\n");
- return;
+ return -EINVAL;
}
sde_enc = to_sde_encoder_virt(drm_enc);
@@ -3067,8 +3199,12 @@
for (i = 0; i < sde_enc->num_phys_encs; i++) {
phys = sde_enc->phys_encs[i];
if (phys) {
- if (phys->ops.prepare_for_kickoff)
- phys->ops.prepare_for_kickoff(phys, params);
+ if (phys->ops.prepare_for_kickoff) {
+ rc = phys->ops.prepare_for_kickoff(
+ phys, params);
+ if (rc)
+ ret = rc;
+ }
if (phys->enable_state == SDE_ENC_ERR_NEEDS_HW_RESET)
needs_hw_reset = true;
_sde_encoder_setup_dither(phys);
@@ -3094,17 +3230,23 @@
if (sde_enc->cur_master && sde_enc->cur_master->connector) {
rc = sde_connector_pre_kickoff(sde_enc->cur_master->connector);
- if (rc)
+ if (rc) {
SDE_ERROR_ENC(sde_enc, "kickoff conn%d failed rc %d\n",
sde_enc->cur_master->connector->base.id,
rc);
+ ret = rc;
+ }
}
- if (sde_encoder_is_dsc_enabled(drm_enc)) {
+ if (_sde_encoder_is_dsc_enabled(drm_enc)) {
rc = _sde_encoder_dsc_setup(sde_enc, params);
- if (rc)
+ if (rc) {
SDE_ERROR_ENC(sde_enc, "failed to setup DSC: %d\n", rc);
+ ret = rc;
+ }
}
+
+ return ret;
}
/**
@@ -3162,12 +3304,6 @@
SDE_DEBUG_ENC(sde_enc, "\n");
- atomic_set(&sde_enc->frame_done_timeout,
- SDE_FRAME_DONE_TIMEOUT * 1000 /
- drm_enc->crtc->state->adjusted_mode.vrefresh);
- mod_timer(&sde_enc->frame_done_timer, jiffies +
- ((atomic_read(&sde_enc->frame_done_timeout) * HZ) / 1000));
-
/* create a 'no pipes' commit to release buffers on errors */
if (is_error)
_sde_encoder_reset_ctl_hw(drm_enc);
@@ -3728,36 +3864,6 @@
return ret;
}
-static void sde_encoder_frame_done_timeout(unsigned long data)
-{
- struct drm_encoder *drm_enc = (struct drm_encoder *) data;
- struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
- struct msm_drm_private *priv;
- u32 event;
-
- if (!drm_enc || !drm_enc->dev || !drm_enc->dev->dev_private) {
- SDE_ERROR("invalid parameters\n");
- return;
- }
- priv = drm_enc->dev->dev_private;
-
- if (!sde_enc->frame_busy_mask[0] || !sde_enc->crtc_frame_event_cb) {
- SDE_DEBUG_ENC(sde_enc, "invalid timeout\n");
- SDE_EVT32(DRMID(drm_enc), sde_enc->frame_busy_mask[0], 0);
- return;
- } else if (!atomic_xchg(&sde_enc->frame_done_timeout, 0)) {
- SDE_ERROR_ENC(sde_enc, "invalid timeout\n");
- SDE_EVT32(DRMID(drm_enc), 0, 1);
- return;
- }
-
- SDE_ERROR_ENC(sde_enc, "frame done timeout\n");
-
- event = SDE_ENCODER_FRAME_EVENT_ERROR;
- SDE_EVT32(DRMID(drm_enc), event);
- sde_enc->crtc_frame_event_cb(sde_enc->crtc_frame_event_cb_data, event);
-}
-
static const struct drm_encoder_helper_funcs sde_encoder_helper_funcs = {
.mode_set = sde_encoder_virt_mode_set,
.disable = sde_encoder_virt_disable,
@@ -3801,10 +3907,6 @@
drm_encoder_init(dev, drm_enc, &sde_encoder_funcs, drm_enc_mode, NULL);
drm_encoder_helper_add(drm_enc, &sde_encoder_helper_funcs);
- atomic_set(&sde_enc->frame_done_timeout, 0);
- setup_timer(&sde_enc->frame_done_timer, sde_encoder_frame_done_timeout,
- (unsigned long) sde_enc);
-
if ((disp_info->intf_type == DRM_MODE_CONNECTOR_DSI) &&
disp_info->is_primary)
setup_timer(&sde_enc->vsync_event_timer,
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index 9a9ff86..3757e3b 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -115,8 +115,9 @@
* Delayed: Block until next trigger can be issued.
* @encoder: encoder pointer
* @params: kickoff time parameters
+ * @Returns: Zero on success, last detected error otherwise
*/
-void sde_encoder_prepare_for_kickoff(struct drm_encoder *encoder,
+int sde_encoder_prepare_for_kickoff(struct drm_encoder *encoder,
struct sde_encoder_kickoff_params *params);
/**
@@ -168,13 +169,6 @@
void sde_encoder_virt_restore(struct drm_encoder *encoder);
/**
- * sde_encoder_is_dsc_enabled - check if encoder is in DSC mode
- * @drm_enc: Pointer to drm encoder object
- * @Return: true if encoder is in DSC mode
- */
-bool sde_encoder_is_dsc_enabled(struct drm_encoder *drm_enc);
-
-/**
* sde_encoder_is_dsc_merge - check if encoder is in DSC merge mode
* @drm_enc: Pointer to drm encoder object
* @Return: true if encoder is in DSC merge mode
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index e5a4da4..d00400d 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -160,7 +160,7 @@
int (*wait_for_commit_done)(struct sde_encoder_phys *phys_enc);
int (*wait_for_tx_complete)(struct sde_encoder_phys *phys_enc);
int (*wait_for_vblank)(struct sde_encoder_phys *phys_enc);
- void (*prepare_for_kickoff)(struct sde_encoder_phys *phys_enc,
+ int (*prepare_for_kickoff)(struct sde_encoder_phys *phys_enc,
struct sde_encoder_kickoff_params *params);
void (*handle_post_kickoff)(struct sde_encoder_phys *phys_enc);
void (*trigger_flush)(struct sde_encoder_phys *phys_enc);
@@ -296,6 +296,7 @@
* @hw_intf: Hardware interface to the intf registers
* @timing_params: Current timing parameter
* @rot_fetch: Prefill for inline rotation
+ * @error_count: Number of consecutive kickoffs that experienced an error
* @rot_fetch_valid: true if rot_fetch is updated (reset in enc enable)
*/
struct sde_encoder_phys_vid {
@@ -303,6 +304,7 @@
struct sde_hw_intf *hw_intf;
struct intf_timing_params timing_params;
struct intf_prog_fetch rot_fetch;
+ int error_count;
bool rot_fetch_valid;
};
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
index 9976f85..164f2e1 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -32,7 +32,7 @@
#define to_sde_encoder_phys_cmd(x) \
container_of(x, struct sde_encoder_phys_cmd, base)
-#define PP_TIMEOUT_MAX_TRIALS 10
+#define PP_TIMEOUT_MAX_TRIALS 2
/*
* Tearcheck sync start and continue thresholds are empirically found
@@ -418,26 +418,26 @@
to_sde_encoder_phys_cmd(phys_enc);
u32 frame_event = SDE_ENCODER_FRAME_EVENT_ERROR
| SDE_ENCODER_FRAME_EVENT_SIGNAL_RELEASE_FENCE;
- bool do_log = false;
if (!phys_enc || !phys_enc->hw_pp || !phys_enc->hw_ctl)
return -EINVAL;
cmd_enc->pp_timeout_report_cnt++;
- if (cmd_enc->pp_timeout_report_cnt == PP_TIMEOUT_MAX_TRIALS) {
- frame_event |= SDE_ENCODER_FRAME_EVENT_PANEL_DEAD;
- do_log = true;
- } else if (cmd_enc->pp_timeout_report_cnt == 1) {
- do_log = true;
- }
SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0,
cmd_enc->pp_timeout_report_cnt,
atomic_read(&phys_enc->pending_kickoff_cnt),
frame_event);
- /* to avoid flooding, only log first time, and "dead" time */
- if (do_log) {
+ if (cmd_enc->pp_timeout_report_cnt >= PP_TIMEOUT_MAX_TRIALS) {
+ cmd_enc->pp_timeout_report_cnt = PP_TIMEOUT_MAX_TRIALS;
+ frame_event |= SDE_ENCODER_FRAME_EVENT_PANEL_DEAD;
+
+ sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_RDPTR);
+ SDE_DBG_DUMP("panic");
+ sde_encoder_helper_register_irq(phys_enc, INTR_IDX_RDPTR);
+ } else if (cmd_enc->pp_timeout_report_cnt == 1) {
+ /* to avoid flooding, only log first time, and "dead" time */
SDE_ERROR_CMDENC(cmd_enc,
"pp:%d kickoff timed out ctl %d cnt %d koff_cnt %d\n",
phys_enc->hw_pp->idx - PINGPONG_0,
@@ -448,7 +448,8 @@
SDE_EVT32(DRMID(phys_enc->parent), SDE_EVTLOG_FATAL);
sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_RDPTR);
- SDE_DBG_DUMP("all", "dbg_bus", "vbif_dbg_bus", "panic");
+ SDE_DBG_DUMP("all", "dbg_bus", "vbif_dbg_bus");
+ sde_encoder_helper_register_irq(phys_enc, INTR_IDX_RDPTR);
}
atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
@@ -984,7 +985,7 @@
hw_res->intfs[phys_enc->intf_idx - INTF_0] = INTF_MODE_CMD;
}
-static void sde_encoder_phys_cmd_prepare_for_kickoff(
+static int sde_encoder_phys_cmd_prepare_for_kickoff(
struct sde_encoder_phys *phys_enc,
struct sde_encoder_kickoff_params *params)
{
@@ -994,7 +995,7 @@
if (!phys_enc || !phys_enc->hw_pp) {
SDE_ERROR("invalid encoder\n");
- return;
+ return -EINVAL;
}
SDE_DEBUG_CMDENC(cmd_enc, "pp %d\n", phys_enc->hw_pp->idx - PINGPONG_0);
@@ -1018,6 +1019,7 @@
SDE_DEBUG_CMDENC(cmd_enc, "pp:%d pending_cnt %d\n",
phys_enc->hw_pp->idx - PINGPONG_0,
atomic_read(&phys_enc->pending_kickoff_cnt));
+ return ret;
}
static int _sde_encoder_phys_cmd_wait_for_ctl_start(
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index a983b7c..77b28f0 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -33,6 +33,9 @@
#define to_sde_encoder_phys_vid(x) \
container_of(x, struct sde_encoder_phys_vid, base)
+/* maximum number of consecutive kickoff errors */
+#define KICKOFF_MAX_ERRORS 2
+
static bool sde_encoder_phys_vid_is_master(
struct sde_encoder_phys *phys_enc)
{
@@ -722,7 +725,8 @@
struct sde_encoder_phys *phys_enc, bool notify)
{
struct sde_encoder_wait_info wait_info;
- int ret;
+ int ret = 0;
+ u32 event = 0;
if (!phys_enc) {
pr_err("invalid encoder\n");
@@ -735,11 +739,10 @@
if (!sde_encoder_phys_vid_is_master(phys_enc)) {
/* signal done for slave video encoder, unless it is pp-split */
- if (!_sde_encoder_phys_is_ppsplit(phys_enc) &&
- notify && phys_enc->parent_ops.handle_frame_done)
- phys_enc->parent_ops.handle_frame_done(
- phys_enc->parent, phys_enc,
- SDE_ENCODER_FRAME_EVENT_DONE);
+ if (!_sde_encoder_phys_is_ppsplit(phys_enc) && notify) {
+ event = SDE_ENCODER_FRAME_EVENT_DONE;
+ goto end;
+ }
return 0;
}
@@ -747,13 +750,20 @@
ret = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_VSYNC,
&wait_info);
- if (ret == -ETIMEDOUT) {
- sde_encoder_helper_report_irq_timeout(phys_enc, INTR_IDX_VSYNC);
- } else if (!ret && notify && phys_enc->parent_ops.handle_frame_done)
+ if (ret == -ETIMEDOUT)
+ event = SDE_ENCODER_FRAME_EVENT_SIGNAL_RELEASE_FENCE
+ | SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE
+ | SDE_ENCODER_FRAME_EVENT_ERROR;
+ else if (!ret && notify)
+ event = SDE_ENCODER_FRAME_EVENT_DONE;
+
+end:
+ SDE_EVT32(DRMID(phys_enc->parent), event, notify, ret,
+ ret ? SDE_EVTLOG_FATAL : 0);
+ if (phys_enc->parent_ops.handle_frame_done && event)
phys_enc->parent_ops.handle_frame_done(
phys_enc->parent, phys_enc,
SDE_ENCODER_FRAME_EVENT_DONE);
-
return ret;
}
@@ -763,7 +773,7 @@
return _sde_encoder_phys_vid_wait_for_vblank(phys_enc, true);
}
-static void sde_encoder_phys_vid_prepare_for_kickoff(
+static int sde_encoder_phys_vid_prepare_for_kickoff(
struct sde_encoder_phys *phys_enc,
struct sde_encoder_kickoff_params *params)
{
@@ -771,15 +781,15 @@
struct sde_hw_ctl *ctl;
int rc;
- if (!phys_enc || !params) {
+ if (!phys_enc || !params || !phys_enc->hw_ctl) {
SDE_ERROR("invalid encoder/parameters\n");
- return;
+ return -EINVAL;
}
vid_enc = to_sde_encoder_phys_vid(phys_enc);
ctl = phys_enc->hw_ctl;
- if (!ctl || !ctl->ops.wait_reset_status)
- return;
+ if (!ctl->ops.wait_reset_status)
+ return 0;
/*
* hw supports hardware initiated ctl reset, so before we kickoff a new
@@ -789,11 +799,35 @@
if (rc) {
SDE_ERROR_VIDENC(vid_enc, "ctl %d reset failure: %d\n",
ctl->idx, rc);
- sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_VSYNC);
- SDE_DBG_DUMP("all", "dbg_bus", "vbif_dbg_bus", "panic");
+
+ ++vid_enc->error_count;
+ if (vid_enc->error_count >= KICKOFF_MAX_ERRORS) {
+ vid_enc->error_count = KICKOFF_MAX_ERRORS;
+
+ sde_encoder_helper_unregister_irq(
+ phys_enc, INTR_IDX_VSYNC);
+ SDE_DBG_DUMP("panic");
+ sde_encoder_helper_register_irq(
+ phys_enc, INTR_IDX_VSYNC);
+ } else if (vid_enc->error_count == 1) {
+ SDE_EVT32(DRMID(phys_enc->parent), SDE_EVTLOG_FATAL);
+
+ sde_encoder_helper_unregister_irq(
+ phys_enc, INTR_IDX_VSYNC);
+ SDE_DBG_DUMP("all", "dbg_bus", "vbif_dbg_bus");
+ sde_encoder_helper_register_irq(
+ phys_enc, INTR_IDX_VSYNC);
+ }
+
+ /* request a ctl reset before the next flush */
+ phys_enc->enable_state = SDE_ENC_ERR_NEEDS_HW_RESET;
+ } else {
+ vid_enc->error_count = 0;
}
programmable_rot_fetch_config(phys_enc, params->inline_rotate_prefill);
+
+ return rc;
}
static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc)
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
index d7084dd..bf7d3da 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
@@ -925,8 +925,9 @@
* sde_encoder_phys_wb_prepare_for_kickoff - pre-kickoff processing
* @phys_enc: Pointer to physical encoder
* @params: kickoff parameters
+ * Returns: Zero on success
*/
-static void sde_encoder_phys_wb_prepare_for_kickoff(
+static int sde_encoder_phys_wb_prepare_for_kickoff(
struct sde_encoder_phys *phys_enc,
struct sde_encoder_kickoff_params *params)
{
@@ -941,7 +942,7 @@
ret = sde_encoder_phys_wb_register_irq(phys_enc);
if (ret) {
SDE_ERROR("failed to register irq %d\n", ret);
- return;
+ return ret;
}
wb_enc->kickoff_count++;
@@ -955,6 +956,7 @@
wb_enc->start_time = ktime_get();
SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), wb_enc->kickoff_count);
+ return 0;
}
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index b8b0967..d49986a 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -15,6 +15,7 @@
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/soc/qcom/llcc-qcom.h>
+#include <linux/pm_qos.h>
#include "sde_hw_mdss.h"
#include "sde_hw_catalog.h"
@@ -115,6 +116,8 @@
"NV12/5/1/1.25 AB24/5/1/1.25 XB24/5/1/1.25"
#define DEFAULT_MAX_PER_PIPE_BW 2400000
#define DEFAULT_AMORTIZABLE_THRESHOLD 25
+#define DEFAULT_CPU_MASK 0
+#define DEFAULT_CPU_DMA_LATENCY PM_QOS_DEFAULT_VALUE
/*************************************************************
* DTSI PROPERTY INDEX
@@ -176,6 +179,8 @@
PERF_QOS_LUT_NRT,
PERF_QOS_LUT_CWB,
PERF_CDP_SETTING,
+ PERF_CPU_MASK,
+ PERF_CPU_DMA_LATENCY,
PERF_PROP_MAX,
};
@@ -448,6 +453,9 @@
{PERF_CDP_SETTING, "qcom,sde-cdp-setting", false,
PROP_TYPE_U32_ARRAY},
+ {PERF_CPU_MASK, "qcom,sde-qos-cpu-mask", false, PROP_TYPE_U32},
+ {PERF_CPU_DMA_LATENCY, "qcom,sde-qos-cpu-dma-latency", false,
+ PROP_TYPE_U32},
};
static struct sde_prop_type sspp_prop[] = {
@@ -3083,6 +3091,15 @@
cfg->has_cdp = true;
}
+ cfg->perf.cpu_mask =
+ prop_exists[PERF_CPU_MASK] ?
+ PROP_VALUE_ACCESS(prop_value, PERF_CPU_MASK, 0) :
+ DEFAULT_CPU_MASK;
+ cfg->perf.cpu_dma_latency =
+ prop_exists[PERF_CPU_DMA_LATENCY] ?
+ PROP_VALUE_ACCESS(prop_value, PERF_CPU_DMA_LATENCY, 0) :
+ DEFAULT_CPU_DMA_LATENCY;
+
freeprop:
kfree(prop_value);
end:
@@ -3147,6 +3164,12 @@
goto end;
}
+ if (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_300) ||
+ IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_301) ||
+ IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_400) ||
+ IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_401))
+ sde_cfg->has_hdr = true;
+
index = sde_copy_formats(sde_cfg->dma_formats, dma_list_size,
0, plane_formats, ARRAY_SIZE(plane_formats));
index += sde_copy_formats(sde_cfg->dma_formats, dma_list_size,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index d56ad06..06aad04 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -866,6 +866,8 @@
* @sfe_lut_tbl: LUT tables for safe signals
* @qos_lut_tbl: LUT tables for QoS signals
* @cdp_cfg cdp use case configurations
+ * @cpu_mask: pm_qos cpu mask value
+ * @cpu_dma_latency: pm_qos cpu dma latency value
*/
struct sde_perf_cfg {
u32 max_bw_low;
@@ -890,6 +892,8 @@
struct sde_qos_lut_tbl sfe_lut_tbl[SDE_QOS_LUT_USAGE_MAX];
struct sde_qos_lut_tbl qos_lut_tbl[SDE_QOS_LUT_USAGE_MAX];
struct sde_perf_cdp_cfg cdp_cfg[SDE_PERF_CDP_USAGE_MAX];
+ u32 cpu_mask;
+ u32 cpu_dma_latency;
};
/**
@@ -913,6 +917,7 @@
* @has_sbuf indicate if stream buffer is available
* @sbuf_headroom stream buffer headroom in lines
* @has_idle_pc indicate if idle power collapse feature is supported
+ * @has_hdr HDR feature support
* @dma_formats Supported formats for dma pipe
* @cursor_formats Supported formats for cursor pipe
* @vig_formats Supported formats for vig pipe
@@ -941,6 +946,7 @@
u32 vbif_qos_nlvl;
u32 ts_prefill_rev;
+ bool has_hdr;
u32 mdss_count;
struct sde_mdss_base_cfg mdss[MAX_BLOCKS];
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
index 88f821d..ea4ca4f 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
@@ -30,6 +30,7 @@
#define CTL_START 0x01C
#define CTL_PREPARE 0x0d0
#define CTL_SW_RESET 0x030
+#define CTL_SW_RESET_OVERRIDE 0x060
#define CTL_LAYER_EXTN_OFFSET 0x40
#define CTL_ROT_TOP 0x0C0
#define CTL_ROT_FLUSH 0x0C4
@@ -345,7 +346,7 @@
{
struct sde_hw_blk_reg_map *c = &ctx->hw;
- pr_debug("issuing hw ctl reset for ctl:%d\n", ctx->idx);
+ pr_debug("issuing hw ctl reset for ctl:%d\n", ctx->idx - CTL_0);
SDE_REG_WRITE(c, CTL_SW_RESET, 0x1);
if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_US))
return -EINVAL;
@@ -353,6 +354,15 @@
return 0;
}
+static void sde_hw_ctl_hard_reset(struct sde_hw_ctl *ctx, bool enable)
+{
+ struct sde_hw_blk_reg_map *c = &ctx->hw;
+
+ pr_debug("hw ctl hard reset for ctl:%d, %d\n",
+ ctx->idx - CTL_0, enable);
+ SDE_REG_WRITE(c, CTL_SW_RESET_OVERRIDE, enable);
+}
+
static int sde_hw_ctl_wait_reset_status(struct sde_hw_ctl *ctx)
{
struct sde_hw_blk_reg_map *c = &ctx->hw;
@@ -586,6 +596,7 @@
ops->trigger_pending = sde_hw_ctl_trigger_pending;
ops->setup_intf_cfg = sde_hw_ctl_intf_cfg;
ops->reset = sde_hw_ctl_reset_control;
+ ops->hard_reset = sde_hw_ctl_hard_reset;
ops->wait_reset_status = sde_hw_ctl_wait_reset_status;
ops->clear_all_blendstages = sde_hw_ctl_clear_all_blendstages;
ops->setup_blendstage = sde_hw_ctl_setup_blendstage;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
index bad80f0..fe4e194 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
@@ -152,6 +152,13 @@
int (*reset)(struct sde_hw_ctl *c);
+ /**
+ * hard_reset - force reset on ctl_path
+ * @ctx : ctl path ctx pointer
+ * @enable : whether to enable/disable hard reset
+ */
+ void (*hard_reset)(struct sde_hw_ctl *c, bool enable);
+
/*
* wait_reset_status - checks ctl reset status
* @ctx : ctl path ctx pointer
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_rot.c b/drivers/gpu/drm/msm/sde/sde_hw_rot.c
index c5af3a9..8d386a8 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_rot.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_rot.c
@@ -588,6 +588,10 @@
cmd_type = SDE_ROTATOR_INLINE_CMD_CLEANUP;
priv_handle = data->priv_handle;
break;
+ case SDE_HW_ROT_CMD_RESET:
+ cmd_type = SDE_ROTATOR_INLINE_CMD_ABORT;
+ priv_handle = data->priv_handle;
+ break;
default:
SDE_ERROR("invalid hw rotator command %d\n", hw_cmd);
return -EINVAL;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_rot.h b/drivers/gpu/drm/msm/sde/sde_hw_rot.h
index 1237858..ea88d05 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_rot.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_rot.h
@@ -28,12 +28,14 @@
* @SDE_HW_ROT_CMD_COMMIT: commit/execute rotator command
* @SDE_HW_ROT_CMD_START: mdp is ready to start
* @SDE_HW_ROT_CMD_CLEANUP: cleanup rotator command after it is done
+ * @SDE_HW_ROT_CMD_RESET: request rotator h/w reset
*/
enum sde_hw_rot_cmd_type {
SDE_HW_ROT_CMD_VALIDATE,
SDE_HW_ROT_CMD_COMMIT,
SDE_HW_ROT_CMD_START,
SDE_HW_ROT_CMD_CLEANUP,
+ SDE_HW_ROT_CMD_RESET,
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index c2fe10a..9b25450 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -498,6 +498,9 @@
struct msm_drm_private *priv;
struct drm_device *dev;
struct drm_encoder *encoder;
+ struct drm_crtc *crtc;
+ struct drm_crtc_state *crtc_state;
+ int i;
if (!kms)
return;
@@ -510,9 +513,15 @@
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true);
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
- if (encoder->crtc != NULL)
+ for_each_crtc_in_state(state, crtc, crtc_state, i) {
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+ head) {
+ if (encoder->crtc != crtc)
+ continue;
+
sde_encoder_prepare_commit(encoder);
+ }
+ }
/*
* NOTE: for secure use cases we want to apply the new HW
@@ -532,7 +541,7 @@
for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
if (crtc->state->active) {
SDE_EVT32(DRMID(crtc));
- sde_crtc_commit_kickoff(crtc);
+ sde_crtc_commit_kickoff(crtc, old_crtc_state);
}
}
}
@@ -2229,6 +2238,41 @@
return ret;
}
+static void _sde_kms_pm_qos_add_request(struct sde_kms *sde_kms)
+{
+ struct pm_qos_request *req;
+ u32 cpu_mask;
+ u32 cpu_dma_latency;
+ int cpu;
+
+ if (!sde_kms || !sde_kms->catalog)
+ return;
+
+ cpu_mask = sde_kms->catalog->perf.cpu_mask;
+ cpu_dma_latency = sde_kms->catalog->perf.cpu_dma_latency;
+ if (!cpu_mask)
+ return;
+
+ req = &sde_kms->pm_qos_cpu_req;
+ req->type = PM_QOS_REQ_AFFINE_CORES;
+ cpumask_empty(&req->cpus_affine);
+ for_each_possible_cpu(cpu) {
+ if ((1 << cpu) & cpu_mask)
+ cpumask_set_cpu(cpu, &req->cpus_affine);
+ }
+ pm_qos_add_request(req, PM_QOS_CPU_DMA_LATENCY, cpu_dma_latency);
+
+ SDE_EVT32_VERBOSE(cpu_mask, cpu_dma_latency);
+}
+
+static void _sde_kms_pm_qos_remove_request(struct sde_kms *sde_kms)
+{
+ if (!sde_kms || !sde_kms->catalog || !sde_kms->catalog->perf.cpu_mask)
+ return;
+
+ pm_qos_remove_request(&sde_kms->pm_qos_cpu_req);
+}
+
static void sde_kms_handle_power_event(u32 event_type, void *usr)
{
struct sde_kms *sde_kms = usr;
@@ -2244,7 +2288,9 @@
if (event_type == SDE_POWER_EVENT_POST_ENABLE) {
sde_irq_update(msm_kms, true);
sde_vbif_init_memtypes(sde_kms);
+ _sde_kms_pm_qos_add_request(sde_kms);
} else if (event_type == SDE_POWER_EVENT_PRE_DISABLE) {
+ _sde_kms_pm_qos_remove_request(sde_kms);
sde_irq_update(msm_kms, false);
}
}
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index f047305..f5613ed 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -21,6 +21,7 @@
#include <linux/msm_ion.h>
#include <linux/pm_domain.h>
+#include <linux/pm_qos.h>
#include "msm_drv.h"
#include "msm_kms.h"
@@ -182,6 +183,7 @@
struct msm_gem_address_space *aspace[MSM_SMMU_DOMAIN_MAX];
struct sde_power_client *core_client;
+ struct pm_qos_request pm_qos_cpu_req;
struct ion_client *iclient;
struct sde_power_event *power_event;
@@ -366,7 +368,8 @@
* @S: Pointer to sde_kms_info structure
* Returns: Pointer to byte data
*/
-#define SDE_KMS_INFO_DATA(S) ((S) ? ((struct sde_kms_info *)(S))->data : 0)
+#define SDE_KMS_INFO_DATA(S) ((S) ? ((struct sde_kms_info *)(S))->data \
+ : NULL)
/**
* SDE_KMS_INFO_DATALEN - Macro for accessing sde_kms_info data length
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 10e52bf..4eeb653 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -38,14 +38,6 @@
#include "sde_color_processing.h"
#include "sde_hw_rot.h"
-static bool suspend_blank = true;
-module_param(suspend_blank, bool, 0400);
-MODULE_PARM_DESC(suspend_blank,
- "If set, active planes will force their outputs to black,\n"
- "by temporarily enabling the color fill, when recovering\n"
- "from a system resume instead of attempting to display the\n"
- "last provided frame buffer.");
-
#define SDE_DEBUG_PLANE(pl, fmt, ...) SDE_DEBUG("plane%d " fmt,\
(pl) ? (pl)->base.base.id : -1, ##__VA_ARGS__)
@@ -103,6 +95,7 @@
* @sbuf_mode: force stream buffer mode if set
* @sbuf_writeback: force stream buffer writeback if set
* @revalidate: force revalidation of all the plane properties
+ * @xin_halt_forced_clk: whether or not clocks were forced on for xin halt
* @blob_rot_caps: Pointer to rotator capability blob
*/
struct sde_plane {
@@ -128,6 +121,7 @@
u32 sbuf_mode;
u32 sbuf_writeback;
bool revalidate;
+ bool xin_halt_forced_clk;
struct sde_csc_cfg csc_cfg;
struct sde_csc_cfg *csc_usr_ptr;
@@ -2489,6 +2483,111 @@
msm_framebuffer_cleanup(state->fb, pstate->aspace);
}
+static bool _sde_plane_halt_requests(struct drm_plane *plane,
+ uint32_t xin_id, bool halt_forced_clk, bool enable)
+{
+ struct sde_plane *psde;
+ struct msm_drm_private *priv;
+ struct sde_vbif_set_xin_halt_params halt_params;
+
+ if (!plane || !plane->dev) {
+ SDE_ERROR("invalid arguments\n");
+ return false;
+ }
+
+ psde = to_sde_plane(plane);
+ if (!psde->pipe_hw || !psde->pipe_hw->cap) {
+ SDE_ERROR("invalid pipe reference\n");
+ return false;
+ }
+
+ priv = plane->dev->dev_private;
+ if (!priv || !priv->kms) {
+ SDE_ERROR("invalid KMS reference\n");
+ return false;
+ }
+
+ memset(&halt_params, 0, sizeof(halt_params));
+ halt_params.vbif_idx = VBIF_RT;
+ halt_params.xin_id = xin_id;
+ halt_params.clk_ctrl = psde->pipe_hw->cap->clk_ctrl;
+ halt_params.forced_on = halt_forced_clk;
+ halt_params.enable = enable;
+
+ return sde_vbif_set_xin_halt(to_sde_kms(priv->kms), &halt_params);
+}
+
+void sde_plane_halt_requests(struct drm_plane *plane, bool enable)
+{
+ struct sde_plane *psde;
+
+ if (!plane) {
+ SDE_ERROR("invalid plane\n");
+ return;
+ }
+
+ psde = to_sde_plane(plane);
+ if (!psde->pipe_hw || !psde->pipe_hw->cap) {
+ SDE_ERROR("invalid pipe reference\n");
+ return;
+ }
+
+ SDE_EVT32(DRMID(plane), psde->xin_halt_forced_clk, enable);
+
+ psde->xin_halt_forced_clk =
+ _sde_plane_halt_requests(plane, psde->pipe_hw->cap->xin_id,
+ psde->xin_halt_forced_clk, enable);
+}
+
+int sde_plane_reset_rot(struct drm_plane *plane, struct drm_plane_state *state)
+{
+ struct sde_plane *psde;
+ struct sde_plane_state *pstate;
+ struct sde_plane_rot_state *rstate;
+ bool halt_ret[MAX_BLOCKS] = {false};
+ signed int i, count;
+
+ if (!plane || !state) {
+ SDE_ERROR("invalid plane\n");
+ return -EINVAL;
+ }
+
+ psde = to_sde_plane(plane);
+ pstate = to_sde_plane_state(state);
+ rstate = &pstate->rot;
+
+ /* do nothing if not master rotator plane */
+ if (!rstate->out_sbuf || !rstate->rot_hw ||
+ !rstate->rot_hw->caps || (rstate->out_xpos != 0))
+ return 0;
+
+ count = (signed int)rstate->rot_hw->caps->xin_count;
+ if (count > ARRAY_SIZE(halt_ret))
+ count = ARRAY_SIZE(halt_ret);
+
+ SDE_DEBUG_PLANE(psde, "issuing reset for rotator\n");
+ SDE_EVT32(DRMID(plane), count);
+
+ for (i = 0; i < count; i++) {
+ const struct sde_rot_vbif_cfg *cfg =
+ &rstate->rot_hw->caps->vbif_cfg[i];
+
+ halt_ret[i] = _sde_plane_halt_requests(plane, cfg->xin_id,
+ false, true);
+ }
+
+ sde_plane_rot_submit_command(plane, state, SDE_HW_ROT_CMD_RESET);
+
+ for (i = count - 1; i >= 0; --i) {
+ const struct sde_rot_vbif_cfg *cfg =
+ &rstate->rot_hw->caps->vbif_cfg[i];
+
+ _sde_plane_halt_requests(plane, cfg->xin_id,
+ halt_ret[i], false);
+ }
+ return 0;
+}
+
int sde_plane_kickoff_rot(struct drm_plane *plane)
{
struct sde_plane_state *pstate;
@@ -3392,10 +3491,6 @@
else if (psde->pipe_hw && psde->csc_ptr && psde->pipe_hw->ops.setup_csc)
psde->pipe_hw->ops.setup_csc(psde->pipe_hw, psde->csc_ptr);
- /* force black color fill during suspend */
- if (sde_kms_is_suspend_state(plane->dev) && suspend_blank)
- _sde_plane_color_fill(psde, 0x0, 0x0);
-
/* flag h/w flush complete */
if (plane->state)
pstate->pending = false;
@@ -4366,15 +4461,6 @@
return ret;
}
-static int sde_plane_set_property(struct drm_plane *plane,
- struct drm_property *property, uint64_t val)
-{
- SDE_DEBUG("\n");
-
- return sde_plane_atomic_set_property(plane,
- plane->state, property, val);
-}
-
static int sde_plane_atomic_get_property(struct drm_plane *plane,
const struct drm_plane_state *state,
struct drm_property *property, uint64_t *val)
@@ -4778,7 +4864,7 @@
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = sde_plane_destroy,
- .set_property = sde_plane_set_property,
+ .set_property = drm_atomic_helper_plane_set_property,
.atomic_set_property = sde_plane_atomic_set_property,
.atomic_get_property = sde_plane_atomic_get_property,
.reset = sde_plane_reset,
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.h b/drivers/gpu/drm/msm/sde/sde_plane.h
index e1b07c2..5c1fff1 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.h
+++ b/drivers/gpu/drm/msm/sde/sde_plane.h
@@ -228,6 +228,23 @@
void sde_plane_flush(struct drm_plane *plane);
/**
+ * sde_plane_halt_requests - control halting of vbif transactions for this plane
+ * This function isn't thread safe. Plane halt enable/disable requests
+ * should always be made from the same commit cycle.
+ * @plane: Pointer to drm plane structure
+ * @enable: Whether to enable/disable halting of vbif transactions
+ */
+void sde_plane_halt_requests(struct drm_plane *plane, bool enable);
+
+/**
+ * sde_plane_reset_rot - reset rotator operations before commit kickoff
+ * @plane: Pointer to drm plane structure
+ * @state: Pointer to plane state associated with reset request
+ * Returns: Zero on success
+ */
+int sde_plane_reset_rot(struct drm_plane *plane, struct drm_plane_state *state);
+
+/**
* sde_plane_kickoff_rot - final plane rotator operations before commit kickoff
* @plane: Pointer to drm plane structure
* Returns: Zero on success
diff --git a/drivers/gpu/drm/msm/sde/sde_vbif.c b/drivers/gpu/drm/msm/sde/sde_vbif.c
index 2cdc2f3..522f7f9 100644
--- a/drivers/gpu/drm/msm/sde/sde_vbif.c
+++ b/drivers/gpu/drm/msm/sde/sde_vbif.c
@@ -230,13 +230,15 @@
for (i = 0; i < ARRAY_SIZE(sde_kms->hw_vbif); i++) {
if (sde_kms->hw_vbif[i] &&
- sde_kms->hw_vbif[i]->idx == params->vbif_idx)
+ sde_kms->hw_vbif[i]->idx == params->vbif_idx) {
vbif = sde_kms->hw_vbif[i];
+ break;
+ }
}
if (!vbif || !mdp) {
SDE_DEBUG("invalid arguments vbif %d mdp %d\n",
- vbif != 0, mdp != 0);
+ vbif != NULL, mdp != NULL);
return;
}
@@ -275,6 +277,58 @@
return;
}
+bool sde_vbif_set_xin_halt(struct sde_kms *sde_kms,
+ struct sde_vbif_set_xin_halt_params *params)
+{
+ struct sde_hw_vbif *vbif = NULL;
+ struct sde_hw_mdp *mdp;
+ bool forced_on = false;
+ int ret, i;
+
+ if (!sde_kms || !params) {
+ SDE_ERROR("invalid arguments\n");
+ return false;
+ }
+ mdp = sde_kms->hw_mdp;
+
+ for (i = 0; i < ARRAY_SIZE(sde_kms->hw_vbif); i++) {
+ if (sde_kms->hw_vbif[i] &&
+ sde_kms->hw_vbif[i]->idx == params->vbif_idx) {
+ vbif = sde_kms->hw_vbif[i];
+ break;
+ }
+ }
+
+ if (!vbif || !mdp) {
+ SDE_DEBUG("invalid arguments vbif %d mdp %d\n",
+ vbif != NULL, mdp != NULL);
+ return false;
+ }
+
+ if (!mdp->ops.setup_clk_force_ctrl ||
+ !vbif->ops.set_halt_ctrl)
+ return false;
+
+ if (params->enable) {
+ forced_on = mdp->ops.setup_clk_force_ctrl(mdp,
+ params->clk_ctrl, true);
+
+ vbif->ops.set_halt_ctrl(vbif, params->xin_id, true);
+
+ ret = _sde_vbif_wait_for_xin_halt(vbif, params->xin_id);
+ if (ret)
+ SDE_EVT32(vbif->idx, params->xin_id, SDE_EVTLOG_ERROR);
+ } else {
+ vbif->ops.set_halt_ctrl(vbif, params->xin_id, false);
+
+ if (params->forced_on)
+ mdp->ops.setup_clk_force_ctrl(mdp,
+ params->clk_ctrl, false);
+ }
+
+ return forced_on;
+}
+
void sde_vbif_set_qos_remap(struct sde_kms *sde_kms,
struct sde_vbif_set_qos_params *params)
{
diff --git a/drivers/gpu/drm/msm/sde/sde_vbif.h b/drivers/gpu/drm/msm/sde/sde_vbif.h
index 30cc416..0edc1a6 100644
--- a/drivers/gpu/drm/msm/sde/sde_vbif.h
+++ b/drivers/gpu/drm/msm/sde/sde_vbif.h
@@ -35,6 +35,23 @@
};
/**
+ * struct sde_vbif_set_xin_halt_params - xin halt parameters
+ * @vbif_idx: vbif identifier
+ * @xin_id: client interface identifier
+ * @clk_ctrl: clock control identifier of the xin
+ * @forced_on: whether or not previous call to xin halt forced the clocks on,
+ * only applicable to xin halt disable calls
+ * @enable: whether to enable/disable xin halts
+ */
+struct sde_vbif_set_xin_halt_params {
+ u32 vbif_idx;
+ u32 xin_id;
+ u32 clk_ctrl;
+ bool forced_on;
+ bool enable;
+};
+
+/**
* struct sde_vbif_set_qos_params - QoS remapper parameter
* @vbif_idx: vbif identifier
* @xin_id: client interface identifier
@@ -59,6 +76,16 @@
struct sde_vbif_set_ot_params *params);
/**
+ * sde_vbif_set_xin_halt - halt one of the xin ports
+ * This function isn't thread safe.
+ * @sde_kms: SDE handler
+ * @params: Pointer to halt configuration parameters
+ * Returns: Whether or not VBIF clocks were forced on
+ */
+bool sde_vbif_set_xin_halt(struct sde_kms *sde_kms,
+ struct sde_vbif_set_xin_halt_params *params);
+
+/**
* sde_vbif_set_qos_remap - set QoS priority level remap
* @sde_kms: SDE handler
* @params: Pointer to QoS configuration parameters
diff --git a/drivers/gpu/drm/msm/sde/sde_wb.c b/drivers/gpu/drm/msm/sde/sde_wb.c
index 8076e0c..a4c8518 100644
--- a/drivers/gpu/drm/msm/sde/sde_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_wb.c
@@ -353,8 +353,7 @@
}
int sde_wb_connector_post_init(struct drm_connector *connector,
- void *info,
- void *display)
+ void *info, void *display, struct msm_mode_info *mode_info)
{
struct sde_connector *c_conn;
struct sde_wb_device *wb_dev = display;
diff --git a/drivers/gpu/drm/msm/sde/sde_wb.h b/drivers/gpu/drm/msm/sde/sde_wb.h
index c3f9e06..5e31664 100644
--- a/drivers/gpu/drm/msm/sde/sde_wb.h
+++ b/drivers/gpu/drm/msm/sde/sde_wb.h
@@ -133,11 +133,13 @@
* @connector: Pointer to drm connector structure
* @info: Pointer to connector info
* @display: Pointer to private display structure
+ * @mode_info: Pointer to the mode info structure
* Returns: Zero on success
*/
int sde_wb_connector_post_init(struct drm_connector *connector,
void *info,
- void *display);
+ void *display,
+ struct msm_mode_info *mode_info);
/**
* sde_wb_connector_detect - perform writeback connection status detection
@@ -280,7 +282,8 @@
static inline
int sde_wb_connector_post_init(struct drm_connector *connector,
void *info,
- void *display)
+ void *display,
+ struct msm_mode_info *mode_info)
{
return 0;
}
diff --git a/drivers/gpu/drm/msm/sde_edid_parser.c b/drivers/gpu/drm/msm/sde_edid_parser.c
index 791a6ca..c2ba3b97 100644
--- a/drivers/gpu/drm/msm/sde_edid_parser.c
+++ b/drivers/gpu/drm/msm/sde_edid_parser.c
@@ -566,45 +566,6 @@
return rc;
}
-u32 sde_get_sink_bpc(void *input)
-{
- struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(input);
- struct edid *edid = edid_ctrl->edid;
-
- if (!edid) {
- SDE_ERROR("invalid edid input\n");
- return 0;
- }
-
- if ((edid->revision < 3) || !(edid->input & DRM_EDID_INPUT_DIGITAL))
- return 0;
-
- if (edid->revision < 4) {
- if (edid->input & DRM_EDID_DIGITAL_TYPE_DVI)
- return 8;
- else
- return 0;
- }
-
- switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) {
- case DRM_EDID_DIGITAL_DEPTH_6:
- return 6;
- case DRM_EDID_DIGITAL_DEPTH_8:
- return 8;
- case DRM_EDID_DIGITAL_DEPTH_10:
- return 10;
- case DRM_EDID_DIGITAL_DEPTH_12:
- return 12;
- case DRM_EDID_DIGITAL_DEPTH_14:
- return 14;
- case DRM_EDID_DIGITAL_DEPTH_16:
- return 16;
- case DRM_EDID_DIGITAL_DEPTH_UNDEF:
- default:
- return 0;
- }
-}
-
u8 sde_get_edid_checksum(void *input)
{
struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(input);
diff --git a/drivers/gpu/drm/msm/sde_edid_parser.h b/drivers/gpu/drm/msm/sde_edid_parser.h
index 07bdf50..fd56116 100644
--- a/drivers/gpu/drm/msm/sde_edid_parser.h
+++ b/drivers/gpu/drm/msm/sde_edid_parser.h
@@ -138,14 +138,6 @@
bool sde_detect_hdmi_monitor(void *edid_ctrl);
/**
- * sde_get_sink_bpc() - return the bpc of sink device.
- * @edid_ctrl: Handle to the edid_ctrl structure.
- *
- * Return: bpc supported by the sink.
- */
-u32 sde_get_sink_bpc(void *edid_ctrl);
-
-/**
* sde_get_edid_checksum() - return the checksum of last block of EDID.
* @input: Handle to the edid_ctrl structure.
*
diff --git a/drivers/gpu/drm/msm/sde_power_handle.h b/drivers/gpu/drm/msm/sde_power_handle.h
index 18777fd..6e00184 100644
--- a/drivers/gpu/drm/msm/sde_power_handle.h
+++ b/drivers/gpu/drm/msm/sde_power_handle.h
@@ -18,7 +18,7 @@
#define SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA 0
#define SDE_POWER_HANDLE_DISABLE_BUS_AB_QUOTA 0
-#define SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA 1600000000
+#define SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA 400000000
#define SDE_POWER_HANDLE_DISABLE_BUS_IB_QUOTA 0
#include <linux/sde_io_util.h>
diff --git a/drivers/gpu/drm/msm/sde_rsc_hw.c b/drivers/gpu/drm/msm/sde_rsc_hw.c
index e957779..654a2ad 100644
--- a/drivers/gpu/drm/msm/sde_rsc_hw.c
+++ b/drivers/gpu/drm/msm/sde_rsc_hw.c
@@ -187,34 +187,34 @@
0x39e038a8, rsc->debug_mode);
dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x10,
0x888babec, rsc->debug_mode);
- dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x14,
- 0xa806a020, rsc->debug_mode);
/* Mode - 2 sequence */
+ dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x14,
+ 0xaaa8a020, rsc->debug_mode);
dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x18,
- 0xa138ebaa, rsc->debug_mode);
+ 0xe1a138eb, rsc->debug_mode);
dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x1c,
- 0xaca581e1, rsc->debug_mode);
+ 0xe0aca581, rsc->debug_mode);
dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x20,
- 0xe2a2ede0, rsc->debug_mode);
+ 0x82e2a2ed, rsc->debug_mode);
dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x24,
- 0xea8a3982, rsc->debug_mode);
+ 0x8cea8a39, rsc->debug_mode);
dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x28,
- 0xa920888c, rsc->debug_mode);
+ 0xe9a92088, rsc->debug_mode);
- /* tcs sleep sequence */
+ /* tcs sleep & wake sequence */
dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x2c,
- 0x89e6a6e9, rsc->debug_mode);
+ 0x2089e6a6, rsc->debug_mode);
dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x30,
- 0xa7e9a920, rsc->debug_mode);
+ 0xe7a7e9a9, rsc->debug_mode);
dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x34,
- 0x002089e7, rsc->debug_mode);
+ 0x00002089, rsc->debug_mode);
/* branch address */
dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_CFG_BR_ADDR_0_DRV0,
- 0x2b, rsc->debug_mode);
+ 0x2a, rsc->debug_mode);
dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_CFG_BR_ADDR_1_DRV0,
- 0x31, rsc->debug_mode);
+ 0x30, rsc->debug_mode);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
index 6584d50..133f896 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
@@ -1129,7 +1129,7 @@
if (trap & 0x00000008) {
u32 stat = nvkm_rd32(device, 0x408030);
- nvkm_snprintbf(error, sizeof(error), gf100_m2mf_error,
+ nvkm_snprintbf(error, sizeof(error), gf100_ccache_error,
stat & 0x3fffffff);
nvkm_error(subdev, "CCACHE %08x [%s]\n", stat, error);
nvkm_wr32(device, 0x408030, 0xc0000000);
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 3b21ca5..82b0112 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -1674,7 +1674,7 @@
radeon_agp_suspend(rdev);
pci_save_state(dev->pdev);
- if (freeze && rdev->family >= CHIP_CEDAR) {
+ if (freeze && rdev->family >= CHIP_CEDAR && !(rdev->flags & RADEON_IS_IGP)) {
rdev->asic->asic_reset(rdev, true);
pci_restore_state(dev->pdev);
} else if (suspend) {
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index c3b2186..1feec34 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -47,6 +47,13 @@
sun4i_tcon_enable_vblank(tcon, false);
}
+static void sun4i_drv_lastclose(struct drm_device *dev)
+{
+ struct sun4i_drv *drv = dev->dev_private;
+
+ drm_fbdev_cma_restore_mode(drv->fbdev);
+}
+
static const struct file_operations sun4i_drv_fops = {
.owner = THIS_MODULE,
.open = drm_open,
@@ -65,6 +72,7 @@
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC,
/* Generic Operations */
+ .lastclose = sun4i_drv_lastclose,
.fops = &sun4i_drv_fops,
.name = "sun4i-drm",
.desc = "Allwinner sun4i Display Engine",
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index 8cbc75e..92f51bc 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -73,14 +73,14 @@
{A6XX_RBBM_CLOCK_CNTL2_SP1, 0x02022220},
{A6XX_RBBM_CLOCK_CNTL2_SP2, 0x02022220},
{A6XX_RBBM_CLOCK_CNTL2_SP3, 0x02022220},
- {A6XX_RBBM_CLOCK_DELAY_SP0, 0x0000F3CF},
- {A6XX_RBBM_CLOCK_DELAY_SP1, 0x0000F3CF},
- {A6XX_RBBM_CLOCK_DELAY_SP2, 0x0000F3CF},
- {A6XX_RBBM_CLOCK_DELAY_SP3, 0x0000F3CF},
- {A6XX_RBBM_CLOCK_HYST_SP0, 0x00000080},
- {A6XX_RBBM_CLOCK_HYST_SP1, 0x00000080},
- {A6XX_RBBM_CLOCK_HYST_SP2, 0x00000080},
- {A6XX_RBBM_CLOCK_HYST_SP3, 0x00000080},
+ {A6XX_RBBM_CLOCK_DELAY_SP0, 0x00000080},
+ {A6XX_RBBM_CLOCK_DELAY_SP1, 0x00000080},
+ {A6XX_RBBM_CLOCK_DELAY_SP2, 0x00000080},
+ {A6XX_RBBM_CLOCK_DELAY_SP3, 0x00000080},
+ {A6XX_RBBM_CLOCK_HYST_SP0, 0x0000F3CF},
+ {A6XX_RBBM_CLOCK_HYST_SP1, 0x0000F3CF},
+ {A6XX_RBBM_CLOCK_HYST_SP2, 0x0000F3CF},
+ {A6XX_RBBM_CLOCK_HYST_SP3, 0x0000F3CF},
{A6XX_RBBM_CLOCK_CNTL_TP0, 0x02222222},
{A6XX_RBBM_CLOCK_CNTL_TP1, 0x02222222},
{A6XX_RBBM_CLOCK_CNTL_TP2, 0x02222222},
@@ -105,10 +105,10 @@
{A6XX_RBBM_CLOCK_HYST2_TP1, 0x77777777},
{A6XX_RBBM_CLOCK_HYST2_TP2, 0x77777777},
{A6XX_RBBM_CLOCK_HYST2_TP3, 0x77777777},
- {A6XX_RBBM_CLOCK_HYST3_TP0, 0x07777777},
- {A6XX_RBBM_CLOCK_HYST3_TP1, 0x07777777},
- {A6XX_RBBM_CLOCK_HYST3_TP2, 0x07777777},
- {A6XX_RBBM_CLOCK_HYST3_TP3, 0x07777777},
+ {A6XX_RBBM_CLOCK_HYST3_TP0, 0x77777777},
+ {A6XX_RBBM_CLOCK_HYST3_TP1, 0x77777777},
+ {A6XX_RBBM_CLOCK_HYST3_TP2, 0x77777777},
+ {A6XX_RBBM_CLOCK_HYST3_TP3, 0x77777777},
{A6XX_RBBM_CLOCK_HYST4_TP0, 0x00077777},
{A6XX_RBBM_CLOCK_HYST4_TP1, 0x00077777},
{A6XX_RBBM_CLOCK_HYST4_TP2, 0x00077777},
@@ -143,20 +143,20 @@
{A6XX_RBBM_CLOCK_CNTL2_RB1, 0x00002222},
{A6XX_RBBM_CLOCK_CNTL2_RB2, 0x00002222},
{A6XX_RBBM_CLOCK_CNTL2_RB3, 0x00002222},
- {A6XX_RBBM_CLOCK_CNTL_CCU0, 0x00000000},
- {A6XX_RBBM_CLOCK_CNTL_CCU1, 0x00000000},
- {A6XX_RBBM_CLOCK_CNTL_CCU2, 0x00000000},
- {A6XX_RBBM_CLOCK_CNTL_CCU3, 0x00000000},
+ {A6XX_RBBM_CLOCK_CNTL_CCU0, 0x00002220},
+ {A6XX_RBBM_CLOCK_CNTL_CCU1, 0x00002220},
+ {A6XX_RBBM_CLOCK_CNTL_CCU2, 0x00002220},
+ {A6XX_RBBM_CLOCK_CNTL_CCU3, 0x00002220},
{A6XX_RBBM_CLOCK_HYST_RB_CCU0, 0x00040F00},
{A6XX_RBBM_CLOCK_HYST_RB_CCU1, 0x00040F00},
{A6XX_RBBM_CLOCK_HYST_RB_CCU2, 0x00040F00},
{A6XX_RBBM_CLOCK_HYST_RB_CCU3, 0x00040F00},
- {A6XX_RBBM_CLOCK_CNTL_RAC, 0x00022022},
- {A6XX_RBBM_CLOCK_CNTL2_RAC, 0x00005550},
- {A6XX_RBBM_CLOCK_DELAY_RAC, 0x00010011},
+ {A6XX_RBBM_CLOCK_CNTL_RAC, 0x05022022},
+ {A6XX_RBBM_CLOCK_CNTL2_RAC, 0x00005555},
+ {A6XX_RBBM_CLOCK_DELAY_RAC, 0x00000011},
{A6XX_RBBM_CLOCK_HYST_RAC, 0x00445044},
- {A6XX_RBBM_CLOCK_CNTL_TSE_RAS_RBBM, 0x04222220},
- {A6XX_RBBM_CLOCK_MODE_GPC, 0x00202222},
+ {A6XX_RBBM_CLOCK_CNTL_TSE_RAS_RBBM, 0x04222222},
+ {A6XX_RBBM_CLOCK_MODE_GPC, 0x02222222},
{A6XX_RBBM_CLOCK_MODE_VFD, 0x00002222},
{A6XX_RBBM_CLOCK_HYST_TSE_RAS_RBBM, 0x00000000},
{A6XX_RBBM_CLOCK_HYST_GPC, 0x04104004},
@@ -765,7 +765,7 @@
*cmds++ = lower_32_bits(gpuaddr);
*cmds++ = upper_32_bits(gpuaddr);
/* Size is in dwords */
- *cmds++ = 0;
+ *cmds++ = sizeof(a6xx_pwrup_reglist) >> 2;
}
/* Pad rest of the cmds with 0's */
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index d984c6d..b81be8f 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -505,6 +505,8 @@
kgsl_drawobj_destroy(list[i]);
}
+ debugfs_remove_recursive(drawctxt->debug_root);
+
/*
* internal_timestamp is set in adreno_ringbuffer_addcmds,
* which holds the device mutex.
@@ -562,8 +564,6 @@
mutex_unlock(&device->mutex);
- debugfs_remove_recursive(drawctxt->debug_root);
-
/* wake threads waiting to submit commands from this context */
wake_up_all(&drawctxt->waiting);
wake_up_all(&drawctxt->wq);
diff --git a/drivers/gpu/msm/adreno_perfcounter.c b/drivers/gpu/msm/adreno_perfcounter.c
index 9ea8069..4233d38 100644
--- a/drivers/gpu/msm/adreno_perfcounter.c
+++ b/drivers/gpu/msm/adreno_perfcounter.c
@@ -171,17 +171,23 @@
*/
inline void adreno_perfcounter_save(struct adreno_device *adreno_dev)
{
+ struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
struct adreno_perfcounters *counters = ADRENO_PERFCOUNTERS(adreno_dev);
struct adreno_perfcount_group *group;
unsigned int counter, groupid;
- int ret;
+ int ret = 0;
if (counters == NULL)
return;
- ret = adreno_perfcntr_active_oob_get(adreno_dev);
+ if (gpudev->oob_set)
+ ret = gpudev->oob_set(adreno_dev, OOB_PERFCNTR_SET_MASK,
+ OOB_PERFCNTR_CHECK_MASK,
+ OOB_PERFCNTR_CLEAR_MASK);
+
+ /* if oob_set timeout, clear the mask and return */
if (ret)
- return;
+ goto done;
for (groupid = 0; groupid < counters->group_count; groupid++) {
group = &(counters->groups[groupid]);
@@ -203,7 +209,9 @@
}
}
- adreno_perfcntr_active_oob_put(adreno_dev);
+done:
+ if (gpudev->oob_clear)
+ gpudev->oob_clear(adreno_dev, OOB_PERFCNTR_CLEAR_MASK);
}
static int adreno_perfcounter_enable(struct adreno_device *adreno_dev,
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index 8008e06..865e7c2 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -604,7 +604,8 @@
{
/* the worst case is computed from the set_report command with a
* reportID > 15 and the maximum report length */
- int args_len = sizeof(__u8) + /* optional ReportID byte */
+ int args_len = sizeof(__u8) + /* ReportID */
+ sizeof(__u8) + /* optional ReportID byte */
sizeof(__u16) + /* data register */
sizeof(__u16) + /* size of the report */
report_size; /* report */
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index ae83af6..7838343 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -971,6 +971,8 @@
unsigned int rsize = 0;
char *rdesc;
int ret, n;
+ int num_descriptors;
+ size_t offset = offsetof(struct hid_descriptor, desc);
quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
@@ -993,10 +995,18 @@
return -ENODEV;
}
+ if (hdesc->bLength < sizeof(struct hid_descriptor)) {
+ dbg_hid("hid descriptor is too short\n");
+ return -EINVAL;
+ }
+
hid->version = le16_to_cpu(hdesc->bcdHID);
hid->country = hdesc->bCountryCode;
- for (n = 0; n < hdesc->bNumDescriptors; n++)
+ num_descriptors = min_t(int, hdesc->bNumDescriptors,
+ (hdesc->bLength - offset) / sizeof(struct hid_class_descriptor));
+
+ for (n = 0; n < num_descriptors; n++)
if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 0c535d0..d72dfb2 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -611,8 +611,10 @@
/* Try to find an already-probed interface from the same device */
list_for_each_entry(data, &wacom_udev_list, list) {
- if (compare_device_paths(hdev, data->dev, '/'))
+ if (compare_device_paths(hdev, data->dev, '/')) {
+ kref_get(&data->kref);
return data;
+ }
}
/* Fallback to finding devices that appear to be "siblings" */
@@ -712,6 +714,9 @@
if (!wacom->led.groups)
return -ENOTSUPP;
+ if (wacom->wacom_wac.features.type == REMOTE)
+ return -ENOTSUPP;
+
if (wacom->wacom_wac.pid) { /* wireless connected */
report_id = WAC_CMD_WL_LED_CONTROL;
buf_size = 13;
@@ -2433,6 +2438,8 @@
if (hdev->bus == BUS_BLUETOOTH)
device_remove_file(&hdev->dev, &dev_attr_speed);
+ wacom_release_resources(wacom);
+
hid_set_drvdata(hdev, NULL);
}
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index c6a922e..db951c4 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -559,8 +559,8 @@
keys = data[9] & 0x07;
}
} else {
- buttons = ((data[6] & 0x10) << 10) |
- ((data[5] & 0x10) << 9) |
+ buttons = ((data[6] & 0x10) << 5) |
+ ((data[5] & 0x10) << 4) |
((data[6] & 0x0F) << 4) |
(data[5] & 0x0F);
}
diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c
index e47d8c9..75126e4 100644
--- a/drivers/hv/hv_fcopy.c
+++ b/drivers/hv/hv_fcopy.c
@@ -161,6 +161,10 @@
out_src = smsg_out;
break;
+ case WRITE_TO_FILE:
+ out_src = fcopy_transaction.fcopy_msg;
+ out_len = sizeof(struct hv_do_fcopy);
+ break;
default:
out_src = fcopy_transaction.fcopy_msg;
out_len = fcopy_transaction.recv_len;
diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c
index dee93ec..84e0994 100644
--- a/drivers/hwmon/gl520sm.c
+++ b/drivers/hwmon/gl520sm.c
@@ -208,11 +208,13 @@
}
static DEVICE_ATTR(cpu0_vid, S_IRUGO, get_cpu_vid, NULL);
-#define VDD_FROM_REG(val) (((val) * 95 + 2) / 4)
-#define VDD_TO_REG(val) clamp_val((((val) * 4 + 47) / 95), 0, 255)
+#define VDD_FROM_REG(val) DIV_ROUND_CLOSEST((val) * 95, 4)
+#define VDD_CLAMP(val) clamp_val(val, 0, 255 * 95 / 4)
+#define VDD_TO_REG(val) DIV_ROUND_CLOSEST(VDD_CLAMP(val) * 4, 95)
-#define IN_FROM_REG(val) ((val) * 19)
-#define IN_TO_REG(val) clamp_val((((val) + 9) / 19), 0, 255)
+#define IN_FROM_REG(val) ((val) * 19)
+#define IN_CLAMP(val) clamp_val(val, 0, 255 * 19)
+#define IN_TO_REG(val) DIV_ROUND_CLOSEST(IN_CLAMP(val), 19)
static ssize_t get_in_input(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -349,8 +351,13 @@
#define DIV_FROM_REG(val) (1 << (val))
#define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : (480000 / ((val) << (div))))
-#define FAN_TO_REG(val, div) ((val) <= 0 ? 0 : \
- clamp_val((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255))
+
+#define FAN_BASE(div) (480000 >> (div))
+#define FAN_CLAMP(val, div) clamp_val(val, FAN_BASE(div) / 255, \
+ FAN_BASE(div))
+#define FAN_TO_REG(val, div) ((val) == 0 ? 0 : \
+ DIV_ROUND_CLOSEST(480000, \
+ FAN_CLAMP(val, div) << (div)))
static ssize_t get_fan_input(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -513,9 +520,9 @@
static DEVICE_ATTR(fan1_off, S_IRUGO | S_IWUSR,
get_fan_off, set_fan_off);
-#define TEMP_FROM_REG(val) (((val) - 130) * 1000)
-#define TEMP_TO_REG(val) clamp_val(((((val) < 0 ? \
- (val) - 500 : (val) + 500) / 1000) + 130), 0, 255)
+#define TEMP_FROM_REG(val) (((val) - 130) * 1000)
+#define TEMP_CLAMP(val) clamp_val(val, -130000, 125000)
+#define TEMP_TO_REG(val) (DIV_ROUND_CLOSEST(TEMP_CLAMP(val), 1000) + 130)
static ssize_t get_temp_input(struct device *dev, struct device_attribute *attr,
char *buf)
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index d883483..16a3e7d 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -757,14 +757,14 @@
return -EINVAL;
if (adc_properties->adc_hc) {
- /* (ADC code * vref_vadc (1.875V)) / 0x4000 */
+ /* (ADC code * vref_vadc (1.875V)) / scale_code */
if (adc_code > QPNP_VADC_HC_MAX_CODE)
adc_code = 0;
pmic_voltage = (int64_t) adc_code;
pmic_voltage *= (int64_t) (adc_properties->adc_vdd_reference
* 1000);
pmic_voltage = div64_s64(pmic_voltage,
- QPNP_VADC_HC_VREF_CODE);
+ adc_properties->full_scale_code);
} else {
if (!chan_properties->adc_graph[CALIB_ABSOLUTE].dy)
return -EINVAL;
@@ -777,7 +777,8 @@
adc_chan_result->measurement = pmic_voltage*
chan_properties->offset_gain_denominator;
- do_div(adc_chan_result->measurement,
+ adc_chan_result->measurement =
+ div64_s64(adc_chan_result->measurement,
chan_properties->offset_gain_numerator * 2);
} else
adc_chan_result->measurement = 0;
@@ -804,10 +805,12 @@
high_output = (param->high_temp + KELVINMIL_DEGMIL) * 2;
if (param->adc_tm_hc) {
- low_output *= QPNP_VADC_HC_VREF_CODE;
- do_div(low_output, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
- high_output *= QPNP_VADC_HC_VREF_CODE;
- do_div(high_output, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
+ low_output *= param->full_scale_code;
+ low_output = div64_s64(low_output,
+ (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
+ high_output *= param->full_scale_code;
+ high_output = div64_s64(high_output,
+ (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
} else {
rc = qpnp_get_vadc_gain_and_offset(chip, &btm_param,
CALIB_ABSOLUTE);
@@ -822,7 +825,7 @@
sign = 1;
low_output = -low_output;
}
- do_div(low_output, QPNP_ADC_625_UV);
+ low_output = div64_s64(low_output, QPNP_ADC_625_UV);
if (sign)
low_output = -low_output;
low_output += btm_param.adc_gnd;
@@ -834,7 +837,7 @@
sign = 1;
high_output = -high_output;
}
- do_div(high_output, QPNP_ADC_625_UV);
+ high_output = div64_s64(high_output, QPNP_ADC_625_UV);
if (sign)
high_output = -high_output;
high_output += btm_param.adc_gnd;
@@ -869,14 +872,14 @@
return -EINVAL;
if (adc_properties->adc_hc) {
- /* (ADC code * vref_vadc (1.875V) * 1000) / (0x4000 * 1000) */
+ /* (code * vref_vadc (1.875V) * 1000) / (scale_code * 1000) */
if (adc_code > QPNP_VADC_HC_MAX_CODE)
adc_code = 0;
xo_thm_voltage = (int64_t) adc_code;
xo_thm_voltage *= (int64_t) (adc_properties->adc_vdd_reference
* 1000);
xo_thm_voltage = div64_s64(xo_thm_voltage,
- QPNP_VADC_HC_VREF_CODE * 1000);
+ adc_properties->full_scale_code * 1000);
qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
xo_thm_voltage, &adc_chan_result->physical);
@@ -885,7 +888,7 @@
adc_properties, chan_properties, &xo_thm_voltage);
if (chan_properties->calib_type == CALIB_ABSOLUTE)
- do_div(xo_thm_voltage, 1000);
+ xo_thm_voltage = div64_s64(xo_thm_voltage, 1000);
qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
ARRAY_SIZE(adcmap_100k_104ef_104fb),
@@ -1068,14 +1071,14 @@
return -EINVAL;
if (adc_properties->adc_hc) {
- /* (ADC code * vref_vadc (1.875V) * 1000) / (0x4000 * 1000) */
+ /* (code * vref_vadc (1.875V) * 1000) / (scale code * 1000) */
if (adc_code > QPNP_VADC_HC_MAX_CODE)
adc_code = 0;
therm_voltage = (int64_t) adc_code;
therm_voltage *= (int64_t) (adc_properties->adc_vdd_reference
* 1000);
therm_voltage = div64_s64(therm_voltage,
- (QPNP_VADC_HC_VREF_CODE * 1000));
+ (adc_properties->full_scale_code * 1000));
qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
@@ -1085,7 +1088,7 @@
adc_properties, chan_properties, &therm_voltage);
if (chan_properties->calib_type == CALIB_ABSOLUTE)
- do_div(therm_voltage, 1000);
+ therm_voltage = div64_s64(therm_voltage, 1000);
qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
ARRAY_SIZE(adcmap_100k_104ef_104fb),
@@ -1105,13 +1108,13 @@
int negative_offset = 0;
if (adc_properties->adc_hc) {
- /* (ADC code * vref_vadc (1.875V)) / 0x4000 */
+ /* (ADC code * vref_vadc (1.875V)) / full_scale_code */
if (reg > QPNP_VADC_HC_MAX_CODE)
reg = 0;
adc_voltage = (int64_t) reg;
adc_voltage *= QPNP_VADC_HC_VDD_REFERENCE_MV;
adc_voltage = div64_s64(adc_voltage,
- QPNP_VADC_HC_VREF_CODE);
+ adc_properties->full_scale_code);
qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
adc_voltage, result);
@@ -1124,7 +1127,7 @@
adc_voltage = -adc_voltage;
}
- do_div(adc_voltage, param1.dy);
+ adc_voltage = div64_s64(adc_voltage, param1.dy);
qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
ARRAY_SIZE(adcmap_100k_104ef_104fb),
@@ -1151,8 +1154,9 @@
param->low_thr_temp, ¶m->low_thr_voltage);
if (rc)
return rc;
- param->low_thr_voltage *= QPNP_VADC_HC_VREF_CODE;
- do_div(param->low_thr_voltage, QPNP_VADC_HC_VDD_REFERENCE_MV);
+ param->low_thr_voltage *= adc_properties->full_scale_code;
+ param->low_thr_voltage = div64_s64(param->low_thr_voltage,
+ QPNP_VADC_HC_VDD_REFERENCE_MV);
rc = qpnp_adc_map_temp_voltage(
adcmap_100k_104ef_104fb_1875_vref,
@@ -1160,8 +1164,9 @@
param->high_thr_temp, ¶m->high_thr_voltage);
if (rc)
return rc;
- param->high_thr_voltage *= QPNP_VADC_HC_VREF_CODE;
- do_div(param->high_thr_voltage, QPNP_VADC_HC_VDD_REFERENCE_MV);
+ param->high_thr_voltage *= adc_properties->full_scale_code;
+ param->high_thr_voltage = div64_s64(param->high_thr_voltage,
+ QPNP_VADC_HC_VDD_REFERENCE_MV);
} else {
qpnp_get_vadc_gain_and_offset(chip, ¶m1, CALIB_RATIOMETRIC);
@@ -1172,7 +1177,8 @@
return rc;
param->low_thr_voltage *= param1.dy;
- do_div(param->low_thr_voltage, param1.adc_vref);
+ param->low_thr_voltage = div64_s64(param->low_thr_voltage,
+ param1.adc_vref);
param->low_thr_voltage += param1.adc_gnd;
rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb,
@@ -1182,7 +1188,8 @@
return rc;
param->high_thr_voltage *= param1.dy;
- do_div(param->high_thr_voltage, param1.adc_vref);
+ param->high_thr_voltage = div64_s64(param->high_thr_voltage,
+ param1.adc_vref);
param->high_thr_voltage += param1.adc_gnd;
}
@@ -1241,13 +1248,13 @@
return -EINVAL;
if (adc_properties->adc_hc) {
- /* (ADC code * vref_vadc (1.875V)) / 0x4000 */
+ /* (ADC code * vref_vadc (1.875V)) / full_scale_code */
if (adc_code > QPNP_VADC_HC_MAX_CODE)
adc_code = 0;
scale_voltage = (int64_t) adc_code;
scale_voltage *= (adc_properties->adc_vdd_reference * 1000);
scale_voltage = div64_s64(scale_voltage,
- QPNP_VADC_HC_VREF_CODE);
+ adc_properties->full_scale_code);
} else {
qpnp_adc_scale_with_calib_param(adc_code, adc_properties,
chan_properties, &scale_voltage);
@@ -1281,11 +1288,11 @@
qpnp_get_vadc_gain_and_offset(chip, &usb_param, CALIB_RATIOMETRIC);
*low_threshold = param->low_thr * usb_param.dy;
- do_div(*low_threshold, usb_param.adc_vref);
+ *low_threshold = div64_s64(*low_threshold, usb_param.adc_vref);
*low_threshold += usb_param.adc_gnd;
*high_threshold = param->high_thr * usb_param.dy;
- do_div(*high_threshold, usb_param.adc_vref);
+ *high_threshold = div64_s64(*high_threshold, usb_param.adc_vref);
*high_threshold += usb_param.adc_gnd;
pr_debug("high_volt:%d, low_volt:%d\n", param->high_thr,
@@ -1305,14 +1312,16 @@
if (param->adc_tm_hc) {
low_thr = (param->low_thr/param->gain_den);
low_thr *= param->gain_num;
- low_thr *= QPNP_VADC_HC_VREF_CODE;
- do_div(low_thr, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
+ low_thr *= param->full_scale_code;
+ low_thr = div64_s64(low_thr,
+ (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
*low_threshold = low_thr;
high_thr = (param->high_thr/param->gain_den);
high_thr *= param->gain_num;
- high_thr *= QPNP_VADC_HC_VREF_CODE;
- do_div(high_thr, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
+ high_thr *= param->full_scale_code;
+ high_thr = div64_s64(high_thr,
+ (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
*high_threshold = high_thr;
} else {
rc = qpnp_get_vadc_gain_and_offset(chip, &vbatt_param,
@@ -1327,7 +1336,7 @@
low_thr = -low_thr;
}
low_thr = low_thr * param->gain_num;
- do_div(low_thr, QPNP_ADC_625_UV);
+ low_thr = div64_s64(low_thr, QPNP_ADC_625_UV);
if (sign)
low_thr = -low_thr;
*low_threshold = low_thr + vbatt_param.adc_gnd;
@@ -1340,7 +1349,7 @@
high_thr = -high_thr;
}
high_thr = high_thr * param->gain_num;
- do_div(high_thr, QPNP_ADC_625_UV);
+ high_thr = div64_s64(high_thr, QPNP_ADC_625_UV);
if (sign)
high_thr = -high_thr;
*high_threshold = high_thr + vbatt_param.adc_gnd;
@@ -1387,7 +1396,7 @@
low_thr = -low_thr;
}
low_thr = low_thr * chan_prop->offset_gain_numerator;
- do_div(low_thr, QPNP_ADC_625_UV);
+ low_thr = div64_s64(low_thr, QPNP_ADC_625_UV);
if (sign)
low_thr = -low_thr;
*low_threshold = low_thr + vbatt_param.adc_gnd;
@@ -1400,7 +1409,7 @@
high_thr = -high_thr;
}
high_thr = high_thr * chan_prop->offset_gain_numerator;
- do_div(high_thr, QPNP_ADC_625_UV);
+ high_thr = div64_s64(high_thr, QPNP_ADC_625_UV);
if (sign)
high_thr = -high_thr;
*high_threshold = high_thr + vbatt_param.adc_gnd;
@@ -1442,7 +1451,7 @@
pr_debug("low_output:%lld\n", low_output);
low_output *= btm_param.dy;
- do_div(low_output, btm_param.adc_vref);
+ low_output = div64_s64(low_output, btm_param.adc_vref);
low_output += btm_param.adc_gnd;
rc = qpnp_adc_map_voltage_temp(
@@ -1457,7 +1466,7 @@
pr_debug("high_output:%lld\n", high_output);
high_output *= btm_param.dy;
- do_div(high_output, btm_param.adc_vref);
+ high_output = div64_s64(high_output, btm_param.adc_vref);
high_output += btm_param.adc_gnd;
/* btm low temperature correspondes to high voltage threshold */
@@ -1500,7 +1509,7 @@
pr_debug("low_output:%lld\n", low_output);
low_output *= btm_param.dy;
- do_div(low_output, btm_param.adc_vref);
+ low_output = div64_s64(low_output, btm_param.adc_vref);
low_output += btm_param.adc_gnd;
rc = qpnp_adc_map_voltage_temp(
@@ -1515,7 +1524,7 @@
pr_debug("high_output:%lld\n", high_output);
high_output *= btm_param.dy;
- do_div(high_output, btm_param.adc_vref);
+ high_output = div64_s64(high_output, btm_param.adc_vref);
high_output += btm_param.adc_gnd;
/* btm low temperature correspondes to high voltage threshold */
@@ -1558,7 +1567,7 @@
pr_debug("low_output:%lld\n", low_output);
low_output *= btm_param.dy;
- do_div(low_output, btm_param.adc_vref);
+ low_output = div64_s64(low_output, btm_param.adc_vref);
low_output += btm_param.adc_gnd;
rc = qpnp_adc_map_voltage_temp(
@@ -1573,7 +1582,7 @@
pr_debug("high_output:%lld\n", high_output);
high_output *= btm_param.dy;
- do_div(high_output, btm_param.adc_vref);
+ high_output = div64_s64(high_output, btm_param.adc_vref);
high_output += btm_param.adc_gnd;
/* btm low temperature correspondes to high voltage threshold */
@@ -1616,7 +1625,7 @@
pr_debug("low_output:%lld\n", low_output);
low_output *= btm_param.dy;
- do_div(low_output, btm_param.adc_vref);
+ low_output = div64_s64(low_output, btm_param.adc_vref);
low_output += btm_param.adc_gnd;
rc = qpnp_adc_map_voltage_temp(
@@ -1631,7 +1640,7 @@
pr_debug("high_output:%lld\n", high_output);
high_output *= btm_param.dy;
- do_div(high_output, btm_param.adc_vref);
+ high_output = div64_s64(high_output, btm_param.adc_vref);
high_output += btm_param.adc_gnd;
/* btm low temperature correspondes to high voltage threshold */
@@ -2027,11 +2036,11 @@
pr_err("Invalid adc vdd reference property\n");
return -EINVAL;
}
- rc = of_property_read_u32(node, "qcom,adc-bit-resolution",
- &adc_prop->bitresolution);
+ rc = of_property_read_u32(node, "qcom,adc-full-scale-code",
+ &adc_prop->full_scale_code);
if (rc) {
- pr_err("Invalid adc bit resolution property\n");
- return -EINVAL;
+ pr_debug("Use default value of 0x4000 for full scale\n");
+ adc_prop->full_scale_code = QPNP_VADC_HC_VREF_CODE;
}
adc_qpnp->adc_prop = adc_prop;
diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c
index 5269890..2dd60ee 100644
--- a/drivers/hwtracing/stm/core.c
+++ b/drivers/hwtracing/stm/core.c
@@ -1125,7 +1125,7 @@
stm_source_link_drop(src);
- device_destroy(&stm_source_class, src->dev.devt);
+ device_unregister(&src->dev);
}
EXPORT_SYMBOL_GPL(stm_source_unregister_device);
diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c
index 0b86c61..c925a69 100644
--- a/drivers/i2c/busses/i2c-at91.c
+++ b/drivers/i2c/busses/i2c-at91.c
@@ -1180,6 +1180,7 @@
static int at91_twi_resume_noirq(struct device *dev)
{
+ struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
int ret;
if (!pm_runtime_status_suspended(dev)) {
@@ -1191,6 +1192,8 @@
pm_runtime_mark_last_busy(dev);
pm_request_autosuspend(dev);
+ at91_init_twi_bus(twi_dev);
+
return 0;
}
diff --git a/drivers/i2c/busses/i2c-meson.c b/drivers/i2c/busses/i2c-meson.c
index 2aa61bb..73b97c7 100644
--- a/drivers/i2c/busses/i2c-meson.c
+++ b/drivers/i2c/busses/i2c-meson.c
@@ -175,7 +175,7 @@
wdata1 |= *buf++ << ((i - 4) * 8);
writel(wdata0, i2c->regs + REG_TOK_WDATA0);
- writel(wdata0, i2c->regs + REG_TOK_WDATA1);
+ writel(wdata1, i2c->regs + REG_TOK_WDATA1);
dev_dbg(i2c->dev, "%s: data %08x %08x len %d\n", __func__,
wdata0, wdata1, len);
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index 793cbb5..1953bf7 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -74,6 +74,8 @@
#define I2C_ARB_LOST GP_IRQ4
#define DM_I2C_RX_ERR ((GP_IRQ1 | GP_IRQ3 | GP_IRQ4) >> 4)
+#define I2C_AUTO_SUSPEND_DELAY 250
+
enum i2c_se_mode {
UNINITIALIZED,
FIFO_SE_DMA,
@@ -385,12 +387,22 @@
struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
int i, ret = 0, timeout = 0;
+ ret = pinctrl_select_state(gi2c->i2c_rsc.geni_pinctrl,
+ gi2c->i2c_rsc.geni_gpio_active);
+ if (ret) {
+ GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
+ "%s: Error %d pinctrl_select_state active\n",
+ __func__, ret);
+ return ret;
+ }
+
if (!gi2c->tx_c) {
gi2c->tx_c = dma_request_slave_channel(gi2c->dev, "tx");
if (!gi2c->tx_c) {
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
"tx dma req slv chan ret :%d\n", ret);
- return -EIO;
+ ret = -EIO;
+ goto geni_i2c_gsi_xfer_out;
}
gi2c->tx_ev.init.callback = gi2c_ev_cb;
gi2c->tx_ev.init.cb_param = gi2c;
@@ -400,7 +412,7 @@
if (ret) {
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
"tx dma slave config ret :%d\n", ret);
- return ret;
+ goto geni_i2c_gsi_xfer_out;
}
}
if (!gi2c->rx_c) {
@@ -408,7 +420,8 @@
if (!gi2c->rx_c) {
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
"rx dma req slv chan ret :%d\n", ret);
- return -EIO;
+ ret = -EIO;
+ goto geni_i2c_gsi_xfer_out;
}
gi2c->rx_ev.init.cb_param = gi2c;
gi2c->rx_ev.init.callback = gi2c_ev_cb;
@@ -418,7 +431,7 @@
if (ret) {
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
"rx dma slave config ret :%d\n", ret);
- return ret;
+ goto geni_i2c_gsi_xfer_out;
}
}
@@ -502,7 +515,7 @@
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
"prep_slave_sg for rx failed\n");
gi2c->err = -ENOMEM;
- return gi2c->err;
+ goto geni_i2c_gsi_xfer_out;
}
gi2c->rx_desc->callback = gi2c_gsi_rx_cb;
gi2c->rx_desc->callback_param = &gi2c->rx_cb;
@@ -534,7 +547,7 @@
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
"prep_slave_sg for tx failed\n");
gi2c->err = -ENOMEM;
- return gi2c->err;
+ goto geni_i2c_gsi_xfer_out;
}
gi2c->tx_desc->callback = gi2c_gsi_tx_cb;
gi2c->tx_desc->callback_param = &gi2c->tx_cb;
@@ -559,10 +572,15 @@
if (gi2c->err) {
dmaengine_terminate_all(gi2c->tx_c);
gi2c->cfg_sent = 0;
- return gi2c->err;
+ goto geni_i2c_gsi_xfer_out;
}
}
- return gi2c->err;
+geni_i2c_gsi_xfer_out:
+ if (!ret && gi2c->err)
+ ret = gi2c->err;
+ pinctrl_select_state(gi2c->i2c_rsc.geni_pinctrl,
+ gi2c->i2c_rsc.geni_gpio_sleep);
+ return ret;
}
static int geni_i2c_xfer(struct i2c_adapter *adap,
@@ -686,7 +704,9 @@
geni_i2c_txn_ret:
if (ret == 0)
ret = num;
- pm_runtime_put_sync(gi2c->dev);
+
+ pm_runtime_mark_last_busy(gi2c->dev);
+ pm_runtime_put_autosuspend(gi2c->dev);
gi2c->cur = NULL;
gi2c->err = 0;
dev_dbg(gi2c->dev, "i2c txn ret:%d\n", ret);
@@ -830,6 +850,8 @@
strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
pm_runtime_set_suspended(gi2c->dev);
+ pm_runtime_set_autosuspend_delay(gi2c->dev, I2C_AUTO_SUSPEND_DELAY);
+ pm_runtime_use_autosuspend(gi2c->dev);
pm_runtime_enable(gi2c->dev);
i2c_add_adapter(&gi2c->adap);
@@ -858,10 +880,13 @@
{
struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
- if (gi2c->se_mode == FIFO_SE_DMA)
+ if (gi2c->se_mode == FIFO_SE_DMA) {
disable_irq(gi2c->irq);
-
- se_geni_resources_off(&gi2c->i2c_rsc);
+ se_geni_resources_off(&gi2c->i2c_rsc);
+ } else {
+ /* GPIO is set to sleep state already. So just clocks off */
+ se_geni_clks_off(&gi2c->i2c_rsc);
+ }
return 0;
}
@@ -876,7 +901,12 @@
snprintf(ipc_name, I2C_NAME_SIZE, "i2c-%d", gi2c->adap.nr);
gi2c->ipcl = ipc_log_context_create(2, ipc_name, 0);
}
- ret = se_geni_resources_on(&gi2c->i2c_rsc);
+
+ if (gi2c->se_mode != GSI_ONLY)
+ ret = se_geni_resources_on(&gi2c->i2c_rsc);
+ else
+ ret = se_geni_clks_on(&gi2c->i2c_rsc);
+
if (ret)
return ret;
diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c
index e6706a0..47c3d7f 100644
--- a/drivers/iio/adc/ad7793.c
+++ b/drivers/iio/adc/ad7793.c
@@ -257,7 +257,7 @@
unsigned int vref_mv)
{
struct ad7793_state *st = iio_priv(indio_dev);
- int i, ret = -1;
+ int i, ret;
unsigned long long scale_uv;
u32 id;
@@ -266,7 +266,7 @@
return ret;
/* reset the serial interface */
- ret = spi_write(st->sd.spi, (u8 *)&ret, sizeof(ret));
+ ret = ad_sd_reset(&st->sd, 32);
if (ret < 0)
goto out;
usleep_range(500, 2000); /* Wait for at least 500us */
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c
index d10bd0c..22c4c17 100644
--- a/drivers/iio/adc/ad_sigma_delta.c
+++ b/drivers/iio/adc/ad_sigma_delta.c
@@ -177,6 +177,34 @@
}
EXPORT_SYMBOL_GPL(ad_sd_read_reg);
+/**
+ * ad_sd_reset() - Reset the serial interface
+ *
+ * @sigma_delta: The sigma delta device
+ * @reset_length: Number of SCLKs with DIN = 1
+ *
+ * Returns 0 on success, an error code otherwise.
+ **/
+int ad_sd_reset(struct ad_sigma_delta *sigma_delta,
+ unsigned int reset_length)
+{
+ uint8_t *buf;
+ unsigned int size;
+ int ret;
+
+ size = DIV_ROUND_UP(reset_length, 8);
+ buf = kcalloc(size, sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ memset(buf, 0xff, size);
+ ret = spi_write(sigma_delta->spi, buf, size);
+ kfree(buf);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ad_sd_reset);
+
static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
unsigned int mode, unsigned int channel)
{
diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c
index 7fd2494..64799ad 100644
--- a/drivers/iio/adc/axp288_adc.c
+++ b/drivers/iio/adc/axp288_adc.c
@@ -28,8 +28,6 @@
#include <linux/iio/driver.h>
#define AXP288_ADC_EN_MASK 0xF1
-#define AXP288_ADC_TS_PIN_GPADC 0xF2
-#define AXP288_ADC_TS_PIN_ON 0xF3
enum axp288_adc_id {
AXP288_ADC_TS,
@@ -123,16 +121,6 @@
return IIO_VAL_INT;
}
-static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode,
- unsigned long address)
-{
- /* channels other than GPADC do not need to switch TS pin */
- if (address != AXP288_GP_ADC_H)
- return 0;
-
- return regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode);
-}
-
static int axp288_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@@ -143,16 +131,7 @@
mutex_lock(&indio_dev->mlock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
- if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC,
- chan->address)) {
- dev_err(&indio_dev->dev, "GPADC mode\n");
- ret = -EINVAL;
- break;
- }
ret = axp288_adc_read_channel(val, chan->address, info->regmap);
- if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON,
- chan->address))
- dev_err(&indio_dev->dev, "TS pin restore\n");
break;
default:
ret = -EINVAL;
@@ -162,15 +141,6 @@
return ret;
}
-static int axp288_adc_set_state(struct regmap *regmap)
-{
- /* ADC should be always enabled for internal FG to function */
- if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON))
- return -EIO;
-
- return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
-}
-
static const struct iio_info axp288_adc_iio_info = {
.read_raw = &axp288_adc_read_raw,
.driver_module = THIS_MODULE,
@@ -199,7 +169,7 @@
* Set ADC to enabled state at all time, including system suspend.
* otherwise internal fuel gauge functionality may be affected.
*/
- ret = axp288_adc_set_state(axp20x->regmap);
+ ret = regmap_write(info->regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
if (ret) {
dev_err(&pdev->dev, "unable to enable ADC device\n");
return ret;
diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c
index 72b32c1..ea264fa 100644
--- a/drivers/iio/adc/fsl-imx25-gcq.c
+++ b/drivers/iio/adc/fsl-imx25-gcq.c
@@ -401,6 +401,7 @@
{ .compatible = "fsl,imx25-gcq", },
{ /* Sentinel */ }
};
+MODULE_DEVICE_TABLE(of, mx25_gcq_ids);
static struct platform_driver mx25_gcq_driver = {
.driver = {
diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
index 634717a..071dd23 100644
--- a/drivers/iio/adc/mcp320x.c
+++ b/drivers/iio/adc/mcp320x.c
@@ -17,6 +17,8 @@
* MCP3204
* MCP3208
* ------------
+ * 13 bit converter
+ * MCP3301
*
* Datasheet can be found here:
* http://ww1.microchip.com/downloads/en/DeviceDoc/21293C.pdf mcp3001
@@ -96,7 +98,7 @@
}
static int mcp320x_adc_conversion(struct mcp320x *adc, u8 channel,
- bool differential, int device_index)
+ bool differential, int device_index, int *val)
{
int ret;
@@ -117,19 +119,25 @@
switch (device_index) {
case mcp3001:
- return (adc->rx_buf[0] << 5 | adc->rx_buf[1] >> 3);
+ *val = (adc->rx_buf[0] << 5 | adc->rx_buf[1] >> 3);
+ return 0;
case mcp3002:
case mcp3004:
case mcp3008:
- return (adc->rx_buf[0] << 2 | adc->rx_buf[1] >> 6);
+ *val = (adc->rx_buf[0] << 2 | adc->rx_buf[1] >> 6);
+ return 0;
case mcp3201:
- return (adc->rx_buf[0] << 7 | adc->rx_buf[1] >> 1);
+ *val = (adc->rx_buf[0] << 7 | adc->rx_buf[1] >> 1);
+ return 0;
case mcp3202:
case mcp3204:
case mcp3208:
- return (adc->rx_buf[0] << 4 | adc->rx_buf[1] >> 4);
+ *val = (adc->rx_buf[0] << 4 | adc->rx_buf[1] >> 4);
+ return 0;
case mcp3301:
- return sign_extend32((adc->rx_buf[0] & 0x1f) << 8 | adc->rx_buf[1], 12);
+ *val = sign_extend32((adc->rx_buf[0] & 0x1f) << 8
+ | adc->rx_buf[1], 12);
+ return 0;
default:
return -EINVAL;
}
@@ -150,12 +158,10 @@
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = mcp320x_adc_conversion(adc, channel->address,
- channel->differential, device_index);
-
+ channel->differential, device_index, val);
if (ret < 0)
goto out;
- *val = ret;
ret = IIO_VAL_INT;
break;
@@ -312,6 +318,7 @@
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &mcp320x_info;
+ spi_set_drvdata(spi, indio_dev);
chip_info = &mcp320x_chip_infos[spi_get_device_id(spi)->driver_data];
indio_dev->channels = chip_info->channels;
diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c
index 0c74869..7ffc5db 100644
--- a/drivers/iio/adc/twl4030-madc.c
+++ b/drivers/iio/adc/twl4030-madc.c
@@ -866,8 +866,10 @@
/* Enable 3v1 bias regulator for MADC[3:6] */
madc->usb3v1 = devm_regulator_get(madc->dev, "vusb3v1");
- if (IS_ERR(madc->usb3v1))
- return -ENODEV;
+ if (IS_ERR(madc->usb3v1)) {
+ ret = -ENODEV;
+ goto err_i2c;
+ }
ret = regulator_enable(madc->usb3v1);
if (ret)
@@ -876,11 +878,13 @@
ret = iio_device_register(iio_dev);
if (ret) {
dev_err(&pdev->dev, "could not register iio device\n");
- goto err_i2c;
+ goto err_usb3v1;
}
return 0;
+err_usb3v1:
+ regulator_disable(madc->usb3v1);
err_i2c:
twl4030_madc_set_current_generator(madc, 0, 0);
err_current_generator:
diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c
index 0a6beb3..56cf590 100644
--- a/drivers/iio/adc/xilinx-xadc-core.c
+++ b/drivers/iio/adc/xilinx-xadc-core.c
@@ -1208,7 +1208,7 @@
ret = xadc->ops->setup(pdev, indio_dev, irq);
if (ret)
- goto err_free_samplerate_trigger;
+ goto err_clk_disable_unprepare;
ret = request_irq(irq, xadc->ops->interrupt_handler, 0,
dev_name(&pdev->dev), indio_dev);
@@ -1268,6 +1268,8 @@
err_free_irq:
free_irq(irq, indio_dev);
+err_clk_disable_unprepare:
+ clk_disable_unprepare(xadc->clk);
err_free_samplerate_trigger:
if (xadc->ops->flags & XADC_FLAGS_BUFFERED)
iio_trigger_free(xadc->samplerate_trigger);
@@ -1277,8 +1279,6 @@
err_triggered_buffer_cleanup:
if (xadc->ops->flags & XADC_FLAGS_BUFFERED)
iio_triggered_buffer_cleanup(indio_dev);
-err_clk_disable_unprepare:
- clk_disable_unprepare(xadc->clk);
err_device_free:
kfree(indio_dev->channels);
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index fc340ed..c5bc731 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -306,8 +306,10 @@
ret = indio_dev->info->debugfs_reg_access(indio_dev,
indio_dev->cached_reg_addr,
0, &val);
- if (ret)
+ if (ret) {
dev_err(indio_dev->dev.parent, "%s: read failed\n", __func__);
+ return ret;
+ }
len = snprintf(buf, sizeof(buf), "0x%X\n", val);
diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c
index f762eb8..19aa957 100644
--- a/drivers/iio/pressure/bmp280-core.c
+++ b/drivers/iio/pressure/bmp280-core.c
@@ -558,7 +558,7 @@
u8 osrs = BMP280_OSRS_TEMP_X(data->oversampling_temp + 1) |
BMP280_OSRS_PRESS_X(data->oversampling_press + 1);
- ret = regmap_update_bits(data->regmap, BMP280_REG_CTRL_MEAS,
+ ret = regmap_write_bits(data->regmap, BMP280_REG_CTRL_MEAS,
BMP280_OSRS_TEMP_MASK |
BMP280_OSRS_PRESS_MASK |
BMP280_MODE_MASK,
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c
index 63e82f8..fb4ce03 100644
--- a/drivers/infiniband/core/addr.c
+++ b/drivers/infiniband/core/addr.c
@@ -446,15 +446,10 @@
ret = ipv6_stub->ipv6_dst_lookup(addr->net, NULL, &dst, &fl6);
if (ret < 0)
- goto put;
+ return ret;
rt = (struct rt6_info *)dst;
- if (ipv6_addr_any(&fl6.saddr)) {
- ret = ipv6_dev_get_saddr(addr->net, ip6_dst_idev(dst)->dev,
- &fl6.daddr, 0, &fl6.saddr);
- if (ret)
- goto put;
-
+ if (ipv6_addr_any(&src_in->sin6_addr)) {
src_in->sin6_family = AF_INET6;
src_in->sin6_addr = fl6.saddr;
}
@@ -471,9 +466,6 @@
*pdst = dst;
return 0;
-put:
- dst_release(dst);
- return ret;
}
#else
static int addr6_resolve(struct sockaddr_in6 *src_in,
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index 9398143..6512a55 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -2577,9 +2577,9 @@
c4iw_put_ep(&child_ep->com);
reject:
reject_cr(dev, hwtid, skb);
+out:
if (parent_ep)
c4iw_put_ep(&parent_ep->com);
-out:
return 0;
}
@@ -3441,7 +3441,7 @@
cm_id->provider_data = ep;
goto out;
}
-
+ remove_handle(ep->com.dev, &ep->com.dev->stid_idr, ep->stid);
cxgb4_free_stid(ep->com.dev->rdev.lldi.tids, ep->stid,
ep->com.local_addr.ss_family);
fail2:
diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c
index 34cfd34..a3dd27b 100644
--- a/drivers/infiniband/hw/hfi1/init.c
+++ b/drivers/infiniband/hw/hfi1/init.c
@@ -297,14 +297,15 @@
* The resulting value will be rounded down to the closest
* multiple of dd->rcv_entries.group_size.
*/
- rcd->egrbufs.buffers = kcalloc(rcd->egrbufs.count,
- sizeof(*rcd->egrbufs.buffers),
- GFP_KERNEL);
+ rcd->egrbufs.buffers = kzalloc_node(
+ rcd->egrbufs.count * sizeof(*rcd->egrbufs.buffers),
+ GFP_KERNEL, numa);
if (!rcd->egrbufs.buffers)
goto bail;
- rcd->egrbufs.rcvtids = kcalloc(rcd->egrbufs.count,
- sizeof(*rcd->egrbufs.rcvtids),
- GFP_KERNEL);
+ rcd->egrbufs.rcvtids = kzalloc_node(
+ rcd->egrbufs.count *
+ sizeof(*rcd->egrbufs.rcvtids),
+ GFP_KERNEL, numa);
if (!rcd->egrbufs.rcvtids)
goto bail;
rcd->egrbufs.size = eager_buffer_size;
@@ -322,8 +323,8 @@
rcd->egrbufs.rcvtid_size = HFI1_MAX_EAGER_BUFFER_SIZE;
if (ctxt < dd->first_user_ctxt) { /* N/A for PSM contexts */
- rcd->opstats = kzalloc(sizeof(*rcd->opstats),
- GFP_KERNEL);
+ rcd->opstats = kzalloc_node(sizeof(*rcd->opstats),
+ GFP_KERNEL, numa);
if (!rcd->opstats)
goto bail;
}
diff --git a/drivers/infiniband/hw/hfi1/pcie.c b/drivers/infiniband/hw/hfi1/pcie.c
index 4ac8f33..335613a1 100644
--- a/drivers/infiniband/hw/hfi1/pcie.c
+++ b/drivers/infiniband/hw/hfi1/pcie.c
@@ -673,12 +673,12 @@
#define UNSET_PSET 255
#define DEFAULT_DISCRETE_PSET 2 /* discrete HFI */
-#define DEFAULT_MCP_PSET 4 /* MCP HFI */
+#define DEFAULT_MCP_PSET 6 /* MCP HFI */
static uint pcie_pset = UNSET_PSET;
module_param(pcie_pset, uint, S_IRUGO);
MODULE_PARM_DESC(pcie_pset, "PCIe Eq Pset value to use, range is 0-10");
-static uint pcie_ctle = 1; /* discrete on, integrated off */
+static uint pcie_ctle = 3; /* discrete on, integrated on */
module_param(pcie_ctle, uint, S_IRUGO);
MODULE_PARM_DESC(pcie_ctle, "PCIe static CTLE mode, bit 0 - discrete on/off, bit 1 - integrated on/off");
diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c
index 4bd5b5c..613074e9 100644
--- a/drivers/infiniband/hw/hfi1/rc.c
+++ b/drivers/infiniband/hw/hfi1/rc.c
@@ -551,7 +551,7 @@
case IB_WR_RDMA_WRITE:
if (newreq && !(qp->s_flags & RVT_S_UNLIMITED_CREDIT))
qp->s_lsn++;
- /* FALLTHROUGH */
+ goto no_flow_control;
case IB_WR_RDMA_WRITE_WITH_IMM:
/* If no credit, return. */
if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT) &&
@@ -559,6 +559,7 @@
qp->s_flags |= RVT_S_WAIT_SSN_CREDIT;
goto bail;
}
+no_flow_control:
put_ib_reth_vaddr(
wqe->rdma_wr.remote_addr,
&ohdr->u.rc.reth);
diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c
index ded2717..cedb447 100644
--- a/drivers/infiniband/hw/qib/qib_iba7322.c
+++ b/drivers/infiniband/hw/qib/qib_iba7322.c
@@ -7080,7 +7080,7 @@
unsigned long flags;
while (wait) {
- unsigned long shadow;
+ unsigned long shadow = 0;
int cstart, previ = -1;
/*
diff --git a/drivers/infiniband/hw/qib/qib_rc.c b/drivers/infiniband/hw/qib/qib_rc.c
index f3fe787..c1523f9 100644
--- a/drivers/infiniband/hw/qib/qib_rc.c
+++ b/drivers/infiniband/hw/qib/qib_rc.c
@@ -357,7 +357,7 @@
case IB_WR_RDMA_WRITE:
if (newreq && !(qp->s_flags & RVT_S_UNLIMITED_CREDIT))
qp->s_lsn++;
- /* FALLTHROUGH */
+ goto no_flow_control;
case IB_WR_RDMA_WRITE_WITH_IMM:
/* If no credit, return. */
if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT) &&
@@ -365,7 +365,7 @@
qp->s_flags |= RVT_S_WAIT_SSN_CREDIT;
goto bail;
}
-
+no_flow_control:
ohdr->u.rc.reth.vaddr =
cpu_to_be64(wqe->rdma_wr.remote_addr);
ohdr->u.rc.reth.rkey =
diff --git a/drivers/infiniband/sw/rxe/rxe_pool.c b/drivers/infiniband/sw/rxe/rxe_pool.c
index 6bac071..ee26a1b 100644
--- a/drivers/infiniband/sw/rxe/rxe_pool.c
+++ b/drivers/infiniband/sw/rxe/rxe_pool.c
@@ -274,6 +274,7 @@
if (index >= range)
index = find_first_zero_bit(pool->table, range);
+ WARN_ON_ONCE(index >= range);
set_bit(index, pool->table);
pool->last = index;
return index + pool->min_index;
diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c
index 4d2a346..8f9aba7 100644
--- a/drivers/infiniband/sw/rxe/rxe_resp.c
+++ b/drivers/infiniband/sw/rxe/rxe_resp.c
@@ -418,7 +418,7 @@
static enum resp_states check_rkey(struct rxe_qp *qp,
struct rxe_pkt_info *pkt)
{
- struct rxe_mem *mem;
+ struct rxe_mem *mem = NULL;
u64 va;
u32 rkey;
u32 resid;
@@ -452,38 +452,38 @@
mem = lookup_mem(qp->pd, access, rkey, lookup_remote);
if (!mem) {
state = RESPST_ERR_RKEY_VIOLATION;
- goto err1;
+ goto err;
}
if (unlikely(mem->state == RXE_MEM_STATE_FREE)) {
state = RESPST_ERR_RKEY_VIOLATION;
- goto err1;
+ goto err;
}
if (mem_check_range(mem, va, resid)) {
state = RESPST_ERR_RKEY_VIOLATION;
- goto err2;
+ goto err;
}
if (pkt->mask & RXE_WRITE_MASK) {
if (resid > mtu) {
if (pktlen != mtu || bth_pad(pkt)) {
state = RESPST_ERR_LENGTH;
- goto err2;
+ goto err;
}
qp->resp.resid = mtu;
} else {
if (pktlen != resid) {
state = RESPST_ERR_LENGTH;
- goto err2;
+ goto err;
}
if ((bth_pad(pkt) != (0x3 & (-resid)))) {
/* This case may not be exactly that
* but nothing else fits.
*/
state = RESPST_ERR_LENGTH;
- goto err2;
+ goto err;
}
}
}
@@ -493,9 +493,9 @@
qp->resp.mr = mem;
return RESPST_EXECUTE;
-err2:
- rxe_drop_ref(mem);
-err1:
+err:
+ if (mem)
+ rxe_drop_ref(mem);
return state;
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 08c4b02..183db0c 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -1302,7 +1302,7 @@
rcu_dereference_protected(neigh->hnext,
lockdep_is_held(&priv->lock)));
/* remove from path/mc list */
- list_del(&neigh->list);
+ list_del_init(&neigh->list);
call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
} else {
np = &neigh->hnext;
@@ -1466,7 +1466,7 @@
rcu_dereference_protected(neigh->hnext,
lockdep_is_held(&priv->lock)));
/* remove from parent list */
- list_del(&neigh->list);
+ list_del_init(&neigh->list);
call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
return;
} else {
@@ -1551,7 +1551,7 @@
rcu_dereference_protected(neigh->hnext,
lockdep_is_held(&priv->lock)));
/* remove from parent list */
- list_del(&neigh->list);
+ list_del_init(&neigh->list);
call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
} else {
np = &neigh->hnext;
@@ -1593,7 +1593,7 @@
rcu_dereference_protected(neigh->hnext,
lockdep_is_held(&priv->lock)));
/* remove from path/mc list */
- list_del(&neigh->list);
+ list_del_init(&neigh->list);
call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
}
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c
index 57eadd2..93b50be 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c
@@ -165,11 +165,11 @@
out:
up_write(&ppriv->vlan_rwsem);
+ rtnl_unlock();
+
if (result)
free_netdev(priv->dev);
- rtnl_unlock();
-
return result;
}
@@ -193,7 +193,6 @@
list_for_each_entry_safe(priv, tpriv, &ppriv->child_intfs, list) {
if (priv->pkey == pkey &&
priv->child_type == IPOIB_LEGACY_CHILD) {
- unregister_netdevice(priv->dev);
list_del(&priv->list);
dev = priv->dev;
break;
@@ -201,6 +200,11 @@
}
up_write(&ppriv->vlan_rwsem);
+ if (dev) {
+ ipoib_dbg(ppriv, "delete child vlan %s\n", dev->name);
+ unregister_netdevice(dev);
+ }
+
rtnl_unlock();
if (dev) {
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index 5be14ad..dbf0983 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -905,6 +905,13 @@
},
},
{
+ /* Gigabyte P57 - Elantech touchpad */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "P57"),
+ },
+ },
+ {
/* Schenker XMG C504 - Elantech touchpad */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "XMG"),
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index c380b7e..1a0b110 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -3120,6 +3120,7 @@
mutex_unlock(&domain->api_lock);
domain_flush_tlb_pde(domain);
+ domain_flush_complete(domain);
return unmap_size;
}
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 333836c..d11b787 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -433,7 +433,7 @@
#define ARM_SMMU_OPT_3LVL_TABLES (1 << 4)
#define ARM_SMMU_OPT_NO_ASID_RETENTION (1 << 5)
#define ARM_SMMU_OPT_DISABLE_ATOS (1 << 6)
-#define ARM_SMMU_OPT_QCOM_MMU500_ERRATA1 (1 << 7)
+#define ARM_SMMU_OPT_MMU500_ERRATA1 (1 << 7)
u32 options;
enum arm_smmu_arch_version version;
enum arm_smmu_implementation model;
@@ -559,7 +559,7 @@
{ ARM_SMMU_OPT_3LVL_TABLES, "qcom,use-3-lvl-tables" },
{ ARM_SMMU_OPT_NO_ASID_RETENTION, "qcom,no-asid-retention" },
{ ARM_SMMU_OPT_DISABLE_ATOS, "qcom,disable-atos" },
- { ARM_SMMU_OPT_QCOM_MMU500_ERRATA1, "qcom,mmu500-errata-1" },
+ { ARM_SMMU_OPT_MMU500_ERRATA1, "qcom,mmu500-errata-1" },
{ 0, NULL},
};
@@ -1792,7 +1792,7 @@
quirks |= IO_PGTABLE_QUIRK_QSMMUV500_NON_SHAREABLE;
tlb = &arm_smmu_gather_ops;
- if (smmu->options & ARM_SMMU_OPT_QCOM_MMU500_ERRATA1)
+ if (smmu->options & ARM_SMMU_OPT_MMU500_ERRATA1)
tlb = &qsmmuv500_errata1_smmu_gather_ops;
ret = arm_smmu_alloc_cb(domain, smmu, dev);
@@ -2899,7 +2899,7 @@
& (1 << DOMAIN_ATTR_CB_STALL_DISABLE));
ret = 0;
break;
- case DOMAIN_ATTR_QCOM_MMU500_ERRATA_MIN_ALIGN:
+ case DOMAIN_ATTR_MMU500_ERRATA_MIN_ALIGN:
*((int *)data) = smmu_domain->qsmmuv500_errata2_min_align;
ret = 0;
break;
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index a65214b..11666fa 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -132,7 +132,7 @@
int min_iova_align = 0;
iommu_domain_get_attr(domain,
- DOMAIN_ATTR_QCOM_MMU500_ERRATA_MIN_ALIGN,
+ DOMAIN_ATTR_MMU500_ERRATA_MIN_ALIGN,
&min_iova_align);
iommu_domain_get_attr(domain, DOMAIN_ATTR_SECURE_VMID, &vmid);
if (vmid >= VMID_LAST || vmid < 0)
diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c
index 73090df..0b1f8ae 100644
--- a/drivers/iommu/dma-mapping-fast.c
+++ b/drivers/iommu/dma-mapping-fast.c
@@ -893,7 +893,7 @@
int min_iova_align = 0;
iommu_domain_get_attr(mapping->domain,
- DOMAIN_ATTR_QCOM_MMU500_ERRATA_MIN_ALIGN,
+ DOMAIN_ATTR_MMU500_ERRATA_MIN_ALIGN,
&min_iova_align);
iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_SECURE_VMID, &vmid);
if (vmid >= VMID_LAST || vmid < 0)
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 30808e9..c7820b3 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -542,7 +542,10 @@
spin_lock_irqsave(&data->lock, flags);
if (is_sysmmu_active(data) && data->version >= MAKE_MMU_VER(3, 3)) {
clk_enable(data->clk_master);
- __sysmmu_tlb_invalidate_entry(data, iova, 1);
+ if (sysmmu_block(data)) {
+ __sysmmu_tlb_invalidate_entry(data, iova, 1);
+ sysmmu_unblock(data);
+ }
clk_disable(data->clk_master);
}
spin_unlock_irqrestore(&data->lock, flags);
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index a3594d2..981172d 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -468,8 +468,12 @@
if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS)
pte |= ARM_LPAE_PTE_NSTABLE;
__arm_lpae_set_pte(ptep, pte, cfg);
- } else {
+ } else if (!iopte_leaf(pte, lvl)) {
cptep = iopte_deref(pte, data);
+ } else {
+ /* We require an unmap first */
+ WARN_ON(!selftest_running);
+ return -EEXIST;
}
/* Rinse, repeat */
diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c
index 1eef56a..05bbf17 100644
--- a/drivers/irqchip/irq-crossbar.c
+++ b/drivers/irqchip/irq-crossbar.c
@@ -198,7 +198,8 @@
static int __init crossbar_of_init(struct device_node *node)
{
- int i, size, max = 0, reserved = 0, entry;
+ int i, size, reserved = 0;
+ u32 max = 0, entry;
const __be32 *irqsr;
int ret = -ENOMEM;
diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c
index bf3fbd0..64b5864 100644
--- a/drivers/isdn/i4l/isdn_ppp.c
+++ b/drivers/isdn/i4l/isdn_ppp.c
@@ -828,7 +828,6 @@
isdn_net_local *lp;
struct ippp_struct *is;
int proto;
- unsigned char protobuf[4];
is = file->private_data;
@@ -842,24 +841,28 @@
if (!lp)
printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n");
else {
- /*
- * Don't reset huptimer for
- * LCP packets. (Echo requests).
- */
- if (copy_from_user(protobuf, buf, 4))
- return -EFAULT;
- proto = PPP_PROTOCOL(protobuf);
- if (proto != PPP_LCP)
- lp->huptimer = 0;
+ if (lp->isdn_device < 0 || lp->isdn_channel < 0) {
+ unsigned char protobuf[4];
+ /*
+ * Don't reset huptimer for
+ * LCP packets. (Echo requests).
+ */
+ if (copy_from_user(protobuf, buf, 4))
+ return -EFAULT;
- if (lp->isdn_device < 0 || lp->isdn_channel < 0)
+ proto = PPP_PROTOCOL(protobuf);
+ if (proto != PPP_LCP)
+ lp->huptimer = 0;
+
return 0;
+ }
if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) &&
lp->dialstate == 0 &&
(lp->flags & ISDN_NET_CONNECTED)) {
unsigned short hl;
struct sk_buff *skb;
+ unsigned char *cpy_buf;
/*
* we need to reserve enough space in front of
* sk_buff. old call to dev_alloc_skb only reserved
@@ -872,11 +875,21 @@
return count;
}
skb_reserve(skb, hl);
- if (copy_from_user(skb_put(skb, count), buf, count))
+ cpy_buf = skb_put(skb, count);
+ if (copy_from_user(cpy_buf, buf, count))
{
kfree_skb(skb);
return -EFAULT;
}
+
+ /*
+ * Don't reset huptimer for
+ * LCP packets. (Echo requests).
+ */
+ proto = PPP_PROTOCOL(cpy_buf);
+ if (proto != PPP_LCP)
+ lp->huptimer = 0;
+
if (is->debug & 0x40) {
printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len);
isdn_ppp_frame_log("xmit", skb->data, skb->len, 32, is->unit, lp->ppp_slot);
diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
index c3ea03c..02619ca 100644
--- a/drivers/md/bcache/bcache.h
+++ b/drivers/md/bcache/bcache.h
@@ -333,6 +333,7 @@
/* Limit number of writeback bios in flight */
struct semaphore in_flight;
struct task_struct *writeback_thread;
+ struct workqueue_struct *writeback_write_wq;
struct keybuf writeback_keys;
diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c
index a37c177..e0f1c6d 100644
--- a/drivers/md/bcache/request.c
+++ b/drivers/md/bcache/request.c
@@ -196,12 +196,12 @@
struct data_insert_op *op = container_of(cl, struct data_insert_op, cl);
struct bio *bio = op->bio, *n;
- if (atomic_sub_return(bio_sectors(bio), &op->c->sectors_to_gc) < 0)
- wake_up_gc(op->c);
-
if (op->bypass)
return bch_data_invalidate(cl);
+ if (atomic_sub_return(bio_sectors(bio), &op->c->sectors_to_gc) < 0)
+ wake_up_gc(op->c);
+
/*
* Journal writes are marked REQ_PREFLUSH; if the original write was a
* flush, it'll wait on the journal write.
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index 66669c8..f4557f5 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -1025,7 +1025,7 @@
}
if (BDEV_STATE(&dc->sb) == BDEV_STATE_DIRTY) {
- bch_sectors_dirty_init(dc);
+ bch_sectors_dirty_init(&dc->disk);
atomic_set(&dc->has_dirty, 1);
atomic_inc(&dc->count);
bch_writeback_queue(dc);
@@ -1058,6 +1058,8 @@
cancel_delayed_work_sync(&dc->writeback_rate_update);
if (!IS_ERR_OR_NULL(dc->writeback_thread))
kthread_stop(dc->writeback_thread);
+ if (dc->writeback_write_wq)
+ destroy_workqueue(dc->writeback_write_wq);
mutex_lock(&bch_register_lock);
@@ -1229,6 +1231,7 @@
goto err;
bcache_device_attach(d, c, u - c->uuids);
+ bch_sectors_dirty_init(d);
bch_flash_dev_request_init(d);
add_disk(d->disk);
@@ -1967,6 +1970,8 @@
else
err = "device busy";
mutex_unlock(&bch_register_lock);
+ if (!IS_ERR(bdev))
+ bdput(bdev);
if (attr == &ksysfs_register_quiet)
goto out;
}
diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c
index b3ff57d..4fbb553 100644
--- a/drivers/md/bcache/sysfs.c
+++ b/drivers/md/bcache/sysfs.c
@@ -191,7 +191,7 @@
{
struct cached_dev *dc = container_of(kobj, struct cached_dev,
disk.kobj);
- unsigned v = size;
+ ssize_t v = size;
struct cache_set *c;
struct kobj_uevent_env *env;
@@ -226,7 +226,7 @@
bch_cached_dev_run(dc);
if (attr == &sysfs_cache_mode) {
- ssize_t v = bch_read_string_list(buf, bch_cache_modes + 1);
+ v = bch_read_string_list(buf, bch_cache_modes + 1);
if (v < 0)
return v;
diff --git a/drivers/md/bcache/util.c b/drivers/md/bcache/util.c
index dde6172..eb70f68 100644
--- a/drivers/md/bcache/util.c
+++ b/drivers/md/bcache/util.c
@@ -73,24 +73,44 @@
STRTO_H(strtoll, long long)
STRTO_H(strtoull, unsigned long long)
+/**
+ * bch_hprint() - formats @v to human readable string for sysfs.
+ *
+ * @v - signed 64 bit integer
+ * @buf - the (at least 8 byte) buffer to format the result into.
+ *
+ * Returns the number of bytes used by format.
+ */
ssize_t bch_hprint(char *buf, int64_t v)
{
static const char units[] = "?kMGTPEZY";
- char dec[4] = "";
- int u, t = 0;
+ int u = 0, t;
- for (u = 0; v >= 1024 || v <= -1024; u++) {
- t = v & ~(~0 << 10);
- v >>= 10;
- }
+ uint64_t q;
- if (!u)
- return sprintf(buf, "%llu", v);
+ if (v < 0)
+ q = -v;
+ else
+ q = v;
- if (v < 100 && v > -100)
- snprintf(dec, sizeof(dec), ".%i", t / 100);
+ /* For as long as the number is more than 3 digits, but at least
+ * once, shift right / divide by 1024. Keep the remainder for
+ * a digit after the decimal point.
+ */
+ do {
+ u++;
- return sprintf(buf, "%lli%s%c", v, dec, units[u]);
+ t = q & ~(~0 << 10);
+ q >>= 10;
+ } while (q >= 1000);
+
+ if (v < 0)
+ /* '-', up to 3 digits, '.', 1 digit, 1 character, null;
+ * yields 8 bytes.
+ */
+ return sprintf(buf, "-%llu.%i%c", q, t * 10 / 1024, units[u]);
+ else
+ return sprintf(buf, "%llu.%i%c", q, t * 10 / 1024, units[u]);
}
ssize_t bch_snprint_string_list(char *buf, size_t size, const char * const list[],
diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c
index e51644e..4ce2b19 100644
--- a/drivers/md/bcache/writeback.c
+++ b/drivers/md/bcache/writeback.c
@@ -20,7 +20,8 @@
static void __update_writeback_rate(struct cached_dev *dc)
{
struct cache_set *c = dc->disk.c;
- uint64_t cache_sectors = c->nbuckets * c->sb.bucket_size;
+ uint64_t cache_sectors = c->nbuckets * c->sb.bucket_size -
+ bcache_flash_devs_sectors_dirty(c);
uint64_t cache_dirty_target =
div_u64(cache_sectors * dc->writeback_percent, 100);
@@ -186,7 +187,7 @@
closure_bio_submit(&io->bio, cl);
- continue_at(cl, write_dirty_finish, system_wq);
+ continue_at(cl, write_dirty_finish, io->dc->writeback_write_wq);
}
static void read_dirty_endio(struct bio *bio)
@@ -206,7 +207,7 @@
closure_bio_submit(&io->bio, cl);
- continue_at(cl, write_dirty, system_wq);
+ continue_at(cl, write_dirty, io->dc->writeback_write_wq);
}
static void read_dirty(struct cached_dev *dc)
@@ -482,17 +483,17 @@
return MAP_CONTINUE;
}
-void bch_sectors_dirty_init(struct cached_dev *dc)
+void bch_sectors_dirty_init(struct bcache_device *d)
{
struct sectors_dirty_init op;
bch_btree_op_init(&op.op, -1);
- op.inode = dc->disk.id;
+ op.inode = d->id;
- bch_btree_map_keys(&op.op, dc->disk.c, &KEY(op.inode, 0, 0),
+ bch_btree_map_keys(&op.op, d->c, &KEY(op.inode, 0, 0),
sectors_dirty_init_fn, 0);
- dc->disk.sectors_dirty_last = bcache_dev_sectors_dirty(&dc->disk);
+ d->sectors_dirty_last = bcache_dev_sectors_dirty(d);
}
void bch_cached_dev_writeback_init(struct cached_dev *dc)
@@ -516,6 +517,11 @@
int bch_cached_dev_writeback_start(struct cached_dev *dc)
{
+ dc->writeback_write_wq = alloc_workqueue("bcache_writeback_wq",
+ WQ_MEM_RECLAIM, 0);
+ if (!dc->writeback_write_wq)
+ return -ENOMEM;
+
dc->writeback_thread = kthread_create(bch_writeback_thread, dc,
"bcache_writeback");
if (IS_ERR(dc->writeback_thread))
diff --git a/drivers/md/bcache/writeback.h b/drivers/md/bcache/writeback.h
index 301eaf5..cdf8d25 100644
--- a/drivers/md/bcache/writeback.h
+++ b/drivers/md/bcache/writeback.h
@@ -14,6 +14,25 @@
return ret;
}
+static inline uint64_t bcache_flash_devs_sectors_dirty(struct cache_set *c)
+{
+ uint64_t i, ret = 0;
+
+ mutex_lock(&bch_register_lock);
+
+ for (i = 0; i < c->nr_uuids; i++) {
+ struct bcache_device *d = c->devices[i];
+
+ if (!d || !UUID_FLASH_ONLY(&c->uuids[i]))
+ continue;
+ ret += bcache_dev_sectors_dirty(d);
+ }
+
+ mutex_unlock(&bch_register_lock);
+
+ return ret;
+}
+
static inline unsigned offset_to_stripe(struct bcache_device *d,
uint64_t offset)
{
@@ -85,7 +104,7 @@
void bcache_dev_sectors_dirty_add(struct cache_set *, unsigned, uint64_t, int);
-void bch_sectors_dirty_init(struct cached_dev *dc);
+void bch_sectors_dirty_init(struct bcache_device *);
void bch_cached_dev_writeback_init(struct cached_dev *);
int bch_cached_dev_writeback_start(struct cached_dev *);
diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c
index 2d82692..fb02c39 100644
--- a/drivers/md/bitmap.c
+++ b/drivers/md/bitmap.c
@@ -1992,6 +1992,11 @@
long pages;
struct bitmap_page *new_bp;
+ if (bitmap->storage.file && !init) {
+ pr_info("md: cannot resize file-based bitmap\n");
+ return -EINVAL;
+ }
+
if (chunksize == 0) {
/* If there is enough space, leave the chunk size unchanged,
* else increase by factor of two until there is enough space.
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index b0c0aef..12abf69 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -223,7 +223,8 @@
* oldconf until no one uses it anymore.
*/
mddev_suspend(mddev);
- oldconf = rcu_dereference(mddev->private);
+ oldconf = rcu_dereference_protected(mddev->private,
+ lockdep_is_held(&mddev->reconfig_mutex));
mddev->raid_disks++;
WARN_ONCE(mddev->raid_disks != newconf->raid_disks,
"copied raid_disks doesn't match mddev->raid_disks");
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 4c4aab0..b19b551 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1407,11 +1407,24 @@
mbio->bi_private = r10_bio;
atomic_inc(&r10_bio->remaining);
+
+ cb = blk_check_plugged(raid10_unplug, mddev,
+ sizeof(*plug));
+ if (cb)
+ plug = container_of(cb, struct raid10_plug_cb,
+ cb);
+ else
+ plug = NULL;
spin_lock_irqsave(&conf->device_lock, flags);
- bio_list_add(&conf->pending_bio_list, mbio);
- conf->pending_count++;
+ if (plug) {
+ bio_list_add(&plug->pending, mbio);
+ plug->pending_cnt++;
+ } else {
+ bio_list_add(&conf->pending_bio_list, mbio);
+ conf->pending_count++;
+ }
spin_unlock_irqrestore(&conf->device_lock, flags);
- if (!mddev_check_plugged(mddev))
+ if (!plug)
md_wakeup_thread(mddev->thread);
}
}
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 549b4af..7aea022 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -829,6 +829,14 @@
spin_unlock(&head->batch_head->batch_lock);
goto unlock_out;
}
+ /*
+ * We must assign batch_head of this stripe within the
+ * batch_lock, otherwise clear_batch_ready of batch head
+ * stripe could clear BATCH_READY bit of this stripe and
+ * this stripe->batch_head doesn't get assigned, which
+ * could confuse clear_batch_ready for this stripe
+ */
+ sh->batch_head = head->batch_head;
/*
* at this point, head's BATCH_READY could be cleared, but we
@@ -836,8 +844,6 @@
*/
list_add(&sh->batch_list, &head->batch_list);
spin_unlock(&head->batch_head->batch_lock);
-
- sh->batch_head = head->batch_head;
} else {
head->batch_head = head;
sh->batch_head = head->batch_head;
@@ -4277,7 +4283,8 @@
set_mask_bits(&sh->state, ~(STRIPE_EXPAND_SYNC_FLAGS |
(1 << STRIPE_PREREAD_ACTIVE) |
- (1 << STRIPE_DEGRADED)),
+ (1 << STRIPE_DEGRADED) |
+ (1 << STRIPE_ON_UNPLUG_LIST)),
head_sh->state & (1 << STRIPE_INSYNC));
sh->check_state = head_sh->check_state;
diff --git a/drivers/media/pci/ttpci/av7110_hw.c b/drivers/media/pci/ttpci/av7110_hw.c
index 0583d56..41ba848 100644
--- a/drivers/media/pci/ttpci/av7110_hw.c
+++ b/drivers/media/pci/ttpci/av7110_hw.c
@@ -56,11 +56,11 @@
by Nathan Laredo <laredo@gnu.org> */
int av7110_debiwrite(struct av7110 *av7110, u32 config,
- int addr, u32 val, int count)
+ int addr, u32 val, unsigned int count)
{
struct saa7146_dev *dev = av7110->dev;
- if (count <= 0 || count > 32764) {
+ if (count > 32764) {
printk("%s: invalid count %d\n", __func__, count);
return -1;
}
@@ -78,12 +78,12 @@
return 0;
}
-u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, int count)
+u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, unsigned int count)
{
struct saa7146_dev *dev = av7110->dev;
u32 result = 0;
- if (count > 32764 || count <= 0) {
+ if (count > 32764) {
printk("%s: invalid count %d\n", __func__, count);
return 0;
}
diff --git a/drivers/media/pci/ttpci/av7110_hw.h b/drivers/media/pci/ttpci/av7110_hw.h
index 1634aba..ccb1480 100644
--- a/drivers/media/pci/ttpci/av7110_hw.h
+++ b/drivers/media/pci/ttpci/av7110_hw.h
@@ -377,14 +377,14 @@
/* DEBI (saa7146 data extension bus interface) access */
extern int av7110_debiwrite(struct av7110 *av7110, u32 config,
- int addr, u32 val, int count);
+ int addr, u32 val, unsigned int count);
extern u32 av7110_debiread(struct av7110 *av7110, u32 config,
- int addr, int count);
+ int addr, unsigned int count);
/* DEBI during interrupt */
/* single word writes */
-static inline void iwdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
+static inline void iwdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count)
{
av7110_debiwrite(av7110, config, addr, val, count);
}
@@ -397,7 +397,7 @@
av7110_debiwrite(av7110, config, addr, 0, count);
}
-static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
+static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count)
{
u32 res;
@@ -408,7 +408,7 @@
}
/* DEBI outside interrupts, only for count <= 4! */
-static inline void wdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
+static inline void wdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count)
{
unsigned long flags;
@@ -417,7 +417,7 @@
spin_unlock_irqrestore(&av7110->debilock, flags);
}
-static inline u32 rdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
+static inline u32 rdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count)
{
unsigned long flags;
u32 res;
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c
index 787bd16..bbb5fee 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.c
+++ b/drivers/media/platform/exynos-gsc/gsc-core.c
@@ -849,9 +849,7 @@
if ((frame->fmt->pixelformat == V4L2_PIX_FMT_VYUY) ||
(frame->fmt->pixelformat == V4L2_PIX_FMT_YVYU) ||
- (frame->fmt->pixelformat == V4L2_PIX_FMT_NV61) ||
(frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420) ||
- (frame->fmt->pixelformat == V4L2_PIX_FMT_NV21) ||
(frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420M))
swap(addr->cb, addr->cr);
diff --git a/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.c b/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.c
index 3fbb3f0..6d699cf 100644
--- a/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.c
+++ b/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.c
@@ -67,11 +67,15 @@
return false;
}
-void cam_cdm_cpas_cb(uint32_t client_handle, void *userdata,
- enum cam_camnoc_irq_type evt_type, uint32_t evt_data)
+bool cam_cdm_cpas_cb(uint32_t client_handle, void *userdata,
+ struct cam_cpas_irq_data *irq_data)
{
- CAM_ERR(CAM_CDM, "CPAS error callback type=%d with data=%x", evt_type,
- evt_data);
+ if (!irq_data)
+ return false;
+
+ CAM_DBG(CAM_CDM, "CPAS error callback type=%d", irq_data->irq_type);
+
+ return false;
}
struct cam_cdm_utils_ops *cam_cdm_get_ops(
diff --git a/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.h b/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.h
index fa3ae04..497832b 100644
--- a/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.h
+++ b/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.h
@@ -32,8 +32,8 @@
uint32_t arg_size);
bool cam_cdm_set_cam_hw_version(
uint32_t ver, struct cam_hw_version *cam_version);
-void cam_cdm_cpas_cb(uint32_t client_handle, void *userdata,
- enum cam_camnoc_irq_type evt_type, uint32_t evt_data);
+bool cam_cdm_cpas_cb(uint32_t client_handle, void *userdata,
+ struct cam_cpas_irq_data *irq_data);
struct cam_cdm_utils_ops *cam_cdm_get_ops(
uint32_t ver, struct cam_hw_version *cam_version, bool by_cam_version);
int cam_virtual_cdm_submit_bl(struct cam_hw_info *cdm_hw,
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
index 4b0cc74..0e5ce85 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
@@ -24,6 +24,18 @@
struct cam_camnoc_info *camnoc_info;
+#define CAMNOC_SLAVE_MAX_ERR_CODE 7
+static const char * const camnoc_salve_err_code[] = {
+ "Target Error", /* err code 0 */
+ "Address decode error", /* err code 1 */
+ "Unsupported request", /* err code 2 */
+ "Disconnected target", /* err code 3 */
+ "Security violation", /* err code 4 */
+ "Hidden security violation", /* err code 5 */
+ "Timeout Error", /* err code 6 */
+ "Unknown Error", /* unknown err code */
+};
+
static int cam_cpastop_get_hw_info(struct cam_hw_info *cpas_hw,
struct cam_cpas_hw_caps *hw_caps)
{
@@ -106,91 +118,155 @@
}
static int cam_cpastop_handle_errlogger(struct cam_cpas *cpas_core,
- struct cam_hw_soc_info *soc_info)
+ struct cam_hw_soc_info *soc_info,
+ struct cam_camnoc_irq_slave_err_data *slave_err)
{
- uint32_t reg_value[4];
- int i;
- int size = camnoc_info->error_logger_size;
int camnoc_index = cpas_core->regbase_index[CAM_CPAS_REG_CAMNOC];
+ int err_code_index = 0;
- for (i = 0; (i + 3) < size; i = i + 4) {
- reg_value[0] = cam_io_r_mb(
- soc_info->reg_map[camnoc_index].mem_base +
- camnoc_info->error_logger[i]);
- reg_value[1] = cam_io_r_mb(
- soc_info->reg_map[camnoc_index].mem_base +
- camnoc_info->error_logger[i + 1]);
- reg_value[2] = cam_io_r_mb(
- soc_info->reg_map[camnoc_index].mem_base +
- camnoc_info->error_logger[i + 2]);
- reg_value[3] = cam_io_r_mb(
- soc_info->reg_map[camnoc_index].mem_base +
- camnoc_info->error_logger[i + 3]);
- CAM_ERR(CAM_CPAS,
- "offset[0x%x] values [0x%x] [0x%x] [0x%x] [0x%x]",
- camnoc_info->error_logger[i], reg_value[0],
- reg_value[1], reg_value[2], reg_value[3]);
+ if (!camnoc_info->err_logger) {
+ CAM_ERR_RATE_LIMIT(CAM_CPAS, "Invalid err logger info");
+ return -EINVAL;
}
- if ((i + 2) < size) {
- reg_value[0] = cam_io_r_mb(
- soc_info->reg_map[camnoc_index].mem_base +
- camnoc_info->error_logger[i]);
- reg_value[1] = cam_io_r_mb(
- soc_info->reg_map[camnoc_index].mem_base +
- camnoc_info->error_logger[i + 1]);
- reg_value[2] = cam_io_r_mb(
- soc_info->reg_map[camnoc_index].mem_base +
- camnoc_info->error_logger[i + 2]);
- CAM_ERR(CAM_CPAS, "offset[0x%x] values [0x%x] [0x%x] [0x%x]",
- camnoc_info->error_logger[i], reg_value[0],
- reg_value[1], reg_value[2]);
- i = i + 3;
- }
+ slave_err->mainctrl.value = cam_io_r_mb(
+ soc_info->reg_map[camnoc_index].mem_base +
+ camnoc_info->err_logger->mainctrl);
- if ((i + 1) < size) {
- reg_value[0] = cam_io_r_mb(
- soc_info->reg_map[camnoc_index].mem_base +
- camnoc_info->error_logger[i]);
- reg_value[1] = cam_io_r_mb(
- soc_info->reg_map[camnoc_index].mem_base +
- camnoc_info->error_logger[i + 1]);
- CAM_ERR(CAM_CPAS, "offset[0x%x] values [0x%x] [0x%x]",
- camnoc_info->error_logger[i], reg_value[0],
- reg_value[1]);
- i = i + 2;
- }
+ slave_err->errvld.value = cam_io_r_mb(
+ soc_info->reg_map[camnoc_index].mem_base +
+ camnoc_info->err_logger->errvld);
- if (i < size) {
- reg_value[0] = cam_io_r_mb(
- soc_info->reg_map[camnoc_index].mem_base +
- camnoc_info->error_logger[i]);
- CAM_ERR(CAM_CPAS, "offset[0x%x] values [0x%x]",
- camnoc_info->error_logger[i], reg_value[0]);
- }
+ slave_err->errlog0_low.value = cam_io_r_mb(
+ soc_info->reg_map[camnoc_index].mem_base +
+ camnoc_info->err_logger->errlog0_low);
+
+ slave_err->errlog0_high.value = cam_io_r_mb(
+ soc_info->reg_map[camnoc_index].mem_base +
+ camnoc_info->err_logger->errlog0_high);
+
+ slave_err->errlog1_low.value = cam_io_r_mb(
+ soc_info->reg_map[camnoc_index].mem_base +
+ camnoc_info->err_logger->errlog1_low);
+
+ slave_err->errlog1_high.value = cam_io_r_mb(
+ soc_info->reg_map[camnoc_index].mem_base +
+ camnoc_info->err_logger->errlog1_high);
+
+ slave_err->errlog2_low.value = cam_io_r_mb(
+ soc_info->reg_map[camnoc_index].mem_base +
+ camnoc_info->err_logger->errlog2_low);
+
+ slave_err->errlog2_high.value = cam_io_r_mb(
+ soc_info->reg_map[camnoc_index].mem_base +
+ camnoc_info->err_logger->errlog2_high);
+
+ slave_err->errlog3_low.value = cam_io_r_mb(
+ soc_info->reg_map[camnoc_index].mem_base +
+ camnoc_info->err_logger->errlog3_low);
+
+ slave_err->errlog3_high.value = cam_io_r_mb(
+ soc_info->reg_map[camnoc_index].mem_base +
+ camnoc_info->err_logger->errlog3_high);
+
+ CAM_ERR_RATE_LIMIT(CAM_CPAS,
+ "Possible memory configuration issue, fault at SMMU raised as CAMNOC SLAVE_IRQ");
+
+ CAM_ERR_RATE_LIMIT(CAM_CPAS,
+ "mainctrl[0x%x 0x%x] errvld[0x%x 0x%x] stall_en=%d, fault_en=%d, err_vld=%d",
+ camnoc_info->err_logger->mainctrl,
+ slave_err->mainctrl.value,
+ camnoc_info->err_logger->errvld,
+ slave_err->errvld.value,
+ slave_err->mainctrl.stall_en,
+ slave_err->mainctrl.fault_en,
+ slave_err->errvld.err_vld);
+
+ err_code_index = slave_err->errlog0_low.err_code;
+ if (err_code_index > CAMNOC_SLAVE_MAX_ERR_CODE)
+ err_code_index = CAMNOC_SLAVE_MAX_ERR_CODE;
+
+ CAM_ERR_RATE_LIMIT(CAM_CPAS,
+ "errlog0 low[0x%x 0x%x] high[0x%x 0x%x] loginfo_vld=%d, word_error=%d, non_secure=%d, device=%d, opc=%d, err_code=%d(%s) sizef=%d, addr_space=%d, len1=%d",
+ camnoc_info->err_logger->errlog0_low,
+ slave_err->errlog0_low.value,
+ camnoc_info->err_logger->errlog0_high,
+ slave_err->errlog0_high.value,
+ slave_err->errlog0_low.loginfo_vld,
+ slave_err->errlog0_low.word_error,
+ slave_err->errlog0_low.non_secure,
+ slave_err->errlog0_low.device,
+ slave_err->errlog0_low.opc,
+ slave_err->errlog0_low.err_code,
+ camnoc_salve_err_code[err_code_index],
+ slave_err->errlog0_low.sizef,
+ slave_err->errlog0_low.addr_space,
+ slave_err->errlog0_high.len1);
+
+ CAM_ERR_RATE_LIMIT(CAM_CPAS,
+ "errlog1_low[0x%x 0x%x] errlog1_high[0x%x 0x%x] errlog2_low[0x%x 0x%x] errlog2_high[0x%x 0x%x] errlog3_low[0x%x 0x%x] errlog3_high[0x%x 0x%x]",
+ camnoc_info->err_logger->errlog1_low,
+ slave_err->errlog1_low.value,
+ camnoc_info->err_logger->errlog1_high,
+ slave_err->errlog1_high.value,
+ camnoc_info->err_logger->errlog2_low,
+ slave_err->errlog2_low.value,
+ camnoc_info->err_logger->errlog2_high,
+ slave_err->errlog2_high.value,
+ camnoc_info->err_logger->errlog3_low,
+ slave_err->errlog3_low.value,
+ camnoc_info->err_logger->errlog3_high,
+ slave_err->errlog3_high.value);
return 0;
}
-static int cam_cpastop_handle_ubwc_err(struct cam_cpas *cpas_core,
- struct cam_hw_soc_info *soc_info, int i)
+static int cam_cpastop_handle_ubwc_enc_err(struct cam_cpas *cpas_core,
+ struct cam_hw_soc_info *soc_info, int i,
+ struct cam_camnoc_irq_ubwc_enc_data *enc_err)
{
- uint32_t reg_value;
int camnoc_index = cpas_core->regbase_index[CAM_CPAS_REG_CAMNOC];
- reg_value = cam_io_r_mb(soc_info->reg_map[camnoc_index].mem_base +
+ enc_err->encerr_status.value =
+ cam_io_r_mb(soc_info->reg_map[camnoc_index].mem_base +
camnoc_info->irq_err[i].err_status.offset);
- CAM_ERR(CAM_CPAS,
- "Dumping ubwc error status [%d]: offset[0x%x] value[0x%x]",
- i, camnoc_info->irq_err[i].err_status.offset, reg_value);
+ /* Let clients handle the UBWC errors */
+ CAM_DBG(CAM_CPAS,
+ "ubwc enc err [%d]: offset[0x%x] value[0x%x]",
+ i, camnoc_info->irq_err[i].err_status.offset,
+ enc_err->encerr_status.value);
- return reg_value;
+ return 0;
}
-static int cam_cpastop_handle_ahb_timeout_err(struct cam_hw_info *cpas_hw)
+static int cam_cpastop_handle_ubwc_dec_err(struct cam_cpas *cpas_core,
+ struct cam_hw_soc_info *soc_info, int i,
+ struct cam_camnoc_irq_ubwc_dec_data *dec_err)
{
- CAM_ERR(CAM_CPAS, "ahb timout error");
+ int camnoc_index = cpas_core->regbase_index[CAM_CPAS_REG_CAMNOC];
+
+ dec_err->decerr_status.value =
+ cam_io_r_mb(soc_info->reg_map[camnoc_index].mem_base +
+ camnoc_info->irq_err[i].err_status.offset);
+
+ /* Let clients handle the UBWC errors */
+ CAM_DBG(CAM_CPAS,
+ "ubwc dec err status [%d]: offset[0x%x] value[0x%x] thr_err=%d, fcl_err=%d, len_md_err=%d, format_err=%d",
+ i, camnoc_info->irq_err[i].err_status.offset,
+ dec_err->decerr_status.value,
+ dec_err->decerr_status.thr_err,
+ dec_err->decerr_status.fcl_err,
+ dec_err->decerr_status.len_md_err,
+ dec_err->decerr_status.format_err);
+
+ return 0;
+}
+
+static int cam_cpastop_handle_ahb_timeout_err(struct cam_hw_info *cpas_hw,
+ struct cam_camnoc_irq_ahb_timeout_data *ahb_err)
+{
+ CAM_ERR_RATE_LIMIT(CAM_CPAS, "ahb timout error");
return 0;
}
@@ -228,10 +304,11 @@
}
static void cam_cpastop_notify_clients(struct cam_cpas *cpas_core,
- enum cam_camnoc_hw_irq_type irq_type, uint32_t irq_data)
+ struct cam_cpas_irq_data *irq_data)
{
int i;
struct cam_cpas_client *cpas_client;
+ bool error_handled = false;
CAM_DBG(CAM_CPAS,
"Notify CB : num_clients=%d, registered=%d, started=%d",
@@ -243,13 +320,15 @@
cpas_client = cpas_core->cpas_client[i];
if (cpas_client->data.cam_cpas_client_cb) {
CAM_DBG(CAM_CPAS,
- "Calling client CB %d : %d 0x%x",
- i, irq_type, irq_data);
- cpas_client->data.cam_cpas_client_cb(
+ "Calling client CB %d : %d",
+ i, irq_data->irq_type);
+ error_handled =
+ cpas_client->data.cam_cpas_client_cb(
cpas_client->data.client_handle,
cpas_client->data.userdata,
- (enum cam_camnoc_irq_type)irq_type,
irq_data);
+ if (error_handled)
+ break;
}
}
}
@@ -263,7 +342,7 @@
struct cam_hw_soc_info *soc_info;
int i;
enum cam_camnoc_hw_irq_type irq_type;
- uint32_t irq_data;
+ struct cam_cpas_irq_data irq_data;
payload = container_of(work, struct cam_cpas_work_payload, work);
if (!payload) {
@@ -280,23 +359,30 @@
(camnoc_info->irq_err[i].enable)) {
irq_type = camnoc_info->irq_err[i].irq_type;
CAM_ERR(CAM_CPAS, "Error occurred, type=%d", irq_type);
- irq_data = 0;
+ memset(&irq_data, 0x0, sizeof(irq_data));
+ irq_data.irq_type = (enum cam_camnoc_irq_type)irq_type;
switch (irq_type) {
case CAM_CAMNOC_HW_IRQ_SLAVE_ERROR:
- irq_data = cam_cpastop_handle_errlogger(
- cpas_core, soc_info);
+ cam_cpastop_handle_errlogger(
+ cpas_core, soc_info,
+ &irq_data.u.slave_err);
break;
case CAM_CAMNOC_HW_IRQ_IFE02_UBWC_ENCODE_ERROR:
case CAM_CAMNOC_HW_IRQ_IFE13_UBWC_ENCODE_ERROR:
- case CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_DECODE_ERROR:
case CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_ENCODE_ERROR:
- irq_data = cam_cpastop_handle_ubwc_err(
- cpas_core, soc_info, i);
+ cam_cpastop_handle_ubwc_enc_err(
+ cpas_core, soc_info, i,
+ &irq_data.u.enc_err);
+ break;
+ case CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_DECODE_ERROR:
+ cam_cpastop_handle_ubwc_dec_err(
+ cpas_core, soc_info, i,
+ &irq_data.u.dec_err);
break;
case CAM_CAMNOC_HW_IRQ_AHB_TIMEOUT:
- irq_data = cam_cpastop_handle_ahb_timeout_err(
- cpas_hw);
+ cam_cpastop_handle_ahb_timeout_err(
+ cpas_hw, &irq_data.u.ahb_err);
break;
case CAM_CAMNOC_HW_IRQ_CAMNOC_TEST:
CAM_DBG(CAM_CPAS, "TEST IRQ");
@@ -306,8 +392,7 @@
break;
}
- cam_cpastop_notify_clients(cpas_core, irq_type,
- irq_data);
+ cam_cpastop_notify_clients(cpas_core, &irq_data);
payload->irq_status &=
~camnoc_info->irq_err[i].sbm_port;
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
index e3639a6..73f7e9b 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
@@ -173,6 +173,34 @@
};
/**
+ * struct cam_camnoc_err_logger_info : CAMNOC error logger register offsets
+ *
+ * @mainctrl: Register offset for mainctrl
+ * @errvld: Register offset for errvld
+ * @errlog0_low: Register offset for errlog0_low
+ * @errlog0_high: Register offset for errlog0_high
+ * @errlog1_low: Register offset for errlog1_low
+ * @errlog1_high: Register offset for errlog1_high
+ * @errlog2_low: Register offset for errlog2_low
+ * @errlog2_high: Register offset for errlog2_high
+ * @errlog3_low: Register offset for errlog3_low
+ * @errlog3_high: Register offset for errlog3_high
+ *
+ */
+struct cam_camnoc_err_logger_info {
+ uint32_t mainctrl;
+ uint32_t errvld;
+ uint32_t errlog0_low;
+ uint32_t errlog0_high;
+ uint32_t errlog1_low;
+ uint32_t errlog1_high;
+ uint32_t errlog2_low;
+ uint32_t errlog2_high;
+ uint32_t errlog3_low;
+ uint32_t errlog3_high;
+};
+
+/**
* struct cam_camnoc_info : Overall CAMNOC settings info
*
* @specific: Pointer to CAMNOC SPECIFICTONTTPTR settings
@@ -180,8 +208,7 @@
* @irq_sbm: Pointer to CAMNOC IRQ SBM settings
* @irq_err: Pointer to CAMNOC IRQ Error settings
* @irq_err_size: Array size of IRQ Error settings
- * @error_logger: Pointer to CAMNOC IRQ Error logger read registers
- * @error_logger_size: Array size of IRQ Error logger
+ * @err_logger: Pointer to CAMNOC IRQ Error logger read registers
* @errata_wa_list: HW Errata workaround info
*
*/
@@ -191,8 +218,7 @@
struct cam_camnoc_irq_sbm *irq_sbm;
struct cam_camnoc_irq_err *irq_err;
int irq_err_size;
- uint32_t *error_logger;
- int error_logger_size;
+ struct cam_camnoc_err_logger_info *err_logger;
struct cam_cpas_hw_errata_wa_list *errata_wa_list;
};
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop100.h b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop100.h
index b30cd05..2654b47 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop100.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop100.h
@@ -498,19 +498,17 @@
}
};
-uint32_t slave_error_logger[] = {
- 0x2700, /* ERRLOGGER_SWID_LOW */
- 0x2704, /* ERRLOGGER_SWID_HIGH */
- 0x2708, /* ERRLOGGER_MAINCTL_LOW */
- 0x2710, /* ERRLOGGER_ERRVLD_LOW */
- 0x2720, /* ERRLOGGER_ERRLOG0_LOW */
- 0x2724, /* ERRLOGGER_ERRLOG0_HIGH */
- 0x2728, /* ERRLOGGER_ERRLOG1_LOW */
- 0x272c, /* ERRLOGGER_ERRLOG1_HIGH */
- 0x2730, /* ERRLOGGER_ERRLOG2_LOW */
- 0x2734, /* ERRLOGGER_ERRLOG2_HIGH */
- 0x2738, /* ERRLOGGER_ERRLOG3_LOW */
- 0x273c, /* ERRLOGGER_ERRLOG3_HIGH */
+static struct cam_camnoc_err_logger_info cam170_cpas100_err_logger_offsets = {
+ .mainctrl = 0x2708, /* ERRLOGGER_MAINCTL_LOW */
+ .errvld = 0x2710, /* ERRLOGGER_ERRVLD_LOW */
+ .errlog0_low = 0x2720, /* ERRLOGGER_ERRLOG0_LOW */
+ .errlog0_high = 0x2724, /* ERRLOGGER_ERRLOG0_HIGH */
+ .errlog1_low = 0x2728, /* ERRLOGGER_ERRLOG1_LOW */
+ .errlog1_high = 0x272c, /* ERRLOGGER_ERRLOG1_HIGH */
+ .errlog2_low = 0x2730, /* ERRLOGGER_ERRLOG2_LOW */
+ .errlog2_high = 0x2734, /* ERRLOGGER_ERRLOG2_HIGH */
+ .errlog3_low = 0x2738, /* ERRLOGGER_ERRLOG3_LOW */
+ .errlog3_high = 0x273c, /* ERRLOGGER_ERRLOG3_HIGH */
};
static struct cam_cpas_hw_errata_wa_list cam170_cpas100_errata_wa_list = {
@@ -533,9 +531,7 @@
.irq_err = &cam_cpas100_irq_err[0],
.irq_err_size = sizeof(cam_cpas100_irq_err) /
sizeof(cam_cpas100_irq_err[0]),
- .error_logger = &slave_error_logger[0],
- .error_logger_size = sizeof(slave_error_logger) /
- sizeof(slave_error_logger[0]),
+ .err_logger = &cam170_cpas100_err_logger_offsets,
.errata_wa_list = &cam170_cpas100_errata_wa_list,
};
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop_v170_110.h b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop_v170_110.h
index b1aef1f..4418fb1 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop_v170_110.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop_v170_110.h
@@ -505,19 +505,17 @@
},
};
-static uint32_t cam_cpas110_slave_error_logger[] = {
- 0x2700, /* ERRLOGGER_SWID_LOW */
- 0x2704, /* ERRLOGGER_SWID_HIGH */
- 0x2708, /* ERRLOGGER_MAINCTL_LOW */
- 0x2710, /* ERRLOGGER_ERRVLD_LOW */
- 0x2720, /* ERRLOGGER_ERRLOG0_LOW */
- 0x2724, /* ERRLOGGER_ERRLOG0_HIGH */
- 0x2728, /* ERRLOGGER_ERRLOG1_LOW */
- 0x272c, /* ERRLOGGER_ERRLOG1_HIGH */
- 0x2730, /* ERRLOGGER_ERRLOG2_LOW */
- 0x2734, /* ERRLOGGER_ERRLOG2_HIGH */
- 0x2738, /* ERRLOGGER_ERRLOG3_LOW */
- 0x273c, /* ERRLOGGER_ERRLOG3_HIGH */
+static struct cam_camnoc_err_logger_info cam170_cpas110_err_logger_offsets = {
+ .mainctrl = 0x2708, /* ERRLOGGER_MAINCTL_LOW */
+ .errvld = 0x2710, /* ERRLOGGER_ERRVLD_LOW */
+ .errlog0_low = 0x2720, /* ERRLOGGER_ERRLOG0_LOW */
+ .errlog0_high = 0x2724, /* ERRLOGGER_ERRLOG0_HIGH */
+ .errlog1_low = 0x2728, /* ERRLOGGER_ERRLOG1_LOW */
+ .errlog1_high = 0x272c, /* ERRLOGGER_ERRLOG1_HIGH */
+ .errlog2_low = 0x2730, /* ERRLOGGER_ERRLOG2_LOW */
+ .errlog2_high = 0x2734, /* ERRLOGGER_ERRLOG2_HIGH */
+ .errlog3_low = 0x2738, /* ERRLOGGER_ERRLOG3_LOW */
+ .errlog3_high = 0x273c, /* ERRLOGGER_ERRLOG3_HIGH */
};
static struct cam_cpas_hw_errata_wa_list cam170_cpas110_errata_wa_list = {
@@ -540,9 +538,7 @@
.irq_err = &cam_cpas110_irq_err[0],
.irq_err_size = sizeof(cam_cpas110_irq_err) /
sizeof(cam_cpas110_irq_err[0]),
- .error_logger = &cam_cpas110_slave_error_logger[0],
- .error_logger_size = sizeof(cam_cpas110_slave_error_logger) /
- sizeof(cam_cpas110_slave_error_logger[0]),
+ .err_logger = &cam170_cpas110_err_logger_offsets,
.errata_wa_list = &cam170_cpas110_errata_wa_list,
};
diff --git a/drivers/media/platform/msm/camera/cam_cpas/include/cam_cpas_api.h b/drivers/media/platform/msm/camera/cam_cpas/include/cam_cpas_api.h
index e0da384..c844ef7 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/include/cam_cpas_api.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/include/cam_cpas_api.h
@@ -82,6 +82,183 @@
};
/**
+ * struct cam_camnoc_irq_slave_err_data : Data for Slave error.
+ *
+ * @mainctrl : Err logger mainctrl info
+ * @errvld : Err logger errvld info
+ * @errlog0_low : Err logger errlog0_low info
+ * @errlog0_high : Err logger errlog0_high info
+ * @errlog1_low : Err logger errlog1_low info
+ * @errlog1_high : Err logger errlog1_high info
+ * @errlog2_low : Err logger errlog2_low info
+ * @errlog2_high : Err logger errlog2_high info
+ * @errlog3_low : Err logger errlog3_low info
+ * @errlog3_high : Err logger errlog3_high info
+ *
+ */
+struct cam_camnoc_irq_slave_err_data {
+ union {
+ struct {
+ uint32_t stall_en : 1; /* bit 0 */
+ uint32_t fault_en : 1; /* bit 1 */
+ uint32_t rsv : 30; /* bits 2-31 */
+ };
+ uint32_t value;
+ } mainctrl;
+ union {
+ struct {
+ uint32_t err_vld : 1; /* bit 0 */
+ uint32_t rsv : 31; /* bits 1-31 */
+ };
+ uint32_t value;
+ } errvld;
+ union {
+ struct {
+ uint32_t loginfo_vld : 1; /* bit 0 */
+ uint32_t word_error : 1; /* bit 1 */
+ uint32_t non_secure : 1; /* bit 2 */
+ uint32_t device : 1; /* bit 3 */
+ uint32_t opc : 3; /* bits 4 - 6 */
+ uint32_t rsv0 : 1; /* bit 7 */
+ uint32_t err_code : 3; /* bits 8 - 10 */
+ uint32_t sizef : 3; /* bits 11 - 13 */
+ uint32_t rsv1 : 2; /* bits 14 - 15 */
+ uint32_t addr_space : 6; /* bits 16 - 21 */
+ uint32_t rsv2 : 10; /* bits 22 - 31 */
+ };
+ uint32_t value;
+ } errlog0_low;
+ union {
+ struct {
+ uint32_t len1 : 10; /* bits 0 - 9 */
+ uint32_t rsv : 22; /* bits 10 - 31 */
+ };
+ uint32_t value;
+ } errlog0_high;
+ union {
+ struct {
+ uint32_t path : 16; /* bits 0 - 15 */
+ uint32_t rsv : 16; /* bits 16 - 31 */
+ };
+ uint32_t value;
+ } errlog1_low;
+ union {
+ struct {
+ uint32_t extid : 18; /* bits 0 - 17 */
+ uint32_t rsv : 14; /* bits 18 - 31 */
+ };
+ uint32_t value;
+ } errlog1_high;
+ union {
+ struct {
+ uint32_t errlog2_lsb : 32; /* bits 0 - 31 */
+ };
+ uint32_t value;
+ } errlog2_low;
+ union {
+ struct {
+ uint32_t errlog2_msb : 16; /* bits 0 - 16 */
+ uint32_t rsv : 16; /* bits 16 - 31 */
+ };
+ uint32_t value;
+ } errlog2_high;
+ union {
+ struct {
+ uint32_t errlog3_lsb : 32; /* bits 0 - 31 */
+ };
+ uint32_t value;
+ } errlog3_low;
+ union {
+ struct {
+ uint32_t errlog3_msb : 32; /* bits 0 - 31 */
+ };
+ uint32_t value;
+ } errlog3_high;
+};
+
+/**
+ * struct cam_camnoc_irq_ubwc_enc_data : Data for UBWC Encode error.
+ *
+ * @encerr_status : Encode error status
+ *
+ */
+struct cam_camnoc_irq_ubwc_enc_data {
+ union {
+ struct {
+ uint32_t encerrstatus : 3; /* bits 0 - 2 */
+ uint32_t rsv : 29; /* bits 3 - 31 */
+ };
+ uint32_t value;
+ } encerr_status;
+};
+
+/**
+ * struct cam_camnoc_irq_ubwc_dec_data : Data for UBWC Decode error.
+ *
+ * @decerr_status : Decoder error status
+ * @thr_err : Set to 1 if
+ * At least one of the bflc_len fields in the bit steam exceeds
+ * its threshold value. This error is possible only for
+ * RGBA1010102, TP10, and RGB565 formats
+ * @fcl_err : Set to 1 if
+ * Fast clear with a legal non-RGB format
+ * @len_md_err : Set to 1 if
+ * The calculated burst length does not match burst length
+ * specified by the metadata value
+ * @format_err : Set to 1 if
+ * Illegal format
+ * 1. bad format :2,3,6
+ * 2. For 32B MAL, metadata=6
+ * 3. For 32B MAL RGB565, Metadata != 0,1,7
+ * 4. For 64B MAL RGB565, metadata[3:1] == 1,2
+ *
+ */
+struct cam_camnoc_irq_ubwc_dec_data {
+ union {
+ struct {
+ uint32_t thr_err : 1; /* bit 0 */
+ uint32_t fcl_err : 1; /* bit 1 */
+ uint32_t len_md_err : 1; /* bit 2 */
+ uint32_t format_err : 1; /* bit 3 */
+ uint32_t rsv : 28; /* bits 4 - 31 */
+ };
+ uint32_t value;
+ } decerr_status;
+};
+
+struct cam_camnoc_irq_ahb_timeout_data {
+ uint32_t data;
+};
+
+/**
+ * struct cam_cpas_irq_data : CAMNOC IRQ data
+ *
+ * @irq_type : To identify the type of IRQ
+ * @u : Union of irq err data information
+ * @slave_err : Data for Slave error.
+ * Valid if type is CAM_CAMNOC_IRQ_SLAVE_ERROR
+ * @enc_err : Data for UBWC Encode error.
+ * Valid if type is one of below:
+ * CAM_CAMNOC_IRQ_IFE02_UBWC_ENCODE_ERROR
+ * CAM_CAMNOC_IRQ_IFE13_UBWC_ENCODE_ERROR
+ * CAM_CAMNOC_IRQ_IPE_BPS_UBWC_ENCODE_ERROR
+ * @dec_err : Data for UBWC Decode error.
+ * Valid if type is CAM_CAMNOC_IRQ_IPE_BPS_UBWC_DECODE_ERROR
+ * @ahb_err : Data for Slave error.
+ * Valid if type is CAM_CAMNOC_IRQ_AHB_TIMEOUT
+ *
+ */
+struct cam_cpas_irq_data {
+ enum cam_camnoc_irq_type irq_type;
+ union {
+ struct cam_camnoc_irq_slave_err_data slave_err;
+ struct cam_camnoc_irq_ubwc_enc_data enc_err;
+ struct cam_camnoc_irq_ubwc_dec_data dec_err;
+ struct cam_camnoc_irq_ahb_timeout_data ahb_err;
+ } u;
+};
+
+/**
* struct cam_cpas_register_params : Register params for cpas client
*
* @identifier : Input identifier string which is the device label
@@ -107,11 +284,10 @@
uint32_t cell_index;
struct device *dev;
void *userdata;
- void (*cam_cpas_client_cb)(
+ bool (*cam_cpas_client_cb)(
uint32_t client_handle,
void *userdata,
- enum cam_camnoc_irq_type event_type,
- uint32_t event_data);
+ struct cam_cpas_irq_data *irq_data);
uint32_t client_handle;
};
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.c b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.c
index 9045dc1..f27d016 100644
--- a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.c
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.c
@@ -20,11 +20,16 @@
#include "cam_fd_hw_core.h"
#include "cam_fd_hw_soc.h"
-static void cam_fd_hw_util_cpas_callback(uint32_t handle, void *userdata,
- enum cam_camnoc_irq_type event_type, uint32_t event_data)
+static bool cam_fd_hw_util_cpas_callback(uint32_t handle, void *userdata,
+ struct cam_cpas_irq_data *irq_data)
{
- CAM_DBG(CAM_FD, "CPAS hdl=%d, udata=%pK, event=%d, event_data=%d",
- handle, userdata, event_type, event_data);
+ if (!irq_data)
+ return false;
+
+ CAM_DBG(CAM_FD, "CPAS hdl=%d, udata=%pK, irq_type=%d",
+ handle, userdata, irq_data->irq_type);
+
+ return false;
}
static int cam_fd_hw_soc_util_setup_regbase_indices(
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 e892772..ce7a8b3 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
@@ -60,10 +60,12 @@
* hfi_read_message() - function for hfi read
* @pmsg: buffer to place read message for hfi queue
* @q_id: queue id
+ * @words_read: total number of words read from the queue
+ * returned as output to the caller
*
- * Returns size read in words/failure(negative value)
+ * Returns success(zero)/failure(non zero)
*/
-int64_t hfi_read_message(uint32_t *pmsg, uint8_t q_id);
+int hfi_read_message(uint32_t *pmsg, uint8_t q_id, uint32_t *words_read);
/**
* hfi_init() - function initialize hfi after firmware download
diff --git a/drivers/media/platform/msm/camera/cam_icp/hfi.c b/drivers/media/platform/msm/camera/cam_icp/hfi.c
index 16fa33a..e51d350 100644
--- a/drivers/media/platform/msm/camera/cam_icp/hfi.c
+++ b/drivers/media/platform/msm/camera/cam_icp/hfi.c
@@ -117,13 +117,14 @@
return rc;
}
-int64_t hfi_read_message(uint32_t *pmsg, uint8_t q_id)
+int hfi_read_message(uint32_t *pmsg, uint8_t q_id,
+ uint32_t *words_read)
{
struct hfi_qtbl *q_tbl_ptr;
struct hfi_q_hdr *q;
uint32_t new_read_idx, size_in_words, word_diff, temp;
uint32_t *read_q, *read_ptr, *write_ptr;
- int64_t rc = 0;
+ int rc = 0;
if (!pmsg) {
CAM_ERR(CAM_HFI, "Invalid msg");
@@ -202,7 +203,7 @@
}
q->qhdr_read_idx = new_read_idx;
- rc = size_in_words;
+ *words_read = size_in_words;
err:
mutex_unlock(&hfi_msg_q_mutex);
return rc;
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_dev.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_dev.c
index 99e2e79..14c3c9c 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_dev.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_dev.c
@@ -50,6 +50,40 @@
};
EXPORT_SYMBOL(cam_a5_hw_info);
+static bool cam_a5_cpas_cb(uint32_t client_handle, void *userdata,
+ struct cam_cpas_irq_data *irq_data)
+{
+ bool error_handled = false;
+
+ if (!irq_data)
+ return error_handled;
+
+ switch (irq_data->irq_type) {
+ case CAM_CAMNOC_IRQ_IPE_BPS_UBWC_DECODE_ERROR:
+ CAM_ERR_RATE_LIMIT(CAM_ICP,
+ "IPE/BPS UBWC Decode error type=%d status=%x thr_err=%d, fcl_err=%d, len_md_err=%d, format_err=%d",
+ irq_data->irq_type,
+ irq_data->u.dec_err.decerr_status.value,
+ irq_data->u.dec_err.decerr_status.thr_err,
+ irq_data->u.dec_err.decerr_status.fcl_err,
+ irq_data->u.dec_err.decerr_status.len_md_err,
+ irq_data->u.dec_err.decerr_status.format_err);
+ error_handled = true;
+ break;
+ case CAM_CAMNOC_IRQ_IPE_BPS_UBWC_ENCODE_ERROR:
+ CAM_ERR_RATE_LIMIT(CAM_ICP,
+ "IPE/BPS UBWC Encode error type=%d status=%x",
+ irq_data->irq_type,
+ irq_data->u.enc_err.encerr_status.value);
+ error_handled = true;
+ break;
+ default:
+ break;
+ }
+
+ return error_handled;
+}
+
int cam_a5_register_cpas(struct cam_hw_soc_info *soc_info,
struct cam_a5_device_core_info *core_info,
uint32_t hw_idx)
@@ -59,7 +93,7 @@
cpas_register_params.dev = &soc_info->pdev->dev;
memcpy(cpas_register_params.identifier, "icp", sizeof("icp"));
- cpas_register_params.cam_cpas_client_cb = NULL;
+ cpas_register_params.cam_cpas_client_cb = cam_a5_cpas_cb;
cpas_register_params.cell_index = hw_idx;
cpas_register_params.userdata = NULL;
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 72f2803..93926a78 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
@@ -1150,11 +1150,12 @@
{
uint32_t *msg_ptr = NULL, *pkt_ptr = NULL;
struct hfi_msg_debug *dbg_msg;
- int64_t read_len, size_processed = 0;
+ uint32_t read_len, size_processed = 0;
char *dbg_buf;
+ int rc = 0;
- read_len = hfi_read_message(icp_hw_mgr.dbg_buf, Q_DBG);
- if (read_len < 0)
+ rc = hfi_read_message(icp_hw_mgr.dbg_buf, Q_DBG, &read_len);
+ if (rc)
return;
msg_ptr = (uint32_t *)icp_hw_mgr.dbg_buf;
@@ -1179,7 +1180,8 @@
static int cam_icp_process_msg_pkt_type(
struct cam_icp_hw_mgr *hw_mgr,
- uint32_t *msg_ptr)
+ uint32_t *msg_ptr,
+ uint32_t *msg_processed_len)
{
int rc = 0;
int size_processed = 0;
@@ -1230,19 +1232,17 @@
break;
}
- if (rc)
- return rc;
-
- return size_processed;
+ *msg_processed_len = size_processed;
+ return rc;
}
static int32_t cam_icp_mgr_process_msg(void *priv, void *data)
{
- int64_t read_len, msg_processed_len;
- int rc = 0;
+ uint32_t read_len, msg_processed_len;
uint32_t *msg_ptr = NULL;
struct hfi_msg_work_data *task_data;
struct cam_icp_hw_mgr *hw_mgr;
+ int rc = 0;
if (!data || !priv) {
CAM_ERR(CAM_ICP, "Invalid data");
@@ -1252,25 +1252,24 @@
task_data = data;
hw_mgr = priv;
- read_len = hfi_read_message(icp_hw_mgr.msg_buf, Q_MSG);
- if (read_len < 0) {
- rc = read_len;
+ rc = hfi_read_message(icp_hw_mgr.msg_buf, Q_MSG, &read_len);
+ if (rc) {
CAM_DBG(CAM_ICP, "Unable to read msg q");
} else {
read_len = read_len << BYTE_WORD_SHIFT;
msg_ptr = (uint32_t *)icp_hw_mgr.msg_buf;
while (true) {
- msg_processed_len = cam_icp_process_msg_pkt_type(
- hw_mgr, msg_ptr);
- if (msg_processed_len < 0) {
- rc = msg_processed_len;
+ rc = cam_icp_process_msg_pkt_type(hw_mgr, msg_ptr,
+ &msg_processed_len);
+ if (rc)
return rc;
- }
read_len -= msg_processed_len;
- if (read_len > 0)
+ if (read_len > 0) {
msg_ptr += (msg_processed_len >>
BYTE_WORD_SHIFT);
+ msg_processed_len = 0;
+ }
else
break;
}
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
index 8537637..ae6b149 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
@@ -427,6 +427,13 @@
return rc;
}
+static int __cam_isp_ctx_reg_upd_in_hw_error(
+ struct cam_isp_context *ctx_isp, void *evt_data)
+{
+ ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF;
+ return 0;
+}
+
static int __cam_isp_ctx_sof_in_activated_state(
struct cam_isp_context *ctx_isp, void *evt_data)
{
@@ -689,8 +696,13 @@
void *evt_data)
{
int rc = 0;
- struct cam_ctx_request *req;
+ uint32_t i = 0;
+ bool found = 0;
+ struct cam_ctx_request *req = NULL;
+ struct cam_ctx_request *req_temp;
+ struct cam_isp_ctx_req *req_isp = NULL;
struct cam_req_mgr_error_notify notify;
+ uint64_t error_request_id;
struct cam_context *ctx = ctx_isp->base;
struct cam_isp_hw_error_event_data *error_event_data =
@@ -701,7 +713,7 @@
CAM_DBG(CAM_ISP, "Enter error_type = %d", error_type);
if ((error_type == CAM_ISP_HW_ERROR_OVERFLOW) ||
(error_type == CAM_ISP_HW_ERROR_BUSIF_OVERFLOW))
- notify.error = CRM_KMD_ERR_FATAL;
+ notify.error = CRM_KMD_ERR_OVERFLOW;
/*
* Need to check the active req
@@ -712,31 +724,92 @@
if (list_empty(&ctx->active_req_list)) {
CAM_ERR_RATE_LIMIT(CAM_ISP,
"handling error with no active request");
- rc = -EINVAL;
- goto end;
+ } else {
+ list_for_each_entry_safe(req, req_temp,
+ &ctx->active_req_list, list) {
+ req_isp = (struct cam_isp_ctx_req *) req->req_priv;
+ if (!req_isp->bubble_report) {
+ for (i = 0; i < req_isp->num_fence_map_out;
+ i++) {
+ CAM_ERR(CAM_ISP, "req %llu, Sync fd %x",
+ req->request_id,
+ req_isp->fence_map_out[i].
+ sync_id);
+ if (req_isp->fence_map_out[i].sync_id
+ != -1) {
+ rc = cam_sync_signal(
+ req_isp->fence_map_out[i].
+ sync_id,
+ CAM_SYNC_STATE_SIGNALED_ERROR);
+ req_isp->fence_map_out[i].
+ sync_id = -1;
+ }
+ }
+ list_del_init(&req->list);
+ list_add_tail(&req->list, &ctx->free_req_list);
+ ctx_isp->active_req_cnt--;
+ } else {
+ found = 1;
+ break;
+ }
+ }
}
- req = list_first_entry(&ctx->active_req_list,
- struct cam_ctx_request, list);
+ if (found) {
+ list_for_each_entry_safe_reverse(req, req_temp,
+ &ctx->active_req_list, list) {
+ req_isp = (struct cam_isp_ctx_req *) req->req_priv;
+ list_del_init(&req->list);
+ list_add(&req->list, &ctx->pending_req_list);
+ ctx_isp->active_req_cnt--;
+ }
+ }
+
+ do {
+ if (list_empty(&ctx->pending_req_list)) {
+ error_request_id = ctx_isp->last_applied_req_id + 1;
+ req_isp = NULL;
+ break;
+ }
+ req = list_first_entry(&ctx->pending_req_list,
+ struct cam_ctx_request, list);
+ req_isp = (struct cam_isp_ctx_req *) req->req_priv;
+ error_request_id = ctx_isp->last_applied_req_id;
+
+ if (req_isp->bubble_report)
+ break;
+
+ for (i = 0; i < req_isp->num_fence_map_out; i++) {
+ if (req_isp->fence_map_out[i].sync_id != -1)
+ rc = cam_sync_signal(
+ req_isp->fence_map_out[i].sync_id,
+ CAM_SYNC_STATE_SIGNALED_ERROR);
+ req_isp->fence_map_out[i].sync_id = -1;
+ }
+ list_del_init(&req->list);
+ list_add_tail(&req->list, &ctx->free_req_list);
+
+ } while (req->request_id < ctx_isp->last_applied_req_id);
+
if (ctx->ctx_crm_intf && ctx->ctx_crm_intf->notify_err) {
notify.link_hdl = ctx->link_hdl;
notify.dev_hdl = ctx->dev_hdl;
- notify.req_id = req->request_id;
+ notify.req_id = error_request_id;
+
+ if (req_isp && req_isp->bubble_report)
+ notify.error = CRM_KMD_ERR_BUBBLE;
+
+ CAM_WARN(CAM_ISP, "Notify CRM: req %lld, frame %lld\n",
+ error_request_id, ctx_isp->frame_id);
ctx->ctx_crm_intf->notify_err(¬ify);
- CAM_ERR_RATE_LIMIT(CAM_ISP, "Notify CRM about ERROR frame %lld",
- ctx_isp->frame_id);
+ ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_HW_ERROR;
} else {
CAM_ERR_RATE_LIMIT(CAM_ISP, "Can not notify ERRROR to CRM");
rc = -EFAULT;
}
- list_del_init(&req->list);
- list_add(&req->list, &ctx->pending_req_list);
- /* might need to check if active list is empty */
-
-end:
CAM_DBG(CAM_ISP, "Exit");
return rc;
}
@@ -746,7 +819,7 @@
/* SOF */
{
.irq_ops = {
- NULL,
+ __cam_isp_ctx_handle_error,
__cam_isp_ctx_sof_in_activated_state,
__cam_isp_ctx_reg_upd_in_sof,
__cam_isp_ctx_notify_sof_in_actived_state,
@@ -779,7 +852,7 @@
/* BUBBLE */
{
.irq_ops = {
- NULL,
+ __cam_isp_ctx_handle_error,
__cam_isp_ctx_sof_in_activated_state,
NULL,
__cam_isp_ctx_notify_sof_in_actived_state,
@@ -790,7 +863,7 @@
/* Bubble Applied */
{
.irq_ops = {
- NULL,
+ __cam_isp_ctx_handle_error,
__cam_isp_ctx_sof_in_activated_state,
__cam_isp_ctx_reg_upd_in_activated_state,
__cam_isp_ctx_epoch_in_bubble_applied,
@@ -798,6 +871,17 @@
__cam_isp_ctx_buf_done_in_bubble_applied,
},
},
+ /* HW ERROR */
+ {
+ .irq_ops = {
+ NULL,
+ __cam_isp_ctx_sof_in_activated_state,
+ __cam_isp_ctx_reg_upd_in_hw_error,
+ NULL,
+ NULL,
+ NULL,
+ },
+ },
/* HALT */
{
},
@@ -878,7 +962,9 @@
} else {
spin_lock_bh(&ctx->lock);
ctx_isp->substate_activated = next_state;
- CAM_DBG(CAM_ISP, "new state %d", next_state);
+ ctx_isp->last_applied_req_id = apply->request_id;
+ CAM_DBG(CAM_ISP, "new substate state %d, applied req %lld",
+ next_state, ctx_isp->last_applied_req_id);
spin_unlock_bh(&ctx->lock);
}
end:
@@ -2005,6 +2091,24 @@
return rc;
}
+static int __cam_isp_ctx_unlink_in_activated(struct cam_context *ctx,
+ struct cam_req_mgr_core_dev_link_setup *unlink)
+{
+ int rc = 0;
+
+ CAM_WARN(CAM_ISP,
+ "Received unlink in activated state. It's unexpected");
+ rc = __cam_isp_ctx_stop_dev_in_activated_unlock(ctx);
+ if (rc)
+ CAM_WARN(CAM_ISP, "Stop device failed rc=%d", rc);
+
+ rc = __cam_isp_ctx_unlink_in_ready(ctx, unlink);
+ if (rc)
+ CAM_ERR(CAM_ISP, "Unlink failed rc=%d", rc);
+
+ return rc;
+}
+
static int __cam_isp_ctx_apply_req(struct cam_context *ctx,
struct cam_req_mgr_apply_request *apply)
{
@@ -2116,6 +2220,7 @@
.config_dev = __cam_isp_ctx_config_dev_in_top_state,
},
.crm_ops = {
+ .unlink = __cam_isp_ctx_unlink_in_activated,
.apply_req = __cam_isp_ctx_apply_req,
.flush_req = __cam_isp_ctx_flush_req_in_top_state,
},
@@ -2184,4 +2289,3 @@
memset(ctx, 0, sizeof(*ctx));
return rc;
}
-
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
index cec0e80..347290c 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
@@ -50,6 +50,7 @@
CAM_ISP_CTX_ACTIVATED_EPOCH,
CAM_ISP_CTX_ACTIVATED_BUBBLE,
CAM_ISP_CTX_ACTIVATED_BUBBLE_APPLIED,
+ CAM_ISP_CTX_ACTIVATED_HW_ERROR,
CAM_ISP_CTX_ACTIVATED_HALT,
CAM_ISP_CTX_ACTIVATED_MAX,
};
@@ -111,6 +112,7 @@
* @reported_req_id: Last reported request id
* @subscribe_event: The irq event mask that CRM subscribes to, IFE will
* invoke CRM cb at those event.
+ * @last_applied_req_id: Last applied request id
*
*/
struct cam_isp_context {
@@ -129,6 +131,7 @@
int32_t active_req_cnt;
int64_t reported_req_id;
uint32_t subscribe_event;
+ int64_t last_applied_req_id;
};
/**
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
index ee01c5e..d5a5347 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
@@ -138,6 +138,39 @@
return rc;
}
+static int cam_ife_hw_mgr_reset_csid_res(
+ struct cam_ife_hw_mgr_res *isp_hw_res)
+{
+ int i;
+ int rc = 0;
+ struct cam_hw_intf *hw_intf;
+ struct cam_csid_reset_cfg_args csid_reset_args;
+
+ csid_reset_args.reset_type = CAM_IFE_CSID_RESET_PATH;
+
+ for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
+ if (!isp_hw_res->hw_res[i])
+ continue;
+ csid_reset_args.node_res = isp_hw_res->hw_res[i];
+ hw_intf = isp_hw_res->hw_res[i]->hw_intf;
+ CAM_DBG(CAM_ISP, "Resetting csid hardware %d",
+ hw_intf->hw_idx);
+ if (hw_intf->hw_ops.reset) {
+ rc = hw_intf->hw_ops.reset(hw_intf->hw_priv,
+ &csid_reset_args,
+ sizeof(struct cam_csid_reset_cfg_args));
+ if (rc <= 0)
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ CAM_ERR(CAM_ISP, "RESET HW res failed: (type:%d, id:%d)",
+ isp_hw_res->res_type, isp_hw_res->res_id);
+ return rc;
+}
+
static int cam_ife_hw_mgr_init_hw_res(
struct cam_ife_hw_mgr_res *isp_hw_res)
{
@@ -1424,6 +1457,8 @@
CAM_ERR(CAM_ISP, "Invalid context parameters");
return -EPERM;
}
+ if (atomic_read(&ctx->overflow_pending))
+ return -EINVAL;
CAM_DBG(CAM_ISP, "Enter ctx id:%d num_hw_upd_entries %d",
ctx->ctx_index, cfg->num_hw_update_entries);
@@ -1455,8 +1490,7 @@
return rc;
}
-static int cam_ife_mgr_stop_hw_in_overflow(void *hw_mgr_priv,
- void *stop_hw_args)
+static int cam_ife_mgr_stop_hw_in_overflow(void *stop_hw_args)
{
int rc = 0;
struct cam_hw_stop_args *stop_args = stop_hw_args;
@@ -1464,7 +1498,7 @@
struct cam_ife_hw_mgr_ctx *ctx;
uint32_t i, master_base_idx = 0;
- if (!hw_mgr_priv || !stop_hw_args) {
+ if (!stop_hw_args) {
CAM_ERR(CAM_ISP, "Invalid arguments");
return -EINVAL;
}
@@ -1477,7 +1511,6 @@
CAM_DBG(CAM_ISP, "Enter...ctx id:%d",
ctx->ctx_index);
- /* stop resource will remove the irq mask from the hardware */
if (!ctx->num_base) {
CAM_ERR(CAM_ISP, "Number of bases are zero");
return -EINVAL;
@@ -1491,17 +1524,13 @@
}
}
- /*
- * if Context does not have PIX resources and has only RDI resource
- * then take the first base index.
- */
-
if (i == ctx->num_base)
master_base_idx = ctx->base[0].idx;
+
/* stop the master CIDs first */
cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_cid,
- master_base_idx, CAM_CSID_HALT_IMMEDIATELY);
+ master_base_idx, CAM_CSID_HALT_IMMEDIATELY);
/* stop rest of the CIDs */
for (i = 0; i < ctx->num_base; i++) {
@@ -1513,7 +1542,7 @@
/* stop the master CSID path first */
cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_csid,
- master_base_idx, CAM_CSID_HALT_IMMEDIATELY);
+ master_base_idx, CAM_CSID_HALT_IMMEDIATELY);
/* Stop rest of the CSID paths */
for (i = 0; i < ctx->num_base; i++) {
@@ -1533,8 +1562,9 @@
for (i = 0; i < CAM_IFE_HW_OUT_RES_MAX; i++)
cam_ife_hw_mgr_stop_hw_res(&ctx->res_list_ife_out[i]);
- /* update vote bandwidth should be done at the HW layer */
+ /* Stop tasklet for context */
+ cam_tasklet_stop(ctx->common.tasklet_info);
CAM_DBG(CAM_ISP, "Exit...ctx id:%d rc :%d",
ctx->ctx_index, rc);
@@ -1664,40 +1694,27 @@
return rc;
}
-static int cam_ife_mgr_reset_hw(struct cam_ife_hw_mgr *hw_mgr,
+static int cam_ife_mgr_reset_vfe_hw(struct cam_ife_hw_mgr *hw_mgr,
uint32_t hw_idx)
{
uint32_t i = 0;
- struct cam_hw_intf *csid_hw_intf;
struct cam_hw_intf *vfe_hw_intf;
- struct cam_csid_reset_cfg_args csid_reset_args;
+ uint32_t vfe_reset_type;
if (!hw_mgr) {
CAM_DBG(CAM_ISP, "Invalid arguments");
return -EINVAL;
}
-
- /* Reset IFE CSID HW */
- csid_reset_args.reset_type = CAM_IFE_CSID_RESET_GLOBAL;
-
- for (i = 0; i < CAM_IFE_CSID_HW_NUM_MAX; i++) {
- if (hw_idx != hw_mgr->csid_devices[i]->hw_idx)
- continue;
-
- csid_hw_intf = hw_mgr->csid_devices[i];
- csid_hw_intf->hw_ops.reset(csid_hw_intf->hw_priv,
- &csid_reset_args,
- sizeof(struct cam_csid_reset_cfg_args));
- break;
- }
-
/* Reset VFE HW*/
+ vfe_reset_type = CAM_VFE_HW_RESET_HW;
+
for (i = 0; i < CAM_VFE_HW_NUM_MAX; i++) {
if (hw_idx != hw_mgr->ife_devices[i]->hw_idx)
continue;
CAM_DBG(CAM_ISP, "VFE (id = %d) reset", hw_idx);
vfe_hw_intf = hw_mgr->ife_devices[i];
- vfe_hw_intf->hw_ops.reset(vfe_hw_intf->hw_priv, NULL, 0);
+ vfe_hw_intf->hw_ops.reset(vfe_hw_intf->hw_priv,
+ &vfe_reset_type, sizeof(vfe_reset_type));
break;
}
@@ -1705,8 +1722,7 @@
return 0;
}
-static int cam_ife_mgr_restart_hw(void *hw_mgr_priv,
- void *start_hw_args)
+static int cam_ife_mgr_restart_hw(void *start_hw_args)
{
int rc = -1;
struct cam_hw_start_args *start_args = start_hw_args;
@@ -1714,7 +1730,7 @@
struct cam_ife_hw_mgr_res *hw_mgr_res;
uint32_t i;
- if (!hw_mgr_priv || !start_hw_args) {
+ if (!start_hw_args) {
CAM_ERR(CAM_ISP, "Invalid arguments");
return -EINVAL;
}
@@ -1725,9 +1741,10 @@
return -EPERM;
}
- CAM_DBG(CAM_ISP, "Enter... ctx id:%d", ctx->ctx_index);
-
CAM_DBG(CAM_ISP, "START IFE OUT ... in ctx id:%d", ctx->ctx_index);
+
+ cam_tasklet_start(ctx->common.tasklet_info);
+
/* start the IFE out devices */
for (i = 0; i < CAM_IFE_HW_OUT_RES_MAX; i++) {
rc = cam_ife_hw_mgr_start_hw_res(&ctx->res_list_ife_out[i]);
@@ -1760,22 +1777,12 @@
}
CAM_DBG(CAM_ISP, "START CID SRC ... in ctx id:%d", ctx->ctx_index);
- /* Start the IFE CID HW devices */
- list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_cid, list) {
- rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res);
- if (rc) {
- CAM_ERR(CAM_ISP, "Can not start IFE CSID (%d)",
- hw_mgr_res->res_id);
- goto err;
- }
- }
-
/* Start IFE root node: do nothing */
CAM_DBG(CAM_ISP, "Exit...(success)");
return 0;
err:
- cam_ife_mgr_stop_hw(hw_mgr_priv, start_hw_args);
+ cam_ife_mgr_stop_hw_in_overflow(start_hw_args);
CAM_DBG(CAM_ISP, "Exit...(rc=%d)", rc);
return rc;
}
@@ -1939,7 +1946,7 @@
rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res);
if (rc) {
CAM_ERR(CAM_ISP, "Can not start IFE CSID (%d)",
- hw_mgr_res->res_id);
+ hw_mgr_res->res_id);
goto err;
}
}
@@ -2363,11 +2370,12 @@
static int cam_ife_mgr_process_recovery_cb(void *priv, void *data)
{
int32_t rc = 0;
- struct cam_hw_event_recovery_data *recovery_data = priv;
- struct cam_hw_start_args start_args;
- struct cam_ife_hw_mgr *ife_hw_mgr = NULL;
- uint32_t hw_mgr_priv;
- uint32_t i = 0;
+ struct cam_hw_event_recovery_data *recovery_data = data;
+ struct cam_hw_start_args start_args;
+ struct cam_hw_stop_args stop_args;
+ struct cam_ife_hw_mgr *ife_hw_mgr = priv;
+ struct cam_ife_hw_mgr_res *hw_mgr_res;
+ uint32_t i = 0;
uint32_t error_type = recovery_data->error_type;
struct cam_ife_hw_mgr_ctx *ctx = NULL;
@@ -2384,20 +2392,57 @@
kfree(recovery_data);
return 0;
}
+ /* stop resources here */
+ CAM_DBG(CAM_ISP, "STOP: Number of affected context: %d",
+ recovery_data->no_of_context);
+ for (i = 0; i < recovery_data->no_of_context; i++) {
+ stop_args.ctxt_to_hw_map =
+ recovery_data->affected_ctx[i];
+ rc = cam_ife_mgr_stop_hw_in_overflow(&stop_args);
+ if (rc) {
+ CAM_ERR(CAM_ISP, "CTX stop failed(%d)", rc);
+ return rc;
+ }
+ }
- ctx = recovery_data->affected_ctx[0];
- ife_hw_mgr = ctx->hw_mgr;
+ CAM_DBG(CAM_ISP, "RESET: CSID PATH");
+ for (i = 0; i < recovery_data->no_of_context; i++) {
+ ctx = recovery_data->affected_ctx[i];
+ list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_csid,
+ list) {
+ rc = cam_ife_hw_mgr_reset_csid_res(hw_mgr_res);
+ if (rc) {
+ CAM_ERR(CAM_ISP, "Failed RESET (%d)",
+ hw_mgr_res->res_id);
+ return rc;
+ }
+ }
+ }
+
+ CAM_DBG(CAM_ISP, "RESET: Calling VFE reset");
for (i = 0; i < CAM_VFE_HW_NUM_MAX; i++) {
if (recovery_data->affected_core[i])
- rc = cam_ife_mgr_reset_hw(ife_hw_mgr, i);
+ cam_ife_mgr_reset_vfe_hw(ife_hw_mgr, i);
}
+ CAM_DBG(CAM_ISP, "START: Number of affected context: %d",
+ recovery_data->no_of_context);
+
for (i = 0; i < recovery_data->no_of_context; i++) {
- start_args.ctxt_to_hw_map =
- recovery_data->affected_ctx[i];
- rc = cam_ife_mgr_restart_hw(&hw_mgr_priv, &start_args);
+ ctx = recovery_data->affected_ctx[i];
+ start_args.ctxt_to_hw_map = ctx;
+
+ atomic_set(&ctx->overflow_pending, 0);
+
+ rc = cam_ife_mgr_restart_hw(&start_args);
+ if (rc) {
+ CAM_ERR(CAM_ISP, "CTX start failed(%d)", rc);
+ return rc;
+ }
+ CAM_DBG(CAM_ISP, "Started resources rc (%d)", rc);
}
+ CAM_DBG(CAM_ISP, "Recovery Done rc (%d)", rc);
break;
@@ -2423,8 +2468,6 @@
struct crm_workq_task *task = NULL;
struct cam_hw_event_recovery_data *recovery_data = NULL;
- return 0;
-
recovery_data = kzalloc(sizeof(struct cam_hw_event_recovery_data),
GFP_ATOMIC);
if (!recovery_data)
@@ -2443,7 +2486,9 @@
}
task->process_cb = &cam_ife_mgr_process_recovery_cb;
- rc = cam_req_mgr_workq_enqueue_task(task, recovery_data,
+ task->payload = recovery_data;
+ rc = cam_req_mgr_workq_enqueue_task(task,
+ recovery_data->affected_ctx[0]->hw_mgr,
CRM_TASK_PRIORITY_0);
return rc;
@@ -2456,9 +2501,9 @@
* affected_core[]
* b. Return 0 i.e.SUCCESS
*/
-static int cam_ife_hw_mgr_match_hw_idx(
+static int cam_ife_hw_mgr_is_ctx_affected(
struct cam_ife_hw_mgr_ctx *ife_hwr_mgr_ctx,
- uint32_t *affected_core)
+ uint32_t *affected_core, uint32_t size)
{
int32_t rc = -EPERM;
@@ -2468,22 +2513,25 @@
CAM_DBG(CAM_ISP, "Enter:max_idx = %d", max_idx);
- while (i < max_idx) {
+ if ((max_idx >= CAM_IFE_HW_NUM_MAX) ||
+ (size > CAM_IFE_HW_NUM_MAX)) {
+ CAM_ERR(CAM_ISP, "invalid parameter = %d", max_idx);
+ return rc;
+ }
+
+ for (i = 0; i < max_idx; i++) {
if (affected_core[ife_hwr_mgr_ctx->base[i].idx])
rc = 0;
else {
ctx_affected_core_idx[j] = ife_hwr_mgr_ctx->base[i].idx;
j = j + 1;
}
-
- i = i + 1;
}
if (rc == 0) {
while (j) {
if (affected_core[ctx_affected_core_idx[j-1]] != 1)
affected_core[ctx_affected_core_idx[j-1]] = 1;
-
j = j - 1;
}
}
@@ -2499,7 +2547,7 @@
* d. For any dual VFE context, if copanion VFE is also serving
* other context it should also notify the CRM with fatal error
*/
-static int cam_ife_hw_mgr_handle_overflow(
+static int cam_ife_hw_mgr_process_overflow(
struct cam_ife_hw_mgr_ctx *curr_ife_hwr_mgr_ctx,
struct cam_isp_hw_error_event_data *error_event_data,
uint32_t curr_core_idx,
@@ -2509,12 +2557,10 @@
struct cam_ife_hw_mgr_ctx *ife_hwr_mgr_ctx = NULL;
cam_hw_event_cb_func ife_hwr_irq_err_cb;
struct cam_ife_hw_mgr *ife_hwr_mgr = NULL;
- uint32_t hw_mgr_priv = 1;
struct cam_hw_stop_args stop_args;
uint32_t i = 0;
CAM_DBG(CAM_ISP, "Enter");
- return 0;
if (!recovery_data) {
CAM_ERR(CAM_ISP, "recovery_data parameter is NULL",
@@ -2535,9 +2581,12 @@
* with this context
*/
CAM_DBG(CAM_ISP, "Calling match Hw idx");
- if (cam_ife_hw_mgr_match_hw_idx(ife_hwr_mgr_ctx, affected_core))
+ if (cam_ife_hw_mgr_is_ctx_affected(ife_hwr_mgr_ctx,
+ affected_core, CAM_IFE_HW_NUM_MAX))
continue;
+ atomic_set(&ife_hwr_mgr_ctx->overflow_pending, 1);
+
ife_hwr_irq_err_cb =
ife_hwr_mgr_ctx->common.event_cb[CAM_ISP_HW_EVENT_ERROR];
@@ -2551,16 +2600,13 @@
ife_hwr_mgr_ctx;
/*
- * Stop the hw resources associated with this context
- * and call the error callback. In the call back function
- * corresponding ISP context will update CRM about fatal Error
+ * In the call back function corresponding ISP context
+ * will update CRM about fatal Error
*/
- if (!cam_ife_mgr_stop_hw_in_overflow(&hw_mgr_priv,
- &stop_args)) {
- CAM_DBG(CAM_ISP, "Calling Error handler CB");
- ife_hwr_irq_err_cb(ife_hwr_mgr_ctx->common.cb_priv,
- CAM_ISP_HW_EVENT_ERROR, error_event_data);
- }
+
+ ife_hwr_irq_err_cb(ife_hwr_mgr_ctx->common.cb_priv,
+ CAM_ISP_HW_EVENT_ERROR, error_event_data);
+
}
/* fill the affected_core in recovery data */
for (i = 0; i < CAM_IFE_HW_NUM_MAX; i++) {
@@ -2572,11 +2618,85 @@
return 0;
}
+static int cam_ife_hw_mgr_get_err_type(
+ void *handler_priv,
+ void *payload)
+{
+ struct cam_isp_resource_node *hw_res_l = NULL;
+ struct cam_isp_resource_node *hw_res_r = NULL;
+ struct cam_ife_hw_mgr_ctx *ife_hwr_mgr_ctx;
+ struct cam_vfe_top_irq_evt_payload *evt_payload;
+ struct cam_ife_hw_mgr_res *isp_ife_camif_res = NULL;
+ uint32_t status = 0;
+ uint32_t core_idx;
+
+ ife_hwr_mgr_ctx = handler_priv;
+ evt_payload = payload;
+
+ if (!evt_payload) {
+ CAM_ERR(CAM_ISP, "No payload");
+ return IRQ_HANDLED;
+ }
+
+ core_idx = evt_payload->core_index;
+ evt_payload->evt_id = CAM_ISP_HW_EVENT_ERROR;
+
+ list_for_each_entry(isp_ife_camif_res,
+ &ife_hwr_mgr_ctx->res_list_ife_src, list) {
+
+ if ((isp_ife_camif_res->res_type ==
+ CAM_IFE_HW_MGR_RES_UNINIT) ||
+ (isp_ife_camif_res->res_id != CAM_ISP_HW_VFE_IN_CAMIF))
+ continue;
+
+ hw_res_l = isp_ife_camif_res->hw_res[CAM_ISP_HW_SPLIT_LEFT];
+ hw_res_r = isp_ife_camif_res->hw_res[CAM_ISP_HW_SPLIT_RIGHT];
+
+ CAM_DBG(CAM_ISP, "is_dual_vfe ? = %d\n",
+ isp_ife_camif_res->is_dual_vfe);
+
+ /* ERROR check for Left VFE */
+ if (!hw_res_l) {
+ CAM_DBG(CAM_ISP, "VFE(L) Device is NULL");
+ break;
+ }
+
+ CAM_DBG(CAM_ISP, "core id= %d, HW id %d", core_idx,
+ hw_res_l->hw_intf->hw_idx);
+
+ if (core_idx == hw_res_l->hw_intf->hw_idx) {
+ status = hw_res_l->bottom_half_handler(
+ hw_res_l, evt_payload);
+ }
+
+ if (status)
+ break;
+
+ /* ERROR check for Right VFE */
+ if (!hw_res_r) {
+ CAM_DBG(CAM_ISP, "VFE(R) Device is NULL");
+ continue;
+ }
+ CAM_DBG(CAM_ISP, "core id= %d, HW id %d", core_idx,
+ hw_res_r->hw_intf->hw_idx);
+
+ if (core_idx == hw_res_r->hw_intf->hw_idx) {
+ status = hw_res_r->bottom_half_handler(
+ hw_res_r, evt_payload);
+ }
+
+ if (status)
+ break;
+ }
+ CAM_DBG(CAM_ISP, "Exit (status = %d)!", status);
+ return status;
+}
+
static int cam_ife_hw_mgr_handle_camif_error(
void *handler_priv,
void *payload)
{
- int32_t rc = 0;
+ int32_t error_status = CAM_ISP_HW_ERROR_NONE;
uint32_t core_idx;
struct cam_ife_hw_mgr_ctx *ife_hwr_mgr_ctx;
struct cam_vfe_top_irq_evt_payload *evt_payload;
@@ -2587,17 +2707,22 @@
evt_payload = payload;
core_idx = evt_payload->core_index;
- rc = evt_payload->error_type;
- CAM_DBG(CAM_ISP, "Enter: error_type (%d)", evt_payload->error_type);
- switch (evt_payload->error_type) {
+ error_status = cam_ife_hw_mgr_get_err_type(ife_hwr_mgr_ctx,
+ evt_payload);
+
+ if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending))
+ return error_status;
+
+ switch (error_status) {
case CAM_ISP_HW_ERROR_OVERFLOW:
case CAM_ISP_HW_ERROR_P2I_ERROR:
case CAM_ISP_HW_ERROR_VIOLATION:
+ CAM_DBG(CAM_ISP, "Enter: error_type (%d)", error_status);
error_event_data.error_type =
CAM_ISP_HW_ERROR_OVERFLOW;
- cam_ife_hw_mgr_handle_overflow(ife_hwr_mgr_ctx,
+ cam_ife_hw_mgr_process_overflow(ife_hwr_mgr_ctx,
&error_event_data,
core_idx,
&recovery_data);
@@ -2607,12 +2732,10 @@
cam_ife_hw_mgr_do_error_recovery(&recovery_data);
break;
default:
- CAM_DBG(CAM_ISP, "None error. Error type (%d)",
- evt_payload->error_type);
+ CAM_DBG(CAM_ISP, "None error (%d)", error_status);
}
- CAM_DBG(CAM_ISP, "Exit (%d)", rc);
- return rc;
+ return error_status;
}
/*
@@ -2677,6 +2800,8 @@
rup_status = hw_res->bottom_half_handler(
hw_res, evt_payload);
}
+ if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending))
+ break;
if (!rup_status) {
ife_hwr_irq_rup_cb(
@@ -2708,6 +2833,8 @@
rup_status = hw_res->bottom_half_handler(
hw_res, evt_payload);
+ if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending))
+ break;
if (!rup_status) {
/* Send the Reg update hw event */
ife_hwr_irq_rup_cb(
@@ -2829,6 +2956,9 @@
if (core_idx == hw_res_l->hw_intf->hw_idx) {
epoch_status = hw_res_l->bottom_half_handler(
hw_res_l, evt_payload);
+ if (atomic_read(
+ &ife_hwr_mgr_ctx->overflow_pending))
+ break;
if (!epoch_status)
ife_hwr_irq_epoch_cb(
ife_hwr_mgr_ctx->common.cb_priv,
@@ -2876,6 +3006,8 @@
core_index1,
evt_payload->evt_id);
+ if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending))
+ break;
if (!rc)
ife_hwr_irq_epoch_cb(
ife_hwr_mgr_ctx->common.cb_priv,
@@ -2936,6 +3068,8 @@
if (core_idx == hw_res_l->hw_intf->hw_idx) {
sof_status = hw_res_l->bottom_half_handler(hw_res_l,
evt_payload);
+ if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending))
+ break;
if (!sof_status) {
cam_ife_mgr_cmd_get_sof_timestamp(
ife_hwr_mgr_ctx,
@@ -2991,6 +3125,9 @@
core_index0 = hw_res_l->hw_intf->hw_idx;
core_index1 = hw_res_r->hw_intf->hw_idx;
+ if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending))
+ break;
+
rc = cam_ife_hw_mgr_check_irq_for_dual_vfe(ife_hwr_mgr_ctx,
core_index0, core_index1, evt_payload->evt_id);
@@ -3149,6 +3286,9 @@
if (core_idx == hw_res_l->hw_intf->hw_idx) {
eof_status = hw_res_l->bottom_half_handler(
hw_res_l, evt_payload);
+ if (atomic_read(
+ &ife_hwr_mgr_ctx->overflow_pending))
+ break;
if (!eof_status)
ife_hwr_irq_eof_cb(
ife_hwr_mgr_ctx->common.cb_priv,
@@ -3193,6 +3333,9 @@
core_index1,
evt_payload->evt_id);
+ if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending))
+ break;
+
if (!rc)
ife_hwr_irq_eof_cb(
ife_hwr_mgr_ctx->common.cb_priv,
@@ -3237,6 +3380,8 @@
ife_hwr_irq_wm_done_cb =
ife_hwr_mgr_ctx->common.event_cb[CAM_ISP_HW_EVENT_DONE];
+ evt_payload->evt_id = CAM_ISP_HW_EVENT_DONE;
+
for (i = 0; i < CAM_IFE_HW_OUT_RES_MAX; i++) {
isp_ife_out_res = &ife_hwr_mgr_ctx->res_list_ife_out[i];
@@ -3293,6 +3438,8 @@
buf_done_event_data.resource_handle[0] =
isp_ife_out_res->res_id;
+ if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending))
+ break;
/* Report for Successful buf_done event if any */
if (buf_done_event_data.num_handles > 0 &&
ife_hwr_irq_wm_done_cb) {
@@ -3330,7 +3477,7 @@
* the affected context and any successful buf_done event is not
* reported.
*/
- rc = cam_ife_hw_mgr_handle_overflow(ife_hwr_mgr_ctx,
+ rc = cam_ife_hw_mgr_process_overflow(ife_hwr_mgr_ctx,
&error_event_data, evt_payload->core_index,
&recovery_data);
@@ -3369,8 +3516,6 @@
evt_payload->irq_reg_val[5]);
CAM_DBG(CAM_ISP, "bus_irq_dual_comp_owrt: = %x",
evt_payload->irq_reg_val[6]);
-
- CAM_DBG(CAM_ISP, "Calling Buf_done");
/* WM Done */
return cam_ife_hw_mgr_handle_buf_done_for_hw_res(ife_hwr_mgr_ctx,
evt_payload_priv);
@@ -3401,8 +3546,15 @@
* for this context it needs to be handled remaining
* interrupts are ignored.
*/
- rc = cam_ife_hw_mgr_handle_camif_error(ife_hwr_mgr_ctx,
- evt_payload_priv);
+ if (g_ife_hw_mgr.debug_cfg.enable_recovery) {
+ CAM_DBG(CAM_ISP, "IFE Mgr recovery is enabled");
+ rc = cam_ife_hw_mgr_handle_camif_error(ife_hwr_mgr_ctx,
+ evt_payload_priv);
+ } else {
+ CAM_DBG(CAM_ISP, "recovery is not enabled");
+ rc = 0;
+ }
+
if (rc) {
CAM_ERR(CAM_ISP, "Encountered Error (%d), ignoring other irqs",
rc);
@@ -3501,6 +3653,15 @@
goto err;
}
+ if (!debugfs_create_u32("enable_recovery",
+ 0644,
+ g_ife_hw_mgr.debug_cfg.dentry,
+ &g_ife_hw_mgr.debug_cfg.enable_recovery)) {
+ CAM_ERR(CAM_ISP, "failed to create enable_recovery");
+ goto err;
+ }
+ g_ife_hw_mgr.debug_cfg.enable_recovery = 0;
+
return 0;
err:
@@ -3700,4 +3861,3 @@
g_ife_hw_mgr.mgr_common.img_iommu_hdl = -1;
return rc;
}
-
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h
index 1c35e5d..4d26138 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h
@@ -85,11 +85,13 @@
*
* @dentry: Debugfs entry
* @csid_debug: csid debug information
+ * @enable_recovery enable recovery
*
*/
struct cam_ife_hw_mgr_debug {
- struct dentry *dentry;
- uint64_t csid_debug;
+ struct dentry *dentry;
+ uint64_t csid_debug;
+ uint32_t enable_recovery;
};
/**
@@ -171,6 +173,7 @@
* @ife_csid_dev_caps csid device capability stored per core
* @ife_dev_caps ife device capability per core
* @work q work queue for IFE hw manager
+ * @debug_cfg debug configuration
*/
struct cam_ife_hw_mgr {
struct cam_isp_hw_mgr mgr_common;
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c
index 4a7eff8..8863275 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c
@@ -261,6 +261,15 @@
*tasklet_info = NULL;
}
+static void cam_tasklet_flush(void *tasklet_info)
+{
+ unsigned long data;
+ struct cam_tasklet_info *tasklet = tasklet_info;
+
+ data = (unsigned long)tasklet;
+ cam_tasklet_action(data);
+}
+
int cam_tasklet_start(void *tasklet_info)
{
struct cam_tasklet_info *tasklet = tasklet_info;
@@ -290,6 +299,7 @@
{
struct cam_tasklet_info *tasklet = tasklet_info;
+ cam_tasklet_flush(tasklet);
atomic_set(&tasklet->tasklet_active, 0);
tasklet_disable(&tasklet->tasklet);
}
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h
index e8a5de5..b7ec511 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h
@@ -70,6 +70,12 @@
CAM_IFE_BUS_IRQ_REGISTERS_MAX,
};
+enum cam_vfe_reset_type {
+ CAM_VFE_HW_RESET_HW_AND_REG,
+ CAM_VFE_HW_RESET_HW,
+ CAM_VFE_HW_RESET_MAX,
+};
+
/*
* struct cam_vfe_hw_get_hw_cap:
*
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
index 7a26370..d1e1605 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
@@ -25,7 +25,6 @@
#include "cam_debug_util.h"
static const char drv_name[] = "vfe";
-
static uint32_t irq_reg_offset[CAM_IFE_IRQ_REGISTERS_MAX] = {
0x0000006C,
0x00000070,
@@ -34,7 +33,12 @@
static uint32_t camif_irq_reg_mask[CAM_IFE_IRQ_REGISTERS_MAX] = {
0x0003FD1F,
- 0x0FFF7EBC,
+ 0x00000000,
+};
+
+static uint32_t camif_irq_err_reg_mask[CAM_IFE_IRQ_REGISTERS_MAX] = {
+ 0x00000000,
+ 0x0FFF7E80,
};
static uint32_t rdi_irq_reg_mask[CAM_IFE_IRQ_REGISTERS_MAX] = {
@@ -83,6 +87,7 @@
}
spin_lock_irqsave(&vfe_core_info->spin_lock, flags);
+ (*evt_payload)->error_type = 0;
list_add_tail(&(*evt_payload)->list, &vfe_core_info->free_payload_list);
spin_unlock_irqrestore(&vfe_core_info->spin_lock, flags);
@@ -143,6 +148,60 @@
return rc;
}
+static int cam_vfe_irq_err_top_half(uint32_t evt_id,
+ struct cam_irq_th_payload *th_payload)
+{
+ int32_t rc;
+ int i;
+ struct cam_vfe_irq_handler_priv *handler_priv;
+ struct cam_vfe_top_irq_evt_payload *evt_payload;
+ struct cam_vfe_hw_core_info *core_info;
+
+ CAM_DBG(CAM_ISP, "IRQ status_0 = %x, IRQ status_1 = %x",
+ th_payload->evt_status_arr[0], th_payload->evt_status_arr[1]);
+
+ handler_priv = th_payload->handler_priv;
+ core_info = handler_priv->core_info;
+ /*
+ * need to handle overflow condition here, otherwise irq storm
+ * will block everything
+ */
+
+ if (th_payload->evt_status_arr[1]) {
+ CAM_ERR(CAM_ISP, "IRQ status_1: %x, Masking all interrupts",
+ th_payload->evt_status_arr[1]);
+ cam_irq_controller_disable_irq(core_info->vfe_irq_controller,
+ core_info->irq_err_handle);
+ }
+
+ rc = cam_vfe_get_evt_payload(handler_priv->core_info, &evt_payload);
+ if (rc) {
+ CAM_ERR_RATE_LIMIT(CAM_ISP,
+ "No tasklet_cmd is free in queue\n");
+ return rc;
+ }
+
+ cam_isp_hw_get_timestamp(&evt_payload->ts);
+
+ evt_payload->core_index = handler_priv->core_index;
+ evt_payload->core_info = handler_priv->core_info;
+ evt_payload->evt_id = evt_id;
+
+ for (i = 0; i < th_payload->num_registers; i++)
+ evt_payload->irq_reg_val[i] = th_payload->evt_status_arr[i];
+
+ for (; i < CAM_IFE_IRQ_REGISTERS_MAX; i++) {
+ evt_payload->irq_reg_val[i] = cam_io_r(handler_priv->mem_base +
+ irq_reg_offset[i]);
+ }
+
+ CAM_DBG(CAM_ISP, "Violation status = %x", evt_payload->irq_reg_val[2]);
+
+ th_payload->evt_payload_priv = evt_payload;
+
+ return rc;
+}
+
int cam_vfe_init_hw(void *hw_priv, void *init_hw_args, uint32_t arg_size)
{
struct cam_hw_info *vfe_hw = hw_priv;
@@ -150,6 +209,8 @@
struct cam_vfe_hw_core_info *core_info = NULL;
struct cam_isp_resource_node *isp_res = NULL;
int rc = 0;
+ uint32_t reset_core_args =
+ CAM_VFE_HW_RESET_HW_AND_REG;
CAM_DBG(CAM_ISP, "Enter");
if (!hw_priv) {
@@ -190,7 +251,7 @@
CAM_DBG(CAM_ISP, "Enable soc done");
/* Do HW Reset */
- rc = cam_vfe_reset(hw_priv, NULL, 0);
+ rc = cam_vfe_reset(hw_priv, &reset_core_args, sizeof(uint32_t));
if (rc) {
CAM_ERR(CAM_ISP, "Reset Failed rc=%d", rc);
goto deinint_vfe_res;
@@ -306,7 +367,8 @@
reinit_completion(&vfe_hw->hw_complete);
CAM_DBG(CAM_ISP, "calling RESET");
- core_info->vfe_top->hw_ops.reset(core_info->vfe_top->top_priv, NULL, 0);
+ core_info->vfe_top->hw_ops.reset(core_info->vfe_top->top_priv,
+ reset_core_args, arg_size);
CAM_DBG(CAM_ISP, "waiting for vfe reset complete");
/* Wait for Completion or Timeout of 500ms */
rc = wait_for_completion_timeout(&vfe_hw->hw_complete, 500);
@@ -333,20 +395,37 @@
time_stamp->mono_time.tv_usec = ts.tv_nsec/1000;
}
-
-int cam_vfe_irq_top_half(uint32_t evt_id,
+static int cam_vfe_irq_top_half(uint32_t evt_id,
struct cam_irq_th_payload *th_payload)
{
int32_t rc;
int i;
struct cam_vfe_irq_handler_priv *handler_priv;
struct cam_vfe_top_irq_evt_payload *evt_payload;
+ struct cam_vfe_hw_core_info *core_info;
handler_priv = th_payload->handler_priv;
CAM_DBG(CAM_ISP, "IRQ status_0 = %x", th_payload->evt_status_arr[0]);
CAM_DBG(CAM_ISP, "IRQ status_1 = %x", th_payload->evt_status_arr[1]);
+ /*
+ * need to handle non-recoverable condition here, otherwise irq storm
+ * will block everything.
+ */
+ if (th_payload->evt_status_arr[0] & 0x3FC00) {
+ CAM_ERR(CAM_ISP,
+ "Encountered Error Irq_status0=0x%x Status1=0x%x",
+ th_payload->evt_status_arr[0],
+ th_payload->evt_status_arr[1]);
+ CAM_ERR(CAM_ISP,
+ "Stopping further IRQ processing from this HW index=%d",
+ handler_priv->core_index);
+ cam_io_w(0, handler_priv->mem_base + 0x60);
+ cam_io_w(0, handler_priv->mem_base + 0x5C);
+ return 0;
+ }
+
rc = cam_vfe_get_evt_payload(handler_priv->core_info, &evt_payload);
if (rc) {
CAM_ERR_RATE_LIMIT(CAM_ISP,
@@ -354,6 +433,7 @@
return rc;
}
+ core_info = handler_priv->core_info;
cam_isp_hw_get_timestamp(&evt_payload->ts);
evt_payload->core_index = handler_priv->core_index;
@@ -369,22 +449,6 @@
}
CAM_DBG(CAM_ISP, "Violation status = %x", evt_payload->irq_reg_val[2]);
- /*
- * need to handle overflow condition here, otherwise irq storm
- * will block everything.
- */
- if (evt_payload->irq_reg_val[1]) {
- CAM_ERR(CAM_ISP,
- "Encountered Error Irq_status1=0x%x. Stopping further IRQ processing from this HW",
- evt_payload->irq_reg_val[1]);
- CAM_ERR(CAM_ISP, "Violation status = %x",
- evt_payload->irq_reg_val[2]);
- cam_io_w(0, handler_priv->mem_base + 0x60);
- cam_io_w(0, handler_priv->mem_base + 0x5C);
-
- evt_payload->error_type = CAM_ISP_HW_ERROR_OVERFLOW;
- }
-
th_payload->evt_payload_priv = evt_payload;
CAM_DBG(CAM_ISP, "Exit");
@@ -475,6 +539,7 @@
core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info;
isp_res = (struct cam_isp_resource_node *)start_args;
+ core_info->tasklet_info = isp_res->tasklet_info;
mutex_lock(&vfe_hw->hw_mutex);
if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_IN) {
@@ -493,6 +558,12 @@
cam_vfe_irq_top_half, cam_ife_mgr_do_tasklet,
isp_res->tasklet_info, cam_tasklet_enqueue_cmd);
+ core_info->irq_err_handle = cam_irq_controller_subscribe_irq(
+ core_info->vfe_irq_controller, CAM_IRQ_PRIORITY_0,
+ camif_irq_err_reg_mask, &core_info->irq_payload,
+ cam_vfe_irq_err_top_half, cam_ife_mgr_do_tasklet,
+ core_info->tasklet_info, cam_tasklet_enqueue_cmd);
+
if (isp_res->irq_handle > 0)
rc = core_info->vfe_top->hw_ops.start(
core_info->vfe_top->top_priv, isp_res,
@@ -505,7 +576,6 @@
} else {
CAM_ERR(CAM_ISP, "Invalid res type:%d", isp_res->res_type);
}
-
mutex_unlock(&vfe_hw->hw_mutex);
return rc;
@@ -534,6 +604,10 @@
rc = core_info->vfe_top->hw_ops.stop(
core_info->vfe_top->top_priv, isp_res,
sizeof(struct cam_isp_resource_node));
+
+ cam_irq_controller_unsubscribe_irq(
+ core_info->vfe_irq_controller, core_info->irq_err_handle);
+
} else if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_OUT) {
rc = core_info->vfe_bus->hw_ops.stop(isp_res, NULL, 0);
} else {
@@ -699,4 +773,3 @@
return rc;
}
-
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.h
index ee29e1cf..0674a6ad 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.h
@@ -50,12 +50,13 @@
void *vfe_irq_controller;
struct cam_vfe_top *vfe_top;
struct cam_vfe_bus *vfe_bus;
-
+ void *tasklet_info;
struct cam_vfe_top_irq_evt_payload evt_payload[CAM_VFE_EVT_MAX];
struct list_head free_payload_list;
struct cam_vfe_irq_handler_priv irq_payload;
uint32_t cpas_handle;
int irq_handle;
+ int irq_err_handle;
spinlock_t spin_lock;
};
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c
index ed5e120..0f93664 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c
@@ -15,6 +15,30 @@
#include "cam_vfe_soc.h"
#include "cam_debug_util.h"
+static bool cam_vfe_cpas_cb(uint32_t client_handle, void *userdata,
+ struct cam_cpas_irq_data *irq_data)
+{
+ bool error_handled = false;
+
+ if (!irq_data)
+ return error_handled;
+
+ switch (irq_data->irq_type) {
+ case CAM_CAMNOC_IRQ_IFE02_UBWC_ENCODE_ERROR:
+ case CAM_CAMNOC_IRQ_IFE13_UBWC_ENCODE_ERROR:
+ CAM_ERR_RATE_LIMIT(CAM_ISP,
+ "IFE UBWC Encode error type=%d status=%x",
+ irq_data->irq_type,
+ irq_data->u.enc_err.encerr_status.value);
+ error_handled = true;
+ break;
+ default:
+ break;
+ }
+
+ return error_handled;
+}
+
static int cam_vfe_get_dt_properties(struct cam_hw_soc_info *soc_info)
{
int rc = 0;
@@ -95,6 +119,8 @@
CAM_HW_IDENTIFIER_LENGTH);
cpas_register_param.cell_index = soc_info->index;
cpas_register_param.dev = soc_info->dev;
+ cpas_register_param.cam_cpas_client_cb = cam_vfe_cpas_cb;
+ cpas_register_param.userdata = soc_info;
rc = cam_cpas_register_client(&cpas_register_param);
if (rc) {
CAM_ERR(CAM_ISP, "CPAS registration failed rc=%d", rc);
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h
index fb6ea6c..a4ba2e1 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h
@@ -77,6 +77,8 @@
.epoch0_irq_mask = 0x00000004,
.reg_update_irq_mask = 0x00000010,
.eof_irq_mask = 0x00000002,
+ .error_irq_mask0 = 0x0003FC00,
+ .error_irq_mask1 = 0x0FFF7E80,
};
struct cam_vfe_top_ver2_reg_offset_module_ctrl lens_170_reg = {
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 13c477d..e94bb62 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
@@ -263,7 +263,7 @@
CAM_ERR(CAM_ISP, "No payload to put");
return -EINVAL;
}
-
+ (*evt_payload)->error_type = 0;
ife_irq_regs = (*evt_payload)->irq_reg_val;
status_reg0 = ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS0];
status_reg1 = ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS1];
@@ -992,6 +992,9 @@
rsrc_data->width = rsrc_data->width * 2;
rsrc_data->stride = rsrc_data->width;
rsrc_data->en_cfg = 0x1;
+
+ /* LSB aligned */
+ rsrc_data->pack_fmt |= 0x10;
} else {
/* Write master 5-6 DS ports, 10 PDAF */
uint32_t align_width;
@@ -1143,6 +1146,8 @@
common_data->mem_base + common_data->common_reg->sw_reset);
wm_res->res_state = CAM_ISP_RESOURCE_STATE_RESERVED;
+ rsrc_data->init_cfg_done = false;
+ rsrc_data->hfr_cfg_done = false;
return rc;
}
@@ -2150,6 +2155,7 @@
if (vfe_out->res_state == CAM_ISP_RESOURCE_STATE_AVAILABLE ||
vfe_out->res_state == CAM_ISP_RESOURCE_STATE_RESERVED) {
+ CAM_DBG(CAM_ISP, "vfe_out res_state is %d", vfe_out->res_state);
return rc;
}
@@ -2297,12 +2303,15 @@
struct cam_irq_th_payload *th_payload)
{
int i = 0;
+ struct cam_vfe_bus_ver2_priv *bus_priv = th_payload->handler_priv;
CAM_ERR_RATE_LIMIT(CAM_ISP, "Bus Err IRQ");
for (i = 0; i < th_payload->num_registers; i++) {
CAM_ERR_RATE_LIMIT(CAM_ISP, "IRQ_Status%d: 0x%x", i,
th_payload->evt_status_arr[i]);
}
+ cam_irq_controller_disable_irq(bus_priv->common_data.bus_irq_controller,
+ bus_priv->error_irq_handle);
/* Returning error stops from enqueuing bottom half */
return -EFAULT;
@@ -3112,4 +3121,3 @@
return rc;
}
-
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
index cd90b57..9848454 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
@@ -329,6 +329,7 @@
struct cam_vfe_mux_camif_data *camif_priv;
struct cam_vfe_top_irq_evt_payload *payload;
uint32_t irq_status0;
+ uint32_t irq_status1;
if (!handler_priv || !evt_payload_priv)
return ret;
@@ -337,6 +338,7 @@
camif_priv = camif_node->res_priv;
payload = evt_payload_priv;
irq_status0 = payload->irq_reg_val[CAM_IFE_IRQ_CAMIF_REG_STATUS0];
+ irq_status1 = payload->irq_reg_val[CAM_IFE_IRQ_CAMIF_REG_STATUS1];
CAM_DBG(CAM_ISP, "event ID:%d", payload->evt_id);
CAM_DBG(CAM_ISP, "irq_status_0 = %x", irq_status0);
@@ -367,6 +369,15 @@
ret = CAM_VFE_IRQ_STATUS_SUCCESS;
}
break;
+ case CAM_ISP_HW_EVENT_ERROR:
+ if (irq_status1 & camif_priv->reg_data->error_irq_mask1) {
+ CAM_DBG(CAM_ISP, "Received ERROR\n");
+ ret = CAM_ISP_HW_ERROR_OVERFLOW;
+ cam_vfe_put_evt_payload(payload->core_info, &payload);
+ } else {
+ ret = CAM_ISP_HW_ERROR_NONE;
+ }
+ break;
default:
break;
}
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.h
index 21058ac..4a73bd7 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.h
@@ -61,6 +61,8 @@
uint32_t epoch0_irq_mask;
uint32_t reg_update_irq_mask;
uint32_t eof_irq_mask;
+ uint32_t error_irq_mask0;
+ uint32_t error_irq_mask1;
};
struct cam_vfe_camif_ver2_hw_info {
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c
index 2c35046..7baac45 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c
@@ -18,6 +18,9 @@
#include "cam_vfe_top_ver2.h"
#include "cam_debug_util.h"
+#define CAM_VFE_HW_RESET_HW_AND_REG_VAL 0x00003F9F
+#define CAM_VFE_HW_RESET_HW_VAL 0x00003F87
+
struct cam_vfe_top_ver2_common_data {
struct cam_hw_soc_info *soc_info;
struct cam_hw_intf *hw_intf;
@@ -108,12 +111,24 @@
struct cam_vfe_top_ver2_priv *top_priv = device_priv;
struct cam_hw_soc_info *soc_info = NULL;
struct cam_vfe_top_ver2_reg_offset_common *reg_common = NULL;
+ uint32_t *reset_reg_args = reset_core_args;
+ uint32_t reset_reg_val;
- if (!top_priv) {
+ if (!top_priv || !reset_reg_args) {
CAM_ERR(CAM_ISP, "Invalid arguments");
return -EINVAL;
}
+ switch (*reset_reg_args) {
+ case CAM_VFE_HW_RESET_HW_AND_REG:
+ reset_reg_val = CAM_VFE_HW_RESET_HW_AND_REG_VAL;
+ break;
+ default:
+ reset_reg_val = CAM_VFE_HW_RESET_HW_VAL;
+ break;
+ }
+
+ CAM_DBG(CAM_ISP, "reset reg value: %x", reset_reg_val);
soc_info = top_priv->common_data.soc_info;
reg_common = top_priv->common_data.common_reg;
@@ -122,7 +137,7 @@
CAM_SOC_GET_REG_MAP_START(soc_info, VFE_CORE_BASE_IDX) + 0x5C);
/* Reset HW */
- cam_io_w_mb(0x00003F9F,
+ cam_io_w_mb(reset_reg_val,
CAM_SOC_GET_REG_MAP_START(soc_info, VFE_CORE_BASE_IDX) +
reg_common->global_reset_cmd);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.c
index 64acea7..c5c9b0a 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.c
@@ -186,13 +186,6 @@
goto free_ctrl;
}
- soc_private = (struct cam_actuator_soc_private *)(id->driver_data);
- if (!soc_private) {
- CAM_ERR(CAM_EEPROM, "board info NULL");
- rc = -EINVAL;
- goto free_ctrl;
- }
-
rc = cam_actuator_init_subdev(a_ctrl);
if (rc)
goto free_soc;
@@ -249,8 +242,10 @@
static int32_t cam_actuator_platform_remove(struct platform_device *pdev)
{
- struct cam_actuator_ctrl_t *a_ctrl;
int32_t rc = 0;
+ struct cam_actuator_ctrl_t *a_ctrl;
+ struct cam_actuator_soc_private *soc_private;
+ struct cam_sensor_power_ctrl_t *power_info;
a_ctrl = platform_get_drvdata(pdev);
if (!a_ctrl) {
@@ -258,8 +253,15 @@
return 0;
}
+ soc_private =
+ (struct cam_actuator_soc_private *)a_ctrl->soc_info.soc_private;
+ power_info = &soc_private->power_info;
+
kfree(a_ctrl->io_master_info.cci_client);
a_ctrl->io_master_info.cci_client = NULL;
+ kfree(power_info->power_setting);
+ kfree(power_info->power_down_setting);
+ kfree(a_ctrl->soc_info.soc_private);
kfree(a_ctrl->i2c_data.per_frame);
a_ctrl->i2c_data.per_frame = NULL;
devm_kfree(&pdev->dev, a_ctrl);
@@ -269,17 +271,29 @@
static int32_t cam_actuator_driver_i2c_remove(struct i2c_client *client)
{
- struct cam_actuator_ctrl_t *a_ctrl = i2c_get_clientdata(client);
int32_t rc = 0;
+ struct cam_actuator_ctrl_t *a_ctrl =
+ i2c_get_clientdata(client);
+ struct cam_actuator_soc_private *soc_private;
+ struct cam_sensor_power_ctrl_t *power_info;
/* Handle I2C Devices */
if (!a_ctrl) {
CAM_ERR(CAM_ACTUATOR, "Actuator device is NULL");
return -EINVAL;
}
+
+ soc_private =
+ (struct cam_actuator_soc_private *)a_ctrl->soc_info.soc_private;
+ power_info = &soc_private->power_info;
+
/*Free Allocated Mem */
kfree(a_ctrl->i2c_data.per_frame);
a_ctrl->i2c_data.per_frame = NULL;
+ kfree(power_info->power_setting);
+ kfree(power_info->power_down_setting);
+ kfree(a_ctrl->soc_info.soc_private);
+ a_ctrl->soc_info.soc_private = NULL;
kfree(a_ctrl);
return rc;
}
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 4c69afb..72b1779 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
@@ -657,6 +657,7 @@
struct cam_packet *csl_packet = NULL;
struct cam_eeprom_soc_private *soc_private =
(struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private;
+ struct cam_sensor_power_ctrl_t *power_info = &soc_private->power_info;
ioctl_ctrl = (struct cam_control *)arg;
@@ -701,7 +702,7 @@
e_ctrl->cal_data.num_map = 0;
CAM_DBG(CAM_EEPROM,
"Returning the data using kernel probe");
- break;
+ break;
}
rc = cam_eeprom_init_pkt_parser(e_ctrl, csl_packet);
if (rc) {
@@ -750,16 +751,21 @@
memdata_free:
kfree(e_ctrl->cal_data.mapdata);
error:
+ kfree(power_info->power_setting);
+ kfree(power_info->power_down_setting);
kfree(e_ctrl->cal_data.map);
e_ctrl->cal_data.num_data = 0;
e_ctrl->cal_data.num_map = 0;
- e_ctrl->cam_eeprom_state = CAM_EEPROM_ACQUIRE;
+ e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT;
return rc;
}
void cam_eeprom_shutdown(struct cam_eeprom_ctrl_t *e_ctrl)
{
int rc;
+ struct cam_eeprom_soc_private *soc_private =
+ (struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private;
+ struct cam_sensor_power_ctrl_t *power_info = &soc_private->power_info;
if (e_ctrl->cam_eeprom_state == CAM_EEPROM_INIT)
return;
@@ -779,6 +785,9 @@
e_ctrl->bridge_intf.device_hdl = -1;
e_ctrl->bridge_intf.link_hdl = -1;
e_ctrl->bridge_intf.session_hdl = -1;
+
+ kfree(power_info->power_setting);
+ kfree(power_info->power_down_setting);
}
e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.c
index d667cf4..5eb29c3 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.c
@@ -201,13 +201,6 @@
goto free_soc;
}
- soc_private = (struct cam_eeprom_soc_private *)(id->driver_data);
- if (!soc_private) {
- CAM_ERR(CAM_EEPROM, "board info NULL");
- rc = -EINVAL;
- goto ectrl_free;
- }
-
rc = cam_eeprom_init_subdev(e_ctrl);
if (rc)
goto free_soc;
@@ -260,10 +253,9 @@
return -EINVAL;
}
- if (soc_private) {
- kfree(soc_private->power_info.gpio_num_info);
+ if (soc_private)
kfree(soc_private);
- }
+
kfree(e_ctrl);
return 0;
@@ -451,6 +443,9 @@
platform_set_drvdata(pdev, e_ctrl);
v4l2_set_subdevdata(&e_ctrl->v4l2_dev_str.sd, e_ctrl);
+
+ e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT;
+
return rc;
free_soc:
kfree(soc_private);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.c
index 97fede2..9eca4c7 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.c
@@ -226,13 +226,21 @@
static int cam_ois_i2c_driver_remove(struct i2c_client *client)
{
- struct cam_ois_ctrl_t *o_ctrl = i2c_get_clientdata(client);
+ struct cam_ois_ctrl_t *o_ctrl = i2c_get_clientdata(client);
+ struct cam_ois_soc_private *soc_private;
+ struct cam_sensor_power_ctrl_t *power_info;
if (!o_ctrl) {
CAM_ERR(CAM_OIS, "ois device is NULL");
return -EINVAL;
}
+ soc_private =
+ (struct cam_ois_soc_private *)o_ctrl->soc_info.soc_private;
+ power_info = &soc_private->power_info;
+
+ kfree(power_info->power_setting);
+ kfree(power_info->power_down_setting);
kfree(o_ctrl->soc_info.soc_private);
kfree(o_ctrl);
@@ -302,6 +310,8 @@
platform_set_drvdata(pdev, o_ctrl);
v4l2_set_subdevdata(&o_ctrl->v4l2_dev_str.sd, o_ctrl);
+ o_ctrl->cam_ois_state = CAM_OIS_INIT;
+
return rc;
unreg_subdev:
cam_unregister_subdev(&(o_ctrl->v4l2_dev_str));
@@ -316,7 +326,9 @@
static int cam_ois_platform_driver_remove(struct platform_device *pdev)
{
- struct cam_ois_ctrl_t *o_ctrl;
+ struct cam_ois_ctrl_t *o_ctrl;
+ struct cam_ois_soc_private *soc_private;
+ struct cam_sensor_power_ctrl_t *power_info;
o_ctrl = platform_get_drvdata(pdev);
if (!o_ctrl) {
@@ -324,6 +336,12 @@
return -EINVAL;
}
+ soc_private =
+ (struct cam_ois_soc_private *)o_ctrl->soc_info.soc_private;
+ power_info = &soc_private->power_info;
+
+ kfree(power_info->power_setting);
+ kfree(power_info->power_down_setting);
kfree(o_ctrl->soc_info.soc_private);
kfree(o_ctrl->io_master_info.cci_client);
kfree(o_ctrl);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr/cam_res_mgr.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr/cam_res_mgr.c
index 949f902..d588b24 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr/cam_res_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr/cam_res_mgr.c
@@ -555,16 +555,20 @@
if (!found) {
gpio_set_value_cansleep(gpio, value);
} else {
- if (value)
+ if (value) {
gpio_res->power_on_count++;
- else
- gpio_res->power_on_count--;
-
- if (gpio_res->power_on_count > 0) {
- gpio_set_value_cansleep(gpio, value);
+ if (gpio_res->power_on_count < 2) {
+ gpio_set_value_cansleep(gpio, value);
+ CAM_DBG(CAM_RES,
+ "Shared GPIO(%d) : HIGH", gpio);
+ }
} else {
- gpio_res->power_on_count = 0;
- gpio_set_value_cansleep(gpio, 0);
+ gpio_res->power_on_count--;
+ if (gpio_res->power_on_count < 1) {
+ gpio_set_value_cansleep(gpio, value);
+ CAM_DBG(CAM_RES,
+ "Shared GPIO(%d) : LOW", gpio);
+ }
}
}
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 bc92d7e..97158e4 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
@@ -182,12 +182,7 @@
return rc;
}
}
-
- i2c_reg_settings->request_id =
- csl_packet->header.request_id;
- i2c_reg_settings->is_settings_valid = 1;
- cam_sensor_update_req_mgr(s_ctrl, csl_packet);
- break;
+ break;
}
case CAM_SENSOR_PACKET_OPCODE_SENSOR_NOP: {
cam_sensor_update_req_mgr(s_ctrl, csl_packet);
@@ -207,6 +202,14 @@
CAM_ERR(CAM_SENSOR, "Fail parsing I2C Pkt: %d", rc);
return rc;
}
+
+ if ((csl_packet->header.op_code & 0xFFFFFF) ==
+ CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE) {
+ i2c_reg_settings->request_id =
+ csl_packet->header.request_id;
+ cam_sensor_update_req_mgr(s_ctrl, csl_packet);
+ }
+
return rc;
}
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
index 39793b6..0a01b6f 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
@@ -877,6 +877,7 @@
mpq_demux->sdmx_eos = 0;
mpq_demux->sdmx_log_level = SDMX_LOG_NO_PRINT;
mpq_demux->ts_packet_timestamp_source = 0;
+ mpq_demux->disable_cache_ops = 1;
if (mpq_demux->demux.feednum > MPQ_MAX_DMX_FILES) {
MPQ_DVB_ERR_PRINT(
@@ -6349,7 +6350,8 @@
continue;
/* Invalidate output buffer before processing the results */
- mpq_sdmx_invalidate_buffer(mpq_feed);
+ if (!mpq_demux->disable_cache_ops)
+ mpq_sdmx_invalidate_buffer(mpq_feed);
if (sts->error_indicators & SDMX_FILTER_ERR_MD_BUF_FULL)
MPQ_DVB_ERR_PRINT(
@@ -6571,13 +6573,15 @@
* We must flush the buffer before SDMX starts reading from it
* so that it gets a valid data in memory.
*/
- ret = msm_ion_do_cache_op(mpq_demux->ion_client,
- ion_handle, rbuf->data,
- rbuf->size, ION_IOC_CLEAN_CACHES);
- if (ret)
- MPQ_DVB_ERR_PRINT(
- "%s: msm_ion_do_cache_op failed, ret = %d\n",
- __func__, ret);
+ if (!mpq_demux->disable_cache_ops) {
+ ret = msm_ion_do_cache_op(mpq_demux->ion_client,
+ ion_handle, rbuf->data,
+ rbuf->size, ION_IOC_CLEAN_CACHES);
+ if (ret)
+ MPQ_DVB_ERR_PRINT(
+ "%s: msm_ion_do_cache_op failed, ret = %d\n",
+ __func__, ret);
+ }
return mpq_sdmx_process(mpq_demux, &buf_desc, count,
read_offset, mpq_demux->demux.ts_packet_size);
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
index 0c20a89..a187707 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
@@ -543,6 +543,8 @@
ktime_t last_notification_time;
int ts_packet_timestamp_source;
+ /* Disable cache operations on qseecom heap since not supported */
+ int disable_cache_ops;
};
/**
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
index ae01baf..dc041a7 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
@@ -139,6 +139,43 @@
return clk_forced_on;
}
+void sde_mdp_halt_vbif_xin(struct sde_mdp_vbif_halt_params *params)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ u32 reg_val;
+ bool forced_on;
+
+ if (!mdata || !params || !params->reg_off_mdp_clk_ctrl) {
+ SDEROT_ERR("null input parameter\n");
+ return;
+ }
+
+ if (params->xin_id > MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0_XIN1) {
+ SDEROT_ERR("xin_id:%d exceed max limit\n", params->xin_id);
+ return;
+ }
+
+ forced_on = force_on_xin_clk(params->bit_off_mdp_clk_ctrl,
+ params->reg_off_mdp_clk_ctrl, true);
+
+ SDEROT_EVTLOG(forced_on, params->xin_id);
+
+ reg_val = SDE_VBIF_READ(mdata, MMSS_VBIF_XIN_HALT_CTRL0);
+ SDE_VBIF_WRITE(mdata, MMSS_VBIF_XIN_HALT_CTRL0,
+ reg_val | BIT(params->xin_id));
+
+ /* this is a polling operation */
+ sde_mdp_wait_for_xin_halt(params->xin_id);
+
+ reg_val = SDE_VBIF_READ(mdata, MMSS_VBIF_XIN_HALT_CTRL0);
+ SDE_VBIF_WRITE(mdata, MMSS_VBIF_XIN_HALT_CTRL0,
+ reg_val & ~BIT(params->xin_id));
+
+ if (forced_on)
+ force_on_xin_clk(params->bit_off_mdp_clk_ctrl,
+ params->reg_off_mdp_clk_ctrl, false);
+}
+
u32 sde_mdp_get_ot_limit(u32 width, u32 height, u32 pixfmt, u32 fps, u32 is_rd)
{
struct sde_rot_data_type *mdata = sde_rot_get_mdata();
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
index 400f53b..c85d255 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
@@ -63,6 +63,18 @@
u32 rotsts_busy_mask;
};
+/*
+ * struct sde_mdp_vbif_halt_params: parameters for issue halt request to vbif
+ * @xin_id: xin port number of vbif
+ * @reg_off_mdp_clk_ctrl: reg offset for vbif clock control
+ * @bit_off_mdp_clk_ctrl: bit offset for vbif clock control
+ */
+struct sde_mdp_vbif_halt_params {
+ u32 xin_id;
+ u32 reg_off_mdp_clk_ctrl;
+ u32 bit_off_mdp_clk_ctrl;
+};
+
enum sde_bus_vote_type {
VOTE_INDEX_DISABLE,
VOTE_INDEX_19_MHZ,
@@ -276,6 +288,8 @@
void sde_mdp_set_ot_limit(struct sde_mdp_set_ot_params *params);
+void sde_mdp_halt_vbif_xin(struct sde_mdp_vbif_halt_params *params);
+
int sde_mdp_init_vbif(void);
#define SDE_VBIF_WRITE(mdata, offset, value) \
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
index ab3223e..d19e475 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -2513,6 +2513,45 @@
req->finished = true;
}
+void sde_rotator_abort_inline_request(struct sde_rot_mgr *mgr,
+ struct sde_rot_file_private *private,
+ struct sde_rot_entry_container *req)
+{
+ struct kthread_work *commit_work;
+ struct kthread_work *done_work;
+ struct sde_rot_entry *entry;
+ struct sde_rot_hw_resource *hw;
+ int i;
+
+ if (!mgr || !private || !req || !req->entries)
+ return;
+
+ for (i = 0; i < req->count; i++) {
+ entry = &req->entries[i];
+ if (!entry)
+ continue;
+
+ commit_work = &entry->commit_work;
+ done_work = &entry->done_work;
+
+ hw = sde_rotator_get_hw_resource(entry->commitq, entry);
+ if (!hw) {
+ SDEROT_ERR("no hw for the queue\n");
+ SDEROT_EVTLOG(i, req->count, SDE_ROT_EVTLOG_ERROR);
+ continue;
+ }
+
+ SDEROT_EVTLOG(i, req->count);
+
+ mgr->ops_abort_hw(hw, entry);
+
+ sde_rot_mgr_unlock(mgr);
+ kthread_flush_work(commit_work);
+ kthread_flush_work(done_work);
+ sde_rot_mgr_lock(mgr);
+ }
+}
+
int sde_rotator_handle_request_common(struct sde_rot_mgr *mgr,
struct sde_rot_file_private *private,
struct sde_rot_entry_container *req)
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 57a68ed..3edb2d0 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
@@ -449,6 +449,8 @@
struct sde_rot_entry *entry);
int (*ops_cancel_hw)(struct sde_rot_hw_resource *hw,
struct sde_rot_entry *entry);
+ int (*ops_abort_hw)(struct sde_rot_hw_resource *hw,
+ struct sde_rot_entry *entry);
int (*ops_kickoff_entry)(struct sde_rot_hw_resource *hw,
struct sde_rot_entry *entry);
int (*ops_wait_for_entry)(struct sde_rot_hw_resource *hw,
@@ -670,6 +672,19 @@
struct sde_rot_entry_container *req);
/*
+ * sde_rotator_abort_inline_request - abort inline rotation request after start
+ * This function allows inline rotation requests to be aborted after
+ * sde_rotator_req_set_start has already been issued.
+ * @mgr: Pointer to rotator manager
+ * @private: Pointer to rotator manager per file context
+ * @req: Pointer to rotation request
+ * return: none
+ */
+void sde_rotator_abort_inline_request(struct sde_rot_mgr *mgr,
+ struct sde_rot_file_private *private,
+ struct sde_rot_entry_container *req);
+
+/*
* sde_rotator_handle_request_common - add the given request to rotator
* manager and clean up completed requests
* @rot_dev: Pointer to rotator device
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 a90bc4c..52aadfa 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
@@ -1731,6 +1731,16 @@
sde_rotator_req_finish(rot_dev->mgr, ctx->private, req);
sde_rotator_retire_request(request);
+ } else if (cmd_type == SDE_ROTATOR_INLINE_CMD_ABORT) {
+ if (!cmd->priv_handle) {
+ ret = -EINVAL;
+ SDEROT_ERR("invalid private handle\n");
+ goto error_invalid_handle;
+ }
+
+ request = cmd->priv_handle;
+ sde_rotator_abort_inline_request(rot_dev->mgr,
+ ctx->private, request->req);
}
sde_rot_mgr_unlock(rot_dev->mgr);
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_inline.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_inline.h
index 474662e..ba70489 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_inline.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_inline.h
@@ -27,12 +27,14 @@
* @SDE_ROTATOR_INLINE_CMD_COMMIT: commit command to hardware
* @SDE_ROTATOR_INLINE_CMD_START: ready to start inline rotation
* @SDE_ROTATOR_INLINE_CMD_CLEANUP: cleanup after commit is done
+ * @SDE_ROTATOR_INLINE_CMD_ABORT: abort current commit and reset
*/
enum sde_rotator_inline_cmd_type {
SDE_ROTATOR_INLINE_CMD_VALIDATE,
SDE_ROTATOR_INLINE_CMD_COMMIT,
SDE_ROTATOR_INLINE_CMD_START,
SDE_ROTATOR_INLINE_CMD_CLEANUP,
+ SDE_ROTATOR_INLINE_CMD_ABORT,
};
/**
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c
index 89ad438..40db488 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c
@@ -349,6 +349,12 @@
return 0;
}
+static int sde_rotator_abort_hw(struct sde_rot_hw_resource *hw,
+ struct sde_rot_entry *entry)
+{
+ return 0;
+}
+
static int sde_rotator_kickoff_entry(struct sde_rot_hw_resource *hw,
struct sde_rot_entry *entry)
{
@@ -691,6 +697,7 @@
mgr->hw_data = hw_data;
mgr->ops_config_hw = sde_rotator_config_hw;
mgr->ops_cancel_hw = sde_rotator_cancel_hw;
+ mgr->ops_abort_hw = sde_rotator_abort_hw;
mgr->ops_kickoff_entry = sde_rotator_kickoff_entry;
mgr->ops_wait_for_entry = sde_rotator_wait_for_entry;
mgr->ops_hw_alloc = sde_rotator_hw_alloc_ext;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
index f7b4e6e..927b937 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -51,6 +51,12 @@
/* wait for at most 2 vsync for lowest refresh rate (24hz) */
#define KOFF_TIMEOUT (42 * 8)
+/*
+ * When in sbuf mode, select a much longer wait, to allow the other driver
+ * to detect timeouts and abort if necessary.
+ */
+#define KOFF_TIMEOUT_SBUF (2000)
+
/* default stream buffer headroom in lines */
#define DEFAULT_SBUF_HEADROOM 20
#define DEFAULT_UBWC_MALSIZE 0
@@ -666,6 +672,25 @@
}
}
+static void sde_hw_rotator_halt_vbif_xin_client(void)
+{
+ struct sde_mdp_vbif_halt_params halt_params;
+
+ memset(&halt_params, 0, sizeof(struct sde_mdp_vbif_halt_params));
+ halt_params.xin_id = XIN_SSPP;
+ halt_params.reg_off_mdp_clk_ctrl = MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0;
+ halt_params.bit_off_mdp_clk_ctrl =
+ MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0_XIN0;
+ sde_mdp_halt_vbif_xin(&halt_params);
+
+ memset(&halt_params, 0, sizeof(struct sde_mdp_vbif_halt_params));
+ halt_params.xin_id = XIN_WRITEBACK;
+ halt_params.reg_off_mdp_clk_ctrl = MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0;
+ halt_params.bit_off_mdp_clk_ctrl =
+ MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0_XIN1;
+ sde_mdp_halt_vbif_xin(&halt_params);
+}
+
/**
* sde_hw_rotator_reset - Reset rotator hardware
* @rot: pointer to hw rotator
@@ -698,6 +723,9 @@
usleep_range(MS_TO_US(10), MS_TO_US(20));
SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_SW_RESET_OVERRIDE, 0);
+ /* halt vbif xin client to ensure no pending transaction */
+ sde_hw_rotator_halt_vbif_xin_client();
+
spin_lock_irqsave(&rot->rotisr_lock, flags);
/* update timestamp register with current context */
@@ -1766,6 +1794,8 @@
mask = ~(SDE_REGDMA_SWTS_MASK << SDE_REGDMA_SWTS_SHIFT);
}
+ SDEROT_EVTLOG(ctx->timestamp, queue_id, length, offset, ctx->sbuf_mode);
+
/* timestamp update can only be used in offline multi-context mode */
if (!ctx->sbuf_mode) {
/* Write timestamp after previous rotator job finished */
@@ -1778,6 +1808,8 @@
/* ensure command packet is issue before the submit command */
wmb();
+ SDEROT_EVTLOG(queue_id, enableInt, ts_length, offset);
+
if (queue_id == ROT_QUEUE_HIGH_PRIORITY) {
SDE_ROTREG_WRITE(rot->mdss_base,
REGDMA_CSR_REGDMA_QUEUE_0_SUBMIT,
@@ -1814,6 +1846,8 @@
if (rot->irq_num >= 0) {
SDEROT_DBG("Wait for Rotator completion\n");
rc = wait_for_completion_timeout(&ctx->rot_comp,
+ ctx->sbuf_mode ?
+ msecs_to_jiffies(KOFF_TIMEOUT_SBUF) :
msecs_to_jiffies(rot->koff_timeout));
spin_lock_irqsave(&rot->rotisr_lock, flags);
@@ -1872,6 +1906,7 @@
{
struct sde_hw_rotator *rot = ctx->rot;
int rc = 0;
+ bool abort;
u32 status;
u32 last_isr;
u32 last_ts;
@@ -1886,6 +1921,8 @@
ctx, ctx->timestamp);
rc = wait_event_timeout(ctx->regdma_waitq,
!sde_hw_rotator_pending_swts(rot, ctx, &swts),
+ ctx->sbuf_mode ?
+ msecs_to_jiffies(KOFF_TIMEOUT_SBUF) :
msecs_to_jiffies(rot->koff_timeout));
ATRACE_INT("sde_rot_done", 0);
@@ -1893,18 +1930,19 @@
last_isr = ctx->last_regdma_isr_status;
last_ts = ctx->last_regdma_timestamp;
+ abort = ctx->abort;
status = last_isr & REGDMA_INT_MASK;
int_id = last_ts & 1;
SDEROT_DBG("INT status:0x%X, INT id:%d, timestamp:0x%X\n",
status, int_id, last_ts);
- if (rc == 0 || (status & REGDMA_INT_ERR_MASK)) {
+ if (rc == 0 || (status & REGDMA_INT_ERR_MASK) || abort) {
bool pending;
pending = sde_hw_rotator_pending_swts(rot, ctx, &swts);
SDEROT_ERR(
- "Timeout wait for regdma interrupt status, ts:0x%X/0x%X pending:%d\n",
- ctx->timestamp, swts, pending);
+ "Timeout wait for regdma interrupt status, ts:0x%X/0x%X, pending:%d, abort:%d\n",
+ ctx->timestamp, swts, pending, abort);
if (status & REGDMA_WATCHDOG_INT)
SDEROT_ERR("REGDMA watchdog interrupt\n");
@@ -1917,7 +1955,7 @@
sde_hw_rotator_dump_status(rot, &ubwcerr);
- if (ubwcerr) {
+ if (ubwcerr || abort) {
/*
* Perform recovery for ROT SSPP UBWC decode
* error.
@@ -2756,6 +2794,42 @@
return 0;
}
+static int sde_hw_rotator_abort_kickoff(struct sde_rot_hw_resource *hw,
+ struct sde_rot_entry *entry)
+{
+ struct sde_hw_rotator *rot;
+ struct sde_hw_rotator_resource_info *resinfo;
+ struct sde_hw_rotator_context *ctx;
+ unsigned long flags;
+
+ if (!hw || !entry) {
+ SDEROT_ERR("null hw resource/entry\n");
+ return -EINVAL;
+ }
+
+ resinfo = container_of(hw, struct sde_hw_rotator_resource_info, hw);
+ rot = resinfo->rot;
+
+ /* Lookup rotator context from session-id */
+ ctx = sde_hw_rotator_get_ctx(rot, entry->item.session_id,
+ entry->item.sequence_id, hw->wb_id);
+ if (!ctx) {
+ SDEROT_ERR("Cannot locate rotator ctx from sesison id:%d\n",
+ entry->item.session_id);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&rot->rotisr_lock, flags);
+ sde_hw_rotator_update_swts(rot, ctx, ctx->timestamp);
+ ctx->abort = true;
+ wake_up_all(&ctx->regdma_waitq);
+ spin_unlock_irqrestore(&rot->rotisr_lock, flags);
+
+ SDEROT_EVTLOG(entry->item.session_id, ctx->timestamp);
+
+ return 0;
+}
+
/*
* sde_hw_rotator_wait4done - wait for completion notification
* @hw: Pointer to rotator resource
@@ -3535,6 +3609,7 @@
mgr->ops_hw_free = sde_hw_rotator_free_ext;
mgr->ops_config_hw = sde_hw_rotator_config;
mgr->ops_cancel_hw = sde_hw_rotator_cancel;
+ mgr->ops_abort_hw = sde_hw_rotator_abort_kickoff;
mgr->ops_kickoff_entry = sde_hw_rotator_kickoff;
mgr->ops_wait_for_entry = sde_hw_rotator_wait4done;
mgr->ops_hw_validate_entry = sde_hw_rotator_validate_entry;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
index 8b391ea..1ff43d6 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
@@ -231,6 +231,7 @@
bool is_secure;
bool is_traffic_shaping;
bool sbuf_mode;
+ bool abort;
u32 start_ctrl;
u32 sys_cache_mode;
u32 op_mode;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c
index e75f36e..b817ff0 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c
@@ -36,6 +36,14 @@
#define SMMU_SDE_ROT_SEC "qcom,smmu_sde_rot_sec"
#define SMMU_SDE_ROT_UNSEC "qcom,smmu_sde_rot_unsec"
+#ifndef SZ_4G
+#define SZ_4G (((size_t) SZ_1G) * 4)
+#endif
+
+#ifndef SZ_2G
+#define SZ_2G (((size_t) SZ_1G) * 2)
+#endif
+
struct sde_smmu_domain {
char *ctx_name;
int domain;
@@ -487,9 +495,9 @@
}
static struct sde_smmu_domain sde_rot_unsec = {
- "rot_0", SDE_IOMMU_DOMAIN_ROT_UNSECURE, SZ_128K, (SZ_1G - SZ_128K)};
+ "rot_0", SDE_IOMMU_DOMAIN_ROT_UNSECURE, SZ_2G, (SZ_4G - SZ_2G)};
static struct sde_smmu_domain sde_rot_sec = {
- "rot_1", SDE_IOMMU_DOMAIN_ROT_SECURE, SZ_1G, SZ_2G};
+ "rot_1", SDE_IOMMU_DOMAIN_ROT_SECURE, SZ_2G, (SZ_4G - SZ_2G)};
static const struct of_device_id sde_smmu_dt_match[] = {
{ .compatible = SMMU_SDE_ROT_UNSEC, .data = &sde_rot_unsec},
diff --git a/drivers/media/platform/msm/vidc/governors/msm_vidc_dyn_gov.c b/drivers/media/platform/msm/vidc/governors/msm_vidc_dyn_gov.c
index 45e8771..83b80d7 100644
--- a/drivers/media/platform/msm/vidc/governors/msm_vidc_dyn_gov.c
+++ b/drivers/media/platform/msm/vidc/governors/msm_vidc_dyn_gov.c
@@ -462,7 +462,7 @@
bw_for_1x_8bpc = fp_div(FP_INT(width * height), FP_INT(32 * 8));
bw_for_1x_8bpc = fp_mult(bw_for_1x_8bpc,
- fp_div(FP_INT(256 * 30), FP_INT(1000 * 1000)));
+ fp_div(FP_INT(((int)(256 * fps))), FP_INT(1000 * 1000)));
dpb_bw_for_1x = dpb_bpp == 8 ? bw_for_1x_8bpc :
fp_mult(bw_for_1x_8bpc, fp_mult(ten_bpc_packing_factor,
@@ -715,7 +715,7 @@
bw_for_1x_8bpc = fp_div(FP_INT(width * height), FP_INT(32 * 8));
bw_for_1x_8bpc = fp_mult(bw_for_1x_8bpc,
- fp_div(FP_INT(256 * 30), FP_INT(1000 * 1000)));
+ fp_div(FP_INT(((int)(256 * fps))), FP_INT(1000 * 1000)));
dpb_bw_for_1x = dpb_bpp == 8 ? bw_for_1x_8bpc :
fp_mult(bw_for_1x_8bpc, fp_mult(ten_bpc_packing_factor,
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index cf0413e..89e83b8 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -1877,6 +1877,22 @@
pkt->size += sizeof(u32) + sizeof(*work_mode);
break;
}
+ case HAL_PARAM_VENC_HDR10_PQ_SEI:
+ {
+ struct hfi_hdr10_pq_sei *hfi;
+ struct hal_hdr10_pq_sei *prop =
+ (struct hal_hdr10_pq_sei *) pdata;
+
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_PARAM_VENC_HDR10_PQ_SEI;
+ hfi = (struct hfi_hdr10_pq_sei *)
+ &pkt->rg_property_data[1];
+
+ memcpy(hfi, prop, sizeof(*hfi));
+ pkt->size += sizeof(u32) +
+ sizeof(struct hfi_hdr10_pq_sei);
+ break;
+ }
/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
case HAL_CONFIG_BUFFER_REQUIREMENTS:
case HAL_CONFIG_PRIORITY:
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index 286a67e..9238176 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -808,7 +808,7 @@
int msm_vdec_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
{
- int rc = 0;
+ int rc = 0, temp;
struct hal_nal_stream_format_supported stream_format;
struct hal_enable_picture enable_picture;
struct hal_enable hal_property;
@@ -1033,6 +1033,31 @@
rc);
break;
case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY:
+ temp_ctrl = TRY_GET_CTRL(
+ V4L2_CID_MPEG_VIDC_VIDEO_DPB_COLOR_FORMAT);
+ switch (temp_ctrl->val) {
+ case V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_UBWC:
+ temp = V4L2_PIX_FMT_NV12_UBWC;
+ break;
+ case V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_TP10_UBWC:
+ temp = V4L2_PIX_FMT_NV12_TP10_UBWC;
+ break;
+ case V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_NONE:
+ default:
+ dprintk(VIDC_DBG,
+ "set default dpb color format as NV12_UBWC\n");
+ temp = V4L2_PIX_FMT_NV12_UBWC;
+ break;
+ }
+ rc = msm_comm_set_color_format(inst,
+ HAL_BUFFER_OUTPUT, temp);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "%s Failed setting output color format: %#x\n",
+ __func__, rc);
+ break;
+ }
+
multi_stream.buffer_type = HAL_BUFFER_OUTPUT2;
multi_stream.enable = true;
pdata = &multi_stream;
@@ -1258,6 +1283,14 @@
}
rc = msm_vidc_update_host_buff_counts(inst);
inst->clk_data.dpb_fourcc = fourcc;
+ control.id =
+ V4L2_CID_MPEG_VIDC_VIDEO_DPB_COLOR_FORMAT;
+ control.value = ext_control[i].value;
+ rc = msm_comm_s_ctrl(inst, &control);
+ if (rc)
+ dprintk(VIDC_ERR,
+ "%s: set control dpb color format %d failed\n",
+ __func__, control.value);
break;
default:
dprintk(VIDC_ERR,
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 266c50e..85b4724 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -1068,6 +1068,135 @@
),
.qmenu = mpeg_video_flip,
},
+ {
+ .id = V4L2_CID_MPEG_VIDC_VENC_HDR_INFO,
+ .name = "Enable/Disable HDR INFO",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = V4L2_MPEG_VIDC_VENC_HDR_INFO_DISABLED,
+ .maximum = V4L2_MPEG_VIDC_VENC_HDR_INFO_ENABLED,
+ .default_value = V4L2_MPEG_VIDC_VENC_HDR_INFO_DISABLED,
+ .step = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_00,
+ .name = "RGB PRIMARIES[0][0]",
+ .type = V4L2_CTRL_TYPE_U32,
+ .minimum = 0,
+ .maximum = UINT_MAX,
+ .default_value = 0,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_01,
+ .name = "RGB PRIMARIES[0][1]",
+ .type = V4L2_CTRL_TYPE_U32,
+ .minimum = 0,
+ .maximum = UINT_MAX,
+ .default_value = 0,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_10,
+ .name = "RGB PRIMARIES[1][0]",
+ .type = V4L2_CTRL_TYPE_U32,
+ .minimum = 0,
+ .maximum = UINT_MAX,
+ .default_value = 0,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_11,
+ .name = "RGB PRIMARIES[1][1]",
+ .type = V4L2_CTRL_TYPE_U32,
+ .minimum = 0,
+ .maximum = UINT_MAX,
+ .default_value = 0,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_20,
+ .name = "RGB PRIMARIES[2][0]",
+ .type = V4L2_CTRL_TYPE_U32,
+ .minimum = 0,
+ .maximum = UINT_MAX,
+ .default_value = 0,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_21,
+ .name = "RGB PRIMARIES[2][1]",
+ .type = V4L2_CTRL_TYPE_U32,
+ .minimum = 0,
+ .maximum = UINT_MAX,
+ .default_value = 0,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_X,
+ .name = "WHITE POINT X",
+ .type = V4L2_CTRL_TYPE_U32,
+ .minimum = 0,
+ .maximum = UINT_MAX,
+ .default_value = 0,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_Y,
+ .name = "WHITE POINT Y",
+ .type = V4L2_CTRL_TYPE_U32,
+ .minimum = 0,
+ .maximum = UINT_MAX,
+ .default_value = 0,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VENC_MAX_DISP_LUM,
+ .name = "MAX DISPLAY LUMINANCE",
+ .type = V4L2_CTRL_TYPE_U32,
+ .minimum = 0,
+ .maximum = UINT_MAX,
+ .default_value = 0,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VENC_MIN_DISP_LUM,
+ .name = "MIN DISPLAY LUMINANCE",
+ .type = V4L2_CTRL_TYPE_U32,
+ .minimum = 0,
+ .maximum = UINT_MAX,
+ .default_value = 0,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VENC_MAX_CLL,
+ .name = "MAX CLL",
+ .type = V4L2_CTRL_TYPE_U32,
+ .minimum = 0,
+ .maximum = UINT_MAX,
+ .default_value = 0,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VENC_MAX_FLL,
+ .name = "MAX FLL",
+ .type = V4L2_CTRL_TYPE_U32,
+ .minimum = 0,
+ .maximum = UINT_MAX,
+ .default_value = 0,
+ .step = 1,
+ .qmenu = NULL,
+ },
};
@@ -2095,6 +2224,19 @@
case V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MAX:
case V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MAX:
case V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MAX:
+ case V4L2_CID_MPEG_VIDC_VENC_HDR_INFO:
+ case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_00:
+ case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_01:
+ case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_10:
+ case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_11:
+ case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_20:
+ case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_21:
+ case V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_X:
+ case V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_Y:
+ case V4L2_CID_MPEG_VIDC_VENC_MAX_DISP_LUM:
+ case V4L2_CID_MPEG_VIDC_VENC_MIN_DISP_LUM:
+ case V4L2_CID_MPEG_VIDC_VENC_MAX_CLL:
+ case V4L2_CID_MPEG_VIDC_VENC_MAX_FLL:
dprintk(VIDC_DBG, "Set the control : %#x using ext ctrl\n",
ctrl->id);
break;
@@ -2133,6 +2275,11 @@
struct hal_frame_size blur_res;
struct hal_quantization_range qp_range;
struct hal_quantization qp;
+ struct hal_hdr10_pq_sei hdr10_sei_params;
+ struct msm_vidc_mastering_display_colour_sei_payload *mdisp_sei
+ = &(hdr10_sei_params.disp_color_sei);
+ struct msm_vidc_content_light_level_sei_payload *cll_sei
+ = &(hdr10_sei_params.cll_sei);
if (!inst || !inst->core || !inst->core->device || !ctrl) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
@@ -2281,6 +2428,75 @@
i++;
}
break;
+ case V4L2_CID_MPEG_VIDC_VENC_HDR_INFO:
+ if (control[i].value ==
+ V4L2_MPEG_VIDC_VENC_HDR_INFO_DISABLED)
+ break;
+ memset(&hdr10_sei_params, 0, sizeof(hdr10_sei_params));
+ i++;
+ while (i < ctrl->count) {
+ switch (control[i].id) {
+ case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_00:
+ mdisp_sei->nDisplayPrimariesX[0] =
+ control[i].value;
+ break;
+ case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_01:
+ mdisp_sei->nDisplayPrimariesY[0] =
+ control[i].value;
+ break;
+ case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_10:
+ mdisp_sei->nDisplayPrimariesX[1] =
+ control[i].value;
+ break;
+ case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_11:
+ mdisp_sei->nDisplayPrimariesY[1] =
+ control[i].value;
+ break;
+ case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_20:
+ mdisp_sei->nDisplayPrimariesX[2] =
+ control[i].value;
+ break;
+ case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_21:
+ mdisp_sei->nDisplayPrimariesY[2] =
+ control[i].value;
+ break;
+ case V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_X:
+ mdisp_sei->nWhitePointX =
+ control[i].value;
+ break;
+ case V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_Y:
+ mdisp_sei->nWhitePointY =
+ control[i].value;
+ break;
+ case V4L2_CID_MPEG_VIDC_VENC_MAX_DISP_LUM:
+ mdisp_sei->
+ nMaxDisplayMasteringLuminance =
+ control[i].value;
+ break;
+ case V4L2_CID_MPEG_VIDC_VENC_MIN_DISP_LUM:
+ mdisp_sei->
+ nMinDisplayMasteringLuminance =
+ control[i].value;
+ break;
+ case V4L2_CID_MPEG_VIDC_VENC_MAX_CLL:
+ cll_sei->nMaxContentLight =
+ control[i].value;
+ break;
+ case V4L2_CID_MPEG_VIDC_VENC_MAX_FLL:
+ cll_sei->nMaxPicAverageLight =
+ control[i].value;
+ break;
+ default:
+ dprintk(VIDC_ERR,
+ "Unknown Ctrl:%d, not part of HDR Info",
+ control[i].id);
+ }
+ i++;
+ }
+ property_id =
+ HAL_PARAM_VENC_HDR10_PQ_SEI;
+ pdata = &hdr10_sei_params;
+ break;
default:
dprintk(VIDC_ERR, "Invalid id set: %d\n",
control[i].id);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index 907e01f..4e282c3 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -1781,12 +1781,6 @@
dprintk(VIDC_ERR,
"Failed to release mark_data buffers\n");
- /*
- * At this point all buffes should be with driver
- * irrespective of scenario
- */
- msm_comm_validate_output_buffers(inst);
-
msm_comm_release_eos_buffers(inst);
if (msm_comm_release_output_buffers(inst, true))
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
index 5183ddd..e258f1f 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
@@ -146,7 +146,7 @@
vote_data->use_dpb_read = false;
/* Check if driver can vote for lower bus BW */
- if (inst->clk_data.load <= inst->clk_data.load_norm) {
+ if (inst->clk_data.load < inst->clk_data.load_norm) {
vote_data->compression_ratio = max_cr;
vote_data->complexity_factor = min_cf;
vote_data->input_cr = max_input_cr;
@@ -283,9 +283,11 @@
return rc;
}
-static inline int get_pending_bufs_fw(struct msm_vidc_inst *inst)
+static inline int get_bufs_outside_fw(struct msm_vidc_inst *inst)
{
- int fw_out_qsize = 0;
+ u32 fw_out_qsize = 0, i = 0;
+ struct vb2_queue *q = NULL;
+ struct vb2_buffer *vb = NULL;
/*
* DCVS always operates on Uncompressed buffers.
@@ -294,10 +296,29 @@
if (inst->state >= MSM_VIDC_OPEN_DONE &&
inst->state < MSM_VIDC_STOP_DONE) {
- if (inst->session_type == MSM_VIDC_DECODER)
- fw_out_qsize = inst->count.ftb - inst->count.fbd;
- else
- fw_out_qsize = inst->count.etb - inst->count.ebd;
+
+ /*
+ * For decoder, there will be some frames with client
+ * but not to be displayed. Ex : VP9 DECODE_ONLY frames.
+ * Hence don't count them.
+ */
+
+ if (inst->session_type == MSM_VIDC_DECODER) {
+ q = &inst->bufq[CAPTURE_PORT].vb2_bufq;
+ for (i = 0; i < q->num_buffers; i++) {
+ vb = q->bufs[i];
+ if (vb->state != VB2_BUF_STATE_ACTIVE &&
+ vb->planes[0].bytesused)
+ fw_out_qsize++;
+ }
+ } else {
+ q = &inst->bufq[OUTPUT_PORT].vb2_bufq;
+ for (i = 0; i < q->num_buffers; i++) {
+ vb = q->bufs[i];
+ if (vb->state != VB2_BUF_STATE_ACTIVE)
+ fw_out_qsize++;
+ }
+ }
}
return fw_out_qsize;
@@ -328,7 +349,7 @@
core = inst->core;
mutex_lock(&inst->lock);
- fw_pending_bufs = get_pending_bufs_fw(inst);
+ buffers_outside_fw = get_bufs_outside_fw(inst);
output_buf_req = get_buff_req_buffer(inst,
dcvs->buffer_type);
@@ -345,8 +366,8 @@
min_output_buf = output_buf_req->buffer_count_min;
- /* Buffers outside FW are with display */
- buffers_outside_fw = total_output_buf - fw_pending_bufs;
+ /* Buffers outside Display are with FW. */
+ fw_pending_bufs = total_output_buf - buffers_outside_fw;
dprintk(VIDC_PROF,
"Counts : total_output_buf = %d Min buffers = %d fw_pending_bufs = %d buffers_outside_fw = %d\n",
total_output_buf, min_output_buf, fw_pending_bufs,
@@ -372,7 +393,7 @@
if (buffers_outside_fw <= dcvs->max_threshold)
dcvs->load = dcvs->load_high;
- else if (fw_pending_bufs <= min_output_buf)
+ else if (fw_pending_bufs < min_output_buf)
dcvs->load = dcvs->load_low;
else
dcvs->load = dcvs->load_norm;
@@ -877,10 +898,7 @@
return;
}
dcvs->max_threshold = output_buf_req->buffer_count_actual -
- output_buf_req->buffer_count_min_host + 1;
- /* Compensate for decode only frames */
- if (inst->fmts[OUTPUT_PORT].fourcc == V4L2_PIX_FMT_VP9)
- dcvs->max_threshold += 2;
+ output_buf_req->buffer_count_min_host + 2;
dcvs->min_threshold =
msm_vidc_get_extra_buff_count(inst, dcvs->buffer_type);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index ed3cfa3..03174c4 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -2202,6 +2202,8 @@
if (!core->trigger_ssr)
msm_comm_print_inst_info(inst);
}
+ /* handle the hw error before core released to get full debug info */
+ msm_vidc_handle_hw_error(core);
dprintk(VIDC_DBG, "Calling core_release\n");
rc = call_hfi_op(hdev, core_release, hdev->hfi_device_data);
if (rc) {
@@ -2212,10 +2214,7 @@
core->state = VIDC_CORE_UNINIT;
mutex_unlock(&core->lock);
- dprintk(VIDC_ERR,
- "SYS_ERROR can potentially crash the system\n");
-
- msm_vidc_handle_hw_error(core);
+ dprintk(VIDC_WARN, "SYS_ERROR handled.\n");
}
void msm_comm_session_clean(struct msm_vidc_inst *inst)
@@ -2328,9 +2327,6 @@
__func__, vb->type);
return -EINVAL;
}
- msm_vidc_debugfs_update(inst, port == CAPTURE_PORT ?
- MSM_VIDC_DEBUGFS_EVENT_FBD :
- MSM_VIDC_DEBUGFS_EVENT_EBD);
mutex_lock(&inst->bufq[port].lock);
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
@@ -2456,6 +2452,7 @@
*/
msm_comm_put_vidc_buffer(inst, mbuf);
msm_comm_vb2_buffer_done(inst, vb2);
+ msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_EBD);
kref_put_mbuf(mbuf);
exit:
put_inst(inst);
@@ -2657,6 +2654,7 @@
*/
msm_comm_put_vidc_buffer(inst, mbuf);
msm_comm_vb2_buffer_done(inst, vb2);
+ msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_FBD);
kref_put_mbuf(mbuf);
exit:
@@ -5440,7 +5438,7 @@
int rc = 0;
struct hfi_device *hdev;
struct msm_vidc_core *core;
- u32 output_height, output_width;
+ u32 output_height, output_width, input_height, input_width;
u32 rotation;
if (!inst || !inst->core || !inst->core->device) {
@@ -5463,6 +5461,22 @@
return -ENOTSUPP;
}
+ output_height = inst->prop.height[CAPTURE_PORT];
+ output_width = inst->prop.width[CAPTURE_PORT];
+ input_height = inst->prop.height[OUTPUT_PORT];
+ input_width = inst->prop.width[OUTPUT_PORT];
+
+ if (input_width % 2 != 0 || input_height % 2 != 0 ||
+ output_width % 2 != 0 || output_height % 2 != 0) {
+ dprintk(VIDC_ERR,
+ "Height and Width should be even numbers for NV12\n");
+ dprintk(VIDC_ERR,
+ "Input WxH = (%u)x(%u), Output WxH = (%u)x(%u)\n",
+ input_width, input_height,
+ output_width, output_height);
+ rc = -ENOTSUPP;
+ }
+
rotation = msm_comm_g_ctrl_for_id(inst,
V4L2_CID_MPEG_VIDC_VIDEO_ROTATION);
@@ -6485,7 +6499,10 @@
if (msm_smem_unmap_dma_buf(inst, &mbuf->smem[i]))
print_vidc_buffer(VIDC_ERR,
"dqbuf: unmap failed..", inst, mbuf);
- } /* else RBR event expected */
+ } else {
+ /* RBR event expected */
+ mbuf->flags |= MSM_VIDC_FLAG_RBR_PENDING;
+ }
}
/*
* remove the entry if plane[0].refcount is zero else
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index 2e2dd13..0b6331c 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -423,9 +423,9 @@
u32 id;
char name[MAX_NAME_LENGTH];
enum v4l2_ctrl_type type;
- s32 minimum;
- s32 maximum;
- s32 default_value;
+ s64 minimum;
+ s64 maximum;
+ s64 default_value;
u32 step;
u32 menu_skip_mask;
u32 flags;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_platform.c b/drivers/media/platform/msm/vidc/msm_vidc_platform.c
index 1818788..145b9d3 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_platform.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_platform.c
@@ -140,11 +140,11 @@
},
{
.key = "qcom,power-collapse-delay",
- .value = 500,
+ .value = 1500,
},
{
.key = "qcom,hw-resp-timeout",
- .value = 250,
+ .value = 1000,
},
{
.key = "qcom,debug-timeout",
@@ -162,6 +162,10 @@
.value = 1,
},
{
+ .key = "qcom,domain-attr-non-fatal-faults",
+ .value = 1,
+ },
+ {
.key = "qcom,max-secure-instances",
.value = 5,
},
@@ -205,6 +209,10 @@
.value = 1,
},
{
+ .key = "qcom,domain-attr-non-fatal-faults",
+ .value = 1,
+ },
+ {
.key = "qcom,max-secure-instances",
.value = 5,
},
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 53df90f5..2260b55 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -226,6 +226,7 @@
HAL_PARAM_VIDEO_CORES_USAGE,
HAL_PARAM_VIDEO_WORK_MODE,
HAL_PARAM_SECURE,
+ HAL_PARAM_VENC_HDR10_PQ_SEI,
};
enum hal_domain {
@@ -1398,6 +1399,11 @@
u32 rg_property_data[1];
};
+struct hal_hdr10_pq_sei {
+ struct msm_vidc_mastering_display_colour_sei_payload disp_color_sei;
+ struct msm_vidc_content_light_level_sei_payload cll_sei;
+};
+
#define call_hfi_op(q, op, args...) \
(((q) && (q)->op) ? ((q)->op(args)) : 0)
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index 001ca39..ca6d803 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -321,6 +321,10 @@
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x033)
#define HFI_PROPERTY_PARAM_VENC_IFRAMESIZE \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x034)
+#define HFI_PROPERTY_PARAM_VENC_SEND_OUTPUT_FOR_SKIPPED_FRAMES \
+ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x035)
+#define HFI_PROPERTY_PARAM_VENC_HDR10_PQ_SEI \
+ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x036)
#define HFI_PROPERTY_CONFIG_VENC_COMMON_START \
(HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x6000)
@@ -1058,4 +1062,24 @@
u32 packet_type;
u32 trigger_type;
};
+
+struct hfi_mastering_display_colour_sei_payload {
+ u32 display_primariesX[3];
+ u32 display_primariesY[3];
+ u32 white_pointX;
+ u32 white_pointY;
+ u32 max_display_mastering_luminance;
+ u32 min_display_mastering_luminance;
+};
+
+struct hfi_content_light_level_sei_payload {
+ u32 max_content_light;
+ u32 max_pic_average_light;
+};
+
+struct hfi_hdr10_pq_sei {
+ struct hfi_mastering_display_colour_sei_payload mdisp_info;
+ struct hfi_content_light_level_sei_payload cll_info;
+};
+
#endif
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 252ab99..9f2a64c 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -2005,6 +2005,13 @@
goto done;
}
+ /* Validate the user-provided bit-size and offset */
+ if (mapping->size > 32 ||
+ mapping->offset + mapping->size > ctrl->info.size * 8) {
+ ret = -EINVAL;
+ goto done;
+ }
+
list_for_each_entry(map, &ctrl->info.mappings, list) {
if (mapping->id == map->id) {
uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', "
diff --git a/drivers/misc/cxl/api.c b/drivers/misc/cxl/api.c
index 2e5233b..ae85616 100644
--- a/drivers/misc/cxl/api.c
+++ b/drivers/misc/cxl/api.c
@@ -244,6 +244,10 @@
ctx->real_mode = false;
}
+ /*
+ * Increment driver use count. Enables global TLBIs for hash
+ * and callbacks to handle the segment table
+ */
cxl_ctx_get();
if ((rc = cxl_ops->attach_process(ctx, kernel, wed, 0))) {
diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c
index afa2113..d3e0094 100644
--- a/drivers/misc/cxl/file.c
+++ b/drivers/misc/cxl/file.c
@@ -91,7 +91,6 @@
pr_devel("afu_open pe: %i\n", ctx->pe);
file->private_data = ctx;
- cxl_ctx_get();
/* indicate success */
rc = 0;
@@ -213,6 +212,12 @@
ctx->glpid = get_task_pid(current->group_leader, PIDTYPE_PID);
+ /*
+ * Increment driver use count. Enables global TLBIs for hash
+ * and callbacks to handle the segment table
+ */
+ cxl_ctx_get();
+
trace_cxl_attach(ctx, work.work_element_descriptor, work.num_interrupts, amr);
if ((rc = cxl_ops->attach_process(ctx, false, work.work_element_descriptor,
@@ -222,6 +227,7 @@
put_pid(ctx->glpid);
put_pid(ctx->pid);
ctx->glpid = ctx->pid = NULL;
+ cxl_ctx_put();
goto out;
}
diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c
index 7eeb71a..4d44084 100644
--- a/drivers/misc/lkdtm_core.c
+++ b/drivers/misc/lkdtm_core.c
@@ -535,7 +535,9 @@
/* Handle test-specific clean-up. */
lkdtm_usercopy_exit();
- unregister_jprobe(lkdtm_jprobe);
+ if (lkdtm_jprobe != NULL)
+ unregister_jprobe(lkdtm_jprobe);
+
pr_info("Crash point unregistered\n");
}
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index fe5dad7..afb8d72 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -2616,6 +2616,8 @@
if (!strcmp((void *)ptr_app->app_name,
(void *)data->client.app_name)) {
found_app = true;
+ if (ptr_app->app_blocked)
+ app_crash = false;
if (app_crash || ptr_app->ref_cnt == 1)
unload = true;
break;
@@ -4785,8 +4787,12 @@
resp.data = desc->ret[2]; /*listener_id*/
mutex_lock(&app_access_lock);
- ret = __qseecom_process_reentrancy(&resp, &dummy_app_entry,
+ if (qseecom.qsee_reentrancy_support)
+ ret = __qseecom_process_reentrancy(&resp, &dummy_app_entry,
&dummy_private_data);
+ else
+ ret = __qseecom_process_incomplete_cmd(&dummy_private_data,
+ &resp);
mutex_unlock(&app_access_lock);
if (ret)
pr_err("Failed on cmd %d for lsnr %d session %d, ret = %d\n",
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index a209aa6..538a8d9 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -1221,16 +1221,16 @@
if (md->area_type & MMC_BLK_DATA_AREA_RPMB)
mmc_blk_part_switch(card, dev_get_drvdata(&card->dev));
- mmc_put_card(card);
-
- err = mmc_blk_ioctl_copy_to_user(ic_ptr, idata);
-
if (mmc_card_cmdq(card)) {
if (mmc_cmdq_halt(card->host, false))
pr_err("%s: %s: cmdq unhalt failed\n",
mmc_hostname(card->host), __func__);
}
+ mmc_put_card(card);
+
+ err = mmc_blk_ioctl_copy_to_user(ic_ptr, idata);
+
cmd_done:
mmc_blk_put(md);
cmd_err:
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 7163e34..15c3e9e 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -669,9 +669,10 @@
{
struct mmc_card *card = filp->private_data;
struct mmc_wr_pack_stats *pack_stats;
- int i;
+ int i, ret = 0;
int max_num_of_packed_reqs = 0;
- char *temp_buf;
+ char *temp_buf, *temp_ubuf;
+ size_t tubuf_cnt = 0;
if (!card)
return cnt;
@@ -697,15 +698,24 @@
max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
- temp_buf = kmalloc(TEMP_BUF_SIZE, GFP_KERNEL);
+ if (cnt <= (strlen_user(ubuf) + 1))
+ goto exit;
+
+ temp_buf = kzalloc(TEMP_BUF_SIZE, GFP_KERNEL);
if (!temp_buf)
goto exit;
+ tubuf_cnt = cnt - strlen_user(ubuf) - 1;
+
+ temp_ubuf = kzalloc(tubuf_cnt, GFP_KERNEL);
+ if (!temp_ubuf)
+ goto cleanup;
+
spin_lock(&pack_stats->lock);
snprintf(temp_buf, TEMP_BUF_SIZE, "%s: write packing statistics:\n",
mmc_hostname(card->host));
- strlcat(ubuf, temp_buf, cnt);
+ strlcat(temp_ubuf, temp_buf, tubuf_cnt);
for (i = 1 ; i <= max_num_of_packed_reqs ; ++i) {
if (pack_stats->packing_events[i]) {
@@ -713,63 +723,63 @@
"%s: Packed %d reqs - %d times\n",
mmc_hostname(card->host), i,
pack_stats->packing_events[i]);
- strlcat(ubuf, temp_buf, cnt);
+ strlcat(temp_ubuf, temp_buf, tubuf_cnt);
}
}
snprintf(temp_buf, TEMP_BUF_SIZE,
"%s: stopped packing due to the following reasons:\n",
mmc_hostname(card->host));
- strlcat(ubuf, temp_buf, cnt);
+ strlcat(temp_ubuf, temp_buf, tubuf_cnt);
if (pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]) {
snprintf(temp_buf, TEMP_BUF_SIZE,
"%s: %d times: exceed max num of segments\n",
mmc_hostname(card->host),
pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]);
- strlcat(ubuf, temp_buf, cnt);
+ strlcat(temp_ubuf, temp_buf, tubuf_cnt);
}
if (pack_stats->pack_stop_reason[EXCEEDS_SECTORS]) {
snprintf(temp_buf, TEMP_BUF_SIZE,
"%s: %d times: exceed max num of sectors\n",
mmc_hostname(card->host),
pack_stats->pack_stop_reason[EXCEEDS_SECTORS]);
- strlcat(ubuf, temp_buf, cnt);
+ strlcat(temp_ubuf, temp_buf, tubuf_cnt);
}
if (pack_stats->pack_stop_reason[WRONG_DATA_DIR]) {
snprintf(temp_buf, TEMP_BUF_SIZE,
"%s: %d times: wrong data direction\n",
mmc_hostname(card->host),
pack_stats->pack_stop_reason[WRONG_DATA_DIR]);
- strlcat(ubuf, temp_buf, cnt);
+ strlcat(temp_ubuf, temp_buf, tubuf_cnt);
}
if (pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]) {
snprintf(temp_buf, TEMP_BUF_SIZE,
"%s: %d times: flush or discard\n",
mmc_hostname(card->host),
pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]);
- strlcat(ubuf, temp_buf, cnt);
+ strlcat(temp_ubuf, temp_buf, tubuf_cnt);
}
if (pack_stats->pack_stop_reason[EMPTY_QUEUE]) {
snprintf(temp_buf, TEMP_BUF_SIZE,
"%s: %d times: empty queue\n",
mmc_hostname(card->host),
pack_stats->pack_stop_reason[EMPTY_QUEUE]);
- strlcat(ubuf, temp_buf, cnt);
+ strlcat(temp_ubuf, temp_buf, tubuf_cnt);
}
if (pack_stats->pack_stop_reason[REL_WRITE]) {
snprintf(temp_buf, TEMP_BUF_SIZE,
"%s: %d times: rel write\n",
mmc_hostname(card->host),
pack_stats->pack_stop_reason[REL_WRITE]);
- strlcat(ubuf, temp_buf, cnt);
+ strlcat(temp_ubuf, temp_buf, tubuf_cnt);
}
if (pack_stats->pack_stop_reason[THRESHOLD]) {
snprintf(temp_buf, TEMP_BUF_SIZE,
"%s: %d times: Threshold\n",
mmc_hostname(card->host),
pack_stats->pack_stop_reason[THRESHOLD]);
- strlcat(ubuf, temp_buf, cnt);
+ strlcat(temp_ubuf, temp_buf, tubuf_cnt);
}
if (pack_stats->pack_stop_reason[LARGE_SEC_ALIGN]) {
@@ -777,25 +787,36 @@
"%s: %d times: Large sector alignment\n",
mmc_hostname(card->host),
pack_stats->pack_stop_reason[LARGE_SEC_ALIGN]);
- strlcat(ubuf, temp_buf, cnt);
+ strlcat(temp_ubuf, temp_buf, tubuf_cnt);
}
if (pack_stats->pack_stop_reason[RANDOM]) {
snprintf(temp_buf, TEMP_BUF_SIZE,
"%s: %d times: random request\n",
mmc_hostname(card->host),
pack_stats->pack_stop_reason[RANDOM]);
- strlcat(ubuf, temp_buf, cnt);
+ strlcat(temp_ubuf, temp_buf, tubuf_cnt);
}
if (pack_stats->pack_stop_reason[FUA]) {
snprintf(temp_buf, TEMP_BUF_SIZE,
"%s: %d times: fua request\n",
mmc_hostname(card->host),
pack_stats->pack_stop_reason[FUA]);
- strlcat(ubuf, temp_buf, cnt);
+ strlcat(temp_ubuf, temp_buf, tubuf_cnt);
}
+ if (strlen_user(ubuf) < cnt - strlen(temp_ubuf))
+ ret = copy_to_user((ubuf + strlen_user(ubuf)),
+ temp_ubuf, tubuf_cnt);
+ else
+ ret = -EFAULT;
+ if (ret)
+ pr_err("%s: %s: Copy to userspace failed: %s\n",
+ mmc_hostname(card->host), __func__, ubuf);
spin_unlock(&pack_stats->lock);
+ kfree(temp_ubuf);
+
+cleanup:
kfree(temp_buf);
pr_info("%s", ubuf);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index efb1b81..dd58288 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1414,6 +1414,23 @@
return err;
}
+static void mmc_select_driver_type(struct mmc_card *card)
+{
+ int card_drv_type, drive_strength, drv_type;
+
+ card_drv_type = card->ext_csd.raw_driver_strength |
+ mmc_driver_type_mask(0);
+
+ drive_strength = mmc_select_drive_strength(card,
+ card->ext_csd.hs200_max_dtr,
+ card_drv_type, &drv_type);
+
+ card->drive_strength = drive_strength;
+
+ if (drv_type)
+ mmc_set_driver_type(card->host, drv_type);
+}
+
static int mmc_select_hs400es(struct mmc_card *card)
{
struct mmc_host *host = card->host;
@@ -1462,6 +1479,8 @@
goto out_err;
}
+ mmc_select_driver_type(card);
+
/* Switch card to HS400 */
val = EXT_CSD_TIMING_HS400 |
card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
@@ -1495,23 +1514,6 @@
return err;
}
-static void mmc_select_driver_type(struct mmc_card *card)
-{
- int card_drv_type, drive_strength, drv_type;
-
- card_drv_type = card->ext_csd.raw_driver_strength |
- mmc_driver_type_mask(0);
-
- drive_strength = mmc_select_drive_strength(card,
- card->ext_csd.hs200_max_dtr,
- card_drv_type, &drv_type);
-
- card->drive_strength = drive_strength;
-
- if (drv_type)
- mmc_set_driver_type(card->host, drv_type);
-}
-
/*
* For device supporting HS200 mode, the following sequence
* should be done before executing the tuning process.
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index e32ed3d..6098489 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -277,7 +277,7 @@
sdio_free_func_cis(func);
kfree(func->info);
-
+ kfree(func->tmpbuf);
kfree(func);
}
@@ -292,6 +292,16 @@
if (!func)
return ERR_PTR(-ENOMEM);
+ /*
+ * allocate buffer separately to make sure it's properly aligned for
+ * DMA usage (incl. 64 bit DMA)
+ */
+ func->tmpbuf = kmalloc(4, GFP_KERNEL);
+ if (!func->tmpbuf) {
+ kfree(func);
+ return ERR_PTR(-ENOMEM);
+ }
+
func->card = card;
device_initialize(&func->dev);
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 32da1fd..c5469f1 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -1615,23 +1615,31 @@
pctrl_data->pins_drv_type_400KHz = pinctrl_lookup_state(
pctrl_data->pctrl, "ds_400KHz");
- if (IS_ERR(pctrl_data->pins_drv_type_400KHz))
+ if (IS_ERR(pctrl_data->pins_drv_type_400KHz)) {
dev_dbg(dev, "Could not get 400K pinstates, err:%d\n", ret);
+ pctrl_data->pins_drv_type_400KHz = NULL;
+ }
pctrl_data->pins_drv_type_50MHz = pinctrl_lookup_state(
pctrl_data->pctrl, "ds_50MHz");
- if (IS_ERR(pctrl_data->pins_drv_type_50MHz))
+ if (IS_ERR(pctrl_data->pins_drv_type_50MHz)) {
dev_dbg(dev, "Could not get 50M pinstates, err:%d\n", ret);
+ pctrl_data->pins_drv_type_50MHz = NULL;
+ }
pctrl_data->pins_drv_type_100MHz = pinctrl_lookup_state(
pctrl_data->pctrl, "ds_100MHz");
- if (IS_ERR(pctrl_data->pins_drv_type_100MHz))
+ if (IS_ERR(pctrl_data->pins_drv_type_100MHz)) {
dev_dbg(dev, "Could not get 100M pinstates, err:%d\n", ret);
+ pctrl_data->pins_drv_type_100MHz = NULL;
+ }
pctrl_data->pins_drv_type_200MHz = pinctrl_lookup_state(
pctrl_data->pctrl, "ds_200MHz");
- if (IS_ERR(pctrl_data->pins_drv_type_200MHz))
+ if (IS_ERR(pctrl_data->pins_drv_type_200MHz)) {
dev_dbg(dev, "Could not get 200M pinstates, err:%d\n", ret);
+ pctrl_data->pins_drv_type_200MHz = NULL;
+ }
pdata->pctrl_data = pctrl_data;
out:
diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index 3ec573c..c26debc 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -326,6 +326,7 @@
static void b53_set_forwarding(struct b53_device *dev, int enable)
{
+ struct dsa_switch *ds = dev->ds;
u8 mgmt;
b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
@@ -336,6 +337,15 @@
mgmt &= ~SM_SW_FWD_EN;
b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+
+ /* Include IMP port in dumb forwarding mode when no tagging protocol is
+ * set
+ */
+ if (ds->ops->get_tag_protocol(ds) == DSA_TAG_PROTO_NONE) {
+ b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, &mgmt);
+ mgmt |= B53_MII_DUMB_FWDG_EN;
+ b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, mgmt);
+ }
}
static void b53_enable_vlan(struct b53_device *dev, bool enable)
diff --git a/drivers/net/dsa/b53/b53_regs.h b/drivers/net/dsa/b53/b53_regs.h
index dac0af4..8104400 100644
--- a/drivers/net/dsa/b53/b53_regs.h
+++ b/drivers/net/dsa/b53/b53_regs.h
@@ -104,6 +104,10 @@
#define B53_UC_FWD_EN BIT(6)
#define B53_MC_FWD_EN BIT(7)
+/* Switch control (8 bit) */
+#define B53_SWITCH_CTRL 0x22
+#define B53_MII_DUMB_FWDG_EN BIT(6)
+
/* (16 bit) */
#define B53_UC_FLOOD_MASK 0x32
#define B53_MC_FLOOD_MASK 0x34
diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c
index aaf6fec..3660a3d 100644
--- a/drivers/net/ethernet/ibm/emac/mal.c
+++ b/drivers/net/ethernet/ibm/emac/mal.c
@@ -402,7 +402,7 @@
unsigned long flags;
MAL_DBG2(mal, "poll(%d)" NL, budget);
- again:
+
/* Process TX skbs */
list_for_each(l, &mal->poll_list) {
struct mal_commac *mc =
@@ -451,7 +451,6 @@
spin_lock_irqsave(&mal->lock, flags);
mal_disable_eob_irq(mal);
spin_unlock_irqrestore(&mal->lock, flags);
- goto again;
}
mc->ops->poll_tx(mc->dev);
}
diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c
index 9f2184b..b8778e7 100644
--- a/drivers/net/ethernet/ibm/ibmvnic.c
+++ b/drivers/net/ethernet/ibm/ibmvnic.c
@@ -1253,6 +1253,7 @@
release_sub_crq_queue(adapter,
adapter->tx_scrq[i]);
}
+ kfree(adapter->tx_scrq);
adapter->tx_scrq = NULL;
}
@@ -1265,6 +1266,7 @@
release_sub_crq_queue(adapter,
adapter->rx_scrq[i]);
}
+ kfree(adapter->rx_scrq);
adapter->rx_scrq = NULL;
}
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index 9affd7c..6a62447 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -7882,6 +7882,11 @@
pci_enable_wake(pdev, PCI_D3hot, 0);
pci_enable_wake(pdev, PCI_D3cold, 0);
+ /* In case of PCI error, adapter lose its HW address
+ * so we should re-assign it here.
+ */
+ hw->hw_addr = adapter->io_addr;
+
igb_reset(adapter);
wr32(E1000_WUS, ~0);
result = PCI_ERS_RESULT_RECOVERED;
diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index 0a4e81a..ed6fae9 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -4413,13 +4413,12 @@
struct mvpp2_txq_pcpu_buf *tx_buf =
txq_pcpu->buffs + txq_pcpu->txq_get_index;
- mvpp2_txq_inc_get(txq_pcpu);
-
dma_unmap_single(port->dev->dev.parent, tx_buf->phys,
tx_buf->size, DMA_TO_DEVICE);
- if (!tx_buf->skb)
- continue;
- dev_kfree_skb_any(tx_buf->skb);
+ if (tx_buf->skb)
+ dev_kfree_skb_any(tx_buf->skb);
+
+ mvpp2_txq_inc_get(txq_pcpu);
}
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_clock.c b/drivers/net/ethernet/mellanox/mlx4/en_clock.c
index a5fc46b..d4d97ca 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_clock.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_clock.c
@@ -88,10 +88,17 @@
}
}
+#define MLX4_EN_WRAP_AROUND_SEC 10UL
+/* By scheduling the overflow check every 5 seconds, we have a reasonably
+ * good chance we wont miss a wrap around.
+ * TOTO: Use a timer instead of a work queue to increase the guarantee.
+ */
+#define MLX4_EN_OVERFLOW_PERIOD (MLX4_EN_WRAP_AROUND_SEC * HZ / 2)
+
void mlx4_en_ptp_overflow_check(struct mlx4_en_dev *mdev)
{
bool timeout = time_is_before_jiffies(mdev->last_overflow_check +
- mdev->overflow_period);
+ MLX4_EN_OVERFLOW_PERIOD);
unsigned long flags;
if (timeout) {
@@ -236,7 +243,6 @@
.enable = mlx4_en_phc_enable,
};
-#define MLX4_EN_WRAP_AROUND_SEC 10ULL
/* This function calculates the max shift that enables the user range
* of MLX4_EN_WRAP_AROUND_SEC values in the cycles register.
@@ -261,7 +267,6 @@
{
struct mlx4_dev *dev = mdev->dev;
unsigned long flags;
- u64 ns, zero = 0;
/* mlx4_en_init_timestamp is called for each netdev.
* mdev->ptp_clock is common for all ports, skip initialization if
@@ -285,13 +290,6 @@
ktime_to_ns(ktime_get_real()));
write_unlock_irqrestore(&mdev->clock_lock, flags);
- /* Calculate period in seconds to call the overflow watchdog - to make
- * sure counter is checked at least once every wrap around.
- */
- ns = cyclecounter_cyc2ns(&mdev->cycles, mdev->cycles.mask, zero, &zero);
- do_div(ns, NSEC_PER_SEC / 2 / HZ);
- mdev->overflow_period = ns;
-
/* Configure the PHC */
mdev->ptp_clock_info = mlx4_en_ptp_clock_info;
snprintf(mdev->ptp_clock_info.name, 16, "mlx4 ptp");
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index ba652d8..727122d 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -841,8 +841,6 @@
return -ENOSYS;
}
- mlx4_log_num_mgm_entry_size = hca_param.log_mc_entry_sz;
-
dev->caps.hca_core_clock = hca_param.hca_core_clock;
memset(&dev_cap, 0, sizeof(dev_cap));
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index a3528dd..df0f396 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -419,7 +419,6 @@
struct cyclecounter cycles;
struct timecounter clock;
unsigned long last_overflow_check;
- unsigned long overflow_period;
struct ptp_clock *ptp_clock;
struct ptp_clock_info ptp_clock_info;
struct notifier_block nb;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index 1806b1f..d50350c 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -249,15 +249,14 @@
}
static struct mlxsw_sp_span_entry *
-mlxsw_sp_span_entry_find(struct mlxsw_sp_port *port)
+mlxsw_sp_span_entry_find(struct mlxsw_sp *mlxsw_sp, u8 local_port)
{
- struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
int i;
for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
- if (curr->used && curr->local_port == port->local_port)
+ if (curr->used && curr->local_port == local_port)
return curr;
}
return NULL;
@@ -268,7 +267,8 @@
{
struct mlxsw_sp_span_entry *span_entry;
- span_entry = mlxsw_sp_span_entry_find(port);
+ span_entry = mlxsw_sp_span_entry_find(port->mlxsw_sp,
+ port->local_port);
if (span_entry) {
/* Already exists, just take a reference */
span_entry->ref_count++;
@@ -453,12 +453,13 @@
}
static void mlxsw_sp_span_mirror_remove(struct mlxsw_sp_port *from,
- struct mlxsw_sp_port *to,
+ u8 destination_port,
enum mlxsw_sp_span_type type)
{
struct mlxsw_sp_span_entry *span_entry;
- span_entry = mlxsw_sp_span_entry_find(to);
+ span_entry = mlxsw_sp_span_entry_find(from->mlxsw_sp,
+ destination_port);
if (!span_entry) {
netdev_err(from->dev, "no span entry found\n");
return;
@@ -1255,10 +1256,8 @@
static void mlxsw_sp_port_del_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port,
struct tc_cls_matchall_offload *cls)
{
- struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
struct mlxsw_sp_port_mall_tc_entry *mall_tc_entry;
enum mlxsw_sp_span_type span_type;
- struct mlxsw_sp_port *to_port;
mall_tc_entry = mlxsw_sp_port_mirror_entry_find(mlxsw_sp_port,
cls->cookie);
@@ -1269,11 +1268,12 @@
switch (mall_tc_entry->type) {
case MLXSW_SP_PORT_MALL_MIRROR:
- to_port = mlxsw_sp->ports[mall_tc_entry->mirror.to_local_port];
span_type = mall_tc_entry->mirror.ingress ?
MLXSW_SP_SPAN_INGRESS : MLXSW_SP_SPAN_EGRESS;
- mlxsw_sp_span_mirror_remove(mlxsw_sp_port, to_port, span_type);
+ mlxsw_sp_span_mirror_remove(mlxsw_sp_port,
+ mall_tc_entry->mirror.to_local_port,
+ span_type);
break;
default:
WARN_ON(1);
diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h
index 653bb57..433f8be 100644
--- a/drivers/net/ethernet/qlogic/qed/qed.h
+++ b/drivers/net/ethernet/qlogic/qed/qed.h
@@ -642,7 +642,9 @@
#define OOO_LB_TC 9
int qed_configure_vport_wfq(struct qed_dev *cdev, u16 vp_id, u32 rate);
-void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev, u32 min_pf_rate);
+void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev,
+ struct qed_ptt *p_ptt,
+ u32 min_pf_rate);
void qed_clean_wfq_db(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
#define QED_LEADING_HWFN(dev) (&dev->hwfns[0])
diff --git a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c
index a4789a9..9d59cb8 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c
@@ -1222,7 +1222,7 @@
{
struct qed_dcbx_get *dcbx_info;
- dcbx_info = kzalloc(sizeof(*dcbx_info), GFP_KERNEL);
+ dcbx_info = kmalloc(sizeof(*dcbx_info), GFP_ATOMIC);
if (!dcbx_info)
return NULL;
diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c
index edae5fc..afe5e57 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_dev.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c
@@ -877,7 +877,7 @@
/* Either EDPM is mandatory, or we are attempting to allocate a
* WID per CPU.
*/
- n_cpus = num_active_cpus();
+ n_cpus = num_present_cpus();
rc = qed_hw_init_dpi_size(p_hwfn, p_ptt, pwm_regsize, n_cpus);
}
@@ -2732,7 +2732,8 @@
}
/* API to configure WFQ from mcp link change */
-void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev, u32 min_pf_rate)
+void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev,
+ struct qed_ptt *p_ptt, u32 min_pf_rate)
{
int i;
@@ -2746,8 +2747,7 @@
for_each_hwfn(cdev, i) {
struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
- __qed_configure_vp_wfq_on_link_change(p_hwfn,
- p_hwfn->p_dpc_ptt,
+ __qed_configure_vp_wfq_on_link_change(p_hwfn, p_ptt,
min_pf_rate);
}
}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
index bdc9ba9..8b7d2f9 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
@@ -628,7 +628,8 @@
/* Min bandwidth configuration */
__qed_configure_pf_min_bandwidth(p_hwfn, p_ptt, p_link, min_bw);
- qed_configure_vp_wfq_on_link_change(p_hwfn->cdev, p_link->min_pf_rate);
+ qed_configure_vp_wfq_on_link_change(p_hwfn->cdev, p_ptt,
+ p_link->min_pf_rate);
p_link->an = !!(status & LINK_STATUS_AUTO_NEGOTIATE_ENABLED);
p_link->an_complete = !!(status &
diff --git a/drivers/net/ethernet/qlogic/qed/qed_roce.c b/drivers/net/ethernet/qlogic/qed/qed_roce.c
index f3a825a..d9dcb0d 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_roce.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_roce.c
@@ -1766,13 +1766,13 @@
if (rc)
goto err_resp;
- dma_free_coherent(&p_hwfn->cdev->pdev->dev, sizeof(*p_resp_ramrod_res),
- p_resp_ramrod_res, resp_ramrod_res_phys);
-
out_params->rq_psn = le32_to_cpu(p_resp_ramrod_res->psn);
rq_err_state = GET_FIELD(le32_to_cpu(p_resp_ramrod_res->err_flag),
ROCE_QUERY_QP_RESP_OUTPUT_PARAMS_ERROR_FLG);
+ dma_free_coherent(&p_hwfn->cdev->pdev->dev, sizeof(*p_resp_ramrod_res),
+ p_resp_ramrod_res, resp_ramrod_res_phys);
+
if (!(qp->req_offloaded)) {
/* Don't send query qp for the requester */
out_params->sq_psn = qp->sq_psn;
@@ -1813,9 +1813,6 @@
if (rc)
goto err_req;
- dma_free_coherent(&p_hwfn->cdev->pdev->dev, sizeof(*p_req_ramrod_res),
- p_req_ramrod_res, req_ramrod_res_phys);
-
out_params->sq_psn = le32_to_cpu(p_req_ramrod_res->psn);
sq_err_state = GET_FIELD(le32_to_cpu(p_req_ramrod_res->flags),
ROCE_QUERY_QP_REQ_OUTPUT_PARAMS_ERR_FLG);
@@ -1823,6 +1820,9 @@
GET_FIELD(le32_to_cpu(p_req_ramrod_res->flags),
ROCE_QUERY_QP_REQ_OUTPUT_PARAMS_SQ_DRAINING_FLG);
+ dma_free_coherent(&p_hwfn->cdev->pdev->dev, sizeof(*p_req_ramrod_res),
+ p_req_ramrod_res, req_ramrod_res_phys);
+
out_params->draining = false;
if (rq_err_state)
diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
index 7567cc4..634e414 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
@@ -1221,7 +1221,7 @@
struct qede_rx_queue *rxq = NULL;
struct sw_rx_data *sw_rx_data;
union eth_rx_cqe *cqe;
- int i, rc = 0;
+ int i, iter, rc = 0;
u8 *data_ptr;
for_each_queue(i) {
@@ -1240,7 +1240,7 @@
* enabled. This is because the queue 0 is configured as the default
* queue and that the loopback traffic is not IP.
*/
- for (i = 0; i < QEDE_SELFTEST_POLL_COUNT; i++) {
+ for (iter = 0; iter < QEDE_SELFTEST_POLL_COUNT; iter++) {
if (!qede_has_rx_work(rxq)) {
usleep_range(100, 200);
continue;
@@ -1287,7 +1287,7 @@
qed_chain_recycle_consumed(&rxq->rx_comp_ring);
}
- if (i == QEDE_SELFTEST_POLL_COUNT) {
+ if (iter == QEDE_SELFTEST_POLL_COUNT) {
DP_NOTICE(edev, "Failed to receive the traffic\n");
return -1;
}
diff --git a/drivers/net/ethernet/qualcomm/emac/emac-mac.c b/drivers/net/ethernet/qualcomm/emac/emac-mac.c
index 0b4deb3..f683bfb 100644
--- a/drivers/net/ethernet/qualcomm/emac/emac-mac.c
+++ b/drivers/net/ethernet/qualcomm/emac/emac-mac.c
@@ -932,7 +932,8 @@
curr_rxbuf->dma_addr =
dma_map_single(adpt->netdev->dev.parent, skb->data,
- curr_rxbuf->length, DMA_FROM_DEVICE);
+ adpt->rxbuf_size, DMA_FROM_DEVICE);
+
ret = dma_mapping_error(adpt->netdev->dev.parent,
curr_rxbuf->dma_addr);
if (ret) {
diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h
index d050f37..5024280 100644
--- a/drivers/net/ethernet/renesas/sh_eth.h
+++ b/drivers/net/ethernet/renesas/sh_eth.h
@@ -339,7 +339,7 @@
ECMR_DPAD = 0x00200000, ECMR_RZPF = 0x00100000,
ECMR_ZPF = 0x00080000, ECMR_PFR = 0x00040000, ECMR_RXF = 0x00020000,
ECMR_TXF = 0x00010000, ECMR_MCT = 0x00002000, ECMR_PRCEF = 0x00001000,
- ECMR_PMDE = 0x00000200, ECMR_RE = 0x00000040, ECMR_TE = 0x00000020,
+ ECMR_MPDE = 0x00000200, ECMR_RE = 0x00000040, ECMR_TE = 0x00000020,
ECMR_RTM = 0x00000010, ECMR_ILB = 0x00000008, ECMR_ELB = 0x00000004,
ECMR_DM = 0x00000002, ECMR_PRM = 0x00000001,
};
diff --git a/drivers/net/ethernet/rocker/rocker_tlv.h b/drivers/net/ethernet/rocker/rocker_tlv.h
index a63ef82..dfae3c9 100644
--- a/drivers/net/ethernet/rocker/rocker_tlv.h
+++ b/drivers/net/ethernet/rocker/rocker_tlv.h
@@ -139,40 +139,52 @@
int rocker_tlv_put(struct rocker_desc_info *desc_info,
int attrtype, int attrlen, const void *data);
-static inline int rocker_tlv_put_u8(struct rocker_desc_info *desc_info,
- int attrtype, u8 value)
+static inline int
+rocker_tlv_put_u8(struct rocker_desc_info *desc_info, int attrtype, u8 value)
{
- return rocker_tlv_put(desc_info, attrtype, sizeof(u8), &value);
+ u8 tmp = value; /* work around GCC PR81715 */
+
+ return rocker_tlv_put(desc_info, attrtype, sizeof(u8), &tmp);
}
-static inline int rocker_tlv_put_u16(struct rocker_desc_info *desc_info,
- int attrtype, u16 value)
+static inline int
+rocker_tlv_put_u16(struct rocker_desc_info *desc_info, int attrtype, u16 value)
{
- return rocker_tlv_put(desc_info, attrtype, sizeof(u16), &value);
+ u16 tmp = value;
+
+ return rocker_tlv_put(desc_info, attrtype, sizeof(u16), &tmp);
}
-static inline int rocker_tlv_put_be16(struct rocker_desc_info *desc_info,
- int attrtype, __be16 value)
+static inline int
+rocker_tlv_put_be16(struct rocker_desc_info *desc_info, int attrtype, __be16 value)
{
- return rocker_tlv_put(desc_info, attrtype, sizeof(__be16), &value);
+ __be16 tmp = value;
+
+ return rocker_tlv_put(desc_info, attrtype, sizeof(__be16), &tmp);
}
-static inline int rocker_tlv_put_u32(struct rocker_desc_info *desc_info,
- int attrtype, u32 value)
+static inline int
+rocker_tlv_put_u32(struct rocker_desc_info *desc_info, int attrtype, u32 value)
{
- return rocker_tlv_put(desc_info, attrtype, sizeof(u32), &value);
+ u32 tmp = value;
+
+ return rocker_tlv_put(desc_info, attrtype, sizeof(u32), &tmp);
}
-static inline int rocker_tlv_put_be32(struct rocker_desc_info *desc_info,
- int attrtype, __be32 value)
+static inline int
+rocker_tlv_put_be32(struct rocker_desc_info *desc_info, int attrtype, __be32 value)
{
- return rocker_tlv_put(desc_info, attrtype, sizeof(__be32), &value);
+ __be32 tmp = value;
+
+ return rocker_tlv_put(desc_info, attrtype, sizeof(__be32), &tmp);
}
-static inline int rocker_tlv_put_u64(struct rocker_desc_info *desc_info,
- int attrtype, u64 value)
+static inline int
+rocker_tlv_put_u64(struct rocker_desc_info *desc_info, int attrtype, u64 value)
{
- return rocker_tlv_put(desc_info, attrtype, sizeof(u64), &value);
+ u64 tmp = value;
+
+ return rocker_tlv_put(desc_info, attrtype, sizeof(u64), &tmp);
}
static inline struct rocker_tlv *
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c
index c4ada72..1d85109 100644
--- a/drivers/net/ethernet/sfc/ef10.c
+++ b/drivers/net/ethernet/sfc/ef10.c
@@ -197,11 +197,15 @@
nic_data->datapath_caps =
MCDI_DWORD(outbuf, GET_CAPABILITIES_OUT_FLAGS1);
- if (outlen >= MC_CMD_GET_CAPABILITIES_V2_OUT_LEN)
+ if (outlen >= MC_CMD_GET_CAPABILITIES_V2_OUT_LEN) {
nic_data->datapath_caps2 = MCDI_DWORD(outbuf,
GET_CAPABILITIES_V2_OUT_FLAGS2);
- else
+ nic_data->piobuf_size = MCDI_WORD(outbuf,
+ GET_CAPABILITIES_V2_OUT_SIZE_PIO_BUFF);
+ } else {
nic_data->datapath_caps2 = 0;
+ nic_data->piobuf_size = ER_DZ_TX_PIOBUF_SIZE;
+ }
/* record the DPCPU firmware IDs to determine VEB vswitching support.
*/
@@ -825,8 +829,8 @@
offset = ((efx->tx_channel_offset + efx->n_tx_channels -
tx_queue->channel->channel - 1) *
efx_piobuf_size);
- index = offset / ER_DZ_TX_PIOBUF_SIZE;
- offset = offset % ER_DZ_TX_PIOBUF_SIZE;
+ index = offset / nic_data->piobuf_size;
+ offset = offset % nic_data->piobuf_size;
/* When the host page size is 4K, the first
* host page in the WC mapping may be within
@@ -1161,11 +1165,11 @@
* functions of the controller.
*/
if (efx_piobuf_size != 0 &&
- ER_DZ_TX_PIOBUF_SIZE / efx_piobuf_size * EF10_TX_PIOBUF_COUNT >=
+ nic_data->piobuf_size / efx_piobuf_size * EF10_TX_PIOBUF_COUNT >=
efx->n_tx_channels) {
unsigned int n_piobufs =
DIV_ROUND_UP(efx->n_tx_channels,
- ER_DZ_TX_PIOBUF_SIZE / efx_piobuf_size);
+ nic_data->piobuf_size / efx_piobuf_size);
rc = efx_ef10_alloc_piobufs(efx, n_piobufs);
if (rc)
diff --git a/drivers/net/ethernet/sfc/nic.h b/drivers/net/ethernet/sfc/nic.h
index 73bee7e..73028f2 100644
--- a/drivers/net/ethernet/sfc/nic.h
+++ b/drivers/net/ethernet/sfc/nic.h
@@ -500,6 +500,7 @@
* @pio_write_base: Base address for writing PIO buffers
* @pio_write_vi_base: Relative VI number for @pio_write_base
* @piobuf_handle: Handle of each PIO buffer allocated
+ * @piobuf_size: size of a single PIO buffer
* @must_restore_piobufs: Flag: PIO buffers have yet to be restored after MC
* reboot
* @rx_rss_context: Firmware handle for our RSS context
@@ -537,6 +538,7 @@
void __iomem *wc_membase, *pio_write_base;
unsigned int pio_write_vi_base;
unsigned int piobuf_handle[EF10_TX_PIOBUF_COUNT];
+ u16 piobuf_size;
bool must_restore_piobufs;
u32 rx_rss_context;
bool rx_rss_context_exclusive;
diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c
index 2337789..6f26acd 100644
--- a/drivers/net/ethernet/sfc/tx.c
+++ b/drivers/net/ethernet/sfc/tx.c
@@ -27,7 +27,6 @@
#ifdef EFX_USE_PIO
-#define EFX_PIOBUF_SIZE_MAX ER_DZ_TX_PIOBUF_SIZE
#define EFX_PIOBUF_SIZE_DEF ALIGN(256, L1_CACHE_BYTES)
unsigned int efx_piobuf_size __read_mostly = EFX_PIOBUF_SIZE_DEF;
diff --git a/drivers/net/phy/xilinx_gmii2rgmii.c b/drivers/net/phy/xilinx_gmii2rgmii.c
index d15dd39..2e5150b 100644
--- a/drivers/net/phy/xilinx_gmii2rgmii.c
+++ b/drivers/net/phy/xilinx_gmii2rgmii.c
@@ -44,7 +44,7 @@
priv->phy_drv->read_status(phydev);
val = mdiobus_read(phydev->mdio.bus, priv->addr, XILINX_GMII2RGMII_REG);
- val &= XILINX_GMII2RGMII_SPEED_MASK;
+ val &= ~XILINX_GMII2RGMII_SPEED_MASK;
if (phydev->speed == SPEED_1000)
val |= BMCR_SPEED1000;
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index a380649..2668170 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -2366,8 +2366,10 @@
hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI,
TEAM_CMD_OPTIONS_GET);
- if (!hdr)
+ if (!hdr) {
+ nlmsg_free(skb);
return -EMSGSIZE;
+ }
if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
goto nla_put_failure;
@@ -2639,8 +2641,10 @@
hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI,
TEAM_CMD_PORT_LIST_GET);
- if (!hdr)
+ if (!hdr) {
+ nlmsg_free(skb);
return -EMSGSIZE;
+ }
if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
goto nla_put_failure;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 35aa28b..7e5ae26 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1283,11 +1283,13 @@
switch (tun->flags & TUN_TYPE_MASK) {
case IFF_TUN:
if (tun->flags & IFF_NO_PI) {
- switch (skb->data[0] & 0xf0) {
- case 0x40:
+ u8 ip_version = skb->len ? (skb->data[0] >> 4) : 0;
+
+ switch (ip_version) {
+ case 4:
pi.proto = htons(ETH_P_IP);
break;
- case 0x60:
+ case 6:
pi.proto = htons(ETH_P_IPV6);
break;
default:
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index cdde590..3a72862 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -364,7 +364,7 @@
optionally with LEDs that indicate traffic
config USB_NET_PLUSB
- tristate "Prolific PL-2301/2302/25A1 based cables"
+ tristate "Prolific PL-2301/2302/25A1/27A1 based cables"
# if the handshake/init/reset problems, from original 'plusb',
# are ever resolved ... then remove "experimental"
depends on USB_USBNET
diff --git a/drivers/net/usb/plusb.c b/drivers/net/usb/plusb.c
index 22e1a9a..6fe5937 100644
--- a/drivers/net/usb/plusb.c
+++ b/drivers/net/usb/plusb.c
@@ -102,7 +102,7 @@
}
static const struct driver_info prolific_info = {
- .description = "Prolific PL-2301/PL-2302/PL-25A1",
+ .description = "Prolific PL-2301/PL-2302/PL-25A1/PL-27A1",
.flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT,
/* some PL-2302 versions seem to fail usb_set_interface() */
.reset = pl_reset,
@@ -139,6 +139,17 @@
* Host-to-Host Cable
*/
.driver_info = (unsigned long) &prolific_info,
+
+},
+
+/* super speed cables */
+{
+ USB_DEVICE(0x067b, 0x27a1), /* PL-27A1, no eeprom
+ * also: goobay Active USB 3.0
+ * Data Link,
+ * Unitek Y-3501
+ */
+ .driver_info = (unsigned long) &prolific_info,
},
{ }, // END
@@ -158,5 +169,5 @@
module_usb_driver(plusb_driver);
MODULE_AUTHOR("David Brownell");
-MODULE_DESCRIPTION("Prolific PL-2301/2302/25A1 USB Host to Host Link Driver");
+MODULE_DESCRIPTION("Prolific PL-2301/2302/25A1/27A1 USB Host to Host Link Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index be5b527..90c0c4a 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -314,6 +314,7 @@
struct ieee80211_vif *vif;
struct ieee80211_sta *sta;
+ bool removed;
int vdev_id;
u8 addr[ETH_ALEN];
DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS);
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index f2e85eb..30e98af 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3738,6 +3738,9 @@
if (!peer)
return NULL;
+ if (peer->removed)
+ return NULL;
+
if (peer->sta)
return peer->sta->txq[tid];
else if (peer->vif)
@@ -7422,6 +7425,20 @@
return 0;
}
+static void ath10k_mac_op_sta_pre_rcu_remove(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct ath10k *ar;
+ struct ath10k_peer *peer;
+
+ ar = hw->priv;
+
+ list_for_each_entry(peer, &ar->peers, list)
+ if (peer->sta == sta)
+ peer->removed = true;
+}
+
static const struct ieee80211_ops ath10k_ops = {
.tx = ath10k_mac_op_tx,
.wake_tx_queue = ath10k_mac_op_wake_tx_queue,
@@ -7462,6 +7479,7 @@
.assign_vif_chanctx = ath10k_mac_op_assign_vif_chanctx,
.unassign_vif_chanctx = ath10k_mac_op_unassign_vif_chanctx,
.switch_vif_chanctx = ath10k_mac_op_switch_vif_chanctx,
+ .sta_pre_rcu_remove = ath10k_mac_op_sta_pre_rcu_remove,
CFG80211_TESTMODE_CMD(ath10k_tm_cmd)
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index e2a459e..51030c3 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -976,7 +976,7 @@
u64 *cookie)
{
const u8 *buf = params->buf;
- size_t len = params->len;
+ size_t len = params->len, total;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int rc;
bool tx_status = false;
@@ -1001,7 +1001,11 @@
if (len < sizeof(struct ieee80211_hdr_3addr))
return -EINVAL;
- cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL);
+ total = sizeof(*cmd) + len;
+ if (total < len)
+ return -EINVAL;
+
+ cmd = kmalloc(total, GFP_KERNEL);
if (!cmd) {
rc = -ENOMEM;
goto out;
@@ -1011,7 +1015,7 @@
cmd->len = cpu_to_le16(len);
memcpy(cmd->payload, buf, len);
- rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, sizeof(*cmd) + len,
+ rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, total,
WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000);
if (rc == 0)
tx_status = !evt.evt.status;
diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c
index 7a33792..77d1902 100644
--- a/drivers/net/wireless/ath/wil6210/fw_inc.c
+++ b/drivers/net/wireless/ath/wil6210/fw_inc.c
@@ -26,14 +26,17 @@
prefix_type, rowsize, \
groupsize, buf, len, ascii)
-#define FW_ADDR_CHECK(ioaddr, val, msg) do { \
- ioaddr = wmi_buffer(wil, val); \
- if (!ioaddr) { \
- wil_err_fw(wil, "bad " msg ": 0x%08x\n", \
- le32_to_cpu(val)); \
- return -EINVAL; \
- } \
- } while (0)
+static bool wil_fw_addr_check(struct wil6210_priv *wil,
+ void __iomem **ioaddr, __le32 val,
+ u32 size, const char *msg)
+{
+ *ioaddr = wmi_buffer_block(wil, val, size);
+ if (!(*ioaddr)) {
+ wil_err_fw(wil, "bad %s: 0x%08x\n", msg, le32_to_cpu(val));
+ return false;
+ }
+ return true;
+}
/**
* wil_fw_verify - verify firmware file validity
@@ -160,7 +163,8 @@
return -EINVAL;
}
- FW_ADDR_CHECK(dst, d->addr, "address");
+ if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address"))
+ return -EINVAL;
wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(d->addr),
s);
wil_memcpy_toio_32(dst, d->data, s);
@@ -192,7 +196,8 @@
return -EINVAL;
}
- FW_ADDR_CHECK(dst, d->addr, "address");
+ if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address"))
+ return -EINVAL;
v = le32_to_cpu(d->value);
wil_dbg_fw(wil, "fill [0x%08x] <== 0x%08x, %zu bytes\n",
@@ -248,7 +253,8 @@
u32 v = le32_to_cpu(block[i].value);
u32 x, y;
- FW_ADDR_CHECK(dst, block[i].addr, "address");
+ if (!wil_fw_addr_check(wil, &dst, block[i].addr, 0, "address"))
+ return -EINVAL;
x = readl(dst);
y = (x & m) | (v & ~m);
@@ -314,10 +320,15 @@
wil_dbg_fw(wil, "gw write record [%3d] blocks, cmd 0x%08x\n",
n, gw_cmd);
- FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr");
- FW_ADDR_CHECK(gwa_val, d->gateway_value_addr, "gateway_value_addr");
- FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr");
- FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address");
+ if (!wil_fw_addr_check(wil, &gwa_addr, d->gateway_addr_addr, 0,
+ "gateway_addr_addr") ||
+ !wil_fw_addr_check(wil, &gwa_val, d->gateway_value_addr, 0,
+ "gateway_value_addr") ||
+ !wil_fw_addr_check(wil, &gwa_cmd, d->gateway_cmd_addr, 0,
+ "gateway_cmd_addr") ||
+ !wil_fw_addr_check(wil, &gwa_ctl, d->gateway_ctrl_address, 0,
+ "gateway_ctrl_address"))
+ return -EINVAL;
wil_dbg_fw(wil, "gw addresses: addr 0x%08x val 0x%08x"
" cmd 0x%08x ctl 0x%08x\n",
@@ -373,12 +384,19 @@
wil_dbg_fw(wil, "gw4 write record [%3d] blocks, cmd 0x%08x\n",
n, gw_cmd);
- FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr");
+ if (!wil_fw_addr_check(wil, &gwa_addr, d->gateway_addr_addr, 0,
+ "gateway_addr_addr"))
+ return -EINVAL;
for (k = 0; k < ARRAY_SIZE(block->value); k++)
- FW_ADDR_CHECK(gwa_val[k], d->gateway_value_addr[k],
- "gateway_value_addr");
- FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr");
- FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address");
+ if (!wil_fw_addr_check(wil, &gwa_val[k],
+ d->gateway_value_addr[k],
+ 0, "gateway_value_addr"))
+ return -EINVAL;
+ if (!wil_fw_addr_check(wil, &gwa_cmd, d->gateway_cmd_addr, 0,
+ "gateway_cmd_addr") ||
+ !wil_fw_addr_check(wil, &gwa_ctl, d->gateway_ctrl_address, 0,
+ "gateway_ctrl_address"))
+ return -EINVAL;
wil_dbg_fw(wil, "gw4 addresses: addr 0x%08x cmd 0x%08x ctl 0x%08x\n",
le32_to_cpu(d->gateway_addr_addr),
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index 59def4f..5cf3417 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -358,6 +358,25 @@
wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
}
+static bool wil_validate_mbox_regs(struct wil6210_priv *wil)
+{
+ size_t min_size = sizeof(struct wil6210_mbox_hdr) +
+ sizeof(struct wmi_cmd_hdr);
+
+ if (wil->mbox_ctl.rx.entry_size < min_size) {
+ wil_err(wil, "rx mbox entry too small (%d)\n",
+ wil->mbox_ctl.rx.entry_size);
+ return false;
+ }
+ if (wil->mbox_ctl.tx.entry_size < min_size) {
+ wil_err(wil, "tx mbox entry too small (%d)\n",
+ wil->mbox_ctl.tx.entry_size);
+ return false;
+ }
+
+ return true;
+}
+
static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
{
struct wil6210_priv *wil = cookie;
@@ -393,7 +412,8 @@
if (isr & ISR_MISC_FW_READY) {
wil_dbg_irq(wil, "IRQ: FW ready\n");
wil_cache_mbox_regs(wil);
- set_bit(wil_status_mbox_ready, wil->status);
+ if (wil_validate_mbox_regs(wil))
+ set_bit(wil_status_mbox_ready, wil->status);
/**
* Actual FW ready indicated by the
* WMI_FW_READY_EVENTID
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 89e3fbf..663e163 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -23,9 +23,9 @@
#include <linux/rtnetlink.h>
#include <linux/pm_runtime.h>
-static bool use_msi;
+static bool use_msi = true;
module_param(use_msi, bool, 0444);
-MODULE_PARM_DESC(use_msi, " Use MSI interrupt, default - false");
+MODULE_PARM_DESC(use_msi, " Use MSI interrupt, default - true");
static bool ftm_mode;
module_param(ftm_mode, bool, 0444);
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 8616f86..05623fa 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -886,6 +886,7 @@
int wil_find_cid(struct wil6210_priv *wil, const u8 *mac);
void wil_set_ethtoolops(struct net_device *ndev);
+void __iomem *wmi_buffer_block(struct wil6210_priv *wil, __le32 ptr, u32 size);
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr);
void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr);
int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr,
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 43cdaef..205c3ab 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -141,13 +141,15 @@
/**
* Check address validity for WMI buffer; remap if needed
* @ptr - internal (linker) fw/ucode address
+ * @size - if non zero, validate the block does not
+ * exceed the device memory (bar)
*
* Valid buffer should be DWORD aligned
*
* return address for accessing buffer from the host;
* if buffer is not valid, return NULL.
*/
-void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_)
+void __iomem *wmi_buffer_block(struct wil6210_priv *wil, __le32 ptr_, u32 size)
{
u32 off;
u32 ptr = le32_to_cpu(ptr_);
@@ -162,10 +164,17 @@
off = HOSTADDR(ptr);
if (off > wil->bar_size - 4)
return NULL;
+ if (size && ((off + size > wil->bar_size) || (off + size < off)))
+ return NULL;
return wil->csr + off;
}
+void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_)
+{
+ return wmi_buffer_block(wil, ptr_, 0);
+}
+
/**
* Check address validity
*/
@@ -223,7 +232,7 @@
uint retry;
int rc = 0;
- if (sizeof(cmd) + len > r->entry_size) {
+ if (len > r->entry_size - sizeof(cmd)) {
wil_err(wil, "WMI size too large: %d bytes, max is %d\n",
(int)(sizeof(cmd) + len), r->entry_size);
return -ERANGE;
@@ -1412,8 +1421,14 @@
};
int rc;
u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len;
- struct wmi_set_appie_cmd *cmd = kzalloc(len, GFP_KERNEL);
+ struct wmi_set_appie_cmd *cmd;
+ if (len < ie_len) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ cmd = kzalloc(len, GFP_KERNEL);
if (!cmd) {
rc = -ENOMEM;
goto out;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 261a0da..1082f66 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -980,7 +980,7 @@
eth_broadcast_addr(params_le->bssid);
params_le->bss_type = DOT11_BSSTYPE_ANY;
- params_le->scan_type = 0;
+ params_le->scan_type = BRCMF_SCANTYPE_ACTIVE;
params_le->channel_num = 0;
params_le->nprobes = cpu_to_le32(-1);
params_le->active_time = cpu_to_le32(-1);
@@ -988,12 +988,9 @@
params_le->home_time = cpu_to_le32(-1);
memset(¶ms_le->ssid_le, 0, sizeof(params_le->ssid_le));
- /* if request is null exit so it will be all channel broadcast scan */
- if (!request)
- return;
-
n_ssids = request->n_ssids;
n_channels = request->n_channels;
+
/* Copy channel array if applicable */
brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
n_channels);
@@ -1030,16 +1027,8 @@
ptr += sizeof(ssid_le);
}
} else {
- brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
- if ((request->ssids) && request->ssids->ssid_len) {
- brcmf_dbg(SCAN, "SSID %s len=%d\n",
- params_le->ssid_le.SSID,
- request->ssids->ssid_len);
- params_le->ssid_le.SSID_len =
- cpu_to_le32(request->ssids->ssid_len);
- memcpy(¶ms_le->ssid_le.SSID, request->ssids->ssid,
- request->ssids->ssid_len);
- }
+ brcmf_dbg(SCAN, "Performing passive scan\n");
+ params_le->scan_type = BRCMF_SCANTYPE_PASSIVE;
}
/* Adding mask to channel numbers */
params_le->channel_num =
@@ -3099,6 +3088,7 @@
struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
s32 status;
struct brcmf_escan_result_le *escan_result_le;
+ u32 escan_buflen;
struct brcmf_bss_info_le *bss_info_le;
struct brcmf_bss_info_le *bss = NULL;
u32 bi_length;
@@ -3115,11 +3105,23 @@
if (status == BRCMF_E_STATUS_PARTIAL) {
brcmf_dbg(SCAN, "ESCAN Partial result\n");
+ if (e->datalen < sizeof(*escan_result_le)) {
+ brcmf_err("invalid event data length\n");
+ goto exit;
+ }
escan_result_le = (struct brcmf_escan_result_le *) data;
if (!escan_result_le) {
brcmf_err("Invalid escan result (NULL pointer)\n");
goto exit;
}
+ escan_buflen = le32_to_cpu(escan_result_le->buflen);
+ if (escan_buflen > BRCMF_ESCAN_BUF_SIZE ||
+ escan_buflen > e->datalen ||
+ escan_buflen < sizeof(*escan_result_le)) {
+ brcmf_err("Invalid escan buffer length: %d\n",
+ escan_buflen);
+ goto exit;
+ }
if (le16_to_cpu(escan_result_le->bss_count) != 1) {
brcmf_err("Invalid bss_count %d: ignoring\n",
escan_result_le->bss_count);
@@ -3136,9 +3138,8 @@
}
bi_length = le32_to_cpu(bss_info_le->length);
- if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
- WL_ESCAN_RESULTS_FIXED_SIZE)) {
- brcmf_err("Invalid bss_info length %d: ignoring\n",
+ if (bi_length != escan_buflen - WL_ESCAN_RESULTS_FIXED_SIZE) {
+ brcmf_err("Ignoring invalid bss_info length: %d\n",
bi_length);
goto exit;
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index a4118c0..5901357 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -45,6 +45,11 @@
#define BRCMF_SCAN_PARAMS_COUNT_MASK 0x0000ffff
#define BRCMF_SCAN_PARAMS_NSSID_SHIFT 16
+/* scan type definitions */
+#define BRCMF_SCANTYPE_DEFAULT 0xFF
+#define BRCMF_SCANTYPE_ACTIVE 0
+#define BRCMF_SCANTYPE_PASSIVE 1
+
/* primary (ie tx) key */
#define BRCMF_PRIMARY_KEY (1 << 1)
#define DOT11_BSSTYPE_ANY 2
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
index 3bd6fc1..33f4d7c 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
@@ -78,6 +78,7 @@
/* NVM offsets (in words) definitions */
enum wkp_nvm_offsets {
/* NVM HW-Section offset (in words) definitions */
+ SUBSYSTEM_ID = 0x0A,
HW_ADDR = 0x15,
/* NVM SW-Section offset (in words) definitions */
@@ -262,13 +263,12 @@
static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
struct iwl_nvm_data *data,
const __le16 * const nvm_ch_flags,
- bool lar_supported)
+ bool lar_supported, bool no_wide_in_5ghz)
{
int ch_idx;
int n_channels = 0;
struct ieee80211_channel *channel;
u16 ch_flags;
- bool is_5ghz;
int num_of_ch, num_2ghz_channels;
const u8 *nvm_chan;
@@ -283,12 +283,20 @@
}
for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
+ bool is_5ghz = (ch_idx >= num_2ghz_channels);
+
ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx);
- if (ch_idx >= num_2ghz_channels &&
- !data->sku_cap_band_52GHz_enable)
+ if (is_5ghz && !data->sku_cap_band_52GHz_enable)
continue;
+ /* workaround to disable wide channels in 5GHz */
+ if (no_wide_in_5ghz && is_5ghz) {
+ ch_flags &= ~(NVM_CHANNEL_40MHZ |
+ NVM_CHANNEL_80MHZ |
+ NVM_CHANNEL_160MHZ);
+ }
+
if (ch_flags & NVM_CHANNEL_160MHZ)
data->vht160_supported = true;
@@ -311,8 +319,8 @@
n_channels++;
channel->hw_value = nvm_chan[ch_idx];
- channel->band = (ch_idx < num_2ghz_channels) ?
- NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
+ channel->band = is_5ghz ?
+ NL80211_BAND_5GHZ : NL80211_BAND_2GHZ;
channel->center_freq =
ieee80211_channel_to_frequency(
channel->hw_value, channel->band);
@@ -324,7 +332,6 @@
* is not used in mvm, and is used for backwards compatibility
*/
channel->max_power = IWL_DEFAULT_MAX_TX_POWER;
- is_5ghz = channel->band == NL80211_BAND_5GHZ;
/* don't put limitations in case we're using LAR */
if (!lar_supported)
@@ -441,7 +448,8 @@
static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
struct iwl_nvm_data *data,
const __le16 *ch_section,
- u8 tx_chains, u8 rx_chains, bool lar_supported)
+ u8 tx_chains, u8 rx_chains, bool lar_supported,
+ bool no_wide_in_5ghz)
{
int n_channels;
int n_used = 0;
@@ -450,12 +458,14 @@
if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
n_channels = iwl_init_channel_map(
dev, cfg, data,
- &ch_section[NVM_CHANNELS], lar_supported);
+ &ch_section[NVM_CHANNELS], lar_supported,
+ no_wide_in_5ghz);
else
n_channels = iwl_init_channel_map(
dev, cfg, data,
&ch_section[NVM_CHANNELS_FAMILY_8000],
- lar_supported);
+ lar_supported,
+ no_wide_in_5ghz);
sband = &data->bands[NL80211_BAND_2GHZ];
sband->band = NL80211_BAND_2GHZ;
@@ -658,6 +668,39 @@
return 0;
}
+static bool
+iwl_nvm_no_wide_in_5ghz(struct device *dev, const struct iwl_cfg *cfg,
+ const __le16 *nvm_hw)
+{
+ /*
+ * Workaround a bug in Indonesia SKUs where the regulatory in
+ * some 7000-family OTPs erroneously allow wide channels in
+ * 5GHz. To check for Indonesia, we take the SKU value from
+ * bits 1-4 in the subsystem ID and check if it is either 5 or
+ * 9. In those cases, we need to force-disable wide channels
+ * in 5GHz otherwise the FW will throw a sysassert when we try
+ * to use them.
+ */
+ if (cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+ /*
+ * Unlike the other sections in the NVM, the hw
+ * section uses big-endian.
+ */
+ u16 subsystem_id = be16_to_cpup((const __be16 *)nvm_hw
+ + SUBSYSTEM_ID);
+ u8 sku = (subsystem_id & 0x1e) >> 1;
+
+ if (sku == 5 || sku == 9) {
+ IWL_DEBUG_EEPROM(dev,
+ "disabling wide channels in 5GHz (0x%0x %d)\n",
+ subsystem_id, sku);
+ return true;
+ }
+ }
+
+ return false;
+}
+
struct iwl_nvm_data *
iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
const __le16 *nvm_hw, const __le16 *nvm_sw,
@@ -668,6 +711,7 @@
struct device *dev = trans->dev;
struct iwl_nvm_data *data;
bool lar_enabled;
+ bool no_wide_in_5ghz = iwl_nvm_no_wide_in_5ghz(dev, cfg, nvm_hw);
u32 sku, radio_cfg;
u16 lar_config;
const __le16 *ch_section;
@@ -738,7 +782,7 @@
}
iwl_init_sbands(dev, cfg, data, ch_section, tx_chains, rx_chains,
- lar_fw_supported && lar_enabled);
+ lar_fw_supported && lar_enabled, no_wide_in_5ghz);
data->calib_version = 255;
return data;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 1db1dc1..9789f3c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -1548,6 +1548,11 @@
struct iwl_mvm_mc_iter_data *data = _data;
struct iwl_mvm *mvm = data->mvm;
struct iwl_mcast_filter_cmd *cmd = mvm->mcast_filter_cmd;
+ struct iwl_host_cmd hcmd = {
+ .id = MCAST_FILTER_CMD,
+ .flags = CMD_ASYNC,
+ .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+ };
int ret, len;
/* if we don't have free ports, mcast frames will be dropped */
@@ -1562,7 +1567,10 @@
memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN);
len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4);
- ret = iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_ASYNC, len, cmd);
+ hcmd.len[0] = len;
+ hcmd.data[0] = cmd;
+
+ ret = iwl_mvm_send_cmd(mvm, &hcmd);
if (ret)
IWL_ERR(mvm, "mcast filter cmd error. ret=%d\n", ret);
}
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 0fd7d7e..d2a28a9 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1357,8 +1357,6 @@
txi->control.rates,
ARRAY_SIZE(txi->control.rates));
- txi->rate_driver_data[0] = channel;
-
if (skb->len >= 24 + 8 &&
ieee80211_is_probe_resp(hdr->frame_control)) {
/* fake header transmission time */
@@ -3048,6 +3046,7 @@
static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
{
struct hwsim_new_radio_params param = { 0 };
+ const char *hwname = NULL;
param.reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG];
param.p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE];
@@ -3061,8 +3060,14 @@
if (info->attrs[HWSIM_ATTR_NO_VIF])
param.no_vif = true;
- if (info->attrs[HWSIM_ATTR_RADIO_NAME])
- param.hwname = nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]);
+ if (info->attrs[HWSIM_ATTR_RADIO_NAME]) {
+ hwname = kasprintf(GFP_KERNEL, "%.*s",
+ nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]),
+ (char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]));
+ if (!hwname)
+ return -ENOMEM;
+ param.hwname = hwname;
+ }
if (info->attrs[HWSIM_ATTR_USE_CHANCTX])
param.use_chanctx = true;
@@ -3090,11 +3095,15 @@
s64 idx = -1;
const char *hwname = NULL;
- if (info->attrs[HWSIM_ATTR_RADIO_ID])
+ if (info->attrs[HWSIM_ATTR_RADIO_ID]) {
idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]);
- else if (info->attrs[HWSIM_ATTR_RADIO_NAME])
- hwname = (void *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]);
- else
+ } else if (info->attrs[HWSIM_ATTR_RADIO_NAME]) {
+ hwname = kasprintf(GFP_KERNEL, "%.*s",
+ nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]),
+ (char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]));
+ if (!hwname)
+ return -ENOMEM;
+ } else
return -EINVAL;
spin_lock_bh(&hwsim_radio_lock);
@@ -3103,7 +3112,8 @@
if (data->idx != idx)
continue;
} else {
- if (strcmp(hwname, wiphy_name(data->hw->wiphy)))
+ if (!hwname ||
+ strcmp(hwname, wiphy_name(data->hw->wiphy)))
continue;
}
@@ -3114,10 +3124,12 @@
spin_unlock_bh(&hwsim_radio_lock);
mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy),
info);
+ kfree(hwname);
return 0;
}
spin_unlock_bh(&hwsim_radio_lock);
+ kfree(hwname);
return -ENODEV;
}
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
index 82d949e..4e725d1 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
@@ -6316,6 +6316,13 @@
.driver_info = (unsigned long)&rtl8192cu_fops},
{USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0x7822, 0xff, 0xff, 0xff),
.driver_info = (unsigned long)&rtl8192cu_fops},
+/* found in rtl8192eu vendor driver */
+{USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0107, 0xff, 0xff, 0xff),
+ .driver_info = (unsigned long)&rtl8192eu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x2019, 0xab33, 0xff, 0xff, 0xff),
+ .driver_info = (unsigned long)&rtl8192eu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x818c, 0xff, 0xff, 0xff),
+ .driver_info = (unsigned long)&rtl8192eu_fops},
#endif
{ }
};
diff --git a/drivers/net/xen-netback/hash.c b/drivers/net/xen-netback/hash.c
index e8c5ddd..3c4c58b 100644
--- a/drivers/net/xen-netback/hash.c
+++ b/drivers/net/xen-netback/hash.c
@@ -39,7 +39,7 @@
unsigned long flags;
bool found;
- new = kmalloc(sizeof(*entry), GFP_KERNEL);
+ new = kmalloc(sizeof(*entry), GFP_ATOMIC);
if (!new)
return;
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index 14eac73..54ea90f 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -96,7 +96,7 @@
struct mutex shutdown_lock;
bool subsystem;
void __iomem *cmb;
- dma_addr_t cmb_dma_addr;
+ pci_bus_addr_t cmb_bus_addr;
u64 cmb_size;
u32 cmbsz;
u32 cmbloc;
@@ -1037,7 +1037,7 @@
if (qid && dev->cmb && use_cmb_sqes && NVME_CMB_SQS(dev->cmbsz)) {
unsigned offset = (qid - 1) * roundup(SQ_SIZE(depth),
dev->ctrl.page_size);
- nvmeq->sq_dma_addr = dev->cmb_dma_addr + offset;
+ nvmeq->sq_dma_addr = dev->cmb_bus_addr + offset;
nvmeq->sq_cmds_io = dev->cmb + offset;
} else {
nvmeq->sq_cmds = dma_alloc_coherent(dev->dev, SQ_SIZE(depth),
@@ -1343,7 +1343,7 @@
resource_size_t bar_size;
struct pci_dev *pdev = to_pci_dev(dev->dev);
void __iomem *cmb;
- dma_addr_t dma_addr;
+ int bar;
dev->cmbsz = readl(dev->bar + NVME_REG_CMBSZ);
if (!(NVME_CMB_SZ(dev->cmbsz)))
@@ -1356,7 +1356,8 @@
szu = (u64)1 << (12 + 4 * NVME_CMB_SZU(dev->cmbsz));
size = szu * NVME_CMB_SZ(dev->cmbsz);
offset = szu * NVME_CMB_OFST(dev->cmbloc);
- bar_size = pci_resource_len(pdev, NVME_CMB_BIR(dev->cmbloc));
+ bar = NVME_CMB_BIR(dev->cmbloc);
+ bar_size = pci_resource_len(pdev, bar);
if (offset > bar_size)
return NULL;
@@ -1369,12 +1370,11 @@
if (size > bar_size - offset)
size = bar_size - offset;
- dma_addr = pci_resource_start(pdev, NVME_CMB_BIR(dev->cmbloc)) + offset;
- cmb = ioremap_wc(dma_addr, size);
+ cmb = ioremap_wc(pci_resource_start(pdev, bar) + offset, size);
if (!cmb)
return NULL;
- dev->cmb_dma_addr = dma_addr;
+ dev->cmb_bus_addr = pci_bus_address(pdev, bar) + offset;
dev->cmb_size = size;
return cmb;
}
diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 286fda4..ab4f8db 100644
--- a/drivers/nvme/host/rdma.c
+++ b/drivers/nvme/host/rdma.c
@@ -337,8 +337,6 @@
struct ib_device *ibdev = dev->dev;
int ret;
- BUG_ON(queue_idx >= ctrl->queue_count);
-
ret = nvme_rdma_alloc_qe(ibdev, &req->sqe, sizeof(struct nvme_command),
DMA_TO_DEVICE);
if (ret)
@@ -643,8 +641,22 @@
static int nvme_rdma_init_io_queues(struct nvme_rdma_ctrl *ctrl)
{
+ struct nvmf_ctrl_options *opts = ctrl->ctrl.opts;
+ unsigned int nr_io_queues;
int i, ret;
+ nr_io_queues = min(opts->nr_io_queues, num_online_cpus());
+ ret = nvme_set_queue_count(&ctrl->ctrl, &nr_io_queues);
+ if (ret)
+ return ret;
+
+ ctrl->queue_count = nr_io_queues + 1;
+ if (ctrl->queue_count < 2)
+ return 0;
+
+ dev_info(ctrl->ctrl.device,
+ "creating %d I/O queues.\n", nr_io_queues);
+
for (i = 1; i < ctrl->queue_count; i++) {
ret = nvme_rdma_init_queue(ctrl, i,
ctrl->ctrl.opts->queue_size);
@@ -1795,20 +1807,8 @@
static int nvme_rdma_create_io_queues(struct nvme_rdma_ctrl *ctrl)
{
- struct nvmf_ctrl_options *opts = ctrl->ctrl.opts;
int ret;
- ret = nvme_set_queue_count(&ctrl->ctrl, &opts->nr_io_queues);
- if (ret)
- return ret;
-
- ctrl->queue_count = opts->nr_io_queues + 1;
- if (ctrl->queue_count < 2)
- return 0;
-
- dev_info(ctrl->ctrl.device,
- "creating %d I/O queues.\n", opts->nr_io_queues);
-
ret = nvme_rdma_init_io_queues(ctrl);
if (ret)
return ret;
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index b57fc6d..d08dfc8 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -586,6 +586,14 @@
events = status & (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_CC |
PCI_EXP_SLTSTA_DLLSC);
+
+ /*
+ * If we've already reported a power fault, don't report it again
+ * until we've done something to handle it.
+ */
+ if (ctrl->power_fault_detected)
+ events &= ~PCI_EXP_SLTSTA_PFD;
+
if (!events)
return IRQ_NONE;
diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c
index de0ea47..e5824c7 100644
--- a/drivers/pci/hotplug/shpchp_hpc.c
+++ b/drivers/pci/hotplug/shpchp_hpc.c
@@ -1062,6 +1062,8 @@
if (rc) {
ctrl_info(ctrl, "Can't get msi for the hotplug controller\n");
ctrl_info(ctrl, "Use INTx for the hotplug controller\n");
+ } else {
+ pci_set_master(pdev);
}
rc = request_irq(ctrl->pci_dev->irq, shpc_isr, IRQF_SHARED,
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 1b07865..f9f4d1c 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -527,7 +527,7 @@
const char *buf, size_t count)
{
struct pci_dev *pdev = to_pci_dev(dev);
- char *driver_override, *old = pdev->driver_override, *cp;
+ char *driver_override, *old, *cp;
/* We need to keep extra room for a newline */
if (count >= (PAGE_SIZE - 1))
@@ -541,12 +541,15 @@
if (cp)
*cp = '\0';
+ device_lock(dev);
+ old = pdev->driver_override;
if (strlen(driver_override)) {
pdev->driver_override = driver_override;
} else {
kfree(driver_override);
pdev->driver_override = NULL;
}
+ device_unlock(dev);
kfree(old);
@@ -557,8 +560,12 @@
struct device_attribute *attr, char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev);
+ ssize_t len;
- return snprintf(buf, PAGE_SIZE, "%s\n", pdev->driver_override);
+ device_lock(dev);
+ len = snprintf(buf, PAGE_SIZE, "%s\n", pdev->driver_override);
+ device_unlock(dev);
+ return len;
}
static DEVICE_ATTR_RW(driver_override);
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 0e75d94..671610c 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -82,6 +82,7 @@
tristate "AMD GPIO pin control"
depends on GPIOLIB
select GPIOLIB_IRQCHIP
+ select PINMUX
select PINCONF
select GENERIC_PINCONF
help
diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c
index c9a1469..a5b7bd3 100644
--- a/drivers/pinctrl/pinctrl-amd.c
+++ b/drivers/pinctrl/pinctrl-amd.c
@@ -32,6 +32,7 @@
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
+#include "core.h"
#include "pinctrl-utils.h"
#include "pinctrl-amd.h"
@@ -712,6 +713,69 @@
.pin_config_group_set = amd_pinconf_group_set,
};
+#ifdef CONFIG_PM_SLEEP
+static bool amd_gpio_should_save(struct amd_gpio *gpio_dev, unsigned int pin)
+{
+ const struct pin_desc *pd = pin_desc_get(gpio_dev->pctrl, pin);
+
+ if (!pd)
+ return false;
+
+ /*
+ * Only restore the pin if it is actually in use by the kernel (or
+ * by userspace).
+ */
+ if (pd->mux_owner || pd->gpio_owner ||
+ gpiochip_line_is_irq(&gpio_dev->gc, pin))
+ return true;
+
+ return false;
+}
+
+int amd_gpio_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct amd_gpio *gpio_dev = platform_get_drvdata(pdev);
+ struct pinctrl_desc *desc = gpio_dev->pctrl->desc;
+ int i;
+
+ for (i = 0; i < desc->npins; i++) {
+ int pin = desc->pins[i].number;
+
+ if (!amd_gpio_should_save(gpio_dev, pin))
+ continue;
+
+ gpio_dev->saved_regs[i] = readl(gpio_dev->base + pin*4);
+ }
+
+ return 0;
+}
+
+int amd_gpio_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct amd_gpio *gpio_dev = platform_get_drvdata(pdev);
+ struct pinctrl_desc *desc = gpio_dev->pctrl->desc;
+ int i;
+
+ for (i = 0; i < desc->npins; i++) {
+ int pin = desc->pins[i].number;
+
+ if (!amd_gpio_should_save(gpio_dev, pin))
+ continue;
+
+ writel(gpio_dev->saved_regs[i], gpio_dev->base + pin*4);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops amd_gpio_pm_ops = {
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(amd_gpio_suspend,
+ amd_gpio_resume)
+};
+#endif
+
static struct pinctrl_desc amd_pinctrl_desc = {
.pins = kerncz_pins,
.npins = ARRAY_SIZE(kerncz_pins),
@@ -751,6 +815,14 @@
return -EINVAL;
}
+#ifdef CONFIG_PM_SLEEP
+ gpio_dev->saved_regs = devm_kcalloc(&pdev->dev, amd_pinctrl_desc.npins,
+ sizeof(*gpio_dev->saved_regs),
+ GFP_KERNEL);
+ if (!gpio_dev->saved_regs)
+ return -ENOMEM;
+#endif
+
gpio_dev->pdev = pdev;
gpio_dev->gc.direction_input = amd_gpio_direction_input;
gpio_dev->gc.direction_output = amd_gpio_direction_output;
@@ -839,6 +911,9 @@
.driver = {
.name = "amd_gpio",
.acpi_match_table = ACPI_PTR(amd_gpio_acpi_match),
+#ifdef CONFIG_PM_SLEEP
+ .pm = &amd_gpio_pm_ops,
+#endif
},
.probe = amd_gpio_probe,
.remove = amd_gpio_remove,
diff --git a/drivers/pinctrl/pinctrl-amd.h b/drivers/pinctrl/pinctrl-amd.h
index 7bfea47..e8bbb20 100644
--- a/drivers/pinctrl/pinctrl-amd.h
+++ b/drivers/pinctrl/pinctrl-amd.h
@@ -95,6 +95,7 @@
struct gpio_chip gc;
struct resource *res;
struct platform_device *pdev;
+ u32 *saved_regs;
};
/* KERNCZ configuration*/
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index c02881b..6727da6 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -24,8 +24,6 @@
#define GSI_CMD_TIMEOUT (5*HZ)
#define GSI_STOP_CMD_TIMEOUT_MS 20
#define GSI_MAX_CH_LOW_WEIGHT 15
-#define GSI_MHI_ER_START 10
-#define GSI_MHI_ER_END 16
#define GSI_RESET_WA_MIN_SLEEP 1000
#define GSI_RESET_WA_MAX_SLEEP 2000
@@ -829,10 +827,23 @@
return -GSI_STATUS_ERROR;
}
- /* bitmap is max events excludes reserved events */
+ if (props->mhi_er_id_limits_valid &&
+ props->mhi_er_id_limits[0] > (gsi_ctx->max_ev - 1)) {
+ devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
+ gsi_ctx->base = NULL;
+ devm_free_irq(gsi_ctx->dev, props->irq, gsi_ctx);
+ GSIERR("MHI event ring start id %u is beyond max %u\n",
+ props->mhi_er_id_limits[0], gsi_ctx->max_ev);
+ return -GSI_STATUS_ERROR;
+ }
+
gsi_ctx->evt_bmap = ~((1 << gsi_ctx->max_ev) - 1);
- gsi_ctx->evt_bmap |= ((1 << (GSI_MHI_ER_END + 1)) - 1) ^
- ((1 << GSI_MHI_ER_START) - 1);
+
+ /* exclude reserved mhi events */
+ if (props->mhi_er_id_limits_valid)
+ gsi_ctx->evt_bmap |=
+ ((1 << (props->mhi_er_id_limits[1] + 1)) - 1) ^
+ ((1 << (props->mhi_er_id_limits[0])) - 1);
/*
* enable all interrupts but GSI_BREAK_POINT.
@@ -1084,8 +1095,8 @@
if (props->intf == GSI_EVT_CHTYPE_MHI_EV &&
(!props->evchid_valid ||
- props->evchid > GSI_MHI_ER_END ||
- props->evchid < GSI_MHI_ER_START)) {
+ props->evchid > gsi_ctx->per.mhi_er_id_limits[1] ||
+ props->evchid < gsi_ctx->per.mhi_er_id_limits[0])) {
GSIERR("MHI requires evchid valid=%d val=%u\n",
props->evchid_valid, props->evchid);
return -GSI_STATUS_INVALID_PARAMS;
diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c
index abb714d..96b9bd6 100644
--- a/drivers/platform/msm/ipa/ipa_api.c
+++ b/drivers/platform/msm/ipa/ipa_api.c
@@ -1095,7 +1095,7 @@
EXPORT_SYMBOL(ipa_reset_flt);
/**
- * allocate_nat_device() - Allocates memory for the NAT device
+ * ipa_allocate_nat_device() - Allocates memory for the NAT device
* @mem: [in/out] memory parameters
*
* Called by NAT client driver to allocate memory for the NAT entries. Based on
@@ -1103,15 +1103,55 @@
*
* Returns: 0 on success, negative on failure
*/
-int allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem)
+int ipa_allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem)
{
int ret;
- IPA_API_DISPATCH_RETURN(allocate_nat_device, mem);
+ IPA_API_DISPATCH_RETURN(ipa_allocate_nat_device, mem);
return ret;
}
-EXPORT_SYMBOL(allocate_nat_device);
+EXPORT_SYMBOL(ipa_allocate_nat_device);
+
+/**
+ * ipa_allocate_nat_table() - Allocates memory for the NAT table
+ * @table_alloc: [in/out] memory parameters
+ *
+ * Called by NAT client to allocate memory for the table entries.
+ * Based on the request size either shared or system memory will be used.
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_allocate_nat_table(struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_allocate_nat_table, table_alloc);
+
+ return ret;
+}
+EXPORT_SYMBOL(ipa_allocate_nat_table);
+
+
+/**
+ * ipa_allocate_ipv6ct_table() - Allocates memory for the IPv6CT table
+ * @table_alloc: [in/out] memory parameters
+ *
+ * Called by IPv6CT client to allocate memory for the table entries.
+ * Based on the request size either shared or system memory will be used.
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_allocate_ipv6ct_table(
+ struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_allocate_ipv6ct_table, table_alloc);
+
+ return ret;
+}
+EXPORT_SYMBOL(ipa_allocate_ipv6ct_table);
/**
* ipa_nat_init_cmd() - Post IP_V4_NAT_INIT command to IPA HW
@@ -1132,6 +1172,25 @@
EXPORT_SYMBOL(ipa_nat_init_cmd);
/**
+ * ipa_ipv6ct_init_cmd() - Post IP_V6_CONN_TRACK_INIT command to IPA HW
+ * @init: [in] initialization command attributes
+ *
+ * Called by IPv6CT client driver to post IP_V6_CONN_TRACK_INIT command
+ * to IPA HW.
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_ipv6ct_init_cmd(struct ipa_ioc_ipv6ct_init *init)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_ipv6ct_init_cmd, init);
+
+ return ret;
+}
+EXPORT_SYMBOL(ipa_ipv6ct_init_cmd);
+
+/**
* ipa_nat_dma_cmd() - Post NAT_DMA command to IPA HW
* @dma: [in] initialization command attributes
*
@@ -1150,8 +1209,26 @@
EXPORT_SYMBOL(ipa_nat_dma_cmd);
/**
- * ipa_nat_del_cmd() - Delete a NAT table
- * @del: [in] delete table table table parameters
+ * ipa_table_dma_cmd() - Post TABLE_DMA command to IPA HW
+ * @dma: [in] initialization command attributes
+ *
+ * Called by NAT/IPv6CT client to post TABLE_DMA command to IPA HW
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_table_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_table_dma_cmd, dma);
+
+ return ret;
+}
+EXPORT_SYMBOL(ipa_table_dma_cmd);
+
+/**
+ * ipa_nat_del_cmd() - Delete the NAT table
+ * @del: [in] delete NAT table parameters
*
* Called by NAT client driver to delete the nat table
*
@@ -1168,6 +1245,60 @@
EXPORT_SYMBOL(ipa_nat_del_cmd);
/**
+ * ipa_del_nat_table() - Delete the NAT table
+ * @del: [in] delete table parameters
+ *
+ * Called by NAT client to delete the table
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_del_nat_table(struct ipa_ioc_nat_ipv6ct_table_del *del)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_del_nat_table, del);
+
+ return ret;
+}
+EXPORT_SYMBOL(ipa_del_nat_table);
+
+/**
+ * ipa_del_ipv6ct_table() - Delete the IPv6CT table
+ * @del: [in] delete table parameters
+ *
+ * Called by IPv6CT client to delete the table
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_del_ipv6ct_table(struct ipa_ioc_nat_ipv6ct_table_del *del)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_del_ipv6ct_table, del);
+
+ return ret;
+}
+EXPORT_SYMBOL(ipa_del_ipv6ct_table);
+
+/**
+ * ipa3_nat_mdfy_pdn() - Modify a PDN entry in PDN config table in IPA SRAM
+ * @mdfy_pdn: [in] PDN info to be written to SRAM
+ *
+ * Called by NAT client driver to modify an entry in the PDN config table
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_nat_mdfy_pdn, mdfy_pdn);
+
+ return ret;
+}
+EXPORT_SYMBOL(ipa_nat_mdfy_pdn);
+
+/**
* ipa_send_msg() - Send "message" from kernel client to IPA driver
* @meta: [in] message meta-data
* @buff: [in] the payload for message
@@ -3056,6 +3187,18 @@
return ret;
}
+/**
+ * ipa_tz_unlock_reg() - Allow AP access to memory regions controlled by TZ
+ */
+int ipa_tz_unlock_reg(struct ipa_tz_unlock_reg_info *reg_info, u16 num_regs)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_tz_unlock_reg, reg_info, num_regs);
+
+ return ret;
+}
+
static const struct dev_pm_ops ipa_pm_ops = {
.suspend_noirq = ipa_ap_suspend,
.resume_noirq = ipa_ap_resume,
diff --git a/drivers/platform/msm/ipa/ipa_api.h b/drivers/platform/msm/ipa/ipa_api.h
index 7a48b68..b526711 100644
--- a/drivers/platform/msm/ipa/ipa_api.h
+++ b/drivers/platform/msm/ipa/ipa_api.h
@@ -115,14 +115,30 @@
int (*ipa_reset_flt)(enum ipa_ip_type ip);
- int (*allocate_nat_device)(struct ipa_ioc_nat_alloc_mem *mem);
+ int (*ipa_allocate_nat_device)(struct ipa_ioc_nat_alloc_mem *mem);
+
+ int (*ipa_allocate_nat_table)(
+ struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc);
+
+ int (*ipa_allocate_ipv6ct_table)(
+ struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc);
int (*ipa_nat_init_cmd)(struct ipa_ioc_v4_nat_init *init);
+ int (*ipa_ipv6ct_init_cmd)(struct ipa_ioc_ipv6ct_init *init);
+
int (*ipa_nat_dma_cmd)(struct ipa_ioc_nat_dma_cmd *dma);
+ int (*ipa_table_dma_cmd)(struct ipa_ioc_nat_dma_cmd *dma);
+
int (*ipa_nat_del_cmd)(struct ipa_ioc_v4_nat_del *del);
+ int (*ipa_del_nat_table)(struct ipa_ioc_nat_ipv6ct_table_del *del);
+
+ int (*ipa_del_ipv6ct_table)(struct ipa_ioc_nat_ipv6ct_table_del *del);
+
+ int (*ipa_nat_mdfy_pdn)(struct ipa_ioc_nat_pdn_entry *mdfy_pdn);
+
int (*ipa_send_msg)(struct ipa_msg_meta *meta, void *buff,
ipa_msg_free_fn callback);
@@ -398,6 +414,9 @@
int (*ipa_disable_wdi3_pipes)(int ipa_ep_idx_tx,
int ipa_ep_idx_rx);
+
+ int (*ipa_tz_unlock_reg)(struct ipa_tz_unlock_reg_info *reg_info,
+ u16 num_regs);
};
#ifdef CONFIG_IPA
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
index e18c0d4..7a683ec 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
@@ -41,11 +41,6 @@
#define IPA_MHI_MAX_UL_CHANNELS 1
#define IPA_MHI_MAX_DL_CHANNELS 1
-#if (IPA_MHI_MAX_UL_CHANNELS + IPA_MHI_MAX_DL_CHANNELS) > \
- (IPA_MHI_GSI_ER_END - IPA_MHI_GSI_ER_START)
-#error not enought event rings for MHI
-#endif
-
/* bit #40 in address should be asserted for MHI transfers over pcie */
#define IPA_MHI_CLIENT_HOST_ADDR_COND(addr) \
((ipa_mhi_client_ctx->assert_bit40)?(IPA_MHI_HOST_ADDR(addr)):(addr))
@@ -1574,8 +1569,7 @@
internal.start.gsi.mhi = &channel->ch_scratch.mhi;
internal.start.gsi.cached_gsi_evt_ring_hdl =
&channel->cached_gsi_evt_ring_hdl;
- internal.start.gsi.evchid =
- channel->index + IPA_MHI_GSI_ER_START;
+ internal.start.gsi.evchid = channel->index;
res = ipa_connect_mhi_pipe(&internal, clnt_hdl);
if (res) {
diff --git a/drivers/platform/msm/ipa/ipa_common_i.h b/drivers/platform/msm/ipa/ipa_common_i.h
index fe8cbc0..0a406d2 100644
--- a/drivers/platform/msm/ipa/ipa_common_i.h
+++ b/drivers/platform/msm/ipa/ipa_common_i.h
@@ -145,9 +145,6 @@
u32 size;
};
-#define IPA_MHI_GSI_ER_START 10
-#define IPA_MHI_GSI_ER_END 16
-
/**
* enum ipa3_mhi_burst_mode - MHI channel burst mode state
*
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c
index 6230356..c760f75 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c
@@ -602,8 +602,6 @@
if (_IOC_TYPE(cmd) != IPA_IOC_MAGIC)
return -ENOTTY;
- if (_IOC_NR(cmd) >= IPA_IOCTL_MAX)
- return -ENOTTY;
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
@@ -1465,7 +1463,7 @@
}
break;
- default: /* redundant, as cmd was checked against MAXNR */
+ default:
IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
return -ENOTTY;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
index 210ddfe..e611abd 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
@@ -4969,7 +4969,7 @@
api_ctrl->ipa_mdfy_flt_rule = ipa2_mdfy_flt_rule;
api_ctrl->ipa_commit_flt = ipa2_commit_flt;
api_ctrl->ipa_reset_flt = ipa2_reset_flt;
- api_ctrl->allocate_nat_device = ipa2_allocate_nat_device;
+ api_ctrl->ipa_allocate_nat_device = ipa2_allocate_nat_device;
api_ctrl->ipa_nat_init_cmd = ipa2_nat_init_cmd;
api_ctrl->ipa_nat_dma_cmd = ipa2_nat_dma_cmd;
api_ctrl->ipa_nat_del_cmd = ipa2_nat_del_cmd;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index e5aa6ef..fb7b3a7 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -86,6 +86,9 @@
#define IPA3_ACTIVE_CLIENT_LOG_TYPE_RESOURCE 2
#define IPA3_ACTIVE_CLIENT_LOG_TYPE_SPECIAL 3
+#define IPA_MHI_GSI_EVENT_RING_ID_START 10
+#define IPA_MHI_GSI_EVENT_RING_ID_END 12
+
#define IPA_SMEM_SIZE (8 * 1024)
#define IPA_GSI_CHANNEL_HALT_MIN_SLEEP 5000
@@ -147,15 +150,33 @@
#define IPA_IOC_ALLOC_NAT_MEM32 _IOWR(IPA_IOC_MAGIC, \
IPA_IOCTL_ALLOC_NAT_MEM, \
compat_uptr_t)
+#define IPA_IOC_ALLOC_NAT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_ALLOC_NAT_TABLE, \
+ compat_uptr_t)
+#define IPA_IOC_ALLOC_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_ALLOC_IPV6CT_TABLE, \
+ compat_uptr_t)
#define IPA_IOC_V4_INIT_NAT32 _IOWR(IPA_IOC_MAGIC, \
IPA_IOCTL_V4_INIT_NAT, \
compat_uptr_t)
-#define IPA_IOC_NAT_DMA32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_NAT_DMA, \
+#define IPA_IOC_INIT_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_INIT_IPV6CT_TABLE, \
+ compat_uptr_t)
+#define IPA_IOC_TABLE_DMA_CMD32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_TABLE_DMA_CMD, \
compat_uptr_t)
#define IPA_IOC_V4_DEL_NAT32 _IOWR(IPA_IOC_MAGIC, \
IPA_IOCTL_V4_DEL_NAT, \
compat_uptr_t)
+#define IPA_IOC_DEL_NAT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_DEL_NAT_TABLE, \
+ compat_uptr_t)
+#define IPA_IOC_DEL_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_DEL_IPV6CT_TABLE, \
+ compat_uptr_t)
+#define IPA_IOC_NAT_MODIFY_PDN32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_NAT_MODIFY_PDN, \
+ compat_uptr_t)
#define IPA_IOC_GET_NAT_OFFSET32 _IOWR(IPA_IOC_MAGIC, \
IPA_IOCTL_GET_NAT_OFFSET, \
compat_uptr_t)
@@ -211,6 +232,18 @@
compat_size_t size;
compat_off_t offset;
};
+
+/**
+ * struct ipa_ioc_nat_ipv6ct_table_alloc32 - table memory allocation
+ * properties
+ * @size: input parameter, size of table in bytes
+ * @offset: output parameter, offset into page in case of system memory
+ */
+struct ipa_ioc_nat_ipv6ct_table_alloc32 {
+ compat_size_t size;
+ compat_off_t offset;
+};
+
#endif
#define IPA_TZ_UNLOCK_ATTRIBUTE 0x0C0311
@@ -545,7 +578,7 @@
return -ENOMEM;
}
- if (copy_from_user((u8 *)wan_msg, (u8 *)usr_param,
+ if (copy_from_user(wan_msg, (const void __user *)usr_param,
sizeof(struct ipa_wan_msg))) {
kfree(wan_msg);
return -EFAULT;
@@ -677,10 +710,14 @@
u8 header[128] = { 0 };
u8 *param = NULL;
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;
+ struct ipa_ioc_ipv6ct_init ipv6ct_init;
struct ipa_ioc_v4_nat_del nat_del;
+ struct ipa_ioc_nat_ipv6ct_table_del table_del;
struct ipa_ioc_nat_pdn_entry mdfy_pdn;
struct ipa_ioc_rm_dependency rm_depend;
+ struct ipa_ioc_nat_dma_cmd *table_dma_cmd;
size_t sz;
int pre_entry;
@@ -688,8 +725,6 @@
if (_IOC_TYPE(cmd) != IPA_IOC_MAGIC)
return -ENOTTY;
- if (_IOC_NR(cmd) >= IPA_IOCTL_MAX)
- return -ENOTTY;
if (!ipa3_is_ready()) {
IPAERR("IPA not ready, waiting for init completion\n");
@@ -700,8 +735,8 @@
switch (cmd) {
case IPA_IOC_ALLOC_NAT_MEM:
- if (copy_from_user((u8 *)&nat_mem, (u8 *)arg,
- sizeof(struct ipa_ioc_nat_alloc_mem))) {
+ if (copy_from_user(&nat_mem, (const void __user *)arg,
+ sizeof(struct ipa_ioc_nat_alloc_mem))) {
retval = -EFAULT;
break;
}
@@ -712,15 +747,53 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, (u8 *)&nat_mem,
- sizeof(struct ipa_ioc_nat_alloc_mem))) {
+ if (copy_to_user((void __user *)arg, &nat_mem,
+ sizeof(struct ipa_ioc_nat_alloc_mem))) {
retval = -EFAULT;
break;
}
break;
+ case IPA_IOC_ALLOC_NAT_TABLE:
+ if (copy_from_user(&table_alloc, (const void __user *)arg,
+ sizeof(struct ipa_ioc_nat_ipv6ct_table_alloc))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ if (ipa3_allocate_nat_table(&table_alloc)) {
+ retval = -EFAULT;
+ break;
+ }
+ if (table_alloc.offset &&
+ copy_to_user((void __user *)arg, &table_alloc, sizeof(
+ struct ipa_ioc_nat_ipv6ct_table_alloc))) {
+ retval = -EFAULT;
+ break;
+ }
+ break;
+
+ case IPA_IOC_ALLOC_IPV6CT_TABLE:
+ if (copy_from_user(&table_alloc, (const void __user *)arg,
+ sizeof(struct ipa_ioc_nat_ipv6ct_table_alloc))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ if (ipa3_allocate_ipv6ct_table(&table_alloc)) {
+ retval = -EFAULT;
+ break;
+ }
+ if (table_alloc.offset &&
+ copy_to_user((void __user *)arg, &table_alloc, sizeof(
+ struct ipa_ioc_nat_ipv6ct_table_alloc))) {
+ retval = -EFAULT;
+ break;
+ }
+ break;
+
case IPA_IOC_V4_INIT_NAT:
- if (copy_from_user((u8 *)&nat_init, (u8 *)arg,
- sizeof(struct ipa_ioc_v4_nat_init))) {
+ if (copy_from_user(&nat_init, (const void __user *)arg,
+ sizeof(struct ipa_ioc_v4_nat_init))) {
retval = -EFAULT;
break;
}
@@ -730,45 +803,56 @@
}
break;
- case IPA_IOC_NAT_DMA:
- if (copy_from_user(header, (u8 *)arg,
- sizeof(struct ipa_ioc_nat_dma_cmd))) {
+ case IPA_IOC_INIT_IPV6CT_TABLE:
+ if (copy_from_user(&ipv6ct_init, (const void __user *)arg,
+ sizeof(struct ipa_ioc_ipv6ct_init))) {
retval = -EFAULT;
break;
}
- pre_entry =
- ((struct ipa_ioc_nat_dma_cmd *)header)->entries;
- pyld_sz =
- sizeof(struct ipa_ioc_nat_dma_cmd) +
- pre_entry * sizeof(struct ipa_ioc_nat_dma_one);
+ if (ipa3_ipv6ct_init_cmd(&ipv6ct_init)) {
+ retval = -EFAULT;
+ break;
+ }
+ break;
+
+ case IPA_IOC_TABLE_DMA_CMD:
+ table_dma_cmd = (struct ipa_ioc_nat_dma_cmd *)header;
+ if (copy_from_user(header, (const void __user *)arg,
+ sizeof(struct ipa_ioc_nat_dma_cmd))) {
+ retval = -EFAULT;
+ break;
+ }
+ pre_entry = table_dma_cmd->entries;
+ pyld_sz = sizeof(struct ipa_ioc_nat_dma_cmd) +
+ pre_entry * sizeof(struct ipa_ioc_nat_dma_one);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
break;
}
- if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
retval = -EFAULT;
break;
}
+ table_dma_cmd = (struct ipa_ioc_nat_dma_cmd *)param;
+
/* add check in case user-space module compromised */
- if (unlikely(((struct ipa_ioc_nat_dma_cmd *)param)->entries
- != pre_entry)) {
+ if (unlikely(table_dma_cmd->entries != pre_entry)) {
IPAERR_RL("current %d pre %d\n",
- ((struct ipa_ioc_nat_dma_cmd *)param)->entries,
- pre_entry);
+ table_dma_cmd->entries, pre_entry);
retval = -EFAULT;
break;
}
- if (ipa3_nat_dma_cmd((struct ipa_ioc_nat_dma_cmd *)param)) {
+ if (ipa3_table_dma_cmd(table_dma_cmd)) {
retval = -EFAULT;
break;
}
break;
case IPA_IOC_V4_DEL_NAT:
- if (copy_from_user((u8 *)&nat_del, (u8 *)arg,
- sizeof(struct ipa_ioc_v4_nat_del))) {
+ if (copy_from_user(&nat_del, (const void __user *)arg,
+ sizeof(struct ipa_ioc_v4_nat_del))) {
retval = -EFAULT;
break;
}
@@ -778,8 +862,32 @@
}
break;
+ case IPA_IOC_DEL_NAT_TABLE:
+ if (copy_from_user(&table_del, (const void __user *)arg,
+ sizeof(struct ipa_ioc_nat_ipv6ct_table_del))) {
+ retval = -EFAULT;
+ break;
+ }
+ if (ipa3_del_nat_table(&table_del)) {
+ retval = -EFAULT;
+ break;
+ }
+ break;
+
+ case IPA_IOC_DEL_IPV6CT_TABLE:
+ if (copy_from_user(&table_del, (const void __user *)arg,
+ sizeof(struct ipa_ioc_nat_ipv6ct_table_del))) {
+ retval = -EFAULT;
+ break;
+ }
+ if (ipa3_del_ipv6ct_table(&table_del)) {
+ retval = -EFAULT;
+ break;
+ }
+ break;
+
case IPA_IOC_NAT_MODIFY_PDN:
- if (copy_from_user((u8 *)&mdfy_pdn, (const void __user *)arg,
+ if (copy_from_user(&mdfy_pdn, (const void __user *)arg,
sizeof(struct ipa_ioc_nat_pdn_entry))) {
retval = -EFAULT;
break;
@@ -791,8 +899,8 @@
break;
case IPA_IOC_ADD_HDR:
- if (copy_from_user(header, (u8 *)arg,
- sizeof(struct ipa_ioc_add_hdr))) {
+ if (copy_from_user(header, (const void __user *)arg,
+ sizeof(struct ipa_ioc_add_hdr))) {
retval = -EFAULT;
break;
}
@@ -806,7 +914,7 @@
retval = -ENOMEM;
break;
}
- if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -823,15 +931,15 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ if (copy_to_user((void __user *)arg, param, pyld_sz)) {
retval = -EFAULT;
break;
}
break;
case IPA_IOC_DEL_HDR:
- if (copy_from_user(header, (u8 *)arg,
- sizeof(struct ipa_ioc_del_hdr))) {
+ if (copy_from_user(header, (const void __user *)arg,
+ sizeof(struct ipa_ioc_del_hdr))) {
retval = -EFAULT;
break;
}
@@ -845,7 +953,7 @@
retval = -ENOMEM;
break;
}
- if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -863,15 +971,15 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ if (copy_to_user((void __user *)arg, param, pyld_sz)) {
retval = -EFAULT;
break;
}
break;
case IPA_IOC_ADD_RT_RULE:
- if (copy_from_user(header, (u8 *)arg,
- sizeof(struct ipa_ioc_add_rt_rule))) {
+ if (copy_from_user(header, (const void __user *)arg,
+ sizeof(struct ipa_ioc_add_rt_rule))) {
retval = -EFAULT;
break;
}
@@ -885,7 +993,7 @@
retval = -ENOMEM;
break;
}
- if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -903,13 +1011,13 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ if (copy_to_user((void __user *)arg, param, pyld_sz)) {
retval = -EFAULT;
break;
}
break;
case IPA_IOC_ADD_RT_RULE_AFTER:
- if (copy_from_user(header, (u8 *)arg,
+ if (copy_from_user(header, (const void __user *)arg,
sizeof(struct ipa_ioc_add_rt_rule_after))) {
retval = -EFAULT;
@@ -925,7 +1033,7 @@
retval = -ENOMEM;
break;
}
- if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -945,15 +1053,15 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ if (copy_to_user((void __user *)arg, param, pyld_sz)) {
retval = -EFAULT;
break;
}
break;
case IPA_IOC_MDFY_RT_RULE:
- if (copy_from_user(header, (u8 *)arg,
- sizeof(struct ipa_ioc_mdfy_rt_rule))) {
+ if (copy_from_user(header, (const void __user *)arg,
+ sizeof(struct ipa_ioc_mdfy_rt_rule))) {
retval = -EFAULT;
break;
}
@@ -967,7 +1075,7 @@
retval = -ENOMEM;
break;
}
- if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -985,15 +1093,15 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ if (copy_to_user((void __user *)arg, param, pyld_sz)) {
retval = -EFAULT;
break;
}
break;
case IPA_IOC_DEL_RT_RULE:
- if (copy_from_user(header, (u8 *)arg,
- sizeof(struct ipa_ioc_del_rt_rule))) {
+ if (copy_from_user(header, (const void __user *)arg,
+ sizeof(struct ipa_ioc_del_rt_rule))) {
retval = -EFAULT;
break;
}
@@ -1007,7 +1115,7 @@
retval = -ENOMEM;
break;
}
- if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -1024,15 +1132,15 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ if (copy_to_user((void __user *)arg, param, pyld_sz)) {
retval = -EFAULT;
break;
}
break;
case IPA_IOC_ADD_FLT_RULE:
- if (copy_from_user(header, (u8 *)arg,
- sizeof(struct ipa_ioc_add_flt_rule))) {
+ if (copy_from_user(header, (const void __user *)arg,
+ sizeof(struct ipa_ioc_add_flt_rule))) {
retval = -EFAULT;
break;
}
@@ -1046,7 +1154,7 @@
retval = -ENOMEM;
break;
}
- if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -1064,15 +1172,15 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ if (copy_to_user((void __user *)arg, param, pyld_sz)) {
retval = -EFAULT;
break;
}
break;
case IPA_IOC_ADD_FLT_RULE_AFTER:
- if (copy_from_user(header, (u8 *)arg,
- sizeof(struct ipa_ioc_add_flt_rule_after))) {
+ if (copy_from_user(header, (const void __user *)arg,
+ sizeof(struct ipa_ioc_add_flt_rule_after))) {
retval = -EFAULT;
break;
@@ -1088,7 +1196,7 @@
retval = -ENOMEM;
break;
}
- if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -1107,15 +1215,15 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ if (copy_to_user((void __user *)arg, param, pyld_sz)) {
retval = -EFAULT;
break;
}
break;
case IPA_IOC_DEL_FLT_RULE:
- if (copy_from_user(header, (u8 *)arg,
- sizeof(struct ipa_ioc_del_flt_rule))) {
+ if (copy_from_user(header, (const void __user *)arg,
+ sizeof(struct ipa_ioc_del_flt_rule))) {
retval = -EFAULT;
break;
}
@@ -1129,7 +1237,7 @@
retval = -ENOMEM;
break;
}
- if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -1147,15 +1255,15 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ if (copy_to_user((void __user *)arg, param, pyld_sz)) {
retval = -EFAULT;
break;
}
break;
case IPA_IOC_MDFY_FLT_RULE:
- if (copy_from_user(header, (u8 *)arg,
- sizeof(struct ipa_ioc_mdfy_flt_rule))) {
+ if (copy_from_user(header, (const void __user *)arg,
+ sizeof(struct ipa_ioc_mdfy_flt_rule))) {
retval = -EFAULT;
break;
}
@@ -1169,7 +1277,7 @@
retval = -ENOMEM;
break;
}
- if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -1187,7 +1295,7 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ if (copy_to_user((void __user *)arg, param, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -1212,8 +1320,8 @@
retval = ipa3_reset_flt(arg);
break;
case IPA_IOC_GET_RT_TBL:
- if (copy_from_user(header, (u8 *)arg,
- sizeof(struct ipa_ioc_get_rt_tbl))) {
+ if (copy_from_user(header, (const void __user *)arg,
+ sizeof(struct ipa_ioc_get_rt_tbl))) {
retval = -EFAULT;
break;
}
@@ -1221,7 +1329,7 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, header,
+ if (copy_to_user((void __user *)arg, header,
sizeof(struct ipa_ioc_get_rt_tbl))) {
retval = -EFAULT;
break;
@@ -1231,8 +1339,8 @@
retval = ipa3_put_rt_tbl(arg);
break;
case IPA_IOC_GET_HDR:
- if (copy_from_user(header, (u8 *)arg,
- sizeof(struct ipa_ioc_get_hdr))) {
+ if (copy_from_user(header, (const void __user *)arg,
+ sizeof(struct ipa_ioc_get_hdr))) {
retval = -EFAULT;
break;
}
@@ -1240,8 +1348,8 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, header,
- sizeof(struct ipa_ioc_get_hdr))) {
+ if (copy_to_user((void __user *)arg, header,
+ sizeof(struct ipa_ioc_get_hdr))) {
retval = -EFAULT;
break;
}
@@ -1253,8 +1361,8 @@
retval = ipa3_cfg_filter(arg);
break;
case IPA_IOC_COPY_HDR:
- if (copy_from_user(header, (u8 *)arg,
- sizeof(struct ipa_ioc_copy_hdr))) {
+ if (copy_from_user(header, (const void __user *)arg,
+ sizeof(struct ipa_ioc_copy_hdr))) {
retval = -EFAULT;
break;
}
@@ -1262,15 +1370,15 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, header,
- sizeof(struct ipa_ioc_copy_hdr))) {
+ if (copy_to_user((void __user *)arg, header,
+ sizeof(struct ipa_ioc_copy_hdr))) {
retval = -EFAULT;
break;
}
break;
case IPA_IOC_QUERY_INTF:
- if (copy_from_user(header, (u8 *)arg,
- sizeof(struct ipa_ioc_query_intf))) {
+ if (copy_from_user(header, (const void __user *)arg,
+ sizeof(struct ipa_ioc_query_intf))) {
retval = -EFAULT;
break;
}
@@ -1278,21 +1386,21 @@
retval = -1;
break;
}
- if (copy_to_user((u8 *)arg, header,
- sizeof(struct ipa_ioc_query_intf))) {
+ if (copy_to_user((void __user *)arg, header,
+ sizeof(struct ipa_ioc_query_intf))) {
retval = -EFAULT;
break;
}
break;
case IPA_IOC_QUERY_INTF_TX_PROPS:
sz = sizeof(struct ipa_ioc_query_intf_tx_props);
- if (copy_from_user(header, (u8 *)arg, sz)) {
+ if (copy_from_user(header, (const void __user *)arg, sz)) {
retval = -EFAULT;
break;
}
if (((struct ipa_ioc_query_intf_tx_props *)header)->num_tx_props
- > IPA_NUM_PROPS_MAX) {
+ > IPA_NUM_PROPS_MAX) {
retval = -EFAULT;
break;
}
@@ -1306,7 +1414,7 @@
retval = -ENOMEM;
break;
}
- if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -1321,24 +1429,24 @@
break;
}
if (ipa3_query_intf_tx_props(
- (struct ipa_ioc_query_intf_tx_props *)param)) {
+ (struct ipa_ioc_query_intf_tx_props *)param)) {
retval = -1;
break;
}
- if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ if (copy_to_user((void __user *)arg, param, pyld_sz)) {
retval = -EFAULT;
break;
}
break;
case IPA_IOC_QUERY_INTF_RX_PROPS:
sz = sizeof(struct ipa_ioc_query_intf_rx_props);
- if (copy_from_user(header, (u8 *)arg, sz)) {
+ if (copy_from_user(header, (const void __user *)arg, sz)) {
retval = -EFAULT;
break;
}
if (((struct ipa_ioc_query_intf_rx_props *)header)->num_rx_props
- > IPA_NUM_PROPS_MAX) {
+ > IPA_NUM_PROPS_MAX) {
retval = -EFAULT;
break;
}
@@ -1352,7 +1460,7 @@
retval = -ENOMEM;
break;
}
- if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -1366,24 +1474,24 @@
break;
}
if (ipa3_query_intf_rx_props(
- (struct ipa_ioc_query_intf_rx_props *)param)) {
+ (struct ipa_ioc_query_intf_rx_props *)param)) {
retval = -1;
break;
}
- if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ if (copy_to_user((void __user *)arg, param, pyld_sz)) {
retval = -EFAULT;
break;
}
break;
case IPA_IOC_QUERY_INTF_EXT_PROPS:
sz = sizeof(struct ipa_ioc_query_intf_ext_props);
- if (copy_from_user(header, (u8 *)arg, sz)) {
+ if (copy_from_user(header, (const void __user *)arg, sz)) {
retval = -EFAULT;
break;
}
if (((struct ipa_ioc_query_intf_ext_props *)
- header)->num_ext_props > IPA_NUM_PROPS_MAX) {
+ header)->num_ext_props > IPA_NUM_PROPS_MAX) {
retval = -EFAULT;
break;
}
@@ -1397,7 +1505,7 @@
retval = -ENOMEM;
break;
}
- if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -1411,18 +1519,18 @@
break;
}
if (ipa3_query_intf_ext_props(
- (struct ipa_ioc_query_intf_ext_props *)param)) {
+ (struct ipa_ioc_query_intf_ext_props *)param)) {
retval = -1;
break;
}
- if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ if (copy_to_user((void __user *)arg, param, pyld_sz)) {
retval = -EFAULT;
break;
}
break;
case IPA_IOC_PULL_MSG:
- if (copy_from_user(header, (u8 *)arg,
- sizeof(struct ipa_msg_meta))) {
+ if (copy_from_user(header, (const void __user *)arg,
+ sizeof(struct ipa_msg_meta))) {
retval = -EFAULT;
break;
}
@@ -1435,7 +1543,7 @@
retval = -ENOMEM;
break;
}
- if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -1449,13 +1557,13 @@
break;
}
if (ipa3_pull_msg((struct ipa_msg_meta *)param,
- (char *)param + sizeof(struct ipa_msg_meta),
- ((struct ipa_msg_meta *)param)->msg_len) !=
- ((struct ipa_msg_meta *)param)->msg_len) {
+ (char *)param + sizeof(struct ipa_msg_meta),
+ ((struct ipa_msg_meta *)param)->msg_len) !=
+ ((struct ipa_msg_meta *)param)->msg_len) {
retval = -1;
break;
}
- if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ if (copy_to_user((void __user *)arg, param, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -1465,8 +1573,8 @@
if (ipa3_ctx->use_ipa_pm)
return 0;
- if (copy_from_user((u8 *)&rm_depend, (u8 *)arg,
- sizeof(struct ipa_ioc_rm_dependency))) {
+ if (copy_from_user(&rm_depend, (const void __user *)arg,
+ sizeof(struct ipa_ioc_rm_dependency))) {
retval = -EFAULT;
break;
}
@@ -1478,8 +1586,8 @@
if (ipa3_ctx->use_ipa_pm)
return 0;
- if (copy_from_user((u8 *)&rm_depend, (u8 *)arg,
- sizeof(struct ipa_ioc_rm_dependency))) {
+ if (copy_from_user(&rm_depend, (const void __user *)arg,
+ sizeof(struct ipa_ioc_rm_dependency))) {
retval = -EFAULT;
break;
}
@@ -1490,7 +1598,7 @@
{
struct ipa_ioc_generate_flt_eq flt_eq;
- if (copy_from_user(&flt_eq, (u8 *)arg,
+ if (copy_from_user(&flt_eq, (const void __user *)arg,
sizeof(struct ipa_ioc_generate_flt_eq))) {
retval = -EFAULT;
break;
@@ -1500,7 +1608,7 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, &flt_eq,
+ if (copy_to_user((void __user *)arg, &flt_eq,
sizeof(struct ipa_ioc_generate_flt_eq))) {
retval = -EFAULT;
break;
@@ -1513,25 +1621,25 @@
break;
}
case IPA_IOC_QUERY_RT_TBL_INDEX:
- if (copy_from_user(header, (u8 *)arg,
- sizeof(struct ipa_ioc_get_rt_tbl_indx))) {
+ if (copy_from_user(header, (const void __user *)arg,
+ sizeof(struct ipa_ioc_get_rt_tbl_indx))) {
retval = -EFAULT;
break;
}
if (ipa3_query_rt_index(
- (struct ipa_ioc_get_rt_tbl_indx *)header)) {
+ (struct ipa_ioc_get_rt_tbl_indx *)header)) {
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, header,
- sizeof(struct ipa_ioc_get_rt_tbl_indx))) {
+ if (copy_to_user((void __user *)arg, header,
+ sizeof(struct ipa_ioc_get_rt_tbl_indx))) {
retval = -EFAULT;
break;
}
break;
case IPA_IOC_WRITE_QMAPID:
- if (copy_from_user(header, (u8 *)arg,
- sizeof(struct ipa_ioc_write_qmapid))) {
+ if (copy_from_user(header, (const void __user *)arg,
+ sizeof(struct ipa_ioc_write_qmapid))) {
retval = -EFAULT;
break;
}
@@ -1539,8 +1647,8 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, header,
- sizeof(struct ipa_ioc_write_qmapid))) {
+ if (copy_to_user((void __user *)arg, header,
+ sizeof(struct ipa_ioc_write_qmapid))) {
retval = -EFAULT;
break;
}
@@ -1567,7 +1675,7 @@
}
break;
case IPA_IOC_ADD_HDR_PROC_CTX:
- if (copy_from_user(header, (u8 *)arg,
+ if (copy_from_user(header, (const void __user *)arg,
sizeof(struct ipa_ioc_add_hdr_proc_ctx))) {
retval = -EFAULT;
break;
@@ -1583,7 +1691,7 @@
retval = -ENOMEM;
break;
}
- if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -1601,13 +1709,13 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ if (copy_to_user((void __user *)arg, param, pyld_sz)) {
retval = -EFAULT;
break;
}
break;
case IPA_IOC_DEL_HDR_PROC_CTX:
- if (copy_from_user(header, (u8 *)arg,
+ if (copy_from_user(header, (const void __user *)arg,
sizeof(struct ipa_ioc_del_hdr_proc_ctx))) {
retval = -EFAULT;
break;
@@ -1622,7 +1730,7 @@
retval = -ENOMEM;
break;
}
- if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -1641,7 +1749,7 @@
retval = -EFAULT;
break;
}
- if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ if (copy_to_user((void __user *)arg, param, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -1655,7 +1763,7 @@
break;
}
memcpy(param, &ipa3_ctx->ipa_hw_type, pyld_sz);
- if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ if (copy_to_user((void __user *)arg, param, pyld_sz)) {
retval = -EFAULT;
break;
}
@@ -1689,7 +1797,7 @@
}
break;
- default: /* redundant, as cmd was checked against MAXNR */
+ default:
IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
return -ENOTTY;
}
@@ -1713,8 +1821,8 @@
struct ipa_rt_rule_add *rt_rule_entry;
rt_rule =
- kzalloc(sizeof(struct ipa_ioc_add_rt_rule) + 1 *
- sizeof(struct ipa_rt_rule_add), GFP_KERNEL);
+ kzalloc(sizeof(struct ipa_ioc_add_rt_rule) + 1 *
+ sizeof(struct ipa_rt_rule_add), GFP_KERNEL);
if (!rt_rule) {
IPAERR("fail to alloc mem\n");
return -ENOMEM;
@@ -1724,7 +1832,7 @@
rt_rule->commit = 1;
rt_rule->ip = IPA_IP_v4;
strlcpy(rt_rule->rt_tbl_name, IPA_DFLT_RT_TBL_NAME,
- IPA_RESOURCE_NAME_MAX);
+ IPA_RESOURCE_NAME_MAX);
rt_rule_entry = &rt_rule->rules[0];
rt_rule_entry->at_rear = 1;
@@ -1853,10 +1961,7 @@
IPAERR("failed to construct dma_shared_mem imm cmd\n");
return -ENOMEM;
}
- desc.opcode = cmd_pyld->opcode;
- desc.pyld = cmd_pyld->data;
- desc.len = cmd_pyld->len;
- desc.type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
rc = ipa3_send_cmd(1, &desc);
if (rc) {
@@ -2117,6 +2222,12 @@
if (!ipa3_ctx->ep[pipe_idx].valid ||
ipa3_ctx->ep[pipe_idx].skip_ep_cfg) {
+ if (num_cmds >= ipa3_ctx->ep_flt_num) {
+ IPAERR("number of commands is out of range\n");
+ retval = -ENOBUFS;
+ goto free_empty_img;
+ }
+
cmd.is_read = false;
cmd.skip_pipeline_clear = false;
cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
@@ -2134,14 +2245,12 @@
retval = -ENOMEM;
goto free_empty_img;
}
- desc[num_cmds].opcode = cmd_pyld[num_cmds]->opcode;
- desc[num_cmds].pyld = cmd_pyld[num_cmds]->data;
- desc[num_cmds].len = cmd_pyld[num_cmds]->len;
- desc[num_cmds].type = IPA_IMM_CMD_DESC;
- num_cmds++;
+ ipa3_init_imm_cmd_desc(&desc[num_cmds],
+ cmd_pyld[num_cmds]);
+ ++num_cmds;
}
- flt_idx++;
+ ++flt_idx;
}
IPADBG("Sending %d descriptors for flt tbl clearing\n", num_cmds);
@@ -2233,10 +2342,7 @@
retval = -ENOMEM;
goto free_desc;
}
- desc->opcode = cmd_pyld->opcode;
- desc->pyld = cmd_pyld->data;
- desc->len = cmd_pyld->len;
- desc->type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(desc, cmd_pyld);
IPADBG("Sending 1 descriptor for rt tbl clearing\n");
retval = ipa3_send_cmd(1, desc);
@@ -2323,10 +2429,7 @@
retval = -EFAULT;
goto bail_desc;
}
- desc->opcode = cmd_pyld->opcode;
- desc->pyld = cmd_pyld->data;
- desc->len = cmd_pyld->len;
- desc->type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(desc, cmd_pyld);
IPADBG("Sending 1 descriptor for tbls flush\n");
retval = ipa3_send_cmd(1, desc);
@@ -2389,13 +2492,10 @@
return -EFAULT;
}
- desc[num_descs].opcode = cmd_pyld->opcode;
- desc[num_descs].type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&desc[num_descs], cmd_pyld);
desc[num_descs].callback = ipa3_destroy_imm;
desc[num_descs].user1 = cmd_pyld;
- desc[num_descs].pyld = cmd_pyld->data;
- desc[num_descs].len = cmd_pyld->len;
- num_descs++;
+ ++num_descs;
}
}
@@ -2565,7 +2665,7 @@
*/
int _ipa_init_hdr_v3_0(void)
{
- struct ipa3_desc desc = { 0 };
+ struct ipa3_desc desc;
struct ipa_mem_buffer mem;
struct ipahal_imm_cmd_hdr_init_local cmd = {0};
struct ipahal_imm_cmd_pyld *cmd_pyld;
@@ -2593,10 +2693,7 @@
mem.phys_base);
return -EFAULT;
}
- desc.opcode = cmd_pyld->opcode;
- desc.type = IPA_IMM_CMD_DESC;
- desc.pyld = cmd_pyld->data;
- desc.len = cmd_pyld->len;
+ ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
if (ipa3_send_cmd(1, &desc)) {
@@ -2620,7 +2717,6 @@
return -ENOMEM;
}
memset(mem.base, 0, mem.size);
- memset(&desc, 0, sizeof(desc));
dma_cmd.is_read = false;
dma_cmd.skip_pipeline_clear = false;
@@ -2638,10 +2734,7 @@
mem.phys_base);
return -EFAULT;
}
- desc.opcode = cmd_pyld->opcode;
- desc.pyld = cmd_pyld->data;
- desc.len = cmd_pyld->len;
- desc.type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
if (ipa3_send_cmd(1, &desc)) {
@@ -2669,7 +2762,7 @@
*/
int _ipa_init_rt4_v3(void)
{
- struct ipa3_desc desc = { 0 };
+ struct ipa3_desc desc;
struct ipa_mem_buffer mem;
struct ipahal_imm_cmd_ip_v4_routing_init v4_cmd;
struct ipahal_imm_cmd_pyld *cmd_pyld;
@@ -2710,10 +2803,7 @@
goto free_mem;
}
- desc.opcode = cmd_pyld->opcode;
- desc.type = IPA_IMM_CMD_DESC;
- desc.pyld = cmd_pyld->data;
- desc.len = cmd_pyld->len;
+ ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
if (ipa3_send_cmd(1, &desc)) {
@@ -2735,7 +2825,7 @@
*/
int _ipa_init_rt6_v3(void)
{
- struct ipa3_desc desc = { 0 };
+ struct ipa3_desc desc;
struct ipa_mem_buffer mem;
struct ipahal_imm_cmd_ip_v6_routing_init v6_cmd;
struct ipahal_imm_cmd_pyld *cmd_pyld;
@@ -2776,10 +2866,7 @@
goto free_mem;
}
- desc.opcode = cmd_pyld->opcode;
- desc.type = IPA_IMM_CMD_DESC;
- desc.pyld = cmd_pyld->data;
- desc.len = cmd_pyld->len;
+ ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
if (ipa3_send_cmd(1, &desc)) {
@@ -2801,7 +2888,7 @@
*/
int _ipa_init_flt4_v3(void)
{
- struct ipa3_desc desc = { 0 };
+ struct ipa3_desc desc;
struct ipa_mem_buffer mem;
struct ipahal_imm_cmd_ip_v4_filter_init v4_cmd;
struct ipahal_imm_cmd_pyld *cmd_pyld;
@@ -2836,10 +2923,7 @@
goto free_mem;
}
- desc.opcode = cmd_pyld->opcode;
- desc.type = IPA_IMM_CMD_DESC;
- desc.pyld = cmd_pyld->data;
- desc.len = cmd_pyld->len;
+ ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
if (ipa3_send_cmd(1, &desc)) {
@@ -2861,7 +2945,7 @@
*/
int _ipa_init_flt6_v3(void)
{
- struct ipa3_desc desc = { 0 };
+ struct ipa3_desc desc;
struct ipa_mem_buffer mem;
struct ipahal_imm_cmd_ip_v6_filter_init v6_cmd;
struct ipahal_imm_cmd_pyld *cmd_pyld;
@@ -2897,10 +2981,7 @@
goto free_mem;
}
- desc.opcode = cmd_pyld->opcode;
- desc.type = IPA_IMM_CMD_DESC;
- desc.pyld = cmd_pyld->data;
- desc.len = cmd_pyld->len;
+ ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
if (ipa3_send_cmd(1, &desc)) {
@@ -3118,9 +3199,38 @@
}
#ifdef CONFIG_COMPAT
+
+static long compat_ipa3_nat_ipv6ct_alloc_table(unsigned long arg,
+ int (alloc_func)(struct ipa_ioc_nat_ipv6ct_table_alloc *))
+{
+ long retval;
+ struct ipa_ioc_nat_ipv6ct_table_alloc32 table_alloc32;
+ struct ipa_ioc_nat_ipv6ct_table_alloc table_alloc;
+
+ retval = copy_from_user(&table_alloc32, (const void __user *)arg,
+ sizeof(struct ipa_ioc_nat_ipv6ct_table_alloc32));
+ if (retval)
+ return retval;
+
+ table_alloc.size = (size_t)table_alloc32.size;
+ table_alloc.offset = (off_t)table_alloc32.offset;
+
+ retval = alloc_func(&table_alloc);
+ if (retval)
+ return retval;
+
+ if (table_alloc.offset) {
+ table_alloc32.offset = (compat_off_t)table_alloc.offset;
+ retval = copy_to_user((void __user *)arg, &table_alloc32,
+ sizeof(struct ipa_ioc_nat_ipv6ct_table_alloc32));
+ }
+
+ return retval;
+}
+
long compat_ipa3_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- int retval = 0;
+ long retval = 0;
struct ipa3_ioc_nat_alloc_mem32 nat_mem32;
struct ipa_ioc_nat_alloc_mem nat_mem;
@@ -3165,11 +3275,10 @@
cmd = IPA_IOC_GET_HDR;
break;
case IPA_IOC_ALLOC_NAT_MEM32:
- if (copy_from_user((u8 *)&nat_mem32, (u8 *)arg,
- sizeof(struct ipa3_ioc_nat_alloc_mem32))) {
- retval = -EFAULT;
- goto ret;
- }
+ retval = copy_from_user(&nat_mem32, (const void __user *)arg,
+ sizeof(struct ipa3_ioc_nat_alloc_mem32));
+ if (retval)
+ return retval;
memcpy(nat_mem.dev_name, nat_mem32.dev_name,
IPA_RESOURCE_NAME_MAX);
nat_mem.size = (size_t)nat_mem32.size;
@@ -3178,26 +3287,40 @@
/* null terminate the string */
nat_mem.dev_name[IPA_RESOURCE_NAME_MAX - 1] = '\0';
- if (ipa3_allocate_nat_device(&nat_mem)) {
- retval = -EFAULT;
- goto ret;
- }
+ retval = ipa3_allocate_nat_device(&nat_mem);
+ if (retval)
+ return retval;
nat_mem32.offset = (compat_off_t)nat_mem.offset;
- if (copy_to_user((u8 *)arg, (u8 *)&nat_mem32,
- sizeof(struct ipa3_ioc_nat_alloc_mem32))) {
- retval = -EFAULT;
- }
-ret:
+ retval = copy_to_user((void __user *)arg, &nat_mem32,
+ sizeof(struct ipa3_ioc_nat_alloc_mem32));
return retval;
+ case IPA_IOC_ALLOC_NAT_TABLE32:
+ return compat_ipa3_nat_ipv6ct_alloc_table(arg,
+ ipa3_allocate_nat_table);
+ case IPA_IOC_ALLOC_IPV6CT_TABLE32:
+ return compat_ipa3_nat_ipv6ct_alloc_table(arg,
+ ipa3_allocate_ipv6ct_table);
case IPA_IOC_V4_INIT_NAT32:
cmd = IPA_IOC_V4_INIT_NAT;
break;
- case IPA_IOC_NAT_DMA32:
- cmd = IPA_IOC_NAT_DMA;
+ case IPA_IOC_INIT_IPV6CT_TABLE32:
+ cmd = IPA_IOC_INIT_IPV6CT_TABLE;
+ break;
+ case IPA_IOC_TABLE_DMA_CMD32:
+ cmd = IPA_IOC_TABLE_DMA_CMD;
break;
case IPA_IOC_V4_DEL_NAT32:
cmd = IPA_IOC_V4_DEL_NAT;
break;
+ case IPA_IOC_DEL_NAT_TABLE32:
+ cmd = IPA_IOC_DEL_NAT_TABLE;
+ break;
+ case IPA_IOC_DEL_IPV6CT_TABLE32:
+ cmd = IPA_IOC_DEL_IPV6CT_TABLE;
+ break;
+ case IPA_IOC_NAT_MODIFY_PDN32:
+ cmd = IPA_IOC_NAT_MODIFY_PDN;
+ break;
case IPA_IOC_GET_NAT_OFFSET32:
cmd = IPA_IOC_GET_NAT_OFFSET;
break;
@@ -4286,6 +4409,12 @@
gsi_props.req_clk_cb = NULL;
gsi_props.rel_clk_cb = NULL;
+ if (ipa3_ctx->ipa_config_is_mhi) {
+ gsi_props.mhi_er_id_limits_valid = true;
+ gsi_props.mhi_er_id_limits[0] = resource_p->mhi_evid_limits[0];
+ gsi_props.mhi_er_id_limits[1] = resource_p->mhi_evid_limits[1];
+ }
+
result = gsi_register_device(&gsi_props,
&ipa3_ctx->gsi_dev_hdl);
if (result != GSI_STATUS_SUCCESS) {
@@ -4492,50 +4621,70 @@
return count;
}
-static int ipa3_tz_unlock_reg(struct ipa3_context *ipa3_ctx)
+/**
+ * ipa3_tz_unlock_reg - Unlocks memory regions so that they become accessible
+ * from AP.
+ * @reg_info - Pointer to array of memory regions to unlock
+ * @num_regs - Number of elements in the array
+ *
+ * Converts the input array of regions to a struct that TZ understands and
+ * issues an SCM call.
+ * Also flushes the memory cache to DDR in order to make sure that TZ sees the
+ * correct data structure.
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa3_tz_unlock_reg(struct ipa_tz_unlock_reg_info *reg_info, u16 num_regs)
{
int i, size, ret, resp;
struct tz_smmu_ipa_protect_region_iovec_s *ipa_tz_unlock_vec;
struct tz_smmu_ipa_protect_region_s cmd_buf;
+ struct scm_desc desc = {0};
- if (ipa3_ctx && ipa3_ctx->ipa_tz_unlock_reg_num > 0) {
- size = ipa3_ctx->ipa_tz_unlock_reg_num *
- sizeof(struct tz_smmu_ipa_protect_region_iovec_s);
- ipa_tz_unlock_vec = kzalloc(PAGE_ALIGN(size), GFP_KERNEL);
- if (ipa_tz_unlock_vec == NULL)
- return -ENOMEM;
-
- for (i = 0; i < ipa3_ctx->ipa_tz_unlock_reg_num; i++) {
- ipa_tz_unlock_vec[i].input_addr =
- ipa3_ctx->ipa_tz_unlock_reg[i].reg_addr ^
- (ipa3_ctx->ipa_tz_unlock_reg[i].reg_addr &
- 0xFFF);
- ipa_tz_unlock_vec[i].output_addr =
- ipa3_ctx->ipa_tz_unlock_reg[i].reg_addr ^
- (ipa3_ctx->ipa_tz_unlock_reg[i].reg_addr &
- 0xFFF);
- ipa_tz_unlock_vec[i].size =
- ipa3_ctx->ipa_tz_unlock_reg[i].size;
- ipa_tz_unlock_vec[i].attr = IPA_TZ_UNLOCK_ATTRIBUTE;
- }
-
- /* pass physical address of command buffer */
- cmd_buf.iovec_buf = virt_to_phys((void *)ipa_tz_unlock_vec);
- cmd_buf.size_bytes = size;
-
- /* flush cache to DDR */
- __cpuc_flush_dcache_area((void *)ipa_tz_unlock_vec, size);
- outer_flush_range(cmd_buf.iovec_buf, cmd_buf.iovec_buf + size);
-
- ret = scm_call(SCM_SVC_MP, TZ_MEM_PROTECT_REGION_ID, &cmd_buf,
- sizeof(cmd_buf), &resp, sizeof(resp));
- if (ret) {
- IPAERR("scm call SCM_SVC_MP failed: %d\n", ret);
- kfree(ipa_tz_unlock_vec);
- return -EFAULT;
- }
- kfree(ipa_tz_unlock_vec);
+ if (reg_info == NULL || num_regs == 0) {
+ IPAERR("Bad parameters\n");
+ return -EFAULT;
}
+
+ size = num_regs * sizeof(struct tz_smmu_ipa_protect_region_iovec_s);
+ ipa_tz_unlock_vec = kzalloc(PAGE_ALIGN(size), GFP_KERNEL);
+ if (ipa_tz_unlock_vec == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < num_regs; i++) {
+ ipa_tz_unlock_vec[i].input_addr = reg_info[i].reg_addr ^
+ (reg_info[i].reg_addr & 0xFFF);
+ ipa_tz_unlock_vec[i].output_addr = reg_info[i].reg_addr ^
+ (reg_info[i].reg_addr & 0xFFF);
+ ipa_tz_unlock_vec[i].size = reg_info[i].size;
+ ipa_tz_unlock_vec[i].attr = IPA_TZ_UNLOCK_ATTRIBUTE;
+ }
+
+ /* pass physical address of command buffer */
+ cmd_buf.iovec_buf = virt_to_phys((void *)ipa_tz_unlock_vec);
+ cmd_buf.size_bytes = size;
+
+ /* flush cache to DDR */
+ __cpuc_flush_dcache_area((void *)ipa_tz_unlock_vec, size);
+ outer_flush_range(cmd_buf.iovec_buf, cmd_buf.iovec_buf + size);
+ if (!is_scm_armv8())
+ ret = scm_call(SCM_SVC_MP, TZ_MEM_PROTECT_REGION_ID,
+ &cmd_buf, sizeof(cmd_buf), &resp, sizeof(resp));
+ else {
+ desc.args[0] = virt_to_phys((void *)ipa_tz_unlock_vec);
+ desc.args[1] = size;
+ desc.arginfo = SCM_ARGS(2, SCM_RO, SCM_VAL);
+ ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
+ TZ_MEM_PROTECT_REGION_ID), &desc);
+ }
+
+ if (ret) {
+ IPAERR("scm call SCM_SVC_MP failed: %d\n", ret);
+ kfree(ipa_tz_unlock_vec);
+ return -EFAULT;
+ }
+ kfree(ipa_tz_unlock_vec);
+
return 0;
}
@@ -4667,6 +4816,8 @@
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->mhi_evid_limits[0] = resource_p->mhi_evid_limits[0];
+ ipa3_ctx->mhi_evid_limits[1] = resource_p->mhi_evid_limits[1];
if (resource_p->ipa_tz_unlock_reg) {
ipa3_ctx->ipa_tz_unlock_reg_num =
resource_p->ipa_tz_unlock_reg_num;
@@ -4687,7 +4838,10 @@
}
/* unlock registers for uc */
- ipa3_tz_unlock_reg(ipa3_ctx);
+ result = ipa3_tz_unlock_reg(ipa3_ctx->ipa_tz_unlock_reg,
+ ipa3_ctx->ipa_tz_unlock_reg_num);
+ if (result)
+ IPAERR("Failed to unlock memory region using TZ\n");
/* default aggregation parameters */
ipa3_ctx->aggregation_type = IPA_MBIM_16;
@@ -4939,7 +5093,6 @@
mutex_init(&ipa3_ctx->msg_lock);
mutex_init(&ipa3_ctx->lock);
- mutex_init(&ipa3_ctx->nat_mem.lock);
mutex_init(&ipa3_ctx->q6_proxy_clk_vote_mutex);
mutex_init(&ipa3_ctx->ipa_cne_evt_lock);
@@ -4969,10 +5122,10 @@
goto fail_device_create;
}
- if (ipa3_create_nat_device()) {
- IPAERR("unable to create nat device\n");
+ if (ipa3_nat_ipv6ct_init_devices()) {
+ IPAERR("unable to init NAT and IPv6CT devices\n");
result = -ENODEV;
- goto fail_nat_dev_add;
+ goto fail_nat_ipv6ct_init_dev;
}
/* Create a wakeup source. */
@@ -5071,7 +5224,8 @@
if (!ipa3_ctx->use_ipa_pm)
ipa_rm_exit();
fail_ipa_rm_init:
-fail_nat_dev_add:
+ ipa3_nat_ipv6ct_destroy_devices();
+fail_nat_ipv6ct_init_dev:
device_destroy(ipa3_ctx->class, ipa3_ctx->dev_num);
fail_device_create:
unregister_chrdev_region(ipa3_ctx->dev_num, 1);
@@ -5240,6 +5394,7 @@
struct resource *resource;
u32 *ipa_tz_unlock_reg;
int elem_num;
+ u32 mhi_evid_limits[2];
/* initialize ipa3_res */
ipa_drv_res->ipa_pipe_mem_start_ofst = IPA_PIPE_MEM_START_OFST;
@@ -5256,6 +5411,8 @@
ipa_drv_res->gsi_ch20_wa = false;
ipa_drv_res->ipa_tz_unlock_reg_num = 0;
ipa_drv_res->ipa_tz_unlock_reg = NULL;
+ ipa_drv_res->mhi_evid_limits[0] = IPA_MHI_GSI_EVENT_RING_ID_START;
+ ipa_drv_res->mhi_evid_limits[1] = IPA_MHI_GSI_EVENT_RING_ID_END;
/* Get IPA HW Version */
result = of_property_read_u32(pdev->dev.of_node, "qcom,ipa-hw-ver",
@@ -5432,6 +5589,34 @@
? "Needed" : "Not needed");
elem_num = of_property_count_elems_of_size(pdev->dev.of_node,
+ "qcom,mhi-event-ring-id-limits", sizeof(u32));
+
+ if (elem_num == 2) {
+ if (of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,mhi-event-ring-id-limits", mhi_evid_limits, 2)) {
+ IPAERR("failed to read mhi event ring id limits\n");
+ return -EFAULT;
+ }
+ if (mhi_evid_limits[0] > mhi_evid_limits[1]) {
+ IPAERR("mhi event ring id low limit > high limit\n");
+ return -EFAULT;
+ }
+ ipa_drv_res->mhi_evid_limits[0] = mhi_evid_limits[0];
+ ipa_drv_res->mhi_evid_limits[1] = mhi_evid_limits[1];
+ IPADBG(": mhi-event-ring-id-limits start=%u end=%u\n",
+ mhi_evid_limits[0], mhi_evid_limits[1]);
+ } else {
+ if (elem_num > 0) {
+ IPAERR("Invalid mhi event ring id limits number %d\n",
+ elem_num);
+ return -EINVAL;
+ }
+ IPADBG("use default mhi evt ring id limits start=%u end=%u\n",
+ ipa_drv_res->mhi_evid_limits[0],
+ ipa_drv_res->mhi_evid_limits[1]);
+ }
+
+ elem_num = of_property_count_elems_of_size(pdev->dev.of_node,
"qcom,ipa-tz-unlock-reg", sizeof(u32));
if (elem_num > 0 && elem_num % 2 == 0) {
@@ -5465,7 +5650,7 @@
ipa_tz_unlock_reg[pos++];
ipa_drv_res->ipa_tz_unlock_reg[i].size =
ipa_tz_unlock_reg[pos++];
- IPADBG("tz unlock reg %d: addr 0x%pa size %d\n", i,
+ IPADBG("tz unlock reg %d: addr 0x%pa size %llu\n", i,
&ipa_drv_res->ipa_tz_unlock_reg[i].reg_addr,
ipa_drv_res->ipa_tz_unlock_reg[i].size);
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index a859ff7..59fe07f 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
@@ -995,6 +995,7 @@
msecs_to_jiffies(IPA_CHANNEL_STOP_IN_PROC_TO_MSEC);
int res;
+ IPADBG("entry\n");
if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
ipa3_ctx->ep[clnt_hdl].valid == 0 ||
!stop_in_proc) {
@@ -1041,6 +1042,7 @@
bool stop_in_proc;
struct ipa3_ep_context *ep;
struct ipa_ep_cfg_ctrl ep_cfg_ctrl;
+ IPADBG("entry\n");
if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
ipa3_ctx->ep[clnt_hdl].valid == 0) {
@@ -1157,6 +1159,7 @@
ep->ep_delay_set = false;
}
}
+ IPADBG("exit\n");
return result;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
index f72f41c..1539a0e 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
@@ -17,7 +17,9 @@
#include <linux/stringify.h>
#include "ipa_i.h"
#include "../ipa_rm_i.h"
+#include "ipahal/ipahal_nat.h"
+#define IPA_MAX_ENTRY_STRING_LEN 500
#define IPA_MAX_MSG_LEN 4096
#define IPA_DBG_MAX_RULE_IN_TBL 128
#define IPA_DBG_ACTIVE_CLIENT_BUF_SIZE ((IPA3_ACTIVE_CLIENTS_LOG_LINE_LEN \
@@ -26,17 +28,18 @@
#define IPA_DUMP_STATUS_FIELD(f) \
pr_err(#f "=0x%x\n", status->f)
-const char *ipa3_excp_name[] = {
- __stringify_1(IPA_A5_MUX_HDR_EXCP_RSVD0),
- __stringify_1(IPA_A5_MUX_HDR_EXCP_RSVD1),
- __stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_IHL),
- __stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_REPLICATED),
- __stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_TAG),
- __stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_SW_FLT),
- __stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_NAT),
- __stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_IP),
+#define IPA_READ_ONLY_MODE 0444
+#define IPA_READ_WRITE_MODE 0664
+#define IPA_WRITE_ONLY_MODE 0220
+
+struct ipa3_debugfs_file {
+ const char *name;
+ umode_t mode;
+ void *data;
+ const struct file_operations fops;
};
+
const char *ipa3_event_name[] = {
__stringify(WLAN_CLIENT_CONNECT),
__stringify(WLAN_CLIENT_DISCONNECT),
@@ -87,30 +90,6 @@
};
static struct dentry *dent;
-static struct dentry *dfile_gen_reg;
-static struct dentry *dfile_ep_reg;
-static struct dentry *dfile_keep_awake;
-static struct dentry *dfile_ep_holb;
-static struct dentry *dfile_hdr;
-static struct dentry *dfile_proc_ctx;
-static struct dentry *dfile_ip4_rt;
-static struct dentry *dfile_ip4_rt_hw;
-static struct dentry *dfile_ip6_rt;
-static struct dentry *dfile_ip6_rt_hw;
-static struct dentry *dfile_ip4_flt;
-static struct dentry *dfile_ip4_flt_hw;
-static struct dentry *dfile_ip6_flt;
-static struct dentry *dfile_ip6_flt_hw;
-static struct dentry *dfile_stats;
-static struct dentry *dfile_wstats;
-static struct dentry *dfile_wdi_stats;
-static struct dentry *dfile_ntn_stats;
-static struct dentry *dfile_dbg_cnt;
-static struct dentry *dfile_msg;
-static struct dentry *dfile_ip4_nat;
-static struct dentry *dfile_rm_stats;
-static struct dentry *dfile_status_stats;
-static struct dentry *dfile_active_clients;
static char dbg_buff[IPA_MAX_MSG_LEN];
static char *active_clients_buf;
@@ -1516,250 +1495,367 @@
return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
}
+static int ipa3_read_table(
+ char *table_addr, u32 table_size,
+ char *buff, u32 buff_size,
+ u32 *total_num_entries,
+ u32 *rule_id,
+ enum ipahal_nat_type nat_type)
+{
+ int result;
+ char *entry;
+ size_t entry_size;
+ bool entry_zeroed;
+ u32 i, num_entries = 0, id = *rule_id, pos = 0;
+
+ IPADBG("\n");
+
+ if (table_addr == NULL)
+ return 0;
+
+ result = ipahal_nat_entry_size(nat_type, &entry_size);
+ if (result) {
+ IPAERR("Failed to retrieve size of %s entry\n",
+ ipahal_nat_type_str(nat_type));
+ return 0;
+ }
+
+ for (i = 0, entry = table_addr;
+ i < table_size;
+ ++i, ++id, entry += entry_size) {
+ result = ipahal_nat_is_entry_zeroed(nat_type, entry,
+ &entry_zeroed);
+ if (result) {
+ IPAERR(
+ "Failed to determine whether the %s entry is definitely zero",
+ ipahal_nat_type_str(nat_type));
+ goto bail;
+ }
+ if (entry_zeroed)
+ continue;
+
+ pos += scnprintf(buff + pos, buff_size - pos,
+ "\tEntry_Index=%d\n", id);
+
+ pos += ipahal_nat_stringify_entry(nat_type, entry,
+ buff + pos, buff_size - pos);
+
+ ++num_entries;
+ }
+
+ if (num_entries)
+ pos += scnprintf(buff + pos, buff_size - pos, "\n");
+ else
+ pos += scnprintf(buff + pos, buff_size - pos, "\tEmpty\n\n");
+
+ IPADBG("return\n");
+bail:
+ *rule_id = id;
+ *total_num_entries += num_entries;
+ return pos;
+}
+
+static int ipa3_start_read_memory_device(
+ struct ipa3_nat_ipv6ct_common_mem *dev,
+ char *buff, u32 buff_size,
+ enum ipahal_nat_type nat_type,
+ u32 *num_entries)
+{
+ u32 rule_id = 0, pos = 0;
+
+ IPADBG("\n");
+
+ pos += scnprintf(buff + pos, buff_size - pos, "%s_Table_Size=%d\n",
+ dev->name, dev->table_entries + 1);
+
+ pos += scnprintf(buff + pos, buff_size - pos,
+ "%s_Expansion_Table_Size=%d\n",
+ dev->name, dev->expn_table_entries);
+
+ if (!dev->is_sys_mem)
+ pos += scnprintf(buff + pos, buff_size - pos,
+ "Not supported for local(shared) memory\n");
+
+ pos += scnprintf(buff + pos, buff_size - pos,
+ "\n%s Base Table:\n", dev->name);
+ pos += ipa3_read_table(dev->base_table_addr, dev->table_entries + 1,
+ buff + pos, buff_size - pos, num_entries, &rule_id, nat_type);
+
+ pos += scnprintf(buff + pos, buff_size - pos,
+ "%s Expansion Table:\n", dev->name);
+ pos += ipa3_read_table(
+ dev->expansion_table_addr, dev->expn_table_entries,
+ buff + pos, buff_size - pos,
+ num_entries,
+ &rule_id,
+ nat_type);
+
+ IPADBG("return\n");
+ return pos;
+}
+
+static int ipa3_finish_read_memory_device(
+ struct ipa3_nat_ipv6ct_common_mem *dev,
+ char *buff, u32 buff_size,
+ u32 curr_pos,
+ u32 num_entries)
+{
+ u32 pos = 0;
+
+ IPADBG("\n");
+
+ /*
+ * A real buffer and buff size, so need to use the
+ * real current position
+ */
+ pos += scnprintf(buff + curr_pos, buff_size - curr_pos,
+ "Overall number %s entries: %d\n\n", dev->name, num_entries);
+
+ if (curr_pos + pos >= buff_size - 1)
+ IPAERR(
+ "The %s debug information is larger than the internal buffer, so the read information might be incomplete",
+ dev->name);
+
+ IPADBG("return\n");
+ return pos;
+}
+
+static int ipa3_read_pdn_table(char *buff, u32 buff_size)
+{
+ int i, result;
+ char *pdn_entry;
+ size_t pdn_entry_size;
+ bool entry_zeroed;
+ u32 pos = 0;
+
+ IPADBG("\n");
+
+ result = ipahal_nat_entry_size(IPAHAL_NAT_IPV4_PDN, &pdn_entry_size);
+ if (result) {
+ IPAERR("Failed to retrieve size of PDN entry");
+ return 0;
+ }
+
+ for (i = 0, pdn_entry = ipa3_ctx->nat_mem.pdn_mem.base;
+ i < IPA_MAX_PDN_NUM;
+ ++i, pdn_entry += pdn_entry_size) {
+ result = ipahal_nat_is_entry_zeroed(IPAHAL_NAT_IPV4_PDN,
+ pdn_entry, &entry_zeroed);
+ if (result) {
+ IPAERR(
+ "Failed to determine whether the PDN entry is definitely zero");
+ goto bail;
+ }
+ if (entry_zeroed)
+ continue;
+
+ pos += scnprintf(buff + pos, buff_size - pos, "PDN %d: ", i);
+
+ pos += ipahal_nat_stringify_entry(IPAHAL_NAT_IPV4_PDN,
+ pdn_entry, buff + pos, buff_size - pos);
+ }
+ pos += scnprintf(buff + pos, buff_size - pos, "\n");
+
+ IPADBG("return\n");
+bail:
+ return pos;
+}
+
static ssize_t ipa3_read_nat4(struct file *file,
char __user *ubuf, size_t count,
- loff_t *ppos) {
+ loff_t *ppos)
+{
+ ssize_t ret;
+ char *buff;
+ u32 rule_id = 0, pos = 0, num_entries = 0, index_num_entries = 0;
+ const u32 buff_size = IPA_MAX_MSG_LEN + 2 * IPA_MAX_ENTRY_STRING_LEN * (
+ ipa3_ctx->nat_mem.dev.table_entries + 1 +
+ ipa3_ctx->nat_mem.dev.expn_table_entries);
-#define ENTRY_U32_FIELDS 8
-#define NAT_ENTRY_ENABLE 0x8000
-#define NAT_ENTRY_RST_FIN_BIT 0x4000
-#define BASE_TABLE 0
-#define EXPANSION_TABLE 1
+ IPADBG("\n");
- u32 *base_tbl, *indx_tbl;
- u32 tbl_size, *tmp;
- u32 value, i, j, rule_id;
- u16 enable, tbl_entry, flag;
- u32 no_entrys = 0;
- struct ipa_pdn_entry *pdn_table = ipa3_ctx->nat_mem.pdn_mem.base;
+ buff = kzalloc(buff_size, GFP_KERNEL);
+ if (buff == NULL)
+ return 0;
- mutex_lock(&ipa3_ctx->nat_mem.lock);
- value = ipa3_ctx->nat_mem.public_ip_addr;
- pr_err(
- "Table IP Address:%d.%d.%d.%d\n",
- ((value & 0xFF000000) >> 24),
- ((value & 0x00FF0000) >> 16),
- ((value & 0x0000FF00) >> 8),
- ((value & 0x000000FF)));
-
- if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
- for (i = 0; i < IPA_MAX_PDN_NUM; i++) {
- pr_err(
- "PDN %d: ip 0x%X, src_metadata 0x%X, dst_metadata 0x%X\n",
- i, pdn_table[i].public_ip,
- pdn_table[i].src_metadata,
- pdn_table[i].dst_metadata);
- }
-
- pr_err("Table Size:%d\n",
- ipa3_ctx->nat_mem.size_base_tables);
-
- pr_err("Expansion Table Size:%d\n",
- ipa3_ctx->nat_mem.size_expansion_tables-1);
-
- if (!ipa3_ctx->nat_mem.is_sys_mem)
- pr_err("Not supported for local(shared) memory\n");
-
- /* Print Base tables */
- rule_id = 0;
- for (j = 0; j < 2; j++) {
- if (j == BASE_TABLE) {
- tbl_size = ipa3_ctx->nat_mem.size_base_tables;
- base_tbl = (u32 *)ipa3_ctx->nat_mem.ipv4_rules_addr;
-
- pr_err("\nBase Table:\n");
- } else {
- tbl_size = ipa3_ctx->nat_mem.size_expansion_tables-1;
- base_tbl =
- (u32 *)ipa3_ctx->nat_mem.ipv4_expansion_rules_addr;
-
- pr_err("\nExpansion Base Table:\n");
- }
-
- if (base_tbl != NULL) {
- for (i = 0; i <= tbl_size; i++, rule_id++) {
- tmp = base_tbl;
- value = tmp[4];
- enable = ((value & 0xFFFF0000) >> 16);
-
- if (enable & NAT_ENTRY_ENABLE) {
- no_entrys++;
- pr_err("Rule:%d ", rule_id);
-
- value = *tmp;
- pr_err(
- "Private_IP:%d.%d.%d.%d ",
- ((value & 0xFF000000) >> 24),
- ((value & 0x00FF0000) >> 16),
- ((value & 0x0000FF00) >> 8),
- ((value & 0x000000FF)));
- tmp++;
-
- value = *tmp;
- pr_err(
- "Target_IP:%d.%d.%d.%d ",
- ((value & 0xFF000000) >> 24),
- ((value & 0x00FF0000) >> 16),
- ((value & 0x0000FF00) >> 8),
- ((value & 0x000000FF)));
- tmp++;
-
- value = *tmp;
- pr_err(
- "Next_Index:%d Public_Port:%d ",
- (value & 0x0000FFFF),
- ((value & 0xFFFF0000) >> 16));
- tmp++;
-
- value = *tmp;
- pr_err(
- "Private_Port:%d Target_Port:%d ",
- (value & 0x0000FFFF),
- ((value & 0xFFFF0000) >> 16));
- tmp++;
-
- value = *tmp;
- flag = ((value & 0xFFFF0000) >> 16);
- if (flag & NAT_ENTRY_RST_FIN_BIT) {
- pr_err(
- "IP_CKSM_delta:0x%x Flags:%s ",
- (value & 0x0000FFFF),
- "Direct_To_A5");
- } else {
- pr_err(
- "IP_CKSM_delta:0x%x Flags:%s ",
- (value & 0x0000FFFF),
- "Fwd_to_route");
- }
- tmp++;
-
- value = *tmp;
- pr_err(
- "Time_stamp:0x%x Proto:%d ",
- (value & 0x00FFFFFF),
- ((value & 0xFF000000) >> 24));
- tmp++;
-
- value = *tmp;
- pr_err(
- "Prev_Index:%d Indx_tbl_entry:%d ",
- (value & 0x0000FFFF),
- ((value & 0xFFFF0000) >> 16));
- tmp++;
-
- value = *tmp;
- pr_err(
- "TCP_UDP_cksum_delta:0x%x\n",
- ((value & 0xFFFF0000) >> 16));
- }
-
- base_tbl += ENTRY_U32_FIELDS;
-
- }
- }
+ if (!ipa3_ctx->nat_mem.dev.is_dev_init) {
+ pos += scnprintf(buff + pos, buff_size - pos,
+ "NAT hasn't been initialized or not supported\n");
+ goto ret;
}
+ mutex_lock(&ipa3_ctx->nat_mem.dev.lock);
+
+ if (!ipa3_ctx->nat_mem.dev.is_hw_init) {
+ pos += scnprintf(buff + pos, buff_size - pos,
+ "NAT H/W hasn't been initialized\n");
+ goto bail;
+ }
+
+ pos += scnprintf(buff + pos, buff_size - pos, "\n");
+
+ if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
+ pos += ipa3_read_pdn_table(buff + pos, buff_size - pos);
+ } else {
+ pos += scnprintf(buff + pos, buff_size - pos,
+ "NAT Table IP Address=%pI4h\n\n",
+ &ipa3_ctx->nat_mem.public_ip_addr);
+ }
+
+ pos += ipa3_start_read_memory_device(&ipa3_ctx->nat_mem.dev,
+ buff + pos, buff_size - pos, IPAHAL_NAT_IPV4, &num_entries);
+
/* Print Index tables */
- rule_id = 0;
- for (j = 0; j < 2; j++) {
- if (j == BASE_TABLE) {
- tbl_size = ipa3_ctx->nat_mem.size_base_tables;
- indx_tbl = (u32 *)ipa3_ctx->nat_mem.index_table_addr;
+ pos += scnprintf(buff + pos, buff_size - pos,
+ "ipaNatTable Index Table:\n");
+ pos += ipa3_read_table(
+ ipa3_ctx->nat_mem.index_table_addr,
+ ipa3_ctx->nat_mem.dev.table_entries + 1,
+ buff + pos, buff_size - pos,
+ &index_num_entries,
+ &rule_id,
+ IPAHAL_NAT_IPV4_INDEX);
- pr_err("\nIndex Table:\n");
- } else {
- tbl_size = ipa3_ctx->nat_mem.size_expansion_tables-1;
- indx_tbl =
- (u32 *)ipa3_ctx->nat_mem.index_table_expansion_addr;
+ pos += scnprintf(buff + pos, buff_size - pos,
+ "ipaNatTable Expansion Index Table:\n");
+ pos += ipa3_read_table(
+ ipa3_ctx->nat_mem.index_table_expansion_addr,
+ ipa3_ctx->nat_mem.dev.expn_table_entries,
+ buff + pos, buff_size - pos,
+ &index_num_entries,
+ &rule_id,
+ IPAHAL_NAT_IPV4_INDEX);
- pr_err("\nExpansion Index Table:\n");
- }
+ if (num_entries != index_num_entries)
+ IPAERR(
+ "The NAT table number of entries %d is different from index table number of entries %d\n",
+ num_entries, index_num_entries);
- if (indx_tbl != NULL) {
- for (i = 0; i <= tbl_size; i++, rule_id++) {
- tmp = indx_tbl;
- value = *tmp;
- tbl_entry = (value & 0x0000FFFF);
+ pos += ipa3_finish_read_memory_device(&ipa3_ctx->nat_mem.dev,
+ buff, buff_size, pos, num_entries);
- if (tbl_entry) {
- pr_err("Rule:%d ", rule_id);
+ IPADBG("return\n");
+bail:
+ mutex_unlock(&ipa3_ctx->nat_mem.dev.lock);
+ret:
+ ret = simple_read_from_buffer(ubuf, count, ppos, buff, pos);
+ kfree(buff);
+ return ret;
+}
- value = *tmp;
- pr_err(
- "Table_Entry:%d Next_Index:%d\n",
- tbl_entry,
- ((value & 0xFFFF0000) >> 16));
- }
+static ssize_t ipa3_read_ipv6ct(struct file *file,
+ char __user *ubuf, size_t count,
+ loff_t *ppos) {
+ ssize_t ret;
+ char *buff;
+ u32 pos = 0, num_entries = 0;
+ const u32 buff_size = IPA_MAX_MSG_LEN + IPA_MAX_ENTRY_STRING_LEN * (
+ ipa3_ctx->nat_mem.dev.table_entries + 1 +
+ ipa3_ctx->nat_mem.dev.expn_table_entries);
- indx_tbl++;
- }
- }
+ IPADBG("\n");
+
+ buff = kzalloc(buff_size, GFP_KERNEL);
+ if (buff == NULL)
+ return 0;
+
+ pos += scnprintf(buff + pos, buff_size - pos, "\n");
+
+ if (!ipa3_ctx->ipv6ct_mem.dev.is_dev_init) {
+ pos += scnprintf(buff + pos, buff_size - pos,
+ "IPv6 connection tracking hasn't been initialized or not supported\n");
+ goto ret;
}
- pr_err("Current No. Nat Entries: %d\n", no_entrys);
- mutex_unlock(&ipa3_ctx->nat_mem.lock);
- return 0;
+ mutex_lock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+
+ if (!ipa3_ctx->ipv6ct_mem.dev.is_hw_init) {
+ pos += scnprintf(buff + pos, buff_size - pos,
+ "IPv6 connection tracking H/W hasn't been initialized\n");
+ goto bail;
+ }
+
+ pos += ipa3_start_read_memory_device(&ipa3_ctx->ipv6ct_mem.dev,
+ buff + pos, buff_size - pos, IPAHAL_NAT_IPV6CT, &num_entries);
+ pos += ipa3_finish_read_memory_device(&ipa3_ctx->ipv6ct_mem.dev,
+ buff, buff_size, pos, num_entries);
+
+ IPADBG("return\n");
+bail:
+ mutex_unlock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+ret:
+ ret = simple_read_from_buffer(ubuf, count, ppos, buff, pos);
+ kfree(buff);
+ return ret;
}
static ssize_t ipa3_rm_read_stats(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
- int result, nbytes, cnt = 0;
+ int result, cnt = 0;
/* deprecate if IPA PM is used */
- if (ipa3_ctx->use_ipa_pm)
- return 0;
+ if (ipa3_ctx->use_ipa_pm) {
+ cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
+ "IPA RM is disabled\n");
+ goto ret;
+ }
result = ipa_rm_stat(dbg_buff, IPA_MAX_MSG_LEN);
if (result < 0) {
- nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
+ cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
"Error in printing RM stat %d\n", result);
- cnt += nbytes;
- } else
- cnt += result;
-
+ goto ret;
+ }
+ cnt += result;
+ret:
return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
}
static ssize_t ipa3_pm_read_stats(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
- int result, nbytes, cnt = 0;
+ int result, cnt = 0;
- if (!ipa3_ctx->use_ipa_pm)
- return 0;
+ if (!ipa3_ctx->use_ipa_pm) {
+ cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
+ "IPA PM is disabled\n");
+ goto ret;
+ }
result = ipa_pm_stat(dbg_buff, IPA_MAX_MSG_LEN);
if (result < 0) {
- nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
+ cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
"Error in printing PM stat %d\n", result);
- cnt += nbytes;
- } else
- cnt += result;
-
+ goto ret;
+ }
+ cnt += result;
+ret:
return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
}
static ssize_t ipa3_pm_ex_read_stats(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
- int result, nbytes, cnt = 0;
+ int result, cnt = 0;
- if (!ipa3_ctx->use_ipa_pm)
- return 0;
+ if (!ipa3_ctx->use_ipa_pm) {
+ cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
+ "IPA PM is disabled\n");
+ goto ret;
+ }
result = ipa_pm_exceptions_stat(dbg_buff, IPA_MAX_MSG_LEN);
if (result < 0) {
- nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
+ cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
"Error in printing PM stat %d\n", result);
- cnt += nbytes;
- } else
- cnt += result;
-
+ goto ret;
+ }
+ cnt += result;
+ret:
return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
}
-
static void ipa_dump_status(struct ipahal_pkt_status *status)
{
IPA_DUMP_STATUS_FIELD(status_opcode);
@@ -1901,113 +1997,139 @@
return count;
}
-const struct file_operations ipa3_gen_reg_ops = {
- .read = ipa3_read_gen_reg,
-};
-
-const struct file_operations ipa3_ep_reg_ops = {
- .read = ipa3_read_ep_reg,
- .write = ipa3_write_ep_reg,
-};
-
-const struct file_operations ipa3_keep_awake_ops = {
- .read = ipa3_read_keep_awake,
- .write = ipa3_write_keep_awake,
-};
-
-const struct file_operations ipa3_ep_holb_ops = {
- .write = ipa3_write_ep_holb,
-};
-
-const struct file_operations ipa3_hdr_ops = {
- .read = ipa3_read_hdr,
-};
-
-const struct file_operations ipa3_rt_ops = {
- .read = ipa3_read_rt,
- .open = ipa3_open_dbg,
-};
-
-const struct file_operations ipa3_rt_hw_ops = {
- .read = ipa3_read_rt_hw,
- .open = ipa3_open_dbg,
-};
-
-const struct file_operations ipa3_proc_ctx_ops = {
- .read = ipa3_read_proc_ctx,
-};
-
-const struct file_operations ipa3_flt_ops = {
- .read = ipa3_read_flt,
- .open = ipa3_open_dbg,
-};
-
-const struct file_operations ipa3_flt_hw_ops = {
- .read = ipa3_read_flt_hw,
- .open = ipa3_open_dbg,
-};
-
-const struct file_operations ipa3_stats_ops = {
- .read = ipa3_read_stats,
-};
-
-const struct file_operations ipa3_wstats_ops = {
- .read = ipa3_read_wstats,
-};
-
-const struct file_operations ipa3_wdi_ops = {
- .read = ipa3_read_wdi,
-};
-
-const struct file_operations ipa3_ntn_ops = {
- .read = ipa3_read_ntn,
-};
-
-const struct file_operations ipa3_msg_ops = {
- .read = ipa3_read_msg,
-};
-
-const struct file_operations ipa3_dbg_cnt_ops = {
- .read = ipa3_read_dbg_cnt,
- .write = ipa3_write_dbg_cnt,
-};
-
-const struct file_operations ipa3_status_stats_ops = {
- .read = ipa_status_stats_read,
-};
-
-const struct file_operations ipa3_nat4_ops = {
- .read = ipa3_read_nat4,
-};
-
-const struct file_operations ipa3_rm_stats = {
- .read = ipa3_rm_read_stats,
-};
-
-static const struct file_operations ipa3_pm_stats = {
- .read = ipa3_pm_read_stats,
-};
-
-
-static const struct file_operations ipa3_pm_ex_stats = {
- .read = ipa3_pm_ex_read_stats,
-};
-
-const struct file_operations ipa3_active_clients = {
- .read = ipa3_print_active_clients_log,
- .write = ipa3_clear_active_clients_log,
-};
-
-const struct file_operations ipa3_ipc_low_ops = {
- .write = ipa3_enable_ipc_low,
+static const struct ipa3_debugfs_file debugfs_files[] = {
+ {
+ "gen_reg", IPA_READ_ONLY_MODE, NULL, {
+ .read = ipa3_read_gen_reg
+ }
+ }, {
+ "active_clients", IPA_READ_WRITE_MODE, NULL, {
+ .read = ipa3_print_active_clients_log,
+ .write = ipa3_clear_active_clients_log
+ }
+ }, {
+ "ep_reg", IPA_READ_WRITE_MODE, NULL, {
+ .read = ipa3_read_ep_reg,
+ .write = ipa3_write_ep_reg,
+ }
+ }, {
+ "keep_awake", IPA_READ_WRITE_MODE, NULL, {
+ .read = ipa3_read_keep_awake,
+ .write = ipa3_write_keep_awake,
+ }
+ }, {
+ "holb", IPA_WRITE_ONLY_MODE, NULL, {
+ .write = ipa3_write_ep_holb,
+ }
+ }, {
+ "hdr", IPA_READ_ONLY_MODE, NULL, {
+ .read = ipa3_read_hdr,
+ }
+ }, {
+ "proc_ctx", IPA_READ_ONLY_MODE, NULL, {
+ .read = ipa3_read_proc_ctx,
+ }
+ }, {
+ "ip4_rt", IPA_READ_ONLY_MODE, (void *)IPA_IP_v4, {
+ .read = ipa3_read_rt,
+ .open = ipa3_open_dbg,
+ }
+ }, {
+ "ip4_rt_hw", IPA_READ_ONLY_MODE, (void *)IPA_IP_v4, {
+ .read = ipa3_read_rt_hw,
+ .open = ipa3_open_dbg,
+ }
+ }, {
+ "ip6_rt", IPA_READ_ONLY_MODE, (void *)IPA_IP_v6, {
+ .read = ipa3_read_rt,
+ .open = ipa3_open_dbg,
+ }
+ }, {
+ "ip6_rt_hw", IPA_READ_ONLY_MODE, (void *)IPA_IP_v6, {
+ .read = ipa3_read_rt_hw,
+ .open = ipa3_open_dbg,
+ }
+ }, {
+ "ip4_flt", IPA_READ_ONLY_MODE, (void *)IPA_IP_v4, {
+ .read = ipa3_read_flt,
+ .open = ipa3_open_dbg,
+ }
+ }, {
+ "ip4_flt_hw", IPA_READ_ONLY_MODE, (void *)IPA_IP_v4, {
+ .read = ipa3_read_flt_hw,
+ .open = ipa3_open_dbg,
+ }
+ }, {
+ "ip6_flt", IPA_READ_ONLY_MODE, (void *)IPA_IP_v6, {
+ .read = ipa3_read_flt,
+ .open = ipa3_open_dbg,
+ }
+ }, {
+ "ip6_flt_hw", IPA_READ_ONLY_MODE, (void *)IPA_IP_v6, {
+ .read = ipa3_read_flt_hw,
+ .open = ipa3_open_dbg,
+ }
+ }, {
+ "stats", IPA_READ_ONLY_MODE, NULL, {
+ .read = ipa3_read_stats,
+ }
+ }, {
+ "wstats", IPA_READ_ONLY_MODE, NULL, {
+ .read = ipa3_read_wstats,
+ }
+ }, {
+ "wdi", IPA_READ_ONLY_MODE, NULL, {
+ .read = ipa3_read_wdi,
+ }
+ }, {
+ "ntn", IPA_READ_ONLY_MODE, NULL, {
+ .read = ipa3_read_ntn,
+ }
+ }, {
+ "dbg_cnt", IPA_READ_WRITE_MODE, NULL, {
+ .read = ipa3_read_dbg_cnt,
+ .write = ipa3_write_dbg_cnt,
+ }
+ }, {
+ "msg", IPA_READ_ONLY_MODE, NULL, {
+ .read = ipa3_read_msg,
+ }
+ }, {
+ "ip4_nat", IPA_READ_ONLY_MODE, NULL, {
+ .read = ipa3_read_nat4,
+ }
+ }, {
+ "ipv6ct", IPA_READ_ONLY_MODE, NULL, {
+ .read = ipa3_read_ipv6ct,
+ }
+ }, {
+ "rm_stats", IPA_READ_ONLY_MODE, NULL, {
+ .read = ipa3_rm_read_stats,
+ }
+ }, {
+ "pm_stats", IPA_READ_ONLY_MODE, NULL, {
+ .read = ipa3_pm_read_stats,
+ }
+ }, {
+ "pm_ex_stats", IPA_READ_ONLY_MODE, NULL, {
+ .read = ipa3_pm_ex_read_stats,
+ }
+ }, {
+ "status_stats", IPA_READ_ONLY_MODE, NULL, {
+ .read = ipa_status_stats_read,
+ }
+ }, {
+ "enable_low_prio_print", IPA_WRITE_ONLY_MODE, NULL, {
+ .write = ipa3_enable_ipc_low,
+ }
+ }
};
void ipa3_debugfs_init(void)
{
- const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH;
- const mode_t read_write_mode = S_IRUSR | S_IRGRP | S_IROTH |
- S_IWUSR | S_IWGRP;
- const mode_t write_only_mode = S_IWUSR | S_IWGRP;
+ const size_t debugfs_files_num =
+ sizeof(debugfs_files) / sizeof(struct ipa3_debugfs_file);
+ size_t i;
struct dentry *file;
dent = debugfs_create_dir("ipa", 0);
@@ -2016,26 +2138,24 @@
return;
}
- file = debugfs_create_u32("hw_type", read_only_mode,
- dent, &ipa3_ctx->ipa_hw_type);
+ file = debugfs_create_u32("hw_type", IPA_READ_ONLY_MODE,
+ dent, &ipa3_ctx->ipa_hw_type);
if (!file) {
IPAERR("could not create hw_type file\n");
goto fail;
}
- dfile_gen_reg = debugfs_create_file("gen_reg", read_only_mode, dent, 0,
- &ipa3_gen_reg_ops);
- if (!dfile_gen_reg || IS_ERR(dfile_gen_reg)) {
- IPAERR("fail to create file for debug_fs gen_reg\n");
- goto fail;
- }
+ for (i = 0; i < debugfs_files_num; ++i) {
+ const struct ipa3_debugfs_file *curr = &debugfs_files[i];
- dfile_active_clients = debugfs_create_file("active_clients",
- read_write_mode, dent, 0, &ipa3_active_clients);
- if (!dfile_active_clients || IS_ERR(dfile_active_clients)) {
- IPAERR("fail to create file for debug_fs active_clients\n");
- goto fail;
+ file = debugfs_create_file(curr->name, curr->mode, dent,
+ curr->data, &curr->fops);
+ if (!file || IS_ERR(file)) {
+ IPAERR("fail to create file for debug_fs %s\n",
+ curr->name);
+ goto fail;
+ }
}
active_clients_buf = NULL;
@@ -2044,177 +2164,7 @@
if (active_clients_buf == NULL)
IPAERR("fail to allocate active clients memory buffer");
- dfile_ep_reg = debugfs_create_file("ep_reg", read_write_mode, dent, 0,
- &ipa3_ep_reg_ops);
- if (!dfile_ep_reg || IS_ERR(dfile_ep_reg)) {
- IPAERR("fail to create file for debug_fs ep_reg\n");
- goto fail;
- }
-
- dfile_keep_awake = debugfs_create_file("keep_awake", read_write_mode,
- dent, 0, &ipa3_keep_awake_ops);
- if (!dfile_keep_awake || IS_ERR(dfile_keep_awake)) {
- IPAERR("fail to create file for debug_fs dfile_keep_awake\n");
- goto fail;
- }
-
- dfile_ep_holb = debugfs_create_file("holb", write_only_mode, dent,
- 0, &ipa3_ep_holb_ops);
- if (!dfile_ep_holb || IS_ERR(dfile_ep_holb)) {
- IPAERR("fail to create file for debug_fs dfile_ep_hol_en\n");
- goto fail;
- }
-
- dfile_hdr = debugfs_create_file("hdr", read_only_mode, dent, 0,
- &ipa3_hdr_ops);
- if (!dfile_hdr || IS_ERR(dfile_hdr)) {
- IPAERR("fail to create file for debug_fs hdr\n");
- goto fail;
- }
-
- dfile_proc_ctx = debugfs_create_file("proc_ctx", read_only_mode, dent,
- 0, &ipa3_proc_ctx_ops);
- if (!dfile_hdr || IS_ERR(dfile_hdr)) {
- IPAERR("fail to create file for debug_fs proc_ctx\n");
- goto fail;
- }
-
- dfile_ip4_rt = debugfs_create_file("ip4_rt", read_only_mode, dent,
- (void *)IPA_IP_v4, &ipa3_rt_ops);
- if (!dfile_ip4_rt || IS_ERR(dfile_ip4_rt)) {
- IPAERR("fail to create file for debug_fs ip4 rt\n");
- goto fail;
- }
-
- dfile_ip4_rt_hw = debugfs_create_file("ip4_rt_hw", read_only_mode, dent,
- (void *)IPA_IP_v4, &ipa3_rt_hw_ops);
- if (!dfile_ip4_rt_hw || IS_ERR(dfile_ip4_rt_hw)) {
- IPAERR("fail to create file for debug_fs ip4 rt hw\n");
- goto fail;
- }
-
- dfile_ip6_rt = debugfs_create_file("ip6_rt", read_only_mode, dent,
- (void *)IPA_IP_v6, &ipa3_rt_ops);
- if (!dfile_ip6_rt || IS_ERR(dfile_ip6_rt)) {
- IPAERR("fail to create file for debug_fs ip6:w rt\n");
- goto fail;
- }
-
- dfile_ip6_rt_hw = debugfs_create_file("ip6_rt_hw", read_only_mode, dent,
- (void *)IPA_IP_v6, &ipa3_rt_hw_ops);
- if (!dfile_ip6_rt_hw || IS_ERR(dfile_ip6_rt_hw)) {
- IPAERR("fail to create file for debug_fs ip6 rt hw\n");
- goto fail;
- }
-
- dfile_ip4_flt = debugfs_create_file("ip4_flt", read_only_mode, dent,
- (void *)IPA_IP_v4, &ipa3_flt_ops);
- if (!dfile_ip4_flt || IS_ERR(dfile_ip4_flt)) {
- IPAERR("fail to create file for debug_fs ip4 flt\n");
- goto fail;
- }
-
- dfile_ip4_flt_hw = debugfs_create_file("ip4_flt_hw", read_only_mode,
- dent, (void *)IPA_IP_v4, &ipa3_flt_hw_ops);
- if (!dfile_ip4_flt_hw || IS_ERR(dfile_ip4_flt_hw)) {
- IPAERR("fail to create file for debug_fs ip4 flt\n");
- goto fail;
- }
-
- dfile_ip6_flt = debugfs_create_file("ip6_flt", read_only_mode, dent,
- (void *)IPA_IP_v6, &ipa3_flt_ops);
- if (!dfile_ip6_flt || IS_ERR(dfile_ip6_flt)) {
- IPAERR("fail to create file for debug_fs ip6 flt\n");
- goto fail;
- }
-
- dfile_ip6_flt_hw = debugfs_create_file("ip6_flt_hw", read_only_mode,
- dent, (void *)IPA_IP_v6, &ipa3_flt_hw_ops);
- if (!dfile_ip6_flt_hw || IS_ERR(dfile_ip6_flt_hw)) {
- IPAERR("fail to create file for debug_fs ip6 flt\n");
- goto fail;
- }
-
- dfile_stats = debugfs_create_file("stats", read_only_mode, dent, 0,
- &ipa3_stats_ops);
- if (!dfile_stats || IS_ERR(dfile_stats)) {
- IPAERR("fail to create file for debug_fs stats\n");
- goto fail;
- }
-
- dfile_wstats = debugfs_create_file("wstats", read_only_mode,
- dent, 0, &ipa3_wstats_ops);
- if (!dfile_wstats || IS_ERR(dfile_wstats)) {
- IPAERR("fail to create file for debug_fs wstats\n");
- goto fail;
- }
-
- dfile_wdi_stats = debugfs_create_file("wdi", read_only_mode, dent, 0,
- &ipa3_wdi_ops);
- if (!dfile_wdi_stats || IS_ERR(dfile_wdi_stats)) {
- IPAERR("fail to create file for debug_fs wdi stats\n");
- goto fail;
- }
-
- dfile_ntn_stats = debugfs_create_file("ntn", read_only_mode, dent, 0,
- &ipa3_ntn_ops);
- if (!dfile_ntn_stats || IS_ERR(dfile_ntn_stats)) {
- IPAERR("fail to create file for debug_fs ntn stats\n");
- goto fail;
- }
-
- dfile_dbg_cnt = debugfs_create_file("dbg_cnt", read_write_mode, dent, 0,
- &ipa3_dbg_cnt_ops);
- if (!dfile_dbg_cnt || IS_ERR(dfile_dbg_cnt)) {
- IPAERR("fail to create file for debug_fs dbg_cnt\n");
- goto fail;
- }
-
- dfile_msg = debugfs_create_file("msg", read_only_mode, dent, 0,
- &ipa3_msg_ops);
- if (!dfile_msg || IS_ERR(dfile_msg)) {
- IPAERR("fail to create file for debug_fs msg\n");
- goto fail;
- }
-
- dfile_ip4_nat = debugfs_create_file("ip4_nat", read_only_mode, dent,
- 0, &ipa3_nat4_ops);
- if (!dfile_ip4_nat || IS_ERR(dfile_ip4_nat)) {
- IPAERR("fail to create file for debug_fs ip4 nat\n");
- goto fail;
- }
-
- if (ipa3_ctx->use_ipa_pm) {
- file = dfile_rm_stats = debugfs_create_file("pm_stats",
- read_only_mode, dent, NULL, &ipa3_pm_stats);
- if (!file || IS_ERR(file)) {
- IPAERR("fail to create file for debug_fs pm_stats\n");
- goto fail;
- }
-
- file = dfile_rm_stats = debugfs_create_file("pm_ex_stats",
- read_only_mode, dent, NULL, &ipa3_pm_ex_stats);
- if (!file || IS_ERR(file)) {
- IPAERR("fail to create file for debugfs pm_ex_stats\n");
- goto fail;
- }
- } else {
- dfile_rm_stats = debugfs_create_file("rm_stats",
- read_only_mode, dent, NULL, &ipa3_rm_stats);
- if (!dfile_rm_stats || IS_ERR(dfile_rm_stats)) {
- IPAERR("fail to create file for debug_fs rm_stats\n");
- goto fail;
- }
- }
-
- dfile_status_stats = debugfs_create_file("status_stats",
- read_only_mode, dent, 0, &ipa3_status_stats_ops);
- if (!dfile_status_stats || IS_ERR(dfile_status_stats)) {
- IPAERR("fail to create file for debug_fs status_stats\n");
- goto fail;
- }
-
- file = debugfs_create_u32("enable_clock_scaling", read_write_mode,
+ file = debugfs_create_u32("enable_clock_scaling", IPA_READ_WRITE_MODE,
dent, &ipa3_ctx->enable_clock_scaling);
if (!file) {
IPAERR("could not create enable_clock_scaling file\n");
@@ -2222,7 +2172,7 @@
}
file = debugfs_create_u32("clock_scaling_bw_threshold_nominal_mbps",
- read_write_mode, dent,
+ IPA_READ_WRITE_MODE, dent,
&ipa3_ctx->ctrl->clock_scaling_bw_threshold_nominal);
if (!file) {
IPAERR("could not create bw_threshold_nominal_mbps\n");
@@ -2230,20 +2180,13 @@
}
file = debugfs_create_u32("clock_scaling_bw_threshold_turbo_mbps",
- read_write_mode, dent,
- &ipa3_ctx->ctrl->clock_scaling_bw_threshold_turbo);
+ IPA_READ_WRITE_MODE, dent,
+ &ipa3_ctx->ctrl->clock_scaling_bw_threshold_turbo);
if (!file) {
IPAERR("could not create bw_threshold_turbo_mbps\n");
goto fail;
}
- file = debugfs_create_file("enable_low_prio_print", write_only_mode,
- dent, 0, &ipa3_ipc_low_ops);
- if (!file) {
- IPAERR("could not create enable_low_prio_print file\n");
- goto fail;
- }
-
ipa_debugfs_init_stats(dent);
return;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
index 5d1bbe7..6a89f49 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
@@ -385,19 +385,15 @@
* payload pointers buffers for headers and bodies of flt structure
* as well as place for flush imm.
* @ipt: the ip address family type
+ * @entries: the number of entries
* @desc: [OUT] descriptor buffer
* @cmd: [OUT] imm commands payload pointers buffer
*
* Return: 0 on success, negative on failure
*/
-static int ipa_flt_alloc_cmd_buffers(enum ipa_ip_type ip,
+static int ipa_flt_alloc_cmd_buffers(enum ipa_ip_type ip, u16 entries,
struct ipa3_desc **desc, struct ipahal_imm_cmd_pyld ***cmd_pyld)
{
- u16 entries;
-
- /* +3: 2 for bodies (hashable and non-hashable) and 1 for flushing */
- entries = (ipa3_ctx->ep_flt_num) * 2 + 3;
-
*desc = kcalloc(entries, sizeof(**desc), GFP_ATOMIC);
if (*desc == NULL) {
IPAERR("fail to alloc desc blob ip %d\n", ip);
@@ -473,6 +469,7 @@
struct ipahal_reg_valmask valmask;
u32 tbl_hdr_width;
struct ipa3_flt_tbl *tbl;
+ u16 entries;
tbl_hdr_width = ipahal_get_hw_tbl_hdr_width();
memset(&alloc_params, 0, sizeof(alloc_params));
@@ -549,7 +546,10 @@
goto fail_size_valid;
}
- if (ipa_flt_alloc_cmd_buffers(ip, &desc, &cmd_pyld)) {
+ /* +3: 2 for bodies (hashable and non-hashable) and 1 for flushing */
+ entries = (ipa3_ctx->ep_flt_num) * 2 + 3;
+
+ if (ipa_flt_alloc_cmd_buffers(ip, entries, &desc, &cmd_pyld)) {
rc = -ENOMEM;
goto fail_size_valid;
}
@@ -573,11 +573,8 @@
rc = -EFAULT;
goto fail_reg_write_construct;
}
- desc[0].opcode = cmd_pyld[0]->opcode;
- desc[0].pyld = cmd_pyld[0]->data;
- desc[0].len = cmd_pyld[0]->len;
- desc[0].type = IPA_IMM_CMD_DESC;
- num_cmd++;
+ ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+ ++num_cmd;
hdr_idx = 0;
for (i = 0; i < ipa3_ctx->ipa_num_pipes; i++) {
@@ -591,6 +588,13 @@
continue;
}
+ if (num_cmd + 1 >= entries) {
+ IPAERR("number of commands is out of range: IP = %d\n",
+ ip);
+ rc = -ENOBUFS;
+ goto fail_imm_cmd_construct;
+ }
+
IPADBG_LOW("Prepare imm cmd for hdr at index %d for pipe %d\n",
hdr_idx, i);
@@ -607,12 +611,11 @@
if (!cmd_pyld[num_cmd]) {
IPAERR("fail construct dma_shared_mem cmd: IP = %d\n",
ip);
+ rc = -ENOMEM;
goto fail_imm_cmd_construct;
}
- desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
- desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
- desc[num_cmd].len = cmd_pyld[num_cmd]->len;
- desc[num_cmd++].type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+ ++num_cmd;
mem_cmd.is_read = false;
mem_cmd.skip_pipeline_clear = false;
@@ -627,17 +630,23 @@
if (!cmd_pyld[num_cmd]) {
IPAERR("fail construct dma_shared_mem cmd: IP = %d\n",
ip);
+ rc = -ENOMEM;
goto fail_imm_cmd_construct;
}
- desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
- desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
- desc[num_cmd].len = cmd_pyld[num_cmd]->len;
- desc[num_cmd++].type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+ ++num_cmd;
- hdr_idx++;
+ ++hdr_idx;
}
if (lcl_nhash) {
+ if (num_cmd >= entries) {
+ IPAERR("number of commands is out of range: IP = %d\n",
+ ip);
+ rc = -ENOBUFS;
+ goto fail_imm_cmd_construct;
+ }
+
mem_cmd.is_read = false;
mem_cmd.skip_pipeline_clear = false;
mem_cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
@@ -649,14 +658,20 @@
if (!cmd_pyld[num_cmd]) {
IPAERR("fail construct dma_shared_mem cmd: IP = %d\n",
ip);
+ rc = -ENOMEM;
goto fail_imm_cmd_construct;
}
- desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
- desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
- desc[num_cmd].len = cmd_pyld[num_cmd]->len;
- desc[num_cmd++].type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+ ++num_cmd;
}
if (lcl_hash) {
+ if (num_cmd >= entries) {
+ IPAERR("number of commands is out of range: IP = %d\n",
+ ip);
+ rc = -ENOBUFS;
+ goto fail_imm_cmd_construct;
+ }
+
mem_cmd.is_read = false;
mem_cmd.skip_pipeline_clear = false;
mem_cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
@@ -668,12 +683,11 @@
if (!cmd_pyld[num_cmd]) {
IPAERR("fail construct dma_shared_mem cmd: IP = %d\n",
ip);
+ rc = -ENOMEM;
goto fail_imm_cmd_construct;
}
- desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
- desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
- desc[num_cmd].len = cmd_pyld[num_cmd]->len;
- desc[num_cmd++].type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+ ++num_cmd;
}
if (ipa3_send_cmd(num_cmd, desc)) {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
index 34624c0..a89bd78 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
@@ -199,9 +199,6 @@
IPAERR("fail construct dma_shared_mem cmd\n");
goto end;
}
- desc[0].opcode = hdr_cmd_pyld->opcode;
- desc[0].pyld = hdr_cmd_pyld->data;
- desc[0].len = hdr_cmd_pyld->len;
}
} else {
if (hdr_mem.size > IPA_MEM_PART(apps_hdr_size_ddr)) {
@@ -217,12 +214,9 @@
IPAERR("fail construct hdr_init_system cmd\n");
goto end;
}
- desc[0].opcode = hdr_cmd_pyld->opcode;
- desc[0].pyld = hdr_cmd_pyld->data;
- desc[0].len = hdr_cmd_pyld->len;
}
}
- desc[0].type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&desc[0], hdr_cmd_pyld);
IPA_DUMP_BUFF(hdr_mem.base, hdr_mem.phys_base, hdr_mem.size);
proc_ctx_size = IPA_MEM_PART(apps_hdr_proc_ctx_size);
@@ -249,9 +243,6 @@
IPAERR("fail construct dma_shared_mem cmd\n");
goto end;
}
- desc[1].opcode = ctx_cmd_pyld->opcode;
- desc[1].pyld = ctx_cmd_pyld->data;
- desc[1].len = ctx_cmd_pyld->len;
}
} else {
proc_ctx_size_ddr = IPA_MEM_PART(apps_hdr_proc_ctx_size_ddr);
@@ -277,12 +268,9 @@
IPAERR("fail construct register_write cmd\n");
goto end;
}
- desc[1].opcode = ctx_cmd_pyld->opcode;
- desc[1].pyld = ctx_cmd_pyld->data;
- desc[1].len = ctx_cmd_pyld->len;
}
}
- desc[1].type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&desc[1], ctx_cmd_pyld);
IPA_DUMP_BUFF(ctx_mem.base, ctx_mem.phys_base, ctx_mem.size);
if (ipa3_send_cmd(2, desc))
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hw_defs.h b/drivers/platform/msm/ipa/ipa_v3/ipa_hw_defs.h
deleted file mode 100644
index dff3a3f..0000000
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_hw_defs.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef _IPA_HW_DEFS_H
-#define _IPA_HW_DEFS_H
-#include <linux/bitops.h>
-
-/* This header defines various HW related data types */
-
-
-#define IPA_A5_MUX_HDR_EXCP_FLAG_IP BIT(7)
-#define IPA_A5_MUX_HDR_EXCP_FLAG_NAT BIT(6)
-#define IPA_A5_MUX_HDR_EXCP_FLAG_SW_FLT BIT(5)
-#define IPA_A5_MUX_HDR_EXCP_FLAG_TAG BIT(4)
-#define IPA_A5_MUX_HDR_EXCP_FLAG_REPLICATED BIT(3)
-#define IPA_A5_MUX_HDR_EXCP_FLAG_IHL BIT(2)
-
-/**
- * struct ipa3_a5_mux_hdr - A5 MUX header definition
- * @interface_id: interface ID
- * @src_pipe_index: source pipe index
- * @flags: flags
- * @metadata: metadata
- *
- * A5 MUX header is in BE, A5 runs in LE. This struct definition
- * allows A5 SW to correctly parse the header
- */
-struct ipa3_a5_mux_hdr {
- u16 interface_id;
- u8 src_pipe_index;
- u8 flags;
- u32 metadata;
-};
-
-#endif /* _IPA_HW_DEFS_H */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 29fe24e..f764a61 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -27,7 +27,6 @@
#include <linux/iommu.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
-#include "ipa_hw_defs.h"
#include "ipa_qmi_service.h"
#include "../ipa_api.h"
#include "ipahal/ipahal_reg.h"
@@ -38,8 +37,9 @@
#include "ipa_uc_offload_i.h"
#include "ipa_pm.h"
+#define IPA_DEV_NAME_MAX_LEN 15
#define DRV_NAME "ipa"
-#define NAT_DEV_NAME "ipaNatTable"
+
#define IPA_COOKIE 0x57831603
#define IPA_RT_RULE_COOKIE 0x57831604
#define IPA_RT_TBL_COOKIE 0x57831605
@@ -125,6 +125,8 @@
#define IPA_RAM_NAT_OFST 0
#define IPA_RAM_NAT_SIZE 0
+#define IPA_RAM_IPV6CT_OFST 0
+#define IPA_RAM_IPV6CT_SIZE 0
#define IPA_MEM_CANARY_VAL 0xdeadbeef
#define IPA_STATS
@@ -780,57 +782,101 @@
u32 dst_metadata;
u32 resrvd;
};
+
/**
- * struct ipa3_nat_mem - IPA NAT memory description
+ * struct ipa3_nat_ipv6ct_tmp_mem - NAT/IPv6CT temporary memory
+ *
+ * In case NAT/IPv6CT table are destroyed the HW is provided with the
+ * temporary memory
+ *
+ * @vaddr: the address of the temporary memory
+ * @dma_handle: the handle of the temporary memory
+ */
+struct ipa3_nat_ipv6ct_tmp_mem {
+ void *vaddr;
+ dma_addr_t dma_handle;
+};
+
+/**
+ * struct ipa3_nat_ipv6ct_common_mem - IPA NAT/IPv6CT memory device
* @class: pointer to the struct class
* @dev: the dev_t of the device
* @cdev: cdev of the device
* @dev_num: device number
- * @vaddr: virtual address
- * @dma_handle: DMA handle
- * @size: NAT memory size
- * @is_mapped: flag indicating if NAT memory is mapped
- * @is_sys_mem: flag indicating if NAT memory is sys memory
- * @is_dev_init: flag indicating if NAT device is initialized
- * @lock: NAT memory mutex
- * @nat_base_address: nat table virutal address
- * @ipv4_rules_addr: base nat table address
- * @ipv4_expansion_rules_addr: expansion table address
- * @index_table_addr: index table address
- * @index_table_expansion_addr: index expansion table address
- * @size_base_tables: base table size
- * @size_expansion_tables: expansion table size
- * @public_ip_addr: ip address of nat table
- * @pdn_mem: pdn config table SW cache memory structure
+ * @vaddr: the virtual address in the system memory
+ * @dma_handle: the system memory DMA handle
+ * @phys_mem_size: the physical size in the shared memory
+ * @smem_offset: the offset in the shared memory
+ * @size: memory size
+ * @is_mapped: flag indicating if memory is mapped
+ * @is_sys_mem: flag indicating if memory is sys memory
+ * @is_mem_allocated: flag indicating if the memory is allocated
+ * @is_hw_init: flag indicating if the corresponding HW is initialized
+ * @is_dev_init: flag indicating if device is initialized
+ * @lock: memory mutex
+ * @base_address: table virtual address
+ * @base_table_addr: base table address
+ * @expansion_table_addr: expansion table address
+ * @table_entries: num of entries in the base table
+ * @expn_table_entries: num of entries in the expansion table
+ * @tmp_mem: temporary memory used to always provide HW with a legal memory
+ * @name: the device name
*/
-struct ipa3_nat_mem {
+struct ipa3_nat_ipv6ct_common_mem {
struct class *class;
struct device *dev;
struct cdev cdev;
dev_t dev_num;
+
+ /* system memory */
void *vaddr;
dma_addr_t dma_handle;
+
+ /* shared memory */
+ u32 phys_mem_size;
+ u32 smem_offset;
+
size_t size;
bool is_mapped;
bool is_sys_mem;
+ bool is_mem_allocated;
+ bool is_hw_init;
bool is_dev_init;
- bool is_dev;
struct mutex lock;
- void *nat_base_address;
- char *ipv4_rules_addr;
- char *ipv4_expansion_rules_addr;
+ void *base_address;
+ char *base_table_addr;
+ char *expansion_table_addr;
+ u32 table_entries;
+ u32 expn_table_entries;
+ struct ipa3_nat_ipv6ct_tmp_mem *tmp_mem;
+ char name[IPA_DEV_NAME_MAX_LEN];
+};
+
+/**
+ * struct ipa3_nat_mem - IPA NAT memory description
+ * @dev: the memory device structure
+ * @index_table_addr: index table address
+ * @index_table_expansion_addr: index expansion table address
+ * @public_ip_addr: ip address of nat table
+ * @pdn_mem: pdn config table SW cache memory structure
+ */
+struct ipa3_nat_mem {
+ struct ipa3_nat_ipv6ct_common_mem dev;
char *index_table_addr;
char *index_table_expansion_addr;
- u32 size_base_tables;
- u32 size_expansion_tables;
u32 public_ip_addr;
- void *tmp_vaddr;
- dma_addr_t tmp_dma_handle;
- bool is_tmp_mem;
struct ipa_mem_buffer pdn_mem;
};
/**
+* struct ipa3_ipv6ct_mem - IPA IPv6 connection tracking memory description
+* @dev: the memory device structure
+*/
+struct ipa3_ipv6ct_mem {
+ struct ipa3_nat_ipv6ct_common_mem dev;
+};
+
+/**
* enum ipa3_hw_mode - IPA hardware mode
* @IPA_HW_Normal: Regular IPA hardware
* @IPA_HW_Virtual: IPA hardware supporting virtual memory allocation
@@ -1034,11 +1080,6 @@
void *user_data;
};
-struct ipa_tz_unlock_reg_info {
- u64 reg_addr;
- u32 size;
-};
-
struct ipa_dma_task_info {
struct ipa_mem_buffer mem;
struct ipahal_imm_cmd_pyld *cmd_pyld;
@@ -1142,6 +1183,7 @@
* from non-restricted bytes
* @smem_restricted_bytes: the bytes that SW should not use in the shared mem
* @nat_mem: NAT memory
+ * @ipv6ct_mem: IPv6CT memory
* @excp_hdr_hdl: exception header handle
* @dflt_v4_rt_rule_hdl: default v4 routing rule handle
* @dflt_v6_rt_rule_hdl: default v6 routing rule handle
@@ -1191,6 +1233,7 @@
* @ipa_ready_cb_list: A list of all the clients who require a CB when IPA
* driver is ready/initialized.
* @init_completion_obj: Completion object to be used in case IPA driver hasn't
+ * @mhi_evid_limits: MHI event rings start and end ids
* finished initializing. Example of use - IOCTLs to /dev/ipa
* IPA context - holds all relevant info about IPA driver and its state
*/
@@ -1228,6 +1271,7 @@
u16 smem_restricted_bytes;
u16 smem_reqd_sz;
struct ipa3_nat_mem nat_mem;
+ struct ipa3_ipv6ct_mem ipv6ct_mem;
u32 excp_hdr_hdl;
u32 dflt_v4_rt_rule_hdl;
u32 dflt_v6_rt_rule_hdl;
@@ -1318,6 +1362,7 @@
struct completion init_completion_obj;
struct completion uc_loaded_completion_obj;
struct ipa3_smp2p_info smp2p_info;
+ u32 mhi_evid_limits[2]; /* start and end values */
u32 ipa_tz_unlock_reg_num;
struct ipa_tz_unlock_reg_info *ipa_tz_unlock_reg;
struct ipa_dma_task_info dma_task_info;
@@ -1351,6 +1396,7 @@
bool apply_rg10_wa;
bool gsi_ch20_wa;
bool tethered_flow_control;
+ u32 mhi_evid_limits[2]; /* start and end values */
u32 ipa_tz_unlock_reg_num;
struct ipa_tz_unlock_reg_info *ipa_tz_unlock_reg;
bool use_ipa_pm;
@@ -1748,13 +1794,24 @@
/*
* NAT
*/
+int ipa3_nat_ipv6ct_init_devices(void);
+void ipa3_nat_ipv6ct_destroy_devices(void);
+
int ipa3_allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem);
+int ipa3_allocate_nat_table(
+ struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc);
+int ipa3_allocate_ipv6ct_table(
+ struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc);
int ipa3_nat_init_cmd(struct ipa_ioc_v4_nat_init *init);
+int ipa3_ipv6ct_init_cmd(struct ipa_ioc_ipv6ct_init *init);
+int ipa3_table_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma);
int ipa3_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma);
int ipa3_nat_del_cmd(struct ipa_ioc_v4_nat_del *del);
+int ipa3_del_nat_table(struct ipa_ioc_nat_ipv6ct_table_del *del);
+int ipa3_del_ipv6ct_table(struct ipa_ioc_nat_ipv6ct_table_del *del);
int ipa3_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn);
@@ -2105,7 +2162,6 @@
bool polling_mode, unsigned long timeout_jiffies);
void ipa3_uc_register_handlers(enum ipa3_hw_features feature,
struct ipa3_uc_hdlrs *hdlrs);
-int ipa3_create_nat_device(void);
int ipa3_uc_notify_clk_state(bool enabled);
int ipa3_dma_setup(void);
void ipa3_dma_shutdown(void);
@@ -2249,4 +2305,7 @@
void ipa3_free_dma_task_for_gsi(void);
int ipa3_set_clock_plan_from_pm(int idx);
void __ipa_gsi_irq_rx_scedule_poll(struct ipa3_sys_context *sys);
+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);
#endif /* _IPA3_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
index 08f2a8c..cb970ba 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
@@ -356,6 +356,14 @@
return -EINVAL;
}
+ if ((IPA_MHI_MAX_UL_CHANNELS + IPA_MHI_MAX_DL_CHANNELS) >
+ ((ipa3_ctx->mhi_evid_limits[1] -
+ ipa3_ctx->mhi_evid_limits[0]) + 1)) {
+ IPAERR("Not enough event rings for MHI\n");
+ ipa_assert();
+ return -EINVAL;
+ }
+
/* Initialize IPA MHI engine */
gsi_ep_info = ipa3_get_gsi_ep_info(IPA_CLIENT_MHI_PROD);
if (!gsi_ep_info) {
@@ -407,6 +415,8 @@
return -EINVAL;
}
+ in->start.gsi.evchid += ipa3_ctx->mhi_evid_limits[0];
+
client = in->sys->client;
ipa_ep_idx = ipa3_get_ep_mapping(client);
if (ipa_ep_idx == -1) {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
index 958fc6c..9f27c4f 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
@@ -18,23 +18,30 @@
#include <linux/uaccess.h>
#include "ipa_i.h"
#include "ipahal/ipahal.h"
+#include "ipahal/ipahal_nat.h"
#define IPA_NAT_PHYS_MEM_OFFSET 0
+#define IPA_IPV6CT_PHYS_MEM_OFFSET 0
#define IPA_NAT_PHYS_MEM_SIZE IPA_RAM_NAT_SIZE
+#define IPA_IPV6CT_PHYS_MEM_SIZE IPA_RAM_IPV6CT_SIZE
-#define IPA_NAT_TEMP_MEM_SIZE 128
+#define IPA_NAT_IPV6CT_TEMP_MEM_SIZE 128
-enum nat_table_type {
+#define IPA_NAT_MAX_NUM_OF_INIT_CMD_DESC 3
+#define IPA_IPV6CT_MAX_NUM_OF_INIT_CMD_DESC 2
+#define IPA_MAX_NUM_OF_TABLE_DMA_CMD_DESC 4
+#define IPA_MAX_NUM_OF_DEL_TABLE_CMD_DESC 2
+
+enum ipa_nat_ipv6ct_table_type {
IPA_NAT_BASE_TBL = 0,
IPA_NAT_EXPN_TBL = 1,
IPA_NAT_INDX_TBL = 2,
IPA_NAT_INDEX_EXPN_TBL = 3,
+ IPA_IPV6CT_BASE_TBL = 4,
+ IPA_IPV6CT_EXPN_TBL = 5
};
-#define NAT_TABLE_ENTRY_SIZE_BYTE 32
-#define NAT_INTEX_TABLE_ENTRY_SIZE_BYTE 4
-
-static int ipa3_nat_vma_fault_remap(
+static int ipa3_nat_ipv6ct_vma_fault_remap(
struct vm_area_struct *vma, struct vm_fault *vmf)
{
IPADBG("\n");
@@ -44,193 +51,340 @@
}
/* VMA related file operations functions */
-static struct vm_operations_struct ipa3_nat_remap_vm_ops = {
- .fault = ipa3_nat_vma_fault_remap,
+static const struct vm_operations_struct ipa3_nat_ipv6ct_remap_vm_ops = {
+ .fault = ipa3_nat_ipv6ct_vma_fault_remap,
};
-static int ipa3_nat_open(struct inode *inode, struct file *filp)
+static int ipa3_nat_ipv6ct_open(struct inode *inode, struct file *filp)
{
- struct ipa3_nat_mem *nat_ctx;
+ struct ipa3_nat_ipv6ct_common_mem *dev;
IPADBG("\n");
- nat_ctx = container_of(inode->i_cdev, struct ipa3_nat_mem, cdev);
- filp->private_data = nat_ctx;
+ dev = container_of(inode->i_cdev,
+ struct ipa3_nat_ipv6ct_common_mem, cdev);
+ filp->private_data = dev;
IPADBG("return\n");
return 0;
}
-static int ipa3_nat_mmap(struct file *filp, struct vm_area_struct *vma)
+static int ipa3_nat_ipv6ct_mmap(struct file *filp, struct vm_area_struct *vma)
{
+ struct ipa3_nat_ipv6ct_common_mem *dev =
+ (struct ipa3_nat_ipv6ct_common_mem *)filp->private_data;
unsigned long vsize = vma->vm_end - vma->vm_start;
- struct ipa3_nat_mem *nat_ctx =
- (struct ipa3_nat_mem *)filp->private_data;
unsigned long phys_addr;
- int result;
+ int result = 0;
- mutex_lock(&nat_ctx->lock);
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- if (nat_ctx->is_sys_mem) {
- IPADBG("Mapping system memory\n");
- if (nat_ctx->is_mapped) {
- IPAERR("mapping already exists, only 1 supported\n");
+ IPADBG("\n");
+
+ if (!dev->is_dev_init) {
+ IPAERR("attempt to mmap %s before dev init\n", dev->name);
+ return -EPERM;
+ }
+
+ mutex_lock(&dev->lock);
+ if (!dev->is_mem_allocated) {
+ IPAERR_RL("attempt to mmap %s before the memory allocation\n",
+ dev->name);
+ result = -EPERM;
+ goto bail;
+ }
+
+ if (dev->is_sys_mem) {
+ if (dev->is_mapped) {
+ IPAERR("%s already mapped, only 1 mapping supported\n",
+ dev->name);
result = -EINVAL;
goto bail;
}
- IPADBG("map sz=0x%zx\n", nat_ctx->size);
+ } else {
+ if ((dev->phys_mem_size == 0) || (vsize > dev->phys_mem_size)) {
+ IPAERR_RL("wrong parameters to %s mapping\n",
+ dev->name);
+ result = -EINVAL;
+ goto bail;
+ }
+ }
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ if (dev->is_sys_mem) {
+ IPADBG("Mapping system memory\n");
+ IPADBG("map sz=0x%zx\n", dev->size);
result =
dma_mmap_coherent(
- ipa3_ctx->pdev, vma,
- nat_ctx->vaddr, nat_ctx->dma_handle,
- nat_ctx->size);
-
+ ipa3_ctx->pdev, vma,
+ dev->vaddr, dev->dma_handle,
+ dev->size);
if (result) {
IPAERR("unable to map memory. Err:%d\n", result);
goto bail;
}
- ipa3_ctx->nat_mem.nat_base_address = nat_ctx->vaddr;
+ dev->base_address = dev->vaddr;
} else {
IPADBG("Mapping shared(local) memory\n");
IPADBG("map sz=0x%lx\n", vsize);
- if ((IPA_NAT_PHYS_MEM_SIZE == 0) ||
- (vsize > IPA_NAT_PHYS_MEM_SIZE)) {
- result = -EINVAL;
- goto bail;
- }
phys_addr = ipa3_ctx->ipa_wrapper_base +
ipa3_ctx->ctrl->ipa_reg_base_ofst +
ipahal_get_reg_n_ofst(IPA_SRAM_DIRECT_ACCESS_n,
- IPA_NAT_PHYS_MEM_OFFSET);
+ dev->smem_offset);
if (remap_pfn_range(
- vma, vma->vm_start,
- phys_addr >> PAGE_SHIFT, vsize, vma->vm_page_prot)) {
+ vma, vma->vm_start,
+ phys_addr >> PAGE_SHIFT, vsize, vma->vm_page_prot)) {
IPAERR("remap failed\n");
result = -EAGAIN;
goto bail;
}
- ipa3_ctx->nat_mem.nat_base_address = (void *)vma->vm_start;
+ dev->base_address = (void *)vma->vm_start;
}
- nat_ctx->is_mapped = true;
- vma->vm_ops = &ipa3_nat_remap_vm_ops;
- IPADBG("return\n");
result = 0;
+ vma->vm_ops = &ipa3_nat_ipv6ct_remap_vm_ops;
+ dev->is_mapped = true;
+ IPADBG("return\n");
bail:
- mutex_unlock(&nat_ctx->lock);
+ mutex_unlock(&dev->lock);
return result;
}
-static const struct file_operations ipa3_nat_fops = {
+static const struct file_operations ipa3_nat_ipv6ct_fops = {
.owner = THIS_MODULE,
- .open = ipa3_nat_open,
- .mmap = ipa3_nat_mmap
+ .open = ipa3_nat_ipv6ct_open,
+ .mmap = ipa3_nat_ipv6ct_mmap
};
/**
- * ipa3_allocate_temp_nat_memory() - Allocates temp nat memory
- *
- * Called during nat table delete
+ * ipa3_allocate_nat_ipv6ct_tmp_memory() - Allocates the NAT\IPv6CT temp memory
*/
-void ipa3_allocate_temp_nat_memory(void)
+static struct ipa3_nat_ipv6ct_tmp_mem *ipa3_nat_ipv6ct_allocate_tmp_memory(void)
{
- struct ipa3_nat_mem *nat_ctx = &(ipa3_ctx->nat_mem);
- int gfp_flags = GFP_KERNEL | __GFP_ZERO;
-
- nat_ctx->tmp_vaddr =
- dma_alloc_coherent(ipa3_ctx->pdev, IPA_NAT_TEMP_MEM_SIZE,
- &nat_ctx->tmp_dma_handle, gfp_flags);
-
- if (nat_ctx->tmp_vaddr == NULL) {
- IPAERR("Temp Memory alloc failed\n");
- nat_ctx->is_tmp_mem = false;
- return;
- }
-
- nat_ctx->is_tmp_mem = true;
- IPADBG("IPA NAT allocated temp memory successfully\n");
-}
-
-/**
- * ipa3_create_nat_device() - Create the NAT device
- *
- * Called during ipa init to create nat device
- *
- * Returns: 0 on success, negative on failure
- */
-int ipa3_create_nat_device(void)
-{
- struct ipa3_nat_mem *nat_ctx = &(ipa3_ctx->nat_mem);
- int result;
+ struct ipa3_nat_ipv6ct_tmp_mem *tmp_mem;
+ gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO;
IPADBG("\n");
- mutex_lock(&nat_ctx->lock);
- nat_ctx->class = class_create(THIS_MODULE, NAT_DEV_NAME);
- if (IS_ERR(nat_ctx->class)) {
- IPAERR("unable to create the class\n");
- result = -ENODEV;
- goto vaddr_alloc_fail;
+ tmp_mem = kzalloc(sizeof(*tmp_mem), GFP_KERNEL);
+ if (tmp_mem == NULL)
+ return NULL;
+
+ tmp_mem->vaddr =
+ dma_alloc_coherent(ipa3_ctx->pdev, IPA_NAT_IPV6CT_TEMP_MEM_SIZE,
+ &tmp_mem->dma_handle, gfp_flags);
+ if (tmp_mem->vaddr == NULL)
+ goto bail_tmp_mem;
+
+ IPADBG("IPA successfully allocated temp memory\n");
+ return tmp_mem;
+
+bail_tmp_mem:
+ kfree(tmp_mem);
+ return NULL;
+}
+
+static int ipa3_nat_ipv6ct_init_device(
+ struct ipa3_nat_ipv6ct_common_mem *dev,
+ const char *name,
+ u32 phys_mem_size,
+ u32 smem_offset,
+ struct ipa3_nat_ipv6ct_tmp_mem *tmp_mem)
+{
+ int result;
+
+ IPADBG("Init %s\n", name);
+
+ if (strnlen(name, IPA_DEV_NAME_MAX_LEN) == IPA_DEV_NAME_MAX_LEN) {
+ IPAERR("device name is too long\n");
+ return -ENODEV;
}
- result = alloc_chrdev_region(&nat_ctx->dev_num,
- 0,
- 1,
- NAT_DEV_NAME);
+
+ strlcpy(dev->name, name, IPA_DEV_NAME_MAX_LEN);
+
+ dev->class = class_create(THIS_MODULE, name);
+ if (IS_ERR(dev->class)) {
+ IPAERR("unable to create the class for %s\n", name);
+ return -ENODEV;
+ }
+ result = alloc_chrdev_region(&dev->dev_num, 0, 1, name);
if (result) {
- IPAERR("alloc_chrdev_region err.\n");
+ IPAERR("alloc_chrdev_region err. for %s\n", name);
result = -ENODEV;
goto alloc_chrdev_region_fail;
}
- nat_ctx->dev =
- device_create(nat_ctx->class, NULL, nat_ctx->dev_num, nat_ctx,
- "%s", NAT_DEV_NAME);
+ dev->dev = device_create(dev->class, NULL, dev->dev_num, NULL, name);
- if (IS_ERR(nat_ctx->dev)) {
- IPAERR("device_create err:%ld\n", PTR_ERR(nat_ctx->dev));
+ if (IS_ERR(dev->dev)) {
+ IPAERR("device_create err:%ld\n", PTR_ERR(dev->dev));
result = -ENODEV;
goto device_create_fail;
}
- cdev_init(&nat_ctx->cdev, &ipa3_nat_fops);
- nat_ctx->cdev.owner = THIS_MODULE;
- nat_ctx->cdev.ops = &ipa3_nat_fops;
+ cdev_init(&dev->cdev, &ipa3_nat_ipv6ct_fops);
+ dev->cdev.owner = THIS_MODULE;
- result = cdev_add(&nat_ctx->cdev, nat_ctx->dev_num, 1);
+ mutex_init(&dev->lock);
+ mutex_lock(&dev->lock);
+
+ result = cdev_add(&dev->cdev, dev->dev_num, 1);
if (result) {
IPAERR("cdev_add err=%d\n", -result);
goto cdev_add_fail;
}
- IPADBG("ipa nat dev added successful. major:%d minor:%d\n",
- MAJOR(nat_ctx->dev_num),
- MINOR(nat_ctx->dev_num));
- nat_ctx->is_dev = true;
- ipa3_allocate_temp_nat_memory();
- IPADBG("IPA NAT device created successfully\n");
- result = 0;
- goto bail;
+ dev->phys_mem_size = phys_mem_size;
+ dev->smem_offset = smem_offset;
+
+ dev->is_dev_init = true;
+ mutex_unlock(&dev->lock);
+
+ IPADBG("ipa dev %s added successful. major:%d minor:%d\n", name,
+ MAJOR(dev->dev_num), MINOR(dev->dev_num));
+ return 0;
cdev_add_fail:
- device_destroy(nat_ctx->class, nat_ctx->dev_num);
+ mutex_unlock(&dev->lock);
+ device_destroy(dev->class, dev->dev_num);
device_create_fail:
- unregister_chrdev_region(nat_ctx->dev_num, 1);
+ unregister_chrdev_region(dev->dev_num, 1);
alloc_chrdev_region_fail:
- class_destroy(nat_ctx->class);
-vaddr_alloc_fail:
- if (nat_ctx->vaddr) {
- IPADBG("Releasing system memory\n");
- dma_free_coherent(
- ipa3_ctx->pdev, nat_ctx->size,
- nat_ctx->vaddr, nat_ctx->dma_handle);
- nat_ctx->vaddr = NULL;
- nat_ctx->dma_handle = 0;
- nat_ctx->size = 0;
+ class_destroy(dev->class);
+ return result;
+}
+
+static void ipa3_nat_ipv6ct_destroy_device(
+ struct ipa3_nat_ipv6ct_common_mem *dev)
+{
+ IPADBG("\n");
+
+ mutex_lock(&dev->lock);
+
+ device_destroy(dev->class, dev->dev_num);
+ unregister_chrdev_region(dev->dev_num, 1);
+ class_destroy(dev->class);
+ dev->is_dev_init = false;
+
+ mutex_unlock(&dev->lock);
+
+ IPADBG("return\n");
+}
+
+/**
+ * ipa3_nat_ipv6ct_init_devices() - Initialize the NAT and IPv6CT devices
+ *
+ * Called during IPA init to create memory device
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa3_nat_ipv6ct_init_devices(void)
+{
+ struct ipa3_nat_ipv6ct_tmp_mem *tmp_mem;
+ int result;
+
+ IPADBG("\n");
+
+ /*
+ * Allocate NAT/IPv6CT temporary memory. The memory is never deleted,
+ * because provided to HW once NAT or IPv6CT table is deleted.
+ * NULL is a legal value
+ */
+ tmp_mem = ipa3_nat_ipv6ct_allocate_tmp_memory();
+
+ if (ipa3_nat_ipv6ct_init_device(
+ &ipa3_ctx->nat_mem.dev,
+ IPA_NAT_DEV_NAME,
+ IPA_NAT_PHYS_MEM_SIZE,
+ IPA_NAT_PHYS_MEM_OFFSET,
+ tmp_mem)) {
+ IPAERR("unable to create nat device\n");
+ result = -ENODEV;
+ goto fail_init_nat_dev;
}
-bail:
- mutex_unlock(&nat_ctx->lock);
+ if ((ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) &&
+ ipa3_nat_ipv6ct_init_device(
+ &ipa3_ctx->ipv6ct_mem.dev,
+ IPA_IPV6CT_DEV_NAME,
+ IPA_IPV6CT_PHYS_MEM_SIZE,
+ IPA_IPV6CT_PHYS_MEM_OFFSET,
+ tmp_mem)) {
+ IPAERR("unable to create IPv6CT device\n");
+ result = -ENODEV;
+ goto fail_init_ipv6ct_dev;
+ }
+ return 0;
+
+fail_init_ipv6ct_dev:
+ ipa3_nat_ipv6ct_destroy_device(&ipa3_ctx->nat_mem.dev);
+fail_init_nat_dev:
+ if (tmp_mem != NULL) {
+ dma_free_coherent(ipa3_ctx->pdev, IPA_NAT_IPV6CT_TEMP_MEM_SIZE,
+ tmp_mem->vaddr, tmp_mem->dma_handle);
+ kfree(tmp_mem);
+ }
+ return result;
+}
+
+/**
+ * ipa3_nat_ipv6ct_destroy_devices() - destroy the NAT and IPv6CT devices
+ *
+ * Called during IPA init to destroy nat device
+ */
+void ipa3_nat_ipv6ct_destroy_devices(void)
+{
+ ipa3_nat_ipv6ct_destroy_device(&ipa3_ctx->nat_mem.dev);
+ if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
+ ipa3_nat_ipv6ct_destroy_device(&ipa3_ctx->ipv6ct_mem.dev);
+}
+
+static int ipa3_nat_ipv6ct_allocate_mem(struct ipa3_nat_ipv6ct_common_mem *dev,
+ struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc)
+{
+ gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO;
+ int result = 0;
+
+ IPADBG("passed memory size %zu for %s\n",
+ table_alloc->size, dev->name);
+
+ if (!dev->is_dev_init) {
+ IPAERR("%s hasn't been initialized\n", dev->name);
+ result = -EPERM;
+ goto bail;
+ }
+
+ if (dev->is_mem_allocated) {
+ IPAERR("Memory already allocated\n");
+ result = 0;
+ goto bail;
+ }
+
+ if (!table_alloc->size) {
+ IPAERR_RL("Invalid Parameters\n");
+ result = -EPERM;
+ goto bail;
+ }
+
+ if (table_alloc->size > IPA_NAT_PHYS_MEM_SIZE) {
+ IPADBG("Allocating system memory\n");
+ dev->is_sys_mem = true;
+ dev->vaddr =
+ dma_alloc_coherent(ipa3_ctx->pdev, table_alloc->size,
+ &dev->dma_handle, gfp_flags);
+ if (dev->vaddr == NULL) {
+ IPAERR("memory alloc failed\n");
+ result = -ENOMEM;
+ goto bail;
+ }
+ dev->size = table_alloc->size;
+ } else {
+ IPADBG("using shared(local) memory\n");
+ dev->is_sys_mem = false;
+ }
+
+ IPADBG("return\n");
+
+bail:
return result;
}
@@ -245,60 +399,51 @@
*/
int ipa3_allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem)
{
+ int result;
+ struct ipa_ioc_nat_ipv6ct_table_alloc tmp;
+
+ tmp.size = mem->size;
+ tmp.offset = 0;
+
+ result = ipa3_allocate_nat_table(&tmp);
+ if (result)
+ goto bail;
+
+ mem->offset = tmp.offset;
+
+bail:
+ return result;
+}
+
+/**
+ * ipa3_allocate_nat_table() - Allocates memory for the NAT table
+ * @table_alloc: [in/out] memory parameters
+ *
+ * Called by NAT client to allocate memory for the table entries.
+ * Based on the request size either shared or system memory will be used.
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa3_allocate_nat_table(struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc)
+{
struct ipa3_nat_mem *nat_ctx = &(ipa3_ctx->nat_mem);
gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO;
int result;
- IPADBG("passed memory size %zu\n", mem->size);
+ IPADBG("\n");
- mutex_lock(&nat_ctx->lock);
- if (strcmp(mem->dev_name, NAT_DEV_NAME)) {
- IPAERR_RL("Nat device name mismatch\n");
- IPAERR_RL("Expect: %s Recv: %s\n", NAT_DEV_NAME, mem->dev_name);
- result = -EPERM;
+ mutex_lock(&nat_ctx->dev.lock);
+
+ result = ipa3_nat_ipv6ct_allocate_mem(&nat_ctx->dev, table_alloc);
+ if (result)
goto bail;
- }
- if (nat_ctx->is_dev != true) {
- IPAERR("Nat device not created successfully during boot up\n");
- result = -EPERM;
- goto bail;
- }
-
- if (nat_ctx->is_dev_init == true) {
- IPAERR("Device already init\n");
- result = 0;
- goto bail;
- }
-
- if (mem->size <= 0 ||
- nat_ctx->is_dev_init == true) {
- IPAERR_RL("Invalid Parameters or device is already init\n");
- result = -EPERM;
- goto bail;
- }
-
- if (mem->size > IPA_NAT_PHYS_MEM_SIZE) {
- IPADBG("Allocating system memory\n");
- nat_ctx->is_sys_mem = true;
- nat_ctx->vaddr =
- dma_alloc_coherent(ipa3_ctx->pdev, mem->size,
- &nat_ctx->dma_handle, gfp_flags);
- if (nat_ctx->vaddr == NULL) {
- IPAERR("memory alloc failed\n");
- result = -ENOMEM;
- goto bail;
- }
- nat_ctx->size = mem->size;
- } else {
- IPADBG("using shared(local) memory\n");
- nat_ctx->is_sys_mem = false;
- }
if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
- struct ipa_pdn_entry *pdn_entries;
- struct ipa_mem_buffer *pdn_mem = &ipa3_ctx->nat_mem.pdn_mem;
+ size_t pdn_entry_size;
+ struct ipa_mem_buffer *pdn_mem = &nat_ctx->pdn_mem;
- pdn_mem->size = sizeof(struct ipa_pdn_entry) * IPA_MAX_PDN_NUM;
+ ipahal_nat_entry_size(IPAHAL_NAT_IPV4_PDN, &pdn_entry_size);
+ pdn_mem->size = pdn_entry_size * IPA_MAX_PDN_NUM;
if (IPA_MEM_PART(pdn_config_size) < pdn_mem->size) {
IPAERR(
"number of PDN entries exceeds SRAM available space\n");
@@ -315,29 +460,203 @@
result = -ENOMEM;
goto fail_alloc_pdn;
}
- pdn_entries = pdn_mem->base;
- memset(pdn_entries, 0, pdn_mem->size);
+ memset(pdn_mem->base, 0, pdn_mem->size);
IPADBG("IPA NAT dev allocated PDN memory successfully\n");
}
- nat_ctx->is_dev_init = true;
+ nat_ctx->dev.is_mem_allocated = true;
IPADBG("IPA NAT dev init successfully\n");
- mutex_unlock(&nat_ctx->lock);
+ mutex_unlock(&nat_ctx->dev.lock);
+
+ IPADBG("return\n");
return 0;
fail_alloc_pdn:
- if (nat_ctx->vaddr) {
- dma_free_coherent(ipa3_ctx->pdev, mem->size, nat_ctx->vaddr,
- nat_ctx->dma_handle);
- nat_ctx->vaddr = NULL;
+ if (nat_ctx->dev.vaddr) {
+ dma_free_coherent(ipa3_ctx->pdev, table_alloc->size,
+ nat_ctx->dev.vaddr, nat_ctx->dev.dma_handle);
+ nat_ctx->dev.vaddr = NULL;
}
bail:
- mutex_unlock(&nat_ctx->lock);
+ mutex_unlock(&nat_ctx->dev.lock);
return result;
}
+/**
+ * ipa3_allocate_ipv6ct_table() - Allocates memory for the IPv6CT table
+ * @table_alloc: [in/out] memory parameters
+ *
+ * Called by IPv6CT client to allocate memory for the table entries.
+ * Based on the request size either shared or system memory will be used.
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa3_allocate_ipv6ct_table(
+ struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc)
+{
+ int result;
+
+ IPADBG("\n");
+
+ if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) {
+ IPAERR_RL("IPv6 connection tracking isn't supported\n");
+ return -EPERM;
+ }
+
+ mutex_lock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+
+ result = ipa3_nat_ipv6ct_allocate_mem(
+ &ipa3_ctx->ipv6ct_mem.dev, table_alloc);
+ if (result)
+ goto bail;
+
+ ipa3_ctx->ipv6ct_mem.dev.is_mem_allocated = true;
+ IPADBG("IPA IPv6CT dev init successfully\n");
+
+bail:
+ mutex_unlock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+ return result;
+}
+
+static int ipa3_nat_ipv6ct_check_table_params(
+ struct ipa3_nat_ipv6ct_common_mem *dev,
+ uint32_t offset, uint16_t entries_num,
+ enum ipahal_nat_type nat_type)
+{
+ int result;
+ size_t entry_size, table_size;
+
+ result = ipahal_nat_entry_size(nat_type, &entry_size);
+ if (result) {
+ IPAERR("Failed to retrieve size of entry for %s\n",
+ ipahal_nat_type_str(nat_type));
+ return result;
+ }
+ table_size = entry_size * entries_num;
+
+ /* check for integer overflow */
+ if (offset > UINT_MAX - table_size) {
+ IPAERR_RL("Detected overflow\n");
+ return -EPERM;
+ }
+
+ /* Check offset is not beyond allocated size */
+ if (dev->size < offset + table_size) {
+ IPAERR_RL("Table offset not valid\n");
+ IPAERR_RL("offset:%d entries:%d table_size:%zu mem_size:%zu\n",
+ offset, entries_num, table_size, dev->size);
+ return -EPERM;
+ }
+
+ if (dev->is_sys_mem && offset > UINT_MAX - dev->dma_handle) {
+ IPAERR_RL("Failed due to integer overflow\n");
+ IPAERR_RL("%s dma_handle: 0x%pa offset: 0x%x\n",
+ dev->name, &dev->dma_handle, offset);
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static inline void ipa3_nat_ipv6ct_create_init_cmd(
+ struct ipahal_imm_cmd_nat_ipv6ct_init_common *table_init_cmd,
+ bool is_shared,
+ dma_addr_t base_addr,
+ uint8_t tbl_index,
+ uint32_t base_table_offset,
+ uint32_t expn_table_offset,
+ uint16_t table_entries,
+ uint16_t expn_table_entries,
+ const char *table_name)
+{
+ table_init_cmd->base_table_addr_shared = is_shared;
+ table_init_cmd->expansion_table_addr_shared = is_shared;
+
+ table_init_cmd->base_table_addr = base_addr + base_table_offset;
+ IPADBG("%s base table offset:0x%x\n", table_name, base_table_offset);
+
+ table_init_cmd->expansion_table_addr = base_addr + expn_table_offset;
+ IPADBG("%s expn table offset:0x%x\n", table_name, expn_table_offset);
+
+ table_init_cmd->table_index = tbl_index;
+ IPADBG("%s table index:0x%x\n", table_name, tbl_index);
+
+ table_init_cmd->size_base_table = table_entries;
+ IPADBG("%s base table size:0x%x\n", table_name, table_entries);
+
+ table_init_cmd->size_expansion_table = expn_table_entries;
+ IPADBG("%s expansion table size:0x%x\n",
+ table_name, expn_table_entries);
+}
+
+static inline void ipa3_nat_ipv6ct_init_device_structure(
+ struct ipa3_nat_ipv6ct_common_mem *dev,
+ uint32_t base_table_offset,
+ uint32_t expn_table_offset,
+ uint16_t table_entries,
+ uint16_t expn_table_entries)
+{
+ dev->base_table_addr = (char *)dev->base_address + base_table_offset;
+ IPADBG("%s base_table_addr: 0x%p\n", dev->name, dev->base_table_addr);
+
+ dev->expansion_table_addr =
+ (char *)dev->base_address + expn_table_offset;
+ IPADBG("%s expansion_table_addr: 0x%p\n",
+ dev->name, dev->expansion_table_addr);
+
+ IPADBG("%s table_entries: %d\n", dev->name, table_entries);
+ dev->table_entries = table_entries;
+
+ IPADBG("%s expn_table_entries: %d\n", dev->name, expn_table_entries);
+ dev->expn_table_entries = expn_table_entries;
+}
+
+static void ipa3_nat_create_init_cmd(
+ struct ipa_ioc_v4_nat_init *init,
+ bool is_shared,
+ dma_addr_t base_addr,
+ struct ipahal_imm_cmd_ip_v4_nat_init *cmd)
+{
+ IPADBG("\n");
+
+ ipa3_nat_ipv6ct_create_init_cmd(
+ &cmd->table_init,
+ is_shared,
+ base_addr,
+ init->tbl_index,
+ init->ipv4_rules_offset,
+ init->expn_rules_offset,
+ init->table_entries,
+ init->expn_table_entries,
+ ipa3_ctx->nat_mem.dev.name);
+
+ cmd->index_table_addr_shared = is_shared;
+ cmd->index_table_expansion_addr_shared = is_shared;
+
+ cmd->index_table_addr =
+ base_addr + init->index_offset;
+ IPADBG("index_offset:0x%x\n", init->index_offset);
+
+ cmd->index_table_expansion_addr =
+ base_addr + init->index_expn_offset;
+ IPADBG("index_expn_offset:0x%x\n", init->index_expn_offset);
+ if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
+ /*
+ * starting IPAv4.0 public ip field changed to store the
+ * PDN config table offset in SMEM
+ */
+ cmd->public_addr_info = IPA_MEM_PART(pdn_config_ofst);
+ IPADBG("pdn config base:0x%x\n", cmd->public_addr_info);
+ } else {
+ cmd->public_addr_info = init->ip_addr;
+ IPADBG("Public IP address:%pI4h\n", &cmd->public_addr_info);
+ }
+
+ IPADBG("return\n");
+}
+
/* IOCTL function handlers */
/**
* ipa3_nat_init_cmd() - Post IP_V4_NAT_INIT command to IPA HW
@@ -349,102 +668,71 @@
*/
int ipa3_nat_init_cmd(struct ipa_ioc_v4_nat_init *init)
{
-#define TBL_ENTRY_SIZE 32
-#define INDX_TBL_ENTRY_SIZE 4
-
- struct ipa3_desc desc[3];
+ struct ipa3_desc desc[IPA_NAT_MAX_NUM_OF_INIT_CMD_DESC];
struct ipahal_imm_cmd_ip_v4_nat_init cmd;
- int num_cmd = 0;
- int i = 0;
- struct ipahal_imm_cmd_pyld *cmd_pyld[3];
+ int i, num_cmd = 0;
+ struct ipahal_imm_cmd_pyld *cmd_pyld[IPA_NAT_MAX_NUM_OF_INIT_CMD_DESC];
struct ipahal_imm_cmd_dma_shared_mem mem_cmd = { 0 };
- int result = 0;
- u32 offset = 0;
- size_t tmp;
+ int result;
IPADBG("\n");
+
+ if (!ipa3_ctx->nat_mem.dev.is_mapped) {
+ IPAERR_RL("attempt to init %s before mmap\n",
+ ipa3_ctx->nat_mem.dev.name);
+ return -EPERM;
+ }
+
+ if (init->tbl_index >= 1) {
+ IPAERR_RL("Unsupported table index %d\n", init->tbl_index);
+ return -EPERM;
+ }
+
if (init->table_entries == 0) {
- IPADBG("Table entries is zero\n");
+ IPAERR_RL("Table entries is zero\n");
return -EPERM;
}
- /* check for integer overflow */
- if (init->ipv4_rules_offset >
- UINT_MAX - (TBL_ENTRY_SIZE * (init->table_entries + 1))) {
- IPAERR_RL("Detected overflow\n");
- return -EPERM;
- }
- /* Check Table Entry offset is not
- * beyond allocated size
- */
- tmp = init->ipv4_rules_offset +
- (TBL_ENTRY_SIZE * (init->table_entries + 1));
- if (tmp > ipa3_ctx->nat_mem.size) {
- IPAERR_RL("Table rules offset not valid\n");
- IPAERR_RL("offset:%d entrys:%d size:%zu mem_size:%zu\n",
- init->ipv4_rules_offset, (init->table_entries + 1),
- tmp, ipa3_ctx->nat_mem.size);
- return -EPERM;
+ result = ipa3_nat_ipv6ct_check_table_params(
+ &ipa3_ctx->nat_mem.dev,
+ init->ipv4_rules_offset,
+ init->table_entries + 1,
+ IPAHAL_NAT_IPV4);
+ if (result) {
+ IPAERR_RL("Bad params for NAT base table\n");
+ return result;
}
- /* check for integer overflow */
- if (init->expn_rules_offset >
- (UINT_MAX - (TBL_ENTRY_SIZE * init->expn_table_entries))) {
- IPAERR_RL("Detected overflow\n");
- return -EPERM;
- }
- /* Check Expn Table Entry offset is not
- * beyond allocated size
- */
- tmp = init->expn_rules_offset +
- (TBL_ENTRY_SIZE * init->expn_table_entries);
- if (tmp > ipa3_ctx->nat_mem.size) {
- IPAERR_RL("Expn Table rules offset not valid\n");
- IPAERR_RL("offset:%d entrys:%d size:%zu mem_size:%zu\n",
- init->expn_rules_offset, init->expn_table_entries,
- tmp, ipa3_ctx->nat_mem.size);
- return -EPERM;
+ result = ipa3_nat_ipv6ct_check_table_params(
+ &ipa3_ctx->nat_mem.dev,
+ init->expn_rules_offset,
+ init->expn_table_entries,
+ IPAHAL_NAT_IPV4);
+ if (result) {
+ IPAERR_RL("Bad params for NAT expansion table\n");
+ return result;
}
- /* check for integer overflow */
- if (init->index_offset >
- UINT_MAX - (INDX_TBL_ENTRY_SIZE * (init->table_entries + 1))) {
- IPAERR_RL("Detected overflow\n");
- return -EPERM;
- }
- /* Check Indx Table Entry offset is not
- * beyond allocated size
- */
- tmp = init->index_offset +
- (INDX_TBL_ENTRY_SIZE * (init->table_entries + 1));
- if (tmp > ipa3_ctx->nat_mem.size) {
- IPAERR_RL("Indx Table rules offset not valid\n");
- IPAERR_RL("offset:%d entrys:%d size:%zu mem_size:%zu\n",
- init->index_offset, (init->table_entries + 1),
- tmp, ipa3_ctx->nat_mem.size);
- return -EPERM;
+ result = ipa3_nat_ipv6ct_check_table_params(
+ &ipa3_ctx->nat_mem.dev,
+ init->index_offset,
+ init->table_entries + 1,
+ IPAHAL_NAT_IPV4_INDEX);
+ if (result) {
+ IPAERR_RL("Bad params for index table\n");
+ return result;
}
- /* check for integer overflow */
- if (init->index_expn_offset >
- UINT_MAX - (INDX_TBL_ENTRY_SIZE * init->expn_table_entries)) {
- IPAERR_RL("Detected overflow\n");
- return -EPERM;
- }
- /* Check Expn Table entry offset is not
- * beyond allocated size
- */
- tmp = init->index_expn_offset +
- (INDX_TBL_ENTRY_SIZE * init->expn_table_entries);
- if (tmp > ipa3_ctx->nat_mem.size) {
- IPAERR_RL("Indx Expn Table rules offset not valid\n");
- IPAERR_RL("offset:%d entrys:%d size:%zu mem_size:%zu\n",
- init->index_expn_offset, init->expn_table_entries,
- tmp, ipa3_ctx->nat_mem.size);
- return -EPERM;
+ result = ipa3_nat_ipv6ct_check_table_params(
+ &ipa3_ctx->nat_mem.dev,
+ init->index_expn_offset,
+ init->expn_table_entries,
+ IPAHAL_NAT_IPV4_INDEX);
+ if (result) {
+ IPAERR_RL("Bad params for index expansion table\n");
+ return result;
}
- memset(&desc, 0, sizeof(desc));
/* NO-OP IC for ensuring that IPA pipeline is empty */
cmd_pyld[num_cmd] =
ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
@@ -454,92 +742,20 @@
goto bail;
}
- desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
- desc[num_cmd].type = IPA_IMM_CMD_DESC;
- desc[num_cmd].callback = NULL;
- desc[num_cmd].user1 = NULL;
- desc[num_cmd].user2 = 0;
- desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
- desc[num_cmd].len = cmd_pyld[num_cmd]->len;
- num_cmd++;
+ ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+ ++num_cmd;
- if (ipa3_ctx->nat_mem.vaddr) {
+ if (ipa3_ctx->nat_mem.dev.is_sys_mem) {
IPADBG("using system memory for nat table\n");
- cmd.ipv4_rules_addr_shared = false;
- cmd.ipv4_expansion_rules_addr_shared = false;
- cmd.index_table_addr_shared = false;
- cmd.index_table_expansion_addr_shared = false;
-
- offset = UINT_MAX - ipa3_ctx->nat_mem.dma_handle;
-
- if ((init->ipv4_rules_offset > offset) ||
- (init->expn_rules_offset > offset) ||
- (init->index_offset > offset) ||
- (init->index_expn_offset > offset)) {
- IPAERR_RL("Failed due to integer overflow\n");
- IPAERR_RL("nat.mem.dma_handle: 0x%pa\n",
- &ipa3_ctx->nat_mem.dma_handle);
- IPAERR_RL("ipv4_rules_offset: 0x%x\n",
- init->ipv4_rules_offset);
- IPAERR_RL("expn_rules_offset: 0x%x\n",
- init->expn_rules_offset);
- IPAERR_RL("index_offset: 0x%x\n",
- init->index_offset);
- IPAERR_RL("index_expn_offset: 0x%x\n",
- init->index_expn_offset);
- result = -EPERM;
- goto destroy_imm_cmd;
- }
- cmd.ipv4_rules_addr =
- ipa3_ctx->nat_mem.dma_handle + init->ipv4_rules_offset;
- IPADBG("ipv4_rules_offset:0x%x\n", init->ipv4_rules_offset);
-
- cmd.ipv4_expansion_rules_addr =
- ipa3_ctx->nat_mem.dma_handle + init->expn_rules_offset;
- IPADBG("expn_rules_offset:0x%x\n", init->expn_rules_offset);
-
- cmd.index_table_addr =
- ipa3_ctx->nat_mem.dma_handle + init->index_offset;
- IPADBG("index_offset:0x%x\n", init->index_offset);
-
- cmd.index_table_expansion_addr =
- ipa3_ctx->nat_mem.dma_handle + init->index_expn_offset;
- IPADBG("index_expn_offset:0x%x\n", init->index_expn_offset);
+ /*
+ * Safe to process, since integer overflow was
+ * checked in ipa3_nat_ipv6ct_check_table_params
+ */
+ ipa3_nat_create_init_cmd(init, false,
+ ipa3_ctx->nat_mem.dev.dma_handle, &cmd);
} else {
IPADBG("using shared(local) memory for nat table\n");
- cmd.ipv4_rules_addr_shared = true;
- cmd.ipv4_expansion_rules_addr_shared = true;
- cmd.index_table_addr_shared = true;
- cmd.index_table_expansion_addr_shared = true;
-
- cmd.ipv4_rules_addr = init->ipv4_rules_offset +
- IPA_RAM_NAT_OFST;
-
- cmd.ipv4_expansion_rules_addr = init->expn_rules_offset +
- IPA_RAM_NAT_OFST;
-
- cmd.index_table_addr = init->index_offset +
- IPA_RAM_NAT_OFST;
-
- cmd.index_table_expansion_addr = init->index_expn_offset +
- IPA_RAM_NAT_OFST;
- }
- cmd.table_index = init->tbl_index;
- IPADBG("Table index:0x%x\n", cmd.table_index);
- cmd.size_base_tables = init->table_entries;
- IPADBG("Base Table size:0x%x\n", cmd.size_base_tables);
- cmd.size_expansion_tables = init->expn_table_entries;
- IPADBG("Expansion Table size:0x%x\n", cmd.size_expansion_tables);
- if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
- /*
- * public ip field changed to store the PDN config base
- * address in IPAv4
- */
- cmd.public_ip_addr = IPA_MEM_PART(pdn_config_ofst);
- IPADBG("pdn config base:0x%x\n", cmd.public_ip_addr);
- } else {
- cmd.public_ip_addr = init->ip_addr;
- IPADBG("Public ip address:0x%x\n", cmd.public_ip_addr);
+ ipa3_nat_create_init_cmd(init, true, IPA_RAM_NAT_OFST, &cmd);
}
cmd_pyld[num_cmd] = ipahal_construct_imm_cmd(
IPA_IMM_CMD_IP_V4_NAT_INIT, &cmd, false);
@@ -549,18 +765,18 @@
goto destroy_imm_cmd;
}
- desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
- desc[num_cmd].type = IPA_IMM_CMD_DESC;
- desc[num_cmd].callback = NULL;
- desc[num_cmd].user1 = NULL;
- desc[num_cmd].user2 = 0;
- desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
- desc[num_cmd].len = cmd_pyld[num_cmd]->len;
- num_cmd++;
+ ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+ ++num_cmd;
if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
struct ipa_pdn_entry *pdn_entries;
+ if (num_cmd >= IPA_NAT_MAX_NUM_OF_INIT_CMD_DESC) {
+ IPAERR("number of commands is out of range\n");
+ result = -ENOBUFS;
+ goto destroy_imm_cmd;
+ }
+
/* store ip in pdn entries cache array */
pdn_entries = ipa3_ctx->nat_mem.pdn_mem.base;
pdn_entries[0].public_ip = init->ip_addr;
@@ -586,14 +802,8 @@
result = -ENOMEM;
goto destroy_imm_cmd;
}
- desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
- desc[num_cmd].type = IPA_IMM_CMD_DESC;
- desc[num_cmd].callback = NULL;
- desc[num_cmd].user1 = NULL;
- desc[num_cmd].user2 = 0;
- desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
- desc[num_cmd].len = cmd_pyld[num_cmd]->len;
- num_cmd++;
+ ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+ ++num_cmd;
IPADBG("added PDN table copy cmd\n");
}
@@ -604,52 +814,186 @@
goto destroy_imm_cmd;
}
+ ipa3_nat_ipv6ct_init_device_structure(
+ &ipa3_ctx->nat_mem.dev,
+ init->ipv4_rules_offset,
+ init->expn_rules_offset,
+ init->table_entries,
+ init->expn_table_entries);
+
ipa3_ctx->nat_mem.public_ip_addr = init->ip_addr;
- IPADBG("Table ip address:0x%x", ipa3_ctx->nat_mem.public_ip_addr);
-
- ipa3_ctx->nat_mem.ipv4_rules_addr =
- (char *)ipa3_ctx->nat_mem.nat_base_address + init->ipv4_rules_offset;
- IPADBG("ipv4_rules_addr: 0x%p\n",
- ipa3_ctx->nat_mem.ipv4_rules_addr);
-
- ipa3_ctx->nat_mem.ipv4_expansion_rules_addr =
- (char *)ipa3_ctx->nat_mem.nat_base_address + init->expn_rules_offset;
- IPADBG("ipv4_expansion_rules_addr: 0x%p\n",
- ipa3_ctx->nat_mem.ipv4_expansion_rules_addr);
+ IPADBG("Public IP address:%pI4h\n", &ipa3_ctx->nat_mem.public_ip_addr);
ipa3_ctx->nat_mem.index_table_addr =
- (char *)ipa3_ctx->nat_mem.nat_base_address +
+ (char *)ipa3_ctx->nat_mem.dev.base_address +
init->index_offset;
IPADBG("index_table_addr: 0x%p\n",
ipa3_ctx->nat_mem.index_table_addr);
ipa3_ctx->nat_mem.index_table_expansion_addr =
- (char *)ipa3_ctx->nat_mem.nat_base_address + init->index_expn_offset;
+ (char *)ipa3_ctx->nat_mem.dev.base_address + init->index_expn_offset;
IPADBG("index_table_expansion_addr: 0x%p\n",
ipa3_ctx->nat_mem.index_table_expansion_addr);
- IPADBG("size_base_tables: %d\n", init->table_entries);
- ipa3_ctx->nat_mem.size_base_tables = init->table_entries;
-
- IPADBG("size_expansion_tables: %d\n", init->expn_table_entries);
- ipa3_ctx->nat_mem.size_expansion_tables = init->expn_table_entries;
-
+ ipa3_ctx->nat_mem.dev.is_hw_init = true;
IPADBG("return\n");
destroy_imm_cmd:
- for (i = 0; i < num_cmd; i++)
+ for (i = 0; i < num_cmd; ++i)
ipahal_destroy_imm_cmd(cmd_pyld[i]);
bail:
return result;
}
/**
-* ipa3_nat_mdfy_pdn() - Modify a PDN entry in PDN config table in IPA SRAM
-* @mdfy_pdn: [in] PDN info to be written to SRAM
-*
-* Called by NAT client driver to modify an entry in the PDN config table
-*
-* Returns: 0 on success, negative on failure
-*/
+ * ipa3_ipv6ct_init_cmd() - Post IP_V6_CONN_TRACK_INIT command to IPA HW
+ * @init: [in] initialization command attributes
+ *
+ * Called by NAT client driver to post IP_V6_CONN_TRACK_INIT command to IPA HW
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa3_ipv6ct_init_cmd(struct ipa_ioc_ipv6ct_init *init)
+{
+ struct ipa3_desc desc[IPA_IPV6CT_MAX_NUM_OF_INIT_CMD_DESC];
+ struct ipahal_imm_cmd_ip_v6_ct_init cmd;
+ int i, num_cmd = 0;
+ struct ipahal_imm_cmd_pyld
+ *cmd_pyld[IPA_IPV6CT_MAX_NUM_OF_INIT_CMD_DESC];
+ int result;
+
+ IPADBG("\n");
+
+ if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) {
+ IPAERR_RL("IPv6 connection tracking isn't supported\n");
+ return -EPERM;
+ }
+
+ if (!ipa3_ctx->ipv6ct_mem.dev.is_mapped) {
+ IPAERR_RL("attempt to init %s before mmap\n",
+ ipa3_ctx->ipv6ct_mem.dev.name);
+ return -EPERM;
+ }
+
+ if (init->tbl_index >= 1) {
+ IPAERR_RL("Unsupported table index %d\n", init->tbl_index);
+ return -EPERM;
+ }
+
+ if (init->table_entries == 0) {
+ IPAERR_RL("Table entries is zero\n");
+ return -EPERM;
+ }
+
+ result = ipa3_nat_ipv6ct_check_table_params(
+ &ipa3_ctx->ipv6ct_mem.dev,
+ init->base_table_offset,
+ init->table_entries + 1,
+ IPAHAL_NAT_IPV6CT);
+ if (result) {
+ IPAERR_RL("Bad params for IPv6CT base table\n");
+ return result;
+ }
+
+ result = ipa3_nat_ipv6ct_check_table_params(
+ &ipa3_ctx->ipv6ct_mem.dev,
+ init->expn_table_offset,
+ init->expn_table_entries,
+ IPAHAL_NAT_IPV6CT);
+ if (result) {
+ IPAERR_RL("Bad params for IPv6CT expansion table\n");
+ return result;
+ }
+
+ /* NO-OP IC for ensuring that IPA pipeline is empty */
+ cmd_pyld[num_cmd] =
+ ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
+ if (!cmd_pyld[num_cmd]) {
+ IPAERR("failed to construct NOP imm cmd\n");
+ result = -ENOMEM;
+ goto bail;
+ }
+
+ ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+ ++num_cmd;
+
+ if (ipa3_ctx->ipv6ct_mem.dev.is_sys_mem) {
+ IPADBG("using system memory for nat table\n");
+ /*
+ * Safe to process, since integer overflow was
+ * checked in ipa3_nat_ipv6ct_check_table_params
+ */
+ ipa3_nat_ipv6ct_create_init_cmd(
+ &cmd.table_init,
+ false,
+ ipa3_ctx->ipv6ct_mem.dev.dma_handle,
+ init->tbl_index,
+ init->base_table_offset,
+ init->expn_table_offset,
+ init->table_entries,
+ init->expn_table_entries,
+ ipa3_ctx->ipv6ct_mem.dev.name);
+ } else {
+ IPADBG("using shared(local) memory for nat table\n");
+ ipa3_nat_ipv6ct_create_init_cmd(
+ &cmd.table_init,
+ true,
+ IPA_RAM_IPV6CT_OFST,
+ init->tbl_index,
+ init->base_table_offset,
+ init->expn_table_offset,
+ init->table_entries,
+ init->expn_table_entries,
+ ipa3_ctx->ipv6ct_mem.dev.name);
+ }
+
+ if (num_cmd >= IPA_IPV6CT_MAX_NUM_OF_INIT_CMD_DESC) {
+ IPAERR("number of commands is out of range\n");
+ result = -ENOBUFS;
+ goto destroy_imm_cmd;
+ }
+
+ cmd_pyld[num_cmd] = ipahal_construct_imm_cmd(
+ IPA_IMM_CMD_IP_V6_CT_INIT, &cmd, false);
+ if (!cmd_pyld[num_cmd]) {
+ IPAERR_RL("Fail to construct ip_v6_ct_init imm cmd\n");
+ result = -EPERM;
+ goto destroy_imm_cmd;
+ }
+
+ ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+ ++num_cmd;
+
+ IPADBG("posting ip_v6_ct_init imm command\n");
+ if (ipa3_send_cmd(num_cmd, desc)) {
+ IPAERR("Fail to send immediate command\n");
+ result = -EPERM;
+ goto destroy_imm_cmd;
+ }
+
+ ipa3_nat_ipv6ct_init_device_structure(
+ &ipa3_ctx->ipv6ct_mem.dev,
+ init->base_table_offset,
+ init->expn_table_offset,
+ init->table_entries,
+ init->expn_table_entries);
+
+ ipa3_ctx->ipv6ct_mem.dev.is_hw_init = true;
+ IPADBG("return\n");
+destroy_imm_cmd:
+ for (i = 0; i < num_cmd; ++i)
+ ipahal_destroy_imm_cmd(cmd_pyld[i]);
+bail:
+ return result;
+}
+
+/**
+ * ipa3_nat_mdfy_pdn() - Modify a PDN entry in PDN config table in IPA SRAM
+ * @mdfy_pdn: [in] PDN info to be written to SRAM
+ *
+ * Called by NAT client driver to modify an entry in the PDN config table
+ *
+ * Returns: 0 on success, negative on failure
+ */
int ipa3_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn)
{
struct ipahal_imm_cmd_dma_shared_mem mem_cmd = { 0 };
@@ -659,21 +1003,24 @@
struct ipa3_nat_mem *nat_ctx = &(ipa3_ctx->nat_mem);
struct ipa_pdn_entry *pdn_entries = nat_ctx->pdn_mem.base;
+ IPADBG("\n");
+
if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) {
- IPAERR("IPA HW does not support multi PDN\n");
+ IPAERR_RL("IPA HW does not support multi PDN\n");
return -EPERM;
}
- if (!nat_ctx->is_dev_init) {
- IPAERR("attempt to modify a PDN entry before dev init\n");
+ if (!nat_ctx->dev.is_mem_allocated) {
+ IPAERR_RL(
+ "attempt to modify a PDN entry before the PDN table memory allocation\n");
return -EPERM;
}
if (mdfy_pdn->pdn_index > (IPA_MAX_PDN_NUM - 1)) {
- IPAERR("pdn index out of range %d\n", mdfy_pdn->pdn_index);
+ IPAERR_RL("pdn index out of range %d\n", mdfy_pdn->pdn_index);
return -EPERM;
}
- mutex_lock(&nat_ctx->lock);
+ mutex_lock(&nat_ctx->dev.lock);
/* store ip in pdn entries cache array */
pdn_entries[mdfy_pdn->pdn_index].public_ip =
@@ -683,12 +1030,10 @@
pdn_entries[mdfy_pdn->pdn_index].src_metadata =
mdfy_pdn->src_metadata;
- IPADBG("Modify PDN in index %d: ", mdfy_pdn->pdn_index);
- IPADBG("Public ip address:0x%x, ", mdfy_pdn->public_ip);
- IPADBG("dst metadata:0x%x, ", mdfy_pdn->dst_metadata);
- IPADBG("src metadata:0x%x\n", mdfy_pdn->src_metadata);
-
- memset(&desc, 0, sizeof(desc));
+ IPADBG("Modify PDN in index: %d Public ip address:%pI4h\n",
+ mdfy_pdn->pdn_index, &mdfy_pdn->public_ip);
+ IPADBG("Modify PDN dst metadata: 0x%x src metadata: 0x%x\n",
+ mdfy_pdn->dst_metadata, mdfy_pdn->src_metadata);
/* Copy the PDN config table to SRAM */
mem_cmd.is_read = false;
@@ -706,25 +1051,200 @@
result = -ENOMEM;
goto bail;
}
- desc.opcode = cmd_pyld->opcode;
- desc.type = IPA_IMM_CMD_DESC;
- desc.callback = NULL;
- desc.user1 = NULL;
- desc.user2 = 0;
- desc.pyld = cmd_pyld->data;
- desc.len = cmd_pyld->len;
+ ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
- IPADBG("sending PDN table copy cmd");
+ IPADBG("sending PDN table copy cmd\n");
if (ipa3_send_cmd(1, &desc)) {
IPAERR("Fail to send immediate command\n");
result = -EPERM;
}
ipahal_destroy_imm_cmd(cmd_pyld);
+
+ IPADBG("return\n");
+
bail:
- mutex_unlock(&nat_ctx->lock);
+ mutex_unlock(&nat_ctx->dev.lock);
return result;
}
+
+static uint32_t ipa3_nat_ipv6ct_calculate_table_size(uint8_t base_addr)
+{
+ size_t entry_size;
+ u32 entries_num;
+ enum ipahal_nat_type nat_type;
+
+ switch (base_addr) {
+ case IPA_NAT_BASE_TBL:
+ entries_num = ipa3_ctx->nat_mem.dev.table_entries + 1;
+ nat_type = IPAHAL_NAT_IPV4;
+ break;
+ case IPA_NAT_EXPN_TBL:
+ entries_num = ipa3_ctx->nat_mem.dev.expn_table_entries;
+ nat_type = IPAHAL_NAT_IPV4;
+ break;
+ case IPA_NAT_INDX_TBL:
+ entries_num = ipa3_ctx->nat_mem.dev.table_entries + 1;
+ nat_type = IPAHAL_NAT_IPV4_INDEX;
+ break;
+ case IPA_NAT_INDEX_EXPN_TBL:
+ entries_num = ipa3_ctx->nat_mem.dev.expn_table_entries;
+ nat_type = IPAHAL_NAT_IPV4_INDEX;
+ break;
+ case IPA_IPV6CT_BASE_TBL:
+ entries_num = ipa3_ctx->ipv6ct_mem.dev.table_entries + 1;
+ nat_type = IPAHAL_NAT_IPV6CT;
+ break;
+ case IPA_IPV6CT_EXPN_TBL:
+ entries_num = ipa3_ctx->ipv6ct_mem.dev.expn_table_entries;
+ nat_type = IPAHAL_NAT_IPV6CT;
+ break;
+ default:
+ IPAERR_RL("Invalid base_addr %d for table DMA command\n",
+ base_addr);
+ return 0;
+ }
+
+ ipahal_nat_entry_size(nat_type, &entry_size);
+ return entry_size * entries_num;
+}
+
+static int ipa3_table_validate_table_dma_one(struct ipa_ioc_nat_dma_one *param)
+{
+ uint32_t table_size;
+
+ if (param->table_index >= 1) {
+ IPAERR_RL("Unsupported table index %d\n", param->table_index);
+ return -EPERM;
+ }
+
+ switch (param->base_addr) {
+ case IPA_NAT_BASE_TBL:
+ case IPA_NAT_EXPN_TBL:
+ case IPA_NAT_INDX_TBL:
+ case IPA_NAT_INDEX_EXPN_TBL:
+ if (!ipa3_ctx->nat_mem.dev.is_hw_init) {
+ IPAERR_RL("attempt to write to %s before HW int\n",
+ ipa3_ctx->nat_mem.dev.name);
+ return -EPERM;
+ }
+ break;
+ case IPA_IPV6CT_BASE_TBL:
+ case IPA_IPV6CT_EXPN_TBL:
+ if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) {
+ IPAERR_RL("IPv6 connection tracking isn't supported\n");
+ return -EPERM;
+ }
+
+ if (!ipa3_ctx->ipv6ct_mem.dev.is_hw_init) {
+ IPAERR_RL("attempt to write to %s before HW int\n",
+ ipa3_ctx->ipv6ct_mem.dev.name);
+ return -EPERM;
+ }
+ break;
+ default:
+ IPAERR_RL("Invalid base_addr %d for table DMA command\n",
+ param->base_addr);
+ return -EPERM;
+ }
+
+ table_size = ipa3_nat_ipv6ct_calculate_table_size(param->base_addr);
+ if (!table_size) {
+ IPAERR_RL("Failed to calculate table size for base_addr %d\n",
+ param->base_addr);
+ return -EPERM;
+ }
+
+ if (param->offset >= table_size) {
+ IPAERR_RL("Invalid offset %d for table DMA command\n",
+ param->offset);
+ IPAERR_RL("table_index %d base addr %d size %d\n",
+ param->table_index, param->base_addr, table_size);
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+
+/**
+ * ipa3_table_dma_cmd() - Post TABLE_DMA command to IPA HW
+ * @dma: [in] initialization command attributes
+ *
+ * Called by NAT/IPv6CT clients to post TABLE_DMA command to IPA HW
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa3_table_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
+{
+ struct ipahal_imm_cmd_table_dma cmd;
+ enum ipahal_imm_cmd_name cmd_name = IPA_IMM_CMD_NAT_DMA;
+ struct ipahal_imm_cmd_pyld *cmd_pyld[IPA_MAX_NUM_OF_TABLE_DMA_CMD_DESC];
+ struct ipa3_desc desc[IPA_MAX_NUM_OF_TABLE_DMA_CMD_DESC];
+ uint8_t cnt, num_cmd = 0;
+ int result = 0;
+
+ IPADBG("\n");
+ if (!dma->entries ||
+ dma->entries >= IPA_MAX_NUM_OF_TABLE_DMA_CMD_DESC) {
+ IPAERR_RL("Invalid number of entries %d\n",
+ dma->entries);
+ result = -EPERM;
+ goto bail;
+ }
+
+ for (cnt = 0; cnt < dma->entries; ++cnt) {
+ result = ipa3_table_validate_table_dma_one(&dma->dma[cnt]);
+ if (result) {
+ IPAERR_RL("Table DMA command parameter %d is invalid\n",
+ cnt);
+ goto bail;
+ }
+ }
+
+ /* NO-OP IC for ensuring that IPA pipeline is empty */
+ cmd_pyld[num_cmd] =
+ ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
+ if (!cmd_pyld[num_cmd]) {
+ IPAERR("Failed to construct NOP imm cmd\n");
+ result = -ENOMEM;
+ goto destroy_imm_cmd;
+ }
+ ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+ ++num_cmd;
+
+ /* NAT_DMA was renamed to TABLE_DMA starting from IPAv4 */
+ if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
+ cmd_name = IPA_IMM_CMD_TABLE_DMA;
+
+ for (cnt = 0; cnt < dma->entries; ++cnt) {
+ cmd.table_index = dma->dma[cnt].table_index;
+ cmd.base_addr = dma->dma[cnt].base_addr;
+ cmd.offset = dma->dma[cnt].offset;
+ cmd.data = dma->dma[cnt].data;
+ cmd_pyld[num_cmd] =
+ ipahal_construct_imm_cmd(cmd_name, &cmd, false);
+ if (!cmd_pyld[num_cmd]) {
+ IPAERR_RL("Fail to construct table_dma imm cmd\n");
+ result = -ENOMEM;
+ goto destroy_imm_cmd;
+ }
+ ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+ ++num_cmd;
+ }
+ result = ipa3_send_cmd(num_cmd, desc);
+ if (result == -EPERM)
+ IPAERR("Fail to send table_dma immediate command\n");
+
+ IPADBG("return\n");
+
+destroy_imm_cmd:
+ for (cnt = 0; cnt < num_cmd; ++cnt)
+ ipahal_destroy_imm_cmd(cmd_pyld[cnt]);
+bail:
+ return result;
+}
+
/**
* ipa3_nat_dma_cmd() - Post NAT_DMA command to IPA HW
* @dma: [in] initialization command attributes
@@ -735,188 +1255,74 @@
*/
int ipa3_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
{
-#define NUM_OF_DESC 2
+ return ipa3_table_dma_cmd(dma);
+}
- struct ipahal_imm_cmd_pyld *nop_cmd_pyld = NULL;
- struct ipahal_imm_cmd_nat_dma cmd;
- enum ipahal_imm_cmd_name cmd_name = IPA_IMM_CMD_NAT_DMA;
- struct ipahal_imm_cmd_pyld *cmd_pyld = NULL;
- struct ipa3_desc *desc = NULL;
- u16 size = 0, cnt = 0;
- int ret = 0;
-
+static void ipa3_nat_ipv6ct_free_mem(struct ipa3_nat_ipv6ct_common_mem *dev)
+{
IPADBG("\n");
- if (dma->entries <= 0) {
- IPAERR_RL("Invalid number of commands %d\n",
- dma->entries);
- ret = -EPERM;
- goto bail;
+ if (!dev->is_mem_allocated) {
+ IPADBG("attempt to delete %s before memory allocation\n",
+ dev->name);
+ /* Deletion of partly initialized table is not an error */
+ goto clear;
}
- for (cnt = 0; cnt < dma->entries; cnt++) {
- if (dma->dma[cnt].table_index >= 1) {
- IPAERR_RL("Invalid table index %d\n",
- dma->dma[cnt].table_index);
- ret = -EPERM;
- goto bail;
- }
-
- switch (dma->dma[cnt].base_addr) {
- case IPA_NAT_BASE_TBL:
- if (dma->dma[cnt].offset >=
- (ipa3_ctx->nat_mem.size_base_tables + 1) *
- NAT_TABLE_ENTRY_SIZE_BYTE) {
- IPAERR_RL("Invalid offset %d\n",
- dma->dma[cnt].offset);
- ret = -EPERM;
- goto bail;
- }
-
- break;
-
- case IPA_NAT_EXPN_TBL:
- if (dma->dma[cnt].offset >=
- ipa3_ctx->nat_mem.size_expansion_tables *
- NAT_TABLE_ENTRY_SIZE_BYTE) {
- IPAERR_RL("Invalid offset %d\n",
- dma->dma[cnt].offset);
- ret = -EPERM;
- goto bail;
- }
-
- break;
-
- case IPA_NAT_INDX_TBL:
- if (dma->dma[cnt].offset >=
- (ipa3_ctx->nat_mem.size_base_tables + 1) *
- NAT_INTEX_TABLE_ENTRY_SIZE_BYTE) {
- IPAERR_RL("Invalid offset %d\n",
- dma->dma[cnt].offset);
- ret = -EPERM;
- goto bail;
- }
-
- break;
-
- case IPA_NAT_INDEX_EXPN_TBL:
- if (dma->dma[cnt].offset >=
- ipa3_ctx->nat_mem.size_expansion_tables *
- NAT_INTEX_TABLE_ENTRY_SIZE_BYTE) {
- IPAERR_RL("Invalid offset %d\n",
- dma->dma[cnt].offset);
- ret = -EPERM;
- goto bail;
- }
-
- break;
-
- default:
- IPAERR_RL("Invalid base_addr %d\n",
- dma->dma[cnt].base_addr);
- ret = -EPERM;
- goto bail;
- }
+ if (dev->is_sys_mem) {
+ IPADBG("freeing the dma memory for %s\n", dev->name);
+ dma_free_coherent(
+ ipa3_ctx->pdev, dev->size,
+ dev->vaddr, dev->dma_handle);
+ dev->size = 0;
+ dev->vaddr = NULL;
}
- size = sizeof(struct ipa3_desc) * NUM_OF_DESC;
- desc = kzalloc(size, GFP_KERNEL);
- if (desc == NULL) {
- IPAERR("Failed to alloc memory\n");
- ret = -ENOMEM;
- goto bail;
- }
+ dev->is_mem_allocated = false;
- /* NO-OP IC for ensuring that IPA pipeline is empty */
- nop_cmd_pyld =
- ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
- if (!nop_cmd_pyld) {
- IPAERR("Failed to construct NOP imm cmd\n");
- ret = -ENOMEM;
- goto bail;
- }
- desc[0].type = IPA_IMM_CMD_DESC;
- desc[0].opcode = nop_cmd_pyld->opcode;
- desc[0].callback = NULL;
- desc[0].user1 = NULL;
- desc[0].user2 = 0;
- desc[0].pyld = nop_cmd_pyld->data;
- desc[0].len = nop_cmd_pyld->len;
+clear:
+ dev->table_entries = 0;
+ dev->expn_table_entries = 0;
+ dev->base_table_addr = NULL;
+ dev->expansion_table_addr = NULL;
- /* NAT_DMA was renamed to TABLE_DMA starting from IPAv4 */
- if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
- cmd_name = IPA_IMM_CMD_TABLE_DMA;
+ dev->is_hw_init = false;
+ dev->is_mapped = false;
+ dev->is_sys_mem = false;
- for (cnt = 0; cnt < dma->entries; cnt++) {
- cmd.table_index = dma->dma[cnt].table_index;
- cmd.base_addr = dma->dma[cnt].base_addr;
- cmd.offset = dma->dma[cnt].offset;
- cmd.data = dma->dma[cnt].data;
- cmd_pyld = ipahal_construct_imm_cmd(cmd_name, &cmd, false);
- if (!cmd_pyld) {
- IPAERR_RL("Fail to construct nat_dma imm cmd\n");
- continue;
- }
- desc[1].type = IPA_IMM_CMD_DESC;
- desc[1].opcode = cmd_pyld->opcode;
- desc[1].callback = NULL;
- desc[1].user1 = NULL;
- desc[1].user2 = 0;
- desc[1].pyld = cmd_pyld->data;
- desc[1].len = cmd_pyld->len;
-
- ret = ipa3_send_cmd(NUM_OF_DESC, desc);
- if (ret == -EPERM)
- IPAERR("Fail to send immediate command %d\n", cnt);
- ipahal_destroy_imm_cmd(cmd_pyld);
- }
-
-bail:
- if (desc != NULL)
- kfree(desc);
-
- if (nop_cmd_pyld != NULL)
- ipahal_destroy_imm_cmd(nop_cmd_pyld);
-
- return ret;
+ IPADBG("return\n");
}
/**
- * ipa3_nat_free_mem_and_device() - free the NAT memory and remove the device
- * @nat_ctx: [in] the IPA NAT memory to free
+ * ipa3_nat_free_mem() - free the NAT memory
*
- * Called by NAT client driver to free the NAT memory and remove the device
+ * Called by NAT client driver to free the NAT memory
*/
-void ipa3_nat_free_mem_and_device(struct ipa3_nat_mem *nat_ctx)
+static int ipa3_nat_free_mem(void)
{
struct ipahal_imm_cmd_dma_shared_mem mem_cmd = { 0 };
struct ipa3_desc desc;
struct ipahal_imm_cmd_pyld *cmd_pyld;
+ int result = 0;
IPADBG("\n");
- mutex_lock(&nat_ctx->lock);
+ mutex_lock(&ipa3_ctx->nat_mem.dev.lock);
- if (nat_ctx->is_sys_mem) {
- IPADBG("freeing the dma memory\n");
- dma_free_coherent(
- ipa3_ctx->pdev, nat_ctx->size,
- nat_ctx->vaddr, nat_ctx->dma_handle);
- nat_ctx->size = 0;
- nat_ctx->vaddr = NULL;
- }
+ ipa3_nat_ipv6ct_free_mem(&ipa3_ctx->nat_mem.dev);
if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
- struct ipa_pdn_entry *pdn_entries =
- nat_ctx->pdn_mem.base;
+ size_t pdn_entry_size;
+
+ ipahal_nat_entry_size(IPAHAL_NAT_IPV4_PDN, &pdn_entry_size);
/* zero the PDN table and copy the PDN config table to SRAM */
IPADBG("zeroing the PDN config table\n");
- memset(pdn_entries, 0, sizeof(struct ipa_pdn_entry) *
- IPA_MAX_PDN_NUM);
+ memset(ipa3_ctx->nat_mem.pdn_mem.base, 0,
+ pdn_entry_size * IPA_MAX_PDN_NUM);
mem_cmd.is_read = false;
mem_cmd.skip_pipeline_clear = false;
mem_cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
- mem_cmd.size = sizeof(struct ipa_pdn_entry) * IPA_MAX_PDN_NUM;
- mem_cmd.system_addr = nat_ctx->pdn_mem.phys_base;
+ mem_cmd.size = pdn_entry_size * IPA_MAX_PDN_NUM;
+ mem_cmd.system_addr = ipa3_ctx->nat_mem.pdn_mem.phys_base;
mem_cmd.local_addr = ipa3_ctx->smem_restricted_bytes +
IPA_MEM_PART(pdn_config_ofst);
cmd_pyld = ipahal_construct_imm_cmd(
@@ -924,32 +1330,97 @@
if (!cmd_pyld) {
IPAERR(
"fail construct dma_shared_mem cmd: for pdn table");
+ result = -ENOMEM;
goto lbl_free_pdn;
}
- memset(&desc, 0, sizeof(desc));
- desc.opcode = cmd_pyld->opcode;
- desc.pyld = cmd_pyld->data;
- desc.len = cmd_pyld->len;
- desc.type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
IPADBG("sending PDN table copy cmd\n");
- if (ipa3_send_cmd(1, &desc))
+ if (ipa3_send_cmd(1, &desc)) {
IPAERR("Fail to send immediate command\n");
+ result = -ENOMEM;
+ }
ipahal_destroy_imm_cmd(cmd_pyld);
lbl_free_pdn:
IPADBG("freeing the PDN memory\n");
dma_free_coherent(ipa3_ctx->pdev,
- nat_ctx->pdn_mem.size,
- nat_ctx->pdn_mem.base,
- nat_ctx->pdn_mem.phys_base);
+ ipa3_ctx->nat_mem.pdn_mem.size,
+ ipa3_ctx->nat_mem.pdn_mem.base,
+ ipa3_ctx->nat_mem.pdn_mem.phys_base);
}
- nat_ctx->is_mapped = false;
- nat_ctx->is_sys_mem = false;
- nat_ctx->is_dev_init = false;
- mutex_unlock(&nat_ctx->lock);
+ mutex_unlock(&ipa3_ctx->nat_mem.dev.lock);
IPADBG("return\n");
+ return result;
+}
+
+static int ipa3_nat_ipv6ct_send_del_table_cmd(
+ uint8_t tbl_index,
+ u32 base_addr,
+ bool mem_type_shared,
+ struct ipa3_nat_ipv6ct_common_mem *dev,
+ enum ipahal_imm_cmd_name cmd_name,
+ struct ipahal_imm_cmd_nat_ipv6ct_init_common *table_init_cmd,
+ const void *cmd)
+{
+ struct ipahal_imm_cmd_pyld *nop_cmd_pyld = NULL, *cmd_pyld = NULL;
+ struct ipa3_desc desc[IPA_MAX_NUM_OF_DEL_TABLE_CMD_DESC];
+ int result = 0;
+
+ IPADBG("\n");
+
+ if (!dev->is_hw_init) {
+ IPADBG("attempt to delete %s before HW int\n", dev->name);
+ /* Deletion of partly initialized table is not an error */
+ return 0;
+ }
+
+ if (tbl_index >= 1) {
+ IPAERR_RL("Unsupported table index %d\n", tbl_index);
+ return -EPERM;
+ }
+
+ /* NO-OP IC for ensuring that IPA pipeline is empty */
+ nop_cmd_pyld =
+ ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
+ if (!nop_cmd_pyld) {
+ IPAERR("Failed to construct NOP imm cmd\n");
+ result = -ENOMEM;
+ goto bail;
+ }
+ ipa3_init_imm_cmd_desc(&desc[0], nop_cmd_pyld);
+
+ table_init_cmd->table_index = tbl_index;
+ table_init_cmd->base_table_addr = base_addr;
+ table_init_cmd->base_table_addr_shared = mem_type_shared;
+ table_init_cmd->expansion_table_addr = base_addr;
+ table_init_cmd->expansion_table_addr_shared = mem_type_shared;
+ table_init_cmd->size_base_table = 0;
+ table_init_cmd->size_expansion_table = 0;
+ cmd_pyld = ipahal_construct_imm_cmd(cmd_name, &cmd, false);
+ if (!cmd_pyld) {
+ IPAERR_RL("Fail to construct table init imm cmd for %s\n",
+ dev->name);
+ result = -EPERM;
+ goto destroy_nop_imm_cmd;
+ }
+ ipa3_init_imm_cmd_desc(&desc[1], cmd_pyld);
+
+ if (ipa3_send_cmd(IPA_MAX_NUM_OF_DEL_TABLE_CMD_DESC, desc)) {
+ IPAERR("Fail to send immediate command\n");
+ result = -EPERM;
+ goto destroy_imm_cmd;
+ }
+
+ IPADBG("return\n");
+
+destroy_imm_cmd:
+ ipahal_destroy_imm_cmd(cmd_pyld);
+destroy_nop_imm_cmd:
+ ipahal_destroy_imm_cmd(nop_cmd_pyld);
+bail:
+ return result;
}
/**
@@ -962,94 +1433,122 @@
*/
int ipa3_nat_del_cmd(struct ipa_ioc_v4_nat_del *del)
{
- struct ipahal_imm_cmd_pyld *nop_cmd_pyld = NULL;
- struct ipa3_desc desc[2];
+ struct ipa_ioc_nat_ipv6ct_table_del tmp;
+
+ if ((ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) &&
+ (del->public_ip_addr == 0)) {
+ IPAERR_RL("Bad Parameter public IP address\n");
+ return -EPERM;
+ }
+
+ tmp.table_index = del->table_index;
+
+ return ipa3_del_nat_table(&tmp);
+}
+
+/**
+ * ipa3_del_nat_table() - Delete the NAT table
+ * @del: [in] delete table parameters
+ *
+ * Called by NAT client to delete the table
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa3_del_nat_table(struct ipa_ioc_nat_ipv6ct_table_del *del)
+{
struct ipahal_imm_cmd_ip_v4_nat_init cmd;
- struct ipahal_imm_cmd_pyld *cmd_pyld;
bool mem_type_shared = true;
u32 base_addr = IPA_NAT_PHYS_MEM_OFFSET;
int result;
IPADBG("\n");
- if (ipa3_ctx->nat_mem.is_tmp_mem) {
- IPAERR("using temp memory during nat del\n");
+ if (!ipa3_ctx->nat_mem.dev.is_dev_init) {
+ IPAERR("NAT hasn't been initialized\n");
+ return -EPERM;
+ }
+
+ if (ipa3_ctx->nat_mem.dev.tmp_mem != NULL) {
+ IPADBG("using temp memory during nat del\n");
mem_type_shared = false;
- base_addr = ipa3_ctx->nat_mem.tmp_dma_handle;
+ base_addr = ipa3_ctx->nat_mem.dev.tmp_mem->dma_handle;
}
- if ((ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) &&
- (del->public_ip_addr == 0)) {
- IPADBG("Bad Parameter\n");
- result = -EPERM;
- goto bail;
- }
-
- memset(&desc, 0, sizeof(desc));
- /* NO-OP IC for ensuring that IPA pipeline is empty */
- nop_cmd_pyld =
- ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
- if (!nop_cmd_pyld) {
- IPAERR("Failed to construct NOP imm cmd\n");
- result = -ENOMEM;
- goto bail;
- }
- desc[0].opcode = nop_cmd_pyld->opcode;
- desc[0].type = IPA_IMM_CMD_DESC;
- desc[0].callback = NULL;
- desc[0].user1 = NULL;
- desc[0].user2 = 0;
- desc[0].pyld = nop_cmd_pyld->data;
- desc[0].len = nop_cmd_pyld->len;
-
- cmd.table_index = del->table_index;
- cmd.ipv4_rules_addr = base_addr;
- cmd.ipv4_rules_addr_shared = mem_type_shared;
- cmd.ipv4_expansion_rules_addr = base_addr;
- cmd.ipv4_expansion_rules_addr_shared = mem_type_shared;
cmd.index_table_addr = base_addr;
cmd.index_table_addr_shared = mem_type_shared;
cmd.index_table_expansion_addr = base_addr;
cmd.index_table_expansion_addr_shared = mem_type_shared;
- cmd.size_base_tables = 0;
- cmd.size_expansion_tables = 0;
- cmd.public_ip_addr = 0;
- cmd_pyld = ipahal_construct_imm_cmd(
- IPA_IMM_CMD_IP_V4_NAT_INIT, &cmd, false);
- if (!cmd_pyld) {
- IPAERR_RL("Fail to construct ip_v4_nat_init imm cmd\n");
- result = -EPERM;
- goto destroy_regwrt_imm_cmd;
- }
- desc[1].opcode = cmd_pyld->opcode;
- desc[1].type = IPA_IMM_CMD_DESC;
- desc[1].callback = NULL;
- desc[1].user1 = NULL;
- desc[1].user2 = 0;
- desc[1].pyld = cmd_pyld->data;
- desc[1].len = cmd_pyld->len;
+ cmd.public_addr_info = 0;
- if (ipa3_send_cmd(2, desc)) {
- IPAERR("Fail to send immediate command\n");
- result = -EPERM;
- goto destroy_imm_cmd;
- }
+ result = ipa3_nat_ipv6ct_send_del_table_cmd(
+ del->table_index,
+ base_addr,
+ mem_type_shared,
+ &ipa3_ctx->nat_mem.dev,
+ IPA_IMM_CMD_IP_V4_NAT_INIT,
+ &cmd.table_init,
+ &cmd);
+ if (result)
+ goto bail;
- ipa3_ctx->nat_mem.size_base_tables = 0;
- ipa3_ctx->nat_mem.size_expansion_tables = 0;
ipa3_ctx->nat_mem.public_ip_addr = 0;
- ipa3_ctx->nat_mem.ipv4_rules_addr = 0;
- ipa3_ctx->nat_mem.ipv4_expansion_rules_addr = 0;
ipa3_ctx->nat_mem.index_table_addr = 0;
ipa3_ctx->nat_mem.index_table_expansion_addr = 0;
- ipa3_nat_free_mem_and_device(&ipa3_ctx->nat_mem);
+ result = ipa3_nat_free_mem();
IPADBG("return\n");
- result = 0;
-
-destroy_imm_cmd:
- ipahal_destroy_imm_cmd(cmd_pyld);
-destroy_regwrt_imm_cmd:
- ipahal_destroy_imm_cmd(nop_cmd_pyld);
bail:
return result;
}
+
+/**
+ * ipa3_del_ipv6ct_table() - Delete the IPv6CT table
+ * @del: [in] delete table parameters
+ *
+ * Called by IPv6CT client to delete the table
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa3_del_ipv6ct_table(struct ipa_ioc_nat_ipv6ct_table_del *del)
+{
+ struct ipahal_imm_cmd_ip_v6_ct_init cmd;
+ bool mem_type_shared = true;
+ u32 base_addr = IPA_IPV6CT_PHYS_MEM_OFFSET;
+ int result;
+
+ IPADBG("\n");
+
+ if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) {
+ IPAERR_RL("IPv6 connection tracking isn't supported\n");
+ return -EPERM;
+ }
+
+ if (!ipa3_ctx->ipv6ct_mem.dev.is_dev_init) {
+ IPAERR("IPv6 connection tracking hasn't been initialized\n");
+ return -EPERM;
+ }
+
+ if (ipa3_ctx->ipv6ct_mem.dev.tmp_mem != NULL) {
+ IPADBG("using temp memory during IPv6CT del\n");
+ mem_type_shared = false;
+ base_addr = ipa3_ctx->ipv6ct_mem.dev.tmp_mem->dma_handle;
+ }
+
+ result = ipa3_nat_ipv6ct_send_del_table_cmd(
+ del->table_index,
+ base_addr,
+ mem_type_shared,
+ &ipa3_ctx->ipv6ct_mem.dev,
+ IPA_IMM_CMD_IP_V6_CT_INIT,
+ &cmd.table_init,
+ &cmd);
+ if (result)
+ goto bail;
+
+ mutex_lock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+ ipa3_nat_ipv6ct_free_mem(&ipa3_ctx->ipv6ct_mem.dev);
+ mutex_unlock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+ IPADBG("return\n");
+bail:
+ return result;
+}
+
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
index 8d7b107..edba283 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
@@ -21,6 +21,8 @@
#define IPA_RT_STATUS_OF_DEL_FAILED (-1)
#define IPA_RT_STATUS_OF_MDFY_FAILED (-1)
+#define IPA_RT_MAX_NUM_OF_COMMIT_TABLES_CMD_DESC 5
+
#define IPA_RT_GET_RULE_TYPE(__entry) \
( \
((__entry)->rule.hashable) ? \
@@ -432,10 +434,11 @@
*/
int __ipa_commit_rt_v3(enum ipa_ip_type ip)
{
- struct ipa3_desc desc[5];
+ struct ipa3_desc desc[IPA_RT_MAX_NUM_OF_COMMIT_TABLES_CMD_DESC];
struct ipahal_imm_cmd_register_write reg_write_cmd = {0};
struct ipahal_imm_cmd_dma_shared_mem mem_cmd = {0};
- struct ipahal_imm_cmd_pyld *cmd_pyld[5];
+ struct ipahal_imm_cmd_pyld
+ *cmd_pyld[IPA_RT_MAX_NUM_OF_COMMIT_TABLES_CMD_DESC];
int num_cmd = 0;
struct ipahal_fltrt_alloc_imgs_params alloc_params;
u32 num_modem_rt_index;
@@ -557,10 +560,7 @@
IPAERR("fail construct register_write imm cmd. IP %d\n", ip);
goto fail_size_valid;
}
- desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
- desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
- desc[num_cmd].len = cmd_pyld[num_cmd]->len;
- desc[num_cmd].type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
num_cmd++;
mem_cmd.is_read = false;
@@ -575,10 +575,7 @@
IPAERR("fail construct dma_shared_mem imm cmd. IP %d\n", ip);
goto fail_imm_cmd_construct;
}
- desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
- desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
- desc[num_cmd].len = cmd_pyld[num_cmd]->len;
- desc[num_cmd].type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
num_cmd++;
mem_cmd.is_read = false;
@@ -593,13 +590,17 @@
IPAERR("fail construct dma_shared_mem imm cmd. IP %d\n", ip);
goto fail_imm_cmd_construct;
}
- desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
- desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
- desc[num_cmd].len = cmd_pyld[num_cmd]->len;
- desc[num_cmd].type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
num_cmd++;
if (lcl_nhash) {
+ if (num_cmd >= IPA_RT_MAX_NUM_OF_COMMIT_TABLES_CMD_DESC) {
+ IPAERR("number of commands is out of range: IP = %d\n",
+ ip);
+ rc = -ENOBUFS;
+ goto fail_imm_cmd_construct;
+ }
+
mem_cmd.is_read = false;
mem_cmd.skip_pipeline_clear = false;
mem_cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
@@ -613,13 +614,17 @@
ip);
goto fail_imm_cmd_construct;
}
- desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
- desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
- desc[num_cmd].len = cmd_pyld[num_cmd]->len;
- desc[num_cmd].type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
num_cmd++;
}
if (lcl_hash) {
+ if (num_cmd >= IPA_RT_MAX_NUM_OF_COMMIT_TABLES_CMD_DESC) {
+ IPAERR("number of commands is out of range: IP = %d\n",
+ ip);
+ rc = -ENOBUFS;
+ goto fail_imm_cmd_construct;
+ }
+
mem_cmd.is_read = false;
mem_cmd.skip_pipeline_clear = false;
mem_cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
@@ -633,10 +638,7 @@
ip);
goto fail_imm_cmd_construct;
}
- desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
- desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
- desc[num_cmd].len = cmd_pyld[num_cmd]->len;
- desc[num_cmd].type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
num_cmd++;
}
@@ -1137,8 +1139,8 @@
goto bail;
}
- if (tbl->rule_cnt <= 0) {
- IPAERR_RL("tbl->rule_cnt <= 0");
+ if (!tbl->rule_cnt) {
+ IPAERR_RL("tbl->rule_cnt == 0");
ret = -EINVAL;
goto bail;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index ae05880..979369a 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -2787,7 +2787,7 @@
ep_hdr->hdr_ofst_pkt_size_valid,
ep_hdr->hdr_additional_const_len);
- IPADBG("ofst_metadata=0x%x, ofst_metadata_valid=%d, len=0x%x",
+ IPADBG("ofst_metadata=0x%x, ofst_metadata_valid=%d, len=0x%x\n",
ep_hdr->hdr_ofst_metadata,
ep_hdr->hdr_ofst_metadata_valid,
ep_hdr->hdr_len);
@@ -2946,7 +2946,7 @@
if (!IPA_CLIENT_IS_CONS(ep_mode->dst))
ep = ipa3_get_ep_mapping(IPA_CLIENT_APPS_LAN_CONS);
- IPADBG("pipe=%d mode=%d(%s), dst_client_number=%d",
+ IPADBG("pipe=%d mode=%d(%s), dst_client_number=%d\n",
clnt_hdl,
ep_mode->mode,
ipa3_get_mode_type_str(ep_mode->mode),
@@ -3908,13 +3908,10 @@
res = -ENOMEM;
goto fail_free_tag_desc;
}
- tag_desc[desc_idx].opcode = cmd_pyld->opcode;
- tag_desc[desc_idx].pyld = cmd_pyld->data;
- tag_desc[desc_idx].len = cmd_pyld->len;
- tag_desc[desc_idx].type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&tag_desc[desc_idx], cmd_pyld);
tag_desc[desc_idx].callback = ipa3_tag_destroy_imm;
tag_desc[desc_idx].user1 = cmd_pyld;
- desc_idx++;
+ ++desc_idx;
/* IP_PACKET_INIT IC for tag status to be sent to apps */
pktinit_cmd.destination_pipe_index =
@@ -3926,13 +3923,10 @@
res = -ENOMEM;
goto fail_free_desc;
}
- tag_desc[desc_idx].opcode = cmd_pyld->opcode;
- tag_desc[desc_idx].pyld = cmd_pyld->data;
- tag_desc[desc_idx].len = cmd_pyld->len;
- tag_desc[desc_idx].type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&tag_desc[desc_idx], cmd_pyld);
tag_desc[desc_idx].callback = ipa3_tag_destroy_imm;
tag_desc[desc_idx].user1 = cmd_pyld;
- desc_idx++;
+ ++desc_idx;
/* status IC */
status.tag = IPA_COOKIE;
@@ -3943,13 +3937,10 @@
res = -ENOMEM;
goto fail_free_desc;
}
- tag_desc[desc_idx].opcode = cmd_pyld->opcode;
- tag_desc[desc_idx].pyld = cmd_pyld->data;
- tag_desc[desc_idx].len = cmd_pyld->len;
- tag_desc[desc_idx].type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&tag_desc[desc_idx], cmd_pyld);
tag_desc[desc_idx].callback = ipa3_tag_destroy_imm;
tag_desc[desc_idx].user1 = cmd_pyld;
- desc_idx++;
+ ++desc_idx;
comp = kzalloc(sizeof(*comp), GFP_KERNEL);
if (!comp) {
@@ -3972,6 +3963,12 @@
memcpy(skb_put(dummy_skb, sizeof(comp)), &comp, sizeof(comp));
+ if (desc_idx >= IPA_TAG_MAX_DESC) {
+ IPAERR("number of commands is out of range\n");
+ res = -ENOBUFS;
+ goto fail_free_skb;
+ }
+
tag_desc[desc_idx].pyld = dummy_skb->data;
tag_desc[desc_idx].len = dummy_skb->len;
tag_desc[desc_idx].type = IPA_DATA_DESC_SKB;
@@ -3984,7 +3981,7 @@
if (res) {
IPAERR("failed to send TAG packets %d\n", res);
res = -ENOMEM;
- goto fail_free_comp;
+ goto fail_free_skb;
}
kfree(tag_desc);
tag_desc = NULL;
@@ -4012,6 +4009,8 @@
return 0;
+fail_free_skb:
+ kfree_skb(dummy_skb);
fail_free_comp:
kfree(comp);
fail_free_desc:
@@ -4082,19 +4081,16 @@
goto fail_alloc_reg_write_agg_close;
}
- desc[desc_idx].opcode = cmd_pyld->opcode;
- desc[desc_idx].pyld = cmd_pyld->data;
- desc[desc_idx].len = cmd_pyld->len;
- desc[desc_idx].type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&desc[desc_idx], cmd_pyld);
desc[desc_idx].callback = ipa3_tag_destroy_imm;
desc[desc_idx].user1 = cmd_pyld;
- desc_idx++;
+ ++desc_idx;
}
return desc_idx;
fail_alloc_reg_write_agg_close:
- for (i = 0; i < desc_idx; i++)
+ for (i = 0; i < desc_idx; ++i)
if (desc[desc_idx].callback)
desc[desc_idx].callback(desc[desc_idx].user1,
desc[desc_idx].user2);
@@ -4377,10 +4373,17 @@
api_ctrl->ipa_mdfy_flt_rule = ipa3_mdfy_flt_rule;
api_ctrl->ipa_commit_flt = ipa3_commit_flt;
api_ctrl->ipa_reset_flt = ipa3_reset_flt;
- api_ctrl->allocate_nat_device = ipa3_allocate_nat_device;
+ api_ctrl->ipa_allocate_nat_device = ipa3_allocate_nat_device;
+ api_ctrl->ipa_allocate_nat_table = ipa3_allocate_nat_table;
+ api_ctrl->ipa_allocate_ipv6ct_table = ipa3_allocate_ipv6ct_table;
api_ctrl->ipa_nat_init_cmd = ipa3_nat_init_cmd;
+ api_ctrl->ipa_ipv6ct_init_cmd = ipa3_ipv6ct_init_cmd;
api_ctrl->ipa_nat_dma_cmd = ipa3_nat_dma_cmd;
+ api_ctrl->ipa_table_dma_cmd = ipa3_table_dma_cmd;
api_ctrl->ipa_nat_del_cmd = ipa3_nat_del_cmd;
+ api_ctrl->ipa_del_nat_table = ipa3_del_nat_table;
+ api_ctrl->ipa_del_ipv6ct_table = ipa3_del_ipv6ct_table;
+ api_ctrl->ipa_nat_mdfy_pdn = ipa3_nat_mdfy_pdn;
api_ctrl->ipa_send_msg = ipa3_send_msg;
api_ctrl->ipa_register_pull_msg = ipa3_register_pull_msg;
api_ctrl->ipa_deregister_pull_msg = ipa3_deregister_pull_msg;
@@ -4501,6 +4504,7 @@
api_ctrl->ipa_disconn_wdi3_pipes = ipa3_disconn_wdi3_pipes;
api_ctrl->ipa_enable_wdi3_pipes = ipa3_enable_wdi3_pipes;
api_ctrl->ipa_disable_wdi3_pipes = ipa3_disable_wdi3_pipes;
+ api_ctrl->ipa_tz_unlock_reg = ipa3_tz_unlock_reg;
return 0;
}
@@ -4996,12 +5000,9 @@
*/
int ipa3_inject_dma_task_for_gsi(void)
{
- struct ipa3_desc desc = {0};
+ struct ipa3_desc desc;
- desc.opcode = ipa3_ctx->dma_task_info.cmd_pyld->opcode;
- desc.pyld = ipa3_ctx->dma_task_info.cmd_pyld->data;
- desc.len = ipa3_ctx->dma_task_info.cmd_pyld->len;
- desc.type = IPA_IMM_CMD_DESC;
+ ipa3_init_imm_cmd_desc(&desc, ipa3_ctx->dma_task_info.cmd_pyld);
IPADBG("sending 1B packet to IPA\n");
if (ipa3_send_cmd_timeout(1, &desc,
@@ -5324,3 +5325,14 @@
ipahal_write_reg_fields(IPA_IDLE_INDICATION_CFG,
&idle_indication_cfg);
}
+
+void ipa3_init_imm_cmd_desc(struct ipa3_desc *desc,
+ struct ipahal_imm_cmd_pyld *cmd_pyld)
+{
+ memset(desc, 0, sizeof(*desc));
+ desc->opcode = cmd_pyld->opcode;
+ desc->pyld = cmd_pyld->data;
+ desc->len = cmd_pyld->len;
+ desc->type = IPA_IMM_CMD_DESC;
+}
+
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/Makefile b/drivers/platform/msm/ipa/ipa_v3/ipahal/Makefile
index 67e491b..869ee7e 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/Makefile
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_IPA3) += ipa_hal.o
-ipa_hal-y := ipahal.o ipahal_reg.o ipahal_fltrt.o ipahal_hw_stats.o
+ipa_hal-y := ipahal.o ipahal_reg.o ipahal_fltrt.o ipahal_hw_stats.o ipahal_nat.o
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
index a8d5342..d015b22 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
@@ -16,7 +16,7 @@
#include "ipahal_reg_i.h"
#include "ipahal_fltrt_i.h"
#include "ipahal_hw_stats_i.h"
-
+#include "ipahal_nat_i.h"
struct ipahal_context *ipahal_ctx;
@@ -35,6 +35,7 @@
__stringify(IPA_IMM_CMD_IP_PACKET_TAG_STATUS),
__stringify(IPA_IMM_CMD_DMA_TASK_32B_ADDR),
__stringify(IPA_IMM_CMD_TABLE_DMA),
+ __stringify(IPA_IMM_CMD_IP_V6_CT_INIT)
};
static const char *ipahal_pkt_status_exception_to_str
@@ -352,8 +353,8 @@
{
struct ipahal_imm_cmd_pyld *pyld;
struct ipa_imm_cmd_hw_nat_dma *data;
- struct ipahal_imm_cmd_nat_dma *nat_params =
- (struct ipahal_imm_cmd_nat_dma *)params;
+ struct ipahal_imm_cmd_table_dma *nat_params =
+ (struct ipahal_imm_cmd_table_dma *)params;
pyld = IPAHAL_MEM_ALLOC(sizeof(*pyld) + sizeof(*data), is_atomic_ctx);
if (unlikely(!pyld)) {
@@ -519,24 +520,55 @@
pyld->len = sizeof(*data);
data = (struct ipa_imm_cmd_hw_ip_v4_nat_init *)pyld->data;
- data->ipv4_rules_addr = nat4_params->ipv4_rules_addr;
+ data->ipv4_rules_addr = nat4_params->table_init.base_table_addr;
data->ipv4_expansion_rules_addr =
- nat4_params->ipv4_expansion_rules_addr;
+ nat4_params->table_init.expansion_table_addr;
data->index_table_addr = nat4_params->index_table_addr;
data->index_table_expansion_addr =
nat4_params->index_table_expansion_addr;
- data->table_index = nat4_params->table_index;
+ data->table_index = nat4_params->table_init.table_index;
data->ipv4_rules_addr_type =
- nat4_params->ipv4_rules_addr_shared ? 1 : 0;
+ nat4_params->table_init.base_table_addr_shared ? 1 : 0;
data->ipv4_expansion_rules_addr_type =
- nat4_params->ipv4_expansion_rules_addr_shared ? 1 : 0;
+ nat4_params->table_init.expansion_table_addr_shared ? 1 : 0;
data->index_table_addr_type =
nat4_params->index_table_addr_shared ? 1 : 0;
data->index_table_expansion_addr_type =
nat4_params->index_table_expansion_addr_shared ? 1 : 0;
- data->size_base_tables = nat4_params->size_base_tables;
- data->size_expansion_tables = nat4_params->size_expansion_tables;
- data->public_ip_addr = nat4_params->public_ip_addr;
+ data->size_base_tables = nat4_params->table_init.size_base_table;
+ data->size_expansion_tables =
+ nat4_params->table_init.size_expansion_table;
+ data->public_addr_info = nat4_params->public_addr_info;
+
+ return pyld;
+}
+
+static struct ipahal_imm_cmd_pyld *ipa_imm_cmd_construct_ip_v6_ct_init(
+ enum ipahal_imm_cmd_name cmd, const void *params, bool is_atomic_ctx)
+{
+ struct ipahal_imm_cmd_pyld *pyld;
+ struct ipa_imm_cmd_hw_ip_v6_ct_init *data;
+ struct ipahal_imm_cmd_ip_v6_ct_init *ipv6ct_params =
+ (struct ipahal_imm_cmd_ip_v6_ct_init *)params;
+
+ pyld = IPAHAL_MEM_ALLOC(sizeof(*pyld) + sizeof(*data), is_atomic_ctx);
+ if (unlikely(!pyld))
+ return pyld;
+ pyld->opcode = ipahal_imm_cmd_get_opcode(cmd);
+ pyld->len = sizeof(*data);
+ data = (struct ipa_imm_cmd_hw_ip_v6_ct_init *)pyld->data;
+
+ data->table_addr = ipv6ct_params->table_init.base_table_addr;
+ data->expansion_table_addr =
+ ipv6ct_params->table_init.expansion_table_addr;
+ data->table_index = ipv6ct_params->table_init.table_index;
+ data->table_addr_type =
+ ipv6ct_params->table_init.base_table_addr_shared ? 1 : 0;
+ data->expansion_table_addr_type =
+ ipv6ct_params->table_init.expansion_table_addr_shared ? 1 : 0;
+ data->size_base_table = ipv6ct_params->table_init.size_base_table;
+ data->size_expansion_table =
+ ipv6ct_params->table_init.size_expansion_table;
return pyld;
}
@@ -685,6 +717,9 @@
[IPA_HW_v4_0][IPA_IMM_CMD_DMA_SHARED_MEM] = {
ipa_imm_cmd_construct_dma_shared_mem_v_4_0,
19},
+ [IPA_HW_v4_0][IPA_IMM_CMD_IP_V6_CT_INIT] = {
+ ipa_imm_cmd_construct_ip_v6_ct_init,
+ 23}
};
/*
@@ -1526,13 +1561,21 @@
if (ipahal_hw_stats_init(ipa_hw_type)) {
IPAHAL_ERR("failed to init ipahal hw stats\n");
result = -EFAULT;
- goto bail_free_ctx;
+ goto bail_free_fltrt;
+ }
+
+ if (ipahal_nat_init(ipa_hw_type)) {
+ IPAHAL_ERR("failed to init ipahal NAT\n");
+ result = -EFAULT;
+ goto bail_free_fltrt;
}
ipahal_debugfs_init();
return 0;
+bail_free_fltrt:
+ ipahal_fltrt_destroy();
bail_free_ctx:
kfree(ipahal_ctx);
ipahal_ctx = NULL;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
index 56b884b..0c2697c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
@@ -37,6 +37,7 @@
IPA_IMM_CMD_IP_PACKET_TAG_STATUS,
IPA_IMM_CMD_DMA_TASK_32B_ADDR,
IPA_IMM_CMD_TABLE_DMA,
+ IPA_IMM_CMD_IP_V6_CT_INIT,
IPA_IMM_CMD_MAX,
};
@@ -46,19 +47,19 @@
* struct ipahal_imm_cmd_ip_v4_filter_init - IP_V4_FILTER_INIT cmd payload
* Inits IPv4 filter block.
* @hash_rules_addr: Addr in sys mem where ipv4 hashable flt tbl starts
+ * @nhash_rules_addr: Addr in sys mem where ipv4 non-hashable flt tbl starts
* @hash_rules_size: Size in bytes of the hashable tbl to cpy to local mem
* @hash_local_addr: Addr in shared mem where ipv4 hashable flt tbl should
* be copied to
- * @nhash_rules_addr: Addr in sys mem where ipv4 non-hashable flt tbl starts
* @nhash_rules_size: Size in bytes of the non-hashable tbl to cpy to local mem
* @nhash_local_addr: Addr in shared mem where ipv4 non-hashable flt tbl should
* be copied to
*/
struct ipahal_imm_cmd_ip_v4_filter_init {
u64 hash_rules_addr;
+ u64 nhash_rules_addr;
u32 hash_rules_size;
u32 hash_local_addr;
- u64 nhash_rules_addr;
u32 nhash_rules_size;
u32 nhash_local_addr;
};
@@ -67,79 +68,98 @@
* struct ipahal_imm_cmd_ip_v6_filter_init - IP_V6_FILTER_INIT cmd payload
* Inits IPv6 filter block.
* @hash_rules_addr: Addr in sys mem where ipv6 hashable flt tbl starts
+ * @nhash_rules_addr: Addr in sys mem where ipv6 non-hashable flt tbl starts
* @hash_rules_size: Size in bytes of the hashable tbl to cpy to local mem
* @hash_local_addr: Addr in shared mem where ipv6 hashable flt tbl should
* be copied to
- * @nhash_rules_addr: Addr in sys mem where ipv6 non-hashable flt tbl starts
* @nhash_rules_size: Size in bytes of the non-hashable tbl to cpy to local mem
* @nhash_local_addr: Addr in shared mem where ipv6 non-hashable flt tbl should
* be copied to
*/
struct ipahal_imm_cmd_ip_v6_filter_init {
u64 hash_rules_addr;
+ u64 nhash_rules_addr;
u32 hash_rules_size;
u32 hash_local_addr;
- u64 nhash_rules_addr;
u32 nhash_rules_size;
u32 nhash_local_addr;
};
/*
+ * struct ipahal_imm_cmd_nat_ipv6ct_init_common - NAT/IPv6CT table init command
+ * common part
+ * @base_table_addr: Address in sys/shared mem where base table start
+ * @expansion_table_addr: Address in sys/shared mem where expansion table
+ * starts. Entries that result in hash collision are located in this table.
+ * @base_table_addr_shared: base_table_addr in shared mem (if not, then sys)
+ * @expansion_table_addr_shared: expansion_rules_addr in
+ * shared mem (if not, then sys)
+ * @size_base_table: Num of entries in the base table
+ * @size_expansion_table: Num of entries in the expansion table
+ * @table_index: For future support of multiple tables
+ */
+struct ipahal_imm_cmd_nat_ipv6ct_init_common {
+ u64 base_table_addr;
+ u64 expansion_table_addr;
+ bool base_table_addr_shared;
+ bool expansion_table_addr_shared;
+ u16 size_base_table;
+ u16 size_expansion_table;
+ u8 table_index;
+};
+
+/*
* struct ipahal_imm_cmd_ip_v4_nat_init - IP_V4_NAT_INIT cmd payload
* Inits IPv4 NAT block. Initiate NAT table with it dimensions, location
- * cache address abd itger related parameters.
- * @table_index: For future support of multiple NAT tables
- * @ipv4_rules_addr: Addr in sys/shared mem where ipv4 NAT rules start
- * @ipv4_rules_addr_shared: ipv4_rules_addr in shared mem (if not, then sys)
- * @ipv4_expansion_rules_addr: Addr in sys/shared mem where expantion NAT
- * table starts. IPv4 NAT rules that result in NAT collision are located
- * in this table.
- * @ipv4_expansion_rules_addr_shared: ipv4_expansion_rules_addr in
- * shared mem (if not, then sys)
+ * cache address and other related parameters.
+ * @table_init: table initialization parameters
* @index_table_addr: Addr in sys/shared mem where index table, which points
* to NAT table starts
- * @index_table_addr_shared: index_table_addr in shared mem (if not, then sys)
* @index_table_expansion_addr: Addr in sys/shared mem where expansion index
* table starts
+ * @index_table_addr_shared: index_table_addr in shared mem (if not, then sys)
* @index_table_expansion_addr_shared: index_table_expansion_addr in
* shared mem (if not, then sys)
- * @size_base_tables: Num of entries in NAT tbl and idx tbl (each)
- * @size_expansion_tables: Num of entries in NAT expantion tbl and expantion
- * idx tbl (each)
- * @public_ip_addr: public IP address
+ * @public_addr_info: Public IP addresses info suitable to the IPA H/W version
+ * IPA H/W >= 4.0 - PDN config table offset in SMEM
+ * IPA H/W < 4.0 - The public IP address
*/
struct ipahal_imm_cmd_ip_v4_nat_init {
- u8 table_index;
- u64 ipv4_rules_addr;
- bool ipv4_rules_addr_shared;
- u64 ipv4_expansion_rules_addr;
- bool ipv4_expansion_rules_addr_shared;
+ struct ipahal_imm_cmd_nat_ipv6ct_init_common table_init;
u64 index_table_addr;
- bool index_table_addr_shared;
u64 index_table_expansion_addr;
+ bool index_table_addr_shared;
bool index_table_expansion_addr_shared;
- u16 size_base_tables;
- u16 size_expansion_tables;
- u32 public_ip_addr;
+ u32 public_addr_info;
+};
+
+/*
+ * struct ipahal_imm_cmd_ip_v6_ct_init - IP_V6_CONN_TRACK_INIT cmd payload
+ * Inits IPv6CT block. Initiate IPv6CT table with it dimensions, location
+ * cache address and other related parameters.
+ * @table_init: table initialization parameters
+ */
+struct ipahal_imm_cmd_ip_v6_ct_init {
+ struct ipahal_imm_cmd_nat_ipv6ct_init_common table_init;
};
/*
* struct ipahal_imm_cmd_ip_v4_routing_init - IP_V4_ROUTING_INIT cmd payload
* Inits IPv4 routing table/structure - with the rules and other related params
* @hash_rules_addr: Addr in sys mem where ipv4 hashable rt tbl starts
+ * @nhash_rules_addr: Addr in sys mem where ipv4 non-hashable rt tbl starts
* @hash_rules_size: Size in bytes of the hashable tbl to cpy to local mem
* @hash_local_addr: Addr in shared mem where ipv4 hashable rt tbl should
* be copied to
- * @nhash_rules_addr: Addr in sys mem where ipv4 non-hashable rt tbl starts
* @nhash_rules_size: Size in bytes of the non-hashable tbl to cpy to local mem
* @nhash_local_addr: Addr in shared mem where ipv4 non-hashable rt tbl should
* be copied to
*/
struct ipahal_imm_cmd_ip_v4_routing_init {
u64 hash_rules_addr;
+ u64 nhash_rules_addr;
u32 hash_rules_size;
u32 hash_local_addr;
- u64 nhash_rules_addr;
u32 nhash_rules_size;
u32 nhash_local_addr;
};
@@ -148,19 +168,19 @@
* struct ipahal_imm_cmd_ip_v6_routing_init - IP_V6_ROUTING_INIT cmd payload
* Inits IPv6 routing table/structure - with the rules and other related params
* @hash_rules_addr: Addr in sys mem where ipv6 hashable rt tbl starts
+ * @nhash_rules_addr: Addr in sys mem where ipv6 non-hashable rt tbl starts
* @hash_rules_size: Size in bytes of the hashable tbl to cpy to local mem
* @hash_local_addr: Addr in shared mem where ipv6 hashable rt tbl should
* be copied to
- * @nhash_rules_addr: Addr in sys mem where ipv6 non-hashable rt tbl starts
* @nhash_rules_size: Size in bytes of the non-hashable tbl to cpy to local mem
* @nhash_local_addr: Addr in shared mem where ipv6 non-hashable rt tbl should
* be copied to
*/
struct ipahal_imm_cmd_ip_v6_routing_init {
u64 hash_rules_addr;
+ u64 nhash_rules_addr;
u32 hash_rules_size;
u32 hash_local_addr;
- u64 nhash_rules_addr;
u32 nhash_rules_size;
u32 nhash_local_addr;
};
@@ -189,36 +209,20 @@
};
/*
- * struct ipahal_imm_cmd_nat_dma - NAT_DMA cmd payload
- * Perform DMA operation on NAT related mem addressess. Copy data into
- * different locations within NAT associated tbls. (For add/remove NAT rules)
- * @table_index: NAT tbl index. Defines the NAT tbl on which to perform DMA op.
- * @base_addr: Base addr to which the DMA operation should be performed.
- * @offset: offset in bytes from base addr to write 'data' to
- * @data: data to be written
- */
-struct ipahal_imm_cmd_nat_dma {
- u8 table_index;
- u8 base_addr;
- u32 offset;
- u16 data;
-};
-
-/*
* struct ipahal_imm_cmd_table_dma - TABLE_DMA cmd payload
* Perform DMA operation on NAT and IPV6 connection tracking related mem
- * addresses. Copy data into different locations within IPV6CT and NAT
+ * addresses. Copy data into different locations within IPv6CT and NAT
* associated tbls. (For add/remove NAT rules)
- * @table_index: NAT tbl index. Defines the tbl on which to perform DMA op.
- * @base_addr: Base addr to which the DMA operation should be performed.
* @offset: offset in bytes from base addr to write 'data' to
* @data: data to be written
+ * @table_index: NAT tbl index. Defines the tbl on which to perform DMA op.
+ * @base_addr: Base addr to which the DMA operation should be performed.
*/
struct ipahal_imm_cmd_table_dma {
- u8 table_index;
- u8 base_addr;
u32 offset;
u16 data;
+ u8 table_index;
+ u8 base_addr;
};
/*
@@ -275,6 +279,7 @@
/*
* struct ipahal_imm_cmd_dma_shared_mem - DMA_SHARED_MEM cmd payload
* Perform mem copy into or out of the SW area of IPA local mem
+ * @system_addr: Address in system memory
* @size: Size in bytes of data to copy. Expected size is up to 2K bytes
* @local_addr: Address in IPA local memory
* @clear_after_read: Clear local memory at the end of a read operation allows
@@ -282,16 +287,15 @@
* @is_read: Read operation from local memory? If not, then write.
* @skip_pipeline_clear: if to skip pipeline clear waiting (don't wait)
* @pipeline_clear_option: options for pipeline clear waiting
- * @system_addr: Address in system memory
*/
struct ipahal_imm_cmd_dma_shared_mem {
+ u64 system_addr;
u32 size;
u32 local_addr;
bool clear_after_read;
bool is_read;
bool skip_pipeline_clear;
enum ipahal_pipeline_clear_option pipeline_clear_options;
- u64 system_addr;
};
/*
@@ -515,6 +519,7 @@
* following statuses: IPA_STATUS_PACKET, IPA_STATUS_DROPPED_PACKET,
* IPA_STATUS_SUSPENDED_PACKET.
* Other statuses types has different status packet structure.
+ * @tag_info: S/W defined value provided via immediate command
* @status_opcode: The Type of the status (Opcode).
* @exception: The first exception that took place.
* In case of exception, src endp and pkt len are always valid.
@@ -522,9 +527,6 @@
* and processing it may passed at IPA. See enum ipahal_pkt_status_mask
* @pkt_len: Pkt pyld len including hdr and retained hdr if used. Does
* not include padding or checksum trailer len.
- * @endp_src_idx: Source end point index.
- * @endp_dest_idx: Destination end point index.
- * Not valid in case of exception
* @metadata: meta data value used by packet
* @flt_local: Filter table location flag: Does matching flt rule belongs to
* flt tbl that resides in lcl memory? (if not, then system mem)
@@ -535,57 +537,59 @@
* specifies to retain header?
* @flt_miss: Filtering miss flag: Was their a filtering rule miss?
* In case of miss, all flt info to be ignored
- * @flt_rule_id: The ID of the matching filter rule (if no miss).
- * This info can be combined with endp_src_idx to locate the exact rule.
* @rt_local: Route table location flag: Does matching rt rule belongs to
* rt tbl that resides in lcl memory? (if not, then system mem)
* @rt_hash: Route hash hit flag: Does matching rt rule was in hash tbl?
* @ucp: UC Processing flag
- * @rt_tbl_idx: Index of rt tbl that contains the rule on which was a match
* @rt_miss: Routing miss flag: Was their a routing rule miss?
- * @rt_rule_id: The ID of the matching rt rule. (if no miss). This info
- * can be combined with rt_tbl_idx to locate the exact rule.
* @nat_hit: NAT hit flag: Was their NAT hit?
- * @nat_entry_idx: Index of the NAT entry used of NAT processing
* @nat_type: Defines the type of the NAT operation:
- * @tag_info: S/W defined value provided via immediate command
- * @seq_num: Per source endp unique packet sequence number
* @time_of_day_ctr: running counter from IPA clock
* @hdr_local: Header table location flag: In header insertion, was the header
* taken from the table resides in local memory? (If no, then system mem)
- * @hdr_offset: Offset of used header in the header table
* @frag_hit: Frag hit flag: Was their frag rule hit in H/W frag table?
+ * @flt_rule_id: The ID of the matching filter rule (if no miss).
+ * This info can be combined with endp_src_idx to locate the exact rule.
+ * @rt_rule_id: The ID of the matching rt rule. (if no miss). This info
+ * can be combined with rt_tbl_idx to locate the exact rule.
+ * @nat_entry_idx: Index of the NAT entry used of NAT processing
+ * @hdr_offset: Offset of used header in the header table
+ * @endp_src_idx: Source end point index.
+ * @endp_dest_idx: Destination end point index.
+ * Not valid in case of exception
+ * @rt_tbl_idx: Index of rt tbl that contains the rule on which was a match
+ * @seq_num: Per source endp unique packet sequence number
* @frag_rule: Frag rule index in H/W frag table in case of frag hit
*/
struct ipahal_pkt_status {
+ u64 tag_info;
enum ipahal_pkt_status_opcode status_opcode;
enum ipahal_pkt_status_exception exception;
u32 status_mask;
u32 pkt_len;
- u8 endp_src_idx;
- u8 endp_dest_idx;
u32 metadata;
bool flt_local;
bool flt_hash;
bool flt_global;
bool flt_ret_hdr;
bool flt_miss;
- u16 flt_rule_id;
bool rt_local;
bool rt_hash;
bool ucp;
- u8 rt_tbl_idx;
bool rt_miss;
- u16 rt_rule_id;
bool nat_hit;
- u16 nat_entry_idx;
enum ipahal_pkt_status_nat_type nat_type;
- u64 tag_info;
- u8 seq_num;
u32 time_of_day_ctr;
bool hdr_local;
- u16 hdr_offset;
bool frag_hit;
+ u16 flt_rule_id;
+ u16 rt_rule_id;
+ u16 nat_entry_idx;
+ u16 hdr_offset;
+ u8 endp_src_idx;
+ u8 endp_dest_idx;
+ u8 rt_tbl_idx;
+ u8 seq_num;
u8 frag_rule;
};
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
index 5eb1aef..4ccb7e0 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
@@ -125,10 +125,10 @@
* struct ipa_imm_cmd_hw_ip_v4_nat_init - IP_V4_NAT_INIT command payload
* in H/W format.
* Inits IPv4 NAT block. Initiate NAT table with it dimensions, location
- * cache address abd itger related parameters.
+ * cache address and other related parameters.
* @ipv4_rules_addr: Addr in sys/shared mem where ipv4 NAT rules start
- * @ipv4_expansion_rules_addr: Addr in sys/shared mem where expantion NAT
- * table starts. IPv4 NAT rules that result in NAT collision are located
+ * @ipv4_expansion_rules_addr: Addr in sys/shared mem where expansion NAT
+ * table starts. IPv4 NAT rules that result in hash collision are located
* in this table.
* @index_table_addr: Addr in sys/shared mem where index table, which points
* to NAT table starts
@@ -143,11 +143,12 @@
* @index_table_expansion_addr_type: index_table_expansion_addr in
* sys or shared mem
* @size_base_tables: Num of entries in NAT tbl and idx tbl (each)
- * @size_expansion_tables: Num of entries in NAT expantion tbl and expantion
+ * @size_expansion_tables: Num of entries in NAT expansion tbl and expansion
* idx tbl (each)
* @rsvd2: reserved
- * @public_ip_addr: public IP address. for IPAv4 this is the PDN config table
- * offset in SMEM
+ * @public_addr_info: Public IP addresses info suitable to the IPA H/W version
+ * IPA H/W >= 4.0 - PDN config table offset in SMEM
+ * IPA H/W < 4.0 - The public IP address
*/
struct ipa_imm_cmd_hw_ip_v4_nat_init {
u64 ipv4_rules_addr:64;
@@ -163,7 +164,38 @@
u64 size_base_tables:12;
u64 size_expansion_tables:10;
u64 rsvd2:2;
- u64 public_ip_addr:32;
+ u64 public_addr_info:32;
+};
+
+/*
+ * struct ipa_imm_cmd_hw_ip_v6_ct_init - IP_V6_CONN_TRACK_INIT command payload
+ * in H/W format.
+ * Inits IPv6CT block. Initiate IPv6CT table with it dimensions, location
+ * cache address and other related parameters.
+ * @table_addr: Address in sys/shared mem where IPv6CT rules start
+ * @expansion_table_addr: Address in sys/shared mem where IPv6CT expansion
+ * table starts. IPv6CT rules that result in hash collision are located
+ * in this table.
+ * @table_index: For future support of multiple IPv6CT tables
+ * @rsvd1: reserved
+ * @table_addr_type: table_addr in sys or shared mem
+ * @expansion_table_addr_type: expansion_table_addr in sys or shared mem
+ * @rsvd2: reserved
+ * @size_base_tables: Number of entries in IPv6CT table
+ * @size_expansion_tables: Number of entries in IPv6CT expansion table
+ * @rsvd3: reserved
+ */
+struct ipa_imm_cmd_hw_ip_v6_ct_init {
+ u64 table_addr:64;
+ u64 expansion_table_addr:64;
+ u64 table_index:3;
+ u64 rsvd1:1;
+ u64 table_addr_type:1;
+ u64 expansion_table_addr_type:1;
+ u64 rsvd2:2;
+ u64 size_base_table:12;
+ u64 size_expansion_table:10;
+ u64 rsvd3:34;
};
/*
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.c
new file mode 100644
index 0000000..d335ba6
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.c
@@ -0,0 +1,360 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/debugfs.h>
+#include "ipahal_nat.h"
+#include "ipahal_nat_i.h"
+#include "ipahal_i.h"
+
+#define IPA_64_LOW_32_MASK (0xFFFFFFFF)
+#define IPA_64_HIGH_32_MASK (0xFFFFFFFF00000000ULL)
+
+static const char *ipahal_nat_type_to_str[IPA_NAT_MAX] = {
+ __stringify(IPAHAL_NAT_IPV4),
+ __stringify(IPAHAL_NAT_IPV4_INDEX),
+ __stringify(IPAHAL_NAT_IPV4_PDN),
+ __stringify(IPAHAL_NAT_IPV6CT)
+};
+
+static size_t ipa_nat_ipv4_entry_size_v_3_0(void)
+{
+ return sizeof(struct ipa_nat_hw_ipv4_entry);
+}
+
+static size_t ipa_nat_ipv4_index_entry_size_v_3_0(void)
+{
+ return sizeof(struct ipa_nat_hw_indx_entry);
+}
+
+static size_t ipa_nat_ipv4_pdn_entry_size_v_4_0(void)
+{
+ return sizeof(struct ipa_nat_hw_pdn_entry);
+}
+
+static size_t ipa_nat_ipv6ct_entry_size_v_4_0(void)
+{
+ return sizeof(struct ipa_nat_hw_ipv6ct_entry);
+}
+
+static bool ipa_nat_ipv4_is_entry_zeroed_v_3_0(const void *entry)
+{
+ struct ipa_nat_hw_ipv4_entry zero_entry = { 0 };
+
+ return (memcmp(&zero_entry, entry, sizeof(zero_entry))) ? false : true;
+}
+
+static bool ipa_nat_ipv4_is_index_entry_zeroed_v_3_0(const void *entry)
+{
+ struct ipa_nat_hw_indx_entry zero_entry = { 0 };
+
+ return (memcmp(&zero_entry, entry, sizeof(zero_entry))) ? false : true;
+}
+
+static bool ipa_nat_ipv4_is_pdn_entry_zeroed_v_4_0(const void *entry)
+{
+ struct ipa_nat_hw_pdn_entry zero_entry = { 0 };
+
+ return (memcmp(&zero_entry, entry, sizeof(zero_entry))) ? false : true;
+}
+
+static bool ipa_nat_ipv6ct_is_entry_zeroed_v_4_0(const void *entry)
+{
+ struct ipa_nat_hw_ipv6ct_entry zero_entry = { 0 };
+
+ return (memcmp(&zero_entry, entry, sizeof(zero_entry))) ? false : true;
+}
+
+static int ipa_nat_ipv4_stringify_entry_v_3_0(const void *entry,
+ char *buff, size_t buff_size)
+{
+ const struct ipa_nat_hw_ipv4_entry *nat_entry =
+ (const struct ipa_nat_hw_ipv4_entry *)entry;
+
+ return scnprintf(buff, buff_size,
+ "\t\tPrivate_IP=%pI4h Target_IP=%pI4h\n"
+ "\t\tNext_Index=%d Public_Port=%d\n"
+ "\t\tPrivate_Port=%d Target_Port=%d\n"
+ "\t\tIP_CKSM_delta=0x%x Enable=%s Redirect=%s\n"
+ "\t\tTime_stamp=0x%x Proto=%d\n"
+ "\t\tPrev_Index=%d Indx_tbl_entry=%d\n"
+ "\t\tTCP_UDP_cksum_delta=0x%x\n",
+ &nat_entry->private_ip, &nat_entry->target_ip,
+ nat_entry->next_index, nat_entry->public_port,
+ nat_entry->private_port, nat_entry->target_port,
+ nat_entry->ip_chksum,
+ (nat_entry->enable) ? "true" : "false",
+ (nat_entry->redirect) ? "Direct_To_APPS" : "Fwd_to_route",
+ nat_entry->time_stamp, nat_entry->protocol,
+ nat_entry->prev_index, nat_entry->indx_tbl_entry,
+ nat_entry->tcp_udp_chksum);
+}
+
+static int ipa_nat_ipv4_stringify_entry_v_4_0(const void *entry,
+ char *buff, size_t buff_size)
+{
+ int length;
+ const struct ipa_nat_hw_ipv4_entry *nat_entry =
+ (const struct ipa_nat_hw_ipv4_entry *)entry;
+
+ length = ipa_nat_ipv4_stringify_entry_v_3_0(entry, buff, buff_size);
+
+ length += scnprintf(buff + length, buff_size - length,
+ "\t\tPDN_Index=%d\n", nat_entry->pdn_index);
+
+ return length;
+}
+
+static int ipa_nat_ipv4_index_stringify_entry_v_3_0(const void *entry,
+ char *buff, size_t buff_size)
+{
+ const struct ipa_nat_hw_indx_entry *index_entry =
+ (const struct ipa_nat_hw_indx_entry *)entry;
+
+ return scnprintf(buff, buff_size,
+ "\t\tTable_Entry=%d Next_Index=%d\n",
+ index_entry->tbl_entry, index_entry->next_index);
+}
+
+static int ipa_nat_ipv4_pdn_stringify_entry_v_4_0(const void *entry,
+ char *buff, size_t buff_size)
+{
+ const struct ipa_nat_hw_pdn_entry *pdn_entry =
+ (const struct ipa_nat_hw_pdn_entry *)entry;
+
+ return scnprintf(buff, buff_size,
+ "ip=%pI4h src_metadata=0x%X, dst_metadata=0x%X\n",
+ &pdn_entry->public_ip,
+ pdn_entry->src_metadata, pdn_entry->dst_metadata);
+}
+
+static inline int ipa_nat_ipv6_stringify_addr(char *buff, size_t buff_size,
+ const char *msg, u64 lsb, u64 msb)
+{
+ struct in6_addr addr;
+
+ addr.s6_addr32[0] = cpu_to_be32((msb & IPA_64_HIGH_32_MASK) >> 32);
+ addr.s6_addr32[1] = cpu_to_be32(msb & IPA_64_LOW_32_MASK);
+ addr.s6_addr32[2] = cpu_to_be32((lsb & IPA_64_HIGH_32_MASK) >> 32);
+ addr.s6_addr32[3] = cpu_to_be32(lsb & IPA_64_LOW_32_MASK);
+
+ return scnprintf(buff, buff_size,
+ "\t\t%s_IPv6_Addr=%pI6c\n", msg, &addr);
+}
+
+static int ipa_nat_ipv6ct_stringify_entry_v_4_0(const void *entry,
+ char *buff, size_t buff_size)
+{
+ int length = 0;
+ const struct ipa_nat_hw_ipv6ct_entry *ipv6ct_entry =
+ (const struct ipa_nat_hw_ipv6ct_entry *)entry;
+
+ length += ipa_nat_ipv6_stringify_addr(
+ buff + length,
+ buff_size - length,
+ "Src",
+ ipv6ct_entry->src_ipv6_lsb,
+ ipv6ct_entry->src_ipv6_msb);
+
+ length += ipa_nat_ipv6_stringify_addr(
+ buff + length,
+ buff_size - length,
+ "Dest",
+ ipv6ct_entry->dest_ipv6_lsb,
+ ipv6ct_entry->dest_ipv6_msb);
+
+ length += scnprintf(buff + length, buff_size - length,
+ "\t\tEnable=%s Redirect=%s Time_Stamp=0x%x Proto=%d\n"
+ "\t\tNext_Index=%d Dest_Port=%d Src_Port=%d\n"
+ "\t\tDirection Settings: Out=%s In=%s\n"
+ "\t\tPrev_Index=%d\n",
+ (ipv6ct_entry->enable) ? "true" : "false",
+ (ipv6ct_entry->redirect) ? "Direct_To_APPS" : "Fwd_to_route",
+ ipv6ct_entry->time_stamp,
+ ipv6ct_entry->protocol,
+ ipv6ct_entry->next_index,
+ ipv6ct_entry->dest_port,
+ ipv6ct_entry->src_port,
+ (ipv6ct_entry->out_allowed) ? "Allow" : "Deny",
+ (ipv6ct_entry->in_allowed) ? "Allow" : "Deny",
+ ipv6ct_entry->prev_index);
+
+ return length;
+}
+
+/*
+ * struct ipahal_nat_obj - H/W information for specific IPA version
+ * @entry_size - CB to get the size of the entry
+ * @is_entry_zeroed - CB to determine whether an entry is definitely zero
+ * @stringify_entry - CB to create string that represents an entry
+ */
+struct ipahal_nat_obj {
+ size_t (*entry_size)(void);
+ bool (*is_entry_zeroed)(const void *entry);
+ int (*stringify_entry)(const void *entry, char *buff, size_t buff_size);
+};
+
+/*
+ * This table contains the info regard each NAT type for IPAv3 and later.
+ * Information like: get entry size and stringify entry functions.
+ * All the information on all the NAT types on IPAv3 are statically
+ * defined below. If information is missing regard some NAT type on some
+ * IPA version, the init function will fill it with the information from the
+ * previous IPA version.
+ * Information is considered missing if all of the fields are 0
+ */
+static struct ipahal_nat_obj ipahal_nat_objs[IPA_HW_MAX][IPA_NAT_MAX] = {
+ /* IPAv3 */
+ [IPA_HW_v3_0][IPAHAL_NAT_IPV4] = {
+ ipa_nat_ipv4_entry_size_v_3_0,
+ ipa_nat_ipv4_is_entry_zeroed_v_3_0,
+ ipa_nat_ipv4_stringify_entry_v_3_0
+ },
+ [IPA_HW_v3_0][IPAHAL_NAT_IPV4_INDEX] = {
+ ipa_nat_ipv4_index_entry_size_v_3_0,
+ ipa_nat_ipv4_is_index_entry_zeroed_v_3_0,
+ ipa_nat_ipv4_index_stringify_entry_v_3_0
+ },
+
+ /* IPAv4 */
+ [IPA_HW_v4_0][IPAHAL_NAT_IPV4] = {
+ ipa_nat_ipv4_entry_size_v_3_0,
+ ipa_nat_ipv4_is_entry_zeroed_v_3_0,
+ ipa_nat_ipv4_stringify_entry_v_4_0
+ },
+ [IPA_HW_v4_0][IPAHAL_NAT_IPV4_PDN] = {
+ ipa_nat_ipv4_pdn_entry_size_v_4_0,
+ ipa_nat_ipv4_is_pdn_entry_zeroed_v_4_0,
+ ipa_nat_ipv4_pdn_stringify_entry_v_4_0
+ },
+ [IPA_HW_v4_0][IPAHAL_NAT_IPV6CT] = {
+ ipa_nat_ipv6ct_entry_size_v_4_0,
+ ipa_nat_ipv6ct_is_entry_zeroed_v_4_0,
+ ipa_nat_ipv6ct_stringify_entry_v_4_0
+ }
+};
+
+static void ipahal_nat_check_obj(struct ipahal_nat_obj *obj,
+ int nat_type, int ver)
+{
+ WARN(obj->entry_size == NULL, "%s missing entry_size for version %d\n",
+ ipahal_nat_type_str(nat_type), ver);
+ WARN(obj->is_entry_zeroed == NULL,
+ "%s missing is_entry_zeroed for version %d\n",
+ ipahal_nat_type_str(nat_type), ver);
+ WARN(obj->stringify_entry == NULL,
+ "%s missing stringify_entry for version %d\n",
+ ipahal_nat_type_str(nat_type), ver);
+}
+
+/*
+ * ipahal_nat_init() - Build the NAT information table
+ * See ipahal_nat_objs[][] comments
+ */
+int ipahal_nat_init(enum ipa_hw_type ipa_hw_type)
+{
+ int i;
+ int j;
+ struct ipahal_nat_obj zero_obj, *next_obj;
+
+ IPAHAL_DBG("Entry - HW_TYPE=%d\n", ipa_hw_type);
+
+ memset(&zero_obj, 0, sizeof(zero_obj));
+
+ if ((ipa_hw_type < 0) || (ipa_hw_type >= IPA_HW_MAX)) {
+ IPAHAL_ERR("invalid IPA HW type (%d)\n", ipa_hw_type);
+ return -EINVAL;
+ }
+
+ for (i = IPA_HW_v3_0 ; i < ipa_hw_type ; ++i) {
+ for (j = 0; j < IPA_NAT_MAX; ++j) {
+ next_obj = &ipahal_nat_objs[i + 1][j];
+ if (!memcmp(next_obj, &zero_obj, sizeof(*next_obj))) {
+ memcpy(next_obj, &ipahal_nat_objs[i][j],
+ sizeof(*next_obj));
+ } else {
+ ipahal_nat_check_obj(next_obj, j, i + 1);
+ }
+ }
+ }
+
+ return 0;
+}
+
+const char *ipahal_nat_type_str(enum ipahal_nat_type nat_type)
+{
+ if (nat_type < 0 || nat_type >= IPA_NAT_MAX) {
+ IPAHAL_ERR("requested NAT type %d is invalid\n", nat_type);
+ return "Invalid NAT type";
+ }
+
+ return ipahal_nat_type_to_str[nat_type];
+}
+
+int ipahal_nat_entry_size(enum ipahal_nat_type nat_type, size_t *entry_size)
+{
+ if (WARN(entry_size == NULL, "entry_size is NULL\n"))
+ return -EINVAL;
+ if (WARN(nat_type < 0 || nat_type >= IPA_NAT_MAX,
+ "requested NAT type %d is invalid\n", nat_type))
+ return -EINVAL;
+
+ IPAHAL_DBG("Get the entry size for NAT type=%s\n",
+ ipahal_nat_type_str(nat_type));
+ *entry_size = ipahal_nat_objs[ipahal_ctx->hw_type][nat_type].
+ entry_size();
+ IPAHAL_DBG("The entry size is %zu\n", *entry_size);
+
+ return 0;
+}
+
+int ipahal_nat_is_entry_zeroed(enum ipahal_nat_type nat_type, void *entry,
+ bool *entry_zeroed)
+{
+ if (WARN(entry == NULL || entry_zeroed == NULL,
+ "NULL pointer received\n"))
+ return -EINVAL;
+ if (WARN(nat_type < 0 || nat_type >= IPA_NAT_MAX,
+ "requested NAT type %d is invalid\n", nat_type))
+ return -EINVAL;
+
+ IPAHAL_DBG("Determine whether the entry is zeroed for NAT type=%s\n",
+ ipahal_nat_type_str(nat_type));
+ *entry_zeroed = ipahal_nat_objs[ipahal_ctx->hw_type][nat_type].
+ is_entry_zeroed(entry);
+ IPAHAL_DBG("The entry is %szeroed\n", (*entry_zeroed) ? "" : "not ");
+
+ return 0;
+}
+
+int ipahal_nat_stringify_entry(enum ipahal_nat_type nat_type, void *entry,
+ char *buff, size_t buff_size)
+{
+ int result;
+
+ if (WARN(entry == NULL || buff == NULL, "NULL pointer received\n"))
+ return -EINVAL;
+ if (WARN(!buff_size, "The output buff size is zero\n"))
+ return -EINVAL;
+ if (WARN(nat_type < 0 || nat_type >= IPA_NAT_MAX,
+ "requested NAT type %d is invalid\n", nat_type))
+ return -EINVAL;
+
+ IPAHAL_DBG("Create the string for the entry of NAT type=%s\n",
+ ipahal_nat_type_str(nat_type));
+ result = ipahal_nat_objs[ipahal_ctx->hw_type][nat_type].
+ stringify_entry(entry, buff, buff_size);
+ IPAHAL_DBG("The string successfully created with length %d\n",
+ result);
+
+ return result;
+}
+
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.h
new file mode 100644
index 0000000..f99c1a0
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.h
@@ -0,0 +1,67 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _IPAHAL_NAT_H_
+#define _IPAHAL_NAT_H_
+
+/*
+ * NAT types
+ *
+ * NOTE:: Any change to this enum, need to change to ipahal_nat_to_str
+ * array as well.
+ */
+enum ipahal_nat_type {
+ IPAHAL_NAT_IPV4,
+ IPAHAL_NAT_IPV4_INDEX,
+ IPAHAL_NAT_IPV4_PDN,
+ IPAHAL_NAT_IPV6CT,
+ IPA_NAT_MAX
+};
+
+/* NAT Function APIs */
+
+/*
+ * ipahal_nat_type_str() - returns string that represent the NAT type
+ * @nat_type: [in] NAT type
+ */
+const char *ipahal_nat_type_str(enum ipahal_nat_type nat_type);
+
+/*
+ * ipahal_nat_entry_size() - Gets the size of HW NAT entry
+ * @nat_type: [in] The type of the NAT entry
+ * @entry_size: [out] The size of the HW NAT entry
+ */
+int ipahal_nat_entry_size(enum ipahal_nat_type nat_type, size_t *entry_size);
+
+/*
+ * ipahal_nat_is_entry_zeroed() - Determines whether HW NAT entry is
+ * definitely zero
+ * @nat_type: [in] The type of the NAT entry
+ * @entry: [in] The NAT entry
+ * @entry_zeroed: [out] True if the received entry is definitely zero
+ */
+int ipahal_nat_is_entry_zeroed(enum ipahal_nat_type nat_type, void *entry,
+ bool *entry_zeroed);
+
+/*
+ * ipahal_nat_stringify_entry() - Creates a string for HW NAT entry
+ * @nat_type: [in] The type of the NAT entry
+ * @entry: [in] The NAT entry
+ * @buff: [out] Output buffer for the result string
+ * @buff_size: [in] The size of the output buffer
+ * @return the number of characters written into buff not including
+ * the trailing '\0'
+ */
+int ipahal_nat_stringify_entry(enum ipahal_nat_type nat_type, void *entry,
+ char *buff, size_t buff_size);
+
+#endif /* _IPAHAL_NAT_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat_i.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat_i.h
new file mode 100644
index 0000000..83bd0f5
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat_i.h
@@ -0,0 +1,153 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _IPAHAL_NAT_I_H_
+#define _IPAHAL_NAT_I_H_
+
+#include <linux/msm_ipa.h>
+
+/* ----------------------- IPv4 NAT Table Entry -------------------------
+ *
+ * -----------------------------------------------------------------------
+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ * -----------------------------------------------------------------------
+ * | Target IP(4B) | Private IP(4B) |
+ * -----------------------------------------------------------------------
+ * |Target Port(2B) |Private Port(2B)| Public Port(2B) | Next Index(2B) |
+ * -----------------------------------------------------------------------
+ * |Proto| TimeStamp(3B) | Flags(2B) |IP check sum Diff|
+ * |(1B) | |EN|Redirect|Resv | (2B) |
+ * -----------------------------------------------------------------------
+ * |TCP/UDP checksum| PDN info(2B) | SW Specific Parameters(4B) |
+ * | diff (2B) |Info|Resv |index table entry| prev index |
+ * -----------------------------------------------------------------------
+ */
+struct ipa_nat_hw_ipv4_entry {
+ /* An IP address can't be bit-field, because its address is used */
+ u32 private_ip;
+ u32 target_ip;
+
+ u32 next_index : 16;
+ u32 public_port : 16;
+ u32 private_port : 16;
+ u32 target_port : 16;
+ u32 ip_chksum : 16;
+
+ u32 rsvd1 : 14;
+ u32 redirect : 1;
+ u32 enable : 1;
+
+ u32 time_stamp : 24;
+ u32 protocol : 8;
+
+ u32 prev_index : 16;
+ u32 indx_tbl_entry : 16;
+
+ u32 rsvd2 : 12;
+ u32 pdn_index : 4; /* IPA 4.0 and greater */
+
+ u32 tcp_udp_chksum : 16;
+};
+
+/*--- IPV4 NAT Index Table Entry --
+ *---------------------------------
+ *| 3 | 2 | 1 | 0 |
+ *---------------------------------
+ *|next index(2B) |table entry(2B)|
+ *---------------------------------
+ */
+struct ipa_nat_hw_indx_entry {
+ u16 tbl_entry;
+ u16 next_index;
+};
+
+/**
+ * struct ipa_nat_hw_pdn_entry - IPA PDN config table entry
+ * @public_ip: the PDN's public ip
+ * @src_metadata: the PDN's metadata to be replaced for source NAT
+ * @dst_metadata: the PDN's metadata to be replaced for destination NAT
+ * @resrvd: reserved field
+ * ---------------------------------
+ * | 3 | 2 | 1 | 0 |
+ * ---------------------------------
+ * | public_ip (4B) |
+ * ---------------------------------
+ * | src_metadata (4B) |
+ * ---------------------------------
+ * | dst_metadata (4B) |
+ * ---------------------------------
+ * | resrvd (4B) |
+ * ---------------------------------
+ */
+struct ipa_nat_hw_pdn_entry {
+ u32 public_ip;
+ u32 src_metadata;
+ u32 dst_metadata;
+ u32 resrvd;
+};
+
+/*------------------------- IPV6CT Table Entry ------------------------------
+ *-----------------------------------------------------------------------------
+ *| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ *-----------------------------------------------------------------------------
+ *| Outbound Src IPv6 Address (8 LSB Bytes) |
+ *-----------------------------------------------------------------------------
+ *| Outbound Src IPv6 Address (8 MSB Bytes) |
+ *-----------------------------------------------------------------------------
+ *| Outbound Dest IPv6 Address (8 LSB Bytes) |
+ *-----------------------------------------------------------------------------
+ *| Outbound Dest IPv6 Address (8 MSB Bytes) |
+ *-----------------------------------------------------------------------------
+ *|Protocol| TimeStamp (3B) | Flags (2B) |Reserved (2B) |
+ *| (1B) | |Enable|Redirect|Resv | |
+ *-----------------------------------------------------------------------------
+ *|Reserved|Direction(1B)|Src Port(2B)| Dest Port (2B) |Next Index(2B)|
+ *| (1B) |IN|OUT|Resv | | | |
+ *-----------------------------------------------------------------------------
+ *| SW Specific Parameters(4B) | Reserved (4B) |
+ *| Prev Index (2B) |Reserved(2B)| |
+ *-----------------------------------------------------------------------------
+ *| Reserved (8B) |
+ *-----------------------------------------------------------------------------
+ */
+struct ipa_nat_hw_ipv6ct_entry {
+ /* An IP address can't be bit-field, because its address is used */
+ u64 src_ipv6_lsb;
+ u64 src_ipv6_msb;
+ u64 dest_ipv6_lsb;
+ u64 dest_ipv6_msb;
+
+ u64 rsvd1 : 30;
+ u64 redirect : 1;
+ u64 enable : 1;
+
+ u64 time_stamp : 24;
+ u64 protocol : 8;
+
+ u64 next_index : 16;
+ u64 dest_port : 16;
+ u64 src_port : 16;
+ u64 rsvd2 : 6;
+ u64 out_allowed : 1;
+ u64 in_allowed : 1;
+ u64 rsvd3 : 8;
+
+ u64 rsvd4 : 48;
+ u64 prev_index : 16;
+
+ u64 rsvd5 : 64;
+};
+
+int ipahal_nat_init(enum ipa_hw_type ipa_hw_type);
+
+#endif /* _IPAHAL_NAT_I_H_ */
+
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index e93210d..f94a342 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -1189,7 +1189,11 @@
static void ipa3_wwan_tx_timeout(struct net_device *dev)
{
- IPAWANERR("[%s] ipa3_wwan_tx_timeout(), data stall in UL\n", dev->name);
+ struct ipa3_wwan_private *wwan_ptr = netdev_priv(dev);
+
+ if (atomic_read(&wwan_ptr->outstanding_pkts) != 0)
+ IPAWANERR("[%s] data stall in UL, %d outstanding\n",
+ dev->name, atomic_read(&wwan_ptr->outstanding_pkts));
}
/**
diff --git a/drivers/platform/msm/ipa/test/ipa_test_mhi.c b/drivers/platform/msm/ipa/test/ipa_test_mhi.c
index 78cdc50..98861de 100644
--- a/drivers/platform/msm/ipa/test/ipa_test_mhi.c
+++ b/drivers/platform/msm/ipa/test/ipa_test_mhi.c
@@ -1359,11 +1359,11 @@
IPA_UT_LOG("DB to event 0x%llx: base %pa ofst 0x%x\n",
p_events[event_ring_index].wp,
&(gsi_ctx->per.phys_addr), GSI_EE_n_EV_CH_k_DOORBELL_0_OFFS(
- event_ring_index + IPA_MHI_GSI_ER_START, 0));
+ event_ring_index + ipa3_ctx->mhi_evid_limits[0], 0));
iowrite32(p_events[event_ring_index].wp,
test_mhi_ctx->gsi_mmio +
GSI_EE_n_EV_CH_k_DOORBELL_0_OFFS(
- event_ring_index + IPA_MHI_GSI_ER_START, 0));
+ event_ring_index + ipa3_ctx->mhi_evid_limits[0], 0));
for (i = 0; i < buf_array_size; i++) {
/* calculate virtual pointer for current WP and RP */
diff --git a/drivers/platform/msm/qcom-geni-se.c b/drivers/platform/msm/qcom-geni-se.c
index 423c8f1..f402589 100644
--- a/drivers/platform/msm/qcom-geni-se.c
+++ b/drivers/platform/msm/qcom-geni-se.c
@@ -575,13 +575,6 @@
}
EXPORT_SYMBOL(se_config_packing);
-static void se_geni_clks_off(struct se_geni_rsc *rsc)
-{
- clk_disable_unprepare(rsc->se_clk);
- clk_disable_unprepare(rsc->s_ahb_clk);
- clk_disable_unprepare(rsc->m_ahb_clk);
-}
-
static bool geni_se_check_bus_bw(struct geni_se_device *geni_se_dev)
{
int i;
@@ -641,6 +634,37 @@
}
/**
+ * se_geni_clks_off() - Turn off clocks associated with the serial
+ * engine
+ * @rsc: Handle to resources associated with the serial engine.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int se_geni_clks_off(struct se_geni_rsc *rsc)
+{
+ int ret = 0;
+ struct geni_se_device *geni_se_dev;
+
+ if (unlikely(!rsc || !rsc->wrapper_dev))
+ return -EINVAL;
+
+ geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
+ if (unlikely(!geni_se_dev || !geni_se_dev->bus_bw))
+ return -ENODEV;
+
+ clk_disable_unprepare(rsc->se_clk);
+ clk_disable_unprepare(rsc->s_ahb_clk);
+ clk_disable_unprepare(rsc->m_ahb_clk);
+
+ ret = geni_se_rmv_ab_ib(geni_se_dev, rsc);
+ if (ret)
+ GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
+ "%s: Error %d during bus_bw_update\n", __func__, ret);
+ return ret;
+}
+EXPORT_SYMBOL(se_geni_clks_off);
+
+/**
* se_geni_resources_off() - Turn off resources associated with the serial
* engine
* @rsc: Handle to resources associated with the serial engine.
@@ -665,37 +689,14 @@
"%s: Error %d pinctrl_select_state\n", __func__, ret);
return ret;
}
- se_geni_clks_off(rsc);
- ret = geni_se_rmv_ab_ib(geni_se_dev, rsc);
+ ret = se_geni_clks_off(rsc);
if (ret)
GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
- "%s: Error %d during bus_bw_update\n", __func__, ret);
+ "%s: Error %d turning off clocks\n", __func__, ret);
return ret;
}
EXPORT_SYMBOL(se_geni_resources_off);
-static int se_geni_clks_on(struct se_geni_rsc *rsc)
-{
- int ret;
-
- ret = clk_prepare_enable(rsc->m_ahb_clk);
- if (ret)
- return ret;
-
- ret = clk_prepare_enable(rsc->s_ahb_clk);
- if (ret) {
- clk_disable_unprepare(rsc->m_ahb_clk);
- return ret;
- }
-
- ret = clk_prepare_enable(rsc->se_clk);
- if (ret) {
- clk_disable_unprepare(rsc->s_ahb_clk);
- clk_disable_unprepare(rsc->m_ahb_clk);
- }
- return ret;
-}
-
static int geni_se_add_ab_ib(struct geni_se_device *geni_se_dev,
struct se_geni_rsc *rsc)
{
@@ -733,13 +734,13 @@
}
/**
- * se_geni_resources_on() - Turn on resources associated with the serial
- * engine
+ * se_geni_clks_on() - Turn on clocks associated with the serial
+ * engine
* @rsc: Handle to resources associated with the serial engine.
*
* Return: 0 on success, standard Linux error codes on failure/error.
*/
-int se_geni_resources_on(struct se_geni_rsc *rsc)
+int se_geni_clks_on(struct se_geni_rsc *rsc)
{
int ret = 0;
struct geni_se_device *geni_se_dev;
@@ -758,11 +759,52 @@
return ret;
}
+ ret = clk_prepare_enable(rsc->m_ahb_clk);
+ if (ret)
+ goto clks_on_err1;
+
+ ret = clk_prepare_enable(rsc->s_ahb_clk);
+ if (ret)
+ goto clks_on_err2;
+
+ ret = clk_prepare_enable(rsc->se_clk);
+ if (ret)
+ goto clks_on_err3;
+ return 0;
+
+clks_on_err3:
+ clk_disable_unprepare(rsc->s_ahb_clk);
+clks_on_err2:
+ clk_disable_unprepare(rsc->m_ahb_clk);
+clks_on_err1:
+ geni_se_rmv_ab_ib(geni_se_dev, rsc);
+ return ret;
+}
+EXPORT_SYMBOL(se_geni_clks_on);
+
+/**
+ * se_geni_resources_on() - Turn on resources associated with the serial
+ * engine
+ * @rsc: Handle to resources associated with the serial engine.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int se_geni_resources_on(struct se_geni_rsc *rsc)
+{
+ int ret = 0;
+ struct geni_se_device *geni_se_dev;
+
+ if (unlikely(!rsc || !rsc->wrapper_dev))
+ return -EINVAL;
+
+ geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
+ if (unlikely(!geni_se_dev))
+ return -EPROBE_DEFER;
+
ret = se_geni_clks_on(rsc);
if (ret) {
GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
"%s: Error %d during clks_on\n", __func__, ret);
- geni_se_rmv_ab_ib(geni_se_dev, rsc);
return ret;
}
@@ -771,7 +813,6 @@
GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
"%s: Error %d pinctrl_select_state\n", __func__, ret);
se_geni_clks_off(rsc);
- geni_se_rmv_ab_ib(geni_se_dev, rsc);
}
return ret;
}
diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c
index 5bdde69..f62f9df 100644
--- a/drivers/power/supply/axp288_fuel_gauge.c
+++ b/drivers/power/supply/axp288_fuel_gauge.c
@@ -169,8 +169,10 @@
break;
}
- if (ret < 0)
+ if (ret < 0) {
dev_err(&info->pdev->dev, "axp288 reg read err:%d\n", ret);
+ return ret;
+ }
return val;
}
diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c
index 7f9a797..4b900e2 100644
--- a/drivers/power/supply/qcom/battery.c
+++ b/drivers/power/supply/qcom/battery.c
@@ -61,6 +61,7 @@
struct delayed_work status_change_work;
struct work_struct pl_disable_forever_work;
struct work_struct pl_taper_work;
+ struct delayed_work pl_awake_work;
bool taper_work_running;
struct power_supply *main_psy;
struct power_supply *pl_psy;
@@ -568,6 +569,14 @@
vote(chip->hvdcp_hw_inov_dis_votable, PL_VOTER, false, 0);
}
+static void pl_awake_work(struct work_struct *work)
+{
+ struct pl_data *chip = container_of(work,
+ struct pl_data, pl_awake_work.work);
+
+ vote(chip->pl_awake_votable, PL_VOTER, false, 0);
+}
+
static bool is_main_available(struct pl_data *chip)
{
if (chip->main_psy)
@@ -595,6 +604,10 @@
total_fcc_ua = get_effective_result_locked(chip->fcc_votable);
if (chip->pl_mode != POWER_SUPPLY_PL_NONE && !pl_disable) {
+ /* keep system awake to talk to slave charger through i2c */
+ cancel_delayed_work_sync(&chip->pl_awake_work);
+ vote(chip->pl_awake_votable, PL_VOTER, true, 0);
+
/* enable parallel charging */
rc = power_supply_get_property(chip->pl_psy,
POWER_SUPPLY_PROP_CHARGE_TYPE, &pval);
@@ -698,6 +711,10 @@
}
rerun_election(chip->fv_votable);
+
+ cancel_delayed_work_sync(&chip->pl_awake_work);
+ schedule_delayed_work(&chip->pl_awake_work,
+ msecs_to_jiffies(5000));
}
pl_dbg(chip, PR_PARALLEL, "parallel charging %s\n",
@@ -1075,6 +1092,7 @@
INIT_DELAYED_WORK(&chip->status_change_work, status_change_work);
INIT_WORK(&chip->pl_taper_work, pl_taper_work);
INIT_WORK(&chip->pl_disable_forever_work, pl_disable_forever_work);
+ INIT_DELAYED_WORK(&chip->pl_awake_work, pl_awake_work);
rc = pl_register_notifier(chip);
if (rc < 0) {
@@ -1128,6 +1146,7 @@
cancel_delayed_work_sync(&chip->status_change_work);
cancel_work_sync(&chip->pl_taper_work);
cancel_work_sync(&chip->pl_disable_forever_work);
+ cancel_delayed_work_sync(&chip->pl_awake_work);
power_supply_unreg_notifier(&chip->nb);
destroy_votable(chip->pl_enable_votable_indirect);
diff --git a/drivers/power/supply/qcom/fg-memif.c b/drivers/power/supply/qcom/fg-memif.c
index c1b5adc..279b097 100644
--- a/drivers/power/supply/qcom/fg-memif.c
+++ b/drivers/power/supply/qcom/fg-memif.c
@@ -847,8 +847,8 @@
static int fg_get_partition_count(struct fg_chip *chip, u16 sram_addr, int len,
int *count)
{
- int i, num = 0;
- u16 end_addr, last_addr = 0;
+ int i, start_partn = 0, end_partn = 0;
+ u16 end_addr = 0;
end_addr = sram_addr + len / BYTES_PER_SRAM_WORD;
if (!(len % BYTES_PER_SRAM_WORD))
@@ -860,24 +860,24 @@
}
for (i = 0; i < NUM_PARTITIONS; i++) {
- pr_debug("address: %d last_addr: %d\n", sram_addr, last_addr);
if (sram_addr >= chip->addr_map[i].partition_start
- && sram_addr <= chip->addr_map[i].partition_end
- && last_addr < end_addr) {
- num++;
- last_addr = chip->addr_map[i].partition_end;
- sram_addr = chip->addr_map[i+1].partition_start;
- }
+ && sram_addr <= chip->addr_map[i].partition_end)
+ start_partn = i + 1;
+
+ if (end_addr >= chip->addr_map[i].partition_start
+ && end_addr <= chip->addr_map[i].partition_end)
+ end_partn = i + 1;
}
- if (num > 0) {
- *count = num;
- return 0;
+ if (!start_partn || !end_partn) {
+ pr_err("Couldn't find number of partitions for address %d\n",
+ sram_addr);
+ return -ENXIO;
}
- pr_err("Couldn't find number of partitions for address %d\n",
- sram_addr);
- return -ENXIO;
+ *count = (end_partn - start_partn) + 1;
+
+ return 0;
}
static int fg_get_partition_avail_bytes(struct fg_chip *chip, u16 sram_addr,
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index 2a47442..c91e46c 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -762,7 +762,19 @@
if (rc < 0)
return rc;
- *msoc = DIV_ROUND_CLOSEST(*msoc * FULL_CAPACITY, FULL_SOC_RAW);
+ /*
+ * To have better endpoints for 0 and 100, it is good to tune the
+ * calculation discarding values 0 and 255 while rounding off. Rest
+ * of the values 1-254 will be scaled to 1-99. DIV_ROUND_UP will not
+ * be suitable here as it rounds up any value higher than 252 to 100.
+ */
+ if (*msoc == FULL_SOC_RAW)
+ *msoc = 100;
+ else if (*msoc == 0)
+ *msoc = 0;
+ else
+ *msoc = DIV_ROUND_CLOSEST((*msoc - 1) * (FULL_CAPACITY - 2),
+ FULL_SOC_RAW - 2) + 1;
return 0;
}
diff --git a/drivers/power/supply/qcom/qpnp-fg.c b/drivers/power/supply/qcom/qpnp-fg.c
index 17b9c1d3..a12b0ad 100644
--- a/drivers/power/supply/qcom/qpnp-fg.c
+++ b/drivers/power/supply/qcom/qpnp-fg.c
@@ -549,6 +549,7 @@
struct fg_chip *chip;
struct fg_log_buffer *log; /* log buffer */
u8 *data; /* fg data that is read */
+ struct mutex memif_dfs_lock; /* Prevent thread concurrency */
};
struct fg_dbgfs {
@@ -5725,6 +5726,7 @@
trans->addr = dbgfs_data.addr;
trans->chip = dbgfs_data.chip;
trans->offset = trans->addr;
+ mutex_init(&trans->memif_dfs_lock);
file->private_data = trans;
return 0;
@@ -5736,6 +5738,7 @@
if (trans && trans->log && trans->data) {
file->private_data = NULL;
+ mutex_destroy(&trans->memif_dfs_lock);
kfree(trans->log);
kfree(trans->data);
kfree(trans);
@@ -5893,10 +5896,13 @@
size_t ret;
size_t len;
+ mutex_lock(&trans->memif_dfs_lock);
/* Is the the log buffer empty */
if (log->rpos >= log->wpos) {
- if (get_log_data(trans) <= 0)
- return 0;
+ if (get_log_data(trans) <= 0) {
+ len = 0;
+ goto unlock_mutex;
+ }
}
len = min(count, log->wpos - log->rpos);
@@ -5904,7 +5910,8 @@
ret = copy_to_user(buf, &log->data[log->rpos], len);
if (ret == len) {
pr_err("error copy sram register values to user\n");
- return -EFAULT;
+ len = -EFAULT;
+ goto unlock_mutex;
}
/* 'ret' is the number of bytes not copied */
@@ -5912,6 +5919,9 @@
*ppos += len;
log->rpos += len;
+
+unlock_mutex:
+ mutex_unlock(&trans->memif_dfs_lock);
return len;
}
@@ -5932,15 +5942,20 @@
int cnt = 0;
u8 *values;
size_t ret = 0;
+ char *kbuf;
+ u32 offset;
struct fg_trans *trans = file->private_data;
- u32 offset = trans->offset;
+
+ mutex_lock(&trans->memif_dfs_lock);
+ offset = trans->offset;
/* Make a copy of the user data */
- char *kbuf = kmalloc(count + 1, GFP_KERNEL);
-
- if (!kbuf)
- return -ENOMEM;
+ kbuf = kmalloc(count + 1, GFP_KERNEL);
+ if (!kbuf) {
+ ret = -ENOMEM;
+ goto unlock_mutex;
+ }
ret = copy_from_user(kbuf, buf, count);
if (ret == count) {
@@ -5991,6 +6006,8 @@
free_buf:
kfree(kbuf);
+unlock_mutex:
+ mutex_unlock(&trans->memif_dfs_lock);
return ret;
}
diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c
index 9d19b9a..315a4be 100644
--- a/drivers/rapidio/devices/tsi721.c
+++ b/drivers/rapidio/devices/tsi721.c
@@ -37,8 +37,8 @@
#include "tsi721.h"
#ifdef DEBUG
-u32 dbg_level;
-module_param(dbg_level, uint, S_IWUSR | S_IRUGO);
+u32 tsi_dbg_level;
+module_param_named(dbg_level, tsi_dbg_level, uint, S_IWUSR | S_IRUGO);
MODULE_PARM_DESC(dbg_level, "Debugging output level (default 0 = none)");
#endif
diff --git a/drivers/rapidio/devices/tsi721.h b/drivers/rapidio/devices/tsi721.h
index 5941437..957eadc 100644
--- a/drivers/rapidio/devices/tsi721.h
+++ b/drivers/rapidio/devices/tsi721.h
@@ -40,11 +40,11 @@
};
#ifdef DEBUG
-extern u32 dbg_level;
+extern u32 tsi_dbg_level;
#define tsi_debug(level, dev, fmt, arg...) \
do { \
- if (DBG_##level & dbg_level) \
+ if (DBG_##level & tsi_dbg_level) \
dev_dbg(dev, "%s: " fmt "\n", __func__, ##arg); \
} while (0)
#else
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index d75f157..ec492af 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -4905,6 +4905,16 @@
if (of_have_populated_dt())
has_full_constraints = true;
+ /*
+ * Regulators may had failed to resolve their input supplies
+ * when were registered, either because the input supply was
+ * not registered yet or because its parent device was not
+ * bound yet. So attempt to resolve the input supplies for
+ * pending regulators before trying to disable unused ones.
+ */
+ class_for_each_device(®ulator_class, NULL, NULL,
+ regulator_register_resolve_supply);
+
/* If we have a full configuration then disable any regulators
* we have permission to change the status for and which are
* not in use or always_on. This is effectively the default
diff --git a/drivers/reset/reset-ti-syscon.c b/drivers/reset/reset-ti-syscon.c
index 47f0ffd..1799fd4 100644
--- a/drivers/reset/reset-ti-syscon.c
+++ b/drivers/reset/reset-ti-syscon.c
@@ -154,8 +154,8 @@
if (ret)
return ret;
- return (reset_state & BIT(control->status_bit)) &&
- (control->flags & STATUS_SET);
+ return !(reset_state & BIT(control->status_bit)) ==
+ !(control->flags & STATUS_SET);
}
static struct reset_control_ops ti_syscon_reset_ops = {
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 84a52db..f1d4ca2 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -372,6 +372,14 @@
}
EXPORT_SYMBOL_GPL(rtc_set_alarm);
+static void rtc_alarm_disable(struct rtc_device *rtc)
+{
+ if (!rtc->ops || !rtc->ops->alarm_irq_enable)
+ return;
+
+ rtc->ops->alarm_irq_enable(rtc->dev.parent, false);
+}
+
/* Called once per device from rtc_device_register */
int rtc_initialize_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
{
@@ -399,7 +407,11 @@
rtc->aie_timer.enabled = 1;
timerqueue_add(&rtc->timerqueue, &rtc->aie_timer.node);
+ } else if (alarm->enabled && (rtc_tm_to_ktime(now).tv64 >=
+ rtc->aie_timer.node.expires.tv64)){
+ rtc_alarm_disable(rtc);
}
+
mutex_unlock(&rtc->ops_lock);
return err;
}
@@ -790,14 +802,6 @@
return 0;
}
-static void rtc_alarm_disable(struct rtc_device *rtc)
-{
- if (!rtc->ops || !rtc->ops->alarm_irq_enable)
- return;
-
- rtc->ops->alarm_irq_enable(rtc->dev.parent, false);
-}
-
/**
* rtc_timer_remove - Removes a rtc_timer from the rtc_device timerqueue
* @rtc rtc device
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index d5bf36e..34367d1 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -3,7 +3,7 @@
*
* Debug traces for zfcp.
*
- * Copyright IBM Corp. 2002, 2016
+ * Copyright IBM Corp. 2002, 2017
*/
#define KMSG_COMPONENT "zfcp"
@@ -447,6 +447,7 @@
struct fc_ct_hdr *reqh = sg_virt(ct_els->req);
struct fc_ns_gid_ft *reqn = (struct fc_ns_gid_ft *)(reqh + 1);
struct scatterlist *resp_entry = ct_els->resp;
+ struct fc_ct_hdr *resph;
struct fc_gpn_ft_resp *acc;
int max_entries, x, last = 0;
@@ -473,6 +474,13 @@
return len; /* not GPN_FT response so do not cap */
acc = sg_virt(resp_entry);
+
+ /* cap all but accept CT responses to at least the CT header */
+ resph = (struct fc_ct_hdr *)acc;
+ if ((ct_els->status) ||
+ (resph->ct_cmd != cpu_to_be16(FC_FS_ACC)))
+ return max(FC_CT_HDR_LEN, ZFCP_DBF_SAN_MAX_PAYLOAD);
+
max_entries = (reqh->ct_mr_size * 4 / sizeof(struct fc_gpn_ft_resp))
+ 1 /* zfcp_fc_scan_ports: bytes correct, entries off-by-one
* to account for header as 1st pseudo "entry" */;
@@ -555,8 +563,8 @@
rec->scsi_retries = sc->retries;
rec->scsi_allowed = sc->allowed;
rec->scsi_id = sc->device->id;
- /* struct zfcp_dbf_scsi needs to be updated to handle 64bit LUNs */
rec->scsi_lun = (u32)sc->device->lun;
+ rec->scsi_lun_64_hi = (u32)(sc->device->lun >> 32);
rec->host_scribble = (unsigned long)sc->host_scribble;
memcpy(rec->scsi_opcode, sc->cmnd,
@@ -564,19 +572,32 @@
if (fsf) {
rec->fsf_req_id = fsf->req_id;
+ rec->pl_len = FCP_RESP_WITH_EXT;
fcp_rsp = (struct fcp_resp_with_ext *)
&(fsf->qtcb->bottom.io.fcp_rsp);
+ /* mandatory parts of FCP_RSP IU in this SCSI record */
memcpy(&rec->fcp_rsp, fcp_rsp, FCP_RESP_WITH_EXT);
if (fcp_rsp->resp.fr_flags & FCP_RSP_LEN_VAL) {
fcp_rsp_info = (struct fcp_resp_rsp_info *) &fcp_rsp[1];
rec->fcp_rsp_info = fcp_rsp_info->rsp_code;
+ rec->pl_len += be32_to_cpu(fcp_rsp->ext.fr_rsp_len);
}
if (fcp_rsp->resp.fr_flags & FCP_SNS_LEN_VAL) {
- rec->pl_len = min((u16)SCSI_SENSE_BUFFERSIZE,
- (u16)ZFCP_DBF_PAY_MAX_REC);
- zfcp_dbf_pl_write(dbf, sc->sense_buffer, rec->pl_len,
- "fcp_sns", fsf->req_id);
+ rec->pl_len += be32_to_cpu(fcp_rsp->ext.fr_sns_len);
}
+ /* complete FCP_RSP IU in associated PAYload record
+ * but only if there are optional parts
+ */
+ if (fcp_rsp->resp.fr_flags != 0)
+ zfcp_dbf_pl_write(
+ dbf, fcp_rsp,
+ /* at least one full PAY record
+ * but not beyond hardware response field
+ */
+ min_t(u16, max_t(u16, rec->pl_len,
+ ZFCP_DBF_PAY_MAX_REC),
+ FSF_FCP_RSP_SIZE),
+ "fcp_riu", fsf->req_id);
}
debug_event(dbf->scsi, level, rec, sizeof(*rec));
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index db186d4..b60667c 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -2,7 +2,7 @@
* zfcp device driver
* debug feature declarations
*
- * Copyright IBM Corp. 2008, 2016
+ * Copyright IBM Corp. 2008, 2017
*/
#ifndef ZFCP_DBF_H
@@ -204,7 +204,7 @@
* @id: unique number of recovery record type
* @tag: identifier string specifying the location of initiation
* @scsi_id: scsi device id
- * @scsi_lun: scsi device logical unit number
+ * @scsi_lun: scsi device logical unit number, low part of 64 bit, old 32 bit
* @scsi_result: scsi result
* @scsi_retries: current retry number of scsi request
* @scsi_allowed: allowed retries
@@ -214,6 +214,7 @@
* @host_scribble: LLD specific data attached to SCSI request
* @pl_len: length of paload stored as zfcp_dbf_pay
* @fsf_rsp: response for fsf request
+ * @scsi_lun_64_hi: scsi device logical unit number, high part of 64 bit
*/
struct zfcp_dbf_scsi {
u8 id;
@@ -230,6 +231,7 @@
u64 host_scribble;
u16 pl_len;
struct fcp_resp_with_ext fcp_rsp;
+ u32 scsi_lun_64_hi;
} __packed;
/**
@@ -323,7 +325,11 @@
{
struct fsf_qtcb *qtcb = req->qtcb;
- if ((qtcb->prefix.prot_status != FSF_PROT_GOOD) &&
+ if (unlikely(req->status & (ZFCP_STATUS_FSFREQ_DISMISSED |
+ ZFCP_STATUS_FSFREQ_ERROR))) {
+ zfcp_dbf_hba_fsf_resp("fs_rerr", 3, req);
+
+ } else if ((qtcb->prefix.prot_status != FSF_PROT_GOOD) &&
(qtcb->prefix.prot_status != FSF_PROT_FSF_STATUS_PRESENTED)) {
zfcp_dbf_hba_fsf_resp("fs_perr", 1, req);
@@ -401,7 +407,8 @@
* @flag: indicates type of reset (Target Reset, Logical Unit Reset)
*/
static inline
-void zfcp_dbf_scsi_devreset(char *tag, struct scsi_cmnd *scmnd, u8 flag)
+void zfcp_dbf_scsi_devreset(char *tag, struct scsi_cmnd *scmnd, u8 flag,
+ struct zfcp_fsf_req *fsf_req)
{
char tmp_tag[ZFCP_DBF_TAG_LEN];
@@ -411,7 +418,7 @@
memcpy(tmp_tag, "lr_", 3);
memcpy(&tmp_tag[3], tag, 4);
- _zfcp_dbf_scsi(tmp_tag, 1, scmnd, NULL);
+ _zfcp_dbf_scsi(tmp_tag, 1, scmnd, fsf_req);
}
/**
diff --git a/drivers/s390/scsi/zfcp_fc.h b/drivers/s390/scsi/zfcp_fc.h
index df2b541..a227582 100644
--- a/drivers/s390/scsi/zfcp_fc.h
+++ b/drivers/s390/scsi/zfcp_fc.h
@@ -4,7 +4,7 @@
* Fibre Channel related definitions and inline functions for the zfcp
* device driver
*
- * Copyright IBM Corp. 2009
+ * Copyright IBM Corp. 2009, 2017
*/
#ifndef ZFCP_FC_H
@@ -279,6 +279,10 @@
!(rsp_flags & FCP_SNS_LEN_VAL) &&
fcp_rsp->resp.fr_status == SAM_STAT_GOOD)
set_host_byte(scsi, DID_ERROR);
+ } else if (unlikely(rsp_flags & FCP_RESID_OVER)) {
+ /* FCP_DL was not sufficient for SCSI data length */
+ if (fcp_rsp->resp.fr_status == SAM_STAT_GOOD)
+ set_host_byte(scsi, DID_ERROR);
}
}
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index 27ff38f..1964391 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -928,8 +928,8 @@
switch (header->fsf_status) {
case FSF_GOOD:
- zfcp_dbf_san_res("fsscth2", req);
ct->status = 0;
+ zfcp_dbf_san_res("fsscth2", req);
break;
case FSF_SERVICE_CLASS_NOT_SUPPORTED:
zfcp_fsf_class_not_supp(req);
@@ -1109,8 +1109,8 @@
switch (header->fsf_status) {
case FSF_GOOD:
- zfcp_dbf_san_res("fsselh1", req);
send_els->status = 0;
+ zfcp_dbf_san_res("fsselh1", req);
break;
case FSF_SERVICE_CLASS_NOT_SUPPORTED:
zfcp_fsf_class_not_supp(req);
@@ -2258,7 +2258,8 @@
fcp_cmnd = (struct fcp_cmnd *) &req->qtcb->bottom.io.fcp_cmnd;
zfcp_fc_scsi_to_fcp(fcp_cmnd, scsi_cmnd, 0);
- if (scsi_prot_sg_count(scsi_cmnd)) {
+ if ((scsi_get_prot_op(scsi_cmnd) != SCSI_PROT_NORMAL) &&
+ scsi_prot_sg_count(scsi_cmnd)) {
zfcp_qdio_set_data_div(qdio, &req->qdio_req,
scsi_prot_sg_count(scsi_cmnd));
retval = zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req,
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 07ffdbb..9bd9b9a 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -3,7 +3,7 @@
*
* Interface to Linux SCSI midlayer.
*
- * Copyright IBM Corp. 2002, 2016
+ * Copyright IBM Corp. 2002, 2017
*/
#define KMSG_COMPONENT "zfcp"
@@ -273,25 +273,29 @@
zfcp_erp_wait(adapter);
ret = fc_block_scsi_eh(scpnt);
- if (ret)
+ if (ret) {
+ zfcp_dbf_scsi_devreset("fiof", scpnt, tm_flags, NULL);
return ret;
+ }
if (!(atomic_read(&adapter->status) &
ZFCP_STATUS_COMMON_RUNNING)) {
- zfcp_dbf_scsi_devreset("nres", scpnt, tm_flags);
+ zfcp_dbf_scsi_devreset("nres", scpnt, tm_flags, NULL);
return SUCCESS;
}
}
- if (!fsf_req)
+ if (!fsf_req) {
+ zfcp_dbf_scsi_devreset("reqf", scpnt, tm_flags, NULL);
return FAILED;
+ }
wait_for_completion(&fsf_req->completion);
if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) {
- zfcp_dbf_scsi_devreset("fail", scpnt, tm_flags);
+ zfcp_dbf_scsi_devreset("fail", scpnt, tm_flags, fsf_req);
retval = FAILED;
} else {
- zfcp_dbf_scsi_devreset("okay", scpnt, tm_flags);
+ zfcp_dbf_scsi_devreset("okay", scpnt, tm_flags, fsf_req);
zfcp_scsi_forget_cmnds(zfcp_sdev, tm_flags);
}
diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c
index ba25821..963c732 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.c
+++ b/drivers/scsi/be2iscsi/be_iscsi.c
@@ -166,33 +166,6 @@
}
/**
- * beiscsi_bindconn_cid - Bind the beiscsi_conn with phba connection table
- * @beiscsi_conn: The pointer to beiscsi_conn structure
- * @phba: The phba instance
- * @cid: The cid to free
- */
-static int beiscsi_bindconn_cid(struct beiscsi_hba *phba,
- struct beiscsi_conn *beiscsi_conn,
- unsigned int cid)
-{
- uint16_t cri_index = BE_GET_CRI_FROM_CID(cid);
-
- if (phba->conn_table[cri_index]) {
- beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
- "BS_%d : Connection table already occupied. Detected clash\n");
-
- return -EINVAL;
- } else {
- beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
- "BS_%d : phba->conn_table[%d]=%p(beiscsi_conn)\n",
- cri_index, beiscsi_conn);
-
- phba->conn_table[cri_index] = beiscsi_conn;
- }
- return 0;
-}
-
-/**
* beiscsi_conn_bind - Binds iscsi session/connection with TCP connection
* @cls_session: pointer to iscsi cls session
* @cls_conn: pointer to iscsi cls conn
@@ -212,6 +185,7 @@
struct hwi_wrb_context *pwrb_context;
struct beiscsi_endpoint *beiscsi_ep;
struct iscsi_endpoint *ep;
+ uint16_t cri_index;
ep = iscsi_lookup_endpoint(transport_fd);
if (!ep)
@@ -229,20 +203,34 @@
return -EEXIST;
}
-
- pwrb_context = &phwi_ctrlr->wrb_context[BE_GET_CRI_FROM_CID(
- beiscsi_ep->ep_cid)];
+ cri_index = BE_GET_CRI_FROM_CID(beiscsi_ep->ep_cid);
+ if (phba->conn_table[cri_index]) {
+ if (beiscsi_conn != phba->conn_table[cri_index] ||
+ beiscsi_ep != phba->conn_table[cri_index]->ep) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BS_%d : conn_table not empty at %u: cid %u conn %p:%p\n",
+ cri_index,
+ beiscsi_ep->ep_cid,
+ beiscsi_conn,
+ phba->conn_table[cri_index]);
+ return -EINVAL;
+ }
+ }
beiscsi_conn->beiscsi_conn_cid = beiscsi_ep->ep_cid;
beiscsi_conn->ep = beiscsi_ep;
beiscsi_ep->conn = beiscsi_conn;
+ /**
+ * Each connection is associated with a WRBQ kept in wrb_context.
+ * Store doorbell offset for transmit path.
+ */
+ pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
beiscsi_conn->doorbell_offset = pwrb_context->doorbell_offset;
-
beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
- "BS_%d : beiscsi_conn=%p conn=%p ep_cid=%d\n",
- beiscsi_conn, conn, beiscsi_ep->ep_cid);
-
- return beiscsi_bindconn_cid(phba, beiscsi_conn, beiscsi_ep->ep_cid);
+ "BS_%d : cid %d phba->conn_table[%u]=%p\n",
+ beiscsi_ep->ep_cid, cri_index, beiscsi_conn);
+ phba->conn_table[cri_index] = beiscsi_conn;
+ return 0;
}
static int beiscsi_iface_create_ipv4(struct beiscsi_hba *phba)
@@ -973,9 +961,9 @@
*/
static int beiscsi_get_cid(struct beiscsi_hba *phba)
{
- unsigned short cid = 0xFFFF, cid_from_ulp;
- struct ulp_cid_info *cid_info = NULL;
uint16_t cid_avlbl_ulp0, cid_avlbl_ulp1;
+ unsigned short cid, cid_from_ulp;
+ struct ulp_cid_info *cid_info;
/* Find the ULP which has more CID available */
cid_avlbl_ulp0 = (phba->cid_array_info[BEISCSI_ULP0]) ?
@@ -984,20 +972,27 @@
BEISCSI_ULP1_AVLBL_CID(phba) : 0;
cid_from_ulp = (cid_avlbl_ulp0 > cid_avlbl_ulp1) ?
BEISCSI_ULP0 : BEISCSI_ULP1;
+ /**
+ * If iSCSI protocol is loaded only on ULP 0, and when cid_avlbl_ulp
+ * is ZERO for both, ULP 1 is returned.
+ * Check if ULP is loaded before getting new CID.
+ */
+ if (!test_bit(cid_from_ulp, (void *)&phba->fw_config.ulp_supported))
+ return BE_INVALID_CID;
- if (test_bit(cid_from_ulp, (void *)&phba->fw_config.ulp_supported)) {
- cid_info = phba->cid_array_info[cid_from_ulp];
- if (!cid_info->avlbl_cids)
- return cid;
-
- cid = cid_info->cid_array[cid_info->cid_alloc++];
-
- if (cid_info->cid_alloc == BEISCSI_GET_CID_COUNT(
- phba, cid_from_ulp))
- cid_info->cid_alloc = 0;
-
- cid_info->avlbl_cids--;
+ cid_info = phba->cid_array_info[cid_from_ulp];
+ cid = cid_info->cid_array[cid_info->cid_alloc];
+ if (!cid_info->avlbl_cids || cid == BE_INVALID_CID) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BS_%d : failed to get cid: available %u:%u\n",
+ cid_info->avlbl_cids, cid_info->cid_free);
+ return BE_INVALID_CID;
}
+ /* empty the slot */
+ cid_info->cid_array[cid_info->cid_alloc++] = BE_INVALID_CID;
+ if (cid_info->cid_alloc == BEISCSI_GET_CID_COUNT(phba, cid_from_ulp))
+ cid_info->cid_alloc = 0;
+ cid_info->avlbl_cids--;
return cid;
}
@@ -1008,22 +1003,28 @@
*/
static void beiscsi_put_cid(struct beiscsi_hba *phba, unsigned short cid)
{
- uint16_t cid_post_ulp;
- struct hwi_controller *phwi_ctrlr;
- struct hwi_wrb_context *pwrb_context;
- struct ulp_cid_info *cid_info = NULL;
uint16_t cri_index = BE_GET_CRI_FROM_CID(cid);
+ struct hwi_wrb_context *pwrb_context;
+ struct hwi_controller *phwi_ctrlr;
+ struct ulp_cid_info *cid_info;
+ uint16_t cid_post_ulp;
phwi_ctrlr = phba->phwi_ctrlr;
pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
cid_post_ulp = pwrb_context->ulp_num;
cid_info = phba->cid_array_info[cid_post_ulp];
- cid_info->avlbl_cids++;
-
+ /* fill only in empty slot */
+ if (cid_info->cid_array[cid_info->cid_free] != BE_INVALID_CID) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BS_%d : failed to put cid %u: available %u:%u\n",
+ cid, cid_info->avlbl_cids, cid_info->cid_free);
+ return;
+ }
cid_info->cid_array[cid_info->cid_free++] = cid;
if (cid_info->cid_free == BEISCSI_GET_CID_COUNT(phba, cid_post_ulp))
cid_info->cid_free = 0;
+ cid_info->avlbl_cids++;
}
/**
@@ -1037,8 +1038,8 @@
beiscsi_put_cid(phba, beiscsi_ep->ep_cid);
beiscsi_ep->phba = NULL;
- phba->ep_array[BE_GET_CRI_FROM_CID
- (beiscsi_ep->ep_cid)] = NULL;
+ /* clear this to track freeing in beiscsi_ep_disconnect */
+ phba->ep_array[BE_GET_CRI_FROM_CID(beiscsi_ep->ep_cid)] = NULL;
/**
* Check if any connection resource allocated by driver
@@ -1049,6 +1050,11 @@
return;
beiscsi_conn = beiscsi_ep->conn;
+ /**
+ * Break ep->conn link here so that completions after
+ * this are ignored.
+ */
+ beiscsi_ep->conn = NULL;
if (beiscsi_conn->login_in_progress) {
beiscsi_free_mgmt_task_handles(beiscsi_conn,
beiscsi_conn->task);
@@ -1079,7 +1085,7 @@
"BS_%d : In beiscsi_open_conn\n");
beiscsi_ep->ep_cid = beiscsi_get_cid(phba);
- if (beiscsi_ep->ep_cid == 0xFFFF) {
+ if (beiscsi_ep->ep_cid == BE_INVALID_CID) {
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
"BS_%d : No free cid available\n");
return ret;
@@ -1285,26 +1291,6 @@
}
/**
- * beiscsi_unbind_conn_to_cid - Unbind the beiscsi_conn from phba conn table
- * @phba: The phba instance
- * @cid: The cid to free
- */
-static int beiscsi_unbind_conn_to_cid(struct beiscsi_hba *phba,
- unsigned int cid)
-{
- uint16_t cri_index = BE_GET_CRI_FROM_CID(cid);
-
- if (phba->conn_table[cri_index])
- phba->conn_table[cri_index] = NULL;
- else {
- beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
- "BS_%d : Connection table Not occupied.\n");
- return -EINVAL;
- }
- return 0;
-}
-
-/**
* beiscsi_ep_disconnect - Tears down the TCP connection
* @ep: endpoint to be used
*
@@ -1318,13 +1304,23 @@
unsigned int tag;
uint8_t mgmt_invalidate_flag, tcp_upload_flag;
unsigned short savecfg_flag = CMD_ISCSI_SESSION_SAVE_CFG_ON_FLASH;
+ uint16_t cri_index;
beiscsi_ep = ep->dd_data;
phba = beiscsi_ep->phba;
beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
- "BS_%d : In beiscsi_ep_disconnect for ep_cid = %d\n",
+ "BS_%d : In beiscsi_ep_disconnect for ep_cid = %u\n",
beiscsi_ep->ep_cid);
+ cri_index = BE_GET_CRI_FROM_CID(beiscsi_ep->ep_cid);
+ if (!phba->ep_array[cri_index]) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BS_%d : ep_array at %u cid %u empty\n",
+ cri_index,
+ beiscsi_ep->ep_cid);
+ return;
+ }
+
if (beiscsi_ep->conn) {
beiscsi_conn = beiscsi_ep->conn;
iscsi_suspend_queue(beiscsi_conn->conn);
@@ -1356,7 +1352,12 @@
free_ep:
msleep(BEISCSI_LOGOUT_SYNC_DELAY);
beiscsi_free_ep(beiscsi_ep);
- beiscsi_unbind_conn_to_cid(phba, beiscsi_ep->ep_cid);
+ if (!phba->conn_table[cri_index])
+ __beiscsi_log(phba, KERN_ERR,
+ "BS_%d : conn_table empty at %u: cid %u\n",
+ cri_index,
+ beiscsi_ep->ep_cid);
+ phba->conn_table[cri_index] = NULL;
iscsi_destroy_endpoint(beiscsi_ep->openiscsi_ep);
}
diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c
index d9239c2..741cc96 100644
--- a/drivers/scsi/be2iscsi/be_main.c
+++ b/drivers/scsi/be2iscsi/be_main.c
@@ -4085,9 +4085,10 @@
}
/* Allocate memory for CID array */
- ptr_cid_info->cid_array = kzalloc(sizeof(void *) *
- BEISCSI_GET_CID_COUNT(phba,
- ulp_num), GFP_KERNEL);
+ ptr_cid_info->cid_array =
+ kcalloc(BEISCSI_GET_CID_COUNT(phba, ulp_num),
+ sizeof(*ptr_cid_info->cid_array),
+ GFP_KERNEL);
if (!ptr_cid_info->cid_array) {
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
"BM_%d : Failed to allocate memory"
diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h
index 6376657..02d00ab 100644
--- a/drivers/scsi/be2iscsi/be_main.h
+++ b/drivers/scsi/be2iscsi/be_main.h
@@ -358,6 +358,7 @@
unsigned int age;
struct list_head hba_queue;
#define BE_MAX_SESSION 2048
+#define BE_INVALID_CID 0xffff
#define BE_SET_CID_TO_CRI(cri_index, cid) \
(phba->cid_to_cri_map[cid] = cri_index)
#define BE_GET_CRI_FROM_CID(cid) (phba->cid_to_cri_map[cid])
diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c
index 375d818..d5f6fbf 100644
--- a/drivers/scsi/device_handler/scsi_dh_emc.c
+++ b/drivers/scsi/device_handler/scsi_dh_emc.c
@@ -461,7 +461,7 @@
static int clariion_std_inquiry(struct scsi_device *sdev,
struct clariion_dh_data *csdev)
{
- int err;
+ int err = SCSI_DH_OK;
char *sp_model;
err = send_inquiry_cmd(sdev, 0, csdev);
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index d8b1fbd..35cbd36 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -1901,9 +1901,12 @@
if (cmd_fusion->sync_cmd_idx != (u32)ULONG_MAX) {
cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx];
if (cmd_mfi->sync_cmd &&
- cmd_mfi->frame->hdr.cmd != MFI_CMD_ABORT)
+ (cmd_mfi->frame->hdr.cmd != MFI_CMD_ABORT)) {
+ cmd_mfi->frame->hdr.cmd_status =
+ MFI_STAT_WRONG_STATE;
megasas_complete_cmd(instance,
cmd_mfi, DID_OK);
+ }
}
}
} else {
@@ -5290,7 +5293,8 @@
instance->throttlequeuedepth =
MEGASAS_THROTTLE_QUEUE_DEPTH;
- if (resetwaittime > MEGASAS_RESET_WAIT_TIME)
+ if ((resetwaittime < 1) ||
+ (resetwaittime > MEGASAS_RESET_WAIT_TIME))
resetwaittime = MEGASAS_RESET_WAIT_TIME;
if ((scmd_timeout < 10) || (scmd_timeout > MEGASAS_DEFAULT_CMD_TIMEOUT))
@@ -5459,6 +5463,14 @@
prev_aen.word =
le32_to_cpu(instance->aen_cmd->frame->dcmd.mbox.w[1]);
+ if ((curr_aen.members.class < MFI_EVT_CLASS_DEBUG) ||
+ (curr_aen.members.class > MFI_EVT_CLASS_DEAD)) {
+ dev_info(&instance->pdev->dev,
+ "%s %d out of range class %d send by application\n",
+ __func__, __LINE__, curr_aen.members.class);
+ return 0;
+ }
+
/*
* A class whose enum value is smaller is inclusive of all
* higher values. If a PROGRESS (= -1) was previously
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 8c4641b..9a34afc 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -318,6 +318,8 @@
return -EINVAL;
if (start > ha->optrom_size)
return -EINVAL;
+ if (size > ha->optrom_size - start)
+ size = ha->optrom_size - start;
mutex_lock(&ha->optrom_mutex);
switch (val) {
@@ -343,8 +345,7 @@
}
ha->optrom_region_start = start;
- ha->optrom_region_size = start + size > ha->optrom_size ?
- ha->optrom_size - start : size;
+ ha->optrom_region_size = start + size;
ha->optrom_state = QLA_SREADING;
ha->optrom_buffer = vmalloc(ha->optrom_region_size);
@@ -417,8 +418,7 @@
}
ha->optrom_region_start = start;
- ha->optrom_region_size = start + size > ha->optrom_size ?
- ha->optrom_size - start : size;
+ ha->optrom_region_size = start + size;
ha->optrom_state = QLA_SWRITING;
ha->optrom_buffer = vmalloc(ha->optrom_region_size);
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index 3dfb54a..f8ae704 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -74,7 +74,7 @@
* ensures no active vp_list traversal while the vport is removed
* from the queue)
*/
- wait_event_timeout(vha->vref_waitq, atomic_read(&vha->vref_count),
+ wait_event_timeout(vha->vref_waitq, !atomic_read(&vha->vref_count),
10*HZ);
spin_lock_irqsave(&ha->vport_slock, flags);
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index 42bca61..c39551b 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -3696,7 +3696,7 @@
uint32_t group;
nlh = nlmsg_hdr(skb);
- if (nlh->nlmsg_len < sizeof(*nlh) ||
+ if (nlh->nlmsg_len < sizeof(*nlh) + sizeof(*ev) ||
skb->len < nlh->nlmsg_len) {
break;
}
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index b9290e7..02823a7 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -2770,8 +2770,6 @@
sd_read_write_same(sdkp, buffer);
}
- sdkp->first_scan = 0;
-
/*
* We now have all cache related info, determine how we deal
* with flush requests.
@@ -2786,7 +2784,7 @@
q->limits.max_dev_sectors = logical_to_sectors(sdp, dev_max);
/*
- * Use the device's preferred I/O size for reads and writes
+ * Determine the device's preferred I/O size for reads and writes
* unless the reported value is unreasonably small, large, or
* garbage.
*/
@@ -2800,8 +2798,19 @@
rw_max = min_not_zero(logical_to_sectors(sdp, dev_max),
(sector_t)BLK_DEF_MAX_SECTORS);
- /* Combine with controller limits */
- q->limits.max_sectors = min(rw_max, queue_max_hw_sectors(q));
+ /* Do not exceed controller limit */
+ rw_max = min(rw_max, queue_max_hw_sectors(q));
+
+ /*
+ * Only update max_sectors if previously unset or if the current value
+ * exceeds the capabilities of the hardware.
+ */
+ if (sdkp->first_scan ||
+ q->limits.max_sectors > q->limits.max_dev_sectors ||
+ q->limits.max_sectors > q->limits.max_hw_sectors)
+ q->limits.max_sectors = rw_max;
+
+ sdkp->first_scan = 0;
set_capacity(disk, logical_to_sectors(sdp, sdkp->capacity));
sd_config_write_same(sdkp);
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 4bd6fd4..0114e2a 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -122,7 +122,7 @@
struct sg_fd;
typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */
- struct sg_request *nextrp; /* NULL -> tail request (slist) */
+ struct list_head entry; /* list entry */
struct sg_fd *parentfp; /* NULL -> not in use */
Sg_scatter_hold data; /* hold buffer, perhaps scatter list */
sg_io_hdr_t header; /* scsi command+info, see <scsi/sg.h> */
@@ -146,8 +146,7 @@
int timeout; /* defaults to SG_DEFAULT_TIMEOUT */
int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */
Sg_scatter_hold reserve; /* buffer held for this file descriptor */
- unsigned save_scat_len; /* original length of trunc. scat. element */
- Sg_request *headrp; /* head of request slist, NULL->empty */
+ struct list_head rq_list; /* head of request list */
struct fasync_struct *async_qp; /* used by asynchronous notification */
Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */
char low_dma; /* as in parent but possibly overridden to 1 */
@@ -833,6 +832,39 @@
return max_sectors << 9;
}
+static void
+sg_fill_request_table(Sg_fd *sfp, sg_req_info_t *rinfo)
+{
+ Sg_request *srp;
+ int val;
+ unsigned int ms;
+
+ val = 0;
+ list_for_each_entry(srp, &sfp->rq_list, entry) {
+ if (val > SG_MAX_QUEUE)
+ break;
+ rinfo[val].req_state = srp->done + 1;
+ rinfo[val].problem =
+ srp->header.masked_status &
+ srp->header.host_status &
+ srp->header.driver_status;
+ if (srp->done)
+ rinfo[val].duration =
+ srp->header.duration;
+ else {
+ ms = jiffies_to_msecs(jiffies);
+ rinfo[val].duration =
+ (ms > srp->header.duration) ?
+ (ms - srp->header.duration) : 0;
+ }
+ rinfo[val].orphan = srp->orphan;
+ rinfo[val].sg_io_owned = srp->sg_io_owned;
+ rinfo[val].pack_id = srp->header.pack_id;
+ rinfo[val].usr_ptr = srp->header.usr_ptr;
+ val++;
+ }
+}
+
static long
sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
{
@@ -949,7 +981,7 @@
if (!access_ok(VERIFY_WRITE, ip, sizeof (int)))
return -EFAULT;
read_lock_irqsave(&sfp->rq_list_lock, iflags);
- for (srp = sfp->headrp; srp; srp = srp->nextrp) {
+ list_for_each_entry(srp, &sfp->rq_list, entry) {
if ((1 == srp->done) && (!srp->sg_io_owned)) {
read_unlock_irqrestore(&sfp->rq_list_lock,
iflags);
@@ -962,7 +994,8 @@
return 0;
case SG_GET_NUM_WAITING:
read_lock_irqsave(&sfp->rq_list_lock, iflags);
- for (val = 0, srp = sfp->headrp; srp; srp = srp->nextrp) {
+ val = 0;
+ list_for_each_entry(srp, &sfp->rq_list, entry) {
if ((1 == srp->done) && (!srp->sg_io_owned))
++val;
}
@@ -1031,40 +1064,13 @@
return -EFAULT;
else {
sg_req_info_t *rinfo;
- unsigned int ms;
- rinfo = kmalloc(SZ_SG_REQ_INFO * SG_MAX_QUEUE,
- GFP_KERNEL);
+ rinfo = kzalloc(SZ_SG_REQ_INFO * SG_MAX_QUEUE,
+ GFP_KERNEL);
if (!rinfo)
return -ENOMEM;
read_lock_irqsave(&sfp->rq_list_lock, iflags);
- for (srp = sfp->headrp, val = 0; val < SG_MAX_QUEUE;
- ++val, srp = srp ? srp->nextrp : srp) {
- memset(&rinfo[val], 0, SZ_SG_REQ_INFO);
- if (srp) {
- rinfo[val].req_state = srp->done + 1;
- rinfo[val].problem =
- srp->header.masked_status &
- srp->header.host_status &
- srp->header.driver_status;
- if (srp->done)
- rinfo[val].duration =
- srp->header.duration;
- else {
- ms = jiffies_to_msecs(jiffies);
- rinfo[val].duration =
- (ms > srp->header.duration) ?
- (ms - srp->header.duration) : 0;
- }
- rinfo[val].orphan = srp->orphan;
- rinfo[val].sg_io_owned =
- srp->sg_io_owned;
- rinfo[val].pack_id =
- srp->header.pack_id;
- rinfo[val].usr_ptr =
- srp->header.usr_ptr;
- }
- }
+ sg_fill_request_table(sfp, rinfo);
read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
result = __copy_to_user(p, rinfo,
SZ_SG_REQ_INFO * SG_MAX_QUEUE);
@@ -1172,7 +1178,7 @@
return POLLERR;
poll_wait(filp, &sfp->read_wait, wait);
read_lock_irqsave(&sfp->rq_list_lock, iflags);
- for (srp = sfp->headrp; srp; srp = srp->nextrp) {
+ list_for_each_entry(srp, &sfp->rq_list, entry) {
/* if any read waiting, flag it */
if ((0 == res) && (1 == srp->done) && (!srp->sg_io_owned))
res = POLLIN | POLLRDNORM;
@@ -2055,7 +2061,6 @@
req_schp->pages = NULL;
req_schp->page_order = 0;
req_schp->sglist_len = 0;
- sfp->save_scat_len = 0;
srp->res_used = 0;
/* Called without mutex lock to avoid deadlock */
sfp->res_in_use = 0;
@@ -2068,7 +2073,7 @@
unsigned long iflags;
write_lock_irqsave(&sfp->rq_list_lock, iflags);
- for (resp = sfp->headrp; resp; resp = resp->nextrp) {
+ list_for_each_entry(resp, &sfp->rq_list, entry) {
/* look for requests that are ready + not SG_IO owned */
if ((1 == resp->done) && (!resp->sg_io_owned) &&
((-1 == pack_id) || (resp->header.pack_id == pack_id))) {
@@ -2086,70 +2091,45 @@
{
int k;
unsigned long iflags;
- Sg_request *resp;
Sg_request *rp = sfp->req_arr;
write_lock_irqsave(&sfp->rq_list_lock, iflags);
- resp = sfp->headrp;
- if (!resp) {
- memset(rp, 0, sizeof (Sg_request));
- rp->parentfp = sfp;
- resp = rp;
- sfp->headrp = resp;
- } else {
- if (0 == sfp->cmd_q)
- resp = NULL; /* command queuing disallowed */
- else {
- for (k = 0; k < SG_MAX_QUEUE; ++k, ++rp) {
- if (!rp->parentfp)
- break;
- }
- if (k < SG_MAX_QUEUE) {
- memset(rp, 0, sizeof (Sg_request));
- rp->parentfp = sfp;
- while (resp->nextrp)
- resp = resp->nextrp;
- resp->nextrp = rp;
- resp = rp;
- } else
- resp = NULL;
+ if (!list_empty(&sfp->rq_list)) {
+ if (!sfp->cmd_q)
+ goto out_unlock;
+
+ for (k = 0; k < SG_MAX_QUEUE; ++k, ++rp) {
+ if (!rp->parentfp)
+ break;
}
+ if (k >= SG_MAX_QUEUE)
+ goto out_unlock;
}
- if (resp) {
- resp->nextrp = NULL;
- resp->header.duration = jiffies_to_msecs(jiffies);
- }
+ memset(rp, 0, sizeof (Sg_request));
+ rp->parentfp = sfp;
+ rp->header.duration = jiffies_to_msecs(jiffies);
+ list_add_tail(&rp->entry, &sfp->rq_list);
write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
- return resp;
+ return rp;
+out_unlock:
+ write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ return NULL;
}
/* Return of 1 for found; 0 for not found */
static int
sg_remove_request(Sg_fd * sfp, Sg_request * srp)
{
- Sg_request *prev_rp;
- Sg_request *rp;
unsigned long iflags;
int res = 0;
- if ((!sfp) || (!srp) || (!sfp->headrp))
+ if (!sfp || !srp || list_empty(&sfp->rq_list))
return res;
write_lock_irqsave(&sfp->rq_list_lock, iflags);
- prev_rp = sfp->headrp;
- if (srp == prev_rp) {
- sfp->headrp = prev_rp->nextrp;
- prev_rp->parentfp = NULL;
+ if (!list_empty(&srp->entry)) {
+ list_del(&srp->entry);
+ srp->parentfp = NULL;
res = 1;
- } else {
- while ((rp = prev_rp->nextrp)) {
- if (srp == rp) {
- prev_rp->nextrp = rp->nextrp;
- rp->parentfp = NULL;
- res = 1;
- break;
- }
- prev_rp = rp;
- }
}
write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
return res;
@@ -2168,7 +2148,7 @@
init_waitqueue_head(&sfp->read_wait);
rwlock_init(&sfp->rq_list_lock);
-
+ INIT_LIST_HEAD(&sfp->rq_list);
kref_init(&sfp->f_ref);
mutex_init(&sfp->f_mutex);
sfp->timeout = SG_DEFAULT_TIMEOUT;
@@ -2209,10 +2189,13 @@
{
struct sg_fd *sfp = container_of(work, struct sg_fd, ew.work);
struct sg_device *sdp = sfp->parentdp;
+ Sg_request *srp;
/* Cleanup any responses which were never read(). */
- while (sfp->headrp)
- sg_finish_rem_req(sfp->headrp);
+ while (!list_empty(&sfp->rq_list)) {
+ srp = list_first_entry(&sfp->rq_list, Sg_request, entry);
+ sg_finish_rem_req(srp);
+ }
if (sfp->reserve.bufflen > 0) {
SCSI_LOG_TIMEOUT(6, sg_printk(KERN_INFO, sdp,
@@ -2615,7 +2598,7 @@
/* must be called while holding sg_index_lock */
static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp)
{
- int k, m, new_interface, blen, usg;
+ int k, new_interface, blen, usg;
Sg_request *srp;
Sg_fd *fp;
const sg_io_hdr_t *hp;
@@ -2635,9 +2618,7 @@
seq_printf(s, " cmd_q=%d f_packid=%d k_orphan=%d closed=0\n",
(int) fp->cmd_q, (int) fp->force_packid,
(int) fp->keep_orphan);
- for (m = 0, srp = fp->headrp;
- srp != NULL;
- ++m, srp = srp->nextrp) {
+ list_for_each_entry(srp, &fp->rq_list, entry) {
hp = &srp->header;
new_interface = (hp->interface_id == '\0') ? 0 : 1;
if (srp->res_used) {
@@ -2672,7 +2653,7 @@
seq_printf(s, "ms sgat=%d op=0x%02x\n", usg,
(int) srp->data.cmd_opcode);
}
- if (0 == m)
+ if (list_empty(&fp->rq_list))
seq_puts(s, " No requests active\n");
read_unlock(&fp->rq_list_lock);
}
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index c5ab1b0..2bf96d3 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -1559,6 +1559,8 @@
ret = storvsc_do_io(dev, cmd_request);
if (ret == -EAGAIN) {
+ if (payload_sz > sizeof(cmd_request->mpb))
+ kfree(payload);
/* no more space */
return SCSI_MLQUEUE_DEVICE_BUSY;
}
diff --git a/drivers/soc/qcom/dcc_v2.c b/drivers/soc/qcom/dcc_v2.c
index e11efb0..457dc5f 100644
--- a/drivers/soc/qcom/dcc_v2.c
+++ b/drivers/soc/qcom/dcc_v2.c
@@ -22,6 +22,7 @@
#include <linux/uaccess.h>
#include <soc/qcom/memory_dump.h>
#include <soc/qcom/scm.h>
+#include <dt-bindings/soc/qcom,dcc_v2.h>
#define TIMEOUT_US (100)
@@ -536,7 +537,7 @@
mutex_lock(&drvdata->mutex);
- memset_io(drvdata->ram_base, 0, drvdata->ram_size);
+ memset_io(drvdata->ram_base, 0xDE, drvdata->ram_size);
for (list = 0; list < DCC_MAX_LINK_LIST; list++) {
@@ -554,14 +555,12 @@
goto err;
}
- /* 3. If in capture mode program DCC_RAM_CFG reg */
- if (drvdata->func_type[list] == DCC_FUNC_TYPE_CAPTURE) {
- dcc_writel(drvdata, ram_cfg_base +
- drvdata->ram_offset/4, DCC_LL_BASE(list));
- dcc_writel(drvdata, drvdata->ram_start +
- drvdata->ram_offset/4, DCC_FD_BASE(list));
- dcc_writel(drvdata, 0xFFF, DCC_LL_TIMEOUT(list));
- }
+ /* 3. program DCC_RAM_CFG reg */
+ dcc_writel(drvdata, ram_cfg_base +
+ drvdata->ram_offset/4, DCC_LL_BASE(list));
+ dcc_writel(drvdata, drvdata->ram_start +
+ drvdata->ram_offset/4, DCC_FD_BASE(list));
+ dcc_writel(drvdata, 0xFFF, DCC_LL_TIMEOUT(list));
/* 4. Configure trigger, data sink and function type */
dcc_writel(drvdata, BIT(9) | ((drvdata->cti_trig << 8) |
@@ -813,6 +812,9 @@
buf[0] = '\0';
+ if (drvdata->curr_list >= DCC_MAX_LINK_LIST)
+ return -EINVAL;
+
mutex_lock(&drvdata->mutex);
list_for_each_entry(entry,
&drvdata->cfg_head[drvdata->curr_list], list) {
@@ -1088,14 +1090,30 @@
static DEVICE_ATTR(interrupt_disable, 0644,
dcc_show_interrupt_disable, dcc_store_interrupt_disable);
+static int dcc_add_loop(struct dcc_drvdata *drvdata, unsigned long loop_cnt)
+{
+ struct dcc_config_entry *entry;
+
+ entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->loop_cnt = min_t(uint32_t, loop_cnt, MAX_LOOP_CNT);
+ entry->index = drvdata->nr_config[drvdata->curr_list]++;
+ entry->desc_type = DCC_LOOP_TYPE;
+ INIT_LIST_HEAD(&entry->list);
+ list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]);
+
+ return 0;
+}
+
static ssize_t dcc_store_loop(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
- int ret = size;
+ int ret;
unsigned long loop_cnt;
struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
- struct dcc_config_entry *entry;
mutex_lock(&drvdata->mutex);
@@ -1110,18 +1128,12 @@
goto err;
}
- entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL);
- if (!entry) {
- ret = -ENOMEM;
+ ret = dcc_add_loop(drvdata, loop_cnt);
+ if (ret)
goto err;
- }
- entry->loop_cnt = min_t(uint32_t, loop_cnt, MAX_LOOP_CNT);
- entry->index = drvdata->nr_config[drvdata->curr_list]++;
- entry->desc_type = DCC_LOOP_TYPE;
- INIT_LIST_HEAD(&entry->list);
- list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]);
-
+ mutex_unlock(&drvdata->mutex);
+ return size;
err:
mutex_unlock(&drvdata->mutex);
return ret;
@@ -1171,16 +1183,37 @@
}
static DEVICE_ATTR(rd_mod_wr, 0200, NULL, dcc_rd_mod_wr);
+static int dcc_add_write(struct dcc_drvdata *drvdata, unsigned int addr,
+ unsigned int write_val, int apb_bus)
+{
+ struct dcc_config_entry *entry;
+
+ entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->desc_type = DCC_WRITE_TYPE;
+ entry->base = addr & BM(4, 31);
+ entry->offset = addr - entry->base;
+ entry->write_val = write_val;
+ entry->index = drvdata->nr_config[drvdata->curr_list]++;
+ entry->len = 1;
+ entry->apb_bus = apb_bus;
+ INIT_LIST_HEAD(&entry->list);
+ list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]);
+
+ return 0;
+}
+
static ssize_t dcc_write(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
- int ret = size;
+ int ret;
int nval;
unsigned int addr, write_val;
- int apb_bus;
+ int apb_bus = 0;
struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
- struct dcc_config_entry *entry;
mutex_lock(&drvdata->mutex);
@@ -1197,23 +1230,15 @@
goto err;
}
- entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL);
- if (!entry) {
- ret = -ENOMEM;
+ if (nval == 3 && apb_bus != 0)
+ apb_bus = 1;
+
+ ret = dcc_add_write(drvdata, addr, write_val, apb_bus);
+ if (ret)
goto err;
- }
- if (nval == 3)
- entry->apb_bus = true;
-
- entry->desc_type = DCC_WRITE_TYPE;
- entry->base = addr & BM(4, 31);
- entry->offset = addr - entry->base;
- entry->write_val = write_val;
- entry->index = drvdata->nr_config[drvdata->curr_list]++;
- entry->len = 1;
- INIT_LIST_HEAD(&entry->list);
- list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]);
+ mutex_unlock(&drvdata->mutex);
+ return size;
err:
mutex_unlock(&drvdata->mutex);
return ret;
@@ -1418,6 +1443,64 @@
dcc_sram_dev_deregister(drvdata);
}
+static void dcc_configure_list(struct dcc_drvdata *drvdata,
+ struct device_node *np)
+{
+ int ret, i;
+ const __be32 *prop;
+ uint32_t len, entry, val1, val2, apb_bus;
+ uint32_t curr_link_list;
+
+ ret = of_property_read_u32(np, "qcom,curr-link-list",
+ &curr_link_list);
+ if (ret)
+ return;
+
+ if (curr_link_list >= DCC_MAX_LINK_LIST) {
+ dev_err(drvdata->dev, "List configuration failed");
+ return;
+ }
+ drvdata->curr_list = curr_link_list;
+
+ prop = of_get_property(np, "qcom,link-list", &len);
+ if (prop) {
+ len /= sizeof(__be32);
+ i = 0;
+ while (i < len) {
+ entry = be32_to_cpu(prop[i++]);
+ val1 = be32_to_cpu(prop[i++]);
+ val2 = be32_to_cpu(prop[i++]);
+ apb_bus = be32_to_cpu(prop[i++]);
+
+ switch (entry) {
+ case DCC_READ:
+ ret = dcc_config_add(drvdata, val1,
+ val2, apb_bus);
+ break;
+ case DCC_WRITE:
+ ret = dcc_add_write(drvdata, val1,
+ val2, apb_bus);
+ break;
+ case DCC_LOOP:
+ ret = dcc_add_loop(drvdata, val1);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (ret) {
+ dev_err(drvdata->dev,
+ "DCC init time config failed err:%d\n",
+ ret);
+ break;
+ }
+ }
+
+ if (!ret)
+ dcc_enable(drvdata);
+ }
+}
+
static int dcc_probe(struct platform_device *pdev)
{
int ret, i;
@@ -1492,6 +1575,8 @@
if (ret)
goto err;
+ dcc_configure_list(drvdata, pdev->dev.of_node);
+
return 0;
err:
return ret;
@@ -1527,13 +1612,7 @@
{
return platform_driver_register(&dcc_driver);
}
-module_init(dcc_init);
-
-static void __exit dcc_exit(void)
-{
- platform_driver_unregister(&dcc_driver);
-}
-module_exit(dcc_exit);
+pure_initcall(dcc_init);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MSM data capture and compare engine");
diff --git a/drivers/soc/qcom/debug_core.c b/drivers/soc/qcom/debug_core.c
deleted file mode 100644
index 164a866..0000000
--- a/drivers/soc/qcom/debug_core.c
+++ /dev/null
@@ -1,330 +0,0 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/string.h>
-#include <linux/debugfs.h>
-#include <linux/ctype.h>
-#include <linux/cpu.h>
-#include "soc/qcom/msm-core.h"
-
-#define MAX_PSTATES 50
-#define NUM_OF_PENTRY 3 /* number of variables for ptable node */
-#define NUM_OF_EENTRY 2 /* number of variables for enable node */
-
-enum arg_offset {
- CPU_OFFSET,
- FREQ_OFFSET,
- POWER_OFFSET,
-};
-
-struct core_debug {
- int cpu;
- struct cpu_pstate_pwr *head;
- int enabled;
- int len;
- struct cpu_pwr_stats *ptr;
- struct cpu_pstate_pwr *driver_data;
- int driver_len;
-};
-
-static DEFINE_PER_CPU(struct core_debug, c_dgfs);
-static struct cpu_pwr_stats *msm_core_data;
-static struct debugfs_blob_wrapper help_msg = {
- .data =
-"MSM CORE Debug-FS Support\n"
-"\n"
-"Hierarchy schema\n"
-"/sys/kernel/debug/msm_core\n"
-" /help - Static help text\n"
-" /ptable - write to p-state table\n"
-" /enable - enable the written p-state table\n"
-" /ptable_dump - Dump the debug ptable\n"
-"\n"
-"Usage\n"
-" Input test frequency and power information in ptable:\n"
-" echo \"0 300000 120\" > ptable\n"
-" format: <cpu> <frequency in khz> <power>\n"
-"\n"
-" Enable the ptable for the cpu:\n"
-" echo \"0 1\" > enable\n"
-" format: <cpu> <1 to enable, 0 to disable>\n"
-" Note: Writing 0 to disable will reset/clear the ptable\n"
-"\n"
-" Dump the entire ptable:\n"
-" cat ptable\n"
-" ----- CPU0 - Enabled ---------\n"
-" Freq Power\n"
-" 700000 120\n"
-"----- CPU0 - Live numbers -----\n"
-" Freq Power\n"
-" 300000 218\n"
-" ----- CPU1 - Written ---------\n"
-" Freq Power\n"
-" 700000 120\n"
-" Ptable dump will dump the status of the table as well\n"
-" It shows:\n"
-" Enabled -> for a cpu that debug ptable enabled\n"
-" Written -> for a cpu that has debug ptable values written\n"
-" but not enabled\n"
-"\n",
-
-};
-
-static void add_to_ptable(unsigned int *arg)
-{
- struct core_debug *node;
- int i, cpu = arg[CPU_OFFSET];
- uint32_t freq = arg[FREQ_OFFSET];
- uint32_t power = arg[POWER_OFFSET];
-
- if (!cpu_possible(cpu))
- return;
-
- if ((freq == 0) || (power == 0)) {
- pr_warn("Incorrect power data\n");
- return;
- }
-
- node = &per_cpu(c_dgfs, cpu);
-
- if (node->len >= MAX_PSTATES) {
- pr_warn("Dropped ptable update - no space left.\n");
- return;
- }
-
- if (!node->head) {
- node->head = kzalloc(sizeof(struct cpu_pstate_pwr) *
- (MAX_PSTATES + 1),
- GFP_KERNEL);
- if (!node->head)
- return;
- }
-
- for (i = 0; i < node->len; i++) {
- if (node->head[i].freq == freq) {
- node->head[i].power = power;
- return;
- }
- }
-
- /*
- * Insert a new frequency (may need to move things around to
- * keep in ascending order).
- */
- for (i = MAX_PSTATES - 1; i > 0; i--) {
- if (node->head[i-1].freq > freq) {
- node->head[i].freq = node->head[i-1].freq;
- node->head[i].power = node->head[i-1].power;
- } else if (node->head[i-1].freq != 0) {
- break;
- }
- }
-
- if (node->len < MAX_PSTATES) {
- node->head[i].freq = freq;
- node->head[i].power = power;
- node->len++;
- }
-
- if (node->ptr)
- node->ptr->len = node->len;
-}
-
-static int split_ptable_args(char *line, unsigned int *arg, uint32_t n)
-{
- char *args;
- int i;
- int ret = 0;
-
- for (i = 0; i < n; i++) {
- if (!line)
- break;
- args = strsep(&line, " ");
- ret = kstrtouint(args, 10, &arg[i]);
- if (ret)
- return ret;
- }
- return ret;
-}
-
-static ssize_t msm_core_ptable_write(struct file *file,
- const char __user *ubuf, size_t len, loff_t *offp)
-{
- char *kbuf;
- int ret;
- unsigned int arg[3];
-
- if (len == 0)
- return 0;
-
- kbuf = kzalloc(len + 1, GFP_KERNEL);
- if (!kbuf)
- return -ENOMEM;
-
- if (copy_from_user(kbuf, ubuf, len)) {
- ret = -EFAULT;
- goto done;
- }
- kbuf[len] = '\0';
- ret = split_ptable_args(kbuf, arg, NUM_OF_PENTRY);
- if (!ret) {
- add_to_ptable(arg);
- ret = len;
- }
-done:
- kfree(kbuf);
- return ret;
-}
-
-static void print_table(struct seq_file *m, struct cpu_pstate_pwr *c_n,
- int len)
-{
- int i;
-
- seq_puts(m, " Freq Power\n");
- for (i = 0; i < len; i++)
- seq_printf(m, " %d %u\n", c_n[i].freq,
- c_n[i].power);
-
-}
-
-static int msm_core_ptable_read(struct seq_file *m, void *data)
-{
- int cpu;
- struct core_debug *node;
-
- for_each_possible_cpu(cpu) {
- node = &per_cpu(c_dgfs, cpu);
- if (node->head) {
- seq_printf(m, "----- CPU%d - %s - Debug -------\n",
- cpu, node->enabled == 1 ? "Enabled" : "Written");
- print_table(m, node->head, node->len);
- }
- if (msm_core_data[cpu].ptable) {
- seq_printf(m, "--- CPU%d - Live numbers at %ldC---\n",
- cpu, node->ptr->temp);
- print_table(m, msm_core_data[cpu].ptable,
- node->driver_len);
- }
- }
- return 0;
-}
-
-static ssize_t msm_core_enable_write(struct file *file,
- const char __user *ubuf, size_t len, loff_t *offp)
-{
- char *kbuf;
- int ret;
- unsigned int arg[3];
- int cpu;
-
- if (len == 0)
- return 0;
-
- kbuf = kzalloc(len + 1, GFP_KERNEL);
- if (!kbuf)
- return -ENOMEM;
-
- if (copy_from_user(kbuf, ubuf, len)) {
- ret = -EFAULT;
- goto done;
- }
- kbuf[len] = '\0';
- ret = split_ptable_args(kbuf, arg, NUM_OF_EENTRY);
- if (ret)
- goto done;
- cpu = arg[CPU_OFFSET];
-
- if (cpu_possible(cpu)) {
- struct core_debug *node = &per_cpu(c_dgfs, cpu);
-
- if (arg[FREQ_OFFSET]) {
- msm_core_data[cpu].ptable = node->head;
- msm_core_data[cpu].len = node->len;
- } else {
- msm_core_data[cpu].ptable = node->driver_data;
- msm_core_data[cpu].len = node->driver_len;
- node->len = 0;
- }
- node->enabled = arg[FREQ_OFFSET];
- }
- ret = len;
- blocking_notifier_call_chain(
- get_power_update_notifier(), cpu, NULL);
-
-done:
- kfree(kbuf);
- return ret;
-}
-
-static const struct file_operations msm_core_enable_ops = {
- .write = msm_core_enable_write,
-};
-
-static int msm_core_dump_open(struct inode *inode, struct file *file)
-{
- return single_open(file, msm_core_ptable_read, inode->i_private);
-}
-
-static const struct file_operations msm_core_ptable_ops = {
- .open = msm_core_dump_open,
- .read = seq_read,
- .write = msm_core_ptable_write,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-int msm_core_debug_init(void)
-{
- struct dentry *dir = NULL;
- struct dentry *file = NULL;
- int i;
-
- msm_core_data = get_cpu_pwr_stats();
- if (!msm_core_data)
- goto fail;
-
- dir = debugfs_create_dir("msm_core", NULL);
- if (IS_ERR_OR_NULL(dir))
- return PTR_ERR(dir);
-
- file = debugfs_create_file("enable", 0660, dir, NULL,
- &msm_core_enable_ops);
- if (IS_ERR_OR_NULL(file))
- goto fail;
-
- file = debugfs_create_file("ptable", 0660, dir, NULL,
- &msm_core_ptable_ops);
- if (IS_ERR_OR_NULL(file))
- goto fail;
-
- help_msg.size = strlen(help_msg.data);
- file = debugfs_create_blob("help", 0444, dir, &help_msg);
- if (IS_ERR_OR_NULL(file))
- goto fail;
-
- for (i = 0; i < num_possible_cpus(); i++) {
- per_cpu(c_dgfs, i).ptr = &msm_core_data[i];
- per_cpu(c_dgfs, i).driver_data = msm_core_data[i].ptable;
- per_cpu(c_dgfs, i).driver_len = msm_core_data[i].len;
- }
- return 0;
-fail:
- debugfs_remove(dir);
- return PTR_ERR(file);
-}
-late_initcall(msm_core_debug_init);
diff --git a/drivers/soc/qcom/eud.c b/drivers/soc/qcom/eud.c
index f7f3317..3c4238c 100644
--- a/drivers/soc/qcom/eud.c
+++ b/drivers/soc/qcom/eud.c
@@ -29,6 +29,9 @@
#include <linux/power_supply.h>
#include <linux/clk.h>
#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
#define EUD_ENABLE_CMD 1
#define EUD_DISABLE_CMD 0
@@ -74,6 +77,10 @@
struct work_struct eud_work;
struct power_supply *batt_psy;
struct clk *cfg_ahb_clk;
+
+ /* regulator and notifier chain for it */
+ struct regulator *vdda33;
+ struct notifier_block vdda33_nb;
};
static const unsigned int eud_extcon_cable[] = {
@@ -487,12 +494,47 @@
return IRQ_HANDLED;
}
+static int vdda33_notifier_block_cb(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct eud_chip *chip = container_of(nb, struct eud_chip, vdda33_nb);
+ int attach_det = 0;
+
+ switch (event) {
+ case REGULATOR_EVENT_ENABLE:
+ attach_det = 1;
+ /* fall throuhg */
+ case REGULATOR_EVENT_DISABLE:
+ clk_prepare_enable(chip->cfg_ahb_clk);
+
+ /* eud does not retain interrupt mask when ldo24
+ * is turned off. Set the interrupt mask when
+ * ldo24 is turned on
+ */
+ if (attach_det)
+ writel_relaxed(EUD_INT_VBUS | EUD_INT_CHGR,
+ chip->eud_reg_base + EUD_REG_INT1_EN_MASK);
+ writel_relaxed(attach_det,
+ chip->eud_reg_base + EUD_REG_SW_ATTACH_DET);
+ clk_disable_unprepare(chip->cfg_ahb_clk);
+
+ dev_dbg(chip->dev, "%s(): %s\n", __func__,
+ attach_det ? "enable" : "disable");
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
static int msm_eud_probe(struct platform_device *pdev)
{
struct eud_chip *chip;
struct uart_port *port;
struct resource *res;
int ret;
+ int pet;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (!chip) {
@@ -575,6 +617,24 @@
eud_private = pdev;
+ chip->vdda33 = devm_regulator_get(&pdev->dev, "vdda33");
+ if (IS_ERR(chip->vdda33)) {
+ dev_err(chip->dev, "%s: failed to get eud 3p1 regulator\n",
+ __func__);
+ return PTR_ERR(chip->vdda33);
+ }
+ chip->vdda33_nb.notifier_call = vdda33_notifier_block_cb;
+ regulator_register_notifier(chip->vdda33, &chip->vdda33_nb);
+
+ clk_prepare_enable(chip->cfg_ahb_clk);
+
+ pet = regulator_is_enabled(chip->vdda33) ? 1 : 0;
+ writel_relaxed(pet, chip->eud_reg_base + EUD_REG_SW_ATTACH_DET);
+
+ dev_dbg(chip->dev, "%s:%s pet\n", __func__, pet ? "Attach" : "Detach");
+
+ clk_disable_unprepare(chip->cfg_ahb_clk);
+
/* Enable EUD */
if (enable)
enable_eud(pdev);
@@ -587,6 +647,8 @@
struct eud_chip *chip = platform_get_drvdata(pdev);
struct uart_port *port = &chip->port;
+ regulator_unregister_notifier(chip->vdda33, &chip->vdda33_nb);
+
uart_remove_one_port(&eud_uart_driver, port);
device_init_wakeup(chip->dev, false);
diff --git a/drivers/soc/qcom/glink_loopback_server.c b/drivers/soc/qcom/glink_loopback_server.c
index 4e9b118..94a3d8c 100644
--- a/drivers/soc/qcom/glink_loopback_server.c
+++ b/drivers/soc/qcom/glink_loopback_server.c
@@ -195,7 +195,7 @@
resp_pkt->response = response;
return glink_tx(handle, (void *)LINEAR, (void *)resp_pkt,
- sizeof(struct resp), 0);
+ sizeof(struct resp), GLINK_TX_REQ_INTENT);
}
static uint32_t calc_delay_ms(uint32_t random_delay, uint32_t delay_ms)
@@ -1143,7 +1143,7 @@
return;
}
- flags = 0;
+ flags = GLINK_TX_REQ_INTENT;
if (tmp_work_info->tracer_pkt) {
flags |= GLINK_TX_TRACER_PKT;
tracer_pkt_log_event(tmp_work_info->data,
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 04c611c..6d008d8 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -200,7 +200,6 @@
enum icnss_msa_perm {
ICNSS_MSA_PERM_HLOS_ALL = 0,
ICNSS_MSA_PERM_WLAN_HW_RW = 1,
- ICNSS_MSA_PERM_DUMP_COLLECT = 2,
ICNSS_MSA_PERM_MAX,
};
@@ -233,13 +232,6 @@
.nelems = 2,
},
- [ICNSS_MSA_PERM_DUMP_COLLECT] = {
- .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_HLOS},
- .perms = {PERM_READ | PERM_WRITE,
- PERM_READ | PERM_WRITE,
- PERM_READ},
- .nelems = 3,
- },
};
struct icnss_msa_perm_list_t msa_perm_list[ICNSS_MSA_PERM_MAX] = {
@@ -257,14 +249,6 @@
.nelems = 3,
},
- [ICNSS_MSA_PERM_DUMP_COLLECT] = {
- .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_WLAN_CE, VMID_HLOS},
- .perms = {PERM_READ | PERM_WRITE,
- PERM_READ | PERM_WRITE,
- PERM_READ | PERM_WRITE,
- PERM_READ},
- .nelems = 4,
- },
};
struct icnss_event_pd_service_down_data {
@@ -2345,7 +2329,7 @@
if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
goto out;
- if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
+ if (test_bit(ICNSS_PD_RESTART, &priv->state) && event_data->crashed) {
icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
event_data->crashed, priv->state);
ICNSS_ASSERT(0);
@@ -2491,9 +2475,10 @@
icnss_pr_vdbg("Modem-Notify: event %lu\n", code);
- if (code == SUBSYS_AFTER_SHUTDOWN) {
+ if (code == SUBSYS_AFTER_SHUTDOWN &&
+ notif->crashed != CRASH_STATUS_WDOG_BITE) {
ret = icnss_assign_msa_perm_all(priv,
- ICNSS_MSA_PERM_DUMP_COLLECT);
+ ICNSS_MSA_PERM_HLOS_ALL);
if (!ret) {
icnss_pr_info("Collecting msa0 segment dump\n");
icnss_msa0_ramdump(priv);
diff --git a/drivers/soc/qcom/llcc-sdm670.c b/drivers/soc/qcom/llcc-sdm670.c
index 494b93b..aaed9ee 100644
--- a/drivers/soc/qcom/llcc-sdm670.c
+++ b/drivers/soc/qcom/llcc-sdm670.c
@@ -63,7 +63,7 @@
SCT_ENTRY("audio", 6, 6, 512, 1, 0, 0xF, 0x0, 0, 0, 1, 1, 0),
SCT_ENTRY("modem", 8, 8, 512, 1, 0, 0xF, 0x0, 0, 0, 1, 1, 0),
SCT_ENTRY("gpu", 12, 12, 384, 1, 1, 0x0, 0x0, 0, 0, 1, 1, 0),
- SCT_ENTRY("mmuhwt", 13, 13, 512, 1, 0, 0x0, 0x8, 0, 0, 1, 0, 1),
+ SCT_ENTRY("mmuhwt", 13, 13, 512, 1, 0, 0xF, 0x0, 0, 0, 1, 0, 1),
SCT_ENTRY("audiohw", 22, 22, 512, 1, 1, 0xF, 0x0, 0, 0, 1, 1, 0),
};
diff --git a/drivers/soc/qcom/msm-core.c b/drivers/soc/qcom/msm-core.c
deleted file mode 100644
index f8103de..0000000
--- a/drivers/soc/qcom/msm-core.c
+++ /dev/null
@@ -1,902 +0,0 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/cpu.h>
-#include <linux/cpufreq.h>
-#include <linux/err.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/kthread.h>
-#include <linux/kernel.h>
-#include <linux/miscdevice.h>
-#include <linux/module.h>
-#include <linux/msm-core-interface.h>
-#include <linux/mutex.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/pm_opp.h>
-#include <linux/platform_device.h>
-#include <linux/pm_opp.h>
-#include <linux/slab.h>
-#include <linux/suspend.h>
-#include <linux/thermal.h>
-#include <linux/types.h>
-#include <linux/uaccess.h>
-#include <linux/uio_driver.h>
-#include <asm/smp_plat.h>
-#include <asm/cputype.h>
-#include <stdbool.h>
-#define CREATE_TRACE_POINTS
-#include <trace/events/trace_msm_core.h>
-
-#define TEMP_BASE_POINT 35
-#define TEMP_MAX_POINT 95
-#define CPU_HOTPLUG_LIMIT 80
-#define CPU_BIT_MASK(cpu) BIT(cpu)
-#define DEFAULT_TEMP 40
-#define DEFAULT_LOW_HYST_TEMP 10
-#define DEFAULT_HIGH_HYST_TEMP 5
-#define MAX_CORES_PER_CLUSTER 4
-#define MAX_NUM_OF_CLUSTERS 2
-#define NUM_OF_CORNERS 10
-#define DEFAULT_SCALING_FACTOR 1
-
-#define ALLOCATE_2D_ARRAY(type) \
-static type **allocate_2d_array_##type(int idx)\
-{\
- int i;\
- type **ptr = NULL;\
- if (!idx) \
- return ERR_PTR(-EINVAL);\
- ptr = kzalloc(sizeof(*ptr) * TEMP_DATA_POINTS, \
- GFP_KERNEL);\
- if (!ptr) { \
- return ERR_PTR(-ENOMEM); \
- } \
- for (i = 0; i < TEMP_DATA_POINTS; i++) { \
- ptr[i] = kzalloc(sizeof(*ptr[i]) * \
- idx, GFP_KERNEL);\
- if (!ptr[i]) {\
- goto done;\
- } \
- } \
- return ptr;\
-done:\
- for (i = 0; i < TEMP_DATA_POINTS; i++) \
- kfree(ptr[i]);\
- kfree(ptr);\
- return ERR_PTR(-ENOMEM);\
-}
-
-struct cpu_activity_info {
- int cpu;
- int mpidr;
- long temp;
- int sensor_id;
- struct cpu_static_info *sp;
-};
-
-struct cpu_static_info {
- uint32_t **power;
- cpumask_t mask;
- struct cpufreq_frequency_table *table;
- uint32_t *voltage;
- uint32_t num_of_freqs;
-};
-
-static DEFINE_MUTEX(policy_update_mutex);
-static DEFINE_MUTEX(kthread_update_mutex);
-static DEFINE_SPINLOCK(update_lock);
-static struct delayed_work sampling_work;
-static struct completion sampling_completion;
-static struct task_struct *sampling_task;
-static int low_hyst_temp;
-static int high_hyst_temp;
-static struct platform_device *msm_core_pdev;
-static struct cpu_activity_info activity[NR_CPUS];
-DEFINE_PER_CPU(struct cpu_pstate_pwr *, ptable);
-static struct cpu_pwr_stats cpu_stats[NR_CPUS];
-ALLOCATE_2D_ARRAY(uint32_t);
-
-static int poll_ms;
-module_param_named(polling_interval, poll_ms, int, 0664);
-
-static int disabled;
-module_param_named(disabled, disabled, int, 0664);
-
-static bool in_suspend;
-static bool activate_power_table;
-static int max_throttling_temp = 80; /* in C */
-module_param_named(throttling_temp, max_throttling_temp, int, 0664);
-
-static void samplequeue_handle(struct work_struct *work)
-{
- complete(&sampling_completion);
-}
-
-static void repopulate_stats(int cpu)
-{
- int i;
- struct cpu_activity_info *cpu_node = &activity[cpu];
- int temp_point;
- struct cpu_pstate_pwr *pt = per_cpu(ptable, cpu);
-
- if (!pt)
- return;
-
- if (cpu_node->temp < TEMP_BASE_POINT)
- temp_point = 0;
- else if (cpu_node->temp > TEMP_MAX_POINT)
- temp_point = TEMP_DATA_POINTS - 1;
- else
- temp_point = (cpu_node->temp - TEMP_BASE_POINT) / 5;
-
- cpu_stats[cpu].temp = cpu_node->temp;
- for (i = 0; i < cpu_node->sp->num_of_freqs; i++)
- pt[i].power = cpu_node->sp->power[temp_point][i];
-
- trace_cpu_stats(cpu, cpu_stats[cpu].temp, pt[0].power,
- pt[cpu_node->sp->num_of_freqs-1].power);
-};
-
-void trigger_cpu_pwr_stats_calc(void)
-{
- int cpu;
- static long prev_temp[NR_CPUS];
- struct cpu_activity_info *cpu_node;
-
- if (disabled)
- return;
-
- spin_lock(&update_lock);
-
- for_each_online_cpu(cpu) {
- cpu_node = &activity[cpu];
- if (cpu_node->sensor_id < 0)
- continue;
-
- prev_temp[cpu] = cpu_node->temp;
-
- /*
- * Do not populate/update stats before policy and ptable have
- * been updated.
- */
- if (activate_power_table && cpu_stats[cpu].ptable
- && cpu_node->sp->table)
- repopulate_stats(cpu);
- }
- spin_unlock(&update_lock);
-}
-EXPORT_SYMBOL(trigger_cpu_pwr_stats_calc);
-
-void set_cpu_throttled(cpumask_t *mask, bool throttling)
-{
- int cpu;
-
- if (!mask)
- return;
-
- spin_lock(&update_lock);
- for_each_cpu(cpu, mask)
- cpu_stats[cpu].throttling = throttling;
- spin_unlock(&update_lock);
-}
-EXPORT_SYMBOL(set_cpu_throttled);
-
-static void update_related_freq_table(struct cpufreq_policy *policy)
-{
- int cpu, num_of_freqs;
- struct cpufreq_frequency_table *table;
-
- table = policy->freq_table;
- if (!table) {
- pr_err("Couldn't get freq table for cpu%d\n",
- policy->cpu);
- return;
- }
-
- for (num_of_freqs = 0; table[num_of_freqs].frequency !=
- CPUFREQ_TABLE_END;)
- num_of_freqs++;
-
- /*
- * Synchronous cores within cluster have the same
- * policy. Since these cores do not have the cpufreq
- * table initialized for all of them, copy the same
- * table to all the related cpus.
- */
- for_each_cpu(cpu, policy->related_cpus) {
- activity[cpu].sp->table = table;
- activity[cpu].sp->num_of_freqs = num_of_freqs;
- }
-}
-
-static __ref int do_sampling(void *data)
-{
- int cpu;
- struct cpu_activity_info *cpu_node;
- static int prev_temp[NR_CPUS];
-
- while (!kthread_should_stop()) {
- wait_for_completion(&sampling_completion);
- cancel_delayed_work(&sampling_work);
-
- mutex_lock(&kthread_update_mutex);
- if (in_suspend)
- goto unlock;
-
- trigger_cpu_pwr_stats_calc();
-
- for_each_online_cpu(cpu) {
- cpu_node = &activity[cpu];
- if (prev_temp[cpu] != cpu_node->temp) {
- prev_temp[cpu] = cpu_node->temp;
- }
- }
- if (!poll_ms)
- goto unlock;
-
- schedule_delayed_work(&sampling_work,
- msecs_to_jiffies(poll_ms));
-unlock:
- mutex_unlock(&kthread_update_mutex);
- }
- return 0;
-}
-
-static void clear_static_power(struct cpu_static_info *sp)
-{
- int i;
-
- if (!sp)
- return;
-
- if (cpumask_first(&sp->mask) < num_possible_cpus())
- return;
-
- for (i = 0; i < TEMP_DATA_POINTS; i++)
- kfree(sp->power[i]);
- kfree(sp->power);
- kfree(sp);
-}
-
-BLOCKING_NOTIFIER_HEAD(msm_core_stats_notifier_list);
-
-struct blocking_notifier_head *get_power_update_notifier(void)
-{
- return &msm_core_stats_notifier_list;
-}
-
-int register_cpu_pwr_stats_ready_notifier(struct notifier_block *nb)
-{
- return blocking_notifier_chain_register(&msm_core_stats_notifier_list,
- nb);
-}
-
-static int update_userspace_power(struct sched_params __user *argp)
-{
- int i;
- int ret;
- int cpu = -1;
- struct cpu_activity_info *node;
- struct cpu_static_info *sp, *clear_sp;
- int cpumask, cluster;
- bool pdata_valid[NR_CPUS] = {0};
- bool cpu_found = false;
-
- get_user(cpumask, &argp->cpumask);
- get_user(cluster, &argp->cluster);
-
- pr_debug("%s: cpumask %d, cluster: %d\n", __func__, cpumask,
- cluster);
- for (i = 0; cpumask > 0; i++, cpumask >>= 1) {
- if (!(cpumask & 0x01))
- continue;
-
- for_each_possible_cpu(cpu) {
- if ((cpu_topology[cpu].core_id != i) ||
- (cpu_topology[cpu].cluster_id != cluster))
- continue;
-
- cpu_found = true;
- break;
- }
- if (cpu_found)
- break;
- }
-
- if ((cpu < 0) || (cpu >= num_possible_cpus()))
- return -EINVAL;
-
- node = &activity[cpu];
- /* Allocate new memory to copy cpumask specific power
- * information.
- */
- sp = kzalloc(sizeof(*sp), GFP_KERNEL);
- if (!sp)
- return -ENOMEM;
-
-
- sp->power = allocate_2d_array_uint32_t(node->sp->num_of_freqs);
- if (IS_ERR_OR_NULL(sp->power)) {
- ret = PTR_ERR(sp->power);
- kfree(sp);
- return ret;
- }
- sp->num_of_freqs = node->sp->num_of_freqs;
- sp->voltage = node->sp->voltage;
- sp->table = node->sp->table;
-
- for (i = 0; i < TEMP_DATA_POINTS; i++) {
- ret = copy_from_user(sp->power[i], &argp->power[i][0],
- sizeof(sp->power[i][0]) * node->sp->num_of_freqs);
- if (ret)
- goto failed;
- }
-
- /* Copy the same power values for all the cpus in the cpumask
- * argp->cpumask within the cluster (argp->cluster)
- */
- get_user(cpumask, &argp->cpumask);
- spin_lock(&update_lock);
- for (i = 0; cpumask > 0; i++, cpumask >>= 1) {
- if (!(cpumask & 0x01))
- continue;
- for_each_possible_cpu(cpu) {
- if (((cpu_topology[cpu].core_id != i) ||
- (cpu_topology[cpu].cluster_id != cluster)))
- continue;
-
- node = &activity[cpu];
- clear_sp = node->sp;
- node->sp = sp;
- cpumask_set_cpu(cpu, &sp->mask);
- if (clear_sp) {
- cpumask_clear_cpu(cpu, &clear_sp->mask);
- clear_static_power(clear_sp);
- }
- cpu_stats[cpu].ptable = per_cpu(ptable, cpu);
- repopulate_stats(cpu);
- pdata_valid[cpu] = true;
- }
- }
- spin_unlock(&update_lock);
-
- for_each_possible_cpu(cpu) {
- if (!pdata_valid[cpu])
- continue;
-
- blocking_notifier_call_chain(
- &msm_core_stats_notifier_list, cpu, NULL);
- }
-
- activate_power_table = true;
- return 0;
-
-failed:
- for (i = 0; i < TEMP_DATA_POINTS; i++)
- kfree(sp->power[i]);
- kfree(sp->power);
- kfree(sp);
- return ret;
-}
-
-static long msm_core_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- long ret = 0;
- struct cpu_activity_info *node = NULL;
- struct sched_params __user *argp = (struct sched_params __user *)arg;
- int i, cpu = num_possible_cpus();
- int cluster, cpumask;
- bool cpu_found = false;
-
- if (!argp)
- return -EINVAL;
-
- get_user(cluster, &argp->cluster);
- get_user(cpumask, &argp->cpumask);
-
- switch (cmd) {
- case EA_LEAKAGE:
- ret = update_userspace_power(argp);
- if (ret)
- pr_err("Userspace power update failed with %ld\n", ret);
- break;
- case EA_VOLT:
- for (i = 0; cpumask > 0; i++, cpumask >>= 1) {
- for_each_possible_cpu(cpu) {
- if (((cpu_topology[cpu].core_id != i) ||
- (cpu_topology[cpu].cluster_id != cluster)))
- continue;
-
- cpu_found = true;
- break;
- }
- if (cpu_found)
- break;
- }
- if (cpu >= num_possible_cpus())
- break;
-
- mutex_lock(&policy_update_mutex);
- node = &activity[cpu];
- if (!node->sp->table) {
- ret = -EINVAL;
- goto unlock;
- }
- ret = copy_to_user((void __user *)&argp->voltage[0],
- node->sp->voltage,
- sizeof(uint32_t) * node->sp->num_of_freqs);
- if (ret)
- break;
- for (i = 0; i < node->sp->num_of_freqs; i++) {
- ret = copy_to_user((void __user *)&argp->freq[i],
- &node->sp->table[i].frequency,
- sizeof(uint32_t));
- if (ret)
- break;
- }
-unlock:
- mutex_unlock(&policy_update_mutex);
- break;
- default:
- break;
- }
-
- return ret;
-}
-
-#ifdef CONFIG_COMPAT
-static long msm_core_compat_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- arg = (unsigned long)compat_ptr(arg);
- return msm_core_ioctl(file, cmd, arg);
-}
-#endif
-
-static int msm_core_open(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static int msm_core_release(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static int msm_core_stats_init(struct device *dev, int cpu)
-{
- int i;
- struct cpu_activity_info *cpu_node;
- struct cpu_pstate_pwr *pstate = NULL;
-
- cpu_node = &activity[cpu];
- cpu_stats[cpu].cpu = cpu;
- cpu_stats[cpu].temp = cpu_node->temp;
- cpu_stats[cpu].throttling = false;
-
- cpu_stats[cpu].len = cpu_node->sp->num_of_freqs;
- pstate = devm_kzalloc(dev,
- sizeof(*pstate) * cpu_node->sp->num_of_freqs,
- GFP_KERNEL);
- if (!pstate)
- return -ENOMEM;
-
- for (i = 0; i < cpu_node->sp->num_of_freqs; i++)
- pstate[i].freq = cpu_node->sp->table[i].frequency;
-
- per_cpu(ptable, cpu) = pstate;
-
- return 0;
-}
-
-static int msm_core_task_init(struct device *dev)
-{
- init_completion(&sampling_completion);
- sampling_task = kthread_run(do_sampling, NULL, "msm-core:sampling");
- if (IS_ERR(sampling_task)) {
- pr_err("Failed to create do_sampling err: %ld\n",
- PTR_ERR(sampling_task));
- return PTR_ERR(sampling_task);
- }
- return 0;
-}
-
-struct cpu_pwr_stats *get_cpu_pwr_stats(void)
-{
- return cpu_stats;
-}
-EXPORT_SYMBOL(get_cpu_pwr_stats);
-
-static int msm_get_power_values(int cpu, struct cpu_static_info *sp)
-{
- int i = 0, j;
- int ret = 0;
- uint64_t power;
-
- /* Calculate dynamic power spent for every frequency using formula:
- * Power = V * V * f
- * where V = voltage for frequency
- * f = frequency
- */
- sp->power = allocate_2d_array_uint32_t(sp->num_of_freqs);
- if (IS_ERR_OR_NULL(sp->power))
- return PTR_ERR(sp->power);
-
- for (i = 0; i < TEMP_DATA_POINTS; i++) {
- for (j = 0; j < sp->num_of_freqs; j++) {
- power = sp->voltage[j] *
- sp->table[j].frequency;
- do_div(power, 1000);
- do_div(power, 1000);
- power *= sp->voltage[j];
- do_div(power, 1000);
- sp->power[i][j] = power;
- }
- }
- return ret;
-}
-
-static int msm_get_voltage_levels(struct device *dev, int cpu,
- struct cpu_static_info *sp)
-{
- unsigned int *voltage;
- int i;
- int corner;
- struct dev_pm_opp *opp;
- struct device *cpu_dev = get_cpu_device(cpu);
- /*
- * Convert cpr corner voltage to average voltage of both
- * a53 and a57 votlage value
- */
- int average_voltage[NUM_OF_CORNERS] = {0, 746, 841, 843, 940, 953, 976,
- 1024, 1090, 1100};
-
- if (!cpu_dev)
- return -ENODEV;
-
- voltage = devm_kzalloc(dev,
- sizeof(*voltage) * sp->num_of_freqs, GFP_KERNEL);
-
- if (!voltage)
- return -ENOMEM;
-
- rcu_read_lock();
- for (i = 0; i < sp->num_of_freqs; i++) {
- opp = dev_pm_opp_find_freq_exact(cpu_dev,
- sp->table[i].frequency * 1000, true);
- corner = dev_pm_opp_get_voltage(opp);
-
- if (corner > 400000)
- voltage[i] = corner / 1000;
- else if (corner > 0 && corner < ARRAY_SIZE(average_voltage))
- voltage[i] = average_voltage[corner];
- else
- voltage[i]
- = average_voltage[ARRAY_SIZE(average_voltage) - 1];
- }
- rcu_read_unlock();
-
- sp->voltage = voltage;
- return 0;
-}
-
-static int msm_core_dyn_pwr_init(struct platform_device *pdev,
- int cpu)
-{
- int ret = 0;
-
- if (!activity[cpu].sp->table)
- return 0;
-
- ret = msm_get_voltage_levels(&pdev->dev, cpu, activity[cpu].sp);
- if (ret)
- return ret;
-
- ret = msm_get_power_values(cpu, activity[cpu].sp);
-
- return ret;
-}
-
-static int msm_core_mpidr_init(struct device_node *phandle)
-{
- int ret = 0;
- char *key = NULL;
- int mpidr;
-
- key = "reg";
- ret = of_property_read_u32(phandle, key,
- &mpidr);
- if (ret) {
- pr_err("%s: Cannot read mpidr\n", __func__);
- return ret;
- }
- return mpidr;
-}
-
-static int msm_core_cpu_policy_handler(struct notifier_block *nb,
- unsigned long val, void *data)
-{
- struct cpufreq_policy *policy = data;
- struct cpu_activity_info *cpu_info = &activity[policy->cpu];
- int cpu;
- int ret;
-
- if (cpu_info->sp->table)
- return NOTIFY_OK;
-
- switch (val) {
- case CPUFREQ_CREATE_POLICY:
- mutex_lock(&policy_update_mutex);
- update_related_freq_table(policy);
-
- for_each_cpu(cpu, policy->related_cpus) {
- ret = msm_core_dyn_pwr_init(msm_core_pdev, cpu);
- if (ret)
- pr_debug("voltage-pwr table update failed\n");
-
- ret = msm_core_stats_init(&msm_core_pdev->dev, cpu);
- if (ret)
- pr_debug("Stats table update failed\n");
- }
- mutex_unlock(&policy_update_mutex);
- break;
- default:
- break;
- }
- return NOTIFY_OK;
-}
-
-struct notifier_block cpu_policy = {
- .notifier_call = msm_core_cpu_policy_handler
-};
-
-static int system_suspend_handler(struct notifier_block *nb,
- unsigned long val, void *data)
-{
- int cpu;
-
- mutex_lock(&kthread_update_mutex);
- switch (val) {
- case PM_POST_HIBERNATION:
- case PM_POST_SUSPEND:
- case PM_POST_RESTORE:
- /*
- * Set completion event to read temperature and repopulate
- * stats
- */
- in_suspend = 0;
- complete(&sampling_completion);
- break;
- case PM_HIBERNATION_PREPARE:
- case PM_SUSPEND_PREPARE:
- /*
- * cancel delayed work to be able to restart immediately
- * after system resume
- */
- in_suspend = 1;
- cancel_delayed_work(&sampling_work);
- /*
- * cancel TSENS interrupts as we do not want to wake up from
- * suspend to take care of repopulate stats while the system is
- * in suspend
- */
- for_each_possible_cpu(cpu) {
- if (activity[cpu].sensor_id < 0)
- continue;
- }
- break;
- default:
- break;
- }
- mutex_unlock(&kthread_update_mutex);
-
- return NOTIFY_OK;
-}
-
-static int msm_core_freq_init(void)
-{
- int cpu;
- struct cpufreq_policy *policy;
-
- for_each_possible_cpu(cpu) {
- activity[cpu].sp = kzalloc(sizeof(*(activity[cpu].sp)),
- GFP_KERNEL);
- if (!activity[cpu].sp)
- return -ENOMEM;
- }
-
- for_each_online_cpu(cpu) {
- if (activity[cpu].sp->table)
- continue;
-
- policy = cpufreq_cpu_get(cpu);
- if (!policy)
- continue;
-
- update_related_freq_table(policy);
- cpufreq_cpu_put(policy);
- }
-
- return 0;
-}
-
-static int msm_core_params_init(struct platform_device *pdev)
-{
- int ret = 0;
- unsigned long cpu = 0;
- struct device_node *child_node = NULL;
- int mpidr;
-
- for_each_possible_cpu(cpu) {
- child_node = of_get_cpu_node(cpu, NULL);
-
- if (!child_node)
- continue;
-
- mpidr = msm_core_mpidr_init(child_node);
- if (mpidr < 0)
- return mpidr;
-
- activity[cpu].mpidr = mpidr;
-
- if (!activity[cpu].sp->table)
- continue;
-
- ret = msm_core_dyn_pwr_init(msm_core_pdev, cpu);
- if (ret)
- pr_debug("voltage-pwr table update failed\n");
-
- ret = msm_core_stats_init(&msm_core_pdev->dev, cpu);
- if (ret)
- pr_debug("Stats table update failed\n");
- }
-
- return 0;
-}
-
-static const struct file_operations msm_core_ops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = msm_core_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = msm_core_compat_ioctl,
-#endif
- .open = msm_core_open,
- .release = msm_core_release,
-};
-
-static struct miscdevice msm_core_device = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "pta",
- .fops = &msm_core_ops
-};
-
-static void free_dyn_memory(void)
-{
- int i, cpu;
-
- for_each_possible_cpu(cpu) {
- if (activity[cpu].sp) {
- for (i = 0; i < TEMP_DATA_POINTS; i++) {
- if (!activity[cpu].sp->power)
- break;
-
- kfree(activity[cpu].sp->power[i]);
- }
- }
- kfree(activity[cpu].sp);
- }
-}
-
-static int msm_core_dev_probe(struct platform_device *pdev)
-{
- int ret = 0;
- char *key = NULL;
- struct device_node *node;
- struct uio_info *info;
-
- if (!pdev)
- return -ENODEV;
-
- msm_core_pdev = pdev;
- node = pdev->dev.of_node;
- if (!node)
- return -ENODEV;
-
- key = "qcom,low-hyst-temp";
- ret = of_property_read_u32(node, key, &low_hyst_temp);
- if (ret)
- low_hyst_temp = DEFAULT_LOW_HYST_TEMP;
-
- key = "qcom,high-hyst-temp";
- ret = of_property_read_u32(node, key, &high_hyst_temp);
- if (ret)
- high_hyst_temp = DEFAULT_HIGH_HYST_TEMP;
-
- key = "qcom,polling-interval";
- ret = of_property_read_u32(node, key, &poll_ms);
- if (ret)
- pr_info("msm-core initialized without polling period\n");
-
- key = "qcom,throttling-temp";
- ret = of_property_read_u32(node, key, &max_throttling_temp);
-
- ret = msm_core_freq_init();
- if (ret)
- goto failed;
-
- ret = misc_register(&msm_core_device);
- if (ret) {
- pr_err("%s: Error registering device %d\n", __func__, ret);
- goto failed;
- }
-
- ret = msm_core_params_init(pdev);
- if (ret)
- goto failed;
-
- ret = msm_core_task_init(&pdev->dev);
- if (ret)
- goto failed;
-
- INIT_DEFERRABLE_WORK(&sampling_work, samplequeue_handle);
- schedule_delayed_work(&sampling_work, msecs_to_jiffies(0));
- cpufreq_register_notifier(&cpu_policy, CPUFREQ_POLICY_NOTIFIER);
- pm_notifier(system_suspend_handler, 0);
- return 0;
-failed:
- info = dev_get_drvdata(&pdev->dev);
- uio_unregister_device(info);
- free_dyn_memory();
- return ret;
-}
-
-static int msm_core_remove(struct platform_device *pdev)
-{
- int cpu;
- struct uio_info *info = dev_get_drvdata(&pdev->dev);
-
- uio_unregister_device(info);
-
- for_each_possible_cpu(cpu) {
- if (activity[cpu].sensor_id < 0)
- continue;
- }
- free_dyn_memory();
- misc_deregister(&msm_core_device);
- return 0;
-}
-
-static const struct of_device_id msm_core_match_table[] = {
- {.compatible = "qcom,apss-core-ea"},
- {},
-};
-
-static struct platform_driver msm_core_driver = {
- .probe = msm_core_dev_probe,
- .driver = {
- .name = "msm_core",
- .owner = THIS_MODULE,
- .of_match_table = msm_core_match_table,
- },
- .remove = msm_core_remove,
-};
-
-static int __init msm_core_init(void)
-{
- return platform_driver_register(&msm_core_driver);
-}
-late_initcall(msm_core_init);
diff --git a/drivers/soc/qcom/qbt1000.c b/drivers/soc/qcom/qbt1000.c
index e4ada03..86f314a 100644
--- a/drivers/soc/qcom/qbt1000.c
+++ b/drivers/soc/qcom/qbt1000.c
@@ -150,18 +150,17 @@
uint32_t *rsp_len)
{
/* 64 bytes alignment for QSEECOM */
- *cmd_len = ALIGN(*cmd_len, 64);
- *rsp_len = ALIGN(*rsp_len, 64);
+ uint64_t aligned_cmd_len = ALIGN((uint64_t)*cmd_len, 64);
+ uint64_t aligned_rsp_len = ALIGN((uint64_t)*rsp_len, 64);
- if (((uint64_t)*rsp_len + (uint64_t)*cmd_len)
- > (uint64_t)g_app_buf_size) {
- pr_err("buffer too small to hold cmd=%d and rsp=%d\n",
- *cmd_len, *rsp_len);
+ if ((aligned_rsp_len + aligned_cmd_len) > (uint64_t)g_app_buf_size)
return -ENOMEM;
- }
*cmd = hdl->sbuf;
+ *cmd_len = aligned_cmd_len;
*rsp = hdl->sbuf + *cmd_len;
+ *rsp_len = aligned_rsp_len;
+
return 0;
}
@@ -318,6 +317,12 @@
drvdata = file->private_data;
+ if (IS_ERR(priv_arg)) {
+ dev_err(drvdata->dev, "%s: invalid user space pointer %lu\n",
+ __func__, arg);
+ return -EINVAL;
+ }
+
mutex_lock(&drvdata->mutex);
pr_debug("qbt1000_ioctl %d\n", cmd);
@@ -362,6 +367,7 @@
}
pr_debug("app %s load before\n", app.name);
+ app.name[MAX_NAME_SIZE - 1] = '\0';
/* start the TZ app */
rc = qseecom_start_app(
@@ -375,7 +381,8 @@
pr_err("App %s failed to set bw\n", app.name);
}
} else {
- pr_err("app %s failed to load\n", app.name);
+ dev_err(drvdata->dev, "%s: Fingerprint Trusted App failed to load\n",
+ __func__);
goto end;
}
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index d6239fa..3f3751e 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -1458,6 +1458,10 @@
{ PCI_VDEVICE(INTEL, 0x1ac2), LPSS_BXT_SSP },
{ PCI_VDEVICE(INTEL, 0x1ac4), LPSS_BXT_SSP },
{ PCI_VDEVICE(INTEL, 0x1ac6), LPSS_BXT_SSP },
+ /* GLK */
+ { PCI_VDEVICE(INTEL, 0x31c2), LPSS_BXT_SSP },
+ { PCI_VDEVICE(INTEL, 0x31c4), LPSS_BXT_SSP },
+ { PCI_VDEVICE(INTEL, 0x31c6), LPSS_BXT_SSP },
/* APL */
{ PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP },
{ PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP },
diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c
index 1cf6b79..eeacb0e 100644
--- a/drivers/staging/iio/adc/ad7192.c
+++ b/drivers/staging/iio/adc/ad7192.c
@@ -222,11 +222,9 @@
struct iio_dev *indio_dev = spi_get_drvdata(st->sd.spi);
unsigned long long scale_uv;
int i, ret, id;
- u8 ones[6];
/* reset the serial interface */
- memset(&ones, 0xFF, 6);
- ret = spi_write(st->sd.spi, &ones, 6);
+ ret = ad_sd_reset(&st->sd, 48);
if (ret < 0)
goto out;
usleep_range(500, 1000); /* Wait for at least 500us */
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
index 1091b9f..f72eebc 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
@@ -106,8 +106,14 @@
g_virt_to_bus_offset = virt_to_dma(dev, (void *)0);
- (void)of_property_read_u32(dev->of_node, "cache-line-size",
+ err = of_property_read_u32(dev->of_node, "cache-line-size",
&g_cache_line_size);
+
+ if (err) {
+ dev_err(dev, "Missing cache-line-size property\n");
+ return -ENODEV;
+ }
+
g_fragments_size = 2 * g_cache_line_size;
/* Allocate space for the channels in coherent memory */
@@ -538,18 +544,20 @@
if (head_bytes > actual)
head_bytes = actual;
- memcpy((char *)page_address(pages[0]) +
+ memcpy((char *)kmap(pages[0]) +
pagelist->offset,
fragments,
head_bytes);
+ kunmap(pages[0]);
}
if ((actual >= 0) && (head_bytes < actual) &&
(tail_bytes != 0)) {
- memcpy((char *)page_address(pages[num_pages - 1]) +
+ memcpy((char *)kmap(pages[num_pages - 1]) +
((pagelist->offset + actual) &
(PAGE_SIZE - 1) & ~(g_cache_line_size - 1)),
fragments + g_cache_line_size,
tail_bytes);
+ kunmap(pages[num_pages - 1]);
}
down(&g_free_fragments_mutex);
diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c
index efc453e..ab92a1b 100644
--- a/drivers/target/iscsi/iscsi_target_erl0.c
+++ b/drivers/target/iscsi/iscsi_target_erl0.c
@@ -44,10 +44,8 @@
*/
if (cmd->unsolicited_data) {
cmd->seq_start_offset = cmd->write_data_done;
- cmd->seq_end_offset = (cmd->write_data_done +
- ((cmd->se_cmd.data_length >
- conn->sess->sess_ops->FirstBurstLength) ?
- conn->sess->sess_ops->FirstBurstLength : cmd->se_cmd.data_length));
+ cmd->seq_end_offset = min(cmd->se_cmd.data_length,
+ conn->sess->sess_ops->FirstBurstLength);
return;
}
diff --git a/drivers/thermal/qpnp-adc-tm.c b/drivers/thermal/qpnp-adc-tm.c
index 04320d8..411588e 100644
--- a/drivers/thermal/qpnp-adc-tm.c
+++ b/drivers/thermal/qpnp-adc-tm.c
@@ -1730,6 +1730,7 @@
param->gain_num = qpnp_vadc_amux_scaling_ratio[amux_prescaling].num;
param->gain_den = qpnp_vadc_amux_scaling_ratio[amux_prescaling].den;
param->adc_tm_hc = chip->adc_tm_hc;
+ param->full_scale_code = chip->adc->adc_prop->full_scale_code;
chip->adc->amux_prop->amux_channel = channel;
chip->adc->amux_prop->decimation =
chip->adc->adc_channels[dt_index].adc_decimation;
diff --git a/drivers/thermal/tsens-dbg.c b/drivers/thermal/tsens-dbg.c
index 9b10a1b..2e795b1 100644
--- a/drivers/thermal/tsens-dbg.c
+++ b/drivers/thermal/tsens-dbg.c
@@ -77,11 +77,10 @@
pr_debug("%d %d\n", id, dbg_type);
tmdev = data;
/* debug */
- idx = tmdev->tsens_dbg.tsens_thread_iq_dbg.idx;
- tmdev->tsens_dbg.tsens_thread_iq_dbg.dbg_count[idx%10]++;
- tmdev->tsens_dbg.tsens_thread_iq_dbg.time_stmp[idx%10] =
+ idx = tmdev->tsens_dbg.irq_idx;
+ tmdev->tsens_dbg.irq_time_stmp[idx%10] =
sched_clock();
- tmdev->tsens_dbg.tsens_thread_iq_dbg.idx++;
+ tmdev->tsens_dbg.irq_idx++;
return 0;
}
diff --git a/drivers/thermal/tsens.h b/drivers/thermal/tsens.h
index a695d57..ec2d592 100644
--- a/drivers/thermal/tsens.h
+++ b/drivers/thermal/tsens.h
@@ -48,7 +48,6 @@
#endif
struct tsens_dbg {
- u32 dbg_count[DEBUG_SIZE];
u32 idx;
unsigned long long time_stmp[DEBUG_SIZE];
unsigned long temp[DEBUG_SIZE];
@@ -56,9 +55,10 @@
struct tsens_dbg_context {
struct tsens_device *tmdev;
- struct tsens_dbg tsens_thread_iq_dbg;
struct tsens_dbg sensor_dbg_info[TSENS_MAX_SENSORS];
int tsens_critical_wd_cnt;
+ u32 irq_idx;
+ unsigned long long irq_time_stmp[DEBUG_SIZE];
struct delayed_work tsens_critical_poll_test;
};
@@ -120,7 +120,6 @@
struct device *dev;
struct platform_device *pdev;
struct list_head list;
- u32 num_sensors;
struct regmap *map;
struct regmap_field *status_field;
void __iomem *tsens_srot_addr;
diff --git a/drivers/thermal/tsens2xxx.c b/drivers/thermal/tsens2xxx.c
index de9f27f..fd625ae 100644
--- a/drivers/thermal/tsens2xxx.c
+++ b/drivers/thermal/tsens2xxx.c
@@ -94,7 +94,7 @@
if (code & TSENS_TM_SN_STATUS_VALID_BIT) {
msm_tsens_convert_temp(last_temp, temp);
- return 0;
+ goto dbg;
}
code = readl_relaxed_no_log(sensor_addr +
@@ -103,7 +103,7 @@
if (code & TSENS_TM_SN_STATUS_VALID_BIT) {
last_temp = last_temp2;
msm_tsens_convert_temp(last_temp, temp);
- return 0;
+ goto dbg;
}
code = readl_relaxed_no_log(sensor_addr +
@@ -113,7 +113,7 @@
if (code & TSENS_TM_SN_STATUS_VALID_BIT) {
last_temp = last_temp3;
msm_tsens_convert_temp(last_temp, temp);
- return 0;
+ goto dbg;
}
if (last_temp == last_temp2)
@@ -123,6 +123,7 @@
msm_tsens_convert_temp(last_temp, temp);
+dbg:
if (tmdev->ops->dbg)
tmdev->ops->dbg(tmdev, (u32) sensor->hw_id,
TSENS_DBG_LOG_TEMP_READS, temp);
diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c
index 3fc9123..996bd47 100644
--- a/drivers/tty/goldfish.c
+++ b/drivers/tty/goldfish.c
@@ -300,7 +300,7 @@
return 0;
err_tty_register_device_failed:
- free_irq(irq, pdev);
+ free_irq(irq, qtty);
err_request_irq_failed:
goldfish_tty_current_line_count--;
if (goldfish_tty_current_line_count == 0)
diff --git a/drivers/tty/serial/8250/8250_moxa.c b/drivers/tty/serial/8250/8250_moxa.c
index 26eb539..d5069b2 100644
--- a/drivers/tty/serial/8250/8250_moxa.c
+++ b/drivers/tty/serial/8250/8250_moxa.c
@@ -68,6 +68,7 @@
sizeof(unsigned int) * nr_ports, GFP_KERNEL);
if (!brd)
return -ENOMEM;
+ brd->num_ports = nr_ports;
memset(&uart, 0, sizeof(struct uart_8250_port));
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index f24d303..1ef31e3 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -1751,8 +1751,6 @@
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
- pr_debug("%s: THRE\n", __func__);
-
/*
* With RPM enabled, we have to wait until the FIFO is empty before the
* HW can go idle. So we get here once again with empty FIFO and disable
@@ -1817,8 +1815,6 @@
status = serial_port_in(port, UART_LSR);
- pr_debug("%s: status = %x\n", __func__, status);
-
if (status & (UART_LSR_DR | UART_LSR_BI)) {
if (!up->dma || handle_rx_dma(up, iir))
status = serial8250_rx_chars(up, status);
diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c
index 4e603d0..59828d8 100644
--- a/drivers/tty/serial/sunhv.c
+++ b/drivers/tty/serial/sunhv.c
@@ -398,6 +398,12 @@
static struct uart_port *sunhv_port;
+void sunhv_migrate_hvcons_irq(int cpu)
+{
+ /* Migrate hvcons irq to param cpu */
+ irq_force_affinity(sunhv_port->irq, cpumask_of(cpu));
+}
+
/* Copy 's' into the con_write_page, decoding "\n" into
* "\r\n" along the way. We have to return two lengths
* because the caller needs to know how much to advance
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index aa80dc9..c220c2c 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -362,6 +362,32 @@
EXPORT_SYMBOL(tty_insert_flip_string_flags);
/**
+ * __tty_insert_flip_char - Add one character to the tty buffer
+ * @port: tty port
+ * @ch: character
+ * @flag: flag byte
+ *
+ * Queue a single byte to the tty buffering, with an optional flag.
+ * This is the slow path of tty_insert_flip_char.
+ */
+int __tty_insert_flip_char(struct tty_port *port, unsigned char ch, char flag)
+{
+ struct tty_buffer *tb;
+ int flags = (flag == TTY_NORMAL) ? TTYB_NORMAL : 0;
+
+ if (!__tty_buffer_request_room(port, 1, flags))
+ return 0;
+
+ tb = port->buf.tail;
+ if (~tb->flags & TTYB_NORMAL)
+ *flag_buf_ptr(tb, tb->used) = flag;
+ *char_buf_ptr(tb, tb->used++) = ch;
+
+ return 1;
+}
+EXPORT_SYMBOL(__tty_insert_flip_char);
+
+/**
* tty_schedule_flip - push characters to ldisc
* @port: tty port to push from
*
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index 0cf149e..f36a1ac3 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -134,9 +134,9 @@
if (!ci->is_otg)
return;
- if (hw_read_otgsc(ci, OTGSC_BSV))
+ if (hw_read_otgsc(ci, OTGSC_BSV) && !ci->vbus_active)
usb_gadget_vbus_connect(&ci->gadget);
- else
+ else if (!hw_read_otgsc(ci, OTGSC_BSV) && ci->vbus_active)
usb_gadget_vbus_disconnect(&ci->gadget);
}
@@ -175,14 +175,21 @@
ci_role_stop(ci);
- if (role == CI_ROLE_GADGET)
+ if (role == CI_ROLE_GADGET &&
+ IS_ERR(ci->platdata->vbus_extcon.edev))
/*
- * wait vbus lower than OTGSC_BSV before connecting
- * to host
+ * Wait vbus lower than OTGSC_BSV before connecting
+ * to host. If connecting status is from an external
+ * connector instead of register, we don't need to
+ * care vbus on the board, since it will not affect
+ * external connector status.
*/
hw_wait_vbus_lower_bsv(ci);
ci_role_start(ci, role);
+ /* vbus change may have already occurred */
+ if (role == CI_ROLE_GADGET)
+ ci_handle_vbus_change(ci);
}
}
/**
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 0b845e5..9f00165 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -194,8 +194,10 @@
/*
* only set a new error if there is no previous error.
* Errors are only cleared during read/open
+ * Avoid propagating -EPIPE (stall) to userspace since it is
+ * better handled as an empty read
*/
- if (desc->rerr == 0)
+ if (desc->rerr == 0 && status != -EPIPE)
desc->rerr = status;
if (length + desc->length > desc->wMaxCommand) {
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 3fd2b54..6e7bafe 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -638,15 +638,23 @@
} else if (header->bDescriptorType ==
USB_DT_INTERFACE_ASSOCIATION) {
+ struct usb_interface_assoc_descriptor *d;
+
+ d = (struct usb_interface_assoc_descriptor *)header;
+ if (d->bLength < USB_DT_INTERFACE_ASSOCIATION_SIZE) {
+ dev_warn(ddev,
+ "config %d has an invalid interface association descriptor of length %d, skipping\n",
+ cfgno, d->bLength);
+ continue;
+ }
+
if (iad_num == USB_MAXIADS) {
dev_warn(ddev, "found more Interface "
"Association Descriptors "
"than allocated for in "
"configuration %d\n", cfgno);
} else {
- config->intf_assoc[iad_num] =
- (struct usb_interface_assoc_descriptor
- *)header;
+ config->intf_assoc[iad_num] = d;
iad_num++;
}
@@ -847,7 +855,7 @@
}
if (dev->quirks & USB_QUIRK_DELAY_INIT)
- msleep(100);
+ msleep(200);
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
bigbuffer, length);
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index c8075eb..860108c 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -1577,7 +1577,11 @@
totlen += isopkt[u].length;
}
u *= sizeof(struct usb_iso_packet_descriptor);
- uurb->buffer_length = totlen;
+ if (totlen <= uurb->buffer_length)
+ uurb->buffer_length = totlen;
+ else
+ WARN_ONCE(1, "uurb->buffer_length is too short %d vs %d",
+ totlen, uurb->buffer_length);
break;
default:
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 3b0cc03..1c9fbcb 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -4841,7 +4841,7 @@
goto loop;
if (udev->quirks & USB_QUIRK_DELAY_INIT)
- msleep(1000);
+ msleep(2000);
/* consecutive bus-powered hubs aren't reliable; they can
* violate the voltage drop budget. if the new child has
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 41a9845..4f8221e 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -2237,6 +2237,10 @@
elength = 1;
goto next_desc;
}
+ if ((buflen < elength) || (elength < 3)) {
+ dev_err(&intf->dev, "invalid descriptor buffer length\n");
+ break;
+ }
if (buffer[1] != USB_DT_CS_INTERFACE) {
dev_err(&intf->dev, "skipping garbage\n");
goto next_desc;
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 5ca987a..4ddb332 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -690,8 +690,6 @@
{
dwc3_event_buffers_cleanup(dwc);
- usb_phy_shutdown(dwc->usb2_phy);
- usb_phy_shutdown(dwc->usb3_phy);
phy_exit(dwc->usb2_generic_phy);
phy_exit(dwc->usb3_generic_phy);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 9d247b8..4f4f7e8 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -950,9 +950,42 @@
if (!node) {
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
+ /*
+ * USB Specification 2.0 Section 5.9.2 states that: "If
+ * there is only a single transaction in the microframe,
+ * only a DATA0 data packet PID is used. If there are
+ * two transactions per microframe, DATA1 is used for
+ * the first transaction data packet and DATA0 is used
+ * for the second transaction data packet. If there are
+ * three transactions per microframe, DATA2 is used for
+ * the first transaction data packet, DATA1 is used for
+ * the second, and DATA0 is used for the third."
+ *
+ * IOW, we should satisfy the following cases:
+ *
+ * 1) length <= maxpacket
+ * - DATA0
+ *
+ * 2) maxpacket < length <= (2 * maxpacket)
+ * - DATA1, DATA0
+ *
+ * 3) (2 * maxpacket) < length <= (3 * maxpacket)
+ * - DATA2, DATA1, DATA0
+ */
if (speed == USB_SPEED_HIGH) {
struct usb_ep *ep = &dep->endpoint;
- trb->size |= DWC3_TRB_SIZE_PCM1(ep->mult - 1);
+ unsigned int mult = ep->mult - 1;
+ unsigned int maxp;
+
+ maxp = usb_endpoint_maxp(ep->desc) & 0x07ff;
+
+ if (length <= (2 * maxp))
+ mult--;
+
+ if (length <= maxp)
+ mult--;
+
+ trb->size |= DWC3_TRB_SIZE_PCM1(mult);
}
} else {
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
@@ -1403,6 +1436,48 @@
if (r == req) {
/* wait until it is processed */
dwc3_stop_active_transfer(dwc, dep->number, true);
+
+ /*
+ * If request was already started, this means we had to
+ * stop the transfer. With that we also need to ignore
+ * all TRBs used by the request, however TRBs can only
+ * be modified after completion of END_TRANSFER
+ * command. So what we do here is that we wait for
+ * END_TRANSFER completion and only after that, we jump
+ * over TRBs by clearing HWO and incrementing dequeue
+ * pointer.
+ *
+ * Note that we have 2 possible types of transfers here:
+ *
+ * i) Linear buffer request
+ * ii) SG-list based request
+ *
+ * SG-list based requests will have r->num_pending_sgs
+ * set to a valid number (> 0). Linear requests,
+ * normally use a single TRB.
+ *
+ * All of these cases need to be taken into
+ * consideration so we don't mess up our TRB ring
+ * pointers.
+ */
+ if (!r->trb)
+ goto out1;
+
+ if (r->num_pending_sgs) {
+ struct dwc3_trb *trb;
+ int i = 0;
+
+ for (i = 0; i < r->num_pending_sgs; i++) {
+ trb = r->trb + i;
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ dwc3_ep_inc_deq(dep);
+ }
+ } else {
+ struct dwc3_trb *trb = r->trb;
+
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ dwc3_ep_inc_deq(dep);
+ }
goto out1;
}
dev_err(dwc->dev, "request %pK was not queued to %s\n",
@@ -1414,6 +1489,7 @@
out1:
dbg_event(dep->number, "DEQUEUE", 0);
/* giveback the request */
+ dep->queued_requests--;
dwc3_gadget_giveback(dep, req, -ECONNRESET);
out0:
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index e32de9a..98509f2 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -2164,6 +2164,8 @@
static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_gadget_strings *gstr = cdev->driver->strings[0];
+ struct usb_string *dev_str = gstr->strings;
/* composite_disconnect() must already have been called
* by the underlying peripheral controller driver!
@@ -2183,6 +2185,9 @@
composite_dev_cleanup(cdev);
+ if (dev_str[USB_GADGET_MANUFACTURER_IDX].s == cdev->def_manufacturer)
+ dev_str[USB_GADGET_MANUFACTURER_IDX].s = "";
+
kfree(cdev->def_manufacturer);
kfree(cdev);
set_gadget_data(gadget, NULL);
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index d6e77a5..885ed26 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1179,11 +1179,12 @@
NULL
};
-int usb_os_desc_prepare_interf_dir(struct config_group *parent,
- int n_interf,
- struct usb_os_desc **desc,
- char **names,
- struct module *owner)
+struct config_group *usb_os_desc_prepare_interf_dir(
+ struct config_group *parent,
+ int n_interf,
+ struct usb_os_desc **desc,
+ char **names,
+ struct module *owner)
{
struct config_group *os_desc_group;
struct config_item_type *os_desc_type, *interface_type;
@@ -1195,7 +1196,7 @@
char *vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL);
if (!vlabuf)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
os_desc_group = vla_ptr(vlabuf, data_chunk, os_desc_group);
os_desc_type = vla_ptr(vlabuf, data_chunk, os_desc_type);
@@ -1220,7 +1221,7 @@
configfs_add_default_group(&d->group, os_desc_group);
}
- return 0;
+ return os_desc_group;
}
EXPORT_SYMBOL(usb_os_desc_prepare_interf_dir);
diff --git a/drivers/usb/gadget/configfs.h b/drivers/usb/gadget/configfs.h
index 36c468c..540d5e9 100644
--- a/drivers/usb/gadget/configfs.h
+++ b/drivers/usb/gadget/configfs.h
@@ -5,11 +5,12 @@
void unregister_gadget_item(struct config_item *item);
-int usb_os_desc_prepare_interf_dir(struct config_group *parent,
- int n_interf,
- struct usb_os_desc **desc,
- char **names,
- struct module *owner);
+struct config_group *usb_os_desc_prepare_interf_dir(
+ struct config_group *parent,
+ int n_interf,
+ struct usb_os_desc **desc,
+ char **names,
+ struct module *owner);
static inline struct usb_os_desc *to_usb_os_desc(struct config_item *item)
{
diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c
index 1590927..9f7a29a 100644
--- a/drivers/usb/gadget/function/f_audio_source.c
+++ b/drivers/usb/gadget/function/f_audio_source.c
@@ -755,11 +755,11 @@
struct audio_dev *audio = substream->private_data;
unsigned long flags;
- spin_lock_irqsave(&audio->lock, flags);
-
/* Remove the QoS request */
pm_qos_remove_request(&audio->pm_qos);
+ spin_lock_irqsave(&audio->lock, flags);
+
audio->substream = NULL;
spin_unlock_irqrestore(&audio->lock, flags);
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 0b758236..33ed64f 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -306,8 +306,6 @@
struct completion thread_notifier;
struct task_struct *thread_task;
- /* Callback functions. */
- const struct fsg_operations *ops;
/* Gadget's private data. */
void *private_data;
@@ -2540,6 +2538,7 @@
static int fsg_main_thread(void *common_)
{
struct fsg_common *common = common_;
+ int i;
/*
* Allow the thread to be killed by a signal, but set the signal mask
@@ -2601,21 +2600,16 @@
common->thread_task = NULL;
spin_unlock_irq(&common->lock);
- if (!common->ops || !common->ops->thread_exits
- || common->ops->thread_exits(common) < 0) {
- int i;
+ /* Eject media from all LUNs */
- down_write(&common->filesem);
- for (i = 0; i < ARRAY_SIZE(common->luns); --i) {
- struct fsg_lun *curlun = common->luns[i];
- if (!curlun || !fsg_lun_is_open(curlun))
- continue;
+ down_write(&common->filesem);
+ for (i = 0; i < ARRAY_SIZE(common->luns); i++) {
+ struct fsg_lun *curlun = common->luns[i];
+ if (curlun && fsg_lun_is_open(curlun))
fsg_lun_close(curlun);
- curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
- }
- up_write(&common->filesem);
}
+ up_write(&common->filesem);
/* Let fsg_unbind() know the thread has exited */
complete_and_exit(&common->thread_notifier, 0);
@@ -2805,13 +2799,6 @@
}
EXPORT_SYMBOL_GPL(fsg_common_remove_luns);
-void fsg_common_set_ops(struct fsg_common *common,
- const struct fsg_operations *ops)
-{
- common->ops = ops;
-}
-EXPORT_SYMBOL_GPL(fsg_common_set_ops);
-
void fsg_common_free_buffers(struct fsg_common *common)
{
_fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers);
diff --git a/drivers/usb/gadget/function/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h
index d390231..dc05ca0 100644
--- a/drivers/usb/gadget/function/f_mass_storage.h
+++ b/drivers/usb/gadget/function/f_mass_storage.h
@@ -60,17 +60,6 @@
struct fsg_common;
/* FSF callback functions */
-struct fsg_operations {
- /*
- * Callback function to call when thread exits. If no
- * callback is set or it returns value lower then zero MSF
- * will force eject all LUNs it operates on (including those
- * marked as non-removable or with prevent_medium_removal flag
- * set).
- */
- int (*thread_exits)(struct fsg_common *common);
-};
-
struct fsg_lun_opts {
struct config_group group;
struct fsg_lun *lun;
@@ -142,9 +131,6 @@
void fsg_common_remove_luns(struct fsg_common *common);
-void fsg_common_set_ops(struct fsg_common *common,
- const struct fsg_operations *ops);
-
int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
unsigned int id, const char *name,
const char **name_pfx);
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index 41e1b47..56a8e1b 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -920,6 +920,7 @@
free_netdev(opts->net);
}
+ kfree(opts->rndis_interf_group); /* single VLA chunk */
kfree(opts);
}
@@ -928,6 +929,7 @@
struct f_rndis_opts *opts;
struct usb_os_desc *descs[1];
char *names[1];
+ struct config_group *rndis_interf_group;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
@@ -948,8 +950,14 @@
names[0] = "rndis";
config_group_init_type_name(&opts->func_inst.group, "",
&rndis_func_type);
- usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
- names, THIS_MODULE);
+ rndis_interf_group =
+ usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
+ names, THIS_MODULE);
+ if (IS_ERR(rndis_interf_group)) {
+ rndis_free_inst(&opts->func_inst);
+ return ERR_CAST(rndis_interf_group);
+ }
+ opts->rndis_interf_group = rndis_interf_group;
return &opts->func_inst;
}
diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h
index 4eafd50..4e2ad04 100644
--- a/drivers/usb/gadget/function/u_rndis.h
+++ b/drivers/usb/gadget/function/u_rndis.h
@@ -26,6 +26,7 @@
bool bound;
bool borrowed_net;
+ struct config_group *rndis_interf_group;
struct usb_os_desc rndis_os_desc;
char rndis_ext_compat_id[16];
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index f959c42..f69dbd4 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -27,7 +27,7 @@
#include <linux/mmu_context.h>
#include <linux/aio.h>
#include <linux/uio.h>
-
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
@@ -116,6 +116,7 @@
struct dev_data {
spinlock_t lock;
atomic_t count;
+ int udc_usage;
enum ep0_state state; /* P: lock */
struct usb_gadgetfs_event event [N_EVENT];
unsigned ev_next;
@@ -513,9 +514,9 @@
INIT_WORK(&priv->work, ep_user_copy_worker);
schedule_work(&priv->work);
}
- spin_unlock(&epdata->dev->lock);
usb_ep_free_request(ep, req);
+ spin_unlock(&epdata->dev->lock);
put_ep(epdata);
}
@@ -939,9 +940,11 @@
struct usb_request *req = dev->req;
if ((retval = setup_req (ep, req, 0)) == 0) {
+ ++dev->udc_usage;
spin_unlock_irq (&dev->lock);
retval = usb_ep_queue (ep, req, GFP_KERNEL);
spin_lock_irq (&dev->lock);
+ --dev->udc_usage;
}
dev->state = STATE_DEV_CONNECTED;
@@ -983,11 +986,14 @@
retval = -EIO;
else {
len = min (len, (size_t)dev->req->actual);
-// FIXME don't call this with the spinlock held ...
+ ++dev->udc_usage;
+ spin_unlock_irq(&dev->lock);
if (copy_to_user (buf, dev->req->buf, len))
retval = -EFAULT;
else
retval = len;
+ spin_lock_irq(&dev->lock);
+ --dev->udc_usage;
clean_req (dev->gadget->ep0, dev->req);
/* NOTE userspace can't yet choose to stall */
}
@@ -1131,6 +1137,7 @@
retval = setup_req (dev->gadget->ep0, dev->req, len);
if (retval == 0) {
dev->state = STATE_DEV_CONNECTED;
+ ++dev->udc_usage;
spin_unlock_irq (&dev->lock);
if (copy_from_user (dev->req->buf, buf, len))
retval = -EFAULT;
@@ -1142,6 +1149,7 @@
GFP_KERNEL);
}
spin_lock_irq(&dev->lock);
+ --dev->udc_usage;
if (retval < 0) {
clean_req (dev->gadget->ep0, dev->req);
} else
@@ -1243,9 +1251,21 @@
struct usb_gadget *gadget = dev->gadget;
long ret = -ENOTTY;
- if (gadget->ops->ioctl)
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_DEV_OPENED ||
+ dev->state == STATE_DEV_UNBOUND) {
+ /* Not bound to a UDC */
+ } else if (gadget->ops->ioctl) {
+ ++dev->udc_usage;
+ spin_unlock_irq(&dev->lock);
+
ret = gadget->ops->ioctl (gadget, code, value);
+ spin_lock_irq(&dev->lock);
+ --dev->udc_usage;
+ }
+ spin_unlock_irq(&dev->lock);
+
return ret;
}
@@ -1463,10 +1483,12 @@
if (value < 0)
break;
+ ++dev->udc_usage;
spin_unlock (&dev->lock);
value = usb_ep_queue (gadget->ep0, dev->req,
GFP_KERNEL);
spin_lock (&dev->lock);
+ --dev->udc_usage;
if (value < 0) {
clean_req (gadget->ep0, dev->req);
break;
@@ -1490,8 +1512,12 @@
req->length = value;
req->zero = value < w_length;
+ ++dev->udc_usage;
spin_unlock (&dev->lock);
value = usb_ep_queue (gadget->ep0, req, GFP_KERNEL);
+ spin_lock(&dev->lock);
+ --dev->udc_usage;
+ spin_unlock(&dev->lock);
if (value < 0) {
DBG (dev, "ep_queue --> %d\n", value);
req->status = 0;
@@ -1518,21 +1544,24 @@
/* break link to FS */
ep = list_first_entry (&dev->epfiles, struct ep_data, epfiles);
list_del_init (&ep->epfiles);
+ spin_unlock_irq (&dev->lock);
+
dentry = ep->dentry;
ep->dentry = NULL;
parent = d_inode(dentry->d_parent);
/* break link to controller */
+ mutex_lock(&ep->lock);
if (ep->state == STATE_EP_ENABLED)
(void) usb_ep_disable (ep->ep);
ep->state = STATE_EP_UNBOUND;
usb_ep_free_request (ep->ep, ep->req);
ep->ep = NULL;
+ mutex_unlock(&ep->lock);
+
wake_up (&ep->wait);
put_ep (ep);
- spin_unlock_irq (&dev->lock);
-
/* break link to dcache */
inode_lock(parent);
d_delete (dentry);
@@ -1603,6 +1632,11 @@
spin_lock_irq (&dev->lock);
dev->state = STATE_DEV_UNBOUND;
+ while (dev->udc_usage > 0) {
+ spin_unlock_irq(&dev->lock);
+ usleep_range(1000, 2000);
+ spin_lock_irq(&dev->lock);
+ }
spin_unlock_irq (&dev->lock);
destroy_ep_files (dev);
diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c
index 125974f..fcba597 100644
--- a/drivers/usb/gadget/legacy/mass_storage.c
+++ b/drivers/usb/gadget/legacy/mass_storage.c
@@ -107,15 +107,6 @@
FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
-static unsigned long msg_registered;
-static void msg_cleanup(void);
-
-static int msg_thread_exits(struct fsg_common *common)
-{
- msg_cleanup();
- return 0;
-}
-
static int msg_do_config(struct usb_configuration *c)
{
struct fsg_opts *opts;
@@ -154,9 +145,6 @@
static int msg_bind(struct usb_composite_dev *cdev)
{
- static const struct fsg_operations ops = {
- .thread_exits = msg_thread_exits,
- };
struct fsg_opts *opts;
struct fsg_config config;
int status;
@@ -173,8 +161,6 @@
if (status)
goto fail;
- fsg_common_set_ops(opts->common, &ops);
-
status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);
if (status)
goto fail_set_cdev;
@@ -210,7 +196,6 @@
usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&cdev->gadget->dev,
DRIVER_DESC ", version: " DRIVER_VERSION "\n");
- set_bit(0, &msg_registered);
return 0;
fail_otg_desc:
@@ -261,9 +246,8 @@
}
module_init(msg_init);
-static void msg_cleanup(void)
+static void __exit msg_cleanup(void)
{
- if (test_and_clear_bit(0, &msg_registered))
- usb_composite_unregister(&msg_driver);
+ usb_composite_unregister(&msg_driver);
}
module_exit(msg_cleanup);
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index a95b3e7..ad84029 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -28,6 +28,8 @@
#include <linux/of_gpio.h>
#include "atmel_usba_udc.h"
+#define USBA_VBUS_IRQFLAGS (IRQF_ONESHOT \
+ | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING)
#ifdef CONFIG_USB_GADGET_DEBUG_FS
#include <linux/debugfs.h>
@@ -2172,7 +2174,7 @@
IRQ_NOAUTOEN);
ret = devm_request_threaded_irq(&pdev->dev,
gpio_to_irq(udc->vbus_pin), NULL,
- usba_vbus_irq_thread, IRQF_ONESHOT,
+ usba_vbus_irq_thread, USBA_VBUS_IRQFLAGS,
"atmel_usba_udc", udc);
if (ret) {
udc->vbus_pin = -ENODEV;
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 94c8a9f..b62a3de 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -237,6 +237,8 @@
struct usb_device *udev;
struct list_head urbp_list;
+ struct urbp *next_frame_urbp;
+
u32 stream_en_ep;
u8 num_stream[30 / 2];
@@ -253,11 +255,13 @@
*/
struct dummy_ep ep[DUMMY_ENDPOINTS];
int address;
+ int callback_usage;
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct dummy_request fifo_req;
u8 fifo_buf[FIFO_SIZE];
u16 devstatus;
+ unsigned ints_enabled:1;
unsigned udc_suspended:1;
unsigned pullup:1;
@@ -416,6 +420,7 @@
static void set_link_state(struct dummy_hcd *dum_hcd)
{
struct dummy *dum = dum_hcd->dum;
+ unsigned int power_bit;
dum_hcd->active = 0;
if (dum->pullup)
@@ -426,32 +431,43 @@
return;
set_link_state_by_speed(dum_hcd);
+ power_bit = (dummy_hcd_to_hcd(dum_hcd)->speed == HCD_USB3 ?
+ USB_SS_PORT_STAT_POWER : USB_PORT_STAT_POWER);
if ((dum_hcd->port_status & USB_PORT_STAT_ENABLE) == 0 ||
dum_hcd->active)
dum_hcd->resuming = 0;
/* Currently !connected or in reset */
- if ((dum_hcd->port_status & USB_PORT_STAT_CONNECTION) == 0 ||
+ if ((dum_hcd->port_status & power_bit) == 0 ||
(dum_hcd->port_status & USB_PORT_STAT_RESET) != 0) {
- unsigned disconnect = USB_PORT_STAT_CONNECTION &
+ unsigned int disconnect = power_bit &
dum_hcd->old_status & (~dum_hcd->port_status);
- unsigned reset = USB_PORT_STAT_RESET &
+ unsigned int reset = USB_PORT_STAT_RESET &
(~dum_hcd->old_status) & dum_hcd->port_status;
/* Report reset and disconnect events to the driver */
- if (dum->driver && (disconnect || reset)) {
+ if (dum->ints_enabled && (disconnect || reset)) {
stop_activity(dum);
+ ++dum->callback_usage;
+ spin_unlock(&dum->lock);
if (reset)
usb_gadget_udc_reset(&dum->gadget, dum->driver);
else
dum->driver->disconnect(&dum->gadget);
+ spin_lock(&dum->lock);
+ --dum->callback_usage;
}
- } else if (dum_hcd->active != dum_hcd->old_active) {
+ } else if (dum_hcd->active != dum_hcd->old_active &&
+ dum->ints_enabled) {
+ ++dum->callback_usage;
+ spin_unlock(&dum->lock);
if (dum_hcd->old_active && dum->driver->suspend)
dum->driver->suspend(&dum->gadget);
else if (!dum_hcd->old_active && dum->driver->resume)
dum->driver->resume(&dum->gadget);
+ spin_lock(&dum->lock);
+ --dum->callback_usage;
}
dum_hcd->old_status = dum_hcd->port_status;
@@ -965,8 +981,11 @@
* can't enumerate without help from the driver we're binding.
*/
+ spin_lock_irq(&dum->lock);
dum->devstatus = 0;
dum->driver = driver;
+ dum->ints_enabled = 1;
+ spin_unlock_irq(&dum->lock);
return 0;
}
@@ -977,6 +996,16 @@
struct dummy *dum = dum_hcd->dum;
spin_lock_irq(&dum->lock);
+ dum->ints_enabled = 0;
+ stop_activity(dum);
+
+ /* emulate synchronize_irq(): wait for callbacks to finish */
+ while (dum->callback_usage > 0) {
+ spin_unlock_irq(&dum->lock);
+ usleep_range(1000, 2000);
+ spin_lock_irq(&dum->lock);
+ }
+
dum->driver = NULL;
spin_unlock_irq(&dum->lock);
@@ -1030,7 +1059,12 @@
memzero_explicit(&dum->gadget, sizeof(struct usb_gadget));
dum->gadget.name = gadget_name;
dum->gadget.ops = &dummy_ops;
- dum->gadget.max_speed = USB_SPEED_SUPER;
+ if (mod_data.is_super_speed)
+ dum->gadget.max_speed = USB_SPEED_SUPER;
+ else if (mod_data.is_high_speed)
+ dum->gadget.max_speed = USB_SPEED_HIGH;
+ else
+ dum->gadget.max_speed = USB_SPEED_FULL;
dum->gadget.dev.parent = &pdev->dev;
init_dummy_udc_hw(dum);
@@ -1239,6 +1273,8 @@
list_add_tail(&urbp->urbp_list, &dum_hcd->urbp_list);
urb->hcpriv = urbp;
+ if (!dum_hcd->next_frame_urbp)
+ dum_hcd->next_frame_urbp = urbp;
if (usb_pipetype(urb->pipe) == PIPE_CONTROL)
urb->error_count = 1; /* mark as a new urb */
@@ -1515,6 +1551,8 @@
if (!is_active((dum->gadget.speed == USB_SPEED_SUPER ?
dum->ss_hcd : dum->hs_hcd)))
return NULL;
+ if (!dum->ints_enabled)
+ return NULL;
if ((address & ~USB_DIR_IN) == 0)
return &dum->ep[0];
for (i = 1; i < DUMMY_ENDPOINTS; i++) {
@@ -1756,6 +1794,7 @@
spin_unlock_irqrestore(&dum->lock, flags);
return;
}
+ dum_hcd->next_frame_urbp = NULL;
for (i = 0; i < DUMMY_ENDPOINTS; i++) {
if (!ep_info[i].name)
@@ -1772,6 +1811,10 @@
int type;
int status = -EINPROGRESS;
+ /* stop when we reach URBs queued after the timer interrupt */
+ if (urbp == dum_hcd->next_frame_urbp)
+ break;
+
urb = urbp->urb;
if (urb->unlinked)
goto return_urb;
@@ -1851,10 +1894,12 @@
* until setup() returns; no reentrancy issues etc.
*/
if (value > 0) {
+ ++dum->callback_usage;
spin_unlock(&dum->lock);
value = dum->driver->setup(&dum->gadget,
&setup);
spin_lock(&dum->lock);
+ --dum->callback_usage;
if (value >= 0) {
/* no delays (max 64KB data stage) */
@@ -2559,8 +2604,6 @@
.product_desc = "Dummy host controller",
.hcd_priv_size = sizeof(struct dummy_hcd),
- .flags = HCD_USB3 | HCD_SHARED,
-
.reset = dummy_setup,
.start = dummy_start,
.stop = dummy_stop,
@@ -2589,8 +2632,12 @@
dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);
dum = *((void **)dev_get_platdata(&pdev->dev));
- if (!mod_data.is_super_speed)
+ if (mod_data.is_super_speed)
+ dummy_hcd.flags = HCD_USB3 | HCD_SHARED;
+ else if (mod_data.is_high_speed)
dummy_hcd.flags = HCD_USB2;
+ else
+ dummy_hcd.flags = HCD_USB11;
hs_hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev));
if (!hs_hcd)
return -ENOMEM;
diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c
index d2cfefa..bb89e24 100644
--- a/drivers/usb/gadget/udc/renesas_usb3.c
+++ b/drivers/usb/gadget/udc/renesas_usb3.c
@@ -879,7 +879,7 @@
usb3_ep->ep.maxpacket);
u8 *buf = usb3_req->req.buf + usb3_req->req.actual;
u32 tmp = 0;
- bool is_last;
+ bool is_last = !len ? true : false;
if (usb3_wait_pipe_status(usb3_ep, PX_STA_BUFSTS) < 0)
return -EBUSY;
@@ -900,7 +900,8 @@
usb3_write(usb3, tmp, fifo_reg);
}
- is_last = usb3_is_transfer_complete(usb3_ep, usb3_req);
+ if (!is_last)
+ is_last = usb3_is_transfer_complete(usb3_ep, usb3_req);
/* Send the data */
usb3_set_px_con_send(usb3_ep, len, is_last);
@@ -991,7 +992,8 @@
usb3_set_p0_con_for_ctrl_read_data(usb3);
} else {
usb3_clear_bit(usb3, P0_MOD_DIR, USB3_P0_MOD);
- usb3_set_p0_con_for_ctrl_write_data(usb3);
+ if (usb3_req->req.length)
+ usb3_set_p0_con_for_ctrl_write_data(usb3);
}
usb3_p0_xfer(usb3_ep, usb3_req);
@@ -1568,7 +1570,16 @@
static u32 usb3_calc_rammap_val(struct renesas_usb3_ep *usb3_ep,
const struct usb_endpoint_descriptor *desc)
{
- return usb3_ep->rammap_val | PN_RAMMAP_MPKT(usb_endpoint_maxp(desc));
+ int i;
+ const u32 max_packet_array[] = {8, 16, 32, 64, 512};
+ u32 mpkt = PN_RAMMAP_MPKT(1024);
+
+ for (i = 0; i < ARRAY_SIZE(max_packet_array); i++) {
+ if (usb_endpoint_maxp(desc) <= max_packet_array[i])
+ mpkt = PN_RAMMAP_MPKT(max_packet_array[i]);
+ }
+
+ return usb3_ep->rammap_val | mpkt;
}
static int usb3_enable_pipe_n(struct renesas_usb3_ep *usb3_ep,
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 0b80cee3..eb121b2 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -45,9 +45,9 @@
If unsure, say N.
config USB_XHCI_MTK
- tristate "xHCI support for Mediatek MT65xx"
+ tristate "xHCI support for Mediatek MT65xx/MT7621"
select MFD_SYSCON
- depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on (MIPS && SOC_MT7621) || ARCH_MEDIATEK || COMPILE_TEST
---help---
Say 'Y' to enable the support for the xHCI host controller
found in Mediatek MT65xx SoCs.
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index 58b9685..ee213c5 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -447,7 +447,7 @@
if ((value & ASMT_CONTROL_WRITE_BIT) == 0)
return 0;
- usleep_range(40, 60);
+ udelay(50);
}
dev_warn(&pdev->dev, "%s: check_write_ready timeout", __func__);
@@ -1022,7 +1022,7 @@
*
* Takes care of the handoff between the Pre-OS (i.e. BIOS) and the OS.
* It signals to the BIOS that the OS wants control of the host controller,
- * and then waits 5 seconds for the BIOS to hand over control.
+ * and then waits 1 second for the BIOS to hand over control.
* If we timeout, assume the BIOS is broken and take control anyway.
*/
static void quirk_usb_handoff_xhci(struct pci_dev *pdev)
@@ -1069,9 +1069,9 @@
if (val & XHCI_HC_BIOS_OWNED) {
writel(val | XHCI_HC_OS_OWNED, base + ext_cap_offset);
- /* Wait for 5 seconds with 10 microsecond polling interval */
+ /* Wait for 1 second with 10 microsecond polling interval */
timeout = handshake(base + ext_cap_offset, XHCI_HC_BIOS_OWNED,
- 0, 5000, 10);
+ 0, 1000000, 10);
/* Assume a buggy BIOS and take HC ownership anyway */
if (timeout) {
@@ -1100,7 +1100,7 @@
* operational or runtime registers. Wait 5 seconds and no more.
*/
timeout = handshake(op_reg_base + XHCI_STS_OFFSET, XHCI_STS_CNR, 0,
- 5000, 10);
+ 5000000, 10);
/* Assume a buggy HC and start HC initialization anyway */
if (timeout) {
val = readl(op_reg_base + XHCI_STS_OFFSET);
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 93aa6a0..667e596 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -112,7 +112,7 @@
/* If PSI table exists, add the custom speed attributes from it */
if (usb3_1 && xhci->usb3_rhub.psi_count) {
- u32 ssp_cap_base, bm_attrib, psi;
+ u32 ssp_cap_base, bm_attrib, psi, psi_mant, psi_exp;
int offset;
ssp_cap_base = USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE;
@@ -139,6 +139,15 @@
for (i = 0; i < xhci->usb3_rhub.psi_count; i++) {
psi = xhci->usb3_rhub.psi[i];
psi &= ~USB_SSP_SUBLINK_SPEED_RSVD;
+ psi_exp = XHCI_EXT_PORT_PSIE(psi);
+ psi_mant = XHCI_EXT_PORT_PSIM(psi);
+
+ /* Shift to Gbps and set SSP Link BIT(14) if 10Gpbs */
+ for (; psi_exp < 3; psi_exp++)
+ psi_mant /= 1000;
+ if (psi_mant >= 10)
+ psi |= BIT(14);
+
if ((psi & PLT_MASK) == PLT_SYM) {
/* Symmetric, create SSA RX and TX from one PSI entry */
put_unaligned_le32(psi, &buf[offset]);
@@ -1571,9 +1580,6 @@
t2 |= PORT_WKOC_E | PORT_WKCONN_E;
t2 &= ~PORT_WKDISC_E;
}
- if ((xhci->quirks & XHCI_U2_DISABLE_WAKE) &&
- (hcd->speed < HCD_USB3))
- t2 &= ~PORT_WAKE_BITS;
} else
t2 &= ~PORT_WAKE_BITS;
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 2383344..c87ef38 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -54,11 +54,6 @@
#define PCI_DEVICE_ID_INTEL_APL_XHCI 0x5aa8
#define PCI_DEVICE_ID_INTEL_DNV_XHCI 0x19d0
-#define PCI_DEVICE_ID_AMD_PROMONTORYA_4 0x43b9
-#define PCI_DEVICE_ID_AMD_PROMONTORYA_3 0x43ba
-#define PCI_DEVICE_ID_AMD_PROMONTORYA_2 0x43bb
-#define PCI_DEVICE_ID_AMD_PROMONTORYA_1 0x43bc
-
#define PCI_DEVICE_ID_ASMEDIA_1042A_XHCI 0x1142
static const char hcd_name[] = "xhci_hcd";
@@ -142,13 +137,6 @@
if (pdev->vendor == PCI_VENDOR_ID_AMD)
xhci->quirks |= XHCI_TRUST_TX_LENGTH;
- if ((pdev->vendor == PCI_VENDOR_ID_AMD) &&
- ((pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_4) ||
- (pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_3) ||
- (pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_2) ||
- (pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_1)))
- xhci->quirks |= XHCI_U2_DISABLE_WAKE;
-
if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
xhci->quirks |= XHCI_LPM_SUPPORT;
xhci->quirks |= XHCI_INTEL_HOST;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index db46db4..c11eab1 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1509,7 +1509,7 @@
static inline unsigned int hcd_index(struct usb_hcd *hcd)
{
- if (hcd->speed == HCD_USB3)
+ if (hcd->speed >= HCD_USB3)
return 0;
else
return 1;
@@ -1670,7 +1670,7 @@
/* For controller with a broken Port Disable implementation */
#define XHCI_BROKEN_PORT_PED (1 << 25)
#define XHCI_LIMIT_ENDPOINT_INTERVAL_7 (1 << 26)
-#define XHCI_U2_DISABLE_WAKE (1 << 27)
+/* Reserved. It was XHCI_U2_DISABLE_WAKE */
#define XHCI_ASMEDIA_MODIFY_FLOWCONTROL (1 << 28)
unsigned int num_active_eps;
diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c
index 1f80cde..6d97dec 100644
--- a/drivers/usb/phy/phy-msm-qusb-v2.c
+++ b/drivers/usb/phy/phy-msm-qusb-v2.c
@@ -63,17 +63,6 @@
#define LINESTATE_DP BIT(0)
#define LINESTATE_DM BIT(1)
-/* eud related registers */
-#define EUD_SW_ATTACH_DET 0x1018
-#define EUD_INT1_EN_MASK 0x0024
-
-/* EUD interrupt mask bits */
-#define EUD_INT_RX BIT(0)
-#define EUD_INT_TX BIT(1)
-#define EUD_INT_VBUS BIT(2)
-#define EUD_INT_CHGR BIT(3)
-#define EUD_INT_SAFE_MODE BIT(4)
-
unsigned int phy_tune1;
module_param(phy_tune1, uint, 0644);
MODULE_PARM_DESC(phy_tune1, "QUSB PHY v2 TUNE1");
@@ -92,7 +81,6 @@
struct usb_phy phy;
struct mutex lock;
void __iomem *base;
- void __iomem *eud_base;
void __iomem *efuse_reg;
struct clk *ref_clk_src;
@@ -667,22 +655,6 @@
return ret;
}
qphy->dpdm_enable = true;
-
- if (qphy->eud_base) {
- if (qphy->cfg_ahb_clk)
- clk_prepare_enable(qphy->cfg_ahb_clk);
- writel_relaxed(BIT(0),
- qphy->eud_base + EUD_SW_ATTACH_DET);
- /* to flush above write before next write */
- wmb();
-
- writel_relaxed(EUD_INT_VBUS | EUD_INT_CHGR,
- qphy->eud_base + EUD_INT1_EN_MASK);
- /* to flush above write before turning off clk */
- wmb();
- if (qphy->cfg_ahb_clk)
- clk_disable_unprepare(qphy->cfg_ahb_clk);
- }
}
return ret;
@@ -697,16 +669,6 @@
__func__, qphy->dpdm_enable);
if (qphy->dpdm_enable) {
- if (qphy->eud_base) {
- if (qphy->cfg_ahb_clk)
- clk_prepare_enable(qphy->cfg_ahb_clk);
- writel_relaxed(0, qphy->eud_base + EUD_SW_ATTACH_DET);
- /* to flush above write before turning off clk */
- wmb();
- if (qphy->cfg_ahb_clk)
- clk_disable_unprepare(qphy->cfg_ahb_clk);
- }
-
ret = qusb_phy_enable_power(qphy, false);
if (ret < 0) {
dev_dbg(qphy->phy.dev,
@@ -813,17 +775,6 @@
}
}
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "eud_base");
- if (res) {
- qphy->eud_base = devm_ioremap(dev, res->start,
- resource_size(res));
- if (IS_ERR(qphy->eud_base)) {
- dev_dbg(dev, "couldn't ioremap eud_base\n");
- qphy->eud_base = NULL;
- }
- }
-
/* ref_clk_src is needed irrespective of SE_CLK or DIFF_CLK usage */
qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
if (IS_ERR(qphy->ref_clk_src)) {
@@ -1040,21 +991,6 @@
if (ret)
return ret;
- /* ldo24 is turned on and eud is pet irrespective of cable
- * cable connection status by boot sw. Assume usb cable is not
- * connected and perform detach pet. If usb cable is connected,
- * eud hw will be pet in the dpdm callback.
- */
- if (qphy->eud_base) {
- if (qphy->cfg_ahb_clk)
- clk_prepare_enable(qphy->cfg_ahb_clk);
-
- writel_relaxed(0, qphy->eud_base + EUD_SW_ATTACH_DET);
-
- if (qphy->cfg_ahb_clk)
- clk_disable_unprepare(qphy->cfg_ahb_clk);
- }
-
ret = qusb_phy_regulator_init(qphy);
if (ret)
usb_remove_phy(&qphy->phy);
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index 857e783..6c6a3a8 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -285,11 +285,26 @@
struct usbhs_fifo *fifo)
{
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ int ret = 0;
- if (!usbhs_pipe_is_dcp(pipe))
- usbhsf_fifo_barrier(priv, fifo);
+ if (!usbhs_pipe_is_dcp(pipe)) {
+ /*
+ * This driver checks the pipe condition first to avoid -EBUSY
+ * from usbhsf_fifo_barrier() with about 10 msec delay in
+ * the interrupt handler if the pipe is RX direction and empty.
+ */
+ if (usbhs_pipe_is_dir_in(pipe))
+ ret = usbhs_pipe_is_accessible(pipe);
+ if (!ret)
+ ret = usbhsf_fifo_barrier(priv, fifo);
+ }
- usbhs_write(priv, fifo->ctr, BCLR);
+ /*
+ * if non-DCP pipe, this driver should set BCLR when
+ * usbhsf_fifo_barrier() returns 0.
+ */
+ if (!ret)
+ usbhs_write(priv, fifo->ctr, BCLR);
}
static int usbhsf_fifo_rcv_len(struct usbhs_priv *priv,
@@ -845,9 +860,9 @@
fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
usbhs_pipe_running(pipe, 1);
- usbhsf_dma_start(pipe, fifo);
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
dma_async_issue_pending(chan);
+ usbhsf_dma_start(pipe, fifo);
usbhs_pipe_enable(pipe);
xfer_work_end:
diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c
index b6f1ade..76062ce 100644
--- a/drivers/usb/serial/console.c
+++ b/drivers/usb/serial/console.c
@@ -186,6 +186,7 @@
tty_kref_put(tty);
reset_open_count:
port->port.count = 0;
+ info->port = NULL;
usb_autopm_put_interface(serial->interface);
error_get_interface:
usb_serial_put(serial);
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 470b17b..11ee55e 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -171,6 +171,7 @@
{ USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */
{ USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */
{ USB_DEVICE(0x18EF, 0xE025) }, /* ELV Marble Sound Board 1 */
+ { USB_DEVICE(0x18EF, 0xE032) }, /* ELV TFD500 Data Logger */
{ USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */
{ USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */
{ USB_DEVICE(0x1901, 0x0194) }, /* GE Healthcare Remote Alarm Box */
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 1939496..3249f42 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1015,6 +1015,8 @@
{ USB_DEVICE(WICED_VID, WICED_USB20706V2_PID) },
{ USB_DEVICE(TI_VID, TI_CC3200_LAUNCHPAD_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_BT_USB_PID) },
+ { USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_WL_USB_PID) },
{ } /* Terminating entry */
};
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 4fcf1ce..f9d15bd 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -610,6 +610,13 @@
#define ADI_GNICEPLUS_PID 0xF001
/*
+ * Cypress WICED USB UART
+ */
+#define CYPRESS_VID 0x04B4
+#define CYPRESS_WICED_BT_USB_PID 0x009B
+#define CYPRESS_WICED_WL_USB_PID 0xF900
+
+/*
* Microchip Technology, Inc.
*
* MICROCHIP_VID (0x04D8) and MICROCHIP_USB_BOARD_PID (0x000A) are
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 136ff5e..135eb04 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -234,11 +234,16 @@
status = usb_control_msg(usbdev, pipe, request, requesttype, value,
index, buf, 1, MOS_WDR_TIMEOUT);
- if (status == 1)
+ if (status == 1) {
*data = *buf;
- else if (status < 0)
+ } else {
dev_err(&usbdev->dev,
"mos7720: usb_control_msg() failed: %d\n", status);
+ if (status >= 0)
+ status = -EIO;
+ *data = 0;
+ }
+
kfree(buf);
return status;
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 5c4fc3a..6baacf6 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -285,9 +285,15 @@
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
MCS_RD_RTYPE, 0, reg, buf, VENDOR_READ_LENGTH,
MOS_WDR_TIMEOUT);
+ if (ret < VENDOR_READ_LENGTH) {
+ if (ret >= 0)
+ ret = -EIO;
+ goto out;
+ }
+
*val = buf[0];
dev_dbg(&port->dev, "%s offset is %x, return val %x\n", __func__, reg, *val);
-
+out:
kfree(buf);
return ret;
}
@@ -353,8 +359,13 @@
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
MCS_RD_RTYPE, Wval, reg, buf, VENDOR_READ_LENGTH,
MOS_WDR_TIMEOUT);
+ if (ret < VENDOR_READ_LENGTH) {
+ if (ret >= 0)
+ ret = -EIO;
+ goto out;
+ }
*val = buf[0];
-
+out:
kfree(buf);
return ret;
}
@@ -1490,10 +1501,10 @@
return -ENODEV;
status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr);
- if (status != 1)
+ if (status < 0)
return -EIO;
status = mos7840_get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr);
- if (status != 1)
+ if (status < 0)
return -EIO;
result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0)
| ((mcr & MCR_RTS) ? TIOCM_RTS : 0)
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 2a99443..db3d34c 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -522,6 +522,7 @@
/* TP-LINK Incorporated products */
#define TPLINK_VENDOR_ID 0x2357
+#define TPLINK_PRODUCT_LTE 0x000D
#define TPLINK_PRODUCT_MA180 0x0201
/* Changhong products */
@@ -2011,6 +2012,7 @@
{ USB_DEVICE(CELLIENT_VENDOR_ID, CELLIENT_PRODUCT_MEN200) },
{ USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T_600A) },
{ USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T_600E) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TPLINK_VENDOR_ID, TPLINK_PRODUCT_LTE, 0xff, 0x00, 0x00) }, /* TP-Link LTE Module */
{ USB_DEVICE(TPLINK_VENDOR_ID, TPLINK_PRODUCT_MA180),
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE(TPLINK_VENDOR_ID, 0x9000), /* TP-Link MA260 */
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 652b433..e1c1e32 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -174,6 +174,10 @@
{DEVICE_SWI(0x413c, 0x81b3)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */
{DEVICE_SWI(0x413c, 0x81b5)}, /* Dell Wireless 5811e QDL */
{DEVICE_SWI(0x413c, 0x81b6)}, /* Dell Wireless 5811e QDL */
+ {DEVICE_SWI(0x413c, 0x81cf)}, /* Dell Wireless 5819 */
+ {DEVICE_SWI(0x413c, 0x81d0)}, /* Dell Wireless 5819 */
+ {DEVICE_SWI(0x413c, 0x81d1)}, /* Dell Wireless 5818 */
+ {DEVICE_SWI(0x413c, 0x81d2)}, /* Dell Wireless 5818 */
/* Huawei devices */
{DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index 1a59f33..a3ccb89 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -834,13 +834,25 @@
if (result == USB_STOR_TRANSPORT_GOOD) {
srb->result = SAM_STAT_GOOD;
srb->sense_buffer[0] = 0x0;
+ }
+
+ /*
+ * ATA-passthru commands use sense data to report
+ * the command completion status, and often devices
+ * return Check Condition status when nothing is
+ * wrong.
+ */
+ else if (srb->cmnd[0] == ATA_16 ||
+ srb->cmnd[0] == ATA_12) {
+ /* leave the data alone */
+ }
/*
* If there was a problem, report an unspecified
* hardware error to prevent the higher layers from
* entering an infinite retry loop.
*/
- } else {
+ else {
srb->result = DID_ERROR << 16;
if ((sshdr.response_code & 0x72) == 0x72)
srb->sense_buffer[1] = HARDWARE_ERROR;
diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h
index f58caa9..a155cd0 100644
--- a/drivers/usb/storage/uas-detect.h
+++ b/drivers/usb/storage/uas-detect.h
@@ -9,7 +9,8 @@
intf->desc.bInterfaceProtocol == USB_PR_UAS);
}
-static int uas_find_uas_alt_setting(struct usb_interface *intf)
+static struct usb_host_interface *uas_find_uas_alt_setting(
+ struct usb_interface *intf)
{
int i;
@@ -17,10 +18,10 @@
struct usb_host_interface *alt = &intf->altsetting[i];
if (uas_is_interface(alt))
- return alt->desc.bAlternateSetting;
+ return alt;
}
- return -ENODEV;
+ return NULL;
}
static int uas_find_endpoints(struct usb_host_interface *alt,
@@ -58,14 +59,14 @@
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
unsigned long flags = id->driver_info;
- int r, alt;
-
+ struct usb_host_interface *alt;
+ int r;
alt = uas_find_uas_alt_setting(intf);
- if (alt < 0)
+ if (!alt)
return 0;
- r = uas_find_endpoints(&intf->altsetting[alt], eps);
+ r = uas_find_endpoints(alt, eps);
if (r < 0)
return 0;
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 5ef014b..9876af4 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -873,14 +873,14 @@
static int uas_switch_interface(struct usb_device *udev,
struct usb_interface *intf)
{
- int alt;
+ struct usb_host_interface *alt;
alt = uas_find_uas_alt_setting(intf);
- if (alt < 0)
- return alt;
+ if (!alt)
+ return -ENODEV;
- return usb_set_interface(udev,
- intf->altsetting[0].desc.bInterfaceNumber, alt);
+ return usb_set_interface(udev, alt->desc.bInterfaceNumber,
+ alt->desc.bAlternateSetting);
}
static int uas_configure_endpoints(struct uas_dev_info *devinfo)
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 9129f6c..2572fd5 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -1459,6 +1459,13 @@
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_SANE_SENSE ),
+/* Reported by Kris Lindgren <kris.lindgren@gmail.com> */
+UNUSUAL_DEV( 0x0bc2, 0x3332, 0x0000, 0x9999,
+ "Seagate",
+ "External",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_WP_DETECT ),
+
UNUSUAL_DEV( 0x0d49, 0x7310, 0x0000, 0x9999,
"Maxtor",
"USB to SATA",
diff --git a/drivers/uwb/hwa-rc.c b/drivers/uwb/hwa-rc.c
index 35a1e77..9a53912 100644
--- a/drivers/uwb/hwa-rc.c
+++ b/drivers/uwb/hwa-rc.c
@@ -825,6 +825,8 @@
if (iface->cur_altsetting->desc.bNumEndpoints < 1)
return -ENODEV;
+ if (!usb_endpoint_xfer_int(&iface->cur_altsetting->endpoint[0].desc))
+ return -ENODEV;
result = -ENOMEM;
uwb_rc = uwb_rc_alloc();
diff --git a/drivers/uwb/uwbd.c b/drivers/uwb/uwbd.c
index 01c20a2..39dd4ef 100644
--- a/drivers/uwb/uwbd.c
+++ b/drivers/uwb/uwbd.c
@@ -302,18 +302,22 @@
/** Start the UWB daemon */
void uwbd_start(struct uwb_rc *rc)
{
- rc->uwbd.task = kthread_run(uwbd, rc, "uwbd");
- if (rc->uwbd.task == NULL)
+ struct task_struct *task = kthread_run(uwbd, rc, "uwbd");
+ if (IS_ERR(task)) {
+ rc->uwbd.task = NULL;
printk(KERN_ERR "UWB: Cannot start management daemon; "
"UWB won't work\n");
- else
+ } else {
+ rc->uwbd.task = task;
rc->uwbd.pid = rc->uwbd.task->pid;
+ }
}
/* Stop the UWB daemon and free any unprocessed events */
void uwbd_stop(struct uwb_rc *rc)
{
- kthread_stop(rc->uwbd.task);
+ if (rc->uwbd.task)
+ kthread_stop(rc->uwbd.task);
uwbd_flush(rc);
}
diff --git a/drivers/video/fbdev/aty/atyfb_base.c b/drivers/video/fbdev/aty/atyfb_base.c
index 11026e7..81367cf 100644
--- a/drivers/video/fbdev/aty/atyfb_base.c
+++ b/drivers/video/fbdev/aty/atyfb_base.c
@@ -1861,7 +1861,7 @@
#if defined(DEBUG) && defined(CONFIG_FB_ATY_CT)
case ATYIO_CLKR:
if (M64_HAS(INTEGRATED)) {
- struct atyclk clk;
+ struct atyclk clk = { 0 };
union aty_pll *pll = &par->pll;
u32 dsp_config = pll->ct.dsp_config;
u32 dsp_on_off = pll->ct.dsp_on_off;
diff --git a/drivers/watchdog/kempld_wdt.c b/drivers/watchdog/kempld_wdt.c
index 8e302d0..3efa295 100644
--- a/drivers/watchdog/kempld_wdt.c
+++ b/drivers/watchdog/kempld_wdt.c
@@ -140,12 +140,19 @@
unsigned int timeout)
{
struct kempld_device_data *pld = wdt_data->pld;
- u32 prescaler = kempld_prescaler[PRESCALER_21];
+ u32 prescaler;
u64 stage_timeout64;
u32 stage_timeout;
u32 remainder;
u8 stage_cfg;
+#if GCC_VERSION < 40400
+ /* work around a bug compiling do_div() */
+ prescaler = READ_ONCE(kempld_prescaler[PRESCALER_21]);
+#else
+ prescaler = kempld_prescaler[PRESCALER_21];
+#endif
+
if (!stage)
return -EINVAL;
diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c
index 679f79f..b68ced5 100644
--- a/drivers/xen/swiotlb-xen.c
+++ b/drivers/xen/swiotlb-xen.c
@@ -680,3 +680,22 @@
return 0;
}
EXPORT_SYMBOL_GPL(xen_swiotlb_set_dma_mask);
+
+/*
+ * Create userspace mapping for the DMA-coherent memory.
+ * This function should be called with the pages from the current domain only,
+ * passing pages mapped from other domains would lead to memory corruption.
+ */
+int
+xen_swiotlb_dma_mmap(struct device *dev, struct vm_area_struct *vma,
+ void *cpu_addr, dma_addr_t dma_addr, size_t size,
+ unsigned long attrs)
+{
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
+ if (__generic_dma_ops(dev)->mmap)
+ return __generic_dma_ops(dev)->mmap(dev, vma, cpu_addr,
+ dma_addr, size, attrs);
+#endif
+ return dma_common_mmap(dev, vma, cpu_addr, dma_addr, size);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_dma_mmap);
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 07e46b7..cb936c9 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -450,10 +450,12 @@
set_page_writeback(page);
result = ops->rw_page(bdev, sector + get_start_sect(bdev), page, true);
- if (result)
+ if (result) {
end_page_writeback(page);
- else
+ } else {
+ clean_page_buffers(page);
unlock_page(page);
+ }
blk_queue_exit(bdev->bd_queue);
return result;
}
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 8a05fa7..f089d7d 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -8050,8 +8050,10 @@
start += sectorsize;
- if (nr_sectors--) {
+ nr_sectors--;
+ if (nr_sectors) {
pgoff += sectorsize;
+ ASSERT(pgoff < PAGE_SIZE);
goto next_block_or_try_again;
}
}
@@ -8157,8 +8159,10 @@
ASSERT(nr_sectors);
- if (--nr_sectors) {
+ nr_sectors--;
+ if (nr_sectors) {
pgoff += sectorsize;
+ ASSERT(pgoff < PAGE_SIZE);
goto next_block;
}
}
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 1782804..0fe346c 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -3052,7 +3052,7 @@
out:
if (ret)
btrfs_cmp_data_free(cmp);
- return 0;
+ return ret;
}
static int btrfs_cmp_data(struct inode *src, u64 loff, struct inode *dst,
@@ -4082,6 +4082,10 @@
ret = PTR_ERR(new_root);
goto out;
}
+ if (!is_fstree(new_root->objectid)) {
+ ret = -ENOENT;
+ goto out;
+ }
path = btrfs_alloc_path();
if (!path) {
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 2cf5e14..04c61bc 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -2367,11 +2367,11 @@
while (!list_empty(list)) {
reloc_root = list_entry(list->next, struct btrfs_root,
root_list);
+ __del_reloc_root(reloc_root);
free_extent_buffer(reloc_root->node);
free_extent_buffer(reloc_root->commit_root);
reloc_root->node = NULL;
reloc_root->commit_root = NULL;
- __del_reloc_root(reloc_root);
}
}
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index 71261b4..77f9efc 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -1680,6 +1680,9 @@
{
int ret;
+ if (ino == BTRFS_FIRST_FREE_OBJECTID)
+ return 1;
+
ret = get_cur_inode_state(sctx, ino, gen);
if (ret < 0)
goto out;
@@ -1865,7 +1868,7 @@
* not deleted and then re-created, if it was then we have no overwrite
* and we can just unlink this entry.
*/
- if (sctx->parent_root) {
+ if (sctx->parent_root && dir != BTRFS_FIRST_FREE_OBJECTID) {
ret = get_inode_info(sctx->parent_root, dir, NULL, &gen, NULL,
NULL, NULL, NULL);
if (ret < 0 && ret != -ENOENT)
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 71a60cc..06a77e4 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -6226,7 +6226,7 @@
for (dev_nr = 0; dev_nr < total_devs; dev_nr++) {
dev = bbio->stripes[dev_nr].dev;
if (!dev || !dev->bdev ||
- (bio_op(bio) == REQ_OP_WRITE && !dev->writeable)) {
+ (bio_op(first_bio) == REQ_OP_WRITE && !dev->writeable)) {
bbio_error(bbio, first_bio, logical);
continue;
}
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 953275b..4a6df2c 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -1323,8 +1323,8 @@
ceph_dir_clear_ordered(dir);
dout("d_delete %p\n", dn);
d_delete(dn);
- } else {
- if (have_lease && d_unhashed(dn))
+ } else if (have_lease) {
+ if (d_unhashed(dn))
d_add(dn, NULL);
update_dentry_lease(dn, rinfo->dlease,
session,
diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c
index 7d752d5..4c9c72f 100644
--- a/fs/ceph/ioctl.c
+++ b/fs/ceph/ioctl.c
@@ -25,7 +25,7 @@
l.stripe_count = ci->i_layout.stripe_count;
l.object_size = ci->i_layout.object_size;
l.data_pool = ci->i_layout.pool_id;
- l.preferred_osd = (s32)-1;
+ l.preferred_osd = -1;
if (copy_to_user(arg, &l, sizeof(l)))
return -EFAULT;
}
@@ -97,7 +97,7 @@
nl.data_pool = ci->i_layout.pool_id;
/* this is obsolete, and always -1 */
- nl.preferred_osd = le64_to_cpu(-1);
+ nl.preferred_osd = -1;
err = __validate_layout(mdsc, &nl);
if (err)
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index e3e1a80..c0f52c4 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -1782,13 +1782,18 @@
int *pfreepath)
{
char *path;
+ struct inode *dir;
- if (ceph_snap(d_inode(dentry->d_parent)) == CEPH_NOSNAP) {
- *pino = ceph_ino(d_inode(dentry->d_parent));
+ rcu_read_lock();
+ dir = d_inode_rcu(dentry->d_parent);
+ if (dir && ceph_snap(dir) == CEPH_NOSNAP) {
+ *pino = ceph_ino(dir);
+ rcu_read_unlock();
*ppath = dentry->d_name.name;
*ppathlen = dentry->d_name.len;
return 0;
}
+ rcu_read_unlock();
path = ceph_mdsc_build_path(dentry, ppathlen, pino, 1);
if (IS_ERR(path))
return PTR_ERR(path);
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index c0c2530..87658f6 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -1360,7 +1360,7 @@
exit_cifs_idmap();
#endif
#ifdef CONFIG_CIFS_UPCALL
- unregister_key_type(&cifs_spnego_key_type);
+ exit_cifs_spnego();
#endif
cifs_destroy_request_bufs();
cifs_destroy_mids();
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 48ef401..7b496a4 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -365,6 +365,8 @@
unsigned int (*calc_smb_size)(void *);
/* check for STATUS_PENDING and process it in a positive case */
bool (*is_status_pending)(char *, struct TCP_Server_Info *, int);
+ /* check for STATUS_NETWORK_SESSION_EXPIRED */
+ bool (*is_session_expired)(char *);
/* send oplock break response */
int (*oplock_response)(struct cifs_tcon *, struct cifs_fid *,
struct cifsInodeInfo *);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 1f91c9d..cc420d6 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -1457,6 +1457,13 @@
return length;
server->total_read += length;
+ if (server->ops->is_session_expired &&
+ server->ops->is_session_expired(buf)) {
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ return -1;
+ }
+
if (server->ops->is_status_pending &&
server->ops->is_status_pending(buf, server, 0)) {
discard_remaining_data(server);
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 1a54569..580b3a4 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -796,6 +796,13 @@
cifs_dump_mem("Bad SMB: ", buf,
min_t(unsigned int, server->total_read, 48));
+ if (server->ops->is_session_expired &&
+ server->ops->is_session_expired(buf)) {
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ return -1;
+ }
+
if (server->ops->is_status_pending &&
server->ops->is_status_pending(buf, server, length))
return -1;
@@ -4071,6 +4078,14 @@
cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n",
server->sec_mode, server->capabilities, server->timeAdj);
+ if (ses->auth_key.response) {
+ cifs_dbg(VFS, "Free previous auth_key.response = %p\n",
+ ses->auth_key.response);
+ kfree(ses->auth_key.response);
+ ses->auth_key.response = NULL;
+ ses->auth_key.len = 0;
+ }
+
if (server->ops->sess_setup)
rc = server->ops->sess_setup(xid, ses, nls_info);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 3925758..cf192f9 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -224,6 +224,13 @@
if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT;
+ /* O_SYNC also has bit for O_DSYNC so following check picks up either */
+ if (f_flags & O_SYNC)
+ create_options |= CREATE_WRITE_THROUGH;
+
+ if (f_flags & O_DIRECT)
+ create_options |= CREATE_NO_BUFFER;
+
oparms.tcon = tcon;
oparms.cifs_sb = cifs_sb;
oparms.desired_access = desired_access;
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index b696824..812e488 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -1018,6 +1018,18 @@
return true;
}
+static bool
+smb2_is_session_expired(char *buf)
+{
+ struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
+
+ if (hdr->Status != STATUS_NETWORK_SESSION_EXPIRED)
+ return false;
+
+ cifs_dbg(FYI, "Session expired\n");
+ return true;
+}
+
static int
smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
struct cifsInodeInfo *cinode)
@@ -1609,6 +1621,7 @@
.close_dir = smb2_close_dir,
.calc_smb_size = smb2_calc_size,
.is_status_pending = smb2_is_status_pending,
+ .is_session_expired = smb2_is_session_expired,
.oplock_response = smb2_oplock_response,
.queryfs = smb2_queryfs,
.mand_lock = smb2_mand_lock,
@@ -1690,6 +1703,7 @@
.close_dir = smb2_close_dir,
.calc_smb_size = smb2_calc_size,
.is_status_pending = smb2_is_status_pending,
+ .is_session_expired = smb2_is_session_expired,
.oplock_response = smb2_oplock_response,
.queryfs = smb2_queryfs,
.mand_lock = smb2_mand_lock,
@@ -1773,6 +1787,7 @@
.close_dir = smb2_close_dir,
.calc_smb_size = smb2_calc_size,
.is_status_pending = smb2_is_status_pending,
+ .is_session_expired = smb2_is_session_expired,
.oplock_response = smb2_oplock_response,
.queryfs = smb2_queryfs,
.mand_lock = smb2_mand_lock,
@@ -1862,6 +1877,7 @@
.close_dir = smb2_close_dir,
.calc_smb_size = smb2_calc_size,
.is_status_pending = smb2_is_status_pending,
+ .is_session_expired = smb2_is_session_expired,
.oplock_response = smb2_oplock_response,
.queryfs = smb2_queryfs,
.mand_lock = smb2_mand_lock,
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 0437e5f..69b610ad 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -366,7 +366,7 @@
build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt);
req->NegotiateContextOffset = cpu_to_le32(OFFSET_OF_NEG_CONTEXT);
req->NegotiateContextCount = cpu_to_le16(2);
- inc_rfc1001_len(req, 4 + sizeof(struct smb2_preauth_neg_context) + 2
+ inc_rfc1001_len(req, 4 + sizeof(struct smb2_preauth_neg_context)
+ sizeof(struct smb2_encryption_neg_context)); /* calculate hash */
}
#else
@@ -531,15 +531,22 @@
/*
* validation ioctl must be signed, so no point sending this if we
- * can not sign it. We could eventually change this to selectively
+ * can not sign it (ie are not known user). Even if signing is not
+ * required (enabled but not negotiated), in those cases we selectively
* sign just this, the first and only signed request on a connection.
- * This is good enough for now since a user who wants better security
- * would also enable signing on the mount. Having validation of
- * negotiate info for signed connections helps reduce attack vectors
+ * Having validation of negotiate info helps reduce attack vectors.
*/
- if (tcon->ses->server->sign == false)
+ if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST)
return 0; /* validation requires signing */
+ if (tcon->ses->user_name == NULL) {
+ cifs_dbg(FYI, "Can't validate negotiate: null user mount\n");
+ return 0; /* validation requires signing */
+ }
+
+ if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL)
+ cifs_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n");
+
vneg_inbuf.Capabilities =
cpu_to_le32(tcon->ses->server->vals->req_capabilities);
memcpy(vneg_inbuf.Guid, tcon->ses->server->client_guid,
@@ -1010,6 +1017,8 @@
while (sess_data->func)
sess_data->func(sess_data);
+ if ((ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) && (ses->sign))
+ cifs_dbg(VFS, "signing requested but authenticated as guest\n");
rc = sess_data->result;
out:
kfree(sess_data);
diff --git a/fs/direct-io.c b/fs/direct-io.c
index c60756e..c6220a2 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -835,7 +835,8 @@
*/
if (sdio->boundary) {
ret = dio_send_cur_page(dio, sdio, map_bh);
- dio_bio_submit(dio, sdio);
+ if (sdio->bio)
+ dio_bio_submit(dio, sdio);
put_page(sdio->cur_page);
sdio->cur_page = NULL;
}
diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c
index dfa5199..dfd01ca 100644
--- a/fs/ext4/acl.c
+++ b/fs/ext4/acl.c
@@ -192,13 +192,6 @@
switch (type) {
case ACL_TYPE_ACCESS:
name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
- if (acl) {
- error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
- if (error)
- return error;
- inode->i_ctime = ext4_current_time(inode);
- ext4_mark_inode_dirty(handle, inode);
- }
break;
case ACL_TYPE_DEFAULT:
@@ -231,6 +224,8 @@
{
handle_t *handle;
int error, retries = 0;
+ umode_t mode = inode->i_mode;
+ int update_mode = 0;
retry:
handle = ext4_journal_start(inode, EXT4_HT_XATTR,
@@ -238,7 +233,20 @@
if (IS_ERR(handle))
return PTR_ERR(handle);
+ if ((type == ACL_TYPE_ACCESS) && acl) {
+ error = posix_acl_update_mode(inode, &mode, &acl);
+ if (error)
+ goto out_stop;
+ update_mode = 1;
+ }
+
error = __ext4_set_acl(handle, inode, type, acl);
+ if (!error && update_mode) {
+ inode->i_mode = mode;
+ inode->i_ctime = ext4_current_time(inode);
+ ext4_mark_inode_dirty(handle, inode);
+ }
+out_stop:
ext4_journal_stop(handle);
if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
goto retry;
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index d17d12e..510e664 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -527,7 +527,7 @@
inode_lock(inode);
isize = i_size_read(inode);
- if (offset >= isize) {
+ if (offset < 0 || offset >= isize) {
inode_unlock(inode);
return -ENXIO;
}
@@ -590,7 +590,7 @@
inode_lock(inode);
isize = i_size_read(inode);
- if (offset >= isize) {
+ if (offset < 0 || offset >= isize) {
inode_unlock(inode);
return -ENXIO;
}
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index de47a29..496c9b5 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -2120,15 +2120,29 @@
static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page)
{
int len;
- loff_t size = i_size_read(mpd->inode);
+ loff_t size;
int err;
BUG_ON(page->index != mpd->first_page);
+ clear_page_dirty_for_io(page);
+ /*
+ * We have to be very careful here! Nothing protects writeback path
+ * against i_size changes and the page can be writeably mapped into
+ * page tables. So an application can be growing i_size and writing
+ * data through mmap while writeback runs. clear_page_dirty_for_io()
+ * write-protects our page in page tables and the page cannot get
+ * written to again until we release page lock. So only after
+ * clear_page_dirty_for_io() we are safe to sample i_size for
+ * ext4_bio_write_page() to zero-out tail of the written page. We rely
+ * on the barrier provided by TestClearPageDirty in
+ * clear_page_dirty_for_io() to make sure i_size is really sampled only
+ * after page tables are updated.
+ */
+ size = i_size_read(mpd->inode);
if (page->index == size >> PAGE_SHIFT)
len = size & ~PAGE_MASK;
else
len = PAGE_SIZE;
- clear_page_dirty_for_io(page);
err = ext4_bio_write_page(&mpd->io_submit, page, len, mpd->wbc, false);
if (!err)
mpd->wbc->nr_to_write--;
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 423a21c..00b8a5a 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3527,6 +3527,12 @@
EXT4_I(old_dentry->d_inode)->i_projid)))
return -EXDEV;
+ if ((ext4_encrypted_inode(old_dir) &&
+ !fscrypt_has_encryption_key(old_dir)) ||
+ (ext4_encrypted_inode(new_dir) &&
+ !fscrypt_has_encryption_key(new_dir)))
+ return -ENOKEY;
+
retval = dquot_initialize(old.dir);
if (retval)
return retval;
@@ -3726,6 +3732,12 @@
u8 new_file_type;
int retval;
+ if ((ext4_encrypted_inode(old_dir) &&
+ !fscrypt_has_encryption_key(old_dir)) ||
+ (ext4_encrypted_inode(new_dir) &&
+ !fscrypt_has_encryption_key(new_dir)))
+ return -ENOKEY;
+
if ((ext4_encrypted_inode(old_dir) ||
ext4_encrypted_inode(new_dir)) &&
(old_dir != new_dir) &&
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 5fa9ba1..f72535e 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -2334,6 +2334,7 @@
unsigned int s_flags = sb->s_flags;
int nr_orphans = 0, nr_truncates = 0;
#ifdef CONFIG_QUOTA
+ int quota_update = 0;
int i;
#endif
if (!es->s_last_orphan) {
@@ -2372,14 +2373,32 @@
#ifdef CONFIG_QUOTA
/* Needed for iput() to work correctly and not trash data */
sb->s_flags |= MS_ACTIVE;
- /* Turn on quotas so that they are updated correctly */
+
+ /*
+ * Turn on quotas which were not enabled for read-only mounts if
+ * filesystem has quota feature, so that they are updated correctly.
+ */
+ if (ext4_has_feature_quota(sb) && (s_flags & MS_RDONLY)) {
+ int ret = ext4_enable_quotas(sb);
+
+ if (!ret)
+ quota_update = 1;
+ else
+ ext4_msg(sb, KERN_ERR,
+ "Cannot turn on quotas: error %d", ret);
+ }
+
+ /* Turn on journaled quotas used for old sytle */
for (i = 0; i < EXT4_MAXQUOTAS; i++) {
if (EXT4_SB(sb)->s_qf_names[i]) {
int ret = ext4_quota_on_mount(sb, i);
- if (ret < 0)
+
+ if (!ret)
+ quota_update = 1;
+ else
ext4_msg(sb, KERN_ERR,
"Cannot turn on journaled "
- "quota: error %d", ret);
+ "quota: type %d: error %d", i, ret);
}
}
#endif
@@ -2438,10 +2457,12 @@
ext4_msg(sb, KERN_INFO, "%d truncate%s cleaned up",
PLURAL(nr_truncates));
#ifdef CONFIG_QUOTA
- /* Turn quotas off */
- for (i = 0; i < EXT4_MAXQUOTAS; i++) {
- if (sb_dqopt(sb)->files[i])
- dquot_quota_off(sb, i);
+ /* Turn off quotas if they were enabled for orphan cleanup */
+ if (quota_update) {
+ for (i = 0; i < EXT4_MAXQUOTAS; i++) {
+ if (sb_dqopt(sb)->files[i])
+ dquot_quota_off(sb, i);
+ }
}
#endif
sb->s_flags = s_flags; /* Restore MS_RDONLY status */
@@ -5365,6 +5386,9 @@
DQUOT_USAGE_ENABLED |
(quota_mopt[type] ? DQUOT_LIMITS_ENABLED : 0));
if (err) {
+ for (type--; type >= 0; type--)
+ dquot_quota_off(sb, type);
+
ext4_warning(sb,
"Failed to enable quota tracking "
"(type=%d, err=%d). Please run "
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 2c5ae0b..08b3f62 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -1630,7 +1630,12 @@
goto fail;
}
repeat:
- page = grab_cache_page_write_begin(mapping, index, flags);
+ /*
+ * Do not use grab_cache_page_write_begin() to avoid deadlock due to
+ * wait_for_stable_page. Will wait that below with our IO control.
+ */
+ page = pagecache_get_page(mapping, index,
+ FGP_LOCK | FGP_WRITE | FGP_CREAT, GFP_NOFS);
if (!page) {
err = -ENOMEM;
goto fail;
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index 489fa0d..08d7dc9 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -663,6 +663,12 @@
bool is_old_inline = f2fs_has_inline_dentry(old_dir);
int err = -ENOENT;
+ if ((f2fs_encrypted_inode(old_dir) &&
+ !fscrypt_has_encryption_key(old_dir)) ||
+ (f2fs_encrypted_inode(new_dir) &&
+ !fscrypt_has_encryption_key(new_dir)))
+ return -ENOKEY;
+
if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) &&
!fscrypt_has_permitted_context(new_dir, old_inode)) {
err = -EPERM;
@@ -843,6 +849,12 @@
int old_nlink = 0, new_nlink = 0;
int err = -ENOENT;
+ if ((f2fs_encrypted_inode(old_dir) &&
+ !fscrypt_has_encryption_key(old_dir)) ||
+ (f2fs_encrypted_inode(new_dir) &&
+ !fscrypt_has_encryption_key(new_dir)))
+ return -ENOKEY;
+
if ((f2fs_encrypted_inode(old_dir) || f2fs_encrypted_inode(new_dir)) &&
(old_dir != new_dir) &&
(!fscrypt_has_permitted_context(new_dir, old_inode) ||
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 74a2b44..e10f616 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -1263,7 +1263,7 @@
struct curseg_info *curseg = CURSEG_I(sbi, type);
const struct victim_selection *v_ops = DIRTY_I(sbi)->v_ops;
- if (IS_NODESEG(type) || !has_not_enough_free_secs(sbi, 0, 0))
+ if (IS_NODESEG(type))
return v_ops->get_victim(sbi,
&(curseg)->next_segno, BG_GC, type, SSR);
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index 7bff6f4..7a8b1d7 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -1820,29 +1820,27 @@
static void gfs2_glock_iter_next(struct gfs2_glock_iter *gi)
{
- do {
- gi->gl = rhashtable_walk_next(&gi->hti);
+ while ((gi->gl = rhashtable_walk_next(&gi->hti))) {
if (IS_ERR(gi->gl)) {
if (PTR_ERR(gi->gl) == -EAGAIN)
continue;
gi->gl = NULL;
+ return;
}
- /* Skip entries for other sb and dead entries */
- } while ((gi->gl) && ((gi->sdp != gi->gl->gl_name.ln_sbd) ||
- __lockref_is_dead(&gi->gl->gl_lockref)));
+ /* Skip entries for other sb and dead entries */
+ if (gi->sdp == gi->gl->gl_name.ln_sbd &&
+ !__lockref_is_dead(&gi->gl->gl_lockref))
+ return;
+ }
}
static void *gfs2_glock_seq_start(struct seq_file *seq, loff_t *pos)
{
struct gfs2_glock_iter *gi = seq->private;
loff_t n = *pos;
- int ret;
- if (gi->last_pos <= *pos)
- n = (*pos - gi->last_pos);
-
- ret = rhashtable_walk_start(&gi->hti);
- if (ret)
+ rhashtable_walk_enter(&gl_hash_table, &gi->hti);
+ if (rhashtable_walk_start(&gi->hti) != 0)
return NULL;
do {
@@ -1850,6 +1848,7 @@
} while (gi->gl && n--);
gi->last_pos = *pos;
+
return gi->gl;
}
@@ -1861,6 +1860,7 @@
(*pos)++;
gi->last_pos = *pos;
gfs2_glock_iter_next(gi);
+
return gi->gl;
}
@@ -1870,6 +1870,7 @@
gi->gl = NULL;
rhashtable_walk_stop(&gi->hti);
+ rhashtable_walk_exit(&gi->hti);
}
static int gfs2_glock_seq_show(struct seq_file *seq, void *iter_ptr)
@@ -1932,12 +1933,10 @@
struct gfs2_glock_iter *gi = seq->private;
gi->sdp = inode->i_private;
- gi->last_pos = 0;
seq->buf = kmalloc(GFS2_SEQ_GOODSIZE, GFP_KERNEL | __GFP_NOWARN);
if (seq->buf)
seq->size = GFS2_SEQ_GOODSIZE;
gi->gl = NULL;
- ret = rhashtable_walk_init(&gl_hash_table, &gi->hti, GFP_KERNEL);
}
return ret;
}
@@ -1948,7 +1947,6 @@
struct gfs2_glock_iter *gi = seq->private;
gi->gl = NULL;
- rhashtable_walk_exit(&gi->hti);
return seq_release_private(inode, file);
}
@@ -1960,12 +1958,10 @@
struct seq_file *seq = file->private_data;
struct gfs2_glock_iter *gi = seq->private;
gi->sdp = inode->i_private;
- gi->last_pos = 0;
seq->buf = kmalloc(GFS2_SEQ_GOODSIZE, GFP_KERNEL | __GFP_NOWARN);
if (seq->buf)
seq->size = GFS2_SEQ_GOODSIZE;
gi->gl = NULL;
- ret = rhashtable_walk_init(&gl_hash_table, &gi->hti, GFP_KERNEL);
}
return ret;
}
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 704fa0b..2c2f182 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -695,14 +695,11 @@
inode = new_inode(sb);
if (inode) {
- struct hugetlbfs_inode_info *info;
inode->i_ino = get_next_ino();
inode->i_mode = S_IFDIR | config->mode;
inode->i_uid = config->uid;
inode->i_gid = config->gid;
inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
- info = HUGETLBFS_I(inode);
- mpol_shared_policy_init(&info->policy, NULL);
inode->i_op = &hugetlbfs_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
/* directory inodes start off with i_nlink == 2 (for "." entry) */
@@ -733,7 +730,6 @@
inode = new_inode(sb);
if (inode) {
- struct hugetlbfs_inode_info *info;
inode->i_ino = get_next_ino();
inode_init_owner(inode, dir, mode);
lockdep_set_class(&inode->i_mapping->i_mmap_rwsem,
@@ -741,15 +737,6 @@
inode->i_mapping->a_ops = &hugetlbfs_aops;
inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
inode->i_mapping->private_data = resv_map;
- info = HUGETLBFS_I(inode);
- /*
- * The policy is initialized here even if we are creating a
- * private inode because initialization simply creates an
- * an empty rb tree and calls rwlock_init(), later when we
- * call mpol_free_shared_policy() it will just return because
- * the rb tree will still be empty.
- */
- mpol_shared_policy_init(&info->policy, NULL);
switch (mode & S_IFMT) {
default:
init_special_inode(inode, mode, dev);
@@ -937,6 +924,18 @@
hugetlbfs_inc_free_inodes(sbinfo);
return NULL;
}
+
+ /*
+ * Any time after allocation, hugetlbfs_destroy_inode can be called
+ * for the inode. mpol_free_shared_policy is unconditionally called
+ * as part of hugetlbfs_destroy_inode. So, initialize policy here
+ * in case of a quick call to destroy.
+ *
+ * Note that the policy is initialized even if we are creating a
+ * private inode. This simplifies hugetlbfs_destroy_inode.
+ */
+ mpol_shared_policy_init(&p->policy, NULL);
+
return &p->vfs_inode;
}
diff --git a/fs/mpage.c b/fs/mpage.c
index 1193d43..d4e17c8 100644
--- a/fs/mpage.c
+++ b/fs/mpage.c
@@ -502,6 +502,16 @@
try_to_free_buffers(page);
}
+/*
+ * For situations where we want to clean all buffers attached to a page.
+ * We don't need to calculate how many buffers are attached to the page,
+ * we just need to specify a number larger than the maximum number of buffers.
+ */
+void clean_page_buffers(struct page *page)
+{
+ clean_buffers(page, ~0U);
+}
+
static int __mpage_writepage(struct page *page, struct writeback_control *wbc,
void *data)
{
@@ -640,10 +650,8 @@
if (bio == NULL) {
if (first_unmapped == blocks_per_page) {
if (!bdev_write_page(bdev, blocks[0] << (blkbits - 9),
- page, wbc)) {
- clean_buffers(page, first_unmapped);
+ page, wbc))
goto out;
- }
}
bio = mpage_alloc(bdev, blocks[0] << (blkbits - 9),
BIO_MAX_PAGES, GFP_NOFS|__GFP_HIGH);
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index 0a21150..af84a92 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -75,7 +75,10 @@
set_freezable();
- while (!kthread_should_stop()) {
+ while (!kthread_freezable_should_stop(NULL)) {
+
+ if (signal_pending(current))
+ flush_signals(current);
/*
* Listen for a request on the socket
*/
@@ -84,6 +87,8 @@
continue;
svc_process(rqstp);
}
+ svc_exit_thread(rqstp);
+ module_put_and_exit(0);
return 0;
}
@@ -102,9 +107,10 @@
set_freezable();
- while (!kthread_should_stop()) {
- if (try_to_freeze())
- continue;
+ while (!kthread_freezable_should_stop(NULL)) {
+
+ if (signal_pending(current))
+ flush_signals(current);
prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
spin_lock_bh(&serv->sv_cb_lock);
@@ -120,11 +126,13 @@
error);
} else {
spin_unlock_bh(&serv->sv_cb_lock);
- schedule();
+ if (!kthread_should_stop())
+ schedule();
finish_wait(&serv->sv_cb_waitq, &wq);
}
- flush_signals(current);
}
+ svc_exit_thread(rqstp);
+ module_put_and_exit(0);
return 0;
}
@@ -220,23 +228,23 @@
static struct svc_serv_ops nfs40_cb_sv_ops = {
.svo_function = nfs4_callback_svc,
.svo_enqueue_xprt = svc_xprt_do_enqueue,
- .svo_setup = svc_set_num_threads,
+ .svo_setup = svc_set_num_threads_sync,
.svo_module = THIS_MODULE,
};
#if defined(CONFIG_NFS_V4_1)
static struct svc_serv_ops nfs41_cb_sv_ops = {
.svo_function = nfs41_callback_svc,
.svo_enqueue_xprt = svc_xprt_do_enqueue,
- .svo_setup = svc_set_num_threads,
+ .svo_setup = svc_set_num_threads_sync,
.svo_module = THIS_MODULE,
};
-struct svc_serv_ops *nfs4_cb_sv_ops[] = {
+static struct svc_serv_ops *nfs4_cb_sv_ops[] = {
[0] = &nfs40_cb_sv_ops,
[1] = &nfs41_cb_sv_ops,
};
#else
-struct svc_serv_ops *nfs4_cb_sv_ops[] = {
+static struct svc_serv_ops *nfs4_cb_sv_ops[] = {
[0] = &nfs40_cb_sv_ops,
[1] = NULL,
};
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 211dc2a..3069cd4 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -753,6 +753,14 @@
return 0;
}
+void cleanup_callback_cred(void)
+{
+ if (callback_cred) {
+ put_rpccred(callback_cred);
+ callback_cred = NULL;
+ }
+}
+
static struct rpc_cred *get_backchannel_cred(struct nfs4_client *clp, struct rpc_clnt *client, struct nfsd4_session *ses)
{
if (clp->cl_minorversion == 0) {
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index a0dee8a..d35eb07 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -7012,23 +7012,24 @@
ret = set_callback_cred();
if (ret)
- return -ENOMEM;
+ return ret;
+
laundry_wq = alloc_workqueue("%s", WQ_UNBOUND, 0, "nfsd4");
if (laundry_wq == NULL) {
ret = -ENOMEM;
- goto out_recovery;
+ goto out_cleanup_cred;
}
ret = nfsd4_create_callback_queue();
if (ret)
goto out_free_laundry;
set_max_delegations();
-
return 0;
out_free_laundry:
destroy_workqueue(laundry_wq);
-out_recovery:
+out_cleanup_cred:
+ cleanup_callback_cred();
return ret;
}
@@ -7086,6 +7087,7 @@
{
destroy_workqueue(laundry_wq);
nfsd4_destroy_callback_queue();
+ cleanup_callback_cred();
}
static void
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 4516e8b..005c911 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -615,6 +615,7 @@
extern __be32 nfs4_check_open_reclaim(clientid_t *clid,
struct nfsd4_compound_state *cstate, struct nfsd_net *nn);
extern int set_callback_cred(void);
+extern void cleanup_callback_cred(void);
extern void nfsd4_probe_callback(struct nfs4_client *clp);
extern void nfsd4_probe_callback_sync(struct nfs4_client *clp);
extern void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *);
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index 77d1632..8dce409 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -532,6 +532,7 @@
init_waitqueue_head(&res->l_event);
INIT_LIST_HEAD(&res->l_blocked_list);
INIT_LIST_HEAD(&res->l_mask_waiters);
+ INIT_LIST_HEAD(&res->l_holders);
}
void ocfs2_inode_lock_res_init(struct ocfs2_lock_res *res,
@@ -749,6 +750,50 @@
res->l_flags = 0UL;
}
+/*
+ * Keep a list of processes who have interest in a lockres.
+ * Note: this is now only uesed for check recursive cluster locking.
+ */
+static inline void ocfs2_add_holder(struct ocfs2_lock_res *lockres,
+ struct ocfs2_lock_holder *oh)
+{
+ INIT_LIST_HEAD(&oh->oh_list);
+ oh->oh_owner_pid = get_pid(task_pid(current));
+
+ spin_lock(&lockres->l_lock);
+ list_add_tail(&oh->oh_list, &lockres->l_holders);
+ spin_unlock(&lockres->l_lock);
+}
+
+static inline void ocfs2_remove_holder(struct ocfs2_lock_res *lockres,
+ struct ocfs2_lock_holder *oh)
+{
+ spin_lock(&lockres->l_lock);
+ list_del(&oh->oh_list);
+ spin_unlock(&lockres->l_lock);
+
+ put_pid(oh->oh_owner_pid);
+}
+
+static inline int ocfs2_is_locked_by_me(struct ocfs2_lock_res *lockres)
+{
+ struct ocfs2_lock_holder *oh;
+ struct pid *pid;
+
+ /* look in the list of holders for one with the current task as owner */
+ spin_lock(&lockres->l_lock);
+ pid = task_pid(current);
+ list_for_each_entry(oh, &lockres->l_holders, oh_list) {
+ if (oh->oh_owner_pid == pid) {
+ spin_unlock(&lockres->l_lock);
+ return 1;
+ }
+ }
+ spin_unlock(&lockres->l_lock);
+
+ return 0;
+}
+
static inline void ocfs2_inc_holders(struct ocfs2_lock_res *lockres,
int level)
{
@@ -2333,8 +2378,9 @@
goto getbh;
}
- if (ocfs2_mount_local(osb))
- goto local;
+ if ((arg_flags & OCFS2_META_LOCK_GETBH) ||
+ ocfs2_mount_local(osb))
+ goto update;
if (!(arg_flags & OCFS2_META_LOCK_RECOVERY))
ocfs2_wait_for_recovery(osb);
@@ -2363,7 +2409,7 @@
if (!(arg_flags & OCFS2_META_LOCK_RECOVERY))
ocfs2_wait_for_recovery(osb);
-local:
+update:
/*
* We only see this flag if we're being called from
* ocfs2_read_locked_inode(). It means we're locking an inode
@@ -2497,6 +2543,59 @@
ocfs2_cluster_unlock(OCFS2_SB(inode->i_sb), lockres, level);
}
+/*
+ * This _tracker variantes are introduced to deal with the recursive cluster
+ * locking issue. The idea is to keep track of a lock holder on the stack of
+ * the current process. If there's a lock holder on the stack, we know the
+ * task context is already protected by cluster locking. Currently, they're
+ * used in some VFS entry routines.
+ *
+ * return < 0 on error, return == 0 if there's no lock holder on the stack
+ * before this call, return == 1 if this call would be a recursive locking.
+ */
+int ocfs2_inode_lock_tracker(struct inode *inode,
+ struct buffer_head **ret_bh,
+ int ex,
+ struct ocfs2_lock_holder *oh)
+{
+ int status;
+ int arg_flags = 0, has_locked;
+ struct ocfs2_lock_res *lockres;
+
+ lockres = &OCFS2_I(inode)->ip_inode_lockres;
+ has_locked = ocfs2_is_locked_by_me(lockres);
+ /* Just get buffer head if the cluster lock has been taken */
+ if (has_locked)
+ arg_flags = OCFS2_META_LOCK_GETBH;
+
+ if (likely(!has_locked || ret_bh)) {
+ status = ocfs2_inode_lock_full(inode, ret_bh, ex, arg_flags);
+ if (status < 0) {
+ if (status != -ENOENT)
+ mlog_errno(status);
+ return status;
+ }
+ }
+ if (!has_locked)
+ ocfs2_add_holder(lockres, oh);
+
+ return has_locked;
+}
+
+void ocfs2_inode_unlock_tracker(struct inode *inode,
+ int ex,
+ struct ocfs2_lock_holder *oh,
+ int had_lock)
+{
+ struct ocfs2_lock_res *lockres;
+
+ lockres = &OCFS2_I(inode)->ip_inode_lockres;
+ if (!had_lock) {
+ ocfs2_remove_holder(lockres, oh);
+ ocfs2_inode_unlock(inode, ex);
+ }
+}
+
int ocfs2_orphan_scan_lock(struct ocfs2_super *osb, u32 *seqno)
{
struct ocfs2_lock_res *lockres;
diff --git a/fs/ocfs2/dlmglue.h b/fs/ocfs2/dlmglue.h
index d293a22..a7fc18b 100644
--- a/fs/ocfs2/dlmglue.h
+++ b/fs/ocfs2/dlmglue.h
@@ -70,6 +70,11 @@
__be32 lvb_os_seqno;
};
+struct ocfs2_lock_holder {
+ struct list_head oh_list;
+ struct pid *oh_owner_pid;
+};
+
/* ocfs2_inode_lock_full() 'arg_flags' flags */
/* don't wait on recovery. */
#define OCFS2_META_LOCK_RECOVERY (0x01)
@@ -77,6 +82,8 @@
#define OCFS2_META_LOCK_NOQUEUE (0x02)
/* don't block waiting for the downconvert thread, instead return -EAGAIN */
#define OCFS2_LOCK_NONBLOCK (0x04)
+/* just get back disk inode bh if we've got cluster lock. */
+#define OCFS2_META_LOCK_GETBH (0x08)
/* Locking subclasses of inode cluster lock */
enum {
@@ -170,4 +177,15 @@
/* To set the locking protocol on module initialization */
void ocfs2_set_locking_protocol(void);
+
+/* The _tracker pair is used to avoid cluster recursive locking */
+int ocfs2_inode_lock_tracker(struct inode *inode,
+ struct buffer_head **ret_bh,
+ int ex,
+ struct ocfs2_lock_holder *oh);
+void ocfs2_inode_unlock_tracker(struct inode *inode,
+ int ex,
+ struct ocfs2_lock_holder *oh,
+ int had_lock);
+
#endif /* DLMGLUE_H */
diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h
index e63af7d..594575e 100644
--- a/fs/ocfs2/ocfs2.h
+++ b/fs/ocfs2/ocfs2.h
@@ -172,6 +172,7 @@
struct list_head l_blocked_list;
struct list_head l_mask_waiters;
+ struct list_head l_holders;
unsigned long l_flags;
char l_name[OCFS2_LOCK_ID_MAX_LEN];
diff --git a/fs/orangefs/acl.c b/fs/orangefs/acl.c
index 7a37544..9409aac 100644
--- a/fs/orangefs/acl.c
+++ b/fs/orangefs/acl.c
@@ -61,9 +61,9 @@
return acl;
}
-int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
+static int __orangefs_set_acl(struct inode *inode, struct posix_acl *acl,
+ int type)
{
- struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
int error = 0;
void *value = NULL;
size_t size = 0;
@@ -72,22 +72,6 @@
switch (type) {
case ACL_TYPE_ACCESS:
name = XATTR_NAME_POSIX_ACL_ACCESS;
- if (acl) {
- umode_t mode;
-
- error = posix_acl_update_mode(inode, &mode, &acl);
- if (error) {
- gossip_err("%s: posix_acl_update_mode err: %d\n",
- __func__,
- error);
- return error;
- }
-
- if (inode->i_mode != mode)
- SetModeFlag(orangefs_inode);
- inode->i_mode = mode;
- mark_inode_dirty_sync(inode);
- }
break;
case ACL_TYPE_DEFAULT:
name = XATTR_NAME_POSIX_ACL_DEFAULT;
@@ -132,6 +116,29 @@
return error;
}
+int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
+{
+ int error;
+
+ if (type == ACL_TYPE_ACCESS && acl) {
+ umode_t mode;
+
+ error = posix_acl_update_mode(inode, &mode, &acl);
+ if (error) {
+ gossip_err("%s: posix_acl_update_mode err: %d\n",
+ __func__,
+ error);
+ return error;
+ }
+
+ if (inode->i_mode != mode)
+ SetModeFlag(ORANGEFS_I(inode));
+ inode->i_mode = mode;
+ mark_inode_dirty_sync(inode);
+ }
+ return __orangefs_set_acl(inode, acl, type);
+}
+
int orangefs_init_acl(struct inode *inode, struct inode *dir)
{
struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
@@ -146,13 +153,14 @@
return error;
if (default_acl) {
- error = orangefs_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
+ error = __orangefs_set_acl(inode, default_acl,
+ ACL_TYPE_DEFAULT);
posix_acl_release(default_acl);
}
if (acl) {
if (!error)
- error = orangefs_set_acl(inode, acl, ACL_TYPE_ACCESS);
+ error = __orangefs_set_acl(inode, acl, ACL_TYPE_ACCESS);
posix_acl_release(acl);
}
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 81818ad..c932ec4 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -60,6 +60,7 @@
#include <linux/tty.h>
#include <linux/string.h>
#include <linux/mman.h>
+#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/uaccess.h>
@@ -416,7 +417,15 @@
* esp and eip are intentionally zeroed out. There is no
* non-racy way to read them without freezing the task.
* Programs that need reliable values can use ptrace(2).
+ *
+ * The only exception is if the task is core dumping because
+ * a program is not able to use ptrace(2) in that case. It is
+ * safe because the task has stopped executing permanently.
*/
+ if (permitted && (task->flags & PF_DUMPCORE)) {
+ eip = KSTK_EIP(task);
+ esp = KSTK_ESP(task);
+ }
}
get_task_comm(tcomm, task);
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 18f7612..1370a4e 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2896,6 +2896,52 @@
}
#endif /* CONFIG_TASK_IO_ACCOUNTING */
+#ifdef CONFIG_DETECT_HUNG_TASK
+static ssize_t proc_hung_task_detection_enabled_read(struct file *file,
+ char __user *buf, size_t count, loff_t *ppos)
+{
+ struct task_struct *task = get_proc_task(file_inode(file));
+ char buffer[PROC_NUMBUF];
+ size_t len;
+ bool hang_detection_enabled;
+
+ if (!task)
+ return -ESRCH;
+ hang_detection_enabled = task->hang_detection_enabled;
+ put_task_struct(task);
+
+ len = snprintf(buffer, sizeof(buffer), "%d\n", hang_detection_enabled);
+
+ return simple_read_from_buffer(buf, sizeof(buffer), ppos, buffer, len);
+}
+
+static ssize_t proc_hung_task_detection_enabled_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct task_struct *task;
+ bool hang_detection_enabled;
+ int rv;
+
+ rv = kstrtobool_from_user(buf, count, &hang_detection_enabled);
+ if (rv < 0)
+ return rv;
+
+ task = get_proc_task(file_inode(file));
+ if (!task)
+ return -ESRCH;
+ task->hang_detection_enabled = hang_detection_enabled;
+ put_task_struct(task);
+
+ return count;
+}
+
+static const struct file_operations proc_hung_task_detection_enabled_operations = {
+ .read = proc_hung_task_detection_enabled_read,
+ .write = proc_hung_task_detection_enabled_write,
+ .llseek = generic_file_llseek,
+};
+#endif
+
#ifdef CONFIG_USER_NS
static int proc_id_map_open(struct inode *inode, struct file *file,
const struct seq_operations *seq_ops)
@@ -3138,6 +3184,10 @@
#ifdef CONFIG_HARDWALL
ONE("hardwall", S_IRUGO, proc_pid_hardwall),
#endif
+#ifdef CONFIG_DETECT_HUNG_TASK
+ REG("hang_detection_enabled", 0666,
+ proc_hung_task_detection_enabled_operations),
+#endif
#ifdef CONFIG_USER_NS
REG("uid_map", S_IRUGO|S_IWUSR, proc_uid_map_operations),
REG("gid_map", S_IRUGO|S_IWUSR, proc_gid_map_operations),
@@ -3526,6 +3576,10 @@
#ifdef CONFIG_HARDWALL
ONE("hardwall", S_IRUGO, proc_pid_hardwall),
#endif
+#ifdef CONFIG_DETECT_HUNG_TASK
+ REG("hang_detection_enabled", 0666,
+ proc_hung_task_detection_enabled_operations),
+#endif
#ifdef CONFIG_USER_NS
REG("uid_map", S_IRUGO|S_IWUSR, proc_uid_map_operations),
REG("gid_map", S_IRUGO|S_IWUSR, proc_gid_map_operations),
diff --git a/fs/read_write.c b/fs/read_write.c
index e479e24..ba28059 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -114,7 +114,7 @@
* In the generic case the entire file is data, so as long as
* offset isn't at the end of the file then the offset is data.
*/
- if (offset >= eof)
+ if ((unsigned long long)offset >= eof)
return -ENXIO;
break;
case SEEK_HOLE:
@@ -122,7 +122,7 @@
* There is a virtual hole at the end of the file, so as long as
* offset isn't i_size or larger, return i_size.
*/
- if (offset >= eof)
+ if ((unsigned long long)offset >= eof)
return -ENXIO;
offset = eof;
break;
@@ -1518,6 +1518,11 @@
if (flags != 0)
return -EINVAL;
+ if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode))
+ return -EISDIR;
+ if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode))
+ return -EINVAL;
+
ret = rw_verify_area(READ, file_in, &pos_in, len);
if (unlikely(ret))
return ret;
diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig
index ffb093e..6dd158a 100644
--- a/fs/squashfs/Kconfig
+++ b/fs/squashfs/Kconfig
@@ -26,34 +26,6 @@
If unsure, say N.
choice
- prompt "File decompression options"
- depends on SQUASHFS
- help
- Squashfs now supports two options for decompressing file
- data. Traditionally Squashfs has decompressed into an
- intermediate buffer and then memcopied it into the page cache.
- Squashfs now supports the ability to decompress directly into
- the page cache.
-
- If unsure, select "Decompress file data into an intermediate buffer"
-
-config SQUASHFS_FILE_CACHE
- bool "Decompress file data into an intermediate buffer"
- help
- Decompress file data into an intermediate buffer and then
- memcopy it into the page cache.
-
-config SQUASHFS_FILE_DIRECT
- bool "Decompress files directly into the page cache"
- help
- Directly decompress file data into the page cache.
- Doing so can significantly improve performance because
- it eliminates a memcpy and it also removes the lock contention
- on the single buffer.
-
-endchoice
-
-choice
prompt "Decompressor parallelisation options"
depends on SQUASHFS
help
diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
index 246a6f3..fe51f15 100644
--- a/fs/squashfs/Makefile
+++ b/fs/squashfs/Makefile
@@ -5,8 +5,7 @@
obj-$(CONFIG_SQUASHFS) += squashfs.o
squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
squashfs-y += namei.o super.o symlink.o decompressor.o
-squashfs-$(CONFIG_SQUASHFS_FILE_CACHE) += file_cache.o
-squashfs-$(CONFIG_SQUASHFS_FILE_DIRECT) += file_direct.o page_actor.o
+squashfs-y += file_direct.o page_actor.o
squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o
squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o
squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o
diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c
index ce62a38..7077476 100644
--- a/fs/squashfs/block.c
+++ b/fs/squashfs/block.c
@@ -28,9 +28,12 @@
#include <linux/fs.h>
#include <linux/vfs.h>
+#include <linux/bio.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/pagemap.h>
#include <linux/buffer_head.h>
+#include <linux/workqueue.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
@@ -38,45 +41,382 @@
#include "decompressor.h"
#include "page_actor.h"
-/*
- * Read the metadata block length, this is stored in the first two
- * bytes of the metadata block.
- */
-static struct buffer_head *get_block_length(struct super_block *sb,
- u64 *cur_index, int *offset, int *length)
+static struct workqueue_struct *squashfs_read_wq;
+
+struct squashfs_read_request {
+ struct super_block *sb;
+ u64 index;
+ int length;
+ int compressed;
+ int offset;
+ u64 read_end;
+ struct squashfs_page_actor *output;
+ enum {
+ SQUASHFS_COPY,
+ SQUASHFS_DECOMPRESS,
+ SQUASHFS_METADATA,
+ } data_processing;
+ bool synchronous;
+
+ /*
+ * If the read is synchronous, it is possible to retrieve information
+ * about the request by setting these pointers.
+ */
+ int *res;
+ int *bytes_read;
+ int *bytes_uncompressed;
+
+ int nr_buffers;
+ struct buffer_head **bh;
+ struct work_struct offload;
+};
+
+struct squashfs_bio_request {
+ struct buffer_head **bh;
+ int nr_buffers;
+};
+
+static int squashfs_bio_submit(struct squashfs_read_request *req);
+
+int squashfs_init_read_wq(void)
{
- struct squashfs_sb_info *msblk = sb->s_fs_info;
- struct buffer_head *bh;
+ squashfs_read_wq = create_workqueue("SquashFS read wq");
+ return !!squashfs_read_wq;
+}
- bh = sb_bread(sb, *cur_index);
- if (bh == NULL)
- return NULL;
+void squashfs_destroy_read_wq(void)
+{
+ flush_workqueue(squashfs_read_wq);
+ destroy_workqueue(squashfs_read_wq);
+}
- if (msblk->devblksize - *offset == 1) {
- *length = (unsigned char) bh->b_data[*offset];
- put_bh(bh);
- bh = sb_bread(sb, ++(*cur_index));
- if (bh == NULL)
- return NULL;
- *length |= (unsigned char) bh->b_data[0] << 8;
- *offset = 1;
- } else {
- *length = (unsigned char) bh->b_data[*offset] |
- (unsigned char) bh->b_data[*offset + 1] << 8;
- *offset += 2;
+static void free_read_request(struct squashfs_read_request *req, int error)
+{
+ if (!req->synchronous)
+ squashfs_page_actor_free(req->output, error);
+ if (req->res)
+ *(req->res) = error;
+ kfree(req->bh);
+ kfree(req);
+}
- if (*offset == msblk->devblksize) {
- put_bh(bh);
- bh = sb_bread(sb, ++(*cur_index));
- if (bh == NULL)
- return NULL;
- *offset = 0;
+static void squashfs_process_blocks(struct squashfs_read_request *req)
+{
+ int error = 0;
+ int bytes, i, length;
+ struct squashfs_sb_info *msblk = req->sb->s_fs_info;
+ struct squashfs_page_actor *actor = req->output;
+ struct buffer_head **bh = req->bh;
+ int nr_buffers = req->nr_buffers;
+
+ for (i = 0; i < nr_buffers; ++i) {
+ if (!bh[i])
+ continue;
+ wait_on_buffer(bh[i]);
+ if (!buffer_uptodate(bh[i]))
+ error = -EIO;
+ }
+ if (error)
+ goto cleanup;
+
+ if (req->data_processing == SQUASHFS_METADATA) {
+ /* Extract the length of the metadata block */
+ if (req->offset != msblk->devblksize - 1) {
+ length = le16_to_cpup((__le16 *)
+ (bh[0]->b_data + req->offset));
+ } else {
+ length = (unsigned char)bh[0]->b_data[req->offset];
+ length |= (unsigned char)bh[1]->b_data[0] << 8;
+ }
+ req->compressed = SQUASHFS_COMPRESSED(length);
+ req->data_processing = req->compressed ? SQUASHFS_DECOMPRESS
+ : SQUASHFS_COPY;
+ length = SQUASHFS_COMPRESSED_SIZE(length);
+ if (req->index + length + 2 > req->read_end) {
+ for (i = 0; i < nr_buffers; ++i)
+ put_bh(bh[i]);
+ kfree(bh);
+ req->length = length;
+ req->index += 2;
+ squashfs_bio_submit(req);
+ return;
+ }
+ req->length = length;
+ req->offset = (req->offset + 2) % PAGE_SIZE;
+ if (req->offset < 2) {
+ put_bh(bh[0]);
+ ++bh;
+ --nr_buffers;
+ }
+ }
+ if (req->bytes_read)
+ *(req->bytes_read) = req->length;
+
+ if (req->data_processing == SQUASHFS_COPY) {
+ squashfs_bh_to_actor(bh, nr_buffers, req->output, req->offset,
+ req->length, msblk->devblksize);
+ } else if (req->data_processing == SQUASHFS_DECOMPRESS) {
+ req->length = squashfs_decompress(msblk, bh, nr_buffers,
+ req->offset, req->length, actor);
+ if (req->length < 0) {
+ error = -EIO;
+ goto cleanup;
}
}
- return bh;
+ /* Last page may have trailing bytes not filled */
+ bytes = req->length % PAGE_SIZE;
+ if (bytes && actor->page[actor->pages - 1])
+ zero_user_segment(actor->page[actor->pages - 1], bytes,
+ PAGE_SIZE);
+
+cleanup:
+ if (req->bytes_uncompressed)
+ *(req->bytes_uncompressed) = req->length;
+ if (error) {
+ for (i = 0; i < nr_buffers; ++i)
+ if (bh[i])
+ put_bh(bh[i]);
+ }
+ free_read_request(req, error);
}
+static void read_wq_handler(struct work_struct *work)
+{
+ squashfs_process_blocks(container_of(work,
+ struct squashfs_read_request, offload));
+}
+
+static void squashfs_bio_end_io(struct bio *bio)
+{
+ int i;
+ int error = bio->bi_error;
+ struct squashfs_bio_request *bio_req = bio->bi_private;
+
+ bio_put(bio);
+
+ for (i = 0; i < bio_req->nr_buffers; ++i) {
+ if (!bio_req->bh[i])
+ continue;
+ if (!error)
+ set_buffer_uptodate(bio_req->bh[i]);
+ else
+ clear_buffer_uptodate(bio_req->bh[i]);
+ unlock_buffer(bio_req->bh[i]);
+ }
+ kfree(bio_req);
+}
+
+static int bh_is_optional(struct squashfs_read_request *req, int idx)
+{
+ int start_idx, end_idx;
+ struct squashfs_sb_info *msblk = req->sb->s_fs_info;
+
+ start_idx = (idx * msblk->devblksize - req->offset) >> PAGE_SHIFT;
+ end_idx = ((idx + 1) * msblk->devblksize - req->offset + 1) >> PAGE_SHIFT;
+ if (start_idx >= req->output->pages)
+ return 1;
+ if (start_idx < 0)
+ start_idx = end_idx;
+ if (end_idx >= req->output->pages)
+ end_idx = start_idx;
+ return !req->output->page[start_idx] && !req->output->page[end_idx];
+}
+
+static int actor_getblks(struct squashfs_read_request *req, u64 block)
+{
+ int i;
+
+ req->bh = kmalloc_array(req->nr_buffers, sizeof(*(req->bh)), GFP_NOIO);
+ if (!req->bh)
+ return -ENOMEM;
+
+ for (i = 0; i < req->nr_buffers; ++i) {
+ /*
+ * When dealing with an uncompressed block, the actor may
+ * contains NULL pages. There's no need to read the buffers
+ * associated with these pages.
+ */
+ if (!req->compressed && bh_is_optional(req, i)) {
+ req->bh[i] = NULL;
+ continue;
+ }
+ req->bh[i] = sb_getblk(req->sb, block + i);
+ if (!req->bh[i]) {
+ while (--i) {
+ if (req->bh[i])
+ put_bh(req->bh[i]);
+ }
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int squashfs_bio_submit(struct squashfs_read_request *req)
+{
+ struct bio *bio = NULL;
+ struct buffer_head *bh;
+ struct squashfs_bio_request *bio_req = NULL;
+ int b = 0, prev_block = 0;
+ struct squashfs_sb_info *msblk = req->sb->s_fs_info;
+
+ u64 read_start = round_down(req->index, msblk->devblksize);
+ u64 read_end = round_up(req->index + req->length, msblk->devblksize);
+ sector_t block = read_start >> msblk->devblksize_log2;
+ sector_t block_end = read_end >> msblk->devblksize_log2;
+ int offset = read_start - round_down(req->index, PAGE_SIZE);
+ int nr_buffers = block_end - block;
+ int blksz = msblk->devblksize;
+ int bio_max_pages = nr_buffers > BIO_MAX_PAGES ? BIO_MAX_PAGES
+ : nr_buffers;
+
+ /* Setup the request */
+ req->read_end = read_end;
+ req->offset = req->index - read_start;
+ req->nr_buffers = nr_buffers;
+ if (actor_getblks(req, block) < 0)
+ goto getblk_failed;
+
+ /* Create and submit the BIOs */
+ for (b = 0; b < nr_buffers; ++b, offset += blksz) {
+ bh = req->bh[b];
+ if (!bh || !trylock_buffer(bh))
+ continue;
+ if (buffer_uptodate(bh)) {
+ unlock_buffer(bh);
+ continue;
+ }
+ offset %= PAGE_SIZE;
+
+ /* Append the buffer to the current BIO if it is contiguous */
+ if (bio && bio_req && prev_block + 1 == b) {
+ if (bio_add_page(bio, bh->b_page, blksz, offset)) {
+ bio_req->nr_buffers += 1;
+ prev_block = b;
+ continue;
+ }
+ }
+
+ /* Otherwise, submit the current BIO and create a new one */
+ if (bio)
+ submit_bio(bio);
+ bio_req = kcalloc(1, sizeof(struct squashfs_bio_request),
+ GFP_NOIO);
+ if (!bio_req)
+ goto req_alloc_failed;
+ bio_req->bh = &req->bh[b];
+ bio = bio_alloc(GFP_NOIO, bio_max_pages);
+ if (!bio)
+ goto bio_alloc_failed;
+ bio->bi_bdev = req->sb->s_bdev;
+ bio->bi_iter.bi_sector = (block + b)
+ << (msblk->devblksize_log2 - 9);
+ bio_set_op_attrs(bio, REQ_OP_READ, 0);
+ bio->bi_private = bio_req;
+ bio->bi_end_io = squashfs_bio_end_io;
+
+ bio_add_page(bio, bh->b_page, blksz, offset);
+ bio_req->nr_buffers += 1;
+ prev_block = b;
+ }
+ if (bio)
+ submit_bio(bio);
+
+ if (req->synchronous)
+ squashfs_process_blocks(req);
+ else {
+ INIT_WORK(&req->offload, read_wq_handler);
+ schedule_work(&req->offload);
+ }
+ return 0;
+
+bio_alloc_failed:
+ kfree(bio_req);
+req_alloc_failed:
+ unlock_buffer(bh);
+ while (--nr_buffers >= b)
+ if (req->bh[nr_buffers])
+ put_bh(req->bh[nr_buffers]);
+ while (--b >= 0)
+ if (req->bh[b])
+ wait_on_buffer(req->bh[b]);
+getblk_failed:
+ free_read_request(req, -ENOMEM);
+ return -ENOMEM;
+}
+
+static int read_metadata_block(struct squashfs_read_request *req,
+ u64 *next_index)
+{
+ int ret, error, bytes_read = 0, bytes_uncompressed = 0;
+ struct squashfs_sb_info *msblk = req->sb->s_fs_info;
+
+ if (req->index + 2 > msblk->bytes_used) {
+ free_read_request(req, -EINVAL);
+ return -EINVAL;
+ }
+ req->length = 2;
+
+ /* Do not read beyond the end of the device */
+ if (req->index + req->length > msblk->bytes_used)
+ req->length = msblk->bytes_used - req->index;
+ req->data_processing = SQUASHFS_METADATA;
+
+ /*
+ * Reading metadata is always synchronous because we don't know the
+ * length in advance and the function is expected to update
+ * 'next_index' and return the length.
+ */
+ req->synchronous = true;
+ req->res = &error;
+ req->bytes_read = &bytes_read;
+ req->bytes_uncompressed = &bytes_uncompressed;
+
+ TRACE("Metadata block @ 0x%llx, %scompressed size %d, src size %d\n",
+ req->index, req->compressed ? "" : "un", bytes_read,
+ req->output->length);
+
+ ret = squashfs_bio_submit(req);
+ if (ret)
+ return ret;
+ if (error)
+ return error;
+ if (next_index)
+ *next_index += 2 + bytes_read;
+ return bytes_uncompressed;
+}
+
+static int read_data_block(struct squashfs_read_request *req, int length,
+ u64 *next_index, bool synchronous)
+{
+ int ret, error = 0, bytes_uncompressed = 0, bytes_read = 0;
+
+ req->compressed = SQUASHFS_COMPRESSED_BLOCK(length);
+ req->length = length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
+ req->data_processing = req->compressed ? SQUASHFS_DECOMPRESS
+ : SQUASHFS_COPY;
+
+ req->synchronous = synchronous;
+ if (synchronous) {
+ req->res = &error;
+ req->bytes_read = &bytes_read;
+ req->bytes_uncompressed = &bytes_uncompressed;
+ }
+
+ TRACE("Data block @ 0x%llx, %scompressed size %d, src size %d\n",
+ req->index, req->compressed ? "" : "un", req->length,
+ req->output->length);
+
+ ret = squashfs_bio_submit(req);
+ if (ret)
+ return ret;
+ if (synchronous)
+ ret = error ? error : bytes_uncompressed;
+ if (next_index)
+ *next_index += length;
+ return ret;
+}
/*
* Read and decompress a metadata block or datablock. Length is non-zero
@@ -87,128 +427,50 @@
* generated a larger block - this does occasionally happen with compression
* algorithms).
*/
-int squashfs_read_data(struct super_block *sb, u64 index, int length,
- u64 *next_index, struct squashfs_page_actor *output)
+static int __squashfs_read_data(struct super_block *sb, u64 index, int length,
+ u64 *next_index, struct squashfs_page_actor *output, bool sync)
{
- struct squashfs_sb_info *msblk = sb->s_fs_info;
- struct buffer_head **bh;
- int offset = index & ((1 << msblk->devblksize_log2) - 1);
- u64 cur_index = index >> msblk->devblksize_log2;
- int bytes, compressed, b = 0, k = 0, avail, i;
+ struct squashfs_read_request *req;
- bh = kcalloc(((output->length + msblk->devblksize - 1)
- >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
- if (bh == NULL)
+ req = kcalloc(1, sizeof(struct squashfs_read_request), GFP_KERNEL);
+ if (!req) {
+ if (!sync)
+ squashfs_page_actor_free(output, -ENOMEM);
return -ENOMEM;
-
- if (length) {
- /*
- * Datablock.
- */
- bytes = -offset;
- compressed = SQUASHFS_COMPRESSED_BLOCK(length);
- length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
- if (next_index)
- *next_index = index + length;
-
- TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
- index, compressed ? "" : "un", length, output->length);
-
- if (length < 0 || length > output->length ||
- (index + length) > msblk->bytes_used)
- goto read_failure;
-
- for (b = 0; bytes < length; b++, cur_index++) {
- bh[b] = sb_getblk(sb, cur_index);
- if (bh[b] == NULL)
- goto block_release;
- bytes += msblk->devblksize;
- }
- ll_rw_block(REQ_OP_READ, 0, b, bh);
- } else {
- /*
- * Metadata block.
- */
- if ((index + 2) > msblk->bytes_used)
- goto read_failure;
-
- bh[0] = get_block_length(sb, &cur_index, &offset, &length);
- if (bh[0] == NULL)
- goto read_failure;
- b = 1;
-
- bytes = msblk->devblksize - offset;
- compressed = SQUASHFS_COMPRESSED(length);
- length = SQUASHFS_COMPRESSED_SIZE(length);
- if (next_index)
- *next_index = index + length + 2;
-
- TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
- compressed ? "" : "un", length);
-
- if (length < 0 || length > output->length ||
- (index + length) > msblk->bytes_used)
- goto block_release;
-
- for (; bytes < length; b++) {
- bh[b] = sb_getblk(sb, ++cur_index);
- if (bh[b] == NULL)
- goto block_release;
- bytes += msblk->devblksize;
- }
- ll_rw_block(REQ_OP_READ, 0, b - 1, bh + 1);
}
- for (i = 0; i < b; i++) {
- wait_on_buffer(bh[i]);
- if (!buffer_uptodate(bh[i]))
- goto block_release;
+ req->sb = sb;
+ req->index = index;
+ req->output = output;
+
+ if (next_index)
+ *next_index = index;
+
+ if (length)
+ length = read_data_block(req, length, next_index, sync);
+ else
+ length = read_metadata_block(req, next_index);
+
+ if (length < 0) {
+ ERROR("squashfs_read_data failed to read block 0x%llx\n",
+ (unsigned long long)index);
+ return -EIO;
}
- if (compressed) {
- length = squashfs_decompress(msblk, bh, b, offset, length,
- output);
- if (length < 0)
- goto read_failure;
- } else {
- /*
- * Block is uncompressed.
- */
- int in, pg_offset = 0;
- void *data = squashfs_first_page(output);
-
- for (bytes = length; k < b; k++) {
- in = min(bytes, msblk->devblksize - offset);
- bytes -= in;
- while (in) {
- if (pg_offset == PAGE_SIZE) {
- data = squashfs_next_page(output);
- pg_offset = 0;
- }
- avail = min_t(int, in, PAGE_SIZE -
- pg_offset);
- memcpy(data + pg_offset, bh[k]->b_data + offset,
- avail);
- in -= avail;
- pg_offset += avail;
- offset += avail;
- }
- offset = 0;
- put_bh(bh[k]);
- }
- squashfs_finish_page(output);
- }
-
- kfree(bh);
return length;
+}
-block_release:
- for (; k < b; k++)
- put_bh(bh[k]);
+int squashfs_read_data(struct super_block *sb, u64 index, int length,
+ u64 *next_index, struct squashfs_page_actor *output)
+{
+ return __squashfs_read_data(sb, index, length, next_index, output,
+ true);
+}
-read_failure:
- ERROR("squashfs_read_data failed to read block 0x%llx\n",
- (unsigned long long) index);
- kfree(bh);
- return -EIO;
+int squashfs_read_data_async(struct super_block *sb, u64 index, int length,
+ u64 *next_index, struct squashfs_page_actor *output)
+{
+
+ return __squashfs_read_data(sb, index, length, next_index, output,
+ false);
}
diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c
index 23813c0..05e4244 100644
--- a/fs/squashfs/cache.c
+++ b/fs/squashfs/cache.c
@@ -209,17 +209,14 @@
*/
void squashfs_cache_delete(struct squashfs_cache *cache)
{
- int i, j;
+ int i;
if (cache == NULL)
return;
for (i = 0; i < cache->entries; i++) {
- if (cache->entry[i].data) {
- for (j = 0; j < cache->pages; j++)
- kfree(cache->entry[i].data[j]);
- kfree(cache->entry[i].data);
- }
+ if (cache->entry[i].page)
+ free_page_array(cache->entry[i].page, cache->pages);
kfree(cache->entry[i].actor);
}
@@ -236,7 +233,7 @@
struct squashfs_cache *squashfs_cache_init(char *name, int entries,
int block_size)
{
- int i, j;
+ int i;
struct squashfs_cache *cache = kzalloc(sizeof(*cache), GFP_KERNEL);
if (cache == NULL) {
@@ -268,22 +265,13 @@
init_waitqueue_head(&cache->entry[i].wait_queue);
entry->cache = cache;
entry->block = SQUASHFS_INVALID_BLK;
- entry->data = kcalloc(cache->pages, sizeof(void *), GFP_KERNEL);
- if (entry->data == NULL) {
+ entry->page = alloc_page_array(cache->pages, GFP_KERNEL);
+ if (!entry->page) {
ERROR("Failed to allocate %s cache entry\n", name);
goto cleanup;
}
-
- for (j = 0; j < cache->pages; j++) {
- entry->data[j] = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (entry->data[j] == NULL) {
- ERROR("Failed to allocate %s buffer\n", name);
- goto cleanup;
- }
- }
-
- entry->actor = squashfs_page_actor_init(entry->data,
- cache->pages, 0);
+ entry->actor = squashfs_page_actor_init(entry->page,
+ cache->pages, 0, NULL);
if (entry->actor == NULL) {
ERROR("Failed to allocate %s cache entry\n", name);
goto cleanup;
@@ -314,18 +302,20 @@
return min(length, entry->length - offset);
while (offset < entry->length) {
- void *buff = entry->data[offset / PAGE_SIZE]
- + (offset % PAGE_SIZE);
+ void *buff = kmap_atomic(entry->page[offset / PAGE_SIZE])
+ + (offset % PAGE_SIZE);
int bytes = min_t(int, entry->length - offset,
PAGE_SIZE - (offset % PAGE_SIZE));
if (bytes >= remaining) {
memcpy(buffer, buff, remaining);
+ kunmap_atomic(buff);
remaining = 0;
break;
}
memcpy(buffer, buff, bytes);
+ kunmap_atomic(buff);
buffer += bytes;
remaining -= bytes;
offset += bytes;
@@ -416,43 +406,38 @@
void *squashfs_read_table(struct super_block *sb, u64 block, int length)
{
int pages = (length + PAGE_SIZE - 1) >> PAGE_SHIFT;
- int i, res;
- void *table, *buffer, **data;
+ struct page **page;
+ void *buff;
+ int res;
struct squashfs_page_actor *actor;
- table = buffer = kmalloc(length, GFP_KERNEL);
- if (table == NULL)
+ page = alloc_page_array(pages, GFP_KERNEL);
+ if (!page)
return ERR_PTR(-ENOMEM);
- data = kcalloc(pages, sizeof(void *), GFP_KERNEL);
- if (data == NULL) {
+ actor = squashfs_page_actor_init(page, pages, length, NULL);
+ if (actor == NULL) {
res = -ENOMEM;
goto failed;
}
- actor = squashfs_page_actor_init(data, pages, length);
- if (actor == NULL) {
- res = -ENOMEM;
- goto failed2;
- }
-
- for (i = 0; i < pages; i++, buffer += PAGE_SIZE)
- data[i] = buffer;
-
res = squashfs_read_data(sb, block, length |
SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor);
- kfree(data);
- kfree(actor);
-
if (res < 0)
- goto failed;
+ goto failed2;
- return table;
+ buff = kmalloc(length, GFP_KERNEL);
+ if (!buff)
+ goto failed2;
+ squashfs_actor_to_buf(actor, buff, length);
+ squashfs_page_actor_free(actor, 0);
+ free_page_array(page, pages);
+ return buff;
failed2:
- kfree(data);
+ squashfs_page_actor_free(actor, 0);
failed:
- kfree(table);
+ free_page_array(page, pages);
return ERR_PTR(res);
}
diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c
index d2bc136..7de35bf 100644
--- a/fs/squashfs/decompressor.c
+++ b/fs/squashfs/decompressor.c
@@ -24,7 +24,8 @@
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/slab.h>
-#include <linux/buffer_head.h>
+#include <linux/highmem.h>
+#include <linux/fs.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
@@ -94,40 +95,44 @@
static void *get_comp_opts(struct super_block *sb, unsigned short flags)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
- void *buffer = NULL, *comp_opts;
+ void *comp_opts, *buffer = NULL;
+ struct page *page;
struct squashfs_page_actor *actor = NULL;
int length = 0;
+ if (!SQUASHFS_COMP_OPTS(flags))
+ return squashfs_comp_opts(msblk, buffer, length);
+
/*
* Read decompressor specific options from file system if present
*/
- if (SQUASHFS_COMP_OPTS(flags)) {
- buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (buffer == NULL) {
- comp_opts = ERR_PTR(-ENOMEM);
- goto out;
- }
- actor = squashfs_page_actor_init(&buffer, 1, 0);
- if (actor == NULL) {
- comp_opts = ERR_PTR(-ENOMEM);
- goto out;
- }
+ page = alloc_page(GFP_KERNEL);
+ if (!page)
+ return ERR_PTR(-ENOMEM);
- length = squashfs_read_data(sb,
- sizeof(struct squashfs_super_block), 0, NULL, actor);
-
- if (length < 0) {
- comp_opts = ERR_PTR(length);
- goto out;
- }
+ actor = squashfs_page_actor_init(&page, 1, 0, NULL);
+ if (actor == NULL) {
+ comp_opts = ERR_PTR(-ENOMEM);
+ goto actor_error;
}
- comp_opts = squashfs_comp_opts(msblk, buffer, length);
+ length = squashfs_read_data(sb,
+ sizeof(struct squashfs_super_block), 0, NULL, actor);
-out:
- kfree(actor);
- kfree(buffer);
+ if (length < 0) {
+ comp_opts = ERR_PTR(length);
+ goto read_error;
+ }
+
+ buffer = kmap_atomic(page);
+ comp_opts = squashfs_comp_opts(msblk, buffer, length);
+ kunmap_atomic(buffer);
+
+read_error:
+ squashfs_page_actor_free(actor, 0);
+actor_error:
+ __free_page(page);
return comp_opts;
}
diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c
index 13d8094..bb2e77e 100644
--- a/fs/squashfs/file.c
+++ b/fs/squashfs/file.c
@@ -47,6 +47,7 @@
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/mutex.h>
+#include <linux/mm_inline.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
@@ -438,6 +439,21 @@
return res;
}
+static int squashfs_readpages_fragment(struct page *page,
+ struct list_head *readahead_pages, struct address_space *mapping)
+{
+ if (!page) {
+ page = lru_to_page(readahead_pages);
+ list_del(&page->lru);
+ if (add_to_page_cache_lru(page, mapping, page->index,
+ mapping_gfp_constraint(mapping, GFP_KERNEL))) {
+ put_page(page);
+ return 0;
+ }
+ }
+ return squashfs_readpage_fragment(page);
+}
+
static int squashfs_readpage_sparse(struct page *page, int index, int file_end)
{
struct inode *inode = page->mapping->host;
@@ -450,54 +466,105 @@
return 0;
}
-static int squashfs_readpage(struct file *file, struct page *page)
+static int squashfs_readpages_sparse(struct page *page,
+ struct list_head *readahead_pages, int index, int file_end,
+ struct address_space *mapping)
{
- struct inode *inode = page->mapping->host;
+ if (!page) {
+ page = lru_to_page(readahead_pages);
+ list_del(&page->lru);
+ if (add_to_page_cache_lru(page, mapping, page->index,
+ mapping_gfp_constraint(mapping, GFP_KERNEL))) {
+ put_page(page);
+ return 0;
+ }
+ }
+ return squashfs_readpage_sparse(page, index, file_end);
+}
+
+static int __squashfs_readpages(struct file *file, struct page *page,
+ struct list_head *readahead_pages, unsigned int nr_pages,
+ struct address_space *mapping)
+{
+ struct inode *inode = mapping->host;
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
- int index = page->index >> (msblk->block_log - PAGE_SHIFT);
int file_end = i_size_read(inode) >> msblk->block_log;
int res;
- void *pageaddr;
+
+ do {
+ struct page *cur_page = page ? page
+ : lru_to_page(readahead_pages);
+ int page_index = cur_page->index;
+ int index = page_index >> (msblk->block_log - PAGE_SHIFT);
+
+ if (page_index >= ((i_size_read(inode) + PAGE_SIZE - 1) >>
+ PAGE_SHIFT))
+ return 1;
+
+ if (index < file_end || squashfs_i(inode)->fragment_block ==
+ SQUASHFS_INVALID_BLK) {
+ u64 block = 0;
+ int bsize = read_blocklist(inode, index, &block);
+
+ if (bsize < 0)
+ return -1;
+
+ if (bsize == 0) {
+ res = squashfs_readpages_sparse(page,
+ readahead_pages, index, file_end,
+ mapping);
+ } else {
+ res = squashfs_readpages_block(page,
+ readahead_pages, &nr_pages, mapping,
+ page_index, block, bsize);
+ }
+ } else {
+ res = squashfs_readpages_fragment(page,
+ readahead_pages, mapping);
+ }
+ if (res)
+ return 0;
+ page = NULL;
+ } while (readahead_pages && !list_empty(readahead_pages));
+
+ return 0;
+}
+
+static int squashfs_readpage(struct file *file, struct page *page)
+{
+ int ret;
TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
- page->index, squashfs_i(inode)->start);
+ page->index, squashfs_i(page->mapping->host)->start);
- if (page->index >= ((i_size_read(inode) + PAGE_SIZE - 1) >>
- PAGE_SHIFT))
- goto out;
+ get_page(page);
- if (index < file_end || squashfs_i(inode)->fragment_block ==
- SQUASHFS_INVALID_BLK) {
- u64 block = 0;
- int bsize = read_blocklist(inode, index, &block);
- if (bsize < 0)
- goto error_out;
-
- if (bsize == 0)
- res = squashfs_readpage_sparse(page, index, file_end);
+ ret = __squashfs_readpages(file, page, NULL, 1, page->mapping);
+ if (ret) {
+ flush_dcache_page(page);
+ if (ret < 0)
+ SetPageError(page);
else
- res = squashfs_readpage_block(page, block, bsize);
- } else
- res = squashfs_readpage_fragment(page);
+ SetPageUptodate(page);
+ zero_user_segment(page, 0, PAGE_SIZE);
+ unlock_page(page);
+ put_page(page);
+ }
- if (!res)
- return 0;
+ return 0;
+}
-error_out:
- SetPageError(page);
-out:
- pageaddr = kmap_atomic(page);
- memset(pageaddr, 0, PAGE_SIZE);
- kunmap_atomic(pageaddr);
- flush_dcache_page(page);
- if (!PageError(page))
- SetPageUptodate(page);
- unlock_page(page);
-
+static int squashfs_readpages(struct file *file, struct address_space *mapping,
+ struct list_head *pages, unsigned int nr_pages)
+{
+ TRACE("Entered squashfs_readpages, %u pages, first page index %lx\n",
+ nr_pages, lru_to_page(pages)->index);
+ __squashfs_readpages(file, NULL, pages, nr_pages, mapping);
return 0;
}
const struct address_space_operations squashfs_aops = {
- .readpage = squashfs_readpage
+ .readpage = squashfs_readpage,
+ .readpages = squashfs_readpages,
};
diff --git a/fs/squashfs/file_cache.c b/fs/squashfs/file_cache.c
deleted file mode 100644
index f2310d2..0000000
--- a/fs/squashfs/file_cache.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2013
- * Phillip Lougher <phillip@squashfs.org.uk>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- */
-
-#include <linux/fs.h>
-#include <linux/vfs.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/pagemap.h>
-#include <linux/mutex.h>
-
-#include "squashfs_fs.h"
-#include "squashfs_fs_sb.h"
-#include "squashfs_fs_i.h"
-#include "squashfs.h"
-
-/* Read separately compressed datablock and memcopy into page cache */
-int squashfs_readpage_block(struct page *page, u64 block, int bsize)
-{
- struct inode *i = page->mapping->host;
- struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
- block, bsize);
- int res = buffer->error;
-
- if (res)
- ERROR("Unable to read page, block %llx, size %x\n", block,
- bsize);
- else
- squashfs_copy_cache(page, buffer, buffer->length, 0);
-
- squashfs_cache_put(buffer);
- return res;
-}
diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c
index cb485d8..dc87f77 100644
--- a/fs/squashfs/file_direct.c
+++ b/fs/squashfs/file_direct.c
@@ -13,6 +13,7 @@
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/mutex.h>
+#include <linux/mm_inline.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
@@ -20,157 +21,136 @@
#include "squashfs.h"
#include "page_actor.h"
-static int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
- int pages, struct page **page);
-
-/* Read separately compressed datablock directly into page cache */
-int squashfs_readpage_block(struct page *target_page, u64 block, int bsize)
-
+static void release_actor_pages(struct page **page, int pages, int error)
{
- struct inode *inode = target_page->mapping->host;
- struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+ int i;
- int file_end = (i_size_read(inode) - 1) >> PAGE_SHIFT;
- int mask = (1 << (msblk->block_log - PAGE_SHIFT)) - 1;
- int start_index = target_page->index & ~mask;
- int end_index = start_index | mask;
- int i, n, pages, missing_pages, bytes, res = -ENOMEM;
+ for (i = 0; i < pages; i++) {
+ if (!page[i])
+ continue;
+ flush_dcache_page(page[i]);
+ if (!error)
+ SetPageUptodate(page[i]);
+ else {
+ SetPageError(page[i]);
+ zero_user_segment(page[i], 0, PAGE_SIZE);
+ }
+ unlock_page(page[i]);
+ put_page(page[i]);
+ }
+ kfree(page);
+}
+
+/*
+ * Create a "page actor" which will kmap and kunmap the
+ * page cache pages appropriately within the decompressor
+ */
+static struct squashfs_page_actor *actor_from_page_cache(
+ unsigned int actor_pages, struct page *target_page,
+ struct list_head *rpages, unsigned int *nr_pages, int start_index,
+ struct address_space *mapping)
+{
struct page **page;
struct squashfs_page_actor *actor;
- void *pageaddr;
+ int i, n;
+ gfp_t gfp = mapping_gfp_constraint(mapping, GFP_KERNEL);
- if (end_index > file_end)
- end_index = file_end;
+ page = kmalloc_array(actor_pages, sizeof(void *), GFP_KERNEL);
+ if (!page)
+ return NULL;
- pages = end_index - start_index + 1;
+ for (i = 0, n = start_index; i < actor_pages; i++, n++) {
+ if (target_page == NULL && rpages && !list_empty(rpages)) {
+ struct page *cur_page = lru_to_page(rpages);
- page = kmalloc_array(pages, sizeof(void *), GFP_KERNEL);
- if (page == NULL)
- return res;
+ if (cur_page->index < start_index + actor_pages) {
+ list_del(&cur_page->lru);
+ --(*nr_pages);
+ if (add_to_page_cache_lru(cur_page, mapping,
+ cur_page->index, gfp))
+ put_page(cur_page);
+ else
+ target_page = cur_page;
+ } else
+ rpages = NULL;
+ }
- /*
- * Create a "page actor" which will kmap and kunmap the
- * page cache pages appropriately within the decompressor
- */
- actor = squashfs_page_actor_init_special(page, pages, 0);
- if (actor == NULL)
- goto out;
-
- /* Try to grab all the pages covered by the Squashfs block */
- for (missing_pages = 0, i = 0, n = start_index; i < pages; i++, n++) {
- page[i] = (n == target_page->index) ? target_page :
- grab_cache_page_nowait(target_page->mapping, n);
-
- if (page[i] == NULL) {
- missing_pages++;
- continue;
+ if (target_page && target_page->index == n) {
+ page[i] = target_page;
+ target_page = NULL;
+ } else {
+ page[i] = grab_cache_page_nowait(mapping, n);
+ if (page[i] == NULL)
+ continue;
}
if (PageUptodate(page[i])) {
unlock_page(page[i]);
put_page(page[i]);
page[i] = NULL;
- missing_pages++;
}
}
- if (missing_pages) {
- /*
- * Couldn't get one or more pages, this page has either
- * been VM reclaimed, but others are still in the page cache
- * and uptodate, or we're racing with another thread in
- * squashfs_readpage also trying to grab them. Fall back to
- * using an intermediate buffer.
- */
- res = squashfs_read_cache(target_page, block, bsize, pages,
- page);
- if (res < 0)
- goto mark_errored;
-
- goto out;
+ actor = squashfs_page_actor_init(page, actor_pages, 0,
+ release_actor_pages);
+ if (!actor) {
+ release_actor_pages(page, actor_pages, -ENOMEM);
+ kfree(page);
+ return NULL;
}
-
- /* Decompress directly into the page cache buffers */
- res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor);
- if (res < 0)
- goto mark_errored;
-
- /* Last page may have trailing bytes not filled */
- bytes = res % PAGE_SIZE;
- if (bytes) {
- pageaddr = kmap_atomic(page[pages - 1]);
- memset(pageaddr + bytes, 0, PAGE_SIZE - bytes);
- kunmap_atomic(pageaddr);
- }
-
- /* Mark pages as uptodate, unlock and release */
- for (i = 0; i < pages; i++) {
- flush_dcache_page(page[i]);
- SetPageUptodate(page[i]);
- unlock_page(page[i]);
- if (page[i] != target_page)
- put_page(page[i]);
- }
-
- kfree(actor);
- kfree(page);
-
- return 0;
-
-mark_errored:
- /* Decompression failed, mark pages as errored. Target_page is
- * dealt with by the caller
- */
- for (i = 0; i < pages; i++) {
- if (page[i] == NULL || page[i] == target_page)
- continue;
- flush_dcache_page(page[i]);
- SetPageError(page[i]);
- unlock_page(page[i]);
- put_page(page[i]);
- }
-
-out:
- kfree(actor);
- kfree(page);
- return res;
+ return actor;
}
+int squashfs_readpages_block(struct page *target_page,
+ struct list_head *readahead_pages,
+ unsigned int *nr_pages,
+ struct address_space *mapping,
+ int page_index, u64 block, int bsize)
-static int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
- int pages, struct page **page)
{
- struct inode *i = target_page->mapping->host;
- struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
- block, bsize);
- int bytes = buffer->length, res = buffer->error, n, offset = 0;
- void *pageaddr;
+ struct squashfs_page_actor *actor;
+ struct inode *inode = mapping->host;
+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+ int start_index, end_index, file_end, actor_pages, res;
+ int mask = (1 << (msblk->block_log - PAGE_SHIFT)) - 1;
- if (res) {
- ERROR("Unable to read page, block %llx, size %x\n", block,
- bsize);
- goto out;
+ /*
+ * If readpage() is called on an uncompressed datablock, we can just
+ * read the pages instead of fetching the whole block.
+ * This greatly improves the performance when a process keep doing
+ * random reads because we only fetch the necessary data.
+ * The readahead algorithm will take care of doing speculative reads
+ * if necessary.
+ * We can't read more than 1 block even if readahead provides use more
+ * pages because we don't know yet if the next block is compressed or
+ * not.
+ */
+ if (bsize && !SQUASHFS_COMPRESSED_BLOCK(bsize)) {
+ u64 block_end = block + msblk->block_size;
+
+ block += (page_index & mask) * PAGE_SIZE;
+ actor_pages = (block_end - block) / PAGE_SIZE;
+ if (*nr_pages < actor_pages)
+ actor_pages = *nr_pages;
+ start_index = page_index;
+ bsize = min_t(int, bsize, (PAGE_SIZE * actor_pages)
+ | SQUASHFS_COMPRESSED_BIT_BLOCK);
+ } else {
+ file_end = (i_size_read(inode) - 1) >> PAGE_SHIFT;
+ start_index = page_index & ~mask;
+ end_index = start_index | mask;
+ if (end_index > file_end)
+ end_index = file_end;
+ actor_pages = end_index - start_index + 1;
}
- for (n = 0; n < pages && bytes > 0; n++,
- bytes -= PAGE_SIZE, offset += PAGE_SIZE) {
- int avail = min_t(int, bytes, PAGE_SIZE);
+ actor = actor_from_page_cache(actor_pages, target_page,
+ readahead_pages, nr_pages, start_index,
+ mapping);
+ if (!actor)
+ return -ENOMEM;
- if (page[n] == NULL)
- continue;
-
- pageaddr = kmap_atomic(page[n]);
- squashfs_copy_data(pageaddr, buffer, offset, avail);
- memset(pageaddr + avail, 0, PAGE_SIZE - avail);
- kunmap_atomic(pageaddr);
- flush_dcache_page(page[n]);
- SetPageUptodate(page[n]);
- unlock_page(page[n]);
- if (page[n] != target_page)
- put_page(page[n]);
- }
-
-out:
- squashfs_cache_put(buffer);
- return res;
+ res = squashfs_read_data_async(inode->i_sb, block, bsize, NULL,
+ actor);
+ return res < 0 ? res : 0;
}
diff --git a/fs/squashfs/lz4_wrapper.c b/fs/squashfs/lz4_wrapper.c
index ff4468b..df4fa3c 100644
--- a/fs/squashfs/lz4_wrapper.c
+++ b/fs/squashfs/lz4_wrapper.c
@@ -94,39 +94,17 @@
struct buffer_head **bh, int b, int offset, int length,
struct squashfs_page_actor *output)
{
- struct squashfs_lz4 *stream = strm;
- void *buff = stream->input, *data;
- int avail, i, bytes = length, res;
+ int res;
size_t dest_len = output->length;
+ struct squashfs_lz4 *stream = strm;
- for (i = 0; i < b; i++) {
- avail = min(bytes, msblk->devblksize - offset);
- memcpy(buff, bh[i]->b_data + offset, avail);
- buff += avail;
- bytes -= avail;
- offset = 0;
- put_bh(bh[i]);
- }
-
+ squashfs_bh_to_buf(bh, b, stream->input, offset, length,
+ msblk->devblksize);
res = lz4_decompress_unknownoutputsize(stream->input, length,
stream->output, &dest_len);
if (res)
return -EIO;
-
- bytes = dest_len;
- data = squashfs_first_page(output);
- buff = stream->output;
- while (data) {
- if (bytes <= PAGE_SIZE) {
- memcpy(data, buff, bytes);
- break;
- }
- memcpy(data, buff, PAGE_SIZE);
- buff += PAGE_SIZE;
- bytes -= PAGE_SIZE;
- data = squashfs_next_page(output);
- }
- squashfs_finish_page(output);
+ squashfs_buf_to_actor(stream->output, output, dest_len);
return dest_len;
}
diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c
index 934c17e..2c844d5 100644
--- a/fs/squashfs/lzo_wrapper.c
+++ b/fs/squashfs/lzo_wrapper.c
@@ -79,45 +79,19 @@
struct buffer_head **bh, int b, int offset, int length,
struct squashfs_page_actor *output)
{
- struct squashfs_lzo *stream = strm;
- void *buff = stream->input, *data;
- int avail, i, bytes = length, res;
+ int res;
size_t out_len = output->length;
+ struct squashfs_lzo *stream = strm;
- for (i = 0; i < b; i++) {
- avail = min(bytes, msblk->devblksize - offset);
- memcpy(buff, bh[i]->b_data + offset, avail);
- buff += avail;
- bytes -= avail;
- offset = 0;
- put_bh(bh[i]);
- }
-
+ squashfs_bh_to_buf(bh, b, stream->input, offset, length,
+ msblk->devblksize);
res = lzo1x_decompress_safe(stream->input, (size_t)length,
stream->output, &out_len);
if (res != LZO_E_OK)
- goto failed;
+ return -EIO;
+ squashfs_buf_to_actor(stream->output, output, out_len);
- res = bytes = (int)out_len;
- data = squashfs_first_page(output);
- buff = stream->output;
- while (data) {
- if (bytes <= PAGE_SIZE) {
- memcpy(data, buff, bytes);
- break;
- } else {
- memcpy(data, buff, PAGE_SIZE);
- buff += PAGE_SIZE;
- bytes -= PAGE_SIZE;
- data = squashfs_next_page(output);
- }
- }
- squashfs_finish_page(output);
-
- return res;
-
-failed:
- return -EIO;
+ return out_len;
}
const struct squashfs_decompressor squashfs_lzo_comp_ops = {
diff --git a/fs/squashfs/page_actor.c b/fs/squashfs/page_actor.c
index 9b7b1b6..e348f56 100644
--- a/fs/squashfs/page_actor.c
+++ b/fs/squashfs/page_actor.c
@@ -9,79 +9,11 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
+#include <linux/buffer_head.h>
#include "page_actor.h"
-/*
- * This file contains implementations of page_actor for decompressing into
- * an intermediate buffer, and for decompressing directly into the
- * page cache.
- *
- * Calling code should avoid sleeping between calls to squashfs_first_page()
- * and squashfs_finish_page().
- */
-
-/* Implementation of page_actor for decompressing into intermediate buffer */
-static void *cache_first_page(struct squashfs_page_actor *actor)
-{
- actor->next_page = 1;
- return actor->buffer[0];
-}
-
-static void *cache_next_page(struct squashfs_page_actor *actor)
-{
- if (actor->next_page == actor->pages)
- return NULL;
-
- return actor->buffer[actor->next_page++];
-}
-
-static void cache_finish_page(struct squashfs_page_actor *actor)
-{
- /* empty */
-}
-
-struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
- int pages, int length)
-{
- struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
-
- if (actor == NULL)
- return NULL;
-
- actor->length = length ? : pages * PAGE_SIZE;
- actor->buffer = buffer;
- actor->pages = pages;
- actor->next_page = 0;
- actor->squashfs_first_page = cache_first_page;
- actor->squashfs_next_page = cache_next_page;
- actor->squashfs_finish_page = cache_finish_page;
- return actor;
-}
-
-/* Implementation of page_actor for decompressing directly into page cache. */
-static void *direct_first_page(struct squashfs_page_actor *actor)
-{
- actor->next_page = 1;
- return actor->pageaddr = kmap_atomic(actor->page[0]);
-}
-
-static void *direct_next_page(struct squashfs_page_actor *actor)
-{
- if (actor->pageaddr)
- kunmap_atomic(actor->pageaddr);
-
- return actor->pageaddr = actor->next_page == actor->pages ? NULL :
- kmap_atomic(actor->page[actor->next_page++]);
-}
-
-static void direct_finish_page(struct squashfs_page_actor *actor)
-{
- if (actor->pageaddr)
- kunmap_atomic(actor->pageaddr);
-}
-
-struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page,
- int pages, int length)
+struct squashfs_page_actor *squashfs_page_actor_init(struct page **page,
+ int pages, int length, void (*release_pages)(struct page **, int, int))
{
struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
@@ -93,8 +25,129 @@
actor->pages = pages;
actor->next_page = 0;
actor->pageaddr = NULL;
- actor->squashfs_first_page = direct_first_page;
- actor->squashfs_next_page = direct_next_page;
- actor->squashfs_finish_page = direct_finish_page;
+ actor->release_pages = release_pages;
return actor;
}
+
+void squashfs_page_actor_free(struct squashfs_page_actor *actor, int error)
+{
+ if (!actor)
+ return;
+
+ if (actor->release_pages)
+ actor->release_pages(actor->page, actor->pages, error);
+ kfree(actor);
+}
+
+void squashfs_actor_to_buf(struct squashfs_page_actor *actor, void *buf,
+ int length)
+{
+ void *pageaddr;
+ int pos = 0, avail, i;
+
+ for (i = 0; i < actor->pages && pos < length; ++i) {
+ avail = min_t(int, length - pos, PAGE_SIZE);
+ if (actor->page[i]) {
+ pageaddr = kmap_atomic(actor->page[i]);
+ memcpy(buf + pos, pageaddr, avail);
+ kunmap_atomic(pageaddr);
+ }
+ pos += avail;
+ }
+}
+
+void squashfs_buf_to_actor(void *buf, struct squashfs_page_actor *actor,
+ int length)
+{
+ void *pageaddr;
+ int pos = 0, avail, i;
+
+ for (i = 0; i < actor->pages && pos < length; ++i) {
+ avail = min_t(int, length - pos, PAGE_SIZE);
+ if (actor->page[i]) {
+ pageaddr = kmap_atomic(actor->page[i]);
+ memcpy(pageaddr, buf + pos, avail);
+ kunmap_atomic(pageaddr);
+ }
+ pos += avail;
+ }
+}
+
+void squashfs_bh_to_actor(struct buffer_head **bh, int nr_buffers,
+ struct squashfs_page_actor *actor, int offset, int length, int blksz)
+{
+ void *kaddr = NULL;
+ int bytes = 0, pgoff = 0, b = 0, p = 0, avail, i;
+
+ while (bytes < length) {
+ if (actor->page[p]) {
+ kaddr = kmap_atomic(actor->page[p]);
+ while (pgoff < PAGE_SIZE && bytes < length) {
+ avail = min_t(int, blksz - offset,
+ PAGE_SIZE - pgoff);
+ memcpy(kaddr + pgoff, bh[b]->b_data + offset,
+ avail);
+ pgoff += avail;
+ bytes += avail;
+ offset = (offset + avail) % blksz;
+ if (!offset) {
+ put_bh(bh[b]);
+ ++b;
+ }
+ }
+ kunmap_atomic(kaddr);
+ pgoff = 0;
+ } else {
+ for (i = 0; i < PAGE_SIZE / blksz; ++i) {
+ if (bh[b])
+ put_bh(bh[b]);
+ ++b;
+ }
+ bytes += PAGE_SIZE;
+ }
+ ++p;
+ }
+}
+
+void squashfs_bh_to_buf(struct buffer_head **bh, int nr_buffers, void *buf,
+ int offset, int length, int blksz)
+{
+ int i, avail, bytes = 0;
+
+ for (i = 0; i < nr_buffers && bytes < length; ++i) {
+ avail = min_t(int, length - bytes, blksz - offset);
+ if (bh[i]) {
+ memcpy(buf + bytes, bh[i]->b_data + offset, avail);
+ put_bh(bh[i]);
+ }
+ bytes += avail;
+ offset = 0;
+ }
+}
+
+void free_page_array(struct page **page, int nr_pages)
+{
+ int i;
+
+ for (i = 0; i < nr_pages; ++i)
+ __free_page(page[i]);
+ kfree(page);
+}
+
+struct page **alloc_page_array(int nr_pages, int gfp_mask)
+{
+ int i;
+ struct page **page;
+
+ page = kcalloc(nr_pages, sizeof(struct page *), gfp_mask);
+ if (!page)
+ return NULL;
+ for (i = 0; i < nr_pages; ++i) {
+ page[i] = alloc_page(gfp_mask);
+ if (!page[i]) {
+ free_page_array(page, i);
+ return NULL;
+ }
+ }
+ return page;
+}
diff --git a/fs/squashfs/page_actor.h b/fs/squashfs/page_actor.h
index 98537ea..aa1ed79 100644
--- a/fs/squashfs/page_actor.h
+++ b/fs/squashfs/page_actor.h
@@ -5,77 +5,61 @@
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
+ * the COPYING file in the top-level squashfsory.
*/
-#ifndef CONFIG_SQUASHFS_FILE_DIRECT
struct squashfs_page_actor {
- void **page;
+ struct page **page;
+ void *pageaddr;
int pages;
int length;
int next_page;
+ void (*release_pages)(struct page **, int, int);
};
-static inline struct squashfs_page_actor *squashfs_page_actor_init(void **page,
- int pages, int length)
-{
- struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
+extern struct squashfs_page_actor *squashfs_page_actor_init(struct page **,
+ int, int, void (*)(struct page **, int, int));
+extern void squashfs_page_actor_free(struct squashfs_page_actor *, int);
- if (actor == NULL)
- return NULL;
+extern void squashfs_actor_to_buf(struct squashfs_page_actor *, void *, int);
+extern void squashfs_buf_to_actor(void *, struct squashfs_page_actor *, int);
+extern void squashfs_bh_to_actor(struct buffer_head **, int,
+ struct squashfs_page_actor *, int, int, int);
+extern void squashfs_bh_to_buf(struct buffer_head **, int, void *, int, int,
+ int);
- actor->length = length ? : pages * PAGE_SIZE;
- actor->page = page;
- actor->pages = pages;
- actor->next_page = 0;
- return actor;
-}
-
+/*
+ * Calling code should avoid sleeping between calls to squashfs_first_page()
+ * and squashfs_finish_page().
+ */
static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
{
actor->next_page = 1;
- return actor->page[0];
+ return actor->pageaddr = actor->page[0] ? kmap_atomic(actor->page[0])
+ : NULL;
}
static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
{
- return actor->next_page == actor->pages ? NULL :
- actor->page[actor->next_page++];
+ if (!IS_ERR_OR_NULL(actor->pageaddr))
+ kunmap_atomic(actor->pageaddr);
+
+ if (actor->next_page == actor->pages)
+ return actor->pageaddr = ERR_PTR(-ENODATA);
+
+ actor->pageaddr = actor->page[actor->next_page] ?
+ kmap_atomic(actor->page[actor->next_page]) : NULL;
+ ++actor->next_page;
+ return actor->pageaddr;
}
static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
{
- /* empty */
+ if (!IS_ERR_OR_NULL(actor->pageaddr))
+ kunmap_atomic(actor->pageaddr);
}
-#else
-struct squashfs_page_actor {
- union {
- void **buffer;
- struct page **page;
- };
- void *pageaddr;
- void *(*squashfs_first_page)(struct squashfs_page_actor *);
- void *(*squashfs_next_page)(struct squashfs_page_actor *);
- void (*squashfs_finish_page)(struct squashfs_page_actor *);
- int pages;
- int length;
- int next_page;
-};
-extern struct squashfs_page_actor *squashfs_page_actor_init(void **, int, int);
-extern struct squashfs_page_actor *squashfs_page_actor_init_special(struct page
- **, int, int);
-static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
-{
- return actor->squashfs_first_page(actor);
-}
-static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
-{
- return actor->squashfs_next_page(actor);
-}
-static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
-{
- actor->squashfs_finish_page(actor);
-}
-#endif
+extern struct page **alloc_page_array(int, int);
+extern void free_page_array(struct page **, int);
+
#endif
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
index 887d6d2..f4faab5 100644
--- a/fs/squashfs/squashfs.h
+++ b/fs/squashfs/squashfs.h
@@ -28,8 +28,12 @@
#define WARNING(s, args...) pr_warn("SQUASHFS: "s, ## args)
/* block.c */
+extern int squashfs_init_read_wq(void);
+extern void squashfs_destroy_read_wq(void);
extern int squashfs_read_data(struct super_block *, u64, int, u64 *,
struct squashfs_page_actor *);
+extern int squashfs_read_data_async(struct super_block *, u64, int, u64 *,
+ struct squashfs_page_actor *);
/* cache.c */
extern struct squashfs_cache *squashfs_cache_init(char *, int, int);
@@ -70,8 +74,9 @@
void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int,
int);
-/* file_xxx.c */
-extern int squashfs_readpage_block(struct page *, u64, int);
+/* file_direct.c */
+extern int squashfs_readpages_block(struct page *, struct list_head *,
+ unsigned int *, struct address_space *, int, u64, int);
/* id.c */
extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *);
diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h
index 1da565c..8a6995d 100644
--- a/fs/squashfs/squashfs_fs_sb.h
+++ b/fs/squashfs/squashfs_fs_sb.h
@@ -49,7 +49,7 @@
int num_waiters;
wait_queue_head_t wait_queue;
struct squashfs_cache *cache;
- void **data;
+ struct page **page;
struct squashfs_page_actor *actor;
};
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
index cf01e15..e2a0a73 100644
--- a/fs/squashfs/super.c
+++ b/fs/squashfs/super.c
@@ -444,9 +444,15 @@
if (err)
return err;
+ if (!squashfs_init_read_wq()) {
+ destroy_inodecache();
+ return -ENOMEM;
+ }
+
err = register_filesystem(&squashfs_fs_type);
if (err) {
destroy_inodecache();
+ squashfs_destroy_read_wq();
return err;
}
@@ -460,6 +466,7 @@
{
unregister_filesystem(&squashfs_fs_type);
destroy_inodecache();
+ squashfs_destroy_read_wq();
}
diff --git a/fs/squashfs/xz_wrapper.c b/fs/squashfs/xz_wrapper.c
index 6bfaef7..2f7be1f 100644
--- a/fs/squashfs/xz_wrapper.c
+++ b/fs/squashfs/xz_wrapper.c
@@ -55,7 +55,7 @@
struct comp_opts *opts;
int err = 0, n;
- opts = kmalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kmalloc(sizeof(*opts), GFP_ATOMIC);
if (opts == NULL) {
err = -ENOMEM;
goto out2;
@@ -136,6 +136,7 @@
enum xz_ret xz_err;
int avail, total = 0, k = 0;
struct squashfs_xz *stream = strm;
+ void *buf = NULL;
xz_dec_reset(stream->state);
stream->buf.in_pos = 0;
@@ -156,12 +157,20 @@
if (stream->buf.out_pos == stream->buf.out_size) {
stream->buf.out = squashfs_next_page(output);
- if (stream->buf.out != NULL) {
+ if (!IS_ERR(stream->buf.out)) {
stream->buf.out_pos = 0;
total += PAGE_SIZE;
}
}
+ if (!stream->buf.out) {
+ if (!buf) {
+ buf = kmalloc(PAGE_SIZE, GFP_ATOMIC);
+ if (!buf)
+ goto out;
+ }
+ stream->buf.out = buf;
+ }
xz_err = xz_dec_run(stream->state, &stream->buf);
if (stream->buf.in_pos == stream->buf.in_size && k < b)
@@ -173,11 +182,13 @@
if (xz_err != XZ_STREAM_END || k < b)
goto out;
+ kfree(buf);
return total + stream->buf.out_pos;
out:
for (; k < b; k++)
put_bh(bh[k]);
+ kfree(buf);
return -EIO;
}
diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c
index 2ec24d1..d917c72 100644
--- a/fs/squashfs/zlib_wrapper.c
+++ b/fs/squashfs/zlib_wrapper.c
@@ -66,6 +66,7 @@
struct buffer_head **bh, int b, int offset, int length,
struct squashfs_page_actor *output)
{
+ void *buf = NULL;
int zlib_err, zlib_init = 0, k = 0;
z_stream *stream = strm;
@@ -84,10 +85,19 @@
if (stream->avail_out == 0) {
stream->next_out = squashfs_next_page(output);
- if (stream->next_out != NULL)
+ if (!IS_ERR(stream->next_out))
stream->avail_out = PAGE_SIZE;
}
+ if (!stream->next_out) {
+ if (!buf) {
+ buf = kmalloc(PAGE_SIZE, GFP_ATOMIC);
+ if (!buf)
+ goto out;
+ }
+ stream->next_out = buf;
+ }
+
if (!zlib_init) {
zlib_err = zlib_inflateInit(stream);
if (zlib_err != Z_OK) {
@@ -115,11 +125,13 @@
if (k < b)
goto out;
+ kfree(buf);
return stream->total_out;
out:
for (; k < b; k++)
put_bh(bh[k]);
+ kfree(buf);
return -EIO;
}
diff --git a/fs/xattr.c b/fs/xattr.c
index ed8c374..932b906 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -249,7 +249,7 @@
}
memcpy(value, buffer, len);
out:
- security_release_secctx(buffer, len);
+ kfree(buffer);
out_noalloc:
return len;
}
diff --git a/fs/xfs/kmem.c b/fs/xfs/kmem.c
index 339c696..bb2beae 100644
--- a/fs/xfs/kmem.c
+++ b/fs/xfs/kmem.c
@@ -24,24 +24,6 @@
#include "kmem.h"
#include "xfs_message.h"
-/*
- * Greedy allocation. May fail and may return vmalloced memory.
- */
-void *
-kmem_zalloc_greedy(size_t *size, size_t minsize, size_t maxsize)
-{
- void *ptr;
- size_t kmsize = maxsize;
-
- while (!(ptr = vzalloc(kmsize))) {
- if ((kmsize >>= 1) <= minsize)
- kmsize = minsize;
- }
- if (ptr)
- *size = kmsize;
- return ptr;
-}
-
void *
kmem_alloc(size_t size, xfs_km_flags_t flags)
{
diff --git a/fs/xfs/kmem.h b/fs/xfs/kmem.h
index 689f746..f0fc84f 100644
--- a/fs/xfs/kmem.h
+++ b/fs/xfs/kmem.h
@@ -69,8 +69,6 @@
}
-extern void *kmem_zalloc_greedy(size_t *, size_t, size_t);
-
static inline void *
kmem_zalloc(size_t size, xfs_km_flags_t flags)
{
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index bce2e26..6c95812 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -1085,6 +1085,7 @@
int *join_flags)
{
struct inode *inode = VFS_I(ip);
+ struct super_block *sb = inode->i_sb;
int error;
*join_flags = 0;
@@ -1097,7 +1098,7 @@
if (fa->fsx_xflags & FS_XFLAG_DAX) {
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
return -EINVAL;
- if (ip->i_mount->m_sb.sb_blocksize != PAGE_SIZE)
+ if (bdev_dax_supported(sb, sb->s_blocksize) < 0)
return -EINVAL;
}
diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c
index d8a77db..26d67ce 100644
--- a/fs/xfs/xfs_itable.c
+++ b/fs/xfs/xfs_itable.c
@@ -361,7 +361,6 @@
xfs_agino_t agino; /* inode # in allocation group */
xfs_agnumber_t agno; /* allocation group number */
xfs_btree_cur_t *cur; /* btree cursor for ialloc btree */
- size_t irbsize; /* size of irec buffer in bytes */
xfs_inobt_rec_incore_t *irbuf; /* start of irec buffer */
int nirbuf; /* size of irbuf */
int ubcount; /* size of user's buffer */
@@ -388,11 +387,10 @@
*ubcountp = 0;
*done = 0;
- irbuf = kmem_zalloc_greedy(&irbsize, PAGE_SIZE, PAGE_SIZE * 4);
+ irbuf = kmem_zalloc_large(PAGE_SIZE * 4, KM_SLEEP);
if (!irbuf)
return -ENOMEM;
-
- nirbuf = irbsize / sizeof(*irbuf);
+ nirbuf = (PAGE_SIZE * 4) / sizeof(*irbuf);
/*
* Loop over the allocation groups, starting from the last
diff --git a/include/asm-generic/percpu.h b/include/asm-generic/percpu.h
index 0504ef8..976f8ac 100644
--- a/include/asm-generic/percpu.h
+++ b/include/asm-generic/percpu.h
@@ -115,15 +115,35 @@
(__ret); \
})
-#define this_cpu_generic_read(pcp) \
+#define __this_cpu_generic_read_nopreempt(pcp) \
({ \
typeof(pcp) __ret; \
preempt_disable_notrace(); \
- __ret = raw_cpu_generic_read(pcp); \
+ __ret = READ_ONCE(*raw_cpu_ptr(&(pcp))); \
preempt_enable_notrace(); \
__ret; \
})
+#define __this_cpu_generic_read_noirq(pcp) \
+({ \
+ typeof(pcp) __ret; \
+ unsigned long __flags; \
+ raw_local_irq_save(__flags); \
+ __ret = raw_cpu_generic_read(pcp); \
+ raw_local_irq_restore(__flags); \
+ __ret; \
+})
+
+#define this_cpu_generic_read(pcp) \
+({ \
+ typeof(pcp) __ret; \
+ if (__native_word(pcp)) \
+ __ret = __this_cpu_generic_read_nopreempt(pcp); \
+ else \
+ __ret = __this_cpu_generic_read_noirq(pcp); \
+ __ret; \
+})
+
#define this_cpu_generic_to_op(pcp, val, op) \
do { \
unsigned long __flags; \
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index f5678aa..0dbddb3 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -23,6 +23,8 @@
#define MIPI_DSI_MSG_USE_LPM BIT(1)
/* read mipi_dsi_msg.ctrl and unicast to only that ctrls */
#define MIPI_DSI_MSG_UNICAST BIT(2)
+/* Stack all commands until lastcommand bit and trigger all in one go */
+#define MIPI_DSI_MSG_LASTCOMMAND BIT(3)
/**
* struct mipi_dsi_msg - read/write DSI buffer
diff --git a/include/dt-bindings/soc/qcom,dcc_v2.h b/include/dt-bindings/soc/qcom,dcc_v2.h
new file mode 100644
index 0000000..fb4ed6d
--- /dev/null
+++ b/include/dt-bindings/soc/qcom,dcc_v2.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DT_BINDINGS_QCOM_DCC_V2_H
+#define __DT_BINDINGS_QCOM_DCC_V2_H
+
+#define DCC_READ 0
+#define DCC_WRITE 1
+#define DCC_LOOP 2
+
+#endif
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 9d4443f..2be99b2 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -387,6 +387,20 @@
return __audit_socketcall(nargs, args);
return 0;
}
+
+static inline int audit_socketcall_compat(int nargs, u32 *args)
+{
+ unsigned long a[AUDITSC_ARGS];
+ int i;
+
+ if (audit_dummy_context())
+ return 0;
+
+ for (i = 0; i < nargs; i++)
+ a[i] = (unsigned long)args[i];
+ return __audit_socketcall(nargs, a);
+}
+
static inline int audit_sockaddr(int len, void *addr)
{
if (unlikely(!audit_dummy_context()))
@@ -513,6 +527,12 @@
{
return 0;
}
+
+static inline int audit_socketcall_compat(int nargs, u32 *args)
+{
+ return 0;
+}
+
static inline void audit_fd_pair(int fd1, int fd2)
{ }
static inline int audit_sockaddr(int len, void *addr)
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index ebbacd1..447a915 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -226,6 +226,7 @@
loff_t, unsigned, unsigned,
struct page *, void *);
void page_zero_new_buffers(struct page *page, unsigned from, unsigned to);
+void clean_page_buffers(struct page *page);
int cont_write_begin(struct file *, struct address_space *, loff_t,
unsigned, unsigned, struct page **, void **,
get_block_t *, loff_t *);
diff --git a/include/linux/ccp.h b/include/linux/ccp.h
index edc5d04..1cfe5ef 100644
--- a/include/linux/ccp.h
+++ b/include/linux/ccp.h
@@ -1,7 +1,7 @@
/*
* AMD Cryptographic Coprocessor (CCP) driver
*
- * Copyright (C) 2013,2016 Advanced Micro Devices, Inc.
+ * Copyright (C) 2013,2017 Advanced Micro Devices, Inc.
*
* Author: Tom Lendacky <thomas.lendacky@amd.com>
* Author: Gary R Hook <gary.hook@amd.com>
@@ -222,6 +222,7 @@
* AES operation the new IV overwrites the old IV.
*/
struct ccp_xts_aes_engine {
+ enum ccp_aes_type type;
enum ccp_aes_action action;
enum ccp_xts_aes_unit_size unit_size;
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index e0aa720..3484287 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -28,21 +28,6 @@
struct device dev;
};
-struct cpu_pstate_pwr {
- unsigned int freq;
- uint32_t power;
-};
-
-struct cpu_pwr_stats {
- int cpu;
- long temp;
- struct cpu_pstate_pwr *ptable;
- bool throttling;
- int len;
-};
-
-extern struct cpu_pwr_stats *get_cpu_pwr_stats(void);
-
extern void boot_cpu_init(void);
extern void boot_cpu_state_init(void);
diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h
index cd32a49..d807fa9 100644
--- a/include/linux/cpuset.h
+++ b/include/linux/cpuset.h
@@ -55,7 +55,9 @@
extern int cpuset_init(void);
extern void cpuset_init_smp(void);
+extern void cpuset_force_rebuild(void);
extern void cpuset_update_active_cpus(bool cpu_online);
+extern void cpuset_wait_for_hotplug(void);
extern void cpuset_cpus_allowed(struct task_struct *p, struct cpumask *mask);
extern void cpuset_cpus_allowed_fallback(struct task_struct *p);
extern nodemask_t cpuset_mems_allowed(struct task_struct *p);
@@ -168,11 +170,15 @@
static inline int cpuset_init(void) { return 0; }
static inline void cpuset_init_smp(void) {}
+static inline void cpuset_force_rebuild(void) { }
+
static inline void cpuset_update_active_cpus(bool cpu_online)
{
partition_sched_domains(1, NULL, NULL);
}
+static inline void cpuset_wait_for_hotplug(void) { }
+
static inline void cpuset_cpus_allowed(struct task_struct *p,
struct cpumask *mask)
{
diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h
index e7fdec4..6cc48ac 100644
--- a/include/linux/iio/adc/ad_sigma_delta.h
+++ b/include/linux/iio/adc/ad_sigma_delta.h
@@ -111,6 +111,9 @@
int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg,
unsigned int size, unsigned int *val);
+int ad_sd_reset(struct ad_sigma_delta *sigma_delta,
+ unsigned int reset_length);
+
int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, int *val);
int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta,
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 3d9e6b8..8f34120 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -144,7 +144,7 @@
DOMAIN_ATTR_PAGE_TABLE_FORCE_COHERENT,
DOMAIN_ATTR_CB_STALL_DISABLE,
DOMAIN_ATTR_UPSTREAM_IOVA_ALLOCATOR,
- DOMAIN_ATTR_QCOM_MMU500_ERRATA_MIN_ALIGN,
+ DOMAIN_ATTR_MMU500_ERRATA_MIN_ALIGN,
DOMAIN_ATTR_MAX,
};
diff --git a/include/linux/ipa.h b/include/linux/ipa.h
index f8e7e8c..dd6849d 100644
--- a/include/linux/ipa.h
+++ b/include/linux/ipa.h
@@ -40,8 +40,8 @@
};
/**
-* enum ipa_ipv6ct_en_type - IPv6CT setting type in IPA end-point
-*/
+ * enum ipa_ipv6ct_en_type - IPv6CT setting type in IPA end-point
+ */
enum ipa_ipv6ct_en_type {
IPA_BYPASS_IPV6CT,
IPA_ENABLE_IPV6CT,
@@ -130,7 +130,7 @@
* struct ipa_ep_cfg_conn_track - IPv6 Connection tracking configuration in
* IPA end-point
* @conn_track_en: Defines speculative conn_track action, means if specific
- * pipe needs to have UL/DL IPv6 Connection Tracking or Bybass
+ * pipe needs to have UL/DL IPv6 Connection Tracking or Bypass
* IPv6 Connection Tracking. 0: Bypass IPv6 Connection Tracking
* 1: IPv6 UL/DL Connection Tracking.
* Valid for Input Pipes only (IPA consumer)
@@ -407,8 +407,8 @@
/**
* struct ipa_ep_cfg - configuration of IPA end-point
- * @nat: NAT parmeters
- * @conn_track: IPv6CT parmeters
+ * @nat: NAT parameters
+ * @conn_track: IPv6CT parameters
* @hdr: Header parameters
* @hdr_ext: Extended header parameters
* @mode: Mode parameters
@@ -1165,6 +1165,16 @@
int ee;
};
+/**
+ * struct ipa_tz_unlock_reg_info - Used in order unlock regions of memory by TZ
+ * @reg_addr - Physical address of the start of the region
+ * @size - Size of the region in bytes
+ */
+struct ipa_tz_unlock_reg_info {
+ u64 reg_addr;
+ u64 size;
+};
+
#if defined CONFIG_IPA || defined CONFIG_IPA3
/*
@@ -1282,15 +1292,24 @@
int ipa_reset_flt(enum ipa_ip_type ip);
/*
- * NAT
+ * NAT\IPv6CT
*/
-int allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem);
+int ipa_allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem);
+int ipa_allocate_nat_table(struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc);
+int ipa_allocate_ipv6ct_table(
+ struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc);
int ipa_nat_init_cmd(struct ipa_ioc_v4_nat_init *init);
+int ipa_ipv6ct_init_cmd(struct ipa_ioc_ipv6ct_init *init);
int ipa_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma);
+int ipa_table_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma);
int ipa_nat_del_cmd(struct ipa_ioc_v4_nat_del *del);
+int ipa_del_nat_table(struct ipa_ioc_nat_ipv6ct_table_del *del);
+int ipa_del_ipv6ct_table(struct ipa_ioc_nat_ipv6ct_table_del *del);
+
+int ipa_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn);
/*
* Messaging
@@ -1530,6 +1549,21 @@
int ipa_register_ipa_ready_cb(void (*ipa_ready_cb)(void *user_data),
void *user_data);
+/**
+ * ipa_tz_unlock_reg - Unlocks memory regions so that they become accessible
+ * from AP.
+ * @reg_info - Pointer to array of memory regions to unlock
+ * @num_regs - Number of elements in the array
+ *
+ * Converts the input array of regions to a struct that TZ understands and
+ * issues an SCM call.
+ * Also flushes the memory cache to DDR in order to make sure that TZ sees the
+ * correct data structure.
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_tz_unlock_reg(struct ipa_tz_unlock_reg_info *reg_info, u16 num_regs);
+
#else /* (CONFIG_IPA || CONFIG_IPA3) */
/*
@@ -1776,29 +1810,64 @@
/*
* NAT
*/
-static inline int allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem)
+static inline int ipa_allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem)
{
return -EPERM;
}
+static inline int ipa_allocate_nat_table(
+ struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc)
+{
+ return -EPERM;
+}
+
+static inline int ipa_allocate_ipv6ct_table(
+ struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc)
+{
+ return -EPERM;
+}
static inline int ipa_nat_init_cmd(struct ipa_ioc_v4_nat_init *init)
{
return -EPERM;
}
+static inline int ipa_ipv6ct_init_cmd(struct ipa_ioc_ipv6ct_init *init)
+{
+ return -EPERM;
+}
static inline int ipa_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
{
return -EPERM;
}
+static inline int ipa_table_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
+{
+ return -EPERM;
+}
static inline int ipa_nat_del_cmd(struct ipa_ioc_v4_nat_del *del)
{
return -EPERM;
}
+static inline int ipa_del_nat_table(struct ipa_ioc_nat_ipv6ct_table_del *del)
+{
+ return -EPERM;
+}
+
+static inline int ipa_del_ipv6ct_table(
+ struct ipa_ioc_nat_ipv6ct_table_del *del)
+{
+ return -EPERM;
+}
+
+static inline int ipa_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn)
+{
+ return -EPERM;
+}
+
/*
* Messaging
*/
@@ -2276,6 +2345,12 @@
return -EPERM;
}
+static inline int ipa_tz_unlock_reg(struct ipa_tz_unlock_reg_info *reg_info,
+ u16 num_regs)
+{
+ return -EPERM;
+}
+
#endif /* (CONFIG_IPA || CONFIG_IPA3) */
#endif /* _IPA_H_ */
diff --git a/include/linux/key.h b/include/linux/key.h
index 7229147..6a54472 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -176,6 +176,7 @@
#define KEY_FLAG_BUILTIN 8 /* set if key is built in to the kernel */
#define KEY_FLAG_ROOT_CAN_INVAL 9 /* set if key can be invalidated by root without permission */
#define KEY_FLAG_KEEP 10 /* set if key should not be removed */
+#define KEY_FLAG_UID_KEYRING 11 /* set if key is a user or user session keyring */
/* the key type and key description string
* - the desc is used to match a key against search criteria
@@ -235,6 +236,7 @@
#define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */
#define KEY_ALLOC_BUILT_IN 0x0004 /* Key is built into kernel */
#define KEY_ALLOC_BYPASS_RESTRICTION 0x0008 /* Override the check on restricted keyrings */
+#define KEY_ALLOC_UID_KEYRING 0x0010 /* allocating a user or user session keyring */
extern void key_revoke(struct key *key);
extern void key_invalidate(struct key *key);
diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h
index d0a69e7..f563bcf 100644
--- a/include/linux/mmc/sdio_func.h
+++ b/include/linux/mmc/sdio_func.h
@@ -61,7 +61,7 @@
unsigned int state; /* function state */
#define SDIO_STATE_PRESENT (1<<0) /* present in sysfs */
- u8 tmpbuf[4]; /* DMA:able scratch buffer */
+ u8 *tmpbuf; /* DMA:able scratch buffer */
unsigned num_info; /* number of info strings */
const char **info; /* info strings */
diff --git a/include/linux/mmu_notifier.h b/include/linux/mmu_notifier.h
index a1a210d..25c0dc3 100644
--- a/include/linux/mmu_notifier.h
+++ b/include/linux/mmu_notifier.h
@@ -419,6 +419,11 @@
#else /* CONFIG_MMU_NOTIFIER */
+static inline int mm_has_notifiers(struct mm_struct *mm)
+{
+ return 0;
+}
+
static inline void mmu_notifier_release(struct mm_struct *mm)
{
}
diff --git a/include/linux/msm_drm_notify.h b/include/linux/msm_drm_notify.h
new file mode 100644
index 0000000..924ba85
--- /dev/null
+++ b/include/linux/msm_drm_notify.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MSM_DRM_NOTIFY_H_
+#define _MSM_DRM_NOTIFY_H_
+
+#include <linux/notifier.h>
+
+/* A hardware display blank change occurred */
+#define MSM_DRM_EVENT_BLANK 0x01
+/* A hardware display blank early change occurred */
+#define MSM_DRM_EARLY_EVENT_BLANK 0x02
+
+enum {
+ /* panel: power on */
+ MSM_DRM_BLANK_UNBLANK,
+ /* panel: power off */
+ MSM_DRM_BLANK_POWERDOWN,
+};
+
+enum msm_drm_display_id {
+ /* primary display */
+ MSM_DRM_PRIMARY_DISPLAY,
+ /* external display */
+ MSM_DRM_EXTERNAL_DISPLAY,
+ MSM_DRM_DISPLAY_MAX
+};
+
+struct msm_drm_notifier {
+ enum msm_drm_display_id id;
+ void *data;
+};
+
+int msm_drm_register_client(struct notifier_block *nb);
+int msm_drm_unregister_client(struct notifier_block *nb);
+#endif
diff --git a/include/linux/msm_gsi.h b/include/linux/msm_gsi.h
index ebca446..6e0b439 100644
--- a/include/linux/msm_gsi.h
+++ b/include/linux/msm_gsi.h
@@ -82,6 +82,8 @@
* @irq: IRQ number
* @phys_addr: physical address of GSI block
* @size: register size of GSI block
+ * @mhi_er_id_limits_valid: valid flag for mhi_er_id_limits
+ * @mhi_er_id_limits: MHI event ring start and end ids
* @notify_cb: general notification callback
* @req_clk_cb: callback to request peripheral clock
* granted should be set to true if request is completed
@@ -105,6 +107,8 @@
unsigned int irq;
phys_addr_t phys_addr;
unsigned long size;
+ bool mhi_er_id_limits_valid;
+ uint32_t mhi_er_id_limits[2];
void (*notify_cb)(struct gsi_per_notify *notify);
void (*req_clk_cb)(void *user_data, bool *granted);
int (*rel_clk_cb)(void *user_data);
diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h
index 77a46bd..fc02ece 100644
--- a/include/linux/qcom-geni-se.h
+++ b/include/linux/qcom-geni-se.h
@@ -547,6 +547,24 @@
bool msb_to_lsb);
/**
+ * se_geni_clks_off() - Turn off clocks associated with the serial
+ * engine
+ * @rsc: Handle to resources associated with the serial engine.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int se_geni_clks_off(struct se_geni_rsc *rsc);
+
+/**
+ * se_geni_clks_on() - Turn on clocks associated with the serial
+ * engine
+ * @rsc: Handle to resources associated with the serial engine.
+ *
+ * Return: 0 on success, standard Linux error codes on failure/error.
+ */
+int se_geni_clks_on(struct se_geni_rsc *rsc);
+
+/**
* se_geni_resources_off() - Turn off resources associated with the serial
* engine
* @rsc: Handle to resources associated with the serial engine.
@@ -842,6 +860,16 @@
{
}
+static inline int se_geni_clks_on(struct se_geni_rsc *rsc)
+{
+ return -ENXIO;
+}
+
+static inline int se_geni_clks_off(struct se_geni_rsc *rsc)
+{
+ return -ENXIO;
+}
+
static inline int se_geni_resources_on(struct se_geni_rsc *rsc)
{
return -ENXIO;
diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h
index 0e4586f..3e060d9 100644
--- a/include/linux/qpnp/qpnp-adc.h
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -968,6 +968,7 @@
* @PMIC_THERM inputs the units in millidegC.
*/
struct qpnp_adc_tm_btm_param {
+ uint32_t full_scale_code;
int32_t high_temp;
int32_t low_temp;
int32_t high_thr;
@@ -1026,13 +1027,13 @@
/**
* struct qpnp_adc_properties - Represent the ADC properties.
* @adc_reference: Reference voltage for QPNP ADC.
- * @bitresolution: ADC bit resolution for QPNP ADC.
+ * @full_scale_code: Full scale value with intrinsic offset removed.
* @biploar: Polarity for QPNP ADC.
* @adc_hc: Represents using HC variant of the ADC controller.
*/
struct qpnp_adc_properties {
uint32_t adc_vdd_reference;
- uint32_t bitresolution;
+ uint32_t full_scale_code;
bool bipolar;
bool adc_hc;
};
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 67860f3..b7ff73d 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1946,6 +1946,7 @@
#ifdef CONFIG_DETECT_HUNG_TASK
/* hung task detection */
unsigned long last_switch_count;
+ bool hang_detection_enabled;
#endif
/* filesystem information */
struct fs_struct *fs;
diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h
index 3597d55..12bd032 100644
--- a/include/linux/sched/sysctl.h
+++ b/include/linux/sched/sysctl.h
@@ -6,6 +6,7 @@
extern unsigned int sysctl_hung_task_panic;
extern unsigned long sysctl_hung_task_timeout_secs;
extern int sysctl_hung_task_warnings;
+extern int sysctl_hung_task_selective_monitoring;
extern int proc_dohung_task_timeout_secs(struct ctl_table *table, int write,
void __user *buffer,
size_t *lenp, loff_t *ppos);
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index 7321ae9..102c84d 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -470,6 +470,7 @@
struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int,
struct svc_serv_ops *);
int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int);
+int svc_set_num_threads_sync(struct svc_serv *, struct svc_pool *, int);
int svc_pool_stats_open(struct svc_serv *serv, struct file *file);
void svc_destroy(struct svc_serv *);
void svc_shutdown_net(struct svc_serv *, struct net *);
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index 41d81fb..751a510 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -275,6 +275,7 @@
int perf_refcount;
struct hlist_head __percpu *perf_events;
struct bpf_prog *prog;
+ struct perf_event *bpf_prog_owner;
int (*perf_perm)(struct trace_event_call *,
struct perf_event *);
diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h
index c28dd52..d43837f 100644
--- a/include/linux/tty_flip.h
+++ b/include/linux/tty_flip.h
@@ -12,6 +12,7 @@
unsigned char **chars, size_t size);
extern void tty_flip_buffer_push(struct tty_port *port);
void tty_schedule_flip(struct tty_port *port);
+int __tty_insert_flip_char(struct tty_port *port, unsigned char ch, char flag);
static inline int tty_insert_flip_char(struct tty_port *port,
unsigned char ch, char flag)
@@ -26,7 +27,7 @@
*char_buf_ptr(tb, tb->used++) = ch;
return 1;
}
- return tty_insert_flip_string_flags(port, &ch, &flag, 1);
+ return __tty_insert_flip_char(port, ch, flag);
}
static inline int tty_insert_flip_string(struct tty_port *port,
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index e2dba93..2c7d876 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -902,21 +902,10 @@
unsigned long jiffies;
};
/* NB: vif can be NULL for injected frames */
- union {
- /* NB: vif can be NULL for injected frames */
- struct ieee80211_vif *vif;
-
- /* When packets are enqueued on txq it's easy
- * to re-construct the vif pointer. There's no
- * more space in tx_info so it can be used to
- * store the necessary enqueue time for packet
- * sojourn time computation.
- */
- codel_time_t enqueue_time;
- };
+ struct ieee80211_vif *vif;
struct ieee80211_key_conf *hw_key;
u32 flags;
- /* 4 bytes free */
+ codel_time_t enqueue_time;
} control;
struct {
u64 cookie;
diff --git a/include/net/netfilter/nf_tables_ipv6.h b/include/net/netfilter/nf_tables_ipv6.h
index d150b50..97983d1 100644
--- a/include/net/netfilter/nf_tables_ipv6.h
+++ b/include/net/netfilter/nf_tables_ipv6.h
@@ -9,12 +9,13 @@
struct sk_buff *skb,
const struct nf_hook_state *state)
{
+ unsigned int flags = IP6_FH_F_AUTH;
int protohdr, thoff = 0;
unsigned short frag_off;
nft_set_pktinfo(pkt, skb, state);
- protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, NULL);
+ protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags);
if (protohdr < 0) {
nft_set_pktinfo_proto_unspec(pkt, skb);
return;
@@ -32,6 +33,7 @@
const struct nf_hook_state *state)
{
#if IS_ENABLED(CONFIG_IPV6)
+ unsigned int flags = IP6_FH_F_AUTH;
struct ipv6hdr *ip6h, _ip6h;
unsigned int thoff = 0;
unsigned short frag_off;
@@ -50,7 +52,7 @@
if (pkt_len + sizeof(*ip6h) > skb->len)
return -1;
- protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, NULL);
+ protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags);
if (protohdr < 0)
return -1;
diff --git a/include/net/netlink.h b/include/net/netlink.h
index 254a0fc..42adccd 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -756,7 +756,10 @@
*/
static inline int nla_put_u8(struct sk_buff *skb, int attrtype, u8 value)
{
- return nla_put(skb, attrtype, sizeof(u8), &value);
+ /* temporary variables to work around GCC PR81715 with asan-stack=1 */
+ u8 tmp = value;
+
+ return nla_put(skb, attrtype, sizeof(u8), &tmp);
}
/**
@@ -767,7 +770,9 @@
*/
static inline int nla_put_u16(struct sk_buff *skb, int attrtype, u16 value)
{
- return nla_put(skb, attrtype, sizeof(u16), &value);
+ u16 tmp = value;
+
+ return nla_put(skb, attrtype, sizeof(u16), &tmp);
}
/**
@@ -778,7 +783,9 @@
*/
static inline int nla_put_be16(struct sk_buff *skb, int attrtype, __be16 value)
{
- return nla_put(skb, attrtype, sizeof(__be16), &value);
+ __be16 tmp = value;
+
+ return nla_put(skb, attrtype, sizeof(__be16), &tmp);
}
/**
@@ -789,7 +796,9 @@
*/
static inline int nla_put_net16(struct sk_buff *skb, int attrtype, __be16 value)
{
- return nla_put_be16(skb, attrtype | NLA_F_NET_BYTEORDER, value);
+ __be16 tmp = value;
+
+ return nla_put_be16(skb, attrtype | NLA_F_NET_BYTEORDER, tmp);
}
/**
@@ -800,7 +809,9 @@
*/
static inline int nla_put_le16(struct sk_buff *skb, int attrtype, __le16 value)
{
- return nla_put(skb, attrtype, sizeof(__le16), &value);
+ __le16 tmp = value;
+
+ return nla_put(skb, attrtype, sizeof(__le16), &tmp);
}
/**
@@ -811,7 +822,9 @@
*/
static inline int nla_put_u32(struct sk_buff *skb, int attrtype, u32 value)
{
- return nla_put(skb, attrtype, sizeof(u32), &value);
+ u32 tmp = value;
+
+ return nla_put(skb, attrtype, sizeof(u32), &tmp);
}
/**
@@ -822,7 +835,9 @@
*/
static inline int nla_put_be32(struct sk_buff *skb, int attrtype, __be32 value)
{
- return nla_put(skb, attrtype, sizeof(__be32), &value);
+ __be32 tmp = value;
+
+ return nla_put(skb, attrtype, sizeof(__be32), &tmp);
}
/**
@@ -833,7 +848,9 @@
*/
static inline int nla_put_net32(struct sk_buff *skb, int attrtype, __be32 value)
{
- return nla_put_be32(skb, attrtype | NLA_F_NET_BYTEORDER, value);
+ __be32 tmp = value;
+
+ return nla_put_be32(skb, attrtype | NLA_F_NET_BYTEORDER, tmp);
}
/**
@@ -844,7 +861,9 @@
*/
static inline int nla_put_le32(struct sk_buff *skb, int attrtype, __le32 value)
{
- return nla_put(skb, attrtype, sizeof(__le32), &value);
+ __le32 tmp = value;
+
+ return nla_put(skb, attrtype, sizeof(__le32), &tmp);
}
/**
@@ -857,7 +876,9 @@
static inline int nla_put_u64_64bit(struct sk_buff *skb, int attrtype,
u64 value, int padattr)
{
- return nla_put_64bit(skb, attrtype, sizeof(u64), &value, padattr);
+ u64 tmp = value;
+
+ return nla_put_64bit(skb, attrtype, sizeof(u64), &tmp, padattr);
}
/**
@@ -870,7 +891,9 @@
static inline int nla_put_be64(struct sk_buff *skb, int attrtype, __be64 value,
int padattr)
{
- return nla_put_64bit(skb, attrtype, sizeof(__be64), &value, padattr);
+ __be64 tmp = value;
+
+ return nla_put_64bit(skb, attrtype, sizeof(__be64), &tmp, padattr);
}
/**
@@ -883,7 +906,9 @@
static inline int nla_put_net64(struct sk_buff *skb, int attrtype, __be64 value,
int padattr)
{
- return nla_put_be64(skb, attrtype | NLA_F_NET_BYTEORDER, value,
+ __be64 tmp = value;
+
+ return nla_put_be64(skb, attrtype | NLA_F_NET_BYTEORDER, tmp,
padattr);
}
@@ -897,7 +922,9 @@
static inline int nla_put_le64(struct sk_buff *skb, int attrtype, __le64 value,
int padattr)
{
- return nla_put_64bit(skb, attrtype, sizeof(__le64), &value, padattr);
+ __le64 tmp = value;
+
+ return nla_put_64bit(skb, attrtype, sizeof(__le64), &tmp, padattr);
}
/**
@@ -908,7 +935,9 @@
*/
static inline int nla_put_s8(struct sk_buff *skb, int attrtype, s8 value)
{
- return nla_put(skb, attrtype, sizeof(s8), &value);
+ s8 tmp = value;
+
+ return nla_put(skb, attrtype, sizeof(s8), &tmp);
}
/**
@@ -919,7 +948,9 @@
*/
static inline int nla_put_s16(struct sk_buff *skb, int attrtype, s16 value)
{
- return nla_put(skb, attrtype, sizeof(s16), &value);
+ s16 tmp = value;
+
+ return nla_put(skb, attrtype, sizeof(s16), &tmp);
}
/**
@@ -930,7 +961,9 @@
*/
static inline int nla_put_s32(struct sk_buff *skb, int attrtype, s32 value)
{
- return nla_put(skb, attrtype, sizeof(s32), &value);
+ s32 tmp = value;
+
+ return nla_put(skb, attrtype, sizeof(s32), &tmp);
}
/**
@@ -943,7 +976,9 @@
static inline int nla_put_s64(struct sk_buff *skb, int attrtype, s64 value,
int padattr)
{
- return nla_put_64bit(skb, attrtype, sizeof(s64), &value, padattr);
+ s64 tmp = value;
+
+ return nla_put_64bit(skb, attrtype, sizeof(s64), &tmp, padattr);
}
/**
@@ -993,7 +1028,9 @@
static inline int nla_put_in_addr(struct sk_buff *skb, int attrtype,
__be32 addr)
{
- return nla_put_be32(skb, attrtype, addr);
+ __be32 tmp = addr;
+
+ return nla_put_be32(skb, attrtype, tmp);
}
/**
diff --git a/include/net/sctp/ulpevent.h b/include/net/sctp/ulpevent.h
index 2c098cd..231df4f 100644
--- a/include/net/sctp/ulpevent.h
+++ b/include/net/sctp/ulpevent.h
@@ -141,8 +141,12 @@
static inline int sctp_ulpevent_type_enabled(__u16 sn_type,
struct sctp_event_subscribe *mask)
{
+ int offset = sn_type - SCTP_SN_TYPE_BASE;
char *amask = (char *) mask;
- return amask[sn_type - SCTP_SN_TYPE_BASE];
+
+ if (offset >= sizeof(struct sctp_event_subscribe))
+ return 0;
+ return amask[offset];
}
/* Given an event subscription, is this event enabled? */
diff --git a/include/soc/qcom/msm-core.h b/include/soc/qcom/msm-core.h
deleted file mode 100644
index f1c06a6..0000000
--- a/include/soc/qcom/msm-core.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (c) 2014-2015,2017 The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef __ARCH_ARM_MACH_MSM_CORE_H
-#define __ARCH_ARM_MACH_MSM_CORE_H
-#ifdef CONFIG_APSS_CORE_EA
-void set_cpu_throttled(struct cpumask *mask, bool throttling);
-struct blocking_notifier_head *get_power_update_notifier(void);
-void trigger_cpu_pwr_stats_calc(void);
-struct cpu_pwr_stats *get_cpu_pwr_stats(void);
-#else
-static inline void set_cpu_throttled(struct cpumask *mask, bool throttling) {}
-struct blocking_notifier_head *get_power_update_notifier(void) {return NULL; }
-static inline void trigger_cpu_pwr_stats_calc(void) {}
-struct cpu_pwr_stats *get_cpu_pwr_stats(void) {return NULL; }
-#endif
-#endif
diff --git a/include/sound/seq_virmidi.h b/include/sound/seq_virmidi.h
index a03acd0..695257a 100644
--- a/include/sound/seq_virmidi.h
+++ b/include/sound/seq_virmidi.h
@@ -60,6 +60,7 @@
int port; /* created/attached port */
unsigned int flags; /* SNDRV_VIRMIDI_* */
rwlock_t filelist_lock;
+ struct rw_semaphore filelist_sem;
struct list_head filelist;
};
diff --git a/include/trace/events/trace_msm_core.h b/include/trace/events/trace_msm_core.h
deleted file mode 100644
index 45747f7..0000000
--- a/include/trace/events/trace_msm_core.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/* Copyright (c) 2014,2016 The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM msm_core
-
-#if !defined(_TRACE_MSM_CORE_H) || defined(TRACE_HEADER_MULTI_READ)
-#define _TRACE_MSM_CORE_H
-
-#include <linux/tracepoint.h>
-#include <linux/thermal.h>
-
-TRACE_EVENT(cpu_stats,
-
- TP_PROTO(unsigned int cpu, long temp,
- uint64_t min_power, uint64_t max_power),
-
- TP_ARGS(cpu, temp, min_power, max_power),
-
- TP_STRUCT__entry(
- __field(unsigned int, cpu)
- __field(long, temp)
- __field(uint64_t, min_power)
- __field(uint64_t, max_power)
- ),
-
- TP_fast_assign(
- __entry->cpu = cpu;
- __entry->temp = temp;
- __entry->min_power = min_power;
- __entry->max_power = max_power;
- ),
-
- TP_printk("Cpu%d: temp:%ld power@minfreq:%llu power@maxfreq:%llu",
- __entry->cpu, __entry->temp, __entry->min_power,
- __entry->max_power)
-);
-
-TRACE_EVENT(temp_threshold,
-
- TP_PROTO(unsigned int cpu, long temp,
- long hi_thresh, long low_thresh),
-
- TP_ARGS(cpu, temp, hi_thresh, low_thresh),
-
- TP_STRUCT__entry(
- __field(unsigned int, cpu)
- __field(long, temp)
- __field(long, hi_thresh)
- __field(long, low_thresh)
- ),
-
- TP_fast_assign(
- __entry->cpu = cpu;
- __entry->temp = temp;
- __entry->hi_thresh = hi_thresh;
- __entry->low_thresh = low_thresh;
- ),
-
- TP_printk("Cpu%d: temp:%ld hi_thresh:%ld low_thresh:%ld",
- __entry->cpu, __entry->temp, __entry->hi_thresh,
- __entry->low_thresh)
-);
-
-TRACE_EVENT(temp_notification,
-
- TP_PROTO(unsigned int sensor_id, enum thermal_trip_type type,
- int temp, int prev_temp),
-
- TP_ARGS(sensor_id, type, temp, prev_temp),
-
- TP_STRUCT__entry(
- __field(unsigned int, sensor_id)
- __field(enum thermal_trip_type, type)
- __field(int, temp)
- __field(int, prev_temp)
- ),
-
- TP_fast_assign(
- __entry->sensor_id = sensor_id;
- __entry->type = type;
- __entry->temp = temp;
- __entry->prev_temp = prev_temp;
- ),
-
- TP_printk("Sensor_id%d: %s threshold triggered temp:%d(previous:%d)",
- __entry->sensor_id,
- __entry->type == THERMAL_TRIP_CONFIGURABLE_HI ? "High" : "Low",
- __entry->temp, __entry->prev_temp)
-);
-
-#endif
-#define TRACE_INCLUDE_FILE trace_msm_core
-#include <trace/define_trace.h>
diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
index 7846ec8..e6ff4cc 100644
--- a/include/uapi/drm/drm_fourcc.h
+++ b/include/uapi/drm/drm_fourcc.h
@@ -154,6 +154,7 @@
/* Vendor Ids: */
#define DRM_FORMAT_MOD_NONE 0
+#define DRM_FORMAT_MOD_VENDOR_NONE 0
#define DRM_FORMAT_MOD_VENDOR_INTEL 0x01
#define DRM_FORMAT_MOD_VENDOR_AMD 0x02
#define DRM_FORMAT_MOD_VENDOR_NV 0x03
diff --git a/include/uapi/linux/esoc_ctrl.h b/include/uapi/linux/esoc_ctrl.h
index 4201c95..45f3222 100644
--- a/include/uapi/linux/esoc_ctrl.h
+++ b/include/uapi/linux/esoc_ctrl.h
@@ -9,6 +9,7 @@
#define ESOC_WAIT_FOR_REQ _IOR(ESOC_CODE, 2, unsigned int)
#define ESOC_NOTIFY _IOW(ESOC_CODE, 3, unsigned int)
#define ESOC_GET_STATUS _IOR(ESOC_CODE, 4, unsigned int)
+#define ESOC_GET_ERR_FATAL _IOR(ESOC_CODE, 5, unsigned int)
#define ESOC_WAIT_FOR_CRASH _IOR(ESOC_CODE, 6, unsigned int)
#define ESOC_REG_REQ_ENG _IO(ESOC_CODE, 7)
#define ESOC_REG_CMD_ENG _IO(ESOC_CODE, 8)
@@ -17,6 +18,7 @@
#define HSIC "HSIC"
#define HSICPCIe "HSIC+PCIe"
#define PCIe "PCIe"
+#define ESOC_REQ_SEND_SHUTDOWN ESOC_REQ_SEND_SHUTDOWN
enum esoc_evt {
ESOC_RUN_STATE = 0x1,
@@ -57,6 +59,7 @@
ESOC_REQ_IMG = 1,
ESOC_REQ_DEBUG,
ESOC_REQ_SHUTDOWN,
+ ESOC_REQ_SEND_SHUTDOWN,
};
#ifdef __KERNEL__
diff --git a/include/uapi/linux/mroute6.h b/include/uapi/linux/mroute6.h
index 5062fb5..ed57211 100644
--- a/include/uapi/linux/mroute6.h
+++ b/include/uapi/linux/mroute6.h
@@ -4,6 +4,7 @@
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/sockios.h>
+#include <linux/in6.h> /* For struct sockaddr_in6. */
/*
* Based on the MROUTING 3.5 defines primarily to keep
diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h
index 51c0165..97eca0a 100644
--- a/include/uapi/linux/msm_ipa.h
+++ b/include/uapi/linux/msm_ipa.h
@@ -16,65 +16,85 @@
#define IPA_IOC_MAGIC 0xCF
/**
+ * IPA device full path
+ */
+#define IPA_DEV_NAME "/dev/ipa"
+
+/**
+ * IPA NAT table character device name
+ */
+#define IPA_NAT_DEV_NAME "ipaNatTable"
+
+/**
+ * IPA IPv6CT table character device name
+ */
+#define IPA_IPV6CT_DEV_NAME "ipaIpv6CTTable"
+
+ /**
* name of the default routing tables for v4 and v6
*/
#define IPA_DFLT_RT_TBL_NAME "ipa_dflt_rt"
/**
- * the commands supported by IPA driver
+ * commands supported by IPA driver
*/
-#define IPA_IOCTL_ADD_HDR 0
-#define IPA_IOCTL_DEL_HDR 1
-#define IPA_IOCTL_ADD_RT_RULE 2
-#define IPA_IOCTL_DEL_RT_RULE 3
-#define IPA_IOCTL_ADD_FLT_RULE 4
-#define IPA_IOCTL_DEL_FLT_RULE 5
-#define IPA_IOCTL_COMMIT_HDR 6
-#define IPA_IOCTL_RESET_HDR 7
-#define IPA_IOCTL_COMMIT_RT 8
-#define IPA_IOCTL_RESET_RT 9
-#define IPA_IOCTL_COMMIT_FLT 10
-#define IPA_IOCTL_RESET_FLT 11
-#define IPA_IOCTL_DUMP 12
-#define IPA_IOCTL_GET_RT_TBL 13
-#define IPA_IOCTL_PUT_RT_TBL 14
-#define IPA_IOCTL_COPY_HDR 15
-#define IPA_IOCTL_QUERY_INTF 16
-#define IPA_IOCTL_QUERY_INTF_TX_PROPS 17
-#define IPA_IOCTL_QUERY_INTF_RX_PROPS 18
-#define IPA_IOCTL_GET_HDR 19
-#define IPA_IOCTL_PUT_HDR 20
-#define IPA_IOCTL_SET_FLT 21
-#define IPA_IOCTL_ALLOC_NAT_MEM 22
-#define IPA_IOCTL_V4_INIT_NAT 23
-#define IPA_IOCTL_NAT_DMA 24
-#define IPA_IOCTL_V4_DEL_NAT 26
-#define IPA_IOCTL_PULL_MSG 27
-#define IPA_IOCTL_GET_NAT_OFFSET 28
-#define IPA_IOCTL_RM_ADD_DEPENDENCY 29
-#define IPA_IOCTL_RM_DEL_DEPENDENCY 30
-#define IPA_IOCTL_GENERATE_FLT_EQ 31
-#define IPA_IOCTL_QUERY_INTF_EXT_PROPS 32
-#define IPA_IOCTL_QUERY_EP_MAPPING 33
-#define IPA_IOCTL_QUERY_RT_TBL_INDEX 34
-#define IPA_IOCTL_WRITE_QMAPID 35
-#define IPA_IOCTL_MDFY_FLT_RULE 36
-#define IPA_IOCTL_NOTIFY_WAN_UPSTREAM_ROUTE_ADD 37
-#define IPA_IOCTL_NOTIFY_WAN_UPSTREAM_ROUTE_DEL 38
-#define IPA_IOCTL_NOTIFY_WAN_EMBMS_CONNECTED 39
-#define IPA_IOCTL_ADD_HDR_PROC_CTX 40
-#define IPA_IOCTL_DEL_HDR_PROC_CTX 41
-#define IPA_IOCTL_MDFY_RT_RULE 42
-#define IPA_IOCTL_ADD_RT_RULE_AFTER 43
-#define IPA_IOCTL_ADD_FLT_RULE_AFTER 44
-#define IPA_IOCTL_GET_HW_VERSION 45
-#define IPA_IOCTL_ADD_RT_RULE_EXT 46
-#define IPA_IOCTL_ADD_VLAN_IFACE 47
-#define IPA_IOCTL_DEL_VLAN_IFACE 48
-#define IPA_IOCTL_ADD_L2TP_VLAN_MAPPING 49
-#define IPA_IOCTL_DEL_L2TP_VLAN_MAPPING 50
-#define IPA_IOCTL_NAT_MODIFY_PDN 51
-#define IPA_IOCTL_MAX 52
+#define IPA_IOCTL_ADD_HDR 0
+#define IPA_IOCTL_DEL_HDR 1
+#define IPA_IOCTL_ADD_RT_RULE 2
+#define IPA_IOCTL_DEL_RT_RULE 3
+#define IPA_IOCTL_ADD_FLT_RULE 4
+#define IPA_IOCTL_DEL_FLT_RULE 5
+#define IPA_IOCTL_COMMIT_HDR 6
+#define IPA_IOCTL_RESET_HDR 7
+#define IPA_IOCTL_COMMIT_RT 8
+#define IPA_IOCTL_RESET_RT 9
+#define IPA_IOCTL_COMMIT_FLT 10
+#define IPA_IOCTL_RESET_FLT 11
+#define IPA_IOCTL_DUMP 12
+#define IPA_IOCTL_GET_RT_TBL 13
+#define IPA_IOCTL_PUT_RT_TBL 14
+#define IPA_IOCTL_COPY_HDR 15
+#define IPA_IOCTL_QUERY_INTF 16
+#define IPA_IOCTL_QUERY_INTF_TX_PROPS 17
+#define IPA_IOCTL_QUERY_INTF_RX_PROPS 18
+#define IPA_IOCTL_GET_HDR 19
+#define IPA_IOCTL_PUT_HDR 20
+#define IPA_IOCTL_SET_FLT 21
+#define IPA_IOCTL_ALLOC_NAT_MEM 22
+#define IPA_IOCTL_V4_INIT_NAT 23
+#define IPA_IOCTL_TABLE_DMA_CMD 24
+#define IPA_IOCTL_NAT_DMA IPA_IOCTL_TABLE_DMA_CMD
+#define IPA_IOCTL_INIT_IPV6CT_TABLE 25
+#define IPA_IOCTL_V4_DEL_NAT 26
+#define IPA_IOCTL_PULL_MSG 27
+#define IPA_IOCTL_GET_NAT_OFFSET 28
+#define IPA_IOCTL_RM_ADD_DEPENDENCY 29
+#define IPA_IOCTL_RM_DEL_DEPENDENCY 30
+#define IPA_IOCTL_GENERATE_FLT_EQ 31
+#define IPA_IOCTL_QUERY_INTF_EXT_PROPS 32
+#define IPA_IOCTL_QUERY_EP_MAPPING 33
+#define IPA_IOCTL_QUERY_RT_TBL_INDEX 34
+#define IPA_IOCTL_WRITE_QMAPID 35
+#define IPA_IOCTL_MDFY_FLT_RULE 36
+#define IPA_IOCTL_NOTIFY_WAN_UPSTREAM_ROUTE_ADD 37
+#define IPA_IOCTL_NOTIFY_WAN_UPSTREAM_ROUTE_DEL 38
+#define IPA_IOCTL_NOTIFY_WAN_EMBMS_CONNECTED 39
+#define IPA_IOCTL_ADD_HDR_PROC_CTX 40
+#define IPA_IOCTL_DEL_HDR_PROC_CTX 41
+#define IPA_IOCTL_MDFY_RT_RULE 42
+#define IPA_IOCTL_ADD_RT_RULE_AFTER 43
+#define IPA_IOCTL_ADD_FLT_RULE_AFTER 44
+#define IPA_IOCTL_GET_HW_VERSION 45
+#define IPA_IOCTL_ADD_RT_RULE_EXT 46
+#define IPA_IOCTL_ADD_VLAN_IFACE 47
+#define IPA_IOCTL_DEL_VLAN_IFACE 48
+#define IPA_IOCTL_ADD_L2TP_VLAN_MAPPING 49
+#define IPA_IOCTL_DEL_L2TP_VLAN_MAPPING 50
+#define IPA_IOCTL_NAT_MODIFY_PDN 51
+#define IPA_IOCTL_ALLOC_NAT_TABLE 52
+#define IPA_IOCTL_ALLOC_IPV6CT_TABLE 53
+#define IPA_IOCTL_DEL_NAT_TABLE 54
+#define IPA_IOCTL_DEL_IPV6CT_TABLE 55
/**
* max size of the header to be inserted
@@ -1442,15 +1462,26 @@
};
/**
- * struct ipa_ioc_v4_nat_init - nat table initialization
- * parameters
+ * struct ipa_ioc_nat_ipv6ct_table_alloc - NAT/IPv6CT table memory allocation
+ * properties
+ * @size: input parameter, size of table in bytes
+ * @offset: output parameter, offset into page in case of system memory
+ */
+struct ipa_ioc_nat_ipv6ct_table_alloc {
+ size_t size;
+ off_t offset;
+};
+
+/**
+ * struct ipa_ioc_v4_nat_init - nat table initialization parameters
* @tbl_index: input parameter, index of the table
* @ipv4_rules_offset: input parameter, ipv4 rules address offset
* @expn_rules_offset: input parameter, ipv4 expansion rules address offset
* @index_offset: input parameter, index rules offset
* @index_expn_offset: input parameter, index expansion rules offset
- * @table_entries: input parameter, ipv4 rules table size in entries
- * @expn_table_entries: input parameter, ipv4 expansion rules table size
+ * @table_entries: input parameter, ipv4 rules table number of entries
+ * @expn_table_entries: input parameter, ipv4 expansion rules table number of
+ * entries
* @ip_addr: input parameter, public ip address
*/
struct ipa_ioc_v4_nat_init {
@@ -1467,6 +1498,23 @@
};
/**
+ * struct ipa_ioc_ipv6ct_init - IPv6CT table initialization parameters
+ * @base_table_offset: input parameter, IPv6CT base table address offset
+ * @expn_table_offset: input parameter, IPv6CT expansion table address offset
+ * @table_entries: input parameter, IPv6CT table number of entries
+ * @expn_table_entries: input parameter, IPv6CT expansion table number of
+ * entries
+ * @tbl_index: input parameter, index of the table
+ */
+struct ipa_ioc_ipv6ct_init {
+ uint32_t base_table_offset;
+ uint32_t expn_table_offset;
+ uint16_t table_entries;
+ uint16_t expn_table_entries;
+ uint8_t tbl_index;
+};
+
+/**
* struct ipa_ioc_v4_nat_del - nat table delete parameter
* @table_index: input parameter, index of the table
* @public_ip_addr: input parameter, public ip address
@@ -1477,7 +1525,15 @@
};
/**
- * struct ipa_ioc_nat_dma_one - nat dma command parameter
+ * struct ipa_ioc_nat_ipv6ct_table_del - NAT/IPv6CT table delete parameter
+ * @table_index: input parameter, index of the table
+ */
+struct ipa_ioc_nat_ipv6ct_table_del {
+ uint8_t table_index;
+};
+
+/**
+ * struct ipa_ioc_nat_dma_one - nat/ipv6ct dma command parameter
* @table_index: input parameter, index of the table
* @base_addr: type of table, from which the base address of the table
* can be inferred
@@ -1494,7 +1550,7 @@
};
/**
- * struct ipa_ioc_nat_dma_cmd - To hold multiple nat dma commands
+ * struct ipa_ioc_nat_dma_cmd - To hold multiple nat/ipv6ct dma commands
* @entries: number of dma commands in use
* @dma: data pointer to the dma commands
*/
@@ -1505,12 +1561,12 @@
};
/**
-* struct ipa_ioc_nat_pdn_entry - PDN entry modification data
-* @pdn_index: index of the entry in the PDN config table to be changed
-* @public_ip: PDN's public ip
-* @src_metadata: PDN's source NAT metadata for metadata replacement
-* @dst_metadata: PDN's destination NAT metadata for metadata replacement
-*/
+ * struct ipa_ioc_nat_pdn_entry - PDN entry modification data
+ * @pdn_index: index of the entry in the PDN config table to be changed
+ * @public_ip: PDN's public ip
+ * @src_metadata: PDN's source NAT metadata for metadata replacement
+ * @dst_metadata: PDN's destination NAT metadata for metadata replacement
+ */
struct ipa_ioc_nat_pdn_entry {
uint8_t pdn_index;
uint32_t public_ip;
@@ -1752,15 +1808,33 @@
#define IPA_IOC_ALLOC_NAT_MEM _IOWR(IPA_IOC_MAGIC, \
IPA_IOCTL_ALLOC_NAT_MEM, \
struct ipa_ioc_nat_alloc_mem *)
+#define IPA_IOC_ALLOC_NAT_TABLE _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_ALLOC_NAT_TABLE, \
+ struct ipa_ioc_nat_ipv6ct_table_alloc *)
+#define IPA_IOC_ALLOC_IPV6CT_TABLE _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_ALLOC_IPV6CT_TABLE, \
+ struct ipa_ioc_nat_ipv6ct_table_alloc *)
#define IPA_IOC_V4_INIT_NAT _IOWR(IPA_IOC_MAGIC, \
IPA_IOCTL_V4_INIT_NAT, \
struct ipa_ioc_v4_nat_init *)
+#define IPA_IOC_INIT_IPV6CT_TABLE _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_INIT_IPV6CT_TABLE, \
+ struct ipa_ioc_ipv6ct_init *)
#define IPA_IOC_NAT_DMA _IOWR(IPA_IOC_MAGIC, \
IPA_IOCTL_NAT_DMA, \
struct ipa_ioc_nat_dma_cmd *)
+#define IPA_IOC_TABLE_DMA_CMD _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_TABLE_DMA_CMD, \
+ struct ipa_ioc_nat_dma_cmd *)
#define IPA_IOC_V4_DEL_NAT _IOWR(IPA_IOC_MAGIC, \
IPA_IOCTL_V4_DEL_NAT, \
struct ipa_ioc_v4_nat_del *)
+#define IPA_IOC_DEL_NAT_TABLE _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_DEL_NAT_TABLE, \
+ struct ipa_ioc_nat_ipv6ct_table_del *)
+#define IPA_IOC_DEL_IPV6CT_TABLE _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_DEL_IPV6CT_TABLE, \
+ struct ipa_ioc_nat_ipv6ct_table_del *)
#define IPA_IOC_GET_NAT_OFFSET _IOWR(IPA_IOC_MAGIC, \
IPA_IOCTL_GET_NAT_OFFSET, \
uint32_t *)
diff --git a/include/uapi/linux/rds.h b/include/uapi/linux/rds.h
index 0f9265c..7af20a1 100644
--- a/include/uapi/linux/rds.h
+++ b/include/uapi/linux/rds.h
@@ -35,6 +35,7 @@
#define _LINUX_RDS_H
#include <linux/types.h>
+#include <linux/socket.h> /* For __kernel_sockaddr_storage. */
#define RDS_IB_ABI_VERSION 0x301
@@ -223,7 +224,7 @@
};
struct rds_get_mr_for_dest_args {
- struct sockaddr_storage dest_addr;
+ struct __kernel_sockaddr_storage dest_addr;
struct rds_iovec vec;
uint64_t cookie_addr;
uint64_t flags;
diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h
index 0e5ce0d..0d69769 100644
--- a/include/uapi/linux/usb/ch9.h
+++ b/include/uapi/linux/usb/ch9.h
@@ -759,6 +759,7 @@
__u8 iFunction;
} __attribute__ ((packed));
+#define USB_DT_INTERFACE_ASSOCIATION_SIZE 8
/*-------------------------------------------------------------------------*/
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index d750568..1df8c41 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -1134,6 +1134,37 @@
V4L2_CID_MPEG_VIDC_VIDEO_FLIP_BOTH = 3,
};
+/* HDR SEI INFO related control IDs and definitions*/
+#define V4L2_MPEG_VIDC_VENC_HDR_INFO_ENABLED 1
+#define V4L2_MPEG_VIDC_VENC_HDR_INFO_DISABLED 0
+
+#define V4L2_CID_MPEG_VIDC_VENC_HDR_INFO \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 116)
+#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_00 \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 117)
+#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_01 \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 118)
+#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_10 \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 119)
+#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_11 \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 120)
+#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_20 \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 121)
+#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_21 \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 122)
+#define V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_X \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 123)
+#define V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_Y \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 124)
+#define V4L2_CID_MPEG_VIDC_VENC_MAX_DISP_LUM \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 125)
+#define V4L2_CID_MPEG_VIDC_VENC_MIN_DISP_LUM \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 126)
+#define V4L2_CID_MPEG_VIDC_VENC_MAX_CLL \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 127)
+#define V4L2_CID_MPEG_VIDC_VENC_MAX_FLL \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 128)
+
/* Camera class control IDs */
#define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900)
diff --git a/include/xen/swiotlb-xen.h b/include/xen/swiotlb-xen.h
index 7c35e27..683057f 100644
--- a/include/xen/swiotlb-xen.h
+++ b/include/xen/swiotlb-xen.h
@@ -58,4 +58,9 @@
extern int
xen_swiotlb_set_dma_mask(struct device *dev, u64 dma_mask);
+
+extern int
+xen_swiotlb_dma_mmap(struct device *dev, struct vm_area_struct *vma,
+ void *cpu_addr, dma_addr_t dma_addr, size_t size,
+ unsigned long attrs);
#endif /* __LINUX_SWIOTLB_XEN_H */
diff --git a/init/initramfs.c b/init/initramfs.c
index f8ce812..d0b53f49 100644
--- a/init/initramfs.c
+++ b/init/initramfs.c
@@ -19,6 +19,7 @@
#include <linux/syscalls.h>
#include <linux/utime.h>
#include <linux/initramfs.h>
+#include <linux/file.h>
static ssize_t __init xwrite(int fd, const char *p, size_t count)
{
@@ -664,6 +665,7 @@
printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err);
free_initrd();
#endif
+ flush_delayed_fput();
/*
* Try loading default modules from initramfs. This gives
* us a chance to load before device_initcalls.
diff --git a/init/main.c b/init/main.c
index aca8f3e..674bc77 100644
--- a/init/main.c
+++ b/init/main.c
@@ -70,7 +70,6 @@
#include <linux/shmem_fs.h>
#include <linux/slab.h>
#include <linux/perf_event.h>
-#include <linux/file.h>
#include <linux/ptrace.h>
#include <linux/blkdev.h>
#include <linux/elevator.h>
@@ -946,8 +945,6 @@
system_state = SYSTEM_RUNNING;
numa_default_policy();
- flush_delayed_fput();
-
rcu_end_inkernel_boot();
if (ramdisk_execute_command) {
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 779c871..372454a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1720,7 +1720,8 @@
}
} else {
if (insn->src_reg != BPF_REG_0 || insn->off != 0 ||
- (insn->imm != 16 && insn->imm != 32 && insn->imm != 64)) {
+ (insn->imm != 16 && insn->imm != 32 && insn->imm != 64) ||
+ BPF_CLASS(insn->code) == BPF_ALU64) {
verbose("BPF_END uses reserved fields\n");
return -EINVAL;
}
diff --git a/kernel/configs/android-fetch-configs.sh b/kernel/configs/android-fetch-configs.sh
new file mode 100755
index 0000000..a5b56d4
--- /dev/null
+++ b/kernel/configs/android-fetch-configs.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+curl https://android.googlesource.com/kernel/configs/+archive/master/android-4.9.tar.gz | tar xzv
+
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
index cbf240d..af9159a 100644
--- a/kernel/cpuset.c
+++ b/kernel/cpuset.c
@@ -2312,6 +2312,13 @@
mutex_unlock(&cpuset_mutex);
}
+static bool force_rebuild;
+
+void cpuset_force_rebuild(void)
+{
+ force_rebuild = true;
+}
+
/**
* cpuset_hotplug_workfn - handle CPU/memory hotunplug for a cpuset
*
@@ -2386,8 +2393,10 @@
}
/* rebuild sched domains if cpus_allowed has changed */
- if (cpus_updated)
+ if (cpus_updated || force_rebuild) {
+ force_rebuild = false;
rebuild_sched_domains();
+ }
}
void cpuset_update_active_cpus(bool cpu_online)
@@ -2406,6 +2415,11 @@
schedule_work(&cpuset_hotplug_work);
}
+void cpuset_wait_for_hotplug(void)
+{
+ flush_work(&cpuset_hotplug_work);
+}
+
/*
* Keep top_cpuset.mems_allowed tracking node_states[N_MEMORY].
* Call this routine anytime after node_states[N_MEMORY] changes.
diff --git a/kernel/events/core.c b/kernel/events/core.c
index f6e81b5..b784662 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7963,6 +7963,7 @@
}
}
event->tp_event->prog = prog;
+ event->tp_event->bpf_prog_owner = event;
return 0;
}
@@ -7977,7 +7978,7 @@
return;
prog = event->tp_event->prog;
- if (prog) {
+ if (prog && event->tp_event->bpf_prog_owner == event) {
event->tp_event->prog = NULL;
bpf_prog_put(prog);
}
diff --git a/kernel/hung_task.c b/kernel/hung_task.c
index 2b59c82..b94d3d1 100644
--- a/kernel/hung_task.c
+++ b/kernel/hung_task.c
@@ -17,6 +17,7 @@
#include <linux/sysctl.h>
#include <linux/utsname.h>
#include <trace/events/sched.h>
+#include <linux/sched/sysctl.h>
/*
* The number of tasks checked:
@@ -24,6 +25,14 @@
int __read_mostly sysctl_hung_task_check_count = PID_MAX_LIMIT;
/*
+ * Selective monitoring of hung tasks.
+ *
+ * if set to 1, khungtaskd skips monitoring tasks, which has
+ * task_struct->hang_detection_enabled value not set, else monitors all tasks.
+ */
+int sysctl_hung_task_selective_monitoring = 1;
+
+/*
* Limit number of tasks checked in a batch.
*
* This value controls the preemptibility of khungtaskd since preemption
@@ -179,7 +188,10 @@
}
/* use "==" to skip the TASK_KILLABLE tasks waiting on NFS */
if (t->state == TASK_UNINTERRUPTIBLE)
- check_hung_task(t, timeout);
+ /* Check for selective monitoring */
+ if (!sysctl_hung_task_selective_monitoring ||
+ t->hang_detection_enabled)
+ check_hung_task(t, timeout);
}
unlock:
rcu_read_unlock();
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index 00bb0ae..77977f55df 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -405,10 +405,8 @@
* The sysfs entry must be serialized against a concurrent
* irq_sysfs_init() as well.
*/
- mutex_lock(&sparse_irq_lock);
kobject_del(&desc->kobj);
delete_irq_desc(irq);
- mutex_unlock(&sparse_irq_lock);
/*
* We free the descriptor, masks and stat fields via RCU. That
@@ -446,20 +444,15 @@
desc = alloc_desc(start + i, node, flags, mask, owner);
if (!desc)
goto err;
- mutex_lock(&sparse_irq_lock);
irq_insert_desc(start + i, desc);
irq_sysfs_add(start + i, desc);
- mutex_unlock(&sparse_irq_lock);
}
+ bitmap_set(allocated_irqs, start, cnt);
return start;
err:
for (i--; i >= 0; i--)
free_desc(start + i);
-
- mutex_lock(&sparse_irq_lock);
- bitmap_clear(allocated_irqs, start, cnt);
- mutex_unlock(&sparse_irq_lock);
return -ENOMEM;
}
@@ -558,6 +551,7 @@
desc->owner = owner;
}
+ bitmap_set(allocated_irqs, start, cnt);
return start;
}
@@ -653,10 +647,10 @@
if (from >= nr_irqs || (from + cnt) > nr_irqs)
return;
+ mutex_lock(&sparse_irq_lock);
for (i = 0; i < cnt; i++)
free_desc(from + i);
- mutex_lock(&sparse_irq_lock);
bitmap_clear(allocated_irqs, from, cnt);
mutex_unlock(&sparse_irq_lock);
}
@@ -703,19 +697,15 @@
from, cnt, 0);
ret = -EEXIST;
if (irq >=0 && start != irq)
- goto err;
+ goto unlock;
if (start + cnt > nr_irqs) {
ret = irq_expand_nr_irqs(start + cnt);
if (ret)
- goto err;
+ goto unlock;
}
-
- bitmap_set(allocated_irqs, start, cnt);
- mutex_unlock(&sparse_irq_lock);
- return alloc_descs(start, cnt, node, affinity, owner);
-
-err:
+ ret = alloc_descs(start, cnt, node, affinity, owner);
+unlock:
mutex_unlock(&sparse_irq_lock);
return ret;
}
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 4d7ffc0..6599c7f 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -3260,10 +3260,17 @@
if (depth) {
hlock = curr->held_locks + depth - 1;
if (hlock->class_idx == class_idx && nest_lock) {
- if (hlock->references)
+ if (hlock->references) {
+ /*
+ * Check: unsigned int references:12, overflow.
+ */
+ if (DEBUG_LOCKS_WARN_ON(hlock->references == (1 << 12)-1))
+ return 0;
+
hlock->references++;
- else
+ } else {
hlock->references = 2;
+ }
return 1;
}
diff --git a/kernel/power/process.c b/kernel/power/process.c
index 68d27ae..9a12c83 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -19,8 +19,9 @@
#include <linux/kmod.h>
#include <trace/events/power.h>
#include <linux/wakeup_reason.h>
+#include <linux/cpuset.h>
-/*
+/*
* Timeout for stopping processes
*/
unsigned int __read_mostly freeze_timeout_msecs = 20 * MSEC_PER_SEC;
@@ -210,6 +211,8 @@
__usermodehelper_set_disable_depth(UMH_FREEZING);
thaw_workqueues();
+ cpuset_wait_for_hotplug();
+
read_lock(&tasklist_lock);
for_each_process_thread(g, p) {
/* No other threads should have PF_SUSPEND_TASK set */
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index 4f8f9c1..084c41c 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -792,8 +792,13 @@
long long oldval;
struct rcu_dynticks *rdtp;
- RCU_LOCKDEP_WARN(!irqs_disabled(), "rcu_irq_exit() invoked with irqs enabled!!!");
rdtp = this_cpu_ptr(&rcu_dynticks);
+
+ /* Page faults can happen in NMI handlers, so check... */
+ if (READ_ONCE(rdtp->dynticks_nmi_nesting))
+ return;
+
+ RCU_LOCKDEP_WARN(!irqs_disabled(), "rcu_irq_exit() invoked with irqs enabled!!!");
oldval = rdtp->dynticks_nesting;
rdtp->dynticks_nesting--;
WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
@@ -930,8 +935,13 @@
struct rcu_dynticks *rdtp;
long long oldval;
- RCU_LOCKDEP_WARN(!irqs_disabled(), "rcu_irq_enter() invoked with irqs enabled!!!");
rdtp = this_cpu_ptr(&rcu_dynticks);
+
+ /* Page faults can happen in NMI handlers, so check... */
+ if (READ_ONCE(rdtp->dynticks_nmi_nesting))
+ return;
+
+ RCU_LOCKDEP_WARN(!irqs_disabled(), "rcu_irq_enter() invoked with irqs enabled!!!");
oldval = rdtp->dynticks_nesting;
rdtp->dynticks_nesting++;
WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 58c4341..01a589c 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -1169,6 +1169,7 @@
cpumask_t allowed_mask;
rq = task_rq_lock(p, &rf);
+ update_rq_clock(rq);
if (p->flags & PF_KTHREAD) {
/*
@@ -7959,16 +7960,15 @@
* operation in the resume sequence, just build a single sched
* domain, ignoring cpusets.
*/
- num_cpus_frozen--;
- if (likely(num_cpus_frozen)) {
- partition_sched_domains(1, NULL, NULL);
+ partition_sched_domains(1, NULL, NULL);
+ if (--num_cpus_frozen)
return;
- }
/*
* This is the last CPU online operation. So fall through and
* restore the original sched domains by considering the
* cpuset configurations.
*/
+ cpuset_force_rebuild();
}
cpuset_update_active_cpus(true);
}
diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c
index f95243b..cc5a97c 100644
--- a/kernel/sched/core_ctl.c
+++ b/kernel/sched/core_ctl.c
@@ -38,6 +38,7 @@
unsigned int active_cpus;
unsigned int num_cpus;
unsigned int nr_isolated_cpus;
+ unsigned int nr_not_preferred_cpus;
#ifdef CONFIG_SCHED_CORE_ROTATE
unsigned long set_max;
unsigned long set_cur;
@@ -350,6 +351,7 @@
unsigned int val[MAX_CPUS_PER_CLUSTER];
unsigned long flags;
int ret;
+ int not_preferred_count = 0;
ret = sscanf(buf, "%u %u %u %u %u %u\n",
&val[0], &val[1], &val[2], &val[3],
@@ -361,7 +363,9 @@
for (i = 0; i < state->num_cpus; i++) {
c = &per_cpu(cpu_state, i + state->first_cpu);
c->not_preferred = val[i];
+ not_preferred_count += !!val[i];
}
+ state->nr_not_preferred_cpus = not_preferred_count;
spin_unlock_irqrestore(&state_lock, flags);
return count;
@@ -793,10 +797,18 @@
continue;
if (cluster->active_cpus == need)
break;
- /* Don't offline busy CPUs. */
+ /* Don't isolate busy CPUs. */
if (c->is_busy)
continue;
+ /*
+ * We isolate only the not_preferred CPUs. If none
+ * of the CPUs are selected as not_preferred, then
+ * all CPUs are eligible for isolation.
+ */
+ if (cluster->nr_not_preferred_cpus && !c->not_preferred)
+ continue;
+
if (!should_we_isolate(c->cpu, cluster))
continue;
@@ -1109,6 +1121,7 @@
cluster->set_cur = cluster->set_max - 1;
#endif
cluster->enable = true;
+ cluster->nr_not_preferred_cpus = 0;
INIT_LIST_HEAD(&cluster->lru);
spin_lock_init(&cluster->pending_lock);
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 2d92092..3d933a0 100755
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -3223,6 +3223,36 @@
return 1;
}
+/*
+ * Check if we need to update the load and the utilization of a blocked
+ * group_entity:
+ */
+static inline bool skip_blocked_update(struct sched_entity *se)
+{
+ struct cfs_rq *gcfs_rq = group_cfs_rq(se);
+
+ /*
+ * If sched_entity still have not zero load or utilization, we have to
+ * decay it:
+ */
+ if (se->avg.load_avg || se->avg.util_avg)
+ return false;
+
+ /*
+ * If there is a pending propagation, we have to update the load and
+ * the utilization of the sched_entity:
+ */
+ if (gcfs_rq->propagate_avg)
+ return false;
+
+ /*
+ * Otherwise, the load and the utilization of the sched_entity is
+ * already zero and there is no pending propagation, so it will be a
+ * waste of time to try to decay it:
+ */
+ return true;
+}
+
#else /* CONFIG_FAIR_GROUP_SCHED */
static inline void update_tg_load_avg(struct cfs_rq *cfs_rq, int force) {}
@@ -8400,6 +8430,8 @@
* list_add_leaf_cfs_rq() for details.
*/
for_each_leaf_cfs_rq(rq, cfs_rq) {
+ struct sched_entity *se;
+
/* throttled entities do not contribute to load */
if (throttled_hierarchy(cfs_rq))
continue;
@@ -8407,9 +8439,10 @@
if (update_cfs_rq_load_avg(cfs_rq_clock_task(cfs_rq), cfs_rq, true))
update_tg_load_avg(cfs_rq, 0);
- /* Propagate pending load changes to the parent */
- if (cfs_rq->tg->se[cpu])
- update_load_avg(cfs_rq->tg->se[cpu], 0);
+ /* Propagate pending load changes to the parent, if any: */
+ se = cfs_rq->tg->se[cpu];
+ if (se && !skip_blocked_update(se))
+ update_load_avg(se, 0);
}
raw_spin_unlock_irqrestore(&rq->lock, flags);
}
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 7feba85..1bf8e63 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -914,15 +914,30 @@
char *pos = buf;
char *end = buf + sizeof(buf);
int idx;
+ struct rt_bandwidth *rt_b = sched_rt_bandwidth(rt_rq);
pos += snprintf(pos, sizeof(buf),
- "sched: RT throttling activated for rt_rq %p (cpu %d)\n",
+ "sched: RT throttling activated for rt_rq %pK (cpu %d)\n",
rt_rq, cpu_of(rq_of_rt_rq(rt_rq)));
+ pos += snprintf(pos, end - pos,
+ "rt_period_timer: expires=%lld now=%llu period=%llu\n",
+ hrtimer_get_expires_ns(&rt_b->rt_period_timer),
+ ktime_get_ns(), sched_rt_period(rt_rq));
+
if (bitmap_empty(array->bitmap, MAX_RT_PRIO))
goto out;
pos += snprintf(pos, end - pos, "potential CPU hogs:\n");
+#ifdef CONFIG_SCHED_INFO
+ if (sched_info_on())
+ pos += snprintf(pos, end - pos,
+ "current %s (%d) is running for %llu nsec\n",
+ current->comm, current->pid,
+ rq_clock(rq_of_rt_rq(rt_rq)) -
+ current->sched_info.last_arrival);
+#endif
+
idx = sched_find_first_bit(array->bitmap);
while (idx < MAX_RT_PRIO) {
list_for_each_entry(rt_se, array->queue + idx, run_list) {
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index 0db7c8a..af182a6 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -457,14 +457,19 @@
return 0;
}
+void __get_seccomp_filter(struct seccomp_filter *filter)
+{
+ /* Reference count is bounded by the number of total processes. */
+ atomic_inc(&filter->usage);
+}
+
/* get_seccomp_filter - increments the reference count of the filter on @tsk */
void get_seccomp_filter(struct task_struct *tsk)
{
struct seccomp_filter *orig = tsk->seccomp.filter;
if (!orig)
return;
- /* Reference count is bounded by the number of total processes. */
- atomic_inc(&orig->usage);
+ __get_seccomp_filter(orig);
}
static inline void seccomp_filter_free(struct seccomp_filter *filter)
@@ -475,10 +480,8 @@
}
}
-/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */
-void put_seccomp_filter(struct task_struct *tsk)
+static void __put_seccomp_filter(struct seccomp_filter *orig)
{
- struct seccomp_filter *orig = tsk->seccomp.filter;
/* Clean up single-reference branches iteratively. */
while (orig && atomic_dec_and_test(&orig->usage)) {
struct seccomp_filter *freeme = orig;
@@ -487,6 +490,12 @@
}
}
+/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */
+void put_seccomp_filter(struct task_struct *tsk)
+{
+ __put_seccomp_filter(tsk->seccomp.filter);
+}
+
/**
* seccomp_send_sigsys - signals the task to allow in-process syscall emulation
* @syscall: syscall number to send to userland
@@ -892,13 +901,13 @@
if (!data)
goto out;
- get_seccomp_filter(task);
+ __get_seccomp_filter(filter);
spin_unlock_irq(&task->sighand->siglock);
if (copy_to_user(data, fprog->filter, bpf_classic_proglen(fprog)))
ret = -EFAULT;
- put_seccomp_filter(task);
+ __put_seccomp_filter(filter);
return ret;
out:
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 2c4cd17..29bb99c 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1186,6 +1186,16 @@
.proc_handler = proc_dointvec_minmax,
.extra1 = &neg_one,
},
+ {
+ .procname = "hung_task_selective_monitoring",
+ .data = &sysctl_hung_task_selective_monitoring,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &zero,
+ .extra2 = &one,
+ },
+
#endif
#ifdef CONFIG_RT_MUTEXES
{
@@ -1292,6 +1302,8 @@
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = timer_migration_handler,
+ .extra1 = &zero,
+ .extra2 = &one,
},
#endif
#ifdef CONFIG_BPF_SYSCALL
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index 2aef653..a01a71f 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -94,17 +94,15 @@
};
static const int hrtimer_clock_to_base_table[MAX_CLOCKS] = {
+ /* Make sure we catch unsupported clockids */
+ [0 ... MAX_CLOCKS - 1] = HRTIMER_MAX_CLOCK_BASES,
+
[CLOCK_REALTIME] = HRTIMER_BASE_REALTIME,
[CLOCK_MONOTONIC] = HRTIMER_BASE_MONOTONIC,
[CLOCK_BOOTTIME] = HRTIMER_BASE_BOOTTIME,
[CLOCK_TAI] = HRTIMER_BASE_TAI,
};
-static inline int hrtimer_clockid_to_base(clockid_t clock_id)
-{
- return hrtimer_clock_to_base_table[clock_id];
-}
-
/*
* Functions and macros which are different for UP/SMP systems are kept in a
* single place
@@ -1091,6 +1089,18 @@
}
#endif
+static inline int hrtimer_clockid_to_base(clockid_t clock_id)
+{
+ if (likely(clock_id < MAX_CLOCKS)) {
+ int base = hrtimer_clock_to_base_table[clock_id];
+
+ if (likely(base != HRTIMER_MAX_CLOCK_BASES))
+ return base;
+ }
+ WARN(1, "Invalid clockid %d. Using MONOTONIC\n", clock_id);
+ return HRTIMER_BASE_MONOTONIC;
+}
+
static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
enum hrtimer_mode mode)
{
diff --git a/kernel/time/tick-oneshot.c b/kernel/time/tick-oneshot.c
index b513446..05ae01e 100644
--- a/kernel/time/tick-oneshot.c
+++ b/kernel/time/tick-oneshot.c
@@ -33,6 +33,7 @@
* We don't need the clock event device any more, stop it.
*/
clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT_STOPPED);
+ dev->next_event.tv64 = KTIME_MAX;
return 0;
}
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 80aa30d..ccf6499 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -245,7 +245,7 @@
int ret;
mutex_lock(&mutex);
- ret = proc_dointvec(table, write, buffer, lenp, ppos);
+ ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
if (!ret && write)
timers_update_migration(false);
mutex_unlock(&mutex);
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 6e432ed..5b8d718 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -2747,13 +2747,14 @@
if (!command || !ftrace_enabled) {
/*
- * If these are per_cpu ops, they still need their
- * per_cpu field freed. Since, function tracing is
+ * If these are dynamic or per_cpu ops, they still
+ * need their data freed. Since, function tracing is
* not currently active, we can just free them
* without synchronizing all CPUs.
*/
- if (ops->flags & FTRACE_OPS_FL_PER_CPU)
- per_cpu_ops_free(ops);
+ if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU))
+ goto free_ops;
+
return 0;
}
@@ -2808,6 +2809,7 @@
if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU)) {
schedule_on_each_cpu(ftrace_sync);
+ free_ops:
arch_ftrace_trampoline_free(ops);
if (ops->flags & FTRACE_OPS_FL_PER_CPU)
@@ -4379,9 +4381,6 @@
static char ftrace_graph_notrace_buf[FTRACE_FILTER_SIZE] __initdata;
static int ftrace_set_func(unsigned long *array, int *idx, int size, char *buffer);
-static unsigned long save_global_trampoline;
-static unsigned long save_global_flags;
-
static int __init set_graph_function(char *str)
{
strlcpy(ftrace_graph_buf, str, FTRACE_FILTER_SIZE);
@@ -5979,17 +5978,6 @@
unregister_pm_notifier(&ftrace_suspend_notifier);
unregister_trace_sched_switch(ftrace_graph_probe_sched_switch, NULL);
-#ifdef CONFIG_DYNAMIC_FTRACE
- /*
- * Function graph does not allocate the trampoline, but
- * other global_ops do. We need to reset the ALLOC_TRAMP flag
- * if one was used.
- */
- global_ops.trampoline = save_global_trampoline;
- if (save_global_flags & FTRACE_OPS_FL_ALLOC_TRAMP)
- global_ops.flags |= FTRACE_OPS_FL_ALLOC_TRAMP;
-#endif
-
out:
mutex_unlock(&ftrace_lock);
}
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index cddedb5..e3aae88 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -2416,11 +2416,17 @@
if (!buffer || buffer->nesting >= 4)
return NULL;
- return &buffer->buffer[buffer->nesting++][0];
+ buffer->nesting++;
+
+ /* Interrupts must see nesting incremented before we use the buffer */
+ barrier();
+ return &buffer->buffer[buffer->nesting][0];
}
static void put_trace_buf(void)
{
+ /* Don't let the decrement of nesting leak before this */
+ barrier();
this_cpu_dec(trace_percpu_buffer->nesting);
}
@@ -3636,11 +3642,17 @@
/* If this file was open for write, then erase contents */
if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) {
int cpu = tracing_get_cpu(inode);
+ struct trace_buffer *trace_buf = &tr->trace_buffer;
+
+#ifdef CONFIG_TRACER_MAX_TRACE
+ if (tr->current_trace->print_max)
+ trace_buf = &tr->max_buffer;
+#endif
if (cpu == RING_BUFFER_ALL_CPUS)
- tracing_reset_online_cpus(&tr->trace_buffer);
+ tracing_reset_online_cpus(trace_buf);
else
- tracing_reset(&tr->trace_buffer, cpu);
+ tracing_reset(trace_buf, cpu);
}
if (file->f_mode & FMODE_READ) {
@@ -5275,7 +5287,7 @@
*
* iter->pos will be 0 if we haven't read anything.
*/
- if (!tracing_is_on() && iter->pos)
+ if (!tracer_tracing_is_on(iter->tr) && iter->pos)
break;
mutex_unlock(&iter->mutex);
@@ -5814,7 +5826,7 @@
tracing_reset_online_cpus(&tr->trace_buffer);
#ifdef CONFIG_TRACER_MAX_TRACE
- if (tr->flags & TRACE_ARRAY_FL_GLOBAL && tr->max_buffer.buffer)
+ if (tr->max_buffer.buffer)
ring_buffer_set_clock(tr->max_buffer.buffer, trace_clocks[i].func);
tracing_reset_online_cpus(&tr->max_buffer);
#endif
diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
index b0f86ea..ca70d11 100644
--- a/kernel/trace/trace_selftest.c
+++ b/kernel/trace/trace_selftest.c
@@ -272,7 +272,7 @@
goto out_free;
if (cnt > 1) {
if (trace_selftest_test_global_cnt == 0)
- goto out;
+ goto out_free;
}
if (trace_selftest_test_dyn_cnt == 0)
goto out_free;
diff --git a/lib/ratelimit.c b/lib/ratelimit.c
index 08f8043..d01f471 100644
--- a/lib/ratelimit.c
+++ b/lib/ratelimit.c
@@ -48,7 +48,9 @@
if (time_is_before_jiffies(rs->begin + rs->interval)) {
if (rs->missed) {
if (!(rs->flags & RATELIMIT_MSG_ON_RELEASE)) {
- pr_warn("%s: %d callbacks suppressed\n", func, rs->missed);
+ printk_deferred(KERN_WARNING
+ "%s: %d callbacks suppressed\n",
+ func, rs->missed);
rs->missed = 0;
}
}
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 3b38b73..fce6c48 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -462,6 +462,8 @@
struct mem_cgroup_tree_per_node *mctz;
mctz = soft_limit_tree_from_page(page);
+ if (!mctz)
+ return;
/*
* Necessary to update all ancestors when hierarchy is used.
* because their event counter is not touched.
@@ -499,7 +501,8 @@
for_each_node(nid) {
mz = mem_cgroup_nodeinfo(memcg, nid);
mctz = soft_limit_tree_node(nid);
- mem_cgroup_remove_exceeded(mz, mctz);
+ if (mctz)
+ mem_cgroup_remove_exceeded(mz, mctz);
}
}
@@ -2565,7 +2568,7 @@
* is empty. Do it lockless to prevent lock bouncing. Races
* are acceptable as soft limit is best effort anyway.
*/
- if (RB_EMPTY_ROOT(&mctz->rb_root))
+ if (!mctz || RB_EMPTY_ROOT(&mctz->rb_root))
return 0;
/*
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 6eb61a4..0dab426 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -179,7 +179,7 @@
void get_page_bootmem(unsigned long info, struct page *page,
unsigned long type)
{
- page->lru.next = (struct list_head *) type;
+ page->freelist = (void *)type;
SetPagePrivate(page);
set_page_private(page, info);
page_ref_inc(page);
@@ -189,11 +189,12 @@
{
unsigned long type;
- type = (unsigned long) page->lru.next;
+ type = (unsigned long) page->freelist;
BUG_ON(type < MEMORY_HOTPLUG_MIN_BOOTMEM_TYPE ||
type > MEMORY_HOTPLUG_MAX_BOOTMEM_TYPE);
if (page_ref_dec_return(page) == 1) {
+ page->freelist = NULL;
ClearPagePrivate(page);
set_page_private(page, 0);
INIT_LIST_HEAD(&page->lru);
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index af783a6..1f13413 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -38,6 +38,7 @@
#include <linux/kthread.h>
#include <linux/init.h>
#include <linux/show_mem_notifier.h>
+#include <linux/mmu_notifier.h>
#include <asm/tlb.h>
#include "internal.h"
@@ -495,6 +496,21 @@
}
/*
+ * If the mm has notifiers then we would need to invalidate them around
+ * unmap_page_range and that is risky because notifiers can sleep and
+ * what they do is basically undeterministic. So let's have a short
+ * sleep to give the oom victim some more time.
+ * TODO: we really want to get rid of this ugly hack and make sure that
+ * notifiers cannot block for unbounded amount of time and add
+ * mmu_notifier_invalidate_range_{start,end} around unmap_page_range
+ */
+ if (mm_has_notifiers(mm)) {
+ up_read(&mm->mmap_sem);
+ schedule_timeout_idle(HZ);
+ goto unlock_oom;
+ }
+
+ /*
* increase mm_users only after we know we will reap something so
* that the mmput_async is called only when we have reaped something
* and delayed __mmput doesn't matter that much
diff --git a/mm/slab_common.c b/mm/slab_common.c
index 5d2f24f..622f6b6 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -255,7 +255,7 @@
{
struct kmem_cache *s;
- if (slab_nomerge || (flags & SLAB_NEVER_MERGE))
+ if (slab_nomerge)
return NULL;
if (ctor)
@@ -266,6 +266,9 @@
size = ALIGN(size, align);
flags = kmem_cache_flags(size, flags, name, NULL);
+ if (flags & SLAB_NEVER_MERGE)
+ return NULL;
+
list_for_each_entry_reverse(s, &slab_caches, list) {
if (slab_unmergeable(s))
continue;
diff --git a/mm/sparse.c b/mm/sparse.c
index 1e168bf..8c4c82e 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -662,7 +662,7 @@
>> PAGE_SHIFT;
for (i = 0; i < nr_pages; i++, page++) {
- magic = (unsigned long) page->lru.next;
+ magic = (unsigned long) page->freelist;
BUG_ON(magic == NODE_INFO);
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 7625ec8..5d4006e 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -1098,11 +1098,14 @@
spin_unlock_bh(&br->lock);
}
- err = br_changelink(dev, tb, data);
+ err = register_netdevice(dev);
if (err)
return err;
- return register_netdevice(dev);
+ err = br_changelink(dev, tb, data);
+ if (err)
+ unregister_netdevice(dev);
+ return err;
}
static size_t br_get_size(const struct net_device *brdev)
diff --git a/net/compat.c b/net/compat.c
index 1cd2ec0..a96fd2f 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -22,6 +22,7 @@
#include <linux/filter.h>
#include <linux/compat.h>
#include <linux/security.h>
+#include <linux/audit.h>
#include <linux/export.h>
#include <net/scm.h>
@@ -781,14 +782,24 @@
COMPAT_SYSCALL_DEFINE2(socketcall, int, call, u32 __user *, args)
{
- int ret;
- u32 a[6];
+ u32 a[AUDITSC_ARGS];
+ unsigned int len;
u32 a0, a1;
+ int ret;
if (call < SYS_SOCKET || call > SYS_SENDMMSG)
return -EINVAL;
- if (copy_from_user(a, args, nas[call]))
+ len = nas[call];
+ if (len > sizeof(a))
+ return -EINVAL;
+
+ if (copy_from_user(a, args, len))
return -EFAULT;
+
+ ret = audit_socketcall_compat(len / sizeof(a[0]), a);
+ if (ret)
+ return ret;
+
a0 = a[0];
a1 = a[1];
diff --git a/net/core/dev.c b/net/core/dev.c
index 27d54eb..7aab8f6 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2357,6 +2357,9 @@
{
unsigned long flags;
+ if (unlikely(!skb))
+ return;
+
if (likely(atomic_read(&skb->users) == 1)) {
smp_rmb();
atomic_set(&skb->users, 0);
@@ -4926,8 +4929,13 @@
while (remsd) {
struct softnet_data *next = remsd->rps_ipi_next;
- if (cpu_online(remsd->cpu))
+ if (cpu_online(remsd->cpu)) {
smp_call_function_single_async(remsd->cpu, &remsd->csd);
+ } else {
+ rps_lock(remsd);
+ remsd->backlog.state = 0;
+ rps_unlock(remsd);
+ }
remsd = next;
}
#endif
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c
index b6791d9..31c4041 100644
--- a/net/core/fib_rules.c
+++ b/net/core/fib_rules.c
@@ -425,6 +425,7 @@
if (tb[FRA_TUN_ID])
rule->tun_id = nla_get_be64(tb[FRA_TUN_ID]);
+ err = -EINVAL;
if (tb[FRA_L3MDEV]) {
#ifdef CONFIG_NET_L3_MASTER_DEV
rule->l3mdev = nla_get_u8(tb[FRA_L3MDEV]);
@@ -446,7 +447,6 @@
else
rule->suppress_ifgroup = -1;
- err = -EINVAL;
if (tb[FRA_GOTO]) {
if (rule->action != FR_ACT_GOTO)
goto errout_free;
@@ -576,8 +576,10 @@
if (tb[FRA_UID_RANGE]) {
range = nla_get_kuid_range(tb);
- if (!uid_range_set(&range))
+ if (!uid_range_set(&range)) {
+ err = -EINVAL;
goto errout;
+ }
} else {
range = fib_kuid_range_unset;
}
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 62893eb..93f7a5a 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -941,7 +941,7 @@
if (!mod_timer(&neigh->timer, next))
neigh_hold(neigh);
}
- if (neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) {
+ if (neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE | NUD_STALE)) {
neigh_probe(neigh);
} else {
out:
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 4d26297..c2339b8 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -3758,6 +3758,9 @@
return -EMSGSIZE;
ifsm = nlmsg_data(nlh);
+ ifsm->family = PF_UNSPEC;
+ ifsm->pad1 = 0;
+ ifsm->pad2 = 0;
ifsm->ifindex = dev->ifindex;
ifsm->filter_mask = filter_mask;
diff --git a/net/core/sock.c b/net/core/sock.c
index f07eaea..c6f42ee 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1501,6 +1501,8 @@
sock_copy(newsk, sk);
+ newsk->sk_prot_creator = sk->sk_prot;
+
/* SANITY */
if (likely(newsk->sk_net_refcnt))
get_net(sock_net(newsk));
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 079d76b..5000e6f 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -1269,26 +1269,32 @@
p->old_duplex = -1;
ds->ports[port].netdev = slave_dev;
- ret = register_netdev(slave_dev);
- if (ret) {
- netdev_err(master, "error %d registering interface %s\n",
- ret, slave_dev->name);
- ds->ports[port].netdev = NULL;
- free_netdev(slave_dev);
- return ret;
- }
netif_carrier_off(slave_dev);
ret = dsa_slave_phy_setup(p, slave_dev);
if (ret) {
netdev_err(master, "error %d setting up slave phy\n", ret);
- unregister_netdev(slave_dev);
- free_netdev(slave_dev);
- return ret;
+ goto out_free;
+ }
+
+ ret = register_netdev(slave_dev);
+ if (ret) {
+ netdev_err(master, "error %d registering interface %s\n",
+ ret, slave_dev->name);
+ goto out_phy;
}
return 0;
+
+out_phy:
+ phy_disconnect(p->phy);
+ if (of_phy_is_fixed_link(ds->ports[port].dn))
+ of_phy_deregister_fixed_link(ds->ports[port].dn);
+out_free:
+ free_netdev(slave_dev);
+ ds->ports[port].netdev = NULL;
+ return ret;
}
void dsa_slave_destroy(struct net_device *slave_dev)
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index c8409ca0..2ec005c 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -319,7 +319,7 @@
int ret, no_addr;
struct fib_result res;
struct flowi4 fl4;
- struct net *net;
+ struct net *net = dev_net(dev);
bool dev_match;
fl4.flowi4_oif = 0;
@@ -332,6 +332,7 @@
fl4.flowi4_scope = RT_SCOPE_UNIVERSE;
fl4.flowi4_tun_key.tun_id = 0;
fl4.flowi4_flags = 0;
+ fl4.flowi4_uid = sock_net_uid(net, NULL);
no_addr = idev->ifa_list == NULL;
@@ -339,13 +340,12 @@
trace_fib_validate_source(dev, &fl4);
- net = dev_net(dev);
if (fib_lookup(net, &fl4, &res, 0))
goto last_resort;
if (res.type != RTN_UNICAST &&
(res.type != RTN_LOCAL || !IN_DEV_ACCEPT_LOCAL(idev)))
goto e_inval;
- if (!rpf && !fib_num_tclassid_users(dev_net(dev)) &&
+ if (!rpf && !fib_num_tclassid_users(net) &&
(dev->ifindex != oif || !IN_DEV_TX_REDIRECTS(idev)))
goto last_resort;
fib_combine_itag(itag, &res);
diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c
index 5d7944f..b120b9b 100644
--- a/net/ipv4/ip_vti.c
+++ b/net/ipv4/ip_vti.c
@@ -168,6 +168,7 @@
struct ip_tunnel_parm *parms = &tunnel->parms;
struct dst_entry *dst = skb_dst(skb);
struct net_device *tdev; /* Device to other host */
+ int pkt_len = skb->len;
int err;
int mtu;
@@ -229,7 +230,7 @@
err = dst_output(tunnel->net, skb->sk, skb);
if (net_xmit_eval(err) == 0)
- err = skb->len;
+ err = pkt_len;
iptunnel_xmit_stats(dev, err);
return NETDEV_TX_OK;
diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic.c b/net/ipv4/netfilter/nf_nat_snmp_basic.c
index c9b52c3..5a8f7c3 100644
--- a/net/ipv4/netfilter/nf_nat_snmp_basic.c
+++ b/net/ipv4/netfilter/nf_nat_snmp_basic.c
@@ -1304,6 +1304,7 @@
static void __exit nf_nat_snmp_basic_fini(void)
{
RCU_INIT_POINTER(nf_nat_snmp_hook, NULL);
+ synchronize_rcu();
nf_conntrack_helper_unregister(&snmp_trap_helper);
}
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index a4faf30..cd632e6 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1860,6 +1860,7 @@
fl4.flowi4_flags = 0;
fl4.daddr = daddr;
fl4.saddr = saddr;
+ fl4.flowi4_uid = sock_net_uid(net, NULL);
err = fib_lookup(net, &fl4, &res, 0);
if (err != 0) {
if (!IN_DEV_FORWARD(in_dev))
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index a835716..dd08d16 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -914,6 +914,7 @@
struct tcp_skb_cb *tcb;
struct tcp_out_options opts;
unsigned int tcp_options_size, tcp_header_size;
+ struct sk_buff *oskb = NULL;
struct tcp_md5sig_key *md5;
struct tcphdr *th;
int err;
@@ -922,11 +923,9 @@
tp = tcp_sk(sk);
if (clone_it) {
- skb_mstamp_get(&skb->skb_mstamp);
TCP_SKB_CB(skb)->tx.in_flight = TCP_SKB_CB(skb)->end_seq
- tp->snd_una;
- tcp_rate_skb_sent(sk, skb);
-
+ oskb = skb;
if (unlikely(skb_cloned(skb)))
skb = pskb_copy(skb, gfp_mask);
else
@@ -934,6 +933,7 @@
if (unlikely(!skb))
return -ENOBUFS;
}
+ skb_mstamp_get(&skb->skb_mstamp);
inet = inet_sk(sk);
tcb = TCP_SKB_CB(skb);
@@ -1035,12 +1035,15 @@
err = icsk->icsk_af_ops->queue_xmit(sk, skb, &inet->cork.fl);
- if (likely(err <= 0))
- return err;
-
- tcp_enter_cwr(sk);
-
- return net_xmit_eval(err);
+ if (unlikely(err > 0)) {
+ tcp_enter_cwr(sk);
+ err = net_xmit_eval(err);
+ }
+ if (!err && oskb) {
+ skb_mstamp_get(&oskb->skb_mstamp);
+ tcp_rate_skb_sent(sk, oskb);
+ }
+ return err;
}
/* This routine just queues the buffer for sending.
@@ -2709,10 +2712,11 @@
skb_headroom(skb) >= 0xFFFF)) {
struct sk_buff *nskb;
- skb_mstamp_get(&skb->skb_mstamp);
nskb = __pskb_copy(skb, MAX_TCP_HEADER, GFP_ATOMIC);
err = nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) :
-ENOBUFS;
+ if (!err)
+ skb_mstamp_get(&skb->skb_mstamp);
} else {
err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);
}
@@ -3325,6 +3329,10 @@
goto done;
}
+ /* data was not sent, this is our new send_head */
+ sk->sk_send_head = syn_data;
+ tp->packets_out -= tcp_skb_pcount(syn_data);
+
fallback:
/* Send a regular SYN with Fast Open cookie request option */
if (fo->cookie.len > 0)
@@ -3374,6 +3382,11 @@
*/
tp->snd_nxt = tp->write_seq;
tp->pushed_seq = tp->write_seq;
+ buff = tcp_send_head(sk);
+ if (unlikely(buff)) {
+ tp->snd_nxt = TCP_SKB_CB(buff)->seq;
+ tp->pushed_seq = TCP_SKB_CB(buff)->seq;
+ }
TCP_INC_STATS(sock_net(sk), TCP_MIB_ACTIVEOPENS);
/* Timer for repeating the SYN until an answer. */
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index 6de016f..0932c85 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -29,6 +29,7 @@
u16 mac_len = skb->mac_len;
int udp_offset, outer_hlen;
__wsum partial;
+ bool need_ipsec;
if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
goto out;
@@ -62,8 +63,10 @@
ufo = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP);
+ need_ipsec = skb_dst(skb) && dst_xfrm(skb_dst(skb));
/* Try to offload checksum if possible */
offload_csum = !!(need_csum &&
+ !need_ipsec &&
(skb->dev->features &
(is_ipv6 ? (NETIF_F_HW_CSUM | NETIF_F_IPV6_CSUM) :
(NETIF_F_HW_CSUM | NETIF_F_IP_CSUM))));
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 5da8649..7ba9a6e 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -914,6 +914,7 @@
fn->fn_flags |= RTN_RTINFO;
}
nsiblings = iter->rt6i_nsiblings;
+ iter->rt6i_node = NULL;
fib6_purge_rt(iter, fn, info->nl_net);
if (fn->rr_ptr == iter)
fn->rr_ptr = NULL;
@@ -928,6 +929,7 @@
break;
if (rt6_qualify_for_ecmp(iter)) {
*ins = iter->dst.rt6_next;
+ iter->rt6i_node = NULL;
fib6_purge_rt(iter, fn, info->nl_net);
if (fn->rr_ptr == iter)
fn->rr_ptr = NULL;
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index ae87b9a..48e6e75 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -940,24 +940,25 @@
}
static int ip6gre_header(struct sk_buff *skb, struct net_device *dev,
- unsigned short type,
- const void *daddr, const void *saddr, unsigned int len)
+ unsigned short type, const void *daddr,
+ const void *saddr, unsigned int len)
{
struct ip6_tnl *t = netdev_priv(dev);
- struct ipv6hdr *ipv6h = (struct ipv6hdr *)skb_push(skb, t->hlen);
- __be16 *p = (__be16 *)(ipv6h+1);
+ struct ipv6hdr *ipv6h;
+ __be16 *p;
- ip6_flow_hdr(ipv6h, 0,
- ip6_make_flowlabel(dev_net(dev), skb,
- t->fl.u.ip6.flowlabel, true,
- &t->fl.u.ip6));
+ ipv6h = (struct ipv6hdr *)skb_push(skb, t->hlen + sizeof(*ipv6h));
+ ip6_flow_hdr(ipv6h, 0, ip6_make_flowlabel(dev_net(dev), skb,
+ t->fl.u.ip6.flowlabel,
+ true, &t->fl.u.ip6));
ipv6h->hop_limit = t->parms.hop_limit;
ipv6h->nexthdr = NEXTHDR_GRE;
ipv6h->saddr = t->parms.laddr;
ipv6h->daddr = t->parms.raddr;
- p[0] = t->parms.o_flags;
- p[1] = htons(type);
+ p = (__be16 *)(ipv6h + 1);
+ p[0] = t->parms.o_flags;
+ p[1] = htons(type);
/*
* Set the source hardware address.
@@ -1301,6 +1302,7 @@
dev->features |= NETIF_F_NETNS_LOCAL;
dev->priv_flags &= ~IFF_TX_SKB_SHARING;
dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+ netif_keep_dst(dev);
}
static bool ip6gre_netlink_encap_parms(struct nlattr *data[],
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index b44e9f5..64aefc2 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1042,6 +1042,7 @@
struct dst_entry *dst = NULL, *ndst = NULL;
struct net_device *tdev;
int mtu;
+ unsigned int eth_hlen = t->dev->type == ARPHRD_ETHER ? ETH_HLEN : 0;
unsigned int psh_hlen = sizeof(struct ipv6hdr) + t->encap_hlen;
unsigned int max_headroom = psh_hlen;
bool use_cache = false;
@@ -1120,7 +1121,7 @@
t->parms.name);
goto tx_err_dst_release;
}
- mtu = dst_mtu(dst) - psh_hlen - t->tun_hlen;
+ mtu = dst_mtu(dst) - eth_hlen - psh_hlen - t->tun_hlen;
if (encap_limit >= 0) {
max_headroom += 8;
mtu -= 8;
@@ -1129,7 +1130,7 @@
mtu = IPV6_MIN_MTU;
if (skb_dst(skb) && !t->parms.collect_md)
skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
- if (skb->len - t->tun_hlen > mtu && !skb_is_gso(skb)) {
+ if (skb->len - t->tun_hlen - eth_hlen > mtu && !skb_is_gso(skb)) {
*pmtu = mtu;
err = -EMSGSIZE;
goto tx_err_dst_release;
@@ -2235,6 +2236,9 @@
{
int err;
+ if (!ipv6_mod_enabled())
+ return -EOPNOTSUPP;
+
err = register_pernet_device(&ip6_tnl_net_ops);
if (err < 0)
goto out_pernet;
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index bbeedff..da64b20 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -445,6 +445,7 @@
struct dst_entry *dst = skb_dst(skb);
struct net_device *tdev;
struct xfrm_state *x;
+ int pkt_len = skb->len;
int err = -1;
int mtu;
@@ -498,7 +499,7 @@
struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
u64_stats_update_begin(&tstats->syncp);
- tstats->tx_bytes += skb->len;
+ tstats->tx_bytes += pkt_len;
tstats->tx_packets++;
u64_stats_update_end(&tstats->syncp);
} else {
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 3bce651..b06acd0 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -1415,6 +1415,9 @@
struct sock *sk = NULL;
tunnel = container_of(work, struct l2tp_tunnel, del_work);
+
+ l2tp_tunnel_closeall(tunnel);
+
sk = l2tp_tunnel_sock_lookup(tunnel);
if (!sk)
goto out;
@@ -1734,15 +1737,12 @@
/* This function is used by the netlink TUNNEL_DELETE command.
*/
-int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel)
+void l2tp_tunnel_delete(struct l2tp_tunnel *tunnel)
{
- l2tp_tunnel_inc_refcount(tunnel);
- l2tp_tunnel_closeall(tunnel);
- if (false == queue_work(l2tp_wq, &tunnel->del_work)) {
- l2tp_tunnel_dec_refcount(tunnel);
- return 1;
+ if (!test_and_set_bit(0, &tunnel->dead)) {
+ l2tp_tunnel_inc_refcount(tunnel);
+ queue_work(l2tp_wq, &tunnel->del_work);
}
- return 0;
}
EXPORT_SYMBOL_GPL(l2tp_tunnel_delete);
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h
index 0095012..42419f1 100644
--- a/net/l2tp/l2tp_core.h
+++ b/net/l2tp/l2tp_core.h
@@ -169,6 +169,9 @@
struct l2tp_tunnel {
int magic; /* Should be L2TP_TUNNEL_MAGIC */
+
+ unsigned long dead;
+
struct rcu_head rcu;
rwlock_t hlist_lock; /* protect session_hlist */
struct hlist_head session_hlist[L2TP_HASH_SIZE];
@@ -257,7 +260,7 @@
u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg,
struct l2tp_tunnel **tunnelp);
void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel);
-int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel);
+void l2tp_tunnel_delete(struct l2tp_tunnel *tunnel);
struct l2tp_session *l2tp_session_create(int priv_size,
struct l2tp_tunnel *tunnel,
u32 session_id, u32 peer_session_id,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 37bec0f..a7aa54f 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -791,6 +791,7 @@
static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
bool going_down)
{
+ struct ieee80211_sub_if_data *txq_sdata = sdata;
struct ieee80211_local *local = sdata->local;
struct fq *fq = &local->fq;
unsigned long flags;
@@ -931,6 +932,9 @@
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
+ txq_sdata = container_of(sdata->bss,
+ struct ieee80211_sub_if_data, u.ap);
+
mutex_lock(&local->mtx);
list_del(&sdata->u.vlan.list);
mutex_unlock(&local->mtx);
@@ -1001,8 +1005,17 @@
}
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
- if (sdata->vif.txq) {
- struct txq_info *txqi = to_txq_info(sdata->vif.txq);
+ if (txq_sdata->vif.txq) {
+ struct txq_info *txqi = to_txq_info(txq_sdata->vif.txq);
+
+ /*
+ * FIXME FIXME
+ *
+ * We really shouldn't purge the *entire* txqi since that
+ * contains frames for the other AP_VLANs (and possibly
+ * the AP itself) as well, but there's no API in FQ now
+ * to be able to filter.
+ */
spin_lock_bh(&fq->lock);
ieee80211_txq_purge(local, txqi);
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index eede5c6..30bba53 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -707,6 +707,8 @@
if (!cookie)
return -ENOENT;
+ flush_work(&local->hw_roc_start);
+
mutex_lock(&local->mtx);
list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
if (!mgmt_tx && roc->cookie != cookie)
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index b2c823ff..348700b4 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -688,7 +688,7 @@
}
/* No need to do anything if the driver does all */
- if (ieee80211_hw_check(&local->hw, AP_LINK_PS))
+ if (ieee80211_hw_check(&local->hw, AP_LINK_PS) && !local->ops->set_tim)
return;
if (sta->dead)
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index dd190ff..274c564 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1277,11 +1277,6 @@
IEEE80211_SKB_CB(skb)->control.enqueue_time = codel_get_time();
}
-static void ieee80211_set_skb_vif(struct sk_buff *skb, struct txq_info *txqi)
-{
- IEEE80211_SKB_CB(skb)->control.vif = txqi->txq.vif;
-}
-
static u32 codel_skb_len_func(const struct sk_buff *skb)
{
return skb->len;
@@ -3388,6 +3383,7 @@
struct ieee80211_tx_info *info;
struct ieee80211_tx_data tx;
ieee80211_tx_result r;
+ struct ieee80211_vif *vif;
spin_lock_bh(&fq->lock);
@@ -3404,8 +3400,6 @@
if (!skb)
goto out;
- ieee80211_set_skb_vif(skb, txqi);
-
hdr = (struct ieee80211_hdr *)skb->data;
info = IEEE80211_SKB_CB(skb);
@@ -3462,6 +3456,34 @@
}
}
+ switch (tx.sdata->vif.type) {
+ case NL80211_IFTYPE_MONITOR:
+ if (tx.sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
+ vif = &tx.sdata->vif;
+ break;
+ }
+ tx.sdata = rcu_dereference(local->monitor_sdata);
+ if (tx.sdata) {
+ vif = &tx.sdata->vif;
+ info->hw_queue =
+ vif->hw_queue[skb_get_queue_mapping(skb)];
+ } else if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) {
+ ieee80211_free_txskb(&local->hw, skb);
+ goto begin;
+ } else {
+ vif = NULL;
+ }
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ tx.sdata = container_of(tx.sdata->bss,
+ struct ieee80211_sub_if_data, u.ap);
+ /* fall through */
+ default:
+ vif = &tx.sdata->vif;
+ break;
+ }
+
+ IEEE80211_SKB_CB(skb)->control.vif = vif;
out:
spin_unlock_bh(&fq->lock);
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index 612f28a..072e80a 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -95,19 +95,26 @@
void nf_conntrack_lock(spinlock_t *lock) __acquires(lock)
{
+ /* 1) Acquire the lock */
spin_lock(lock);
- while (unlikely(nf_conntrack_locks_all)) {
- spin_unlock(lock);
- /*
- * Order the 'nf_conntrack_locks_all' load vs. the
- * spin_unlock_wait() loads below, to ensure
- * that 'nf_conntrack_locks_all_lock' is indeed held:
- */
- smp_rmb(); /* spin_lock(&nf_conntrack_locks_all_lock) */
- spin_unlock_wait(&nf_conntrack_locks_all_lock);
- spin_lock(lock);
- }
+ /* 2) read nf_conntrack_locks_all, with ACQUIRE semantics
+ * It pairs with the smp_store_release() in nf_conntrack_all_unlock()
+ */
+ if (likely(smp_load_acquire(&nf_conntrack_locks_all) == false))
+ return;
+
+ /* fast path failed, unlock */
+ spin_unlock(lock);
+
+ /* Slow path 1) get global lock */
+ spin_lock(&nf_conntrack_locks_all_lock);
+
+ /* Slow path 2) get the lock we want */
+ spin_lock(lock);
+
+ /* Slow path 3) release the global lock */
+ spin_unlock(&nf_conntrack_locks_all_lock);
}
EXPORT_SYMBOL_GPL(nf_conntrack_lock);
@@ -148,28 +155,27 @@
int i;
spin_lock(&nf_conntrack_locks_all_lock);
+
nf_conntrack_locks_all = true;
- /*
- * Order the above store of 'nf_conntrack_locks_all' against
- * the spin_unlock_wait() loads below, such that if
- * nf_conntrack_lock() observes 'nf_conntrack_locks_all'
- * we must observe nf_conntrack_locks[] held:
- */
- smp_mb(); /* spin_lock(&nf_conntrack_locks_all_lock) */
-
for (i = 0; i < CONNTRACK_LOCKS; i++) {
- spin_unlock_wait(&nf_conntrack_locks[i]);
+ spin_lock(&nf_conntrack_locks[i]);
+
+ /* This spin_unlock provides the "release" to ensure that
+ * nf_conntrack_locks_all==true is visible to everyone that
+ * acquired spin_lock(&nf_conntrack_locks[]).
+ */
+ spin_unlock(&nf_conntrack_locks[i]);
}
}
static void nf_conntrack_all_unlock(void)
{
- /*
- * All prior stores must be complete before we clear
+ /* All prior stores must be complete before we clear
* 'nf_conntrack_locks_all'. Otherwise nf_conntrack_lock()
* might observe the false value but not the entire
- * critical section:
+ * critical section.
+ * It pairs with the smp_load_acquire() in nf_conntrack_lock()
*/
smp_store_release(&nf_conntrack_locks_all, false);
spin_unlock(&nf_conntrack_locks_all_lock);
diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c
index da9df2d..22fc321 100644
--- a/net/netfilter/nf_conntrack_ecache.c
+++ b/net/netfilter/nf_conntrack_ecache.c
@@ -290,6 +290,7 @@
BUG_ON(notify != new);
RCU_INIT_POINTER(net->ct.nf_conntrack_event_cb, NULL);
mutex_unlock(&nf_ct_ecache_mutex);
+ /* synchronize_rcu() is called from ctnetlink_exit. */
}
EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier);
@@ -326,6 +327,7 @@
BUG_ON(notify != new);
RCU_INIT_POINTER(net->ct.nf_expect_event_cb, NULL);
mutex_unlock(&nf_ct_ecache_mutex);
+ /* synchronize_rcu() is called from ctnetlink_exit. */
}
EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier);
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index f8dbacf..0d6c72d 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -411,7 +411,7 @@
struct net *net = nf_ct_exp_net(expect);
struct hlist_node *next;
unsigned int h;
- int ret = 1;
+ int ret = 0;
if (!master_help) {
ret = -ESHUTDOWN;
@@ -461,7 +461,7 @@
spin_lock_bh(&nf_conntrack_expect_lock);
ret = __nf_ct_expect_check(expect);
- if (ret <= 0)
+ if (ret < 0)
goto out;
ret = nf_ct_expect_insert(expect);
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 08b24a9..6bd58eea 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -3417,6 +3417,7 @@
#ifdef CONFIG_NETFILTER_NETLINK_GLUE_CT
RCU_INIT_POINTER(nfnl_ct_hook, NULL);
#endif
+ synchronize_rcu();
}
module_init(ctnetlink_init);
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 3a8dc39..f132ef9 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -49,13 +49,28 @@
MODULE_PARM_DESC(sip_direct_signalling, "expect incoming calls from registrar "
"only (default 1)");
-static int sip_direct_media __read_mostly = 1;
-module_param(sip_direct_media, int, 0600);
-MODULE_PARM_DESC(sip_direct_media, "Expect Media streams between signalling "
- "endpoints only (default 1)");
-
const struct nf_nat_sip_hooks *nf_nat_sip_hooks;
EXPORT_SYMBOL_GPL(nf_nat_sip_hooks);
+static struct ctl_table_header *sip_sysctl_header;
+static unsigned int nf_ct_disable_sip_alg;
+static int sip_direct_media = 1;
+static struct ctl_table sip_sysctl_tbl[] = {
+ {
+ .procname = "nf_conntrack_disable_sip_alg",
+ .data = &nf_ct_disable_sip_alg,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .procname = "nf_conntrack_sip_direct_media",
+ .data = &sip_direct_media,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {}
+};
static int string_len(const struct nf_conn *ct, const char *dptr,
const char *limit, int *shift)
@@ -1467,6 +1482,8 @@
const struct nf_nat_sip_hooks *hooks;
int ret;
+ if (nf_ct_disable_sip_alg)
+ return NF_ACCEPT;
if (strncasecmp(*dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0)
ret = process_sip_request(skb, protoff, dataoff, dptr, datalen);
else
@@ -1626,6 +1643,16 @@
{
int i, ret;
+ sip_sysctl_header = register_net_sysctl(&init_net, "net/netfilter",
+ sip_sysctl_tbl);
+ if (!sip_sysctl_header)
+ pr_debug("nf_ct_sip:Unable to register SIP systbl\n");
+
+ if (nf_ct_disable_sip_alg)
+ pr_debug("nf_ct_sip: SIP ALG disabled\n");
+ else
+ pr_debug("nf_ct_sip: SIP ALG enabled\n");
+
if (ports_c == 0)
ports[ports_c++] = SIP_PORT;
diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c
index dde64c4..2916f48 100644
--- a/net/netfilter/nf_nat_core.c
+++ b/net/netfilter/nf_nat_core.c
@@ -892,6 +892,8 @@
#ifdef CONFIG_XFRM
RCU_INIT_POINTER(nf_nat_decode_session_hook, NULL);
#endif
+ synchronize_rcu();
+
for (i = 0; i < NFPROTO_NUMPROTO; i++)
kfree(nf_nat_l4protos[i]);
diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c
index 3b79f34..b1fcfa0 100644
--- a/net/netfilter/nfnetlink_cthelper.c
+++ b/net/netfilter/nfnetlink_cthelper.c
@@ -161,6 +161,7 @@
int i, ret;
struct nf_conntrack_expect_policy *expect_policy;
struct nlattr *tb[NFCTH_POLICY_SET_MAX+1];
+ unsigned int class_max;
ret = nla_parse_nested(tb, NFCTH_POLICY_SET_MAX, attr,
nfnl_cthelper_expect_policy_set);
@@ -170,19 +171,18 @@
if (!tb[NFCTH_POLICY_SET_NUM])
return -EINVAL;
- helper->expect_class_max =
- ntohl(nla_get_be32(tb[NFCTH_POLICY_SET_NUM]));
-
- if (helper->expect_class_max != 0 &&
- helper->expect_class_max > NF_CT_MAX_EXPECT_CLASSES)
+ class_max = ntohl(nla_get_be32(tb[NFCTH_POLICY_SET_NUM]));
+ if (class_max == 0)
+ return -EINVAL;
+ if (class_max > NF_CT_MAX_EXPECT_CLASSES)
return -EOVERFLOW;
expect_policy = kzalloc(sizeof(struct nf_conntrack_expect_policy) *
- helper->expect_class_max, GFP_KERNEL);
+ class_max, GFP_KERNEL);
if (expect_policy == NULL)
return -ENOMEM;
- for (i=0; i<helper->expect_class_max; i++) {
+ for (i = 0; i < class_max; i++) {
if (!tb[NFCTH_POLICY_SET+i])
goto err;
@@ -191,6 +191,8 @@
if (ret < 0)
goto err;
}
+
+ helper->expect_class_max = class_max - 1;
helper->expect_policy = expect_policy;
return 0;
err:
@@ -377,10 +379,10 @@
goto nla_put_failure;
if (nla_put_be32(skb, NFCTH_POLICY_SET_NUM,
- htonl(helper->expect_class_max)))
+ htonl(helper->expect_class_max + 1)))
goto nla_put_failure;
- for (i=0; i<helper->expect_class_max; i++) {
+ for (i = 0; i < helper->expect_class_max + 1; i++) {
nest_parms2 = nla_nest_start(skb,
(NFCTH_POLICY_SET+i) | NLA_F_NESTED);
if (nest_parms2 == NULL)
diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c
index 139e086..47d6656 100644
--- a/net/netfilter/nfnetlink_cttimeout.c
+++ b/net/netfilter/nfnetlink_cttimeout.c
@@ -646,8 +646,8 @@
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, NULL);
RCU_INIT_POINTER(nf_ct_timeout_put_hook, NULL);
+ synchronize_rcu();
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
- rcu_barrier();
}
module_init(cttimeout_init);
diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c
index 1cf2874..822be06 100644
--- a/net/netfilter/xt_qtaguid.c
+++ b/net/netfilter/xt_qtaguid.c
@@ -1174,6 +1174,38 @@
spin_unlock_bh(&iface_stat_list_lock);
}
+/* Guarantied to return a net_device that has a name */
+static void get_dev_and_dir(const struct sk_buff *skb,
+ struct xt_action_param *par,
+ enum ifs_tx_rx *direction,
+ const struct net_device **el_dev)
+{
+ BUG_ON(!direction || !el_dev);
+
+ if (par->in) {
+ *el_dev = par->in;
+ *direction = IFS_RX;
+ } else if (par->out) {
+ *el_dev = par->out;
+ *direction = IFS_TX;
+ } else {
+ pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n",
+ par->hooknum, __func__);
+ BUG();
+ }
+ if (unlikely(!(*el_dev)->name)) {
+ pr_err("qtaguid[%d]: %s(): no dev->name?!!\n",
+ par->hooknum, __func__);
+ BUG();
+ }
+ if (skb->dev && *el_dev != skb->dev) {
+ MT_DEBUG("qtaguid[%d]: skb->dev=%pK %s vs par->%s=%pK %s\n",
+ par->hooknum, skb->dev, skb->dev->name,
+ *direction == IFS_RX ? "in" : "out", *el_dev,
+ (*el_dev)->name);
+ }
+}
+
/*
* Update stats for the specified interface from the skb.
* Do nothing if the entry
@@ -1185,46 +1217,27 @@
{
struct iface_stat *entry;
const struct net_device *el_dev;
- enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX;
+ enum ifs_tx_rx direction;
int bytes = skb->len;
int proto;
- if (!skb->dev) {
- MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum);
- el_dev = par->in ? : par->out;
- } else {
- const struct net_device *other_dev;
- el_dev = skb->dev;
- other_dev = par->in ? : par->out;
- if (el_dev != other_dev) {
- MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs "
- "par->(in/out)=%p %s\n",
- par->hooknum, el_dev, el_dev->name, other_dev,
- other_dev->name);
- }
- }
-
- if (unlikely(!el_dev)) {
- pr_err_ratelimited("qtaguid[%d]: %s(): no par->in/out?!!\n",
- par->hooknum, __func__);
- BUG();
- } else {
- proto = ipx_proto(skb, par);
- MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n",
- par->hooknum, el_dev->name, el_dev->type,
- par->family, proto);
- }
+ get_dev_and_dir(skb, par, &direction, &el_dev);
+ proto = ipx_proto(skb, par);
+ MT_DEBUG("qtaguid[%d]: iface_stat: %s(%s): "
+ "type=%d fam=%d proto=%d dir=%d\n",
+ par->hooknum, __func__, el_dev->name, el_dev->type,
+ par->family, proto, direction);
spin_lock_bh(&iface_stat_list_lock);
entry = get_iface_entry(el_dev->name);
if (entry == NULL) {
- IF_DEBUG("qtaguid: iface_stat: %s(%s): not tracked\n",
- __func__, el_dev->name);
+ IF_DEBUG("qtaguid[%d]: iface_stat: %s(%s): not tracked\n",
+ par->hooknum, __func__, el_dev->name);
spin_unlock_bh(&iface_stat_list_lock);
return;
}
- IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__,
+ IF_DEBUG("qtaguid[%d]: %s(%s): entry=%p\n", par->hooknum, __func__,
el_dev->name, entry);
data_counters_update(&entry->totals_via_skb, 0, direction, proto,
@@ -1289,14 +1302,14 @@
spin_lock_bh(&iface_stat_list_lock);
iface_entry = get_iface_entry(ifname);
if (!iface_entry) {
- pr_err_ratelimited("qtaguid: iface_stat: stat_update() "
+ pr_err_ratelimited("qtaguid: tag_stat: stat_update() "
"%s not found\n", ifname);
spin_unlock_bh(&iface_stat_list_lock);
return;
}
/* It is ok to process data when an iface_entry is inactive */
- MT_DEBUG("qtaguid: iface_stat: stat_update() dev=%s entry=%p\n",
+ MT_DEBUG("qtaguid: tag_stat: stat_update() dev=%s entry=%pK\n",
ifname, iface_entry);
/*
@@ -1313,8 +1326,8 @@
tag = combine_atag_with_uid(acct_tag, uid);
uid_tag = make_tag_from_uid(uid);
}
- MT_DEBUG("qtaguid: iface_stat: stat_update(): "
- " looking for tag=0x%llx (uid=%u) in ife=%p\n",
+ MT_DEBUG("qtaguid: tag_stat: stat_update(): "
+ " looking for tag=0x%llx (uid=%u) in ife=%pK\n",
tag, get_uid_from_tag(tag), iface_entry);
/* Loop over tag list under this interface for {acct_tag,uid_tag} */
spin_lock_bh(&iface_entry->tag_stat_list_lock);
@@ -1573,8 +1586,8 @@
struct sock *sk;
unsigned int hook_mask = (1 << par->hooknum);
- MT_DEBUG("qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb,
- par->hooknum, par->family);
+ MT_DEBUG("qtaguid[%d]: find_sk(skb=%pK) family=%d\n",
+ par->hooknum, skb, par->family);
/*
* Let's not abuse the the xt_socket_get*_sk(), or else it will
@@ -1595,8 +1608,8 @@
}
if (sk) {
- MT_DEBUG("qtaguid: %p->sk_proto=%u "
- "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state);
+ MT_DEBUG("qtaguid[%d]: %pK->sk_proto=%u->sk_state=%d\n",
+ par->hooknum, sk, sk->sk_protocol, sk->sk_state);
}
return sk;
}
@@ -1606,35 +1619,19 @@
struct xt_action_param *par)
{
const struct net_device *el_dev;
+ enum ifs_tx_rx direction;
+ int proto;
- if (!skb->dev) {
- MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum);
- el_dev = par->in ? : par->out;
- } else {
- const struct net_device *other_dev;
- el_dev = skb->dev;
- other_dev = par->in ? : par->out;
- if (el_dev != other_dev) {
- MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs "
- "par->(in/out)=%p %s\n",
- par->hooknum, el_dev, el_dev->name, other_dev,
- other_dev->name);
- }
- }
+ get_dev_and_dir(skb, par, &direction, &el_dev);
+ proto = ipx_proto(skb, par);
+ MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d dir=%d\n",
+ par->hooknum, el_dev->name, el_dev->type,
+ par->family, proto, direction);
- if (unlikely(!el_dev)) {
- pr_info("qtaguid[%d]: no par->in/out?!!\n", par->hooknum);
- } else {
- int proto = ipx_proto(skb, par);
- MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n",
- par->hooknum, el_dev->name, el_dev->type,
- par->family, proto);
-
- if_tag_stat_update(el_dev->name, uid,
- skb->sk ? skb->sk : alternate_sk,
- par->in ? IFS_RX : IFS_TX,
- proto, skb->len);
- }
+ if_tag_stat_update(el_dev->name, uid,
+ skb->sk ? skb->sk : alternate_sk,
+ direction,
+ proto, skb->len);
}
static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
@@ -1646,6 +1643,11 @@
kuid_t sock_uid;
bool res;
bool set_sk_callback_lock = false;
+ /*
+ * TODO: unhack how to force just accounting.
+ * For now we only do tag stats when the uid-owner is not requested
+ */
+ bool do_tag_stat = !(info->match & XT_QTAGUID_UID);
if (unlikely(module_passive))
return (info->match ^ info->invert) == 0;
@@ -1718,19 +1720,13 @@
MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n",
par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par));
-
- if (sk == NULL) {
+ if (!sk) {
/*
* Here, the qtaguid_find_sk() using connection tracking
* couldn't find the owner, so for now we just count them
* against the system.
*/
- /*
- * TODO: unhack how to force just accounting.
- * For now we only do iface stats when the uid-owner is not
- * requested.
- */
- if (!(info->match & XT_QTAGUID_UID))
+ if (do_tag_stat)
account_for_uid(skb, sk, 0, par);
MT_DEBUG("qtaguid[%d]: leaving (sk=NULL)\n", par->hooknum);
res = (info->match ^ info->invert) == 0;
@@ -1741,12 +1737,9 @@
goto put_sock_ret_res;
}
sock_uid = sk->sk_uid;
- /*
- * TODO: unhack how to force just accounting.
- * For now we only do iface stats when the uid-owner is not requested
- */
- if (!(info->match & XT_QTAGUID_UID))
- account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), par);
+ if (do_tag_stat)
+ account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid),
+ par);
/*
* The following two tests fail the match when:
@@ -1758,8 +1751,8 @@
kuid_t uid_min = make_kuid(&init_user_ns, info->uid_min);
kuid_t uid_max = make_kuid(&init_user_ns, info->uid_max);
- if ((uid_gte(sk->sk_uid, uid_min) &&
- uid_lte(sk->sk_uid, uid_max)) ^
+ if ((uid_gte(sock_uid, uid_min) &&
+ uid_lte(sock_uid, uid_max)) ^
!(info->invert & XT_QTAGUID_UID)) {
MT_DEBUG("qtaguid[%d]: leaving uid not matching\n",
par->hooknum);
@@ -1773,16 +1766,18 @@
set_sk_callback_lock = true;
read_lock_bh(&sk->sk_callback_lock);
MT_DEBUG("qtaguid[%d]: sk=%pK->sk_socket=%pK->file=%pK\n",
- par->hooknum, sk, sk->sk_socket,
- sk->sk_socket ? sk->sk_socket->file : (void *)-1LL);
+ par->hooknum, sk, sk->sk_socket,
+ sk->sk_socket ? sk->sk_socket->file : (void *)-1LL);
filp = sk->sk_socket ? sk->sk_socket->file : NULL;
if (!filp) {
- res = ((info->match ^ info->invert) & XT_QTAGUID_GID) == 0;
+ res = ((info->match ^ info->invert) &
+ XT_QTAGUID_GID) == 0;
atomic64_inc(&qtu_events.match_no_sk_gid);
goto put_sock_ret_res;
}
MT_DEBUG("qtaguid[%d]: filp...uid=%u\n",
- par->hooknum, filp ? from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1);
+ par->hooknum, filp ?
+ from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1);
if ((gid_gte(filp->f_cred->fsgid, gid_min) &&
gid_lte(filp->f_cred->fsgid, gid_max)) ^
!(info->invert & XT_QTAGUID_GID)) {
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 246f29d..2a5775f 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -2211,10 +2211,13 @@
mutex_unlock(nlk->cb_mutex);
+ ret = 0;
if (cb->start)
- cb->start(cb);
+ ret = cb->start(cb);
- ret = netlink_dump(sk);
+ if (!ret)
+ ret = netlink_dump(sk);
+
sock_put(sk);
if (ret)
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 35ba4b6..b17f909 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1648,10 +1648,6 @@
mutex_lock(&fanout_mutex);
- err = -EINVAL;
- if (!po->running)
- goto out;
-
err = -EALREADY;
if (po->fanout)
goto out;
@@ -1700,7 +1696,10 @@
list_add(&match->list, &fanout_list);
}
err = -EINVAL;
- if (match->type == type &&
+
+ spin_lock(&po->bind_lock);
+ if (po->running &&
+ match->type == type &&
match->prot_hook.type == po->prot_hook.type &&
match->prot_hook.dev == po->prot_hook.dev) {
err = -ENOSPC;
@@ -1712,6 +1711,13 @@
err = 0;
}
}
+ spin_unlock(&po->bind_lock);
+
+ if (err && !atomic_read(&match->sk_ref)) {
+ list_del(&match->list);
+ kfree(match);
+ }
+
out:
if (err && rollover) {
kfree(rollover);
@@ -2832,6 +2838,7 @@
struct virtio_net_hdr vnet_hdr = { 0 };
int offset = 0;
struct packet_sock *po = pkt_sk(sk);
+ bool has_vnet_hdr = false;
int hlen, tlen, linear;
int extra_len = 0;
@@ -2875,6 +2882,7 @@
err = packet_snd_vnet_parse(msg, &len, &vnet_hdr);
if (err)
goto out_unlock;
+ has_vnet_hdr = true;
}
if (unlikely(sock_flag(sk, SOCK_NOFCS))) {
@@ -2935,7 +2943,7 @@
packet_pick_tx_queue(dev, skb);
- if (po->has_vnet_hdr) {
+ if (has_vnet_hdr) {
err = packet_snd_vnet_gso(skb, &vnet_hdr);
if (err)
goto out_free;
@@ -3063,13 +3071,15 @@
int ret = 0;
bool unlisted = false;
- if (po->fanout)
- return -EINVAL;
-
lock_sock(sk);
spin_lock(&po->bind_lock);
rcu_read_lock();
+ if (po->fanout) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
if (name) {
dev = dev_get_by_name_rcu(sock_net(sk), name);
if (!dev) {
@@ -3884,6 +3894,8 @@
case PACKET_HDRLEN:
if (len > sizeof(int))
len = sizeof(int);
+ if (len < sizeof(int))
+ return -EINVAL;
if (copy_from_user(&val, optval, len))
return -EFAULT;
switch (val) {
diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
index 5b2ab95..169156c 100644
--- a/net/rds/ib_cm.c
+++ b/net/rds/ib_cm.c
@@ -405,7 +405,7 @@
ret = PTR_ERR(ic->i_send_cq);
ic->i_send_cq = NULL;
rdsdebug("ib_create_cq send failed: %d\n", ret);
- goto out;
+ goto rds_ibdev_out;
}
cq_attr.cqe = ic->i_recv_ring.w_nr;
@@ -416,19 +416,19 @@
ret = PTR_ERR(ic->i_recv_cq);
ic->i_recv_cq = NULL;
rdsdebug("ib_create_cq recv failed: %d\n", ret);
- goto out;
+ goto send_cq_out;
}
ret = ib_req_notify_cq(ic->i_send_cq, IB_CQ_NEXT_COMP);
if (ret) {
rdsdebug("ib_req_notify_cq send failed: %d\n", ret);
- goto out;
+ goto recv_cq_out;
}
ret = ib_req_notify_cq(ic->i_recv_cq, IB_CQ_SOLICITED);
if (ret) {
rdsdebug("ib_req_notify_cq recv failed: %d\n", ret);
- goto out;
+ goto recv_cq_out;
}
/* XXX negotiate max send/recv with remote? */
@@ -453,7 +453,7 @@
ret = rdma_create_qp(ic->i_cm_id, ic->i_pd, &attr);
if (ret) {
rdsdebug("rdma_create_qp failed: %d\n", ret);
- goto out;
+ goto recv_cq_out;
}
ic->i_send_hdrs = ib_dma_alloc_coherent(dev,
@@ -463,7 +463,7 @@
if (!ic->i_send_hdrs) {
ret = -ENOMEM;
rdsdebug("ib_dma_alloc_coherent send failed\n");
- goto out;
+ goto qp_out;
}
ic->i_recv_hdrs = ib_dma_alloc_coherent(dev,
@@ -473,7 +473,7 @@
if (!ic->i_recv_hdrs) {
ret = -ENOMEM;
rdsdebug("ib_dma_alloc_coherent recv failed\n");
- goto out;
+ goto send_hdrs_dma_out;
}
ic->i_ack = ib_dma_alloc_coherent(dev, sizeof(struct rds_header),
@@ -481,7 +481,7 @@
if (!ic->i_ack) {
ret = -ENOMEM;
rdsdebug("ib_dma_alloc_coherent ack failed\n");
- goto out;
+ goto recv_hdrs_dma_out;
}
ic->i_sends = vzalloc_node(ic->i_send_ring.w_nr * sizeof(struct rds_ib_send_work),
@@ -489,7 +489,7 @@
if (!ic->i_sends) {
ret = -ENOMEM;
rdsdebug("send allocation failed\n");
- goto out;
+ goto ack_dma_out;
}
ic->i_recvs = vzalloc_node(ic->i_recv_ring.w_nr * sizeof(struct rds_ib_recv_work),
@@ -497,7 +497,7 @@
if (!ic->i_recvs) {
ret = -ENOMEM;
rdsdebug("recv allocation failed\n");
- goto out;
+ goto sends_out;
}
rds_ib_recv_init_ack(ic);
@@ -505,8 +505,33 @@
rdsdebug("conn %p pd %p cq %p %p\n", conn, ic->i_pd,
ic->i_send_cq, ic->i_recv_cq);
-out:
+ return ret;
+
+sends_out:
+ vfree(ic->i_sends);
+ack_dma_out:
+ ib_dma_free_coherent(dev, sizeof(struct rds_header),
+ ic->i_ack, ic->i_ack_dma);
+recv_hdrs_dma_out:
+ ib_dma_free_coherent(dev, ic->i_recv_ring.w_nr *
+ sizeof(struct rds_header),
+ ic->i_recv_hdrs, ic->i_recv_hdrs_dma);
+send_hdrs_dma_out:
+ ib_dma_free_coherent(dev, ic->i_send_ring.w_nr *
+ sizeof(struct rds_header),
+ ic->i_send_hdrs, ic->i_send_hdrs_dma);
+qp_out:
+ rdma_destroy_qp(ic->i_cm_id);
+recv_cq_out:
+ if (!ib_destroy_cq(ic->i_recv_cq))
+ ic->i_recv_cq = NULL;
+send_cq_out:
+ if (!ib_destroy_cq(ic->i_send_cq))
+ ic->i_send_cq = NULL;
+rds_ibdev_out:
+ rds_ib_remove_conn(rds_ibdev, conn);
rds_ib_dev_put(rds_ibdev);
+
return ret;
}
diff --git a/net/rds/ib_send.c b/net/rds/ib_send.c
index 84d90c9..1910981 100644
--- a/net/rds/ib_send.c
+++ b/net/rds/ib_send.c
@@ -69,16 +69,6 @@
complete(rm, notify_status);
}
-static void rds_ib_send_unmap_data(struct rds_ib_connection *ic,
- struct rm_data_op *op,
- int wc_status)
-{
- if (op->op_nents)
- ib_dma_unmap_sg(ic->i_cm_id->device,
- op->op_sg, op->op_nents,
- DMA_TO_DEVICE);
-}
-
static void rds_ib_send_unmap_rdma(struct rds_ib_connection *ic,
struct rm_rdma_op *op,
int wc_status)
@@ -139,6 +129,21 @@
rds_ib_stats_inc(s_ib_atomic_fadd);
}
+static void rds_ib_send_unmap_data(struct rds_ib_connection *ic,
+ struct rm_data_op *op,
+ int wc_status)
+{
+ struct rds_message *rm = container_of(op, struct rds_message, data);
+
+ if (op->op_nents)
+ ib_dma_unmap_sg(ic->i_cm_id->device,
+ op->op_sg, op->op_nents,
+ DMA_TO_DEVICE);
+
+ if (rm->rdma.op_active && rm->data.op_notify)
+ rds_ib_send_unmap_rdma(ic, &rm->rdma, wc_status);
+}
+
/*
* Unmap the resources associated with a struct send_work.
*
diff --git a/net/rds/rdma.c b/net/rds/rdma.c
index 4c93bad..8d3a851 100644
--- a/net/rds/rdma.c
+++ b/net/rds/rdma.c
@@ -626,6 +626,16 @@
}
op->op_notifier->n_user_token = args->user_token;
op->op_notifier->n_status = RDS_RDMA_SUCCESS;
+
+ /* Enable rmda notification on data operation for composite
+ * rds messages and make sure notification is enabled only
+ * for the data operation which follows it so that application
+ * gets notified only after full message gets delivered.
+ */
+ if (rm->data.op_sg) {
+ rm->rdma.op_notify = 0;
+ rm->data.op_notify = !!(args->flags & RDS_RDMA_NOTIFY_ME);
+ }
}
/* The cookie contains the R_Key of the remote memory region, and
diff --git a/net/rds/rds.h b/net/rds/rds.h
index 67ba67c..f107a96 100644
--- a/net/rds/rds.h
+++ b/net/rds/rds.h
@@ -414,6 +414,7 @@
} rdma;
struct rm_data_op {
unsigned int op_active:1;
+ unsigned int op_notify:1;
unsigned int op_nents;
unsigned int op_count;
unsigned int op_dmasg;
diff --git a/net/rds/send.c b/net/rds/send.c
index 896626b..f28651b 100644
--- a/net/rds/send.c
+++ b/net/rds/send.c
@@ -475,12 +475,14 @@
struct rm_rdma_op *ro;
struct rds_notifier *notifier;
unsigned long flags;
+ unsigned int notify = 0;
spin_lock_irqsave(&rm->m_rs_lock, flags);
+ notify = rm->rdma.op_notify | rm->data.op_notify;
ro = &rm->rdma;
if (test_bit(RDS_MSG_ON_SOCK, &rm->m_flags) &&
- ro->op_active && ro->op_notify && ro->op_notifier) {
+ ro->op_active && notify && ro->op_notifier) {
notifier = ro->op_notifier;
rs = rm->m_rs;
sock_hold(rds_rs_to_sk(rs));
diff --git a/net/rmnet_data/rmnet_data_handlers.c b/net/rmnet_data/rmnet_data_handlers.c
index 57646ef..68a4376 100644
--- a/net/rmnet_data/rmnet_data_handlers.c
+++ b/net/rmnet_data/rmnet_data_handlers.c
@@ -184,19 +184,35 @@
return RX_HANDLER_CONSUMED;
}
-#ifdef NET_SKBUFF_DATA_USES_OFFSET
-static void rmnet_reset_mac_header(struct sk_buff *skb)
+/* RX/TX Fixup */
+
+/* rmnet_vnd_rx_fixup() - Virtual Network Device receive fixup hook
+ * @skb: Socket buffer ("packet") to modify
+ * @dev: Virtual network device
+ *
+ * Additional VND specific packet processing for ingress packets
+ *
+ * Return: void
+ */
+static void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev)
{
- skb->mac_header = 0;
- skb->mac_len = 0;
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
}
-#else
-static void rmnet_reset_mac_header(struct sk_buff *skb)
+
+/* rmnet_vnd_tx_fixup() - Virtual Network Device transmic fixup hook
+ * @skb: Socket buffer ("packet") to modify
+ * @dev: Virtual network device
+ *
+ * Additional VND specific packet processing for egress packets
+ *
+ * Return: void
+ */
+static void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev)
{
- skb->mac_header = skb->network_header;
- skb->mac_len = 0;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
}
-#endif /*NET_SKBUFF_DATA_USES_OFFSET*/
/* rmnet_check_skb_can_gro() - Check is skb can be passed through GRO handler
*
@@ -321,42 +337,33 @@
trace___rmnet_deliver_skb(skb);
switch (ep->rmnet_mode) {
+ case RMNET_EPMODE_VND:
+ skb_reset_transport_header(skb);
+ skb_reset_network_header(skb);
+ rmnet_vnd_rx_fixup(skb, skb->dev);
+
+ skb->pkt_type = PACKET_HOST;
+ skb_set_mac_header(skb, 0);
+
+ if (rmnet_check_skb_can_gro(skb) &&
+ (skb->dev->features & NETIF_F_GRO)) {
+ napi = get_current_napi_context();
+
+ skb_size = skb->len;
+ gro_res = napi_gro_receive(napi, skb);
+ trace_rmnet_gro_downlink(gro_res);
+ rmnet_optional_gro_flush(napi, ep, skb_size);
+ } else{
+ netif_receive_skb(skb);
+ }
+ return RX_HANDLER_CONSUMED;
+
case RMNET_EPMODE_NONE:
return RX_HANDLER_PASS;
case RMNET_EPMODE_BRIDGE:
return rmnet_bridge_handler(skb, ep);
- case RMNET_EPMODE_VND:
- skb_reset_transport_header(skb);
- skb_reset_network_header(skb);
- switch (rmnet_vnd_rx_fixup(skb, skb->dev)) {
- case RX_HANDLER_CONSUMED:
- return RX_HANDLER_CONSUMED;
-
- case RX_HANDLER_PASS:
- skb->pkt_type = PACKET_HOST;
- rmnet_reset_mac_header(skb);
- if (rmnet_check_skb_can_gro(skb) &&
- (skb->dev->features & NETIF_F_GRO)) {
- napi = get_current_napi_context();
- if (napi) {
- skb_size = skb->len;
- gro_res = napi_gro_receive(napi, skb);
- trace_rmnet_gro_downlink(gro_res);
- rmnet_optional_gro_flush(napi, ep,
- skb_size);
- } else {
- WARN_ONCE(1, "current napi is NULL\n");
- netif_receive_skb(skb);
- }
- } else {
- netif_receive_skb(skb);
- }
- return RX_HANDLER_CONSUMED;
- }
- return RX_HANDLER_PASS;
-
default:
LOGD("Unknown ep mode %d", ep->rmnet_mode);
rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_DELIVER_NO_EP);
@@ -441,16 +448,7 @@
ep = &config->muxed_ep[mux_id];
- if (!ep->refcount) {
- LOGD("Packet on %s:%d; has no logical endpoint config",
- skb->dev->name, mux_id);
-
- rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPINGRESS_MUX_NO_EP);
- return RX_HANDLER_CONSUMED;
- }
-
- if (config->ingress_data_format & RMNET_INGRESS_FORMAT_DEMUXING)
- skb->dev = ep->egress_dev;
+ skb->dev = ep->egress_dev;
if ((config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV3) ||
(config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV4)) {
@@ -462,6 +460,7 @@
skb->ip_summed |= CHECKSUM_UNNECESSARY;
else if (ckresult !=
RMNET_MAP_CHECKSUM_ERR_UNKNOWN_IP_VERSION &&
+ ckresult != RMNET_MAP_CHECKSUM_VALIDATION_FAILED &&
ckresult != RMNET_MAP_CHECKSUM_ERR_UNKNOWN_TRANSPORT &&
ckresult != RMNET_MAP_CHECKSUM_VALID_FLAG_NOT_SET &&
ckresult != RMNET_MAP_CHECKSUM_FRAGMENTED_PACKET) {
@@ -495,17 +494,13 @@
(struct sk_buff *skb, struct rmnet_phys_ep_config *config)
{
struct sk_buff *skbn;
- int rc, co = 0;
+ int rc;
if (config->ingress_data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) {
trace_rmnet_start_deaggregation(skb);
while ((skbn = rmnet_map_deaggregate(skb, config)) != 0) {
_rmnet_map_ingress_handler(skbn, config);
- co++;
}
- trace_rmnet_end_deaggregation(skb, co);
- LOGD("De-aggregated %d packets", co);
- rmnet_stats_deagg_pkts(co);
rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPINGRESS_AGGBUF);
rc = RX_HANDLER_CONSUMED;
} else {
@@ -538,12 +533,15 @@
int required_headroom, additional_header_length, ckresult;
struct rmnet_map_header_s *map_header;
int non_linear_skb;
+ int csum_required = (config->egress_data_format &
+ RMNET_EGRESS_FORMAT_MAP_CKSUMV3) ||
+ (config->egress_data_format &
+ RMNET_EGRESS_FORMAT_MAP_CKSUMV4);
additional_header_length = 0;
required_headroom = sizeof(struct rmnet_map_header_s);
- if ((config->egress_data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV3) ||
- (config->egress_data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV4)) {
+ if (csum_required) {
required_headroom +=
sizeof(struct rmnet_map_ul_checksum_header_s);
additional_header_length +=
@@ -558,8 +556,7 @@
return 1;
}
- if ((config->egress_data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV3) ||
- (config->egress_data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV4)) {
+ if (csum_required) {
ckresult = rmnet_map_checksum_uplink_packet
(skb, orig_dev, config->egress_data_format);
trace_rmnet_map_checksum_uplink_packet(orig_dev, ckresult);
diff --git a/net/rmnet_data/rmnet_data_stats.c b/net/rmnet_data/rmnet_data_stats.c
index f4aa492..db74c4f 100644
--- a/net/rmnet_data/rmnet_data_stats.c
+++ b/net/rmnet_data/rmnet_data_stats.c
@@ -41,11 +41,6 @@
module_param_array(queue_xmit, ulong, 0, 0444);
MODULE_PARM_DESC(queue_xmit, "SKBs queued for transmit");
-static DEFINE_SPINLOCK(rmnet_deagg_count);
-unsigned long int deagg_count[RMNET_STATS_AGG_MAX];
-module_param_array(deagg_count, ulong, 0, 0444);
-MODULE_PARM_DESC(deagg_count, "SKBs De-aggregated");
-
static DEFINE_SPINLOCK(rmnet_agg_count);
unsigned long int agg_count[RMNET_STATS_AGG_MAX];
module_param_array(agg_count, ulong, 0, 0444);
@@ -72,16 +67,7 @@
skb_free[reason]++;
spin_unlock_irqrestore(&rmnet_skb_free_lock, flags);
- if (likely(skb)) {
- struct rmnet_phys_ep_conf_s *config;
-
- config = (struct rmnet_phys_ep_conf_s *)rcu_dereference
- (skb->dev->rx_handler_data);
- if (likely(config))
- config->recycle(skb);
- else
- kfree_skb(skb);
- }
+ kfree_skb(skb);
}
void rmnet_stats_queue_xmit(int rc, unsigned int reason)
@@ -108,16 +94,6 @@
spin_unlock_irqrestore(&rmnet_agg_count, flags);
}
-void rmnet_stats_deagg_pkts(int aggcount)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&rmnet_deagg_count, flags);
- deagg_count[RMNET_STATS_AGG_BUFF]++;
- deagg_count[RMNET_STATS_AGG_PKT] += aggcount;
- spin_unlock_irqrestore(&rmnet_deagg_count, flags);
-}
-
void rmnet_stats_dl_checksum(unsigned int rc)
{
unsigned long flags;
diff --git a/net/rmnet_data/rmnet_data_stats.h b/net/rmnet_data/rmnet_data_stats.h
index e3350ef..366e486 100644
--- a/net/rmnet_data/rmnet_data_stats.h
+++ b/net/rmnet_data/rmnet_data_stats.h
@@ -24,7 +24,6 @@
RMNET_STATS_SKBFREE_DELIVER_NO_EP,
RMNET_STATS_SKBFREE_IPINGRESS_NO_EP,
RMNET_STATS_SKBFREE_MAPINGRESS_BAD_MUX,
- RMNET_STATS_SKBFREE_MAPINGRESS_MUX_NO_EP,
RMNET_STATS_SKBFREE_MAPINGRESS_AGGBUF,
RMNET_STATS_SKBFREE_INGRESS_NOT_EXPECT_MAPD,
RMNET_STATS_SKBFREE_INGRESS_NOT_EXPECT_MAPC,
diff --git a/net/rmnet_data/rmnet_data_vnd.c b/net/rmnet_data/rmnet_data_vnd.c
index 72f3c3b..c4ef460 100644
--- a/net/rmnet_data/rmnet_data_vnd.c
+++ b/net/rmnet_data/rmnet_data_vnd.c
@@ -101,55 +101,6 @@
}
}
-/* RX/TX Fixup */
-
-/* rmnet_vnd_rx_fixup() - Virtual Network Device receive fixup hook
- * @skb: Socket buffer ("packet") to modify
- * @dev: Virtual network device
- *
- * Additional VND specific packet processing for ingress packets
- *
- * Return:
- * - RX_HANDLER_PASS if packet should continue to process in stack
- * - RX_HANDLER_CONSUMED if packet should not be processed in stack
- *
- */
-int rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev)
-{
- if (unlikely(!dev || !skb))
- return RX_HANDLER_CONSUMED;
-
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += skb->len;
-
- return RX_HANDLER_PASS;
-}
-
-/* rmnet_vnd_tx_fixup() - Virtual Network Device transmic fixup hook
- * @skb: Socket buffer ("packet") to modify
- * @dev: Virtual network device
- *
- * Additional VND specific packet processing for egress packets
- *
- * Return:
- * - RX_HANDLER_PASS if packet should continue to be transmitted
- * - RX_HANDLER_CONSUMED if packet should not be transmitted by stack
- */
-int rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev)
-{
- struct rmnet_vnd_private_s *dev_conf;
-
- dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev);
-
- if (unlikely(!dev || !skb))
- return RX_HANDLER_CONSUMED;
-
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
-
- return RX_HANDLER_PASS;
-}
-
/* Network Device Operations */
/* rmnet_vnd_start_xmit() - Transmit NDO callback
@@ -220,12 +171,16 @@
switch (cmd) {
case RMNET_IOCTL_SET_QOS_ENABLE:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
LOGM("RMNET_IOCTL_SET_QOS_ENABLE on %s", dev->name);
if (!dev_conf->qos_version)
dev_conf->qos_version = RMNET_IOCTL_QOS_MODE_6;
break;
case RMNET_IOCTL_SET_QOS_DISABLE:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
LOGM("RMNET_IOCTL_SET_QOS_DISABLE on %s", dev->name);
dev_conf->qos_version = 0;
break;
@@ -240,6 +195,8 @@
break;
case RMNET_IOCTL_FLOW_ENABLE:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
LOGL("RMNET_IOCTL_FLOW_ENABLE on %s", dev->name);
if (copy_from_user(&ioctl_data, ifr->ifr_ifru.ifru_data,
sizeof(struct rmnet_ioctl_data_s))) {
@@ -252,6 +209,8 @@
break;
case RMNET_IOCTL_FLOW_DISABLE:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
LOGL("RMNET_IOCTL_FLOW_DISABLE on %s", dev->name);
if (copy_from_user(&ioctl_data, ifr->ifr_ifru.ifru_data,
sizeof(struct rmnet_ioctl_data_s))) {
@@ -367,6 +326,8 @@
break;
case RMNET_IOCTL_SET_QOS_VERSION:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
if (ext_cmd.u.data == RMNET_IOCTL_QOS_MODE_6 ||
ext_cmd.u.data == RMNET_IOCTL_QOS_MODE_8 ||
ext_cmd.u.data == 0) {
diff --git a/net/rmnet_data/rmnet_data_vnd.h b/net/rmnet_data/rmnet_data_vnd.h
index 9d8eb54..1d3a63b 100644
--- a/net/rmnet_data/rmnet_data_vnd.h
+++ b/net/rmnet_data/rmnet_data_vnd.h
@@ -27,8 +27,6 @@
int rmnet_vnd_create_dev(int id, struct net_device **new_device,
const char *prefix, int use_name);
int rmnet_vnd_free_dev(int id);
-int rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev);
-int rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev);
int rmnet_vnd_is_vnd(struct net_device *dev);
int rmnet_vnd_add_tc_flow(u32 id, u32 map_flow, u32 tc_flow);
int rmnet_vnd_del_tc_flow(u32 id, u32 map_flow, u32 tc_flow);
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index c651cfc..f311732 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -141,7 +141,7 @@
hlist_for_each_entry_safe(p, n, head, tcfa_head) {
ret = __tcf_hash_release(p, false, true);
if (ret == ACT_P_DELETED) {
- module_put(p->ops->owner);
+ module_put(ops->owner);
n_i++;
} else if (ret < 0)
goto nla_put_failure;
@@ -450,13 +450,15 @@
int tcf_action_destroy(struct list_head *actions, int bind)
{
+ const struct tc_action_ops *ops;
struct tc_action *a, *tmp;
int ret = 0;
list_for_each_entry_safe(a, tmp, actions, list) {
+ ops = a->ops;
ret = __tcf_hash_release(a, bind, true);
if (ret == ACT_P_DELETED)
- module_put(a->ops->owner);
+ module_put(ops->owner);
else if (ret < 0)
return ret;
}
diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c
index b12bc2a..e75fb65 100644
--- a/net/sched/cls_matchall.c
+++ b/net/sched/cls_matchall.c
@@ -32,6 +32,7 @@
if (tc_skip_sw(head->flags))
return -1;
+ *res = head->res;
return tcf_exts_exec(skb, &head->exts, res);
}
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 260d1df..b8031aa 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -686,6 +686,7 @@
qdisc->gso_skb = NULL;
}
qdisc->q.qlen = 0;
+ qdisc->qstats.backlog = 0;
}
EXPORT_SYMBOL(qdisc_reset);
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 75f290b..272c345 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -702,6 +702,65 @@
return task;
}
+/* create new threads */
+static int
+svc_start_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
+{
+ struct svc_rqst *rqstp;
+ struct task_struct *task;
+ struct svc_pool *chosen_pool;
+ unsigned int state = serv->sv_nrthreads-1;
+ int node;
+
+ do {
+ nrservs--;
+ chosen_pool = choose_pool(serv, pool, &state);
+
+ node = svc_pool_map_get_node(chosen_pool->sp_id);
+ rqstp = svc_prepare_thread(serv, chosen_pool, node);
+ if (IS_ERR(rqstp))
+ return PTR_ERR(rqstp);
+
+ __module_get(serv->sv_ops->svo_module);
+ task = kthread_create_on_node(serv->sv_ops->svo_function, rqstp,
+ node, "%s", serv->sv_name);
+ if (IS_ERR(task)) {
+ module_put(serv->sv_ops->svo_module);
+ svc_exit_thread(rqstp);
+ return PTR_ERR(task);
+ }
+
+ rqstp->rq_task = task;
+ if (serv->sv_nrpools > 1)
+ svc_pool_map_set_cpumask(task, chosen_pool->sp_id);
+
+ svc_sock_update_bufs(serv);
+ wake_up_process(task);
+ } while (nrservs > 0);
+
+ return 0;
+}
+
+
+/* destroy old threads */
+static int
+svc_signal_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
+{
+ struct task_struct *task;
+ unsigned int state = serv->sv_nrthreads-1;
+
+ /* destroy old threads */
+ do {
+ task = choose_victim(serv, pool, &state);
+ if (task == NULL)
+ break;
+ send_sig(SIGINT, task, 1);
+ nrservs++;
+ } while (nrservs < 0);
+
+ return 0;
+}
+
/*
* Create or destroy enough new threads to make the number
* of threads the given number. If `pool' is non-NULL, applies
@@ -719,13 +778,6 @@
int
svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
{
- struct svc_rqst *rqstp;
- struct task_struct *task;
- struct svc_pool *chosen_pool;
- int error = 0;
- unsigned int state = serv->sv_nrthreads-1;
- int node;
-
if (pool == NULL) {
/* The -1 assumes caller has done a svc_get() */
nrservs -= (serv->sv_nrthreads-1);
@@ -735,46 +787,52 @@
spin_unlock_bh(&pool->sp_lock);
}
- /* create new threads */
- while (nrservs > 0) {
- nrservs--;
- chosen_pool = choose_pool(serv, pool, &state);
-
- node = svc_pool_map_get_node(chosen_pool->sp_id);
- rqstp = svc_prepare_thread(serv, chosen_pool, node);
- if (IS_ERR(rqstp)) {
- error = PTR_ERR(rqstp);
- break;
- }
-
- __module_get(serv->sv_ops->svo_module);
- task = kthread_create_on_node(serv->sv_ops->svo_function, rqstp,
- node, "%s", serv->sv_name);
- if (IS_ERR(task)) {
- error = PTR_ERR(task);
- module_put(serv->sv_ops->svo_module);
- svc_exit_thread(rqstp);
- break;
- }
-
- rqstp->rq_task = task;
- if (serv->sv_nrpools > 1)
- svc_pool_map_set_cpumask(task, chosen_pool->sp_id);
-
- svc_sock_update_bufs(serv);
- wake_up_process(task);
- }
- /* destroy old threads */
- while (nrservs < 0 &&
- (task = choose_victim(serv, pool, &state)) != NULL) {
- send_sig(SIGINT, task, 1);
- nrservs++;
- }
-
- return error;
+ if (nrservs > 0)
+ return svc_start_kthreads(serv, pool, nrservs);
+ if (nrservs < 0)
+ return svc_signal_kthreads(serv, pool, nrservs);
+ return 0;
}
EXPORT_SYMBOL_GPL(svc_set_num_threads);
+/* destroy old threads */
+static int
+svc_stop_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
+{
+ struct task_struct *task;
+ unsigned int state = serv->sv_nrthreads-1;
+
+ /* destroy old threads */
+ do {
+ task = choose_victim(serv, pool, &state);
+ if (task == NULL)
+ break;
+ kthread_stop(task);
+ nrservs++;
+ } while (nrservs < 0);
+ return 0;
+}
+
+int
+svc_set_num_threads_sync(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
+{
+ if (pool == NULL) {
+ /* The -1 assumes caller has done a svc_get() */
+ nrservs -= (serv->sv_nrthreads-1);
+ } else {
+ spin_lock_bh(&pool->sp_lock);
+ nrservs -= pool->sp_nrthreads;
+ spin_unlock_bh(&pool->sp_lock);
+ }
+
+ if (nrservs > 0)
+ return svc_start_kthreads(serv, pool, nrservs);
+ if (nrservs < 0)
+ return svc_stop_kthreads(serv, pool, nrservs);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(svc_set_num_threads_sync);
+
/*
* Called from a server thread as it's exiting. Caller must hold the "service
* mutex" for the service.
diff --git a/net/tipc/msg.c b/net/tipc/msg.c
index 56ea0ad..912f1fb 100644
--- a/net/tipc/msg.c
+++ b/net/tipc/msg.c
@@ -547,7 +547,7 @@
return false;
if (msg_errcode(msg))
return false;
- *err = -TIPC_ERR_NO_NAME;
+ *err = TIPC_ERR_NO_NAME;
if (skb_linearize(skb))
return false;
msg = buf_msg(skb);
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index d942c7c..e0a3978 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -41,10 +41,8 @@
bool "Large payload keys"
depends on KEYS
depends on TMPFS
- depends on (CRYPTO_ANSI_CPRNG = y || CRYPTO_DRBG = y)
select CRYPTO_AES
- select CRYPTO_ECB
- select CRYPTO_RNG
+ select CRYPTO_GCM
help
This option provides support for holding large keys within the kernel
(for example Kerberos ticket caches). The data may be stored out to
diff --git a/security/keys/big_key.c b/security/keys/big_key.c
index 835c1ab..47c6dca 100644
--- a/security/keys/big_key.c
+++ b/security/keys/big_key.c
@@ -1,5 +1,6 @@
/* Large capacity key type
*
+ * Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
@@ -16,10 +17,10 @@
#include <linux/shmem_fs.h>
#include <linux/err.h>
#include <linux/scatterlist.h>
+#include <linux/random.h>
#include <keys/user-type.h>
#include <keys/big_key-type.h>
-#include <crypto/rng.h>
-#include <crypto/skcipher.h>
+#include <crypto/aead.h>
/*
* Layout of key payload words.
@@ -49,7 +50,12 @@
/*
* Key size for big_key data encryption
*/
-#define ENC_KEY_SIZE 16
+#define ENC_KEY_SIZE 32
+
+/*
+ * Authentication tag length
+ */
+#define ENC_AUTHTAG_SIZE 16
/*
* big_key defined keys take an arbitrary string as the description and an
@@ -64,57 +70,62 @@
.destroy = big_key_destroy,
.describe = big_key_describe,
.read = big_key_read,
+ /* no ->update(); don't add it without changing big_key_crypt() nonce */
};
/*
- * Crypto names for big_key data encryption
+ * Crypto names for big_key data authenticated encryption
*/
-static const char big_key_rng_name[] = "stdrng";
-static const char big_key_alg_name[] = "ecb(aes)";
+static const char big_key_alg_name[] = "gcm(aes)";
/*
- * Crypto algorithms for big_key data encryption
+ * Crypto algorithms for big_key data authenticated encryption
*/
-static struct crypto_rng *big_key_rng;
-static struct crypto_skcipher *big_key_skcipher;
+static struct crypto_aead *big_key_aead;
/*
- * Generate random key to encrypt big_key data
+ * Since changing the key affects the entire object, we need a mutex.
*/
-static inline int big_key_gen_enckey(u8 *key)
-{
- return crypto_rng_get_bytes(big_key_rng, key, ENC_KEY_SIZE);
-}
+static DEFINE_MUTEX(big_key_aead_lock);
/*
* Encrypt/decrypt big_key data
*/
static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key)
{
- int ret = -EINVAL;
+ int ret;
struct scatterlist sgio;
- SKCIPHER_REQUEST_ON_STACK(req, big_key_skcipher);
+ struct aead_request *aead_req;
+ /* We always use a zero nonce. The reason we can get away with this is
+ * because we're using a different randomly generated key for every
+ * different encryption. Notably, too, key_type_big_key doesn't define
+ * an .update function, so there's no chance we'll wind up reusing the
+ * key to encrypt updated data. Simply put: one key, one encryption.
+ */
+ u8 zero_nonce[crypto_aead_ivsize(big_key_aead)];
- if (crypto_skcipher_setkey(big_key_skcipher, key, ENC_KEY_SIZE)) {
+ aead_req = aead_request_alloc(big_key_aead, GFP_KERNEL);
+ if (!aead_req)
+ return -ENOMEM;
+
+ memset(zero_nonce, 0, sizeof(zero_nonce));
+ sg_init_one(&sgio, data, datalen + (op == BIG_KEY_ENC ? ENC_AUTHTAG_SIZE : 0));
+ aead_request_set_crypt(aead_req, &sgio, &sgio, datalen, zero_nonce);
+ aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
+ aead_request_set_ad(aead_req, 0);
+
+ mutex_lock(&big_key_aead_lock);
+ if (crypto_aead_setkey(big_key_aead, key, ENC_KEY_SIZE)) {
ret = -EAGAIN;
goto error;
}
-
- skcipher_request_set_tfm(req, big_key_skcipher);
- skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP,
- NULL, NULL);
-
- sg_init_one(&sgio, data, datalen);
- skcipher_request_set_crypt(req, &sgio, &sgio, datalen, NULL);
-
if (op == BIG_KEY_ENC)
- ret = crypto_skcipher_encrypt(req);
+ ret = crypto_aead_encrypt(aead_req);
else
- ret = crypto_skcipher_decrypt(req);
-
- skcipher_request_zero(req);
-
+ ret = crypto_aead_decrypt(aead_req);
error:
+ mutex_unlock(&big_key_aead_lock);
+ aead_request_free(aead_req);
return ret;
}
@@ -146,15 +157,13 @@
*
* File content is stored encrypted with randomly generated key.
*/
- size_t enclen = ALIGN(datalen, crypto_skcipher_blocksize(big_key_skcipher));
+ size_t enclen = datalen + ENC_AUTHTAG_SIZE;
- /* prepare aligned data to encrypt */
data = kmalloc(enclen, GFP_KERNEL);
if (!data)
return -ENOMEM;
memcpy(data, prep->data, datalen);
- memset(data + datalen, 0x00, enclen - datalen);
/* generate random key */
enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL);
@@ -162,13 +171,10 @@
ret = -ENOMEM;
goto error;
}
-
- ret = big_key_gen_enckey(enckey);
- if (ret)
- goto err_enckey;
+ get_random_bytes(enckey, ENC_KEY_SIZE);
/* encrypt aligned data */
- ret = big_key_crypt(BIG_KEY_ENC, data, enclen, enckey);
+ ret = big_key_crypt(BIG_KEY_ENC, data, datalen, enckey);
if (ret)
goto err_enckey;
@@ -194,7 +200,7 @@
*path = file->f_path;
path_get(path);
fput(file);
- kfree(data);
+ kzfree(data);
} else {
/* Just store the data in a buffer */
void *data = kmalloc(datalen, GFP_KERNEL);
@@ -210,9 +216,9 @@
err_fput:
fput(file);
err_enckey:
- kfree(enckey);
+ kzfree(enckey);
error:
- kfree(data);
+ kzfree(data);
return ret;
}
@@ -226,7 +232,7 @@
path_put(path);
}
- kfree(prep->payload.data[big_key_data]);
+ kzfree(prep->payload.data[big_key_data]);
}
/*
@@ -258,7 +264,7 @@
path->mnt = NULL;
path->dentry = NULL;
}
- kfree(key->payload.data[big_key_data]);
+ kzfree(key->payload.data[big_key_data]);
key->payload.data[big_key_data] = NULL;
}
@@ -294,7 +300,7 @@
struct file *file;
u8 *data;
u8 *enckey = (u8 *)key->payload.data[big_key_data];
- size_t enclen = ALIGN(datalen, crypto_skcipher_blocksize(big_key_skcipher));
+ size_t enclen = datalen + ENC_AUTHTAG_SIZE;
data = kmalloc(enclen, GFP_KERNEL);
if (!data)
@@ -326,7 +332,7 @@
err_fput:
fput(file);
error:
- kfree(data);
+ kzfree(data);
} else {
ret = datalen;
if (copy_to_user(buffer, key->payload.data[big_key_data],
@@ -342,47 +348,31 @@
*/
static int __init big_key_init(void)
{
- struct crypto_skcipher *cipher;
- struct crypto_rng *rng;
int ret;
- rng = crypto_alloc_rng(big_key_rng_name, 0, 0);
- if (IS_ERR(rng)) {
- pr_err("Can't alloc rng: %ld\n", PTR_ERR(rng));
- return PTR_ERR(rng);
- }
-
- big_key_rng = rng;
-
- /* seed RNG */
- ret = crypto_rng_reset(rng, NULL, crypto_rng_seedsize(rng));
- if (ret) {
- pr_err("Can't reset rng: %d\n", ret);
- goto error_rng;
- }
-
/* init block cipher */
- cipher = crypto_alloc_skcipher(big_key_alg_name, 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(cipher)) {
- ret = PTR_ERR(cipher);
+ big_key_aead = crypto_alloc_aead(big_key_alg_name, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(big_key_aead)) {
+ ret = PTR_ERR(big_key_aead);
pr_err("Can't alloc crypto: %d\n", ret);
- goto error_rng;
+ return ret;
}
-
- big_key_skcipher = cipher;
+ ret = crypto_aead_setauthsize(big_key_aead, ENC_AUTHTAG_SIZE);
+ if (ret < 0) {
+ pr_err("Can't set crypto auth tag len: %d\n", ret);
+ goto free_aead;
+ }
ret = register_key_type(&key_type_big_key);
if (ret < 0) {
pr_err("Can't register type: %d\n", ret);
- goto error_cipher;
+ goto free_aead;
}
return 0;
-error_cipher:
- crypto_free_skcipher(big_key_skcipher);
-error_rng:
- crypto_free_rng(big_key_rng);
+free_aead:
+ crypto_free_aead(big_key_aead);
return ret;
}
diff --git a/security/keys/internal.h b/security/keys/internal.h
index a705a7d..fb0c650 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -137,7 +137,7 @@
extern key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx);
extern key_ref_t search_process_keyrings(struct keyring_search_context *ctx);
-extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check);
+extern struct key *find_keyring_by_name(const char *name, bool uid_keyring);
extern int install_user_keyrings(void);
extern int install_thread_keyring_to_cred(struct cred *);
diff --git a/security/keys/key.c b/security/keys/key.c
index 2f4ce35..135e1eb 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -301,6 +301,8 @@
key->flags |= 1 << KEY_FLAG_IN_QUOTA;
if (flags & KEY_ALLOC_BUILT_IN)
key->flags |= 1 << KEY_FLAG_BUILTIN;
+ if (flags & KEY_ALLOC_UID_KEYRING)
+ key->flags |= 1 << KEY_FLAG_UID_KEYRING;
#ifdef KEY_DEBUGGING
key->magic = KEY_DEBUG_MAGIC;
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index ada12c3..1302cb3 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -766,6 +766,11 @@
key = key_ref_to_ptr(key_ref);
+ if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) {
+ ret = -ENOKEY;
+ goto error2;
+ }
+
/* see if we can read it directly */
ret = key_permission(key_ref, KEY_NEED_READ);
if (ret == 0)
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index c91e4e0..a86d0ae 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -416,7 +416,7 @@
}
struct keyring_read_iterator_context {
- size_t qty;
+ size_t buflen;
size_t count;
key_serial_t __user *buffer;
};
@@ -428,9 +428,9 @@
int ret;
kenter("{%s,%d},,{%zu/%zu}",
- key->type->name, key->serial, ctx->count, ctx->qty);
+ key->type->name, key->serial, ctx->count, ctx->buflen);
- if (ctx->count >= ctx->qty)
+ if (ctx->count >= ctx->buflen)
return 1;
ret = put_user(key->serial, ctx->buffer);
@@ -465,16 +465,12 @@
return 0;
/* Calculate how much data we could return */
- ctx.qty = nr_keys * sizeof(key_serial_t);
-
if (!buffer || !buflen)
- return ctx.qty;
-
- if (buflen > ctx.qty)
- ctx.qty = buflen;
+ return nr_keys * sizeof(key_serial_t);
/* Copy the IDs of the subscribed keys into the buffer */
ctx.buffer = (key_serial_t __user *)buffer;
+ ctx.buflen = buflen;
ctx.count = 0;
ret = assoc_array_iterate(&keyring->keys, keyring_read_iterator, &ctx);
if (ret < 0) {
@@ -989,15 +985,15 @@
/*
* Find a keyring with the specified name.
*
- * All named keyrings in the current user namespace are searched, provided they
- * grant Search permission directly to the caller (unless this check is
- * skipped). Keyrings whose usage points have reached zero or who have been
- * revoked are skipped.
+ * Only keyrings that have nonzero refcount, are not revoked, and are owned by a
+ * user in the current user namespace are considered. If @uid_keyring is %true,
+ * the keyring additionally must have been allocated as a user or user session
+ * keyring; otherwise, it must grant Search permission directly to the caller.
*
* Returns a pointer to the keyring with the keyring's refcount having being
* incremented on success. -ENOKEY is returned if a key could not be found.
*/
-struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
+struct key *find_keyring_by_name(const char *name, bool uid_keyring)
{
struct key *keyring;
int bucket;
@@ -1025,10 +1021,15 @@
if (strcmp(keyring->description, name) != 0)
continue;
- if (!skip_perm_check &&
- key_permission(make_key_ref(keyring, 0),
- KEY_NEED_SEARCH) < 0)
- continue;
+ if (uid_keyring) {
+ if (!test_bit(KEY_FLAG_UID_KEYRING,
+ &keyring->flags))
+ continue;
+ } else {
+ if (key_permission(make_key_ref(keyring, 0),
+ KEY_NEED_SEARCH) < 0)
+ continue;
+ }
/* we've got a match but we might end up racing with
* key_cleanup() if the keyring is currently 'dead'
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 45536c6..ce45c78 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -76,7 +76,8 @@
if (IS_ERR(uid_keyring)) {
uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID,
cred, user_keyring_perm,
- KEY_ALLOC_IN_QUOTA,
+ KEY_ALLOC_UID_KEYRING |
+ KEY_ALLOC_IN_QUOTA,
NULL, NULL);
if (IS_ERR(uid_keyring)) {
ret = PTR_ERR(uid_keyring);
@@ -93,7 +94,8 @@
session_keyring =
keyring_alloc(buf, user->uid, INVALID_GID,
cred, user_keyring_perm,
- KEY_ALLOC_IN_QUOTA,
+ KEY_ALLOC_UID_KEYRING |
+ KEY_ALLOC_IN_QUOTA,
NULL, NULL);
if (IS_ERR(session_keyring)) {
ret = PTR_ERR(session_keyring);
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index b75c31a..530ed9b 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1486,7 +1486,7 @@
* @inode: the object
* @name: attribute name
* @buffer: where to put the result
- * @alloc: unused
+ * @alloc: duplicate memory
*
* Returns the size of the attribute or an error code
*/
@@ -1499,43 +1499,38 @@
struct super_block *sbp;
struct inode *ip = (struct inode *)inode;
struct smack_known *isp;
- int ilen;
- int rc = 0;
- if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
+ if (strcmp(name, XATTR_SMACK_SUFFIX) == 0)
isp = smk_of_inode(inode);
- ilen = strlen(isp->smk_known);
- *buffer = isp->smk_known;
- return ilen;
+ else {
+ /*
+ * The rest of the Smack xattrs are only on sockets.
+ */
+ sbp = ip->i_sb;
+ if (sbp->s_magic != SOCKFS_MAGIC)
+ return -EOPNOTSUPP;
+
+ sock = SOCKET_I(ip);
+ if (sock == NULL || sock->sk == NULL)
+ return -EOPNOTSUPP;
+
+ ssp = sock->sk->sk_security;
+
+ if (strcmp(name, XATTR_SMACK_IPIN) == 0)
+ isp = ssp->smk_in;
+ else if (strcmp(name, XATTR_SMACK_IPOUT) == 0)
+ isp = ssp->smk_out;
+ else
+ return -EOPNOTSUPP;
}
- /*
- * The rest of the Smack xattrs are only on sockets.
- */
- sbp = ip->i_sb;
- if (sbp->s_magic != SOCKFS_MAGIC)
- return -EOPNOTSUPP;
-
- sock = SOCKET_I(ip);
- if (sock == NULL || sock->sk == NULL)
- return -EOPNOTSUPP;
-
- ssp = sock->sk->sk_security;
-
- if (strcmp(name, XATTR_SMACK_IPIN) == 0)
- isp = ssp->smk_in;
- else if (strcmp(name, XATTR_SMACK_IPOUT) == 0)
- isp = ssp->smk_out;
- else
- return -EOPNOTSUPP;
-
- ilen = strlen(isp->smk_known);
- if (rc == 0) {
- *buffer = isp->smk_known;
- rc = ilen;
+ if (alloc) {
+ *buffer = kstrdup(isp->smk_known, GFP_KERNEL);
+ if (*buffer == NULL)
+ return -ENOMEM;
}
- return rc;
+ return strlen(isp->smk_known);
}
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index a098656..99ee618 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -981,14 +981,13 @@
static int snd_compress_dev_register(struct snd_device *device)
{
int ret = -EINVAL;
- char str[16];
struct snd_compr *compr;
if (snd_BUG_ON(!device || !device->device_data))
return -EBADFD;
compr = device->device_data;
- pr_debug("reg %s for device %s, direction %d\n", str, compr->name,
+ pr_debug("reg device %s, direction %d\n", compr->name,
compr->direction);
/* register compressed device */
ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS,
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 67c4c68..c411483 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -1259,6 +1259,7 @@
struct snd_seq_port_info *info = arg;
struct snd_seq_client_port *port;
struct snd_seq_port_callback *callback;
+ int port_idx;
/* it is not allowed to create the port for an another client */
if (info->addr.client != client->number)
@@ -1269,7 +1270,9 @@
return -ENOMEM;
if (client->type == USER_CLIENT && info->kernel) {
- snd_seq_delete_port(client, port->addr.port);
+ port_idx = port->addr.port;
+ snd_seq_port_unlock(port);
+ snd_seq_delete_port(client, port_idx);
return -EINVAL;
}
if (client->type == KERNEL_CLIENT) {
@@ -1290,6 +1293,7 @@
snd_seq_set_port_info(port, info);
snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port);
+ snd_seq_port_unlock(port);
return 0;
}
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index fe686ee..f04714d 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -122,7 +122,9 @@
}
-/* create a port, port number is returned (-1 on failure) */
+/* create a port, port number is returned (-1 on failure);
+ * the caller needs to unref the port via snd_seq_port_unlock() appropriately
+ */
struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
int port)
{
@@ -151,6 +153,7 @@
snd_use_lock_init(&new_port->use_lock);
port_subs_info_init(&new_port->c_src);
port_subs_info_init(&new_port->c_dest);
+ snd_use_lock_use(&new_port->use_lock);
num = port >= 0 ? port : 0;
mutex_lock(&client->ports_mutex);
@@ -165,9 +168,9 @@
list_add_tail(&new_port->list, &p->list);
client->num_ports++;
new_port->addr.port = num; /* store the port number in the port */
+ sprintf(new_port->name, "port-%d", num);
write_unlock_irqrestore(&client->ports_lock, flags);
mutex_unlock(&client->ports_mutex);
- sprintf(new_port->name, "port-%d", num);
return new_port;
}
diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
index c82ed3e..2007649 100644
--- a/sound/core/seq/seq_virmidi.c
+++ b/sound/core/seq/seq_virmidi.c
@@ -77,13 +77,17 @@
* decode input event and put to read buffer of each opened file
*/
static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev,
- struct snd_seq_event *ev)
+ struct snd_seq_event *ev,
+ bool atomic)
{
struct snd_virmidi *vmidi;
unsigned char msg[4];
int len;
- read_lock(&rdev->filelist_lock);
+ if (atomic)
+ read_lock(&rdev->filelist_lock);
+ else
+ down_read(&rdev->filelist_sem);
list_for_each_entry(vmidi, &rdev->filelist, list) {
if (!vmidi->trigger)
continue;
@@ -97,7 +101,10 @@
snd_rawmidi_receive(vmidi->substream, msg, len);
}
}
- read_unlock(&rdev->filelist_lock);
+ if (atomic)
+ read_unlock(&rdev->filelist_lock);
+ else
+ up_read(&rdev->filelist_sem);
return 0;
}
@@ -115,7 +122,7 @@
struct snd_virmidi_dev *rdev;
rdev = rmidi->private_data;
- return snd_virmidi_dev_receive_event(rdev, ev);
+ return snd_virmidi_dev_receive_event(rdev, ev, true);
}
#endif /* 0 */
@@ -130,7 +137,7 @@
rdev = private_data;
if (!(rdev->flags & SNDRV_VIRMIDI_USE))
return 0; /* ignored */
- return snd_virmidi_dev_receive_event(rdev, ev);
+ return snd_virmidi_dev_receive_event(rdev, ev, atomic);
}
/*
@@ -209,7 +216,6 @@
struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
struct snd_rawmidi_runtime *runtime = substream->runtime;
struct snd_virmidi *vmidi;
- unsigned long flags;
vmidi = kzalloc(sizeof(*vmidi), GFP_KERNEL);
if (vmidi == NULL)
@@ -223,9 +229,11 @@
vmidi->client = rdev->client;
vmidi->port = rdev->port;
runtime->private_data = vmidi;
- write_lock_irqsave(&rdev->filelist_lock, flags);
+ down_write(&rdev->filelist_sem);
+ write_lock_irq(&rdev->filelist_lock);
list_add_tail(&vmidi->list, &rdev->filelist);
- write_unlock_irqrestore(&rdev->filelist_lock, flags);
+ write_unlock_irq(&rdev->filelist_lock);
+ up_write(&rdev->filelist_sem);
vmidi->rdev = rdev;
return 0;
}
@@ -264,9 +272,11 @@
struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
struct snd_virmidi *vmidi = substream->runtime->private_data;
+ down_write(&rdev->filelist_sem);
write_lock_irq(&rdev->filelist_lock);
list_del(&vmidi->list);
write_unlock_irq(&rdev->filelist_lock);
+ up_write(&rdev->filelist_sem);
snd_midi_event_free(vmidi->parser);
substream->runtime->private_data = NULL;
kfree(vmidi);
@@ -520,6 +530,7 @@
rdev->rmidi = rmidi;
rdev->device = device;
rdev->client = -1;
+ init_rwsem(&rdev->filelist_sem);
rwlock_init(&rdev->filelist_lock);
INIT_LIST_HEAD(&rdev->filelist);
rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
index e1af24f..c308a4f 100644
--- a/sound/pci/au88x0/au88x0_core.c
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -2279,6 +2279,9 @@
} else {
int src[2], mix[2];
+ if (nr_ch < 1)
+ return -EINVAL;
+
/* Get SRC and MIXER hardware resources. */
for (i = 0; i < nr_ch; i++) {
if ((mix[i] =
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 9370717..286f5e3 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -1272,11 +1272,11 @@
chip = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
uinfo->value.integer.min = ECHOGAIN_MINOUT;
uinfo->value.integer.max = ECHOGAIN_MAXOUT;
uinfo->dimen.d[0] = num_busses_out(chip);
uinfo->dimen.d[1] = num_busses_in(chip);
- uinfo->count = uinfo->dimen.d[0] * uinfo->dimen.d[1];
return 0;
}
@@ -1344,11 +1344,11 @@
chip = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
uinfo->value.integer.min = ECHOGAIN_MINOUT;
uinfo->value.integer.max = ECHOGAIN_MAXOUT;
uinfo->dimen.d[0] = num_busses_out(chip);
uinfo->dimen.d[1] = num_pipes_out(chip);
- uinfo->count = uinfo->dimen.d[0] * uinfo->dimen.d[1];
return 0;
}
@@ -1728,6 +1728,7 @@
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 96;
uinfo->value.integer.min = ECHOGAIN_MINOUT;
uinfo->value.integer.max = 0;
#ifdef ECHOCARD_HAS_VMIXER
@@ -1737,7 +1738,6 @@
#endif
uinfo->dimen.d[1] = 16; /* 16 channels */
uinfo->dimen.d[2] = 2; /* 0=level, 1=peak */
- uinfo->count = uinfo->dimen.d[0] * uinfo->dimen.d[1] * uinfo->dimen.d[2];
return 0;
}
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 775c678..bd65022 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -3685,6 +3685,7 @@
HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi),
HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi),
diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c
index f24b7cf..e024800 100644
--- a/sound/soc/codecs/rt5514.c
+++ b/sound/soc/codecs/rt5514.c
@@ -395,14 +395,14 @@
"DMIC1", "DMIC2"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5514_stereo1_dmic_enum, RT5514_DIG_SOURCE_CTRL,
RT5514_AD0_DMIC_INPUT_SEL_SFT, rt5514_dmic_src);
static const struct snd_kcontrol_new rt5514_sto1_dmic_mux =
SOC_DAPM_ENUM("Stereo1 DMIC Source", rt5514_stereo1_dmic_enum);
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5514_stereo2_dmic_enum, RT5514_DIG_SOURCE_CTRL,
RT5514_AD1_DMIC_INPUT_SEL_SFT, rt5514_dmic_src);
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index db54550..635818f 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -1150,28 +1150,28 @@
"L/R", "R/L", "L/L", "R/R"
};
-static const SOC_ENUM_SINGLE_DECL(rt5659_if1_01_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if1_01_adc_enum,
RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT01_SFT, rt5659_data_select);
-static const SOC_ENUM_SINGLE_DECL(rt5659_if1_23_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if1_23_adc_enum,
RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT23_SFT, rt5659_data_select);
-static const SOC_ENUM_SINGLE_DECL(rt5659_if1_45_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if1_45_adc_enum,
RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT45_SFT, rt5659_data_select);
-static const SOC_ENUM_SINGLE_DECL(rt5659_if1_67_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if1_67_adc_enum,
RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT67_SFT, rt5659_data_select);
-static const SOC_ENUM_SINGLE_DECL(rt5659_if2_dac_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if2_dac_enum,
RT5659_DIG_INF23_DATA, RT5659_IF2_DAC_SEL_SFT, rt5659_data_select);
-static const SOC_ENUM_SINGLE_DECL(rt5659_if2_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if2_adc_enum,
RT5659_DIG_INF23_DATA, RT5659_IF2_ADC_SEL_SFT, rt5659_data_select);
-static const SOC_ENUM_SINGLE_DECL(rt5659_if3_dac_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if3_dac_enum,
RT5659_DIG_INF23_DATA, RT5659_IF3_DAC_SEL_SFT, rt5659_data_select);
-static const SOC_ENUM_SINGLE_DECL(rt5659_if3_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if3_adc_enum,
RT5659_DIG_INF23_DATA, RT5659_IF3_ADC_SEL_SFT, rt5659_data_select);
static const struct snd_kcontrol_new rt5659_if1_01_adc_swap_mux =
@@ -1207,31 +1207,31 @@
0, 1, 2, 3, 5, 6,
};
-static const SOC_VALUE_ENUM_SINGLE_DECL(
+static SOC_VALUE_ENUM_SINGLE_DECL(
rt5659_da_sto_asrc_enum, RT5659_ASRC_2, RT5659_DA_STO_T_SFT, 0x7,
rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-static const SOC_VALUE_ENUM_SINGLE_DECL(
+static SOC_VALUE_ENUM_SINGLE_DECL(
rt5659_da_monol_asrc_enum, RT5659_ASRC_2, RT5659_DA_MONO_L_T_SFT, 0x7,
rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-static const SOC_VALUE_ENUM_SINGLE_DECL(
+static SOC_VALUE_ENUM_SINGLE_DECL(
rt5659_da_monor_asrc_enum, RT5659_ASRC_2, RT5659_DA_MONO_R_T_SFT, 0x7,
rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-static const SOC_VALUE_ENUM_SINGLE_DECL(
+static SOC_VALUE_ENUM_SINGLE_DECL(
rt5659_ad_sto1_asrc_enum, RT5659_ASRC_2, RT5659_AD_STO1_T_SFT, 0x7,
rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-static const SOC_VALUE_ENUM_SINGLE_DECL(
+static SOC_VALUE_ENUM_SINGLE_DECL(
rt5659_ad_sto2_asrc_enum, RT5659_ASRC_3, RT5659_AD_STO2_T_SFT, 0x7,
rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-static const SOC_VALUE_ENUM_SINGLE_DECL(
+static SOC_VALUE_ENUM_SINGLE_DECL(
rt5659_ad_monol_asrc_enum, RT5659_ASRC_3, RT5659_AD_MONO_L_T_SFT, 0x7,
rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
-static const SOC_VALUE_ENUM_SINGLE_DECL(
+static SOC_VALUE_ENUM_SINGLE_DECL(
rt5659_ad_monor_asrc_enum, RT5659_ASRC_3, RT5659_AD_MONO_R_T_SFT, 0x7,
rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
@@ -1930,14 +1930,14 @@
"IF1 DAC2", "IF2 DAC", "IF3 DAC", "Mono ADC MIX"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_dac_l2_enum, RT5659_DAC_CTRL,
RT5659_DAC_L2_SEL_SFT, rt5659_dac2_src);
static const struct snd_kcontrol_new rt5659_dac_l2_mux =
SOC_DAPM_ENUM("DAC L2 Source", rt5659_dac_l2_enum);
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_dac_r2_enum, RT5659_DAC_CTRL,
RT5659_DAC_R2_SEL_SFT, rt5659_dac2_src);
@@ -1951,7 +1951,7 @@
"DAC MIX", "ADC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_sto1_adc1_enum, RT5659_STO1_ADC_MIXER,
RT5659_STO1_ADC1_SRC_SFT, rt5659_sto1_adc1_src);
@@ -1964,7 +1964,7 @@
"ADC1", "ADC2"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_sto1_adc_enum, RT5659_STO1_ADC_MIXER,
RT5659_STO1_ADC_SRC_SFT, rt5659_sto1_adc_src);
@@ -1977,7 +1977,7 @@
"DAC MIX", "DMIC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_sto1_adc2_enum, RT5659_STO1_ADC_MIXER,
RT5659_STO1_ADC2_SRC_SFT, rt5659_sto1_adc2_src);
@@ -1990,7 +1990,7 @@
"DMIC1", "DMIC2"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_sto1_dmic_enum, RT5659_STO1_ADC_MIXER,
RT5659_STO1_DMIC_SRC_SFT, rt5659_sto1_dmic_src);
@@ -2004,7 +2004,7 @@
"Mono DAC MIXL", "DMIC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_mono_adc_l2_enum, RT5659_MONO_ADC_MIXER,
RT5659_MONO_ADC_L2_SRC_SFT, rt5659_mono_adc_l2_src);
@@ -2018,7 +2018,7 @@
"Mono DAC MIXL", "ADC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_mono_adc_l1_enum, RT5659_MONO_ADC_MIXER,
RT5659_MONO_ADC_L1_SRC_SFT, rt5659_mono_adc_l1_src);
@@ -2031,14 +2031,14 @@
"ADC1 L", "ADC1 R", "ADC2 L", "ADC2 R"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_mono_adc_l_enum, RT5659_MONO_ADC_MIXER,
RT5659_MONO_ADC_L_SRC_SFT, rt5659_mono_adc_src);
static const struct snd_kcontrol_new rt5659_mono_adc_l_mux =
SOC_DAPM_ENUM("Mono ADC L Source", rt5659_mono_adc_l_enum);
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_mono_adcr_enum, RT5659_MONO_ADC_MIXER,
RT5659_MONO_ADC_R_SRC_SFT, rt5659_mono_adc_src);
@@ -2051,7 +2051,7 @@
"DMIC1 L", "DMIC2 L"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_mono_dmic_l_enum, RT5659_MONO_ADC_MIXER,
RT5659_MONO_DMIC_L_SRC_SFT, rt5659_mono_dmic_l_src);
@@ -2064,7 +2064,7 @@
"Mono DAC MIXR", "DMIC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_mono_adc_r2_enum, RT5659_MONO_ADC_MIXER,
RT5659_MONO_ADC_R2_SRC_SFT, rt5659_mono_adc_r2_src);
@@ -2077,7 +2077,7 @@
"Mono DAC MIXR", "ADC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_mono_adc_r1_enum, RT5659_MONO_ADC_MIXER,
RT5659_MONO_ADC_R1_SRC_SFT, rt5659_mono_adc_r1_src);
@@ -2090,7 +2090,7 @@
"DMIC1 R", "DMIC2 R"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_mono_dmic_r_enum, RT5659_MONO_ADC_MIXER,
RT5659_MONO_DMIC_R_SRC_SFT, rt5659_mono_dmic_r_src);
@@ -2104,14 +2104,14 @@
"IF1 DAC1", "IF2 DAC", "IF3 DAC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_dac_r1_enum, RT5659_AD_DA_MIXER,
RT5659_DAC1_R_SEL_SFT, rt5659_dac1_src);
static const struct snd_kcontrol_new rt5659_dac_r1_mux =
SOC_DAPM_ENUM("DAC R1 Source", rt5659_dac_r1_enum);
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_dac_l1_enum, RT5659_AD_DA_MIXER,
RT5659_DAC1_L_SEL_SFT, rt5659_dac1_src);
@@ -2124,14 +2124,14 @@
"Stereo DAC Mixer", "Mono DAC Mixer"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_dig_dac_mixl_enum, RT5659_DIG_MIXER,
RT5659_DAC_MIX_L_SFT, rt5659_dig_dac_mix_src);
static const struct snd_kcontrol_new rt5659_dig_dac_mixl_mux =
SOC_DAPM_ENUM("DAC Digital Mixer L Source", rt5659_dig_dac_mixl_enum);
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_dig_dac_mixr_enum, RT5659_DIG_MIXER,
RT5659_DAC_MIX_R_SFT, rt5659_dig_dac_mix_src);
@@ -2144,14 +2144,14 @@
"DAC", "Stereo DAC Mixer"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_alg_dac_l1_enum, RT5659_A_DAC_MUX,
RT5659_A_DACL1_SFT, rt5659_alg_dac1_src);
static const struct snd_kcontrol_new rt5659_alg_dac_l1_mux =
SOC_DAPM_ENUM("Analog DACL1 Source", rt5659_alg_dac_l1_enum);
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_alg_dac_r1_enum, RT5659_A_DAC_MUX,
RT5659_A_DACR1_SFT, rt5659_alg_dac1_src);
@@ -2164,14 +2164,14 @@
"Stereo DAC Mixer", "Mono DAC Mixer"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_alg_dac_l2_enum, RT5659_A_DAC_MUX,
RT5659_A_DACL2_SFT, rt5659_alg_dac2_src);
static const struct snd_kcontrol_new rt5659_alg_dac_l2_mux =
SOC_DAPM_ENUM("Analog DAC L2 Source", rt5659_alg_dac_l2_enum);
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_alg_dac_r2_enum, RT5659_A_DAC_MUX,
RT5659_A_DACR2_SFT, rt5659_alg_dac2_src);
@@ -2184,7 +2184,7 @@
"IF_ADC1", "IF_ADC2", "DAC_REF", "IF_ADC3"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_if2_adc_in_enum, RT5659_DIG_INF23_DATA,
RT5659_IF2_ADC_IN_SFT, rt5659_if2_adc_in_src);
@@ -2197,7 +2197,7 @@
"IF_ADC1", "IF_ADC2", "DAC_REF", "Stereo2_ADC_L/R"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_if3_adc_in_enum, RT5659_DIG_INF23_DATA,
RT5659_IF3_ADC_IN_SFT, rt5659_if3_adc_in_src);
@@ -2210,14 +2210,14 @@
"Mono DAC", "Stereo DAC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_pdm_l_enum, RT5659_PDM_OUT_CTRL,
RT5659_PDM1_L_SFT, rt5659_pdm_src);
static const struct snd_kcontrol_new rt5659_pdm_l_mux =
SOC_DAPM_ENUM("PDM L Source", rt5659_pdm_l_enum);
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_pdm_r_enum, RT5659_PDM_OUT_CTRL,
RT5659_PDM1_R_SFT, rt5659_pdm_src);
@@ -2230,7 +2230,7 @@
"IF1_DAC1", "IF1_DAC2", "IF2_DAC", "IF3_DAC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_spdif_enum, RT5659_SPDIF_CTRL,
RT5659_SPDIF_SEL_SFT, rt5659_spdif_src);
@@ -2250,7 +2250,7 @@
"NUL:AD2:DAC:AD1", "NUL:DAC:DAC:AD2", "NUL:DAC:AD2:DAC"
};
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
rt5659_rx_adc_data_enum, RT5659_TDM_CTRL_2,
RT5659_ADCDAT_SRC_SFT, rt5659_rx_adc_data_src);
diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c
index 9f0933c..e396b768 100644
--- a/sound/soc/codecs/rt5660.c
+++ b/sound/soc/codecs/rt5660.c
@@ -526,10 +526,10 @@
"L/R", "R/L", "L/L", "R/R"
};
-static const SOC_ENUM_SINGLE_DECL(rt5660_if1_dac_enum,
+static SOC_ENUM_SINGLE_DECL(rt5660_if1_dac_enum,
RT5660_DIG_INF1_DATA, RT5660_IF1_DAC_IN_SFT, rt5660_data_select);
-static const SOC_ENUM_SINGLE_DECL(rt5660_if1_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5660_if1_adc_enum,
RT5660_DIG_INF1_DATA, RT5660_IF1_ADC_IN_SFT, rt5660_data_select);
static const struct snd_kcontrol_new rt5660_if1_dac_swap_mux =
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index b943dde..3bdd819 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -789,7 +789,10 @@
mutex_lock(&ctl->dsp->pwr_lock);
- memcpy(ctl->cache, p, ctl->len);
+ if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
+ ret = -EPERM;
+ else
+ memcpy(ctl->cache, p, ctl->len);
ctl->set = 1;
if (ctl->enabled && ctl->dsp->running)
@@ -816,6 +819,8 @@
ctl->set = 1;
if (ctl->enabled && ctl->dsp->running)
ret = wm_coeff_write_control(ctl, ctl->cache, size);
+ else if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
+ ret = -EPERM;
}
mutex_unlock(&ctl->dsp->pwr_lock);
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 05cf809..d7013bd 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -13,7 +13,7 @@
config SND_SOC_MT2701_CS42448
tristate "ASoc Audio driver for MT2701 with CS42448 codec"
- depends on SND_SOC_MT2701
+ depends on SND_SOC_MT2701 && I2C
select SND_SOC_CS42XX8_I2C
select SND_SOC_BT_SCO
help
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index d40bfef..172af54 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -360,6 +360,10 @@
snd_soc_dapm_new_control_unlocked(widget->dapm,
&template);
kfree(name);
+ if (IS_ERR(data->widget)) {
+ ret = PTR_ERR(data->widget);
+ goto err_data;
+ }
if (!data->widget) {
ret = -ENOMEM;
goto err_data;
@@ -394,6 +398,10 @@
data->widget = snd_soc_dapm_new_control_unlocked(
widget->dapm, &template);
kfree(name);
+ if (IS_ERR(data->widget)) {
+ ret = PTR_ERR(data->widget);
+ goto err_data;
+ }
if (!data->widget) {
ret = -ENOMEM;
goto err_data;
@@ -3327,11 +3335,22 @@
mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
w = snd_soc_dapm_new_control_unlocked(dapm, widget);
+ /* Do not nag about probe deferrals */
+ if (IS_ERR(w)) {
+ int ret = PTR_ERR(w);
+
+ if (ret != -EPROBE_DEFER)
+ dev_err(dapm->dev,
+ "ASoC: Failed to create DAPM control %s (%d)\n",
+ widget->name, ret);
+ goto out_unlock;
+ }
if (!w)
dev_err(dapm->dev,
"ASoC: Failed to create DAPM control %s\n",
widget->name);
+out_unlock:
mutex_unlock(&dapm->card->dapm_mutex);
return w;
}
@@ -3354,6 +3373,8 @@
w->regulator = devm_regulator_get(dapm->dev, w->name);
if (IS_ERR(w->regulator)) {
ret = PTR_ERR(w->regulator);
+ if (ret == -EPROBE_DEFER)
+ return ERR_PTR(ret);
dev_err(dapm->dev, "ASoC: Failed to request %s: %d\n",
w->name, ret);
return NULL;
@@ -3372,6 +3393,8 @@
w->clk = devm_clk_get(dapm->dev, w->name);
if (IS_ERR(w->clk)) {
ret = PTR_ERR(w->clk);
+ if (ret == -EPROBE_DEFER)
+ return ERR_PTR(ret);
dev_err(dapm->dev, "ASoC: Failed to request %s: %d\n",
w->name, ret);
return NULL;
@@ -3490,6 +3513,16 @@
mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
for (i = 0; i < num; i++) {
w = snd_soc_dapm_new_control_unlocked(dapm, widget);
+ if (IS_ERR(w)) {
+ ret = PTR_ERR(w);
+ /* Do not nag about probe deferrals */
+ if (ret == -EPROBE_DEFER)
+ break;
+ dev_err(dapm->dev,
+ "ASoC: Failed to create DAPM control %s (%d)\n",
+ widget->name, ret);
+ break;
+ }
if (!w) {
dev_err(dapm->dev,
"ASoC: Failed to create DAPM control %s\n",
@@ -3766,6 +3799,15 @@
dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name);
w = snd_soc_dapm_new_control_unlocked(&card->dapm, &template);
+ if (IS_ERR(w)) {
+ ret = PTR_ERR(w);
+ /* Do not nag about probe deferrals */
+ if (ret != -EPROBE_DEFER)
+ dev_err(card->dev,
+ "ASoC: Failed to create %s widget (%d)\n",
+ link_name, ret);
+ goto outfree_kcontrol_news;
+ }
if (!w) {
dev_err(card->dev, "ASoC: Failed to create %s widget\n",
link_name);
@@ -3817,6 +3859,16 @@
template.name);
w = snd_soc_dapm_new_control_unlocked(dapm, &template);
+ if (IS_ERR(w)) {
+ int ret = PTR_ERR(w);
+
+ /* Do not nag about probe deferrals */
+ if (ret != -EPROBE_DEFER)
+ dev_err(dapm->dev,
+ "ASoC: Failed to create %s widget (%d)\n",
+ dai->driver->playback.stream_name, ret);
+ return ret;
+ }
if (!w) {
dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
dai->driver->playback.stream_name);
@@ -3836,6 +3888,16 @@
template.name);
w = snd_soc_dapm_new_control_unlocked(dapm, &template);
+ if (IS_ERR(w)) {
+ int ret = PTR_ERR(w);
+
+ /* Do not nag about probe deferrals */
+ if (ret != -EPROBE_DEFER)
+ dev_err(dapm->dev,
+ "ASoC: Failed to create %s widget (%d)\n",
+ dai->driver->playback.stream_name, ret);
+ return ret;
+ }
if (!w) {
dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
dai->driver->capture.stream_name);
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 6b05047..8a758c9 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -1473,6 +1473,15 @@
widget = snd_soc_dapm_new_control(dapm, &template);
else
widget = snd_soc_dapm_new_control_unlocked(dapm, &template);
+ if (IS_ERR(widget)) {
+ ret = PTR_ERR(widget);
+ /* Do not nag about probe deferrals */
+ if (ret != -EPROBE_DEFER)
+ dev_err(tplg->dev,
+ "ASoC: failed to create widget %s controls (%d)\n",
+ w->name, ret);
+ goto hdr_err;
+ }
if (widget == NULL) {
dev_err(tplg->dev, "ASoC: failed to create widget %s controls\n",
w->name);
diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c
index b871ba4..4458190 100644
--- a/sound/usb/caiaq/device.c
+++ b/sound/usb/caiaq/device.c
@@ -469,10 +469,12 @@
err = snd_usb_caiaq_send_command(cdev, EP1_CMD_GET_DEVICE_INFO, NULL, 0);
if (err)
- return err;
+ goto err_kill_urb;
- if (!wait_event_timeout(cdev->ep1_wait_queue, cdev->spec_received, HZ))
- return -ENODEV;
+ if (!wait_event_timeout(cdev->ep1_wait_queue, cdev->spec_received, HZ)) {
+ err = -ENODEV;
+ goto err_kill_urb;
+ }
usb_string(usb_dev, usb_dev->descriptor.iManufacturer,
cdev->vendor_name, CAIAQ_USB_STR_LEN);
@@ -507,6 +509,10 @@
setup_card(cdev);
return 0;
+
+ err_kill_urb:
+ usb_kill_urb(&cdev->ep1_in_urb);
+ return err;
}
static int snd_probe(struct usb_interface *intf,
diff --git a/sound/usb/card.c b/sound/usb/card.c
index a87a526..f029f8c 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -287,6 +287,7 @@
struct usb_interface_descriptor *altsd;
struct usb_interface *usb_iface;
int i, protocol;
+ int rest_bytes;
usb_iface = usb_ifnum_to_if(dev, ctrlif);
if (!usb_iface) {
@@ -328,12 +329,31 @@
return -EINVAL;
}
+ rest_bytes = (void *)(host_iface->extra + host_iface->extralen) -
+ control_header;
+
+ /* just to be sure -- this shouldn't hit at all */
+ if (rest_bytes <= 0) {
+ dev_err(&dev->dev, "invalid control header\n");
+ return -EINVAL;
+ }
+
h1 = control_header;
+ if (rest_bytes < sizeof(*h1)) {
+ dev_err(&dev->dev, "too short v1 buffer descriptor\n");
+ return -EINVAL;
+ }
+
if (!h1->bInCollection) {
dev_info(&dev->dev, "skipping empty audio interface (v1)\n");
return -EINVAL;
}
+ if (rest_bytes < h1->bLength) {
+ dev_err(&dev->dev, "invalid buffer length (v1)\n");
+ return -EINVAL;
+ }
+
if (h1->bLength < sizeof(*h1) + h1->bInCollection) {
dev_err(&dev->dev, "invalid UAC_HEADER (v1)\n");
return -EINVAL;
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index ab3c280..58d6249 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -775,9 +775,10 @@
return 0;
error:
- if (line6->disconnect)
- line6->disconnect(line6);
- snd_card_free(card);
+ /* we can call disconnect callback here because no close-sync is
+ * needed yet at this point
+ */
+ line6_disconnect(interface);
return ret;
}
EXPORT_SYMBOL_GPL(line6_probe);
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 49cd4a6..5ab9e0c 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -307,6 +307,9 @@
line6->disconnect = podhd_disconnect;
+ init_timer(&pod->startup_timer);
+ INIT_WORK(&pod->startup_work, podhd_startup_workqueue);
+
if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
/* create sysfs entries: */
err = snd_card_add_dev_attr(line6->card, &podhd_dev_attr_group);
@@ -330,8 +333,6 @@
}
/* init device and delay registering */
- init_timer(&pod->startup_timer);
- INIT_WORK(&pod->startup_work, podhd_startup_workqueue);
podhd_startup(pod);
return 0;
}
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 6c3d62f..3501ff9 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -216,7 +216,6 @@
int index, char *buf, int maxlen)
{
int len = usb_string(state->chip->dev, index, buf, maxlen - 1);
- buf[len] = 0;
return len;
}
@@ -2505,6 +2504,9 @@
static void snd_usb_mixer_free(struct usb_mixer_interface *mixer)
{
+ /* kill pending URBs */
+ snd_usb_mixer_disconnect(mixer);
+
kfree(mixer->id_elems);
if (mixer->urb) {
kfree(mixer->urb->transfer_buffer);
@@ -2938,8 +2940,13 @@
void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer)
{
- usb_kill_urb(mixer->urb);
- usb_kill_urb(mixer->rc_urb);
+ if (mixer->disconnected)
+ return;
+ if (mixer->urb)
+ usb_kill_urb(mixer->urb);
+ if (mixer->rc_urb)
+ usb_kill_urb(mixer->rc_urb);
+ mixer->disconnected = true;
}
#ifdef CONFIG_PM
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index 2b4b067..545d99b 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -22,6 +22,8 @@
struct urb *rc_urb;
struct usb_ctrlrequest *rc_setup_packet;
u8 rc_buffer[6];
+
+ bool disconnected;
};
#define MAX_CHANNELS 16 /* max logical channels */
diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c
index bf618e1..e7b934f 100644
--- a/sound/usb/usx2y/usb_stream.c
+++ b/sound/usb/usx2y/usb_stream.c
@@ -191,7 +191,8 @@
}
pg = get_order(read_size);
- sk->s = (void *) __get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO, pg);
+ sk->s = (void *) __get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO|
+ __GFP_NOWARN, pg);
if (!sk->s) {
snd_printk(KERN_WARNING "couldn't __get_free_pages()\n");
goto out;
@@ -211,7 +212,8 @@
pg = get_order(write_size);
sk->write_page =
- (void *)__get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO, pg);
+ (void *)__get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO|
+ __GFP_NOWARN, pg);
if (!sk->write_page) {
snd_printk(KERN_WARNING "couldn't __get_free_pages()\n");
usb_stream_free(sk);
diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c
index 3e199b5..9664b1f 100644
--- a/tools/power/x86/turbostat/turbostat.c
+++ b/tools/power/x86/turbostat/turbostat.c
@@ -2003,8 +2003,10 @@
if (fp == NULL)
fp = fopen_or_die("/sys/class/graphics/fb0/device/drm/card0/gt_cur_freq_mhz", "r");
- else
+ else {
rewind(fp);
+ fflush(fp);
+ }
retval = fscanf(fp, "%d", &gfx_cur_mhz);
if (retval != 1)
diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c
index 03f1fa4..cbb0564 100644
--- a/tools/testing/selftests/seccomp/seccomp_bpf.c
+++ b/tools/testing/selftests/seccomp/seccomp_bpf.c
@@ -6,10 +6,18 @@
*/
#include <sys/types.h>
-#include <asm/siginfo.h>
-#define __have_siginfo_t 1
-#define __have_sigval_t 1
-#define __have_sigevent_t 1
+
+/*
+ * glibc 2.26 and later have SIGSYS in siginfo_t. Before that,
+ * we need to use the kernel's siginfo.h file and trick glibc
+ * into accepting it.
+ */
+#if !__GLIBC_PREREQ(2, 26)
+# include <asm/siginfo.h>
+# define __have_siginfo_t 1
+# define __have_sigval_t 1
+# define __have_sigevent_t 1
+#endif
#include <errno.h>
#include <linux/filter.h>
@@ -676,7 +684,7 @@
syscall(__NR_getpid);
}
-static struct siginfo TRAP_info;
+static siginfo_t TRAP_info;
static volatile int TRAP_nr;
static void TRAP_action(int nr, siginfo_t *info, void *void_context)
{