diff options
author | Pawan Chilka <pchilka@codeaurora.org> | 2019-09-02 14:05:56 +0530 |
---|---|---|
committer | Pawan Chilka <pchilka@codeaurora.org> | 2019-09-02 14:07:24 +0530 |
commit | 824a56366b5fd803dd4e4c230a6e788d34919d1b (patch) | |
tree | 5886f070e4eae166b945682370749f2bae60b9ad | |
parent | 3ad2c95a85f688455db07d1a4b248082d329a52a (diff) | |
parent | a7395d9607aefeb12aba5bb66b3c71a24a2dc6ff (diff) |
Merge commit 'a7395d9607aefeb12aba5bb66b3c71a24a2dc6ff' into HEADLA.UM.8.1.r1-08800-sm8150.0LA.UM.8.1.r1-08700-sm8150.0LA.UM.8.1.r1-08600-sm8150.0
Change-Id: I5de0582240dd8a5e2b05b0ac20851d84fb9e255b
Signed-off-by: Pawan Chilka <pchilka@codeaurora.org>
89 files changed, 3429 insertions, 508 deletions
diff --git a/Documentation/devicetree/bindings/pci/msm_pcie.txt b/Documentation/devicetree/bindings/pci/msm_pcie.txt index 8c12600f65a9..52f9e5ade3ff 100644 --- a/Documentation/devicetree/bindings/pci/msm_pcie.txt +++ b/Documentation/devicetree/bindings/pci/msm_pcie.txt @@ -74,6 +74,10 @@ Optional Properties: is changed from L0s to L0. - qcom,phy-power-down-offset: Offset from PCIe PHY base to control the power state of the PHY. + - qcom,core-preset: Value for PCIe core preset. Determines how aggressive the + PCIe PHY equalization is. The following are recommended settings: + short channels: 0x55555555 (default) + long channels: 0x77777777 - qcom,pcie-phy-ver: version of PCIe PHY. - qcom,phy-sequence: The initialization sequence to bring up the PCIe PHY. Should be specified in groups (offset, value, delay). @@ -288,6 +292,7 @@ Example: qcom,phy-status-offset = <0x800>; qcom,phy-status-status = <6>; qcom,phy-power-down-offset = <0x840>; + qcom,core-preset = <0x55555555>; /* short channels */ qcom,cpl-timeout = <0x2>; iommus = <&anoc0_smmu>; diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt index 3daca154bb52..cccb14dd2f66 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt +++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt @@ -404,6 +404,20 @@ First Level Node - QGAUGE device 'qcom,qg-fast-chg-config' is enabled. The default value if not specified is 1. +- qcom,fvss-enable + Usage: optional + Value type: bool + Definition: Enable Filtered Voltage based SOC scaling. + This logic enables SOC scaling to report + 0 at the cutoff voltage. + +- qcom,fvss-vbatt-mv + Usage: optional + Value type: <u32> + Definition: Battery voltage threshold at which FVSS is + enabled. Applicable only if 'qcom,fvss-enable' + is set. + ========================================================== Second Level Nodes - Peripherals managed by QGAUGE driver ========================================================== diff --git a/arch/arm64/boot/dts/qcom/atoll-atp.dtsi b/arch/arm64/boot/dts/qcom/atoll-atp.dtsi index a2bfa96448c0..5190bed198e0 100644 --- a/arch/arm64/boot/dts/qcom/atoll-atp.dtsi +++ b/arch/arm64/boot/dts/qcom/atoll-atp.dtsi @@ -10,6 +10,7 @@ * GNU General Public License for more details. */ +#include <dt-bindings/gpio/gpio.h> #include <dt-bindings/iio/qcom,spmi-vadc.h> &soc { @@ -65,6 +66,69 @@ extcon = <&pm6150_pdphy>; }; +&ufsphy_mem { + compatible = "qcom,ufs-phy-qmp-v3"; + + vdda-phy-supply = <&pm6150_l4>; /* 0.9v */ + vdda-pll-supply = <&pm6150l_l3>; /* 1.2v */ + vdda-phy-max-microamp = <62900>; + vdda-pll-max-microamp = <18300>; + + status = "ok"; +}; + +&ufshc_mem { + vdd-hba-supply = <&ufs_phy_gdsc>; + vdd-hba-fixed-regulator; + vcc-supply = <&pm6150_l19>; + vcc-voltage-level = <2960000 2960000>; + vcc-max-microamp = <600000>; + vccq2-supply = <&pm6150_l12>; + vccq2-voltage-level = <1750000 1950000>; + vccq2-max-microamp = <600000>; + + qcom,vddp-ref-clk-supply = <&pm6150l_l3>; /* PX10 */ + qcom,vddp-ref-clk-max-microamp = <100>; + + status = "ok"; +}; + +&sdhc_1 { + vdd-supply = <&pm6150_l19>; + qcom,vdd-voltage-level = <2960000 2960000>; + qcom,vdd-current-level = <0 570000>; + + vdd-io-supply = <&pm6150_l12>; + qcom,vdd-io-always-on; + qcom,vdd-io-lpm-sup; + qcom,vdd-io-voltage-level = <1800000 1800000>; + qcom,vdd-io-current-level = <0 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 = <&pm6150l_l9>; + qcom,vdd-voltage-level = <2960000 2960000>; + qcom,vdd-current-level = <0 800000>; + + vdd-io-supply = <&pm6150l_l6>; + qcom,vdd-io-voltage-level = <1800000 2950000>; + qcom,vdd-io-current-level = <0 22000>; + + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &sdc2_cd_off>; + + cd-gpios = <&tlmm 69 GPIO_ACTIVE_LOW>; + + status = "ok"; +}; + &spmi_bus { qcom,pm6150l@4 { pm6150l_adc_tm_iio: adc_tm@3400 { diff --git a/arch/arm64/boot/dts/qcom/atoll-camera.dtsi b/arch/arm64/boot/dts/qcom/atoll-camera.dtsi index 020032d19482..66f7212bd595 100644 --- a/arch/arm64/boot/dts/qcom/atoll-camera.dtsi +++ b/arch/arm64/boot/dts/qcom/atoll-camera.dtsi @@ -1243,4 +1243,46 @@ clock-rates = <0>; status = "ok"; }; + + qcom,cam-lrme { + compatible = "qcom,cam-lrme"; + arch-compat = "lrme"; + status = "ok"; + }; + + cam_lrme: qcom,lrme@ac6b000 { + cell-index = <0>; + compatible = "qcom,lrme"; + reg-names = "lrme"; + reg = <0xac6b000 0x1000>; + reg-cam-base = <0x6b000>; + interrupt-names = "lrme"; + interrupts = <0 476 0>; + regulator-names = "camss"; + camss-supply = <&titan_top_gdsc>; + clock-names = "camera_ahb", + "camera_axi", + "soc_ahb_clk", + "cpas_ahb_clk", + "camnoc_axi_clk", + "lrme_clk_src", + "lrme_clk"; + clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>, + <&clock_gcc GCC_CAMERA_HF_AXI_CLK>, + <&clock_camcc CAM_CC_SOC_AHB_CLK>, + <&clock_camcc CAM_CC_CPAS_AHB_CLK>, + <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>, + <&clock_camcc CAM_CC_LRME_CLK_SRC>, + <&clock_camcc CAM_CC_LRME_CLK>; + clock-rates = <0 0 0 0 0 200000000 200000000>, + <0 0 0 0 0 216000000 216000000>, + <0 0 0 0 0 300000000 300000000>, + <0 0 0 0 0 404000000 404000000>, + <0 0 0 0 0 404000000 404000000>, + <0 0 0 0 0 404000000 404000000>; + clock-cntl-level = "lowsvs", "svs", "svs_l1", "nominal", + "nominal_l1", "turbo"; + src-clock-name = "lrme_clk_src"; + status = "ok"; + }; }; diff --git a/arch/arm64/boot/dts/qcom/atoll-idp.dtsi b/arch/arm64/boot/dts/qcom/atoll-idp.dtsi index 56ae442900f0..649190984014 100644 --- a/arch/arm64/boot/dts/qcom/atoll-idp.dtsi +++ b/arch/arm64/boot/dts/qcom/atoll-idp.dtsi @@ -89,7 +89,7 @@ vdd-hba-supply = <&ufs_phy_gdsc>; vdd-hba-fixed-regulator; vcc-supply = <&pm6150_l19>; - vcc-voltage-level = <2950000 2960000>; + vcc-voltage-level = <2960000 2960000>; vcc-max-microamp = <600000>; vccq2-supply = <&pm6150_l12>; vccq2-voltage-level = <1750000 1950000>; @@ -103,7 +103,7 @@ &sdhc_1 { vdd-supply = <&pm6150_l19>; - qcom,vdd-voltage-level = <2950000 2950000>; + qcom,vdd-voltage-level = <2960000 2960000>; qcom,vdd-current-level = <0 570000>; vdd-io-supply = <&pm6150_l12>; @@ -121,7 +121,7 @@ &sdhc_2 { vdd-supply = <&pm6150l_l9>; - qcom,vdd-voltage-level = <2950000 2950000>; + qcom,vdd-voltage-level = <2960000 2960000>; qcom,vdd-current-level = <0 800000>; vdd-io-supply = <&pm6150l_l6>; @@ -295,3 +295,26 @@ qcom,platform-te-gpio = <&tlmm 10 0>; qcom,platform-reset-gpio = <&pm6150l_gpios 3 0>; }; + +&qupv3_se0_i2c { + status = "ok"; + qcom,clk-freq-out = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + nq@28 { + compatible = "qcom,nq-nci"; + reg = <0x28>; + qcom,nq-irq = <&tlmm 37 0x00>; + qcom,nq-ven = <&tlmm 12 0x00>; + qcom,nq-firm = <&tlmm 36 0x00>; + qcom,nq-clkreq = <&tlmm 31 0x00>; + interrupt-parent = <&tlmm>; + interrupts = <37 0>; + interrupt-names = "nfc_irq"; + pinctrl-names = "nfc_active", "nfc_suspend"; + pinctrl-0 = <&nfc_int_active &nfc_enable_active + &nfc_clk_req_active>; + pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend + &nfc_clk_req_suspend>; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/atoll-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/atoll-pinctrl.dtsi index 46589b2972c8..99472bb0be26 100644 --- a/arch/arm64/boot/dts/qcom/atoll-pinctrl.dtsi +++ b/arch/arm64/boot/dts/qcom/atoll-pinctrl.dtsi @@ -374,6 +374,98 @@ }; }; + nfc { + nfc_int_active: nfc_int_active { + /* active state */ + mux { + /* GPIO 37 NFC Read Interrupt */ + pins = "gpio37"; + function = "gpio"; + }; + + config { + pins = "gpio37"; + drive-strength = <2>; /* 2 MA */ + bias-pull-up; + }; + }; + + nfc_int_suspend: nfc_int_suspend { + /* sleep state */ + mux { + /* GPIO 37 NFC Read Interrupt */ + pins = "gpio37"; + function = "gpio"; + }; + + config { + pins = "gpio37"; + drive-strength = <2>; /* 2 MA */ + bias-pull-up; + }; + }; + + nfc_enable_active: nfc_enable_active { + /* active state */ + mux { + /* 12: Enable 36: Firmware */ + pins = "gpio12", "gpio36"; + function = "gpio"; + }; + + config { + pins = "gpio12", "gpio36"; + drive-strength = <2>; /* 2 MA */ + bias-pull-up; + }; + }; + + nfc_enable_suspend: nfc_enable_suspend { + /* sleep state */ + mux { + /* 12: Enable 36: Firmware */ + pins = "gpio12", "gpio36"; + function = "gpio"; + }; + + config { + pins = "gpio12", "gpio36"; + drive-strength = <2>; /* 2 MA */ + bias-disable; + }; + }; + + nfc_clk_req_active: nfc_clk_req_active { + /* active state */ + mux { + /* GPIO 31: NFC CLOCK REQUEST */ + pins = "gpio31"; + function = "gpio"; + }; + + config { + pins = "gpio31"; + drive-strength = <2>; /* 2 MA */ + bias-pull-up; + }; + }; + + nfc_clk_req_suspend: nfc_clk_req_suspend { + /* sleep state */ + mux { + /* GPIO 31: NFC CLOCK REQUEST */ + pins = "gpio31"; + function = "gpio"; + }; + + config { + pins = "gpio31"; + drive-strength = <2>; /* 2 MA */ + bias-disable; + }; + }; + }; + qupv3_se1_i2c_pins: qupv3_se1_i2c_pins { qupv3_se1_i2c_active: qupv3_se1_i2c_active { mux { diff --git a/arch/arm64/boot/dts/qcom/atoll-qrd.dtsi b/arch/arm64/boot/dts/qcom/atoll-qrd.dtsi index b582b02da5a7..1f92d08e239a 100644 --- a/arch/arm64/boot/dts/qcom/atoll-qrd.dtsi +++ b/arch/arm64/boot/dts/qcom/atoll-qrd.dtsi @@ -14,6 +14,7 @@ #include <dt-bindings/pinctrl/qcom,pmic-gpio.h> #include <dt-bindings/iio/qcom,spmi-vadc.h> #include <dt-bindings/input/input.h> +#include "atoll-audio-overlay.dtsi" #include "atoll-camera-sensor-qrd.dtsi" @@ -141,7 +142,7 @@ vdd-hba-supply = <&ufs_phy_gdsc>; vdd-hba-fixed-regulator; vcc-supply = <&pm6150_l19>; - vcc-voltage-level = <2950000 2960000>; + vcc-voltage-level = <2960000 2960000>; vcc-max-microamp = <600000>; vccq2-supply = <&pm6150_l12>; vccq2-voltage-level = <1750000 1950000>; @@ -155,7 +156,7 @@ &sdhc_1 { vdd-supply = <&pm6150_l19>; - qcom,vdd-voltage-level = <2950000 2950000>; + qcom,vdd-voltage-level = <2960000 2960000>; qcom,vdd-current-level = <0 570000>; vdd-io-supply = <&pm6150_l12>; @@ -173,7 +174,7 @@ &sdhc_2 { vdd-supply = <&pm6150l_l9>; - qcom,vdd-voltage-level = <2950000 2950000>; + qcom,vdd-voltage-level = <2960000 2960000>; qcom,vdd-current-level = <0 800000>; vdd-io-supply = <&pm6150l_l6>; @@ -188,3 +189,112 @@ status = "ok"; }; + +&atoll_snd { + qcom,model = "atoll-qrd-snd-card"; + qcom,audio-routing = + "AMIC1", "MIC BIAS1", + "MIC BIAS1", "Analog Mic1", + "AMIC2", "MIC BIAS2", + "MIC BIAS2", "Analog Mic2", + "AMIC3", "MIC BIAS3", + "MIC BIAS3", "Analog Mic3", + "AMIC4", "MIC BIAS1", + "MIC BIAS1", "Analog Mic4", + "TX DMIC0", "MIC BIAS1", + "MIC BIAS1", "Digital Mic0", + "TX DMIC1", "MIC BIAS1", + "MIC BIAS1", "Digital Mic1", + "TX DMIC2", "MIC BIAS3", + "MIC BIAS3", "Digital Mic2", + "TX DMIC3", "MIC BIAS3", + "MIC BIAS3", "Digital Mic3", + "TX DMIC4", "MIC BIAS4", + "MIC BIAS4", "Digital Mic4", + "IN1_HPHL", "HPHL_OUT", + "IN2_HPHR", "HPHR_OUT", + "IN3_AUX", "AUX_OUT", + "TX SWR_ADC0", "ADC1_OUTPUT", + "TX SWR_ADC1", "ADC2_OUTPUT", + "TX SWR_ADC2", "ADC3_OUTPUT", + "TX SWR_ADC3", "ADC4_OUTPUT", + "TX SWR_DMIC0", "DMIC1_OUTPUT", + "TX SWR_DMIC1", "DMIC2_OUTPUT", + "TX SWR_DMIC2", "DMIC3_OUTPUT", + "TX SWR_DMIC3", "DMIC4_OUTPUT", + "TX SWR_DMIC4", "DMIC5_OUTPUT", + "TX SWR_DMIC5", "DMIC6_OUTPUT", + "TX SWR_DMIC6", "DMIC7_OUTPUT", + "TX SWR_DMIC7", "DMIC8_OUTPUT", + "WSA SRC0_INP", "SRC0", + "WSA_TX DEC0_INP", "TX DEC0 MUX", + "WSA_TX DEC1_INP", "TX DEC1 MUX", + "RX_TX DEC0_INP", "TX DEC0 MUX", + "RX_TX DEC1_INP", "TX DEC1 MUX", + "RX_TX DEC2_INP", "TX DEC2 MUX", + "RX_TX DEC3_INP", "TX DEC3 MUX", + "SpkrLeft IN", "WSA_SPK1 OUT", + "SpkrRight IN", "WSA_SPK2 OUT", + "VA MIC BIAS3", "Digital Mic0", + "VA MIC BIAS3", "Digital Mic1", + "VA MIC BIAS1", "Digital Mic2", + "VA MIC BIAS1", "Digital Mic3", + "VA MIC BIAS4", "Digital Mic4", + "VA MIC BIAS4", "Digital Mic5", + "VA DMIC0", "VA MIC BIAS3", + "VA DMIC1", "VA MIC BIAS3", + "VA DMIC2", "VA MIC BIAS1", + "VA DMIC3", "VA MIC BIAS1", + "VA DMIC4", "VA MIC BIAS4", + "VA DMIC5", "VA MIC BIAS4", + "VA SWR_ADC0", "VA_SWR_CLK", + "VA SWR_ADC1", "VA_SWR_CLK", + "VA SWR_ADC2", "VA_SWR_CLK", + "VA SWR_ADC3", "VA_SWR_CLK", + "VA SWR_MIC0", "VA_SWR_CLK", + "VA SWR_MIC1", "VA_SWR_CLK", + "VA SWR_MIC2", "VA_SWR_CLK", + "VA SWR_MIC3", "VA_SWR_CLK", + "VA SWR_MIC4", "VA_SWR_CLK", + "VA SWR_MIC5", "VA_SWR_CLK", + "VA SWR_MIC6", "VA_SWR_CLK", + "VA SWR_MIC7", "VA_SWR_CLK", + "VA SWR_ADC0", "ADC1_OUTPUT", + "VA SWR_ADC1", "ADC2_OUTPUT", + "VA SWR_ADC2", "ADC3_OUTPUT", + "VA SWR_ADC3", "ADC4_OUTPUT", + "VA SWR_MIC0", "DMIC1_OUTPUT", + "VA SWR_MIC1", "DMIC2_OUTPUT", + "VA SWR_MIC2", "DMIC3_OUTPUT", + "VA SWR_MIC3", "DMIC4_OUTPUT", + "VA SWR_MIC4", "DMIC5_OUTPUT", + "VA SWR_MIC5", "DMIC6_OUTPUT", + "VA SWR_MIC6", "DMIC7_OUTPUT", + "VA SWR_MIC7", "DMIC8_OUTPUT"; + qcom,wsa-max-devs = <1>; + qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0213>; + qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrLeft"; +}; + +&qupv3_se0_i2c { + status = "ok"; + qcom,clk-freq-out = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + nq@28 { + compatible = "qcom,nq-nci"; + reg = <0x28>; + qcom,nq-irq = <&tlmm 37 0x00>; + qcom,nq-ven = <&tlmm 12 0x00>; + qcom,nq-firm = <&tlmm 36 0x00>; + qcom,nq-clkreq = <&tlmm 31 0x00>; + interrupt-parent = <&tlmm>; + interrupts = <37 0>; + interrupt-names = "nfc_irq"; + pinctrl-names = "nfc_active", "nfc_suspend"; + pinctrl-0 = <&nfc_int_active &nfc_enable_active + &nfc_clk_req_active>; + pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend + &nfc_clk_req_suspend>; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/atoll-sde-display.dtsi b/arch/arm64/boot/dts/qcom/atoll-sde-display.dtsi index ee2bb89b1de9..1acc087214a0 100644 --- a/arch/arm64/boot/dts/qcom/atoll-sde-display.dtsi +++ b/arch/arm64/boot/dts/qcom/atoll-sde-display.dtsi @@ -147,6 +147,7 @@ qcom,mdss-dsi-panel-phy-timings = [00 20 08 08 24 23 08 08 05 02 04 00]; qcom,display-topology = <1 0 1>; + qcom,default-topology-index = <0>; }; }; }; diff --git a/arch/arm64/boot/dts/qcom/atoll-sde.dtsi b/arch/arm64/boot/dts/qcom/atoll-sde.dtsi index 8a01f196c5e7..b469e5efe542 100644 --- a/arch/arm64/boot/dts/qcom/atoll-sde.dtsi +++ b/arch/arm64/boot/dts/qcom/atoll-sde.dtsi @@ -136,7 +136,7 @@ qcom,sde-highest-bank-bit = <0x1>; qcom,sde-ubwc-version = <0x200>; qcom,sde-ubwc-bw-calc-version = <0x1>; - qcom,sde-ubwc-static = <0x18>; + qcom,sde-ubwc-static = <0x1E>; qcom,sde-panic-per-pipe; qcom,sde-smart-panel-align-mode = <0xc>; qcom,sde-has-cdp; diff --git a/arch/arm64/boot/dts/qcom/atoll-usb.dtsi b/arch/arm64/boot/dts/qcom/atoll-usb.dtsi index 1552d109b652..61d831a75090 100644 --- a/arch/arm64/boot/dts/qcom/atoll-usb.dtsi +++ b/arch/arm64/boot/dts/qcom/atoll-usb.dtsi @@ -133,9 +133,9 @@ }; /* Primary USB port related QUSB2 PHY */ - qusb_phy0: qusb@88e2000 { + qusb_phy0: qusb@88e3000 { compatible = "qcom,qusb2phy-v2"; - reg = <0x088e2000 0x400>, + reg = <0x088e3000 0x400>, <0x00780258 0x4>, <0x088e7014 0x4>; reg-names = "qusb_phy_base", "efuse_addr", diff --git a/arch/arm64/boot/dts/qcom/atoll.dtsi b/arch/arm64/boot/dts/qcom/atoll.dtsi index f570ca76490d..e47e22d70f77 100644 --- a/arch/arm64/boot/dts/qcom/atoll.dtsi +++ b/arch/arm64/boot/dts/qcom/atoll.dtsi @@ -484,7 +484,7 @@ compatible = "android,fstab"; vendor { compatible = "android,vendor"; - dev = "/dev/block/platform/soc/7c4000.sdhci/by-name/vendor"; + dev = "/dev/block/platform/soc/1d84000.ufshc/by-name/vendor"; type = "ext4"; mnt_flags = "ro,barrier=1,discard"; fsmgr_flags = "wait,slotselect,avb"; @@ -897,6 +897,12 @@ reg-names = "pshold-base", "tcsr-boot-misc-detect"; }; + qcom,mpm2-sleep-counter@0xc221000 { + compatible = "qcom,mpm2-sleep-counter"; + reg = <0xc221000 0x1000>; + clock-frequency = <32768>; + }; + qcom_seecom: qseecom@82200000 { compatible = "qcom,qseecom"; reg = <0x82200000 0x2200000>; @@ -1377,6 +1383,12 @@ cap-based-alloc-and-pwr-collapse; }; + qcom,llcc-perfmon { + compatible = "qcom,llcc-perfmon"; + clocks = <&clock_aop QDSS_CLK>; + clock-names = "qdss_clk"; + }; + qcom,llcc-erp { compatible = "qcom,llcc-erp"; }; @@ -2585,10 +2597,12 @@ }; icnss: qcom,icnss@18800000 { - status = "disabled"; compatible = "qcom,icnss"; - reg = <0x18800000 0x800000>; - reg-names = "membase"; + reg = <0x18800000 0x800000>, + <0xa0000000 0x10000000>, + <0xb0000000 0x10000>; + reg-names = "membase", "smmu_iova_base", "smmu_iova_ipa"; + iommus = <&apps_smmu 0xC0 0x1>; interrupts = <GIC_SPI 414 IRQ_TYPE_LEVEL_HIGH /* CE0 */ >, <GIC_SPI 415 IRQ_TYPE_LEVEL_HIGH /* CE1 */ >, <GIC_SPI 416 IRQ_TYPE_LEVEL_HIGH /* CE2 */ >, @@ -2601,8 +2615,18 @@ <GIC_SPI 423 IRQ_TYPE_LEVEL_HIGH /* CE9 */ >, <GIC_SPI 424 IRQ_TYPE_LEVEL_HIGH /* CE10 */ >, <GIC_SPI 425 IRQ_TYPE_LEVEL_HIGH /* CE11 */ >; - qcom,smmu-s1-bypass; qcom,wlan-msa-fixed-region = <&wlan_fw_mem>; + vdd-cx-mx-supply = <&L9A>; + vdd-1.8-xo-supply = <&L1C>; + vdd-1.3-rfa-supply = <&L2C>; + vdd-3.3-ch0-supply = <&L10C>; + qcom,vdd-cx-mx-config = <640000 640000>; + qcom,smp2p_map_wlan_1_in { + interrupts-extended = <&smp2p_wlan_1_in 0 0>, + <&smp2p_wlan_1_in 1 0>; + interrupt-names = "qcom,smp2p-force-fatal-error", + "qcom,smp2p-early-crash-ind"; + }; }; qcom,venus@aae0000 { diff --git a/arch/arm64/boot/dts/qcom/msm-arm-smmu-trinket.dtsi b/arch/arm64/boot/dts/qcom/msm-arm-smmu-trinket.dtsi index bd1f37554f7e..f9cb196a2c2f 100644 --- a/arch/arm64/boot/dts/qcom/msm-arm-smmu-trinket.dtsi +++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-trinket.dtsi @@ -23,6 +23,7 @@ #iommu-cells = <2>; qcom,dynamic; qcom,skip-init; + qcom,testbus-version = <1>; qcom,no-dynamic-asid; qcom,use-3-lvl-tables; #global-interrupts = <1>; @@ -55,6 +56,7 @@ <0x59c2200 0x8>; reg-names = "base", "status-reg"; qcom,stream-id-range = <0x0 0x400>; + interrupts = <GIC_SPI 176 IRQ_TYPE_LEVEL_HIGH>; }; }; @@ -153,6 +155,7 @@ <0xc782200 0x8>; reg-names = "base", "status-reg"; qcom,stream-id-range = <0x0 0x400>; + interrupts = <GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>; qcom,msm-bus,name = "apps_smmu"; qcom,msm-bus,num-cases = <2>; qcom,msm-bus,active-only; @@ -178,6 +181,7 @@ <0xc782208 0x8>; reg-names = "base", "status-reg"; qcom,stream-id-range = <0x400 0x400>; + interrupts = <GIC_SPI 158 IRQ_TYPE_LEVEL_HIGH>; qcom,regulator-names = "vdd"; vdd-supply = <&hlos1_vote_mm_snoc_mmu_tbu_rt_gdsc>; qcom,msm-bus,name = "apps_smmu"; @@ -205,6 +209,7 @@ <0xc782210 0x8>; reg-names = "base", "status-reg"; qcom,stream-id-range = <0x800 0x400>; + interrupts = <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>; qcom,regulator-names = "vdd"; vdd-supply = <&hlos1_vote_mm_snoc_mmu_tbu_nrt_gdsc>; qcom,msm-bus,name = "apps_smmu"; @@ -232,6 +237,7 @@ <0xc782218 0x8>; reg-names = "base", "status-reg"; qcom,stream-id-range = <0xc00 0x400>; + interrupts = <GIC_SPI 160 IRQ_TYPE_LEVEL_HIGH>; qcom,regulator-names = "vdd"; vdd-supply = <&hlos1_vote_turing_mmu_tbu0_gdsc>; qcom,msm-bus,name = "apps_smmu"; diff --git a/arch/arm64/boot/dts/qcom/qcs405-pcie.dtsi b/arch/arm64/boot/dts/qcom/qcs405-pcie.dtsi index b8fbb572f023..9c5d1d796a7c 100644 --- a/arch/arm64/boot/dts/qcom/qcs405-pcie.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs405-pcie.dtsi @@ -81,6 +81,7 @@ qcom,phy-status-offset = <0x3c>; qcom,phy-status-bit = <0>; qcom,phy-power-down-offset = <0x98>; + qcom,core-preset = <0x77777777>; qcom,boot-option = <0x1>; qcom,keep-powerdown-phy; qcom,no-l0s-supported; diff --git a/arch/arm64/boot/dts/qcom/sa6155-pcie.dtsi b/arch/arm64/boot/dts/qcom/sa6155-pcie.dtsi index d22818a1f459..c0c9ac316e4d 100644 --- a/arch/arm64/boot/dts/qcom/sa6155-pcie.dtsi +++ b/arch/arm64/boot/dts/qcom/sa6155-pcie.dtsi @@ -151,6 +151,7 @@ qcom,phy-status-offset = <0x974>; qcom,phy-status-bit = <6>; qcom,phy-power-down-offset = <0x804>; + qcom,core-preset = <0x77777777>; qcom,boot-option = <0x1>; diff --git a/arch/arm64/boot/dts/qcom/sa8155.dtsi b/arch/arm64/boot/dts/qcom/sa8155.dtsi index 95fa47b280ac..0e7494be74ed 100644 --- a/arch/arm64/boot/dts/qcom/sa8155.dtsi +++ b/arch/arm64/boot/dts/qcom/sa8155.dtsi @@ -94,11 +94,13 @@ qcom,no-l1-supported; qcom,no-l1ss-supported; qcom,no-aux-clk-sync; + qcom,core-preset = <0x77777777>; }; &pcie1 { vreg-1.8-supply = <&pm8150_2_l8>; vreg-0.9-supply = <&pm8150_2_l18>; + qcom,core-preset = <0x77777777>; }; &pcie_ep { diff --git a/arch/arm64/boot/dts/qcom/sa8195p-adp-star-display.dtsi b/arch/arm64/boot/dts/qcom/sa8195p-adp-star-display.dtsi index 24262a3a9614..b2eca5ab8c46 100644 --- a/arch/arm64/boot/dts/qcom/sa8195p-adp-star-display.dtsi +++ b/arch/arm64/boot/dts/qcom/sa8195p-adp-star-display.dtsi @@ -302,5 +302,9 @@ }; &mdss_mdp { + qcom,sde-ctl-display-pref = "primary", "none", "none", + "none", "none"; + qcom,sde-mixer-display-pref = "primary", "none", "none", + "none", "none", "none"; connectors = <&dsi_dp1 &dsi_dp2 &sde_dp &sde_wb>; }; diff --git a/arch/arm64/boot/dts/qcom/sdxprairie-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdxprairie-pinctrl.dtsi index 2ba4e8fefb26..a5addb26df0e 100644 --- a/arch/arm64/boot/dts/qcom/sdxprairie-pinctrl.dtsi +++ b/arch/arm64/boot/dts/qcom/sdxprairie-pinctrl.dtsi @@ -708,7 +708,7 @@ config { pins = "gpio17"; drive-strength = <2>; /* 2 mA */ - bias-pull-down; /* PULL DOWN */ + bias-disable; /* NO PULL */ input-enable; }; }; diff --git a/arch/arm64/boot/dts/qcom/sdxprairie-v2.dtsi b/arch/arm64/boot/dts/qcom/sdxprairie-v2.dtsi index a58b88f7b13d..68f2731612e2 100644 --- a/arch/arm64/boot/dts/qcom/sdxprairie-v2.dtsi +++ b/arch/arm64/boot/dts/qcom/sdxprairie-v2.dtsi @@ -26,6 +26,129 @@ status = "okay"; }; +&pcie0 { + qcom,pcie-phy-ver = <2100>; + qcom,phy-sequence = <0x1240 0x03 0x0 + 0x1010 0x00 0x0 + 0x101c 0x31 0x0 + 0x1020 0x01 0x0 + 0x1024 0xce 0x0 + 0x1028 0x0b 0x0 + 0x1030 0x97 0x0 + 0x1034 0x0c 0x0 + 0x1044 0x18 0x0 + 0x1048 0x90 0x0 + 0x1058 0x0f 0x0 + 0x1074 0x06 0x0 + 0x1078 0x06 0x0 + 0x107c 0x16 0x0 + 0x1080 0x16 0x0 + 0x1084 0x36 0x0 + 0x1088 0x36 0x0 + 0x1094 0x08 0x0 + 0x10a4 0x46 0x0 + 0x10a8 0x04 0x0 + 0x10ac 0x04 0x0 + 0x10b0 0x0d 0x0 + 0x10b4 0x0a 0x0 + 0x10b8 0x1a 0x0 + 0x10bc 0xc3 0x0 + 0x10c4 0xd0 0x0 + 0x10d4 0x05 0x0 + 0x10d8 0x55 0x0 + 0x10dc 0x55 0x0 + 0x10e0 0x05 0x0 + 0x110c 0x02 0x0 + 0x1154 0x34 0x0 + 0x1158 0x12 0x0 + 0x115c 0x00 0x0 + 0x1168 0x05 0x0 + 0x116c 0x04 0x0 + 0x119c 0x88 0x0 + 0x11a0 0x03 0x0 + 0x11ac 0xca 0x0 + 0x11b0 0x1e 0x0 + 0x11b4 0xd8 0x0 + 0x11b8 0x20 0x0 + 0x11bc 0x22 0x0 + 0x106c 0x0a 0x0 + 0x1070 0x10 0x0 + 0x11a4 0x17 0x0 + 0x11a8 0x0b 0x0 + 0x0088 0x05 0x0 + 0x008c 0xf6 0x0 + 0x0090 0x13 0x0 + 0x00e0 0x00 0x0 + 0x00c4 0x00 0x0 + 0x0208 0x0c 0x0 + 0x0258 0x16 0x0 + 0x0378 0x44 0x0 + 0x03c8 0x1a 0x0 + 0x03cc 0x5a 0x0 + 0x03d0 0x09 0x0 + 0x03d4 0x37 0x0 + 0x03d8 0xbd 0x0 + 0x03dc 0xf9 0x0 + 0x03e0 0xbf 0x0 + 0x03e4 0xce 0x0 + 0x03e8 0x62 0x0 + 0x03ec 0xbf 0x0 + 0x03f0 0x7d 0x0 + 0x03f4 0xbf 0x0 + 0x03f8 0xcf 0x0 + 0x03fc 0xd6 0x0 + 0x02ac 0x7f 0x0 + 0x0310 0x55 0x0 + 0x0334 0x0c 0x0 + 0x0338 0x00 0x0 + 0x0350 0x08 0x0 + 0x0400 0xa0 0x0 + 0x043c 0x02 0x0 + 0x0888 0x05 0x0 + 0x088c 0xf6 0x0 + 0x0890 0x13 0x0 + 0x08e0 0x00 0x0 + 0x08c4 0x00 0x0 + 0x0a08 0x0c 0x0 + 0x0a58 0x16 0x0 + 0x0b78 0x44 0x0 + 0x0bc8 0x1a 0x0 + 0x0bcc 0x5a 0x0 + 0x0bd0 0x09 0x0 + 0x0bd4 0x37 0x0 + 0x0bd8 0xbd 0x0 + 0x0bdc 0xf9 0x0 + 0x0be0 0xbf 0x0 + 0x0be4 0xce 0x0 + 0x0be8 0x62 0x0 + 0x0bec 0xbf 0x0 + 0x0bf0 0x7d 0x0 + 0x0bf4 0xbf 0x0 + 0x0bf8 0xcf 0x0 + 0x0bfc 0xd6 0x0 + 0x0aac 0x7f 0x0 + 0x0b10 0x55 0x0 + 0x0b34 0x0c 0x0 + 0x0b38 0x00 0x0 + 0x0b50 0x08 0x0 + 0x0c00 0xa0 0x0 + 0x0c3c 0x02 0x0 + 0x161c 0xc1 0x0 + 0x1690 0x00 0x0 + 0x13e0 0x16 0x0 + 0x13e4 0x02 0x0 + 0x1708 0x02 0x0 + 0x16a0 0x17 0x0 + 0x13d8 0x01 0x0 + 0x16fc 0x01 0x0 + 0x16f0 0x13 0x0 + 0x16f4 0x13 0x0 + 0x1e24 0x00 0x0 + 0x1e28 0x00 0x0 + 0x1200 0x00 0x0 + 0x1244 0x03 0x0>; +}; + &clock_gcc { compatible = "qcom,gcc-sdxprairie-v2", "syscon"; }; diff --git a/arch/arm64/boot/dts/qcom/sdxprairie.dtsi b/arch/arm64/boot/dts/qcom/sdxprairie.dtsi index fdb7bca4eee6..9da6d91ab44f 100644 --- a/arch/arm64/boot/dts/qcom/sdxprairie.dtsi +++ b/arch/arm64/boot/dts/qcom/sdxprairie.dtsi @@ -1044,9 +1044,9 @@ qcom,cpulist = <&CPU0>; qcom,target-dev = <&cpubw>; qcom,core-dev-table = - < 153600 MHZ_TO_MBPS( 300, 4) >, - < 576000 MHZ_TO_MBPS(1017, 4) >, - < 1497600 MHZ_TO_MBPS(1804, 4)>; + < 576000 MHZ_TO_MBPS( 300, 4) >, + < 1497600 MHZ_TO_MBPS(1017, 4) >, + < 1555200 MHZ_TO_MBPS(1804, 4)>; }; cnss_qca6390: qcom,cnss-qca6390@a0000000 { diff --git a/arch/arm64/configs/vendor/trinket_defconfig b/arch/arm64/configs/vendor/trinket_defconfig index b9019ce1f73d..523a2dd3b4be 100644 --- a/arch/arm64/configs/vendor/trinket_defconfig +++ b/arch/arm64/configs/vendor/trinket_defconfig @@ -570,6 +570,7 @@ CONFIG_MSM_QMP=y CONFIG_IOMMU_IO_PGTABLE_FAST=y CONFIG_ARM_SMMU=y CONFIG_IOMMU_TLBSYNC_DEBUG=y +CONFIG_ARM_SMMU_TESTBUS_DUMP=y CONFIG_QCOM_LAZY_MAPPING=y CONFIG_IOMMU_DEBUG=y CONFIG_IOMMU_DEBUG_TRACKING=y diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c index 366eaf277578..aec1fd692eb9 100644 --- a/drivers/char/diag/diag_dci.c +++ b/drivers/char/diag/diag_dci.c @@ -2083,6 +2083,11 @@ static int diag_process_dci_pkt_rsp(unsigned char *buf, int len) if ((ret == DIAG_DCI_NO_ERROR && !common_cmd) || ret < 0) return ret; + reg_entry.cmd_code = 0; + reg_entry.subsys_id = 0; + reg_entry.cmd_code_hi = 0; + reg_entry.cmd_code_lo = 0; + if (header_len >= (sizeof(uint8_t))) reg_entry.cmd_code = header->cmd_code; if (header_len >= (2 * sizeof(uint8_t))) diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c index cd57bb0202aa..acb205dec57b 100644 --- a/drivers/gpu/drm/msm/dp/dp_debug.c +++ b/drivers/gpu/drm/msm/dp/dp_debug.c @@ -200,8 +200,7 @@ static ssize_t dp_debug_write_dpcd(struct file *file, goto bail; size = min_t(size_t, count, SZ_2K); - - if (size <= char_to_nib) + if (size < 4) goto bail; buf = kzalloc(size, GFP_KERNEL); @@ -231,6 +230,8 @@ static ssize_t dp_debug_write_dpcd(struct file *file, } size -= 4; + if (size == 0) + goto bail; dpcd_size = size / char_to_nib; data_len = dpcd_size; @@ -316,6 +317,7 @@ static ssize_t dp_debug_read_dpcd(struct file *file, debug->aux->dpcd_updated(debug->aux); } + len = min_t(size_t, count, len); if (!copy_to_user(user_buff, buf, len)) *ppos += len; @@ -647,6 +649,7 @@ static ssize_t dp_debug_max_pclk_khz_read(struct file *file, debug->dp_debug.max_pclk_khz, debug->parser->max_pclk_khz); + len = min_t(size_t, count, len); if (copy_to_user(user_buff, buf, len)) { kfree(buf); return -EFAULT; @@ -808,6 +811,7 @@ static ssize_t dp_debug_read_connected(struct file *file, len += snprintf(buf, SZ_8, "%d\n", debug->hpd->hpd_high); + len = min_t(size_t, count, len); if (copy_to_user(user_buff, buf, len)) return -EFAULT; @@ -858,6 +862,7 @@ static ssize_t dp_debug_read_hdcp(struct file *file, len = sizeof(debug->dp_debug.hdcp_status); + len = min_t(size_t, count, len); if (copy_to_user(user_buff, debug->dp_debug.hdcp_status, len)) return -EFAULT; @@ -921,6 +926,7 @@ static ssize_t dp_debug_read_edid_modes(struct file *file, } mutex_unlock(&connector->dev->mode_config.mutex); + len = min_t(size_t, count, len); if (copy_to_user(user_buff, buf, len)) { kfree(buf); rc = -EFAULT; @@ -996,6 +1002,7 @@ static ssize_t dp_debug_read_edid_modes_mst(struct file *file, } mutex_unlock(&connector->dev->mode_config.mutex); + len = min_t(size_t, count, len); if (copy_to_user(user_buff, buf, len)) { kfree(buf); rc = -EFAULT; @@ -1036,6 +1043,7 @@ static ssize_t dp_debug_read_mst_con_id(struct file *file, ret = snprintf(buf, max_size, "%u\n", debug->mst_con_id); len += ret; + len = min_t(size_t, count, len); if (copy_to_user(user_buff, buf, len)) { kfree(buf); rc = -EFAULT; @@ -1099,6 +1107,7 @@ static ssize_t dp_debug_read_mst_conn_info(struct file *file, } mutex_unlock(&debug->dp_debug.dp_mst_connector_list.lock); + len = min_t(size_t, count, len); if (copy_to_user(user_buff, buf, len)) { kfree(buf); rc = -EFAULT; @@ -1188,6 +1197,7 @@ static ssize_t dp_debug_read_info(struct file *file, char __user *user_buff, if (dp_debug_check_buffer_overflow(rc, &max_size, &len)) goto error; + len = min_t(size_t, count, len); if (copy_to_user(user_buff, buf, len)) goto error; @@ -1220,6 +1230,7 @@ static ssize_t dp_debug_bw_code_read(struct file *file, len += snprintf(buf + len, (SZ_4K - len), "max_bw_code = %d\n", debug->panel->max_bw_code); + len = min_t(size_t, count, len); if (copy_to_user(user_buff, buf, len)) { kfree(buf); return -EFAULT; @@ -1245,6 +1256,7 @@ static ssize_t dp_debug_tpg_read(struct file *file, len += snprintf(buf, SZ_8, "%d\n", debug->dp_debug.tpg_state); + len = min_t(size_t, count, len); if (copy_to_user(user_buff, buf, len)) return -EFAULT; @@ -1435,6 +1447,7 @@ static ssize_t dp_debug_read_hdr(struct file *file, goto error; } + len = min_t(size_t, count, len); if (copy_to_user(user_buff, buf, len)) { kfree(buf); rc = -EFAULT; @@ -1612,6 +1625,7 @@ static ssize_t dp_debug_read_dump(struct file *file, print_hex_dump(KERN_DEBUG, prefix, DUMP_PREFIX_NONE, 16, 4, buf, len, false); + len = min_t(size_t, count, len); if (copy_to_user(user_buff, buf, len)) return -EFAULT; diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c index a8395de6bb7a..0e58dd8ed4d9 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c @@ -186,14 +186,15 @@ static void dsi_bridge_pre_enable(struct drm_bridge *bridge) return; } - SDE_ATRACE_BEGIN("dsi_bridge_pre_enable"); + SDE_ATRACE_BEGIN("dsi_display_prepare"); rc = dsi_display_prepare(c_bridge->display); if (rc) { pr_err("[%d] DSI display prepare failed, rc=%d\n", c_bridge->id, rc); - SDE_ATRACE_END("dsi_bridge_pre_enable"); + SDE_ATRACE_END("dsi_display_prepare"); return; } + SDE_ATRACE_END("dsi_display_prepare"); SDE_ATRACE_BEGIN("dsi_display_enable"); rc = dsi_display_enable(c_bridge->display); @@ -203,7 +204,6 @@ static void dsi_bridge_pre_enable(struct drm_bridge *bridge) (void)dsi_display_unprepare(c_bridge->display); } SDE_ATRACE_END("dsi_display_enable"); - SDE_ATRACE_END("dsi_bridge_pre_enable"); rc = dsi_display_splash_res_cleanup(c_bridge->display); if (rc) diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c index f369aba32b8a..be06c3c964ef 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder.c @@ -371,6 +371,24 @@ static int _sde_encoder_get_mode_info(struct drm_encoder *drm_enc, return sde_connector_get_mode_info(conn_state, mode_info); } +static bool _sde_encoder_is_autorefresh_enabled( + struct sde_encoder_virt *sde_enc) +{ + struct drm_connector *drm_conn; + + if (!sde_enc->cur_master || + !(sde_enc->disp_info.capabilities & MSM_DISPLAY_CAP_CMD_MODE)) + return false; + + drm_conn = sde_enc->cur_master->connector; + + if (!drm_conn || !drm_conn->state) + return false; + + return sde_connector_get_property(drm_conn->state, + CONNECTOR_PROP_AUTOREFRESH) ? true : false; +} + static bool _sde_encoder_is_dsc_enabled(struct drm_encoder *drm_enc) { struct msm_compression_info *comp_info; @@ -3865,7 +3883,7 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc) u32 pending_kickoff_cnt; struct msm_drm_private *priv = NULL; struct sde_kms *sde_kms = NULL; - bool is_vid_mode = false; + bool is_regdma_blocking = false, is_vid_mode = false; if (!sde_enc) { SDE_ERROR("invalid encoder\n"); @@ -3874,6 +3892,8 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc) is_vid_mode = sde_enc->disp_info.capabilities & MSM_DISPLAY_CAP_VID_MODE; + is_regdma_blocking = (is_vid_mode || + _sde_encoder_is_autorefresh_enabled(sde_enc)); /* don't perform flush/start operations for slave encoders */ for (i = 0; i < sde_enc->num_phys_encs; i++) { @@ -3902,7 +3922,7 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc) if (!phys->ops.needs_single_flush || !phys->ops.needs_single_flush(phys)) { if (ctl->ops.reg_dma_flush) - ctl->ops.reg_dma_flush(ctl, is_vid_mode); + ctl->ops.reg_dma_flush(ctl, is_regdma_blocking); _sde_encoder_trigger_flush(&sde_enc->base, phys, 0x0); } else if (ctl->ops.get_pending_flush) { ctl->ops.get_pending_flush(ctl, &pending_flush); @@ -3913,7 +3933,7 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc) if (pending_flush.pending_flush_mask && sde_enc->cur_master) { ctl = sde_enc->cur_master->hw_ctl; if (ctl->ops.reg_dma_flush) - ctl->ops.reg_dma_flush(ctl, is_vid_mode); + ctl->ops.reg_dma_flush(ctl, is_regdma_blocking); _sde_encoder_trigger_flush(&sde_enc->base, sde_enc->cur_master, &pending_flush); } 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 13fbac27e3fe..bcc5dd6bff03 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c @@ -1594,6 +1594,8 @@ static void sde_encoder_phys_wb_disable(struct sde_encoder_phys *phys_enc) exit: phys_enc->enable_state = SDE_ENC_DISABLED; wb_enc->crtc = NULL; + phys_enc->hw_cdm = NULL; + phys_enc->hw_ctl = NULL; } /** diff --git a/drivers/gpu/drm/msm/sde/sde_trace.h b/drivers/gpu/drm/msm/sde/sde_trace.h index 2909778f4e46..61807d572272 100644 --- a/drivers/gpu/drm/msm/sde/sde_trace.h +++ b/drivers/gpu/drm/msm/sde/sde_trace.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2019, 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 @@ -145,38 +145,24 @@ TRACE_EVENT(sde_encoder_underrun, ); TRACE_EVENT(tracing_mark_write, - TP_PROTO(int pid, const char *name, bool trace_begin), - TP_ARGS(pid, name, trace_begin), + TP_PROTO(char trace_type, const struct task_struct *task, + const char *name, int value), + TP_ARGS(trace_type, task, name, value), TP_STRUCT__entry( + __field(char, trace_type) __field(int, pid) __string(trace_name, name) - __field(bool, trace_begin) - ), - TP_fast_assign( - __entry->pid = pid; - __assign_str(trace_name, name); - __entry->trace_begin = trace_begin; - ), - TP_printk("%s|%d|%s", __entry->trace_begin ? "B" : "E", - __entry->pid, __get_str(trace_name)) -) - -TRACE_EVENT(sde_trace_counter, - TP_PROTO(int pid, char *name, int value), - TP_ARGS(pid, name, value), - TP_STRUCT__entry( - __field(int, pid) - __string(counter_name, name) __field(int, value) ), TP_fast_assign( - __entry->pid = current->tgid; - __assign_str(counter_name, name); + __entry->trace_type = trace_type; + __entry->pid = task ? task->tgid : 0; + __assign_str(trace_name, name); __entry->value = value; ), - TP_printk("%d|%s|%d", __entry->pid, - __get_str(counter_name), __entry->value) -) + TP_printk("%c|%d|%s|%d", __entry->trace_type, + __entry->pid, __get_str(trace_name), __entry->value) +); #define SDE_TRACE_EVTLOG_SIZE 15 TRACE_EVENT(sde_evtlog, @@ -319,12 +305,13 @@ TRACE_EVENT(sde_perf_calc_crtc, __entry->core_clk_rate) ); -#define SDE_ATRACE_END(name) trace_tracing_mark_write(current->tgid, name, 0) -#define SDE_ATRACE_BEGIN(name) trace_tracing_mark_write(current->tgid, name, 1) +#define sde_atrace trace_tracing_mark_write + +#define SDE_ATRACE_END(name) sde_atrace('E', current, name, 0) +#define SDE_ATRACE_BEGIN(name) sde_atrace('B', current, name, 0) #define SDE_ATRACE_FUNC() SDE_ATRACE_BEGIN(__func__) -#define SDE_ATRACE_INT(name, value) \ - trace_sde_trace_counter(current->tgid, name, value) +#define SDE_ATRACE_INT(name, value) sde_atrace('C', current, name, value) #endif /* _SDE_TRACE_H_ */ diff --git a/drivers/gpu/drm/msm/sde_power_handle.c b/drivers/gpu/drm/msm/sde_power_handle.c index 85bfa9ce4419..2e5f4179a70e 100644 --- a/drivers/gpu/drm/msm/sde_power_handle.c +++ b/drivers/gpu/drm/msm/sde_power_handle.c @@ -1046,10 +1046,9 @@ int sde_power_resource_enable(struct sde_power_handle *phandle, SDE_POWER_EVENT_POST_DISABLE); } -end: SDE_EVT32_VERBOSE(enable, SDE_EVTLOG_FUNC_EXIT); - mutex_unlock(&phandle->phandle_lock); SDE_ATRACE_END("sde_power_resource_enable"); + mutex_unlock(&phandle->phandle_lock); return rc; clk_err: @@ -1063,8 +1062,10 @@ vreg_err: sde_power_data_bus_update(&phandle->data_bus_handle[i], 0); data_bus_hdl_err: phandle->current_usecase_ndx = prev_usecase_ndx; - mutex_unlock(&phandle->phandle_lock); SDE_ATRACE_END("sde_power_resource_enable"); + +end: + mutex_unlock(&phandle->phandle_lock); return rc; } diff --git a/drivers/gpu/drm/msm/sde_rsc.c b/drivers/gpu/drm/msm/sde_rsc.c index f0944844a8f3..606f54052aa4 100644 --- a/drivers/gpu/drm/msm/sde_rsc.c +++ b/drivers/gpu/drm/msm/sde_rsc.c @@ -29,6 +29,7 @@ #include <drm/drm_irq.h> #include "sde_rsc_priv.h" #include "sde_dbg.h" +#include "sde_trace.h" #define SDE_RSC_DRV_DBG_NAME "sde_rsc_drv" #define SDE_RSC_WRAPPER_DBG_NAME "sde_rsc_wrapper" @@ -987,6 +988,7 @@ int sde_rsc_client_state_update(struct sde_rsc_client *caller_client, } pr_debug("state switch successfully complete: %d\n", state); + SDE_ATRACE_INT("rsc_state", state); rsc->current_state = state; SDE_EVT32(caller_client->id, caller_client->current_state, state, rsc->current_state, SDE_EVTLOG_FUNC_EXIT); diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c index 11ec6fa448df..b54aed6971bd 100644 --- a/drivers/gpu/msm/kgsl_gmu.c +++ b/drivers/gpu/msm/kgsl_gmu.c @@ -1293,6 +1293,9 @@ static int gmu_aop_mailbox_init(struct kgsl_device *device, struct adreno_device *adreno_dev = ADRENO_DEVICE(device); struct kgsl_mailbox *mailbox = &gmu->mailbox; + if (adreno_is_a640v2(adreno_dev) && (!adreno_dev->speed_bin)) + return 0; + mailbox->client = kzalloc(sizeof(*mailbox->client), GFP_KERNEL); if (!mailbox->client) return -ENOMEM; @@ -1309,8 +1312,8 @@ static int gmu_aop_mailbox_init(struct kgsl_device *device, return PTR_ERR(mailbox->channel); } - if (adreno_dev->speed_bin) - set_bit(ADRENO_ACD_CTRL, &adreno_dev->pwrctrl_flag); + set_bit(ADRENO_ACD_CTRL, &adreno_dev->pwrctrl_flag); + return 0; } diff --git a/drivers/iommu/arm-smmu-debug.c b/drivers/iommu/arm-smmu-debug.c index fde0710be8e2..ad0fbf0c98c6 100644 --- a/drivers/iommu/arm-smmu-debug.c +++ b/drivers/iommu/arm-smmu-debug.c @@ -231,3 +231,60 @@ void arm_smmu_debug_dump_tcu_testbus(struct device *dev, void __iomem *base, arm_smmu_debug_tcu_testbus_select(base, tcu_base, CLK_TESTBUS, READ, 0)); } + +void arm_smmu_debug_set_tnx_tcr_cntl(void __iomem *tbu_base, u64 val) +{ + writel_relaxed(val, tbu_base + ARM_SMMU_TNX_TCR_CNTL); +} + +unsigned long arm_smmu_debug_get_tnx_tcr_cntl(void __iomem *tbu_base) +{ + return readl_relaxed(tbu_base + ARM_SMMU_TNX_TCR_CNTL); +} + +void arm_smmu_debug_set_mask_and_match(void __iomem *tbu_base, u64 sel, + u64 mask, u64 match) +{ + writeq_relaxed(mask, tbu_base + ARM_SMMU_CAPTURE1_MASK(sel)); + writeq_relaxed(match, tbu_base + ARM_SMMU_CAPTURE1_MATCH(sel)); +} + +void arm_smmu_debug_get_mask_and_match(void __iomem *tbu_base, u64 *mask, + u64 *match) +{ + int i; + + for (i = 0; i < NO_OF_MASK_AND_MATCH; ++i) { + mask[i] = readq_relaxed(tbu_base + + ARM_SMMU_CAPTURE1_MASK(i+1)); + match[i] = readq_relaxed(tbu_base + + ARM_SMMU_CAPTURE1_MATCH(i+1)); + } +} + +void arm_smmu_debug_get_capture_snapshot(void __iomem *tbu_base, + u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT]) +{ + int valid, i, j; + + valid = readl_relaxed(tbu_base + APPS_SMMU_TNX_TCR_CNTL_2); + + for (i = 0; i < NO_OF_CAPTURE_POINTS ; ++i) { + if (valid & (1 << i)) + for (j = 0; j < REGS_PER_CAPTURE_POINT; ++j) + snapshot[i][j] = readq_relaxed(tbu_base + + ARM_SMMU_CAPTURE_SNAPSHOT(i, j)); + else + for (j = 0; j < REGS_PER_CAPTURE_POINT; ++j) + snapshot[i][j] = 0xdededede; + } +} + +void arm_smmu_debug_clear_intr_and_validbits(void __iomem *tbu_base) +{ + int val = 0; + + val |= INTR_CLR; + val |= RESET_VALID; + writel_relaxed(val, tbu_base + ARM_SMMU_TNX_TCR_CNTL); +} diff --git a/drivers/iommu/arm-smmu-debug.h b/drivers/iommu/arm-smmu-debug.h index 6b7843a54892..399cab64c8bc 100644 --- a/drivers/iommu/arm-smmu-debug.h +++ b/drivers/iommu/arm-smmu-debug.h @@ -55,6 +55,18 @@ enum testbus_ops { TESTBUS_OUTPUT, }; +#define ARM_SMMU_TNX_TCR_CNTL 0x130 +#define ARM_SMMU_CAPTURE1_MASK(i) (0x100 + (0x8)*(i-1)) +#define ARM_SMMU_CAPTURE1_MATCH(i) (0x118 + (0x8)*(i-1)) +#define ARM_SMMU_CAPTURE_SNAPSHOT(i, j) ((0x138 + (0x10)*i) + j*0x8) +#define APPS_SMMU_TNX_TCR_CNTL_2 0x178 + +#define NO_OF_MASK_AND_MATCH 0x3 +#define NO_OF_CAPTURE_POINTS 0x4 +#define REGS_PER_CAPTURE_POINT 0x2 +#define INTR_CLR (1 << 0) +#define RESET_VALID (1 << 7) + #ifdef CONFIG_ARM_SMMU u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base, @@ -71,7 +83,16 @@ void arm_smmu_debug_dump_tbu_testbus(struct device *dev, void __iomem *tbu_base, u32 testbus_version); void arm_smmu_debug_dump_tcu_testbus(struct device *dev, void __iomem *base, void __iomem *tcu_base, int tcu_testbus_sel); - +void arm_smmu_debug_set_tnx_tcr_cntl(void __iomem *tbu_base, u64 val); +unsigned long arm_smmu_debug_get_tnx_tcr_cntl(void __iomem *tbu_base); +unsigned long arm_smmu_debug_get_tnx_tcr_cntl_2(void __iomem *tbu_base); +void arm_smmu_debug_set_mask_and_match(void __iomem *tbu_base, u64 sel, + u64 mask, u64 match); +void arm_smmu_debug_get_mask_and_match(void __iomem *tbu_base, + u64 *mask, u64 *match); +void arm_smmu_debug_get_capture_snapshot(void __iomem *tbu_base, + u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT]); +void arm_smmu_debug_clear_intr_and_validbits(void __iomem *tbu_base); #else static inline u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base, void __iomem *tcu_base, u32 testbus_version, bool write, @@ -100,4 +121,28 @@ static inline void arm_smmu_debug_dump_tcu_testbus(struct device *dev, int tcu_testbus_sel) { } +void arm_smmu_debug_set_tnx_tcr_cntl(void __iomem *tbu_base, u64 val) +{ +} +unsigned long arm_smmu_debug_get_tnx_tcr_cntl(void __iomem *tbu_base) +{ +} +unsigned long arm_smmu_debug_get_tnx_tcr_cntl_2(void __iomem *tbu_base) +{ +} +void arm_smmu_debug_set_mask_and_match(void __iomem *tbu_base, u64 sel, + u64 mask, u64 match) +{ +} +void arm_smmu_debug_get_mask_and_match(void __iomem *tbu_base, + u64 *mask, u64 *match) +{ +} +void arm_smmu_debug_get_capture_snapshot(void __iomem *tbu_base, + u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT]) +{ +} +void arm_smmu_debug_clear_intr_and_validbits(void __iomem *tbu_base) +{ +} #endif diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 52eb755a5335..d69ac838de7a 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -353,6 +353,7 @@ static int tbu_testbus_sel = TBU_TESTBUS_SEL_ALL; static int tcu_testbus_sel = TCU_TESTBUS_SEL_ALL; static struct dentry *debugfs_testbus_dir; static DEFINE_SPINLOCK(testbus_lock); +static struct dentry *debugfs_capturebus_dir; module_param_named(tcu_testbus_sel, tcu_testbus_sel, int, 0644); module_param_named(tbu_testbus_sel, tbu_testbus_sel, int, 0644); @@ -430,6 +431,7 @@ struct qsmmuv500_tbu_device { /* Protects halt count */ spinlock_t halt_lock; u32 halt_count; + unsigned int *irqs; }; static atomic_t cavium_smmu_context_count = ATOMIC_INIT(0); @@ -5971,6 +5973,260 @@ err: return 0; } +static ssize_t arm_smmu_debug_capturebus_snapshot_read(struct file *file, + char __user *ubuf, size_t count, loff_t *offset) +{ + struct qsmmuv500_tbu_device *tbu = file->private_data; + struct arm_smmu_device *smmu = tbu->smmu; + void __iomem *tbu_base = tbu->base; + u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT]; + char buf[400]; + ssize_t retval; + size_t buflen; + int buf_len = sizeof(buf); + int i, j; + + if (*offset) + return 0; + + memset(buf, 0, buf_len); + + arm_smmu_power_on(smmu->pwr); + arm_smmu_power_on(tbu->pwr); + + arm_smmu_debug_get_capture_snapshot(tbu_base, snapshot); + + arm_smmu_power_off(tbu->pwr); + arm_smmu_power_off(smmu->pwr); + + for (i = 0; i < NO_OF_CAPTURE_POINTS ; ++i) { + for (j = 0; j < REGS_PER_CAPTURE_POINT; ++j) { + snprintf(buf + strlen(buf), buf_len - strlen(buf), + "Capture_%d_Snapshot_%d : 0x%0llx\n", + i+1, j+1, snapshot[i][j]); + } + } + + buflen = min(count, strlen(buf)); + if (copy_to_user(ubuf, buf, buflen)) { + pr_err_ratelimited("Couldn't copy_to_user\n"); + retval = -EFAULT; + } else { + *offset = 1; + retval = buflen; + } + + return retval; +} +static const struct file_operations arm_smmu_debug_capturebus_snapshot_fops = { + .open = simple_open, + .read = arm_smmu_debug_capturebus_snapshot_read, +}; + +static ssize_t arm_smmu_debug_capturebus_config_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *offset) +{ + struct qsmmuv500_tbu_device *tbu = file->private_data; + struct arm_smmu_device *smmu = tbu->smmu; + void __iomem *tbu_base = tbu->base; + char *comma1, *comma2; + char buf[100]; + u64 sel, mask, match, val; + + if (count >= 100) { + pr_err_ratelimited("Input too large\n"); + goto invalid_format; + } + + memset(buf, 0, 100); + + if (copy_from_user(buf, ubuf, count)) { + pr_err_ratelimited("Couldn't copy from user\n"); + return -EFAULT; + } + + comma1 = strnchr(buf, count, ','); + if (!comma1) + goto invalid_format; + + *comma1 = '\0'; + + if (kstrtou64(buf, 0, &sel)) + goto invalid_format; + + if (sel > 4) { + goto invalid_format; + } else if (sel == 4) { + if (kstrtou64(comma1 + 1, 0, &val)) + goto invalid_format; + goto program_capturebus; + } + + comma2 = strnchr(comma1 + 1, count, ','); + if (!comma2) + goto invalid_format; + + /* split up the words */ + *comma2 = '\0'; + + if (kstrtou64(comma1 + 1, 0, &mask)) + goto invalid_format; + + if (kstrtou64(comma2 + 1, 0, &match)) + goto invalid_format; + +program_capturebus: + arm_smmu_power_on(smmu->pwr); + arm_smmu_power_on(tbu->pwr); + + if (sel == 4) + arm_smmu_debug_set_tnx_tcr_cntl(tbu_base, val); + else + arm_smmu_debug_set_mask_and_match(tbu_base, sel, mask, match); + + arm_smmu_power_off(tbu->pwr); + arm_smmu_power_off(smmu->pwr); + return count; + +invalid_format: + pr_err_ratelimited("Invalid format. Expected: <1/2/3,Mask,Match> (or) <4,TNX_TCR_CNTL>>\n"); + return -EINVAL; +} +static ssize_t arm_smmu_debug_capturebus_config_read(struct file *file, + char __user *ubuf, size_t count, loff_t *offset) +{ + struct qsmmuv500_tbu_device *tbu = file->private_data; + struct arm_smmu_device *smmu = tbu->smmu; + void __iomem *tbu_base = tbu->base; + unsigned long val; + u64 mask[NO_OF_MASK_AND_MATCH], match[NO_OF_MASK_AND_MATCH]; + char buf[400]; + ssize_t retval; + size_t buflen; + int buf_len = sizeof(buf); + int i; + + if (*offset) + return 0; + + memset(buf, 0, buf_len); + + arm_smmu_power_on(smmu->pwr); + arm_smmu_power_on(tbu->pwr); + + arm_smmu_debug_get_mask_and_match(tbu_base, + mask, match); + val = arm_smmu_debug_get_tnx_tcr_cntl(tbu_base); + + arm_smmu_power_off(tbu->pwr); + arm_smmu_power_off(smmu->pwr); + + for (i = 0; i < NO_OF_MASK_AND_MATCH; ++i) { + snprintf(buf + strlen(buf), buf_len - strlen(buf), + "Mask_%d : 0x%0llx\t", i+1, mask[i]); + snprintf(buf + strlen(buf), buf_len - strlen(buf), + "Match_%d : 0x%0llx\n", i+1, match[i]); + } + snprintf(buf + strlen(buf), buf_len - strlen(buf), "0x%0x\n", val); + + buflen = min(count, strlen(buf)); + if (copy_to_user(ubuf, buf, buflen)) { + pr_err_ratelimited("Couldn't copy_to_user\n"); + retval = -EFAULT; + } else { + *offset = 1; + retval = buflen; + } + + return retval; +} + +static const struct file_operations arm_smmu_debug_capturebus_config_fops = { + .open = simple_open, + .write = arm_smmu_debug_capturebus_config_write, + .read = arm_smmu_debug_capturebus_config_read, +}; + +static int qsmmuv500_capturebus_init(struct qsmmuv500_tbu_device *tbu) +{ + struct dentry *capturebus_dir; + + if (!debugfs_capturebus_dir) { + debugfs_capturebus_dir = debugfs_create_dir( + "capturebus", iommu_debugfs_top); + if (!debugfs_capturebus_dir) { + pr_err_ratelimited("Couldn't create iommu/capturebus debugfs directory\n"); + return -ENODEV; + } + } + capturebus_dir = debugfs_create_dir(dev_name(tbu->dev), + debugfs_capturebus_dir); + if (!capturebus_dir) { + pr_err_ratelimited("Couldn't create iommu/capturebus/%s debugfs directory\n", + dev_name(tbu->dev)); + goto err; + } + + if (!debugfs_create_file("config", 0400, capturebus_dir, tbu, + &arm_smmu_debug_capturebus_config_fops)) { + pr_err_ratelimited("Couldn't create iommu/capturebus/%s/config debugfs file\n", + dev_name(tbu->dev)); + goto err_rmdir; + } + + if (!debugfs_create_file("snapshot", 0400, capturebus_dir, tbu, + &arm_smmu_debug_capturebus_snapshot_fops)) { + pr_err_ratelimited("Couldn't create iommu/capturebus/%s/snapshot debugfs file\n", + dev_name(tbu->dev)); + goto err_rmdir; + } + return 0; +err_rmdir: + debugfs_remove_recursive(capturebus_dir); +err: + return 0; +} + +static irqreturn_t arm_smmu_debug_capture_bus_match(int irq, void *dev) +{ + struct qsmmuv500_tbu_device *tbu = dev; + struct arm_smmu_device *smmu = tbu->smmu; + void __iomem *tbu_base = tbu->base; + u64 mask[NO_OF_MASK_AND_MATCH], match[NO_OF_MASK_AND_MATCH]; + u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT]; + int i, j, val; + + if (arm_smmu_power_on(smmu->pwr) || arm_smmu_power_on(tbu->pwr)) + return IRQ_NONE; + + val = arm_smmu_debug_get_tnx_tcr_cntl(tbu_base); + arm_smmu_debug_get_mask_and_match(tbu_base, mask, match); + arm_smmu_debug_get_capture_snapshot(tbu_base, snapshot); + arm_smmu_debug_clear_intr_and_validbits(tbu_base); + + arm_smmu_power_off(tbu->pwr); + arm_smmu_power_off(smmu->pwr); + + dev_info(tbu->dev, "TNX_TCR_CNTL : 0x%0llx\n", val); + + for (i = 0; i < NO_OF_MASK_AND_MATCH; ++i) { + dev_info(tbu->dev, + "Mask_%d : 0x%0llx\n", i+1, mask[i]); + dev_info(tbu->dev, + "Match_%d : 0x%0llx\n", i+1, match[i]); + } + + for (i = 0; i < NO_OF_CAPTURE_POINTS ; ++i) { + for (j = 0; j < REGS_PER_CAPTURE_POINT; ++j) { + dev_info(tbu->dev, + "Capture_%d_Snapshot_%d : 0x%0llx\n", + i+1, j+1, snapshot[i][j]); + } + } + + return IRQ_HANDLED; +} + static int qsmmuv500_arch_init(struct arm_smmu_device *smmu) { struct resource *res; @@ -6055,7 +6311,7 @@ static int qsmmuv500_tbu_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct qsmmuv500_tbu_device *tbu; const __be32 *cell; - int len; + int len, i, err, num_irqs = 0; tbu = devm_kzalloc(dev, sizeof(*tbu), GFP_KERNEL); if (!tbu) @@ -6082,12 +6338,42 @@ static int qsmmuv500_tbu_probe(struct platform_device *pdev) tbu->sid_start = of_read_number(cell, 1); tbu->num_sids = of_read_number(cell + 1, 1); + while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, num_irqs))) + num_irqs++; + + tbu->irqs = devm_kzalloc(dev, sizeof(*tbu->irqs) * num_irqs, + GFP_KERNEL); + if (!tbu->irqs) + return -ENOMEM; + + for (i = 0; i < num_irqs; ++i) { + int irq = platform_get_irq(pdev, i); + + if (irq < 0) { + dev_err(dev, "failed to get irq index %d\n", i); + return -ENODEV; + } + tbu->irqs[i] = irq; + + err = devm_request_threaded_irq(tbu->dev, tbu->irqs[i], + NULL, arm_smmu_debug_capture_bus_match, + IRQF_ONESHOT | IRQF_SHARED, + "capture bus", tbu); + if (err) { + dev_err(dev, "failed to request capture bus irq%d (%u)\n", + i, tbu->irqs[i]); + return err; + } + } + tbu->pwr = arm_smmu_init_power_resources(pdev); if (IS_ERR(tbu->pwr)) return PTR_ERR(tbu->pwr); dev_set_drvdata(dev, tbu); + qsmmuv500_tbu_testbus_init(tbu); + qsmmuv500_capturebus_init(tbu); return 0; } diff --git a/drivers/media/platform/msm/camera/cam_core/cam_hw_mgr_intf.h b/drivers/media/platform/msm/camera/cam_core/cam_hw_mgr_intf.h index 6b7a9007cff9..2afdafb03184 100644 --- a/drivers/media/platform/msm/camera/cam_core/cam_hw_mgr_intf.h +++ b/drivers/media/platform/msm/camera/cam_core/cam_hw_mgr_intf.h @@ -262,6 +262,16 @@ struct cam_hw_dump_pf_args { bool *mem_found; }; +/** + * struct cam_hw_reset_args -hw reset arguments + * + * @ctxt_to_hw_map: HW context from the acquire + * + */ +struct cam_hw_reset_args { + void *ctxt_to_hw_map; +}; + /* enum cam_hw_mgr_command - Hardware manager command type */ enum cam_hw_mgr_command { CAM_HW_MGR_CMD_INTERNAL, @@ -313,6 +323,7 @@ struct cam_hw_cmd_args { * @hw_open: Function pointer for HW init * @hw_close: Function pointer for HW deinit * @hw_flush: Function pointer for HW flush + * @hw_reset: Function pointer for HW reset * */ struct cam_hw_mgr_intf { @@ -333,6 +344,7 @@ struct cam_hw_mgr_intf { int (*hw_open)(void *hw_priv, void *fw_download_args); int (*hw_close)(void *hw_priv, void *hw_close_args); int (*hw_flush)(void *hw_priv, void *hw_flush_args); + int (*hw_reset)(void *hw_priv, void *hw_reset_args); }; #endif /* _CAM_HW_MGR_INTF_H_ */ 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 0d0ce4210ae4..52dd3196a339 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 @@ -452,6 +452,14 @@ static int __cam_isp_ctx_handle_buf_done_in_activated_state( trace_cam_buf_done("ISP", ctx, req); req_isp = (struct cam_isp_ctx_req *) req->req_priv; + if (ctx_isp->frame_id == 1) + ctx_isp->irq_timestamps = done->irq_mono_boot_time; + else if (ctx_isp->fps && ((done->irq_mono_boot_time - + ctx_isp->irq_timestamps) > ((1000*1000)/ctx_isp->fps))) + ctx_isp->irq_delay_detect = true; + + ctx_isp->irq_timestamps = done->irq_mono_boot_time; + for (i = 0; i < done->num_handles; i++) { for (j = 0; j < req_isp->num_fence_map_out; j++) { if (done->resource_handle[i] == @@ -565,6 +573,26 @@ static int __cam_isp_ctx_handle_buf_done_in_activated_state( ctx_isp->substate_activated); } + if (ctx_isp->active_req_cnt && ctx_isp->irq_delay_detect) { + CAM_ERR(CAM_ISP, "isp req[%lld] IRQ buf done got delayed", + req->request_id); + req = list_first_entry(&ctx->active_req_list, + struct cam_ctx_request, list); + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + + for (j = 0; j < req_isp->num_fence_map_out; j++) { + rc = cam_sync_signal(req_isp->fence_map_out[j].sync_id, + CAM_SYNC_STATE_SIGNALED_ERROR); + if (rc) + CAM_DBG(CAM_ISP, "Sync failed with rc = %d", + rc); + req_isp->fence_map_out[j].sync_id = -1; + } + list_del_init(&req->list); + list_add_tail(&req->list, &ctx->free_req_list); + ctx_isp->active_req_cnt--; + } + ctx_isp->irq_delay_detect = false; end: return rc; } @@ -643,12 +671,47 @@ static void __cam_isp_ctx_send_sof_timestamp( static int __cam_isp_ctx_reg_upd_in_epoch_state( struct cam_isp_context *ctx_isp, void *evt_data) { - if (ctx_isp->frame_id == 1) + struct cam_isp_hw_reg_update_event_data *rup_event_data = evt_data; + + struct cam_context *ctx = ctx_isp->base; + struct cam_ctx_request *req = NULL; + struct cam_isp_ctx_req *req_isp = NULL; + + if (ctx_isp->frame_id == 1) { CAM_DBG(CAM_ISP, "Reg update for early PCR"); - else + if (!list_empty(&ctx->active_req_list)) { + req = list_first_entry(&ctx->active_req_list, + struct cam_ctx_request, list); + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + } else if (!list_empty(&ctx->wait_req_list)) { + req = list_first_entry(&ctx->active_req_list, + struct cam_ctx_request, list); + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + } + } else { + if (!list_empty(&ctx->wait_req_list)) { + req = list_first_entry(&ctx->active_req_list, + struct cam_ctx_request, list); + req_isp = (struct cam_isp_ctx_req *) req->req_priv; + } CAM_WARN(CAM_ISP, "Unexpected reg update in activated substate:%d for frame_id:%lld", ctx_isp->substate_activated, ctx_isp->frame_id); + } + + if (req_isp && req_isp->hw_update_data.fps) { + ctx_isp->fps = req_isp->hw_update_data.fps; + CAM_DBG(CAM_ISP, "req_isp %pK, Upadting ctx_isp->fps %d", + req_isp, ctx_isp->fps); + } + + if (ctx_isp->frame_id == 1) + ctx_isp->irq_timestamps = rup_event_data->irq_mono_boot_time; + else if (ctx_isp->fps && ((rup_event_data->irq_mono_boot_time - + ctx_isp->irq_timestamps) > ((1000*1000)/ctx_isp->fps))) + ctx_isp->irq_delay_detect = true; + + ctx_isp->irq_timestamps = rup_event_data->irq_mono_boot_time; return 0; } @@ -658,7 +721,8 @@ static int __cam_isp_ctx_reg_upd_in_activated_state( int rc = 0; struct cam_ctx_request *req; struct cam_context *ctx = ctx_isp->base; - struct cam_isp_ctx_req *req_isp; + struct cam_isp_ctx_req *req_isp = NULL; + struct cam_isp_hw_reg_update_event_data *rup_event_data = evt_data; if (list_empty(&ctx->wait_req_list)) { CAM_ERR(CAM_ISP, "Reg upd ack with no waiting request"); @@ -683,13 +747,22 @@ static int __cam_isp_ctx_reg_upd_in_activated_state( req->request_id, ctx_isp->active_req_cnt, ctx->ctx_id); } + if (req_isp && req_isp->hw_update_data.fps) + ctx_isp->fps = req_isp->hw_update_data.fps; + /* * This function only called directly from applied and bubble applied * state so change substate here. */ ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_EPOCH; CAM_DBG(CAM_ISP, "next substate %d", ctx_isp->substate_activated); + if (ctx_isp->frame_id == 1) + ctx_isp->irq_timestamps = rup_event_data->irq_mono_boot_time; + else if (ctx_isp->fps && ((rup_event_data->irq_mono_boot_time - + ctx_isp->irq_timestamps) > ((1000*1000)/ctx_isp->fps))) + ctx_isp->irq_delay_detect = true; + ctx_isp->irq_timestamps = rup_event_data->irq_mono_boot_time; end: return rc; } @@ -817,6 +890,14 @@ static int __cam_isp_ctx_sof_in_activated_state( ctx_isp->sof_timestamp_val = sof_event_data->timestamp; ctx_isp->boot_timestamp = sof_event_data->boot_time; + if (ctx_isp->frame_id == 1) + ctx_isp->irq_timestamps = sof_event_data->irq_mono_boot_time; + else if (ctx_isp->fps && ((sof_event_data->irq_mono_boot_time - + ctx_isp->irq_timestamps) > ((1000*1000)/ctx_isp->fps))) + ctx_isp->irq_delay_detect = true; + + ctx_isp->irq_timestamps = sof_event_data->irq_mono_boot_time; + CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx, ctx %u", ctx_isp->frame_id, ctx_isp->sof_timestamp_val, ctx->ctx_id); @@ -828,8 +909,9 @@ static int __cam_isp_ctx_reg_upd_in_sof(struct cam_isp_context *ctx_isp, { int rc = 0; struct cam_ctx_request *req = NULL; - struct cam_isp_ctx_req *req_isp; + struct cam_isp_ctx_req *req_isp = NULL; struct cam_context *ctx = ctx_isp->base; + struct cam_isp_hw_reg_update_event_data *rup_event_data = evt_data; if (ctx->state != CAM_CTX_ACTIVATED && ctx_isp->frame_id > 1) { CAM_DBG(CAM_ISP, "invalid RUP"); @@ -852,6 +934,16 @@ static int __cam_isp_ctx_reg_upd_in_sof(struct cam_isp_context *ctx_isp, "receive rup in unexpected state"); } + if (req_isp && req_isp->hw_update_data.fps) + ctx_isp->fps = req_isp->hw_update_data.fps; + + if (ctx_isp->frame_id == 1) + ctx_isp->irq_timestamps = rup_event_data->irq_mono_boot_time; + else if (ctx_isp->fps && ((rup_event_data->irq_mono_boot_time - + ctx_isp->irq_timestamps) > ((1000*1000)/ctx_isp->fps))) + ctx_isp->irq_delay_detect = true; + + ctx_isp->irq_timestamps = rup_event_data->irq_mono_boot_time; end: return rc; } @@ -860,9 +952,10 @@ static int __cam_isp_ctx_epoch_in_applied(struct cam_isp_context *ctx_isp, void *evt_data) { struct cam_ctx_request *req; - struct cam_isp_ctx_req *req_isp; + struct cam_isp_ctx_req *req_isp = NULL; struct cam_context *ctx = ctx_isp->base; uint64_t request_id = 0; + struct cam_isp_hw_epoch_event_data *epoch_hw_event_data = evt_data; if (list_empty(&ctx->wait_req_list)) { /* @@ -928,6 +1021,15 @@ static int __cam_isp_ctx_epoch_in_applied(struct cam_isp_context *ctx_isp, ctx_isp->substate_activated); end: + if (ctx_isp->frame_id == 1) + ctx_isp->irq_timestamps = + epoch_hw_event_data->irq_mono_boot_time; + else if (ctx_isp->fps && ((epoch_hw_event_data->irq_mono_boot_time - + ctx_isp->irq_timestamps) > ((1000*1000)/ctx_isp->fps))) + ctx_isp->irq_delay_detect = true; + + ctx_isp->irq_timestamps = epoch_hw_event_data->irq_mono_boot_time; + return 0; } @@ -960,6 +1062,14 @@ static int __cam_isp_ctx_sof_in_epoch(struct cam_isp_context *ctx_isp, ctx_isp->sof_timestamp_val = sof_event_data->timestamp; ctx_isp->boot_timestamp = sof_event_data->boot_time; + if (ctx_isp->frame_id == 1) + ctx_isp->irq_timestamps = sof_event_data->irq_mono_boot_time; + else if (ctx_isp->fps && ((sof_event_data->irq_mono_boot_time - + ctx_isp->irq_timestamps) > ((1000*1000)/ctx_isp->fps))) + ctx_isp->irq_delay_detect = true; + + ctx_isp->irq_timestamps = sof_event_data->irq_mono_boot_time; + if (list_empty(&ctx->active_req_list)) ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF; else @@ -1442,7 +1552,7 @@ static int __cam_isp_ctx_fs2_reg_upd_in_sof(struct cam_isp_context *ctx_isp, { int rc = 0; struct cam_ctx_request *req = NULL; - struct cam_isp_ctx_req *req_isp; + struct cam_isp_ctx_req *req_isp = NULL; struct cam_context *ctx = ctx_isp->base; if (ctx->state != CAM_CTX_ACTIVATED && ctx_isp->frame_id > 1) { @@ -1466,6 +1576,9 @@ static int __cam_isp_ctx_fs2_reg_upd_in_sof(struct cam_isp_context *ctx_isp, "receive rup in unexpected state"); } + if (req_isp && req_isp->hw_update_data.fps) + ctx_isp->fps = req_isp->hw_update_data.fps; + end: return rc; } @@ -1476,7 +1589,7 @@ static int __cam_isp_ctx_fs2_reg_upd_in_applied_state( int rc = 0; struct cam_ctx_request *req = NULL; struct cam_context *ctx = ctx_isp->base; - struct cam_isp_ctx_req *req_isp; + struct cam_isp_ctx_req *req_isp = NULL; struct cam_req_mgr_trigger_notify notify; uint64_t request_id = 0; @@ -1499,6 +1612,9 @@ static int __cam_isp_ctx_fs2_reg_upd_in_applied_state( list_add_tail(&req->list, &ctx->free_req_list); } + if (req_isp && req_isp->hw_update_data.fps) + ctx_isp->fps = req_isp->hw_update_data.fps; + /* * This function only called directly from applied and bubble applied * state so change substate here. @@ -1922,16 +2038,19 @@ static int __cam_isp_ctx_flush_req_in_top_state( struct cam_req_mgr_flush_request *flush_req) { int rc = 0; - struct cam_isp_context *ctx_isp; - - ctx_isp = (struct cam_isp_context *) ctx->ctx_priv; + struct cam_isp_context *ctx_isp = + (struct cam_isp_context *) ctx->ctx_priv; + struct cam_isp_stop_args stop_isp; + struct cam_hw_stop_args stop_args; + struct cam_isp_start_args start_isp; + struct cam_hw_reset_args reset_args; if (flush_req->type == CAM_REQ_MGR_FLUSH_TYPE_ALL) { - CAM_INFO(CAM_ISP, "Last request id to flush is %lld", - flush_req->req_id); + CAM_INFO(CAM_ISP, "ctx id:%d Last request id to flush is %lld", + ctx->ctx_id, flush_req->req_id); ctx->last_flush_req = flush_req->req_id; } - CAM_DBG(CAM_ISP, "try to flush pending list"); + CAM_DBG(CAM_ISP, "ctx id:%d try to flush pending list", ctx->ctx_id); spin_lock_bh(&ctx->lock); rc = __cam_isp_ctx_flush_req(ctx, &ctx->pending_req_list, flush_req); @@ -1954,6 +2073,57 @@ static int __cam_isp_ctx_flush_req_in_top_state( spin_unlock_bh(&ctx->lock); atomic_set(&ctx_isp->process_bubble, 0); + if (flush_req->type == CAM_REQ_MGR_FLUSH_TYPE_ALL) { + /* if active and wait list are empty, return */ + spin_lock_bh(&ctx->lock); + if ((list_empty(&ctx->wait_req_list)) && + (list_empty(&ctx->active_req_list))) { + spin_unlock_bh(&ctx->lock); + CAM_DBG(CAM_ISP, "ctx id:%d active,wait list are empty", + ctx->ctx_id); + goto end; + } + spin_unlock_bh(&ctx->lock); + + /* Stop hw first before active list flush */ + CAM_DBG(CAM_ISP, "ctx id:%d try to stop hw", ctx->ctx_id); + stop_args.ctxt_to_hw_map = ctx_isp->hw_ctx; + stop_isp.hw_stop_cmd = CAM_ISP_HW_STOP_AT_FRAME_BOUNDARY; + stop_isp.stop_only = true; + stop_args.args = (void *)&stop_isp; + ctx->hw_mgr_intf->hw_stop(ctx->hw_mgr_intf->hw_mgr_priv, + &stop_args); + + spin_lock_bh(&ctx->lock); + CAM_DBG(CAM_ISP, "try to flush wait list"); + rc = __cam_isp_ctx_flush_req(ctx, &ctx->wait_req_list, + flush_req); + CAM_DBG(CAM_ISP, "try to flush active list"); + rc = __cam_isp_ctx_flush_req(ctx, &ctx->active_req_list, + flush_req); + ctx_isp->active_req_cnt = 0; + spin_unlock_bh(&ctx->lock); + + CAM_DBG(CAM_ISP, "try to reset hw"); + /* Reset hw */ + reset_args.ctxt_to_hw_map = ctx_isp->hw_ctx; + rc = ctx->hw_mgr_intf->hw_reset(ctx->hw_mgr_intf->hw_mgr_priv, + &reset_args); + if (rc) + goto end; + + CAM_DBG(CAM_ISP, "ctx id%d try to start hw", ctx->ctx_id); + /* Start hw */ + start_isp.hw_config.ctxt_to_hw_map = ctx_isp->hw_ctx; + start_isp.start_only = true; + start_isp.hw_config.priv = NULL; + + rc = ctx->hw_mgr_intf->hw_start(ctx->hw_mgr_intf->hw_mgr_priv, + &start_isp); + } + +end: + ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF; return rc; } @@ -2339,7 +2509,7 @@ static int __cam_isp_ctx_rdi_only_reg_upd_in_bubble_applied_state( { struct cam_ctx_request *req; struct cam_context *ctx = ctx_isp->base; - struct cam_isp_ctx_req *req_isp; + struct cam_isp_ctx_req *req_isp = NULL; struct cam_req_mgr_trigger_notify notify; uint64_t request_id = 0; @@ -2394,6 +2564,9 @@ static int __cam_isp_ctx_rdi_only_reg_upd_in_bubble_applied_state( jiffies_to_msecs(jiffies); } + if (req_isp && req_isp->hw_update_data.fps) + ctx_isp->fps = req_isp->hw_update_data.fps; + __cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id, CAM_REQ_MGR_SOF_EVENT_SUCCESS); CAM_DBG(CAM_ISP, "next substate %d", ctx_isp->substate_activated); @@ -2669,7 +2842,7 @@ static int __cam_isp_ctx_config_dev_in_top_state( { int rc = 0, i; struct cam_ctx_request *req = NULL; - struct cam_isp_ctx_req *req_isp; + struct cam_isp_ctx_req *req_isp = NULL; uintptr_t packet_addr; struct cam_packet *packet; size_t len = 0; @@ -2758,6 +2931,7 @@ static int __cam_isp_ctx_config_dev_in_top_state( rc = -EFAULT; goto free_cpu_buf; } + req_isp->num_cfg = cfg.num_hw_update_entries; req_isp->num_fence_map_out = cfg.num_out_map_entries; req_isp->num_fence_map_in = cfg.num_in_map_entries; @@ -2814,6 +2988,7 @@ static int __cam_isp_ctx_config_dev_in_top_state( CAM_ERR(CAM_ISP, "Recevied Update in wrong state"); } } + if (rc) goto put_ref; @@ -3595,8 +3770,6 @@ static int __cam_isp_ctx_apply_req(struct cam_context *ctx, return rc; } - - static int __cam_isp_ctx_handle_irq_in_activated(void *context, uint32_t evt_id, void *evt_data) { 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 a4f4e5ae0ee9..cb73252363db 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 @@ -35,6 +35,11 @@ #define CAM_ISP_CTX_CFG_MAX 22 /* + * Defalut fps value set to 30 + */ +#define CAM_ISP_CTX_DEFAULT_FPS 30 + +/* * Maximum entries in state monitoring array for error logging */ #define CAM_ISP_CTX_STATE_MONITOR_MAX_ENTRIES 20 @@ -180,6 +185,9 @@ struct cam_isp_context_req_id_info { * @hw_acquired: Indicate whether HW resources are acquired * @init_received: Indicate whether init config packet is received * @split_acquire: Indicate whether a separate acquire is expected + * @irq_delay_detect: Indicate whether a irq delay has detected or not + * @irq_timestamps: Timestamp from last handled IRQ + * @fps: Current FPS for the activated state. * */ struct cam_isp_context { @@ -209,6 +217,9 @@ struct cam_isp_context { bool hw_acquired; bool init_received; bool split_acquire; + bool irq_delay_detect; + uint64_t irq_timestamps; + uint32_t fps; }; /** 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 3b6d04a302ed..c0b8c66fad31 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 @@ -146,6 +146,74 @@ static int cam_ife_hw_mgr_is_rdi_res(uint32_t res_id) return rc; } +static const char *cam_ife_hw_mgr_get_res_id( + enum cam_ife_pix_path_res_id csid_res_id) +{ + char *res_name = NULL; + + switch (csid_res_id) { + case CAM_IFE_PIX_PATH_RES_RDI_0: + res_name = "RDI_0"; + break; + case CAM_IFE_PIX_PATH_RES_RDI_1: + res_name = "RDI_1"; + break; + case CAM_IFE_PIX_PATH_RES_RDI_2: + res_name = "RDI_2"; + break; + case CAM_IFE_PIX_PATH_RES_RDI_3: + res_name = "RDI_3"; + break; + case CAM_IFE_PIX_PATH_RES_IPP: + res_name = "IPP"; + break; + case CAM_IFE_PIX_PATH_RES_PPP: + res_name = "PPP"; + break; + case CAM_IFE_PIX_PATH_RES_MAX: + res_name = "Invalid Max res"; + break; + default: + res_name = "Invalid"; + break; + } + return res_name; +} + +static const char *cam_ife_hw_mgr_get_res_type( + enum cam_isp_resource_type csid_res_type) +{ + char *res_type = NULL; + + switch (csid_res_type) { + case CAM_ISP_RESOURCE_UNINT: + res_type = "Unint"; + break; + case CAM_ISP_RESOURCE_SRC: + res_type = "Src"; + break; + case CAM_ISP_RESOURCE_CID: + res_type = "Cid"; + break; + case CAM_ISP_RESOURCE_PIX_PATH: + res_type = "Pix Path"; + break; + case CAM_ISP_RESOURCE_VFE_IN: + res_type = "Vfe In"; + break; + case CAM_ISP_RESOURCE_VFE_OUT: + res_type = "Vfe Out"; + break; + case CAM_ISP_RESOURCE_MAX: + res_type = "Invalid Max res"; + break; + default: + res_type = "Invalid"; + break; + } + return res_type; +} + static int cam_ife_hw_mgr_reset_csid_res( struct cam_ife_hw_mgr_res *isp_hw_res) { @@ -599,6 +667,61 @@ static int cam_ife_hw_mgr_get_ctx( return rc; } +static void cam_ife_hw_mgr_dump_all_ctx( + struct cam_ife_hw_mgr_ctx *ife_ctx) +{ + uint32_t i; + struct cam_ife_hw_mgr_ctx *ctx; + struct cam_ife_hw_mgr_res *hw_mgr; + + mutex_lock(&g_ife_hw_mgr.ctx_mutex); + list_for_each_entry(ctx, &g_ife_hw_mgr.used_ctx_list, list) { + CAM_ERR_RATE_LIMIT(CAM_ISP, + "ctx id:%d dual:%d in src:%d num_base:%d rdi only:%d", + ctx->ctx_index, + ctx->res_list_ife_in.is_dual_vfe, + ctx->res_list_ife_in.res_id, + ctx->num_base, ctx->is_rdi_only_context); + list_for_each_entry(hw_mgr, &ctx->res_list_ife_csid, + list) { + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!hw_mgr->hw_res[i]) + continue; + CAM_ERR_RATE_LIMIT(CAM_ISP, + "csid:%d res_type:%s id:%s state:%d", + hw_mgr->hw_res[i]->hw_intf->hw_idx, + cam_ife_hw_mgr_get_res_type( + hw_mgr->hw_res[i]->res_type), + cam_ife_hw_mgr_get_res_id( + hw_mgr->hw_res[i]->res_id), + hw_mgr->hw_res[i]->res_state); + } + } + list_for_each_entry(hw_mgr, &ctx->res_list_ife_src, + list) { + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!hw_mgr->hw_res[i]) + continue; + CAM_ERR_RATE_LIMIT(CAM_ISP, + "Src IFE:%d res_type:%s id:%s state:%d", + hw_mgr->hw_res[i]->hw_intf->hw_idx, + cam_ife_hw_mgr_get_res_type( + hw_mgr->hw_res[i]->res_type), + cam_ife_hw_mgr_get_res_id( + hw_mgr->hw_res[i]->res_id), + hw_mgr->hw_res[i]->res_state); + } + } + } + CAM_ERR_RATE_LIMIT(CAM_ISP, + "Current ctx id:%d dual:%d in src:%d num_base:%d rdi only:%d", + ife_ctx->ctx_index, + ife_ctx->res_list_ife_in.is_dual_vfe, + ife_ctx->res_list_ife_in.res_id, + ife_ctx->num_base, ife_ctx->is_rdi_only_context); + mutex_unlock(&g_ife_hw_mgr.ctx_mutex); +} + static void cam_ife_mgr_add_base_info( struct cam_ife_hw_mgr_ctx *ctx, enum cam_isp_hw_split_id split_id, @@ -1509,8 +1632,9 @@ static int cam_ife_hw_mgr_acquire_res_ife_csid_pxl( &csid_acquire, sizeof(csid_acquire)); if (rc) { CAM_ERR(CAM_ISP, - "Cannot acquire ife csid pxl path rsrc %s", - (is_ipp) ? "IPP" : "PPP"); + "Cannot acquire ife csid pxl path rsrc %s, hw=%d rc=%d", + (is_ipp) ? "IPP" : "PPP", + hw_intf->hw_idx, rc); goto put_res; } @@ -1622,7 +1746,6 @@ static int cam_ife_hw_mgr_acquire_res_ife_csid_rdi( "CSID Path reserve failed hw=%d rc=%d cid=%d", hw_intf->hw_idx, rc, cid_res->hw_res[0]->res_id); - goto put_res; } @@ -2018,6 +2141,8 @@ static int cam_ife_mgr_acquire_hw(void *hw_mgr_priv, void *acquire_hw_args) return 0; free_res: + /*Dump all the current acquired resources */ + cam_ife_hw_mgr_dump_all_ctx(ife_ctx); cam_ife_hw_mgr_release_hw_for_ctx(ife_ctx); free_cdm: cam_cdm_release(ife_ctx->cdm_handle); @@ -2172,11 +2297,13 @@ static int cam_ife_mgr_acquire_dev(void *hw_mgr_priv, void *acquire_hw_args) ife_ctx->ctx_in_use = 1; cam_ife_hw_mgr_put_ctx(&ife_hw_mgr->used_ctx_list, &ife_ctx); - CAM_DBG(CAM_ISP, "Exit...(success)"); return 0; + free_res: + /*Dump all the current acquired resources */ + cam_ife_hw_mgr_dump_all_ctx(ife_ctx); cam_ife_hw_mgr_release_hw_for_ctx(ife_ctx); cam_cdm_release(ife_ctx->cdm_handle); free_ctx: @@ -2572,6 +2699,11 @@ static int cam_ife_mgr_pause_hw(struct cam_ife_hw_mgr_ctx *ctx) return cam_ife_mgr_bw_control(ctx, CAM_VFE_BW_CONTROL_EXCLUDE); } +static int cam_ife_mgr_resume_hw(struct cam_ife_hw_mgr_ctx *ctx) +{ + return cam_ife_mgr_bw_control(ctx, CAM_VFE_BW_CONTROL_INCLUDE); +} + /* entry function: stop_hw */ static int cam_ife_mgr_stop_hw(void *hw_mgr_priv, void *stop_hw_args) { @@ -2925,6 +3057,9 @@ start_only: CAM_DBG(CAM_ISP, "START IFE OUT ... in ctx id:%d", ctx->ctx_index); + if (start_isp->start_only) + cam_ife_mgr_resume_hw(ctx); + /* start the IFE out devices */ for (i = 0; i < CAM_IFE_HW_OUT_RES_MAX; i++) { rc = cam_ife_hw_mgr_start_hw_res( @@ -3025,6 +3160,44 @@ static int cam_ife_mgr_write(void *hw_mgr_priv, void *write_args) return -EPERM; } +static int cam_ife_mgr_reset(void *hw_mgr_priv, void *hw_reset_args) +{ + struct cam_ife_hw_mgr *hw_mgr = hw_mgr_priv; + struct cam_hw_reset_args *reset_args = hw_reset_args; + struct cam_ife_hw_mgr_ctx *ctx; + struct cam_ife_hw_mgr_res *hw_mgr_res; + uint32_t i; + int rc = 0; + + if (!hw_mgr_priv || !hw_reset_args) { + CAM_ERR(CAM_ISP, "Invalid arguments"); + return -EINVAL; + } + + ctx = (struct cam_ife_hw_mgr_ctx *)reset_args->ctxt_to_hw_map; + if (!ctx || !ctx->ctx_in_use) { + CAM_ERR(CAM_ISP, "Invalid context is used"); + return -EPERM; + } + + CAM_DBG(CAM_ISP, "reset csid and vfe hw"); + 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) rc:%d", + hw_mgr_res->res_id, rc); + goto end; + } + } + + for (i = 0; i < ctx->num_base; i++) + rc = cam_ife_mgr_reset_vfe_hw(hw_mgr, ctx->base[i].idx); + +end: + return rc; +} + static int cam_ife_mgr_release_hw(void *hw_mgr_priv, void *release_hw_args) { @@ -3152,6 +3325,54 @@ static int cam_isp_blob_fe_update( return rc; } +static int cam_isp_blob_fps_config( + uint32_t blob_type, + struct cam_isp_generic_blob_info *blob_info, + struct cam_fps_config *fps_config, + struct cam_hw_prepare_update_args *prepare) +{ + struct cam_ife_hw_mgr_ctx *ctx = NULL; + struct cam_ife_hw_mgr_res *hw_mgr_res; + struct cam_hw_intf *hw_intf; + struct cam_vfe_fps_config_args fps_config_args; + int rc = -EINVAL; + uint32_t i; + + ctx = prepare->ctxt_to_hw_map; + + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) { + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!hw_mgr_res->hw_res[i]) + continue; + + if (hw_mgr_res->res_id == CAM_ISP_HW_VFE_IN_CAMIF) { + hw_intf = hw_mgr_res->hw_res[i]->hw_intf; + if (hw_intf && hw_intf->hw_ops.process_cmd) { + fps_config_args.fps = + fps_config->fps; + fps_config_args.node_res = + hw_mgr_res->hw_res[i]; + + rc = hw_intf->hw_ops.process_cmd( + hw_intf->hw_priv, + CAM_ISP_HW_CMD_FPS_CONFIG, + &fps_config_args, + sizeof( + struct cam_vfe_fps_config_args) + ); + if (rc) + CAM_ERR(CAM_ISP, + "Failed fps config:%d", + fps_config->fps); + } else + CAM_WARN(CAM_ISP, "NULL hw_intf!"); + } + } + } + + return rc; +} + static int cam_isp_blob_ubwc_update( uint32_t blob_type, struct cam_isp_generic_blob_info *blob_info, @@ -3528,6 +3749,75 @@ static int cam_isp_blob_clock_update( return rc; } +static int cam_isp_blob_sensor_config( + uint32_t blob_type, + struct cam_isp_generic_blob_info *blob_info, + struct cam_isp_sensor_config *dim_config, + struct cam_hw_prepare_update_args *prepare) +{ + struct cam_ife_hw_mgr_ctx *ctx = NULL; + struct cam_ife_hw_mgr_res *hw_mgr_res; + struct cam_hw_intf *hw_intf; + struct cam_ife_sensor_dimension_update_args update_args; + int rc = -EINVAL, found = 0; + uint32_t i, j; + struct cam_isp_sensor_dimension *path_config; + + ctx = prepare->ctxt_to_hw_map; + + list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_csid, list) { + for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) { + if (!hw_mgr_res->hw_res[i]) + continue; + found = 1; + hw_intf = hw_mgr_res->hw_res[i]->hw_intf; + if (hw_intf && hw_intf->hw_ops.process_cmd) { + path_config = &(dim_config->ipp_path); + update_args.ipp_path.width = + path_config->width; + update_args.ipp_path.height = + path_config->height; + update_args.ipp_path.measure_enabled = + path_config->measure_enabled; + path_config = &(dim_config->ppp_path); + update_args.ppp_path.width = + path_config->width; + update_args.ppp_path.height = + path_config->height; + update_args.ppp_path.measure_enabled = + path_config->measure_enabled; + for (j = 0; j < CAM_IFE_RDI_NUM_MAX; j++) { + path_config = + &(dim_config->rdi_path[j]); + update_args.rdi_path[j].width = + path_config->width; + update_args.rdi_path[j].height = + path_config->height; + update_args.rdi_path[j].measure_enabled = + path_config->measure_enabled; + } + rc = hw_intf->hw_ops.process_cmd( + hw_intf->hw_priv, + CAM_IFE_CSID_SET_SENSOR_DIMENSION_CFG, + &update_args, + sizeof( + struct + cam_ife_sensor_dimension_update_args) + ); + if (rc) + CAM_ERR(CAM_ISP, + "Dimension Update failed"); + } else + CAM_ERR(CAM_ISP, "hw_intf is NULL"); + } + if (found) + break; + } + + return rc; +} + + void fill_res_bitmap(uint32_t resource_type, unsigned long *res_bitmap) { @@ -3620,12 +3910,6 @@ static int cam_isp_packet_generic_blob_handler(void *user_data, return -EINVAL; } - if (blob_type >= CAM_ISP_GENERIC_BLOB_TYPE_MAX) { - CAM_WARN(CAM_ISP, "Invalid Blob Type %d Max %d", blob_type, - CAM_ISP_GENERIC_BLOB_TYPE_MAX); - return 0; - } - prepare = blob_info->prepare; if (!prepare) { CAM_ERR(CAM_ISP, "Failed. prepare is NULL, blob_type %d", @@ -3633,7 +3917,6 @@ static int cam_isp_packet_generic_blob_handler(void *user_data, return -EINVAL; } - CAM_DBG(CAM_ISP, "FS2: BLOB Type: %d", blob_type); switch (blob_type) { case CAM_ISP_GENERIC_BLOB_TYPE_HFR_CONFIG: { struct cam_isp_resource_hfr_config *hfr_config; @@ -3868,6 +4151,49 @@ static int cam_isp_packet_generic_blob_handler(void *user_data, CAM_ERR(CAM_ISP, "Init Frame drop Update Failed"); } break; + case CAM_ISP_GENERIC_BLOB_TYPE_SENSOR_DIMENSION_CONFIG: { + struct cam_isp_sensor_config *csid_dim_config; + + if (blob_size < sizeof(struct cam_isp_sensor_config)) { + CAM_ERR(CAM_ISP, "Invalid blob size %u expected %u", + blob_size, + sizeof(struct cam_isp_sensor_config)); + return -EINVAL; + } + + csid_dim_config = + (struct cam_isp_sensor_config *)blob_data; + + rc = cam_isp_blob_sensor_config(blob_type, blob_info, + csid_dim_config, prepare); + if (rc) + CAM_ERR(CAM_ISP, + "Sensor Dimension Update Failed rc: %d", rc); + } + break; + case CAM_ISP_GENERIC_BLOB_TYPE_FPS_CONFIG: { + struct cam_fps_config *fps_config; + struct cam_isp_prepare_hw_update_data *prepare_hw_data; + + if (blob_size < sizeof(struct cam_fps_config)) { + CAM_ERR(CAM_ISP, "Invalid fps blob size %u expected %u", + blob_size, sizeof(struct cam_fps_config)); + return -EINVAL; + } + + fps_config = (struct cam_fps_config *)blob_data; + prepare_hw_data = (struct cam_isp_prepare_hw_update_data *) + prepare->priv; + + prepare_hw_data->fps = fps_config->fps; + + rc = cam_isp_blob_fps_config(blob_type, blob_info, + fps_config, prepare); + if (rc) + CAM_ERR(CAM_ISP, "FPS Update Failed rc: %d", rc); + + } + break; default: CAM_WARN(CAM_ISP, "Invalid blob type %d", blob_type); break; @@ -4025,10 +4351,6 @@ end: return rc; } -static int cam_ife_mgr_resume_hw(struct cam_ife_hw_mgr_ctx *ctx) -{ - return cam_ife_mgr_bw_control(ctx, CAM_VFE_BW_CONTROL_INCLUDE); -} static int cam_ife_mgr_sof_irq_debug( struct cam_ife_hw_mgr_ctx *ctx, @@ -4766,6 +5088,8 @@ static int cam_ife_hw_mgr_handle_reg_update( break; if (!rup_status) { + rup_event_data.irq_mono_boot_time = + evt_payload->ts.time_usecs; ife_hwr_irq_rup_cb( ife_hwr_mgr_ctx->common.cb_priv, CAM_ISP_HW_EVENT_REG_UPDATE, @@ -4795,6 +5119,8 @@ static int cam_ife_hw_mgr_handle_reg_update( if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending)) break; if (!rup_status) { + rup_event_data.irq_mono_boot_time = + evt_payload->ts.time_usecs; /* Send the Reg update hw event */ ife_hwr_irq_rup_cb( ife_hwr_mgr_ctx->common.cb_priv, @@ -5154,6 +5480,8 @@ static int cam_ife_hw_mgr_handle_sof( ife_hw_mgr_ctx, &sof_done_event_data.timestamp, &sof_done_event_data.boot_time); + sof_done_event_data.irq_mono_boot_time = + evt_payload->ts.time_usecs; ife_hw_irq_sof_cb( ife_hw_mgr_ctx->common.cb_priv, @@ -5177,6 +5505,8 @@ static int cam_ife_hw_mgr_handle_sof( ife_hw_mgr_ctx, &sof_done_event_data.timestamp, &sof_done_event_data.boot_time); + sof_done_event_data.irq_mono_boot_time = + evt_payload->ts.time_usecs; ife_hw_irq_sof_cb( ife_hw_mgr_ctx->common.cb_priv, @@ -5262,13 +5592,15 @@ static int cam_ife_hw_mgr_handle_eof_for_camif_hw_res( if (atomic_read( &ife_hwr_mgr_ctx->overflow_pending)) break; - if (!eof_status) + if (!eof_status) { + eof_done_event_data.irq_mono_boot_time = + evt_payload->ts.time_usecs; ife_hwr_irq_eof_cb( ife_hwr_mgr_ctx->common.cb_priv, CAM_ISP_HW_EVENT_EOF, &eof_done_event_data); + } } - break; /* Handling dual VFE Scenario */ case 1: @@ -5309,11 +5641,14 @@ static int cam_ife_hw_mgr_handle_eof_for_camif_hw_res( if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending)) break; - if (!rc) + if (!rc) { + eof_done_event_data.irq_mono_boot_time = + evt_payload->ts.time_usecs; ife_hwr_irq_eof_cb( ife_hwr_mgr_ctx->common.cb_priv, CAM_ISP_HW_EVENT_EOF, &eof_done_event_data); + } break; @@ -5413,6 +5748,8 @@ static int cam_ife_hw_mgr_handle_buf_done_for_hw_res( if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending)) break; + buf_done_event_data.irq_mono_boot_time = + evt_payload->ts.time_usecs; /* Report for Successful buf_done event if any */ if (buf_done_event_data.num_handles > 0 && ife_hwr_irq_wm_done_cb) { @@ -5852,6 +6189,7 @@ int cam_ife_hw_mgr_init(struct cam_hw_mgr_intf *hw_mgr_intf, int *iommu_hdl) hw_mgr_intf->hw_prepare_update = cam_ife_mgr_prepare_hw_update; hw_mgr_intf->hw_config = cam_ife_mgr_config_hw; hw_mgr_intf->hw_cmd = cam_ife_mgr_cmd; + hw_mgr_intf->hw_reset = cam_ife_mgr_reset; if (iommu_hdl) *iommu_hdl = g_ife_hw_mgr.mgr_common.img_iommu_hdl; diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h index 096e0f186bbf..1ab9361af1d1 100644 --- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h +++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h @@ -130,6 +130,7 @@ struct cam_isp_bw_config_internal { * @bw_config: BW config information * @bw_config_valid: Flag indicating whether the bw_config at the index * is valid or not + * @fps: fps vaue which has been updated in hw * */ struct cam_isp_prepare_hw_update_data { @@ -137,40 +138,47 @@ struct cam_isp_prepare_hw_update_data { struct cam_isp_bw_config_internal bw_config[CAM_IFE_HW_NUM_MAX]; struct cam_isp_bw_config_internal_ab bw_config_ab[CAM_IFE_HW_NUM_MAX]; bool bw_config_valid[CAM_IFE_HW_NUM_MAX]; + uint32_t fps; }; /** * struct cam_isp_hw_sof_event_data - Event payload for CAM_HW_EVENT_SOF * - * @timestamp: Time stamp for the sof event - * @boot_time: Boot time stamp for the sof event + * @timestamp: Time stamp for the sof event + * @boot_time: Boot time stamp for the sof event + * @irq_mono_boot_time: Time stamp till the execution of IRQ wrt event started * */ struct cam_isp_hw_sof_event_data { uint64_t timestamp; uint64_t boot_time; + uint64_t irq_mono_boot_time; }; /** * struct cam_isp_hw_reg_update_event_data - Event payload for * CAM_HW_EVENT_REG_UPDATE * - * @timestamp: Time stamp for the reg update event + * @timestamp: Time stamp for the reg update event + * @irq_mono_boot_time: Time stamp till the execution of IRQ wrt event started * */ struct cam_isp_hw_reg_update_event_data { uint64_t timestamp; + uint64_t irq_mono_boot_time; }; /** * struct cam_isp_hw_epoch_event_data - Event payload for CAM_HW_EVENT_EPOCH * - * @timestamp: Time stamp for the epoch event + * @timestamp: Time stamp for the epoch event + * @irq_mono_boot_time: Time stamp till the execution of this event started * */ struct cam_isp_hw_epoch_event_data { uint64_t timestamp; + uint64_t irq_mono_boot_time; }; /** @@ -179,6 +187,7 @@ struct cam_isp_hw_epoch_event_data { * @num_handles: Number of resource handeles * @resource_handle: Resource handle array * @timestamp: Timestamp for the buf done event + * @irq_mono_boot_time: Time stamp till the execution of this event started * */ struct cam_isp_hw_done_event_data { @@ -186,16 +195,19 @@ struct cam_isp_hw_done_event_data { uint32_t resource_handle[ CAM_NUM_OUT_PER_COMP_IRQ_MAX]; uint64_t timestamp; + uint64_t irq_mono_boot_time; }; /** * struct cam_isp_hw_eof_event_data - Event payload for CAM_HW_EVENT_EOF * * @timestamp: Timestamp for the eof event + * @irq_mono_boot_time: Time stamp till the execution of this event started * */ struct cam_isp_hw_eof_event_data { uint64_t timestamp; + uint64_t irq_mono_boot_time; }; /** diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.h index 0e58617e1956..6254b97c436a 100644 --- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.h +++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2019, 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 @@ -293,6 +293,10 @@ static struct cam_ife_csid_common_reg_offset .ppp_irq_mask_all = 0x0, .measure_en_hbi_vbi_cnt_mask = 0xC, .format_measure_en_val = 1, + .format_measure_height_mask_val = 0xFFFF, + .format_measure_height_shift_val = 0x10, + .format_measure_width_mask_val = 0xFFFF, + .format_measure_width_shift_val = 0x0, }; static struct cam_ife_csid_reg_offset cam_ife_csid_170_reg_offset = { diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid175.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid175.h index 46dec6e4156a..680b641038cf 100644 --- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid175.h +++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid175.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2019, 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 @@ -334,6 +334,10 @@ static struct cam_ife_csid_common_reg_offset .ppp_irq_mask_all = 0xFFFF, .measure_en_hbi_vbi_cnt_mask = 0xC, .format_measure_en_val = 1, + .format_measure_height_mask_val = 0xFFFF, + .format_measure_height_shift_val = 0x10, + .format_measure_width_mask_val = 0xFFFF, + .format_measure_width_shift_val = 0x0, }; static struct cam_ife_csid_reg_offset cam_ife_csid_175_reg_offset = { diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid175_200.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid175_200.h index 1d6f7715add6..48570dad5f37 100644 --- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid175_200.h +++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid175_200.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2019, 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 @@ -350,6 +350,10 @@ static struct cam_ife_csid_common_reg_offset .ppp_irq_mask_all = 0xFFFF, .measure_en_hbi_vbi_cnt_mask = 0xC, .format_measure_en_val = 1, + .format_measure_height_mask_val = 0xFFFF, + .format_measure_height_shift_val = 0x10, + .format_measure_width_mask_val = 0xFFFF, + .format_measure_width_shift_val = 0x0, }; static struct cam_ife_csid_reg_offset cam_ife_csid_175_200_reg_offset = { diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c index 5ab397bb7bf8..a3fce7733503 100644 --- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c +++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c @@ -79,7 +79,7 @@ static int cam_ife_csid_is_ipp_ppp_format_supported( static int cam_ife_csid_get_format_rdi( uint32_t in_format, uint32_t out_format, - uint32_t *decode_fmt, uint32_t *plain_fmt) + uint32_t *decode_fmt, uint32_t *plain_fmt, uint32_t *in_bpp) { int rc = 0; @@ -97,6 +97,7 @@ static int cam_ife_csid_get_format_rdi( rc = -EINVAL; break; } + *in_bpp = 6; break; case CAM_FORMAT_MIPI_RAW_8: switch (out_format) { @@ -112,6 +113,7 @@ static int cam_ife_csid_get_format_rdi( rc = -EINVAL; break; } + *in_bpp = 8; break; case CAM_FORMAT_MIPI_RAW_10: switch (out_format) { @@ -127,6 +129,7 @@ static int cam_ife_csid_get_format_rdi( rc = -EINVAL; break; } + *in_bpp = 10; break; case CAM_FORMAT_MIPI_RAW_12: switch (out_format) { @@ -141,6 +144,7 @@ static int cam_ife_csid_get_format_rdi( rc = -EINVAL; break; } + *in_bpp = 12; break; case CAM_FORMAT_MIPI_RAW_14: switch (out_format) { @@ -155,6 +159,7 @@ static int cam_ife_csid_get_format_rdi( rc = -EINVAL; break; } + *in_bpp = 14; break; case CAM_FORMAT_MIPI_RAW_16: switch (out_format) { @@ -169,6 +174,7 @@ static int cam_ife_csid_get_format_rdi( rc = -EINVAL; break; } + *in_bpp = 16; break; case CAM_FORMAT_MIPI_RAW_20: switch (out_format) { @@ -183,6 +189,7 @@ static int cam_ife_csid_get_format_rdi( rc = -EINVAL; break; } + *in_bpp = 20; break; case CAM_FORMAT_DPCM_10_6_10: *decode_fmt = 0x7; @@ -567,10 +574,6 @@ static int cam_ife_csid_path_reset(struct cam_ife_csid_hw *csid_hw, init_completion(complete); reset_strb_val = csid_reg->cmn_reg->path_rst_stb_all; - /* Enable the Test gen before reset */ - cam_io_w_mb(1, csid_hw->hw_info->soc_info.reg_map[0].mem_base + - csid_reg->tpg_reg->csid_tpg_ctrl_addr); - /* Reset the corresponding ife csid path */ cam_io_w_mb(reset_strb_val, soc_info->reg_map[0].mem_base + reset_strb_addr); @@ -585,10 +588,6 @@ static int cam_ife_csid_path_reset(struct cam_ife_csid_hw *csid_hw, rc = -ETIMEDOUT; } - /* Disable Test Gen after reset*/ - cam_io_w_mb(0, soc_info->reg_map[0].mem_base + - csid_reg->tpg_reg->csid_tpg_ctrl_addr); - end: return rc; @@ -1168,6 +1167,11 @@ static int cam_ife_csid_disable_hw(struct cam_ife_csid_hw *csid_hw) for (i = 0; i < CAM_IFE_PIX_PATH_RES_MAX; i++) csid_hw->res_sof_cnt[i] = 0; + csid_hw->ipp_path_config.measure_enabled = 0; + csid_hw->ppp_path_config.measure_enabled = 0; + for (i = 0; i <= CAM_IFE_PIX_PATH_RES_RDI_3; i++) + csid_hw->rdi_path_config[i].measure_enabled = 0; + csid_hw->hw_info->hw_state = CAM_HW_STATE_POWER_DOWN; csid_hw->error_irq_count = 0; csid_hw->first_sof_ts = 0; @@ -1626,6 +1630,7 @@ static int cam_ife_csid_init_config_pxl_path( const struct cam_ife_csid_pxl_reg_offset *pxl_reg = NULL; bool is_ipp; uint32_t decode_format = 0, plain_format = 0, val = 0; + struct cam_isp_sensor_dimension *path_config; path_data = (struct cam_ife_csid_path_cfg *) res->res_priv; csid_reg = csid_hw->csid_info->csid_reg; @@ -1634,9 +1639,11 @@ static int cam_ife_csid_init_config_pxl_path( if (res->res_id == CAM_IFE_PIX_PATH_RES_IPP) { is_ipp = true; pxl_reg = csid_reg->ipp_reg; + path_config = &(csid_hw->ipp_path_config); } else { is_ipp = false; pxl_reg = csid_reg->ppp_reg; + path_config = &(csid_hw->ppp_path_config); } if (!pxl_reg) { @@ -1705,6 +1712,24 @@ static int cam_ife_csid_init_config_pxl_path( } } + /* configure pixel format measure */ + if (path_config->measure_enabled) { + val = (((path_config->height & + csid_reg->cmn_reg->format_measure_height_mask_val) << + csid_reg->cmn_reg->format_measure_height_shift_val) | + (path_config->width & + csid_reg->cmn_reg->format_measure_width_mask_val)); + CAM_DBG(CAM_ISP, "CSID:%d format measure cfg1 value : 0x%x", + csid_hw->hw_intf->hw_idx, val); + + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + pxl_reg->csid_pxl_format_measure_cfg1_addr); + + /* enable pixel and line counter */ + cam_io_w_mb(3, soc_info->reg_map[0].mem_base + + pxl_reg->csid_pxl_format_measure_cfg0_addr); + } + /* set frame drop pattern to 0 and period to 1 */ cam_io_w_mb(1, soc_info->reg_map[0].mem_base + pxl_reg->csid_pxl_frm_drop_period_addr); @@ -1850,6 +1875,7 @@ static int cam_ife_csid_enable_pxl_path( const struct cam_ife_csid_pxl_reg_offset *pxl_reg = NULL; bool is_ipp; uint32_t val = 0, path_status; + struct cam_isp_sensor_dimension *path_config; path_data = (struct cam_ife_csid_path_cfg *) res->res_priv; csid_reg = csid_hw->csid_info->csid_reg; @@ -1858,9 +1884,11 @@ static int cam_ife_csid_enable_pxl_path( if (res->res_id == CAM_IFE_PIX_PATH_RES_IPP) { is_ipp = true; pxl_reg = csid_reg->ipp_reg; + path_config = &(csid_hw->ipp_path_config); } else { is_ipp = false; pxl_reg = csid_reg->ppp_reg; + path_config = &(csid_hw->ppp_path_config); } if (res->res_state != CAM_ISP_RESOURCE_STATE_INIT_HW) { @@ -1921,6 +1949,10 @@ static int cam_ife_csid_enable_pxl_path( if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOF_IRQ) val |= CSID_PATH_INFO_INPUT_EOF; + if (path_config->measure_enabled) + val |= (CSID_PATH_ERROR_PIX_COUNT | + CSID_PATH_ERROR_LINE_COUNT); + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + pxl_reg->csid_pxl_irq_mask_addr); @@ -2021,7 +2053,7 @@ static int cam_ife_csid_init_config_rdi_path( struct cam_ife_csid_path_cfg *path_data; const struct cam_ife_csid_reg_offset *csid_reg; struct cam_hw_soc_info *soc_info; - uint32_t path_format = 0, plain_fmt = 0, val = 0, id; + uint32_t path_format = 0, plain_fmt = 0, val = 0, id, in_bpp = 0; uint32_t format_measure_addr; path_data = (struct cam_ife_csid_path_cfg *) res->res_priv; @@ -2036,7 +2068,7 @@ static int cam_ife_csid_init_config_rdi_path( } rc = cam_ife_csid_get_format_rdi(path_data->in_format, - path_data->out_format, &path_format, &plain_fmt); + path_data->out_format, &path_format, &plain_fmt, &in_bpp); if (rc) return rc; @@ -2085,6 +2117,32 @@ static int cam_ife_csid_init_config_rdi_path( CAM_DBG(CAM_ISP, "CSID:%d Vertical Crop config val: 0x%x", csid_hw->hw_intf->hw_idx, val); } + + /* configure pixel format measure */ + if (csid_hw->rdi_path_config[id].measure_enabled) { + val = ((csid_hw->rdi_path_config[id].height & + csid_reg->cmn_reg->format_measure_height_mask_val) << + csid_reg->cmn_reg->format_measure_height_shift_val); + + if (path_format == 0xF) + val |= (((csid_hw->rdi_path_config[id].width * + in_bpp) / 8) & + csid_reg->cmn_reg->format_measure_width_mask_val); + else + val |= (csid_hw->rdi_path_config[id].width & + csid_reg->cmn_reg->format_measure_width_mask_val); + + CAM_DBG(CAM_ISP, "CSID:%d format measure cfg1 value : 0x%x", + csid_hw->hw_intf->hw_idx, val); + + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_format_measure_cfg1_addr); + + /* enable pixel and line counter */ + cam_io_w_mb(3, soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[id]->csid_rdi_format_measure_cfg0_addr); + } + /* set frame drop pattern to 0 and period to 1 */ cam_io_w_mb(1, soc_info->reg_map[0].mem_base + csid_reg->rdi_reg[id]->csid_rdi_frm_drop_period_addr); @@ -2257,6 +2315,10 @@ static int cam_ife_csid_enable_rdi_path( if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOF_IRQ) val |= CSID_PATH_INFO_INPUT_EOF; + if (csid_hw->rdi_path_config[id].measure_enabled) + val |= (CSID_PATH_ERROR_PIX_COUNT | + CSID_PATH_ERROR_LINE_COUNT); + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + csid_reg->rdi_reg[id]->csid_rdi_irq_mask_addr); @@ -2710,6 +2772,13 @@ static int cam_ife_csid_release(void *hw_priv, case CAM_ISP_RESOURCE_PIX_PATH: res->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE; cam_ife_csid_reset_init_frame_drop(csid_hw); + if (res->res_id == CAM_IFE_PIX_PATH_RES_IPP) + csid_hw->ipp_path_config.measure_enabled = 0; + else if (res->res_id == CAM_IFE_PIX_PATH_RES_PPP) + csid_hw->ppp_path_config.measure_enabled = 0; + else + csid_hw->rdi_path_config[res->res_id].measure_enabled + = 0; break; default: CAM_ERR(CAM_ISP, "CSID:%d Invalid res type:%d res id%d", @@ -3141,6 +3210,57 @@ static int cam_ife_csid_set_csid_clock( return 0; } +static int cam_ife_csid_set_sensor_dimension( + struct cam_ife_csid_hw *csid_hw, void *cmd_args) +{ + struct cam_ife_sensor_dimension_update_args *dimension_update = NULL; + uint32_t i; + + if (!csid_hw) + return -EINVAL; + + dimension_update = + (struct cam_ife_sensor_dimension_update_args *)cmd_args; + csid_hw->ipp_path_config.measure_enabled = + dimension_update->ipp_path.measure_enabled; + if (dimension_update->ipp_path.measure_enabled) { + csid_hw->ipp_path_config.width = + dimension_update->ipp_path.width; + csid_hw->ipp_path_config.height = + dimension_update->ipp_path.height; + CAM_DBG(CAM_ISP, "CSID ipp path width %d height %d", + csid_hw->ipp_path_config.width, + csid_hw->ipp_path_config.height); + } + csid_hw->ppp_path_config.measure_enabled = + dimension_update->ppp_path.measure_enabled; + if (dimension_update->ppp_path.measure_enabled) { + csid_hw->ppp_path_config.width = + dimension_update->ppp_path.width; + csid_hw->ppp_path_config.height = + dimension_update->ppp_path.height; + CAM_DBG(CAM_ISP, "CSID ppp path width %d height %d", + csid_hw->ppp_path_config.width, + csid_hw->ppp_path_config.height); + } + for (i = 0; i <= CAM_IFE_PIX_PATH_RES_RDI_3; i++) { + csid_hw->rdi_path_config[i].measure_enabled + = dimension_update->rdi_path[i].measure_enabled; + if (csid_hw->rdi_path_config[i].measure_enabled) { + csid_hw->rdi_path_config[i].width = + dimension_update->rdi_path[i].width; + csid_hw->rdi_path_config[i].height = + dimension_update->rdi_path[i].height; + CAM_DBG(CAM_ISP, + "CSID rdi path[%d] width %d height %d", + i, csid_hw->rdi_path_config[i].width, + csid_hw->rdi_path_config[i].height); + } + } + + return 0; +} + static int cam_ife_csid_process_cmd(void *hw_priv, uint32_t cmd_type, void *cmd_args, uint32_t arg_size) { @@ -3178,6 +3298,9 @@ static int cam_ife_csid_process_cmd(void *hw_priv, case CAM_IFE_CSID_SET_INIT_FRAME_DROP: rc = cam_ife_csid_set_init_frame_drop(csid_hw, cmd_args); break; + case CAM_IFE_CSID_SET_SENSOR_DIMENSION_CFG: + rc = cam_ife_csid_set_sensor_dimension(csid_hw, cmd_args); + break; default: CAM_ERR(CAM_ISP, "CSID:%d unsupported cmd:%d", csid_hw->hw_intf->hw_idx, cmd_type); @@ -3200,7 +3323,7 @@ irqreturn_t cam_ife_csid_irq(int irq_num, void *data) const struct cam_ife_csid_rdi_reg_offset *rdi_reg; uint32_t i, irq_status_top, irq_status_rx, irq_status_ipp = 0; uint32_t irq_status_rdi[4] = {0, 0, 0, 0}; - uint32_t val, irq_status_ppp = 0; + uint32_t val, val2, irq_status_ppp = 0; bool fatal_err_detected = false; uint32_t sof_irq_debug_en = 0; unsigned long flags; @@ -3255,7 +3378,7 @@ irqreturn_t cam_ife_csid_irq(int irq_num, void *data) cam_io_w_mb(1, soc_info->reg_map[0].mem_base + csid_reg->cmn_reg->csid_irq_cmd_addr); - CAM_DBG(CAM_ISP, + CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID %d irq status 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", csid_hw->hw_intf->hw_idx, irq_status_top, irq_status_rx, irq_status_ipp, irq_status_ppp, @@ -3492,6 +3615,25 @@ handle_fatal_error: csid_reg->ipp_reg->csid_pxl_ctrl_addr); } } + + if ((irq_status_ipp & CSID_PATH_ERROR_PIX_COUNT) || + (irq_status_ipp & CSID_PATH_ERROR_LINE_COUNT)) { + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->ipp_reg->csid_pxl_format_measure0_addr); + + CAM_ERR(CAM_ISP, + "CSID:%d irq_status_ipp:0x%x", + csid_hw->hw_intf->hw_idx, irq_status_ipp); + CAM_ERR(CAM_ISP, + "Expected sz 0x%x*0x%x actual sz 0x%x*0x%x", + csid_hw->ipp_path_config.height, + csid_hw->ipp_path_config.width, + ((val >> + csid_reg->cmn_reg->format_measure_height_shift_val) & + csid_reg->cmn_reg->format_measure_height_mask_val), + val & + csid_reg->cmn_reg->format_measure_width_mask_val); + } } /*read PPP errors */ @@ -3573,6 +3715,25 @@ handle_fatal_error: csid_reg->ppp_reg->csid_pxl_ctrl_addr); } } + + if ((irq_status_ppp & CSID_PATH_ERROR_PIX_COUNT) || + (irq_status_ppp & CSID_PATH_ERROR_LINE_COUNT)) { + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->ppp_reg->csid_pxl_format_measure0_addr); + + CAM_ERR(CAM_ISP, + "CSID:%d irq_status_ppp:0x%x", + csid_hw->hw_intf->hw_idx, irq_status_ppp); + CAM_ERR(CAM_ISP, + "Expected sz 0x%x*0x%x actual sz 0x%x*0x%x", + csid_hw->ppp_path_config.height, + csid_hw->ppp_path_config.width, + ((val >> + csid_reg->cmn_reg->format_measure_height_shift_val) & + csid_reg->cmn_reg->format_measure_height_mask_val), + val & + csid_reg->cmn_reg->format_measure_width_mask_val); + } } for (i = 0; i < csid_reg->cmn_reg->num_rdis; i++) { @@ -3639,6 +3800,31 @@ handle_fatal_error: soc_info->reg_map[0].mem_base + csid_reg->rdi_reg[i]->csid_rdi_ctrl_addr); } + + if ((irq_status_rdi[i] & CSID_PATH_ERROR_PIX_COUNT) || + (irq_status_rdi[i] & CSID_PATH_ERROR_LINE_COUNT)) { + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[i]->csid_rdi_format_measure0_addr); + val2 = cam_io_r_mb(soc_info->reg_map[0].mem_base + + csid_reg->rdi_reg[i]->csid_rdi_format_measure_cfg1_addr + ); + CAM_ERR(CAM_ISP, + "CSID:%d irq_status_rdi[%d]:0x%x", + csid_hw->hw_intf->hw_idx, i, + irq_status_rdi[i]); + CAM_ERR(CAM_ISP, + "Expected sz 0x%x*0x%x actual sz 0x%x*0x%x", + ((val2 >> + csid_reg->cmn_reg->format_measure_height_shift_val) & + csid_reg->cmn_reg->format_measure_height_mask_val), + val2 & + csid_reg->cmn_reg->format_measure_width_mask_val, + ((val >> + csid_reg->cmn_reg->format_measure_height_shift_val) & + csid_reg->cmn_reg->format_measure_height_mask_val), + val & + csid_reg->cmn_reg->format_measure_width_mask_val); + } } if (csid_hw->irq_debug_cnt >= CAM_CSID_IRQ_SOF_DEBUG_CNT_MAX) { @@ -3782,6 +3968,10 @@ int cam_ife_csid_hw_probe_init(struct cam_hw_intf *csid_hw_intf, ife_csid_hw->csid_debug = 0; ife_csid_hw->error_irq_count = 0; ife_csid_hw->first_sof_ts = 0; + ife_csid_hw->ipp_path_config.measure_enabled = 0; + ife_csid_hw->ppp_path_config.measure_enabled = 0; + for (i = 0; i <= CAM_IFE_PIX_PATH_RES_RDI_3; i++) + ife_csid_hw->rdi_path_config[i].measure_enabled = 0; /* Check if ppi bridge is present or not? */ ife_csid_hw->ppi_enable = of_property_read_bool( diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h index 26b6a7871845..f2173f13f0c4 100644 --- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h +++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h @@ -310,6 +310,10 @@ struct cam_ife_csid_common_reg_offset { uint32_t ppp_irq_mask_all; uint32_t measure_en_hbi_vbi_cnt_mask; uint32_t format_measure_en_val; + uint32_t format_measure_width_shift_val; + uint32_t format_measure_width_mask_val; + uint32_t format_measure_height_shift_val; + uint32_t format_measure_height_mask_val; }; /** @@ -469,6 +473,11 @@ struct cam_ife_csid_path_cfg { * @csid_debug: csid debug information to enable the SOT, EOT, * SOF, EOF, measure etc in the csid hw * @clk_rate Clock rate + * @ipp_path ipp path configuration + * @ppp_path ppp path configuration + * @rdi_path RDI path configuration + * @hbi Horizontal blanking + * @vbi Vertical blanking * @sof_irq_triggered: Flag is set on receiving event to enable sof irq * incase of SOF freeze. * @irq_debug_cnt: Counter to track sof irq's when above flag is set. @@ -507,6 +516,11 @@ struct cam_ife_csid_hw { struct completion csid_rdin_complete[CAM_IFE_CSID_RDI_MAX]; uint64_t csid_debug; uint64_t clk_rate; + struct cam_isp_sensor_dimension ipp_path_config; + struct cam_isp_sensor_dimension ppp_path_config; + struct cam_isp_sensor_dimension rdi_path_config[4]; + uint32_t hbi; + uint32_t vbi; bool sof_irq_triggered; uint32_t irq_debug_cnt; uint32_t error_irq_count; diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h index 0c45bd1268b9..8d340207a0a1 100644 --- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h +++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h @@ -158,6 +158,7 @@ enum cam_ife_csid_cmd_type { CAM_IFE_CSID_SET_CSID_DEBUG, CAM_IFE_CSID_SOF_IRQ_DEBUG, CAM_IFE_CSID_SET_INIT_FRAME_DROP, + CAM_IFE_CSID_SET_SENSOR_DIMENSION_CFG, CAM_IFE_CSID_CMD_MAX, }; @@ -181,5 +182,17 @@ struct cam_ife_csid_clock_update_args { uint64_t clk_rate; }; +/* + * struct cam_ife_sensor_dim_update_args: + * + * @ppp_path: expected ppp path configuration + * @ipp_path: expected ipp path configuration + * @rdi_path: expected rdi path configuration + */ +struct cam_ife_sensor_dimension_update_args { + struct cam_isp_sensor_dimension ppp_path; + struct cam_isp_sensor_dimension ipp_path; + struct cam_isp_sensor_dimension rdi_path[4]; +}; #endif /* _CAM_CSID_HW_INTF_H_ */ diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h index b23014773022..d90030d9ed16 100644 --- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h +++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h @@ -20,17 +20,21 @@ #include "cam_irq_controller.h" #include <uapi/media/cam_isp.h> +#define CAM_ISP_FPS_60 60 + /* * struct cam_isp_timestamp: * * @mono_time: Monotonic boot time * @vt_time: AV Timer time * @ticks: Qtimer ticks + * @time_usecs: time in micro seconds */ struct cam_isp_timestamp { struct timeval mono_time; struct timeval vt_time; uint64_t ticks; + uint64_t time_usecs; }; /* @@ -105,6 +109,7 @@ enum cam_isp_hw_cmd_type { CAM_ISP_HW_CMD_FE_UPDATE_IN_RD, CAM_ISP_HW_CMD_FE_UPDATE_BUS_RD, CAM_ISP_HW_CMD_GET_IRQ_REGISTER_DUMP, + CAM_ISP_HW_CMD_FPS_CONFIG, CAM_ISP_HW_CMD_MAX, }; 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 9d6bcb71bb69..3bcedc948a18 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 @@ -178,6 +178,17 @@ struct cam_vfe_clock_update_args { }; /* + * struct cam_vfe_fps_config_args: + * + * @node_res: Resource to get the fps value + * @fps: FPS value to configure EPOCH + */ +struct cam_vfe_fps_config_args { + struct cam_isp_resource_node *node_res; + uint32_t fps; +}; + +/* * struct cam_vfe_bw_update_args: * * @node_res: Resource to get the BW 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 a26c11264d2c..162ddadd744f 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 @@ -444,6 +444,8 @@ void cam_isp_hw_get_timestamp(struct cam_isp_timestamp *time_stamp) get_monotonic_boottime(&ts); time_stamp->mono_time.tv_sec = ts.tv_sec; time_stamp->mono_time.tv_usec = ts.tv_nsec/1000; + time_stamp->time_usecs = ts.tv_sec * 1000000 + + time_stamp->mono_time.tv_usec; } static int cam_vfe_irq_top_half(uint32_t evt_id, @@ -761,6 +763,7 @@ int cam_vfe_process_cmd(void *hw_priv, uint32_t cmd_type, case CAM_ISP_HW_CMD_BW_UPDATE: case CAM_ISP_HW_CMD_BW_CONTROL: case CAM_ISP_HW_CMD_GET_IRQ_REGISTER_DUMP: + case CAM_ISP_HW_CMD_FPS_CONFIG: rc = core_info->vfe_top->hw_ops.process_cmd( core_info->vfe_top->top_priv, cmd_type, cmd_args, arg_size); 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 3ed45ee530e8..112a2d680c30 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 @@ -44,6 +44,7 @@ struct cam_vfe_mux_camif_data { bool enable_sof_irq_debug; uint32_t irq_debug_cnt; uint32_t camif_debug; + uint32_t fps; }; static int cam_vfe_camif_validate_pix_pattern(uint32_t pattern) @@ -269,9 +270,15 @@ static int cam_vfe_camif_resource_start( rsrc_data->camif_reg->epoch_irq); break; default: - epoch0_irq_mask = ((rsrc_data->last_line - + if (rsrc_data->fps == CAM_ISP_FPS_60) { + epoch0_irq_mask = ((rsrc_data->last_line - rsrc_data->first_line) / 2) + rsrc_data->first_line; + } else { + epoch0_irq_mask = (((rsrc_data->last_line - + rsrc_data->first_line) * 2) / 3) + + rsrc_data->first_line; + } epoch1_irq_mask = rsrc_data->reg_data->epoch_line_cfg & 0xFFFF; computed_epoch_line_cfg = (epoch0_irq_mask << 16) | @@ -515,6 +522,20 @@ static int cam_vfe_camif_sof_irq_debug( return 0; } +static int cam_vfe_camif_set_fps_config( + struct cam_isp_resource_node *rsrc_node, void *cmd_args) +{ + struct cam_vfe_mux_camif_data *camif_priv = NULL; + struct cam_vfe_fps_config_args *fps_args = cmd_args; + + camif_priv = + (struct cam_vfe_mux_camif_data *)rsrc_node->res_priv; + + camif_priv->fps = fps_args->fps; + + return 0; + +} static int cam_vfe_camif_process_cmd(struct cam_isp_resource_node *rsrc_node, uint32_t cmd_type, void *cmd_args, uint32_t arg_size) @@ -546,6 +567,9 @@ static int cam_vfe_camif_process_cmd(struct cam_isp_resource_node *rsrc_node, case CAM_ISP_HW_CMD_GET_IRQ_REGISTER_DUMP: rc = cam_vfe_camif_irq_reg_dump(rsrc_node); break; + case CAM_ISP_HW_CMD_FPS_CONFIG: + rc = cam_vfe_camif_set_fps_config(rsrc_node, cmd_args); + break; default: CAM_ERR(CAM_ISP, "unsupported process command:%d", cmd_type); 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 d512128b28e5..0461b0820b4f 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 @@ -283,6 +283,22 @@ static int cam_vfe_top_fs_update( return 0; } +static int cam_vfe_top_fps_config( + struct cam_vfe_top_ver2_priv *top_priv, + void *cmd_args, uint32_t arg_size) +{ + struct cam_vfe_fps_config_args *cmd_update = NULL; + + cmd_update = + (struct cam_vfe_fps_config_args *)cmd_args; + + if (cmd_update->node_res->process_cmd) + return cmd_update->node_res->process_cmd(cmd_update->node_res, + CAM_ISP_HW_CMD_FPS_CONFIG, cmd_args, arg_size); + + return 0; +} + static int cam_vfe_top_clock_update( struct cam_vfe_top_ver2_priv *top_priv, void *cmd_args, uint32_t arg_size) @@ -740,6 +756,10 @@ int cam_vfe_top_process_cmd(void *device_priv, uint32_t cmd_type, rc = cam_vfe_get_irq_register_dump(top_priv, cmd_args, arg_size); break; + case CAM_ISP_HW_CMD_FPS_CONFIG: + rc = cam_vfe_top_fps_config(top_priv, cmd_args, + arg_size); + break; default: rc = -EINVAL; CAM_ERR(CAM_ISP, "Error! Invalid cmd:%d", cmd_type); 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 c74be4cf4e45..b0901f83fa97 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 @@ -1015,7 +1015,7 @@ int32_t cam_eeprom_driver_cmd(struct cam_eeprom_ctrl_t *e_ctrl, void *arg) &eeprom_cap, sizeof(struct cam_eeprom_query_cap_t))) { CAM_ERR(CAM_EEPROM, "Failed Copy to User"); - return -EFAULT; + rc = -EFAULT; goto release_mutex; } CAM_DBG(CAM_EEPROM, "eeprom_cap: ID: %d", eeprom_cap.slot_info); diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c index 0ae7d0ac4186..88c5c88030dc 100644 --- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c +++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c @@ -3663,6 +3663,8 @@ STREAM_BUFF_END: } else { ioctl_cmd = VIDIOC_MSM_BUF_MNGR_IOCTL_CMD; idx = MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX; + buff_mgr_info.index = + frame_info.output_buffer_info[0].index; } rc = msm_cpp_buffer_ops(cpp_dev, ioctl_cmd, idx, &buff_mgr_info); @@ -4376,6 +4378,8 @@ static long msm_cpp_subdev_fops_compat_ioctl(struct file *file, memset(&k64_frame_info, 0, sizeof(k64_frame_info)); k64_frame_info.identity = k32_frame_info.identity; k64_frame_info.frame_id = k32_frame_info.frame_id; + k64_frame_info.output_buffer_info[0].index = + k32_frame_info.output_buffer_info[0].index; kp_ioctl.ioctl_ptr = (__force void __user *)&k64_frame_info; diff --git a/drivers/net/ethernet/aquantia/Kconfig b/drivers/net/ethernet/aquantia/Kconfig index 1b35d4e5a4a2..a5311a0f2d03 100644 --- a/drivers/net/ethernet/aquantia/Kconfig +++ b/drivers/net/ethernet/aquantia/Kconfig @@ -29,20 +29,4 @@ config AQFWD source "drivers/net/ethernet/aquantia/atlantic-fwd/Kconfig" -config AQFWD_QCOM - bool "QTI MSM/MDM target support" - depends on AQFWD - depends on ARCH_QCOM - help - Enable support for integration with Qualcomm Technologies, Inc. chipsets. - -config AQFWD_QCOM_IPA - bool "QTI IPA offload support" - depends on IPA_ETH - select AQFWD_QCOM - select ATLFWD_FWD - help - Enable support for Qualcomm Technologies, Inc. IPA (Internet Protocol Accelerator). - If unsure, say N. - endif # NET_VENDOR_AQUANTIA diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/Kconfig b/drivers/net/ethernet/aquantia/atlantic-fwd/Kconfig index 4a60625404de..1e47b65b8d6b 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/Kconfig +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/Kconfig @@ -42,3 +42,24 @@ config ATLFWD_FWD_TXBUF API enabled, 0 otherwise. endif + +config AQFWD_QCOM + bool "QTI MSM/MDM target support" + depends on AQFWD + depends on ARCH_QCOM + help + Some older targets using Qualcomm Technologies, Inc. chipsets + require peripheral drivers to explicitly set IOMMU attributes + and perform IOMMU attach. Enable this option if your platform + is affected. Using this feature will also require the user to + provide SMMU configuration via PCI device tree. + If unsure, say N. + +config AQFWD_QCOM_IPA + bool "QTI IPA offload support" + depends on IPA_ETH + select AQFWD_QCOM + select ATLFWD_FWD + help + Enable support for Qualcomm Technologies, Inc. IPA (Internet Protocol Accelerator). + If unsure, say N. diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/Makefile b/drivers/net/ethernet/aquantia/atlantic-fwd/Makefile index 52d9467d566e..f098674f7557 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/Makefile +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/Makefile @@ -40,8 +40,6 @@ atlantic-fwd-objs := atl_fw.o \ atlantic-fwd-$(CONFIG_ATLFWD_FWD) += atl_fwd.o -atlantic-fwd-$(CONFIG_OF) += atl_of.o - atlantic-fwd-$(CONFIG_AQFWD_QCOM) += atl_qcom.o atlantic-fwd-$(CONFIG_AQFWD_QCOM_IPA) += atl_qcom_ipa.o diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h index a3dfebf9c00c..1324486dba9c 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h @@ -18,7 +18,7 @@ #include <linux/netdevice.h> #include <linux/moduleparam.h> -#define ATL_VERSION "1.0.19" +#define ATL_VERSION "1.0.20" struct atl_nic; @@ -220,9 +220,8 @@ struct atl_nic { int nvecs; struct atl_hw hw; unsigned flags; - unsigned long state; uint32_t priv_flags; - struct timer_list link_timer; + struct timer_list work_timer; int max_mtu; int requested_nvecs; int requested_rx_size; @@ -249,13 +248,6 @@ enum atl_nic_flags { ATL_FL_WOL = BIT(1), }; -enum atl_nic_state { - ATL_ST_UP, - ATL_ST_CONFIGURED, - ATL_ST_ENABLED, - ATL_ST_WORK_SCHED, -}; - #define ATL_PF(_name) ATL_PF_ ## _name #define ATL_PF_BIT(_name) ATL_PF_ ## _name ## _BIT #define ATL_DEF_PF_BIT(_name) ATL_PF_BIT(_name) = BIT(ATL_PF(_name)) @@ -335,18 +327,37 @@ extern int atl_enable_msi; #define atl_nic_err(fmt, args...) \ dev_err(&nic->hw.pdev->dev, fmt, ## args) +#define atl_dev_init_warn(fmt, args...) \ +do { \ + if (hw) \ + atl_dev_warn(fmt, ## args); \ + else \ + printk(KERN_WARNING "%s: " fmt, atl_driver_name, ##args); \ +} while(0) + +#define atl_dev_init_err(fmt, args...) \ +do { \ + if (hw) \ + atl_dev_warn(fmt, ## args); \ + else \ + printk(KERN_ERR "%s: " fmt, atl_driver_name, ##args); \ +} while(0) + #define atl_module_param(_name, _type, _mode) \ module_param_named(_name, atl_ ## _name, _type, _mode) static inline void atl_intr_enable_non_ring(struct atl_nic *nic) { struct atl_hw *hw = &nic->hw; - uint32_t mask = hw->intr_mask; -#ifdef CONFIG_ATLFWD_FWD - mask |= (uint32_t)(nic->fwd.msi_map); -#endif - atl_intr_enable(hw, mask); + atl_intr_enable(hw, hw->non_ring_intr_mask); +} + +static inline void atl_intr_disable_non_ring(struct atl_nic *nic) +{ + struct atl_hw *hw = &nic->hw; + + atl_intr_disable(hw, hw->non_ring_intr_mask); } netdev_tx_t atl_start_xmit(struct sk_buff *skb, struct net_device *ndev); @@ -360,6 +371,7 @@ int atl_setup_datapath(struct atl_nic *nic); void atl_clear_datapath(struct atl_nic *nic); int atl_start_rings(struct atl_nic *nic); void atl_stop_rings(struct atl_nic *nic); +void atl_clear_rdm_cache(struct atl_nic *nic); int atl_alloc_rings(struct atl_nic *nic); void atl_free_rings(struct atl_nic *nic); irqreturn_t atl_ring_irq(int irq, void *priv); @@ -403,5 +415,8 @@ int atl_mdio_write(struct atl_hw *hw, uint8_t prtad, uint8_t mmd, void atl_refresh_rxfs(struct atl_nic *nic); void atl_schedule_work(struct atl_nic *nic); int atl_hwmon_init(struct atl_nic *nic); +int atl_update_thermal(struct atl_hw *hw); +int atl_update_thermal_flag(struct atl_hw *hw, int bit, bool val); +int atl_verify_thermal_limits(struct atl_hw *hw, struct atl_thermal *thermal); #endif diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.c index d481355291b3..c445f1b9b18a 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.c @@ -36,17 +36,8 @@ struct atl_link_type atl_link_types[] = { const int atl_num_rates = ARRAY_SIZE(atl_link_types); -static inline void atl_lock_fw(struct atl_hw *hw) -{ - mutex_lock(&hw->mcp.lock); -} - -static inline void atl_unlock_fw(struct atl_hw *hw) -{ - mutex_unlock(&hw->mcp.lock); -} - -static int atl_fw1_wait_fw_init(struct atl_hw *hw) +/* fw lock must be held */ +static int __atl_fw1_wait_fw_init(struct atl_hw *hw) { uint32_t hostData_addr; uint32_t id, new_id; @@ -79,7 +70,8 @@ static int atl_fw1_wait_fw_init(struct atl_hw *hw) return 0; } -static int atl_fw2_wait_fw_init(struct atl_hw *hw) +/* fw lock must be held */ +static int __atl_fw2_wait_fw_init(struct atl_hw *hw) { uint32_t reg; @@ -148,6 +140,8 @@ static struct atl_link_type *atl_fw1_check_link(struct atl_hw *hw) return link; } +static void __atl_fw2_thermal_check(struct atl_hw *hw, uint32_t sts); + static struct atl_link_type *atl_fw2_check_link(struct atl_hw *hw) { struct atl_link_type *link; @@ -162,58 +156,91 @@ static struct atl_link_type *atl_fw2_check_link(struct atl_hw *hw) high = atl_read(hw, ATL_MCP_SCRATCH(FW2_LINK_RES_HIGH)); link = atl_parse_fw_bits(hw, low, high, 1); - if (!link) - goto unlock; - if (high & atl_fw2_pause) - fc |= atl_fc_rx; - if (high & atl_fw2_asym_pause) - fc |= atl_fc_tx; + __atl_fw2_thermal_check(hw, low); + + /* Thermal check might have reset link due to throttling */ + link = lstate->link; + + if (link) { + if (high & atl_fw2_pause) + fc |= atl_fc_rx; + if (high & atl_fw2_asym_pause) + fc |= atl_fc_tx; + } lstate->fc.cur = fc; -unlock: atl_unlock_fw(hw); return link; } -static int atl_fw1_get_link_caps(struct atl_hw *hw) +/* fw lock must be held */ +static int __atl_fw1_get_link_caps(struct atl_hw *hw) { return 0; } -static int atl_fw2_get_link_caps(struct atl_hw *hw) +/* fw lock must be held */ +static int __atl_fw2_get_link_caps(struct atl_hw *hw) { - uint32_t fw_stat_addr = hw->mcp.fw_stat_addr; + struct atl_mcp *mcp = &hw->mcp; + uint32_t fw_stat_addr = mcp->fw_stat_addr; + struct atl_link_type *rate; unsigned int supported = 0; - uint32_t caps[2]; + uint32_t caps[2], mask = atl_fw2_pause_mask | atl_fw2_link_drop; int i, ret; - atl_lock_fw(hw); - atl_dev_dbg("Host data struct addr: %#x\n", fw_stat_addr); ret = atl_read_mcp_mem(hw, fw_stat_addr + atl_fw2_stat_lcaps, caps, 8); if (ret) - goto unlock; + return ret; + + mcp->caps_low = caps[0]; + mcp->caps_high = caps[1]; + atl_dev_dbg("Got link caps: %#x %#x\n", caps[0], caps[1]); + + atl_for_each_rate(i, rate) { + uint32_t bit = rate->fw_bits[1]; - for (i = 0; i < atl_num_rates; i++) - if (atl_link_types[i].fw_bits[1] & caps[0]) { + if (bit & caps[0]) { supported |= BIT(i); - if (atl_link_types[i].fw_bits[1] & caps[1]) + if (bit & caps[1]) { supported |= BIT(i + ATL_EEE_BIT_OFFT); + mask |= bit; + } } + } + mcp->req_high_mask = ~mask; hw->link_state.supported = supported; + hw->link_state.lp_lowest = fls(supported) - 1; -unlock: - atl_unlock_fw(hw); return ret; } static inline unsigned int atl_link_adv(struct atl_link_state *lstate) { - return lstate->force_off ? 0 : lstate->advertized; + struct atl_hw *hw = container_of(lstate, struct atl_hw, link_state); + + if (lstate->force_off) + return 0; + + if (lstate->thermal_throttled + && hw->thermal.flags & atl_thermal_throttle) + /* FW doesn't provide raw LP's advertized rates, only + * the rates adverized both by us and LP. Here we + * advertize not just the throttled_to rate, but also + * all the lower rates as well. That way if LP changes + * or dynamically starts to adverize a lower rate than + * throttled_to, we will notice that in + * atl_fw2_thermal_check() and switch to that lower + * rate there. + */ + return BIT(lstate->throttled_to + 1) - 1; + + return lstate->advertized; } static inline bool atl_fw1_set_link_needed(struct atl_link_state *lstate) @@ -276,16 +303,14 @@ static void atl_fw1_set_link(struct atl_hw *hw, bool force) atl_unlock_fw(hw); } -static void atl_fw2_set_link(struct atl_hw *hw, bool force) +/* fw lock must be held */ +static void __atl_fw2_set_link(struct atl_hw *hw) { struct atl_link_state *lstate = &hw->link_state; - uint32_t hi_bits = 0; + uint32_t hi_bits; uint64_t bits; - if (!force && !atl_fw2_set_link_needed(lstate)) - return; - - atl_lock_fw(hw); + hi_bits = hw->mcp.req_high & hw->mcp.req_high_mask; if (lstate->fc.req & atl_fc_rx) hi_bits |= atl_fw2_pause | atl_fw2_asym_pause; @@ -294,15 +319,26 @@ static void atl_fw2_set_link(struct atl_hw *hw, bool force) hi_bits ^= atl_fw2_asym_pause; bits = atl_set_fw_bits(hw, 1); - hi_bits |= bits >> 32; /* If no modes are advertized, put PHY into low-power */ if (!bits) - hi_bits = atl_fw2_link_drop; + hi_bits |= atl_fw2_link_drop; + else + hi_bits |= bits >> 32; + + hw->mcp.req_high = hi_bits; atl_write(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_LOW), bits); atl_write(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_HIGH), hi_bits); +} + +static void atl_fw2_set_link(struct atl_hw *hw, bool force) +{ + if (!force && !atl_fw2_set_link_needed(&hw->link_state)) + return; + atl_lock_fw(hw); + __atl_fw2_set_link(hw); atl_unlock_fw(hw); } @@ -314,6 +350,8 @@ static int atl_fw1_unsupported(struct atl_hw *hw) static int atl_fw2_restart_aneg(struct atl_hw *hw) { atl_lock_fw(hw); + /* Autoneg restart is self-clearing, no need to track via + * mcp->req_high */ atl_set_bits(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_HIGH), BIT(31)); atl_unlock_fw(hw); return 0; @@ -398,15 +436,17 @@ int atl_read_mcp_word(struct atl_hw *hw, uint32_t offt, uint32_t *val) return 0; } -static int atl_fw2_get_phy_temperature(struct atl_hw *hw, int *temp) +/* fw lock must be held */ +static int __atl_fw2_get_phy_temperature(struct atl_hw *hw, int *temp) { uint32_t req, res; int ret = 0; - atl_lock_fw(hw); + if (test_bit(ATL_ST_RESETTING, &hw->state)) + return 0; - req = atl_read(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_HIGH)); - req ^= atl_fw2_phy_temp; + hw->mcp.req_high ^= atl_fw2_phy_temp; + req = hw->mcp.req_high; atl_write(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_HIGH), req); busy_wait(1000, udelay(10), res, @@ -414,27 +454,95 @@ static int atl_fw2_get_phy_temperature(struct atl_hw *hw, int *temp) ((res ^ req) & atl_fw2_phy_temp) != 0); if (((res ^ req) & atl_fw2_phy_temp) != 0) { atl_dev_err("Timeout waiting for PHY temperature\n"); - ret = -EIO; - goto unlock; + return -EIO; } ret = atl_read_fwstat_word(hw, atl_fw2_stat_temp, &res); if (ret) - goto unlock; + return ret; *temp = (res & 0xffff) * 1000 / 256; -unlock: + return ret; +} + +static int atl_fw2_get_phy_temperature(struct atl_hw *hw, int *temp) +{ + int ret; + + atl_lock_fw(hw); + ret = __atl_fw2_get_phy_temperature(hw, temp); atl_unlock_fw(hw); return ret; } +/* fw lock must be held */ +static void __atl_fw2_thermal_check(struct atl_hw *hw, uint32_t sts) +{ + bool alarm; + int temp, ret; + struct atl_link_state *lstate = &hw->link_state; + struct atl_link_type *link = lstate->link; + int lowest; + + alarm = !!(sts & atl_fw2_thermal_alarm); + + if (link) { + /* ffs() / fls() number bits starting at 1 */ + lowest = ffs(lstate->lp_advertized) - 1; + if (lowest < lstate->lp_lowest) { + lstate->lp_lowest = lowest; + if (lowest < lstate->throttled_to && + lstate->thermal_throttled && alarm) + /* We're still thermal-throttled, and + * just found out we can lower the + * speed even more, so renegotiate. */ + goto relink; + } + } else + lstate->lp_lowest = fls(lstate->supported) - 1; + + if (alarm == lstate->thermal_throttled) + return; + + lstate->thermal_throttled = alarm; + + ret = __atl_fw2_get_phy_temperature(hw, &temp); + if (ret) + temp = 0; + else + /* Temperature is in millidegrees C */ + temp = (temp + 50) / 100; + + if (alarm) { + if (temp) + atl_dev_warn("PHY temperature above threshold: %d.%d\n", + temp / 10, temp % 10); + else + atl_dev_warn("PHY temperature above threshold\n"); + } else { + if (temp) + atl_dev_warn("PHY temperature back in range: %d.%d\n", + temp / 10, temp % 10); + else + atl_dev_warn("PHY temperature back in range\n"); + } + +relink: + if (hw->thermal.flags & atl_thermal_throttle) { + /* If throttling is enabled, renegotiate link */ + lstate->link = 0; + lstate->throttled_to = lstate->lp_lowest; + __atl_fw2_set_link(hw); + } +} + static struct atl_fw_ops atl_fw_ops[2] = { [0] = { - .wait_fw_init = atl_fw1_wait_fw_init, + .__wait_fw_init = __atl_fw1_wait_fw_init, .set_link = atl_fw1_set_link, .check_link = atl_fw1_check_link, - .get_link_caps = atl_fw1_get_link_caps, + .__get_link_caps = __atl_fw1_get_link_caps, .restart_aneg = atl_fw1_unsupported, .set_default_link = atl_fw1_set_default_link, .enable_wol = atl_fw1_unsupported, @@ -442,10 +550,10 @@ static struct atl_fw_ops atl_fw_ops[2] = { .efuse_shadow_addr_reg = ATL_MCP_SCRATCH(FW1_EFUSE_SHADOW), }, [1] = { - .wait_fw_init = atl_fw2_wait_fw_init, + .__wait_fw_init = __atl_fw2_wait_fw_init, .set_link = atl_fw2_set_link, .check_link = atl_fw2_check_link, - .get_link_caps = atl_fw2_get_link_caps, + .__get_link_caps = __atl_fw2_get_link_caps, .restart_aneg = atl_fw2_restart_aneg, .set_default_link = atl_fw2_set_default_link, .enable_wol = atl_fw2_enable_wol, @@ -454,10 +562,225 @@ static struct atl_fw_ops atl_fw_ops[2] = { }, }; +/* fw lock must be held */ +static int __atl_fw2_set_thermal_monitor(struct atl_hw *hw, bool enable) +{ + struct atl_mcp *mcp = &hw->mcp; + int ret; + uint32_t high; + + if (enable) { + struct atl_fw2_thermal_cfg cfg __attribute__((__aligned__(4))); + + cfg.msg_id = 0x17; + cfg.shutdown_temp = hw->thermal.crit; + cfg.high_temp = hw->thermal.high; + cfg.normal_temp = hw->thermal.low; + + ret = atl_write_mcp_mem(hw, 0, &cfg, (sizeof(cfg) + 3) & ~3, + MCP_AREA_CONFIG); + if (ret) { + atl_dev_err("Failed to upload thermal thresholds to firmware: %d\n", + ret); + return ret; + } + + mcp->req_high |= atl_fw2_set_thermal; + } else + mcp->req_high &= ~atl_fw2_set_thermal; + + atl_write(hw, ATL_MCP_SCRATCH(FW2_LINK_REQ_HIGH), mcp->req_high); + busy_wait(1000, udelay(10), high, + atl_read(hw, ATL_MCP_SCRATCH(FW2_LINK_RES_HIGH)), + !!(high & atl_fw2_set_thermal) != enable); + if (!!(high & atl_fw2_set_thermal) != enable) { + atl_dev_err("Timeout waiting for thermal monitoring FW request\n"); + return -EIO; + } + + return 0; +} + +/* fw lock must be held */ +static int __atl_fw2_update_thermal(struct atl_hw *hw) +{ + struct atl_mcp *mcp = &hw->mcp; + int ret = 0; + bool enable = !!(hw->thermal.flags & atl_thermal_monitor); + + if (!enable || (mcp->req_high & atl_fw2_set_thermal)) { + /* If monitoring is on and we need to change the + * thresholds, we need to temporarily disable thermal + * monitoring first. */ + ret = __atl_fw2_set_thermal_monitor(hw, false); + if (ret) + return ret; + } + + if (enable) + ret = __atl_fw2_set_thermal_monitor(hw, true); + + /* Thresholds might have changed, recheck state. */ + __atl_fw2_thermal_check(hw, + atl_read(hw, ATL_MCP_SCRATCH(FW2_LINK_RES_LOW))); + return ret; +} + +struct atl_thermal_limit { + uintptr_t offset; + const char *name; + unsigned min; + unsigned max; +}; +#define atl_def_thermal_limit(_name, _field, _min, _max) \ +{ \ + .offset = offsetof(struct atl_thermal, _field), \ + .name = _name, \ + .min = _min, \ + .max = _max, \ +}, + +static struct atl_thermal_limit atl_thermal_limits[] = { + atl_def_thermal_limit("Shutdown", crit, 108, 118) + atl_def_thermal_limit("High", high, 90, 107) + atl_def_thermal_limit("Normal", low, 50, 85) +}; + +int atl_verify_thermal_limits(struct atl_hw *hw, struct atl_thermal *thermal) +{ + int i; + bool ignore = !!(thermal->flags & atl_thermal_ignore_lims); + + for (i = 0; i < ARRAY_SIZE(atl_thermal_limits); i++) { + struct atl_thermal_limit *lim = &atl_thermal_limits[i]; + unsigned val = *((uint8_t *)thermal + lim->offset); + + if (val >= lim->min && val <= lim->max) + continue; + + if (ignore) { + atl_dev_init_warn("%s temperature threshold out of range (%d - %d): %d, allowing anyway\n", + lim->name, lim->min, lim->max, val); + continue; + } else { + atl_dev_init_err("%s temperature threshold out of range (%d - %d): %d\n", + lim->name, lim->min, lim->max, val); + return -EINVAL; + } + } + + return 0; +} + +int atl_update_thermal(struct atl_hw *hw) +{ + int ret; + + ret = atl_verify_thermal_limits(hw, &hw->thermal); + if (ret) + return ret; + + if (test_bit(ATL_ST_RESETTING, &hw->state)) + /* After reset, atl_fw_init() will apply the settings + * skipped here */ + return 0; + + atl_lock_fw(hw); + ret = __atl_fw2_update_thermal(hw); + atl_unlock_fw(hw); + + return ret; +} + +int atl_update_thermal_flag(struct atl_hw *hw, int bit, bool val) +{ + struct atl_thermal *thermal = &hw->thermal; + unsigned flags, changed; + int ret = 0; + + atl_lock_fw(hw); + flags = thermal->flags; + + switch (bit) { + case atl_thermal_monitor_shift: + if (!val) + /* Disable throttling along with monitoring */ + flags &= ~atl_thermal_throttle; + else + if (!(hw->mcp.caps_high & atl_fw2_set_thermal)) { + atl_dev_err("Thermal monitoring not supported by firmware\n"); + ret = -EINVAL; + } + break; + + case atl_thermal_throttle_shift: + if (val && !(flags & atl_thermal_monitor)) { + atl_dev_err("Thermal monitoring needs to be enabled before enabling throttling\n"); + ret = -EINVAL; + } + break; + + case atl_thermal_ignore_lims_shift: + break; + + default: + ret = -EINVAL; + break; + } + if (ret) + goto unlock; + + flags &= ~BIT(bit); + flags |= val << bit; + + changed = flags ^ thermal->flags; + thermal->flags = flags; + + if (test_bit(ATL_ST_RESETTING, &hw->state)) + /* After reset, atl_fw_init() will apply the settings + * skipped here */ + goto unlock; + + if (changed & atl_thermal_monitor) + ret = __atl_fw2_update_thermal(hw); + else if (changed & atl_thermal_throttle && + hw->link_state.thermal_throttled) + __atl_fw2_set_link(hw); + + if (ret) + /* __atl_fw2_update_thermal() failed. Revert flag + * changes */ + thermal->flags ^= changed; + +unlock: + atl_unlock_fw(hw); + return ret; +} + +/* fw lock must be held */ +static int __atl_fw2_get_hbeat(struct atl_hw *hw, uint16_t *hbeat) +{ + int ret; + uint32_t val; + + ret = atl_read_fwstat_word(hw, atl_fw2_stat_phy_hbeat, &val); + if (ret) + atl_dev_err("FW watchdog: failure reading PHY heartbeat: %d\n", + -ret); + else + *hbeat = val & 0xffff; + + return ret; +} + +static unsigned int atl_wdog_period = 1100; +module_param_named(wdog_period, atl_wdog_period, uint, 0644); + int atl_fw_init(struct atl_hw *hw) { uint32_t tries, reg, major; int ret; + struct atl_mcp *mcp = &hw->mcp; tries = busy_wait(10000, mdelay(1), reg, atl_read(hw, 0x18), !reg); if (!reg) { @@ -473,22 +796,87 @@ int atl_fw_init(struct atl_hw *hw) } if (major > 2) major--; - hw->mcp.ops = &atl_fw_ops[major - 1]; - hw->mcp.poll_link = major == 1; - hw->mcp.fw_rev = reg; - hw->mcp.fw_stat_addr = atl_read(hw, ATL_MCP_SCRATCH(FW_STAT_STRUCT)); + mcp->ops = &atl_fw_ops[major - 1]; + mcp->fw_rev = reg; + + ret = mcp->ops->__wait_fw_init(hw); + if (ret) + return ret; + + mcp->fw_stat_addr = atl_read(hw, ATL_MCP_SCRATCH(FW_STAT_STRUCT)); + + ret = __atl_fw2_get_hbeat(hw, &mcp->phy_hbeat); + if (ret) + return ret; + mcp->next_wdog = jiffies + 2 * HZ; if (major > 1) { + mcp->req_high = 0; + ret = atl_read_fwstat_word(hw, atl_fw2_stat_settings_addr, - &hw->mcp.fw_settings_addr); + &mcp->fw_settings_addr); if (ret) return ret; ret = atl_read_fwstat_word(hw, atl_fw2_stat_settings_len, - &hw->mcp.fw_settings_len); + &mcp->fw_settings_len); if (ret) return ret; + + } + + ret = mcp->ops->__get_link_caps(hw); + if (ret) + return ret; + + if (!(mcp->caps_high & atl_fw2_set_thermal)) { + if (hw->thermal.flags & atl_thermal_monitor) + atl_dev_warn("Thermal monitoring not supported by firmware\n"); + hw->thermal.flags &= + ~(atl_thermal_monitor | atl_thermal_throttle); + } else + ret = __atl_fw2_update_thermal(hw); + + + return ret; +} + +void atl_fw_watchdog(struct atl_hw *hw) +{ + struct atl_mcp *mcp = &hw->mcp; + int ret; + uint16_t hbeat; + + if (mcp->wdog_disabled || !time_after(jiffies, mcp->next_wdog)) + return; + + if (test_bit(ATL_ST_RESETTING, &hw->state) || + !test_bit(ATL_ST_ENABLED, &hw->state)) + return; + + atl_lock_fw(hw); + + ret = __atl_fw2_get_hbeat(hw, &hbeat); + if (ret) { + atl_dev_err("FW watchdog: failure reading PHY heartbeat: %d\n", + -ret); + goto out; } - return hw->mcp.ops->wait_fw_init(hw); + if (hbeat == 0 && mcp->phy_hbeat == 0) { + atl_dev_warn("FW heartbeat stuck at 0, probably not provisioned. Disabling watchdog.\n"); + mcp->wdog_disabled = true; + goto out; + } + + if (hbeat == mcp->phy_hbeat) { + atl_dev_err("FW watchdog: FW hang (PHY heartbeat stuck at %hd), resetting\n", hbeat); + set_bit(ATL_ST_RESET_NEEDED, &hw->state); + } + + mcp->phy_hbeat = hbeat; + +out: + mcp->next_wdog = jiffies + atl_wdog_period * HZ / 1000; + atl_unlock_fw(hw); } diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.h index dc0aff8ca413..da00018b2190 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.h +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.h @@ -12,6 +12,22 @@ struct atl_hw; +struct atl_mcp { + uint32_t fw_rev; + struct atl_fw_ops *ops; + uint32_t fw_stat_addr; + uint32_t fw_settings_addr; + uint32_t fw_settings_len; + uint32_t req_high; + uint32_t req_high_mask; /* Clears link rate-dependend bits */ + uint32_t caps_low; + uint32_t caps_high; + struct mutex lock; + unsigned long next_wdog; + bool wdog_disabled; + uint16_t phy_hbeat; +}; + struct atl_link_type { unsigned speed; unsigned ethtool_idx; @@ -22,6 +38,13 @@ struct atl_link_type { extern struct atl_link_type atl_link_types[]; extern const int atl_num_rates; +struct atl_fw2_thermal_cfg { + uint32_t msg_id; + uint8_t shutdown_temp; + uint8_t high_temp; + uint8_t normal_temp; +}; + #define atl_for_each_rate(idx, type) \ for (idx = 0, type = atl_link_types; \ idx < atl_num_rates; \ @@ -37,12 +60,15 @@ enum atl_fw2_opts { atl_fw2_pause_mask = atl_fw2_pause | atl_fw2_asym_pause, atl_define_bit(atl_fw2_wake_on_link, 16) atl_define_bit(atl_fw2_phy_temp, 18) + atl_define_bit(atl_fw2_set_thermal, 21) atl_define_bit(atl_fw2_link_drop, 22) atl_define_bit(atl_fw2_nic_proxy, 0x17) atl_define_bit(atl_fw2_wol, 0x18) + atl_define_bit(atl_fw2_thermal_alarm, 29) }; enum atl_fw2_stat_offt { + atl_fw2_stat_phy_hbeat = 0x4c, atl_fw2_stat_temp = 0x50, atl_fw2_stat_lcaps = 0x84, atl_fw2_stat_settings_addr = 0x110, @@ -64,6 +90,12 @@ enum atl_fc_mode { atl_fc_full = atl_fc_rx | atl_fc_tx, }; +enum atl_thermal_flags { + atl_define_bit(atl_thermal_monitor, 0) + atl_define_bit(atl_thermal_throttle, 1) + atl_define_bit(atl_thermal_ignore_lims, 2) +}; + struct atl_fc_state { enum atl_fc_mode req; enum atl_fc_mode prev_req; @@ -81,7 +113,11 @@ struct atl_link_state{ unsigned advertized; unsigned lp_advertized; unsigned prev_advertized; + int lp_lowest; /* Idx of lowest rate advertized by + * link partner in atl_link_types[] */ + int throttled_to; /* Idx of the rate we're throttled to */ bool force_off; + bool thermal_throttled; bool autoneg; bool eee; bool eee_enabled; @@ -92,8 +128,8 @@ struct atl_link_state{ struct atl_fw_ops { void (*set_link)(struct atl_hw *hw, bool force); struct atl_link_type *(*check_link)(struct atl_hw *hw); - int (*wait_fw_init)(struct atl_hw *hw); - int (*get_link_caps)(struct atl_hw *hw); + int (*__wait_fw_init)(struct atl_hw *hw); + int (*__get_link_caps)(struct atl_hw *hw); int (*restart_aneg)(struct atl_hw *hw); void (*set_default_link)(struct atl_hw *hw); int (*enable_wol)(struct atl_hw *hw); diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.c index bf04d22de15f..31c4d687bc57 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.c @@ -415,6 +415,8 @@ struct atl_fwd_ring *atl_fwd_request_ring(struct net_device *ndev, goto free_ring; } + memset(hwring->descs, 0, hwring->size * sizeof(*hwring->descs)); + hwring->reg_base = dir_tx ? ATL_TX_RING(idx) : ATL_RX_RING(idx); ret = atl_fwd_alloc_bufs(ring, page_order); diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.c index 5324fdb785ba..641e719ae6fd 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.c @@ -100,10 +100,12 @@ static inline void atl_glb_soft_reset_full(struct atl_hw *hw) atl_glb_soft_reset(hw); } +/* entered with fw lock held */ static int atl_hw_reset_nonrbl(struct atl_hw *hw) { uint32_t tries; uint32_t reg = atl_read(hw, ATL_GLOBAL_DAISY_CHAIN_STS1); + int ret; bool daisychain_running = (reg & 0x30) != 0x30; @@ -131,7 +133,8 @@ static int atl_hw_reset_nonrbl(struct atl_hw *hw) !(reg & 0x10)); if (!(reg & 0x10)) { atl_dev_err("FLB kickstart timed out: %#x\n", reg); - return -EIO; + ret = -EIO; + goto unlock; } atl_dev_dbg("FLB kickstart took %d ms\n", tries); @@ -141,15 +144,33 @@ static int atl_hw_reset_nonrbl(struct atl_hw *hw) atl_glb_soft_reset_full(hw); - return atl_fw_init(hw); + ret = atl_fw_init(hw); + +unlock: + atl_unlock_fw(hw); + + if (ret) + set_bit(ATL_ST_RESET_NEEDED, &hw->state); + else + set_bit(ATL_ST_GLOBAL_CONF_NEEDED, &hw->state); + + return ret; } +/* Must be called either during early init when netdev isn't yet + * registered, or with RTNL lock held */ int atl_hw_reset(struct atl_hw *hw) { - uint32_t reg = atl_read(hw, ATL_MCP_SCRATCH(RBL_STS)); - uint32_t flb_stat = atl_read(hw, ATL_GLOBAL_DAISY_CHAIN_STS1); + uint32_t reg; + uint32_t flb_stat; int tries = 0; /* bool host_load_done = false; */ + int ret; + + atl_lock_fw(hw); + + reg = atl_read(hw, ATL_MCP_SCRATCH(RBL_STS)); + flb_stat = atl_read(hw, ATL_GLOBAL_DAISY_CHAIN_STS1); while (!reg && flb_stat == 0x6000000 && tries++ < 1000) { mdelay(1); @@ -160,10 +181,12 @@ int atl_hw_reset(struct atl_hw *hw) atl_dev_dbg("0x388: %#x 0x704: %#x\n", reg, flb_stat); if (tries >= 1000) { atl_dev_err("Timeout waiting to choose RBL or FLB path\n"); - return -EIO; + ret = -EIO; + goto unlock; } if (!reg) + /* atl_hw_reset_nonrbl() releases the fw lock */ return atl_hw_reset_nonrbl(hw); atl_write(hw, 0x404, 0x40e1); @@ -199,11 +222,13 @@ int atl_hw_reset(struct atl_hw *hw) if (reg == 0xf1a7) { atl_dev_err("MAC FW Host load not supported yet\n"); - return -EIO; + ret = -EIO; + goto unlock; } if (!reg || reg == 0xdead) { atl_dev_err("RBL restart timeout: %#x\n", reg); - return -EIO; + ret = -EIO; + goto unlock; } atl_dev_dbg("RBL restart took %d ms result %#x\n", tries, reg); @@ -220,7 +245,17 @@ int atl_hw_reset(struct atl_hw *hw) /* } */ /* } */ - return atl_fw_init(hw); + ret = atl_fw_init(hw); + +unlock: + atl_unlock_fw(hw); + + if (ret) + set_bit(ATL_ST_RESET_NEEDED, &hw->state); + else + set_bit(ATL_ST_GLOBAL_CONF_NEEDED, &hw->state); + + return ret; } static int atl_get_mac_addr(struct atl_hw *hw, uint8_t *buf) @@ -240,15 +275,16 @@ static int atl_get_mac_addr(struct atl_hw *hw, uint8_t *buf) return ret; } -int atl_hwinit(struct atl_nic *nic, enum atl_board brd_id) +int atl_hwinit(struct atl_hw *hw, enum atl_board brd_id) { - struct atl_hw *hw = &nic->hw; struct atl_board_info *brd = &atl_boards[brd_id]; int ret; /* Default supported speed set based on device id. */ hw->link_state.supported = brd->link_mask; + hw->thermal = atl_def_thermal; + ret = atl_hw_reset(hw); atl_dev_info("rev 0x%x chip 0x%x FW img 0x%x\n", @@ -260,18 +296,14 @@ int atl_hwinit(struct atl_nic *nic, enum atl_board brd_id) return ret; ret = atl_get_mac_addr(hw, hw->mac_addr); - if (ret) { + if (ret) atl_dev_err("couldn't read MAC address\n"); - return ret; - } - return hw->mcp.ops->get_link_caps(hw); + return ret; } -static void atl_rx_xoff_set(struct atl_nic *nic, bool fc) +static void atl_rx_xoff_set(struct atl_hw *hw, bool fc) { - struct atl_hw *hw = &nic->hw; - atl_write_bit(hw, ATL_RX_PBUF_REG2(0), 31, fc); } @@ -280,6 +312,11 @@ void atl_refresh_link(struct atl_nic *nic) struct atl_hw *hw = &nic->hw; struct atl_link_type *link, *prev_link = hw->link_state.link; + if (test_bit(ATL_ST_RESETTING, &hw->state) || + !test_bit(ATL_ST_ENABLED, &hw->state) || + !test_and_clear_bit(ATL_ST_UPDATE_LINK, &hw->state)) + return; + link = hw->mcp.ops->check_link(hw); if (link) { @@ -291,15 +328,17 @@ void atl_refresh_link(struct atl_nic *nic) atl_nic_info("Link down\n"); netif_carrier_off(nic->ndev); } - atl_rx_xoff_set(nic, !!(hw->link_state.fc.cur & atl_fc_rx)); + atl_rx_xoff_set(hw, !!(hw->link_state.fc.cur & atl_fc_rx)); + + atl_intr_enable_non_ring(nic); } static irqreturn_t atl_link_irq(int irq, void *priv) { struct atl_nic *nic = (struct atl_nic *)priv; + set_bit(ATL_ST_UPDATE_LINK, &nic->hw.state); atl_schedule_work(nic); - atl_intr_enable(&nic->hw, BIT(0)); return IRQ_HANDLED; } @@ -307,7 +346,7 @@ static irqreturn_t atl_legacy_irq(int irq, void *priv) { struct atl_nic *nic = priv; struct atl_hw *hw = &nic->hw; - uint32_t mask = hw->intr_mask | BIT(atl_qvec_intr(nic->qvecs)); + uint32_t mask = hw->non_ring_intr_mask | BIT(atl_qvec_intr(nic->qvecs)); uint32_t stat; @@ -441,10 +480,15 @@ unsigned int atl_fwd_tx_buf_reserve = module_param_named(fwd_tx_buf_reserve, atl_fwd_tx_buf_reserve, uint, 0444); module_param_named(fwd_rx_buf_reserve, atl_fwd_rx_buf_reserve, uint, 0444); +/* Must be called either during early init when netdev isn't yet + * registered, or with RTNL lock held */ void atl_start_hw_global(struct atl_nic *nic) { struct atl_hw *hw = &nic->hw; + if (!test_and_clear_bit(ATL_ST_GLOBAL_CONF_NEEDED, &hw->state)) + return; + /* Enable TPO2 */ atl_write(hw, 0x7040, 0x10000); /* Enable RPF2, filter logic 3 */ @@ -540,9 +584,6 @@ void atl_start_hw_global(struct atl_nic *nic) /* Reset Rx/Tx on unexpected PERST# */ atl_write_bit(hw, 0x1000, 29, 0); atl_write(hw, 0x448, 3); - - /* Enable non-ring interrupts */ - atl_intr_enable_non_ring(nic); } #define atl_vlan_flt_val(vid) ((uint32_t)(vid) | 1 << 16 | 1 << 31) @@ -938,9 +979,11 @@ int atl_update_eth_stats(struct atl_nic *nic) uint32_t reg = 0, reg2 = 0; int ret; + atl_lock_fw(hw); + ret = atl_hwsem_get(hw, ATL_MCP_SEM_MSM); if (ret) - return ret; + goto unlock_fw; __READ_MSM_OR_GOTO(ret, hw, ATL_MSM_CTR_TX_PAUSE, ®, hwsem_put); stats.tx_pause = reg; @@ -990,6 +1033,8 @@ int atl_update_eth_stats(struct atl_nic *nic) hwsem_put: atl_hwsem_put(hw, ATL_MCP_SEM_MSM); +unlock_fw: + atl_unlock_fw(hw); return ret; } #undef __READ_MSM_OR_GOTO diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.h index 16e1f618eda2..d7a512da60b7 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.h +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.h @@ -42,27 +42,45 @@ enum atl_board { ATL_AQC100, }; +struct atl_thermal { + unsigned flags; + uint8_t crit; + uint8_t high; + uint8_t low; +}; + +extern struct atl_thermal atl_def_thermal; + +enum atl_nic_state { + ATL_ST_ENABLED, + ATL_ST_CONFIGURED, + ATL_ST_RINGS_RUNNING, + /* ATL_ST_FWD_RINGS_RUNNING, */ + ATL_ST_UP, + ATL_ST_WORK_SCHED, + ATL_ST_UPDATE_LINK, + ATL_ST_RESETTING, + ATL_ST_RESET_NEEDED, + ATL_ST_GLOBAL_CONF_NEEDED, + ATL_ST_START_NEEDED, + ATL_ST_DETACHED, +}; + #define ATL_WAKE_SUPPORTED (WAKE_MAGIC | WAKE_PHY) struct atl_hw { uint8_t __iomem *regs; struct pci_dev *pdev; + unsigned long state; struct atl_link_state link_state; unsigned wol_mode; - struct { - uint32_t fw_rev; - bool poll_link; - struct atl_fw_ops *ops; - uint32_t fw_stat_addr; - uint32_t fw_settings_addr; - uint32_t fw_settings_len; - struct mutex lock; - } mcp; - uint32_t intr_mask; + struct atl_mcp mcp; + uint32_t non_ring_intr_mask; uint8_t mac_addr[ETH_ALEN]; #define ATL_RSS_KEY_SIZE 40 uint8_t rss_key[ATL_RSS_KEY_SIZE]; #define ATL_RSS_TBL_SIZE (1 << 6) uint8_t rss_tbl[ATL_RSS_TBL_SIZE]; + struct atl_thermal thermal; }; union atl_desc; @@ -178,7 +196,7 @@ static inline void atl_set_vlan_promisc(struct atl_hw *hw, int promisc) int atl_read_mcp_mem(struct atl_hw *hw, uint32_t mcp_addr, void *host_addr, unsigned size); -int atl_hwinit(struct atl_nic *nic, enum atl_board brd_id); +int atl_hwinit(struct atl_hw *hw, enum atl_board brd_id); void atl_refresh_link(struct atl_nic *nic); void atl_set_rss_key(struct atl_hw *hw); void atl_set_rss_tbl(struct atl_hw *hw); @@ -219,4 +237,16 @@ static inline int atl_read_fwsettings_word(struct atl_hw *hw, uint32_t offt, return atl_read_mcp_word(hw, offt + hw->mcp.fw_settings_addr, val); } +static inline void atl_lock_fw(struct atl_hw *hw) +{ + mutex_lock(&hw->mcp.lock); +} + +static inline void atl_unlock_fw(struct atl_hw *hw) +{ + mutex_unlock(&hw->mcp.lock); +} + +void atl_fw_watchdog(struct atl_hw *hw); + #endif diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hwmon.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hwmon.c index 684f0c57659b..b8b592ffabec 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hwmon.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hwmon.c @@ -9,14 +9,59 @@ #include "atl_common.h" #include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + #if LINUX_VERSION_CODE > KERNEL_VERSION(4,10,0) +static ssize_t atl_hwmon_set_flag(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct atl_hw *hw = dev_get_drvdata(dev); + bool val; + int ret; + + if (strtobool(buf, &val) < 0) + return -EINVAL; + + ret = atl_update_thermal_flag(hw, sattr->index, val); + + return ret ?: size; +} + +static ssize_t atl_hwmon_show_flag(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct atl_hw *hw = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", + !!(hw->thermal.flags & BIT(sattr->index))); +} + +#define ATL_HWMON_BIT_ATTR(_name, _bit) \ + SENSOR_DEVICE_ATTR(_name, S_IRUGO | S_IWUSR, atl_hwmon_show_flag, \ + atl_hwmon_set_flag, _bit) + +static ATL_HWMON_BIT_ATTR(monitor, atl_thermal_monitor_shift); +static ATL_HWMON_BIT_ATTR(throttle, atl_thermal_throttle_shift); +static ATL_HWMON_BIT_ATTR(ignore_lims, atl_thermal_ignore_lims_shift); + +static struct attribute *atl_hwmon_attrs[] = { + &sensor_dev_attr_monitor.dev_attr.attr, + &sensor_dev_attr_throttle.dev_attr.attr, + &sensor_dev_attr_ignore_lims.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(atl_hwmon); + static char *atl_hwmon_labels[] = { "PHY Temperature", }; static const uint32_t atl_hwmon_temp_config[] = { - HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_MAX_ALARM, 0, }; @@ -33,7 +78,18 @@ static const struct hwmon_channel_info *atl_hwmon_info[] = { static umode_t atl_hwmon_is_visible(const void *p, enum hwmon_sensor_types type, uint32_t attr, int channel) { - return type == hwmon_temp ? S_IRUGO : 0; + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: case hwmon_temp_max_alarm: case hwmon_temp_label: + return S_IRUGO; + + case hwmon_temp_crit: case hwmon_temp_max: case hwmon_temp_max_hyst: + return S_IRUGO | S_IWUSR; + } + + return 0; } static int atl_hwmon_read(struct device *dev, enum hwmon_sensor_types type, @@ -42,17 +98,77 @@ static int atl_hwmon_read(struct device *dev, enum hwmon_sensor_types type, struct atl_hw *hw = dev_get_drvdata(dev); int temp, ret; - if (type != hwmon_temp || attr != hwmon_temp_input) + if (type != hwmon_temp) return -EINVAL; - ret = hw->mcp.ops->get_phy_temperature(hw, &temp); - if (ret) - return ret; + switch (attr) { + case hwmon_temp_input: + ret = hw->mcp.ops->get_phy_temperature(hw, &temp); + if (ret) + return ret; + + *val = temp; + break; + + case hwmon_temp_crit: + *val = hw->thermal.crit * 1000; + break; + + case hwmon_temp_max: + *val = hw->thermal.high * 1000; + break; + + case hwmon_temp_max_hyst: + *val = hw->thermal.low * 1000; + break; + + case hwmon_temp_max_alarm: + *val = hw->link_state.thermal_throttled; + break; - *val = temp; + default: + return -EINVAL; + } return 0; } +static int atl_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + uint32_t attr, int channel, long val) +{ + struct atl_hw *hw = dev_get_drvdata(dev); + uint8_t *ptr, old; + int ret; + + if (type != hwmon_temp) + return -EINVAL; + + switch (attr) { + case hwmon_temp_crit: + ptr = &hw->thermal.crit; + break; + + case hwmon_temp_max: + ptr = &hw->thermal.high; + break; + + case hwmon_temp_max_hyst: + ptr = &hw->thermal.low; + break; + + default: + return -EINVAL; + } + + old = *ptr; + *ptr = val / 1000; + + ret = atl_update_thermal(hw); + if (ret) + *ptr = old; + + return ret; +} + static int atl_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, const char **str) { @@ -67,6 +183,7 @@ static const struct hwmon_ops atl_hwmon_ops = { .is_visible = atl_hwmon_is_visible, .read = atl_hwmon_read, .read_string = atl_hwmon_read_string, + .write = atl_hwmon_write, }; static const struct hwmon_chip_info atl_hwmon = { @@ -77,9 +194,10 @@ static const struct hwmon_chip_info atl_hwmon = { int atl_hwmon_init(struct atl_nic *nic) { struct device *hwmon_dev; + struct atl_hw *hw = &nic->hw; - hwmon_dev = devm_hwmon_device_register_with_info(&nic->hw.pdev->dev, - nic->ndev->name, &nic->hw, &atl_hwmon, NULL); + hwmon_dev = devm_hwmon_device_register_with_info(&hw->pdev->dev, + nic->ndev->name, hw, &atl_hwmon, atl_hwmon_groups); return PTR_ERR_OR_ZERO(hwmon_dev); } diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_main.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_main.c index 531aa9d9b4fe..f47dfcd34582 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_main.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_main.c @@ -13,9 +13,7 @@ #include <linux/etherdevice.h> #include <linux/rtnetlink.h> -#include "atl_qcom_ipa.h" - -#include "atl_of.h" +#include "atl_qcom.h" const char atl_driver_name[] = "atlantic-fwd"; @@ -29,29 +27,61 @@ module_param_named(tx_mod, atl_tx_mod, uint, 0444); static unsigned int atl_keep_link = 0; module_param_named(keep_link, atl_keep_link, uint, 0644); -static void atl_link_up(struct atl_nic *nic) +static void atl_start_link(struct atl_nic *nic) { struct atl_hw *hw = &nic->hw; - if (hw->mcp.poll_link) - mod_timer(&nic->link_timer, jiffies + HZ); - hw->link_state.force_off = 0; hw->mcp.ops->set_link(hw, true); + set_bit(ATL_ST_UPDATE_LINK, &hw->state); + atl_schedule_work(nic); } -static int atl_do_open(struct atl_nic *nic) +static void atl_stop_link(struct atl_nic *nic) { - int ret; + struct atl_hw *hw = &nic->hw; - ret = atl_start_rings(nic); + hw->link_state.force_off = 1; + hw->mcp.ops->set_link(hw, true); + hw->link_state.link = 0; + netif_carrier_off(nic->ndev); +} + +static int atl_start(struct atl_nic *nic) +{ + int ret = 0; + + atl_start_hw_global(nic); + + if (atl_keep_link || netif_running(nic->ndev)) + atl_start_link(nic); + + if (netif_running(nic->ndev)) + ret = atl_start_rings(nic); + + if (ret && !atl_keep_link) + atl_stop_link(nic); + + /* if (ret) */ + /* goto out; */ + /* ret = atl_fwd_resume_rings(nic); */ + +/* out: */ if (ret) - return ret; + set_bit(ATL_ST_START_NEEDED, &nic->hw.state); + return ret; +} - if (!atl_keep_link) - atl_link_up(nic); +static void atl_stop(struct atl_nic *nic, bool full) +{ + atl_stop_rings(nic); - return 0; + /* if (full) { */ + /* atl_stop_fwd_rings(nic); */ + /* } */ + + if (!atl_keep_link || full) + atl_stop_link(nic); } static int atl_open(struct net_device *ndev) @@ -59,7 +89,7 @@ static int atl_open(struct net_device *ndev) struct atl_nic *nic = netdev_priv(ndev); int ret; - if (!test_bit(ATL_ST_CONFIGURED, &nic->state)) { + if (!test_bit(ATL_ST_CONFIGURED, &nic->hw.state)) { /* A previous atl_reconfigure() had failed. Try once more. */ ret = atl_setup_datapath(nic); if (ret) @@ -77,13 +107,11 @@ static int atl_open(struct net_device *ndev) if (ret) goto free_rings; - ret = atl_do_open(nic); + ret = atl_start(nic); if (ret) goto free_rings; - netif_tx_start_all_queues(ndev); - - set_bit(ATL_ST_UP, &nic->state); + set_bit(ATL_ST_UP, &nic->hw.state); return 0; free_rings: @@ -91,25 +119,6 @@ free_rings: return ret; } -static void atl_link_down(struct atl_nic *nic) -{ - struct atl_hw *hw = &nic->hw; - - del_timer_sync(&nic->link_timer); - hw->link_state.force_off = 1; - hw->mcp.ops->set_link(hw, true); - hw->link_state.link = 0; - netif_carrier_off(nic->ndev); -} - -static void atl_do_close(struct atl_nic *nic) -{ - if (!atl_keep_link) - atl_link_down(nic); - - atl_stop_rings(nic); -} - static int atl_close(struct net_device *ndev) { struct atl_nic *nic = netdev_priv(ndev); @@ -117,12 +126,10 @@ static int atl_close(struct net_device *ndev) /* atl_close() can be called a second time if * atl_reconfigure() fails. Just return */ - if (!test_and_clear_bit(ATL_ST_UP, &nic->state)) + if (!test_and_clear_bit(ATL_ST_UP, &nic->hw.state)) return 0; - netif_tx_stop_all_queues(ndev); - - atl_do_close(nic); + atl_stop(nic, false); atl_free_rings(nic); return 0; @@ -197,7 +204,7 @@ int atl_reconfigure(struct atl_nic *nic) goto err; /* Re-enable link interrupts disabled in atl_clear_datapath() */ - atl_intr_enable(&nic->hw, BIT(0)); + atl_intr_enable_non_ring(nic); /* Number of rings might have changed, re-init RSS * redirection table. @@ -222,25 +229,83 @@ static struct workqueue_struct *atl_wq; void atl_schedule_work(struct atl_nic *nic) { - if (!test_and_set_bit(ATL_ST_WORK_SCHED, &nic->state)) + if (!test_and_set_bit(ATL_ST_WORK_SCHED, &nic->hw.state)) queue_work(atl_wq, &nic->work); } +static int atl_do_reset(struct atl_nic *nic) +{ + struct atl_hw *hw = &nic->hw; + int ret; + bool reset, start; + + if (!test_bit(ATL_ST_ENABLED, &hw->state)) + /* We're suspending, postpone resets till resume */ + return 0; + + reset = test_and_clear_bit(ATL_ST_RESET_NEEDED, &hw->state); + start = test_and_clear_bit(ATL_ST_START_NEEDED, &hw->state); + + if (!reset && !start) + return 0; + + if (reset) + set_bit(ATL_ST_RESETTING, &hw->state); + rtnl_lock(); + + if (reset) { + atl_stop(nic, true); + + ret = atl_hw_reset(hw); + if (ret) { + atl_nic_err("HW reset failed, re-trying\n"); + if (!test_and_set_bit(ATL_ST_DETACHED, &hw->state)) + netif_device_detach(nic->ndev); + goto out; + } + start = true; + clear_bit(ATL_ST_RESETTING, &hw->state); + } + + if (start) { + ret = atl_start(nic); + if (ret) + goto out; + } + + if (test_and_clear_bit(ATL_ST_DETACHED, &hw->state)) + netif_device_attach(nic->ndev); + +out: + rtnl_unlock(); + return ret; +} + static void atl_work(struct work_struct *work) { struct atl_nic *nic = container_of(work, struct atl_nic, work); + struct atl_hw *hw = &nic->hw; + int ret; + clear_bit(ATL_ST_WORK_SCHED, &hw->state); + + atl_fw_watchdog(hw); + ret = atl_do_reset(nic); + if (ret) + goto out; atl_refresh_link(nic); - clear_bit(ATL_ST_WORK_SCHED, &nic->state); + +out: + if (test_bit(ATL_ST_ENABLED, &hw->state)) + mod_timer(&nic->work_timer, jiffies + HZ); } -static void atl_link_timer(struct timer_list *timer) +static void atl_work_timer(struct timer_list *timer) { struct atl_nic *nic = - container_of(timer, struct atl_nic, link_timer); + container_of(timer, struct atl_nic, work_timer); atl_schedule_work(nic); - mod_timer(&nic->link_timer, jiffies + HZ); } static const struct pci_device_id atl_pci_tbl[] = { @@ -291,16 +356,6 @@ static int atl_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct atl_hw *hw; int disable_needed; - if (atl_max_queues < 1 || atl_max_queues > ATL_MAX_QUEUES) { - dev_err(&pdev->dev, "Bad atl_max_queues value %d, must be between 1 and %d inclusive\n", - atl_max_queues, ATL_MAX_QUEUES); - return -EINVAL; - } - - ret = atl_parse_dt(&pdev->dev); - if (ret) - return ret; - ret = pci_enable_device_mem(pdev); if (ret) return ret; @@ -334,9 +389,9 @@ static int atl_probe(struct pci_dev *pdev, const struct pci_device_id *id) spin_lock_init(&nic->stats_lock); INIT_WORK(&nic->work, atl_work); mutex_init(&nic->hw.mcp.lock); - __set_bit(ATL_ST_ENABLED, &nic->state); hw = &nic->hw; + __set_bit(ATL_ST_ENABLED, &hw->state); hw->regs = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); if (!hw->regs) { @@ -344,10 +399,13 @@ static int atl_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_ioremap; } - ret = atl_hwinit(nic, id->driver_data); + ret = atl_hwinit(hw, id->driver_data); if (ret) goto err_hwinit; + hw->mcp.ops->set_default_link(hw); + hw->link_state.force_off = 1; + pci_set_master(pdev); eth_platform_get_mac_address(&hw->pdev->dev, hw->mac_addr); @@ -394,11 +452,9 @@ static int atl_probe(struct pci_dev *pdev, const struct pci_device_id *id) ndev->priv_flags |= IFF_UNICAST_FLT; - timer_setup(&nic->link_timer, &atl_link_timer, 0); + timer_setup(&nic->work_timer, &atl_work_timer, 0); - hw->mcp.ops->set_default_link(hw); - hw->link_state.force_off = 1; - hw->intr_mask = BIT(ATL_NUM_NON_RING_IRQS) - 1; + hw->non_ring_intr_mask = BIT(ATL_NUM_NON_RING_IRQS) - 1; ndev->netdev_ops = &atl_ndev_ops; ndev->mtu = 1500; #ifdef ATL_HAVE_MINMAX_MTU @@ -412,17 +468,27 @@ static int atl_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_drvdata(pdev, nic); netif_carrier_off(ndev); + /* Safe to ignore ret value here. atl_start() only returns + * errors when rings are started. We could race with someone + * doing ifup on newly created netdev, but either they will + * succeed with grabbing RTNL first and handle ring-related + * errors there, or we will be first and just bring the + * global HW up. */ + rtnl_lock(); + atl_start(nic); + rtnl_unlock(); + ret = atl_hwmon_init(nic); if (ret) goto err_hwmon_init; - atl_start_hw_global(nic); - if (atl_keep_link) - atl_link_up(nic); + atl_intr_enable_non_ring(nic); + mod_timer(&nic->work_timer, jiffies + HZ); return 0; err_hwmon_init: + atl_stop(nic, true); unregister_netdev(nic->ndev); err_register: atl_clear_datapath(nic); @@ -430,7 +496,7 @@ err_datapath: err_hwinit: iounmap(hw->regs); err_ioremap: - disable_needed = test_and_clear_bit(ATL_ST_ENABLED, &nic->state); + disable_needed = test_and_clear_bit(ATL_ST_ENABLED, &hw->state); free_netdev(ndev); err_alloc_ndev: pci_release_regions(pdev); @@ -450,8 +516,11 @@ static void atl_remove(struct pci_dev *pdev) return; netif_carrier_off(nic->ndev); + atl_stop(nic, true); + disable_needed = test_and_clear_bit(ATL_ST_ENABLED, &nic->hw.state); + del_timer_sync(&nic->work_timer); + cancel_work_sync(&nic->work); atl_intr_disable_all(&nic->hw); - /* atl_hw_reset(&nic->hw); */ unregister_netdev(nic->ndev); #ifdef CONFIG_ATLFWD_FWD @@ -460,8 +529,6 @@ static void atl_remove(struct pci_dev *pdev) atl_clear_datapath(nic); iounmap(nic->hw.regs); - disable_needed = test_and_clear_bit(ATL_ST_ENABLED, &nic->state); - cancel_work_sync(&nic->work); free_netdev(nic->ndev); pci_release_regions(pdev); if (disable_needed) @@ -476,13 +543,13 @@ static int atl_suspend_common(struct device *dev, bool deep) int ret; rtnl_lock(); - netif_device_detach(nic->ndev); - if (netif_running(nic->ndev)) - atl_do_close(nic); + if (!test_and_set_bit(ATL_ST_DETACHED, &hw->state)) + netif_device_detach(nic->ndev); + + atl_stop(nic, true); - if (deep && atl_keep_link) - atl_link_down(nic); + atl_clear_rdm_cache(nic); if (deep && nic->flags & ATL_FL_WOL) { ret = hw->mcp.ops->enable_wol(hw); @@ -490,11 +557,12 @@ static int atl_suspend_common(struct device *dev, bool deep) atl_dev_err("Enable WoL failed: %d\n", -ret); } - pci_save_state(pdev); - pci_disable_device(pdev); - pci_set_power_state(pdev, PCI_D3hot); + clear_bit(ATL_ST_ENABLED, &hw->state); + cancel_work_sync(&nic->work); - __clear_bit(ATL_ST_ENABLED, &nic->state); + pci_disable_device(pdev); + pci_save_state(pdev); + pci_prepare_to_sleep(pdev); rtnl_unlock(); @@ -526,23 +594,16 @@ static int atl_resume_common(struct device *dev, bool deep) if (ret) goto exit; + set_bit(ATL_ST_ENABLED, &nic->hw.state); pci_set_master(pdev); - __set_bit(ATL_ST_ENABLED, &nic->state); if (deep) { ret = atl_hw_reset(&nic->hw); if (ret) goto exit; - - atl_start_hw_global(nic); } - if (netif_running(nic->ndev)) - ret = atl_do_open(nic); - - if (deep && atl_keep_link) - atl_link_up(nic); - + ret = atl_start(nic); if (ret) goto exit; @@ -550,7 +611,8 @@ static int atl_resume_common(struct device *dev, bool deep) if (ret) goto exit; - netif_device_attach(nic->ndev); + if (test_and_clear_bit(ATL_ST_DETACHED, &nic->hw.state)) + netif_device_attach(nic->ndev); exit: rtnl_unlock(); @@ -593,19 +655,52 @@ static struct pci_driver atl_pci_ops = { #endif }; +struct atl_thermal atl_def_thermal; + +static bool atl_def_thermal_monitor = true, atl_def_thermal_throttle = false, + atl_def_thermal_ignore_lims = false; +module_param_named(thermal_monitor, atl_def_thermal_monitor, bool, 0444); +module_param_named(thermal_throttle, atl_def_thermal_throttle, bool, 0444); +module_param_named(thermal_ignore_limits, atl_def_thermal_ignore_lims, bool, 0444); + +static uint8_t atl_def_thermal_crit = 108, atl_def_thermal_high = 100, + atl_def_thermal_low = 80; +module_param_named(thermal_crit, atl_def_thermal_crit, byte, 0444); +module_param_named(thermal_high, atl_def_thermal_high, byte, 0444); +module_param_named(thermal_low, atl_def_thermal_low, byte, 0444); + static int __init atl_module_init(void) { + struct atl_hw *hw = NULL; int ret; + atl_def_thermal.flags = + atl_def_thermal_monitor << atl_thermal_monitor_shift | + atl_def_thermal_throttle << atl_thermal_throttle_shift | + atl_def_thermal_ignore_lims << atl_thermal_ignore_lims_shift; + atl_def_thermal.crit = atl_def_thermal_crit; + atl_def_thermal.high = atl_def_thermal_high; + atl_def_thermal.low = atl_def_thermal_low; + + ret = atl_verify_thermal_limits(hw, &atl_def_thermal); + if (ret) + return ret; + + if (atl_max_queues < 1 || atl_max_queues > ATL_MAX_QUEUES) { + atl_dev_init_err("Bad atl_max_queues value %d, must be between 1 and %d inclusive\n", + atl_max_queues, ATL_MAX_QUEUES); + return -EINVAL; + } + atl_wq = create_singlethread_workqueue(atl_driver_name); if (!atl_wq) { pr_err("%s: Couldn't create workqueue\n", atl_driver_name); return -ENOMEM; } - ret = atl_qcom_ipa_register(&atl_pci_ops); + ret = atl_qcom_register(&atl_pci_ops); if (ret) { - pr_err("%s: Failed to register driver with IPA\n", + pr_err("%s: Failed to register driver with platform\n", atl_driver_name); destroy_workqueue(atl_wq); return ret; @@ -613,7 +708,7 @@ static int __init atl_module_init(void) ret = pci_register_driver(&atl_pci_ops); if (ret) { - atl_qcom_ipa_unregister(&atl_pci_ops); + atl_qcom_unregister(&atl_pci_ops); destroy_workqueue(atl_wq); return ret; } @@ -626,7 +721,7 @@ static void __exit atl_module_exit(void) { pci_unregister_driver(&atl_pci_ops); - atl_qcom_ipa_unregister(&atl_pci_ops); + atl_qcom_unregister(&atl_pci_ops); if (atl_wq) { destroy_workqueue(atl_wq); diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_of.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_of.c deleted file mode 100644 index ef3c68163384..000000000000 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_of.c +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/of.h> - -#include "atl_qcom.h" -#include "atl_of.h" - -static const struct of_device_id aqc_matches[] = { - { .compatible = "aquantia,aqc-107" }, - { .compatible = "aquantia,aqc-108" }, - { .compatible = "aquantia,aqc-109" }, -}; - -int atl_parse_dt(struct device *dev) -{ - if (!dev->of_node) { - dev_dbg(dev, "device tree node is not present\n"); - return 0; - } - - if (!of_match_node(aqc_matches, dev->of_node)) { - dev_notice(dev, "device tree node is not compatible\n"); - return 0; - } - - /* Aquantia properties go here */ - - /* OEM properties go here */ - - return atl_qcom_parse_dt(dev); -} diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_of.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_of.h deleted file mode 100644 index 7f857574216e..000000000000 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_of.h +++ /dev/null @@ -1,31 +0,0 @@ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _ATL_OF_H_ -#define _ATL_OF_H_ - -#include <linux/device.h> - -#ifdef CONFIG_OF - -int atl_parse_dt(struct device *dev); - -#else - -static inline int atl_parse_dt(struct device *dev) -{ - return 0; -} - -#endif // CONFIG_OF - -#endif // _ATL_OF_H_ diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_qcom.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_qcom.c index 75132cb42076..56023aeeec0a 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_qcom.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_qcom.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -10,12 +10,18 @@ * GNU General Public License for more details. */ +#include <linux/of.h> + #include <linux/iommu.h> #include <asm/dma-iommu.h> #include <linux/platform_device.h> +#include "atl_qcom_ipa.h" #include "atl_qcom.h" +static int (*atl_probe_real)(struct pci_dev *, const struct pci_device_id *); +static void (*atl_remove_real)(struct pci_dev *); + static int atl_qcom_parse_smmu_attr(struct device *dev, struct iommu_domain *domain, const char *key, @@ -60,7 +66,7 @@ static int atl_qcom_parse_smmu_attrs(struct device *dev, return rc; } -static int atl_qcom_parse_smmu(struct device *dev) +static int __atl_qcom_attach_smmu(struct device *dev) { int rc; const char *key; @@ -117,14 +123,110 @@ err_release_mapping: return rc; } -int atl_qcom_parse_dt(struct device *dev) +static int atl_qcom_attach_smmu(struct device *dev) { int rc = 0; + if (!dev->of_node) { + dev_dbg(dev, "device tree node is not present\n"); + return 0; + } + if (of_find_property(dev->of_node, "qcom,smmu", NULL)) - rc = atl_qcom_parse_smmu(dev); + rc = __atl_qcom_attach_smmu(dev); else dev_dbg(dev, "SMMU config not present in DT\n"); return 0; } + +static void atl_qcom_detach_smmu(struct device *dev) +{ + struct dma_iommu_mapping *mapping; + + if (!dev->of_node || !of_find_property(dev->of_node, "qcom,smmu", NULL)) + return; + + mapping = to_dma_iommu_mapping(dev); + if (!mapping) + return; + + arm_iommu_detach_device(dev); + arm_iommu_release_mapping(mapping); +} + +static int atl_qcom_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int rc; + + rc = atl_qcom_attach_smmu(&pdev->dev); + if (rc) + return rc; + + rc = atl_probe_real(pdev, id); + if (rc) { + atl_qcom_detach_smmu(&pdev->dev); + return rc; + } + + return 0; +} + +static void atl_qcom_remove(struct pci_dev *pdev) +{ + atl_remove_real(pdev); + atl_qcom_detach_smmu(&pdev->dev); +} + +static int __atl_qcom_register(struct pci_driver *pdrv) +{ + if (atl_probe_real || atl_remove_real) { + pr_err("%s: Driver already registered\n", __func__); + return -EEXIST; + } + + atl_probe_real = pdrv->probe; + pdrv->probe = atl_qcom_probe; + + atl_remove_real = pdrv->remove; + pdrv->remove = atl_qcom_remove; + + return 0; +} + +static void __atl_qcom_unregister(struct pci_driver *pdrv) +{ + if (atl_probe_real) { + pdrv->probe = atl_probe_real; + atl_probe_real = NULL; + } + + if (atl_remove_real) { + pdrv->remove = atl_remove_real; + atl_remove_real = NULL; + } +} + +int atl_qcom_register(struct pci_driver *pdrv) +{ + int rc; + + rc = __atl_qcom_register(pdrv); + if (rc) + return rc; + + rc = atl_qcom_ipa_register(pdrv); + if (rc) { + pr_err("%s: Failed to register driver with IPA\n", __func__); + __atl_qcom_unregister(pdrv); + return rc; + } + + return 0; +} + +void atl_qcom_unregister(struct pci_driver *pdrv) +{ + atl_qcom_ipa_unregister(pdrv); + __atl_qcom_unregister(pdrv); +} diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_qcom.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_qcom.h index 2730d048e47c..08bfffa722d4 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_qcom.h +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_qcom.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -13,16 +13,17 @@ #ifndef _ATL_QCOM_H_ #define _ATL_QCOM_H_ +#include <linux/pci.h> + #ifdef CONFIG_AQFWD_QCOM -int atl_qcom_parse_dt(struct device *dev); +int atl_qcom_register(struct pci_driver *pdrv); +void atl_qcom_unregister(struct pci_driver *pdrv); #else -static inline int atl_qcom_parse_dt(struct device *dev) -{ - return 0; -} +static inline int atl_qcom_register(struct pci_driver *pdrv) { return 0; } +static inline void atl_qcom_unregister(struct pci_driver *pdrv) { return; } #endif // CONFIG_AQFWD_QCOM diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ring.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ring.c index 5455aec58233..5ae31f627019 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ring.c @@ -16,6 +16,9 @@ #include <linux/vmalloc.h> #include <linux/interrupt.h> #include <linux/cpu.h> +#include <uapi/linux/ip.h> +#include <uapi/linux/tcp.h> +#include <uapi/linux/udp.h> #include "atl_trace.h" @@ -384,7 +387,7 @@ static bool atl_clean_tx(struct atl_desc_ring *ring) smp_mb(); if (__netif_subqueue_stopped(ndev, ring->qvec->idx) && - test_bit(ATL_ST_UP, &nic->state)) { + test_bit(ATL_ST_RINGS_RUNNING, &nic->hw.state)) { atl_nic_dbg("restarting tx queue\n"); netif_wake_subqueue(ndev, ring->qvec->idx); atl_update_ring_stat(ring, tx.tx_restart, 1); @@ -394,12 +397,72 @@ static bool atl_clean_tx(struct atl_desc_ring *ring) return !!budget; } +/* work around HW bugs in checksum calculation: + * - packets less than 60 octets + * - ip, tcp or udp checksum is 0xFFFF + */ +static bool atl_checksum_workaround(struct sk_buff *skb, + struct atl_rx_desc_wb *desc) +{ + int ip_header_offset = 14; + int l4_header_offset = 0; + struct iphdr *ip; + struct tcphdr *tcp; + struct udphdr *udp; + + if (desc->pkt_len <= 60) + return true; + + if ((desc->pkt_type & atl_rx_pkt_type_vlan_msk) == + atl_rx_pkt_type_vlan) + ip_header_offset += 4; + + if ((desc->pkt_type & atl_rx_pkt_type_vlan_msk) == + atl_rx_pkt_type_dbl_vlan) + ip_header_offset += 8; + + switch (desc->pkt_type & atl_rx_pkt_type_l3_msk) { + case atl_rx_pkt_type_ipv4: + ip = (struct iphdr *) &skb->data[ip_header_offset]; + + if (ip->check == 0xFFFF) + return true; + l4_header_offset = ip->ihl << 2; + break; + case atl_rx_pkt_type_ipv6: + l4_header_offset = ip_header_offset + sizeof(struct ipv6hdr); + break; + default: + return false; + } + + switch (desc->pkt_type & atl_rx_pkt_type_l4_msk) { + case atl_rx_pkt_type_tcp: + tcp = (struct tcphdr *) &skb->data[ip_header_offset + + l4_header_offset]; + + if (tcp->check == 0xFFFF) + return true; + break; + case atl_rx_pkt_type_udp: + udp = (struct udphdr *) &skb->data[ip_header_offset + + l4_header_offset]; + if (udp->check == 0xFFFF) + return true; + break; + default: + return false; + } + + return false; +} + static bool atl_rx_checksum(struct sk_buff *skb, struct atl_rx_desc_wb *desc, struct atl_desc_ring *ring) { struct atl_nic *nic = ring->qvec->nic; struct net_device *ndev = nic->ndev; - int csum_ok = 1, recheck = 0; + int csum_ok = 1; skb_checksum_none_assert(skb); @@ -426,7 +489,6 @@ static bool atl_rx_checksum(struct sk_buff *skb, struct atl_rx_desc_wb *desc, switch (desc->pkt_type & atl_rx_pkt_type_l4_msk) { case atl_rx_pkt_type_tcp: case atl_rx_pkt_type_udp: - recheck = desc->pkt_len <= 60; csum_ok &= !(desc->rx_stat & atl_rx_stat_l4_err); break; default: @@ -436,8 +498,10 @@ static bool atl_rx_checksum(struct sk_buff *skb, struct atl_rx_desc_wb *desc, if (csum_ok) { skb->ip_summed = CHECKSUM_UNNECESSARY; return true; - } else if (recheck) - return true; + } else { + if (atl_checksum_workaround(skb, desc)) + return true; + } atl_update_ring_stat(ring, rx.csum_err, 1); @@ -1135,7 +1199,7 @@ void atl_clear_datapath(struct atl_nic *nic) * pci_ops->remove(), without an intervening * atl_setup_datapath(). */ - if (!test_and_clear_bit(ATL_ST_CONFIGURED, &nic->state)) + if (!test_and_clear_bit(ATL_ST_CONFIGURED, &nic->hw.state)) return; atl_free_link_intr(nic); @@ -1229,7 +1293,7 @@ int atl_setup_datapath(struct atl_nic *nic) nic->max_mtu = atl_rx_linear ? ATL_MAX_RX_LINEAR_MTU : ATL_MAX_MTU; - set_bit(ATL_ST_CONFIGURED, &nic->state); + set_bit(ATL_ST_CONFIGURED, &nic->hw.state); return 0; err_link_intr: @@ -1620,6 +1684,9 @@ int atl_start_rings(struct atl_nic *nic) struct atl_queue_vec *qvec; int ret; + if (test_bit(ATL_ST_RINGS_RUNNING, &hw->state)) + return 0; + if (nic->flags & ATL_FL_MULTIPLE_VECTORS) { mask = BIT(nic->nvecs + ATL_NUM_NON_RING_IRQS) - BIT(ATL_NUM_NON_RING_IRQS); @@ -1638,6 +1705,9 @@ int atl_start_rings(struct atl_nic *nic) goto stop; } + set_bit(ATL_ST_RINGS_RUNNING, &hw->state); + netif_tx_start_all_queues(nic->ndev); + return 0; stop: @@ -1647,17 +1717,28 @@ stop: return ret; } +void atl_clear_rdm_cache(struct atl_nic *nic) +{ + struct atl_hw *hw = &nic->hw; + + atl_write_bit(hw, 0x5a00, 0, 1); + udelay(10); + atl_write_bit(hw, 0x5a00, 0, 0); +} + void atl_stop_rings(struct atl_nic *nic) { struct atl_queue_vec *qvec; - struct atl_hw *hw = &nic->hw; + + if (!test_and_clear_bit(ATL_ST_RINGS_RUNNING, &nic->hw.state)) + return; + + netif_tx_stop_all_queues(nic->ndev); atl_for_each_qvec(nic, qvec) atl_stop_qvec(qvec); - atl_write_bit(hw, 0x5a00, 0, 1); - udelay(10); - atl_write_bit(hw, 0x5a00, 0, 0); + atl_clear_rdm_cache(nic); } int atl_set_features(struct net_device *ndev, netdev_features_t features) @@ -1698,6 +1779,10 @@ void atl_update_global_stats(struct atl_nic *nic) int i; struct atl_ring_stats stats; + if (!test_bit(ATL_ST_ENABLED, &nic->hw.state) || + test_bit(ATL_ST_RESETTING, &nic->hw.state)) + return; + memset(&stats, 0, sizeof(stats)); atl_update_eth_stats(nic); diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/release_notes.txt b/drivers/net/ethernet/aquantia/atlantic-fwd/release_notes.txt index 4b4c614d36a6..a9aef75a8a85 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/release_notes.txt +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/release_notes.txt @@ -1,3 +1,13 @@ +Version 1.0.20 +============== +net: aquantia: Re-initialize fwd rings on driver resume +Do entire suspend from dev_pm_ops->suspend() (AQ052) +Prevent atl_update_global_stats() calls when suspended (AQ056) (ATLDRV-1021) +Implement thermal throttling (ATLDRV-1009) +Add log message when high temperature treshold is reached (ATLDRV-998)) +Implement FW heartbeat watchdog (AQ010) (ATLDRV-502) +Workaround checksum offload bugs (ATLDRV-1013) + Version 1.0.19 ============== Fix freeing of buf vaddr vector in FWD API (AQ050) diff --git a/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth.c b/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth.c index 67eafb233d93..0e987a776be0 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth.c +++ b/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth.c @@ -27,6 +27,11 @@ enum ipa_eth_states { IPA_ETH_ST_MAX, }; +enum ipa_eth_dev_states { + IPA_ETH_DEV_ST_UNPAIRING, + IPA_ETH_DEV_ST_MAX, +}; + static unsigned long ipa_eth_state; static struct dentry *ipa_eth_debugfs; @@ -56,12 +61,15 @@ static inline bool ipa_eth_ready(void) static inline bool initable(struct ipa_eth_device *eth_dev) { - return eth_dev->init; + return !test_bit(IPA_ETH_DEV_ST_UNPAIRING, ð_dev->state) && + eth_dev->init; } static inline bool startable(struct ipa_eth_device *eth_dev) { - return eth_dev->init && eth_dev->start && + return !test_bit(IPA_ETH_DEV_ST_UNPAIRING, ð_dev->state) && + eth_dev->init && + eth_dev->start && test_bit(IPA_ETH_IF_ST_LOWER_UP, ð_dev->if_state); } @@ -148,9 +156,13 @@ static int ipa_eth_deinit_device(struct ipa_eth_device *eth_dev) return 0; } +static void ipa_eth_free_msg(void *buff, u32 len, u32 type) {} + static int ipa_eth_start_device(struct ipa_eth_device *eth_dev) { int rc; + struct ipa_msg_meta msg_meta; + struct ipa_ecm_msg ecm_msg; if (eth_dev->of_state == IPA_ETH_OF_ST_STARTED) return 0; @@ -180,6 +192,16 @@ static int ipa_eth_start_device(struct ipa_eth_device *eth_dev) return rc; } + memset(&msg_meta, 0, sizeof(msg_meta)); + memset(&ecm_msg, 0, sizeof(ecm_msg)); + + ecm_msg.ifindex = eth_dev->net_dev->ifindex; + strlcpy(ecm_msg.name, eth_dev->net_dev->name, IPA_RESOURCE_NAME_MAX); + + msg_meta.msg_type = ECM_CONNECT; + msg_meta.msg_len = sizeof(struct ipa_ecm_msg); + (void) ipa_send_msg(&msg_meta, &ecm_msg, ipa_eth_free_msg); + ipa_eth_dev_log(eth_dev, "Started device"); eth_dev->of_state = IPA_ETH_OF_ST_STARTED; @@ -190,6 +212,18 @@ static int ipa_eth_start_device(struct ipa_eth_device *eth_dev) static int ipa_eth_stop_device(struct ipa_eth_device *eth_dev) { int rc; + struct ipa_msg_meta msg_meta; + struct ipa_ecm_msg ecm_msg; + + memset(&msg_meta, 0, sizeof(msg_meta)); + memset(&ecm_msg, 0, sizeof(ecm_msg)); + + ecm_msg.ifindex = eth_dev->net_dev->ifindex; + strlcpy(ecm_msg.name, eth_dev->net_dev->name, IPA_RESOURCE_NAME_MAX); + + msg_meta.msg_type = ECM_DISCONNECT; + msg_meta.msg_len = sizeof(struct ipa_ecm_msg); + (void) ipa_send_msg(&msg_meta, &ecm_msg, ipa_eth_free_msg); if (eth_dev->of_state == IPA_ETH_OF_ST_DEINITED) return 0; @@ -342,7 +376,7 @@ static void ipa_eth_dev_start_timer_cb(unsigned long data) ipa_eth_refresh_device(eth_dev); } -static int ipa_eth_netdev_event_change(struct ipa_eth_device *eth_dev) +static int __ipa_eth_netdev_event(struct ipa_eth_device *eth_dev) { bool refresh_needed = netif_carrier_ok(eth_dev->net_dev) ? !test_and_set_bit(IPA_ETH_IF_ST_LOWER_UP, ð_dev->if_state) : @@ -365,17 +399,9 @@ static int ipa_eth_netdev_event(struct notifier_block *nb, if (net_dev != eth_dev->net_dev) return NOTIFY_DONE; - ipa_eth_dev_log(eth_dev, "Received netdev event %lu", event); - - switch (event) { - case NETDEV_CHANGE: - return ipa_eth_netdev_event_change(eth_dev); - default: - /* Ignore other events */ - break; - } + ipa_eth_dev_log(eth_dev, "Received netdev event 0x%04lx", event); - return NOTIFY_DONE; + return __ipa_eth_netdev_event(eth_dev); } static int ipa_eth_uc_ready_cb(struct notifier_block *nb, @@ -620,7 +646,7 @@ static void __ipa_eth_unpair_device(struct ipa_eth_device *eth_dev) flush_work(ð_dev->refresh); - eth_dev->init = eth_dev->start = false; + set_bit(IPA_ETH_DEV_ST_UNPAIRING, ð_dev->state); ipa_eth_refresh_device(eth_dev); flush_work(ð_dev->refresh); @@ -628,6 +654,8 @@ static void __ipa_eth_unpair_device(struct ipa_eth_device *eth_dev) unregister_netdevice_notifier(ð_dev->netdevice_nb); ipa_eth_offload_unpair_device(eth_dev); + + clear_bit(IPA_ETH_DEV_ST_UNPAIRING, ð_dev->state); } static void ipa_eth_pair_devices(void) @@ -904,6 +932,7 @@ int ipa_eth_register_offload_driver(struct ipa_eth_offload_driver *od) ipa_eth_log("Registered offload driver %s", od->name); ipa_eth_pair_devices(); + ipa_eth_refresh_devices(); return 0; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_gsi.c b/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_gsi.c index 8f3983dd7c7d..006953a825ce 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_gsi.c +++ b/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_gsi.c @@ -242,6 +242,14 @@ int ipa_eth_gsi_dealloc(struct ipa_eth_channel *ch) } if (ep_ctx->gsi_evt_ring_hdl != ~0) { + gsi_rc = gsi_reset_evt_ring(ep_ctx->gsi_evt_ring_hdl); + if (gsi_rc != GSI_STATUS_SUCCESS) { + ipa_eth_dev_err(ch->eth_dev, + "Failed to reset event ring %lu", + ep_ctx->gsi_evt_ring_hdl); + return gsi_rc; + } + gsi_rc = gsi_dealloc_evt_ring(ep_ctx->gsi_evt_ring_hdl); if (gsi_rc != GSI_STATUS_SUCCESS) { ipa_eth_dev_err(ch->eth_dev, diff --git a/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_offload.c b/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_offload.c index 89acc788609c..77d92d9ae82f 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_offload.c +++ b/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_offload.c @@ -207,6 +207,8 @@ int ipa_eth_offload_register_driver(struct ipa_eth_offload_driver *od) void ipa_eth_offload_unregister_driver(struct ipa_eth_offload_driver *od) { + debugfs_remove_recursive(od->debugfs); + mutex_lock(&ipa_eth_offload_drivers_lock); list_del(&od->driver_list); mutex_unlock(&ipa_eth_offload_drivers_lock); diff --git a/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_pm.c b/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_pm.c index d969bb56b834..a8d4c7b7b50b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_pm.c +++ b/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_pm.c @@ -11,6 +11,7 @@ */ #include <linux/ethtool.h> +#include <linux/rtnetlink.h> #include "ipa_eth_i.h" @@ -100,7 +101,10 @@ static u32 __fetch_ethtool_link_speed(struct ipa_eth_device *eth_dev) int rc; struct ethtool_link_ksettings link_ksettings; + rtnl_lock(); rc = __ethtool_get_link_ksettings(eth_dev->net_dev, &link_ksettings); + rtnl_unlock(); + if (rc) { ipa_eth_dev_err(eth_dev, "Failed to obtain link settings via ethtool"); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c index 94e1acde00f6..c71dcee6d586 100644..100755 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c @@ -537,7 +537,7 @@ static dma_addr_t ipa_mpm_smmu_map(void *va_addr, /* check cache coherent */ if (ipa_mpm_ctx->dev_info.is_cache_coherent) { - IPA_MPM_DBG(" enable cache coherent\n"); + IPA_MPM_DBG_LOW(" enable cache coherent\n"); prot |= IOMMU_CACHE; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c index 4e44e94e4ac0..d5fcdc348730 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c @@ -3026,7 +3026,7 @@ int ipa3_create_wdi_mapping(u32 num_buffers, struct ipa_wdi_buffer_info *info) } for (i = 0; i < num_buffers; i++) { - IPADBG("i=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", i, + IPADBG_LOW("i=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", i, &info[i].pa, info[i].iova, info[i].size); info[i].result = ipa3_iommu_map(cb->iommu, rounddown(info[i].iova, PAGE_SIZE), @@ -3056,7 +3056,7 @@ int ipa3_release_wdi_mapping(u32 num_buffers, struct ipa_wdi_buffer_info *info) } for (i = 0; i < num_buffers; i++) { - IPADBG("i=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", i, + IPADBG_LOW("i=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", i, &info[i].pa, info[i].iova, info[i].size); info[i].result = iommu_unmap(cb->iommu, rounddown(info[i].iova, PAGE_SIZE), diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c index 7ef12d8c54c7..3395f0b327ae 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c @@ -4088,6 +4088,7 @@ bdy_alloc_fail: int ipahal_fltrt_allocate_hw_sys_tbl(struct ipa_mem_buffer *tbl_mem) { struct ipahal_fltrt_obj *obj; + gfp_t flag = GFP_KERNEL; IPAHAL_DBG_LOW("Entry\n"); @@ -4105,10 +4106,14 @@ int ipahal_fltrt_allocate_hw_sys_tbl(struct ipa_mem_buffer *tbl_mem) /* add word for rule-set terminator */ tbl_mem->size += obj->tbl_width; - +alloc: tbl_mem->base = dma_alloc_coherent(ipahal_ctx->ipa_pdev, tbl_mem->size, - &tbl_mem->phys_base, GFP_KERNEL); + &tbl_mem->phys_base, flag); if (!tbl_mem->base) { + if (flag == GFP_KERNEL) { + flag = GFP_ATOMIC; + goto alloc; + } IPAHAL_ERR("fail to alloc DMA buf of size %d\n", tbl_mem->size); return -ENOMEM; diff --git a/drivers/power/supply/qcom/qg-battery-profile.c b/drivers/power/supply/qcom/qg-battery-profile.c index beb6f519539e..744f26dc7930 100644 --- a/drivers/power/supply/qcom/qg-battery-profile.c +++ b/drivers/power/supply/qcom/qg-battery-profile.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2019 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 @@ -107,7 +107,8 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd, rc = -EINVAL; } else { /* OCV is passed as deci-uV - 10^-4 V */ - soc = interpolate_soc(&battery->profile[bp.table_index], + soc = qg_interpolate_soc( + &battery->profile[bp.table_index], bp.batt_temp, UV_TO_DECIUV(bp.ocv_uv)); soc = CAP(QG_MIN_SOC, QG_MAX_SOC, soc); rc = put_user(soc, &bp_user->soc); @@ -127,7 +128,7 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd, bp.table_index); rc = -EINVAL; } else { - ocv_uv = interpolate_var( + ocv_uv = qg_interpolate_var( &battery->profile[bp.table_index], bp.batt_temp, bp.soc); ocv_uv = DECIUV_TO_UV(ocv_uv); @@ -149,7 +150,7 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd, bp.table_index); rc = -EINVAL; } else { - fcc_mah = interpolate_single_row_lut( + fcc_mah = qg_interpolate_single_row_lut( &battery->profile[bp.table_index], bp.batt_temp, DEGC_SCALE); fcc_mah = CAP(QG_MIN_FCC_MAH, QG_MAX_FCC_MAH, fcc_mah); @@ -169,7 +170,8 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd, bp.table_index); rc = -EINVAL; } else { - var = interpolate_var(&battery->profile[bp.table_index], + var = qg_interpolate_var( + &battery->profile[bp.table_index], bp.batt_temp, bp.soc); var = CAP(QG_MIN_VAR, QG_MAX_VAR, var); rc = put_user(var, &bp_user->var); @@ -189,7 +191,7 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd, bp.table_index); rc = -EINVAL; } else { - slope = interpolate_slope( + slope = qg_interpolate_slope( &battery->profile[bp.table_index], bp.batt_temp, bp.soc); slope = CAP(QG_MIN_SLOPE, QG_MAX_SLOPE, slope); @@ -401,7 +403,7 @@ int lookup_soc_ocv(u32 *soc, u32 ocv_uv, int batt_temp, bool charging) if (!the_battery || !the_battery->profile_node) return -ENODEV; - *soc = interpolate_soc(&the_battery->profile[table_index], + *soc = qg_interpolate_soc(&the_battery->profile[table_index], batt_temp, UV_TO_DECIUV(ocv_uv)); *soc = CAP(0, 100, DIV_ROUND_CLOSEST(*soc, 100)); @@ -417,7 +419,7 @@ int qg_get_nominal_capacity(u32 *nom_cap_uah, int batt_temp, bool charging) if (!the_battery || !the_battery->profile_node) return -ENODEV; - fcc_mah = interpolate_single_row_lut( + fcc_mah = qg_interpolate_single_row_lut( &the_battery->profile[table_index], batt_temp, DEGC_SCALE); fcc_mah = CAP(QG_MIN_FCC_MAH, QG_MAX_FCC_MAH, fcc_mah); diff --git a/drivers/power/supply/qcom/qg-core.h b/drivers/power/supply/qcom/qg-core.h index eb85b4e1ee36..31404fd7189e 100644 --- a/drivers/power/supply/qcom/qg-core.h +++ b/drivers/power/supply/qcom/qg-core.h @@ -63,6 +63,7 @@ struct qg_dt { int shutdown_soc_threshold; int min_sleep_time_secs; int sys_min_volt_mv; + int fvss_vbat_mv; bool hold_soc_while_full; bool linearize_soc; bool cl_disable; @@ -73,6 +74,7 @@ struct qg_dt { bool use_s7_ocv; bool qg_sleep_config; bool qg_fast_chg_cfg; + bool fvss_enable; }; struct qg_esr_data { @@ -134,6 +136,7 @@ struct qpnp_qg { bool dc_present; bool charge_full; bool force_soc; + bool fvss_active; int charge_status; int charge_type; int chg_iterm_ma; @@ -142,6 +145,8 @@ struct qpnp_qg { int esr_nominal; int soh; int soc_reporting_ready; + int last_fifo_v_uv; + int last_fifo_i_ua; u32 fifo_done_count; u32 wa_flags; u32 seq_no; @@ -150,6 +155,8 @@ struct qpnp_qg { u32 esr_last; u32 s2_state; u32 s2_state_mask; + u32 soc_fvss_entry; + u32 vbat_fvss_entry; ktime_t last_user_update_time; ktime_t last_fifo_update_time; unsigned long last_maint_soc_update_time; diff --git a/drivers/power/supply/qcom/qg-profile-lib.c b/drivers/power/supply/qcom/qg-profile-lib.c index 2af997ecac0e..c506fea51cdb 100644 --- a/drivers/power/supply/qcom/qg-profile-lib.c +++ b/drivers/power/supply/qcom/qg-profile-lib.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2019 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 @@ -16,7 +16,7 @@ #include "qg-profile-lib.h" #include "qg-defs.h" -static int linear_interpolate(int y0, int x0, int y1, int x1, int x) +int qg_linear_interpolate(int y0, int x0, int y1, int x1, int x) { if (y0 == y1 || x == x0) return y0; @@ -26,7 +26,7 @@ static int linear_interpolate(int y0, int x0, int y1, int x1, int x) return y0 + ((y1 - y0) * (x - x0) / (x1 - x0)); } -int interpolate_single_row_lut(struct profile_table_data *lut, +int qg_interpolate_single_row_lut(struct profile_table_data *lut, int x, int scale) { int i, result; @@ -52,7 +52,7 @@ int interpolate_single_row_lut(struct profile_table_data *lut, if (x == lut->col_entries[i] * scale) { result = lut->data[0][i]; } else { - result = linear_interpolate( + result = qg_linear_interpolate( lut->data[0][i-1], lut->col_entries[i-1] * scale, lut->data[0][i], @@ -63,7 +63,7 @@ int interpolate_single_row_lut(struct profile_table_data *lut, return result; } -int interpolate_soc(struct profile_table_data *lut, +int qg_interpolate_soc(struct profile_table_data *lut, int batt_temp, int ocv) { int i, j, soc_high, soc_low, soc; @@ -94,7 +94,7 @@ int interpolate_soc(struct profile_table_data *lut, if (ocv >= lut->data[i][j]) { if (ocv == lut->data[i][j]) return lut->row_entries[i]; - soc = linear_interpolate( + soc = qg_linear_interpolate( lut->row_entries[i], lut->data[i][j], lut->row_entries[i - 1], @@ -115,7 +115,7 @@ int interpolate_soc(struct profile_table_data *lut, for (i = 0; i < rows-1; i++) { if (soc_high == 0 && is_between(lut->data[i][j], lut->data[i+1][j], ocv)) { - soc_high = linear_interpolate( + soc_high = qg_linear_interpolate( lut->row_entries[i], lut->data[i][j], lut->row_entries[i + 1], @@ -125,7 +125,7 @@ int interpolate_soc(struct profile_table_data *lut, if (soc_low == 0 && is_between(lut->data[i][j-1], lut->data[i+1][j-1], ocv)) { - soc_low = linear_interpolate( + soc_low = qg_linear_interpolate( lut->row_entries[i], lut->data[i][j-1], lut->row_entries[i + 1], @@ -134,7 +134,7 @@ int interpolate_soc(struct profile_table_data *lut, } if (soc_high && soc_low) { - soc = linear_interpolate( + soc = qg_linear_interpolate( soc_low, lut->col_entries[j-1] * DEGC_SCALE, soc_high, @@ -155,7 +155,7 @@ int interpolate_soc(struct profile_table_data *lut, return 10000; } -int interpolate_var(struct profile_table_data *lut, +int qg_interpolate_var(struct profile_table_data *lut, int batt_temp, int soc) { int i, var1, var2, var, rows, cols; @@ -199,7 +199,7 @@ int interpolate_var(struct profile_table_data *lut, break; if (batt_temp == lut->col_entries[i] * DEGC_SCALE) { - var = linear_interpolate( + var = qg_linear_interpolate( lut->data[row1][i], lut->row_entries[row1], lut->data[row2][i], @@ -208,21 +208,21 @@ int interpolate_var(struct profile_table_data *lut, return var; } - var1 = linear_interpolate( + var1 = qg_linear_interpolate( lut->data[row1][i - 1], lut->col_entries[i - 1] * DEGC_SCALE, lut->data[row1][i], lut->col_entries[i] * DEGC_SCALE, batt_temp); - var2 = linear_interpolate( + var2 = qg_linear_interpolate( lut->data[row2][i - 1], lut->col_entries[i - 1] * DEGC_SCALE, lut->data[row2][i], lut->col_entries[i] * DEGC_SCALE, batt_temp); - var = linear_interpolate( + var = qg_linear_interpolate( var1, lut->row_entries[row1], var2, @@ -232,7 +232,7 @@ int interpolate_var(struct profile_table_data *lut, return var; } -int interpolate_slope(struct profile_table_data *lut, +int qg_interpolate_slope(struct profile_table_data *lut, int batt_temp, int soc) { int i, ocvrow1, ocvrow2, rows, cols; @@ -284,14 +284,14 @@ int interpolate_slope(struct profile_table_data *lut, lut->row_entries[row2]); return slope; } - ocvrow1 = linear_interpolate( + ocvrow1 = qg_linear_interpolate( lut->data[row1][i - 1], lut->col_entries[i - 1] * DEGC_SCALE, lut->data[row1][i], lut->col_entries[i] * DEGC_SCALE, batt_temp); - ocvrow2 = linear_interpolate( + ocvrow2 = qg_linear_interpolate( lut->data[row2][i - 1], lut->col_entries[i - 1] * DEGC_SCALE, lut->data[row2][i], diff --git a/drivers/power/supply/qcom/qg-profile-lib.h b/drivers/power/supply/qcom/qg-profile-lib.h index eb7263dd7395..48408e04ab42 100644 --- a/drivers/power/supply/qcom/qg-profile-lib.h +++ b/drivers/power/supply/qcom/qg-profile-lib.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -22,13 +22,14 @@ struct profile_table_data { int **data; }; -int interpolate_single_row_lut(struct profile_table_data *lut, +int qg_linear_interpolate(int y0, int x0, int y1, int x1, int x); +int qg_interpolate_single_row_lut(struct profile_table_data *lut, int x, int scale); -int interpolate_soc(struct profile_table_data *lut, +int qg_interpolate_soc(struct profile_table_data *lut, int batt_temp, int ocv); -int interpolate_var(struct profile_table_data *lut, +int qg_interpolate_var(struct profile_table_data *lut, int batt_temp, int soc); -int interpolate_slope(struct profile_table_data *lut, +int qg_interpolate_slope(struct profile_table_data *lut, int batt_temp, int soc); #endif /*__QG_PROFILE_LIB_H__ */ diff --git a/drivers/power/supply/qcom/qg-reg.h b/drivers/power/supply/qcom/qg-reg.h index 95276251ddd8..d2591280622b 100644 --- a/drivers/power/supply/qcom/qg-reg.h +++ b/drivers/power/supply/qcom/qg-reg.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2019 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 @@ -128,6 +128,7 @@ #define QG_SDAM_ESR_DISCHARGE_DELTA_OFFSET 0x6E /* 4-byte 0x6E-0x71 */ #define QG_SDAM_ESR_CHARGE_SF_OFFSET 0x72 /* 2-byte 0x72-0x73 */ #define QG_SDAM_ESR_DISCHARGE_SF_OFFSET 0x74 /* 2-byte 0x74-0x75 */ +#define QG_SDAM_MAGIC_OFFSET 0x80 /* 4-byte 0x80-0x83 */ #define QG_SDAM_MAX_OFFSET 0xA4 /* Below offset is used by PBS */ diff --git a/drivers/power/supply/qcom/qg-sdam.c b/drivers/power/supply/qcom/qg-sdam.c index a7cb97e0e53d..95ac8ecc61c9 100644 --- a/drivers/power/supply/qcom/qg-sdam.c +++ b/drivers/power/supply/qcom/qg-sdam.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2019 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 @@ -88,6 +88,11 @@ static struct qg_sdam_info sdam_info[] = { .offset = QG_SDAM_ESR_DISCHARGE_SF_OFFSET, .length = 2, }, + [SDAM_MAGIC] = { + .name = "SDAM_MAGIC_OFFSET", + .offset = QG_SDAM_MAGIC_OFFSET, + .length = 4, + }, }; int qg_sdam_write(u8 param, u32 data) @@ -242,6 +247,23 @@ int qg_sdam_write_all(u32 *sdam_data) return 0; } +int qg_sdam_clear(void) +{ + int i, rc = 0; + struct qg_sdam *chip = the_chip; + u8 data = 0; + + if (!chip) { + pr_err("Invalid sdam-chip pointer\n"); + return -EINVAL; + } + + for (i = SDAM_MIN_OFFSET; i <= SDAM_MAX_OFFSET; i++) + rc |= qg_sdam_multibyte_write(i, &data, 1); + + return rc; +} + int qg_sdam_init(struct device *dev) { int rc; diff --git a/drivers/power/supply/qcom/qg-sdam.h b/drivers/power/supply/qcom/qg-sdam.h index 45218a83776e..d365f25def7b 100644 --- a/drivers/power/supply/qcom/qg-sdam.h +++ b/drivers/power/supply/qcom/qg-sdam.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -14,6 +14,8 @@ #define __QG_SDAM_H__ #define SDAM_TYPE 0x2E +#define SDAM_MIN_OFFSET 0x45 +#define SDAM_MAX_OFFSET 0xB3 enum qg_sdam_param { SDAM_VALID, @@ -28,6 +30,7 @@ enum qg_sdam_param { SDAM_ESR_DISCHARGE_DELTA, SDAM_ESR_CHARGE_SF, SDAM_ESR_DISCHARGE_SF, + SDAM_MAGIC, SDAM_MAX, }; @@ -43,5 +46,6 @@ int qg_sdam_write_all(u32 *sdam_data); int qg_sdam_read_all(u32 *sdam_data); int qg_sdam_multibyte_write(u32 offset, u8 *sdam_data, u32 length); int qg_sdam_multibyte_read(u32 offset, u8 *sdam_data, u32 length); +int qg_sdam_clear(void); #endif diff --git a/drivers/power/supply/qcom/qg-soc.c b/drivers/power/supply/qcom/qg-soc.c index 4c227af2665a..21e8171651fe 100644 --- a/drivers/power/supply/qcom/qg-soc.c +++ b/drivers/power/supply/qcom/qg-soc.c @@ -24,6 +24,7 @@ #include "qg-reg.h" #include "qg-util.h" #include "qg-defs.h" +#include "qg-profile-lib.h" #include "qg-soc.h" #define DEFAULT_UPDATE_TIME_MS 64000 @@ -36,6 +37,11 @@ module_param_named( soc_interval_ms, qg_delta_soc_interval_ms, int, 0600 ); +static int qg_fvss_delta_soc_interval_ms = 10000; +module_param_named( + fvss_soc_interval_ms, qg_fvss_delta_soc_interval_ms, int, 0600 +); + static int qg_delta_soc_cold_interval_ms = 4000; module_param_named( soc_cold_interval_ms, qg_delta_soc_cold_interval_ms, int, 0600 @@ -46,6 +52,84 @@ module_param_named( maint_soc_update_ms, qg_maint_soc_update_ms, int, 0600 ); +/* FVSS scaling only based on VBAT */ +static int qg_fvss_vbat_scaling = 1; +module_param_named( + fvss_vbat_scaling, qg_fvss_vbat_scaling, int, 0600 +); + +static int qg_process_fvss_soc(struct qpnp_qg *chip, int sys_soc) +{ + int rc, vbat_uv = 0, vbat_cutoff_uv = chip->dt.vbatt_cutoff_mv * 1000; + int soc_vbat = 0, wt_vbat = 0, wt_sys = 0, soc_fvss = 0; + + if (!chip->dt.fvss_enable) + return 0; + + if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) + goto exit_soc_scale; + + rc = qg_get_battery_voltage(chip, &vbat_uv); + if (rc < 0) + goto exit_soc_scale; + + if (!chip->last_fifo_v_uv) + chip->last_fifo_v_uv = vbat_uv; + + if (chip->last_fifo_v_uv > (chip->dt.fvss_vbat_mv * 1000)) { + qg_dbg(chip, QG_DEBUG_SOC, "FVSS: last_fifo_v=%d fvss_entry_uv=%d - exit\n", + chip->last_fifo_v_uv, chip->dt.fvss_vbat_mv * 1000); + goto exit_soc_scale; + } + + /* Enter FVSS */ + if (!chip->fvss_active) { + chip->vbat_fvss_entry = CAP(vbat_cutoff_uv, + chip->dt.fvss_vbat_mv * 1000, + chip->last_fifo_v_uv); + chip->soc_fvss_entry = sys_soc; + chip->fvss_active = true; + } else if (chip->last_fifo_v_uv > chip->vbat_fvss_entry) { + /* VBAT has gone beyond the entry voltage */ + chip->vbat_fvss_entry = chip->last_fifo_v_uv; + chip->soc_fvss_entry = sys_soc; + } + + soc_vbat = qg_linear_interpolate(chip->soc_fvss_entry, + chip->vbat_fvss_entry, + 0, + vbat_cutoff_uv, + chip->last_fifo_v_uv); + soc_vbat = CAP(0, 100, soc_vbat); + + if (qg_fvss_vbat_scaling) { + wt_vbat = 100; + wt_sys = 0; + } else { + wt_sys = qg_linear_interpolate(100, + chip->soc_fvss_entry, + 0, + 0, + sys_soc); + wt_sys = CAP(0, 100, wt_sys); + wt_vbat = 100 - wt_sys; + } + + soc_fvss = ((soc_vbat * wt_vbat) + (sys_soc * wt_sys)) / 100; + soc_fvss = CAP(0, 100, soc_fvss); + + qg_dbg(chip, QG_DEBUG_SOC, "FVSS: vbat_fvss_entry=%d soc_fvss_entry=%d cutoff_uv=%d vbat_uv=%d fifo_avg_v=%d soc_vbat=%d sys_soc=%d wt_vbat=%d wt_sys=%d soc_fvss=%d\n", + chip->vbat_fvss_entry, chip->soc_fvss_entry, + vbat_cutoff_uv, vbat_uv, chip->last_fifo_v_uv, + soc_vbat, sys_soc, wt_vbat, wt_sys, soc_fvss); + + return soc_fvss; + +exit_soc_scale: + chip->fvss_active = false; + return sys_soc; +} + int qg_adjust_sys_soc(struct qpnp_qg *chip) { int soc, vbat_uv, rc; @@ -72,8 +156,11 @@ int qg_adjust_sys_soc(struct qpnp_qg *chip) soc = DIV_ROUND_CLOSEST(chip->sys_soc, 100); } - qg_dbg(chip, QG_DEBUG_SOC, "last_adj_sys_soc=%d adj_sys_soc=%d\n", - chip->last_adj_ssoc, soc); + qg_dbg(chip, QG_DEBUG_SOC, "sys_soc=%d adjusted sys_soc=%d\n", + chip->sys_soc, soc); + + soc = qg_process_fvss_soc(chip, soc); + chip->last_adj_ssoc = soc; return soc; @@ -103,6 +190,8 @@ static void get_next_update_time(struct qpnp_qg *chip) else if (chip->maint_soc > 0 && chip->maint_soc >= chip->recharge_soc) /* if in maintenance mode scale slower */ min_delta_soc_interval_ms = qg_maint_soc_update_ms; + else if (chip->fvss_active) + min_delta_soc_interval_ms = qg_fvss_delta_soc_interval_ms; if (!min_delta_soc_interval_ms) min_delta_soc_interval_ms = 1000; /* 1 second */ diff --git a/drivers/power/supply/qcom/qpnp-fg-gen4.c b/drivers/power/supply/qcom/qpnp-fg-gen4.c index 0df0636a034d..53b7244c04f3 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen4.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen4.c @@ -2334,8 +2334,8 @@ done: out: if (!chip->esr_fast_calib || is_debug_batt_id(fg)) { /* If it is debug battery, then disable ESR fast calibration */ - chip->esr_fast_calib = false; fg_gen4_esr_fast_calib_config(chip, false); + chip->esr_fast_calib = false; } if (chip->dt.multi_profile_load && rc < 0) diff --git a/drivers/power/supply/qcom/qpnp-qg.c b/drivers/power/supply/qcom/qpnp-qg.c index e9030aa74a55..7afd45172d18 100644 --- a/drivers/power/supply/qcom/qpnp-qg.c +++ b/drivers/power/supply/qcom/qpnp-qg.c @@ -460,6 +460,9 @@ static int qg_process_fifo(struct qpnp_qg *chip, u32 fifo_length) chip->kdata.fifo[j].interval = sample_interval; chip->kdata.fifo[j].count = sample_count; + chip->last_fifo_v_uv = chip->kdata.fifo[j].v; + chip->last_fifo_i_ua = chip->kdata.fifo[j].i; + qg_dbg(chip, QG_DEBUG_FIFO, "FIFO %d raw_v=%d uV=%d raw_i=%d uA=%d interval=%d count=%d\n", j, fifo_v, chip->kdata.fifo[j].v, @@ -524,6 +527,9 @@ static int qg_process_accumulator(struct qpnp_qg *chip) if (chip->kdata.fifo_length == MAX_FIFO_LENGTH) chip->kdata.fifo_length = MAX_FIFO_LENGTH - 1; + chip->last_fifo_v_uv = chip->kdata.fifo[index].v; + chip->last_fifo_i_ua = chip->kdata.fifo[index].i; + if (chip->kdata.fifo_length == 1) /* Only accumulator data */ chip->kdata.seq_no = chip->seq_no++ % U32_MAX; @@ -2068,6 +2074,9 @@ static int qg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_POWER_AVG: rc = qg_get_power(chip, &pval->intval, true); break; + case POWER_SUPPLY_PROP_SCALE_MODE_EN: + pval->intval = chip->fvss_active; + break; default: pr_debug("Unsupported property %d\n", psp); break; @@ -2126,6 +2135,7 @@ static enum power_supply_property qg_psy_props[] = { POWER_SUPPLY_PROP_VOLTAGE_AVG, POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_POWER_NOW, + POWER_SUPPLY_PROP_SCALE_MODE_EN, }; static const struct power_supply_desc qg_psy_desc = { @@ -3147,6 +3157,39 @@ static int qg_set_wa_flags(struct qpnp_qg *chip) return 0; } +#define SDAM_MAGIC_NUMBER 0x12345678 +static int qg_sanitize_sdam(struct qpnp_qg *chip) +{ + int rc = 0; + u32 data = 0; + + rc = qg_sdam_read(SDAM_MAGIC, &data); + if (rc < 0) { + pr_err("Failed to read SDAM rc=%d\n", rc); + return rc; + } + + if (data == SDAM_MAGIC_NUMBER) { + qg_dbg(chip, QG_DEBUG_PON, "SDAM valid\n"); + } else if (data == 0) { + rc = qg_sdam_write(SDAM_MAGIC, SDAM_MAGIC_NUMBER); + if (!rc) + qg_dbg(chip, QG_DEBUG_PON, "First boot. SDAM initilized\n"); + } else { + /* SDAM has invalid value */ + rc = qg_sdam_clear(); + if (!rc) { + pr_err("SDAM uninitialized, SDAM reset\n"); + rc = qg_sdam_write(SDAM_MAGIC, SDAM_MAGIC_NUMBER); + } + } + + if (rc < 0) + pr_err("Failed in SDAM operation, rc=%d\n", rc); + + return rc; +} + #define ADC_CONV_DLY_512MS 0xA static int qg_hw_init(struct qpnp_qg *chip) { @@ -3568,6 +3611,7 @@ static int qg_alg_init(struct qpnp_qg *chip) #define DEFAULT_SLEEP_TIME_SECS 1800 /* 30 mins */ #define DEFAULT_SYS_MIN_VOLT_MV 2800 #define DEFAULT_FAST_CHG_S2_FIFO_LENGTH 1 +#define DEFAULT_FVSS_VBAT_MV 3500 static int qg_parse_dt(struct qpnp_qg *chip) { int rc = 0; @@ -3862,6 +3906,18 @@ static int qg_parse_dt(struct qpnp_qg *chip) else chip->dt.min_sleep_time_secs = temp; + if (of_property_read_bool(node, "qcom,fvss-enable")) { + + chip->dt.fvss_enable = true; + + rc = of_property_read_u32(node, + "qcom,fvss-vbatt-mv", &temp); + if (rc < 0) + chip->dt.fvss_vbat_mv = DEFAULT_FVSS_VBAT_MV; + else + chip->dt.fvss_vbat_mv = temp; + } + /* Capacity learning params*/ if (!chip->dt.cl_disable) { chip->dt.cl_feedback_on = of_property_read_bool(node, @@ -4268,6 +4324,12 @@ static int qpnp_qg_probe(struct platform_device *pdev) return rc; } + rc = qg_sanitize_sdam(chip); + if (rc < 0) { + pr_err("Failed to sanitize SDAM, rc=%d\n", rc); + return rc; + } + rc = qg_soc_init(chip); if (rc < 0) { pr_err("Failed to initialize SOC scaling init rc=%d\n", rc); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index ca9511afa3d3..d3b7bc626d69 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1139,6 +1139,12 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable) trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id); + /* + * Ensure that updates of buffer address and size happens + * before we set the DWC3_TRB_CTRL_HWO so that core + * does not process any stale TRB. + */ + mb(); trb->ctrl |= DWC3_TRB_CTRL_HWO; dwc3_ep_inc_enq(dep); diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 838e058a3eaf..332997abd098 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1392,6 +1392,7 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, struct usb_endpoint_descriptor desc1, *desc; switch (epfile->ffs->gadget->speed) { + case USB_SPEED_SUPER_PLUS: case USB_SPEED_SUPER: desc_idx = 2; break; diff --git a/include/linux/ipa_eth.h b/include/linux/ipa_eth.h index 2e09ac2b3916..732cc199f5cc 100644 --- a/include/linux/ipa_eth.h +++ b/include/linux/ipa_eth.h @@ -440,6 +440,7 @@ struct ipa_eth_channel { * @start_on_resume: Allow start upon driver resume * @start_on_timeout: Timeout in milliseconds after which @start is enabled * @start_timer: Timer associated with @start_on_timer + * @state: Device state * @if_state: Interface state - one or more bit numbers IPA_ETH_IF_ST_* * @pm_handle: IPA PM client handle for the device * @bus_priv: Private field for use by offload subsystem bus layer @@ -478,6 +479,7 @@ struct ipa_eth_device { u32 start_on_timeout; struct timer_list start_timer; + unsigned long state; unsigned long if_state; u32 pm_handle; diff --git a/include/uapi/linux/qg-profile.h b/include/uapi/linux/qg-profile.h index 0230b3227f78..e18844af5b79 100644 --- a/include/uapi/linux/qg-profile.h +++ b/include/uapi/linux/qg-profile.h @@ -47,7 +47,7 @@ struct battery_params { /* Profile MIN / MAX values */ #define QG_MIN_SOC 0 #define QG_MAX_SOC 10000 -#define QG_MIN_OCV_UV 3000000 +#define QG_MIN_OCV_UV 2000000 #define QG_MAX_OCV_UV 5000000 #define QG_MIN_VAR 0 #define QG_MAX_VAR 65535 diff --git a/include/uapi/media/cam_isp.h b/include/uapi/media/cam_isp.h index 68e8446f1585..4dd315d90600 100644 --- a/include/uapi/media/cam_isp.h +++ b/include/uapi/media/cam_isp.h @@ -84,14 +84,16 @@ #define CAM_ISP_DSP_MODE_ROUND 2 /* ISP Generic Cmd Buffer Blob types */ -#define CAM_ISP_GENERIC_BLOB_TYPE_HFR_CONFIG 0 -#define CAM_ISP_GENERIC_BLOB_TYPE_CLOCK_CONFIG 1 -#define CAM_ISP_GENERIC_BLOB_TYPE_BW_CONFIG 2 -#define CAM_ISP_GENERIC_BLOB_TYPE_UBWC_CONFIG 3 -#define CAM_ISP_GENERIC_BLOB_TYPE_CSID_CLOCK_CONFIG 4 -#define CAM_ISP_GENERIC_BLOB_TYPE_FE_CONFIG 5 -#define CAM_ISP_GENERIC_BLOB_TYPE_BW_CONFIG_V2 6 -#define CAM_ISP_GENERIC_BLOB_TYPE_INIT_FRAME_DROP 10 +#define CAM_ISP_GENERIC_BLOB_TYPE_HFR_CONFIG 0 +#define CAM_ISP_GENERIC_BLOB_TYPE_CLOCK_CONFIG 1 +#define CAM_ISP_GENERIC_BLOB_TYPE_BW_CONFIG 2 +#define CAM_ISP_GENERIC_BLOB_TYPE_UBWC_CONFIG 3 +#define CAM_ISP_GENERIC_BLOB_TYPE_CSID_CLOCK_CONFIG 4 +#define CAM_ISP_GENERIC_BLOB_TYPE_FE_CONFIG 5 +#define CAM_ISP_GENERIC_BLOB_TYPE_BW_CONFIG_V2 6 +#define CAM_ISP_GENERIC_BLOB_TYPE_INIT_FRAME_DROP 10 +#define CAM_ISP_GENERIC_BLOB_TYPE_SENSOR_DIMENSION_CONFIG 11 +#define CAM_ISP_GENERIC_BLOB_TYPE_FPS_CONFIG 12 /* Per Path Usage Data */ #define CAM_ISP_USAGE_INVALID 0 @@ -464,6 +466,36 @@ struct cam_fe_config { uint32_t latency_buf_size; } __attribute__((packed)); +/** + * struct cam_isp_sensor_path_dimension + * + * @width expected width + * @height expected height + * @measure_enabled flag to indicate if pixel measurement is to be enabled + */ +struct cam_isp_sensor_dimension { + uint32_t width; + uint32_t height; + uint32_t measure_enabled; +} __attribute__((packed)); + +/** + * struct cam_isp_sensor_config - Sensor Dimension configuration + * + * @pix_path: expected ppp path configuration + * @pix_path: expected ipp path configuration + * @rdi_path: expected rdi path configuration + * @hbi: HBI value + * @vbi: VBI value + */ +struct cam_isp_sensor_config { + struct cam_isp_sensor_dimension ppp_path; + struct cam_isp_sensor_dimension ipp_path; + struct cam_isp_sensor_dimension rdi_path[4]; + uint32_t hbi; + uint32_t vbi; +} __attribute__((packed)); + /* Acquire Device/HW v2 */ /** @@ -489,6 +521,15 @@ struct cam_isp_acquire_hw_info { uint64_t data; }; +/** + * struct cam_fps_config - FPS blob support + * + * @fps: FPS value + */ +struct cam_fps_config { + uint32_t fps; +} __attribute__((packed)); + #define CAM_ISP_ACQUIRE_COMMON_VER0 0x1000 #define CAM_ISP_ACQUIRE_COMMON_SIZE_VER0 0x0 |